import sys
import os

# setting path
sys.path.append('../TM2_segmentation')

import numpy as np
import pandas as pd
import nibabel as nib
from skimage.transform import resize, rescale
from scipy.ndimage import zoom
from settings import target_size_unet, scaling_factor

import matplotlib.pyplot as plt
from scripts.preprocess_utils import load_nii,find_file_in_path

# helper function to get the id and path of the image and the mask
def get_id_and_path(row, image_dir, nested = False, no_tms=True):
    patient_id, image_path, ltm_file, rtm_file = "","","",""
    patient_id = str(row['Filename']).split(".")[0].split("/")[-1]
    path = find_file_in_path(patient_id, os.listdir(image_dir))

    scan_folder = image_dir+path

    for file in os.listdir(scan_folder):
        t = image_dir+path+"/"+file
        if "LTM" in file:
            ltm_file = t
        elif "RTM" in file:
            rtm_file = t
        elif "TM" in file:
            rtm_file = t
            ltm_file = t
        if patient_id in file:
            image_path = t
    return patient_id, image_path, ltm_file, rtm_file

# convert nifti to numpy array
def nifty_to_npy(df, path_images_array, path_masks_array):
    # create empty arrays for populating with image slices
    images_array  = np.zeros((df.shape[0]*4, target_size_unet[0], target_size_unet[1], 1))
    masks_array  = np.zeros((df.shape[0]*4, target_size_unet[0], target_size_unet[1], 1))

    for idx, row in df.iterrows():
        #print(idx)
        patient_id, image_path, tm_file, _ = get_id_and_path(row, image_dir)
        #NDAR processed differently

        if image_path != 0:
            seg = nib.load(tm_file)
            seg_data, seg_affine  = seg.get_fdata(), seg.affine
            if len(seg_data)>3:
                #print(np.asarray(nib.aff2axcodes(seg_affine)))

                if 'gz' in image_path :
                    os.rename(image_path,image_path.split('.gz')[0])
                    image_path = image_path.split('.gz')[0]
                    image_array, img_aff = load_nii(image_path)
                else:
                    image_array, img_aff = load_nii(image_path)

                if (np.asarray(nib.aff2axcodes(seg_affine))==['L', 'P', 'S']).all() or\
                    (np.asarray(nib.aff2axcodes(seg_affine))==['R', 'A', 'S']).all():
                    slice_label = np.asarray(np.where(seg_data != 0)).T[0, 2]
                else:
                    print("Wrong orientation cannot infer slice label")

                # rescale to 512x512
                rescaled_mask  = rescale(seg_data[:,15:-21,slice_label], scaling_factor).reshape(1,target_size_unet[0],target_size_unet[1],1)
                rescaled_image = rescale(image_array[:,15:-21,slice_label], scaling_factor).reshape(1,target_size_unet[0],target_size_unet[1],1)

                images_array[idx,:,:,:] = np.concatenate((rescaled_image[:,:256,:,:],np.zeros_like(rescaled_image[:,:256,:,:])),axis=1)
                masks_array[idx,:,:,:] = np.concatenate((rescaled_mask[:,:256,:,:],np.zeros_like(rescaled_mask[:,:256,:,:])),axis=1)
                images_array[df.shape[0]+idx,:,:,:] = np.concatenate((np.zeros_like(rescaled_image[:,256:,:,:]),rescaled_image[:,256:,:,:]),axis=1)
                masks_array[df.shape[0]+idx,:,:,:] = np.concatenate((np.zeros_like(rescaled_image[:,256:,:,:]),rescaled_mask[:,256:,:,:]),axis=1)
                images_array[2*df.shape[0]+idx,:,:,:] = np.concatenate((np.zeros_like(rescaled_image[:,:256,:,:]),rescaled_image[:,:256,:,:]),axis=1)
                masks_array[2*df.shape[0]+idx,:,:,:] = np.concatenate((np.zeros_like(rescaled_image[:,:256,:,:]),rescaled_mask[:,:256,:,:]),axis=1)
                images_array[3*df.shape[0]+idx,:,:,:] = np.concatenate((rescaled_image[:,256:,:,:],np.zeros_like(rescaled_image[:,256:,:,:])),axis=1)
                masks_array[3*df.shape[0]+idx,:,:,:] = np.concatenate((rescaled_mask[:,256:,:,:],np.zeros_like(rescaled_image[:,256:,:,:])),axis=1)

                #print(idx, patient_id, np.shape(images_array[idx,:,:,:]),np.shape(masks_array[idx,:,:,:]))
                if np.sum(masks_array[idx,:,:,:])<=15 or np.sum(masks_array[df.shape[0]+idx,:,:,:])<=15\
                    or np.sum(masks_array[2*df.shape[0]+idx,:,:,:])<=15 or np.sum(masks_array[3*df.shape[0]+idx,:,:,:])<=15:
                    print("smal", patient_id, image_path, tm_file,np.asarray(nib.aff2axcodes(seg_affine)))

                #break

    masks_array=masks_array.astype(np.uint8)
    np.save(path_images_array, images_array)
    np.save(path_masks_array, masks_array)

if __name__=="__main__":
    input_annotation_file = 'itmt_v2/itmt2.0_joint.csv'
    image_dir  = 'data/itmt2.0/'

    df = pd.read_csv(input_annotation_file, header=0)
    df_train = df[df['train/test']=='train'].reset_index()
    #df_train=df_train[df_train['Ok registered? Y/N']=='Y'].reset_index()
    df_val = df[df['train/test']=='val'].reset_index()
    #df_val = df_val[df_val['Ok registered? Y/N']=='Y'].reset_index()

    nifty_to_npy(df_train, path_images_array='data/segmentations_itmt2.0/train_images.npy',
                 path_masks_array='data/segmentations_itmt2.0/train_masks.npy')
    nifty_to_npy(df_val, path_images_array='data/segmentations_itmt2.0/val_images.npy',
                 path_masks_array='data/segmentations_itmt2.0/val_masks.npy')


# Produce graph for glove sales
from turtle import*

# Set up glove sales variable
gloves1 = 10
gloves2 = 8
gloves3 = 3
gloves4 = 5

totalgloves = gloves1 + gloves2+ gloves3 + gloves4
print(totalgloves)

# Produce x-axis
goto(100, 0)

# Produce y-axis
goto(0, 0)
goto(0, 100)

# Plot data
goto(0, 0)
goto(25, gloves1 * 10)
goto(50, gloves2 * 10)
goto(75, gloves3 * 10)
goto(100, gloves4 * 10)

# variable that holds a list of numbers
gloves = [10, 8, 3, 5]

# Access values in my list
print(gloves[0])
print(gloves[1])
print(len(gloves))

# Calculate total glove sales
total = 0                                                                  # Sets initial variable to 0
for index in range (len(gloves)):                     # considers the 4 values in the variable 'gloves'
                total = total + gloves [index]           # starts at 0 and adds all the values in the range

print(total)

# Reverse the list using a function
gloves.reverse()
print('Updated List:', gloves)

# Reverse the list using OUs stupid method
gloves = [10, 8, 3, 5]
temp1 = gloves[0]
gloves[0] = gloves[3]
gloves[3] = temp1
temp2 = gloves[1]
gloves[1] = gloves[2]
gloves[2] = temp2
print(gloves)


r = 99
a = float(input("Enter the number of packages purchased: "))
totalAmount = 0
if a <= 9:
    discount = 0
elif a<=19:
    discount = 10
elif a<=49:
    discount = 20
elif a<=99:
    discount = 30
elif a>=100:
    discount = 40
z = round(r * a * discount / 100, 2)
totalAmount = r*a-z
z = "{:.2f}".format(z)
totalAmount = "{:.2f}".format(totalAmount)
print("discount amount @", discount,"% :", z)
print("Total amount", totalAmount )


"""Official evaluation script for v1.1 of the SQuAD dataset.
From the SQuAD website: https://rajpurkar.github.io/SQuAD-explorer/
"""

from collections import Counter
import string
import re
import argparse
import json
import sys

def normalize_answer(s):
    """Lower text and remove punctuation, articles and extra whitespace."""
    def remove_articles(text):
        return re.sub(r'\b(a|an|the)\b', ' ', text)

    def white_space_fix(text):
        return ' '.join(text.split())

    def remove_punc(text):
        exclude = set(string.punctuation)
        return ''.join(ch for ch in text if ch not in exclude)

    def lower(text):
        return text.lower()

    return white_space_fix(remove_articles(remove_punc(lower(s))))

def f1_score(prediction, ground_truth):
    prediction_tokens = normalize_answer(prediction).split()
    ground_truth_tokens = normalize_answer(ground_truth).split()
    common = Counter(prediction_tokens) & Counter(ground_truth_tokens)
    num_same = sum(common.values())
    if num_same == 0:
        return 0
    precision = 1.0 * num_same / len(prediction_tokens)
    recall = 1.0 * num_same / len(ground_truth_tokens)
    f1 = (2 * precision * recall) / (precision + recall)
    return f1

def exact_match_score(prediction, ground_truth):
    return (normalize_answer(prediction) == normalize_answer(ground_truth))

def metric_max_over_ground_truths(metric_fn, prediction, ground_truths):
    scores_for_ground_truths = []
    for ground_truth in ground_truths:
        score = metric_fn(prediction, ground_truth)
        scores_for_ground_truths.append(score)
    return max(scores_for_ground_truths)

def evaluate(dataset, predictions):
    f1 = exact_match = total = 0
    for article in dataset:
        for paragraph in article['paragraphs']:
            for qa in paragraph['qas']:
                total += 1
                if qa['id'] not in predictions:
                    message = 'Unanswered question ' + qa['id'] + \
                              ' will receive score 0.'
                    print(message, file=sys.stderr)
                    continue
                ground_truths = list(map(lambda x: x['text'], qa['answers']))
                prediction = predictions[qa['id']]
                exact_match += metric_max_over_ground_truths(
                    exact_match_score, prediction, ground_truths)
                f1 += metric_max_over_ground_truths(
                    f1_score, prediction, ground_truths)

    exact_match = 100.0 * exact_match / total
    f1 = 100.0 * f1 / total

    return {'exact_match': exact_match, 'f1': f1}

def unofficial_eval(dataset, predictions):
    """Adapted from evaluate function above. Used during training
    to produce local eval scores with the same formulae used for official eval.
    """
    f1 = exact_match = total = 0
    for example_id, prediction in predictions.items():
        total += 1
        ground_truths = dataset[example_id]['a']
        exact_match += metric_max_over_ground_truths(
            exact_match_score, prediction, ground_truths)
        f1 += metric_max_over_ground_truths(
            f1_score, prediction, ground_truths)

    exact_match = 100.0 * exact_match / total
    f1 = 100.0 * f1 / total

    return {'exact_match': exact_match, 'f1': f1}

def get_pred_text(eval_dict, example_ids, pred_starts, pred_ends, probs, use_official_ids=False):
    """Get text of predicted answers for a number of predicted starts, ends, and the corresponding example_ids.
    If use_official_ids is True, the returned dictionary maps from official UUIDs to predicted (answer text, prob) pairs.
    Otherwise, maps from simple numeric IDs to (answer text, prob) pairs.
    """
    pred_text = {}
    for example_id, pred_start, pred_end, prob in zip(example_ids, pred_starts, pred_ends, probs):
        context = eval_dict[str(example_id)]['c']
        bounds = eval_dict[str(example_id)]['bounds']
        start_idx = bounds[pred_start][0]
        end_idx = bounds[pred_end][1]
        if use_official_ids:
            official_id = eval_dict[str(example_id)]['id']
            pred_text[official_id] = (context[start_idx: end_idx], prob)
        else:
            pred_text[str(example_id)] = (context[start_idx: end_idx], prob)
    return pred_text

if __name__ == '__main__':
    expected_version = '1.1'
    parser = argparse.ArgumentParser(
        description='Evaluation for SQuAD ' + expected_version)
    parser.add_argument('dataset_file', help='Dataset file')
    parser.add_argument('prediction_file', help='Prediction File')
    args = parser.parse_args()
    with open(args.dataset_file) as dataset_file:
        dataset_json = json.load(dataset_file)
        if (dataset_json['version'] != expected_version):
            print('Evaluation expects v-' + expected_version +
                  ', but got dataset with v-' + dataset_json['version'],
                  file=sys.stderr)
        dataset = dataset_json['data']
    with open(args.prediction_file) as prediction_file:
        predictions = json.load(prediction_file)
    print(json.dumps(evaluate(dataset, predictions)))


from django.conf.urls import patterns, include, url
from rest_receipts import views

__author__ = 'Roland'
urlpatterns = patterns(
    '',
    url(r'^expense/add/', views.add),
    url(r'^expense/index/', views.index),
    url(r'^expense/edit/(\d+)', views.edit),
    url(r'^expense/delete/(\d+)', views.delete),
    url(r'^expenses/(.+?).json', views.expense_list_json, name="day_json"),
    url(r'^expenses/(.+?).json', views.expense_list_json, name="all_json"),
)


# str1 = "abcabcc"
# output: 2a2b3c
# sorted format
str1 = "abcabcc"
list1 = sorted(set(str1))
print(list1)
str2 = ""
for value in list1:
    c = str1.count(value)
    str2 = str2+str(c)+value
print(str2)


from tkinter import *
import addressbook_menu
import notebook_menu
import sort_menu
import other_menu

def addressbook_but():
    addressbook_menu.main()

def notebook_but():
    notebook_menu.main()

def sorter_but():
    sort_menu.main()

def other_but():
    other_menu.main()

def exit_but():
    tk.destroy()
    try:
        addressbook_menu.tka.destroy()
        notebook_menu.tkn.destroy()
        sort_menu.tks.destroy()
    except:
        print("\nhelper>>_ Goodbye!")

def main():
    global tk
    tk = Tk()
    tk.geometry("500x500+430+50")
    tk.title("Персональний помічник")
    tk.config(background="grey")
    tit = Label(tk, text="Персональний помічник вітає Вас!", bg="grey", font='Arial 20', foreground="white")
    but_1 = Button(tk, text="Адресна книга", command=addressbook_but, font="Arial 15", width=18)
    but_2 = Button(tk, text="Нотатник", command=notebook_but, font="Arial 15", width=18)
    but_3 = Button(tk, text="Сортувальник", command=sorter_but, font="Arial 15", width=18)
    but_4 = Button(tk, text="Інше", command=other_but, font="Arial 15", width=18)
    but_5 = Button(tk, text="Вихід", command=exit_but, font="Arial 15", width=18)
    footer = Label(tk, text="© Dream Team", bg="grey", font='Arial 10', foreground="white")
    tit.place(x=30, y=50)
    but_1.place(x=150, y=150)
    but_2.place(x=150, y=200)
    but_3.place(x=150, y=250)
    but_4.place(x=150, y=300)
    but_5.place(x=150, y=350)
    footer.place(x=20, y=475)
    tk.mainloop()

if __name__ == '__main__':
    main()


import numpy as np
np.random.seed(1003)
import pandas as pd
import os
import time
import datetime
import pickle
import math
import sys
import pdb
import json

#Suppress messages from tensorflow
stderr = sys.stderr
sys.stderr = open(os.devnull, 'w')
import keras
from keras import callbacks
from keras import layers
from keras import optimizers
from keras.models import  Model
from keras import backend as K
from keras.models import load_model

import tensorflow as tf
sys.stderr = stderr
os.environ['TF_CPP_MIN_LOG_LEVEL'] = '3' #Stop TensorFlow Debugging Info

import sklearn.linear_model
from sklearn.multioutput import MultiOutputRegressor
from sklearn.metrics import mean_squared_error as mse
from sklearn.model_selection import ParameterGrid
from sklearn import preprocessing

## Mine IMPORTS:

import customModels
import customCallbacks
import customUtils
from customMetrics import atan_mse,modulo_2pi_error,log_likelihood_normal_cost,nll,mean_squared_error_mu,acc_dist,cv_test,custom_loss,phase_mse,smape,custom_loss_TRP,custom_metroc_TRP

import prep

#extra
import argparse
import argcomplete

import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot as plt

import email_logger

##Helpers Function

def plotFunct2(model_params,pred,true,input,dataset = "dev"):

	names1 = model_params["OutputNames"]
	names2 = model_params["InputNames"]

	data_name = ["pred","true"]

	#pdb.set_trace()

	long_range = np.arange(len(true[:,0])*len(model_params["OutputNames"]))

	true_list = []
	sort_ix = np.argsort(true[:,0])

	for i in range(len(model_params["OutputNames"])):
		val1 = true[:,0]
		val2 = true[:,1]
		val3 = true[:,2]
		temp_long = np.concatenate((val1[sort_ix],val2[sort_ix],val3[sort_ix]),axis = 0)
		true_list.append(temp_long)

	pred_list = []
	for i in range(len(model_params["OutputNames"])):
		val1 = pred[:,0]
		val2 = pred[:,1]
		val3 = pred[:,2]
		temp_long = np.concatenate((val1[sort_ix],val2[sort_ix],val3[sort_ix]),axis = 0)
		pred_list.append(temp_long)

	for i in range(1):
		plt.plot(long_range,true_list[i],'bo',markersize = 1);
		plt.plot(long_range,pred_list[i],'ro', markersize = 1);

	image_path = os.path.join("images",model_params["model_name"] + "input" + dataset +  '.png')
	plt.savefig(image_path)

	plt.gcf().clear()
			#sendtext_mesage, title_text
	email_logger.send_email(text_mesage = dataset+" vs input", title_text = model_params["model_name"],image_path = image_path)

	#pdb.set_trace()

def plotFunct1(model_params,pred,true,dataset = "dev"):

	names = model_params["OutputNames"]

	data_name = ["pred","true"]
	data_list = [pred,true]

	for ix,data in enumerate(data_list):
		for i,name1 in enumerate(names):
			for j,name2 in enumerate(names):

				plt.subplot(len(names), len(names), i*len(names)+ j+1)
				plt.scatter(data[:,j], data[:,i], marker=".")
				plt.xlabel(name2)
				plt.ylabel(name1)

		plt.suptitle(dataset) # or plt.suptitle('Main title')

		image_path = os.path.join("images",model_params["model_name"] + data_name[ix] + dataset +  '.png')
		plt.savefig(image_path)

		plt.gcf().clear()
			#sendtext_mesage, title_text
		email_logger.send_email(text_mesage = dataset + data_name[ix], title_text = model_params["model_name"],image_path = image_path)

def plotAndSendPic(name,true_val,mu,model_params,variance = None, percent = 1,isdots = True, dataset = "dev"):

	#pdb.set_trace()
	#choose only 30%
	np.random.seed(1003)
	msk = np.random.rand(len(true_val)) < 0.03 #change to chnage percentage

	true_val = true_val[msk]
	mu = mu[msk]

	try:
		variance = variance[msk]
		lower = mu - 3*np.sqrt(variance)
		upper = mu + 3*np.sqrt(variance)
	except:
		print("No Variance")

	if "ph" in name:
		print("test")
		old_mu = mu
		mu = mu%(math.pi*2)
		print((mu - old_mu).sum())
		#pdb.set_trace()

	ape = 100.*np.abs(mu - true_val)/(true_val+ np.finfo(float).eps)
	ape = np.clip(ape,0,100)
	error = np.abs(mu - true_val)

	message = "For data: "+ dataset +  " Name :" + str(name) + " better than 50%: " + str(100.*ape[ape<50].shape[0]/ape.shape[0])
	print(message)

	#pdb.set_trace()
	sort_ix = np.argsort(true_val)
	x = np.arange(len(sort_ix))
	#plot

	plt.plot(x,true_val[sort_ix] , 'bo', markersize = 1)
	plt.errorbar(x, mu[sort_ix], yerr=[lower[sort_ix], upper[sort_ix]], color='green',markersize=2,ecolor='r', capthick=2)

	"""
	if isdots:
		plt.plot(x,mu[sort_ix], 'ro', markersize = 1)
		plt.plot(x,true_val[sort_ix] , 'bo', markersize = 1)
	else:
		plt.plot(x,mu[sort_ix], color = 'red', markersize = 1)
		plt.plot(x,true_val[sort_ix] , color = 'blue', markersize = 1)
	"""

	#plt.scatter(mu,true_val)

	#plt.legend([ r'$\mu ', r'$\mu+3$\variance', r'$\mu+3$\variance', r'true'], loc='upper left')
	plt.ylabel('Value of ' + str(name))
	plt.xlabel('sample N')
	plt.legend([ 'predicted ', 'true'], loc='upper left')
	plt.title(model_params["model_name"] + "_plot_for: "+ str(name) +" data: " + dataset)
	image_path = os.path.join("images",model_params["model_name"] +str(name) + dataset + '.png')
	plt.savefig(image_path)

	plt.gcf().clear()
	#sendtext_mesage, title_text
	email_logger.send_email(text_mesage = message, title_text =model_params["model_name"],image_path = image_path)

def loadData(model_params,dataset):
	train_data,dev_data,test_data = prep.loadData()

	if dataset == "test":
		data = [train_data,test_data]
	elif dataset == "train":
		data = [train_data,train_data]
	elif dataset == "dev":
		data = [train_data,dev_data]
	else:
		raise NameError("Not correct dataset")
	# change to use Test:

	#data
	AP_train,TRP_train = data[0]
	AP_dev,TRP_dev = data[1]

	#Check direction
	if (model_params["DirectionForward"]):
		X_train,Y_train,X_dev,Y_dev = TRP_train,AP_train,TRP_dev,AP_dev
	else:
		X_train,Y_train,X_dev,Y_dev = AP_train,TRP_train,AP_dev,TRP_dev

	return (X_train,Y_train,X_dev,Y_dev)

def loadBestModel(model_location):

	best_model =  load_model(model_location,
	custom_objects={
	'nll': nll,
	'atan_mse':atan_mse,
	'modulo_2pi_error':modulo_2pi_error,
	'log_likelihood_normal_cost':log_likelihood_normal_cost,
	'mean_squared_error_mu':mean_squared_error_mu,
	'acc_dist':acc_dist,
	'cv_test':cv_test,
	'custom_loss':atan_mse,
	'phase_mse':phase_mse,
	'smape':smape,
	})
	return best_model

def loadBestModel_Custom(model_location):

	best_model =  load_model(model_location,
	custom_objects={
	'nll': nll,
	'atan_mse':atan_mse,
	'modulo_2pi_error':modulo_2pi_error,
	'log_likelihood_normal_cost':log_likelihood_normal_cost,
	'mean_squared_error_mu':mean_squared_error_mu,
	'acc_dist':acc_dist,
	'cv_test':cv_test,
	'custom_loss':custom_loss,
	'phase_mse':phase_mse,
	'smape':smape,
	'custom_loss_TRP':custom_loss_TRP,
	'custom_metroc_TRP':custom_metroc_TRP,
	})
	return best_model

#Eval Runners

def runSimpleNNModelEval(model_location,model_params,dataset,email):

	X_train,Y_train,X_dev,Y_dev = loadData(model_params,dataset)

	if model_params["scaling"]:
		X_train,X_dev,scaler = prep.scaleData(X_train,X_dev)

	best_model =  loadBestModel(model_location)

	Y_pred_dev = best_model.predict(X_dev)
	Y_pred_dev = np.array(Y_pred_dev)
	Y_pred_dev = np.squeeze(Y_pred_dev)
	Y_pred_dev = np.transpose(Y_pred_dev)
	Y_true_dev = np.array([row for row in Y_dev.T])
	Y_true_dev = np.transpose(Y_true_dev)

	#pdb.set_trace()

	if (model_params["DirectionForward"]):
		mse_total = customUtils.mse_p(ix = (3,6),Y_pred = Y_pred_dev,Y_true = Y_true_dev)
	else:
		diff = Y_pred_dev - Y_true_dev
		mse_total = np.sqrt(((diff) ** 2)).mean(axis=0)

	#plotFunct2(model_params,Y_pred_dev,Y_true_dev,X_dev,dataset)
	#plotFunct1(model_params,Y_pred_dev,Y_true_dev,dataset)

	if email == 'true':
		for i,name in enumerate(model_params["OutputNames"]):
			plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 1,isdots = True,dataset = dataset)
			#plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 0.1,isdots = False,dataset = dataset)

	print("*"*50)
	print(model_params["OutputNames"]+model_params["InputNames"])
	print("MSE TOTAL DEV: " ,mse_total)
	print("*"*50)
	print("Model Was:",
	str(",".join(model_params["InputNames"])),
	"->",str(",".join(model_params["OutputNames"])),
	"->",str(",".join(model_params["InputNames"])))

	return mse_total

def runCustomEval(model_location,model_params,dataset,email):

	X_train,Y_train,X_dev,Y_dev = loadData(model_params,dataset)

	weights_location = os.path.join('weights',model_params["model_name"] +  '.h5')

	if model_params["scaling"]:
		X_train,X_dev,scaler = prep.scaleData(X_train,X_dev)

	#pdb.set_trace()

	combined_model =  loadBestModel_Custom(model_location)
	combined_model.load_weights(weights_location)

	model_forward = loadBestModel_Custom(model_params["ForwardModelLocation"])
	model_forward.load_weights(weights_location,by_name = True)

	model_reverse = loadBestModel(model_params["InverseModelLocation"]) #change to load weights

	Y_pred = model_forward.predict(X_dev)
	#Y_pred = (np.asarray(Y_pred)[:,:,0]).T
	X_from_pred = model_reverse.predict(Y_pred)
	angs_pred = (np.asarray(X_from_pred)[3:6,:,0]).T

	X_from_true = model_reverse.predict(Y_dev)
	angs_true = (np.asarray(X_from_true)[3:6,:,0]).T

	angle_error = np.arctan2(np.sin(angs_true - angs_pred), np.cos(angs_true - angs_pred))
	rad_error = np.abs(angle_error).mean(axis = 0)

	#pdb.set_trace()

	mse_amp = ((np.asarray(X_from_pred)[0:3,:,0].T - np.asarray(X_from_true)[0:3,:,0].T) ** 2).mean(axis=0)

	Y_pred_dev = Y_pred
	Y_true_dev = Y_dev

	mse_total = ((Y_pred_dev - Y_true_dev) ** 2).mean(axis=0)

	smape = 2*100*(np.abs(Y_pred_dev - Y_true_dev)/np.abs(Y_true_dev + Y_pred_dev + np.finfo(np.float32).eps)).mean(axis = 0)
	non_valid = 100* np.where(Y_pred_dev[:,1] >= Y_pred_dev[:,2])[0].shape[0]/Y_pred_dev[:,1].shape[0]
	valid  = 100* np.where(Y_pred_dev[:,1] < Y_pred_dev[:,2])[0].shape[0]/Y_pred_dev[:,1].shape[0]

	print(dataset)
	print("mse_amp" ,mse_amp )
	print("rad_error",rad_error)
	print("mse_TRP",mse_total)
	print("smape",smape)
	print("non valid",non_valid,"valid",valid)

	#pdb.set_trace()

	if email == 'true':
		for i,name in enumerate(model_params["OutputNames"]):
			plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 1,isdots = True,dataset = dataset)
			#plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 0.1,isdots = False,dataset = dataset)

	print("*"*50)
	#print(model_params["OutputNames"]+model_params["InputNames"])
	#print("MSE TOTAL DEV: " ,mse_total)
	#print("*"*50)
	#print("Model Was:",
	#str(",".join(model_params["InputNames"])),
	#"->",str(",".join(model_params["OutputNames"])),
	#"->",str(",".join(model_params["InputNames"])))

	return mse_total

def runSimpleEncoderDecoderEval(model_location,model_params,dataset,email):

	X_train,Y_train,X_dev,Y_dev = loadData(model_params,dataset)
	best_model =  loadBestModel(model_location)

	if model_params["scaling"]:
		X_train,X_dev,scaler = prep.scaleData(X_train,X_dev)

	Y_pred_dev = best_model.predict(X_dev)
	Y_pred_dev = np.array(Y_pred_dev)
	Y_pred_dev = np.squeeze(Y_pred_dev)
	Y_pred_dev = np.transpose(Y_pred_dev)
	Y_true_dev = np.array([row for row in Y_dev.T]+[row for row in X_dev.T])
	Y_true_dev = np.transpose(Y_true_dev)

	if (model_params["DirectionForward"]):
		mse_total = customUtils.mse_p(ix = (3,6),Y_pred = Y_pred_dev,Y_true = Y_true_dev)
	else:
		mse_total = customUtils.mse_p(ix = (6,9),Y_pred = Y_pred_dev,Y_true = Y_true_dev)

	#plotFunct1(model_params,Y_pred_dev,Y_true_dev,dataset)

	if email == 'true':
		for i,name in enumerate(model_params["OutputNames"]):
			plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 1,isdots = True,dataset = dataset)
			#plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 0.1,isdots = False,dataset = dataset)

	print("*"*50)
	print(model_params["OutputNames"]+model_params["InputNames"])
	print("MSE TOTAL DEV: " ,mse_total)
	print("*"*50)
	print("Model Was:",
	str(",".join(model_params["InputNames"])),
	"->",str(",".join(model_params["OutputNames"])),
	"->",str(",".join(model_params["InputNames"])))

	return mse_total

def runDistModelEval(model_location,model_params,dataset,email):

	X_train,Y_train,X_dev,Y_dev = loadData(model_params,dataset)
	best_model =  loadBestModel(model_location)

	if model_params["scaling"]:
		X_train,X_dev,scaler = prep.scaleData(X_train,X_dev)

	Y_true_dev = np.array([row for row in Y_dev.T])
	Y_true_dev = np.transpose(Y_true_dev)
	Y_pred_dev = best_model.predict(X_dev)

	"""
	pdb.set_trace()

	Y_pred_dev = np.array(Y_pred_dev)
	Y_pred_dev = np.squeeze(Y_pred_dev)
	Y_pred_dev = np.transpose(Y_pred_dev)

	pdb.set_trace()
	#ReverseScaling
	Y_pred_dev = scaler_Y.inverse_transform(Y_pred_dev)
	Y_true_dev = scaler_Y.inverse_transform(Y_true_dev)

	"""

	number_to_pick = 1
	output_pred = np.zeros(( Y_pred_dev[0].shape[0],number_to_pick*len(model_params["OutputNames"])))

	for i,name in enumerate(model_params["OutputNames"]):
		temp = Y_pred_dev[i][:]
		mu = temp[:,0]
		variance = temp[:,1]
		CV = np.sqrt(variance)/mu
		print("CV for:",name,"=",CV.mean()*100)
		upper = mu + 3*np.sqrt(variance)
		lower = mu - 3*np.sqrt(variance)
		true_val = Y_true_dev[:,i]
		correct_num = np.sum(np.logical_and(true_val < upper, true_val > lower))
		acc = 100*correct_num/len(true_val)
		print("Acc for:", name , "=", acc, "%")
		dist_size = 1
		distributions = [np.random.normal(m, s, dist_size) for m, s in zip(mu, np.sqrt(variance))]
		distributions_array  = np.stack(distributions,axis = 1).T

		if email == 'true':
			plotAndSendPic(name,true_val,mu,model_params,variance,percent = 1,isdots = True,dataset = dataset)
			#plotAndSendPic(name,true_val,mu,model_params,variance,percent = 0.1,isdots = False,dataset = dataset)

		output_pred[:,i] = mu

	Y_pred_dev = output_pred

	"""
	#ReverseScaling
	Y_pred_dev = scaler_Y.inverse_transform(Y_pred_dev)
	Y_true_dev = scaler_Y.inverse_transform(Y_true_dev)
	"""

	#pdb.set_trace()
	#plotFunct1(model_params,Y_pred_dev[:,0:len(model_params["OutputNames"])],Y_true_dev[:,0:len(model_params["OutputNames"])],dataset)

	if (model_params["DirectionForward"]):
		mse_total = customUtils.mse_p(ix = (3,6),Y_pred = Y_pred_dev,Y_true = Y_true_dev)
	else:
		diff = Y_pred_dev - Y_true_dev
		mse_total = ((diff) ** 2).mean(axis=0)

	print("*"*50)
	print(model_params["OutputNames"]+model_params["InputNames"])
	print("MSE TOTAL DEV: " ,mse_total)
	print("*"*50)
	print("Model Was:",
	str(",".join(model_params["InputNames"])),
	"->",str(",".join(model_params["OutputNames"])),
	"->",str(",".join(model_params["InputNames"])))

	return mse_total

def runDistEncoderDecoderEval(model_location,model_params,dataset,email):

	X_train,Y_train,X_dev,Y_dev = loadData(model_params,dataset)
	best_model =  loadBestModel(model_location)

	if model_params["scaling"]:
		X_train,X_dev,scaler = prep.scaleData(X_train,X_dev)

	Y_true_dev = np.array([row for row in Y_dev.T]+[row for row in X_dev.T])
	Y_true_dev = np.transpose(Y_true_dev)

	Y_pred_dev = best_model.predict(X_dev)

	number_to_pick = 1
	output_pred = np.zeros(( Y_pred_dev[0].shape[0],number_to_pick*len(model_params["OutputNames"])))

	for i,name in enumerate(model_params["OutputNames"]):
		temp = Y_pred_dev[i][:]
		mu = temp[:,0]
		variance = temp[:,1]
		CV = np.sqrt(variance)/mu
		print("CV for:",name,"=",CV.mean()*100)
		upper = mu + 3*np.sqrt(variance)
		lower = mu - 3*np.sqrt(variance)
		true_val = Y_true_dev[:,i]
		correct_num = np.sum(np.logical_and(true_val < upper, true_val > lower))
		acc = 100*correct_num/len(true_val)
		print("Acc for:", name , "=", acc, "%")

		#tempDist = np.random.normal(mu, variance, 1000)
		dist_size = 1

		distributions = [np.random.normal(m, s, dist_size) for m, s in zip(mu, np.sqrt(variance))]
		distributions_array  = np.stack(distributions,axis = 1).T
		#output_pred[:,i:i+number_to_pick] = distributions_array

		if email == 'true':
			plotAndSendPic(name,true_val,mu,model_params,variance,percent = 1,isdots = True,dataset = dataset)
			#plotAndSendPic(name,true_val,mu,model_params,variance,percent = 0.1,isdots = False,dataset = dataset)

		output_pred[:,i] = mu

	input_pred = np.zeros(( Y_pred_dev[0].shape[0],len(model_params["InputNames"])))

	for i,name in enumerate(model_params["InputNames"]):
		input_pred[:,i] = np.squeeze(Y_pred_dev[len(model_params["OutputNames"])+i][:])

	Y_pred_dev = np.concatenate((output_pred,input_pred),axis = 1)

	#plotFunct1(model_params,Y_pred_dev[:,0:len(model_params["OutputNames"])],Y_true_dev[:,0:len(model_params["OutputNames"])],dataset)

	if (model_params["DirectionForward"]):
		mse_total = customUtils.mse_p(ix = (3,6),Y_pred = Y_pred_dev,Y_true = Y_true_dev)
	else:
		mse_total = customUtils.mse_p(ix = (6,9),Y_pred = Y_pred_dev,Y_true = Y_true_dev)
	print("*"*50)
	print(model_params["OutputNames"]+model_params["InputNames"])
	print("MSE TOTAL DEV: " ,mse_total)
	print("*"*50)
	print("Model Was:",
	str(",".join(model_params["InputNames"])),
	"->",str(",".join(model_params["OutputNames"])),
	"->",str(",".join(model_params["InputNames"])))

	return mse_total

def runLREval(model_location,model_params,dataset,email):

	X_train,Y_train,X_dev,Y_dev = loadData(model_params,dataset)

	regr = MultiOutputRegressor(sklearn.linear_model.LinearRegression())
	regr.fit(X_train,Y_train)
	Y_pred_dev = regr.predict(X_dev)
	Y_true_dev = Y_dev

	#plotFunct1(model_params,Y_pred_dev,Y_true_dev,dataset)

	if email == 'true':
		for i,name in enumerate(model_params["OutputNames"]):
			plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 1,isdots = True,dataset = dataset)
			#plotAndSendPic(name = name,true_val = Y_true_dev[:,i],mu = Y_pred_dev[:,i],model_params = model_params,percent = 0.1,isdots = False,dataset = dataset)

	if (model_params["DirectionForward"]):
		mse_total = customUtils.mse_p(ix = (3,6),Y_pred = Y_pred_dev,Y_true = Y_true_dev)
	else:
		mse_total = customUtils.mse_p(ix = (6,9),Y_pred = Y_pred_dev,Y_true = Y_true_dev)
	print("*"*50)
	print(model_params["OutputNames"]+model_params["InputNames"])
	print("MSE TOTAL DEV: " ,mse_total)
	print("*"*50)
	print("Model Was:",
	str(",".join(model_params["InputNames"])),
	"->",str(",".join(model_params["OutputNames"])),
	"->",str(",".join(model_params["InputNames"])))

	return mse_total

def run_eval_base(model_location,dataset,email = 'false'):

	path, model_name_with_ext = os.path.split(model_location)
	model_name, file_extension = os.path.splitext(model_name_with_ext)

	model_params_loc = os.path.join('model_params',model_name+".json")

	with open(model_params_loc) as jf:
		model_params = json.load(jf)

	if model_params["RunType"] == "SimpleAutoencoder":
		mse_total = runSimpleEncoderDecoderEval(model_location,model_params,dataset,email)
	elif model_params["RunType"] == "SimpleNN":
		mse_total = runSimpleNNModelEval(model_location,model_params,dataset,email)
	elif model_params["RunType"] == "DistAutoencoder":
		mse_total =  runDistEncoderDecoderEval(model_location,model_params,dataset,email)
	elif model_params["RunType"] == "DistNN":
		mse_total = runDistModelEval(model_location,model_params,dataset,email)
	elif model_params["RunType"] == "LR":
		mse_total = runLREval(model_location,model_params,dataset,email)
	elif model_params["RunType"] == "Custom":
		mse_total = runCustomEval(model_location,model_params,dataset,email)
	else:
		raise NotImplementedError("Not Correct Run Type")

	if email == 'true':
		email_logger.send_email(text_mesage ="Results for:" +  dataset + str(model_params["OutputNames"]) + str(mse_total), title_text = model_params["model_name"])

	return mse_total

def main(args):

	if args.dataset not in ["test","dev","train"]:
		raise NameError("Not correct datasetName")

	run_eval_base(args.model_file,args.dataset,args.email)

if __name__ == "__main__":

	parser = argparse.ArgumentParser(description = 'LOads Model and DataFile')
	parser.add_argument('-mf','--model_file',type = str, help='model_file_location')
	parser.add_argument('-d', '--dataset', type = str, help = 'choose from train/dev/test')
	parser.add_argument('-e', '--email', type = str, help = 'send email with results',default = 'false')
	argcomplete.autocomplete(parser)
	args = parser.parse_args()

	main(args)


"""Definition and setup of the Omnilogic Sensors for Home Assistant."""
from homeassistant.components.sensor import (
    SensorDeviceClass,
    SensorEntity,
    STATE_CLASS_MEASUREMENT,
)
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
    CONCENTRATION_PARTS_PER_MILLION,
    ELECTRIC_POTENTIAL_MILLIVOLT,
    MASS_GRAMS,
    PERCENTAGE,
    TEMP_CELSIUS,
    TEMP_FAHRENHEIT,
    VOLUME_LITERS,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback

from .common import OmniLogicEntity, OmniLogicUpdateCoordinator, check_guard
from .const import COORDINATOR, DEFAULT_PH_OFFSET, DOMAIN, PUMP_TYPES

async def async_setup_entry(
    hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
    """Set up the sensor platform."""

    coordinator = hass.data[DOMAIN][entry.entry_id][COORDINATOR]
    entities = []

    for item_id, item in coordinator.data.items():
        id_len = len(item_id)
        item_kind = item_id[-2]
        entity_settings = SENSOR_TYPES.get((id_len, item_kind))

        if not entity_settings:
            continue

        for entity_setting in entity_settings:
            for state_key, entity_class in entity_setting["entity_classes"].items():
                if check_guard(state_key, item, entity_setting):
                    continue

                entity = entity_class(
                    coordinator=coordinator,
                    state_key=state_key,
                    name=entity_setting["name"],
                    kind=entity_setting["kind"],
                    item_id=item_id,
                    device_class=entity_setting["device_class"],
                    state_class=entity_setting["state_class"],
                    icon=entity_setting["icon"],
                    unit=entity_setting["unit"],
                )

                entities.append(entity)

    async_add_entities(entities)

class OmnilogicSensor(OmniLogicEntity, SensorEntity):
    """Defines an Omnilogic sensor entity."""

    def __init__(
        self,
        coordinator: OmniLogicUpdateCoordinator,
        kind: str,
        name: str,
        device_class: str,
        state_class: str,
        icon: str,
        unit: str,
        item_id: tuple,
        state_key: str,
    ) -> None:
        """Initialize Entities."""
        super().__init__(
            coordinator=coordinator,
            kind=kind,
            name=name,
            item_id=item_id,
            icon=icon,
        )

        backyard_id = item_id[:2]
        unit_type = coordinator.data[backyard_id].get("Unit-of-Measurement")

        self._unit_type = unit_type
        self._device_class = device_class
        self._state_class = state_class
        self._unit = unit
        self._state_key = state_key

    @property
    def device_class(self):
        """Return the device class of the entity."""
        return self._device_class

    @property
    def state_class(self):
        """Return the state class of the entity."""
        return self._state_class

    @property
    def native_unit_of_measurement(self):
        """Return the right unit of measure."""
        return self._unit

class OmniLogicTemperatureSensor(OmnilogicSensor):
    """Define an OmniLogic Temperature (Air/Water) Sensor."""

    @property
    def native_value(self):
        """Return the state for the temperature sensor."""
        sensor_data = self.coordinator.data[self._item_id][self._state_key]

        hayward_state = sensor_data
        hayward_unit_of_measure = TEMP_FAHRENHEIT
        state = sensor_data

        if self._unit_type == "Metric":
            hayward_state = round((int(hayward_state) - 32) * 5 / 9, 1)
            hayward_unit_of_measure = TEMP_CELSIUS

        if int(sensor_data) == -1 or int(sensor_data) == 255:
            hayward_state = None
            state = None

        self._attrs["hayward_temperature"] = hayward_state
        self._attrs["hayward_unit_of_measure"] = hayward_unit_of_measure

        self._unit = TEMP_FAHRENHEIT

        return state

class OmniLogicPumpSpeedSensor(OmnilogicSensor):
    """Define an OmniLogic Pump Speed Sensor."""

    @property
    def native_value(self):
        """Return the state for the pump speed sensor."""

        pump_type = PUMP_TYPES[
            self.coordinator.data[self._item_id].get(
                "Filter-Type", self.coordinator.data[self._item_id].get("Type", {})
            )
        ]
        pump_speed = self.coordinator.data[self._item_id][self._state_key]

        if pump_type == "VARIABLE":
            self._unit = PERCENTAGE
            state = pump_speed
        elif pump_type == "DUAL":
            self._unit = None
            if pump_speed == 0:
                state = "off"
            elif pump_speed == self.coordinator.data[self._item_id].get(
                "Min-Pump-Speed"
            ):
                state = "low"
            elif pump_speed == self.coordinator.data[self._item_id].get(
                "Max-Pump-Speed"
            ):
                state = "high"

        self._attrs["pump_type"] = pump_type

        return state

class OmniLogicSaltLevelSensor(OmnilogicSensor):
    """Define an OmniLogic Salt Level Sensor."""

    @property
    def native_value(self):
        """Return the state for the salt level sensor."""

        salt_return = self.coordinator.data[self._item_id][self._state_key]
        unit_of_measurement = self._unit

        if self._unit_type == "Metric":
            salt_return = round(int(salt_return) / 1000, 2)
            unit_of_measurement = f"{MASS_GRAMS}/{VOLUME_LITERS}"

        self._unit = unit_of_measurement

        return salt_return

class OmniLogicChlorinatorSensor(OmnilogicSensor):
    """Define an OmniLogic Chlorinator Sensor."""

    @property
    def native_value(self):
        """Return the state for the chlorinator sensor."""
        state = self.coordinator.data[self._item_id][self._state_key]

        return state

class OmniLogicPHSensor(OmnilogicSensor):
    """Define an OmniLogic pH Sensor."""

    @property
    def native_value(self):
        """Return the state for the pH sensor."""

        ph_state = self.coordinator.data[self._item_id][self._state_key]

        if ph_state == 0:
            ph_state = None
        else:
            ph_state = float(ph_state) + float(
                self.coordinator.config_entry.options.get(
                    "ph_offset", DEFAULT_PH_OFFSET
                )
            )

        return ph_state

class OmniLogicORPSensor(OmnilogicSensor):
    """Define an OmniLogic ORP Sensor."""

    def __init__(
        self,
        coordinator: OmniLogicUpdateCoordinator,
        state_key: str,
        name: str,
        kind: str,
        item_id: tuple,
        device_class: str,
        state_class: str,
        icon: str,
        unit: str,
    ) -> None:
        """Initialize the sensor."""
        super().__init__(
            coordinator=coordinator,
            kind=kind,
            name=name,
            device_class=device_class,
            state_class=state_class,
            icon=icon,
            unit=unit,
            item_id=item_id,
            state_key=state_key,
        )

    @property
    def native_value(self):
        """Return the state for the ORP sensor."""

        orp_state = int(self.coordinator.data[self._item_id][self._state_key])

        if orp_state == -1:
            orp_state = None

        return orp_state

SENSOR_TYPES = {
    (2, "Backyard"): [
        {
            "entity_classes": {"airTemp": OmniLogicTemperatureSensor},
            "name": "Air Temperature",
            "kind": "air_temperature",
            "device_class": SensorDeviceClass.TEMPERATURE,
            "state_class": STATE_CLASS_MEASUREMENT,
            "icon": None,
            "unit": TEMP_FAHRENHEIT,
            "native_unit_of_measurement": TEMP_FAHRENHEIT,
            "guard_condition": [{}],
        },
    ],
    (4, "BOWS"): [
        {
            "entity_classes": {"waterTemp": OmniLogicTemperatureSensor},
            "name": "Water Temperature",
            "kind": "water_temperature",
            "device_class": SensorDeviceClass.TEMPERATURE,
            "state_class": STATE_CLASS_MEASUREMENT,
            "icon": None,
            "unit": TEMP_FAHRENHEIT,
            "native_unit_of_measurement": TEMP_FAHRENHEIT,
            "guard_condition": [{}],
        },
    ],
    (6, "Filter"): [
        {
            "entity_classes": {"filterSpeed": OmniLogicPumpSpeedSensor},
            "name": "Speed",
            "kind": "filter_pump_speed",
            "device_class": None,
            "state_class": None,
            "icon": "mdi:speedometer",
            "unit": PERCENTAGE,
            "guard_condition": [
                {"Filter-Type": "FMT_SINGLE_SPEED"},
            ],
        },
    ],
    (6, "Pumps"): [
        {
            "entity_classes": {"pumpSpeed": OmniLogicPumpSpeedSensor},
            "name": "Pump Speed",
            "kind": "pump_speed",
            "device_class": None,
            "state_class": None,
            "icon": "mdi:speedometer",
            "unit": PERCENTAGE,
            "guard_condition": [
                {"Type": "PMP_SINGLE_SPEED"},
            ],
        },
    ],
    (6, "Chlorinator"): [
        {
            "entity_classes": {"Timed-Percent": OmniLogicChlorinatorSensor},
            "name": "Setting",
            "kind": "chlorinator",
            "device_class": None,
            "state_class": None,
            "icon": "mdi:gauge",
            "unit": PERCENTAGE,
            "guard_condition": [
                {
                    "Shared-Type": "BOW_SHARED_EQUIPMENT",
                    "status": "0",
                },
                {
                    "operatingMode": "2",
                },
            ],
        },
        {
            "entity_classes": {"avgSaltLevel": OmniLogicSaltLevelSensor},
            "name": "Average Salt Level",
            "kind": "average_salt_level",
            "device_class": None,
            "state_class": STATE_CLASS_MEASUREMENT,
            "icon": "mdi:gauge",
            "unit": CONCENTRATION_PARTS_PER_MILLION,
            "guard_condition": [
                {
                    "Shared-Type": "BOW_SHARED_EQUIPMENT",
                    "status": "0",
                },
            ],
        },
        {
            "entity_classes": {"instantSaltLevel": OmniLogicSaltLevelSensor},
            "name": "Instant Salt Level",
            "kind": "instant_salt_level",
            "device_class": None,
            "state_class": STATE_CLASS_MEASUREMENT,
            "icon": "mdi:gauge",
            "unit": CONCENTRATION_PARTS_PER_MILLION,
            "guard_condition": [
                {
                    "Shared-Type": "BOW_SHARED_EQUIPMENT",
                    "status": "0",
                },
            ],
        },
    ],
    (6, "CSAD"): [
        {
            "entity_classes": {"ph": OmniLogicPHSensor},
            "name": "pH",
            "kind": "csad_ph",
            "device_class": None,
            "state_class": STATE_CLASS_MEASUREMENT,
            "icon": "mdi:gauge",
            "unit": "pH",
            "guard_condition": [
                {"ph": ""},
            ],
        },
        {
            "entity_classes": {"orp": OmniLogicORPSensor},
            "name": "ORP",
            "kind": "csad_orp",
            "device_class": None,
            "state_class": STATE_CLASS_MEASUREMENT,
            "icon": "mdi:gauge",
            "unit": ELECTRIC_POTENTIAL_MILLIVOLT,
            "guard_condition": [
                {"orp": ""},
            ],
        },
    ],
}


from torch.utils.tensorboard import SummaryWriter
import torch.nn.functional as F
import torch
import datetime
from os.path import join
import numpy as np
from torch.utils.data.sampler import SubsetRandomSampler

pixel_coords = None

class AttrDict(dict):
    def __init__(self, *args, **kwargs):
        super(AttrDict, self).__init__(*args, **kwargs)
        self.__dict__ = self

def split(joint_dataset,val_percent, sampler=SubsetRandomSampler,random_seed=42):

    '''
    function useful there is no explicit valitdation scripts available
    joint_dataset = train + val items
    returns train and validation samplers based on sampling strategy
    '''
    dataset_size = len(joint_dataset)
    indices = list(range(dataset_size))
    split = int(np.floor(val_percent * dataset_size))
    np.random.seed(random_seed)
    np.random.shuffle(indices)
    train_indices, val_indices = indices[split:], indices[:split]
    train_sampler = SubsetRandomSampler(train_indices)
    valid_sampler = SubsetRandomSampler(val_indices)

    return train_sampler, valid_sampler

def get_log_dir():
    '''
    New log dir at every run according to the time at that point in time.
    '''
    now = datetime.datetime.now()
    return "logs/run-%d-%d-%d-%d-%d-%d"%(now.year,now.month,now.day,now.hour,now.minute,now.second)

def get_summary_writer(rootdir):

    return SummaryWriter(join(rootdir,get_log_dir()))

def grad(im):
    gradx = F.pad((im[:, :, :, 2:] - im[:, :, :, :-2]) / 2.0, (1, 1, 0, 0))
    grady = F.pad((im[:, :, 2:, :] - im[:, :, :-2, :]) / 2.0, (0, 0, 1, 1))
    return gradx, grady

def sobel(im):
    c = im.size()[1]
    fx = torch.Tensor([[1, 0, -1], [2, 0, -2], [1, 0, -1]])
    fx = fx.view(1, 1, 3, 3).expand(1, c, 3, 3)
    fy = torch.Tensor([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
    fy = fy.view(1, 1, 3, 3).expand(1, c, 3, 3)
    if im.is_cuda:
        fx = fx.cuda()
        fy = fy.cuda()
    gradx = F.pad(F.conv2d(im, fx), (1, 1, 1, 1))
    grady = F.pad(F.conv2d(im, fy), (1, 1, 1, 1))
    return gradx, grady

def set_id_grid(depth):
    global pixel_coords
    b, _,  h, w = depth.size()
    i_range = torch.arange(0, h).view(1, h, 1).expand(1, h, w).type_as(depth)  # [1, H, W]
    j_range = torch.arange(0, w).view(1, 1, w).expand(1, h, w).type_as(depth)  # [1, H, W]
    ones = torch.ones(1, h, w).type_as(depth)

    pixel_coords = torch.stack((j_range, i_range, ones), dim=1)  # [1, 3, H, W]

def euler2mat(angle):
    B = angle.size(0)
    x, y, z = angle[:, 0], angle[:, 1], angle[:, 2]

    cosz = torch.cos(z)
    sinz = torch.sin(z)

    zeros = z.detach()
    ones = zeros.detach()+1
    zmat = torch.stack([cosz, -sinz, zeros,
                        sinz,  cosz, zeros,
                        zeros, zeros,  ones], dim=1).reshape(B, 3, 3)

    cosy = torch.cos(y)
    siny = torch.sin(y)

    ymat = torch.stack([cosy, zeros,  siny,
                        zeros,  ones, zeros,
                        -siny, zeros,  cosy], dim=1).reshape(B, 3, 3)

    cosx = torch.cos(x)
    sinx = torch.sin(x)

    xmat = torch.stack([ones, zeros, zeros,
                        zeros,  cosx, -sinx,
                        zeros,  sinx,  cosx], dim=1).reshape(B, 3, 3)

    rotMat = xmat @ ymat @ zmat
    return rotMat

def quat2mat(quat):
    norm_quat = torch.cat([quat[:, :1].detach()*0 + 1, quat], dim=1)
    norm_quat = norm_quat/norm_quat.norm(p=2, dim=1, keepdim=True)
    w, x, y, z = norm_quat[:, 0], norm_quat[:, 1], norm_quat[:, 2], norm_quat[:, 3]

    B = quat.size(0)

    w2, x2, y2, z2 = w.pow(2), x.pow(2), y.pow(2), z.pow(2)
    wx, wy, wz = w*x, w*y, w*z
    xy, xz, yz = x*y, x*z, y*z

    rotMat = torch.stack([w2 + x2 - y2 - z2, 2*xy - 2*wz, 2*wy + 2*xz,
                          2*wz + 2*xy, w2 - x2 + y2 - z2, 2*yz - 2*wx,
                          2*xz - 2*wy, 2*wx + 2*yz, w2 - x2 - y2 + z2], dim=1).reshape(B, 3, 3)
    return rotMat

def pose_vec2mat(vec, rotation_mode='euler'):
    translation = vec[:, :3].unsqueeze(-1)  # [B, 3, 1]
    rot = vec[:, 3:]
    if rotation_mode == 'euler':
        rot_mat = euler2mat(rot)  # [B, 3, 3]
    elif rotation_mode == 'quat':
        rot_mat = quat2mat(rot)  # [B, 3, 3]
    transform_mat = torch.cat([rot_mat, translation], dim=2)  # [B, 3, 4]
    return transform_mat

def cam2pixel(cam_coords, proj_c2p_rot, proj_c2p_tr):
    b, _, h, w = cam_coords.size()
    cam_coords_flat = cam_coords.reshape(b, 3, -1)  # [B, 3, H*W]
    if proj_c2p_rot is not None:
        pcoords = proj_c2p_rot @ cam_coords_flat
    else:
        pcoords = cam_coords_flat

    if proj_c2p_tr is not None:
        pcoords = pcoords + proj_c2p_tr  # [B, 3, H*W]
    X = pcoords[:, 0]
    Y = pcoords[:, 1]
    Z = pcoords[:, 2].clamp(min=1e-3)

    X_norm = 2*(X / Z)/(w-1) - 1  # Normalized, -1 if on extreme left, 1 if on extreme right (x = w-1) [B, H*W]
    Y_norm = 2*(Y / Z)/(h-1) - 1  # Idem [B, H*W]

    pixel_coords = torch.stack([X_norm, Y_norm], dim=2)  # [B, H*W, 2]
    return pixel_coords.reshape(b, h, w, 2)

def pixel2cam(depth, intrinsics_inv):
    global pixel_coords
    b,_,h, w = depth.size()
    if (pixel_coords is None) or pixel_coords.size(2) < h:
        set_id_grid(depth)
    current_pixel_coords = pixel_coords[..., :h, :w].expand(b, 3, h, w).reshape(b, 3, -1)  # [B, 3, H*W]
    cam_coords = (intrinsics_inv @ current_pixel_coords).reshape(b, 3, h, w)
    return cam_coords * depth

def inverse_warp(img, depth, pose, intrinsics, rotation_mode='euler', padding_mode='zeros'):

    batch_size, _, img_height, img_width = img.size()

    cam_coords = pixel2cam(depth, intrinsics.inverse())  # [B,3,H,W]
    pose_mat = pose_vec2mat(pose, rotation_mode)  # [B,3,4]

    # Get projection matrix for tgt camera frame to source pixel frame
    proj_cam_to_src_pixel = intrinsics @ pose_mat  # [B, 3, 4]

    rot, tr = proj_cam_to_src_pixel[..., :3], proj_cam_to_src_pixel[..., -1:]
    src_pixel_coords = cam2pixel(cam_coords, rot, tr)  # [B,H,W,2]
    projected_img = F.grid_sample(img, src_pixel_coords, padding_mode=padding_mode, align_corners=True)

    valid_points = src_pixel_coords.abs().max(dim=-1)[0] <= 1

    return projected_img, valid_points


import msprime, pyslim, argparse, random, subprocess
import numpy as np
import re

## This function comes courtesy of Peter Ralph, who directed me to it
## after I posted on the SLiM forum

def remove_mutations(ts, start, end, proportion):
	'''
	This function will return a new tree sequence the same as the input,
	but after removing each non-SLiM mutation within regions specified in lists
	start and end with probability `proportion`, independently. So then, if we
	want to add neutral mutations with rate 1.0e-8 within the regions and 0.7e-8
	outside the regions, we could do
	ts = pyslim.load("my.trees")
	first_mut_ts = msprime.mutate(ts, rate=1e-8)
	mut_ts = remove_mutations(first_mut_ts, start, end, 0.3)

	:param float proportion: The proportion of mutations to remove.
	'''
	new_tables = ts.dump_tables()
	new_tables.mutations.clear()
	mutation_map = [-1 for _ in range(ts.num_mutations)]
	for j, mut in enumerate(ts.mutations()):
	   keep_mutation = True
	for i in range(len(start)):
		left = start[i]
		right = end[i]
		if left == right:
			continue
		assert(left < right)
		if i > 0:
			 assert(end[i - 1] <= left)
		if mut.position >= left and mut.position < right and len(mut.metadata) == 0:
			 keep_mutation = (random.uniform(0, 1) > proportion)
		if keep_mutation:
			mutation_map[j] = new_tables.mutations.num_rows
		if mut.parent < 0:
			 new_parent = -1
		else:
			new_parent = mutation_map[mut.parent]
			new_tables.mutations.add_row(site = mut.site, node = mut.node,
			derived_state = mut.derived_state,
			parent = new_parent,
			metadata = mut.metadata)
	return new_tables.tree_sequence()

class SLiM:
	def __init__(self, configFile):
		regexString = "[-+]?[.]?[\d]+(?:,\d\d\d)*[\.]?\d*(?:[eE][-+]?\d+)?"
		lines = [i.strip().strip(';') for i in open(configFile)]
		elementsRaw = [i.split('(')[-1].strip(')').split(' ') for i in lines if i.startswith('initializeGenomicElement(')]
		self.elementStarts = [int(i[1].split(',')[0]) for i in elementsRaw]
		self.elementEnds = [int(i[2].split(',')[0]) for i in elementsRaw]
#		self.mutRate = float([i for i in lines if i.startswith('initializeMutationRate(')][0].strip().strip(';').strip(')').split('(')[1])
		self.mutRate = float(re.findall(regexString, [i for i in lines if i.startswith('initializeMutationRate(')][0])[0])
		print(self.mutRate)

def main():
	parser = argparse.ArgumentParser(description="PYTHON 3 !!!! This script takes a .trees file from SLiM 3.x and add neutral mutations to the simulated populations. ")
	parser.add_argument("--input","-i",
			required = True,
			dest = "input",
			type =str,
			help = "name the simulation config file")
	parser.add_argument("--trees","-t",
			required = True,
			dest = "trees",
			type =str,
			help = "name the trees file")
#	parser.add_argument("--N",
#			required = True,
#			dest = "N",
#			type = int,
#			help = "The population size that was simulated")
	parser.add_argument("--sample",
			required = True,
			dest = "sample",
			type = int,
			help = "The number of haploid copies to sample from the population")
#	parser.add_argument("--theta",
#			required = True,
#			dest = "theta",
#			type = float,
#			help = "The population effective mutation rate")
	parser.add_argument("-o",
			required = True,
			dest = "output",
			type =str,
			help = "name the output file")
	args = parser.parse_args()

	bgzipPath = '/home/booker/bin/htslib-1.9/'
	tabixPath = '/home/booker/bin/htslib-1.9/'

	config = SLiM(args.input)

	ts = pyslim.load(args.trees).simplify()
	N = ts.get_sample_size()

#	mut_rate = args.theta/(4.*args.N)

	sprinkled = msprime.mutate(ts, rate= config.mutRate, keep=True)

	mutated = remove_mutations(sprinkled, config.elementStarts, config.elementEnds, 0.0)

#	Here we strip out the neutral mutations that were added on top of elements that were already simulated

	haploidSample = np.random.choice(mutated.samples(),args.sample, replace = False)

	ts2 = sprinkled.simplify(samples = haploidSample)

	with open(args.output, "w") as vcf_file:
		ts2.write_vcf(vcf_file, 1)

	bgzip = [bgzipPath + 'bgzip', args.output]
	subprocess.call(bgzip)
	tabix = [tabixPath +'tabix', args.output+'.gz']
	subprocess.call(tabix)

	return
#	for tree in ts2.trees():
#		print("-" * 20)
#		print("tree {}: interval = {}".format(tree.index, tree.interval))
#		print(tree.draw(format="unicode"))

main()


from flask import Flask, request
import json
from markupsafe import escape
from flask_jwt_extended import JWTManager, jwt_required
from controller.utlisateur_controller import UtilisateurController

app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "Une phrase secrète d'une complexité à couper le souffle !"
jwt = JWTManager(app)

@app.route("/user/create", methods=["POST"])
@jwt_required()
def createUser():
    user = UtilisateurController()
    body = request.get_json()
    user.addUser(
        escape(body["email"]),
        escape(body["pseudo"]),
        escape(body["password"]),
        escape(body["role"])
    )
    response = {
        "result": "success"
    }
    return json.dumps(response, separators=(",",":"))

@app.route("/user/<id>/delete", methods=["DELETE"])
@jwt_required()
def deleteUser(id):
    user = UtilisateurController()
    user.deleteUser(int(escape(id)))
    response = {
        "result": "success"
    }
    return json.dumps(response, separators=(",",":"))

@app.route("/user/<id>", methods=["GET"])
@jwt_required()
def getUser(id):
    user = UtilisateurController()
    response = user.getUser(int(escape(id)))
    return json.dumps(response, separators=(",",":"))

@app.route("/user/<id>/update", methods=["PUT"])
@jwt_required()
def updateUser(id):
    user = UtilisateurController()
    body = request.get_json()
    user.updateUser(escape(id),body)
    response = {
        "result": "success"
    }
    return json.dumps(response, separators=(",",":"))

@app.route("/login", methods=["POST"])
def login():
    user = UtilisateurController()
    body = request.get_json()
    token = user.login(body)
    response = {
        "access_token": token
    }
    return json.dumps(response, separators=(",",":"))


# We import our Shapes class from the
#   shapes.py modeule
from shapes import Shapes

def main():
    # Create an instance of the Shape class
    my_shape = Shapes()

    # Call methods on our shape instance
    my_shape.draw_rectangle(0, 0, 100, 50)
    my_shape.draw_equ_triangle(10, 50, 125)
    my_shape.draw_sprial(0, 0, 10, 50)

    # Keep the window open
    my_shape.wait()

if __name__ == "__main__":
    main()


import numpy as np
from rlgym.envs import Match
from rlgym.utils.action_parsers import DiscreteAction
from stable_baselines3 import PPO
from stable_baselines3.common.callbacks import CheckpointCallback
from stable_baselines3.common.vec_env import VecMonitor, VecNormalize, VecCheckNan
from stable_baselines3.ppo import MlpPolicy

from rlgym.utils.obs_builders import AdvancedObs
from rlgym.utils.state_setters import DefaultState
from rlgym.utils.terminal_conditions.common_conditions import TimeoutCondition, NoTouchTimeoutCondition, GoalScoredCondition
from rlgym_tools.sb3_utils import SB3MultipleInstanceEnv
from rlgym_tools.extra_rewards.anneal_rewards import AnnealRewards, _DummyReward
from rlgym.utils.reward_functions.common_rewards.misc_rewards import EventReward
from rlgym.utils.reward_functions.common_rewards.player_ball_rewards import VelocityPlayerToBallReward, TouchBallReward
from rlgym.utils.reward_functions.common_rewards.ball_goal_rewards import VelocityBallToGoalReward
from rlgym.utils.reward_functions import CombinedReward

from rlgym_tools.extra_rewards.kickoff_reward import KickoffReward

from sb3_log_reward import SB3CombinedLogReward, SB3CombinedLogRewardCallback

import atexit
from torch.nn import Tanh

# V1->V2
# increased learning rate
# reduced epochs
# increased net size
# discrete actions instead of continuous
# obs includes other players
# better initial rewards for the first 100M steps
# 1v1 training against itself
# slower transitioning of rewards

if __name__ == '__main__':
    frame_skip = 8
    half_life_seconds = 5

    fps = 120 / frame_skip
    gamma = np.exp(np.log(0.5) / (fps * half_life_seconds))
    agents_per_match = 2
    num_instances = 1
    target_steps = 75_000
    steps = target_steps // (num_instances * agents_per_match)
    batch_size = steps

    def exit_save(model):
        model.save("models/exit_save")

    # train using the 2 velocity rewards until ~100M and then start using AnnealReward to phase into other rewards
    # need to make sure we don't drastically change rewards too quickly like we did on CarBallAI-V1
    # ... so use AnnealReward
    def get_match():
        return Match(
            team_size=1,
            tick_skip=frame_skip,
            reward_function=SB3CombinedLogReward
                (
                    (
                        VelocityPlayerToBallReward(),
                        VelocityBallToGoalReward(),
                        EventReward
                        (
                            team_goal=100.0
                        ),
                        EventReward
                        (
                            concede=-100.0
                        ),
                        EventReward
                        (
                            shot=5.0
                        ),
                        EventReward
                        (
                            save=30.0
                        ),
                        EventReward
                        (
                            demo=10.0
                        ),
                        TouchBallReward(),
                        KickoffReward()
                    ),
                        (0.1, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 0.15, 0.125)
                ),
            self_play=True,
            terminal_conditions=[TimeoutCondition(fps * 300), NoTouchTimeoutCondition(fps * 20), GoalScoredCondition()],
            obs_builder=AdvancedObs(),
            state_setter=DefaultState(),
            action_parser=DiscreteAction()
        )

    env = SB3MultipleInstanceEnv(get_match, num_instances)
    env = VecCheckNan(env)
    env = VecMonitor(env)
    env = VecNormalize(env, norm_obs=False, gamma=gamma)

    # load an existing model, or create a new one it doesn't exist
    try:
        print("Looking for existing model ...")
        model = PPO.load(
            "models/exit_save.zip",
            env,
            device="auto",
            custom_objects=dict(n_envs=env.num_envs, n_epochs=10, batch_size=75_000)
        )
        print("Existing model found.")
    except:
        from torch.nn import Tanh
        print("Creating new model ...")
        policy_kwargs = dict(
            activation_fn=Tanh,
            net_arch=[512, 512, dict(pi=[256, 256, 256], vf=[256, 256, 256])],
        )

        model = PPO(
            MlpPolicy,
            env,
            n_epochs=10,                 # PPO calls for multiple epochs
            policy_kwargs=policy_kwargs,
            learning_rate=5e-5,          # Around this is fairly common for PPO
            ent_coef=0.01,               # From PPO Atari
            vf_coef=1.,                  # From PPO Atari
            gamma=gamma,                 # Gamma as calculated using half-life
            verbose=3,                   # Print out all the info as we're going
            batch_size=batch_size,             # Batch size as high as possible within reason
            n_steps=steps,                # Number of steps to perform before optimizing network
            tensorboard_log="logs",  # `tensorboard --logdir logs/logs` in terminal to see graphs
            device="auto"                # Uses GPU if available
        )

    # Divide by num_envs (number of agents) because callback only increments every time all agents have taken a step
    callback = CheckpointCallback(round(2_500_000 / env.num_envs), save_path="models", name_prefix="CarBallAI-V2")
    rewardCallback = SB3CombinedLogRewardCallback(reward_names=["velocity_player_to_ball", "velocity_ball_to_goal", "team_goal", "opponent_goal", "shot", "save", "demolition", "ball_touch", "kickoff_reward"])

    atexit.register(exit_save, model)
    try:
        while True:
            model.learn(25_000_000, callback=[callback, rewardCallback], reset_num_timesteps=False)
            model.save("models/exit_save")
            model.save(f"mmr_models/{model.num_timesteps}")
    except Exception as e:
        print(e)


import wx
import wx.py as py
import sys

# Regular expression operations
import re
import os

# Conjurer
import pynebula
import childdialoggui
import freedialoggui
import nebulahelpevent as nh
import app
import stringtree
import servers

class TestListCtrl(wx.ListCtrl):
    def __init__(self, parent, ID, pos=wx.DefaultPosition,
                 size=wx.DefaultSize, style=0):
        wx.ListCtrl.__init__(self, parent, ID, pos, size, style)

class StringLister (wx.Panel):

    def __init__(self, parent, ID, style=0):
        wx.Panel.__init__(self, parent, -1, style=wx.WANTS_CHARS)

        tID = wx.NewId()

        self.string_list = {}

        self.scale = (64, 64)
        self.il = wx.ImageList(64, 64)
        self.list = TestListCtrl(self, tID,
                                 style=wx.LC_ICON
                                 | wx.BORDER_SUNKEN
                                 | wx.LC_EDIT_LABELS
                                 | wx.LC_SORT_ASCENDING
                                 | wx.LC_AUTOARRANGE
                                 )

        self.list.SetImageList(self.il, wx.IMAGE_LIST_NORMAL)

        # Bind events
        self.Bind(wx.EVT_SIZE, self.on_size)
        self.Bind(wx.EVT_LIST_ITEM_SELECTED, self.on_item_selected, self.list)
        self.Bind(wx.EVT_LIST_ITEM_ACTIVATED, self.on_item_activated, self.list)

        self.list.Bind(wx.EVT_LEFT_DCLICK, self.on_double_click)
        self.list.Bind(wx.EVT_COMMAND_RIGHT_CLICK, self.on_right_click)

    def display_normal (self):
        self.scale = (64, 64)
        self.il = wx.ImageList(64, 64)
        self.list.SetImageList(self.il, wx.IMAGE_LIST_NORMAL)
        self.list.SetWindowStyleFlag(wx.LC_ICON
                                    | wx.BORDER_SUNKEN
                                    | wx.LC_EDIT_LABELS
                                    | wx.LC_SORT_ASCENDING
                                    | wx.LC_AUTOARRANGE
                                    )

    def display_list (self):
        self.scale = (16, 16)
        self.il = wx.ImageList(16, 16)
        self.list.SetImageList(self.il, wx.IMAGE_LIST_SMALL)
        self.list.SetWindowStyleFlag(wx.LC_LIST
                                    | wx.BORDER_SUNKEN
                                    | wx.LC_EDIT_LABELS
                                    | wx.LC_SORT_ASCENDING
                                    | wx.LC_AUTOARRANGE
                                    )

    def display_large (self):
        self.scale = (128, 128)
        self.il = wx.ImageList(128, 128)
        self.list.SetImageList(self.il, wx.IMAGE_LIST_NORMAL)
        self.list.SetWindowStyleFlag(wx.LC_ICON
                                    | wx.BORDER_SUNKEN
                                    | wx.LC_EDIT_LABELS
                                    | wx.LC_SORT_ASCENDING
                                    | wx.LC_AUTOARRANGE
                                    )

    def on_item_selected(self, event):
        self.current_item = event.m_itemIndex

        event.Skip()

    def on_item_activated(self, event):
        self.current_item = event.m_itemIndex

    def on_double_click (self, event):
        event.Skip()

    def on_right_click (self, event):
        event.Skip()

    def populate_list(self, string_list):
        self.string_list = string_list
        items = string_list.items()
        self.list.DeleteAllItems()
        for key, data in items:
            bmp = wx.Bitmap(data[0])

            # scale if needed

            if self.scale != (128,128):
                image = bmp.ConvertToImage()
                bmp = wx.BitmapFromImage(image.Scale(self.scale[0], self.scale[1]))

            self.idx1 = self.il.Add(bmp)
            index = self.list.InsertImageStringItem(sys.maxint, data[1], self.idx1)
            self.list.SetItemData(index, key)

        self.current_item = 0

    def on_size(self, event):
        w,h = self.GetClientSizeTuple()
        self.list.SetDimensions(0, 0, w, h)

    def get_list (self):
        return self.list


import sys
n, c = map(int, sys.stdin.readline().rstrip().split())
houses = []
for i in range(n):
    houses.append(int(sys.stdin.readline()))

houses = sorted(houses)
start, end = 1, max(houses)
while start <= end:
    first = houses[0]
    mid = (start + end)//2
    count = 1
    for x in houses:
        if x >= first + mid:
            first = x
            count += 1
    if count >= c:
        start = mid + 1
    else:
        end = mid - 1
print(start - 1)


# Copyright (C) 2021 Victor Soupday
# This file is part of CC3_Blender_Tools <https://github.com/soupday/cc3_blender_tools>
#
# CC3_Blender_Tools is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# CC3_Blender_Tools is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with CC3_Blender_Tools.  If not, see <https://www.gnu.org/licenses/>.

import bpy
from . import nodeutils
from . import utils
from . import vars

# when updating linked materials, attempt to update the properties in all the material types in the same list:
LINKED_MATERIALS = [
    ["SKIN_HEAD", "SKIN_BODY", "SKIN_ARM", "SKIN_LEG"],
    ["EYE_RIGHT", "CORNEA_RIGHT", "OCCLUSION_RIGHT", "TEARLINE_RIGHT",
     "EYE_LEFT", "CORNEA_LEFT", "OCCLUSION_LEFT", "TEARLINE_LEFT"],
    ["TEETH_UPPER", "TEETH_LOWER"],
    ["HAIR", "SMART_HAIR", "SCALP", "EYELASH"]
]

# These material types must be updated together as they share the same properties:
PAIRED_MATERIALS = [
    ["EYE_RIGHT", "CORNEA_RIGHT"],
    ["EYE_LEFT", "CORNEA_LEFT"],
]

# These properties must be updated as linked as they are duplicated across the linked materials and must be kept in sync:
FORCE_LINKED_PROPS = ["skin_blend", "skin_normal_blend", "skin_mouth_ao", "skin_nostril_ao", "skin_lips_ao",
                      "skin_head_micronormal", "skin_body_micronormal", "skin_arm_micronormal", "skin_leg_micronormal",
                      "skin_head_tiling", "skin_body_tiling", "skin_arm_tiling", "skin_leg_tiling",]

# TODO will need to work something out to separate left from right eye materials...
FORCE_LINKED_TYPES = []
#["EYE", "CORNEA", "OCCLUSION", "TEARLINE"]

PROP_DEPENDANTS = {
    "eye_shadow_radius": ["eye_shadow_hardness"],
    "eye_iris_radius": ["eye_iris_hardness"],
}

PROP_MATRIX = [
    # Base Color groups
    {   "start": "(color_",
        "end": "_mixer)",
        "groups": [

            {   "name": "_skin_",
                "inputs": [
                    ["AO Strength", "skin_ao"],
                    ["Blend Strength", "skin_blend"],
                    ["Mouth AO", "skin_mouth_ao"],
                    ["Nostril AO", "skin_nostril_ao"],
                    ["Lips AO", "skin_lips_ao"],
                ],
            },

            {   "name": "_eye_",
                "inputs": [
                    ["AO Strength", "eye_ao"],
                    ["Blend Strength", "eye_blend"],
                    ["Shadow Radius", "eye_shadow_radius"],
                    ["Shadow Hardness", "eye_shadow_hardness", "parameters.eye_shadow_hardness * parameters.eye_shadow_radius * 0.99"],
                    ["Corner Shadow", "eye_shadow_color"],
                    ["Sclera Brightness", "eye_sclera_brightness"],
                    ["Iris Brightness", "eye_iris_brightness"],
                    ["Sclera Hue", "eye_sclera_hue"],
                    ["Iris Hue", "eye_iris_hue"],
                    ["Sclera Saturation", "eye_sclera_saturation"],
                    ["Iris Saturation", "eye_iris_saturation"],
                    ["Limbus Color", "eye_limbus_color"],
                ],
            },

            {   "name": "_hair_",
                "inputs": [
                    ["AO Strength", "hair_ao"],
                    ["Blend Strength", "hair_blend"],
                    ["Diffuse Bright", "hair_brightness"],
                    ["Diffuse Contrast", "hair_contrast"],
                    ["Diffuse Hue", "hair_hue"],
                    ["Diffuse Saturation", "hair_saturation"],
                    ["Aniso Color", "hair_aniso_color"],
                    ["Aniso Strength", "hair_aniso_strength"],
                    ["Aniso Strength Cycles", "hair_aniso_strength_cycles"],
                    ["Base Color Strength", "hair_vertex_color_strength"],
                    ["Base Color Strength", "hair_vertex_color_strength"],
                    ["Base Color Map Strength", "hair_base_color_map_strength"],
                    ["Depth Blend Strength", "hair_depth_strength"],
                    ["Diffuse Strength", "hair_diffuse_strength"],
                    ["Global Strength", "hair_global_strength"],
                    ["Root Color", "hair_root_color", "gamma_correct(parameters.hair_root_color)"],
                    ["End Color", "hair_end_color", "gamma_correct(parameters.hair_end_color)"],
                    ["Root Color Strength", "hair_root_strength"],
                    ["End Color Strength", "hair_end_strength"],
                    ["Invert Root and End Color", "hair_invert_strand"],
                    ["Highlight A Start", "hair_a_start"],
                    ["Highlight A Mid", "hair_a_mid"],
                    ["Highlight A End", "hair_a_end"],
                    ["Highlight A Strength", "hair_a_strength"],
                    ["Highlight A Overlap End", "hair_a_overlap"],
                    ["Highlight Color A", "hair_a_color", "gamma_correct(parameters.hair_a_color)"],
                    ["Highlight B Start", "hair_b_start"],
                    ["Highlight B Mid", "hair_b_mid"],
                    ["Highlight B End", "hair_b_end"],
                    ["Highlight B Strength", "hair_b_strength"],
                    ["Highlight B Overlap End", "hair_b_overlap"],
                    ["Highlight Color B", "hair_b_color", "gamma_correct(parameters.hair_b_color)"],
                ],
            },

            {   "name": "_eyelash_",
                "inputs": [
                    ["AO Strength", "hair_ao"],
                ],
            },

            {   "name": "_teeth_",
                "inputs": [
                    ["AO Strength", "teeth_ao"],
                    ["Front", "teeth_front"],
                    ["Rear", "teeth_rear"],
                    ["Teeth Brightness", "teeth_teeth_brightness"],
                    ["Teeth Saturation", "teeth_teeth_desaturation", "1 - parameters.teeth_teeth_desaturation"],
                    ["Gums Brightness", "teeth_gums_brightness"],
                    ["Gums Saturation", "teeth_gums_desaturation", "1 - parameters.teeth_gums_desaturation"],
                ],
            },

            {   "name": "_tongue_",
                "inputs": [
                    ["AO Strength", "tongue_ao"],
                    ["Front", "tongue_front"],
                    ["Rear", "tongue_rear"],
                    ["Brightness", "tongue_brightness"],
                    ["Saturation", "tongue_desaturation", "1 - parameters.tongue_desaturation"],
                ],
            },

            {   "name": "_nails_",
                "inputs": [
                    ["AO Strength", "nails_ao"],
                ],
            },

            {   "name": "_default_",
                "inputs": [
                    ["AO Strength", "default_ao"],
                    ["Blend Strength", "default_blend"],
                ],
            },
        ],
    },

    # Subsurface groups
    {   "start": "(subsurface_",
        "end": "_mixer)",
        "groups": [

            {   "name": "_skin_",
                "inputs": [
                    ["Radius", "skin_sss_radius", "parameters.skin_sss_radius * vars.UNIT_SCALE"],
                    ["Falloff", "skin_sss_falloff"],
                ],
            },

            {   "name": "_eye_",
                "inputs": [
                    ["Radius1", "eye_sss_radius", "parameters.eye_sss_radius * vars.UNIT_SCALE"],
                    ["Radius2", "eye_sss_radius", "parameters.eye_sss_radius * vars.UNIT_SCALE"],
                    ["Falloff1", "eye_sss_falloff"],
                    ["Falloff2", "eye_sss_falloff"],
                ],
            },

            {   "name": "_hair_",
                "inputs": [
                    ["Radius", "hair_sss_radius", "parameters.hair_sss_radius * vars.UNIT_SCALE"],
                    ["Falloff", "hair_sss_falloff"],
                ],
            },

            {   "name": "_teeth_",
                "inputs": [
                    ["Radius1", "teeth_sss_radius", "parameters.teeth_sss_radius * vars.UNIT_SCALE"],
                    ["Radius2", "teeth_sss_radius", "parameters.teeth_sss_radius * vars.UNIT_SCALE"],
                    ["Falloff1", "teeth_sss_falloff"],
                    ["Falloff2", "teeth_sss_falloff"],
                    ["Scatter1", "teeth_gums_sss_scatter"],
                    ["Scatter2", "teeth_teeth_sss_scatter"],
                ],
            },

            {   "name": "_tongue_",
                "inputs": [
                    ["Scatter", "tongue_sss_scatter"],
                    ["Radius", "tongue_sss_radius", "parameters.tongue_sss_radius * vars.UNIT_SCALE"],
                    ["Falloff", "tongue_sss_falloff"],
                ],
            },

            {   "name": "_nails_",
                "inputs": [
                    ["Radius", "nails_sss_radius", "parameters.nails_sss_radius * vars.UNIT_SCALE"],
                    ["Falloff", "nails_sss_falloff"],
                ],
            },

            {   "name": "_default_",
                "inputs": [
                    ["Radius", "default_sss_radius", "parameters.default_sss_radius * vars.UNIT_SCALE"],
                    ["Falloff", "default_sss_falloff"],
                ],
            },
        ],
    },

    # MSR groups
    {   "start": "(msr_",
        "end": "_mixer)",
        "groups": [

            {   "name": ["_skin_head_", "_skin_body_", "_skin_arm_", "_skin_leg_"],
                "inputs": [
                    ["Roughness Power", "skin_roughness_power"],
                    ["Roughness Remap", "skin_roughness"],
                    ["Specular", "skin_specular"],
                ],
            },

            {   "name": "_eye_",
                "inputs": [
                    ["Specular1", "eye_specular"],
                    ["Roughness1", "eye_sclera_roughness"],
                ],
            },

            {   "name": "_cornea_",
                "inputs": [
                    ["Specular1", "eye_specular"],
                    ["Specular2", "eye_specular"],
                    ["Roughness1", "eye_sclera_roughness"],
                    ["Roughness2", "eye_iris_roughness"],
                ],
            },

            {   "name": "_hair_",
                "inputs": [
                    ["Roughness Remap", "hair_roughness"],
                    ["Specular", "hair_specular"],
                ],
            },

            {   "name": "_scalp_",
                "inputs": [
                    ["Roughness Remap", "hair_scalp_roughness"],
                    ["Specular", "hair_scalp_specular"],
                ],
            },

            {   "name": "_eyelash_",
                "inputs": [
                    ["Roughness Remap", "hair_eyelash_roughness"],
                    ["Specular", "hair_eyelash_specular"],
                ],
            },

            {   "name": "_teeth_",
                "inputs": [
                    ["Specular2", "teeth_specular"],
                ],
            },

            {   "name": "_tongue_",
                "inputs": [
                    ["Specular2", "tongue_specular"],
                    ["Roughness2", "tongue_roughness"],
                ],
            },

            {   "name": "_nails_",
                "inputs": [
                    ["Roughness Remap", "nails_roughness"],
                    ["Specular", "nails_specular"],
                ],
            },

            {   "name": "_default_",
                "inputs": [
                    ["Roughness Remap", "default_roughness"],
                ],
            },
        ],
    },

    # Normal groups
    {   "start": "(normal_",
        "end": "_mixer)",
        "groups": [

            {   "name": "_skin_head_",
                "inputs": [
                    ["Normal Blend Strength", "skin_normal_blend"],
                    ["Micro Normal Strength", "skin_head_micronormal"],
                ],
            },

            {   "name": "_skin_body_",
                "inputs": [
                    ["Normal Blend Strength", "skin_normal_blend"],
                    ["Micro Normal Strength", "skin_body_micronormal"],
                ],
            },

            {   "name": "_skin_arm_",
                "inputs": [
                    ["Normal Blend Strength", "skin_normal_blend"],
                    ["Micro Normal Strength", "skin_arm_micronormal"],
                ],
            },

            {   "name": "_skin_leg_",
                "inputs": [
                    ["Normal Blend Strength", "skin_normal_blend"],
                    ["Micro Normal Strength", "skin_leg_micronormal"],
                ],
            },

            {   "name": "_eye_",
                "inputs": [
                    ["Micro Normal Strength", "eye_sclera_normal", "1 - parameters.eye_sclera_normal"],
                    ["Sclera Normal Strength", "eye_sclera_normal", "1 - parameters.eye_sclera_normal"],
                    ["Blood Vessel Height", "eye_blood_vessel_height", "parameters.eye_blood_vessel_height/1000"],
                    ["Iris Bump Height", "eye_iris_bump_height", "parameters.eye_iris_bump_height/1000"],
                ],
            },

            {   "name": ["_hair_", "_scalp_", "_eyelash_"],
                "inputs": [
                    ["Bump Map Height", "hair_bump", "parameters.hair_bump / 1000"],
                    ["Bump Map Midpoint", "hair_fake_bump_midpoint"],
                ],
            },

            {   "name": "_teeth_",
                "inputs": [
                    ["Micro Normal Strength", "teeth_micronormal"],
                ],
            },

            {   "name": "_tongue_",
                "inputs": [
                    ["Micro Normal Strength", "tongue_micronormal"],
                ],
            },

            {   "name": "_nails_",
                "inputs": [
                    ["Micro Normal Strength", "nails_micronormal"],
                ],
            },

            {   "name": "_default_",
                "inputs": [
                    ["Normal Blend Strength", "default_normal_blend"],
                    ["Micro Normal Strength", "default_micronormal"],
                    ["Bump Map Height", "default_bump", "parameters.default_bump / 1000"],
                ],
            },
        ],
    },

    # Tiling groups
    {   "start": "(tiling_",
        "end": "_mapping)",
        "groups": [

            {   "name": "_skin_head_",
                "inputs": [
                    ["Tiling", "skin_head_tiling"],
                ],
            },

            {   "name": "_skin_body_",
                "inputs": [
                    ["Tiling", "skin_body_tiling"],
                ],
            },

            {   "name": "_skin_arm_",
                "inputs": [
                    ["Tiling", "skin_arm_tiling"],
                ],
            },

            {   "name": "_skin_leg_",
                "inputs": [
                    ["Tiling", "skin_leg_tiling"],
                ],
            },

            # TODO: this could be part of the iris mask group and linked...
            {   "name": "_normal_sclera_",
                "inputs": [
                    ["Tiling", "eye_sclera_tiling"],
                ],
            },

            # TODO: this could be part of the iris mask group and linked...
            {   "name": "_color_sclera_",
                "inputs": [
                    ["Tiling", "eye_sclera_scale", "1.0 / parameters.eye_sclera_scale"],
                ],
            },

            {   "name": "_teeth_",
                "inputs": [
                    ["Tiling", "teeth_tiling"],
                ],
            },

            {   "name": "_tongue_",
                "inputs": [
                    ["Tiling", "tongue_tiling"],
                ],
            },

            {   "name": "_nails_",
                "inputs": [
                    ["Tiling", "nails_tiling"],
                ],
            },

            {   "name": "_default_",
                "inputs": [
                    ["Tiling", "default_tiling"],
                ],
            },
        ],
    },

    # Mask groups
    {   "start": "(",
        "end": "_mask)",
        "groups": [

            {   "name": "(iris_mask)",
                "inputs": [
                    ["Scale", "eye_iris_scale", "1.0 / parameters.eye_iris_scale"],
                    ["Radius", "eye_iris_radius"],
                    ["Hardness", "eye_iris_hardness", "parameters.eye_iris_radius * parameters.eye_iris_hardness * 0.99"],
                    ["Limbus Radius", "eye_limbus_radius"],
                    ["Limbus Hardness", "eye_limbus_hardness"],
                ],
            },

            {   "name": "(eye_occlusion_mask)",
                "inputs": [
                    ["Strength", "eye_occlusion"],
                    ["Hardness", "eye_occlusion_hardness"],
                ],
            },

            {   "name": "(eye_occlusion_adv_mask)",
                "inputs": [
                    ["Strength", "eye_occlusion_strength"],
                    ["Strength2", "eye_occlusion_2nd_strength"],
                    ["Power", "eye_occlusion_power"],
                    ["Top Min", "eye_occlusion_top_min"],
                    ["Top Max", "eye_occlusion_top_max"],
                    ["Top Curve", "eye_occlusion_top_curve"],
                    ["Bottom Min", "eye_occlusion_bottom_min"],
                    ["Bottom Max", "eye_occlusion_bottom_max"],
                    ["Bottom Curve", "eye_occlusion_bottom_curve"],
                    ["Inner Min", "eye_occlusion_inner_min"],
                    ["Inner Max", "eye_occlusion_inner_max"],
                    ["Outer Min", "eye_occlusion_outer_min"],
                    ["Outer Max", "eye_occlusion_outer_max"],
                    ["Top2 Min", "eye_occlusion_2nd_top_min"],
                    ["Top2 Max", "eye_occlusion_2nd_top_max"],
                    ["Tear Duct Position", "eye_occlusion_tear_duct_position"],
                    ["Tear Duct Width", "eye_occlusion_tear_duct_width"],
                ],
            },
        ],
    },

    # Shaders
    {   "start": "(rl_",
        "end": "_shader)",
        "groups": [

            {   "name": "eye_occlusion_shader",
                "inputs": [
                    ["Color", "eye_occlusion_color"],
                    ["Strength", "eye_occlusion_strength"],
                    ["Strength2", "eye_occlusion_2nd_strength"],
                    ["Power", "eye_occlusion_power"],
                    ["Top Min", "eye_occlusion_top_min"],
                    ["Top Max", "eye_occlusion_top_max"],
                    ["Top Curve", "eye_occlusion_top_curve"],
                    ["Bottom Min", "eye_occlusion_bottom_min"],
                    ["Bottom Max", "eye_occlusion_bottom_max"],
                    ["Bottom Curve", "eye_occlusion_bottom_curve"],
                    ["Inner Min", "eye_occlusion_inner_min"],
                    ["Inner Max", "eye_occlusion_inner_max"],
                    ["Outer Min", "eye_occlusion_outer_min"],
                    ["Outer Max", "eye_occlusion_outer_max"],
                    ["Top2 Min", "eye_occlusion_2nd_top_min"],
                    ["Top2 Max", "eye_occlusion_2nd_top_max"],
                    ["Tear Duct Position", "eye_occlusion_tear_duct_position"],
                    ["Tear Duct Width", "eye_occlusion_tear_duct_width"],
                ],
            },
        ],
    },

    # Individual nodes
    {   "start": "",
        "end": "",
        "groups": [

            {   "name": "teeth_roughness",
                "inputs": [
                    [1, "teeth_roughness"],
                ],
            },

            {   "name": "eye_tearline_shader",
                "inputs": [
                    ["Alpha", "tearline_alpha"],
                    ["Roughness", "tearline_roughness"],
                ],
            },

            {   "name": "eye_occlusion_shader",
                "inputs": [
                    ["Base Color", "eye_occlusion_color"],
                ],
            },

            {   "name": "eye_occlusion_adv_shader",
                "inputs": [
                    ["Base Color", "eye_occlusion_color"],
                ],
            },

            {   "name": "hair_opacity",
                "inputs": [
                    [1, "hair_opacity"],
                ],
            },

            {   "name": "scalp_opacity",
                "inputs": [
                    [1, "hair_scalp_opacity"],
                ],
            },

            {   "name": "eyelash_opacity",
                "inputs": [
                    [1, "hair_eyelash_opacity"],
                ],
            },

            {   "name": "default_opacity",
                "inputs": [
                    [1, "default_opacity"],
                ],
            },

            # dummy entry so that 'eye_iris_depth_radius' wil update linked materials...
            {   "name": "_CC3IID_DO_NOT_MATCH_",
                "inputs": [
                    [0, "eye_iris_depth_radius"],
                ],
            },
        ],
    },
]

# property_name, material_type, modifer_type, modifer_name, eval_code
MODIFIER_MATRIX = [
    [ "eye_iris_depth", "EYE_RIGHT", "DISPLACE", "Eye_Displace_R", "mod.strength = parameters.eye_iris_depth"],
    [ "eye_pupil_scale", "EYE_RIGHT", "UV_WARP", "Eye_UV_Warp_R", "mod.scale = (1.0 / parameters.eye_pupil_scale, 1.0 / parameters.eye_pupil_scale)" ],

    [ "eye_iris_depth", "EYE_LEFT", "DISPLACE", "Eye_Displace_L", "mod.strength = parameters.eye_iris_depth"],
    [ "eye_pupil_scale", "EYE_LEFT", "UV_WARP", "Eye_UV_Warp_L", "mod.scale = (1.0 / parameters.eye_pupil_scale, 1.0 / parameters.eye_pupil_scale)" ],

    [ "eye_occlusion_displace", "OCCLUSION_RIGHT", "DISPLACE", "Occlusion_Displace_All_R", "mod.strength = parameters.eye_occlusion_displace"],
    [ "eye_occlusion_inner", "OCCLUSION_RIGHT", "DISPLACE", "Occlusion_Displace_Inner_R", "mod.strength = parameters.eye_occlusion_inner"],
    [ "eye_occlusion_outer", "OCCLUSION_RIGHT", "DISPLACE", "Occlusion_Displace_Outer_R", "mod.strength = parameters.eye_occlusion_outer"],
    [ "eye_occlusion_top", "OCCLUSION_RIGHT", "DISPLACE", "Occlusion_Displace_Top_R", "mod.strength = parameters.eye_occlusion_top"],
    [ "eye_occlusion_bottom", "OCCLUSION_RIGHT", "DISPLACE", "Occlusion_Displace_Bottom_R", "mod.strength = parameters.eye_occlusion_bottom"],

    [ "eye_occlusion_displace", "OCCLUSION_LEFT", "DISPLACE", "Occlusion_Displace_All_L", "mod.strength = parameters.eye_occlusion_displace"],
    [ "eye_occlusion_inner", "OCCLUSION_LEFT", "DISPLACE", "Occlusion_Displace_Inner_L", "mod.strength = parameters.eye_occlusion_inner"],
    [ "eye_occlusion_outer", "OCCLUSION_LEFT", "DISPLACE", "Occlusion_Displace_Outer_L", "mod.strength = parameters.eye_occlusion_outer"],
    [ "eye_occlusion_top", "OCCLUSION_LEFT", "DISPLACE", "Occlusion_Displace_Top_L", "mod.strength = parameters.eye_occlusion_top"],
    [ "eye_occlusion_bottom", "OCCLUSION_LEFT", "DISPLACE", "Occlusion_Displace_Bottom_L", "mod.strength = parameters.eye_occlusion_bottom"],

    [ "tearline_displace", "TEARLINE_RIGHT", "DISPLACE", "Tearline_Displace_All_R", "mod.strength = -parameters.tearline_displace"],
    [ "tearline_inner", "TEARLINE_RIGHT", "DISPLACE", "Tearline_Displace_Inner_R", "mod.strength = -parameters.tearline_inner"],

    [ "tearline_displace", "TEARLINE_LEFT", "DISPLACE", "Tearline_Displace_All_L", "mod.strength = -parameters.tearline_displace"],
    [ "tearline_inner", "TEARLINE_LEFT", "DISPLACE", "Tearline_Displace_Inner_L", "mod.strength = -parameters.tearline_inner"],

]

BASIC_PROPS = [
    ["OUT", "Value",    "", "skin_ao"],
    ["OUT", "Value",    "", "skin_basic_specular"],
    ["IN", "To Min",    "", "skin_basic_roughness"],
    ["OUT", "Value",    "", "eye_specular"],
    ["OUT", "Value",    "", "eye_basic_roughness"],
    ["OUT", "Value",    "", "eye_basic_normal"],
    ["IN", "Strength",  "", "eye_occlusion"],
    ["IN", "Value",     "eye_basic_hsv", "eye_basic_brightness"],
    ["OUT", "Value",    "", "teeth_specular"],
    ["IN", 1,           "", "teeth_roughness"],
    ["OUT", "Value",    "", "tongue_specular"],
    ["IN", 1,           "", "tongue_roughness"],
    ["OUT", "Value",    "", "nails_specular"],
    ["OUT", "Value",    "", "hair_ao"],
    ["OUT", "Value",    "", "hair_specular"],
    ["OUT", "Value",    "", "hair_scalp_specular"],
    ["OUT", "Value",    "", "hair_bump", "parameters.hair_bump / 1000"],
    ["OUT", "Value",    "", "default_ao"],
    ["OUT", "Value",    "", "default_bump", "parameters.default_bump / 1000"],
    ["IN", "Alpha",     "eye_tearline_shader", "tearline_alpha"],
    ["IN", "Roughness", "eye_tearline_shader", "tearline_roughness"],
]

def get_prop_matrix(prop_name):
    matrix = []
    for mixer in PROP_MATRIX:
        for group in mixer["groups"]:
            for input in group["inputs"]:
                if input[1] == prop_name:
                    matrix.append([mixer, group, input])
    return matrix

def get_prop_matrix_group(group_name):
    group_name = "(" + group_name + ")"
    for mixer in PROP_MATRIX:
        if mixer["start"] in group_name:
            if mixer["end"] in group_name:
                for group in mixer["groups"]:
                    if type(group["name"]) is list:
                        for name in group["name"]:
                            if name in group_name:
                                return group
                    else:
                        if group["name"] in group_name:
                            return group
    return None

def set_from_prop_matrix(node, cache, group_name):
    props = bpy.context.scene.CC3ImportProps
    parameters = cache.parameters
    scope = locals()

    group = get_prop_matrix_group(group_name)

    for input in group["inputs"]:
        try:
            if len(input) == 3:
                prop_eval = input[2]
            else:
                prop_eval = "parameters." + input[1]

            prop_value = eval(prop_eval, None, scope)

            nodeutils.set_node_input(node, input[0], prop_value)

        except Exception as e:
                utils.log_error("set_from_prop_matrix(): Unable to evaluate or set: " + prop_eval, e)


# https://leetcode.com/problems/rotate-list/

# Given a linked list, rotate the list to the right by k places, where k is non-negative.

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def rotateRight(self, head: ListNode, k: int) -> ListNode:
        if not head:
            return head

        n = 0
        temp = last = head
        while temp:
            n += 1
            last, temp = temp, temp.next

        k = k % n
        if k == 0:
            return head

        k = n - k
        curr = prev = head
        while k:
            prev, curr = curr, curr.next
            k -= 1

        last.next, prev.next = head, None
        return curr

30-Day LeetCoding Challenge/October/Medium/61. Rotate List.py


import sys
input=sys.stdin.readline
n=int(input())
lst=list(map(int,input().split()))
m=int(input())
check=list(map(int,input().split()))
result=[0]*m

# -10 -10 2 3 3 6 7 10 10 10
def bin(start, end, i):
    x=check[i]
    if start>end:
        return

    mid=(start+end)//2
    if lst[mid]==x:
        result[i]=lst[start:end+1].count(x)
        return

    if x>lst[mid]:
        bin(mid+1, end, i)
    else:
        bin(start, mid-1, i)

find={}
lst.sort()
for i,c in enumerate(check):
    if c in find:
        result[i]=find[c]
        continue
    bin(0,n-1,i)
    find[c]=result[i]

print(*result)