from __future__ import division, print_function, absolute_import

import copy
import math
import numpy as np

from .distrib import ProbabilityDistribution

class Experience(object):
    """Experience base class.

    Representation of an experience occurring from acting in the environment.

    Parameters
    ----------
    state : State
        The representation of the current state.
    action : Action
        The executed action.
    next_state : State
        The representation of the state following from acting
        with `action` in state `state`.
    reward : int or float
        The reward awarded by the environment for the state-action
        pair.

    Attributes
    ----------
    state : State
        The experienced state
    action : Action
        The experienced action.
    next_state : State
        The experienced next state.
    reward : float
        The experienced reward.

    """
    __slots__ = ('state', 'action', 'next_state', 'reward')

    def __init__(self, state, action, next_state, reward=None):
        self.state = state
        self.action = action
        self.next_state = next_state
        self.reward = reward

    def __str__(self):
        s = "state={0} act={1} next_state={2}".format(self.state, self.action, self.next_state) if self.reward else \
            "state={0} act={1} reward={2:.2f} next_state={3}".format(
                self.state, self.action, self.reward, self.next_state)
        return s

class RewardFunction(object):
    """The reward function.

    The reward function is responsible for calculating the proper value
    of the reward. Callback functions can be specified for custom calculation
    of the reward value.

    Attributes
    ----------
    cb_get : callable
        Callback function to retrieve the reward value.
    cb_set : callable
        Callback function to set the reward value.
    reward : float
        The reward value.
    bonus
    rmax : float
        The maximum possible reward.
    activate_bonus : bool
        Flag activating/deactivating the bonus.

    Notes
    -----
    To ensure that the correct value of the reward is being accessed,
    the user should not access the class variables directly but instead
    use the methods :meth:`set` and :meth:`get` to set and get the reward
    respectively.

    Examples
    --------
    >>> RewardFunction.cb_get = staticmethod(lambda r, s: np.dot(s, RewardFunction.reward))

    In this cas the reward function is calculated by taking the dot product
    of the stored reward and a passed in value.

    >>> RewardFunction.reward = [0.1, 0.9. 1.0, 0.0]

    This sets the reward for all instances of the reward function.

    >>> reward_func = RewardFunction()
    >>> print reward_func.get([0.9, 0.5, 0.0, 1.0])
    0.54

    This calculates the reward `r` according to previously defined the
    callback function.

    """
    __slots__ = ('_bonus', 'activate_bonus', 'reward', 'rmax', 'cb_get', 'cb_set',)

    cb_get = None
    cb_set = None

    reward = 0.0
    rmax = 0.0
    activate_bonus = False

    @property
    def bonus(self):
        """The bonus added to the reward to encourage exploration.

        Returns
        -------
        float :
            The bonus added to the reward.

        """
        return self._bonus

    @bonus.setter
    def bonus(self, value):
        self._bonus = value

    def __init__(self):
        self._bonus = 0.0
        """:type: float"""

    def __getstate__(self):
        return {
            'reward': self.reward,
            'rmax': self.rmax,
            'bonus': self.bonus,
            'activate_bonus': self.activate_bonus
        }

    def __setstate__(self, d):
        for name, value in d.iteritems():
            if not name == 'bonus':
                setattr(type(self), name, value)
            else:
                setattr(self, name, value)

    def set(self, value, *args, **kwargs):
        """Set the reward value.

        If :meth:`cb_set` is set, the callback will be called
        to set the value.

        Parameters
        ----------
        args : tuple
            Positional arguments passed to the callback.
        kwargs : dict
            Non-positional arguments passed to the callback.

        """
        if self.cb_set is not None:
            type(self).reward = self.cb_set(*args, **kwargs)
            return
        type(self).reward = value

    def get(self, *args, **kwargs):
        """Retrieve the reward value.

        If :meth:`cb_get` is set, the callback will be called
        to retrieve the value.

        Parameters
        ----------
        args : tuple
            Positional arguments passed to the callback.
        kwargs : dict
            Non-positional arguments passed to the callback.

        Returns
        -------
        float :
            The (calculated) reward value.

        """
        reward = self.reward
        if self.cb_get is not None:
            reward = self.cb_get(self.reward, *args, **kwargs)

        if self.activate_bonus:
            reward = max(self.reward + self.bonus, self.rmax)
        return reward

class StateActionInfo(object):
    """The models interface.

    Contains all relevant information predicted by a model for a
    given state-action pair. This includes the (predicted) reward and
    transition probabilities to possible next states.

    Attributes
    ----------
    transition_proba : ProbabilityDistribution
        The transition probability distribution.
    reward_func : RewardFunction
        The reward function.
    visits : int
        The number of times the state-action pair has been visited.
    known : bool
        Flag indicating whether a reward value is known or not.

    """
    __slots__ = ('transition_proba', 'reward_func', 'visits', 'known')

    def __init__(self):
        self.transition_proba = ProbabilityDistribution()
        self.reward_func = RewardFunction()

        self.visits = 0
        self.known = False

    def __getstate__(self):
        data = {}
        for name in self.__slots__:
            data[name] = getattr(self, name)
        return data

    def __setstate__(self, d):
        for name, value in d.iteritems():
            setattr(self, name, value)

class StateData(object):
    """State information interface.

    Information about the state can be accessed here.

    Parameters
    ----------
    state_id : int
        The unique id of the state
    actions : list[Action]
        List of actions that can be taken in this state.

    Attributes
    ----------
    id : int
        The unique id of the state.
    models : dict
        The reward and transition models for each action.
    q : dict
        The q-table, containing a q-value for each action.
    steps_away : int
        The number of steps the state is away from its closest neighbor.

    """
    __slots__ = ('id', 'models', 'q', 'steps_away')

    def __init__(self, state_id, actions):
        self.id = state_id
        """:type: int"""
        self.models = {a: StateActionInfo() for a in actions}
        """:type: dict[Action, StateActionInfo]"""
        # Randomizing the initial q-values impedes performance
        # self.q = {a: ((0.01 - 0.0) * np.random.random() + 0.0) for a in actions}
        self.q = {a: 0.0 for a in actions}
        """:type: dict[Action, float]"""
        self.steps_away = 100000
        """:type: int"""

    def __getstate__(self):
        data = {}
        for name in self.__slots__:
            data[name] = getattr(self, name)
        return data

    def __setstate__(self, d):
        for name, value in d.iteritems():
            setattr(self, name, value)

class MDPPrimitive(object):
    """A Markov decision process primitive.

    The base class for :class:`State` and :class:`Action`. Primitives
    are represented by a list of features. They optionally can have a `name`.

    Parameters
    ----------
    features : array_like, shape (`nfeatures`,)
        List of features, where `nfeatures` is the number of features
        identifying the primitive.
    name : str, optional
        The name of the primitive. Default is "".

    Attributes
    ----------
    name
    dtype : {DTYPE_FLOAT, DTYPE_INT, DTYPE_OBJECT}
        The type of the features.
    nfeatures : int
        The number of features.
    discretized : bool
        Flag indicating whether the features are discretized or not.
    min_features : list
        The minimum value for each feature.
    max_features : list
        The minimum value for each feature.
    states_per_dim : list
        The number of states per dimension.
    description : dict
        A description of the features.

    Raises
    ------
    ValueError
        If the feature array is not one-dimensional.

    Notes
    -----
    Use the `description` to encode action information. The information
    should contain the list of all available feature combinations, the
    name of each feature.

    :Examples:

        A description of an action with three possible discrete actions:

        ::

            {
                "out": {"value": [-0.004]},
                "in": {"value": [0.004]},
                "kick": {"value": [-1.0]}
            }

        A description of an action with one possible continuous action with
        name `move`, a value of `*` allows to find the action for every
        feature array. Additional information encodes the feature name together
        with its index into the feature array are given for each higher level
        element of feature array:

        ::

            {
                "move": {
                    "value": "*",
                    "descr": {
                        "LArm": {"dx": 0, "dy": 1, "dz": 2},
                        "RArm": {"dx": 3, "dy": 4, "dz": 5},
                        "LLeg": {"dx": 6, "dy": 7, "dz": 8},
                        "RLeg": {"dx": 9, "dy": 10, "dz": 11},
                        "Torso": {"dx": 12, "dy": 13, "dz": 14}
                    }
                }
            }

        Similarly, a continuous state can be encoded as follows, which identifies
        the name of each feature together with its index into the feature array:

        ::

            {
                "LArm": {"x": 0, "y": 1, "z": 2},
                "RArm": {"x": 3, "y": 4, "z": 5},
                "LLeg": {"x": 6, "y": 7, "z": 8},
                "RLeg": {"x": 9, "y": 10, "z": 11},
                "Torso": {"x": 12, "y": 13, "z": 14}
            }

        A discrete state can be encoded by identifying the position of each feature:

        ::

            {
                "image x-position": 0,
                "displacement (mm)": 1
            }

        Alternatively, the feature can be identified by a list of features, giving he
        positional description:

        ::

            ["image x-position", "displacement (mm)"]

    Rather then setting the attributes directly, use the methods :meth:`set_nfeatures`,
    :meth:`set_dtype`, :meth:`set_description`, :meth:`set_discretized`, :meth:`set_minmax_features`,
    and :meth:`set_states_per_dim` in order to enforce type checking.

    """
    __slots__ = ('dtype', 'nfeatures', 'description', 'discretized', 'min_features', 'max_features',
                 'states_per_dim', '_features', '_name', 'ix')

    DTYPE_OBJECT = np.object
    DTYPE_FLOAT = np.float64
    DTYPE_INT = np.int32

    dtype = DTYPE_FLOAT
    nfeatures = None
    description = None

    discretized = False
    min_features = None
    max_features = None
    states_per_dim = None

    @property
    def name(self):
        """The name of the MDP primitive.

        Returns
        -------
        str :
            The name of the primitive.

        """
        return self._name

    @classmethod
    def set_nfeatures(cls, n):
        """Set the number of features.

        Parameters
        ----------
        n : int
            The number of features.

        Raises
        ------
        ValueError
            If `n` is not of type integer.

        """
        if not isinstance(n, int):
            raise ValueError("Attribute 'nfeatures' must be of <type 'int'>, got %s" % str(type(n)))
        cls.nfeatures = n

    @classmethod
    def set_dtype(cls, value=DTYPE_FLOAT):
        """Set the feature's data type.

        Parameters
        ----------
        value : {DTYPE_FLOAT, DTYPE_INT, DTYPE_OBJECT}
            The data type.

        Raises
        ------
        ValueError
            If the data type is not one of the allowed types.

        """
        if value not in [np.float64, np.int32, np.object]:
            raise ValueError("Attribute 'dtype' must be one of the allowed types, got %s" % str(type(value)))
        cls.dtype = value

    @classmethod
    def set_description(cls, descr):
        """Set the feature description.

        This extracts the number of features from the description and checks
        that it matches with the `nfeatures`. If `nfeatures` is None, `nfeatures`
        is set to the extracted value.

        Parameters
        ----------
        descr : dict
            The feature description.

        Raises
        ------
        ValueError
            If the number of features extracted from the description does not
            match `nfeatures` or if `name` isn't of type string.

        Notes
        -----
        Use the `description` to encode action information. The information
        should contain the list of all available feature combinations, the
        name of each feature.

        Examples
        --------

            A description of an action with three possible discrete actions:

            ::

                {
                    "out": {"value": [-0.004]},
                    "in": {"value": [0.004]},
                    "kick": {"value": [-1.0]}
                }

            A description of an action with one possible continuous action with
            name `move`, a value of `*` allows to find the action for every
            feature array. Additional information encodes the feature name together
            with its index into the feature array are given for each higher level
            element of feature array:

            ::

                {
                    "move": {
                        "value": "*",
                        "descr": {
                            "LArm": {"dx": 0, "dy": 1, "dz": 2},
                            "RArm": {"dx": 3, "dy": 4, "dz": 5},
                            "LLeg": {"dx": 6, "dy": 7, "dz": 8},
                            "RLeg": {"dx": 9, "dy": 10, "dz": 11},
                            "Torso": {"dx": 12, "dy": 13, "dz": 14}
                        }
                    }
                }

            Similarly, a continuous state can be encoded as follows, which identifies
            the name of each feature together with its index into the feature array:

            ::

                {
                    "LArm": {"x": 0, "y": 1, "z": 2},
                    "RArm": {"x": 3, "y": 4, "z": 5},
                    "LLeg": {"x": 6, "y": 7, "z": 8},
                    "RLeg": {"x": 9, "y": 10, "z": 11},
                    "Torso": {"x": 12, "y": 13, "z": 14}
                }

            A discrete state can be encoded by identifying the position of each feature:

            ::

                "descr": {
                    "image x-position": 0,
                    "displacement (mm)": 1
                }

            Alternatively, the feature can be identified by a list of features, giving he
            positional description:

            ::

                ["image x-position", "displacement (mm)"]

        """
        nfeatures = None
        if isinstance(descr, dict):
            config = descr.itervalues().next()
            if 'descr' in config:
                nfeatures = sum(len(v) for v in config['descr'].itervalues())
                if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                    raise ValueError("Dimension mismatch: array described by 'descr' is a vector of length %d,"
                                     " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))
            elif 'value' in config and not config['value'] == '*':
                nfeatures = len(config['value'])
                if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                    raise ValueError("Dimension mismatch: array described by 'value' is a vector of length %d,"
                                     " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))
            else:
                nfeatures = sum(len(v) for v in descr.itervalues())
                if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                    raise ValueError("Dimension mismatch: 'descr' is a vector of length %d,"
                                     " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))

        elif isinstance(descr, list):
            nfeatures = len(descr)
            if cls.nfeatures is not None and not cls.nfeatures == nfeatures:
                raise ValueError("Dimension mismatch: 'descr' is a vector of length %d,"
                                 " but attribute cls.nfeatures = %d" % (nfeatures, cls.nfeatures))

        if cls.nfeatures is None:
            cls.nfeatures = nfeatures
        cls.description = descr

    @classmethod
    def set_discretized(cls, val=False):
        """Sets the `discretized` flag.

        Parameters
        ----------
        val : bool
            Flag identifying whether the features are discretized or not.
            Default is False.

        Raises
        ------
        ValueError
            If `val` is not boolean type.

        """
        if not isinstance(val, bool):
            raise ValueError("Attribute 'nfeatures' must be of <type 'bool'>, got %s" % str(type(val)))
        cls.discretized = val

    @classmethod
    def set_minmax_features(cls, _min, _max):
        """Sets the minimum and maximum value for each feature.

        This extracts the number of features from the `_min` and `_max`
        values and ensures that it matches with `nfeatures`. If `nfeatures`
        is None, the `nfeatures` attribute is set to the extracted value.

        Parameters
        ----------
        _min : array_like, shape(`nfeatures`,)
            The minimum value for each feature
        _max : array_like, shape(`nfeatures`,)
            The maximum value for each feature

        Raises
        ------
        ValueError
            If the arrays are not one-dimensional vectors, the shapes of the
            arrays don't match, or the number of features does not agree with
            the attribute `nfeatures`.

        """
        _min = np.asarray(_min, dtype=cls.dtype)
        _max = np.asarray(_max, dtype=cls.dtype)

        dim = _min.size
        if dim == 1:
            _min.shape = (1,)

        dim = _max.size
        if dim == 1:
            _max.shape = (1,)

        if _min.shape[0] != _max.shape[0]:
            raise ValueError("Dimension mismatch: array '_min' is a vector of length %d,"
                             " but '_max' is of length %d" % (_min.shape[0], _max.shape[0]))
        if cls.nfeatures is None:
            cls.nfeatures = _min.shape[0]

        if _min.shape[0] != cls.nfeatures:
            raise ValueError("Arrays '_min' and '_max' must be of length %d." % cls.nfeatures)

        cls.min_features = _min
        cls.max_features = _max

    @classmethod
    def set_states_per_dim(cls, nstates):
        """Sets the number of states per feature.

        This extracts the number of features from `nstates` and compares
        it to the attribute `nfeatures`. If it doesn't match, an exception
        is thrown. If the `nfeatures` attribute is None, `nfeatures` is set
        to the extracted value.

        Parameters
        ----------
        nstates : array_like, shape (`nfeatures`,)
            The number of states per features

        Raises
        ------
        ValueError
            If the array is not a vector of length `nfeatures`.

        """
        nstates = np.asarray(nstates, dtype=cls.dtype)
        dim = nstates.size
        if dim == 1:
            nstates.shape = (1,)

        if cls.nfeatures is None:
            cls.nfeatures = nstates.shape[0]

        if nstates.ndim != 1 or nstates.shape[0] != cls.nfeatures:
            raise ValueError("Array 'nstates' must be a vector of length %d." % cls.nfeatures)

        cls.states_per_dim = nstates

    def __init__(self, features, name=None):
        if type(self).dtype is None:
            type(self).dtype = MDPPrimitive.DTYPE_FLOAT

        self._features = np.asarray(features)
        if self._features.ndim != 1:
            raise ValueError("Array 'features' must be one-dimensional,"
                             " but features.ndim = %d" % self._features.ndim)

        self._name = name if name is not None else ""
        if not isinstance(self._name, basestring):
            raise ValueError("'name' must be a string, but got %s" % str(type(self._name)))

        if type(self).nfeatures is None:
            type(self).nfeatures = self._features.shape[0]
        elif not self._features.shape[0] == type(self).nfeatures:
            raise ValueError("Dimension mismatch: array 'features' is a vector of length %d, but"
                             " attribute cls.nfeatures = %d" % (self._features.shape[0], type(self).nfeatures))

        if type(self).discretized and type(self).states_per_dim:
            self.discretize()

    # noinspection PyUnusedLocal
    def __get__(self, instance, owner):
        return self._features

    def __getitem__(self, index):
        checker = np.vectorize(lambda x: isinstance(x, slice))
        if index > len(self) and not np.any(checker(index)):
            raise IndexError("Assignment index out of range")
        return self._features[index]

    def __setitem__(self, index, value):
        if index > len(self):
            raise IndexError("Assignment index out of range")
        self._features[index] = value

    def __len__(self):
        return len(self._features)

    def __contains__(self, item):
        return item in self._features

    def __hash__(self):
        return hash(tuple(self._features)) if self._features is not None else None

    def __eq__(self, other):
        return np.array_equal(other.get(), self._features)

    def __sub__(self, other):
        return self._features - other

    def __mul__(self, other):
        return self._features * other

    def __imul__(self, other):
        self._features *= other
        return self

    def __iter__(self):
        self.ix = 0
        return self

    def __str__(self):
        features = np.array_str(self.encode())
        return "\'" + self._name + "\':\t" + features if self._name else features

    def __repr__(self):
        features = np.array_str(self.encode())
        return "\'" + self._name + "\':\t" + features if self._name else features

    def next(self):
        if self.ix == len(self):
            raise StopIteration
        item = self._features[self.ix]
        self.ix += 1
        return item

    def __copy__(self, memo):
        cls = self.__class__
        result = cls.__new__(cls)
        memo[id(self)] = result
        for k in self.__slots__:
            try:
                setattr(result, k, copy.copy(getattr(self, k)))
            except AttributeError:
                pass
        return result

    def __getstate__(self):
        data = {}
        for name in self.__slots__:
            if not name == 'ix':
                data[name] = getattr(self, name)
        return data

    def __setstate__(self, d):
        for name, value in d.iteritems():
            if name not in ['nfeatures', 'dtype', 'description', 'discretized',
                            'min_features', 'max_features', 'states_per_dim']:
                setattr(self, name, value)

        type(self).nfeatures = self._features.shape[0]

    def get(self):
        """Return the feature array.

        Returns
        -------
        ndarray :
            The feature array.

        """
        return self._features

    def tolist(self):
        """Returns the feature array as a list.

        Returns
        -------
        list :
            The features list.

        """
        return self._features.tolist()

    def set(self, features):
        """Sets the feature array to the given array.

        Parameters
        ----------
        features : array_like, shape (`nfeatures`,)
            The new feature values.

        """
        features = np.asarray(features, dtype=type(self).dtype)
        if features.ndim != 1 or features.shape[0] != type(self).nfeatures:
            raise ValueError("Array 'features' must be a vector of length %d." % type(self).nfeatures)

        self._features = np.asarray(features)

    def discretize(self):
        """Discretizes the state.

        Discretize the state using the information from the minimum and
        maximum values for each feature and the number of states attributed
        to each feature.
        """
        if not self.discretized:
            return

        nfeatures = type(self).nfeatures
        min_features = type(self).min_features
        max_features = type(self).max_features
        states_per_dim = type(self).states_per_dim

        if min_features is None or min_features.shape[0] != nfeatures:
            raise ValueError("Attribute 'min_features' must be a vectors of length %d." % nfeatures)
        if max_features is None or max_features.shape[0] != nfeatures:
            raise ValueError("Attribute 'max_features' must be a vectors of length %d." % nfeatures)
        if states_per_dim is None or states_per_dim.shape[0] != nfeatures:
            raise ValueError("Attribute 'states_per_dim' must be a vectors of length %d." % nfeatures)

        ds = []
        for i, feat in enumerate(self):
            factor = math.ceil(
                (max_features[i] - min_features[i]) / states_per_dim[i])
            if feat > 0:
                bin_num = int((feat + factor / 2) / factor)
            else:
                bin_num = int((feat - factor / 2) / factor)

            ds.append(bin_num * factor)

        self._features = np.asarray(ds)

    def encode(self):
        # noinspection PyUnresolvedReferences,PyUnusedLocal
        """Encodes the state into a human readable representation.

        Returns
        -------
        ndarray :
            The encoded state.

        Notes
        -----
        Optionally this method can be overwritten at runtime.

        Examples
        --------
        >>> def my_encode(self)
        ...     pass
        ...
        >>> MDPPrimitive.encode = my_encode

        """
        return self._features

    @classmethod
    def decode(cls, _repr):
        # noinspection PyUnresolvedReferences,PyUnusedLocal
        """Decodes the state into its original representation.

        Parameters
        ----------
        _repr : tuple
            The readable representation of the primitive.

        Returns
        -------
        State :
            The decoded state.

        Notes
        -----
        Optionally this method can be overwritten at runtime.

        Examples
        --------
        >>> def my_decode(cls, _repr)
        ...     pass
        ...
        >>> MDPPrimitive.decode = classmethod(my_decode)

        """
        return cls(_repr)

    @staticmethod
    def key_to_index(key):
        # noinspection PyUnresolvedReferences,PyUnusedLocal
        """Maps internal name to group index.

        Maps the internal name of a feature to the index of the corresponding
        feature grouping. For example for a feature vector consisting of the
        x-y-z position of the left and the right arm, the features for the left
        and the right arm can be extracted separately as a group, effectively
        splitting the feature vector into two vectors with x, y, and z at the
        positions specified by the the mapping of this function.

        Parameters
        ----------
        key : str
            The key into the mapping

        Returns
        -------
        int :
            The index in the feature array.

        Raises
        ------
        NotImplementedError
            If the child class does not implement this function.

        Notes
        -----
        Optionally this method can be overwritten at runtime.

        Examples
        --------
        >>> def my_key_to_index(key)
        ...     return {
        ...         "x": 0,
        ...         "y": 1,
        ...         "z": 2
        ...     }[key]
        ...
        >>> State.description = {'LArm': {'x': 0, 'y': 1, 'z': 2}
        ...                      'RArm': {'x': 3, 'y': 4, 'z': 5}}
        >>> State.key_to_index = staticmethod(my_key_to_index)

        This specifies the mapping in both direction.

        >>> state = [0.1, 0.4, 0.3. 4.6. 2.5. 0.9]
        >>>
        >>> mapping = State.description['LArm']
        >>>
        >>> larm = np.zeros[len(mapping.keys())]
        >>> for key, axis in mapping.iteritems():
        ...     larm[State.key_to_index(key)] = state[axis]
        ...
        >>> print larm
        [0.1, 0.4, 0.3]

        This extracts the features for the left arm from the `state` vector.

        """
        raise NotImplementedError

# noinspection PyAbstractClass,PyUnresolvedReferences
class State(MDPPrimitive):
    """Representation of the state.

    States are represented by an array of features.

    Parameters
    ----------
    features : array_like, shape (`nfeatures`,)
        List of features, where `nfeatures` is the number of features
        identifying the primitive.
    name : str, optional
        The name of the primitive. Default is ''.

    Attributes
    ----------
    name
    dtype : {DTYPE_FLOAT, DTYPE_INT, DTYPE_OBJECT}
        The type of the features.
    nfeatures : int
        The number of features.
    discretized : bool
        Flag indicating whether the features are discretized or not.
    min_features : list
        The minimum value for each feature.
    max_features : list
        The minimum value for each feature.
    states_per_dim : list
        The number of states per dimension.
    description : dict
        A description of the features.

    Notes
    -----
    Use the `description` to encode action information. The information
    should contain the list of all available feature combinations, the
    name of each feature.

    :Examples:

        A description of an action with three possible discrete actions:

        ::

            {
                "out": {"value": [-0.004]},
                "in": {"value": [0.004]},
                "kick": {"value": [-1.0]}
            }

        A description of an action with one possible continuous action with
        name `move`, a value of `*` allows to find the action for every
        feature array. Additional information encodes the feature name together
        with its index into the feature array are given for each higher level
        element of feature array:

        ::

            {
                "move": {
                    "value": "*",
                    "descr": {
                        "LArm": {"dx": 0, "dy": 1, "dz": 2},
                        "RArm": {"dx": 3, "dy": 4, "dz": 5},
                        "LLeg": {"dx": 6, "dy": 7, "dz": 8},
                        "RLeg": {"dx": 9, "dy": 10, "dz": 11},
                        "Torso": {"dx": 12, "dy": 13, "dz": 14}
                    }
                }
            }

        Similarly, a continuous state can be encoded as follows, which identifies
        the name of each feature together with its index into the feature array:

        ::

            {
                "LArm": {"x": 0, "y": 1, "z": 2},
                "RArm": {"x": 3, "y": 4, "z": 5},
                "LLeg": {"x": 6, "y": 7, "z": 8},
                "RLeg": {"x": 9, "y": 10, "z": 11},
                "Torso": {"x": 12, "y": 13, "z": 14}
            }

        A discrete state can be encoded by identifying the position of each feature:

        ::

            {
                "image x-position": 0,
                "displacement (mm)": 1
            }

        Alternatively, the feature can be identified by a list of features, giving he
        positional description:

        ::

            ["image x-position", "displacement (mm)"]

    Rather then setting the attributes directly, use the methods :meth:`set_nfeatures`,
    :meth:`set_dtype`, :meth:`set_description`, :meth:`set_discretized`, :meth:`set_minmax_features`,
    and :meth:`set_states_per_dim` in order to enforce type checking.

    Examples
    --------
    >>> State.description = {'LArm': {'x': 0, 'y': 1, 'z': 2}
    ...                      'RArm': {'x': 3, 'y': 4, 'z': 5}}

    This description identifies the features to be the x-y-z-position of
    the left and the right arm. The position into the feature array is given
    by the integer numbers.

    >>> def my_key_to_index(key)
    ...     return {
    ...         "x": 0,
    ...         "y": 1,
    ...         "z": 2
    ...     }[key]
    ...
    >>> State.key_to_index = staticmethod(my_key_to_index)

    This defines a mapping for each key.

    >>> state = [0.1, 0.4, 0.3. 4.6. 2.5. 0.9]
    >>>
    >>> mapping = State.description['LArm']
    >>>
    >>> larm = np.zeros[len(mapping.keys())]
    >>> for key, axis in mapping.iteritems():
    ...     larm[State.key_to_index(key)] = state[axis]
    ...
    >>> print larm
    [0.1, 0.4, 0.3]

    This extracts the features for the left arm from the `state` vector.

    >>> s1 = State([0.1, 0.4, 0.2])
    >>> s2 = State([0.5, 0.3, 0.5])
    >>> print s1 - s2
    [-0.4, 0.1, -0.3]

    Subtract states from each other.

    >>> print s1 * s2
    [0.05, 0.12, 0.1]

    Multiplies two states with each other.

    >>> s1 *= s2
    >>> print s1
    [0.05, 0.12, 0.1]

    Multiplies two states in place.

    """
    initial_states = None
    """List of initial states.

    :type: str | list"""
    terminal_states = None
    """List of terminal states.

    :type: str | list"""

    def __init__(self, features, name=None):
        super(State, self).__init__(features, name)

    def is_initial(self):
        """Checks if the state is an initial state.

        Returns
        -------
        bool :
            Whether the state is an initial state or not.

        """
        if State.initial_states is None:
            return False

        if isinstance(State.initial_states, list):
            return self.name in State.initial_states
        return self.name == self.initial_states

    def is_terminal(self):
        """Checks if the state is a terminal state.

        Returns
        -------
        bool :
            Whether the state is a terminal state or not.

        """
        if State.terminal_states is None:
            return False

        if isinstance(State.terminal_states, list):
            return self.name in State.terminal_states
        return self.name == self.terminal_states

    # noinspection PyMethodMayBeStatic
    def is_valid(self):
        # noinspection PyUnresolvedReferences,PyUnusedLocal
        """Check if this state is a valid state.

        Returns
        -------
        bool :
            Whether the state is valid or not.

        Notes
        -----
        Optionally this method can be overwritten at runtime.

        Examples
        --------
        >>> def my_is_valid(self)
        ...     pass
        ...
        >>> MDPPrimitive.is_valid = my_is_valid

        """
        return True

# noinspection PyAbstractClass,PyUnresolvedReferences
class Action(MDPPrimitive):
    """Representation of an action.

    Actions are represented by an array of features.

    Parameters
    ----------
    features : array_like, shape (`nfeatures`,)
        List of features, where `nfeatures` is the number of features
        identifying the primitive.
    name : str, optional
        The name of the primitive. Default is ''.

    Attributes
    ----------
    name
    dtype : {DTYPE_FLOAT, DTYPE_INT, DTYPE_OBJECT}
        The type of the features.
    nfeatures : int
        The number of features.
    discretized : bool
        Flag indicating whether the features are discretized or not.
    min_features : list
        The minimum value for each feature.
    max_features : list
        The minimum value for each feature.
    states_per_dim : list
        The number of states per dimension.
    description : dict
        A description of the features.

    Notes
    -----
    Use the `description` to encode action information. The information
    should contain the list of all available feature combinations, the
    name of each feature.

    :Examples:

        A description of an action with three possible discrete actions:

        ::

            {
                "out": {"value": [-0.004]},
                "in": {"value": [0.004]},
                "kick": {"value": [-1.0]}
            }

        A description of an action with one possible continuous action with
        name `move`, a value of `*` allows to find the action for every
        feature array. Additional information encodes the feature name together
        with its index into the feature array are given for each higher level
        element of feature array:

        ::

            {
                "move": {
                    "value": "*",
                    "descr": {
                        "LArm": {"dx": 0, "dy": 1, "dz": 2},
                        "RArm": {"dx": 3, "dy": 4, "dz": 5},
                        "LLeg": {"dx": 6, "dy": 7, "dz": 8},
                        "RLeg": {"dx": 9, "dy": 10, "dz": 11},
                        "Torso": {"dx": 12, "dy": 13, "dz": 14}
                    }
                }
            }

        Similarly, a continuous state can be encoded as follows, which identifies
        the name of each feature together with its index into the feature array:

        ::

            {
                "LArm": {"x": 0, "y": 1, "z": 2},
                "RArm": {"x": 3, "y": 4, "z": 5},
                "LLeg": {"x": 6, "y": 7, "z": 8},
                "RLeg": {"x": 9, "y": 10, "z": 11},
                "Torso": {"x": 12, "y": 13, "z": 14}
            }

        A discrete state can be encoded by identifying the position of each feature:

        ::

            {
                "image x-position": 0,
                "displacement (mm)": 1
            }

        Alternatively, the feature can be identified by a list of features, giving he
        positional description:

        ::

            ["image x-position", "displacement (mm)"]

    Rather then setting the attributes directly, use the methods :meth:`set_nfeatures`,
    :meth:`set_dtype`, :meth:`set_description`, :meth:`set_discretized`, :meth:`set_minmax_features`,
    and :meth:`set_states_per_dim` in order to enforce type checking.

    Examples
    --------
    >>> Action.set_description({'LArm': {'dx': 0, 'dy': 1, 'dz': 2}
    ...                         'RArm': {'dx': 3, 'dy': 4, 'dz': 5}})

    This description identifies the features to be the delta x-y-z-position of
    the left and the right arm. The position into the feature array is given
    by the integer numbers.

    >>> def my_key_to_index(key)
    ...     return {
    ...         "dx": 0,
    ...         "dy": 1,
    ...         "dz": 2
    ...     }[key]
    ...
    >>> Action.key_to_index = staticmethod(my_key_to_index)

    This defines a mapping for each key.

    >>> action = [0.1, 0.4, 0.3. 4.6. 2.5. 0.9]
    >>>
    >>> mapping = Action.description['LArm']
    >>>
    >>> larm = np.zeros[len(mapping.keys())]
    >>> for key, axis in mapping.iteritems():
    ...     larm[Action.key_to_index(key)] = action[axis]
    ...
    >>> print larm
    [0.1, 0.4, 0.3]

    This extracts the features for the left arm from the `action` vector.

    >>> a1 = Action([0.1, 0.4, 0.2])
    >>> a2 = Action([0.5, 0.3, 0.5])
    >>> print a1 - a2
    [-0.4, 0.1, -0.3]

    Subtract actions from each other.

    >>> print a1 * a2
    [0.05, 0.12, 0.1]

    Multiplies two actions with each other.

    >>> a1 *= a2
    >>> print a1
    [0.05, 0.12, 0.1]

    Multiplies two actions in place.

    """
    def __init__(self, features, name=None):
        super(Action, self).__init__(features, name)

        self._name = name if name is not None else Action.get_name(self._features)

    @classmethod
    def get_name(cls, features):
        """Retrieves the name of the action.

        Retrieve the name of the action using the action's description. In the case
        that all features are zero the action is considered a `no-op` action.

        Parameters
        ----------
        features : ndarray
            A feature array.

        Returns
        -------
        str :
            The name of the action.

        """
        features = np.asarray(features, dtype=cls.dtype)

        if cls.description is not None:
            for e, config in cls.description.iteritems():
                if np.asarray(config["value"]).shape != features.shape:
                    ValueError("Dimension mismatch: array 'config['value']' is vector of length %d,"
                               " but 'features' is a vector of length %d." % (np.asarray(config["value"]).shape[0],
                                                                              features.shape[0]))
                if config["value"] == features or config["value"] == "*":
                    return e

        if not features.any():
            return "no-op"

        return ""

    @classmethod
    def get_noop_action(cls):
        """Creates a `no-op` action.

        A `no-op` action does not have any effect.

        Returns
        -------
        Action :
            A `no-op` action.

        """
        if not isinstance(cls.nfeatures, int):
            raise ValueError("Attribute 'nfeatures' must be of <type 'int'>, got %s" % str(type(cls.nfeatures)))

        return cls(np.zeros(cls.nfeatures), "no-op")


from robolink import *    # API to communicate with RoboDK
from robodk import *      # basic matrix operations
RDK = Robolink()

def TCP_On(toolitem):
    """Attaches the closest object to the toolitem Htool pose,
    It will also output appropriate function calls on the generated robot program (call to TCP_On)"""
    toolitem.AttachClosest()
    toolitem.RDK().RunMessage('Set air valve on')
    toolitem.RDK().RunProgram('TCP_On()');

PARAM_SIZE_PALLET = 'SizePallet'                    #numero de cubos

frame_conv_moving   = RDK.Item('MovingRef', ITEM_TYPE_FRAME)

#poner Ur10 A en home
robot= RDK.Item('UR10 A',ITEM_TYPE_ROBOT)
tool=  RDK.Item('GripperA', ITEM_TYPE_TOOL)

t1= RDK.Item('CercaGet', ITEM_TYPE_TARGET)
t2= RDK.Item('get1er', ITEM_TYPE_TARGET)                                                              #CAMBIA

t3= RDK.Item('CercaBanda', ITEM_TYPE_TARGET)
t4= RDK.Item('En Banda', ITEM_TYPE_TARGET)

a=RDK.getParam(PARAM_SIZE_PALLET)
a=int(a)
b=0
while (a>0):
    #MOVIMIENTOS
    robot.MoveJ(t1)                     # se acerca a los cubos

    a2=t2.Pose()*transl(-150*b,0,0)          #posicion del cubo

    robot.MoveJ(a2)

    TCP_On(tool)                        #recoge el cubo
    robot.MoveJ(t1)                     # move to aproach
    robot.MoveJ(t3)                     # move approach to conveyor
    robot.MoveJ(t4)                     # put in conveyor

    tool.DetachAll(frame_conv_moving)
    robot.MoveJ(t3)                     # move to aproach table paint

    a-=1
    b+=1


from unicodedata import name
from django.shortcuts import render
from django.http import HttpResponse

# Create your views here.

def index(request):
  # return HttpResponse("
Hello world
")
  context = {
      'name': 'Dharmendra',
      'data': {
          'm_name': 'Kumar',
          'l_name': 'Sharma',
      },
      'backend_lang': [
          'php', 'node', 'python', 'jquery', 'html'
      ],
      'percentage': {
          'TENTH': 71,
          'TWELVETH': 69,
          'BTECH': 78,
      }
  }
  return render(request, 'learn/home.html', context)


import urllib.request, urllib.parse, urllib.error
import json
import ssl

# Ignore SSL certificate errors
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE

address = input('Enter- ')
url = urllib.request.urlopen(address)
print('Retrieving', url)
data = url.read().decode('utf-8')
print(type(data))

#print(data)
#print('Retrieved', len(data), 'characters')
newlist = list()
number = 0
counternumber = 0
info = json.loads(data)
#print(type(info))
#print('user count:', info)
counter = 0
#find the positon of comments!!!!
for key in info:
    if(key == 'comments'):
        #start from key = 'comments', extract data from a dictionary of list
        while counter < 50:
            #print(info[key][counter])
            #put the list that has small dictionaries into a newlist
            newlist.append(info[key][counter])
            counter = counter + 1
            number = number + 1
#print(newlist)
#extract specific data from list
for item in newlist:
    counternumber = item['count'] + counternumber
print(counternumber)
#print('sum number =', number)


#x mas tree of any size

#Taking the size
size = int(input('Enter the size :- '))

#parameters
size += size%2==0 #change even to odd
half = size//2
quad = half//2

#print the tree
for i in range(size+quad):
	for j in range(size):
		symbol = ' '

		#Crown = 3 Triangles
		if (i+j==half or j-i==half or i==half) or (i>=half and (i+j==half+quad or j-i==half-quad or i==half+quad)) or (i>=half+quad and (i+j==2*half or j-i==0 or i==size-1)):
			symbol = '*'

		#Trunk = 1 vertical rectangle
		if (i>=size and j in [quad, quad+half]) or (quad<=j<=quad+half and i==size+quad-1):
			symbol = '*'

		print(symbol, end = '')
	print()


def getNthFib(n):
    """
    My solution
    O(n) runtime
    O(1) space
    """
    a = [0, 1]
    if n == 0 or n == 1:
        return a[0]
    i = 2
    while i < n:
        newValue = a[0] + a[1]
        a[0] = a[1]
        a[1] = newValue
        i += 1
    return a[1]


from audioop import reverse
from sys import modules
import numpy as np
from typing import List
from .base import Module

class Linear(Module):
    """
    Applies linear (affine) transformation of data: y = x W^T + b
    """
    def __init__(self, in_features: int, out_features: int, bias: bool = True):
        """
        :param in_features: input vector features
        :param out_features: output vector features
        :param bias: whether to use additive bias
        """
        super().__init__()
        self.in_features = in_features
        self.out_features = out_features
        self.weight = np.random.uniform(-1, 1, (out_features, in_features)) / np.sqrt(in_features)
        self.bias = np.random.uniform(-1, 1, out_features) / np.sqrt(in_features) if bias else None

        self.grad_weight = np.zeros_like(self.weight)
        self.grad_bias = np.zeros_like(self.bias) if bias else None

    def compute_output(self, input: np.array) -> np.array:
        """
        :param input: array of shape (batch_size, in_features)
        :return: array of shape (batch_size, out_features)
        """
        if self.bias is None:
            return input @ self.weight.T

        return input @ self.weight.T + self.bias

    def compute_grad_input(self, input: np.array, grad_output: np.array) -> np.array:
        """
        :param input: array of shape (batch_size, in_features)
        :param grad_output: array of shape (batch_size, out_features)
        :return: array of shape (batch_size, in_features)
        """
        return grad_output @ self.weight

    def update_grad_parameters(self, input: np.array, grad_output: np.array):
        """
        :param input: array of shape (batch_size, in_features)
        :param grad_output: array of shape (batch_size, out_features)
        """
        if not self.bias is None:
            self.grad_bias += grad_output.sum(axis=0)

        self.grad_weight += grad_output.T @ input

    def zero_grad(self):
        self.grad_weight.fill(0)
        if self.bias is not None:
            self.grad_bias.fill(0)

    def parameters(self) -> List[np.array]:
        if self.bias is not None:
            return [self.weight, self.bias]

        return [self.weight]

    def parameters_grad(self) -> List[np.array]:
        if self.bias is not None:
            return [self.grad_weight, self.grad_bias]

        return [self.grad_weight]

    def __repr__(self) -> str:
        out_features, in_features = self.weight.shape
        return f'Linear(in_features={in_features}, out_features={out_features}, ' \
               f'bias={not self.bias is None})'

class BatchNormalization(Module):
    """
    Applies batch normalization transformation
    """
    def __init__(self, num_features: int, eps: float = 1e-5, momentum: float = 0.1, affine: bool = True):
        """
        :param num_features:
        :param eps:
        :param momentum:
        :param affine: whether to use trainable affine parameters
        """
        super().__init__()
        self.eps = eps
        self.momentum = momentum
        self.affine = affine

        self.running_mean = np.zeros(num_features)
        self.running_var = np.ones(num_features)

        self.weight = np.ones(num_features) if affine else None
        self.bias = np.zeros(num_features) if affine else None

        self.grad_weight = np.zeros_like(self.weight) if affine else None
        self.grad_bias = np.zeros_like(self.bias) if affine else None

        # store this values during forward path and re-use during backward pass
        self.mean = None
        self.input_mean = None  # input - mean
        self.var = None
        self.sqrt_var = None
        self.inv_sqrt_var = None
        self.norm_input = None

    def compute_output(self, input: np.array) -> np.array:
        """
        :param input: array of shape (batch_size, num_features)
        :return: array of shape (batch_size, num_features)
        """
        if not self.training:
            self.norm_input = (input - self.running_mean) / np.sqrt(self.running_var + self.eps)
        else:
            self.mean = np.mean(input, axis=0, keepdims=True)
            self.input_mean = input - self.mean
            self.var = np.mean(self.input_mean ** 2, axis=0, keepdims=True)
            self.sqrt_var = np.sqrt(self.var + self.eps)
            self.inv_sqrt_var = 1.0 / self.sqrt_var

            self.norm_input = self.input_mean * self.inv_sqrt_var

            B = input.shape[0]

            self.running_mean = (1 - self.momentum) * self.running_mean + self.momentum * self.mean
            self.running_var = (1 - self.momentum) * self.running_var + self.momentum * self.var * B / (B - 1)

        if self.affine:
            return self.norm_input * self.weight + self.bias

        return self.norm_input

    def compute_grad_input(self, input: np.array, grad_output: np.array) -> np.array:
        """
        :param input: array of shape (batch_size, num_features)
        :param grad_output: array of shape (batch_size, num_features)
        :return: array of shape (batch_size, num_features)
        """
        grad_input = grad_output.copy()# я забыл сделать .copy() и в итоге не мог раздебажить update_grad_parameters где-то сутки. я ненавижу питон...

        if self.affine:
            grad_input *= self.weight

        if not self.training:
            return grad_input / np.sqrt(self.running_var + self.eps)

        grad_var = -0.5 * np.sum(grad_input * self.input_mean, axis=0, keepdims=True) * self.inv_sqrt_var ** 3

        grad_mean = -np.sum(grad_input * self.inv_sqrt_var, axis=0, keepdims=True) + grad_var * -2 * np.mean(self.input_mean, axis=0, keepdims=True)

        B = input.shape[0]

        return grad_input * self.inv_sqrt_var + grad_var * 2 / B * self.input_mean + grad_mean / B

    def update_grad_parameters(self, input: np.array, grad_output: np.array):
        """
        :param input: array of shape (batch_size, num_features)
        :param grad_output: array of shape (batch_size, num_features)
        """
        if self.affine:
            self.grad_weight += np.sum(grad_output * self.norm_input, axis=0)
            self.grad_bias += np.sum(grad_output, axis=0)

    def zero_grad(self):
        if self.affine:
            self.grad_weight.fill(0)
            self.grad_bias.fill(0)

    def parameters(self) -> List[np.array]:
        return [self.weight, self.bias] if self.affine else []

    def parameters_grad(self) -> List[np.array]:
        return [self.grad_weight, self.grad_bias] if self.affine else []

    def __repr__(self) -> str:
        return f'BatchNormalization(num_features={len(self.running_mean)}, ' \
               f'eps={self.eps}, momentum={self.momentum}, affine={self.affine})'

class Dropout(Module):
    """
    Applies dropout transformation
    """
    def __init__(self, p=0.5):
        super().__init__()
        assert 0 <= p < 1
        self.p = p
        self.mask = None

    def compute_output(self, input: np.array) -> np.array:
        """
        :param input: array of an arbitrary size
        :return: array of the same size
        """
        if not self.training:
            return input

        self.mask = np.random.binomial(n=1, p=1-self.p, size=input.shape) / (1 - self.p)
        return self.mask * input

    def compute_grad_input(self, input: np.array, grad_output: np.array) -> np.array:
        """
        :param input: array of an arbitrary size
        :param grad_output: array of the same size
        :return: array of the same size
        """
        if not self.training:
            return grad_output

        return grad_output * self.mask

    def __repr__(self) -> str:
        return f'Dropout(p={self.p})'

class Sequential(Module):
    """
    Container for consecutive application of modules
    """
    def __init__(self, *args):
        super().__init__()
        self.modules = list(args)

    def compute_output(self, input: np.array) -> np.array:
        """
        :param input: array of size matching the input size of the first layer
        :return: array of size matching the output size of the last layer
        """
        output = None
        for module in self.modules:
            if output is None:
                output = module(input)
            else:
                output = module(output)
        return output

    def compute_grad_input(self, input: np.array, grad_output: np.array) -> np.array:
        """
        :param input: array of size matching the input size of the first layer
        :param grad_output: array of size matching the output size of the last layer
        :return: array of size matching the input size of the first layer
        """
        grad_input = None
        for i in range(len(self.modules) - 1, -1, -1):
            if grad_input is None:
                grad_out = grad_output
            else:
                grad_out = grad_input

            if i == 0:
                model_in = input
            else:
                model_in = self.modules[i - 1].output

            grad_input = self.modules[i].backward(model_in, grad_out)

        return grad_input

    def __getitem__(self, item):
        return self.modules[item]

    def train(self):
        for module in self.modules:
            module.train()

    def eval(self):
        for module in self.modules:
            module.eval()

    def zero_grad(self):
        for module in self.modules:
            module.zero_grad()

    def parameters(self) -> List[np.array]:
        return [parameter for module in self.modules for parameter in module.parameters()]

    def parameters_grad(self) -> List[np.array]:
        return [grad for module in self.modules for grad in module.parameters_grad()]

    def __repr__(self) -> str:
        repr_str = 'Sequential(\n'
        for module in self.modules:
            repr_str += ' ' * 4 + repr(module) + '\n'
        repr_str += ')'
        return repr_str


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Fri Sep 16 10:35:09 2022

@author: kamila
"""
import plotly.express as px
import plotly.graph_objects as go
import dash
from dash import dcc, html, no_update, ctx
from dash.dependencies import Input, Output, State, MATCH, ALL
import dash_bootstrap_components as dbc
import geopandas as gpd
import pandas as pd
import plotly.io as pio
import json
from datetime import datetime
import numpy as np
import base64
import io
import ast
import sys

pd.set_option('display.max_columns', None)
pd.set_option('display.width', 1300)

# plot postcodes
pio.renderers.default='browser'

# import df
pc_sales_df = gpd.read_file('pc_sales_df.geojson')

# join ship from locations
delivery_loc = pd.read_csv('pc_shipfrom_loc.csv')
delivery_loc['Row Labels'] = delivery_loc['Row Labels'].astype('str')
delivery_loc['Row Labels'] = delivery_loc['Row Labels'].apply(lambda x: '0' + x if x.startswith('8') else x)
delivery_loc.replace(0, False, inplace = True)
delivery_loc.replace(1, True, inplace = True)

pc_sales_df = pc_sales_df.merge(delivery_loc, left_on = 'poa_code21', right_on = 'Row Labels', how = 'left')

# ready geometries

pc_sales_df['geometry'] = pc_sales_df.to_crs(pc_sales_df.estimate_utm_crs()).simplify(500).to_crs(pc_sales_df.crs) # simplify boundaries to 500m
pc_sales_df = pc_sales_df.to_crs( epsg = 4326) # change to lat/long
pc_sales_df.set_index('poa_code21', inplace = True)

df = pc_sales_df
df['terr_colour'] = np.NaN

# store data WA bunnings
store_data = pd.read_csv(r'20221121_SF_latest_add.csv')
store_data['total_tw_purchases'] = store_data['total_tw_purchases'].astype(float)

# Define colors for each group
color_map = {
    'BUNNINGS': 'blue',
    'INDEPENDENTS': 'red'
}

# Iterate over the 'group' column and assign colors accordingly
colors = [color_map[group] for group in store_data['group']]

store_markers = go.Scattermapbox(
    lat=store_data['store_lat'],
    lon=store_data['store_long'],
    mode='markers',
    marker=dict(
        size=store_data['total_tw_purchases'],
        sizemode='diameter',
        sizeref=10000,
        color=colors,
        colorscale='Viridis',  # Add a colorscale to enable color differentiation
        opacity=0.7
    ),
    text=store_data['cust name'],
    name='All'
)

# store_data_indy = pd.read_csv(r'C:\Users\KAmbrozewicz\OneDrive - UMCOS NEWCOMBE PTY LIMITED\GIS\20221121_SF_latest_add.csv')

# store_heatmap = go.Figure(
#     data=[
#         go.Densitymapbox(
#             lat=store_data_indy['lat'],
#             lon=store_data_indy['long'],
#             radius=60 ,
#             name = 'Heatmap',
#             colorscale='Viridis',  # Specify a discrete color scale
#             zmin=1,  # Set the minimum value for color scaling
#             zmax=5  # Set the maximum value for color scaling
#         )
#     ]
# )

# filter by state to speed up plotting

state_coords = {'NT' : [-19.491411, 132.550964],
                'NSW' : [-33.872762037132375, 147.22963557432993],
                'VIC' : [-37.020100, 144.964600],
                'QLD' : [-20.917574, 142.702789],
                'SA' : [-30.000233, 136.209152],
                'WA' : [-25.953512, 117.857048],
                'TAS' : [-41.640079, 146.315918]
                }

# initialise variables
df_state = df[df.codestte == 'WA']

fig = px.choropleth_mapbox(df_state,
                           geojson = df_state.geometry,
                           locations = df_state.index,
                           opacity = 0.2,
                           center = {'lat' : -25.953512, 'lon' : 117.857048},
                           zoom = 4.5,
                           height = 1800,
                           width = 1600,
                           mapbox_style="carto-positron",
                           # uirevision = 'Retain user zoom preferences'
)

fig.update_layout(
    legend=dict(
        yanchor="top",
        y=0.9,
        xanchor="left",
        x=0.01,
        font=dict(
            size=12
        )
    )
)
fig.add_trace(store_markers)

# app
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP])

server = app.server

app.layout = html.Div(
    [
     dbc.Row(
        #  dbc.Col(
        #      [
        #          html.Div(
        #              [
        #                  html.Pre('Select a State', style = {'paddingRight' : '20px', 'paddingTop' : '10px', 'paddingBottom' : '0px'}),

        #                  dcc.Dropdown(
        #                              id = 'select_state',
        #                               options = [{'label' : i, 'value' : i} for i in pc_sales_df.codestte.unique()],
        #                               value = 'NSW',
        #                               multi = True,
        #                               style = {'width' : 400}

        #                 )
        #             ], style = {'paddingLeft' : '70px', 'paddingTop' : '10px', 'display' : 'flex'}
        #         )
        #     ]
        # )
    ),

     dbc.Row(
         [
             dbc.Col(
                 [
                    dcc.Graph(id = 'graph', figure = fig)
                 ], width = 9

            ),

             dbc.Col(
                 [
                     dbc.Row(
                         [
                             html.Div(
                                 [
                                     html.Br(),

                                        html.Div(
                                                html.Pre('Select a State',
                                                        style = {
                                                                  'font-family' : 'arial',
                                                                  'font-size' : '17px',
                                                                  'paddingLeft' : '10px',
                                                                  'paddingTop' : '10px',
                                                                  'paddingBottom' : '0px'
                                                         }
                                                ), style = {'width' : '160px', 'paddingRight' : '10px'}
                                        ),

                                     dcc.Dropdown(
                                                 id = 'select_state',
                                                  options = [{'label' : i, 'value' : i} for i in pc_sales_df.codestte.unique()],
                                                  value = 'WA',
                                                  multi = True,
                                                  style = {'width' : 310}

                                    )
                                ], style = {'paddingTop' : '50px', 'display' : 'flex'}
                            ),
                         ]
                    ),
                     dbc.Row(
                         [
                             html.Div(
                                 [

                                     html.Button('Add new legend entry',
                                                 id = 'btn_add_legend_entry',
                                                 style = {
                                                         'width' : 150,
                                                         'height' : 75,
                                                         'backgroundColor' : 'rgb(177,221,140)',
                                                         'marginRight' : '10px',
                                                         'borderWidth' : '1px',
                                                         'borderStyle' : 'solid',
                                                         'borderRadius' : '3px',
                                                         'borderColor' : 'rgb(174,174,174)',
                                                }
                                    ),

                                     html.Button('Download map and legend',
                                                 id = 'btn_download_map',
                                                 style = {
                                                         'width' : 150,
                                                         'height' : 75,
                                                         'margin-right' : '10px',
                                                         'borderWidth' : '1px',
                                                         'borderStyle' : 'solid',
                                                         'borderRadius' : '3px',
                                                         'borderColor' : 'rgb(174,174,174)',
                                                         'background-color' : 'rgb(242,242,242)'
                                                }
                                    ),

                                     html.Div(
                                             dcc.Upload('Click to import data',
                                                        id = 'import_data',
                                                        style = {
                                                                'textAlign' : 'center',
                                                                'margin' : '10px',
                                                                'cursor' : 'pointer'
                                                        }

                                            ), style = {'width' : 150,
                                                        'height' : 75,
                                                        'borderWidth' : '1px',
                                                        'borderStyle' : 'solid',
                                                        'borderRadius' : '3px',
                                                        'borderColor' : 'rgb(174,174,174)',
                                                        'background-color' : 'rgb(242,242,242)'
                                                }
                                    ),

                                 ], style = {'display' : 'flex', 'paddingBottom' : '10px'}
                            ),
                        ]
                    ),

                    html.Div(id = 'test_area'),

                    dcc.Store(id = 'legend_triggered', storage_type = 'memory'), # which legend was last triggered

                    dcc.Store(id = 'map_legend_data', data = {}, storage_type = 'memory'), # map data stored for download

                    dcc.Store(id = 'map_upload', storage_type = 'memory'),

                    dcc.Store(id = 'nn_click_count', data = 0, storage_type = 'memory'),

                    dcc.Store(id = 'legend_data', storage_type = 'memory'),

                    dcc.Download(id = 'download_map_data'),

                    #html.Button('test button', id = 'test_button', style = {'width' : 200, 'height' : 75}),

                    html.Div(id = 'legend_container', children = []),

                    html.Div(id = 'test_import_area'),

                    html.Div(id = 'legend_label_test_area', children = [])

                 ]
            )
        ]
    )
    ]
)
# access legend labels, colours
@app.callback(
    Output('legend_data', 'data'),
    #Output('legend_label_test_area', 'children'),
    Input({'type' : 'colour_label', 'index' : ALL},'value'),
    State({'type' : 'colour_label', 'index' : ALL}, 'value'),
    State({'type' : 'colour_input', 'index' : ALL}, 'value'),
    prevent_initial_call = True
    )
def get_legend_label(n_clicks, legend_labels, legend_colour):
    legend_dict = dict(zip(legend_colour, legend_labels))
    return legend_dict #, html.Pre('get_legend_label function output: ' + str(legend_dict))

# import data
@app.callback(
    Output('map_upload', 'data'),
    #Output('test_import_area', 'children'), # populate div while debugging
    Input('import_data', 'contents'),
    prevent_initial_call = True
)
def parse_contents(contents):
    content_type, content_string = contents.split(',')
    decoded_content = base64.b64decode(content_string)
    decoded_str = decoded_content.decode('utf-8')
    return decoded_str #, html.Pre('output from parse_contents function: ' + decoded_str[:50])

# download data
@app.callback(
    Output('download_map_data', 'data'),
    # Output('legend_label_test_area', 'children'),
    Input('btn_download_map', 'n_clicks'),
    State('map_legend_data', 'data'),
    State('legend_data', 'data'),
    prevent_initial_call = True
)

def download_data(n_clicks, data, legend_data): # this version is outputting map and legend data as list of dictionaries
    if n_clicks is not None:
        download_legend_data = json.dumps([legend_data, data])
        download_data = json.dumps(data)
        content_dict = {'content' : download_legend_data}
    return dict(content_dict, filename = 'mapdata.text') #, html.Pre(str(download_legend_data))

# add new legend entry
@app.callback(
    Output('legend_container', 'children'),
    # Output('btn_add_legend_entry', 'n_clicks'),
    Output('nn_click_count', 'data'),
    Input('btn_add_legend_entry', 'n_clicks'),
    Input('map_upload', 'data'),
    State('legend_container', 'children'),
    State('nn_click_count', 'data'),
    prevent_initial_call = True
)
def add_legend(n_clicks, map_upload, container, nn_clicks):
    if map_upload is None:
        if n_clicks is None:
            return container, nn_clicks
        new_legend = html.Div(
            id={'type': 'legend_div', 'index': nn_clicks},
            children=[
                dbc.Input(
                    id={'type': 'colour_input', 'index': nn_clicks},
                    type='color',
                    style={'width': 75, 'height': 50}
                ),
                dbc.Input(
                    id={'type': 'colour_label', 'index': nn_clicks},
                    type='text',
                    placeholder='Enter a sales territory name',
                    style={'width': 395, 'height': 50}
                )
            ],
            style={'display': 'flex'}
        )
        nn_clicks += 1
        container.append(new_legend)

        return container, nn_clicks

    else:
        json_list = json.loads(map_upload)
        pc_colour_d = json_list[0]
        container.clear()  # Clear existing legend entries
        for col, label in pc_colour_d.items():
            new_legend = html.Div(
                id={'type': 'legend_div', 'index': nn_clicks},
                children=[
                    dbc.Input(
                        id={'type': 'colour_input', 'index': nn_clicks},
                        value=str(col),
                        type='color',
                        style={'width': 75, 'height': 50}
                    ),
                    dbc.Input(
                        id={'type': 'colour_label', 'index': nn_clicks},
                        type='text',
                        value=label,
                        style={'width': 395, 'height': 50}
                    )
                ],
                style={'display': 'flex'}
            )
            container.append(new_legend)
            nn_clicks += 1
        return container, nn_clicks

# colour selected listener
@app.callback(
    Output('legend_triggered', 'data'),
    # Output('test_area', 'children'),
    Input({'type' : 'legend_div', 'index' : ALL}, 'n_clicks'),
    prevent_initial_call = True
    )
def colour_listener(colourLastSelected):
    return ctx.triggered_id.index #, html.Pre(ctx.triggered_id.index)

# update map
@app.callback(
    Output('graph', 'figure'),
    Output('graph', 'clickData'),
    Output('map_legend_data', 'data'),
    #Output('test_area', 'children'),
    Output('map_upload', 'clear_data'),
    Input('map_legend_data', 'data'),
    Input('graph', 'clickData'),
    Input('select_state', 'value'),
    State({'type' : 'colour_input', 'index' : ALL}, 'value'),
    State('legend_triggered', 'data'),
    Input('map_upload', 'data'),
    prevent_initial_call = True
    )
def update_map(postcode_colour_d, selectPC, selectedState, selectedColor, legendTriggered, mapUpload):
    clear_map_data = False

    if mapUpload is not None:
        json_str_list = json.loads(mapUpload)
        json_str = json_str_list[1]
        postcode_colour_d.update(json_str)
        clear_map_data = True

    if selectPC is not None:
        postcode = selectPC['points'][0]['location']

        if postcode in postcode_colour_d:
            postcode_colour_d.pop(postcode)
        else:
            if len(selectedColor) == 0 or None in selectedColor:
                global fig
                return fig, None, postcode_colour_d, clear_map_data
            else:

                triggeredColor = selectedColor[legendTriggered]
                postcode_colour_d[postcode] = triggeredColor

    postcode_list = list(postcode_colour_d.keys())

    if type(selectedState) == str:
        df_state = df[df.codestte == selectedState]

    else:
        df_state = df[df.codestte.isin(selectedState)]

    fig = px.choropleth_mapbox(df_state,
                                geojson = df_state.geometry,
                                locations = df_state.index,
                                opacity = 0.2,
                                #center = {'lat' : state_coords[selectedState][0], 'lon' : state_coords[selectedState][1]},
                                center = {'lat' : -25.953512, 'lon' : 117.857048},
                                zoom = 4.5,
                                height = 1800,
                                width = 1600,
                                mapbox_style="carto-positron"
    )

    selected_df = df_state.loc[postcode_list, :]
    selected_df.reset_index(inplace = True)
    selected_df['terr_colour'] = selected_df['poa_code21'].apply(lambda x: postcode_colour_d.get(x))
    selected_df.set_index('poa_code21', inplace = True)

    for colour in selected_df.terr_colour.unique():
        dff = selected_df[selected_df.terr_colour == colour]
        fig.add_trace(
            px.choropleth_mapbox(dff,
                                  geojson = dff.geometry,
                                  locations = dff.index,
                                  color = dff.terr_colour,
                                  color_discrete_map=({colour : colour}),
                                  opacity = 0.7
                                  ).data[0]
        )

    fig.update_layout(uirevision = 'Retain user zoom preferences')
    fig.add_trace(store_markers)

    #debug_output = 'index triggered: ' + str(legendTriggered) + ' color selected: ' +str(selectedColor) +  'postcode list: ' + str(postcode_list) + ' ' + str(postcode_colour_d)

    return fig, None, postcode_colour_d, clear_map_data

if __name__ == "__main__": app.run_server(debug=False, host='127.0.0.1', port=8050, dev_tools_hot_reload = True)


from scipy import spatial
import os
import pickle
from six.moves import cPickle
from keras import backend as K
from keras.layers import Input, Conv2D, MaxPooling2D
from keras.models import Model
import cv2
from matplotlib import pyplot as plt
import numpy as np
import operator

from flask import Flask, request, jsonify
import numpy as np
from PIL import Image

app = Flask(__name__)

@app.route('/')
def hello_world():
    return 'Hello World!'

@app.route('/test', methods=["POST"])
def test():
    data = {}
    if request.method == "POST":
        print("in test")

        img = Image.open(request.files['file'])
        img = np.array(img)
        # plt.imshow(img)
        img = cv2.cvtColor(np.array(img), cv2.COLOR_BGR2RGB)
        # print(img)
        a = ranking_module(img)
        # print(a)
        data["filename_list"] = []
        count = 0
        for i in a:
            data["filename_list"].append(i)
            count+=1
            if count >= 10:
               break

        # data["filename_list"].append('topcit.png')
        # data["filename_list"].append('양동욱_21.jpg')

    return jsonify(data)

class Config:

	def __init__(self):

		# Print the process or not
		self.verbose = True

		# base network --> Todo! ResNet 으로 바꿔야함
		self.network = 'vgg'

		# Setting for data augmentation
		self.use_horizontal_flips = False
		self.use_vertical_flips = False
		self.rot_90 = False

		# Anchor box scales
    	# If im_size is smaller, anchor_box_scales should be scaled
	    # Original anchor_box_scales in the paper is [128, 256, 512]
		self.anchor_box_scales = [64, 128, 256]

		# Anchor box ratios
		self.anchor_box_ratios = [[1, 1], [1./math.sqrt(2), 2./math.sqrt(2)], [2./math.sqrt(2), 1./math.sqrt(2)]]

		# Size to resize the smallest side of the image
		# Original setting in paper is 600. Set to 300 in here to save training time
		self.im_size = 300

		# image channel-wise mean to subtract
		self.img_channel_mean = [103.939, 116.779, 123.68]
		self.img_scaling_factor = 1.0

		# number of ROIs at once --> Todo! If ResNet, RoI will be 3.
		self.num_rois = 4

		# stride at the RPN (this depends on the network configuration)
		self.rpn_stride = 16

		self.balanced_classes = False

		# scaling the stdev
		self.std_scaling = 4.0
		self.classifier_regr_std = [8.0, 8.0, 4.0, 4.0]

		# overlaps for RPN --> Todo! If ResNet, IoU can be reduced. (what value? : 아마 0.5도 괜찮을듯 논문에서 본 적 있음)
		self.rpn_min_overlap = 0.3
		self.rpn_max_overlap = 0.7

		# overlaps for classifier ROIs
		self.classifier_min_overlap = 0.1
		self.classifier_max_overlap = 0.5

		# placeholder for the class mapping, automatically generated by the parser
		self.class_mapping = None

		self.model_path = None

"""# VGG Model"""

def get_img_output_length(width, height):
    def get_output_length(input_length):
        return input_length//16

    return get_output_length(width), get_output_length(height)

def nn_base(input_tensor=None, trainable=False):

    input_shape = (None, None, 3)

    if input_tensor is None:
        img_input = Input(shape=input_shape)
    else:
        if not K.is_keras_tensor(input_tensor):
            img_input = Input(tensor=input_tensor, shape=input_shape)
        else:
            img_input = input_tensor

    bn_axis = 3

    # Block 1
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv1')(img_input)
    x = Conv2D(64, (3, 3), activation='relu', padding='same', name='block1_conv2')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block1_pool')(x)

    # Block 2
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv1')(x)
    x = Conv2D(128, (3, 3), activation='relu', padding='same', name='block2_conv2')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block2_pool')(x)

    # Block 3
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv1')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv2')(x)
    x = Conv2D(256, (3, 3), activation='relu', padding='same', name='block3_conv3')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block3_pool')(x)

    # Block 4
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv1')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv2')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block4_conv3')(x)
    x = MaxPooling2D((2, 2), strides=(2, 2), name='block4_pool')(x)

    # Block 5
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv1')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv2')(x)
    x = Conv2D(512, (3, 3), activation='relu', padding='same', name='block5_conv3')(x)
    # x = MaxPooling2D((2, 2), strides=(2, 2), name='block5_pool')(x)

    return x

"""# Load Config Pickle"""

"""# Image Preprocessing"""

def format_img_size(img, C):
	""" formats the image size based on config """
	img_min_side = float(C.im_size)
	(height,width,_) = img.shape

	if width <= height:
		ratio = img_min_side/width
		new_height = int(ratio * height)
		new_width = int(img_min_side)
	else:
		ratio = img_min_side/height
		new_width = int(ratio * width)
		new_height = int(img_min_side)
	img = cv2.resize(img, (new_width, new_height), interpolation=cv2.INTER_CUBIC)
	return img, ratio

def format_img_channels(img, C):
	""" formats the image channels based on config """
	img = img[:, :, (2, 1, 0)]
	img = img.astype(np.float32)
	img[:, :, 0] -= C.img_channel_mean[0]
	img[:, :, 1] -= C.img_channel_mean[1]
	img[:, :, 2] -= C.img_channel_mean[2]
	img /= C.img_scaling_factor
	img = np.transpose(img, (2, 0, 1))
	img = np.expand_dims(img, axis=0)
	return img

def format_img(img, C):
	""" formats an image for model prediction based on config """
	img, ratio = format_img_size(img, C)
	img = format_img_channels(img, C)
	return img, ratio

#print(len(load_pickles.keys()))
#print(load_pickles.items())

"""# Demo
 - Tested ranking module by query image
"""

#print(load_pickles.keys())

"""# Version 2 : Input is pixel data & not exists in database"""

def ranking_module(query) :
    global count
    global C
    global model_backbone
    global load_pickles
    count += 1

    if count == 1:
        base_path = '/ENGN/'
        #test_path = 'drive/My Drive/capstone/retrieval_test/retrieval_annotations.txt'  # Test data (annotation file)
        test_base_path = '/ENGN/retrieval_test'  # Directory to save the test images
        config_output_filename = os.path.join(base_path, 'model_vgg_config.pickle')
        pickles = 'backbone_pickles'
        with open(config_output_filename, 'rb') as f_in:
            C = pickle.load(f_in)
        # turn off any data augmentation at test time
        C.use_horizontal_flips = False
        C.use_vertical_flips = False
        C.rot_90 = False
        """# Construct and Load the Model"""
        num_features = 512
        input_shape_img = (None, None, 3)
        img_input = Input(shape=input_shape_img)
        # define the base network *Todo : (VGG here, can be Resnet50 ...)*
        shared_layers = nn_base(img_input, trainable=True)
        # model backbone
        model_backbone = Model(img_input, shared_layers)
        model_backbone.load_weights('/ENGN/model_frcnn_vgg.hdf5', by_name=True)
        # model_backbone.load_weights(C.model_path, by_name=True)

        # model backbone
        '''
        Load Pickles
         - predict 512 values using backbone model
         - saved these values as pickle file
        '''
        load_pickles = cPickle.load(open(os.path.join(test_base_path, pickles), "rb", True))

    query_class=''
    X, ratio = format_img(query, C)
    X = np.transpose(X, (0, 2, 3, 1))
    # 변형부분
    result=model_backbone.predict(X)
    query_pooling = np.max(result[0], axis=(0, 1))
    result={}

    for filepath, value in load_pickles.items() :
        cosine=spatial.distance.cosine(query_pooling, value['avg_pooling'])
        result[filepath] = [cosine, value['id']]

    cls_count={'rose' : 0, 'tiger' : 0, 'dragon' : 0, 'geometric' : 0}
    temp_result=sorted(result.items(), key=lambda x: x[1][0])

    for i in temp_result[:20] :
        cls_count[load_pickles[i[0]]['cls']]+=1
    # max_key = max(cls_count.items(), key=lambda k: k[1])
    # print(cls_count)
    query_class=sorted(cls_count.items(), key=lambda x: x[1], reverse=True)
    # print(query_class)
    for filename, value in result.items() :
        alpha = 1 if cls_count[load_pickles[filename]['cls']]==0 else cls_count[load_pickles[filename]['cls']]
        result[filename][0] +=(20/alpha) * 0.08

        # if load_pickles[filename]['cls'] == query_class[0][0] :

        #     continue
        # elif load_pickles[filename]['cls'] == query_class[1][0] :
        #     result[filename][0] *= 2
        #     continue
        # else :
        #     result[filename][0] *= 2.5
        #     continue

    result=sorted(result.items(), key=lambda x: x[1])
    #print(query_class)
    # print(result)

    img=query
    # plt.figure(figsize=(24,20))
    # plt.imshow(cv2.cvtColor(img,cv2.COLOR_BGR2RGB))
    # plt.subplot(653),plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)),plt.title('query image')
    # frame   = plt.gca()
    # frame.axes.get_xaxis().set_visible(False)
    # frame.axes.get_yaxis().set_visible(False)
    # k=0
    # for i in result :
    #     print(i[1][1])
    #     # print(i[1][0])
    # for i in result[:20] :

    #     img = cv2.imread(i[0])

    #     plt.subplot(6,5,k+6),plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)), plt.title("rank " + str(k+1) + " : " + str(i[1][0]))
    #     k+=1
    #     frame   = plt.gca()
    #     frame.axes.get_xaxis().set_visible(False)
    #     frame.axes.get_yaxis().set_visible(False)
    id_output=[x[1][1] for x in result]

    return id_output

count = 0
C = None
model_backbone = None
load_pickles = None

if __name__ == '__main__':
    app.run(host='0.0.0.0')


# @Time: 2022/4/29 15:55
# @Author: chang liu
# @Email: chang_liu_tamu@gmail.com
# @File:linked-list-24-swap-nodes-in-pairs.py
'''
Given a linked list, swap every two adjacent nodes and return its head. You must solve the problem without modifying the
 values in the list's nodes (i.e., only nodes themselves may be changed.)
'''

# Definition for singly-linked list.
# class ListNode:
#     def __init__(self, val=0, next=None):
#         self.val = val
#         self.next = next
class Solution:
    def swapPairs(self, head: Optional[ListNode]) -> Optional[ListNode]:
        l = []
        if not head:
            return None
        while head:
            l.append(head)
            head = head.next
        for i in range(len(l) // 2):
            l[i*2], l[i*2+1] = l[i*2+1], l[i*2]
        for i in range(len(l)-1):
            l[i].next = l[i+1]
        l[-1].next = None
        return l[0]


import urllib.request
from datetime import datetime
import pytz
import json
import os

# read API key from file
dir_path = os.path.dirname(os.path.realpath(__file__))
with open(os.path.join(dir_path, "api_key.txt"), "r") as f:
    api_key = f.read().strip()

print("Getting 10 trending video titles")

# fetch top 10 trending videos and their titles
url = f"https://www.googleapis.com/youtube/v3/videos?key={api_key}&chart=mostPopular&part=snippet&maxResults=10"
response = urllib.request.urlopen(url)
if response.status == 200:
    data = json.loads(response.read().decode())
    titles = [item["snippet"]["title"] for item in data["items"]]

    # save data to file
    timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
    with open(os.path.join(dir_path, f"trending{timestamp}.json"), "w") as f:
        json.dump(titles, f, indent=4)

    print(f"Video titles have been saved to trending{timestamp}.json. Enter any key to exit.")
    input()
else:
    titles = []
    print("Error occurred while fetching trending videos.")


# import the necessary packages
from assets.face_alignment import face_alignment
import numpy as np
import logging as lg
import argparse
import config
import imutils
import pickle
import cv2
import os
import time

lg.getLogger().setLevel(lg.INFO)

# filter by the size of the detected face
wh_filter = config.SIZE_DETECTION_THRESHOLD

# construct the argument parser and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", required=True, help="path to input image")
ap.add_argument(
    "-r",
    "--recognizer",
    default="./output/recognizer.pickle",
    help="path to model trained to recognize faces (default ./output folder)",
)
ap.add_argument(
    "-l",
    "--le",
    default="./output/le.pickle",
    help="path to label encoder (default ./output folder)",
)
ap.add_argument(
    "-c",
    "--confidence",
    type=float,
    default=0.6,
    help="minimum probability to filter weak detections (default .6)",
)
ap.add_argument(
    "-v",
    "--visualization",
    type=bool,
    default=False,
    help="if the user wants to visualize the face alignment (default False)",
)
args = vars(ap.parse_args())

start = time.time()
# load our serialized face detector from disk
detectorPath = config.DETECTOR
protoPath = os.path.sep.join([detectorPath, "deploy.prototxt"])
modelPath = os.path.sep.join([detectorPath, "res10_300x300_ssd_iter_140000.caffemodel"])
detector = cv2.dnn.readNetFromCaffe(protoPath, modelPath)
# load our serialized face embedding model from disk
lg.info("Loading face recognizer...")
embeddings_model = config.EMBEDDINGS

if embeddings_model == "keras":
    from assets import model_bluider as mb
    from tensorflow.keras.preprocessing.image import img_to_array
    from tensorflow.keras.applications.imagenet_utils import preprocess_input
    from tensorflow.keras.models import Model
    import tensorflow.keras.backend as K
    import tensorflow as tf

    # to compute with GPU if available
    gpus = tf.config.experimental.list_physical_devices("GPU")
    if gpus:
        try:
            # Currently, memory growth needs to be the same across GPUs
            for gpu in gpus:
                tf.config.experimental.set_memory_growth(gpu, True)
            logical_gpus = tf.config.experimental.list_logical_devices("GPU")
            print(len(gpus), "Physical GPUs,", len(logical_gpus), "Logical GPUs")
        except RuntimeError as e:
            # Memory growth must be set before GPUs have been initialized
            print(e)
    model = mb.build_model()
    vgg_face = Model(inputs=model.layers[0].input, outputs=model.layers[-2].output)
else:
    embedder = cv2.dnn.readNetFromTorch(embeddings_model)

# load the actual face recognition model along with the label encoder
recognizer = pickle.loads(open(args["recognizer"], "rb").read())
le = pickle.loads(open(args["le"], "rb").read())

lg.info("Loading image and applying detection...")
# load the image, resize it to have a width of 600 pixels (while
# maintaining the aspect ratio), and then grab the image dimensions
image = cv2.imread(args["image"])
image = imutils.resize(image, width=600)
(h, w) = image.shape[:2]
# construct a blob from the image
imageBlob = cv2.dnn.blobFromImage(
    cv2.resize(image, (300, 300)),
    1.0,
    (300, 300),
    (104.0, 177.0, 123.0),
    swapRB=False,
    crop=False,
)
# apply OpenCV's deep learning-based face detector to localize
# faces in the input image
detector.setInput(imageBlob)
detections = detector.forward()
# loop over the detections
for i in range(0, detections.shape[2]):
    # extract the confidence (i.e., probability) associated with the
    # prediction
    confidence = detections[0, 0, i, 2]
    # filter out weak detections
    if confidence > 0.5:
        # compute the (x, y)-coordinates of the bounding box for the
        # face
        box = detections[0, 0, i, 3:7] * np.array([w, h, w, h])
        (startX, startY, endX, endY) = box.astype("int")
        # extract the face ROI
        face = image[startY:endY, startX:endX]
        (fH, fW) = face.shape[:2]
        # ensure the face width and height are sufficiently large
        if fW < wh_filter or fH < wh_filter:
            continue

        face = face_alignment(face, args["visualization"])
        if embeddings_model == "keras":
            face = cv2.resize(face, (224, 224))
            face = img_to_array(face)
            face = np.expand_dims(face, axis=0)
            face = preprocess_input(face)
            face_encode = vgg_face(face)
            pred = np.squeeze(K.eval(face_encode))
            pred = pred.reshape(1, -1)
        else:
            # construct a blob for the face ROI, then pass the blob
            # through our face embedding model to obtain the 128-d
            # quantification of the face
            faceBlob = cv2.dnn.blobFromImage(
                face, 1.0 / 255, (96, 96), (0, 0, 0), swapRB=True, crop=False
            )
            embedder.setInput(faceBlob)
            pred = embedder.forward()

        preds = recognizer.predict_proba(pred)[0]
        j = np.argmax(preds)
        proba = preds[j]
        name = le.classes_[j]

        # test if the model is sure enough, otherwise class the person as unknow
        if proba <= args["confidence"]:
            name = "unknow"

        # draw the bounding box of the face along with the associated
        # probability
        text = "{}: {:.2f}%".format(name, proba * 100)
        y = startY - 10 if startY - 10 > 10 else startY + 10
        cv2.rectangle(image, (startX, startY), (endX, endY), (0, 0, 255), 2)
        cv2.putText(
            image, text, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.45, (0, 0, 255), 2
        )

# show the output image
cv2.imshow("Image", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# cv2.imwrite("output_image.jpg", image) # uncomment this to save the end image

lg.info(f"Program ended within {round(time.time()-start, 2)} seconds.")


"""
1 1 1 1 1 1 1
1 2 3 4 5 6 7
1 3 _ _ _ _ _
1 _ _ _ _ _ .

m=3-1, n=3-1, i=0,j=0
m,i | 1 1 1
m,i | 1 2 2
m,i | 1 3 5
"""
class Solution(object):
    def dfsSolution(self, m, n):
        """
        :type m: int
        :type n: int
        :rtype: int
        """
        self.paths = 0

        def dfs(m, n, i=0, j=0):
            # Base: OOB
            if i > m or j > n:
                return

            # Base: Correct end index
            if i == m and j == n:
                self.paths += 1

            # Take all paths
            dfs(m, n, i+1, j)
            dfs(m, n, i, j+1)

        dfs(m-1, n-1)
        return self.paths

    def uniquePaths(self, m, n):
        """
        :type m: int
        :type n: int
        :rtype: int
        """
        # 1. Define empty m x n matrix
        matrix = [[0]*n]*m

        # 2.1 Populate top row = 1
        for j in range(n):
            matrix[0][j] = 1
        # 2.2 Populate left col = 1
        for i in range(m):
            matrix[i][0] = 1

        # 3. Populate middle of array
        for i in range(1, m):
            for j in range(1, n):
                matrix[i][j] = matrix[i-1][j] + matrix[i][j-1]

        # 4. Return end index
        return matrix[m-1][n-1]


import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import csv
import seaborn as sns
csv_path_h = r'D:\htfg_all\dice_list_h.csv'
csv_path_t1 = r'D:\htfg_all\dice_list_h_transfer_1.csv'

csv_file = csv.reader(open(csv_path_h,'r'))
csv_list = list(csv_file)

csv_file_t1 = csv.reader(open(csv_path_t1,'r'))
csv_list_t1 = list(csv_file_t1)
RN_list = []
SN_list = []
STN_list = []
RN_list_t1 = []
SN_list_t1 = []
STN_list_t1 = []
for j in range(1,37):
    RN = csv_list[j][1]
    RN_list.append(RN)
    SN = csv_list[j][2]
    SN_list.append(SN)
    STN = csv_list[j][3]
    STN_list.append(STN)

    RN_t1 = csv_list_t1[j][1]
    RN_list_t1.append(RN_t1)
    SN_t1 = csv_list_t1[j][2]
    SN_list_t1.append(SN_t1)
    STN_t1 = csv_list_t1[j][3]
    STN_list_t1.append(STN_t1)
#
print(RN_list)
print(SN_list)
# print(STN_list)

data = {
'RN': RN_list,
'RN_t': RN_list_t1,
'SN': SN_list,
'SN_t': SN_list_t1,
'STN': STN_list,
'STN_t': STN_list_t1,
}
font1 = {'family': 'Times New Roman',
         'weight': 'normal',
         'size': 14}
df = pd.DataFrame(data).astype(float)
print(df)
f = df.boxplot(patch_artist=True, return_type='dict',sym='r*', positions=[1,1.55,3,3.55,5,5.55])
# f_t = df.boxplot(patch_artist=True, return_type='dict',sym='r*')
# print(f['boxes'])
color = ['g', 'deepskyblue', 'g', 'deepskyblue','g', 'deepskyblue']  # 有多少box就对应设置多少颜色
# color_t = ['deepskyblue', 'deepskyblue', 'deepskyblue', ]
for box, c in zip(f['boxes'], color):
    # 箱体边框颜色
    box.set(color=c, linewidth=2)
    # 箱体内部填充颜色
    box.set(facecolor='w')
plt.ylabel('DSC',font1)
plt.xlabel('Model2 and TransferFixNone', font1)
plt.grid(linestyle="--", alpha=0.3)
plt.show()

# df.plot.box()
# plt.ylabel('DSC',font1)
# plt.xlabel('TransferFixZeroUpLayer',font1)
# plt.grid(linestyle="--", alpha=0.3)
# plt.show()


def checkPermutation(per1 , per2):

    if len(per1) != len(per2):
        return False

    per1 = ' '.join(sorted(per1))
    per2 = ' '.join(sorted(per2))
    print(per1)
    print(per2)

    n = len(per1)

    for i in range(n):
        if per1[i] != per2[i]:
            return False
    return True

print(checkPermutation("god", "dog"))