#
# Dimensional and dimensionless parameter values, and scales
#
import pybamm
import pandas as pd
import os
import numbers
import warnings
from pprint import pformat
from collections import defaultdict

class ParameterValues:
    """
    The parameter values for a simulation.

    Note that this class does not inherit directly from the python dictionary class as
    this causes issues with saving and loading simulations.

    Parameters
    ----------
    values : dict or string
        Explicit set of parameters, or reference to a file of parameters
        If string, gets passed to read_parameters_csv to read a file.
    chemistry : dict
        Dict of strings for default chemistries. Must be of the form:
        {"base chemistry": base_chemistry,
        "cell": cell_properties_authorYear,
        "negative electrode": negative_electrode_chemistry_authorYear,
        "separator": separator_chemistry_authorYear,
        "positive electrode": positive_electrode_chemistry_authorYear,
        "electrolyte": electrolyte_chemistry_authorYear,
        "experiment": experimental_conditions_authorYear}.
        Then the negative electrode chemistry is loaded from the file
        inputs/parameters/base_chemistry/negative electrodes/
        negative_electrode_chemistry_authorYear, etc.
        Parameters in "cell" should include geometry and current collector properties.
        Parameters in "experiment" should include parameters relating to experimental
        conditions, such as initial conditions and currents.

    Examples
    --------
    >>> import pybamm
    >>> values = {"some parameter": 1, "another parameter": 2}
    >>> param = pybamm.ParameterValues(values)
    >>> param["some parameter"]
    1
    >>> file = "input/parameters/lithium-ion/cells/kokam_Marquis2019/parameters.csv"
    >>> values_path = pybamm.get_parameters_filepath(file)
    >>> param = pybamm.ParameterValues(values=values_path)
    >>> param["Negative current collector thickness [m]"]
    2.5e-05
    >>> param = pybamm.ParameterValues(chemistry=pybamm.parameter_sets.Marquis2019)
    >>> param["Reference temperature [K]"]
    298.15

    """

    def __init__(self, values=None, chemistry=None):
        self._dict_items = pybamm.FuzzyDict()
        # Must provide either values or chemistry, not both (nor neither)
        if values is not None and chemistry is not None:
            raise ValueError(
                "Only one of values and chemistry can be provided. To change parameters"
                " slightly from a chemistry, first load parameters with the chemistry"
                " (param = pybamm.ParameterValues(chemistry=...)) and then update with"
                " param.update({dict of values})."
            )
        if values is None and chemistry is None:
            raise ValueError("values and chemistry cannot both be None")
        # First load chemistry
        if chemistry is not None:
            self.update_from_chemistry(chemistry)
        # Then update with values dictionary or file
        if values is not None:
            # If base_parameters is a filename, load from that filename
            if isinstance(values, str):
                file_path = self.find_parameter(values)
                path = os.path.split(file_path)[0]
                values = self.read_parameters_csv(file_path)
            else:
                path = ""
            # Don't check parameter already exists when first creating it
            self.update(values, check_already_exists=False, path=path)

        # Initialise empty _processed_symbols dict (for caching)
        self._processed_symbols = {}
        self.parameter_events = []

    def __getitem__(self, key):
        return self._dict_items[key]

    def get(self, key, default=None):
        """Return item correspoonding to key if it exists, otherwise return default"""
        try:
            return self._dict_items[key]
        except KeyError:
            return default

    def __setitem__(self, key, value):
        """Call the update functionality when doing a setitem"""
        self.update({key: value})

    def __delitem__(self, key):
        del self._dict_items[key]

    def __repr__(self):
        return pformat(self._dict_items, width=1)

    def keys(self):
        """Get the keys of the dictionary"""
        return self._dict_items.keys()

    def values(self):
        """Get the values of the dictionary"""
        return self._dict_items.values()

    def items(self):
        """Get the items of the dictionary"""
        return self._dict_items.items()

    def copy(self):
        """Returns a copy of the parameter values. Makes sure to copy the internal
        dictionary."""
        return ParameterValues(values=self._dict_items.copy())

    def search(self, key, print_values=True):
        """
        Search dictionary for keys containing 'key'.

        See :meth:`pybamm.FuzzyDict.search()`.
        """
        return self._dict_items.search(key, print_values)

    def update_from_chemistry(self, chemistry):
        """
        Load standard set of components from a 'chemistry' dictionary
        """
        base_chemistry = chemistry["chemistry"]

        # Load each component name

        component_groups = [
            "cell",
            "negative electrode",
            "positive electrode",
            "separator",
            "electrolyte",
            "experiment",
        ]

        # add SEI parameters if provided
        if "sei" in chemistry:
            component_groups += ["sei"]

        if "anode" in chemistry.keys():
            if "negative electrode" in chemistry.keys():
                raise KeyError(
                    "both 'anode' and 'negative electrode' keys provided in the "
                    "chemistry. The 'anode' notation will be deprecated in the next "
                    "release so 'negative electrode' should be used instead."
                )
            else:
                chemistry["negative electrode"] = chemistry["anode"]
                warnings.warn(
                    "the 'anode' component notation will be deprecated in the next "
                    "release, as it has now been renamed to 'negative electrode'. "
                    "Simulation will continue passing the 'anode' component as "
                    "'negative electrode' (it might overwrite any existing definition "
                    "of the component).",
                    DeprecationWarning,
                )

        if "cathode" in chemistry.keys():
            if "positive electrode" in chemistry.keys():
                raise KeyError(
                    "both 'cathode' and 'positive electrode' keys provided in the "
                    "chemistry. The 'cathode' notation will be deprecated in the next "
                    "release so 'positive electrode' should be used instead."
                )
            else:
                chemistry["positive electrode"] = chemistry["cathode"]
                warnings.warn(
                    "the 'cathode' component notation will be deprecated in the next "
                    "release, as it has now been renamed to 'positive electrode'. "
                    "Simulation will continue passing the 'cathode' component as "
                    "'positive electrode' (it might overwrite any existing definition "
                    "of the component).",
                    DeprecationWarning,
                )

        for component_group in component_groups:
            # Make sure component is provided
            try:
                component = chemistry[component_group]
            except KeyError:
                raise KeyError(
                    "must provide '{}' parameters for {} chemistry".format(
                        component_group, base_chemistry
                    )
                )
            # Create path to component and load values
            component_path = os.path.join(
                base_chemistry, component_group.replace(" ", "_") + "s", component
            )
            file_path = self.find_parameter(
                os.path.join(component_path, "parameters.csv")
            )
            component_params = self.read_parameters_csv(file_path)

            # Update parameters, making sure to check any conflicts
            self.update(
                component_params,
                check_conflict=True,
                check_already_exists=False,
                path=os.path.dirname(file_path),
            )

        # register (list of) citations
        if "citation" in chemistry:
            citations = chemistry["citation"]
            if not isinstance(citations, list):
                citations = [citations]
            for citation in citations:
                pybamm.citations.register(citation)

    def read_parameters_csv(self, filename):
        """Reads parameters from csv file into dict.

        Parameters
        ----------
        filename : str
            The name of the csv file containing the parameters.

        Returns
        -------
        dict
            {name: value} pairs for the parameters.

        """
        df = pd.read_csv(filename, comment="#", skip_blank_lines=True)
        # Drop rows that are all NaN (seems to not work with skip_blank_lines)
        df.dropna(how="all", inplace=True)
        return {k: v for (k, v) in zip(df["Name [units]"], df["Value"])}

    def update(self, values, check_conflict=False, check_already_exists=True, path=""):
        """
        Update parameter dictionary, while also performing some basic checks.

        Parameters
        ----------
        values : dict
            Dictionary of parameter values to update parameter dictionary with
        check_conflict : bool, optional
            Whether to check that a parameter in `values` has not already been defined
            in the parameter class when updating it, and if so that its value does not
            change. This is set to True during initialisation, when parameters are
            combined from different sources, and is False by default otherwise
        check_already_exists : bool, optional
            Whether to check that a parameter in `values` already exists when trying to
            update it. This is to avoid cases where an intended change in the parameters
            is ignored due a typo in the parameter name, and is True by default but can
            be manually overridden.
        path : string, optional
            Path from which to load functions
        """
        # check parameter values
        self.check_parameter_values(values)
        # update
        for name, value in values.items():
            # check for conflicts
            if (
                check_conflict is True
                and name in self.keys()
                and not (self[name] == float(value) or self[name] == value)
            ):
                raise ValueError(
                    "parameter '{}' already defined with value '{}'".format(
                        name, self[name]
                    )
                )
            # check parameter already exists (for updating parameters)
            if check_already_exists is True:
                try:
                    self._dict_items[name]
                except KeyError as err:
                    raise KeyError(
                        "Cannot update parameter '{}' as it does not ".format(name)
                        + "have a default value. ({}). If you are ".format(err.args[0])
                        + "sure you want to update this parameter, use "
                        + "param.update({{name: value}}, check_already_exists=False)"
                    )
            # if no conflicts, update, loading functions and data if they are specified
            # Functions are flagged with the string "[function]"
            if isinstance(value, str):
                if value.startswith("[function]"):
                    loaded_value = pybamm.load_function(
                        os.path.join(path, value[10:] + ".py")
                    )
                    self._dict_items[name] = loaded_value
                    values[name] = loaded_value
                # Data is flagged with the string "[data]" or "[current data]"
                elif value.startswith("[current data]") or value.startswith("[data]"):
                    if value.startswith("[current data]"):
                        data_path = os.path.join(
                            pybamm.root_dir(), "pybamm", "input", "drive_cycles"
                        )
                        filename = os.path.join(data_path, value[14:] + ".csv")
                        function_name = value[14:]
                    else:
                        filename = os.path.join(path, value[6:] + ".csv")
                        function_name = value[6:]
                    filename = pybamm.get_parameters_filepath(filename)
                    data = pd.read_csv(
                        filename, comment="#", skip_blank_lines=True, header=None
                    ).to_numpy()
                    # Save name and data
                    self._dict_items[name] = (function_name, data)
                    values[name] = (function_name, data)
                elif value == "[input]":
                    self._dict_items[name] = pybamm.InputParameter(name)
                # Anything else should be a converted to a float
                else:
                    self._dict_items[name] = float(value)
                    values[name] = float(value)
            else:
                self._dict_items[name] = value
        # reset processed symbols
        self._processed_symbols = {}

    def check_parameter_values(self, values):
        # Make sure typical current is non-zero
        if "Typical current [A]" in values and values["Typical current [A]"] == 0:
            raise ValueError(
                "'Typical current [A]' cannot be zero. A possible alternative is to "
                "set 'Current function [A]' to `0` instead."
            )
        if "C-rate" in values:
            raise ValueError(
                "The 'C-rate' parameter has been deprecated, "
                "use 'Current function [A]' instead. The Nominal "
                "cell capacity can be accessed as 'Nominal cell "
                "capacity [A.h]', and used to calculate current from C-rate."
            )
        if "Cell capacity [A.h]" in values:
            if "Nominal cell capacity [A.h]" in values:
                raise ValueError(
                    "both 'Cell capacity [A.h]' and 'Nominal cell capacity [A.h]' "
                    "provided in values. The 'Cell capacity [A.h]' notation will be "
                    "deprecated in the next release so 'Nominal cell capacity [A.h]' "
                    "should be used instead."
                )
            else:
                values["Nominal cell capacity [A.h]"] = values["Cell capacity [A.h]"]
                warnings.warn(
                    "the 'Cell capacity [A.h]' notation will be "
                    "deprecated in the next release, as it has now been renamed "
                    "to 'Nominal cell capacity [A.h]'. Simulation will continue "
                    "passing the 'Cell capacity [A.h]' as 'Nominal cell "
                    "capacity [A.h]' (it might overwrite any existing definition "
                    "of the component)",
                    DeprecationWarning,
                )
        for param in values:
            if "surface area density" in param:
                raise ValueError(
                    "Parameters involving 'surface area density' have been renamed to "
                    "'surface area to volume ratio' ('{}' found)".format(param)
                )
            if "reaction rate" in param:
                raise ValueError(
                    "Parameters involving 'reaction rate' have been replaced with "
                    "'exchange-current density' ('{}' found)".format(param)
                )
        for param in values:
            if "particle distribution in x" in param:
                raise ValueError(
                    "The parameter '{}' has been deprecated".format(param)
                    + "The particle radius is now set as a function of x directly "
                    "instead of providing a reference value and a distribution."
                )
        for param in values:
            if "surface area to volume ratio distribution in x" in param:
                raise ValueError(
                    "The parameter '{}' has been deprecated".format(param)
                    + "The surface area to volume ratio is now set as a function "
                    "of x directly instead of providing a reference value and a "
                    "distribution."
                )

    def process_model(self, unprocessed_model, inplace=True):
        """Assign parameter values to a model.
        Currently inplace, could be changed to return a new model.

        Parameters
        ----------
        unprocessed_model : :class:`pybamm.BaseModel`
            Model to assign parameter values for
        inplace: bool, optional
            If True, replace the parameters in the model in place. Otherwise, return a
            new model with parameter values set. Default is True.

        Raises
        ------
        :class:`pybamm.ModelError`
            If an empty model is passed (`model.rhs = {}` and `model.algebraic = {}` and
            `model.variables = {}`)

        """
        pybamm.logger.info(
            "Start setting parameters for {}".format(unprocessed_model.name)
        )

        # set up inplace vs not inplace
        if inplace:
            # any changes to unprocessed_model attributes will change model attributes
            # since they point to the same object
            model = unprocessed_model
        else:
            # create a blank model of the same class
            model = unprocessed_model.new_empty_copy()

        if (
            len(unprocessed_model.rhs) == 0
            and len(unprocessed_model.algebraic) == 0
            and len(unprocessed_model.variables) == 0
        ):
            raise pybamm.ModelError("Cannot process parameters for empty model")

        new_rhs = {}
        for variable, equation in unprocessed_model.rhs.items():
            pybamm.logger.verbose(
                "Processing parameters for {!r} (rhs)".format(variable)
            )
            new_rhs[variable] = self.process_symbol(equation)
        model.rhs = new_rhs

        new_algebraic = {}
        for variable, equation in unprocessed_model.algebraic.items():
            pybamm.logger.verbose(
                "Processing parameters for {!r} (algebraic)".format(variable)
            )
            new_algebraic[variable] = self.process_symbol(equation)
        model.algebraic = new_algebraic

        new_initial_conditions = {}
        for variable, equation in unprocessed_model.initial_conditions.items():
            pybamm.logger.verbose(
                "Processing parameters for {!r} (initial conditions)".format(variable)
            )
            new_initial_conditions[variable] = self.process_symbol(equation)
        model.initial_conditions = new_initial_conditions

        model.boundary_conditions = self.process_boundary_conditions(unprocessed_model)

        new_variables = {}
        for variable, equation in unprocessed_model.variables.items():
            pybamm.logger.verbose(
                "Processing parameters for {!r} (variables)".format(variable)
            )
            new_variables[variable] = self.process_symbol(equation)
        model.variables = new_variables

        new_events = []
        for event in unprocessed_model.events:
            pybamm.logger.verbose(
                "Processing parameters for event '{}''".format(event.name)
            )
            new_events.append(
                pybamm.Event(
                    event.name, self.process_symbol(event.expression), event.event_type
                )
            )

        for event in self.parameter_events:
            pybamm.logger.verbose(
                "Processing parameters for event '{}''".format(event.name)
            )
            new_events.append(
                pybamm.Event(
                    event.name, self.process_symbol(event.expression), event.event_type
                )
            )

        model.events = new_events

        # Set external variables
        model.external_variables = [
            self.process_symbol(var) for var in unprocessed_model.external_variables
        ]

        # Process timescale
        model.timescale = self.process_symbol(unprocessed_model.timescale)

        # Process length scales
        new_length_scales = {}
        for domain, scale in unprocessed_model.length_scales.items():
            new_length_scales[domain] = self.process_symbol(scale)
        model.length_scales = new_length_scales

        pybamm.logger.info("Finish setting parameters for {}".format(model.name))

        return model

    def process_boundary_conditions(self, model):
        """
        Process boundary conditions for a model
        Boundary conditions are dictionaries {"left": left bc, "right": right bc}
        in general, but may be imposed on the tabs (or *not* on the tab) for a
        small number of variables, e.g. {"negative tab": neg. tab bc,
        "positive tab": pos. tab bc "no tab": no tab bc}.
        """
        new_boundary_conditions = {}
        sides = ["left", "right", "negative tab", "positive tab", "no tab"]
        for variable, bcs in model.boundary_conditions.items():
            processed_variable = self.process_symbol(variable)
            new_boundary_conditions[processed_variable] = {}
            for side in sides:
                try:
                    bc, typ = bcs[side]
                    pybamm.logger.verbose(
                        "Processing parameters for {!r} ({} bc)".format(variable, side)
                    )
                    processed_bc = (self.process_symbol(bc), typ)
                    new_boundary_conditions[processed_variable][side] = processed_bc
                except KeyError as err:
                    # don't raise error if the key error comes from the side not being
                    # found
                    if err.args[0] in side:
                        pass
                    # do raise error otherwise (e.g. can't process symbol)
                    else:
                        raise KeyError(err)

        return new_boundary_conditions

    def update_model(self, model, disc):
        raise NotImplementedError(
            """
            update_model functionality has been deprecated.
            Use pybamm.InputParameter to quickly change a parameter value instead
            """
        )

    def process_geometry(self, geometry):
        """
        Assign parameter values to a geometry (inplace).

        Parameters
        ----------
        geometry : dict
            Geometry specs to assign parameter values to
        """
        for domain in geometry:
            for spatial_variable, spatial_limits in geometry[domain].items():
                # process tab information if using 1 or 2D current collectors
                if spatial_variable == "tabs":
                    for tab, position_size in spatial_limits.items():
                        for position_size, sym in position_size.items():
                            geometry[domain]["tabs"][tab][
                                position_size
                            ] = self.process_symbol(sym)
                else:
                    for lim, sym in spatial_limits.items():
                        if isinstance(sym, pybamm.Symbol):
                            geometry[domain][spatial_variable][
                                lim
                            ] = self.process_symbol(sym)

    def process_symbol(self, symbol):
        """Walk through the symbol and replace any Parameter with a Value.
        If a symbol has already been processed, the stored value is returned.

        Parameters
        ----------
        symbol : :class:`pybamm.Symbol`
            Symbol or Expression tree to set parameters for

        Returns
        -------
        symbol : :class:`pybamm.Symbol`
            Symbol with Parameter instances replaced by Value

        """

        try:
            return self._processed_symbols[symbol.id]
        except KeyError:
            processed_symbol = self._process_symbol(symbol)
            self._processed_symbols[symbol.id] = processed_symbol

            return processed_symbol

    def _process_symbol(self, symbol):
        """ See :meth:`ParameterValues.process_symbol()`. """

        if isinstance(symbol, pybamm.Parameter):
            value = self[symbol.name]
            if isinstance(value, numbers.Number):
                # Scalar inherits name (for updating parameters) and domain (for
                # Broadcast)
                return pybamm.Scalar(value, name=symbol.name, domain=symbol.domain)
            elif isinstance(value, pybamm.Symbol):
                new_value = self.process_symbol(value)
                new_value.domain = symbol.domain
                return new_value
            else:
                raise TypeError("Cannot process parameter '{}'".format(value))

        elif isinstance(symbol, pybamm.FunctionParameter):
            new_children = []
            for child in symbol.children:
                if symbol.diff_variable is not None and any(
                    x.id == symbol.diff_variable.id for x in child.pre_order()
                ):
                    # Wrap with NotConstant to avoid simplification,
                    # which would stop symbolic diff from working properly
                    new_child = pybamm.NotConstant(child.new_copy())
                    new_children.append(self.process_symbol(new_child))
                else:
                    new_children.append(self.process_symbol(child))
            function_name = self[symbol.name]

            # Create Function or Interpolant or Scalar object
            if isinstance(function_name, tuple):
                # If function_name is a tuple then it should be (name, data) and we need
                # to create an Interpolant
                name, data = function_name
                function = pybamm.Interpolant(
                    data[:, 0], data[:, 1], *new_children, name=name
                )
                # Define event to catch extrapolation. In these events the sign is
                # important: it should be positive inside of the range and negative
                # outside of it
                self.parameter_events.append(
                    pybamm.Event(
                        "Interpolant {} lower bound".format(name),
                        pybamm.min(new_children[0] - min(data[:, 0])),
                        pybamm.EventType.INTERPOLANT_EXTRAPOLATION,
                    )
                )
                self.parameter_events.append(
                    pybamm.Event(
                        "Interpolant {} upper bound".format(name),
                        pybamm.min(max(data[:, 0]) - new_children[0]),
                        pybamm.EventType.INTERPOLANT_EXTRAPOLATION,
                    )
                )
            elif isinstance(function_name, numbers.Number):
                # If the "function" is provided is actually a scalar, return a Scalar
                # object instead of throwing an error.
                # Also use ones_like so that we get the right shapes
                function = pybamm.Scalar(
                    function_name, name=symbol.name
                ) * pybamm.ones_like(*new_children)
            elif (
                isinstance(function_name, pybamm.Symbol)
                and function_name.evaluates_to_number()
            ):
                # If the "function" provided is a pybamm scalar-like, use ones_like to
                # get the right shape
                # This also catches input parameters
                function = function_name * pybamm.ones_like(*new_children)
            elif callable(function_name):
                # otherwise evaluate the function to create a new PyBaMM object
                function = function_name(*new_children)
            elif isinstance(function_name, pybamm.Interpolant):
                function = function_name
            else:
                raise TypeError(
                    "Parameter provided for '{}' ".format(symbol.name)
                    + "is of the wrong type (should either be scalar-like or callable)"
                )
            # Differentiate if necessary
            if symbol.diff_variable is None:
                function_out = function
            else:
                # return differentiated function
                new_diff_variable = self.process_symbol(symbol.diff_variable)
                function_out = function.diff(new_diff_variable)
            # Convert possible float output to a pybamm scalar
            if isinstance(function_out, numbers.Number):
                return pybamm.Scalar(function_out)
            # Process again just to be sure
            return self.process_symbol(function_out)

        elif isinstance(symbol, pybamm.BinaryOperator):
            # process children
            new_left = self.process_symbol(symbol.left)
            new_right = self.process_symbol(symbol.right)
            # Special case for averages, which can appear as "integral of a broadcast"
            # divided by "integral of a broadcast"
            # this construction seems very specific but can appear often when averaging
            if (
                isinstance(symbol, pybamm.Division)
                # right is integral(Broadcast(1))
                and (
                    isinstance(new_right, pybamm.Integral)
                    and isinstance(new_right.child, pybamm.Broadcast)
                    and new_right.child.child.id == pybamm.Scalar(1).id
                )
                # left is integral
                and isinstance(new_left, pybamm.Integral)
            ):
                # left is integral(Broadcast)
                if (
                    isinstance(new_left.child, pybamm.Broadcast)
                    and new_left.child.child.domain == []
                ):
                    integrand = new_left.child
                    if integrand.auxiliary_domains == {}:
                        return integrand.orphans[0]
                    else:
                        domain = integrand.auxiliary_domains["secondary"]
                        if "tertiary" not in integrand.auxiliary_domains:
                            return pybamm.PrimaryBroadcast(integrand.orphans[0], domain)
                        else:
                            auxiliary_domains = {
                                "secondary": integrand.auxiliary_domains["tertiary"]
                            }
                            return pybamm.FullBroadcast(
                                integrand.orphans[0], domain, auxiliary_domains
                            )
                # left is "integral of concatenation of broadcasts"
                elif isinstance(new_left.child, pybamm.Concatenation) and all(
                    isinstance(child, pybamm.Broadcast)
                    for child in new_left.child.children
                ):
                    return self.process_symbol(pybamm.x_average(new_left.child))
            # make new symbol, ensure domain remains the same
            new_symbol = symbol._binary_new_copy(new_left, new_right)
            new_symbol.domain = symbol.domain
            return new_symbol

        # Unary operators
        elif isinstance(symbol, pybamm.UnaryOperator):
            new_child = self.process_symbol(symbol.child)
            new_symbol = symbol._unary_new_copy(new_child)
            # ensure domain remains the same
            new_symbol.domain = symbol.domain
            return new_symbol

        # Functions
        elif isinstance(symbol, pybamm.Function):
            new_children = [self.process_symbol(child) for child in symbol.children]
            return symbol._function_new_copy(new_children)

        # Concatenations
        elif isinstance(symbol, pybamm.Concatenation):
            new_children = [self.process_symbol(child) for child in symbol.children]
            return symbol._concatenation_new_copy(new_children)

        else:
            # Backup option: return new copy of the object
            try:
                return symbol.new_copy()
            except NotImplementedError:
                raise NotImplementedError(
                    "Cannot process parameters for symbol of type '{}'".format(
                        type(symbol)
                    )
                )

    def evaluate(self, symbol):
        """
        Process and evaluate a symbol.

        Parameters
        ----------
        symbol : :class:`pybamm.Symbol`
            Symbol or Expression tree to evaluate

        Returns
        -------
        number of array
            The evaluated symbol
        """
        processed_symbol = self.process_symbol(symbol)
        if processed_symbol.evaluates_to_constant_number():
            return processed_symbol.evaluate()
        else:
            raise ValueError("symbol must evaluate to a constant scalar")

    def _ipython_key_completions_(self):
        return list(self._dict_items.keys())

    def export_csv(self, filename):

        # process functions and data to output
        # like they appear in inputs csv files
        parameter_output = {}
        for key, val in self.items():
            if callable(val):
                val = "[function]" + val.__name__
            elif isinstance(val, tuple):
                val = "[data]" + val[0]
            parameter_output[key] = [val]

        df = pd.DataFrame(parameter_output)
        df = df.transpose()
        df.to_csv(filename, header=None)

    def print_parameters(self, parameters, output_file=None):
        """
        Return dictionary of evaluated parameters, and optionally print these evaluated
        parameters to an output file.
        For dimensionless parameters that depend on the C-rate, the value is given as a
        function of the C-rate (either x * Crate or x / Crate depending on the
        dependence)

        Parameters
        ----------
        parameters : class or dict containing :class:`pybamm.Parameter` objects
            Class or dictionary containing all the parameters to be evaluated
        output_file : string, optional
            The file to print parameters to. If None, the parameters are not printed,
            and this function simply acts as a test that all the parameters can be
            evaluated, and returns the dictionary of evaluated parameters.

        Returns
        -------
        evaluated_parameters : defaultdict
            The evaluated parameters, for further processing if needed

        Notes
        -----
        A C-rate of 1 C is the current required to fully discharge the battery in 1
        hour, 2 C is current to discharge the battery in 0.5 hours, etc
        """
        # Set list of attributes to ignore, for when we are evaluating parameters from
        # a class of parameters
        ignore = [
            "__name__",
            "__doc__",
            "__package__",
            "__loader__",
            "__spec__",
            "__file__",
            "__cached__",
            "__builtins__",
            "absolute_import",
            "division",
            "print_function",
            "unicode_literals",
            "pybamm",
            "constants",
            "np",
            "geo",
            "elec",
            "therm",
        ]

        # If 'parameters' is a class, extract the dict
        if not isinstance(parameters, dict):
            parameters = {
                k: v for k, v in parameters.__dict__.items() if k not in ignore
            }

        evaluated_parameters = defaultdict(list)
        # Calculate parameters for each C-rate
        for Crate in [1, 10]:
            # Update Crate
            capacity = self.get("Nominal cell capacity [A.h]")
            if capacity is not None:
                self.update(
                    {"Current function [A]": Crate * capacity},
                    check_already_exists=False,
                )
            for name, symbol in parameters.items():
                if not callable(symbol):
                    proc_symbol = self.process_symbol(symbol)
                    if not (
                        callable(proc_symbol)
                        or proc_symbol.has_symbol_of_classes(
                            (pybamm.Concatenation, pybamm.Broadcast)
                        )
                    ):
                        evaluated_parameters[name].append(proc_symbol.evaluate(t=0))

        # Calculate C-dependence of the parameters based on the difference between the
        # value at 1C and the value at C / 10
        for name, values in evaluated_parameters.items():
            if values[1] == 0 or abs(values[0] / values[1] - 1) < 1e-10:
                C_dependence = ""
            elif abs(values[0] / values[1] - 10) < 1e-10:
                C_dependence = " * Crate"
            elif abs(values[0] / values[1] - 0.1) < 1e-10:
                C_dependence = " / Crate"
            evaluated_parameters[name] = (values[0], C_dependence)
        # Print the evaluated_parameters dict to output_file
        if output_file:
            self.print_evaluated_parameters(evaluated_parameters, output_file)

        return evaluated_parameters

    def print_evaluated_parameters(self, evaluated_parameters, output_file):
        """
        Print a dictionary of evaluated parameters to an output file

        Parameters
        ----------
        evaluated_parameters : defaultdict
            The evaluated parameters, for further processing if needed
        output_file : string, optional
            The file to print parameters to. If None, the parameters are not printed,
            and this function simply acts as a test that all the parameters can be
            evaluated

        """
        # Get column width for pretty printing
        column_width = max(len(name) for name in evaluated_parameters.keys())
        s = "{{:>{}}}".format(column_width)
        with open(output_file, "w") as file:
            for name, (value, C_dependence) in sorted(evaluated_parameters.items()):
                if 0.001 < abs(value) < 1000:
                    file.write(
                        (s + " : {:10.4g}{!s}\n").format(name, value, C_dependence)
                    )
                else:
                    file.write(
                        (s + " : {:10.3E}{!s}\n").format(name, value, C_dependence)
                    )

    @staticmethod
    def find_parameter(path):
        """Look for parameter file in the different locations
        in PARAMETER_PATH
        """
        for location in pybamm.PARAMETER_PATH:
            trial_path = os.path.join(location, path)
            if os.path.isfile(trial_path):
                return trial_path
        raise FileNotFoundError("Could not find parameter {}".format(path))


# -*- coding: utf-8 -*-
#
# Rewrite the simulated data path ('sim-data.txt') as you see fit.
# try `$ pytest tests -s`
# to print results to the standard output.
#
from bbc1.lib.smart_rfid_reader_drv import SimpleRfidReaderSimulator

def test_simulator():

    reader = SimpleRfidReaderSimulator('sim-data.txt')

    for i in range(10):
        aTags = reader.read()

        print('Case {0}: {1} tag(s):'.format(i, len(aTags)))
        for sTag in aTags:
            print(sTag)

    reader.close()

# end of tests/test_simulator_drv.py


from os import getcwd, listdir
from os.path import isfile, join

diry = raw_input("Please input the path of the directory(enter nothing if it is current directory): ")
if diry == "":
    diry = getcwd()
files = [f for f in listdir(diry) if isfile(join(diry, f))]
count = {}
types = [".py", ".cpp", ".c", ".js", ".html", ".css", ".cs", ".h", ".java"]
for f in files:
    isCodingFile = False
    for ty in types:
        if ty in f:
            isCodingFile = True
            break
    if not isCodingFile:
        continue
    fin = open(join(diry, f), "r")
    codes = fin.read().split('\n')
    count[f] = len(codes)
    fin.close()
fout = open(join(diry, "summary.txt"), "w")
for item in count:
    fout.write(item + ": " + str(count[item]) + "\n")
fout.close()


import torch
import torch_geometric
import networkx as nx
import random

TorchNodes = torch.LongTensor
TorchEdges = torch.LongTensor
TorchMappings = torch.LongTensor
TorchEdgeMask = torch.BoolTensor

def augment(s_1: TorchEdges, s_1_nodes: TorchNodes, s_2: TorchEdges,  s_2_nodes: TorchNodes, G_noisy: nx.Graph, t: set(("src", "tar"))) -> (TorchEdges, TorchEdges, list(("src", "tar")), list(("src", "tar"))):
    d1 = torch_geometric.data.Data(edge_index=s_1)
    d1.num_nodes = 0
    d2 = torch_geometric.data.Data(edge_index=s_2)
    d2.num_nodes = 0
    s_1_nx = torch_geometric.utils.to_networkx(d1, to_undirected=False)
    s_2_nx = torch_geometric.utils.to_networkx(d2, to_undirected=False)
    s_12_nx = G_noisy.subgraph(list(s_1_nx.nodes) + list(s_2_nx.nodes)).copy()

    y_s_12 = list(filter(lambda x: s_12_nx.has_edge(x[0], x[1]), t))

    y_s_12_sample = []
    if len(y_s_12) != 0:
        y_s_12_sample = random.sample(y_s_12, random.randint(
            0,  max(1, min(len(y_s_12) - 1, min((s_1[0].size(0) // 2) - 1, (s_2[0].size(0) // 2) - 1)))))

    if (len(s_1_nx.edges)) > 2:
        s_1_nx.remove_edges_from(y_s_12_sample)
        s_1_nx.remove_edges_from(list(map(swap, y_s_12_sample)))
    if (len(s_2_nx.edges)) > 2:
        s_2_nx.remove_edges_from(y_s_12_sample)
        s_2_nx.remove_edges_from(list(map(swap, y_s_12_sample)))

    y_neg = [(random.choice(list(s_1_nx.nodes) + list(s_2_nx.nodes)), random.randint(
        len(G_noisy.nodes) + 1, len(G_noisy.nodes) * 2) - 1) for _ in range(len(y_s_12_sample))]

    y_neg_s_1 = list(filter(lambda x: x[0] in s_1_nx.nodes, y_neg))
    y_neg_s_2 = list(filter(lambda x: x[0] in s_2_nx.nodes, y_neg))
    s_1_nx.add_edges_from(y_neg_s_1)
    s_1_nx.add_edges_from(list(map(swap, y_neg_s_1)))
    s_2_nx.add_edges_from(y_neg_s_2)
    s_2_nx.add_edges_from(list(map(swap, y_neg_s_2)))

    return torch.LongTensor(list(s_1_nx.edges)).t().contiguous(), torch.LongTensor(list(s_2_nx.edges)).t().contiguous(), y_s_12, y_s_12_sample, y_neg

def swap(x):
    return (x[1], x[0])


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
    ]

    operations = [
        migrations.CreateModel(
            name='WorkUnit',
            fields=[
                ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
                ('user', models.CharField(max_length=50)),
                ('project', models.CharField(max_length=50)),
                ('name', models.CharField(max_length=50)),
                ('start', models.DateTimeField(verbose_name=b'start time')),
                ('end', models.DateTimeField(verbose_name=b'end time')),
            ],
            options={
            },
            bases=(models.Model,),
        ),
        migrations.AlterUniqueTogether(
            name='workunit',
            unique_together=set([('user', 'start', 'end')]),
        ),
    ]


import json
import subprocess

from flask_restful import Resource, reqparse

class Info(Resource):

    def post(self):
        parser = reqparse.RequestParser()
        parser.add_argument('url', type(str))
        data = parser.parse_args()

        url = data['url']

        info = subprocess.check_output(["you-get", url, "--json"])

        return {
                   'info': json.loads(info)
               }, 200


import json,requests
import sys

from flask import Flask, request, render_template
sys.path.append(sys.argv[0] + '../')
from flask_run import WXBizDataCrypt

app = Flask(__name__)

@app.route('/login', mothod=["GET",'POST'])
def login():
    if request.method == 'GET':
        return render_template('login/login.html') # 模板名，关键字传参
    elif request.method == 'POST':
        pass

@app.route('/wxlogin', method=['POST'])
def wxlogin():
    '''微信登录'''
    data = json.loads(request.get_data().decode('utf-8')) # 将前端Json数据转为字典
    appID = 'appID' # 开发者关于微信小程序的appID
    appSecret = 'appSecret' # 开发者关于微信小程序的appSecret
    code = data['platCode'] # 前端POST过来的微信临时登录凭证code
    encryptedData = data['platUserInfoMap']['encryptedData']
    iv = data['platUserInfoMap']['iv']
    req_params = {
        'appid': appID,
        'secret': appSecret,
        'js_code': code,
        'grant_type': 'authorization_code'
    }
    wx_login_api = 'https://api.weixin.qq.com/sns/jscode2session'
    response_data = requests.get(wx_login_api, params=req_params) # 向API发起GET请求
    resData = response_data.json()
    openid = resData ['openid'] # 得到用户关于当前小程序的OpenID
    session_key = resData ['session_key'] # 得到用户关于当前小程序的会话密钥session_key

    pc = WXBizDataCrypt(appID, session_key) #对用户信息进行解密
    userinfo = pc.decrypt(encryptedData, iv) #获得用户信息
    print(userinfo)

    '''下面部分是通过判断数据库中用户是否存在来确定添加或返回自定义登录态（若用户不存在则添加；若用户存在，返回用户信息）'''

    return json.dumps({"code": 200, "msg": "登录成功", "userinfo": userinfo}, indent=4, sort_keys=True, default=str, ensure_ascii=False)

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


# В Python 3 три числовых типа, а в Python 2 было 4.
# https://docs.python.org/3/library/stdtypes.html#numeric-types-int-float-complex
# https://docs.python.org/2/library/stdtypes.html#numeric-types-int-float-long-complex

import inspect

numeric_types = [int, float, complex]

def get_score():
    score = {}

    for cls in numeric_types:
        cls_and_base_classes = inspect.getmro(cls)
        base_classes = cls_and_base_classes[1:]
        score[cls] = len(base_classes)

    return score

def announce_result(score):

    for item in score.items():
        print("{}\n".format(item))

def find_base_classes(numeric_types):
    score = get_score()
    announce_result(score)

find_base_classes(numeric_types)


# -*- coding: utf-8 -*-
import os, sys, cgi, time, types, exif

PATH     = u"/var/www/public/fotos"
URL      = u"https://example.com/fotos"
TEMPLATE = open(os.path.join(os.path.dirname(__file__), "template.html")).read().decode("utf8")
NPERPAGE = 10

print >> sys.stderr, "booted"

listing, ts = None, 0

def application(env, start_resp):
    global listing, ts

    def ret_badrequest():
        text = "400 Bad Request"
        start_resp("400 Bad Request",
                [("Content-Type", "text/plain"),
                 ("Content-Length", str(len(text)))])
        return [text]

    def ret_response(ctype, data):
        ctype = str(ctype)
        if not type(data) == types.StringType:
            data = data.encode("utf8")
        start_resp("200 OK", [
            ("Cache-Control", "max-age=0, no-store"),
            ("Content-Type", ctype),
            ("Content-Length", str(len(data)))])
        return [data]

    cts = os.path.getmtime(PATH) #int(time.time())
    #if cts - ts > 1200:
    if not cts == ts:
        ts = cts
        print >> sys.stderr, "reload listing"
        listing = []
        for root, dirs, files in os.walk(PATH):
            for f in files:
                if not f.startswith(".") and os.path.splitext(f)[1].lower() in [".jpg", ".jpeg"]:
                    path = os.path.join(root, f)
                    created = exif.parse(path).get("DateTimeOriginal")
                    url = u"%s%s" % (URL, path[len(PATH):])
                    listing.append((created, url))
        listing = sorted(listing)

    env_script = env["SCRIPT_URI"]

    params = env["QUERY_STRING"]
    params = cgi.parse_qs(params)
    params = dict([(key, params[key][0].strip()) for key in params])
    env_offs  = params.get("offs",  len(listing) - 1)
    env_count = params.get("count", NPERPAGE)
    del params
    try:
        env_offs  = int(env_offs)
        env_count = int(env_count)
    except ValueError:
        return ret_badrequest()
    if env_offs < 0:
        #env_offs = len(listing) - 1
        return ret_badrequest()
    if env_count <= 0:
        #env_count = NPERPAGE
        return ret_badrequest()

    count = env_count
    if env_offs - count + 1 < 0:
        count = env_offs + 1

    page = listing[env_offs - count + 1: env_offs + 1]
    page.reverse()
    page = [u'
\n
\n%s\n
' % (url, url, created) for created, url in page]
    page = "\n".join(page)

    linkprev, linknext = u"prev", u"next"

    if env_offs - count >= 0:
        linkprev = u'prev' % (
            env_script, env_offs - env_count, env_count)

    if env_offs < len(listing) - 1:
        linknext = u'next' % (
            env_script, env_offs + env_count, env_count)

    navi = u'\n%s\n%s\n' % (
        linkprev, linknext)

    content = u'
\n%s
\n%s
\n%s
' % (navi, page, navi)
    content = TEMPLATE % content

    return ret_response("text/html", content)


from __future__ import unicode_literals
import sys
import os
import json
import collections

from pip import cmdoptions

from pip.basecommand import Command
try:
    from pip.log import logger
except ImportError:
    from pip import logger  # 6.0
from pip.index import PackageFinder
from pip.req import RequirementSet, InstallRequirement, parse_requirements
from pip.locations import build_prefix, src_prefix

from .dependency import trace_dependencies
from .version import __version__

def pretty_project_name(req):
    """Get project name in a pretty form:

    name-version

    """
    try:
        print(req.name, req.installed_version)
    except Exception:
        print("dep %s has a problem sire." % req.name)
        return req.name
    return '%s-%s' % (req.name, req.installed_version)

class DependencyChecker(Command):
    bundle = False
    name = 'dependency'

    def __init__(self, *args, **kw):
        super(DependencyChecker, self).__init__(*args, **kw)

        # fix prog name to be gluttony instead of pip dependancy
        self.parser.prog = 'gluttony'

        self.cmd_opts.add_option(cmdoptions.requirements.make())
        self.cmd_opts.add_option(cmdoptions.build_dir.make())
        self.cmd_opts.add_option(cmdoptions.download_cache.make())

        # cmdoptions.editable exist in pip's git
        self.parser.add_option(
            '-e', '--editable',
            dest='editables',
            action='append',
            default=[],
            metavar='VCS+REPOS_URL[@REV]#egg=PACKAGE',
            help='Install a package directly from a checkout. Source will be checked '
            'out into src/PACKAGE (lower-case) and installed in-place (using '
            'setup.py develop). You can run this on an existing directory/checkout (like '
            'pip install -e src/mycheckout). This option may be provided multiple times. '
            'Possible values for VCS are: svn, git, hg and bzr.')

        self.parser.add_option(
            '-d', '--download', '--download-dir', '--download-directory',
            dest='download_dir',
            metavar='DIR',
            default=None,
            help='Download packages into DIR instead of installing them')
        self.parser.add_option(
            '--src', '--source', '--source-dir', '--source-directory',
            dest='src_dir',
            metavar='DIR',
            default=None,
            help='Check out --editable packages into DIR (default %s)' % src_prefix)
        self.parser.add_option(
            '-U', '--upgrade',
            dest='upgrade',
            action='store_true',
            help='Upgrade all packages to the newest available version')
        self.parser.add_option(
            '-I', '--ignore-installed',
            dest='ignore_installed',
            action='store_true',
            help='Ignore the installed packages (reinstalling instead)')

        # options for output
        self.parser.add_option(
            '--dump',
            dest='dump',
            metavar='FILE',
            help='dump dependancy by level')
        self.parser.add_option(
            '-j', '--json',
            dest='json_file',
            metavar='FILE',
            help='JSON filename for result output')
        self.parser.add_option(
            '--pydot',
            dest='py_dot',
            metavar='FILE',
            help='Output dot file with pydot')
        self.parser.add_option(
            '--pygraphviz',
            dest='py_graphviz',
            metavar='FILE',
            help='Output dot file with PyGraphviz')
        self.parser.add_option(
            '--display', '--display-graph',
            dest='display_graph',
            action='store_true',
            help='Display graph with Networkx and matplotlib')
        self.parser.add_option(
            '-R', '--reverse',
            dest='reverse',
            action='store_true',
            help='Reverse the direction of edge')

        index_opts = cmdoptions.make_option_group(
            cmdoptions.index_group,
            self.parser,
        )

        self.parser.insert_option_group(0, index_opts)

    def _build_package_finder(self, options, index_urls, session):
        """
        Create a package finder appropriate to this install command.
        This method is meant to be overridden by subclasses, not
        called directly.
        """
        return PackageFinder(
            use_wheel=False,
            find_links=options.find_links,
            index_urls=index_urls,
            allow_external=options.allow_external,
            allow_unverified=options.allow_unverified,
            allow_all_external=options.allow_all_external,
            session=session,
        )

    def run(self, options, args):
        if not options.build_dir:
            options.build_dir = build_prefix
        if not options.src_dir:
            options.src_dir = src_prefix
        if options.download_dir:
            options.no_install = True
            options.ignore_installed = True
        else:
            options.build_dir = os.path.abspath(options.build_dir)
            options.src_dir = os.path.abspath(options.src_dir)
        session = self._build_session(options)
        index_urls = [options.index_url] + options.extra_index_urls
        if options.no_index:
            logger.notify('Ignoring indexes: %s' % ','.join(index_urls))
            index_urls = []
        finder = self._build_package_finder(options, index_urls, session)
        requirement_set = RequirementSet(
            build_dir=options.build_dir,
            src_dir=options.src_dir,
            download_dir=options.download_dir,
            download_cache=options.download_cache,
            upgrade=options.upgrade,
            ignore_installed=options.ignore_installed,
            ignore_dependencies=False,
            session=session,
        )

        for name in args:
            requirement_set.add_requirement(
                InstallRequirement.from_line(name, None))
        for name in options.editables:
            requirement_set.add_requirement(
                InstallRequirement.from_editable(name, default_vcs=options.default_vcs))
        for filename in options.requirements:
            for req in parse_requirements(filename, finder=finder, options=options):
                requirement_set.add_requirement(req)

        requirement_set.prepare_files(
            finder,
            force_root_egg_info=self.bundle,
            bundle=self.bundle,
        )

        return requirement_set

    def _output_json(self, json_file, dependencies):
        packages = set()
        json_deps = []
        for src, dest in dependencies:
            packages.add(src)
            packages.add(dest)
            json_deps.append([
                pretty_project_name(src),
                pretty_project_name(dest),
            ])

        json_packages = []
        for package in packages:
            json_packages.append(dict(
                name=package.name,
                installed_version=package.installed_version,
            ))

        with open(json_file, 'wt') as jfile:
            json.dump(dict(
                packages=json_packages,
                dependencies=json_deps,
            ), jfile, sort_keys=True, indent=4, separators=(',', ': '))

    def check_conflicts(self, dependencies):
        dependancies_flattened = collections.defaultdict(set)
        for dep1, dep2 in dependencies:
            try:
                if dep1.installed_version is not None:
                    dependancies_flattened[dep1.name].add(dep1.installed_version)
            except Exception:
                print("%s has an unknown version" % dep1.name)

            try:
                if dep2.installed_version is not None:
                    dependancies_flattened[dep2.name].add(dep2.installed_version)
            except Exception:
                print("%s has an unknown version" % dep2.name)

        for dependency_name, dependency_versions in dependancies_flattened.items():
            if dependency_versions and len(dependency_versions) > 1:
                print("Warning: This project requires %s in multiple versions:" % dependency_name, ",".join(dependency_versions))

    def output(self, options, args, dependencies):
        """Output result

        """

        if options.reverse:
            dependencies = map(lambda x: x[::-1], dependencies)

        if options.json_file:
            self._output_json(options.json_file, dependencies)
            logger.notify("Dependencies relationships result is in %s now",
                          options.json_file)

        self.check_conflicts(dependencies)

        if options.display_graph or options.py_dot or options.py_graphviz or options.dump:
            import networkx as nx

            # extract name and version
            def convert(pair):
                return (
                    pretty_project_name(pair[0]),
                    pretty_project_name(pair[1]),
                )
            plain_dependencies = map(convert, dependencies)
            dg = nx.DiGraph()
            dg.add_edges_from(plain_dependencies)

            if options.dump:
                dependancies_ordered = []
                for n, nbrs in dg.adjacency_iter():
                    for nbr, eattr in nbrs.items():
                        dependancies_ordered.append(nbr)

                dependancies_ordered = set(dependancies_ordered)
                with open(options.dump, mode='wt') as myfile:
                    myfile.write('\n'.join(dependancies_ordered))

            if options.py_dot:
                logger.notify("Writing dot to %s with Pydot ...",
                              options.py_dot)
                from networkx.drawing.nx_pydot import write_dot
                write_dot(dg, options.py_dot)
            if options.py_graphviz:
                logger.notify("Writing dot to %s with PyGraphviz ...",
                              options.py_graphviz)
                from networkx.drawing.nx_agraph import write_dot
                write_dot(dg, options.py_graphviz)
            if options.display_graph:
                import matplotlib.pyplot as plt
                logger.notify("Drawing graph ...")

                if not plain_dependencies:
                    logger.notify("There is no dependency to draw.")
                else:
                    pydot_graph = nx.drawing.to_pydot(dg)
                    pydot_graph.write_png("dependency.png")

    def main(self, args):
        options, args = self.parser.parse_args(args)
        if not args:
            self.parser.print_help()
            return

        level = 1  # Notify
        logger.level_for_integer(level)
        logger.consumers.extend([(level, sys.stdout)])
        # get all files
        requirement_set = self.run(options, args)
        # trace dependencies
        logger.notify("Tracing dependencies ...")
        dependencies = []
        values = None
        if hasattr(requirement_set.requirements, 'itervalues'):
            values = list(requirement_set.requirements.itervalues())
        elif hasattr(requirement_set.requirements, 'values'):
            values = list(requirement_set.requirements.values())
        for req in values:
            trace_dependencies(req, requirement_set, dependencies)
        # output the result
        logger.notify("Output result ...")
        self.output(options, args, dependencies)
        requirement_set.cleanup_files()

def main():
    command = DependencyChecker()
    command.main(sys.argv[1:])

if __name__ == '__main__':
    main()


import pygame as pg
import random
from utils import LIST_DICTIONARY, COLOR_LINES, ORANGE, PURPLE, FONT

class GuessWord:
    def __init__(self):
        self.guess_word = ""
        self.x = 150
        self.w = 190
        self.new_x = 40
        self.new_y = 50
        self.space = 10
        self.y, self.h, = 130, 130
        self.color = COLOR_LINES
        self.rect = []
        self.txt_surface = FONT.render('', True, self.color)
        self.txt_surf_win = FONT.render('You won!', True, ORANGE)
        self.rect_win = pg.Rect(self.x + 150, self.y + 60, self.w - 50, self.h - 80)

    def random_word(self):
        rand_num = random.randint(0, len(LIST_DICTIONARY) - 1)
        self.guess_word = LIST_DICTIONARY[rand_num]
        return self.guess_word

    def check_in(self, letter, print_word):
        for i in range(len(self.guess_word)):
            if self.guess_word[i] == letter:
                print_word[i] = letter
        return print_word

    def draw_lines(self, screen, print_word):
        x = self.x
        w = self.w
        new_x = self.new_x
        space = self.space
        y, h = self.y, self.h
        txt = [" " if not i else i for i in print_word]
        for i in range(len(self.guess_word)):
            self.rect.append(pg.Rect(x, y-self.new_y, new_x, new_x))
            self.txt_surface = FONT.render(txt[i], True, self.color)
            screen.blit(self.txt_surface, (self.rect[i].x + 5, self.rect[i].y-5))
            pg.draw.line(
                screen, COLOR_LINES, (x, y), (w, h), 5)
            pg.draw.rect(screen, PURPLE, self.rect[i], 2)
            x = x + new_x + space
            w = w + new_x + space
        if all([' ' != i for i in txt]):
            screen.blit(self.txt_surf_win, (self.rect_win.x + 5, self.rect_win.y - 5))
            pg.draw.rect(screen, PURPLE, self.rect_win, 2)


from xai.brain.wordbase.verbs._exasperate import _EXASPERATE

#calss header
class _EXASPERATES(_EXASPERATE, ):
	def __init__(self,):
		_EXASPERATE.__init__(self)
		self.name = "EXASPERATES"
		self.specie = 'verbs'
		self.basic = "exasperate"
		self.jsondata = {}


import sys
import os
import time
import tempfile

import arcade
import pyglet

from arcade.color import RED

from ev3dev2simulator.state import WorldState
from ev3dev2simulator.visualisation.Sidebar import Sidebar
from ev3dev2simulator.config.config import get_simulation_settings, debug

def start():
    arcade.run()

def get_screens():
    display = pyglet.canvas.get_display()
    screens = display.get_screens()
    return screens

class Visualiser(arcade.Window):
    """
    Main simulator class.
    This class extends from arcade.Window and manages the updates and rendering of the simulator window.
    """

    def __init__(self, update_world_cb, world_state: WorldState, show_fullscreen: bool,
                 show_maximized: bool, use_second_screen_to_show_simulator: bool):

        self.pid_file = None
        self.pid = None
        self.check_for_unique_instance()
        self.update_callback = update_world_cb

        self.sidebar = None
        self.world_state = world_state

        self.current_screen_index = None
        self.set_screen_to_display_simulator_at_startup(use_second_screen_to_show_simulator)

        sim_settings = get_simulation_settings()

        self.scale = None
        self.screen_width = int(sim_settings['screen_settings']['screen_width'])
        self.screen_height = int(sim_settings['screen_settings']['screen_height'])
        self.side_bar_width = int(sim_settings['screen_settings']['side_bar_width'])

        from ev3dev2.version import __version__ as api_version
        from ev3dev2simulator.version import __version__ as sim_version
        screen_title = sim_settings['screen_settings'][
                           'screen_title'] + f'          version: {sim_version}      ev3dev2 api: {api_version}'

        self.frames_per_second = sim_settings['exec_settings']['frames_per_second']
        self.falling_msg = sim_settings['screen_settings']['falling_message']
        self.restart_msg = sim_settings['screen_settings']['restart_message']

        self.change_scale(self.screen_width, self.screen_height)
        if debug:
            print('starting simulation with scaling', self.scale)
            print('arcade version: ', arcade.version.VERSION)

        super(Visualiser, self).__init__(self.screen_width, self.screen_height, screen_title, update_rate=1 / 30,
                                         resizable=True)

        icon1 = pyglet.image.load(r'assets/images/body.png')
        self.set_icon(icon1)
        arcade.set_background_color(eval(sim_settings['screen_settings']['background_color']))

        self.msg_counter = 0

        self.setup_sidebar()
        self.world_state.setup_pymunk_shapes(self.scale)
        self.world_state.setup_visuals(self.scale)

        if show_fullscreen:
            self.toggleFullScreenOnCurrentScreen()

        if show_maximized:
            self.maximize()

        self.check_for_activation()

    @property
    def msg_x(self):
        return (self.screen_width - self.side_bar_width) / 2

    def change_scale(self, new_screen_width, new_screen_height):
        x_scale = (new_screen_width - self.side_bar_width) / self.world_state.board_width
        y_scale = new_screen_height / self.world_state.board_height
        if x_scale <= y_scale:
            scale = x_scale
        else:
            scale = y_scale
        self.screen_height = int(scale * self.world_state.board_height)
        self.screen_width = self.side_bar_width + int(scale * self.world_state.board_width)
        self.scale = scale

    def setup_sidebar(self):
        self.sidebar = Sidebar(self.screen_width - self.side_bar_width, self.screen_height - 70,
                               self.side_bar_width, self.screen_height)
        for robot in self.world_state.get_robots():
            self.sidebar.init_robot(robot.name, robot.sensors, robot.bricks, robot.side_bar_sprites)

    def set_screen_to_display_simulator_at_startup(self, use_second_screen_to_show_simulator):
        """ Set screen to use to display the simulator at startup. For windows this works only in fullscreen mode.

           By default set current screen to show simulator, but if use_second_screen_to_show_simulator==True
           then change screen to other screen.

           On MacOS this works for both fullscreen and none-fullscreen mode.
           On Windows this only works for fullscreen mode. For none-fullscreen always the first screen is used.
        """

        # get current_screen_index
        current_screen_index = 1 if use_second_screen_to_show_simulator else 0
        screens = get_screens()
        # for screen in screens: print(screen)
        num_screens = len(screens)
        if num_screens == 1:
            current_screen_index = 0
        self.current_screen_index = current_screen_index

        # change screen to show simulator
        # HACK override default screen function to change it.
        # Note: arcade window class doesn't has the screen parameter which pyglet has, so by overriding
        #       the get_default_screen method we can still change the screen parameter.
        def get_default_screen():
            """Get the default screen as specified by the user's operating system preferences."""
            return screens[self.current_screen_index]

        display = pyglet.canvas.get_display()
        display.get_default_screen = get_default_screen

        # note:
        #  for macOS  get_default_screen() is also used to as the screen to draw the window initially
        #  for windows the current screen is used to to draw the window initially,
        #              however the value set by get_default_screen() is used as the screen
        #              where to display the window fullscreen!

        # note:  BUG: dragging window to other screen in macOS messes up view size
        #   for macOS the screen of the mac can have higher pixel ratio (self.get_pixel_ratio())
        #   then the second screen connected. If you drag the window from the mac screen to the
        #   second screen then the windows may be the same size, but the simulator is drawn in only
        #   in the lower left quart of the window.
        #      => we need somehow make drawing of the simulator larger

        # how to view simulator window on second screen when dragging not working?
        #    SOLUTION: just when starting up the simulator set it to open on the second screen,
        #              then it goes well, and you can also open it fullscreen on the second screen
        # see also : https://stackoverflow.com/questions/49302201/highdpi-retina-windows-in-pyglet

    def check_for_unique_instance(self):
        """ Detect whether an other instance is already running. If so then trigger the
            activation for the other instance and terminate this instance.
        """

        tmpdir = tempfile.gettempdir()
        self.pid_file = os.path.join(tmpdir, "ev3dev2simulator.pid")

        self.pid = str(os.getpid())
        f = open(self.pid_file, 'w')
        f.write(self.pid)
        f.flush()
        f.close()

        time.sleep(2)

        file = open(self.pid_file, 'r')
        line = file.readline()
        file.close()
        read_pid = line.rstrip()
        if read_pid != self.pid:
            # other process already running
            sys.exit()

    def check_for_activation(self):
        """ checks each interval whether the simulator windows must be activated (bring to front)

            note: activation can happen when one tries to start another instance of the simulator,
                  and that instance detects an instance is already running. It then triggers the
                  activation for the other instance and terminates itself.
        """
        from pyglet import clock

        def callback(_):
            file = open(self.pid_file, 'r')
            line = file.readline()
            file.close()
            read_pid = line.rstrip()
            if read_pid != self.pid:

                # other simulator tries to start running
                # write pid to pid_file to notify this simulator is already running
                f = open(self.pid_file, 'w')
                f.write(self.pid)
                f.close()

                import platform
                if platform.system().lower().startswith('win'):
                    self.windows_activate()
                else:
                    self.activate()

        clock.schedule_interval(callback, 1)

    def windows_activate(self):
        # noinspection PyProtectedMember
        from pyglet.libs.win32 import _user32
        from pyglet.libs.win32.constants import SW_SHOWMINIMIZED, SW_SHOWNORMAL
        _user32.ShowWindow(self._hwnd, SW_SHOWMINIMIZED)
        _user32.ShowWindow(self._hwnd, SW_SHOWNORMAL)

    def on_close(self):
        sys.exit(0)

    def on_key_press(self, key, modifiers):
        """Called whenever a key is pressed. """

        # Quit the simulator
        if key == arcade.key.Q:
            self.on_close()

        # Toggle fullscreen between screens (only works at fullscreen mode)
        elif key == arcade.key.T:
            # User hits T. When at fullscreen, then switch screen used for fullscreen.
            if len(get_screens()) == 0:
                return
            if self.fullscreen:
                # to switch screen when in fullscreen we first have to back to normal window, and do fullscreen again
                self._set_full_screen(False)
                # Toggle between first and second screen (other screens are ignored)
                self.toggle_screen_used_for_fullscreen()
                self._set_full_screen(True)

        # Maximize window
        # note: is toggle on macOS, but not on windows
        elif key == arcade.key.M:
            self.maximize()

        # Toggle between Fullscreen and window
        #   keeps viewport coordinates the same   STRETCHED (FULLSCREEN)
        #   Instead of a one-to-one mapping to screen size, we use stretch/squash window to match the constants.
        #   src: http://arcade.academy/examples/full_screen_example.html
        elif key == arcade.key.F:
            self.update_current_screen()
            self._set_full_screen(not self.fullscreen)

    # toggle screen for fullscreen
    # BUG: doesn't work on macOS => see explanation in set_screen_to_display_simulator_at_startup() method
    def toggle_screen_used_for_fullscreen(self):

        # toggle only between screen 0 and 1 (other screens are ignored)
        self.current_screen_index = (self.current_screen_index + 1) % 2

        # override hidden screen parameter in window
        screens = get_screens()
        self._screen = screens[self.current_screen_index]

    def update_current_screen(self):
        """ using the windows position and size we determine on which screen it is currently displayed and make that
            current screen for displaying in fullscreen!!
        """

        screens = get_screens()
        if len(screens) == 1:
            return

        location = self.get_location()
        top_left_x = location[0]
        top_left_y = location[1]
        size = self.get_size()
        win_width = size[0]
        win_height = size[1]

        done = False
        locations = [location, (top_left_x + win_width, top_left_y), (top_left_x, top_left_y + win_height),
                     (top_left_x + win_width, top_left_y + win_height)]
        for location in locations:
            if done:
                break
            loc_x = location[0]
            loc_y = location[1]
            num = 0
            for screen in screens:
                within_screen_width = screen.x <= loc_x < (screen.x + screen.width)
                within_screen_height = screen.y <= (loc_y < (screen.y + screen.height))
                if within_screen_width and within_screen_height:
                    self.current_screen_index = num
                    done = True
                    break
                num += 1

        # override hidden screen parameter in window
        self._screen = screens[self.current_screen_index]

    def _set_full_screen(self, is_full_screen: bool = True):
        self.set_fullscreen(is_full_screen)

        # Instead of a one-to-one mapping, stretch/squash window to match the
        # constants. This does NOT respect aspect ratio. You'd need to
        # do a bit of math for that.
        self.set_viewport(0, self.screen_width, 0, self.screen_height)

        # HACK for macOS: without this hack fullscreen on the second screen is shifted downwards in the y direction
        #     By also calling the maximize function te position the fullscreen in second screen is corrected!)
        import platform
        if platform.system().lower() == "darwin":
            self.maximize()

    def on_resize(self, width, height):
        """ This method is automatically called when the window is resized. """

        # Call the parent. Failing to do this will mess up the coordinates, and default to 0,0 at the center and the
        # edges being -1 to 1.
        super().on_resize(self.screen_width, self.screen_height)

    def on_draw(self):
        """
        Render the simulation.
        """

        arcade.start_render()

        for obstacleList in self.world_state.static_obstacles:
            for shape in obstacleList.get_shapes():
                if shape.line_width == 1:
                    shape.draw()
                else:
                    print(shape)
        self.world_state.sprite_list.draw()

        for robot in self.world_state.get_robots():
            robot.get_sprites().draw()

            if debug:
                for sprite in robot.get_sprites():
                    sprite.draw_hit_box(color=RED, line_thickness=5)
                    if robot.debug_shapes is not None:
                        for shape in robot.debug_shapes:
                            shape.draw()
                    robot.debug_shapes.clear()

            if robot.is_stuck and self.msg_counter <= 0:
                self.msg_counter = self.frames_per_second * 3

        for robot in self.world_state.get_robots():
            self.sidebar.add_robot_info(robot.name, robot.values, robot.sounds)

        self.sidebar.draw()
        if self.msg_counter > 0:
            self.msg_counter -= 1

            arcade.draw_text(self.falling_msg, self.msg_x, self.screen_height - 100, arcade.color.RADICAL_RED,
                             14,
                             anchor_x="center")
            arcade.draw_text(self.restart_msg, self.msg_x, self.screen_height - 130, arcade.color.RADICAL_RED,
                             14,
                             anchor_x="center")

    def update(self, delta_time):
        """
        All the logic to move the robot. Collision detection is also performed.
        Callback to WorldSimulator.update is called
        """
        self.update_callback()

    def on_mouse_press(self, x: float, y: float, button: int, modifiers: int):
        self.world_state.set_object_at_position_as_selected((x, y))

    def on_mouse_release(self, x: float, y: float, button: int,
                         modifiers: int):
        self.world_state.unselect_object()

    def on_mouse_drag(self, x: float, y: float, dx: float, dy: float, buttons: int, modifiers: int):
        if buttons == arcade.MOUSE_BUTTON_LEFT:
            self.world_state.move_selected_object(dx, dy)
        if buttons == arcade.MOUSE_BUTTON_RIGHT:
            self.world_state.rotate_selected_object(dy)


import math

def distance(p1, p2): #return distance between two cartesian coordinates. Usage: distance((1,1),(2,2))
    a = abs(p1[0] - p2[0]) #horizontal displacement
    b = abs(p1[1] - p2[1]) #vertical displacement
    return math.sqrt(a*a + b*b)

def weightedGraph(cityArray):
    retMatrix = [[0.0 for _ in range(len(cityArray))] for _ in range(len(cityArray))] #build empty 2D matrix
    for i in range(0, len(cityArray)):
      for j in range(0, len(cityArray)):
        retMatrix[i][j] = distance(cityArray[i], cityArray[j]) #populate the 2D matrix with euclidean distances
    return retMatrix


import copy
import os, time
import augment
import pretrained_model

import albumentations as A
from albumentations.pytorch import ToTensorV2
import cv2
import torch
import torch.backends.cudnn as cudnn
import torch.nn as nn
import torch.optim as optim
from torch.optim import lr_scheduler
from torch.utils.data import Dataset, DataLoader
import torchvision.models as models
from tqdm import tqdm
from time import sleep

cudnn.benchmark = True

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

def train_model(model, criterion, optimizer, scheduler, num_epochs=25):
    since = time.time() # get the starting time of training

    best_loss = 100000.0
    tmax_factor = 1.5 # with warm restart, multiply the max factor by this amount
    tmax = 10 # after tmax iterations, the learning rate is reset

    for epoch in range(num_epochs):
        print('Epoch {}/{}'.format(epoch, num_epochs - 1))
        print('-' * 10)

        # Each epoch has a training and validation phase
        for phase in ['train', 'val']:
            if phase == 'train':
                model.train()  # Set model to training mode
            else:
                model.eval()   # Set model to evaluate mode

            running_loss = 0.0
            iterations = len(dataloaders[phase])

            # Iterate over data.
            pbar = tqdm(total=iterations,desc=phase,ncols=70)
            for inputs, labels in dataloaders[phase]:
                inputs = inputs.to(device)
                output_tensor = labels.to(device)

                # zero the parameter gradients
                optimizer.zero_grad()

                # Run the forward pass and track history if only in training
                with torch.set_grad_enabled(phase == 'train'):
                    outputs = model(inputs)
                    preds = outputs
                    loss = criterion(outputs, output_tensor)

                    # backward + optimize only if in training phase
                    if phase == 'train':
                        loss.backward()
                        optimizer.step()

                # statistics
                running_loss += loss.item() * inputs.size(0)
                pbar.update(1)
                sleep(0.01) #delay to print stats
            pbar.close()

            if phase == 'train': #adjust the learning rate if training
                scheduler.step()

            # With the cosine annealing scheduler, the learning rate is
            # gradually reduced.  If the learning rate is very small, then
            # reset the scheduler to do a warm restart
            if optimizer.param_groups[0]['lr'] < .0000001:
                tmax = int(tmax * tmax_factor)
                scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=tmax, verbose=True)
                print ("New T_max: " + str(tmax))

            epoch_loss = running_loss / dataset_sizes[phase]

            print('{} Loss: {:.4f}'.format(phase, epoch_loss))

            # deep copy the model
            if phase == 'val' and epoch_loss < best_loss:
                best_loss = epoch_loss
                best_model_wts = copy.deepcopy(model.state_dict())
                print ("Saving new best model...")
                torch.save(model, 'test.pt')

        print()

    time_elapsed = time.time() - since
    print('Training complete in {:.0f}m {:.0f}s'.format(
        time_elapsed // 60, time_elapsed % 60))
    print('Best val Acc: {:4f}'.format(best_loss))

    # load best model weights
    model.load_state_dict(best_model_wts)
    return model

if __name__ == '__main__':
    train_fp, val_fp = augment.setup_dir()
    train_d, val_d = augment.create_datasets(train_fp, val_fp)

    image_datasets = {}
    image_datasets['train'] = train_d
    image_datasets['val'] = val_d

    dataloaders = {x: torch.utils.data.DataLoader(image_datasets[x], \
                   batch_size=32, shuffle=True, num_workers=12) for x in ['train', 'val']}

    dataset_sizes = {x: len(image_datasets[x]) for x in ['train', 'val']}
    print (dataset_sizes)

    #create the model
    m = pretrained_model.Pretrained_Model(shape=(360,640,3), num_outputs=80)
    model = m.build()

    if torch.cuda.is_available(): #send the model to the GPU if available
        model.cuda()

    #configure the training
    criterion = nn.L1Loss()
    optimizer = optim.AdamW(model.parameters(), lr=0.005)
    exp_lr_scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=10, verbose=True)

    #train the model
    #model = train_model(model, criterion, optimizer, exp_lr_scheduler, num_epochs=480)
    model = train_model(model, criterion, optimizer, exp_lr_scheduler, num_epochs=203)


import datetime
import redis

from django.db import models
from django.db.models import Q
from django.utils import timezone
from django.utils.translation import to_locale, get_language
from django.contrib.auth.models import User
from django.conf import settings
from django.contrib.gis.db.models import PointField
from django_extensions.db.models import (
    TitleSlugDescriptionModel, TimeStampedModel)
from django.utils.translation import gettext_lazy as _

def user_directory_path(instance, filename):
    # file will be uploaded to MEDIA_ROOT / user_<id>/<filename>
    return f'media/{instance.__class__.__name__}/{instance.owner.id}_{filename}'

class AdvertsManager(models.Manager):
    def search(self, query=None):
        qs = self.get_queryset()
        if query is not None:
            or_lookup = (Q(title__icontains=query) |
                         Q(description__icontains=query))
            qs = qs.filter(or_lookup).distinct()  # distinct() is often necessary with Q lookups
        return qs

class Advert(TitleSlugDescriptionModel, TimeStampedModel):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True, verbose_name=_('owner'))
    expires = models.DateTimeField(blank=True, null=True, help_text=_('Format mm.dd.yyyy'),
                                   verbose_name=_('expires'))

    LOCALES = (('en', 'en_US'),
               ('uk', 'uk_UA'),
               ('ru', 'ru_RU'),
               ('pl', 'pl_PL'),
               )

    local = models.CharField(
        max_length=2,
        choices=LOCALES,
        default='en'
    )

    objects = AdvertsManager()

    class Meta:
        ordering = ['modified']
        abstract = True

    def was_published_recently(self):
        return self.created >= timezone.now() - datetime.timedelta(days=1)

    def save(self, *args, **kwargs):
        if not self.local:
            lang = get_language()
            if lang:
                self.local = lang[:2]
        super(Advert, self).save(*args, **kwargs)

        r = redis.StrictRedis(host=settings.REDIS_HOST,
                              port=settings.REDIS_PORT,
                              db=settings.REDIS_DB)
        if (self.modified - self.created).seconds == 0:
            r.incr(f'Total:saved')
            r.incr(f'{self.__class__.__name__}:{self.id}:saved')
            data = self.created.timestamp()
            score = f'{self.__class__.__name__}:{self.created.timestamp()}'
            r.zadd("adverts", {score: data})

    def delete(self, using=None, keep_parents=False):
        r = redis.StrictRedis(host=settings.REDIS_HOST,
                              port=settings.REDIS_PORT,
                              db=settings.REDIS_DB)
        super(Advert, self).delete()
        r.decr(f'Total:saved')
        r.decr(f'{self.__class__.__name__}:{self.id}:saved')
        data = self.created.timestamp()
        score = f'{self.__class__.__name__}:{self.created.timestamp()}'
        r.zadd("adverts", {score: data})

    def __str__(self):
        return self.title

class Location(models.Model):
    city = models.CharField(max_length=50, default='Chicago', verbose_name=_('city'))
    address = models.CharField(max_length=100, blank=True, verbose_name=_('address'))
    point = PointField(blank=True, verbose_name=_('map'), null=True)

    # zipcode =

    @property
    def lat_lng(self):
        return list(getattr(self.point, 'coords', [])[::-1])

    class Meta:
        ordering = ['city']
        abstract = True

    def __unicode__(self):
        return self.city

    def __str__(self):
        return self.city