import operator

import cupy
from cupy._core import internal
from cupy._core._scalar import get_typename

from cupyx.scipy.sparse import csr_matrix

import numpy as np

TYPES = ['double', 'thrust::complex<double>']
INT_TYPES = ['int', 'long long']

INTERVAL_KERNEL = r'''
#include <cupy/complex.cuh>
extern "C" {
__global__ void find_interval(
        const double* t, const double* x, long long* out,
        int k, int n, bool extrapolate, int total_x) {

    int idx = blockDim.x * blockIdx.x + threadIdx.x;
    if(idx >= total_x) {
        return;
    }

    double xp = *&x[idx];
    double tb = *&t[k];
    double te = *&t[n];

    if(isnan(xp)) {
        out[idx] = -1;
        return;
    }

    if((xp < tb || xp > te) && !extrapolate) {
        out[idx] = -1;
        return;
    }

    int left = k;
    int right = n;
    int mid;
    bool found = false;

    while(left < right && !found) {
        mid = ((right + left) / 2);
        if(xp > *&t[mid]) {
            left = mid + 1;
        } else if (xp < *&t[mid]) {
            right = mid - 1;
        } else {
            found = true;
        }
    }

    int default_value = left - 1 < k ? k : left - 1;
    int result = found ? mid + 1 : default_value + 1;

    while(xp >= *&t[result] && result != n) {
        result++;
    }

    out[idx] = result - 1;
}
}
'''

INTERVAL_MODULE = cupy.RawModule(
    code=INTERVAL_KERNEL, options=('-std=c++11',),)
#    name_expressions=[f'find_interval<{type_name}>' for type_name in TYPES])

D_BOOR_KERNEL = r'''
#include <cupy/complex.cuh>
#include <cupy/math_constants.h>
#define COMPUTE_LINEAR 0x1

template<typename T>
__global__ void d_boor(
        const double* t, const T* c, const int k, const int mu,
        const double* x, const long long* intervals, T* out,
        double* temp, int num_c, int mode, int num_x) {

    int idx = blockDim.x * blockIdx.x + threadIdx.x;

    if(idx >= num_x) {
        return;
    }

    double xp = *&x[idx];
    long long interval = *&intervals[idx];

    double* h = temp + idx * (2 * k + 1);
    double* hh = h + k + 1;

    int ind, j, n;
    double xa, xb, w;

    if(mode == COMPUTE_LINEAR && interval < 0) {
        for(j = 0; j < num_c; j++) {
            out[num_c * idx + j] = CUDART_NAN;
        }
        return;
    }

    /*
     * Perform k-m "standard" deBoor iterations
     * so that h contains the k+1 non-zero values of beta_{ell,k-m}(x)
     * needed to calculate the remaining derivatives.
     */
    h[0] = 1.0;
    for (j = 1; j <= k - mu; j++) {
        for(int p = 0; p < j; p++) {
            hh[p] = h[p];
        }
        h[0] = 0.0;
        for (n = 1; n <= j; n++) {
            ind = interval + n;
            xb = t[ind];
            xa = t[ind - j];
            if (xb == xa) {
                h[n] = 0.0;
                continue;
            }
            w = hh[n - 1]/(xb - xa);
            h[n - 1] += w*(xb - xp);
            h[n] = w*(xp - xa);
        }
    }

    /*
     * Now do m "derivative" recursions
     * to convert the values of beta into the mth derivative
     */
    for (j = k - mu + 1; j <= k; j++) {
        for(int p = 0; p < j; p++) {
            hh[p] = h[p];
        }
        h[0] = 0.0;
        for (n = 1; n <= j; n++) {
            ind = interval + n;
            xb = t[ind];
            xa = t[ind - j];
            if (xb == xa) {
                h[mu] = 0.0;
                continue;
            }
            w = ((double) j) * hh[n - 1]/(xb - xa);
            h[n - 1] -= w;
            h[n] = w;
        }
    }

    if(mode != COMPUTE_LINEAR) {
        return;
    }

    // Compute linear combinations
    for(j = 0; j < num_c; j++) {
        out[num_c * idx + j] = 0;
        for(n = 0; n < k + 1; n++) {
            out[num_c * idx + j] = (
                out[num_c * idx + j] +
                c[(interval + n - k) * num_c + j] * ((T) h[n]));
        }
    }

}
'''

D_BOOR_MODULE = cupy.RawModule(
    code=D_BOOR_KERNEL, options=('-std=c++11',),
    name_expressions=[f'd_boor<{type_name}>' for type_name in TYPES])

DESIGN_MAT_KERNEL = r'''
#include <cupy/complex.cuh>

template<typename U>
__global__ void compute_design_matrix(
        const int k, const long long* intervals, double* bspline_basis,
        double* data, U* indices, int num_intervals) {

    int idx = blockDim.x * blockIdx.x + threadIdx.x;
    if(idx >= num_intervals) {
        return;
    }

    long long interval = *&intervals[idx];

    double* work = bspline_basis + idx * (2 * k + 1);

    for(int j = 0; j <= k; j++) {
        int m = (k + 1) * idx + j;
        data[m] = work[j];
        indices[m] = (U) (interval - k + j);
    }
}
'''

DESIGN_MAT_MODULE = cupy.RawModule(
    code=DESIGN_MAT_KERNEL, options=('-std=c++11',),
    name_expressions=[f'compute_design_matrix<{itype}>'
                      for itype in INT_TYPES])

def _get_module_func(module, func_name, *template_args):
    def _get_typename(dtype):
        typename = get_typename(dtype)
        if dtype.kind == 'c':
            typename = 'thrust::' + typename
        return typename
    args_dtypes = [_get_typename(arg.dtype) for arg in template_args]
    template = ', '.join(args_dtypes)
    kernel_name = f'{func_name}<{template}>' if template_args else func_name
    kernel = module.get_function(kernel_name)
    return kernel

def _get_dtype(dtype):
    """Return np.complex128 for complex dtypes, np.float64 otherwise."""
    if cupy.issubdtype(dtype, cupy.complexfloating):
        return cupy.complex_
    else:
        return cupy.float_

def _as_float_array(x, check_finite=False):
    """Convert the input into a C contiguous float array.
    NB: Upcasts half- and single-precision floats to double precision.
    """
    x = cupy.ascontiguousarray(x)
    dtyp = _get_dtype(x.dtype)
    x = x.astype(dtyp, copy=False)
    if check_finite and not cupy.isfinite(x).all():
        raise ValueError("Array must not contain infs or nans.")
    return x

def _evaluate_spline(t, c, k, xp, nu, extrapolate, out):
    """
    Evaluate a spline in the B-spline basis.

    Parameters
    ----------
    t : ndarray, shape (n+k+1)
        knots
    c : ndarray, shape (n, m)
        B-spline coefficients
    xp : ndarray, shape (s,)
        Points to evaluate the spline at.
    nu : int
        Order of derivative to evaluate.
    extrapolate : int, optional
        Whether to extrapolate to ouf-of-bounds points, or to return NaNs.
    out : ndarray, shape (s, m)
        Computed values of the spline at each of the input points.
        This argument is modified in-place.
    """
    n = t.shape[0] - k - 1
    intervals = cupy.empty_like(xp, dtype=cupy.int64)

    # Compute intervals for each value
    interval_kernel = _get_module_func(INTERVAL_MODULE, 'find_interval')
    interval_kernel(((xp.shape[0] + 128 - 1) // 128,), (128,),
                    (t, xp, intervals, k, n, extrapolate, xp.shape[0]))

    # Compute interpolation
    num_c = int(np.prod(c.shape[1:]))
    temp = cupy.empty(xp.shape[0] * (2 * k + 1))
    d_boor_kernel = _get_module_func(D_BOOR_MODULE, 'd_boor', c)
    d_boor_kernel(((xp.shape[0] + 128 - 1) // 128,), (128,),
                  (t, c, k, nu, xp, intervals, out, temp, num_c, 1,
                   xp.shape[0]))

def _make_design_matrix(x, t, k, extrapolate, indices):
    """
    Returns a design matrix in CSR format.
    Note that only indices is passed, but not indptr because indptr is already
    precomputed in the calling Python function design_matrix.

    Parameters
    ----------
    x : array_like, shape (n,)
        Points to evaluate the spline at.
    t : array_like, shape (nt,)
        Sorted 1D array of knots.
    k : int
        B-spline degree.
    extrapolate : bool, optional
        Whether to extrapolate to ouf-of-bounds points.
    indices : ndarray, shape (n * (k + 1),)
        Preallocated indices of the final CSR array.
    Returns
    -------
    data
        The data array of a CSR array of the b-spline design matrix.
        In each row all the basis elements are evaluated at the certain point
        (first row - x[0], ..., last row - x[-1]).

    indices
        The indices array of a CSR array of the b-spline design matrix.
    """
    n = t.shape[0] - k - 1
    intervals = cupy.empty_like(x, dtype=cupy.int64)

    # Compute intervals for each value
    interval_kernel = _get_module_func(INTERVAL_MODULE, 'find_interval')
    interval_kernel(((x.shape[0] + 128 - 1) // 128,), (128,),
                    (t, x, intervals, k, n, extrapolate, x.shape[0]))

    # Compute interpolation
    bspline_basis = cupy.empty(x.shape[0] * (2 * k + 1))
    d_boor_kernel = _get_module_func(D_BOOR_MODULE, 'd_boor', x)
    d_boor_kernel(((x.shape[0] + 128 - 1) // 128,), (128,),
                  (t, None, k, 0, x, intervals, None, bspline_basis, 0, 0,
                   x.shape[0]))

    data = cupy.zeros(x.shape[0] * (k + 1), dtype=cupy.float_)
    design_mat_kernel = _get_module_func(
        DESIGN_MAT_MODULE, 'compute_design_matrix', indices)
    design_mat_kernel(((x.shape[0] + 128 - 1) // 128,), (128,),
                      (k, intervals, bspline_basis, data, indices,
                       x.shape[0]))

    return data, indices

def splder(tck, n=1):
    """
    Compute the spline representation of the derivative of a given spline

    Parameters
    ----------
    tck : tuple of (t, c, k)
        Spline whose derivative to compute
    n : int, optional
        Order of derivative to evaluate. Default: 1

    Returns
    -------
    tck_der : tuple of (t2, c2, k2)
        Spline of order k2=k-n representing the derivative
        of the input spline.

    Notes
    -----
    .. seealso:: :class:`scipy.interpolate.splder`

    See Also
    --------
    splantider, splev, spalde
    """
    if n < 0:
        return splantider(tck, -n)

    t, c, k = tck

    if n > k:
        raise ValueError(("Order of derivative (n = %r) must be <= "
                          "order of spline (k = %r)") % (n, tck[2]))

    # Extra axes for the trailing dims of the `c` array:
    sh = (slice(None),) + ((None,)*len(c.shape[1:]))

    try:
        for j in range(n):
            # See e.g. Schumaker, Spline Functions: Basic Theory, Chapter 5

            # Compute the denominator in the differentiation formula.
            # (and append traling dims, if necessary)
            dt = t[k+1:-1] - t[1:-k-1]
            dt = dt[sh]
            # Compute the new coefficients
            c = (c[1:-1-k] - c[:-2-k]) * k / dt
            # Pad coefficient array to same size as knots (FITPACK
            # convention)
            c = cupy.r_[c, np.zeros((k,) + c.shape[1:])]
            # Adjust knots
            t = t[1:-1]
            k -= 1
    except FloatingPointError as e:
        raise ValueError(("The spline has internal repeated knots "
                          "and is not differentiable %d times") % n) from e

    return t, c, k

def splantider(tck, n=1):
    """
    Compute the spline for the antiderivative (integral) of a given spline.

    Parameters
    ----------
    tck : tuple of (t, c, k)
        Spline whose antiderivative to compute
    n : int, optional
        Order of antiderivative to evaluate. Default: 1

    Returns
    -------
    tck_ader : tuple of (t2, c2, k2)
        Spline of order k2=k+n representing the antiderivative of the input
        spline.

    See Also
    --------
    splder, splev, spalde

    Notes
    -----
    The `splder` function is the inverse operation of this function.
    Namely, ``splder(splantider(tck))`` is identical to `tck`, modulo
    rounding error.

    .. seealso:: :class:`scipy.interpolate.splantider`
    """
    if n < 0:
        return splder(tck, -n)

    t, c, k = tck

    # Extra axes for the trailing dims of the `c` array:
    sh = (slice(None),) + (None,)*len(c.shape[1:])

    for j in range(n):
        # This is the inverse set of operations to splder.

        # Compute the multiplier in the antiderivative formula.
        dt = t[k+1:] - t[:-k-1]
        dt = dt[sh]
        # Compute the new coefficients
        c = cupy.cumsum(c[:-k-1] * dt, axis=0) / (k + 1)
        c = cupy.r_[cupy.zeros((1,) + c.shape[1:]),
                    c, [c[-1]] * (k+2)]
        # New knots
        t = cupy.r_[t[0], t, t[-1]]
        k += 1

    return t, c, k

class BSpline:
    r"""Univariate spline in the B-spline basis.

    .. math::
        S(x) = \sum_{j=0}^{n-1} c_j  B_{j, k; t}(x)

    where :math:`B_{j, k; t}` are B-spline basis functions of degree `k`
    and knots `t`.

    Parameters
    ----------
    t : ndarray, shape (n+k+1,)
        knots
    c : ndarray, shape (>=n, ...)
        spline coefficients
    k : int
        B-spline degree
    extrapolate : bool or 'periodic', optional
        whether to extrapolate beyond the base interval, ``t[k] .. t[n]``,
        or to return nans.
        If True, extrapolates the first and last polynomial pieces of b-spline
        functions active on the base interval.
        If 'periodic', periodic extrapolation is used.
        Default is True.
    axis : int, optional
        Interpolation axis. Default is zero.

    Attributes
    ----------
    t : ndarray
        knot vector
    c : ndarray
        spline coefficients
    k : int
        spline degree
    extrapolate : bool
        If True, extrapolates the first and last polynomial pieces of b-spline
        functions active on the base interval.
    axis : int
        Interpolation axis.
    tck : tuple
        A read-only equivalent of ``(self.t, self.c, self.k)``

    Notes
    -----
    B-spline basis elements are defined via

    .. math::
        B_{i, 0}(x) = 1, \textrm{if $t_i \le x < t_{i+1}$, otherwise $0$,}

        B_{i, k}(x) = \frac{x - t_i}{t_{i+k} - t_i} B_{i, k-1}(x)
                 + \frac{t_{i+k+1} - x}{t_{i+k+1} - t_{i+1}} B_{i+1, k-1}(x)

    **Implementation details**

    - At least ``k+1`` coefficients are required for a spline of degree `k`,
      so that ``n >= k+1``. Additional coefficients, ``c[j]`` with
      ``j > n``, are ignored.

    - B-spline basis elements of degree `k` form a partition of unity on the
      *base interval*, ``t[k] <= x <= t[n]``.

    - Based on [1]_ and [2]_

    .. seealso:: :class:`scipy.interpolate.BSpline`

    References
    ----------
    .. [1] Tom Lyche and Knut Morken, Spline methods,
        http://www.uio.no/studier/emner/matnat/ifi/INF-MAT5340/v05/undervisningsmateriale/
    .. [2] Carl de Boor, A practical guide to splines, Springer, 2001.
    """

    def __init__(self, t, c, k, extrapolate=True, axis=0):
        self.k = operator.index(k)
        self.c = cupy.asarray(c)
        self.t = cupy.ascontiguousarray(t, dtype=cupy.float64)

        if extrapolate == 'periodic':
            self.extrapolate = extrapolate
        else:
            self.extrapolate = bool(extrapolate)

        n = self.t.shape[0] - self.k - 1

        axis = internal._normalize_axis_index(axis, self.c.ndim)

        # Note that the normalized axis is stored in the object.
        self.axis = axis
        if axis != 0:
            # roll the interpolation axis to be the first one in self.c
            # More specifically, the target shape for self.c is (n, ...),
            # and axis !=0 means that we have c.shape (..., n, ...)
            #                                               ^
            #                                              axis
            self.c = cupy.moveaxis(self.c, axis, 0)

        if k < 0:
            raise ValueError("Spline order cannot be negative.")
        if self.t.ndim != 1:
            raise ValueError("Knot vector must be one-dimensional.")
        if n < self.k + 1:
            raise ValueError("Need at least %d knots for degree %d" %
                             (2*k + 2, k))
        if (cupy.diff(self.t) < 0).any():
            raise ValueError("Knots must be in a non-decreasing order.")
        if len(cupy.unique(self.t[k:n+1])) < 2:
            raise ValueError("Need at least two internal knots.")
        if not cupy.isfinite(self.t).all():
            raise ValueError("Knots should not have nans or infs.")
        if self.c.ndim < 1:
            raise ValueError("Coefficients must be at least 1-dimensional.")
        if self.c.shape[0] < n:
            raise ValueError(
                "Knots, coefficients and degree are inconsistent.")

        dt = _get_dtype(self.c.dtype)
        self.c = cupy.ascontiguousarray(self.c, dtype=dt)

    @classmethod
    def construct_fast(cls, t, c, k, extrapolate=True, axis=0):
        """Construct a spline without making checks.
        Accepts same parameters as the regular constructor. Input arrays
        `t` and `c` must of correct shape and dtype.
        """
        self = object.__new__(cls)
        self.t, self.c, self.k = t, c, k
        self.extrapolate = extrapolate
        self.axis = axis
        return self

    @property
    def tck(self):
        """Equivalent to ``(self.t, self.c, self.k)`` (read-only).
        """
        return self.t, self.c, self.k

    @classmethod
    def basis_element(cls, t, extrapolate=True):
        """Return a B-spline basis element ``B(x | t[0], ..., t[k+1])``.

        Parameters
        ----------
        t : ndarray, shape (k+2,)
            internal knots
        extrapolate : bool or 'periodic', optional
            whether to extrapolate beyond the base interval,
            ``t[0] .. t[k+1]``, or to return nans.
            If 'periodic', periodic extrapolation is used.
            Default is True.

        Returns
        -------
        basis_element : callable
            A callable representing a B-spline basis element for the knot
            vector `t`.

        Notes
        -----
        The degree of the B-spline, `k`, is inferred from the length of `t` as
        ``len(t)-2``. The knot vector is constructed by appending and
        prepending ``k+1`` elements to internal knots `t`.

        .. seealso:: :class:`scipy.interpolate.BSpline`
        """
        k = len(t) - 2
        t = _as_float_array(t)
        t = cupy.r_[(t[0]-1,) * k, t, (t[-1]+1,) * k]
        c = cupy.zeros_like(t)
        c[k] = 1.
        return cls.construct_fast(t, c, k, extrapolate)

    @classmethod
    def design_matrix(cls, x, t, k, extrapolate=False):
        """
        Returns a design matrix as a CSR format sparse array.

        Parameters
        ----------
        x : array_like, shape (n,)
            Points to evaluate the spline at.
        t : array_like, shape (nt,)
            Sorted 1D array of knots.
        k : int
            B-spline degree.
        extrapolate : bool or 'periodic', optional
            Whether to extrapolate based on the first and last intervals
            or raise an error. If 'periodic', periodic extrapolation is used.
            Default is False.

        Returns
        -------
        design_matrix : `csr_matrix` object
            Sparse matrix in CSR format where each row contains all the basis
            elements of the input row (first row = basis elements of x[0],
            ..., last row = basis elements x[-1]).

        Notes
        -----
        In each row of the design matrix all the basis elements are evaluated
        at the certain point (first row - x[0], ..., last row - x[-1]).
        `nt` is a length of the vector of knots: as far as there are
        `nt - k - 1` basis elements, `nt` should be not less than `2 * k + 2`
        to have at least `k + 1` basis element.

        Out of bounds `x` raises a ValueError.

        .. note::
            This method returns a `csr_matrix` instance as CuPy still does not
            have `csr_array`.

        .. seealso:: :class:`scipy.interpolate.BSpline`
        """
        x = _as_float_array(x, True)
        t = _as_float_array(t, True)

        if extrapolate != 'periodic':
            extrapolate = bool(extrapolate)

        if k < 0:
            raise ValueError("Spline order cannot be negative.")
        if t.ndim != 1 or np.any(t[1:] < t[:-1]):
            raise ValueError(f"Expect t to be a 1-D sorted array_like, but "
                             f"got t={t}.")
        # There are `nt - k - 1` basis elements in a BSpline built on the
        # vector of knots with length `nt`, so to have at least `k + 1` basis
        # elements we need to have at least `2 * k + 2` elements in the vector
        # of knots.
        if len(t) < 2 * k + 2:
            raise ValueError(f"Length t is not enough for k={k}.")

        if extrapolate == 'periodic':
            # With periodic extrapolation we map x to the segment
            # [t[k], t[n]].
            n = t.size - k - 1
            x = t[k] + (x - t[k]) % (t[n] - t[k])
            extrapolate = False
        elif not extrapolate and (
            (min(x) < t[k]) or (max(x) > t[t.shape[0] - k - 1])
        ):
            # Checks from `find_interval` function
            raise ValueError(f'Out of bounds w/ x = {x}.')

        # Compute number of non-zeros of final CSR array in order to determine
        # the dtype of indices and indptr of the CSR array.
        n = x.shape[0]
        nnz = n * (k + 1)
        if nnz < cupy.iinfo(cupy.int32).max:
            int_dtype = cupy.int32
        else:
            int_dtype = cupy.int64
        # Preallocate indptr and indices
        indices = cupy.empty(n * (k + 1), dtype=int_dtype)
        indptr = cupy.arange(0, (n + 1) * (k + 1), k + 1, dtype=int_dtype)

        # indptr is not passed to CUDA as it is already fully computed
        data, indices = _make_design_matrix(
            x, t, k, extrapolate, indices
        )
        return csr_matrix(
            (data, indices, indptr),
            shape=(x.shape[0], t.shape[0] - k - 1)
        )

    def __call__(self, x, nu=0, extrapolate=None):
        """
        Evaluate a spline function.

        Parameters
        ----------
        x : array_like
            points to evaluate the spline at.
        nu : int, optional
            derivative to evaluate (default is 0).
        extrapolate : bool or 'periodic', optional
            whether to extrapolate based on the first and last intervals
            or return nans. If 'periodic', periodic extrapolation is used.
            Default is `self.extrapolate`.

        Returns
        -------
        y : array_like
            Shape is determined by replacing the interpolation axis
            in the coefficient array with the shape of `x`.
        """
        if extrapolate is None:
            extrapolate = self.extrapolate

        x = cupy.asarray(x)
        x_shape, x_ndim = x.shape, x.ndim
        x = cupy.ascontiguousarray(cupy.ravel(x), dtype=cupy.float_)

        # With periodic extrapolation we map x to the segment
        # [self.t[k], self.t[n]].
        if extrapolate == 'periodic':
            n = self.t.size - self.k - 1
            x = self.t[self.k] + (x - self.t[self.k]) % (self.t[n] -
                                                         self.t[self.k])
            extrapolate = False

        out = cupy.empty(
            (len(x), int(np.prod(self.c.shape[1:]))), dtype=self.c.dtype)

        self._evaluate(x, nu, extrapolate, out)
        out = out.reshape(x_shape + self.c.shape[1:])
        if self.axis != 0:
            # transpose to move the calculated values to the interpolation axis
            dim_order = list(range(out.ndim))
            dim_order = (
                dim_order[x_ndim:x_ndim+self.axis] +
                dim_order[:x_ndim] +
                dim_order[x_ndim+self.axis:])
            out = out.transpose(dim_order)

        return out

    def _ensure_c_contiguous(self):
        if not self.t.flags.c_contiguous:
            self.t = self.t.copy()
        if not self.c.flags.c_contiguous:
            self.c = self.c.copy()

    def _evaluate(self, xp, nu, extrapolate, out):
        _evaluate_spline(self.t, self.c.reshape(self.c.shape[0], -1),
                         self.k, xp, nu, extrapolate, out)

    def derivative(self, nu=1):
        """
        Return a B-spline representing the derivative.

        Parameters
        ----------
        nu : int, optional
            Derivative order.
            Default is 1.

        Returns
        -------
        b : BSpline object
            A new instance representing the derivative.

        See Also
        --------
        splder, splantider
        """
        c = self.c
        # pad the c array if needed
        ct = len(self.t) - len(c)
        if ct > 0:
            c = cupy.r_[c, cupy.zeros((ct,) + c.shape[1:])]
        tck = splder((self.t, c, self.k), nu)
        return self.construct_fast(*tck, extrapolate=self.extrapolate,
                                   axis=self.axis)

    def antiderivative(self, nu=1):
        """
        Return a B-spline representing the antiderivative.

        Parameters
        ----------
        nu : int, optional
            Antiderivative order. Default is 1.

        Returns
        -------
        b : BSpline object
            A new instance representing the antiderivative.

        Notes
        -----
        If antiderivative is computed and ``self.extrapolate='periodic'``,
        it will be set to False for the returned instance. This is done because
        the antiderivative is no longer periodic and its correct evaluation
        outside of the initially given x interval is difficult.

        See Also
        --------
        splder, splantider
        """
        c = self.c
        # pad the c array if needed
        ct = len(self.t) - len(c)
        if ct > 0:
            c = cupy.r_[c, cupy.zeros((ct,) + c.shape[1:])]
        tck = splantider((self.t, c, self.k), nu)

        if self.extrapolate == 'periodic':
            extrapolate = False
        else:
            extrapolate = self.extrapolate

        return self.construct_fast(*tck, extrapolate=extrapolate,
                                   axis=self.axis)

    def integrate(self, a, b, extrapolate=None):
        """
        Compute a definite integral of the spline.

        Parameters
        ----------
        a : float
            Lower limit of integration.
        b : float
            Upper limit of integration.
        extrapolate : bool or 'periodic', optional
            whether to extrapolate beyond the base interval,
            ``t[k] .. t[-k-1]``, or take the spline to be zero outside of the
            base interval. If 'periodic', periodic extrapolation is used.
            If None (default), use `self.extrapolate`.

        Returns
        -------
        I : array_like
            Definite integral of the spline over the interval ``[a, b]``.
        """
        if extrapolate is None:
            extrapolate = self.extrapolate

        # Prepare self.t and self.c.
        self._ensure_c_contiguous()

        # Swap integration bounds if needed.
        sign = 1
        if b < a:
            a, b = b, a
            sign = -1
        n = self.t.size - self.k - 1

        if extrapolate != "periodic" and not extrapolate:
            # Shrink the integration interval, if needed.
            a = max(a, self.t[self.k].item())
            b = min(b, self.t[n].item())

            # if self.c.ndim == 1:
            #     # Fast path: use FITPACK's routine
            #     # (cf _fitpack_impl.splint).
            #     integral = splint(a, b, self.tck)
            #     return integral * sign

        out = cupy.empty(
            (2, int(np.prod(self.c.shape[1:]))), dtype=self.c.dtype)

        # Compute the antiderivative.
        c = self.c
        ct = len(self.t) - len(c)
        if ct > 0:
            c = cupy.r_[c, cupy.zeros((ct,) + c.shape[1:])]
        ta, ca, ka = splantider((self.t, c, self.k), 1)

        if extrapolate == 'periodic':
            # Split the integral into the part over period (can be several
            # of them) and the remaining part.

            ts, te = self.t[self.k], self.t[n]
            period = te - ts
            interval = b - a
            n_periods, left = divmod(interval, period)

            if n_periods > 0:
                # Evaluate the difference of antiderivatives.
                x = cupy.asarray([ts, te], dtype=cupy.float_)
                _evaluate_spline(ta, ca.reshape(ca.shape[0], -1),
                                 ka, x, 0, False, out)
                integral = out[1] - out[0]
                integral *= n_periods
            else:
                integral = cupy.zeros((1, int(np.prod(self.c.shape[1:]))),
                                      dtype=self.c.dtype)

            # Map a to [ts, te], b is always a + left.
            a = ts + (a - ts) % period
            b = a + left

            # If b <= te then we need to integrate over [a, b], otherwise
            # over [a, te] and from xs to what is remained.
            if b <= te:
                x = cupy.asarray([a, b], dtype=cupy.float_)
                _evaluate_spline(ta, ca.reshape(ca.shape[0], -1),
                                 ka, x, 0, False, out)
                integral += out[1] - out[0]
            else:
                x = cupy.asarray([a, te], dtype=cupy.float_)
                _evaluate_spline(ta, ca.reshape(ca.shape[0], -1),
                                 ka, x, 0, False, out)
                integral += out[1] - out[0]

                x = cupy.asarray([ts, ts + b - te], dtype=cupy.float_)
                _evaluate_spline(ta, ca.reshape(ca.shape[0], -1),
                                 ka, x, 0, False, out)
                integral += out[1] - out[0]
        else:
            # Evaluate the difference of antiderivatives.
            x = cupy.asarray([a, b], dtype=cupy.float_)
            _evaluate_spline(ta, ca.reshape(ca.shape[0], -1),
                             ka, x, 0, extrapolate, out)
            integral = out[1] - out[0]

        integral *= sign
        return integral.reshape(ca.shape[1:])


from flask import Flask, app
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy

# init SQLAlchemy so we can use it later in our models
db = SQLAlchemy()

def create_app():
    application = Flask(__name__)

    application.config['SECRET_KEY'] = '9OLWxND4o83j4K4iuopO'
    application.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite'
    application.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False

    db.init_app(application)

    login_manager = LoginManager()
    login_manager.login_view = 'auth.login'
    login_manager.init_app(application)

    from .models import User

    # blueprint for auth routes in our app
    from . import auth
    application.register_blueprint(auth.bp)

    @login_manager.user_loader
    def load_user(id):
        # since the user_id is just the primary key of our user table, use it in the query for the user
        return User.query.get(id)

    return application


from django.db import IntegrityError
from django.utils.timezone import make_aware

from datetime import datetime

import logging

from .utils import get_extras

class DatabaseHandler(logging.Handler):
    """
    A log handler to store logs into the database.

    Currently, only log entries that belong to an event are stored in the database.
    All other log entries are available in the log files / via syslog.
    """

    def __init__(self, *args, **kwargs):
        self._logentry_model = None

        super(DatabaseHandler, self).__init__(*args, **kwargs)

    def emit(self, record):
        # the handler is initialized by django before the database setup, so the import would fail
        # therefore, we do it here dynamically when necessary - but only once
        if not self._logentry_model:
            from .models import LogEntry

            self._logentry_model = LogEntry

        # get the event, helper and user if they are stored in the entry
        event = record.event if hasattr(record, "event") else None
        if not event:
            return

        helper = record.helper if hasattr(record, "helper") else None
        user = record.user if hasattr(record, "user") else None

        # create the entry
        entry = self._logentry_model(
            timestamp=make_aware(datetime.fromtimestamp(record.created)),
            level=record.levelname,
            message=record.getMessage(),
            event=event,
            helper=helper,
            user=user,
            extras=get_extras(record),
            module=record.name,
        )

        try:
            entry.save()
        except ValueError:
            # if the event is deleted, we cannot save. we only store logs for existing events,
            # so we can discard this event (deletions are still logged via syslog / in files if container is used)
            pass
        except IntegrityError:
            # if a helper is deleted, the helper object is still there while we prepare the entry.
            # on save, the helper may already be deleted, so we have a foreign key error.
            entry.helper = None
            entry.save()


"""
Support module for PyWikipediaBot regression tests.
"""
__version__ = '$Id: 7895f03ac2688d7155e5e94da60e51af65ee9b11 $'

import sys

# Add current directory and parent directory to module search path.
sys.path.insert(0, '..')
sys.path.insert(0, '.')

del sys


from rubrix.server.apis.v0.models.commons.model import BulkResponse
from rubrix.server.apis.v0.models.text2text import (
    Text2TextBulkRequest,
    Text2TextRecordInputs,
    Text2TextSearchResults,
)

def test_search_records(mocked_client):
    dataset = "test_search_records"
    delete_dataset(dataset, mocked_client)

    records = [
        Text2TextRecordInputs.parse_obj(data)
        for data in [
            {
                "id": 0,
                "text": "This is a text data",
                "metadata": {
                    "field_one": "value one",
                },
                "prediction": {
                    "agent": "test",
                    "sentences": [{"text": "This is a test data", "score": 0.6}],
                },
            },
            {
                "id": 1,
                "text": "Ånother data",
            },
        ]
    ]
    response = mocked_client.post(
        f"/api/datasets/{dataset}/Text2Text:bulk",
        json=Text2TextBulkRequest(
            tags={"env": "test", "class": "text classification"},
            metadata={"config": {"the": "config"}},
            records=records,
        ).dict(by_alias=True),
    )

    assert response.status_code == 200, response.json()
    bulk_response = BulkResponse.parse_obj(response.json())
    assert bulk_response.dataset == dataset
    assert bulk_response.failed == 0
    assert bulk_response.processed == 2

    response = mocked_client.post(f"/api/datasets/{dataset}/Text2Text:search", json={})
    assert response.status_code == 200, response.json()
    results = Text2TextSearchResults.parse_obj(response.json())
    assert results.total == 2
    assert results.records[0].predicted is None

    assert results.aggregations.dict(exclude={"score"}) == {
        "annotated_as": {},
        "annotated_by": {},
        "annotated_text": {},
        "metadata": {"field_one": {"value one": 1}},
        "predicted": {},
        "predicted_as": {},
        "predicted_by": {"test": 1},
        "predicted_text": {},
        "status": {"Default": 2},
        "words": {"data": 2, "ånother": 1},
    }

def test_api_with_new_predictions_data_model(mocked_client):
    dataset = "test_api_with_new_predictions_data_model"
    delete_dataset(dataset, mocked_client)

    records = [
        Text2TextRecordInputs.parse_obj(
            {
                "text": "This is a text data",
                "predictions": {
                    "test": {
                        "sentences": [{"text": "This is a test data", "score": 0.6}]
                    },
                },
            }
        ),
        Text2TextRecordInputs.parse_obj(
            {
                "text": "Another data",
                "annotations": {
                    "annotator-1": {"sentences": [{"text": "THis is a test data"}]},
                    "annotator-2": {"sentences": [{"text": "This IS the test datay"}]},
                },
            }
        ),
    ]

    response = mocked_client.post(
        f"/api/datasets/{dataset}/Text2Text:bulk",
        json=Text2TextBulkRequest(
            records=records,
        ).dict(by_alias=True),
    )

    assert response.status_code == 200, response.json()
    bulk_response = BulkResponse.parse_obj(response.json())
    assert bulk_response.dataset == dataset
    assert bulk_response.failed == 0
    assert bulk_response.processed == 2

    response = mocked_client.post(
        f"/api/datasets/{dataset}/Text2Text:search",
        json={"query": {"query_text": "predictions.test.sentences.text.exact:data"}},
    )

    assert response.status_code == 200, response.json()
    results = Text2TextSearchResults.parse_obj(response.json())
    assert results.total == 1, results

    response = mocked_client.post(
        f"/api/datasets/{dataset}/Text2Text:search",
        json={"query": {"query_text": "_exists_:annotations.annotator-1"}},
    )

    assert response.status_code == 200, response.json()
    results = Text2TextSearchResults.parse_obj(response.json())
    assert results.total == 1, results

def delete_dataset(dataset, mocked_client):
    assert mocked_client.delete(f"/api/datasets/{dataset}").status_code == 200


import IECore
import IECoreScene

import Gaffer
import GafferScene
import GafferImage

# Add standard cycles AOVs

with IECore.IgnoredExceptions( ImportError ) :

	# If cycles isn't available for any reason, this will fail
	# and we won't add any unnecessary output definitions.
	import GafferCycles

	lightPasses = [
		"emission",
		"background",
		"ao",
		"shadow",
		"diffuse_direct",
		"diffuse_indirect",
		"glossy_direct",
		"glossy_indirect",
		"transmission",
		"transmission_direct",
		"transmission_indirect",
		"volume_direct",
		"volume_indirect",
		"lightgroup",
	]

	dataPasses = [
		"depth",
		"position",
		"normal",
		"roughness",
		"uv",
		"object_id",
		"material_id",
		"motion",
		"motion_weight",
		"render_time",
		"cryptomatte_asset",
		"cryptomatte_object",
		"cryptomatte_material",
		"aov_color",
		"aov_value",
		"adaptive_aux_buffer",
		"sample_count",
		"diffuse_color",
		"glossy_color",
		"transmission_color",
		"mist",
		"denoising_normal",
		"denoising_albedo",

		"shadow_catcher",
		"shadow_catcher_sample_count",
		"shadow_catcher_matte",

		"bake_primitive",
		"bake_differential",
	]

	def __registerOutputs( aovs, halfFloat = False, denoise = False ) :
		for aov in aovs :

			label = aov.replace( "_", " " ).title().replace( " ", "_" )

			data = aov

			interactiveOutput = {
				"driverType" : "ClientDisplayDriver",
				"displayHost" : "localhost",
				"displayPort" : "${image:catalogue:port}",
				"remoteDisplayType" : "GafferImage::GafferDisplayDriver",
				"quantize" : IECore.IntVectorData( [ 0, 0, 0, 0 ] ),
			}
			batchOutput = {
				"quantize" : IECore.IntVectorData( [ 0, 0, 0, 0 ] ),
				"halfFloat" : halfFloat
			}

			if data == "lightgroup":
				if not GafferCycles.withLightGroups :
					continue
				data = "lg lightgroup"
				label = "Light_Group"

			if data == "aov_color" :
				data = "aovc aov_color"

			if data == "aov_value" :
				data = "aovv aov_value"

			if data.startswith( "cryptomatte" ) :
				data = data.replace( "_", " " )

			GafferScene.Outputs.registerOutput(
				"Interactive/Cycles/" + label,
				IECoreScene.Output(
					aov,
					"ieDisplay",
					data,
					interactiveOutput
				)
			)

			GafferScene.Outputs.registerOutput(
				"Batch/Cycles/" + label,
				IECoreScene.Output(
					"${project:rootDirectory}/renders/${script:name}/%s/%s.####.exr" % ( aov, aov ),
					"exr",
					data,
					batchOutput
				)
			)

			if denoise:
				interactiveOutput["denoise"] = True
				batchOutput["denoise"] = True

				# Denoised variants
				GafferScene.Outputs.registerOutput(
					"Interactive/Cycles/" + label + "_Denoised",
					IECoreScene.Output(
						aov + "_denoised",
						"ieDisplay",
						data,
						interactiveOutput
					)
				)

				GafferScene.Outputs.registerOutput(
					"Batch/Cycles/" + label + "_Denoised",
					IECoreScene.Output(
						"${project:rootDirectory}/renders/${script:name}/%s/%s_denoised.####.exr" % ( aov, aov ),
						"exr",
						data,
						batchOutput
					)
				)

	GafferScene.Outputs.registerOutput(
		"Interactive/Cycles/Beauty_Denoised",
		IECoreScene.Output(
			"beauty_denoised",
			"ieDisplay",
			"rgba",
			{
				"driverType" : "ClientDisplayDriver",
				"displayHost" : "localhost",
				"displayPort" : "${image:catalogue:port}",
				"remoteDisplayType" : "GafferImage::GafferDisplayDriver",
				"quantize" : IECore.IntVectorData( [ 0, 0, 0, 0 ] ),
				"denoise" : True
			}
		)
	)

	GafferScene.Outputs.registerOutput(
		"Batch/Cycles/Beauty_Denoised",
		IECoreScene.Output(
			"${project:rootDirectory}/renders/${script:name}/beauty/beauty_denoised.####.exr",
			"exr",
			"rgba",
			{
				"quantize" : IECore.IntVectorData( [ 0, 0, 0, 0 ] ),
				"denoise" : True,
				"halfFloat" : True
			}
		)
	)

	__registerOutputs( lightPasses, True )
	__registerOutputs( dataPasses )


import pprint
import threading
from typing import Dict, TYPE_CHECKING
from PySide2.QtWidgets import QTabWidget, QTextBrowser, QWidget

from lib.comm import get_var, set_var
from widgets import PMTableView, PMGTableWidget, PMDockObject, PMGTableViewer, PMGJsonTree

if TYPE_CHECKING:
    from lib.extensions.extensionlib.extension_lib import extension_lib

class AbstractViewer(object):
    """
    抽象视图
    """

    @staticmethod
    def is_valid(data) -> bool:
        """
        判断data是否为合法的变量类型
        """
        return True

    def set_data(self, data: object, metadata: dict):
        """
        设置其显示数据的值为data，显示的元数据为meadata。
        """
        pass

class PDDataViewer(PMGTableViewer, AbstractViewer):
    """
    显示Pandas数据的视图
    """

    def __init__(self, parent=None):
        PMGTableViewer.__init__(self, parent, table_view=PMTableView())
        AbstractViewer.__init__(self)
        # self.action_split_by_columns:QAction = self.table_view.menu.addAction('提取当前列')
        # self.action_split_by_columns.triggered.connect(self.split_by_columns)

    def split_by_columns(self):
        # row =
        # self.table_view.data
        print('splitted!')

    @staticmethod
    def is_valid(data):
        import pandas as pd
        return isinstance(data, pd.DataFrame)

    def set_data(self, data: object, metadata: dict = None):
        super().set_data(data)

class NPDataViewer(PMGTableViewer, AbstractViewer):
    """
    显示numpy.ndarray的视图
    """

    def __init__(self, parent=None):
        PMGTableViewer.__init__(self, parent, table_view=PMTableView())
        AbstractViewer.__init__(self)

    @staticmethod
    def is_valid(data):
        import numpy
        return isinstance(data, numpy.ndarray)

    def set_data(self, data: object, metadata: dict = None):
        super(NPDataViewer, self).set_data(data)

class JsonViewer(PMGJsonTree, AbstractViewer):
    """
    树状图，专门显示dict型数据。
    """

    def __init__(self, parent=None):
        PMGJsonTree.__init__(self, parent)
        # AbstractViewer.__init__(self)

    @staticmethod
    def is_valid(data) -> bool:
        return isinstance(data, dict)

    def set_data(self, data: Dict[str, object], metadata: dict = None) -> None:
        self.set_data_dic({self.tr('Data:'): data})
        self.expandToDepth(1)

class GeneralIterableViewer(PMGTableWidget, AbstractViewer):
    """
    显示可迭代对象的视图
    这个变量可以为列表、每行长度不等的二维嵌套列表等。
    解析方式为先从第一个可迭代维度上解析，取出元素，也就是data[0],data[1]。。。data[len(data)-1]，逐行显示。
    如果元素不可迭代，那么就填在对应行的第一列；如果元素可迭代的，那么就把元素依次填写在同一行各个列中。
    data[0][1],data[0][2]....
    """

    def __init__(self, parent=None):
        PMGTableWidget.__init__(self, parent)
        AbstractViewer.__init__(self)

    @staticmethod
    def is_valid(data: object):
        import numpy
        import pandas
        if isinstance(data, numpy.ndarray) or isinstance(
                data, pandas.DataFrame):
            return False
        return PMGTableWidget.check_data_can_be_displayed_by_table(data=data)

    def set_data(self, data: 'np.ndarray', metadata: dict = None):
        super().set_data_2d(data)

class GeneralObjectViewer(QTextBrowser, AbstractViewer):
    """
    一个文本显示控件
    专门显示metadata。
    """

    def __init__(self, parent=None):
        QTextBrowser.__init__(self, parent)
        AbstractViewer.__init__(self)

    @staticmethod
    def is_valid(data: object):
        import numpy
        import pandas
        if isinstance(data, numpy.ndarray) or isinstance(
                data, pandas.DataFrame):
            return False
        elif GeneralIterableViewer.is_valid(data):
            return False
        return True

    def set_data(self, data: object, metadata: dict = None):
        self.setText(self.tr('value:') + '\n\n   ' + pprint.pformat(data)
                     + '\n\n\n' + self.tr('meta data:') + '\n\n' + pprint.pformat(metadata))

viewer_classes = [
    PDDataViewer,
    NPDataViewer,
    GeneralIterableViewer,
    JsonViewer,
    GeneralObjectViewer]

def build_viewer(data: object, metadata: object) -> 'QWidget':
    """
    创建变量视图的工厂函数。
    """
    for viewer_class in viewer_classes:
        if viewer_class.is_valid(data):
            viewer = viewer_class()
            viewer.set_data(data, metadata)

            return viewer

def get_viewer_class(data):
    for viewer_class in viewer_classes:
        if viewer_class.is_valid(data):
            return viewer_class

class PMVariableViewerWidget(QTabWidget, PMDockObject):
    """
    在这里采用了多继承的方式。注意，一定要把PMDockObject写在右边。
    """
    if TYPE_CHECKING:
        lib = extension_lib

    def __init__(self, parent=None):
        super().__init__(parent)
        self.setTabsClosable(True)
        self.var_view_tables: Dict[str, object] = {}
        self.tabCloseRequested.connect(self.on_tab_close_request)

        self.variable_view_factory = None

    def is_temporary(self) -> bool:
        return True

    def get_widget_text(self) -> str:
        return self.tr('Variable Viewer')

    def set_lib(self, lib):
        '''
        设置回调函数。注意，只有主线程中才能刷新界面，否则将引起崩溃。
        :param varname:
        :param variable:
        :return:
        '''
        self.lib = lib

        def on_changed(varname: str, variable, source: str):
            if threading.current_thread() is threading.main_thread():
                if varname in self.var_view_tables:
                    self.show_data(varname, raise_window=False)

        def on_deletion(varname: str, provider: str):
            if threading.current_thread() is threading.main_thread():
                if varname in self.var_view_tables:
                    tab = self.var_view_tables.pop(varname)
                    index = self.indexOf(tab)
                    self.removeTab(index)

        self.lib.Data.add_data_changed_callback(on_changed)
        self.lib.Data.add_data_deleted_callback(on_deletion)

    def show_data(self, dataname: str, raise_window=True):
        """
        显示数据，显示数据之后，使得上层控件将其提升到上层可见。特别适用于几个dockwidget叠在一起的情况。
        如果与已有的数据不是同一种类型，就移除原先的，重建新的。
        :param dataname:
        :return:
        """
        from lib.comm.base import DataDesc
        desc: DataDesc = self.lib.Data.get_data_desc(dataname)
        if desc.big:
            data = get_var(dataname, preview=True)
        else:
            data = get_var(dataname)

        try:
            dataview: 'QWidget' = self.var_view_tables.get(dataname)
            metadata = self.lib.Data.get_metadata(dataname)
        except BaseException:
            import traceback
            traceback.print_exc()
            return
        last_index = self.count()
        if dataview is not None:
            if not isinstance(dataview, get_viewer_class(data)):
                index = self.indexOf(dataview)
                self.removeTab(index)
                last_index = index
                self.var_view_tables.pop(dataname)
                dataview = None

        if dataview is None:
            dataview = build_viewer(data, metadata)
            self.insertTab(last_index, dataview, dataname)
            self.addTab(dataview, dataname)
            self.var_view_tables[dataname] = dataview
        dataview.set_data(data, metadata)

        if hasattr(dataview, 'data_modified_signal'):
            def set_var_data_modified():
                set_var(dataname, dataview.get_data())

            dataview.data_modified_signal.connect(set_var_data_modified)

        dataview.setWindowTitle(dataname)
        dataview.windowTitleChanged.connect(self.on_tab_window_title_changed)

        self.setCurrentWidget(dataview)
        if raise_window:
            self.lib.UI.raise_dock_into_view('data_view_table')

    def on_tab_window_title_changed(self, title: str):
        widget = self.sender()
        self.setTabText(self.indexOf(widget), title)

    def on_tab_close_request(self, close_index: int):
        self.var_view_tables.pop(self.tabText(close_index))

        tab_to_close: 'QTextBrowser' = self.widget(close_index)
        tab_to_close.deleteLater()

        self.removeTab(close_index)


"""
Created on Sat Sep 25 00:00:00 2018

@author: Nikhil
"""

"""
    If you have any questions or suggestions regarding this script,
    feel free to contact me via nikhil.ss4795@gmail.com
"""

# Polynomial Regression

# Importing the libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd

# Importing the dataset
dataset = pd.read_csv('Position_Salaries.csv')
X = dataset.iloc[:, 1:2].values
y = dataset.iloc[:, 2].values

plt.scatter(X, y, color = 'red')
plt.title('Salary vs Experience')
plt.xlabel('Position level')
plt.ylabel('Salary')
plt.show()

# Fitting Linear Regression to the dataset
from sklearn.linear_model import LinearRegression
linear_reg = LinearRegression()
linear_reg.fit(X, y)

# Visualising the Linear Regression results
plt.scatter(X, y, color = 'red')
plt.plot(X, linear_reg.predict(X), color = 'blue')
plt.title('Salary vs Experience')
plt.xlabel('Experience')
plt.ylabel('Salary')
plt.show()

# Fitting Polynomial Regression to the dataset
from sklearn.preprocessing import PolynomialFeatures
poly_features = PolynomialFeatures(degree = 4)
X_polynomial = poly_features.fit_transform(X)
poly_features.fit(X_polynomial, y)

polynomial_regression = LinearRegression()
polynomial_regression.fit(X_polynomial, y)

# Visualising the Polynomial Regression results
plt.scatter(X, y, color = 'red')
plt.plot(X, polynomial_regression.predict(poly_features.fit_transform(X)), color = 'blue')
plt.title('Salary vs Experience')
plt.xlabel('Experience')
plt.ylabel('Salary')
plt.show()

# Predicting a new result with Linear Regression
linear_reg.predict(6.5)

# Predicting a new result with Polynomial Regression
polynomial_regression.predict(poly_features.fit_transform(6.5))

"""
    If you have any questions or suggestions regarding this script,
    feel free to contact me via nikhil.ss4795@gmail.com
"""


import datetime

from django import forms
from django.core.exceptions import ValidationError

from .models import TimeLog, Subject, Tag

class DateForm(forms.Form):
    def __init__(self, *args, **kwargs):
        self.min_date = kwargs.pop('min_date')
        self.max_date = kwargs.pop('max_date')
        # if user has no record (min_date and max_date is None) then disable the date inputs else set the min and max attrs
        if self.min_date and self.max_date:
            self.base_fields['start'].widget.attrs['min'] = self.min_date.isoformat()
            self.base_fields['start'].widget.attrs['max'] = self.max_date.isoformat()
            self.base_fields['end'].widget.attrs['min'] = self.min_date.isoformat()
            self.base_fields['end'].widget.attrs['max'] = self.max_date.isoformat()
        else:
            self.base_fields['start'].widget.attrs['disabled'] = True
            self.base_fields['end'].widget.attrs['disabled'] = True

        super().__init__(*args, **kwargs)

    start = forms.DateField(label='From')
    end = forms.DateField(label='To')

    def clean(self):
        # if min_date or max_date is none, it means that user has no record yet
        if self.min_date is None:
            raise ValidationError("You don't have any record!")

        cleaned_data = super().clean()
        start = cleaned_data.get('start')
        end = cleaned_data.get('end')
        if start and end:
            if start > end:
                raise ValidationError('Your selected start date is greater then selected end date')
            if not (self.min_date <= start <= self.max_date and self.min_date <= end <= self.max_date):
                raise ValidationError(f'Your records date are between {self.min_date} and {self.max_date}')
        return cleaned_data

class TimeLogForm(forms.ModelForm):
    hours = forms.IntegerField(min_value=0, max_value=24)
    minutes = forms.IntegerField(min_value=0, max_value=59)

    def __init__(self, *args, **kwargs):
        self.registrant_user = kwargs.pop('registrant_user', None)
        super().__init__(*args, **kwargs)
        # add registrant user's subjects and tags to the corresponding field choices
        self.fields['subject'].queryset = self.registrant_user.subject_set.all()
        self.fields['tags'].queryset = self.registrant_user.tag_set.all()
        # add html attribute to the widget of fields
        self.fields['subject'].widget.attrs['class'] = 'form-select'
        self.fields['tags'].widget.attrs['class'] = 'form-select'
        self.fields['tags'].widget.attrs['size'] = '3'
        self.fields['date'].widget.attrs['class'] = 'form-control'
        self.fields['hours'].widget.attrs['class'] = 'form-control'
        self.fields['minutes'].widget.attrs['class'] = 'form-control'
        self.fields['description'].widget.attrs['class'] = 'form-control'

    class Meta:
        model = TimeLog
        exclude = ['user', 'duration']
        widgets = {
            'date': forms.DateInput(attrs={'type': 'date', 'max': datetime.date.today}),
        }

    def clean(self):
        clean_data = super().clean()
        hours = clean_data.get('hours')
        minutes = clean_data.get('minutes')
        date = clean_data.get('date')

        # calculate and check if the duration is valid
        if hours is not None and minutes is not None and date:
            # calculate duration minutes based on hours and minutes
            duration = (hours * 60) + minutes
            if duration == 0:
                raise ValidationError("Both hour and minute fields can not be 0.")

            if duration > 1440:
                raise ValidationError("One day is 24 hours!")

            # check the particular date's durations doesn't exceed 24 hours
            previous_durations_total = 0
            for timelog in self.registrant_user.timelogs.filter(date=date):
                previous_durations_total += timelog.duration

            if (previous_durations_total + duration) > 1440:
                remaind_hours = (1440 - previous_durations_total) // 60
                remaind_miuntes = (1440 - previous_durations_total) % 60

                if remaind_miuntes or remaind_hours:
                    raise ValidationError(f'Your remaind duration for '
                                          f'{date} is {remaind_hours} hours and {remaind_miuntes} minutes.')
                else:
                    raise ValidationError(f'There is no time left for {date}')

            clean_data['duration'] = duration

        return clean_data

class SubjectForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.user_subjects = kwargs.pop('user_subjects')
        super().__init__(*args, **kwargs)
        # add bootstarp class to the fields
        for v in self.visible_fields():
            v.field.widget.attrs['class'] = 'form-control'
    class Meta:
        model = Subject
        fields = ['name', 'description']

    def clean(self):
        clean_data = super().clean()
        name = clean_data.get('name')
        if name:
            if name.lower() in [s.name for s in self.user_subjects]:
                raise ValidationError(f'{name.lower()} already exists.')
            clean_data['name'] = name.lower()

        return clean_data

class TagForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        self.user_tags = kwargs.pop('user_tags')
        super().__init__(*args, **kwargs)
        # add bootstarp class to the name field
        self.fields['name'].widget.attrs['class'] = 'form-control'

    class Meta:
        model = Tag
        fields = ['name']

    def clean(self):
        clean_data = super().clean()
        name = clean_data.get('name')
        if name:
            if name.lower() in [s.name for s in self.user_tags]:
                raise ValidationError(f'{name.lower()} already exists.')
            clean_data['name'] = name.lower()

        return clean_data


import argparse
import os
import shutil
import numpy as np
import torch
import torchvision
from torch import nn as nn
from torch.utils.tensorboard import SummaryWriter
from torch.utils.data import Dataset, DataLoader
from tqdm import tqdm

import helpers
from dcgan import generators, discriminators
from dcgan.train_config import TrainConfig

def train(dataset: Dataset, train_config: TrainConfig,
          generator, discriminator):

    global_step = epoch = 0

    if train_config.overwrite and os.path.exists(train_config.experiment_dirpath):
        shutil.rmtree(train_config.experiment_dirpath)

    real_images_writer = SummaryWriter(f"{train_config.experiment_dirpath}/real")
    fake_images_writer = SummaryWriter(f"{train_config.experiment_dirpath}/fake")
    stats_writer = SummaryWriter(f"{train_config.experiment_dirpath}/stats")

    dataloader = DataLoader(
        dataset=dataset, batch_size=train_config.batch_size, shuffle=True, num_workers=train_config.num_workers
    )
    num_iterations_per_epoch = len(dataset) // train_config.batch_size

    generator = generator.to(device=train_config.device).train()
    discriminator = discriminator.to(device=train_config.device).train()

    criterion = torch.nn.BCELoss()
    gen_opt = torch.optim.Adam(params=generator.parameters(), lr=train_config.lr, betas=(0.5, 0.999))
    disc_opt = torch.optim.Adam(params=discriminator.parameters(), lr=train_config.lr, betas=(0.5, 0.999))

    while True:
        for batch_idx, (real_img_batch, labels) in tqdm(enumerate(dataloader), total=num_iterations_per_epoch, leave=False):
            img_batch = normalize(real_img_batch)
            if train_config.conditional_dim > 0:
                conditional_input = helpers.conditional_input_encoder_discriminator(
                    labels=labels, cardinality=train_config.conditional_dim, spatial_size=train_config.image_size
                )
                img_batch = torch.cat([img_batch, conditional_input], dim=1)
            img_batch = img_batch.to(device=train_config.device)

            # train discriminator
            noise = torch.randn(size=(len(labels), train_config.z_dim))
            if train_config.conditional_dim > 0:
                conditional_input = helpers.conditional_input_encoder_generator(
                    labels=labels, cardinality=train_config.conditional_dim
                )
                noise = torch.cat([noise, conditional_input], dim=1)
            noise = noise.to(device=train_config.device)
            fake_img_batch = generator(noise)
            if train_config.conditional_dim > 0:
                conditional_input = helpers.conditional_input_encoder_discriminator(
                    labels=labels, cardinality=train_config.conditional_dim, spatial_size=train_config.image_size
                ).to(device=train_config.device)
                fake_img_batch = torch.cat([fake_img_batch, conditional_input], dim=1)

            real_proba = discriminator(img_batch)
            fake_proba = discriminator(fake_img_batch.detach())

            disc_loss = (criterion(real_proba, torch.ones_like(real_proba)) +
                         criterion(fake_proba, torch.zeros_like(fake_proba)))
            disc_loss = disc_loss / 2

            disc_opt.zero_grad()
            disc_loss.backward()
            disc_opt.step()

            # train generator
            fake_proba = discriminator(fake_img_batch)
            gen_loss = criterion(fake_proba, torch.ones_like(fake_proba))

            gen_opt.zero_grad()
            gen_loss.backward()
            gen_opt.step()

            if global_step % train_config.send_every == 0:
                stats_writer.add_scalar("generator loss", gen_loss, global_step=global_step)
                stats_writer.add_scalar("discriminator loss", disc_loss, global_step=global_step)
                stats_writer.add_scalar("total loss", gen_loss + disc_loss, global_step=global_step)

            if global_step % train_config.show_every == 0:
                # visualize
                real_images_grid = torchvision.utils.make_grid(
                    real_img_batch, normalize=True
                )
                real_images_writer.add_image("real images", real_images_grid, global_step=epoch)

                generated_images = generate(train_config=train_config, generator=generator)
                generated_images = torchvision.utils.make_grid(
                    generated_images, normalize=True
                )
                fake_images_writer.add_image("fake images", generated_images, global_step=global_step)
            global_step += 1
        epoch += 1

def normalize(x):
    return 2 * x - 1

def generate(train_config: TrainConfig, generator: nn.Module) -> torch.Tensor:
    noise = torch.randn(train_config.batch_size, train_config.z_dim)
    if train_config.conditional_dim > 0:
        label = np.random.randint(low=0, high=train_config.conditional_dim)
        labels = np.asarray([label] * train_config.batch_size)
        labels = torch.from_numpy(labels)
        conditional_input = helpers.conditional_input_encoder_generator(
            labels=labels, cardinality=train_config.conditional_dim
        )
        noise = torch.cat([noise, conditional_input], dim=1)
    noise = noise.to(device=train_config.device)
    with torch.no_grad():
        generated_images = generator(noise).view(train_config.batch_size, -1, train_config.image_size, train_config.image_size)
    return generated_images

def get_dataset_and_in_channels(dataset_name: str, image_size: int):
    name_to_dataset_cls = {
        "mnist": (torchvision.datasets.MNIST, 1),
        "cifar-10": (torchvision.datasets.CIFAR10, 3)
    }
    dataset_cls, in_channels = name_to_dataset_cls[dataset_name]
    dataset = dataset_cls(
        root="data/", train=True,
        transform=torchvision.transforms.Compose([
            torchvision.transforms.Resize(image_size),
            torchvision.transforms.ToTensor()
        ]),
        download=True
    )
    return dataset, in_channels

def parse_args():
    parser = argparse.ArgumentParser()
    parser.add_argument("--exp_name")
    parser.add_argument("--dataset", choices=["mnist", "cifar-10"],  default="mnist")
    parser.add_argument("--image_size", type=int, default=32)
    return parser.parse_args()

def main():
    args = parse_args()

    exp_dir = "../experiments"
    if args.exp_name is not None:
        exp_dir = f"{exp_dir}/{args.exp_name}"

    dataset, in_channels = get_dataset_and_in_channels(dataset_name=args.dataset, image_size=args.image_size)

    config = TrainConfig(
        experiment_dirpath=exp_dir,
        image_size=args.image_size,
        in_channels=in_channels
    )

    generator = generators.DCGenerator.from_train_config(config)
    discriminator = discriminators.DCDiscriminator.from_train_config(config)

    train(
        dataset=dataset, train_config=config, generator=generator, discriminator=discriminator
    )

if __name__ == '__main__':
    main()


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import argparse
import os
from vgg import load_pretrained_VGG16_pool5
import cifar10_utils
import tensorflow as tf
import numpy as np

LEARNING_RATE_DEFAULT = 1e-4
BATCH_SIZE_DEFAULT = 128
MAX_STEPS_DEFAULT = 15000
EVAL_FREQ_DEFAULT = 1000
CHECKPOINT_FREQ_DEFAULT = 5000
PRINT_FREQ_DEFAULT = 10
OPTIMIZER_DEFAULT = 'ADAM'
REFINE_AFTER_K_STEPS_DEFAULT = 0

DATA_DIR_DEFAULT = './cifar10/cifar-10-batches-py'
LOG_DIR_DEFAULT = './logs/cifar10'
CHECKPOINT_DIR_DEFAULT = './checkpoints'

def train_step(loss):
    """
    Defines the ops to conduct an optimization step. You can set a learning
    rate scheduler or pick your favorite optimizer here. This set of operations
    should be applicable to both ConvNet() and Siamese() objects.

    Args:
        loss: scalar float Tensor, full loss = cross_entropy + reg_loss

    Returns:
        train_op: Ops for optimization.
    """
    ########################
    # PUT YOUR CODE HERE  #
    ########################
    train_op = tf.train.AdamOptimizer(FLAGS.learning_rate).minimize(loss)
    ########################
    # END OF YOUR CODE    #
    ########################

    return train_op

def fully_connected_layers(vgg_output):
    # dense layers
    with tf.name_scope('dense'):
        flat = tf.reshape(vgg_output, [vgg_output.get_shape()[0].value, -1], name='flat_out')

        xavier = tf.contrib.layers.xavier_initializer()
        const0 = tf.constant_initializer(0.)
        l2_reg = tf.contrib.layers.l2_regularizer(0.1)
        n_classes = 10

        with tf.name_scope('dense1'):
            w1 = tf.get_variable('w1', shape=[flat.get_shape()[1], 384], dtype=tf.float32,
                                 initializer=xavier, regularizer=l2_reg)
            b1 = tf.get_variable('b1', shape=[384], dtype=tf.float32,
                                 initializer=const0)
            fc1 = tf.nn.relu(tf.matmul(flat, w1) + b1, name='d1_out')

        # fc2	        Multiplication	[384, 192]
        #               ReLU
        with tf.name_scope('dense2'):
            w2 = tf.get_variable('w2', shape=[384, 192], dtype=tf.float32,
                                 initializer=xavier, regularizer=l2_reg)
            b2 = tf.get_variable('b2', shape=[192], dtype=tf.float32,
                                 initializer=const0)
            fc2 = tf.nn.relu(tf.matmul(fc1, w2) + b2, name='d2_out')

        # fc3	        Multiplication	[192, 10]
        with tf.name_scope('dense3'):
            w3 = tf.get_variable('w3', shape=[192, n_classes], dtype=tf.float32,
                                 initializer=xavier, regularizer=l2_reg)
            b3 = tf.get_variable('b3', shape=[n_classes], dtype=tf.float32,
                                 initializer=const0)
            fc3 = tf.matmul(fc2, w3) + b3
        return fc3

def vgg_loss(logits, labels):
    ce_loss = tf.nn.softmax_cross_entropy_with_logits(logits, labels)
    ce_loss = tf.reduce_mean(ce_loss)
    reg_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES)
    reg_loss = tf.to_float(0.)
    if None not in reg_losses:  # this IS meant to switch while building the graph
        reg_loss = reduce(lambda x, y: tf.add(x, y), reg_losses)
    loss = ce_loss + reg_loss
    tf.scalar_summary('ce_loss', ce_loss)
    tf.scalar_summary('reg_loss', reg_loss)
    tf.scalar_summary('full_loss', loss)

    return loss

def accuracy(logits, labels):
        guesses = tf.argmax(logits, dimension=1)
        targets = tf.argmax(labels, dimension=1)
        score = tf.to_int32(tf.equal(guesses, targets))
        acc = tf.reduce_sum(score) / tf.size(score)

        tf.scalar_summary('accuracy', acc)
        return acc

def train():
    """
    Performs training and evaluation of your model.

    First define your graph using vgg.py with your fully connected layer.
    Then define necessary operations such as trainer (train_step in this case),
    savers and summarizers. Finally, initialize your model within a
    tf.Session and do the training.

    ---------------------------------
    How often to evaluate your model:
    ---------------------------------
    - on training set every PRINT_FREQ iterations
    - on test set every EVAL_FREQ iterations

    ---------------------------
    How to evaluate your model:
    ---------------------------
    Evaluation on test set should be conducted over full batch, i.e. 10k images,
    while it is alright to do it over minibatch for train set.
    """

    # Set the random seeds for reproducibility. DO NOT CHANGE.
    tf.set_random_seed(42)
    np.random.seed(42)

    ########################
    # PUT YOUR CODE HERE  #
    ########################
    cifar10 = cifar10_utils.get_cifar10(FLAGS.data_dir)
    data_dims = list(cifar10.train.images.shape[1:])
    n_classes = 10
    with tf.Graph().as_default():
        x_pl = tf.placeholder(dtype=tf.float32, shape=[FLAGS.batch_size] + data_dims)
        y_pl = tf.placeholder(dtype=tf.float32, shape=[FLAGS.batch_size, n_classes])
        stopgrads = tf.placeholder(dtype=tf.bool)
        pool5, assign_ops = load_pretrained_VGG16_pool5(x_pl, scope_name='vgg')
        pool5 = tf.cond(stopgrads, lambda: tf.stop_gradient(pool5), lambda: pool5)
        logits = fully_connected_layers(pool5)
        loss = vgg_loss(logits, y_pl)
        acc = accuracy(logits, y_pl)

        train_op = train_step(loss)
        summary_op = tf.merge_all_summaries()
        init_op = tf.initialize_all_variables()

        with tf.Session() as sess:
            saver = tf.train.Saver()
            sess.run(init_op)
            sess.run(assign_ops)
            train_summary_writer = tf.train.SummaryWriter(FLAGS.log_dir + '/train', sess.graph)
            test_summary_writer = tf.train.SummaryWriter(FLAGS.log_dir + '/test', sess.graph)

            for step in range(FLAGS.max_steps):
                x, y = cifar10.train.next_batch(FLAGS.batch_size)
                switch = True if step < FLAGS.refine_after_k else False
                feed = {x_pl: x, y_pl: y, stopgrads: switch}
                train_loss, train_acc, summary_str, _ = sess.run([loss, acc, summary_op, train_op], feed_dict=feed)

                if step == 0 or (step + 1) % FLAGS.print_freq == 0 or step + 1 == FLAGS.max_steps:
                    print('TRAIN step: ', str(step), ' err: ', str(train_loss), ' acc: ', str(train_acc))
                    train_summary_writer.add_summary(summary_str, step)
                    train_summary_writer.flush()
                if step == 0 or (step + 1) % FLAGS.eval_freq == 0 or step + 1 == FLAGS.max_steps:
                    x, y = cifar10.test.images, cifar10.test.labels
                    num_batches = int(np.floor(x.shape[0] / FLAGS.batch_size))

                    test_err = 0.
                    test_acc = 0.
                    for idx in range(num_batches):

                        x_batch = x[idx * FLAGS.batch_size:(idx + 1) * FLAGS.batch_size, :, :, :]
                        y_batch = y[idx * FLAGS.batch_size:(idx + 1) * FLAGS.batch_size, :]
                        feed = {x_pl: x_batch, y_pl: y_batch, stopgrads: True}

                        batch_err, batch_acc = sess.run([loss, acc], feed_dict=feed)

                        test_err += batch_err
                        test_acc += batch_acc
                        summary_str = sess.run(summary_op, feed_dict=feed)  # possibly incorrect. should pool summaries
                        test_summary_writer.add_summary(summary_str, step)
                        test_summary_writer.flush()
                    test_err /= num_batches
                    test_acc /= num_batches
                    print('--- TEST --- step: ', str(step), ' err: ', str(train_loss), ' acc: ', str(train_acc))

                    # summary_str = sess.run(summary_op, feed_dict=feed)  # possibly incorrect. should pool summaries
                    # test_summary_writer.add_summary(summary_str, step)
                    # test_summary_writer.flush()
                if (step + 1) % FLAGS.checkpoint_freq == 0 or step + 1 == FLAGS.max_steps:
                    checkpoint_file = os.path.join(FLAGS.checkpoint_dir, 'ckpt')
                    saver.save(sess, checkpoint_file, global_step=(step + 1))

    ########################
    # END OF YOUR CODE    #
    ########################

def initialize_folders():
    """
    Initializes all folders in FLAGS variable.
    """

    if not tf.gfile.Exists(FLAGS.log_dir):
        tf.gfile.MakeDirs(FLAGS.log_dir)

    if not tf.gfile.Exists(FLAGS.data_dir):
        tf.gfile.MakeDirs(FLAGS.data_dir)

    if not tf.gfile.Exists(FLAGS.checkpoint_dir):
        tf.gfile.MakeDirs(FLAGS.checkpoint_dir)

def print_flags():
    """
    Prints all entries in FLAGS variable.
    """
    for key, value in vars(FLAGS).items():
        print(key + ' : ' + str(value))

def main(_):
    print_flags()

    initialize_folders()
    train()

if __name__ == '__main__':
    # Command line arguments
    parser = argparse.ArgumentParser()

    parser.add_argument('--learning_rate', type = float, default = LEARNING_RATE_DEFAULT,
                        help='Learning rate')
    parser.add_argument('--max_steps', type = int, default = MAX_STEPS_DEFAULT,
                        help='Number of steps to run trainer.')
    parser.add_argument('--batch_size', type = int, default = BATCH_SIZE_DEFAULT,
                        help='Batch size to run trainer.')
    parser.add_argument('--print_freq', type = int, default = PRINT_FREQ_DEFAULT,
                        help='Frequency of evaluation on the train set')
    parser.add_argument('--eval_freq', type = int, default = EVAL_FREQ_DEFAULT,
                        help='Frequency of evaluation on the test set')
    parser.add_argument('--refine_after_k', type = int, default = REFINE_AFTER_K_STEPS_DEFAULT,
                        help='Number of steps after which to refine VGG model parameters (default 0).')
    parser.add_argument('--checkpoint_freq', type = int, default = CHECKPOINT_FREQ_DEFAULT,
                        help='Frequency with which the model state is saved.')
    parser.add_argument('--data_dir', type = str, default = DATA_DIR_DEFAULT,
                        help='Directory for storing input data')
    parser.add_argument('--log_dir', type = str, default = LOG_DIR_DEFAULT,
                        help='Summaries log directory')
    parser.add_argument('--checkpoint_dir', type = str, default = CHECKPOINT_DIR_DEFAULT,
                        help='Checkpoint directory')

    FLAGS, unparsed = parser.parse_known_args()

    tf.app.run()


import time
import json
import datetime

invalid = "\n--Invalid response, please try again.--"
scheduleFile = "schedule.json"
assignmentFile = "assignment.json"

def load():
    for i in range(0, 40):
        time.sleep(0.00000000000001)
        print("-", end='', flush=True)
    print()

def unload():
    for i in range(0, 40):
        time.sleep(0.00000000000001)
        print('-' * (40 - i))
    print("--Goodbye!--")

def anythingElse():
    load()
    while True:
        userInput = input("Anything else?\n[0] - Yes\n[1] - No\n\nPlease choose an option: ")
        if userInput == "0":
            load()
            break
        elif userInput == "1":
            unload()
            return 1
        else:
            print(invalid)

def createSchedule():
    with open(scheduleFile) as f:
        data = json.load(f)
    while True:
        sched = input("Schedule Name: ")
        if sched not in data:
            break
        print(f'"{sched}" is already a schedule. Please enter a different name.')
    data[sched] = {}
    while True:
        number = input("Number of Classes (1 - 7): ")
        try:
            number = int(number)
            if number > 7 or number < 1:
                print(invalid)
            else:
                break
        except Exception:
            print(invalid)
    for i in range(1, number + 1):
        name = input(f"\nPeriod {i}: ")
        teacher = input("Teacher: ")
        description = input("Description: ")
        data[sched][name] = {}
        data[sched][name]["teacher"] = teacher
        data[sched][name]["description"] = description
    with open(scheduleFile, "w") as f:
        json.dump(data, f, indent=2)
    load()
    print(f'--Schedule "{sched}" created!--')

def seeSchedule():
    with open(scheduleFile) as f:
        data = json.load(f)
    if len(data) < 1:
        print("--There are currently no schedules.--")
        return
    num = 0
    while True:
        lister = []
        for i in data:
            lister += [i]
            print(f"[{num}] {i}\nPeriods: {len(data[i])}\n")
            num += 1
        num -= 1
        userInput = input("Please choose a schedule (or press e to exit): ")
        if userInput == "e":
            return
        else:
            try:
                userInput = int(userInput)
                if userInput > -1 and userInput <= num:
                    num = 0
                    load()
                    for i in data[lister[userInput]]:
                        print(f"Period {num + 1}: {i}\nTeacher: {data[lister[userInput]][i]['teacher']}\nDescription: {data[lister[userInput]][i]['description']}\n")
                        num += 1
                    userInput = input("Enter any key to return: ")
                    load()
                else:
                    print(invalid)
            except Exception:
                print(invalid)

def deleteSchedule():
    with open(scheduleFile) as f:
        data = json.load(f)
    if len(data) < 1:
        print("--There are currently no schedules.--")
        return
    num = 0
    while True:
        lister = []
        for i in data:
            lister += [i]
            print(f"[{num}] {i}\nPeriods: {len(data[i])}\n")
            num += 1
        num -= 1
        userInput = input("Please choose a schedule to delete (or press e to exit): ")
        if userInput == "e":
            return
        else:
            print()
            try:
                userInput = int(userInput)
                if userInput > -1 and userInput <= num:
                    num = 0
                    confirm = input(f'Are you sure you want to delete "{lister[userInput]}"?\nEnter "13579" to confirm, or enter anything else to cancel: ')
                    if confirm == "13579":
                        load()
                        del data[i]
                        with open(scheduleFile, "w") as f:
                            json.dump(data, f, indent=2)
                        userInput = input("--Schedule has been deleted.--\n\nEnter any key to return: ")
                        print()
                        break
                    else:
                        return
                else:
                    print(invalid)
            except Exception:
                print(invalid)

def createAssignment():
    with open(assignmentFile) as f:
        data = json.load(f)
    while True:
        name = input("Assignment Name: ")
        if name not in data:
            break
        else:
            print(f'"{name}" is already an assignment. Please enter a different name.')
    classname = input("Class: ")
    while True:
        due = input('Due Date (mm/dd/yyyy): ')
        try:
            s = datetime.date(int(due.split("/")[2]), int(due.split("/")[1]), int(due.split("/")[0]))
            n = datetime.datetime.now().date()
            if s > n and len(due.split("/")) == 3:
                break
            elif(s <= n):
                print("\n--That date has already passed. Please enter a different response.--")
            else:
                print(invalid)
        except Exception:
            print(invalid)
    description = input("Description: ")
    data[name] = {}
    data[name]["class"] = classname
    data[name]["due"] = due
    data[name]["description"] = description
    with open(assignmentFile, "w") as f:
        json.dump(data, f, indent=2)
    load()
    print(f'--Assignment "{name}" created!--')

def seeAssignment():
    with open(assignmentFile) as f:
        data = json.load(f)
    if len(data) < 1:
        print("--There are currently no assignments.--")
        return
    num = 0
    for i in data:
        print(f"[{num}] Assignment: {i}{len(data[i])}\n{' ' * len(str(len(data[i])))}   Class: {data[i]['class']}\n{' ' * len(str(len(data[i])))}   Due Date: {data[i]['due']}\n{' ' * len(str(len(data[i])))}   Description: {data[i]['description']}\n")
        num += 1
    userInput = input("Press any key to return: ")

def deleteAssignment():
    with open(assignmentFile) as f:
        data = json.load(f)
    lister = [x for x in data]
    if len(data) < 1:
        print("--There are currently no assignments.--")
        return
    num = 0
    for i in data:
        print(f"[{num}] Assignment: {i}{len(data[i])}\n{' ' * len(str(len(data[i])))}   Class: {data[i]['class']}\n{' ' * len(str(len(data[i])))}   Due Date: {data[i]['due']}\n{' ' * len(str(len(data[i])))}   Description: {data[i]['description']}\n")
        num += 1
    num -= 1
    while True:
        try:
            userInput = input("Please choose an assignment to delete (or press e to exit): ")
            if userInput == "e":
                return
            elif int(userInput) > -1 and int(userInput) <= num:
                confirm = input(f'\nAre you sure you want to delete "{lister[int(userInput)]}"?\nEnter "13579" to confirm, or enter anything else to cancel: ')
                if confirm == "13579":
                    del data[lister[int(userInput)]]
                    with open(assignmentFile, "w") as f:
                        json.dump(data, f, indent=2)
                    userInput = input("--Assignment has been deleted.--\n\nEnter any key to return: ")
                    print()
                    break
            else:
                print(invalid)
        except Exception as e:
            print(e)

def programChoice():
    while True:
        userInput = input("[0] - Create a schedule\n[1] - See existing schedules\n[2] - Delete a schedule\n[3] - Create an assignment\n[4] - Create an assignment\n[5] - Delete a schedule\n\nPlease choose the program you would like to use: ")
        if userInput == "0":
            load()
            createSchedule()
            if anythingElse() == 1:
                break
        elif userInput == "1":
            load()
            seeSchedule()
            if anythingElse() == 1:
                break
        elif userInput == "2":
            load()
            deleteSchedule()
            if anythingElse() == 1:
                break
        elif userInput == "3":
            load()
            createAssignment()
            if anythingElse() == 1:
                break
        elif userInput == "4":
            load()
            seeAssignment()
            if anythingElse() == 1:
                break
        elif userInput == "5":
            load()
            deleteAssignment()
            if anythingElse() == 1:
                break
        else:
            print(invalid)

def main():
    print("\n\n-----Welcome to Scheduler.py, a program made to schedule classes and assignments.-----")
    while True:
        userInput = input("[0] - Begin\n[1] - Quit\n\nPlease choose an option: ")
        if userInput == "0":
            load()
            programChoice()
            break
        elif userInput == "1":
            unload()
            break
        else:
            print(invalid)

main()


import csv # import csv library
'''This code takes a file input and header input
the function utilises those inputs to open the file then checks the header
the header is added to a dictionary called unique_list and stores the count of the header
it then gets printed'''

# a function that takes the file and header and checks it adding the selected header into a list and the count of the header into a dictionary
def read_and_check(file, header):
    unique_list = {} # initiates a unique list
    file_infile = open(file, "r") # open file
    csv_dict_reader = csv.DictReader(file_infile) # reads the file and store in dictionary

    for lst_cols in csv_dict_reader: #iterates through the list of columns
        if lst_cols[header] not in unique_list:
            unique_list[lst_cols[header]] = 1 # if the value is not in unique_list add it with the value 1
        else:
            unique_list[lst_cols[header]] += 1 # if the value is is in unique_list increase value by 1

    print(unique_list)

    file_infile.close() #close the file

# Testing
read_and_check("Google Play Store.csv", "Category")
read_and_check("Google Play Store.csv", "Rating")


from time import sleep

from appium import webdriver
from appium.webdriver.common.mobileby import MobileBy
from appium.webdriver.extensions.android.gsm import GsmCallActions
from selenium.webdriver.support import expected_conditions
from selenium.webdriver.support.wait import WebDriverWait

class TestBrowser():
    def setup(self):
        des_caps = {
            'platformName':'android',
            'platformVersion':'6.0',
            'appPackage':'com.xueqiu.android',
            'appActivity':'com.xueqiu.android.common.MainActivity',
            # 'browserName':'Browser',
            # 不停止APP，不清除app数据，不卸载app
            'noReset':True,
            # 停止app，清除app数据卸载app
            # 'fullReset':True,
            # 不停止测试app的进程
            'dontStopAppOnReset':True,
            'deviceName':'127.0.0.1:7555',
            'autoGrantPermissions':True,
            # 自动启动模拟器 emulator -list-avds 中的 Pixel_23_6
            # 只能是安卓自带的模拟器 第三方的不可以
            # 'avd':'Pixel_23_6'
            'newCommandTimeout':300

        }
        self.driver = webdriver.Remote('http://127.0.0.1:4723/wd/hub', des_caps)
        self.driver.implicitly_wait(10)

    def teardown(self):
        self.driver.quit()

    def test_mobile(self):
        pass
        # self.driver.make_gsm_call('15910852286',GsmCallActions.CALL)
        # self.driver.send_sms('15910852286','hello appium api')
        # # 录屏 8.0版本以上可以 华为不可
        # self.driver.start_recording_screen()
        # # 开启飞行模式
        # self.driver.set_network_connection(1)
        # self.driver.get_screenshot_as_file('./photos/img.png')
        # sleep(3)
        # self.driver.set_network_connection(4)
        # sleep(3)
        # self.driver.stop_recording_screen()


from math import ceil
type_sushi = input()
name_restaurant = input()
number_portions = int(input())
delivery = input()
is_invalid_restaurant = False
price = 1

if name_restaurant == "Sushi Zone":
    if type_sushi == "sashimi":
        price = 4.99
    elif type_sushi == "maki":
        price = 5.29
    elif type_sushi == "uramaki":
        price = 5.99
    elif type_sushi == "temaki":
        price = 4.29

elif name_restaurant == "Sushi Time":
    if type_sushi == "sashimi":
        price = 5.49
    elif type_sushi == "maki":
        price = 4.69
    elif type_sushi == "uramaki":
        price = 4.49
    elif type_sushi == "temaki":
        price = 5.19

elif name_restaurant == "Sushi Bar":
    if type_sushi == "sashimi":
        price = 5.25
    elif type_sushi == "maki":
        price = 5.55
    elif type_sushi == "uramaki":
        price = 6.25
    elif type_sushi == "temaki":
        price = 4.75

elif name_restaurant == "Asian Pub":
    if type_sushi == "sashimi":
        price = 4.50
    elif type_sushi == "maki":
        price = 4.80
    elif type_sushi == "uramaki":
        price = 5.50
    elif type_sushi == "temaki":
        price = 5.50

else:
    print(f"{name_restaurant} is invalid restaurant!")
    is_invalid_restaurant = True

if delivery == "Y":
    price_order = price * number_portions
    price_order *= 120/100
    price_order = ceil(price_order)
else:
    price_order = price * number_portions
    price_order = ceil(price_order)

if not is_invalid_restaurant:
    print(f"Total price: {price_order} lv.")


#!/usr/bin/python3
"""
Unittest for review module
"""
import os
import unittest
from models.review import Review
from models.base_model import BaseModel
from models.engine.file_storage import FileStorage

class Test_Review(unittest.TestCase):
    """ Test for
    Review Class """

    m = Review()

    def setUp(self):
        """set up the
        test for testing Reviews"""
        FileStorage._FileStorage__file_path = "test.json"
        self.rev = Review()
        self.rev.place_id = "666"
        self.rev.user_id = "666"
        self.rev.text = "666"
        self.rev.save()

    def test_atrr_type_review(self):
        """test attribute type for Review"""
        self.assertEqual(type(self.m.place_id), str)
        self.assertEqual(type(self.m.user_id), str)
        self.assertEqual(type(self.m.text), str)

    def test_attribute_place_id(self):
        """ Tests attr """
        self.assertEqual(hasattr(self.m, "place_id"), True)
        self.assertEqual(hasattr(self.m, "user_id"), True)
        self.assertEqual(hasattr(self.m, "text"), True)

    def test_subcls_Review(self):
        """test   subclass  BaseModel"""
        self.assertTrue(issubclass(self.rev.__class__, BaseModel), True)
        self.assertIsInstance(self.rev, Review)

    def test_docstring_Review(self):
        """checking for docstrings"""
        self.assertIsNotNone(Review.__doc__)

    def testpublic(self):
        self.assertEqual(str, type(Review().id))

if __name__ == "__main__":
    unittest.main()


from sys import stdin

ways = [[0 for length in range(1001)] for n in range(1001)]
ways[0][0] = 1

for n in range(1001):
    for length in range(1, 1001):
        ways[n][length] += 2 * ways[n - 1][length - 1]
        if n >= 2:
            ways[n][length] += ways[n - 2][length - 1]
        if n >= 3:
            ways[n][length] += ways[n - 3][length - 1]

ways = [sum(row) for row in ways]

for line in stdin:
    print(ways[int(line)])


from django.contrib.auth.models import User
from django.db import models
import cover.models
from documents.models import (Book, Chunk, Image, BookPublishRecord,
        ImagePublishRecord)
from documents.signals import post_publish
from dvcs.signals import post_publishable

def book_changed(sender, instance, created, **kwargs):
    instance.touch()
    for c in instance:
        c.touch()
models.signals.post_save.connect(book_changed, sender=Book)

def chunk_changed(sender, instance, created, **kwargs):
    instance.book.touch()
    instance.touch()
models.signals.post_save.connect(chunk_changed, sender=Chunk)

def image_changed(sender, instance, created, **kwargs):
    instance.touch()
models.signals.post_save.connect(image_changed, sender=Image)

def publish_listener(sender, *args, **kwargs):
    if isinstance(sender, BookPublishRecord):
        sender.book.touch()
        for c in sender.book:
            c.touch()
    elif isinstance(sender, ImagePublishRecord):
        sender.image.touch()
post_publish.connect(publish_listener)

def chunk_publishable_listener(sender, *args, **kwargs):
    sender.tree.touch()
    if isinstance(sender.tree, Chunk):
        sender.tree.book.touch()
post_publishable.connect(chunk_publishable_listener)

def publishable_listener(sender, *args, **kwargs):
    sender.tree.touch()
post_publishable.connect(publishable_listener, sender=Image)

def listener_create(sender, instance, created, **kwargs):
    if created:
        instance.chunk_set.create(number=1, slug='1')
models.signals.post_save.connect(listener_create, sender=Book)

def cover_changed(sender, instance, created, **kwargs):
    for book in instance.book_set.all():
        book.build_cover()
models.signals.post_save.connect(cover_changed, sender=cover.models.Image)


"""
Given an array consisting of n integers, find the contiguous subarray of given length k that has the maximum average value. And you need to output the maximum average value.

Example 1:
Input: [1,12,-5,-6,50,3], k = 4
Output: 12.75
Explanation: Maximum average is (12-5-6+50)/4 = 51/4 = 12.75
Note:
1 <= k <= n <= 30,000.
Elements of the given array will be in the range [-10,000, 10,000].
"""

class Solution(object):
    def findMaxAverage(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: float
        """
        if len(nums) > k:
            num_sum = sum(nums[0:k])
            max_sum = num_sum
            for i in range(1, len(nums)-k+1):
                num_sum += nums[i+k-1] - nums[i-1]
                max_sum = max(max_sum, num_sum)
            return max_sum/float(k)
        else:
            return sum(nums)/float(k)


# -*- coding: utf-8 -*-
"""
Created on Fri Aug 30 18:57:26 2019

@author: Mico
"""

import pandas as pd
import os
import numpy as np

def enconde_string_category(df,df_clean,mapping,col_name):
    column_data = pd.factorize(df[col_name].str.lower())
    mapping[col_name] = column_data[1].tolist()
    df_clean[col_name] = column_data[0]

    return df_clean, mapping

def column_to_float(df,col_name):
    col_list = []
    column_data = df[col_name]
    for row in column_data:
        col_list.append(float(str(row).replace(' in','').replace('.','.').replace('..','.')))

    return pd.DataFrame(col_list, columns=[col_name])

data_path = os.path.join('.','BaseDatosHistorica_Tronadura_Hackathon.xlsx')
df = pd.read_excel(data_path,skiprows=2) #el header esta en la linea

df = df.dropna() #quitamos las filas con datos NaN

headers = df.columns #nombres de los headers

#constantes
altura_banco = 15 #15 metros
pasadura = 1 #1 metro
largo_pozo = altura_banco + pasadura

categorias = ['Fase','Tipo de tronadura','Tipo Material','M','Dominio Estructural','Tipo Explosivo']
floats = ['Banco','Diámetro','Fc','P10','P20','P30','P40','P50','P60','P70','P80','P90','P100','Este','Norte','Cota']
otros = ['BxS','Tiempo entre Pozos Filas ms']

clean_dataframe = pd.DataFrame()
class_mapping = {}

#Fase
for header_name in headers:
    if header_name in categorias:
        clean_dataframe, class_mapping = enconde_string_category(df,clean_dataframe,class_mapping,header_name)
    else:
        if header_name in floats:
            clean_dataframe[header_name] = column_to_float(df,header_name)
            #clean_dataframe[header_name] = pd.to_numeric(df[header_name])

#otros

#BxS
burden_list = []
espaciamiento_list = []
for bxs in df['BxS']:
    burden,espaciamiento = (bxs.lower()).split('x')
    burden_list.append(float(burden))
    espaciamiento_list.append(float(espaciamiento))

clean_dataframe['Burden'] = burden_list
clean_dataframe['Espaciamiento'] = espaciamiento_list
clean_dataframe['Area'] = clean_dataframe['Burden']*clean_dataframe['Espaciamiento']
# tiempo entre pozos y filas ms
tx_list = []
ty_list = []

for txy in df['Tiempo entre Pozos Filas ms']:
    tx,ty = txy.split('-')
    tx_list.append(float(tx))
    ty_list.append(float(ty))

clean_dataframe['t_x'] = tx_list
clean_dataframe['t_y'] = ty_list

clean_dataframe.to_excel('clean_database.xls', index = False)
np.save('mapping.npy',class_mapping)