# -*- coding: utf-8 -*-

import numpy as np
import pandas as pd

import netCDF4
import xarray as xr

import re

from collections import OrderedDict

from datetime import datetime, timedelta

from pyproj import Geod

from matplotlib import pyplot as plt

from scipy.spatial import cKDTree, KDTree

import argparse

from glob import glob

import json

import os

import logging

logging.basicConfig(format='%(asctime)s - %(levelname)s - %(message)s', level=logging.INFO)

deg2rad = np.pi / 180

dtypes = {"TIME": 'float64',
          "DEPH": 'float32',
          "BEAR": 'float32',
          "RNGE": 'float32',
          "LONGITUDE": 'float32',
          "LATITUDE": 'float32',
          "EWCT": 'int16',
          "NSCT": 'int16',
          "EWCS": 'int16',
          "NSCS": 'int16',
          "GDOP": 'int16',
          "CCOV": 'int32',
          "DDENS": 'int32',  # Variable auxiliar. No saldrá en el netCDF
          "err": 'int8',  # Variable auxiliar. No saldrá en el netCDF
          "NARX": 'int8',
          "NATX": 'int8',
          "SLTR": 'int32',
          "SLNR": 'int32',
          "SLTT": 'int32',
          "SLNT": 'int32',
          "TIME_QC": 'int8',
          "POSITION_QC": 'int8',
          "DEPH_QC": 'int8',
          "QCflag": 'int8',
          "VART_QC": 'int8',
          "GDOP_QC": 'int8',
          "CSPD_QC": 'int8',
          "DDNS_QC": 'int8'}

scale_factors = {"XDST": 0.001,
                 "YDST": 0.001,
                 "EWCT": 0.001,
                 "NSCT": 0.001,
                 "EWCS": 0.001,
                 "NSCS": 0.001,
                 "GDOP": 0.001,
                 "CCOV": 1E-06,
                 "DDENS": 1,  # Variable auxiliar. No saldrá en el netCDF
                 "err": 1,  # Variable auxiliar. No saldrá en el netCDF
                 "NARX": 1,
                 "NATX": 1,
                 "SLTR": 0.001,
                 "SLNR": 0.001,
                 "SLTT": 0.001,
                 "SLNT": 0.001,
                 "TIME_QC": 1,
                 "POSITION_QC": 1,
                 "DEPH_QC": 1,
                 "QCflag": 1,
                 "VART_QC": 1,
                 "GDOP_QC": 1,
                 "CSPD_QC": 1,
                 "DDNS_QC": 1}

add_offsets = {}

for key, value in scale_factors.items():
    if isinstance(value, float):

        scale_factors[key] = np.float32(scale_factors[key])
        add_offsets[key] = np.float32(0)

    else:

        # Generamos un conversor de tipo a partir del tipo de la variable:
        conversor = np.dtype(dtypes[key])

        # Utilizamos el conversor para recodificar un tipo nativo de python a un escalar tipo numpy:
        scale_factors[key] = np.int_(scale_factors[key]).astype(conversor)
        add_offsets[key] = np.int_(0).astype(conversor)

_FillValues = {}

for key, value in dtypes.items():
    if 'float' in value:
        _FillValues[key] = np.finfo(dtypes[key]).min + 1
    else:
        _FillValues[key] = np.iinfo(dtypes[key]).min + 1

def geodesic_inverse(points, endpoints):
    """
    cartopy.geodesic.inverse has changed by this function because it is the only function to be used by this program.
    pyproj is needed.

    From cartopy (PMV: 20211228.)
    Cartopy. v0.20.1 28-Dec-2021 Met Office. UK. https://github.com/SciTools/cartopy/releases/tag/v0.20.1
    Solve the inverse geodesic problem.

    Can accept and broadcast length 1 arguments. For example, given a
    single start point, an array of different endpoints can be supplied to
    find multiple distances.

    Parameters
    ----------
    points: array_like, shape=(n *or* 1, 2)
        The starting longitude-latitude point(s) from which to travel.

    endpoints: array_like, shape=(n *or* 1, 2)
        The longitude-latitude point(s) to travel to.

    Returns
    -------
    `numpy.ndarray`, shape=(n, 3)
        The distances, and the (forward) azimuths of the start and end
        points.

    """
    geod = Geod(ellps="WGS84")

    # Create numpy arrays from inputs, and ensure correct shape.
    points = np.array(points, dtype=np.float64)
    endpoints = np.array(endpoints, dtype=np.float64)

    if points.ndim > 2 or (points.ndim == 2 and points.shape[1] != 2):
        raise ValueError(
            f'Expecting input points to be (N, 2), got {points.shape}')

    pts = points.reshape((-1, 2))
    epts = endpoints.reshape((-1, 2))

    sizes = [pts.shape[0], epts.shape[0]]
    n_points = max(sizes)
    if not all(size in [1, n_points] for size in sizes):
        raise ValueError("Inputs must have common length n or length one.")

    # Broadcast any length 1 arrays to the correct size.
    if pts.shape[0] == 1:
        orig_pts = pts
        pts = np.empty([n_points, 2], dtype=np.float64)
        pts[:, :] = orig_pts

    if epts.shape[0] == 1:
        orig_pts = epts
        epts = np.empty([n_points, 2], dtype=np.float64)
        epts[:, :] = orig_pts

    start_azims, end_azims, dists = geod.inv(pts[:, 0], pts[:, 1],
                                                  epts[:, 0], epts[:, 1])
    # Convert back azimuth to forward azimuth.
    end_azims += np.where(end_azims > 0, -180, 180)
    return np.column_stack([dists, start_azims, end_azims])

class Total:
    """
    Clase de abstracción para la lectura y procesamiento de ficheros totales (.tuv)

    Atributos
    ---------

    Metodos
    -------
    """

    def __init__(self, tuv_file):

        """
        Constructor

        Parametros
        ----------
        fichero: Fichero .tuv con las velocidades radiales
        """

        # File must be open as binary:
        content = [linea.decode('utf-8').replace('%', '').replace('\n', '') for linea in
                   open(tuv_file, 'rb').readlines() if '%%' not in str(linea)]

        metadata = [linea for linea in content if 'Table' not in linea]
        metadata = dict([(linea.split(':')[0], linea.split(':')[1]) for linea in metadata if ':' in str(linea)])

        # Some needed parameters parsed :
        self.GridSpacing = 6.0  # TODO: must we take it from a config file?
        self.TimeStamp = datetime.strptime(metadata['TimeStamp'], ' %Y %m %d %H %M %S')

        # Start and end lines from the tables:
        starts = np.arange(len(content))[['TableStart' in linea for linea in content]]
        ends = np.arange(len(content))[['TableEnd' in linea for linea in content]]
        lengths = ends - starts - 1

        # Line with header:
        columns = np.arange(len(content))[['TableColumnTypes' in line for line in content]]

        tables = []

        # Aquí podemos aplicar los cambios en los nombres de las variables:
        headers = [content[index].split(':')[1].split() for index in columns]

        # Solo estas columnas son fijas:
        header_common = ['LOND', 'LATD', 'EWCT', 'NSCT', 'OWTR_QC', 'EWCS', 'NSCS', 'CCOV', 'XDST', 'YDST', 'RNGE',
                        'BEAR', 'RDVA', 'DRVA']
        # Originales:
        # LOND   LATD   VELU   VELV   VFLG      UQAL   VQAL   CQAL   XDST   YDST   RNGE   BEAR   VELO   HEAD

        headers[0][0:14] = header_common
        for i in range(2):
            if lengths[i] != 0:
                start = starts[i] + 1
                end = ends[i]
                tables.append(pd.DataFrame(np.array([linea.replace('"', '').split() for linea in content[start:end]]),
                                           columns=headers[i]))

        tipos = {'LOND': np.dtype(float),
                 'LATD': np.dtype(float),
                 'EWCT': np.dtype(float),
                 'NSCT': np.dtype(float),
                 'OWTR_QC': np.dtype(int),
                 'EWCS': np.dtype(float),
                 'NSCS': np.dtype(float),
                 'CCOV': np.dtype(float),
                 'XDST': np.dtype(float),
                 'YDST': np.dtype(float),
                 'RNGE': np.dtype(float),
                 'BEAR': np.dtype(float),
                 'RDVA': np.dtype(float),
                 'DRVA': np.dtype(float)}

        # Hay que gestionar el número variable de columnas del final:
        for i in range(1, len(headers[0]) - len(header_common) + 1):
            tipos['S%iCN' % i] = np.dtype(int)

        tables[0] = tables[0].astype(tipos)

        tipos = {'SNDX': np.dtype(int),
                 'SITE': np.dtype('U4'),
                 'OLAT': np.dtype(float),
                 'OLON': np.dtype(float),
                 'COVH': np.dtype(float),
                 'RNGS': np.dtype(float),
                 'PATK': np.dtype('U4'),
                 'REFB': np.dtype(float),
                 'NUMV': np.dtype(float),
                 'MAXN': np.dtype(float),
                 'MAXS': np.dtype(float),
                 'MAXE': np.dtype(float),
                 'MAXW': np.dtype(float),
                 'PATH': np.dtype('U72'),
                 'UUID': np.dtype('U36')}

        tables[1] = tables[1].astype(tipos)

        # Factores de conversión necesarios para algunas columnas de la tabla:
        tables[0].EWCT /= 100.
        tables[0].NSCT /= 100.
        tables[0].EWCS = 0.01 * np.sqrt(tables[0].EWCS)
        tables[0].NSCS = 0.01 * np.sqrt(tables[0].NSCS)
        tables[0].CCOV *= 0.0001

        # Variables derivadas de columnas de las tables:

        ## DDENS depende del número de radares activos:
        tables[0]['DDENS'] = tables[0][['S%iCN' % i for i in tables[1].SNDX]].sum(axis=1).astype(float)

        self.radares = tables[1].SITE.values

        ## Objeto para calcular los ángulos geodésicos:
        # self.g = Geodesic()  # Por defecto, cálculos en WGS84 como elipsoide de referencia

        puntos = np.column_stack([tables[0].LOND.values, tables[0].LATD.values])

        self.radialAngles = np.empty((len(tables[0]), len(self.radares)))
        self.max_ortho = np.empty((len(tables[0]), 2))

        # Usamos el objerto de Cartopy para calcular los ángulos:
        for i, (SNDX, siteLon, siteLat) in enumerate(tables[1][['SNDX', 'OLON', 'OLAT']].values):
            # self.radialAngles[:,i] = self.g.inverse((siteLon, siteLat), puntos).base[:,1]
            self.radialAngles[:, i] = geodesic_inverse((siteLon, siteLat), puntos)[:, 1]

        # Angulos en el rango [0,360]
        self.radialAngles += 360
        self.radialAngles %= 360

        # Filtramos los ángulos y nos quedamos con los dos angulos cuya ortogonalidad es máxima:
        X, Y = np.meshgrid(range(len(self.radares)), range(len(self.radares)))

        for i, angulos in enumerate(self.radialAngles):
            c = np.cos(np.array([angulos]) * deg2rad)
            s = np.sin(np.array([angulos]) * deg2rad)

            t = c * c.T + s * s.T

            self.max_ortho[i] = angulos[X[t == t.min()]]

        GDOP = np.empty(len(tables[0]))

        # Calculamos el GDOP:
        for i, angulos in enumerate(self.max_ortho):
            A = np.array([np.cos(deg2rad * angulos), np.sin(deg2rad * angulos)])
            GDOP[i] = np.sqrt(np.linalg.inv(np.dot(A, A.T)).trace())

        tables[0]['GDOP'] = GDOP

        # Clean Totals:
        err = np.sqrt(
            tables[0].EWCT ** 2 + tables[0].NSCT ** 2) > Total_QC_params.VelThr  # Valores posibles del error [0,1]
        err += 2 * (GDOP > Total_QC_params.GDOPThr)  # Valores posibles del error [0,2]
        # Por lo tanto este codigo de error puede tener los valores [0,1,2,3]

        tables[0]['err'] = err.astype('float')

        self.metadatos = metadata
        self.tablas = tables

    def to_grid(self, grid):

        longitud, latitud = np.meshgrid(grid.longitud, grid.latitud)

        destino = np.column_stack([longitud.flatten(), latitud.flatten()])
        puntos = np.column_stack([self.tablas[0].LOND.values, self.tablas[0].LATD.values])

        # Busqueda cKDTree:
        nearest = cKDTree(puntos)
        distancias, vecinos = nearest.query(destino)

        # Generamos la máscara con las distancias:
        mascara = np.ones_like(longitud)
        condicion = distancias.reshape(longitud.shape) > 0.05
        mascara[condicion] = np.nan

        variables = ['EWCT', 'NSCT', 'EWCS', 'NSCS', 'CCOV', 'DDENS', 'GDOP', 'err']

        self.variables = OrderedDict()

        # Necesitamos completar la lista de coordenadas:
        delta = self.TimeStamp - datetime(1950, 1, 1)
        self.variables['TIME'] = xr.DataArray([delta.days + delta.seconds / 86400], dims={'TIME': 1})
        self.variables['DEPH'] = xr.DataArray([0.], dims={'DEPTH': 1})

        for variable in variables:

            # Creamos la matriz que se llenará con los datos:
            tmp = np.ones_like(longitud.flatten()) * np.nan

            # Asignamos los vecinos más próximos:
            tmp = self.tablas[0][variable][vecinos].values

            # Volvemos a la forma original:
            tmp = tmp.reshape(longitud.shape)
            tmp *= mascara

            # Creamos el DataArray:
            if variable in ['EWCT', 'NSCT', 'EWCS', 'NSCS', 'CCOV', 'DDENS', 'GDOP', 'err']:
                # Crecemos en DEPTH:
                tmp = np.expand_dims(tmp, axis=0)

                # Crecemos en TIME:
                tmp = np.expand_dims(tmp, axis=0)

                self.variables[variable] = xr.DataArray(tmp,
                                                        dims={'TIME': 1, 'DEPTH': 1, 'LATITUDE': grid.nLAT,
                                                              'LONGITUDE': grid.nLON},
                                                        coords={'TIME': self.variables['TIME'],
                                                                'DEPH': self.variables['DEPH'],
                                                                'LATITUDE': grid.latitud, 'LONGITUDE': grid.longitud})

                # Encoding de las variables en el fichero:
                self.variables[variable].encoding["scale_factor"] = scale_factors[variable]
                self.variables[variable].encoding["add_offset"] = add_offsets[variable]
                self.variables[variable].encoding["dtype"] = dtypes[variable]
                self.variables[variable].encoding["_FillValue"] = _FillValues[variable]

    def QC_control(self):

        """
        Método para el control de calidad de los datos

        Parametros
        ----------
        Utiliza la lista de variables del objeto, que deben estar ya ongrid.
        """

        U_grid = self.variables['EWCT']
        V_grid = self.variables['NSCT']
        # U_std  = self.variables['EWCS']
        # V_std  = self.variables['NSCS']
        DDENS = self.variables['DDENS']

        # Construimos alias de las variables para no tener que reescribir mucho código:

        # Variance Threshold QC test
        # variance = ((mat_tot.U_grid.^2).*(mat_tot.U_std).^2) + ((mat_tot.V_grid.^2).*(mat_tot.V_std).^2);
        # variance = ((U_grid**2)*(U_std**2) + (V_grid**2)*(V_std)**2)

        # Velocity Threshold QC test
        # totVel = sqrt(((mat_tot.U_grid).^2) + ((mat_tot.V_grid).^2));
        totVel = np.sqrt(U_grid ** 2 + V_grid ** 2)

        '''
	    byte GDOP_QC(TIME, DEPTH, LATITUDE, LONGITUDE) ;
        ncwrite(ncfile,'GDOP_QC',GDOP_QCflag');
        '''

        # Time quality flag:
        sdnTime_QCflag = 1
        self.variables['TIME_QC'] = xr.DataArray(sdnTime_QCflag, dims={'TIME': 1},
                                                 coords={'TIME': self.variables['TIME']})

        # Position quality flag:
        sdnPosition_QCflag = totVel.copy() * 0 + 1
        self.variables['POSITION_QC'] = sdnPosition_QCflag

        # Depth quality flag
        sdnDepth_QCflag = 1
        self.variables['DEPH_QC'] = xr.DataArray(sdnTime_QCflag, dims={'TIME': 1},
                                                 coords={'TIME': self.variables['TIME']})

        # Velocity Threshold quality flags
        ## Velocity Threshold QC test
        err = self.variables['err']

        velThr = totVel.copy()
        velThr.values = np.select([(err == 0) | (err == 2), (err == 1) | (err == 3), np.isnan(err)], [1, 4, np.nan])
        # velThr.values = np.select([totVel <= Total_QC_params.VelThr, totVel > Total_QC_params.VelThr, np.isnan(totVel)], [1,4,np.nan])

        '''
        % Velocity Threshold quality flags
        if (I(i) == 1 || I(i) == 3)
            velThr(lonGrid_idx,latGrid_idx,1) = 4;
        elseif (I(i) == 0 || I(i) == 2)
            velThr(lonGrid_idx,latGrid_idx,1) = 1;
        end
        '''
        self.variables['CSPD_QC'] = velThr

        # GDOP Threshold quality flags
        GDOPThr = totVel.copy()
        GDOPThr.values = np.select([(err == 0) | (err == 1), (err == 2) | (err == 3), np.isnan(err)], [1, 4, np.nan])

        '''
        if (I(i) == 2 || I(i) == 3)
            GDOPThr(lonGrid_idx,latGrid_idx,1) = 4;
        elseif (I(i) == 0 || I(i) == 1)
            GDOPThr(lonGrid_idx,latGrid_idx,1) = 1;
        end
        '''
        self.variables['GDOP_QC'] = GDOPThr

        # La variable err solo se usa en el control de calidad:
        var = self.variables.pop('err')

        # Data Density Threshold quality flag
        dataDens = totVel.copy()
        dataDens.values = np.select(
            [DDENS >= Total_QC_params.DataDensityThr, DDENS < Total_QC_params.DataDensityThr, np.isnan(DDENS)],
            [1, 4, np.nan])

        self.variables['DDNS_QC'] = dataDens

        # La varaible DDENS solo se usa en el control de calidad:
        var = self.variables.pop('DDENS')

        # Set the QC flag for the current hour to 0 (no QC performed)
        tempDer = totVel.copy()
        tempDer *= 0

        self.variables['VART_QC'] = tempDer

        # Populate the overall quality variable:
        condicion = (self.variables['CSPD_QC'] == 1) & \
                    (self.variables['DDNS_QC'] == 1) & \
                    (self.variables['GDOP_QC'] == 1) & \
                    (self.variables['VART_QC'] == 1)

        is_nan = np.isnan(self.variables['CSPD_QC'])

        self.variables['QCflag'] = self.variables['CSPD_QC'].copy()
        self.variables['QCflag'].values = np.select([condicion & ~is_nan, ~condicion & ~is_nan, is_nan], [1, 4, np.nan])

        # Terminamos ajustando algunos parámetros de las variables:
        for variable in ['TIME_QC', 'POSITION_QC', 'DEPH_QC', 'CSPD_QC', 'DDNS_QC', 'QCflag', 'GDOP_QC', 'VART_QC']:
            # Encoding de las variables en el fichero:
            self.variables[variable].encoding["scale_factor"] = scale_factors[variable]
            self.variables[variable].encoding["add_offset"] = add_offsets[variable]
            self.variables[variable].encoding["dtype"] = dtypes[variable]
            self.variables[variable].encoding["_FillValue"] = _FillValues[variable]

    def to_netcdf(self, path_out, fichero):

        radar = re.findall("[A-Z]{4}", fichero.split('/')[-1])[0]

        fecha = datetime.strptime('%s%s%s%s' % tuple(re.findall("\d+", fichero.split('/')[-1])), '%Y%m%d%H%M')

        logging.info('Fichero: %s Radar: %s' % (fichero, radar))

        # Info de la proyección:
        self.variables['crs'] = xr.DataArray(np.int16(0), )

        # Datos SDN:
        # Número de sites usados para obtener el total:
        num_sites = len(self.tablas[1])

        SDN_EDMO_CODEs = {'PRIO': 4841, 'SILL': 2751, 'VILA': 4841, 'LPRO': 590, 'FIST': 2751}
        codigos = np.array([SDN_EDMO_CODEs[site] for site in self.tablas[1].SITE], dtype=np.int16)
        codigos = np.expand_dims(codigos, axis=0)
        self.variables['SDN_EDMO_CODE'] = xr.DataArray(codigos, dims={'TIME': 1, 'MAXINST': num_sites})

        cadena = b'HFR-Galicia'
        n = len(cadena)
        self.variables['SDN_CRUISE'] = xr.DataArray(np.array([cadena]), dims={'TIME': 1})

        # cadena = ('HFR-Galicia-%s' % radar).encode()
        cadena = b'HFR-Galicia-Total'
        n = len(cadena)
        self.variables['SDN_STATION'] = xr.DataArray(np.array([cadena]), dims={'TIME': 1})

        cadena = ('HFR-Galicia-Total_%sZ' % (self.TimeStamp.isoformat())).encode()
        n = len(cadena)
        self.variables['SDN_LOCAL_CDI_ID'] = xr.DataArray(np.array([cadena]), dims={'TIME': 1})

        cadena = b'http://opendap.intecmar.gal/thredds/catalog/data/nc/RADAR_HF/Galicia/catalog.html'
        n = len(cadena)
        self.variables['SDN_REFERENCES'] = xr.DataArray(np.array([cadena]), dims={'TIME': 1})

        cadena = b"<sdn_reference xlink:href=\"http://opendap.intecmar.gal/thredds/catalog/data/nc/RADAR_HF/Galicia/catalog.html\" xlink:role=\"\" xlink:type=\"URL\"/>"
        n = len(cadena)
        self.variables['SDN_XLINK'] = xr.DataArray(np.array([[cadena]]), dims={'TIME': 1, 'REFMAX': 1})

        # Otras:
        # Por ahora forzamos a estos valores para probar pero hay que sacarlos de la segunda tabla:
        siteLats = np.empty(50) * np.nan
        siteLons = np.empty(50) * np.nan

        siteLats[0:num_sites] = self.tablas[1].OLAT.values
        siteLons[0:num_sites] = self.tablas[1].OLON.values

        siteLats = np.expand_dims(siteLats, axis=0)
        siteLons = np.expand_dims(siteLons, axis=0)

        # Las posiciones de emisión y recepción son las mismas:
        self.variables['SLTR'] = xr.DataArray(siteLats, dims={'TIME': 1, 'MAXSITE': 50})
        self.variables['SLNR'] = xr.DataArray(siteLons, dims={'TIME': 1, 'MAXSITE': 50})
        self.variables['SLTT'] = xr.DataArray(siteLats, dims={'TIME': 1, 'MAXSITE': 50})
        self.variables['SLNT'] = xr.DataArray(siteLons, dims={'TIME': 1, 'MAXSITE': 50})

        sites = np.array([site.ljust(15).encode() for site in self.tablas[1].SITE])
        sites = np.pad(sites, (0, 50 - len(sites)), 'constant', constant_values=('', '               '))
        sites = np.expand_dims(sites, axis=0)

        self.variables['SCDR'] = xr.DataArray(sites, dims={'TIME': 1, 'MAXSITE': 50})
        self.variables['SCDT'] = xr.DataArray(sites, dims={'TIME': 1, 'MAXSITE': 50})

        self.variables['NARX'] = xr.DataArray([num_sites], dims={'TIME': 1})
        self.variables['NATX'] = xr.DataArray([num_sites], dims={'TIME': 1})

        for variable in ['SLTT', 'SLNT', 'SLTR', 'SLNR', 'NARX', 'NATX']:
            # Encoding de las variables en el fichero:
            self.variables[variable].encoding["scale_factor"] = scale_factors[variable]
            self.variables[variable].encoding["add_offset"] = add_offsets[variable]
            self.variables[variable].encoding["dtype"] = dtypes[variable]
            self.variables[variable].encoding["_FillValue"] = _FillValues[variable]

        for variable in ['SCDR', 'SCDT']:
            # Encoding de las variables en el fichero:
            self.variables[variable].encoding["_FillValue"] = ' '

        # Generamos el xarra.Dataset. radial.variables contienen los xr.DataArray necesarios:
        dataset = xr.Dataset(self.variables)

        # Atributos globales:
        # Leemos los atributos específicos de cada radar:
        f = open('%s.json' % radar)
        atributos = json.loads(f.read())
        f.close()

        # Creamos algunos atributos:
        #atributos['id'] = 'HFR-Galicia-Total_%sZ' % self.TimeStamp.isoformat()
        atributos['id'] = atributos['id'] + self.TimeStamp.strftime("%Y%m%d")

        atributos['time_coverage_start'] = '%sZ' % (self.TimeStamp - timedelta(minutes=30)).isoformat()
        atributos['time_coverage_end'] = '%sZ' % (self.TimeStamp + timedelta(minutes=30)).isoformat()

        ahora = datetime(*datetime.now().timetuple()[0:6]).isoformat()
        atributos['date_created'] = '%sZ' % ahora
        atributos['metadata_date_stamp'] = '%sZ' % ahora
        atributos['date_modified'] = '%sZ' % ahora
        atributos['date_issued'] = '%sZ' % ahora

        atributos['history'] = '%s data collected. %s netCDF file created and sent to European HFR Node' % \
                               (self.TimeStamp.isoformat(), ahora)

        atributos['DoA_estimation_method'] = ';'.join(
            [' %s: Direction Finding' % radar for radar in self.radares]).strip()

        correos = {'LPRO': 'margarida.alves@hidrografico.pt', 'PRIO': 'gis@intecmar.gal', 'FIST': 'maribel@puertos.es',
                   'SILL': 'maribel@puertos.es', 'VILA': 'gis@intecmar.gal'}
        atributos['calibration_link'] = ';'.join(
            [' %s: %s' % (radar, correos[radar]) for radar in self.radares]).strip()

        atributos['calibration_type'] = ';'.join([' %s: APM' % radar for radar in self.radares]).strip()

        #  ... y los insertamos
        dataset.attrs = atributos

        # Atributos de las variables:
        f = open('variables.json')
        atributos = json.loads(f.read())
        f.close()

        # Los tipos de los atributos valid_min/max son deserializados incorrectamente:
        for var in dataset:
            for key, value in atributos[var].items():
                if isinstance(atributos[var][key], int):

                    # Generamos un conversor de tipo a partir del tipo de la variable:
                    conversor = np.dtype(dtypes[var])

                    # Utilizamos el conversor para recodificar un tipo nativo de python a un escalar tipo numpy:
                    atributos[var][key] = np.int_(atributos[var][key]).astype(conversor)

                elif isinstance(atributos[var][key], list):

                    # Generamos un conversor de tipo a partir del tipo de la variable:
                    conversor = np.dtype(dtypes[var])

                    # Utilizamos el conversor para recodificar un tipo nativo de python a un escalar tipo numpy:
                    atributos[var][key] = np.array(atributos[var][key]).astype(conversor)

        for var in dataset:
            dataset[var].attrs = atributos[var]

        # Completamos coordenadas y dimensiones que xArray procesa de forma automática una vez creado el xr.
        # Dataset a partir del diccionario de variables:
        for var in ['LONGITUDE', 'LATITUDE','TIME']:
            dataset[var].encoding["dtype"] = dtypes[var]
            dataset[var].encoding["_FillValue"] = None
            dataset[var].attrs = atributos[var]

        for var in ['DEPH']:
            dataset[var].encoding["dtype"] = 'float32'
            dataset[var].encoding["_FillValue"] = netCDF4.default_fillvals['f4'] # _FillValues[var]
            dataset[var].attrs = atributos[var]

        for var in ['DEPH', 'LONGITUDE', 'LATITUDE']:
            # Generamos un conversor de tipo a partir del tipo de la variable:
            conversor = np.dtype(dtypes[var])

            # Utilizamos el conversor para recodificar un tipo nativo de python a un escalar tipo numpy:
            dataset[var].attrs['valid_min'] = np.float_(atributos[var]['valid_min']).astype(conversor)
            dataset[var].attrs['valid_max'] = np.float_(atributos[var]['valid_max']).astype(conversor)

        # Write netCDF:
        file_out = os.path.join(path_out, 'HFR-Galicia-%s_%s.nc')
        file_out_total = file_out % ('Total', fecha.strftime('%Y_%m_%d_%H%M'))
        dataset.reset_coords(drop=False).to_netcdf(file_out_total)

        # Dimension string* moves to STRING*
        with netCDF4.Dataset(file_out_total, 'r+') as nc:
            dim_list = [dim for dim in nc.dimensions if 'string' in dim]
            for dim_actual in dim_list:
                nc.renameDimension(dim_actual, dim_actual.upper())

    def __repr__(self):
        return '<Total class>'

class Total_QC_params():
    """
    Clase estática para contener los umbrales.
    """

    VelThr = 1.2  # (m/s)
    tempDer_Thr = 1  # Diferencia de 1 m/s
    GDOPThr = 2
    DataDensityThr = 3

class Grid:
    """
    Clase para la generación de la malla para albergar los datos

    Atributos
    ---------
    longitud, latitud: Matriz con las longitudes y latitudes reconstruidas

    Metodos
    -------
    """

    def __init__(self, fichero):
        """
        Constructor

        Parametros
        ----------
        """

        '''
        nLON, nLAT =  47, 81

        self.nLON, self.nLAT = nLON, nLAT

        origen_lon, origen_lat = -11.327712, 40.354694

        longitud = np.arange(nLON)*0.0730114  + origen_lon
        latitud  = np.arange(nLAT)*0.05401373 + origen_lat

        self.longitud = xr.DataArray(longitud, dims={'LONGITUDE' : nLON})
        self.latitud  = xr.DataArray(latitud , dims={'LATITUDE'  : nLAT})
        '''

        coordenadas = xr.open_dataset(fichero)

        nLON, = coordenadas.LONGITUDE.shape
        nLAT, = coordenadas.LATITUDE.shape

        self.nLON, self.nLAT = nLON, nLAT

        self.longitud = coordenadas.LONGITUDE
        self.latitud = coordenadas.LATITUDE

    def __repr__(self):
        return '<Grid class -> nLON: %i, nLAT: %i>' % (self.nLON, self.nLAT)

def VART_QC(ficheros):
    datasets = [xr.open_dataset(fichero) for fichero in ficheros]
    totales = [np.sqrt(dataset.EWCT ** 2 + dataset.NSCT ** 2)[0, 0].values for dataset in datasets]

    totVel2h, totVel1h, totVel = totales

    # Definición del umbral:
    tempDer_Thr = Total_QC_params.tempDer_Thr

    tempDer1h = np.full_like(totVel1h, 4)

    condicion = np.abs(totVel - totVel1h) < tempDer_Thr
    condicion &= np.abs(totVel2h - totVel1h) < tempDer_Thr

    tempDer1h[condicion] = 1

    condicion = np.isnan(totVel) | np.isnan(totVel2h)

    tempDer1h[condicion] = 0  # Ojo!!! HFR_Node_tools lo tiene a 1 (dato correcto)!

    tempDer1h[np.isnan(totVel1h)] = np.nan

    datasets[1].VART_QC.values[0, 0, :] = tempDer1h[:]

    # Redefinimos overall quality variable para incluir los cambios recientes en VART_QC:
    condicion = (datasets[1].variables['CSPD_QC'] == 1) & \
                (datasets[1].variables['DDNS_QC'] == 1) & \
                (datasets[1].variables['VART_QC'] == 1)
    #                    (datasets[1].variables['GDOP_QC'] == 1) & \

    is_nan = np.isnan(datasets[1].variables['CSPD_QC'])

    datasets[1].variables['QCflag'].values = \
        np.select([condicion & ~is_nan, ~condicion & ~is_nan, is_nan], [1, 4, np.nan])
    datasets[1].to_netcdf('%s_new.nc' % ficheros[1].split('.')[0])

def tuv2nc(path_in, path_out, file):
    file_in = path_in + '/' + file

    radar = re.findall("[A-Z]{4}", file.split('/')[-1])[0]

    fecha = datetime.strptime('%s%s%s%s' % tuple(re.findall("\d+", file.split('/')[-1])), '%Y%m%d%H%M')

    total = Total(file_in)

    # Metemos la tabla en la malla regular del fichero auxiliar:
    grd = Grid('coordenadas.nc')
    total.to_grid(grd)

    # Generamos las variables de control de calidad:
    total.QC_control()

    # Generamos el fichero NetCDF:
    total.to_netcdf(path_out, file)
    file_out = os.path.join(path_out, 'HFR-Galicia-%s_%s.nc')
    ficheros = [file_out % ('Total', (fecha + timedelta(hours=-i)).strftime('%Y_%m_%d_%H%M'))
                for i in range(3)]

    condiciones = [os.path.isfile(fichero) for fichero in ficheros]

    if np.all(condiciones):
        logging.info('Procesando VART_QC en %s' % ficheros[0])
        VART_QC(ficheros)
    else:
        logging.info('No VART_QC')

if __name__ == '__main__':
    file = r'TOTL_GALI_2021_12_29_0100.tuv'
    path_in = r'../datos/radarhf_tmp/tuv/GALI'
    path_out = r'../datos/radarhf_tmp/nc/total'
    tuv2nc(path_in, path_out, file)


#Stripping Whitespace----------------------------------------
#print(var_1)            #This will print string with whitesplace
#var_1=var_1.rstrip()
#var_2="Python "         #With whitespace
#print(var_1)            #This will print string without whitespace
#print(var_2)

#Example of rstrip() , lstrip() ,strip()-----------

variable1="python "
variable2=" python"
variable3=" python "

variable1=variable1.rstrip()
variable2=variable2.lstrip()
variable3=variable3.strip()

print(variable1)
print(variable2)
print(variable3)


def get(l, i, d=None):
    """Returns the value of `l[i]` if possible, otherwise `d`."""

    if l is None:
        return d

    if type(l) in (dict, set):
        return l[i] if i in l else d

    try:
        return l[i] if i >= 0 else d
    except (IndexError, KeyError, TypeError):
        return d

def loop(n, a, b):
    """Loops `n` between `a` and `b` (exclusive)."""
    return ((n - a) - ((b - a) * ((n - a) // (b - a)))) + a

def flatten(l):
    f = []

    if type(l) in (list, tuple):
        for v in l:
            f += flatten(v) if type(v) in (list, tuple) else [v]

    return f

def hide(obj):
    data = {
        'hidden': obj.hide_viewport,
        'selected': obj.select_get()
    }

    obj.hide_viewport = True

    return obj, data

def show(hidden):
    obj, data = hidden

    obj.hide_viewport = data['hidden']
    obj.select_set(data['selected'])


import pytest

from sqlalchemy.exc import IntegrityError

from application.models import User

def test__create_user(database):
    # Case for creation query
    email = "some.email@server.com"
    user = User(email=email)
    database.session.add(user)
    database.session.commit()

    user = User.query.first()

    assert user.email == email

    users_count = User.query.count()

    assert users_count == 1

    # Case for unique email field
    another_user = User(email=email)
    with pytest.raises(IntegrityError):
        database.session.add(another_user)
        database.session.commit()
    database.session.rollback()

    # Case for nullable email field
    another_user = User()
    with pytest.raises(IntegrityError):
        database.session.add(another_user)
        database.session.commit()
    database.session.rollback()

def test__users_count(database):
    # Cases for user count function
    first_email = "some.email@server.com"
    second_email = "another.email@server.com"
    first_user = User(email=first_email)
    second_user = User(email=second_email)

    database.session.add(first_user)
    users_count = User.query.count()

    assert users_count == 1

    database.session.add(second_user)
    database.session.commit()

    users_count = User.query.count()

    assert users_count == 2

def test__users_count_endpoint(client, database):
    # Case for success status of endpoint
    response = client.get('/users')
    assert response.status_code == 200

    # Case for correct empty output
    assert response.get_json() == {'Number of users': 0}

    # Case for correct output after user creation
    email = "some.email@server.com"
    user = User(email=email)
    database.session.add(user)
    database.session.commit()

    response = client.get('/users')
    assert response.get_json() == {'Number of users': 1}


#@+leo-ver=5-thin
#@+node:tom.20220909175119.1: * @file readout.py
"""Display y value at specified point."""

from AbstractPlotMgr import MAIN
from .require_datasets import needs_main
from entry import GetSingleFloat

BUTTON_DEF  = ('Readout Y', 'readout', 'Show the y value of a specified point')
plotmgr = None  # Suppress pyflake complaints

#@+others
#@+node:tom.20220909180932.1: ** find_closest
def find_closest(value, x):
    """If value is not exactly a member of the list, return the
    closest point.

    Assumes ordered points.

    ARGUMENTS
    value -- a value that should be included in the values of x.
    x -- a list.

    RETURNS
    the index of the closest value, else -1.
    """

    given = -1
    try:
        given = x.index(value)
    except ValueError:  # Didn't get an exact x value
        # Scan until we exceed the given x value
        increasing = x[1] >= x[0]
        foundit = False
        for i, v in enumerate(x):
            foundit = v >= value if increasing else v < value
            if foundit:
                given = i
                break
    return given
#@-others

def proc():
    if not needs_main(plotmgr):
        return

    _id = 'readout'
    lastparm = plotmgr.parmsaver.get(_id, 0.0)
    dia = GetSingleFloat(plotmgr.root,
                         'Find Peak Near', 'X axis coord', lastparm)
    if dia.result is None:
        return
    plotmgr.parmsaver[_id] = dia.result

    _ds = plotmgr.stack[MAIN]
    _x = list(_ds.xdata)
    _y = list(_ds.ydata)  # Might be a numpy nd array.

    given = find_closest(dia.result, _x)
    if given == -1:
        plotmgr.announce(f"Can't find point near X = {dia.result}")
        return

    height = _y[given]
    plotmgr.announce(
        f'Y: {height:0.4f} at X: {_x[given]:0.4f}')
#@-leo


#IP Home Assignment 5
#Suchet Aggarwal
#2018105
#CSE
#Section A Group 1

import pygame
from pygame.locals import *
from numpy import loadtxt
import time

#Constants for the game
WIDTH, HEIGHT = (32, 32)
COIN_SIZE = 3
PACMAN_SIZE = 15
WALL_COLOR = pygame.Color(0, 0, 255, 255) # BLUE
COIN_COLOR = pygame.Color(255, 255, 0, 255) # WHITE
PACMAN_COLOR = pygame.Color(255, 255, 0, 255) # YELLOW
ENEMY_COLOR = pygame.Color(250, 80, 10, 0)
DOWN = (0,1)
RIGHT = (1,0)
TOP = (0,-1)
LEFT = (-1,0)

#Draws a rectangle for the wall
def draw_wall(screen, pos):
	pixels = pixels_from_points(pos)
	pygame.draw.rect(screen, WALL_COLOR, [pixels, (WIDTH, HEIGHT)])

#Draws a rectangle for the player
def draw_pacman(screen, pos,p):
	pixels = pixels_from_points(pos)
	pac = ["up.png","left.png","right.png","down.png"]
	pacman = pygame.image.load(pac[p])
	pacman_mini = pygame.transform.scale(pacman, (32, 32))
	screen.blit(pacman_mini, pixels)

#Draws a rectangle for the coin
def draw_coin(screen, pos):
	pixels = pixels_from_points(pos)
	a=pixels[0]+16
	b=pixels[1]+16
	pixels=(a,b)
	pygame.draw.circle(screen, COIN_COLOR,pixels,COIN_SIZE)

#Draws a rectangle for the enemy
def draw_enemy(screen, pos,no):
	pixels = pixels_from_points(pos)
	enem = ["e1.png","e2.png","e3.png"]
	ene = pygame.image.load(enem[no%3])
	ene_mini = pygame.transform.scale(ene, (25, 19))
	screen.blit(ene_mini, pixels)

#Draws lives remaining for the player
def draw_lives(screen, x, y, lives, img):
	for i in range(lives):
		img_rect = img.get_rect()
		img_rect.x = x + 30 * i
		img_rect.y = y
		screen.blit(img, img_rect)

#Uitlity functions
def add_to_pos(pos, pos2):
	return (pos[0]+pos2[0], pos[1]+pos2[1])
def pixels_from_points(pos):
	return (pos[0]*WIDTH, pos[1]*HEIGHT)

#Initializing pygame
pygame.init()
screen = pygame.display.set_mode((640,640), 0, 32)
background = pygame.surface.Surface((640,640)).convert()

#Initializing variables
layout = loadtxt('layout.txt', dtype=str)
rows, cols = layout.shape
score = 0
pacman_position = (1,1)
background.fill((0,0,0))
#stores no of enemies in the game
NoOfEne =0
MaxScore = 0
for col in range(cols):
		for row in range(rows):
			value = layout[row][col]
			pos = (col, row)
			if value == 'e':
				NoOfEne +=1
			elif value == 'c':
				MaxScore +=1
#list storing values of the layout corresponfing to the next position of the enemy
temp = []
for i in range(NoOfEne):
	temp.append('.')
#5 lives for the player
life =5
p=2
# Main game loop
while True:
	#lists sotring the positions for coins,enemies,and wall
	coinPos=[]
	wallPos=[]
	enePos=[]
	k=1
	for event in pygame.event.get():
		if event.type == QUIT:
			exit()

	screen.blit(background, (0,0))

	#Draw board from the 2d layout array.
	#In the board, '.' denote the empty space, 'w' are the walls, 'c' are the coins, and 'e' are the enemies
	for col in range(cols):
		for row in range(rows):
			value = layout[row][col]
			pos = (col, row)
			if value == 'w':
				draw_wall(screen, pos)
				wallPos.append(pos)
			elif value == 'c':
				draw_coin(screen, pos)
				coinPos.append(pos)
			elif value == 'e' :
				draw_enemy(screen, pos,k)
				enePos.append(pos)
				k+=1

	#Draw the player
	draw_pacman(screen, pacman_position,p)

	#Display score
	myfont = pygame.font.SysFont("monospace", 16)
	scoretext = myfont.render("Score = "+str(score), 1, (255,255,255))
	screen.blit(scoretext, (5, 10))

	#display lives
	lives = pygame.image.load("life.png")
	lives_mini = pygame.transform.scale(lives, (25, 19))
	draw_lives(screen, 480, 5, life,lives_mini)

	#Move the Player
	move_direction = (0,0)
	#Move the enemies
	enemy_direction = []

	#input from the user
	for event in pygame.event.get():
		if event.type == pygame.KEYDOWN :
			if event.key == ord('w') :
				move_direction = TOP
				p=0
			elif event.key == ord('a') :
				move_direction = LEFT
				p=1
			elif event.key == ord('d') :
				move_direction = RIGHT
				p=2
			elif event.key == ord('s') :
				move_direction = DOWN
				p=3

	prevPos = pacman_position
	#Update player position based on movement.
	if add_to_pos(pacman_position, move_direction) not in wallPos:
		pacman_position = add_to_pos(pacman_position, move_direction)
	else:
		pacman_position = prevPos

	# coin should dissapear when eating, i.e update the layout array and score
	if pacman_position in coinPos:
		layout[pacman_position[1]][pacman_position[0]]= '.'
		score+=1
	else:
		pass

	#Move the enemy according to the current position of pacman
	for i in range(len(enePos)):
		if abs(enePos[i][0] - pacman_position[0]) > abs(enePos[i][1] - pacman_position[1]) :
			if enePos[i][0] > pacman_position[0]:
				enemy_direction.append(LEFT)
			else:
				enemy_direction.append(RIGHT)
		else:
			if enePos[i][1] > pacman_position[1]:
				enemy_direction.append(TOP)
			else:
				enemy_direction.append(DOWN)

	#Updating positions of enemy
	for i in range(len(enePos)):
		x = add_to_pos(enePos[i], enemy_direction[i])
		if x not in wallPos and x not in enePos:
			layout[enePos[i][1]][enePos[i][0]] = temp[i]
			temp[i] = layout[x[1]][x[0]]
			enePos[i] = x
			layout[enePos[i][1]][enePos[i][0]] = 'e'
		else:
			pass

	#check whether enemy and pacman have collided
	if pacman_position in enePos:
		score = 0
		layout = loadtxt('layout.txt', dtype=str)
		pacman_position = (1,1)
		life -=1
	else:
		pass

	#check maximum score and flash Winning screen
	if score == MaxScore:
		myfont = pygame.font.SysFont("monospace", 100)
		win = myfont.render("YOU WIN", 1, (255,255,255))
		TextRect = win.get_rect()
		TextRect.center = ((320),(320))
		screen.blit(win, TextRect)
		pygame.display.update()
		time.sleep(5)

	#Check lives remaining and if equal to zero flash GAME OVER
	elif life == 0:
		myfont = pygame.font.SysFont("monospace", 100)
		lose = myfont.render("GAME OVER", 1, (255,255,255))
		TextRect = lose.get_rect()
		TextRect.center = ((320),(320))
		screen.blit(lose, TextRect)
		pygame.display.update()
		time.sleep(5)

	time.sleep(0.1)

	#Update the display
	pygame.display.update()


from django.urls import path
from apps.accounts import views
from django.views.decorators.csrf import csrf_exempt

# app_name = "account"

urlpatterns = [
    path('login/', views.loginView),
    path('register/', views.registerView),
    path('refresh-token/', views.CookieTokenRefreshView.as_view()),
    path('logout/', views.logoutView),
    path("user/", views.user),
]


import cv2
import statistics as st

img=cv2.imread("img/Sample_image.png",0)

#img=cv2.copyMakeBorder(img.copy(),2,2,2,2,cv2.BORDER_CONSTANT,value=120)

cv2.imshow("image",img)
cv2.waitKey(0)
cv2.destroyAllWindows()
h,v=img.shape

k=3

##################################################
def denoise(img):
    img2=img.copy()
    for i in range(h-k):
        for j in range(v-k):
            arr=[]
            for x in range(k):
                for y in range(k):
                    arr.append(img[i+x][j+y])
            median=st.median(arr)
            img2[i][j]=median
    return img2

img=denoise(img)

cv2.imshow("image",img)
cv2.waitKey(0)
cv2.destroyAllWindows()

##################################################
def edges(img):
    img2=img.copy()
    swh=[-1,-2,-1,0,0,0,1,2,1]
    swv=[-1,0,1,-2,0,2,-1,0,1]
    for i in range(h-k):
        for j in range(v-k):
            gh=0#radient horizontally and vertically
            gv=0
            for x in range(k):
                for y in range(k):
                    gh+=(img[i+x][j+y])*swh[x*k+y];
                    gv+=(img[i+x][j+y])*swv[x*k+y];

            img2[i][j]=round((gh**2+gv**2)**0.5) # magnitude of gradient

            if img2[i][j]>=45:
                img2[i][j]=255
            else:
                img2[i][j]=0
    return img2

img=edges(img)
cv2.imshow("image",img)
cv2.waitKey(0)
cv2.destroyAllWindows()

##################################################
def erosion(img):
    img2=img.copy()
    for i in range(h-k):
        for j in range(v-k):
            c=0
            for x in range(k):
                for y in range(k):
                    if img[i+x][j+y]==255:
                        c+=1
            if c==k*k:
                img2[i+x//2][j+y//2]=255
            else:
                img2[i+x//2][j+y//2]=0
    return img2

def dilation(img):
    img2=img.copy()
    for i in range(h-k):
        for j in range(v-k):
            c=0
            for x in range(k):
                for y in range(k):
                    if img[i+x][j+y]==255:
                        c+=1
            if c!=0:
                img2[i+x//2][j+y//2]=255
            else:
                img2[i+x//2][j+y//2]=0
    return img2

def fineseg(img):   #  a
    return dilation(img)

img=fineseg(img)
cv2.imshow("image",img)
cv2.waitKey(0)
cv2.destroyAllWindows()

def fill(img):      #  b
    return erosion((dilation(img)))

img=fill(img)
cv2.imshow("image",img)
cv2.waitKey(0)
cv2.destroyAllWindows()

def seg(img):       #  c
    for i in range(h):
        for j in range(v):
            if i+j<100:
                img[i][j]=0
            if i+j>180:
                img[i][j]=0
    return img

img=seg(img)
cv2.imshow("image",img)
cv2.waitKey(0)
cv2.destroyAllWindows()

def erd(img):       #  d
    return erosion(img)

img=erd(img)
cv2.imshow("image",img)
cv2.waitKey(0)
cv2.destroyAllWindows()


# ---------- IMPORT ------------
import pandas as pd
import logging
import json
import datetime
from datetime import timedelta, date
# ------------------------------

# ---------- INPUT -------------
path_source = "/home/johannes/data/github/mysql-2019-06-01/users.csv"
# ------------------------------

# ---------- OUTPUT ------------
path_results = "/home/lmoldon/data/communitysize_per_day.json"
# ------------------------------

# ---------- CONFIG ------------
chunksize = 100000
datetimeFormat = "%Y-%m-%d %H:%M:%S"
startinterval = date(2011, 1, 1)
endinterval = date(2019, 4, 1)
# ------------------------------

# ---------- INITIAL -----------
logging.basicConfig(format='%(asctime)s [%(levelname)s] - %(message)s', datefmt='%d-%m-%y %H:%M:%S', level=logging.INFO)
data = {} # key = date between start and end, value = amount of commits at that day
communitysize = {}
# ------------------------------

def daterange(observedtime_start, observedtime_end):
        for n in range(int((observedtime_end - observedtime_start).days)):
                yield observedtime_start + timedelta(n)

log_starttime = datetime.datetime.now()

logging.info("Starting ...")
cnt = 0
for chunk in pd.read_csv(path_source, chunksize=chunksize, header=None, delimiter=",", usecols=[3], names=["created_at"]):
    for timestamp in list(chunk["created_at"].values):
        try:
            date = datetime.datetime.strptime(timestamp, datetimeFormat).date()
            if date >= startinterval and date <= endinterval:
                date = str(date)
                if date in data:
                    data[date] += 1
                else:
                    data[date] = 1
        except:
            logging.warning("Saving Date failed at chunk " + str(cnt+1))
    cnt += 1
    logging.info("Chunk counter = " + str(cnt))

logging.info("Done (1/3)")

# Calculate size of community per day in interval [startinterval, endinterval]
logging.info("Calculating size of community per day...")
cnt = 0
for single_date in daterange(startinterval, endinterval):
    communitysize[str(single_date)] = 0

for single_date in daterange(startinterval, endinterval):
    if str(single_date) in data:
        for following_date in daterange(single_date, endinterval):
            communitysize[str(following_date)] += data[str(single_date)]
    else:
        logging.warning("No users of observed group joined on " + str(single_date))
    cnt += 1
    if cnt % 100 == 0:
        logging.info("Days computed: " + str(cnt))

logging.info("Done (2/3)")

logging.info("Storing data ... ")
with open(path_results, "w") as fp:
    json.dump(communitysize, fp)

logging.info("Done. (3/3)")

log_endtime = datetime.datetime.now()
log_runtime = (log_endtime - log_starttime)
logging.info("Total runtime: " + str(log_runtime))


# -*- coding:utf8 -*-

# Install library:
import json
import random

import os
THIS_FOLDER = os.path.dirname(os.path.abspath(__file__))
character_file = os.path.join(THIS_FOLDER, 'characters.json')
quote_file = os.path.join(THIS_FOLDER, 'quotes.json')

# Receive a JSON file and return a Python list from it:
def read_values_from_json(path, key):
  values = []
  with open(path) as f:
    data = json.load(f)
    for entry in data:
      values.append(entry[key])
  return values

# Give a json and return a list:
def clean_strings(sentences):
  cleaned = []
  # Store quotes on a list. Create an empty list and add each sentence one by one.
  for sentence in sentences:
    # Clean quotes from whitespaces and so on
    clean_sentence = sentence.strip()
    # Don't use extend as it adds each letter one by one!
    cleaned.append(clean_sentence)
  return cleaned

# Return a random item in a list:
def random_item_in(object_list):
  rand_numb = random.randint(0, len(object_list) - 1)
  return object_list[rand_numb]

# Return a random value from a JSON file:
def random_value(source_path, key):
  all_values = read_values_from_json(source_path, key)
  clean_values = clean_strings(all_values)
  return random_item_in(clean_values)

####################
##     QUOTES     ##
####################

# Gather quotes from San Antonio:

def random_quote():
  return random_value(quote_file, 'quote')

####################
##   CHARACTERS   ##
####################

# Gather characters list from Wikipedia

def random_character():
  return random_value(character_file, 'character')

####################
##  INTERACTION   ##
####################

# Print a random sentence:

def print_random_sentence():
  rand_quote = random_quote()
  rand_character = random_character()
  print(">>>>> {} a dit: {}".format(rand_character, rand_quote))

def main_loop():
  while True:
    print_random_sentence()
    message = ("Voulez-vous voir une autre citation?"
      "Pour sortir du programme, tapez [B]")
    choice = input(message).upper()
    if choice == 'B':
      break
      # stops the loop

if __name__ == '__main__':
  main_loop()


from socket import socket, AF_INET, SOCK_STREAM
from .match.matchmaking import MatchMaking
from .malloc.room import Room
from .player_info.player import Player
from .rule.tictactoe import TicTacToe
from typing import Any
from dataclasses import dataclass, field
import time

def get_time() -> str:
    return time.strftime("%Y.%m.%d - %H:%M:%S")

@dataclass(frozen=True, kw_only=True)
class Config:
    IP: str
    PORT: int
    backlog: int
    number_of_room: int
    created_at: str = field(default_factory=get_time)

class TicTacToeServer:
    def __init__(self) -> None:
        self.matchmaker: MatchMaking
        self.listener: socket
        self.rooms: dict[int, Room]
        self.ids: list[int]
        self.room_count: int = 0

    def setting(self, config: Config) -> None:
        self.listener = socket(AF_INET, SOCK_STREAM)
        self.listener.bind((config.IP, config.PORT))
        self.listener.listen(config.backlog)
        self.listener.setblocking(False)
        self.ids = [_id for _id in range(config.number_of_room)]
        self.rooms = {}
        self.matchmaker = MatchMaking(2)

    def update(self) -> None:
        client: socket
        address: Any
        try:
            client, address = self.listener.accept()
            self.matchmaker.push(Player(client, address))
            print("Accepted", len(self.matchmaker.player_list))
        except:
            ...
        players: list[Player] = self.matchmaker.match()
        if len(players) == 2:
            self.rooms[self.ids.pop()] = Room(rule=TicTacToe, players=players)
            self.room_count += 1
            print("Matched", self.room_count)
        call_back: list[int] = []
        for room_id, room in self.rooms.items():
            room.update()
            if room.end_check:
                room.close()
                call_back.append(room_id)
        for room_id in call_back:
            self.room_count -= 1
            print(self.rooms[room_id].info, self.room_count)
            self.rooms.pop(room_id)
            self.ids.append(room_id)


import math
import tensorflow as tf
import numpy as np
import logging

from . import data_utils

class SkipthoughtModel:

    SUPPORTED_CELLTYPES = ['lstm', 'gru']

    def __init__(self, cell_type, num_hidden, num_layers, embedding_size, max_vocab_size,
                 learning_rate, decay_rate, decay_steps, grad_clip, num_samples, max_length_decoder):
        self.cell_type = cell_type
        self.max_length_decoder = max_length_decoder
        self.grad_clip = grad_clip
        self.decay_rate = decay_rate
        self.decay_steps = decay_steps
        self.lr = learning_rate
        self.max_vocab_size = max_vocab_size
        self.embedding_size = embedding_size
        self.num_hidden = num_hidden
        self.num_layers = num_layers
        self.num_samples = num_samples
        self._logger = logging.getLogger(__name__)

        self._logger.info("Creating SkipthoughModel.")
        self._check_args()
        self._logger.info("Checked args.")

        if self.cell_type == 'lstm':
            self.cell_fn = lambda x: tf.nn.rnn_cell.BasicLSTMCell(x, state_is_tuple=True)
        elif self.cell_type == 'gru':
            self.cell_fn = tf.nn.rnn_cell.GRUCell

        self._create_placeholders()
        self._logger.info("Created placeholders.")
        self._create_network()
        self._logger.info("Created SkipthoughtModel.")

    def _check_args(self):
        if self.cell_type not in self.SUPPORTED_CELLTYPES:
            raise ValueError("This cell type is not supported.")
        if self.num_samples <= 0 or self.num_samples > self.max_vocab_size:
            raise ValueError("num_samples must be greater than zero and leq than max_vocab_size")

    def _create_placeholders(self):
        with tf.variable_scope('placeholders'):
            self.encoder_input = tf.placeholder(tf.int32, [None, None], name='encoder_input')
            self.encoder_seq_len = tf.placeholder(tf.int32, [None, ], name='encoder_sequence_lengths')

            self.prev_decoder_input = [tf.placeholder(tf.int32, [None, ], name="prev_decoder_input{0}".format(i))
                                       for i in range(self.max_length_decoder)]
            self.prev_decoder_target = [tf.placeholder(tf.int32, [None, ], name="prev_decoder_target{0}".format(i))
                                        for i in range(self.max_length_decoder)]
            self.prev_decoder_weights = [tf.placeholder(tf.float32, shape=[None,], name="prev__decoder_weight{0}".format(i))
                                         for i in range(self.max_length_decoder)]

            self.next_decoder_input = [tf.placeholder(tf.int32, [None, ], name="next_decoder_input{0}".format(i))
                                       for i in range(self.max_length_decoder)]
            self.next_decoder_target = [tf.placeholder(tf.int32, [None, ], name="next_decoder_target{0}".format(i))
                                        for i in range(self.max_length_decoder)]
            self.next_decoder_weights = [tf.placeholder(tf.float32, shape=[None,], name="next__decoder_weight{0}".format(i))
                                         for i in range(self.max_length_decoder)]

    def _create_decoder(self, scope_name, encoder_state, decoder_input):
        with tf.variable_scope(scope_name):
            embedded_prev = [tf.nn.embedding_lookup(self.embedding_matrix, inp) for inp in decoder_input]

            cell = self.cell_fn(self.num_hidden)

            # cell = tf.nn.rnn_cell.OutputProjectionWrapper(cell, self.max_vocab_size)

            w_t = tf.get_variable("proj_w", [self.max_vocab_size, self.num_hidden])
            w = tf.transpose(w_t)
            b = tf.get_variable("proj_b", [self.max_vocab_size])
            output_projection = (w, b)

            decoder_outputs, _ = tf.nn.seq2seq.rnn_decoder(embedded_prev, initial_state=encoder_state,
                                                           cell=cell)

        loop_function_predict = tf.nn.seq2seq._extract_argmax_and_embed(self.embedding_matrix, output_projection=(w, b), update_embedding=False)
        with tf.variable_scope(scope_name, reuse=True):
            decoder_predict_hiddens, _ = tf.nn.seq2seq.rnn_decoder(embedded_prev, initial_state=encoder_state,
                                                                       cell=cell, loop_function=loop_function_predict)

            decoder_predict_logits = [tf.nn.xw_plus_b(x, w, b) for x in decoder_predict_hiddens]
        return decoder_outputs, decoder_predict_logits, output_projection

    def _create_encoder(self, embedded, cudnn=False):
        with tf.variable_scope('encoder'):
            if cudnn:
                if self.cell_type == 'lstm':
                    pass
                    # lstm_cell = tf.contrib.cudnn_rnn.CudnnLSTM(self.num_layers, self.num_hidden, self.embedding_size)
                    # params_size_t = lstm_cell.params_size()
                    # input_h = tf.Variable(tf.ones([self.num_layers, 64, self.num_hidden]), name='input_h')
                    # input_c = tf.Variable(tf.ones([self.num_layers, 64, self.num_hidden]), name='input_c')
                    # params = tf.Variable(tf.ones([params_size_t]), validate_shape=False, name='params_lstm')
                    # lstm_cell(is_training=True, )
                else:
                    pass
                    # tf.contrib.cudnn_rnn.CudnnGRU(self.num_layers, self.num_hidden, self.embedding_size)
            else:
                cell = self.cell_fn(self.num_hidden)
                if self.num_layers > 1:
                    cell = tf.nn.rnn_cell.MultiRNNCell([cell] * self.num_layers)
                encoder_output, encoder_state = tf.nn.dynamic_rnn(cell, dtype=tf.float32,
                                                                  inputs=embedded,
                                                                  sequence_length=self.encoder_seq_len,
                                                                  swap_memory=True)
                if self.num_layers == 1:
                    encoder_state = encoder_state
                else:
                    assert isinstance(encoder_state, tuple)
                    encoder_state = encoder_state[-1]
        self._logger.info("Encoder done")
        return encoder_state

    def _create_network(self):
        self._logger.info("Create computational graph")
        with tf.variable_scope('embeddings'):
            # Default initializer for embeddings should have variance=1.
            sqrt3 = math.sqrt(3)  # Uniform(-sqrt(3), sqrt(3)) has variance=1.
            initializer = tf.random_uniform_initializer(-sqrt3, sqrt3)
            self.embedding_matrix = tf.get_variable("embedding_matrix",
                                                    shape=[self.max_vocab_size, self.embedding_size],
                                                    initializer=initializer)
            embedded = tf.nn.embedding_lookup(self.embedding_matrix, self.encoder_input)
        self._logger.info("Embeddings done")

        self.encoder_state = self._create_encoder(embedded, cudnn=False)

        prev_decoder_outputs, prev_decoder_predict_logits, prev_decoder_output_proj = \
            self._create_decoder("prev_decoder", self.encoder_state, self.prev_decoder_input)
        self._logger.info("Prev decoder done")

        next_decoder_outputs, next_decoder_predict_logits, next_decoder_output_proj = \
            self._create_decoder("next_decoder", self.encoder_state, self.next_decoder_input)
        self._logger.info("Next decoder done")

        self.prev_decoder_outputs = prev_decoder_outputs
        self.prev_decoder_predict_logits = prev_decoder_predict_logits
        self.prev_decoder_predict = [tf.argmax(logit, 1) for logit in self.prev_decoder_predict_logits]

        self.next_decoder_outputs = next_decoder_outputs
        self.next_decoder_predict_logits = next_decoder_predict_logits
        self.next_decoder_predict = [tf.argmax(logit, 1) for logit in self.next_decoder_predict_logits]

        def get_sampled_loss(w, b):
            w_t = tf.transpose(w)

            def sampled_loss(inputs, labels):
                labels = tf.reshape(labels, [-1, 1])
                # We need to compute the sampled_softmax_loss using 32bit floats to
                # avoid numerical instabilities.
                local_w_t = tf.cast(w_t, tf.float32)
                local_b = tf.cast(b, tf.float32)
                local_inputs = tf.cast(inputs, tf.float32)
                return tf.nn.sampled_softmax_loss(local_w_t, local_b, local_inputs, labels,
                                                  self.num_samples, self.max_vocab_size)
            return sampled_loss

        prev_sampled_loss = get_sampled_loss(*prev_decoder_output_proj)
        next_sampled_loss = get_sampled_loss(*next_decoder_output_proj)
        loss_prev = tf.nn.seq2seq.sequence_loss(prev_decoder_outputs,
                                                self.prev_decoder_target,
                                                self.prev_decoder_weights,
                                                softmax_loss_function=prev_sampled_loss)
        loss_next = tf.nn.seq2seq.sequence_loss(next_decoder_outputs,
                                                self.next_decoder_target,
                                                self.next_decoder_weights,
                                                softmax_loss_function=next_sampled_loss)
        self.loss = loss_prev + loss_next

        global_step = tf.Variable(0, trainable=False)
        learning_rate = tf.train.exponential_decay(self.lr, global_step, self.decay_steps,
                                                   self.decay_rate, staircase=True)

        optimizer = tf.train.AdamOptimizer(learning_rate=learning_rate)
        tvars = tf.trainable_variables()
        grads = tf.gradients(self.loss, tvars)
        clipped_grads, _ = tf.clip_by_global_norm(grads, self.grad_clip)
        self.train_op = optimizer.apply_gradients(zip(clipped_grads, tvars), global_step=global_step)
        self._logger.info("Loss and optimizer done")

    def _fill_feed_dict_train(self, enc_inp,
                              prev_inp, prev_targ,
                              next_inp, next_targ):
        """Fills feed dictionary.

        Args:
            enc_inp (data_utils.Batch): Encoder input batch.
            prev_inp (data_reader.Batch): Prev decoder input batch.
            prev_targ (data_reader.Batch): Prev decoder target batch.
            next_inp (data_reader.Batch): Next decoder input batch.
            next_targ (data_reader.Batch): Next decoder target batch.
        Returns:
            feed_dict (dict): Feed dictionary.
        """
        assert prev_inp.shape == prev_targ.shape == next_inp.shape == next_targ.shape
        assert prev_inp.shape[1] == self.max_length_decoder
        max_len = self.max_length_decoder

        feed_dict = {self.encoder_input: enc_inp.data, self.encoder_seq_len: enc_inp.seq_lengths}
        feed_dict.update({self.prev_decoder_input[i]: prev_inp.data[:, i] for i in range(max_len)})
        feed_dict.update({self.prev_decoder_target[i]: prev_targ.data[:, i] for i in range(max_len)})
        feed_dict.update({self.prev_decoder_weights[i]: prev_targ.weights[:, i] for i in range(max_len)})

        feed_dict.update({self.next_decoder_input[i]: next_inp.data[:, i] for i in range(max_len)})
        feed_dict.update({self.next_decoder_target[i]: next_targ.data[:, i] for i in range(max_len)})
        feed_dict.update({self.next_decoder_weights[i]: next_targ.weights[:, i] for i in range(max_len)})
        return feed_dict

    def _fill_feed_dict_predict(self, curr):
        feed_dict = {self.encoder_input: curr.data, self.encoder_seq_len: curr.seq_lengths,
                     self.prev_decoder_input[0]: np.array([curr.go_value]),
                     self.next_decoder_input[0]: np.array([curr.go_value])}
        return feed_dict

    def train_step(self, enc_inp, prev_inp, prev_targ, next_inp, next_targ):
        """Returns train_op, loss and feed_dict for performing sess.run(...) on them.

        Args:
            enc_inp (data_utils.Batch): Encoder input with a shape [batch_size, batch_length].
                Batch length can vary from batch to batch.
            prev_inp (data_reader.Batch): Prev decoder input with a shape [batch_size, self.max_decoder_length]
            prev_targ (data_reader.Batch): Prev decoder target with a shape [batch_size, self.max_decoder_length]
            next_inp (data_reader.Batch): Next decoder input with a shape [batch_size, self.max_decoder_length]
            next_targ (data_reader.Batch): Next decoder target with a shape [batch_size, self.max_decoder_length]
        Returns:
            (self.train_op, self.loss, feed_dict)
        """
        feed_dict = self._fill_feed_dict_train(enc_inp, prev_inp, prev_targ, next_inp, next_targ)
        return self.train_op, self.loss, feed_dict

    def encode(self, curr):
        feed_dict = self._fill_feed_dict_predict(curr)
        return self.encoder_state, feed_dict

    def predict(self, curr):
        feed_dict = self._fill_feed_dict_predict(curr)
        return self.prev_decoder_predict, self.next_decoder_predict, feed_dict


from keras.models import Sequential
from keras.layers import Dense, Conv1D, GlobalAveragePooling1D, MaxPooling1D, Flatten
from keras.utils import to_categorical
import pandas as pd
import numpy as np
import pickle
from sklearn.metrics import classification_report

pickle_in = open("Dataset_4class_20person_TopBody.pckl","rb")
dataset = pickle.load(pickle_in)

np.random.shuffle(dataset)

x_train = dataset[:, :-1]
y_train = dataset[:, -1]
y_train = to_categorical(y_train, 4)

x_train = np.expand_dims(x_train, axis=2)
x_test = x_train

model = Sequential()
model.add(Conv1D(64, 3, activation="relu", input_shape=(990, 1)))
model.add(Conv1D(64, 3, activation="relu"))
model.add(MaxPooling1D(3))
model.add(Flatten())
model.add(Dense(units=100, activation="relu"))
model.add(Dense(units=4, activation="softmax"))
model.compile(loss="mean_squared_error",
              optimizer="adam",
              metrics=["accuracy"])

model.fit(x_train, y_train, batch_size=32, epochs=10, validation_split=0.33)

model.save("Model_4class_20personX4_TopBody.h5")

y_pred = model.predict(x_train)
y_pred = np.argmax(y_pred, axis = 1)
print(classification_report(np.argmax(y_train, axis=1), y_pred))


import numpy as np
import math
import matplotlib.pyplot as plt
from numba import jit, njit, prange, vectorize, float64, jitclass
import numba as nb
from collections import namedtuple
import scipy.interpolate
#def njit(*args, **kwargs):
#    return lambda f: f

# TODO: Rewrite in cython

VddmParams = namedtuple('VddmParams',
    'dt     std     damping     tau_threshold   pass_threshold  scale   act_threshold',
    defaults=(                                  0.0,            1.0,    1.0)
        )
eps = np.finfo(float).eps

DEFAULT_MINACT = -3.0
DEFAULT_MAXACT = 3.0

@njit()
def normpdf(x, m, v):
    return np.exp(-(x - m)**2/(2*v))/np.sqrt(2*np.pi*v)

@vectorize()
def normcdf(x, m, v):
    return (1.0 + math.erf((x - m)/np.sqrt(v*2)))/2.0

@vectorize()
def stdnormcdf(x):
    return 0.5 + math.erf(x)/2.0

@njit()
def normsf(*args):
    return 1.0 - normcdf(*args)

@jitclass(dict(start=float64, dv=float64, N=nb.uint64))
class Grid1d:
    def __init__(self, start, end, N):
        self.start = start
        self.dv = (end - start)/N
        self.N = N

    def __getitem__(self, i):
        return self.start + i*self.dv

    @property
    def size(self):
        return self.N

    @property
    def end(self):
        return self.start + self.N*self.dv

    def closest(self, x):
        return int((x - self.start)/self.dv)

    @property
    def values(self):
        return np.linspace(self.start, self.end, self.N)

@jitclass(dict(grid=Grid1d.class_type.instance_type, y=float64[:], upper_fill=float64))
class GridInterp1d:
    def __init__(self, grid, y, upper_fill):
        self.grid = grid
        self.y = y
        self.upper_fill = upper_fill

    def get(self, x):
        if x < self.grid.start: return np.nan
        if x > self.grid.end: return self.upper_fill
        return self.y[self.grid.closest(x)] # TODO: linear interpolation

@njit(parallel=False, fastmath=True)
def vdd_step(p, da, tau, acts, pweights, nweights, decision_prob=1.0):
    decided = 0.0
    N = acts.size
    dt = p.dt; std=p.std; damping=p.damping;
    tau_threshold=p.tau_threshold; scale=p.scale; act_threshold=p.act_threshold

    alpha = 1 - np.exp(-dt*damping)
    diff_var = dt*std**2
    if tau >= p.pass_threshold:
        diff_mean_tau = dt*np.arctan(scale*(tau - tau_threshold))
    else:
        diff_mean_tau = dt*np.pi/2

    hacks = math.sqrt(2.0*diff_var)
    for fr in range(N):
        too_small = 0.0
        frw = pweights[fr]
        act_fr = acts[fr]
        diff_mean = diff_mean_tau - alpha*act_fr
        for to in range(N - 1):
            #diff = acts[to] - act_fr
            small_enough = stdnormcdf((acts[to] - act_fr + da/2 - diff_mean)/hacks)
            nweights[to] += (small_enough - too_small)*frw
            too_small = small_enough
        nweights[-1] += (1.0 - small_enough)*frw

    # TODO: No need to do the whole loop, but one more loop here
    # doesn't matter so much
    for i in range(N):
        a = acts[i]
        # Assume the activations are uniformly distributed within a
        # bin. Important for getting continuous derivatives w.r.t. act_threshold
        share_over = (a - p.act_threshold)/da + 0.5
        share_over = min(max(share_over, 0.0), 1.0)
        bindec = decision_prob*share_over*nweights[i]
        nweights[i] -= bindec
        decided += bindec
    nweights /= np.sum(nweights)
    return decided

@njit()
def vdd_activation_pdf(p, taus, N=100, minact=DEFAULT_MINACT, maxact=DEFAULT_MAXACT):
    acts = np.linspace(minact, maxact, N)
    da = acts[1] - acts[0]
    weights = np.zeros((len(taus) + 1, N))
    weights[0, np.searchsorted(acts, 0.0)] = 1.0

    deadpdf = np.empty(len(taus))
    alive = 1.0

    for i, tau in enumerate(taus):
        dead = alive*vdd_step(p, da, tau, acts, weights[i], weights[i+1])
        alive -= dead
        deadpdf[i] = dead

    return weights[1:], deadpdf/p.dt, acts

@njit()
def vdd_decision_pdf(p, taus, N=100, minact=DEFAULT_MINACT, maxact=DEFAULT_MAXACT):
    #acts = np.linspace(minact, maxact, N)
    #da = acts[1] - acts[0]
    acts = Grid1d(minact, maxact, N)
    da = acts.dv

    pweights = np.zeros(N)
    #pweights[np.searchsorted(acts, 0.0)] = 1.0
    pweights[acts.closest(0.0)] = 1.0
    nweights = np.zeros_like(pweights)

    deadpdf = np.empty(len(taus))
    alive = 1.0

    for i, tau in enumerate(taus):
        dead = alive*vdd_step(p, da, tau, acts, pweights, nweights)
        pweights, nweights = nweights, pweights
        nweights[:] = 0

        alive -= dead
        deadpdf[i] = dead

    return GridInterp1d(Grid1d(0.0, len(taus)*p.dt, len(taus)), crossedpdf/p.dt, uncrossed)

@njit()
def vdd_blocker_activation_pdf(p, taus, blocker_taus, N=100, minact=DEFAULT_MINACT, maxact=DEFAULT_MAXACT):
    acts = np.linspace(minact, maxact, N)
    da = acts[1] - acts[0]
    weights = np.zeros((len(taus) + 1, N))
    weights[0, np.searchsorted(acts, 0.0)] = 1.0
    bweights = weights.copy()

    crossedpdf = np.empty(len(taus))
    unblockedpdf = np.empty(len(taus))
    uncrossed = 1.0
    blocked = 1.0

    #cp = p._replace(pass_threshold=0.0)
    cp = VddmParams(
        dt=p.dt, std=p.std, damping=p.damping, tau_threshold=p.tau_threshold, pass_threshold=0.0,
        scale=p.scale, act_threshold=p.act_threshold
    )
    for i, (tau, btau) in enumerate(zip(taus, blocker_taus)):
        gone = 1.0 if btau <= p.pass_threshold else 0.0
        unblocked = blocked*vdd_step(p, da, btau, acts, bweights[i], bweights[i+1], decision_prob=gone)
        blocked -= unblocked

        crossed = uncrossed*vdd_step(cp, da, tau, acts, weights[i], weights[i+1], 1.0 - blocked)
        uncrossed -= crossed

        crossedpdf[i] = crossed
        unblockedpdf[i] = unblocked

    return (weights[1:], bweights[1:]), (crossedpdf/p.dt, unblockedpdf/p.dt), acts

@njit
def vdd_blocker_decision_pdf(p, taus, blocker_taus, N=50, minact=DEFAULT_MINACT, maxact=DEFAULT_MAXACT):
    acts = Grid1d(minact, maxact, N)
    da = acts.dv
    #acts = np.linspace(minact, maxact, N)
    #da = acts[1] - acts[0]

    weights = np.zeros(N)
    pweights = np.zeros(N)
    pweights[acts.closest(0.0)] = 1.0
    #pweights[np.searchsorted(acts, 0.0)] = 1.0

    weights_b = np.zeros(N)
    pweights_b = np.zeros(N)
    pweights_b[acts.closest(0.0)] = 1.0
    #pweights_b[np.searchsorted(acts, 0.0)] = 1.0

    crossedpdf = np.empty(len(taus))
    unblockedpdf = np.empty(len(taus))
    uncrossed = 1.0
    blocked = 1.0

    #cp = p._replace(pass_threshold=0.0)
    # Damn numba!
    cp = VddmParams(
        dt=p.dt, std=p.std, damping=p.damping, tau_threshold=p.tau_threshold, pass_threshold=0.0,
        scale=p.scale, act_threshold=p.act_threshold
    )
    for i, (tau, btau) in enumerate(zip(taus, blocker_taus)):
        gone = 1.0 if btau <= p.pass_threshold else 0.0
        unblocked = blocked*vdd_step(p, da, btau, acts, pweights_b, weights_b, decision_prob=gone)
        blocked -= unblocked
        weights_b, pweights_b = pweights_b, weights_b
        weights_b[:] = 0

        crossed = uncrossed*vdd_step(cp, da, tau, acts, pweights, weights, 1.0 - blocked)
        uncrossed -= crossed
        weights, pweights = pweights, weights
        weights[:] = 0

        crossedpdf[i] = crossed
        unblockedpdf[i] = unblocked

    return GridInterp1d(Grid1d(0.0, len(taus)*p.dt, len(taus)), crossedpdf/p.dt, uncrossed)
    #return scipy.interpolate.interp1d(
    #        np.linspace(0, len(taus)*p.dt, len(taus)), crossedpdf/p.dt,
    #        fill_value=(np.nan, uncrossed),
    #        bounds_error=False
    #        )

def vdd_loss(trials, dt, N=100):
    taus, rts = zip(*trials)
    ts = [np.arange(len(tau))*dt for tau in taus]

    def loss(**kwargs):
        lik = 0.0
        for tau, rts in zip(taus, rts):
            kwargs['dt'] = dt
            pdf = vdd_decision_pdf(VddmParams(**kwargs), tau, N=N)
            #lik += np.sum(np.log(pdf(rt) + eps))
            for rt in rts: lik += np.log(pdf.get(rt) + eps)

        return -lik

    return loss

def vdd_blocker_loss(trials, dt, N=100):
    taus, btaus, rtss = zip(*trials)
    def loss(p):
        lik = 0.0
        for i in range(len(taus)):
            tau = taus[i]
            btau = btaus[i]
            rts = rtss[i]
            pdf = vdd_blocker_decision_pdf(p, tau, btau, N=N)
            for rt in rts: lik += np.log(pdf.get(rt) + eps)

        return -lik

    return lambda **kwargs: loss(VddmParams(**dict(dt=dt, **kwargs)))

def test_activations():
    dt = 1/30
    dur = 20
    ts = np.arange(0, dur, dt)

    tau0 = 3.0
    speed = 20.0
    dist = tau0*speed - ts*speed
    tau = dist/speed

    param = dict(
            dt=dt,
            std=0.75,
            damping=1.6,
            tau_threshold=2.3,
            scale=1.0,
            act_threshold=1.0,
            pass_threshold=0.0
    )
    N = 100
    act, crossing_prob, actgrid = vdd_activation_pdf(VddmParams(**param), tau, N=N)
    #crossing_prob = vdd_decision_pdf(hacktau, dt, N=500, minact=-10, **param)
    #plt.plot(actgrid, act[30])
    #plt.show()
    dead = np.cumsum(crossing_prob*dt)
    alive = 1 - dead
    plt.pcolormesh(ts, actgrid, np.sqrt(act*alive.reshape(-1, 1) + np.finfo(float).eps).T, cmap='jet', antialiased=True)
    plt.colorbar()
    plt.twinx()
    plt.plot(ts, crossing_prob)
    plt.show()

def test_blocked():
    dt = 1/30
    dur = 20
    ts = np.arange(0, dur, dt)

    tau0 = 8.0
    speed = 20.0
    dist = tau0*speed - ts*speed
    tau = dist/speed

    tau0b = tau0 - 3.0

    distb = tau0b*speed - ts*speed
    taub = distb/speed

    param = dict(
            dt=dt,
            std=0.75,
            damping=1.6,
            tau_threshold=2.3,
            scale=1.0,
            act_threshold=1.0,
            pass_threshold=0.0
    )

    (act, actb), (crossing_prob, unblock_prob), actgrid = vdd_blocker_activation_pdf(
            VddmParams(**param), tau, taub, N=200,
            #minact=-1.0, maxact=1.0
            )
    #plt.plot(ts, crossing_prob)
    #plt.plot(ts, 1 - np.cumsum(unblock_prob))

    uncrossed = 1.0 - np.cumsum(crossing_prob*dt)
    act_dens = act*uncrossed.reshape(-1, 1)
    a = act
    plt.pcolormesh(ts, actgrid, a.T, vmax=np.max(a[10:]), cmap='jet')
    plt.twinx()
    plt.plot(ts, crossing_prob)

    plt.figure()
    plt.pcolormesh(ts, actgrid, actb.T, vmax=np.max(a[10:]), cmap='jet')
    plt.twinx()
    plt.plot(ts, unblock_prob)
    plt.show()

def test_gridsize():
    dt = 1/30
    dur = 20
    ts = np.arange(0, dur, dt)

    tau0 = 3.0
    speed = 20.0
    dist = tau0*speed - ts*speed
    tau = dist/speed

    param = dict(
            dt=dt,
            std=0.75,
            damping=1.6,
            tau_threshold=2.3,
            scale=1.0,
            act_threshold=1.0,
            pass_threshold=0.0
    )

    for N in [25, 50, 100, 200]:
        crossing_prob = vdd_decision_pdf(VddmParams(**param), tau, N=N)
        plt.plot(ts, crossing_prob, label=N)
    plt.legend()
    plt.show()

def benchmark():
    dt = 1/30
    dur = 10
    ts = np.arange(0, dur, dt)

    tau0 = 3.0
    speed = 20.0
    dist = tau0*speed - ts*speed
    tau = dist/speed

    tau0b = tau0 - 0.0

    distb = tau0b*speed - ts*speed
    taub = distb/speed

    param = dict(
            dt=dt,
            std=0.5,
            damping=0.5,
            tau_threshold=2.5,
            scale=1.0,
            act_threshold=0.5,
            pass_threshold=0.0
    )
    N = 100

    # Compilation
    vdd_decision_pdf(VddmParams(**param), tau, N=N)
    import time
    trials = 10
    st = time.perf_counter()
    for i in range(trials):
        pdf = vdd_decision_pdf(VddmParams(**param), tau, N=N)
    dur = time.perf_counter() - st
    print(f"{dur/trials*1000} ms per run")

    plt.plot(ts, pdf.y)
    plt.show()

if __name__ == '__main__':
    #test_activations()
    test_blocked()
    #benchmark()
    #test_gridsize()


"""
Mixin methods to be attached to :class:`oqhazlib.gsim.GroundShakingIntensityModel`
"""

from __future__ import absolute_import, division, print_function, unicode_literals

import numpy as np

## Try importing truncnorm speedup
try:
	from ..c_speedups import (norm, truncnorm)
	from ..c_speedups.norm import sf as _norm_sf
except:
	print("Failed importing truncnorm speedup!")
	from scipy.stats import truncnorm
	from scipy.special import ndtr
	from openquake.hazardlib.gsim.base import _truncnorm_sf
	_norm_sf = lambda zvalues: ndtr(-zvalues)
else:
	_truncnorm_sf = lambda tl, zvalues: truncnorm.sf(zvalues, -tl, tl)
	print("Successfully imported truncnorm speedup!")

__all__ = ['make_contexts', 'get_poes_cav']

def make_contexts(self, sites, rupture):
	"""
	Wrapper for :func:`rshalib.gsim.make_gsim_contexts`
	"""
	return make_gsim_contexts(self, sites, rupture)

def get_poes_cav(self, sctx, rctx, dctx, imt, imls, truncation_level,
				 cav_min=0.16, cav_max_mag=5.5, depsilon=0.02,
				 eps_correlation_model="EUS"):
	"""
	Calculate and return probabilities of exceedance (PoEs) of one or more
	intensity measure levels (IMLs) of one intensity measure type (IMT)
	for one or more pairs "site -- rupture", jointly with the probability
	of exceeding a minimum CAV value.

	:param sctx:
		An instance of :class:`SitesContext` with sites information
		to calculate PoEs on.
	:param rctx:
		An instance of :class:`RuptureContext` with a single rupture
		information.
	:param dctx:
		An instance of :class:`DistancesContext` with information about
		the distances between sites and a rupture.

		All three contexts (``sctx``, ``rctx`` and ``dctx``) must conform
		to each other. The easiest way to get them is to call
		:meth:`make_contexts`.
	:param imt:
		An intensity measure type object (that is, an instance of one
		of classes from :mod:`openquake.hazardlib.imt`).
	:param imls:
		List of interested intensity measure levels (of type ``imt``).
	:param truncation_level:
		Can be ``None``, which means that the distribution of intensity
		is treated as Gaussian distribution with possible values ranging
		from minus infinity to plus infinity.

		When set to zero, the mean intensity is treated as an exact
		value (standard deviation is not even computed for that case)
		and resulting array contains 0 in places where IMT is strictly
		lower than the mean value of intensity and 1.0 where IMT is equal
		or greater.

		If truncation_level is None or zero, no CAV filtering is applied.

		When truncation level is positive number, the intensity
		distribution is processed as symmetric truncated Gaussian with
		range borders being ``mean - truncation_level * stddev`` and
		``mean + truncation_level * stddev``. That is, the truncation
		level expresses how far the range borders are from the mean
		value and is defined in units of sigmas. The resulting PoEs
		for that mode are values of complementary cumulative distribution
		function of that truncated Gaussian applied to IMLs.
	:param cav_min:
		Float, CAV threshold (in g.s)
		(default: 0.16)
	:param cav_max_mag:
		Float, maximum magnitude to consider for CAV filtering
		(default: 5.5)
	:param depsilon:
		Float, bin width used to discretize epsilon pga. Should be
		sufficiently small for PGA
		(default: 0.02).
	:param eps_correlation_model:
		Str, name of model used for correlation of epsilon values
		of PGA and SA, either "WUS" or "EUS"
		(default: "EUS")

	:returns:
		2-D float array, joint probabilities for each site (first
		dimension) and each IML (second dimension)

	:raises ValueError:
		If truncation level is not ``None`` and neither non-negative
		float number, and if ``imts`` dictionary contain wrong or
		unsupported IMTs (see :attr:`DEFINED_FOR_INTENSITY_MEASURE_TYPES`).
	"""
	from openquake.hazardlib import const
	from openquake.hazardlib import imt as imt_module

	from ..cav.cav_oq import (calc_cav_exceedance_prob, B1_FREQS, B1_WUS, B1_EUS)

	if truncation_level is not None and truncation_level < 0:
		raise ValueError('truncation level must be zero, positive number '
						 'or None')
	self._check_imt(imt)

	if truncation_level == 0:
		## zero truncation mode, just compare imls to mean
		## No CAV filtering
		imls = self.to_distribution_values(imls)
		mean, _ = self.get_mean_and_stddevs(sctx, rctx, dctx, imt, [])
		mean = mean.reshape(mean.shape + (1, ))
		return (imls <= mean).astype(float)

	else:
		## use real normal distribution
		assert (const.StdDev.TOTAL
				in self.DEFINED_FOR_STANDARD_DEVIATION_TYPES)
		ln_imls = self.to_distribution_values(imls)
		ln_sa_med, [sigma_ln_sa] = self.get_mean_and_stddevs(sctx, rctx, dctx,
													imt, [const.StdDev.TOTAL])
		nsites = len(ln_sa_med)
		ln_sa_med = ln_sa_med.reshape((nsites, 1))
		sigma_ln_sa = sigma_ln_sa.reshape((nsites, 1))
		zvalues = (ln_imls - ln_sa_med) / sigma_ln_sa
		if truncation_level is None:
			## CAV filtering not supported
			if cav_min > 0:
				raise Exception('CAV filtering not supported '
								'if truncation level is None')
			return _norm_sf(zvalues)

		else:
			sa_exceedance_prob = _truncnorm_sf(truncation_level, zvalues)
			if cav_min == 0 or rctx.mag > cav_max_mag:
				return sa_exceedance_prob
			else:
				imt_pga = imt_module.PGA()
				if imt != imt_pga:
					depsilon = 0.5
				## Normally distributed epsilon values and corresponding probabilities
				neps = int(truncation_level / depsilon) * 2 + 1
				eps_pga_array = np.linspace(-truncation_level, truncation_level,
											neps)
				prob_eps_array = (truncnorm.pdf(eps_pga_array, -truncation_level,
												truncation_level) * depsilon)
				#prob_eps_array /= np.cumsum(prob_eps_array)

				## Pre-calculate CAV exceedance probabilities for epsilon PGA
				ln_pga_eps_pga_array = np.zeros((neps, nsites))
				cav_exceedance_prob = np.zeros((neps, nsites))
				ln_pga_med, [sigma_ln_pga] = self.get_mean_and_stddevs(sctx, rctx,
												dctx, imt_pga, [const.StdDev.TOTAL])

				for e, eps_pga in enumerate(eps_pga_array):
					## Determine PGA corresponding to eps_pga
					ln_pga = ln_pga_med + eps_pga * sigma_ln_pga
					ln_pga_eps_pga_array[e] = ln_pga
					## CAV exceedance probability for PGA
					cav_exceedance_prob[e] = calc_cav_exceedance_prob(ln_pga,
											rctx.mag, sctx.vs30, cav_min=cav_min)

				joint_exceedance_probs = np.zeros((nsites, len(imls)))

				if imt == imt_pga:
					## Integrate explicitly over epsilon
					for e in range(neps):
						for d in range(nsites):
							idxs = ln_pga_eps_pga_array[e][d] > ln_imls
							joint_exceedance_probs[d][idxs] += \
								(prob_eps_array[e] * cav_exceedance_prob[e, d])

				else:
					if eps_correlation_model == "WUS":
						b1 = np.interp([1./imt.period], B1_FREQS, B1_WUS,
										left=B1_WUS[0], right=B1_WUS[-1])[0]
					elif eps_correlation_model == "EUS":
						b1 = np.interp([1./imt.period], B1_FREQS, B1_EUS,
										left=B1_EUS[0], right=B1_EUS[-1])[0]

					## Eq. 3-3
					sigma_ln_sa_given_pga = np.sqrt(1 - b1*b1) * sigma_ln_sa

					## Loop over eps_pga
					for e in range(neps):
						eps_pga = eps_pga_array[e]
						## Determine epsilon value of SA, and sigma
						## Eq. 3-1
						eps_sa = b1 * eps_pga

						## Eq. 3-2
						ln_sa_given_pga = ln_sa_med + eps_sa * sigma_ln_sa

						for d in range(nsites):
							## Determine probability of exceedance of SA given PGA
							## Eq. 4-3
							eps_sa_dot = ((ln_imls - ln_sa_given_pga[d])
											/ sigma_ln_sa_given_pga[d])
							## Eq. 4-2
							prob_sa_given_pga = _truncnorm_sf(truncation_level,
															eps_sa_dot)

							joint_exceedance_probs[d] += \
								(prob_eps_array[e] * cav_exceedance_prob[e, d]
								* prob_sa_given_pga)

					## Workaround to make sure SA values are properly truncated
					joint_exceedance_probs = np.minimum(joint_exceedance_probs,
														sa_exceedance_prob)

				return joint_exceedance_probs


import argparse
import configparser
import logging
import tqdm
import time
class TqdmLoggingHandler(logging.Handler):
    def __init__(self, level=logging.NOTSET):
        super().__init__(level)

    def emit(self, record):
        try:
            msg = self.format(record)
            tqdm.tqdm.write(msg)
            time.sleep(0.05)
            self.flush()
        except Exception:
            self.handleError(record)

def parse_input():
    parser = argparse.ArgumentParser(
        prog ='Canvas grader',
        description='A script for batch grade and comment upload to Canvas LMS.')
    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        "--list", "-l", help='list all assignments', action="store_true")
    group.add_argument("--grade", "-g", nargs=2,
                       metavar=('assignment_id', 'filepath'), help='grading mode')
    group.add_argument("--batch_upload", "-u", help='upload batch file', nargs=1)
    group.add_argument("--create_batch", "-b", help='create batch file', action="store_const", const=True)

    # parser.add_argument("assignment_id", help='assignment ID')
    # parser.add_argument('filename', help='grade filename')
    return parser.parse_args()

def parse_config():
    config = configparser.ConfigParser()
    config.read('settings.ini')
    return config['Settings']

def setup_logger(assignment_id):
    logging.getLogger(__name__)
    logging.basicConfig(filename='logs/' + str(assignment_id) + '.log',
                        format='%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s',
                        datefmt='%H:%M:%S',
                        level=logging.WARNING)
    # adding tqdm logger to preserve pg bar at the bottom
    logging.getLogger('').addHandler(TqdmLoggingHandler())


'''
Created on 11/06/2015

@author: wacero
'''

from obspy.signal import filter
import numpy as np
import os
import logging

from obspy.clients.arclink import Client as clientArclink
from obspy.clients.seedlink import Client as clientSeedlink
from obspy.clients.filesystem.sds import Client as clientArchive
import DBConexion
import json

def getRMS(datosFlujo):
    """
    Calculate rms of a time series

    The :func: `rsam.getRMS` calculates

    """

    datosCuadrado=[]
    sumData=np.float64()
    mediaDatos=np.float64()

    arrayNP64=np.array(datosFlujo,dtype='float64')
    arrayNP64SQR=np.square(arrayNP64)
    sumData=arrayNP64SQR.sum()
    mediaDatos=sumData/float(len(datosFlujo))
    rms=mediaDatos**(0.5)
    return rms

def fillZeros(estacion,canal,diaUTC):
    rmsData['rsam_estacion']=estacion
    rmsData['rsam_canal']=canal
    rmsData['rsam_fecha_proceso']=str(diaUTC.datetime)
    rmsData['rms']=0
    rmsData['rsam_banda1']=0
    rmsData['rsam_banda2']=0
    rmsData['rsam_banda3']=0
    rmsData['rsam_banda4']=0

def fillRMS(strSta,dUTC):

    rmsData['rsam_estacion']=strSta[0].stats['station']
    rmsData['rsam_canal']=strSta[0].stats['channel']
    rmsData['rsam_fecha_proceso']=str(dUTC.datetime)

    rmsData['rms']=getRMS(strSta[0].data)

    dataFilt1=filter.bandpass(strSta[0].data,0.05,0.125,strSta[0].stats['sampling_rate'])
    rmsData['rsam_banda1']=getRMS(dataFilt1)

    dataFilt2=filter.bandpass(strSta[0].data,2,8,strSta[0].stats['sampling_rate'])
    rmsData['rsam_banda2']=getRMS(dataFilt2)

    dataFilt3=filter.bandpass(strSta[0].data,0.25,2,strSta[0].stats['sampling_rate'])
    rmsData['rsam_banda3']=getRMS(dataFilt3)

    dataFilt4=filter.highpass(strSta[0].data,10.0,strSta[0].stats['sampling_rate'],corners=1,zerophase=True)
    rmsData['rsam_banda4']=getRMS(dataFilt4)

def chooseService(parSrv):

    if parSrv['name'] == 'ARCLINK':
        try:
            print("Trying  Arclink : %s %s %s" %(parSrv['user'],parSrv['serverIP'],parSrv['port']) )
            return clientArclink(parSrv['user'],parSrv['serverIP'],int(parSrv['port']))
        except Exception as e:
            print("Error Arclink : %s -- %s %s %s" %(str(e), parSrv['user'],parSrv['serverIP'],parSrv['port']) )
            return -1
    elif parSrv['name'] == 'SEEDLINK':
        try:
            print("Trying  Seedlink : %s %s" %(parSrv['serverIP'],parSrv['port']) )
            return clientSeedlink(parSrv['serverIP'],int(parSrv['port']),timeout=5 )
        except Exception as e:
            print("Error Seedlink :%s -- %s %s" %(str(e),parSrv['serverIP'],parSrv['port']) )
            return -1
    elif parSrv['name'] == 'ARCHIVE':
        try:
            print("Trying  Archive : %s " %(parSrv['archivePath']) )
            return clientArchive(parSrv['archivePath'])
        except Exception as e:
            print("Error Archive : %s -- %s" %(str(e),parSrv['archivePath']))
            return -1

def readConfigFile(jsonFile):
    try:
        with open(jsonFile) as json_data_files:
            return json.load(json_data_files)
    except Exception as e:
        print("Error readConfigFile(): %s" %str(e))
        return -1

def getStream(service,conSrv,net,cod,loc,cha,dUTC):
    print('Starting station: %s %s %s %s' %(net,cod,loc,cha))
    if service=='ARCLINK':
        try:
            streamStat=conSrv.get_waveforms(net,cod,loc,cha,dUTC,dUTC + 60,route=False,compressed=False)
            return streamStat
        except Exception as e:
            print("Error getting streamStat: %s" %str(e))
    else:
        try:
            streamStat=conSrv.get_waveforms(net,cod,loc,cha,dUTC,dUTC + 60)
            return streamStat
        except Exception as e:
            print("Error getting streamStat: %s" %str(e))

def rsamInsert(stations,dUTC,service,conSrv,conDB):

    for stname,sta in stations.iteritems():
        for loc in sta['loc']:
            for cha in sta['cha']:
                print(sta['net'],sta['cod'],loc,cha)
                streamStat=getStream(service,conSrv,sta['net'],sta['cod'],loc,cha,dUTC)
                if streamStat != None:
                    if len(streamStat)>0:
                        print("Starting calculateRSAM()")
                        print(streamStat)
                        fillRMS(streamStat,dUTC)
                    else:
                        print("insertar 0")
                        fillZeros(sta['cod'], cha, dUTC)
                    print(rmsData)
                    DBConexion.insertRow(conDB,rmsData)
                else:
                    continue

def rsamUpdate(stations,dUTC,service,conSrv,conDB):

    for stname,sta in stations.iteritems():
        for loc in sta['loc']:
            for cha in sta['cha']:
                print(sta['net'],sta['cod'],loc,cha)
                streamStat=getStream(service,conSrv,sta['net'],sta['cod'],loc,cha,dUTC)
                if streamStat != None:
                    if len(streamStat)>0:
                        print("Starting calculateRSAM()")
                        print(streamStat)
                        fillRMS(streamStat,dUTC)
                    else:
                        print("insertar 0")
                        fillZeros(sta['cod'], cha, dUTC)
                    print(rmsData)
                    DBConexion.updateRow(conDB,rmsData)
                else:
                    continue

rmsData={'rsam_estacion':'','rsam_canal':'','rsam_fecha_proceso':'','rms':'','rsam_banda1':'','rsam_banda2':'','rsam_banda3':'','rsam_banda4':''}


from collections import deque

def DFS(edge_list, S):
    global V, stack
    result = [S]
    stack.append(S)
    visited = [0 for _ in range(V+1)]
    visited[S] = 1
    while stack:
        for j in range(1, V+1):
            if edge_list[stack[-1]][j] == 1 and visited[j] == 0:
                visited[j] = 1
                stack.append(j)
                result.append(j)
                break
        else:
            stack.pop()
    return result

def BFS(edge_list, S):
    global V, queue
    result = [S]
    queue.append(S)
    visited = [0 for _ in range(V+1)]
    visited[S] = 1
    while queue:
        for j in range(1, V+1):
            if edge_list[queue[0]][j] == 1 and visited[j] == 0:
                visited[j] = 1
                queue.append(j)
                result.append(j)
                break
        else:
            queue.popleft()
    return result

V = int(input())
edge_list = []
stack, queue = deque(), deque()