import warnings
warnings.filterwarnings("ignore",category=DeprecationWarning)
warnings.simplefilter(action='ignore', category=FutureWarning)
import psutil, os, peakutils, time, sys, ast, codecs, re, itertools
import numpy as np
import multiprocessing as mp
from scipy.signal import savgol_filter
from scipy.signal import find_peaks
import matplotlib.pyplot as plt

def spawn(options):
	options = ast.literal_eval(options)
	procs = list()
	n_cpus = int(options[0])
	buffer_points = np.asarray(range(int(options[1])+1))

	job_indexes = jobDiv(buffer_points, n_cpus)
	lock = mp.Lock()
	if 0.5*psutil.cpu_count() < n_cpus:
		logical = np.asarray(range(1, 2*int(n_cpus - psutil.cpu_count()/2), 2))
		print('Core Affinity Logical:', logical)
		physical = np.asarray(range(0, 2*int(psutil.cpu_count()/2), 2))
		print('Core Affinity Physical:', physical)
		core_affinity_list = np.concatenate((physical, logical))
	else:
		core_affinity_list = np.asarray(range(0, 2*int(n_cpus), 2))
		print('Core Affinity Physical:', core_affinity_list)
	sys.stdout.flush()
	for cpu_i in range(n_cpus):
		d = dict(affinity = int(core_affinity_list[cpu_i].item()), work = job_indexes[cpu_i], ID = cpu_i, options = options, lock=lock)
		p = mp.Process(target=wrapper_targetFunc, kwargs=d)
		p.start()
		procs.append(p)
	for p in procs:
		p.join()
	print('Returned to Main')
	sys.stdout.flush()
	sys.exit(0)

def wrapper_targetFunc(affinity, work, ID, options, lock):
	waiting = False
	proc = psutil.Process()  # get self pid
	proc.cpu_affinity([affinity])
	print('Process-'+str(ID)+' queueing ',work)
	sys.stdout.flush()
	etd = time.time()
	targetFunc(work, etd, ID, options, lock)
	lock.acquire()
	print('Process-'+str(ID)+' Task Complete')
	if ID == 0:
		print('Percent Complete =',100)
		print('Returning to Main')
		waiting = True
	if waiting == True:
		print('Waiting')
	sys.stdout.flush()
	lock.release()
	os._exit(0)

def jobDiv(inputArray, numCPUs):
	jobs = []
	arrayLength = len(inputArray)
	jobRange = int(arrayLength / numCPUs)
	extra = arrayLength - (jobRange * numCPUs)
	prevEnd = 0
	for c in range(numCPUs):
		endIdx = (c * jobRange) + jobRange - 1
		if (c == (numCPUs-1)):
			endIdx += extra
		startIdx = prevEnd
		if ( (c > 0) and (startIdx+1 < arrayLength) ):
			startIdx += 1
		jobs.append( (startIdx, endIdx) )
		prevEnd = endIdx
	return jobs

def targetFunc(work, etd, ID, options, lock):
	ncpus, buffer_points, headerSize, buffer, encoder_bin, nData, resample_factor, enconder_sampling, encoder_smooth, file_type = options
	roots = []
	angs = []

	time.sleep(ID/ncpus)

	if 'bin' in file_type:
		f = open(encoder_bin+'.bin', 'rb')
		f.seek(0)

	if 'qex' in file_type:
		headerSize, line_bytes, dt, nData, nLines = header_read_qex(encoder_bin+'.qex')
		f = open(encoder_bin+'.qex', 'rb')
		#encoder = np.zeros(nLines)
		encoder_2 = np.zeros(np.int(np.floor(nLines/resample_factor))+100)
		#time_data = np.zeros(nLines)
		f.seek(0)

	for i in range(work[0], work[1]+1):
		start_time = etd

		if 'bin' in file_type:
			f.seek(int(headerSize+(i*buffer)))
			if ((i+1)*buffer) > nData*4:
				data_E = np.fromfile(f, dtype='f4', count = -1)
			else:
				data_E = np.fromfile(f, dtype='f4', count = int(buffer/4))
			y = data_E[0::resample_factor]

		if 'qex' in file_type:
			f.seek(int(headerSize))
			last = 0
			first = 0

			for k in range(int(np.ceil(nLines/(resample_factor*80000)))):
				if ((k+1)*(resample_factor*80000)) > nLines:
					first = np.int(1+last)
					data_E = np.fromfile(f, dtype=dt, count = -1)
					#encoder[k*8000000:nLines+1] = data_E['encoder']
					encoder_2[first:first+np.int(len(data_E['encoder'][0::resample_factor]))] = data_E['encoder'][0::resample_factor]
					last = last + len(data_E['encoder'][0::resample_factor])
					#time_data[k*8000000:nLines+1] = data_E['time']
				else:
					if last != 0:
						first = np.int(last+1)
					data_E = np.fromfile(f, dtype=dt, count = int(resample_factor*80000))
					#encoder[k*8000000:(k+1)*8000000] = data_E['encoder']
					encoder_2[first:first+np.int(len(data_E['encoder'][0::resample_factor]))] = data_E['encoder'][0::resample_factor]
					#time_data[k*8000000:(k+1)*8000000] = data_E['time']
					last = last + len(data_E['encoder'][0::resample_factor])

				lock.acquire()
				print('data read :', (100*(k+1))/int(np.ceil(nLines/(resample_factor*80000))), ' %')
				sys.stdout.flush()
				lock.release()

			encoder_2 = encoder_2[:last]

		#takes derivate of encoder data and applies Savitzky-Golay filter
		if 'bin' in file_type:
			try:
				c1_derivative = savgol_filter(np.gradient(savgol_filter(y, int(encoder_smooth), 2)), int(encoder_smooth), 2)
			except:
				c1_derivative = np.gradient(y)

		if 'qex' in file_type:
			##find turning points of encoder by peaks in number of averaged data points per encoder value
			##the minimum distance between peaks is defined by considering the number of encoder values in an oscillation (actually allowing for some 'short' oscillations)

			try:
				#smoothed_encoder = savgol_filter(encoder[0::resample_factor], int(encoder_smooth), 2)
				smoothed_encoder = savgol_filter(encoder_2, int(encoder_smooth), 2)
				c1_derivative = savgol_filter(np.gradient(smoothed_encoder), int(encoder_smooth), 2)
			except:
				#c1_derivative = np.gradient(encoder[0::resample_factor])\
				c1_derivative = np.gradient(encoder_2)

			print('print data :', 'derivative caluculated')

			indicies = np.asarray(np.where(np.diff(np.sign(c1_derivative)))[0]+1)

			#fig, axs = plt.subplots(2, 1)

			#axs[0].plot(smoothed_encoder)
			#axs[0].plot(indicies, smoothed_encoder[indicies], 'kx')
			#axs[0].set_xlabel('Encoder Datapoint')
			#axs[0].set_ylabel('Monochromator Angle')
			#
			#axs[1].plot(c1_derivative)
			#axs[1].plot(indicies, c1_derivative[indicies], 'kx')
			#axs[1].set_xlabel('Encoder Datapoint')
			#axs[1].set_ylabel('Derivative Monochromator Angle')
			#plt.show()
			#plt.close()

			print('print data :', 'indicies found')

			#roots_ang = list(encoder[0::resample_factor][indicies])
			roots_ang = list(encoder_2[indicies])

			#if (i == 0) & (ID == 0):
			#	min_diff = np.min(np.diff(indicies))
			#	#print('print data :', min_diff)
			#	#print('print data :', peaks)
			#	indicies2 = indicies[:-1] + min_diff
			#	#print('print data :', peaks2)
			#	sys.stdout.flush()
			#
			#	roots_ang2 = list(encoder[0::resample_factor][indicies2])
			#
			#	roots_check = list(itertools.chain(roots_ang, roots_ang2))
			#
			#	#print('print data :', roots_check)
			#	sys.stdout.flush()
			#
			#	B = [i for i in roots_check  if i >= np.average(roots_check)]
			#	C = [i for i in roots_check if i <= np.average(roots_check)]
			#
			#	max_ang = np.min(B)
			#	min_ang = np.max(C)
			#
			#	lock.acquire()
			#	print('min_ang :', min_ang)
			#	print('max_ang :', max_ang)
			#	sys.stdout.flush()
			#	lock.release()

		#finds the roots of the encoder file used for splitting the raw absorption data into spectra
		if 'bin' in file_type:
			indicies = np.asarray((np.where(np.diff(np.sign(c1_derivative)))[0]+1)*resample_factor)
			roots_ang = list(data_E[indicies])
		#if 'qex' in file_type:
		#	indicies = np.asarray((np.where(np.diff(np.sign(c1_derivative)))[0]+1))
		#	roots_ang = list(y[indicies])

		if 'bin' in file_type:
			roots_loop = indicies+(i*buffer/4)
		if 'qex' in file_type:
			roots_loop = indicies*resample_factor

		roots = np.concatenate((roots, roots_loop), axis=0)
		angs = np.concatenate((angs, roots_ang), axis=0)
		sys.stdout.flush()

		if ID == 0:
			tpl = time.time()-start_time
			lock.acquire()
			print('Percent Complete:',str(100*i/(work[1]-work[0]+1)))
			print('Time Per Loop (s):',tpl)
			print('Estimated Time Remaining:',time.strftime("%H:%M:%S", time.gmtime(int(tpl*(work[1]-i-1)))))
			sys.stdout.flush()
			lock.release()

	lock.acquire()
	print('angs_'+str(ID)+'_'+str(list(angs)))
	sys.stdout.flush()
	print('roots_'+str(ID)+'_'+str(list(roots)))
	sys.stdout.flush()
	lock.release()

def header_read_qex(filename):
	header_lines = []
	with codecs.open(filename, 'rb', encoding='cp1252', errors='ignore') as qex_file:
		line = qex_file.readline().strip('\r\n').replace('# ', '')
		while not '_Header_End_'in line:
			header_lines.append(line)
			line = qex_file.readline().strip('\r\n').replace('# ', '')

	#search headerlines for keywords
	headerSize = np.int([x for x in header_lines if 'FileHeaderSize_byte' in x][0].split(': ')[1])
	nColumns = np.int([x for x in header_lines if 'AdcNumberColumnsInDataFile' in x][0].split(': ')[1])
	nChannels = np.int([x for x in header_lines if 'AdcNumberChannelsStored' in x][0].split(': ')[1])
	DataLineFormat = ([x for x in header_lines if 'DataLineFormat' in x][0].split(': ')[1]).split(', ')
	DataLineLabels = ([x for x in header_lines if 'DataLineLabels' in x][0].split(': ')[1]).split(', ')

	#Interpret DataTypes to extract number of bytes per line
	line_bytes = int(sum([int(format_item[1]) for format_item in [re.split(r'(\d+)', s) for s in DataLineFormat]])/8)

	#calculate the number of datapoints in file
	nLines = int((os.path.getsize(filename) - headerSize)/(line_bytes))
	nData = int(nColumns*nLines)

	d_types = [('encoder', DataLineFormat[0]), ('time', DataLineFormat[1])]
	for i in range(nChannels):
		d_types.append(tuple(('CH '+str(i), DataLineFormat[i+2])))

	dt = np.dtype(d_types)

	return headerSize, line_bytes, dt, nData, nLines

if __name__ == '__main__':
	pyfile,options = sys.argv
	spawn(options)


"""empty message

Revision ID: edfad35c2de6
Revises: 53e5c0498957
Create Date: 2020-12-26 07:57:25.336764

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = "edfad35c2de6"
down_revision = "53e5c0498957"
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        "group_pair_reveals",
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("group_id", sa.Integer(), nullable=True),
        sa.Column("user_id", sa.Integer(), nullable=True),
        sa.Column("timestamp", sa.DateTime(), nullable=True),
        sa.ForeignKeyConstraint(
            ["group_id"],
            ["groups.id"],
        ),
        sa.ForeignKeyConstraint(
            ["user_id"],
            ["users.id"],
        ),
        sa.PrimaryKeyConstraint("id"),
    )
    op.add_column(
        "groups", sa.Column("reveal_latest_pairs", sa.Boolean(), nullable=True)
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_column("groups", "reveal_latest_pairs")
    op.drop_table("group_pair_reveals")
    # ### end Alembic commands ###


from sklearn.feature_selection import  SelectFromModel
from sklearn.svm import LinearSVC
from sklearn.datasets import  load_digits,load_diabetes
import numpy as np
import  matplotlib.pyplot as plt
from sklearn.linear_model import Lasso

def test_SelectFromModel():
    '''
    test the method of SelectFromModel
    :return: None
    '''
    digits=load_digits()
    X=digits.data
    y=digits.target
    estimator=LinearSVC(penalty='l1',dual=False)
    selector=SelectFromModel(estimator=estimator,threshold='mean')
    selector.fit(X,y)
    selector.transform(X)
    print("Threshold %s"%selector.threshold_)
    print("Support is %s"%selector.get_support(indices=True))
def test_Lasso(*data):
    '''
    test the correlation between alpha and sparse condition
    :param data: train_data, test_data, train_value, test_value
    :return: None
    '''
    X,y=data
    alphas=np.logspace(-2,2)
    zeros=[]
    for alpha in alphas:
        regr=Lasso(alpha=alpha)
        regr.fit(X,y)
        num=0
        for ele in regr.coef_:
            if abs(ele) < 1e-5:num+=1
        zeros.append(num)
    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)
    ax.plot(alphas,zeros)
    ax.set_xlabel(r"$\alpha$")
    ax.set_xscale("log")
    ax.set_ylim(0,X.shape[1]+1)
    ax.set_ylabel("zeros in coef")
    ax.set_title("Sparsity In Lasso")
    plt.show()
def test_LinearSVC(*data):
    '''
    test the correlation between C and sparse condition
    :param data: train_data, test_data, train_value, test_value
    :return: None
    '''
    X,y=data
    Cs=np.logspace(-2,2)
    zeros=[]
    for C in Cs:
        clf=LinearSVC(C=C,penalty='l1',dual=False)
        clf.fit(X,y)

        num=0
        for row in clf.coef_:
            for ele in row:
                if abs(ele) < 1e-5:num+=1
        zeros.append(num)

    fig=plt.figure()
    ax=fig.add_subplot(1,1,1)
    ax.plot(Cs,zeros)
    ax.set_xlabel("C")
    ax.set_xscale("log")
    ax.set_ylabel("zeros in coef")
    ax.set_title("Sparsity In SVM")
    plt.show()
if __name__=='__main__':
    test_SelectFromModel()
    data=load_diabetes()
    test_Lasso(data.data,data.target)
    data=load_digits()
    test_LinearSVC(data.data,data.target)


""" Simulation Level Data """
from __future__ import annotations
from typing import Dict, Callable, Tuple

import xarray as xr
import pydantic as pd
import numpy as np

from .monitor_data import MonitorDataTypes, MonitorDataType, AbstractFieldData
from ..base import Tidy3dBaseModel
from ..simulation import Simulation
from ..boundary import BlochBoundary
from ..types import Ax, Axis, annotate_type, Literal
from ..viz import equal_aspect, add_ax_if_none
from ...log import DataError, log, Tidy3dKeyError, ValidationError

DATA_TYPE_MAP = {data.__fields__["monitor"].type_: data for data in MonitorDataTypes}

class SimulationData(Tidy3dBaseModel):
    """Stores data from a collection of :class:`.Monitor` objects in a :class:`.Simulation`.

    Example
    -------
    >>> from tidy3d import ModeSpec, GridSpec, ScalarFieldDataArray, FieldMonitor, FieldData
    >>> num_modes = 5
    >>> x = [-1,1]
    >>> y = [-2,0,2]
    >>> z = [-3,-1,1,3]
    >>> f = [2e14, 3e14]
    >>> t = [0, 1e-12, 2e-12]
    >>> mode_index = np.arange(num_modes)
    >>> direction = ["+", "-"]
    >>> coords = dict(x=x, y=y, z=z, f=f)
    >>> scalar_field = ScalarFieldDataArray((1+1j) * np.random.random((2,3,4,2)), coords=coords)
    >>> field_monitor = FieldMonitor(size=(2,4,6), freqs=[2e14, 3e14], name='field', fields=['Ex'])
    >>> sim = Simulation(
    ...     size=(2, 4, 6),
    ...     grid_spec=GridSpec(wavelength=1.0),
    ...     monitors=[field_monitor],
    ...     run_time=2e-12,
    ... )
    >>> field_data = FieldData(monitor=field_monitor, Ex=scalar_field)
    >>> sim_data = SimulationData(simulation=sim, data=(field_data,))
    """

    simulation: Simulation = pd.Field(
        ...,
        title="Simulation",
        description="Original :class:`.Simulation` associated with the data.",
    )

    data: Tuple[annotate_type(MonitorDataType), ...] = pd.Field(
        ...,
        title="Monitor Data",
        description="List of :class:`.MonitorData` instances "
        "associated with the monitors of the original :class:`.Simulation`.",
    )

    log: str = pd.Field(
        None,
        title="Solver Log",
        description="A string containing the log information from the simulation run.",
    )

    diverged: bool = pd.Field(
        False,
        title="Diverged",
        description="A boolean flag denoting whether the simulation run diverged.",
    )

    def __getitem__(self, monitor_name: str) -> MonitorDataType:
        """Get a :class:`.MonitorData` by name. Apply symmetry if applicable."""
        monitor_data = self.monitor_data[monitor_name]
        return monitor_data.symmetry_expanded_copy

    @property
    def monitor_data(self) -> Dict[str, MonitorDataType]:
        """Dictionary mapping monitor name to its associated :class:`.MonitorData`."""
        return {monitor_data.monitor.name: monitor_data for monitor_data in self.data}

    @pd.validator("data", always=True)
    def data_monitors_match_sim(cls, val, values):
        """Ensure each MonitorData in ``.data`` corresponds to a monitor in ``.simulation``."""
        sim = values.get("simulation")
        if sim is None:
            raise ValidationError("Simulation.simulation failed validation, can't validate data.")
        for mnt_data in val:
            try:
                monitor_name = mnt_data.monitor.name
                sim.get_monitor_by_name(monitor_name)
            except Tidy3dKeyError as exc:
                raise DataError(
                    f"Data with monitor name {monitor_name} supplied "
                    "but not found in the Simulation"
                ) from exc
        return val

    @property
    def final_decay_value(self) -> float:
        """Returns value of the field decay at the final time step."""
        log_str = self.log
        if log_str is None:
            raise DataError(
                "No log string in the SimulationData object, can't find final decay value."
            )
        lines = log_str.split("\n")
        decay_lines = [l for l in lines if "field decay" in l]
        final_decay = 1.0
        if len(decay_lines) > 0:
            final_decay_line = decay_lines[-1]
            final_decay = float(final_decay_line.split("field decay: ")[-1])
        return final_decay

    def source_spectrum(self, source_index: int) -> Callable:
        """Get a spectrum normalization function for a given source index."""

        if source_index is None or len(self.simulation.sources) == 0:
            return np.ones_like

        source = self.simulation.sources[source_index]
        source_time = source.source_time
        times = self.simulation.tmesh
        dt = self.simulation.dt
        user_defined_phase = np.exp(1j * source_time.phase)

        # get boundary information to determine whether to use complex fields
        boundaries = self.simulation.boundary_spec.to_list
        boundaries_1d = [boundary_1d for dim_boundary in boundaries for boundary_1d in dim_boundary]
        complex_fields = any(isinstance(boundary, BlochBoundary) for boundary in boundaries_1d)

        # plug in mornitor_data frequency domain information
        def source_spectrum_fn(freqs):
            """Source amplitude as function of frequency."""
            spectrum = source_time.spectrum(times, freqs, dt, complex_fields)

            # remove user defined phase from normalization so its effect is present in the result
            return spectrum * np.conj(user_defined_phase)

        return source_spectrum_fn

    def renormalize(self, normalize_index: int) -> SimulationData:
        """Return a copy of the :class:`.SimulationData` with a different source used for the
        normalization."""

        num_sources = len(self.simulation.sources)
        if normalize_index == self.simulation.normalize_index or num_sources == 0:
            # already normalized to that index
            return self.copy()

        if normalize_index and (normalize_index < 0 or normalize_index >= num_sources):
            # normalize index out of bounds for source list
            raise DataError(
                f"normalize_index {normalize_index} out of bounds for list of sources "
                f"of length {num_sources}"
            )

        def source_spectrum_fn(freqs):
            """Normalization function that also removes previous normalization if needed."""
            new_spectrum_fn = self.source_spectrum(normalize_index)
            old_spectrum_fn = self.source_spectrum(self.simulation.normalize_index)
            return new_spectrum_fn(freqs) / old_spectrum_fn(freqs)

        # Make a new monitor_data dictionary with renormalized data
        data_normalized = [mnt_data.normalize(source_spectrum_fn) for mnt_data in self.data]

        simulation = self.simulation.copy(update=dict(normalize_index=normalize_index))

        return self.copy(update=dict(simulation=simulation, data=data_normalized))

    def load_field_monitor(self, monitor_name: str) -> AbstractFieldData:
        """Load monitor and raise exception if not a field monitor."""
        mon_data = self[monitor_name]
        if not isinstance(mon_data, AbstractFieldData):
            raise DataError(
                f"data for monitor '{monitor_name}' does not contain field data "
                f"as it is a `{type(mon_data)}`."
            )
        return mon_data

    def at_centers(self, field_monitor_name: str) -> xr.Dataset:
        """return xarray.Dataset representation of field monitor data
        co-located at Yee cell centers.

        Parameters
        ----------
        field_monitor_name : str
            Name of field monitor used in the original :class:`Simulation`.

        Returns
        -------
        xarray.Dataset
            Dataset containing all of the fields in the data
            interpolated to center locations on Yee grid.
        """

        # get the data
        monitor_data = self.load_field_monitor(field_monitor_name)

        # discretize the monitor and get center locations
        sub_grid = self.simulation.discretize(monitor_data.monitor, extend=False)
        centers = sub_grid.centers

        # pass coords if each of the scalar field data have more than one coordinate along a dim
        xyz_kwargs = {}
        for dim, centers in zip("xyz", (centers.x, centers.y, centers.z)):
            scalar_data = list(monitor_data.field_components.values())
            coord_lens = [len(data.coords[dim]) for data in scalar_data]
            if all(ncoords > 1 for ncoords in coord_lens):
                xyz_kwargs[dim] = centers

        return monitor_data.colocate(**xyz_kwargs)

    def get_intensity(self, field_monitor_name: str) -> xr.DataArray:
        """return `xarray.DataArray` of the intensity of a field monitor at Yee cell centers.

        Parameters
        ----------
        field_monitor_name : str
            Name of field monitor used in the original :class:`Simulation`.

        Returns
        -------
        xarray.DataArray
            DataArray containing the electric intensity of the field-like monitor.
            Data is interpolated to the center locations on Yee grid.
        """

        field_dataset = self.at_centers(field_monitor_name)

        field_components = ("Ex", "Ey", "Ez")
        if not all(field_cmp in field_dataset for field_cmp in field_components):
            raise DataError(
                "Field monitor must contain 'Ex', 'Ey', and 'Ez' fields to compute intensity."
            )

        intensity_data = 0.0
        for field_cmp in field_components:
            field_cmp_data = field_dataset.data_vars[field_cmp]
            intensity_data += abs(field_cmp_data) ** 2
        intensity_data.name = "Intensity"
        return intensity_data

    def plot_field(  # pylint:disable=too-many-arguments, too-many-locals, too-many-branches
        self,
        field_monitor_name: str,
        field_name: str,
        val: Literal["real", "imag", "abs"] = "real",
        eps_alpha: float = 0.2,
        robust: bool = True,
        vmin: float = None,
        vmax: float = None,
        ax: Ax = None,
        **sel_kwargs,
    ) -> Ax:
        """Plot the field data for a monitor with simulation plot overlayed.

        Parameters
        ----------
        field_monitor_name : str
            Name of :class:`.FieldMonitor`, :class:`.FieldTimeData`, or :class:`.ModeSolverData`
            to plot.
        field_name : str
            Name of `field` component to plot (eg. `'Ex'`).
            Also accepts `'int'` to plot intensity.
        val : Literal['real', 'imag', 'abs'] = 'real'
            Which part of the field to plot.
            If ``field_name='int'``, this has no effect.
        eps_alpha : float = 0.2
            Opacity of the structure permittivity.
            Must be between 0 and 1 (inclusive).
        robust : bool = True
            If True and vmin or vmax are absent, uses the 2nd and 98th percentiles of the data
            to compute the color limits. This helps in visualizing the field patterns especially
            in the presence of a source.
        vmin : float = None
            The lower bound of data range that the colormap covers. If `None`, they are
            inferred from the data and other keyword arguments.
        vmax : float = None
            The upper bound of data range that the colormap covers. If `None`, they are
            inferred from the data and other keyword arguments.
        ax : matplotlib.axes._subplots.Axes = None
            matplotlib axes to plot on, if not specified, one is created.
        sel_kwargs : keyword arguments used to perform `.sel()` selection in the monitor data.
            These kwargs can select over the spatial dimensions (`x`, `y`, `z`),
            frequency or time dimensions (`f`, `t`) or `mode_index`, if applicable.
            For the plotting to work appropriately, the resulting data after selection must contain
            only two coordinates with len > 1.
            Furthermore, these should be spatial coordinates (`x`, `y`, or `z`).

        Returns
        -------
        matplotlib.axes._subplots.Axes
            The supplied or created matplotlib axes.
        """

        # get the DataArray corresponding to the monitor_name and field_name

        # intensity
        if field_name == "int":
            field_data = self.get_intensity(field_monitor_name)
            val = "abs"

        # normal case (eg. Ex)
        else:
            field_monitor_data = self.load_field_monitor(field_monitor_name)
            if field_name not in field_monitor_data.field_components:
                raise DataError(f"field_name '{field_name}' not found in data.")
            field_data = field_monitor_data.field_components[field_name]

        # interp out any monitor.size==0 dimensions
        monitor = self.simulation.get_monitor_by_name(field_monitor_name)
        thin_dims = {
            "xyz"[dim]: monitor.center[dim]
            for dim in range(3)
            if monitor.size[dim] == 0 and "xyz"[dim] not in sel_kwargs
        }
        for axis, pos in thin_dims.items():
            if field_data.coords[axis].size <= 1:
                field_data = field_data.sel(**{axis: pos}, method="nearest")
            else:
                field_data = field_data.interp(**{axis: pos}, kwargs=dict(bounds_error=True))

        # warn about new API changes and replace the values
        if "freq" in sel_kwargs:
            log.warning(
                "'freq' suppled to 'plot_field', frequency selection key renamed to 'f' and 'freq' "
                "will error in future release, please update your local script to use 'f=value'."
            )
            sel_kwargs["f"] = sel_kwargs.pop("freq")
        if "time" in sel_kwargs:
            log.warning(
                "'time' suppled to 'plot_field', frequency selection key renamed to 't' and 'time' "
                "will error in future release, please update your local script to use 't=value'."
            )
            sel_kwargs["t"] = sel_kwargs.pop("time")

        # select the extra coordinates out of the data from user-specified kwargs
        for coord_name, coord_val in sel_kwargs.items():
            if field_data.coords[coord_name].size <= 1:
                field_data = field_data.sel(**{coord_name: coord_val}, method=None)
            else:
                field_data = field_data.interp(
                    **{coord_name: coord_val}, kwargs=dict(bounds_error=True)
                )
        field_data = field_data.squeeze(drop=True)
        non_scalar_coords = {name: val for name, val in field_data.coords.items() if val.size > 1}

        # assert the data is valid for plotting
        if len(non_scalar_coords) != 2:
            raise DataError(
                f"Data after selection has {len(non_scalar_coords)} coordinates "
                f"({list(non_scalar_coords.keys())}), "
                "must be 2 spatial coordinates for plotting on plane. "
                "Please add keyword arguments to `plot_field()` to select out the other coords."
            )

        spatial_coords_in_data = {
            coord_name: (coord_name in non_scalar_coords) for coord_name in "xyz"
        }

        if sum(spatial_coords_in_data.values()) != 2:
            raise DataError(
                "All coordinates in the data after selection must be spatial (x, y, z), "
                f" given {non_scalar_coords.keys()}."
            )

        # get the spatial coordinate corresponding to the plane
        planar_coord = [name for name, val in spatial_coords_in_data.items() if val is False][0]
        axis = "xyz".index(planar_coord)
        position = float(field_data.coords[planar_coord])

        # the frequency at which to evaluate the permittivity with None signaling freq -> inf
        freq_eps_eval = sel_kwargs["f"] if "f" in sel_kwargs else None
        return self.plot_field_array(
            field_data=field_data,
            axis=axis,
            position=position,
            val=val,
            freq=freq_eps_eval,
            eps_alpha=eps_alpha,
            robust=robust,
            vmin=vmin,
            vmax=vmax,
            ax=ax,
        )

    @equal_aspect
    @add_ax_if_none
    # pylint:disable=too-many-arguments, too-many-locals, too-many-branches, too-many-statements
    def plot_field_array(
        self,
        field_data: xr.DataArray,
        axis: Axis,
        position: float,
        val: Literal["real", "imag", "abs"] = "real",
        freq: float = None,
        eps_alpha: float = 0.2,
        robust: bool = True,
        vmin: float = None,
        vmax: float = None,
        ax: Ax = None,
    ) -> Ax:
        """Plot the field data for a monitor with simulation plot overlayed.

        Parameters
        ----------
        field_data: xr.DataArray
            DataArray with the field data to plot.
        axis: Axis
            Axis normal to the plotting plane.
        position: float
            Position along the axis.
        val : Literal['real', 'imag', 'abs'] = 'real'
            Which part of the field to plot.
        freq: float = None
            Frequency at which the permittivity is evaluated at (if dispersive).
            By default, chooses permittivity as frequency goes to infinity.
        eps_alpha : float = 0.2
            Opacity of the structure permittivity.
            Must be between 0 and 1 (inclusive).
        robust : bool = True
            If True and vmin or vmax are absent, uses the 2nd and 98th percentiles of the data
            to compute the color limits. This helps in visualizing the field patterns especially
            in the presence of a source.
        vmin : float = None
            The lower bound of data range that the colormap covers. If `None`, they are
            inferred from the data and other keyword arguments.
        vmax : float = None
            The upper bound of data range that the colormap covers. If `None`, they are
            inferred from the data and other keyword arguments.
        ax : matplotlib.axes._subplots.Axes = None
            matplotlib axes to plot on, if not specified, one is created.

        Returns
        -------
        matplotlib.axes._subplots.Axes
            The supplied or created matplotlib axes.
        """

        # select the cross section data
        interp_kwarg = {"xyz"[axis]: position}

        # select the field value
        if val not in ("real", "imag", "abs"):
            raise DataError(f"`val` must be one of `{'real', 'imag', 'abs'}`, given {val}.")

        if val == "real":
            field_data = field_data.real
        elif val == "imag":
            field_data = field_data.imag
        elif val == "abs":
            field_data = abs(field_data)

        if val == "abs":
            cmap = "magma"
            eps_reverse = True
        else:
            cmap = "RdBu"
            eps_reverse = False

        # plot the field
        xy_coord_labels = list("xyz")
        xy_coord_labels.pop(axis)
        x_coord_label, y_coord_label = xy_coord_labels[0], xy_coord_labels[1]
        field_data.plot(
            ax=ax, x=x_coord_label, y=y_coord_label, cmap=cmap, vmin=vmin, vmax=vmax, robust=robust
        )

        # plot the simulation epsilon
        ax = self.simulation.plot_structures_eps(
            freq=freq,
            cbar=False,
            alpha=eps_alpha,
            reverse=eps_reverse,
            ax=ax,
            **interp_kwarg,
        )

        # set the limits based on the xarray coordinates min and max
        x_coord_values = field_data.coords[x_coord_label]
        y_coord_values = field_data.coords[y_coord_label]
        ax.set_xlim(min(x_coord_values), max(x_coord_values))
        ax.set_ylim(min(y_coord_values), max(y_coord_values))

        return ax


from selenium import webdriver
from selenium.webdriver.common.desired_capabilities import DesiredCapabilities
from selenium.webdriver.chrome.options import Options

# first run "docker-compose up" to launch the webdrivers
chrome_options = Options()
chrome_options.add_argument("--headless")

drivers = []
for port in [5001, 5002, 5003]:
    drivers.append(
        webdriver.Remote(
            command_executor=f'http://localhost:{port}/wd/hub',
            desired_capabilities=DesiredCapabilities.CHROME,
            options=chrome_options)
    )

drivers[0].get("http://www.google.com")
drivers[0].save_screenshot(f'test0.png')
drivers[1].get("http://www.xtreamers.io")
drivers[0].save_screenshot(f'test1.png')
drivers[2].get("http://www.python.org")
drivers[0].save_screenshot(f'test2.png')

for driver in drivers:
    print(driver.title)

for driver in drivers:
    driver.quit()

# docker logs -f chrome1


## Internal Modules
from scripts.utils import DataLoc, MyLogger, catch_and_log_exception
from scripts.comms_twitter import CommsTwitter
from scripts.comms_dex_pancakeswapv2 import CommsDEXPancakeSwapV2
from scripts.comms_blockchain_data_providers import CommsBlockchainDataProviders

## External Libraries
from logging import log
from typing import Union
import json
import queue
import re
import multiprocessing

class StratCoinListing():
    def __init__(self, log_queue: Union[multiprocessing.Queue, None] = multiprocessing.Queue()):
        '''
        Parameters
        ----------
        mp : bool
            If any instances of this class will be in a child process, a multiprocessing.Queue() object should be passed to this argument.
            Else, pass NoneType.
        '''
        self.class_name = 'ReconCoinListing'
        self.user_handle = 'binance'
        self.DataLoc = DataLoc()
        with open(self.DataLoc.File.CONFIG.value) as json_file:
            config = json.load(json_file)[self.class_name]
        self.CommsTwitter = CommsTwitter(log_queue=log_queue)
        self.CommsBlockchainDataProviders = CommsBlockchainDataProviders(blockchain_net='mainnet', log_queue=log_queue)
        self.MyLogger = MyLogger(log_queue=log_queue)
        self.logger = self.MyLogger.configure_logger(fileloc=self.DataLoc.Log.STRAT_COIN_LISTING.value)

    def is_tweet_new_listing(self, text):
        match = re.search('Binance will list .*?\((\w*?)\)', text)  # .*? is any character; \( is an open parentheses; (\w*?) is in another set of parentheses to group it seperately; \) is a close parentheses
        if match is not None:
            symbol = match.group(1)
        else:
            symbol = None
        return symbol

    @catch_and_log_exception
    def listen_for_new_listing(self, formatted_tweet_queue: multiprocessing.Queue, new_listing_queue: multiprocessing.Queue):
        self.MyLogger.activate_mp_logger()
        self.logger.debug('Process started.')
        while True:
            try:
                formatted_tweet = formatted_tweet_queue.get(block=True, timeout=5)
                ## Check for Kill Signal
                if formatted_tweet == 'kill_signal':
                    self.logger.info('Kill signal received, terminating process.')
                    break
                ## Look for New Listing Info in Tweet Text
                formatted_tweet = list(formatted_tweet.values())[0]
                genesis_text = formatted_tweet['genesis']['text']  # the text of genesis (account holder's) tweet
                symbol = self.is_tweet_new_listing(text=genesis_text)
                if symbol is not None:
                    self.logger.info(f'New listing found for symbol "{symbol}".')
                    new_listing_queue.put(symbol)
            except queue.Empty:
                pass
        return

    def run_strat(self, tweet_queue: multiprocessing.Queue, order_queue: multiprocessing.Queue):
        self.logger.debug('Strategy started.')
        while True:
            ## Get Tweet
            try:
                tweet = tweet_queue.get(block=True, timeout=3600)
            except queue.Empty:
                self.logger.debug(f'Found no {self.user_handle} tweet for 3600 seconds...')
            ## Parse Tweet
            symbol = self.is_tweet_new_listing(text=tweet)
            ## Send Order
            if symbol is not None:
                order_queue.put(
                    self.make_order(
                        underlying=symbol,
                        quote='usdt',
                        order_type='market',
                        side='buy',
                        asset='spot',
                        quantity=0.00001234,
                        price=0,
                        exchanges='cex'
                    )
                )
            ## Check for Exit Signal
            elif tweet == 'exit signal code: 8m3hxg087mg4hc58g4c5ehc5g34o34':
                return
            pass  ## queue.get(block=True) alleviates need for time.sleep() argument; we want every queued tweet to be processed asap


from pip._vendor import pkg_resources
from pip_upgrade.version_checker import version_check, not_equal_check
from pip_upgrade.store import Store

class DependenciesBase:
    def __init__(self):
        self.self_check = False

        self.packages = [dist.project_name for dist in pkg_resources.working_set]
        self.be_upgraded = {}
        self.wont_upgrade = {}

        self.dict = self.create_dict(self.packages)
        self.outdated = None

    def create_dict(self, packages):
        """
            Creates a dict of Stores class for packages
        """
        return {x: Store(x) for x in packages}

    def get_dependencies(self):
        """
            The main func for getting dependencies and comparing them to output a final list
        """

        self.retrieve_dependencies()

        for pkg_dict in self.outdated:
            pkg_name = pkg_dict['name']
            current_version = pkg_dict['version']
            latest_version = pkg_dict['latest_version']

            try:
                pkg_store = self.dict[pkg_name]
            except:
                try:
                    pkg_store = self.dict[pkg_name.lower()]
                except Exception as e:
                    if '_' in pkg_name:     # Fix for '_'
                        pkg_name = pkg_name.replace('_', '-')
                    try:
                        pkg_store = self.dict[pkg_name]
                    except Exception as e:
                        raise e

            pkg_store.current_version = current_version
            pkg_store.latest_version = latest_version

            self.compare_deps(pkg_store)

    def compare_deps(self, pkg_store):
        """
            Compares dependencies in a list and decides what packages' final version should be
        """
        result = []
        done = False

        for key in ['==', '~=', '<', '<=']:
            if len(pkg_store.data[key]) > 0:
                result = [key, min(pkg_store.data[key])]
                done = True
                break
        if not done:
            for key in ['>', '>=']:
                if len(pkg_store.data[key]) > 0:
                    result = [key, max(pkg_store.data[key])]
                    done = True
                    break
            #  Nonequal Check
            for item in pkg_store.data['!=']:
                not_equal_check(item, pkg_store.latest_version)
                result = ['!=', pkg_store.latest_version]

        if version_check(result, pkg_store.latest_version):
            self.be_upgraded[pkg_store.name] = result

    def retrieve_dependencies(self):
        """
            Retrieves dependencies pkg_main requires, and puts all dependent packages in self.dict with their version.
        """
        for pkg_main in self.packages:

            try:
                dep_list = pkg_resources.working_set.by_key[pkg_main].requires()
            except:
                dep_list = pkg_resources.working_set.by_key[pkg_main.lower()].requires()

            for i in dep_list:
                name = i.name        # Name of dependency
                specs = i.specs      # Specs of dependency

                if len(specs) != 0:
                    try:
                        self.dict[name] += specs
                    except:
                        for key in self.dict:
                            if key.lower() == name.lower():
                                name = key
                        try:
                            self.dict[name] += specs
                        except Exception as e:
                            if '_' in name:     # Fix for '_'
                                name = name.replace('_', '-')
                            try:
                                self.dict[name] += specs
                            except Exception as e:
                                # raise e
                                print(f'Skipping {name}, warning: Name mismatch. This will be improved. Manually upgrade if needed')

    def check_name_in_dict(self):
        """
            Case and '_', '-' checks. Returns the true name on dict
        """
        return


import asyncio
import re
import traceback
from datetime import datetime

from telethon.errors import MessageNotModifiedError
from telethon.tl import functions
from telethon.tl.types import SendMessageTypingAction, SendMessageCancelAction, KeyboardButtonCallback, Message, \
    MessageService, PeerUser, User

class BotActionBranch:

    def __init__(self, tg_bot_controller, branch_parent, branch_code=None):
        if branch_code is None:
            branch_code = ''
        self.tg_bot_controller = tg_bot_controller
        self.max_commands = 1
        self.branches = []
        self.read_once_callbacks = {}
        self.default_pick_action_text = 'Выберите дальнейшее действие'
        self.default_not_avail_command_text = 'Невозможно выполнить команду!\n\nДля получения списка доступных команд нажмите **[Продолжить]**'
        self.default_error_text = 'Какая-то ошибка!\n\n[error_name]'
        self.yes_variants = ['1', 'да', 'ок', 'yes', 'ok', 'y', 'хорошо', '+']
        self.no_variants = ['0', 'нет', 'не', 'no', 'not', 'n', '-']
        self.branch_parent = branch_parent
        self.branch_code = branch_code
        self.last_typing_sent_date = None
        self.use_timer = False
        self.commands = {
            '/help': {
                'cmd': self.cmd_help,
                'display_condition': self.do_not_display,
                'bot_button': {
                    'title': 'Справка',
                    'position': [1000, 1],
                },
                'places': ['bot', 'dialog'],
                'rights_level': 0,
                'desc': 'краткая справка, это ее ты сейчас видишь.'
            },
            '/main': {
                'cmd': self.cmd_to_top,
                'display_condition': self.is_in_second_branch,
                'bot_button': {
                    'title': 'Главное меню',
                    'position': [1000, 0],
                },
                'places': ['bot'],
                'rights_level': 0,
                'desc': 'вернуться в основное меню'
            },
            '/back': {
                'cmd': self.cmd_back,
                'display_condition': self.is_in_third_branches,
                'bot_button': {
                    'title': 'Назад',
                    'position': [1000, 0],
                },
                'places': ['bot'],
                'rights_level': 0,
                'desc': 'подняться на уровень выше'
            },
            '/enter_str': {
                'cmd': self.cmd_enter_str,
                'display_condition': self.do_not_display,
                'places': ['bot'],
                'rights_level': 0,
                'desc': None
            },
        }
        self.command_groups = {}
        self.sub_commands_forbidden = []
        self.sub_commands_forbidden_command = {
            'places': [],
            'rights_level': 10,
            'desc': None
        }

    async def on_timer(self):
        pass

    def on_init_finish(self):
        if self != self.tg_bot_controller:
            if self.branch_parent:
                self.branch_parent.branches.append(self)
            self.tg_bot_controller.sub_commands_forbidden = self.tg_bot_controller.sub_commands_forbidden + list(self.commands.keys())

    def register_cmd_branches(self):
        for k in self.commands.keys():
            if ('class' in self.commands[k]) and self.commands[k]['class']:
                cmd_class = self.commands[k]['class']
                if issubclass(cmd_class, BotActionBranch):
                    obj = cmd_class(self.tg_bot_controller, self, str(k).strip('/'))
                    self.commands[k]['cmd'] = obj
                    self.commands[k]['condition'] = obj.can_use_branch
                    obj.register_cmd_branches()
        if self.use_timer:
            self.tg_bot_controller.tg_client.register_on_timer(self.on_timer)

    def can_use_branch(self, user_id):
        return True

    async def user_link(self, user_id, user_name=None):
        if not user_name:
            user_name = await self.tg_bot_controller.tg_client.get_entity_name(user_id, 'User')
        return "["+user_name+"](tg://user?id="+str(user_id)+")"

    async def get_commands_buttons_list(self, for_user_id):
        button_rows = {}

        def place_button(title, cmd, position):
            row = position[0]
            col = position[1]
            if row not in button_rows:
                button_rows[row] = {}
            while col in button_rows[row]:
                col = col + 1
            button_rows[row][col] = KeyboardButtonCallback(title, str(cmd).encode())

        curr_place = self.tg_bot_controller.get_user_place_code(for_user_id)
        curr_rights = await self.tg_bot_controller.get_user_rights_level_realtime(for_user_id)
        for k in self.commands.keys():
            if (
                self.commands[k]['desc'] and
                (curr_place in self.commands[k]['places']) and
                (curr_rights >= self.commands[k]['rights_level']) and
                (('condition' not in self.commands[k]) or self.commands[k]['condition'](for_user_id)) and
                (('display_condition' not in self.commands[k]) or self.commands[k]['display_condition'](for_user_id)) and
                (curr_place == 'bot') and ('bot_button' in self.commands[k])
            ):
                place_button(self.commands[k]['bot_button']['title'], k, self.commands[k]['bot_button']['position'])

        button_rows = list(map(
            lambda x: list(map(
                lambda xs: xs[1],
                sorted(x[1].items(), key=lambda kx: kx[0])
            )),
            sorted(button_rows.items(), key=lambda kv: kv[0])
        ))

        return button_rows

    async def get_commands_description_list(self, for_user_id, str_pick_text=None):
        if str_pick_text is None:
            str_pick_text = self.default_pick_action_text
        result_str = []
        curr_place = self.tg_bot_controller.get_user_place_code(for_user_id)
        curr_rights = await self.tg_bot_controller.get_user_rights_level_realtime(for_user_id)
        commands_results = []
        commands_count = 0
        for k in self.commands.keys():
            if (
                self.commands[k]['desc'] and
                (curr_place in self.commands[k]['places']) and
                (curr_rights >= self.commands[k]['rights_level']) and
                (('condition' not in self.commands[k]) or self.commands[k]['condition'](for_user_id)) and
                (('display_condition' not in self.commands[k]) or self.commands[k]['display_condition'](for_user_id))
            ):
                if k in self.command_groups:
                    commands_results.append('')
                    if self.command_groups[k]:
                        commands_results.append(self.command_groups[k])
                        commands_results.append('')
                commands_count = commands_count + 1
                if (curr_place != 'bot') or ('bot_button' not in self.commands[k]):
                    str_k = str(k)
                else:
                    str_k = '**[' + self.commands[k]['bot_button']['title'] + ']**'
                commands_results.append( str_k + ' - ' + str(self.commands[k]['desc']).replace('[me_user]', self.tg_bot_controller.tg_client.me_user_entity_name))
        result_str.append('\n'+str_pick_text+' (' + str(commands_count) + '/' + str(self.max_commands) + '):\n')
        result_str = result_str + commands_results
        return result_str

    @staticmethod
    def adapt_command(text):
        text = re.sub(r'[^\w\s!\\?/]', ' ', text)
        text = re.sub(' +', ' ', text)
        return text.strip().lower()

    def is_in_current_branch(self, user_id):
        return self.tg_bot_controller.get_user_branch(user_id) == self

    def is_in_main_branch(self, user_id):
        return self.tg_bot_controller.get_user_branch(user_id) is None

    def is_in_second_branch(self, user_id):
        return (self.tg_bot_controller.get_user_branch(user_id) is not None) and (self.branch_parent == self.tg_bot_controller)

    def is_in_third_branches(self, user_id):
        return (self.tg_bot_controller.get_user_branch(user_id) is not None) and (self.branch_parent != self.tg_bot_controller)

    def is_not_active_message(self, user_id):
        msg_id_active = self.tg_bot_controller.get_user_message_id(user_id, True)
        msg_id_last = self.tg_bot_controller.get_user_message_id(user_id, False)
        return (not msg_id_active) or (msg_id_active != msg_id_last)

    def do_not_display(self, user_id):
        return False

    def get_branch_path(self):
        self_path = self.branch_code
        if self.branch_parent:
            pre_path = self.branch_parent.get_branch_path()
            self_path = (pre_path + '/' + self_path).strip('/')
        return '/' + self_path

    async def get_branch_for_user_by_code(self, user_id, branch_code):
        if self.branch_code == branch_code:
            return self if self != self.tg_bot_controller else None
        for command in self.commands.values():
            if ('cmd' in command) and command['cmd']:
                command_obj = command['cmd']
                if isinstance(command_obj, BotActionBranch):
                    if command_obj.branch_code == branch_code:
                        can_access = await self.user_can_access_command(user_id, command)
                        if can_access:
                            return command_obj
                        else:
                            return None
        return None

    async def user_can_access_command(self, user_id, command):
        curr_place = self.tg_bot_controller.get_user_place_code(user_id)
        curr_rights = await self.tg_bot_controller.get_user_rights_level_realtime(user_id)
        if (
            command and
            (curr_place in command['places']) and
            (curr_rights >= command['rights_level']) and
            (('condition' not in command) or command['condition'](user_id)) and
            ('cmd' in command) and
            command['cmd']
        ):
            return True
        return False

    def activate_branch_for_user(self, user_id):
        curr_branch = self.tg_bot_controller.get_user_branch(user_id)
        if (not curr_branch) or (curr_branch == self.branch_parent):
            self.tg_bot_controller.set_branch_for_user(user_id, self)

    def deactivate_branch_for_user(self, user_id):
        self.tg_bot_controller.set_branch_for_user(user_id, None)

    async def read_bot_str(self, from_id, callback, message=None, params=None):
        if message:
            await self.send_message_to_user(from_id, message, do_set_next=False)
        self.read_once_callbacks[from_id] = {
            'callback': callback,
            'params': params
        }

    @staticmethod
    def cmd_to_btns(cmd_dict, cols=1):
        buttons = []
        curr_row = []
        for b_cmd, b_text in cmd_dict.items():
            curr_row.append(KeyboardButtonCallback(str(b_text), str(b_cmd).encode()))
            if len(curr_row) >= cols:
                buttons.append(curr_row)
                curr_row = []
        if len(curr_row) > 0:
            buttons.append(curr_row)
        return buttons

    async def read_username_str(self, from_id, callback, message=None, params=None, allow_pick_myself=True):
        if not message:
            message = 'Выберите вариант из списка:'
        username_variants = await self.tg_bot_controller.entity_controller.get_username_variants(from_id, allow_pick_myself)
        buttons = [[KeyboardButtonCallback(str(variant[1]), str(variant[0]).encode())] for variant in username_variants.items()]
        buttons.append([KeyboardButtonCallback('Ввести логин/ID', b"/enter_str")])
        await self.send_message_to_user(from_id, message, buttons=buttons, do_set_next=False)
        self.read_once_callbacks[from_id] = {
            'enter_str_text': 'Введите логин/ID пользователя Telegram:',
            'need_username_add': True,
            'username_add_text': 'Выбран пользователь: **{}**',
            'allow_pick_myself': allow_pick_myself,
            'callback': callback,
            'params': params
        }

    async def get_from_id_param(self, from_id, params):
        if params and (len(params) > 0) and params[0]:
            try:
                entity = await self.tg_bot_controller.tg_client.get_entity(str(params[0]).strip())
            except ValueError:
                try:
                    entity = await self.tg_bot_controller.tg_client.get_entity(PeerUser(int(params[0])))
                except:
                    entity = None
            if type(entity) == User:
                from_id = entity.id
        return from_id

    async def run_user_callback(self, message, from_id):
        if message == '/enter_str':
            await self.send_message_to_user(from_id, self.read_once_callbacks[from_id]['enter_str_text'], do_set_next=False)
            return
        if ('need_username_add' in self.read_once_callbacks[from_id]) and self.read_once_callbacks[from_id]['need_username_add']:
            message_user_id = await self.get_from_id_param(None, [message])
            if message_user_id:
                if ('allow_pick_myself' not in self.read_once_callbacks[from_id]) or not self.read_once_callbacks[from_id]['allow_pick_myself']:
                    if (int(message_user_id) == self.tg_bot_controller.tg_client.me_user_id) and (from_id == self.tg_bot_controller.tg_client.me_user_id):
                        await self.send_message_to_user(from_id, 'Недопустимый выбор! Попробуй что-нибудь другое!', do_set_next=False)
                        return
                    if (int(message_user_id) == self.tg_bot_controller.tg_client.me_user_id) and (from_id != self.tg_bot_controller.tg_client.me_user_id):
                        message_user_id = from_id
                if ('username_add_text' in self.read_once_callbacks[from_id]) and self.read_once_callbacks[from_id]['username_add_text']:
                    user_name = await self.tg_bot_controller.tg_client.get_entity_name(message_user_id, 'User')
                    await self.send_message_to_user(from_id, self.read_once_callbacks[from_id]['username_add_text'].format(user_name), do_set_next=False)
                await self.tg_bot_controller.entity_controller.add_username_variant(from_id, message_user_id)
        n_params = self.read_once_callbacks[from_id]['params']
        if n_params is None:
            n_params = [message]
        elif type(n_params) == list:
            n_params.append(message)
        else:
            n_params = [n_params, message]
        r_callback = self.read_once_callbacks[from_id]['callback']
        self.read_once_callbacks[from_id] = None
        await r_callback(from_id, n_params)

    async def read_or_run_default(self, from_id, callback_cmd, callback_param, read_message, cmd_params=None):
        # async def callback_cmd(from_id, params)
        # def callback_param(user_id)
        u_param = False
        if callback_param:
            u_param = callback_param(from_id)
        if u_param:
            n_params = cmd_params
            if n_params is None:
                n_params = [u_param]
            elif type(n_params) == list:
                n_params.append(u_param)
            else:
                n_params = [n_params, u_param]
            await callback_cmd(from_id, n_params)
        else:
            await self.read_bot_str(from_id, callback_cmd, read_message, cmd_params)

    async def run_main_setup(self, from_id, params):
        if self.tg_bot_controller.is_active_for_user(from_id):
            self.activate_branch_for_user(from_id)
            await self.show_current_branch_commands(from_id)
            pass

    async def show_current_branch_commands(self, from_id, pre_text=None):
        msg_text = "\n".join(await self.get_commands_description_list(from_id, pre_text))
        buttons = await self.get_commands_buttons_list(from_id)
        await self.send_message_to_user(from_id, msg_text, buttons=buttons, set_active=True)

    async def on_bot_message(self, message, from_id):
        if not self.is_in_current_branch(from_id):
            return False
        if (from_id in self.read_once_callbacks) and self.read_once_callbacks[from_id]:
            await self.run_user_callback(message, from_id)
            return True
        message = message.lower()
        if await self.run_command_text(message, from_id):
            return True
        return False

    async def run_command_text(self, command_text, from_id):
        command_parts = command_text.split(' ')
        command_code = None
        if (len(command_parts) > 0) and command_parts[0]:
            command_code = command_parts[0].lower()
        if command_code and (command_parts[0] in self.commands):
            command = self.commands[command_code]
            can_access = await self.user_can_access_command(from_id, command)
            if can_access:
                try:
                    command_obj = command['cmd']
                    if isinstance(command_obj, BotActionBranch):
                        command_f = command_obj.run_main_setup
                    else:
                        command_f = command_obj
                    if ('cmd_params_reader' in command) and command['cmd_params_reader']:
                        await command['cmd_params_reader'](from_id, command_f)
                    else:
                        await command_f(from_id, command_parts[1:])
                except Exception as exception:
                    traceback.print_exc()
                    await self.send_message_to_user(from_id, self.default_error_text.replace('[error_name]', str(exception)).strip())
            else:
                await self.send_message_to_user(from_id, self.default_not_avail_command_text)
            return True
        elif command_code in self.sub_commands_forbidden:
            await self.send_message_to_user(from_id, self.default_not_avail_command_text)
            return True
        return False

    async def remove_message_buttons(self, user_id, message_id):
        if not self.tg_bot_controller.is_active_for_user(user_id):
            return
        str_user_id = str(user_id)
        message_client = self.tg_bot_controller.users[str_user_id]['message_client']
        dialog_entity = self.tg_bot_controller.users[str_user_id]['dialog_entity']
        message = await message_client.get_messages(dialog_entity, ids=message_id)
        if not message:
            return
        if (type(message) != Message) and (type(message) != MessageService):
            if len(message) == 0:
                return
            message = message[0]
        if type(message) != Message:
            return
        try:
            msg_text = message.text
            if message.entities and len(message.entities) > 0 and str(msg_text).find('Фото профиля:') >= 0:
                link_preview = True
            else:
                link_preview = False
            await message_client.edit_message(dialog_entity, message_id, text=msg_text, link_preview=link_preview, buttons=None)
        except MessageNotModifiedError:
            return

    async def send_message_to_user(self, user_id, message_text: str, link_preview=False, buttons=None, set_active=False, do_set_next=True):
        if not self.tg_bot_controller.is_active_for_user(user_id):
             return
        if (len(message_text) + 8) >= 4096:
            status_results = message_text.strip().splitlines()
            status_results.reverse()
            buff_len = 1
            while buff_len > 0:
                buff = []
                buff_len = 0
                while (len(status_results) > 0) and (buff_len + len(status_results[len(status_results) - 1]) + 8) < 4096:
                    last_s = status_results.pop()
                    buff.append(last_s)
                    buff_len = buff_len + len(last_s) + 1
                if buff_len > 0:
                    if len(status_results) > 0:
                        await self.send_one_message_to_user(user_id, "\n".join(buff), link_preview, None, False, False)
                        await asyncio.sleep(0.75)
                    else:
                        await self.send_one_message_to_user(user_id, "\n".join(buff), link_preview, buttons, set_active, do_set_next)
        else:
            await self.send_one_message_to_user(user_id, message_text, link_preview, buttons, set_active, do_set_next)

    async def send_one_message_to_user(self, user_id, message_text: str, link_preview=False, buttons=None, set_active=False, do_set_next=True):
        if not self.tg_bot_controller.is_active_for_user(user_id):
            return
        message = None
        try:
            str_user_id = str(user_id)
            message_client = self.tg_bot_controller.users[str_user_id]['message_client']
            dialog_entity = self.tg_bot_controller.users[str_user_id]['dialog_entity']
            message_to_send = self.tg_bot_controller.text_to_bot_text(message_text, user_id)
            last_active = None
            last_next = self.tg_bot_controller.get_user_next_message_id(user_id)
            set_next = False
            if set_active:
                last_active = self.tg_bot_controller.get_user_message_id(user_id, True)
                last_msg_id = self.tg_bot_controller.get_user_message_id(user_id, False)
                if last_active and (last_active != last_msg_id):
                    await message_client.delete_messages(dialog_entity, [last_active])
                    last_active = None
            if last_next:
                await self.remove_message_buttons(user_id, last_next)
                self.tg_bot_controller.set_message_for_user(user_id, None, False, True)
            if not last_active:
                if (not buttons) and (not set_active) and do_set_next:
                    buttons = [KeyboardButtonCallback('Продолжить', '/help'.encode())]
                    set_next = True
                elif buttons and (not set_active):
                    set_next = True
                message = await message_client.send_message(dialog_entity, message_to_send, link_preview=link_preview, buttons=buttons)
                if message and (type(message) == Message):
                    self.tg_bot_controller.set_message_for_user(user_id, message.id, set_active, set_next)
            else:
                is_except = False
                try:
                    message = await message_client.edit_message(dialog_entity, last_active, message_to_send, link_preview=link_preview, buttons=buttons)
                except:
                    is_except = True
                    message = None
                if is_except:
                    message = await message_client.get_messages(dialog_entity, ids=last_active)
                if message and (type(message) == Message):
                    return True
                else:
                    self.tg_bot_controller.set_message_for_user(user_id, None, True)
                    return await self.send_one_message_to_user(user_id, message_text, link_preview, buttons, set_active, do_set_next)
        except:
            traceback.print_exc()
        return True if message else False

    async def send_file_to_user(self, user_id, file_name, caption, force_document=False):
        if not self.tg_bot_controller.is_active_for_user(user_id):
            return
        str_user_id = str(user_id)
        message_client = self.tg_bot_controller.users[str_user_id]['message_client']
        dialog_entity = self.tg_bot_controller.users[str_user_id]['dialog_entity']
        caption = self.tg_bot_controller.text_to_bot_text(caption, user_id)
        message = await message_client.send_file(dialog_entity, file_name, caption=caption, force_document=force_document)
        if message and (type(message) == Message):
            self.tg_bot_controller.set_message_for_user(user_id, message.id, False, False)
        return True if message else False

    async def send_typing_to_user(self, user_id, typing_begin=True):
        str_user_id = str(user_id)
        message_client = self.tg_bot_controller.users[str_user_id]['message_client']
        dialog_entity = self.tg_bot_controller.users[str_user_id]['dialog_entity']
        self.last_typing_sent_date = datetime.now()
        return await message_client(functions.messages.SetTypingRequest(
            peer=dialog_entity,
            action=SendMessageTypingAction() if typing_begin else SendMessageCancelAction()
        ))

    async def resend_typing_to_user(self, user_id):
        if self.last_typing_sent_date and ((datetime.now() - self.last_typing_sent_date).total_seconds() > 5):
            await self.send_typing_to_user(user_id)

    async def return_to_main_branch(self, from_id):
        self.deactivate_branch_for_user(from_id)
        await self.tg_bot_controller.cmd_help(from_id, [])

    async def return_to_back_branch(self, from_id):
        if self == self.tg_bot_controller:
            self.deactivate_branch_for_user(from_id)
        else:
            if self.branch_parent != self.tg_bot_controller:
                self.tg_bot_controller.set_branch_for_user(from_id, self.branch_parent)
                await self.branch_parent.cmd_help(from_id, [])
            else:
                self.deactivate_branch_for_user(from_id)

    async def cmd_help(self, from_id, params):
        text = []
        if params == 'Start':
            text.append('Привет, я - чат-бот (и не только)')
        text.append('Список доступных для тебя моих команд')
        await self.show_current_branch_commands(from_id, "\n".join(text))

    async def cmd_to_top(self, from_id, params):
        await self.return_to_main_branch(from_id)

    async def cmd_back(self, from_id, params):
        await self.return_to_back_branch(from_id)

    async def cmd_enter_str(self, from_id, params):
        await self.return_to_back_branch(from_id)


'''
This file moves you to a specified location in x/y/z coordinates.
Note that the Y-coordinate is up and down.

python teleport.py -x X -y Y -z Z
'''

import argparse
from mcpi.minecraft import Minecraft

parser = argparse.ArgumentParser ()
parser.add_argument ( "-x", action='store', dest='x', type=int, default=0,
    help="The east-west coordinate that you want to go to. Defaults to 0." )
parser.add_argument ( "-y", action='store', dest='y', type=int, default=120,
    help="The vertical height that you want to go to. Defaults to 120 (high)." )
parser.add_argument ( "-z", action='store', dest='z', type=int, default=0,
    help="The north-south coordinate that you want to go to. Defaults to 0." )

args = parser.parse_args ()

mc = Minecraft.create ()
mc.player.setTilePos ( args.x, args.y, args.z )


from mpi4py import MPI
from pycompss.util.typing_helper import typing

comm = MPI.COMM_WORLD
size = comm.size
rank = comm.rank

def rank_distributor(
    collection_layout: typing.Tuple[int, int, int]
) -> typing.List[int]:
    """Distribute mpi ranks to data given a collection layout.

    :param collection_layout: Layout of the collection.
    :return distribution: distribution of rank x
    """
    block_count, block_length, stride = collection_layout
    if block_count == -1:
        block_count = size
    if stride == -1:
        stride = 1
    if block_length == -1:
        block_length = 1
    distribution = []
    if block_count == size:
        # Number of block bigger than processes
        # (one block per process)
        offset = rank * stride
        distribution = list(range(offset, offset + block_length))
    else:
        # If number of blocks is bigger than processes
        # (blocks round-robin distributed)
        block = rank
        while block < block_count:
            offset = block * stride
            offset_bl = offset + block_length
            distribution = distribution + list(range(offset, offset_bl))
            block = block + size
    return distribution


"""Microsoft translator implementation module."""
import requests
import uuid

from flask import current_app
from flask_babel import _

from app.translators import BaseTranslator

class MicrosoftTranslator(BaseTranslator):
    """Microsoft translator class."""
    @staticmethod
    def translate(text: str, source_language: str, dest_language: str) -> str:
        """Translate text using MS Translator API"""
        if (
                'MS_TRANSLATOR_KEY' not in current_app.config or
                not current_app.config['MS_TRANSLATOR_KEY']
        ):
            return _('Error: the translator service is not properly configured.')
        auth = {
            'Ocp-Apim-Subscription-Key': current_app.config['MS_TRANSLATOR_KEY'],
            'Ocp-Apim-Subscription-Region': current_app.config['MS_TRANSLATOR_LOCATION'],
            'Content-type': 'application/json',
            'X-ClientTraceId': str(uuid.uuid4())
        }
        params = {
            'api-version': '3.0',
            'from': source_language,
            'to': [dest_language, ]
        }
        body = [{
            'text': text
        }]
        response = requests.post(
            'https://api.cognitive.microsofttranslator.com/translate',
            headers=auth,
            params=params,
            json=body,
        )
        if response.status_code != 200:
            return _('Error: the translation service failed.')
        response_json = response.json()
        return response_json[0]['translations'][0]['text']


import matplotlib.pyplot as plt
import numpy as np

def main():
    mapname = 'f1_aut_wide_raceline'
    iteration_no = 1
    TESTMODE = "Benchmark"
    Max_iter = 5
    raceline = np.loadtxt('src/my_waypoint_follower/my_waypoint_follower/' + mapname+'.csv',delimiter=',')
    # raceline_float = raceline_str.astype(np.float)
    while iteration_no <= Max_iter:
        data = np.loadtxt('src/my_waypoint_follower/my_waypoint_follower/csv/'+mapname+'/'+TESTMODE+"/lap"+str(iteration_no)+'.csv',delimiter=',',skiprows=2)
        # print(data)
        plt.figure()
        plt.plot(data[:,0],data[:,1],label = "Actual paths",marker='x',markersize = 1)
        plt.plot(raceline[:,1],raceline[:,2],label = "Desired paths")
        plt.legend()
        plt.savefig(f"src/my_waypoint_follower/my_waypoint_follower/csv/{mapname}/{TESTMODE}/graph/Trajectory_lap_{str(iteration_no)}.svg")
        plt.figure()
        plt.plot(data[:,5],data[:,3],label = "Speed Profile")
        plt.plot(data[:,5],data[:,4],label = "Acttual speed")
        plt.legend()
        plt.savefig(f"src/my_waypoint_follower/my_waypoint_follower/csv/{mapname}/{TESTMODE}/graph/Speed_lap_{str(iteration_no)}.svg")
        iteration_no += 1

    for i in range(0,Max_iter):
        pass

if __name__ == "__main__":
    main()


import os
import logging
import array

from franklin.backbone.analysis import Analyzer, scrape_info_from_fname
from franklin.pipelines.pipelines import seq_pipeline_runner
from franklin.backbone.specifications import (BACKBONE_DIRECTORIES,
                                              BACKBONE_BASENAMES,
                                              PLOT_FILE_FORMAT)
from franklin.utils.seqio_utils import seqs_in_file
from franklin.seq.writers import SequenceWriter
from franklin.statistics import (write_distribution, draw_histogram,
                                 draw_boxplot, IntsStats)
from franklin.seq.seq_stats import create_nucleotide_freq_histogram

class CleanReadsAnalyzer(Analyzer):
    'It does a cleaning reads analysis'

    @staticmethod
    def _guess_cleaning_pipepile(file_info):
        'It returns the pipeline suited to clean the given file'
        if 'pl' not in file_info:
            raise RuntimeError('platform not in the file name: %s' %
                               file_info['fpath'])
        platform = file_info['pl']
        if file_info['format'] == 'fasta':
            return 'sanger_without_qual'
        elif platform == 'illumina':
            return 'solexa'
        elif platform == '454':
            return 'sanger_with_qual'
        elif platform == 'sanger':
            return 'sanger_with_qual'
        elif platform == 'solid':
            return 'solid'
        else:
            raise ValueError('Unable to guess the cleaning pipeline: %s' %
                             str(file_info))

    def _create_cleaning_configuration_solid(self, platform):
        '''It returns the pipeline configuration for solid pipeline looking at
        the project settings'''
        settings = self._project_settings['Cleaning']
        configuration = {}
        # min length settings
        min_length = settings['min_seq_length'][platform]
        configuration['remove_short'] = {}
        configuration['remove_short']['length'] = min_length
        return configuration

    def _create_cleaning_configuration_no_solid(self, platform, library=None):
        '''It returns the pipeline configuration for no solid pipeline looking
        at the project settings'''
        settings = self._project_settings['Cleaning']
        configuration = {}

        configuration['remove_vectors_blastdb'] = {}
        vec_db = settings['vector_database']
        configuration['remove_vectors_blastdb']['vectors'] = vec_db

        configuration['remove_vectors_file'] = {}
        vec_file = settings['vector_file']
        configuration['remove_vectors_file']['vectors'] = vec_file

        # adaptors settings
        adap_param = 'adaptors_file_%s' % platform
        adaptors_fpath = settings[adap_param]
        configuration['remove_adaptors'] = {}
        configuration['remove_adaptors']['adaptors'] = adaptors_fpath

        # Words settings
        word_param = 'short_adaptors_%s' % platform
        words = settings[word_param]
        configuration['remove_short_adaptors'] = {}
        configuration['remove_short_adaptors']['words'] = words

        # lucy settings.
        lucy_settings = settings['lucy']
        lucy_vector_settings = lucy_settings['vector_settings']
        if lucy_vector_settings is not None:
            lucy_settings_dir = os.path.dirname(lucy_vector_settings)
            lucy_settings_fhand = open(lucy_vector_settings)
            lucy_libraries = eval(lucy_settings_fhand.read())
            lucy_settings_fhand.close()
            if library in lucy_libraries:
                vector = os.path.join(lucy_settings_dir,
                                      lucy_libraries[library]['vector_file'])
                splice = os.path.join(lucy_settings_dir,
                                      lucy_libraries[library]['splice_file'])
                vector_settings = [vector, splice]
            else:
                vector_settings = None
        else:
            vector_settings = None

        parameters = {'bracket': lucy_settings['bracket'],
                      'window': lucy_settings['window'],
                      'error': lucy_settings['error']}
        if vector_settings:
            parameters['vector'] = vector_settings

        configuration['strip_lucy'] = {}
        configuration['strip_lucy']['parameters'] = parameters

        n_trim = settings['strip_n_percent']
        configuration['strip_trimpoly'] = {}
        configuration['strip_trimpoly']['ntrim_above_percent'] = n_trim

        #edge_remover
        left = settings['edge_removal']['%s_left' % platform]
        right = settings['edge_removal']['%s_right' % platform]
        configuration['edge_removal'] = {}
        configuration['edge_removal']['left_length'] = left
        configuration['edge_removal']['right_length'] = right

        # min length settings
        min_length = settings['min_seq_length'][platform]
        configuration['remove_short'] = {}
        configuration['remove_short']['length'] = min_length

        return configuration

    def create_cleaning_configuration(self, platform, library=None):
        'It returns the pipeline configuration looking at the project settings'
        if platform == 'solid':
            return self._create_cleaning_configuration_solid(platform)
        else:
            return self._create_cleaning_configuration_no_solid(platform,
                                                                library=library)

    def run(self):
        '''It runs the analysis. It checks if the analysis is already done per
        input file'''
        logger = logging.getLogger("franklin")
        self._log({'analysis_started': True})
        input_paths = self._get_input_fpaths()['reads']
        output_dir = self._create_output_dirs()['reads']
        for input_path in input_paths:
            input_fpath = str(input_path)
            fname = os.path.split(input_fpath)[-1]
            output_fpath = os.path.join(output_dir, fname)
            if os.path.exists(output_fpath):
                msg = '%s already cleaned. Not cleaned again' % output_fpath
                logger.info(msg)
                continue
            file_info = scrape_info_from_fname(input_path)
            input_fhand = open(input_fpath)
            output_fhand = open(output_fpath, 'w')
            pipeline = self._guess_cleaning_pipepile(file_info)
            infhands = {'in_seq': input_fhand}
            writer = SequenceWriter(output_fhand,
                                    file_format=file_info['format'])

            configuration = self.create_cleaning_configuration(
                                                      platform=file_info['pl'],
                                                      library=file_info['lb'])
            try:
                seq_pipeline_runner(pipeline, configuration, infhands,
                                    file_info['format'],
                                    processes=self.threads,
                                    writers={'seq': writer})
            except Exception as error:
                output_fhand.close()
                os.remove(output_fpath)
                raise(error)
            output_fhand.close()
            input_fhand.close()

        self._log({'analysis_finished': True})
        return

BASENAME_FOR_ALL_TOGETHER = 'all'

PLOT_LABELS = {
        'seq_length': {'title': 'Sequence length distribution',
                       'xlabel': 'Sequence length',
                       'ylabel': 'Number of sequences'},
        'seq_qual': {'title': 'Sequence quality distribution',
                     'xlabel': 'quality',
                     'ylabel': 'Number of bp'},
        'qual_boxplot': {'title': 'Qualities along the sequence',
                         'xlabel': 'seqence position',
                         'ylabel': 'quality distribution'},
        }

class ReadsStatsAnalyzer(Analyzer):
    '''It calculates stats for original and cleaned reads.
    It calculates the distribution between both type off reads'''

    def run(self):
        'It runs the analysis'
        self._log({'analysis_started': True})
        self._create_output_dirs()

        clean_paths = self._get_input_fpaths()['cleaned_reads']
        raw_paths = self._get_input_fpaths()['raw_reads']

        clean_stats_dir = os.path.join(self._get_project_path(),
                             BACKBONE_DIRECTORIES['cleaned_reads_stats'])
        raw_stats_dir = os.path.join(self._get_project_path(),
                             BACKBONE_DIRECTORIES['raw_reads_stats'])

        for path in raw_paths:
            self._do_stats_for_file(path, raw_stats_dir)

        for path in clean_paths:
            self._do_stats_for_file(path, clean_stats_dir)

        self._log({'analysis_finished': True})

    def _do_stats_for_file(self, path, stats_dir):
        'It calculates the stats for a file os seqs'
        stats_fpath = os.path.join(stats_dir,
                                       BACKBONE_BASENAMES['statistics_file'])
        stats_fhand = open(stats_fpath, 'a')

        fpath = path.last_version
        basename = path.basename
        #output_files
        freq_pos_out_fpath = os.path.join(stats_dir,
                                          basename + '.freq_position')
        length_out_fpath = os.path.join(stats_dir, basename + '.length')
        qual_out_fpath = os.path.join(stats_dir, basename + '.qual')
        qual_boxplot_out_fpath = os.path.join(stats_dir,
                                              basename + '.qual.boxplot')

        # nucleotide freq per position
        if not os.path.exists(freq_pos_out_fpath):
            plot_fpath = freq_pos_out_fpath + '.' + PLOT_FILE_FORMAT
            seqs = seqs_in_file(open(fpath))
            create_nucleotide_freq_histogram(seqs, fhand=open(plot_fpath, 'w'))

        #Extract lengths and quals
        if (not os.path.exists(length_out_fpath) or
            not  os.path.exists(qual_out_fpath)):
            lengths_, quals_ = self._get_lengths_quals_from_file(fpath)

        #the distributions for the lengths
        if not os.path.exists(length_out_fpath):
            plot_fpath = length_out_fpath + '.' + PLOT_FILE_FORMAT
            distrib_fpath = length_out_fpath + '.dat'
            distrib = lengths_.calculate_distribution()
            lengths_.draw_distribution(distrib,
                                 labels=PLOT_LABELS['seq_length'],
                                 distrib_fhand=open(distrib_fpath, 'w'),
                                 plot_fhand=open(plot_fpath, 'w'))

        #the distributions for the quals
        if not  os.path.exists(qual_out_fpath) or quals_.count != 0:
            plot_fpath = qual_out_fpath + '.' + PLOT_FILE_FORMAT
            distrib_fpath = qual_out_fpath + '.dat'

            distrib = quals_.calculate_distribution()
            quals_.draw_distribution(distrib,
                                     labels=PLOT_LABELS['seq_qual'],
                                     plot_fhand=open(plot_fpath, 'w'),
                                 distrib_fhand=open(distrib_fpath, 'w'))

        # qual boxplot
        if not os.path.exists(qual_boxplot_out_fpath):
            quals = self._get_quals_by_length_from_file(fpath)
            if (quals and quals[0]):
                plot_fpath = qual_boxplot_out_fpath + '.' + PLOT_FILE_FORMAT
                distrib_fpath = qual_boxplot_out_fpath + '.dat'
                #boxplot
                draw_boxplot(quals, fhand=open(plot_fpath, 'w'),
                             title=PLOT_LABELS['qual_boxplot']['title'],
                             xlabel=PLOT_LABELS['qual_boxplot']['xlabel'],
                             ylabel=PLOT_LABELS['qual_boxplot']['ylabel'],
                             stats_fhand=open(distrib_fpath, 'w'),
                             max_plotted_boxes=30)

        #the statistics for the statistics file
        self._write_statistics(stats_fhand, fpath, lengths_, quals_)

    @staticmethod
    def _get_quals_by_length_from_file(fpath):
        'It returns the qualities along the sequence as a list of lists'

        quals_by_position = []
        for seq in seqs_in_file(open(fpath)):
            quals = seq.qual
            if quals:
                for base_number, qual in enumerate(quals):
                    try:
                        quals_by_position[base_number]
                    except IndexError:
                        quals_by_position.append(array.array('B'))
                    quals_by_position[base_number].append(qual)
        return quals_by_position

    @staticmethod
    def _write_statistics(stats_fhand, seq_fpath, lengths, quals):
        'It writes some statistics'

        to_print = 'statistics for %s\n' % os.path.basename(seq_fpath)
        stats_fhand.write(to_print)
        if lengths.count == 0:
            stats_fhand.write('File empty, no stats for this file\n')
            return
        stats_fhand.write('-' * (len(to_print) - 1) + '\n')

        stats_fhand.write('Num sequences: %i\n' % lengths.count)

        stats_fhand.write('Total sequence length: %i\n' % lengths.sum)

        stats_fhand.write('Sequence length minimum: %i\n' % lengths.min)

        stats_fhand.write('Sequence length maximum: %i\n' % lengths.max)

        stats_fhand.write('Sequence length average: %.2f\n' % lengths.average)

        stats_fhand.write('Sequence length variance: %.2f\n' % lengths.variance)
        if quals and quals.count != 0:
            stats_fhand.write('Sequence qualities minimum: %i\n' % quals.min)

            stats_fhand.write('Sequence qualities maximum: %i\n' % quals.max)

            stats_fhand.write('Sequence qualities average: %.2f\n' % \
                                                                  quals.average)
            stats_fhand.write('Sequence qualities variance: %.2f\n' % \
                                                                 quals.variance)
        stats_fhand.write('\n')

    @staticmethod
    def _get_lengths_quals_from_file(seq_fpath):
        'Given a sequence file it returns the lengths and quals'
        lengths = IntsStats(init_len=1000)
        quals = IntsStats(init_len=100)
        for seq in seqs_in_file(open(seq_fpath)):
            lengths.append(len(seq))
            qual = seq.qual
            if qual:
                quals.extend(qual)
        return lengths, quals

DEFINITIONS = {
    'clean_reads':
        {'inputs': {
            'reads':
                {'directory': 'raw_reads',
                 'file_kinds': 'sequence_files'}
            },
         'outputs': {'reads': {'directory': 'cleaned_reads'}},
         'analyzer': CleanReadsAnalyzer,
        },
    'read_stats':
        {'inputs': {
            'raw_reads':
                {'directory': 'raw_reads',
                 'file_kinds': 'sequence_files'},
            'cleaned_reads':
                {'directory': 'cleaned_reads',
                 'file_kinds': 'sequence_files'}
                },
         'outputs': {'raw_reads': {'directory': 'raw_reads_stats'},
                    'cleaned_reads': {'directory': 'cleaned_reads_stats'}},
         'analyzer': ReadsStatsAnalyzer,
        },
     }


import sys
from collections import deque

read = sys.stdin.readline

def topology(graph, indegree, n):
    res = []
    q = deque()

    for i in range(1, n + 1):
        if indegree[i] == 0:
            q.append(i)

    while q:
        x = q.popleft()
        res.append(x)

        for i in graph[x]:
            indegree[i] -= 1
            if indegree[i] == 0:
                q.append(i)
    return res

def sol():
    n, m = map(int, read().split())
    indegree = [0] * (n + 1)
    graph = [[] for _ in range(n + 1)]

    for _ in range(m):
        u, v = map(int, read().split())
        graph[u].append(v)
        indegree[v] += 1
    print(*topology(graph, indegree, n))
sol()


'''
--- Day 9: Marble Mania ---
You talk to the Elves while you wait for your navigation system to initialize. To pass the time, they introduce you to their favorite marble game.

The Elves play this game by taking turns arranging the marbles in a circle according to very particular rules. The marbles are numbered starting with 0 and increasing by 1 until every marble has a number.

First, the marble numbered 0 is placed in the circle. At this point, while it contains only a single marble, it is still a circle: the marble is both clockwise from itself and counter-clockwise from itself. This marble is designated the current marble.

Then, each Elf takes a turn placing the lowest-numbered remaining marble into the circle between the marbles that are 1 and 2 marbles clockwise of the current marble. (When the circle is large enough, this means that there is one marble between the marble that was just placed and the current marble.) The marble that was just placed then becomes the current marble.

However, if the marble that is about to be placed has a number which is a multiple of 23, something entirely different happens. First, the current player keeps the marble they would have placed, adding it to their score. In addition, the marble 7 marbles counter-clockwise from the current marble is removed from the circle and also added to the current player's score. The marble located immediately clockwise of the marble that was removed becomes the new current marble.

For example, suppose there are 9 players. After the marble with value 0 is placed in the middle, each player (shown in square brackets) takes a turn. The result of each of those turns would produce circles of marbles like this, where clockwise is to the right and the resulting current marble is in parentheses:

[-] (0)
[1]  0 (1)
[2]  0 (2) 1
[3]  0  2  1 (3)
[4]  0 (4) 2  1  3
[5]  0  4  2 (5) 1  3
[6]  0  4  2  5  1 (6) 3
[7]  0  4  2  5  1  6  3 (7)
[8]  0 (8) 4  2  5  1  6  3  7
[9]  0  8  4 (9) 2  5  1  6  3  7
[1]  0  8  4  9  2(10) 5  1  6  3  7
[2]  0  8  4  9  2 10  5(11) 1  6  3  7
[3]  0  8  4  9  2 10  5 11  1(12) 6  3  7
[4]  0  8  4  9  2 10  5 11  1 12  6(13) 3  7
[5]  0  8  4  9  2 10  5 11  1 12  6 13  3(14) 7
[6]  0  8  4  9  2 10  5 11  1 12  6 13  3 14  7(15)
[7]  0(16) 8  4  9  2 10  5 11  1 12  6 13  3 14  7 15
[8]  0 16  8(17) 4  9  2 10  5 11  1 12  6 13  3 14  7 15
[9]  0 16  8 17  4(18) 9  2 10  5 11  1 12  6 13  3 14  7 15
[1]  0 16  8 17  4 18  9(19) 2 10  5 11  1 12  6 13  3 14  7 15
[2]  0 16  8 17  4 18  9 19  2(20)10  5 11  1 12  6 13  3 14  7 15
[3]  0 16  8 17  4 18  9 19  2 20 10(21) 5 11  1 12  6 13  3 14  7 15
[4]  0 16  8 17  4 18  9 19  2 20 10 21  5(22)11  1 12  6 13  3 14  7 15
[5]  0 16  8 17  4 18(19) 2 20 10 21  5 22 11  1 12  6 13  3 14  7 15
[6]  0 16  8 17  4 18 19  2(24)20 10 21  5 22 11  1 12  6 13  3 14  7 15
[7]  0 16  8 17  4 18 19  2 24 20(25)10 21  5 22 11  1 12  6 13  3 14  7 15
The goal is to be the player with the highest score after the last marble is used up. Assuming the example above ends after the marble numbered 25, the winning score is 23+9=32 (because player 5 kept marble 23 and removed marble 9, while no other player got any points in this very short example game).

Here are a few more examples:

10 players; last marble is worth 1618 points: high score is 8317
13 players; last marble is worth 7999 points: high score is 146373
17 players; last marble is worth 1104 points: high score is 2764
21 players; last marble is worth 6111 points: high score is 54718
30 players; last marble is worth 5807 points: high score is 37305
'''

import re
import itertools as it
from collections import deque

TEST = '''\
10 players; last marble is worth 1618 points: high score is 8317
13 players; last marble is worth 7999 points: high score is 146373
17 players; last marble is worth 1104 points: high score is 2764
21 players; last marble is worth 6111 points: high score is 54718
30 players; last marble is worth 5807 points: high score is 37305\
'''.split('\n')
INPUT = '452 players; last marble is worth 71250 points'

p = re.compile(r'(\d+) players; last marble is worth (\d+) points.*')
input_ = INPUT

def step(circle, current_position=0, next_marble=0):
    if next_marble % 23 == 0:
        score = next_marble
        next_position = (current_position - 7) % len(circle)
        return circle[:next_position] + circle[next_position + 1:], next_position, score + circle[next_position]
    next_position = (current_position + 2) % len(circle)
    if next_position == 0:
        next_position = len(circle)
    return circle[:next_position] + [next_marble] + circle[next_position:], next_position, 0

def part1(input_):
    n_players, n_marbles = *map(int, p.match(input_).groups()),
    players_score = [0 for player in range(n_players)]
    circle = [0] ; current_position = 0
    for player, marble in zip(it.cycle(range(n_players)),range(1, n_marbles+1)):
        circle, current_position, score = step(circle,current_position,marble)
        players_score[player] += score
    print('Part1: ', max(players_score))

def game(n_marbles):
    circle = deque([0])
    for i in range(23,n_marbles+1,23):
        for j in range(i-22,i):
            circle.rotate(-1)
            circle.append(j)
        circle.rotate(7)
        yield i, i + circle.pop()
        circle.rotate(-1)

def part2(input_):
    n_players, n_marbles = *map(int, p.match(input_).groups()),
    n_marbles = 100 * n_marbles
    players_score = [0 for player in range(n_players)]
    for marble, score in game(n_marbles):
        player = marble % n_players
        players_score[player] += score
    print('Part2: ', max(players_score))

#
# for input_ in TEST:
#     part1(input_)
# part1(INPUT)
part2(INPUT)