import netCDF4
import numpy as np
from pyamg.classical import interpolate as amginterp
from pyamg.classical import split
from scipy import sparse, spatial

VERTICAL_TREATMENTS = {"indexLevel": 1,
                       "fixedZLevel": 2,
                       "passiveFloat": 3,
                       "buoyancySurface": 4,
                       "argoFloat": 5}
DEFAULTS = {"dt": 300, "resettime": 1.0 * 24.0 * 60.0 * 60.0}
TYPELIST = ["buoyancy", "passive", "surface", "all"]
VERTSEEDTYPE = ["linear", "denseCenter", "log"]
SPATIAL_FILTER = ["SouthernOceanPlanar", "SouthernOceanXYZ"]

def write(init_filename, graph_filename, particle_filename, types='all',
          n_vert_levels=10, vert_seed_type='linear', n_buoy_surf=11,
          pot_dens_min=1028.5, pot_dens_max=1030.0, spatial_filter=None,
          downsample=0, seed_center=True, seed_vertex=False,
          add_noise=False, cfl_min=0.005):
    """
    Write an initial condition for particles partitioned across cores

    Parameters
    ----------
    init_filename : str
        path of netCDF init/mesh file

    graph_filename : str
        path of graph partition file of form */*.info.part

    particle_filename : str
        path of output netCDF particle file

    types : {"buoyancy", "passive", "surface", "all"}, optional
        types of particles",

    n_vert_levels : int, optional
        number of vertical levels for passive, 3D floats

    vert_seed_type : {"linear", "denseCenter", "log"}, optional
        method for seeding in the vertical

    n_buoy_surf : int, optional
        number of buoyancy surfaces for isopycnally-constrained particles

    pot_dens_min : float, optional
        minimum value of potential density surface for isopycnally-constrained
        particles

    pot_dens_max : float, optional
        maximum value of potential density surface for isopycnally-constrained
        particles

    spatial_filter : {"SouthernOceanPlanar", "SouthernOceanXYZ"}, optional
        apply a certain type of spatial filter

    downsample : int, optional
        downsample particle positions using AMG a number of times

    seed_center : bool, optional
        seed particles on cell centers

    seed_vertex : bool, optional
        seed three particles by a fixed epsilon off each cell vertex

    add_noise : bool, optional
        add gaussian noise to generate three particles around the cell center

    cfl_min : float, optional
        minimum assumed CFL, which is used in perturbing particles if
        ``seed_vertex=True`` or ``add_noise=True``
    """

    buoy_surf = np.linspace(pot_dens_min, pot_dens_max, n_buoy_surf)
    cpts, xCell, yCell, zCell = _particle_coords(
        init_filename, downsample, seed_center, seed_vertex, add_noise,
        cfl_min)

    # build particles
    particlelist = []
    if "buoyancy" in types or "all" in types:
        particlelist.append(_build_isopycnal_particles(
            cpts, xCell, yCell, zCell, buoy_surf, spatial_filter))
    if "passive" in types or "all" in types:
        particlelist.append(_build_passive_floats(
            cpts, xCell, yCell, zCell, init_filename, n_vert_levels,
            spatial_filter, vert_seed_type))
    # apply surface particles everywhere to ensure that LIGHT works
    # (allow for some load-imbalance for filters)
    if "surface" in types or "all" in types:
        particlelist.append(_build_surface_floats(
            cpts, xCell, yCell, zCell, spatial_filter))

    # write particles to disk
    ParticleList(particlelist).write(particle_filename, graph_filename)

def remap_particles(init_filename, particle_filename, graph_filename):
    """
    Remap particles onto a new grid decomposition.

    Load in particle positions, locations of grid cell centers, and
    decomposition corresponding to ``init_filename``.

    The goal is to update particle field ``currentBlock`` to comply with the
    new grid as defined by ``init_filename`` and ``particle_filename``.
    NOTE: ``init_filename`` and ``graph_filename`` must be compatible!

    We assume that all particles will be within the domain such that a nearest
    neighbor search is sufficient to make the remap.

    Parameters
    ----------
    init_filename : str
        path of netCDF init/mesh file

    graph_filename : str
        path of graph partition file of form */*.info.part

    particle_filename : str
        path of input/output netCDF particle file
    """
    # load the files
    with netCDF4.Dataset(init_filename, "r") as f_in, \
            netCDF4.Dataset(particle_filename, "r+") as f_part:

        # get the particle data
        xpart = f_part.variables["xParticle"]
        ypart = f_part.variables["yParticle"]
        zpart = f_part.variables["zParticle"]
        currentBlock = f_part.variables["currentBlock"]
        try:
            currentCell = f_part.variables["currentCell"]
            currentCellGlobalID = f_part.variables["currentCellGlobalID"]
        except KeyError:
            currentCell = f_part.createVariable("currentCell", "i",
                                                ("nParticles",))
            currentCellGlobalID = f_part.createVariable(
                "currentCellGlobalID", "i", ("nParticles",))

        # get the cell positions
        xcell = f_in.variables["xCell"]
        ycell = f_in.variables["yCell"]
        zcell = f_in.variables["zCell"]

        # build the spatial tree
        tree = spatial.cKDTree(np.vstack((xcell, ycell, zcell)).T)

        # get nearest cell for each particle
        dvEdge = f_in.variables["dvEdge"]
        maxdist = 2.0 * max(dvEdge[:])
        _, cellIndices = tree.query(
            np.vstack((xpart, ypart, zpart)).T, distance_upper_bound=maxdist,
            k=1)

        # load the decomposition (apply to latest time step)
        decomp = np.genfromtxt(graph_filename)
        currentBlock[-1, :] = decomp[cellIndices]
        currentCell[-1, :] = -1
        currentCellGlobalID[-1, :] = cellIndices + 1

def build_particle_simple(f_grid, f_name, f_decomp, buoySurf, vertexReconst=1,
                          horizTreatment=1, timeInt=2, verticalTreatment=4):
    """

    Parameters
    ----------
    f_grid
    f_name
    f_decomp
    buoySurf
    vertexReconst
    horizTreatment
    timeInt
    verticalTreatment
    """

    nBuoySurf = buoySurf.shape[0]

    f_grid = netCDF4.Dataset(f_grid, 'r')

    nparticles = len(f_grid.dimensions['nCells'])
    f_out = netCDF4.Dataset(f_name, 'w', format='NETCDF3_CLASSIC')
    f_out.createDimension('nParticles', nparticles*nBuoySurf)
    f_out.createDimension('nBuoyancySurfaces', nBuoySurf)
    f_out.createDimension('Time')

    f_out.createVariable('xParticle', 'f8', ('Time', 'nParticles'))
    f_out.createVariable('yParticle', 'f8', ('Time', 'nParticles'))
    f_out.createVariable('zParticle', 'f8', ('Time', 'nParticles'))
    f_out.createVariable('dtParticle', 'f8', ('Time', 'nParticles'))
    f_out.createVariable('buoyancyParticle', 'f8', ('Time', 'nParticles'))
    f_out.createVariable('currentBlock', 'i', ('Time', 'nParticles'))
    f_out.createVariable('currentCell', 'i', ('Time', 'nParticles'))

    f_out.createVariable('indexToParticleID', 'i', ('nParticles',))
    f_out.createVariable('vertexReconstMethod', 'i', ('Time', 'nParticles'))
    f_out.createVariable('timeIntegration', 'i', ('Time', 'nParticles'))
    f_out.createVariable('horizontalTreatment', 'i', ('Time', 'nParticles'))
    f_out.createVariable('verticalTreatment', 'i', ('Time', 'nParticles'))
    f_out.createVariable('indexLevel', 'i', ('Time', 'nParticles'))
    f_out.createVariable('buoyancySurfaceValues', 'f8', ('nBuoyancySurfaces',))

    # reset variables
    f_out.createVariable('resetTime', 'i', ('nParticles',))
    f_out.createVariable('currentBlockReset', 'i', ('nParticles',))
    f_out.createVariable('currentCellReset', 'i', ('nParticles',))
    f_out.createVariable('xParticleReset', 'f8', ('nParticles',))
    f_out.createVariable('yParticleReset', 'f8', ('nParticles',))
    f_out.createVariable('zParticleReset', 'f8', ('nParticles',))

    f_out.variables['xParticle'][0, :] = \
        np.tile(f_grid.variables['xCell'][:], nBuoySurf)
    f_out.variables['yParticle'][0, :] = \
        np.tile(f_grid.variables['yCell'][:], nBuoySurf)
    f_out.variables['zParticle'][0, :] = \
        np.tile(f_grid.variables['zCell'][:], nBuoySurf)
    f_out.variables['buoyancyParticle'][0, :] = \
        (np.tile(buoySurf, (nparticles, 1))).reshape(
            nparticles*nBuoySurf, order='F').copy()
    f_out.variables['buoyancySurfaceValues'][:] = buoySurf
    f_out.variables['dtParticle'][0, :] = 300.0  # 5 min
    # assume single-processor mode for now
    f_out.variables['currentBlock'][:] = 0
    f_out.variables['resetTime'][:] = 1.0*24.0*60.0*60.0  # reset each day
    f_out.variables['vertexReconstMethod'][:] = vertexReconst
    f_out.variables['timeIntegration'][:] = timeInt
    f_out.variables['horizontalTreatment'][:] = horizTreatment
    f_out.variables['verticalTreatment'][:] = verticalTreatment
    f_out.variables['indexLevel'][:] = 1
    f_out.variables['indexToParticleID'][:] = np.arange(nparticles*nBuoySurf)

    # resets
    cellIndices = np.tile(np.arange(nparticles), (nBuoySurf,))
    decomp = np.genfromtxt(f_decomp)
    f_out.variables['currentBlock'][0, :] = decomp[cellIndices]
    f_out.variables['currentBlockReset'][:] = decomp[cellIndices]
    f_out.variables['currentCell'][0, :] = -1
    f_out.variables['currentCellReset'][:] = -1
    f_out.variables['xParticleReset'][:] = f_out.variables['xParticle'][0, :]
    f_out.variables['yParticleReset'][:] = f_out.variables['yParticle'][0, :]
    f_out.variables['zParticleReset'][:] = f_out.variables['zParticle'][0, :]

    f_out.close()

def _use_defaults(name, val):
    if (val is not None) or (val is not np.nan):
        return val
    else:
        return DEFAULTS[name]

def _ensure_shape(start, new):
    if isinstance(new, (int, float)):
        new *= np.ones_like(start)
    return new

def _southern_ocean_only_xyz(x, y, z, maxNorth=-45.0):
    sq = np.sqrt(x ** 2 + y ** 2 + z ** 2)
    lat = np.arcsin(z / sq)
    ok = np.pi / 180.0 * maxNorth
    ids = lat < ok
    return ids

def _southern_ocean_only_planar(x, y, z, maxy=1000.0 * 1e3):
    ids = y < maxy
    return ids

def _downsample_points(x, y, z, tri, nsplit):
    """
    Downsample points using algebraic multigrid splitting.

    Note, currently assumes that all points on grid are equidistant, which does
    a numeric (not area-weighted) downsampling.

    Phillip Wolfram
    LANL
    Origin: 03/09/2015, Updated: 01/14/2019
    """
    # reference on cleanest way to do this calculation:
    # https://www.mathworks.com/matlabcentral/answers/
    # 369143-how-to-do-delaunay-triangulation-and-return-an-adjacency-matrix

    # allocate the memory
    Np = x.shape[0]
    A = sparse.lil_matrix((Np, Np))

    # cleanup impartial cells (don't include the triangles on boundary)
    tri = tri[np.logical_not(np.any(tri == -1, axis=1)), :]

    # handle one direction for triangles
    A[tri[:, 0], tri[:, 1]] = 1
    A[tri[:, 1], tri[:, 2]] = 1
    A[tri[:, 2], tri[:, 0]] = 1

    # handle other direction (bi-directional graph)
    A[tri[:, 1], tri[:, 0]] = 1
    A[tri[:, 2], tri[:, 1]] = 1
    A[tri[:, 0], tri[:, 2]] = 1

    A = A.tocsr()

    Cpts = np.arange(Np)
    # Grab root-nodes (i.e., Coarse / Fine splitting)
    for ii in np.arange(nsplit):
        splitting = split.PMIS(A)
        # convert to index for subsetting particles
        Cpts = Cpts[np.asarray(splitting, dtype=bool)]

        if ii < nsplit - 1:
            P = amginterp.direct_interpolation(A, A, splitting)
            R = P.T.tocsr()
            A = R * A * P

    return Cpts, x[Cpts], y[Cpts], z[Cpts]

class Particles:
    def __init__(
        self,
        x,
        y,
        z,
        cellindices,
        verticaltreatment,
        dt=np.nan,
        zlevel=np.nan,
        indexlevel=np.nan,
        buoypart=np.nan,
        buoysurf=None,
        spatialfilter=None,
        resettime=np.nan,
        xreset=np.nan,
        yreset=np.nan,
        zreset=np.nan,
        zlevelreset=np.nan,
    ):

        # start with all the indices and restrict
        ids = np.ones((len(x)), dtype=bool)
        if type(spatialfilter) is str:
            spatialfilter = [spatialfilter]
        if spatialfilter:
            if np.max(["SouthernOceanXYZ" == afilter for afilter in
                       spatialfilter]):
                ids = np.logical_and(ids, _southern_ocean_only_xyz(x, y, z))
            if np.max(["SouthernOceanPlanar" == afilter for afilter in
                       spatialfilter]):
                ids = np.logical_and(ids, _southern_ocean_only_planar(x, y, z))

        self.x = x[ids]
        self.y = y[ids]
        self.z = z[ids]
        self.verticaltreatment = _ensure_shape(
            self.x, VERTICAL_TREATMENTS[verticaltreatment])
        self.nparticles = len(self.x)

        self.dt = dt

        # 3D passive floats
        self.zlevel = _ensure_shape(x, zlevel)[ids]

        # isopycnal floats
        if buoysurf is not None:
            self.buoysurf = buoysurf
        self.buoypart = _ensure_shape(x, buoypart)[ids]
        self.cellindices = cellindices[ids]
        self.cellGlobalID = cellindices[ids]

        # index level following floats
        self.indexlevel = _ensure_shape(x, indexlevel)[ids]

        # reset features
        self.resettime = _ensure_shape(x, resettime)[ids]
        self.xreset = _ensure_shape(x, xreset)[ids]
        self.yreset = _ensure_shape(x, yreset)[ids]
        self.zreset = _ensure_shape(x, zreset)[ids]
        self.zlevelreset = _ensure_shape(x, zlevelreset)[ids]

    def compute_lat_lon(self):
        """
        Ripped out whole-sale from latlon_coordinate_transforms.py
        PJW 01/15/2019
        """

        x = self.x
        y = self.y
        z = self.z

        self.latParticle = np.arcsin(z / np.sqrt(x ** 2 + y ** 2 + z ** 2))
        self.lonParticle = np.arctan2(y, x)

class ParticleList:
    def __init__(self, particlelist):
        self.particlelist = particlelist

    def aggregate(self):
        self.len()

        # buoyancysurf
        buoysurf = np.array([])
        for alist in self.particlelist:
            if "buoysurf" in dir(alist):
                buoysurf = np.unique(
                    np.setdiff1d(np.append(buoysurf, alist.buoysurf), None)
                )
        if len(buoysurf) > 0:
            self.buoysurf = np.asarray(buoysurf, dtype="f8")
        else:
            self.buoysurf = None

    def __getattr__(self, name):
        # __getattr__ ensures self.x is concatenated properly
        return self.concatenate(name)

    def concatenate(self, varname):
        var = getattr(self.particlelist[0], varname)
        for alist in self.particlelist[1:]:
            var = np.append(var, getattr(alist, varname))
        return var

    def append(self, particlelist):
        self.particlelist.append(particlelist[:])

    def len(self):
        self.nparticles = 0
        for alist in self.particlelist:
            self.nparticles += alist.nparticles

        return self.nparticles

    # probably a cleaner way to have this "fall through" to the particle
    # instances themselves, but didn't have time to sort this all out so this
    # isn't general for now
    def compute_lat_lon(self):
        for alist in self.particlelist:
            alist.compute_lat_lon()

    def write(self, f_name, f_decomp):

        decomp = np.genfromtxt(f_decomp)

        self.aggregate()
        assert (
            max(decomp) < self.nparticles
        ), "Number of particles must be larger than decomposition!"

        f_out = netCDF4.Dataset(f_name, "w", format="NETCDF3_64BIT_OFFSET")

        f_out.createDimension("Time")
        f_out.createDimension("nParticles", self.nparticles)

        f_out.createVariable("xParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("yParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("zParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("lonParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("latParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("zLevelParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("dtParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("buoyancyParticle", "f8", ("Time", "nParticles"))
        f_out.createVariable("currentBlock", "i", ("Time", "nParticles"))
        f_out.createVariable("currentCell", "i", ("Time", "nParticles"))
        f_out.createVariable("currentCellGlobalID", "i", ("Time",
                                                          "nParticles"))
        f_out.createVariable("indexToParticleID", "i", ("nParticles",))
        f_out.createVariable("verticalTreatment", "i", ("Time", "nParticles"))
        f_out.createVariable("indexLevel", "i", ("Time", "nParticles"))
        f_out.createVariable("resetTime", "i", ("nParticles",))
        f_out.createVariable("currentBlockReset", "i", ("nParticles",))
        f_out.createVariable("currentCellReset", "i", ("nParticles",))
        f_out.createVariable("xParticleReset", "f8", ("nParticles",))
        f_out.createVariable("yParticleReset", "f8", ("nParticles",))
        f_out.createVariable("zParticleReset", "f8", ("nParticles",))
        f_out.createVariable("zLevelParticleReset", "f8", ("nParticles",))

        f_out.variables["xParticle"][0, :] = self.x
        f_out.variables["yParticle"][0, :] = self.y
        f_out.variables["zParticle"][0, :] = self.z

        self.compute_lat_lon()
        f_out.variables["lonParticle"][0, :] = self.lonParticle
        f_out.variables["latParticle"][0, :] = self.latParticle

        f_out.variables["verticalTreatment"][0, :] = self.verticaltreatment

        f_out.variables["zLevelParticle"][0, :] = self.zlevel

        if self.buoysurf is not None and len(self.buoysurf) > 0:
            f_out.createDimension("nBuoyancySurfaces", len(self.buoysurf))
            f_out.createVariable("buoyancySurfaceValues", "f8",
                                 ("nBuoyancySurfaces"))
            f_out.variables["buoyancyParticle"][0, :] = self.buoypart
            f_out.variables["buoyancySurfaceValues"][:] = self.buoysurf

        f_out.variables["dtParticle"][0, :] = DEFAULTS["dt"]
        # assume single-processor mode for now
        f_out.variables["currentBlock"][:] = 0
        # reset each day
        f_out.variables["resetTime"][:] = DEFAULTS["resettime"]
        f_out.variables["indexLevel"][:] = 1
        f_out.variables["indexToParticleID"][:] = np.arange(self.nparticles)

        # resets
        f_out.variables["currentBlock"][0, :] = decomp[self.cellindices]
        f_out.variables["currentBlockReset"][:] = decomp[self.cellindices]
        f_out.variables["currentCell"][0, :] = -1
        f_out.variables["currentCellGlobalID"][0, :] = self.cellGlobalID + 1
        f_out.variables["currentCellReset"][:] = -1
        f_out.variables["xParticleReset"][:] = \
            f_out.variables["xParticle"][0, :]
        f_out.variables["yParticleReset"][:] = \
            f_out.variables["yParticle"][0, :]
        f_out.variables["zParticleReset"][:] = \
            f_out.variables["zParticle"][0, :]
        f_out.variables["zLevelParticleReset"][:] = \
            f_out.variables["zLevelParticle"][0, :]

        f_out.close()

def _rescale_for_shell(f_init, x, y, z):
    rearth = f_init.sphere_radius
    r = np.sqrt(x * x + y * y + z * z)
    x *= rearth / r
    y *= rearth / r
    z *= rearth / r
    return x, y, z

def _get_particle_coords(f_init, seed_center=True, seed_vertex=False,
                         add_noise=False, CFLmin=None):
    xCell = f_init.variables["xCell"][:]
    yCell = f_init.variables["yCell"][:]
    zCell = f_init.variables["zCell"][:]

    # Case of only cell-center seeding a single particle.
    if seed_center and not add_noise:
        cells_center = (xCell, yCell, zCell)
        cpts_center = np.arange(len(xCell))

    # Case of cell-center seeding with 3 particles distributed around the
    # center by noise.
    elif seed_center and add_noise:
        cellsOnCell = f_init.variables["cellsOnCell"][:, :]

        nCells = len(f_init.dimensions["nCells"])
        perturbation = CFLmin * np.ones((nCells,))

        allx = []
        ally = []
        allz = []
        allcpts = []
        # There are six potential cell neighbors to perturb the particles for.
        # This selects three random directions (without replacement) at every
        # cell.
        cellDirs = np.stack(
            [
                np.random.choice(np.arange(6), size=3, replace=False)
                for _ in range(nCells)
            ]
        )
        for ci in np.arange(3):
            epsilon = np.abs(np.random.normal(size=nCells))
            epsilon /= epsilon.max()
            # Adds gaussian noise at each cell, creating range of
            # [CFLMin, 2*CFLMin]
            theta = perturbation * epsilon + perturbation

            x = (1.0 - theta) * xCell + theta * xCell[
                cellsOnCell[range(nCells), cellDirs[:, ci]] - 1
            ]
            y = (1.0 - theta) * yCell + theta * yCell[
                cellsOnCell[range(nCells), cellDirs[:, ci]] - 1
            ]
            z = (1.0 - theta) * zCell + theta * zCell[
                cellsOnCell[range(nCells), cellDirs[:, ci]] - 1
            ]

            x, y, z = _rescale_for_shell(f_init, x, y, z)

            allx.append(x)
            ally.append(y)
            allz.append(z)
            allcpts.append(cellsOnCell[:, ci] - 1)
        cells_center = (
            np.concatenate(allx),
            np.concatenate(ally),
            np.concatenate(allz),
        )
        cpts_center = np.concatenate(allcpts)

    # Case of seeding 3 particles by a small epsilon around the vertices.
    if seed_vertex:
        cellsOnVertex = f_init.variables["cellsOnVertex"][:, :]
        xVertex = f_init.variables["xVertex"][:]
        yVertex = f_init.variables["yVertex"][:]
        zVertex = f_init.variables["zVertex"][:]

        nVertices = len(f_init.dimensions["nVertices"])
        perturbation = CFLmin * np.ones((nVertices,))

        allx = []
        ally = []
        allz = []
        allcpts = []
        for vi in np.arange(3):
            ids = np.where(cellsOnVertex[:, vi] != 0)[0]
            theta = perturbation[ids]

            x = (1.0 - theta) * xVertex[ids] + \
                theta * xCell[cellsOnVertex[ids, vi] - 1]
            y = (1.0 - theta) * yVertex[ids] + \
                theta * yCell[cellsOnVertex[ids, vi] - 1]
            z = (1.0 - theta) * zVertex[ids] + \
                theta * zCell[cellsOnVertex[ids, vi] - 1]

            x, y, z = _rescale_for_shell(f_init, x, y, z)

            allx.append(x)
            ally.append(y)
            allz.append(z)
            allcpts.append(cellsOnVertex[ids, vi] - 1)
        cells_vertex = (
            np.concatenate(allx),
            np.concatenate(ally),
            np.concatenate(allz),
        )
        cpts_vertex = np.concatenate(allcpts)

    # Allows for both cell-center and cell-vertex seeding.
    if seed_center and not seed_vertex:
        cells = cells_center
        cpts = cpts_center
    elif not seed_center and seed_vertex:
        cells = cells_vertex
        cpts = cpts_vertex
    else:
        cpts = np.concatenate((cpts_vertex, cpts_center))
        cells = (
            np.concatenate((cells_vertex[0], cells_center[0])),
            np.concatenate((cells_vertex[1], cells_center[1])),
            np.concatenate((cells_vertex[2], cells_center[2])),
        )
    return cells, cpts

def _expand_nlevels(x, n):
    return np.tile(x, (n))

def _particle_coords(
    f_init, downsample, seed_center, seed_vertex, add_noise, CFLmin
):

    f_init = netCDF4.Dataset(f_init, "r")
    cells, cpts = _get_particle_coords(
        f_init, seed_center, seed_vertex, add_noise, CFLmin
    )
    xCell, yCell, zCell = cells
    if downsample:
        tri = f_init.variables["cellsOnVertex"][:, :] - 1
        cpts, xCell, yCell, zCell = _downsample_points(
            xCell, yCell, zCell, tri, downsample
        )
    f_init.close()

    return cpts, xCell, yCell, zCell

def _build_isopycnal_particles(cpts, xCell, yCell, zCell, buoysurf, afilter):

    nparticles = len(xCell)
    nbuoysurf = buoysurf.shape[0]

    x = _expand_nlevels(xCell, nbuoysurf)
    y = _expand_nlevels(yCell, nbuoysurf)
    z = _expand_nlevels(zCell, nbuoysurf)

    buoypart = (
        (np.tile(buoysurf, (nparticles, 1)))
        .reshape(nparticles * nbuoysurf, order="F")
        .copy())
    cellindices = np.tile(cpts, (nbuoysurf))

    return Particles(x, y, z, cellindices, "buoyancySurface",
                     buoypart=buoypart, buoysurf=buoysurf,
                     spatialfilter=afilter)

def _build_passive_floats(cpts, xCell, yCell, zCell, f_init, nvertlevels,
                          afilter, vertseedtype):

    x = _expand_nlevels(xCell, nvertlevels)
    y = _expand_nlevels(yCell, nvertlevels)
    z = _expand_nlevels(zCell, nvertlevels)
    f_init = netCDF4.Dataset(f_init, "r")
    if vertseedtype == "linear":
        wgts = np.linspace(0, 1, nvertlevels + 2)[1:-1]
    elif vertseedtype == "log":
        wgts = np.geomspace(1.0 / (nvertlevels - 1), 1, nvertlevels + 1)[0:-1]
    elif vertseedtype == "denseCenter":
        wgts = _dense_center_seeding(nvertlevels)
    else:
        raise ValueError(
            "Must designate `vertseedtype` as one of the following: "
            + f"{VERTSEEDTYPE}"
        )
    zlevel = -np.kron(wgts, f_init.variables["bottomDepth"][cpts])
    cellindices = np.tile(cpts, (nvertlevels))
    f_init.close()

    return Particles(
        x, y, z, cellindices, "passiveFloat", zlevel=zlevel,
        spatialfilter=afilter)

def _dense_center_seeding(nVert):
    """
    Distributes passive floats with 50% of them occurring between 40% and 60%
    of the bottom depth.
    """
    nMid = np.ceil((1 / 2) * nVert)
    nRem = nVert - nMid
    if nRem % 2 != 0:
        nMid += 1
        nRem -= 1
    upper = np.linspace(0, 0.4, (int(nRem) // 2) + 1)
    center = np.linspace(0.4, 0.6, int(nMid) + 2)
    lower = np.linspace(0.6, 1, (int(nRem) // 2) + 1)
    c_wgts = np.concatenate([upper[1:], center[1:-1], lower[0:-1]])
    return c_wgts

def _build_surface_floats(cpts, xCell, yCell, zCell, afilter):

    x = _expand_nlevels(xCell, 1)
    y = _expand_nlevels(yCell, 1)
    z = _expand_nlevels(zCell, 1)
    cellindices = cpts

    return Particles(x, y, z, cellindices, "indexLevel", indexlevel=1,
                     zlevel=0, spatialfilter=afilter)

def _build_particle_file(f_init, f_name, f_decomp, types, spatialfilter,
                         buoySurf, nVertLevels, downsample, vertseedtype,
                         seed_center, seed_vertex, add_noise, CFLmin):

    cpts, xCell, yCell, zCell = _particle_coords(
        f_init, downsample, seed_center, seed_vertex, add_noise, CFLmin)

    # build particles
    particlelist = []
    if "buoyancy" in types or "all" in types:
        particlelist.append(
            _build_isopycnal_particles(
                cpts, xCell, yCell, zCell, buoySurf, spatialfilter))
    if "passive" in types or "all" in types:
        particlelist.append(
            _build_passive_floats(
                cpts, xCell, yCell, zCell, f_init, nVertLevels, spatialfilter,
                vertseedtype))
    # apply surface particles everywhere to ensure that LIGHT works
    # (allow for some load-imbalance for filters)
    if "surface" in types or "all" in types:
        particlelist.append(
            _build_surface_floats(cpts, xCell, yCell, zCell, spatialfilter))

    # write particles to disk
    ParticleList(particlelist).write(f_name, f_decomp)


from asyncio import Task
from typing import (
    Callable,
    Coroutine,
    Generator,
    Generic,
    List,
    Tuple,
    Type,
    TypeVar,
    Union,
    overload,
)

from mpyc.sectypes import SecureFixedPoint, SecureObject

InnerType = TypeVar("InnerType")
ReturnType = Union[InnerType, List[InnerType], List[List[InnerType]]]

def _ncopy(nested_list: InnerType) -> InnerType: ...
@overload
def _nested_list(
    rt: Type[SecureObject],
    n: int,
    dims: List[int],
) -> ReturnType[SecureObject]: ...
@overload
def _nested_list(
    rt: Type[None],
    n: int,
    dims: List[int],
) -> ReturnType[None]: ...
@overload
def _nested_list(
    rt: Callable[..., SecureFixedPoint],
    n: int,
    dims: List[int],
) -> ReturnType[SecureFixedPoint]: ...
def __reconcile(decl: InnerType, givn: InnerType) -> None: ...
def _reconcile(decl: InnerType, task: Task[InnerType]) -> None: ...
def mpc_coro(
    f: Callable[..., Coroutine[InnerType, None, InnerType]],
    pc: bool = ...,
) -> Callable[..., InnerType]: ...
def mpc_coro_ignore(
    func: Callable[..., Coroutine[InnerType, None, InnerType]]
) -> Callable[..., InnerType]: ...

SomeType = TypeVar("SomeType")

class YieldAwaitable(Generic[SomeType]):
    def __init__(self, value: SomeType) -> None: ...
    def __await__(self) -> Generator[SomeType, None, None]: ...

@overload
def returnType(
    return_type: Type[InnerType],
    *dimensions: int,
) -> YieldAwaitable[Union[InnerType, List[InnerType], List[List[InnerType]]]]: ...
@overload
def returnType(
    return_type: Tuple[Type[SecureObject], bool],
    *dimensions: int,
) -> YieldAwaitable[
    Union[SecureFixedPoint, List[SecureFixedPoint], List[List[SecureFixedPoint]]]
]: ...
@overload
def returnType_no_wrap(
    return_type: Type[None],
    *dimensions: int,
) -> None: ...
@overload
def returnType_no_wrap(
    return_type: Type[InnerType],
    *dimensions: int,
) -> Union[InnerType, List[InnerType], List[List[InnerType]]]: ...
@overload
def returnType_no_wrap(
    return_type: Tuple[Type[SecureObject], bool],
    *dimensions: int,
) -> Union[SecureFixedPoint, List[SecureFixedPoint], List[List[SecureFixedPoint]]]: ...


"""
Baidu TTS　Developer by Charley
"""
import voluptuous as vol
from homeassistant.components.tts import Provider, PLATFORM_SCHEMA, CONF_LANG
import homeassistant.helpers.config_validation as cv

import requests
import logging
import json

_Log=logging.getLogger(__name__)

# 默认语言
DEFAULT_LANG = 'zh'

# 支持的语言
SUPPORT_LANGUAGES = [
    'zh',
]

CONF_APIKEY = 'api_key'
CONF_SECRETKEY = 'secret_key'
CONF_SPEED =  'speed'
CONF_PITCH = 'pitch'
CONF_VOLUME = 'volume'
PERSON = 'person'

TOKEN_INTERFACE = 'https://openapi.baidu.com/oauth/2.0/token'
TEXT2AUDIO_INTERFACE = 'http://tsn.baidu.com/text2audio'

PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
    vol.Optional(CONF_LANG, default=DEFAULT_LANG): vol.In(SUPPORT_LANGUAGES),
    vol.Optional(CONF_APIKEY): cv.string,
    vol.Optional(CONF_SECRETKEY):cv.string,
    vol.Optional(CONF_SPEED,default='5'): cv.string,
    vol.Optional(CONF_PITCH,default='5'): cv.string,
    vol.Optional(CONF_VOLUME,default='5'): cv.string,
    vol.Optional(PERSON,default='0'): cv.string,
})

def get_engine(hass, config):
    lang = config.get(CONF_LANG)
    apiKey = config.get(CONF_APIKEY)
    secretKey = config.get(CONF_SECRETKEY)
    speed = config.get(CONF_SPEED)
    pitch = config.get(CONF_PITCH)
    volume = config.get(CONF_VOLUME)
    person = config.get(PERSON)

    if apiKey == None:
        _Log.error('Api Key is nil')
        return False
    if secretKey == None:
        _Log.error('secretKey is nil')
        return False

    return BaiduTTS(lang,apiKey,secretKey,speed,pitch,volume,person)

class BaiduTTS (Provider):

    def __init__(self,lang,apiKey,secretKey,speed,pitch,volume,person):
        self._lang = lang
        self._apiKey = apiKey
        self._secretKey = secretKey
        self._speed = speed
        self._pitch = pitch
        self._volume = volume
        self._person = person
        token = self.getToken()
        _Log.info("token =====>" + token)
        self._Token = token

    def getToken(self):
        resp = requests.get(TOKEN_INTERFACE,params={'grant_type': 'client_credentials','client_id':self._apiKey,'client_secret':self._secretKey})
        if resp.status_code != 200:
            _Log.error('Get ToKen Http Error status_code:%s' % resp.status_code)
            return None
        resp.encoding = 'utf-8'
        # toKenjsonStr =  resp.text
        tokenJson =  resp.json()

        if not 'access_token' in tokenJson:
            _Log.error('Get ToKen Json Error!')
            return None
        return tokenJson['access_token']

    @property
    def default_language(self):
        """Default language."""
        return self._lang

    @property
    def supported_languages(self):
        """List of supported languages."""
        return SUPPORT_LANGUAGES

    def get_tts_audio(self, message, language, options=None):
        if self._Token == None:
            self._Token = self.getToken()

        if self._Token == None:
            _Log.error('get_tts_audio Self.ToKen is nil')
            return

        resp = requests.get(TEXT2AUDIO_INTERFACE,params={'tex':message,'lan':language,'tok':self._Token,'ctp':'1','cuid':'HomeAssistant','spd':self._speed,'pit':self._pitch,'vol':self._volume,'per':self._person})

        if resp.status_code == 500:
            _Log.error('Text2Audio Error:500 Not Support.')
            return
        if resp.status_code == 501:
            _Log.error('Text2Audio Error:501 Params Error')
            return
        if resp.status_code == 502:
            _Log.error('Text2Audio Error:502 TokenVerificationError.')
            _Log.Info('Now Get Token!')
            self._Token = self.getToken()
            self.get_tts_audio(message,language,options)
            return
        if resp.status_code == 503:
            _Log.error('Text2Audio Error:503 Composite Error.')
            return

        if resp.status_code != 200:
            _Log.error('get_tts_audio Http Error status_code:%s' % resp.status_code)
            return

        data = resp.content
        return ('mp3',data)


# -*- coding: utf-8 -*-
import os
import sys
import time
import json
from contextlib import contextmanager

import requests

from covador.compat import bstr
from . import helpers

@contextmanager
def server(covfile, module_name, append_coverage=False):
    pid = os.fork()
    if not pid:
        import coverage
        cov = coverage.Coverage(covfile, source=['covador'])
        cov._auto_load = append_coverage
        cov.start()
        print('Server starting...')
        try:
            __import__(module_name)
            module = sys.modules[module_name]
            module.main()
            print('Server stopped')
        except:
            import traceback
            traceback.print_exc()
        sys.stdout.flush()
        sys.stderr.flush()
        cov.stop()
        cov.save()
        os._exit(0)

    for _ in range(50):
        try:
            resp = requests.get('http://127.0.0.1:5000/status/', timeout=2)
            resp.content
        except:
            time.sleep(0.1)
        else:
            break
    else:
        raise Exception('Server did not start')

    try:
        yield
    finally:
        os.kill(pid, 2)
        wait_till = time.time() + 10
        killed = False
        while time.time() < wait_till:
            if os.waitpid(pid, os.WNOHANG) == (0, 0):
                time.sleep(0.3)
                os.kill(pid, 2)
            else:
                killed = True
                break

        if not killed:
            print('Server did not stoped on SIGINT, sending SIGKILL')
            os.kill(pid, 9)

def do_tests():
    # qs
    resp = requests.get('http://127.0.0.1:5000/qs/?boo=boo', timeout=2)
    assert resp.content == b'boo'

    # form
    resp = requests.post('http://127.0.0.1:5000/form/', timeout=2,
                         data={'p1': bstr(u'буу', 'utf-8'), 'p2': 10})
    assert resp.content == bstr(u'буу.10', 'utf-8')

    resp = requests.post('http://127.0.0.1:5000/form/', timeout=2,
                         data='p1=boo&p2=10')
    assert resp.status_code == 400
    assert resp.json() == {'details': {'p1': 'Required item',
                                       'p2': 'Required item'},
                           'error': 'bad-request'}

    # mform
    resp = requests.post('http://127.0.0.1:5000/form/', timeout=2,
                         data=helpers.mform,
                         headers={'Content-Type': helpers.mct})
    assert resp.content == bstr(u'буу.10', 'utf-8')

    # params
    resp = requests.post('http://127.0.0.1:5000/params/', timeout=2,
                         params={'p2': 10}, data={'p1': 'boo'})
    assert resp.content == b'boo.10'

    # args
    resp = requests.get('http://127.0.0.1:5000/args/boo/', timeout=2)
    assert resp.content == b'boo'

    # error
    resp = requests.get('http://127.0.0.1:5000/qs/', timeout=2)
    assert resp.status_code == 400
    assert resp.json() == {'details': {'boo': 'Required item'},
                           'error': 'bad-request'}

    # json body
    data = {'boo': 'утф'}
    resp = requests.post('http://127.0.0.1:5000/json/', timeout=2, json=data)
    assert resp.content == bstr(u'утф', 'utf-8')

    resp = requests.post('http://127.0.0.1:5000/json/', timeout=2,
                         data=json.dumps(data))
    assert resp.status_code == 400
    assert resp.json() == {'details': {'boo': 'Required item'},
                           'error': 'bad-request'}

def test_flask():
    with server('flask.cov', 'integration.flask_server'):
        do_tests()

def test_tornado():
    with server('tornado.cov', 'integration.tornado_server'):
        do_tests()

def test_django():
    with server('django.cov', 'integration.django_server'):
        do_tests()

    with server('django.cov', 'integration.django_cbv_server', True):
        do_tests()

def test_aiohttp_async():
    with server('aiohttp_async.cov', 'integration.aiohttp_server'):
        do_tests()

    with server('aiohttp_async.cov', 'integration.aiohttp_cbv_server', True):
        do_tests()

def test_aiohttp_yield():
    with server('aiohttp_yield.cov', 'integration.aiohttp_yield_server'):
        do_tests()

def test_sanic():
    with server('sanic.cov', 'integration.sanic_server'):
        do_tests()

        resp = requests.get('http://127.0.0.1:5000/qs-multi/?boo=boo&boo=foo',
                            timeout=2)
        assert resp.content == b'boo,foo'


#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from flask import Flask, abort, request, jsonify
from flask_script import Manager
from flask_cors import CORS
import os
import logging
import boto3
from botocore.exceptions import ClientError
from ipfs import ipfs_hash
import urllib
import time
from log import init_log

app = Flask(__name__)
CORS(app)
app.config.from_pyfile('config.py')

manager = Manager(app)

init_log(app.config['LOGDNA_KEY'])
log = logging.getLogger('logdna')

global file_count
file_count = int(app.config['INIT_FILE_COUNT'])

def upload_file(file, bucket='dfile', object_name=None):
    """Upload a file to an S3 bucket

    :param file: File to upload
    :param bucket: Bucket to upload to
    :param object_name: S3 object name. If not specified then file_name is used
    :return: True if file was uploaded, else False
    """

    # Upload the file
    try:
        start1 = time.time()
        file_name = file.filename
        log.info('content_type: {}'.format(file.content_type))
        bytes = file.read()
        hash = ipfs_hash(bytes)
        log.info("ipfs hash: {0:.2f}s".format(time.time() - start1))
        name, ext = os.path.splitext(file_name)
        log.info('hash: {}, ext: {}'.format(hash, ext))

        # If S3 object_name was not specified, use ipfs hash
        if object_name is None:
            object_name = '{}{}'.format(hash, ext)

        session = boto3.session.Session()
        client = session.client('s3',
                                region_name=app.config['S3_REGION'],
                                endpoint_url=app.config['S3_ENDPOINT'],
                                aws_access_key_id=app.config['S3_KEY'],
                                aws_secret_access_key=app.config['S3_SECRET'])
        file.seek(0, 0)
        # https://boto3.amazonaws.com/v1/documentation/api/latest/reference/customizations/s3.html#boto3.s3.transfer.S3Transfer.ALLOWED_UPLOAD_ARGS
        # ExtraArgs:['ACL', 'CacheControl', 'ContentDisposition', 'ContentEncoding', 'ContentLanguage', 'ContentType', 'Expires', 'GrantFullControl',
        # 'GrantRead', 'GrantReadACP', 'GrantWriteACP', 'Metadata', 'RequestPayer', 'ServerSideEncryption', 'StorageClass', 'SSECustomerAlgorithm',
        # 'SSECustomerKey', 'SSECustomerKeyMD5', 'SSEKMSKeyId', 'WebsiteRedirectLocation']
        extra_args = {'ACL': 'public-read', 'ContentType': file.content_type, 'Metadata': {'name': urllib.parse.quote(file_name, safe='')}}
        start2 = time.time()
        response = client.upload_fileobj(file, bucket, object_name, ExtraArgs=extra_args)
        log.info("s3 upload: {0:.2f}s".format(time.time() - start2))
        log.info("total: {0:.2f}s".format(time.time() - start1))
        # log.info('res: {}'.format(response))
        return {'hash': object_name}
    except ClientError as e:
        log.exception("S3 Client Error! file name:{0}, exception:{1}".format(file.filename, str(e)))
        return {'hash': '', 'error': "S3 Client Error! \n"}
    except Exception as e:
        log.exception("S3 Upload Error! file name:{0}, exception:{1}".format(file.filename, str(e)))
        return {'hash': '', 'error': "S3 Upload Error! \n"}

@app.route("/", methods=["POST", "PUT"])
@app.route("/up", methods=["POST", "PUT"])
def up():
    try:
        if "file" in request.files:
            file = request.files["file"]

            log.info("file name: {}".format(file.filename), {'app': 'dfile-up-req'})

            res = upload_file(file)

            log.info("upload res: {}".format(res), {'app': 'dfile-up-res'})
            if not res['hash']:
                return res['error']
            global file_count
            file_count += 1
            url = app.config['DOMAIN'] + '/' + str(res['hash'])
            return url

        abort(400)

    except Exception as e:
        log.exception("Upload Error! exception:{}".format(str(e)))
        return "Upload Error! \n", 503

@app.route("/stat", methods=["GET"])
def stat():
    try:
        return jsonify({'file_count': file_count})

    except Exception as e:
        log.exception("Upload Error! exception:{}".format(str(e)))
        return "Upload Error! \n", 503

def legal():
    return "451 Unavailable For Legal Reasons\n", 451

@manager.command
def debug():
    app.run(debug=True, port=5000, host="0.0.0.0")
    log.info("DFile is debugging.")

if __name__ == "__main__":
    manager.run()
    log.info("DFile is running.")


import random
from flask import Flask, render_template, jsonify

# Load quotes with authors from a file
with open("quotes.txt", "r") as file:
    quotes = [line.strip() for line in file]

# Flask web app to serve the HTML
app = Flask(__name__, template_folder="templates", static_folder="static")

# Function to get a random quote and author
def get_random_quote_with_author():
    random_quote = random.choice(quotes)
    quote, author = random_quote.split(" - ", 1)
    return quote, author

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/get_quote")
def get_quote():
    quote, author = get_random_quote_with_author()
    return jsonify({"quote": quote, "author": author})

if __name__ == "__main__":
    app.debug = True  # Enable debug mode
    app.run()


import multiprocessing
import time
import sys

# To wait until a process has completed its work and exited, use the join() method.

def daemon():
	name = multiprocessing.current_process().name
	print('Starting:', name)
	time.sleep(2)
	print('Exiting :', name)

def non_daemon():
	name = multiprocessing.current_process().name
	print('Starting:', name)
	print('Exiting :', name)

if __name__ == '__main__':
	d = multiprocessing.Process(
	name='daemon',
	target=daemon,
	)
	d.daemon = True

	n = multiprocessing.Process(
	name='non-daemon',
	target=non_daemon,
	)
	n.daemon = False

	d.start()
	time.sleep(1)
	n.start()
	d.join()
	n.join()


import requests
from lxml import etree
from DataTools.tools import MysqlHelper

# 抓取微博找人下关键词搜索50页用户数据并存入数据库

def write_data(nickname, uid, kw):
    mysqlHelper = MysqlHelper('localhost', 'root', '123456', 'weibo_userinfo')
    mysqlHelper.connect()
    params = [nickname, uid, kw]
    sql = 'insert into User(nickname,uid,kw) values(%s,%s,%s) '
    count = mysqlHelper.insert(sql, params)
    if count > 0:
        print('成功写入数据库')

headers = {
    'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
    'Accept-Language': 'en',
    'User-Agent': 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:61.0) Gecko/20100101 Firefox/61.0',
}
# 设置关键词
kw = '创始人'

for page in range(1, 51):
    url = 'https://s.weibo.com/user?q=' + kw + '&Refer=weibo_user&page=' + str(page)

    response = requests.get(url, headers=headers)
    html = etree.HTML(response.content)
    user_list = html.xpath('//div[@id="pl_user_feedList"]//div[@class="card card-user-b s-pg16 s-brt1"]')
    for userinfo in user_list:
        userName = userinfo.xpath('.//div[@class="info"]//a[@class="name"]')[0].xpath('string(.)')
        userId = userinfo.xpath('.//div[@class="info"]//a[@class="s-btn-c"]/@uid')[0]
        print('用户昵称：', userName, '用户ID：', userId)

        count = write_data(userName, userId, kw)


"""Contains class for graph page"""

import json
from datetime import datetime, timedelta
import sys

from tuckshop.page.page_base import PageBase, VariableVerificationTypes
from tuckshop.core.tuckshop_exception import TuckshopException
from tuckshop.core.utils import getMoneyString
from tuckshop.app.models import (InventoryTransaction, StockPayment,
                                 Transaction, User, Inventory)
from tuckshop.core.simulation import FloatHistorySimulation
from tuckshop.core.permission import Permission

class Graph(PageBase):

    NAME = 'Graph'
    TEMPLATE = 'graph'
    REQUIRES_AUTHENTICATION = True
    PERMISSION = Permission.FLOAT_ACCESS
    MENU_ORDER = 4
    URL = '/graph'

    def processPage(self):
        def filter_data(filter_vars, transactions, users, stock_payments, stock_payment_transactions, inventory_transactions, inventory):
            import datetime
            transactions = transactions.filter(timestamp__lt=datetime.date(filter_vars['year'], filter_vars['month'], filter_vars['day']))
            inventory_transactions = inventory_transactions.filter(timestamp__lt=datetime.date(filter_vars['year'], filter_vars['month'], filter_vars['day']))
            stock_payments = stock_payments.filter(timestamp__lt=datetime.date(filter_vars['year'], filter_vars['month'], filter_vars['day']))
            stock_payment_transactions = stock_payment_transactions.filter(timestamp__lt=datetime.date(filter_vars['year'], filter_vars['month'], filter_vars['day']))
            return filter_vars, transactions, users, stock_payments, stock_payment_transactions, inventory_transactions, inventory

        not_enough_data = 0
        float_amount = ['Float']
        total_user_balance = ['User Credit']
        stock_value = ['Stock Value']
        owed = ['Owed Stock']
        sup_float = ['Superficial Float']
        for days_back in list(reversed(range(1, 29))):
            d = datetime.today() - timedelta(days=days_back)
            sim = FloatHistorySimulation(name='test', filter_method=filter_data, filter_vars={'year': d.year, 'month': d.month, 'day': d.day})
            cel = sim.start()
            try:
                if cel.successful():
                    data = cel.get(timeout=1)
                    float_amount.append(float(data['float_amount']/100.0))
                    total_user_balance.append(float(data['total_user_balance']/100.0))
                    stock_value.append(float(data['stock_value']/100.0))
                    owed.append(float(data['owed']/100.0))
                    sup_float.append(float((((data['float_amount'] - data['total_user_balance']) - data['owed']) + data['stock_value'])/100.0))
                elif cel.state in ('FAILURE', 'REVOKED'):
                    sim.delete_cache()
                    sim.start()
                    not_enough_data += 1
                else:
                    not_enough_data += 1
            except:
                sim.delete_cache()
                not_enough_data += 1

        self.return_vars['graph_data'] = json.dumps([float_amount, total_user_balance, stock_value, owed, sup_float]) if not not_enough_data else None
        self.return_vars['not_enough_data'] = not_enough_data


import openpyxl

def delete_columns_with_specific_text(file_path, sheet_name, text_to_match):
    """
    删除Excel工作表中列名包含特定文本的列

    :param file_path: Excel文件的路径
    :param sheet_name: 要操作的工作表名称
    :param text_to_match: 要匹配的文本
    """
    # 加载工作簿和工作表
    workbook = openpyxl.load_workbook(file_path)
    sheet = workbook[sheet_name]

    # 获取第一行（标题行）的值
    header_row = list(sheet.iter_rows(min_row=1, max_row=1, values_only=True))[0]

    # 查找包含特定文本的列索引
    columns_to_delete = [index for index, value in enumerate(header_row, start=1) if value and text_to_match in str(value)]

    # 按降序排列列索引，以便在删除时保持正确的索引
    columns_to_delete.sort(reverse=True)

    for col_index in columns_to_delete:
        # 删除指定的列
        sheet.delete_cols(col_index)

    # 保存更改后的工作簿
    workbook.save(file_path)
    print(f"删除了{len(columns_to_delete)}列")

# # 示例用法
# file_path = "your_excel_file.xlsx"  # 你的Excel文件路径
# sheet_name = "Sheet1"  # 你要操作的工作表名称
# text_to_match = "example"  # 要匹配的文本

# delete_columns_with_specific_text(file_path, sheet_name, text_to_match)


import base64
import subprocess
import time
from collections import defaultdict
from functools import partial
import os
from examples.useful_python.examples.spiders.utils.tools import MyAES

subprocess.Popen = partial(subprocess.Popen, encoding="utf-8")
import execjs
import requests

common_headers = {
    "Host": "match.yuanrenxue.com",
    "Connection": "keep-alive",
    "sec-ch-ua": '" Not A;Brand";v="99", "Chromium";v="96", "Google Chrome";v="96"',
    "Accept": "application/json, text/javascript, */*; q=0.01",
    "X-Requested-With": "XMLHttpRequest",
    "sec-ch-ua-mobile": "?0",
    "User-Agent": "yuanrenxue.project",
    "sec-ch-ua-platform": "Windows",
    "Sec-Fetch-Site": "same-origin",
    "Sec-Fetch-Mode": "cors",
    "Sec-Fetch-Dest": "empty",
    "Referer": "https://match.yuanrenxue.com/match/5",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "zh,zh-HK;q=0.9,ru;q=0.8,en;q=0.7,en-US;q=0.6,zh-CN;q=0.5",
}

common_cookies = {
    'sessionid': 'c9jqi35m9wa3k16kzd63h1jzsljy332f',
}
proxies = {
    'http': '127.0.0.1:8888',
    'https': '127.0.0.1:8888',
}

total = defaultdict(int)

session = requests.session()
session.headers = common_headers
session.cookies.update({
    "sessionid": "q9mzvylmp1p61z3xbkoyanr7cs3v20l9",
})
t = int(time.time() * 1000)
f = open("sdk.js", "r+", encoding='utf8', )
js_file = f.read()
js_compile = execjs.compile(js_file)
temp1 = js_compile.call("getCookie")
m = temp1[2]
t = temp1[0]
text = temp1[3]

# t = 1641287390884
sec_key = base64.b64encode(str(t).encode()).decode()[0:16]
print('sec_key', sec_key, t)
myAes = MyAES(sec_key)

print("加密文本:", text)
# text = '2c19c45fe56c91072c0eff540547b553,10b0395acfe3e66f0c14649561d1f3fe,6ab5e43cbee9e88be053f49d1f69ee83,e29ff5852aee529eff2f72473a097bc4,e10f123fa9ab8a2524512376eef09010'
aes_text = myAes.aes_encrypt_ecb(text, True)
print("加密后结果:", aes_text)
print(t)
session.cookies.update({
    "m": m,
    "RM4hZBv0dDon443M": aes_text,
})
print(session.cookies)
for i in range(1, 6):

    ret = session.get(
        url=f'https://match.yuanrenxue.com/api/match/5?page={i}&m={t}&f={int(t / 1000) * 1000}',
        proxies=proxies, verify=False)

    print(ret.text, 'olkk')
    json_content = ret.json()
    items = json_content.get("data")
    for item in items:
        total[str(item.get("value"))] += 1

total_list = []
for k, v in total.items():
    total_list.append((k, v))

total_list.sort(key=lambda x: x[0], reverse=True)
print(total_list)
total = 0
for i in range(0, 5):
    total += int(total_list[i][0])

print(total)


import datetime
import os
import time
import uuid

import amri.utils.constants as utils_constants
from amri.usernode.google_stt import google_stt
from amri.usernode.google_tts import google_tts
from amri.utils import log_utils

audio_file_path = os.path.dirname(os.path.abspath(__file__))
audio_file_path = os.path.normpath(os.path.join(audio_file_path, 'output.mp3'))

def main():
    """
    Returns:
    --------
    subject_info : list
        List of subject parameter information; first entry in the list is timestamp
    """
    # Initialize Google TTS and Google STT
    google_stt_obj = google_stt()
    google_tts_obj = google_tts()

    text = 'Welcome, let us begin the self-administered M R exam.'
    log_utils.log(text)
    google_tts_obj.speak(text)

    text = '\nWhat is your last name?'
    log_utils.log(text)
    google_tts_obj.speak(text)
    last_name = google_stt_obj.listen()

    text = '\nWhat is your gender?'
    log_utils.log(text)
    google_tts_obj.speak(text)
    while True:
        gender = google_stt_obj.listen()
        gender = 'male' if gender == 'mail' else gender
        gender = 'male' if gender == 'men' else gender
        if gender != 'male' and gender != 'female' and gender != 'other':
            text = 'Sorry, I couldn\'t understand that, let us try again. What is your gender?'
            google_tts_obj.speak(text)
        else:
            break

    text = '\nWhat is your age?'
    log_utils.log(text)
    google_tts_obj.speak(text)
    while True:
        age = google_stt_obj.listen()
        if not age.isnumeric() or int(age) > 100 or int(age) < 1:
            text = 'Sorry, I couldn\'t understand that, let us try again. What is your age?'
            google_tts_obj.speak(text)
        else:
            break
    current_year = datetime.datetime.now().year
    dob = '1/1/{}'.format(current_year - int(age))

    text = '\nWhat is your height in centimeters?'
    log_utils.log(text)
    google_tts_obj.speak(text)
    while True:
        height_cms = google_stt_obj.listen()
        if not height_cms.isnumeric() or int(height_cms) < 121 or int(height_cms) > 213:
            text = 'Sorry, I couldn\'t understand that, let us try again. What is your height in centimeters?'
            google_tts_obj.speak(text)
        else:
            break

    text = '\nWhat is your weight in pounds?'
    log_utils.log(text)
    google_tts_obj.speak(text)
    while True:
        weight_lbs = google_stt_obj.listen()
        if not weight_lbs.isnumeric() or int(weight_lbs) < 88 or int(weight_lbs) > 286:
            text = 'Sorry, I couldn\'t understand that, let us try again. What is your weight in pounds?'
            google_tts_obj.speak(text)
        else:
            break

    text = '\nAlright. Now, please state your application.'
    log_utils.log(text)
    google_tts_obj.speak(text)
    application = google_stt_obj.listen()

    text = '\nHow many minutes would you like to spend on this exam?'
    log_utils.log(text)
    google_tts_obj.speak(text)
    while True:
        time_mins = google_stt_obj.listen()
        if ' ' in time_mins:
            time_mins = time_mins.split(' ')[0]
        if float(time_mins) < 0:
            text = 'Sorry, I couldn\'t understand that, let us try again. How many minutes would you like to spend on ' \
                   'this exam?'
            google_tts_obj.speak(text)
        else:
            time_mins = float(time_mins)
            break
    time_seconds = int(time_mins * 60)

    # Re-instantiate Google STT object
    del google_stt_obj
    text = '\nThank you. Now, please set yourself up in the scanner. When you are good to go, please say \'Start\'.'
    log_utils.log(text)
    google_tts_obj.speak(text)
    while True:
        try:
            google_stt_obj = google_stt()
            ready = google_stt_obj.listen(single_utterance=False)
        except Exception:
            log_utils.log(msg='.', endline='')
            del google_stt_obj
            continue
        if ready.lower() == 'start':
            break

    text = '\nAlright, we will begin momentarily...'
    log_utils.log(text)
    google_tts_obj.speak(text)

    os.remove(audio_file_path)

    patient_uuid = str(uuid.uuid4().int)
    patient_info = [time.time(), last_name, patient_uuid, dob, gender, height_cms, weight_lbs, application,
                    time_seconds]
    patient_info = dict(zip(utils_constants.PATIENT_PARAMS_KEYS, patient_info))

    del google_tts_obj
    return patient_info


from flask import Flask, jsonify, request, render_template
import requests
import pymongo
import time

app = Flask(__name__)

client = pymongo.MongoClient()

mydb = client['final']
webcol = mydb['webcol']

# Get document id in string format
def get_data(data):
    data['_id'] = str(data['_id'])
    return data

# Get last mongo collection document
def get_last_doc(col):
    try:
        last_doc = col.find().sort([('_id', -1)]).limit(1)[0]
    except:
        last_doc = 'error'

    return last_doc

# Get morse code
def get_morse(decimal):

    total = ''
    for num in decimal:
        if num == 0:
            morse = '11111'
        elif num == 1:
            morse = '01111'
        elif num == 2:
            morse = '00111'
        elif num == 3:
            morse = '00011'
        elif num == 4:
            morse = '00001'
        elif num == 5:
            morse = '00000'
        elif num == 6:
            morse = '10000'
        elif num == 7:
            morse = '11000'
        elif num == 8:
            morse = '11100'
        else:
            morse = '11110'

        total = total + morse

    return total

# receive get action from pi and return response based on form page
@app.route("/", methods=['GET', 'POST'])
def index():
    if(request.method == 'GET'):
        # Wait until form() updates webcol
        aux_doc = get_last_doc(webcol)
        while True:
            last_doc = get_last_doc(webcol)
            try:
                if last_doc['_id'] != aux_doc['_id']:
                    break
            except:
                print('cannot get last_doc')

        last_doc = get_last_doc(webcol)
        decimal = last_doc['decimal']
        morse = get_morse(decimal)
        return jsonify({'morse':morse})

# receive post action from html page, insert to db and return response
@app.route("/form", methods=['GET', 'POST'])
def form():

    if(request.method == 'POST'):
        # get and insert input into webcol
        webpost = request.form.get('inputval')
        #if len(webpost) > 10:
        #    last_webpost = 'input is longer than 10'
        #    return render_template('final.html', data=last_webpost)
        #elif webpost.isdigit():
        #    last_webpost = 'has letters'
        #    return render_template('final.html', data=last_webpost)

        webcol.insert_one({'decimal':webpost})
        # get last webcol document
        try:
            last_webpost = webcol.find().sort([('_id', -1)]).limit(1)[0]['decimal']
        except:
            last_webpost = 'error'
            print('cannot get last_webpost')

        return render_template('final.html', data=last_webpost)
    else:
        # get form with last_webpost
        try:
            last_webpost = webcol.find().sort([('_id', -1)]).limit(1)[0]['decimal']
        except:
            last_webpost = 'error'
            print('cannot get last_webpost')
        return render_template('final.html', data=last_webpost)

# query webcol-JSON
@app.route('/webson', methods=['GET'])
def get_webson():
    reg_data = [get_data(i) for i in webcol.find()]
    return jsonify(reg_data)

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


import api
import braille
import config
import controlTypes
import ctypes
import NVDAHelper
import NVDAState
import speech
import textInfos
import textUtils
import UIAHandler

from comtypes import COMError
from diffHandler import prefer_difflib
from logHandler import log
from typing import (
	Any,
	Optional,
)
from UIAHandler.utils import _getConhostAPILevel, _shouldUseWindowsTerminalNotifications
from UIAHandler.constants import WinConsoleAPILevel
from . import UIA, UIATextInfo
from ..behaviors import EnhancedTermTypedCharSupport, KeyboardHandlerBasedTypedCharSupport
from ..window import Window

class ConsoleUIATextInfo(UIATextInfo):
	"A TextInfo implementation for consoles with an IMPROVED, but not FORMATTED, API level."
	def __init__(self, obj, position, _rangeObj=None):
		collapseToEnd = None
		# We want to limit  textInfos to just the visible part of the console.
		# Therefore we specifically handle POSITION_FIRST, POSITION_LAST and POSITION_ALL.
		if not _rangeObj and position in (
			textInfos.POSITION_FIRST,
			textInfos.POSITION_LAST,
			textInfos.POSITION_ALL
		):
			try:
				_rangeObj, collapseToEnd = self._getBoundingRange(obj, position)
			except (COMError, RuntimeError):
				# We couldn't bound the console.
				log.warning("Couldn't get bounding range for console", exc_info=True)
				# Fall back to presenting the entire buffer.
				_rangeObj, collapseToEnd = None, None
		super(ConsoleUIATextInfo, self).__init__(obj, position, _rangeObj)
		if collapseToEnd is not None:
			self.collapse(end=collapseToEnd)

	def _getBoundingRange(self, obj, position):
		"""Returns the UIA text range to which the console should be bounded,
		and whether the textInfo should be collapsed after instantiation."""
		# microsoft/terminal#4495: In newer consoles,
		# IUIAutomationTextRange::getVisibleRanges returns a reliable contiguous range.
		_rangeObj = obj.UIATextPattern.GetVisibleRanges().GetElement(0)
		collapseToEnd = None
		if position == textInfos.POSITION_FIRST:
			collapseToEnd = False
		elif position == textInfos.POSITION_LAST:
			# The exclusive end hangs off the end of the visible ranges.
			# Move back one character to remain within bounds.
			_rangeObj.MoveEndpointByUnit(
				UIAHandler.TextPatternRangeEndpoint_End,
				UIAHandler.NVDAUnitsToUIAUnits['character'],
				-1
			)
			collapseToEnd = True
		return (_rangeObj, collapseToEnd)

	def move(self, unit, direction, endPoint=None):
		oldInfo = None
		if self.basePosition != textInfos.POSITION_CARET:
			# Ensure we haven't gone beyond the visible text.
			# UIA adds thousands of blank lines to the end of the console.
			boundingInfo = self.obj.makeTextInfo(textInfos.POSITION_ALL)
			oldInfo = self.copy()
		res = self._move(unit, direction, endPoint)
		# Console textRanges have access to the entire console buffer.
		# However, we want to limit ourselves to onscreen text.
		# Therefore, if the textInfo was originally visible,
		# but we are now above or below the visible range,
		# Restore the original textRange and pretend the move didn't work.
		if oldInfo:
			try:
				if (
					(
						self.compareEndPoints(boundingInfo, "startToStart") < 0
						or self.compareEndPoints(boundingInfo, "startToEnd") >= 0
					)
					and not (
						oldInfo.compareEndPoints(boundingInfo, "startToStart") < 0
						or oldInfo.compareEndPoints(boundingInfo, "startToEnd") >= 0
					)
				):
					self._rangeObj = oldInfo._rangeObj
					return 0
			except (COMError, RuntimeError):
				pass
		return res

	def _move(self, unit, direction, endPoint=None):
		"Perform a move without respect to bounding."
		return super(ConsoleUIATextInfo, self).move(unit, direction, endPoint)

	def _get_text(self) -> str:
		# #14689: IMPROVED and END_INCLUSIVE UIA consoles have many blank lines,
		# which slows speech dictionary processing to a halt
		res = super()._get_text()
		stripRes = res.rstrip("\r\n")
		IGNORE_TRAILING_WHITESPACE_LENGTH = 100
		if len(res) - len(stripRes) > IGNORE_TRAILING_WHITESPACE_LENGTH:
			return stripRes
		else:
			return res

	def __ne__(self, other):
		"""Support more accurate caret move detection."""
		return not self == other

class ConsoleUIATextInfoWorkaroundEndInclusive(ConsoleUIATextInfo):
	"""Implementation of various workarounds for pre-microsoft/terminal#4018
	conhost: fixes expand/collapse, uses rangeFromPoint instead of broken
	GetVisibleRanges for bounding, and implements word movement support."""
	def _getBoundingRange(self, obj, position):
		# We could use IUIAutomationTextRange::getVisibleRanges, but it seems very broken in consoles
		# once more than a few screens worth of content has been written to the console.
		# Therefore we resort to using IUIAutomationTextPattern::rangeFromPoint
		# for the top left, and bottom right of the console window.
		_rangeObj = None
		if position is textInfos.POSITION_FIRST:
			_rangeObj = self.__class__(obj, obj.location.topLeft)._rangeObj
		elif position is textInfos.POSITION_LAST:
			# Asking for the range at the bottom right of the window
			# Seems to sometimes ignore the x coordinate.
			# Therefore use the bottom left, then move   to the last character on that line.
			tempInfo = self.__class__(obj, obj.location.bottomLeft)
			tempInfo.expand(textInfos.UNIT_LINE)
			# We must pull back the end by one character otherwise when we collapse to end,
			# a console bug results in a textRange covering the entire console buffer!
			# Strangely the *very* last character is a special blank point
			# so we never seem to miss a real character.
			UIATextInfo.move(tempInfo, textInfos.UNIT_CHARACTER, -1, endPoint="end")
			tempInfo.setEndPoint(tempInfo, "startToEnd")
			_rangeObj = tempInfo._rangeObj
		elif position is textInfos.POSITION_ALL:
			first = self.__class__(obj, textInfos.POSITION_FIRST)
			last = self.__class__(obj, textInfos.POSITION_LAST)
			first.setEndPoint(last, "endToEnd")
			_rangeObj = first._rangeObj
		return (_rangeObj, None)

	def collapse(self, end=False):
		"""Works around a UIA bug on conhost versions before microsoft/terminal#4018.
		When collapsing, consoles seem to incorrectly push the start of the
		textRange back one character.
		Correct this by bringing the start back up to where the end is."""
		oldInfo = self.copy()
		super(ConsoleUIATextInfo, self).collapse(end=end)
		if not end:
			self._rangeObj.MoveEndpointByRange(
				UIAHandler.TextPatternRangeEndpoint_Start,
				oldInfo._rangeObj,
				UIAHandler.TextPatternRangeEndpoint_Start
			)

	def compareEndPoints(self, other, which):
		"""Works around a UIA bug on conhost versions before microsoft/terminal#4018.
		Even when a console textRange's start and end have been moved to the
		same position, the console incorrectly reports the end as being
		past the start.
		Compare to the start (not the end) when collapsed."""
		selfEndPoint, otherEndPoint = which.split("To")
		if selfEndPoint == "end" and self._isCollapsed():
			selfEndPoint = "start"
		if otherEndPoint == "End" and other._isCollapsed():
			otherEndPoint = "Start"
		which = f"{selfEndPoint}To{otherEndPoint}"
		return super().compareEndPoints(other, which=which)

	def setEndPoint(self, other, which):
		"""Override of L{textInfos.TextInfo.setEndPoint}.
		Works around a UIA bug on conhost versions before microsoft/terminal#4018 that means we can
		not trust the "end" endpoint of a collapsed (empty) text range
		for comparisons.
		"""
		selfEndPoint, otherEndPoint = which.split("To")
		# In this case, there is no need to check if self is collapsed
		# since the point of this method is to change its text range, modifying the "end" endpoint of a collapsed
		# text range is fine.
		if otherEndPoint == "End" and other._isCollapsed():
			otherEndPoint = "Start"
		which = f"{selfEndPoint}To{otherEndPoint}"
		return super().setEndPoint(other, which=which)

	def expand(self, unit):
		if unit == textInfos.UNIT_WORD:
			# UIA doesn't implement word movement, so we need to do it manually.
			lineInfo = self.copy()
			lineInfo.expand(textInfos.UNIT_LINE)
			offset = self._getCurrentOffsetInThisLine(lineInfo)
			start, end = self._getWordOffsetsInThisLine(offset, lineInfo)
			wordEndPoints = (
				(offset - start) * -1,
				end - offset - 1
			)
			if wordEndPoints[0]:
				self._rangeObj.MoveEndpointByUnit(
					UIAHandler.TextPatternRangeEndpoint_Start,
					UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER],
					wordEndPoints[0]
				)
			if wordEndPoints[1]:
				self._rangeObj.MoveEndpointByUnit(
					UIAHandler.TextPatternRangeEndpoint_End,
					UIAHandler.NVDAUnitsToUIAUnits[textInfos.UNIT_CHARACTER],
					wordEndPoints[1]
				)
		else:
			return super(ConsoleUIATextInfo, self).expand(unit)

	def _move(self, unit, direction, endPoint=None):
		if unit == textInfos.UNIT_WORD and direction != 0:
			# On conhost versions before microsoft/terminal#4018, UIA doesn't implement word
			# movement, so we need to do it manually.
			# Relative to the current line, calculate our offset
			# and the current word's offsets.
			lineInfo = self.copy()
			lineInfo.expand(textInfos.UNIT_LINE)
			offset = self._getCurrentOffsetInThisLine(lineInfo)
			start, end = self._getWordOffsetsInThisLine(offset, lineInfo)
			if direction > 0:
				# Moving in a forward direction, we can just jump to the
				# end offset of the current word and we're done.
				res = self.move(
					textInfos.UNIT_CHARACTER,
					end - offset,
					endPoint=endPoint
				)
			else:
				# Moving backwards
				wordStartDistance = (offset - start) * -1
				if wordStartDistance < 0:
					# We are after the beginning of a word.
					# So first move back to the start of the word.
					self.move(
						textInfos.UNIT_CHARACTER,
						wordStartDistance,
						endPoint=endPoint
					)
					offset += wordStartDistance
				# Try to move one character back before the start of the word.
				res = self.move(textInfos.UNIT_CHARACTER, -1, endPoint=endPoint)
				if res == 0:
					return 0
				offset -= 1
				# We are now positioned  within the previous word.
				if offset < 0:
					# We've moved on to the previous line.
					# Recalculate the current offset based on the new line we are now on.
					lineInfo = self.copy()
					lineInfo.expand(textInfos.UNIT_LINE)
					offset = self._getCurrentOffsetInThisLine(lineInfo)
				# Finally using the new offset,
				# Calculate the current word offsets and move to the start of
				# this word if we are not already there.
				start, end = self._getWordOffsetsInThisLine(offset, lineInfo)
				wordStartDistance = (offset - start) * -1
				if wordStartDistance < 0:
					self.move(
						textInfos.UNIT_CHARACTER,
						wordStartDistance,
						endPoint=endPoint
					)
		else:  # moving by a unit other than word
			res = super(ConsoleUIATextInfo, self).move(unit, direction, endPoint)
		if not endPoint:
			# #10191: IUIAutomationTextRange::move in consoles does not correctly produce a collapsed range
			# after moving.
			# Therefore manually collapse.
			self.collapse()
		return res

	def _getCurrentOffsetInThisLine(self, lineInfo):
		"""
		Given a caret textInfo expanded to line, returns the index into the
		line where the caret is located.
		This is necessary since Uniscribe requires indices into the text to
		find word boundaries, but UIA only allows for relative movement.
		"""
		# position a textInfo from the start of the line up to the current position.
		charInfo = lineInfo.copy()
		charInfo.setEndPoint(self, "endToStart")
		text = charInfo._rangeObj.getText(-1)
		offset = textUtils.WideStringOffsetConverter(text).wideStringLength
		return offset

	def _getWordOffsetsInThisLine(self, offset, lineInfo):
		lineText = lineInfo._rangeObj.getText(-1)
		# Convert NULL and non-breaking space to space to make sure
		# that words will break on them
		lineText = lineText.translate({0: u' ', 0xa0: u' '})
		start = ctypes.c_int()
		end = ctypes.c_int()
		# Uniscribe does some strange things when you give it a string  with
		# not more than two alphanumeric chars in a row.
		# Inject two alphanumeric characters at the end to fix this.
		lineText += "xx"
		lineTextLen = textUtils.WideStringOffsetConverter(lineText).wideStringLength
		NVDAHelper.localLib.calculateWordOffsets(
			lineText,
			lineTextLen,
			offset,
			ctypes.byref(start),
			ctypes.byref(end)
		)
		return (
			start.value,
			min(end.value, max(1, lineTextLen - 2))
		)

	def _isCollapsed(self):
		"""Works around a UIA bug on conhost versions before microsoft/terminal#4018 that means we
		cannot trust the "end" endpoint of a collapsed (empty) text range
		for comparisons.
		Instead we check to see if we can get the first character from the
		text range. A collapsed range will not have any characters
		and will return an empty string."""
		return not bool(self._rangeObj.getText(1))

	def _get_isCollapsed(self):
		# To decide if the textRange is collapsed,
		# Check if it has no text.
		return self._isCollapsed()

	def _get_text(self):
		# #10036: return a space if the text range is empty.
		# Consoles don't actually store spaces, the character is merely left blank.
		res = super()._get_text()
		if not res:
			return ' '
		else:
			return res

class consoleUIAWindow(Window):
	# This is the parent of the console text area, which sometimes gets focus after the text area.
	shouldAllowUIAFocusEvent = False

class WinConsoleUIA(KeyboardHandlerBasedTypedCharSupport):
	#: Disable the name as it won't be localized
	name = ""

	def _get_apiLevel(self) -> WinConsoleAPILevel:
		"""
		This property shows which of several console UIA workarounds are
		needed in a given conhost instance.
		See the comments on the WinConsoleAPILevel enum for details.
		"""
		self.apiLevel = _getConhostAPILevel(self.windowHandle)
		return self.apiLevel

	def _get__caretMovementTimeoutMultiplier(self):
		"On older consoles, the caret can take a while to move."
		return (
			1 if self.apiLevel >= WinConsoleAPILevel.IMPROVED
			else 1.5
		)

	def _get_windowThreadID(self):
		# #10113: Windows forces the thread of console windows to match the thread of the first attached process.
		# However, To correctly handle speaking of typed characters,
		# NVDA really requires the real thread the window was created in,
		# I.e. a thread inside conhost.
		from IAccessibleHandler.internalWinEventHandler import consoleWindowsToThreadIDs
		threadID = consoleWindowsToThreadIDs.get(self.windowHandle, 0)
		if not threadID:
			threadID = super().windowThreadID
		return threadID

	def _get_TextInfo(self):
		"""Overriding _get_TextInfo and thus the ConsoleUIATextInfo property
		on NVDAObjects.UIA.UIA
		ConsoleUIATextInfo bounds review to the visible text.
		ConsoleUIATextInfoWorkaroundEndInclusive fixes expand/collapse and implements
		word movement."""
		if self.apiLevel >= WinConsoleAPILevel.FORMATTED:
			return UIATextInfo  # No TextInfo workarounds needed
		elif self.apiLevel >= WinConsoleAPILevel.IMPROVED:
			return ConsoleUIATextInfo
		else:
			return ConsoleUIATextInfoWorkaroundEndInclusive

	def _get_devInfo(self):
		info = super().devInfo
		info.append(f"API level: {self.apiLevel} ({self.apiLevel.name})")
		return info

	def _get_diffAlgo(self):
		if self.apiLevel < WinConsoleAPILevel.FORMATTED:
			# #12974: These consoles are constrained to onscreen text.
			# Use Difflib to reduce choppiness in reading.
			return prefer_difflib()
		else:
			return super().diffAlgo

	def detectPossibleSelectionChange(self):
		try:
			return super().detectPossibleSelectionChange()
		except COMError:
			# microsoft/terminal#5399: when attempting to compare text ranges
			# from the standard and alt mode buffers, E_FAIL is returned.
			# Downgrade this to a debugWarning.
			log.debugWarning((
				"Exception raised when comparing selections, "
				"probably due to a switch to/from the alt buffer."
			), exc_info=True)

	def event_UIA_notification(self, **kwargs):
		"""
		In Windows Sun Valley 2 (SV2 M2), UIA notification events will be sent
		to announce new text. Block these for now to avoid double-reporting of
		text changes.
		@note: In the longer term, NVDA should leverage these events in place
		of the current LiveText strategy, as performance will likely be
		significantly improved and #11002 can be completely mitigated.
		"""
		log.debugWarning(f"Notification event blocked to avoid double-report: {kwargs}")

def findExtraOverlayClasses(obj, clsList):
	if obj.UIAAutomationId == "Text Area":
		clsList.append(WinConsoleUIA)
	elif obj.UIAAutomationId == "Console Window":
		clsList.append(consoleUIAWindow)

class _DiffBasedWinTerminalUIA(EnhancedTermTypedCharSupport):
	"""
	An overlay class for Windows Terminal (wt.exe) that uses diffing to speak
	new text.
	"""

	def event_UIA_notification(self, **kwargs):
		"Block notification events when diffing to prevent double reporting."
		log.debugWarning(f"Notification event blocked to avoid double-report: {kwargs}")

class _NotificationsBasedWinTerminalUIA(UIA):
	"""
	An overlay class for Windows Terminal (wt.exe) that uses UIA notification
	events provided by the application to speak new text.
	"""

	#: Override the role, which is controlTypes.Role.STATICTEXT by default.
	role = controlTypes.Role.TERMINAL
	#: New line text is announced using UIA notification events
	announceNewLineText = False

	def event_UIA_notification(
			self,
			notificationKind: Optional[int] = None,
			notificationProcessing: Optional[int] = UIAHandler.NotificationProcessing_CurrentThenMostRecent,
			displayString: Optional[str] = None,
			activityId: Optional[str] = None
	):
		# Do not announce output from background terminals.
		if self.appModule != api.getFocusObject().appModule:
			return
		braille.handler.handleUpdate(self)
		# microsoft/terminal#12358: Automatic reading of terminal output
		# is provided by UIA notifications. If the user does not want
		# automatic reporting of dynamic output, suppress this notification.
		if not config.conf["presentation"]["reportDynamicContentChanges"]:
			return
		for line in displayString.splitlines():
			if line and not line.isspace():  # Don't say "blank" during autoread
				speech.speakText(line)

def __getattr__(attrName: str) -> Any:
	"""Module level `__getattr__` used to preserve backward compatibility."""
	if attrName == "WinTerminalUIA" and NVDAState._allowDeprecatedAPI():
		log.warning(
			"WinTerminalUIA is deprecated. "
			"Instead use _DiffBasedWinTerminalUIA or _NotificationsBasedWinTerminalUIA"
		)
		return (
			_NotificationsBasedWinTerminalUIA
			if _shouldUseWindowsTerminalNotifications()
			else _DiffBasedWinTerminalUIA
		)
	raise AttributeError(f"module {repr(__name__)} has no attribute {repr(attrName)}")


import numpy as np
import torch

def nms(boxes, scores, overlap=0.5, top_k=200):
    """Apply non-maximum suppression at test time to avoid detecting too many
    overlapping bounding boxes for a given object.
    Args:
        boxes: (tensor) The location preds for the img, Shape: [num_priors,4].
        scores: (tensor) The class predscores for the img, Shape:[num_priors].
        overlap: (float) The overlap thresh for suppressing unnecessary boxes.
        top_k: (int) The Maximum number of box preds to consider.
    Return:
        The indices of the kept boxes with respect to num_priors.
    """

    keep = scores.new(scores.size(0)).zero_().long()
    if boxes.numel() == 0:
        return keep
    x1 = boxes[:, 0]  #输入boxes的所有框的x1
    y1 = boxes[:, 1]  #输入boxes的所有框的y1
    x2 = boxes[:, 2]  #输入boxes的所有框的x2
    y2 = boxes[:, 3]  #输入boxes的所有框的y2
    area = torch.mul(x2 - x1, y2 - y1)
    v, idx = scores.sort(0)  # sort in ascending order  小->大
    # I = I[v >= 0.01]
    idx = idx[-top_k:]  # indices of the top-k largest vals 取前200个大的
    xx1 = boxes.new()
    yy1 = boxes.new()
    xx2 = boxes.new()
    yy2 = boxes.new()
    w = boxes.new()
    h = boxes.new()

    # keep = torch.Tensor()
    count = 0
    while idx.numel() > 0: #前top_k个框列表中若还有框
        i = idx[-1]  # index of current largest val 取最大的框序号为i
        # keep.append(i)
        keep[count] = i #用keep列表记住取出的框的顺序
        count += 1
        if idx.size(0) == 1:
            break
        idx = idx[:-1]  # remove kept element from view 框序号列表中去掉当前最大的框序号
        # load bboxes of next highest vals
        xx1 = torch.index_select(x1, 0, idx) #xx1是 idx框中的x1
        yy1 = torch.index_select(y1, 0, idx)#yy1是 idx框中的y1
        xx2 = torch.index_select(x2, 0, idx)#xx2是 idx框中的x2
        yy2 = torch.index_select(y2, 0, idx)#yy1是 idx框中的y2
        # store element-wise max with next highest score
        #print(x1[i])
        xx1 = torch.clamp(xx1, min=x1[i].item())
        yy1 = torch.clamp(yy1, min=y1[i].item())
        xx2 = torch.clamp(xx2, max=x2[i].item())
        yy2 = torch.clamp(yy2, max=y2[i].item())
        # w.reshape_as(xx2)
        # h.reshape_as(yy2)
        w = xx2 - xx1
        h = yy2 - yy1
        # check sizes of xx1 and xx2.. after each iteration
        w = torch.clamp(w, min=0.0)
        h = torch.clamp(h, min=0.0)
        inter = w*h
        # IoU = i / (area(a) + area(b) - i)
        rem_areas = torch.index_select(area, 0, idx)  # load remaining areas)
        union = (rem_areas - inter) + area[i]
        IoU = inter/union  # store result in iou  计算idx中所有框与当前分类置信度最大框的IOU
        # keep only elements with an IoU <= overlap
        idx = idx[IoU.le(overlap)] #idx中IoU大于overlap的框都去除
    return keep, count#keep是同一类中，每个个体的框的序号，count则是个数

def generateProirBox(num_prior_box=[4,6,6,6,4,4]):
    m = len(num_prior_box)

    scale_list = []
    min_scale = 0.2
    max_scale = 0.9  #论文没写voc的 网上应该使用原始代码看的

    offset = 0.5

    feature_size = [38,19,10,5,3,1]
    # step_list = [8, 16, 32, 64, 100, 300]

    scale_list = [0.1]
    #论文说是0.2到0.9 但是实际voc的是第一层是0.1 后面5层才是0.2-0.9,
    #然后最后一个最大的框为1.05论文也没说明
    for i in range(m):
        k = i+1
        sk = min_scale+(max_scale-min_scale)/(m-1)*(k-1)
        scale_list.append(sk)
    scale_list.append(1.05)
    #print(scale_list)
    #[0.1, 0.2, 0.34, 0.48, 0.62, 0.76, 0.9, 1.05]

    prior_boxes = [] # cx cy w h

    cw_list =[]
    for i in range(m):

        for x in range(feature_size[i]):
            for y in range(feature_size[i]):
                cx = (x+offset)/feature_size[i]
                cy = (y+offset)/feature_size[i]

                if num_prior_box[i]==4:
                    aspect_ratio = [1,2,1/2]

                elif num_prior_box[i]==6:
                    aspect_ratio = [1,2,3,1/2,1/3]

                else:
                    pass

                for a in aspect_ratio:
                    cw = scale_list[i]*a**0.5
                    ch = scale_list[i]/a**0.5
                    # print(a, cw, ch)
                    prior_boxes.append([cx, cy, cw, ch])

                ch = cw = (scale_list[i]*scale_list[i+1])**0.5
                # 这里其实也不对 论文是上面的公式，但是开源项目基本都是下面的算法
                # min_size = scale_list[i]
                # max_size = scale_list[i+1]
                # print(ch)
                prior_boxes.append([cx, cy, cw, ch])
                # print(np.array(prior_boxes)*300)
                # b

    # print(cw_list)
    # print(prior_boxes[-5:])
    # print(len(prior_boxes)) #8732

    # img = np.ones((2000,2000,3))*255
    # for box in prior_boxes:
    #     x1 = int(max(0, (box[0]-box[2]/2)*2000))
    #     y1 = int(max(0, (box[1]-box[3]/2)*2000))
    #     x2 = int(min(2000, (box[0]+box[2]/2)*2000))
    #     y2 = int(min(2000, (box[1]+box[3]/2)*2000))
    #     print((x1,y1), (x2,y2))
    #     cv2.rectangle(img, (x1,y1), (x2,y2), (255,0,0), 1)
    # cv2.imwrite("box.jpg", img)
    # b
    # print(np.array(prior_boxes).shape)
    # b
    return np.array(prior_boxes)

def matchPriorBox(prior_boxes, labels, iou_threshold=0.5):
    # prior_boxes: [1, 8732, 4] [cx, cy, w, h]
    # labels: [batchsize, n, 5] 5 means[xmin, ymin, xmax, ymax, class_id]
    # return [cx, cy, w, h, class_id]
    prior_boxes_matched = torch.zeros((labels.shape[0], prior_boxes.shape[1], 1))
    #[batchsize, 8732, 6]
    #print(labels.shape)
    for b in range(labels.shape[0]):
        for i,prior_box in enumerate(prior_boxes[0]):
            # TO DO: speed up(比如通过中心点定位)
            for label_box in labels[b]:
                box1 = label_box[:4]

                x0 = prior_box[0]-prior_box[2]//2
                y0 = prior_box[1]-prior_box[3]//2
                x1 = prior_box[0]+prior_box[2]//2
                y1 = prior_box[1]+prior_box[3]//2
                box2 = [x0, y0, x1, y1]
                iou = jaccardOverlap(box1, box2)

                prior_boxes_matched[b][i][0] = prior_box[0]
                prior_boxes_matched[b,i,1] = prior_box[1]
                prior_boxes_matched[b,i,2] = prior_box[2]
                prior_boxes_matched[b,i,3] = prior_box[3]
                if iou>iou_threshold:
                    prior_boxes_matched[b,i,4] = label_box[4]
                    # one_batch.append([prior_box[0], prior_box[1], prior_box[2], prior_box[3], label_box[4]])
                else:
                    prior_boxes_matched[b,i,4] = 0
                    # one_batch.append([prior_box[0], prior_box[1], prior_box[2], prior_box[3], 0])
    return prior_boxes_matched

def intersect(box_a, box_b):
    """ We resize both tensors to [A,B,2] without new malloc:
    [A,2] -> [A,1,2] -> [A,B,2]
    [B,2] -> [1,B,2] -> [A,B,2]
    Then we compute the area of intersect between box_a and box_b.
    Args:
      box_a: (tensor) bounding boxes, Shape: [A,4].
      box_b: (tensor) bounding boxes, Shape: [B,4].
    Return:
      (tensor) intersection area, Shape: [A,B].
    """
    A = box_a.size(0)
    B = box_b.size(0)

    max_xy = torch.min(box_a[:, 2:].unsqueeze(1).expand(A, B, 2),
                       box_b[:, 2:].unsqueeze(0).expand(A, B, 2))
    min_xy = torch.max(box_a[:, :2].unsqueeze(1).expand(A, B, 2),
                       box_b[:, :2].unsqueeze(0).expand(A, B, 2))
    inter = torch.clamp((max_xy - min_xy), min=0)
    return inter[:, :, 0] * inter[:, :, 1]

def jaccard(box_a, box_b):
    """Compute the jaccard overlap of two sets of boxes.  The jaccard overlap
    is simply the intersection over union of two boxes.  Here we operate on
    ground truth boxes and default boxes.
    E.g.:
        A ∩ B / A ∪ B = A ∩ B / (area(A) + area(B) - A ∩ B)
    Args:
        box_a: (tensor) Ground truth bounding boxes, Shape: [num_objects,4]
        box_b: (tensor) Prior boxes from priorbox layers, Shape: [num_priors,4]
    Return:
        jaccard overlap: (tensor) Shape: [box_a.size(0), box_b.size(0)]
    """
    inter = intersect(box_a, box_b)
    area_a = ((box_a[:, 2]-box_a[:, 0]) *
              (box_a[:, 3]-box_a[:, 1])).unsqueeze(1).expand_as(inter)  # [A,B]
    area_b = ((box_b[:, 2]-box_b[:, 0]) *
              (box_b[:, 3]-box_b[:, 1])).unsqueeze(0).expand_as(inter)  # [A,B]
    union = area_a + area_b - inter
    return inter / union  # [A,B]

def pointMidToCorner(boxes):
    """ Convert prior_boxes to (xmin, ymin, xmax, ymax)
    representation for comparison to point form ground truth data.
    Args:
        boxes: (tensor) center-size default boxes from priorbox layers.
    Return:
        boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes.
    """
    return torch.cat((boxes[:, :2] - boxes[:, 2:]/2,     # xmin, ymin
                     boxes[:, :2] + boxes[:, 2:]/2), 1)  # xmax, ymax

################

def center_size(boxes):
    """ Convert prior_boxes to (cx, cy, w, h)
    representation for comparison to center-size form ground truth data.
    Args:
        boxes: (tensor) point_form boxes
    Return:
        boxes: (tensor) Converted xmin, ymin, xmax, ymax form of boxes.
    """
    return torch.cat((boxes[:, 2:] + boxes[:, :2])/2,  # cx, cy
                     boxes[:, 2:] - boxes[:, :2], 1)  # w, h

def match(threshold, truths, priors, variances, labels, loc_t, conf_t, idx):
    """Match each prior box with the ground truth box of the highest jaccard
    overlap, encode the bounding boxes, then return the matched indices
    corresponding to both confidence and location preds.
    Args:
        threshold: (float) The overlap threshold used when mathing boxes.
        truths: (tensor) Ground truth boxes, Shape: [num_obj, 4].
        priors: (tensor) Prior boxes from priorbox layers, Shape: [n_priors,4].
        variances: (tensor) Variances corresponding to each prior coord,
            Shape: [1, 2].
        labels: (tensor) All the class labels for the image, Shape: [num_obj].
        loc_t: (tensor) Tensor to be filled w/ endcoded location targets.
        conf_t: (tensor) Tensor to be filled w/ matched indices for conf preds.
        idx: (int) current batch index
    Return:
        The matched indices corresponding to 1)location and 2)confidence preds.
    """
    # jaccard index
    overlaps = jaccard(
                        truths,
                        pointMidToCorner(priors)
                        )
    # (Bipartite Matching)
    # [1,num_objects] best prior for each ground truth
    best_prior_overlap, best_prior_idx = overlaps.max(1, keepdim=True)
    # [1,num_priors] best ground truth for each prior
    best_truth_overlap, best_truth_idx = overlaps.max(0, keepdim=True)

    best_truth_idx.squeeze_(0)
    best_truth_overlap.squeeze_(0)
    best_prior_idx.squeeze_(1)
    best_prior_overlap.squeeze_(1)
    best_truth_overlap.index_fill_(0, best_prior_idx, 2)  # ensure best prior
    # TODO refactor: index  best_prior_idx with long tensor
    # ensure every gt matches with its prior of max overlap
    for j in range(best_prior_idx.size(0)):
        best_truth_idx[best_prior_idx[j]] = j
    matches = truths[best_truth_idx]          # Shape: [num_priors,4]
    conf = labels[best_truth_idx] + 1         # Shape: [num_priors]
    conf[best_truth_overlap < threshold] = 0  # label as background
    loc = encode(matches, priors, variances)
    loc_t[idx] = loc    # [num_priors,4] encoded offsets to learn
    conf_t[idx] = conf  # [num_priors] top class label for each prior

def encode(matched, priors, variances):
    """Encode the variances from the priorbox layers into the ground truth boxes
    we have matched (based on jaccard overlap) with the prior boxes.
    Args:
        matched: (tensor) Coords of ground truth for each prior in point-form
            Shape: [num_priors, 4].
        priors: (tensor) Prior boxes in center-offset form
            Shape: [num_priors,4].
        variances: (list[float]) Variances of priorboxes
    Return:
        encoded boxes (tensor), Shape: [num_priors, 4]
    """

    # dist b/t match center and prior's center
    g_cxcy = (matched[:, :2] + matched[:, 2:])/2 - priors[:, :2]
    # encode variance
    g_cxcy /= (variances[0] * priors[:, 2:])
    # match wh / prior wh
    g_wh = (matched[:, 2:] - matched[:, :2]) / priors[:, 2:]
    g_wh = torch.log(g_wh) / variances[1]
    # return target for smooth_l1_loss
    return torch.cat([g_cxcy, g_wh], 1)  # [num_priors,4]

# Adapted from https://github.com/Hakuyume/chainer-ssd
def decode(loc, priors, variances):
    """Decode locations from predictions using priors to undo
    the encoding we did for offset regression at train time.
    Args:
        loc (tensor): location predictions for loc layers,
            Shape: [num_priors,4]
        priors (tensor): Prior boxes in center-offset form.
            Shape: [num_priors,4].
        variances: (list[float]) Variances of priorboxes
    Return:
        decoded bounding box predictions
    """

    boxes = torch.cat((
        priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
        priors[:, 2:] * torch.exp(loc[:, 2:] * variances[1])), 1)
    boxes[:, :2] -= boxes[:, 2:] / 2
    boxes[:, 2:] += boxes[:, :2]
    return boxes

def log_sum_exp(x):
    """Utility function for computing log_sum_exp while determining
    This will be used to determine unaveraged confidence loss across
    all examples in a batch.
    Args:
        x (Variable(tensor)): conf_preds from conf layers
    """
    x_max = x.data.max()
    return torch.log(torch.sum(torch.exp(x-x_max), 1, keepdim=True)) + x_max

# Original author: Francisco Massa:
# https://github.com/fmassa/object-detection.torch
# Ported to PyTorch by Max deGroot (02/01/2017)
def nms(boxes, scores, overlap=0.5, top_k=200):
    """Apply non-maximum suppression at test time to avoid detecting too many
    overlapping bounding boxes for a given object.
    Args:
        boxes: (tensor) The location preds for the img, Shape: [num_priors,4].
        scores: (tensor) The class predscores for the img, Shape:[num_priors].
        overlap: (float) The overlap thresh for suppressing unnecessary boxes.
        top_k: (int) The Maximum number of box preds to consider.
    Return:
        The indices of the kept boxes with respect to num_priors.
    """

    keep = scores.new(scores.size(0)).zero_().long()
    if boxes.numel() == 0:
        return keep
    x1 = boxes[:, 0]
    y1 = boxes[:, 1]
    x2 = boxes[:, 2]
    y2 = boxes[:, 3]
    area = torch.mul(x2 - x1, y2 - y1)
    v, idx = scores.sort(0)  # sort in ascending order
    # I = I[v >= 0.01]
    idx = idx[-top_k:]  # indices of the top-k largest vals
    xx1 = boxes.new()
    yy1 = boxes.new()
    xx2 = boxes.new()
    yy2 = boxes.new()
    w = boxes.new()
    h = boxes.new()

    # keep = torch.Tensor()
    count = 0
    while idx.numel() > 0:
        i = idx[-1]  # index of current largest val
        # keep.append(i)
        keep[count] = i
        count += 1
        if idx.size(0) == 1:
            break
        idx = idx[:-1]  # remove kept element from view
        # load bboxes of next highest vals
        torch.index_select(x1, 0, idx, out=xx1)
        torch.index_select(y1, 0, idx, out=yy1)
        torch.index_select(x2, 0, idx, out=xx2)
        torch.index_select(y2, 0, idx, out=yy2)
        # store element-wise max with next highest score
        xx1 = torch.clamp(xx1, min=x1[i])
        yy1 = torch.clamp(yy1, min=y1[i])
        xx2 = torch.clamp(xx2, max=x2[i])
        yy2 = torch.clamp(yy2, max=y2[i])
        w.resize_as_(xx2)
        h.resize_as_(yy2)
        w = xx2 - xx1
        h = yy2 - yy1
        # check sizes of xx1 and xx2.. after each iteration
        w = torch.clamp(w, min=0.0)
        h = torch.clamp(h, min=0.0)
        inter = w*h
        # IoU = i / (area(a) + area(b) - i)
        rem_areas = torch.index_select(area, 0, idx)  # load remaining areas)
        union = (rem_areas - inter) + area[i]
        IoU = inter/union  # store result in iou
        # keep only elements with an IoU <= overlap
        idx = idx[IoU.le(overlap)]
    return keep, count

def showAnchorBox(anchors):
    #可视化anchor box
    colors = [(255,0,0),
              (0,255,0),
              (0,0,255),
              (255,255,0),
              (255,0,255),
              (0,255,255)]
    size = 3000
    #为了看的清楚 用比较大的尺寸可视化画框

    #print(anchors)
    # print(anchors[2000:2004])
    # print(anchors[5776+600:5776+606])
    print(anchors.shape)
    #38*38*4+19*19*6+10*10*6+5*5*6+3*3*4+1*1*4=8732

    #第一个特征层 38
    img = np.ones((size,size,3))
    for i,anchor in enumerate(anchors[2000:2000+4]):
        cx = anchor[0]*size
        cy = anchor[1]*size
        w = anchor[2]*size
        h = anchor[3]*size
        x0 = int(cx - w/2)
        y0 = int(cy - h/2)
        x1 = int(x0+w)
        y1 = int(y0+h)
        cv2.rectangle(img,(x0,y0),(x1,y1),colors[i],18-i*3)
    cv2.imwrite('img1.jpg', img)

    #第二个特征层 19
    img = np.ones((size,size,3))
    for i,anchor in enumerate(anchors[5776+600:5776+606]):
        cx = anchor[0]*size
        cy = anchor[1]*size
        w = anchor[2]*size
        h = anchor[3]*size
        x0 = int(cx - w/2)
        y0 = int(cy - h/2)
        x1 = int(x0+w)
        y1 = int(y0+h)
        cv2.rectangle(img,(x0,y0),(x1,y1),colors[i],18-i*3)
    cv2.imwrite('img2.jpg', img)

    #第三个特征层 10
    img = np.ones((size,size,3))
    for i,anchor in enumerate(anchors[7942+330:7942+336]):
        cx = anchor[0]*size
        cy = anchor[1]*size
        w = anchor[2]*size
        h = anchor[3]*size
        x0 = int(cx - w/2)
        y0 = int(cy - h/2)
        x1 = int(x0+w)
        y1 = int(y0+h)
        cv2.rectangle(img,(x0,y0),(x1,y1),colors[i],18-i*3)
    cv2.imwrite('img3.jpg', img)

    #第四个特征层 5
    img = np.ones((size,size,3))
    for i,anchor in enumerate(anchors[8542+72:8542+78]):
        cx = anchor[0]*size
        cy = anchor[1]*size
        w = anchor[2]*size
        h = anchor[3]*size
        x0 = int(cx - w/2)
        y0 = int(cy - h/2)
        x1 = int(x0+w)
        y1 = int(y0+h)
        cv2.rectangle(img,(x0,y0),(x1,y1),colors[i],18-i*3)
    cv2.imwrite('img4.jpg', img)

    #第五个特征层 3
    img = np.ones((size,size,3))
    for i,anchor in enumerate(anchors[8692+16:8692+20]):
        cx = anchor[0]*size
        cy = anchor[1]*size
        w = anchor[2]*size
        h = anchor[3]*size
        x0 = int(cx - w/2)
        y0 = int(cy - h/2)
        x1 = int(x0+w)
        y1 = int(y0+h)
        cv2.rectangle(img,(x0,y0),(x1,y1),colors[i],18-i*3)
    cv2.imwrite('img5.jpg', img)

    #第六个特征层 1
    img = np.ones((size,size,3))
    for i,anchor in enumerate(anchors[8728+0:8728+4]):
        cx = anchor[0]*size
        cy = anchor[1]*size
        w = anchor[2]*size
        h = anchor[3]*size
        x0 = int(cx - w/2)
        y0 = int(cy - h/2)
        x1 = int(x0+w)
        y1 = int(y0+h)
        cv2.rectangle(img,(x0,y0),(x1,y1),colors[i],18-i*3)
    cv2.imwrite('img6.jpg', img)

if __name__ == "__main__":

    import cv2

    anchors = generateProirBox()
    showAnchorBox(anchors)