# Import necessary libraries
import openai
import sys
import json
import html
import re
import ssl
import os
import pprint
import nltk
import requests
import time

if not nltk.data.find('tokenizers/punkt'):
    nltk.download('punkt', quiet=True)

# Get the first command line argument
location = sys.argv[1]
sku = sys.argv[2]

# Initialize an empty dictionary for credentials
credentials = {}

# Define the file path to the credentials file
creds_file_path = os.path.join(
    os.path.dirname(os.path.abspath(__file__)),  # Get the directory of the current file
    "../creds2.txt"  # Append the relative path to the credentials file
)
if os.path.exists('product.json'):
    os.remove('product.json')

# Define a class to represent a location
class Location:
    def __init__(self, website, user, city, phone, consumer_key, consumer_secret, api_key):
        self.website = website
        self.user = user
        self.city = city
        self.phone = phone
        self.consumer_key = consumer_key
        self.consumer_secret = consumer_secret
        self.api_key = api_key  # Here's the new attribute

# Initialize an empty list to store locations
locations = []

# Open the credentials file
with open(creds_file_path) as f:
    # Initialize variables for parsing the file
    website = None
    user = None
    city = None
    phone = None
    consumer_key = None
    consumer_secret = None
    openai.api_key = None
    # Loop over each line in the file
    for line in f:
        line = line.strip()  # Remove trailing and leading whitespace
        # If the line is a website (indicated by brackets), store it and reset other variables
        if line.startswith("[") and line.endswith("]"):
            if website and user and city and phone and consumer_key and consumer_secret and openai.api_key:
                locations.append(Location(website, user, city, phone, consumer_key, consumer_secret, openai.api_key))
            website = line[1:-1].lstrip()  # Remove the brackets and any leading whitespace
            user = None
            city = None
            phone = None
            consumer_key = None
            consumer_secret = None
            openai.api_key = None
        # If the line starts with a bracket but doesn't end with one, it's a multiline website; just store the first part
        elif line.startswith("["):
            website = line[1:]
        # If the line ends with a bracket but doesn't start with one, it's the end of a multiline website; append this part
        elif line.endswith("]"):
            website += line[:-1]
        # If the line contains " = ", it's a key-value pair; parse and store it
        elif website and " = " in line:
            key, value = line.split(" = ")
            if key == "user":
                user = value
            elif key == "city":
                city = value
            elif key == "phone":
                phone = value
            elif key.lower().endswith("_consumer_key"):
                consumer_key = value
            elif key.lower().endswith("_consumer_secret"):
                consumer_secret = value
            elif key == "openai.api_key":
                openai.api_key = value

    # Once we've parsed the entire file, check if there are any leftover variables and, if so, add another location
    if website and user and city and phone and consumer_key and consumer_secret and openai.api_key:
        locations.append(Location(website, user, city, phone, consumer_key, consumer_secret, openai.api_key))

# Print the locations
for location in locations:
    print("Using " + location.website + " as source product." + sku)
    base_url = "https://" + location.website + "/wp-json/wc/v3/products"
    city = location.city
    phone = location.phone
    consumer_key = location.website + "_consumer_key:" + location.consumer_key
    consumer_secret = location.website + "_consumer_secret:" + location.consumer_secret

    auth = (
     location.consumer_key,
     location.consumer_secret,
           )
    response = requests.get(f'{base_url}', auth=auth, params={'sku': sku})
    response.raise_for_status()

    if not response.json():
        print(f"No product found with SKU: {sku}")
        exit()

    product = response.json()[0]

    #time.sleep(1)
    # pprint.pprint(product)
    print("Source site: ", location.website)
    print("Product to clone: ", product['sku'], "Name: ", product['name'])
    print()
    break

time.sleep(1)
print("Turn AI loose...")
for location in locations[1:]:
    print("Using " + location.website + " as source product." + sku)
    base_url = "https://" + location.website + "/wp-json/wc/v3/products"
    consumer_key = location.website + "_consumer_key:" + location.consumer_key
    consumer_secret = location.website + "_consumer_secret:" + location.consumer_secret

    auth = (
     location.consumer_key,
     location.consumer_secret,
           )

    response = requests.get(f'{base_url}', auth=auth, params={'sku': sku})
    response.raise_for_status()

    if not response.json():
        print(f"No product found with SKU: {sku}")
        exit()

    product = response.json()[0]

    sku = product['sku']
    city = location.city
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages = [
            {
                "role": "system",
                "content": "You are a helpful budtender who knows all about the cannabis industry in Northern California. ",
                },
            {
                "role": "user",
                "content": f"I have a product with SKU '{sku}' named '{product['name']}' with a short description of '{product['short_description']}'."
                f"I need a new but similar name for this product that will both help with SEO and improve the product visibility in search engines."
                f"Use the city name '{city}' in the title. Examples of really good usage would be variations of things like Best in Burlingame or San Ramons best. "
                f"Don't stray too far from the core idea of the original name.  Add the city name to the product title somehow. "
                f"Limit the new product name to about 70 characters.  Do not use any punctuation or apostrophes or any single or double quotes. "
                f"Use proper capitalization. Optimize all for SEO.  Never use prices in the new titles."
                },
            ]
        )

    new_product_name = response['choices'][0]['message']['content'].strip()
    new_product_name = html.unescape(re.sub('<.*?>', '', new_product_name))
    print("Suggested new product name: ", new_product_name)
    product['name'] = new_product_name

    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages = [
            {
                "role": "system",
                "content": "You are a helpful budtender who knows all about the cannabis industry in and around the city of '{city}'. ",
                },
            {
                "role": "user",
                "content": f"I am a budtender looking to deliver cannabis products to my customers. "
                f"Create a list of 5 possible public meetup spots in the area of '{city}' that can be used as meetup spots if the customer doesnt want to meet at home or work. Prioritize discreet and easy access."
                f"The locations should be convenient, well known, and not near a school or police station. Present them as a bullet list with short descriptions as to why they are good meetup spots."
                },
            ]
        )

    meetup_spots = response['choices'][0]['message']['content'].strip()
    meetup_spots = html.unescape(re.sub('<.*?>', '', meetup_spots))
    product['description'] = product['description'] + " \n\n  Have your " + new_product_name +  " delivered to your home or work or choose one of these great meetup spots in " + city + "\n" +  meetup_spots
    #print("Suggested meetups spots for ",city,": ", meetup_spots)
    #print("New product description", product['description'])
    update_url = f'{base_url}/{product["id"]}'
    update_response = requests.put(update_url, json=product, auth=auth)
    update_response.raise_for_status()
    break


import os
import cv2
import numpy as np
from keras.models import load_model
from keras.utils import to_categorical

# Define the path to the FER-2013 test dataset directory
test_data_path = "/content/Dissertation-Project/dataset/test"

# Define the list of emotions and their corresponding labels
emotion_labels = {
    'angry': 0,
    'disgust': 1,
    'fear': 2,
    'happy': 3,
    'neutral': 4,
    'sad': 5,
    'surprise': 6
}

# Load the test images and labels
test_images = []
test_labels = []

for emotion_folder in os.listdir(test_data_path):
    emotion = emotion_folder
    emotion_folder_path = os.path.join(test_data_path, emotion_folder)

    # Skip non-directory files
    if not os.path.isdir(emotion_folder_path):
        continue

    for image_filename in os.listdir(emotion_folder_path):
        # Skip non-image files
        if not image_filename.endswith(('.jpg', '.jpeg', '.png')):
            continue

        image_path = os.path.join(emotion_folder_path, image_filename)
        image = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
        image = cv2.resize(image, (48, 48))  # Resize images to the desired shape
        test_images.append(image)
        test_labels.append(emotion_labels[emotion])

# Convert test images and labels to numpy arrays
test_images = np.array(test_images)
test_labels = np.array(test_labels)

# Normalize pixel values to range between 0 and 1
test_images = test_images / 255.0

# Perform one-hot encoding on the test labels
test_labels = to_categorical(test_labels)

# Load the trained model
model_path = "/content/Dissertation-Project/my_model_with_early_stopping.keras"
model = load_model(model_path)

# Evaluate the model on the test set
loss, accuracy = model.evaluate(test_images, test_labels)

print(f"Test Loss: {loss:.4f}")
print(f"Test Accuracy: {accuracy:.4f}")


#!/usr/bin/python3
"""
Executes storage when module is called
"""
from models.engine import file_storage
from models.base_model import BaseModel
from models.user import User
from models.review import Review
from models.place import Place
from models.city import City
from models.amenity import Amenity
from models.state import State

storage = file_storage.FileStorage()
storage.reload()

if __name__ != '__main__':
    def error_check(list_arg=[], n=0):
        """Check for common error messages for all methods"""
        check_class = ['BaseModel', 'User', 'State',
                       'City', 'Amenity', 'Place', 'Review']
        try:
            class_name = "{}".format(list_arg[0])
        except IndexError:
            if len(list_arg) == 0:
                print("** class name missing **")
                return 0
        if class_name not in check_class:
            print("** class doesn't exist **")
            return 0

        if n == 0:
            try:
                key = "{}.{}".format(list_arg[0], list_arg[1])
                return key
            except IndexError:
                if len(list_arg) == 1:
                    print("** instance id missing **")
                    return 0


import signal
import time
import sys
from typing import *
from multiprocessing import Process, Pipe, Value
from multiprocessing.connection import Connection
from threading import Thread

import zmq
import pyaudio
import numpy as np

from config import sr, input_dim, dtw_k, dtw_cost, channels
from transformer import mfcc_transform, audio_length
from dtw import DTW
from predictor import Predictor

DATA_ENDPOINT = 'ipc://twitrecog_data'
COMMAND_ENDPOINT = 'ipc://twitrecog_command'

result_queue = []
found_sleep = False

class AudioHandler(object):
    def __init__(self, callback, predictors: List[Predictor]):
        self.reader, self.writer = Pipe(False)
        self.exiter = Value('b', False)
        self.p: Process = None
        self.t: Thread = None
        self.buf = np.empty((0,), dtype=np.float32)
        self.callback = callback
        self.predictors = predictors

        self.CHUNK = 1536
        self.sleepy = (self.CHUNK / sr / 2)
        self.skippy = 0

    def start(self):
        self.p = Process(
            target=AudioHandler._start_process,
            args=(self.CHUNK, self.writer, self.exiter)
        )
        self.p.start()
        self.t = Thread(
            target=self._start_thread
        )
        self.t.start()

    def _predict(self) -> Tuple[bool, int]:
        for predictor in self.predictors:
            sample_len = predictor.get_sample_len()
            if sample_len > self.buf.shape[0]:
                continue
            x = self.buf[:sample_len]
            x_transformed, _ = mfcc_transform(x, sr, input_dim)
            if predictor.predict(x_transformed):
                return True, predictor.get_sample_len()
        return False, 0

    def _start_thread(self):
        min_sample_len = np.min([x.get_sample_len() for x in self.predictors])
        skipping = False
        while not self.exiter.value:
            recv = np.frombuffer(self.reader.recv_bytes(), np.float32)
            self.buf = np.append(self.buf, recv)

            if self.skippy > 0:
                skipped = self.skippy
                if self.buf.shape[0] < skipped:
                    skipped = self.buf.shape[0]
                self.skippy -= skipped
                self.buf = self.buf[skipped:]
            while self.skippy == 0 and self.buf.shape[0] > min_sample_len:
                if skipping:
                    self.callback(False)
                    skipping = False
                skipping, self.skippy = self._predict()
                if skipping:
                    self.callback(True)

                self.buf = self.buf[self.CHUNK:]

            time.sleep(self.sleepy)

    @staticmethod
    def _start_process(chunk, conn: Connection, should_exit):
        def cb(in_data, frame_count, time_info, flag):
            conn.send_bytes(in_data)
            return None, pyaudio.paContinue

        def signal_cb(_, __):
            should_exit.value = True

        signal.signal(signal.SIGINT, signal_cb)
        p = pyaudio.PyAudio()
        print('process started')
        stream = p.open(format=pyaudio.paFloat32,
                        channels=channels,
                        rate=sr,
                        input=True,
                        output=False,
                        stream_callback=cb,
                        frames_per_buffer=chunk)
        while not should_exit.value:
            time.sleep(0.1)
        stream.close()
        p.terminate()

    def stop(self):
        self.exiter.value = True
        self.p.join()
        self.t.join()

def get_models(root: str) -> Tuple[List[List[Tuple[np.ndarray, int]]], List[Tuple[np.ndarray, int]]]:
    import os
    import glob
    from config import models_glob, models_rel_path, active_path, preprocessed_rel_path, model_extension
    from config import recording_extension

    ret = []
    false_ret = []
    path = os.path.join(root, models_rel_path)
    for pattern_name in os.listdir(path):
        print('found pattern', pattern_name)
        pattern_path = os.path.join(path, pattern_name)
        with open(os.path.join(pattern_path, active_path), 'r') as active_file:
            if active_file.read() == 'false':
                continue
        if pattern_name == 'false':
            target = false_ret
        else:
            ret.append([])
            target = ret[-1]

        pattern_models_paths = [os.path.join(pattern_path, pathname)
                                for pathname in glob.glob(os.path.join(pattern_path, models_glob))]
        for pattern_model_path in pattern_models_paths:
            pattern_model_idx = pattern_model_path[pattern_model_path.rindex("/") + 1:].replace(model_extension, '')
            audio_len = int(np.ceil(audio_length(os.path.join(
                root, preprocessed_rel_path,
                pattern_name,
                pattern_model_idx + recording_extension
            )) * sr))
            target.append((np.load(pattern_model_path), audio_len))

    return ret, false_ret

def main():
    context = zmq.Context()
    socket: zmq.Socket = context.socket(zmq.PUB)

    done: zmq.Socket = context.socket(zmq.SUB)
    done.subscribe('')
    done.setsockopt(zmq.RCVTIMEO, 2000)

    socket.connect(DATA_ENDPOINT)
    done.connect(COMMAND_ENDPOINT)

    predictors = []
    models, false_model = get_models(sys.argv[1])
    for model in models:
        x = [recording for recording, _ in model]
        sample_len = int(np.ceil(np.average([recording_len for _, recording_len in model])))
        predictor = DTW(dtw_cost, sample_len)
        predictor.compile(dtw_k)
        predictor.train(x, [[0.0, 1.0] for _ in range(len(model))])
        predictors.append(predictor)

    def cb(found):
        socket.send_string('true' if found else 'false')
        print('found!', found)

    audio_handler = AudioHandler(cb, predictors)
    audio_handler.start()

    signal_received = False

    def signal_cb(_, __):
        nonlocal signal_received
        signal_received = True

    signal.signal(signal.SIGINT, signal_cb)
    print('started')
    socket.send_string('started')
    while not signal_received:
        try:
            string = done.recv_string()
            if string == 'done':
                signal_received = True
                break
        except zmq.ZMQError as err:
            print('Error while receiving: ', err)
        time.sleep(1)
    audio_handler.stop()
    socket.close()
    done.close()

if __name__ == '__main__':
    main()


import requests
from bs4 import BeautifulSoup
from time import sleep

movie_links_id = []

for z in range(10):
    print(z)

    url_1 = f"https://www.kinoafisha.info/rating/movies/?page={z}"
    sleep(5)
    r_1 = requests.get(url_1, timeout=5)
    soup_1 = BeautifulSoup(r_1.text, 'lxml')

    films = soup_1.find_all('div', class_='movieItem')
    for film in films:
        link = film.find('a', 'movieItem_title').get('href')
        link = ''.join([i for i in link if i.isdigit()])
        movie_links_id.append(link)


class Account:
    def __init__(self, name, balance):
        self._name = name
        self.__balance = balance
        self.show_balance()

    def deposit(self, amount):
        if amount > 0:
            self.__balance += amount
            print("Aomunt deposited")
            self.show_balance()

    def withdraw(self, amount):
        if 0 < amount < self.__balance:
            self.__balance -= amount
            print("Amount Withdrawn")
        else:
            print("insufficient balance")
        self.show_balance()

    def show_balance(self):
        print("Balance is {}".format(self.__balance))

Anant = Account("Anant", 1000)

Anant.deposit(1000)
Anant.withdraw(200)
Anant.withdraw(5000)


fd = open('Day3.txt', 'r')

def priority(prod):
    offset = 27 - ord('A') if prod.isupper() else 1 - ord('a')
    return ord(prod) + offset

# Day3_1
# total = 0
# for line in fd:
#     line = line.strip()
#     mid = len(line)//2
#     sym_left, sym_right = set(line[:mid]), set(line[mid:])
#     total += priority((sym_left & sym_right).pop())
# print(total)

# Day3_2
total = 0
lines = fd.readlines()
i = 0
while i < len(lines):
    r1, r2, r3 = set(lines[i].strip()), set(lines[i+1].strip()), set(lines[i+2].strip())
    total += priority((r1 & r2 & r3).pop())
    i += 3
print(total)


import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F

import numpy as np
import os
from collections import deque

from src.game_env import *
from src.ppo import *
import src.utils as utils

is_cuda = torch.cuda.is_available()
# device = 'cuda' if is_cuda else 'cpu'
device = 'cpu'

class Net(nn.Module):
    def __init__(self, input_shape, n_actions):
        super(Net, self).__init__()

        self.conv = nn.Sequential(
            nn.Conv2d(input_shape[0], 32, kernel_size=8, stride=4),
            nn.ReLU(),
            nn.Conv2d(32, 64, kernel_size=4, stride=2),
            nn.ReLU(),
            nn.Conv2d(64, 64, kernel_size=3, stride=1),
            nn.ReLU()
        )

        conv_out_size = self._get_conv_out(input_shape)

        self.policy = nn.Sequential(
            nn.Linear(conv_out_size, 512),
            nn.ReLU(),
            nn.Linear(512, n_actions)
        )

        self.value = nn.Sequential(
            nn.Linear(conv_out_size, 512),
            nn.ReLU(),
            nn.Linear(512, 1)
        )

    def _get_conv_out(self, shape):
        o = self.conv(torch.zeros(1, *shape))
        return int(np.prod(o.size()))

    def forward(self, x):
        fx = x.float() / 255
        conv_out = self.conv(fx).view(fx.size()[0], -1)
        return self.policy(conv_out), self.value(conv_out)

class Agent:

    def __init__(self, obs_shape, action_space):

        self.policy = Net(obs_shape, action_space).to(device)

    def choose_action(self, logits, noise_action=True):
        # take an logits ---> action

        u = torch.rand(logits.size()).to(device)
        if noise_action:
            _, action = torch.max( logits.detach() - (-u.log()).log(), 1)
        else:
            logits = F.softmax(logits, dim=-1)
            action = np.argmax(logits.cpu().detach().numpy(), axis=-1)[0]

        return action

    def train(self, game_id,
                    obs_size=84,
                    mini_batch=4,
                    num_steps=128,
                    num_workers=16,
                    mini_epochs=3,
                    gamma_=0.99,
                    tau_=0.95,
                    initial_lr=1e-4,
                    constant_lr=False,
                    max_update_times=10000,
                    early_stop=False,
                    save_model_steps=500):

        '''
        Agent training process!
        :param game_id:
        :param obs_size:
        :param mini_batch:
        :param num_steps:
        :param num_workers:
        :param mini_epochs:
        :param gamma_:
        :param lambda_:
        :param learning_rate:
        :param max_update_times:
        :param early_stop:
        :param save_model_steps:
        :return:
        '''
        batch_size = num_workers * num_steps

        mini_batch_size = batch_size // mini_batch
        assert (batch_size % mini_batch == 0)

        workers = [Worker(game_id, obs_size) for i in range(num_workers)]

        state = np.zeros((num_workers, obs_size, obs_size, 4), dtype=np.uint8)

        reward_queue = deque(maxlen=100)
        length_queue = deque(maxlen=100)

        for worker in workers:
            worker.child.send(('reset', None))

        for i, worker in enumerate(workers):
            state[i] = worker.child.recv()

        state = state.transpose(0, 3, 1, 2) # channel first for pytorch

        optimizer = optim.Adam(self.policy.parameters(), lr=initial_lr)

        current_update_times = 0
        while current_update_times < max_update_times and not early_stop:

            # ------------------------------------
            # interact with env and generate data
            log_probs = []
            values = []
            rewards = []
            actions = []
            states = []
            masks = []

            for _ in range(num_steps):

                state = torch.FloatTensor(state).to(device)
                logits, value = self.policy(state)
                # +++++
                values.append(value)

                action_this_step = self.choose_action(logits)
                action_this_step = action_this_step.cpu().detach().numpy()
                # +++++
                actions.append(torch.from_numpy(np.asarray(action_this_step)).unsqueeze(1).to(device))

                prob = F.softmax(logits, dim=-1)
                log_prob = F.log_softmax(logits, dim=-1)
                # +++++
                log_probs.append(log_prob)

                # Interact with environments
                for i, worker in enumerate(workers):
                    worker.child.send(('step', action_this_step[i]))
                next_state = []
                reward = []
                done = []

                for w, worker in enumerate(workers):
                    next_state_, reward_, done_, info = worker.child.recv()
                    next_state_ = next_state_.transpose(2, 0, 1)
                    next_state.append(next_state_[np.newaxis, ...])
                    reward.append(reward_)
                    done.append(done_)

                    if info:
                        reward_queue.append(info['reward'])
                        length_queue.append(info['length'])

                next_state = np.concatenate(next_state, axis=0)
                reward = np.asarray(reward)
                done = np.asarray(done)

                # +++++
                rewards.append(torch.FloatTensor(reward).unsqueeze(1).to(device))  # 2D list
                # +++++
                masks.append(torch.FloatTensor(1 - done).unsqueeze(1).to(device))  # 2D list
                # +++++
                states.append(state)

                state = next_state

            current_update_times += 1
            # ------------------------------------

            #  Update parameters
            #  Change numpy to pytorch tensor and reshape
            next_state = torch.FloatTensor(next_state).to(device)
            _, next_value = self.policy(next_state)

            returns = compute_gae(next_value, rewards, masks, values, gamma=gamma_, tau=tau_)
            returns = torch.cat(returns).detach()

            log_probs = torch.cat(log_probs).detach()
            values = torch.cat(values).detach()

            states = torch.cat(states)
            actions = torch.cat(actions)
            advantages = returns - values  # target_reward - predict_reward

            clip_p = 0.2 * (1 - current_update_times / max_update_times)
            ppo_update(self.policy, optimizer, mini_epochs, mini_batch_size,
                       states, actions, log_probs, returns, advantages, clip_param=clip_p)
            # ------------------------------------

            # save model and print information
            if current_update_times % save_model_steps == 0:
                self.save_model(current_update_times)

            if len(length_queue) != 0:
                print("Update step: [{}/{}] \t mean reward: {:3f} \t length: {}".
                      format(current_update_times, max_update_times,
                             sum(reward_queue)/len(reward_queue),
                             sum(length_queue) / len(length_queue)))
            # ------------------------------------

            if not constant_lr:
                utils.adjust_learning_rate(optimizer, initial_lr, max_update_times, current_update_times)

        # Close each worker when training end.
        for w in workers:
            w.child.send(("close", None))

    # Play game with model
    def play(self, model_path, game, visual=True, save_video=True):

        self.policy.load_state_dict(torch.load(model_path, map_location='cpu'))

        state = game.reset()
        done = False

        total_reward = 0.0

        while not done:

            if visual:
                game.env.render()
            state = torch.FloatTensor(state.transpose(2, 0, 1)).unsqueeze(0).to(device)
            logits, _ = self.policy(state)

            action = self.choose_action(logits, noise_action=False)

            next_state, reward, done, info = game.step(action, save_video)

            state = next_state
            total_reward += reward

            if done:
                game.env.reset()
                game.env.close()

        return total_reward, info['length']

    def save_model(self, update_step):
        filename = 'model_{}.dat'.format(update_step)
        torch.save(self.policy.state_dict(), os.path.join('checkpoint', filename))

# Unit test
if __name__ == '__main__':
    input_shape = (4, 84, 84)
    action_shape = 12
    model = Net(input_shape, action_shape)


# If input is not valid letter, show message that prompts user to put in a valid letter

import random

with open('words.txt') as words:
    lines = words.readlines()
    lines = [line.lower().replace('\n', '') for line in lines]
    easy_words = [line for line in lines if 4 <= len(line) <= 6]
    normal_words = [line for line in lines if 6 <= len(line) <= 8]
    hard_words = [line for line in lines if 8 <= len(line)]

difficulty = input('Select your difficulty level by typing "easy" "normal" or "hard". ').lower()

def set_game_level():
    if difficulty == 'easy':
        word_choice = random.choice(easy_words)
    elif difficulty == 'normal':
        word_choice = random.choice(normal_words)
    elif difficulty == 'hard':
        word_choice = random.choice(hard_words)

    return word_choice

mystery_word = set_game_level()

guess_count = 0

letter_guesses = []

def display_word(letter, guesses):
    if letter in guesses:
        return letter
    else:
        return "_"

def print_word(word, guesses):
    output_letters = [display_word(letter, letter_guesses) for letter in mystery_word]
    return(' '.join(output_letters))

first_display = print_word(mystery_word, letter_guesses)

print(' ')
print(f'Your word has {len(mystery_word)} letters:  ', first_display)

user_input = input('Guess a letter: ').lower()

while user_input.lower() != 'quit':
    user_input = user_input.lower()

    if len(user_input) >= 2:
        print(' ')
        user_input = input('Only guess one letter at a time. Try again. ')

    elif " " in user_input:
        print(' ')
        user_input = input('That is not a valid guess. Try again. ')

    elif user_input in letter_guesses:
        print(' ')
        user_input = input('You\'ve already guessed that letter! Try again. ')

    elif user_input not in letter_guesses and user_input in mystery_word:
        letter_guesses.append(user_input)
        guesses_display = print_word(mystery_word, letter_guesses)
        print(' ')
        print(guesses_display)

        if "_" not in guesses_display:
            print('You won!')
            user_input = input('Do you want to play again? Type yes or quit. ')
            if user_input.lower() == 'yes':
                difficulty = input('Select your difficulty level by typing "easy" "normal" or "hard". ').lower()

                mystery_word = set_game_level()
                guess_count = 0
                letter_guesses = []
                first_display = print_word(mystery_word, letter_guesses)
                print(' ')
                print(f'Your word has {len(mystery_word)} letters:  ', first_display)

                user_input = input('Guess a letter: ')

        else:
            user_input = input('Correct! Make your next guess. ')

    elif user_input not in letter_guesses and user_input not in mystery_word:
        guess_count += 1
        print(' ')

        if guess_count == 8:
            print('You are out of guesses!')
            print(f'The mystery word was {mystery_word}.')
            user_input = input('Do you want to play again? Type yes or quit. ')
            if user_input.lower() == 'yes':
                difficulty = input('Select your difficulty level by typing "easy" "normal" or "hard". ').lower()

                mystery_word = set_game_level()
                guess_count = 0
                letter_guesses = []
                first_display = print_word(mystery_word, letter_guesses)
                print(' ')
                print(f'Your word has {len(mystery_word)} letters:  ', first_display)

                user_input = input('Guess a letter: ')

        else:
            print(f'Incorrect! You\'ve used {guess_count} of 8 incorrect guesses.')
            letter_guesses.append(user_input)
            user_input = input('Try again. ')


#!/usr/bin/env python2
# -*- encoding:utf-8 -*-

"""
Polyshaper svg paths extraction tests

NOTE: to run this test standalone you must add ../plugin to the PYTHONPATH shell
variable tro to sys.path as well as the global inkscape plugin directory. If run
through testAll.py, there is no need to add directories (they are inserted by
that script)
"""

import unittest
from inkex import etree # pylint: disable=import-error
import simplepath # pylint: disable=import-error
from polyshaper.pathsextraction import FlattenBezier, PathsExtractor # pylint: disable=import-error,no-name-in-module
from polyshaper.errors import UnrecognizedSVGElement # pylint: disable=import-error,no-name-in-module

class FlattenBezierTest(unittest.TestCase):
    """ Tests for the class discretizing beziers and arcs in SVG paths

    The class uses the inkscape library, so we have one test to check we get the expected result
    """

    def test_flatten_complex_path(self):
        """ Tests flattening of a complex svg path
        """

        original = ("M 31.75,246.35119 Q -103.17337,325.09171 -73.319959,35.128951 "
                    "30.994047,99.696429 64.916958,144.277 47.499406,119.14669 142.11905,128.42262 "
                    "c 125.15382,72.71094 28.37304,48.92858 30.23809,68.03571 "
                    "-127.361415,-31.73781 -47.32064,32.81825 -69.54762,49.89286 "
                    "-66.090829,61.27158 28.37765,33.34354 80.88691,28.72618 A 51.016859,153.05058 "
                    "0 1 0 100,100")

        flattened = ("M31.75 246.35119L-25.4131361875 262.677495062L-61.97917475 232.91589025"
                     "L-77.9481156875 157.066375563L-73.319959 35.128951L64.916958 144.277"
                     "L75.508705 127.74825L142.11905 128.42262L199.380746719 167.041187344"
                     "L203.47138375 182.54190375L172.35714 196.45833L110.885396484 188.46358375"
                     "L98.157916875 203.1001025L105.143143828 226.88895L102.80952 246.35119"
                     "L80.1819165156 277.337919687L98.777941625 285.4226325L183.69643 275.07737"
                     "L188.045578834 122.576600131L171.030193349 61.983637335L146.195362269 "
                     "35.0337282295L120.196244292 48.9488233654L100.0 100.0")

        flatten_bezier = FlattenBezier(10.0)

        expected = simplepath.parsePath(flattened)
        result = flatten_bezier(original)

        self.assertEqual(len(result), len(expected))
        for (res, expect) in zip(result, expected):
            self.assertEqual(len(res), 2)
            self.assertEqual(len(expect), 2)

            self.assertEqual(res[0], expect[0])

            self.assertEqual(len(res[1]), len(expect[1]))
            for (resv, expectv) in zip(res[1], expect[1]):
                self.assertAlmostEqual(resv, expectv)

    def test_failure_empty_curve(self):
        """ Tests fix to a bug with empty input curves
        """

        flatten_bezier = FlattenBezier(10.0)
        self.assertEqual(flatten_bezier(""), [])

def to_mm(value):
    """ The function to convert to millimeters. For tests the identity function is enough
    """

    return value

class PathsExtractorTest(unittest.TestCase):
    """ Tests for the class extracting tool paths from svg elements
    """

    def test_get_elements(self):
        """ Tests that elements passed to the constructor are used
        """

        root = etree.Element("root")
        element1 = etree.SubElement(root, "path")
        element2 = etree.SubElement(root, "rect")

        extractor = PathsExtractor([element1, element2], to_mm, "wId")

        self.assertEqual(extractor.get_elements(), [element1, element2])

    def test_remove_working_area(self):
        """ Tests that the working area is not considered among svg elements to use
        """

        root = etree.Element("root")
        working_area = etree.SubElement(root, "svg", {"id": "wId"})
        other_element = etree.SubElement(root, "path")

        extractor = PathsExtractor([working_area, other_element], to_mm, "wId")

        self.assertEqual(extractor.get_elements(), [other_element])

    def test_throw_exception_for_unrecognized_svg_element(self): #pylint: disable=invalid-name
        """ Tests that an exception is thrown for unrecognized svg elements
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "invalidElement")

        extractor = PathsExtractor([element], to_mm, "wId")

        with self.assertRaises(UnrecognizedSVGElement) as context_manager:
            extractor.extract()

        exception = context_manager.exception
        self.assertEqual(exception.element, "invalidElement")

    def test_extract_svg_path_with_only_straight_lines(self): #pylint: disable=invalid-name
        """ Tests that svg paths are correctly extracted
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'d': "M 70,800 l 125,-300 l 60,490"})

        extractor = PathsExtractor([element], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 1)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])

    def test_extract_multiple_paths_when_lines_interrupted(self): #pylint: disable=invalid-name
        """ Tests that multiple paths are generated when svg path is not continuos
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'d': "M 70,800 l 125,-300 l 60,490 M 100,100 L 200,200"})

        extractor = PathsExtractor([element], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 2)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])
        self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0)])

    def test_extract_path_inside_groups(self): #pylint: disable=invalid-name
        """ Tests that a path inside a group is correctly extracted
        """

        root = etree.Element("root")
        group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g")
        etree.SubElement(group, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 70,800 l 125,-300 l 60,490"})

        extractor = PathsExtractor([group], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 1)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])

    def test_extract_paths_inside_groups(self): #pylint: disable=invalid-name
        """ Tests that paths inside groups are correctly extracted
        """

        root = etree.Element("root")
        group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g")
        etree.SubElement(group, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 70,800 l 125,-300 l 60,490"})
        etree.SubElement(group, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 60,700 l 127,-500 l 70,900"})

        extractor = PathsExtractor([group], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 2)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])
        self.assertEqual(extractor.paths()[1], [(60.0, 700.0), (187.0, 200.0), (257.0, 1100.0)])

    def test_extract_paths_inside_groups_and_outside(self): #pylint: disable=invalid-name
        """ Tests that a mix of paths inside groups and outside are correctly extracted
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'d': "M 70,800 l 125,-300 l 60,390"})
        group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g")
        etree.SubElement(group, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 70,800 l 125,-300 l 60,490"})
        etree.SubElement(group, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 60,700 l 127,-500 l 70,900"})

        extractor = PathsExtractor([element, group], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 3)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 890.0)])
        self.assertEqual(extractor.paths()[1], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])
        self.assertEqual(extractor.paths()[2], [(60.0, 700.0), (187.0, 200.0), (257.0, 1100.0)])

    def test_extract_paths_inside_group_keeping_transform(self): #pylint: disable=invalid-name
        """ Tests that extracted path from a group take the transformation into account
        """

        root = etree.Element("root")
        group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g",
                                 {'transform': "matrix(1,2,3,4,5,6)"})
        etree.SubElement(group, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 10,20 L 50,30 L 20,70"})

        extractor = PathsExtractor([group], to_mm, "wId")
        extractor.extract()

        self.assertEqual(extractor.paths()[0], [(75.0, 106.0), (145.0, 226.0), (235.0, 326.0)])

    def test_extract_paths_keeping_transform(self): #pylint: disable=invalid-name
        """ Tests that extracted path from a group take the transformation into account
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'transform': "matrix(1,2,3,4,5,6)",
                                    'd': "M 10,20 L 50,30 L 20,70"})

        extractor = PathsExtractor([element], to_mm, "wId")
        extractor.extract()

        self.assertEqual(extractor.paths()[0], [(75.0, 106.0), (145.0, 226.0), (235.0, 326.0)])

    def test_extract_paths_inside_group_keeping_nested_transform(self): #pylint: disable=invalid-name
        """ Tests that extracted path from elements with nested transformation works correctly
        """

        root = etree.Element("root")
        group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g",
                                 {'transform': "translate(10,20)"})
        subgroup = etree.SubElement(group, "{http://www.w3.org/2000/svg}g",
                                    {'transform': "matrix(1,2,3,4,5,6)"})
        etree.SubElement(subgroup, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 10,20 L 50,30 L 20,70"})

        extractor = PathsExtractor([group], to_mm, "wId")
        extractor.extract()

        self.assertEqual(extractor.paths()[0], [(85.0, 126.0), (155.0, 246.0), (245.0, 346.0)])

    def test_closed_paths(self):
        """ Tests that closed paths are extracted correctly by repeating the first point at the end
        """

        root = etree.Element("root")
        group = etree.SubElement(root, "{http://www.w3.org/2000/svg}g")
        etree.SubElement(group, "{http://www.w3.org/2000/svg}path",
                         {'d': "M 70,800 l 125,-300 l 60,490 Z"})

        extractor = PathsExtractor([group], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 1)
        self.assertEqual(extractor.paths()[0],
                         [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0), (70.0, 800.0)])

    def test_closed_paths_with_multiple_paths(self): #pylint: disable=invalid-name
        """ Tests that closed paths are extracted correctly also when multiple subpaths are present
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'d': "M 70,800 l 125,-300 l 60,490 Z M 100,100 L 200,200"})

        extractor = PathsExtractor([element], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 2)
        self.assertEqual(extractor.paths()[0],
                         [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0), (70.0, 800.0)])
        self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0)])

    def test_flatten_path_if_requested(self):
        """ Tests that path is flattened if the constructor flatten parameter is not None
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'d': "dummy value to check"})

        class FlattenMock: #pylint: disable=missing-docstring,too-few-public-methods
            def __init__(self, test):
                self.test = test
            def __call__(self, curve): #pylint: disable=missing-docstring
                self.test.assertEqual(curve, "dummy value to check")

                return [('M', (70.0, 800.0)), ('L', (195.0, 500.0)), ('L', (255.0, 990.0))]

        extractor = PathsExtractor([element], to_mm, "wId", FlattenMock(self))
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 1)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0)])

    def test_use_parent_transform_for_single_path(self): #pylint: disable=invalid-name
        """ Tests that extracted path takes the parent transformation into account
        """

        root = etree.Element("root", {'transform': "translate(10,20)"})
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'transform': "matrix(1,2,3,4,5,6)",
                                    'd': "M 10,20 L 50,30 L 20,70"})

        extractor = PathsExtractor([element], to_mm, "wId")
        extractor.extract()

        self.assertEqual(extractor.paths()[0], [(85.0, 126.0), (155.0, 246.0), (245.0, 346.0)])

    def test_use_parent_transform_for_single_path_with_multiple_parents(self): #pylint: disable=invalid-name
        """ Tests that extracted path takes all the ancestors transformations into account
        """

        root = etree.Element("root", {'transform': "scale(10)"})
        parent = etree.SubElement(root, "{http://www.w3.org/2000/svg}g",
                                  {'transform': "translate(10,20)"})
        element = etree.SubElement(parent, "{http://www.w3.org/2000/svg}path",
                                   {'transform': "matrix(1,2,3,4,5,6)",
                                    'd': "M 10,20 L 50,30 L 20,70"})

        extractor = PathsExtractor([element], to_mm, "wId")
        extractor.extract()

        self.assertEqual(extractor.paths()[0],
                         [(850.0, 1260.0), (1550.0, 2460.0), (2450.0, 3460.0)])

    def test_use_parent_transform_for_multiple_path(self): #pylint: disable=invalid-name
        """ Tests that extracted path takes the parent transformation into account
        """

        root1 = etree.Element("root", {'transform': "translate(10,20)"})
        element1 = etree.SubElement(root1, "{http://www.w3.org/2000/svg}path",
                                    {'transform': "matrix(1,2,3,4,5,6)",
                                     'd': "M 10,20 L 50,30 L 20,70"})
        root2 = etree.Element("root", {'transform': "translate(5,10)"})
        element2 = etree.SubElement(root2, "{http://www.w3.org/2000/svg}path",
                                    {'transform': "matrix(1,2,3,4,5,6)",
                                     'd': "M 10,20 L 50,30 L 20,70"})

        extractor = PathsExtractor([element1, element2], to_mm, "wId")
        extractor.extract()

        self.assertEqual(extractor.paths()[0], [(85.0, 126.0), (155.0, 246.0), (245.0, 346.0)])
        self.assertEqual(extractor.paths()[1], [(80.0, 116.0), (150.0, 236.0), (240.0, 336.0)])

    def test_automatically_close_open_paths_does_nothing_if_paths_closed(self): #pylint: disable=invalid-name
        """ Tests that nothing changes if closing paths is requested but paths are closed
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'d': ("M 70,800 l 125,-300 l 60,490 Z "
                                          "M 100,100 L 200,200 L 100,100")})

        extractor = PathsExtractor([element], to_mm, "wId")
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 2)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0),
                                                (70.0, 800.0)])
        self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0), (100.0, 100.0)])

    def test_automatically_close_open_paths(self): #pylint: disable=invalid-name
        """ Tests that open paths are automatically closed if requested
        """

        root = etree.Element("root")
        element = etree.SubElement(root, "{http://www.w3.org/2000/svg}path",
                                   {'d': "M 70,800 l 125,-300 l 60,490 M 100,100 L 200,200"})

        extractor = PathsExtractor([element], to_mm, "wId", auto_close_path=True)
        extractor.extract()

        self.assertEqual(len(extractor.paths()), 2)
        self.assertEqual(extractor.paths()[0], [(70.0, 800.0), (195.0, 500.0), (255.0, 990.0),
                                                (70.0, 800.0)])
        self.assertEqual(extractor.paths()[1], [(100.0, 100.0), (200.0, 200.0), (100.0, 100.0)])


import logging
import numpy as np

from ComperfModelling import predict_from_model

def run_kernel_density_estimation(values, normalised=False, normalisation_statistic=0):

	values = values.reshape(-1,1)

	# What should bandwidth be? Add as part of experiment configuration?
	bandwidth = 100000
	evaluation_gap = 50000

	if normalised:
		bandwidth = float(bandwidth)/normalisation_statistic
		evaluation_gap = float(evaluation_gap)/normalisation_statistic

	kde = KernelDensity(kernel='gaussian', bandwidth=bandwidth).fit(values)

	return kde, bandwidth, evaluation_gap

def compute_sample_weights_from_kde(kde, values, normalised=False, normalisation_statistic=0.0):

	# It is very expensive to evaluate every single example, so bin the examples into bins of 100k cycles, and apply the same weight to all examples within a given bin

	range_max = np.amax(values)
	range_min = np.amin(values)

	# Making the bin size equal to the bandwidth of the KDE
	bin_size = 100000
	if normalised:
		bin_size = bin_size/normalisation_statistic

	bins = int((float(range_max) - range_min)/float(bin_size))

	bin_positions = np.linspace(range_min,range_max,bins) + (bin_size/2.0) # i.e. evaluate the middle of each bin

	density_estimations = np.exp(kde.score_samples(bin_positions.reshape(-1,1)))

	# clip density estimations for bins that very likely don't contain any examples, as otherwise sample weights explode
	density_estimations[density_estimations < 0.1] = np.nan
	sample_weights = np.reciprocal(density_estimations)
	sample_weights[np.isnan(sample_weights)] = 10

	# now, apply the correct weight to each original estimation_position
	estimation_position_corresponding_bins = ((values - range_min) / bin_size).astype(np.int32)
	estimation_position_corresponding_bins[estimation_position_corresponding_bins >= bins] = bins-1

	per_example_sample_weights = sample_weights[estimation_position_corresponding_bins]

	logging.debug("Clipped %d examples to have a maximum sample weight of 10", len(per_example_sample_weights[per_example_sample_weights == 10]))

	return per_example_sample_weights

def kde_clustering_get_indexes(values, normalised=False, normalisation_statistic=0):

	counts = values.reshape(-1,1)

	kde, bandwidth, evaluation_gap = run_kernel_density_estimation(counts, normalised, normalisation_statistic)

	num_divisions = int(np.amax(counts)-np.amin(counts))/evaluation_gap
	if num_divisions < 20:
		# i.e. if we evaluate the density at less than this number of divisions, then we can't possibly cluster...
		return None, [], []

	estimation_positions = np.linspace(np.amin(counts),np.amax(counts),num_divisions)
	estimation_positions = estimation_positions.reshape(-1,1)

	estimations = np.exp(kde.score_samples(estimation_positions))

	# form a minimum by starting at 1.0 then descending to 0.0, then continuing with the histogram
	estimations = np.insert(estimations,0,0.0,axis=0)
	estimation_positions = np.insert(estimation_positions,0,np.amin(counts)-evaluation_gap,axis=0)
	estimations = np.insert(estimations,0,1.0,axis=0)
	estimation_positions = np.insert(estimation_positions,0,np.amin(counts)-(2*evaluation_gap),axis=0)

	mi, ma = argrelextrema(estimations, np.less_equal)[0], argrelextrema(estimations, np.greater)[0]

	bounds = []

	populated_cluster_id = 0

	cluster_indexes = []
	cluster_bounds = []

	for idx in np.arange(1,len(mi)):

		lower_bound = estimation_positions[mi[idx-1]]

		if idx == (len(mi)-1):
			upper_bound = np.amax(counts)+1
		else:
			upper_bound = estimation_positions[mi[idx]]

		clustered_task_indexes = np.where(np.logical_and(counts >= lower_bound, counts < upper_bound))[0]

		if(len(clustered_task_indexes) > 0):
			cluster_indexes.append(clustered_task_indexes)
			cluster_bounds.append([lower_bound,upper_bound])

	return kde, cluster_indexes, cluster_bounds

"""
	This function aims to identify significant differences between the responses, and saving the examples to later investigate as a comparative study
	A comparison may be between individual tasks or two task sets, where we necessarily average the latter
"""
def get_comparisons(responses):

	# should also save the cluster details so we can later use them to repeat the equivalent analysis on separate testing sets

	individual_comparisons = []
	cluster_comparisons = []
	cluster_comparison_bounds = []

	central_cluster_indexes = []
	central_cluster_bounds = []
	individual_comparisons = []
	cluster_comparisons = []
	kde = None

	# return a list of pairs, where each element is a list of task indexes to compare against eachother
	# return two types of comparisons: closest-to-mean task, tasks within 1*std-deviation of mean

	kde, clustered_task_indexes, cluster_bounds = kde_clustering_get_indexes(responses)
	if kde is None:
		log.info("Could not cluster instances as there is no significant variation.")
		return central_cluster_indexes, central_cluster_bounds, individual_comparisons, cluster_comparisons, kde

	# filter to only the significant clusters?
	large_clusters = []
	minimum_cluster_size = max([int(0.01*len(responses)),10])
	for cluster_idx, cluster_indexes in enumerate(clustered_task_indexes):
		if len(cluster_indexes) >= minimum_cluster_size:
			large_clusters.append(cluster_idx)

	logging.info("Found %d significant clusters (minimum cluster size was: %d)", len(large_clusters), minimum_cluster_size)

	clustered_task_indexes = [cluster_indexes for cluster_idx, cluster_indexes in enumerate(clustered_task_indexes) if cluster_idx in large_clusters]
	cluster_bounds = [cluster_indexes for cluster_idx, cluster_indexes in enumerate(cluster_bounds) if cluster_idx in large_clusters]

	if len(clustered_task_indexes) < 2:
		logging.info("There were fewer than two clusters, so cannot do a comparative analysis")
		return central_cluster_indexes, central_cluster_bounds, individual_comparisons, cluster_comparisons, kde

	fast_cluster = clustered_task_indexes[0]
	slow_clusters = clustered_task_indexes[1:]

	fast_durations = responses[fast_cluster]
	mean_fast_task_idx = (np.abs(fast_durations - np.mean(fast_durations))).argmin()
	mean_fast_task_idx_original_set = fast_cluster[mean_fast_task_idx]

	constraint_coefficient = 0.5
	fast_lower_central_bound = np.mean(fast_durations) - constraint_coefficient * np.std(fast_durations)
	fast_upper_central_bound = np.mean(fast_durations) + constraint_coefficient * np.std(fast_durations)
	central_fast_task_idxs = np.where(np.logical_and(fast_durations>=fast_lower_central_bound, fast_durations<=fast_upper_central_bound))[0]

	fast_durations_within_constraint = fast_durations[central_fast_task_idxs].flatten()
	distances_from_mean = np.absolute(fast_durations_within_constraint - np.mean(fast_durations))
	distances_sorted_indexes = np.argsort(distances_from_mean)

	# now filter the central indexes to only those that are within the bounds and are the closest to mean
	central_fast_task_idxs = central_fast_task_idxs[distances_sorted_indexes[:50]]

	# update the bounds
	fast_lower_central_bound = np.amin(fast_durations[central_fast_task_idxs])
	fast_upper_central_bound = np.amax(fast_durations[central_fast_task_idxs])

	central_fast_task_idxs_original_set = fast_cluster[central_fast_task_idxs]

	central_cluster_bounds = []
	central_cluster_bounds.append([fast_lower_central_bound, fast_upper_central_bound])

	central_cluster_indexes = []
	central_cluster_indexes.append(central_fast_task_idxs_original_set)

	for slow_cluster_idx, slow_cluster in enumerate(slow_clusters):

		slow_durations = responses[slow_cluster]
		mean_slow_task_idx = (np.abs(slow_durations - np.mean(slow_durations))).argmin()
		mean_slow_task_idx_original_set = slow_cluster[mean_slow_task_idx]
		lower_central_bound = np.mean(slow_durations) - constraint_coefficient * np.std(slow_durations)
		upper_central_bound = np.mean(slow_durations) + constraint_coefficient * np.std(slow_durations)
		central_slow_task_idxs = np.where(np.logical_and(slow_durations>=lower_central_bound, slow_durations<=upper_central_bound))[0]
		central_slow_task_idxs_original_set = slow_cluster[central_slow_task_idxs]

		slow_durations_within_constraint = slow_durations[central_slow_task_idxs].flatten()
		slow_distances_from_mean = np.absolute(slow_durations_within_constraint - np.mean(slow_durations))
		slow_distances_sorted_indexes = np.argsort(slow_distances_from_mean)

		# now filter the central indexes to only those that are within the bounds and are the closest to mean
		central_slow_task_idxs = central_slow_task_idxs[slow_distances_sorted_indexes[:50]]
		central_slow_task_idxs_original_set = slow_cluster[central_slow_task_idxs]

		# update the bounds
		lower_central_bound = np.amin(slow_durations[central_slow_task_idxs])
		upper_central_bound = np.amax(slow_durations[central_slow_task_idxs])

		individual_comparisons.append([mean_fast_task_idx_original_set, mean_slow_task_idx_original_set])
		central_cluster_bounds.append([lower_central_bound, upper_central_bound])
		central_cluster_indexes.append(central_slow_task_idxs_original_set)
		cluster_comparisons.append([0,slow_cluster_idx+1])

	return central_cluster_indexes, central_cluster_bounds, individual_comparisons, cluster_comparisons, kde

def run_pca_transformation(taskset, pca):

	taskset = list(taskset)
	taskset[1] = np.array([sum((taskset[1][i]*np.array(pca.components_[:])).transpose()) for i in range(len(taskset[1]))])
	taskset = tuple(taskset)

def run_sample_weighting_and_pca_transformation(
		training_taskset,
		validation_taskset,
		testing_taskset,
		should_standardise,
		pca_transform,
		training_event_stats):

	training_responses = training_taskset[2]
	validation_responses = validation_taskset[2]
	testing_responses = testing_taskset[2]

	kde_params = {}
	kde_params["normalised"] = should_standardise
	kde_params["normalisation_statistic"] = training_event_stats[-1][1]

	kde_params["values"] = training_responses
	kde, _, _ = run_kernel_density_estimation(**kde_params)

	kde_params["kde"] = kde
	training_sample_weights = compute_sample_weights_from_kde(**kde_params)

	kde_params["values"] = validation_responses
	validation_sample_weights = compute_sample_weights_from_kde(**kde_params)

	kde_params["values"] = testing_responses
	testing_sample_weights = compute_sample_weights_from_kde(**kde_params)

	num_input_events = len(training_taskset[1][0])

	if pca_transform == True:
		from sklearn.decomposition import PCA
		pca = PCA(n_components=num_input_events)
		pca.fit_transform(training_taskset[1])
	else:
		pca = pca_struct()
		pca.components_ = np.identity(num_input_events)

	run_pca_transformation(training_taskset, pca)
	run_pca_transformation(validation_taskset, pca)
	run_pca_transformation(testing_taskset, pca)

	return training_taskset, validation_taskset, testing_taskset, training_sample_weights, validation_sample_weights, testing_sample_weights, pca

def calculate_mae(models, testing_taskset):

	testing_examples = testing_taskset[1]
	testing_responses_true = testing_taskset[2][:,0]

	maes = []
	for model in models:
		testing_responses_pred = predict_from_model(model.model_file, testing_examples)
		mae = np.sum(np.absolute(testing_responses_pred - testing_responses_true)) / float(len(testing_responses_true))
		maes.append(mae)

	mean_mae = np.mean(maes)
	return mean_mae

def get_analysis_tasksets_for_configuration(
		dataset,
		all_events,
		pca_component_struct,
		taskset_kwargs
		):

	# Which configuration to get from the dataset is within the "benchmark_id" kwarg

	taskset, _, _ = dataset.get_taskset(**taskset_kwargs)
	run_pca_transformation(taskset, pca_component_struct)

	taskset_fs, _, _ = dataset.get_taskset(**taskset_kwargs) # No pca transformation here

	taskset_kwargs["input_events"] = all_events
	taskset_kwargs["standardised"] = False

	taskset_all_events_fs_destandardised, _, _ = dataset.get_taskset(**taskset_kwargs) # No pca transformation here

	return taskset, taskset_fs, taskset_all_events_fs_destandardised

def analyse_tasks_across_benchmarks(self,
		models,
		testing_tasksets, # array of length two, for two sets of tasks to compare to eachother
		scores,
		predicted_responses,
		std_devs,
		reference_responses,
		true_responses,
		reference_benchmark_idx, # i.e. what index of testing_tasksets is the reference
		reference_strategy="minimum_predicted_response",
		reference_indexes=[],
		just_get_values=False # If True, then deeplift won't be invoked at all, and all produced contributions/coefficients will be 0
		):

	target_benchmark_idx = [benchmark_idx for benchmark_idx in range(len(testing_tasksets)) if benchmark_idx != reference_benchmark_idx][0]

	if just_get_values:
		models = [0] # We don't need the models if we just want to get the value variations, but we do need an element in the list
	else:
		backend.set_floatx('float32')
		backend.set_learning_phase(False) # this is some keras funkery when using models with backend: https://github.com/fchollet/keras/issues/2310
		config = tf.ConfigProto()
		config.gpu_options.allow_growth = True
		backend.set_session(tf.Session(config=config))

	reference_example_idx = -1

	for model in models:

		if just_get_values:

			reference_example = np.zeros(len(testing_tasksets[reference_benchmark_idx][1][0])).tolist()
			reference_response = 0.0
			deeplift_results = np.array([[0.0 for event in reference_example] for task in testing_tasksets[target_benchmark_idx][1]])

			model_reference_predictions = np.array([0.0 for task in testing_tasksets[target_benchmark_idx][1]])
			model_target_predictions = np.array([0.0 for task in testing_tasksets[target_benchmark_idx][1]])

			if reference_responses == None:
				reference_responses = [reference_response]
			else:
				reference_responses.append(reference_response)

			reference_example_idx = 0
			reference_indexes.append(reference_example_idx)

		else:

			model_reference_predictions = predict_from_model(model.model_file, testing_tasksets[reference_benchmark_idx][1])
			model_target_predictions = predict_from_model(model.model_file, testing_tasksets[target_benchmark_idx][1])

			# If the comparison is between two groups of examples, which ones do we compare?
			# After this switch, we will have one reference example and one target example
			if reference_strategy == "minimum_predicted_response":
				reference_example_idx = np.argmin(model_reference_predictions)
				reference_example = testing_tasksets[reference_benchmark_idx][1][reference_example_idx]
				reference_response = model_reference_predictions[reference_example_idx]

			elif reference_strategy == "closest_to_mean_predicted_response":
				difference_from_mean = 9999999
				reference_example_idx = 0
				mean = np.mean(model_reference_predictions)
				for task_idx, duration in enumerate(model_reference_predictions):
					if abs(mean - duration) < difference_from_mean:
						difference_from_mean = abs(mean - duration)
						reference_example_idx = task_idx

				reference_example = testing_tasksets[reference_benchmark_idx][1][reference_example_idx]
				reference_response = model_reference_predictions[reference_example_idx]

			elif reference_strategy == "minimum_true_response":
				reference_example_idx = np.argmin(testing_tasksets[reference_benchmark_idx][2])
				reference_example = testing_tasksets[reference_benchmark_idx][1][reference_example_idx]
				reference_response = keras_model.predict(np.array([reference_example]))[:,0]

			elif reference_strategy == "minimum_example_inputs":
				reference_example = [np.amin(event_counts) for event_idx, event_counts in enumerate(testing_tasksets[reference_benchmark_idx][1].transpose())]
				reference_response = keras_model.predict(np.array([reference_example]))[:,0]

			elif reference_strategy == "zeros":
				reference_example = np.zeros(len(testing_tasksets[reference_benchmark_idx][1][0])).tolist()
				reference_response = keras_model.predict(np.array([reference_example]))[:,0]

			else:
				logging.error("Invalid reference strategy '%s'", reference_strategy)
				raise ValueError()

			if reference_responses == None:
				reference_responses = [reference_response]
			else:
				reference_responses.append(reference_response)

			del model_reference_predictions

			reference_indexes.append(reference_example_idx)

			target_example = testing_tasksets[target_benchmark_idx][1][0]

			# Now apply deeplift
			deeplift_results = run_deeplift_comparison(model.model_file,
				target_example,
				reference_example)

		# deeplift results is of format [task_idx][event_idx] = score
		# Aim to return a scores format of: [label_rank][input_event_idx] = list of scores, one per model

		if scores is None:
			# a list of scores for every task label and event
			scores = [[[] for event_idx in range(len(reference_example))] for i in list(range(int(np.amax(testing_tasksets[target_benchmark_idx][3]))))]
			# a list of predicted responses for every task label
			predicted_responses = [[] for i in list(range(int(np.amax(testing_tasksets[target_benchmark_idx][3]))))]
			# a list of input values for every task label and event
			std_devs = [[[] for event_idx in range(len(reference_example))] for i in list(range(int(np.amax(testing_tasksets[target_benchmark_idx][3]))))]

		add_true_responses = False
		if true_responses is None:
			add_true_responses = True
			true_responses = [[] for i in list(range(int(np.amax(testing_tasksets[target_benchmark_idx][3]))))]

		for task_idx, rank in enumerate(testing_tasksets[target_benchmark_idx][3]): # the true number of analysed tasks (we have scores for all)

			label_rank = int(rank)
			if label_rank >= len(scores):
				difference = (len(scores) - label_rank) + 1
				for i in range(difference + 1):
					scores.append([[] for event_idx in range(len(reference_example))])
					predicted_responses.append([])
					std_devs.append([[] for event_idx in range(len(reference_example))])
					if add_true_responses:
						true_responses.append([])

			for event_idx, score in enumerate(deeplift_results[task_idx]):
				scores[label_rank][event_idx].append(score)
				std_devs[label_rank][event_idx].append(testing_tasksets[target_benchmark_idx][1][task_idx][event_idx])

			predicted_responses[label_rank].append(model_target_predictions[task_idx])

			if add_true_responses:
				true_responses[label_rank].append(testing_tasksets[target_benchmark_idx][2][task_idx])

	return scores, predicted_responses, std_devs, reference_responses, true_responses

def get_delta_values(
		tasksets,
		reference_benchmark_idx,
		reference_index):

	target_benchmark_idx = [benchmark_idx for benchmark_idx in range(len(tasksets)) if benchmark_idx != reference_benchmark_idx][0]

	reference_example = tasksets[reference_benchmark_idx][1][reference_index]

	values = [[[] for event_idx in range(len(reference_example))] for i in list(range(int(np.amax(tasksets[target_benchmark_idx][3]))))]

	for task_idx, rank in enumerate(tasksets[target_benchmark_idx][3]):

		label_rank = int(rank)
		if label_rank >= len(values):
			difference = (len(values) - label_rank) + 1
			for i in range(difference + 1):
				values.append([[] for event_idx in range(len(reference_example))])

		for event_idx in range(len(tasksets[target_benchmark_idx][1][task_idx])):
			values[label_rank][event_idx].append(tasksets[target_benchmark_idx][1][task_idx][event_idx] - reference_example[event_idx])

	return values

def performance_comparison(
		reference_taskset,
		reference_taskset_fs,
		reference_taskset_all_events_fs_destandardised,
		target_taskset,
		target_taskset_fs,
		target_taskset_all_events_fs_destandardised,
		models,
		training_event_stats,
		training_pca_matrix,
		linreg_coefficients_fs_des
		):

	# The performance comparison may operate as a 1-1 comparison, or a many-many comparison
	# For the latter case, we average across multiple individual comparisons (all-all)

	linreg_contributions_across_comparisons = []
	contributions_across_comparisons = []
	coefficients_across_comparisons = []
	event_variations_across_comparisons = []
	all_event_variations_across_comparisons = []
	pred_duration_variations_across_comparisons = []
	true_duration_variations_across_comparisons = []

	logging.debug("For the performance comparison, there are %d reference examples and %d target examples.", len(reference_taskset[1]))

	reference_instance_indexes_to_use_for_comparison = list(range(len(reference_taskset[1])))[:]

	# At the moment, all reference instances are compared to all target instances
	"""
	num_reference_instances_to_use = 4 # TODO parameterise this arbitrary number!
	if len(reference_instance_indexes_to_use_for_comparison) > num_reference_instances_to_use:
		selection = np.random.choice(np.arange(len(reference_instance_indexes_to_use_for_comparison)),num_reference_instances_to_use,replace=False)
		reference_instance_indexes_to_use_for_comparison = np.array(reference_instance_indexes_to_use_for_comparison)[selection].tolist()
	"""

	# For each reference example, compare it with the corresponding target example
	for reference_instance_idx in reference_instance_indexes_to_use_for_comparison:

		# Build a pseudo taskset containing the one reference example that we are comparing
		reference_taskset_temp = (reference_taskset[0], reference_taskset[1][[reference_instance_idx]],reference_taskset[2][[reference_instance_idx]],reference_taskset[3][[reference_instance_idx]])

		# Build a pseudo taskset containing the one target example that we are comparing
		target_instance_idx = np.where(target_taskset[3] == reference_taskset_temp[3][0])[0] # i.e. find the matching label
		target_taskset_temp = (target_taskset[0], target_taskset[1][[target_instance_idx]],target_taskset[2][[target_instance_idx]],target_taskset[3][[target_instance_idx]])

		# reference and target tasksets in FS
		reference_instance_idx_fs = np.where(reference_taskset_fs[3] == reference_taskset_temp[3][0])[0]
		target_instance_idx_fs = np.where(target_taskset_fs[3] == reference_taskset_temp[3][0])[0]
		reference_instance_idx_fs_all_events = np.where(reference_taskset_all_events_fs_destandardised[3] == reference_taskset_temp[3][0])[0]
		target_instance_idx_fs_all_events = np.where(target_taskset_all_events_fs_destandardised[3] == reference_taskset_temp[3][0])[0]
		target_taskset_fs_temp = (target_taskset_fs[0], target_taskset_fs[1][[target_instance_idx_fs]],target_taskset_fs[2][[target_instance_idx_fs]],target_taskset_fs[3][[target_instance_idx_fs]])
		reference_taskset_fs_temp = (reference_taskset_fs[0], reference_taskset_fs[1][[reference_instance_idx_fs]],reference_taskset_fs[2][[reference_instance_idx_fs]],reference_taskset_fs[3][[reference_instance_idx_fs]])

		comparison_tasksets = [reference_taskset_temp,target_taskset_temp]
		comparison_tasksets_fs = [reference_taskset_fs_temp,target_taskset_fs_temp]

		reference_indexes = []
		task_scores = None
		task_std_devs = None
		predicted_durations = None
		true_durations = None
		reference_response = None

		# TODO this function (and thus the surrounding code) is in need of significant refactoring!
		scores, predicted_durations_tmp, task_std_devs_tmp, reference_responses_tmp, true_durations_tmp = analyse_tasks_across_benchmarks(models,
			comparison_tasksets,
			task_scores,
			predicted_durations,
			task_std_devs,
			reference_response,
			true_durations,
			reference_benchmark_idx=0,
			reference_strategy="closest_to_mean_predicted_response",
			reference_indexes=reference_indexes)

		event_variations_fs = get_delta_values(comparison_tasksets_fs,
			reference_benchmark_idx=0,
			reference_index=reference_indexes[0])

		reference_example = reference_taskset_temp[1][reference_indexes[0]]
		reference_duration_true = reference_taskset_temp[2][reference_indexes[0]]
		reference_duration_pred = reference_responses_tmp[0]

		reference_example_all_events = reference_taskset_all_events_fs_destandardised[1][reference_instance_idx_fs_all_events]
		target_example_all_events = target_taskset_all_events_fs_destandardised[1][target_instance_idx_fs_all_events]

		duration_variation_true = np.array([np.mean(the_durations) for task_idx, the_durations in enumerate(true_durations_tmp) if len(the_durations) > 0]) - reference_duration_true
		duration_variation_destandardised_true = (duration_variation_true * training_event_stats[-1][1])

		duration_variation_pred = np.array([np.mean(the_durations) for task_idx, the_durations in enumerate(predicted_durations_tmp) if len(the_durations) > 0]) - reference_duration_pred
		duration_variation_destandardised_pred = (duration_variation_pred * training_event_stats[-1][1])

		event_variations_fs = np.array([np.mean(delta_values,axis=1) for task_idx, delta_values in enumerate(event_variations_fs) if any(len(s) > 0 for s in delta_values)])
		event_variations_fs_destandardized = event_variations_fs * np.array(training_event_stats).transpose()[1].transpose()[:-1]
		event_variations = np.array([np.mean(the_inputs,axis=1) for task_idx, the_inputs in enumerate(task_std_devs_tmp) if any(len(s) > 0 for s in the_inputs)]) - reference_example

		all_event_variations = np.array([target_value - reference_value for reference_value,target_value in zip(reference_example_all_events,target_example_all_events)])

		scores_pc = np.array([np.mean(the_scores,axis=1) for task_idx, the_scores in enumerate(scores) if any(len(s) > 0 for s in the_scores)])
		coefficients_pc = np.divide(scores_pc, event_variations, out=np.zeros_like(scores_pc), where=event_variations!=0)
		coefficients_fs = np.matmul(coefficients_pc, training_pca_matrix.components_)
		coefficients_fs_destandardized = ((coefficients_fs * np.array(training_event_stats)[-1][1])) / np.array(training_event_stats).transpose()[1].transpose()[:-1]
		scores_fs = coefficients_fs * event_variations_fs
		scores_fs_destandardized = scores_fs * training_event_stats[-1][1]

		# Get the average over the compared examples (though there should only be one comparison here)
		average_scores = scores_fs_destandardized[0]
		average_coefficients = coefficients_fs_destandardized[0]
		average_values = event_variations_fs_destandardized[0]

		linreg_contributions = np.array(average_values) * linreg_coefficients_fs_des
		linreg_contributions_across_comparisons.append(linreg_contributions)

		if configuration_idx == reference_benchmark_id:
			# Record the actual reference values so we can later reconstruct absolute counts from deltas if we want
			average_values = np.matmul(reference_example, training_pca_matrix.components_) * np.array(training_event_stats).transpose()[1].transpose()[:-1] + np.array(training_event_stats).transpose()[0][:-1]
			predicted_durations_destandardised = reference_duration_pred * np.array(training_event_stats[-1][1]) + training_event_stats[-1][0]
			duration_variation_destandardised_pred = predicted_durations_destandardised
			durations_destandardised = reference_duration_true * np.array(training_event_stats[-1][1]) + training_event_stats[-1][0]
			duration_variation_destandardised_true = durations_destandardised

		all_event_variations_across_comparisons.append(all_event_variations)
		contributions_across_comparisons.append(average_scores)
		coefficients_across_comparisons.append(average_coefficients)
		event_variations_across_comparisons.append(average_values)
		pred_duration_variations_across_comparisons.append(np.mean(duration_variation_destandardised_pred))
		true_duration_variations_across_comparisons.append(np.mean(duration_variation_destandardised_true))

	# And I need to get value variations for *all* events
	average_all_event_variations = np.mean(np.array(all_event_variations_across_comparisons),axis=0)[0]

	# Now I have the computed data for all the comparisons between the reference set and target set
	average_linreg_contributions = np.mean(np.array(linreg_contributions_across_comparisons),axis=1)

	average_contributions = np.mean(np.array(contributions_across_comparisons),axis=0)
	average_coefficients = np.mean(np.array(coefficients_across_comparisons),axis=0)
	average_event_variations = np.mean(np.array(event_variations_across_comparisons),axis=0)
	average_predicted_duration_variations = np.mean(np.array(pred_duration_variations_across_comparisons),axis=0)
	average_true_duration_variations = np.mean(np.array(true_duration_variations_across_comparisons),axis=0)

	return average_contributions, average_linreg_contributions, average_coefficients, average_event_variations, average_all_event_variations, np.mean(average_predicted_duration_variations), np.mean(average_true_duration_variations)


import numpy as np
import torch
from collections import deque
from motion_pred.utils.config import Config
from models.motion_pred import *
import pickle
from datetime import datetime
import os
import time

index = 0

class HumanLatent:
    def __init__(self, date_time=None):
        self.device = torch.device('cuda:0')
        self.date_time = date_time
        self.history_len = 13
        self.human_fps = 16.5
        self.human_joints = np.array([4, 5, 7, 8, 10, 11, 27, 28])  # corresponds to [right knee, ankle, foot, big toe]

        self.previous_ball = deque(maxlen=100000000)
        self.previous_ball_raw = deque(maxlen=100000000)
        self.previous_human = deque(maxlen=100000000)
        self.previous_ball_time = deque(maxlen=100000000)
        self.previous_human_time = deque(maxlen=100000000)

        cfg = Config('human_kick', test=True)

        self.model = get_kick_vae_model(cfg)
        self.model_weight = 'vae_0500.p'
        model_cp = pickle.load(open(self.model_weight, "rb"))
        self.model.load_state_dict(model_cp['model_dict'])
        self.model.eval()
        self.model.to(self.device)

        self.human_latent = None
        self.index = 0

    def get_human_latent(self):
        return self.human_latent

    def set_ball_states(self, ball_pos, ball_time):
        if len(self.previous_ball) > 0 and ball_pos[0] > 6:
            print("if", ball_pos[0], ball_time)
            self.previous_ball.append(np.copy(self.previous_ball[-1]))
            self.previous_ball_time.append(ball_time)
        else:
            print("else", ball_pos[0], ball_time)
            self.previous_ball.append(np.copy(ball_pos))
            self.previous_ball_time.append(ball_time)

        # self.previous_ball_raw.append(np.copy(ball_pos))
        # self.previous_ball.append(np.copy(ball_pos))
        # self.previous_ball_time.append(ball_time)

    def set_human_poses(self, human_pose, human_time):
        self.previous_human.append(np.copy(human_pose))
        self.previous_human_time.append(human_time)

    def run_encoding_once(self):
        print(self.human_latent)
        if len(self.previous_human_time) == 0 or len(self.previous_ball_time) == 0:
            return

        if self.previous_ball[-1][0] < 1.5:
            self.human_latent = None
            return
        if self.human_latent is not None:
            return

        human_time = self.previous_human_time[-1]
        human_idx = 1
        ball_idx = 1
        ball_pos = np.zeros((self.history_len, 3))
        human_pose = np.zeros((self.history_len, 29, 3))

        for i in range(1, self.history_len+1):
            while ball_idx + 1 < len(self.previous_ball_time) and human_time < self.previous_ball_time[-ball_idx-1]:
                ball_idx += 1

            if self.previous_ball_time[-ball_idx] - self.previous_ball_time[-ball_idx-1] == 0:
                ball_pos[-i] = self.previous_ball[-ball_idx]
            else:
                curr_ball_pos = self.previous_ball[-ball_idx] * (human_time - self.previous_ball_time[-ball_idx-1]) + self.previous_ball[-ball_idx-1] * (self.previous_ball_time[-ball_idx] - human_time)
                ball_pos[-i] = np.copy(curr_ball_pos/(self.previous_ball_time[-ball_idx]-self.previous_ball_time[-ball_idx-1]))

            while human_idx + 1 < len(self.previous_human_time) and human_time < self.previous_human_time[-human_idx-1]:
                human_idx += 1
            if self.previous_human_time[-human_idx] - self.previous_human_time[-human_idx-1] == 0:
                human_pose[-i] = self.previous_human[-human_idx]
            else:
                curr_human_pos = self.previous_human[-human_idx] * (human_time - self.previous_human_time[-human_idx-1]) + self.previous_human[-human_idx-1] * (self.previous_human_time[-human_idx] - human_time)
                human_pose[-i] = np.copy(curr_human_pos/(self.previous_human_time[-human_idx]-self.previous_human_time[-human_idx-1]))

            if human_time < self.previous_ball_time[0] or human_time < self.previous_human_time[0]:
                ball_pos[-i] = 0
                human_pose[-i] = 0
                break

            human_time = human_time - 1 / self.human_fps

        ball_pos = ball_pos[:, None, :]
        human_pose = human_pose[:, self.human_joints, :]
        human_ball = np.concatenate([human_pose, ball_pos], axis=1)
        human_ball = (human_ball[1:, :] - human_ball[:-1, :]) / 0.1

        human_ball[:2] = 0  # same with training, the first two frames are zeros

        human_ball = human_ball.reshape(self.history_len-1, 1, -1)
        human_ball = torch.from_numpy(human_ball).to(self.device).float()

        h_x = self.model.encode_x(human_ball)
        self.human_latent = h_x[0].detach().cpu().numpy().tolist()

        print(self.human_latent)

    def run_encoding_forever(self):
        while True:
            print('encoding')
            self.run_encoding_once()

if __name__ == '__main__':
    hl = HumanLatent()
    for i in range(100):
        hl.set_ball_states(np.array([i, i, i]), 0.01*i)
        hl.set_human_poses(np.ones((29, 3))*i, 1*i)

    hl.run_encoding_once()


from django.http import Http404, HttpResponseRedirect
from django.shortcuts import render_to_response, get_object_or_404
from models import Meuble, CommandeParticulier, CommandeProfessionnel
from forms import MeubleQuantiteListForm, ParticulierContactForm, MeubleQuantiteFormSet, ProfessionnelContactForm
from django.template import RequestContext
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
from django.utils import simplejson
from decimal import Decimal
from django.core.urlresolvers import reverse

def particulier_devis(request):

    if request.method == 'POST':
        contact_form = ParticulierContactForm(request.POST)
        meublequantite_formset = MeubleQuantiteFormSet(request.POST)
        if contact_form.is_valid() and meublequantite_formset.is_valid():
            meubles = meublequantite_formset.save()
            commande = contact_form.save()
            for meuble in meubles:
                commande.meubles.add(meuble)
            commande.save()
            return render_to_response('moving/merci.html', {
                'commande': commande,
            }, context_instance=RequestContext(request))
    else:
        contact_form = ParticulierContactForm()
        meublequantite_formset = MeubleQuantiteListForm

    return render_to_response('moving/devis.html', {
        'contact_form': contact_form,
        'meublequantite_formset': meublequantite_formset
    }, context_instance=RequestContext(request))

def professionnel_devis(request):
    if request.method == 'POST':
        contact_form = ProfessionnelContactForm(request.POST)
#        meublequantite_formset = MeubleQuantiteFormSet(request.POST)
        if contact_form.is_valid():# and meublequantite_formset.is_valid():
#            meubles = meublequantite_formset.save()
            commande = contact_form.save()
#            for meuble in meubles:
#                commande.meubles.add(meuble)
            commande.save()
            return render_to_response('moving/merci.html', {
                'commande': commande,
            }, context_instance=RequestContext(request))
    else:
        contact_form = ProfessionnelContactForm()
#        meublequantite_formset = MeubleQuantiteListForm

    return render_to_response('moving/devis_professionnel.html', {
        'contact_form': contact_form,
#        'meublequantite_formset': meublequantite_formset
    }, context_instance=RequestContext(request))

def index(request):
    return render_to_response('moving/index.html', {
    }, context_instance=RequestContext(request))

@csrf_exempt
def calculate_volume(request):
    to_return = {
        'msg': u'No POST data sent.',
        'volume': None,
        }

    if request.method == "POST": # and is_ajax
        volume = Decimal(0)
        meublequantite_formset = MeubleQuantiteFormSet(request.POST)
        if meublequantite_formset.is_valid():
            dummy_commande = CommandeParticulier()
            for form in meublequantite_formset:
                volume = volume + Decimal(form.cleaned_data['quantite']) * form.cleaned_data['meuble'].volume
        to_return['volume'] = str(volume)
        to_return['msg'] = 'Success'

    data = simplejson.dumps(to_return)
    return HttpResponse(data, mimetype='application/json')


#!/usr/bin/env python3
import os
import pickle
import warnings

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from scipy.stats import norm
import ROOT
import uproot
import yaml

SPEED_OF_LIGHT = 2.99792458
SPLIT = True

centrality_colors = [ROOT.kOrange+7, ROOT.kAzure+4, ROOT.kTeal+4, ROOT.kBlack, ROOT.kBlack, ROOT.kBlack]

# avoid pandas warning
warnings.simplefilter(action='ignore', category=FutureWarning)
ROOT.gROOT.SetBatch()

##################################################################
# read configuration file
##################################################################
config = 'config.yaml'
with open(os.path.expandvars(config), 'r') as stream:
    try:
        params = yaml.full_load(stream)
    except yaml.YAMLError as exc:
        print(exc)

ANALYSIS_RESULTS_PATH = params['ANALYSIS_RESULTS_PATH']
CT_BINS_CENT = params['CT_BINS_CENT']
CENTRALITY_LIST = params['CENTRALITY_LIST']
RANDOM_STATE = params['RANDOM_STATE']
##################################################################

# split matter/antimatter
SPLIT_LIST = ['']
if SPLIT:
    SPLIT_LIST = ['antimatter', 'matter']

if not os.path.isdir("plots/cpt_and_lifetime"):
    os.mkdir("plots/cpt_and_lifetime")

ROOT.gStyle.SetOptStat(0)
ROOT.gStyle.SetOptFit(0)

eff_cut_dict = pickle.load(open("file_eff_cut_dict", "rb"))
presel_eff_file = uproot.open('PreselEff.root')
signal_extraction_file = ROOT.TFile.Open('SignalExtraction.root')
signal_extraction_keys = uproot.open('SignalExtraction.root').keys()
analysis_results_file = uproot.open(os.path.expandvars(ANALYSIS_RESULTS_PATH))

# get centrality selected histogram
cent_counts, cent_edges = analysis_results_file['Centrality_selected;1'].to_numpy()
cent_bin_centers = (cent_edges[:-1]+cent_edges[1:])/2

abs_correction_file = ROOT.TFile.Open('He3_abs.root')
eff_correction_file = ROOT.TFile.Open('EffAbsCorrection_1.root')
ratio_file = ROOT.TFile.Open('Ratio.root', 'recreate')

for i_cent_bins in range(len(CENTRALITY_LIST)):
    cent_bins = CENTRALITY_LIST[i_cent_bins]

    # get number of events
    cent_range_map = np.logical_and(cent_bin_centers > cent_bins[0], cent_bin_centers < cent_bins[1])
    counts_cent_range = cent_counts[cent_range_map]
    evts = np.sum(counts_cent_range)
    print(f'Number of events: {evts}')

    h_corrected_yields = [ROOT.TH1D(), ROOT.TH1D()]
    for i_split, split in enumerate(SPLIT_LIST):
        print(f'{i_split} -> {split}')
        # get preselection efficiency and abs correction histograms
        presel_eff_counts, presel_eff_edges = presel_eff_file[
            f'fPreselEff_vs_ct_{split}_{cent_bins[0]}_{cent_bins[1]};1'].to_numpy()
        presel_eff_bin_centers = (presel_eff_edges[1:]+presel_eff_edges[:-1])/2

        func_name = 'BGBW'
        if (cent_bins[1] == 90) or (cent_bins[0] == 0 and cent_bins[1] == 10):
            func_name = 'BlastWave'
        g_abs_correction = ROOT.TGraphAsymmErrors()
        g_abs_correction = abs_correction_file.Get(f"{cent_bins[0]}_{cent_bins[1]}/{func_name}/fEffCt_{split}_{cent_bins[0]}_{cent_bins[1]}_{func_name}")
        eff_abs_correction = ROOT.TH1D()
        eff_abs_correction = eff_correction_file.Get(f"{cent_bins[0]}_{cent_bins[1]}/{func_name}/fCorrection_{split}_{cent_bins[0]}_{cent_bins[1]}_{func_name}")

        # list of corrected yields
        ct_bins_tmp = [0]
        ct_bins_tmp += CT_BINS_CENT[i_cent_bins]
        if cent_bins[0]==30 or cent_bins[0]==50:
            ct_bins_tmp = [0, 2, 4, 7, 14, 35]
        if cent_bins[0]==10:
            ct_bins_tmp = [0, 2, 7, 14, 35]
        #if cent_bins[1] == 90:
        #    ct_bin_tmp = CT_BINS_CENT[i_cent_bins]s
        bins = np.array(ct_bins_tmp, dtype=float)
        # print(bins)
        h_corrected_yields[i_split] = ROOT.TH1D(

            f'fYields_{split}_{cent_bins[0]}_{cent_bins[1]}', f'{split}, {cent_bins[0]}-{cent_bins[1]}%', len(bins)-1, bins)

        for ct_bins in zip(CT_BINS_CENT[i_cent_bins][:-1], CT_BINS_CENT[i_cent_bins][1:]):

            bin = f'{split}_{cent_bins[0]}_{cent_bins[1]}_{ct_bins[0]}_{ct_bins[1]}'
            formatted_eff_cut = "{:.2f}".format(eff_cut_dict[bin])

            # look for plot with eff = eff_cut (or the nearest one)
            bkg_shape = 'pol1'
            eff_cut_increment = 0
            eff_cut_sign = -1
            while signal_extraction_keys.count(f'{bin}_{bkg_shape}/fInvMass_{formatted_eff_cut};1') == 0:
                if eff_cut_sign == -1:
                    eff_cut_increment += 0.01
                eff_cut_sign *= -1
                formatted_eff_cut = "{:.2f}".format(eff_cut_dict[bin]+eff_cut_increment*eff_cut_sign)

            # get signal
            h_raw_yield = signal_extraction_file.Get(f'{bin}_{bkg_shape}/fRawYields;1')
            eff_index = h_raw_yield.FindBin(float(formatted_eff_cut))
            raw_yield = h_raw_yield.GetBinContent(eff_index)
            raw_yield_error = h_raw_yield.GetBinError(eff_index)
            print(f'eff_cut = {formatted_eff_cut}, raw_yield = {raw_yield}+{raw_yield_error}')

            # apply corrections
            # 1. efficiency (presel x BDT)
            presel_eff_map = np.logical_and(
                presel_eff_bin_centers > ct_bins[0],
                presel_eff_bin_centers < ct_bins[1])
            presel_eff = presel_eff_counts[presel_eff_map]
            bdt_eff = float(formatted_eff_cut)
            print(f'bin: {presel_eff_map}, presel_eff: {presel_eff}')
            eff = presel_eff * eff_cut_dict[bin]
            # 2. absorption correction
            abs = g_abs_correction.GetPointY(CT_BINS_CENT[i_cent_bins].index(ct_bins[0]))
            print(f"absorption correction for point {CT_BINS_CENT[i_cent_bins].index(ct_bins[0])}: {abs}")
            # 3. efficiency correction
            eff_correct = eff_abs_correction.GetBinContent(eff_abs_correction.FindBin(ct_bins[0]))

            ct_bin_index = h_corrected_yields[i_split].FindBin(ct_bins[0]+0.5)
            h_corrected_yields[i_split].SetBinContent(ct_bin_index, raw_yield/eff[0]/abs/eff_correct)
            h_corrected_yields[i_split].SetBinError(ct_bin_index, raw_yield_error/eff[0]/abs/eff_correct)

        # set labels
        h_corrected_yields[i_split].GetXaxis().SetTitle("#it{c}t (cm)")
        h_corrected_yields[i_split].GetYaxis().SetTitle("d#it{N}/d(#it{c}t) (cm^{-1})")
        #h_corrected_yields[i_split].Scale(1, "width")
        for i_bin in range(len(bins))[2:]:
            bin_width = h_corrected_yields[i_split].GetBinWidth(i_bin)
            print(f"bin: {h_corrected_yields[i_split].GetBinLowEdge(i_bin)}; bin width: {bin_width}")
            bin_content = h_corrected_yields[i_split].GetBinContent(i_bin)
            bin_error = h_corrected_yields[i_split].GetBinError(i_bin)
            h_corrected_yields[i_split].SetBinContent(i_bin, bin_content/bin_width)
            h_corrected_yields[i_split].SetBinError(i_bin, bin_error/bin_width)
        h_corrected_yields[i_split].GetYaxis().SetRangeUser(1., 450.)
        h_corrected_yields[i_split].SetMarkerStyle(20)
        h_corrected_yields[i_split].SetMarkerSize(0.8)
        h_corrected_yields[i_split].Write()

        # fit with exponential pdf
        fit_function_expo = ROOT.TF1("expo", "expo", 2, 35)
        if cent_bins[0] == 30 or cent_bins[0]==50:
            fit_function_expo = ROOT.TF1("expo", "expo", 2, 14)
        elif cent_bins[1] == 90:
            fit_function_expo = ROOT.TF1("expo", "expo", 0, 35)
        res_expo = h_corrected_yields[i_split].Fit(fit_function_expo, "RMIS+")

        # compute and display lifetime
        tau = -1/fit_function_expo.GetParameter(1)*100/SPEED_OF_LIGHT # ps
        tau_error = fit_function_expo.GetParError(1)*100/SPEED_OF_LIGHT/fit_function_expo.GetParameter(1)/fit_function_expo.GetParameter(1) # ps
        tau_text = ROOT.TLatex(20, 100, '#tau = ' + "{:.0f}".format(tau) + '#pm' + "{:.0f}".format(tau_error) + ' ps')
        tau_text.SetTextSize(0.05)

        # display chi2
        formatted_chi2_lifetime = "{:.2f}".format(fit_function_expo.GetChisquare())
        chi2_lifetime_text = ROOT.TLatex(20, 70, "#chi^{2}/NDF = "+formatted_chi2_lifetime+"/"+str(fit_function_expo.GetNDF()))
        chi2_lifetime_text.SetTextSize(0.05)

        # compute yield
        integral = float()
        integral_error = float()
        integral = fit_function_expo.Integral(0, 1.e9)
        integral_error = fit_function_expo.IntegralError(0, 1.e9, res_expo.GetParams(), res_expo.GetCovarianceMatrix().GetMatrixArray())
        formatted_integral = "{:.10f}".format(integral/2./evts)
        formatted_integral_error = "{:.10f}".format(integral_error/evts/2.)
        print(f"N_ev = {evts}")
        integral_yield = ROOT.TLatex(20, 40, "1/#it{N_{ev}} #times BR #times d#it{N}/d#it{y} = "+formatted_integral+" #pm "+formatted_integral_error)
        integral_yield.SetTextSize(0.05)

        # draw on canvas
        canv = ROOT.TCanvas()
        ROOT.gStyle.SetOptStat(0)
        canv.SetTicks(1, 1)
        canv.SetName(h_corrected_yields[i_split].GetName())
        canv.SetTitle(h_corrected_yields[i_split].GetTitle())
        canv.cd()
        h_corrected_yields[i_split].Draw("")
        tau_text.Draw("same")
        chi2_lifetime_text.Draw("same")
        integral_yield.Draw("same")
        canv.SetLogy()
        canv.Write() # write to file
        canv.Print(f'plots/cpt_and_lifetime/{h_corrected_yields[i_split].GetName()}.pdf')

    # ratios
    ROOT.gStyle.SetOptStat(0)
    h_ratio = ROOT.TH1D(h_corrected_yields[0])
    h_ratio.SetName(f'fRatio_{cent_bins[0]}_{cent_bins[1]}')
    h_ratio.SetTitle(f'{cent_bins[0]}-{cent_bins[1]}%')
    h_ratio.Divide(h_corrected_yields[0], h_corrected_yields[1], 1, 1)
    h_ratio.SetLineColor(centrality_colors[i_cent_bins])
    h_ratio.SetMarkerColor(centrality_colors[i_cent_bins])
    h_ratio.GetYaxis().SetTitle("Ratio ^{3}_{#bar{#Lambda}}#bar{H} / ^{3}_{#Lambda}H")
    h_ratio.GetYaxis().SetRangeUser(0., 1.8)
    h_ratio.GetXaxis().SetRangeUser(0., 35.)
    h_ratio.SetMarkerStyle(20)
    h_ratio.SetMarkerSize(0.8)
    h_ratio.Fit("pol0","R","",2,35)
    h_ratio.Write()

    # plot ratios
    c = ROOT.TCanvas("c", "c")
    c.SetTicks(1, 1)
    c.cd()
    h_ratio.Draw()
    formatted_ratio = "{:.2f}".format(h_ratio.GetFunction("pol0").GetParameter(0))
    formatted_ratio_error = "{:.2f}".format(h_ratio.GetFunction("pol0").GetParError(0))
    text_x_position = 20
    ratio_text = ROOT.TLatex(text_x_position, 1.6, f"R = {formatted_ratio} #pm {formatted_ratio_error}")
    ratio_text.SetTextFont(44)
    ratio_text.SetTextSize(28)
    ratio_text.Draw("same")
    formatted_chi2 = "{:.2f}".format(h_ratio.GetFunction("pol0").GetChisquare())
    chi2_text = ROOT.TLatex(text_x_position, 1.45, "#chi^{2}/NDF = "+formatted_chi2+"/"+str(h_ratio.GetFunction("pol0").GetNDF()))
    chi2_text.SetTextFont(44)
    chi2_text.SetTextSize(28)
    chi2_text.Draw("same")
    c.Print(f"plots/{h_ratio.GetName()}.pdf")

    del h_corrected_yields
    del h_ratio

ratio_file.Close()


import io
import sys

_INPUT = """\
4 4
1 2
1 3
1 4
3 4
1 3
1 4
2 3
3 4
"""
sys.stdin = io.StringIO(_INPUT)

# 本番コードはこのコメント以下に書く、import,def,も

n,m=map(int, input().split())  #複数数値入力　「A B」みたいなスペース空いた入力のとき

graph_taka = [[] for _ in range(n)]
for _ in range(m):
    a, b = map(int, input().split())
    graph_taka[a-1].append(b-1)
    graph_taka[b-1].append(a-1)  # 有向グラフなら消す
# print(graph_taka)  # [[2, 3, 5], ..., [1, 3, 4]]

graph_ao = [[] for _ in range(n)]
for _ in range(m):
    a, b = map(int, input().split())
    graph_ao[a-1].append(b-1)
    graph_ao[b-1].append(a-1)  # 有向グラフなら消す
# print(graph_ao)  # [[2, 3, 5], ..., [1, 3, 4]]

# if graph_taka == graph_ao :

graph_ao = [ graph_ao[2] , graph_ao[1] , graph_ao[0] ,graph_ao[3]]
print(graph_ao)


from django.urls import include, path
from rest_framework import routers

from api.views import BasketModelAPIViewSet, ProductModelAPIViewSet

app_name = 'api'

router = routers.DefaultRouter()
router.register(r'products', ProductModelAPIViewSet)
router.register(r'baskets', BasketModelAPIViewSet)

urlpatterns = [
    path('', include(router.urls)),
]


from pathlib import Path

from git import Repo

GENERATION_DICT = dict(example_mouse=[100])

cwd = Path.home() / "bg_auto"
cwd.mkdir(exist_ok=True)

if __name__ == "__main__":
    repo_path = cwd / "atlas_repo"
    atlas_gen_path = Path(__file__).parent

    repo = Repo(repo_path)

    # repo.git.add(".")
    # repo.git.commit('-m', 'test commit', author='luigi.petrucco@gmail.com')
    repo.git.pull()
    repo.git.push()


# import json
# import requests
from Logging_Conversation import LoggingConversation
# import os
# from dotenv import load_dotenv

#######################################################################################################################
#######											Executing Logic
#######################################################################################################################

def UserEnteringNameYes(req,IntentName):
    result = req.get("queryResult")
    outputContexts = result.get("outputContexts")
    user_name = outputContexts[1]['parameters']['user-name']
    print(user_name)
    queryTextRequest = 'User Confirmed his name with - Yes'
    queryTextResponse = {"fulfillmentMessages": [{"text": {"text": ["Thanks for confirming your name.. PLease enter your email"]},"platform": "TELEGRAM"}]}

    print('User name response is:')
    print(queryTextResponse)

    LoggingConversation(queryTextRequest, queryTextResponse, IntentName,user_name)

    return(queryTextResponse)