from typing import Any, Callable, Dict, Optional, Type, Union

from fugue.execution.execution_engine import ExecutionEngine, SQLEngine
from fugue.execution.native_execution_engine import NativeExecutionEngine
from triad.utils.convert import to_instance
from triad import assert_or_throw, ParamDict

class _ExecutionEngineFactory(object):
    def __init__(self):
        self._funcs: Dict[str, Callable] = {}
        self._type_funcs: Dict[Type, Callable] = {}
        self._sql_funcs: Dict[str, Callable] = {}
        self.register_default(lambda conf, **kwargs: NativeExecutionEngine(conf=conf))
        self.register_default_sql_engine(lambda engine, **kwargs: engine.sql_engine)

    def register(
        self, name_or_type: Union[str, Type], func: Callable, on_dup="overwrite"
    ) -> None:
        if isinstance(name_or_type, str):
            self._register(self._funcs, name=name_or_type, func=func, on_dup=on_dup)
        else:
            self._register(
                self._type_funcs, name=name_or_type, func=func, on_dup=on_dup
            )

    def register_default(self, func: Callable, on_dup="overwrite") -> None:
        self.register("", func, on_dup)

    def register_sql_engine(
        self, name: str, func: Callable, on_dup="overwrite"
    ) -> None:
        self._register(self._sql_funcs, name=name, func=func, on_dup=on_dup)

    def register_default_sql_engine(self, func: Callable, on_dup="overwrite") -> None:
        self.register_sql_engine("", func, on_dup)

    def make(
        self, engine: Any = None, conf: Any = None, **kwargs: Any
    ) -> ExecutionEngine:
        if isinstance(engine, tuple):
            execution_engine = self.make_execution_engine(
                engine[0], conf=conf, **kwargs
            )
            sql_engine = self.make_sql_engine(engine[1], execution_engine)
            execution_engine.set_sql_engine(sql_engine)
            return execution_engine
        else:
            return self.make((engine, None), conf=conf, **kwargs)

    def make_execution_engine(
        self, engine: Any = None, conf: Any = None, **kwargs: Any
    ) -> ExecutionEngine:
        # Apply this function to an Execution Engine instance can
        # make sure the compile conf is a superset of conf
        # TODO: it's a mess here, can we make the logic more intuitive?

        def make_engine(engine: Any) -> ExecutionEngine:
            if isinstance(engine, str) and engine in self._funcs:
                return self._funcs[engine](conf, **kwargs)
            for k, f in self._type_funcs.items():
                if isinstance(engine, k):
                    return f(engine, conf, **kwargs)
            if isinstance(engine, ExecutionEngine):
                if conf is not None:
                    engine.compile_conf.update(conf)
                engine.compile_conf.update(kwargs)
                return engine
            return to_instance(
                engine, ExecutionEngine, kwargs=dict(conf=conf, **kwargs)
            )

        result = make_engine(engine or "")
        result.compile_conf.update(result.conf, on_dup=ParamDict.IGNORE)
        result.compile_conf.update(conf, on_dup=ParamDict.OVERWRITE)
        result.compile_conf.update(kwargs, on_dup=ParamDict.OVERWRITE)
        return result

    def make_sql_engine(
        self,
        engine: Any = None,
        execution_engine: Optional[ExecutionEngine] = None,
        **kwargs: Any,
    ) -> SQLEngine:
        if engine is None:
            engine = ""
        if isinstance(engine, str) and engine in self._sql_funcs:
            return self._sql_funcs[engine](execution_engine, **kwargs)
        if isinstance(engine, SQLEngine):
            assert_or_throw(
                execution_engine is None and len(kwargs) == 0,
                lambda: ValueError(
                    f"{engine} is an instance, can't take arguments "
                    f"execution_engine={execution_engine}, kwargs={kwargs}"
                ),
            )
            return engine
        return to_instance(
            engine, SQLEngine, kwargs=dict(execution_engine=execution_engine, **kwargs)
        )

    def _register(
        self,
        callables: Dict[Any, Callable],
        name: Any,
        func: Callable,
        on_dup="overwrite",
    ) -> None:
        if name not in callables:
            callables[name] = func
        if on_dup in ["raise", "throw"]:
            raise KeyError(f"{name} is already registered")
        if on_dup == "overwrite":
            callables[name] = func
            return
        if on_dup == "ignore":
            return
        raise ValueError(on_dup)

_EXECUTION_ENGINE_FACTORY = _ExecutionEngineFactory()

def register_execution_engine(
    name_or_type: Union[str, Type], func: Callable, on_dup="overwrite"
) -> None:
    """Register :class:`~fugue.execution.execution_engine.ExecutionEngine` with
    a given name.

    :param name_or_type: alias of the execution engine, or type of an object that
      can be converted to an execution engine
    :param func: a callable taking |ParamsLikeObject| and ``**kwargs`` and returning an
      :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. admonition:: Examples

        Alias registration examples:

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_execution_engine("my", lambda conf: MyExecutionEngine(conf))

            # 0
            make_execution_engine("my")
            make_execution_engine("my", {"myconfig":"value})

            # 1
            with FugueWorkflow("my") as dag:
                dag.create([[0]],"a:int").show()

            # 2
            dag = FugueWorkflow()
            dag.create([[0]],"a:int").show()
            dag.run("my", {"myconfig":"value})

            # 3
            fsql('''
            CREATE [[0]] SCHEMA a:int
            PRINT
            ''').run("my")

        Type registration examples:

        .. code-block:: python

            from pyspark.sql import SparkSession
            from fugue_spark import SparkExecutionEngine
            from fugue_sql import fsql

            register_execution_engine(
                SparkSession,
                lambda session, conf: SparkExecutionEngine(session, conf))

            spark_session = SparkSession.builder.getOrCreate()

            fsql('''
            CREATE [[0]] SCHEMA a:int
            PRINT
            ''').run(spark_session)
    """
    _EXECUTION_ENGINE_FACTORY.register(name_or_type, func, on_dup)

def register_default_execution_engine(func: Callable, on_dup="overwrite") -> None:
    """Register :class:`~fugue.execution.execution_engine.ExecutionEngine` as the
    default engine.

    :param func: a callable taking |ParamsLikeObject| and ``**kwargs`` and returning an
      :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. admonition:: Examples

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_default_execution_engine(lambda conf: MyExecutionEngine(conf))

            # the following examples will use MyExecutionEngine

            # 0
            make_execution_engine()
            make_execution_engine(None, {"myconfig":"value})

            # 1
            with FugueWorkflow() as dag:
                dag.create([[0]],"a:int").show()

            # 2
            dag = FugueWorkflow()
            dag.create([[0]],"a:int").show()
            dag.run(None, {"myconfig":"value})

            # 3
            fsql('''
            CREATE [[0]] SCHEMA a:int
            PRINT
            ''').run("", {"myconfig":"value})
    """
    _EXECUTION_ENGINE_FACTORY.register_default(func, on_dup)

def register_sql_engine(name: str, func: Callable, on_dup="overwrite") -> None:
    """Register :class:`~fugue.execution.execution_engine.SQLEngine` with
    a given name.

    :param name: name of the SQL engine
    :param func: a callable taking
      :class:`~fugue.execution.execution_engine.ExecutionEngine`
      and ``**kwargs`` and returning a
      :class:`~fugue.execution.execution_engine.SQLEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. admonition:: Examples

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_sql_engine("mysql", lambda engine: MySQLEngine(engine))

            # create execution engine with MySQLEngine as the default
            make_execution_engine(("", "mysql"))

            # create DaskExecutionEngine with MySQLEngine as the default
            make_execution_engine(("dask", "mysql"))

            # default execution engine + MySQLEngine
            with FugueWorkflow(("","mysql")) as dag:
                dag.create([[0]],"a:int").show()
    """
    _EXECUTION_ENGINE_FACTORY.register_sql_engine(name, func, on_dup)

def register_default_sql_engine(func: Callable, on_dup="overwrite") -> None:
    """Register :class:`~fugue.execution.execution_engine.SQLEngine` as the
    default engine

    :param func: a callable taking
      :class:`~fugue.execution.execution_engine.ExecutionEngine`
      and ``**kwargs`` and returning a
      :class:`~fugue.execution.execution_engine.SQLEngine` instance
    :param on_dup: action on duplicated ``name``. It can be "overwrite", "ignore"
      (not overwriting) or "throw" (throw exception), defaults to "overwrite".

    :raises KeyError: if ``on_dup`` is ``throw`` and the ``name`` already exists

    .. note::

        You should be careful to use this function, because when you set a custom
        SQL engine as default, all execution engines you create will use this SQL
        engine unless you are explicit. For example if you set the default SQL engine
        to be a Spark specific one, then if you start a NativeExecutionEngine, it will
        try to use it and will throw exceptions.

        So it's always a better idea to use ``register_sql_engine`` instead

    .. admonition:: Examples

        .. code-block:: python

            # create a new engine with name my (overwrites if existed)
            register_default_sql_engine(lambda engine: MySQLEngine(engine))

            # create NativeExecutionEngine with MySQLEngine as the default
            make_execution_engine()

            # create SparkExecutionEngine with MySQLEngine instead of SparkSQLEngine
            make_execution_engine("spark")

            # NativeExecutionEngine with MySQLEngine
            with FugueWorkflow() as dag:
                dag.create([[0]],"a:int").show()
    """
    _EXECUTION_ENGINE_FACTORY.register_default_sql_engine(func, on_dup)

def make_execution_engine(
    engine: Any = None, conf: Any = None, **kwargs: Any
) -> ExecutionEngine:
    """Create :class:`~fugue.execution.execution_engine.ExecutionEngine`
    with specified ``engine``

    :param engine: it can be empty string or null (use the default execution
      engine), a string (use the registered execution engine), an
      :class:`~fugue.execution.execution_engine.ExecutionEngine` type, or
      the :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
      , or a tuple of two values where the first value represents execution
      engine and the second value represents the sql engine (you can use ``None``
      for either of them to use the default one), defaults to None
    :param conf: |ParamsLikeObject|, defaults to None
    :param kwargs: additional parameters to initialize the execution engine

    :return: the :class:`~fugue.execution.execution_engine.ExecutionEngine`
      instance

    .. admonition:: Examples

        .. code-block:: python

            register_default_execution_engine(lambda conf: E1(conf))
            register_execution_engine("e2", lambda conf, **kwargs: E2(conf, **kwargs))

            register_sql_engine("s", lambda conf: S2(conf))

            # E1 + E1.default_sql_engine
            make_execution_engine()

            # E2 + E2.default_sql_engine
            make_execution_engine(e2)

            # E1 + S2
            make_execution_engine((None, "s"))

            # E2(conf, a=1, b=2) + S2
            make_execution_engine(("e2", "s"), conf, a=1, b=2)

            # SparkExecutionEngine + SparkSQLEngine
            make_execution_engine(SparkExecutionEngine)
            make_execution_engine(SparkExecutionEngine(spark_session, conf))

            # SparkExecutionEngine + S2
            make_execution_engine((SparkExecutionEngine, "s"))
    """
    import fugue._utils.register  # pylint: disable=W0611 # noqa: F401

    return _EXECUTION_ENGINE_FACTORY.make(engine, conf, **kwargs)

def make_sql_engine(
    engine: Any = None,
    execution_engine: Optional[ExecutionEngine] = None,
    **kwargs: Any,
) -> SQLEngine:
    """Create :class:`~fugue.execution.execution_engine.SQLEngine`
    with specified ``engine``

    :param engine: it can be empty string or null (use the default SQL
      engine), a string (use the registered SQL engine), an
      :class:`~fugue.execution.execution_engine.SQLEngine` type, or
      the :class:`~fugue.execution.execution_engine.SQLEngine` instance
      (you can use ``None`` to use the default one), defaults to None
    :param execution_engine: the
      :class:`~fugue.execution.execution_engine.ExecutionEngine` instance
      to create
      the :class:`~fugue.execution.execution_engine.SQLEngine`. Normally you
      should always provide this value.
    :param kwargs: additional parameters to initialize the sql engine

    :return: the :class:`~fugue.execution.execution_engine.SQLEngine`
      instance

    .. note::

        For users, you normally don't need to call this function directly.
        Use ``make_execution_engine`` instead

    .. admonition:: Examples

        .. code-block:: python

            register_default_sql_engine(lambda conf: S1(conf))
            register_sql_engine("s2", lambda conf: S2(conf))

            engine = NativeExecutionEngine()

            # S1(engine)
            make_sql_engine(None, engine)

            # S1(engine, a=1)
            make_sql_engine(None, engine, a=1)

            # S2(engine)
            make_sql_engine("s2", engine)

            # SqliteEngine(engine)
            make_sql_engine(SqliteEngine)
    """
    import fugue._utils.register  # pylint: disable=W0611 # noqa: F401

    return _EXECUTION_ENGINE_FACTORY.make_sql_engine(engine, execution_engine, **kwargs)


import math
from cmath import exp
import numpy as np
import pandas as pd

from Operators import Operator, Density_Matrix, Observable

from Many_Body import tensor_product

from Nuclear_Spin import Nuclear_Spin, Many_Spins

def h_zeeman(spin, theta_z, phi_z, B_0):
    """
    Computes the term of the Hamiltonian associated with the Zeeman interaction between the nuclear spin and the external static field.

    Parameters
    ----------
    - spin: Nuclear_Spin
            Spin under study;
    - theta_z: float
               Polar angle of the magnetic field in the laboratory coordinate system (expressed in radians);
    - phi_z: float
             Azimuthal angle of the magnetic field in the laboratory coordinate system (expressed in radians);
    - B_0: non-negative float
           Magnitude of the external magnetic field (expressed in tesla).

    Returns
    -------
    An Observable object which represents the Zeeman Hamiltonian in the laboratory reference frame (expressed in MHz).

    Raises
    ------
    ValueError, when the passed B_0 is a negative number.
    """
    if B_0<0: raise ValueError("The modulus of the magnetic field must be a non-negative quantity")
    h_z = -spin.gyro_ratio_over_2pi*B_0* \
          (math.sin(theta_z)*math.cos(phi_z)*spin.I['x'] + \
           math.sin(theta_z)*math.sin(phi_z)*spin.I['y'] + \
           math.cos(theta_z)*spin.I['z'])
    return Observable(h_z.matrix)

def h_quadrupole(spin, e2qQ, eta, alpha_q, beta_q, gamma_q):
    """
    Computes the term of the Hamiltonian associated with the quadrupolar interaction.

    Parameters
    ----------
    - spin: Nuclear_Spin
            Spin under study;
    - e2qQ: float
            Product of the quadrupole moment constant, eQ, and the eigenvalue of the EFG tensor which is greatest in absolute value, eq. e2qQ is measured in MHz;
    - eta: float in the interval [0, 1]
           Asymmetry parameter of the EFG;
    - alpha_q, beta_q, gamma_q: float
                                Euler angles for the conversion from the system of the principal axes of the EFG tensor (PAS) to the lab system (LAB) (expressed in radians).

    Returns
    -------
    If the quantum number of the spin is 1/2, the whole calculation is skipped and a null Observable object is returned.
    Otherwise, the function returns the Observable object which correctly represents the quadrupolar Hamiltonian in the laboratory reference frame (expressed in MHz).

    """
    if math.isclose(spin.quantum_number, 1/2, rel_tol=1e-10):
        return Observable(spin.d)*0
    I = spin.quantum_number
    h_q = (e2qQ/(I*(2*I-1)))* \
          ((1/2)*(3*(spin.I['z']**2) - Operator(spin.d)*I*(I+1))*v0_EFG(eta, alpha_q, beta_q, gamma_q)+\
           (math.sqrt(6)/4)*
           ((spin.I['z']*spin.I['+'] + spin.I['+']*spin.I['z'])*\
                             v1_EFG(-1, eta, alpha_q, beta_q, gamma_q) + \
            (spin.I['z']*spin.I['-'] + spin.I['-']*spin.I['z'])*\
                             v1_EFG(+1, eta, alpha_q, beta_q, gamma_q) + \
            (spin.I['+']**2)*\
            v2_EFG(-2, eta, alpha_q, beta_q, gamma_q) + \
            (spin.I['-']**2)*\
            v2_EFG(2, eta, alpha_q, beta_q, gamma_q)))
    return Observable(h_q.matrix)

def v0_EFG(eta, alpha_q, beta_q, gamma_q):
    """
    Returns the component V0 of the EFG tensor (divided by eq) as seen in the LAB system. This quantity is expressed in terms of the Euler angles which relate PAS and LAB systems and the parameter eta.

    Parameters
    ----------
    - eta: float in the interval [0, 1]
           Asymmetry parameter of the EFG;
    - alpha_q, beta_q, gamma_q: float
                                Euler angles connecting the system of the principal axes of the EFG tensor (PAS) to the lab system (LAB) (expressed in radians).

    Returns
    -------
    A float representing the component V0 (divided by eq) of the EFG tensor evaluated in the LAB system.

    Raises
    ValueError, when the passed eta is not in the interval [0, 1].
    """
    if eta<0 or eta>1: raise ValueError("The asymmetry parameter must fall in the interval [0, 1]")
    v0 = (1/2)*(((3*(math.cos(beta_q))**2-1)/2) - (eta*(math.sin(beta_q))**2)*(math.cos(2*gamma_q))/2)
    return v0

def v1_EFG(sign, eta, alpha_q, beta_q, gamma_q):
    """
    Returns the components V+/-1 of the EFG tensor (divided by eq) as seen in the LAB system. These quantities are expressed in terms of the Euler angles which relate PAS and LAB systems and the parameter eta.

    Parameters
    ----------
    - sign: float
            Specifies wether the V+1 or the V-1 component is to be computed;
    - eta: float in the interval [0, 1]
           Asymmetry parameter of the EFG;
    - alpha_q, beta_q, gamma_q: float
                                Euler angles connecting the system of the principal axes of the EFG tensor (PAS) to the lab system (LAB) (expressed in radians).

    Returns
    -------
    A complex number representing the component:
    - V+1, if sign is positive;
    - V-1, if sign is negative.
    of the EFG tensor (divided by eq).

    Raises
    ------
    ValueError, when the passed eta is not in the interval [0, 1].
    """
    if eta<0 or eta>1: raise ValueError("The asymmetry parameter must fall within the interval [0, 1]")
    sign = np.sign(sign)
    v1 = (1/2)*\
         (
          -1j*sign*math.sqrt(3/8)*math.sin(2*beta_q)*exp(sign*1j*alpha_q)+\
          1j*(eta/(math.sqrt(6)))*math.sin(beta_q)*\
          (
           ((1+sign*math.cos(beta_q))/2)*exp(1j*(sign*alpha_q+2*gamma_q))-\
            ((1-sign*math.cos(beta_q))/2)*exp(1j*(sign*alpha_q-2*gamma_q))
          )
         )
    return v1

def v2_EFG(sign, eta, alpha_q, beta_q, gamma_q):
    """
    Returns the components V+/-2 of the EFG tensor (divided by eq) as seen in the LAB system. These quantities are expressed in terms of the Euler angles which relate PAS and LAB systems and the parameter eta.

    Parameters
    ----------
    - sign: float
            Specifies wether the V+2 or the V-2 component is to be returned;
    - eta: float in the interval [0, 1]
           Asymmetry parameter of the EFG tensor;
    - alpha_q, beta_q, gamma_q: float
                                Euler angles connecting the system of the principal axes of the EFG tensor (PAS) to the lab system (LAB) (expressed in radians).

    Returns
    -------
    A float representing the component:
    - V+2, if sign is positive;
    - V-2, if sign is negative.
    of the EFG tensor (divided by eq).

    Raises
    ------
    ValueError, when the passed eta is not in the interval [0, 1].
    """
    if eta<0 or eta>1: raise ValueError("The asymmetry parameter must fall in the interval [0, 1]")
    sign = np.sign(sign)
    v2 = (1/2)*\
         (math.sqrt(3/8)*((math.sin(beta_q))**2)*exp(sign*2j*alpha_q)+\
          (eta/math.sqrt(6))*exp(sign*2j*alpha_q)*\
           (
            exp(2j*gamma_q)*((1+sign*math.cos(beta_q))**2)/4 +\
            exp(-2j*gamma_q)*((1-sign*math.cos(beta_q))**2)/4
           )
         )
    return v2

def h_single_mode_pulse(spin, frequency, B_1, phase, theta_1, phi_1, t):
    """
    Computes the term of the Hamiltonian describing the interaction with a monochromatic and linearly polarized electromagnetic pulse.

    Parameters
    ----------
    - spin: Nuclear_Spin
            Spin under study.
    - frequency: non-negative float
                 Frequency of the monochromatic wave (expressed in MHz).
    - phase: float
             Inital phase of the wave (at t=0) (expressed in radians).
    - B_1: non-negative float
           Maximum amplitude of the oscillating magnetic field (expressed in tesla).
    - theta_1, phi_1: float
                      Polar and azimuthal angles of the direction of polarization of the magnetic wave in the LAB frame (expressed in radians);
    - t: float
         Time of evaluation of the Hamiltonian (expressed in microseconds).

    Returns
    -------
    An Observable object which represents the Hamiltonian of the coupling with the electromagnetic pulse evaluated at time t (expressed in MHz).

    Raises
    ------
    ValueError, in two distinct cases:
    1. When the passed frequency parameter is a negative quantity;
    2. When the passed B_1 parameter is a negative quantity.
    """
    if frequency < 0: raise ValueError("The modulus of the angular frequency of the electromagnetic wave must be a positive quantity")
    if B_1 < 0: raise ValueError("The amplitude of the electromagnetic wave must be a positive quantity")
    h_pulse = -spin.gyro_ratio_over_2pi*B_1*\
              (math.sin(theta_1)*math.cos(phi_1)*spin.I['x'] +\
               math.sin(theta_1)*math.sin(phi_1)*spin.I['y'] +\
               math.cos(theta_1)*spin.I['z']
               )*\
               math.cos(2*math.pi*frequency*t-phase)
    return Observable(h_pulse.matrix)

def h_multiple_mode_pulse(spin, mode, t):
    """
    Computes the term of the Hamiltonian describing the interaction with a superposition of single-mode electromagnetic pulses. If the passed argument spin is a Nuclear_Spin object, the returned Hamiltonian will describe the interaction between the pulse of radiation and the single spin; if it is a Many_Spins object, it will represent the interaction with the whole system of many spins.

    Parameters
    ----------
    - spin: Nuclear_Spin or Many_Spins
            Spin or spin system under study;
    - mode: pandas.DataFrame
            Table of the parameters of each electromagnetic mode in the superposition. It is organised according to the following template:

    | index |  'frequency'  |  'amplitude'  |  'phase'  |  'theta_p'  |  'phi_p'  |
    | ----- | ------------- | ------------- | --------- | ----------- | --------- |
    |       |     (MHz)     |      (T)      |   (rad)   |    (rad)    |   (rad)   |
    |   0   |    omega_0    |      B_0      |  phase_0  |   theta_0   |   phi_0   |
    |   1   |    omega_1    |      B_1      |  phase_1  |   theta_1   |   phi_1   |
    |  ...  |      ...      |      ...      |    ...    |     ...     |    ...    |
    |   N   |    omega_N    |      B_N      |  phase_N  |   theta_N   |   phi_N   |

    where the meaning of each column is analogous to the corresponding parameters in h_single_mode_pulse.

    - t: float
         Time of evaluation of the Hamiltonian (expressed in microseconds).

    Returns
    -------
    An Observable object which represents the Hamiltonian of the coupling with the superposition of the given modes evaluated at time t (expressed in MHz).
    """
    h_pulse = Operator(spin.d)*0
    omega = mode['frequency']
    B = mode['amplitude']
    phase = mode['phase']
    theta = mode['theta_p']
    phi = mode['phi_p']
    if isinstance(spin, Many_Spins):
        for i in mode.index:
            h_pulse = Operator(spin.d)*0
            for n in range(spin.n_spins):
                term_n = h_single_mode_pulse(spin.spin[n], omega[i], B[i], phase[i], theta[i], phi[i], t)
                for m in range(spin.n_spins)[:n]:
                    term_n = tensor_product(Operator(spin.spin[m].d), term_n)
                for l in range(spin.n_spins)[n+1:]:
                    term_n = tensor_product(term_n, Operator(spin.spin[l].d))
                h_pulse = h_pulse + term_n
    elif isinstance(spin, Nuclear_Spin):
        for i in mode.index:
            h_pulse = h_pulse + h_single_mode_pulse(spin, omega[i], B[i], phase[i], theta[i], phi[i], t)
    return Observable(h_pulse.matrix)

# Global Hamiltonian of the system (stationary term + pulse term) cast in the picture generated by
# the Operator h_change_of_picture
def h_changed_picture(spin, mode, h_unperturbed, h_change_of_picture, t):
    """
    Returns the global Hamiltonian of the system, made up of the time-dependent term h_multiple_mode_pulse(spin, mode, t) and the stationary term h_unperturbed, cast in the picture generated by h_change_of_picture.

    Parameters
    ----------
    - spin, mode, t: same meaning as the corresponding arguments of h_multiple_mode_pulse;
    - h_unperturbed: Operator
                     Stationary term of the global Hamiltonian (in MHz);
    - h_change_of_picture: Operator
                           Operator which generates the new picture (in MHz).

    Returns
    -------
    Observable object representing the Hamiltonian of the pulse evaluated at time t in the new picture (in MHz).
    """
    h_cp = (h_unperturbed + h_multiple_mode_pulse(spin, mode, t) - \
            h_change_of_picture).changed_picture(h_change_of_picture, t)
    return Observable(h_cp.matrix)

def h_j_coupling(spins, j_matrix):
    """
    Returns the term of the Hamiltonian describing the J-coupling between the spins of a system of many nuclei.

    Parameters
    ----------
    - spins: Many_Spins
             Spins' system under study;

    - j_matrix: np.ndarray
                Array storing the coefficients Jmn which enter the formula for the computation of the Hamiltonian for the j-coupling.
                Remark: j_matrix doesn't have to be symmetric, since the function reads only those elements located in the upper half with respect to the diagonal. This means that the elements j_matrix[m, n] which matter are those for which m<n.

    Returns
    -------
    Observable object acting on the full Hilbert space of the spins' system representing the Hamiltonian of the J-coupling between the spins.
    """
    h_j = Operator(spins.d)*0

    for m in range(j_matrix.shape[0]):
        for n in range(m):
            term_nm = j_matrix[n, m]*spins.spin[n].I['z']
            for l in range(n):
                term_nm = tensor_product(Operator(spins.spin[l].d), term_nm)
            for k in range(m)[n+1:]:
                term_nm = tensor_product(term_nm, Operator(spins.spin[k].d))
            term_nm = tensor_product(term_nm, spins.spin[m].I['z'])
            for j in range(spins.n_spins)[m+1:]:
                term_nm = tensor_product(term_nm, Operator(spins.spin[j].d))

            h_j = h_j + term_nm

    return h_j.cast_to_observable()


# Get all positions around given point within 2 km circle.
# ZHOU Kunpeng, 14 Dec 2018

from model import models
from controller import utils

# Get all positions around 2km range centered at given point
class GetPositionsAround():
    # params: longitude (float), latitude(float)
    # returns: a list of positions with their item information
    def getPositionsAround(self, longitude, latitude):

        # Range of the circle (only get positions within range)
        RANGE = 2

        positionsAround = []
        positions = models.Position.objects.all()

        for position in positions:
            # within 2 km range
            dist = utils.getDistance(longitude, latitude, position.longitude, position.latitude)
            if dist <= RANGE:
                # positionsAround.append([position.id, position.longitude, position.latitude, position])
                positionsAround.append(position)

        return positionsAround

# Get all positions on map
class GetAllPositions():
    # params: longitude (float), latitude(float)
    # returns: a list of positions with their item information
    def getAllPositions(self):

        positionsRet = []
        positions = models.Position.objects.all()

        for position in positions:
            positionsRet.append(position)

        return positionsRet

# update last location of a user
class UpdateUserLocation():
    # params: user(string), longitude(float), latitude(float)
    def updateUserLocation(self, userId, longitude, latitude):
        user = models.User.objects.get(wechatId = userId)
        user.lastLongitude = longitude
        user.lastLatitude = latitude
        user.save()

from controller.pet import petDAOs
# A user checks in a given position
class CheckIn():
    # params: wechatId (string), positionId (int)
    # returns: a dictionary that shows total effect on user's pet.
    def checkIn(self, wechatId, positionId):
        # get user and position
        user = models.User.objects.filter(wechatId = wechatId)[0]
        position = models.Position.objects.filter(id = positionId)[0]

        if user == None or position == None:
            return None

        # check if this place (position) has been checked in by this user
        checkInQuery = models.CheckInRecord.objects.filter(user=user, point=position)
        if len(checkInQuery) != 0:
            return None

        # Create a check-in record
        checkInRecord = models.CheckInRecord.objects.create(user=user, point=position)

        # Upgrade pet's ability
        pet = user.pets.all()[0]
        item = position.itemLinked

        # pet.experience += item.addExp
        pet.health += item.addHealth
        pet.attack += item.addAttack
        pet.defend += item.addDefend
        pet.speed += item.addSpeed
        pet.dodgeRate += item.addDodgeRate
        pet.save()

        # update pet's experience (if leveled-up, ability will be updated accordingly)
        petDAOs.UpdateExperience().updateExperience(pet.id, pet.experience + item.addExp)

        return item


import random

DESCRIPTION = "Find the greatest common divisor of given numbers."

def find_gcd(x, y):
    while x and y:
        if x > y:
            x = x % y
        else:
            y = y % x
    gcd = str(x + y)
    return gcd

def get_question_and_answer():
    a = random.randint(1, 100)
    b = random.randint(1, 100)

    question = f"{a} {b}"

    correct_answer = str(find_gcd(a, b))

    return question, correct_answer


import cv2
import time
import os
import HandTrackingModule as htm
from dronekit import connect, VehicleMode, LocationGlobalRelative, APIException
import time
import socket
import math
import argparse
from pymavlink import mavutil
from time import sleep
import numpy as np

###################################################################################

def connectMyCopter():
    parser=argparse.ArgumentParser(description='commands')
    parser.add_argument('--connect', default='127.0.0.1:14550')
    args=parser.parse_args()

    connection_string=args.connect
    baud_rate=921600

    vehicle=connect(connection_string, baud=baud_rate, wait_ready=True)
    return vehicle

###################################################################################
# Function to arm and takeoff

def arm_and_takeoff(TargetAltitude):

    # Switch vehicle to Guided Mode
    vehicle.mode = VehicleMode("GUIDED")
    while vehicle.mode!="GUIDED":
        print("Waiting for guided mode")
        time.sleep(1)

    # Arming the Vehicle
    vehicle.armed = True
    while vehicle.armed == False:
        print("Waiting for the vehicle to be armed")
        time.sleep(1)

    vehicle.simple_takeoff(TargetAltitude)

    while True:
        print("Current Altitude: %d" , vehicle.location.global_relative_frame.alt)
        if vehicle.location.global_relative_frame.alt >= TargetAltitude*.95:
            break
        time.sleep(1)

    print("Target Altitude reached")
    return None

##################################################################
#-- Define the function for sending mavlink velocity command in body frame
def set_velocity_body(vehicle, vx, vy, vz):
    """ Remember: vz is positive downward!!!
    http://ardupilot.org/dev/docs/copter-commands-in-guided-mode.html

    Bitmask to indicate which dimensions should be ignored by the vehicle
    (a value of 0b0000000000000000 or 0b0000001000000000 indicates that
    none of the setpoint dimensions should be ignored). Mapping:
    bit 1: x,  bit 2: y,  bit 3: z,
    bit 4: vx, bit 5: vy, bit 6: vz,
    bit 7: ax, bit 8: ay, bit 9:

    """
    msg = vehicle.message_factory.set_position_target_local_ned_encode(
            0,
            0, 0,
            mavutil.mavlink.MAV_FRAME_BODY_NED,
            0b0000111111000111, #-- BITMASK -> Consider only the velocities
            0, 0, 0,        #-- POSITION
            vx, vy, vz,     #-- VELOCITY
            0, 0, 0,        #-- ACCELERATIONS
            0, 0)
    vehicle.send_mavlink(msg)
    vehicle.flush()
###################################################################
vehicle = connectMyCopter()

wCam, hCam = 640, 480
deadZone = 100
pTime = 0
cap = cv2.VideoCapture(0)
cap.set(3, wCam)
cap.set(4, hCam)
detector = htm.handDetector(detectionCon=0.8, maxHands=1)
x = [300, 245, 200, 170, 145, 130, 112, 103, 93, 87, 80, 75, 70, 67, 62, 59, 57]
y = [20, 25, 30, 35, 40, 45, 50, 55, 60, 65, 70, 75, 80, 85, 90, 95, 100]
coff = np.polyfit(x, y, 2)   # y = AX^2 + BX + C
c = []
i = 0
tipIds = [4, 8, 12, 16, 20]
while True:
    success, img = cap.read()
    img = detector.findHands(img)
    lmList = detector.findPosition(img, draw=False)
    #print(lmList)

    if len(lmList) !=0:
        fingers = []
        # Thumb . Here the x value of thumb tip is compared with the x value of mid thumb
        if lmList[tipIds[0]][1] > lmList[tipIds[0] - 1][1]:
            fingers.append(1)

        else:
            fingers.append(0)

        # Other Fingers
        for id in range(1,5):
            if lmList[tipIds[id]][2] < lmList[tipIds[id]-2][2]:
                fingers.append(1)
            else:
                fingers.append(0)

        #print(sum(fingers))

        x1, y1 = lmList[5][1], lmList[5][2]
        x2, y2 = lmList[17][1], lmList[17][2]
        cx, cy = (x1 + x2) // 2, (y1 + y2) // 2
        ty = lmList[4][2]
        #print(cx, cy)
        cv2.circle(img, (cx, cy), 5, (255, 0, 255), cv2.FILLED)
        #length = int(math.hypot(x2 - x1, y2 - y1))
        #A, B, C = coff
        #distanceCM = A*length**2 + B*length + C
        #print(distanceCM)

        if sum(fingers) == 0:
            print(" Arm and Takeoff ")
            arm_and_takeoff(2)

        if sum(fingers) == 5:

            if ((cx < int(wCam/2) + deadZone) and (cx > int(wCam/2) - deadZone)):
                print("Hold Position")
                set_velocity_body(vehicle, 0, 0, 0)

            if (cx < int(wCam/2) - deadZone):
                print("Moving Right")
                set_velocity_body(vehicle, 0, 0.5, 0)

            if (cx > int(wCam/2) + deadZone):
                print("Moving Left")
                set_velocity_body(vehicle, 0, -0.5, 0)

        if sum(fingers) == 1:

            if ((ty < int(hCam/2) + deadZone) and (ty > int(hCam/2) - deadZone)):
                print("Hold Position")
                set_velocity_body(vehicle, 0, 0, 0)

            if (ty < int(hCam/2) - deadZone):
                print("Moving Up")
                set_velocity_body(vehicle, 0, 0, -1)

            if (ty > int(hCam/2) + deadZone):
                print("Moving Down")
                set_velocity_body(vehicle, 0, 0, 1)

        #if sum(fingers) == 5:
         #   c.append(cx)
          #  if len(c)!=0:
           #     for i in range(len(c)):
            #        difference = c[i]-c[i-1]
                    #print(difference)
             #       if difference > 0:
              #          print("Moving Left")
               #         set_velocity_body(vehicle, 0, -3, 0)
                #    elif difference < 0:
                 #       print("Moving Right")
                  #      set_velocity_body(vehicle, 0, 3, 0)
                   # elif difference == 0:
                    #    print("Hold Position")
                     #   set_velocity_body(vehicle, 0, 0, 0)
            #
            #print(" Moving Right ")
            #set_velocity_body(vehicle, distanceCM*0.05, 0, 0)

    cTime = time.time()
    fps = 1 / (cTime - pTime)
    pTime = cTime
    cv2.putText(img, f'FPS: {int(fps)}', (40, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (255, 0, 0), 3)
    cv2.imshow("Image", img)
    cv2.waitKey(1)


import sys

"""
이분 탐색 기반 풀이
시작 지점이 end보다 작거나 같아질 때 까지 진행
시작을 1, 끝을 끝 - 시작으로 진행.
둘 간의 거리를 최대로 하기 위해서는 middle보다 커야한다.
x를 갱신해서 공유기 위치를 조정한다.
cnt가 c보다 크거나 같을 경우 -> 공유기를 많이 설치한 케이스 -> start 업데이트 하고, ans를 middle로 설정
cnt가 c보다 적을 경우 -> 공유기를 적게 설치한 케이스 -> end 업데이트
"""

n, c = map(int, sys.stdin.readline().split(" "))
house = []
for _ in range(n):
    house.append(int(sys.stdin.readline()))
house.sort()

start = 1
end = house[-1] - house[0]

while start <= end:
    middle = (start + end) // 2
    x = house[0]
    cnt = 1

    for i in range(len(house)):
        if house[i] - x >= middle:
            x = house[i]
            cnt += 1

    if cnt >= c:
        start = middle + 1
        ans = middle
    elif cnt < c:
        end = middle - 1

print(ans)


'''from math import prod
a=int(input())
for i in range(10,10001):
    if prod(list(map(int,str(i))))==a:
        print(i)
        break
else:
    print("Not Possible")
'''
n=int(input())
a=[]
for i in range(n):
    a+=[int(input())]
if n<2:
    print("Invalid Input")
else:
    a.sort()
    if  n == a.count(a[0]):
        print("Equal")
    else:
        print(a[0],a[1],sep=" ")


def read():

    file = open("PuzzleInput_9.txt", "r")

    dict = []
    line_count = 0
    for x in file.readlines():
        x = x.strip()
        x = int(x)
        dict.append(x)
        line_count += 1
    file.close()
    return dict

dict = read()

for x in range(25, len(dict)):
    check = False
    for y in dict[x-25: x]:
        if dict[x] - y in dict[x-25: x]:
            check = True
    if not check:
        invalid_number = dict[x]

print(f"gedeelte 1:{invalid_number}")

index_number = 0
index_check = 0
answer = []
answer_fnd = True
while answer_fnd:
    check_sum = 0
    index_check = index_number
    while True:
        if check_sum == invalid_number:
            for x in dict[index_number:index_check]:
                answer.append(x)
            answer_fnd = False

            break
        if check_sum > invalid_number:
            break
        if index_check == len(dict):
            break
        check_sum += dict[index_check]
        index_check += 1
    index_number += 1

print(f"gedeelte 2:{max(answer) + min(answer)}")


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Mon Oct 12 17:56:03 2020

@author: mints
"""
import logging
import itertools
import joblib
import warnings
import numpy as np
from astropy.utils.exceptions import AstropyUserWarning
from pandas.core.common import SettingWithCopyWarning
from semiphore_public.cuda.cudaprocessor import CudaProcessor
from semiphore_public.utils import interpolate
warnings.filterwarnings('ignore', category=AstropyUserWarning, append=True)
warnings.filterwarnings('ignore', category=SettingWithCopyWarning, append=True)

def distance(w1, w2, sed1, sed2, err1, err2):
    """Calculate distance between two SED templates

    Args:
        w1 (float): weight of the first SED
        w2 (float): weight of the second SED
        sed1 (float[]): magnitudes of the first SED
        sed2 (float[]): magnitudes of the second SED
        err1 (float[]): width of the first SED
        err2 (float[]): width of the second SED

    Returns:
        "Distance"
    """
    d = (w1 * (sed1 - sed2)**2 / (err1**2 + 1e-2),
         w2 * (sed1 - sed2)**2 / (err2**2 + 1e-2))
    return np.sum(np.sqrt(d))

def get_order(w1, w2, sed1, sed2, err1, err2):
    """Reorder SEDs. Here all parameters are arrays along the redshift.

    Args:
        w1 (float[]): weight of the first SED
        w2 (float[]): weight of the second SED
        sed1 (float[][]): magnitudes of the first SED
        sed2 (float[][]): magnitudes of the second SED
        err1 (float[][]): width of the first SED
        err2 (float[][]): width of the second SED

    Returns:
        [type]: [description]
    """
    nn = len(w1)
    d = np.zeros((nn, nn))
    for i in range(nn):
        for j in range(nn):
            d[i, j] = distance(w1[i], w2[j],
                               sed1[i], sed2[j],
                               err1[i], err2[j])
    smin = np.inf
    tOk = None
    for t in itertools.permutations(np.arange(nn, dtype=int), nn):
        s = 0
        for i in range(nn):
            s += d[i, t[i]]
        if s < smin:
            smin = s
            tOk = t
    return tOk

if __name__ == '__main__':
    import argparse
    import logging
    logger = logging.getLogger("FIT")
    logger.setLevel(logging.DEBUG)
    logging.basicConfig(format='%(asctime)s - %(name)s - %(levelname)s - %(message)s')
    parser = argparse.ArgumentParser(description="""
    Perform a full CUDA-based SED-PhotoZ fit.
    """, formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument('-i', '--input', type=str, default=None,
                        help='Input filename')
    parser.add_argument('-c', '--catalogs', type=str,
                        default=None, required=True,
                        help='Input catalogs to use (comma separated)')
    parser.add_argument('-o', '--output', type=str, default=None,
                        help='Output filename')
    parser.add_argument('-n', '--nsed', type=int, default=1,
                        help='Number of SEDs to fit')
    parser.add_argument('-V', '--verbose', action="store_true",
                        default=False,
                        help='Be verbose')

    args = parser.parse_args()

    processor = CudaProcessor(args.catalogs.split(','), args.nsed)
    results = []
    sizes = []
    logger.info("Load data from %s", args.catalogs)
    processor.load_data(filename=args.input)
    z_len = len(processor.z)
    is_ok = []
    izs = []
    # Forward run
    for z0, mags, errs in processor.iterate_data(size=1000):
        logger.info("Forward run, redshift=%.2f", processor.z[int(z0)])
        if len(results) > 0:
            output = processor.run_on_data(mags, errs,
                                           custom_params=results[-1][0])
        else:
            output = processor.run_on_data(mags, errs)
        if output is not None:
            res, size, _ = output
            if res[1] >= processor.MAX_ITERATIONS * args.nsed:
                logger.warn(f'Iteration count exceeded for z nr {z0}')
                is_ok.append(False)
            else:
                is_ok.append(True)
            results.append(res)
            sizes.append(size)
            izs.append(z0)

    # Backward run:
    for ii in range(len(izs)-2, 0, -1):
        if not is_ok[ii + 1] or not is_ok[ii]:
            continue
        old_norm = results[ii][2] / sizes[ii]
        if results[ii + 1][2] / sizes[ii + 1] > old_norm:
            logger.info("Backward run, redshift=%.2f",
                        processor.z[int(izs[ii])])
            mags, errs = processor.get_data_for_zs(izs[ii])
            output = processor.run_on_data(mags, errs,
                                           custom_params=results[ii+1][0])
            if output is not None:
                res, size, _ = output
                if res[2] / size >= results[ii][2] / sizes[ii]:
                    logger.debug(f'...new l_norm={res[2] / size} is better')
                    results[ii] = res
                    sizes[ii] = size
                else:
                    logger.debug(f'...new l_norm={res[2] / size} is lower, rejecting')
    iz_min = int(np.ceil(np.min(izs)))
    iz_max = int(np.ceil(np.max(izs)))
    izs = processor.z[0] + np.array(izs) * 0.02

    sed_shape = (z_len, processor.n_seds, len(processor.columns))
    output = {'z': processor.z,
              'names': processor.names,
              'weights': np.zeros((z_len, processor.n_seds)),
              'sed': np.zeros(sed_shape),
              'err': np.zeros(sed_shape),
              'l_values': np.zeros(len(izs)),
              'iterations': np.zeros(len(izs)),
              'sizes': sizes,
              }
    w = np.array([results[ii][0][0] for ii in range(len(results))])
    sed = np.array([results[ii][0][1] for ii in range(len(results))])
    err = np.array([results[ii][0][2] for ii in range(len(results))])
    output['iterations'] = np.array([results[ii][1]
                                     for ii in range(len(results))])
    output['l_values'] = np.array([results[ii][2]
                                   for ii in range(len(results))])
    ind = np.argsort(w)

    logger.info("Reordering...")
    # Reordering
    output['weights00'] = w
    output['sed00'] = sed
    output['err00'] = err
    w_order = [w[0]]
    sed_order = [sed[0]]
    err_order = [err[0]]
    for i in range(0, len(w)-1):
        new_order = list(get_order(w_order[i], w[i+1],
                                   sed_order[i], sed[i+1],
                                   err_order[i], err[i+1]))
        w_order.append(w[i + 1][new_order])
        sed_order.append(sed[i + 1][new_order])
        err_order.append(err[i + 1][new_order])

    logger.info("Interpolating...")
    # Interpolation
    output['weights0'] = w_order
    output['sed0'] = sed_order
    output['err0'] = err_order
    output['weights'] = interpolate.curve_processor(izs, np.array(w_order),
                                                    processor.z, is_log=True)
    output['sed'] = interpolate.curve_processor(izs, np.array(sed_order),
                                                processor.z, is_log=False)
    output['err'] = interpolate.curve_processor(izs, np.array(err_order),
                                                processor.z,
                                                is_log=True, bounded=True)

    output['weights'] = output['weights'] / \
        output['weights'].sum(axis=1)[:, np.newaxis]
    output['z_base'] = izs
    output['input_file'] = args.input

    if args.output is None:
        names = '_'.join(processor.names)
        outname = f'../calibrations/seds/{names}_{processor.n_seds}seds.joblib'
    else:
        outname = args.output
    logger.info('Saving calibration to %s', outname)
    joblib.dump(output, outname)
    logger.info("Finished")


# Answer to almostIncreasingSequence
# https://app.codesignal.com/arcade/intro/level-2/2mxbGwLzvkTCKAJMG

def almostIncreasingSequence(sequence):
    droppped = False
    last = prev = min(sequence) - 1
    for elm in sequence:
        if elm <= last:
            if droppped:
                return False
            else:
                droppped = True
            if elm <= prev:
                prev = last
            elif elm >= prev:
                prev = last = elm
        else:
            prev, last = last, elm
    return True

# I had to take help to solve this.
# And this is taken from a user pharfenmeister

Arcade/Intro/Edge of the Ocean/almostIncreasingSequence.py


from flask import Flask, request, redirect, url_for
from flask_jsonpify import jsonify
from flask import render_template
from flask import abort
from flask import Response
from flask_api import status
import json
from flaskext.mysql import MySQL
import pandas as pd
import requests
from datetime import datetime, timedelta
import matplotlib as plt
import base64
import io

app = Flask(__name__)
mysql = MySQL()

# MySQL configurations
app.config['MYSQL_DATABASE_USER'] = 'root'
app.config['MYSQL_DATABASE_PASSWORD'] = ''
app.config['MYSQL_DATABASE_DB'] = 'cloud'
app.config['MYSQL_DATABASE_HOST'] = 'localhost'
mysql.init_app(app)

db = mysql.connect()
cursor = db.cursor()

@app.route('/')
def homepage():
    return  render_template('forecast.html')

@app.route('/historical/', methods=['GET','POST'])   #lists all the dates
def historical():
    if(request.method=='GET'):
        dates_list = []
        cursor.execute("select DATE from dailyweather")
        query=cursor.fetchall()
        my_hist = [i[0] for i in query]
        for item in my_hist:
            a = {"DATE":str(item)}
            dates_list.append(a)
        js = json.dumps(dates_list)
        return js, 200
    else:
        l=request.get_json()
        d=l['DATE']
        tmax=l['TMAX']
        tmin=l['TMIN']
        obj = {}
        cursor.execute("select DATE from dailyweather")
        q=cursor.fetchall()
        list=[i[0] for i in q]
        x=0
        for item in list:
            if(int(d)==item):
                x=1
        if(x==1):
            cursor.execute("update dailyweather set TMAX=%f, TMIN=%f where DATE=%d" %(float(tmax),float(tmin),int(d)))
        else:
            cursor.execute("insert into dailyweather values(%d,%f,%f)" % (int(d),float(tmax),float(tmin)))
        db.commit()
        obj={"DATE":str(d)}
        return jsonify(obj), 201

@app.route('/historical/<string:DATE>', methods=['GET'])    #gets the weather info of a particular day
def get_info(DATE):
    obj = {}
    l=[]
    cursor.execute("select DATE,TMAX,TMIN from dailyweather where DATE =%d" % int(DATE))
    q=cursor.fetchall()
    if(len(q)>0):
        for i in range(3):
            l.append(q[0][i])
        obj =  {
                "DATE": str(l[0]),
                "TMAX": l[1],
                "TMIN": l[2]
                }
        return jsonify(obj), 200
    else:
        return '', 404

@app.route('/historical/<int:DATE>', methods=['DELETE'])
def del_info(DATE):
    obj={}
    l=[]
    cursor.execute("select DATE,TMAX,TMIN from dailyweather where DATE=%d" % int(DATE))
    query=cursor.fetchall()
    cursor.execute("delete from dailyweather where DATE=%d" % int(DATE))
    db.commit()
    if(len(query)>0):
        for i in range(3):
            l.append(str(query[0][i]))
        obj =  {
                "DATE": l[0],
                "TMAX": l[1],
                "TMIN": l[2]
                }
        return jsonify(obj), 200
    else:
        return '', 204

@app.route('/forecast/<DATE>', methods=['GET'])   #forecasts weather info of the next 7days
def forecast(DATE):
    lst_dates = []
    lst_obj = []
    current_date = pd.to_datetime(DATE,format='%Y%m%d')
    stop_date = current_date+timedelta(days=7)
    while current_date<stop_date:
        lst_dates.append(str(pd.to_datetime(current_date)).split(' ')[0].replace("-",""))
        current_date = current_date+timedelta(days=1)
    for curr_date in lst_dates:
        cursor.execute("select DATE,TMAX,TMIN from dailyweather where DATE =%d" % int(curr_date))
        query=cursor.fetchall()
        if (len(query) > 0):
            obj = {
                    "DATE": curr_date,
                    "TMAX": query[0][1],
                    "TMIN": query[0][2]
                    }
            lst_obj.append(obj)
        else:
            cursor.execute("select ROUND(RAND()*(80-75+1),1)+75")
            q=cursor.fetchall()
            cursor.execute("select ROUND(RAND()*(50-45+1),1)+45")
            q1=cursor.fetchall()
            obj = {
                    "DATE": curr_date,
                    "TMAX": q[0][0],
                    "TMIN": q1[0][0]
                }
            lst_obj.append(obj)
    return jsonify(lst_obj), 200

if __name__ == '__main__':
     app.run(host='0.0.0.0',debug=True,port=80)


from typing import Optional, Any, Union, Callable

import torch
from torch import Tensor
import torch.nn.functional as F
from torch.nn.modules import Module

from .linear import Linear
from .normalization import LayerNorm
from .activation import MultiheadAttention
from .dropout import Dropout

class TransformerEncoderLayer(Module):
    r"""Pytorch 2.0
    TransformerEncoderLayer is made up of self-attn and feedforward network.
    This standard encoder layer is based on the paper "Attention Is All You Need".
    Ashish Vaswani, Noam Shazeer, Niki Parmar, Jakob Uszkoreit, Llion Jones, Aidan N Gomez,
    Lukasz Kaiser, and Illia Polosukhin. 2017. Attention is all you need. In Advances in
    Neural Information Processing Systems, pages 6000-6010. Users may modify or implement
    in a different way during application.

    Args:
        d_model: the number of expected features in the input (required).
        nhead: the number of heads in the multiheadattention models (required).
        dim_feedforward: the dimension of the feedforward network model (default=2048).
        dropout: the dropout value (default=0.1).
        activation: the activation function of the intermediate layer, can be a string
            ("relu" or "gelu") or a unary callable. Default: relu
        layer_norm_eps: the eps value in layer normalization components (default=1e-5).
        batch_first: If ``True``, then the input and output tensors are provided
            as (batch, seq, feature). Default: ``False`` (seq, batch, feature).
        norm_first: if ``True``, layer norm is done prior to attention and feedforward
            operations, respectively. Otherwise it's done after. Default: ``False`` (after).

    Examples::
        >>> encoder_layer = nn.TransformerEncoderLayer(d_model=512, nhead=8)
        >>> src = torch.rand(10, 32, 512)
        >>> out = encoder_layer(src)

    Alternatively, when ``batch_first`` is ``True``:
        >>> encoder_layer = nn.TransformerEncoderLayer(d_model=512, nhead=8, batch_first=True)
        >>> src = torch.rand(32, 10, 512)
        >>> out = encoder_layer(src)

    Fast path:
        forward() will use a special optimized implementation described in
        `FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness`_ if all of the following
        conditions are met:

        - Either autograd is disabled (using ``torch.inference_mode`` or ``torch.no_grad``) or no tensor
          argument ``requires_grad``
        - training is disabled (using ``.eval()``)
        - batch_first is ``True`` and the input is batched (i.e., ``src.dim() == 3``)
        - activation is one of: ``"relu"``, ``"gelu"``, ``torch.functional.relu``, or ``torch.functional.gelu``
        - at most one of ``src_mask`` and ``src_key_padding_mask`` is passed
        - if src is a `NestedTensor <https://pytorch.org/docs/stable/nested.html>`_, neither ``src_mask``
          nor ``src_key_padding_mask`` is passed
        - the two ``LayerNorm`` instances have a consistent ``eps`` value (this will naturally be the case
          unless the caller has manually modified one without modifying the other)

        If the optimized implementation is in use, a
        `NestedTensor <https://pytorch.org/docs/stable/nested.html>`_ can be
        passed for ``src`` to represent padding more efficiently than using a padding
        mask. In this case, a `NestedTensor <https://pytorch.org/docs/stable/nested.html>`_ will be
        returned, and an additional speedup proportional to the fraction of the input that
        is padding can be expected.

        .. _`FlashAttention: Fast and Memory-Efficient Exact Attention with IO-Awareness`:
         https://arxiv.org/abs/2205.14135

    """
    __constants__ = ["batch_first", "norm_first"]

    def __init__(
        self,
        d_model: int,
        nhead: int,
        dim_feedforward: int = 2048,
        dropout: float = 0.1,
        activation: Union[str, Callable[[Tensor], Tensor]] = F.relu,
        layer_norm_eps: float = 1e-5,
        batch_first: bool = False,
        norm_first: bool = False,
        device=None,
        dtype=None,
        B: int = 1,
    ) -> None:
        factory_kwargs = {"device": device, "dtype": dtype}
        super().__init__()
        self.B = B

        self.self_attn = MultiheadAttention(d_model, nhead, dropout=dropout, batch_first=batch_first, B=B, **factory_kwargs)
        # Implementation of Feedforward model
        self.linear1 = Linear(d_model, dim_feedforward, B=B, **factory_kwargs)
        self.dropout = Dropout(dropout)
        self.linear2 = Linear(dim_feedforward, d_model, B=B, **factory_kwargs)

        self.norm_first = norm_first
        self.norm1 = LayerNorm(d_model, eps=layer_norm_eps, B=B, **factory_kwargs)
        self.norm2 = LayerNorm(d_model, eps=layer_norm_eps, B=B, **factory_kwargs)
        self.dropout1 = Dropout(dropout)
        self.dropout2 = Dropout(dropout)

        # For Hydro scaling
        self.d_model = d_model
        self.nhead = nhead
        self.dim_feedforward = dim_feedforward
        self.dropout_value = dropout
        self.layer_norm_eps = layer_norm_eps
        self.batch_first = batch_first

        # Legacy string support for activation function.
        if isinstance(activation, str):
            self.activation = _get_activation_fn(activation)
        else:
            self.activation = activation

        # We can't test self.activation in forward() in TorchScript,
        # so stash some information about it instead.
        if activation is F.relu or isinstance(activation, torch.nn.ReLU):
            self.activation_relu_or_gelu = 1
        elif activation is F.gelu or isinstance(activation, torch.nn.GELU):
            self.activation_relu_or_gelu = 2
        else:
            self.activation_relu_or_gelu = 0

    def __setstate__(self, state):
        super().__setstate__(state)
        if not hasattr(self, "activation"):
            self.activation = F.relu

    def forward(
        self,
        src: Tensor,
        src_mask: Optional[Tensor] = None,
        src_key_padding_mask: Optional[Tensor] = None,
        is_causal: bool = False,
    ) -> Tensor:
        r"""Pass the input through the encoder layer.

        Args:
            src: the sequence to the encoder layer (required).
            src_mask: the mask for the src sequence (optional).
            is_causal: If specified, applies a causal mask as src_mask.
              Default: ``False``.
            src_key_padding_mask: the mask for the src keys per batch (optional).

        Shape:
            see the docs in Transformer class.
        """
        src_key_padding_mask = F._canonical_mask(
            mask=src_key_padding_mask,
            mask_name="src_key_padding_mask",
            other_type=F._none_or_dtype(src_mask),
            other_name="src_mask",
            target_type=src.dtype,
        )

        # Fast path NOT support training
        # see Fig. 1 of https://arxiv.org/pdf/2002.04745v1.pdf
        why_not_sparsity_fast_path = ""
        if not src.dim() == 3:
            why_not_sparsity_fast_path = f"input not batched; expected src.dim() of 3 but got {src.dim()}"
        elif self.training:
            why_not_sparsity_fast_path = "training is enabled"
        elif not self.self_attn.batch_first:
            why_not_sparsity_fast_path = "self_attn.batch_first was not True"
        elif not self.self_attn._qkv_same_embed_dim:
            why_not_sparsity_fast_path = "self_attn._qkv_same_embed_dim was not True"
        elif not self.activation_relu_or_gelu:
            why_not_sparsity_fast_path = "activation_relu_or_gelu was not True"
        elif not (self.norm1.eps == self.norm2.eps):
            why_not_sparsity_fast_path = "norm1.eps is not equal to norm2.eps"
        elif src.is_nested and (src_key_padding_mask is not None or src_mask is not None):
            why_not_sparsity_fast_path = "neither src_key_padding_mask nor src_mask are not supported with NestedTensor input"
        elif self.self_attn.num_heads % 2 == 1:
            why_not_sparsity_fast_path = "num_head is odd"
        elif torch.is_autocast_enabled():
            why_not_sparsity_fast_path = "autocast is enabled"
        if not why_not_sparsity_fast_path:
            tensor_args = (
                src,
                self.self_attn.in_proj_weight,
                self.self_attn.in_proj_bias,
                self.self_attn.out_proj.weight,
                self.self_attn.out_proj.bias,
                self.norm1.weight,
                self.norm1.bias,
                self.norm2.weight,
                self.norm2.bias,
                self.linear1.weight,
                self.linear1.bias,
                self.linear2.weight,
                self.linear2.bias,
            )

            # We have to use list comprehensions below because TorchScript does not support
            # generator expressions.
            if torch.overrides.has_torch_function(tensor_args):
                why_not_sparsity_fast_path = "some Tensor argument has_torch_function"
            elif not all((x.is_cuda or "cpu" in str(x.device)) for x in tensor_args):
                why_not_sparsity_fast_path = "some Tensor argument is neither CUDA nor CPU"
            elif torch.is_grad_enabled() and any(x.requires_grad for x in tensor_args):
                why_not_sparsity_fast_path = (
                    "grad is enabled and at least one of query or the "
                    "input/output projection weights or biases requires_grad"
                )

            if not why_not_sparsity_fast_path:
                merged_mask, mask_type = self.self_attn.merge_masks(src_mask, src_key_padding_mask, src)
                return torch._transformer_encoder_layer_fwd(
                    src,
                    self.self_attn.embed_dim,
                    self.self_attn.num_heads,
                    self.self_attn.in_proj_weight,
                    self.self_attn.in_proj_bias,
                    self.self_attn.out_proj.weight,
                    self.self_attn.out_proj.bias,
                    self.activation_relu_or_gelu == 2,
                    self.norm_first,
                    self.norm1.eps,
                    self.norm1.weight,
                    self.norm1.bias,
                    self.norm2.weight,
                    self.norm2.bias,
                    self.linear1.weight,
                    self.linear1.bias,
                    self.linear2.weight,
                    self.linear2.bias,
                    merged_mask,
                    mask_type,
                )

        x = src
        if self.norm_first:
            x = x + self._sa_block(self.norm1(x), src_mask, src_key_padding_mask)
            x = x + self._ff_block(self.norm2(x))
        else:
            x = self.norm1(x + self._sa_block(x, src_mask, src_key_padding_mask))
            x = self.norm2(x + self._ff_block(x))

        return x

    # self-attention block
    def _sa_block(self, x: Tensor, attn_mask: Optional[Tensor], key_padding_mask: Optional[Tensor]) -> Tensor:
        x = self.self_attn(x, x, x, attn_mask=attn_mask, key_padding_mask=key_padding_mask, need_weights=False)[0]
        return self.dropout1(x)

    # feed forward block
    def _ff_block(self, x: Tensor) -> Tensor:
        x = self.linear2(self.dropout(self.activation(self.linear1(x))))
        return self.dropout2(x)

    def extra_repr(self) -> str:
        s = "{d_model}, {nhead}, dim_feedforward={dim_feedforward}, dropout={dropout_value}, layer_norm_eps={layer_norm_eps}, B={B}"
        if self.activation != F.relu:
            if isinstance(self.activation, str):
                s += ", activation={activation}"
            else:
                s += ", activation={activation.__name__}"
        if self.batch_first:
            s += ", batch_first=True"
        if self.norm_first:
            s += ", norm_first=True"
        return s.format(**self.__dict__)

def _get_activation_fn(activation: str) -> Callable[[Tensor], Tensor]:
    if activation == "relu":
        return F.relu
    elif activation == "gelu":
        return F.gelu

    raise RuntimeError("activation should be relu/gelu, not {}".format(activation))


"""
Python script, Python v 2.7
written 1/30/17 by James Novakowski

This script was developed to summarize anonymized claims data for Radial
Analytics as part of a coding assessment.

To run the script, the script file should be in the same folder as the
data file, called "data.csv".

The script will generate an output csv file called "State_Level_Summary.csv",
which will provide a summary of claims data by state, gender, and age.

Note: The current implementation of this script leaves out data points
where the state is undefined, the gender is undefined, or the age is undefined.

The sript will also generate an output file called
"Claims_Utilization_Summary.csv", whcih will provide a summary of claims data
by the Utilization Range, providing the counts of claims and the percentage
of claims that fall into each range bucket.
"""

import csv

#Create data dictionary for state summary data
data = {}
state_code = range(1,67)
for item in range(97,100):
    state_code.append(item)

for state in state_code:
    data[state] = {"male":0,
                   "female":0,
                   "age_under_65":0,
                   "age_65_to_74":0,
                   "age_over_74":0,
                   "state":str(state)
                   }

#Create data dictionary for Utilization Range data
util_days = {}
util_code = range(0,6)
for item in util_code:
    util_days[str(item)] = 0

util_days["6_to_10"] = 0
util_days["11_to_30"] = 0
util_days["over_30"] = 0

"""
Read the data from the csv file to a dictionary.
Note: Current implementation ignores values 0: Unknown
in Gender and Age fields.

Then: Summarize the data.

Data fields coded as follows:

Gender Code from Claim
0 = Unknown
1 = Male
2 = Female

LDS Age Category
0 = Unknown
1 = <65
2 = 65 Thru 69
3 = 70 Thru 74
4 = 75 Thru 79
5 = 80 Thru 84
6 = >84

And want the following fields in the final tabulation:
State
Gender (male)
Gender (female)
Age (under 65)
Age (65-74)
Age (75 +)
"""

data_file = "data.csv"

f = open(data_file)
d = csv.DictReader(f)

for row in d:
    #print row
    age = int(row["LDS Age Category"])
    gender = int(row["Gender Code from Claim"])
    state = int(row["State Code from Claim (SSA)"])
    day_count = int(row["Claim Utilization Day Count"])

    #Read the data into the data nested dictionary

    if gender == 1:
        data[state]["male"] += 1
    elif gender == 2:
        data[state]["female"] += 1

    if age == 1:
        data[state]["age_under_65"] += 1
    elif age > 1 and age < 4:
        data[state]["age_65_to_74"] += 1
    elif age >= 4:
        data[state]["age_over_74"] += 1

    if day_count < 6:
        util_days[str(day_count)] += 1
    elif day_count >= 6 and day_count <= 10:
        util_days["6_to_10"] += 1
    elif day_count >= 11 and day_count <= 30:
        util_days["11_to_30"] += 1
    elif day_count > 30:
        util_days["over_30"] += 1

f.close()

"""
Generate an output csv file for the state claim summary data.
"""

with open("State_Level_Summary.csv", 'w') as csvfile:
    fieldnames = ['state',
                  'female',
                  'male',
                  'age_under_65',
                  'age_65_to_74',
                  'age_over_74']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

    writer.writerow({'state':'State',
                     'female':'Female',
                     'male':'Male',
                     'age_under_65':'Ages < 65',
                     'age_65_to_74':'Ages 65-74',
                     'age_over_74':'Ages75+'
                     })
    for state in data:
        writer.writerow(data[state])

"""
Generate an output csv file for the utilization days summary data.
Also use this step to calculate the total claims, and the percentage
of claims falling into each utilization range bucket.
"""
total_claims = 0

for key, value in util_days.iteritems():
    total_claims += value

with open("Claims_Utilization_Summary.csv", 'w') as csvfile:
    fieldnames = ['Utilization Range',
                  'Counts',
                  'Percentages']
    writer = csv.DictWriter(csvfile, fieldnames=fieldnames)

    writer.writeheader()

    for key, value in util_days.iteritems():

        if value > 0:
            percent = (value / float(total_claims)) * 100
            percent = round(percent, 2)
            percent = str(percent) + "%"
        else:
            percent = "0.00%"

        new_row = {'Utilization Range':key,
               'Counts':str(value),
               'Percentages':percent}
        writer.writerow(new_row)


import sys, os
sys.path.append(os.path.join(os.path.dirname(__file__), 'classes'))

from classes import data_manager

from sklearn.neural_network import MLPClassifier, MLPRegressor
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
from sklearn.ensemble import RandomForestRegressor
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import GradientBoostingClassifier
from sklearn import svm
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import SGDClassifier
from sklearn.ensemble import StackingClassifier, VotingClassifier
from sklearn.metrics import accuracy_score, precision_score, recall_score
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler

from tensorflow.keras.models import Sequential
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import Dense, Embedding, GlobalMaxPool1D, Conv1D
from tensorflow.keras.callbacks import EarlyStopping
from tensorflow.keras.models import Sequential, load_model

from keras.preprocessing.text import Tokenizer
from keras.preprocessing.sequence import pad_sequences

import autokeras as ak

from xgboost import XGBRegressor
from xgboost import XGBClassifier

# Prediction models:
# regressors and classifiers that take a positinal embedding vector as input,
# output storypoints (or other impact related value)

seed=42

class regressors:

    @staticmethod
    def get_autokeras_paraphrase5():
        model = load_model("regression_models/autokeras5_desc_paraphrase_rmse", custom_objects=ak.CUSTOM_OBJECTS)
        return model

    @staticmethod
    def get_autokeras_roberta3_mae():
        model = load_model("regression_models/autokeras3_roberta_mae", custom_objects=ak.CUSTOM_OBJECTS)
        return model

    @staticmethod
    def keras_convolutional(X_train, y_train, X_test, y_test, vocab_size, max_len):
        #https://realpython.com/python-keras-text-classification/
        callbacks = [
            EarlyStopping(patience=5, restore_best_weights=True, mode='min')
        ]

        model = Sequential()
        model.add(Embedding(input_dim=vocab_size+1,
                            output_dim=50,
                            input_length=max_len))
        model.add(Conv1D(50, 5, activation='relu'))
        model.add(GlobalMaxPool1D())
        model.add(Dense(units=25, activation='relu'))
        model.add(Dense(units=1, activation='relu'))

        model.compile(optimizer=Adam(learning_rate=0.0001),
                    loss='mae',
                    metrics=['mse'],
                    run_eagerly=True)

        history = model.fit(X_train, y_train,
                            epochs=15,
                            verbose=True,
                            validation_data=(X_test, y_test),
                            batch_size=50,
                            callbacks=callbacks)

        return model

    @staticmethod
    def create_MLP(X, y):
        model = MLPRegressor(random_state=seed)
        model = model.fit(X, y)

        pipe = Pipeline([('mlp', model)])
        param_grid = {
            'mlp__solver': ['sgd'],
            'mlp__alpha': [0.01],
            'mlp__learning_rate_init': [0.0001],
            'mlp__max_iter': [300]
        }

        gs = gridsearch(pipe, param_grid, 'neg_mean_squared_error')
        gs.fit(X, y)

        data_manager.print_gridsearch_best_stats(gs)
        return model

    @staticmethod
    def create_SVR(X, y):
        model = svm.SVR()

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('svr', model)])
        param_grid = {
            'svr__C': [1.75], #1.957,1.8,2,1.7 #multi lang 1.75
            'svr__gamma': ['scale'],
            'svr__kernel': ['rbf'],
            'svr__epsilon': [0.01], #0.1,0.01 #multi lang 0.01
            'svr__degree': [2] #2,3,4
        }

        gs = gridsearch(pipe, param_grid, 'neg_mean_absolute_error')  #neg_mean_squared_error
        gs.fit(X, y)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod
    def create_Randomforest(X, y):
        model = RandomForestRegressor(random_state=seed, n_estimators=300, min_samples_leaf=4, max_depth=20)

        pipe = Pipeline([('rtree', model)])

        param_grid = {
            # 'rtree__n_estimators': [300],
            # 'rtree__min_samples_leaf': [4],
            # 'rtree__max_depth': [20]
        }

        gs = gridsearch(pipe, param_grid, 'neg_mean_absolute_error')
        gs.fit(X, y)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod
    def create_XGBregressor(X_train, y_train):
        model = XGBRegressor(learning_rate=0.001,
                            n_estimators=400,
                            n_jobs=5,
                            random_state=seed)

        pipe = Pipeline([('XGB', model)])
        param_grid = {
        }

        gs = gridsearch(pipe, param_grid, 'neg_mean_squared_error')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)
        return model

    @staticmethod
    def keras_sequential_network(X_train, y_train, X_test, y_test, lr=0.001):
        input_dim = len(X_train[0])

        callbacks = [
            EarlyStopping(patience=5, restore_best_weights=True, mode='min')
        ]

        model = Sequential()
        model.add(Dense(100, input_dim=input_dim, kernel_initializer='normal', activation='relu'))
        model.add(Dense(20, activation='relu'))
        model.add(Dense(10, activation='relu'))
        model.add(Dense(1, activation='linear'))

        model.compile(loss='mse', optimizer=Adam(learning_rate=lr), metrics=['mse', 'mae'], run_eagerly=True)

        model.fit(X_train, y_train,
                            epochs=15,
                            verbose=True,
                            validation_data=(X_test, y_test),
                            batch_size=50,
                            callbacks=callbacks)

        pipe = Pipeline([('nn', model)])

        param_grid = {}

        gs = gridsearch(pipe, param_grid, 'neg_mean_squared_error')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return model

class classifiers(object):
    @staticmethod #0.7133 - F 0.7220 - H 0.7281 - H2 0.73152
    def create_mlpclassifier(X_train, y_train):
        model = MLPClassifier(random_state=seed)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('sgd', model)])

        param_grid = {
            'mlp__max_iter':[200], #200, 400, 600, 800 | 200
            'mlp__solver':['adam'], #'adam', 'lbfgs' | 'adam'
            'mlp__alpha':[0.001], #0.0001, 0.001 | 0.001
            'mlp__batch_size':[50], #100, 150, 200, 400 | 50
            'mlp__learning_rate_init':[0.0001] #0.01, 0.001, 0.0001 | 0.0001
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod #0.6709 - F 0.6817
    def create_Randomforest(X_train, y_train):
        model = RandomForestClassifier(random_state=seed)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('sgd', model)])

        param_grid = {
            # 'rtree__n_estimators': [700], #best from range 150 - 700
            # 'rtree__min_samples_leaf': [2], #best from range 1 - 7
            # 'rtree__max_depth': [20]
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod #0.7147 - F 0.7206 - H 0.7256 - H2 0.72656
    def create_XGB(X_train, y_train):
        model = XGBClassifier(seed=seed,
                              use_label_encoder=False)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('xgb', model)])

        param_grid = {
            'xgb__learning_rate':[0.05, 0.03], #0.2, 0.1, 0.15, 0.01 | 0.05
            'xgb__n_estimators':[600, 800], #100, 300, 400, 500 | 600
            'xgb__max_depth':[7], #4, 5, 6, 7, 8 | 7
            'xgb__colsample_bytree':[0.2], #0.1, 0.2 | 0.2
            'xgb__reg_lambda':[4, 6, 8] #1, 2, 3, 4 | 4
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod #0.6750 - F 0.6885
    def create_GB(X_train, y_train):

        #max_depth=6, n_estimators=500, random_state=42))])
        # best parms:  {'gb__learning_rate': 0.1, 'gb__max_depth': 6, 'gb__n_estimators': 500}
        model = GradientBoostingClassifier(random_state=seed)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('gb', model)])
        param_grid = {
            # 'gb__n_estimators': [500], #50 - 600
            # 'gb__learning_rate': [0.1], #0.2 - 0.01
            # 'gb__max_depth': [6], #1-7
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod #0.7152 - F 0.7195 H 0.73417
    def create_SVC(X_train, y_train):
        model = svm.SVC(random_state=seed,
                        probability=True)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('svc', model)])

        param_grid = {
            'svc__kernel': ['rbf'], #'rbf', 'linear' | rbf
            'svc__degree': [2], #2,3,4 | 2
            'svc__gamma': ['scale'], #'auto', 'scale' | 'scale'
            'svc__C': [1.95] #1, 1.95 | 1.95
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod #0.6670 - F 0.6735
    def create_KNN(X_train, y_train):
        model = KNeighborsClassifier()

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('KNN', model)])

        param_grid = {
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod #0.6764 - F 0.667
    def create_SGD(X_train, y_train):
        model = SGDClassifier(random_state=seed)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('sgd', model)])

        param_grid = {
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod #F - 0.7311 - H2 0.73587
    def create_voting(X_train, y_train):
        SVC = svm.SVC(random_state=seed,
                      probability=True,
                      kernel='rbf',
                      degree=2,
                      gamma='scale',
                      C=1.95)
        XGB = XGBClassifier(seed=seed,
                            learning_rate=0.05,
                            n_estimators=600,
                            max_depth=7,
                            reg_lambda=4,
                            colsample_bytree=0.2,
                            use_label_encoder=False)
        MLP = MLPClassifier(random_state=seed,
                            max_iter=200,
                            solver='adam',
                            alpha=0.001,
                            batch_size=50,
                            learning_rate_init=0.0001)

        estimators = [
            ('svc', SVC),
            ('xgb', XGB),
            ('mlp', MLP)
        ]

        model = VotingClassifier(
            estimators=estimators,
            voting='soft',
            weights=[1,1,1],
            n_jobs=-1,
            verbose=True)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('vc', model)])

        param_grid = {
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        print('voting done')
        return gs

    @staticmethod #F - 0.72848 - H2 0.7373
    def create_stacking(X_train, y_train):
        SVC = svm.SVC(random_state=seed,
                      probability=True,
                      kernel='rbf',
                      degree=2,
                      gamma='scale',
                      C=1.95)
        XGB = XGBClassifier(seed=seed,
                            learning_rate=0.05,
                            n_estimators=600,
                            max_depth=7,
                            reg_lambda=4,
                            colsample_bytree=0.2,
                            use_label_encoder=False)
        MLP = MLPClassifier(random_state=seed,
                            max_iter=200,
                            solver='adam',
                            alpha=0.001,
                            batch_size=50,
                            learning_rate_init=0.0001)

        estimators = [
            ('svc', SVC),
            ('xgb', XGB),
            ('mlp', MLP)
        ]

        model = StackingClassifier(
            estimators=estimators,
            final_estimator=LogisticRegression(random_state=42)
        )

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('stack', model)])

        param_grid = {
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        print('stacking done')

        data_manager.print_gridsearch_best_stats(gs)

        return gs

    @staticmethod
    def create_logisticregression(X_train, y_train):
        model = LogisticRegression(random_state=42)

        pipe = Pipeline([('standardize', StandardScaler()),
                         ('lg', model)])
        param_grid = {
            'lg__max_iter':[600]
        }

        gs = gridsearch(pipe, param_grid, 'recall_macro')
        gs.fit(X_train, y_train)

        data_manager.print_gridsearch_best_stats(gs)

        return gs

def gridsearch(pipe, param_grid, metric):
    gs = GridSearchCV(pipe,
                      param_grid,
                      verbose=0,
                      cv=5,
                      scoring=metric,
                      n_jobs=4,
                      return_train_score=True)

    return gs


import os
from app import create_app

if __name__ == '__main__':
    verify_token = os.getenv("VERIFY_TOKEN", None)
    access_token = os.getenv("ACCESS_TOKEN", None)
    url = os.getenv("URL", None)

    if not verify_token:
        raise Exception("verify_token not set")
    if not access_token:
        raise Exception("access_token not set")

    env = {
        "VERIFY_TOKEN": verify_token,
        "ACCESS_TOKEN": access_token,
        "URL": url
    }

    app = create_app.create_app(env=env)
    app.logger.info("Initializing")
    app.run(debug=True, host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))


# -*- coding: utf-8 -*-
# Author：sen
# Date：9090/3/94 10:48

from typing import List
from heapq import *
class Solution:
    def majorityElement(self, nums: List[int]) -> int:
        from collections import Counter
        counter = Counter(nums)
        for item in counter.items(): # item: (元素, 数量)
            if item[1] > (len(nums) / 2.0):
                return item[0]

class Solution2:
    def majorityElement(self, nums: List[int]) -> int:
        # 不使用自带Counter
        counter = {}
        for num in nums:
            counter[num] = counter.get(num, 0) + 1
        for item in counter.items():
            if item[1] > (len(nums) / 2.0):
                return item[0]

if __name__ == '__main__':
    nums = [9,9,8,8,8,9,9]
    so = Solution()
    print(so.majorityElement(nums))
    so = Solution2()
    print(so.majorityElement(nums))


"""
Very simple HTTP server in python for logging requests
Usage::
    ./server.py [<port>]
"""
from http.server import BaseHTTPRequestHandler, HTTPServer
from urllib import parse
import os
import logging

class S(BaseHTTPRequestHandler):
    def _set_response(self):
        self.send_response(200)
        self.send_header('Content-type', 'text/html')
        self.end_headers()

    def do_POST(self):
        content_length = int(self.headers['Content-Length']) # <--- Gets the size of data
        post_data = self.rfile.read(content_length) # <--- Gets the data itself
        # logging.info("POST request,\nPath: %s\nHeaders:\n%s\n\nBody:\n%s\n",
                # str(self.path), str(self.headers), post_data.decode('utf-8'))

        standard = post_data.decode("utf-8")
        dump = parse.parse_qs(standard)

        if "type" in dump.keys():
            if dump["type"][0] == "vote":
                writeVoteToFile(dump["titleID"][0])
            if dump["type"][0] == "chat":
                writeChatToFile(dump)

        self._set_response()

def run(server_class=HTTPServer, handler_class=S, port=3000):
    # logging.basicConfig(level=logging.INFO)
    server_address = ('', port)
    httpd = server_class(server_address, handler_class)
    # logging.info('Starting httpd...\n')
    try:
        httpd.serve_forever()
    except KeyboardInterrupt:
        pass
    httpd.server_close()
    # logging.info('Stopping httpd...\n')

# Safety operations
def doesIDExist(titleID):
    with open("/resources/text/titleinfo.txt", "r") as f:
        for line in f:
            if line[8:16] == titleID[8:16]:
                return True
    return False

# Saving operations
def writeVoteToFile(titleID):
    if doesIDExist(titleID):
        with open("/resources/text/vote.txt", "a") as f:
            f.write(titleID + "\r")

        os.system("echo \"$(tail -n 200 /resources/text/vote.txt)\" > /resources/text/vote.txt")
    else:
        print("Could not write vote for: " + titleID)

def writeChatToFile(details):
    with open("/resources/text/msg.txt", "a") as f:
        f.write(details["author"][0] + ";;" + details["time"][0] + ";;" + details["message"][0] +"\r")

    os.system("echo \"$(tail -n 200 /resources/text/msg.txt)\" > /resources/text/msg.txt")

# Main function
if __name__ == '__main__':
    from sys import argv

    if len(argv) == 2:
        run(port=int(argv[1]))
    else:
        run()