print("Sistema para calcular promedio de un alumno")

nombre = input("Para comenzar, cual es tu nombre?: ")

matematicas = int(input(nombre + "Cual es tu calificacion en matematicas?: "))
quimica = int(input(nombre + "Cual es tu calificacion en quimica?: "))
biologia = int(input(nombre + "Cual es tu calificacion en Biologia?: "))

promedio = (matematicas + quimica + biologia) /3
promedio = int(promedio)

if promedio >= 6:
    print('Felicidades ' + nombre + ' "aprobaste" con un promedio de: ', promedio)

print("Fin")


import numpy as np
import sofar as sf
import pyfar as pf

def compute_dtfs(
        sofa, smooth_fractions=None, phase="minimum", weights="equal"):
    r"""
    Compute directional transfer functions from HRIRs.

    The directional transfer functions (DTFs) are computed by a spectral
    division of the HRTFs by the diffuse field transfer function (DFTF)

    .. math::

        \mathrm{DTF} = \frac{\mathrm{HRTF}}{\mathrm{DFTF}}

    where the DFTF is computed as the energetic average across source positions
    and ears

    .. math::

        \mathrm{DFTF} = \sqrt{\sum_{n=0}^{N-1} |\mathrm{HRTF}|^2_{l,n}\,w_n +
        \sum_{n=0}^{N-1} |\mathrm{HRTF}|^2_{r,n}\,w_n}

    The index :math:`n` denotes the source position, :math:`|\cdot|` the
    absolute spectrum, and :math:`\sum_n w_n=1` normalized area weights for
    numerical integration (see below). The average across ears is made to not
    alter binaural cues.

    Parameters
    ----------
    sofa : sofar Sofa object, str
       Sofa object containing the sound pressure or filename of a SOFA file to
       be loaded. SOFA object/file must be of the convention
       SimpleFreeFieldHRIR or GeneralFIR
    smooth_fractions : number, None, optional
        Apply `fractional octave smoothing
        <https://pyfar.readthedocs.io/en/latest/modules/pyfar.dsp.html\
        ?highlight=smooth#pyfar.dsp.smooth_fractional_octave>`_ to the DFTF.
        E.g. a value of ``3`` applies third octave smoothing and a value of
        ``1`` applies octave smoothing. The default ``None`` does not apply any
        smoothing.
    phase : string, optional
        Define the phase of the inverse DFTF.

        ``'minimum'``
            generate a `minimum phase response
            <https://pyfar.readthedocs.io/en/latest/modules/pyfar.dsp.html?\
            highlight=minimum%20phase#pyfar.dsp.minimum_phase>`_
        ``'linear'``
            generate a `linear phase response
            <https://pyfar.readthedocs.io/en/latest/modules/pyfar.dsp.html?\
            highlight=linear%20phase#pyfar.dsp.linear_phase>`_
        ``'zero'``
            generates a zero phase response.

        The default is ``'minimum'``.
    weights : optional
        Define the weights used for the numerical integration

        ``'equal'``
            Uses equal weights across source positions
        ``'voronoi'``
            Uses `spherical Voronoi weights
            <https://pyfar.readthedocs.io/en/latest/modules/pyfar.samplings.html\
            ?highlight=voronoi#pyfar.samplings.calculate_sph_voronoi_weights>`_
        array like
            Uses the weights provided in a list or numpy array. The size of the
            array like must agree with the number of HRTFs

        The default is ``'equal'``

    Returns
    -------
    sofa : sofar Sofa.object
        The DTFs as Sofa object. Can be written to disk with `sofar.write_sofa
        <https://sofar.readthedocs.io/en/latest/sofar.html#sofar.sofar.write_sofa\
        sofar.sofar.write_sofa>`_.
    DTFT_inverse : pyfar Signal object
        The inverse diffuse field transfer function as a `pyfar signal object
        <https://pyfar.readthedocs.io/en/latest/classes/pyfar.audio.html#\
        pyfar.classes.audio.Signal>`_
    """

    if isinstance(sofa, str):
        sofa = sf.read_sofa(sofa)
    else:
        sofa = sofa.copy()

    if sofa.GLOBAL_SOFAConventions not in [
            "SimpleFreeFieldHRIR", "GeneralFIR"]:
        raise ValueError(("Sofa object must have the conventions "
                          "SimpleFreeFieldHRIR or GeneralFIR"))

    # get pyfar objects (use pf.io.convert_sofa once released)
    hrir, coordinates, _ = pf.io.convert_sofa(sofa)

    # get or check the weights
    if weights == "equal":
        weights = None
    elif weights == "voronoi":
        weights = pf.samplings.calculate_sph_voronoi_weights(coordinates)
        weights = weights[..., None]
    elif isinstance(weights, (list, np.ndarray)):
        weights = np.asarray(weights).flatten()
        if weights.size != hrir.cshape[0]:
            raise ValueError((
                f"{weights.size} provided but {hrir.cshape[0]} expected "
                "(number of HRIRs)"))
        weights = weights[..., None]
    else:
        raise ValueError("weights must be 'equal', 'voronoi' or an array like")

    # compute DFTF
    dftf = pf.dsp.average(
        hrir, "power", caxis=(0, 1), weights=weights)

    if smooth_fractions is not None:
        dftf, _ = pf.dsp.smooth_fractional_octave(dftf, smooth_fractions)

    # get inverse DFTF
    if phase == "minimum":
        dftf_inv = pf.dsp.minimum_phase(1 / dftf, truncate=False)
    elif phase == "linear":
        dftf_inv = pf.dsp.linear_phase(1 / dftf, dftf.n_samples / 2)
    elif phase == "zero":
        dftf_inv = 1 / dftf
    else:
        raise ValueError(
            f"phase is '{phase}' but must be 'minimum' or 'linear'")

    # compute DTF
    dtf = hrir * dftf_inv
    sofa.Data_IR = dtf.time

    return sofa, dftf_inv


# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import migrations, models
import wagtail.fields
import wagtail.blocks

class Migration(migrations.Migration):

    dependencies = [
        ('home', '0002_create_homepage'),
    ]

    operations = [
        migrations.AddField(
            model_name='homepage',
            name='body',
            field=wagtail.fields.StreamField((('events_list', wagtail.blocks.StructBlock((('heading', wagtail.blocks.CharBlock(required=False)), ('count', wagtail.blocks.IntegerBlock(help_text='How many events should be shown?', min_value=2))), icon='date')), ('text', wagtail.blocks.RichTextBlock())), default=''),
            preserve_default=False,
        ),
    ]


#--

#--
import pandas as pd
import numpy as np

# ---- # ---- # ----
# 11 Time Series

# La series de tiempo pueden ser de "frecuencia fija" o "frecuencia irregular"
# las primera indican que  los intervalos de tiempos entre puntos es constante, la
# segunda que los intervalos de tiempos entre puntos no es constante.

# Algunos conceptos:
# Timestamps = instante especifico del tiempo
# Fixed periods = como todo el mes de enero 2017, o todo el año 2022
# Intervals of time = indicado por una marca de tiempo de inicio y finalización.

# ---- # ---- # ----
# 11.1 Date and Time Data Types and Tools
# Python trae consigo librerias para poder trabajar con fechas: datetime, time y calendar

from datetime import datetime
now = datetime.now(); now
type(now)

#Podemos acceder al año, mes o día de un datetime.datetime object
now.year
now.month
now.day

#Podemos operar entre fechas. El resultado es un objeto datetime.timedelta.
delta = datetime(2011,1,7) - datetime(2008, 6, 24, 8, 15); delta

delta.days                      #Accedemos a los resulados de la operacion
delta.seconds

#Podemos utilizar un objeto datetime.timedelta para operar sobre un objeto datetime.datetime
from datetime import timedelta
start = datetime(2011, 1, 7)
start + timedelta(12)           #Se obsera que timedelta(12) se referencia por defecto a dias

# ---- # ---- # ----
# 11.1.1 Converting Between String and Datetime
# Podemos convertir un datetime object o un timestamps (objecto fecha de pandas) a un string por medio
# del metodo de datetime.strftime()  o la funcion str()

stamp = datetime(2011, 1, 3)

stamp.strftime("%Y-%m-%d")      #por medio del metodo .strftime()
str(stamp)                      #por medio de la funcion str()

# De la forma contraria, podemos pasar de string a dates usando la funcino de datatime.strptime()
value = "2011-01-03"
datetime.strptime(value, "%Y-%m-%d")

datestrs = ["7/6/2011", "8/6/2011"]
[datetime.strptime(x, "%m/%d/%Y") for x in datestrs]

# Pandas esta orientado a trabajar con arrays de fechas, por lo que el metodo pandas.to_datetime()
# es conveniente para pasar arrays de string a fechas.
datestrs = ["2011-07-06 12:00:00", "2011-08-06 00:00:00"]
dates =  pd.to_datetime(datestrs); dates

[type(datetime.strftime(i, "%Y-%m-%d")) for i in dates]

# ---- # ---- # ----
# ---- # ---- # ----
# ---- # ---- # ----
# ---- # ---- # ----

# ---- # ---- # ----
# 11.2 Time Series Basics
# Un tipo basico de serie de tiempo en pandas es una Serie indexada por peridos de tiempo.

#Creamos una lista de datatime objects y la utilizaremos como indice para una pd.Series()
dates = [datetime(2011, 1, 2), datetime(2011, 1, 5),
         datetime(2011, 1, 7), datetime(2011, 1, 8),
         datetime(2011, 1, 10), datetime(2011, 1, 12)]

ts = pd.Series(np.random.standard_normal(6), index=dates); ts

#Esa serie de datetime objects ahora, en el indice de un pd.Series, es un DatetimeIndex
ts.index

#Como cualquier otra Serie las operaciones aritmeticas se hacen por la union entre indices.
ts + ts[::2]        #Recordar que ts[::2] indica seleccion cada dos elementos.

#Pandas almacena los timestamps como datos de tipo "datetime64" con resolucion de nanosegundo
ts.index.dtype

#Los valores escalares de una DatetimeIndex son objectos "Timestamps" de Pandas
stamp = ts.index[0]; stamp

# ---- # ---- # ----
# 11.2.1 Indexing, Selection, Subsetting
# Las Series de tiempos se comportan como cualquier otra Serie cuando tu estas "indexing and selecting"
# datos basados en etiquetas
stamp = ts.index[2]; stamp      #Usando una etiqueta del indice, version 1
ts[stamp]

ts["2011-01-10"]                #Usando una etiqueta del indice, version 2

#Para series de tiempo mas largas, podemos utiilzar un año, o un año y un mes como etiqueta de filtro
longer_ts = pd.Series(np.random.standard_normal(1000),
                      index=pd.date_range("2000-01-01", periods=1000)); longer_ts

longer_ts["2001"]               #Solo un año
longer_ts["2001-05"]            #Un año un mes

#Los Datetime Objetos de Python tambien funcionan como etiquetas para filtrar
ts
ts[datetime(2011,1,7)]

#Como las series de tiempo son cronologicas, podemos hacer filtros por rangos. Ademas, no es necesario
#que la fecha ingresada en el rango exista.
ts
ts["2011-01-06":"2011-01-10"]

#NOTA:
# Al igual que en los arrays de Numpy, las Series obtenidas por "slicing" no son copias (solo "views"),
# sino que siguen siendo referenciadas a la Serie de origen

#El metodo .truncate() nos divide en dos la Serie dada una fecha entregada
ts.truncate(after="2011-01-09")

#Tdo lo anterior se cumple con los DataFrames
dates = pd.date_range("2000-01-01", periods=100, freq="W-WED");
long_df = pd.DataFrame(np.random.standard_normal((100, 4)),
                       index=dates,
                       columns=["Colorado", "Texas","New York", "Ohio"])

long_df.loc["2001-05"]

# ---- # ---- # ----
# 11.2.2 Time Series with Duplicate Indices
#En algunas aplicaciones, hay multiples observaciones con el mismo instante de tiempo
dates = pd.DatetimeIndex(["2000-01-01", "2000-01-02", "2000-01-02", "2000-01-02", "2000-01-03"]); dates
dup_ts = pd.Series(np.arange(5), index=dates)

#Podemos comprobar si los indices son unicos con el metodo para indices .is_unique
dup_ts.index.is_unique

#Si los indices no son unicos, la seleccion de elementos por indices ahora podra retornarnos
#valores singulares o multiples

dup_ts["2000-01-03"]  # not duplicated
dup_ts["2000-01-02"]  # duplicated

#Si nos conviene podemos utilizar .groupby() para agrupar los indices duplicados. Con ayuda
#del parametro "level=0" decimos que queremos agrupar por el indice
grouped = dup_ts.groupby(level=0)
grouped.mean()
grouped.count()

# ---- # ---- # ----
# ---- # ---- # ----
# ---- # ---- # ----
# ---- # ---- # ----

# ---- # ---- # ----
# 11.3 Date Ranges, Frequencies, and Shifting

# ---- # ---- # ----
# 11.3.1 Generating Date Ranges
# Como hemos visto .data_ranges() puede generar un DateTimeIndex de un determinado largo
# acorde a una determinada frecuencia

# En el siguiente ejmpleo generamos un DateTimeIndex desde "2012-04-01" a "2012-06-01" con
# frecuencia diaria (que es la por defecto)
index = pd.date_range("2012-04-01", "2012-06-01"); index

# Si solo ponemos una fecha podemos agregar la cantidad de periodos consecutivos a ella con
# "periods"
pd.date_range(start = "2012-04-01", periods=20)     #Usamos "start"
pd.date_range(end = "2012-04-01", periods=20)       #Usamos "end"

#Podemos moficiar la frecuencia de los Timestamp generados. Por ejemplo en vez de que fuera
#diario podria ser el ultimo dia laboral del mes (Business end of month -> BM)
pd.date_range("2000-01-01", "2000-12-01", freq="BM")

#VER LA TABLA 11.4 con las abrevaciones para cada frecuencia disponible

#Por defecto .data_range() conserva el tiempo (si es que lo trae) del comienzo o el final
#del Timestamp. Pero podemos normalizarlo a que posea el medio dia.

pd.date_range("2012-05-02 12:56:31","2012-10-02 11:50:27")          #DataTimeIndex conserva 12:56:31
pd.date_range("2012-05-02 12:56:31", periods=5)

pd.date_range("2012-05-02 12:56:31", periods=5, normalize=True)     #DateTimeIndex Noramlizado

# ---- # ---- # ----
# 11.3.2 Frequencies and Date Offsets
# Las frecuencias en Pandas son compuestas por un "base frecuency" y "multiplier".

# Las "base frecuency" son las referencias tipicas de string como "M" para frecuencia Mensual
# o "H" para frecuencia por hora. Para cada "base frecuency" hay un objeto referido como "date offset"

from pandas.tseries.offsets import Hour, Minute

hour = Hour(); hour                         #"date offset" de Hora
four_hours = Hour(4); four_hours            #"date offset" de 4 Horas

Hour(2) + Minute(30)                        #Aritmetica con "date offset"

#En verdad pocas veces usamos los "data offset", es mas comun usar los formatos string como "H" o "4H"
pd.date_range("2000-01-01", "2000-01-03 23:59", freq="4H")      #freq de cada 4 horas
pd.date_range("2000-01-01", periods=10, freq="1h30min")         #frea de 1h y 30 min

#Una frecuencia muy usada es la semana de cada mes, esta frecuencia queda sujeta al
#string alias de "WOM" (week of months).
#En el siguiente ejemplo tenemos WOM-3FI que indica el tercer viernes de cada mes
monthly_dates = pd.date_range("2012-01-01", "2012-09-01", freq="WOM-3FRI"); monthly_dates

# ---- # ---- # ----
# 11.3.3 Shifting (Leading and Lagging) Data
# Nos referimos a "Shifting" cuando movemos los datos hacia adelante o hacia atras a traves
# del tiempo.
# Existe el metodo .shifts() nos permite mover los datos hacia adelante o hacia atras sin
# modificar el indice. Obsevar que al mover los datos introducimos NAs

ts = pd.Series(np.random.standard_normal(4),
               index=pd.date_range("2000-01-01", periods=4, freq="M"))

ts
ts.shift(2)                         #movemos hacia adelante los datos dos momentos en el tiempo
ts.shift(-2)                        #movemos hacia atras los datos un momento en el tiempo

#Un comun uso de .shift es calcular la variacion porcentual en el tiempo.
ts / ts.shift(1) - 1

#Dado que el indice no se modifica algunos datos son descartados (quedan indices con valores NAs)
#Por lo tanto si la frecuencia es conocida, esta puede ser pasada para que no solo se muevan los
#datos sino tambien los indices

ts
ts.shift(2, freq="M")               #Desplazamos los indices sobre una frecuencia Mensual (ej: 31 de enero a 31 de marzo)
ts.shift(3, freq="D")               #Desplazamos los indices sobre una frecuencia Diaria  (ej: 31 de enero al 3 de febrero)
ts.shift(1, freq="90T")             #Desplazamos los indices sobre frecuencia de 90T (min)

#Shifting dates with offsets
#(Recordar que los "date offsets" son los objetos asociados a los "string alias" que usamos para definir
# una freq)
#Los "date offsets" de Pandas pueden ser usados con objetos datetime or timestamp

from pandas.tseries.offsets import Day, MonthEnd
now = datetime(2011, 11, 17)
now + 3 * Day()

#Si tu sumas un "anchored offset" como MonthEnd(), el primer incremento llevara a la fecha
#a la siguiente fecha acorde a la regla de la frecuencia

now + MonthEnd()
now + MonthEnd(2)
now + MonthEnd(-1)

#Los "anchored offset" pueden ser aplicados a una fecha por medio de sus metodos .rollfoward o .rollback

offset = MonthEnd()
offset.rollforward(now)
offset.rollback(now)

#Un uso creativo para el uso de un "anchored offset" y sus metodos vistos, es aplicandolo con un .groupby()
ts = pd.Series(np.random.standard_normal(20), index=pd.date_range("2000-01-15", periods=20, freq="4D")); ts
ts.groupby(MonthEnd().rollforward).mean()

#Aunque lo anterior puede ser remplazado por un .resample() (aun no visto)
ts.resample("M").mean()


#!/usr/bin/env python3
def main():
    import sys
    import shutil
    if not len(sys.argv) == 2:
        print("usage: {} file".format(sys.argv[0]))
        return -1

    shutil.copy2(sys.argv[1],sys.argv[1]+'~')
    file  = open(sys.argv[1],'r')

    output = ''
    for l in file.readlines():
        if not (('petsclogdouble' in l.lower()) or ('flops' in l.lower()) or ('petsclogflops' in l.lower())):
            output+=l
    file.close()

    file = open(sys.argv[1],'w')
    file.write(output)
    file.close()

if __name__ == "__main__":
    import sys
    sys.exit(main())


import requests.exceptions
import os

def lookup_():
    from API.lookup import look
    user = input('조회하고 싶은 코인을 입력 : ')
    if user == '코인조회':
        print(coins)
        print(f'''그외 : {len(ather)}...
=================================================''')
        lookup_()

    elif user == '시세 그래프':
        from API.graph import graph_show
        graph_show(coins=coins)
        lookup_()

    elif user == '':
        lookup_()

    else:
        try:
            if user not in 'exit':
                data = look(coinname=user.lower())
                market_conditions, percent, url = data.real_time()

                print(f'{user}의 현재 시세 : {market_conditions}\npercnet >> {percent}\nLink >> {url}')
                print('=================================================')

                lookup_()
        except AttributeError:
            print('코인이 존재하지 않습니다.')
            lookup_()

if __name__ == '__main__':
    try:
        try:
            from API.coin import ather, coins
            lookup_()
        except ModuleNotFoundError:
            os.system('pip install -r requirements.txt')
            print('시스템을 다시 켜 주세요')
    except requests.exceptions.ConnectionError:
        print('와이파이 연결후 시도해 주세요.')


from matplotlib import pyplot as plt

activities = [
    'sleeping',
    'eating',
    'working',
    'playing',
    'running'
]

colors = [
    '#357a96',
    '#354a96',
    '#513596',
    '#96354a',
    '#96357a',
]

slices = [1,2,3,4,5]

plt.pie(slices,
        labels=activities,
        colors=colors,
        startangle=90,
        shadow=True,
        explode=(0,0,0.1,0,0),
        wedgeprops={'edgecolor': 'white'},
        autopct='%1.1f%%' )

plt.title('Amazing stack plot')

plt.tight_layout()
plt.show()


import numpy as np
import pyshtools as sht  # used in many spherical calculations
import spherical as sf  # currently used only for CG coefficients
import matplotlib.pyplot as plt  # used only for plotting tools

'''
This module solves analytically the rotational diffusion and kinetics
in flurescence experiments with complex photophysics.
An arbitrary kinetic scheme can be implemented using fluorophore classes.
The illumination of the experiment is implemented as a pulse scheme with
square wave modulations.
Detection and illumination can be done in arbitrary NA objectives, using
the Axelrod correction.
'''

################################################################################
# Classes for programming experiments
################################################################################

class System:
    def __init__(self,
                 fluorophore,
                 diffusion,
                 illumination,
                 detection=[],
                 lmax=6,
                 norm='4pi',
                 ):
        # The simulation requires the expansion of all angular functions in
        # spherical harmonics (SH). lmax is the cutoff for the maximum l quantum
        # number of the SH basis set, i.e. 0 <= l <= lmax.
        #
        # The '4pi' normalization is reccomended because the coefficients for
        # l=0 and m=0 for the angular probability distributions give directly
        # the state populations.
        self.lmax = lmax
        self.norm = norm

        # Compute arrays with quantum numbers l and m of the SH basis set.
        self._l, self._m = quantum_numbers(self.lmax)

        # Compute wigner3j coefficients. These are necessary for the evaluation
        # of the product of angular functions from the SH expansion
        # coefficients.
        self._wigner3j_prod_coeffs = wigner_3j_prod_3darray(self._l, self._m)

        # Import the classes containing the parametrization and characteristics
        # of fluorophore, diffusion model, illumination, and detection.
        self.fluorophore = fluorophore
        self.diffusion = diffusion
        self.illumination = illumination
        self.detection = detection

        '''
        Small description of hidden variables.
        These are used mainly for debugging.
        self._F is the photon flux prod matrix, when multiplied by the cross
            section gives the absorption rate prod matrix as a function of the
            angle.
        self._c_exc are SH coefficients for the orientational functions behind F.
            It can be interpred as the number of photons that would be absorbed
            by a molecule with cross section 1cm2 as a function of the
            orientation of the dipole.
        self._D Diffusion matrix rate model for each species.
        self._K Kinetic matrix rate model for each time window and each pair
            of species.
        self._M = M Full diffusion-kinetics matrix for each time window and each
            pair of species.
        self._c0 SH coeffs of starting orientational populations
        self._c SH coeffs of populations computed at the derired times
        self._c_det SH coeffs of the detectors collection functions
        self._s fluorescent signal for each detector
        '''
        return None

    def detector_signals(self, time):
        # Compute the flurescence signal registered in the detectors
        # Firt, solve the diffusion/kinetics problem and get the SH coefficients
        # of populations.
        c = self.solve(time)

        # Multiply the populations by the quantum yields of fluorescence.
        # Usually, only the fluorescent state will have a quantum yield
        # different from zero.
        c_fluo = c * self.fluorophore.quantum_yield_fluo[:,None,None]

        # Compute the SH coefficients for the collection functions of
        # the detectors
        c_det = self.detection.detector_coeffs(self._l, self._m)
        ndetectors = c_det.shape[0]

        # Initialize signal array and compute signals
        s = np.zeros( (ndetectors, time.size) )
        for i in range(ndetectors):
            s[i] = np.sum(c_det[i][None,:,None] * c_fluo, axis=(0,1))
        if self.norm == '4pi':
            s = s/4*np.pi

        # Save variables, mainly for debugging.
        self._c_det = c_det
        self._s = s
        return s

    def solve(self, time):
        # Solve the time evolution of the system including the pulse scheme.

        # Compute the diffusion kinetics matrix
        # Matrix M contains all the information about the time evolution of the
        # system in every time window.
        M = self.diffusion_kinetics_matrix()

        # Get the initial conditions for the experiment from the fluorophore
        # class.
        c0 = self.fluorophore.starting_coeffs(self._l, self._m)

        # Optimization, remove all odd l value coefficients from c0 and M.
        # We will need to add them back, after the solution.
        # This semplification is usefull when only light matter interaction can
        # affect the orientational probablities.None
        # print(M.shape)
        # M, c0 = remove_odd_coeffs(M, c0, self._l)
        # print(M.shape)

        # Shift the time with time0, so it is represented in the laboratory
        # time (starting with the time zero of pulse sequence).
        time_lab = time + self.illumination.time0
        time_mod = np.cumsum(self.illumination.time_windows)

        # Insert a zero at the beginning of the modulation time sequence and
        # extend last time window to fit the whole requested times to sovle.
        time_mod = np.insert(time_mod, 0, 0 )
        time_mod[-1] = np.max(time_lab)

        # Solve evolution in every time window
        c = np.zeros((self.fluorophore.nspecies,
                      self._l.size,
                      time.size))
        for i in np.arange(self.illumination.nwindows):
            # Selection of time axis inside the current time window
            time_sel = np.logical_and(time_lab >= time_mod[i],
                                      time_lab <= time_mod[i+1])

            # Select the time points inside the current time window
            time_i = time_lab[time_sel]

            # Add to the time points the end of the time window
            # so we can comute the starting coefficients for the next window.
            time_i = np.append(time_i, time_mod[i+1])

            # Shift the local time axis so the zero concides with the beginning
            # of the current time window
            time_i = time_i - time_mod[i]

            # Solve the time evolution
            c_i, _, _ = solve_evolution(M[i], c0, time_i)

            # Save results and update initial conditions for the next window
            c[:,:,time_sel] = c_i[:,:,:-1]
            c0 = c_i[:,:,-1]

        # Add back zeros in place of odd l coeffs
        # M, c0, c = add_odd_coeffs_zeros(M, c0, c, self._l)

        # Save variables, mainly for debugging.
        self._c0 = c0
        self._c = c
        return c

    def diffusion_kinetics_matrix(self):
        # Preliminary computations based on the illumination class.
        # Photon flux product coefficients based on wigner3j symbols
        F, c_exc = self.illumination.photon_flux_prod_coeffs(
                                                    self._l,
                                                    self._m,
                                                    self._wigner3j_prod_coeffs)

        # Compute the rotational diffusion matrix, it will not change with the
        # time windows of laser modulation.
        # The diffusion model is provided by the fluorophore class.
        # In the future, if more complex diffusion models will be implemented,
        # it might be convinient to create a separate diffusion class.
        D = self.diffusion.diffusion_matrix(self._l, self._m,
                self.fluorophore.nspecies)

        # Prepare the rotational diffusion matrix for each time window
        nwindows = self.illumination.nwindows
        nspecies = self.fluorophore.nspecies
        M = np.zeros( (nwindows,
                       nspecies, nspecies,
                       self._l.size, self._l.size) )
        K = np.zeros( (nwindows,
                       nspecies, nspecies,
                       self._l.size, self._l.size) )
        for i in np.arange(nwindows):
            K[i] = self.fluorophore.kinetics_matrix(self._l, self._m,
                        F * self.illumination.modulation[:,i][:,None,None],
                        self.illumination.wavelength)
            M[i] = diffusion_kinetics_matrix(D, K[i])

        # Save variables, mainly for debugging.
        self._c_exc = c_exc
        self._F = F
        self._D = D
        self._K = K
        self._M = M
        return M

def anisotropy(signals):
    return (signals[0] - signals[1]) / (signals[0] + 2*signals[1])

def anisotropy_variance(signals, signals_minus_background):
    il_wobg = signals_minus_background[0]; # Intensity parallel w/o background
    ip_wobg = signals_minus_background[1]; # Intensity perpendicular w/o background
    E_il = signals[0]; # Intensity parallel
    E_ip = signals[1]; # Intensity perpendicular

    # Expectations, variances and covariances
    Cov_il_ip = - E_il * E_ip / (E_il + E_ip)
    E_N = np.abs(il_wobg - ip_wobg)
    E_D = np.abs(il_wobg + 2*ip_wobg)
    Var_N = E_il + E_ip - 2*Cov_il_ip
    Var_D = E_il + 4*E_ip + 4*Cov_il_ip
    Cov_N_D = E_il - 2*E_ip + Cov_il_ip

    # Anisotropy Variance (see DP 20200224) and reference
    Var_r = (
        Var_N/np.power(E_D,2)
        - 2*E_N*Cov_N_D/np.power(E_D,3)
        + Var_D/np.power(E_D,4)
        )
    return Var_r

##########################
# Diffusion Models Classes
##########################
class IsotropicDiffusion:
    def __init__(self,
                 diffusion_coefficient=14e-9,  # GFP rotational diffusion
                 ):
        # Rotational diffusion coefficient in Hertz
        self.diffusion_coefficient = diffusion_coefficient  # [Hz]

    def diffusion_matrix(self, l, m, nspecies):
        # Compute the diffusion matrix
        # Here an isotropic diffusion model is employed, every state has also
        # the same rotational diffusion properties.
        D = isotropic_diffusion_matrix(l, m,
                self.diffusion_coefficient,
                nspecies)
        return D

#####################
# Fluorophore classes
#####################
class NegativeSwitcher:
    def __init__(self,
                 extinction_coeff_on=[0, 0],
                 extinction_coeff_off=[0, 0],
                 wavelength=[405, 488],
                 lifetime_on=3e-9,
                 lifetime_off=16e-12,
                 quantum_yield_on_to_off=0.001,
                 quantum_yield_off_to_on=0.2,
                 quantum_yield_on_fluo=1,
                 starting_populations=[1,0,0,0],
                 deprotonation_time_off = 15e-6,  # for 6 states model
                 protonation_time_on = 150e-6,  # for 6 states model
                 quantum_yield_trans_to_cis_anionic=0,  # for 8 states model
                 quantum_yield_cis_to_trans_neutral=0, # for 8 states model
                 quantum_yield_cis_anionic_bleaching=0,
                 quantum_yield_trans_anionic_bleaching=0,
                 nspecies=4):
        # Cross section in cm2 of absorptions
        epsilon2sigma = 3.825e-21  # [Tkachenko2007, page 5]
        self.extinction_coeff_on = np.array(extinction_coeff_on)  # [M-1 cm-1]
        self.extinction_coeff_off = np.array(extinction_coeff_off)  # [M-1 cm-1]
        self.cross_section_on = self.extinction_coeff_on * epsilon2sigma  # [cm2]
        self.cross_section_off = self.extinction_coeff_off * epsilon2sigma  # [cm2]
        self.wavelength = np.array(wavelength)  # [nm]

        # Lifetime of the on excited state in seconds
        # Assumption: lifetime on and off are the same for the same protonation
        # state. This might not be the case, especially because cis_neutral
        # species is not fluorescent, so most likely it will have a shorter
        # lifetime. For small enough excitation kinetic rates, if we don't have
        # accumulation of exited state species, then it doesn't matter much.
        self.lifetime_on = lifetime_on  # [s]
        self.lifetime_off = lifetime_off  # [s]

        # Quantum yield of an off-switching event from the on excited state and
        # fluorescence from the on state
        self.quantum_yield_on_fluo = quantum_yield_on_fluo
        self.quantum_yield_on_to_off = quantum_yield_on_to_off  # cis_to_trans_anionic
        self.quantum_yield_off_to_on = quantum_yield_off_to_on  # trans_to_cis_neutra

        # Quantum yeilds and Protonation and deprotonation times for 6 and 8
        # states models.
        self.quantum_yield_cis_to_trans_neutral = quantum_yield_cis_to_trans_neutral
        self.quantum_yield_trans_to_cis_anionic = quantum_yield_trans_to_cis_anionic
        self.protonation_time_on = protonation_time_on
        self.deprotonation_time_off = deprotonation_time_off

        # Bleaching quantum yields, assuming only a bleaching channel from
        # excitaiton of the on state.
        self.quantum_yield_cis_anionic_bleaching = quantum_yield_cis_anionic_bleaching
        self.quantum_yield_trans_anionic_bleaching = quantum_yield_trans_anionic_bleaching

        # Label describing the fluorophore type
        # Number of states in the kinetic model
        self.nspecies = nspecies

        # Index of the fluorescent state
        # Here, only one fluorescent state is assumed.
        # This is not necessary in general and could be extended in the future.
        self.quantum_yield_fluo = np.zeros((nspecies))
        self.quantum_yield_fluo[1] = self.quantum_yield_on_fluo

        # Population at the beginning of the experiment
        self.starting_populations = starting_populations
        return None

    def kinetics_matrix(self, l, m, F, wavelength_laser):
        # Compute the kinetics matrix expanded in l, m coefficients
        # Note that the order of the laser in F must be consistent with the
        # choices made in the fluorophore class.
        # In this case F[0] is the blue light.

        # l, m, F, and wavelength must be provided by outside.
        # l and m are the quantum numbers of SH, while F encodes the info about
        # photon flux and it's angular dependence.

        # Initialize arrays
        Feye = np.eye( (l.size) )
        K = np.zeros( (self.nspecies, self.nspecies, l.size, l.size))

        # Put the kinetic constants connecting the right species
        # K[1,0] is the kinetic constant for the proces 1 <- 0.
        nlasers = F.shape[0]
        nwavelengths = self.wavelength.size
        if self.nspecies == 4:
            self.fluorophore_type = 'rsFP_negative_4states'
            for i in np.arange(nlasers):
                for j in np.arange(nwavelengths):
                    if wavelength_laser[i] == self.wavelength[j]:
                        K[1,0] = K[1,0] + F[i] * self.cross_section_on[j]
                        K[3,2] = K[3,2] + F[i] * self.cross_section_off[j]
            K[0,1] = Feye / self.lifetime_on
            K[2,1] = Feye / self.lifetime_on  * self.quantum_yield_on_to_off
            K[2,3] = Feye / self.lifetime_off
            K[0,3] = Feye / self.lifetime_off * self.quantum_yield_off_to_on

        if self.nspecies == 6:
            self.fluorophore_type = 'rsFP_negative_6states'
            for i in np.arange(nlasers):
                for j in np.arange(nwavelengths):
                    if wavelength_laser[i] == self.wavelength[j]:
                        K[1,0] = K[1,0] + F[i] * self.cross_section_on[j]
                        K[4,3] = K[4,3] + F[i] * self.cross_section_off[j]
            K[0,1] = Feye / self.lifetime_on
            K[2,1] = Feye / self.lifetime_on  * self.quantum_yield_on_to_off
            K[3,2] = Feye / self.protonation_time_on
            K[3,4] = Feye / self.lifetime_off
            K[5,4] = Feye / self.lifetime_off * self.quantum_yield_off_to_on
            K[0,5] = Feye / self.deprotonation_time_off

        if self.nspecies == 8:
            self.fluorophore_type = 'rsFP_negative_8states'
            for i in np.arange(nlasers):
                for j in np.arange(nwavelengths):
                    if wavelength_laser[i] == self.wavelength[j]:
                        K[1,0] = K[1,0] + F[i] * self.cross_section_on[j]  # cis anionic
                        K[3,2] = K[3,2] + F[i] * self.cross_section_on[j]  # trans anionic
                        K[5,4] = K[5,4] + F[i] * self.cross_section_off[j]  # trans neutral
                        K[7,6] = K[7,6] + F[i] * self.cross_section_off[j]  # cis neutral
            # On-branch
            K[0,1] = Feye / self.lifetime_on
            K[2,1] = Feye / self.lifetime_on  * self.quantum_yield_on_to_off
            K[2,3] = Feye / self.lifetime_on
            K[0,3] = Feye / self.lifetime_on  * self.quantum_yield_trans_to_cis_anionic
            K[4,2] = Feye / self.protonation_time_on

            # Off-branch
            K[4,5] = Feye / self.lifetime_off
            K[6,5] = Feye / self.lifetime_off * self.quantum_yield_off_to_on
            K[6,7] = Feye / self.lifetime_off
            K[4,7] = Feye / self.lifetime_off * self.quantum_yield_cis_to_trans_neutral
            K[0,6] = Feye / self.deprotonation_time_off

        if self.nspecies == 9:
            self.fluorophore_type = 'rsFP_negative_9states_bleaching'
            for i in np.arange(nlasers):
                for j in np.arange(nwavelengths):
                    if wavelength_laser[i] == self.wavelength[j]:
                        K[1,0] = K[1,0] + F[i] * self.cross_section_on[j]  # cis anionic
                        K[3,2] = K[3,2] + F[i] * self.cross_section_on[j]  # trans anionic
                        K[5,4] = K[5,4] + F[i] * self.cross_section_off[j]  # trans neutral
                        K[7,6] = K[7,6] + F[i] * self.cross_section_off[j]  # cis neutral
            # On-branch
            K[0,1] = Feye / self.lifetime_on
            K[2,1] = Feye / self.lifetime_on  * self.quantum_yield_on_to_off
            K[2,3] = Feye / self.lifetime_on
            K[0,3] = Feye / self.lifetime_on  * self.quantum_yield_trans_to_cis_anionic
            K[4,2] = Feye / self.protonation_time_on

            # Off-branch
            K[4,5] = Feye / self.lifetime_off
            K[6,5] = Feye / self.lifetime_off * self.quantum_yield_off_to_on
            K[6,7] = Feye / self.lifetime_off
            K[4,7] = Feye / self.lifetime_off * self.quantum_yield_cis_to_trans_neutral
            K[0,6] = Feye / self.deprotonation_time_off

            # Bleaching channels from teh on-branch
            K[8,1] = Feye / self.lifetime_on * self.quantum_yield_cis_anionic_bleaching
            K[8,3] = Feye / self.lifetime_on * self.quantum_yield_trans_anionic_bleaching
        return K

    def starting_coeffs(self, l, m):
        # Compute the starting values for the population SH coefficients
        c0 = np.zeros( (self.nspecies, l.size) )
        c0[:,0] = self.starting_populations
        return c0

class STEDDye:
    def __init__(self):
        pass

######################
# Illumination classes
######################
class SingleLaser:
    def __init__(self,
                 power_density,
                 polarization='x',
                 wavelength=488,
                 numerical_aperture=1.4,
                 refractive_index=1.518):
        # Compute the photon flux from power density and wavelength
        self.power_density = power_density  # [W/cm2]
        self.wavelength = wavelength  # [nm]
        self.photon_flux = photon_flux(self.power_density,
                                       self.wavelength)  # [photons/cm2]

        # Polarization of the beam
        # It can be 'x', 'y', or 'c' for circular.
        self.polarization = polarization

        # Numerical aperture of excitation light beam
        self.numerical_aperture = numerical_aperture

        # Refractive index of the immersion medium of the objective
        self.refractive_index = refractive_index

        # Time windows settings for compatibility
        self.modulation = np.array([[1]], dtype='float')
        self.time_windows = np.array([0], dtype='float')
        self.nwindows = 1
        self.time0 = 0
        return None

    def photon_flux_prod_coeffs(self, l, m, wigner_3j_prod_coeffs):
        # Compute the prod_matrix for the angular dependence of the photon flux.
        # When the photon flux is multiplied by the absorbtion cross-section
        # the kinetic rate of absortion is obtained.
        c_exc = np.zeros((1, l.size))
        c_exc[0] = na_corrected_linear_coeffs(l, m,
                polarization = self.polarization,
                numerical_aperture = self.numerical_aperture,
                refractive_index = self.refractive_index,
                ) * self.photon_flux

        # Return the matrix F as a 3 dimensional array, this will be helpfull
        # when more than one laser is used to excite the sample.
        F = np.zeros((1, l.size, l.size))
        F[0] = kinetic_prod_block(c_exc[0], wigner_3j_prod_coeffs)
        return F, c_exc

class ModulatedLasers:
    def __init__(self,
                 power_density,
                 polarization=['x', 'xy'],
                 wavelength=[405, 488],
                 modulation=[[1, 0], [1, 1]],
                 time_windows=[250e-9, 1e-3],
                 time0=0,
                 numerical_aperture=1.4,
                 refractive_index=1.518):
        # Compute the photon flux from power density and wavelength
        self.power_density = np.array(power_density)  # [W/cm2]
        self.wavelength = np.array(wavelength)  # [nm]
        self.photon_flux = photon_flux(self.power_density,
                                       self.wavelength)  # [photons/cm2]

        # Polarization of the beam
        # It can be 'x', 'y', or 'c' for circular.
        self.polarization = polarization

        # Time Modulation properties
        self.modulation = np.array(modulation, dtype='float')
        self.time_windows = np.array(time_windows, dtype='float')
        self.nwindows = self.time_windows.shape[0]
        self.time0 = time0

        # Numerical aperture of excitation light beam
        # Refractive index of the immersion medium of the objective
        self.numerical_aperture = numerical_aperture
        self.refractive_index = refractive_index

        return None

    def photon_flux_prod_coeffs(self, l, m, wigner_3j_prod_coeffs):
        # Compute the prod_matrix for the angular dependence of the photon flux.
        # When the photon flux is multiplied by the absorbtion cross-section
        # the kinetic rate of absortion is obtained.

        # Initialize arrays for SH photoselection coefficients in c, and
        # product coefficients in F.
        nlasers = np.size(self.polarization)
        c_exc = np.zeros((nlasers, l.size))
        F = np.zeros((nlasers, l.size, l.size))
        for i in np.arange(nlasers):
            c_exc[i] = na_corrected_linear_coeffs(l, m,
                    polarization = self.polarization[i],
                    numerical_aperture = self.numerical_aperture,
                    refractive_index = self.refractive_index,
                    ) * self.photon_flux[i]
            F[i] = kinetic_prod_block(c_exc[i],
                        wigner_3j_prod_coeffs)

        # F is a 3 dimensional array. The firt index is for the lasers, the last
        # indexes are for lm of SH.
        return F, c_exc

def photon_flux(power_density, wavelength):
    # Compute the photon flux from power density and wavelength.
    # The wavelength is used to compute the photon energy
    # photon_energy = h*c/wavelength
    # The wavelength is coverted in meters.
    light_speed = 299792457  # [m/s]
    h_planck = 6.62607015e-34  # [J*s]
    photon_energy = h_planck * light_speed / (wavelength*1e-9)  # [J]
    photon_flux = power_density / photon_energy  # [photons/cm2]
    return photon_flux

# TODO Check that photon flux is conserved changing polarization state

def na_corrected_linear_coeffs(l, m,
                               polarization='x',
                               numerical_aperture=1.4,
                               refractive_index=1.518,
                               norm='4pi'):
    # Compute Axelrod high-NA correction coefficients
    k = axelrod_correction_k(numerical_aperture, refractive_index)

    # Get the SH expansion coefficients for x, y, and z photoselections.
    cx = linear_light_matter_coeffs(l, m, polarization='x', norm=norm)
    cy = linear_light_matter_coeffs(l, m, polarization='y', norm=norm)
    cz = linear_light_matter_coeffs(l, m, polarization='z', norm=norm)

    # Compute the SH expansion coefficients
    # We are assuming that the propagation direction is z.
    # If z polarization is chosen, the propagation direction is assumed as x.
    if polarization == 'x':
        c = k[0]*cz + k[1]*cy + k[2]*cx

    if polarization == 'y':
        c = k[0]*cz + k[1]*cx + k[2]*cy

    if polarization == 'z':
        c = k[0]*cx + k[1]*cy + k[2]*cz

    if polarization == 'xy':
        c = k[0]*cz + (k[1]+k[2])/2*cy + (k[1]+k[2])/2*cx

    # c are the SH coefficients of the expanded angular function.
    return c

def axelrod_correction_k(numerical_aperture, refractive_index):
    # Normalized Axelrod correction cartesian coefficients from [Fisz2005] eq.(2).
    # For a linear polarized beam:
    # - k[0] refers to the propagation direction
    # - k[1] refers to the perpendicular direction
    # - k[2] refers to the parallel direction
    # In the same paper the correction for the spherical coordinates problem are
    # discussed. It will be useful when adding the photoswithing angle.
    # Note that k[0] + k[1] + k[2] = 1. In other words, for a randomly oriented
    # sample in linear excitation, the total number of excited molecules is the
    # same regardless of the NA.

    # The maximum angle of the focalized beam with respect to the
    # propagation direction of the beam is computed.
    # NA = n*sin(theta), thus theta = arcsin(NA/n).
    max_ray_angle = np.arcsin(numerical_aperture/
                              refractive_index)

    k = np.zeros(3)
    costh = np.cos(max_ray_angle)
    k[0] = 1/6  * (2 -3*costh             +costh**3) / (1 - costh)
    k[1] = 1/24 * (1 -3*costh +3*costh**2 -costh**3) / (1 - costh)
    k[2] = 1/8  * (5 -3*costh   -costh**2 -costh**3) / (1 - costh)
    return k

def linear_light_matter_coeffs(l, m, polarization='x',
                                    norm='4pi'):
    # This function computes the spherical harmonics expansion for linear
    # light matter interactions photoselection functions.
    # For example, the function (r dot x)^2 is expanded and has only three non
    # zero coefficients, in particular l=0, m=0 and l=2, m=0,2. r and x are
    # versors. In this function few polarizations are expressed in terms of
    # their SH coefficients.
    c = np.zeros(l.shape)
    if polarization == 'x':
        c[np.logical_and(l==0, m==0)] = 1/3
        c[np.logical_and(l==2, m==0)] = -np.sqrt(1/45)
        c[np.logical_and(l==2, m==2)] = np.sqrt(1/15)
    if polarization == 'y':
        c[np.logical_and(l==0, m==0)] = 1/3
        c[np.logical_and(l==2, m==0)] = -np.sqrt(1/45)
        c[np.logical_and(l==2, m==2)] = -np.sqrt(1/15)
    if polarization == 'z':
        c[np.logical_and(l==0, m==0)] = 1/3
        c[np.logical_and(l==2, m==0)] = np.sqrt(4/45)
    if polarization == 'xy':  # circular polarization in the xy plane
        c[np.logical_and(l==0, m==0)] = 1/3
        c[np.logical_and(l==2, m==0)] = -np.sqrt(1/45)
    if norm == 'ortho':
        c = c/np.sqrt(4*np.pi)

    # Normalize the coefficients so the total integral of the function is 1
    # c = c/c[0]  # For now it is not on, because we loose the probability
    # connection
    return c

###################
# Detection classes
###################
class PolarizedDetection:
    def __init__(self,
                 polarization=['x', 'y'],
                 numerical_aperture=1.4,
                 refractive_index=1.518,
                 ):
        # Numerical aperture of the collection objective
        self.numerical_aperture = numerical_aperture

        # Refractive index of the immersion medium
        self.refractive_index = refractive_index

        # Polarization directions of the detection channels
        self.polarization = polarization
        return None

    def detector_coeffs(self, l, m):
        # Compute the SH expansion for the angular collection functions of the
        # detectors
        ndetectors = np.size(self.polarization)
        c = np.zeros((ndetectors, l.size))

        for i in np.arange(ndetectors):
            c[i] = na_corrected_linear_coeffs(l, m,
                polarization=self.polarization[i],
                numerical_aperture=self.numerical_aperture,
                refractive_index=self.refractive_index,
                )
        return c

################################################################################
# Engine of the rotational-diffusion and kinetics solver
################################################################################

def quantum_numbers(lmax):
    # Generate arrays with quantum numbers l and m
    l = np.array([])
    m = np.array([])
    for li in np.arange(lmax+1):
        # This is the way pyshtools is exporting vectors of sh expansions
        l = np.concatenate((l, np.ones(2*li+1)*li))
        m = np.concatenate((m, np.arange(li+1)))
        m = np.concatenate((m, np.arange(-1,-li-1,-1)))
    l = np.int32(l)
    m = np.int32(m)
    return l, m

def clebsch_gordan_prod_3darray(l, m):
    # 3D array with all the clebsh gordan coefficients for sh multiplication
    n = l.size
    cgp = np.zeros([n, n, n])
    for i in np.arange(n):
        for j in np.arange(n):
            for k in np.arange(n):
                    cgp[i,j,k] = np.sqrt( (2*l[i] + 1) * (2*l[j] + 1) /
                                        ( np.pi*4    * (2*l[k] + 1) ) ) * (
                                sf.clebsch_gordan(l[i], 0,
                                                l[j], 0,
                                                l[k], 0) *
                                sf.clebsch_gordan(l[i], m[i],
                                                l[j], m[j],
                                                l[k], m[k])
                                )

    # Constant due to normalizatino issues
    cgp = cgp*np.sqrt(4*np.pi)
    return cgp

def wigner_3j_prod_3darray(l, m):
    # 3D array with all the coefficient for sh multiplication
    # Here we use Wigner3j symbols from sht, it is 10-20 time faster than
    # clebsch_gordan_prod_3darray(l, m).

    # Preliminary computations
    n = l.size
    lmax = np.uint(np.max(l))
    w3jp = np.zeros([n, n, n])

    # Limit calculation to allowed l1 indexes
    # This optimization uses the fact that l1 can assume only limite values
    # in light-matter interaction.
    # For linear interaction only l1=0 and l1=2 are allowed.
    l1_allowed = np.logical_or(l==0, l==2)
    l1_allowed_indexes = np.arange(n)[l1_allowed]

    for i in l1_allowed_indexes:
        for j in np.arange(n):
            # Get all the quantum numbers for 1 and 2
            l1 = l[i]
            m1 = m[i]
            l2 = l[j]
            m2 = m[j]

            # Compute quantum numbers for 3
            m3 = -m1-m2 # requirement for w3j to be non zero

            # Compute Wigner3j symbols
            # w3j0 = w3jcalc.calculate(l1, l2, 0, 0)
            # w3j1 = w3jcalc.calculate(l1, l2, m1, m2)
            w3j1 =  wigner_3j_all_l(l, l1, l2, m3, m1, m2, lmax)
            w3j0 =  wigner_3j_all_l_m0(l, l1, l2, lmax)

            # Compute SH product coefficients
            w3jp[i,j,:] = np.sqrt( (2*l1 + 1) * (2*l2 + 1) * (2*l + 1) /
                                    (np.pi*4) ) * w3j0 * w3j1 *(-1)**np.float(m3)
            #NOTE: (-1)** factor is necessary to match the result obtained
            # with clebsh-gordan coefficients. I am not sure why it is the case.

    # Constant due to normalizatino issues
    # Normalization of SH: https://shtools.github.io/SHTOOLS/real-spherical-harmonics.html
    w3jp = w3jp*np.sqrt(4*np.pi)
    return w3jp

def wigner_3j_all_l(l, l1, l2, m3, m1, m2, lmax):
    # Compute all Wigner 3j symbols for a set of l3 at l1,l2,m3,m1,m2

    # Compute the coefficients
    # https://shtools.github.io/SHTOOLS/pywigner3j.html
    w3j, l3min, l3max = sht.utils.Wigner3j(l1, l2, m3, m1, m2)
    l3 = np.arange(l3min, l3max+1)
    l3 = l3[l3<=lmax]  # Restrict to values below lmax
    w3j = w3j[np.arange(l3.size)]

    # Index of l, m vector of SHTools
    # https://shtools.github.io/SHTOOLS/pyshcilmtovector.html
    i = np.uint(1.5-np.sign(-m3)/2)  # The sign minus is necessary, m3 = -M
    k = np.uint(l3**2+(i-1)*l3+np.abs(m3))

    # Final array of l.size with Wigner 3j coefficients
    w3jl = np.zeros(l.size)
    w3jl[k] = w3j
    return w3jl

def wigner_3j_all_l_m0(l, l1, l2, lmax):
    # Compute all Wigner 3j symbols for a set of l3 at l1,l2,m3,m1,m2

    # Compute the coefficients
    # https://shtools.github.io/SHTOOLS/pywigner3j.html
    w3j, l3min, l3max = sht.utils.Wigner3j(l1, l2, 0, 0, 0)
    l3 = np.arange(l3min, l3max+1)
    l3 = l3[l3<=lmax]  # Restrict to values below lmax
    w3j = w3j[np.arange(l3.size)]

    # Create array of size l.size adding w3j symbols for all l values
    w3jl = np.zeros(lmax+1)
    w3jl[l3] = w3j
    l3 = np.arange(lmax+1)
    w3jl = w3jl[l]
    return w3jl

def kinetic_prod_block(kvec, cgp):
    # Create a kinetic constant block for multiplication using cg coeffs.
    kblock = np.transpose(cgp, [2, 1, 0]).dot(kvec)
    return kblock

def isotropic_diffusion_block(l, m, diffusion_coefficient):
    # Make the diffusion diagonal block for isotropic rotational diffusion
    ddiag = -l*(l+1)*diffusion_coefficient
    dblock = np.zeros([l.size, l.size])
    np.fill_diagonal(dblock, ddiag)
    return dblock

def isotropic_diffusion_matrix(l, m, diffusion_coefficient, nspecies):
    # Make all the diffusion matrices for all the species assuming isotropic
    # rotational diffusion and the same rotational diffusion coefficient for
    # every species.
    D = np.zeros( (nspecies, l.size, l.size) )
    D[0] = isotropic_diffusion_block(l, m, diffusion_coefficient)
    for i in np.arange(1,nspecies):
        D[i] = D[0]
    return D

def diffusion_kinetics_matrix(D, K):
    # Create the full kinetic diffusion matrix expanded in sh
    nspecies = D.shape[0]
    M = np.zeros(K.shape)

    for i in range(nspecies):
        for j in range(nspecies):
            # Add the rotational diffusion blocks on the diagonal
            if i==j:
                M[i,i] = D[i]
            # Add the kinetics blocks in all the slots
            M[i,j] = M[i,j] + K[i,j]

    # Add on the diagonal the kinetics contributions that deplete the states
    for i in range(nspecies):
        for j in range(nspecies):
            if i != j:
                M[i,i] = M[i,i] - M[j,i]
    return M

def kinetics_diffusion_matrix_lmax(Dvec, Kmatrix, lmax):
    # Create the full kinetic diffusion matrix expanded in sh staring
    # from scratch.
    # In this routine the expansion in l,m are included.
    assert len(Dvec) == len(Kmatrix)

    # Compute quantum number arrays and cg coefficients
    # The main simplification is that linear light matter interaction
    # limits l1 to 0 and 2.
    l, m = quantum_numbers(lmax)
    #TODO Compute only cg coefficients that are needed, partially done
    # cgp = clebsch_gordan_prod_3darray(l, m)
    cgp = wigner_3j_prod_3darray(l, m)

    ncoeff = l.size  # number of spherical harmonics expansion coefficients
    nspecies = len(Dvec)  # number of species

    M = np.array( [ [np.zeros([ncoeff,ncoeff])]*nspecies ]*nspecies )
    for i in range(nspecies):
        for j in range(nspecies):
            if i == j:
                M[i,j] = isotropic_diffusion_block(l, m, Dvec[i])
            # Create blocks of the kinetic matrix rates
            else:
                k = Kmatrix[i][j]
                if np.size(k) == 1:
                    M[i,j] = np.eye(ncoeff).dot(k)
                else:
                    M[i,j] = kinetic_prod_block(k, cgp)

    for i in range(nspecies):
        for j in range(nspecies):
            if i != j:
                M[i,i] = M[i,i]-M[j,i]
    return M

def solve_evolution(M, c0, time):
    # Analitically solve the diffusion-kinetic problem by matrix exp of M
    nspecies = c0.shape[0]
    ncoeffs = c0.shape[1]
    c = np.zeros((nspecies, ncoeffs, time.size))

    # Variables with unique index for species and spherical harmonics
    c0 = c0.flatten()
    M = np.transpose(M, axes=[0,2,1,3])
    M = np.reshape(M, [nspecies*ncoeffs, nspecies*ncoeffs])
    c = np.reshape(c, [nspecies*ncoeffs, time.size])

    #TODO: Optimize matrix multiplication for only c0 values different from zero
    # Coefficients of starting conditions that are zeros
    # Useful for optimizing matrix multiplication
    # Most of the coefficient are zero because of simmetry and could be removed
    # from the problem.

    # Diagonalize M and invert eigenvector matrix
    # This will optimize the computation of matrix exponentiation.
    L, U = np.linalg.eig(M)
    Uinv = np.linalg.inv(U)

    # The following is equivalent to:
    #
    # for i in np.arange(time.size):
    #     ci = np.matmul(U, np.diag(np.exp(L*time[i])))
    #     ci = np.matmul(ci, Uinv)
    #     ci = ci.dot(c0)
    #     c[:,i] = ci
    #
    # Doing the matrix multiplication transposed reduces all the costly full
    # matrix multiplications to only matrix times vectors.
    # Depending on matrix size this version is several times faster.

    ci0 = c0.dot(Uinv.T)
    UTr = U.T  # Transpose the matrix only once
    for i in np.arange(time.size):
        ci = np.matmul(ci0, np.diag(np.exp(L*time[i])))
        ci = np.matmul(ci, UTr)
        c[:,i] = np.real(ci)  # Discard small rounding error complex parts

    # Reshape the coefficients into a 3D array, separating the coefficients
    # for each species.
    c = np.reshape(c, (nspecies, ncoeffs, time.size))
    return c, L, U

def remove_odd_coeffs(M, c0, l):
    l_sel = l%2 == 0
    c0 = c0[:, l_sel]
    M = M[:, :, :, l_sel, l_sel]
    return M, c0

def add_odd_coeffs_zeros(M, c0, c, l):
    c0_out = np.zeros((c0.shape[0], l.size))
    c_out = np.zeros((c0.shape[0], l.size, c0.shape[2]))
    M_out = np.zeros((M.shape[0], M.shape[1], M.shape[2], l.size, l.size))
    l_sel = l%2 == 0
    c0_out[:,l_sel] = c0
    c_out[:,l_sel,:] = c
    M_out[:,:,:,l_sel,l_sel] = M
    return M_out, c0_out, c_out

################################################################################
# Auxiliary functions
################################################################################

def make_angles(lmax):
    # Create angles for computing your functions of theta and phy
    cos2 = sht.SHGrid.from_zeros(lmax=lmax, kind='real')
    # cos2 = sht.shclasses.DHComplexGrid.from_zeros(lmax=2)
    theta = cos2.lats()
    phi = cos2.lons()
    omega = np.meshgrid(theta,phi)
    omega[0] = omega[0].transpose()*np.pi/180
    omega[1] = omega[1].transpose()*np.pi/180
    return omega

def make_grid(farray, lmax):
    # Make a sht grid from array data and expand in sh up to lmax
    fgrid = sht.SHGrid.from_zeros(lmax=lmax, kind='real')
    # fgrid = sht.SHGrid.from_array(farray)
    fgrid.data = farray
    fcilm = fgrid.expand(normalization='4pi')
    fvec = sht.shio.SHCilmToVector(fcilm.coeffs, lmax)

    # Remove small coefficients from errors
    fvec[np.abs(fvec)<1e-6] = 0
    return fgrid, fvec, fcilm

def vecs2grids(cvecs):
    # Convert an array of vectors of SH coefficients to an array of SH grids
    cgridarray = []
    ntimes = cvecs.shape[1]
    for i in range(ntimes):
        cvec = cvecs[:,i]
        cgrid = vec2grid(cvec)
        cgridarray.append(cgrid)
    return cgridarray

def vec2grid(cvec, lmax=32):
    # Convert a vector of SH coefficients to an SH grid
    cilm = sht.shio.SHVectorToCilm(cvec)
    carr = sht.expand.MakeGridDH(cilm, sampling=2, extend=1, norm=1, lmax=lmax) # 4 orthonorm, 1 4pi
    cgrid = sht.shclasses.SHGrid.from_array(carr)
    return cgrid

################################################################################
# Plotting functions
################################################################################

def plot_prob_sphere(grid):
    prob = grid.data
    omega = make_angles(grid.lmax)
    cmap = plt.cm.Blues

    x = np.cos(omega[0]) * np.cos(omega[1])
    y = np.cos(omega[0]) * np.sin(omega[1])
    z = np.sin(omega[0])

    ax = plt.subplot(111, projection='3d')
    ax._axis3don = False
    ax.plot_surface(x, y, z,
                    cstride=1,
                    rstride=1,
                    facecolors=cmap(prob))
    ax.view_init(elev=30, azim=150)
    ax.set_xticks(np.linspace(-1,1,5))
    ax.set_yticks(np.linspace(-1,1,5))
    ax.set_zticks(np.linspace(-1,1,5))
    ax.set_xticklabels([])
    ax.set_yticklabels([])
    ax.set_zticklabels([])
    ax.set_xlabel('X')
    ax.set_ylabel('Y')
    ax.set_zlabel('Z')
    plt.show()
    return

def plot_proj(grid, clims=[0, 1], cmap=plt.cm.Blues):
    prob = grid.data

    if clims == []:
        clims = [np.min(prob), np.max(prob)]

    im1 = plt.imshow(prob, extent=(0, 2, -0.5, 0.5),
                    vmin=clims[0], vmax=clims[1],
                    cmap=cmap, alpha=1)
    plt.xlabel('$\phi$ $(\pi)$')
    plt.ylabel('$\\theta$ $(\pi)$')
    plt.xticks([0, 0.5, 1, 1.5, 2])
    plt.yticks([0.5, 0.25, 0, -0.25, -0.5])
    plt.colorbar(ax=plt.gca())
    return

if __name__ == "__main__":
    a = None
    # from codetiming import Timer

    # rsEGFP2 = NegativeSwitcher(cross_section_on_blue=1e-10,
    #                            lifetime_on=3.6e-9,
    #                            quantum_yield_on_to_off=0.001,
    #                            diffusion_coefficient=1/(6*100e-6) )

    # tau = 100e-6 # us anisotropy decay
    # D = 1/(tau*6) # Hz
    # yield_off = 0.001
    # tau_off = 80e-6 # time constant off switching
    # tau_on_exc = 3.6e-9 # lifetime of excited state
    # k21 = 1 / (tau_off * yield_off)
    # k12 = 1 / tau_on_exc
    # k32 = (1/tau_off) / yield_off

    # lmax = 8
    # omega = make_angles(lmax)
    # k21a = (np.sin(omega[0])**2) * k21
    # k21grid, k21c, k21cilm = make_grid(k21a, lmax)
    # plot_proj(k21grid, clims=[])

    # # Test product using cg coeff
    # l, m = quantum_numbers(lmax)
    # t = Timer()
    # t.start()
    # cgp = clebsch_gordan_prod_3darray(l, m)
    # t.stop()
    # # k21prod = kinetic_prod_block(k21c, cgp)
    # # kp = np.cos(omega[0])**2 * np.cos(omega[1]+np.pi)**2
    # # kpgrid, kpc = make_grid(kp, lmax)
    # # k21kpc = k21prod.dot(kpc)
    # # k21kpcilm = sht.shio.SHVectorToCilm(k21kpc)
    # # k21kparray = sht.expand.MakeGridDH(k21kpcilm, sampling=2)
    # # k21kpgrid = sht.shclasses.SHGrid.from_array(k21kparray)
    # # k21kpgrid.plot3d()

    # # Array with all the diffusion tensors/scalar for every specie.
    # # In this case every specie diffuse with the same rate.
    # Dvec = [D, D, D]

    # # Array with kinetic constants connecting the states.
    # Kmatrix = [[   0, k12, 0],
    #            [k21a,   0, 0],
    #            [   0, k32, 0]]

    # # # Simplified kinetic scheme
    # # # Array with all the diffusion tensors/scalar for every specie.
    # # # In this case every specie diffuse with the same rate.
    # # Dvec = [D, D]

    # # # Array with kinetic constants connecting the states.
    # # Kmatrix = [[   0,   0],
    # #            [k21a,   0]]

    # t = Timer()
    # t.start()
    # M = kinetics_diffusion_matrix(Dvec, Kmatrix, lmax)
    # t.stop()

    # # initial conditions
    # c0a = omega[0]*0 +1
    # c0grid, c0vec, c0cilm = make_grid(c0a, lmax)
    # c0 = np.zeros(((lmax+1)**2,3))
    # c0[:,0] = c0vec
    # # p0_1 = 1 + np.cos(omega[0]) * 0
    # # p0_1grid, c0_1 = make_grid(p0_1, lmax)
    # # c0[:,0] =c0_1

    # time = np.logspace(-11,-3,128)
    # # time = np.linspace(0,1e-3,1000)
    # t.start()
    # c, L, U =solve_evolution(M, c0, time)
    # t.stop()
    # # plt.imshow(np.real(Dblock))

    # Uinv = np.linalg.inv(U)
    # ci = np.matmul(U, np.diag(np.exp(L*100e-6)))
    # ci = np.matmul(ci, Uinv)
    # plt.imshow(np.real(ci))

    # t.start()
    # np.linalg.inv(U)
    # t.stop()

    # cplot = c[:,0,:]
    # cplotgrid = vecs2grids(cplot)

    # plt.figure()
    # plt.plot(time, cplot.T)
    # plt.xscale('log')

    # plot_proj(cplotgrid[100])

    # # cplotgrid[10].plot()
    # # plt.imshow(M)
    # # plt.show()
    # # plt.imshow(cplotgrid[20].data, vmin=0, vmax=1)


import torch
import torchvision
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np

def imshow(img):
    """Функция вывода картинок на экран,
    :param img: тензор изображения"""
    #unnormalize
    img = img / 2 + 0.5
    np_img = img.numpy()
    plt.imshow(np.transpose(np_img, (1, 2, 0)))
    plt.show()

def load_data(batch_size):
    """Функция загрузки и подготовки данных,
    :param batch_size: размер пакета данных"""
    transform = transforms.Compose([
        transforms.ToTensor(),
        transforms.Normalize((0.5, ), (0.5, ))
    ])

    #загрузить картинки из папки, сделать преобразование
    trainset = torchvision.datasets.FashionMNIST(root='./data',
                                            train=True,
                                            download=True,
                                            transform=transform)
    trainloader = torch.utils.data.DataLoader(trainset,
                                              batch_size=batch_size,
                                              shuffle=True,
                                              num_workers=2)
    testset = torchvision.datasets.FashionMNIST(root='./data',
                                            train=False,
                                            download=True,
                                            transform=transform)
    testloader = torch.utils.data.DataLoader(testset,
                                              batch_size=batch_size,
                                              shuffle=False,
                                              num_workers=2)
    classes = ('T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot')
    return trainloader, testloader, classes


import argparse
import time
from pathlib import Path
import numpy as np

import cv2
import torch
import torch.backends.cudnn as cudnn
from numpy import random

from models.experimental import attempt_load
from utils.datasets import LoadStreams, LoadImages
from utils.general import check_img_size, check_requirements, check_imshow, non_max_suppression, apply_classifier, \
    scale_coords, xyxy2xywh, strip_optimizer, set_logging, increment_path, save_one_box
from utils.plots import colors, plot_one_box
from utils.torch_utils import select_device, load_classifier, time_synchronized

from detect import detect
from collect_data import collect_data
from update_conf_score import update_conf_score
from API2.clear_db import clear_db
from API2.upload_img import upload_img
from API2.update_conf import update_conf
from API2.get_size_with_id import get_size_with_id

#source ~/code/parrot-groundsdk/./products/olympe/linux/env/shell
if __name__ == "__main__":
    print('Starting....\n')

    conf_cutoff = 0.6 #for senting the drone
    conf_thres=0.1 #for yolo
    webcam_id = 2
    primary_drone_id = 1 #primary_drone_id for the databasse
    secondary_drone_id = 2 #secondary_drone_id for the databasse
    clear_db(primary_drone_id) #clear the database
    clear_db(secondary_drone_id)
    print('Starting the Webcam')
    cap = cv2.VideoCapture(webcam_id)
    cont = True
    while(cont):
        ret, frame = cap.read()
        cv2.imshow('frame',frame)
        #Run yolo detection
        xyxy_list,label_list,conf_list = detect(frame,conf_thres)
        num_detect = len(label_list)
        print('detected: {} with {}'.format(label_list,conf_list))
        ###############Check if the confidence score is less than conf_cutoff####################
        for i in range(num_detect):
            xyxy = xyxy_list[i]#ndarray
            label = label_list[i]#str
            conf = conf_list[i]#float
            ###############################Need to sent a drone############################3
            if conf<conf_cutoff:
                plot_one_box(xyxy, frame, label=label+' '+ "{:.2f}".format(conf), color=colors(0, True))
                cv2.imshow('frame',frame)
                cv2.waitKey(1)
                # print('Current database size: {}'.format(get_size_with_id(primary_drone_id)))
                save_path = 'frame_dir|primary|tmp_frame.jpg'
                cv2.imwrite(save_path.replace('|','/'), frame)
                upload_img(save_path,
                            primary_drone_id,
                            xyxy[0],
                            xyxy[1],
                            xyxy[2],
                            xyxy[3],
                            label,
                            conf)
                # print("Database size after upload: {}".format(get_size_with_id(primary_drone_id)))
                #sent_drone
                print("Sending the Drone")
                collect_data(secondary_drone_id)
                #update_conf_score
                new_conf,new_label = update_conf_score(save_path,conf_thres)

                if new_conf != 0 and new_conf>conf:
                    # plot_one_box(, frame, label=label+' '+ "{:.2f}".format(conf), color=colors(0, True))
                    plot_one_box(xyxy, frame, label=new_label+' '+ "{:.2f}".format(new_conf), color=colors(0, True))
                    cv2.imshow('frame',frame)
                    cv2.waitKey(1)
                    # cv2.imshow('frame',frame)
                    # print('Old:{} with {}  New:{} with {}'.format(label,conf,new_label,new_conf))
                    print('New Confidence Score Been Updated')
                else:
                    # plot_one_box(xyxy, frame, label=label+' '+ "{:.2f}".format(conf), color=colors(0, True))

                    print('False Detection')
                cont = False;

            else:
                plot_one_box(xyxy, frame, label=label+' '+ "{:.2f}".format(conf), color=colors(0, True))
                cv2.imshow('frame',frame)
                # cv2.waitKey(1)

#############################################Press Q to quit###############################################
        if cv2.waitKey(1) & 0xFF == ord('q'):
            print('Closing the Webcam')
            cv2.destroyAllWindows()
            break

    print('FINISH')


"""
Note: this test file was heavily influenced by its dbplyr counterpart.

https://github.com/tidyverse/dbplyr/blob/master/tests/testthat/test-verb-group_by.R
"""

from siuba import (
        _, group_by,
        join, inner_join, left_join, right_join, full_join,
        semi_join, anti_join
        )
from siuba.dply.vector import row_number, n
from siuba.sql.verbs import collect

import pytest
from .helpers import assert_equal_query, assert_frame_sort_equal, data_frame, backend_notimpl, backend_sql

DF1 = data_frame(
        ii = [1,2,3,4],
        x = ["a", "b", "c", "d"]
        )

DF2 = data_frame(
        ii = [1,2,26],
        y = ["a", "b", "z"]
        )

DF3 = data_frame(
        ii = [26],
        z = ["z"]
        )

@pytest.fixture(scope = "module")
def df1(backend):
    return backend.load_df(DF1)

@pytest.fixture(scope = "module")
def df2(backend):
    return backend.load_df(DF2)

@pytest.fixture(scope = "module")
def df2_jj(backend):
    return backend.load_df(DF2.rename(columns = {"ii": "jj"}))

@pytest.fixture(scope = "module")
def df3(backend):
    return backend.load_df(DF3)

@backend_sql("TODO: pandas")
def test_join_diff_vars_keeps_left(backend, df1, df2_jj):
    out = inner_join(df1, df2_jj, {"ii": "jj"}) >> collect()

    assert out.columns.tolist() == ["ii", "x", "y"]

def test_join_on_str_arg(df1, df2):
    out = inner_join(df1, df2, "ii") >> collect()

    target = DF1.iloc[:2,].assign(y = ["a", "b"])
    assert_frame_sort_equal(out, target)

def test_join_on_list_arg(backend):
    # TODO: how to validate how cols are being matched up?
    data = DF1.assign(jj = lambda d: d.ii)
    df_a = backend.load_df(data)
    df_b = backend.load_df(DF2.assign(jj = lambda d: d.ii))
    out = inner_join(df_a, df_b, ["ii", "jj"]) >> collect()

    assert_frame_sort_equal(out, data.iloc[:2, :].assign(y = ["a", "b"]))

@pytest.mark.skip("TODO: note, unsure of this syntax")
def test_join_on_same_col_multiple_times():
    data = data_frame(ii = [1,2,3], jj = [1,2, 9])
    df_a = backend.load_df(data)
    df_b = backend.load_df(data_frame(ii = [1,2,3]))

    out = inner_join(df_a, df_b, {("ii", "jj"): "ii"}) >> collect()
    # keeps all but last row
    assert_frame_sort_equal(out, data.iloc[:2,])

def test_join_on_missing_col(df1, df2):
    with pytest.raises(KeyError):
        inner_join(df1, df2, {"ABCDEF": "ii"})

    with pytest.raises(KeyError):
        inner_join(df1, df2, {"ii": "ABCDEF"})

def test_join_suffixes_dupe_names(df1):
    out = inner_join(df1, df1, {"ii": "ii"}) >> collect()
    non_index_cols = DF1.columns[DF1.columns != "ii"]
    assert all((non_index_cols + "_x").isin(out))
    assert all((non_index_cols + "_y").isin(out))

# Test basic join types -------------------------------------------------------

def test_basic_left_join(df1, df2):
    out = left_join(df1, df2, {"ii": "ii"}) >> collect()
    target = DF1.assign(y = ["a", "b", None, None])
    assert_frame_sort_equal(out, target)

@backend_sql("TODO: pandas returns columns in rev name order")
def test_basic_right_join(backend, df1, df2):
    # same as left join, but flip df arguments
    out = right_join(df2, df1, {"ii": "ii"}) >> collect()
    target = DF1.assign(y = ["a", "b", None, None])
    assert_frame_sort_equal(out, target)

def test_basic_inner_join(df1, df2):
    out = inner_join(df1, df2, {"ii": "ii"}) >> collect()
    target = DF1.iloc[:2,:].assign(y = ["a", "b"])
    assert_frame_sort_equal(out, target)

@pytest.mark.skip_backend("sqlite")
def test_basic_full_join(skip_backend, backend, df1, df2):
    out = full_join(df1, df2, {"ii": "ii"}) >> collect()
    target = DF1.merge(DF2, on = "ii", how = "outer")
    assert_frame_sort_equal(out, target)

def test_basic_semi_join(backend, df1, df2):
    assert_frame_sort_equal(
            semi_join(df1, df2, {"ii": "ii"}) >> collect(),
            DF1.iloc[:2,]
            )

def test_semi_join_no_cross(backend, df1, df2):
    df_ii = backend.load_df(data_frame(ii = [1,1]))
    assert_frame_sort_equal(
            semi_join(df1, df_ii, {"ii": "ii"}) >> collect(),
            DF1.iloc[:1,]
            )

def test_basic_anti_join(backend, df1, df2):
    assert_frame_sort_equal(
            anti_join(df1, df2, on = {"ii": "ii"}) >> collect(),
            DF1.iloc[2:,]
            )

def test_basic_anti_join(backend, df1, df2):
    assert_frame_sort_equal(
            anti_join(df1, df2, on = {"ii": "ii"}) >> collect(),
            DF1.iloc[2:,]
            )

def test_basic_anti_join(backend, df1, df2):
    assert_frame_sort_equal(
            anti_join(df1, df2, on = {"ii": "ii", "x": "y"}) >> collect(),
            DF1.iloc[2:,]
            )


import csv
from multiprocessing import Process

from pymongo import MongoClient

client = MongoClient("localhost", 27017)

my_test = client['my_test']

def first():
    busan = my_test['busan']
    seoul = my_test['seoul']
    f = open('./prev_csv_data/상가업소정보_202012_1.csv', 'r', encoding='cp949')
    rdr = csv.reader(f)

    current_count = 0
    for line in rdr:
        current_count += 1
        print(current_count)
        rdnmAdr = line[31]
        ctprvnNm = line[12]
        lon = line[37]
        lat = line[38]

        store = {
            "address": rdnmAdr,
            "lon": lon,
            "lat": lat
        }
        print(store)
        if ctprvnNm == "부산광역시":
            busan_id = busan.insert_one(store).inserted_id
        elif ctprvnNm == "서울특별시":
            seoul_id = seoul.insert_one(store).inserted_id

def second():
    daegu = my_test['daegu']
    incheon = my_test['incheon']
    gwangju = my_test['gwangju']
    daejeon = my_test['daejeon']
    ulsan = my_test['ulsan']
    sejong = my_test['sejong']
    f = open('./prev_csv_data/상가업소정보_202012_2.csv', 'r', encoding='cp949')
    rdr = csv.reader(f)

    current_count = 0
    for line in rdr:
        current_count += 1
        print(current_count)
        rdnmAdr = line[31]
        ctprvnNm = line[12]
        lon = line[37]
        lat = line[38]

        store = {
            "address": rdnmAdr,
            "lon": lon,
            "lat": lat
        }
        print(store)
        if ctprvnNm == "대구광역시":
            daegu_id = daegu.insert_one(store).inserted_id
        elif ctprvnNm == "인천광역시":
            incheon_id = incheon.insert_one(store).inserted_id
        elif ctprvnNm == "광주광역시":
            gwangju_id = gwangju.insert_one(store).inserted_id
        elif ctprvnNm == "대전광역시":
            daejeon_id = daejeon.insert_one(store).inserted_id
        elif ctprvnNm == "울산광역시":
            ulsan_id = ulsan.insert_one(store).inserted_id
        elif ctprvnNm == "세종특별자치시":
            sejong_id = sejong.insert_one(store).inserted_id

def third():
    gyeonggi = my_test['gyeonggi']
    gwangwon = my_test['gwangwon']
    chungbuk = my_test['chungbuk']

    f = open('./prev_csv_data/상가업소정보_202012_3.csv', 'r', encoding='cp949')
    rdr = csv.reader(f)

    current_count = 0
    for line in rdr:
        current_count += 1
        print(current_count)
        rdnmAdr = line[31]
        ctprvnNm = line[12]
        lon = line[37]
        lat = line[38]

        store = {
            "address": rdnmAdr,
            "lon": lon,
            "lat": lat
        }
        print(store)
        if ctprvnNm == "경기도":
            gyeonggi_id = gyeonggi.insert_one(store).inserted_id
        elif ctprvnNm == "강원도":
            gwangwon_id = gwangwon.insert_one(store).inserted_id
        elif ctprvnNm == "충청북도":
            chungbuk_id = chungbuk.insert_one(store).inserted_id

def four():
    chungnam = my_test['chungnam']
    jeonbuk = my_test['jeonbuk']
    jeonnam = my_test['jeonnam']
    gyeongbuk = my_test['gyeongbuk']
    gyeongnam = my_test['gyeongnam']
    jeju = my_test['jeju']
    f = open('./prev_csv_data/상가업소정보_202012_4.csv', 'r', encoding='cp949')
    rdr = csv.reader(f)

    current_count = 0
    for line in rdr:
        current_count += 1
        print(current_count)
        rdnmAdr = line[31]
        ctprvnNm = line[12]
        lon = line[37]
        lat = line[38]

        store = {
            "address": rdnmAdr,
            "lon": lon,
            "lat": lat
        }
        print(store)
        if ctprvnNm == "충청남도":
            daegu_id = chungnam.insert_one(store).inserted_id
        elif ctprvnNm == "전라북도":
            incheon_id = jeonbuk.insert_one(store).inserted_id
        elif ctprvnNm == "전라남도":
            gwangju_id = jeonnam.insert_one(store).inserted_id
        elif ctprvnNm == "경상북도":
            daejeon_id = gyeongbuk.insert_one(store).inserted_id
        elif ctprvnNm == "경상남도":
            ulsan_id = gyeongnam.insert_one(store).inserted_id
        elif ctprvnNm == "제주특별자치도":
            sejong_id = jeju.insert_one(store).inserted_id

if __name__ == "__main__":
    p1 = Process(target=second)
    p2 = Process(target=third)
    p3 = Process(target=four)
    p4 = Process(target=first)

    p1.start()
    p2.start()
    p3.start()
    p4.start()

    p1.join()
    p2.join()
    p3.join()
    p4.join()


from django.conf.urls import include, url
from django.contrib import admin
from HN import views as HN_Views
urlpatterns = [
    ##url(r'^$|Comments|Points|Author|Time|Title|Domain',HN_Views.db_sort),
    url(r'^admin/',include(admin.site.urls)),

    url(r'new',HN_Views.get_new_content),
    url(r'history',HN_Views.history),
    url(r'suggested',HN_Views.similar),
    url(r'about',HN_Views.about),
    url(r'feedback',HN_Views.feedback),
    url(r'del',HN_Views.delkeep),

	url(r'^$',HN_Views.db_index),
    url(r'(?P<item>\w+)/(?P<item2>\d{1,})$',HN_Views.db_write),
    url(r'(?P<id>\w+)$',HN_Views.db_index),

    ]


import math
import sys
from itertools import permutations
from collections import deque

input = sys.stdin.readline

# 13417
for _ in range(int(input())):
    N = int(input())
    li = list(map(str, input().split()))
    # 좌 우로 넣었을때 사전순으로 가장 빠른것
    ans = ''
    for i in li:
        if len(ans) == 0:
            ans = i
        elif ord(ans[0]) < ord(i):
            ans += i
        else:
            ans = i + ans
    print(ans)


#############################################################################
## This is the script for concatenating (joining) overlapping partial ORFs ##
#############################################################################

import sys
from Bio import SeqIO
import os

NONCOMPLETE = sys.argv[1]

inputfile = sys.argv[2]

outputfile = sys.argv[3]

with open(inputfile) as f:
    a = f.readlines()

a = map(lambda x: x.strip().split(), a)

sw = {}
handle = open(NONCOMPLETE, "rU")
for record in SeqIO.parse(handle, "fasta"):
    sw[record.id] = record.seq.tostring()
handle.close()

joined_guys = {}

for x, y, z in a:
    id1, id2 = x[1:], y[1:]
    try:
        seq1, seq2 = sw[id1], sw[id2]
    except Exception:
        continue
    overlap = int(z)
    newseq = seq1 + seq2[overlap:]
    joined_guys[id1 + "___" + id2] = newseq

# write fasta file
with open(outputfile, "w") as f:
    for seq in joined_guys:
        f.write(">%s\n" % seq)
        f.write("%s\n" % joined_guys[seq])


# -*- coding: utf-8 -*-
from pyparsing import *
import re

def parseResultsCmp(pr1, pr2):
  """
  Compare two pyparsing ParseResults.  Necessary because pyparsing does not
  define equality on this type.
  >>> parseResultsCmp(ParseResults([],{}), ParseResults([],{}))
  0
  >>> parseResultsCmp(ParseResults([1,2,3], {}), ParseResults([], {}))
  1
  >>> parseResultsCmp(ParseResults([1,2,3], {}), ParseResults([1,2,3], {}))
  0
  """
  if (cmp(pr1.asList(), pr2.asList()) == 0):
    return cmp(pr1.asDict(), pr2.asDict())
  else:
    return cmp(pr1.asList(), pr2.asList())

def replaceEntitiesInStr(text):
  # + is inserted for in-word line breaks, remove it
  text = re.sub(r'\+', '', text)
  # Fix lonely ampersands
  text = re.sub(r'(\s)\&(\s)', r'\1&\2', text)
  # Fix &c (etc)
  text = text.replace('&c.', '&c.')
  # Fix <= and >=
  text = text.replace('<=', '<=')
  text = text.replace('>=', '>=')
  text = re.sub(r'<(\s*)(\d)', r'<\1\2', text)
  text = re.sub(r'(\s+)>(\s*)(\d)', r'\1>\2\3', text)

  # Replace non-XML entities
  text = text.replace('£', u'\u00a3')
  text = text.replace('&pounds;', u'\u00a3')
  text = text.replace('®', u'\u00ae')
  text = text.replace('&degree;', u'\u00b0')
  text = text.replace('&degrees;', u'\u00b0')
  text = text.replace('°', u'\u00b0')
  text = text.replace('¶', u'\u00b6')
  text = text.replace('•', u'\2022')
  text = text.replace('⊃', u'\u2283')

  text = text.replace('Α', u'\u0391')
  text = text.replace('Β', u'\u0392')
  text = text.replace('Γ', u'\u0393')
  text = text.replace('Δ', u'\u0394')
  text = text.replace('Ε', u'\u0395')
  text = text.replace('Ζ', u'\u0396')
  text = text.replace('Η', u'\u0397')
  text = text.replace('&Rheta;', u'\u0398')
  text = text.replace('Ι', u'\u0399')
  text = text.replace('Κ', u'\u039a')
  text = text.replace('Λ', u'\u039b')
  text = text.replace('Μ', u'\u039c')
  text = text.replace('Ν', u'\u039d')
  text = text.replace('Ξ', u'\u039e')
  text = text.replace('Ο', u'\u039f')
  text = text.replace('Π', u'\u03a0')
  text = text.replace('Ρ', u'\u03a1')
  text = text.replace('&Sigmaf;', u'\u03a2')
  text = text.replace('Σ', u'\u03a3')
  text = text.replace('Τ', u'\u03a4')
  text = text.replace('Υ', u'\u03a5')
  text = text.replace('Φ', u'\u03a6')
  text = text.replace('Χ', u'\u03a7')
  text = text.replace('Ψ', u'\u03a8')
  text = text.replace('Ω', u'\u03a9')

  text = text.replace('α', u'\u03b1')
  text = text.replace('β', u'\u03b2')
  text = text.replace('γ', u'\u03b3')
  text = text.replace('δ', u'\u03b4')
  text = text.replace('ε', u'\u03b5')
  text = text.replace('ζ', u'\u03b6')
  text = text.replace('η', u'\u03b7')
  text = text.replace('θ', u'\u03b8')
  text = text.replace('ι', u'\u03b9')
  text = text.replace('κ', u'\u03ba')
  text = text.replace('λ', u'\u03bb')
  text = text.replace('μ', u'\u03bc')
  text = text.replace('ν', u'\u03bd')
  text = text.replace('ξ', u'\u03be')
  text = text.replace('ο', u'\u03bf')
  text = text.replace('π', u'\u03c0')
  text = text.replace('ρ', u'\u03c1')
  text = text.replace('ς', u'\u03c2')
  text = text.replace('σ', u'\u03c3')
  text = text.replace('τ', u'\u03c4')
  text = text.replace('υ', u'\u03c5')
  text = text.replace('φ', u'\u03c6')
  text = text.replace('χ', u'\u03c7')
  text = text.replace('ψ', u'\u03c8')
  text = text.replace('ω', u'\u03c9')

  text = text.replace('&formula;', '?')
  text = text.replace('&symbol;', '?')
  text = text.replace('&SYMBOL;', '?')

  return text

def putLTBack(text):
  text = text.replace('<', '<')
  return text

## parsing tables
def tableParser(cols):
  """
  This function will parse a table in the format that antiword gives us from the Monash Word files.  Tables are
  pipe delimited and there are no row markers.  We assume each new line is a row in this function and arrayToMeta
  takes care of working out when this was not the right assumption
  """
  delim = Suppress(Literal(u"|"))
  cellData = CharsNotIn(u"|\n").leaveWhitespace().setParseAction(lambda x: x[0].strip())
  rowParser = delim
  for i in range(0,cols):
    rowParser = rowParser + cellData + delim
  return OneOrMore(rowParser).setParseAction(lambda x, loc, toks: arrayToMeta(fillArray(toks,cols)))

def fillArray(onedarr, cols):
  """
  Converts a one dimensional array into a two dimensional array based on the number
  of colums given
  """
  twodarr = []
  for i in range(0, len(onedarr)/cols):
    twodarr.append(onedarr[i*cols:(i+1)*cols])
  return twodarr

def arrayToMeta(data):
  """
  Takes a two dimensional array of strings and converts them to
  key value pairs according to the following rules:
    * the outer/first dimension is columns
    * odd numbered colums are keys, even numbered columns are values
    * a key or a value can span two lines/rows in which case it should
      be appended to the row above.
    * no such value should ever be in the first column, so a blank element in
      the first column is an indicator that the values in this column should
      be appended to those above it.  "INITIALS:" is an exception to this rule
  >>> arrayToMeta([['a','aval', 'b', 'bval']])
  {'a': 'aval', 'b': 'bval'}
  >>> arrayToMeta([['a','aval', 'b', 'bval'], ['', 'more a', '', 'more b'], ['c', 'cval', 'd', 'dval'], ['', '', 'd is complicated', 'as is its val']])
  {'a': 'aval more a', 'c': 'cval', 'b': 'bval more b', 'd d is complicated': 'dval as is its val'}
  """
  def arrConcat(a1, a2):
    res = a1
    splitter = ''
    for i in range(0, len(a1)):
      if a2[i] != "": splitter = " "
      else:           splitter = ""
      res[i] = res[i] + splitter +  a2[i]
    return res

  # flatten cells that are over two rows
  flatData = []
  lastGoodRow = 0
  for ri, row in enumerate(data):
    if ((data[ri][0] == "") | (data[ri][0]=="INITIALS:")):
      extraRow = True
    else:
      extraRow    = False

    if (not extraRow):
      flatData.append(data[ri])
      lastGoodRow = len(flatData) - 1
    else:
      flatData[lastGoodRow] = arrConcat(flatData[lastGoodRow], data[ri])

  # go through the flattened data and create a dictionary
  res = {}
  for row in flatData:
    for ci in range(0, len(row), 2):
      res[row[ci]] = row[ci+1]

  return res

def toXMLName(ustr):
    """
    convert given unicode string to a valid XML Name.
    see http://www.w3.org/TR/REC-xml/#NT-CombiningChar

    The resulting string is constructed by removing all illegal characters from the input string.

    >>> toXMLName(u'This will be reduced to (x)')
    u'Thiswillbereducedtox'
    >>> toXMLName('Only Unicode as input')
    Traceback (most recent call last):
      ...
    TypeError: must be unicode, not str

    There must be something left at the end.
    >>> toXMLName(u'&&((-))')
    Traceback (most recent call last):
      ...
    Exception: can't convert &&((-)) to xmlname
    >>> toXMLName(u'&&((_-))')
    u'_-'
    """
    from unicodedata import category, decomposition
    NAME_START_CATEGORIES = ["Ll", "Lu", "Lo", "Lt", "Nl"]
    NAME_CATEGORIES = NAME_START_CATEGORIES + ["Mc", "Me", "Mn", "Lm", "Nd"]
    ALLOWED_NAME_START_CHARS = [u"_"] # u":" can't be part of NCNames
    ALLOWED_NAME_CHARS = ALLOWED_NAME_START_CHARS + [u"\u00B7", u"\u0387", u"-", u"."]

    ret = []
    start = True
    for c in ustr:
        if start:
            if not category(c) in NAME_START_CATEGORIES and c not in ALLOWED_NAME_START_CHARS:
                continue
            start = False
        else:
            if not category(c) in NAME_CATEGORIES and c not in ALLOWED_NAME_CHARS:
                continue
        ret.append(c)
    if not ret:
        raise Exception("can't convert %s to xmlname" % ustr)
    return ''.join(ret)

def add_to_dictionary(key, dict1, dict2):
    '''
    This method adds a dictionary to an existing dictionary. Overlapping keys in the second dictionary are prioritised over those
    in the first dictionary
     '''
    if key in dict1:
      if type(dict1[key]) == type({}):
        dict1[key] = merge_dictionaries(dict1[key], dict2)
        return dict1
      else:
        return merge_dictionaries(dict1, dict2)
    else:
      dict1[key] = dict2
      return dict1

def merge_dictionaries(dict1, dict2):
    '''
    This method merges two dictionaries, if the two dictionaries contain similar keys, it picks the key with the non empty
    value, simply updating dictionaries using the update method is insufficient
    '''
    new_dict = {}

    for key, value in dict1.iteritems():

      # If the key does not exist in the second dictionary then just add this one to the new dictionary
      # If there is a value pick the non empty one and delete the key from the old
      if (dict2.get(key) == None) or (dict2.get(key) == ""):
        new_dict[key] = value
      else:
          new_dict[key] = dict2[key]

      # After this is done delete the item from the second dictionary
      if dict2.get(key) != None:
        del dict2[key]

    # After all this is done just merge the remaining items
    new_dict.update(dict2)
    return new_dict


from celery import Celery, Task, current_app

import base64
import io
from PIL import Image, ImageDraw, ImageFont
import textwrap
import json
import requests
from celery.contrib import rdb

app = Celery('tasks', CELERY_RESULT_BACKEND="rpc://", broker='amqp://')

addr = 'http://127.0.0.1:5000'
result_url = addr + '/result'

# prepare headers for http request
content_type = 'image/jpg'
headers = {'content-type': content_type}

def check_text_size_fit(text_desc, text_size, font_family, rect_width, rect_height):
    """ Check if the text will fit inside the rectangle """
    #Create text font
    text_font = ImageFont.truetype(font_family, size=text_size)

    #Wrap text based on width of the rectangle
    avg_char_width = sum(text_font.getsize(char)[0] for char in text_desc) / len(text_desc)
    max_char_count = int((rect_width) / avg_char_width)

    if max_char_count < 1:
        return 0

    wrapped_text = textwrap.fill(text=text_desc, width=max_char_count)
    text_lines_num = wrapped_text.count('\n')+1

    #Text fits inside rectangle
    if( text_size*text_lines_num <= rect_height):
        return 1

def image_rectangle_text(img, text, coords, position, text_color, text_size, font_family):
    draw = ImageDraw.Draw(img)

    #Grab text and rectangle info
    text_description = text
    rectangle = coords

    #Get rectangle width and height
    rect_width = rectangle[2]-rectangle[0]
    rect_height = rectangle[3]-rectangle[1]

    #Check if text fits rectangle, lower text size if it doesn't
    text_size_fits = False
    while(not(text_size_fits)):
        try:
            result = check_text_size_fit(text_description,text_size,font_family,rect_width,rect_height)
        except Exception as e:
            print(str(e))
            result = 0
        if( result != 1):
            text_size -= 1
        else:
            text_size_fits = True

    #Create text font
    text_font = ImageFont.truetype(font_family, size=text_size)

    #Split text if there are newlines
    text_description = text_description.split('\n')

    line_pos = 0    #Keep track of line position

    #Go through each text segment
    for text_desc in text_description:
        #Wrap text based on width of the rectangle
        print(text_font.getsize(text_desc[0]))
        avg_char_width = text_font.getsize(text_desc)[0] / len(text_desc)
        max_char_count = int((rect_width) / avg_char_width)
        wrapped_text = textwrap.fill(text=text_desc, width=max_char_count)
        text_lines_num = wrapped_text.count('\n')+1

        #Get the width of text box
        text_width = text_font.getsize(wrapped_text.split('\n')[0])[0]

        #Draw Rectangle for Debugging Only
        # draw.rectangle([(rectangle[0],rectangle[1]),(rectangle[2],rectangle[3])],outline="red") # Uncomment to see rectangle

        #Draw the text based on position
        if position == "top-left":
            draw.text((rectangle[0], rectangle[1]+(text_size*line_pos)), wrapped_text, fill =text_color, font=text_font)
        if position == "top-center":
            draw.text((rectangle[0]+int(rect_width/2)-int(text_width/2), rectangle[1]+(text_size*line_pos)), wrapped_text, fill =text_color, font=text_font)
        if position == "top-right":
            draw.text((rectangle[2]-text_width, rectangle[1]+(text_size*line_pos)), wrapped_text, fill =text_color, font=text_font)

        if position == "center-left":
            draw.text((rectangle[0], rectangle[1]+int(rect_height/2)-int(text_size*text_lines_num/2)+(text_size*line_pos)), wrapped_text, fill =text_color, font=text_font)
        if position == "center":
            draw.text((rectangle[0]+int(rect_width/2)-int(text_width/2), rectangle[1]+int(rect_height/2)-int(text_size*text_lines_num/2)+(text_size*line_pos)), wrapped_text, fill=text_color, font=text_font)
        if position == "center-right":
            draw.text((rectangle[2]-text_width, rectangle[1]+int(rect_height/2)-int(text_size*text_lines_num/2)+(text_size*line_pos)), wrapped_text, fill =text_color, font=text_font)

        if position == "below-left":
            draw.text(((rectangle[0], rectangle[3]-(text_size*text_lines_num)+(text_size*line_pos))), wrapped_text, fill =text_color, font=text_font)
        if position == "below-center":
            draw.text((rectangle[0]+int(rect_width/2)-int(text_width/2), rectangle[3]-(text_size*text_lines_num)+(text_size*line_pos)), wrapped_text, fill =text_color, font=text_font)
        if position == "below-right":
            draw.text((rectangle[2]-text_width, rectangle[3]-(text_size*text_lines_num)+(text_size*line_pos)), wrapped_text, fill =text_color, font=text_font)

        line_pos+=text_lines_num

    return img

@app.task
def process_image_text(image_string, text_and_boxes):
    processed_image_str = ""
    try:
        image = io.BytesIO(base64.b64decode(image_string.encode('utf-8')))
        img = Image.open(image)

        for textbox in text_and_boxes:
            text = textbox['text']
            coords = textbox['coords']
            position = textbox['position']
            color = tuple(textbox['color'])
            text_size = textbox['text_size']
            font = textbox['font']
            img = image_rectangle_text(img, text, coords, position, tuple(color), text_size, font)

        byte_io = io.BytesIO()
        img.save(byte_io,'PNG')
        processed_image_str = base64.b64encode(byte_io.getvalue()).decode('utf-8')

    except Exception as e:
        print(str(e))

    # send http request with image and receive response
    requests.post(result_url, data=processed_image_str, headers=headers)
    return 1


print ("""
  ___    _/ |~
  \\ ,`--' ooo'---+
   \^            |
 ~~~~   -- '' -'-'""")

import random
board = []

for x in range(0,5):
  board.append(["o"] * 5)

def print_board(board):
  for row in board:
    print (" ".join(row))

print("amiral battıya hoşgeldin")
print("2 kişilik")

player_1 = input("birinci oyuncu adı: ")
player_2 = input("ikinci oyuncu adı: ")
players = [player_1, player_2]

def random_player(players):
    return random.choice(players)

def random_row(board):
  return random.randint(0,len(board)-1)

def random_col(board):
  return random.randint(0,len(board[0])-1)

if random_player(players) == player_1:
  print(player_1, "başla")
else:
  print(player_2, "başla")

ship_row_1 = random_row(board)
ship_col_1 = random_col(board)

ship_row_2 = random_row(board)
ship_col_2 = random_col(board)

print_board(board)

player_start = random_player(players)

hit_count = 0

for turn in range(4):
     guess_row = int(input("birinci tahmin (izin verilen değerler: 0-4) "))
     guess_col = int(input("ikinci tahmin(izin verilen değerler: 0-4) "))

     if (guess_row == ship_row_1 and guess_col == ship_col_1) or (guess_row == ship_row_2 and guess_col == ship_col_2):
            hit_count = hit_count + 1
            board[guess_row][guess_col] = "*"
            print ("tebrikler")
            if hit_count == 1:
                   print("savaş gemisini vurdun")
            elif hit_count == 2:
                   print("ikinci savaş gemisini vurdun")
                   print_board(board)
                   break
     else:
            if(guess_row<=4 or guess_col<=0):
              if(board[guess_row][guess_col] == "X"):
                   print ("tahmin ettin.")
              else:
                 print ("savaşı kaçırdın")
                 board[guess_row][guess_col] = "X"
            else:
              print ("KAPTAAAN dağa taşa ateş ediyorsun.")
            print (turn + 1, "turn")
     print_board(board)

print ("birinci kişi")
print (ship_row_1)
print (ship_col_1)

print ("ikinci kişi")
print (ship_row_2)
print (ship_col_2)


import numpy as np
from numpy.core.fromnumeric import transpose

# 1
x = np.array(range(100))
y = np.array([range(711,811), range(100)])
print(x.shape) # (5, 100)
print(y.shape) # (2, 100)

x = np.transpose(x)
y = np.transpose(y)

from sklearn.model_selection import train_test_split
x_train, x_test, y_train, y_test = train_test_split(
    x, y, shuffle=True, train_size=0.8, random_state=66
)

print(x_train.shape)
print(y_train.shape)