# PYTHON STANDARD LIBRARY IMPORTS ---------------------------------------------
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from collections import OrderedDict
import math

# DUNDER ----------------------------------------------------------------------
__all__ = [
    "KnitDiNetwork"
]

# LOCAL MODULE IMPORTS --------------------------------------------------------
from cockatoo._knitnetworkbase import KnitNetworkBase
from cockatoo.environment import RHINOINSIDE
from cockatoo.exception import KnitNetworkTopologyError
from cockatoo.utilities import is_ccw_xy
from cockatoo.utilities import pairwise
from cockatoo.utilities import tween_planes

# THIRD PARTY MODULE IMPORTS --------------------------------------------------
import networkx as nx

# RHINO IMPORTS ---------------------------------------------------------------
if RHINOINSIDE:
    import rhinoinside
    rhinoinside.load()
    from Rhino.Geometry import Mesh as RhinoMesh
    from Rhino.Geometry import MeshNgon as RhinoMeshNgon
    from Rhino.Geometry import NurbsSurface as RhinoNurbsSurface
    from Rhino.Geometry import Plane as RhinoPlane
    from Rhino.Geometry import Vector3d as RhinoVector3d
else:
    from Rhino.Geometry import Mesh as RhinoMesh
    from Rhino.Geometry import MeshNgon as RhinoMeshNgon
    from Rhino.Geometry import NurbsSurface as RhinoNurbsSurface
    from Rhino.Geometry import Plane as RhinoPlane
    from Rhino.Geometry import Vector3d as RhinoVector3d

# CLASS DECLARATION -----------------------------------------------------------

class KnitDiNetwork(nx.DiGraph, KnitNetworkBase):
    """
    Datastructure representing a directed graph of nodes aswell as 'weft'
    and 'warp' edges. Used in the automatic generation of knitting patterns.

    Inherits from :class:`networkx.DiGraph`, :class:`KnitNetworkBase`.
    For more info, see *NetworkX* [13]_.

    Notes
    -----
    The implemented algorithms are strongly based on the paper
    *Automated Generation of Knit Patterns for Non-developable Surfaces* [1]_.
    Also see *KnitCrete - Stay-in-place knitted formworks for complex concrete
    structures* [2]_.

    The implementation was further influenced by concepts and ideas presented
    in the papers *Automatic Machine Knitting of 3D Meshes* [3]_,
    *Visual Knitting Machine Programming* [4]_ and
    *A Compiler for 3D Machine Knitting* [5]_.
    """

    # INITIALIZATION ----------------------------------------------------------

    def __init__(self, data=None, **attr):
        """
        Initialize a KnitNetwork (inherits NetworkX graph with edges, name,
        graph attributes.

        Parameters
        ----------
        data : input graph
            Data to initialize graph.  If data=None (default) an empty
            graph is created.  The data can be an edge list, or any
            NetworkX graph object.  If the corresponding optional Python
            packages are installed the data can also be a NumPy matrix
            or 2d ndarray, a SciPy sparse matrix, or a PyGraphviz graph.

        name : string, optional (default='')
            An optional name for the graph.

        attr : keyword arguments, optional (default= no attributes)
            Attributes to add to graph as key=value pairs.
        """

        # initialize using original init method
        super(KnitDiNetwork, self).__init__(data=data, **attr)

        # also copy the mapping_network attribute if it is already available
        if data and isinstance(data, KnitDiNetwork) and data.mapping_network:
            self.mapping_network = data.mapping_network
        else:
            self.mapping_network = None

        # also copy or initialize the halfedge dict for finding faces
        if data and isinstance(data, KnitDiNetwork) and data.halfedge:
            self.halfedge = data.halfedge
        else:
            self.halfedge = {}

    # TEXTUAL REPRESENTATION OF NETWORK ---------------------------------------

    def __repr__(self):
        """
        Return a textual description of the network.

        Returns
        -------
        description : str
            A textual description of the network.
        """

        if self.name != "":
            name = self.name
        else:
            name = "KnitDiNetwork"

        nn = len(self.nodes())
        ce = len(self.contour_edges)
        wee = len(self.weft_edges)
        wae = len(self.warp_edges)
        data = ("({} Nodes, {} Segment Contours, {} Weft, {} Warp)")
        data = data.format(nn, ce, wee, wae)

        return name + data

    def ToString(self):
        """
        Return a textual description of the network.

        Returns
        -------
        description : str
            A textual description of the network.

        Notes
        -----
        Used for overloading the Grasshopper display in data parameters.
        """

        return repr(self)

    # NODE WEFT EDGE METHODS --------------------------------------------------

    def node_weft_edges_out(self, node, data=False):
        """
        Gets the outgoing 'weft' edges connected to the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for outgoing 'weft' edges.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of outgoing 'weft' edges.
        """

        weft_edges = [e for e in
                      self.edges_iter(node, data=True) if e[2]["weft"]]

        if data:
            return weft_edges
        else:
            return [(e[0], e[1]) for e in weft_edges]

    def node_weft_edges_in(self, node, data=False):
        """
        Gets the incoming 'weft' edges connected to the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for incoming 'weft' edges.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of incoming 'weft' edges.
        """

        weft_edges = [e for e in
                      self.in_edges_iter(node, data=True) if e[2]["weft"]]

        if data:
            return weft_edges
        else:
            return [(e[0], e[1]) for e in weft_edges]

    def node_weft_edges(self, node, data=False):
        """
        Gets incoming and outgoing 'weft' edges connected to the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for incoming and outgoing
            'weft' edges.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of incoming and outgoing 'weft' edges.
        """

        weft_edges = [e for e in
                      self.edges_iter(node, data=True) if e[2]["weft"]]
        weft_edges.extend(e for e in
                          self.in_edges_iter(node, data=True) if e[2]["weft"])

        if data:
            return weft_edges
        else:
            return [(e[0], e[1]) for e in weft_edges]

    # NODE WARP EDGE METHODS --------------------------------------------------

    def node_warp_edges_out(self, node, data=False):
        """
        Gets the outgoing 'warp' edges connected to the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for outgoing 'warp' edges.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of outgoing 'warp' edges.
        """

        warp_edges = [e for e in
                      self.edges_iter(node, data=True) if e[2]["warp"]]

        if data:
            return warp_edges
        else:
            return [(e[0], e[1]) for e in warp_edges]

    def node_warp_edges_in(self, node, data=False):
        """
        Gets the incoming 'warp' edges connected to the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for incoming 'warp' edges.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of incoming 'warp' edges.
        """

        warp_edges = [e for e in
                      self.in_edges_iter(node, data=True) if e[2]["warp"]]

        if data:
            return warp_edges
        else:
            return [(e[0], e[1]) for e in warp_edges]

    def node_warp_edges(self, node, data=False):
        """
        Gets the incoming and outgoing 'warp' edges connected to the given
        node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for incoming and outgoing
            'warp' edges.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of incoming and outgoing 'warp' edges.
        """

        warp_edges = [e for e in
                      self.edges_iter(node, data=True) if e[2]["warp"]]
        warp_edges.extend(e for e in
                          self.in_edges_iter(node, data=True) if e[2]["warp"])

        if data:
            return warp_edges
        else:
            return [(e[0], e[1]) for e in warp_edges]

    # NODE CONTOUR EDGE METHODS -----------------------------------------------

    def node_contour_edges_out(self, node, data=False):
        """
        Gets the outgoing edges marked neither 'warp' nor 'weft' connected to
        the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for outgoing edges neither
            'weft' nor 'warp'.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of outgoing edges neither 'weft' nor 'warp'.
        """

        contour_edges = [(s, e, d) for s, e, d in
                         self.edges_iter(node, data=True)
                         if not d["warp"] and not d["weft"]]

        if data:
            return contour_edges
        else:
            return [(e[0], e[1]) for e in contour_edges]

    def node_contour_edges_in(self, node, data=False):
        """
        Gets the incoming edges marked neither 'warp' nor 'weft' connected to
        the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for incoming edges neither
            'weft' nor 'warp'.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of incoming edges neither 'weft' nor 'warp'.
        """

        contour_edges = [(s, e, d) for s, e, d in
                         self.in_edges_iter(node, data=True)
                         if not d["warp"] and not d["weft"]]

        if data:
            return contour_edges
        else:
            return [(e[0], e[1]) for e in contour_edges]

    def node_contour_edges(self, node, data=False):
        """
        Gets the incoming and outcoing edges marked neither 'warp' nor 'weft'
        connected to the given node.

        Parameters
        ----------
        node : hashable
            Hashable identifier of the node to check for incoming and outgoing
            edges neither 'weft' nor 'warp'.

        data : bool, optional
            If ``True``, will also return the edges associated data attribute
            dictionary.

            Defaults to ``False``.

        Returns
        -------
        weft_edges : :obj:`list`
            List of incoming and outgoing edges neither 'weft' nor 'warp'.
        """

        contour_edges = [(s, e, d) for s, e, d in
                         self.edges_iter(node, data=True)
                         if not d["warp"] and not d["weft"]]
        contour_edges.extend([(s, e, d) for s, e, d in
                              self.in_edges_iter(node, data=True)
                              if not d["warp"] and not d["weft"]])

        if data:
            return contour_edges
        else:
            return [(e[0], e[1]) for e in contour_edges]

    # FIND FACES (CYCLES) OF NETWORK ------------------------------------------

    def _sort_node_neighbors(self, key, nbrs, xyz, geo,
                             cbp, nrm, mode=-1, ccw=True):
        """
        Sort the neighbors of a network node.

        Notes
        -----
        Based on an implementation inside the COMPAS framework.
        For more info see [7]_.

        References
        ----------
        .. [7] Van Mele, Tom et al. *COMPAS: A framework for computational
               research in architecture and structures*.

               See: `sort_node_neighbors() inside COMPAS <https://github.com/
               compas-dev/compas/blob/e313502995b0dd86d460f86e622cafc0e29d1b75/
               src/compas/datastructures/network/duality.py#L132>`_
        """

        # if there is only one neighbor we don't need to sort anything
        if len(nbrs) == 1:
            return nbrs

        # initialize the ordered list of neighbors with the first node
        ordered_nbrs = nbrs[0:1]

        # retrieve coordinates for current node
        a = xyz[key]

        # compute local orientation if reference geometry data is present
        # CASE 1: Plane is determined by mesh normal of origin node
        if cbp and nrm and mode == 0:
            # construct local reference plane and map coordinates to plane
            # space
            a_geo = geo[key]
            localplane = RhinoPlane(a_geo, nrm[key])
            a_local = localplane.RemapToPlaneSpace(a_geo)[1]
            a = (a_local.X, a_local.Y, a_local.Z)
            # compute local plane coordinates for all neighbors
            xyz_local = {}
            for nbr in nbrs:
                # find closest point on plane and remap to plane space
                nbr_cp = localplane.ClosestPoint(geo[nbr])
                local_nbr = localplane.RemapToPlaneSpace(nbr_cp)[1]
                nbr_coords = (local_nbr.X, local_nbr.Y, local_nbr.Z)
                # set coordinate dict value
                xyz_local[nbr] = nbr_coords
            # reassign coordinate dictionary for neighbor sorting
            xyz = xyz_local
        # CASE 2: Plane is determined by average normal of origin node and nbrs
        elif cbp and nrm and mode == 1:
            # construct local reference plane and map coordinates to plane
            # space
            a_geo = geo[key]
            # get average normal
            avg_nrm = nrm[key]
            nbr_nrms = [nrm[n] for n in nbrs]
            for nv in nbr_nrms:
                avg_nrm += nv
            # construct plane based on average normal
            localplane = RhinoPlane(a_geo, avg_nrm)
            a_local = localplane.RemapToPlaneSpace(a_geo)[1]
            a = (a_local.X, a_local.Y, a_local.Z)
            # compute local plane coordinates for all neighbors
            xyz_local = {}
            for nbr in nbrs:
                # find closest point on plane and remap to plane space
                nbr_cp = localplane.ClosestPoint(geo[nbr])
                local_nbr = localplane.RemapToPlaneSpace(nbr_cp)[1]
                nbr_coords = (local_nbr.X, local_nbr.Y, local_nbr.Z)
                # set coordinate dict value
                xyz_local[nbr] = nbr_coords
            # reassign coordinate dictionary for neighbor sorting
            xyz = xyz_local
        # CASE 3: Plane is determined by avg between fitplane and avg meshplane
        elif cbp and nrm and mode == 2:
            # construct local reference plane and map coordinates to plane
            # space
            a_geo = geo[key]
            # get average normal
            avg_nrm = nrm[key]
            nbr_nrms = [nrm[n] for n in nbrs]
            for nv in nbr_nrms:
                avg_nrm += nv
            # construct plane based on average normal
            localplane = RhinoPlane(a_geo, avg_nrm)
            fitplane = RhinoPlane.FitPlaneToPoints([geo[n] for n in nbrs])[1]
            # align fitplane with localplane
            if fitplane.Normal * localplane.Normal < 0:
                fitplane.Flip()
            # tween the planes and set origin
            tweenplane = tween_planes(localplane, fitplane, 0.5)
            tweenplane.Origin = a_geo
            # remap origin node to plane space
            a_local = tweenplane.RemapToPlaneSpace(a_geo)[1]
            a = (a_local.X, a_local.Y, a_local.Z)
            # compute local plane coordinates for all neighbors
            xyz_local = {}
            for nbr in nbrs:
                # find closest point on plane and remap to plane space
                nbr_cp = tweenplane.ClosestPoint(geo[nbr])
                local_nbr = tweenplane.RemapToPlaneSpace(nbr_cp)[1]
                nbr_coords = (local_nbr.X, local_nbr.Y, local_nbr.Z)
                # set coordinate dict value
                xyz_local[nbr] = nbr_coords
            # reassign coordinate dictionary for neighbor sorting
            xyz = xyz_local

        # loop over all neighbors except the first one
        for i, nbr in enumerate(nbrs[1:]):
            c = xyz[nbr]
            pos = 0
            b = xyz[ordered_nbrs[pos]]
            while not is_ccw_xy(a, b, c):
                pos += 1
                if pos > i:
                    break
                b = xyz[ordered_nbrs[pos]]
            if pos == 0:
                pos -= 1
                b = xyz[ordered_nbrs[pos]]
                while is_ccw_xy(a, b, c):
                    pos -= 1
                    if pos < -len(ordered_nbrs):
                        break
                    b = xyz[ordered_nbrs[pos]]
                pos += 1
            ordered_nbrs.insert(pos, nbr)

        # return the ordered neighbors in cw or ccw order
        if not ccw:
            return ordered_nbrs[::-1]
        return ordered_nbrs

    def _sort_neighbors(self, mode=-1, ccw=True):
        """
        Sort the neighbors of all network nodes.

        Notes
        -----
        Based on an implementation inside the COMPAS framework.
        For more info see [8]_.

        References
        ----------
        .. [8] Van Mele, Tom et al. *COMPAS: A framework for computational
               research in architecture and structures*.

               See: `sort_neighbors() inside COMPAS <https://github.com/
               compas-dev/compas/blob/e313502995b0dd86d460f86e622cafc0e29d1b75/
               src/compas/datastructures/network/duality.py#L121>`_
        """

        # initialize sorted neighbors dict
        sorted_neighbors = {}

        # get dictionary of all coordinates by node index
        xyz = {k: (d["x"], d["y"], d["z"]) for k, d in self.nodes_iter(True)}
        geo = {k: d["geo"] for k, d in self.nodes_iter(True)}

        # compute local orientation data when reference geometry is present
        try:
            reference_geometry = self.graph["reference_geometry"]
        except KeyError:
            reference_geometry = None

        if not reference_geometry:
            cbp = None
            nrm = None
        elif isinstance(reference_geometry, RhinoMesh):
            cbp = {k: reference_geometry.ClosestMeshPoint(geo[k], 0)
                   for k in self.nodes_iter()}
            nrm = {k: reference_geometry.NormalAt(cbp[k])
                   for k in self.nodes_iter()}
        elif isinstance(reference_geometry, RhinoNurbsSurface):
            cbp = {k: reference_geometry.ClosestPoint(geo[k])[1:]
                   for k in self.nodes_iter()}
            nrm = {k: reference_geometry.NormalAt(cbp[k][0], cbp[k][1])
                   for k in self.nodes_iter()}

        # loop over all nodes in network
        for key in self.nodes_iter():
            nbrs = self[key].keys()
            sorted_neighbors[key] = self._sort_node_neighbors(
                                                            key,
                                                            nbrs,
                                                            xyz,
                                                            geo,
                                                            cbp,
                                                            nrm,
                                                            mode=mode,
                                                            ccw=ccw)

        # set the sorted neighbors list as an attribute to the nodes
        for key, nbrs in sorted_neighbors.items():
            self.node[key]["sorted_neighbors"] = nbrs[::-1]

        # return the sorted neighbors dict
        return sorted_neighbors

    def _find_first_node_neighbor(self, key):
        """
        Find the first neighbor for a given node in the network.

        Notes
        -----
        Based on an implementation inside the COMPAS framework.
        For more info see [9]_.

        References
        ----------
        .. [9] Van Mele, Tom et al. *COMPAS: A framework for computational
               research in architecture and structures*.

               See: `find_first_node_neighbor() inside COMPAS <https://
               github.com/compas-dev/compas/blob/
               e313502995b0dd86d460f86e622cafc0e29d1b75/src/compas/
               datastructures/network/duality.py#L103>`_
        """

        # get all node neighbors
        nbrs = self[key].keys()

        # if there is only one neighbor, we have already found our candidate
        if len(nbrs) == 1:
            return nbrs[0]

        ab = [-1.0, -1.0, 0.0]
        rhino_ab = RhinoVector3d(*ab)
        a = self.node_coordinates(key)
        b = [a[0] + ab[0], a[1] + ab[1], 0]

        angles = []
        for nbr in nbrs:
            c = self.node_coordinates(nbr)
            ac = [c[0] - a[0], c[1] - a[1], 0]
            rhino_ac = RhinoVector3d(*ac)
            alpha = RhinoVector3d.VectorAngle(rhino_ab, rhino_ac)
            if is_ccw_xy(a, b, c, True):
                alpha = (2 * math.pi) - alpha
            angles.append(alpha)

        return nbrs[angles.index(min(angles))]

    def _find_edge_cycle(self, u, v):
        """
        Find a cycle based on the given edge.

        Parameters
        ----------
        u : hashable
            Index of the start node of the origin edge for the cycle.

        v : hashable
            Index of the end node of the origin edge for the cycle.

        Notes
        -----
        Based on an implementation inside the COMPAS framework.
        For more info see [6]_.

        References
        ----------
        .. [6] Van Mele, Tom et al. *COMPAS: A framework for computational
               research in architecture and structures*.

               See: `find_edge_cycle() inside COMPAS <https://github.com/
               compas-dev/compas/blob/09153de6718fb3d49a4650b89d2fe91ea4a9fd4a/
               src/compas/datastructures/network/duality.py#L161>`_
        """
        cycle = [u]
        while True:
            cycle.append(v)
            nbrs = self.node[v]["sorted_neighbors"]
            nbr = nbrs[nbrs.index(u) - 1]
            u, v = v, nbr
            if v == cycle[0]:
                break
        return cycle

    def find_cycles(self, mode=-1):
        """
        Finds the cycles (faces) of this network by utilizing a wall-follower
        mechanism.

        Parameters
        ----------
        mode : int, optional
            Determines how the neighbors of each node are sorted when finding
            cycles for the network.

            ``-1`` equals to using the world XY plane.

            ``0`` equals to using a plane normal to the origin nodes closest
            point on the reference geometry.

            ``1`` equals to using a plane normal to the average of the origin
            and neighbor nodes' closest points on the reference geometry.

            ``2`` equals to using an average plane between a plane fit to the
            origin and its neighbor nodes and a plane normal to the origin
            nodes closest point on the reference geometry.

            Defaults to ``-1``.

        Warning
        -------
        Modes other than -1 (default) are only possible if this network has an
        underlying reference geometry in form of a Mesh or NurbsSurface. The
        reference geometry should be assigned when initializing the network by
        assigning the geometry to the "reference_geometry" attribute of the
        network.

        Notes
        -----
        Based on an implementation inside the COMPAS framework.
        For more info see [17]_.

        References
        ----------
        .. [17] Van Mele, Tom et al. *COMPAS: A framework for computational
               research in architecture and structures*.

               See: `find_cycles() inside COMPAS <https://github.com/
               compas-dev/compas/blob/09153de6718fb3d49a4650b89d2fe91ea4a9fd4a/
               src/compas/datastructures/network/duality.py#L20>`_
        """

        # initialize the halfedge dict of the directed network
        for u, v in self.edges_iter():
            try:
                self.halfedge[u][v] = None
            except KeyError:
                self.halfedge[u] = {}
                self.halfedge[u][v] = None
            try:
                self.halfedge[v][u] = None
            except KeyError:
                self.halfedge[v] = {}
                self.halfedge[v][u] = None

        # sort the all the neighbors for each node of the network
        self._sort_neighbors(mode=mode)

        # find start node
        # sort leaf nodes by y and x coordinates
        # leaves = self.leaf_nodes
        # if leaves:
        #     u = sorted(leaves, key=lambda n: (n[1]["y"], n[1]["x"]))[0][0]
        # else:
        #     u = sorted(self.nodes_iter(data=True),
        #                key=lambda n: (n[1]["y"], n[1]["x"]))[0][0]

        # find start node
        # sort leaf nodes by node identifier / index
        leaves = self.leaf_nodes
        if leaves:
            u = sorted(leaves, key=lambda n: n[0])[0][0]
        else:
            u = sorted(self.nodes_iter(data=True), key=lambda n: n[0])[0][0]

        # initialize found and cycles dict
        cycles = {}
        found = {}
        ckey = 0

        # find the very first cycle
        v = self._find_first_node_neighbor(u)
        cycle = self._find_edge_cycle(u, v)
        frozen = frozenset(cycle)
        found[frozen] = ckey
        cycles[ckey] = cycle

        # set halfedge dict
        for a, b in pairwise(cycle + cycle[:1]):
            self.halfedge[a][b] = ckey
        ckey += 1

        # loop over all edges and find cycles
        for u, v in self.edges_iter():
            # find cycles for u -> v edges
            if self.halfedge[u][v] == None:
                cycle = self._find_edge_cycle(u, v)
                frozen = frozenset(cycle)
                if frozen not in found:
                    found[frozen] = ckey
                    cycles[ckey] = cycle
                    ckey += 1
                for a, b in pairwise(cycle + cycle[:1]):
                    self.halfedge[a][b] = found[frozen]
            # find cycles for v -> u edges
            if self.halfedge[v][u] == None:
                cycle = self._find_edge_cycle(v, u)
                frozen = frozenset(cycle)
                if frozen not in found:
                    found[frozen] = ckey
                    cycles[ckey] = cycle
                    ckey += 1
                for a, b in pairwise(cycle + cycle[:1]):
                    self.halfedge[a][b] = found[frozen]

        return cycles

    # MESHING -----------------------------------------------------------------

    def create_mesh(self, mode=-1, max_valence=4):
        """
        Constructs a mesh from this network by finding cycles and using them as
        mesh faces.

        Parameters
        ----------
        mode : int, optional
            Determines how the neighbors of each node are sorted when finding
            cycles for the network.

            ``-1`` equals to using the world XY plane.

            ``0`` equals to using a plane normal to the origin nodes closest
            point on the reference geometry.

            ``1`` equals to using a plane normal to the average of the origin
            and neighbor nodes' closest points on the reference geometry.

            ``2`` equals to using an average plane between a plane fit to the
            origin and its neighbor nodes and a plane normal to the origin
            nodes closest point on the reference geometry.

            Defaults to ``-1``.

        max_valence : int, optional
            Sets the maximum edge valence of the faces. If this is set to > 4,
            n-gon faces (more than 4 edges) are allowed. Otherwise, their
            cycles are treated as invalid and will be ignored.

            Defaults to ``4``.

        Warning
        -------
        Modes other than ``-1`` are only possible if this network has an
        underlying reference geometry in form of a Mesh or NurbsSurface. The
        reference geometry should be assigned when initializing the network by
        assigning the geometry to the "reference_geometry" attribute of the
        network.
        """

        # get cycles dict of this network
        cycles = self.find_cycles(mode=mode)

        # create an empty mesh
        Mesh = RhinoMesh()

        # intialize mapping of network nodes to mesh vertices
        node_to_vertex = {}
        vcount = 0

        # fill mesh and map network nodes to mesh vertices
        for node, data in self.nodes_iter(data=True):
            node_to_vertex[node] = vcount
            Mesh.Vertices.Add(data["x"], data["y"], data["z"])
            vcount += 1

        # loop over cycles and add faces to the mesh
        fcount = 0
        for ckey in cycles.keys():
            cycle = cycles[ckey]
            c_len = len(cycle)
            if c_len > 4:
                if c_len > max_valence:
                    continue
                # find centroid of ngon nodes
                cycle_coords = [[self.node[k]["x"],
                                 self.node[k]["y"],
                                 self.node[k]["z"]] for k in cycle]
                # compute centroid
                c_x, c_y, c_z = zip(*cycle_coords)
                centroid = [sum(c_x) / c_len,
                            sum(c_y) / c_len,
                            sum(c_z) / c_len]
                # add centroid to mesh
                Mesh.Vertices.Add(*centroid)
                # create triangle with centroid for every pair in cycle
                closed_cycle = cycle[:]
                closed_cycle.append(cycle[0])
                ngon_faces = []
                for a, b in pairwise(closed_cycle):
                    Mesh.Faces.AddFace(node_to_vertex[a],
                                       node_to_vertex[b],
                                       vcount)
                    ngon_faces.append(fcount)
                    fcount += 1
                ngon_cycle = [node_to_vertex[n] for n in cycle]
                RhinoMeshNgon.Create(ngon_cycle, ngon_faces)
                # increment mesh vertex counter
                vcount += 1
            elif c_len < 3:
                continue
            else:
                mesh_cycle = [node_to_vertex[n] for n in cycle]
                Mesh.Faces.AddFace(*mesh_cycle)
                fcount += 1

        # unify the normals of the mesh
        Mesh.UnifyNormals()

        return Mesh

    # CONVERSION TO 2D-KNITTINGPATTERN (PIXEL IMAGE) --------------------------

    def verify_dual_form(self):
        """
        Verifies this network to have the correct form of a dual as needed for
        representing this network as a 2d knitting pattern.

        Returns
        -------
        bool
            ``True`` on success, ``False`` otherwise.
        """

        # check every single node
        for node in self.nodes_iter():
            # verify if all nodes have the correct keys for attributes
            try:
                start = self.node[node]["start"]
                end = self.node[node]["end"]
                inc = self.node[node]["increase"]
                dec = self.node[node]["decrease"]
                lfn = self.node[node]["leaf"]
                geo = self.node[node]["geo"]
            except KeyError:
                return False

            # get all neighbors
            prd = self.predecessors(node)
            lpr = len(prd)
            # check for nodes with more than two predecessors
            if lpr > 2:
                return False

            suc = self.successors(node)
            lsu = len(suc)
            # check for nodes with more than two successors
            if lsu > 2:
                return False

            nbr = prd + suc
            lnn = len(nbr)
            # check for disconnected nodes
            if not lnn:
                return False

            # maximum connections per node is four
            elif lnn > 4:
                return False

        return True

    def make_pattern_data(self, consolidate=False):
        """
        Topological sort this network to represent it as 2d knitting pattern
        consisting of rows and columns.

        Parameters
        ----------
        consolidate : bool
            If ``True``, will consolidate the final pattern data.
            Defaulst to ``False``.

        Returns
        -------
        pattern_data : :obj:`list` of :obj:`list`
            List (rows) of lists (column values) where every value represents
            a node.

        Raises
        ------
        KnitNetworkTopologyError
            if the network does not satisfy the topology constraints needed for
            this operation and the outcome would be unfeasible or
            unpredictable.

        Notes
        -----
        Closely resembles the implementation described in *Automated Generation
        of Knit Patterns for Non-developable Surfaces* [1]_. Also see
        *KnitCrete - Stay-in-place knitted formworks for complex concrete
        structures* [2]_.
        """

        # initialize dict for seen nodes and list for storage of rows
        seenrows = {}
        seencols = {}
        rows = []
        cols = []

        # initialize mapping dicts for ordering of rows and columns
        id2row = OrderedDict()
        id2col = OrderedDict()
        node2rowid = OrderedDict()
        node2colid = OrderedDict()

        # BUILD ROWS ----------------------------------------------------------

        # every 'end' node defines the start of a row
        # loop over all 'end' nodes
        for node, data in self.end_nodes:
            # continue if this node has already been visited
            if node in seenrows:
                continue

            # get outgoing 'weft' edges of the current 'end' node
            nodeweft_out = self.node_weft_edges_out(node, data=True)
            nodeweft_in = self.node_weft_edges_in(node, data=True)

            # skip 'end' nodes which have only incoming 'weft' edges
            if nodeweft_in and not nodeweft_out:
                continue

            # if there is more than one outgoing 'weft' edge, we have a problem
            if len(nodeweft_out) > 1:
                errMsg = "More than one outgoing 'weft' edge at " + \
                         "first row node {}!".format(node)
                raise KnitNetworkTopologyError(errMsg)
            # if this is a singular node, it is a separate row.
            elif len(nodeweft_out) == 0 and len(nodeweft_in) == 0:
                # append it as a row to the list of rows
                rows.append([node])
                # set the mapping dictionaries
                row_id = (node, node)
                id2row[row_id] = [node]
                node2rowid.update({node: row_id})
                # set the seen marker and continue to next 'end' node
                seenrows[node] = True
                continue
            # if there is exactly one 'weft' edge, traverse until next node
            elif len(nodeweft_out) == 1:
                # get the connected node to the current node
                connected_node = (nodeweft_out[0][1],
                                  self.node[nodeweft_out[0][1]])
                # define initial row nodes with nodes of the first edge
                row_nodes = [node, connected_node[0]]
                # traverse as long as there is an outgoing next 'weft' edge
                # until an 'end' node is discovered
                while True:
                    # get 'weft' edges of last node in row nodes
                    next_weft = self.node_weft_edges_out(row_nodes[-1])
                    # if there is more than one connected 'weft' edge, we
                    # have a problem
                    if len(next_weft) > 1:
                        errMsg = "More than one outgoing 'weft' edge at " + \
                                 "row node {}!".format(node)
                        raise KnitNetworkTopologyError(errMsg)
                    # if there are no next 'weft' edges, row is complete
                    elif len(next_weft) == 0:
                        if self.node[row_nodes[-1]]["end"]:
                            # this is the finishing 'end' node; set it seen
                            # and complete this row by breaking
                            seenrows[row_nodes[-1]] = True
                            break
                        # if there are no next 'weft' edges but this is not
                        # an 'end' node, we have a problem
                        else:
                            # see if there are incoming 'weft' edges at the
                            # current node which are not the way we came from
                            next_weft = [nw for nw in self.node_weft_edges_in(
                                         row_nodes[-1], data=True)
                                         if nw[0] != row_nodes[-2]]

                            # try to reverse them as a failsafe for imperfect
                            # topological dual graphs
                            if len(next_weft) == 1:
                                # flip geometry first, then the graph edge
                                nwe = next_weft[0]
                                nw_attr = nwe[2].copy()
                                nw_attr["geo"].Flip()
                                self.remove_edge(nwe[0], nwe[1])
                                self.add_edge(nwe[1],
                                              nwe[0],
                                              attr_dict=nw_attr)
                            else:
                                errMsg = ("Unexpected end of row. Missing " +
                                          "'end' attribute at node {}!")
                                errMsg.format(row_nodes[-1])
                                raise KnitNetworkTopologyError(errMsg)

                    # if there is a next node over a 'weft' edge, append to
                    # row and continue
                    if len(next_weft) == 1:
                        row_nodes.append(next_weft[0][1])
                        continue
                # append the completed row to the list of rows
                rows.append(row_nodes)
                # set the mapping dictionaries
                row_id = (row_nodes[0], row_nodes[-1])
                id2row[row_id] = row_nodes
                node2rowid.update({node: row_id for node in row_nodes})
                # finally, set the current node as seen
                seenrows[node] = True

        # BUILD COLUMNS -------------------------------------------------------

        # every 'end' node defines the start of a row
        # loop over all 'end' nodes
        col_sources = [(n, d) for n, d in self.nodes_iter(data=True)
                       if d["increase"] or d["leaf"] or d["end"]]
        for node, data in col_sources:
            # continue if this node has already been visited
            if node in seencols:
                continue

            # get outgoing 'warp' edges of the current node
            nodewarp_out = self.node_warp_edges_out(node, data=True)
            nodewarp_in = self.node_warp_edges_in(node, data=True)

            # skip nodes which have incoming 'warp' edges
            if nodewarp_in:
                continue

            # if there is more than one outgoing 'warp' edge, we have a problem
            if len(nodewarp_out) > 1:
                errMsg = "More than one outgoing 'warp' edge at " + \
                         "first column node {}!".format(node)
                raise KnitNetworkTopologyError(errMsg)
            # if this is a singular node, it is a separate column (?)
            elif len(nodewarp_out) == 0 and len(nodewarp_in) == 0:
                # errMsg = ("Absolutely no 'warp' edges at " +
                #           "node {}!".format(node))
                # raise KnitNetworkTopologyError(errMsg)

                # append it as a column to the list of columns
                cols.append([node])
                # set the mapping dictionaries
                col_id = (node, node)
                id2col[col_id] = [node]
                node2colid.update({node: col_id})
                # set the seen marker and continue to next node
                seencols[node] = True
                continue
            # if there is exactly one 'warp' edge, traverse until next node
            elif len(nodewarp_out) == 1:
                # get the connected node to the current node
                connected_node = (nodewarp_out[0][1],
                                  self.node[nodewarp_out[0][1]])
                # define initial column nodes with nodes of the first edge
                col_nodes = [node, connected_node[0]]
                # traverse as long as there is an outgoing next 'warp' edge
                while True:
                    # get 'warp' edges of last node in row nodes
                    next_warp = self.node_warp_edges_out(col_nodes[-1])
                    # if there is more than one connected 'warp' edge, we
                    # have a problem
                    if len(next_warp) > 1:
                        errMsg = "More than one outgoing 'warp' edge at " + \
                                 "col node {}!".format(node)
                        raise KnitNetworkTopologyError(errMsg)
                    # if there are no next 'warp' edges, column is complete
                    elif len(next_warp) == 0:
                        seencols[col_nodes[-1]] = True
                        break
                    # if there is a next node over a 'warp' edge, append to
                    # column and continue
                    elif len(next_warp) == 1:
                        col_nodes.append(next_warp[0][1])
                        continue
                # append the completed column to the list of columns
                cols.append(col_nodes)
                # set the mapping dictionaries
                col_id = (col_nodes[0], col_nodes[-1])
                id2col[col_id] = col_nodes
                node2colid.update({node: col_id for node in col_nodes})
                # finally, set the current node as seen
                seencols[node] = True

        # BUILD ROW MAPPING FOR TOPOLOGICAL SORT ------------------------------

        # initialize mapping for topological sort of rows
        row_map = nx.DiGraph()
        row_ids = id2row.keys()
        # find all targets of all rows by checking all row nodes
        # for targets and getting the corresponding row
        for row_id in row_ids:
            # get row from mapping dict
            row = id2row[row_id]
            # initialize list for storage of targets
            target_ids = []
            # loop over all nodes in the current row
            for node in row:
                # check the node for outgoing 'warp' edges and get its
                # successor
                try:
                    node_suc = self.node_warp_edges_out(node)[0][1]
                except IndexError:
                    continue
                # find the id of the row which contains the 'warp' edge
                # successor node
                target_id = node2rowid[node_suc]
                # if we already found this id before, continue
                if target_id in target_ids:
                    continue
                # if its a new id, append it to the list of found target ids
                target_ids.append(target_id)

            [row_map.add_edge(row_id, tid) for tid in target_ids]

        # BUILD COLUMN MAPPING FOR TOPOLOGICAL SORT ---------------------------

        # initialize mapping for backtracking of columns
        col_map = nx.DiGraph()
        col_ids = id2col.keys()
        # find all targets of all columns by checking all column nodes
        # for targets and getting the corresponding column
        for col_id in col_ids:
            # get column from mapping dict
            col = id2col[col_id]
            # initialize list for storage of targets
            target_ids = []
            # loop over all nodes in the current column
            for node in col:
                # check the node for outgoing 'weft' edges and get its
                # successor
                try:
                    node_suc = self.node_weft_edges_out(node)[0][1]
                except IndexError:
                    continue
                # find the id of the column which contains the 'weft' edge
                # successor node
                target_id = node2colid[node_suc]
                # if we already found this id before, continue
                if target_id in target_ids:
                    continue
                # if its a new id, append it to the list of found target ids
                target_ids.append(target_id)

            [col_map.add_edge(col_id, tid) for tid in target_ids]

        # TOPOLOGICAL SORT OF ROWS --------------------------------------------

        # own method of topological sort for rows (in utilities)
        # ordered_row_stack = resolve_order_by_backtracking(row_map)

        # use nx topological sort for rows
        try:
            ordered_row_ids = nx.topological_sort(row_map)
        except nx.NetworkXError as e:
            raise KnitNetworkTopologyError(str(e.message))
        except nx.NetworkXUnfeasible as e:
            raise KnitNetworkTopologyError(str(e.message))

        # get the rows with the topological sorted result
        toposort_rows = [id2row[id] for id in ordered_row_ids]
        for i, row in enumerate(toposort_rows):
            for n in row:
                self.node[n]["chain"] = i

        if consolidate:
            # HORIZONTAL CONSOLIDATION ----------------------------------------
            consolidated_rows = []
            for i, row in enumerate(toposort_rows):
                # if we are at the first row, just insert it
                if i == 0:
                    consolidated_rows.append(row)
                    continue

                # else get the previously inserted rows
                prevrows = reversed(consolidated_rows)
                # find first incoming warp edge that connects to a previous row
                for prevrow in prevrows:
                    row_found = False
                    for node in row:
                        warp_in = self.node_warp_edges_in(node)
                        if warp_in:
                            if warp_in[0][0] not in prevrow:
                                continue
                            connection_node = warp_in[0][1]
                            connection_index = row.index(connection_node)
                            prevrow_connection_node = warp_in[0][0]
                            prevrow_connection_index = prevrow.index(
                                                    prevrow_connection_node)
                            row_found = True
                    if row_found:
                        break

                # compute offset
                if connection_index == prevrow_connection_index:
                    offset = 0
                else:
                    offset = prevrow_connection_index - connection_index

                # execute offset and append to consolidated rows
                if offset == 0:
                    consolidated_rows.append(row)
                elif offset < 0:
                    for consrow in consolidated_rows:
                        for o in range(abs(offset)):
                            consrow.insert(0, -1)
                    consolidated_rows.append(row)
                elif offset > 0:
                    offset_list = [-1] * abs(offset)
                    offset_list.extend(row)
                    consolidated_rows.append(offset_list)

            # fill to minlength
            minrl = max([len(row) for row in consolidated_rows])
            for row in consolidated_rows:
                if len(row) != minrl:
                    row.extend([-1] * (minrl - len(row)))

            # VERTICAL CONSOLIDATION ------------------------------------------
            vert_consolidated_rows = []
            for i, row in enumerate(consolidated_rows):
                if i == 0:
                    vert_consolidated_rows.append(row)
                    continue

                # get the first index that is not -1
                rowstart = None
                rowend = None
                for j, nodevalue in enumerate(row):
                    if nodevalue != -1 and rowstart == None:
                        rowstart = j
                        if j == len(row) - 1:
                            rowend = rowstart
                            break
                        continue
                    elif nodevalue == -1 and rowstart != None:
                        rowend = j
                        break
                    elif (nodevalue != -1 and
                          j == len(row) - 1 and
                          rowstart != None):
                        rowend = j
                        break

                # get previous inserted rows and loop over them
                prevrows = reversed(vert_consolidated_rows)

                insertion_index = None
                for j, prevrow in enumerate(prevrows):
                    # get window
                    window = prevrow[rowstart:rowend]
                    # check if the window on the previous row is free
                    if window == [-1] * len(window):
                        insertion_index = len(vert_consolidated_rows) - 1 - j
                        continue
                    else:
                        break

                if insertion_index != None:
                    insertion_row = vert_consolidated_rows[insertion_index]
                    merged_row = insertion_row[:rowstart]
                    merged_row.extend(row[rowstart:rowend + 1])
                    merged_row.extend(insertion_row[rowend + 1:])

                    vert_consolidated_rows[insertion_index] = merged_row
                else:
                    vert_consolidated_rows.append(row)

            return vert_consolidated_rows

        else:
            # TOPOLOGICAL SORT OF COLUMNS -------------------------------------

            # own method of topological sort for columns (in utilities)
            # ordered_column_stack = resolve_order_by_backtracking(col_map)

            # use nx topological sort for columns
            try:
                ordered_column_stack = nx.topological_sort(col_map)
            except nx.NetworkXError as e:
                raise KnitNetworkTopologyError(str(e.message))
            except nx.NetworkXUnfeasible as e:
                raise KnitNetworkTopologyError(str(e.message))

            # SPREAD OUT BY FILLING WITH -1 FILLER ----------------------------

            # fill all the rows to minimum row length with
            # placeholder values (-1)
            minrl = max([len(row) for row in rows])
            # loop over all rows and fill until minimum length
            for key in id2row.keys():
                row = id2row[key]
                for j in range(minrl):
                    try:
                        node = row[j]
                    except IndexError:
                        row.append(-2)
                id2row[key] = row

            # spread out rows according to toposorted columns
            for i, col in enumerate(ordered_column_stack):
                # get column nodes
                colnodes = id2col[col]
                # loop over all rows
                for j, row in enumerate(toposort_rows):
                    # check the entry at the current column index
                    # if this entry is not in colnodes, shift it to the right
                    entry = row[i]
                    if entry in colnodes:
                        toposort_rows[j].append(-2)
                    elif entry not in colnodes:
                        toposort_rows[j].insert(i, -1)

            # trim final topological sorted rows
            trim = toposort_rows[0].index(-2)
            toposort_rows = [btr[:trim] for btr in toposort_rows]

        # return all sorted rows
        return toposort_rows

# MAIN ------------------------------------------------------------------------

if __name__ == '__main__':
    pass


num=int(input())
coutList=[]
testList=[]
for i in range(num):
    coutList.append(input())
    testList.append(input())

for i in range(num):
    q=coutList[i].split(" ")
    length=int(q[0])
    k=int(q[1])
    test=testList[i].split(" ")
    temp=[]
    res=[]
    c=0
    for n in range(length-k+1):
        for m in range(c,c+k):
            temp.append(test[m])
        f = list(map(int, temp))
        res.append(max(f))
        c+=1
        temp=[]
    end_str=""
    for x in res:
        end_str=end_str+str(x)+" "
    print(end_str)


#Question: ('the numeric addition', 'of given values', 10,20, 30)
('addition of first seto f numbers', 10, 20, 30,'second set of numbers', 40,50,60)
"addition of first seto f numbers 60 second set of numbers 150"

# Ismail
def addition(*params):
    sum = 0
    for x in params:
        if type(x) == int or type(x) == float:
           sum+=x
        else:
            print(x,end='')
    return sum

print(addition('the addition ', 'of given numbers are', 10,20,20,30))

# Amala
def addition(*params):
    for value in params:
        if type(value) == str:
            result1 += value
        if type(value) == int:
            result2 += value
    return result1 + str(result2)

# Jyothsna
def addition(*params):
    result = sum(params)
    return f"the addition of given values is {result}"

print(addition(10,20,30))

# Pavithra
def addition(*params):
    result = ''
    sum_val = 0
    for i in params:
        if type(i) == str:
            result+=i + ' '
        if type(i) == int:
            sum_val+= i
    result = result + str(sum_val)

    return result

print(addition("the addition", "of given value is ", 10,20,30))


from sklearn import preprocessing, linear_model

from .model import Model

class LogisticRegression(Model):
    def __init__(self, balanced=False, scaler="minmax"):
        self.balanced = balanced
        if scaler == "minmax":
            self.scaler = preprocessing.MinMaxScaler()
        elif scaler == "standard":
            self.scaler = preprocessing.StandardScaler()
        else:
            raise ValueError(f"Scaler '{scaler}' is not supported.")

    def create_model(self, random_state):
        """
        Create a logistic regression model with tuned hyperparameters.

        Class balance is implemented with inverse weight coefficients.
        """
        if self.balanced:
            class_weight = "balanced"
        else:
            class_weight = None
        return linear_model.LogisticRegression(
            class_weight=class_weight,
            max_iter=200,
            fit_intercept=False,
            random_state=random_state,
        )

    def fit_model(self, model, train_data, train_labels):
        train_scaled = self.scaler.fit_transform(train_data)
        model.fit(train_scaled, train_labels)

    def predict_model(self, model, test_data):
        test_scaled = self.scaler.transform(test_data)
        preds = model.predict_proba(test_scaled)
        preds = preds[:, 1]
        return preds


# -*- coding: utf-8 -*-
"""
-------------------------------------------------
   File Name：     testWebRequest
   Description :   test class WebRequest
   Author :        J_hao
   date：          2017/7/31
-------------------------------------------------
   Change Activity:
                   2017/7/31: function testWebRequest
-------------------------------------------------
"""
__author__ = 'J_hao'

from Util.WebRequest import WebRequest

# noinspection PyPep8Naming
def testWebRequest():
    """
    test class WebRequest in Util/WebRequest.py
    :return:
    """
    wr = WebRequest()
    request_object = wr.get('https://www.baidu.com/')
    assert request_object.status_code == 200

if __name__ == '__main__':
    testWebRequest()


import torch
import torch.nn as nn
import torch.nn.functional as F
from torch.nn.utils import spectral_norm, weight_norm

class DiscriminatorR(torch.nn.Module):
    def __init__(
        self,
        *,
        n_fft: int = 1024,
        hop_length: int = 120,
        win_length: int = 600,
        use_spectral_norm: bool = False,
        leaky_relu_slope: float = 0.2,
    ):
        super(DiscriminatorR, self).__init__()

        self.n_fft = n_fft
        self.hop_length = hop_length
        self.win_length = win_length
        self.leaky_relu_slope = leaky_relu_slope

        norm_f = weight_norm if use_spectral_norm == False else spectral_norm

        self.convs = nn.ModuleList(
            [
                norm_f(nn.Conv2d(1, 32, (3, 9), padding=(1, 4))),
                norm_f(nn.Conv2d(32, 32, (3, 9), stride=(1, 2), padding=(1, 4))),
                norm_f(nn.Conv2d(32, 32, (3, 9), stride=(1, 2), padding=(1, 4))),
                norm_f(nn.Conv2d(32, 32, (3, 9), stride=(1, 2), padding=(1, 4))),
                norm_f(nn.Conv2d(32, 32, (3, 3), padding=(1, 1))),
            ]
        )
        self.conv_post = norm_f(nn.Conv2d(32, 1, (3, 3), padding=(1, 1)))

    def forward(self, x):
        fmap = []

        x = self.spectrogram(x)
        x = x.unsqueeze(1)

        for l in self.convs:
            x = l(x)
            x = F.leaky_relu(x, self.leaky_relu_slope, inplace=True)
            fmap.append(x)

        x = self.conv_post(x)
        fmap.append(x)
        x = torch.flatten(x, 1, -1)

        return x, fmap

    def spectrogram(self, x):
        x = F.pad(
            x,
            (
                int((self.n_fft - self.hop_length) / 2),
                int((self.n_fft - self.hop_length) / 2),
            ),
            mode="reflect",
        )
        x = x.squeeze(1)
        x = torch.stft(
            x,
            n_fft=self.n_fft,
            hop_length=self.hop_length,
            win_length=self.win_length,
            center=False,
            return_complex=True,
        )
        x = torch.view_as_real(x)  # [B, F, TT, 2]
        mag = torch.norm(x, p=2, dim=-1)  # [B, F, TT]

        return mag

class MultiResolutionDiscriminator(torch.nn.Module):
    def __init__(self, resolutions: list[tuple[int]]):
        super(MultiResolutionDiscriminator, self).__init__()

        self.discriminators = nn.ModuleList(
            [
                DiscriminatorR(
                    n_fft=n_fft, hop_length=hop_length, win_length=win_length
                )
                for n_fft, hop_length, win_length in resolutions
            ]
        )

    def forward(
        self, x: torch.Tensor
    ) -> tuple[list[torch.Tensor], list[list[torch.Tensor]]]:
        ret_x, ret_fmap = [], []

        for disc in self.discriminators:
            res, fmap = disc(x)

            ret_x.append(res)
            ret_fmap.append(fmap)

        return ret_x, ret_fmap


import cv2
import os
import numpy as np

def imagesToVideo(images, fps, file_path):
    if len(images) == 0:
        raise Exception("image list size is 0")
    # 高， 宽， 通道数
    image_shape = images[0].shape
    file_path = file_path.replace(file_path.split('.')[-1], 'avi')
    size = (image_shape[1], image_shape[0])  # 需要转为视频的图片的尺寸
    video = cv2.VideoWriter(file_path, cv2.VideoWriter_fourcc(*'XVID'), fps, size)  # avi格式

    for i in range(len(images)):
        video.write(images[i])
    video.release()
    cv2.destroyAllWindows()

def videoToImages(video_path):
    cap = cv2.VideoCapture(video_path)  # 获取到一个视频
    images = list()
    while cap.isOpened():
        (flag, frame) = cap.read()  # 读取一张图像
        if (flag == True):
            # 设置保存路径
            images.append(frame)
        else:
            break
    fps = cap.get(cv2.CAP_PROP_FPS)
    return images, fps

def videoMerge(video1, video2, fps, file_path):#两个图片列表合并为一个视频
    merged = list()
    print(type(video1[0]))
    print(type(video2[0]))
    for img1, img2 in zip(video1, video2):
        merged.append(np.hstack((img1, img2)))
    imagesToVideo(merged, fps, file_path)

'''
将图片转化为avi视频
'''
def convertImagesToAVI(data_path, fps, out_file, ext):
    files = os.listdir(data_path)
    files = [x for x in files if x.endswith(ext)]
    new_files = sorted(files, key=lambda x: int(x[:-1 * len(ext)]))
    image = cv2.imread(os.path.join(data_path, new_files[0]))
    # 高， 宽， 通道数
    image_shape = image.shape

def mp4ToAvi(input_path, output_path):
    cap = cv2.VideoCapture(input_path)  # 获取到一个视频
    images = list()
    while cap.isOpened():
        (flag, frame) = cap.read()  # 读取一张图像
        if (flag == True):
            # 设置保存路径
            images.append(frame)
        else:
            break
    fps = cap.get(cv2.CAP_PROP_FPS)
    image_shape = images[0].shape
    size = (image_shape[1], image_shape[0])  # 需要转为视频的图片的尺寸
    video = cv2.VideoWriter(output_path, cv2.VideoWriter_fourcc(*'XVID'), fps, size)  # avi格式

    for i in range(len(images)):
        video.write(images[i])
    video.release()
    cv2.destroyAllWindows()

def convertImagesToMp4(data_path, fps, out_file, ext):
    files = os.listdir(data_path)
    files = [x for x in files if x.endswith(ext)]
    new_files = sorted(files, key=lambda x: int(x[:-1 * len(ext)]))
    image = cv2.imread(os.path.join(data_path, new_files[0]))
    # 高， 宽， 通道数
    image_shape = image.shape
    size = (image_shape[1], image_shape[0])  # 需要转为视频的图片的尺寸
    video = cv2.VideoWriter(out_file, cv2.VideoWriter_fourcc(*'mp4v'), fps, size)

    for i in range(len(new_files)):
        image_path = os.path.join(data_path, new_files[i])
        img = cv2.imread(image_path)
        video.write(img)

    video.release()
    cv2.destroyAllWindows()

'''
    将avi视频转化为图片
    '''

def convertVideoToImages(video_file, target_dir):
    cap = cv2.VideoCapture(video_file)  # 获取到一个视频
    isOpened = cap.isOpened  # 判断是否打开
    i = 0
    while isOpened:
        i += 1
        (flag, frame) = cap.read()  # 读取一张图像

        fileName = str(i) + ".png"
        if (flag == True):
            # 设置保存路径
            save_path = os.path.join(target_dir, fileName)
            if not cv2.imwrite(save_path, frame, [cv2.IMWRITE_JPEG_QUALITY, 100]):
                raise Exception("Could not write image")
        else:
            break


import re
import warnings
from collections import OrderedDict
from operator import attrgetter
from urllib.parse import urljoin
from weakref import WeakKeyDictionary

from django.core.exceptions import PermissionDenied
from django.core.validators import (
    DecimalValidator,
    EmailValidator,
    MaxLengthValidator,
    MaxValueValidator,
    MinLengthValidator,
    MinValueValidator,
    RegexValidator,
    URLValidator,
)
from django.db import models
from django.http import Http404
from django.utils.encoding import force_str, smart_str
from rest_framework import exceptions, renderers, serializers
from rest_framework.compat import uritemplate
from rest_framework.fields import empty, Field
from rest_framework.request import clone_request
from rest_framework.schemas.generators import EndpointEnumerator, get_pk_name
from rest_framework.schemas.utils import is_list_view, get_pk_description
from rest_framework.settings import api_settings
from rest_framework.utils import formatting
from rest_framework.views import APIView

class ServerSwagger:
    def __init__(self, url, description):
        self.url = url
        self.description = description

class _UnvalidatedField(Field):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.allow_blank = True
        self.allow_null = True

    def to_internal_value(self, data):
        return data

    def to_representation(self, value):
        return value

class SortedPathSchemaGenerator:
    endpoint_inspector_cls = EndpointEnumerator

    # 'pk' isn't great as an externally exposed name for an identifier,
    # so by default we prefer to use the actual model field name for schemas.
    # Set by 'SCHEMA_COERCE_PATH_PK'.
    coerce_path_pk = None

    def __init__(
            self,
            title=None,
            url=None,
            description=None,
            patterns=None,
            urlconf=None,
            version=None,
    ):
        if url and not url.endswith("/"):
            url += "/"

        self.coerce_path_pk = api_settings.SCHEMA_COERCE_PATH_PK

        self.patterns = patterns
        self.urlconf = urlconf
        self.title = title
        self.description = description
        self.version = version
        self.url = url
        self.endpoints = None

    def _initialise_endpoints(self):
        if self.endpoints is None:
            inspector = self.endpoint_inspector_cls(self.patterns, self.urlconf)
            self.endpoints = inspector.get_api_endpoints()

    def _get_paths_and_endpoints(self, request):
        """
        Generate (path, method, view) given (path, method, callback) for paths.
        """
        paths = []
        view_endpoints = []
        for path, method, callback in self.endpoints:
            view = self.create_view(callback, method, request)
            path = self.coerce_path(path, method, view)
            if view:
                paths.append(path)
                view_endpoints.append((path, method, view))
        return paths, view_endpoints

    def _user_has_perm(self, user, endpoint_view, method):
        view_perms = endpoint_view.get_permissions()
        is_allowed = []
        for view_perm in view_perms:
            perms = view_perm.get_required_permissions(method, endpoint_view.get_serializer().Meta.model)
            is_allowed.append(all(user.has_perm(perm) if perm else True for perm in perms))
        return all(is_allowed)

    def create_view(self, callback, method, request=None):
        """
        Given a callback, return an actual view instance.
        """
        view = callback.cls(**getattr(callback, "initkwargs", {}))
        view.args = ()
        view.kwargs = {}
        view.format_kwarg = None
        view.request = None
        view.action_map = getattr(callback, "actions", None)

        actions = getattr(callback, "actions", None)
        if actions is not None:
            if method == "OPTIONS":
                view.action = "metadata"
            else:
                view.action = actions.get(method.lower())

        if request is not None:
            view.request = clone_request(request, method)
        is_allowed = True
        if not request.user.is_superuser:
            is_allowed = self._user_has_perm(request.user, view, method)
        if is_allowed:
            return view

    def coerce_path(self, path, method, view):
        """
        Coerce {pk} path arguments into the name of the model field,
        where possible. This is cleaner for an external representation.
        (Ie. "this is an identifier", not "this is a database primary key")
        """
        if not self.coerce_path_pk or "{pk}" not in path:
            return path
        model = getattr(getattr(view, "queryset", None), "model", None)
        if model:
            field_name = get_pk_name(model)
        else:
            field_name = "id"
        return path.replace("{pk}", "{%s}" % field_name)

    def has_view_permissions(self, path, method, view):
        """
        Return `True` if the incoming request has the correct view permissions.
        """
        if view.request is None:
            return True

        try:
            view.check_permissions(view.request)
        except (exceptions.APIException, Http404, PermissionDenied):
            return False
        return True

    def get_info(self):
        # Title and version are required by openapi specification 3.x
        info = {"title": self.title or "", "version": self.version or ""}

        if self.description is not None:
            info["description"] = self.description

        return info

    def get_paths(self, request=None):
        result = {}

        self._initialise_endpoints()

        paths, view_endpoints = self._get_paths_and_endpoints(request)

        # Only generate the path prefix for paths that will be included
        if not paths:
            return None

        for path, method, view in view_endpoints:
            if not self.has_view_permissions(path, method, view):
                continue
            operation = view.schema.get_operation(path, method)
            # Normalise path for any provided mount url.
            if path.startswith("/"):
                path = path[1:]
            path = urljoin(self.url or "/", path)

            result.setdefault(path, {})
            result[path][method.lower()] = operation
        return result

    def get_schema(self, request=None, public=False, servers: [ServerSwagger] = None):
        """
        Generate a OpenAPI schema.
        """

        paths = self.get_paths(None if public else request)
        if not paths:
            return None

        schema = {
            "openapi": "3.0.2",
            "info": self.get_info(),
            "components": {
                "securitySchemes": {
                    "ApiKeyAuth": {
                        "type": "apiKey",
                        "in": "header",
                        "name": "Authorization",
                        "value": "Token ",
                    }
                }
            },

            "paths": dict(OrderedDict(sorted(paths.items(), key=lambda t: t[0]))),
        }
        if servers is not None:
            servers = []
            for e in servers:
                servers.append({"url": e.url, "description": e.description})
            schema['servers'] = servers
        if schema is None:
            return
        return schema

class CustomAutoSchema:
    request_media_types = []
    response_media_types = []

    method_mapping = {
        "get": "Retrieve",
        "post": "Create",
        "put": "Update",
        "patch": "PartialUpdate",
        "delete": "Destroy",
    }

    header_regex = re.compile("^[a-zA-Z][0-9A-Za-z_]*:")

    def __init__(self):
        self.instance_schemas = WeakKeyDictionary()

    def __get__(self, instance, owner):
        """
        Enables `ViewInspector` as a Python _Descriptor_.

        This is how `view.schema` knows about `view`.

        `__get__` is called when the descriptor is accessed on the owner.
        (That will be when view.schema is called in our case.)

        `owner` is always the owner class. (An APIView, or subclass for us.)
        `instance` is the view instance or `None` if accessed from the class,
        rather than an instance.

        See: https://docs.python.org/3/howto/descriptor.html for info on
        descriptor usage.
        """
        if instance in self.instance_schemas:
            return self.instance_schemas[instance]

        self.view = instance
        return self

    def __set__(self, instance, other):
        self.instance_schemas[instance] = other
        if other is not None:
            other.view = instance

    @property
    def view(self):
        """View property."""
        assert self._view is not None, (
            "Schema generation REQUIRES a view instance. (Hint: you accessed "
            "`schema` from the view class rather than an instance.)"
        )
        return self._view

    @view.setter
    def view(self, value):
        self._view = value

    @view.deleter
    def view(self):
        self._view = None

    def get_description(self, path, method):
        """
        Determine a path description.

        This will be based on the method docstring if one exists,
        or else the class docstring.
        """
        view = self.view

        method_name = getattr(view, "action", method.lower())
        method_docstring = getattr(view, method_name, None).__doc__
        if method_docstring:
            # An explicit docstring on the method or action.
            return self._get_description_section(
                view, method.lower(), formatting.dedent(smart_str(method_docstring))
            )
        else:
            return self._get_description_section(
                view,
                getattr(view, "action", method.lower()),
                view.get_view_description(),
            )

    def _get_description_section(self, view, header, description):
        lines = [line for line in description.splitlines()]
        current_section = ""
        sections = {"": ""}

        for line in lines:
            if self.header_regex.match(line):
                current_section, separator, lead = line.partition(":")
                sections[current_section] = lead.strip()
            else:
                sections[current_section] += "\n" + line

        # TODO: SCHEMA_COERCE_METHOD_NAMES appears here and in `SchemaGenerator.get_keys`
        coerce_method_names = api_settings.SCHEMA_COERCE_METHOD_NAMES
        if header in sections:
            return sections[header].strip()
        if header in coerce_method_names:
            if coerce_method_names[header] in sections:
                return sections[coerce_method_names[header]].strip()
        return sections[""].strip()

    def get_operation(self, path, method):
        operation = {
            "operationId": self._get_operation_id(path, method),
            "description": self.get_description(path, method),
        }

        tags = self.get_tags(path, method)
        if tags:
            operation["tags"] = tags

        if self.is_deprecated(path, method):
            operation["deprecated"] = True

        parameters = []
        parameters += self._get_path_parameters(path)
        parameters += self._get_pagination_parameters(path, method)
        parameters += self._get_filter_parameters(path, method)

        operation["parameters"] = parameters

        request_body = self._get_request_body(path, method)
        if request_body:
            operation["requestBody"] = request_body
        operation["responses"] = self._get_responses(path, method)

        return operation

    def _get_operation_id(self, path, method):
        """
        Compute an operation ID from the model, serializer or view name.
        """
        method_name = getattr(self.view, "action", method.lower())
        if is_list_view(path, method, self.view):
            action = "list"
        elif method_name not in self.method_mapping:
            action = method_name
        else:
            action = self.method_mapping[method.lower()]

        # Try to deduce the ID from the view's model
        model = getattr(getattr(self.view, "queryset", None), "model", None)
        if model is not None:
            name = model.__name__

        # Try with the serializer class name
        elif hasattr(self.view, "get_serializer_class"):
            name = self.view.get_serializer_class().__name__
            if name.endswith("Serializer"):
                name = name[:-10]

        # Fallback to the view name
        else:
            name = self.view.__class__.__name__
            if name.endswith("APIView"):
                name = name[:-7]
            elif name.endswith("View"):
                name = name[:-4]

            # Due to camel-casing of classes and `action` being lowercase, apply title in order to find if action truly
            # comes at the end of the name
            if name.endswith(
                    action.title()
            ):  # ListView, UpdateAPIView, ThingDelete ...
                name = name[: -len(action)]

        if action == "list" and not name.endswith(
                "s"
        ):  # listThings instead of listThing
            name += "s"

        return action + name

    def get_tags(self, path, method):
        """
        Compute the tags.
        """
        try:
            return self.view.tags
        except AttributeError:
            return

    def is_deprecated(self, path, method):
        """
        Is deprecated.
        """
        try:
            return self.view.deprecated
        except AttributeError:
            return False

    def _get_path_parameters(self, path):
        """
        Return a list of parameters from templated path variables.
        """
        assert (
            uritemplate
        ), "`uritemplate` must be installed for OpenAPI schema support."

        model = getattr(getattr(self.view, "queryset", None), "model", None)
        parameters = []

        for variable in uritemplate.variables(path):
            description = ""
            if model is not None:  # TODO: test this.
                # Attempt to infer a field description if possible.
                try:
                    model_field = model._meta.get_field(variable)
                except Exception:
                    model_field = None

                if model_field is not None and model_field.help_text:
                    description = force_str(model_field.help_text)
                elif model_field is not None and model_field.primary_key:
                    description = get_pk_description(model, model_field)

            parameter = {
                "name": variable,
                "in": "path",
                "required": True,
                "description": description,
                "schema": {"type": "string", },  # TODO: integer, pattern, ...
            }
            parameters.append(parameter)

        return parameters

    def _get_filter_parameters(self, path, method):
        if not self._allows_filters(path, method):
            return []
        parameters = []
        for filter_backend in self.view.filter_backends:
            parameters += filter_backend().get_schema_operation_parameters(self.view)
        return parameters

    def _allows_filters(self, path, method):
        """
        Determine whether to include filter Fields in schema.

        Default implementation looks for ModelViewSet or GenericAPIView
        actions/methods that cause filtering on the default implementation.
        """
        if getattr(self.view, "filter_backends", None) is None:
            return False
        if hasattr(self.view, "action"):
            return self.view.action in [
                "list",
                "retrieve",
                "update",
                "partial_update",
                "destroy",
            ]
        return method.lower() in ["get", "put", "patch", "delete"]

    def _get_pagination_parameters(self, path, method):
        view = self.view

        if not is_list_view(path, method, view):
            return []

        paginator = self._get_paginator()
        if not paginator:
            return []

        return paginator.get_schema_operation_parameters(view)

    def _map_field(self, field):

        # Nested Serializers, `many` or not.
        if isinstance(field, serializers.ListSerializer):
            return {"type": "array", "items": self._map_serializer(field.child)}
        if isinstance(field, serializers.Serializer):
            data = self._map_serializer(field)
            data["type"] = "object"
            return data

        # Related fields.
        if isinstance(field, serializers.ManyRelatedField):
            return {"type": "array", "items": self._map_field(field.child_relation)}
        if isinstance(field, serializers.PrimaryKeyRelatedField):
            model = getattr(field.queryset, "model", None)
            if model is not None:
                model_field = model._meta.pk
                if isinstance(model_field, models.AutoField):
                    return {"type": "integer"}

        # ChoiceFields (single and multiple).
        # Q:
        # - Is 'type' required?
        # - can we determine the TYPE of a choicefield?
        if isinstance(field, serializers.MultipleChoiceField):
            return {
                "type": "array",
                "items": {"enum": list(field.choices)},
            }

        if isinstance(field, serializers.ChoiceField):
            return {
                "enum": list(field.choices),
            }

        # ListField.
        if isinstance(field, serializers.ListField):
            mapping = {
                "type": "array",
                "items": {},
            }
            if not isinstance(field.child, _UnvalidatedField):
                map_field = self._map_field(field.child)
                items = {"type": map_field.get("type")}
                if "format" in map_field:
                    items["format"] = map_field.get("format")
                mapping["items"] = items
            return mapping

        # DateField and DateTimeField type is string
        if isinstance(field, serializers.DateField):
            return {
                "type": "string",
                "format": "date",
            }

        if isinstance(field, serializers.DateTimeField):
            return {
                "type": "string",
                "format": "date-time",
            }

        # "Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification."
        # see: https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types
        # see also: https://swagger.io/docs/specification/data-models/data-types/#string
        if isinstance(field, serializers.EmailField):
            return {"type": "string", "format": "email"}

        if isinstance(field, serializers.URLField):
            return {"type": "string", "format": "uri"}

        if isinstance(field, serializers.UUIDField):
            return {"type": "string", "format": "uuid"}

        if isinstance(field, serializers.IPAddressField):
            content = {
                "type": "string",
            }
            if field.protocol != "both":
                content["format"] = field.protocol
            return content

        # DecimalField has multipleOf based on decimal_places
        if isinstance(field, serializers.DecimalField):
            content = {"type": "number"}
            if field.decimal_places:
                content["multipleOf"] = float(
                    "." + (field.decimal_places - 1) * "0" + "1"
                )
            if field.max_whole_digits:
                content["maximum"] = int(field.max_whole_digits * "9") + 1
                content["minimum"] = -content["maximum"]
            self._map_min_max(field, content)
            return content

        if isinstance(field, serializers.FloatField):
            content = {"type": "number"}
            self._map_min_max(field, content)
            return content

        if isinstance(field, serializers.IntegerField):
            content = {"type": "integer"}
            self._map_min_max(field, content)
            # 2147483647 is max for int32_size, so we use int64 for format
            if (
                    int(content.get("maximum", 0)) > 2147483647
                    or int(content.get("minimum", 0)) > 2147483647
            ):
                content["format"] = "int64"
            return content

        if isinstance(field, serializers.FileField):
            return {"type": "string", "format": "binary"}

        # Simplest cases, default to 'string' type:
        FIELD_CLASS_SCHEMA_TYPE = {
            serializers.BooleanField: "boolean",
            serializers.JSONField: "object",
            serializers.DictField: "object",
            serializers.HStoreField: "object",
        }
        return {"type": FIELD_CLASS_SCHEMA_TYPE.get(field.__class__, "string")}

    def _map_min_max(self, field, content):
        if field.max_value:
            content["maximum"] = field.max_value
        if field.min_value:
            content["minimum"] = field.min_value

    def _map_serializer(self, serializer):
        # Assuming we have a valid serializer instance.
        # TODO:
        #   - field is Nested or List serializer.
        #   - Handle read_only/write_only for request/response differences.
        #       - could do this with readOnly/writeOnly and then filter dict.
        required = []
        properties = {}

        for field in serializer.fields.values():
            if isinstance(field, serializers.HiddenField):
                continue

            if field.required:
                required.append(field.field_name)

            schema = self._map_field(field)
            if field.read_only:
                schema["readOnly"] = True
            if field.write_only:
                schema["writeOnly"] = True
            if field.allow_null:
                schema["nullable"] = True
            if field.default and field.default != empty:  # why don't they use None?!
                schema["default"] = field.default
            if field.help_text:
                schema["description"] = str(field.help_text)
            self._map_field_validators(field, schema)

            properties[field.field_name] = schema

        result = {"properties": properties}
        if required:
            result["required"] = required

        return result

    def _map_field_validators(self, field, schema):
        """
        map field validators
        """
        for v in field.validators:
            # "Formats such as "email", "uuid", and so on, MAY be used even though undefined by this specification."
            # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#data-types
            if isinstance(v, EmailValidator):
                schema["format"] = "email"
            if isinstance(v, URLValidator):
                schema["format"] = "uri"
            if isinstance(v, RegexValidator):
                schema["pattern"] = v.regex.pattern
            elif isinstance(v, MaxLengthValidator):
                attr_name = "maxLength"
                if isinstance(field, serializers.ListField):
                    attr_name = "maxItems"
                schema[attr_name] = v.limit_value
            elif isinstance(v, MinLengthValidator):
                attr_name = "minLength"
                if isinstance(field, serializers.ListField):
                    attr_name = "minItems"
                schema[attr_name] = v.limit_value
            elif isinstance(v, MaxValueValidator):
                schema["maximum"] = v.limit_value
            elif isinstance(v, MinValueValidator):
                schema["minimum"] = v.limit_value
            elif isinstance(v, DecimalValidator):
                if v.decimal_places:
                    schema["multipleOf"] = float(
                        "." + (v.decimal_places - 1) * "0" + "1"
                    )
                if v.max_digits:
                    digits = v.max_digits
                    if v.decimal_places is not None and v.decimal_places > 0:
                        digits -= v.decimal_places
                    schema["maximum"] = int(digits * "9") + 1
                    schema["minimum"] = -schema["maximum"]

    def _get_paginator(self):
        pagination_class = getattr(self.view, "pagination_class", None)
        if pagination_class:
            return pagination_class()
        return None

    def map_parsers(self, path, method):
        return list(map(attrgetter("media_type"), self.view.parser_classes))

    def map_renderers(self, path, method):
        media_types = []
        for renderer in self.view.renderer_classes:
            # BrowsableAPIRenderer not relevant to OpenAPI spec
            if renderer == renderers.BrowsableAPIRenderer:
                continue
            media_types.append(renderer.media_type)
        return media_types

    def _get_serializer(self, method, path):
        view = self.view

        if not hasattr(view, "get_serializer"):
            return None

        try:
            return view.get_serializer()
        except exceptions.APIException:
            warnings.warn(
                "{}.get_serializer() raised an exception during "
                "schema generation. Serializer fields will not be "
                "generated for {} {}.".format(view.__class__.__name__, method, path)
            )
            return None

    def _get_request_body(self, path, method):
        if method not in ("PUT", "PATCH", "POST"):
            return {}

        self.request_media_types = self.map_parsers(path, method)

        serializer = self._get_serializer(path, method)

        if not isinstance(serializer, serializers.Serializer):
            return {}

        content = self._map_serializer(serializer)
        # No required fields for PATCH
        if method == "PATCH":
            content.pop("required", None)
        # No read_only fields for request.
        for name, schema in content["properties"].copy().items():
            if "readOnly" in schema:
                del content["properties"][name]

        return {"content": {ct: {"schema": content} for ct in self.request_media_types}}

    def _get_responses(self, path, method):
        # TODO: Handle multiple codes and pagination classes.
        if method == "DELETE":
            return {"204": {"description": ""}}

        self.response_media_types = self.map_renderers(path, method)

        item_schema = {}
        serializer = self._get_serializer(path, method)

        if isinstance(serializer, serializers.Serializer):
            item_schema = self._map_serializer(serializer)
            # No write_only fields for response.
            for name, schema in item_schema["properties"].copy().items():
                if "writeOnly" in schema:
                    del item_schema["properties"][name]
                    if "required" in item_schema:
                        item_schema["required"] = [
                            f for f in item_schema["required"] if f != name
                        ]

        if is_list_view(path, method, self.view):
            response_schema = {
                "type": "array",
                "items": item_schema,
            }
            paginator = self._get_paginator()
            if paginator:
                response_schema = paginator.get_paginated_response_schema(
                    response_schema
                )
        else:
            response_schema = item_schema

        return {
            "200": {
                "content": {
                    ct: {"schema": response_schema} for ct in self.response_media_types
                },
                # description is a mandatory property,
                # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#responseObject
                # TODO: put something meaningful into it
                "description": "",
            }
        }

class AdvanceApiView(APIView):
    schema = CustomAutoSchema()


from drift_dac.perturbation_shared_utils import *

__all__ = ['apply_shift']

def apply_shift(X_te_orig, y_te_orig, shift, is_categorical=None):

    if is_categorical is None:
        is_categorical = [False] * X_te_orig.shape[1]

    if isinstance(shift, Shift):
        shift_obj = shift
        print(shift.name.replace('_', ' ').title())
    else:
        raise ValueError("Input shift must be a Shift object.")

    X_te_1 = copy.deepcopy(X_te_orig)
    if shift_obj.feature_type == PerturbationConstants.NUMERIC:
        (X_te_1[:, ~is_categorical], y_te_1) = shift_obj.transform(X_te_orig[:, ~is_categorical].astype(float), y_te_orig)
    elif shift_obj.feature_type == PerturbationConstants.CATEGORICAL:
        (X_te_1[:, is_categorical], y_te_1) = shift_obj.transform(X_te_orig[:, is_categorical], y_te_orig)
    else:
        (X_te_1, y_te_1) = shift_obj.transform(X_te_orig, y_te_orig)

    indices, feat_indices = shift_obj.shifted_indices, shift_obj.shifted_features

    return (X_te_1, y_te_1), indices, feat_indices


#ejercicio2
#ingresar un valor y mostrar los numeros desde el -10 hasta el nuemro ingresado.

print("bienvedios".center(50,"*"))
numero= int(input("ingrese el valor"))
contador = -10
while contador <= numero:
    print (contador)
    contador+=1