import dolfinx
import ufl
from mpi4py import MPI

import numpy as np

import os
from scipy.interpolate import interp1d

from Utilities import linstep, project

class Pretreatment:
    def __init__(self, verbose, show_plots):

        self._init_constants()
        self.verbose = verbose
        self.show_plots = show_plots

        self.comm = MPI.COMM_WORLD
        self.rank = self.comm.Get_rank()
        self.num_procs = self.comm.Get_size()

        self.path_to_output = "output"
        self.path_to_data_files = os.path.join(self.path_to_output, "spatially_varying")

        os.makedirs(self.path_to_data_files, exist_ok=True)

    # ================================================================

    def _init_constants(self):

        # Physical Constants
        self.k_b = 1.380649e-23
        self.eta = 2.8e-4  # dynamic viscosity of solvent (water)
        self.R = 8.31446261815324

        # Diffusion coefficient parameters (originally nano-meters)
        self.d_pore = 15.0 / 1.0e9  # pore diameter
        self.r_xy = 0.34 / 1.0e9  # xylose molecular radius
        self.r_xo = 1.02 / 1.0e9  # xylooligomer molecular radius
        self.r_f = 0.28 / 1.0e9  # furfural molecular radius

        # Thermal conductivities
        self.k_s = 0.12  # solid
        self.k_l = 0.63  # liquid
        self.k_g = 0.0197  # gas
        self.h = 5000.0  # convective heat transfer coeff

        # Specific heat capacities
        self.C_s = 2000.0  # solid
        self.C_l = 4350.0  # liquid
        self.C_g = 1970.0  # gas

        # Mass densities
        self.rho_s = 1000.0  # solid
        self.rho_l = 1000.0  # liquid
        self.rho_g = 2.01928202521  # gas
        self.rho_x = 730.0  # xlyan
        self.rho_gl = 1500.0  # glucan
        self.rho_li = 1300.0  # lignin
        self.rho_os = 0.5 * (self.rho_gl + self.rho_li)  # other solids

        # Molecular weights (originally g/mol)
        self.M_x = 132.0 / 1.0e3  # xylan
        self.M_xy = 150.0 / 1.0e3  # xylose
        self.M_xo = 450.0 / 1.0e3  # xylooligomer
        self.M_f = 96.0 / 1.0e3  # furfural
        self.M_w = 18.01528 / 1.0e3  # water

        # Latent heat of steam
        self.L_cond = 2200.0e3

        # Condensation and evaporation rate (s^-1)
        self.k_bar = 10.0

    # ================================================================

    def generate_mesh(self, nn=15):
        self.length = 5e-3 / 2.0
        self.diam = 2.0 * self.length / 20.0

        self.mesh = dolfinx.mesh.create_interval(self.comm, nn, [0.0, self.length])
        self.ndim = self.mesh.topology.dim

    # ================================================================

    def build_functions(self, degree=2):
        d1p3 = ufl.FiniteElement("P", self.mesh.ufl_cell(), degree)
        self.S = dolfinx.fem.FunctionSpace(self.mesh, d1p3)

        self.num_comp = 7

        d1p3_mix = ufl.MixedElement([d1p3 for k in range(self.num_comp)])
        self.Q = dolfinx.fem.FunctionSpace(self.mesh, d1p3_mix)

        self.spaces = []
        self.maps = []
        for i in range(self.num_comp):
            space_i, map_i = self.Q.sub(i).collapse()
            self.spaces.append(space_i)
            self.maps.append(map_i)

        self.v = ufl.TestFunctions(self.Q)

        self.u = dolfinx.fem.Function(self.Q)
        self.u_n = dolfinx.fem.Function(self.Q)

        self.c_s_id = 0
        self.eps_l_id = 1
        self.fstar_x_id = 2
        self.cstar_xo_id = 3
        self.cstar_xy_id = 4
        self.cstar_f_id = 5
        self.T_id = 6

    # ================================================================

    def build_problem(self, ve_params):

        self._define_reaction_rates()

        self._set_initial_conditions(ve_params)
        self._set_boundary_conditions()

        self._build_general_forms()

        self._build_convection_terms()
        self._build_diffusion_terms()
        self._build_reaction_terms()
        self._build_forcing_terms()

        self._build_nonlinear_form()

    # ================================================================

    def _define_reaction_rates(self):
        # Reaction rates (originally in A:cm^3/mol/s and E_A:kJ/mol)
        self.k_xo = 1.0e9 * ufl.exp(-110.0e3 / (self.R * self.u[self.T_id]))
        self.k_x1 = 8.0e11 * ufl.exp(-130.0e3 / (self.R * self.u[self.T_id]))
        self.k_x2 = 2.5e8 * ufl.exp(-110.0e3 / (self.R * self.u[self.T_id]))
        self.k_f = 7.0e5 * ufl.exp(-98.0e3 / (self.R * self.u[self.T_id]))

    # ================================================================

    def _set_initial_conditions(self, ve_params):

        if ve_params is None:
            # self.c_sbulk = 0.14 * 1e3   # bulk steam concentration (from paper, now set by lookup table)
            self.c_acid0 = (
                0.1 * 1e3
            )  # initial acid concentration: Convert mol/self.length to mol/m^3
            self.f_x0 = 0.26  # xylan mass fraction
            self.eps_p0 = 0.8  # initial porosity
            self.f_is0 = 0.44  # initial solid fraction
            self.T_s = 423.15  # steam temperature
            self.glucan_solid_fraction_0 = 0.4
        else:
            self.c_acid0 = ve_params.pt_in["initial_acid_conc"]*1e6 # converting mol/mL into mol/m^3
            self.f_x0 = ve_params.feedstock["xylan_solid_fraction"]
            self.eps_p0 = ve_params.feedstock["initial_porosity"]
            self.f_is0 = ve_params.pt_in["initial_solid_fraction"]
            self.T_s = ve_params.pt_in["steam_temperature"]
            self.glucan_solid_fraction_0 = ve_params.feedstock["glucan_solid_fraction"]

        # Obtain steam concentration from lookup table and add to dictionary
        pt_path = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
        steam_datafile = os.path.join(pt_path, "lookup_tables", "sat_steam_table.csv")
        steam_data = np.loadtxt(steam_datafile, delimiter=",", skiprows=1)

        # build interpolator interp_steam = interp.interp1d(temp_in_K, dens_in_kg/m3)
        interp_steam = interp1d(steam_data[:, 2], steam_data[:, 4])
        steam_density = interp_steam(self.T_s)

        # Convert c_sbulk to (mol/m^3) => density (kg/m^3) / molecular weight (kg/mol)
        self.c_sbulk = steam_density / self.M_w

        self.eps_lt = self.eps_p0  # initial liquid+gas (= 1-solid) volumetric fraction
        self.eps_l0 = 0.25  # initial liquid volumetric fraction
        self.T_0 = 300.0

        ic = dolfinx.fem.Function(self.S)

        # u[self.eps_l_id]
        ic.x.array[:] = self.eps_l0
        self.u.x.array[self.maps[self.eps_l_id]] = ic.x.array[:]
        self.u_n.x.array[self.maps[self.eps_l_id]] = ic.x.array[:]
        # assign(self.u.sub(self.eps_l_id), ic)
        # assign(self.u_n.sub(self.eps_l_id), ic)

        # u[self.fstar_x_id]
        self.fstar_x0 = self.f_x0 * (1.0 - self.eps_p0)
        ic.x.array[:] = self.fstar_x0
        self.u.x.array[self.maps[self.fstar_x_id]] = ic.x.array[:]
        self.u_n.x.array[self.maps[self.fstar_x_id]] = ic.x.array[:]

        # u[self.T_id]
        ic.x.array[:] = self.T_0
        self.u.x.array[self.maps[self.T_id]] = ic.x.array[:]
        self.u_n.x.array[self.maps[self.T_id]] = ic.x.array[:]

    # ================================================================

    def _set_boundary_conditions(self):
        def xlo_wall(x):
            return np.isclose(x[0], 0.0)

        def xhi_wall(x):
            return np.isclose(x[0], self.length)

        xlo_facets = dolfinx.mesh.locate_entities_boundary(
            self.mesh, self.ndim - 1, xlo_wall
        )
        xhi_facets = dolfinx.mesh.locate_entities_boundary(
            self.mesh, self.ndim - 1, xhi_wall
        )

        xhi_c_s_dofs = dolfinx.fem.locate_dofs_topological(
            self.Q.sub(self.c_s_id), self.ndim - 1, xhi_facets
        )
        xhi_T_dofs = dolfinx.fem.locate_dofs_topological(
            self.Q.sub(self.T_id), self.ndim - 1, xhi_facets
        )

        self.bcs = []
        self.bcs.append(
            dolfinx.fem.dirichletbc(
                dolfinx.fem.Constant(self.mesh, self.c_sbulk),
                xhi_c_s_dofs,
                self.Q.sub(self.c_s_id),
            )
        )

        self.bcs.append(
            dolfinx.fem.dirichletbc(
                dolfinx.fem.Constant(self.mesh, self.T_s),
                xhi_T_dofs,
                self.Q.sub(self.T_id),
            )
        )

    # ================================================================

    def _build_general_forms(self):

        aa = self.rho_x / self.rho_os
        # aa = 2.0 # TODO: This is set as 2.0 in the paper/OG code
        bb = self.f_x0 / (1.0 - self.f_x0) + aa
        cc = 1.0 - self.eps_p0

        ss1 = aa**2 * cc**2
        ss2 = 2.0 * (aa - 2.0) * bb * cc * self.u_n[self.fstar_x_id]
        ss3 = bb**2 * self.u_n[self.fstar_x_id] ** 2

        ss = ufl.sqrt(ss1 - ss2 + ss3)

        self.eps_p = -(aa * cc + bb * (self.u_n[self.fstar_x_id] - 2.0) + ss) / (
            2.0 * bb
        )
        self.eps_g = self.eps_p - self.u_n[self.eps_l_id]

        # --------------------------------

        self.c_acid = self.c_acid0 * self.eps_l0 / self.u_n[self.eps_l_id]

        # --------------------------------

        self.D_s = (
            self.eps_g
            * self.d_pore
            * ufl.sqrt((8.0 * self.R * self.u_n[self.T_id]) / (ufl.pi * self.M_w))
            / 3.0
        )

        self.D_xy_0 = (self.k_b * self.u_n[self.T_id]) / (
            6.0 * ufl.pi * self.eta * self.r_xy
        )
        self.D_xo_0 = (self.k_b * self.u_n[self.T_id]) / (
            6.0 * ufl.pi * self.eta * self.r_xo
        )
        self.D_f_0 = (self.k_b * self.u_n[self.T_id]) / (
            6.0 * ufl.pi * self.eta * self.r_f
        )

        self.D_xy = self.u_n[self.eps_l_id] * self.D_xy_0
        self.D_xo = self.u_n[self.eps_l_id] * self.D_xo_0
        self.D_f = self.u_n[self.eps_l_id] * self.D_f_0

        # --------------------------------

        self.rho_eff_c_eff = (
            (1.0 - self.eps_p) * self.rho_s * self.C_s
            + self.u_n[self.eps_l_id] * self.rho_l * self.C_l
            + self.eps_g * self.rho_g * self.C_g
        )
        self.k_eff = (
            (1.0 - self.eps_p) * self.k_s
            + self.u_n[self.eps_l_id] * self.k_l
            + self.eps_g * self.k_g
        )

        # self.heat_flux = (
        #     self.h
        #     / self.k_eff
        #     * (self.u_n[self.T_id] - self.T_s)
        #     * self.v[self.T_id]
        #     * self.ds(1)
        # )  # TODO: fix heat flux specification, currently not used in formulation

        # --------------------------------

        self.k_cond = dolfinx.fem.Function(self.S)
        self.k_cond.x.array[:] = self.k_bar

        self.k_evap = dolfinx.fem.Function(self.S)
        self.k_evap.x.array[:] = 0.0

    # ================================================================

    def _build_convection_terms(self):
        self.conv = [0 for k in range(self.num_comp)]

        self.conv[self.c_s_id] = 0
        self.conv[self.eps_l_id] = 0
        self.conv[self.fstar_x_id] = 0
        self.conv[self.cstar_xo_id] = (
            self.D_xo_0 / self.u_n[self.eps_l_id] * ufl.Dx(self.u_n[self.eps_l_id], 0)
        )
        self.conv[self.cstar_xy_id] = (
            self.D_xy_0 / self.u_n[self.eps_l_id] * ufl.Dx(self.u_n[self.eps_l_id], 0)
        )
        self.conv[self.cstar_f_id] = (
            self.D_f_0 / self.u_n[self.eps_l_id] * ufl.Dx(self.u_n[self.eps_l_id], 0)
        )
        # self.conv[self.cstar_xo_id] = self.D_xo_0 / self.u_n[self.eps_l_id] * ufl.grad(self.u_n[self.eps_l_id])
        # self.conv[self.cstar_xy_id] = self.D_xy_0 / self.u_n[self.eps_l_id] * ufl.grad(self.u_n[self.eps_l_id])
        # self.conv[self.cstar_f_id] = self.D_f_0 / self.u_n[self.eps_l_id] * ufl.grad(self.u_n[self.eps_l_id])
        self.conv[self.T_id] = 0

    # ================================================================

    def _build_diffusion_terms(self):
        self.diff = [0 for k in range(self.num_comp)]

        self.diff[self.c_s_id] = self.D_s
        self.diff[self.eps_l_id] = 0
        self.diff[self.fstar_x_id] = 0
        self.diff[self.cstar_xo_id] = self.D_xo_0
        self.diff[self.cstar_xy_id] = self.D_xy_0
        self.diff[self.cstar_f_id] = self.D_f_0
        self.diff[self.T_id] = self.k_eff / self.rho_eff_c_eff

    # ================================================================

    def _build_reaction_terms(self):
        self.reac = [0 for k in range(self.num_comp)]

        self.reac[self.c_s_id] = -self.k_cond * self.eps_g
        self.reac[self.eps_l_id] = (
            -self.M_w * self.k_cond * self.u_n[self.c_s_id] / self.rho_l - self.k_evap
        )
        self.reac[self.fstar_x_id] = -(self.k_xo + self.k_x1) * self.c_acid * self.eps_p
        self.reac[self.cstar_xo_id] = -self.k_x2 * self.c_acid
        self.reac[self.cstar_xy_id] = -self.k_f * self.c_acid
        self.reac[self.cstar_f_id] = 0
        self.reac[self.T_id] = -4.0 * self.h / (self.rho_eff_c_eff * self.diam)
        # self.reac[self.T_id] = -4.0 * self.h / self.diam

    # ================================================================

    def _build_forcing_terms(self):
        self.forc = [0 for k in range(self.num_comp)]

        self.forc[self.c_s_id] = (
            self.k_evap
            * (self.u_n[self.eps_l_id] - self.eps_lt)
            * self.rho_l
            / self.M_w
        )
        self.forc[self.eps_l_id] = (
            self.k_cond * self.u_n[self.c_s_id] * self.eps_p * self.M_w / self.rho_l
            + self.k_evap * self.eps_lt
        )
        self.forc[self.fstar_x_id] = 0
        self.forc[self.cstar_xo_id] = (
            self.k_xo
            * self.rho_s
            * self.c_acid
            * self.eps_p
            * self.u_n[self.fstar_x_id]
            / self.M_xo
        )
        self.forc[self.cstar_xy_id] = (
            self.k_x1
            * self.rho_s
            * self.c_acid
            * self.eps_p
            * self.u_n[self.fstar_x_id]
            / self.M_xy
            + self.k_x2
            * self.u_n[self.cstar_xo_id]
            * self.c_acid
            * self.M_xo
            / self.M_xy
        )
        self.forc[self.cstar_f_id] = (
            self.k_f * self.u_n[self.cstar_xy_id] * self.c_acid * self.M_xy / self.M_f
        )
        self.forc[self.T_id] = (
            self.L_cond
            / self.rho_eff_c_eff
            * (
                self.k_cond * self.u_n[self.c_s_id] * self.M_w * (self.eps_g)
                - self.k_evap * self.rho_l * (self.u_n[self.eps_l_id] - self.eps_lt)
            )
            + 4.0 * self.h / (self.rho_eff_c_eff * self.diam) * self.T_s
        )
        # self.forc[self.T_id] = (
        #     (self.L_cond / self.rho_eff_c_eff)
        #     * (
        #         self.k_cond * self.u_n[self.c_s_id] * self.M_w * (self.eps_g)
        #         - self.k_evap * self.rho_l * (self.u_n[self.eps_l_id] - self.eps_lt)
        #     )
        #     + 4.0 * self.h / self.diam * self.T_s
        # )

    # ================================================================

    def _build_nonlinear_form(self):
        self.F = 0

        # Use a placeholder value of dt to build the dolfinx.fem.form, it will
        # be updated with the correct value in solve(...)
        self.dt_c = dolfinx.fem.Constant(self.mesh, 1.0)

        for k in range(self.num_comp):

            self.F += (1.0 / self.dt_c) * (self.u[k] - self.u_n[k]) * self.v[k] * ufl.dx

            if self.conv[k] != 0:
                # BEST ONE (move the ufl.grad onto the test space and swap sign)
                self.F -= self.conv[k] * self.u[k] * ufl.Dx(self.v[k], 0) * ufl.dx

                # self.F += ufl.Dx(self.conv[k]*self.u[k], 0) * self.v[k] * ufl.dx
                # self.F -= ufl.inner(self.conv[k]*self.u[k], ufl.grad(self.v[k]))*ufl.dx
                # self.F += dot(self.conv[k], ufl.grad(self.u[k]))*self.v[k]*ufl.dx

            if self.diff[k] != 0:
                # BEST ONE (move the ufl.grad onto the test space and swap sign)
                self.F += (
                    self.diff[k] * ufl.Dx(self.u[k], 0) * ufl.Dx(self.v[k], 0) * ufl.dx
                )

                # self.F += self.diff[k] * ufl.inner(ufl.grad(self.u[k]), ufl.grad(self.v[k])) * ufl.dx
                # self.F += self.diff[k] * dot(ufl.grad(self.u[k]), ufl.grad(self.v[k])) * ufl.dx
                # self.F += ufl.inner(self.diff[k] * ufl.grad(self.u[k]), ufl.grad(self.v[k])) * ufl.dx

            if self.reac[k] != 0:
                self.F -= self.reac[k] * self.u[k] * self.v[k] * ufl.dx

            if self.forc[k] != 0:
                self.F -= self.forc[k] * self.v[k] * ufl.dx

        # TODO: fix heat flux specification, currently not used in formulation
        # self.F += self.heat_flux

    # ================================================================

    def solve(self, t_final=30.0, dt=1.0, save_every=2.0):

        # Time stepping parameters
        self.dt = dt
        self.dt_c.value = self.dt

        self.t_final = t_final
        t_steps = int(self.t_final / self.dt)
        save_every_n = int(save_every / self.dt)

        tt = 0
        self.write_solution(tt)

        du = ufl.TrialFunction(self.Q)
        J = ufl.derivative(self.F, self.u, du)

        nonlinear_problem = dolfinx.fem.petsc.NonlinearProblem(self.F, self.u, self.bcs)
        nonlinear_solver = dolfinx.nls.petsc.NewtonSolver(self.comm, nonlinear_problem)

        for k in range(t_steps):
            # Solve the nonlinear problem
            nonlinear_solver.solve(self.u)

            # Extract a numpy vector for temp and eps_l
            np_T = self.u.x.array[self.maps[self.T_id]]
            np_eps_l = self.u.x.array[self.maps[self.eps_l_id]]

            self._update_k_cond(np_T)
            self._update_k_evap(np_T, np_eps_l)

            # Update previous solution
            self.u_n.x.array[:] = self.u.x.array[:]

            tt = (k + 1) * self.dt

            if (k + 1) % save_every_n == 0 or (k+1) == t_steps:
                self.write_solution(tt)

    # ================================================================

    def _update_k_cond(self, temp):

        T_tol = 0.001
        del_T = T_tol * self.T_s
        out_vec = linstep(temp, self.T_s - del_T, self.T_s + del_T, self.k_bar, 0.0)

        self.k_cond.x.array[:] = out_vec

    # ================================================================

    def _update_k_evap(self, temp, liq):

        T_tol = 0.001
        del_T = T_tol * self.T_s
        out_vec = linstep(temp, self.T_s - del_T, self.T_s + del_T, 0.0, self.k_bar)

        eps_lt_tol = 0.1
        del_eps_lt = eps_lt_tol * self.eps_lt
        out_vec_2 = linstep(liq, self.eps_lt, self.eps_lt + del_eps_lt, 0.0, 1.0)
        out_vec *= out_vec_2
        # TODO: needed to soften the transition to nonzero k_evap on
        # only one side of eps_lt

        relaxation_method = 3

        if relaxation_method == 1:
            k_evap_scaling = 0.05
            k_evap_new = k_evap_scaling * out_vec

        elif relaxation_method == 2:
            k_evap_old = self.k_evap.vector()[:]

            relaxation_time = 10.0
            delta_k_evap = 1.0 / relaxation_time * (out_vec - k_evap_old)

            k_evap_new = k_evap_old + delta_k_evap

            # k_evap_new *= 0.4

        elif relaxation_method == 3:
            k_evap_new = out_vec

        self.k_evap.x.array[:] = k_evap_new

    # ================================================================

    def write_solution(self, tt):

        self._save_spatially_varying_quantities(tt)
        self._write_integrated_quantities(tt)

    def _save_spatially_varying_quantities(self, tt):
        output_array = []

        data_header = "X-position (cm),"
        xpts = self.S.tabulate_dof_coordinates()[:, 0]
        xpts = xpts * 100.0
        xpts_id = np.argsort(xpts)
        output_array.append(xpts[xpts_id])

        # Steam (M)
        data_header += "Steam (M),"
        c_s_vec = self.u.x.array[self.maps[self.c_s_id]]
        c_s_vec = c_s_vec / 1000.0
        output_array.append(c_s_vec[xpts_id])

        # Liquid Fraction
        data_header += "Liquid Fraction,"
        eps_l_vec = self.u.x.array[self.maps[self.eps_l_id]]
        output_array.append(eps_l_vec[xpts_id])

        # Temperature (K)
        data_header += "Temperature (K),"
        T_vec = self.u.x.array[self.maps[self.T_id]]
        output_array.append(T_vec[xpts_id])

        # Acid (M)
        data_header += "Acid (M),"
        acid_fn = dolfinx.fem.Function(self.S)
        acid_fn = project(self.c_acid, acid_fn)
        acid_vec = acid_fn.x.array[:] / 1000.0
        output_array.append(acid_vec[xpts_id])

        # Xylan Fraction
        data_header += "Xylan Fraction,"
        f_x_fn = dolfinx.fem.Function(self.S)
        f_x_fn = project(self.u[self.fstar_x_id] / (1.0 - self.eps_p), f_x_fn)
        f_x_vec = f_x_fn.x.array[:]
        output_array.append(f_x_vec[xpts_id])

        # Xylooligomers (M)
        data_header += "Xylooligomer (M),"
        c_xo_vec = self.u.x.array[self.maps[self.cstar_xo_id]]
        c_xo_vec = c_xo_vec / eps_l_vec / 1000.0
        output_array.append(c_xo_vec[xpts_id])

        # Xylose (M)
        data_header += "Xylose (M),"
        c_xy_vec = self.u.x.array[self.maps[self.cstar_xy_id]]
        c_xy_vec = c_xy_vec / eps_l_vec / 1000.0
        output_array.append(c_xy_vec[xpts_id])

        # Furfural (mM)
        data_header += "Furfural (mM)"
        c_f_vec = self.u.x.array[self.maps[self.cstar_f_id]]
        c_f_vec = c_f_vec / eps_l_vec
        output_array.append(c_f_vec[xpts_id])

        output_array = np.array(output_array).T
        output_array_filename = os.path.join(
            self.path_to_data_files, f"data_t{tt:05.0f}s.csv"
        )

        np.savetxt(
            output_array_filename,
            output_array,
            delimiter=",",
            header=data_header,
        )

    def _write_integrated_quantities(self, tt):
        eps_l_bar = dolfinx.fem.assemble_scalar(
            dolfinx.fem.form(self.u[self.eps_l_id] * ufl.dx)
        )
        eps_p_bar = dolfinx.fem.assemble_scalar(
            dolfinx.fem.form((1.0 - self.eps_p) * ufl.dx)
        )

        # This is equivalent to dolfinx.fem.assemble_scalar(...) since fstar_x0 is scalar
        fstar_x0_bar = self.fstar_x0 * self.length / eps_p_bar
        fstar_x_bar = (
            dolfinx.fem.assemble_scalar(
                dolfinx.fem.form(self.u[self.fstar_x_id] * ufl.dx)
            )
            / eps_p_bar
        )
        cstar_xo_bar = (
            dolfinx.fem.assemble_scalar(
                dolfinx.fem.form(self.u[self.cstar_xo_id] * ufl.dx)
            )
            / eps_l_bar
        )
        cstar_xy_bar = (
            dolfinx.fem.assemble_scalar(
                dolfinx.fem.form(self.u[self.cstar_xy_id] * ufl.dx)
            )
            / eps_l_bar
        )
        cstar_f_bar = (
            dolfinx.fem.assemble_scalar(
                dolfinx.fem.form(self.u[self.cstar_f_id] * ufl.dx)
            )
            / eps_l_bar
        )

        fis = self.rho_s * eps_p_bar / (self.rho_s * eps_p_bar + self.rho_l * eps_l_bar)

        converted_mass = fstar_x0_bar - fstar_x_bar
        conversion_percent = converted_mass / fstar_x0_bar

        glucan_solid_fraction = self.glucan_solid_fraction_0 / (
            1.0 - self.f_x0 * conversion_percent
        )

        # Olga's version from intuition (very close to above!)
        # glucan_f = (1.0 - fstar_x_bar)*(self.glucan_solid_fraction_0)/(1.0 - self.f_x0)

        if not hasattr(self, "integrated_quantities"):
            self.integrated_quantities = {}

        self.integrated_quantities["fis_0"] = fis
        self.integrated_quantities["conv"] = conversion_percent
        self.integrated_quantities["X_X"] = fstar_x_bar
        self.integrated_quantities["X_G"] = glucan_solid_fraction
        self.integrated_quantities["rho_x"] = (
            cstar_xo_bar * self.M_xo + cstar_xy_bar * self.M_xy
        )
        self.integrated_quantities["rho_f"] = cstar_f_bar * self.M_f

        if not hasattr(self, "integrated_quantities_array"):
            self.integrated_quantities_array = []

        data_header = [
            "Time (s)",
            "Xylan (g/g)",
            "Xylog (g/L)",
            "Xylose (g/L)",
            "Furfural (g/L)",
            "FIS (g/g)",
            "rho_x (g/g)",
            "X_G (g/g)",
            "conv",
        ]

        self.integrated_quantities_array.append(
            [
                tt,
                fstar_x_bar,
                cstar_xo_bar * self.M_xo,
                cstar_xy_bar * self.M_xy,
                cstar_f_bar * self.M_f,
                fis,
                cstar_xo_bar * self.M_xo + cstar_xy_bar * self.M_xy,
                glucan_solid_fraction,
                conversion_percent,
            ]
        )

        if self.verbose:
            for label, value in zip(data_header, self.integrated_quantities_array[-1]):
                print(f"{label}: {value}")
            print()

        output_array = np.array(self.integrated_quantities_array)
        output_array_filename = os.path.join(
            self.path_to_output, f"spatially_averaged_timeseries.csv"
        )

        np.savetxt(
            output_array_filename,
            output_array,
            delimiter=",",
            header=",".join(data_header),
        )

    # ================================================================


"""
Cho dãy số A[], B[] và C[] là dãy không giảm và có lần lượt N, M, K phần tử. Nhiệm vụ của bạn là hãy tìm các phần tử chung của 3 dãy số này.
Input:
3
6 5 8
1 5 10 20 40 80
5 7 20 80 100
3 4 15 20 30 70 80 120
3 5 4
1 5 5
3 4 5 5 10
5 5 10 20
3 3 3
1 2 3
4 5 6
7 8 9
Output:
20 80
5 5
NO
"""
if __name__ == "__main__":
    t = int(input())
    while t > 0:
        n, m, k = map(int, input().split())
        a = [int(x) for x in input().split()]
        b = [int(x) for x in input().split()]
        c = [int(x) for x in input().split()]
        dem, x, y, z = 0, 0, 0, 0
        while x < n and y < m and z < k:
            if a[x] == b[y] == c[z]:
                print(a[x], end=' ')
                dem += 1
                x, y, z = x + 1, y + 1, z + 1
            elif a[x] < b[y]:
                x += 1
            elif b[y] < c[z]:
                y += 1
            else:
                z += 1
        if dem == 0: print("NO", end=' ')
        print()
        t -= 1


import math
import serialCmd
import time
class robot:
    """
    Commands and Repsonses(->)
    # MOVE
    <M, 24, 0, 0, 2, 0, 50> # Send a Move command IN STEPS
    -> <M, Y> Success
    -> <M, N> Failure

    # HOME
    <H, 0, 0, 0, 0, 0, 0> # ONLY THE H IS READ, starts a homing proceedure
    -> <H, Y> Success (Homing angles are known in Pyhton already obviously 0 steps maps to those angles)
    -> <M, N> Failure

    # INFO
    <I,> # INFO Get info of robot arm
    -> <I,j1,j2,j3,j4,vac,speed>

    # SETUP
    <S, microstep > # Other things that need to be changed? speed? Speed is same for all motors?
    -> <S, Y> # Microstepping set (should update speeds and accelerations)

    # CONTROL
    <C, Stop, Pause, DisableMotors > # Priority left to right set to 1 or zero, could be 0,1, or 2 also?
    -> <C, Y> # Control Done, should ech back what it did?

    """
    def __init__(self):
        self.serial = serialCmd.Serial_cmd()
        # if not self.serial.connected:
        #     print("Please Connect Arduino")
        #     quit()

        self.stepsPerRev = 200      # Number of steps in 1 rev of the steppers
        self.P1xyz = (0,0,110)       # Only the Z offset does anything
        self.L1 = 322               # mm length of first Arm Bearing to bearing
        self.L2 = 322               # 2nd Arm
        self.L3 = 17.5                # Length of rotating end effector
        self.maxExtension=650           # How far out can the arm go!
        #
        self.J1microSteps = 16
        self.J2microSteps = 16
        self.J3microSteps = 16
        self.J1gearing = 3
        self.J2gearing = 3
        self.J3gearing = 3

        # Radian values for the limit switches
        self.limitJ1 = math.pi/2 + math.radians(13.4) # + math.radians(13.8)
        self.limitJ2 = math.radians(87)
        self.limitJ2min = math.radians(0)
        self.limitJ3 = math.pi/2+math.radians(92)
        self.limitJ3min = math.radians(85)

        self.lookingForBlock = True
        self.j4Offset = 0 # THis is used for non perpendicular picks!

        print(f"j1Limit: {self.limitJ1}\nj2Limit: {self.limitJ2}\nj3Limit: {self.limitJ3}")
        self.j1ZeroSteps = self.radToSteps(self.limitJ1, self.J1microSteps, self.J1gearing)
        self.j2ZeroSteps = self.radToSteps(self.limitJ2, self.J2microSteps, self.J2gearing)
        self.j3ZeroSteps = self.radToSteps(self.limitJ3, self.J3microSteps, self.J3gearing)

        # Offsets from Pivot point to servo Horn
        self.servoArmOffset = 32.5
        self.servoZOffset = 35

        self.suctionOffset = 0 # Also could be droop

        # This should just be steps each stepper is currently Commanded to? Not in Use
        self.theta1 = 0
        self.theta2 = 0
        self.theta3 = 0
        self.theta4 = 0
        self.lastObjective = ''

        # ETC
        self.commands = {}
        self.commands["info"] = 'I'
        self.commands["move"] = 'M'
        self.commands["home"] = 'H'
        self.commands["other"] = 'O'
        self.commands["control"] = 'C'
        self.commands["calibrate"] = 'B'

        # self.infoChar = 'I'
        # self.move = "M"
        # self.info = "I"
        # self.home = "H"
        # self.other = "O"
        # self.control = "C"
        # self.timeout = "T"
        self.handshakeChar = "A"
        self.successChar = "Y"
        self.failChar = "N"
        self.splitChar = ","
        self.startChar = '<'
        self.endChar = '>'

    def worldToJoint(self, coords, thetab):
        print("Block Rotation!:", thetab)
        '''
        Takes True world frame coordinates of the block! thetab is relative to block
        coords: tuple looks like -> (xb,yb,zb)
        '''

        # CHeck to see if the arm is going to a bad place!
        if coords[1] < -75 or 150 > math.sqrt(coords[0]**2 + coords[1]**2) or  math.sqrt(coords[0]**2 + coords[1]**2) > self.maxExtension:
            print("Cannot reach this position")
            return False

        armAngle = math.degrees(math.atan2(-coords[0], coords[1]))
        print(f"jOffset: {self.j4Offset}")
        if thetab-armAngle <=-90:
            print("Left Side")
            thetab += 180
        elif thetab-armAngle >= 90:
            thetab -= 180
            print("RightSide")

        if self.lookingForBlock:
            self.j4Offset = 0   # Reset the offset and see if we need a new offset for where we are going
             # Determine if this is a hard to pick spot

            print(f"Angle for looking at parallel picks:{abs(thetab-armAngle)}")
            # This means we should pick at a different angle!
            if abs(thetab-armAngle) > 80 and abs(thetab-armAngle < 100):
                #Left
                if coords[0]<0:
                    self.j4Offset = 45
                else: #Right
                    self.j4Offset = -45
        else:
            # Keep using the offset that is already here
            pass
        # First lets assume we are choosing the position of the end effector ie 45*
        thetabRad = math.radians(thetab+self.j4Offset)

        # Calculate the xyz of arm
        #First work backwords from block to position the end of servo
        za = coords[2] + self.suctionOffset #Z_arm

        xa = coords[0] + self.L3*math.sin(thetabRad)
        ya = coords[1] - self.L3*math.cos(thetabRad)

        # Now calculate the position of the rotating joint of the arm
        za +=  self.servoZOffset

        # Time for Law of Cosines
        effectiveArmShadow = math.sqrt(xa**2 + ya**2)-self.servoArmOffset # imagine shinging a light directly above the arm
        zextra = (za-self.P1xyz[2]) # bottom of law of cosines trinagle

        b = math.sqrt(zextra**2 + effectiveArmShadow**2)

        gamma = math.atan2(zextra, effectiveArmShadow)

        # NOTE THETSE ARE BOTH Zero when they point straight out
        theta2 = math.acos((self.L1**2 + b**2 - self.L2**2)/(2*self.L2*b)) + gamma
        theta3 = math.acos((self.L1**2 - b**2 + self.L2**2)/(2*self.L1*self.L2)) + theta2
        theta1 = math.atan2(-xa, ya) # Flip the coordinates since we need to offset by pi/2 this allows us to handle negatives!

        #print(theta1, theta2, theta3)

        # if theta2 > self.limitJ2 or theta2 < self.limitJ2min:
        #     return None
        # if theta3 > self.limitJ3 or theta3 < self.limitJ3min:
        #     return None

        theta4 = -(theta1 - thetabRad)
        print("theta4:", math.degrees(theta4))
        return (theta1, theta2, theta3, theta4)

    def jointToWorld(self, stepPos):
        jPos = self.stepTupleToRadTuple(stepPos)

        theta1 = jPos[0]
        theta2 = jPos[1]
        theta3 = jPos[2]   # Angle of stepper arm to the angle of the triangle
        theta4 = jPos[3]

        thetaI = theta3 - (math.pi/2)

        #print(f"{theta1}, {theta2}, {theta3}, {theta4}, {thetaI}")
        middleJointZ = self.L1 * math.sin(theta2) - self.L2*math.cos(thetaI)
        middleJointArm = self.L1 * math.cos(theta2) + self.L2*math.sin(thetaI)

        # Now lets offset to servo, Please lets design this so the horn is in line
        servoZ = middleJointZ - self.servoZOffset
        servoArm = middleJointArm + self.servoArmOffset

        # Now lets decompose the arm
        servoHornX = servoArm * math.sin(-1*theta1)
        servoHornY = servoArm * math.cos(theta1)

        #Uncomment to look at the pivot point
        #print(f"{self.P1xyz[0] + servoHornX}, {self.P1xyz[1] + servoHornY}, {self.P1xyz[2] + servoZ}")

        # now do the rotation of the end effector
        eofRot = theta1 + theta4
        eofX = self.L3 * math.sin(eofRot)
        eofY = self.L3 * math.cos(eofRot)

        # Apply EOF Z adjustment
        finalX = self.P1xyz[0] + servoHornX - eofX
        finalY = self.P1xyz[1] + servoHornY + eofY
        finalZ = self.P1xyz[2] + servoZ - self.suctionOffset
        return (finalX,finalY,finalZ)

    def radTupleToStepTuple(self, jPos):
        if len(jPos) != 4:
            print("Error!")
            return
        else:
            step1 = self.radToSteps(jPos[0], self.J1microSteps, self.J1gearing)
            step2 = self.radToSteps(jPos[1], self.J2microSteps, self.J2gearing)
            step3 = self.radToSteps(jPos[2], self.J3microSteps, self.J3gearing)
            eofDegrees = math.degrees(jPos[3])
            return (int(step1), int(step2), int(step3), int(eofDegrees))

    def stepTupleToRadTuple(self, stepPos):
        if len(stepPos) != 4:
            print("Error!")
            return
        rad1 = self.stepsToRads(stepPos[0], self.J1microSteps, self.J1gearing)
        rad2 = self.stepsToRads(stepPos[1], self.J2microSteps, self.J2gearing)
        rad3 = self.stepsToRads(stepPos[2], self.J3microSteps, self.J3gearing)
        rad4 = math.radians(stepPos[3])
        return (rad1, rad2, rad3, rad4)

    def radToSteps(self, rad, microSteps, gearing, stepsRev = 200):
        '''
        Conversion from radians to a number of steps
        given ceratin steps per revolution and microstepping
        '''
        return rad/(2*math.pi) * stepsRev * microSteps * gearing

    def stepsToRads(self, steps, microSteps, gearing, stepsRev = 200):
        return steps/(stepsRev * microSteps * gearing) * (2*math.pi)

    def waitForResponse(self):
        while 1:
            if(self.serial.dev.in_waiting > 0):
                #print(f"bytes:{s.dev.in_waiting}")
                data = self.serial.read().strip()[1:-1] #gets data of form <I,435,45,4,3,3,2>
                #print(f"data: {data} type: {type(data)}")
                if not data: continue
                if (data[0] == self.commands["info"]):
                    # PARSE INFO INTO A LIST
                    data_tup = tuple('('+data+')')
                    #infoList.append(data_tup)
                    if(self.lastObjective == self.commands["info"]):
                        print(data[2:])
                        return data[2:]
                        break

                if(data[0] == self.lastObjective):
                    resultChar = data.split(self.splitChar)[-1]
                    return(resultChar == self.successChar)
                    break
            time.sleep(.02)
        return

    def waitForArduino(self):
        # Established serial connection
        while 1:
            var = self.serial.dev.read().decode()
            print(f"{var, type(var)}")
            if var == self.handshakeChar:
                self.serial.write(self.handshakeChar)
                print("Handshake Done!")
                self.serial.dev.reset_input_buffer() # may be uneccessary
                data = self.serial.read().strip()     # This line is essential and somehow reads the empty line that is output at the end of Setup
                break

    def createMessage(self, messageType, jPos, vac = 0, pump = 0):
        """
        Sends form startChar j1,j2,j3,j4,vac,speed endChar
        ie. <M,0,20,30,50,1.0,50>
        """
        # Make sure that jPos etc are trimmed to x decimals
        # Should we send angles or steps?
        self.lastObjective = messageType
        message = (self.startChar+messageType+self.splitChar+str(jPos[0])+self.splitChar+str(jPos[1])
                    +self.splitChar+str(jPos[2])+self.splitChar+str(jPos[3])
                    +self.splitChar+str(vac)+self.splitChar+str(pump)+self.endChar)
        return message

    def moveTo(self, x, y, z, theta, suction, pump):
        # Go to a position
        jPos = self.worldToJoint((x, y, z), theta)
        if not jPos:
            print("Arm is unable to get to reach this position!")
            return False
        stepPos = self.radTupleToStepTuple(jPos)
        nextPoint = self.createMessage(self.commands["move"], stepPos, vac = float(suction), pump = float(pump))
        self.serial.write(nextPoint)
        print(f"sent message: {nextPoint}")
        result = self.waitForResponse()
        print(result)
        return True

    def home(self):
        homeTuple = (self.j1ZeroSteps ,self.j2ZeroSteps, self.j3ZeroSteps, 0)
        homingMessage = self.createMessage(self.commands["home"],homeTuple,0,0)
        self.serial.write(homingMessage)
        print(f"Homing: {homingMessage}")
        result = self.waitForResponse()
        print(result)

    def calibrate(self):
        calibratingMessage = self.createMessage(self.commands["calibrate"],(0, 0, 0, 0),0,0)
        self.serial.write(calibratingMessage)
        print(f"Calibrating: {calibratingMessage}")
        result = self.waitForResponse()
        print(result)

    def controlVacPump(self, vac=1,pump=1):
        controlMessage = self.createMessage(self.commands["control"], (0, 0, 0, 0), int(vac), int(pump))
        self.serial.write(controlMessage)
        print(f"Calibrating: {controlMessage}")
        result = self.waitForResponse()
        print(result)

    def calcSmoothPick(self, coords, approachZ=5, stepSize=1, extract=False):
        """
        Calculates approach for the block using several points!
        """
        print("SMOOTH")
        x = coords[0]
        y = coords[1]
        z = coords[2]
        rotation = coords[3]

        tupleApproach = []

        steps = int(approachZ/stepSize)+1

        for i in range(steps):
            tupleApproach.append((x,y,z+i*stepSize,rotation))

        if not extract:
            return reversed(tupleApproach)
        return tupleApproach

    def calcSmoothPlace(self, coords, approachZ=15, approachTangent=20,steps = 15,direction=1, behind=False):
            rotation = coords[3]
            deltaX = approachTangent * math.sin(math.radians(rotation)) * direction
            deltaY = approachTangent * math.cos(math.radians(rotation)) * direction
            if behind:
                deltaX *= -1
                deltaY *= -1

            stepX = deltaX/steps
            stepY = deltaY/steps
            stepZ = approachZ/steps

            tupleApproach = []
            for i in range(steps):
                tupleApproach.append((coords[0]+i*stepX,coords[1]+i*stepY,coords[2]+i*stepZ,rotation))

            if not behind:
                return reversed(tupleApproach)
            return tupleApproach


import os

params = {"local_folder": r"C:\Users\pedri\OneDrive\Área de Trabalho\Stuff\Python\Aulas\29-06-23-basic-data\dataset"}

def rename_files(folder):

    renamed_files = []
    non_renamed_files = []

    for file in os.listdir(folder):
        if "_dataset.csv" in file:
            renamed_files.append(file)
            old_filepath = os.path.join(folder, file)
            new_name = file.split("_", 1)[1].replace("_dataset", "")
            #new_name = left_split[1].replace("_dataset", "")
            new_filepath = os.path.join(folder, new_name)
            os.rename(old_filepath, new_filepath)
        else:
            non_renamed_files.append(file)

    return {"Renamed files": renamed_files, "Not renamed": non_renamed_files}

if __name__ == "__main__":

    rename_files(folder=params["local_folder"])


from collections import deque
import copy
import sys
sys.setrecursionlimit(10000)

n, m = map(int, input().split())
ground = [list(map(int, sys.stdin.readline().split())) for _ in range(n)]
d = deque()
res = 0

dx = [1, 0, -1, 0]
dy = [0, -1, 0, 1]

def wall(s, l):
    if l == 3:
        virus()
        return
    else:
        for i in range(s, n*m):
            x = i // m
            y = i % m
            if not ground[x][y]:
                ground[x][y] = 1
                wall(i+1, l+1)
                ground[x][y] = 0

def virus():
    global res
    graph = copy.deepcopy(ground)
    for i in range(n):
        for j in range(m):
            if graph[i][j] == 2:
                d.append((i,j))
    while d:
        x, y = d.popleft()
        for i in range(4):
            nx = x + dx[i]
            ny = y + dy[i]
            if 0<=nx<n and 0<=ny<m and not graph[nx][ny]:
                d.append((nx, ny))
                graph[nx][ny] = 2

    cnt = 0
    for row in graph:
        cnt += row.count(0)
    if cnt > res:
        res = cnt

wall(0, 0)
print(res)


# -*- coding: utf-8 -*-
"""
Created on Sun Dec  6 22:45:46 2020

@author: wanglei
"""

#DBSCAN聚类模型在模拟blob数据集上的聚类实验

import numpy as np
from sklearn.cluster import DBSCAN
from sklearn import metrics
from sklearn.datasets import make_blobs
import matplotlib.pyplot as plt

# 1. 获得数据集
n_samples = 750 #数量
X, y = make_blobs(n_samples=n_samples, centers=3,random_state=123456)

#2. DBSCAN模型创建和训练
#模型参数
#  eps: 邻居半径（默认0.5）
#  min_samples: minPts(成为核心样本时半径内的最少样本数）
db = DBSCAN( eps=0.8, metric= 'euclidean',min_samples=10)
db.fit(X)
# 聚类结果,-1代表样本点是噪声点,其余值表示样本点所属的类（0，1）
labels = db.labels_
# 聚类数量
n_clusters_ = len(set(labels)) - (1 if -1 in labels else 0)

#3. 聚类模型评价
print('Estimated number of clusters: %d' % n_clusters_)
#轮廓系数
print("Silhouette Coefficient: %0.3f"  % metrics.silhouette_score(X, labels))
#Rand指数
print("Adjusted Rand Index: %0.3f" % metrics.adjusted_rand_score(y, labels))

##############################################################################
# 4. 绘图
#4.1 获得核心对象的掩码
core_samples_mask = np.zeros_like(db.labels_, dtype=bool)
core_samples_mask[db.core_sample_indices_] = True

#4.2 绘制原始数据
plt.figure(figsize=(9, 9))
plt.scatter(X[:,0], X[:,1],marker='o',color= 'b')
plt.title("original data")

#4.3 绘制DBSCAN的聚类结果
plt.figure(figsize=(9, 9))
unique_labels = set(labels)
colors = plt.cm.Spectral(np.linspace(0, 1, len(unique_labels)))
i = -1
marker = ['v','^','o','x']  #依次表示第0，1类，噪声的marker
for k, col in zip(unique_labels, colors):
    if k == -1:
        col = 'k'  # 黑色表示标记噪声点.
    class_member_mask = (labels == k)

    i += 1
    if (i>=len(unique_labels)):
        i = 0

    # 绘制核心对象
    xy = X[class_member_mask & core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], marker[i], markerfacecolor=col,
             markeredgecolor='k', markersize=14)
    # 绘制非核心对象和噪声
    xy = X[class_member_mask & ~core_samples_mask]
    plt.plot(xy[:, 0], xy[:, 1], marker[i], markerfacecolor=col,
             markeredgecolor='k', markersize=6)
plt.title('Estimated number of clusters: %d' % n_clusters_)
plt.show()


# -*- coding: utf-8 -*-
"""
@author: Yifan Cheng, Skylar Du, Yashash Gaurav, Mark He
"""

# Importing required packages
import numpy as np
import pandas as pd
from fuzzywuzzy import fuzz
from fuzzywuzzy import process

# Declaring global variables
bestcolleges_data_path = "bestcolleges_careers.csv"
bestcolleges_data = pd.read_csv(bestcolleges_data_path)

# Print all the careers
def view_all_careers():
    print("We are ready to guide you on the following careers : ")
    for index, careers_name in enumerate(
        bestcolleges_data.career_name.to_list()
    ):
        print(str(index + 1) + ". " + careers_name)

# Search and print all information for the given career
def view_career_info_by_name(searched_career):

    # Doing a partial search to find the search term in the database.
    closest_result = process.extractOne(
        searched_career,
        bestcolleges_data.career_name.to_list(),
        scorer=fuzz.partial_ratio,
    )  # Selects rows that meet the search criteria

    # If we have results, show!
    if len(closest_result) > 0:
        search_result_row = bestcolleges_data[
            bestcolleges_data.career_name == closest_result[0]
        ]
        print(
            "Here is some guidance on " + search_result_row.career_name.iloc[0]
        )

        # If data point exists, show else skip the section
        if search_result_row.career_info is not np.nan:
            print("----  Info ----")
            print(search_result_row.career_info.iloc[0])

        if search_result_row.why_career is not np.nan:
            print(
                "----  Why " + search_result_row.career_name.iloc[0] + " ----"
            )
            print(search_result_row.why_career.iloc[0])

        if search_result_row.how_to_start.iloc[0] is not np.nan:
            print(
                "----  How to start on "
                + search_result_row.career_name.iloc[0]
                + " ----"
            )
            print(search_result_row.how_to_start.iloc[0])


def fibonacci(n):
    fn = [0, 1, ]
    for i in range(2, n):
        fn.append(fn[i - 1] + fn[i - 2])
    return fn

id = int(input())

list1 = fibonacci(id)
str1 = ', '.join(str(e) for e in list1)

print(str1)


import sys
sys.path.append("Documents/Codes/OSIDH")
from sage.all import *
from OSIDH_protocol import *
from time import time

# Usual toy Parameters
n=28
t=10
l=2
r=3
# so that h\simeq l**n\simeq (2r+1)**t
d_K=-4

osidh=OSIDH(n,t,l,r,d_K)
C=Chain(osidh)

t1=time()
L_chains_hor=[]
for j in range(0):
	chain=C
	L_plus=[]
	for k in range(osidh.r):
		chain=chain.action_prime(osidh.L_mfq[j],j)
		L_plus.append(chain.L_j[-1])
	chain=C
	L_minus=[]
	for k in range(osidh.r):
		chain=chain.action_prime(osidh.L_mfq_inv[j],j)
		L_minus.append(chain.L_j[-1])
	L_chains_hor.append(Chain_hor(osidh,j,chain.L_j[-1],L_plus,L_minus))
t2=time()
print("Time horizontal chains: {0} s".format(t2-t1))

t1=time()
#chain_test=L_chains_hor[0].action_step(1,L_chains_hor[1].L_plus[0],-3)
t2=time()
print("Time action_step: {0} s".format(t2-t1))

t1=time()
#chain_test=L_chains_hor[0].action_chain(L_chains_hor[1],-3,2)
t2=time()
print("Time action_chain: {0} s".format(t2-t1))

t1=time()
OSIDH_exe(osidh,C)
t2=time()
print("OSIDH execution time: {0} s".format(t2-t1))


from dash import Dash, dcc, html, Input, Output, callback
import dash_bootstrap_components as dbc

app = Dash(external_stylesheets=[dbc.themes.COSMO])
server=app.server
rxn_temp = dcc.Input(value='', type='number',placeholder='temperature',max=300)
my_output = html.Div()
vial_vol = dcc.Input(value='', type='number',placeholder='vial vol',min=1,max=50)
gas_mmol = dcc.Input(value='0',type='number',placeholder='mmol gas',min=0)
vol_solvent = dcc.Input(value='',type='number')
#need a way of error proofing vol_solvent < vial_vol (child dependency?)

def calculate_pressure(vial_vol,vol_solvent,rxn_temp,mmol_gas_generated):
    total_gas = float(vial_vol) - float(vol_solvent)
    mmol_gas = (total_gas/24.5) + float(mmol_gas_generated)
    temp_ideal_gas= float(rxn_temp) + 273
    print(mmol_gas)
    pressure = (mmol_gas/1000)*8.314*(temp_ideal_gas)/(total_gas/1E6)
    return round(pressure/100000, 2)

app.layout = html.Div([
    html.H1("Sealed vessel risk assesment tool"),
    html.Div([
        "Reaction temp: ",
        rxn_temp,
    ],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(["Volume of vial (mL): ",vial_vol],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(["mmol of gas generated in reaction: ",gas_mmol],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(["total volume of solvent used (mL): ",vol_solvent],style={'padding': 10, 'flex': 1}),
    html.Br(),
    html.Div(),
    html.Button('Run', id='apply-button', n_clicks=0),
    my_output
])

if rxn_temp != None:
    @callback(
        Output(my_output, component_property='children'),
        [Input('apply-button','n_clicks')],
        Input(vial_vol, component_property='value'),
        Input(vol_solvent,component_property='value'),
        Input(rxn_temp,component_property='value'),
        Input(gas_mmol,component_property='value')
    )
    def update_output_div(n_clicks,vial_vol,vol_solvent,rxn_temp,gas_mmol):
        if n_clicks > 0:
            delta_vol = calculate_pressure(vial_vol,vol_solvent,rxn_temp,gas_mmol)
            n_clicks = 0
            if delta_vol < 7:
                return f'The reaction was conducted in vial of volume {vial_vol} mL, with total solvent volume of {vol_solvent} mL at a max reaction temperature of {rxn_temp}, this generates a pressure of {delta_vol} bar, assuming ideal gas and no vapor pressure'
            else:
                return f'pressure is {delta_vol} bar, recommended that the maximum operating pressure be no more than 100 psi (~7 bar). '
        else:
            return 'not calaculated yet'

if __name__ == '__main__':
    app.run_server(debug=True)


import pytest

@pytest.fixture
def create_dict(app):
    from app.main.config_helper import get_dict_by_name
    obj = get_dict_by_name("Sample")
    return obj

def test_add_word(create_dict):
    init_len = len(create_dict)
    create_dict.add_word('Москва', 12)  # нет городов в словаре -> точная гарантия добавления
    assert ('Москва' in create_dict.word_list)
    assert ('Москва' in create_dict.dictionary.keys())
    assert init_len + 1 == len(create_dict)
    create_dict.add_word('Москва', 12)
    assert init_len + 1 == len(create_dict)

def test_del_word(create_dict):
    init_len = len(create_dict)
    create_dict.del_word('сон')
    assert 'сон' not in create_dict.word_list
    assert 'сон' not in create_dict.dictionary.keys()
    assert init_len - 1 == len(create_dict)
    create_dict.del_word('сон')
    assert init_len - 1 == len(create_dict)

def test_del_random_word(create_dict):
    init_len = len(create_dict)
    word = create_dict.pop_random_word()
    assert word not in create_dict.word_list
    assert word not in create_dict.dictionary.keys()
    assert init_len - 1 == len(create_dict)
    assert type(word) == str

@pytest.fixture
def create_immutable_dict():
    from app.main.config_helper import get_dict_instance_by_name
    obj = get_dict_instance_by_name("Sample")
    return obj

def test_get_dict_for_game(create_immutable_dict):
    a = create_immutable_dict.get_dict_for_game(3000, 10, 1)  # проверка расширения границ
    assert len(a) == 3000
    assert type(a) == dict
    b = create_immutable_dict.get_dict_for_game(30000, 50, 50)  # берем больше, чем в словаре. в нем 22580
    assert len(b) == 22580

from words_controller.dictionary import SimpleDictionary

def test_create_simple_from_instance_dictionary(create_dict):
    simple_dict = create_dict.create_simple_dictionary(1000, 10, 1)
    assert type(simple_dict) == SimpleDictionary
    assert len(simple_dict) == 1000

@pytest.fixture
def create_simple_dict():
    from app.main.config_helper import get_simple_dict_by_name
    obj = get_simple_dict_by_name("Sample")
    return obj

def test_simple_initializtion():
    word_list = ["заяц0", "заяц1", "заяц2"]
    dictionary = SimpleDictionary(**{'dictionary': word_list})
    assert len(dictionary) == 3
    for i in range(3):
        assert f'заяц{str(i)}' in dictionary.word_list

def test_simple_get_dict_for_game(create_simple_dict):
    dictionary = create_simple_dict.get_dict_for_game(500)
    assert type(dictionary) == list
    assert len(dictionary) == 500

def test_simple_add_word(create_simple_dict):
    init_len = len(create_simple_dict)
    create_simple_dict.add_word('Москва')
    assert 'Москва' in create_simple_dict.word_list
    assert init_len + 1 == len(create_simple_dict)
    create_simple_dict.add_word('Москва')
    assert init_len + 1 == len(create_simple_dict)

def test_simple_del_word(create_simple_dict):
    init_len = len(create_simple_dict)
    assert 'такси' in create_simple_dict.word_list
    create_simple_dict.del_word('такси')
    assert 'такси' not in create_simple_dict.word_list
    assert init_len - 1 == len(create_simple_dict)
    create_simple_dict.del_word('такси')
    assert init_len - 1 == len(create_simple_dict)


# -*- coding: utf-8 -*-
##############################################################################
# Author：QQ173782910
##############################################################################

import traceback
import time
import random
from basic.JD_TOOL import robot_bugerror
from basic.DB_TOOL import DB_pg3
from dbconfig import scott, tiger, host, port, dbname
from moment.MTOOL import GetData
from function_scheduling_distributed_framework import patch_frame_config,task_deco,fsdf_background_scheduler, timing_publish_deco
patch_frame_config(REDIS_HOST='127.0.0.1',REDIS_PASSWORD='',REDIS_PORT=6379,REDIS_DB=7)

try:
    link =[host,port, dbname,scott, tiger]
    db = DB_pg3(link)
except:
    raise NameError('db link error')

def get_random_no(E_R=2, T=''):
    if E_R == 0:
        cur_random_no = "%s%s" % (time.time(), random.random())
        return cur_random_no.replace('.', '')
    elif E_R == 1:
        timeStamp = time.time()
        timeArray = time.localtime(timeStamp)
        danhao = time.strftime("%Y%m%d%H%M%S", timeArray)
        romcode = str(time.time()).split('.')[-1]  # [3:]
        order_number = T + danhao[2:] + romcode
        return order_number

    timeStamp = time.time()
    timeArray = time.localtime(timeStamp)
    danhao = time.strftime("%Y%m%d%H%M%S", timeArray)
    romcode = str(time.time()).split('.')[-1]  # [3:]
    upperCase = [
        'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
        'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
    ]
    Dstr = random.sample(upperCase, E_R)
    D_c = ''.join(Dstr)
    random_no = D_c + danhao[2:] + romcode
    return random_no

def time_to(ti):
    ti = ti.replace("T", " ").replace(".000Z", "")
    data_sj = time.strptime(ti, "%Y-%m-%d %H:%M:%S")  # 定义格式
    time_int = int(time.mktime(data_sj))
    return time_int

@task_deco('printlog',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def printlog(cname,errors):
    sql="insert into print_log(cname,errors,ctime)values(%s,%s,now())"
    db.query(sql,[cname,errors])
    return

@task_deco('ustatus',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def update_status(usr_id,us_id,status):

    try:
        sql = "update user_strategy set status=%s,utime=now() where usr_id=%s and id=%s;"
        db.query(sql, [status, usr_id, us_id])
    except:
        robot_bugerror(traceback, ctype='update_status')
    return

@task_deco('tlog',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def trading_log(remark,usr_id,symbol,num,tp_okexs):
    #us_id  user_strategy的id
    try:
        id=get_random_no(E_R=0)
        sql="""insert into trading_log(id,usr_id,deal_num,instrument_id,remark,ctime,tp_okexs)
        values(%s,%s,%s,%s,%s,now(),%s);"""
        db.query(sql,[id,usr_id,num,symbol,remark,tp_okexs])
    except:
        robot_bugerror(traceback, ctype='trading_log')
    return

@task_deco('sp',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def save_position(usr_id,instrument_id,pt,us_id,result,ctype,client_oid,remark):
    try:
        if ctype != 0:#2永续
            robot_bugerror('获取持仓故障%s:%s:%s'%(client_oid,instrument_id,result), ctype='ok')
            return

        longnum, longcost, longpnl, shortnum, shortcost, shortpnl, long_pnl_ratio, short_pnl_ratio,last = pt

        if longnum>0:
            profit_loss = longpnl
        else:
            profit_loss = shortpnl

        sql = """update user_strategy set longnum=%s,longcost=%s,longpnl=%s,
                shortnum=%s,shortcost=%s,shortpnl=%s,ptime=now(),long_pnl_ratio=%s,short_pnl_ratio=%s,
                hold_position=%s,profit_loss=%s where id=%s;"""
        parm = [longnum, longcost, longpnl, shortnum, shortcost, shortpnl,
                long_pnl_ratio, short_pnl_ratio,longnum,profit_loss, us_id]

        db.query(sql, parm)
        if longnum == 0 and shortnum == 0:
            update_reset.push(usr_id, us_id)

        if longnum != 0 or shortnum != 0:
            sql = """update positions set holds=%s,results=%s,ctime=now(),longnum=%s,longcost=%s,longpnl=%s,
            shortnum=%s,shortcost=%s,shortpnl=%s,long_pnl_ratio=%s,short_pnl_ratio=%s,lasts=%s where client_oid=%s;"""
            parm = [remark, '%s' % result,
                    longnum,longcost,longpnl,shortnum,shortcost,shortpnl,long_pnl_ratio,short_pnl_ratio,last,client_oid]
            db.query(sql, parm)

    except:
        robot_bugerror(traceback, ctype='save_position')
    return

@task_deco('spt',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def stop_profit(usr_id,instrument_id,ptlist,results,ctype,deal_num,highest,lowhest,client_oid):
    try:
        longnum, longcost, longpnl, shortnum, shortcost, shortpnl, long_pnl_ratio, short_pnl_ratio, last = ptlist
        if ctype==0:
            if longnum == 0 and shortnum==0:
                return
            id=get_random_no(E_R=0)
            sql="""insert into stop_profit(id,usr_id,instrument_id,
            longnum,longcost,longpnl,shortnum,shortcost,shortpnl,ctime,long_pnl_ratio,short_pnl_ratio,
            results,lasts,highest,lowhest,client_oid)
            values(%s,%s,%s,%s,%s,%s,%s,%s,%s,now(),%s,%s,%s,%s,%s,%s,%s);"""
            parm=[id,usr_id,instrument_id,longnum,longcost,longpnl,shortnum,
                  shortcost,shortpnl,long_pnl_ratio,short_pnl_ratio,results,last,highest,lowhest,client_oid]
            db.query(sql,parm)
        elif ctype==1:
            if longnum!=0:
                #def spl_log(remark, symbol, deal_num, num, costs, pnl, lasts):
                stop_profit_log.push(results, instrument_id, deal_num, longnum, longcost, longpnl, last,highest,lowhest,client_oid)#多单
            elif shortnum!=0:
                stop_profit_log.push(results, instrument_id, deal_num, shortnum, shortcost, shortpnl, last,highest,lowhest,client_oid)#空单
    except:
        robot_bugerror(traceback, ctype='stop_profit')
    return

@task_deco('splog',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def stop_profit_log(remark,symbol,deal_num,num,costs,pnl,lasts,highest,lowhest,client_oid):

    try:
        id=get_random_no(E_R=0)
        sql="""insert into stop_profit_log(id,instrument_id,deal_num,num,costs,pnl,lasts,remark,ctime,highest,lowhest,client_oid)
        values(%s,%s,%s,%s,%s,%s,%s,%s,now(),%s,%s,%s);"""
        db.query(sql,[id,symbol,deal_num,num,costs,pnl,lasts,remark,highest,lowhest,client_oid])
    except:
        robot_bugerror(traceback, ctype='stop_profit_log')
    return

@task_deco('upre',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def update_reset(usr_id,us_id):
    #ctype 0空单，1多单
    try:
        sql = """update user_strategy set h_longpnl='0',h_shortpnl='0',
        htime=now(),h_last='0',l_last='0' where usr_id=%s and id=%s;"""
        parm = [usr_id, us_id]
        db.query(sql, parm)
    except:
        robot_bugerror(traceback, ctype='update_reset')
    return

@task_deco('uep',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def update_earnings_price(usr_id,us_id,ctype,flag,last,pnl,client_oid):
    #ctype 0:空单,1:多单
    #flag  'H'高价，'L':低价
    try:
        if ctype==0:#空单
            if flag=='H':#更高价
                sql = """update user_strategy set htime=now(),h_last=%s where usr_id=%s and id=%s;
                update positions set highest=%s where client_oid=%s;
                """
                parm = [last, usr_id, us_id,last,client_oid]
                db.query(sql, parm)
            else:#更低价
                sql = """update user_strategy set h_shortpnl=%s,htime=now(),l_last=%s where usr_id=%s and id=%s;
                update positions set lowhest=%s where client_oid=%s;
                """
                parm = [pnl, last, usr_id, us_id,last,client_oid]
                db.query(sql, parm)
        else:#多单
            if flag == 'H':#更高价
                sql = """update user_strategy set h_longpnl=%s,htime=now(),h_last=%s where usr_id=%s and id=%s;
                 update positions set highest=%s where client_oid=%s;
                """
                parm = [pnl, last, usr_id, us_id,last,client_oid]
                db.query(sql, parm)
            else:#更低价
                sql = """update user_strategy set htime=now(),l_last=%s where usr_id=%s and id=%s;
                update positions set lowhest=%s where client_oid=%s;
                """
                parm = [last, usr_id, us_id,last,client_oid]
                db.query(sql, parm)
    except:
        robot_bugerror(traceback, ctype='update_earnings_price')
    return

@task_deco('cou',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def client_oid_update(us_id,client_oid,usr_id,symbol):
    try:
        id = get_random_no(E_R=0)
        sql = """update user_strategy set client_oid=%s where id=%s;
        insert into positions(id,usr_id,instrument_id,client_oid,ctime)values(%s,%s,%s,%s,now());
        """
        parm = [client_oid, us_id,id,usr_id,symbol,client_oid]
        db.query(sql, parm)

    except:
        robot_bugerror(traceback, ctype='client_oid_update')
    return

@task_deco('gsc',log_level=50,is_print_detail_exception=False,create_logger_file=False,broker_kind=2)
def get_symbol_check():
    try:
        sql = """select
                    t_instrument_id,tp_okexs
                    from user_strategy where coalesce(del_flag,0)=0 and coalesce(status,0) not in (0,5)
                    """
        l, t = db.select(sql)
        if t > 0:
            for i in l:
                symbol, time_period=i
                GetData(symbol,time_period).signal_chekc()

    except:
        robot_bugerror(traceback, ctype='get_symbol_check')
    return

if __name__ == '__main__':
    fsdf_background_scheduler.add_job(timing_publish_deco(get_symbol_check), 'interval', id='timing10s', seconds=10)

    # 启动定时
    fsdf_background_scheduler.start()
    ################################# 日志任务
    printlog.consume()#策略运行日志
    trading_log.consume()#策略运行日志
    stop_profit_log.consume()#止盈日志
    save_position.consume()#持仓处理
    ############################
    update_status.consume()#更新状态
    stop_profit.consume()#止盈处理
    update_reset.consume()#止盈记录
    update_earnings_price.consume()#更新收益与价格记录
    client_oid_update.consume()#
    ###############
    get_symbol_check.consume()  #


lista = [int(i) for i in input("").split(" ")]

def mergesort(arry):
	if(len(arry) > 1):
		mid = len(arry)//2
		L = arry[:mid]
		R = arry[mid:]

		mergesort(L)
		mergesort(R)

		i = j = k = 0

		while (i < len(L) > 0 and j <len(R)):

			if(L[i] < R[j]):
				arry[k] = L[i]
				i += 1

			else:
				arry[k] = R[j]
				j +=1
			k += 1

		while(i <len(L)):
			arry[k] = L[i]
			k += 1
			i += 1
		while(j < len(R)):
			arry[k] = R[j]
			k += 1
			j += 1

mergesort(lista)
print(lista)


from typing import Any, Dict, List, Tuple, Union

import numpy as np
import torch
from torch import Tensor

from flash.core.data.base_viz import BaseVisualization
from flash.core.data.io.input import DataKeys
from flash.core.utilities.imports import _MATPLOTLIB_AVAILABLE, Image, requires
from flash.core.utilities.stages import RunningStage
from flash.image.segmentation.output import SegmentationLabelsOutput

if _MATPLOTLIB_AVAILABLE:
    import matplotlib.pyplot as plt
else:
    plt = None

class SegmentationMatplotlibVisualization(BaseVisualization):
    """Process and show the image batch and its associated label using matplotlib."""

    def __init__(self, labels_map: Dict[int, Tuple[int, int, int]]):
        super().__init__()

        self.max_cols: int = 4  # maximum number of columns we accept
        self.block_viz_window: bool = True  # parameter to allow user to block visualisation windows
        self.labels_map: Dict[int, Tuple[int, int, int]] = labels_map

    @staticmethod
    @requires("image")
    def _to_numpy(img: Union[Tensor, Image.Image]) -> np.ndarray:
        out: np.ndarray
        if isinstance(img, np.ndarray):
            out = img
        elif isinstance(img, Image.Image):
            out = np.array(img)
        elif isinstance(img, Tensor):
            out = img.squeeze(0).permute(1, 2, 0).cpu().numpy()
        else:
            raise TypeError(f"Unknown image type. Got: {type(img)}.")
        return out

    @requires("matplotlib")
    def _show_images_and_labels(
        self,
        data: List[Any],
        num_samples: int,
        title: str,
        limit_nb_samples: int = None,
        figsize: Tuple[int, int] = (6.4, 4.8),
    ):
        num_samples = max(1, min(num_samples, limit_nb_samples))

        # define the image grid
        cols: int = min(num_samples, self.max_cols)
        rows: int = num_samples // cols

        # create figure and set title
        fig, axs = plt.subplots(rows, cols, figsize=figsize)
        fig.suptitle(title)

        if not isinstance(axs, np.ndarray):
            axs = np.array(axs)
        axs = axs.flatten()

        for i, ax in enumerate(axs):
            # unpack images and labels
            sample = data[i]
            if isinstance(sample, dict):
                image = sample[DataKeys.INPUT]
                label = sample[DataKeys.TARGET]
            elif isinstance(sample, tuple):
                image = sample[0]
                label = sample[1]
            else:
                raise TypeError(f"Unknown data type. Got: {type(data)}.")
            # convert images and labels to numpy and stack horizontally
            image_vis: np.ndarray = self._to_numpy(image)
            label_tmp: Tensor = SegmentationLabelsOutput.labels_to_image(
                torch.as_tensor(label).squeeze(), self.labels_map
            )
            label_vis: np.ndarray = self._to_numpy(label_tmp)
            img_vis = np.hstack((image_vis, label_vis))
            # send to visualiser
            ax.imshow(img_vis)
            ax.axis("off")
        plt.show(block=self.block_viz_window)

    def show_load_sample(
        self,
        samples: List[Any],
        running_stage: RunningStage,
        limit_nb_samples: int,
        figsize: Tuple[int, int] = (6.4, 4.8),
    ):
        win_title: str = f"{running_stage} - show_load_sample"
        self._show_images_and_labels(samples, len(samples), win_title, limit_nb_samples, figsize)

    def show_per_sample_transform(
        self,
        samples: List[Any],
        running_stage: RunningStage,
        limit_nb_samples: int,
        figsize: Tuple[int, int] = (6.4, 4.8),
    ):
        win_title: str = f"{running_stage} - show_per_sample_transform"
        self._show_images_and_labels(samples, len(samples), win_title, limit_nb_samples, figsize)


try:
    import os
    import re
    import sys
    import requests
    import validators
except ModuleNotFoundError:
    print("[-] Requirements not satisfied!\n\trun 'pip install -r requirements.txt'\n\t\tOR\n\trun 'pip3 install -r requirements.txt'")
    quit()

def print_docs():
    docs = f"""
    Documentation:
        Base Usage: {"python" if os.name == "nt" else "python3"} {sys.argv[0]} [URL] [FLAGS]

        Flags:
            -a:
                Gets Phone Numbers & Email Adresses from the URL
            -e:
                Gets Only Email Adresses from the URL
            -p:
                Gets Only Phone Numbers from the URL
            -O [FILENAME]:
                Saves Scraped Data to a file.
    """
    print(docs)

def log(data, filename):
    with open(filename, "a") as f:
        for info in data:
            f.write(f"{info}\n")

def harvest_emails(data, saveEmails=False, filename=None):
    pattern = r"([\d\w\.]+@[\d\w\.\-]+\.\w+)"

    if re.search(pattern, data):
        emails = set(re.findall(pattern, data))
        for email in emails:
            print(f"[+] Email Found: {email}")
        print(f"[*] Found {len(emails)} emails\n")
        if saveEmails:
            log(emails, filename)
            print(f"[*] Saved Harvested Emails in '{filename}'\n")
    else:
        print("[-] Did not find any valid emails")

def harvest_phones(data, savePhones=False, filename=None):
    pattern = r"(\(?[0-9]{3}\)?(?:\-|\s|\.)?[0-9]{3}(?:\-|\.)[0-9]{4})"

    if re.search(pattern, data):
        nums = set(re.findall(pattern, data))
        for num in nums:
            print(f"[+] Phone Number Found: {num}")
        print(f"[*] Found {len(nums)} phone numbers\n")
        if savePhones:
            log(nums, filename)
            print(f"[*] Saved Harvested Phone Numbers in '{filename}'\n")
    else:
        print("[-] Did not find any valid phone numbers")

def harvest(data, save=False, filename=None):
    harvest_emails(data, save, filename)
    harvest_phones(data, save, filename)

def main():
    if len(sys.argv) >= 2:
        if "--help" in sys.argv:
            print_docs()
            sys.exit()
        url = sys.argv[1]
        isValid = validators.url(url)
        if isValid:
            try:
                r = requests.get(url)
            except:
                print("[-] The requested URL did not respond back")
                sys.exit()
            data = r.text
            if "-a" in sys.argv:
                if "-O" in sys.argv:
                    try:
                        filename = sys.argv[sys.argv.index("-O") + 1]
                    except IndexError:
                        print("[-] Please provide a valid filename")
                        sys.exit()
                    harvest(data, True, filename)
                else:
                    harvest(data)
            elif "-e" in sys.argv:
                if "-O" in sys.argv:
                    try:
                        filename = sys.argv[sys.argv.index("-O") + 1]
                    except IndexError:
                        print("[-] Please provide a valid filename")
                        sys.exit()
                    harvest_emails(data, True, filename)
                else:
                    harvest_emails(data)
            elif "-p" in sys.argv:
                if "-O" in sys.argv:
                    try:
                        filename = sys.argv[sys.argv.index("-O") + 1]
                    except IndexError:
                        print("[-] Please provide a valid filename")
                        sys.exit()
                    harvest_phones(data, True, filename)
                else:
                    harvest_phones(data)
            else:
                print(f"[-] Please provide a valid flag operation")
        else:
            print(f"[-] Invalid URL")
            sys.exit()
    else:
        print(f"[-] Invalid Usage:\n\tUsage: {'python' if os.name == 'nt' else 'python3'} {sys.argv[0]} [URL] [FLAGS]")
        print(f"\tTry '{'python' if os.name == 'nt' else 'python3'} {sys.argv[0]} --help' for help")
        sys.exit()

if __name__ == "__main__":
    main()


"""
Tests for plotting operations
"""

import itertools
import os
import shutil
import sys
import tempfile
import unittest
from contextlib import contextmanager
from unittest import TestCase

import numpy as np
import pandas as pd
import xarray as xr

from cate.core.op import OP_REGISTRY
from cate.ops.plot import plot, plot_map, plot_data_frame, plot_hovmoeller
from cate.util.misc import object_to_qualified_name

_counter = itertools.count()
ON_WIN = sys.platform == 'win32'

@contextmanager
def create_tmp_file(name, ext):
    tmp_dir = tempfile.mkdtemp()
    path = os.path.join(tmp_dir, '{}{}.{}'.format(name,
                                                  next(_counter),
                                                  ext))
    try:
        yield path
    finally:
        try:
            shutil.rmtree(tmp_dir)
        except OSError:
            if not ON_WIN:
                raise

@unittest.skipIf(condition=os.environ.get('CATE_DISABLE_PLOT_TESTS', None),
                 reason="skipped if CATE_DISABLE_PLOT_TESTS=1")
class TestPlotMap(TestCase):
    """
    Test plot_map() function
    """

    def test_plot_map(self):
        # Test the nominal functionality. This doesn't check that the plot is what is expected,
        # rather, it simply tests if it seems to have been created
        dataset = xr.Dataset({
            'first': (['lat', 'lon', 'time'], np.random.rand(5, 10, 2)),
            'second': (['lat', 'lon', 'time'], np.random.rand(5, 10, 2)),
            'lat': np.linspace(-89.5, 89.5, 5),
            'lon': np.linspace(-179.5, 179.5, 10),
            'time': pd.date_range('2000-01-01', periods=2)})

        with create_tmp_file('remove_me', 'png') as tmp_file:
            plot_map(dataset, file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

        # Test if an error is raised when an unsupported format is passed
        with create_tmp_file('remove_me', 'pgm') as tmp_file:
            with self.assertRaises(ValueError):
                plot_map(dataset, file=tmp_file)
            self.assertFalse(os.path.isfile(tmp_file))

        # Test if extents can be used
        with create_tmp_file('remove_me', 'pdf') as tmp_file:
            plot_map(dataset,
                     var='second',
                     region='-40.0, -20.0, 50.0, 60.0',
                     file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

        # Test time slice selection
        with create_tmp_file('remove_me', 'png') as tmp_file:
            plot_map(dataset, time='2000-01-01', file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

    def test_plot_map_exceptions(self):
        # Test if the corner cases are detected without creating a plot for it.

        # Test value error is raised when passing an unexpected dataset type
        with create_tmp_file('remove_me', 'jpeg') as tmp_file:
            with self.assertRaises(ValueError) as cm:
                plot_map([1, 2, 4], file=tmp_file)
            self.assertEqual(str(cm.exception),
                             "Input 'ds' for operation 'cate.ops.plot.plot_map' "
                             "must be of type 'Dataset', but got type 'list'.")
            self.assertFalse(os.path.isfile(tmp_file))

        # Test the extensions bound checking
        dataset = xr.Dataset({
            'first': (['lat', 'lon', 'time'], np.random.rand(2, 4, 1)),
            'lat': np.linspace(-89.5, 89.5, 2),
            'lon': np.linspace(-179.5, 179.5, 4)})

        with self.assertRaises(ValueError):
            region = '-40.0, -95.0, 50.0, 60.0',
            plot_map(dataset, region=region)

        with self.assertRaises(ValueError):
            region = '-40.0, -20.0, 50.0, 95.0',
            plot_map(dataset, region=region)

        with self.assertRaises(ValueError):
            region = '-181.0, -20.0, 50.0, 60.0',
            plot_map(dataset, region=region)

        with self.assertRaises(ValueError):
            region = '-40.0, -20.0, 181.0, 60.0',
            plot_map(dataset, region=region)

        with self.assertRaises(ValueError):
            region = '-40.0, -20.0, 50.0, -25.0',
            plot_map(dataset, region=region)

        with self.assertRaises(ValueError):
            region = '-20.0, -20.0, -25.0, 60.0',
            plot_map(dataset, region=region)

        # Test temporal slice validation
        with self.assertRaises(ValueError):
            plot_map(dataset, time=0)

    def test_registered(self):
        """
        Test nominal execution of the function as a registered operation.
        """
        reg_op = OP_REGISTRY.get_op(object_to_qualified_name(plot_map))
        dataset = xr.Dataset({
            'first': (['lat', 'lon', 'time'], np.random.rand(5, 10, 2)),
            'second': (['lat', 'lon', 'time'], np.random.rand(5, 10, 2)),
            'lat': np.linspace(-89.5, 89.5, 5),
            'lon': np.linspace(-179.5, 179.5, 10),
            'time': pd.date_range('2000-01-01', periods=2)})

        with create_tmp_file('remove_me', 'png') as tmp_file:
            reg_op(ds=dataset, file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

@unittest.skipIf(condition=os.environ.get('CATE_DISABLE_PLOT_TESTS', None),
                 reason="skipped if CATE_DISABLE_PLOT_TESTS=1")
class TestPlot(TestCase):
    """
    Test plot() function
    """

    def test_plot(self):
        # Test plot
        dataset = xr.Dataset({
            'first': np.random.rand(10)})

        with create_tmp_file('remove_me', 'jpg') as tmp_file:
            plot(dataset, 'first', file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

    def test_registered(self):
        """
        Test nominal execution of the function as a registered operation.
        """
        reg_op = OP_REGISTRY.get_op(object_to_qualified_name(plot))
        # Test plot
        dataset = xr.Dataset({
            'first': np.random.rand(10)})

        with create_tmp_file('remove_me', 'jpg') as tmp_file:
            reg_op(ds=dataset, var='first', file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

@unittest.skipIf(condition=os.environ.get('CATE_DISABLE_PLOT_TESTS', None),
                 reason="skipped if CATE_DISABLE_PLOT_TESTS=1")
class TestPlotDataFrame(TestCase):
    """
    Test plot_data_frame() function.
    """

    def test_nominal(self):
        """
        Test nominal execution
        """
        data = {'A': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
                'B': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
        df = pd.DataFrame(data=data, index=pd.date_range('2000-01-01',
                                                         periods=10))

        with create_tmp_file('remove_me', 'png') as tmp_file:
            plot_data_frame(df, file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

    def test_registered(self):
        """
        Test the method when run as a registered operation
        """
        reg_op = OP_REGISTRY.get_op(object_to_qualified_name(plot_data_frame))

        data = {'A': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
                'B': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]}
        df = pd.DataFrame(data=data, index=pd.date_range('2000-01-01',
                                                         periods=10))

        with create_tmp_file('remove_me', 'png') as tmp_file:
            reg_op(df=df, file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

@unittest.skipIf(condition=os.environ.get('CATE_DISABLE_PLOT_TESTS', None),
                 reason="skipped if CATE_DISABLE_PLOT_TESTS=1")
class TestPlotHovmoeller(TestCase):
    """
    Test plot_hovmoeller() function
    """

    def test_nominal(self):
        """
        Test nominal execution
        """
        # Test nominal
        dataset = xr.Dataset({
            'first': (['lat', 'lon', 'time'], np.random.rand(5, 10, 2)),
            'second': (['lat', 'lon', 'time'], np.random.rand(5, 10, 2)),
            'lat': np.linspace(-89.5, 89.5, 5),
            'lon': np.linspace(-179.5, 179.5, 10),
            'time': pd.date_range('2000-01-01', periods=2)})

        with create_tmp_file('remove_me', 'png') as tmp_file:
            plot_hovmoeller(dataset, file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

        # Test colormesh and title
        with create_tmp_file('remove_me', 'png') as tmp_file:
            plot_hovmoeller(dataset, contour=False, title='FooBar', file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

        # More than three dims
        dataset = xr.Dataset({
            'first': (['lat', 'lon', 'time', 'depth'], np.random.rand(5, 10, 2, 2)),
            'second': (['lat', 'lon', 'time'], np.random.rand(5, 10, 2)),
            'lat': np.linspace(-89.5, 89.5, 5),
            'lon': np.linspace(-179.5, 179.5, 10),
            'depth': [1, 2],
            'time': pd.date_range('2000-01-01', periods=2)})

        with create_tmp_file('remove_me', 'png') as tmp_file:
            plot_hovmoeller(dataset, var='first', x_axis='time', y_axis='depth', file=tmp_file)
            self.assertTrue(os.path.isfile(tmp_file))

    def test_exceptions(self):
        """
        Test error conditions
        """
        dataset = xr.Dataset({
            'first': (['lat'], np.random.rand(5)),
            'second': (['lat', 'lon'], np.random.rand(5, 10)),
            'lat': np.linspace(-89.5, 89.5, 5),
            'lon': np.linspace(-179.5, 179.5, 10),
            'time': pd.date_range('2000-01-01', periods=2)})

        # test 1D dataset
        with self.assertRaises(ValueError):
            plot_hovmoeller(dataset, var='first', x_axis='lat', y_axis='lat')

        # test illegal dimensions
        with self.assertRaises(ValueError):
            plot_hovmoeller(dataset, var='second', x_axis='foo', y_axis='bar')


from paasta_tools.autoscaling import load_boost
from paasta_tools.cli.utils import execute_paasta_cluster_boost_on_remote_master
from paasta_tools.cli.utils import lazy_choices_completer
from paasta_tools.utils import DEFAULT_SOA_DIR
from paasta_tools.utils import list_clusters
from paasta_tools.utils import load_system_paasta_config

def add_subparser(subparsers):
    boost_parser = subparsers.add_parser(
        "boost",
        help="Set, print the status, or clear a capacity boost for a given region in a PaaSTA cluster",
        description=(
            "'paasta boost' is used to temporarily provision more capacity in a given cluster "
            "It operates by ssh'ing to a Mesos master of a remote cluster, and "
            "interacting with the boost in the local zookeeper cluster. If you set or clear "
            "a boost, you may want to run the cluster autoscaler manually afterward."
        ),
        epilog=(
            "The boost command may time out during heavy load. When that happens "
            "users may execute the ssh command directly, in order to bypass the timeout."
        ),
    )
    boost_parser.add_argument(
        "-v",
        "--verbose",
        action="count",
        dest="verbose",
        default=0,
        help="""Print out more output regarding the state of the cluster.
        Multiple v options increase verbosity. Maximum is 3.""",
    )
    boost_parser.add_argument(
        "-c",
        "--cluster",
        type=str,
        required=True,
        help="""Paasta cluster(s) to boost. This option can take comma separated values.
        If auto-completion doesn't work, you can get a list of cluster with `paasta list-clusters'""",
    ).completer = lazy_choices_completer(list_clusters)
    boost_parser.add_argument(
        "--soa-dir",
        dest="soa_dir",
        metavar="SOA_DIR",
        default=DEFAULT_SOA_DIR,
        help="define a different soa config directory",
    )
    boost_parser.add_argument(
        "-p",
        "--pool",
        type=str,
        default="default",
        help="Name of the pool you want to increase the capacity. Default is 'default' pool.",
    )
    boost_parser.add_argument(
        "-b",
        "--boost",
        type=float,
        default=load_boost.DEFAULT_BOOST_FACTOR,
        help="Boost factor to apply. Default is 1.5. A big failover should be 2, 3 is the max.",
    )
    boost_parser.add_argument(
        "-d",
        "--duration",
        type=int,
        default=load_boost.DEFAULT_BOOST_DURATION,
        help="Duration of the capacity boost in minutes. Default is 40",
    )
    boost_parser.add_argument(
        "-f",
        "--force",
        action="store_true",
        dest="override",
        help="Replace an existing boost. Default is false",
    )
    boost_parser.add_argument(
        "action",
        choices=["set", "status", "clear"],
        help="You can view the status, set or clear a boost.",
    )
    boost_parser.set_defaults(command=paasta_boost)

def paasta_boost(args):
    soa_dir = args.soa_dir
    system_paasta_config = load_system_paasta_config()
    all_clusters = list_clusters(soa_dir=soa_dir)
    clusters = args.cluster.split(",")
    for cluster in clusters:
        if cluster not in all_clusters:
            print(
                f"Error: {cluster} doesn't look like a valid cluster. "
                + "Here is a list of valid paasta clusters:\n"
                + "\n".join(all_clusters)
            )
            return 1

    return_code, output = execute_paasta_cluster_boost_on_remote_master(
        clusters=clusters,
        system_paasta_config=system_paasta_config,
        action=args.action,
        pool=args.pool,
        duration=args.duration if args.action == "set" else None,
        override=args.override if args.action == "set" else None,
        boost=args.boost if args.action == "set" else None,
        verbose=args.verbose,
    )
    print(output)
    return return_code


import requests
import logging
import urllib.parse
from config import APIKEY

def alert(url):
    logging.basicConfig(level=logging.INFO, format='%(levelname)s - %(asctime)s - %(name)s - %(message)s')
    sendMsg(url)

def test():
    logging.basicConfig(level=logging.DEBUG, format='%(levelname)s - %(asctime)s - %(name)s - %(message)s')
    sendMsg('http://<test-parameter>')

def sendMsg(url):
    title = '[kWordSentry]'
    msg = "kWordSentry发现有关键词消失：{0}".format(url)
    msg = urllib.parse.quote_plus(msg)
    api = "https://sctapi.ftqq.com/{0}.send?title={1}&desp={2}".format(APIKEY, title, msg)
    try:
        r = requests.get(api)
    except Exception as e:
        logging.warning('Failed to connect to ServerChan: {0}'.format(e))
        return
    logging.info('Done.')

if __name__ == "__main__":
    print('TESTING...')
    test()


import matplotlib.pyplot as plt
import numpy as np
from matplotlib import gridspec

from odeModels.insuline.simulation1Day import simulation, pidC1

t, y = simulation([300, 300, 1400 - 300 - 300], [36.29, 86.88, 94.18], pidC1)
y = y + 10
step = 20
s = np.repeat(np.random.normal(y[::step, 0], 10), step)
minIndex = min(len(t), len(s))
t = t[:minIndex]
s = s[:minIndex]

fig = plt.figure(figsize=(8, 6))
gs = gridspec.GridSpec(1, 2, width_ratios=[12, 1])
ax = plt.subplot(gs[0])
bx = plt.subplot(gs[1])
ax.axhline(y=70, color='k', linestyle='-')
ax.plot(t, s, label='G(t)')
ax.fill_between(t, 70, where=s >= 70, facecolor='red', alpha=0.3, interpolate=True)
# x.fill_between(t, 55, where=s < 180, facecolor='green',alpha=1, interpolate=True)
ax.fill_between(t, 35, where=s >= 70, facecolor='red', alpha=1, interpolate=True)
bx.fill_between([0, 1], 0.95, facecolor='red', alpha=1)
bx.set_ylim([0, 1])
ax.set_ylim([30, 250])
ax.set_xlim([0, 1400])
ax.legend()
plt.savefig('GnoiseFree')
plt.show()


#4302271	 Aug 17, 2013 12:41:50 PM	fuwutu	 337B - Routine Problem	 Python 3	Accepted	 92 ms	 0 KB
def gcd(a, b):
    while a % b != 0:
        a, b = b, a % b
    return b

l = input().split(' ')
a, b, c, d = int(l[0]), int(l[1]), int(l[2]), int(l[3])
if a * d > b * c:
    p = a * d - b * c
    q = a * d
else:
    p = b * c - a * d
    q = b * c
e = gcd(p, q)
p //= e
q //= e
print(str(p) + "/" + str(q))


import cv2
import mediapipe as mp
import numpy as np
import time

#ML solution provided by mediapipe for detecting face mesh
mp_face_mesh = mp.solutions.face_mesh

#need confidence score higher than 0.5 to detect face
#and mesh from frame to frame
face_mesh = mp_face_mesh.FaceMesh(min_detection_confidence=0.5,
                                  min_tracking_confidence=0.5)

mpFaceDetection = mp.solutions.face_detection
faceDetection = mpFaceDetection.FaceDetection(0.5)

#setting up thickness and circle radius of the mesh
#drawn on the detected face
mp_drawing = mp.solutions.drawing_utils
drwaing_spec = mp_drawing.DrawingSpec(thickness=1, circle_radius=1)

# cap = cv2.VideoCapture("PilotData_DriverFatigue_GoPro.mp4")
cap = cv2.VideoCapture(0)

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

    #to take time of how long the algorithm takes to work and to calculate
    #frames per second
    start = time.time()

    #the models we are paasing through mediapipe use RGB where openCV
    #reads in the images as RGB. Image is also flipped to maintain the
    #left and right head movement
    image = cv2.cvtColor(cv2.flip(image,1), cv2.COLOR_BGR2RGB)

    #we need the frames passed in the NN to be readable to improve performance
    image.flags.writeable = False

    #passing the frames to the face_mesh object created above, to get all the different landmarks in the face.
    #First it runs as a face detector. It then crops the face to input it to the NN.
    results = face_mesh.process(image)

    results_mark = faceDetection.process(image)
    bboxs = []

    #changing iamge to writable again to draw on the image
    image.flags.writeable = True

    #converting the image back to BGR
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)

    img_h, img_w, img_c = image.shape
    face_3d = []
    face_2d = []

    #face detection
    if results_mark.detections:
	    for id, detection in enumerate(results_mark.detections):
	        bboxC = detection.location_data.relative_bounding_box
	        ih, iw, ic = image.shape
	        bbox = int(bboxC.xmin * iw), int(bboxC.ymin * ih), \
	               int(bboxC.width * iw), int(bboxC.height * ih)
	        cx, cy = bbox[0] + (bbox[2] // 2), \
	                 bbox[1] + (bbox[3] // 2)
	        bboxInfo = {"id": id, "bbox": bbox, "score": detection.score, "center": (cx, cy)}
	        bboxs.append(bboxInfo)
	        image = cv2.rectangle(image, bbox, (255, 0, 255), 2)

	        cv2.putText(image, f'{int(detection.score[0] * 100)}%',
	        	(bbox[0], bbox[1] - 20), cv2.FONT_HERSHEY_PLAIN,
	        	2, (255, 0, 255), 2)

    #pose detection
    if results.multi_face_landmarks:
        #if one person in the image, one result
        for face_landmark in results.multi_face_landmarks:

            #running through all the landmarks (nose, mouth ends, eye ends)
            for idx, lm in enumerate(face_landmark.landmark):

                if idx == 33 or idx == 263 or idx == 1 or idx == 61 or idx == 291 or idx == 199:
                    #separating out nose coordinates, used to draw a line from the nose as a guide to where
                    #the person is seeing
                    if idx ==1:
                        nose_2d = (lm.x * img_w, lm.y * img_h)
                        nose_3d = (lm.x * img_w, lm.y * img_h, lm.x * 3000)

                    #scaling the normalized x,y value
                    x,y = int(lm.x*img_w), int(lm.y*img_h)

                    face_2d.append([x,y])
                    face_3d.append([x, y, lm.z])

            face_2d = np.array(face_2d, dtype = np.float32)
            face_3d = np.array(face_3d, dtype = np.float32)

            #assumed intrinsic parameters of the camera
            focal_length = 1 * img_w
            cam_matrix = np.array([[focal_length, 0.0, img_h/2],
                                  [0.0, focal_length, img_w/2],
                                  [0.0, 0.0, 1.0]], dtype=np.float32)

            #distrotion parameters, camera assumed to be not distorted
            dist_matrix = np.zeros((4,1), dtype=np.float32)

            #Solving PnP:
            #rot_vec: how much is the point actually in the image
            #trans_vec: how much is the our point translated
            success, rot_vec, trans_vec = cv2.solvePnP(face_3d, face_2d, cam_matrix, dist_matrix)

            #Getting Rotational matrix from rot_vec
            rmat, jac = cv2.Rodrigues(rot_vec)

            #Getting angles
            angles, mtxR, mtxQ, Qx, Qy, Qz = cv2.RQDecomp3x3(rmat)

            #y rotation degrees to display on the image
            x = angles[0] * 360
            y = angles[1] * 360
            z = angles[2] * 360

            #for webcam: where the head is tilted
            if y<-10:
                text = "Looking left"
            elif y>10:
                text = "Looking right"
            elif x<-10:
                text = "Looking down"
            elif x>10:
                text = "Looking up"
            else:
                text = "Forward"

#             #for provided video: where the head is tilted
#             if y>0:
#                 text = "Looking left"
#             elif y<-8:
#                 text = "Looking right"
#             elif x<-10:
#                 text = "Looking down"
#             elif x>10:
#                 text = "Looking up"
#             else:
#                 text = "Forward"

            #to display the nose direction (where the face would be seeing)
            nose_3d_projection, jacobian = cv2.projectPoints(nose_3d, rot_vec, trans_vec, cam_matrix, dist_matrix)

            #nose tip coordinates
            p1 = (int(nose_2d[0]), int(nose_2d[1]))
            #scaled based how much is the face looking in either directions
            p2 = (int(nose_2d[0] +y*10), int(nose_2d[1] -x*10))

            cv2.line(image, p1, p2, (255,0,0) ,3)

            # Adding the text on the image
            cv2.putText(image, text, (20, 20), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
            cv2.putText(image, "x: "+str(np.round(x,2)), (500, 50), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
            cv2.putText(image, "y: "+str(np.round(y,2)), (500, 100), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
            cv2.putText(image, "z: "+str(np.round(z,2)), (500, 150), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)

        end = time.time()
        totalTime = end - start
        fps = 1/totalTime
        print("FPS: ", fps)
        cv2.putText(image, f'FPS: {int(fps)}', (20, 450), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 255, 0), 2)

    cv2.imshow('Head Pose Estimation', image)

    if cv2.waitKey(5) & 0xFF == 27:
        break

cap.release()


from Grid_3 import Grid
from Heuristics import Heuristics
from Node import Node

class MiniMax:
    def __init__(self, root_node):
        self.root = root_node
        self.h_eval = Heuristics()
        self.best_direction = None
        self.direction_list = ['UP', 'DOWN', 'LEFT', 'RIGHT']

    def alpha_beta(self, depth):
        result = self.alpha_beta_recursive(self.root, depth, -float('inf'), float('inf'), True)

        for child in self.root.children:
            if child.h_value == result:
                self.best_direction = self.direction_list[child.direction]
                print("Child h_value = ", child.h_value,"\nBest move =", self.best_direction)

        return result

    def alpha_beta_recursive(self, node, depth, a, b, MaxPlayer = True):
        if depth == 0 or len(node.children) == 0:
            h_terminal = self.h_eval.evaluate_h(node.grid)
            node.h_value = h_terminal
            return h_terminal
        if MaxPlayer:
            for child in node.children:
                a = max(a, self.alpha_beta_recursive(child, depth-1, a, b, False))
                node.h_value = max(node.h_value, child.h_value)
                if a >= b:
                    break   # pruning

            return a
        else:
            for child in node.children:
                b = min(b, self.alpha_beta_recursive(child, depth-1, a, b, True))
                node.h_value = min(node.h_value, child.h_value)
                if a >= b:
                    break   # pruning

            return b