import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import time
RUNALL = False

"""
Read in train and test as Pandas DataFrames
"""
df_train = pd.read_csv("train.csv")
df_test = pd.read_csv("test.csv")

# randomly sample a portion of df_train
# df_train = df_train.sample(n=10000)

#store gap values
Y_train = df_train.gap.values
#row where testing examples start
test_idx = df_train.shape[0]
#delete 'Id' column
df_test = df_test.drop(['Id'], axis=1)
#delete 'gap' column
df_train = df_train.drop(['gap'], axis=1)

#DataFrame with all train and test examples so we can more easily apply feature engineering on
df_all = pd.concat((df_train, df_test), axis=0)
df_all.head()

# adding features
from __future__ import print_function
from rdkit import Chem
from rdkit.Chem import Descriptors
from rdkit.Chem import rdMolDescriptors

# Feature engineering
# store smiles
smiles = df_all.smiles

start = time.time()

carbons = []
carbons = np.vstack(df_all.smiles.apply(lambda x: x.count('c')))
df_all['carbons'] = carbons

end = time.time()
print("how long", (end - start))

double = []
double = np.vstack(df_all.smiles.apply(lambda x: x.count('=')))
df_all['double'] = double

end = time.time()
print("how long", (end - start))

single = []
single = np.vstack(df_all.smiles.apply(lambda x: x.count('-')))
df_all['single'] = single

end = time.time()
print("how long", (end - start))

nitrogen = []
nitrogen = np.vstack(df_all.smiles.apply(lambda x: x.count('n')))
df_all['nitrogen'] = nitrogen

end = time.time()
print("how long", (end - start))

oxygen = []
oxygen = np.vstack(df_all.smiles.apply(lambda x: x.count('o')))
df_all['oxygen'] = oxygen

end = time.time()
print("how long", (end - start))

ccccc = []
ccccc = np.vstack(df_all.smiles.apply(lambda x: x.count('ccccc')))
df_all['ccccc'] = ccccc

end = time.time()
print("how long", (end - start))

ccc = []
ccc = np.vstack(df_all.smiles.apply(lambda x: x.count('ccc')))
df_all['ccc'] = ccc

end = time.time()
print("how long", (end - start))

Si = []
Si = np.vstack(df_all.smiles.apply(lambda x: x.count('Si')))
df_all['Si'] = Si

end = time.time()
print("how long", (end - start))

se = []
se = np.vstack(df_all.smiles.astype(str).apply(lambda x: x.count('se')))
df_all['se'] = se

end = time.time()
print("how long", (end - start))

mols = df_all.smiles.astype(str).apply(lambda x: Chem.MolFromSmiles(x))
df_all['mols'] = mols

atoms = np.vstack(df_all.mols.apply(lambda x: x.GetNumAtoms()))
df_all['atoms'] = atoms
bonds = np.vstack(df_all.mols.apply(lambda x: x.GetNumBonds()))
df_all['bonds'] = bonds

# aromaticity
aro = []
aro = np.vstack(df_all.mols.apply(lambda x: sum(int(x.GetAtomWithIdx(i).GetIsAromatic()) for i in range(x.GetNumAtoms()))))
df_all['aro'] = aro

# adding new features

# TPSA BAAAAAD
# tpsa = np.vstack(df_all.smiles.apply(lambda x: Descriptors.TPSA(Chem.MolFromSmiles(x))))
# df_all['tpsa'] = pd.DataFrame(tpsa)

# sp3 hybridization
sp3 = np.vstack(df_all.mols.apply(lambda x: rdMolDescriptors.CalcFractionCSP3(x)))
df_all['sp3'] = sp3

#Drop the 'smiles' column
df_all = df_all.drop(['smiles'], axis=1)
df_all = df_all.drop(['mols'], axis=1)
vals = df_all.values
X_train = vals[:test_idx]
X_test = vals[test_idx:]
print("Train features:", X_train.shape)
print("Train gap:", Y_train.shape)
print("Test features:", X_test.shape)

hd = np.vstack(mols.apply(lambda x: Descriptors.NumHDonors(x)))
df_all['hd'] = hd

ha = np.vstack(mols.apply(lambda x: Descriptors.NumHAcceptors(x)))
df_all['ha'] = ha

aliphcarbo = np.vstack(mols.apply(lambda x: Descriptors.NumAliphaticCarbocycles(x)))
df_all['aliphcarbo'] = aliphcarbo

aliphhetero = np.vstack(mols.apply(lambda x: Descriptors.NumAliphaticHeterocycles(x)))
df_all['aliphhetero'] = aliphhetero

aliphrings = np.vstack(mols.apply(lambda x: Descriptors.NumAliphaticRings(x)))
df_all['aliphrings'] = aliphrings

arocarbos = np.vstack(mols.apply(lambda x: Descriptors.NumAromaticCarbocycles(x)))
df_all['arocarbos'] = arocarbos

aroheteros = np.vstack(mols.apply(lambda x: Descriptors.NumAromaticHeterocycles(x)))
df_all['aroheteros'] = aroheteros

arorings = np.vstack(mols.apply(lambda x: Descriptors.NumAromaticRings(x)))
df_all['arorings'] = arorings

heteros = np.vstack(mols.apply(lambda x: Descriptors.NumHeteroatoms(x)))
df_all['heteros'] = heteros

radelecs = np.vstack(mols.apply(lambda x: Descriptors.NumRadicalElectrons(x)))
df_all['radelecs'] = radelecs

rotbonds = np.vstack(mols.apply(lambda x: Descriptors.NumRotatableBonds(x)))
df_all['rotbonds'] = rotbonds

satcarbos = np.vstack(mols.apply(lambda x: Descriptors.NumSaturatedCarbocycles(x)))
df_all['satcarbos'] = satcarbos

satheteros = np.vstack(mols.apply(lambda x: Descriptors.NumSaturatedHeterocycles(x)))
df_all['satheteros'] = satheteros

satrings = np.vstack(mols.apply(lambda x: Descriptors.NumSaturatedRings(x)))
df_all['satrings'] = satrings

valelecs = np.vstack(mols.apply(lambda x: Descriptors.NumValenceElectrons(x)))
df_all['valelecs'] = valelecs

from sklearn import linear_model
from sklearn.linear_model import LinearRegression
from sklearn.svm import SVR
from sklearn.ensemble import RandomForestRegressor

# Models

# fn that returns trained linear function to make predictions
def train_and_test_linear_model(X_train, Y_train):
    LR = LinearRegression()
    LR.fit(X_train, Y_train)
    # return the prediction we've trained
    return LR.predict

# RIDGE REG
def train_and_test_ridge_model(alpha):
    def helper(X_train, Y_train):
        reg = linear_model.Ridge(alpha=alpha)
        reg.fit(X_train, Y_train)
        return reg.predict
    return helper

# LASSO REG
def train_and_test_lasso_model(alpha):
    def helper(X_train, Y_train):
        reg = linear_model.Lasso(alpha=alpha)
        reg.fit(X_train, Y_train)
        return reg.predict
    return helper

# ELASTICNET REG
def train_and_test_elastic_net_model(alpha):
    def helper(X_train, Y_train):
        reg = linear_model.ElasticNet(alpha=alpha)
        reg.fit(X_train, Y_train)
        return reg.predict
    return helper

# SVM REG
def train_and_test_svm_model():
    def helper(X_train, Y_train):
        reg = SVR(kernel='rbf', C=1.0, epsilon=0.2)
        reg.fit(X_train, Y_train)
        return reg.predict
    return helper

# RF pred
# fn that returns trained Random Forest to make predictions
def train_and_test_RF_model(X_train, Y_train):
    RF = RandomForestRegressor(n_estimators=20)
    RF.fit(X_train, Y_train)
    # return the prediction we've trained
    return RF.predict

from training_and_validation import select_best_model

if RUNALL:
    select_best_model(X_train, Y_train, 4, train_and_test_linear_model)

if RUNALL:
    select_best_model(X_train, Y_train, 2, train_and_test_ridge_model(0.1))

if RUNALL:
    select_best_model(X_train, Y_train, 4, train_and_test_lasso_model(0.1))

if RUNALL:
    select_best_model(X_train, Y_train, 4, train_and_test_elastic_net_model(0.1))

if RUNALL:
    select_best_model(X_train, Y_train, 4, train_and_test_svm_model())

if RUNALL:
    select_best_model(X_train, Y_train, 2, train_and_test_RF_model)

def write_to_file(filename, predictions):
    with open(filename, "w") as f:
        f.write("Id,Prediction\n")
        for i,p in enumerate(predictions):
            f.write(str(i+1) + "," + str(p) + "\n")

# write a file for ridge regression
rid_reg = linear_model.Ridge(alpha=0.1)
rid_reg.fit(X_train, Y_train)
rid_reg_pred = rid_reg.predict(X_test)
write_to_file("rid_reg_correct_more_features.csv", rid_reg_pred)
print("starting RF")
# write a file for RF
rf = RandomForestRegressor(n_estimators=20)
rf.fit(X_train, Y_train)
rf_pred = rf.predict(X_test)
write_to_file("rf_correct_more_features.csv", rf_pred)

from sklearn.cluster import KMeans
from mpl_toolkits.mplot3d import Axes3D

X_red = PCA_red(X_train)

estimators = [('k_means_15', KMeans(n_clusters=15)),
              ('k_means_iris_10', KMeans(n_clusters=10)),
              ('k_means_iris_5', KMeans(n_clusters=5))]

fignum = 1
titles = ['15 clusters', '10 clusters', '5 clusters']
for name, est in estimators:
    fig = plt.figure(fignum, figsize=(8, 6))
    ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=48, azim=134)
    est.fit(X_red)
    labels = est.labels_
    ax.scatter(X_red[:, 1], X_red[:, 0], X_red[:, 2],
               c=labels.astype(np.float), edgecolor='k')

    ax.w_xaxis.set_ticklabels([])
    ax.w_yaxis.set_ticklabels([])
    ax.w_zaxis.set_ticklabels([])
    ax.set_xlabel('PC2')
    ax.set_ylabel('PC1')
    ax.set_zlabel('PC3')
    ax.set_title(titles[fignum - 1])
    ax.dist = 12
    fignum = fignum + 1

# Plot the ground truth
fig = plt.figure(fignum, figsize=(8, 6))
ax = Axes3D(fig, rect=[0, 0, .95, 1], elev=48, azim=134)

#labels = Y_train*10-10
#y = labels.astype(np.float)

ax.scatter(X_red[:, 1], X_red[:, 0], X_red[:, 2], c=Y_train, edgecolor='k')

ax.w_xaxis.set_ticklabels([])
ax.w_yaxis.set_ticklabels([])
ax.w_zaxis.set_ticklabels([])
ax.set_xlabel('PCA 2')
ax.set_ylabel('PCA 1')
ax.set_zlabel('PCA 3')
ax.set_title('Real Y Train')
ax.dist = 12

plt.show()

# PCA theory and learning:
from sklearn.preprocessing import StandardScaler

# standardize data to mean=0, var=1
X_std = StandardScaler().fit_transform(X_train)

# compute covariance matrix
cov_mat = np.cov(X_std.T)

#eigendecomposition - make real
eig_vals, eig_vecs = np.linalg.eig(cov_mat)
eig_vals = np.real(eig_vals)
eig_vecs = np.real(eig_vecs)

# Singular Vector Decomposition
u, s, v = np.linalg.svd(X_std.T)

# select principal components
eig_pairs = [(np.abs(eig_vals[i]), eig_vecs[:,i]) for i in range(len(eig_vals))]

eig_pairs.sort(key=lambda x: x[0])
eig_pairs.reverse()
eig_vals_sorted = [x[0] for x in eig_pairs]

idx = [i for i in range(len(eig_pairs)) ]

plt.figure()
plt.scatter(idx, eig_vals_sorted)
plt.show()

import numpy as np
from sklearn.model_selection import KFold

# Validation

# to compute RSM error
def compute_error(pred, actual):
    num_pts = float(pred.shape[0])
    # RSM error
    return np.sqrt(np.sum(np.square(np.subtract(pred, actual))) / num_pts)

# estimate model performance using validation sets
def select_best_model(X, Y, num_validation_sets, model_fn):
    # error accumulator
    error = 0
    # set up the kfold operation
    kf = KFold(n_splits=num_validation_sets)
    kf.get_n_splits(X)
    for train_index, validation_index in kf.split(X):
        print("next validation set")
        # get train and validate data
        X_train, X_validation = X[train_index], X[validation_index]
        Y_train, Y_validation = Y[train_index], Y[validation_index]
        # train, should return a function to make predictions
        print("training")
        trained_fn = model_fn(X_train, Y_train)
        # get error
        pred = trained_fn(X_validation)
        print("got a prediction!")
        error += compute_error(pred, Y_validation)
    return error / float(num_validation_sets)


import os.path
from src.data.dataset import DatasetBase
import numpy as np
from torchvision.datasets.utils import download_url
import tarfile
import sys
import pickle
import struct
import random
import matplotlib.pyplot as plt
from os import listdir

import matplotlib.pyplot as plt
import matplotlib.image as mpimg
import glob
from PIL import Image
import re

from collections import Counter

def natural_key(string_):
    """See http://www.codinghorror.com/blog/archives/001018.html"""
    return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', string_)]

class eventRawSurfacesDataset(DatasetBase):
    def __init__(self, opt, is_for, subset, transform, dataset_type):
        super(eventRawSurfacesDataset, self).__init__(opt, is_for, subset, transform, dataset_type)
        self._name = 'event-surfaces-dataset'

        # init meta
        self._init_meta(opt)

        # read dataset
        self._read_dataset()

    def _init_meta(self, opt):
        #self._rgb = not opt[self._name]["use_bgr"]
        self._root = opt[self._name]["data_dir"]
        self._sae_type = opt[self._name]["sae_type"]

        if self._is_for == "train":
            #self._data_folder = opt[self._name]["train_folder"]
            self._data_file = opt[self._name]["train_file"]
        elif self._is_for == "val":
            self._data_file = opt[self._name]["val_file"]
        elif self._is_for == "test":
            self._data_file = opt[self._name]["test_file"]
        else:
            raise ValueError(f"is_for={self._is_for} not valid")

    def __getitem__(self, index):
        assert (index < self._dataset_size)

        # Generate balanced pairs for each file and concatenate classes
        current_stream_num = random.sample(range(0, self._num_of_streams), 1)[0]  # Select randomly one of the streams

        # get data
        imgs, target = self.generate_balanced_pairs(current_stream_num)

        # pack data
        sample = {'img1': imgs[0], 'img2': imgs[1], 'img3': imgs[2], 'target1': target[0], 'target2': target[1]}

        # apply transformations
        if self._transform is not None:
            sample = self._transform(sample)

        return sample

    def __len__(self):
        return self._dataset_size

    def _read_dataset(self):

        # Read all data files of the correspondent folder
        self.data_filenames = [line.rstrip() for line in open(self._data_file)]
        self._num_of_streams = self.data_filenames.__len__()

        self._kpt_names = []
        self._kpt_num = []

        self._kpt_samples_data_pol=[]
        self._kpt_samples_data_time=[]

        self._data = []
        self._targets = []
        self._mask = []
        self._dataset_size = 0

        arbitrary_factor = self._opt['dataset']['mining_factor']
        id = 0

        for current_filename in self.data_filenames:

            data_filepath = os.path.join(self._root, current_filename, 'raw',)
            id = id + 1
            print(str(id)+' '+current_filename)

            #Save keypoint names and numberof keypoints per stream
            kpts_name_list = os.listdir(os.path.join(self._root, current_filename,'raw'))
            kpts_name_list = [i for i in kpts_name_list if 'kpt_' in i]
            kpts_name_list = sorted(kpts_name_list, key=natural_key)
            self._kpt_names.append(kpts_name_list)
            self._kpt_num.append(kpts_name_list.__len__())
            self._dataset_size = self._dataset_size + kpts_name_list.__len__()

            kpt_samples_data_pol = dict.fromkeys(kpts_name_list,[])
            kpt_samples_data_time = dict.fromkeys(kpts_name_list, [])

            for current_kpt_filename in kpts_name_list:

                # Save each sample pol
                kpt_samples_data_pol[current_kpt_filename] = np.loadtxt(os.path.join(data_filepath,current_kpt_filename,'kpt_SAE_polarity.txt'), delimiter=",").astype(int)
                # Save each sample time
                kpt_samples_data_time[current_kpt_filename] = np.loadtxt( os.path.join(data_filepath, current_kpt_filename, 'kpt_event_ts.txt'), delimiter=",")

            self._kpt_samples_data_pol.append(kpt_samples_data_pol)
            self._kpt_samples_data_time.append(kpt_samples_data_time)

        self._dataset_size = self._dataset_size * arbitrary_factor

    def generate_balanced_pairs(self, current_stream_num):

        num_of_classes = self._kpt_num[current_stream_num]

        # Select 2 different classes randomly (rnd_cls1 and rnd_cls2)
        if num_of_classes == 0:
            print('Error: num_of_classes = 0')
        if num_of_classes < 2:
            print('error_sample')

        kpts_list = range(0, self._kpt_names[current_stream_num].__len__())
        rnd_cls1, rnd_cls2 = random.sample(kpts_list, 2)

        rnd_cls1_name = self._kpt_names[current_stream_num][rnd_cls1] # Kpt 1 name
        rnd_cls2_name = self._kpt_names[current_stream_num][rnd_cls2] # Kpt 2 name

        data_pol_cls1 = self._kpt_samples_data_pol[current_stream_num][rnd_cls1_name]
        data_pol_cls2 = self._kpt_samples_data_pol[current_stream_num][rnd_cls2_name]

        data_time_cls1 = self._kpt_samples_data_time[current_stream_num][rnd_cls1_name]
        data_time_cls2 = self._kpt_samples_data_time[current_stream_num][rnd_cls2_name]

        if rnd_cls1 > self._kpt_names[current_stream_num].__len__() or rnd_cls2 > self._kpt_names[current_stream_num].__len__()  :
            print('error')

        # Select 2 samples of rnd_cls1 and one of a rnd_cls2

        anchor_class_path = os.path.join(self._root, self.data_filenames[current_stream_num],'raw', rnd_cls1_name)
        negative_class_path = os.path.join(self._root, self.data_filenames[current_stream_num],'raw',rnd_cls2_name)

        # Select rnd_cls1 sample randomly
        num_of_samples_a = data_time_cls1.size
        rnd_smpls_a = random.sample(range(0, num_of_samples_a), 2)
        samples_list_a = [line.rstrip() for line in open( os.path.join(self._root, self.data_filenames[current_stream_num], 'raw', rnd_cls1_name, 'kpt_files.txt'))]

        # Select two random samples from the random subclass

        # Select rnd_cls2 subclass randomly
        num_of_samples_n = data_time_cls2.size
        rnd_smpl_n = random.sample(range(0, num_of_samples_n), 1)[0]
        samples_list_n = [line.rstrip() for line in open(os.path.join(self._root, self.data_filenames[current_stream_num],'raw',self._kpt_names[current_stream_num][rnd_cls2], 'kpt_files.txt'))]

        img_name_1 = os.path.join(self._root, self.data_filenames[current_stream_num], 'raw', self._kpt_names[current_stream_num][rnd_cls1], samples_list_a[rnd_smpls_a[0]])
        img_name_2 = os.path.join(self._root, self.data_filenames[current_stream_num], 'raw', self._kpt_names[current_stream_num][rnd_cls1], samples_list_a[rnd_smpls_a[1]])
        img_name_3 = os.path.join(self._root, self.data_filenames[current_stream_num], 'raw', self._kpt_names[current_stream_num][rnd_cls2], samples_list_n[rnd_smpl_n])

        # img_1 = Image.open(img_name_1)
        # img_2 = Image.open(img_name_2)
        # img_3 = Image.open(img_name_3)

        # data_sample1 = np.asarray(img_1)
        # data_sample2 = np.asarray(img_2)
        # data_sample3 = np.asarray(img_3)

        data_sample1 = np.load(img_name_1)
        data_sample2 = np.load(img_name_2)
        data_sample3 = np.load(img_name_3)

        imgs = [data_sample1, data_sample2, data_sample3]
        targets = [1, 0]

        # if 'true':
        #      font1 = {'color': 'green'}
        #      font2 = {'color': 'darkred'}
        #
        #      plt.plot([1,2,3])
        #      plt.subplot(1, 3, 1)
        #      plt.imshow(data_sample1, cmap='Greys_r')
        #      plt.title('Class {}\n sample {}'.format(self._kpt_names[current_stream_num][rnd_cls1], samples_list_a[rnd_subsmpl1][-10:]), fontdict=font1)
        #      #plt.title('Class {}\n Subclass {}\n sample {}'.format(rnd_cls1, rnd_subcls, rnd_subsmpl1), fontdict=font1)
        #      plt.subplot(1, 3, 2)
        #      plt.imshow(data_sample2, cmap='Greys_r')
        #      plt.title('Class {}\n sample {}'.format(self._kpt_names[current_stream_num][rnd_cls1],samples_list_a[rnd_subsmpl2][-10:]), fontdict=font1)
        #      #plt.title('Class {}\n Subclass {}\n sample {}'.format(rnd_cls1, rnd_subcls, rnd_subsmpl2), fontdict=font1)
        #      plt.subplot(1, 3, 3)
        #      plt.imshow(data_sample3, cmap='Greys_r')
        #      plt.title('Class {}\n sample {}'.format(self._kpt_names[current_stream_num][rnd_cls2],samples_list_a[rnd_subsmpl3][-10:]), fontdict=font2)
        #      #plt.title('Class {}\n Subclass {}\n sample {}'.format(rnd_cls2, rnd_subcls2, rnd_subsmpl3), fontdict=font2)
        #      plt.show()

        return imgs, targets


# -*- coding: utf-8 -*-
"""
Spyder Editor

This is a temporary script file.
"""
import csv

def loadcsv(inputcsv):
    rows = []
    with open(inputcsv, newline='') as csvfile:
        sreader = csv.reader(csvfile, delimiter=',', quotechar='"')
        headerrow = next(sreader)
        for row in sreader:
            if row:
                rows.append(row)
    return headerrow, rows

def coldata(inputcsv, theset):
    headerrow, rows = loadcsv(inputcsv)
    return [r[theset] for r in rows]

import matplotlib.pyplot as plt
plt.figure(1)

inputcsv = 'Pagano_3lay_cyl_bend_THIN_T10_conv_Stress_errors_default.CSV'
x = coldata(inputcsv, 0)
y = coldata(inputcsv, 3)
plt.loglog(x, y, color = "black", marker = "x", markersize = 12, label = "DCE")

inputcsv = 'Pagano_3lay_cyl_bend_THIN_MST10_conv_Stress_errors_extraptrend.CSV'
x = coldata(inputcsv, 0)
y = coldata(inputcsv, 3)
plt.loglog(x, y, color = "black", marker = "o", markersize = 12, label = "TBE")

inputcsv = 'Pagano_3lay_cyl_bend_THIN_MST10_conv_Stress_errors_extrapmean.CSV'
x = coldata(inputcsv, 0)
y = coldata(inputcsv, 3)
plt.loglog(x, y, color = "black", marker = "d", markersize = 12, label = "MSOE")

plt.xlabel('Element size')
plt.ylabel('Normalized error')
plt.axis([0.004, 0.1, 0.0001, 1.0])
plt.grid(True)
legend = plt.legend(loc='best', shadow=False, fontsize='x-large')
plt.show()


import numpy as np
import matplotlib.pyplot as plt
import colorsys as colors

def get_color_order(i,max_num):
    #take a blue to red scale from 0 to max_num
    # uses HSV system, H_red = 0, H_green = 1/3 H_blue=2/3
    return colors.hsv_to_rgb(2.*float(i)/(float(max_num)*3.),1.,1.)

def get_color_from_cmap(i,max_num):
    pass

def flex_color_plot_vs_x(xvals,yvals,zvals,ax=None,
                         xwidth=None,
                         normalize=False,log=False,
                         save_name=None,**kw):
    """
    Display a color figure for something like a tracked DAC sweep.
    xvals should be a single vector with values for the primary sweep.
    yvals and zvals should be a list of arrays with the sweep points and measured values.
    """
    # create a figure and set of axes
    if ax == None:
        fig = plt.figure(figsize=(12,7))
        ax = fig.add_subplot(111)

    # calculate coordinates for corners of color blocks
    # x coordinates
    if xwidth is None:
        xvals = np.array(xvals)
        xvertices = np.zeros(np.array(xvals.shape)+1)
        xvertices[1:-1] = (xvals[:-1]+xvals[1:])/2.
        xvertices[0] = xvals[0] - (xvals[1]-xvals[0])/2
        xvertices[-1] = xvals[-1] + (xvals[-1]-xvals[-2])/2
    else:
        xvertices = []
        for xval in xvals:
            xvertices.append(xval+np.array([-0.5,0.5])*xwidth)
    # y coordinates
    yvertices = []
    for xx in range(len(xvals)):
        yvertices.append(np.zeros(np.array(yvals[xx].shape)+1))
        yvertices[xx][1:-1] = (yvals[xx][:-1]+yvals[xx][1:])/2.
        yvertices[xx][0] = yvals[xx][0] - (yvals[xx][1]-yvals[xx][0])/2
        yvertices[xx][-1] = yvals[xx][-1] + (yvals[xx][-1]-yvals[xx][-2])/2

    # various plot options
    # define colormap
    cmap = plt.get_cmap(kw.pop('cmap', 'CMRmap'))
    clim = kw.pop('clim', [None, None])
    # normalized plot
    if normalize:
        for xx in range(len(xvals)):
            zvals[xx] /= np.mean(zvals[xx])
    # logarithmic plot
    if log:
        for xx in range(len(xvals)):
            zvals[xx] = np.log(zvals[xx])/np.log(10)

    alpha = kw.pop('alpha',1)

    # add blocks to plot
    # hold = kw.pop('hold',False)
    colormap = []
    for xx in range(len(xvals)):
        tempzvals = np.array([np.append(zvals[xx],np.array(0)),np.append(zvals[xx],np.array(0))]).transpose()
        if xwidth is None:
            colormap.append(ax.pcolor(xvertices[xx:xx+2],
                                      yvertices[xx],
                                      tempzvals,
                                      cmap=cmap, vmin=clim[0], vmax=clim[1],
                                      alpha=alpha))
        else:
            colormap.append(ax.pcolor(xvertices[xx],yvertices[xx],tempzvals,cmap=cmap,alpha=alpha))
        #XX, YY = np.meshgrid(xvertices[xx:xx+2],yvertices[xx])
        #ax1.pcolor(XX,YY,tempzvals,cmap=cmap)

    return {'fig':ax.figure,'ax':ax,'cmap':colormap}

def flex_colormesh_plot_vs_xy(xvals,yvals,zvals,ax=None,
                         normalize=False,log=False,
                         save_name=None,**kw):
    """
    Add a rectangular block to a color plot using pcolormesh.
    xvals and yvals should be single vectors with values for the two sweep points.
    zvals should be a list of arrays with the measured values with shape (len(yvals), len(xvals)).
    """
    # create a figure and set of axes
    if ax == None:
        fig = plt.figure(figsize=(12,7))
        ax = fig.add_subplot(111)

    # convert xvals and yvals to single dimension arrays
    xvals = np.squeeze(np.array(xvals))
    yvals = np.squeeze(np.array(yvals))

    # calculate coordinates for corners of color blocks
    # x coordinates
    xvertices = np.zeros(np.array(xvals.shape)+1)
    xvertices[1:-1] = (xvals[:-1]+xvals[1:])/2.
    xvertices[0] = xvals[0] - (xvals[1]-xvals[0])/2
    xvertices[-1] = xvals[-1] + (xvals[-1]-xvals[-2])/2
    # y coordinates
    yvertices = np.zeros(np.array(yvals.shape)+1)
    yvertices[1:-1] = (yvals[:-1]+yvals[1:])/2.
    yvertices[0] = yvals[0] - (yvals[1]-yvals[0])/2
    yvertices[-1] = yvals[-1] + (yvals[-1]-yvals[-2])/2

    xgrid, ygrid = np.meshgrid(xvertices, yvertices)

    # various plot options
    # define colormap
    cmap = plt.get_cmap(kw.pop('cmap', 'CMRmap'))
    clim = kw.pop('clim', [None, None])
    # normalized plot
    if normalize:
        zvals /= np.mean(zvals, axis=0)
    # logarithmic plot
    if log:
        for xx in range(len(xvals)):
            zvals[xx] = np.log(zvals[xx])/np.log(10)

    alpha = kw.pop('alpha',1)

    # add blocks to plot
    # hold = kw.pop('hold',False)
    do_transpose = kw.pop('transpose', False)
    if do_transpose:
        colormap = ax.pcolormesh(ygrid.transpose(),
                      xgrid.transpose(),
                      zvals.transpose(),
                      cmap=cmap, vmin=clim[0], vmax=clim[1])
    else:
        colormap = ax.pcolormesh(xgrid, ygrid, zvals, cmap=cmap,
                      vmin=clim[0], vmax=clim[1])

    return {'fig':ax.figure,'ax':ax,'cmap':colormap}


from PyQt5 import QtCore, uic
from PyQt5.QtWidgets import QApplication, QLabel, \
    QPushButton, QAction, QSpinBox

def formulas():

    cash = qtd_cash.value()

    if cash == 0:
        pouco = 0
        razoavel = 0
        adequado = 0

    elif (0<cash)and(cash<=30):
        pouco = 1
        razoavel = 0
        adequado = 0

    elif (cash > 30)and(cash<50):
        pouco = (50-cash)/(50-30)
        razoavel = (cash-30)/(50-30)
        adequado = 0

    elif cash >= 70:
        pouco = 0
        razoavel = 0
        adequado = 1

    elif cash == 50:
        pouco = 0
        adequado = 0
        razoavel = 1

    elif (cash > 50)and(cash < 70):
        razoavel = (70-cash)/(70-50)
        adequado = (cash-50)/(70-50)
        pouco = 0

    people = quantidade_pessoa.value()

    if people == 0:
        insuficiente = 0
        satisfatorio = 0

    elif people >= 70:
        insuficiente = 0
        satisfatorio = 1

    elif (people > 0)and(people <= 30):
        insuficiente = 1
        satisfatorio = 0

    elif (people > 30)and(people < 70):
        insuficiente = (70-people)/(70-30)
        satisfatorio = (people - 30)/(70-30)

    regras(pouco, razoavel, adequado, insuficiente, satisfatorio)

def regras(pouco, razoavel, adequado, insuficiente, satisfatorio):

    if (pouco >= insuficiente) and (pouco >= satisfatorio):
        alto = pouco

    else:
        if (insuficiente <= satisfatorio):
            alto = insuficiente

        else:
            alto = satisfatorio

    if razoavel <= satisfatorio:
        medio = razoavel
    else:
        medio = satisfatorio

    if adequado <= satisfatorio:
        baixo = adequado
    else:
        baixo = satisfatorio

    soma = 0
    divisor = 0

    if baixo > 0:
        soma += 60 * baixo
        divisor += 3*baixo

    if medio > 0:
        soma += 150 * medio
        divisor += 3*medio

    if alto > 0:
        soma += 240 * alto
        divisor += 3*alto

    risco_final = soma/divisor
    show(risco_final)

def show(risco_final):
    if (risco_final <= 35):
        resultado.setText('Baixo : ' + str(risco_final))
        resultado.setVisible(True)
    elif(risco_final > 35)and(risco_final <= 65):
        resultado.setText('Médio : ' + str(risco_final))
        resultado.setVisible(True)
    elif(risco_final > 65):
        resultado.setText('Alto : ' + str(risco_final))
        resultado.setVisible(True)

if __name__ == "__main__":
    QtCore.QCoreApplication.setAttribute(QtCore.Qt.AA_ShareOpenGLContexts)
    app = QApplication([])

    window = uic.loadUi(r"C:\Users\Aender\Desktop\CC\Inteligencia artificial\ia-cc-2020-2-AendersonSouza\2_logica_fuzzy\interface.ui")

    dinheiro = window.findChild(QLabel, 'dinheiro')
    qtd_cash = window.findChild(QSpinBox, 'qtd_dinheiro')

    pessoa = window.findChild(QLabel, 'pessoa')
    quantidade_pessoa = window.findChild(QSpinBox, 'qtd_pessoa')

    risco = window.findChild(QLabel, 'risco')
    resultado= window.findChild(QLabel, 'resultado')
    resultado.setVisible(False)

    Calcular = window.findChild(QPushButton, 'calcular')
    Calcular.clicked.connect(formulas)

    window.show()

    app.exec_()


# -*- coding:utf-8 -*-
class Solution:
    # matrix类型为二维列表，需要返回列表
    def printMatrix(self, matrix):
        # write code here
        if not matrix or not matrix[0]:
            return []

        l, w = len(matrix), len(matrix[0])
        start = 0
        result = []
        while start * 2 < l and start * 2 < w:
            result += self.printMatrixIncircle(matrix, start)
            start += 1

        return result

    def printMatrixIncircle(self, matrix, start):
        endx = len(matrix[0]) - 1 - start
        endy = len(matrix) - 1 - start
        nums = []

        for i in range(endx + 1 - start):
            nums.append(matrix[start][start + i])

        if start <= endx and start < endy:
            for i in range(endy - start):
                nums.append(matrix[start + i + 1][endx])

        if start < endx and start < endy:
            for i in range(endx - start):
                nums.append(matrix[endy][endx - 1 - i])

        if start < endx and start + 1 < endy:
            for i in range(endy - 1 - start):
                nums.append(matrix[endy - 1 - i][start])

        return nums

if __name__ == '__main__':
    s = Solution()
    l = [[1], [2], [3], [4], [5]]
    r = s.printMatrix(l)
    print(r)


# Copyright (c) 2015-2019 Shawn Chin

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
Example usage:

        # Crete a mesh (collection of connected components)
        m = Mesh()

        # Add components (hexagons) to the mesh, each with a set of 'needs' ports (output ports) and
        # and 'provides' ports (input ports)
        m.add_component('A', needs_ports=['n1', 'n2'])
        m.add_component('B', provides_ports=['p1', 'p2'])
        m.add_component('C', needs_ports=['nX'])
        m.add_component('D', needs_ports=['data'], provides_ports=['pX'])

        # Add pass-through adapters to connect ports between components
        m.add_connection('A', 'n1', 'B', 'p1')
        m.add_connection('A', 'n2', 'D', 'pX')
        m.add_connection('C', 'nX', 'B', 'p2')

        # Define an external adapter and attach it to a 'needs' port
        m.add_resource('Resource X')
        m.add_connection_to_resource('D', 'data', 'Resource X')

        # Render as a Graphviz dot file
        print render_mesh_as_dot(m)

When visualised using Graphviz, this should produce an output that looks like:

            _____________              _____________
           |      A      |            |      B      |
           |-------------|            |-------------|
           |      |  n1  |----------->|  p1  |      |
           |      |______|            |______|      |
           |      |  n2  |-----+  +-->|  p2  |      |
           |______|______|     |  |   |______|______|
                            +- | -+
                            |  |
            _____________   |  |      _____________
           |      C      |  |  |     |      D      |
           |-------------|  |  |     |-------------|
           |      |  nX  |--+  +---->|  pX  | data |------->[  Resource X  ]
           |______|______|           |______|______|

"""
__author__ = "Shawn Chin"
__version__ = "v1.2.1"

import re
import hashlib
import textwrap
from jinja2 import Environment
try:
    from collections import OrderedDict
except ImportError:
    from ordereddict import OrderedDict

class DuplicateEntry(Exception):
    """Raised when a duplicate element is added to the mesh.
    """

class InvalidComponent(Exception):
    """Raised when a referencing a component that does not exist in the mesh.
    """

class InvalidDomain(Exception):
    """Raised when a referencing a domain that does not exist in the mesh.
    """

class InvalidPort(Exception):
    """Raised when a referencing an invalid port on a component in the mesh.
    """

class InvalidConnection(Exception):
    """Raised when there is an attempt to create an invalid connection in the mesh.
    """

class InvalidResource(Exception):
    """Raised when there is an attempt to create an invalid resource in the mesh.
    """

class ConnectionNode(object):
    """Internal representation of a connection between components within the mesh.
    """
    def __init__(self, consumer, producer):
        """Instantiates a new connection between the given consumer and producer.

        :param tuple consumer: consumer of the connection defined as (consumer_component, consumer_port)
        :param tuple producer: producer of the connection defined as (producer_component, producer_port)
        """
        self.consumer = consumer
        self.producer = producer
        self.highlighted = False

    def as_dict(self):
        """Returns a dict representation of the connection.
        """
        consumer_component, consumer_port = self.consumer
        producer_component, producer_port = self.producer
        d = {
            'consumer_component': consumer_component,
            'consumer_port': consumer_port,
            'producer_component': producer_component,
            'producer_port': producer_port,
        }

        if self.highlighted:
            d['highlighted'] = True

        return d

class DomainNeedsConnectionNode(ConnectionNode):
    """Internal representation of component's port being exposed as that of its parent domain."""

    def as_dict(self):
        d = super(DomainNeedsConnectionNode, self).as_dict()
        d['domain_export'] = "needs"
        return d

class DomainProvidesConnectionNode(ConnectionNode):
    """Internal representation of component's port being exposed as that of its parent domain."""

    def as_dict(self):
        d = super(DomainProvidesConnectionNode, self).as_dict()
        d['domain_export'] = "provides"
        return d

class ResourceConnectionNode(ConnectionNode):
    """Internal representation of a connection between components and resources within the mesh.
    """
    def as_dict(self):
        """Returns a dict representation of the connection.
        """
        consumer_component, consumer_port = self.consumer
        d = {
            'consumer_component': consumer_component,
            'consumer_port': consumer_port,
            'resource': self.producer,
        }

        if self.highlighted:
            d['highlighted'] = True

        return d

class Mesh(object):
    """Light-weight representation of a hexagonal mesh that can drive template renderers (e.g. jinja2) to
    produce a textual representation of the mesh.
    """

    GRAPH_TEMPLATE = ''

    def __init__(self):
        self.components = OrderedDict()
        self.connections = OrderedDict()
        self.resources = []
        self._highlighted_resource = set()
        self.connected_consumers = set()

    def add_component(self, component_name, needs_ports=None, provides_ports=None):
        """Adds a component to the mesh.

        :param str component_name: Name of component to add
        :param list needs_ports: List of needs ports for this component
        :param list provides_ports: List of provides ports for this component
        :raises: DuplicateEntry if component of that name already exists
        """
        if component_name in self.components:
            raise DuplicateEntry('Component or Domain with name {0} already exists'.format(component_name))

        self.components[component_name] = ComponentNode(component_name)

        needs = needs_ports or tuple()
        for port_name in needs:
            self.add_needs_port(component_name, port_name)

        provides = provides_ports or tuple()
        for port_name in provides:
            self.add_provides_port(component_name, port_name)

    def add_resource(self, resource_name):
        """Adds a resource (adapter to external data) to the mesh.

        :param str resource_name: Name of resource to add
        :raises: DuplicateEntry if component of that name already exists
        """
        if resource_name in self.resources:
            raise DuplicateEntry('Resource with name {0} already exists'.format(resource_name))

        self.resources.append(resource_name)

    def add_needs_port(self, component_name, port_name):
        """Assigns an additional needs port to an existing component.

        :param str component_name: name of component to add the port to
        :param str port_name: name of needs port to add
        :raises: DuplicateEntry if that needs port already exists on the component
        """
        self.components[component_name].add_needs_port(port_name)

    def add_provides_port(self, component_name, port_name):
        """Assigns an additional provides port to an existing component.

        :param str component_name: name of component to add the port to
        :param str port_name: name of provides port to add
        :raises: DuplicateEntry if that provides port already exists on the component
        """
        self.components[component_name].add_provides_port(port_name)

    def add_connection(self, consumer_component, consumer_port, producer_component, producer_port):
        """Adds a connection between a needs port from a consumer component to the provides port of a producer.

        :param str consumer_component: name of the consumer component
        :param str consumer_port: name of the needs port of the consumer
        :param str producer_component: name of the producer component
        :param str producer_port: name of the provides port of the producer
        """
        try:
            consumer = self.components[consumer_component]
            consumer.assert_is_valid_needs_port(consumer_port)
        except KeyError:
            raise InvalidComponent('{0} component does not exist in the mesh'.format(consumer_component))

        try:
            producer = self.components[producer_component]
            producer.assert_is_valid_provides_port(producer_port)
        except KeyError:
            raise InvalidComponent('{0} component does not exist in the mesh'.format(producer_component))

        # FIXME: using label_for_* here leaks the requirements of a specific viz template (dot) to the conceptual model
        #        of the mesh. Not great. This is essentially a hack to get domains working for now. Need to rethink this
        consumer = consumer.label_for_needs, consumer_port
        producer = producer.label_for_provides, producer_port
        self._add_connection_between_consumer_and_producer(consumer, producer)

    def add_connection_to_resource(self, consumer_component, consumer_port, resource):
        """Adds a connection between a needs port from a consumer component to a resource.

        :param str consumer_component: name of the consumer component
        :param str consumer_port: name of the needs port of the consumer
        :param str resource: name of resource
        """
        try:
            consumer = self.components[consumer_component]
            consumer.assert_is_valid_needs_port(consumer_port)
        except KeyError:
            raise InvalidComponent('{0} component does not exist in the mesh'.format(consumer_component))

        if resource not in self.resources:
            raise InvalidResource('{0} resource does not exist in the mesh'.format(resource))

        consumer = consumer.label_for_needs, consumer_port
        self._add_connection_between_consumer_and_producer(consumer, resource, connectionClass=ResourceConnectionNode)

    def _add_connection_between_consumer_and_producer(self, consumer, producer, connectionClass=ConnectionNode):
        if consumer in self.connected_consumers:
            raise InvalidConnection('{0} already connected'.format(consumer))

        self.connections[consumer, producer] = (connectionClass(consumer, producer))
        self.connected_consumers.add(consumer)

    def add_domain(self, domain_name):
        """Creates a domain which groups together components as a single entity.

        :param str domain_name: name of domain to add
        :raises: DuplicateEntry if component or domain of that name already exists
        """
        if domain_name in self.components:
            raise DuplicateEntry('Component or Domain with name {0} already exists'.format(domain_name))

        self.components[domain_name] = DomainNode(domain_name)

    def add_component_to_domain(self, component_name, domain_name):
        """Adds a component into the given domain.

        :param str component_name: Name of the component
        :param str domain_name: Name of the domain
        """
        try:
            component = self.components[component_name]
        except KeyError:
            raise InvalidComponent('{0} component does not exist in the mesh'.format(component_name))

        # No reason why we cannot eventually support nested domains. We just need to test it out and potentially
        # generalise the way we set up connections between ports.
        if isinstance(component, DomainNode):
            raise InvalidComponent(
                'Cannot add {0} to {1}. Nested domains not yet supported'.format(component_name, domain_name))

        if component.parent:
            raise DuplicateEntry('Component {0} is already part of domain {1}'.format(component.name, component.parent))

        try:
            domain = self.components[domain_name]
        except KeyError:
            raise InvalidDomain('{0} domain does not exist in the mesh'.format(domain_name))

        component.parent = domain_name
        domain.add_child_component(component_name)

    def expose_component_needs_port(self, component_name, port_name):
        """Associated a component's need port to that of its parent domain.

        :param str component_name: Name of component
        :param str port_name: Name of needs port
        """
        try:
            component = self.components[component_name]
        except KeyError:
            raise InvalidComponent('{0} component does not exist in the mesh'.format(component_name))

        component.assert_is_valid_needs_port(port_name)

        try:
            domain = self.components[component.parent]
        except KeyError:
            raise InvalidDomain('Parent domain for {0} component is unspecified or invalid'.format(component_name))

        if port_name not in domain.needs_ports:
            domain.add_needs_port(port_name)

        consumer = component_name, port_name
        producer = domain.label_for_needs, port_name
        self._add_connection_between_consumer_and_producer(consumer, producer, connectionClass=DomainNeedsConnectionNode)

    def expose_component_provides_port(self, component_name, port_name):
        """Associated a component's provides port to that of its parent domain.

        :param str component_name: Name of component
        :param str port_name: Name of provides port
        """
        try:
            component = self.components[component_name]
        except KeyError:
            raise InvalidComponent('{0} component does not exist in the mesh'.format(component_name))

        component.assert_is_valid_provides_port(port_name)

        try:
            domain = self.components[component.parent]
        except KeyError:
            raise InvalidDomain('Parent domain for {0} component is unspecified or invalid'.format(component_name))

        if port_name in domain.provides_ports:
            raise DuplicateEntry('{0} domain already has exposed provides port for {1}'.format(domain.name, port_name))
        else:
            domain.add_provides_port(port_name)

        consumer = domain.label_for_provides, port_name
        producer = component_name, port_name
        self._add_connection_between_consumer_and_producer(consumer, producer, connectionClass=DomainProvidesConnectionNode)

    def highlight_component(self, component_name):
        """Highlights a component in the mesh.

        :param str component_name: name of component to highlight
        """
        try:
            self.components[component_name].highlighted = True
        except KeyError:
            raise InvalidComponent('{0} component does not exist in the mesh'.format(component_name))

    def highlight_connection(self, consumer_component, consumer_port, producer_component, producer_port):
        """Highlights a connection between a needs port from a consumer component to the provides port of a producer.

        :param str consumer_component: name of the consumer component
        :param str consumer_port: name of the needs port of the consumer
        :param str producer_component: name of the producer component
        :param str producer_port: name of the provides port of the producer
        """
        consumer = consumer_component, consumer_port
        producer = producer_component, producer_port
        try:
            self.connections[consumer, producer].highlighted = True
        except KeyError:
            raise InvalidConnection('Invalid Connection: {0} -> {1}'.format(consumer, producer))

    def highlight_connection_to_resource(self, consumer_component, consumer_port, resource):
        """Highlights a connection between a needs port and a resource.

        :param str consumer_component: name of the consumer component
        :param str consumer_port: name of the needs port of the consumer
        :param str resource: name of resource
        """
        consumer = consumer_component, consumer_port
        try:
            self.connections[consumer, resource].highlighted = True
        except KeyError:
            raise InvalidConnection('Invalid Connection: {0} -> {1}'.format(consumer, resource))

    def highlight_resource(self, resource):
        """Highlights a resource in the mesh.

        :param str resource: name of resource to highlight
        """
        if resource not in self.resources:
            raise InvalidResource('{0} resource does not exist in the mesh'.format(resource))

        self._highlighted_resource.add(resource)

    def as_dict(self):
        """Returns a dict representation of the mesh.
        """
        d = {
            'components': [c.as_dict() for c in self.components.values() if not isinstance(c, DomainNode)],
            'domains': [c.as_dict() for c in self.components.values() if isinstance(c, DomainNode)],
            'connections': [c.as_dict() for c in self.connections.values()],
            'resources': [r for r in self.resources],
        }

        if self._highlighted_resource:
            d['highlighted_resources'] = list(self._highlighted_resource)

        return d

class ComponentNode(object):
    """Internal representation of a Component within the mesh.
    """

    def __init__(self, name):
        """Instantiates the component node with a given name

        :param str name: component name
        """
        self.name = name
        self.needs_ports = []
        self.provides_ports = []
        self.highlighted = False
        self.parent = None

    def add_needs_port(self, port_name):
        """Assigns a needs port.

        :param str port_name: name of needs port to add
        :raises: DuplicateEntry if that needs port already exists
        """
        if port_name in self.needs_ports:
            raise DuplicateEntry('Needs port with name {0} already exists for {1}'.format(port_name, self.name))

        self.needs_ports.append(port_name)

    def add_provides_port(self, port_name):
        """Assigns a provides port.

        :param str port_name: name of provides port to add
        :raises: DuplicateEntry if that provides port already exists
        """
        if port_name in self.provides_ports:
            raise DuplicateEntry('Provides port with name {0} already exists for {1}'.format(port_name, self.name))

        self.provides_ports.append(port_name)

    def as_dict(self):
        """Returns a dict representation of the component.
        """
        d = {
            'name': self.name,
            'needs_ports': list(self.needs_ports),
            'provides_ports': list(self.provides_ports),
        }

        if self.highlighted:
            d['highlighted'] = True

        return d

    @property
    def label_for_needs(self):
        """Label used by connections to differentiate between needs and provides placeholders of components."""
        return self.name

    @property
    def label_for_provides(self):
        """Label used by connections to differentiate between needs and provides placeholders of components."""
        return self.name

    def assert_is_valid_needs_port(self, port_name):
        """Raises InvalidPort if given port is not a valid needs port.
        """
        if port_name not in self.needs_ports:
            raise InvalidPort('{0} is not a valid valid needs port for component {1}'.format(port_name, self.name))

    def assert_is_valid_provides_port(self, port_name):
        """Raises InvalidPort if given port is not a valid provides port.
        """
        if port_name not in self.provides_ports:
            raise InvalidPort('{0} is not a valid valid provides port for component {1}'.format(port_name, self.name))

class DomainNode(ComponentNode):
    def __init__(self, name):
        """Instantiates the domain node with a given name

        :param str name: domain name
        """
        self.name = name
        self.needs_ports = []
        self.provides_ports = []
        self.children = set()

    def add_child_component(self, component_name):
        """Adds a component as a child to this domain.

        :param str component_name: Name of component to add
        :raises: DuplicateEntry if compoent already a child of this domain.
        """
        if component_name in self.children:
            raise DuplicateEntry('{0} component is already a child of {1} domain'.format(component_name, self.name))

        self.children.add(component_name)

    def as_dict(self):
        """Returns a dict representation of the component.
        """
        d = {
            'name': self.name,
            'needs_ports': list(self.needs_ports),
            'provides_ports': list(self.provides_ports),
            'children': list(self.children),
            'label_for_needs': self.label_for_needs,
            'label_for_provides': self.label_for_provides,
        }
        return d

    @property
    def label_for_needs(self):
        """Label used by connections to differentiate between needs and provides placeholders of components."""
        return self.name + "__needs"

    @property
    def label_for_provides(self):
        """Label used by connections to differentiate between needs and provides placeholders of components."""
        return self.name + "__provides"

    def assert_is_valid_needs_port(self, port_name):
        """Raises InvalidPort if given port is not a valid needs port.
        """
        if port_name not in self.needs_ports:
            raise InvalidPort('{0} is not a valid needs port for component {1}'.format(port_name, self.name))

    def assert_is_valid_provides_port(self, port_name):
        """Raises InvalidPort if given port is not a valid provides port.
        """
        if port_name not in self.provides_ports:
            raise InvalidPort('{0} is not a valid provides port for component {1}'.format(port_name, self.name))

def render(mesh, template, custom_filters=None):
    """Renders the given mesh using the template text provided.

    The template and filters should be compatible with jinja2

    :param str template: the template text to be used for rendering
    :param dict custom_filters: dict of custom filters where the key is the filter tag and the value is the callables
    :returns: rendered textual representation of the mesh
    """
    jinja_env = Environment()
    if custom_filters:
        jinja_env.filters.update(custom_filters)

    jinja_template = jinja_env.from_string(template)

    return jinja_template.render(mesh.as_dict())

DOT_TEMPLATE = textwrap.dedent('''
    digraph G {

        rankdir=LR;
        node [shape=plaintext];

        {% for component in components %}
        {{ component.name|hash }} [label=<
        <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4"{% if component.highlighted %} BGCOLOR="yellow"{% endif %}>
        <TR>
            <TD COLSPAN="2"> {{ component.name|escape }}</TD>
        </TR>
        <TR>
            <TD>
                {% if component.provides_ports %}
                <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
                {% for port in component.provides_ports %}
                <TR><TD PORT="{{ port|hash_p }}" BGCOLOR="grey">{{ port|escape }}</TD></TR>
                {% endfor %}
                </TABLE>
                {% else %} {% endif %}
            </TD>
            <TD>
                {% if component.needs_ports %}
                <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
                {% for port in component.needs_ports %}
                <TR><TD PORT="{{ port|hash }}" BGCOLOR="grey">{{ port|escape }}</TD></TR>
                {% endfor %}
                </TABLE>
                {% else %} {% endif %}
            </TD>
        </TR>

        </TABLE>>];
        {% endfor %}

        {% for domain in domains %}
        subgraph cluster_domain_{{ domain.name|hash }} {
            label="{{ domain.name }}";
            style="rounded";
            rank=same;

            {% if domain.provides_ports %}
            {{ domain.label_for_provides|hash }} [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="4" CELLPADDING="4">
                {% for port in domain.provides_ports %}
                <TR><TD PORT="{{ port|hash_p }}" BGCOLOR="lightgrey">{{ port|escape }}</TD></TR>
                {% endfor %}
            </TABLE>>];
            {% endif %}

            subgraph cluster_domain_{{ domain.name|hash }}_services {
                style="rounded,dashed";
                label="";

                {% for child in domain.children %}
                {{ child|hash }};
                {% endfor %}
            }

            {% if domain.needs_ports %}
            {{ domain.label_for_needs|hash }} [label=<<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="4" CELLPADDING="4">
                {% for port in domain.needs_ports %}
                <TR><TD PORT="{{ port|hash }}" BGCOLOR="lightgrey">{{ port|escape }}</TD></TR>
                {% endfor %}
            </TABLE>>];
            {% endif %}
        }
        {% endfor %}

        {% for resource in resources %}
        {{ resource|hash_p }} [shape="rect";label="{{ resource }}", style="dashed"{% if resource in highlighted_resources %}, color="red"{% endif %}];
        {% endfor %}

        {% for conn in connections %}
        {% if "resource" in conn %}
        {{ conn.consumer_component|hash }}:{{ conn.consumer_port|hash }} -> {{ conn.resource|hash_p }} [style="dashed"{% if conn.highlighted %}, color="red"{% endif %}];
        {% elif "domain_export" in conn %}
        {% if conn.domain_export == "needs" %}
        {{ conn.consumer_component|hash }}:{{ conn.consumer_port|hash }} -> {{ conn.producer_component|hash }}:{{ conn.producer_port|hash }} [color="grey",arrowhead="dot"];
        {% else %}
        {{ conn.consumer_component|hash }}:{{ conn.consumer_port|hash_p }} -> {{ conn.producer_component|hash }}:{{ conn.producer_port|hash_p }} [color="grey",dir="back",arrowtail="dot"];
        {% endif %}
        {% else %}
        {{ conn.consumer_component|hash }}:{{ conn.consumer_port|hash }} -> {{ conn.producer_component|hash }}:{{ conn.producer_port|hash_p }}{% if conn.highlighted %}[color="red"]{% endif %};
        {% endif %}
        {% endfor %}
    }
''').lstrip()

def render_mesh_as_dot(mesh, template=DOT_TEMPLATE):
    """Renders the given mesh in the Graphviz dot format.

    :param Mesh mesh: the mesh to be rendered
    :param str template: alternative template to use
    :returns: textual dot representation of the mesh
    """

    custom_filters = {
        'hash': lambda s: "id" + hashlib.md5(s).hexdigest()[:6],
        # alternative hash for provides ports to avoid conflicts with needs ports with same name
        'hash_p': lambda s: "idp" + hashlib.md5(s).hexdigest()[:6],
        'escape': lambda s: re.sub(r'([{}|"<>])', r'\\\1', s),
    }

    return render(mesh, template, custom_filters=custom_filters)


import math
import os
import uuid
from glob import glob
from typing import Union
import json
import cv2
import numpy as np
import torch
import webcolors
from torch import nn
from torch.nn.init import _calculate_fan_in_and_fan_out, _no_grad_normal_
from torchvision.ops.boxes import batched_nms
from .bb_polygon import is_bounding_box_intersect
import torchvision
import sys
sys.path.append('..')

def one_hot_embedding(labels, num_classes):
    '''
    Embedding labels to one-hot form.

    :param labels: (LongTensor) class labels, sized [N,].
    :param num_classes: (int) number of classes.
    :return: (tensor) encoded labels, sized [N,#classes].
    '''
    y = torch.eye(num_classes)  # [D,D]
    return y[labels]            # [N,D]

def box_nms(boxes, scores, threshold=0.5):
    """
    Non Maximum Suppression
    Use custom (very slow) or torchvision non-maximum supression on bounding boxes

    :param bboxes: (tensor) bounding boxes, size [N, 4]
    :param scores: (tensor) bbox scores, sized [N]
    :return: keep: (tensor) selected box's indices
    """

    # Torchvision NMS:
    keep = torchvision.ops.boxes.nms(boxes, scores,threshold)
    return keep

    # Custom NMS: uncomment to use
    """x1 = bboxes[:, 0]
    y1 = bboxes[:, 1]
    x2 = bboxes[:, 2]
    y2 = bboxes[:, 3]

    areas = (x2 - x1) * (y2 - y1)
    _, order = scores.sort(0, descending=True)
    keep = []
    while order.numel() > 0:
        try:
            i = order[0]
        except IndexError:
            break
        keep.append(i)

        if order.numel() == 1:
            break

        xx1 = x1[order[1:]].clamp(min=x1[i].item())
        yy1 = y1[order[1:]].clamp(min=y1[i].item())
        xx2 = x2[order[1:]].clamp(max=x2[i].item())
        yy2 = y2[order[1:]].clamp(max=y2[i].item())

        w = (xx2 - xx1).clamp(min=0)
        h = (yy2 - yy1).clamp(min=0)
        inter = w * h

        if mode == 'union':
            ovr = inter / (areas[i] + areas[order[1:]] - inter)
        elif mode == 'min':
            ovr = inter / areas[order[1:]].clamp(max=areas[i])
        else:
            raise TypeError('Unknown nms mode: %s.' % mode)

        ids = (ovr < threshold).nonzero().squeeze()
        if ids.numel() == 0:
            break
        # because the length of the ovr is less than the order by 1
        # so we have to add to ids to get the right one
        order = order[ids + 1]
    return torch.LongTensor(keep)"""

def clip_gradient(optimizer, grad_clip):
    """
    Clips gradients computed during backpropagation to avoid explosion of gradients.

    :param optimizer: optimizer with the gradients to be clipped
    :param grad_clip: clip value
    """
    for group in optimizer.param_groups:
        for param in group['params']:
            if param.grad is not None:
                param.grad.data.clamp_(-grad_clip, grad_clip)

def change_box_order(boxes, order):
    """
    Change box order between (xmin, ymin, xmax, ymax) and (xcenter, ycenter, width, height).

    :param boxes: (tensor) or {np.array) bounding boxes, sized [N, 4]
    :param order: (str) ['xyxy2xywh', 'xywh2xyxy', 'xyxy2cxcy', 'cxcy2xyxy']
    :return: (tensor) converted bounding boxes, size [N, 4]
    """

    assert order in ['xyxy2xywh', 'xywh2xyxy', 'xyxy2cxcy', 'cxcy2xyxy']

    # Convert 1-d to a 2-d tensor of boxes, which first dim is 1
    if isinstance(boxes, torch.Tensor):
        if len(boxes.shape) == 1:
            boxes = boxes.unsqueeze(0)

        if order == 'xyxy2xywh':
            return torch.cat([boxes[:, :2], boxes[:, 2:] - boxes[:, :2]], 1)
        elif order ==  'xywh2xyxy':
            return torch.cat([boxes[:, :2], boxes[:, :2] + boxes[:, 2:]], 1)
        elif order == 'xyxy2cxcy':
            return torch.cat([(boxes[:, 2:] + boxes[:, :2]) / 2,  # c_x, c_y
                            boxes[:, 2:] - boxes[:, :2]], 1)  # w, h
        elif order == 'cxcy2xyxy':
            return torch.cat([boxes[:, :2] - (boxes[:, 2:] *1.0 / 2),  # x_min, y_min
                            boxes[:, :2] + (boxes[:, 2:] *1.0 / 2)], 1)  # x_max, y_max
    else:
        # Numpy
        new_boxes = boxes.copy()
        if order == 'xywh2xyxy':
            new_boxes[:,2] = boxes[:,0] + boxes[:,2]
            new_boxes[:,3] = boxes[:,1] + boxes[:,3]
            return new_boxes
        elif order == 'xyxy2xywh':
            new_boxes[:,2] = boxes[:,2] - boxes[:,0]
            new_boxes[:,3] = boxes[:,3] - boxes[:,1]
            return new_boxes

def find_intersection(set_1, set_2):
    """
    Find the intersection of every box combination between two sets of boxes that are in boundary coordinates.

    :param set_1: set 1, a tensor of dimensions (n1, 4)
    :param set_2: set 2, a tensor of dimensions (n2, 4)
    :return: intersection of each of the boxes in set 1 with respect to each of the boxes in set 2, a tensor of dimensions (n1, n2)
    """

    # PyTorch auto-broadcasts singleton dimensions
    lower_bounds = torch.max(set_1[:, :2].unsqueeze(1), set_2[:, :2].unsqueeze(0))  # (n1, n2, 2)
    upper_bounds = torch.min(set_1[:, 2:].unsqueeze(1), set_2[:, 2:].unsqueeze(0))  # (n1, n2, 2)
    intersection_dims = torch.clamp(upper_bounds - lower_bounds, min=0)  # (n1, n2, 2)
    return intersection_dims[:, :, 0] * intersection_dims[:, :, 1]  # (n1, n2)

def find_jaccard_overlap(set_1, set_2, order='xyxy'):
    """
    Find the Jaccard Overlap (IoU) of every box combination between two sets of boxes that are in boundary coordinates.
    The default box order is (xmin, ymin, xmax, ymax).

    :param set_1: set 1, a tensor of dimensions (n1, 4)
    :param set_2: set 2, a tensor of dimensions (n2, 4)
    :return: Jaccard Overlap of each of the boxes in set 1 with respect to each of the boxes in set 2, a tensor of dimensions (n1, n2)
    """

    if order == 'xywh':
        set_1 = change_box_order(set_1, 'xywh2xyxy')
        set_2 = change_box_order(set_2, 'xywh2xyxy')

    # Find intersections
    intersection = find_intersection(set_1, set_2)  # (n1, n2)

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

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

    return intersection / union  # (n1, n2)

def postprocessing(outs, imgs, retransforms = None, out_format='xyxy'):
    for item in outs:

        boxes_out = item['bboxes']
        boxes_out_xywh = change_box_order(boxes_out, order = 'xyxy2xywh')
        new_boxes = retransforms(img = imgs, box=boxes_out_xywh)['box']
        if out_format == 'xyxy':
            new_boxes = change_box_order(new_boxes, order = 'xywh2xyxy')
        item['bboxes'] = new_boxes

    return outs

def display(preds, imgs, obj_list, imshow=True, imwrite=False):
    for i in range(len(imgs)):
        if len(preds[i]['rois']) == 0:
            continue

        imgs[i] = imgs[i].copy()

        for j in range(len(preds[i]['rois'])):
            (x1, y1, x2, y2) = preds[i]['rois'][j].astype(np.int)
            obj = obj_list[preds[i]['class_ids'][j]]
            score = float(preds[i]['scores'][j])

            plot_one_box(imgs[i], [x1, y1, x2, y2], label=obj, score=score,
                         color=color_list[get_index_label(obj, obj_list)])
        if imshow:
            cv2.imshow('img', imgs[i])
            cv2.waitKey(0)

        if imwrite:
            os.makedirs('test/', exist_ok=True)
            cv2.imwrite(f'test/{uuid.uuid4().hex}.jpg', imgs[i])

def init_weights(model):
    for name, module in model.named_modules():
        is_conv_layer = isinstance(module, nn.Conv2d)

        if is_conv_layer:
            if "conv_list" or "header" in name:
                variance_scaling_(module.weight.data)
            else:
                nn.init.kaiming_uniform_(module.weight.data)

            if module.bias is not None:
                if "classifier.header" in name:
                    bias_value = -np.log((1 - 0.01) / 0.01)
                    torch.nn.init.constant_(module.bias, bias_value)
                else:
                    module.bias.data.zero_()

def variance_scaling_(tensor, gain=1.):
    # type: (Tensor, float) -> Tensor
    r"""
    initializer for SeparableConv in Regressor/Classifier
    reference: https://keras.io/zh/initializers/  VarianceScaling
    """
    fan_in, fan_out = _calculate_fan_in_and_fan_out(tensor)
    std = math.sqrt(gain / float(fan_in))

    return _no_grad_normal_(tensor, 0., std)

STANDARD_COLORS = [
    'LawnGreen', 'LightBlue' , 'Crimson', 'Gold', 'Azure', 'BlanchedAlmond', 'Bisque',
    'Aquamarine', 'BlueViolet', 'BurlyWood', 'CadetBlue', 'AntiqueWhite',
    'Chocolate', 'Coral', 'CornflowerBlue', 'Cornsilk', 'Crimson', 'Cyan',
    'DarkCyan', 'DarkGoldenRod', 'DarkGrey', 'DarkKhaki', 'DarkOrange',
    'DarkOrchid', 'DarkSalmon', 'DarkSeaGreen', 'DarkTurquoise', 'DarkViolet',
    'DeepPink', 'DeepSkyBlue', 'DodgerBlue', 'FireBrick', 'FloralWhite',
    'ForestGreen', 'Fuchsia', 'Gainsboro', 'GhostWhite', 'Gold', 'GoldenRod',
    'Salmon', 'Tan', 'HoneyDew', 'HotPink', 'IndianRed', 'Ivory', 'Khaki',
    'Lavender', 'LavenderBlush', 'AliceBlue', 'LemonChiffon', 'LightBlue',
    'LightCoral', 'LightCyan', 'LightGoldenRodYellow', 'LightGray', 'LightGrey',
    'LightGreen', 'LightPink', 'LightSalmon', 'LightSeaGreen', 'LightSkyBlue',
    'LightSlateGray', 'LightSlateGrey', 'LightSteelBlue', 'LightYellow', 'Lime',
    'LimeGreen', 'Linen', 'Magenta', 'MediumAquaMarine', 'MediumOrchid',
    'MediumPurple', 'MediumSeaGreen', 'MediumSlateBlue', 'MediumSpringGreen',
    'MediumTurquoise', 'MediumVioletRed', 'MintCream', 'MistyRose', 'Moccasin',
    'NavajoWhite', 'OldLace', 'Olive', 'OliveDrab', 'Orange', 'OrangeRed',
    'Orchid', 'PaleGoldenRod', 'PaleGreen', 'PaleTurquoise', 'PaleVioletRed',
    'PapayaWhip', 'PeachPuff', 'Peru', 'Pink', 'Plum', 'PowderBlue', 'Purple',
    'Red', 'RosyBrown', 'RoyalBlue', 'SaddleBrown', 'Green', 'SandyBrown',
    'SeaGreen', 'SeaShell', 'Sienna', 'Silver', 'SkyBlue', 'SlateBlue',
    'SlateGray', 'SlateGrey', 'Snow', 'SpringGreen', 'SteelBlue', 'GreenYellow',
    'Teal', 'Thistle', 'Tomato', 'Turquoise', 'Violet', 'Wheat', 'White',
    'WhiteSmoke', 'Yellow', 'YellowGreen'
]

def from_colorname_to_bgr(color):
    rgb_color = webcolors.name_to_rgb(color)
    result = (rgb_color.blue, rgb_color.green, rgb_color.red)
    return result

def standard_to_bgr(list_color_name):
    standard = []
    for i in range(len(list_color_name) - 36):  # -36 used to match the len(obj_list)
        standard.append(from_colorname_to_bgr(list_color_name[i]))
    return standard

def plot_one_box(img, coord, label=None, score=None, color=None, line_thickness=None):
    tl = line_thickness or int(round(0.001 * max(img.shape[0:2])))  # line thickness
    color = color
    c1, c2 = (int(coord[0]), int(coord[1])), (int(coord[2]), int(coord[3]))
    cv2.rectangle(img, c1, c2, color, thickness=tl)
    if label:
        tf = max(tl - 2, 1)  # font thickness
        s_size = cv2.getTextSize(str('{:.0%}'.format(score)), 0, fontScale=float(tl) / 3, thickness=tf)[0]
        t_size = cv2.getTextSize(label, 0, fontScale=float(tl) / 3, thickness=tf)[0]
        c2 = c1[0] + t_size[0] + s_size[0] + 15, c1[1] - t_size[1] - 3
        cv2.rectangle(img, c1, c2, color, -1)  # filled
        cv2.putText(img, '{}: {:.0%}'.format(label, score), (c1[0], c1[1] - 2), 0, float(tl) / 3, [0, 0, 0],
                    thickness=tf, lineType=cv2.FONT_HERSHEY_SIMPLEX)

color_list = standard_to_bgr(STANDARD_COLORS)

def boolean_string(s):
    if s not in {'False', 'True'}:
        raise ValueError('Not a valid boolean string')
    return s == 'True'

# Additionals

def re_id(outputs, ori_img, labels, polygons=None):
    results= []
    if len(outputs) > 0:
        bbox_tlwh = []
        bbox_xyxy = outputs[:, :4]
        identities = outputs[:, -1]
        ori_im = draw_boxes(ori_img, bbox_xyxy, identities, labels)

    else:
        ori_im = ori_img
    return ori_im

def draw_boxes(img, bbox, identities=None, labels = None):
    for i,box in enumerate(bbox):
        x1,y1,x2,y2 = [int(i) for i in box]
        # box text and bar
        id = int(identities[i]) if identities is not None else 0
        color = color_list[labels]
        label = '{}id:{:d} c:{}'.format("", id, str(labels+1))
        t_size = cv2.getTextSize(label, cv2.FONT_HERSHEY_PLAIN, 1 , 1)[0]
        try:
            cv2.rectangle(img,(x1, y1),(x2,y2),color,2)
            cv2.rectangle(img,(x1, y1),(x1+t_size[0]+3,y1+t_size[1]+4), color,-1)
            cv2.putText(img,label,(x1,y1+t_size[1]+4), cv2.FONT_HERSHEY_PLAIN, 1, [255,255,255], 2)
        except:
            pass
    return img

def draw_boxes_v2(img, preds, obj_list):
    bboxes = preds['bboxes']
    labels = preds['classes']
    scores = preds['scores']
    for i, box in enumerate(bboxes):
        x1,y1,x2,y2 = [int(i) for i in box]

        label = labels[i]
        score = np.round(scores[i], 3)
        color = color_list[label]
        text = '{}: {}'.format(obj_list[label], str(score))

        t_size = cv2.getTextSize(text, cv2.FONT_HERSHEY_PLAIN, 1 , 2)[0]

        cv2.rectangle(img,(x1, y1),(x2,y2),color,2)
        cv2.rectangle(img,(x1, y1),(x1+t_size[0]+3,y1+t_size[1]+4), color,-1)
        cv2.putText(img,text,(x1,y1+t_size[1]+4), cv2.FONT_HERSHEY_PLAIN, 1, [255,255,255], 2)
    return img

def display_img(preds, imgs, imshow=True,  outvid = None, obj_list=None):

    for i in range(len(imgs)):
        if len(preds[i]['bboxes']) == 0:
            continue

        imgs[i] = imgs[i].copy()

        for j in range(len(preds[i]['bboxes'])):
            x1, y1, x2, y2 = preds[i]['bboxes'][j].astype(np.int)
            obj = obj_list[preds[i]['classes'][j]]
            score = float(preds[i]['scores'][j])
            plot_one_box(imgs[i], [x1, y1, x2, y2], label=obj,score=score,color=color_list[preds[i]['classes'][j]])

        if imshow:

            cv2.namedWindow('img',cv2.WINDOW_NORMAL)
            cv2.resizeWindow('image', 600,600)
            cv2.imshow('img', imgs[i])
            cv2.waitKey(1)

        if outvid is not None:
            outvid.write(imgs[i])

def cosin_similarity(a2d, b2d):
    a = np.array((a2d[1][0] - a2d[0][0], a2d[1][1 ]- a2d[0][1]))
    b = np.array((b2d[1][0] - b2d[0][1], b2d[1][1] - b2d[1][0]))
    return np.dot(a, b)/(np.linalg.norm(a)*np.linalg.norm(b))

def distance_point_line(line, p3): # line(p1,p2), point(p3)
    p1, p2 = line
    p1 = np.array(p1)
    p2 = np.array(p2)
    p3 = np.array(p3)
    return np.linalg.norm(np.cross(p2-p1, p1-p3))/np.linalg.norm(p2-p1)

def counting_moi_cosine(paths, obj_tracks, polygons):
    moi_detection_list = []
    for i in range(len(obj_tracks)):
        for obj_id, obj_list in obj_tracks[i].items():

            # Most frequency class
            true_label = max(set(obj_list['labels']), key = obj_list['labels'].count)
            last_frame = obj_list['frame_id'][-1]

            max_cosin = -2
            movement_id = 1
            first_point = obj_list['coords'][0]
            last_point = obj_list['coords'][-1]
            obj_vector = (first_point, last_point)
            for movement_label, movement_vector in paths.items():
                cosin = cosin_similarity(obj_vector,movement_vector)
                if cosin > max_cosin:
                    max_cosin = cosin
                    movement_id = movement_label

            moi_detection_list.append((obj_id, last_frame, movement_id, true_label))
    return moi_detection_list

def counting_moi_distance(paths, obj_tracks, polygons, cam_id):
    """
    Args:
    paths: List of MOI - (first_point, last_point)
    moto_vector_list: List of tuples (first_point, last_point, last_frame_id)

    Returns:
    A list of tuples (frame_id, movement_id, vehicle_class_id)
    """

    s = [[polygons[i],polygons[0]] if i == len(polygons)-1 else [polygons[i], polygons[i+1]] for i in range(len(polygons))]
    movement_dict = {
        #7: [[None,None,'06','01'], [None,None,'06','01'], [None,'03',None,'04'], [None,'02','05', None]],
        #9: [['05','01','01','04'], ['02',None,'01','03'], ['02','02','06','03'], ['05','06','06', '04']],
        #10: [['03','04',None,'05'], ['03','02','01','01'], ['06','02',None,'01'], ['06','02','02', '05']],

    }
    movements = movement_dict[cam_id]
    moi_detection_list = []
    for i in range(len(obj_tracks)):
        for obj_id, obj_list in obj_tracks[i].items():

            # Most frequency class
            true_label = max(set(obj_list['labels']), key = obj_list['labels'].count)
            last_frame = obj_list['frame_id'][-1]

            movement_id = 1
            first_point = obj_list['coords'][0]
            last_point = obj_list['coords'][-1]

            minn = 10000
            minn2 = 10000
            min_id = None
            min_id2 = None
            for idx, i in enumerate(s):
                dist1 = distance_point_line(i, first_point)
                dist2 = distance_point_line(i, last_point)
                if  dist1 <= minn:
                    minn = dist1
                    min_id = idx
                if  dist2 <= minn2:
                    minn2 = dist2
                    min_id2 = idx

            if min_id is None or min_id2 is None:
                continue
            movement_id = movements[min_id][min_id2]
            if movement_id is None:
                continue

            moi_detection_list.append((obj_id, last_frame, movement_id, true_label))
    return moi_detection_list

def counting_moi(paths, obj_tracks, polygons, cam_id):
    # This is used for roads have intersect
    intersect = []
    if cam_id in intersect:
        moi_detection_list = counting_moi_distance(paths, obj_tracks, polygons, cam_id)
    else:
        moi_detection_list = counting_moi_cosine(paths, obj_tracks, polygons) # Two-way road
    return moi_detection_list

def check_bbox_intersect_polygon(polygon, bbox):
    """

    Args:
        polygon: List of points (x,y)
        bbox: A tuple (xmin, ymin, xmax, ymax)

    Returns:
        True if the bbox intersect the polygon
    """
    x1, y1, x2, y2 = bbox
    bb = [(x1,y1), (x2, y1), (x2,y2), (x1,y2)]
    return is_bounding_box_intersect(bb, polygon)


# encoding: utf8
from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        (u'series', '0003_seriesrelease_tags'),
    ]

    operations = [
        migrations.AlterField(
            model_name='seriessubtitle',
            name='file',
            field=models.FileField(help_text=u'A f\xe1jln\xe9v a release szab\xe1lyok alapj\xe1n lesz t\xe1rolva.', upload_to=u'subtitles', null=True, verbose_name=u'Feliratf\xe1jl', blank=True),
        ),
        migrations.AlterField(
            model_name='seriessubtitle',
            name='filename',
            field=models.CharField(help_text=u'Kiz\xe1r\xf3lag akkor t\xf6ltsd ki ha a filen\xe9v elt\xe9r a release-ben megadott formai jel\xf6l\xe9st\u0151l (pl. dupla r\xe9sz, elt\xe9r\u0151 filen\xe9v, stb.)', max_length=255, verbose_name=u'Egyedi f\xe1jln\xe9v', blank=True),
        ),
        migrations.AlterField(
            model_name='seriessubtitle',
            name='double',
            field=models.BooleanField(default=False, verbose_name=u'Dupla r\xe9sz'),
        ),
    ]


from ctypes import *

import ctypes.util;

import numpy as np

class EBUR128_State(Structure):
	_fields_ = [
		("mode", c_int),
		("channels", c_uint),
		("samplerate", c_ulong),
		("ebur128_state_internal", c_void_p)
	]

libebur128 = cdll.LoadLibrary(ctypes.util.find_library("ebur128"))

ebur128_get_version = libebur128.ebur128_get_version
ebur128_get_version.argtypes = [POINTER(c_int), POINTER(c_int), POINTER(c_int)]
ebur128_get_version.restype = None

ebur128_init = libebur128.ebur128_init
ebur128_init.argtypes = [c_uint, c_ulong, c_int]
ebur128_init.restype = POINTER(EBUR128_State)

ebur128_destroy = libebur128.ebur128_destroy
ebur128_destroy.argtypes = [POINTER(POINTER(EBUR128_State))]
ebur128_destroy.restype = None

ebur128_set_channel = libebur128.ebur128_set_channel
ebur128_set_channel.argtypes = [POINTER(EBUR128_State), c_uint, c_int]
ebur128_set_channel.restype = c_int

ebur128_change_parameters = libebur128.ebur128_change_parameters
ebur128_change_parameters.argtypes = [POINTER(EBUR128_State), c_uint, c_ulong]
ebur128_change_parameters.restype = c_int

ebur128_add_frames_float = libebur128.ebur128_add_frames_float
ebur128_add_frames_float.argtypes = [POINTER(EBUR128_State), np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags='C_CONTIGUOUS'), c_uint]
ebur128_add_frames_float.restype = c_int

ebur128_add_frames_int = libebur128.ebur128_add_frames_int
ebur128_add_frames_int.argtypes = [POINTER(EBUR128_State), np.ctypeslib.ndpointer(dtype=np.int32, ndim=1, flags='C_CONTIGUOUS'), c_uint]
ebur128_add_frames_int.restype = c_int

ebur128_add_frames_short = libebur128.ebur128_add_frames_short
ebur128_add_frames_short.argtypes = [POINTER(EBUR128_State), np.ctypeslib.ndpointer(dtype=np.int16, ndim=1, flags='C_CONTIGUOUS'), c_uint]
ebur128_add_frames_short.restype = c_int

ebur128_add_frames_double = libebur128.ebur128_add_frames_double
ebur128_add_frames_double.argtypes = [POINTER(EBUR128_State), np.ctypeslib.ndpointer(dtype=np.float64, ndim=1, flags='C_CONTIGUOUS'), c_uint]
ebur128_add_frames_double.restype = c_int

ebur128_loudness_global = libebur128.ebur128_loudness_global
ebur128_loudness_global.argtypes = [POINTER(EBUR128_State), POINTER(c_double)]
ebur128_loudness_global.restype = c_int

ebur128_loudness_global_multiple = libebur128.ebur128_loudness_global_multiple
ebur128_loudness_global_multiple.argtypes = [POINTER(EBUR128_State), c_size_t, POINTER(c_double)]
ebur128_loudness_global_multiple.restype = c_int

ebur128_loudness_momentary = libebur128.ebur128_loudness_momentary
ebur128_loudness_momentary.argtypes = [POINTER(EBUR128_State), POINTER(c_double)]
ebur128_loudness_momentary.restype = c_int

ebur128_loudness_shortterm = libebur128.ebur128_loudness_shortterm
ebur128_loudness_shortterm.argtypes = [POINTER(EBUR128_State), POINTER(c_double)]
ebur128_loudness_shortterm.restype = c_int

ebur128_loudness_range = libebur128.ebur128_loudness_range
ebur128_loudness_range.argtypes = [POINTER(EBUR128_State), POINTER(c_double)]
ebur128_loudness_range.restype = c_int

ebur128_loudness_range_multiple = libebur128.ebur128_loudness_range_multiple
ebur128_loudness_range_multiple.argtypes = [POINTER(EBUR128_State), c_size_t, POINTER(c_double)]
ebur128_loudness_range_multiple.restype = c_int

ebur128_sample_peak = libebur128.ebur128_sample_peak
ebur128_sample_peak.argtypes = [POINTER(EBUR128_State), c_uint, POINTER(c_double)]
ebur128_sample_peak.restype = c_int

# ebur128_true_peak = libebur128.ebur128_true_peak
# ebur128_true_peak.argtypes = [POINTER(EBUR128_State), c_uint, POINTER(c_double)]
# ebur128_true_peak.restype = c_int

EBUR128_UNUSED = 0
EBUR128_LEFT = 1
EBUR128_RIGHT = 2
EBUR128_CENTER = 3
EBUR128_LEFT_SURROUND = 4
EBUR128_RIGHT_SURROUND = 5
EBUR128_DUAL_MONO = 6

EBUR128_SUCCESS = 0
EBUR128_ERROR_NOMEM = 1
EBUR128_ERROR_INVALID_MODE = 2
EBUR128_ERROR_INVALID_CHANNEL_INDEX = 3
EBUR128_ERROR_NO_CHANGE = 4

EBUR128_MODE_M = (1 << 0)
EBUR128_MODE_S = (1 << 1) | EBUR128_MODE_M
EBUR128_MODE_I = (1<<2) | EBUR128_MODE_M
EBUR128_MODE_LRA = (1<<3) | EBUR128_MODE_S
EBUR128_MODE_SAMPLE_PEAK = (1<<4) | EBUR128_MODE_M
EBUR128_MODE_TRUE_PEAK = (1<<5) | EBUR128_MODE_M | EBUR128_MODE_SAMPLE_PEAK
EBUR128_MODE_HISTOGRAM = 1<<6

class EBUR128:
	def __init__(self, channels, samplerate, mode):

		major = c_int()
		minor = c_int()
		patch = c_int()

		ebur128_get_version(byref(major), byref(minor), byref(patch))

		self.major = major.value
		self.minor = minor.value
		self.patch = patch.value

		self.channels = channels
		self.samplerate = samplerate
		self.mode = mode

		print("libebur128 version: %d.%d.%d"%(self.major, self.minor, self.patch))

		self.state = ebur128_init(c_uint(self.channels), c_ulong(self.samplerate), c_int(self.mode))

	def add_frames(self, src):
		numframes = src.shape[0]/self.channels
		if src.dtype == np.float32:
			if EBUR128_ERROR_NOMEM == ebur128_add_frames_float(self.state, src, numframes):
				raise RuntimeError("EBUR128_ERROR_NOMEM")
		elif src.dtype == np.float64:
			if EBUR128_ERROR_NOMEM == ebur128_add_frames_double(self.state, src, numframes):
				raise RuntimeError("EBUR128_ERROR_NOMEM")
		if src.dtype == np.int16:
			if EBUR128_ERROR_NOMEM == ebur128_add_frames_short(self.state, src, numframes):
				raise RuntimeError("EBUR128_ERROR_NOMEM")
		if src.dtype == np.int32:
			if EBUR128_ERROR_NOMEM == ebur128_add_frames_int(self.state, src, numframes):
				raise RuntimeError("EBUR128_ERROR_NOMEM")

	def change_parameters(self, channels, samplerate):
		err = ebur128_change_parameters(self.state, channels, samplerate)
		if EBUR128_ERROR_NOMEM == err:
			raise RuntimeError("EBUR128_ERROR_NOMEM")
		elif EBUR128_ERROR_NO_CHANGE == err:
			raise RuntimeWarning("EBUR128_ERROR_NO_CHANGE")

	def set_channel(self, channel_number, value):
		if EBUR128_ERROR_INVALID_CHANNEL_INDEX == ebur128_set_channel(self.state, channel_number, value):
			raise RuntimeError("EBUR128_ERROR_INVALID_CHANNEL_INDEX")

	def loudness_global(self):
		l = c_double()
		if EBUR128_ERROR_INVALID_MODE == ebur128_loudness_global(self.state, byref(l)):
			raise RuntimeError("EBUR128_ERROR_INVALID_MODE")
		return l.value

	def loudness_momentary(self):
		l = c_double()
		if EBUR128_ERROR_INVALID_MODE == ebur128_loudness_momentary(self.state, byref(l)):
			raise RuntimeError("EBUR128_ERROR_INVALID_MODE")
		return l.value

	def loudness_shortterm(self):
		l = c_double()
		if EBUR128_ERROR_INVALID_MODE == ebur128_loudness_shortterm(self.state, byref(l)):
			raise RuntimeError("EBUR128_ERROR_INVALID_MODE")
		return l.value

	def loudness_range(self):
		l = c_double()
		if EBUR128_ERROR_INVALID_MODE == ebur128_loudness_range(self.state, byref(l)):
			raise RuntimeError("EBUR128_ERROR_INVALID_MODE")
		return l.value

	def __del__(self):
		ebur128_destroy(byref(self.state))


"""
Created on Thu Dec  7 10:41:24 2017

@author: mwatson
"""

import pandas as pd

# I cut 2009 from the list because the tracts changed in 2010, it is simplifying for now
year_list =["10","11","12","13","14","15","16"]
full_year_list=["2010","2011","2012","2013","2014","2015","2016"]
year_index = 0
i = 0

var_string = 'Married-couple families; Estimate; Median income (dollars)'

for year in year_list:
    print(year)
    if year_index == 0:
        big_df = pd.read_csv("./data/ACS_%s_5YR_S1901_with_ann.csv"%year_list[0])
    else:
        big_df = pd.concat([big_df,pd.read_csv("./data/ACS_%s_5YR_S1901_with_ann.csv"%year)],axis=1)
    year_index +=1

year_index = 0
geography = big_df['%s_Id2'%"2010"]
geo_df = pd.DataFrame(geography)
for year in full_year_list:
    census_variable = '%s_%s'%(year,var_string)
    data = big_df[census_variable]
    if year_index == 0:
        var_df = pd.DataFrame(data)
        int_df = pd.concat([geo_df,var_df],axis=1)
        full_df = int_df

    else:
        full_df = pd.concat([full_df,data],axis=1)
    year_index += 1


# Copyright 2020 Huawei Technologies Co., Ltd.All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ==============================================================================
"""Define PyTorch graph node."""
from .base import GraphNode

from ..constant import NodeType, SEPARATOR_IN_SCOPE, SEPARATOR_BTW_NAME_AND_ID, LEFT_BUCKET, RIGHT_BUCKET, \
    SEPARATOR_IN_ONNX_OP
from ..mapper.base import Mapper

class PyTorchGraphNode(GraphNode):
    """
    PyTorch graph node.

    Args:
        node (torch._C.Node): Node in raw PyTorch graph.

    """

    _type_frozen = False
    _module_name_frozen = False

    def __init__(self, node=None, weight=None):
        super(PyTorchGraphNode, self).__init__(node=node)
        self._op_params = self._get_raw_params(node)
        self._op_name = node.kind() if node else None
        self._scope_name = node.scopeName() if node else None
        self._opt_var_name = None
        self._variable_name = self._extract_var_name(self._scope_name)
        self._module_name = None
        self._weight = weight

    def clear_args_of_declaration(self):
        """
        Clear `self._args_in_code`.
        """
        self._args_in_code = dict()

    def _get_arg_name(self, arg):
        """
        Get arg name.

        Args:
            arg (str): Generate arg name.

        Returns:
            str, arg name in function or class declaration.
        """
        return f"{arg}_{self._variable_name}"

    @property
    def hash_key(self):
        """
        Return unique hash key of current node.

        Returns:
            str, hash key.
        """
        if self._node_type not in {NodeType.CLASS.value,
                                   NodeType.FUNC.value,
                                   NodeType.MODULE.value}:
            self._hash_key = self._op_name.lower()
        return self._hash_key

    @hash_key.setter
    def hash_key(self, h):
        """
        Setter of hash key.

        Args:
            h (str): Key.

        """
        self._hash_key = h

    @property
    def variable_name(self):
        """
        Variable name.

        Returns:
            str, variable name declared in init.
        """
        return self._variable_name

    @variable_name.setter
    def variable_name(self, v):
        """
        Setter of variable name.

        Args:
            v (str): Variable name.

        """
        self._variable_name = v

    @property
    def module_name(self):
        """
        Module name.

        Returns:
            str, module name.
        """
        if not self._module_name_frozen:
            module_name = self.tag
            # if self._node_type == NodeType.CLASS.value:
            #     module_name = f"{module_name[0].upper()}{module_name[1:]}"
            return module_name

        return self._module_name

    def _froze_module_name(self, m):
        """
        Once module_name is set, then it's unchangeable.

        Args:
            m (str): Module name.

        """
        if not self._module_name_frozen:
            self._module_name = m
            self._module_name_frozen = True

    @property
    def op_name(self):
        """
        Op name in torch.

        Returns:
            str, op name.
        """
        return self._op_name  # if self.is_empty() else self.tag

    @property
    def real_name(self):
        return

    def add_input_and_output_shape(self, input_shape, output_shape):
        """
        Add the node input shape.

        Args:
            output_shape (tuple): Output tensor shape.
            input_shape (tuple): Input tensor shape.

        """
        self._ipt_shape = input_shape
        self._opt_shape = output_shape

    def to_code(self, ipt_args_in_construct: str, output_var: str):
        """
        Generate statements.

        Args:
            ipt_args_in_construct (str): Args of input.
            output_var (str): Output variable name in construct.

        Returns:
            Union[str, str], declare in init and call in construct.
        """
        operator = self.op_in_ms or self.module_name
        self._opt_var_name = output_var

        args = self.args_in_code
        if self._node_type == NodeType.OPERATION.value and not self.convert_successful():
            args.update({"input_shape": self.input_shape,
                         "output_shape": self.output_shape})

        expr = ", ".join([f"{k.replace(f'_{self._variable_name}', '')}={v}"
                          for k, v in args.items()])
        declare = f"self.{self._variable_name} = {operator}({expr})"
        call = f"{self._opt_var_name} = self.{self._variable_name}({ipt_args_in_construct})"

        return declare, call

    def to_ir(self):
        """
        No need to implement for now.
        """
        raise NotImplementedError

    def _get_raw_params(self, node):
        """
        Get params in onnx.

        Args:
            node (Any): Node.

        Returns:
            dict, raw params.
        """
        from .torch_utils import getitem_of_node

        raw_params = dict()

        if not node:
            return raw_params

        for k in node.attributeNames():
            raw_params[k] = getitem_of_node(node, k)
        return raw_params

    def replace_with_arg(self, arg):
        """
        Replace actual parameter with formal parameter.

        Args:
            arg (str): Arg name.

        """
        self._args_in_code[arg] = arg

    @staticmethod
    def _extract_var_name(scope_name: str):
        """
        Extract variable name from scope name.
        """
        if not scope_name:
            return None
        var = scope_name.split(SEPARATOR_IN_SCOPE)[-1].lower()
        var = var.replace(LEFT_BUCKET, SEPARATOR_BTW_NAME_AND_ID).replace(
            RIGHT_BUCKET, "")
        return var

    def param_transform(self, mapper: Mapper):
        """
        Transform torch params into mindspore.

        Args:
            mapper (Mapper): Mapper of params.

        """
        if not self.transformed:
            _, _ = super(PyTorchGraphNode, self).param_transform(mapper)

            for arg, value in self._params_in_ms.items():
                self._args_in_code[self._get_arg_name(arg)] = value

            self.transformed = True

        return self._op_in_ms, self._params_in_ms

    def froze_node_type_and_module_name(self, node_type, module_name):
        """
        Froze node type and module name.

        After node_type is frozen, then the `module_name`
        will be affected when `node_type` is `class`.
        Thus, this line must be placed before `nd_inst.data.module_name`.

        Args:
            module_name: Modified module name.
            node_type (str): Node type, class of func.

        """
        if not self._type_frozen:
            self._node_type = node_type
            self._type_frozen = True

        if not self._module_name_frozen:
            self._froze_module_name(module_name)

    def convert_successful(self):
        """
        Whether convert successfully.

        Returns:
            bool, true or false.
        """
        if self._op_in_ms and SEPARATOR_IN_ONNX_OP not in self._op_in_ms:
            return True
        return False