# from pudb import set_trace; set_trace()
from typing import List
import heapq

class Solution1:
    def scheduleCourse(self, courses: List[List[int]]) -> int:
        """LeetCode 630

        This solution is based on hint, although I have basically the same
        idea as the hint. Yet, I was not sure to pursue the idea illustrated in
        the hint.

        The first task is to sort courses. We sort based on last day. It can be
        proven that having large last day after small last day is a better
        strategy than the other way around. This is because, if cur_time + dur
        fits in both the small and large last day, then their order doesn't
        matter. Similarly, if cur_time + dur does not fit in either the small or
        large last day, their order doesn't matter. It only matters, if one
        of the last day fits. Apparently, it can only be the large last day that
        fits, and the small last day doesn't fit. In this scenario, if we do
        the large last day first, then we are guaranteed to only fit one course.
        However, if we do the small last day first, then it is guaranteed that
        we can fit both courses. Therefore, it is always better to sort the
        courses based on their last day.

        If the last day is the same, we want to have the smaller duration first,
        because that can produce larger gaps between the end of the class and
        the last day, which provides higher chance of fitting another class.

        The key idea from the hint is to iterate through the sorted courses, and
        when a course cannot fit, we check whether it is a good deal to drop
        the course with the largest duration so far. The check is based on
        whether the drop can fit the new last day and whether the drop leads to
        bigger gap between the end of the current course and the last day. If
        both conditions are satisfied, we do the drop.

        To obtain the largest duration so far, we use a heapq.

        O(Nlog(N)) 732 ms, 31% ranking.
        """
        courses.sort(key=lambda tup: (tup[1], tup[0]))
        heap = []
        cur_time = 0
        for dur, last in courses:
            if cur_time + dur <= last:
                heapq.heappush(heap, -dur)
                cur_time += dur
            else:
                if heap:
                    max_dur = -heapq.heappop(heap)
                    if cur_time + dur - max_dur <= last and cur_time + dur - max_dur < cur_time:
                        cur_time = cur_time - max_dur + dur
                        heapq.heappush(heap, -dur)
                    else:
                        heapq.heappush(heap, -max_dur)
        return len(heap)

class Solution2:
    def scheduleCourse(self, courses: List[List[int]]) -> int:
        """This is according to the solution. The key idea is to pop the max
        duration encountered so far if and only if it is larger than the current
        duration. This is because if the max duration is larger than the current
        duration, we can guarantee that the swap results in larger gap between
        the current time and the last day. In other words, we don't have to
        check for it separately.
        """
        courses.sort(key=lambda tup: (tup[1], tup[0]))
        heap = []
        cur_time = 0
        for dur, last in courses:
            if cur_time + dur <= last:
                heapq.heappush(heap, -dur)
                cur_time += dur
            elif heap and -heap[0] > dur:
                cur_time = cur_time + dur + heapq.heappop(heap)
                heapq.heappush(heap, -dur)
        return len(heap)

sol = Solution2()
tests = [
    ([[100, 200], [200, 1300], [1000, 1250], [2000, 3200]], 3),
    ([[1, 2]], 1),
    ([[3, 2], [4, 3]], 0),
    ([[5, 5], [4, 6], [2, 6]], 2),
]
for i, (courses, ans) in enumerate(tests):
    res = sol.scheduleCourse(courses)
    if res == ans:
        print(f'Test {i}: PASS')
    else:
        print(f'Test {i}; Fail. Ans: {ans}, Res: {res}')


# {
# 	"national":
# 		[
# 			"THW force all news organizations to be non profit(i.e. NHK, BBC, PBS)",
# 			"THR the romanticization of success stories about educational achievement(e.g Biri Gal, Dragon Zakura)",
# 		],
# 	"international":
# 		""
# 		[

# 		],
# }

import json
from collections import OrderedDict
import pprint

json_dict = {}
json_dict["national"] = []
json_dict["international"] = []

input_file_name = "./data/datastructure/datastructure.json"

with open(input_file_name, "r") as f:
	datas = json.loads(f.read())
	for i in range(len(datas["national"])):
		for j in range(len(datas["national"][i]["rounds"])):
			json_dict["national"].append(datas["national"][i]["rounds"][j]["motion"])

	for i in range(len(datas["international"])):
		for j in range(len(datas["international"][i]["rounds"])):
			json_dict["international"].append(datas["international"][i]["rounds"][j]["motion"])

print(len(json_dict["national"]))

class MyEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, numpy.integer):
            return int(obj)
        elif isinstance(obj, numpy.floating):
            return float(obj)
        elif isinstance(obj, numpy.ndarray):
            return obj.tolist()
        else:
            return super(MyEncoder, self).default(obj)

output_file_name = "./data/datastructure/motions.json"
with open(output_file_name, 'w', encoding='utf-8') as fw:
    fw.write(json.dumps(json_dict, indent=4, cls=MyEncoder))


from uuid import UUID

from crm_pilates.command.command_bus import CommandBus
from crm_pilates.command.response import Response
from crm_pilates.command.saga_handler import SagaHandler
from crm_pilates.domain.attending.session import ConfirmedSession
from crm_pilates.domain.commands import SessionCreationCommand
from crm_pilates.domain.sagas import SessionCheckinSaga
from crm_pilates.domain.scheduling.attendee import Attendee
from crm_pilates.event.event_store import Event, EventSourced
from crm_pilates.infrastructure.repository_provider import RepositoryProvider

@EventSourced
class SessionCheckedIn(Event):
    def __init__(self, root_id: UUID, attendee: Attendee) -> None:
        self.checked_in_attendee = attendee
        super().__init__(root_id)

    def __map_attendee(self, attendee: Attendee) -> dict:
        return {"id": attendee.id, "attendance": attendee.attendance.value}

    def _to_payload(self):
        return {
            "session_id": self.root_id,
            "attendee": self.__map_attendee(self.checked_in_attendee),
        }

class SessionCheckinSagaHandler(SagaHandler):
    def __init__(self, command_bus: CommandBus) -> None:
        super().__init__(command_bus)

    def execute(self, saga: SessionCheckinSaga) -> SessionCheckedIn:
        session = (
            RepositoryProvider.write_repositories.session.get_by_classroom_id_and_date(
                saga.classroom_id, saga.session_date
            )
        )
        if session:
            session_id = session.id
        else:
            confirmed_session_event: Response = self._command_bus.send(
                SessionCreationCommand(saga.classroom_id, saga.session_date)
            )
            session_id = confirmed_session_event.event.root_id

        confirmed_session: ConfirmedSession = (
            RepositoryProvider.write_repositories.session.get_by_id(session_id)
        )
        checked_in_attendee: Attendee = confirmed_session.checkin(
            Attendee.create(saga.attendee)
        )
        return SessionCheckedIn(confirmed_session.id, checked_in_attendee)


#Program to tanslate languages.This uses google translate module to do the transilation
#Created by Aswin KS

import googletrans
from tkinter import *  #for creating GUI
from tkinter import messagebox #for message box
from googletrans import Translator
from numpy import common_type
import pyperclip #for copy to clipboard

#-----Method to translate--------------------
def transilate():
    dest.delete(0,END)
    translator = Translator()

    input=source_input.get()
    print(input)
    destination=menu.get()
    result = translator.translate(input,dest=destination)
    dest.insert(0,result.text)

#-----Method to Copy text to clipboard--------------------
def copyclip():
    op=dest.get()
    pyperclip.copy(op)   #To copy password to clipboard
    messagebox.showinfo(title="Data Copied", message="Data Copied to Clipboard")

#--------------------------------------------UI setup---------------------------------------------------------------
window=Tk()

#Title bar
window.title("Aswin's Transilator")
window.resizable(0,0)  #to remove maximise button
window.config(padx=50,pady=50)
canvas=Canvas(height=200,width=300)

#image
logo=PhotoImage(file="D:\\Devops\\Python-Begineer\Real-world-Projects\\Language-Transilator\\trans.png") #chnage the path accordingly
canvas.create_image(80,80,image=logo)
canvas.grid(row=4,column=3)

# Create Dropdown menu
choices = { 'English','Hindi','Gujarati','Spanish','German','malayalam'}
menu= StringVar()
menu.set("Select source Language")
drop1 = OptionMenu(window,menu,*choices)
drop1.grid(row=0,column=1)

#label for source
source=Label(text="Enter the source text: ")
source.grid(row=1,column=1)

#inputbox for source
source_input=Entry(width=70)
source_input.grid(row=1,column=2)

# Create Dropdown menu
menu= StringVar()
menu.set("Select Destination Language")
drop2 = OptionMenu(window,menu,*choices)
drop2.grid(row=2,column=1)

#Inputbox for Dest
dest=Entry(width=70)
dest.grid(row=2,column=2)

#button for transilate calling transilate method()
search_button=Button(text="Translate",command=transilate)
search_button.grid(row=1,column=3)

#button for copy calling copyclip method()
copy_button=Button(text="Copy",command=copyclip)
copy_button.grid(row=2,column=3)

#method for printing about
def print_about():
    messagebox.showinfo(title="About", message="Created by Aswin KS. Find More @ https://github.com/aswinks1995")

#button for About
about_button=Button(text="About",command=print_about)
about_button.grid(row=4,column=1)

window.mainloop()


#########################################################
## Stat 413 - Homework 3
## Author: Luxi Li
## Date : 2/18/2018
## Description: This script implements an adaboost classifier
#########################################################

#############################################################
## INSTRUCTIONS: Please fill in the missing lines of code
## only where specified. Do not change function names,
## function inputs or outputs. You can add examples at the
## end of the script (in the "Optional examples" section) to
## double-check your work, but MAKE SURE TO COMMENT OUT ALL
## OF YOUR EXAMPLES BEFORE SUBMITTING.
##
## Very important: Do not use the function "os.chdir" anywhere
## in your code. If you do, I will be unable to grade your
## work since Python will attempt to change my working directory
## to one that does not exist.
#############################################################

import math
import numpy as np
import sklearn.datasets as ds
from sklearn.model_selection import train_test_split

def prepare_data(valid_digits = np.array((6, 5))):

    ## valid_digits is a vector containing the digits
    ## we wish to classify.
    ## Do not change anything inside of this function
    if len(valid_digits) != 2:
        raise Exception("Error: you must specify exactly 2 digits for classification!")

    data = ds.load_digits()
    labels = data['target']
    features = data['data']

    X = features[(labels == valid_digits[0]) | (labels == valid_digits[1]), :]
    Y = labels[(labels == valid_digits[0]) | (labels == valid_digits[1]),]

    X = np.asarray(list(map(lambda k: X[k,:]/X[k,:].max(), range(0,len(X)))))

    Y[Y == valid_digits[0]] = 0
    Y[Y == valid_digits[1]] = 1

    X_train, X_test, Y_train, Y_test = train_test_split(X, Y, test_size=0.25, random_state=10)
    Y_train = Y_train.reshape((len(Y_train), 1))
    Y_test = Y_test.reshape((len(Y_test), 1))

    return X_train, Y_train, X_test, Y_test

##########################
## Function 1: Adaboost ##
##########################

## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##
## Use Adaboost to classify the digits data ##
## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ##

def my_Adaboost(X_train, Y_train, X_test, Y_test, num_iterations=200):
    ## X_train: Training set of features
    ## Y_train: Training set of labels corresponding to X_train
    ## X_test: Testing set of features
    ## Y_test: Testing set of labels correspdonding to X_test
    ## num_iterations: Number of iterations.

    ## Function should learn the parameters of an Adaboost classifier.

    n = X_train.shape[0]
    p = X_train.shape[1]
    threshold = 0.8

    X_train1 = 2 * (X_train > threshold) - 1
    Y_train = 2 * Y_train - 1

    X_test1 = 2 * (X_test > threshold) - 1
    Y_test = 2 * Y_test - 1

    beta = np.repeat(0., p).reshape((p, 1))
    w = np.repeat(1. / n, n).reshape((n, 1))

    weak_results = np.multiply(Y_train, X_train1) > 0

    acc_train = np.repeat(0., num_iterations, axis=0)
    acc_test = np.repeat(0., num_iterations, axis=0)

    #######################
    ## FILL IN CODE HERE ##
    #######################

    for it in range(num_iterations):
        weighted_weak_results = np.multiply(w, weak_results)
        weighted_accuracy = np.sum(weighted_weak_results, axis = 0)
        e = 1 - weighted_accuracy
        j = np.argmin(e)
        dbeta = 0.5* math.log((1 - e[j]) / e[j])
        beta[j] = beta[j] + dbeta
        w = np.multiply(w, np.exp(-np.multiply(np.multiply(Y_train, X_train1[:, j:(j+1)]), dbeta)))
        w = w/np.sum(w)
        acc_train[it] = np.mean(np.multiply(np.sign(np.dot(X_train1, beta)), Y_train) >0)
        #print acc_train[it]
        #acc_test[it] = np.mean(np.sign(np.dot(X_test1, beta)) == Y_test)
        acc_test[it] = np.mean(np.multiply(np.sign(np.dot(X_test1, beta)), Y_test) > 0 )

    ## Function should output 3 things:
    ## 1. The learned parameters of the adaboost classifier, beta
    ## 2. The accuracy over the training set, acc_train (a "num_iterations" dimensional vector).
    ## 3. The accuracy over the testing set, acc_test (a "num_iterations" dimensional vector).
    return beta, acc_train, acc_test

############################################################################
## Testing your functions and visualize the results here##
############################################################################

X_train, Y_train, X_test, Y_test = prepare_data()

beta, acc_train, acc_test = my_Adaboost(X_train, Y_train, X_test, Y_test, num_iterations = 300)
#print(beta)
#print(acc_train)
#print(acc_test)
print('median accuracy when number of iterations is 300:')
print('test accuracy:')
print(np.median(acc_test))
print('train accuracy:')
print(np.median(acc_train))

beta, acc_train, acc_test = my_Adaboost(X_train, Y_train, X_test, Y_test, num_iterations = 100)
print('accuracy in iterations of 1000:')
print('maximum accuracy in testing data:')
print(np.max(acc_test))
print('maximum accuracy in training data:')
print(np.max(acc_train))
print('median accuracy in testing data:')
print(np.median(acc_test))
print('median accuracy in training data:')
print(np.median(acc_train))

print(acc_train)
print(acc_test)

####################################################
## Optional examples (comment out your examples!) ##
####################################################


from graphviz import Digraph

#class ExecuteTaskGraph(task_graph):

    #def __init__(self, task_graph):
        #print ('init ExecuteTaskGraph')

    #def execute_tool(tool_name, tool_args):
        #args = ast.literal_eval(tool_args)
        #tool_instance = globals()[tool_name]()
        #tool_result = tool_instance.run( tool_args)
        #return tool_result

def visualize_knowledge_graph(kg):
    print (kg.keys())
    dot = Digraph(comment="Knowledge Graph")

    # Add nodes
    for node in kg['nodes']:
        dot.node(str(node['id']), node['label'], color=node['color'])

    # Add edges
    for edge in kg['edges']:
        #dot.edge(str(edge['source']), str(edge['target']), label=edge['label'], color=edge['color'])
        dot.edge(str(edge['source']), str(edge['target']), label=edge['label'])

    # Render the graph
    #dot.render("knowledge_graph.gv", view=True)
    return dot

def generate_graphviz(data):
    dot = Digraph()

    # Create nodes
    for item in data:
        task_id = str(item['id'])
        task_name =  f"{task_id}: {item['task']}"
        tool_name = f"{item['tool_name']}"
        dot.node(task_id, task_name + "\n Tool to Use:" + tool_name)

    # Create edges
    for item in data:
        task_id = str(item['id'])
        subtasks = item['subtasks']
        for subtask in subtasks:
            dot.edge(task_id, str(subtask))
            #dot.edge(str(subtask), task_id)
    # Render the graph
    #dot.format = 'png'
    dot.attr(size=f"{10},{10}", dpi='50')
    dot.render('graph', view=True, cleanup=True, format='svg')

def code_to_work ():

    # Generate the graph
    generate_graphviz(plan.dict()['task_graph'])

    from IPython.display import Image

    from IPython.display import Image, display, HTML, SVG

    # Path to your image
    image_path = 'graph.png'

    # Desired height for the image (in pixels)
    image_height = 1000
    image_width = 1000
    # Create an HTML tag with the image and set the height
    image_html = f'<img src="{image_path}" style="height:{image_height}px; width:{image_width}px">'

    # Display the image in the notebook
    #display(HTML(image_html))

    #display(Image(filename='graph.png', width=1000, height=2000)    )
    display(SVG(filename='graph.svg')    )


import scooby
import unittest
from SimPEG import Report

class TestReport(unittest.TestCase):
    def test_version_defaults(self):
        # Reporting is now done by the external package scooby.
        # We just ensure the shown packages do not change (core and optional).
        out1 = Report()
        out2 = scooby.Report(
            core=[
                "SimPEG",
                "discretize",
                "pymatsolver",
                "numpy",
                "scipy",
                "sklearn",
                "matplotlib",
                "empymod",
                "geoana",
                "pandas",
            ],
            # Optional packages.
            optional=[
                "cython",
                "pydiso",
                "numba",
                "dask",
                "sympy",
                "IPython",
                "ipywidgets",
                "plotly",
                "vtk",
                "utm",
                "memory_profiler",
            ],
            ncol=3,
            text_width=80,
            sort=False,
        )

        # Ensure they're the same; exclude initial time to avoid errors due
        # to second-change.
        assert out1.__repr__()[115:] == out2.__repr__()[115:]

if __name__ == "__main__":
    unittest.main()


from database import db
from datetime import date
from sqlalchemy import and_
from .inventoryModel import Inventory
from ..theatre.theatreRepository import get_theatre_by_id
import os, sys

# Add the project's root directory to the Python path
current_dir = os.path.dirname(os.path.realpath(__file__))
project_root = os.path.abspath(os.path.join(current_dir, '../../../'))
sys.path.insert(0, project_root)

# Now you can import the required module
from utils.errors import *

from datetime import datetime

#add new inventory
def add_inventory(theatre_id, show_id, date, start_time, end_time, retail_price, selling_price, is_active=True):
    try:
        theatre = get_theatre_by_id(theatre_id)
        date_obj = datetime.strptime(date, "%Y-%m-%d").date()
        start_time_obj = datetime.strptime(start_time, "%H:%M:%S").time()
        end_time_obj = datetime.strptime(end_time, "%H:%M:%S").time()

        new_inventory = Inventory(
            theatre_id=theatre_id,
            show_id=show_id,
            date=date_obj,
            start_time=start_time_obj,
            end_time=end_time_obj,
            retail_price=retail_price,
            selling_price=selling_price,
            is_active=is_active,
            available_seats=theatre.capacity
        )
        db.session.add(new_inventory)
        db.session.commit()
        return new_inventory
    except Exception as err:
        return {
            'success': False,
            'error': str(err),
        }

#get all inventorys
def get_all_inventorys():
    try:
        inventorys = Inventory.query.filter(Inventory.date >= date.today()).all()
        if inventorys:
            return inventorys  # Return the list of inventory objects
        raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        raise Exception(str(err))

#delete inventory
def delete_inventory_by_id(id):
    try:
        # Check if the inventory with the given ID exists
        inventory_to_delete = Inventory.query.get(id)

        if inventory_to_delete:
            db.session.delete(inventory_to_delete)
            db.session.commit()
            return {'success': True, 'message': 'Inventory deleted successfully'}
        else:
            raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        return {'success': False, 'error': str(err)}

# Update inventory by ID
def update_inventory(id, theatre_id, show_id, date, start_time, end_time, retail_price, selling_price, is_active):
    try:
        # Check if the inventory with the given ID exists
        inventory_to_update = Inventory.query.get(id)

        if inventory_to_update:
            # Update the inventory attributes
            date_obj = datetime.strptime(date, "%Y-%m-%d").date()
            start_time_obj = datetime.strptime(start_time, "%H:%M:%S").time()
            end_time_obj = datetime.strptime(end_time, "%H:%M:%S").time()

            inventory_to_update.theatre_id = theatre_id
            inventory_to_update.show_id = show_id
            inventory_to_update.date = date_obj
            inventory_to_update.start_time = start_time_obj
            inventory_to_update.end_time = end_time_obj
            inventory_to_update.retail_price = retail_price
            inventory_to_update.selling_price = selling_price
            inventory_to_update.is_active = is_active

            db.session.commit()
            return {
                'success': True,
                'message': 'Inventory updated successfully'
            }
        else:
            raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        return {
            'success': False,
            'error': str(err),
        }

# Update inventory by ID
def update_inventory_count(id,count):
    try:
        # Check if the inventory with the given ID exists
        inventory_to_update = Inventory.query.get(id)

        if inventory_to_update:
            inventory_to_update.available_seats=count
            db.session.commit()
            return {
                'success': True,
                'message': 'Inventory updated successfully'
            }
        else:
            raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        return {
            'success': False,
            'error': str(err),
        }

#find inventory by theatre
def find_inventory_by_theatre(theatre_id):
    try:
        inventorys = Inventory.query.filter_by(theatre_id=theatre_id)
        if inventorys:
            return inventorys  # Return the list of inventory objects
        raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        raise Exception(str(err))

#find inventory by show
def find_inventory_by_show(show_id):
    try:
        inventorys = Inventory.query.filter_by(show_id=show_id)
        if inventorys:
            return inventorys  # Return the list of inventory objects
        raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        raise Exception(str(err))

#find inventory by start_time
def find_inventory_by_start_time(start_time):
    try:
        start_time_obj = datetime.strptime(start_time, "%H:%M:%S").time()
        inventorys = Inventory.query.filter_by(start_time=start_time_obj)
        if inventorys:
            return inventorys  # Return the list of inventory objects
        raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        raise Exception(str(err))

#find inventory by id
def find_inventory_by_id(id):
    try:
        inventory = Inventory.query.get(id)
        if inventory:
            return inventory  # Return the list of inventory objects
        raise Exception(ERROR_MESSAGES["INVENTORY_NOT_FOUND"])
    except Exception as err:
        raise Exception(str(err))


from typing import Type

import torch

from loss import temperature
from patch import Patch

class ContrastiveWeightedKNN():
    def __init__(self, contrastive_model: Type[torch.nn.Module], train, train_subset_ratio=.5, k=10):
        self.contrastive_model = contrastive_model
        self.train_x, self.train_y = train
        self.train_subset_ratio = train_subset_ratio
        self.k = k

    def shuffle(self):
        random_order = torch.randperm(self.train_x.shape[0])
        self.train_x = self.train_x[random_order]
        self.train_y = self.train_y[random_order]

    def w_knn(self, test_x):
        train_x = self.train_x[:(int)(self.train_subset_ratio * len(self.train_x))]
        train_y = self.train_y[:(int)(self.train_subset_ratio * len(self.train_y))]

        _c = Patch.classes
        _cn = 2
        weights = torch.mm(test_x, train_x.t())  # |test| x |train|
        kweights, k_train_indices = torch.topk(weights, self.k, dim=-1)  # |test| x k
        kweights = (kweights / temperature).exp()
        klabels = train_y[k_train_indices, :]  # |test| x k x c
        klabels = torch.nn.functional.one_hot(klabels.long(), num_classes=_cn)  # |test| x k x c x _cn
        voting = kweights.unsqueeze(-1).unsqueeze(-1) * klabels
        voting = torch.sum(voting, dim=1)
        voting = torch.argsort(voting, dim=-1, descending=True)

        return voting[:, :, 0]

    def __call__(self, x):
        x = x.cuda(non_blocking=True)
        features, _ = self.contrastive_model(x)
        prediction = self.w_knn(features)
        return prediction


import numpy as np
import nengo
from nengo.dists import Uniform
from nengo.solvers import NoSolver
from nengo.utils.numpy import rmse
from nengolib import Lowpass, DoubleExp
from nengolib.signal import s
from utils import decodeNoF, WNode, learnEncoders, setWeights, decode, plotState, plotActivity
from neurons import LIF, ALIF, Wilson, Bio, reset_neuron, NMDA
import neuron
import matplotlib.pyplot as plt
import seaborn as sns
sns.set(context='paper', style='whitegrid')

def makeSignal(t, fPre, fNMDA, dt=0.001, value=1.0, freq=1, seed=0, c=None):
    if not c: c = t
    stim = nengo.processes.WhiteSignal(period=t/2, high=freq, rms=0.5, seed=seed)
    with nengo.Network() as model:
        u = nengo.Node(stim)
        pU = nengo.Probe(u, synapse=None)
        pX = nengo.Probe(u, synapse=1/s)
    with nengo.Simulator(model, progress_bar=False) as sim:
        sim.run(t/2, progress_bar=False)
    u = fNMDA.filt(fPre.filt(sim.data[pU]))
    x = fNMDA.filt(fPre.filt(sim.data[pX]))
    norm = value / np.max(np.abs(u))
    # norm = value / np.max(np.abs(x))
    mirrored = np.concatenate(([[0]], sim.data[pU]*norm, -sim.data[pU]*norm))
    func = lambda t: mirrored[int(t/dt)] if t<c else 0
    return func

# def ideal(N=100, t=10, c=7, f=DoubleExp(10e-3, 2.85e-1)):
#     stim = nengo.processes.WhiteSignal(period=t, high=0.5, rms=0.3, seed=0)
#     cutoff = lambda t: 0 if t<c else 1
#     with nengo.Network() as network:
#         inpt = nengo.Node(stim)
#         cut = nengo.Node(cutoff)
#         diff = nengo.Ensemble(N, 1, max_rates=Uniform(30, 30), intercepts=Uniform(-0.8, 0.8), seed=0)
#         ens = nengo.Ensemble(N, 1, max_rates=Uniform(30, 30), intercepts=Uniform(-0.8, 0.8), seed=0)
#         nengo.Connection(inpt, diff, synapse=None)
#         nengo.Connection(cut, diff.neurons, synapse=None, transform=-1e2*np.ones((N, 1)))
#         nengo.Connection(diff, ens, synapse=f)
#         nengo.Connection(ens, ens, synapse=f)
#         nengo.Connection(ens, diff, synapse=f, transform=-1)
#         pInpt = nengo.Probe(inpt, synapse=f)
#         pDiff = nengo.Probe(diff, synapse=f)
#         pEns = nengo.Probe(ens, synapse=f)
#     with nengo.Simulator(network, progress_bar=False) as sim:
#         sim.run(t, progress_bar=True)
#     fig, ax = plt.subplots()
#     ax.plot(sim.trange(), sim.data[pInpt], label='inpt')
#     ax.plot(sim.trange(), sim.data[pDiff], label='diff')
#     ax.plot(sim.trange(), sim.data[pEns], label='ens')
#     ax.legend()
#     fig.savefig("plots/diffMemory_ideal.pdf")

def go(NPre=100, N=100, t=10, c=None, dt=0.001, m=Uniform(30, 30), i=Uniform(-0.8, 0.8), seed=0, neuron_type=LIF(),
    fPre=None, fNMDA=None, dPre=None, dDiff=None, dEns=None, dNeg=None, Tff=1.0, stim=lambda t: 0, train=False):

    if not c: c = t
    dFF = dDiff*Tff if np.any(dDiff) else None
    with nengo.Network(seed=seed) as model:
        inpt = nengo.Node(stim)
        intg = nengo.Ensemble(1, 1, neuron_type=nengo.Direct())
        cut = nengo.Node(lambda t: 0 if t<c else 1)
        pre = nengo.Ensemble(NPre, 1, max_rates=m, seed=seed)
        pre2 = nengo.Ensemble(NPre, 1, max_rates=m, seed=seed)
        diff = nengo.Ensemble(N, 1, max_rates=m, intercepts=i, neuron_type=neuron_type, seed=seed)
        ens = nengo.Ensemble(N, 1, max_rates=m, intercepts=i, neuron_type=neuron_type, seed=seed)
        nengo.Connection(inpt, intg, synapse=1/s)
        nengo.Connection(inpt, pre, synapse=None, seed=seed)
        nengo.Connection(intg, pre2, synapse=fNMDA, seed=seed)
        c1 = nengo.Connection(pre, diff, synapse=fPre, solver=NoSolver(dPre), seed=seed)
        c2 = nengo.Connection(diff, ens, synapse=fNMDA, solver=NoSolver(dFF), seed=seed)
        c3 = nengo.Connection(ens, ens, synapse=fNMDA, solver=NoSolver(dEns), seed=seed)
        c4 = nengo.Connection(ens, diff, synapse=fNMDA, solver=NoSolver(dNeg), seed=seed)
        c5 = nengo.Connection(cut, diff.neurons, synapse=None, transform=-1e2*np.ones((N, 1)))
        if train: nengo.Connection(pre2, ens, synapse=fPre, solver=NoSolver(dPre), seed=seed)
        pInpt = nengo.Probe(inpt, synapse=None)
        pIntg = nengo.Probe(intg, synapse=None)
        pPre = nengo.Probe(pre.neurons, synapse=None)
        pDiff = nengo.Probe(diff.neurons, synapse=None)
        pEns = nengo.Probe(ens.neurons, synapse=None)

    with nengo.Simulator(model, seed=seed, dt=dt, progress_bar=False) as sim:
        sim.run(t, progress_bar=True)

    return dict(
        times=sim.trange(),
        inpt=sim.data[pInpt],
        intg=sim.data[pIntg],
        pre=sim.data[pPre],
        diff=sim.data[pDiff],
        ens=sim.data[pEns],
    )

def run(NPre=100, N=100, t=10, c=None, nTrain=10, nTest=5, dt=0.001, neuron_type=LIF(),
    fPre=DoubleExp(1e-3, 1e-1), fNMDA=DoubleExp(10.6e-3, 285e-3),
    reg=1e-2, load=[], file=None, neg=True):

    if not c: c = t
    if 0 in load:
        dPre = np.load(file)['dPre']
    else:
        print('readout decoders for pre')
        spikesInpt = np.zeros((nTrain, int(t/dt), NPre))
        targetsInpt = np.zeros((nTrain, int(t/dt), 1))
        for n in range(nTrain):
            stim = makeSignal(t, fPre, fNMDA, seed=n)
            data = go(NPre=NPre, N=N, t=t, fPre=fPre, fNMDA=fNMDA, neuron_type=neuron_type, stim=stim)
            spikesInpt[n] = data['pre']
            targetsInpt[n] = fPre.filt(data['inpt'], dt=dt)
        dPre, X, Y, error = decodeNoF(spikesInpt, targetsInpt, nTrain, fPre, reg=reg)
        np.savez("data/diffMemory_%s.npz"%neuron_type, dPre=dPre)
        times = np.arange(0, t*nTrain, 0.001)
        plotState(times, X, Y, error, "diffMemory_%s"%neuron_type, "pre", t*nTrain)

    if 1 in load:
        dDiff = np.load(file)['dDiff']
    else:
        print('readout decoders for diff')
        spikes = np.zeros((nTrain, int(t/dt), N))
        targets = np.zeros((nTrain, int(t/dt), 1))
        for n in range(nTrain):
            stim = makeSignal(t, fPre, fNMDA, seed=n)
            data = go(NPre=NPre, N=N, t=t, fPre=fPre, fNMDA=fNMDA, neuron_type=neuron_type, stim=stim, dPre=dPre)
            spikes[n] = data['diff']
            targets[n] = fNMDA.filt(fPre.filt(data['inpt'], dt=dt), dt=dt)
        dDiff, X, Y, error = decodeNoF(spikes, targets, nTrain, fNMDA, reg=reg)
        np.savez("data/diffMemory_%s.npz"%neuron_type, dPre=dPre, dDiff=dDiff)
        times = np.arange(0, t*nTrain, dt)
        plotState(times, X, Y, error, "diffMemory_%s"%neuron_type, "diff", t*nTrain)

    if 2 in load:
        dEns = np.load(file)['dEns']
        dNeg = np.load(file)['dNeg']
    else:
        print('readout decoders for ens')
        spikes = np.zeros((nTrain, int(t/dt), N))
        targets = np.zeros((nTrain, int(t/dt), 1))
        for n in range(nTrain):
            stim = makeSignal(t, fPre, fNMDA, seed=n)
            # data = go(NPre=NPre, N=N, t=t, fPre=fPre, fNMDA=fNMDA, neuron_type=neuron_type, stim=stim, dPre=dPre, dDiff=dDiff)
            data = go(NPre=NPre, N=N, t=t, fPre=fPre, fNMDA=fNMDA, neuron_type=neuron_type, stim=stim, dPre=dPre, dDiff=dDiff, train=True, Tff=0.3)
            spikes[n] = data['ens']
            # targets[n] = fNMDA.filt(fNMDA.filt(fPre.filt(data['inpt'], dt=dt), dt=dt), dt=dt)
            targets[n] = fNMDA.filt(fPre.filt(data['intg'], dt=dt), dt=dt)
        dEns, X, Y, error = decodeNoF(spikes, targets, nTrain, fNMDA, reg=reg)
        dNeg = -np.array(dEns)
        np.savez("data/diffMemory_%s.npz"%neuron_type, dPre=dPre, dDiff=dDiff, dEns=dEns, dNeg=dNeg)
        times = np.arange(0, t*nTrain, dt)
        plotState(times, X, Y, error, "diffMemory_%s"%neuron_type, "ens", t*nTrain)

    print('testing')
    vals = np.linspace(-1, 1, nTest)
    for test in range(nTest):
        if neg:
            stim = lambda t: vals[test] if t<c else 0
            data = go(NPre=NPre, N=N, t=t, fPre=fPre, fNMDA=fNMDA, neuron_type=neuron_type,
                stim=stim, dPre=dPre, dDiff=dDiff, dEns=dEns, dNeg=dNeg, c=c)
        else:
            stim = makeSignal(t, fPre, fNMDA, seed=test)
            data = go(NPre=NPre, N=N, t=t, fPre=fPre, fNMDA=fNMDA, neuron_type=neuron_type,
                stim=stim, dPre=dPre, dDiff=dDiff, dEns=dEns, dNeg=None, c=None, Tff=0.3)
        aDiff = fNMDA.filt(fPre.filt(data['diff'], dt=dt), dt=dt)
        aEns = fNMDA.filt(fNMDA.filt(fPre.filt(data['ens'], dt=dt), dt=dt), dt=dt)
        xhatDiff = np.dot(aDiff, dDiff)
        xhatEns = np.dot(aEns, dEns)
        u = fNMDA.filt(fPre.filt(data['inpt'], dt=dt), dt=dt)
        u2 = fNMDA.filt(fNMDA.filt(fPre.filt(data['inpt'], dt=dt), dt=dt), dt=dt)
        x = fNMDA.filt(fNMDA.filt(fPre.filt(data['intg'], dt=dt), dt=dt), dt=dt)
        error = rmse(xhatEns, x)
        fig, ax = plt.subplots()
        if neg:
            ax.plot(data['times'], u2, alpha=0.5, label="input (delayed)")
            ax.axvline(c, label="cutoff")
        else:
            # ax.plot(data['times'], 0.3*u2, alpha=0.5, label="input")
            ax.plot(data['times'], x, alpha=0.5, label="integral")
        # ax.plot(data['times'], xhatDiff, label="diff")
        ax.plot(data['times'], xhatEns, label="ens")
        ax.set(xlabel='time', ylabel='state', title="rmse=%.3f"%error, xlim=((0, t)), ylim=((-1, 1)))
        ax.legend(loc='upper left')
        sns.despine()
        fig.savefig("plots/diffMemory_%s_test%s.pdf"%(neuron_type, test))

# ideal()
# run(N=100, t=10, c=5, nTrain=3, load=[0,1,2], neg=True, neuron_type=LIF(), file="data/diffMemory_LIF().npz")
run(N=30, t=10, c=5, nTrain=3, load=[0,1,2], neg=False, neuron_type=ALIF(), file="data/diffMemory_ALIF().npz")


from flask import Flask, render_template, request, redirect, Response, jsonify
import pandas as pd
import json
from sklearn.cluster import KMeans
from sklearn import metrics
from sklearn.decomposition import PCA
from sklearn.preprocessing import MinMaxScaler
from sklearn.preprocessing import StandardScaler
from sklearn.manifold import MDS

app = Flask(__name__)

@app.route("/", methods = ['GET', 'POST'])
def index():
    return render_template("index.html", data = "")

@app.route("/elbow-k-means", methods = ['POST'])
def get_elbow():
    global elbow_vals
    return jsonify(elbow_vals)

@app.route("/screeplot_full_data", methods = ['POST'])
def scree_plot_full_data():
    global df1
    data, list_intrinsic_dimensions, count_2 = calculate_intrinsic_dimensionality(df1)
    return jsonify(data)

@app.route("/screeplot_random_sample_data", methods = ['POST'])
def scree_plot_random_data():
    global df2
    data, list_intrinsic_dimensions, count_2 = calculate_intrinsic_dimensionality(df2)
    return jsonify(data)

@app.route("/screeplot_stratified_sample_data", methods = ['POST'])
def scree_plot_stratified_data():
    global df3
    data, list_intrinsic_dimensions, count_2 = calculate_intrinsic_dimensionality(df3)
    return jsonify(data)

@app.route("/scatter_plot_full_data_pc_values", methods = ['POST'])
def scatter_plot_full_data_pc_values():
    global df1
    data = calculate_top_PC_Vals(df1)
    return jsonify(data)

@app.route("/scatter_plot_random_sampled_data_pc_values", methods = ['POST'])
def scatter_plot_random_sampled_data_pc_values():
    global df2
    data = calculate_top_PC_Vals(df2)
    return jsonify(data)

@app.route("/scatter_plot_stratified_sampled_data_pc_values", methods = ['POST'])
def scatter_plot_stratified_sampled_data_pc_values():
    global df3
    data = calculate_top_PC_Vals(df3)
    return jsonify(data)

@app.route("/scatter_matrix_full_data", methods = ['POST'])
def scatter_matrix_full_data():
    global df1
    data_temp, list_intrinsic_dimensions, count_2 = calculate_intrinsic_dimensionality(df1)
    data = calculate_top_attributes(df1, list_intrinsic_dimensions,count_2)
    return jsonify(data)

@app.route("/scatter_matrix_random_sampled_data", methods = ['POST'])
def scatter_matrix_random_sampled_data():
    global df2
    data_temp, list_intrinsic_dimensions, count_2 = calculate_intrinsic_dimensionality(df2)
    data = calculate_top_attributes(df2, list_intrinsic_dimensions,count_2)
    return jsonify(data)

@app.route("/scatter_matrix_stratified_sampled_data", methods = ['POST'])
def scatter_matrix_stratified_sampled_data():
    global df3
    data_temp, list_intrinsic_dimensions, count_2 = calculate_intrinsic_dimensionality(df1)
    data = calculate_top_attributes(df3, list_intrinsic_dimensions, count_2)
    return jsonify(data)

@app.route("/mds_euclidean_full_data", methods = ['POST'])
def mds_euclidean_full_data():
    global df1
    data = calculate_mds(df1, 'euclidean')
    return jsonify(data)

@app.route("/mds_euclidean_random_data", methods = ['POST'])
def mds_euclidean_random_data():
    global df2
    data = calculate_mds(df2, 'euclidean')
    return jsonify(data)

@app.route("/mds_euclidean_stratified_data", methods = ['POST'])
def mds_euclidean_stratified_data():
    global df3
    data = calculate_mds(df3, 'euclidean')
    return jsonify(data)

@app.route("/mds_correlation_full_data", methods = ['POST'])
def mds_correlation_full_data():
    global df1
    data = calculate_mds(df1, 'correlation')
    return jsonify(data)

@app.route("/mds_correlation_random_data", methods = ['POST'])
def mds_correlation_random_data():
    global df2
    data = calculate_mds(df2, 'correlation')
    return jsonify(data)

@app.route("/mds_correlation_stratified_data", methods = ['POST'])
def mds_correlation_stratified_data():
    global df3
    data = calculate_mds(df3, 'correlation')
    return jsonify(data)

def calculate_intrinsic_dimensionality(df):
    #variables to obtain required values to send to front-end. Count_2 holds the intrinsic dimensionality
    count = 0
    count_2 = 0
    sum = 0
    sum_2 = 0
    ptg_data = []
    list_intrinsic_dimensions = []

    #performing PCA = 13
    pca = PCA(n_components=13)

    #scaling the data using standardscalar()
    x = StandardScaler().fit_transform(df.loc[:, :'Overall_Ranking'])
    principal_components = pca.fit_transform(x)
    data = pca.explained_variance_ratio_
    data = data.tolist()

    #identifying the PC components contributing < 80% and storing them to send to front end
    for i in data:
        sum += i
        if sum<0.80:
            count_2 +=1
            list_intrinsic_dimensions.append("PC" + str(count_2))

        ptg_data.append(sum)

    list_pca = []
    for i in data:
        count += 1
        list_pca.append("PC" + str(count))

    flag = 0
    list_pc_vector = []
    for i in range(len(data)):
        sum_2 += data[i]
        if sum_2 < 0.80:
            list_pc_vector.append("True")
        else:
            list_pc_vector.append("False")

    for i in range(len(list_pc_vector)):
        if list_pc_vector[i+1] == 'False':
            for j in range(i):
                list_pc_vector[j] = 'False'
            break

    #convering the data to json to send it to front end.
    mapping = {"PC": list_pca, "Value": data, "Percent_data": ptg_data, "PC_T_F": list_pc_vector}
    df_combined = pd.DataFrame(mapping)
    mapping = df_combined.to_dict(orient="records")
    chart_data = json.dumps(mapping, indent=2)
    data = {'chart_data': chart_data}
    return data, list_intrinsic_dimensions, count_2

def calculate_top_PC_Vals(df):

    #Performing PCA for n=2
    pca = PCA(n_components=2)

    #Scaling data using Standard Scalar and appending the University, Country and Rank
    x = StandardScaler().fit_transform(df.loc[:, :'Overall_Ranking'])
    pc = pca.fit_transform(x)
    data = pca.explained_variance_ratio_
    principal_Df = pd.DataFrame(data=pc, columns=['PC1', 'PC2'])
    principal_Df['University'] = df['University']
    principal_Df['Country'] = df['Country']
    principal_Df['Rank'] = df['Rank']

    #Formatting the data to send to the front end
    mapping = principal_Df.to_dict(orient="records")
    chart_data = json.dumps(mapping, indent=2)
    data = {'chart_data': chart_data}
    return data

def calculate_top_attributes(df, list_intrinsic_dimensions, count):
    df_temp = df.loc[:, :'Overall_Ranking']
    list_country = df['Country'].tolist()
    list_uni = df['University'].tolist()
    list_rank = df['Rank'].tolist()

    #running PCA for the intrinsic dimensionality
    pca = PCA(n_components=count)

    #transforming the values using standardscalar()
    x = StandardScaler().fit_transform(df_temp)
    pc1 = pca.fit_transform(x)
    data = pca.explained_variance_ratio_
    data = data.tolist()

    #obtaining the loadings per column and storing them in a dataframe
    loadings = pd.DataFrame(pca.components_.T, columns= list_intrinsic_dimensions,
                            index=df_temp.columns)

    #calculating the values with the highest PCA loadings
    for i in loadings:
        loadings[i] = loadings[i] ** 2
    loadings['sum'] = loadings.sum(axis=1)
    df_final = df_temp.loc[:, loadings.nlargest(3, ['sum']).index.tolist()]
    df_final['Country'] = list_country
    df_final['University'] = list_uni
    df_final['Rank'] = list_rank

    #formatting data to send to front end
    mapping = df_final.to_dict(orient="records")
    chart_data = json.dumps(mapping, indent=2)
    data = {'chart_data': chart_data}
    return data

def calculate_mds(df, distance):
    df_temp = df.loc[:, :'Overall_Ranking']

    #Scale data
    df_scaled = StandardScaler().fit_transform(df_temp)
    df_scaled_final = pd.DataFrame(df_scaled)

    #compute required pairwise distance
    matrix = metrics.pairwise.pairwise_distances(df_scaled_final, metric=distance)
    list_country = df['Country'].tolist()
    list_uni = df['University'].tolist()
    list_rank = df['Rank'].tolist()

    #perform MDS
    mds = MDS(n_components=2, dissimilarity='precomputed', n_jobs=-1)

    values = mds.fit_transform(matrix)

    df_final = pd.DataFrame(values, columns= ['MDS1', 'MDS2'])
    df_final['Country'] = list_country
    df_final['University'] = list_uni
    df_final['Rank'] = list_rank

    mapping = df_final.to_dict(orient="records")
    chart_data = json.dumps(mapping, indent=2)
    data = {'chart_data': chart_data}
    return data

def create_random_samples():
    global df1
    df_random = df1.sample(frac=0.25)
    df_random.to_csv("random_sample.csv", index=False)

def create_random_stratified_samples():

    global df1

    #array to store sum of squared distances
    sum_of_squared_distances = []
    #array to store k values
    k_vals = []
    #array to store elbow values
    elbow_values = []

    #performing k-means for k = 1 to k = 17
    for k in range(1,18):
        #appending k value in k_vals array
        k_vals.append(k)
        #running k-means function
        k_means = KMeans(n_clusters=k)
        #fitting the values and appending them to sum_of_squared_distances array
        model = k_means.fit(df1.loc[:, :'Overall_Ranking'])
        sum_of_squared_distances.append(k_means.inertia_ * 10**-12)

    #transforming the data to be able to send it to front end
    elbow_values.append(k_vals)
    elbow_values.append(sum_of_squared_distances)
    final_elbow_vals = pd.DataFrame(elbow_values)
    final_elbow_values = final_elbow_vals.T
    final_elbow_values.columns = ['K', 'Value']
    mapping = final_elbow_values.to_dict(orient="records")
    chart_data = json.dumps(mapping, indent=2)
    data = {'chart_data': chart_data}

    #performing k-means for k=4 based on elbow point
    k_means = KMeans(n_clusters=4)
    model = k_means.fit(df1.loc[:, :'Overall_Ranking'])
    y = k_means.predict(df1.loc[:, :'Overall_Ranking'])
    df1['Cluster'] = y

    #sampling 25% of each cluster
    df_c0 = df1.loc[df1['Cluster'] == 0]
    df_c0_sample = df_c0.sample(frac=0.25)

    df_c1 = df1.loc[df1['Cluster'] == 1]
    df_c1_sample = df_c1.sample(frac=0.25)

    df_c2 = df1.loc[df1['Cluster'] == 2]
    df_c2_sample = df_c2.sample(frac=0.25)

    df_c3 = df1.loc[df1['Cluster'] == 3]
    df_c3_sample = df_c3.sample(frac=0.25)

    #appending the sampled data into a single dataframe and dropping the cluser column
    df_strat_samples = df_c0_sample.append(df_c1_sample.append(df_c2_sample.append(df_c3_sample)))
    df_strat_final = df_strat_samples.drop(columns='Cluster')

    #storing the data into a .csv file
    df_strat_final.to_csv("random_stratified.csv", index=False)

    #returning the k-values for plotting elbow curve
    return data

if __name__ == "__main__":
    df1 = pd.read_csv("data_full.csv")
    create_random_samples()
    df2 = pd.read_csv("random_sample.csv")
    elbow_vals = create_random_stratified_samples()
    df3 = pd.read_csv("random_stratified.csv")
    app.run(debug=True)


"""A class that provides methods used to properly format and display replay data"""

import copy
from immutabledict import immutabledict
from typing import Iterable, Optional
from . import game_ids
from replays import models

#   These table fields configure the display of the stage split columns
#   These may differ from whether the replay file actually has the field or not
#       depending on whether it is relevant to show that field
#   eg. life_pieces will almost always be False as displaying it is integrated into Lives

_table_fields_th06 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": False,
        "graze": False,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": True,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th07 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": True,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": True,
        "th07_cherrymax": True,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th08 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": True,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th09 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": False,
        "graze": False,
        "point_items": False,
        "power": False,
        "lives": True,
        "bombs": False,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": True,
        "th09_p2_score": True,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th09_PVP = immutabledict(
    {
        "stage": True,
        "score": False,
        "piv": False,
        "graze": False,
        "point_items": False,
        "power": False,
        "lives": False,
        "bombs": False,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": True,
        "th09_p2_cpu": True,
        "th09_p2_shot": True,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th10 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": False,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": False,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th11 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": False,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th12 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th13 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": True,
        "th16_season_power": False,
        "extends": True,
    }
)

_table_fields_th14 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th15 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th16 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": True,
        "extends": False,
    }
)

_table_fields_th17 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th18 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": True,
        "graze": True,
        "point_items": False,
        "power": True,
        "lives": True,
        "bombs": True,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": False,
        "th128_perfect_freeze": False,
        "th128_frozen_area": False,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_table_fields_th128 = immutabledict(
    {
        "stage": True,
        "score": True,
        "piv": False,
        "graze": True,
        "point_items": False,
        "power": False,
        "lives": False,
        "bombs": False,
        "th06_rank": False,
        "th07_cherry": False,
        "th07_cherrymax": False,
        "th09_p1_cpu": False,
        "th09_p2_cpu": False,
        "th09_p2_shot": False,
        "th09_p2_score": False,
        "th128_motivation": True,
        "th128_perfect_freeze": True,
        "th128_frozen_area": True,
        "th13_trance": False,
        "th16_season_power": False,
        "extends": False,
    }
)

_game_fields = immutabledict(
    {
        "th01": None,
        "th05": None,
        "th06": _table_fields_th06,
        "th07": _table_fields_th07,
        "th08": _table_fields_th08,
        "th09": _table_fields_th09,
        "th10": _table_fields_th10,
        "th11": _table_fields_th11,
        "th12": _table_fields_th12,
        "th128": _table_fields_th128,
        "th13": _table_fields_th13,
        "th14": _table_fields_th14,
        "th15": _table_fields_th15,
        "th16": _table_fields_th16,
        "th17": _table_fields_th17,
        "th18": _table_fields_th18,
    }
)

_game_fields_PVP = immutabledict(
    {
        "th01": None,
        "th05": None,
        "th06": None,
        "th07": None,
        "th08": None,
        "th09": _table_fields_th09_PVP,
        "th10": None,
        "th11": None,
        "th12": None,
        "th128": None,
        "th13": None,
        "th14": None,
        "th15": None,
        "th16": None,
        "th17": None,
        "th18": None,
    }
)

def GetFormatPower(
    game_id: str, power: Optional[int], shot: Optional[str] = None
) -> str:
    if power is None:
        return ""
    if game_id in (game_ids.GameIDs.TH06, game_ids.GameIDs.TH07, game_ids.GameIDs.TH08):
        return str(power)
    if game_id == game_ids.GameIDs.TH11 and shot == "MarisaA":
        return "%.2f" % round(float(power) / 12, ndigits=2)
    if game_id in (game_ids.GameIDs.TH10, game_ids.GameIDs.TH11):
        return "%.2f" % (float(power) * 0.05)
    if game_id in (
        game_ids.GameIDs.TH12,
        game_ids.GameIDs.TH13,
        game_ids.GameIDs.TH14,
        game_ids.GameIDs.TH15,
        game_ids.GameIDs.TH16,
        game_ids.GameIDs.TH17,
        game_ids.GameIDs.TH18,
    ):
        return "{:.2f}".format(power / 100)

    return str(power)

# used in GetFormatLives for string formatting the life/life piece counts
# if this is None, lives will be displayed standalone instead of including the life piece counts
# Technically the maximum life pieces for TH12 is 5, but the first piece you collect gives you 2, so functionally there are only 4
#   The parser for the game accounts for this discrepancy
_life_pieces = immutabledict(
    {
        "th01": None,
        "th02": None,
        "th03": None,
        "th04": None,
        "th05": None,
        "th06": None,
        "th07": None,
        "th08": None,
        "th09": None,
        "th10": None,
        "th11": 5,
        "th12": 4,
        "th128": None,
        "th13": None,  # this game has variable life pieces, so hardcoding it doesn't work
        "th14": 3,
        "th15": 3,
        "th16": None,
        "th17": 3,
        "th18": 3,
    }
)

# used in GetFormatBombs for string formatting the bomb/bomb piece counts
_bomb_pieces = immutabledict(
    {
        "th01": None,
        "th02": None,
        "th03": None,
        "th04": None,
        "th05": None,
        "th06": None,
        "th07": None,
        "th08": None,
        "th09": None,
        "th10": None,
        "th11": None,
        "th12": 3,
        "th128": None,
        "th13": 8,
        "th14": 8,
        "th15": 5,
        "th16": 5,
        "th17": 3,
        "th18": 3,
    }
)

# Takes the lives and life pieces and formats them accordingly for viewing in frontend
# A special case is made here for TH13, since it has a variable number of life pieces needed
#   so the amount is calculated off of the current extends
def GetFormatLives(
    game_id: str,
    lives: Optional[int],
    life_pieces: Optional[int],
    extends: Optional[int] = 0,
) -> str:
    if lives is None:
        return ""

    life_pieces_str = ""

    if game_id == "th13":
        threshholds = [8, 10, 12, 15, 18, 20, 25]
        if extends is None:
            life_pieces_str = ""
        else:
            if extends > 5:
                extends = 6
            if extends < 0:
                extends = 0
            life_pieces_str = f" ({life_pieces}/{threshholds[extends]})"
    else:
        total_life_pieces = _life_pieces[game_id]
        if total_life_pieces is None:
            life_pieces_str = ""
        else:
            if life_pieces is None:
                life_pieces = 0
            life_pieces_str = f" ({life_pieces}/{total_life_pieces})"
    return f"{lives}{life_pieces_str}"

def GetFormatBombs(
    game_id: str, bombs: Optional[int], bomb_pieces: Optional[int]
) -> str:
    if bombs is None:
        return ""
    total_bomb_pieces = _bomb_pieces[game_id]
    if total_bomb_pieces is None:
        return str(bombs)
    else:
        if bomb_pieces is None:
            bomb_pieces = 0
        return f"{bombs} ({bomb_pieces}/{total_bomb_pieces})"

def GetGameField(gameid: str, replay_type: game_ids.ReplayTypes):
    if replay_type is game_ids.ReplayTypes.PVP:
        if gameid in _game_fields_PVP:
            return _game_fields_PVP[gameid]
    else:
        if gameid in _game_fields:
            return _game_fields[gameid]
    return None

FORMAT_EXTRA = "Extra"
FORMAT_PHANTASM = "Phantasm"

def GetFormatStage(game_id: str, stage: Optional[int]) -> str:
    if stage is None:
        return ""
    if game_id == "th08":
        stages = {
            1: "1",
            2: "2",
            3: "3",
            4: "4A",
            5: "4B",
            6: "5",
            7: "6A",
            8: "6B",
            9: FORMAT_EXTRA,
        }
        return stages[stage]
    if game_id == "th09":
        if stage == 10:
            return FORMAT_EXTRA
    elif game_id == "th07" and stage == 8:
        return FORMAT_PHANTASM
    elif game_id == "th128":
        stages = {
            1: "A1-1",
            2: "A1-2",
            3: "A1-3",
            4: "A2-2",
            5: "A2-3",
            6: "B1-1",
            7: "B1-2",
            8: "B1-3",
            9: "B2-2",
            10: "B2-3",
            11: "C1-1",
            12: "C1-2",
            13: "C1-3",
            14: "C2-2",
            15: "C2-3",
            16: "Extra",
        }
        return stages[stage]
    elif stage == 7:
        return FORMAT_EXTRA
    return str(stage)

def FormatStages(game_id: str, replay_stages: Iterable[models.ReplayStage], shot: str):
    """This function formats the stage values to be displayed in the front end"""
    new_stages = copy.deepcopy(replay_stages)

    for stage in new_stages:
        stage.power = GetFormatPower(game_id, stage.power, shot)
        stage.stage = GetFormatStage(game_id, stage.stage)
        stage.lives = GetFormatLives(
            game_id, stage.lives, stage.life_pieces, stage.extends
        )
        stage.bombs = GetFormatBombs(game_id, stage.bombs, stage.bomb_pieces)
        if game_id == game_ids.GameIDs.TH09:
            stage.th09_p2_shotFormat = stage.th09_p2_shot.GetName()

        if stage.th128_motivation is None:
            stage.th128_motivation = ""
        else:
            stage.th128_motivation = f"{stage.th128_motivation//100}%"

        if stage.th128_perfect_freeze is None:
            stage.th128_perfect_freeze = ""
        else:
            stage.th128_perfect_freeze = f"{stage.th128_perfect_freeze//100}%"

        if stage.th128_frozen_area is None:
            stage.th128_frozen_area = ""
        else:
            stage.th128_frozen_area = f"{int(stage.th128_frozen_area)}%"

        if stage.stage is None:
            stage.stage = ""
        if stage.score is None:
            stage.score = ""
        if stage.piv is None:
            stage.piv = ""
        if stage.graze is None:
            stage.graze = ""
        if stage.point_items is None:
            stage.point_items = ""
        if stage.power is None:
            stage.power = ""
        if stage.lives is None:
            stage.lives = ""
        if stage.bombs is None:
            stage.bombs = ""
        if stage.th06_rank is None:
            stage.th06_rank = ""
        if stage.th07_cherry is None:
            stage.th07_cherry = ""
        if stage.th07_cherrymax is None:
            stage.th07_cherrymax = ""
        if stage.th09_p1_cpu is None:
            stage.th09_p1_cpu = ""
        if stage.th09_p2_cpu is None:
            stage.th09_p2_cpu = ""
        if stage.th09_p2_score is None:
            stage.th09_p2_score = ""
        if stage.th13_trance is None:
            stage.th13_trance = ""
        if stage.th16_season_power is None:
            stage.th16_season_power = ""
        if stage.extends is None:
            stage.extends = ""

    return new_stages

_games_with_pvp = ["th03", "th09"]

def game_has_pvp(game_id: str) -> bool:
    return game_id in _games_with_pvp


"""Credit Calculator Project"""

# Libraries
import math
import argparse

class CreditCalculator:
    def __init__(self):
        """Initialize the class"""
        self.payment: float = 0
        self.principal: float = 0
        self.interest: float = 0
        self.overpayment: float = 0
        self.periods = 0

    def annuity_mon_pay(self):
        """Calculate annuity monthly payment"""
        i = self.interest / (12 * 100)
        a = math.ceil(self.principal * (i * pow(1 + i, self.periods)) / (pow(1 + i, self.periods) - 1))
        print(f'Your annuity payment = {a}!')
        self.overpayment = a * self.periods - self.principal
        print(f'Overpayment = {self.overpayment}')

    def diff_pay(self):
        """Calculate differentiated monthly payment"""
        i = self.interest / (12 * 100)
        for m in range(1, self.periods + 1):
            d = math.ceil(self.principal / self.periods + i *
                          (self.principal - (self.principal * (m - 1)) / self.periods))
            print(f'Month {m}: paid out {d}')
            self.overpayment += d
        print(f'Overpayment = {self.overpayment - self.principal}')

    def num_mon_pay(self):
        """Calculate the number of months to repay the credit"""
        i = self.interest / (12 * 100)
        n = math.ceil(math.log(self.payment / (self.payment - i * self.principal), 1 + i))
        years = n // 12
        months = n % 12
        self.overpayment = self.payment * n - self.principal
        if years == 0:
            print(f'You need {months} months to repay this credit!')
        elif months == 0:
            print(f'You need {years} years to repay this credit!')
        else:
            print(f'You need {years} years and {months} months to repay this credit!')
        print(f'Overpayment = {self.overpayment}')

    def loan_principal(self):
        """Calculate the loan principal"""
        i = self.interest / (12 * 100)
        p = math.floor(self.payment / ((i * pow(1 + i, self.periods)) / (pow(1 + i, self.periods) - 1)))
        print(f'Your loan principal = {p}!')
        self.overpayment = self.payment * self.periods - p
        print(f'Overpayment = {self.overpayment}')

    def parser(self):
        """Parse the arguments and call the corresponding method"""
        if args.type == 'annuity':
            if args.payment is not None and args.principal is not None and args.interest is not None:
                self.payment = args.payment
                self.principal = args.principal
                self.interest = args.interest
                self.num_mon_pay()
            elif args.payment is not None and args.periods is not None and args.interest is not None:
                self.payment = args.payment
                self.periods = args.periods
                self.interest = args.interest
                self.loan_principal()
            elif args.principal is not None and args.periods is not None and args.interest is not None:
                self.principal = args.principal
                self.periods = args.periods
                self.interest = args.interest
                self.annuity_mon_pay()
            else:
                print('Incorrect parameters')
        elif args.type == 'diff':
            if args.type is None or args.interest is None or args.principal is None \
                    or args.periods is None:
                print('Incorrect parameters')
            elif args.principal and args.periods and args.interest:
                self.principal = args.principal
                self.periods = args.periods
                self.interest = args.interest
                self.diff_pay()
            else:
                print('Incorrect parameters')
        else:
            print('Incorrect parameters')

# Main
if __name__ == '__main__':
    credit_calc = CreditCalculator()
    parser = argparse.ArgumentParser()
    # Add the arguments to the parser
    parser.add_argument('--type', type=str, help='type of payment')
    parser.add_argument('--payment', type=float, help='monthly payment')
    parser.add_argument('--principal', type=float, help='credit principal')
    parser.add_argument('--periods', type=int, help='the number of payment periods')
    parser.add_argument('--interest', type=float, help='the loan interest rate')
    args = parser.parse_args()
    credit_calc.parser()


# coding=utf-8

import threading
import logging
import argparse
import sys
import atexit
import mimetypes
import pkg_resources
import gettext

from . import config, utils
from . import coordinator
from .__version__ import __version__
from .channel import EFBChannel
from .middleware import EFBMiddleware

# gettext.install('ehforwarderbot', 'locale')
coordinator.translator = gettext.translation('ehforwarderbot',
                                             pkg_resources.resource_filename('ehforwarderbot', 'locale'),
                                             fallback=True)

_ = coordinator.translator.gettext
ngettext = coordinator.translator.ngettext

if sys.version_info < (3, 6):
    raise Exception(_("Python 3.6 or higher is required. Your version is {version}.").format(version=sys.version))

description = _("EH Forwarder Bot is an extensible chat tunnel framework which allows "
                "users to contact people from other chat platforms, and ultimately "
                "remotely control their accounts in other platforms.")

parser = argparse.ArgumentParser(description=description,
                                 epilog="GitHub: https://github.com/blueset/ehForwarderBot")

parser.add_argument("-v", '--verbose', action='store_true',
                    help=_("Enable verbose mode. (Level: Debug)"))
parser.add_argument("-V", "--version", action='store_true',
                    help=_("Show version numbers and exit."))
parser.add_argument("-p", "--profile",
                    help=_("Choose a profile to start with."),
                    default="default")

def stop_gracefully():
    logger = logging.getLogger(__name__)
    if isinstance(coordinator.master, EFBChannel):
        coordinator.master.stop_polling()
        logger.debug("Stop signal sent to master: %s" % coordinator.master.channel_name)
    for i in coordinator.slaves:
        if isinstance(coordinator.slaves[i], EFBChannel):
            coordinator.slaves[i].stop_polling()
            logger.debug("Stop signal sent to slave: %s" % coordinator.slaves[i].channel_name)
    if coordinator.master_thread and coordinator.master_thread.is_alive():
        coordinator.master_thread.join()
    for i in coordinator.slave_threads.values():
        if i.is_alive():
            i.join()

def init():
    """
    Initialize all channels.
    """

    logger = logging.getLogger(__name__)

    # Initialize mimetypes library
    mimetypes.init([pkg_resources.resource_filename('ehforwarderbot', 'mimetypes')])

    # Initialize all channels
    # (Load libraries and modules and init them with Queue `q`)

    conf = config.load_config()

    for i in conf['slave_channels']:
        logger.log(99, "\x1b[0;37;46m %s \x1b[0m", _("Initializing slave {}...").format(i))

        cls = utils.locate_module(i, 'slave')
        instance_id = i.split('#', 1)[1:]
        instance_id = (instance_id and instance_id[0]) or None
        coordinator.add_channel(cls(instance_id=instance_id))

        logger.log(99, "\x1b[0;37;42m %s \x1b[0m",
                   _("Slave channel {name} ({id}) # {instance_id} is initialized.")
                   .format(name=cls.channel_name, id=cls.channel_id,
                           instance_id=instance_id))

    logger.log(99, "\x1b[0;37;46m %s \x1b[0m",
               _("Initializing master {}...").format(conf['master_channel']))
    instance_id = conf['master_channel'].split('#', 1)[1:]
    instance_id = (instance_id and instance_id[0]) or None
    coordinator.add_channel(utils.locate_module(conf['master_channel'], 'master')
                            (instance_id=instance_id))
    logger.log(99, "\x1b[0;37;42m %s \x1b[0m",
               _("Master channel {name} ({id}) # {instance_id} is initialized.")
               .format(name=coordinator.master.channel_name,
                       id=coordinator.master.channel_id,
                       instance_id=instance_id))

    logger.log(99, "\x1b[1;37;42m %s \x1b[0m", _("All channels initialized."))
    for i in conf['middlewares']:
        logger.log(99, "\x1b[0;37;46m %s \x1b[0m", _("Initializing middleware {}...").format(i))
        cls = utils.locate_module(i, 'middleware')

        instance_id = i.split('#', 1)[1:]
        instance_id = (instance_id and instance_id[0]) or None
        coordinator.add_middleware(cls(instance_id=instance_id))
        logger.log(99, "\x1b[0;37;42m %s \x1b[0m",
                   _("Middleware {name} ({id}) # {instance_id} is initialized.")
                   .format(name=cls.middleware_name, id=cls.middleware_id,
                           instance_id=instance_id))

    logger.log(99, "\x1b[1;37;42m %s \x1b[0m", _("All middlewares are initialized."))

    coordinator.master_thread = threading.Thread(target=coordinator.master.poll)
    coordinator.slave_threads = {key: threading.Thread(target=coordinator.slaves[key].poll)
                                 for key in coordinator.slaves}

def poll():
    """
    Start threads for polling
    """
    coordinator.master_thread.start()
    for i in coordinator.slave_threads:
        coordinator.slave_threads[i].start()

def main():
    args = parser.parse_args()

    if getattr(args, "version", None):
        versions = _("EH Forwarder Bot\n"
                     "Version: {version}\n"
                     "Python version:\n"
                     "{py_version}").format(version=__version__, py_version=sys.version)
        try:
            conf = config.load_config()
            # Master channel
            master_channel: EFBChannel = utils.locate_module(conf['master_channel'], 'master')
            instance_id = conf['master_channel'].split('#', 1)[1:]
            instance_id = (instance_id and instance_id[0]) or _("Default profile")
            versions += "\n\n" + _("Master channel:") + "\n    " + _("{name} ({id}) {version} # {instance_id}") \
                .format(name=master_channel.channel_name,
                        id=master_channel.channel_id,
                        version=master_channel.__version__,
                        instance_id=instance_id)
            versions += "\n\n" + ngettext("Slave channel:", "Slave channels:", len(conf['slave_channels']))
            for i in conf['slave_channels']:
                instance_id = i.split('#', 1)[1:]
                instance_id = (instance_id and instance_id[0]) or _("Default profile")
                slave_channel: EFBChannel = utils.locate_module(i, 'slave')
                versions += "\n    " + _("{name} ({id}) {version} # {instance_id}") \
                            .format(name=slave_channel.channel_name,
                                    id=slave_channel.channel_id,
                                    version=slave_channel.__version__,
                                    instance_id=instance_id)
            versions += "\n\n" + ngettext("Middleware:", "Middlewares:", len(conf['middlewares']))
            if conf['middlewares']:
                for i in conf['middlewares']:
                    instance_id = i.split('#', 1)[1:]
                    instance_id = (instance_id and instance_id[0]) or _("Default profile")
                    middleware: EFBMiddleware = utils.locate_module(i, 'middleware')
                    versions += "\n    " + _("{name} ({id}) {version} # {instance_id}") \
                                .format(name=middleware.middleware_name,
                                        id=middleware.middleware_name,
                                        version=middleware.__version__,
                                        instance_id=instance_id)
            else:
                versions += "\n    " + _("No middleware is enabled.")
        finally:
            print(versions)
    else:
        if getattr(args, "verbose", None):
            level = logging.DEBUG
        else:
            level = logging.ERROR
        logging.basicConfig(format='%(asctime)s [%(levelname)s]: %(name)s (%(module)s.%(funcName)s; '
                                   '%(filename)s:%(lineno)d) \n    %(message)s', level=level)
        logging.root.setLevel(level)

        # signal.signal(signal.SIGINT, stop_gracefully)
        # signal.signal(signal.SIGTERM, stop_gracefully)
        atexit.register(stop_gracefully)

        if args.profile:
            coordinator.profile = str(args.profile)

        init()
        poll()

if __name__ == '__main__':
    main()


import torch
import torch.optim as optim
from lib.progressbar import progress_bar

def train_epoch(model, criterion, optimizer, train_loader, device=torch.device('cuda'), dtype=torch.float):
    model.train()
    train_loss = 0

    for batch_idx, (inputs, targets) in enumerate(train_loader):
        inputs, targets = inputs.to(device, dtype), targets.to(device, dtype)
        #print("TARGETS:", targets)
        optimizer.zero_grad()
        outputs = model(inputs)
        #print("OUTPUTS:", outputs)
        loss = criterion(outputs, targets)
        loss.backward()
        optimizer.step()
        #print("LOSS:",loss.item())
        #quit()

        train_loss += loss.item()
        progress_bar(batch_idx, len(train_loader),
                     'Loss: {0:.4e}'.format(train_loss/(batch_idx+1)))    #
        #print('loss: {0: .4e}'.format(train_loss/(batch_idx+1)))

def val_epoch(model, criterion, val_loader, device=torch.device('cuda'), dtype=torch.float):
    model.eval()
    val_loss = 0

    with torch.no_grad():
        for batch_idx, (inputs, targets) in enumerate(val_loader):
            inputs, targets = inputs.to(
                device, dtype), targets.to(device, dtype)
            outputs = model(inputs)
            loss = criterion(outputs, targets)

            val_loss += loss.item()
            progress_bar(batch_idx, len(val_loader),
                         'Val Loss: {0:.4e}'.format(val_loss/(batch_idx+1)))
            #print('loss: {0: .4e}'.format(val_loss/(batch_idx+1)))

def test_epoch(model, test_loader, result_collector, device=torch.device('cuda'), dtype=torch.float):
    model.eval()

    with torch.no_grad():
        for batch_idx, (inputs, targets, extra) in enumerate(test_loader):
            ## test_loader should have a final transformer which returns
            ## `y_gt_mm_not_centered` i.e. straight from dataset
            ## `x_cropped_scaled` i.e. no aug but perform crop based on CoM (from dataset) and scale according
            ## to the model requirements i.e 128x128
            ## `CoM point` <-- this will be used to recover back y_pred_mm_not_centered
            # y_pred_std_centered # in mm but standardised
            # values are i.e. [-1, 1]
            ## need special function forward eval to now also pass through last pca layer ans get back
            ## 1, 63  instead of 1, 30
            outputs = model.forward_eval(inputs.to(device, dtype))

            # result college will convert y ->
            result_collector((inputs, outputs, targets, extra))

            progress_bar(batch_idx, len(test_loader))

def test_epoch_dropout(model, test_loader, result_collector, device=torch.device('cuda'), dtype=torch.float, output_rate=10):
	'''
		use dropouts to produce output_rate outputs for each input and then use the mean as output
		we could additionally do the above and supply multiple outcomes.

		important please use batch_size=1 for data_loader!

		`output_rate` => number of evaluations or outputs per input sample
	'''
	## special case here we will be doing dropout and using the mean value ideally...
	## in this case all dropouts are used, maybe in future we would want to use only one dropout
	model.train()

	with torch.no_grad():
		for batch_idx, (inputs, targets, extra) in enumerate(test_loader):
			assert(targets.shape[0] == 1) # only works currently for batch_size=1
			output_set = torch.cat([model.forward_eval(inputs.to(device, dtype)) for _ in range(output_rate)])

			#print("Set shape: ", output_set.shape)

			## do torch.mean in the right axis
			output_mean = torch.mean(output_set, dim=0, keepdim=True)

			#print("Mean Shape: ", output_mean.shape)
			#quit()

			# result college will convert y ->
			result_collector((inputs, output_mean, targets, extra))

			progress_bar(batch_idx, len(test_loader))


import cv2
import numpy as np
import matplotlib.pyplot as plt

# range color
def detect_chess_board(path):
    hh = 500
    ww = 400
    image = cv2.imread(path)

    # resize image
    image = cv2.resize(image,(hh,ww))

    #1. Tách màu sáng, giữ lại màu đen
    # converting frame(image == BGR) to HSV(hue-saturation-value)
    hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    lower_red = np.array([0, 0, 137])
    upper_red = np.array([255, 255, 255])

    # Morphological Transformations,Opening and Closing
    thresh = cv2.inRange(hsv,lower_red, upper_red)
    kernel = np.ones((5,5),np.uint8)
    mask = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
    result = cv2.bitwise_and(image, image, mask = mask)

    #2. lấy vùng bàn cờ
    contours = cv2.findContours(255 - mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    # print(contours)
    contours = contours[0] if len(contours) == 2 else contours[1]
    mask_0 = np.zeros((ww,hh), dtype=np.uint8)
    for cntr in contours:
        cv2.drawContours(mask_0, [cntr], 0, (255,255,255), -1)
    result = cv2.bitwise_and(image, image, mask=mask_0)

    #3. Tách trắng lấy đen loại bỏ viền bàn cờ
    hsv = cv2.cvtColor(result, cv2.COLOR_BGR2HSV)
    lower_red = np.array([0, 0, 0])
    upper_red = np.array([255, 255, 162])

    # Morphological Transformations,Opening and Closing
    thresh = cv2.inRange(hsv,lower_red, upper_red)
    kernel = np.ones((15,15),np.uint8)

    #4. khử nhiễu
    mask = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel)
    mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel)
    image = np.zeros((ww,hh), dtype=np.uint8)
    image += 255
    result = cv2.bitwise_and(image, image, mask = mask)
    corners = 0
    mask_0 = mask
    #5. Lấy góc ô cờ
    found, corners = cv2.findChessboardCorners(result, (7, 7),flags=cv2.CALIB_CB_FAST_CHECK)
    # print(np.shape(corners))

    # corner_lt = (corners[0]-corners[16])

    img = cv2.imread(path)
    img = cv2.resize(img,(hh,ww))
    img2 = np.copy(img)  # Make a copy of original img as img2

    for corner in corners:
        # print(corner[0])
        coord = (int(corner[0][0]), int(corner[0][1]))
        # print(coord)
        cv2.circle(img=img2, center=coord, radius=2, color=(255, 0, 0), thickness=2)

    corner_lt = np.array([corners[0][0][0]-(corners[8][0][0]-corners[0][0][0])**2/(corners[16][0][0]-corners[8][0][0]),corners[0][0][1]-(corners[8][0][1]-corners[0][0][1])**2/(corners[16][0][1]-corners[8][0][1])])
    corner_rb = np.array([corners[48][0][0]+(corners[48][0][0]-corners[40][0][0])**2/(corners[40][0][0]-corners[32][0][0]),corners[48][0][1]+(corners[48][0][1]-corners[40][0][1])**2/(corners[40][0][1]-corners[32][0][1])])
    corner_lb = np.array([corners[42][0][0]-(corners[36][0][0]-corners[42][0][0])**2/(corners[30][0][0]-corners[36][0][0]),corners[42][0][1]+(corners[42][0][1]-corners[36][0][1])**2/(corners[36][0][1]-corners[30][0][1])])
    corner_rt = np.array([corners[6][0][0]+(corners[6][0][0]-corners[12][0][0])**2/(corners[12][0][0]-corners[18][0][0]),corners[6][0][1]-(corners[12][0][1]-corners[6][0][1])**2/(corners[18][0][1]-corners[12][0][1])])

    mask = np.zeros((ww,hh), dtype=np.uint8)
    mask = cv2.line(mask, (int(corner_lt[0]),int(corner_lt[1])), (int(corner_rt[0]),int(corner_rt[1])), (255,255,255), 2)
    mask = cv2.line(mask, (int(corner_lt[0]),int(corner_lt[1])), (int(corner_lb[0]),int(corner_lb[1])), (255,255,255), 2)
    mask = cv2.line(mask, (int(corner_rb[0]),int(corner_rb[1])), (int(corner_rt[0]),int(corner_rt[1])), (255,255,255), 2)
    mask = cv2.line(mask, (int(corner_rb[0]),int(corner_rb[1])), (int(corner_lb[0]),int(corner_lb[1])), (255,255,255), 2)

    contours = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    contours = contours[0] if len(contours) == 2 else contours[1]
    mask_0 = np.zeros((ww,hh), dtype=np.uint8)
    for cntr in contours:
        cv2.drawContours(mask_0, [cntr], 0, (255,255,255), -1)

    return mask_0, corners

def distance(p1,p2):
    return np.sqrt((p1[0]-p2[0])**2+(p1[1]-p2[1])**2)

def point_near(point, array):
    near = 10000
    near_index = np.array([0,0])
    for i in range(len(array)):
        dt= distance(point, array[i])
        if near > dt:
            near = dt

            near_index = np.array([i//7,i%7])
    print(near_index)
    arr_near = np.zeros((4,2))
    if point[0] > near_index[0] and point[1]> near_index[1]:
        arr_near[0] = np.array(near_index)
        arr_near[1] = np.array([near_index[0],near_index[1]+1])
        arr_near[2] = np.array([near_index[0]+1,near_index[1]+1])
        arr_near[3] = np.array([near_index[0]+1,near_index[1]])
    elif point[0] < near_index[0] and point[1]> near_index[1]:
        arr_near[0] = np.array([near_index[0],near_index[1]-1])
        arr_near[1] = np.array(near_index)
        arr_near[2] = np.array([near_index[0]+1,near_index[1]])
        arr_near[3] = np.array([near_index[0]+1,near_index[1]-1])
    elif point[0] < near_index[0] and point[1]< near_index[1]:
        arr_near[0] = np.array([near_index[0]-1,near_index[1]-1])
        arr_near[1] = np.array([near_index[0]-1,near_index[1]])
        arr_near[2] = np.array(near_index)
        arr_near[3] = np.array([near_index[0],near_index[1]-1])
    elif point[0] > near_index[0] and point[1]< near_index[1]:
        arr_near[0] = np.array([near_index[0]-1,near_index[1]])
        arr_near[1] = np.array([near_index[0]-1,near_index[1]+1])
        arr_near[2] = np.array([near_index[0],near_index[1]+1])
        arr_near[3] = np.array(near_index)
    print(arr_near)
    arr_point = np.array([array[convert_2d_to_1d(arr_near[0].astype(int))],array[convert_2d_to_1d(arr_near[1].astype(int))],array[convert_2d_to_1d(arr_near[2].astype(int))],array[convert_2d_to_1d(arr_near[3].astype(int))]])
    return near, near_index, arr_near, arr_point

def convert_2d_to_1d(a,n=7):
    return a[0]*n+a[1]

# print(convert_2d_to_1d([3,2]))

def get_line_2point(A,B):
    vector_AB = B - A
    a = 0-vector_AB[1]
    b = vector_AB[0]
    c = -a*A[0] - b*A[1]
    return a,b,c

def check_line(A,B,C):
    a,b,c = get_line_2point(A,B)
    if C[0]*a+C[1]*b+c ==0:
        print("True")
    else:
        print("false")

def check_phuong_vecto(a,b):
    if a[0]/b[0] == a[1]/b[1]:
        print("true")
    else:
        print("false")

def giao_diem(a1,b1,c1,a2,b2,c2):
    y = (c2-(a2*c1/a1))/((a2*b1/a1)-b2)
    x = 0-(c1+b1*y)/a1
    return x,y

# a1,b1,c1=get_line_2point(np.array([0,1]),np.array([2,3]))
# a2,b2,c2=get_line_2point(np.array([0,4]),np.array([2,3]))
# print(giao_diem(a1,b1,c1,a2,b2,c2))

def hinhchieu(C,p1,p2,p3):
    a1,b1,c1 = get_line_2point(p1,p2)
    a2,b2,c2 = get_line_2point(C,p3)

    # print(a2,b2,c2,a1,b1,c1)
    x,y = giao_diem(a1,b1,c1,a2,b2,c2)
    G = np.array([x,y])
    H = C+(p2-p3)*(G-C)/(G-p3)
    return H

def ti_le(a1,a2,a3,n=2.75):
    return distance(a1,a2)/distance(a1,a3)*n
# img2 = cv2.bitwise_and(img2, img2, mask=mask_0)

# path = "D:/Study/Xulyanh/code/data/back.jpg"
# mask, corners = detect_chess_board(path)
# image = cv2.imread(path)
# image = cv2.resize(image,(500,400))
# crop_chess_board = cv2.bitwise_and(image, image, mask=mask)
# for corner in corners:
#         # print(corner[0])
#         coord = (int(corner[0][0]), int(corner[0][1]))
#         # print(coord)
#         cv2.circle(img=crop_chess_board, center=coord, radius=2, color=(255, 0, 0), thickness=2)
# cv2.imshow('mask',crop_chess_board);
# cv2.waitKey(0)


import select
import os
import subprocess
from collections import namedtuple
import logging

# project
from .exceptions import (
    AzureHostedCommandException,
    AzureHostedCommandNotFoundException
)

logger = logging.getLogger('Azure_LI_Services')

class Command(object):
    """
    **Implements command invocation**

    An instance of Command provides methods to invoke external
    commands in blocking and non blocking mode. Control of
    stdout and stderr is given to the caller
    """
    @classmethod
    def run(self, command, custom_env=None, raise_on_error=True):
        """
        Execute a program and block the caller. The return value
        is a hash containing the stdout, stderr and return code
        information. Unless raise_on_error is set to false an
        exception is thrown if the command exits with an error
        code not equal to zero

        Example:

        .. code:: python

            result = Command.run(['ls', '-l'])

        :param list command: command and arguments
        :param list custom_env: custom os.environ
        :param bool raise_on_error: control error behaviour

        :return:
            Contains call results in command type

            .. code:: python

                command(output='string', error='string', returncode=int)

        :rtype: namedtuple
        """
        from .path import Path
        command_type = namedtuple(
            'command', ['output', 'error', 'returncode']
        )
        environment = os.environ
        if custom_env:
            environment = custom_env
        if not Path.which(
            command[0], custom_env=environment, access_mode=os.X_OK
        ):
            message = 'Command "%s" not found in the environment' % command[0]
            if not raise_on_error:
                return command_type(
                    output=None,
                    error=None,
                    returncode=-1
                )
            else:
                raise AzureHostedCommandNotFoundException(message)
        try:
            logger.info('Calling: {0}'.format(command))
            process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                env=environment
            )
        except Exception as e:
            raise AzureHostedCommandException(
                '%s: %s: %s' % (command[0], type(e).__name__, format(e))
            )
        output, error = process.communicate()
        if process.returncode != 0 and not error:
            error = bytes(b'(no output on stderr)')
        if process.returncode != 0 and not output:
            output = bytes(b'(no output on stdout)')
        if process.returncode != 0 and raise_on_error:
            raise AzureHostedCommandException(
                '%s: stderr: %s, stdout: %s' % (
                    command[0], error.decode(), output.decode()
                )
            )
        return command_type(
            output=output.decode(),
            error=error.decode(),
            returncode=process.returncode
        )

    @classmethod
    def call(self, command, custom_env=None):
        """
        Execute a program and return an io file handle pair back.
        stdout and stderr are both on different channels. The caller
        must read from the output file handles in order to actually
        run the command. This can be done using the CommandIterator
        from command_process

        Example:

        .. code:: python

            process = Command.call(['ls', '-l'])

        :param list command: command and arguments
        :param list custom_env: custom os.environ

        :return:
            Contains process results in command type

            .. code:: python

                command(
                    output='string', output_available=bool,
                    error='string', error_available=bool,
                    process=subprocess
                )

        :rtype: namedtuple
        """
        from .path import Path
        environment = os.environ
        if custom_env:
            environment = custom_env
        if not Path.which(
            command[0], custom_env=environment, access_mode=os.X_OK
        ):
            raise AzureHostedCommandNotFoundException(
                'Command "%s" not found in the environment' % command[0]
            )
        try:
            logger.info('Calling: {0}'.format(command))
            process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=subprocess.PIPE,
                env=environment
            )
        except Exception as e:
            raise AzureHostedCommandException(
                '%s: %s' % (type(e).__name__, format(e))
            )

        def output_available():
            def _select():
                descriptor_lists = select.select(
                    [process.stdout], [], [process.stdout], 1e-4
                )
                readable = descriptor_lists[0]
                exceptional = descriptor_lists[2]
                if readable and not exceptional:
                    return True
            return _select

        def error_available():
            def _select():
                descriptor_lists = select.select(
                    [process.stderr], [], [process.stderr], 1e-4
                )
                readable = descriptor_lists[0]
                exceptional = descriptor_lists[2]
                if readable and not exceptional:
                    return True
            return _select

        command = namedtuple(
            'command', [
                'output', 'output_available',
                'error', 'error_available',
                'process'
            ]
        )
        return command(
            output=process.stdout,
            output_available=output_available(),
            error=process.stderr,
            error_available=error_available(),
            process=process
        )