import theano
from theano import tensor as T
from theano.sandbox.rng_mrg import MRG_RandomStreams as RandomStreams
from theano.tensor.signal import pool
from theano.tensor.nnet import conv3d2d
import numpy as np
from collections import OrderedDict

from .. import config
from .numpy_backend import get_random_magic_seed, get_random_magic_seed

_FLOATX = config.floatX()
_EPSILON = config.epsilon()

# ===========================================================================
# INTERNAL UTILS
# ===========================================================================
theano.config.floatX = _FLOATX

def _on_gpu():
    '''Return whether the session is set to
    run on GPU or not (i.e. on CPU).
    '''
    return theano.config.device[:3] == 'gpu' or theano.sandbox.cuda.cuda_enabled

if _on_gpu():
    '''Import cuDNN only if running on GPU:
    not having Cuda installed should not
    prevent from running the present code.
    '''
    from theano.sandbox.cuda import dnn

def get_session():
    return _on_gpu()

# ===========================================================================
# VARIABLE MANIPULATION
# ===========================================================================
def variable(value, dtype=_FLOATX, name=None, broadcastable=None):
    '''Instantiate a tensor variable.
    '''
    value = np.asarray(value, dtype=dtype)
    if broadcastable:
        return theano.shared(value=value, name=name, strict=False,
                             broadcastable=broadcastable)
    return theano.shared(value=value, name=name, strict=False)

def zeros_var(shape, dtype=_FLOATX, name=None):
    '''Instantiate an all-zeros variable.
    '''
    return variable(np.zeros(shape), dtype, name)

def ones_var(shape, dtype=_FLOATX, name=None):
    '''Instantiate an all-ones variable.
    '''
    return variable(np.ones(shape), dtype, name)

def is_variable(v):
    return isinstance(v, theano.compile.SharedVariable)

_PLACEHOLDER_ID = 0
_PLACEHOLDER_SHAPE = {}

def placeholder(shape=None, ndim=None, dtype=_FLOATX, name=None):
    '''Instantiate an input data placeholder variable.
    '''
    if shape is None and ndim is None:
        raise Exception('Specify either a shape or ndim value.')
    if shape is not None:
        ndim = len(shape)
    broadcast = (False,) * ndim

    # ====== Modify add name prefix ====== #
    global _PLACEHOLDER_ID
    name_prefix = 'ID.%02d.' % _PLACEHOLDER_ID
    _PLACEHOLDER_ID += 1
    if name is None:
        name = ''
    name = name_prefix + name
    placeholder = T.TensorType(dtype, broadcast)(name)
    # store the predefined shape of placeholder
    _PLACEHOLDER_SHAPE[name] = \
        [None for _ in range(ndim)] if shape is None else shape
    return placeholder

def is_expression(v):
    '''placeholder also is an expression'''
    return isinstance(v, theano.tensor.TensorVariable)

def is_placeholder(v):
    if is_expression(v) and v.name in _PLACEHOLDER_SHAPE:
        return True
    return False

def eval(x):
    '''Run a graph.
    '''
    # just a hack to return placeholder shape when eval
    if x in _PLACEHOLDER_SHAPE:
        return _PLACEHOLDER_SHAPE[x]
    return x.eval()

# ===========================================================================
# Shape operator
# ===========================================================================

def shape(x):
    '''Return the shape of a tensor.

    Warning: type returned will be different for
    Theano backend (Theano tensor type) and TF backend (TF TensorShape).
    '''
    shape = x.shape
    # little to eval the shape of placeholder
    if hasattr(x, 'name'):
        if x.name in _PLACEHOLDER_SHAPE:
            _PLACEHOLDER_SHAPE[shape] = _PLACEHOLDER_SHAPE[x.name]
    return shape

def int_shape(x):
    return x.shape.eval()

def ndim(x):
    return x.ndim

def broadcastable(x):
    return x.broadcastable

def addbroadcast(x, *axes):
    return T.addbroadcast(x, *axes)

# ===========================================================================
# Predefined data
# ===========================================================================

def zeros(shape, dtype=_FLOATX, name=None):
    '''Instantiate an all-zeros variable.
    '''
    return T.zeros(shape=shape, dtype=dtype)

def ones(shape, dtype=_FLOATX, name=None):
    '''Instantiate an all-ones variable.
    '''
    return T.ones(shape=shape, dtype=dtype)

def ones_like(x):
    return T.ones_like(x)

def zeros_like(x):
    return T.zeros_like(x)

def count_params(x):
    '''Return number of scalars in a tensor.

    Return: numpy integer.
    '''
    return np.prod(x.shape.eval())

def cast(x, dtype):
    if 'theano.' in str(x.__class__):
        return T.cast(x, dtype)
    return np.cast[dtype](x)

def castX(x):
    return cast(x, _FLOATX)

# LINEAR ALGEBRA

'''
Assumed overridden:
+, -, /, *, +=, -=, *=, /=
'''

def dot(x, y):
    return T.dot(x, y)

def transpose(x):
    return T.transpose(x)

def gather(reference, indices):
    '''reference: a tensor.
    indices: an int tensor of indices.

    Return: a tensor of same type as reference.
    '''
    return reference[indices]

# ===========================================================================
# ELEMENT-WISE OPERATIONS
# ===========================================================================
def var(x, axis=None, keepdims=False):
    return T.var(x, axis=axis, keepdims=keepdims)

def max(x, axis=None, keepdims=False):
    return T.max(x, axis=axis, keepdims=keepdims)

def min(x, axis=None, keepdims=False):
    return T.min(x, axis=axis, keepdims=keepdims)

def sum(x, axis=None, keepdims=False):
    '''Sum of the values in a tensor, alongside the specified axis.
    '''
    return T.sum(x, axis=axis, keepdims=keepdims)

def prod(x, axis=None, keepdims=False):
    '''Multiply the values in a tensor, alongside the specified axis.
    '''
    return T.prod(x, axis=axis, keepdims=keepdims)

def mean(x, axis=None, keepdims=False):
    dtype = None
    if 'int' in x.dtype:
        dtype = _FLOATX
    return T.mean(x, axis=axis, keepdims=keepdims, dtype=dtype)

def std(x, axis=None, keepdims=False):
    return T.std(x, axis=axis, keepdims=keepdims)

def any(x, axis=None, keepdims=False):
    '''Bitwise reduction (logical OR).
    '''
    return T.any(x, axis=axis, keepdims=keepdims)

def argmax(x, axis=-1):
    return T.argmax(x, axis=axis, keepdims=False)

def argsort(x, axis=-1):
    return T.argsort(x, axis)

def argtop_k(x, k=1):
    # top-k accuracy
    top = T.argsort(x, axis=-1)
    # (Theano cannot index with [..., -top_k:], we need to simulate that)
    top = top[[slice(None) for _ in range(top.ndim - 1)] +
              [slice(-k, None)]]
    top = top[(slice(None),) * (top.ndim - 1) + (slice(None, None, -1),)]
    return top

def argmin(x, axis=-1):
    return T.argmin(x, axis=axis, keepdims=False)

def square(x):
    return T.sqr(x)

def abs(x):
    return T.abs_(x)

def sqrt(x):
    x = T.clip(x, 0., np.inf)
    return T.sqrt(x)

def exp(x):
    return T.exp(x)

def log(x):
    return T.log(x)

def round(x):
    return T.round(x)

def pow(x, a):
    return T.pow(x, a)

def clip(x, min_value, max_value):
    if max_value < min_value:
        max_value = min_value
    return T.clip(x, min_value, max_value)

def maximum(x, y):
    return T.maximum(x, y)

def minimum(x, y):
    return T.minimum(x, y)

# ===========================================================================
# SHAPE OPERATIONS
# ===========================================================================

def reverse(x, axis=-1):
    '''Apply [::-1] to appropriate axis'''
    if axis < 0:
        axis += x.ndim
    return x[(slice(None),) * axis + (slice(None, None, -1),)]

def concatenate(tensors, axis=-1):
    return T.concatenate(tensors, axis=axis)

def reshape(x, shape):
    return T.reshape(x, shape)

def dimshuffle(x, pattern):
    '''Transpose dimensions.

    pattern should be a tuple or list of
    dimension indices, e.g. [0, 2, 1].
    '''
    pattern = tuple(pattern)
    return x.dimshuffle(pattern)

def repeat_elements(x, rep, axis):
    '''Repeat the elements of a tensor along an axis, like np.repeat.

    If x has shape (s1, s2, s3) and axis=1, the output
    will have shape (s1, s2 * rep, s3).
    '''
    return T.repeat(x, rep, axis=axis)

def resize_images(X, height_factor, width_factor, dim_ordering):
    '''Resize the images contained in a 4D tensor of shape
    - [batch, channels, height, width] (for 'th' dim_ordering)
    - [batch, height, width, channels] (for 'tf' dim_ordering)
    by a factor of (height_factor, width_factor). Both factors should be
    positive integers.
    '''
    if dim_ordering == 'th':
        output = repeat_elements(X, height_factor, axis=2)
        output = repeat_elements(output, width_factor, axis=3)
        return output
    elif dim_ordering == 'tf':
        output = repeat_elements(X, height_factor, axis=1)
        output = repeat_elements(output, width_factor, axis=2)
        return output
    else:
        raise Exception('Invalid dim_ordering: ' + dim_ordering)

def repeat(x, n):
    '''Repeat a 2D tensor.

    If x has shape (samples, dim) and n=2,
    the output will have shape (samples, 2, dim).
    '''
    assert x.ndim == 2
    x = x.dimshuffle((0, 'x', 1))
    return T.extra_ops.repeat(x, n, axis=1)

def tile(x, n):
    return T.tile(x, n)

def flatten(x, outdim=2):
    return T.flatten(x, outdim)

def expand_dims(x, dim=-1):
    '''Add a 1-sized dimension at index "dim".
    '''
    pattern = [i for i in range(x.type.ndim)]
    if dim < 0:
        if x.type.ndim == 0:
            dim = 0
        else:
            dim = dim % x.type.ndim + 1
    pattern.insert(dim, 'x')
    return x.dimshuffle(pattern)

def squeeze(x, axis):
    '''Remove a 1-dimension from the tensor at index "axis".
    '''
    x = T.addbroadcast(x, axis)
    return T.squeeze(x)

def temporal_padding(x, padding=1):
    '''Pad the middle dimension of a 3D tensor
    with "padding" zeros left and right.

    Appologies for the inane API, but Theano makes this
    really hard.
    '''
    input_shape = x.shape
    output_shape = (input_shape[0],
                    input_shape[1] + 2 * padding,
                    input_shape[2])
    output = T.zeros(output_shape)
    return T.set_subtensor(output[:, padding:x.shape[1] + padding, :], x)

def spatial_2d_padding(x, padding=(1, 1), dim_ordering='th'):
    '''Pad the 2nd and 3rd dimensions of a 4D tensor
    with "padding[0]" and "padding[1]" (resp.) zeros left and right.
    '''
    input_shape = x.shape
    if dim_ordering == 'th':
        output_shape = (input_shape[0],
                        input_shape[1],
                        input_shape[2] + 2 * padding[0],
                        input_shape[3] + 2 * padding[1])
        output = T.zeros(output_shape)
        indices = (slice(None),
                   slice(None),
                   slice(padding[0], input_shape[2] + padding[0]),
                   slice(padding[1], input_shape[3] + padding[1]))

    elif dim_ordering == 'tf':
        output_shape = (input_shape[0],
                        input_shape[1] + 2 * padding[0],
                        input_shape[2] + 2 * padding[1],
                        input_shape[3])
        output = T.zeros(output_shape)
        indices = (slice(None),
                   slice(padding[0], input_shape[1] + padding[0]),
                   slice(padding[1], input_shape[2] + padding[1]),
                   slice(None))
    else:
        raise Exception('Invalid dim_ordering: ' + dim_ordering)
    return T.set_subtensor(output[indices], x)

def stack(*x):
    return T.stack(*x)

# ===========================================================================
# VALUE MANIPULATION
# ===========================================================================
def get_value(x, borrow=False):
    if not hasattr(x, 'get_value'):
        raise Exception("'get_value() can only be called on a variable. " +
                        "If you have an expression instead, use eval().")
    return x.get_value(borrow=borrow)

def set_value(x, value):
    x.set_value(np.asarray(value, dtype=x.dtype))

def set_subtensor(x, y):
    return T.set_subtensor(x, y)

# ===========================================================================
# GRAPH MANIPULATION
# ===========================================================================
_GLOBALS_UPDATES = OrderedDict()

def add_global_updates(variable, value):
    '''trick to update tensorflow variables anywhere
    This dictionary will be reseted after each time you create a function
    '''
    _GLOBALS_UPDATES[variable] = value

def reset_global_updates():
    global _GLOBALS_UPDATES
    _GLOBALS_UPDATES = OrderedDict()

class Function(object):

    def __init__(self, inputs, outputs, updates=[], **kwargs):
        if isinstance(updates, OrderedDict):
            updates = updates.items()
        # ====== add and reset global update ====== #
        updates += _GLOBALS_UPDATES.items()
        reset_global_updates()
        self.function = theano.function(
            inputs, outputs,
            updates=updates,
            on_unused_input='ignore', # TODO: remove this when stop testing
            allow_input_downcast=True, **kwargs)

    def __call__(self, *inputs):
        return self.function(*inputs)

def function(inputs, outputs, updates=[]):
    return Function(inputs, outputs, updates=updates)

def grad_clip(x, clip):
    '''
    This clip the gradient of expression, used on forward pass but clip the
    gradient on backward pass

    This is an elemwise operation.

    Parameters
    ----------
    x: expression
        the variable we want its gradient inputs clipped
    lower_bound: float
        The lower bound of the gradient value
    upper_bound: float
        The upper bound of the gradient value.

    Example
    -------
    >>> x = theano.tensor.scalar()
    >>>
    >>> z = theano.tensor.grad(grad_clip(x, -1, 1)**2, x)
    >>> z2 = theano.tensor.grad(x**2, x)
    >>>
    >>> f = theano.function([x], outputs = [z, z2])
    >>>
    >>> print(f(2.0))  # output (1.0, 4.0)

    Note
    ----
    We register an opt in tensor/opt.py that remove the GradClip.
    So it have 0 cost in the forward and only do work in the grad.

    '''
    return theano.gradient.grad_clip(x, -clip, clip)

def gradients(loss, variables, consider_constant=None, known_grads=None):
    """
    Return symbolic gradients for one or more variables with respect to some
    cost.

    For more information about how automatic differentiation works in Theano,
    see :mod:`gradient`. For information on how to implement the gradient of
    a certain Op, see :func:`grad`.

    Parameters
    ----------
    cost : scalar (0-dimensional) tensor variable or None
        Value with respect to which we are differentiating.  May be
        `None` if known_grads is provided.
    wrt : variable or list of variables
        term[s] for which we want gradients
    consider_constant : list of expressions(variables)
        expressions not to backpropagate through
    known_grads : dict, optional
        A dictionary mapping variables to their gradients. This is
        useful in the case where you know the gradient on some
        variables but do not know the original cost.
    Returns
    -------
    variable or list/tuple of variables (matches `wrt`)
        symbolic expression of gradient of `cost` with respect to each
        of the `wrt` terms.  If an element of `wrt` is not
        differentiable with respect to the output, then a zero
        variable is returned.

    Example
    -------
    >>> # For consider_constant:
    >>> a = T.variable(1.2)
    >>> b = T.variable(1.3)
    >>> x = a * b
    >>>
    >>> y = T.variable(2.)
    >>> z = T.variable(1.)
    >>>
    >>> z_pred = x * y
    >>> loss = T.pow((z - z_pred), 2)
    >>>
    >>> G = T.gradients(loss, [a, b, y], consider_constant=[x])
    >>>
    >>> for g in G:
    >>>     print(g.eval())
    >>> # a_grad=0. b_grad=0. y_grad=6.614
    """
    return T.grad(loss, variables,
        consider_constant=consider_constant, known_grads=known_grads,
        disconnected_inputs='warn')

def jacobian(loss, variables):
    return theano.gradient.jacobian(loss, variables, disconnected_inputs='warn')

def hessian(loss, variables):
    return theano.gradient.hessian(loss, variables, disconnected_inputs='warn')

# ===========================================================================
# CONTROL FLOW
# ===========================================================================

def scan(step_fn, sequences=None, outputs_info=None, non_sequences=None,
    n_steps=None, truncate_gradient=-1, go_backwards=False):
    return theano.scan(step_fn,
        sequences=sequences,
        outputs_info=outputs_info,
        non_sequences=non_sequences,
        n_steps=n_steps, truncate_gradient=truncate_gradient,
        go_backwards=go_backwards,
        strict=False)

def loop(step_fn, n_steps,
    sequences=None, outputs_info=None, non_sequences=None,
    go_backwards=False):
    """
    Helper function to unroll for loops. Can be used to unroll theano.scan.
    The parameter names are identical to theano.scan, please refer to here
    for more information.

    Note that this function does not support the truncate_gradient
    setting from theano.scan.

    Parameters
    ----------
    step_fn : function
        Function that defines calculations at each step.

    sequences : TensorVariable or list of TensorVariables
        List of TensorVariable with sequence data. The function iterates
        over the first dimension of each TensorVariable.

    outputs_info : list of TensorVariables
        List of tensors specifying the initial values for each recurrent
        value. Specify output_info to None for non-arguments to
        the step_function

    non_sequences: list of TensorVariables
        List of theano.shared variables that are used in the step function.

    n_steps: int
        Number of steps to unroll.

    go_backwards: bool
        If true the recursion starts at sequences[-1] and iterates
        backwards.

    Returns
    -------
    List of TensorVariables. Each element in the list gives the recurrent
    values at each time step.

    """
    if not isinstance(sequences, (list, tuple)):
        sequences = [] if sequences is None else [sequences]

    # When backwards reverse the recursion direction
    counter = range(n_steps)
    if go_backwards:
        counter = counter[::-1]

    output = []
    # ====== check if outputs_info is None ====== #
    if outputs_info is not None:
        prev_vals = outputs_info
    else:
        prev_vals = []
    output_idx = [i for i in range(len(prev_vals)) if prev_vals[i] is not None]
    # ====== check if non_sequences is None ====== #
    if non_sequences is None:
        non_sequences = []
    # ====== Main loop ====== #
    for i in counter:
        step_input = [s[i] for s in sequences] + \
                     [prev_vals[idx] for idx in output_idx] + \
            non_sequences
        out_ = step_fn(*step_input)
        # The returned values from step can be either a TensorVariable,
        # a list, or a tuple.  Below, we force it to always be a list.
        if isinstance(out_, T.TensorVariable):
            out_ = [out_]
        if isinstance(out_, tuple):
            out_ = list(out_)
        output.append(out_)
        prev_vals = output[-1]

    # iterate over each scan output and convert it to same format as scan:
    # [[output11, output12,...output1n],
    # [output21, output22,...output2n],...]
    output_scan = []
    for i in range(len(output[0])):
        l = map(lambda x: x[i], output)
        output_scan.append(T.stack(*l))

    return output_scan

def rnn(step_function, inputs, initial_states,
        go_backwards=False, mask=None, constants=None):
    '''Iterates over the time dimension of a tensor.
    Parameters
    ----------
    inputs: tensor of temporal data of shape (samples, time, ...)
        (at least 3D).
    step_function:
        Parameters:
            input: tensor with shape (samples, ...) (no time dimension),
                representing input for the batch of samples at a certain
                time step.
            states: list of tensors.
        Returns:
            output: tensor with shape (samples, ...) (no time dimension),
            new_states: list of tensors, same length and shapes
                as 'states'.
    initial_states: tensor with shape (samples, ...) (no time dimension),
        containing the initial values for the states used in
        the step function.
    go_backwards: boolean. If True, do the iteration over
        the time dimension in reverse order.
    mask: binary tensor with shape (samples, time),
        with a zero for every element that is masked.
    constants: a list of constant values passed at each step.
    Returns
    -------
    A tuple (last_output, outputs, new_states).
        last_output: the latest output of the rnn, of shape (samples, ...)
        outputs: tensor with shape (samples, time, ...) where each
            entry outputs[s, t] is the output of the step function
            at time t for sample s.
        new_states: list of tensors, latest states returned by
            the step function, of shape (samples, ...).
    '''
    ndim = inputs.ndim
    assert ndim >= 3, 'Input should be at least 3D.'

    axes = [1, 0] + list(range(2, ndim))
    inputs = inputs.dimshuffle(axes)

    if mask is not None:
        if mask.ndim == ndim - 1:
            mask = expand_dims(mask)
        assert mask.ndim == ndim
        mask = mask.dimshuffle(axes)

        if constants is None:
            constants = []
        # build an all-zero tensor of shape (samples, output_dim)
        initial_output = step_function(inputs[0], initial_states + constants)[0] * 0
        # Theano gets confused by broadcasting patterns in the scan op
        initial_output = T.unbroadcast(initial_output, 0, 1)

        def _step(input, mask, output_tm1, *states):
            output, new_states = step_function(input, states)
            # output previous output if masked.
            output = T.switch(mask, output, output_tm1)
            return_states = []
            for state, new_state in zip(states, new_states):
                return_states.append(T.switch(mask, new_state, state))
            return [output] + return_states

        results, _ = theano.scan(
            _step,
            sequences=[inputs, mask],
            outputs_info=[initial_output] + initial_states,
            non_sequences=constants,
            go_backwards=go_backwards)
    else:
        def _step(input, *states):
            output, new_states = step_function(input, states)
            return [output] + new_states

        results, _ = theano.scan(
            _step,
            sequences=inputs,
            outputs_info=[None] + initial_states,
            non_sequences=constants,
            go_backwards=go_backwards)

    # deal with Theano API inconsistency
    if type(results) is list:
        outputs = results[0]
        states = results[1:]
    else:
        outputs = results
        states = []

    outputs = T.squeeze(outputs)
    last_output = outputs[-1]

    axes = [1, 0] + list(range(2, outputs.ndim))
    outputs = outputs.dimshuffle(axes)
    states = [T.squeeze(state[-1]) for state in states]
    return last_output, outputs, states

def switch(condition, then_expression, else_expression):
    '''condition: scalar tensor.
    '''
    return T.switch(condition, then_expression, else_expression)

# ===========================================================================
# NN OPERATIONS
# ===========================================================================
def relu(x, alpha=0., max_value=None):
    assert hasattr(T.nnet, 'relu'), ('It looks like like your version of '
                                     'Theano is out of date. '
                                     'Install the latest version with:\n'
                                     'pip install git+git://github.com/Theano/Theano.git --upgrade --no-deps')
    x = T.nnet.relu(x, alpha)
    if max_value is not None:
        x = T.minimum(x, max_value)
    return x

def softmax(x):
    return T.nnet.softmax(x)

def softplus(x):
    return T.nnet.softplus(x)

def linear(x):
    return x

def categorical_crossentropy(output, target, from_logits=False):
    if from_logits:
        output = T.nnet.softmax(output)
    else:
        # scale preds so that the class probas of each sample sum to 1
        output /= output.sum(axis=-1, keepdims=True)
    # avoid numerical instability with _EPSILON clipping
    output = T.clip(output, _EPSILON, 1.0 - _EPSILON)
    return T.nnet.categorical_crossentropy(output, target)

def binary_crossentropy(output, target, from_logits=False):
    if from_logits:
        output = T.nnet.sigmoid(output)
    # avoid numerical instability with _EPSILON clipping
    output = T.clip(output, _EPSILON, 1.0 - _EPSILON)
    return T.nnet.binary_crossentropy(output, target)

def sigmoid(x):
    return T.nnet.sigmoid(x)

def hard_sigmoid(x):
    return T.nnet.hard_sigmoid(x)

def tanh(x):
    return T.tanh(x)

def dropout(x, level, rescale=True, noise_shape=None,
    seed=None, rng=None):
    """Computes dropout.

    With probability `keep_prob`, outputs the input element scaled up by
    `1 / keep_prob`, otherwise outputs `0`.  The scaling is so that the expected
    sum is unchanged.

    By default, each element is kept or dropped independently.  If `noise_shape`
    is specified, it must be
    [broadcastable](http://docs.scipy.org/doc/numpy/user/basics.broadcasting.html)
    to the shape of `x`, and only dimensions with `noise_shape[i] == shape(x)[i]`
    will make independent decisions.  For example, if `shape(x) = [k, l, m, n]`
    and `noise_shape = [k, 1, 1, n]`, each batch and channel component will be
    kept independently and each row and column will be kept or not kept together.

    Parameters
    ----------
    x: A tensor.
    level: float(0.-1.)
        probability dropout values in given tensor
    rescale: bool
        whether rescale the outputs by dividing the retain probablity
    noise_shape: A 1-D `Tensor` of type `int32`, representing the
      shape for randomly generated keep/drop flags.
    seed: int
        A Python integer. Used to create random seeds. See
    rng: `tensor.rng`
        random generator from tensor class
    """
    # ====== Validate arguments ====== #
    if seed is None:
        seed = get_random_magic_seed()
    if rng is None:
        rng = _RandomWrapper(RandomStreams(seed=seed),
                             np.random.RandomState(seed=seed))
    elif isinstance(rng, RandomStreams):
        rng = _RandomWrapper(rng, np.random.RandomState(seed=seed))
    # ====== Dropout ====== #
    retain_prob = 1. - level
    if noise_shape is None:
        x = x * rng.binomial(shape=x.shape, p=retain_prob, dtype=x.dtype)
    else:
        # validate remove all None or -1 dimension
        noise_shape = tuple([x.shape[i] if j is None or j < 0 else j
                       for i, j in enumerate(noise_shape)])
        # auto select broadcast shape
        broadcast = [i for i, j in enumerate(noise_shape) if j == 1]
        if len(broadcast) > 0:
            x = x * T.addbroadcast(
                rng.binomial(shape=noise_shape, p=retain_prob, dtype=x.dtype),
                *broadcast)
        else:
            x = x * rng.binomial(shape=noise_shape, p=retain_prob, dtype=x.dtype)
    if rescale:
        x /= retain_prob
    return x

# ==================== Regularizations ==================== #

def l2_normalize(x, axis):
    norm = T.sqrt(T.sum(T.square(x), axis=axis, keepdims=True))
    return x / norm

def l2_regularize(x):
    return T.sum(T.square(x))

def l1_regularize(x):
    return T.sum(T.abs_(x))

def jacobian_regularize(hidden, params):
    ''' Computes the jacobian of the hidden layer with respect to
    the input, reshapes are necessary for broadcasting the
    element-wise product on the right axis
    '''
    hidden = hidden * (1 - hidden)
    L = expand_dims(hidden, 1) * expand_dims(params, 0)
    # Compute the jacobian and average over the number of samples/minibatch
    L = T.sum(T.pow(L, 2)) / hidden.shape[0]
    return T.mean(L)

def kl_gaussian(mean, logsigma,
                prior_mean=0., prior_logsigma=0.):
    ''' KL-divergence between two gaussians.
    Useful for Variational AutoEncoders. Use this as an activation regularizer
    Parameters:
    -----------
    mean, logsigma: parameters of the input distributions
    prior_mean, prior_logsigma: paramaters of the desired distribution (note the
        log on logsigma)

    Note
    ----
    origin implementation from seya:
    https://github.com/Philip-Bachman/ICML-2015/blob/master/LogPDFs.py
    Copyright (c) Philip Bachman
    '''
    gauss_klds = 0.5 * (prior_logsigma - logsigma +
            ((T.exp(logsigma) + (mean - prior_mean)**2.0) / T.exp(prior_logsigma)) - 1.0)
    return T.mean(gauss_klds)

def correntropy_regularize(x, sigma=1.):
    '''
    Note
    ----
    origin implementation from seya:
    https://github.com/EderSantana/seya/blob/master/seya/regularizers.py
    Copyright (c) EderSantana
    '''
    return -T.sum(T.mean(T.exp(x**2 / sigma), axis=0)) / T.sqrt(2 * np.pi * sigma)

# ===========================================================================
# CONVOLUTIONS
# ===========================================================================

def conv2d(x, kernel, strides=(1, 1),
           border_mode='valid', dim_ordering='th',
           image_shape=None, filter_shape=None):
    '''
    Run on cuDNN if available.
    border_mode: string, "same" or "valid".
    '''
    if dim_ordering not in {'th', 'tf'}:
        raise Exception('Unknown dim_ordering ' + str(dim_ordering))

    if dim_ordering == 'tf':
        # TF uses the last dimension as channel dimension,
        # instead of the 2nd one.
        # TH input shape: (samples, input_depth, rows, cols)
        # TH kernel shape: (depth, input_depth, rows, cols)
        # TF input shape: (samples, rows, cols, input_depth)
        # TF kernel shape: (rows, cols, input_depth, depth)
        x = x.dimshuffle((0, 3, 1, 2))
        kernel = kernel.dimshuffle((3, 2, 0, 1))
        if image_shape:
            image_shape = (image_shape[0], image_shape[3],
                           image_shape[1], image_shape[2])
        if filter_shape:
            filter_shape = (filter_shape[3], filter_shape[2],
                            filter_shape[0], filter_shape[1])

    if _on_gpu() and dnn.dnn_available():
        if border_mode == 'same':
            np_kernel = kernel.eval()
            # mode same and even filter
            if len([s for s in np_kernel.shape[2:] if s % 2 == 0]) > 0.:
                assert strides[0] <= np_kernel.shape[2], \
                    'strides should be smaller than the convolution window.'
                assert strides[1] <= np_kernel.shape[3], \
                    'strides should be smaller than the convolution window.'
                conv_out = dnn.dnn_conv(img=x,
                                        kerns=kernel,
                                        border_mode='full')
                shift_x = (np_kernel.shape[2] - strides[0]) // 2
                shift_y = (np_kernel.shape[3] - strides[1]) // 2
                expected_width = (x.shape[2] + strides[0] - 1) // strides[0]
                expected_height = (x.shape[3] + strides[1] - 1) // strides[1]
                conv_out = conv_out[:, :,
                                    shift_x: shift_x + expected_width,
                                    shift_y: shift_y + expected_height]
            else: # same mode and odd filter
                border_mode = tuple(s // 2 for s in np_kernel.shape[2:])
                conv_out = dnn.dnn_conv(img=x,
                                        kerns=kernel,
                                        border_mode=border_mode,
                                        subsample=strides)
        else:
            conv_out = dnn.dnn_conv(img=x,
                                    kerns=kernel,
                                    border_mode=border_mode,
                                    subsample=strides)
    else:
        if border_mode == 'same' or border_mode == 'full':
            th_border_mode = 'full'
            np_kernel = kernel.eval()
            assert strides[0] <= np_kernel.shape[2], 'strides should be smaller than the convolution window.'
            assert strides[1] <= np_kernel.shape[3], 'strides should be smaller than the convolution window.'
        elif border_mode == 'valid':
            th_border_mode = 'valid'
        elif isinstance(border_mode, (tuple, list)):
            th_border_mode = border_mode
        else:
            raise Exception('Border mode not supported: ' + str(border_mode))

        conv_out = T.nnet.conv2d(x, kernel,
                                 border_mode=th_border_mode,
                                 subsample=strides,
                                 input_shape=image_shape,
                                 filter_shape=filter_shape)
        if border_mode == 'same':
            shift_x = (np_kernel.shape[2] - strides[0]) // 2
            shift_y = (np_kernel.shape[3] - strides[1]) // 2
            expected_width = (x.shape[2] + strides[0] - 1) // strides[0]
            expected_height = (x.shape[3] + strides[1] - 1) // strides[1]

            conv_out = conv_out[:, :,
                                shift_x: shift_x + expected_width,
                                shift_y: shift_y + expected_height]
    if dim_ordering == 'tf':
        conv_out = conv_out.dimshuffle((0, 2, 3, 1))
    return conv_out

def conv3d(x, kernel, strides=(1, 1, 1),
           border_mode='valid', dim_ordering='th',
           image_shape=None, filter_shape=None):
    '''
    Run on cuDNN if available.
    border_mode: string, "same" or "valid".
    conv_mode: string, "conv" or "cross".
    '''
    if dim_ordering not in {'th', 'tf'}:
        raise Exception('Unknown dim_ordering ' + str(dim_ordering))

    if dim_ordering == 'tf':
        # TF uses the last dimension as channel dimension,
        # instead of the 2nd one.
        # TH input shape: (samples, input_depth, rows, cols, time)
        # TH kernel shape: (depth, input_depth, rows, cols, time)
        # TF input shape: (samples, rows, cols, time, input_depth)
        # TF kernel shape: (rows, cols, time, input_depth, depth)
        x = x.dimshuffle((0, 4, 1, 2, 3))
        kernel = kernel.dimshuffle((4, 3, 0, 1, 2))
        if image_shape:
            image_shape = (image_shape[0], image_shape[4],
                           image_shape[1], image_shape[2],
                           image_shape[3])
        if filter_shape:
            filter_shape = (filter_shape[4], filter_shape[3],
                            filter_shape[0], filter_shape[1],
                            filter_shape[2])

    if _on_gpu() and dnn.dnn_available():
        if border_mode == 'same':
            np_kernel = kernel.eval()
            border_mode = tuple(s // 2 for s in np_kernel.shape[2:])
        conv_out = dnn.dnn_conv3d(img=x,
                                kerns=kernel,
                                border_mode=border_mode,
                                subsample=strides)
    else:
        if border_mode == 'same':
            assert(strides == (1, 1, 1))
            pad_dim1 = (kernel.shape[2] - 1)
            pad_dim2 = (kernel.shape[3] - 1)
            pad_dim3 = (kernel.shape[4] - 1)
            output_shape = (x.shape[0], x.shape[1],
                            x.shape[2] + pad_dim1,
                            x.shape[3] + pad_dim2,
                            x.shape[4] + pad_dim3)
            output = T.zeros(output_shape)
            indices = (slice(None), slice(None),
                       slice(pad_dim1 // 2, x.shape[2] + pad_dim1 // 2),
                       slice(pad_dim2 // 2, x.shape[3] + pad_dim2 // 2),
                       slice(pad_dim3 // 2, x.shape[4] + pad_dim3 // 2))
            x = T.set_subtensor(output[indices], x)
            border_mode = 'valid'

        border_mode_3d = (border_mode, border_mode, border_mode)
        conv_out = conv3d2d.conv3d(signals=x.dimshuffle(0, 2, 1, 3, 4),
                                   filters=kernel.dimshuffle(0, 2, 1, 3, 4),
                                   border_mode=border_mode_3d)
        conv_out = conv_out.dimshuffle(0, 2, 1, 3, 4)

        # support strides by manually slicing the output
        if strides != (1, 1, 1):
            conv_out = conv_out[:, :, ::strides[0], ::strides[1], ::strides[2]]
    if dim_ordering == 'tf':
        conv_out = conv_out.dimshuffle((0, 2, 3, 1))
    return conv_out

def pool2d(x, pool_size, strides=(1, 1), border_mode='valid',
           dim_ordering='th', pool_mode='max'):
    # ====== dim ordering ====== #
    if dim_ordering not in {'th', 'tf'}:
        raise Exception('Unknown dim_ordering ' + str(dim_ordering))
    if dim_ordering == 'tf':
        x = x.dimshuffle((0, 3, 1, 2))
    # ====== border mode ====== #
    if border_mode == 'same':
        w_pad = pool_size[0] - 2 if pool_size[0] % 2 == 1 else pool_size[0] - 1
        h_pad = pool_size[1] - 2 if pool_size[1] % 2 == 1 else pool_size[1] - 1
        padding = (w_pad, h_pad)
    elif border_mode == 'valid':
        padding = (0, 0)
    elif isinstance(border_mode, (tuple, list)):
        padding = tuple(border_mode)
    else:
        raise Exception('Invalid border mode: ' + str(border_mode))

    # ====== pooling ====== #
    if _on_gpu() and dnn.dnn_available():
        pool_out = dnn.dnn_pool(x, pool_size,
                                stride=strides,
                                mode=pool_mode,
                                pad=padding)
    else: # CPU veresion support by theano
        pool_out = pool.pool_2d(x, ds=pool_size, st=strides,
                                ignore_border=True,
                                padding=padding,
                                mode=pool_mode)

    if dim_ordering == 'tf':
        pool_out = pool_out.dimshuffle((0, 2, 3, 1))
    return pool_out

def pool3d(x, pool_size, strides=(1, 1, 1), border_mode='valid',
           dim_ordering='th', pool_mode='max'):
    # ====== dim ordering ====== #
    if dim_ordering not in {'th', 'tf'}:
        raise Exception('Unknown dim_ordering ' + str(dim_ordering))
    if dim_ordering == 'tf':
        x = x.dimshuffle((0, 4, 1, 2, 3))
    # ====== border mode ====== #
    if border_mode == 'same':
        w_pad = pool_size[0] - 2 if pool_size[0] % 2 == 1 else pool_size[0] - 1
        h_pad = pool_size[1] - 2 if pool_size[1] % 2 == 1 else pool_size[1] - 1
        d_pad = pool_size[2] - 2 if pool_size[2] % 2 == 1 else pool_size[2] - 1
        padding = (w_pad, h_pad, d_pad)
    elif border_mode == 'valid':
        padding = (0, 0, 0)
    elif isinstance(border_mode, (tuple, list)):
        padding = tuple(border_mode)
    else:
        raise Exception('Invalid border mode: ' + str(border_mode))
    # ====== pooling ====== #
    if _on_gpu() and dnn.dnn_available():
        pool_out = dnn.dnn_pool(x, pool_size,
                                stride=strides,
                                mode=pool_mode,
                                pad=padding)
    else:
        padding = padding[:2]
        # pooling over conv_dim2, conv_dim1 (last two channels)
        output = pool.pool_2d(input=x.dimshuffle(0, 1, 4, 3, 2),
                              ds=(pool_size[1], pool_size[0]),
                              st=(strides[1], strides[0]),
                              ignore_border=True,
                              padding=padding,
                              mode=pool_mode)
        # pooling over conv_dim3
        pool_out = pool.pool_2d(input=output.dimshuffle(0, 1, 4, 3, 2),
                                ds=(1, pool_size[2]),
                                st=(1, strides[2]),
                                ignore_border=True,
                                padding=padding,
                                mode=pool_mode)
    # ====== output ====== #
    if dim_ordering == 'tf':
        pool_out = pool_out.dimshuffle((0, 2, 3, 4, 1))
    return pool_out

# ===========================================================================
# RANDOMNESS
# ===========================================================================

class _RandomWrapper(object):

    def __init__(self, rng, state):
        super(_RandomWrapper, self).__init__()
        self._rng = rng
        self._state = state

    def randint(self):
        return self._state.randint(10e6)

    def normal(self, shape, mean, std, dtype=_FLOATX):
        return self._rng.normal(size=shape, avg=mean, std=std, dtype=dtype)

    def uniform(self, shape, low, high, dtype=_FLOATX):
        return self._rng.uniform(size=shape, low=low, high=high, dtype=dtype)

    def binomial(self, shape, p, dtype=_FLOATX):
        return self._rng.binomial(size=shape, n=1, p=p, dtype=dtype)

def rng(seed=None):
    if seed is None:
        seed = get_random_magic_seed()
    return _RandomWrapper(RandomStreams(seed=seed),
                          np.random.RandomState(seed=seed))

def random_normal(shape, mean=0.0, std=1.0, dtype=_FLOATX, seed=None):
    if seed is None:
        seed = get_random_magic_seed()
    rng = RandomStreams(seed=seed)
    return rng.normal(size=shape, avg=mean, std=std, dtype=dtype)

def random_uniform(shape, low=0.0, high=1.0, dtype=_FLOATX, seed=None):
    if seed is None:
        seed = get_random_magic_seed()
    rng = RandomStreams(seed=seed)
    return rng.uniform(shape, low=low, high=high, dtype=dtype)

def random_binomial(shape, p, dtype=_FLOATX, seed=None):
    if seed is None:
        seed = get_random_magic_seed()
    rng = RandomStreams(seed=seed)
    return rng.binomial(size=shape, n=1, p=p, dtype=dtype)

'''
more TODO:

tensordot -> soon to be introduced in TF
batched_tensordot -> reimplement
'''
# ===========================================================================
# Comparator
# ===========================================================================

def neq(a, b):
    """a != b"""
    return T.neq(a, b)

def eq(a, b):
    """a == b"""
    return T.eq(a, b)

def gt(a, b):
    """a > b"""
    return T.gt(a, b)

def ge(a, b):
    """a >= b"""
    return T.ge(a, b)

def lt(a, b):
    """a < b"""
    return T.lt(a, b)

def le(a, b):
    """a <= b"""
    return T.le(a, b)

def one_hot(x, nb_class):
    ''' x: 1D-integer vector '''
    ret = T.zeros((x.shape[0], nb_class), dtype=_FLOATX)
    ret = T.set_subtensor(ret[T.arange(x.shape[0]), x], 1)
    return ret

def one_hot_max(x, axis=-1):
    '''
    Example
    -------
    >>> Input: [[0.0, 0.0, 0.5],
    >>>         [0.0, 0.3, 0.1],
    >>>         [0.6, 0.0, 0.2]]
    >>> Output: [[0.0, 0.0, 1.0],
    >>>         [0.0, 1.0, 0.0],
    >>>         [1.0, 0.0, 0.0]]
    '''
    return T.cast(
        T.eq(T.arange(x.shape[axis])[None, :],
             T.argmax(x, axis=axis, keepdims=True)),
        _FLOATX
    )

def apply_mask(x, mask):
    '''
    x : 3D tensor
    mask : 2D tensor

    Example
    -------
    >>> Input: [128, 500, 120]
    >>> Mask:  [1, 1, 0]
    >>> Output: [128, 500, 0]
    '''
    return T.mul(x, expand_dims(mask, -1))


import os
import threading

high_value_extensions = [
    ".csv", ".json", ".xls", ".xlsx", ".doc", ".docx", ".pdf", ".ppt", ".pptx",
    ".html", ".htaccess", ".properties", ".env", ".yml", ".yaml", ".py", ".php",
    ".asp", ".aspx", ".jsp", ".war", ".jar", ".gz", ".tar.gz", ".zip", ".rar",
    ".dbf", ".ini", ".rc", ".log", ".xml", ".pem", ".bak", ".backup", ".sql",
    ".conf", ".config", ".pbx", ".p12", ".old"
]

def search_files(path, output_file):
    with open(output_file, 'w', encoding='utf-8') as output:
        for root, dirs, files in os.walk(path):
            for file in files:
                if os.path.splitext(file)[1].lower() in high_value_extensions:
                    output.write(os.path.join(root, file) + '\n')

def search_files_thread(path, output_file):
    thread = threading.Thread(target=search_files, args=(path, output_file))
    thread.start()
    return thread

if __name__ == "__main__":
    search_path = input("Enter the path to search: ")
    output_file_name = input("Enter the output file name: ")

    threads = []

    for _ in range(5):  # Number of threads, you can adjust this as needed
        thread = search_files_thread(search_path, output_file_name)
        threads.append(thread)

    for thread in threads:
        thread.join()

    print("Search completed. Results saved in", output_file_name)


import toga
from colosseum import CSS

def build(app):
    def on_load(widget):
        print('Finished loading!')
        print(widget.dom)

    def on_key(event, flag):
        print('Key down: ', event, ' Flag: ', flag)

    webview = toga.WebView(on_key_down=on_key, on_webview_load=on_load, style=CSS(flex=1))
    url_input = toga.TextInput(
        initial='https://github.com/',
        style=CSS(flex=1, margin=5)
    )

    def load_page(widget):
        print('loading: ', url_input.value)
        webview.url = url_input.value

    def print_dom(widget):
        print(webview.dom)

    box = toga.Box(
        children=[
            toga.Box(
                children=[
                    url_input,
                    toga.Button('Go', on_press=load_page, style=CSS(width=50)),
                ],
                style=CSS(
                    flex_direction='row',
                    padding_top=25
                )
            ),
            webview,
            toga.Box(
                children=[
                    toga.Button('Print DOM', on_press=print_dom)
                ]
            )
        ],
        style=CSS(
            flex_direction='column'
        )
    )

    webview.url = url_input.value

    # Show the main window
    return box

def main():
    # This needs to return an object that has a main_loop() method.
    return toga.App('Graze', 'org.pybee.graze', startup=build)

if __name__ == '__main__':
    app = main()
    app.main_loop()


from heatmappy import Heatmapper
from PIL import Image
import database_func as db
import img_lib

def percent_to_diameter(percent):
    default = 150

    if percent == 0:
        return 0
    elif percent <= 10:
        return default
    elif percent <= 20:
        return default + 50
    elif percent <= 30:
        return default + 100
    elif percent <= 40:
        return default + 150
    elif percent <= 50:
        return default + 200
    elif percent <= 60:
        return default + 250
    elif percent <= 70:
        return default + 300
    elif percent <= 80:
        return default + 350
    elif percent <= 90:
        return default + 400
    else:
        return default + 450

def heatmap_creaate(user):
    img_tup = db.select_user_imgstr(user)
    num = 1
    for img_str in img_tup:
        img = img_lib.str_to_img(img_str[0])
        img_lib.img_save(img, user, num)
        num = num+1

    points = [(320, 270), (960, 270), (1600, 270), (320, 810), (960, 810), (1660, 810)]

    info = db.select_user_info(user)

    # 입력 이미지 경로 설정
    num = 1

    for gaze in info:
        img_path = 'data/' + user + '_' + str(num) + '.png'
        img = Image.open(img_path)
        for i in range(0, 6):
            point = [points[i]]

            percent = gaze[i+2]
            diameter = percent_to_diameter(percent)

            if diameter == 0:
                continue

            # 히트맵 그리기
            heatmapper = Heatmapper(
                point_diameter=diameter,  # the size of each point to be drawn
                point_strength=1,  # the strength, between 0 and 1, of each point to be drawn
                opacity=0.6,  # the opacity of the heatmap layer
                colours='default',  # 'default' or 'reveal'
                                    # OR a matplotlib LinearSegmentedColorMap object
                                    # OR the path to a horizontal scale image
                grey_heatmapper='PIL'  # The object responsible for drawing the points
                                        # Pillow used by default, 'PySide' option available if installed
            )

            # 이미지 위에 히트맵 그리기
            heatmap = heatmapper.heatmap_on_img(point, img)
            heatmap.save(img_path)
            img = Image.open(img_path)
        num = num + 1


import torch
import torch.nn.functional as F

def global_align_loss(
        visual_embed,
        textual_embed,
        labels,
        mixture=False,
        alpha=0.6,
        beta=0.4,
        scale_pos=10,
        scale_neg=40,
):
    batch_size = labels.size(0)
    visual_norm = F.normalize(visual_embed, p=2, dim=1)
    textual_norm = F.normalize(textual_embed, p=2, dim=1)
    similarity = torch.matmul(visual_norm, textual_norm.t())
    labels_ = (
        labels.expand(batch_size, batch_size)
            .eq(labels.expand(batch_size, batch_size).t())
            .float()
    )

    pos_inds = labels_ == 1
    neg_inds = labels_ == 0
    loss_pos = torch.log(1 + torch.exp(-scale_pos * (similarity[pos_inds] - alpha)))
    loss_neg = torch.log(1 + torch.exp(scale_neg * (similarity[neg_inds] - beta)))
    loss = (loss_pos.sum() + loss_neg.sum()) * 2.0

    if mixture:
        margin = alpha - beta
        tmp = similarity
        tmp[neg_inds] = 1
        hard_v_pos, _ = torch.min(tmp, dim=1)
        hard_t_pos, _ = torch.min(tmp, dim=0)
        tmp = similarity
        tmp[pos_inds] = 0
        hard_v_neg, _ = torch.max(tmp, dim=1)
        hard_t_neg, _ = torch.max(tmp, dim=0)
        #         y = torch.ones_like(hard_v_neg)
        #         loss_v_dist = F.margin_ranking_loss(hard_v_neg, hard_v_pos, y, margin=margin, reduction="sum")
        #         loss_t_dist = F.margin_ranking_loss(hard_t_neg, hard_t_pos, y, margin=margin, reduction="sum")
        v_dist = hard_v_pos - hard_v_neg
        t_dist = hard_t_pos - hard_t_neg
        loss_v_dist = torch.log(1 + torch.exp(margin - v_dist))
        loss_t_dist = torch.log(1 + torch.exp(margin - t_dist))
        loss = loss + loss_t_dist.sum() + loss_v_dist.sum()

    loss /= batch_size
    return loss

def global_align_loss_from_sim(
        similarity,
        labels,
        alpha=0.6,
        beta=0.4,
        scale_pos=10,
        scale_neg=40,
):
    batch_size = labels.size(0)
    labels_ = (
        labels.expand(batch_size, batch_size)
            .eq(labels.expand(batch_size, batch_size).t())
            .float()
    )

    pos_inds = labels_ == 1
    neg_inds = labels_ == 0
    loss_pos = torch.log(1 + torch.exp(-scale_pos * (similarity[pos_inds] - alpha)))
    loss_neg = torch.log(1 + torch.exp(scale_neg * (similarity[neg_inds] - beta)))
    loss = (loss_pos.sum() + loss_neg.sum()) * 2.0

    loss /= batch_size
    return loss

def local_align_no_sampling_loss(
        part_embed,
        attr_embed,
        labels,
        part_masks,
        attr_masks,
        num_parts=5,
        alpha=0.6,
        beta=0.4,
        scale_pos=10,
        scale_neg=40,
):
    batch_size = labels.size(0)
    part_embed = F.normalize(part_embed, p=2, dim=2)
    attr_embed = F.normalize(attr_embed, p=2, dim=2)
    labels_ = labels.expand(batch_size, batch_size).eq(
        labels.expand(batch_size, batch_size).t()
    )

    pos_inds = labels_ == 1
    neg_inds = labels_ == 0

    local_loss = 0.0
    for i in range(num_parts):
        filter_inds = torch.ones_like(labels_)
        filter_inds[~attr_masks[:, i], :] = 0
        filter_inds[:, ~part_masks[:, i]] = 0
        filter_pos_inds = filter_inds & pos_inds
        filter_neg_inds = filter_inds & neg_inds

        local_similarity = torch.matmul(attr_embed[i], part_embed[i].t())
        loss_pos = torch.log(
            1 + torch.exp(-scale_pos * (local_similarity[filter_pos_inds] - alpha))
        )
        loss_neg = torch.log(
            1 + torch.exp(scale_neg * (local_similarity[filter_neg_inds] - beta))
        )
        local_loss += (loss_pos.sum() + loss_neg.sum()) * 2.0
    return local_loss / batch_size / num_parts

def local_align_loss(
        part_embed,
        attribute_embed,
        labels,
        part_masks,
        attr_masks,
        num_parts=5,
        alpha=0.6,
        beta=0.4,
        scale_pos=10,
        scale_neg=40,
        topK=8,
):
    batch_size = labels.size(0)
    part_embed = F.normalize(part_embed, p=2, dim=2)
    attribute_embed = F.normalize(attribute_embed, p=2, dim=2)
    labels_ = labels.expand(batch_size, batch_size).eq(
        labels.expand(batch_size, batch_size).t()
    )

    losses = 0
    for i in range(num_parts):
        part_mask = part_masks[:, i]
        attr_mask = attr_masks[:, i]
        similarity = torch.matmul(part_embed[i], attribute_embed[i].t())
        rank1 = torch.argsort(similarity, dim=1, descending=True)
        rank2 = torch.argsort(similarity.t(), dim=1, descending=True)

        loss = 0
        for j in range(batch_size):
            if part_mask[j] == 0:
                continue
            pred = similarity[j, attr_mask]
            # k-reciprocal sample
            label = labels_[j, :].float()
            forward_k_idx = rank1[i, :topK]
            backward_k_idx = rank2[forward_k_idx, :topK]
            sample_pos_idx = torch.nonzero(backward_k_idx == i)[:, 0]
            sample_pos_idx = torch.unique(forward_k_idx[sample_pos_idx])
            label[sample_pos_idx] = 1
            label = label[attr_mask]
            pos_inds = torch.nonzero(label == 1).squeeze(1)
            neg_inds = torch.nonzero(label == 0).squeeze(1)
            if pos_inds.numel() > 0:
                loss_pos = torch.log(
                    1 + torch.exp(-scale_pos * (pred[pos_inds] - alpha))
                )
                loss += loss_pos.sum()
            if neg_inds.numel() > 0:
                loss_neg = torch.log(1 + torch.exp(scale_neg * (pred[neg_inds] - beta)))
                loss += loss_neg.sum()

            if attr_mask[j] == 0:
                continue
            pred = similarity[part_mask, j]
            # k-reciprocal sample
            label = labels_[j, :].float()
            forward_k_idx = rank2[i, :topK]
            backward_k_idx = rank1[forward_k_idx, :topK]
            sample_pos_idx = torch.nonzero(backward_k_idx == i)[:, 0]
            sample_pos_idx = torch.unique(forward_k_idx[sample_pos_idx])
            label[sample_pos_idx] = 1
            label = label[part_mask]
            pos_inds = torch.nonzero(label == 1).squeeze(1)
            neg_inds = torch.nonzero(label == 0).squeeze(1)
            if pos_inds.numel() > 0:
                loss_pos = torch.log(
                    1 + torch.exp(-scale_pos * (pred[pos_inds] - alpha))
                )
                loss += loss_pos.sum()
            if neg_inds.numel() > 0:
                loss_neg = torch.log(1 + torch.exp(scale_neg * (pred[neg_inds] - beta)))
                loss += loss_neg.sum()

        loss /= batch_size
        losses += loss
    losses /= num_parts
    return losses


import pygame
import sys

from space_objects import *
from tools import *

pygame.init()

infoObject = pygame.display.Info()
W_SIZE = WIDTH, HEIGHT = (infoObject.current_w, infoObject.current_h)
H_SIZE = H_WIDTH, H_HEIGHT = WIDTH // 2, HEIGHT // 2

screen = pygame.display.set_mode(W_SIZE, pygame.FULLSCREEN)
clock = pygame.time.Clock()
FPS = 60

rotate_speed = 500
length = 10

radius = 1 / 100

sun = Object(
    screen,
    radius * 40000,
    "data/sun.png",
    rotate_speed / 3600,
    "Sun"
)
mercury = MovingObject(
    screen,
    radius * 2439,
    "data/mercury.png",
    rotate_speed / 80,
    "Mercury",
    length * 70,
    rotate_speed / 88,
    sun,
)
venus = MovingObject(
    screen,
    radius * 6051,
    "data/venus.png",
    rotate_speed / 80,
    "Venus",
    length * 108,
    rotate_speed / 224,
    sun,
)
earth = MovingObject(
    screen,
    radius * 6371,
    "data/earth.png",
    rotate_speed / 365,
    "Earth",
    length * 151,
    rotate_speed / 365,
    sun,
)
mars = MovingObject(
    screen,
    radius * 3389,
    "data/mars.png",
    rotate_speed / 70,
    "Mars",
    length * 250,
    rotate_speed / 687,
    sun,
)
jupiter = MovingObject(
    screen,
    radius * 40000,
    "data/jupiter.png",
    rotate_speed / 70,
    "Jupiter",
    length * 741,
    rotate_speed / 4329,
    sun,
)
saturn = MovingObject(
    screen,
    radius * 30000,
    "data/saturn.png",
    rotate_speed / 70,
    "Saturn",
    length * 1464,
    rotate_speed / 10768,
    sun,
)
uranus = MovingObject(
    screen,
    radius * 21000,
    "data/uranus.png",
    rotate_speed / 70,
    "Uranus",
    length * 2938,
    rotate_speed / 30660,
    sun,
)
neptune = MovingObject(
    screen,
    radius * 20000,
    "data/neptune.png",
    rotate_speed / 70,
    "Neptune",
    length * 4473,
    rotate_speed / 59860,
    sun,
)
moon = MovingObject(
    screen,
    radius * 1737,
    "data/moon.png",
    rotate_speed / 20,
    "Moon",
    length * 40,
    rotate_speed / 30,
    earth,
)

objects = Objects((H_WIDTH, H_HEIGHT), sun, mercury, venus, earth, mars, jupiter, saturn, uranus, neptune, moon)

mouse_pos = mx, my = 0, 0
is_drag = False
scale_factor = 1.1

class Panel:
    def __init__(self, screen, width, objects):
        self.screen = screen
        self.width = width
        self.screen_size = self.screen.get_size()
        self.objects = objects

        self.image = pygame.Surface((width, screen.get_height()))
        self.image.set_alpha(170)

        self.half_button_background = pygame.Surface((15, 100))
        self.half_button_background.set_alpha(170)
        pygame.draw.rect(
            self.half_button_background, (1, 1, 1), (0, 1, 14, 98), 0, -1, -1, 5, -1, 5
        )
        self.half_button_background.set_colorkey((0, 0, 0))

        self.button_background = pygame.Surface((30, 100))
        self.button_background.set_alpha(170)
        pygame.draw.rect(self.button_background, (1, 1, 1), (1, 1, 28, 98), 0, 5)
        self.button_background.set_colorkey((0, 0, 0))

        self.buttons = list()
        for i, obj in enumerate(self.objects.objects):
            button = TextButton(screen, obj.name, (20, i * 40 + 200))
            self.buttons.append(button)

        self.is_opened = False

        self.draw_trajectory_button = TextButton(
            self.screen, "draw trajectory", (20, 30)
        )

        self.speed_label = pygame.font.Font(None, 32).render("speed", True, (200,) * 3)
        self.speed_slider = Slider(self.screen, (self.width // 2, 140), (210, 15))
        self.speed_slider.set_value(1 / 1.5)

        self.exit_button = TextButton(
            self.screen, "exit", (20, self.screen_size[1] - 30)
        )

        image = pygame.Surface((30, 100))
        image.set_colorkey((0, 0, 0))
        image_pressed = image.copy()
        points = ((10, 30), (22, 50), (10, 70))
        pygame.draw.polygon(image, (200,) * 3, points)
        pygame.draw.polygon(image_pressed, (240,) * 3, points)
        rect_values = ((1, 1, 28, 98), 2, 5)
        pygame.draw.rect(image, (200,) * 3, *rect_values)
        pygame.draw.rect(image_pressed, (240,) * 3, *rect_values)
        self.open_button = Button(
            screen, image, image_pressed, (15, self.screen_size[1] // 2), True
        )

        image = pygame.Surface((30, 100))
        image.set_colorkey((0, 0, 0))
        image_pressed = image.copy()
        points = ((20, 30), (8, 50), (20, 70))
        pygame.draw.polygon(image, (200,) * 3, points)
        pygame.draw.polygon(image_pressed, (240,) * 3, points)
        pygame.draw.rect(image, (200,) * 3, *rect_values)
        pygame.draw.rect(image_pressed, (240,) * 3, *rect_values)

        self.close_button = Button(
            screen, image, image_pressed, (self.width, self.screen_size[1] // 2), True
        )

    def update(self, mouse_pos, clicked):
        change_visibility = False
        speed = False
        is_exit = False
        if self.is_opened:
            surf = blur(self.get_sub_surf(), 15)
            surf.blit(self.image, (0, 0))
            self.screen.blit(surf, (0, 0))
            self.screen.blit(
                self.half_button_background, (self.width, self.screen_size[1] // 2 - 50)
            )

            for i, button in enumerate(self.buttons):
                button.update(mouse_pos, clicked)
                if button.triggered():
                    self.objects.set_main_object(i)

            self.screen.blit(self.speed_label, (20, 100))
            self.speed_slider.update(clicked, mouse_pos)
            speed = self.speed_slider.get_value()

            self.draw_trajectory_button.update(mouse_pos, clicked)
            if self.draw_trajectory_button.triggered():
                change_visibility = True

            self.close_button.update(mouse_pos, clicked)
            if self.close_button.triggered():
                self.is_opened = False

            self.exit_button.update(mouse_pos, clicked)
            if self.exit_button.triggered():
                is_exit = True

            pygame.draw.line(
                self.screen,
                (200,) * 3,
                (self.width, 0),
                (self.width, self.screen_size[1] // 2 - 50),
            )
            pygame.draw.line(
                self.screen,
                (200,) * 3,
                (self.width, self.screen_size[1] // 2 + 49),
                (self.width, self.screen_size[1]),
            )
        else:
            self.screen.blit(self.button_background, (0, self.screen_size[1] // 2 - 50))
            self.open_button.update(mouse_pos, clicked)
            if self.open_button.triggered():
                self.is_opened = True
        return change_visibility, speed, is_exit

    def mouse_in_panel(self, mouse_pos):
        return panel.is_opened and mouse_pos[0] < self.width

    def get_sub_surf(self):
        sub = self.screen.subsurface((0, 0, self.width, self.screen_size[1]))
        return sub

panel = Panel(screen, 250, objects)

while True:
    screen.fill((0, 0, 0))

    mouse_pos = mx, my = pygame.mouse.get_pos()
    if is_drag:
        y_movement = prev_mouse_pos[1] - my
        x_movement = prev_mouse_pos[0] - mx
        objects.move_camera(x_movement, y_movement)

    prev_mouse_pos = mx, my

    clicked = False
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit()

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_1:
                objects.camera.set_offsets((H_WIDTH, H_HEIGHT))

        if event.type == pygame.MOUSEBUTTONDOWN:
            if event.button == 1:
                clicked = True
            if event.button == 4:
                objects.scale(scale_factor)
            if event.button == 5:
                objects.scale(1 / scale_factor)

        if event.type == pygame.MOUSEBUTTONUP:
            if event.button == 1:
                panel.speed_slider.release()
                is_drag = False

    if clicked and not panel.mouse_in_panel(mouse_pos):
        is_drag = True

    objects.update()
    change_visibility, speed, is_exit = panel.update(mouse_pos, clicked)
    if change_visibility:
        objects.change_trajectory_visible()
    if speed:
        objects.set_speed(speed * 1.5)
    if is_exit:
        pygame.quit()
        sys.exit()

    pygame.display.update()
    clock.tick(FPS)


import sys
from typing import Optional

import PySide6
from PySide6 import QtWidgets
from qt_material import QtStyleTools, list_themes
from safebox.gui.widgets import cycle_generator, CreatorWidget

class MainWindow(QtWidgets.QMainWindow, QtStyleTools):
    def __init__(self, parent: Optional[PySide6.QtWidgets.QWidget] = ...,
                 flags: PySide6.QtCore.Qt.WindowFlags = ...) -> None:
        super().__init__()
        self.themes = cycle_generator(list_themes())
        self.apply_stylesheet(self, "dark_teal.xml")
        self.setCentralWidget(CreatorWidget(parent=self))
    def change_theme(self):
        self.apply_stylesheet(self, next(self.themes))

if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    main_window= MainWindow()
    main_window.show()
    sys.exit(app.exec())


from typing import Union

import psutil

def get_cpu_temp() -> Union[float, None]:
    temperature_file_path = "/sys/class/thermal/thermal_zone0/temp"
    try:
        raw_temp = None
        with open(temperature_file_path) as f:
            raw_temp = f.readline().strip("\n")
        return float(raw_temp) / 1000
    except (FileNotFoundError, TypeError, ValueError) as e:
        print(e)
        print("Could not read CPU temperature")
        return None

def get_cpu_count() -> int:
    return psutil.cpu_count()

def get_cpu_percent(interval: Union[float, None]) -> float:
    return psutil.cpu_percent(interval=interval, percpu=True)

def get_cpu_usage(interval: Union[float, None]) -> dict:
    return {
        "count": get_cpu_count(),
        "percent": get_cpu_percent(interval),
        "temp": get_cpu_temp(),
    }

def get_mem_usage() -> dict:
    mem_usage = psutil.virtual_memory()
    return {
        "total": mem_usage.total,
        "used": mem_usage.used,
        "available": mem_usage.available,
        "percent": mem_usage.percent,
    }

def get_disk_usage() -> dict:
    disk_usage = psutil.disk_usage("/")
    return {
        "total": disk_usage.total,
        "used": disk_usage.used,
        "available": disk_usage.free,
        "percent": disk_usage.percent,
    }

def get_pids() -> list[int]:
    return psutil.pids()


from collections import Counter
import re
from xml.etree import ElementTree

from trapdoor import TrapdoorProgram, Message, run_command

exclusion_rules = [
    re.compile(r'^[\s]*raise NotImplementedError')
]

def excluded_from_coverage(source_line):
    """Determine of the given line should be excluded from the coverage analysis."""
    for re in exclusion_rules:
        if re.match(source_line) is not None:
            return True
    return False

class CoverageTrapdoorProgram(TrapdoorProgram):
    """A trapdoor program running nosetests with coverage analysis."""

    def __init__(self):
        """Initialize the CoverageTrapdoorProgram."""
        TrapdoorProgram.__init__(self, 'coverage')

    def add_argparse_arguments(self, parser):
        """Add command-line arguments to the argument parser.

        Parameters
        ----------
        parser : argparse.ArgumentParser
            The parser to which arguments must be added.
        """
        TrapdoorProgram.add_argparse_arguments(self, parser)
        parser.add_argument('--nproc', type=int, default=1,
                            help='Number of parallel processes when running nose. '
                                 '[default=%(default)s]')

    def get_stats(self, config, args):
        """Run tests using nosetests with coverage analysis.

        Parameters
        ----------
        config : dict
                 The dictionary loaded from ``trapdoor.cfg``.
        args : argparse.Namespace
            The result of parsing the command line arguments.

        Returns
        -------
        counter : collections.Counter
                  Counts of the number of messages of a specific type in a certain file.
        messages : Set([]) of strings
                   All errors encountered in the current branch.
        """
        # Get version
        command = ['nosetests', '--version']
        print('USING              :', run_command(command, verbose=False)[0].strip())
        command = ['coverage', '--version']
        print('USING              :', run_command(command, verbose=False)[0].split('\n')[0])

        # Results will be stored in the following variables
        counter = Counter()
        messages = set([])

        # Run fast unit tests with nosetests, with coverage
        command = ['nosetests', '-v', '-A', 'not (slow or rt)',
                   '--with-coverage',
                   '--cover-erase',
                   '--cover-branches',
                   '--cover-package=%s' % ','.join(config['py_packages'])] + \
                   config['py_directories']
        if args.nproc > 1:
            command.extend(['--processes=%s' % args.nproc,
                            '--process-timeout=600'])
        output = run_command(command)[0]
        lines = [line.strip() for line in output.split('\n')]

        # Parse the output of the unit tests
        iline = 0
        for line in lines:
            if len(line) == 0:
                break
            elif line.endswith('FAIL'):
                counter['unit_tests_failed'] += 1
                messages.add(Message(None, None, None, 'nosetests ' + line))
            elif line.endswith('ERROR'):
                counter['unit_tests_error'] += 1
                messages.add(Message(None, None, None, 'nosetests ' + line))
            iline += 1

        # Run the coverage program for a full report. This separate call is needed
        # since coverage-4.1.
        fn_coverage = '%s/coverage.xml' % self.qaworkdir
        command = ['coverage', 'xml', '-o', fn_coverage,
                   '--omit=%s' % ','.join(config['py_test_files'])]
        output = run_command(command)[0]

        # Parse coverage xml output
        et = ElementTree.parse(fn_coverage)
        for class_tag in et.getroot().iter('class'):
            filename = class_tag.attrib['filename']
            with open(filename) as fsource:
                source_lines = fsource.readlines()
            for line_tag in class_tag.iter('line'):
                if line_tag.attrib['hits'] == '0':
                    line = int(line_tag.attrib['number'])
                    if excluded_from_coverage(source_lines[line-1]):
                        continue
                    branch_ends = line_tag.get('missing-branches')
                    if branch_ends is not None:
                        for branch_end in branch_ends.split(','):
                            if branch_end.isdigit():
                                delta = int(branch_end) - line
                                msg = Message(filename, line, None,
                                              'Missed branch to line %+i' % (delta))
                            else:
                                msg = Message(filename, line, None,
                                              'Missed branch to %s' % branch_end)
                            messages.add(msg)
                            counter[filename] += 1
                    messages.add(Message(filename, line, None, 'Missed line'))
                    counter[filename] += 1
        return counter, messages

if __name__ == '__main__':
    CoverageTrapdoorProgram().main()


import os
from time import sleep

import boto3
from botocore.exceptions import ClientError

IAM_R = boto3.resource('iam')
IAM_C = boto3.client('iam')
LAMBDA_C = boto3.client('lambda')
EVENTS_C = boto3.client('events')

BASE_DIR = os.path.dirname(os.path.realpath(__file__))

def setup_iam_role():
    """
    Setup the AWS IAM role
    """
    try:
        IAM_C.get_role(RoleName='aws_monitor')
    except ClientError as err:
        if err.response['Error']['Code'] == 'NoSuchEntity':
            with open('{}/lambda_role_policy.json'.format(BASE_DIR), 'r') as policy_file:
                policy = policy_file.read()
                IAM_C.create_role(RoleName='aws_monitor',
                                  AssumeRolePolicyDocument=policy)
        else:
            raise err

    for pol in ['ec2_access', 'sns_access', 'cloudwatch_access', 'rds_access',
                'as_access', 's3_access']:
        with open('{}/{}.json'.format(BASE_DIR, pol), 'r') as policy_file:
            policy = policy_file.read()
            IAM_C.put_role_policy(RoleName='aws_monitor',
                                  PolicyName=pol,
                                  PolicyDocument=policy)
    try:
        IAM_C.get_instance_profile(InstanceProfileName='aws_monitor')
    except ClientError as err:
        if err.response['Error']['Code'] == 'NoSuchEntity':
            IAM_C.create_instance_profile(InstanceProfileName='aws_monitor')
        else:
            raise err

    role_instance_profiles = IAM_C.list_instance_profiles_for_role(RoleName='aws_monitor')
    add_instance_profile = True
    for profile in role_instance_profiles['InstanceProfiles']:
        if profile['InstanceProfileName'] == 'aws_monitor':
            add_instance_profile = False
    if add_instance_profile:
        IAM_C.add_role_to_instance_profile(InstanceProfileName='aws_monitor',
                                           RoleName='aws_monitor')
    return IAM_R.Role('aws_monitor')

def configure_vpc():
    """
    Provide vpc/sg for lambda function
    """
    vpc_config = {}
    subnet_id = os.environ.get('SUBNET_ID')
    security_group_id = os.environ.get('SECURITY_GROUP_ID')
    if subnet_id:
        vpc_config['SubnetIds'] = [subnet_id]
    if security_group_id:
        vpc_config['SecurityGroupIds'] = [security_group_id]
    return vpc_config

def upload_lambda_function():
    """
    main function of deployment.
    Ensure IAM is setup. Upload zip. Create function.
    """
    vpc_config = configure_vpc()
    role = setup_iam_role()

    rule = EVENTS_C.put_rule(Name='DiscoverInstancesSchedule',
                             ScheduleExpression=os.environ.get('DISCOVERY_SCHEDULE'),
                             State='ENABLED',
                             Description='Run the instance discovery')

    with open('{}/../aws_monitor.zip'.format(BASE_DIR), 'rb') as zip_file:
        zip_bytes = zip_file.read()
        fcn = {}
        try:
            LAMBDA_C.get_function(FunctionName='DiscoverInstances')
            fcn = LAMBDA_C.update_function_code(FunctionName='DiscoverInstances',
                                                ZipFile=zip_bytes,
                                                Publish=True)
        except ClientError as err:
            if err.response['Error']['Code'] == 'ResourceNotFoundException':
                sleep(10)
                fcn = LAMBDA_C.create_function(FunctionName='DiscoverInstances',
                                               Code={'ZipFile': zip_bytes},
                                               Runtime='python2.7',
                                               Role=role.arn,
                                               Handler='zumoco.main',
                                               Timeout=300,
                                               Description="Discover, add cloudwatch alerts",
                                               MemorySize=128,
                                               VpcConfig=vpc_config)

            else:
                raise err

        try:
            LAMBDA_C.add_permission(FunctionName='DiscoverInstances',
                                    StatementId='DiscoverInstancesSchedule-Permission',
                                    Action='lambda:InvokeFunction',
                                    Principal='events.amazonaws.com',
                                    SourceArn=rule['RuleArn'])
        except ClientError as err:
            if err.response['Error']['Code'] != 'ResourceConflictException':
                # ignore conflicts if the rule exists
                raise err

        EVENTS_C.put_targets(Rule='DiscoverInstancesSchedule',
                             Targets=[{'Id': 'DiscoverInstances-schedule',
                                       'Arn': fcn['FunctionArn'],}])

upload_lambda_function()


from telegram.ext import *
from telegram import *
import openai

openai.api_key = "YOUR OPENAI API KEY"          #   Enter your OpenAI Secret Key.
telegram_token = "YOUR TELEGRAM BOT TOKEN"      #   Enter your Telegram Bot Token.
conversation=[{"role": "system", "content": "You are a helpful assistant."}]    #   Defined the assistant role.

def main():
    app = Application.builder().token(telegram_token).build()           #   Created a Telegram app.
    app.add_handler(CommandHandler('start', start_command))             #   Added start_command function.
    app.add_handler(CommandHandler('restart', restart_command))         #   Added restart_command function.
    app.add_handler(MessageHandler(filters.TEXT, handle_message))       #   Added handle_message function.
    app.add_error_handler(error)                                        #   Added error_handle function.
    app.run_polling()                                                   #   Started the app.

def reply(lastMessage):                                                 #   ChatGPT conversation function
    if(len(conversation)>=7):                                           #   The conversation has a limit. Only assistant role, last 3 messages and last 3 replies are saved. Other messages and replies are deleted.
        conversation.pop(1)
    conversation.append({"role": "user", "content": lastMessage})       #   Added last request.
    completion = openai.ChatCompletion.create(                          #   Sent completion request and received ChatGPT message.
        model="gpt-3.5-turbo",                                          #   Used "gpt-3.5-turbo" model. "gpt-4" can also be used.
        messages=conversation,                                          #   Sent all conversation.
        max_tokens=1000                                                 #   Defined as max 1000 tokens. Changeable value.
    )
    if(len(conversation)>7):                                           #   The conversation has a limit. Only assistant role, last 3 messages and last 3 replies are saved. Other messages and replies are deleted.
        conversation.pop(1)
    lastReply = completion.choices[0].message['content']                #   Read last reply from completion.
    conversation.append({"role": "assistant", "content": lastReply})    #   Added last reply.
    return lastReply                                                    #   Returned last reply.

def replyStartRestart():
    global conversation
    conversation.clear()
    conversation=[{"role": "system", "content": "You are a helpful assistant."}]    #   Defined the assistant role.
    return 'Hello! How can I help you?'

async def handle_message(update: Update, context: ContextTypes.DEFAULT_TYPE):
    text: str = update.message.text                                     #   Read last Telegram message from user.
    await update.message.reply_text(reply(text))                        #   Sent ChatGPT message to Telegram user.

async def start_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(replyStartRestart())                #   Replied to Telegram user.

async def restart_command(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(replyStartRestart())                #   Replied to Telegram user.

async def error(update: Update, context: ContextTypes.DEFAULT_TYPE):
    print(f'Error: {context.error}')                                    #   Printed error log
    await update.message.reply_text('Please wait! If I don\'t respond within a few minutes, try again')     #   Replied to Telegram user

if __name__ == "__main__":
    main()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed May 23 12:34:08 2018

@author: michal
"""

import networkx as nx
from networkx.algorithms.isomorphism import GraphMatcher
from networkx.readwrite.json_graph import node_link_data
from os.path import isdir, join, isfile
from os import mkdir
import json
from glob import glob
import shutil

class anionMatcher(GraphMatcher):
    def semantic_feasibility(self, G1_node, G2_node):
        if "charged" in self.G1.node[G1_node]:
            if self.G1.node[G1_node]["charged"] != self.G2.node[G2_node]["charged"]:
                return False
        elif self.G2.node[G2_node]["charged"]:
            return False

        if self.G2.node[G2_node]["terminating"]:
            if len(list(self.G2.neighbors(G2_node))) != len(list(self.G1.neighbors(G1_node))):
                return False

        if ( self.G2.node[G2_node]["element"] == "X" or "X" in self.G2.node[G2_node]["aliases"] ) and  not self.G1.node[G1_node]["element"] in self.G2.node[G2_node]["notAliases"] :
            return True

        return self.G1.node[G1_node]["element"] == self.G2.node[G2_node]["element"] or self.G1.node[G1_node]["element"] in self.G2.node[G2_node]["aliases"]

def addAtribute( graph, nodes, key ):
    if isinstance(nodes, list):
        for nodeId in nodes:
            graph.node[nodeId][key] = True

    else:
        graph.node[nodes][key] = True

def saveAnion( atoms, bonds, charged, name, priority , terminating = [], aliases = {},
              notAliases = {}, geometry = {}, fullIsomorphism = False, nameMapping = {} , nonUniqueCharge = [] ,
              properties2measure = [] ):
    graph = nx.Graph()
    nonUniqueCharge = set(nonUniqueCharge)

    for i, el in enumerate(atoms):
        graph.add_node(i, element = el, terminating = False, bonded = False, aliases = [], charged = False )

    graph.add_edges_from(bonds)

    addAtribute( graph, terminating, "terminating")

    for nodeId in aliases:
        graph.node[nodeId]["aliases"] = aliases[nodeId]

    for nodeId in notAliases:
        graph.node[nodeId]["notAliases"] = notAliases[nodeId]

    if not geometry:
        graph.graph["geometry"]= "no restrictions"
    else:
        graph.graph["geometry"]= geometry

    graph.graph["fullIsomorphism"] = fullIsomorphism
    graph.graph["name"] = name
    graph.graph["nameMapping"] = nameMapping
    graph.graph["priority"] = priority
    graph.graph["properties2measure"] = properties2measure

    fileName = str(priority)+"_"+name

    if isinstance( charged , list ) :
        uniqueCharges = set(charged)

        for nodeId in charged:
            nuc = uniqueCharges | nonUniqueCharge
            nuc.remove(nodeId)
            saveAnionJson(graph, fileName, nodeId, nuc)
    else:
        saveAnionJson(graph, fileName, charged, nonUniqueCharge)

def saveAnionJson( graph, fileName, charged, nonUniqueCharges = []):
    mainElement = graph.node[charged]["element"]
    elements = [ mainElement ]

    if "aliases" in graph.node[charged]:
        elements += graph.node[charged]["aliases"]
        graph.node[charged]["aliases"] = []

    graph.node[charged]["charged"] = True
    graph.graph["charged"] = charged
    graph.graph["otherCharges"] = list(nonUniqueCharges)

    oldName = ""
    nameMapping = False
    if "X" in graph.graph["name"] and charged in graph.graph["nameMapping"]:
        oldName = graph.graph["name"]
        nameMapping = graph.graph["nameMapping"][charged]
        graph.graph["nameMapping"].pop(charged)

    for element in elements:
        graph.node[charged]["element"] = element

        if nameMapping:
            graph.graph["name"] = oldName.replace( nameMapping , element)

        dir_path = join("anion_templates", element)
        if not isdir( dir_path ):
            mkdir( dir_path )

        path2save = getUniquePath( dir_path , fileName)
        output = open(path2save, 'w')

        json.dump(node_link_data(graph), output )
        output.close()

    graph.node[charged]["charged"] = False

def getUniquePath(dirPath, fileName):
    path2save = join( dirPath , fileName+".json")
    if not isfile(path2save):
        return path2save

    similarFiles = glob( join(dirPath, fileName)+"_*.json" )
    if not similarFiles:
        return join( dirPath , fileName+"_0.json")

    maxNumber = -1
    for s in similarFiles:
        newNumber = int( s[:-5].split("_")[-1] )
        maxNumber = max(maxNumber, newNumber)

    return join( dirPath , fileName+"_"+str(maxNumber+1)+".json")

def clearAnionTemplates():
    if isdir("anion_templates"):
        shutil.rmtree("anion_templates")
    mkdir("anion_templates")

if __name__ == "__main__":
    clearAnionTemplates()

# atoms, bonds, charged, name, priority, terminating = [], aliases = {}, notAliases = {}, geometry = {}, fullIsomorphism = False
    #OXYGEN

#    #RCOOH
    saveAnion( [ "C" , "C", "O", "O" ], [ (0,1), (1,2), (1,3) ],
              2, "RCOO", 0, terminating = [1, 2, 3],
              geometry = "planar", nonUniqueCharge = [3],
              properties2measure= [ { "kind" : "plane", "atoms" : [ 1, 2, 3 ] , "directionalVector" : [ { "atom" : 1 }, { "center" : [ 2, 3] } ] } ] )

    #ClO, BrO, IO,
    saveAnion([ "CL",  "O" ], [(0, 1)],
              1, "XO", 5, fullIsomorphism = True,
              aliases = { 0 : [ "BR", "I" ] }, nameMapping = { 0 : "X"}, properties2measure= [ {  "kind" : "line", "atoms" : [ 0, 1 ] } ] )

    #NO2, ClO2, BRO2,
    saveAnion([ "N",  "O" , "O" ], [(0, 1), (0,2)],
              1, "XO2", 10, fullIsomorphism = True, aliases = { 0 : ["CL", "BR"]}, nameMapping = { 0 : "X" },
              nonUniqueCharge=[2], properties2measure= [ { "kind" : "plane" , "atoms" : [ 0, 1, 2 ],
                               "directionalVector" : [ { "atom" : 0 }, { "center" : [ 1, 2] } ] } ])

    #NO3, CO3, PO3, SO3, AsO3, BO3, ClO3, BRO3
    saveAnion( ["N", "O", "O", "O"], [(0,1), (0,2), (0,3)],
              1, "XO3", 15, fullIsomorphism = True,
              aliases = { 0 : [ "C", "P", "B", "S", "AS", "CL", "BR", "I" ] }, nameMapping = { 0 : "X" },
              nonUniqueCharge= [2, 3],
              properties2measure= [ { "kind" : "plane", "atoms" : [ 1, 2, 3 ] , "directionalVector" : [ { "closest" : [1, 2, 3] }, { "center" : [ 1, 2, 3] } ]} ])

    #PO4, SO4, AsO4, ClO4, BRO4
    saveAnion( ["P", "O", "O", "O", "O"], [(0,1), (0,2), (0,3), (0, 4)],
              1, "XO4", 20, fullIsomorphism = True,
              aliases = { 0 : [ "S", "AS", "CL", "BR", "I" ] }, nameMapping = { 0 : "X" },
              nonUniqueCharge=[2, 3, 4])

#   Ph-OH
#    saveAnion( [ "C" , "C" , "C" , "C" , "C", "C" , "O" ], [(0,1),(1,2), (2,3), (3,4),( 4, 5), (5, 0), (5,6)],
#              6, "PhOH", 25, terminating = [6], geometry = "planarWithSubstituents")

    #    #RBOOH
    saveAnion( [ "X" , "B", "O", "O" ], [ (0,1), (1,2), (1,3) ],
              2, "RBOO", 30, terminating = [2, 3],
              notAliases = {0 : [ "O" ] },
              nonUniqueCharge=[3],
              properties2measure= [ { "kind" : "plane" , "atoms" : [ 1, 2, 3 ] , "directionalVector" : [ { "atom" : 1 }, { "center" : [ 2, 3] } ]} ])

    #COO
    saveAnion( [  "C", "O", "O" ], [ (0,1), (0,2) ],
              1, "COO", 35, terminating = [1, 2], nonUniqueCharge=[2],
              properties2measure= [ { "kind" : "plane", "atoms" : [ 0, 1, 2 ], "directionalVector" : [ { "atom" : 0 }, { "center" : [ 1, 2] } ] } ] )

    #R-PO4, R-SO4, R-AsO4
    saveAnion( ["P", "O", "O", "O", "O"], [(0,1), (0,2), (0,3), (0, 4)],
              1, "R-XO4", 45, terminating = [ 1, 2, 3 ] ,
              aliases = { 0 : [ "S", "AS" ] }, nameMapping = { 0 : "X" },
              nonUniqueCharge=[2,3])

    #R2-PO4, R2-SO4, R2-AsO4
    saveAnion( ["P", "O", "O", "O", "O"], [(0,1), (0,2), (0,3), (0, 4)],
              1, "R2-XO4", 47, terminating = [ 1, 2 ] ,
              aliases = { 0 : [ "S", "AS" ] }, nameMapping = { 0 : "X" },
              nonUniqueCharge=[2])

    #R3-PO4, R3-SO4, R3-AsO4
#    saveAnion( ["P", "O", "O", "O", "O"], [(0,1), (0,2), (0,3), (0, 4)],
#              1, "R2-XO4", 48, terminating = [ 1 ] ,
#              aliases = { 0 : [ "S", "AS" ] }, nameMapping = { 0 : "X" } )

    #RAsO3, RPO3, RSO3
    saveAnion( ["P", "O", "O", "O", "C"], [(0,1), (0,2), (0,3), (0, 4)],
              1, "RXO3", 50,  terminating = [1, 2, 3] ,
              aliases = { 0 : [ "S", "AS" ] }, nameMapping = { 0 : "X" },
              nonUniqueCharge=[2,3])

    #R2AsO2, R2PO2, RRSO2
#    saveAnion( ["P", "O", "O", "C", "C"], [(0,1), (0,2), (0,3), (0, 4)],
#              1, "R2XO2", 55, terminating = [1, 2],
#              aliases = { 0 : [ "S", "AS" ] }, nameMapping = { 0 : "X" } )

    #F, CL, BR, I, S
    saveAnion( [ "F" ], [], 0, "X", 55, aliases = { 0 : [ "CL", "BR", "I", "S"] },
              fullIsomorphism = True, nameMapping = { 0 : "X"})

    #SCN
    saveAnion([ "S",  "C" , "N" ], [(0, 1), (0,2)],
              [0,1,2], "SCN", 62, fullIsomorphism = True, properties2measure= [ { "kind" : "line", "atoms" : [ 0, 2 ] } ])

#    #RSH
#    saveAnion( [ "X" , "S" ], [ (0,1)],
#              1, "RSH", 60, terminating = [1],
#              notAliases = {0 : [ "O" ] } )
#

    #N3
    saveAnion([ "N",  "N" , "N" ], [(0, 1), (0,2)],
              [0,1], "N3", 70, fullIsomorphism = True, nonUniqueCharge=[2], properties2measure= [ { "kind" : "lineSymmetric", "atoms" : [ 0, 2 ] } ])

    #CN
    saveAnion([  "C" , "N" ], [(0, 1)],
              [0,1], "CN", 75, fullIsomorphism = True, properties2measure= [ { "kind" : "line", "atoms" : [ 0, 1 ] } ])

   #    #RSSR
#    saveAnion( [ "X" , "S", "S" ], [ (0,1), (1,2)],
#              1, "RSS", 80 ,
#              notAliases = {0 : [ "O" ] } )


from unittest import TestCase
from unittest.mock import MagicMock, patch

from src.utils.callgrind import extract_function_calls, extract_hotspots

class TestExtractHotspots(TestCase):
    def test(self):
        callgrind = MagicMock()
        count = 2

        # Fake CallgrindParser internals
        event1 = MagicMock()
        event1.name = "Time ratio"

        event2 = MagicMock()
        event2.name = "Samples"

        fun1 = MagicMock()
        fun1.events = MagicMock()
        fun1.events.items.return_value = [(event1, 0.1), (event2, 1)]

        fun2 = MagicMock()
        fun2.events = MagicMock()
        fun2.events.items.return_value = [(event1, 0.2), (event2, 1)]

        fun3 = MagicMock()
        fun3.events = MagicMock()
        fun3.events.items.return_value = [(event1, 0.3), (event2, 1)]

        fake_functions = {"name1": fun1,
                          "name2": fun2,
                          "name3": fun3, }

        profile = MagicMock()
        profile.functions = fake_functions

        parser = MagicMock()
        parser.parse.return_value = profile

        with patch("src.utils.callgrind.open"):
            with patch("src.utils.callgrind.gprof2dot.CallgrindParser") as mock_parser:
                mock_parser.return_value = parser
                result = extract_hotspots(callgrind, count)

        self.assertEqual(len(result), 2)
        self.assertTrue("name3" in result[0] and "30.0%" in result[0])
        self.assertTrue("name2" in result[1] and "20.0%" in result[1])

class TestExtractFunctionCalls(TestCase):
    def test(self):
        callgrind = MagicMock()

        # Fake CallgrindParser internals
        event = MagicMock()
        event.name = "Samples"

        call1 = MagicMock()
        call1.callee_id = "name2"
        call1.events = {event: 5}

        call2 = MagicMock()
        call2.callee_id = "name3"
        call2.events = {event: 6}

        caller = MagicMock()
        caller.calls = {"key": call1, "another_key": call2}

        callee1 = MagicMock()
        callee1.name = "function2"

        callee2 = MagicMock()
        callee2.name = "function3"

        fake_functions = {"name1": caller,
                          "name2": callee1,
                          "name3": callee2}

        profile = MagicMock()
        profile.functions = fake_functions

        parser = MagicMock()
        parser.parse.return_value = profile

        with patch("src.utils.callgrind.open"):
            with patch("src.utils.callgrind.gprof2dot.CallgrindParser") as mock_parser:
                mock_parser.return_value = parser
                result = extract_function_calls(callgrind, "name1")

        self.assertEqual(result["function2"], 5)
        self.assertEqual(result["function3"], 6)