import torch
import torch.nn as nn
import Core.Constants as Constants
from typing import Optional
from Core.Models import TrfEncoder, SynTrfEncoder, MultiEncDecoder

class MultiEncTransformer(nn.Module):
    """ A sequence to sequence model with attention mechanism. """

    def __init__(self,
                 n_txt_token: int,
                 n_syn_token: int,
                 n_lvl_token: int,
                 max_syn_len: int,
                 max_txt_len: int,
                 d_model: Optional[int] = 256,
                 d_inner: Optional[int] = 1024,
                 n_trf_txt_enc_layer: Optional[int] = 6,
                 n_trf_syn_enc_layer: Optional[int] = 6,
                 n_trf_dec_layer: Optional[int] = 6,
                 n_txt_attn_head: Optional[int] = 5,
                 n_syn_attn_head: Optional[int] = 3,
                 d_k: Optional[int] = 64,
                 d_v: Optional[int] = 64,
                 dropout: Optional[int] = 0.1,
                 tgt_emb_prj_weight_sharing: Optional[bool] = True,
                 txt_token_emb: Optional[torch.Tensor] = None,
                 syn_token_emb: Optional[torch.Tensor] = None,
                 lvl_token_emb: Optional[torch.Tensor] = None
                 ):

        n_head = n_txt_attn_head + n_syn_attn_head

        super().__init__()

        # build embeddings
        if txt_token_emb is None:
            self.txt_token_emb = nn.Embedding(
                n_txt_token, d_model, padding_idx=Constants.PAD
            )
        else:
            self.txt_token_emb = txt_token_emb

        if syn_token_emb is None:
            self.syn_token_emb = nn.Embedding(
                n_syn_token, d_model, padding_idx=Constants.PAD
            )
        else:
            self.syn_token_emb = syn_token_emb

        if lvl_token_emb is None:
            self.lvl_token_emb = nn.Embedding(
                n_lvl_token, d_model, padding_idx=Constants.PAD
            )
        else:
            self.lvl_token_emb = lvl_token_emb

        self.syn_encoder = SynTrfEncoder(
            token_emb=self.syn_token_emb,
            lvl_emb=self.lvl_token_emb,
            max_seq_len=max_syn_len,
            d_model=d_model,
            d_inner=d_inner,
            n_layer=n_trf_syn_enc_layer,
            n_head=n_head,
            d_k=d_k,
            d_v=d_v,
            dropout=dropout
        )

        self.txt_encoder = TrfEncoder(
            token_emb=self.txt_token_emb,
            max_seq_len=max_txt_len,
            d_model=d_model,
            d_inner=d_inner,
            n_layer=n_trf_txt_enc_layer,
            n_head=n_head,
            d_k=d_k,
            d_v=d_v,
            dropout=dropout
        )

        self.multi_enc_decoder = MultiEncDecoder(
            max_seq_len=max_txt_len,
            token_emb=self.txt_token_emb,
            d_model=d_model,
            d_inner=d_inner,
            n_layer=n_trf_dec_layer,
            n_txt_attn_head=n_txt_attn_head,
            n_syn_attn_head=n_syn_attn_head,
            d_k=d_k,
            d_v=d_v,
            dropout=dropout
        )

        self.tgt_word_prj = nn.Linear(d_model, n_txt_token, bias=False)
        nn.init.xavier_normal_(self.tgt_word_prj.weight)
        if tgt_emb_prj_weight_sharing:
            # Share the weight matrix between target word embedding & the final logit dense layer
            self.tgt_word_prj.weight = self.txt_token_emb.weight
            self.x_logit_scale = (d_model ** -0.5)
        else:
            self.x_logit_scale = 1.

    def forward(self,
                src_txt_seqs,
                syn_seqs,
                lvl_seqs,
                tgt_txt_seqs,
                src_pos_seqs,
                tgt_pos_seqs,
                syn_pos_seqs,
                path_mask
                ):

        tgt_txt_seqs = tgt_txt_seqs[:, :-1]
        tgt_pos_seqs = tgt_pos_seqs[:, :-1]

        syn_enc_output, syn_enc_attn = self.syn_encoder(
            syn_seq=syn_seqs,
            lvl_seq=lvl_seqs,
            pos_seq=syn_pos_seqs,
            path_mask=path_mask
        )

        txt_enc_output, txt_enc_attn = self.txt_encoder(
            token_seq=src_txt_seqs,
            pos_seq=src_pos_seqs
        )

        multi_enc_decoder_output, (dec_slf_attn, dec_txt_attn, dec_syn_attn) = self.multi_enc_decoder(
            tgt_seq=tgt_txt_seqs,
            tgt_pos_seq=tgt_pos_seqs,
            src_txt_seq=src_txt_seqs,
            src_syn_seq=syn_seqs,
            txt_enc_output=txt_enc_output,
            syn_enc_output=syn_enc_output,
        )

        txt_logit = self.tgt_word_prj(multi_enc_decoder_output) * self.x_logit_scale

        return txt_logit.view(-1, txt_logit.size(2)), \
            (syn_enc_attn, txt_enc_attn, dec_slf_attn, dec_txt_attn, dec_syn_attn)


"""
Dan Krog
based mostly on
http://www.fem.unicamp.br/~phoenics/SITE_PHOENICS/Apostilas/CFD-1_U%20Michigan_Hong/Lecture06.pdf

NV in Primitive variable Velocity/Pressure

conservations of mass, incompressible
nabla.u = 0

conservation of momentum,

du/dt = -u.nablau + alpha*nabla^2u - nablap / rho
du/dt = advection + diffusion + pressure

MAC grid, Marker-And-Cell
P pressure in the middle, they get the i,j indexs
u is to left and right of P
v is below and above P
So let's say we have a cell, then,

Pij
ui-1/2,j
ui+1/2,j
vi, j+1/2
vi, j-1/2

Oh cool, we actually change the Cell definition, based on which equation we look at
So, for X-momentum equation, the cell is centered on ui+1/2,j

FIRST TRY SIMPLE NO STAGGERED GRID

staggered grid, define, because we can't use fractional indices in the code
#define like this, and then be careful
u(i,j) = u(i+1/2,j)
v(i,j) = v(i,j+1)

#GHost Cells,
the Pressure ghost cells OUTSIDE BOUDNARY are ALWAYS = 0 I think, so no interpolation etc for them
the pressure ON BOUNDARY is not 0 though...

https://www.diva-portal.org/smash/get/diva2:1328776/FULLTEXT01.pdf
Divergence free, so constant density, so no shocks and no sound waves
But, we are still non-linear, so turbulence persists

"""

def PsiSolvePoissonJacobi(psi,zeta,dx,dy,nx,ny,epstol,Ngc):
	"""
	Solve Poisson Equation
	Holton page 465

	psi is streamfunction
	zeta is vorticity

	nabla^2psi = zeta

	where we are solving for psi the streamfunction, and zeta is the relative vorticity

	hmm i think zeta is relative vorticity right, but we actually use another Zeta thing too???

	hmm... zeta er fatisk absolute vorticity
	og relative vorticity er Xi!
	hmm ok...
	Holton bruger faktisk zeta til absolut vorticity....
	Men, nogen steder siges det,
	Spørgsmålet er om poisson skal løses for
	nabla^2psi = xi
	eller
	nabla^2psi = zeta

	fordi, zeta = xi+f
	tror jeg...

	ahh, absolute vorticity i Holton er
	eta = zeta+f
	så zeta ER relative vorticity
	så (11.14) er aboslute vorticity
	og jeg tror indeed at (13.26) på en måde er set ud fra relative vorticity zeta, fordi at f part er sat i F(x,y,t)
	så det er nok fra absolute vorticity zeta+f, men så har man lavet det til en PDE for zeta

	We set BCs at y = top and y = bottom, but not x, i think
	Following lec12.pdf here

	From lec12.pdf,
	Solving poisson equation means putting some boundary condition on psi, right?
	Because otherwise you could add any constant to psi, and it would still be able to give some curvature
	nabla^2psi = f...
	so it makes sense, to give some unique solution, we impose boundary conditions,
	but i think, for the sake of calculating velocity, it also wouldn't matter if we added some constant to psi

	right now i'm doing psi[1:ny-1,1:nx-1]
	#So that means i'm not setting psi[bottom,x] but I AM setting psi[top,x]
	#So i think it should be psi[1:ny-2,1:nx-1]
	#Or maybe, x should also be changed...
	But, remember in Python, 1:ny-1, then 1 is included, but ny-1 is excluded, so in effect, we're doing 1:ny-2

	Solve Poisson Equation,
	nabla^2psi = zeta
	Elliptic PDE
	It is the steady state solution of the pseudo-time problem,
	dpsi/dt = nabla^2psi - zeta

	FTCS = Forward Time, Centered Space

	#psi1[m,n] = psi[m,n]+dt/dx^2 (psi[m-1,n]+psi[m+1,n]+psi[m,n-1]+psi[m,n+1]-4psi[m,n])-dt*zeta[m,n]

	#stability requires, dt <= 1/4 * dx^2

	Jacobi Method is choosing dt = 1/4 * dx^2

	https://www.mi.uni-hamburg.de/arbeitsgruppen/theoretische-meteorologie/personen/lunkeit-frank/numerik/dokumente/barotrop.pdf
	nabla^2Ø - G0 = 0
	1) initial field, eg Ø = 0, which is what I have by psin = np.zeros()
	2) compute error e0 for each grid point... so it be practically an array...

	Should solve Poisson WITH beta plane?
	http://www.math.ualberta.ca/ijnam/Volume-10-2013/No-3-13/2013-03-04.pdf
	nabla^2psi = zeta-beta*y

	Både Holton og Durran de solver for nabla^2psi = zeta
	de solver for RELATIVE vorticity

	Yeah, jeg kan se, forskellige steder bruger forskellig notation.....
	http://empslocal.ex.ac.uk/people/staff/dbs202/cag/courses/MTMW14/notes2006.pdf
	her bruger de Xi til relative vorticity,
	men det er SAMME PDE system overall, det er så nabla^2psi = xi
	så, overall, er det altid nabla^2psi = relative vorticity

	Boundary Conditions
	Lec12, Dirichlet BCs psi = 0
	http://twister.caps.ou.edu/CFD2003/Phillips_NLInstablity.pdf
	at boundaries, psi=0 and xi=0 for all time

	http://empslocal.ex.ac.uk/people/staff/dbs202/cag/courses/MTMW14/notes2006.pdf
	Free-slip BCs at north and south, means v = 0, and du/dy = 0,
	this leads to psi = constant and xi = 0 on north and south.

	#TO DO
	So actually maybe also, we can't JUST do a simple double for loop both both u,v
	because one of them has nx+1 in one direction and the other has ny+1 in one direction etc...
	SO probably must be more careful...

	"""
	#Compute streamfunction from vorticity
	#ζ_(i,j)^1=(((ψ_(i+1,j)-2ψ_(i,j)+ψ_(i-1,j) ))/dx^2 +((ψ_(i,j+1)-2ψ_(i,j)+ψ_(i,j-1) ))/dy^2 )

	#solve psi from lec12.pdf
	psin = np.zeros((ny,nx),dtype=np.float64)
	psin = psi.copy()

	#error betweeen iterated solution and RHS at each gridpoint
	eps = np.zeros((ny,nx),dtype=np.float64)

	#DO I NEED THIS???
	nabla2psi = np.zeros((ny,nx),dtype=np.float64)

	#I should, imo, be explicit and set BCs psi = 0....

	##JACOBI METHOD
	#dtau = 0.5*0.5*(0.5*dx**2+0.5*dy**2)
	nit = 0
	while True:
	#for r in range(500): #pseudo-time

		#update iteration counter
		nit +=1

		#Make copy of current iterate
		#We should probably do a copy, because, otherwise, we are doing intermediate changes...
		#i.e we are changing values that will be used later, that shouldn't....
		#So, that is, to really follow the algorithm, we need to copy the values...
		#At least, most likely... if it was a double for loop, then we would have to....
		#BUT, MAYBE, we don't have to... maybe numpy is smart enough to figure it out
		psin = psi.copy()

		#Calculate intermediate array
		#1/dx**2(psi[1:ny-1,2:nx]+psi[1:ny-1,0:nx-2]-2*psi[1:ny-1,1:nx-1]) +
		#1/dy**2(psi[2:ny,1:nx-1]+psi[0:ny-2,1:nx-1]-2*psi[1:ny-1,1:nx-1])
		nabla2psi[1+Ngc:ny-1-Ngc,Ngc+1:nx-1-Ngc]= ((1/dx**2)*(psi[1+Ngc:ny-1-Ngc,2+Ngc:nx-Ngc]
								+psi[1+Ngc:ny-1-Ngc,0+Ngc:nx-2-Ngc]-2*psi[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc])
								+(1/dy**2)*(psi[2+Ngc:ny-Ngc,1+Ngc:nx-1-Ngc]
								+psi[0+Ngc:ny-2-Ngc,1+Ngc:nx-1-Ngc]-2*psi[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc]))

		#Calculate error
		eps[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc] = nabla2psi[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc]-zeta[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc]

		#Update iteration
		psi[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc] = psin[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc]+eps[1+Ngc:ny-1-Ngc,1+Ngc:nx-1-Ngc]/(2/dx**2+2/dy**2)

		#Interior points
		#psi[1:ny-1,1:nx-1] = psin[1:ny-1,1:nx-1]+dtau*(
		#		+(psin[1:ny-1,2:nx]-2*psin[1:ny-1,1:nx-1]+psin[1:ny-1,0:nx-2])/dx**2
		#		+(psin[2:ny,1:nx-1]-2*psin[1:ny-1,1:nx-1]+psin[0:ny-2,1:nx-1])/dy**2
		#		-zeta[1:ny-1,1:nx-1])

		#https://www.mi.uni-hamburg.de/arbeitsgruppen/theoretische-meteorologie/personen/lunkeit-frank/numerik/dokumente/barotrop.pdf
		#psi[1:ny-1,1:nx-1] = ((1/dx**2)*(psi[1:ny-1,2:nx]+psi[1:ny-1,0:nx-2]-2*psi[1:ny-1,1:nx-1])
		#						+(1/dy**2)*(psi[2:ny,1:nx-1]+psi[0:ny-2,1:nx-1]-2*psi[1:ny-1,1:nx-1])
		#						-zeta[1:ny-1,1:nx-1])/(2/dx**2+2/dy**2)

		#Jacobi interior
		#psi[1:ny-1,1:nx-1] = ((1.0/4.0)*(
		#						+psin[1:ny-1,2:nx]
		#						+psin[1:ny-1,0:nx-2]
		#						+psin[2:ny,1:nx-1]
		#						+psin[0:ny-2,1:nx-1])
		#					-(dx**2/4)*zeta[1:ny-1,1:nx-1])

		#x = 0 boundary
		#Jeg tror, vi skal tage et note ud af Holtons bog, og her, skipper vi psin[1:ny-1,-1] og bruger psin[1:ny-1,-2]
		#psi[1:ny-1,0] = psin[1:ny-1,0]+dtau*(
		#		+(psin[1:ny-1,1]-2*psin[1:ny-1,0]+psin[1:ny-1,-2])/dx**2
		#		+(psin[2:ny,0]-2*psin[1:ny-1,0]+psin[0:ny-2,0])/dy**2
		#		-zeta[1:ny-1,0])
		#Old
		#psi[1:ny-1,0] = psin[1:ny-1,0]+dtau*(
		#		+(psin[1:ny-1,1]-2*psin[1:ny-1,0]+psin[1:ny-1,-1])/dx**2
		#		+(psin[2:ny,0]-2*psin[1:ny-1,0]+psin[0:ny-2,0])/dy**2
		#		-zeta[1:ny-1,0])

		#x = L boundary
		#Enten så gør den periodic sådan her
		#psi[1:ny-1,-1] = psi[1:ny-1,0]
		#eller sådan her, Holton
		#psi[:,-1] = psi[:,0]

		#Eller gør den periodic sådan her
		#psi[1:ny-1,-1] = psin[1:ny-1,-1]+dtau*(
		#+(psin[1:ny-1,0]-2*psin[1:ny-1,-1]+psin[1:ny-1,-2])/dx**2
		#+(psin[2:ny,-1]-2*psin[1:ny-1,-1]+psin[0:ny-2,-1])/dy**2
		#-zeta[1:ny-1,-1])

		#Psi no-flow across boundary GHOST CELLS
		#boundary at x = L, i'm gonna set equal to x = 0...
		#Boundary for psi, maybe i should remove these!
		psi[:,-1] = 0 #right boundary
		psi[:,0] = 0	#left boundary

		#What if i don't set it to 0? afaik it should be constant along the edges, but maybe not 0...
		#Det har i hvert fald bestemt en effekt om man sætter det til 0 eller ej..
		#Probably not needed to put it to 0, if I'm just careful the other places,
		#But, to follow the algorithm completely, I here set the y boundarys upper and lower to 0
		psi[0,:] = 0  #south boundary
		psi[-1,:] = 0 #north boundary

		#Psi no-flow across boundary ACTUAL BOUNDARY
		#psi[:,-1+Ngc] = 0 #right boundary
		#psi[:,0+Ngc] = 0	#left boundary
		#psi[0+Ngc,:] = 0  #south boundary
		#psi[-1-Ngc,:] = 0 #north boundary

		#

		#No-slip psi streamfunction BC
		#Maybe I need additional BCs due to the no-slip condition??
		#no-slip, hence, on sourthern boundary,
		#hence, u0 = 0 on sourthern boundary, and ALSO v0 = 0 on sourthern boundary
		#now, u0 = 0, but, u1 != 0 likely...
		#so, on southern boundary, there would be, du/dy...
		#hence, there WOULD be a psi and a zeta, right?
		#ie, for no-slip, then zeta is NOT zero right, at boundary?
		#yep... for no-slip, zeta is NOT zero at boundary, and i think, psi is ALSO a bit more complicated
		#maybe,
		#psi[:,1] = psi[:,0]+u[:,0]*dy
		#something like that..
		#actually hmm it might be good enough.... i actually think it's okay,
		#psi[:,1] will come from solving poisson equation,

		#But, should maybe plot the solution... to see if the boundaries are somewhat satisfied...
		#so make a 3d plot of the psi field...

		#lec12, on sourthern boundary, n = (0,1), hence nablapsi.n = 0 = dpsi/dy = 0
		#yeah... that will give, u = -dpsi/dy = 0 on boundary....
		#but that almost means, that, it should be, psi0 = psi1 = 0...
		#like, psi0,i = psi1,i = 0...
		#So i should actually enfore the psi to be zero even further out, i THINK...
		#because ONE thing is ghost cells, those are not the ACTUAL boundary, i think...
		#they are a pseudo-boundary... the ACTUAL boundary is even further inside,
		#so i think i need to enfore psi = 0 even further inside that what i currently have...
		#also, hence, the Poisson solver, should actually solve LESS area than what it doesn
		#I need to reduce the area by +-1 in each direction i think...
		#like, there's BOUNDARY conditions, AND, ghost cell conditions...
		#so just think like, psi[:,0] is not an ACTUAL BOUNDARY condition, it's actually a GHOST CELL CONDITION
		#so i need to ALSO set psi[:,1] as the ACTUAL boundary condition, AND change the algorithms...

		#so... if i want u = 0 on southern boundary, I would need,
		#u = 0 = -(psi(1)-psi(-1))/dy
		#so maybe I DO need ghost cells or something...
		#and then I would have psi(-1) = psi(0) = 0
		#psi(0) = 0 from dirichlet psi = 0 condition on BCs
		#psi(-1) = 0 from no-slipp conditions...
		#
		#Like, det er lidt som, it's a bit like,
		#hvis man har en vorticity-streamfunction formulation
		#så SKAL man jo nærmest have en staggered grid?
		#almost JUST HAPPENS to be that way...
		#because, u = dpsi/dx... so... it's like... i mean maybe you could FORCE it to be unstaggered,
		#but, by it's very nature, it seems staggered
		#so, how do we deal with that then??

		#psi[:,-1-1] = 0 #right boundary
		#psi[:,0+1] = 0	#left boundary
		#psi[0+1,:] = 0  #south boundary
		#psi[-1-1,:] = 0 #north boundary

		#Now me tihnking, ghost cells or just complicated boundary conditions?
		#Well actually, ghost cells are also used in eg Semi-Lagrangian CFD...
		#so, it's actually maybe rather common in some ways...
		#and it also speaks to the idea of simulating a larger domain, it's sort of same philosophy
		#so imo, might aswell just get used to it, and start adding ghost cells a bit here...
		#

		#print(eps)
		if (np.abs(eps) < epstol).all():
			print("Break Poisson algorithm at", nit)
			break

	#I think here, we should return psi, instead of doing it implicitly....
	#so, change the way the method works...
	return psi

def nabladotv(u,v,nx,ny,dx,dy):
	"""
	Divergence should be 0 for BVE,
	nabla.v =0

	Test it out
	Should maybe be centered differences??
	And maybe on edges, do forward differences
	"""

	divv = (u[:,1:nx-1]-u[:,0:nx-2])/dx + (v[1:ny-1,:]-v[0:ny-2,:])/dy

	return divv

def CalcVelocity(psi,dx,dy):
	"""
	Should probably also take u,v as input, or initiate u,v here...
	ug,vg geostrophic

	ug = -dpsi/dy
	vg = dpsi/dx

	these velocities depend on streamfunction psi being correct, especially near BCs...
	BCs are set outside a bit...
	#so v[0,:] eg is outside this function...

	"""

	#psi[y,x] array order
	#for i in range(1,nx-1):
	#	for j in range(1,ny-1):
	#		u[j,i] = (psi[j+1,i]-psi[j-1,i])/(2*dy)
	#		v[j,i] = -(psi[j,i+1]-psi[j,i-1])/(2*dx)

	u[1:ny-1,1:nx-1] = (psi[2:ny,1:nx-1]-psi[0:ny-2,1:nx-1])/(2*dy)
	v[1:ny-1,1:nx-1] = -(psi[1:ny-1,2:nx]-psi[1:ny-1,0:nx-2])/(2*dx)

	return u,v

if __name__ == "__main__":

	import numpy as np
	#from pylab import *
	import matplotlib.pyplot as plt
	import matplotlib.animation as animation
	from mpl_toolkits.mplot3d import axes3d

	print("ARRAY TEST")
	atest = np.zeros((4,4))-3
	print(np.abs(atest))
	if (atest<4).all():

		print("ths works .all()")

	if (atest<4).any():
		print("ths works .any()")

	Lx = 1
	Ly = 1
	nx = 22
	ny = 22
	#x = np.linspace(0,Lx,nx)
	#y = np.linspace(0,Ly,ny)
	X = np.linspace(0,Lx,nx)
	Y = np.linspace(0,Ly,ny)
	x,y = np.meshgrid(X,Y)

	#initial vorticity and streamfunction

	#Poisson solver error
	epstol = 1e-4

	Uwall = 0.5
	Re = 1000 #Re = uL/nu  where u is flow speed, L is length, nu is kinematic viscosity
	dt = 0.0001
	h = 1.0/(nx-1)
	dx = h
	dy = h

	alpha = 0.1
	rho = 1

	#time integration parameters					#hours
	time_end = 20*dt				#seconds

	t = 0
	nt = 0

	#not NEEDED, but, I think psi array should probably be nx+1,ny+1, or, velocity should be nx-1,ny-1
	# P = np.zeros((ny,nx),dtype=np.float64)
	# u = np.zeros((ny,nx),dtype=np.float64)
	# v = np.zeros((ny,nx),dtype=np.float64)
	# ut = np.zeros((ny,nx),dtype=np.float64)
	# vt = np.zeros((ny,nx),dtype=np.float64)
	# Au = np.zeros((ny,nx),dtype=np.float64) #advection
	# Av = np.zeros((ny,nx),dtype=np.float64) #advection
	# Du = np.zeros((ny,nx),dtype=np.float64) #diffusion
	# Dv = np.zeros((ny,nx),dtype=np.float64) #diffusion

	#Staggered Grid arrays
	P = np.zeros((ny+2,nx+2),dtype=np.float64)
	u = np.zeros((ny+2,nx+1),dtype=np.float64)
	v = np.zeros((ny+1,nx+2),dtype=np.float64)
	ut = np.zeros((ny+2,nx+1),dtype=np.float64)
	vt = np.zeros((ny+1,nx+2),dtype=np.float64)
	Au = np.zeros((ny+2,nx+1),dtype=np.float64) #advection
	Av = np.zeros((ny+1,nx+2),dtype=np.float64) #advection
	Du = np.zeros((ny+2,nx+1),dtype=np.float64) #diffusion
	Dv = np.zeros((ny+1,nx+2),dtype=np.float64) #diffusion

	nablau = np.zeros((ny,nx),dtype=np.float64)
	#dfly = np.zeros((ny,nx),dtype=np.float64)
	#dflx = np.zeros((ny,nx),dtype=np.float64)

	# num1 = 125
	# num2 = 28
	# num3 = -25
	# print(num1)
	# print(num2)
	# print(num3)
	# print("AVERAGE", (num1+num2+num3)/3)

	# print("START")
	# print("num1 is", num1)
	# print("num2 is", num2)
	# print("num3 is", num3)
	# print("The average of num1,num2,and num3 is", (num1+num2+num3)/3)

	# print("DONE")

	#Ghost cells
	#Ngc = 0

	#Boundary conditions from beginning
	#zeta = NoSlipBCvorticity(zeta,psi,dx,dy)

	#====================================================================================
	#Main loop. Integrate forward in time

	#First step
	#Euler forward is easy to implement...
	#If you can accept the large first error?
	#Alternatively, you can use, Euler-Backwards (Matsuno) step on the first?
	#https://maths.ucd.ie/~plynch/LECTURE-NOTES/NWP-2004/NWP-CH03-2-3-P4.pdf
	#You're basically doing a leapfrog scheme with half-step too i think...

	while t < time_end:

		#Do a copy, but just assigning may also be work
		#old zeta, is equal to the intermediate zetacurrent we had in previous step

		#First solve for psi
		#psi = PsiSolvePoissonJacobi(psi,-w,h,h,nx,ny,epstol,Ngc)

		#Put BCs before updating fields, probably best
		##Alternative BCs
		#ALL velocities at edges are 0, except the lid
		#no slip, and no-flow through
		#u[-1,:] = 1 #top
		#u[0,:] = 0
		#u[:,-1] = 0
		#u[:,0] = 0
		#v[-1,:] = 0
		#v[0,:] = 0
		#v[:,-1] = 0
		#v[:,0] = 0

		#STAGGERED GRID BCs, NOT ghost cells
		#no-slip, no-inflow
		# u[-2,:] = 1 #top Uwall
		# u[1,:] = 0 #bottom no slip
		# u[:,-2] = 0 #right no-inflow
		# u[:,1] = 0 #left no-inflow
		# v[-2,:] = 0 #top no-inflow
		# v[1,:] = 0 #bottom no-inflow
		# v[:,-2] = 0 #right no-slip
		# v[:,1] = 0 #left no-slip

		#STAGGERED GRID, CHOST CELLS!
		Ubottomwall = 0
		Utopwall = 1
		Urightwall = 0
		Uleftwall = 0
		Vtopwall = 0
		Vbottomwall = 0
		Vleftwall = 0
		Vrightwall = 0
		#Should not be :... should be more specific...
		#also, do reflection technique for Ghost Cells
		#u0 = 2*uwall - u1
		#ahh interesting... if we set Utopwall = 1, actually, NONE of the arrays will be set u[-1,:] = Utopwall
		#The Utopwall is actually an "inbetween" point... i.e,
		#u[-1,:] is Ghost cell
		#u[-2,:] is NOT boundary, actually! It's ACTUALLY an INTERIOR point
		#So technically, on SOME boundaries, for SOME variables, they are actually NOT DEFINED ON THE BOUNDARY...
		#Except e.g P, I think this is defined everywhere, Ghost, Cell, AND interior...

		#Need to specific, WHICH u or v is defined ON which boundary??
		#And which side/variable is ONLY interior/ghost

		#TOP BOUNDARY:
		#u is ghost/interior,
		#v is boundary/interior
		#BOTTOM BOUNDARY:
		#u is ghost/interior
		#v is boundary/interior
		#LEFT BOUNDARY:
		#u is boundary/interior
		#v is ghost/interior
		#RIGHT BOUNDARY:
		#u is boundary/interior
		#v is ghost/interior

		#THe reflection technique is ONLY USED when the variable is ghost/interior, I think
		u[-1,:] = 2*Utopwall-u[-2,:] # top ghost cells
		u[0,:] = 2*Ubottomwall-u[-1,:] # #bottom ghost cell
		u[:,-1] = 0 # no inflow BOUNDARY 2*Urightwall-u[:,-2] # #right ghost
		u[:,0] = 0 #no inflow BOUNDARY #2*Uleftwall-u[:,1] #left no-inflow
		v[-1,:] = 0#no inflow BOUNDARY #2*Vtopwall-v[ #top no-inflow
		v[0,:] = 0 #no inflow BOUNDARY bottom #2*Vbottomwall-v[] #bottom no-inflow
		v[:,-1] = 2*Vrightwall-v[:,-2] #right ghost cell
		v[:,0] = 2*Vleftwall-v[:,1] #left ghost cell

		#Should be maybe ALSO.... put BCs on ut,vt???

		#A and D is integer j in y direction, but i+1/2 in x direction,
		#Advection
		#Right now doing it simple, take care that we really get staggered i+1/2 etc grid
		#Actually yes it's wrong..... most likely wrong, u[j,i+1] has specific meaning
		#for j in range(1,ny-1):
		#	for i in range(1, nx-1):
		#Staggered Grid, I think like this
		#We only want INTERIOR loop here.
		#Ie, NOT ghost cells, and NOT boundary cells
		for j in range(1,ny-1):
			for i in range(1, nx-1):

				#Put in also intermediates here actually
				#I think we do some linear interpolation actually, halfway midpoint interpolation
				ui1j = (u[j,i+1]+u[j,i])*0.5
				uij = (u[j,i]+u[j,i-1])*0.5
				uvi05j05 = 0.5*(u[j,i]+u[j+1,i])*0.5*(v[j,i]+v[j,i+1])
				uvi05jm05 = 0.5*(u[j,i]+u[j-1,i])*0.5*(v[j-1,i]+v[j-1,i+1])

				#v advection
				vij1 = (v[j+1,i]+v[j,i])*0.5
				vij = (v[j,i]+u[j-1,i])*0.5
				uvi05j05 = 0.5*(u[j,i]+u[j+1,i])*0.5*(v[j,i]+v[j,i+1]) #Actually just same as above
				uvim05j05 = 0.5*(u[j+1,i-1]+u[j,i-1])*0.5*(v[j,i]+v[j,i-1])

				Au[j,i] = (ui1j**2-uij**2)/h + (uvi05j05-uvi05jm05)/h
				Av[j,i] = (vij1**2-vij**2)/h + (uvi05j05-uvim05j05)/h

				#Non-staggered versions
				#Au[j,i] = (u[j,i+1]**2-u[j,i]**2)/h + (u[j+1,i+1]*v[j+1,i+1]-u[j-1,i+1]*v[j-1,i+1])/h
				#Av[j,i] = (u[j+1,i+1]*v[j+1,i+1]-u[j-1,i+1]*v[j-1,i+1])/h+(v[j+1,i]**2-v[j,i]**2)/h

		#Diffusion
		#We would actually need 2, I think?
		#D for u, and D for v?
		#for j in range(1,ny-1):
		#	for i in range(1, nx-1):
		#Staggered grid
		#We could do ONLY interior points, i.e NOT ghost cells and NOT Boundary cells,
		#but, keep it this way?
		#for j in range(1,ny):
		#	for i in range(1,nx):
		#		Du[j,i] = alpha*((u[j,i+1]-2*u[j,i]+u[j,i-1])/h**2+(u[j+1,i]-2*u[j,i]+u[j-1,i])/h**2)
		#		Dv[j,i] = alpha*((v[j,i+1]-2*v[j,i]+v[j,i-1])/h**2+(v[j+1,i]-2*v[j,i]+v[j-1,i])/h**2)
		#		for j in range(1,ny):
		#Diffusion u
		for j in range(1,ny):
			for i in range(1,nx-1):
				Du[j,i] = alpha*((u[j,i+1]-2*u[j,i]+u[j,i-1])/h**2+(u[j+1,i]-2*u[j,i]+u[j-1,i])/h**2)

		#Diffusion v
		for j in range(1,ny-1):
			for i in range(1,nx):
				Dv[j,i] = alpha*((v[j,i+1]-2*v[j,i]+v[j,i-1])/h**2+(v[j+1,i]-2*v[j,i]+v[j-1,i])/h**2)

		#momentum
		#u = u + dt(-A+D)+dt*nablaP / rho
		#v = v + dt(-A+D)+dt*nablaP / rho

		#splitting + project procedure... projection(fractional step) method
		#we do intermediate step, before reaching un+1
		#we do un -> ut
		#ut -> un+1
		#and some stuff changes etc
		#basically to enforce nabla.u = 0

		#FIRST INTEGRATE WITHOUT PRESSURE, THEN get PRESSURES FROM Poisson...
		#for j in range(1,ny-1):
		#	for i in range(1,nx-1):
		#Staggered Grid, I think like thius
		#But i think, ONLY interior, right?
		#for j in range(1,ny-1):
		#	for i in range(1,nx-1):
		#		ut[j,i] = u[j,i] + dt*(-Au[j,i]+Du[j,i])
		#		vt[j,i] = v[j,i] + dt*(-Av[j,i]+Dv[j,i])

		#ut update
		#j ghost 0, ny+1 ghost -> interior 1, ny interior
		#i border 0, nx border -> interior 1, nx-1 interior
		for j in range(1,ny):
			for i in range(1,nx-1):
				ut[j,i] = u[j,i] + dt*(-Au[j,i]+Du[j,i])

		#vt update
		#j border 0, ny border -> interior 1, ny-1 interior
		#i ghost 0, nx+1 ghost -> interior 1, nx interior
		for j in range(1,ny-1):
			for i in range(1,nx):
				vt[j,i] = v[j,i] + dt*(-Av[j,i]+Dv[j,i])

		#FIRST, I need to get the pressure from Poisson
		#THEN do projection
		#SOLVE POISSON
		npseudotime = 200
		#P[:,:] = 0
		pn = P.copy()
		for nit in range(npseudotime):
			pn = P.copy()
			#for j in range(1,ny-1):
			#	for i in range(1,nx-1):
			#Staggered Grid, I thin like this
			#ONLY INTERIOR NODES, NOT GHOST CELLS, and NOT boundary cells!
			for j in range(2,ny-1):
				for i in range(2,nx-1):
					#P[j,i] = (1/4)*(P[j,i+1]+P[j,i-1]+P[j+1,i]+P[j-1,i])-(1/4)*h/dt*(ut[j,i]-ut[j,i-1]+vt[j,i]-vt[j-1,i])
					P[j,i] = (1/4)*(pn[j,i+1]+pn[j,i-1]+pn[j+1,i]+pn[j-1,i])-(1/4)*h/dt*(ut[j,i]-ut[j,i-1]+vt[j,i]-vt[j-1,i])

			#ALTERNATIVE PRESSURE BOUNDARIES
			#P[:,0] = P[:,1] #dp/dx = 0 at left boundary
			#P[0,:] = P[1,:] #dp/dy = 0 at bottom boundary
			#P[:,-1] = P[:,-2] #dp/dx = 0 at right boundary
			#P[-1,:] = 0 #Pressure is 0 at upper lid

			# #BCs from WPIdoc
			#I think the 1/3, 1/2 factor etc is because in interior points we do double centered differences
			#but at sides, we can only do centered in ONE direction, and FORWARD in another direction, so
			#We only get 1/3....
			#And at corners, we can ONLY do forward differences in BOTH directions, so it will be 1/2.... probably..
			#when evaluating the nabla^2

			NGC = 1
			#SIDES
			#Left side, i.e left INTERIOR
			i = 0+NGC
			#j = (0:ny)
			P[1:ny-1,i] = (1/3)*(P[1:ny-1,i+1]+P[1:ny-1,i-1]+P[2:ny,i]+P[0:ny-2,i])-(1/3)*h/dt*(ut[1:ny-1,i]-ut[j,i-1]+vt[1:ny-1,i]-vt[0:ny-2,i])

			#Right side, i.e right INTERIOR
			i = -1-NGC
			# #j = (0:ny)
			P[1:ny-1,i] = (1/3)*(P[1:ny-1,i+1]+P[1:ny-1,i-1]+P[2:ny,i]+P[0:ny-2,i])-(1/3)*h/dt*(ut[1:ny-1,i]-ut[j,i-1]+vt[1:ny-1,i]-vt[0:ny-2,i])

			#Bottom side, i.e bottom INTERIOR
			# #i = (0:nx)
			j = 0+NGC
			P[j,1:nx-1] = (1/3)*(P[j,2:nx]+P[j,0:nx-2]+P[j+1,1:nx-1]+P[j-1,1:nx-1])-(1/3)*h/dt*(ut[j,1:nx-1]-ut[j,0:nx-2]+vt[j,1:nx-1]-vt[j-1,1:nx-1])

			# #i = (0:nx), top INTERIOR
			j = -1-NGC
			P[j,1:nx-1] = (1/3)*(P[j,2:nx]+P[j,0:nx-2]+P[j+1,1:nx-1]+P[j-1,1:nx-1])-(1/3)*h/dt*(ut[j,1:nx-1]-ut[j,0:nx-2]+vt[j,1:nx-1]-vt[j-1,1:nx-1])

			#CORNER nodes IN the domain, not ghost chells...
			#so, i,j should actually not be 0,-1 etc.
			#Ghost cell corners are (0,0) (0,nx+1), (ny+1,nx+1), (ny+1,0)
			#BOUNDARY corners are (1,1), (1,nx), (ny,nx), (ny,1)
			#P is actually NEVER defined ON the boundary
			#P is ONLY ghost/interior
			i = 1
			j = 1
			P[j,i] = (1/2)*(P[j,i+1]+P[j,i-1]+P[j+1,i]+P[j-1,i])-(1/2)*h/dt*(ut[j,i]-ut[j,i-1]+vt[j,i]-vt[j-1,i])

			i = -2
			j = -2
			P[j,i] = (1/2)*(P[j,i+1]+P[j,i-1]+P[j+1,i]+P[j-1,i])-(1/2)*h/dt*(ut[j,i]-ut[j,i-1]+vt[j,i]-vt[j-1,i])

			i = 1
			j = -2
			P[j,i] = (1/2)*(P[j,i+1]+P[j,i-1]+P[j+1,i]+P[j-1,i])-(1/2)*h/dt*(ut[j,i]-ut[j,i-1]+vt[j,i]-vt[j-1,i])

			i = -2
			j = 1
			P[j,i] = (1/2)*(P[j,i+1]+P[j,i-1]+P[j+1,i]+P[j-1,i])-(1/2)*h/dt*(ut[j,i]-ut[j,i-1]+vt[j,i]-vt[j-1,i])

		#PROJECTION, forward difference
		#for j in range(1,ny-1):
		#	for i in range(1,nx-1):
		#Staggered Grid method
		#ONLY INTERIOR POINTS HERE, Ghost Cells and Boundary Cells will be treated separately
		#for j in range(1,ny):
		#	for i in range(1,nx):
		#		u[j,i] = ut[j,i]-dt/(rho*h)*(P[j,i+1]-P[j,i])
		#		v[j,i] = vt[j,i]-dt/(rho*h)*(P[j+1,i]-P[j,i])

		#update u velocity
		for j in range(1,ny):
			for i in range(1,nx-1):
				u[j,i] = ut[j,i]-dt/(rho*h)*(P[j,i+1]-P[j,i])

		#update v velocity
		for j in range(1,ny-1):
			for i in range(1,nx):
				v[j,i] = vt[j,i]-dt/(rho*h)*(P[j+1,i]-P[j,i])

		#Now, more specifically, we want nabla.un+1 = 0
		#so, the updated velocity will satisfy incompressibility
		#SO... now we take the divergence of each side! Simple!

		#nabla.un+1 = nabla.ut -nabla*dt*nablap/rho
		#Now, enforce first time = 0, then, we get the equations,
		#0 = nabla.ut - dt/rho*nabla^2p

		#So basically, we are solving a poisson equation

		#Pressure boundary condition is not needed?
		#But, velocity is

		#Btw, maybe to be safe, make intermediate velocity array ut

		#===========================================================
		#BOUNDARY CONDITIONS
		#Reflection technique seems to be mostly done on GHOST cells actually...
		#they are done for cells e.g ny+1, so i think they are for ghost-cell purposes
		#also, you can tell what the consequence of refleciton technique is,
		#it's basically creating a velocity slightly larger than Uwall...
		#As if the velocity above the wall is even slightly larger thana t the wall and etc etc etc....
		#makes sense, in terms of viscosity etc!

		#no slip, error in slides, i think he swapped u and v
		#also, correct for ghost cells
		# #bottom boundary
		# v[:,0]= 0
		# v[:,-1] = 0
		# U0wall = 0
		# Unywall = 1
		# u[0,:] = 2*U0wall-u[1,:] #Bottom reflection technique
		# u[-1,:]= 2*Unywall-u[-2,:] #top reflection technique

		# #left boundary
		# u[:,0] = 0 #no slip

		# #right boundary
		# u[:,-1] = 0 #no slip

		# #Pressure boundary conditions, Neumann conditions from Projection
		# #These needs to be put into the Poisson solver i think actually...
		# #Corner
		# #(i,j() = (1,1) (nx,ny) (1,ny) (nx,1)
		# P[1,1] = (1/2)*beta*()

		# #vspeed = np.sqrt(u*u+v*v)
		# #vspeedmax = np.max(vspeed)
		# #dt = 0.9*0.25*h*h/(vspeedmax+0.01)

		print(t, dt)
		nt += 1
		t+= dt #should actually be 2*dt for leapfrog

	print("Some of P")
	print(P[:10,0:10])
	Xp = np.linspace(0,Lx,nx+2)
	Yp = np.linspace(0,Ly,ny+2)
	xp,yp = np.meshgrid(Xp,Yp)

	#plot initial poisson solution of nabla^2psi = zeta
	fig00 = plt.figure() #If i do pcolor, then no need for 3d projection
	ax00 = fig00.gca()
	#ax00.plot_surface(x, y, psi)#, rstride=3, cstride=3, color='black')
	C = ax00.contour(xp,yp,P,8,colors='black')
	ax00.set_title('Final Psi from Poisson nabla2psi = xi')
	ax00.set_xlabel('x')
	ax00.set_ylabel('y')
	plt.show()

	print("SIzes")
	print(xp.shape())
	print(yp.shape())
	print(u.shape())
	print(v.shape())
	fig2 = plt.figure(3)
	ax2 = plt.gca()
	ax2.set_title('Navier Stokes')
	ax2.set_xlabel('x')
	ax2.set_ylabel('y')
	#ax.quiver(x/1000,y/1000,u,v)
	#C = ax.contour(x/1000,y/1000,zeta0*10**7,8,colors='black')
	ax2.quiver(xp[1:ny,1:nx],yp[1:ny,1:nx],u[0:ny,:],v[:,0:nx])
	plt.show()


from itertools import groupby
n = input()
print(''.join( str(len(list(g))) + k for k, g in groupby(n)))

n = input()
a=n[0]
c=1
r=''
for i in range(1,len(n)):
    if n[i]==a:
        c+=1
    else:
        r+=str(c)+str(n[i-1])
        c=1
        a=n[i]
print(r+str(c)+n[len(n)-1])


# Specify a name for the model to avoid overwriting old results
folder = 'My_Network'

from matplotlib import offsetbox
import matplotlib.pyplot as plt
import nest as sim # type: ignore
import numpy as np
import pandas
from collections import Counter
import time as tm
import scipy.stats
import os

# Used to generate non-constant AHVs
def smoothclamp(x, mi, mx): return mi + (mx-mi)*(lambda t: np.where(t < 0 , 0, np.where( t <= 1 , 3*t**2-2*t**3, 1 ) ) )( (x-mi)/(mx-mi) )

# Used to generate population weight matrices
def insertVec(m, v, x, y, axis = 0):
    if axis == 1:
        for i in range(len(v)):
            m[y,x+i]=v[i]
    else:
        for i in range(len(v)):
            m[y+i,x]=v[i]
    return m

# Used to calculate the current to be injected into the stationary ring
# c describes vertical scale
# s describes steepness
# b is vertical offset
def velCurve(x, c, s, b):
    y = np.empty_like(x)
    for i in range(len(x)):
        y[i] = c/(s*abs(x[i])+1) + b
    return y

deg_sign = u'\N{DEGREE SIGN}'

# misc params
use_stationary_ring = False # Creates a 5th ring. Connections are not currently set
vel_rescale = False     # Non-functional
long = False            # If true, runs a 720deg rotation instead of 180. Used for noisy models

# params describing number of cells in each population
N_ex = 180
N_in = N_ex
N_cj = N_ex # number of cj POPULATIONS, not neurons

# params for describing connection weights between populations
sigma = 0.12    # def 0.12
delay = 0.1     # def 0.1
base_ex = 4000  # def 4000    EX->IN
base_in = 450   # def 450     IN->EX
base_cj = 25    # def 169     CJ->EX   #narrow 25   #halfvel 36
base_ex_cj = 660        # def 660
I_ex = 450.0            # def 450 pA
vel_mult = 3500         # def 3500                  #halfvel 1750
sh = 150                # def 150
in_sigma_scale = 0.5    # def 1        #narrow 0.5                 # multiplier of Sigma for the IN->EX distribution

base_st_ex = 0   #      ST->EX
I_st = 0.0        # def ? pA   peak current into the stationary layer, (c in the curve function)

# params describing size , duration and excitatory cell number to initialize the bump
I_init = 300.0 #pA
I_init_dur = 100.0 #ms
I_init_pos = N_ex//2

# PopCoding parameters
cj_population_size = 1      # set to 1 to not use pop-coding
pop_input_vari = 0.5        # EX->CJ variance
pop_output_vari = 0.0       # CJ->EX variance

# figure rendering tweak
ringhist_pad = 0.01

# ensure file struct
if not os.path.exists(f'results/{folder}'):
    os.makedirs(f'results/{folder}')
if not os.path.exists(f'results/{folder}/ring_hist'):
    os.makedirs(f'results/{folder}/ring_hist')
if not os.path.exists(f'results/{folder}/drift_hist'):
    os.makedirs(f'results/{folder}/drift_hist')
# Record parameter details
f = open("results/"+folder+"/Parameters.txt", "w")
f.write("N_ex " + str(N_ex) + "\n")
f.write("N_in " + str(N_in) + "\n")
f.write("N_cj " + str(N_cj) + "\n")
f.write("sigma " + str(sigma) + "\n")
f.write("base_ex " + str(base_ex) + "\n")
f.write("base_in " + str(base_in) + "\n")
f.write("base_cj " + str(base_cj) + "\n")
f.write("base_ex_cj " + str(base_ex_cj) + "\n")
f.write("I_ex " + str(I_ex) + "\n")
f.write("vel_mult " + str(vel_mult) + "\n")
f.write("sh " + str(sh) + "\n")
f.write("base_st_ex " + str(base_st_ex) + "\n")
f.write("I_st " + str(I_st) + "\n")
f.write("cj_population_size " + str(cj_population_size) + "\n")
f.write("in_sigma_scale " + str(in_sigma_scale) + "\n")
f.write("pop_input_vari " + str(pop_input_vari) + "\n")
f.write("pop_output_vari " + str(pop_output_vari) + "\n")
f.write("long " + str(long))
f.close()

# scale the the CJ->EX weight value to be appropriate for pop-size
base_cj /= cj_population_size # CJ -> EX#

# Everything in one big function to reset the kernal just in case
def run(myVel, graph_end=20, smoothVel = False, save_data = True):
    sim.ResetKernel()

    # V_th default = -55
    exc = sim.Create("iaf_psc_alpha",N_ex, params={"I_e": I_ex})
    inh = sim.Create("iaf_psc_alpha",N_in)

    l = sim.Create("iaf_psc_alpha",N_cj * cj_population_size)
    r = sim.Create("iaf_psc_alpha",N_cj * cj_population_size)
    st = sim.Create("iaf_psc_alpha",N_cj * cj_population_size)    # stationary ring

    # connections between excitatory and inhibitory rings described in weight matrices
    w_ex = np.empty((N_in,N_ex))
    w_in = np.empty((N_ex,N_in))

    #loop through each excitatory and inhibitory cell
    for e in range(N_ex):
        for i in range(N_in):
            # find the smallest distance between the excitatory and inhibitory cell, looking both ways around the ring
            d1 = abs(e/N_ex - i/N_in)
            d2 = abs(e/N_ex - i/N_in -1)
            d3 = abs(e/N_ex - i/N_in +1)
            d = min(abs(d1),abs(d2),abs(d3))

            #gaussian function finds the distance dependent connection strength
            w_gauss = np.exp(-(d)**2/2/sigma**2)
            w_ring = np.exp(-(d - 0.5)**2/2/(sigma*in_sigma_scale)**2) #inhibitory connections are ofset by parameter mu  # reduce deviation on inhib
            # scale by base weight and add to matrix
            w_ex[i,e] = base_ex * w_gauss
            w_in[e,i] = base_in * w_ring# * in_sigma_scale    # account for increased distrib        # MISTAKE HERE

    # set all very small weights to zero to reduce total number of connections
    w_ex[w_ex<10]=0
    w_in[w_in<10]=0

    ###
    #
    #   Build CJ weights
    #
    ###

    w_ex_cj = np.identity(N_ex) * base_ex_cj
    if(cj_population_size > 1):
        w_ex_cj = np.repeat(w_ex_cj, cj_population_size, axis=0)
        vec = np.linspace(base_ex_cj * (1+pop_input_vari), base_ex_cj  * (1-pop_input_vari), cj_population_size)
        for i in range(N_ex):
            w_ex_cj = insertVec(w_ex_cj, vec, i, i * cj_population_size, axis = 0)

    w_st_ex = np.identity(N_cj) * base_st_ex
    w_st_ex = np.repeat(w_st_ex, cj_population_size, axis=1)

    # connections between conjuntive layers and excitatory ring
    w_l = np.empty((N_ex,N_cj))
    w_r = np.empty((N_ex,N_cj)) # (row major)
    for c in range(N_cj):
        for e in range(N_ex):
            d1 = abs((e-1)/N_cj - c/N_ex)
            d2 = abs((e-1)/N_cj - c/N_ex -1)
            d3 = abs((e-1)/N_cj - c/N_ex +1)
            d = min(abs(d1),abs(d2),abs(d3))
            w_l[e,c] = base_cj * (np.exp(-(d)**2/2/sigma**2))

            d1 = abs((e+1)/N_cj - c/N_ex)
            d2 = abs((e+1)/N_cj - c/N_ex -1)
            d3 = abs((e+1)/N_cj - c/N_ex +1)
            d = min(abs(d1),abs(d2),abs(d3))
            w_r[e,c] = base_cj * (np.exp(-(d)**2/2/sigma**2))
    #temp1 = w_l
    #for c in range(N_cj):
    #    for e in range(N_ex):
    #        if w_l[e,c] <= w_l[(e+1)%(N_ex),c]:
    #            temp1[e,c] = 0
            #if w_r[(e+1)%(N_ex),c] <= w_r[e,c]:
            #    temp2[(e+1)%(N_ex),c] = 0
    #w_l = temp1
    #w_r = np.transpose(w_l)

    # this only takes the highest value. Takes the blurred line and turns it into a single thin line
    m = np.amax(w_l)
    w_l[w_l<m] = 0
    m = np.amax(w_r)
    w_r[w_r<m] = 0

    if(cj_population_size > 1):
        w_r = np.repeat(w_r, cj_population_size, axis=1)
        w_l = np.repeat(w_l, cj_population_size, axis=1)
        w_r = np.zeros_like(w_l)
        w_l = np.zeros_like(w_r)
        vec = np.linspace(base_cj * (1+pop_output_vari), base_cj * (1-pop_output_vari), cj_population_size)
        for i in range(N_ex):
            w_r = insertVec(w_r, vec, i * cj_population_size, i-1, axis = 1)
        for i in range(N_ex):
            w_l = insertVec(w_l, vec, (i-1) * cj_population_size, i, axis = 1)

    c = plt.matshow(w_l)
    plt.colorbar(c)
    plt.xlabel("CJ neuron index")
    plt.ylabel("EX neuron index")
    plt.tick_params(labelbottom=False,labeltop=True)
    plt.savefig("W_cjL-ex.png", dpi=100*cj_population_size,bbox_inches="tight")
    plt.clf()
    c = plt.matshow(w_r)
    plt.colorbar(c)
    plt.xlabel("CJ neuron index")
    plt.ylabel("EX neuron index")
    plt.tick_params(labelbottom=False,labeltop=True)
    plt.savefig("W_cjR-ex.png", dpi=100*cj_population_size,bbox_inches="tight")
    plt.clf()
    c = plt.matshow(w_ex)
    plt.colorbar(c)
    plt.xlabel("EX neuron index")
    plt.ylabel("IN neuron index")
    plt.tick_params(labelbottom=False,labeltop=True)
    plt.savefig("W_ex-in.png", dpi=300,bbox_inches="tight")
    plt.clf()
    c = plt.matshow(-w_in, cmap=plt.get_cmap("viridis_r"))
    plt.colorbar(c)
    plt.xlabel("IN neuron index")
    plt.ylabel("EX neuron index")
    plt.tick_params(labelbottom=False,labeltop=True)
    plt.savefig("W_in-ex.png", dpi=300,bbox_inches="tight")
    plt.clf()
    c = plt.matshow(w_ex_cj)
    plt.colorbar(c)
    plt.xlabel("EX neuron index")
    plt.ylabel("CJ neuron index")
    plt.tick_params(labelbottom=False,labeltop=True)
    plt.savefig("W_ex_cj.png", dpi=300,bbox_inches="tight")
    plt.clf()
    print("weights vis generated")

    sim.Connect(exc,inh,'all_to_all',syn_spec={'weight': w_ex, 'delay': delay})     # connect Excitatory to Inhibitory
    sim.Connect(inh,exc,'all_to_all',syn_spec={'weight': -w_in, 'delay': delay})    # connect Inhibitory to Excitatory

    sim.Connect(exc,l,'all_to_all',syn_spec={'weight': w_ex_cj, 'delay': delay})    # connect Excitatory to AHV_L
    sim.Connect(exc,r,'all_to_all',syn_spec={'weight': w_ex_cj, 'delay': delay})    # connect Excitatory to AHV_R

    sim.Connect(l,exc,'all_to_all',syn_spec={'weight': w_l, 'delay': delay})        # connect AHV_L to Excitatory
    sim.Connect(r,exc,'all_to_all',syn_spec={'weight': w_r, 'delay': delay})        # connect AHV_R to Excitatory
    if(use_stationary_ring):
        sim.Connect(st,exc,'all_to_all',syn_spec={'weight': w_st_ex, 'delay': delay})   # connect Stationary to Excitatory

    #os._exit(0)

    exc_spikes = sim.Create("spike_detector", 1, params={"withgid": True,"withtime": True}) # write to variable
    sim.Connect(exc,exc_spikes)
    l_spikes = sim.Create("spike_detector", 1, params={"withgid": True,"withtime": True}) # write to variable
    sim.Connect(l,l_spikes)
    r_spikes = sim.Create("spike_detector", 1, params={"withgid": True,"withtime": True}) # write to variable
    sim.Connect(r,r_spikes)
    inh_spikes = sim.Create("spike_detector", 1, params={"withgid": True,"withtime": True}) # write to variable
    sim.Connect(inh,inh_spikes)
    st_spikes = sim.Create("spike_detector", 1, params={"withgid": True,"withtime": True}) # write to variable
    sim.Connect(st,st_spikes)

    ###
    #
    #   Create vel curve
    #
    ###

    # posedata describes an actual path, but I can just set vel to a desired value
    #posedata = pandas.read_csv(f'data/{folder}/{folder}.csv')

    dt = 20 # 20ms, 50Hz
    #theta = posedata['field.theta']
    #theta = np.array(theta)
    targetPos = np.pi #*4 # radians
    if(long):
        targetPos *= 4
    duration = (targetPos / (myVel * np.pi/180)) * 1000
    framecount = duration // dt
    theta = np.linspace(0,targetPos,int(framecount)) # in radians
    if(smoothVel): theta = smoothclamp(theta,0,targetPos) #smooth clamp velocity
    #print(theta)
    angle_per_cell = (2*np.pi)/N_ex
    I_init_pos = np.around((theta[0]//angle_per_cell)+(N_ex//2)).astype(int)

    t = np.arange(0,len(theta)*dt,dt*1.) #assume 20ms timestep
    sim_len = len(theta) * 20
    time = [i for i in t if i < sim_len]

    vel = np.diff(np.unwrap(theta))
    print(len(theta))

    #arbitary scaling
            #vel rescale
    #print("downscaled vel: " + str(vel[0]))
    #if(vel_rescale == True):
    #    vel = (vel + 14) / 26

    Ivel = (vel) * vel_mult
    #Ivel *= Ivel

    fig = plt.figure()
    ax = plt.axes()
    ax.plot(t[1:], Ivel)
    fig.savefig("base vel over time.png")
    plt.clf()

    #os._exit(0)
    #print(vel)
    #print(Ivel)

    #st_i = velCurve(Ivel, I_st, 1, 0)
    st_i = np.ones_like(Ivel)
    st_i *= I_st
    #Ivel = velCurve(Ivel, -I_st, 1, I_st)

    go_l,go_r = Ivel,-Ivel #separate into clockwise and anticlockwise movements
    go_l = go_l+sh
    go_r = go_r+sh
    go_l[go_l<=sh] = 0 # everything below the threshold set to 0pA
    go_r[go_r<=sh] = 0

    # Connect AV input to conjunctive layers
    l_input = sim.Create('step_current_generator', 1)
    sim.SetStatus(l_input,{'amplitude_times': t[1:],'amplitude_values': go_l})  # current in picoamperes
    r_input = sim.Create('step_current_generator', 1)
    sim.SetStatus(r_input,{'amplitude_times': t[1:],'amplitude_values': go_r})
    st_input = sim.Create('step_current_generator', 1)
    sim.SetStatus(st_input,{'amplitude_times': t[1:],'amplitude_values': st_i})

    sim.Connect(r_input,r,'all_to_all')
    sim.Connect(l_input,l,'all_to_all')
    sim.Connect(st_input,st,'all_to_all')

    #os._exit(0)

    bump_init = sim.Create('step_current_generator', 1, params = {'amplitude_times':[0.1,0.1+I_init_dur],'amplitude_values':[I_init,0.0]})
    sim.Connect(bump_init,[exc[I_init_pos]])

    tic = tm.time()
    sim.Simulate(sim_len)
    print(f'Simulation run time: {np.around(tm.time()-tic,2)} s  Simulated time: {np.around(sim_len/1000,2)} s')

    if myVel >= 5:
        r_st = 0
        # Render all neuron histories
        T = np.arange(0,(len(theta)*dt),dt)   # Column
        fig_h = 2*(4+r_st)
        fig_l = len(T)/180 * fig_h
        fig, ax=plt.subplots(4+r_st,1, figsize=(fig_l, fig_h), sharex=True,)
        plt.xlabel("Timestep (20ms)")

        offset = 1
        ex_mat = np.zeros((N_ex, len(T)))
        ev = sim.GetStatus(exc_spikes)[0]['events']
        t = ev['times'] # time of each spike (in simulation time)
        sp = ev['senders'] # sender of each spike (cell ID)
        for i in range(len(T)-1):
            idx = (t>T[i])*(t<T[i+1]) # find all spikes in the bin
            sender_list = sp[np.where(idx)] # get the senders of each spike in the bin#
            for j in sender_list:
                ex_mat[j-offset][i] += 1
        ax[2+r_st].set_title("Exitatory ring spike history")
        plot = ax[2+r_st].imshow(ex_mat, cmap='hot', interpolation='nearest')
        ax[2+r_st].set_ylabel("Neuron index")
        plt.colorbar(plot,ax=ax[2+r_st],pad=ringhist_pad)

        offset += N_ex
        in_mat = np.zeros((N_in, len(T)))
        ev = sim.GetStatus(inh_spikes)[0]['events']
        t = ev['times'] # time of each spike (in simulation time)
        sp = ev['senders'] # sender of each spike (cell ID)
        for i in range(len(T)-1):
            idx = (t>T[i])*(t<T[i+1]) # find all spikes in the bin
            sender_list = sp[np.where(idx)] # get the senders of each spike in the bin#
            for j in sender_list:
                in_mat[j-offset][i] += 1
        ax[3+r_st].set_title("Inhibitory ring spike history")
        plot = ax[3+r_st].imshow(in_mat, cmap='hot', interpolation='nearest')
        ax[3+r_st].set_ylabel("Neuron index")
        plt.colorbar(plot,ax=ax[3+r_st],pad=ringhist_pad)

        offset += N_in
        cjl_mat = np.zeros((N_cj, len(T)))
        ev = sim.GetStatus(l_spikes)[0]['events']
        t = ev['times'] # time of each spike (in simulation time)
        sp = ev['senders'] # sender of each spike (cell ID)
        for i in range(len(T)-1):
            idx = (t>T[i])*(t<T[i+1]) # find all spikes in the bin
            sender_list = sp[np.where(idx)] # get the senders of each spike in the bin#
            for j in sender_list:
                cjl_mat[(j-offset)//cj_population_size][i] += 1
        ax[1+r_st].set_title("Left Conjunctive ring spike history")
        plot = ax[1+r_st].imshow(cjl_mat, cmap='hot', interpolation='nearest')
        if(cj_population_size>1):
            ax[1+r_st].set_ylabel("Population index")
        else:
            ax[1+r_st].set_ylabel("Neuron index")
        plt.colorbar(plot,ax=ax[1+r_st],pad=ringhist_pad)

        offset += N_cj*cj_population_size
        cjr_mat = np.zeros((N_cj, len(T)))
        ev = sim.GetStatus(r_spikes)[0]['events']
        t = ev['times'] # time of each spike (in simulation time)
        sp = ev['senders'] # sender of each spike (cell ID)
        for i in range(len(T)-1):
            idx = (t>T[i])*(t<T[i+1]) # find all spikes in the bin
            sender_list = sp[np.where(idx)] # get the senders of each spike in the bin#
            for j in sender_list:
                cjr_mat[(j-offset)//cj_population_size][i] += 1
        ax[0+r_st].set_title("Right Conjunctive ring spike history")
        plot = ax[0+r_st].imshow(cjr_mat, cmap='hot', interpolation='nearest')
        if(cj_population_size>1):
            ax[0+r_st].set_ylabel("Population index")
        else:
            ax[0+r_st].set_ylabel("Neuron index")
        plt.colorbar(plot,ax=ax[0+r_st],pad=ringhist_pad)

        if(r_st == 1):
            offset += N_cj*cj_population_size
            st_mat = np.zeros((N_cj, len(T)))
            ev = sim.GetStatus(st_spikes)[0]['events']
            t = ev['times'] # time of each spike (in simulation time)
            sp = ev['senders'] # sender of each spike (cell ID)
            for i in range(len(T)-1):
                idx = (t>T[i])*(t<T[i+1]) # find all spikes in the bin
                sender_list = sp[np.where(idx)] # get the senders of each spike in the bin#
                for j in sender_list:
                    st_mat[(j-offset)//cj_population_size][i] += 1
            ax[0].set_title("Stationary Conjunctive ring spike history")
            plot = ax[0].imshow(st_mat, cmap='hot', interpolation='nearest')
            if(cj_population_size>1):
                ax[0].set_ylabel("Population index")
            else:
                ax[0].set_ylabel("Neuron index")
            plt.colorbar(plot,ax=ax[0],pad=ringhist_pad)

        fig.tight_layout()
        plt.savefig("results/"+folder+"/ring_hist/Ring histories " + str(myVel) +".png", dpi=150,bbox_inches="tight")
        plt.clf()

    T1 = np.arange(0,(len(theta)*dt),dt)   # 20ms bins
    offset = N_ex + N_in + 1
    ## Diagnose CJ spikes
    ev = sim.GetStatus(l_spikes)[0]['events']
    t = ev['times'] # time of each spike (in simulation time)
    sp = ev['senders'] # sender of each spike (cell ID)
    rate = np.zeros(len(T1)-1)
    for i in range(len(T1)-1):
        idx = (t>T1[i])*(t<T1[i+1]) # find all spikes in the bin
        sender_list = sp[np.where(idx)] # get the senders of each spike in the bin
        #print(sender_list)
        pop_list = np.zeros(N_cj)
        for j in sender_list:
            pop_list[(j-offset)//cj_population_size] += 1
        #print(pop_list)
        mode = pop_list.max() # find most common sender
        rate[i] = mode # spikerate in Hz
    #print("AHV spikes: " + str(rate))
    average_rate = rate.sum()/len(rate) #avg per timestep
    average_rate = average_rate * 50 #avg in seconds
    print("cj Hz: " + str(average_rate))
    average_cj_rate = average_rate

    ## ST spike rate
    ev = sim.GetStatus(st_spikes)[0]['events']
    t = ev['times'] # time of each spike (in simulation time)
    sp = ev['senders'] # sender of each spike (cell ID)
    modes = np.zeros(len(T1))
    modes[:] = np.nan
    rate = np.zeros(len(T1)-1)
    for i in range(len(T1)-1):
        idx = (t>T1[i])*(t<T1[i+1]) # find all spikes in the bin
        sender_list = sp[np.where(idx)] # get the senders of each spike in the bin
        occurence_count = Counter(sender_list)
        mode = occurence_count.most_common(1) # find most common sender
        if(len(mode)>0):
            rate[i] = mode[0][1] # spikerate in Hz
        else:
            rate[i] = 0
    average_rate = rate.sum()/len(rate) #avg per timestep
    average_rate = average_rate * 50 #avg in seconds
    print("st Hz: " + str(average_rate))

    ## ex spike rate
    ev = sim.GetStatus(exc_spikes)[0]['events']
    t = ev['times'] # time of each spike (in simulation time)
    sp = ev['senders'] # sender of each spike (cell ID)
    modes = np.zeros(len(T1))
    modes[:] = np.nan
    rate = np.zeros(len(T1)-1)
    for i in range(len(T1)-1):
        idx = (t>T1[i])*(t<T1[i+1]) # find all spikes in the bin
        sender_list = sp[np.where(idx)] # get the senders of each spike in the bin
        occurence_count = Counter(sender_list)
        mode = occurence_count.most_common(1) # find most common sender
        if(len(mode)>0):
            rate[i] = mode[0][1] # spikerate in Hz
        else:
            rate[i] = 0
    average_rate = rate.sum()/len(rate) #avg per timestep
    average_rate = average_rate * 50 #avg in seconds
    print("ex Hz: " + str(average_rate))
    average_ex_rate = average_rate

    T2 = np.arange(0,(len(theta)*dt),dt*2)   # 40ms bins

    # get the spike times of the cells from the spike recorder
    ev = sim.GetStatus(exc_spikes)[0]['events']
    t = ev['times'] # time of each spike (in simulation time)
    sp = ev['senders'] # sender of each spike (cell ID)
    #find the most active cell in each 40ms bin
    modes = np.zeros(len(T2))
    modes[:] = np.nan
    for i in range(len(T2)-1):
        idx = (t>T2[i])*(t<T2[i+1]) # find all spikes in the bin
        lst = sp[np.where(idx)] # get the senders of those spikes
        occurence_count = Counter(lst)
        mode = occurence_count.most_common(1) # find most common sender
        if len(mode):
            modes[i] = mode[0][0]
    step = (2*np.pi)/N_ex
    modes = (modes*step) - np.pi

    if  myVel >= 5:
        # fig, ax = plt.subplots(1, 1,figsize=(5, 2),facecolor='w')
        fig, ax = plt.subplots(1, 1,figsize=(8, 3),facecolor='w')
        ax.set_xlabel('Time (s)')
        ax.set_ylabel('Head angle (deg)')
        ax.set_title('AHV = ' + str(myVel) + " " + deg_sign + "/s")

        theta_wrap = (theta + np.pi) % (2*np.pi) - np.pi
        #print(theta_wrap)
        ax.plot(T2/1000,modes*(180/np.pi),'.',label='HD signal',color='steelblue')
        ax.plot(np.array(time)/1000,(theta_wrap[:len(time)])*(180/np.pi),'.',markersize=.5,label='ground truth',color='black')
        ax.set_xlim([0,sim_len/1000])
        ax.set_yticks([-180,-135,-90,-45,0,45,90,135,180])
        #ax.set_yticks([0,45,90,135,180])
        #ax.set_ylim([-10, 190])
        #ax2 = ax.twinx()
        #ax2.plot(T1[1:]/1000,rate, 'ro-', markersize=.5, alpha=0.6, linewidth=1)
        #ax2.set_ylabel("AHV cell firerate per timestep",color="red",fontsize=14)
        #ax2.set_ylim([0, 8])

        fig.legend(bbox_to_anchor=(1.05, 1),loc='upper left', borderaxespad=0.)
        plt.tight_layout()
        plt.savefig("results/"+folder+"/drift_hist/"+str(myVel)+"_drift.png", dpi=300,bbox_inches="tight")
        plt.clf()

    # Compare against ground truth
    nanidx = np.where(~np.isnan(modes[:-1]))
    modes = modes[nanidx]
    T2=T2[nanidx]

    est = np.unwrap(modes)
    groundTruth = np.unwrap(theta[:len(time)])

    estimate = np.interp(time, T2, est)

    hd_estimate = np.vstack([T2,modes])
    np.save(f'results/{folder}/{folder}_estimate.npy',hd_estimate)

    # Find net RMSE
    fig, ax = plt.subplots(1, 1,figsize=(5, 2),facecolor='w')

    d = (estimate-groundTruth)

    if  myVel >= 5:
        plt.plot(np.array(time)/1000,abs(d)*(180/np.pi),color='steelblue')
        plt.xlabel('Time (s)')
        plt.ylabel('Abs Error (deg)')
        plt.xlim([0,sim_len/1000])
        plt.yticks([-45,0,45,90,135,180])
        plt.tight_layout()
        plt.savefig(f'results/{folder}/{folder}_error.png', bbox_inches="tight")
        plt.clf

    RMSE = np.sqrt(np.sum(d**2)/len(d))
    print("RMSE: " + str(RMSE*(180/np.pi)))

    drift = d[-1]*(180/np.pi) * 50 / len(theta) # drift per second

    print("Vel: " + str(myVel))
    print("Ivel: " + str(Ivel[1]))
    print("Total drift: " + str(d[-1]*(180/np.pi)))
    print("Drift per second: " + str(drift))

    if not save_data:
        return
    veldrift_f = open("results/" + folder + "/vel_drift_data.txt", "a")
    veldrift_f.write(str(myVel) + " " + str(drift) + " " + str(average_ex_rate) + " " + str(average_cj_rate) + "\n")
    veldrift_f.close()

    # draw all drift against vel values
    X = []
    Y = []
    Z = []

    f = open("results/"+folder+"/vel_drift_data.txt")
    for row in f:
        row = row.split(' ')
        X.append(float(row[0]))
        Y.append(float(row[1]))
        Z.append(float(row[3]))

    Y = [y for _,y in sorted(zip(X,Y))]
    Z = [z for _,z in sorted(zip(X,Z))]
    X.sort()

    fig,ax = plt.subplots()
    ax.hlines(0, 0, 1000, 'k', alpha=0.5)
    ax.plot(X, Y, 'bo-', markersize=3)
    ax.set_xlim([0, 80])
    ax.set_ylabel("Rate of Drift (" + deg_sign + "/s)",color="Blue",fontsize=14)
    ax.set_xlabel("Fixed AHV (" + deg_sign + "/s)",fontsize=14)
    ax2=ax.twinx()
    ax2.plot(X, Z, 'ro-', markersize=0.5, alpha=0.7)
    ax2.set_ylabel("Average peak CJ firerate (Hz)",color="red",fontsize=14)
    ax2.set_ylim([20, 70])
    fig.savefig("results/"+folder+"/DriftThruVel.png")
    plt.clf()
    print("DONE")

    plt.close('all')

loop = True
if(loop):
    start = 5
    end = 80
    step = 1
    for i in np.arange(start, end+step, step):
        print("\n\nRunning vel " + str(i))
        run(i, graph_end = end)
    print("\nCompleted batch " + folder + "\n")
else:
    run(50, save_data = False)


import mock

from treq.test.util import TestCase

from treq._utils import default_reactor, default_pool, set_global_pool

class DefaultReactorTests(TestCase):
    def test_passes_reactor(self):
        mock_reactor = mock.Mock()

        self.assertEqual(default_reactor(mock_reactor), mock_reactor)

    def test_uses_default_reactor(self):
        from twisted.internet import reactor
        self.assertEqual(default_reactor(None), reactor)

class DefaultPoolTests(TestCase):
    def setUp(self):
        set_global_pool(None)

        pool_patcher = mock.patch('treq._utils.HTTPConnectionPool')

        self.HTTPConnectionPool = pool_patcher.start()
        self.addCleanup(pool_patcher.stop)

        self.reactor = mock.Mock()

    def test_persistent_false(self):
        self.assertEqual(
            default_pool(self.reactor, None, False),
            self.HTTPConnectionPool.return_value
        )

        self.HTTPConnectionPool.assert_called_once_with(
            self.reactor, persistent=False
        )

    def test_pool_none_persistent_none(self):
        self.assertEqual(
            default_pool(self.reactor, None, None),
            self.HTTPConnectionPool.return_value
        )

        self.HTTPConnectionPool.assert_called_once_with(
            self.reactor, persistent=True
        )

    def test_pool_none_persistent_true(self):
        self.assertEqual(
            default_pool(self.reactor, None, True),
            self.HTTPConnectionPool.return_value
        )

        self.HTTPConnectionPool.assert_called_once_with(
            self.reactor, persistent=True
        )

    def test_cached_global_pool(self):
        pool1 = default_pool(self.reactor, None, None)

        self.HTTPConnectionPool.return_value = mock.Mock()

        pool2 = default_pool(self.reactor, None, True)

        self.assertEqual(pool1, pool2)

    def test_specified_pool(self):
        pool = mock.Mock()

        self.assertEqual(
            default_pool(self.reactor, pool, None),
            pool
        )

        self.HTTPConnectionPool.assert_not_called()


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

from django.db import models, migrations

class Migration(migrations.Migration):

    dependencies = [
        ('sponsors', '0001_initial'),
    ]

    operations = [
        migrations.AlterField(
            model_name='sponsor',
            name='logo',
            field=models.ImageField(upload_to='', blank=True),
        ),
    ]


from django.urls import path
from django.views.generic import RedirectView

from . import views

app_name = 'project'

urlpatterns = [
    path('', RedirectView.as_view(url='projects'), name='_projects'),
    path('projects/', views.projects, name='projects'),
    path('recent/', views.recent_project, name='recent'),
    path('create/', views.create_project, name='create'),
    path('join/', views.join_project, name='join'),
    path('<str:project_key>/', RedirectView.as_view(url='board'), name='_board'),
    path('<slug:slug>/board/', views.board, name='board'),
    path('<slug:slug>/issues/', views.issues, name='issues'),
    path('<slug:slug>/log/', views.log, name='log'),
    path('<slug:slug>/create/', views.create_issue, name='create_issue'),
    path('<slug:slug>/create_sprint/', views.create_sprint, name='create_sprint'),
    path('<slug:slug>/edit_sprint/', views.edit_sprint, name='edit_sprint'),
    path('<slug:slug>/delete_sprint/', views.delete_sprint, name='delete_sprint'),
    path('<slug:project_slug>/issue/<slug:issue_slug>/', views.issue, name='issue'),
    path('<slug:project_slug>/delete_issue/<slug:issue_slug>/', views.delete_issue, name='delete_issue'),
]


"""empty message

Revision ID: 8dc6b7729d49
Revises: fde7518177ce
Create Date: 2019-02-16 23:06:18.550438

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '8dc6b7729d49'
down_revision = 'fde7518177ce'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('atributos',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.PrimaryKeyConstraint('id')
    )
    op.create_table('usuarios',
    sa.Column('id', sa.Integer(), nullable=False),
    sa.Column('login', sa.String(length=100), nullable=False),
    sa.Column('senha', sa.String(length=100), nullable=False),
    sa.Column('atributos_id', sa.Integer(), nullable=False),
    sa.ForeignKeyConstraint(['atributos_id'], ['atributos.id'], ondelete='CASCADE'),
    sa.PrimaryKeyConstraint('id'),
    sa.UniqueConstraint('login')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('usuarios')
    op.drop_table('atributos')
    # ### end Alembic commands ###


#!/usr/bin/env python3

import argparse
import torch

def main(args):
    ckpt = torch.load(args.checkpoint)
    ns = ckpt["args"]

    for line in ns.__dict__.items():
        print(line)

def parse_args():
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument(
        "--checkpoint", type=str, required=True)

    return parser.parse_args()

if __name__ == "__main__":
    main(parse_args())


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import json
import requests
from bs4 import BeautifulSoup

link = '''https://api-zero.livere.com/v1/comments/list?callback=jQuery112409167777692145258_1515115423265&limit=10&repSeq=
3871836&requestPath=%2Fv1%2Fcomments%2Flist&consumerSeq=1020&livereSeq=28583&smartloginSeq=5154&_=1515115423267'''
headers = {
    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36',
    'Host': 'api-zero.livere.com'
}
response = requests.get(link, headers = headers)

json_data = json.loads(response.text)
comment_list = json_data['data']['comments']
for each_comment in comment_list:
    message = comment_list[each_comment]['content']
    print(message)


"""create table comment

Revision ID: 9336eef458e7
Revises: 9d8c8c38a1d0
Create Date: 2022-03-30 21:37:02.080429

"""
from alembic import op
import sqlalchemy as sa
from sqlalchemy.dialects.postgresql import UUID

# revision identifiers, used by Alembic.
revision = "9336eef458e7"
down_revision = "d66baafab5ec"
branch_labels = None
depends_on = None

def upgrade():
    NOW = sa.text("NOW()")

    op.create_table(
        "comment",
        sa.Column("id", sa.Integer, autoincrement=True, primary_key=True),
        sa.Column("uid", UUID, server_default=sa.func.uuid_generate_v4()),
        sa.Column("created_at", sa.DateTime, nullable=False, server_default=NOW),
        sa.Column(
            "updated_at", sa.DateTime, nullable=False, server_default=NOW, onupdate=NOW
        ),
        sa.Column("body", sa.TEXT),
        sa.Column(
            "author_id", sa.Integer, sa.ForeignKey("user.id", ondelete="CASCADE")
        ),
        sa.Column(
            "track_id", sa.Integer, sa.ForeignKey("track.id", ondelete="CASCADE")
        ),
    )

def downgrade():
    op.drop_table("comment")


import functools

import numpy as np

from skeleton.rotational_operators import get_directions_list

"""
Following is an application of memoization - pre-generating a look up array.
In this algorithm the look up array is of length (2 ** 26)
that has all possible configurations of binary strings of length 26 representing
26 voxels around a voxel at origin in a cube as indices and values as
a boolean that says the voxel can be removed or not (as it belongs to the boundary
not the skeleton) as in reference paper
A Parallel 3D 12-Subiteration Thinning Algorithm Kálmán Palágyi,Graphical Models and Image Processing
Volume 61, Issue 4, July 1999, Pages 199-221 Attila Kuba, 1999
"""

class Templates:
    def __init__(self, *args):
        (self.a, self.b, self.c, self.d, self.e, self.f, self.g, self.h, self.i, self.j, self.k, self.l, self.m, self.n,
         self.o, self.p, self.q, self.r, self.s, self.t, self.u, self.v, self.w, self.x, self.y, self.z) = [arg for arg
                                                                                in args]

    def first_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.j) & (not self.k) & (not self.l) &
                  (not self.r) & (not self.s) & (not self.t) & self.p &
                  (self.d | self.e | self.f | self.m | self.n | self.u | self.v | self.w | self.g | self.h | self.i |
                   self.o | self.q | self.x | self.y | self.z))
        return result

    def second_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.d) & (not self.e) & (not self.f) &
                  (not self.g) & (not self.h) & (not self.i) & self.v &
                  (self.r | self.s | self.t | self.j | self.k | self.l | self.m | self.n | self.u | self.w |
                   self.o | self.p | self.q | self.x | self.y | self.z))
        return result

    def third_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.j) & (not self.k) & (not self.l) &
                  (not self.r) & (not self.s) & (not self.t) & (not self.d) &
                  (not self.e) & (not self.f) & (not self.g) & (not self.h) & (not self.i) & self.y & (self.m | self.n |
                  self.u | self.w | self.o | self.q | self.x | self.z))
        return result

    def fourth_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.k) & (not self.e) & (not (self.d & self.j)) &
                  (not (self.l & self.f)) & self.p & self.v)
        return result

    def fifth_template(self):
        result = ((not self.a) & (not self.b) & (not self.k) & (not self.e) & self.c & self.v &
                  self.p & (not (self.j & self.d)) & (self.l ^ self.f))
        return result

    def sixth_template(self):
        result = (self.a & self.v & self.p & (not self.b) & (not self.c) & (not self.k) & (not self.e) &
                  (not (self.l & self.f)) & (self.j ^ self.d))
        return result

    def seventh_template(self):
        result = ((not self.a) & (not self.b) & (not self.k) & (not self.e) & self.n & self.v & self.p &
                  (not (self.j & self.d)))
        return result

    def eighth_template(self):
        result = ((not self.b) & (not self.c) & (not self.k) & (not self.e) & self.m & self.v & self.p &
                  (not (self.l & self.f)))
        return result

    def ninth_template(self):
        result = ((not self.b) & (not self.k) & (not self.e) & self.a & self.n & self.v & self.p & (self.j ^ self.d))
        return result

    def tenth_template(self):
        result = ((not self.b) & (not self.k) & (not self.e) & self.c & self.m & self.v & self.p & (self.l ^ self.f))
        return result

    def eleventh_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.j) & (not self.k) & (not self.l) &
                  (not self.r) & (not self.s) & (not self.t) & (not self.d) &
                  (not self.e) & (not self.g) & (not self.h) & self.q & self.y)
        return result

    def twelveth_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.j) & (not self.k) & (not self.l) &
                  (not self.r) & (not self.s) & (not self.t) & (not self.e) &
                  (not self.f) & (not self.h) & (not self.i) & self.o & self.y)
        return result

    def thirteenth_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.j) & (not self.k) & (not self.r) &
                  (not self.s) & (not self.d) & (not self.e) & (not self.f) &
                  (not self.g) & (not self.h) & (not self.i) & self.w & self.y)
        return result

    def fourteenth_template(self):
        result = ((not self.a) & (not self.b) & (not self.c) & (not self.d) & (not self.e) & (not self.f) &
                  (not self.g) & (not self.h) & (not self.i) &
                  (not self.k) & (not self.l) & (not self.s) & (not self.t) & self.u & self.y)
        return result

def _get_voxel_deletion_flag(neighbor_values, direction):
    """
    Returns self.a flag saying self.voxel should be deleted or not

    Parameters
    ----------
    neighbor_values : list
        list of first order neighborhood (26 voxels) of a nonzero value at origin

    direction : array
       transformation array describing rotation of cube to remove boundary voxels in a different direction

    Returns
    -------
    should_voxel_be_deleted : boolean
        0 => should not be deleted
        1 => should be deleted

    """
    assert len(neighbor_values) == 27
    # reshape neighbor_values to a 3 x 3 x 3 cube
    neighbor_matrix = np.reshape(neighbor_values, (3, 3, 3))
    # transform neighbor_values to direction
    neighbor_values = get_directions_list(neighbor_matrix)[direction]
    neighbor_values = list(np.reshape(neighbor_values, 27))
    del(neighbor_values[13])
    # assign 26 voxels in a 2nd ordered neighborhood of a 3D voxels as 26 alphabet variables
    neighbor_values = tuple(neighbor_values)
    # insert aplhabetical variables into equations of templates for deleting the boundary voxel
    template = Templates(*neighbor_values)
    should_voxel_be_deleted = functools.reduce(lambda x, y: x | y, [template.first_template(),
                                                                    template.second_template(),
                                                                    template.third_template(),
                                                                    template.fourth_template(),
                                                                    template.fifth_template(),
                                                                    template.sixth_template(),
                                                                    template.seventh_template(),
                                                                    template.eighth_template(),
                                                                    template.ninth_template(),
                                                                    template.tenth_template(),
                                                                    template.eleventh_template(),
                                                                    template.twelveth_template(),
                                                                    template.thirteenth_template(),
                                                                    template.fourteenth_template()])
    return should_voxel_be_deleted

def generate_lookup_array(stop=2**26, direction=0):
    """
    Returns lookuparray

    Parameters
    ----------
    stop : int
    integer describing the length of array

    direction : int
       describing nth rotation of cube to remove boundary voxels in a different direction

    Returns
    -------
    lookup_array : array
        value at an index of the array = 0 => should not be deleted
        value at an index of the array = 1 => should be deleted

    Notes
    ------
    This program is run once, and the array is saved as lookuparray.npy in
    the same folder in main function. It doesn't have to be run again unless if templates are changed

    """
    lookup_array = np.zeros(stop, dtype=bool)
    for item in range(0, stop):
        # convert the decimal number to a binary string
        neighbor_values = [(item >> digit) & 0x01 for digit in range(26)]
        # if it's a single non zero voxel in the 26 neighbors
        if np.sum(neighbor_values) == 1:
            lookup_array[item] = 0
        else:
            # voxel at origin/center of the cube should be nonzero, so insert
            neighbor_values.insert(13, 1)
            lookup_array[item] = _get_voxel_deletion_flag(neighbor_values, direction)
    return lookup_array

if __name__ == '__main__':
    # generating and saving all the 12 lookuparrays
    for index in range(12):
        lookup_array = generate_lookup_array(2 ** 26, index)
        np.save("lookuparray%i.npy" % (index + 1), lookup_array)


import asyncio
import dataclasses
import io
import os
from typing import AsyncIterator, BinaryIO, Optional, Union

from modal.exception import ExecutionError
from modal_proto import api_pb2
from modal_utils.async_utils import retry
from modal_utils.blob_utils import use_md5
from modal_utils.grpc_utils import retry_transient_errors
from modal_utils.hash_utils import get_md5_base64, get_sha256_hex
from modal_utils.http_utils import http_client_with_tls
from modal_utils.logger import logger

# Max size for function inputs and outputs.
MAX_OBJECT_SIZE_BYTES = 1024 * 1024  # 1MB

#  If a file is LARGE_FILE_LIMIT bytes or larger, it's uploaded to blob store (s3) instead of going through grpc
#  It will also make sure to chunk the hash calculation to avoid reading the entire file into memory
LARGE_FILE_LIMIT = 1024 * 1024  # 1MB

# Max parallelism during map calls
BLOB_MAX_PARALLELISM = 10

@retry(n_attempts=5, base_delay=0.5, timeout=None)
async def _upload_to_url(upload_url: str, content_md5: str, payload: Union[bytes, BinaryIO]) -> None:
    async with http_client_with_tls(timeout=None) as session:
        headers = {"content-type": "application/octet-stream"}

        if use_md5(upload_url):
            headers["Content-MD5"] = content_md5

        wrapped_payload: Union[bytes, BinaryIO]
        if isinstance(payload, bytes) and len(payload) > 100_000:
            wrapped_payload = io.BytesIO(payload)
        else:
            wrapped_payload = payload

        async with session.put(upload_url, data=wrapped_payload, headers=headers) as resp:
            # S3 signal to slow down request rate.
            if resp.status == 503:
                logger.warning("Received SlowDown signal from S3, sleeping for 1 second before retrying.")
                await asyncio.sleep(1)

            if resp.status != 200:
                text = await resp.text()
                raise ExecutionError(f"Put to url failed with status {resp.status}: {text}")

async def _blob_upload(content_md5: str, payload: Union[bytes, BinaryIO], stub) -> str:
    req = api_pb2.BlobCreateRequest(content_md5=content_md5)
    resp = await retry_transient_errors(stub.BlobCreate, req)

    blob_id = resp.blob_id
    target = resp.upload_url

    await _upload_to_url(target, content_md5, payload)

    return blob_id

async def blob_upload(payload: bytes, stub) -> str:
    if isinstance(payload, str):
        logger.warning("Blob uploading string, not bytes - auto-encoding as utf8")
        payload = payload.encode("utf8")
    content_md5 = get_md5_base64(payload)
    return await _blob_upload(content_md5, payload, stub)

async def blob_upload_file(file_obj: BinaryIO, stub) -> str:
    content_md5 = get_md5_base64(file_obj)
    file_obj.seek(0)
    return await _blob_upload(content_md5, file_obj, stub)

@retry(n_attempts=5, base_delay=0.1, timeout=None)
async def _download_from_url(download_url) -> bytes:
    async with http_client_with_tls(timeout=None) as session:
        async with session.get(download_url) as resp:
            # S3 signal to slow down request rate.
            if resp.status == 503:
                logger.warning("Received SlowDown signal from S3, sleeping for 1 second before retrying.")
                await asyncio.sleep(1)

            if resp.status != 200:
                text = await resp.text()
                raise ExecutionError(f"Get from url failed with status {resp.status}: {text}")
            return await resp.read()

async def blob_download(blob_id, stub) -> bytes:
    # convenience function reading all of the downloaded file into memory
    req = api_pb2.BlobGetRequest(blob_id=blob_id)
    resp = await retry_transient_errors(stub.BlobGet, req)

    return await _download_from_url(resp.download_url)

async def blob_iter(blob_id, stub) -> AsyncIterator[bytes]:
    req = api_pb2.BlobGetRequest(blob_id=blob_id)
    resp = await retry_transient_errors(stub.BlobGet, req)
    download_url = resp.download_url
    async with http_client_with_tls(timeout=None) as session:
        async with session.get(download_url) as resp:
            # S3 signal to slow down request rate.
            if resp.status == 503:
                logger.warning("Received SlowDown signal from S3, sleeping for 1 second before retrying.")
                await asyncio.sleep(1)

            if resp.status != 200:
                text = await resp.text()
                raise ExecutionError(f"Get from url failed with status {resp.status}: {text}")

            async for chunk in resp.content.iter_any():
                yield chunk

@dataclasses.dataclass
class FileUploadSpec:
    filename: str
    mount_filename: str

    use_blob: bool
    content: Optional[bytes]  # typically None if using blob, required otherwise
    sha256_hex: str
    size: int

def get_file_upload_spec(filename: str, mount_filename: str) -> FileUploadSpec:
    # Somewhat CPU intensive, so we run it in a thread/process
    size = os.path.getsize(filename)
    if size >= LARGE_FILE_LIMIT:
        use_blob = True
        content = None
        with open(filename, "rb") as fp:
            sha256_hex = get_sha256_hex(fp)
    else:
        use_blob = False
        with open(filename, "rb") as fp:
            content = fp.read()
        sha256_hex = get_sha256_hex(content)
    return FileUploadSpec(
        filename, mount_filename, use_blob=use_blob, content=content, sha256_hex=sha256_hex, size=size
    )


import torch
import torch.nn as nn
import torch.nn.functional as F
from torchvision.transforms import Resize
import random
import matplotlib.pyplot as plt
import gc
gc.collect(2)
import numpy as np
import buteo as beo
import os

def patience_calculator(epoch, t_0, t_m, max_patience=50):
    """ Calculate the patience for the scheduler. """
    if epoch <= t_0:
        return t_0

    p = [t_0 * t_m ** i for i in range(100) if t_0 * t_m ** i <= epoch][-1]
    if p > max_patience:
        return max_patience

    return p

class TiledMSE(nn.Module):
    """
    Calculates the MSE at full image level and at the pixel level and weights the two.
    result = (sum_mse * (1 - bias)) + (mse * bias)
    """

    def __init__(self, bias=0.2):
        super(TiledMSE, self).__init__()
        self.bias = bias

    def forward(self, y_pred, y_true):
        y_pred_sum = torch.sum(y_pred, dim=(1, 2, 3)) / (y_pred.shape[1] * y_pred.shape[2] * y_pred.shape[3])
        y_true_sum = torch.sum(y_true, dim=(1, 2, 3)) / (y_true.shape[1] * y_true.shape[2] * y_true.shape[3])

        sum_mse = ((y_pred_sum - y_true_sum) ** 2).mean()
        mse = ((y_pred - y_true) ** 2).mean()

        weighted = (sum_mse * (1 - self.bias)) + (mse * self.bias)

        return weighted

def render_s2_as_rgb(arr, channel_first=False):
    # If there are nodata values, lets cast them to zero.
    if np.ma.isMaskedArray(arr):
        arr = np.ma.getdata(arr.filled(0))

    if channel_first:
        arr = beo.channel_first_to_last(arr)
    # Select only Blue, green, and red. Then invert the order to have R-G-B
    rgb_slice = arr[:, :, 0:3][:, :, ::-1]

    # Clip the data to the quantiles, so the RGB render is not stretched to outliers,
    # Which produces dark images.
    rgb_slice = np.clip(
        rgb_slice,
        np.quantile(rgb_slice, 0.02),
        np.quantile(rgb_slice, 0.98),
    )

    # The current slice is uint16, but we want an uint8 RGB render.
    # We normalise the layer by dividing with the maximum value in the image.
    # Then we multiply it by 255 (the max of uint8) to be in the normal RGB range.
    rgb_slice = (rgb_slice / rgb_slice.max()) * 255.0

    # We then round to the nearest integer and cast it to uint8.
    rgb_slice = np.rint(rgb_slice).astype(np.uint8)

    return rgb_slice

def visualise(x, y, y_pred=None, images=5, channel_first=False, vmin=0, vmax=1, save_path=None):
    rows = images
    if y_pred is None:
        columns = 2
    else:
        columns = 3
    i = 0
    fig = plt.figure(figsize=(10 * columns, 10 * rows))

    for idx in range(0, images):
        arr = x[idx]
        rgb_image = render_s2_as_rgb(arr, channel_first)

        i = i + 1
        fig.add_subplot(rows, columns, i)
        plt.imshow(rgb_image)
        plt.axis('on')
        plt.grid()

        i = i + 1
        fig.add_subplot(rows, columns, i)
        plt.imshow(y[idx], vmin=vmin, vmax=vmax, cmap='magma')
        plt.axis('on')
        plt.grid()

        if y_pred is not None:
            i = i + 1
            fig.add_subplot(rows, columns, i)
            plt.imshow(y_pred[idx], vmin=vmin, vmax=vmax, cmap='magma')
            plt.axis('on')
            plt.grid()

    fig.tight_layout()

    del x
    del y
    del y_pred

    if save_path is not None:
        plt.savefig(save_path)
    plt.close()


import models
from proto import Handler

class CommentHandler(Handler):
    # This is an AJAX handler. Do not navigate here

    def get(self, post_id):
        '''Retrieves comments for post. Sends them to user in JSON
        '''
        post = models.Post.get_by_id(int(post_id))
        if post:
            comments = post.comments
            obj = {'success': True}
            if comments and self.user:
                obj['result'] = self.render_str('comment.html',
                                                comments=comments,
                                                user=self.user)
            elif comments:
                obj['result'] = self.render_str('comment.html',
                                                comments=comments)
        else:
            obj = {'success': False}
        self.json(obj)

    def post(self, post_id):
        '''Takes a new comment as input. If user is logged in comment is
        added to db, rendered using jinja, and returned to the user in JSON.
        If error, success: False and an error message is sent to user in JSON.
        '''

        comment = self.request.get('comment')
        post = models.Post.get_by_id(int(post_id))

        @self.logged_in
        def post_comment(post, comment):
            if not self.valid_post:
                return
            elif not comment:
                obj = {
                       'success': False,
                       "message": 'You have to type a comment'
                }
            else:
                comment = models.Comment(user=self.user, comment=comment,
                                         post=post)
                comment.put()
                obj = {
                        'success': True,
                        'result': self.render_str('newcommentresponse.html',
                                                  comment=comment,
                                                  user=self.user)
                }
                self.json(obj)

        post_comment(post, comment)


import pytest
import random
import string

from name import Name

@pytest.fixture
def name():
    return get_name()

def get_name():
    length = random.randint(1, 12)
    first = "".join(
        random.choices(
            string.ascii_uppercase + string.ascii_lowercase, k=length
        )
    )
    last = "".join(
        random.choices(string.ascii_uppercase + string.digits, k=length)
    )
    return Name(parts=[first, last], sort_order=(0, 1))

@pytest.fixture
def name_factory():
    class NameFactory(object):
        def get(self):
            name_factory = get_name()
            return name_factory

    return NameFactory()

def test_name_equals(name_factory) -> None:
    ret = 0
    # using loop on the small chace that the random names are the same
    for i in range(1, 10):
        name1 = name_factory.get()
        name2 = name_factory.get()
        if name1 != name2:
            ret += 1
    assert ret >= 5

def test_name_sort() -> None:
    name1 = Name(["Bob", "T", "Smith"])
    name2 = Name(["Zee", "Atlantic"], (1, 0))
    name3 = Name(["Zee", "Atlantic"], (0, 1))
    name4 = Name(["Zee", "Atlantic"])
    assert name1 > name2 and str(name1) < str(name2)
    assert name1 < name3
    assert name1 < name4

def test_name_repr() -> None:
    name = Name(["Niomi", "Turle"])
    name2 = eval(repr(name))
    print(name2)


#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import argparse
import os

USAGE = r"""
             %prog -e CTGCAG -o out_file \

             provide a Restriction endonuclease with cut site
             example 1: C:\Users\user\Desktop>C:\Users\user\AppData\Local\Programs\Python\Python35\python.exe 1.py -e TTCGA^A -o rest.txt \
                 TTCOT \
             example 2: C:\Users\user\Desktop>C:\Users\user\AppData\Local\Programs\Python\Python35\python.exe 1.py -o rest.txt \
                 CTGCAG \
                 CTGCA^G
          """

parser = argparse.ArgumentParser(description = USAGE)

# parser.add_argument('-e', "--restriction", action = "store", type= str, default = "CTGCA^G", help = "the restriction enzyme you choose")
parser.add_argument('-e', "--restriction", action = "store", type= str, dest = "RE", default = "CTGCA^G", help = "the restriction enzyme you choose")
parser.add_argument('-o', "--output", action = "store", type = argparse.FileType("w"))

args = parser.parse_args()

os.chdir("C:\\Users\\user\\Downloads\\")

def read_fasta(fp):
    name, seq = (None, [])
    for line in fp:
        line = line.rstrip()
        if line.startswith(">"):
            if name:
                yield (name, ''.join(seq))
            name, seq = (line, [])
        else:
            seq.append(line)
    if name:
        yield (name, ''.join(seq))

cut_site = args.RE.find("^")
enz_seq =args.RE.replace("^","")
print (enz_seq)

with open("test_restriction.fa") as f_handle:
    for contig_n, seq in read_fasta(f_handle):
        #print (args.restriction)
        print (args.RE)
        #args.output.write (seq+"##\n")
        enz_locus = -1
        while True:
            # find enzyme sequence location from the start
            enz_locus = seq.find(enz_seq, enz_locus + 1)
            # break if not found
            if enz_locus == -1:
                break
            args.output.write("{0}\n".format(enz_locus + cut_site))

args.output.close()

os.chdir("C:\\Users\\user\\Desktop\\")

def get_lines(file_obj):
    items = [""]*2
    #items = ("", "")
    for number, line in enumerate(file_obj):
        slot=number % 2
        items[slot] = line.rstrip()
        if number >=1:
            yield (items)

bed_file = open(args.output.name + ".bed", 'w')
#with open("rest.txt", 'r') as fobj:
with open(args.output.name, 'r') as fobj:
    for loci in get_lines(fobj):
        #print (loci)
        #length = abs(int(loci[0]) - int(loci[1]))
        if int(loci[0]) > int(loci[1]):

            length = int(loci[0]) - int(loci[1])
            bed_file.write("{0}\t{1}\t{2}\t{3}\n".format("test_contig", loci[1], loci[0], length))
        else:
            length = int(loci[1]) - int(loci[0])
            bed_file.write("{0}\t{1}\t{2}\t{3}\n".format("test_contig", loci[0], loci[1], length))

bed_file.close()


#!/usr/env/python

"""
Base Event Publisher and Subscriber

Implements the Event, Publisher and Subscriber classes, that provides a pub-
sub interface for broadcasting Events from Publishers (Subject) to Subscribers
(Observers). The Publisher is an Observer as well that enables rebroadcasting
of Events.

Classes:
- Event
- Subscriber
- Publisher
"""

from collections import abc
from dataclasses import dataclass
from enum import IntEnum
from typing import Callable, Generic, TypeVar, Union

from rx import Observer
from rx.subjects import Subject

T = TypeVar('T')

@dataclass
class Event(Generic[T]): # pylint: disable=E1136
  """
  Data class for an event. Each event has a specified event type,
  and the object associated with the event as context.

  Generics:
    T:  Contextual object associated with each Event.

  Attributes:
    kind:     The type of event.
    context:  The object associated with this event.
  """
  kind: IntEnum
  context: T

  def setparams(self, **kwargs):
    """
    Assigns the given keyword arguments as attributes to this Event.

    Args:
      kwargs:
        The arguments to set as attributes.
    """
    for k, v in kwargs.items():
      setattr(self, k, v)

class Subscriber(Observer, Generic[T]): # pylint: disable=E1136
  """
  Implements the wrapper for an Observer.

  Each Event is paired with an IntEnum event type (or kind), and invokes
  specific event handler methods for each Event type.

  Generics:
    T:  Contextual object associated with each Event.

  Extends:
    Observer

  Attributes:
    events:
      The registered Event types (kind).
      If None, no register Event types.
    eventmapper:
      A lambda method that maps each Event to a
      method name for a specific event handler.
    strict:
      Boolean flag whether or not to raise an exception
      when Event handler not found.
      - True:  Raise exception when handler not found.
      - False: Otherwise. Default.
  """
  events:       Union[IntEnum, None]
  eventmapper:  Callable[[Event[T]], str]
  strict:       bool

  def __init__(self, events: Union[IntEnum, None] = None):
    """
    Initialize this Subscriber with the given Event types.

    Args:
      events:
        The registered Event types (kind).
        If None, no register Event types.
    """
    def eventmapper(event: Event[T]) -> str:
      assert isinstance(event, Event)
      assert isinstance(event.kind, IntEnum)

      return f'on_{event.kind.name}'.lower()

    self.events = events
    self.eventmapper = eventmapper
    self.strict = False

  ### Methods: Event Handlers

  def on_next(self, event: Event[T]):
    """
    The Event handler when an Event occurs.

    Args:
      event:
        The next Event to occur.

    Raises:
      AttributeError:
        Event handler not found.
    """
    handle = self.eventmapper(event)
    if hasattr(self, handle):
      #print(f'{type(self).__name__}.{handle}')
      getattr(self, handle)(event)
    elif self.strict:
      raise AttributeError(handle)

  def on_completed(self):
    """
    The Event handler when no more Events.
    Subscription completed.
    """
    pass # Do nothing

  def on_error(self, exception: Exception):
    """
    The Event handler when an error occurs.

    Args:
      exception:
        The error that occurred.
    """
    pass # Do nothing

class Publisher(Subscriber[T], abc.Container):
  """
  Implements the wrapper for an Observer and a Subject.

  Broadcasts the Events to the subscribed Observers on the Subject.
  Each Event is paired with an IntEnum event type (or kind), and invokes
  specific event handler methods for each Event type.

  Generics:
    T:  Contextual object associated with each Event.

  Extends:
    Subscriber[T]
    abc.Container

  Attributes:
    subject:
      The Subject for Observers to subscribe to.
    presubj:
      The Subject for Observers to subscribe to, whose
      on_next are always called before, self.on_next
      and the subjects' on_next.
  """
  subject: Subject
  presubj: Subject

  def __init__(self, events: Union[IntEnum, None] = None):
    """
    Initialize this Publisher with the given Event types.

    Args:
      events:
        The registered Event types (kind).
        If None, no register Event types.
    """
    Subscriber.__init__(self, events)

    self.presubj = Subject()
    self.subject = Subject()

  ### Methods: Queries

  def __contains__(self, observer: Observer) -> bool:
    """
    Determine whether or not the given is already subscribed
    to this Publisher.

    Args:
      observer:
        The Observer to test whether or not
        already subscribed to this Publisher.

    Returns:
      True:   If already subscribed.
      False:  Otherwise.
    """
    return observer in self.subject.observers or \
           observer in self.presubj.observers

  ### Methods: Subscribe

  def subscribe(self, observer: Observer, before: bool = False):
    """
    Subscribes the given Observer to the subject of
    this Publisher when an Event occurs.

    Args:
      observer:
        The Observer to subscribe to.
      before:
        Boolean flag for whether or not to receive Event
        before self.on_next or after self.on_next.
        True, for before; False, otherwise.
    """
    assert observer not in self

    subject = self.presubj if before else self.subject
    subject.subscribe(observer)

  ### Methods: Broadcast

  def broadcast(self, event: Event, **kwargs):
    """
    Broadcast the given event to subscribed Observers.

    Args:
      event:
        The Event to be broadcasted.
      kwargs:
        The parameters to be added or modified
        within the given Event.
    """
    if hasattr(event, 'source') and event.source is self:
      return

    event.setparams(source=self, **kwargs)
    self.subject.on_next(event)

  ### Methods: Event Handlers

  def on_next(self, event: Event[T]):
    """
    The Event handler when an Event occurs.
    Broadcast next Event.

    Overrides:
      Subscriber.on_next

    Args:
      event:
        The next Event to occur.
    """
    if hasattr(event, 'source') and event.source is self:
      return

    self.presubj.on_next(event)

    try:
      if self.events:
        Subscriber.on_next(self, event)
    finally:
      self.broadcast(event)

  def on_completed(self):
    """
    The Event handler when no more Events. Subscription completed.
    Broadcast completion Event.

    Overrides:
      Subscriber.on_completed
    """
    self.presubj.on_completed()
    self.subject.on_completed()

  def on_error(self, exception: Exception):
    """
    The Event handler when an error occurs.
    Broadcast error Event.

    Overrides:
      Subscriber.on_error

    Args:
      exception:
        The error that occurred.
    """
    self.presubj.on_error(exception)
    self.subject.on_error(exception)


# -*- coding: UTF-8 -*-
"""PyRamen Homework Starter."""

# @TODO: Import libraries
import csv
from pathlib import Path

menuFile = Path('.\Resources/menu_data.csv')
salesFile = Path('.\Resources/sales_data.csv')

# @TODO: Initialize list objects to hold our menu and sales data
menu = []
sales = []

# @TODO: Read in the menu data into the menu list

with open (menuFile , 'r') as menuData:
    csvRead = csv.reader(menuData, delimiter = ',')
    # skip header record
    next(menuData)
    for  menuRow in menuData:
        menuRowStripped=menuRow.rstrip()
        each=[]
        each=menuRowStripped.split(',')
        menu.append(each)

# @TODO: Read in the sales data into the sales list

with open (salesFile , 'r') as salesData:
    csvRead = csv.reader(salesData, delimiter = ',')
    # skip header record
    next(salesData)
    for  salesRow in salesData:
        salesRowStripped=salesRow.rstrip()
        each=[]
        each=salesRowStripped.split(',')
        sales.append(each)

# @TODO: Initialize dict object to hold our key-value pairs of items and metrics
report = {}

# Initialize a row counter variable
row_count = 0

# @TODO: Loop over every row in the sales list object
for each_sale in sales:
    menu_item=each_sale[-1]
    quantity=each_sale[-2]
    if menu_item not in report:
        report={ menu_item:{"01-count":0,"02-revenue":0,"03-cogs":0,"04-profit":0} }
    else:
        #print (f"{menu_item} already exist in report!")
        pass

print (report)

    # Line_Item_ID,Date,Credit_Card_Number,Quantity,Menu_Item
    # @TODO: Initialize sales data variables

    # @TODO:
    # If the item value not in the report, add it as a new entry with initialized metrics
    # Naming convention allows the keys to be ordered in logical fashion, count, revenue, cost, profit

    # @TODO: For every row in our sales data, loop over the menu records to determine a match

        # Item,Category,Description,Price,Cost
        # @TODO: Initialize menu data variables

        # @TODO: Calculate profit of each item in the menu data

        # @TODO: If the item value in our sales data is equal to the any of the items in the menu, then begin tracking metrics for that item

            # @TODO: Print out matching menu data

            # @TODO: Cumulatively add up the metrics for each item key

        # @TODO: Else, the sales item does not equal any fo the item in the menu data, therefore no match

    # @TODO: Increment the row counter by 1

# @TODO: Print total number of records in sales data

# @TODO: Write out report to a text file (won't appear on the command line output)