import numpy as np

__all__ = ['find_span',
           'basis_funs',
           'basis_funs_1st_der',
           'basis_funs_all_ders',
           'collocation_matrix',
           'histopolation_matrix',
           'breakpoints',
           'greville',
           'elements_spans',
           'make_knots',
           'elevate_knots',
           'quadrature_grid',
           'basis_integrals',
           'basis_ders_on_quad_grid',
           'scaling_matrix',
           'hrefinement_matrix']

#==============================================================================
def find_span( knots, degree, x ):
    """
    Determine the knot span index at location x, given the B-Splines' knot
    sequence and polynomial degree. See Algorithm A2.1 in [1].

    For a degree p, the knot span index i identifies the indices [i-p:i] of all
    p+1 non-zero basis functions at a given location x.

    Parameters
    ----------
    knots : array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    x : float
        Location of interest.

    Returns
    -------
    span : int
        Knot span index.

    """
    # Knot index at left/right boundary
    low  = degree
    high = len(knots)-1-degree

    # Check if point is exactly on left/right boundary, or outside domain
    if x <= knots[low ]: return low
    if x >= knots[high]: return high-1

    # Perform binary search
    span = (low+high)//2
    while x < knots[span] or x >= knots[span+1]:
        if x < knots[span]:
           high = span
        else:
           low  = span
        span = (low+high)//2

    return span

#==============================================================================
def basis_funs( knots, degree, x, span ):
    """
    Compute the non-vanishing B-splines at location x, given the knot sequence,
    polynomial degree and knot span. See Algorithm A2.2 in [1].

    Parameters
    ----------
    knots : array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    x : float
        Evaluation point.

    span : int
        Knot span index.

    Results
    -------
    values : numpy.ndarray
        Values of p+1 non-vanishing B-Splines at location x.

    Notes
    -----
    The original Algorithm A2.2 in The NURBS Book [1] is here slightly improved
    by using 'left' and 'right' temporary arrays that are one element shorter.

    """
    left   = np.empty( degree  , dtype=float )
    right  = np.empty( degree  , dtype=float )
    values = np.empty( degree+1, dtype=float )

    values[0] = 1.0
    for j in range(0,degree):
        left [j] = x - knots[span-j]
        right[j] = knots[span+1+j] - x
        saved    = 0.0
        for r in range(0,j+1):
            temp      = values[r] / (right[r] + left[j-r])
            values[r] = saved + right[r] * temp
            saved     = left[j-r] * temp
        values[j+1] = saved

    return values

#==============================================================================
def basis_funs_1st_der( knots, degree, x, span ):
    """
    Compute the first derivative of the non-vanishing B-splines at location x,
    given the knot sequence, polynomial degree and knot span.

    See function 's_bsplines_non_uniform__eval_deriv' in Selalib's source file
    'src/splines/sll_m_bsplines_non_uniform.F90'.

    Parameters
    ----------
    knots : array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    x : float
        Evaluation point.

    span : int
        Knot span index.

    Results
    -------
    ders : numpy.ndarray
        Derivatives of p+1 non-vanishing B-Splines at location x.

    """
    # Compute nonzero basis functions and knot differences for splines
    # up to degree deg-1
    values = basis_funs( knots, degree-1, x, span )

    # Compute derivatives at x using formula based on difference of splines of
    # degree deg-1
    # -------
    # j = 0
    ders  = np.empty( degree+1, dtype=float )
    saved = degree * values[0] / (knots[span+1]-knots[span+1-degree])
    ders[0] = -saved
    # j = 1,...,degree-1
    for j in range(1,degree):
        temp    = saved
        saved   = degree * values[j] / (knots[span+j+1]-knots[span+j+1-degree])
        ders[j] = temp - saved
    # j = degree
    ders[degree] = saved

    return ders

#==============================================================================
def basis_funs_all_ders( knots, degree, x, span, n ):
    """
    Evaluate value and n derivatives at x of all basis functions with
    support in interval [x_{span-1}, x_{span}].

    ders[i,j] = (d/dx)^i B_k(x) with k=(span-degree+j),
                for 0 <= i <= n and 0 <= j <= degree+1.

    Parameters
    ----------
    knots : array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    x : float
        Evaluation point.

    span : int
        Knot span index.

    n : int
        Max derivative of interest.

    Results
    -------
    ders : numpy.ndarray (n+1,degree+1)
        2D array of n+1 (from 0-th to n-th) derivatives at x of all (degree+1)
        non-vanishing basis functions in given span.

    Notes
    -----
    The original Algorithm A2.3 in The NURBS Book [1] is here improved:
        - 'left' and 'right' arrays are 1 element shorter;
        - inverse of knot differences are saved to avoid unnecessary divisions;
        - innermost loops are replaced with vector operations on slices.

    """
    left  = np.empty( degree )
    right = np.empty( degree )
    ndu   = np.empty( (degree+1, degree+1) )
    a     = np.empty( (       2, degree+1) )
    ders  = np.zeros( (     n+1, degree+1) ) # output array

    # Number of derivatives that need to be effectively computed
    # Derivatives higher than degree are = 0.
    ne = min( n, degree )

    # Compute nonzero basis functions and knot differences for splines
    # up to degree, which are needed to compute derivatives.
    # Store values in 2D temporary array 'ndu' (square matrix).
    ndu[0,0] = 1.0
    for j in range(0,degree):
        left [j] = x - knots[span-j]
        right[j] = knots[span+1+j] - x
        saved    = 0.0
        for r in range(0,j+1):
            # compute inverse of knot differences and save them into lower triangular part of ndu
            ndu[j+1,r] = 1.0 / (right[r] + left[j-r])
            # compute basis functions and save them into upper triangular part of ndu
            temp       = ndu[r,j] * ndu[j+1,r]
            ndu[r,j+1] = saved + right[r] * temp
            saved      = left[j-r] * temp
        ndu[j+1,j+1] = saved

    # Compute derivatives in 2D output array 'ders'
    ders[0,:] = ndu[:,degree]
    for r in range(0,degree+1):
        s1 = 0
        s2 = 1
        a[0,0] = 1.0
        for k in range(1,ne+1):
            d  = 0.0
            rk = r-k
            pk = degree-k
            if r >= k:
               a[s2,0] = a[s1,0] * ndu[pk+1,rk]
               d = a[s2,0] * ndu[rk,pk]
            j1 = 1   if (rk  > -1 ) else -rk
            j2 = k-1 if (r-1 <= pk) else degree-r
            a[s2,j1:j2+1] = (a[s1,j1:j2+1] - a[s1,j1-1:j2]) * ndu[pk+1,rk+j1:rk+j2+1]
            d += np.dot( a[s2,j1:j2+1], ndu[rk+j1:rk+j2+1,pk] )
            if r <= pk:
               a[s2,k] = - a[s1,k-1] * ndu[pk+1,r]
               d += a[s2,k] * ndu[r,pk]
            ders[k,r] = d
            j  = s1
            s1 = s2
            s2 = j

    # Multiply derivatives by correct factors
    r = degree
    for k in range(1,ne+1):
        ders[k,:] = ders[k,:] * r
        r = r * (degree-k)

    return ders

#==============================================================================
def collocation_matrix(knots, degree, periodic, normalization, xgrid):
    """
    Compute the collocation matrix $C_ij = B_j(x_i)$, which contains the
    values of each B-spline basis function $B_j$ at all locations $x_i$.

    If called with normalization='M', this uses M-splines instead of B-splines.

    Parameters
    ----------
    knots : 1D array_like
        Knots sequence.

    degree : int
        Polynomial degree of spline space.

    periodic : bool
        True if domain is periodic, False otherwise.

    normalization : str
        Set to 'B' for B-splines, and 'M' for M-splines.

    xgrid : 1D array_like
        Evaluation points.

    Returns
    -------
    mat : 2D numpy.ndarray
        Collocation matrix: values of all basis functions on each point in xgrid.

    """
    # Number of basis functions (in periodic case remove degree repeated elements)
    nb = len(knots)-degree-1
    if periodic:
        nb -= degree

    # Number of evaluation points
    nx = len(xgrid)

    # Collocation matrix as 2D Numpy array (dense storage)
    mat = np.zeros( (nx,nb) )

    # Indexing of basis functions (periodic or not) for a given span
    if periodic:
        js = lambda span: [(span-degree+s) % nb for s in range( degree+1 )]
    else:
        js = lambda span: slice( span-degree, span+1 )

    # Rescaling of B-splines, to get M-splines if needed
    if normalization == 'B':
        normalize = lambda basis, span: basis
    elif normalization == 'M':
        scaling = 1 / basis_integrals(knots, degree)
        normalize = lambda basis, span: basis * scaling[span-degree: span+1]

    # Fill in non-zero matrix values
    for i,x in enumerate( xgrid ):
        span  =  find_span( knots, degree, x )
        basis = basis_funs( knots, degree, x, span )
        mat[i,js(span)] = normalize(basis, span)

    # Mitigate round-off errors
    mat[abs(mat) < 1e-14] = 0.0

    return mat

#==============================================================================
def histopolation_matrix(knots, degree, periodic, normalization, xgrid):
    r"""
    Compute the histopolation matrix $H_{ij} = \int_{x_i}^{x_{i+1}} B_j(x) dx$,
    which contains the integrals of each B-spline basis function $B_j$ between
    two successive grid points.

    If called with normalization='M', this uses M-splines instead of B-splines.

    Parameters
    ----------
    knots : 1D array_like
        Knots sequence.

    degree : int
        Polynomial degree of spline space.

    periodic : bool
        True if domain is periodic, False otherwise.

    normalization : str
        Set to 'B' for B-splines, and 'M' for M-splines.

    xgrid : 1D array_like
        Grid points.

    """
    # Check that knots are ordered (but allow repeated knots)
    if not np.all(np.diff(knots) >= 0):
        raise ValueError("Cannot accept knot sequence: {}".format(knots))

    # Check that spline degree is non-negative integer
    if not isinstance(degree, (int, np.integer)):
        raise TypeError("Degree {} must be integer, got type {} instead".format(degree, type(degree)))
    if degree < 0:
        raise ValueError("Cannot accept negative degree: {}".format(degree))

    # Check 'periodic' flag
    if not isinstance(periodic, bool):
        raise TypeError("Cannot accept non-boolean 'periodic' parameter: {}".format(periodic))

    # Check 'normalization' option
    if normalization not in ['B', 'M']:
        raise ValueError("Cannot accept 'normalization' parameter: {}".format(normalization))

    # Check that grid points are ordered, and do not allow repetitions
    if not np.all(np.diff(xgrid) > 0):
        raise ValueError("Grid points must be ordered, with no repetitions: {}".format(xgrid))

    # Number of basis functions (in periodic case remove degree repeated elements)
    nb = len(knots)-degree-1
    if periodic:
        nb -= degree

    # Number of evaluation points
    nx = len(xgrid)

    # In periodic case, make sure that evaluation points include domain boundaries
    # TODO: only do this if the user asks for it!
    if periodic:
        xmin  = knots[degree]
        xmax  = knots[-1-degree]
        if xgrid[0] > xmin:
            xgrid = [xmin, *xgrid]
        if xgrid[-1] < xmax:
            xgrid = [*xgrid, xmax]

    # B-splines of degree p+1: basis[i,j] := Bj(xi)
    #
    # NOTES:
    #  . cannot use M-splines in analytical formula for histopolation matrix
    #  . always use non-periodic splines to avoid circulant matrix structure
    C = collocation_matrix(
        knots    = elevate_knots(knots, degree, periodic),
        degree   = degree + 1,
        periodic = False,
        normalization = 'B',
        xgrid = xgrid
    )

    # Rescaling of M-splines, to get B-splines if needed
    if normalization == 'M':
        normalize = lambda bi, j: bi
    elif normalization == 'B':
        scaling = basis_integrals(knots, degree)
        normalize = lambda bi, j: bi * scaling[j]

    # Compute span for each row (index of last non-zero basis function)
    # TODO: would be better to have this ready beforehand
    # TODO: use tolerance instead of comparing against zero
    spans = [(row != 0).argmax() + (degree+1) for row in C]

    # Compute histopolation matrix from collocation matrix of higher degree
    m = C.shape[0] - 1
    n = C.shape[1] - 1
    H = np.zeros((m, n))
    for i in range(m):
        # Indices of first/last non-zero elements in row of collocation matrix
        jstart = spans[i] - (degree+1)
        jend   = min(spans[i+1], n)
        # Compute non-zero values of histopolation matrix
        for j in range(1+jstart, jend+1):
            s = C[i, 0:j].sum() - C[i+1, 0:j].sum()
            H[i, j-1] = normalize(s, j-1)

    # Mitigate round-off errors
    H[abs(H) < 1e-14] = 0.0

    # Non periodic case: stop here
    if not periodic:
        return H

    # Periodic case: wrap around histopolation matrix
    #  1. identify repeated basis functions (sum columns)
    #  2. identify split interval (sum rows)
    Hp = np.zeros((nx, nb))
    for i in range(m):
        for j in range(n):
            Hp[i%nx, j%nb] += H[i, j]

    return Hp

#==============================================================================
def breakpoints( knots, degree ):
    """
    Determine breakpoints' coordinates.

    Parameters
    ----------
    knots : 1D array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    Returns
    -------
    breaks : numpy.ndarray (1D)
        Abscissas of all breakpoints.

    """
    return np.unique( knots[degree:-degree] )

#==============================================================================
def greville( knots, degree, periodic ):
    """
    Compute coordinates of all Greville points.

    Parameters
    ----------
    knots : 1D array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    periodic : bool
        True if domain is periodic, False otherwise.

    Returns
    -------
    xg : numpy.ndarray (1D)
        Abscissas of all Greville points.

    """
    T = knots
    p = degree
    n = len(T)-2*p-1 if periodic else len(T)-p-1

    # Compute greville abscissas as average of p consecutive knot values
    xg = np.array([sum(T[i:i+p])/p for i in range(1,1+n)])

    # Domain boundaries
    a = T[p]
    b = T[-1-p]

    # If needed apply periodic boundary conditions, then sort array
    if periodic:
        xg = (xg-a) % (b-a) + a
        xg = xg[np.argsort(xg)]

    # Make sure roundoff errors don't push Greville points outside domain
    xg[ 0] = max(xg[ 0], a)
    xg[-1] = min(xg[-1], b)

    return xg

#===============================================================================
def elements_spans( knots, degree ):
    """
    Compute the index of the last non-vanishing spline on each grid element
    (cell). The length of the returned array is the number of cells.

    Parameters
    ----------
    knots : 1D array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    Returns
    -------
    spans : numpy.ndarray (1D)
        Index of last non-vanishing spline on each grid element.

    Examples
    --------
    >>> import numpy as np
    >>> from psydac.core.bsplines import make_knots, elements_spans

    >>> p = 3 ; n = 8
    >>> grid  = np.arange( n-p+1 )
    >>> knots = make_knots( breaks=grid, degree=p, periodic=False )
    >>> spans = elements_spans( knots=knots, degree=p )
    >>> spans
    array([3, 4, 5, 6, 7])

    Notes
    -----
    1) Numbering of basis functions starts from 0, not 1;
    2) This function could be written in two lines:

       breaks = breakpoints( knots, degree )
       spans  = np.searchsorted( knots, breaks[:-1], side='right' ) - 1

    """
    breaks = breakpoints( knots, degree )
    nk     = len(knots)
    ne     = len(breaks)-1
    spans  = np.zeros( ne, dtype=int )

    ie = 0
    for ik in range( degree, nk-degree ):
        if knots[ik] != knots[ik+1]:
            spans[ie] = ik
            ie += 1
        if ie == ne:
            break

    return spans

#===============================================================================
def make_knots( breaks, degree, periodic ):
    """
    Create spline knots from breakpoints, with appropriate boundary conditions.
    Let p be spline degree. If domain is periodic, knot sequence is extended
    by periodicity so that first p basis functions are identical to last p.
    Otherwise, knot sequence is clamped (i.e. endpoints are repeated p times).

    Parameters
    ----------
    breaks : array_like
        Coordinates of breakpoints (= cell edges); given in increasing order and
        with no duplicates.

    degree : int
        Spline degree (= polynomial degree within each interval).

    periodic : bool
        True if domain is periodic, False otherwise.

    Result
    ------
    T : numpy.ndarray (1D)
        Coordinates of spline knots.

    """
    # Type checking
    assert isinstance( degree  , int  )
    assert isinstance( periodic, bool )

    # Consistency checks
    assert len(breaks) > 1
    assert all( np.diff(breaks) > 0 )
    assert degree > 0
    if periodic:
        assert len(breaks) > degree

    p = degree
    T = np.zeros( len(breaks)+2*p )
    T[p:-p] = breaks

    if periodic:
        period = breaks[-1]-breaks[0]
        T[0:p] = [xi-period for xi in breaks[-p-1:-1 ]]
        T[-p:] = [xi+period for xi in breaks[   1:p+1]]
    else:
        T[0:p] = breaks[ 0]
        T[-p:] = breaks[-1]

    return T

#==============================================================================
def elevate_knots(knots, degree, periodic):
    """
    Given the knot sequence of a spline space S of degree p, compute the knot
    sequence of a spline space S_0 of degree p+1 such that u' is in S for all
    u in S_0.

    Specifically, on bounded domains the first and last knots are repeated in
    the sequence, and in the periodic case the knot sequence is extended by
    periodicity.

    Parameters
    ----------
    knots : array_like
        Knots sequence of spline space of degree p.

    degree : int
        Spline degree (= polynomial degree within each interval).

    periodic : bool
        True if domain is periodic, False otherwise.

    Returns
    -------
    new_knots : 1D numpy.ndarray
        Knots sequence of spline space of degree p+1.

    """

    if periodic:
        [T, p] = knots, degree
        period = T[-1-p] - T[p]
        left   = T[-1-p-(p+1)] - period
        right  = T[   p+(p+1)] + period
    else:
        left  = knots[0]
        right = knots[-1]

    return np.array([left, *knots, right])

#==============================================================================
def quadrature_grid( breaks, quad_rule_x, quad_rule_w ):
    r"""
    Compute the quadrature points and weights for performing integrals over
    each element (interval) of the 1D domain, given a certain Gaussian
    quadrature rule.

    An n-point Gaussian quadrature rule for the canonical interval $[-1,+1]$
    and trivial weighting function $\omega(x)=1$ is defined by the n abscissas
    $x_i$ and n weights $w_i$ that satisfy the following identity for
    polynomial functions $f(x)$ of degree $2n-1$ or less:

    $\int_{-1}^{+1} f(x) dx = \sum_{i=0}^{n-1} w_i f(x_i)$.

    Parameters
    ----------
    breaks : 1D array_like
        Coordinates of spline breakpoints.

    quad_rule_x : 1D array_like
        Coordinates of quadrature points on canonical interval [-1,1].

    quad_rule_w : 1D array_like
        Weights assigned to quadrature points on canonical interval [-1,1].

    Returns
    -------
    quad_x : 2D numpy.ndarray
        Abscissas of quadrature points on each element (interval) of the 1D
        domain. See notes below.

    quad_w : 2D numpy.ndarray
        Weights assigned to the quadrature points on each element (interval)
        of the 1D domain. See notes below.

    Notes
    -----
    Contents of 2D output arrays 'quad_x' and 'quad_w' are accessed with two
    indices (ie,iq) where:
      . ie is the global element index;
      . iq is the local index of a quadrature point within the element.

    """
    # Check that input arrays have correct size
    assert len(breaks)      >= 2
    assert len(quad_rule_x) == len(quad_rule_w)

    # Check that provided quadrature rule is defined on interval [-1,1]
    assert min(quad_rule_x) >= -1
    assert max(quad_rule_x) <= +1

    quad_rule_x = np.asarray( quad_rule_x )
    quad_rule_w = np.asarray( quad_rule_w )

    ne     = len(breaks)-1
    nq     = len(quad_rule_x)
    quad_x = np.zeros( (ne,nq) )
    quad_w = np.zeros( (ne,nq) )

    # Compute location and weight of quadrature points from basic rule
    for ie,(a,b) in enumerate(zip(breaks[:-1],breaks[1:])):
        c0 = 0.5*(a+b)
        c1 = 0.5*(b-a)
        quad_x[ie,:] = c1*quad_rule_x[:] + c0
        quad_w[ie,:] = c1*quad_rule_w[:]

    return quad_x, quad_w

#==============================================================================
def basis_ders_on_quad_grid(knots, degree, quad_grid, nders, normalization):
    """
    Evaluate B-Splines and their derivatives on the quadrature grid.

    If called with normalization='M', this uses M-splines instead of B-splines.

    Parameters
    ----------
    knots : array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    quad_grid: 2D numpy.ndarray (ne,nq)
        Coordinates of quadrature points of each element in 1D domain,
        which can be given by quadrature_grid() or chosen arbitrarily.

    nders : int
        Maximum derivative of interest.

    normalization : str
        Set to 'B' for B-splines, and 'M' for M-splines.

    Returns
    -------
    basis: 4D numpy.ndarray
        Values of B-Splines and their derivatives at quadrature points in
        each element of 1D domain. Indices are
        . ie: global element         (0 <= ie <  ne    )
        . il: local basis function   (0 <= il <= degree)
        . id: derivative             (0 <= id <= nders )
        . iq: local quadrature point (0 <= iq <  nq    )

    """
    # TODO: add example to docstring

    ne,nq = quad_grid.shape
    basis = np.zeros((ne, degree+1, nders+1, nq))

    if normalization == 'M':
        scaling = 1. / basis_integrals(knots, degree)

    for ie in range(ne):
        xx = quad_grid[ie, :]
        for iq, xq in enumerate(xx):
            span = find_span(knots, degree, xq)
            ders = basis_funs_all_ders(knots, degree, xq, span, nders)
            if normalization == 'M':
                ders *= scaling[None, span-degree:span+1]
            basis[ie, :, :, iq] = ders.transpose()

    return basis

#==============================================================================
def basis_integrals(knots, degree):
    r"""
    Return the integral of each B-spline basis function over the real line:

    K[i] := \int_{-\infty}^{+\infty} B[i](x) dx = (T[i+p+1]-T[i]) / (p+1).

    This array can be used to convert B-splines to M-splines, which have unit
    integral over the real line but no partition-of-unity property.

    Parameters
    ----------
    knots : 1D array_like
        Knots sequence.

    degree : int
        Polynomial degree of B-splines.

    Returns
    -------
    K : 1D numpy.ndarray
        Array with the integrals of each B-spline basis function.

    Notes
    -----
    For convenience, this function does not distinguish between periodic and
    non-periodic spaces, hence the length of the output array is always equal
    to (len(knots)-degree-1). In the periodic case the last (degree) values in
    the array are redundant, as they are a copy of the first (degree) values.

    """
    T = knots
    p = degree
    n = len(T)-p-1
    K = np.array([(T[i+p+1] - T[i]) / (p + 1) for i in range(n)])

    return K

#==============================================================================
def scaling_matrix(p, n, T):
    """Returns the scaling array for M-splines.
    It is an array whose elements are (p+1)/(T[i+p+1]-T[i])

    """

    x = np.zeros(n)
    for i in range(0, n):
        x[i] = (p+1)/(T[i+p+1]-T[i])
    return x

#==============================================================================
def _refinement_matrix_one_stage(t, p, knots):
    """
    computes the refinement matrix corresponding to the insertion of a given knot
    """

    # ...
    def alpha_function(i, k, t, n, p, knots):
        if i <= k-p:
            alpha = 1.

        elif (k-p < i) and (i <= k):
            alpha = (t-knots[i]) / (knots[i+p] - knots[i])

        else:
            alpha = 0.

        return alpha
    # ...

    n = len(knots) - p - 1

    mat = np.zeros((n+1,n))

    left = find_span( knots, p, t )

    # ...
    j = 0
    alpha = alpha_function(j, left, t, n, p, knots)
    mat[j,j] = alpha

    for j in range(1, n):
        alpha = alpha_function(j, left, t, n, p, knots)
        mat[j,j]   = alpha
        mat[j,j-1] = 1.0 - alpha

    j = n
    alpha = alpha_function(j, left, t, n, p, knots)
    mat[j,j-1] = 1.0 - alpha
    # ...

    # ...
    new = np.zeros(n+1+p+1)

    new[:left+1] = knots[:left+1]
    new[left+1] = t
    new[left+2:] = knots[left+1:]
    # ...

    return mat, new

#==============================================================================
def hrefinement_matrix(ts, p, knots):
    """
    computes the refinement matrix corresponding to the insertion of a given list of knots

    Parameters
    ----------
    ts: np.array
        array containing the knots to be inserted

    p: int
        spline degree

    knots : array_like
        Knots sequence.

    Returns
    -------
    mat : np.array[:,:]
        h-refinement matrix

    Examples
    --------
    >>> import numpy as np
    >>> from simplines import make_knots
    >>> from simplines import hrefinement_matrix
    >>> grid = np.linspace(0.,1.,5)
    >>> degree = 2
    >>> knots = make_knots(grid, degree, periodic=False)
    >>> ts    = np.array([0.1, 0.2, 0.4, 0.5, 0.7, 0.8])
    >>> hrefinement_matrix(ts, p, knots)
    array([[1.  , 0.  , 0.  , 0.  , 0.  , 0.  ],
           [0.6 , 0.4 , 0.  , 0.  , 0.  , 0.  ],
           [0.12, 0.72, 0.16, 0.  , 0.  , 0.  ],
           [0.  , 0.6 , 0.4 , 0.  , 0.  , 0.  ],
           [0.  , 0.2 , 0.8 , 0.  , 0.  , 0.  ],
           [0.  , 0.  , 0.7 , 0.3 , 0.  , 0.  ],
           [0.  , 0.  , 0.5 , 0.5 , 0.  , 0.  ],
           [0.  , 0.  , 0.1 , 0.9 , 0.  , 0.  ],
           [0.  , 0.  , 0.  , 0.6 , 0.4 , 0.  ],
           [0.  , 0.  , 0.  , 0.4 , 0.6 , 0.  ],
           [0.  , 0.  , 0.  , 0.  , 0.8 , 0.2 ],
           [0.  , 0.  , 0.  , 0.  , 0.  , 1.  ]])
    """

    m = len(ts)
    n = len(knots) - p - 1
    out = np.eye(n)

    for i in range(m):
        t = ts[i]
        mat, knots = _refinement_matrix_one_stage(t, p, knots)
        out = np.matmul(mat, out)

    return out


import os
import os.path as path

VOC_ROOT = path.join('./', 'VOCdevkit', 'VOC2012')
VOC_LABEL_DIR = path.join(VOC_ROOT, 'ImageSets', 'Main')
VOC_IMG_DIR = path.join(VOC_ROOT, 'JPEGImages')
VOC_OUT_REARRANGED = path.join('./', 'voc-data-rearr')

CLASSES = ['aeroplane', 'bicycle', 'bird', 'boat', 'bottle', 'bus', 'car', 'cat', 'chair', 'cow', 'diningtable', 'dog', 'horse', 'motorbike', 'person', 'pottedplant', 'sheep', 'sofa', 'train', 'tvmonitor']

def move_img(img_name, cls_name):
    img_path = path.join(VOC_IMG_DIR, img_name + '.jpg')
    out_img_path = path.join(VOC_OUT_REARRANGED, cls_name)
    os.system('cp {} {}'.format(img_path, out_img_path))

# create output directory
if not os.path.exists(VOC_OUT_REARRANGED):
    os.mkdir(VOC_OUT_REARRANGED)

for clsname in CLASSES:
    class_folder = path.join(VOC_OUT_REARRANGED, clsname)
    if not os.path.exists(class_folder):
        os.mkdir(class_folder)

for filename in os.listdir(VOC_LABEL_DIR):
    if filename in ['val.txt', 'train.txt', 'trainval.txt']:
        continue

    cls_traintype, _ = path.splitext(filename)
    clsname, traintype = cls_traintype.split('_')

    if traintype != 'train':
        continue

    print('Processing: {}'.format(filename))
    with open(path.join(VOC_LABEL_DIR, filename), 'r') as f:
        lines = f.readlines()
        for line in lines:
          img_name, is_cls = line.strip().split()
          if is_cls == '1':
              move_img(img_name, clsname)


import api, bz2, config, globalPluginHandler, globalVars, gui, addonHandler, os, pickle, tones, weakref, wx, ui

from gui import guiHelper, nvdaControls
from gui.dpiScalingHelper import DpiScalingHelperMixin, DpiScalingHelperMixinWithoutInit
from scriptHandler import script

addonHandler.initTranslation()

def loadFile(path):
	b = bz2.BZ2File(path, "rb")
	return pickle.load(b	)

def saveFile(d, path):
	f= bz2.BZ2File(path, "wb")
	pickle.dump(d, f, 4)
	f.close()

NOTE_FILE = "notes.dat"
pathFile = os.path.abspath(os.path.join(globalVars.appArgs.configPath, NOTE_FILE))

class GlobalPlugin(globalPluginHandler.GlobalPlugin):
	# Translators: script category for add-on gestures
	scriptCategory = _("Note manager")

	def __init__(self, *args, **kwargs):
		super().__init__(*args, **kwargs)
		self.modified = False
		try:
			self.notes = loadFile(pathFile)
			self.curNote = 0
		except:
			self.notes = []
			self.curNote = -1

	def save(self):
		if self.modified:
			saveFile(self.notes, pathFile)
			self.modified = False

	def newNote(self, text, index=None, update=False):
		if index == None:
			self.notes.append(text)
			index = len(self.notes) -1
			self.curNote = index
		else:
			if update:
				self.notes[index] = text
			else:
				self.notes.insert(index, text)
		self.modified = True
		return index

	def removeNote(self, index):
		del self.notes[index]
		self.modified = True

	def isNotes(self):
		if len(self.notes) == 0:
			ui.message(_("no notes available"))
			return False
		else:
			return True

	@script(
		# Translators: Documentation string for copy currently selected item script
		_("Copy the currently selected note to the clipboard, which by default will be the most recently added note."),
		gesture="kb:nvda+alt+v"
	)
	def script_copyCurNote(self, gesture):
		if not self.isNotes() or self.curNote == -1:
			return
		text = self.notes[self.curNote]
		if api.copyToClip(text):
			tones.beep(1500, 120)

	@script(
		# Translators: Documentation string for previous note item script
		_("Review the previous item in NVDA's note manager."),
		gesture="kb:nvda+alt+b"
	)
	def script_prevNote(self, gesture):
		if not self.isNotes():
			return
		self.curNote += 1
		if self.curNote >= len(self.notes):
			tones.beep(220, 100, 100)
			self.curNote -= 1
		ui.message(self.notes[self.curNote])

	@script(
		# Translators: Documentation string for next note item script
		_("Review the next item in NVDA's note manager."),
		gesture="kb:nvda+alt+n"
	)
	def script_nextNote(self, gesture):
		if not self.isNotes():
			return
		self.curNote -= 1
		if self.curNote < 0:
			tones.beep(220, 100, 0, 50)
			self.curNote += 1
		ui.message(self.notes[self.curNote])

	@script(
		# Translators: Documentation string for show in a dialog all notes saved in the note manager.
		_("Opens a dialog showing all notes saved in note manager"),
		gesture="kb:nvda+windows+n"
	)
	def script_showNotesDialog(self, gesture):
		gui.mainFrame.prePopup()
		NotesDialog(gui.mainFrame, self).Show()
		gui.mainFrame.postPopup()

	def terminate(self, *args, **kwargs):
		super().terminate(*args, **kwargs)

	"""
	TODO
	"kb:nvda+alt+r": "recordNote",
	"kb:nvda+alt+v": "writeNote",
	"""

class NotesDialog(
		DpiScalingHelperMixinWithoutInit,
		gui.contextHelp.ContextHelpMixin,
		wx.Dialog  # wxPython does not seem to call base class initializer, put last in MRO
):
	@classmethod
	def _instance(cls):
		""" type: () -> NotesDialog
		return None until this is replaced with a weakref.ref object. Then the instance is retrieved
		with by treating that object as a callable.
		"""
		return None

	helpId = "NoteManagerList"

	def __new__(cls, *args, **kwargs):
		instance = NotesDialog._instance()
		if instance is None:
			return super(NotesDialog, cls).__new__(cls, *args, **kwargs)
		return instance

	def __init__(self, parent, addon):
		if NotesDialog._instance() is not None:
			return
		NotesDialog._instance = weakref.ref(self)
		# Translators: The title of the Notes manager Dialog
		title = _("Notes manager")
		super().__init__(
			parent,
			title=title,
			style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER | wx.MAXIMIZE_BOX,
		)
		# hte add-on instance
		self.addon = addon

		# the results of a search, it's a pair of note / index. initially equals to notes.
		self.searchNotes = None
		# indexes of search, to save the selected item in a specific search.
		self.searches = {"": 0}
		# the current search, initially "".
		self.curSearch = ""
		# the indexes of items selected.
		self.selection = set()

		szMain = guiHelper.BoxSizerHelper(self, sizer=wx.BoxSizer(wx.VERTICAL))
		szCurrent = guiHelper.BoxSizerHelper(self, sizer=wx.BoxSizer(wx.HORIZONTAL))
		szBottom = guiHelper.BoxSizerHelper(self, sizer=wx.BoxSizer(wx.HORIZONTAL))

		# Translators: the label for the search text field in the notes manager add-on.
		self.searchTextFiel = szMain.addLabeledControl(_("&Search"),
			wx.TextCtrl,
			style =wx.TE_PROCESS_ENTER
		)
		self.searchTextFiel.Bind(wx.EVT_TEXT_ENTER, self.onSearch)
		self.searchTextFiel.Bind(wx.EVT_KILL_FOCUS, self.onSearch)

		# Translators: the label for the history elements list in the notes manager add-on.
		entriesLabel = _("Notes")
		self.noteList = nvdaControls.AutoWidthColumnListCtrl(
			parent=self,
			autoSizeColumn=1,
			style=wx.LC_REPORT|wx.LC_NO_HEADER
			)

		szMain.addItem(
			self.noteList,
			flag=wx.EXPAND,
			proportion=4
		)

		# This list consists of only one column.
		# The provided column header is just a placeholder, as it is hidden due to the wx.LC_NO_HEADER style flag.
		self.noteList.InsertColumn(0, entriesLabel)
		self.noteList.Bind(wx.EVT_LIST_ITEM_SELECTED, self.onSelect)
		self.noteList.Bind(wx.EVT_LIST_ITEM_DESELECTED, self.onDeselect)

		# a multiline text field containing the text from the current selected element.
		self.currentNoteField= szCurrent.addItem(
			wx.TextCtrl(self, style =wx.TE_MULTILINE),
			flag=wx.EXPAND,
			proportion=1
		)
		self.currentNoteField.Bind(wx.EVT_KILL_FOCUS, self.onNoteUpdate)

		# Translators: the label for the copy button in the notes Manager add-on.
		self.copyButton = szCurrent.addItem(wx.Button(self, label=_("&Copy item")), proportion=0)
		self.copyButton.Bind(wx.EVT_BUTTON, self.onCopy)

		# Translators: the label for the delete button in the notes Manager add-on.
		self.deleteButton = szCurrent.addItem(wx.Button(self, label=_("&Delete note")), proportion=0)
		self.deleteButton.Bind(wx.EVT_BUTTON, self.onDelete)

		szMain.addItem(
			szCurrent.sizer,
			border=guiHelper.BORDER_FOR_DIALOGS,
			flag = wx.EXPAND,
			proportion=1,
		)

		szMain.addItem(
			wx.StaticLine(self),
			border=guiHelper.BORDER_FOR_DIALOGS,
			flag=wx.ALL | wx.EXPAND
		)

		# Translators: the label for the copy all button in the notes Manager add-on. This is based on the current search.
		self.copyAllButton = szBottom.addItem(wx.Button(self, label=_("Copy &all")))
		self.copyAllButton.Bind(wx.EVT_BUTTON, self.onCopyAll)

		# Translators: the label for the new note button in the notes Manager add-on. This is based on the current search.
		self.newNoteButton = szBottom.addItem(wx.Button(self, label=_("&New note")))
		self.newNoteButton.Bind(wx.EVT_BUTTON, self.onNewNote)

		''' to do"""
		# Translators: the label for the clear notes button in the notes Manager add-on.
		self.clearNotesButton = szBottom.addItem(wx.Button(self, label=_("Clean notes")))
		self.clearNotesButton .Bind(wx.EVT_BUTTON, self.onClear) '''

		# Translators: The label of a button to close the notes Manager dialog.
		closeButton = wx.Button(self, label=_("C&lose"), id=wx.ID_CLOSE)
		closeButton.Bind(wx.EVT_BUTTON, lambda evt: self.Close())
		szBottom.addItem(closeButton)
		self.Bind(wx.EVT_CLOSE, self.onClose)
		self.EscapeId = wx.ID_CLOSE

		szMain.addItem(
			szBottom.sizer,
			border=guiHelper.BORDER_FOR_DIALOGS,
			flag=wx.ALL | wx.EXPAND,
			proportion=1,
		)
		szMain = szMain.sizer
		szMain.Fit(self)
		self.SetSizer(szMain)
		self.doSearch()

		self.SetMinSize(szMain.GetMinSize())
		# Historical initial size, result of L{self.noteList} being (550, 350)
		# Setting an initial size on L{self.noteList} by passing a L{size} argument when
		# creating the control would also set its minimum size and thus block the dialog from being shrunk.
		self.SetSize(self.scaleSize((763, 509)))
		self.CentreOnScreen()
		self.searchTextFiel.SetFocus()

	def doSearch(self, text=""):
		self.selection = set()
		if not text:
			self.searchNotes = list(enumerate(self.addon.notes))
		else:
			self.searchNotes = [k for k in enumerate(self.addon.notes) if text in k[1].lower()]
		self.noteList.DeleteAllItems()
		self.currentNoteField.SetValue("")
		self.currentNoteField.SetEditable(False)
		for k in self.searchNotes: self.noteList.Append((k[1][0:100],))
		if len(self.searchNotes) > 0:
			index = self.searches.get(text, 0)
			self.setFocusItem(index)

	def setFocusItem(self, index):
		if len(self.searchNotes) == 0:
			self.currentNoteField.SetValue("")
			self.noteList.ClearAll()
			return
		if index == -1 or index >= len(self.searchNotes):
			index = len(self.searchNotes) -1
		note = self.searchNotes[index]
		self.selection = {index}
		self.currentNoteField.SetValue(note[1])
		self.currentNoteField.SetEditable(True)
		self.noteList.Select(index, on=1)
		self.noteList.SetItemState(index,wx.LIST_STATE_FOCUSED,wx.LIST_STATE_FOCUSED)

	def updateSelection(self):
		self.currentNoteField.SetValue(self.itemsToString(sorted(self.selection)))
		if len(self.selection) > 1:
			self.currentNoteField.SetEditable(False)
		else:
			self.currentNoteField.SetEditable(True)

	def itemsToString(self, items):
		s = ""
		for k in items:
			s += self.searchNotes[k][1] +"\n"
		if s: s= s[0:-1]
		return s

	def onSearch(self, evt=None, t=""):
		if evt: t = self.searchTextFiel.GetValue().lower()
		index = self.noteList.GetFocusedItem()
		self.searches[self.curSearch] = index
		self.curSearch = t
		self.doSearch(t)

	def onClose(self,evt):
		self.onNoteUpdate()
		self.DestroyChildren()
		self.Destroy()

	def onCopy(self,evt):
		t = self.currentNoteField.GetValue()
		if t:
			if api.copyToClip(t):
				tones.beep(1500, 120)

	def onCopyAll(self, evt):
		t = self.itemsToString(range(0, len(self.searchNotes)))
		if t and api.copyToClip(t):
			tones.beep(1500, 120)

	def onSelect(self, evt):
		index=evt.GetIndex()
		self.selection.add(index)
		self.updateSelection()

	def onDeselect(self, evt):
		index=evt.GetIndex()
		if index in self.selection:
			self.selection.remove(index)
		self.updateSelection()

	def onNewNote(self, evt):
		self.onNoteUpdate(evt)
		newIndex = len(self.addon.notes)
		self.searches[""] = newIndex
		self.addon.notes.append("")
		self.searchNotes.append((newIndex, ""))
		self.noteList.Append(("",))
		newIndex = len(self.searchNotes)-1
		self.setFocusItem(newIndex)
		self.currentNoteField.SetFocus()

	def onDelete(self, evt):
		if not self.selection or len(self.searchNotes) == 0:
			return
		result = gui.messageBox(
			# Translators: A message asking the user to confirm notes deletion.
			message=_("Are you sure to delete the selected notes? This action can't be undone."),
			# Translators: Title for message to delete notes.
			caption=_("Delete selected notes"),
			style=wx.YES | wx.NO | wx.ICON_WARNING
		)
		if wx.YES != result:
			return
		deletedItems = 0
		for selected in sorted(self.selection):
			nIndex = self.searchNotes[selected][0]-deletedItems
			self.addon.removeNote(nIndex)
			deletedItems = deletedItems+1
		self.addon.save()
		self.onSearch(t=self.curSearch)

	def onNoteUpdate(self, evt=None):
		if len(self.selection) > 1 or len(self.searchNotes) == 0:
			return
		text = self.currentNoteField.GetValue()
		index = self.noteList.GetFocusedItem()
		origIndex, oldText = self.searchNotes[index]
		if text != oldText:
			self.addon.newNote(text, origIndex, True)
			self.addon.save()
			tones.beep(1500, 120)
			if evt:
				e = self.noteList.SetItemText(index, text[0:100])
				self.searchNotes[index] = (origIndex, text)


import requests as res
import json
from lxml import html
z=1
pokemon_dict={}
pokemon_list=[]
def Extract_data(url):
    response=res.get(url)
    tree=html.fromstring(html=response.text)
    title=tree.xpath('/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/h1/text()')[0]
    price=('£'+str(tree.xpath('/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/p[1]/span/text()')[0]))
    stock=tree.xpath('/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/p[2]/text()')[0].split(" ")[0]
    description=tree.xpath('/html/body/div[1]/div[2]/div/div[2]/main/div/div[3]/div[1]/p/text()')[0]
    sku=tree.xpath('/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/div[2]/span[1]/span/text()')[0]
    category_count=len(tree.xpath('/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/div[2]/span[2]')[0])
    category_list=[]
    for i in range(1,category_count+1):
        xp='/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/div[2]/span[2]/a['+str(i)+']/text()'
        xp2=tree.xpath(xp)[0]
        category_list.append(xp2)
    tag_count=len(tree.xpath('/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/div[2]/span[3]')[0])
    tag_list=[]
    for j in range(1,tag_count+1):
        xp3='/html/body/div[1]/div[2]/div/div[2]/main/div/div[2]/div[2]/span[3]/a['+str(j)+']/text()'
        xp4=tree.xpath(xp3)[0]
        tag_list.append(xp4)
    pokemon={}
    pokemon_dtls={}

    pokemon['Price']=str(price)
    pokemon['Stock']=stock
    pokemon['SKU']=sku
    pokemon['Description']=str(description)
    pokemon['Categories']=category_list
    pokemon['Tags']=tag_list
    pokemon_dtls[title]=pokemon
    pokemon_list.append(pokemon_dtls)

response1=res.get('https://scrapeme.live/shop/')
tree=html.fromstring(html=response1.text)
main=tree.xpath('//*[@id="main"]/ul')[0]
itr_no=(len(main))
for i in range(1,itr_no+1):
    url='//*[@id="main"]/ul/li['+str(i)+']/a[1]'
    main2=tree.xpath(url)[0]
    Extract_data(str(html.tostring(main2)).split(" ")[1].replace("href=","").replace("\"",""))
for j in range(2,49):
    temp_url='https://scrapeme.live/shop/page/'+str(j)
    response1=res.get(temp_url)
    tree=html.fromstring(html=response1.text)
    main=tree.xpath('//*[@id="main"]/ul')[0]
    itr_no=(len(main))
    for i in range(1,itr_no+1):
        url='//*[@id="main"]/ul/li['+str(i)+']/a[1]'
        main2=tree.xpath(url)[0]
        Extract_data(str(html.tostring(main2)).split(" ")[1].replace("href=","").replace("\"",""))
    print(str(z),'/',str(775))
    z+=1
pokemon_dict['Pokemon']=pokemon_list
# print(pokemon_dict)

with open ('pokemon_1.json','w',encoding='utf-8') as write_file:
    json.dump(pokemon_dict,write_file,indent=2,ensure_ascii=False)


#!/usr/bin/python
# coding=utf-8
from __future__ import print_function
from common import *
from entry import *
from textwrap import dedent

__doc__ = """Stores collective data for individual flower knights.

The master data stores data for each and every flower knight across
multiple CSV entries. Each entry pertains to one evolution stage.
The FlowerKnight class connects all the data from the CharacterEntry instances
and makes a single representable flower knight out of them. It is easier
to manage character data this way.
"""

class FlowerKnight(object):
	"""Class for storing all of a flower knight's info.

	It does not store info for other variants.
	"""
	# These constants correspond to the "stats" dict.
	(HP,
	ATK,
	DEF) = range(3)
	# These are the evolution tiers the character is capable of.
	(NO_BLOOM,
	BLOOMABLE,
	BLOOM_POWERS_ONLY) = range(3)
	# These are the rarity growth tiers the character is capable of.
	(NO_RARITY_GROWTH,
	HAS_RARITY_GROWTH) = range(2)

	# If a bug was found regarding how the data was parsed, state it only once.
	# When False, a message is printed and it turns to True.
	# When True, no message is printed.
	stated_integrity_bug = False

	def __init__(my, entries=[]):
		"""Constructor.

		@param entries: A list of 0~3 CharacterEntry instances.
			They would be the pre-evolved, evolved, and bloomed forms.
		"""

		# Unset the full name. When we get the 1st entry, this will be filled.
		my.fullName = ''
		# Organize the entries based on evolution tier.
		# They use the Lua list format: Indices start at 1.
		my.tiers = {1:{}, 2:{}, 3:{}, 4:{}}
		# Assume the character can't bloom or rarity grow.
		my.bloomability = FlowerKnight.NO_BLOOM
		my.growability = FlowerKnight.NO_RARITY_GROWTH
		# Store the latest date out of all CSV values.
		# This will be only calculated once on the fly once
		# get_latest_date is called.
		my.latest_date = None
		my.add_entries(entries)

	def add_entries(my, entries):
		"""Adds a list of CharacterEntry instances to this knight's data."""
		# Force the param to be iterable
		if type(entries) is not list:
			entries = [entries]
		for entry in entries:
			my.add_entry(entry)

	def add_entry(my, entry):
		"""Adds a CharacterEntry instance to this knight's data."""
		if int(entry.id0) >= 700000 and entry.fullName == 'ツツジ':
			# This is an NPC character. Do not store their data.
			# Otherwise, it might overwrite the playable character's data.
			# An example is the one-star Tsutsuji/Azalea.
			return
		tier = entry.evolutionTier
		isRarityGrown = entry.isRarityGrown == '1'
		maxEvolutionFlag = entry.maxEvolutionFlag == '1'
		bloomingEnableFlag = entry.bloomingEnableFlag == '1'
		if tier == '1':
			my._add_tier1_entry(entry)
		elif tier == '2':
			my._add_tier2_entry(entry)
		elif (tier == '3' and not isRarityGrown) or \
			(tier == '98' and isRarityGrown and not maxEvolutionFlag and bloomingEnableFlag):
			# tier = 3 means it is a 5* or 6* that can bloom.
			# tier = 98 and tier = 99 means it is a 2-4* that can go beyond evolution.
			my._add_tier3_entry(entry)
		elif (tier == '4' and isRarityGrown) or \
			(tier == '99' and isRarityGrown and maxEvolutionFlag and not bloomingEnableFlag):
			my._add_tier4_entry(entry)
		elif not FlowerKnight.stated_integrity_bug:
			print('Warning: CharacterEntry w/an invalid evolution tier.\n' \
				'The logic may be wrong or the CSVs have changed meanings.\n' \
				'Character {0} w/id {1} was an exception.\n'.format(
					entry.fullName, entry.id0) + \
				'Will ignore bugged entries and stop stating this message.')
			FlowerKnight.stated_integrity_bug = True

	def _add_common_data(my, entry):
		"""Stores info which should be the same for all evolution tiers."""
		if my.fullName:
			# The data is already stored.
			return
		my.fullName = entry.fullName
		my.reading = entry.reading
		my.rarity = entry.rarity
		my.spd = entry.lvlOneSpd
		# TODO: Rely on my.tiers[NUMBER]['skill'] instead.
		# The skill is no longer the same between all evolution tiers.
		# It changes if the character undergoes rarity growth.
		my.skill = entry.skill1ID
		my.family = entry.family
		my.type = entry.type
		my.nation = entry.nation
		my.gift = entry.gift
		my.is_event = entry.isEventKnight
		my.is_powers_only_bloom = entry.isBloomedPowersOnly
		my._determine_romaji()

	def _add_tier1_entry(my, entry):
		"""Stores the CharacterEntry's data as the pre-evolved info.

		As a side-effect of calling this method, some of the common
		information between all evolution tiers is stored in the class.
		"""

		my._add_common_data(entry)
		my.tiers[1]['id'] = entry.id0
		my.tiers[1]['lvlCap'] = maxLevel[my.rarity][0]
		my.tiers[1]['skill'] = entry.skill1ID
		my.tiers[1]['abilities'] = [entry.ability1ID, entry.ability2ID, entry.ability3ID]
		my.tiers[1]['lvlOne'] = [entry.lvlOneHP, entry.lvlOneAtk, entry.lvlOneDef]
		my.tiers[1]['lvlMax'] = [entry.lvlMaxHP, entry.lvlMaxAtk, entry.lvlMaxDef]
		my.tiers[1]['aff1'] = [entry.aff1MultHP, entry.aff1MultAtk, entry.aff1MultDef]
		my.tiers[1]['aff2'] = [entry.aff2MultHP, entry.aff2MultAtk, entry.aff2MultDef]
		# Both date values and the game-version-when-added strings can differ between tiers.
		my.tiers[1]['date0'] = entry.date0
		my.tiers[1]['gameVersionWhenAdded'] = entry.gameVersionWhenAdded
		# charID and charID2 is now almost identical except for skins.
		# libraryID is same across character variants and skins.
		my.charID1 = entry.charID1
		my.charID2 = entry.charID2
		my.libID = entry.libraryID

	def _add_tier2_entry(my, entry):
		"""Stores the CharacterEntry's data as the evolved info."""
		my.tiers[2]['id'] = entry.id0
		my.tiers[2]['skill'] = entry.skill1ID
		my.tiers[2]['lvlCap'] = maxLevel[my.rarity][1]
		my.tiers[2]['abilities'] = [entry.ability1ID, entry.ability2ID, entry.ability3ID]
		my.tiers[2]['lvlOne'] = [entry.lvlOneHP, entry.lvlOneAtk, entry.lvlOneDef]
		my.tiers[2]['lvlMax'] = [entry.lvlMaxHP, entry.lvlMaxAtk, entry.lvlMaxDef]
		my.tiers[2]['aff1'] = [entry.aff1MultHP, entry.aff1MultAtk, entry.aff1MultDef]
		my.tiers[2]['aff2'] = [entry.aff2MultHP, entry.aff2MultAtk, entry.aff2MultDef]
		my.tiers[2]['date0'] = entry.date0
		my.tiers[2]['gameVersionWhenAdded'] = entry.gameVersionWhenAdded

	def _add_tier3_entry(my, entry):
		"""Stores the CharacterEntry's data as the bloomed info.

		Aa a side-effect of calling this method, the "bloomability" is set.
		"""

		if entry.isBloomedPowersOnly == "1":
			my.bloomability = FlowerKnight.BLOOM_POWERS_ONLY
		else:
			my.bloomability = FlowerKnight.BLOOMABLE
		my.tiers[3]['id'] = entry.id0
		my.tiers[3]['skill'] = entry.skill1ID
		my.tiers[3]['lvlCap'] = maxLevel[my.rarity][2]
		my.tiers[3]['abilities'] = [entry.ability1ID, entry.ability2ID, entry.ability3ID]
		if entry.ability4ID != "0" or entry.ability5ID != "0" or entry.ability6ID != "0":
			my.tiers[3]['abilities'] += [entry.ability4ID, entry.ability5ID, entry.ability6ID]
		if entry.ability7ID != "0" or entry.ability8ID != "0" or entry.ability9ID != "0":
			my.tiers[3]['abilities'] += [entry.ability7ID, entry.ability8ID, entry.ability9ID]
		my.tiers[3]['lvlOne'] = [entry.lvlOneHP, entry.lvlOneAtk, entry.lvlOneDef]
		my.tiers[3]['lvlMax'] = [entry.lvlMaxHP, entry.lvlMaxAtk, entry.lvlMaxDef]
		my.tiers[3]['aff1'] = [entry.aff1MultHP, entry.aff1MultAtk, entry.aff1MultDef]
		my.tiers[3]['aff2'] = [entry.aff2MultHP, entry.aff2MultAtk, entry.aff2MultDef]
		my.tiers[3]['date0'] = entry.date0
		my.tiers[3]['gameVersionWhenAdded'] = entry.gameVersionWhenAdded

	def _add_tier4_entry(my, entry):
		"""Stores the CharacterEntry's data as the rarity grown info.

		Aa a side-effect of calling this method, the "growability" is set.
		"""

		my.growability = FlowerKnight.HAS_RARITY_GROWTH
		my.tiers[4]['id'] = entry.id0
		# Rarity growth turns the character's rarity into a 6-star.
		my.tiers[4]['lvlCap'] = maxLevel['6'][2]
		my.tiers[4]['skill'] = entry.skill1ID
		my.tiers[4]['abilities'] = [entry.ability1ID, entry.ability2ID, entry.ability3ID]
		if entry.ability4ID != "0" or entry.ability5ID != "0" or entry.ability6ID != "0":
			my.tiers[4]['abilities'] += [entry.ability4ID, entry.ability5ID, entry.ability6ID]
		if entry.ability7ID != "0" or entry.ability8ID != "0" or entry.ability9ID != "0":
			my.tiers[4]['abilities'] += [entry.ability7ID, entry.ability8ID, entry.ability9ID]
		my.tiers[4]['lvlOne'] = [entry.lvlOneHP, entry.lvlOneAtk, entry.lvlOneDef]
		my.tiers[4]['lvlMax'] = [entry.lvlMaxHP, entry.lvlMaxAtk, entry.lvlMaxDef]
		my.tiers[4]['aff1'] = [entry.aff1MultHP, entry.aff1MultAtk, entry.aff1MultDef]
		my.tiers[4]['aff2'] = [entry.aff2MultHP, entry.aff2MultAtk, entry.aff2MultDef]
		my.tiers[4]['date0'] = entry.date0
		my.tiers[4]['gameVersionWhenAdded'] = entry.gameVersionWhenAdded

	def _determine_romaji(my):
		"""Determines the romaji spelling of the full name."""
		# TODO
		my.romajiName = '""'

	def can_evolve(my):
		"""Returns True if the character can evolve.

		Characters that can't evolve are either materials or skins."""
		return 'id' in my.tiers[2]

	def can_bloom(my):
		"""Returns True if the character can bloom."""
		return my.bloomability != FlowerKnight.NO_BLOOM

	def can_rarity_grow(my):
		"""Returns True if the character can rarity grow."""
		return my.growability != FlowerKnight.NO_RARITY_GROWTH

	def get_latest_date(my):
		"""Gets the latest date in all evolution tiers."""
		if my.latest_date:
			# The latest date was determined and stored.
			# Just return the pre-calculated value.
			return my.latest_date
		# Determine the latest date amongst all available dates.
		if my.bloomability == FlowerKnight.NO_BLOOM:
			# Cannot bloom or rarity grow.
			my.latest_date = max([
				my.tiers[1]['date0'],
				my.tiers[2]['date0'],])
		elif my.growability == FlowerKnight.NO_RARITY_GROWTH:
			# Can bloom, but not rarity grow.
			my.latest_date = max([
				my.tiers[1]['date0'],
				my.tiers[2]['date0'],
				my.tiers[3]['date0'],])
		else:
			# Can bloom and rarity grow.
			my.latest_date = max([
				my.tiers[1]['date0'],
				my.tiers[2]['date0'],
				my.tiers[3]['date0'],
				my.tiers[4]['date0'],])
		return my.latest_date

	def has_id(my, id):
		"""Checks if the passed ID is related to this flower knight.

		@param id: String or integer. The ID to check.

		@returns: Boolean. True if the ID is related to this flower knight.
			False if the id is 0, a blank string, or an unrelated number.
		"""

		id = str(id)
		if not id:
			return False
		elif 'id' not in my.tiers[2]:
			# This flower knight is only a skin. It can't evolve.
			return id == my.tiers[1]['id']
		elif my.bloomability == FlowerKnight.NO_BLOOM:
			# This flower knight is evolvable, but not bloomable.
			return id in (my.tiers[1]['id'], my.tiers[2]['id'])
		elif my.growability == FlowerKnight.NO_RARITY_GROWTH:
			# This flower knight can bloom, but not rarity grow.
			return id in (my.tiers[1]['id'], my.tiers[2]['id'],
				my.tiers[3]['id'])
		else:
			# This flower knight can bloom and rarity grow.
			return id in (my.tiers[1]['id'], my.tiers[2]['id'],
				my.tiers[3]['id'], my.tiers[4]['id'])

	def get_lua(my, quoted=False):
		"""Returns the stored data as a Lua list.

		@param quoted: Boolean. When True, encloses string values
			of this class' variables in double-quotes.

		@returns: String. All variables of the class in Lua table format.
		"""

		#Fill out a string format table with descriptive variable names.
		formatDict = {
			'id':my.tiers[1]['id'],
			'charID1':my.charID1,
			'charID2':my.charID2,
			'libID':my.libID,
			'type':my.type,
			'rarity':my.rarity,
			'isEvent':my.is_event,
			'reading':my.reading,
			# Has bloom features?
			'tier3PowersOnlyBloom':my.is_powers_only_bloom,
			'gift':my.gift,
			'nation':my.nation,
			'family':my.family,
			'japanese':quotify_non_number(my.fullName),
			'dateAdded':quotify_non_number(my.tiers[1]['date0']),
			# Stats
			'speed':my.spd,
			# Pre-evolution stats.
			'tier1Lv1HP':my.tiers[1]['lvlOne'][HP],
			'tier1Lv1Atk':my.tiers[1]['lvlOne'][ATK],
			'tier1Lv1Def':my.tiers[1]['lvlOne'][DEF],
			'tier1LvMaxHP':my.tiers[1]['lvlMax'][HP],
			'tier1LvMaxAtk':my.tiers[1]['lvlMax'][ATK],
			'tier1LvMaxDef':my.tiers[1]['lvlMax'][DEF],
			# Bonus stats from affection 1 and 2.
			# Pre-evolution affection bonuses.
			'tier1Aff1HP':my.tiers[1]['aff1'][HP],
			'tier1Aff1Atk':my.tiers[1]['aff1'][ATK],
			'tier1Aff1Def':my.tiers[1]['aff1'][DEF],
			'tier1Aff2HP':my.tiers[1]['aff2'][HP],
			'tier1Aff2Atk':my.tiers[1]['aff2'][ATK],
			'tier1Aff2Def':my.tiers[1]['aff2'][DEF],
			# Abilities
			# Skill
			'skill':my.tiers[1]['skill'],
			}

		if my.can_evolve():
			formatDict.update({
				# Evolution stats.
				'tier2Lv1HP':my.tiers[2]['lvlOne'][HP],
				'tier2Lv1Atk':my.tiers[2]['lvlOne'][ATK],
				'tier2Lv1Def':my.tiers[2]['lvlOne'][DEF],
				'tier2LvMaxHP':my.tiers[2]['lvlMax'][HP],
				'tier2LvMaxAtk':my.tiers[2]['lvlMax'][ATK],
				'tier2LvMaxDef':my.tiers[2]['lvlMax'][DEF],
				# Evolution affection bonuses.
				'tier2Aff1HP':my.tiers[2]['aff1'][HP],
				'tier2Aff1Atk':my.tiers[2]['aff1'][ATK],
				'tier2Aff1Def':my.tiers[2]['aff1'][DEF],
				'tier2Aff2HP':my.tiers[2]['aff2'][HP],
				'tier2Aff2Atk':my.tiers[2]['aff2'][ATK],
				'tier2Aff2Def':my.tiers[2]['aff2'][DEF],
			})

		if my.can_bloom():
			formatDict.update({
				# Bloom stats.
				'tier3Lv1HP':my.tiers[3]['lvlOne'][HP],
				'tier3Lv1Atk':my.tiers[3]['lvlOne'][ATK],
				'tier3Lv1Def':my.tiers[3]['lvlOne'][DEF],
				'tier3LvMaxHP':my.tiers[3]['lvlMax'][HP],
				'tier3LvMaxAtk':my.tiers[3]['lvlMax'][ATK],
				'tier3LvMaxDef':my.tiers[3]['lvlMax'][DEF],
				# Bloom affection bonuses.
				'tier3Aff1HP':my.tiers[3]['aff1'][HP],
				'tier3Aff1Atk':my.tiers[3]['aff1'][ATK],
				'tier3Aff1Def':my.tiers[3]['aff1'][DEF],
				'tier3Aff2HP':my.tiers[3]['aff2'][HP],
				'tier3Aff2Atk':my.tiers[3]['aff2'][ATK],
				'tier3Aff2Def':my.tiers[3]['aff2'][DEF],
			})

		if my.can_rarity_grow():
			formatDict.update({
				'tier4skill':my.tiers[4]['skill'],
				# Rarity grown stats.
				'tier4Lv1HP':my.tiers[4]['lvlOne'][HP],
				'tier4Lv1Atk':my.tiers[4]['lvlOne'][ATK],
				'tier4Lv1Def':my.tiers[4]['lvlOne'][DEF],
				'tier4LvMaxHP':my.tiers[4]['lvlMax'][HP],
				'tier4LvMaxAtk':my.tiers[4]['lvlMax'][ATK],
				'tier4LvMaxDef':my.tiers[4]['lvlMax'][DEF],
				# Rarity grown affection bonuses.
				'tier4Aff1HP':my.tiers[4]['aff1'][HP],
				'tier4Aff1Atk':my.tiers[4]['aff1'][ATK],
				'tier4Aff1Def':my.tiers[4]['aff1'][DEF],
				'tier4Aff2HP':my.tiers[4]['aff2'][HP],
				'tier4Aff2Atk':my.tiers[4]['aff2'][ATK],
				'tier4Aff2Def':my.tiers[4]['aff2'][DEF],
			})

		#Generate specific portions of the table.
		tier2StatsString = tier2AffString = ''
		abilityString = '{{{0}, {1}, {2}}},'.format(
			my.tiers[1]['abilities'][0], my.tiers[1]['abilities'][1],
			my.tiers[1]['abilities'][2])
		if my.can_evolve():
			tier2StatsString = dedent('''
				tier2Lv1 = {{ {tier2Lv1HP}, {tier2Lv1Atk}, {tier2Lv1Def} }},
				tier2LvMax = {{ {tier2LvMaxHP}, {tier2LvMaxAtk}, {tier2LvMaxDef} }},
				''').lstrip().format(**formatDict)
			tier2AffString = dedent('''
				tier2Aff1Bonus = {{ {tier2Aff1HP}, {tier2Aff1Atk}, {tier2Aff1Def} }},
				tier2Aff2Bonus = {{ {tier2Aff2HP}, {tier2Aff2Atk}, {tier2Aff2Def} }},
				''').lstrip().format(**formatDict)
			abilityString += '\n    {{{0}, {1}, {2}}},'.format(
				my.tiers[2]['abilities'][0], my.tiers[2]['abilities'][1],
				my.tiers[2]['abilities'][2])

		tier3StatsString = tier3AffString = ''
		if my.can_bloom():
			tier3StatsString = dedent('''
				tier3Lv1 = {{ {tier3Lv1HP}, {tier3Lv1Atk}, {tier3Lv1Def} }},
				tier3LvMax = {{ {tier3LvMaxHP}, {tier3LvMaxAtk}, {tier3LvMaxDef} }},'''
				).lstrip().format(**formatDict)
			tier3AffString = dedent('''
				tier3Aff1Bonus = {{ {tier3Aff1HP}, {tier3Aff1Atk}, {tier3Aff1Def} }},
				tier3Aff2Bonus = {{ {tier3Aff2HP}, {tier3Aff2Atk}, {tier3Aff2Def} }},
				''').lstrip().format(**formatDict)
			abilityString += '\n    {{{0}}},'.format(
				', '.join([abilityid for abilityid in my.tiers[3]['abilities']]))

		tier4StatsString = tier4AffString = ''
		tier4SkillString = ''
		if my.can_rarity_grow():
			tier4StatsString = '\n' + dedent('''
				tier4Lv1 = {{ {tier4Lv1HP}, {tier4Lv1Atk}, {tier4Lv1Def} }},
				tier4LvMax = {{ {tier4LvMaxHP}, {tier4LvMaxAtk}, {tier4LvMaxDef} }},'''
				).lstrip().format(**formatDict)
			tier4AffString = dedent('''
				tier4Aff1Bonus = {{ {tier4Aff1HP}, {tier4Aff1Atk}, {tier4Aff1Def} }},
				tier4Aff2Bonus = {{ {tier4Aff2HP}, {tier4Aff2Atk}, {tier4Aff2Def} }},
				''').lstrip().format(**formatDict)
			abilityString += '\n    {{{0}}},'.format(
				', '.join([abilityid for abilityid in my.tiers[4]['abilities']]))
			tier4SkillString = dedent('''
				tier4skill = {tier4skill},
				''').lstrip().format(**formatDict)

		# Add all of the generated strings to the string format table.
		formatDict.update({
			'tier2StatsString':tier2StatsString,
			'tier2AffString':tier2AffString,
			'tier3StatsString':tier3StatsString,
			'tier3AffString':tier3AffString,
			'tier4StatsString':tier4StatsString,
			'tier4AffString':tier4AffString,
			'abilityString':abilityString,
			'tier4skill':tier4SkillString,
		})

		lua_table = dedent(u'''
			{{id = {id},
			name = {japanese},
			reading = "{reading}",
			libID = {libID},
			personalEquipOwnerID = {charID1},
			type = {type},
			rarity = {rarity},
			isEvent = {isEvent},
			tier3PowersOnlyBloom = {tier3PowersOnlyBloom},
			likes = {gift},
			nation = {nation},
			family = {family},
			dateAdded = {dateAdded},
			skill = {skill},
			{tier4skill}bundledAbilities = {{ {abilityString} }},
			tier1Lv1 = {{ {tier1Lv1HP}, {tier1Lv1Atk}, {tier1Lv1Def} }},
			tier1LvMax = {{ {tier1LvMaxHP}, {tier1LvMaxAtk}, {tier1LvMaxDef} }},
			{tier2StatsString}{tier3StatsString}{tier4StatsString}
			tier1Aff1Bonus = {{ {tier1Aff1HP}, {tier1Aff1Atk}, {tier1Aff1Def} }},
			tier1Aff2Bonus = {{ {tier1Aff2HP}, {tier1Aff2Atk}, {tier1Aff2Def} }},
			{tier2AffString}{tier3AffString}{tier4AffString}speed = {speed}}}''').lstrip().format(**formatDict)

		return lua_table

	def __str__(my):
		return 'FlowerKnight: {0} who is a {1}* {2} type with pre-evo ID {3}.'.format(
			remove_quotes(my.fullName), my.rarity, attribList[my.type],
			my.tiers[1]['id'])

# Globalize the FlowerKnight constants for short access.
HP = FlowerKnight.HP
ATK = FlowerKnight.ATK
DEF = FlowerKnight.DEF


#!/usr/bin/env python3
import sys
from os.path import join, realpath, dirname
sys.path.append(dirname(realpath(__file__)))
from pathlib import Path
from common import rm_dirs, make_dirs, mount_overlay, unmount_overlay

WEECHAT_CONTAINER_DIR = Path().home() / '.weechat'
WEECHAT_HOME = WEECHAT_CONTAINER_DIR / 'client'
WEECHAT_COW = WEECHAT_CONTAINER_DIR / 'cow'
WEECHAT_UPPERDIR = WEECHAT_COW / 'upperdir'
WEECHAT_WORKDIR = WEECHAT_COW / 'workdir'
WEECHAT_MERGED = WEECHAT_COW / 'merged'
WEECHAT_NET = WEECHAT_CONTAINER_DIR / '.weechat_net'
WEECHAT_HOSTS_FILE = WEECHAT_NET / 'hosts'

CONFIG = {
    'alias': {
        'weechat': 'weechat',
        'wc': 'weechat',
    },
    'global': {
        'run_options': [
            '--rm',
            '--interactive',
            '--tty',
            '--cap-drop=ALL',
            '--read-only',
            '--privileged=False'
        ],
        'daemon_url': 'unix://var/run/docker.sock'
    },
    'weechat': {
        'image': '0b10/weechat:edge',
        'project_dir': 'weechat',
        'run_options': [
            '--volume',
            '{}:/weechat:rw'.format(WEECHAT_MERGED)
        ],
        # docker network create --driver=bridge --subnet=172.18.0.0/30 --ip-range=172.18.0.0/30 --gateway=172.18.0.1 weechat-bridge
        'network': {
            'bridge_name': 'weechat-bridge',
            'driver': 'bridge',
            'subnet': '172.18.0.0/30',
            'ip_range': '172.18.0.0/30',
            'gateway': '172.18.0.1'
        },
        'pre': [
            {
                'description': 'discarding any previous overlay data',
                'job': lambda: rm_dirs(dirs=[WEECHAT_UPPERDIR], ignore_errors=True)
            },
            {
                'description': 'creating overlay directories',
                'job': lambda: make_dirs(
                    [WEECHAT_HOME, WEECHAT_UPPERDIR, WEECHAT_WORKDIR, WEECHAT_MERGED]
                )
            },
            {
                'description': 'mounting overlay fs',
                'job': lambda: mount_overlay(lower=WEECHAT_HOME, upper=WEECHAT_UPPERDIR,
                                             work=WEECHAT_WORKDIR, merged=WEECHAT_MERGED)
            }
        ],
        'post': [
            {
                'description': 'unmounting overlay fs',
                'job': lambda: unmount_overlay(mountpoint=WEECHAT_MERGED)
            },
            {
                'description': 'discarding overlay data',
                'job': lambda: rm_dirs(dirs=[WEECHAT_UPPERDIR], clean=True)
            }
        ],
    }
}

class Config:
    @classmethod
    def alias(cls, alias):
        return CONFIG['alias'].get(alias, alias)

    @classmethod
    def get(cls, alias):
        return CONFIG[cls.alias(alias)]

    @classmethod
    def daemon_url(cls):
        return CONFIG['global']['daemon_url']

    @classmethod
    def network(cls, alias):
        name = cls.alias(alias)
        return CONFIG[name]['network']

    @classmethod
    def image_name(cls, alias):
        name = cls.alias(alias)
        return CONFIG[name]['image']

    @classmethod
    def project_dir(cls, alias):
        name = cls.alias(alias)
        return CONFIG[name]['project_dir']

    @classmethod
    def all_aliases(cls):
        return CONFIG['alias'].keys()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Jul 13 14:19:34 2022

@author: ali
"""

import dcor
import math

import numpy as np
from timeit import timeit
import matplotlib.pyplot as plt
from numpy import random
import time

def vertex(x, y, delta=0.05, z=None, return_plot = False, verbose=False):

    V=np.arange(0, x.shape[2], 1, dtype=int)

    V_temp=V
    V_dim=V_temp.shape
    V_dim,=V_dim
    x_reduced=x

    dcors=[]
    V_temp_all = []
    k=0
    V_temp_all = []
    c_all=[]
    V_temp_all.append(V)
    if verbose:
        print('Begin iterating process')
    while V_dim > 1:
        time1= time.time()
        c=[]
        V_index=[]
        for u in V_temp:
            xtemp=x[:,V_temp,:]
            #print(xtemp.shape)
            xtemp=xtemp[:,:,u]
            #print(xtemp)
            #print(dcor.distance_correlation(xtemp, y))
            if z is None:
                c=np.append(c,dcor.distance_correlation(xtemp, y))
            else:
                c=np.append(c,dcor.partial_distance_correlation(xtemp, y,z)) #partial distance correlation

        dcors.append(np.max(c))
        t_temp=np.quantile(c,delta)
        index_temp=c>t_temp

        V_temp = np.array(V_temp_all[k])[index_temp]
        V_temp_all.append(V_temp)
        c_all.append(c)
        V_dim=np.size(V_temp)
        time_step = time.time() - time1
        if verbose:
            print(f'Step {k} accomplished in {np.round(time_step)} secs, max time {np.round(x.shape[1]*time_step*(1-delta)**k)}')
        k=k+1

    index_winner=np.max(np.argwhere(dcors == np.amax(dcors)))

    if verbose:
        print(dcors[index_winner])
        print(V_temp_all[index_winner].shape)

    V_winner=V_temp_all[index_winner]
    c_winner=c_all[index_winner]

    if return_plot:
        import matplotlib.pyplot as plt
        dimensions=[x.shape[0]for x in V_temp_all]
        plt.scatter( dimensions[0:len(dimensions)-1] , dcors )
        plt.title("Signal Distance Correlation")
        plt.xlabel("Dimension of Vertices")
        plt.ylabel("Maximum Distance Correlation")
        plt.savefig('/Users/daniel/Downloads/algo1fig.png', dpi=1000)
        return V_winner, c_winner, dcors, plt
    else:
        return V_winner, c_winner, dcors


REPLACE_IDENTIFIER = "[name]"

with open("./Input/Names/invited_names.txt") as receiver:
    names = receiver.readlines()
    with open("./Input/Letters/starting_letter.txt") as letter:
        content = letter.read()
        for mail_name in names:
            name = mail_name.strip()
            new_content = content.replace(REPLACE_IDENTIFIER, name)
            with open(f"./Output/ReadyToSend/letter_for_{name}.txt", mode="w") as output:
                output.write(new_content)

# Note - Check the path before run this code, it uses the relative path to read and write the file.


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

# Importção de Módulos - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
import sys

# Implementação de Funções - - - - - - - - - - - - - - - - - - - - - - - - - -#
def main(args):
    """
Uma loja de discos anota diariamente durante o mês de Março a quantidade de
discos vendidos. Determinar em que dia desse mês ocorreu a maior venda e qual
foi a quantidade de discos vendida nesse dia.
    """

    max_dias_marco = 5
    max_quantidade = 0

    for dia in range(1, max_dias_marco +1):
        quantidade = int(input())

        if quantidade > max_quantidade:
            max_quantidade = quantidade
            max_dia = dia

    print("dia: ", max_dia, "\t quantidade: ", max_quantidade)

    return 0

# Script Principal - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -#
return_code = 0

if __name__ == "__main__":
    return_code = main(sys.argv)

sys.exit(return_code)


# Node class
class Node:

	# Function to initialize the node object
	def __init__(self, data):
		self.data = data # Assign data
		self.next = None # Initialize next as null

# Linked List class

class LinkedList:

	# Function to initialize the Linked List object
	def __init__(self):
		self.head = None

	def append(self, data):
		new_node = Node(data)
		if not self.head:
			self.head = new_node
		else:
			current = self.head
			while current.next:
				current=current.next
			current.next = new_node

	def display(self):
		current = self.head
		while current:
			print(current.data, end = ' ')
			current = current.next
		print()

	# Iterative Approach to finding an element in the Linked list
	def search(self, x) -> bool:
		current = self.head
		while current: # Iterate over the list
			if current.data == x:
				return True
			current = current.next
		return False

	# Recursive Approach to finding an element in the Linked List
	def search_cur(self, curr_node ,key) -> bool:
		if not curr_node:
			return False

		if curr_node.data == key:
			return True

		return self.search_cur(curr_node.next, key)

# Example usage:
slist = LinkedList()
slist.append(1)
slist.append(2)
slist.append(3)
slist.append(4)

print("The current Singly Linked List is:")
slist.display()  # Output: 1 2 3 4

inlist = slist.search(3)
if inlist:
	print('There is 3 in the Linked List')
else:
	print('There is not the corresponding value 3 in the list')

inlist = slist.search_cur(slist.head, 5)
if inlist:
	print('There is 5 in the Linked List')
else:
	print('There is not the corresponding value 5 in the list')


#!/usr/bin/python3
from flask import Flask, render_template, request, url_for, redirect
from werkzeug.utils import secure_filename
from werkzeug.datastructures import  FileStorage
import os
from generator import Generator
from encoder import Encoder
from led import Led
from font import Font
app = Flask(__name__, template_folder='./html')

uploads_dir = os.path.join('.', 'frames')
os.makedirs(uploads_dir, exist_ok=True)

num_lights = 100
num_colors = 250
screen_height = 5
DEFAULT_FPS = 60
DEFAULT_NUM_FRAMES = 250

board = Led(num_lights, num_colors)
board.run()

def make_tree(path):
    tree = dict(name=path, children=[])
    try: lst = os.listdir(path)
    except OSError:
        pass #ignore errors
    else:
        for name in lst:
            fn = os.path.join(path, name)
            if os.path.isdir(fn):
                tree['children'].append(make_tree(fn))
            else:
                tree['children'].append(dict(name=fn))
    return tree

@app.route('/upload')
def upload_file():
   return render_template('upload.html')

@app.route('/uploader', methods = ['GET', 'POST'])
def upload_file_route():
	if request.method == 'POST':
		f = request.files['file']
		f.save(os.path.join(uploads_dir, secure_filename(f.filename)))
		return 'file uploaded successfully'
	elif request.method == 'GET':
		return render_template('upload.html')

def get_uploaded_files():
	children = make_tree(uploads_dir)["children"]
	files = []
	for child in children:
		files.append(child["name"].split('/')[-1])
	return files

@app.route('/play', methods = ['GET', 'POST'])
def play():
	files = get_uploaded_files()
	if request.method == 'POST':
		f = request.form["file"]
		fps = int(request.form["fps"])
		with open(os.path.join(uploads_dir, f), "rb") as frames:
			board.put(frames.read(), fps)
		return render_template('play.html', data=files)
	elif request.method == 'GET':
		if "func" in request.args:
			play_generated_frames(parse_args(request.args), request.args['func'])
		return render_template('play.html', data=files)

@app.route('/text', methods = ['GET', 'POST'])
def text():
	if request.method == 'POST':
		font = Font(screen_height, int(num_lights / screen_height))
		e = Encoder(num_lights, num_colors)
		board.put(e.encode(font.text_to_frames(request.form['text'])))
		return render_template('text.html')
	else:
		return render_template('text.html')

def play_generated_frames(args, func):
	g = Generator(num_lights, num_colors, screen_height)
	e = Encoder(num_lights, num_colors)

	frames = None
	if func == "rainbow":
		frames = g.generate_rainbow_frames(args['num_frames'])
	elif func == "flow":
		frames = g.generate_flow_frames(args['num_frames'])
	elif func == "dot":
		frames = g.generate_dot_frames(args['num_frames'])
	elif func == "radiate":
		frames = g.generate_radiate_frames(args['num_frames'])
	elif func == "cascade":
		frames = g.generate_cascade_frames()
	elif func == "snake":
		frames = g.generate_snake_frames()
	elif func == "game_of_life":
		frames = g.generate_game_of_life_frames(args['num_frames'])
	elif func == "dance":
		frames = g.generate_dance_frames(args['num_frames'])
	elif func == "merge" or func == "quick" or func == "comb":
		frames = g.generate_sort_frames(func)

	if frames is not None:
		board.put(e.encode(frames), args['fps'])

def parse_args(args):
	fps = DEFAULT_FPS
	num_frames = DEFAULT_NUM_FRAMES
	if 'fps' in args:
		try:
			fps = int(args['fps'])
		except ValueError:
			pass
	if 'num_frames' in args:
		try:
			num_frames = int(args['num_frames'])
		except ValueError:
			pass

	return {'fps': fps, 'num_frames': num_frames}

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


# 'GUI to get the input files and tracking dir for the on the fly burst version
#  Expected files are:
#  1. Root of the folder that shall be tracked (the one in which the .ht3 files will be inserted) /path/target_folder
#  2. A file containing the settings (excel?!) *.csv vs. *.xls
#  3. First correction file *.hhd
#  4. Second correction file *.hhd'

# PyQT imports
from PyQt5.QtWidgets import QGridLayout,  QApplication
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtWidgets import *
import sys
#from OnTheFlyBurst import OTF_Burst

class File_DD_Dialog(QDialog):

    def __init__(self, parent=None):

        super(File_DD_Dialog, self).__init__(parent)
        self.setWindowTitle("Drag and Drop")
        self.setAcceptDrops(True)
        self.grid = QGridLayout()
        self.resize(250,200)

        # total 4 elements for 200 pixels -> boxes y coords -> 1. 1-50, 2. 51-101, 3. 102-152

        # build fields
        self.file1Widget = QLineEdit(self)
        self.file1Widget.setPlaceholderText('Select tracking folder')
        self.grid.addWidget(self.file1Widget,0,0)

        # build fields
        self.file2Widget = QLineEdit(self)
        self.file2Widget.setPlaceholderText('Select settings file')
        self.grid.addWidget(self.file2Widget,1,0)

        # build fields
        self.file3Widget = QLineEdit(self)
        self.file3Widget.setPlaceholderText('Select first hhd file')
        self.grid.addWidget(self.file3Widget,2,0)

        # build fiels
        self.file4Widget = QLineEdit(self)
        self.file4Widget.setPlaceholderText('Select second hhd file ')
        self.grid.addWidget(self.file4Widget,3,0)

        # Close with selection button
        self.select_button = QPushButton("OK")
        self.select_button.clicked.connect(self.ok_button_pressed)
        self.labelResult = QLabel()
        self.grid.addWidget(self.select_button, 10, 0, 1,2)

        # Buttons for file dialogs

        # First define a Folder Icon
        pixmapi = getattr(QStyle, 'SP_DirOpenIcon')
        icon = self.style().standardIcon(pixmapi)

        # File 1 Button
        self.file1Button = QPushButton('')
        self.file1Button.setIcon(icon)
        self.file1Button.clicked.connect(self.folder_button1_select)
        self.grid.addWidget(self.file1Button, 0,1)

        # File 2 Button
        self.file2Button = QPushButton('')
        self.file2Button.setIcon(icon)
        self.file2Button.clicked.connect(self.folder_button2_select)
        self.grid.addWidget(self.file2Button, 1,1)

        # File 3 Button
        self.file3Button = QPushButton('')
        self.file3Button.setIcon(icon)
        self.file3Button.clicked.connect(self.folder_button3_select)
        self.grid.addWidget(self.file3Button, 2,1)

        # file 4 Button
        self.file4Button = QPushButton('')
        self.file4Button.setIcon(icon)
        self.file4Button.clicked.connect(self.folder_button4_select)
        self.grid.addWidget(self.file4Button, 3,1)

        self.setLayout(self.grid)

    def folder_button1_select(self):
        self.first_file = QtWidgets.QFileDialog.getExistingDirectory(
            self, "Select Folder")
        try: # if nothing selected the empty var won't have an index and the program will return (same below)
            self.file1Widget.setText(self.first_file)
        except IndexError:
            return

    def folder_button2_select(self):
        self.first_file = QtWidgets.QFileDialog.getOpenFileNames(
            self, "Select File", "", "*.csv;;*.xls")
        try:
            self.file2Widget.setText(self.first_file[0][0])
        except IndexError:
            return

    def folder_button3_select(self):
        self.first_file = QtWidgets.QFileDialog.getOpenFileNames(
            self, "Select File", "", "*.hhd")
        try:
            self.file3Widget.setText(self.first_file[0][0])
        except IndexError:
            return

    def folder_button4_select(self):
        self.first_file = QtWidgets.QFileDialog.getOpenFileNames(
            self, "Select File", "", "*.hhd")
        try:
            self.file4Widget.setText(self.first_file[0][0])
        except IndexError:
            return

    def dragEnterEvent(self, event):
        if event.mimeData().hasUrls():
            event.accept()
        else:
            event.ignore()

    def dropEvent(self, event):

        # Define vertical coordinates for accepted boxes in a given file widget position
        # it does scale with the window size therefore coordinates as ranges are defined

        self.ycoord_range_pos1 = \
            [self.file1Widget.pos().y()+self.file1Widget.size().height()+10,
             self.file1Widget.pos().y()-self.file1Widget.size().height()-10]
        self.ycoord_range_pos2 = \
            [self.file2Widget.pos().y()+self.file2Widget.size().height()+10,
             self.file2Widget.pos().y()-self.file2Widget.size().height()-10]
        self.ycoord_range_pos3 = \
            [self.file3Widget.pos().y()+self.file3Widget.size().height()+10,
             self.file3Widget.pos().y()-self.file3Widget.size().height()-10]
        self.ycoord_range_pos4 = \
            [self.file4Widget.pos().y() + self.file4Widget.size().height() + 10,
             self.file4Widget.pos().y() - self.file4Widget.size().height() - 10]

        files = [u.toLocalFile() for u in event.mimeData().urls()]

        if event.pos().y() <= self.ycoord_range_pos1[0] and event.pos().y() > self.ycoord_range_pos1[1]:
            if files[0].endswith('.ht3'): # only if correct ht3 file type
                self.file1Widget.setText(files[0])
        elif event.pos().y() <= self.ycoord_range_pos2[0] and event.pos().y() >=  self.ycoord_range_pos2[1]:
            if files[0].endswith('.hhd'): # only if correct hhd file type
                self.file2Widget.setText(files[0])
        elif event.pos().y() <= self.ycoord_range_pos3[0] and event.pos().y() >= self.ycoord_range_pos3[1]:
            if files[0].endswith('.hhd'): # only if correct hhd file type
                self.file3Widget.setText(files[0])
        elif event.pos().y() <= self.ycoord_range_pos4[0] and event.pos().y() >= self.ycoord_range_pos4[1]:
            if files[0].endswith('.hhd'):  # only if correct hhd file type
                self.file4Widget.setText(files[0])

    def ok_button_pressed(self):
        # Store the selected file path's in a dictionary
        self.file_dir_dict = dict()
        self.file_dir_dict['Tracker'] = self.file1Widget.text()
        self.file_dir_dict['Settings'] = self.file2Widget.text()
        self.file_dir_dict['HHD1'] = self.file3Widget.text()
        self.file_dir_dict['HHD2'] = self.file4Widget.text()

        self.close()

if __name__ == '__main__':

    app = QApplication(sys.argv)
    app.setApplicationDisplayName("OTF_pyBat")
    #app.setWindowIcon(QtGui.QIcon('requirements/batIcon.png'))
    w = File_DD_Dialog()
    w.setWindowTitle('OTF-Bat')
    w.show()
    sys.exit(app.exec_())