"""Spectral estimation methods for nonstationary/nonlinear timeseries"""

from collections import OrderedDict
import numpy as np
import scipy.signal as signal
import scipy.stats.distributions as dists
try:
    import scipy.fft as fft
    POCKET_FFT = True
except ImportError:
    import scipy.fftpack as fft
    POCKET_FFT = False
import nitime.utils as nt_utils

import ecogdata.parallel.sharedmem as shm
import ecogdata.filt.blocks as blocks
from ecogdata.util import nextpow2, dpss_windows

from .resampling import Jackknife

__all__ = ['bw2nw',
           'nw2bw',
           'MultitaperEstimator',
           'mtm_spectrogram_basic',
           'mtm_spectrogram',
           'mtm_complex_demodulate',
           'bispectrum',
           'normalize_spectrogram']

def _parse_mtm_args(N, kw_dict):
    # parse args for some retro signatures
    nfft = None
    if 'NFFT' in kw_dict:
        nfft = kw_dict.pop('NFFT')
    elif 'nfft' in kw_dict:
        nfft = kw_dict.pop('nfft')
    if nfft is None:
        nfft = N
    elif nfft == 'auto':
        nfft = nextpow2(N)
    BW = kw_dict.pop('BW', None)
    Fs = kw_dict.pop('Fs', 1)
    NW = kw_dict.pop('NW', None)
    if BW is not None:
        # BW wins in a contest (since it was the original implementation)
        NW = bw2nw(BW, N, Fs, halfint=True)
    elif NW is None:
        # default NW
        NW = 4
    # (else BW is None and NW is not None) ... all set
    lb = kw_dict.pop('low_bias', True)
    return NW, nfft, lb

def bw2nw(bw, n, fs, halfint=True):
    """Full BW to NW, given sequence length n"""
    # nw = tw = t(bw)/2 = (n/fs)(bw)/2
    bw, n, fs = list(map(float, (bw, n, fs)))
    nw = (n / fs) * (bw / 2)
    if halfint:
        # round 2NW to the closest integer and then halve again
        nw = round(2 * nw) / 2.0
    return nw

def nw2bw(nw, n, fs):
    """NW to full BW, given sequence length n"""
    # bw = 2w = 2(tw)/t = 2(nw)/t = 2(nw) / (n/fs) = 2(nw)(fs/n)
    nw, n, fs = list(map(float, (nw, n, fs)))
    return 2 * nw * fs / n

# These estimator functions used to be closures.
# They need to be importable for spawn-based multiprocessing.
def _psd_from_direct_spectra(Y, w, logout=False):
    sk = np.sum(w * Y, axis=1) / np.sum(w, axis=1)
    if logout:
        return np.log(sk)
    else:
        return sk

def _csd_from_direct_spectra(y, w):
    # weighted sdfs
    yw = y * w
    numer = np.einsum('mkl,nkl->mnl', yw, yw.conj())
    wsum = np.sum(w ** 2, axis=1) ** 0.5
    denom = wsum[:, None, :] * wsum[None, :, :]
    return numer / denom

class _DPSScache:

    max_cached = 50
    cache = OrderedDict()

    @classmethod
    def prepare_dpss(cls, N, NW, low_bias=True, Kmax=None):
        NW = round(2 * NW) / 2
        if (N, NW) in cls.cache:
            dpss_c, eigs_c = cls.cache[(N, NW)]
            dpss = dpss_c.copy()
            eigs = eigs_c.copy()
        else:
            dpss, eigs = dpss_windows(N, NW, int(2 * NW) - 1)
            # always store 2NW eigenvectors/values
            cls.cache[(N, NW)] = (dpss.copy(), eigs.copy())
            if len(cls.cache) > cls.max_cached:
                first_key = list(cls.cache.keys())
                cls.cache.pop(first_key)
        if low_bias:
            low_bias = 0.99 if float(low_bias) == 1.0 else float(low_bias)
            keepers = eigs > low_bias
            dpss = dpss[keepers]
            eigs = eigs[keepers]
        if Kmax is not None and len(eigs) > Kmax:
            dpss = dpss[:Kmax]
            eigs = eigs[:Kmax]
        return dpss, eigs

class MultitaperEstimator:
    """
    Class to handle multitaper method spectral estimation.
    """

    def __init__(self, N, NW, fs=1.0, nfft=None, low_bias=True, dpss=None):
        """
        Constructs a multitaper estimator based on length-bandwidth parameters. Constructs discrete prolate
        spheroidal sequences (DPSS) and the estimator's frequency grid.

        Parameters
        ----------
        N: int
            Length of sequences
        NW: float
            Time-bandwidth product to define DPSS. 2NW - 1 DPSS will be constructed and K < 2NW will be used based on
            spectral concentration and the low_bias argument.
        fs: float
            Sampling frequency (or 1 for normalized digital frequencies).
        nfft: int
            Compute the FFT with this many points, rather than N. If the value is 'auto', use the next highest power
            of two.
        low_bias: bool, float
            Restrict DPSS tapers based on bandpass concentration ratios, given by eigenvalues. If True, then restrict
            to tapers with eigenvalues > 0.99. If a number is given, then use that threshold.
        dpss: 2-tuple
            If DPSS and eigenvalues were pre-created, use them instead. Specify as dpss=(dpss_vecs, eigs)

        """
        if dpss is not None:
            self.dpss, self.eigs = dpss
        else:
            self.dpss, self.eigs = _DPSScache.prepare_dpss(N, NW, low_bias=low_bias)
        if nfft is None:
            self.nfft = N
        elif nfft == 'auto':
            self.nfft = nextpow2(N)
        else:
            self.nfft = nfft
        self.NW = NW
        self.freq = np.linspace(0, fs / 2.0, self.nfft // 2 + 1)

    @property
    def BW(self):
        return nw2bw(self.NW, self.dpss.shape[-1], self.freq[-2] * 2)

    def direct_sdfs(self, x, adaptive_weights=False, dof=False):
        """
        Compute uncorrelated direct spectral density functions under each of K tapers for array(s) in x.

        Parameters
        ----------
        x: ndarray
            1- or 2-dimension timeseries array. SDFs are computed for series in the last dimension.
        adaptive_weights: bool
            Compute adaptive weightings per frequency. If False, then use standard weights based on eigenvalues.
        dof: bool
            Return "approximate" chi-square degrees of freedom based on adaptive weights. Otherwise nu=2K

        Returns
        -------
        yk: ndarray
            Complex array of SDFs shaped (K, nfreq) for 1D input or (M, K, nfreq) for 2D input.
        w: ndarray
            SDF weights. If adaptive weighting was used, the shape of w matches yk.shape. Otherwise,
            dummy-dimensions are used in place of M and/or nfreq.

        Notes
        -----
        Since DPSS tapers are orthonormal, these SDFs can also be used as linear coefficients of narrowband
        subspaces. The subspaces are defined by frequency-shifted DPSS tapers: v(k, n) * exp(-j * w[b] * n)
        where w[b] is the FFT bin at b. The bandwidth of each subspace centered at w[b] is determined by the
        NW taper parameter.

        """
        shp = x.shape
        # x must be 2D (even if shaped (1, T))
        x = np.atleast_2d(x)
        M = x.shape[0]
        K = len(self.dpss)
        tapered = x[..., np.newaxis, :] * self.dpss
        fft_args = dict(axis=-1, n=self.nfft, overwrite_x=True)
        if POCKET_FFT:
            fft_args['workers'] = -1
        yk = fft.fft(tapered, **fft_args)
        half_pts = self.nfft // 2 + 1
        nu = np.zeros((M, half_pts))
        if adaptive_weights:
            w = np.zeros((M, K, half_pts))
            for m in range(M):
                w[m], nu[m] = nt_utils.adaptive_weights(yk[m], self.eigs)
        else:
            w = np.sqrt(self.eigs[None, :, None])
            nu[:] = 2 * K
            # w = np.tile(w, (M, 1, half_pts))
        # TODO: tiling weights and cutting/copying yk are fairly expensive (e.g. 700 ms for (300, 2 ** 15) input)
        yk = yk[..., :half_pts].copy()
        # if input was 1D then only return SDFs for single timeseries (get rid of dummy dimension)
        if len(shp) == 1:
            yk = yk[0]
            w = w[0]
            nu = nu[0]
        if dof:
            return yk, w, nu
        else:
            return yk, w

    def compute_psd(self, x, adaptive_weights=False, jackknife=False, jn_jobs=1, detrend=None, ci=False):
        """
        Compute the multitaper psd estimate(s) of series in x. PSD normalization is defined such that the integral of
        spectral power per Hz equals to the total variance in x:

        E{(x - E{x}) ** 2} = integral P(f) * df (eval from f=0 to f=Fs / 2)

        Parameters
        ----------
        x: ndarray
            1- or 2-dim array with timeseries in last dimension.
        adaptive_weights: bool
            Use adaptive weights in combining multitaper estimates.
        jackknife: bool
            Use jackknife resampling in combining multitaper estimates. In this mode, standard error is calculated
            after log-transforming jackknifed estimates. The confidence interval is calculated for the log domain
            based on Student's t distribution for K - 1 degrees of freedom, but is then exponentiated before return.
        jn_jobs: int
            Perform Jackknife resampling on this many processes.
        detrend: str, bool
            Remove order-1 (detrend='linear') or order-0 (detrend='constant') trends. If detrend=True, remove constant.
        ci: bool or float
            Compute the confidence interval at (100 * ci) percent or 95% by default. If jackknife is not used,
            then the standard chi-squared assumption is used. If adaptive weights are used, the minimum chi-squared
            DOF will be clipped to 1.

        Returns
        -------
        freqs: ndarray
            Frequency grid
        psds: ndarray
            Power spectral density estimate(s) in power / Hz.

        """
        # TODO: return jackknife or chi2 confidence interval
        if detrend is not None:
            if isinstance(detrend, bool):
                detrend = 'constant'
            x = signal.detrend(x, axis=-1, type=detrend)
        shp = x.shape
        x = np.atleast_2d(x).reshape(-1, x.shape[-1])
        yk, w, nu = self.direct_sdfs(x, adaptive_weights=adaptive_weights, dof=True)

        # funny -- this is faster (in one step) than doing inplace squares
        yk = np.abs(yk) ** 2
        # np.power(yk, 2, yk)
        np.power(w, 2, w)
        if jackknife:
            pxx, se = Jackknife([yk, w], axis=1, n_jobs=jn_jobs).estimate(_psd_from_direct_spectra,
                                                                          correct_bias=True,
                                                                          se=True,
                                                                          logout=True)
        else:
            pxx = _psd_from_direct_spectra(yk, w)

        # PSD needs normalization:
        # P(f) <-- 2 * Pf / samp_rate (for 0 < f < f_nyq)
        # P(f) <-- Pf / samp_rate (for DC and nyquist)
        # Save normalization for different scenarios, but reshape to output here
        pxx = pxx.reshape(shp[:-1] + (pxx.shape[-1],))
        if ci:
            if isinstance(ci, float):
                p = 1 - ci
            else:
                p = 1 - 0.95
            if jackknife:
                # normalize pxx from log domain
                pxx[..., 1:-1] += np.log(2) - np.log(self.freq[-1] * 2)
                pxx[..., 0] -= np.log(self.freq[-1] * 2)
                pxx[..., -1] -= np.log(self.freq[-1] * 2)
                se.shape = pxx.shape
                # Since SE is computed under log transform, it doesn't need to be scaled. The PSD normalization here
                # is just a shift in the mean
                t_iv = dists.t.ppf([p / 2, 1 - p / 2], yk.shape[1] - 1)
                conf_iv = np.array([pxx + t_iv[0] * se, pxx + t_iv[1] * se])
                np.exp(pxx, out=pxx)
                np.exp(conf_iv, out=conf_iv)
            else:
                # normalize
                pxx[..., 1:-1] /= self.freq[-1]
                pxx[..., 0] /= (self.freq[-1] * 2)
                pxx[..., -1] /= (self.freq[-1] * 2)
                nu.shape = pxx.shape
                np.putmask(nu, nu < 1, 1)
                chi2_iv_lo = dists.chi2.ppf(p / 2, nu)
                chi2_iv_hi = dists.chi2.ppf(1 - p / 2, nu)
                conf_iv = np.array([nu * pxx / chi2_iv_hi, nu * pxx / chi2_iv_lo])
            return self.freq, pxx, conf_iv
        else:
            pxx[..., 1:-1] /= self.freq[-1]
            pxx[..., 0] /= (self.freq[-1] * 2)
            pxx[..., -1] /= (self.freq[-1] * 2)
            return self.freq, pxx

    @classmethod
    def psd(cls, x, NW=2.5, fs=1.0, nfft=None, low_bias=True, dpss=None,
            adaptive_weights=False, jackknife=False, jn_jobs=1, detrend=None, ci=False):
        """
        Shortcut to create a MultitaperEstimator and then compute the psd for x. See arguments for
        MultitaperEstimator construction and compute_psd

        Returns
        -------
        freqs: ndarray
            Frequency grid
        psds: ndarray
            Power spectral density estimate(s) in power / Hz.

        """
        N = x.shape[-1]
        mt_estimator = cls(N, NW, fs=fs, nfft=nfft, low_bias=low_bias, dpss=dpss)
        return mt_estimator.compute_psd(x, adaptive_weights=adaptive_weights, jackknife=jackknife,
                                        jn_jobs=jn_jobs, detrend=detrend, ci=ci)

    def compute_csd(self, x, y=None, adaptive_weights=False, jackknife=False, jn_jobs=1, detrend=None):
        """
        Compute cross-spectra either between timeseries x and y, or between
        all the combinations in the vector timeseries x. Other arguments follow compute_psd.

        Returns
        -------
        freqs: ndarray
            Frequency grid
        csds: ndarray
            (M, M, nfreq) matrix of cross spectral densities. M=2 if x and y are specified separarely, else M=len(x).

        """
        # TODO: maybe implement "seed" kwargs -- basically computes only 1 row of the matrix
        if x.ndim == 1 and y is None:
            raise ValueError('Need two or more vectors')
        if y is not None:
            x = np.vstack([x, y])
        if detrend is not None:
            if isinstance(detrend, bool):
                detrend = 'constant'
            x = signal.detrend(x, axis=-1, type=detrend)
        yk, w, nu = self.direct_sdfs(x, adaptive_weights=adaptive_weights, dof=True)

        if jackknife:
            cxy = Jackknife([yk, w], axis=1, n_jobs=jn_jobs).estimate(_csd_from_direct_spectra,
                                                                      correct_bias=True,
                                                                      se=False)
        else:
            cxy = _csd_from_direct_spectra(yk, w)
        cxy /= (self.freq[-1] * 2)
        cxy[..., 1:] *= 2
        return self.freq, cxy

    @classmethod
    def csd(cls, x, y=None, NW=2.5, fs=1.0, nfft=None, low_bias=True, dpss=None,
            adaptive_weights=False, jackknife=False, jn_jobs=1, detrend=None):
        """
        Shortcut to create a MultitaperEstimator and then compute the csds for x. See arguments for
        MultitaperEstimator construction and compute_csd

        Returns
        -------
        freqs: ndarray
            Frequency grid
        csds: ndarray
            (M, M, nfreq) matrix of cross spectral densities. M=2 if x and y are specified separarely, else M=len(x).

        """
        N = x.shape[-1]
        mt_estimator = cls(N, NW, fs=fs, nfft=nfft, low_bias=low_bias, dpss=dpss)
        return mt_estimator.compute_csd(x, y=y, adaptive_weights=adaptive_weights,
                                        jackknife=jackknife, jn_jobs=jn_jobs, detrend=detrend)

def mtm_spectrogram_basic(x, n, pl=0.25, detrend='', **mtm_kwargs):
    """
    Make spectrogram using the multitaper spectral estimation method at
    each block.

    Parameters
    ----------
    n: int
        number of points per block
    pl: float
        percent overlap between blocks
    detrend: ''
        detrend each block as 'linear', 'constant',  (or not at all)
    mtm_args: dict
        keyword arguments for the multitaper family of methods

    Returns
    -------
    tx, fx, psd_matrix

    """

    if x.ndim < 2:
        x = x.reshape((1,) + x.shape)
    NW, nfft, low_bias = _parse_mtm_args(x.shape[-1], mtm_kwargs)
    mtm_kwargs.setdefault('adaptive_weights', True)
    mtm_kwargs.setdefault('jackknife', False)
    xb = blocks.BlockedSignal(x, n, overlap=pl, partial_block=False)
    x_lapped = xb._x_blk.copy()
    if detrend:
        x_lapped = signal.detrend(x_lapped, type=detrend, axis=-1).copy()

    # fx, psds, nu = multi_taper_psd(x_lapped, **mtm_kwargs)
    fx, psds = MultitaperEstimator.psd(x_lapped, NW=NW, nfft=nfft, low_bias=low_bias, **mtm_kwargs)

    # infer time-resolution
    lag = round((1 - pl) * n)
    Fs = 2 * fx[-1]
    spec_res = lag / Fs

    tx = np.arange(0, len(xb)) * spec_res
    # align the bins at the middle of the strips
    tx += 0.5 * n / Fs

    return tx, fx, psds.transpose(0, 2, 1).squeeze()

def mtm_spectrogram(
        x, n, pl=0.25, detrend='', Fs=1.0, adaptive_weights=True, samp_factor=1,
        freqs=None, pad=False, **mtm_kwargs
):
    """
    Make spectrogram from baseband complex-demodulates computed for
    each frequency over moving blocks.

    Parameters
    ----------
    n: int
        number of points per block
    pl: float
        percent overlap between blocks
    detrend: ''
        detrend each block as 'linear', 'constant',  (or not at all)
    adaptive_weights: bool
        weight each spectral estimator adaptively
    samp_factor: int
        Each complex demodulate has a temporal resolution of 1/2W, and will
        be resampled by default. The time resolution will be calculated
        to ensure that shifting windows overlap correctly (i.e. the
        window step time will be ensured to be a multiple of the time
        resolution). Set samp_factor to compute complex demodulates within
        a window at a higher time resolution.
    freqs: list
        If given, only keep the power envelopes at these frequencies
        (instead of the full spectrogram)
    mtm_args: dict
        keyword arguments for the multitaper family of methods, viz:

        * NW
        * low_bias
        * NFFT

    Returns
    -------
    tx, fx, psd_matrix

    """

    # clean up possible call signature difference
    if 'adaptive' in mtm_kwargs:
        adaptive_weights = mtm_kwargs.pop('adaptive')
    NW, nfft, lb = _parse_mtm_args(n, mtm_kwargs)
    if pl > 1:
        # re-specify in terms of a fraction
        pl = float(pl) / n

    # set up time-resolution and adjust overlap to a more
    # convenient number if necessary
    K = 2 * NW - 1
    if samp_factor == 0:
        user_delta = 1.0
    else:
        user_delta = float(n) / K / samp_factor
    m = round((1 - pl) * n)
    if m < user_delta:
        delta = m
    else:
        p = np.ceil(m / user_delta)
        if (m // p) * p < m:
            m = p * (m // p)
            pl = 1 - float(m) / n
        delta = m // p

    # check contiguous
    if not x.flags.c_contiguous:
        x = x.copy(order='C')
    blk_x = blocks.BlockedSignal(x, n, overlap=pl, partial_block=False)

    nblock = len(blk_x)

    # calculate the number of psd matrix time points
    # total size of time-frequency array
    pts_per_block = int(n // delta)
    overlap = pts_per_block - m // delta
    psd_len = int(nblock * pts_per_block - (nblock - 1) * overlap)
    psd_pl = float(overlap) / pts_per_block

    pad_n = mtm_complex_demodulate(n, NW, nfft=None, pad=pad, return_pad_length=True,
                                   samp_factor=(1.0 / delta if delta > 1 else 0))
    nfft = nextpow2(pad_n)
    dpss, eigs = _DPSScache.prepare_dpss(pad_n, float(pad_n * NW) / n, low_bias=lb)
    fx = np.linspace(0, Fs / 2, nfft // 2 + 1)

    if freqs is None:
        nfreq = len(fx)
    else:
        nfreq = len(freqs)

    psd_matrix = np.zeros(x.shape[:-1] + (nfreq, psd_len), 'd')
    blk_psd = blocks.BlockedSignal(
        psd_matrix, pts_per_block, overlap=psd_pl,
        partial_block=False, axis=-1
    )
    # this array mirrors the psd matrix blocks and counts accumulation
    n_avg = np.zeros(psd_len)
    blk_n = blocks.BlockedSignal(
        n_avg, pts_per_block, overlap=psd_pl, partial_block=False
    )

    ind = (np.arange(pts_per_block) + 0.5) * delta
    dpss_sub = dpss[..., ind.astype('i')]
    weight = delta * dpss_sub.T.dot(dpss_sub.dot(np.ones(pts_per_block)))
    window = np.hamming(pts_per_block)
    weight *= window
    weight = window

    for b in range(len(blk_n)):
        # it's possible to exceed the data blocks, since we're not
        # using fractional blocks in the signal (??)
        if b >= nblock:
            break
        nwin = blk_n.block(b)
        # nwin[:] += 1 # just keep count of how many times we hit these points
        # oddly sqrt *looks* more right
        # nwin[:] += weight**.5
        nwin[:] += weight
        dwin = blk_x.block(b)
        if detrend:
            dwin = signal.detrend(dwin, type=detrend)
        mtm_pwr, ix, weighting = mtm_complex_demodulate(
            dwin, NW, nfft=nfft, adaptive_weights=adaptive_weights, dpss=dpss, eigs=eigs,
            samp_factor=1.0 / delta if delta > 1 else 0, pad=pad
        )
        if freqs is None:
            mtm_pwr = 2 * np.abs(mtm_pwr)**2
        else:
            f_idx = fx.searchsorted(freqs)
            mtm_pwr = 2 * np.abs(mtm_pwr[f_idx])**2

        if np.iterable(weighting):
            mtm_pwr /= weighting[..., None]
        else:
            mtm_pwr /= weighting
        psd_win = blk_psd.block(b)
        psd_win[:] = psd_win + window * mtm_pwr

    n_avg[n_avg == 0] = 1
    psd_matrix /= n_avg
    if samp_factor == 0:
        tx = np.arange(psd_matrix.shape[-1], dtype='d')
    else:
        tx = (np.arange(psd_matrix.shape[-1]) + 0.5) * delta
    tx /= Fs

    # scale by freq so that total power(t) = \int{ psd(t,f) * df }
    df = Fs / nfft
    psd_matrix /= df
    if freqs is not None:
        fx = freqs

    return tx, fx, psd_matrix

def mtm_complex_demodulate(x, NW, nfft=None, adaptive_weights=True, low_bias=True,
                           dpss=None, eigs=None, samp_factor=1,
                           fmax=0.5, bandpass=(), pad=False, return_pad_length=False):
    """
    Computes the complex demodulate of x. The complex demodulate is
    essentially a time-frequency matrix that holds the complex "baseband"
    of x, as if demodulated from each frequency band (f +/- W). The width
    of the carrier band is of course determined by the Slepian concentration
    band W.

    Parameters
    ----------
    x : array
        Array with signal sequence in the last dimension
    NW : float
        DPSS parameter
    samp_factor: int
        By default, the complex demodulate will be resampled to its temporal
        resolution of 1/2W. Setting samp_factor > 1 samples at that many times
        the temporal resolution.
        If a specific (integer) sample-resolution is desired, then set
        samp_factor = 1.0 / sample_resolution.
        Setting samp_factor == 0 disables resampling.
    dpss: array
        pre-computed Slepian sequences
    eigs: array
        eigenvalues for pre-computed Slepians
    pad: bool
        pad the timeseries to reflect the edges
    return_pad_length: bool
        Only compute the (nontrivial) padded length and return.
        Helpful for pre-computing DPSS and freq axis.

    Returns
    -------
    x_tf: array
        complex demodulates at baseband for each frequency
    ix: array
        ix is an array of resampled points, relative to the full indexing of
        the input array.
    weight: array
        weight is the matrix of weights **(why??)**

    """

    if return_pad_length and isinstance(x, int):
        N = x
    else:
        N = x.shape[-1]
    # Find the time basis
    if samp_factor == 0:
        t_res = 1
        # resample on samples (i.e. don't resample)
        resample_point = 1.0
    elif samp_factor < 1:
        t_res = max(2, int(1 / samp_factor))
        # resample midway between samples separated by t_res
        resample_point = 0.5
    else:
        samp_factor = min(samp_factor, float(N) / (4 * NW))
        # TODO: this seems too coarse by a factor of 2!!
        t_res = int(np.floor(float(N) / (2 * NW) / samp_factor))
        t_res = max(2, t_res)
        resample_point = 0.5
    if pad:
        # Append an exact multiple of t_res for the pre-pad record so that
        # the downsampled time base aligns correctly between [0, N-1]
        neg_segs = ((N - 1) // t_res)
        neg_length = neg_segs * t_res
        N_pad = neg_length + 2 * N - 1
        if return_pad_length:
            return N_pad
        x = np.hstack([x[..., 1:1 + neg_length][..., ::-1], x, x[..., :-1][..., ::-1]])
        ix = ((np.arange(N_pad // t_res) - neg_segs) + resample_point) * t_res
    else:
        if return_pad_length:
            return N
        N_pad = N
        neg_segs = 0
        ix = (np.arange(N // t_res) + resample_point) * t_res

    BW = float(NW) / N
    NW_pad = BW * N_pad
    if dpss is None:
        dpss, eigs = _DPSScache.prepare_dpss(N_pad, NW_pad, low_bias=low_bias)
    if nfft is None:
        nfft = nextpow2(N_pad)
    K = len(eigs)

    mtm = MultitaperEstimator(N_pad, NW_pad, nfft=nfft, dpss=(dpss, eigs))
    xk, weight = mtm.direct_sdfs(x, adaptive_weights=adaptive_weights)
    if adaptive_weights:
        xk *= weight
        # repurpose weight as the sum of squared weights across K direct SDFs
        weight = np.sum(weight ** 2, axis=-2)
    else:
        weight = float(K)

    xk *= np.sqrt(eigs[:, None])

    xk_win_ax = x.ndim - 1
    # Expand the complex coefficients at each frequency with the baseband DPSS vectors
    # x_tf is shaped (..., K, f) for K support vectors and f frequencies --
    # this is K complex coefficients per frequency to linearly combine the dpss.
    # But do time-axis resampling first to avoid expansion on unneeded time points.
    if pad:
        if resample_point == 0.5:
            # limit last point to an interior point between samples
            ix_mask = (ix >= 0) & (ix <= N - 0.5 * t_res)
        else:
            # limit last point to actual last point
            ix_mask = (ix >= 0) & (ix < N)
        ix = ix[ix_mask]
    if resample_point == 0.5:
        t1 = (ix + neg_segs * t_res).astype('i')
        dpss_sub = np.take(dpss, t1, axis=-1)
        if t_res % 2:
            dpss_sub += np.take(dpss, t1 + 1, axis=-1)
            dpss_sub /= 2
        dpss = dpss_sub
    else:
        dpss = dpss[:, ix_mask]
    # Also trim the number of frequencies to include, if specified
    if fmax < 0.5:
        fmax = int(fmax / 0.5 * xk.shape[-1])
        xk = xk[..., :fmax]
    elif len(bandpass):
        f1 = int(bandpass[0] / 0.5 * xk.shape[-1])
        f2 = int(bandpass[1] / 0.5 * xk.shape[-1])
        xk = xk[..., f1:f2]
    x_tf = np.tensordot(xk, dpss, axes=(xk_win_ax, 0))
    return x_tf, ix, weight

# bi-coherence estimator -- needs to be importable for pickle & spawn
def _BIC_ratio(P):
    D = np.mean(np.abs(P) ** 2, axis=0) ** 0.5
    return np.abs(P.mean(axis=0)) / D

def bispectrum(
        x, NW, low_bias=True, nfft=None, fmax=0.5,
        bic=True, se=True, jackknife=True, jn_jobs=1, all_samps=False,
        return_symmetric=False, return_sparse=False
):
    """
    Estimate the bispectrum of x using complex demodulates.

    Parameters
    ----------
    x : ndarray (1D)
        Compute bispectrum (or bicoherence) on this signal. Due to the
        memory consumption of this method, only 1D is implemented.
    NW : float
        Time-bandwidth product (2NW should be an integer).
    low_bias : {True | False | 0 < p < 1}
        Only use eigenvalues above 0.99 (by default), or the value
        given.
    nfft : int
        Number of FFT grid points
    bic : bool {True | False}
        Normalize the bispectrum (to yield the bicoherence).
    se : bool {True | False}
        Return the jack-knife standard error (sets jackknife to True).
    jackknife : bool {True | False}
        Use jackknife resampling for estimating the bispectrum mean
        and standard error. The Jackknife is ALWAYS used as the
        bicoherence ratio estimator.
    jn_jobs: int
            Perform Jackknife resampling on this many processes.
    all_samps : bool {True | False}
        Skip all estimates and return all samples.
    return_symmetric : bool {True | False}
        If True, return the full (f1 + f2 <= 0.5) area of the bispectrum.
        (This option is implied if the cython-ized bispectrum method
        cannot be loaded.)
    return_sparse : bool {True | False}
        If True, return the sparse matrix (matrices). (This option is
        only applies to the cython-ized bispectrum method.)

    Returns
    ------
    B : ndarray (n_freq, n_freq)
        The bispectrum (or bicoherence).
    se : ndarray (n_freq, n_freq)
        The jackknife standard error (if se==True)

    """

    N = x.shape[-1]
    dpss, eigs = _DPSScache.prepare_dpss(N, NW, low_bias=low_bias)

    x_tf, ix, w = mtm_complex_demodulate(
        x, NW, nfft=nfft, dpss=dpss, eigs=eigs, samp_factor=1, fmax=fmax, pad=False
    )

    nf, K = x_tf.shape

    dpss = dpss * np.sqrt(eigs[:, None])
    P = np.einsum('im,jm,km->ijk', dpss, dpss, dpss)
    v = np.sum(P**2)

    x_tf = x_tf.T
    try:
        from ._bispectrum import calc_bispectrum
        from scipy.sparse import csr_matrix
        samps, row, col = calc_bispectrum(x_tf.real, x_tf.imag)
        samps = samps.view('D')
        samps *= (N / v)

        def r(X):
            if X.ndim > 1:
                sm = [csr_matrix((X_, (row, col)), shape=(nf, nf))
                      for X_ in X]
                if return_sparse:
                    return sm
                dm = np.empty((len(X), nf, nf), dtype=X.dtype)
                for k in range(len(X)):
                    dm[k] = sm[k].todense()
                    if return_symmetric:
                        dm[k].flat[::nf + 1] /= 2.0
                        dm[k] = dm[k] + dm[k].T
                return dm
            sm = csr_matrix((X, (row, col)), shape=(nf, nf))
            if return_sparse:
                return sm
            if return_symmetric:
                dm = sm.todense()
                dm.flat[::nf + 1] /= 2.0
                return dm + dm.T
            return sm.todense()
    except ImportError:
        samps = shm.shared_ndarray((K, nf, nf), typecode='D')
        np.einsum('...i,...j->...ij', x_tf, x_tf, out=samps)
        tr_i, tr_j = np.tril_indices(nf)

        # reflect the row indices to give the upper-left triangle:
        # i + j < nf for all entries in this triangle
        tr_i = nf - 1 - tr_i

        # @split_at(split_arg=(0,1))
        def _apply_third_product(x, tf, nf, tr_i, tr_j):
            b_tr = np.zeros((nf, nf), dtype=x.dtype)
            for k in range(x.shape[0]):
                b_tr[(tr_i, tr_j)] = np.take(tf[k], tr_i + tr_j)
                x[k] *= b_tr.conj()
            return x

        samps = _apply_third_product(samps, x_tf, nf, tr_i, tr_j)
        samps *= (N / v)

        def r(X):
            return X

    if all_samps:
        return r(samps)

    if se:
        jackknife = True

    if not jackknife:
        # not jackknife is synonymous with not se
        est = _BIC_ratio(samps) if bic else samps.mean(0)
        return r(est)

    # But jackknife == True does not imply se == True
    if bic:
        pv = Jackknife(samps, axis=0, n_jobs=jn_jobs).pseudovals(_BIC_ratio)
    else:
        pv = Jackknife(samps, axis=0, n_jobs=jn_jobs).pseudovals(np.mean)
    return (r(pv.mean(0)), r(pv.std(0) / K**0.5)) if se else r(pv.mean(0))

def _circular_clip(x, eps=0):
    if x.dtype not in np.sctypes['complex']:
        return np.clip(x, -1, 1)
    x_mag = np.abs(x)
    x_phs = np.angle(x)
    np.putmask(x, x_mag > 1 - eps, (1 - eps) * np.exp(1j * x_phs))
    return x

# coherence estimator: needs to be importable for pickle & spawn
def _coh_estimator(Y, seed, msc, fisher):
    # Y is (M, K, Nf)
    if seed is not None:
        coh = (Y * Y[seed].conj()).sum(-2)
    else:
        coh = np.einsum('mkn,pkn->mpn', Y, Y.conj())
    y_auto = np.sum(np.abs(Y) ** 2, axis=-2)
    np.sqrt(y_auto, y_auto)
    if seed is not None:
        y_auto *= y_auto[seed]
        coh /= y_auto
    else:
        coh = coh / y_auto[None, :, :]
        coh = coh / y_auto[:, None, :]
    # Need to "circularly clip" imaginary values by shrinking them to the unit circle
    coh = _circular_clip(coh, eps=1e-16)
    if msc:
        coh = np.abs(coh) ** 2
        if fisher:
            coh = np.arctanh(coh)
    elif fisher:
        # Fisher's transform, but complex
        # apply transformation separately to real and imag values?
        coh.real[:] = np.arctanh(coh.real)
        coh.imag[:] = np.arctanh(coh.imag)
    return coh

def coherence(
        x, NW, msc=True, dpss=None, eigs=None, ci=False, jn_jobs=1, fisher=True,
        low_bias=True, nfft='auto', seed=None
):
    """Estimate the coherence spectrum between different signals.

    Parameters
    ----------
    x : ndarray (M, N)
        Vector timeseries of sources
    NW : float
        Time-bandwidth product defining the Slepian tapers
    msc : {True | False}
        Compute the magnitude-square coherence (MSC) of the
        dual spectrum.
    dpss : None
        Precomputed Slepian tapers vk(N,W)
    eigs : None
        Eigenvalues of precomputed tapers
    ci : bool or float
        Return a confidence interval based on the jackknife variance of the estimator.
    jn_jobs: int
            Perform Jackknife resampling on this many processes.
    fisher: bool
        Use arc hyperbolic tangent (Fisher) normalization for jackknifing (does not seem to work well!!)
    low_bias : {True | False | 0 < p < 1}
        Only use eigenvalues above 0.99 (by default), or the value
    nfft : {None, int, 'auto'}
        Number of FFT points to use (default is next-power-of-2)

    Returns
    -------
    spec : ndarray (..., nfreq, nfreq)
        Dual spectrum (or MSC)
    se : ndarray (..., nfreq, nfreq)
        Standard error (if se==True)

    """

    N = x.shape[-1]
    if dpss is None:
        dpss, eigs = _DPSScache.prepare_dpss(N, NW, low_bias=low_bias)

    mtm = MultitaperEstimator(N, NW, nfft=nfft, dpss=(dpss, eigs))
    xk, weight = mtm.direct_sdfs(x, adaptive_weights=False)
    if not ci:
        fisher = False

    d_spec = _coh_estimator(xk, seed, msc, fisher)
    if ci:
        err = Jackknife(xk, axis=-2, n_jobs=jn_jobs).variance(_coh_estimator, e_args=(seed, msc, fisher))
        if isinstance(ci, float):
            p = 1 - ci
        else:
            p = 1 - 0.95
        t_iv = dists.t.ppf([p / 2, 1 - p / 2], len(xk) - 1)
        conf_iv = np.array([d_spec + t_iv[0] * err, d_spec + t_iv[1] * err])
        if fisher:
            if msc:
                conf_iv = np.tanh(conf_iv)
            else:
                # don't know??
                pass
    if fisher:
        if msc:
            d_spec = np.tanh(d_spec)
        else:
            d_spec.real[:] = np.tanh(d_spec.real)
            d_spec.imag[:] = np.tanh(d_spec.imag)

    if msc:
        d_spec = np.clip(d_spec, 0, 1)
    else:
        d_spec = _circular_clip(d_spec)
    return (d_spec, conf_iv) if ci else d_spec

def _semiherence_estimator(Y, seed, real):
    # Y is (M, K, Nf)
    if seed is not None:
        semih = (Y * Y[seed].conj()).mean(-2)
    else:
        semih = np.einsum('mkn,pkn->mpn', Y, Y.conj())
        K = Y.shape[1]
        semih /= K
    y_auto = np.mean(np.abs(Y)**2, axis=-2)
    if real:
        semih = np.abs(semih.real)
    else:
        semih = np.abs(semih)

    semih *= -1
    semih += 0.5 * y_auto[:, None, :]
    semih += 0.5 * y_auto[None, :, :]
    return semih

def semiherence(
        x, NW, real=True, dpss=None, eigs=None, jackknife=True, jn_jobs=1, se=False,
        low_bias=True, nfft='auto', fmax=0.5, seed=None
):
    """Estimate the coherence spectrum between different signals.

    Parameters
    ----------
    x : ndarray (M, N)
        Vector timeseries of sources
    NW : float
        Time-bandwidth product defining the Slepian tapers
    msc : {True | False}
        Compute the magnitude-square coherence (MSC) of the
        dual spectrum.
    dpss : None
        Precomputed Slepian tapers vk(N,W)
    eigs : None
        Eigenvalues of precomputed tapers
    jackknife : bool
        Use the jackknife to estimate the dual spectrum (or dual MSC)
    jn_jobs: int
            Perform Jackknife resampling on this many processes.
    se : bool
        Also return standard error of the estimator
        (sets jackknife to True)
    low_bias : {True | False | 0 < p < 1}
        Only use eigenvalues above 0.99 (by default), or the value
    nfft : {None, int, 'auto'}
        Number of FFT points to use (default is next-power-of-2)

    Returns
    -------
    spec : ndarray (..., nfreq, nfreq)
        Dual spectrum (or MSC)
    se : ndarray (..., nfreq, nfreq)
        Standard error (if se==True)

    """

    N = x.shape[-1]
    if dpss is None:
        dpss, eigs = _DPSScache.prepare_dpss(N, NW, low_bias=low_bias)

    mtm = MultitaperEstimator(N, NW, nfft=nfft, dpss=(dpss, eigs))
    xk, weight = mtm.direct_sdfs(x, adaptive_weights=False)

    if se:
        jackknife = True

    if jackknife:
        d_spec, err = Jackknife(xk, axis=-2, n_jobs=jn_jobs).estimate(_semiherence_estimator,
                                                                      se=True,
                                                                      e_args=(seed, real))
    else:
        d_spec = _semiherence_estimator(xk, seed, real)
    np.clip(d_spec, 0, d_spec.max(), out=d_spec)
    return (d_spec, err) if se else d_spec

# Freq x Freq mean-square coherence estimator: importable for pickle & spawn
def _dual_msc_estimator(Y):
    # Y is packed with y1, y2 = Y
    y1, y2 = Y
    #d_spec = y1[..., :, :, None] * y2[..., :, None, :].conj()
    d_spec = np.einsum('...ki,...kj->...kij', y1, y2.conj())
    d_spec = np.abs(d_spec.mean(axis=-3))**2
    # get marginal spectral densities
    S1f = np.mean(np.abs(y1)**2, axis=-2)
    S2f = np.mean(np.abs(y2)**2, axis=-2)
    denom = np.einsum('...i,...j->...ij', S1f, S2f)
    return d_spec / denom

def dual_spectrum(
        x1, x2, NW, msc=True, dpss=None, eigs=None, jackknife=True, jn_jobs=1, se=False,
        low_bias=True, nfft='auto', fmax=0.5
):
    """Estimate the dual frequency spectrum between different signals.

    Parameters
    ----------
    x1 : ndarray (..., N)
        (Vector) timeseries from source 1
    x2 : ndarray (..., N)
        (Vector) timeseries from source 2
    NW : float
        Time-bandwidth product defining the Slepian tapers
    msc : {True | False}
        Compute the magnitude-square coherence (MSC) of the
        dual spectrum.
    dpss : None
        Precomputed Slepian tapers vk(N,W)
    eigs : None
        Eigenvalues of precomputed tapers
    jackknife : bool
        Use the jackknife to estimate the dual spectrum (or dual MSC)
    jn_jobs: int
            Perform Jackknife resampling on this many processes.
    se : bool
        Also return standard error of the estimator
        (sets jackknife to True)
    low_bias : {True | False | 0 < p < 1}
        Only use eigenvalues above 0.99 (by default), or the value
    nfft : {None, int, 'auto'}
        Number of FFT points to use (default is next-power-of-2)

    Returns
    -------
    spec : ndarray (..., nfreq, nfreq)
        Dual spectrum (or MSC)
    se : ndarray (..., nfreq, nfreq)
        Standard error (if se==True)

    """

    N = x1.shape[-1]
    assert x2.shape[-1] == N, 'need same length sequences'
    if dpss is None:
        dpss, eigs = _DPSScache.prepare_dpss(N, NW, low_bias=low_bias)

    mtm = MultitaperEstimator(N, NW, nfft=nfft, dpss=(dpss, eigs))
    x1k, weight1 = mtm.direct_sdfs(x1, adaptive_weights=False)
    x2k, weight2 = mtm.direct_sdfs(x2, adaptive_weights=False)

    if se:
        jackknife = True

    if not msc:
        #       x_{1,k}(f1)            x_{2,k}(f2)
        #samps = x1k[..., :, :, None] * x2k[..., :, None, :].conj()
        samps = np.einsum('...ki,...kj->...kij', x1k, x2k.conj())
        if jackknife:
            d_spec, err = Jackknife(samps, axis=-3, n_jobs=jn_jobs).estimate(np.mean, se=True)
        else:
            d_spec = np.mean(samps, axis=-3)
        return (d_spec, err) if se else d_spec

    samps = np.array([x1k, x2k])

    # seems like a slight abuse of the jackknife machinery
    if jackknife:
        msc, err = Jackknife(samps, axis=-2, n_jobs=jn_jobs).estimate(_dual_msc_estimator, se=True)
        np.clip(msc, 0, 1, msc)
    else:
        msc = _dual_msc_estimator(samps)
    return (msc, err) if se else msc

def normalize_spectrogram(x, baseline):
    # normalize based on the assumption of stationarity in baseline
    nf = x.shape[1]
    y = np.log(x[..., baseline]).transpose(1, 0, 2).reshape(nf, -1)
    b_spec = y.mean(-1)
    b_rms = y.std(-1)

    z = (np.log(x).mean(0) - b_spec[:, None]) / b_rms[:, None]
    return z

if __name__ == '__main__':
    arr = np.random.randn(300, 2 ** 15)
    freqs, psds = MultitaperEstimator.psd(arr, NW=5, fs=1.0, adaptive_weights=False, jackknife=True, low_bias=0.9)


class Solution(object):
  def lengthOfLongestSubstring(self, s: str) -> int:
        d = {}
        start = maxlen = 0

        for i, c in enumerate(s):
            if c in d and start <= d[c]:
                start = d[c] + 1
            else:
                maxlen = max(maxlen, i-start + 1)
            d[c] = i
        return maxlen
if __name__ == "__main__":
    ans = Solution()
    print (ans.lengthOfLongestSubstring("abrtaet"))

Longest substring without repeating characters/Length_of_longest_substring_without_repeating_characters.py


from odoo import models, fields, api
from pytz import timezone, utc

class EmployeesAttendanceReportWizard(models.TransientModel):
    _name = 'emp.monthly.att.wizard'

    employee_name = fields.Many2one('hr.employee', string='اسم الموظف')
    start_date = fields.Date(string='تاريخ البداية')
    end_date = fields.Date(string='تاريخ النهاية')

    def fetch_dates_between_2_dates(self, start_date, end_date):
        print('start date ', start_date)
        print('end date ', end_date)
        self._cr.execute(f"""SELECT date_trunc('day', dd):: date FROM generate_series
                ( '{start_date.isoformat()}'::timestamp
                , '{end_date.isoformat()}'::timestamp
                , '1 day'::interval) dd
                """)
        return self._cr.fetchall()

    def fill_view_data(self, employee_id, start_date, end_date):
        dates = self.fetch_dates_between_2_dates(start_date, end_date)
        print('dates ', dates)
        fetched_data = []
        for date1 in dates:
            print('dd', date1[0].isoformat())
            self.env.cr.execute(f"""
                                           select DISTINCT edl.log_date from emp_date_log edl, od_inout oi, hr_employee he
                                           where log_date::date = '{date1[0].isoformat()}'
                                           and oi.id = edl.inout_id
                                           and he.studio_employee_number = oi.emp_deviceno
                                           and he.id = {employee_id.id}
            """)
            logs = self.env.cr.fetchall()
            print('logs ', logs)
            print('type', type(date1[0]), str(date1[0].strftime('%Y-%m-%d')))
            log_1 = f'{logs[0][0].astimezone(timezone("Asia/baghdad")).strftime("%p %I:%M")}' if len(logs) >= 1 else ''
            log_2 = f'{logs[1][0].astimezone(timezone("Asia/baghdad")).strftime("%p %I:%M")}' if len(logs) >= 2 else ''
            log_3 = f'{logs[2][0].astimezone(timezone("Asia/baghdad")).strftime("%p %I:%M")}' if len(logs) >= 3 else ''
            log_4 = f'{logs[3][0].astimezone(timezone("Asia/baghdad")).strftime("%p %I:%M")}' if len(logs) >= 4 else ''
            query = f"""
                        SELECT he.name as emp_name,
                        hd.name as dep_name,
                        '{log_1}' as entry1,
                        '{log_2}' as out1,
                        '{log_3}' as entry2,
                        '{log_4}' as out2,
                        hwet.name as day_type,
                        oi.att_date,
                        oi.date_in,
                        oi.date_out,
                        (hlt.name ->> 'ar_001') as leave_name,
                        oa."diff_Exit",
                        oa.diff_entry
                        from hr_employee he
                        LEFT JOIN hr_work_entry hwe on he.id = hwe.employee_id
                        AND '{(date1[0].isoformat())}' = Date(hwe.date_start)
                        LEFT JOIN hr_department hd ON hd.id = he.department_id
                        LEFT JOIN hr_work_entry_type hwet ON hwet.id = hwe.work_entry_type_id
                        LEFT JOIN od_inout oi ON oi.emp_deviceno = he.studio_employee_number
                        and oi.att_date = '{(date1[0].isoformat())}'
                        LEFT JOIN od_attpayroll oa ON oa.inout = oi.id
                        LEFT JOIN hr_leave hl ON hl.employee_id = he.id
                        and (hl.request_unit_half = TRUE OR hl.request_unit_hours = TRUE)
                        and hl.request_date_from = '{(date1[0].isoformat())}'
                        LEFT JOIN hr_leave_type hlt ON hlt.id = hl.holiday_status_id
                        AND hwet.code <> ANY (ARRAY['MISSION','LATE','LATE2','ERLYIN','ERLYOUT','ERLYIN2',
                        'OVT1','OVT2','OVT3','OVT4'])
                        where hwe.employee_id = '{employee_id.id}'
                        and hwe.state != 'cancelled'
            """
            # query = f"""SELECT he.name as emp_name,
            #                             hd.name as dep_name,
            #                             '{log_1}' as entry1,
            #                             '{log_2}' as out1,
            #                             '{log_3}' as entry2,
            #                             '{log_4}' as out2,
            #                             hwet.name as day_type,
            #                             oi.att_date,
            #                             oi.date_in,
            #                             oi.date_out,
            #                             (hlt.name ->> 'ar_001') as leave_name,
            #                             oa."diff_Exit",
            #                             oa.diff_entry
            #                             from hr_work_entry hwe
            #                             LEFT JOIN hr_employee he on he.id = hwe.employee_id
            #                             AND '{(date1[0].isoformat())}' = Date(hwe.date_start)
            #                             LEFT JOIN hr_department hd ON hd.id = he.department_id
            #                             LEFT JOIN hr_work_entry_type hwet ON hwet.id = hwe.work_entry_type_id
            #                             LEFT JOIN od_inout oi ON oi.emp_deviceno = he.studio_employee_number
            #                             and oi.att_date = '{(date1[0].isoformat())}'
            #                             LEFT JOIN od_attpayroll oa ON oa.inout = oi.id
            #                             LEFT JOIN hr_leave hl ON hl.employee_id = he.id
            #                             and (hl.request_unit_half = TRUE OR hl.request_unit_hours = TRUE)
            #                             and hl.request_date_from = '{(date1[0].isoformat())}'
            #                             LEFT JOIN hr_leave_type hlt ON hlt.id = hl.holiday_status_id
            #                             AND hwet.code <> ANY (ARRAY['MISSION','LATE','LATE2','ERLYIN','ERLYOUT','ERLYIN2',
            #                             'OUT1','OUT2','OUT3','OUT4'])
        	# 							and hwe.employee_id = '{employee_id.id}'
            # """
            self._cr.execute(query)
            rec = self._cr.dictfetchone()
            if rec and rec['date_in'] and rec['date_out']:
                print('in ', rec['date_in'], 'out ', rec['date_out'], rec['date_out'] - rec['date_in'])
            day = ''
            if date1[0].strftime('%A') == 'Sunday':
                day = 'الأحد '
            elif date1[0].strftime('%A') == 'Monday':
                day = 'الإثنين '
            elif date1[0].strftime('%A') == 'Tuesday':
                day = 'الثلاثاء '
            elif date1[0].strftime('%A') == 'Wednesday':
                day = 'الأربعاء '
            elif date1[0].strftime('%A') == 'Thursday':
                day = 'الخميس '
            elif date1[0].strftime('%A') == 'Friday':
                day = 'الجمعة '
            elif date1[0].strftime('%A') == 'Saturday':
                day = 'السبت '
            if rec:
                rec['date'] = day + date1[0].strftime('%m-%d')
                if rec['date_in'] and rec['date_out']:
                    rec['work_hours'] = (rec['date_out'] - rec['date_in'])
                fetched_data.append(rec)
        print('all fetched : ', fetched_data)
        return fetched_data

    def save_options(self):
        # print('report date', self.date.strftime('%Y-%m-%d'))
        fetched = self.fill_view_data(self.employee_name, self.start_date, self.end_date)
        total_hours = 0
        for rec in fetched:
            if rec.get('work_hours'):
                total_hours += rec['work_hours'].seconds / 3600
        total_hours = "{:.2f}".format(total_hours)
        print('total hours ', total_hours)
        data = {
            'allFetched': fetched,
            'employee_name': self.employee_name.name,
            'department_name': self.employee_name.department_id.name,
            'badge_number': self.employee_name.barcode,
            'start_date': self.start_date,
            'end_date': self.end_date,
            'total_hours': total_hours
        }
        print('data ', data)
        return self.env.ref('ALTANMYA_Attendance_Reports.employee_monthly_attendance_action').report_action(self, data)


import torch

from deepafx_st.processors.autodiff.compressor import Compressor
from deepafx_st.processors.autodiff.peq import ParametricEQ
from deepafx_st.processors.autodiff.fir import FIRFilter

class AutodiffChannel(torch.nn.Module):
    def __init__(self, sample_rate):
        super().__init__()

        self.peq = ParametricEQ(sample_rate)
        self.comp = Compressor(sample_rate)
        self.ports = [self.peq.ports, self.comp.ports]
        self.num_control_params = (
            self.peq.num_control_params + self.comp.num_control_params
        )

    def forward(self, x, p, sample_rate=24000, **kwargs):

        # split params between EQ and Comp.
        p_peq = p[:, : self.peq.num_control_params]
        p_comp = p[:, self.peq.num_control_params :]

        y = self.peq(x, p_peq, sample_rate)
        y = self.comp(y, p_comp, sample_rate)

        return y


# --------------
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

# code starts here
df = pd.read_csv(path)
#print(df.info())
p_a = ((df['fico'] > 700).sum())/len(df)
print(p_a)
p_b = ((df['purpose'] == 'debt_consolidation').sum())/len(df)
print(p_b)
df1 = df[df['purpose']== 'debt_consolidation']
p_a_b = df1[df1['fico'].astype(float) >700].shape[0]/df1.shape[0]
print(p_a_b)
result = p_a_b == p_a
print(result)
# code ends here

# --------------
# code starts here
prob_lp = (df['paid.back.loan'] == 'Yes').sum()/len(df)
print(prob_lp)
prob_cs = (df['credit.policy'] == 'Yes').sum()/len(df)
print(prob_cs)
new_df = df[df['paid.back.loan'] == 'Yes']
prob_pd_cs = (new_df['credit.policy'] == 'Yes').sum()/len(new_df)
print(prob_pd_cs)
bayes = (prob_pd_cs*prob_lp)/prob_cs
print(bayes)
# code ends here

# --------------
# code starts here
plt.bar(df['purpose'],df['purpose'].index)
df1 = df[df['paid.back.loan'] == 'No']
df1
plt.bar(df1['purpose'],df1['purpose'].index)
# code ends here

# --------------
# code starts here
inst_median = df['installment'].median()
print(inst_median)
inst_mean = df['installment'].mean()
print(inst_mean)
plt.hist(df['installment'])
plt.hist(df['log.annual.inc'])
# code ends here


from plugp100.api.hub.hub_device import HubDevice

from plugp100.common.functional.tri import Try
from plugp100.requests.tapo_request import TapoRequest
from plugp100.responses.components import Components
from plugp100.responses.hub_childs.t31x_device_state import (
    T31DeviceState,
    TemperatureHumidityRecordsRaw,
)

class T31Device:
    def __init__(self, hub: HubDevice, device_id: str):
        self._hub = hub
        self._device_id = device_id

    async def get_device_state(self) -> Try[T31DeviceState]:
        return (
            await self._hub.control_child(self._device_id, TapoRequest.get_device_info())
        ).flat_map(T31DeviceState.from_json)

    async def get_temperature_humidity_records(
        self,
    ) -> Try[TemperatureHumidityRecordsRaw]:
        request = TapoRequest.get_temperature_humidity_records()
        response = await self._hub.control_child(self._device_id, request)
        return response.flat_map(TemperatureHumidityRecordsRaw.from_json)

    async def get_component_negotiation(self) -> Try[Components]:
        return (
            await self._hub.control_child(
                self._device_id, TapoRequest.component_negotiation()
            )
        ).map(Components.try_from_json)


###########
# Imports #
###########

""" Global """
import json
import numpy as np
from tqdm import tqdm

""" Local """
import constants

##############
# Classifier #
##############

class BaselineClassifier(object):
    def __init__(self, catalog_images_paths, params={}):
        self.catalog_images_paths = sorted(catalog_images_paths)
        self.get_params(params)

    def get_params(self, params):
        # <-- General --> #
        self.verbose = params.get("verbose", constants.VERBOSE)
        self.feature_extractor = params.get("feature_extractor", constants.DEFAULT_FEATURE_EXTRACTOR)
        self.matcher_index_params = params.get("matcher_index_params", constants.DEFAULT_MATCHER_INDEX_PARAMS)
        self.matcher_query_params = params.get("matcher_query_params", constants.DEFAULT_MATCHER_QUERY_PARAMS)
        self.background_label = params.get("background_label", constants.BACKGROUND_LABEL)
        self.image_size = params.get("image_size", constants.DEFAULT_CLASSIFIER_IMAGE_SIZE)
        self.keypoint_stride = params.get("keypoint_stride", constants.DEFAULT_CLASSIFIER_KEYPOINT_STRIDE)
        self.keypoint_sizes = params.get("keypoint_sizes", constants.DEFAULT_CLASSIFIER_KEYPOINT_SIZES)

        # <-- Classifier Custom --> #
        self.matcher_path_custom = params.get("matcher_path", constants.DEFAULT_CLASSIFIER_CUSTOM_MATCHER_PATH)
        self.force_matcher_compute = params.get("force_matcher_compute", constants.DEFAULT_CLASSIFIER_CUSTOM_FORCE_MATCHER_COMPUTE)
        self.k_nn_custom = params.get("k_nn", constants.DEFAULT_CLASSIFIER_CUSTOM_K_NN)
        self.score_sigma = params.get("sigma", constants.DEFAULT_CLASSIFIER_CUSTOM_SCORE_SIGMA)

        # <-- Classifier Deep --> #
        self.matcher_path_deep = params.get("matcher_path", constants.DEFAULT_CLASSIFIER_DEEP_MATCHER_PATH)
        self.k_nn_deep = params.get("k_nn", constants.DEFAULT_CLASSIFIER_DEEP_K_NN)
        self.triplet_margin = params.get("triplet_margin", constants.DEFAULT_CLASSIFIER_DEEP_TRIPLET_MARGIN)
        self.n_train_epochs = params.get("n_train_epochs", constants.DEFAULT_CLASSIFIER_DEEP_TRAIN_EPOCHS)
        self.batch_size = params.get("batch_size", constants.DEFAULT_CLASSIFIER_DEEP_TRAIN_BATCH_SIZE)
        self.augment_factor = params.get("augment_factor", constants.DEFAULT_CLASSIFIER_DEEP_TRAIN_AUGMENT_FACTOR)
        self.model_path = params.get("model_path", constants.DEFAULT_CLASSIFIER_DEEP_MODEL_PATH)
        self.force_train = params.get("force_train", constants.DEFAULT_CLASSIFIER_DEEP_FORCE_TRAIN)

        # <-- Classifier BoW --> #
        self.vocab_size = params.get("vocab_size", constants.DEFAULT_CLASSIFIER_BOW_VOCAB_SIZE)
        self.vocab_path = params.get("vocab_path", constants.DEFAULT_CLASSIFIER_BOW_VOCAB_PATH)
        self.catalog_features_path = params.get("catalog_features_path", constants.DEFAULT_CLASSIFIER_BOW_CATALOG_FEATURES_PATH)
        self.force_vocab_compute = params.get("force_vocab_compute", constants.DEFAULT_CLASSIFIER_BOW_FORCE_VOCAB_COMPUTE)
        self.force_catalog_features_compute = params.get("force_catalog_features_compute", constants.DEFAULT_CLASSIFIER_BOW_FORCE_CATALOG_FEATURES_COMPUTE)

    def predict_query(self, query):
        return {}

    def predict_query_batch(self, query_paths):
        iterator = query_paths
        if self.verbose: iterator = tqdm(iterator, desc="Query prediction")

        results = {}
        for query_path in iterator:
            query_id = query_path.split("/")[-1]
            results[query_id] = self.predict_query(query_path)

        return results

    def compute_top_k_accuracy(self, ground_truth, predictions, k):
        nb_correct = counter = 0
        for img_id in ground_truth:
            if ground_truth[img_id] != self.background_label and img_id in predictions:
                predicted_labels = sorted(predictions[img_id].keys(), key=lambda x: predictions[img_id][x], reverse=True)[:k]
                if ground_truth[img_id] in predicted_labels:
                    nb_correct += 1
                counter += 1
        if counter == 0:
            return 0.0
        return 1. * nb_correct / counter

    def compute_metrics(self, query_paths, ground_truth_path):
        with open(ground_truth_path, "r") as f: ground_truth = json.load(f)
        predictions = self.predict_query_batch(query_paths)
        top1 = self.compute_top_k_accuracy(ground_truth, predictions, 1)
        top3 = self.compute_top_k_accuracy(ground_truth, predictions, 3)
        top5 = self.compute_top_k_accuracy(ground_truth, predictions, 5)
        return top1, top3, top5


import gmpy2
import binascii

# 利用Pollard p-1算法可以快速分解出p，q相差太大的n
# 适用于p-1或q-1能够被小素数整除的情况
def cald(n):
    B = 2 ** 20
    a = 2
    for i in range(2, B + 1):
        a = pow(a, i, n)
        d = gmpy2.gcd(a - 1, n)
        if (d >= 2) and (d <= (n - 1)):
            q = n // d
            n = q * d
    return d

def pollard():
    index_list = [2, 6, 19]
    plaintext = []
    for i in range(3):
        N = int(N_list[index_list[i]], 16)
        c = int(c_list[index_list[i]], 16)
        e = int(e_list[index_list[i]], 16)
        p = cald(N)
        # print("p of " + str(index_list[i]) + " is : " + str(p))
        q = N // p
        phiOfFrame = (p - 1) * (q - 1)
        d = gmpy2.invert(e, phiOfFrame)
        m = gmpy2.powmod(c, d, N)
        plaintext.append(binascii.a2b_hex(hex(m)[2:]))
    return plaintext

if __name__ == "__main__":
    N_list = []
    c_list = []
    e_list = []
    for i in range(21):
        with open("frame_set/Frame" + str(i), "r") as f:
            tmp = f.read()
            N_list.append(tmp[0:256])
            e_list.append(tmp[256:512])
            c_list.append(tmp[512:768])
    # 使用Pollard p-1分解法爆破得出Frame2,Frame6,Frame19的模数N可在较短时间内成功分解
    plaintext2_6_19 = pollard()
    print(plaintext2_6_19)


import os
os.system("cls")

horas = int (input ("ingrese las horas trabajadas: "))
tarifa = int (input ("ingrese tarifa horaria: "))

if horas <= 48:
    sueldoBruto = horas * tarifa
elif horas >= 49:
    recarga = (horas * tarifa) * 0.20
    sueldoBruto = (horas * tarifa) + recarga

if sueldoBruto >= 1500: descuento = sueldoBruto * 0.11
elif sueldoBruto <= 1499: descuento = 0

totalApagar = sueldoBruto - descuento

print (f"horas trabajadas: {(horas)}")
print (f"pago por hora: {(tarifa)}")
print (f"sueldo Bruto: {(sueldoBruto)}")
print (f"descuento: {(descuento)}")
print (f"total a pagar por la empresa: {(totalApagar)}")
print()


# Incluir bibliotecas/módulos

import os #para chamar comandos do sistema
import random

# Funções

playlist = []

baseDeDados = [
    ['Fa fe fi fo Funk','Anira', 'Funk', 2019, '3:05'],
    [ 'Sofrência de programar', 'Ada & Turing',	'Sertanejo', 1998, '2:58' ],
    [ 'Rockn Rolo', 'The Buns','Rock',	1984, '4:01' ],
    [ 'Grifinoria Girls', 'Katy Potter', 'Pop',	2017, '2:25' ],
    ['Outra musica', 'Anira', 'Funk', 2019, '3:05']
]

def ExibirBaseDados():
    for l in range(len(baseDeDados)):
            print(f"{l+1} - {baseDeDados[l]}")
    print()

def Iniciar():
    print('Iniciando o programa...')
    # Carregar ou ler dados de entrada

ExibirBaseDados()

def EscolherOpcao():
    print('1 - Visualizar base de dados')
    print('2 - Montar playlist')
    print('3 - Visualizar playlist')
    print('4 - Embaralhar playlist')
    print('5 - Duração total da playlist')
    print('0 - Sair\n')
    resposta = input('Escolha uma opção: ')
    os.system('cls'if os.name== 'nt'else'clear')
    return resposta

def ExecutarAcao1():
    print('Visualizando base de dados...\n\n')
    ExibirBaseDados()

def ExecutarAcao2():
    print('Montando playlist...\n\n')
    playlist.clear()
    baseDeDadosTemp = baseDeDados[:]
    encerrar = False
    while not encerrar:
        for l in range(len(baseDeDadosTemp)):
            print(f"{l+1} - {baseDeDadosTemp[l]}")
        escolhaMusica = (int(input(f"\nQual músicas deseja adicionar? (1 - {len(baseDeDadosTemp)}): ")))
        if escolhaMusica == 0:
            encerrar = True
        else:
            playlist.append(baseDeDadosTemp.pop(escolhaMusica-1))
        os.system('cls'if os.name== 'nt'else'clear')
        print(f"\nMúsica {baseDeDadosTemp[escolhaMusica-1][0]} adicionada\n")
    return playlist

def ExecutarAcao3(playlistMontada):
    print('Visualizando playlist...')
    for l in range(len(playlistMontada)):
            print(f"{l+1} - {playlistMontada[l]}")
    print()

def ExecutarAcao4():
    print('Playlist embaralhada...\n\n')

def ExecutarAcao5():
    print(playlist)
    minutos = 0
    segundos = 0
    for l in range(len(playlist)):
        minutos += int(playlist[l][4][:1])
        segundos += int(playlist[l][4][2:])
    print(minutos)
    print(segundos)
    saldo = segundos % 60
    while segundos/60 > 1:
        segundos -= 60
        minutos += 1

    print("Duração total - {}:{}".format(minutos, saldo))

def Executar():
    terminarExecucao = False
    while not terminarExecucao:
        acaoUsuario = EscolherOpcao()
        if (acaoUsuario == '1'):
            ExecutarAcao1()
        elif (acaoUsuario == '2'):
            playlist = ExecutarAcao2()
        elif (acaoUsuario == '3'):
            ExecutarAcao3(playlist)
        elif (acaoUsuario == '4'):
            ExecutarAcao4()
        elif (acaoUsuario == '5'):
            ExecutarAcao5()
        elif (acaoUsuario == '0'):
            terminarExecucao = True
        else:
            print('Escolha invalida! Tente de novo.')

def Finalizar():
    print('O programa foi encerrado!')
    # Salvar dados, encerrar processos etc

# Programa principal
if __name__ == '__main__':
    Iniciar()
    Executar()
    Finalizar()


# Importing Third Party Packages

from django.urls import path

# Local Imports

from .views import about, home, details

# Url Patterns Registration

urlpatterns = [
    path('about/', about, name='about'),
    path('', home, name='home'),
    path('details/', details, name='details'),
]


#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Time    : 2019/11/12 9:34
# @Author  : LI Dongdong
# @FileName: 234. Palindrome Linked List.py
''''''
'''
题目分析
1.要求：Given a singly linked list, determine if it is a palindrome.
    Input: 1->2
    Output: false
2.理解：判断链表是否是回文
3.类型：判断链表结构
4.方法及方法分析：list储存判断法 reverse-TwoPointer Method
time complexity order: reverse-TwoPointer O(N) = Method list储存判断法 O(N)
space complexity order: reverse-TwoPointer O(1) < Method list储存判断法 O(N)
'''

'''
list储存判断法
思路：转换成list储存，然后通过比较头尾数值，遍历整个list进行判断
方法：
    traverse the linkedlist, save the node value in node_list #tO(N),sO(N)
    compare the node_list front and end value for all the value using iteration from 0 to mid index value #tO(N)
        if all front == end ->return True
        else: return False
边界条件：LinkedList = None/only one, return True/True
time complex: O(N)
space complex: O(N)
易错点：从头尾同时向中间迭代时，头部序号的起始是0，尾部的序号起始是-1不为-i，而是-(i+1)，故node_list[i] != node_list[-(i+1)]
'''
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def isPalindrome(self, head):
        if head is None or head.next is None:
            return True #edge case

        node_list = []
        while head:
            node_list.append(head.val)
            head = head.next

        for i in range(len(node_list)//2):
            if node_list[i] != node_list[-(i+1)]:
                return False
        return True

'''
reverse-TwoPointer Method
Idea：use 双指针 in linkedlist so that in one traverse, we can scan the front and later part of linkedlist at the same time
Method：
    set slow pointer -> one node step, fast-p ->two step. traverse pointer, so when fast-p arrive at end, slow arrive at mid.
        Meanwhile for slow-p, reverse its scaned node, set new_head. #tO(N/2) sO(1)
    traverse new_head and slow.next at the same time. compare new_head.val and slow.next.val, if they are equal, return True, else return false #tO(N/2) sO(1)
边界条件：LinkedList = None/only one, return True/True; length is odd or even
time complex: O(N)
space complex: O(1)
易错点：链表迭代在写while的时候，一定要同时写next！！！不然会忘记！/ node 是even，slow-p要next；是odd，slow-p不用next
'''
class ListNode:
    def __init__(self, x):
        self.val = x
        self.next = None

class Solution:
    def isPalindrome(self, head):
        # edge case
        if head is None or head.next is None:
            return True

        slow_p = fast_p = head
        new_head = None
        while fast_p and fast_p.next:
            # fast pointer
            fast_p = fast_p.next.next
            # slow pointer and reverse it
            next_node = slow_p.next
            slow_p.next = new_head
            new_head = slow_p
            slow_p = next_node

        if fast_p is None:
            # compare front part with later part
            while new_head:
                if new_head.val != slow_p.val:
                    return False
                new_head = new_head.next
                slow_p = slow_p.next
            return True
        else:
            # slow pointer from mid to later part beginning
            slow_p = slow_p.next
            # compare front part with later part
            while new_head:
                if new_head.val != slow_p.val:
                    return False
                new_head = new_head.next
                slow_p = slow_p.next
            return True

'''
test case
'''
A1 = ListNode(0)
A2 = ListNode(0)
A3 = ListNode(3)
A1.next = A2

x = Solution()
x.isPalindrome(A1)

链表/判断链表回文/234. Palindrome Linked List.py


# class LLNode:
#     def __init__(self, val, next=None):
#         self.val = val
#         self.next = next
class Solution: # version 1
    def solve(self, node, k):
        head, past = node, None
        while head:
            head.next, past, head = past, head, head.next
        c = 0
        while past:
            if k == c:
                return past.val
            c+=1
            past = past.next

"""
In here, node is last element which we can keep in memory
and if current index is greater than k we mode last index to one right
"""
class Solution: # best solution
    def solve(self, node, k):
            t, i = node, 0
            while t:
                if i > k:
                    node = node.next
                i += 1
                t = t.next
            return node.val


# https://programmers.co.kr/learn/courses/30/lessons/12977
def solution(nums):
    answer = 0

    for i in range(len(nums)):
        for j in range(i + 1, len(nums)):
            for k in range(j + 1, len(nums)):
                temp = nums[i] + nums[j] + nums[k]
                if temp > 2:
                    for _ in range(2, temp // 2 + 1):
                        if temp % _ == 0:
                            break
                    else:
                        answer += 1

    return answer

print(solution([1, 2, 3, 4]))
print(solution([1, 2, 7, 6, 4]))


"""
Implementation of the user_profile_uploadphoto function.
H11A-quadruples, April 2020.
"""

import imghdr
from io import BytesIO
import requests
from PIL import Image
from error import AccessError, InputError
from database.database import AUTH_DATABASE, CHANNELS_DATABASE
from database.helpers_auth import is_token_valid, find_u_id, generate_code
from constants import PFP_FOLDER, DEFAULT_PFP

def user_profile_uploadphoto(token, img_url, x_start, y_start, x_end, y_end):
    """
    Given a url 'img_url' of a .jpg from the Internet, crops the image
    within bounds (x_start, y_start) and (x_end, y_end) and sets the user's
    profile picture to this cropped image.

    Args:
        token (str): Token of user who is uploading their photo.
        img_url (str): URL to the image on the Internet.
        x_start (int): left pixel to start crop.
        y_start (int): upper pixel to start crop.
        x_end (int): right pixel to end crop.
        y_end (int): lower pixel to end crop.
    Raises:
        AccessError: if token is invalid.
        InputError: if img_url does not return a HTTP status code 200.
        InputError: if any of x_start, y_start, x_end, y_end are out of image bounds.
        InputError: if image is not a JPG.
    Returns:
        Empty dictionary.
    """
    ## Check for AccessErrors
    if not is_token_valid(token):
        raise AccessError(description="Token is not a valid token")

    ## Check for InputErrors
    response = requests.get(img_url)
    if response.status_code != 200:
        raise InputError(description="img_url did not return a 200 HTTP status")
    if imghdr.what(BytesIO(response.content)) != "jpeg":
        raise InputError(description="Image uploaded is not a JPG")

    ## Set the name of the image, eg. "jdH5ixbma3.jpg" and the path to
    ## the image relative to src, eg. "profile_pics/jdH5ixbma3.jpg"
    img_code = generate_code() if img_url != DEFAULT_PFP else "default"
    img_name = f"{img_code}.jpg"
    img_path = f"{PFP_FOLDER}/{img_name}"

    ## Open the image and check for InputErrors
    image = Image.open(BytesIO(response.content))
    width, height = image.size
    if x_start < 0 or x_end > width or y_start < 0 or y_end > height:
        raise InputError(description="Bounds are not within the dimensions of the image")

    ## Crop the image and save it
    image_cropped = image.crop((x_start, y_start, x_end, y_end))
    image_cropped.save(img_path)
    image.close()

    ## Set the url that contains the image and post the image to that url
    from port_settings import BASE_URL
    profile_img_url = f"{BASE_URL}/imgurl/{img_name}"
    requests.get(f"{profile_img_url}")

    ## Update the user's profile_img_url in the AUTH_DATABASE
    u_id = find_u_id(token)
    auth_data = AUTH_DATABASE.get()
    for user in auth_data["registered_users"]:
        if user["u_id"] == u_id:
            user["profile_img_url"] = profile_img_url
            break
    AUTH_DATABASE.update(auth_data)

    ## Update the user's profile_img_url in the CHANNELS_DATABASE
    channel_data = CHANNELS_DATABASE.get()
    for channel in channel_data["channels"]:
        for member in channel["all_members"]:
            if member["u_id"] == u_id:
                member["profile_img_url"] = profile_img_url
                break
        for owner in channel["owner_members"]:
            if owner["u_id"] == u_id:
                owner["profile_img_url"] = profile_img_url
                break
    CHANNELS_DATABASE.update(channel_data)
    return {}


""" Пользователь вводит месяц в виде целого числа от 1 до 12.
Сообщить к какому времени года относится месяц (зима, весна, лето, осень).
Напишите решения через list и через dict."""

# Решение через список с защитой от неверного ввода,
# до тех пор пока не введут правильное значение.

month = int(input("Введите число месяца 1 по 12 : "))
mlist = ["зима", "весна", "лето", "осень"]
while True:
    if month > 12 or month <= 0:
        print(f"\tНЕВЕРНОЕ число!!! \n\tВведите в диапозоне 1 по 12!")
        month = int(input("Введите число месяца 1 по 12 : "))
        continue
    mlist = ["зима", "весна", "лето", "осень"]
    if month == 12 or month >=1 and month < 3:
        print(f"\tВаше число месяца #{month}  это '{mlist[0]}'")
        break
    elif month >= 3 and month < 6:
        print(f"\tВаше число месяца #{month} это '{mlist[1]}'")
        break
    elif month >= 6 and month < 9:
        print(f"\tВаше число месяца #{month} это '{mlist[2]}'")
        break
    elif month >= 9 and month < 12:
        print(f"\tВаше число месяца #{month} это '{mlist[3]}'")
        break

# Решение через словарь
seasons = {1: "зима", 2: "весна", 3: "лето", 4: "осень"}
months_dict = {1: seasons.get(1), 2: seasons.get(1), 3: seasons.get(2),
               4: seasons.get(2), 5: seasons.get(2), 6: seasons.get(3),
               7: seasons.get(3), 8: seasons.get(3), 9: seasons.get(4),
               10: seasons.get(4), 11: seasons.get(4), 12: seasons.get(1)}
value = months_dict[month] \
    if month in months_dict \
    else print(f"Ваше число месяца {month} не встречается во времена года")
print(f"\tПоиска по словарю сезонов, по числу месяца #{month} Это: '{value}'")


# multiple transcript
from unique_transcripts import unique_transcript
from statistics import mean
from merge_overlapping_intervals import mergeIntervals

def is_multiple_transcript(transcript_a, transcript_a_allfeatures_positions, transcript_a_aligned_b_genes):
    unique_count=0
    calls = []
    genes = []
    scores = []
    for gene in transcript_a_aligned_b_genes.keys():
        #print(transcript_a,  transcript_a_aligned_b_genes[gene]['perc_identity'])
        interval_list=[[a,b] for a, b in transcript_a_aligned_b_genes[gene][transcript_a]['aln_pos']]  # b+1 to check if adding one base overlaps with nearest interval
        score = mean(transcript_a_aligned_b_genes[gene]['perc_identity'])
        transcript_b_positions = mergeIntervals(sorted(interval_list) )

        call = unique_transcript(transcript_a_allfeatures_positions, transcript_b_positions)
        if call == 'exact_match' or call == 'all_jxn_match' or call == 'target_contained':
            unique_count +=1
            calls.append(call)
            genes.append(gene)
            scores.append(score)

    return unique_count, calls, genes, scores


from twisted.internet.defer import Deferred, gatherResults

from viff.test.util import RuntimeTestCase, protocol

class ProgramCounterTest(RuntimeTestCase):
    """Program counter tests."""

    @protocol
    def test_initial_value(self, runtime):
        self.assertEquals(runtime.program_counter, [0, 0])

    @protocol
    def test_synchronize(self, runtime):
        """Test whether synchronize increases the program counter.

        Every synchronize operation should have its unique program
        counter."""
        self.assertEquals(runtime.program_counter, [0, 0])
        runtime.synchronize()
        self.assertEquals(runtime.program_counter, [0, 1])
        runtime.synchronize()
        self.assertEquals(runtime.program_counter, [0, 2])

    @protocol
    def test_callback(self, runtime):
        """Test a scheduled callback.

        The callback should see the program counter as it was when the
        callback was added and not the current value.
        """

        def verify_program_counter(_):
            # The callback is run with its own sub-program counter.
            self.assertEquals(runtime.program_counter, [0, 1, 0])

        d = Deferred()

        self.assertEquals(runtime.program_counter, [0, 0])

        # Scheduling a callback increases the program counter.
        runtime.schedule_callback(d, verify_program_counter)
        self.assertEquals(runtime.program_counter, [0, 1])

        # Now trigger verify_program_counter.
        d.callback(None)

    @protocol
    def test_multiple_callbacks(self, runtime):

        d1 = Deferred()
        d2 = Deferred()

        def verify_program_counter(_, count):
            self.assertEquals(runtime.program_counter, [0, count, 0])

        def method_a(runtime):
            # No calls to schedule_callback yet.
            self.assertEquals(runtime.program_counter, [0, 0])

            runtime.schedule_callback(d1, verify_program_counter, 1)
            runtime.schedule_callback(d2, verify_program_counter, 2)

        method_a(runtime)

        # Trigger verify_program_counter.
        d1.callback(None)
        d2.callback(None)

        return gatherResults([d1, d2])

    @protocol
    def test_multi_send(self, runtime):
        """Test sending multiple times from a Runtime method."""

        # First send a couple of times to everybody.
        for peer_id in range(1, self.num_players+1):
            if peer_id != runtime.id:
                pc = tuple(runtime.program_counter)
                runtime.protocols[peer_id].sendData(pc, 42, "100")
                runtime.protocols[peer_id].sendData(pc, 42, "200")
                runtime.protocols[peer_id].sendData(pc, 42, "300")

        # Then receive the data.
        deferreds = []
        for peer_id in range(1, self.num_players+1):
            if peer_id != runtime.id:
                d100 = Deferred().addCallback(self.assertEquals, "100")
                d200 = Deferred().addCallback(self.assertEquals, "200")
                d300 = Deferred().addCallback(self.assertEquals, "300")
                runtime._expect_data(peer_id, 42, d100)
                runtime._expect_data(peer_id, 42, d200)
                runtime._expect_data(peer_id, 42, d300)
                deferreds.extend([d100, d200, d300])

        return gatherResults(deferreds)


import sqlite3,random,ssl,smtplib,hashlib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email import encoders
import tkinter as tk
from tkinter import messagebox
class login():
    def __init__(self):
        self.durum=True
        self.con=sqlite3.connect("film.db")
        self.yasakliKarakterler = ["!", "'", "#", "^", "+", "$", "%", "&", "/", "{", "(", "[", ")", "]", "=", "}", "?","*", "-", "|", "_", '"', "'", "<", ">"]
        self.sifreYasakliKarakter=["ı","İ","ö","Ö","ü","Ü","ş","Ş"]
        self.telKontrol=["0","1","2","3","4","5","6","7","8","9"]
    def karakterKontrol(self, kontrolEdilecekKelime):
        for harf in kontrolEdilecekKelime:
            if harf in self.yasakliKarakterler:
                return True
    def kullanicAdiKontrol(self,kullaniciAdi):
        sistemdekiKullaniiciAdlari=list(self.con.execute("select KullaniciAdi from kullanicilar"))
        for i in sistemdekiKullaniiciAdlari:
            if kullaniciAdi==i[0]:
                return True
    def isimKontrol(self,isim):
        for i in isim:
            if i[0] in self.telKontrol:
                return True
    def aktivasyon(self,isim,alici):
        self.hataliGiris = 0
        def gonder():
            if aktivasyon.get() == self.aktivasyonKodu:
                aktivasyonForm.destroy()
                return True
            else:
                self.hataliGiris += 1
                hata = messagebox.showerror("Uyarı", "Aktivasyon kodu hatali girildi Lütfen tekrar deneyiniz")
                if self.hataliGiris == 6:
                    hata = messagebox.showerror("Uyarı", "Aktivasyon kodunu 6 kere hatalı girdiğiniz için sizi menüye aktarıyorum")
                    aktivasyonForm.destroy()
                    self.program()
        def yeniAktivasyon():
            self.aktivasyonMail(isim,alici)
        aktivasyonForm=tk.Tk()
        aktivasyonForm.geometry("500x300")
        aktivasyonForm.title("Mail Aktivasyon")

        aktivasyon=tk.IntVar()

        mesaj=tk.Label(aktivasyonForm,text="Mailinize gelen aktivasyon kodunu giriniz").place(x=10,y=10)
        aktivasyonLabel=tk.Label(aktivasyonForm,text="Aktivasyon Kodu:").place(x=10,y=35)
        aktivasyonEntry=tk.Entry(aktivasyonForm,textvariable=aktivasyon).place(x=120,y=35)
        aktivasyonBtn=tk.Button(aktivasyonForm,text="Gonder",command=gonder).place(x=195,y=60)
        yeniAktivasyonKoduBtn=tk.Button(aktivasyonForm,text="Kodu Tekrar Gonder",command=yeniAktivasyon).place(x=70,y=60)
        aktivasyonForm.mainloop()
    def aktivasyonMail(self,isim,alici):
        self.aktivasyonKodu = random.randint(10000, 99999)
        kullanici = ""
        sifre = ""
        context = ssl.create_default_context()
        port = 465
        host = 'smtp.gmail.com'
        posta = MIMEMultipart()
        posta['From'] = kullanici
        posta['To'] = alici
        posta['Subject'] = "BurakFilm Aktivasyon Kodu"
        mesaj =f"Merhabalar {isim},\n\naktivasyon kodun:{self.aktivasyonKodu}\n\nİyi Günler"

        posta.attach(MIMEText(mesaj, 'plain'))

        eposta_sunucu = smtplib.SMTP_SSL(host=host, port=port, context=context)
        eposta_sunucu.login(kullanici, sifre)
        eposta_sunucu.sendmail(kullanici, alici, str(posta))
    def sifreKarakterKontrol(self,sifre):
        for i in sifre:
            if i[0] in self.sifreYasakliKarakter:
                return True
    def telefonKontrol(self,tel):
        for i in tel:
            if i[0] not in self.telKontrol:
                return True
    def kullaniciKayit(self):
        def kayitKontrol():
            hataListesi = []
            if kullaniciAdi.get()=="":
                hataListesi.append('Kullanici Adi bos birakilamaz')
            if self.karakterKontrol(kullaniciAdi.get()):
                hataListesi.append(f"Kullanici Adi bu karakterleri iceremez {self.yasakliKarakterler}")
            if self.kullanicAdiKontrol(kullaniciAdi.get()):
                hataListesi.append("Bu kullanici Adi daha onceden alinmis")
            if len(sifre.get())<6:
                hataListesi.append("Sifre en az 6 karakterden olusmalidir")
            if self.sifreKarakterKontrol(sifre.get()):
                hataListesi.append(f"Sifrede bu karakterleri kullanamazsiniz {self.sifreYasakliKarakter}")
            if isim.get()=="":
                hataListesi.append("isim bos birakilamaz")
            if self.karakterKontrol(isim.get()):
                hataListesi.append(f"isim bu karakterleri iceremez {self.yasakliKarakterler}")
            if soyadi.get()=="":
                hataListesi.append("soyadi bos birakilamaz")
            if self.karakterKontrol(soyadi.get()):
                hataListesi.append(f"soyadi bu karakterleri iceremez {self.yasakliKarakterler}")
            if eposta.get()=="":
                hataListesi.append("eposta bos birakilamaz")
            if len(tel.get())!=11:
                hataListesi.append("telefon 11 haneli olmak zorundadir!")
            if self.telefonKontrol(tel.get()):
                 hataListesi.append(f"Telefon sadece bu karakterleri içerebilir {self.telKontrol}")
            dogumGunu = gun.get(), ay.get(), yil.get()
            if len(hataListesi)==0:
                kullaniciKayitForm.destroy()
                self.aktivasyonMail(isim.get(), eposta.get())
                self.aktivasyon(isim.get(), eposta.get())
                SHA256Sifre = hashlib.sha256(sifre.get().encode("ascii")).hexdigest()
                self.con.execute("insert into kullanicilar (KullaniciAdi,Sifre,Isim,Soyadi,EPosta,Telefon,DogumTarihi) values (?,?,?,?,?,?,?)",(kullaniciAdi.get(), SHA256Sifre, isim.get().lower().title(), soyadi.get().lower().title(), eposta.get(), tel.get(), str(dogumGunu)))
                self.con.commit()
            if len(hataListesi)!=0:
                hatalar = messagebox.showerror("Uyarı", str(hataListesi).split("}")[0].split("{")[0])
        kullaniciKayitForm=tk.Tk()
        kullaniciKayitForm.geometry("500x300")
        kullaniciKayitForm.title('Kullanıcı Kayıt')

        kullaniciAdi = tk.StringVar()
        sifre = tk.StringVar()
        isim = tk.StringVar()
        soyadi = tk.StringVar()
        eposta = tk.StringVar()
        tel = tk.StringVar()
        gun=tk.IntVar()
        ay = tk.IntVar()
        yil = tk.IntVar()

        kullaniciAdiLabel=tk.Label(text='Kullanıcı Adı:').place(x=10,y=10)
        kullaniciAdiEntry=tk.Entry(kullaniciKayitForm,textvariable=kullaniciAdi).place(x=100,y=10)

        sifreLabel=tk.Label(text='Sifre:').place(x=10,y=35)
        sifreEntry = tk.Entry(kullaniciKayitForm, textvariable=sifre,show='*').place(x=100, y=35)

        isimLabel = tk.Label(text='İsim:').place(x=10, y=60)
        isimEntry = tk.Entry(kullaniciKayitForm, textvariable=isim).place(x=100, y=60)

        soyadiLabel = tk.Label(text='Soyadi:').place(x=10, y=85)
        soyadiEntry = tk.Entry(kullaniciKayitForm, textvariable=soyadi).place(x=100, y=85)

        epostaLabel = tk.Label(text='e-posta:').place(x=10, y=110)
        epostaEntry = tk.Entry(kullaniciKayitForm, textvariable=eposta).place(x=100, y=110)

        telLabel = tk.Label(text='telefon:').place(x=10, y=135)
        telEntry = tk.Entry(kullaniciKayitForm, textvariable=tel).place(x=100, y=135)

        dogumGunu = tk.Label(text='Dogum Gününüzü Giriniz').place(x=10, y=160)
        dogumGun = tk.Label(text='Gun').place(x=10, y=180)
        dogumGunSpinBox = tk.Spinbox(kullaniciKayitForm,from_=1,to=31,width=2,textvariable=gun).place(x=40,y=182)
        dogumAy = tk.Label(text='Ay').place(x=70, y=180)
        dogumAySpinBox = tk.Spinbox(kullaniciKayitForm, from_=1, to=12, width=2,textvariable=ay).place(x=90, y=182)
        dogumYil = tk.Label(text='Yıl').place(x=120, y=180)
        dogumYilSpinBox = tk.Spinbox(kullaniciKayitForm, from_=1900, to=2021, width=4,textvariable=yil).place(x=140, y=182)

        gonder=tk.Button(kullaniciKayitForm,text="Kayıt ol",command=kayitKontrol).place(x=175,y=210)
        kullaniciKayitForm.mainloop()
    def kullaniciGirisKontrol(self,giris):
        veriTabaniKullanicilari = list(self.con.execute("select KullaniciAdi,Sifre from kullanicilar"))
        for i in veriTabaniKullanicilari:
            if giris[0] == i[0] and giris[1] == i[1]:
                ikiFaktorluDogrulama=list(self.con.execute(f"select IkiFaktorluDogrulama from kullanicilar where KullaniciAdi='{giris[0]}'"))
                ikiFaktorluDogrulama=ikiFaktorluDogrulama[0]
                if ikiFaktorluDogrulama[0]=="1":
                    eposta=list(self.con.execute(f"select EPosta from kullanicilar where KullaniciAdi='{giris[0]}'"))
                    eposta=eposta[0]
                    self.kullaniciGirisform.destroy()
                    self.aktivasyonMail("",eposta[0])
                    self.aktivasyon("",eposta[0])
                    with open("kullaniciAdi.txt", "w") as f:
                        f.write(giris[0])
                    import kullanicipanel
                    self.program()
                else:
                    self.kullaniciGirisform.destroy()
                    with open("kullaniciAdi.txt","w") as f:
                        f.write(giris[0])
                    import kullanicipanel
                    self.program()
        return True
    def kullaniciGiris(self):
        self.kullaniciGirisform=tk.Tk()
        self.kullaniciGirisform.geometry('500x300')
        self.kullaniciGirisform.title('Kullanici Giris')

        entry1=tk.StringVar()
        entry2=tk.StringVar()

        kullaniciAdi=tk.Label(self.kullaniciGirisform,text='Kullanıcı Adı:').place(x=10,y=10)
        kullaniciadiEntry=tk.Entry(self.kullaniciGirisform,textvariable=entry1).place(x=100,y=10)

        sifre=tk.Label(self.kullaniciGirisform,text='Sifre').place(x=10,y=40)
        sifreEntry = tk.Entry(self.kullaniciGirisform,textvariable=entry2,show='*').place(x=100, y=40)
        def goster():
            def kayitMenusuneDon():
                self.kullaniciGirisform.destroy()
                self.kullaniciKayit()
                self.program()
            def menuyeDon():
                self.kullaniciGirisform.destroy()
                self.program()
            def sifremiUnuttumBtn():
                self.kullaniciGirisform.destroy()
                self.sifremiUnuttum()
            SHA256Sifre = hashlib.sha256(entry2.get().encode("ascii")).hexdigest()
            giris = [entry1.get(), SHA256Sifre]
            if self.kullaniciGirisKontrol(giris):
                kayitolBtn = tk.Button(self.kullaniciGirisform, text='Kayıt ol',command=kayitMenusuneDon).place(x=130, y=70)
                menuyeDonBtn = tk.Button(self.kullaniciGirisform, text='Menuye Don', command=menuyeDon).place(x=145, y=105)
                sifremiUnuttumBtn=tk.Button(self.kullaniciGirisform, text='Sifremi Unuttum', command=sifremiUnuttumBtn).place(x=20, y=70)
        girisBtn=tk.Button(self.kullaniciGirisform,text='Giris',command=goster).place(x=190,y=70)
        self.kullaniciGirisform.mainloop()
    def sifremiUnuttum(self):
        def gonder():
            self.hatasayisi=0
            def gonder():
                if sifre.get()!=sifreKontrol.get():
                    hata=messagebox.showerror("Uyarı","Sifrelerin aynı olması lazım")
                    self.hatasayisi+=1
                if len(sifre.get())<6:

                    hata=messagebox.showerror("Uyarı","Sifre en az 6 karakterden olusmalidir")
                    self.hatasayisi += 1
                if self.sifreKarakterKontrol(sifre.get()):
                    hata=messagebox.showerror("Uyarı", f"Sifrede bu karakterleri kullanamazsiniz {self.sifreYasakliKarakter}")
                    self.hatasayisi += 1
                if self.hatasayisi==0:
                    SHA256Sifre = hashlib.sha256(sifre.get().encode("ascii")).hexdigest()
                    self.con.execute(f"Update kullanicilar SET Sifre='{SHA256Sifre}' where KullaniciAdi='{kullaniciAdi.get()}'")
                    self.con.commit()
                    sifreYenilemeForm.destroy()
            eposta = list(self.con.execute(f"select EPosta from kullanicilar where KullaniciAdi= '{kullaniciAdi.get()}'"))
            eposta = eposta[0]
            hatalar = messagebox.showinfo("Uyarı",f"Sistemimizde kayıtlı '{eposta[0]}' adresine bir onay kodu gelecektir onay kodunu girip sifrenizi sıfırlayabilirsiniz")
            sifremiUnuttumForm.destroy()
            self.aktivasyonMail("", eposta[0])
            self.aktivasyon("", eposta[0])
            sifreYenilemeForm=tk.Tk()
            sifreYenilemeForm.title("Sifre Yenileme Ekranı")
            sifreYenilemeForm.geometry("500x300")

            sifre=tk.StringVar()
            sifreKontrol=tk.StringVar()

            sifreLabel = tk.Label(sifreYenilemeForm, text="Yeni Sifre:").place(x=10, y=10)
            sifreEntry = tk.Entry(sifreYenilemeForm, textvariable=sifre,show="*").place(x=80, y=10)

            sifreKontrolLabel = tk.Label(sifreYenilemeForm, text="Yeni Sifre:").place(x=10, y=35)
            sifreKontrolEntry = tk.Entry(sifreYenilemeForm, textvariable=sifreKontrol,show="*").place(x=80, y=35)

            sifreBtn=tk.Button(sifreYenilemeForm,text="Gonder",command=gonder).place(x=150,y=60)

        sifremiUnuttumForm=tk.Tk()
        sifremiUnuttumForm.title("Sifremi Unuttum")
        sifremiUnuttumForm.geometry("500x300")

        kullaniciAdi=tk.StringVar()

        mesaj=tk.Label(sifremiUnuttumForm,text="Kullanici adınızı girip sistemde kayıtlı olan epostanız sayesinde hesabınızı kurtarabilirsiniz").place(x=10,y=10)
        kullaniciAdiLabel=tk.Label(sifremiUnuttumForm,text="Kullanici Adi").place(x=10,y=35)
        kullaniciAdiEntry=tk.Entry(sifremiUnuttumForm,textvariable=kullaniciAdi).place(x=90,y=35)

        gonder=tk.Button(sifremiUnuttumForm,text="Gonder",command=gonder).place(x=165,y=60)

        sifremiUnuttumForm.mainloop()
    def adminKontrol(self,giris):
        adminBilgileri=list(self.con.execute("select KullaniciAdi,Sifre from kullanicilar where KullaniciAdi='admin'"))
        adminBilgileri=list(adminBilgileri[0])
        if giris[0] == adminBilgileri[0] and giris[1] == adminBilgileri[1]:
            self.adminGirisform.destroy()
            import adminpanel
            self.program()
        return True
    def adminGiris(self):
        self.adminGirisform = tk.Tk()
        self.adminGirisform.geometry('500x300')
        self.adminGirisform.title('Kullanici Giris')

        entry1 = tk.StringVar()
        entry2 = tk.StringVar()

        adminKullaniciAdi = tk.Label(self.adminGirisform, text='Kullanıcı Adı:').place(x=10, y=10)
        admimKullaniciadiEntry = tk.Entry(self.adminGirisform, textvariable=entry1).place(x=100, y=10)

        sifre = tk.Label(self.adminGirisform, text='Sifre:').place(x=10, y=40)
        sifreEntry = tk.Entry(self.adminGirisform, textvariable=entry2, show='*').place(x=100, y=40)

        def goster():
            def kayitMenusuneDon():
                self.adminGirisform.destroy()
                self.kullaniciKayit()
                self.program()

            def menuyeDon():
                self.adminGirisform.destroy()
                self.program()

            def sifremiUnuttumBtn():
                self.adminGirisform.destroy()
                self.sifremiUnuttum()

            SHA256Sifre = hashlib.sha256(entry2.get().encode("ascii")).hexdigest()
            giris = [entry1.get(), SHA256Sifre]
            if self.adminKontrol(giris):
                kayitolBtn = tk.Button(self.adminGirisform, text='Kayıt ol', command=kayitMenusuneDon).place(x=130,
                                                                                y=70)
                menuyeDonBtn = tk.Button(self.adminGirisform, text='Menuye Don', command=menuyeDon).place(x=145,
                                                                                y=105)
                sifremiUnuttumBtn = tk.Button(self.adminGirisform, text='Sifremi Unuttum',command=sifremiUnuttumBtn).place(x=20, y=70)

        girisBtn = tk.Button(self.adminGirisform, text='Giris', command=goster).place(x=190, y=70)
        self.adminGirisform.mainloop()
    def program(self):
        menuForm = tk.Tk()
        menuForm.geometry('500x300')
        menuForm.title('Burak Ekiz Film')
        def kontrol():
            menuForm.destroy()
            if x.get()==1:
                self.kullaniciGiris()
            if x.get()==2:
                self.adminGiris()
            if x.get()==3:
                self.kullaniciKayit()
            if x.get()==4:
                self.sifremiUnuttum()
            if x.get()==5:
                self.durum=False
        x=tk.IntVar()
        menusecim1 = tk.Radiobutton(menuForm, text='Kullanici Girisi', variable=x, value=1,command=kontrol,bg="light blue",indicator=0,width=20).place(x=10,y=10)
        menusecim2 = tk.Radiobutton(menuForm, text='Admin Girisi', variable=x, value=2,command=kontrol, bg="light blue",indicator=0,width=20).place(x=10,y=35)
        menusecim3 = tk.Radiobutton(menuForm, text='Kullanici Kayıt', variable=x, value=3,command=kontrol, bg="light blue",indicator=0,width=20).place(x=10,y=60)
        menusecim4 = tk.Radiobutton(menuForm, text='Sifremi Unuttum', variable=x, value=4,command=kontrol, bg="light blue",indicator=0,width=20).place(x=10,y=85)
        menusecim5 = tk.Radiobutton(menuForm, text='Cikis', variable=x, value=5,command=kontrol, bg="light blue",indicator=0,width=20).place(x=10,y=110)
        label=tk.Label(menuForm)
        label.pack()
        menuForm.mainloop()
login=login()
while login.durum:
    login.program()