"""
WRITEME

"""
from __future__ import absolute_import, print_function, division
from copy import copy, deepcopy
from sys import getsizeof
import sys
import traceback
import numpy as np

import theano
from theano.compat import izip
from six import reraise
from six.moves import StringIO
from theano.gof import utils
from theano.gof import graph
from theano.gof.type import Type

from .utils import undef

__excepthook = sys.excepthook

def log_thunk_trace(value, f=sys.stderr):
    """
    Log Theano's diagnostic stack trace for an exception
    raised by raise_with_op.

    """
    # in future, consider accepting `write` as arg rather than file
    # to support writing to a logger
    def write(msg):
        print("log_thunk_trace: %s" % msg.strip(), file=f)

    if hasattr(value, '__thunk_trace__'):
        trace2 = value.__thunk_trace__
        write("There was a problem executing an Op.")
        if trace2 is None:
            write("Could not find where this Op was defined.")
            write(" * You might have instantiated this Op "
                  "directly instead of using a constructor.")
            write(" * The Op you constructed might have been"
                  " optimized. Try turning off optimizations.")
        elif trace2:
            write("Definition in: ")
            for line in traceback.format_list(trace2):
                write(line)
            write("For the full definition stack trace set"
                  " the Theano flags traceback.limit to -1")

def thunk_hook(type, value, trace):
    """
    This function is meant to replace excepthook and do some
    special work if the exception value has a __thunk_trace__
    field.
    In that case, it retrieves the field, which should
    contain a trace as returned by L{traceback.extract_stack},
    and prints it out on L{stderr}.

    The normal excepthook is then called.

    Parameters:
    ----------
    type
        Exception class
    value
        Exception instance
    trace
        Traceback object

    Notes
    -----
    This hook replaced by nosetests, so it does not run in nose tests.

    """
    log_thunk_trace(value)
    __excepthook(type, value, trace)
sys.excepthook = thunk_hook

# TODO: Make this work with linker defined schedule
def raise_with_op(node, thunk=None, exc_info=None, storage_map=None):
    """
    Re-raise an exception while annotating the exception object with
    debug info.

    Parameters
    ----------
    node : Apply node
        The Apply node object that resulted in the raised exception.
    exc_info : tuple, optional
        A tuple containing the exception type, exception object and
        associated traceback, as would be returned by a call to
        `sys.exc_info()` (which is done if `None` is passed).
    storage_map: dict, optional
        storage map of the theano function that resulted in the
        raised exception.

    Notes
    -----
    This re-raises the exception described by `exc_info` (or the last
    one raised, if `exc_info` is omitted) and annotates the exception
    object with several new members which may be helpful for debugging
    Theano graphs. They are:

     * __op_instance__: The Op that is responsible for the exception
       being raised.
     * __thunk_trace__: A traceback corresponding to the code that
       actually generated the exception, if it is available.
     * __applynode_index__: The index of the Apply node corresponding
       to this op in `op.fgraph.toposort()`.

    The exception is not annotated if it is of type `KeyboardInterrupt`.

    """
    if exc_info is None:
        exc_info = sys.exc_info()
    exc_type, exc_value, exc_trace = exc_info
    if exc_type == KeyboardInterrupt:
        # print a simple traceback from KeyboardInterrupt
        reraise(exc_type, exc_value, exc_trace)
    try:
        trace = node.outputs[0].tag.trace
    except AttributeError:
        try:
            trace = node.op.tag.trace
        except AttributeError:
            trace = ()
    exc_value.__thunk_trace__ = trace
    exc_value.__op_instance__ = node
    topo = node.fgraph.toposort()
    if node in topo:
        node_index = topo.index(node)
    else:
        node_index = None
    exc_value.__applynode_index__ = node_index

    hints = []
    detailed_err_msg = "\nApply node that caused the error: " + str(node)
    if exc_value.__applynode_index__ is not None:
        detailed_err_msg += "\nToposort index: %d" % node_index

    types = [getattr(ipt, 'type', 'No type') for ipt in node.inputs]
    detailed_err_msg += "\nInputs types: %s\n" % types

    if thunk is not None:
        if hasattr(thunk, 'inputs'):
            shapes = [getattr(ipt[0], 'shape', 'No shapes')
                      for ipt in thunk.inputs]
            strides = [getattr(ipt[0], 'strides', 'No strides')
                       for ipt in thunk.inputs]
            scalar_values = []
            for ipt in thunk.inputs:
                if getattr(ipt[0], "size", -1) <= 5:
                    scalar_values.append(ipt[0])
                else:
                    scalar_values.append("not shown")
        else:
            shapes = "The thunk don't have an inputs attributes."
            strides = "So we can't access the strides of inputs values"
            scalar_values = "And can't print its inputs scalar value"
        clients = [[c[0] for c in var.clients] for var in node.outputs]
        detailed_err_msg += ("Inputs shapes: %s" % shapes +
                             "\nInputs strides: %s" % strides +
                             "\nInputs values: %s" % scalar_values)
        if theano.config.exception_verbosity == 'high':
            detailed_err_msg += "\nInputs type_num: %s" % str(
                [getattr(getattr(i[0], 'dtype', ''), 'num', '') for i in thunk.inputs])
        if hasattr(node.op, '__input_name__'):
            detailed_err_msg += "\nInputs name: %s\n" % str(node.op.__input_name__)

        detailed_err_msg += "\nOutputs clients: %s\n" % clients
    else:
        hints.append(
            "HINT: Use another linker then the c linker to"
            " have the inputs shapes and strides printed.")

    # Print node backtraces
    tr = getattr(node.outputs[0].tag, 'trace', [])
    if isinstance(tr, list) and len(tr) > 0:
        detailed_err_msg += "\nBacktrace when the node is created(use Theano flag traceback.limit=N to make it longer):\n"

        # Print separate message for each element in the list of batcktraces
        sio = StringIO()
        for subtr in tr:
            traceback.print_list(subtr, sio)
        detailed_err_msg += str(sio.getvalue())
    else:
        hints.append(
            "HINT: Re-running with most Theano optimization disabled could"
            " give you a back-trace of when this node was created. This can"
            " be done with by setting the Theano flag"
            " 'optimizer=fast_compile'. If that does not work,"
            " Theano optimizations can be disabled with 'optimizer=None'.")

    if theano.config.exception_verbosity == 'high':

        f = StringIO()
        theano.printing.debugprint(node, file=f, stop_on_name=True,
                                   print_type=True)
        detailed_err_msg += "\nDebugprint of the apply node: \n"
        detailed_err_msg += f.getvalue()

    # Prints output_map
    if theano.config.exception_verbosity == 'high' and storage_map is not None:
        detailed_err_msg += "\nStorage map footprint:\n"
        shared_input_list = [
            item for item in node.fgraph.inputs
            if isinstance(item, theano.compile.SharedVariable)]
        nonshared_input_list = [
            item for item in node.fgraph.inputs
            if not isinstance(item, theano.compile.SharedVariable)]
        storage_map_list = []
        total_size = 0
        total_size_inputs = 0
        for k in storage_map:
            storage_map_item = []

            # storage_map_item[0]: the variable
            storage_map_item.append(str(k))

            # storage_map_item[1]: the shape
            shapeinfo = None
            if hasattr(storage_map[k][0], 'shape'):
                shapeinfo = storage_map[k][0].shape
                if len(shapeinfo) != 0:
                    storage_map_item.append(shapeinfo)
                else:
                    storage_map_item.append(tuple())
            else:
                storage_map_item.append(None)

            # storage_map_item[2]: itemsize
            # storage_map_item[3]: bytes
            if hasattr(storage_map[k][0], 'dtype'):
                dtype = storage_map[k][0].dtype
                storage_map_item.append(np.dtype(dtype).itemsize)
                if shapeinfo is None:
                    storage_map_item.append(-1)
                else:
                    sz = np.dtype(dtype).itemsize * np.prod(shapeinfo)
                    storage_map_item.append(sz)
                    total_size += sz
                    if not k.owner:
                        total_size_inputs += sz
                    else:
                        # If it is a view, don't count it twice.
                        if getattr(k.owner.op, 'view_map', None):
                            vmap = k.owner.op.view_map
                            out_idx = k.owner.outputs.index(k)
                            data = storage_map[k][0]
                            if out_idx in vmap:
                                assert len(vmap[out_idx]) == 1
                                input_data = storage_map[
                                    k.owner.inputs[vmap[out_idx][0]]][0]
                                if k.type.may_share_memory(data, input_data):
                                    total_size -= sz
                        # If it is a destroyed input, the input
                        # shouldn't be in the storage_map anymore
                        # except if there is a special flag used. So
                        # we still must check it.
                        if getattr(k.owner.op, 'destroy_map', None):
                            vmap = k.owner.op.destroy_map
                            out_idx = k.owner.outputs.index(k)
                            data = storage_map[k][0]
                            if out_idx in vmap:
                                assert len(vmap[out_idx]) == 1
                                input_data = storage_map[
                                    k.owner.inputs[vmap[out_idx][0]]][0]
                                if k.type.may_share_memory(data, input_data):
                                    total_size -= sz
            else:
                bytes = getsizeof(storage_map[k][0])
                storage_map_item.append(bytes)
                storage_map_item.append(-1)

            # Flag of shared val
            # storage_map_item[4]
            if k in shared_input_list:
                storage_map_item.append(True)
            elif k in nonshared_input_list:
                storage_map_item.append(False)
            else:
                storage_map_item.append(None)
            storage_map_list.append(storage_map_item)

        from operator import itemgetter
        storage_map_list.sort(key=itemgetter(3), reverse=True)
        for item in storage_map_list:
            if item[3] == -1:
                continue
            detailed_err_msg += " - " + item[0] + ", "
            if item[4] is True:
                detailed_err_msg += "Shared Input, "
            elif item[4] is False:
                detailed_err_msg += "Input, "
            if item[1] is not None:
                detailed_err_msg += "Shape: %s, " % str(item[1])
            detailed_err_msg += "ElemSize: %s Byte(s)" % item[2]
            if item[3] is not None:
                detailed_err_msg += ", TotalSize: %s Byte(s)\n" % item[3]
            else:
                detailed_err_msg += "\n"
        detailed_err_msg += " TotalSize: %s Byte(s) %.3f GB\n" % (
            total_size, total_size / 1024. / 1024 / 1024)
        detailed_err_msg += " TotalSize inputs: %s Byte(s) %.3f GB\n" % (
            total_size_inputs, total_size_inputs / 1024. / 1024 / 1024)

    else:
        hints.append(
            "HINT: Use the Theano flag 'exception_verbosity=high'"
            " for a debugprint and storage map footprint of this apply node.")

    try:
        exc_value = exc_type(str(exc_value) + detailed_err_msg +
                             '\n' + '\n'.join(hints))
    except TypeError:
        print("WARNING: %s error does not allow us to add extra error message" %
              str(exc_type))
        # Some exception need extra parameter in inputs. So forget the
        # extra long error message in that case.
        pass
    reraise(exc_type, exc_value, exc_trace)

class Linker(object):
    """
    WRITEME

    """

    def clone(self, allow_gc=undef):
        new = copy(self)
        if allow_gc is not undef:
            new.allow_gc = allow_gc
        return new

    def make_thunk(self):
        """
        This function must return a triplet (function, input_variables,
        output_variables) where function is a thunk that operates on the
        returned variables. If inplace is True, the input_variables and
        output_variables lists will be the same as the inputs and outputs
        of the graph provided to the L{Linker}. Else, independent
        variables will be returned.

        Examples
        --------
        x, y = Variable(Double), Variable(Double)
        e = x + y
        fgraph = FunctionGraph([x, y], [e])
        fn, (new_x, new_y), (new_e, ) = MyLinker(fgraph).make_thunk(inplace)
        new_x.data = 1.0
        new_y.data = 2.0
        fn()
        print new_e.data # 3.0
        print e.data # 3.0 iff inplace == True (else unknown)

        """
        raise utils.MethodNotDefined("make_thunk", type(self),
                                     self.__class__.__name__)

    # DELETEME #
    def make_function(self, unpack_single=True, **kwargs):
        """
        Returns a function that takes values corresponding to the inputs of the
        fgraph used by this L{Linker} and returns values corresponding the the
        outputs of that fgraph. If inplace is True, the calculations will
        operate in the same storage the fgraph uses, else independent storage
        will be allocated for the function.

        Example
        -------
        e = x + y
        fgraph = FunctionGraph([x, y], [e])
        fn = MyLinker(fgraph).make_function(inplace)
        print fn(1.0, 2.0) # 3.0
        print e.data # 3.0 iff inplace == True (else unknown)

        If unpack_single is True (default) and that the function has only one
        output, then that output will be returned. Else, a list or tuple of
        length 1 will be returned.

        """
        thunk, inputs, outputs = self.make_thunk(**kwargs)

        def execute(*args):
            def e_arity(takes, got):
                return 'Function call takes exactly %i %s (%i given)' % (
                    takes, ['argument', 'arguments'][takes > 1], got)
            if (len(args) != len(inputs)):
                raise TypeError(e_arity(len(inputs), len(args)))
            for arg, variable in izip(args, inputs):
                variable.data = arg
            thunk()
            if unpack_single:
                return utils.to_return_values([variable.data
                                               for variable in outputs])
            else:
                return [variable.data for variable in outputs]
        execute.thunk = thunk
        execute.inputs = inputs
        execute.outputs = outputs

        return execute

    def schedule(self, fgraph):
        return fgraph.toposort()

# TODO: Move this class to the compile module, where it is used (and for which it exists).
class Container(object):
    """
    This class joins a variable with its computed value.

    It is used in linkers, especially for the inputs and outputs of a Function.

    Parameters
    ----------
    r : a Variable or a Type
    storage
        A list of length 1, whose element is the value for `r`.
    readonly : bool
        True indicates that this should not be setable by Function[r] = val.
    strict : bool
        If True, we don't allow type casting.
    allow_downcast
        If True (and `strict` is False), allow upcasting of type, but not
        downcasting. If False, prevent it. If None (default), allows only
        downcasting of float to floatX scalar.
    name : str
        A string (for pretty-printing?)

    """

    def __init__(self, r, storage, readonly=False, strict=False,
                 allow_downcast=None, name=None):
        if not isinstance(storage, list) or not len(storage) >= 1:
            raise TypeError("storage must be a list of length at least one")
        # self.r = r
        if isinstance(r, Type):
            self.type = r
        else:
            self.type = r.type
        if name is None:
            # Some Type do not have a name field.
            self.name = getattr(r, 'name', None)
        else:
            self.name = name

        self.storage = storage
        self.readonly = readonly
        self.strict = strict
        self.allow_downcast = allow_downcast

    def __get__(self):
        return self.storage[0]

    def __set__(self, value):
        if self.readonly:
            raise Exception("Cannot set readonly storage: %s" % self.name)
        try:
            if value is None:
                self.storage[0] = None
                return

            kwargs = {}
            if self.strict:
                kwargs['strict'] = True
            if self.allow_downcast is not None:
                kwargs['allow_downcast'] = self.allow_downcast
            if hasattr(self.type, 'filter_inplace'):
                self.storage[0] = self.type.filter_inplace(value,
                                                           self.storage[0],
                                                           **kwargs)
            else:
                self.storage[0] = self.type.filter(value, **kwargs)

        except Exception as e:
            e.args = e.args + (('Container name "%s"' % self.name),)
            raise
    data = property(__get__, __set__)
    value = property(__get__, __set__)

    def __str__(self):
        return "<" + str(self.storage[0]) + ">"

    def __repr__(self):
        return "<" + repr(self.storage[0]) + ">"

    def __deepcopy__(self, memo):
        data_was_in_memo = id(self.storage[0]) in memo
        r = type(self)(
            deepcopy(self.type, memo=memo),
            deepcopy(self.storage, memo=memo),
            deepcopy(self.readonly, memo=memo),
            deepcopy(self.strict, memo=memo),
            deepcopy(self.allow_downcast, memo=memo),
            deepcopy(self.name, memo=memo),
        )
        # Work around NumPy deepcopy of ndarray with 0 dimension that
        # don't return an ndarray.
        if (r.storage[0] is not None and
                not self.type.is_valid_value(r.storage[0])):
            assert not data_was_in_memo
            assert self.type.is_valid_value(self.storage[0])
            # This should also work for read only container.
            r.storage[0] = self.type.filter(r.storage[0],
                                            strict=False,
                                            allow_downcast=False)
            memo[id(self.storage[0])] = r.storage[0]
        return r

def map_storage(fgraph, order, input_storage, output_storage, storage_map=None):
    """Ensure there is storage (a length-1 list) for inputs, outputs, and interior nodes.

    :param fgraph: The current fgraph.  This function uses the inputs and outputs attributes.
    :param order: an iterable over Apply instances (in program running order)
    :param input_storage: None or existing input storage (see below)
    :param output_storage: None or existing output storage (see below)

    :rtype: 3-tuple
    :returns: (list of storage for inputs, list of storage for outputs, and the `storage_map`)

    Parameters
    ----------
    fgraph
        The current fgraph. This function uses the inputs and outputs
        attributes.
    order
        An iterable over Apply instances (in program running order).
    input_storage
        None or existing input storage (see below).
    output_storage
        None or existing output storage (see below).

    Returns
    -------
    3-tuple
        List of storage for inputs, list of storage for outputs, and
        the `storage_map`.

    Extended summary
    ----------------
    This function iterates over the nodes in `order` and ensures that for every
    input and output `Variable`, there is a unique storage container. This is
    returned as a dictionary Variable -> storage called the `storage_map`.

    This function also returns `input_storage`, which is a list of storages
    corresponding to fgraph.inputs.
    This function also returns `output_storage`, which is a list of storages
    corresponding to fgraph.outputs.

    """
    # each Apply argument's data is stored in a list of length 1 (these lists act like pointers)

    if storage_map is None:
        storage_map = {}

    # input_storage is a list of data-containers for the inputs.
    if input_storage is None:
        input_storage = [[None] for input in fgraph.inputs]
    else:
        assert len(fgraph.inputs) == len(input_storage)

    # add input storage into storage_map
    for r, storage in zip(fgraph.inputs, input_storage):
        if r in storage_map:
            assert storage_map[r] is storage, ("Given input_storage conflicts "
                                               "with storage in given storage_"
                                               "map. Given input_storage: ",
                                               storage, "Storage in storage_ma"
                                               "p: ", storage_map[r])
        else:
            storage_map[r] = storage
#     for orphan in fgraph.orphans:
#         if not isinstance(orphan, Constant):
#             raise TypeError("Cannot link a graph with non-constant orphans.", orphan)
#         storage_map[orphan] = [orphan.data]

    # allocate output storage
    if output_storage is not None:
        assert len(fgraph.outputs) == len(output_storage)
        for r, storage in zip(fgraph.outputs, output_storage):
            if r in storage_map:
                assert storage_map[r] is storage, ("Given output_storage confl"
                                                   "icts with storage in given"
                                                   " storage_map. Given output"
                                                   "_storage: ", storage, "Sto"
                                                   "rage in storage_map: ",
                                                   storage_map[r])
            else:
                storage_map[r] = storage

    # allocate storage for intermediate computation
    for node in order:
        for r in node.inputs:
            if r not in storage_map:
                assert isinstance(r, graph.Constant)
                storage_map[r] = [r.data]
        for r in node.outputs:
            storage_map.setdefault(r, [None])
    for r in fgraph.outputs:
        if isinstance(r, graph.Constant):
            storage_map.setdefault(r, [r.data])

    # extract output storage
    if output_storage is None:
        output_storage = [storage_map[r] for r in fgraph.outputs]

    return input_storage, output_storage, storage_map

def streamline(fgraph, thunks, order, post_thunk_old_storage=None,
               no_recycling=None, nice_errors=True):
    """
    WRITEME

    Parameters
    ----------
    fgraph
    thunks
        The list of program instructions.
    order
        The list of apply instances that gave rise to the thunks
        (same order as thunks).
    post_thunk_old_storage
        A list (corresponding to thunks, order) whose elements are lists of
        storage cells, that should be cleared after running thecorresponding
        thunk. A value of None disables this functionality.
    no_recycling
        Storage elements that cannot be 'recycled' by repeatedly executing the
        program. These storage elements are cleared before re-running.
    nice_errors
        Run in such a way that the double-traceback is printed. This costs a
        bit of performance in the inner python loop.

    """
    if no_recycling is None:
        no_recycling = []

    if len(thunks) != len(order):
        raise ValueError('Length of thunks and order must match',
                         (len(thunks), len(order)))

    if post_thunk_old_storage:
        if len(thunks) != len(post_thunk_old_storage):
            raise ValueError(
                'Length of thunks and post_thunk_old_storage must match',
                (len(thunks), len(post_thunk_old_storage)))

        def streamline_default_f():
            for x in no_recycling:
                x[0] = None
            try:
                for thunk, node, old_storage in izip(thunks, order,
                                                     post_thunk_old_storage):
                    thunk()
                    for old_s in old_storage:
                        old_s[0] = None
            except Exception:
                raise_with_op(node, thunk)
        f = streamline_default_f
    elif nice_errors:
        def streamline_nice_errors_f():
            for x in no_recycling:
                x[0] = None
            try:
                for thunk, node in izip(thunks, order):
                    thunk()
            except Exception:
                raise_with_op(node, thunk)
        f = streamline_nice_errors_f
    else:
        # don't worry about raise_with_op, just go a little faster.
        # there is a mix of python and c thunks
        def streamline_fast_f():
            for x in no_recycling:
                x[0] = None
            for thunk in thunks:
                thunk()
        f = streamline_fast_f
    return f

class LocalLinker(Linker):
    """
    Useful base class for L{Linker}s which keep all nodes in the graph, and run
    a thunk associated with each node.

    """

    def make_thunk(self, input_storage=None, output_storage=None, storage_map=None):
        return self.make_all(input_storage=input_storage,
                             output_storage=output_storage,
                             storage_map=storage_map)[:3]

    def make_all(self, input_storage, output_storage):
        # By convention, subclasses of LocalLinker should implement this function!
        #
        # This function should return a tuple of 5 things
        # 1. function to run the program
        # 2. input storage
        # 3. output storage
        # 4. thunks: list of nodes' functions in the order they will be run by the function in (1)
        # 5. order: list of nodes, in the order they will be run by the function in (1)
        raise utils.MethodNotDefined("make_all", type(self),
                                     self.__class__.__name__)

def gc_helper(node_list):
    """
    Return the set of Variable instances which are computed by node_list.
    Parameters
    ----------
    node_list
        List of Apply instances in program execution order.

    Returns
    -------
    2-tuple
        FIRST, the set of Variable instances which are computed by node_list,
        and SECOND a dictionary that maps each Variable instance to a the last
        node to use Variable as an input.

    Extended Summary
    ----------------
    This is used to allow garbage collection within graphs.

    It ignores view_map and destroy_map. This isn't needed as python
    have reference count. In Theano gc, we should not take into
    account view_map and destroy_map as if the thunk decided to create
    a new output, we would delay uselessly its gc by Python.

    """
    # for freeing memory
    last_user = {}
    computed = set()
    for node in node_list:
        for input in node.inputs:
            last_user[input] = node
        for output in node.outputs:
            computed.add(output)
    return computed, last_user

class PerformLinker(LocalLinker):
    """
    Basic L{Linker} subclass that calls the perform method on each L{Op} in
    the L{FunctionGraph} in the order given by L{Linker.schedule}.

    """

    def __init__(self, allow_gc=None, schedule=None):
        if allow_gc is None:
            allow_gc = theano.config.allow_gc
        self.fgraph = None
        self.allow_gc = allow_gc
        if schedule:
            self.schedule = schedule

    def accept(self, fgraph, no_recycling=None, profile=None):
        """

        Parameters
        ----------
        fgraph
            A PerformLinker can have accepted one FunctionGraph instance at a time.
        no_recycling
            WRITEME

        Returns
        -------
        object
            self (TODO: WHY? Who calls this function?)

        """
        if no_recycling is None:
            no_recycling = []
        if self.fgraph is not None and self.fgraph is not fgraph:
            return type(self)(allow_gc=self.allow_gc).accept(
                fgraph, no_recycling, profile)
            # raise Exception("Cannot accept from a Linker that is already tied to another FunctionGraph.")
        self.fgraph = fgraph
        self.no_recycling = no_recycling
        return self

    def make_all(self, input_storage=None, output_storage=None, storage_map=None):
        """
        Returns Function to run all nodes, list of input containers, list of outputs

        Parameters
        ----------
        input_storage
            list of storages corresponding to fgraph.inputs
        output_storage
            list of storages corresponding to fgraph.outputs

        Returns
        -------
        object
            Function to run all nodes, list of input containers, list of output
            containers, list of thunks (for all programs), list of nodes
            (for all programs).

        """
        fgraph = self.fgraph
        order = self.schedule(fgraph)
        no_recycling = self.no_recycling

        input_storage, output_storage, storage_map = map_storage(fgraph, order, input_storage, output_storage, storage_map)

        compute_map = {}
        for k in storage_map:
            compute_map[k] = [k.owner is None]

        thunks = []
        for node in order:
            # Maker sure we don't use C version of the code, but rather only
            # the python version
            # Note : ops that implement their own make thunk don't usually
            # have this attribute defiend !!
            thunks += [node.op.make_thunk(node,
                                          storage_map,
                                          compute_map,
                                          no_recycling,
                                          'py')]
            thunks[-1].inputs = [storage_map[v] for v in node.inputs]
            thunks[-1].outputs = [storage_map[v] for v in node.outputs]

        computed, last_user = gc_helper(order)
        if self.allow_gc:
            post_thunk_old_storage = []
        else:
            post_thunk_old_storage = None

        for node in order:
            if self.allow_gc:
                post_thunk_old_storage.append(
                    [storage_map[input]
                     for input in node.inputs
                     if (input in computed) and (
                        input not in fgraph.outputs) and (
                        node == last_user[input])])

        if no_recycling is True:
            # True seems like some special code for *everything*?? -JB
            # FunctionMaker always passes a list I think   -JB
            no_recycling = list(storage_map.values())
            no_recycling = utils.difference(no_recycling, input_storage)
        else:
            no_recycling = [storage_map[r] for r in no_recycling if r not in fgraph.inputs]

        # The function that actually runs your program is one of the f's in streamline.
        f = streamline(fgraph, thunks, order, post_thunk_old_storage,
                       no_recycling=no_recycling)

        f.allow_gc = self.allow_gc  # HACK: this is a way of passing an arg to Function.__call__
        add_clear_storage(f, computed, storage_map)
        f.storage_map = storage_map

        return (f,
                [Container(input, storage)
                 for input, storage in izip(fgraph.inputs, input_storage)],
                [Container(output, storage, True)
                 for output, storage in izip(fgraph.outputs, output_storage)],
                thunks,
                order)

def add_clear_storage(f, computed, storage_map):
    def clear_storage():
        for c in computed:
            storage_map[c][0] = None
    f.clear_storage = clear_storage

class WrapLinker(Linker):
    """
    This class makes it easier to run several L{LocalLinker}s in parallel, and
    offers some control over how each thunk is run.

    A wrapper function must be provided, and it can be used to execute the
    thunks, inspect the nodes, print stuff out, etc.

    The constructor initializes a WrapLinker.

    Parameters
    ----------
    linkers : list of L{LocalLinker} subclasses, whose make_all() method returns
        thunks in the same order.
        For each node in the graph, each linker will provide a
        thunk.  This class makes it possible to iterate over each linker's
        program in parallel.
    wrapper : lambda (i, i_node, i_thunk1, i_thunk2, ...) : None
        Does some user-defined action for the i'th element of the program.
        i_thunk<n> is the thunk returned by the n'th linker. (If you want
        to run the program, make sure to call the necessary thunks in this
        function.)

    Notes
    -----
    The outputs of the first linker will be returned.

    This linker ensures that each linker has its own storage for inputs and
    outputs and intermediate variables. There is no interference between
    linkers.

    """

    def __init__(self, linkers, wrapper):
        self.fgraph = None
        self.linkers = linkers
        self.wrapper = wrapper

    def __copy__(self):
        """
        Shallow copy of a WrapLinker.

        Returns
        -------
        object
            A copy of self, where each of the linkers in self.linkers
            have been shallow-copied.

        It is useful because in FunctionMaker, copy.copy is called on the
        Mode's linker, so that it is not modified inplace when linker.accept()
        is called. In this case, we want the wrapped linkers to be copied too.

        """
        other = self.__class__(
            linkers=[copy(l) for l in self.linkers],
            wrapper=self.wrapper)
        return other

    def clone(self, allow_gc=undef):
        return self.__class__(
            linkers=[l.clone(allow_gc=allow_gc) for l in self.linkers],
            wrapper=self.wrapper)

    def accept(self, fgraph, no_recycling=None, profile=None):
        """

        Parameters
        ----------
        fgraph : gof.FunctionGraph
            The fgraph which we will link.
        no_recycling : a list of Variables that belong to fgraph.
            If a Variable is in no_recycling, L{WrapLinker} will clear
            the output storage associated to it (for each linker in linkers)
            during the computation to avoid reusing it.

        """
        if no_recycling is None:
            no_recycling = []
        if self.fgraph is not None and self.fgraph is not fgraph:
            return type(self)(self.linkers, self.wrapper).accept(fgraph,
                                                                 no_recycling)

        self.fgraph = fgraph
        self.no_recycling = no_recycling
        self.linkers = [linker.accept(fgraph, no_recycling)
                        for linker in self.linkers]
        return self

    def pre(self, f, inputs, order, thunk_groups):
        pass

    def make_thunk(self, **kwargs):
        no_recycling = self.no_recycling

        make_all = [self.linkers[0].make_all(**kwargs)]
        kwargs.pop('input_storage', None)
        make_all += [l.make_all(**kwargs) for l in self.linkers[1:]]

        fns, input_lists, output_lists, thunk_lists, order_lists \
            = zip(*make_all)

        order_list0 = order_lists[0]
        for order_list in order_lists[1:]:
            if not order_list0 == order_list:
                raise Exception(
                    "All linkers to WrapLinker should execute operations in the same order.")

        inputs0 = input_lists[0]
        outputs0 = output_lists[0]

        thunk_groups = list(zip(*thunk_lists))
        order = [x[0] for x in zip(*order_lists)]

        to_reset = []
        for thunks, node in izip(thunk_groups, order):
            for j, output in enumerate(node.outputs):
                if output in no_recycling:
                    for thunk in thunks:
                        to_reset.append(thunk.outputs[j])

        wrapper = self.wrapper
        pre = self.pre

        def f():
            for inputs in input_lists[1:]:
                for input1, input2 in izip(inputs0, inputs):
                    input2.storage[0] = copy(input1.storage[0])
            for x in to_reset:
                x[0] = None
            pre(self, [input.data for input in input_lists[0]],
                order, thunk_groups)
            for i, (thunks, node) in enumerate(izip(thunk_groups, order)):
                try:
                    wrapper(i, node, *thunks)
                except Exception:
                    raise_with_op(node, *thunks)
        f.thunk_groups = thunk_groups

        return f, inputs0, outputs0

def WrapLinkerMany(linkers, wrappers):
    """
    Variant on WrapLinker that runs a series of wrapper functions instead of
    just one.

    """
    def wrapper(*args):
        for f in wrappers:
            f(*args)
    return WrapLinker(linkers, wrapper)


import wx

class SlideshowFrame(wx.Frame):
    def __init__(self,**kwargs):
        wx.Frame.__init__(self, **kwargs)
        self.SetBackgroundColour(wx.BLACK)

        self.panel = wx.Panel(self, pos=self.Rect.GetPosition(), size=self.Rect.GetSize())
        self.empty_img = wx.EmptyImage(self.Rect.GetWidth(),
                                       self.Rect.GetHeight())
        self.imageCtrl = wx.StaticBitmap(self.panel, wx.ID_ANY,
                                         wx.BitmapFromImage(self.empty_img))
        #self.verSizer = wx.BoxSizer(wx.VERTICAL)
        #self.horSizer = wx.BoxSizer(wx.HORIZONTAL)
        #self.mainSizer.Add(self.imageCtrl, 0, wx.ALL|wx.ALIGN_CENTER, 0)
        #self.panel.SetSizer(self.mainSizer)
        #self.mainSizer.Fit(self)
        #self.panel.Layout()

    def load_img(self, img_path):
        if img_path is None:
            img = self.empty_img
        else:
            img = wx.Image(img_path, wx.BITMAP_TYPE_ANY)

        #
        # scale the image, preserving the aspect ratio
        #

        w = img.GetWidth()
        h = img.GetHeight()

        W = self.Rect.GetWidth()
        H = self.Rect.GetHeight()

        # scale w to match W, and see if height is over/under H. If so, scale
        # h to match H instead.
        w2, h2 = W, h*(float(W)/w)
        if h2 > H:
            w2, h2 = w*(float(H)/h), H

        img = img.Scale(w2,h2,quality=wx.IMAGE_QUALITY_HIGH)
        self.imageCtrl.SetBitmap(wx.BitmapFromImage(img))
        #self.panel.Layout()

        O = self.Rect.GetPosition() # frame origin
        X,Y = (O[0] + (W-w2)/2, O[1] + (H-h2)/2)
        self.panel.SetRect((X,Y,w2,h2))
        #self.mainSizer.Fit(self)
        #self.panel.Layout()
        self.panel.Refresh()


from src import app
from flask import jsonify, request
import requests
import json
import os

slackToken = os.environ['SLACK_TOKEN']
botAccessToken = os.environ['BOT_ACCESS_TOKEN']
hasuraDataUrl = "http://data.hasura/v1/query"
chatUrl = "https://slack.com/api/chat.postMessage"

##################### APIs ######################

@app.route('/', methods=['GET'])
def test():
    return "Slackbot is running."

@app.route('/echo', methods=['POST'])
def event():
    data = request.form.to_dict()
    print(data)
    print("SlackToken: " + slackToken)
    receivedToken = data["token"]
    print("ReceivedToken: " + receivedToken)
    if (receivedToken==slackToken):
        receivedMessage= data["text"]
        id = storeMsgToDB(receivedMessage)
        sendConfirmation(id, receivedMessage, data["response_url"])
        return "Waiting for confirmation"
    else:
        return "Invalid Token"

@app.route('/repo', methods=['POST'])
def repos():
    data = request.form.to_dict()
    print(data)
    print("SlackToken: " + slackToken)
    receivedToken = data["token"]
    print("ReceivedToken: " + receivedToken)
    if (receivedToken==slackToken):
        receivedMessage= data["text"]
        return getRepo(receivedMessage)
    else:
        return "Invalid Token"

@app.route('/issue', methods=['POST'])
def issues():
    data = request.form.to_dict()
    print(data)
    print("SlackToken: " + slackToken)
    receivedToken = data["token"]
    print("ReceivedToken: " + receivedToken)
    if (receivedToken==slackToken):
        receivedMessage= data["text"]
        return getIssue(receivedMessage)
    else:
        return "Invalid Token"

@app.route('/branch', methods=['POST'])
def branches():
    data = request.form.to_dict()
    print(data)
    print("SlackToken: " + slackToken)
    receivedToken = data["token"]
    print("ReceivedToken: " + receivedToken)
    if (receivedToken==slackToken):
        receivedMessage= data["text"]
        return getBranch(receivedMessage)
    else:
        return "Invalid Token"

@app.route('/helpme', methods=['POST'])
def helps():
    data = request.form.to_dict()
    print(data)
    print("SlackToken: " + slackToken)
    receivedToken = data["token"]
    print("ReceivedToken: " + receivedToken)
    if (receivedToken==slackToken):
        receivedMessage= data["text"]
        return getHelp(receivedMessage)
    else:
        return "Invalid Token"

@app.route('/member', methods=['POST'])
def members():
    data = request.form.to_dict()
    print(data)
    print("SlackToken: " + slackToken)
    receivedToken = data["token"]
    print("ReceivedToken: " + receivedToken)
    if (receivedToken==slackToken):
        receivedMessage= data["text"]
        return getMember(receivedMessage)
    else:
        return "Invalid Token"

@app.route('/tag', methods=['POST'])
def tags():
    data = request.form.to_dict()
    print(data)
    print("SlackToken: " + slackToken)
    receivedToken = data["token"]
    print("ReceivedToken: " + receivedToken)
    if (receivedToken==slackToken):
        receivedMessage= data["text"]
        return getTag(receivedMessage)
    else:
        return "Invalid Token"

@app.route('/confirm', methods=['POST'])
def confirm():
    req = request.form.to_dict()
    data = json.loads(req["payload"])
    print (data)
    receivedToken = data["token"]
    channel = data["channel"]["id"]
    if (receivedToken == slackToken):
        if (data["actions"][0]["value"] == "yes"):
            message = fetchFromDBAndSend(data["callback_id"], channel)
            return ("Message Sent: " + str(message))
        else:
            return "Ok. Not sending. :confused:"

##################### Utility functions ######################

def getRepo(text):
    strtext = ""
    slashparts = text.split('/')
    if text == "" or len(slashparts)<=1 or slashparts[1] == "":
        strtext =  "Please enter the deatils in proper order"
        return strtexts
    url = 'https://api.github.com/repos/'+ slashparts[0] + '/' + slashparts[1]
    req = requests.get(url)
    resp = req.json()
    finalstr = ""
    if 'message' not in resp:
        resplist = [resp['language'],str(resp['forks']),str(resp['open_issues']),resp['html_url']]
        strlist = ["Majority of the repo is written in ","No of Forks made ","No of open issues for this repo is ","Check here: "]
        for i in range(0,4):
            strlist[i] = strlist[i] + resplist[i]
        for j in range(0,3):
            finalstr = finalstr + strlist[j] + '\n'
        finalstr = finalstr + strlist[3]
        return finalstr
    else:
        finalstr = "We could not find the result" + '\n' + "Make sure you entered the correct details :confused:"
        return finalstr

def getIssue(text):
    strtext = ""
    slashparts = text.split('/')
    if text == "" or len(slashparts)<=2 or slashparts[2] == "":
        strtext = "Please enter the deatils in proper order"
        return strtext
    url = 'https://api.github.com/repos/'+ slashparts[0] + '/' + slashparts[1] + '/issues/' + slashparts[2]
    r = requests.get(url)
    resp = r.json()
    finalstr = ""
    if 'message' not in resp:
        resplist = [resp['title'],resp['user']['login'],resp['state'],resp['html_url']]
        strlist = ["Issue title: ","Issue was opened by ","The issue is ","Check here: "]
        for i in range(0,4):
            strlist[i] = strlist[i] + resplist[i]
        for j in range(0,3):
            finalstr = finalstr + strlist[j] + '\n'
        finalstr = finalstr + strlist[3]
        return finalstr
    else:
        finalstr = "We could not find the result" + '\n' + "Make sure that the particular issue exists :confused:"
        return finalstr

def getHelp(text):
    str1 = ":robot_face: Bot works on the following Slash commands: \n"
    sl_str = ["/repo <org_name>/<repo_name> \n","/issue <org_name>/<repo_name>/<issue_no> \n","/branch <org_name>/<repo_name>/<branch_name> \n","/member <org_name> \n","/tag <org_name>/<repo_name>"]
    for i in range(0,5):
        str1 = str1 + sl_str[i]
    return str1

def getBranch(text):
    strtext = ""
    slashparts = text.split('/')
    if text == "" or len(slashparts)<=2 or slashparts[2] == "":
        strtext = "Please enter the deatils in proper order"
        return strtext
    url = 'https://api.github.com/repos/'+ slashparts[0] + '/' + slashparts[1] + '/branches/' + slashparts[2]
    r = requests.get(url)
    resp = r.json()
    finalstr = ""
    if 'message' not in resp:
        resplist = [resp['commit']['author']['login'],resp['commit']['commit']['message'],resp['commit']['html_url']]
        strlist = ["Author of this branch: ","Message: ","Check here: "]
        for i in range(0,3):
            strlist[i] = strlist[i] + resplist[i]
        for j in range(0,2):
            finalstr = finalstr + strlist[j] + '\n'
        finalstr = finalstr + strlist[2]
        return finalstr
    else:
        finalstr = "We could not find the result" + '\n' + "Are u sure about the typo :confused:??"
        return finalstr

def getMember(text):
    strtext = ""
    if text == "":
        strtext = "Please enter the deatils in proper order"
        return strtext
    url = 'https://api.github.com/orgs/'+text+'/public_members'
    r = requests.get(url)
    resp = r.json()
    finalstr = ""
    fstr = ""
    if 'message' not in resp:
        i = len(resp)
        for j in range(0,i):
            fstr = fstr + resp[j]['login'] + " "
        finalstr = "Your organisation has " + fstr + "as their public members"
        return finalstr
    else:
        finalstr = "We could not find the result" + '\n' + "Make sure that the particular organisation exists :confused:"
        return finalstr

def getTag(text):
    strtext = ""
    slashparts = text.split('/')
    if text == "" or len(slashparts)<=1 or slashparts[1] == "":
        strtext =  "Please enter the deatils in proper order"
        return strtexts
    url = 'https://api.github.com/repos/'+ slashparts[0] + '/' + slashparts[1] +'/tags'
    req = requests.get(url)
    resp = req.json()
    finalstr = ""
    if 'message' not in resp:
        i = len(resp)
        if i != 0:
            finalstr = "The most recent release present for this repo is " + resp[0]['name']
        else:
            finalstr = "No tags are present in this repo :disappointed:"
        return finalstr
    else:
        finalstr = "We could not find the result" + '\n' + "Make sure you entered the correct details :confused:"
        return finalstr

def sendConfirmation(id, message, responseUrl):
    payload = {
        "text": "Are you sure you want to send a message?",
        "attachments": [
            {
                "text": '"'+message+'"',
                "fallback": "You are indecisive",
                "callback_id": id,
                "color": "#3AA3E3",
                "attachment_type": "default",
                "actions": [
                    {
                        "name": "yes",
                        "text": "Yep",
                        "type": "button",
                        "value": "yes"
                    },
                    {
                        "name": "no",
                        "text": "Nope",
                        "type": "button",
                        "value": "no"
                    }
                ]
            }
        ]
    }
    headers = {
        'content-type': "application/json",
    }

    response = requests.request("POST", responseUrl, data=json.dumps(payload), headers=headers)
    print(response.text)

def storeMsgToDB(text):
    """
        This function stores 'text' in the database, and
        takes note of the auto-generated unique id for the message.

        The table it stores it in is:
        +-------------------------+----------------+
        | id (auto-increment int) | message (text) |
        +-------------------------+----------------+

        Instead of contacting the postgres database directly
        this function uses the Hasura Data APIs.

        Try out the data APIs by running this from your terminal:
        $ hasura api-console

        Use the query builder and the API explorer to try out the
        data APIs.
    """
    requestPayload = {
        "type": "insert",
        "args": {
            "table": "slack_messages",
            "objects": [
                {
                    "message": text,
                }
            ],
            "returning": [
                "id"
            ]
        }
    }

    # Setting headers
    headers = {
        "Content-Type": "application/json",
        "X-Hasura-User-Id": "1",
        "X-Hasura-Role": "admin"
    }

    # Make the query and store response in resp
    resp = requests.request("POST", hasuraDataUrl, data=json.dumps(requestPayload), headers=headers)
    respObj = resp.json()
    print(respObj)
    id = respObj["returning"][0]["id"]
    return id

def fetchFromDBAndSend(id, channel):
    """
        This function fetches the stored message from the database.

        The table it fetches from is:
        +-------------------------+----------------+
        | id (auto-increment int) | message (text) |
        +-------------------------+----------------+

        Instead of contacting the postgres database directly
        this function uses the Hasura Data APIs.

        Try out the data APIs by running this from your terminal:
        $ hasura api-console

        Use the query builder and the API explorer to try out the
        data APIs.
    """
    requestPayload = {
        "type": "select",
        "args": {
            "table": "slack_messages",
            "columns": [
                "message",
            ],
            "where": {
                "id": {
                    "$eq": id
                }
            }
        }
    }

    # Setting headers
    headers = {
        "Content-Type": "application/json",
        "X-Hasura-User-Id": "1",
        "X-Hasura-Role": "admin"
    }

    # Make the query and store response in resp
    resp = requests.request("POST", hasuraDataUrl, data=json.dumps(requestPayload), headers=headers)
    respObj = resp.json()
    print(respObj)
    message = respObj[0]["message"]
    return sendSlackMessage(message, channel)

def sendSlackMessage(message, channel):
    payload = {
        "token": botAccessToken,
        "text": message,
        "channel": channel
    }
    headers = {
        'content-type': "application/json",
        'Authorization': 'Bearer '+botAccessToken
    }

    response = requests.request("POST", chatUrl, data=json.dumps(payload), headers=headers)
    print(response.json())
    return message


import re
from io import StringIO

from flask import Flask, request, Response, redirect
import pandas as pd

app = Flask(__name__)

def is_valid_query(q):
    '''
    A query is valid if it is strictly consisted of the following three entries:
    [(A-Z, a-z, 0-9)+ or *] [==, !=, $=, &=] ["..."]
    Queries can be concat with 'and' & 'or'. The 'and' 'or' operators are executed in sequential order.
    Entries and operators must be separated by at least one single space. i.e. {C1=="a"} is not acceptable.
    Additional white spaces are allowed between entries.
    For the "" inside the query, the query term is the content wrapped by the first and last occurance of "".
    In the processing of query term, any sequence of consecutive spaces is reduced to a single space for clarity.
    (since consecutive spaces usually do not convey any semantic meanings in phrases)

    Output:
    a message indicating the validity of query ("valid" or error message),
    a list of valid queries (each query is represented by a 3-element list),
    a list of and/or operators
    '''
    entries = q.split()
    valid_q = [] # a 3-element list consist of 3 valid entries
    queries = [] # list of valid_q
    operators = [] # operators between queries
    operand = ['==', '!=', '$=', '&='] # valid operand

    # check the valid status of three entries defined above
    valid_first = False
    valid_second = False
    valid_third = False

    i = 0
    while(i < len(entries)):
        if not valid_first:
            column = re.findall('[A-Za-z0-9]+|\*', entries[i])
            # if valid, must be exactly one match
            # i.e. "abc*123" will give three matches and is invalid
            if len(column) != 1 or column[0] != entries[i]:
                return "Invalid column name", queries, operators
            else:
                valid_q.append(entries[i])
                valid_first = True

        elif not valid_second:
            if entries[i] not in operand:
                return "Invalid operator, must be ==, !=, $=, &=", queries, operators
            else:
                # store as int if only numbers
                valid_q.append(entries[i])
                valid_second = True

        elif not valid_third:
            if entries[i][0] != '\"':
                return "Invalid query term, must begin with \"", queries, operators
            else: # traverse the list to find the last " before the next query
                term = ""
                # find the string before next query
                if entries[i:].count('and') > 0:
                    end = entries[i:].index('and') + i
                    term = " ".join(entries[i:end])
                elif entries[i:].count('or') > 0:
                    end = entries[i:].index('or') + i
                    term = " ".join(entries[i:end])
                else:
                    end = len(entries)
                    term = " ".join(entries[i:])
                # test the validity of term
                if term[-1] != '\"':
                    return "Invalid query term, must end with \"", queries, operators
                else:
                    i = end
                    valid_q.append(term[1:-1]) # remove the front and end "" when storing
                    valid_third = True
                    continue

        else:
            if i == len(queries) - 1:
                return "Extra term after queries", queries, operators
            if entries[i] == 'and' or entries[i] == 'or':
                queries.append(valid_q)
                operators.append(entries[i])
                valid_q = []
                valid_first = valid_second = valid_third = False
            else:
                return "Invalid and/or operand between queries", queries, operators
        i += 1

    # append the last valid query and check incomplete query
    if valid_first and valid_second and valid_third:
        queries.append(valid_q)
    else:
        return "Missing entries in queries", queries, operators

    return "valid", queries, operators

def match_query(queries, operators, df):
    '''
    This function matches the queries associated with the operators to df.
    Output:
    a message indicating the validity of query matching ('valid' or error message)
    matched rows in df
    '''
    columns = df.columns.tolist()
    res_df = pd.DataFrame(columns = columns) # empty df to append matching rows

    for i,q in enumerate(queries):
        # if this is the first query or the operator connecting pervious query is 'or', check the entire df
        if i - 1 < 0 or operators[i - 1] == 'or':
            cur_df = df.astype(str) # convert the content of df to string for comparison
        elif operators[i - 1] == 'and':
            cur_df = res_df

            # select rows from df
        if q[0] == "*":
            select_df = pd.DataFrame(columns = columns) # empty df to append matching rows
            for (col, _) in cur_df.iteritems():
                if q[1] == "==":
                    select_df = select_df.append(cur_df[cur_df[col] == q[2]])
                elif q[1] == "!=":
                    drop_df = cur_df[cur_df[col] == q[2]]
                    select_df = select_df.append(cur_df.drop(index=drop_df.index.tolist()))
                elif q[1] == "$=":
                    select_df = select_df.append(cur_df[cur_df[col].str.lower().isin([q[2].lower()])])
                elif q[1] == "&=":
                    select_df = select_df.append(cur_df[cur_df[col].str.contains(q[2], case=True)])
            cur_df = select_df.drop_duplicates(keep='first')
        else:
            if q[0] not in columns:
                return 'No corresponding column name in data', res_df
            elif q[0] not in cur_df.columns:
                cur_df = pd.DataFrame(columns = columns) # no matching column, set the cur_df to empty
            else:
                if q[1] == "==":
                    cur_df = cur_df[cur_df[q[0]] == q[2]]
                elif q[1] == "!=":
                    drop_df = cur_df[cur_df[q[0]] == q[2]]
                    cur_df = cur_df.drop(index=drop_df.index.tolist())
                elif q[1] == "$=":
                    cur_df = cur_df[cur_df[q[0]].str.lower().isin([q[2].lower()])]
                elif q[1] == "&=":
                    cur_df = cur_df[cur_df[q[0]].str.contains(q[2], case=True)]

        # update res_df according to 'and' 'or' operators
        if i - 1 < 0 or operators[i - 1] == 'or':
            res_df = res_df.append(cur_df)
            res_df.drop_duplicates(keep='first',inplace=True)
        elif operators[i - 1] == 'and':
            res_df = cur_df

    if res_df.empty:
        return 'No corresponding items for the query', res_df
    return 'valid', res_df

@app.route('/')
def get_info():
    args = request.args
    query = args['query']

    # '&' will separate the query to two items, append it back
    for key, val in args.items():
        if key == 'query':
            continue
        if key == '':
            query += '&=' + val
        else:
            query += '&' + key
    print(query)

    # Query error checking and parsing
    mes, queries, operators = is_valid_query(query)
    if mes != "valid":
        return mes
    print(queries)
    print(operators)

    # Query match
    df = pd.read_csv('data.csv')
    mes, res_df = match_query(queries, operators, df)
    if mes != "valid":
        return mes

    res_df.to_csv('res.csv')

    return '''

        The query has been successfully processed.
        To download the extracted results in a csv file, click me.

        '''

@app.route("/getCSV")
def getCSV():
    output = StringIO()
    df = pd.read_csv('res.csv')
    df.to_csv(output)

    return Response(
        output.getvalue(),
        mimetype="text/csv",
        headers={"Content-disposition":"attachment; filename=res.csv"})

if __name__ == '__main__':
   app.run(host='127.0.0.1', port=9527)


import collections
import difflib
import logging
import os
import re

from pytype.platform_utils import path_utils
from pytype.tools.merge_pyi import merge_pyi
import unittest

__all__ = ('TestBuilder', 'load_tests')

PY, PYI, EXPECTED = 'py', 'pyi', 'pep484.py'
OVERWRITE_EXPECTED = 0  # flip to regenerate expected files

def load_tests(unused_loader, standard_tests, unused_pattern):
  root = path_utils.join(path_utils.dirname(__file__), 'test_data')
  standard_tests.addTests(TestBuilder().build(root))
  return standard_tests

class TestBuilder:

  def build(self, data_dir):
    """Return a unittest.TestSuite with tests for the files in data_dir."""

    suite = unittest.TestSuite()

    files_by_base = self._get_files_by_base(data_dir)

    for base, files_by_ext in sorted(files_by_base.items()):
      if not (PY in files_by_ext and PYI in files_by_ext):
        continue

      if not OVERWRITE_EXPECTED and EXPECTED not in files_by_ext:
        continue

      py, pyi = (files_by_ext[x] for x in (PY, PYI))
      outfile = path_utils.join(data_dir, base + '.' + EXPECTED)

      test = build_regression_test(py, pyi, outfile)
      suite.addTest(test)

    return suite

  def _get_files_by_base(self, data_dir):
    files = os.listdir(data_dir)

    file_pat = re.compile(r'(?P<filename>(?P<base>.+?)\.(?P<ext>.*))$')
    matches = [m for m in map(file_pat.match, files) if m]
    ret = collections.defaultdict(dict)
    for m in matches:
      base, ext, filename = m.group('base'), m.group('ext'), m.group('filename')
      ret[base][ext] = path_utils.join(data_dir, filename)

    return ret

def build_regression_test(py, pyi, outfile):

  def regression_test(test_case):
    py_input, pyi_src = (_read_file(f) for f in (py, pyi))
    try:
      output = merge_pyi.merge_sources(py=py_input, pyi=pyi_src)
    except merge_pyi.MergeError:
      pass

    if OVERWRITE_EXPECTED:
      with open(outfile, 'w') as f:
        f.write(output)
    else:
      expected = _read_file(outfile)
      test_case.assertEqual(expected, output, _get_diff(expected, output))

  name = path_utils.splitext(path_utils.basename(outfile))[0].replace('.', '_')
  test = f'test_{name}'
  case = type('RegressionTest', (unittest.TestCase,), {test: regression_test})
  return case(test)

def _read_file(filename):
  with open(filename) as f:
    return f.read()

def _get_diff(a, b):
  a, b = a.split('\n'), b.split('\n')

  diff = difflib.Differ().compare(a, b)
  return '\n'.join(diff)

if __name__ == '__main__':
  logging.basicConfig(level=logging.CRITICAL)
  unittest.main()


import pandas as pd
import plotly.graph_objects as go
import prepare_data

population = {
    'NSW':8089526,
    'QLD':5095100,
    'VIC':6594804,
    'SA':1751693,
    'WA':2621680,
    'TAS':534281,
    'ACT':426709,
    'NT':245869,
    'Total':25359662,
    'DeathsNationally':25359662,
}

df_aus = prepare_data.australia()
df_aus_change = prepare_data.australia_change(df_aus)

# Let's plot this mofo
fig = go.Figure()

# Plot all the states!
for state in list(df_aus):
    fig.add_trace(go.Scatter(
        x=df_aus.index,
        y=pd.to_numeric(df_aus[state]).divide(population[state])*100000,
        name=state,
    ))

# Make the plot look fancy.
fig.update_layout(title='Per Capita COVID-19 Cases by State/Territory in Austalia',
                   xaxis_title='Date',
                   yaxis_title='Cases per 100,000 people')
fig.show()

# Let's plot this mofo
fig_change = go.Figure()

# Plot all the states!
for state in list(df_aus_change):
    fig_change.add_trace(go.Scatter(
        x=df_aus_change.index,
        y=pd.to_numeric(df_aus_change[state]).divide(population[state])*100000,
        name=state,
    ))

# Make the plot look fancy.
fig_change.update_layout(title='Per Capita Change in COVID-19 Cases by State/Territory in Austalia',
                         xaxis_title='Date',
                         yaxis_title='Change in cases per 100,000 people')
fig_change.show()

# Roll those numbers over a week
df_aus_change = df_aus_change.rolling(7).mean()

# Let's plot this mofo
fig_rolling_change = go.Figure()

# Plot all the states!
for state in list(df_aus):
    fig_rolling_change.add_trace(go.Scatter(
        x=df_aus_change.index,
        y=pd.to_numeric(df_aus_change[state]).divide(population[state])*100000,
        name=state,
    ))

# Make the plot look fancy.
fig_rolling_change.update_layout(
    title='7-day Rolling Per Capita Change in COVID-19 Cases by State/Territory in Austalia',
    xaxis_title='Date',
    yaxis_title='Change in cases per 100,000 people'
)

fig_rolling_change.show()


class Node(object):
    def __init__(self, key, val):
        self.val = val
        self.key = key
        self.next = None
        self.prev = None

class List(object):
    def __init__(self):
        self.head = Node(None, None)
        self.tail = None

    def append(self, node):
        if self.tail is None:
            self.head.next = node
            node.prev = self.head
            self.tail = node
        else:
            self.tail.next = node
            node.prev = self.tail
            self.tail = node

    def push_to_front(self, node):
        if node is None:
            return

        if self.tail is node:
            return

        node.prev.next = node.next
        node.next.prev = node.prev
        node.prev = self.tail
        node.next = None
        self.tail.next = node
        self.tail = self.tail.next

    def evict(self):
        node = self.head.next
        self.head.next = self.head.next.next
        self.head.next.prev = self.head
        return node

class LRUCache(object):
    def __init__(self, capacity):
        self.capacity = capacity
        self.size = 0
        self.map = {}
        self.list = List()

    def get(self, key):
        if key not in self.map:
            return None

        node = self.map[key]
        self.list.push_to_front(node)
        if node:
            return node.val
        return None

    def set(self, key, value):
        if key in self.map:
            node = self.map[key]
            self.list.push_to_front(node)
            return

        if self.size == self.capacity:
            node = self.list.evict()
            del self.map[node.key]
        else:
            self.size += 1
        node = Node(key, value)
        self.list.append(node)
        self.map[key] = node

if __name__ == "__main__":
    cache = LRUCache(3)
    cache.set(1, "hello")
    cache.set(2, "world")
    cache.set(3, "!!!")
    cache.set(4, "!!!")

    print(cache.get(1))
    print(cache.get(2))
    print(cache.get(3))
    print(cache.get(4))
    print(cache.get(2))
    cache.set(5, "match")
    cache.set(6, "all")
    print(cache.get(5))
    print(cache.get(6))
    print(cache.get(2))
    print(cache.get(4))
    print(cache.get(3))


import itertools
import numpy as np
import networkx as nx
from sklearn.neighbors import kneighbors_graph
from sklearn.metrics.pairwise import euclidean_distances
from scipy.sparse.csgraph import minimum_spanning_tree

from ggc.utils import *

def knn_graph(X, k):
    """Returns k-Nearest Neighbor (MkNN) graph from the feature matrix.

    Parameters
    ----------
    X : ndarray, shape (N, F)
        N samples and F-dimensional features.
    k : int, k >= 1
        Parameter for knn: the k-th nearest neighbour.

    Returns
    -------
    adj : ndarray, shape (N, N)
        The adjacency matrix of the constructed knn graph.
    """
    assert k < X.shape[0]

    adj_directed = kneighbors_graph(X=X,
                            n_neighbors=k,
                            p=2,
                            include_self=False,
                            ).toarray()
    adj = adj_directed + adj_directed.T
    adj[adj > 0] = 1
    np.fill_diagonal(adj,0)

    return adj

def mknn_graph(X, k):
    """Returns Mutual k-Nearest Neighbor (MkNN) graph from the feature matrix.

    Parameters
    ----------
    X : ndarray, shape (N, F)
        N samples and F-dimensional features.
    k : int, k >= 1
        Parameter for mknn: the k-th nearest neighbour.

    Returns
    -------
    adj : ndarray, shape (N, N)
        The adjacency matrix of the constructed mknn graph.
    """
    assert k < X.shape[0]

    adj_directed = kneighbors_graph(X=X,
                            n_neighbors=k,
                            p=2,
                            include_self=False,
                            ).toarray()
    adj = adj_directed + adj_directed.T
    adj[adj < 2] = 0
    adj[adj >= 2] = 1
    np.fill_diagonal(adj,0)

    return adj

def cknn_graph(X, delta, k):
    """Returns Continuous k-Nearest Neighbor (CkNN) graph from the feature matrix.

    Parameters
    ----------
    X : ndarray, shape (N, F)
        N samples and F-dimensional features.
    delta : float, delta > 0
        Parameter for cknn.
    k : int, k >= 1
        Parameter for cknn: the k-th nearest neighbour.

    Returns
    -------
    adj : ndarray, shape (N, N)
        The adjacency matrix of the constructed cknn graph.

    References
    ----------
    .. [1] Tyrus Berry, Timothy Sauer. Consistent manifold representation for topological data analysis.
           Foundations of Data Science, 2019, 1 (1) : 1-38. doi: 10.3934/fods.2019001
    """
    assert k < X.shape[0]

    D = euclidean_distances(X, X)
    N = D.shape[0]
    np.fill_diagonal(D,0)
    D_k = np.sort(D)

    adj = np.zeros([N, N])
    adj[np.square(D) < delta * delta * np.dot(D_k[:,k].reshape(-1,1),D_k[:,k].reshape(1,-1))] = 1
    np.fill_diagonal(adj,0)

    return adj

def mst_graph(X):
    """Returns Minimum Spanning Tree (MST) graph from the feature matrix.

    Parameters
    ----------
    X : ndarray, shape (N, F)
        N samples and F-dimensional features.

    Returns
    -------
    adj : ndarray, shape (N, N)
        The adjacency matrix of the constructed mst graph.
    """
    D = euclidean_distances(X, X)
    adj_directed = minimum_spanning_tree(D).toarray()
    adj = adj_directed + adj_directed.T
    adj[adj > 0] = 1
    np.fill_diagonal(adj,0)

    return adj

def rmst_graph(X, gamma, k):
    """Returns Relaxed Minimum Spanning Tree (RMST) graph from the feature matrix.

    Parameters
    ----------
    X : ndarray, shape (N, F)
        N samples and F-dimensional features.
    gamma : float, gamma > 0
        Parameter for rmst.
    k : int, k >= 1
        Parameter for rmst: the k-th nearest neighbour.

    Returns
    -------
    adj : ndarray, shape (N, N)
        The adjacency matrix of the constructed rmst graph.

    References
    ----------
    .. [1] Beguerisse-Díaz, Mariano, Borislav Vangelov, and Mauricio Barahona.
           "Finding role communities in directed networks using role-based similarity,
           markov stability and the relaxed minimum spanning tree."
           2013 IEEE Global Conference on Signal and Information Processing. IEEE, 2013.
    """
    D = euclidean_distances(X, X)
    N = D.shape[0]
    assert k < N
    np.fill_diagonal(D,0)

    adj = np.zeros([N, N])

    D_k = np.sort(D)
    D_k = np.tile(D_k[:,k],(N,1))
    D_k = gamma * (D_k + D_k.T)
    np.fill_diagonal(D_k,0)

    max_weight = np.zeros((N,N))
    G = nx.Graph(D)
    T = nx.minimum_spanning_tree(G)
    path = dict(nx.all_pairs_dijkstra_path(T))
    for i,j in itertools.combinations(range(N),2):
        p = path[i][j]
        path_weight = np.zeros(len(p)-1)
        for k in range(len(p)-1):
            path_weight[k] = T.edges[p[k],p[k+1]]['weight']
        max_weight[i][j] = np.amax(path_weight)
    max_weight = max_weight + max_weight.T
    np.fill_diagonal(max_weight,0)

    adj[D < (max_weight + D_k)] = 1
    np.fill_diagonal(adj,0)

    return adj


import re

def get_puzzle_input(file: str) -> list[str]:
    return [line.strip() for line in open(f"{file}.txt", "r").readlines()]

def create_list(r1: int, r2: int) -> list:
    return list(range(r1, r2 + 1))

def parse_input(sections: list) -> list:
    ranges = []
    for section in sections:
        input = re.split(",|-", section)
        elf_1 = create_list(int(input[0]), int(input[1]))
        elf_2 = create_list(int(input[2]), int(input[3]))
        ranges.append([elf_1, elf_2])
    return ranges

def sort_sections(elves: list) -> list:
    sorted_sections = []
    for elf in elves:
        pass
        if len(elf[0]) >= len(elf[1]):
            outer, inner = elf[0], elf[1]
        else:
            outer, inner = elf[1], elf[0]
        sorted_sections.append([outer, inner])
    return sorted_sections

def solution(sections: list) -> int:
    dubble_work = 0
    overlapping = 0
    for outer, inner in sections:
        all_in = []
        for section in inner:
            if section in outer:
                all_in.append(True)
            else:
                all_in.append(False)

        if all(all_in):
            dubble_work += 1
        if any(all_in):
            overlapping += 1
    return dubble_work, overlapping

sections = get_puzzle_input("input")
ranges = parse_input(sections)
sorted_sections = sort_sections(ranges)
solutions = solution(sorted_sections)
print(solutions)


# Thư viện
import pygame, sys
import numpy as np
import time
# Khởi tạo game
pygame.init()

# ---------
# CÁC HẰNG SỐ
# ---------
WIDTH = 600
HEIGHT = WIDTH
LINE_WIDTH = 15
WIN_LINE_WIDTH = 8
BOARD_ROWS = 5
BOARD_COLS = BOARD_ROWS
SQUARE_SIZE = WIDTH/BOARD_ROWS
CIRCLE_RADIUS = SQUARE_SIZE/3
CIRCLE_WIDTH = 15
CROSS_WIDTH = 25
SPACE = SQUARE_SIZE/4

RED = (235, 47, 6)
BG_COLOR = (72, 84, 96)
LINE_COLOR = (23, 145, 135)
CIRCLE_COLOR = (255, 211, 42)
CROSS_COLOR = (186, 220, 88)
WIN_COLOR = (66, 66, 66)

# ---------
# VARIABLES
# ---------
player = 1
game_over = False

# ------
# SCREEN
# ------
screen = pygame.display.set_mode( (WIDTH, HEIGHT) )
pygame.display.set_caption( 'Isolation' )
screen.fill( BG_COLOR )

# -------------
# CONSOLE BOARD
# -------------
board = np.zeros( (BOARD_ROWS, BOARD_COLS) )

# ---------
# FUNCTIONS
# ---------
def draw_lines():

	for i in range(1,BOARD_ROWS):
		# horizontal
		pygame.draw.line( screen, LINE_COLOR, (0, SQUARE_SIZE*i), (WIDTH, SQUARE_SIZE*i), LINE_WIDTH )

	for i in range(1,BOARD_COLS):
		# vertical
		pygame.draw.line( screen, LINE_COLOR, (i * SQUARE_SIZE, 0), (i * SQUARE_SIZE, HEIGHT), LINE_WIDTH )

def draw_figures():
    	for row in range(BOARD_ROWS):
            for col in range(BOARD_COLS):
                if board[row][col] == 1:
                    pygame.draw.circle( screen, CIRCLE_COLOR, (int( col * SQUARE_SIZE + SQUARE_SIZE//2 ), int( row * SQUARE_SIZE + SQUARE_SIZE//2 )), CIRCLE_RADIUS, CIRCLE_WIDTH )
                elif board[row][col] == 2:
                    pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SPACE), CROSS_WIDTH )
                    pygame.draw.line( screen, CROSS_COLOR, (col * SQUARE_SIZE + SPACE, row * SQUARE_SIZE + SPACE), (col * SQUARE_SIZE + SQUARE_SIZE - SPACE, row * SQUARE_SIZE + SQUARE_SIZE - SPACE), CROSS_WIDTH )

def mark_square(row, col, player):
	board[row][col] = player
	# print ("----------------------------------------------------")
	# print("Player " + str(player) + " marked square : (" + str(row) + "," + str(col) + ")")
	# print(board)
	# print ("----------------------------------------------------")

def available_square(row, col):
    return board[row][col] == 0

def is_board_full():
	for row in range(BOARD_ROWS):
		for col in range(BOARD_COLS):
			if board[row][col] == 0:
				return False

	return True

WIN_LENGTH = 4

def check_win(player):
    # Dọc
    for col in range(BOARD_COLS):
        for row in range(BOARD_ROWS - (WIN_LENGTH - 1)):
            if all(board[row+i][col] == player for i in range(WIN_LENGTH)):
                draw_vertical_winning_line(col, row, player)
                return True

    # Ngang
    for row in range(BOARD_ROWS):
        for col in range(BOARD_COLS - (WIN_LENGTH - 1)):
            if all(board[row][col+i] == player for i in range(WIN_LENGTH)):
                draw_horizontal_winning_line(col, row, player)
                return True

    # Chéo trái
    for row in range(BOARD_ROWS - (WIN_LENGTH - 1)):
        for col in range(BOARD_COLS - (WIN_LENGTH - 1)):
            if all(board[row+i][col+i] == player for i in range(WIN_LENGTH)):
                draw_asc_diagonal(col, row, player)
                return True

    # Chéo phải
    for row in range((WIN_LENGTH - 1),BOARD_ROWS):
        for col in range(BOARD_ROWS - (WIN_LENGTH - 1)):

            if all(board[row-i][col+i] == player for i in range(WIN_LENGTH)):
                draw_desc_diagonal(row, col, player)
                # print(row,col)
                return True

    return False

# =========
# Hàm vẽ đường win
# =========
def draw_vertical_winning_line(col, row, player):
    x = int(col * SQUARE_SIZE + SQUARE_SIZE / 2)
    y1 = int(row * SQUARE_SIZE + SQUARE_SIZE / 2) - 48
    y2 = int((row + (WIN_LENGTH - 1)) * SQUARE_SIZE + SQUARE_SIZE / 2) + 48
    pygame.draw.line(screen, WIN_COLOR, (x, y1), (x, y2), WIN_LINE_WIDTH)

def draw_horizontal_winning_line(col, row, player):
    x1 = int(col * SQUARE_SIZE + SQUARE_SIZE / 2) - 48
    x2 = int((col + (WIN_LENGTH - 1)) * SQUARE_SIZE + SQUARE_SIZE / 2) + 48
    y = int(row * SQUARE_SIZE + SQUARE_SIZE / 2)
    pygame.draw.line(screen, WIN_COLOR, (x1, y), (x2, y), WIN_LINE_WIDTH)

def draw_asc_diagonal(col, row, player):
    x1 = int(col * SQUARE_SIZE + SQUARE_SIZE / 2)
    y1 = int(row * SQUARE_SIZE + SQUARE_SIZE / 2)
    x2 = int((col + (WIN_LENGTH - 1)) * SQUARE_SIZE + SQUARE_SIZE / 2)
    y2 = int((row + (WIN_LENGTH - 1)) * SQUARE_SIZE + SQUARE_SIZE / 2)
    pygame.draw.line(screen, WIN_COLOR, (x1, y1), (x2, y2), WIN_LINE_WIDTH)

def draw_desc_diagonal(row,col, player):
    x1 = int(col * SQUARE_SIZE + SQUARE_SIZE / 2)
    y1 = int(row * SQUARE_SIZE + SQUARE_SIZE / 2)
    x2 = int((col+WIN_LENGTH-1)* SQUARE_SIZE + SQUARE_SIZE / 2)
    y2 = int((row-WIN_LENGTH+1)* SQUARE_SIZE + SQUARE_SIZE / 2)
    pygame.draw.line(screen, WIN_COLOR, (x1,y1), (x2, y2), WIN_LINE_WIDTH)
def restart():
	screen.fill( BG_COLOR )
	draw_lines()
	for row in range(BOARD_ROWS):
		for col in range(BOARD_COLS):
			board[row][col] = 0

def checkWinner():
    # Ngang
    for row in range(BOARD_ROWS):
        for col in range(BOARD_COLS - 3):
            if board[row][col] == board[row][col+1] == board[row][col+2] == board[row][col+3] != 0:
                return board[row][col]

    # Dọc
    for row in range(BOARD_ROWS - 3):
        for col in range(BOARD_COLS):
            if board[row][col] == board[row+1][col] == board[row+2][col] == board[row+3][col] != 0:
                return board[row][col]

    # Chéo xuống
    for row in range(BOARD_ROWS - 3):
        for col in range(BOARD_COLS - 3):
            if board[row][col] == board[row+1][col+1] == board[row+2][col+2] == board[row+3][col+3] != 0:
                return board[row][col]

    # Chéo lên
    for row in range(3, BOARD_ROWS):
        for col in range(BOARD_COLS - 3):
            if board[row][col] == board[row-1][col+1] == board[row-2][col+2] == board[row-3][col+3] != 0:
                return board[row][col]

    # Đường chéo từ phải xuống trái
    for row in range(BOARD_ROWS - 3):
        for col in range(BOARD_COLS - 3):
            if board[row][col+3] == board[row+1][col+2] == board[row+2][col+1] == board[row+3][col] != 0:
                return board[row][col+3]

    # Hòa
    for row in range(BOARD_ROWS):
        for col in range(BOARD_COLS):
            if board[row][col] == 0:
                return None

    return 0
def numberplay():
    n = 0
    for row in range(BOARD_ROWS):
        for col in range(BOARD_COLS):
            if(board[row][col] == 2):
                n = n + 1
    if((n//2)%2==0):
        return(n//2)
    else:
        return ((n//2)-1)
mytime = 0
def bestMove():
    global mytime
    n = 3
    start_time = time.time()
    bestScore = -100000
    move = None
    empty_cells = [(row, col) for row in range(BOARD_ROWS) for col in range(BOARD_COLS) if board[row][col] == 0]
    if not empty_cells:
        return (-1, -1)

    for row, col in empty_cells:
        board[row][col] = 2
        score = minimax(board, 0,n, -100000, 100000, False)
        board[row][col] = 0

        if score > bestScore:
            bestScore = score
            move = (row, col)
    if move:
        mark_square(move[0], move[1], 2)
        draw_figures()
    end_time = time.time()
    elapsed_time = end_time - start_time
    mytime = mytime + elapsed_time
    print("Caro_5x5:time to make first move :%f"%(mytime))
    return move

scores = {
  1: -10,
  2: 10,
  0: 0
}
i = 0
def minimax(board, depth,depthmax, alpha, beta, isMaximizing):
    global i
    i = i+1
    print(i)
    result = checkWinner()
    if result is not None:
        return scores[result]
    if isMaximizing:
        bestScore = -100000
        for row in range(BOARD_ROWS):
            for col in range(BOARD_COLS):
                if board[row][col] == 0:
                    board[row][col] = 2
                    if(depth > depthmax):
                        board[row][col] = 0
                        break
                    score = minimax(board,depth+1,depthmax, alpha, beta, False)
                    board[row][col] = 0
                    bestScore = max(score, bestScore)
                    alpha = max(alpha, bestScore)
                    if beta <= alpha:
                        break
        return bestScore

    else:
        bestScore = 100000
        for row in range(BOARD_ROWS):
            for col in range(BOARD_COLS):
                if board[row][col] == 0:
                    board[row][col] = 1
                    if(depth > depthmax):
                        board[row][col] = 0
                        break
                    score = minimax(board, depth+1,depthmax, alpha, beta, True)
                    board[row][col] = 0
                    bestScore = min(score, bestScore)
                    beta = min(beta, bestScore)
                    if beta <= alpha:
                        break
        return bestScore

draw_lines()

# --------
# MAINLOOP
# --------
while True:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            sys.exit()

        if event.type == pygame.MOUSEBUTTONDOWN and not game_over:

            mouseX = event.pos[0] # x
            mouseY = event.pos[1] # y

            clicked_row = int(mouseY // SQUARE_SIZE)
            clicked_col = int(mouseX // SQUARE_SIZE)

            if available_square( clicked_row, clicked_col ):

                player = 1
                mark_square( clicked_row, clicked_col, player )
                draw_figures()

                if check_win( player ):
                    font = pygame.font.SysFont(None, 100)
                    text = font.render("You win", True, pygame.Color(RED))
                    text_rect = text.get_rect(center=(WIDTH/2, HEIGHT/2))
                    screen.blit(text, text_rect)
                    game_over = True

                elif is_board_full():
                    font = pygame.font.SysFont(None, 100)
                    text = font.render("Hòa", True, pygame.Color(RED))
                    text_rect = text.get_rect(center=(WIDTH/2, HEIGHT/2))
                    screen.blit(text, text_rect)
                    game_over = True

                else:
                    player = 2
                    draw_figures()
                    pygame.display.update()
                    bestMove()
                    draw_figures()

                    if check_win( player ):
                        font = pygame.font.SysFont(None, 100)
                        text = font.render("Máy win", True, pygame.Color(RED))
                        text_rect = text.get_rect(center=(WIDTH/2, HEIGHT/2))
                        screen.blit(text, text_rect)
                        game_over = True

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_r:
                restart()
                player = 1
                game_over = False
                draw_figures()

    pygame.display.update()


# 1,2,3 더하기
import sys
input = sys.stdin.readline

case = []
for i in range(int(input())):
    case.append(int(input()))
maxNum = max(case)

dp = [0]*(maxNum+1)
dp[1] = 1
dp[2] = 2
dp[3] = 4
for i in range(4, maxNum+1):
    dp[i] = dp[i-3]+dp[i-2]+dp[i-1]
for i in case:
    print(dp[i])


import math

def merge_sort(array, left_bound, right_bound):
    if left_bound < right_bound:
        middle_bound = math.floor((left_bound + right_bound)/2)
        merge_sort(array, left_bound, middle_bound)
        merge_sort(array, middle_bound + 1, right_bound)
        merge(array, left_bound, middle_bound, right_bound)

def merge(array, left_bound, middle_bound, right_bound):
    temp_left = []
    temp_right = []
    temp_left.append(list(array[0][left_bound : middle_bound + 1]))
    temp_left.append(list(array[1][left_bound : middle_bound + 1]))
    temp_right.append(list(array[0][middle_bound + 1 : right_bound + 1]))
    temp_right.append(list(array[1][middle_bound + 1 : right_bound + 1]))

    i = 0
    j = 0
    for k in range(left_bound, right_bound):
        if (temp_left[1][i] <= temp_right[1][j]):
            array[0][k] = temp_left[0][i]
            array[1][k] = temp_left[1][i]
            i+=1
            if (i == len(temp_left[0])):
                array[0][k + 1 : right_bound + 1] = temp_right[0][j:]
                array[1][k + 1 : right_bound + 1] = temp_right[1][j:]
                break
        else:
            array[0][k] = temp_right[0][j]
            array[1][k] = temp_right[1][j]
            j+=1
            if (j == len(temp_right[0])):
                array[0][k + 1 : right_bound + 1] = temp_left[0][i:]
                array[1][k + 1 : right_bound + 1] = temp_left[1][i:]
                break

def count_inv(array, left_bound, right_bound):
    inversions = 0
    if left_bound < right_bound:
        middle_bound = math.floor((left_bound + right_bound)/2)
        inversions += count_inv(array, left_bound, middle_bound)
        inversions += count_inv(array, middle_bound + 1, right_bound)
        inversions += count_split_inv(array, left_bound, middle_bound, right_bound)
    return inversions

def count_split_inv(array, left_bound, middle_bound, right_bound):
    temp_left = array[left_bound : middle_bound + 1]
    temp_right = array[middle_bound + 1 : right_bound + 1]

    i = 0
    j = 0
    split_inversions = 0
    for k in range(left_bound, right_bound):
        if (temp_left[i] <= temp_right[j]):
            array[k] = temp_left[i]
            i+=1
            if (i == len(temp_left)):
                array[k + 1 : right_bound + 1] = temp_right[j:]
                break
        else:
            array[k] = temp_right[j]
            split_inversions += len(temp_left) - i
            j+=1
            if (j == len(temp_right)):
                array[k + 1 : right_bound + 1] = temp_left[i:]
                break
    return split_inversions

def read_first_row(filename):
    data = []
    with open(filename, "r") as file_object:
         data = file_object.readline().split(' ')
         data = list(map(int, data))
    return data

def read_comp_row(filename, user_index):
    user_data = []
    row = 0
    with open(filename, "r") as file_object:
         while (row != user_index):
             file_object.readline()
             row+=1
         user_data = file_object.readline().split(' ')
         user_data = list(map(int, user_data))
         user_data = user_data[1:]
    return user_data

def read_row(filename, user_index, order_user_data):
    user_data = []
    row = 0
    with open(filename, "r") as file_object:
         while (row != user_index):
             file_object.readline()
             row+=1
         user_data = file_object.readline().split(' ')
         user_data = list(map(int, user_data))
         user_data = user_data[1:]
    temp_data = user_data[:]
    for i in range(len(user_data)):
        user_data[order_user_data[i]-1] = temp_data[i]
    return user_data

def write_results(filename, user_index, inversions_array):
    output_file = open(filename, "w");
    output_file.writelines([str(user_index), '\n'])
    for i in range(0, len(inversions_array[0])):
        output_file.writelines([str(inversions_array[0][i]), ' ',
                                str(inversions_array[1][i]), '\n'])
    output_file.close()


import datetime
import time

from gpiozero import LED, Device
from gpiozero.pins.pigpio import PiGPIOFactory

Device.pin_factory = PiGPIOFactory()

# NOTE: Change this to match the GPIO pin you're connecting the LED to
led = LED(18)
# NOTE: Change these values to set the time you want the light to turn on and off at
weekday_on_time = datetime.time(hour=7, minute=0, second=0)
weekday_off_time = datetime.time(hour=17, minute=0, second=0)
weekend_on_time = datetime.time(hour=7, minute=30, second=0)
weekend_off_time = datetime.time(hour=17, minute=0, second=0)

while True:
    dayOfWeek = datetime.datetime.now().weekday()
    currentTime = datetime.datetime.now().time()
    on_time = weekday_on_time if dayOfWeek < 5 else weekend_on_time
    off_time = weekday_off_time if dayOfWeek < 5 else weekend_off_time
    if currentTime > on_time and currentTime < off_time:
        led.on()
    else:
        led.off()

    time.sleep(60)


""" This module contains testcase_32_ephemeral test """
from testcase import Testcase
from os.path import basename

class testcase_32_ephemeral(Testcase):
    """
    It should be possible to use ephemeral device (if we have one)
    Note that in rhel6.5 there is no shift letter in dick device name
    """
    stages = ['stage1']
    tags = ['default']
    after = ['testcase_21_disk_size_format']

    def test(self, connection, params):
        """ Perform test """

        prod = params['product'].upper()
        ver = params['version'].upper()
        ephemerals = []

        if 'bmap' in params:
            ephemerals = [bdev for bdev in params['bmap'] if 'ephemeral_name' in bdev]

        if not ephemerals:
            # are there are some devices to check?
            self.log.append({'result': 'skip',
                             'comment': 'no ephemeral devices in block map'
                             })
            return self.log

        # figure out what fstype to use with focus on mkfs.<fstype> speed
        fstype = None
        for fstype in ['vfat', 'xfs', 'ext3']:
            if self.get_result(connection, 'ls -la /sbin/mkfs.%s 2> /dev/null | wc -l' % fstype, 5) == '1':
                break

        if fstype == 'vfat':
            # so that mkfs.vfat /dev/<device> doesn't complain
            # because of making fs on whole drive instead of a partition
            mkfs_opt = '-I'
        else:
            mkfs_opt = ''

        devices = []
        for bdev in ephemerals:
            name = bdev['name']
            if (prod in ['RHEL', 'BETA']) and (ver.startswith('5.')):
                if name.startswith('/dev/xvd'):
                    # no xvd* for RHEL5
                    continue
            elif (prod in ['RHEL', 'BETA']) and (ver.startswith('6.') and ver != '6.0'):
                if name.startswith('/dev/sd'):
                    name = '/dev/xvd' + name[7:]
                if params['virtualization'] != 'hvm' and ver != '6.5' and len(name) == 9 and ord(name[8]) < ord('w'):
                    # there is a 4-letter shift
                    name = name[:8] + chr(ord(name[8]) + 4)
            else:
                # Fedora and newer RHELs
                if name.startswith('/dev/sd'):
                    name = '/dev/xvd' + name[7:]
            # test: check device presence
            self.get_return_value(connection, 'fdisk -l %s | grep \'^Disk\'' % name, 30)
            if self.get_result(connection, 'grep \'%s \' /proc/mounts  | wc -l' % name, 5) == '0':
                # device is not mounted, doing fs creation
                devices.append(name)

        if not devices:
            return self.log

        mkfs_commands = ['mkfs.%s %s %s' % (fstype, mkfs_opt, name) for name in devices]
        assert self.ping_pong(connection, ' & '.join(mkfs_commands) + ' & echo MKFS', '(?s).*\r\nMKFS.*'), "call mkfs_commands failed"
        assert self.ping_pong(connection, 'wait && echo WAIT', '(?s).*\r\nWAIT.*', 1000), "wait failed?!?!"
        dest_names = ['/tmp/mnt-%s' % basename(name) for name in devices]
        self.get_return_value(connection, 'mkdir -p ' + ' '.join(dest_names))
        mount_commands = ['mount -t %s %s /tmp/mnt-%s' % (fstype, name, basename(name)) for name in devices]
        self.get_return_value(connection, ' && '.join(mount_commands))
        return self.log


"""Module to index columns of the paper-summarized CSV file."""
import pandas as pd
from loguru import logger
from omegaconf import OmegaConf

from utils import create_embeddings

# Load the configuration
cfg = OmegaConf.load("conf/config.yaml")

FILE_PATH = cfg.data.path
INDEXED_FILE_PATH = cfg.data.indexed_path

df = pd.read_csv(cfg.data.path, compression="gzip", header=0)
logger.info(f"Loaded {len(df)} rows from {cfg.data.path}")
logger.info(f"columns: {df.columns}")

logger.info("Creating embeddings for experiment time")
df = create_embeddings(df, ["experiment time"])

logger.info("Creating embeddings for device")
df = create_embeddings(df, ["device"])

print(df.head(5))

logger.info(f"Saving indexed file to {INDEXED_FILE_PATH}")
df.to_csv(INDEXED_FILE_PATH, compression="gzip", index=False)

logger.success("Done!")


import rclpy
import rclpy.node
from airobot_interfaces.srv import StringCommand

from gtts import gTTS
import speech_recognition as sr
import subprocess

class SpeechService(rclpy.node.Node):
    def __init__(self):
        super().__init__('speech_service')

        self.get_logger().info('音声サーバーを起動しました')

        self.init_rec = sr.Recognizer()

        self.service = self.create_service(
            StringCommand, '/speech_service/wake_up', self.command_callback)

    def command_callback(self, request, response):

        self.synthesis('I\'m ready.')

        text = None
        while text is None:
            text = self.recognition()

        self.synthesis(text)

        response.answer = text
        return response

    def recognition(self):
        text = ''

        with sr.Microphone() as source:
            while text == '':
                audio_data = self.init_rec.record(source, duration=5)
                self.get_logger().info(f'音声認識を行います')

                try:
                    text = self.init_rec.recognize_google(audio_data)
                except sr.UnknownValueError:
                    pass

        self.get_logger().info(f'認識したテキストは "{text}" です')

        return text

    def synthesis(self, text):

        self.get_logger().info(f'音声合成を実行します')
        self.get_logger().info(f'発話内容は "{text}"')

        gTTS(text, lang='en').save('voice.mp3')

        subprocess.run(['mpg123 voice.mp3'], shell=True)

def main():
    rclpy.init()

    speech_service = SpeechService()

    try:
        rclpy.spin(speech_service)
    except KeyboardInterrupt:
        pass

    rclpy.shutdown()


import networkx as nx
import pandas as pd
from matplotlib import pyplot as plt
from networkx.generators.ego import ego_graph
from pyvis.network import Network
from sklearn.decomposition import PCA

def plot_network_with_edge_weights(G, figsize=(10, 10)):
    elarge = [(u, v) for (u, v, d) in G.edges(data=True) if (d["weight"] > 0.8)]
    emedium = [
        (u, v) for (u, v, d) in G.edges(data=True) if (0.8 >= d["weight"] >= 0.5)
    ]
    esmall = [(u, v) for (u, v, d) in G.edges(data=True) if (d["weight"] < 0.5)]
    plt.figure(figsize=figsize)
    pos = nx.spring_layout(G)
    nx.draw_networkx_nodes(G, pos, node_color="red", node_size=300)
    nx.draw_networkx_edges(G, pos, edgelist=elarge, width=8, alpha=0.2)
    nx.draw_networkx_edges(G, pos, edgelist=emedium, width=5, alpha=0.2)
    nx.draw_networkx_edges(G, pos, edgelist=esmall, width=2, alpha=0.2)
    nx.draw_networkx_labels(
        G,
        pos,
        font_size=10,
        font_weight="bold",
        font_family="sans-serif",
        font_color="white",
    )
    plt.axis("off")
    plt.show()

def plot_ego_network(G, n, radius, **options):
    """
    plot ego network around a node n depending
    on radius setting i.e. only include upto
    n nodes directly or indirectly connected to
    this node
    """
    hub_ego = ego_graph(G, n, radius=radius)
    pos = nx.spring_layout(hub_ego)
    nx.draw(hub_ego, pos, node_color="b", node_size=50, with_labels=False)
    nx.draw_networkx_nodes(hub_ego, pos, nodelist=[n], **options)
    plt.show()
    return hub_ego

def plot_centrality_hist(centrality, name):
    plt.figure(figsize=(15, 8))
    plt.hist(centrality.values(), bins=60)
    plt.xticks(ticks=[0, 0.01, 0.02, 0.04, 0.06, 0.08])
    plt.title(f"Histogram - {name} ", fontdict={"size": 35}, loc="center")
    plt.xlabel(f"{name}", fontdict={"size": 20})
    plt.ylabel("Counts", fontdict={"size": 20})
    plt.show()

def interactive_network_vis(
    dag, *widgets, options=None, weights=False, notebook=True, directed=True
):
    nt = Network("800px", "800px", directed=directed, notebook=notebook)

    nt.from_nx(dag)
    if weights:
        for edge in nt.edges:
            edge["value"] = edge["weight"]
        if options is not None:
            nt.set_options(options=options)
            return nt
        else:
            nt.show_buttons(filter=widgets)
            return nt

def plot_community_class_count(communities):
    count_list = []
    class_list = []
    for i, c in enumerate(communities):
        class_list.append(i)
        count_list.append(len(list(c)))

    df = pd.DataFrame({"class": class_list, "count": count_list})
    df.plot.bar(x="class", y="count")
    return df

def plot_link_features_projection(n_components, link_features, labels_test):
    pca = PCA(n_components=n_components)
    X_transformed = pca.fit_transform(link_features)
    plt.figure(figsize=(16, 12))
    col = []
    for label in labels_test:
        if label == 1:
            col.append("red")
        else:
            col.append("blue")

    plt.scatter(
        X_transformed[:, 0],
        X_transformed[:, 1],
        c=col,
        alpha=0.5,
    )
    plt.show()

def plot_shortest_paths_hist(frequencies):
    plt.figure(figsize=(15, 8))
    plt.bar(x=[i + 1 for i in range(8)], height=frequencies)
    plt.title(
        "Percentages of Shortest Path Lengths", fontdict={"size": 35}, loc="center"
    )
    plt.xlabel("Shortest Path Length", fontdict={"size": 22})
    plt.ylabel("Percentage", fontdict={"size": 22})
    plt.show()

def plot_degree_freq_log_log(G, m=0):
    degree_freq = G.degree_historgam(G)
    degrees = range(len(degree_freq))
    plt.figure(figsize=(10, 6))
    plt.loglog(degrees[m:], degree_freq[m:], "go-")
    plt.title("log log plot for degree freq")
    plt.xlabel("degree")
    plt.ylabel("frequency")
    plt.show()