#!/home/kelsi/.virtualenvs/mtg/bin/python
# -*- coding: utf-8 -*-

from bottle import route, run, template, view
from datetime import date

import sqlite3

con = sqlite3.connect('mtg.db')
con.row_factory = sqlite3.Row
cursor = con.cursor()

@route('/hello/<name>')
@view('sample')
def hello(name):
    return dict(name=name)

@route('/index')
@view('index')
def index():
    return dict(date=date.today())

@route('/tournaments/<date>')
@view('tournament_index')
def tournament_index(date):

    import datetime

    pretty_date = datetime.date.fromtimestamp(float(date))
    #pretty_date = pretty_date.strftime('%B %d, %Y')

    cursor.execute('''select * from tourneys where date=?''', (date,))
    return dict(date=pretty_date.strftime('%B %d, %Y'), cursor=cursor)

@route('/events/<eventid>')
@view('event_index')
def event_index(eventid):

    import datetime

    # Get generic tournament info
    cursor.execute('''select * from tourneys where id=?''', (eventid,))
    tourney_info = cursor.fetchone()

    date = datetime.date.fromtimestamp(float(tourney_info['date']))

    # Eventually want to show results, bracket
    # For now, participants and their basic win/losses and deck

    cursor.execute('''select participants.*, users.name, users.id as userid from participants left join users on participants.user_id = users.id where participants.tourney_id=? order by participants.rank''', (eventid,))
    return dict(eventid=eventid, cursor=cursor, date=date.strftime('%d/%m/%y'), info=tourney_info)

@route('/users/<userid>')
@view('user_index')
def user_index(eventid):
    # Want a view of users
    # With links to tourneys played in and decks used

    return dict(eventid=eventid, cursor=cursor, date=date.strftime('%d/%m/%y'), info=tourney_info)

@route('/cards/<cardid>')
@view('card_stats')
def user_index(cardid):
    # Want a view of individual cards
    # Tracking usage over time
    # Want to be able to visualize multiple cards on same graph
    # To compare usage...

    cursor.execute('''select count(*) as decks, sum(quantity) as total from decklists where card=?''', (cardid,))
    stats = cursor.fetchone()

    return dict(cardid=cardid, stats=stats)

# Want a view of decks
# Need to be able to identify decks first!
# Want to be able to see deck usage over time
# Want to be able to compare to other decks over same time period

run(host='localhost', port=8000)
con.close()


import pyfiglet
import platform
import os
import sys
import socket
import threading
from time import sleep
from queue import Queue

def banner():
    banner = pyfiglet.figlet_format('PORT SCANNER')
    print('\033[93m'+banner+'\033[m')

ports = Queue()

first_port = 1
last_port = 65535

for i in range(first_port, last_port+1):
    ports.put(i)

def clear_screen():
    if platform.system() == 'Windows':
        os.system('cls')
    elif platform.system() == 'Linux':
        os.system('clear')

def validate_ip(ip):

    splited_ip = ip.split('.')

    if len(splited_ip) == 4:
        for ip_part in splited_ip:
            if len(ip_part) <= 3 and len(ip_part) > 0:
                return True

            else:
                return False

    else:
        return False

def scan(port):
    try:
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        socket.setdefaulttimeout(1)
        res = sock.connect_ex((target, port))

        if res == 0:
            print(f'Port {port} is open!')

        sock.close()
    except:
        pass

def worker():
    while not ports.empty():
        port = ports.get()
        scan(port)
    ports.task_done()

def start_workers():
    threads = []

    for i in range(500):
        thread = threading.Thread(target=worker, daemon=True)
        thread.start()
        threads.append(thread)

    for thread in threads:
        thread.join()

def start_scanning():
    clear_screen()
    banner()
    sleep(1)
    print(f'Scanning the ports from {first_port} to {last_port}...\n')
    start_workers()

banner()

try:
    target = sys.argv[1]
except IndexError:
    raise ValueError('\033[91mYou need to pass an IP address as an argument! e.g.'+
                     'python portscanner.py an.ip.address.here\033[m')

print('\033[93m'+
      '--------------------Welcome to the port scanner!--------------------'+
      '\033[m')

print(f'\n\nThis program will scan now the ports of the address {target}')

while True:
    ip_is_okay = input('Is this okay? [Y/N]')

    if ip_is_okay.strip().upper() == 'Y':
        break

    elif ip_is_okay.strip().upper() == 'N':

        while True:
            new_ip = input('Enter a new IP: ')
            if validate_ip(new_ip):
                target = new_ip
                print('\033[34mYou succefully changed the target IP adress.\033[m')
                sleep(1.5)
                break

            else:
                print('''\033[91mERROR! The IP you writed is invalid. Please write
an valid ip address, in the format: 000.000.000.000\033[m \n''')
                sleep(2)
                continue

        break

    else:
        continue

start_scanning()

print('\n\033[93mAll the ports were scanned. Goodbye!\033[m')


"""
The goal of this program is to optimize the movement to achieve a rudi out pike (803<) for left twisters.
"""
import os
import numpy as np
import biorbd_casadi as biorbd
from casadi import MX, Function
from bioptim import (
            OptimalControlProgram,
            DynamicsList,
            DynamicsFcn,
            ObjectiveList,
            ObjectiveFcn,
            BoundsList,
            InitialGuessList,
            InterpolationType,
            OdeSolver,
            Node,
            Solver,
            BiMappingList,
            CostType,
            ConstraintList,
            ConstraintFcn,
            PenaltyController,
            MultiStart,
            Solution,
            MagnitudeType,
            BiorbdModel,
        )
import time
import pickle

class Model:
    """
    Attributes
    ----------
    model: str
        A reference to the name of the model
    with_hsl  :
        no hsl, don't use libhsl
    n_threads : int
        refers to the numbers of threads in the solver
    savesol :
        returns true if empty, else returns False
    show_online : bool
        returns true if empty, else returns False
    print_ocp : bool
        returns False if empty, else returns True """

    def __init__(self, model, n_threads=5, with_hsl=False, savesol=False, show_online=False, print_ocp=False):

        self.model = model
        self.with_hsl = with_hsl
        self.n_threads = n_threads
        self.savesol = savesol
        self.show_online = show_online
        self.print_ocp = print_ocp

        #
        # # if savesol :
        # #    return False
        #
        # if show_online:
        #     return False
        #
        # if print_ocp:
        #     return True

        #   parser = argparse.ArgumentParser()
        # parser.add_argument("model", type=str, help="the bioMod file")
        # parser.add_argument("--no-hsl", dest='with_hsl', action='store_false', help="do not use libhsl")
        # parser.add_argument("-j", default=1, dest='n_threads', type=int, help="number of threads in the solver")
        # parser.add_argument("--no-sol", action='store_false', dest='savesol', help="do not save the solution")
        # parser.add_argument("--no-show-online", action='store_false', dest='show_online', help="do not show graphs during optimization")
        # parser.add_argument("--print-ocp", action='store_true', dest='print_ocp', help="print the ocp")
        # args = parser.parse_args()
        #

try:
    import IPython

    IPYTHON = True
except ImportError:
    print("No IPython.")
    IPYTHON = False

def minimize_dofs(controller: PenaltyController, dofs: list, targets: list):
    diff = 0
    for i, dof in enumerate(dofs):
        diff += (controller.states['q'].cx_start[dof] - targets[i]) ** 2
    return diff

def prepare_ocp(
        biorbd_model_path: str, nb_twist: int, seed : int,
        ode_solver: OdeSolver = OdeSolver.RK4(),
) -> OptimalControlProgram:

    """
    Prepare the ocp

    Parameters
    ----------
    biorbd_model_path: str
        The path to the bioMod file
    ode_solver: OdeSolver
        The ode solver to use

    Returns
    -------
    The OptimalControlProgram ready to be solved
    """
    final_time = 1.87
    n_shooting = (40, 100, 100, 100, 40)

    biomodel = (BiorbdModel(biorbd_model_path))
    biorbd_model = (biomodel,biomodel, biomodel, biomodel,biomodel)

    nb_q = biorbd_model[0].nb_q
    nb_qdot = biorbd_model[0].nb_qdot
    nb_qddot_joints = nb_q - biorbd_model[0].nb_root

    # Pour la lisibilite
    X = 0
    Y = 1
    Z = 2
    Xrot = 3
    Yrot = 4
    Zrot = 5
    ZrotBD = 6
    YrotBD = 7
    ZrotABD = 8
    XrotABD = 9
    ZrotBG = 10
    YrotBG = 11
    ZrotABG = 12
    XrotABG = 13
    XrotC = 14
    YrotC = 15
    vX = 0
    vY = 1
    vZ = 2
    vXrot = 3
    vYrot = 4
    vZrot = 5
    vZrotBD = 6
    vYrotBD = 7
    vZrotABD = 8
    vYrotABD = 9
    vZrotBG = 10
    vYrotBG = 11
    vZrotABG = 12
    vYrotABG = 13
    vXrotC = 14
    vYrotC = 15

    # Add objective functions
    objective_functions = ObjectiveList()
    # objective_functions.add(ObjectiveFcn.Mayer.MINIMIZE_MARKERS, marker_index=1, weight=-1)
    objective_functions.add(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="qddot_joints", node=Node.ALL_SHOOTING,
                            weight=1, phase=0)
    objective_functions.add(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="qddot_joints", node=Node.ALL_SHOOTING,
                            weight=1, phase=1)
    objective_functions.add(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="qddot_joints", node=Node.ALL_SHOOTING,
                            weight=1, phase=2)
    objective_functions.add(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="qddot_joints", node=Node.ALL_SHOOTING,
                            weight=1, phase=3)
    objective_functions.add(ObjectiveFcn.Lagrange.MINIMIZE_CONTROL, key="qddot_joints", node=Node.ALL_SHOOTING,
                            weight=1, phase=4)

    objective_functions.add(ObjectiveFcn.Mayer.MINIMIZE_TIME, min_bound=.0, max_bound=1.0, weight=100000,
                            phase=0)

    objective_functions.add(ObjectiveFcn.Mayer.MINIMIZE_TIME, min_bound=.0, max_bound=1.0, weight=100000,
                            phase=2)

    objective_functions.add(ObjectiveFcn.Mayer.SUPERIMPOSE_MARKERS, node=Node.END, first_marker='MidMainG',
                            second_marker='CibleMainG', weight=1000, phase=0)
    objective_functions.add(ObjectiveFcn.Mayer.SUPERIMPOSE_MARKERS, node=Node.END, first_marker='MidMainD',
                            second_marker='CibleMainD', weight=1000, phase=0)

    # arrete de gigoter les bras
    les_bras = [ZrotBD, YrotBD, ZrotABD, XrotABD, ZrotBG, YrotBG, ZrotABG, XrotABG]
    les_coudes = [ZrotABD, XrotABD, ZrotABG, XrotABG]
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Lagrange, node=Node.ALL_SHOOTING,
                            dofs=les_coudes, targets=np.zeros(len(les_coudes)), weight=1000, phase=0)
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Lagrange, node=Node.ALL_SHOOTING,
                            dofs=les_bras, targets=np.zeros(len(les_bras)), weight=10, phase=0)
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Lagrange, node=Node.ALL_SHOOTING,
                            dofs=les_bras, targets=np.zeros(len(les_bras)), weight=10, phase=1)
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Lagrange, node=Node.ALL_SHOOTING,
                            dofs=les_bras, targets=np.zeros(len(les_bras)), weight=10, phase=2)
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Lagrange, node=Node.ALL_SHOOTING,
                            dofs=les_bras, targets=np.zeros(len(les_bras)), weight=10, phase=3)
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Lagrange, node=Node.ALL_SHOOTING,
                            dofs=les_bras, targets=np.zeros(len(les_bras)), weight=10, phase=4)
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Lagrange, node=Node.ALL_SHOOTING,
                            dofs=les_coudes, targets=np.zeros(len(les_coudes)), weight=1000, phase=4)

    # ouvre les hanches rapidement apres la vrille
    objective_functions.add(minimize_dofs, custom_type=ObjectiveFcn.Mayer, node=Node.END, dofs=[XrotC],
                            targets=[0], weight=10000, phase=3)

    # Dynamics
    dynamics = DynamicsList()
    dynamics.add(DynamicsFcn.JOINTS_ACCELERATION_DRIVEN)
    dynamics.add(DynamicsFcn.JOINTS_ACCELERATION_DRIVEN)
    dynamics.add(DynamicsFcn.JOINTS_ACCELERATION_DRIVEN)
    dynamics.add(DynamicsFcn.JOINTS_ACCELERATION_DRIVEN)
    dynamics.add(DynamicsFcn.JOINTS_ACCELERATION_DRIVEN)

    qddot_joints_min, qddot_joints_max, qddot_joints_init = -500, 500, 0
    u_bounds = BoundsList()
    for i in range(5):
        u_bounds.add("qddot_joints", min_bound=[qddot_joints_min] * nb_qddot_joints, max_bound=[qddot_joints_max] * nb_qddot_joints, phase=i)

    u_init = InitialGuessList()
    for i in range(5):
        u_init.add("qddot_joints", [qddot_joints_init] * nb_qddot_joints, phase=i)
        u_init[i]["qddot_joints"].add_noise(
            bounds=u_bounds[i]["qddot_joints"],
            magnitude=0.2,
            magnitude_type=MagnitudeType.RELATIVE,
            n_shooting=n_shooting[i],
            seed=seed,
        )

    # Path constraint
    x_bounds = BoundsList()
    for i in range(5):
        x_bounds.add("q", min_bound=biorbd_model[0].bounds_from_ranges("q").min, max_bound=biorbd_model[0].bounds_from_ranges("q").max, phase=i)
        x_bounds.add("qdot", min_bound=biorbd_model[0].bounds_from_ranges("qdot").min, max_bound=biorbd_model[0].bounds_from_ranges("qdot").max, phase=i)

    # Pour la lisibilite
    DEBUT, MILIEU, FIN = 0, 1, 2

    #
    # Contraintes de position: PHASE 0 la montee en carpe
    #

    zmax = 8
    # 12 / 8 * final_time**2 + 1  # une petite marge

    # deplacement
    x_bounds[0]["q"].min[X, :] = -.1
    x_bounds[0]["q"].max[X, :] = .1
    x_bounds[0]["q"].min[Y, :] = -1.
    x_bounds[0]["q"].max[Y, :] = 1.
    x_bounds[0]["q"].min[:Z + 1, DEBUT] = 0
    x_bounds[0]["q"].max[:Z + 1, DEBUT] = 0
    x_bounds[0]["q"].min[Z, MILIEU:] = 0
    x_bounds[0]["q"].max[Z, MILIEU:] = zmax  # beaucoup plus que necessaire, juste pour que la parabole fonctionne

    # le salto autour de x
    x_bounds[0]["q"].min[Xrot, :] = 0
    # 2 * 3.14 + 3 / 2 * 3.14 - .2
    x_bounds[0]["q"].max[Xrot, :] = -.50 + 3.14
    x_bounds[0]["q"].min[Xrot, DEBUT] = .50  # penche vers l'avant un peu carpe
    x_bounds[0]["q"].max[Xrot, DEBUT] = .50
    x_bounds[0]["q"].min[Xrot, MILIEU:] = 0
    x_bounds[0]["q"].max[Xrot, MILIEU:] = 4 * 3.14 + .1  # salto
    # limitation du tilt autour de y
    x_bounds[0]["q"].min[Yrot, DEBUT] = 0
    x_bounds[0]["q"].max[Yrot, DEBUT] = 0
    x_bounds[0]["q"].min[Yrot, MILIEU:] = - 3.14 / 16  # vraiment pas suppose tilte
    x_bounds[0]["q"].max[Yrot, MILIEU:] = 3.14 / 16
    # la vrille autour de z
    x_bounds[0]["q"].min[Zrot, DEBUT] = 0
    x_bounds[0]["q"].max[Zrot, DEBUT] = 0
    x_bounds[0]["q"].min[Zrot, MILIEU:] = -.1  # pas de vrille dans cette phase
    x_bounds[0]["q"].max[Zrot, MILIEU:] = .1

    # bras droit
    x_bounds[0]["q"].min[YrotBD, DEBUT] = 2.9  # debut bras aux oreilles
    x_bounds[0]["q"].max[YrotBD, DEBUT] = 2.9
    x_bounds[0]["q"].min[ZrotBD, DEBUT] = 0
    x_bounds[0]["q"].max[ZrotBD, DEBUT] = 0
    # bras gauche
    x_bounds[0]["q"].min[YrotBG, DEBUT] = -2.9  # debut bras aux oreilles
    x_bounds[0]["q"].max[YrotBG, DEBUT] = -2.9
    x_bounds[0]["q"].min[ZrotBG, DEBUT] = 0
    x_bounds[0]["q"].max[ZrotBG, DEBUT] = 0

    # coude droit
    x_bounds[0]["q"].min[ZrotABD:XrotABD + 1, DEBUT] = 0
    x_bounds[0]["q"].max[ZrotABD:XrotABD + 1, DEBUT] = 0
    # coude gauche
    x_bounds[0]["q"].min[ZrotABG:XrotABG + 1, DEBUT] = 0
    x_bounds[0]["q"].max[ZrotABG:XrotABG + 1, DEBUT] = 0

    # le carpe
    x_bounds[0]["q"].min[XrotC, DEBUT] = -.50  # depart un peu ferme aux hanches
    x_bounds[0]["q"].max[XrotC, DEBUT] = -.50
    x_bounds[0]["q"].max[XrotC, FIN] = -2.5
    # x_bounds[0].min[XrotC, FIN] = 2.7  # min du modele
    # le dehanchement
    x_bounds[0]["q"].min[YrotC, DEBUT] = 0
    x_bounds[0]["q"].max[YrotC, DEBUT] = 0
    x_bounds[0]["q"].min[YrotC, MILIEU:] = -.1
    x_bounds[0]["q"].max[YrotC, MILIEU:] = .1

    # Contraintes de vitesse: PHASE 0 la montee en carpe

    vzinit = 9.81 / (2 * final_time ) # vitesse initiale en z du CoM pour revenir a terre au temps final

    # decalage entre le bassin et le CoM
    CoM_Q_sym = MX.sym('CoM', nb_q)
    CoM_Q_init = x_bounds[0]["q"].min[:nb_q,
                 DEBUT]  # min ou max ne change rien a priori, au DEBUT ils sont egaux normalement
    CoM_Q_func = Function('CoM_Q_func', [CoM_Q_sym], [biorbd_model[0].center_of_mass(CoM_Q_sym)])
    bassin_Q_func = Function('bassin_Q_func', [CoM_Q_sym],
                             [biorbd_model[0].homogeneous_matrices_in_global(CoM_Q_sym, 0).to_mx()])  # retourne la RT du bassin

    r = np.array(CoM_Q_func(CoM_Q_init)).reshape(1, 3) - np.array(bassin_Q_func(CoM_Q_init))[-1,
                                                         :3]  # selectionne seulement la translation de la RT

    # en xy bassin
    x_bounds[0]["qdot"].min[vX:vY + 1, :] = -10
    x_bounds[0]["qdot"].max[vX:vY + 1, :] = 10
    x_bounds[0]["qdot"].min[vX:vY + 1, DEBUT] = -.5
    x_bounds[0]["qdot"].max[vX:vY + 1, DEBUT] = .5
    # z bassin
    x_bounds[0]["qdot"].min[vZ, :] = -50
    x_bounds[0]["qdot"].max[vZ, :] = 50
    x_bounds[0]["qdot"].min[vZ, DEBUT] = vzinit - .5
    x_bounds[0]["qdot"].max[vZ, DEBUT] = vzinit + .5

    # autour de x
    x_bounds[0]["qdot"].min[vXrot, :] = .5  # d'apres une observation video
    x_bounds[0]["qdot"].max[vXrot, :] = 20  # aussi vite que nécessaire, mais ne devrait pas atteindre cette vitesse
    # autour de y
    x_bounds[0]["qdot"].min[vYrot, :] = -50
    x_bounds[0]["qdot"].max[vYrot, :] = 50
    x_bounds[0]["qdot"].min[vYrot, DEBUT] = 0
    x_bounds[0]["qdot"].max[vYrot, DEBUT] = 0
    # autour de z
    x_bounds[0]["qdot"].min[vZrot, :] = -50
    x_bounds[0]["qdot"].max[vZrot, :] = 50
    x_bounds[0]["qdot"].min[vZrot, DEBUT] = 0
    x_bounds[0]["qdot"].max[vZrot, DEBUT] = 0

    # tenir compte du decalage entre bassin et CoM avec la rotation
    # Qtransdot = Qtransdot + v cross Qrotdot
    borne_inf = (x_bounds[0]["qdot"].min[vX:vZ + 1, DEBUT] + np.cross(r, x_bounds[0]["qdot"].min[vXrot:vZrot + 1, DEBUT]))[0]
    borne_sup = (x_bounds[0]["qdot"].max[vX:vZ + 1, DEBUT] + np.cross(r, x_bounds[0]["qdot"].max[vXrot:vZrot + 1, DEBUT]))[0]
    x_bounds[0]["qdot"].min[vX:vZ + 1, DEBUT] = min(borne_sup[0], borne_inf[0]), min(borne_sup[1], borne_inf[1]), min(
        borne_sup[2], borne_inf[2])
    x_bounds[0]["qdot"].max[vX:vZ + 1, DEBUT] = max(borne_sup[0], borne_inf[0]), max(borne_sup[1], borne_inf[1]), max(
        borne_sup[2], borne_inf[2])

    # bras droit
    x_bounds[0]["qdot"].min[vZrotBD:vYrotBD + 1, :] = -50
    x_bounds[0]["qdot"].max[vZrotBD:vYrotBD + 1, :] = 50
    x_bounds[0]["qdot"].min[vZrotBD:vYrotBD + 1, DEBUT] = 0
    x_bounds[0]["qdot"].max[vZrotBD:vYrotBD + 1, DEBUT] = 0
    # bras droit
    x_bounds[0]["qdot"].min[vZrotBG:vYrotBG + 1, :] = -50
    x_bounds[0]["qdot"].max[vZrotBG:vYrotBG + 1, :] = 50
    x_bounds[0]["qdot"].min[vZrotBG:vYrotBG + 1, DEBUT] = 0
    x_bounds[0]["qdot"].max[vZrotBG:vYrotBG + 1, DEBUT] = 0

    # coude droit
    x_bounds[0]["qdot"].min[vZrotABD:vYrotABD + 1, :] = -50
    x_bounds[0]["qdot"].max[vZrotABD:vYrotABD + 1, :] = 50
    x_bounds[0]["qdot"].min[vZrotABD:vYrotABD + 1, DEBUT] = 0
    x_bounds[0]["qdot"].max[vZrotABD:vYrotABD + 1, DEBUT] = 0
    # coude gauche
    x_bounds[0]["qdot"].min[vZrotABD:vYrotABG + 1, :] = -50
    x_bounds[0]["qdot"].max[vZrotABD:vYrotABG + 1, :] = 50
    x_bounds[0]["qdot"].min[vZrotABG:vYrotABG + 1, DEBUT] = 0
    x_bounds[0]["qdot"].max[vZrotABG:vYrotABG + 1, DEBUT] = 0

    # du carpe
    x_bounds[0]["qdot"].min[vXrotC, :] = -50
    x_bounds[0]["qdot"].max[vXrotC, :] = 50
    x_bounds[0]["qdot"].min[vXrotC, DEBUT] = 0
    x_bounds[0]["qdot"].max[vXrotC, DEBUT] = 0
    # du dehanchement
    x_bounds[0]["qdot"].min[vYrotC, :] = -50
    x_bounds[0]["qdot"].max[vYrotC, :] = 50
    x_bounds[0]["qdot"].min[vYrotC, DEBUT] = 0
    x_bounds[0]["qdot"].max[vYrotC, DEBUT] = 0

    #
    # Contraintes de position: PHASE 1 le salto carpe
    #

    # deplacement
    x_bounds[1]["q"].min[X, :] = -.1
    x_bounds[1]["q"].max[X, :] = .1
    x_bounds[1]["q"].min[Y, :] = -1.
    x_bounds[1]["q"].max[Y, :] = 1.
    x_bounds[1]["q"].min[Z, :] = 0
    x_bounds[1]["q"].max[Z, :] = zmax  # beaucoup plus que necessaire, juste pour que la parabole fonctionne

    # le salto autour de x
    x_bounds[1]["q"].min[Xrot, :] = 0
    x_bounds[1]["q"].max[Xrot, :] = -.50 + 4 * 3.14
    x_bounds[1]["q"].min[Xrot, FIN] = 2 * 3.14 - .1
    # limitation du tilt autour de y
    x_bounds[1]["q"].min[Yrot, :] = - 3.14 / 16
    x_bounds[1]["q"].max[Yrot, :] = 3.14 / 16
    # la vrille autour de z
    x_bounds[1]["q"].min[Zrot, :] = -.1
    x_bounds[1]["q"].max[Zrot, :] = .1

    # le carpe
    x_bounds[1]["q"].max[XrotC, :] = -2.5
    # le dehanchement
    x_bounds[1]["q"].min[YrotC, DEBUT] = -.1
    x_bounds[1]["q"].max[YrotC, DEBUT] = .1

    # Contraintes de vitesse: PHASE 1 le salto carpe

    # en xy bassin
    x_bounds[1]["qdot"].min[vX:vY + 1, :] = -10
    x_bounds[1]["qdot"].max[vX:vY + 1, :] = 10

    # z bassin
    x_bounds[1]["qdot"].min[vZ, :] = -50
    x_bounds[1]["qdot"].max[vZ, :] = 50

    # autour de x
    x_bounds[1]["qdot"].min[vXrot, :] = -50
    x_bounds[1]["qdot"].max[vXrot, :] = 50
    # autour de y
    x_bounds[1]["qdot"].min[vYrot, :] = -50
    x_bounds[1]["qdot"].max[vYrot, :] = 50

    # autour de z
    x_bounds[1]["qdot"].min[vZrot, :] = -50
    x_bounds[1]["qdot"].max[vZrot, :] = 50

    # bras droit
    x_bounds[1]["qdot"].min[vZrotBD:vYrotBD + 1, :] = -50
    x_bounds[1]["qdot"].max[vZrotBD:vYrotBD + 1, :] = 50
    # bras droit
    x_bounds[1]["qdot"].min[vZrotBG:vYrotBG + 1, :] = -50
    x_bounds[1]["qdot"].max[vZrotBG:vYrotBG + 1, :] = 50

    # coude droit
    x_bounds[1]["qdot"].min[vZrotABD:vYrotABD + 1, :] = -50
    x_bounds[1]["qdot"].max[vZrotABD:vYrotABD + 1, :] = 50
    # coude gauche
    x_bounds[1]["qdot"].min[vZrotABD:vYrotABG + 1, :] = -50
    x_bounds[1]["qdot"].max[vZrotABD:vYrotABG + 1, :] = 50

    # du carpe
    x_bounds[1]["qdot"].min[vXrotC, :] = -50
    x_bounds[1]["qdot"].max[vXrotC, :] = 50
    # du dehanchement
    x_bounds[1]["qdot"].min[vYrotC, :] = -50
    x_bounds[1]["qdot"].max[vYrotC, :] = 50

    #
    # Contraintes de position: PHASE 2 l'ouverture
    #

    # deplacement
    x_bounds[2]["q"].min[X, :] = -.2
    x_bounds[2]["q"].max[X, :] = .2
    x_bounds[2]["q"].min[Y, :] = -1.
    x_bounds[2]["q"].max[Y, :] = 1.
    x_bounds[2]["q"].min[Z, :] = 0
    x_bounds[2]["q"].max[Z, :] = zmax  # beaucoup plus que necessaire, juste pour que la parabole fonctionne

    # le salto autour de x
    x_bounds[2]["q"].min[Xrot, :] = 2 * 3.14 - .1
    x_bounds[2]["q"].max[Xrot, :] = -.50 + 4 * 3.14
    # limitation du tilt autour de y
    x_bounds[2]["q"].min[Yrot, :] = - 3.14 / 4
    x_bounds[2]["q"].max[Yrot, :] = 3.14 / 4
    # la vrille autour de z
    x_bounds[2]["q"].min[Zrot, :] = 0
    x_bounds[2]["q"].max[Zrot, :] = 3.14  # 5 * 3.14

    x_bounds[2]["q"].min[XrotC, FIN] = -.4

    # Contraintes de vitesse: PHASE 2 l'ouverture

    # en xy bassin
    x_bounds[2]["qdot"].min[vX:vY + 1, :] = -10
    x_bounds[2]["qdot"].max[vX:vY + 1, :] = 10

    # z bassin
    x_bounds[2]["qdot"].min[vZ, :] = -50
    x_bounds[2]["qdot"].max[vZ, :] = 50

    # autour de x
    x_bounds[2]["qdot"].min[vXrot, :] = -50
    x_bounds[2]["qdot"].max[vXrot, :] = 50
    # autour de y
    x_bounds[2]["qdot"].min[vYrot, :] = -50
    x_bounds[2]["qdot"].max[vYrot, :] = 50

    # autour de z
    x_bounds[2]["qdot"].min[vZrot, :] = -50
    x_bounds[2]["qdot"].max[vZrot, :] = 50

    # bras droit
    x_bounds[2]["qdot"].min[vZrotBD:vYrotBD + 1, :] = -50
    x_bounds[2]["qdot"].max[vZrotBD:vYrotBD + 1, :] = 50
    # bras droit
    x_bounds[2]["qdot"].min[vZrotBG:vYrotBG + 1, :] = -50
    x_bounds[2]["qdot"].max[vZrotBG:vYrotBG + 1, :] = 50

    # coude droit
    x_bounds[2]["qdot"].min[vZrotABD:vYrotABD + 1, :] = -50
    x_bounds[2]["qdot"].max[vZrotABD:vYrotABD + 1, :] = 50
    # coude gauche
    x_bounds[2]["qdot"].min[vZrotABD:vYrotABG + 1, :] = -50
    x_bounds[2]["qdot"].max[vZrotABD:vYrotABG + 1, :] = 50

    # du carpe
    x_bounds[2]["qdot"].min[vXrotC, :] = -50
    x_bounds[2]["qdot"].max[vXrotC, :] = 50
    # du dehanchement
    x_bounds[2]["qdot"].min[vYrotC, :] = -50
    x_bounds[2]["qdot"].max[vYrotC, :] = 50

    #
    # Contraintes de position: PHASE 3 la vrille et demie
    #

    # deplacement
    x_bounds[3]["q"].min[X, :] = -.2
    x_bounds[3]["q"].max[X, :] = .2
    x_bounds[3]["q"].min[Y, :] = -1.
    x_bounds[3]["q"].max[Y, :] = 1.
    x_bounds[3]["q"].min[Z, :] = 0
    x_bounds[3]["q"].max[Z, :] = zmax  # beaucoup plus que necessaire, juste pour que la parabole fonctionne

    # le salto autour de x
    x_bounds[3]["q"].min[Xrot, :] = 0
    x_bounds[3]["q"].min[Xrot, :] = 2 * 3.14 - .1

    x_bounds[3]["q"].max[Xrot, :] = 2 * 3.14 + 3 / 2 * 3.14 + .1  # 1 salto 3/4
    x_bounds[3]["q"].min[Xrot, FIN] = 2 * 3.14 + 3 / 2 * 3.14 - .1
    x_bounds[3]["q"].max[Xrot, FIN] = 2 * 3.14 + 3 / 2 * 3.14 + .1  # 1 salto 3/4

    # limitation du tilt autour de y
    x_bounds[3]["q"].min[Yrot, :] = - 3.14 / 4
    x_bounds[3]["q"].max[Yrot, :] = 3.14 / 4
    x_bounds[3]["q"].min[Yrot, FIN] = - 3.14 / 8
    x_bounds[3]["q"].max[Yrot, FIN] = 3.14 / 8
    # la vrille autour de z
    x_bounds[3]["q"].min[Zrot, :] = 0
    x_bounds[3]["q"].max[Zrot, :] = 5 * 3.14
    x_bounds[3]["q"].min[Zrot, FIN] = nb_twist * 3.14 - .1  # complete la vrille
    x_bounds[3]["q"].max[Zrot, FIN] = nb_twist * 3.14 + .1

    # le carpe  f4a les jambes
    x_bounds[3]["q"].min[XrotC, :] = -.4
    # le dehanchement

    # Contraintes de vitesse: PHASE 3 la vrille et demie

    # en xy bassin
    x_bounds[3]["qdot"].min[vX:vY + 1, :] = -10
    x_bounds[3]["qdot"].max[vX:vY + 1, :] = 10

    # z bassin
    x_bounds[3]["qdot"].min[vZ, :] = -50
    x_bounds[3]["qdot"].max[vZ, :] = 50

    # autour de x
    x_bounds[3]["qdot"].min[vXrot, :] = -50
    x_bounds[3]["qdot"].max[vXrot, :] = 50
    # autour de y
    x_bounds[3]["qdot"].min[vYrot, :] = -50
    x_bounds[3]["qdot"].max[vYrot, :] = 50

    # autour de z
    x_bounds[3]["qdot"].min[vZrot, :] = -50
    x_bounds[3]["qdot"].max[vZrot, :] = 50

    # bras droit
    x_bounds[3]["qdot"].min[vZrotBD:vYrotBD + 1, :] = -50
    x_bounds[3]["qdot"].max[vZrotBD:vYrotBD + 1, :] = 50
    # bras droit
    x_bounds[3]["qdot"].min[vZrotBG:vYrotBG + 1, :] = -50
    x_bounds[3]["qdot"].max[vZrotBG:vYrotBG + 1, :] = 50

    # coude droit
    x_bounds[3]["qdot"].min[vZrotABD:vYrotABD + 1, :] = -50
    x_bounds[3]["qdot"].max[vZrotABD:vYrotABD + 1, :] = 50
    # coude gauche
    x_bounds[3]["qdot"].min[vZrotABD:vYrotABG + 1, :] = -50
    x_bounds[3]["qdot"].max[vZrotABD:vYrotABG + 1, :] = 50

    # du carpe
    x_bounds[3]["qdot"].min[vXrotC, :] = -50
    x_bounds[3]["qdot"].max[vXrotC, :] = 50
    # du dehanchement
    x_bounds[3]["qdot"].min[vYrotC, :] = -50
    x_bounds[3]["qdot"].max[vYrotC, :] = 50

    #
    # Contraintes de position: PHASE 4 la reception
    #

    # deplacement
    x_bounds[4]["q"].min[X, :] = -.1
    x_bounds[4]["q"].max[X, :] = .1
    x_bounds[4]["q"].min[Y, FIN] = -.1
    x_bounds[4]["q"].max[Y, FIN] = .1
    x_bounds[4]["q"].min[Z, :] = 0
    x_bounds[4]["q"].max[Z, :] = zmax  # beaucoup plus que necessaire, juste pour que la parabole fonctionne
    x_bounds[4]["q"].min[Z, FIN] = 0
    x_bounds[4]["q"].max[Z, FIN] = .1

    # le salto autour de x
    x_bounds[4]["q"].min[Xrot, :] = 2 * 3.14 + 3 / 2 * 3.14 - .2  # penche vers avant -> moins de salto
    x_bounds[4]["q"].max[Xrot, :] = -.50 + 4 * 3.14  # un peu carpe a la fin
    x_bounds[4]["q"].min[Xrot, FIN] = -.50 + 4 * 3.14 - .1  #  salto fin un peu carpe
    x_bounds[4]["q"].max[Xrot, FIN] = -.50 + 4 * 3.14 + .1  #  salto fin un peu carpe
    # limitation du tilt autour de y
    x_bounds[4]["q"].min[Yrot, :] = - 3.14 / 16
    x_bounds[4]["q"].max[Yrot, :] = 3.14 / 16
    # la vrille autour de z
    x_bounds[4]["q"].min[Zrot, :] = nb_twist * 3.14 - .1  # complete la vrille
    x_bounds[4]["q"].max[Zrot, :] = nb_twist * 3.14 + .1

    # bras droit
    x_bounds[4]["q"].min[YrotBD, FIN] = 2.9 - .1  # debut bras aux oreilles
    x_bounds[4]["q"].max[YrotBD, FIN] = 2.9 + .1
    x_bounds[4]["q"].min[ZrotBD, FIN] = -.1
    x_bounds[4]["q"].max[ZrotBD, FIN] = .1
    # bras gauche
    x_bounds[4]["q"].min[YrotBG, FIN] = -2.9 - .1  # debut bras aux oreilles
    x_bounds[4]["q"].max[YrotBG, FIN] = -2.9 + .1
    x_bounds[4]["q"].min[ZrotBG, FIN] = -.1
    x_bounds[4]["q"].max[ZrotBG, FIN] = .1

    # coude droit
    x_bounds[4]["q"].min[ZrotABD:XrotABD + 1, FIN] = -.1
    x_bounds[4]["q"].max[ZrotABD:XrotABD + 1, FIN] = .1
    # coude gauche
    x_bounds[4]["q"].min[ZrotABG:XrotABG + 1, FIN] = -.1
    x_bounds[4]["q"].max[ZrotABG:XrotABG + 1, FIN] = .1

    # le carpe
    x_bounds[4]["q"].min[XrotC, :] = -.4
    x_bounds[4]["q"].min[XrotC, FIN] = -.60
    x_bounds[4]["q"].max[XrotC, FIN] = -.40  # fin un peu carpe
    # le dehanchement
    x_bounds[4]["q"].min[YrotC, FIN] = -.1
    x_bounds[4]["q"].max[YrotC, FIN] = .1

    # Contraintes de vitesse: PHASE 4 la reception

    # en xy bassin
    x_bounds[4]["qdot"].min[vX:vY + 1, :] = -10
    x_bounds[4]["qdot"].max[vX:vY + 1, :] = 10

    # z bassin
    x_bounds[4]["qdot"].min[vZ, :] = -50
    x_bounds[4]["qdot"].max[vZ, :] = 50

    # autour de x
    x_bounds[4]["qdot"].min[vXrot, :] = -50
    x_bounds[4]["qdot"].max[vXrot, :] = 50
    # autour de y
    x_bounds[4]["qdot"].min[vYrot, :] = -50
    x_bounds[4]["qdot"].max[vYrot, :] = 50

    # autour de z
    x_bounds[4]["qdot"].min[vZrot, :] = -50
    x_bounds[4]["qdot"].max[vZrot, :] = 50

    # bras droit
    x_bounds[4]["qdot"].min[vZrotBD:vYrotBD + 1, :] = -50
    x_bounds[4]["qdot"].max[vZrotBD:vYrotBD + 1, :] = 50
    # bras droit
    x_bounds[4]["qdot"].min[vZrotBG:vYrotBG + 1, :] = -50
    x_bounds[4]["qdot"].max[vZrotBG:vYrotBG + 1, :] = 50

    # coude droit
    x_bounds[4]["qdot"].min[vZrotABD:vYrotABD + 1, :] = -50
    x_bounds[4]["qdot"].max[vZrotABD:vYrotABD + 1, :] = 50
    # coude gauche
    x_bounds[4]["qdot"].min[vZrotABD:vYrotABG + 1, :] = -50
    x_bounds[4]["qdot"].max[vZrotABD:vYrotABG + 1, :] = 50

    # du carpe
    x_bounds[4]["qdot"].min[vXrotC, :] = -50
    x_bounds[4]["qdot"].max[vXrotC, :] = 50
    # du dehanchement
    x_bounds[4]["qdot"].min[vYrotC, :] = -50
    x_bounds[4]["qdot"].max[vYrotC, :] = 50

    #
    # Initial guesses
    #
    x0 = np.vstack((np.zeros((nb_q, 2)), np.zeros((nb_qdot, 2))))
    x1 = np.vstack((np.zeros((nb_q, 2)), np.zeros((nb_qdot, 2))))
    x2 = np.vstack((np.zeros((nb_q, 2)), np.zeros((nb_qdot, 2))))
    x3 = np.vstack((np.zeros((nb_q, 2)), np.zeros((nb_qdot, 2))))
    x4 = np.vstack((np.zeros((nb_q, 2)), np.zeros((nb_qdot, 2))))

    # bras droit  f4a la vrille
    # décollage prise del aposition carpée
    x0[Xrot, 0] = .50
    x0[ZrotBG] = -.75
    x0[ZrotBD] = .75
    x0[YrotBG, 0] = -2.9
    x0[YrotBD, 0] = 2.9
    x0[YrotBG, 1] = -1.35
    x0[YrotBD, 1] = 1.35
    x0[XrotC, 0] = -.5
    x0[XrotC, 1] = -2.6

    # rotater en salto (x) en carpé
    x1[ZrotBG] = -.75
    x1[ZrotBD] = .75
    x1[Xrot, 1] = 2 * 3.14
    x1[YrotBG] = -1.35
    x1[YrotBD] = 1.35
    x1[XrotC] = -2.6

    # ouverture des hanches
    x2[Xrot] = 2 * 3.14
    x2[Zrot, 1] = 0.2
    x2[ZrotBG, 0] = -.75
    x2[ZrotBD, 0] = .75
    x2[YrotBG, 0] = -1.35
    x2[YrotBD, 0] = 1.35
    x2[XrotC, 0] = -2.6

    # Vrille en position tendue
    x3[Xrot, 0] = 2 * 3.14
    x3[Xrot, 1] = 2 * 3.14 + 3 / 2 * 3.14
    x3[Zrot, 0] = 0  # METTRE 0 ?
    x3[Zrot, 1] = nb_twist * 3.14

    # Aterrissage (réduire le tilt)
    x4[Xrot, 0] = 2 * 3.14 + 3 / 2 * 3.14
    x4[Xrot, 1] = 4 * 3.14
    x4[Zrot] = nb_twist * 3.14
    x4[XrotC, 1] = -.5

    x_init = InitialGuessList()
    x_init.add("q", initial_guess=x0[:nb_q, :], interpolation=InterpolationType.LINEAR, phase=0)
    x_init.add("qdot", initial_guess=x0[nb_q:, :], interpolation=InterpolationType.LINEAR, phase=0)
    x_init.add("q", initial_guess=x1[:nb_q, :], interpolation=InterpolationType.LINEAR, phase=1)
    x_init.add("qdot", initial_guess=x1[nb_q:, :], interpolation=InterpolationType.LINEAR, phase=1)
    x_init.add("q", initial_guess=x2[:nb_q, :], interpolation=InterpolationType.LINEAR, phase=2)
    x_init.add("qdot", initial_guess=x2[nb_q:, :], interpolation=InterpolationType.LINEAR, phase=2)
    x_init.add("q", initial_guess=x3[:nb_q, :], interpolation=InterpolationType.LINEAR, phase=3)
    x_init.add("qdot", initial_guess=x3[nb_q:, :], interpolation=InterpolationType.LINEAR, phase=3)
    x_init.add("q", initial_guess=x4[:nb_q, :], interpolation=InterpolationType.LINEAR, phase=4)
    x_init.add("qdot", initial_guess=x4[nb_q:, :], interpolation=InterpolationType.LINEAR, phase=4)

    for i in range(5):
        x_init[i]["q"].add_noise(
            bounds=x_bounds[i]["q"],
            n_shooting=np.array(n_shooting[i])+1,
            magnitude=0.2,
            magnitude_type=MagnitudeType.RELATIVE,
            seed=seed,
            )
        x_init[i]["qdot"].add_noise(
            bounds=x_bounds[i]["qdot"],
            n_shooting=np.array(n_shooting[i])+1,
            magnitude=0.2,
            magnitude_type=MagnitudeType.RELATIVE,
            seed=seed,
            )

    constraints = ConstraintList()
    constraints.add(ConstraintFcn.SUPERIMPOSE_MARKERS, node=Node.ALL_SHOOTING, min_bound=-.1, max_bound=.1,
                    first_marker='MidMainG', second_marker='CibleMainG', phase=1)
    constraints.add(ConstraintFcn.SUPERIMPOSE_MARKERS, node=Node.ALL_SHOOTING, min_bound=-.1, max_bound=.1,
                    first_marker='MidMainD', second_marker='CibleMainD', phase=1)
    constraints.add(ConstraintFcn.TIME_CONSTRAINT, node=Node.END, min_bound=1e-4, max_bound=1.5, phase=1)
    constraints.add(ConstraintFcn.TIME_CONSTRAINT, node=Node.END, min_bound=1e-4, max_bound=0.7, phase=3)
    constraints.add(ConstraintFcn.TIME_CONSTRAINT, node=Node.END, min_bound=1e-4, max_bound=0.5, phase=4)

    return OptimalControlProgram(
        biorbd_model,
        dynamics,
        n_shooting,
        [final_time / len(biorbd_model)] * len(biorbd_model),
        x_init=x_init,
        u_init=u_init,
        x_bounds=x_bounds,
        u_bounds=u_bounds,
        objective_functions=objective_functions,
        constraints=constraints,
        n_threads=5,
    )

def construct_filepath(biorbd_model_path, nb_twist, seed):
    stunts = dict({3: "vrille_et_demi", 5: "double_vrille_et_demi", 7: "triple_vrille_et_demi"})
    stunt = stunts[nb_twist]
    athlete = biorbd_model_path.split('/')[-1].removesuffix('.bioMod')
    title_before_solve = f"{athlete}_{stunt}_{seed}"
    return title_before_solve

def save_results(sol: Solution,
                 *combinatorial_parameters,
                 **extra_parameter):
    """
    Solving the ocp
    Parameters
    ----------
    sol: Solution
        The solution to the ocp at the current pool
    """

    title_before_solve = construct_filepath(biorbd_model_path, nb_twist, seed)

    convergence = sol.status
    dict_state = {}
    q = []
    qdot = []
    tau = []

    for i in range(len(sol.states)) :
        q.append(sol.states[i]['q'])
        qdot.append(sol.states[i]['qdot'])
        tau.append(sol.controls[i]['qddot_joints'])

    dict_state['q'] = q
    dict_state['qdot'] = qdot
    dict_state['tau'] = tau
    del sol.ocp
    dict_state['sol'] = sol

    if convergence == 0 :
        convergence = 'CVG'
        print(f'{athlete}  doing' + f' {stunt}' + ' converge')
    else:
        convergence = 'DVG'
        print(f'{athlete} doing ' + f'{stunt}' + ' doesn t converge')

    if save_folder:
        with open(f'{save_folder}/{title_before_solve}_{convergence}.pkl', "wb") as file:
            pickle.dump(dict_state, file)
    else:
        raise RuntimeError(f"This folder {save_folder} does not exist")

def should_solve(*combinatorial_parameters, **extra_parameters):
    """
    Check if the filename already appears in the folder where files are saved, if not ocp must be solved
    """
    biorbd_model_path, nb_twist, seed = combinatorial_parameters
    save_folder = extra_parameters["save_folder"]
    file_path = construct_filepath(biorbd_model_path, nb_twist, seed)
    already_done_filenames = os.listdir(f"{save_folder}")

    if file_path not in already_done_filenames:
        return True
    else:
        return False

def prepare_multi_start(
    combinatorial_parameters: dict[tuple,...],
    save_folder: str = None,
    n_pools: int = 6
) -> MultiStart:

    """
    The initialization of the multi-start
    """
    return MultiStart(
        combinatorial_parameters=combinatorial_parameters,
        prepare_ocp_callback=prepare_ocp,
        post_optimization_callback=(save_results, {'save_folder': save_folder}),
        should_solve_callback=(should_solve, {'save_folder': save_folder}),
        solver=Solver.IPOPT(show_online_optim=False),  # You cannot use show_online_optim with multi-start
        n_pools=n_pools,
    )

def main():
    """
    Prepares and solves an ocp for a 803<. Animates the results
    """

    seed = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
    nb_twist = [3, 5]
    athletes = [
        # "AdCh",
        # "AlAd",
        # "AuJo",
        # "Benjamin",
        # "ElMe",
        # "EvZl",
        # "FeBl",
        # "JeCh",
        # "KaFu",
        # "KaMi",
        # "LaDe",
        # "MaCu",
        # "MaJa",
        # "OlGa",
        "Sarah",
        # "SoMe",
        # "WeEm",
        # "ZoTs"
        ]

    all_paths = []
    for athlete in athletes :
        path = f'{athlete}'+'.bioMod'
        biorbd_model_path = "Models/Models_Lisa/" + f'{path}'
        all_paths.append(biorbd_model_path)

    combinatorial_parameters = {'bio_model_path': all_paths,
                                'nb_twist': nb_twist,
                                'seed': seed}
    save_folder = "Multistart_double_vrille"

    multi_start = prepare_multi_start(combinatorial_parameters=combinatorial_parameters, save_folder=save_folder, n_pools=6)

    multi_start.solver = Solver.IPOPT(show_online_optim=False, show_options=dict(show_bounds=False))
        #if Mod.with_hsl:
    multi_start.solver.set_linear_solver('ma57')
    #else:
    #    print("Not using ma57")
    multi_start.solver.set_maximum_iterations(3000)
    multi_start.solver.set_convergence_tolerance(1e-4)
    #multi_start.solver.set_print_level(0)

    multi_start.solve()

    #sol.graphs(show_bounds=True, show_now=False, save_path=f'{folder}/{athlete}')

if __name__ == "__main__":
    main()


from PIL import Image
from torchvision import transforms
from JointCompose import JointCompose, IMG_ONLY_TRANSFORM, MASK_ONLY_TRANSFORM, RANDOM_JOINT_TRANSFORM_WITH_BORDERS, BORDER_ONLY_TRANSFORM, JOINT_TRANSFORM_WITH_BORDERS
import numpy as np
from scipy.ndimage.interpolation import map_coordinates
from scipy.ndimage.filters import gaussian_filter
from skimage.segmentation import mark_boundaries, find_boundaries
from skimage.exposure import adjust_gamma, rescale_intensity, equalize_hist, equalize_adapthist
from skimage.util import img_as_float
from skimage.segmentation import quickshift, felzenszwalb, slic
import random
from skimage.color import rgb2lab, rgb2grey
from skimage import filters
import cv2

IMG_SIZE = 256

class TransformSpec:
    def __init__(self, transform, transform_type, prob = None):
        self.transform = transform
        self.transform_type = transform_type
        self.prob = prob

class Flip(object):
    """flips the given PIL Image horizontally or vertically.
    param type: 0 for horizontal flip, 1 for vertical flip
    """

    def __init__(self, flip_type):
        self.type = flip_type

    def __call__(self, img):
        """
        Args:
            img (PIL Image): Image to be flipped.
        Returns:
            PIL Image: flipped image.
        """
        if self.type == 0:
            return img.transpose(Image.FLIP_LEFT_RIGHT)
        else:
            return img.transpose(Image.FLIP_TOP_BOTTOM)

# aside: transforms are written as callable classes instead of simple functions so that parameters
#  of the transform need not be passed everytime it is called. For this, we just need to implement
# __call__ method and if required, __init__ method.
class Segment(object):
    def __call__(self, img):
        # img is a numpy rgb image
        grey_img = rgb2grey(img)
        t1 = filters.threshold_minimum(grey_img)
        t2 = filters.threshold_yen(grey_img)

        img1 = mark_boundaries(img, (grey_img > t1), color=(1,0,0))
        img1 = mark_boundaries(img1, (grey_img > t2), color=(1,0,0))
        img2 = mark_boundaries(img, grey_img < 0)
        img = ((img1+img2)/2)

        #img = mark_boundaries(img, quickshift(img_as_float(img), kernel_size =5, max_dist = 10, ratio = 1.0))

        #img = mark_boundaries(img, slic(img_as_float(img), n_segments=10))
        #fimg = rgb2grey(img)
        #t = filters.threshold_otsu(fimg)
        #img = mark_boundaries(img, (fimg > t).astype(np.uint8), color=(1,0,0))
        #img  = mark_boundaries(img, (fimg - filters.threshold_niblack(fimg)< 0).astype(np.uint8), color=(1,0,0))

        #img_gray = rgb2grey(img)
        #img_gray = img[:, :, 1]
        # morphological opening (size tuned on training data)
        #circle7 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7))
        #img_open = cv2.morphologyEx(img_gray, cv2.MORPH_OPEN, circle7)
        # Otsu thresholding
        #img_th = cv2.threshold(img_open, 0, 255, cv2.THRESH_OTSU)[1]
        # Invert the image in case the objects of interest are in the dark side
        #if (np.sum(img_th == 255) > np.sum(img_th == 0)):
        #    img_th = cv2.bitwise_not(img_th)
        # second morphological opening (on binary image this time)
        #bin_open = cv2.morphologyEx(img_th, cv2.MORPH_OPEN, circle7)
        # connected components
        #img = mark_boundaries(img,cv2.connectedComponents(bin_open)[1], color=(1,0,0))

        return (img*255).astype(np.uint8)

class JitterBrightness(object):
    def __call__(self, img):
        # img is a numpy rgb image
        gamma = random.random() + 0.3
        return adjust_gamma(img, gamma)

class Rescale(object):
    def __call__(self, img):
        # img is a numpy rgb image
        return equalize_adapthist(img)

class Negative(object):
    def __call__(self, img):
        # img is a numpy rgb image
        return rescale_intensity(255-img)

class To3D(object):
    # make into a 3d RGB-like array required for making it a PIL image and then a tensor
    def __call__(self, mask):
        h = mask.shape[0]
        w = mask.shape[1]
        mask_rgb = np.zeros((h,w,3))
        for i in xrange(h):
            for j in xrange(w):
                if mask[i,j] == 1:
                    mask_rgb[i,j,:] = 255
        return mask_rgb.astype(np.uint8)

class To1Ch(object):
    def __call__(self, img, channel = 0):
        return img[:,:,channel][:,:,None]

class Binarize(object):
    def __call__(self, img):
        img[img > 0.5] = 1.0
        img[img < 1.0] = 0.0
        return img

class ElasticTransform(object):
    '''
    sigma: positive float for smoothing the transformation (elasticy of the transformation.)
    If sigma is small teh field looks like a completely random field after normalization
    For intermidiate sigma values the displacement fields look like elastic deformation, where sigma is the elasticity coefficient.
    If sigma is large, the displacements become close to affine. If sigma is very large the displacements become translations.
    alpha: scaling facor - positive float giving the intensity of the transformation. Larger alphas require larger sigmas
    default values take from the paper
    '''
    def __init__(self, sigma=1.5, alpha=34.0):
        '''

        :param sigma: positive floaf giving the elasticity of the transformation
        :param alpha: positive float giving the intensity of the transformation
        '''
        self.sigma = sigma
        self.alpha = alpha

    def __call__(self, img, mask, borders):

        if len(mask.shape) == 2:
            # merge the image and the mask
            merged_img = np.zeros(img.shape)
            merged_img[:,:,] = img[:,:,]
            merged_img[:,:,0] = mask[:,:]

            # apply elastic deformation on the merged image
            [deformed_merged_img, deformed_borders] = self.__elastic_deformation__([merged_img, borders])

            # split image and mask from the merged deformed image
            # mask
            deformed_mask = np.zeros(mask.shape)
            deformed_mask[:,:] = deformed_merged_img[:, :, 0]
            self.dichotom(deformed_mask, 0.5, 1.0)
            # image
            deformed_img = deformed_merged_img[:,:,:]
            deformed_img[:,:,0] = img[:,:,0]

        else:
            [deformed_img, deformed_mask, deformed_borders] = self.__elastic_deformation__([img, mask, borders])

        return deformed_img.astype(np.uint8), deformed_mask.astype(np.uint8), deformed_borders.astype(np.uint8)

    '''
    based on the paper 'Best Practices for Convolutional Neural Networks Applied to Visual Document Analysis' Simard et al 2003
    generalized the following implementation: https://gist.github.com/chsasank/4d8f68caf01f041a6453e67fb30f8f5a
    Works on numpy images
    '''
    def __elastic_deformation__(self, imgs):
        img = imgs[0]
        # img is a numpy image
        shape = img.shape
        n_dim = len(shape)
        convolved_displacement_fields = []
        grid = []
        fsize = len(img.flatten())
        for i in xrange(n_dim):
            if i < 2:  # don't touch the channel
                cdf = np.array([random.random() for j in xrange(fsize)]).reshape(shape) * 2 - 1
                convolved_displacement_fields.append(
                    gaussian_filter(cdf, self.sigma, mode="constant", cval=0) * self.alpha)
            grid.append(np.arange(shape[i]))
        grid = np.meshgrid(*grid, indexing='ij')
        indices = []
        for i in xrange(n_dim):
            if i < 2:  # don't touch the channel
                indices.append(np.reshape(grid[i] + convolved_displacement_fields[i], (-1, 1)))
            else:
                indices.append(np.reshape(grid[i], (-1, 1)))
        deformed_imgs = [map_coordinates(my_img, indices, order=3).reshape(shape) for my_img in imgs]
        return deformed_imgs

    def dichotom(self, img, thr, v1, v0=0):
        if len(img.shape) == 2:
            img[img > thr] = v1
            img[img < v1] = v0
        else:
            height, width, channel = img.shape
            for i in xrange(height):
                for j in xrange(width):
                    for k in xrange(channel):
                        if img[i, j, k] == thr:
                            img[i, j, :] = v1
                            break
            img[img < v1] = v0

def PIL_torch_to_numpy(img):
    img = np.transpose(img.numpy(), (1, 2, 0))
    if img.shape[2] == 1:
        img = img[:,:,0]
    return img

def reverse_test_transform(img, original_size):
    '''
    reverse the basic mask transformation
    :param img:
    :param original_size: H X W X C of image
    :return:
    '''
    # resize the tenstor to the original size
    reverse_transform = transforms.Compose([transforms.ToPILImage(),
                                            transforms.Resize(original_size[:2]), transforms.ToTensor()])
    img = PIL_torch_to_numpy(reverse_transform(img))
    return img

def to_binary_mask(labelled_mask, with_borders, use_borders_as_mask):

    if use_borders_as_mask:
        mask = find_boundaries(labelled_mask, mode='outer')
    else:
        mask = (labelled_mask > 0)
        if with_borders:
            mask[find_boundaries(labelled_mask, mode='outer')] = 0

    #borders = (labelled_mask > 0).astype(np.uint8) - mask  # borders of touching cells (if borders are marked)
    borders = find_boundaries(labelled_mask, mode='outer')
    return mask.astype(np.uint8), borders.astype(np.uint8)

# add transformations to color

transformations = {
"train_transform_elastic":JointCompose(# transformations
    [
    # turn mask into 3D RGB-Like for PIL and tensor transformation
    TransformSpec(To3D(), MASK_ONLY_TRANSFORM),
    TransformSpec(To3D(), BORDER_ONLY_TRANSFORM),
    #Elastic deformation on the numpy images
    TransformSpec(ElasticTransform(), RANDOM_JOINT_TRANSFORM_WITH_BORDERS, prob=0.8),
    # Convert borders and mask to 1 channel
    TransformSpec(To1Ch(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), MASK_ONLY_TRANSFORM),
    # color jittering (image only)
    TransformSpec(JitterBrightness(), IMG_ONLY_TRANSFORM),
    TransformSpec(Negative(), IMG_ONLY_TRANSFORM, prob=0.5),
    # turn into a PIL image - required to apply torch transforms (both image, mask and borders)
    TransformSpec(transforms.ToPILImage(), JOINT_TRANSFORM_WITH_BORDERS),
    # flipping
    TransformSpec(Flip(1), JOINT_TRANSFORM_WITH_BORDERS, prob=0.5),

    #resize image (bilinear interpolation)
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.BILINEAR),
                  IMG_ONLY_TRANSFORM),
    #resize borders (bilinear interpolation)
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  BORDER_ONLY_TRANSFORM),
    # resize mask
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  MASK_ONLY_TRANSFORM),
    # finally turn into a torch tenstor (both image and mask)
    # swap color axis because
    # numpy image: H x W x C
    # torch image: C X H X W
    TransformSpec(transforms.ToTensor(),JOINT_TRANSFORM_WITH_BORDERS),
    # ensure mask and borders are binarized
    TransformSpec(Binarize(), BORDER_ONLY_TRANSFORM),
    TransformSpec(Binarize(), MASK_ONLY_TRANSFORM)]
),
"train_transform":JointCompose(
    [TransformSpec(To3D(), MASK_ONLY_TRANSFORM),
    TransformSpec(To3D(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), MASK_ONLY_TRANSFORM),
    TransformSpec(JitterBrightness(), IMG_ONLY_TRANSFORM, prob=0.5),
    TransformSpec(transforms.ToPILImage(), JOINT_TRANSFORM_WITH_BORDERS),
    TransformSpec(Flip(1), JOINT_TRANSFORM_WITH_BORDERS, prob=0.2),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.BILINEAR),
                  IMG_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  BORDER_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  MASK_ONLY_TRANSFORM),
    TransformSpec(transforms.ToTensor(),JOINT_TRANSFORM_WITH_BORDERS),
     TransformSpec(Binarize(), BORDER_ONLY_TRANSFORM),
     TransformSpec(Binarize(), MASK_ONLY_TRANSFORM)]
),
"train_transform_segment":JointCompose(
    [TransformSpec(To3D(), MASK_ONLY_TRANSFORM),
    TransformSpec(To3D(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), MASK_ONLY_TRANSFORM),
    TransformSpec(JitterBrightness(), IMG_ONLY_TRANSFORM, prob=0.9),
     TransformSpec(Segment(), IMG_ONLY_TRANSFORM),
    TransformSpec(transforms.ToPILImage(), JOINT_TRANSFORM_WITH_BORDERS),
    TransformSpec(Flip(1), JOINT_TRANSFORM_WITH_BORDERS, prob=0.2),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.BILINEAR),
                  IMG_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  BORDER_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  MASK_ONLY_TRANSFORM),
    TransformSpec(transforms.ToTensor(),JOINT_TRANSFORM_WITH_BORDERS),
     TransformSpec(Binarize(), BORDER_ONLY_TRANSFORM),
     TransformSpec(Binarize(), MASK_ONLY_TRANSFORM)]
),
"train_transform_jitter":JointCompose(
    [TransformSpec(To3D(), MASK_ONLY_TRANSFORM),
    TransformSpec(To3D(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), MASK_ONLY_TRANSFORM),
    TransformSpec(JitterBrightness(), IMG_ONLY_TRANSFORM, prob=0.9),
    TransformSpec(Negative(), IMG_ONLY_TRANSFORM, prob=0.5),
    TransformSpec(transforms.ToPILImage(), JOINT_TRANSFORM_WITH_BORDERS),
    TransformSpec(Flip(1), JOINT_TRANSFORM_WITH_BORDERS, prob=0.2),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.BILINEAR),
                  IMG_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  BORDER_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  MASK_ONLY_TRANSFORM),
    TransformSpec(transforms.ToTensor(),JOINT_TRANSFORM_WITH_BORDERS),
     TransformSpec(Binarize(), BORDER_ONLY_TRANSFORM),
     TransformSpec(Binarize(), MASK_ONLY_TRANSFORM)]
),
"test_transform":JointCompose(
    [TransformSpec(To3D(), MASK_ONLY_TRANSFORM),
TransformSpec(To3D(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), MASK_ONLY_TRANSFORM),
     TransformSpec(Segment(), IMG_ONLY_TRANSFORM),
     TransformSpec(transforms.ToPILImage(), JOINT_TRANSFORM_WITH_BORDERS),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.BILINEAR),
                  IMG_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  BORDER_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  MASK_ONLY_TRANSFORM),
    TransformSpec(transforms.ToTensor(),JOINT_TRANSFORM_WITH_BORDERS),
     TransformSpec(Binarize(), BORDER_ONLY_TRANSFORM),
     TransformSpec(Binarize(), MASK_ONLY_TRANSFORM)]
),
"toy_transform":JointCompose(
    [ # turn mask into 3D RGB-Like for PIL and tensor transformation
    TransformSpec(To3D(), MASK_ONLY_TRANSFORM),
        TransformSpec(To3D(), BORDER_ONLY_TRANSFORM),

        #Elastic deformation on the numpy images
    #TransformSpec(ElasticTransform(), RANDOM_JOINT_TRANSFORM_WITH_BORDERS),
    # Convert borders and mask to 1 channel
    TransformSpec(To1Ch(), BORDER_ONLY_TRANSFORM),
    TransformSpec(To1Ch(), MASK_ONLY_TRANSFORM),
    #TransformSpec(Rescale(), IMG_ONLY_TRANSFORM),

        # color jittering (image only)

    TransformSpec(JitterBrightness(), IMG_ONLY_TRANSFORM),
    #TransformSpec(Negative(), IMG_ONLY_TRANSFORM),
        TransformSpec(Segment(), IMG_ONLY_TRANSFORM),

        TransformSpec(transforms.ToPILImage(), JOINT_TRANSFORM_WITH_BORDERS),
    TransformSpec(Flip(1), JOINT_TRANSFORM_WITH_BORDERS, prob=0.0),

    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.BILINEAR),
                  IMG_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                  BORDER_ONLY_TRANSFORM),
    TransformSpec(transforms.Resize((IMG_SIZE,IMG_SIZE),interpolation=Image.NEAREST),
                 MASK_ONLY_TRANSFORM),
    TransformSpec(transforms.ToTensor(),JOINT_TRANSFORM_WITH_BORDERS),
    TransformSpec(Binarize(), BORDER_ONLY_TRANSFORM),
    TransformSpec(Binarize(), MASK_ONLY_TRANSFORM)]
)
}


"""Futurepedia"""
import json
import re
import time
import random
import scrapy
import os
from urllib.parse import urljoin

from zimeiti.items import ZimeitiItem
from zimeiti.public import refactoring_img, down_img, contenc_description, get_words, timetimes, execute, is_exists, \
    refactoring_img1
import math

class MainSpider(scrapy.Spider):
    name = "futurepedia"
    # allowed_domains = ["xxx.com"]
    start_urls = ["https://www.futurepedia.io/ai-tools"]
    path = f'//192.168.0.15/data/SEO/images/{name}/'
    s = 0
    l = 0
    def start_requests(self):
        start_url = 'https://www.futurepedia.io/api/tags'
        yield scrapy.Request(url=start_url,callback=self.lanmu)

    def lanmu(self,response):
        for resp in json.loads(response.text):
            categoryName = resp['categoryName']
            toolCount = resp['toolCount']
            page = math.ceil(toolCount/9)
            for p in range(1,page+1):
                url = F'https://www.futurepedia.io/api/tools?page={p}&tag={categoryName}&sort=verified'
                yield scrapy.Request(url=url,callback=self.lists,meta={'ncolumn':categoryName})

    def lists(self,repsonse):
        lists = repsonse.xpath('//ul[@class="yl_left_ul"]/li/a')
        if lists:
            for li in lists:
                fmt = li.xpath('img/@src').get()
                fmt_url = urljoin(self.start_urls[0],fmt)
                print(fmt_url)
                de_url = li.xpath('@href').get()
                detail_url = urljoin(self.start_urls[0],de_url)
                num = is_exists({'name':self.name,'url':detail_url})
                # yield scrapy.Request(url='http://www.41sky.com/gprj/2018-10-24/110.html', callback=self.detail, meta={'ncolumn': repsonse.meta['ncolumn']})
                if num == 0:
                    imgUrl = down_img(fmt_url,repsonse.url,self.path)  # 调用下载图片方法
                    print(imgUrl)
                    yield scrapy.Request(url=detail_url,callback=self.detail,meta={'ncolumn':repsonse.meta['ncolumn'],'imgUrl':imgUrl})
                else:
                    print('数据库已存在')
                    pass
        next_page = repsonse.xpath('//div[@class="pages"]/ul/li/a[contains(text(),"下一页")]/@href').get()
        if next_page:
            next_url = urljoin(repsonse.url,next_page)
            yield scrapy.Request(url=next_url,callback=self.lists,meta={'ncolumn':repsonse.meta['ncolumn']})

    def detail(self,response):
        print('获取内容')
        self.s += 1
        item = ZimeitiItem()
        item['title'] = response.xpath('//div[@class="art_content"]/h1/text()').get()
        if item['title']:
            item['title'] = item['title'].strip()
        content = response.xpath('//div[@class="text"]').getall()
        if content:
            text = "".join(content)
            item['Ncontent'] = refactoring_img(text,response.url,self.path)
            # item['Ncontent'] = refactoring_img(text,response.url,self.path)
            # item['Ncontent'] = content
            item['description'] = contenc_description(item['Ncontent'])
            item['nkeywords'] = get_words(item['Ncontent'])
            item['tag'] = item['nkeywords']
            item['domian'] = self.name
            item['webName'] = 'Futurepedia'
            item['url'] = response.url
            item['ncolumn'] = response.meta['ncolumn']
            item['naddtime'] = str(int(time.time()))
            item['imgUrl'] = response.meta['imgUrl']
            # item['lmImgUrl'] = response.meta['lmImgUrl']
            item['seo_title'] = response.xpath('//title/text()').get()
            item['seo_keywords'] = response.xpath('//meta[@name="keywords"]/@content').get()
            item['seo_description'] = response.xpath('//meta[@name="description"]/@content').get()
            yield item

if __name__ == '__main__':
    os.system('scrapy crawl futurepedia')


import cv2
import pickle as pkl
import time
import xgboost as xgb
import math
import numpy as np
import mediapipe as mp
from speak import speakText

mp_drawing = mp.solutions.drawing_utils
mp_hands = mp.solutions.hands

width = 640
height = 480

hands = mp_hands.Hands(min_detection_confidence=0.6, min_tracking_confidence=0.75)

modelRight = pkl.load(open('./models/xgboost-model-dynamic-words-16-right-tuned', 'rb'))
modelLeft = pkl.load(open('./models/xgboost-model-dynamic-words-16-left-tuned', 'rb'))

start_time = time.time()
speakWaitTime = 1

labels = {
    "0" : "me",
    "1" : "you",
    "2" : "hello",
    "3" : "from",
    "4" : "good",
    "5" : "how",
    "6" : "university",
    "7" : "welcome",
    "8" : "hope",
    "9" : "like",
    "10" : "new",
    "11" : "people",
    "12" : "technology",
    "13" : "use",
    "14" : "voice",
    "15" : "create"
}

cap = cv2.VideoCapture(0)
cap.set(cv2.CAP_PROP_FRAME_WIDTH, width)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, height)

rightHandFirst = False
isMultiHand = False

# Initialised to True for first frame
addRightFrame = True
addLeftFrame = True

rightKeyFrames = []
leftKeyFrames = []

rightKeyCheckPoints = []
leftKeyCheckPoints = []

rightLabel = ''
rightProb = 0
leftLabel = ''
leftProb = 0

null_12 = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

d_threshold = 0.1

connections = [
    (1, 4), (5, 8), (9, 12), (13, 16), (17, 20)
]

r_c_x = 0
r_c_y = 0
l_c_x = 0
l_c_y = 0
r_p_x = 0
r_p_y = 0
l_p_x = 0
l_p_y = 0

def generatePointVectors(points, previousFrames):
    vectors = []

    prev_origin_x = 0
    prev_origin_y = 0

    dx = 0
    dy = 0

    if(len(previousFrames) == 0):
        prev_origin_x = 0
        prev_origin_y = 0
    else:
        prev_origin_x = previousFrames[0]
        prev_origin_y = previousFrames[1]

    origin_x, origin_y = points[0]

    origin_x_rounded = round((origin_x), 5)
    origin_y_rounded = round((origin_y), 5)

    dx = origin_x_rounded - prev_origin_x
    dy = origin_y_rounded - prev_origin_y

    vectors.append(origin_x_rounded)
    vectors.append(origin_y_rounded)

    for num, connection in enumerate(connections):

        x0, y0 = points[connection[0]]
        x1, y1 = points[connection[1]]
        x_final = x1 - x0
        y_final = y1 - y0
        mag = math.sqrt((x_final)**2+(y_final)**2)

        x_vector = round((x_final/mag) + dx,5)
        y_vector = round((y_final/mag) + dy,5)

        vectors.append(x_vector)
        vectors.append(y_vector)

    return vectors

def generateCheckPoints(points):
    checkPoints = []

    palm_x, palm_y = points[0]
    thumb_x, thumb_y = points[4]
    index_x, index_y = points[8]
    pinky_x, pinky_y = points[20]

    mean_x = round((palm_x + thumb_x + index_x + pinky_x)/4, 5)
    mean_y = round((palm_y + thumb_y + index_y + pinky_y)/4, 5)

    checkPoints.append(mean_x)
    checkPoints.append(mean_y)

    return checkPoints

def checkPreviousFrame(currCheckPoints, prevCheckPoints):
    current_dx = currCheckPoints[0]
    current_dy = currCheckPoints[1]

    prev_dx = prevCheckPoints[0]
    prev_dy = prevCheckPoints[1]

    dx = round(abs(current_dx - prev_dx), 5)
    dy = round(abs(current_dy - prev_dy), 5)

    if(dx >= d_threshold or dy >= d_threshold):
        print("Thresold crossed.")
        return True, current_dx, current_dy, prev_dx, prev_dy
    else:
        return False, current_dx, current_dy, prev_dx, prev_dy

def recalculateFrames(frames):

    cycledFrames = []
    cycledFrames.extend(frames)
    # Current Origin
    if(len(frames) > 12):
        base_x = cycledFrames[0]
        base_y = cycledFrames[1]

        secondFrame_dx = cycledFrames[12] - base_x
        secondFrame_dy = cycledFrames[13] - base_y

        # New Origin
        new_base_x = cycledFrames[12]
        new_base_y = cycledFrames[13]

        if(len(frames) > 24):
            thirdFrame_dx = cycledFrames[24] - base_x
            thirdFrame_dy = cycledFrames[25] - base_y

            # New second frame
            new_secondFrame_dx = cycledFrames[24] - new_base_x
            new_secondFrame_dy = cycledFrames[25] - new_base_y

            if(len(frames) > 36):
                fourthFrame_dx = cycledFrames[36] - base_x
                fourthFrame_dy = cycledFrames[37] - base_y

                # New third frame
                new_thirdFrame_dx = cycledFrames[36] - new_base_x
                new_thirdFrame_dy = cycledFrames[37] - new_base_y

        i = 12
        while(i < 48):

            # This
            if(i >= 14 and i < 24 and len(frames) > 12):
                cycledFrames[i] = round((cycledFrames[i] - secondFrame_dx), 5)
                cycledFrames[i + 1] = round((cycledFrames[i + 1] - secondFrame_dy), 5)
            # This
            elif(i >= 26 and i < 36 and len(frames) > 24):
                original_keyframe_dx = cycledFrames[i] - thirdFrame_dx
                original_keyframe_dy = cycledFrames[i + 1] - thirdFrame_dy

                cycledFrames[i] = round(original_keyframe_dx + new_secondFrame_dx, 5)
                cycledFrames[i + 1] = round(original_keyframe_dy + new_secondFrame_dy, 5)
            # This
            elif(i >= 38 and i < 48 and len(frames) > 36):
                original_keyframe_dx = cycledFrames[i] - fourthFrame_dx
                original_keyframe_dy = cycledFrames[i + 1] - fourthFrame_dy

                cycledFrames[i] = round(original_keyframe_dx + new_thirdFrame_dx, 5)
                cycledFrames[i + 1] = round(original_keyframe_dy + new_thirdFrame_dy, 5)
            i = i + 2
    # 0 - 11
    # 12 - 23
    # 24 - 35
    # 36 - 47
    # Cycle out
    cycledFrames = cycledFrames[12:]
    return cycledFrames

def preprocessData(frames):

    dataToProcess = []
    dataToProcess.extend(frames)
    if(len(dataToProcess) != 48):
        if(len(dataToProcess) == 12):
            dataToProcess.extend(null_12)
            dataToProcess.extend(null_12)
            dataToProcess.extend(null_12)
        elif(len(dataToProcess) == 24):
            dataToProcess.extend(null_12)
            dataToProcess.extend(null_12)
        elif(len(dataToProcess) == 36):
            dataToProcess.extend(null_12)
        else:
            print("Error in preprocessData. Length of dataToProcess: ", len(dataToProcess))

    group_0 = []
    group_0.extend(dataToProcess[:12])
    group_0.extend(null_12)
    group_0.extend(null_12)
    group_0.extend(null_12)

    group_1 = []
    group_1.extend(dataToProcess[:24])
    group_1.extend(null_12)
    group_1.extend(null_12)

    group_2 = []
    group_2.extend(dataToProcess[:36])
    group_2.extend(null_12)

    group_3 = []
    group_3.extend(dataToProcess[:48])

    arr_0 = np.array(group_0)
    arr_1 = np.array(group_1)
    arr_2 = np.array(group_2)
    arr_3 = np.array(group_3)

    inputData_0 = xgb.DMatrix(arr_0.data)
    inputData_1 = xgb.DMatrix(arr_1.data)
    inputData_2 = xgb.DMatrix(arr_2.data)
    inputData_3 = xgb.DMatrix(arr_3.data)
    # Convert values to DMatrix format
    return xgb.DMatrix(arr_0.data), xgb.DMatrix(arr_1.data), xgb.DMatrix(arr_2.data), xgb.DMatrix(arr_3.data)

def classification(inputData_0, inputData_1, inputData_2, inputData_3, model):
    prob_list_0 = model.predict(inputData_0)[0]
    prob_list_1 = model.predict(inputData_1)[0]
    prob_list_2 = model.predict(inputData_2)[0]
    prob_list_3 = model.predict(inputData_3)[0]

    max_prob_0 = np.amax(prob_list_0)
    max_prob_1 = np.amax(prob_list_1)
    max_prob_2 = np.amax(prob_list_2)
    max_prob_3 = np.amax(prob_list_3)

    out_label_0 = labels["{}".format(np.argmax(prob_list_0, axis=0))]
    out_label_1 = labels["{}".format(np.argmax(prob_list_1, axis=0))]
    out_label_2 = labels["{}".format(np.argmax(prob_list_2, axis=0))]
    out_label_3 = labels["{}".format(np.argmax(prob_list_3, axis=0))]

    label = out_label_0
    prob = max_prob_0

    if(prob < max_prob_1 and max_prob_1 > max_prob_2 and max_prob_1 > max_prob_3):
        prob = max_prob_1
        label = out_label_1
    elif(prob < max_prob_2 and max_prob_2 > max_prob_3 and max_prob_2 > max_prob_1):
        prob = max_prob_2
        label = out_label_2
    elif(prob < max_prob_3 and max_prob_3 > max_prob_1 and max_prob_3 > max_prob_2):
        prob = max_prob_3
        label = out_label_3

    return label, prob

def cleanUp(frames, model):
    temp_frames = []
    temp_frames.extend(frames)

    temp_label = ''
    temp_prob = 0

    if(model is None):
        temp_frames = []
        return temp_frames, temp_label, temp_prob

    while(len(temp_frames) != 0):

        temp_frames = recalculateFrames(temp_frames)
        if(len(temp_frames) != 0):
            # Preprocess
            set0, set1, set2, set3 = preprocessData(temp_frames)
            # Classify
            temp_label, temp_prob = classification(set0, set1, set2, set3, model)
    temp_frames = []
    return temp_frames, temp_label, temp_prob

while cap.isOpened():
    success, image = cap.read()
    if not success:
        break

    # Flip the image horizontally for a later selfie-view display, and convert the BGR image to RGB.
    image = cv2.cvtColor(cv2.flip(image, 1), cv2.COLOR_BGR2RGB)
    # To improve performance, optionally mark the image as not writeable to pass by reference.
    image.flags.writeable = False
    results = hands.process(image)
    if(results.multi_handedness):
        if(len(results.multi_handedness) == 1):
            isMultiHand = False
        else:
            isMultiHand = True
        # results.multi_handedness[0] is first detected hand
        if(results.multi_handedness[0].classification[0].index == 0):  # Index 0 is Left, 1 is Right
            rightHandFirst = False
        else:
            rightHandFirst = True

    # Draw the hand annotations on the image.
    image.flags.writeable = True
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    if results.multi_hand_landmarks:

        rightHandPoints = []
        leftHandPoints = []

        rightVectors = []
        leftVectors = []

        rightCheckPoints = []
        leftCheckPoints = []

        for hand, hand_landmarks in enumerate(results.multi_hand_landmarks):
            if(rightHandFirst):                       # First hand (0) is Right, Second hand (1) is Left
                if(hand == 0):
                    for idx, landmark in enumerate(hand_landmarks.landmark):
                        rightHandPoints.append((landmark.x, landmark.y))
                else:
                    for idx, landmark in enumerate(hand_landmarks.landmark):
                        leftHandPoints.append((landmark.x, landmark.y))
            else:                                     # First hand (0) is Left, Second hand (1) is Right
                if(hand == 0):
                    for idx, landmark in enumerate(hand_landmarks.landmark):
                        leftHandPoints.append((landmark.x, landmark.y))
                else:
                    for idx, landmark in enumerate(hand_landmarks.landmark):
                        rightHandPoints.append((landmark.x, landmark.y))

            if(isMultiHand):
                if(hand == 1):

                    if(len(rightHandPoints) != 0 and len(leftHandPoints) != 0):
                        rightVectors = generatePointVectors(rightHandPoints, rightKeyFrames)
                        rightCheckPoints = generateCheckPoints(rightHandPoints)

                        leftVectors = generatePointVectors(leftHandPoints, leftKeyFrames)
                        leftCheckPoints = generateCheckPoints(leftHandPoints)

                        if(len(rightKeyFrames) == 48):
                            rightKeyFrames = recalculateFrames(rightKeyFrames)
                            print("Right Frame Cycled:", len(rightKeyFrames))
                        if(len(leftKeyFrames) == 48):
                            leftKeyFrames = recalculateFrames(leftKeyFrames)
                            print("Left Frame Cycled:", len(leftKeyFrames))

                        if(addRightFrame == True or addLeftFrame == True):
                            rightKeyFrames.extend(rightVectors)
                            rightKeyCheckPoints.extend(rightCheckPoints)

                            leftKeyFrames.extend(leftVectors)
                            leftKeyCheckPoints.extend(leftCheckPoints)

                            print("Right Added: ", len(rightKeyFrames), "Left Added: ", len(leftKeyFrames))
                            # Preprocess
                            r_set0, r_set1, r_set2, r_set3 = preprocessData(rightKeyFrames)
                            l_set0, l_set1, l_set2, l_set3 = preprocessData(leftKeyFrames)
                            # Classify
                            rightLabel, rightProb = classification(r_set0, r_set1, r_set2, r_set3, modelRight)
                            leftLabel, leftProb = classification(l_set0, l_set1, l_set2, l_set3, modelLeft)

                            addRightFrame = False
                            addLeftFrame = False
                        else:
                            if(len(rightKeyCheckPoints) == 0):
                                rightKeyCheckPoints.extend(rightCheckPoints)
                            else:
                                addRightFrame, r_c_x, r_c_y, r_p_x, r_p_y = checkPreviousFrame(rightCheckPoints, rightKeyCheckPoints)

                            if(len(leftKeyCheckPoints) == 0):
                                leftKeyCheckPoints.extend(leftCheckPoints)
                            else:
                                addLeftFrame, l_c_x, l_c_y, l_p_x, l_p_y = checkPreviousFrame(leftCheckPoints, leftKeyCheckPoints)

                            if(addRightFrame == True or addLeftFrame == True):
                                rightKeyCheckPoints = []
                                leftKeyCheckPoints = []
                                if(len(rightKeyFrames) == 48):
                                    rightKeyFrames = recalculateFrames(rightKeyFrames)
                                    print("Right Frame Cycled:", len(rightKeyFrames))
                                if(len(leftKeyFrames) == 48):
                                    leftKeyFrames = recalculateFrames(leftKeyFrames)
                                    print("Left Frame Cycled:", len(leftKeyFrames))

                    cv2.circle(image, (int(r_c_x * width), int(r_c_y * height)), 3, (255, 0, 0), 2)
                    cv2.circle(image, (int(l_c_x * width), int(l_c_y * height)), 3, (255, 255, 0), 2)

                    cv2.circle(image, (int(r_p_x * width), int(r_p_y * height)), 3, (0, 255, 0), 2)
                    cv2.circle(image, (int(l_p_x * width), int(l_p_y * height)), 3, (0, 255, 255), 2)

            else:

                if(len(rightHandPoints) != 0):
                    rightVectors = generatePointVectors(rightHandPoints, rightKeyFrames)
                    rightCheckPoints = generateCheckPoints(rightHandPoints)

                    if(addRightFrame == True):
                        rightKeyFrames.extend(rightVectors)
                        rightKeyCheckPoints.extend(rightCheckPoints)

                        print("Right Frame Added: ", len(rightKeyFrames))
                        # Preprocess
                        r_set0, r_set1, r_set2, r_set3 = preprocessData(rightKeyFrames)
                        # Classify
                        rightLabel, rightProb = classification(r_set0, r_set1, r_set2, r_set3, modelRight)

                        leftLabel = ''
                        leftProb = 0

                        addRightFrame = False
                    else:
                        if(len(rightKeyCheckPoints) == 0):
                            rightKeyCheckPoints.extend(rightCheckPoints)
                        else:
                            addRightFrame, r_c_x, r_c_y, r_p_x, r_p_y = checkPreviousFrame(rightCheckPoints, rightKeyCheckPoints)

                        if(addRightFrame == True):
                            rightKeyCheckPoints = []
                            if(len(rightKeyFrames) == 48):
                                rightKeyFrames = recalculateFrames(rightKeyFrames)
                                print("Right Frame Cycled:", len(rightKeyFrames))

                    if(len(leftKeyFrames) != 0):
                        leftKeyFrames, leftLabel, leftProb = cleanUp(leftKeyFrames, None)
                        leftLabel = ''

                if(len(leftHandPoints) != 0):
                    leftVectors = generatePointVectors(leftHandPoints, leftKeyFrames)
                    leftCheckPoints = generateCheckPoints(leftHandPoints)

                    if(addLeftFrame == True):
                        leftKeyFrames.extend(leftVectors)
                        leftKeyCheckPoints.extend(leftCheckPoints)

                        print("Left Frame Added: ", len(leftKeyFrames))
                        # Preprocess
                        l_set0, l_set1, l_set2, l_set3 = preprocessData(leftKeyFrames)
                        # Classify
                        leftLabel, leftProb = classification(l_set0, l_set1, l_set2, l_set3, modelLeft)

                        rightLabel = ''
                        rightProb = 0

                        addLeftFrame = False
                    else:
                        if(len(leftKeyCheckPoints) == 0):
                            leftKeyCheckPoints.extend(leftCheckPoints)
                        else:
                            addLeftFrame, l_c_x, l_c_y, l_p_x, l_p_y = checkPreviousFrame(leftCheckPoints, leftKeyCheckPoints)

                        if(addLeftFrame == True):
                            leftKeyCheckPoints = []
                            if(len(leftKeyFrames) == 48):
                                leftKeyFrames = recalculateFrames(leftKeyFrames)
                                print("Left Frame Cycled:", len(leftKeyFrames))

                    if(len(rightKeyFrames) != 0):
                        rightKeyFrames, rightLabel, rightProb = cleanUp(rightKeyFrames, None)
                        rightLabel = ''

                cv2.circle(image, (int(r_c_x * width), int(r_c_y * height)), 3, (255, 0, 0), 2)
                cv2.circle(image, (int(l_c_x * width), int(l_c_y * height)), 3, (255, 255, 0), 2)

                cv2.circle(image, (int(r_p_x * width), int(r_p_y * height)), 3, (0, 255, 0), 2)
                cv2.circle(image, (int(l_p_x * width), int(l_p_y * height)), 3, (0, 255, 255), 2)

            mp_drawing.draw_landmarks(image, hand_landmarks, mp_hands.HAND_CONNECTIONS)

    else:
        if(len(rightKeyFrames) != 0):
            rightKeyFrames, rightLabel, rightProb = cleanUp(rightKeyFrames, modelRight)
            rightLabel = ''
        if(len(leftKeyFrames) != 0):
            leftKeyFrames, leftLabel, leftProb = cleanUp(leftKeyFrames, modelLeft)
            leftLabel = ''

    cv2.putText(image, rightLabel, (width - 200, height - 10), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2, 1)
    cv2.putText(image, str(rightProb), (10, height - 10), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 2, 1)

    # Calculate FPS
    if (time.time() - start_time) > speakWaitTime :
        # Speak
        if rightLabel:
            speakText(rightLabel)
        start_time = time.time()

    cv2.imshow('MediaPipe Hands', image)
    if cv2.waitKey(5) & 0xFF == 27:
        break

hands.close()
cap.release()


from __future__ import unicode_literals

import os
import tempfile
import unittest
from textwrap import dedent

import mock
import six

import rows
import rows.plugins.postgresql
import rows.plugins.utils
import tests.utils as utils
from rows import fields
from rows.plugins.postgresql import pgconnect
from rows.utils import Source

class PluginPostgreSQLTestCase(utils.RowsTestMixIn, unittest.TestCase):

    plugin_name = "postgresql"
    override_fields = {
        "bool_column": fields.BoolField,
        "percent_column": fields.FloatField,
    }
    uri = os.environ["POSTGRESQL_URI"]
    expected_meta = {
        "imported_from": "postgresql",
        "source": Source(uri=uri, plugin_name=plugin_name, encoding=None),
    }

    def get_table_names(self):
        connection = pgconnect(self.uri)
        cursor = connection.cursor()
        cursor.execute(rows.plugins.postgresql.SQL_TABLE_NAMES)
        header = [item[0] for item in cursor.description]
        result = [dict(zip(header, row))["tablename"] for row in cursor.fetchall()]
        cursor.close()
        connection.close()
        return result

    def tearDown(self):
        connection = pgconnect(self.uri)
        for table in self.get_table_names():
            if table.startswith("rows_"):
                cursor = connection.cursor()
                cursor.execute("DROP TABLE " + table)
                cursor.close()
        connection.commit()
        connection.close()

    def test_imports(self):
        self.assertIs(
            rows.import_from_postgresql, rows.plugins.postgresql.import_from_postgresql
        )
        self.assertIs(
            rows.export_to_postgresql, rows.plugins.postgresql.export_to_postgresql
        )

    @mock.patch("rows.plugins.postgresql.create_table")
    def test_import_from_postgresql_uses_create_table(self, mocked_create_table):
        mocked_create_table.return_value = 42
        kwargs = {"encoding": "test", "some_key": 123, "other": 456}
        rows.export_to_postgresql(utils.table, self.uri, table_name="rows_1")
        result = rows.import_from_postgresql(self.uri, table_name="rows_1", **kwargs)
        self.assertTrue(mocked_create_table.called)
        self.assertEqual(mocked_create_table.call_count, 1)
        self.assertEqual(result, 42)

        call = mocked_create_table.call_args
        meta = call[1].pop("meta")
        source = meta.pop("source")
        expected_meta = self.expected_meta.copy()
        expected_source = expected_meta.pop("source")

        self.assertEqual(call[1], kwargs)
        self.assertEqual(meta, expected_meta)
        self.assertEqual(expected_source.uri, source.uri)

    @unittest.skipIf(six.PY2, "psycopg2 on Python2 returns binary, skippging test")
    @mock.patch("rows.plugins.postgresql.create_table")
    def test_import_from_postgresql_retrieve_desired_data(self, mocked_create_table):
        mocked_create_table.return_value = 42
        connection, table_name = rows.export_to_postgresql(
            utils.table, self.uri, table_name="rows_2"
        )
        self.assertTrue(connection.closed)

        # import using uri
        table_1 = rows.import_from_postgresql(
            self.uri, close_connection=True, table_name="rows_2"
        )
        call_args = mocked_create_table.call_args_list[0]
        self.assert_create_table_data(call_args, expected_meta=self.expected_meta)

        # import using connection
        connection = pgconnect(self.uri)
        table_2 = rows.import_from_postgresql(
            connection, close_connection=False, table_name="rows_2"
        )
        self.assertFalse(connection.closed)
        connection_type = type(connection)
        connection.close()

        call_args = mocked_create_table.call_args_list[1]
        meta = call_args[1].pop("meta")
        call_args[1]["meta"] = {}
        self.assert_create_table_data(call_args, expected_meta={})
        self.assertTrue(isinstance(meta["source"].fobj, connection_type))

    def test_postgresql_injection(self):
        with self.assertRaises(ValueError):
            rows.import_from_postgresql(
                self.uri, table_name=('table1","postgresql_master')
            )

        with self.assertRaises(ValueError):
            rows.export_to_postgresql(
                utils.table, self.uri, table_name='table1", "postgresql_master'
            )

    @unittest.skipIf(six.PY2, "psycopg2 on Python2 returns binary, skippging test")
    def test_export_to_postgresql_uri(self):
        rows.export_to_postgresql(utils.table, self.uri, table_name="rows_3")

        table = rows.import_from_postgresql(self.uri, table_name="rows_3")
        self.assert_table_equal(table, utils.table)

    @unittest.skipIf(six.PY2, "psycopg2 on Python2 returns binary, skippging test")
    def test_export_to_postgresql_connection(self):
        connection = pgconnect(self.uri)
        rows.export_to_postgresql(
            utils.table, connection, close_connection=True, table_name="rows_4"
        )

        table = rows.import_from_postgresql(self.uri, table_name="rows_4")
        self.assert_table_equal(table, utils.table)

    @unittest.skipIf(six.PY2, "psycopg2 on Python2 returns binary, skippging test")
    def test_export_to_postgresql_create_unique_table_name(self):
        first_table = utils.table
        second_table = utils.table + utils.table

        table_names_before = self.get_table_names()
        rows.export_to_postgresql(
            first_table, self.uri, table_name_format="rows_{index}"
        )
        table_names_after = self.get_table_names()
        rows.export_to_postgresql(
            second_table, self.uri, table_name_format="rows_{index}"
        )
        table_names_final = self.get_table_names()

        diff_1 = list(set(table_names_after) - set(table_names_before))
        diff_2 = list(set(table_names_final) - set(table_names_after))
        self.assertEqual(len(diff_1), 1)
        self.assertEqual(len(diff_2), 1)
        new_table_1 = diff_1[0]
        new_table_2 = diff_2[0]

        result_first_table = rows.import_from_postgresql(
            self.uri, table_name=new_table_1
        )
        result_second_table = rows.import_from_postgresql(
            self.uri, table_name=new_table_2
        )
        self.assert_table_equal(result_first_table, first_table)
        self.assert_table_equal(result_second_table, second_table)

    @unittest.skipIf(six.PY2, "psycopg2 on Python2 returns binary, skippging test")
    def test_export_to_postgresql_forcing_table_name_appends_rows(self):
        repeat = 3
        for _ in range(repeat):
            rows.export_to_postgresql(utils.table, self.uri, table_name="rows_7")
        expected_table = utils.table
        for _ in range(repeat - 1):
            expected_table += utils.table

        result_table = rows.import_from_postgresql(self.uri, table_name="rows_7")

        self.assertEqual(len(result_table), repeat * len(utils.table))
        self.assert_table_equal(result_table, expected_table)

    @mock.patch("rows.plugins.postgresql.prepare_to_export")
    def test_export_to_postgresql_prepare_to_export(self, mocked_prepare_to_export):
        encoding = "iso-8859-15"
        kwargs = {"test": 123, "parameter": 3.14}
        mocked_prepare_to_export.return_value = iter(
            rows.plugins.utils.prepare_to_export(utils.table)
        )

        rows.export_to_postgresql(
            utils.table, self.uri, encoding=encoding, table_name="rows_8", **kwargs
        )
        self.assertTrue(mocked_prepare_to_export.called)
        self.assertEqual(mocked_prepare_to_export.call_count, 1)

        call = mocked_prepare_to_export.call_args
        self.assertEqual(call[0], (utils.table,))
        kwargs["encoding"] = encoding
        self.assertEqual(call[1], kwargs)

    def test_import_from_postgresql_query_args(self):
        connection, table_name = rows.export_to_postgresql(
            utils.table, self.uri, close_connection=False, table_name="rows_9"
        )
        table = rows.import_from_postgresql(
            connection,
            query="SELECT * FROM rows_9 WHERE float_column > %s",
            query_args=(3,),
        )
        for row in table:
            self.assertTrue(row.float_column > 3)

    def test_pgimport_force_null(self):
        temp = tempfile.NamedTemporaryFile()
        filename = "{}.csv".format(temp.name)
        temp.close()
        self.files_to_delete.append(filename)
        with open(filename, mode="wb") as fobj:
            fobj.write(
                dedent(
                    """
                field1,field2
                "","4"
                ,2
                """
                )
                .strip()
                .encode("utf-8")
            )
        rows.utils.pgimport(
            filename=filename,
            database_uri=self.uri,
            table_name="rows_force_null",
        )
        table = rows.import_from_postgresql(self.uri, "rows_force_null")
        self.assertIs(table[0].field1, None)
        self.assertEqual(table[0].field2, 4)
        self.assertIs(table[1].field1, None)
        self.assertEqual(table[1].field2, 2)


from typing import TypedDict

from gdbdash.commands import BoolOption, StrOption
from gdbdash.modules import Module
from gdbdash.utils import FileDescriptorOrPath

DashboardOptions = TypedDict(
    "DashboardOptions",
    {
        "text-highlight": StrOption,
        "text-secondary": StrOption,
        "text-divider": StrOption,
        "text-divider-title": StrOption,
        "divider-fill-char": StrOption,
        "show-divider": BoolOption,
    },
)

DashboardModulesDict = dict[FileDescriptorOrPath, list[Module]]

class Dashboard:
    def on_order_changed(self) -> None: ...


from itertools import combinations

def answer(l):
	l.sort(reverse=True)

	for i in range(len(l), 0, -1):
		for c in itertools.combinations(l, i):
			if sum(c) % 3 == 0:
				return int(''.join(map(str, c)))

	return 0


from collections import defaultdict
from itertools import permutations

class DinnerTable:
    def __init__(self, instructions) -> None:
        self.ins = instructions
        self.happiness_scores = {}
        self.guest_list = []

    def process_happiness_scores(self):
        lines = [line for line in self.ins.split("\n") if line != ""]
        for line in lines:
            elements = line.split()
            name = elements[0].strip()
            pos_neg = 0
            if elements[2].strip() == "gain":
                pos_neg = 1
            else:
                pos_neg = -1
            score = int(elements[3].strip())
            neighbour = elements[-1][:-1]
            if name not in self.happiness_scores.keys():
                self.happiness_scores[name] = {}
            self.happiness_scores[name].update({neighbour: score * pos_neg})

        self.guest_list = list(self.happiness_scores.keys())

    def add_host_to_happiness_scores(self):
        self.happiness_scores["Host"] = {}
        self.guest_list = list(self.happiness_scores.keys())
        for guest in self.guest_list:
            self.happiness_scores["Host"].update({guest: 0})
            self.happiness_scores[guest].update({"Host": 0})

    def seating_plan_naive(self):
        possible_plans = permutations(self.guest_list)
        possible_plans = [p + (p[0],) for p in possible_plans]

        best_plan = []
        best_plan_score = 0

        for plan in possible_plans:
            happiness = 0
            first = plan[0]
            for neighbour in plan[1:]:
                happiness += self.happiness_scores[first][neighbour]
                happiness += self.happiness_scores[neighbour][first]
                first = neighbour
            if happiness > best_plan_score:
                best_plan_score = happiness
                best_plan = plan
        print(
            f"The happiest plan {best_plan} with a happiness score of {best_plan_score}."
        )

        return best_plan_score

if __name__ == "__main__":
    with open("input.txt", "r") as f:
        instructions = f.read()

    dt = DinnerTable(instructions)
    dt.process_happiness_scores()
    dt.add_host_to_happiness_scores()
    dt.seating_plan_naive()


from ase import Atom, Atoms
from ase.units import Bohr
from gpaw import GPAW
from gpaw.test import equal

def test_xc_nonselfconsistentLDA(in_tmp_dir):
    a = 7.5 * Bohr
    n = 16
    atoms = Atoms([Atom('He', (0.0, 0.0, 0.0))], cell=(a, a, a), pbc=True)
    calc = GPAW(gpts=(n, n, n), nbands=1, xc='LDA')
    atoms.calc = calc
    e1 = atoms.get_potential_energy()
    e1ref = calc.get_reference_energy()
    de12 = calc.get_xc_difference({'name': 'PBE', 'stencil': 1})
    calc = GPAW(gpts=(n, n, n), nbands=1, xc={'name': 'PBE', 'stencil': 1})
    atoms.calc = calc
    e2 = atoms.get_potential_energy()
    e2ref = calc.get_reference_energy()
    de21 = calc.get_xc_difference('LDA')
    print(e1ref + e1 + de12, e2ref + e2)
    print(e1ref + e1, e2ref + e2 + de21)
    print(de12, de21)
    equal(e1ref + e1 + de12, e2ref + e2, 0.02)
    equal(e1ref + e1, e2ref + e2 + de21, 0.025)

    calc.write('PBE.gpw')

    de21b = GPAW('PBE.gpw').get_xc_difference('LDA')
    print(de21, de21b)
    equal(de21, de21b, 9e-8)

    energy_tolerance = 0.0007
    equal(e1, -0.0961003634812, energy_tolerance)  # svnversion 5252
    equal(e2, -0.0790249564625, energy_tolerance)  # svnversion 5252