import cv2
import numpy as np
import random
import glob
import math
import time
import matplotlib.pyplot as plt
import argparse

def FunMat(src_pts, dst_pts) :
	src_pts, T1 = normalizePoints(src_pts)
	dst_pts, T2 = normalizePoints(dst_pts)

	A = np.zeros((src_pts.shape[0], 9)).astype(int)
	for i in range(src_pts.shape[0]) :
		s, d = src_pts[i], dst_pts[i]
		A[i, :] = np.array([s[0]*d[0], s[0]*d[1], s[0], s[1]*d[0], s[1]*d[1], s[1], d[0], d[1], 1])

	# solution of Af = 0 using SVD
	Ui, Si, Vi = np.linalg.svd(A)
	F = (Vi[-1,:])
	F = F.reshape((3,3))

	Ui_, Si_, Vi_ = np.linalg.svd(F)
	Si_ = np.diag(Si_)
	Si_[2,2] = 0

	# # constrain F
	# make rank 2 by zeroing out last singular value
	F = Ui_@(Si_@Vi_)

	# unnormalize Fmatrix
	F = T2.T@(F@T1)

	return F

def normalizePoints(pts):
	pts_mean = np.mean(pts, axis=0)
	x_bar = pts_mean[0]
	y_bar = pts_mean[1]

	# origin of the new coordinate system should be located at the centroid of the image points
	x_s, y_s = pts[:,0] - x_bar, pts[:, 1] - y_bar

	# scale by the scaling factor
	s = (2/np.mean(x_s**2 + y_s**2))**(0.5)

	# construct transformation matrix (translation+scaling)
	T_S = np.diag([s,s,1])
	T_T = np.array([[1, 0, -x_bar],[0, 1, -y_bar],[0, 0, 1]])
	Ta = np.dot(T_S, T_T)

	x = np.column_stack((pts, np.ones(pts.shape[0])))
	x_norm = (Ta@x.T).T
	return x_norm, Ta

def FunMat1(src_pts, dst_pts) :
	A = np.zeros((8, 9)).astype(int)
	for i in range(src_pts.shape[0]) :
		s, d = src_pts[i], dst_pts[i]
		A[i, :] = np.array([s[0]*d[0], s[0]*d[1], s[0], s[1]*d[0], s[1]*d[1], s[1], d[0], d[1], 1])

	# solution of Af = 0 using SVD
	Ui, Si, Vi = np.linalg.svd(A)
	F = (Vi[-1,:] / Vi[-1,-1])
	F = F.reshape((3,3))

	return F

def estimateFunMatrix(img1, img2) :
	gray1 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY)
	gray2 = cv2.cvtColor(img2, cv2.COLOR_BGR2GRAY)

	# # Creating SIFT object
	sift = cv2.xfeatures2d.SIFT_create()

	kpts1, descs1 = sift.detectAndCompute(gray1,None)
	kpts2, descs2 = sift.detectAndCompute(gray2,None)

	# feature matching
	bf = cv2.BFMatcher(cv2.NORM_L2, crossCheck = True)
	matches = bf.match(descs1, descs2)
	dmatches = sorted(matches, key = lambda x:x.distance)

	## extract the matched keypoints
	src_pts  = np.array([kpts1[m.queryIdx].pt for m in dmatches]).astype(int)
	dst_pts  = np.array([kpts2[m.trainIdx].pt for m in dmatches]).astype(int)

	# drawMatches(img1, img2, src_pts, dst_pts)

	# Getting the inliers using RANSAC
	num_iterations = 2000 # default is 5000
	rows = src_pts.shape[0]
	max_error = 0.001
	Fmatrix = np.zeros((3, 3))
	maxinliers = 0
	finalidx = []

	for i in range(num_iterations) :
		indexes = random.sample(range(0, rows), 8)
		src = src_pts[indexes]
		dst = dst_pts[indexes]
		FM = FunMat(src, dst)
		test = np.zeros((rows, 1))
		tempidx = []
		for j in range(rows) :
			error = np.abs(np.array([[src_pts[j][0]], [src_pts[j][1]], [1]]).T @ FM @ np.array([[dst_pts[j][0]], [dst_pts[j][1]], [1]]))
			if error < max_error :
				tempidx.append(j)

		if len(tempidx) > maxinliers:
			maxinliers = len(tempidx)
			finalidx = tempidx
			Fmatrix = FM
			srcFinal = src_pts[tempidx]
			dstFinal = dst_pts[tempidx]

	print('---Fmatrix---')
	Fnmatrix = FunMat(srcFinal, dstFinal)
	Fnmatrix = Fnmatrix/Fnmatrix[2,2]
	print(Fnmatrix)
	return Fnmatrix, dstFinal, srcFinal

def drawMatches(img1, img2, keypoints1, keypoints2) :
	new_img = np.concatenate((img1, img2), axis=1)
	numkeypoints = len(keypoints1)
	r = 4
	thickness = 1

	for i in range(keypoints1.shape[0]) :
		end1 = keypoints1[i]
		end2 = (keypoints2[i][0]+img1.shape[1], keypoints2[i][1])

		cv2.line(new_img, end1, end2, (0,255,255), thickness)
		cv2.circle(new_img, end1, r, (0,0,255), thickness)
		cv2.circle(new_img, end2, r, (0,255,0), thickness)

	resized = cv2.resize(new_img, (1440, 720), interpolation = cv2.INTER_AREA)
	cv2.imshow('SIFT Matches', resized)
	cv2.waitKey(0)

def getEssentMatrix(Fmatrix, flag) :
	if flag == 1 :
		# Pendulumn
		## R1 C1
		K1 = np.array([[1729.05, 0, -364.24], [0, 1729.05, 552.22], [0, 0, 1]])
		K2 = np.array([[1729.05, 0, -364.24], [0, 1729.05, 552.22], [0, 0, 1]])
		b = 537.75
		f = K1[0, 0]
		vmin = 25
		vmax = 150
		ndisp = 180

	if flag == 2 :
		# Octagon
		K1 = np.array([[1742.11, 0, 804.90], [0, 1742.11, 541.22], [0, 0, 1]])
		K2 = np.array([[1742.11, 0, 804.90], [0, 1742.11, 541.22], [0, 0, 1]])
		b = 221.76
		f = K1[0, 0]
		vmin = 29
		vmax = 61
		ndisp = 100

	if flag == 3 :
		# curule
		## R4 C4
		K1 = np.array([[1758.23, 0, 977.42], [0, 1758.23, 552.15], [0, 0, 1]])
		K2 = np.array([[1758.23, 0, 977.42], [0, 1758.23, 552.15], [0, 0, 1]])
		b = 88.39
		f = K1[0, 0]
		vmin = 55
		vmax = 195
		ndisp = 220

	Ematrix = K1.T @ Fmatrix @ K2

	# singular values of E are not necessarily (1,1,0)
	U, D, V = np.linalg.svd(Ematrix)
	A = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 0]])
	Ematrix = U@A@V
	print()
	print('---Ematrix---')
	print(Ematrix)

	return Ematrix, K1, K2, b, f, vmin, vmax, ndisp

def getCameraPose(Ematrix) :
	U, D, V = np.linalg.svd(Ematrix)
	W = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]])

	C1, C2, C3, C4 = U[:,2], -U[:,2], U[:,2], -U[:,2]
	R1, R2, R3, R4 = U@W@V, U@W@V, U@W.T@V, U@W.T@V
	if np.linalg.det(R1) < 0 :
		C1 = np.negative(C1)
		R1 = np.negative(R1)

	if np.linalg.det(R2) < 0 :
		C2 = np.negative(C2)
		R2 = np.negative(R2)

	if np.linalg.det(R3) < 0  :
		C3 = np.negative(C3)
		R3 = np.negative(R3)

	if np.linalg.det(R4) < 0  :
		C4 = np.negative(C4)
		R4 = np.negative(R4)

	return C1, C2, C3, C4, R1, R2, R3, R4

def chiralityCheck(C, R, K1, K2, dstFinal, srcFinal) :
	maxcount = 0
	Cf, Rf = C[-1], R[-1]
	for i in range(4) :
		Ci = C[i]
		Ri = R[i]
		Ci = Ci.reshape((3,1))

		#RT matrix for C1 is identity.
		R1 = np.identity(3)
		T1 = np.zeros((3,1))
		I = np.identity(3)

		P1 = np.dot(K1, np.dot(R1, np.hstack((I, -T1.reshape(3,1)))))
		P2 = np.dot(K2, np.dot(Ri, np.hstack((I, -Ti.reshape(3,1)))))

		# RT1 = np.concatenate([np.eye(3), [[0],[0],[0]]], axis = -1)
		# P1 = K1 @ RT1 #projection matrix for C1

		## RT matrix for C2 is the R and T obtained from stereo calibration.
		# RT2 = np.concatenate([Ri, Ci], axis = -1)
		# P2 = K2 @ RT2 #projection matrix for C2
		count = 0
		for j in range(srcFinal.shape[0]) :
			src = srcFinal[i]
			dst = dstFinal[i]
			src3d = cv2.triangulatePoints(P1, P2, src.T, dst.T)
			src3d = src3d/src3d[3]

			r3 = Ri[2, :]
			print((src3d[:3] - Ci))
			print(r3)
			if r3@(src3d[:3] - Ci).flatten() > 0 :
				count += 1

		if count > maxcount :
			Cf = Ci
			Rf = Ri
			num = i
			maxcount = count

	return Cf, Rf, num

def stereo(path, scaling, windowsize, flag=1) :
	image_list = []
	for filename in glob.glob(f'{path}/*.png'):
		im = cv2.imread(filename)
		image_list.append(im)

	img1 = image_list[0]
	img2 = image_list[1]
	Fmatrix, dstFinal, srcFinal = estimateFunMatrix(img1, img2)

	drawMatches(img1, img2, srcFinal, dstFinal)
	Ematrix, K1, K2, b, f, vmin, vmax, ndisp = getEssentMatrix(Fmatrix, flag)
	C1, C2, C3, C4, R1, R2, R3, R4 = getCameraPose(Ematrix)
	C = [C1, C2, C3, C4]
	R = [R1, R2, R3, R4]
	print('C and R')
	print(C1)
	print(C2)
	print(C3)
	print(C4)

	print()
	print(R1)
	print(R2)
	print(R3)
	print(R4)

	## Cheirality Check for choosing R and C
	# Cf, Rf, num = chiralityCheck(C, R, K1, K2, dstFinal, srcFinal)
	# print(f'{num+1} is the best')

	## Get epipole line
	els, eld = getEpipoleLine(srcFinal, dstFinal, Fmatrix)
	# drawEpipoleLine(els, eld, img1, img2, srcFinal, dstFinal)

	## Get epipoles
	# eps, epd = getEpipoles(els, eld)

	## Rectification
	# H1, H2 = rectify(srcFinal, dstFinal, Fmatrix, eps, epd, img1, img2)
	ret, H1, H2 = cv2.stereoRectifyUncalibrated(srcFinal, dstFinal, Fmatrix, (img1.shape[1], img1.shape[0]))
	R1 = H1
	R2 = H2
	print('---Homography---')
	R1 = R1 / R1[2,2]
	R2 = R2 / R2[2,2]
	print(R1)
	print(R2)

	img1c = img1.copy()
	img2c = img2.copy()
	drawEpipoleLine(els, eld, img1c, img2c, srcFinal, dstFinal)

	trans_img1 = cv2.warpPerspective(img1, R1, (img1.shape[1], img1.shape[0]))
	trans_img2 = cv2.warpPerspective(img2, R2, (img1.shape[1], img1.shape[0]))

	trans_img1c = cv2.warpPerspective(img1c, R1, (img1.shape[1], img1.shape[0]))
	trans_img2c = cv2.warpPerspective(img2c, R2, (img1.shape[1], img1.shape[0]))
	nFmatrix = np.linalg.inv(H2.T) @ Fmatrix @ np.linalg.inv(H1)

	# drawEpipoleLine(els, eld, trans_img1, trans_img2, nsrcFinal, ndstFinal)
	scale_percent = scaling
	scalingfactor = scale_percent / 100
	width = int(img1.shape[1] * scalingfactor)
	height = int(img1.shape[0] * scalingfactor)
	ims = (width, height)
	ndisp = int(ndisp * scalingfactor)

	res1 = cv2.resize(trans_img1, ims, interpolation = cv2.INTER_AREA)
	res2 = cv2.resize(trans_img2, ims, interpolation = cv2.INTER_AREA)
	new_img = np.concatenate((res1, res2), axis=1)

	## Visulization
	res1c = cv2.resize(trans_img1c, ims, interpolation = cv2.INTER_AREA)
	res2c = cv2.resize(trans_img2c, ims, interpolation = cv2.INTER_AREA)
	new_imgc = np.concatenate((res1c, res2c), axis=1)

	cv2.imshow('Warp1', res1)
	cv2.imshow('Warp2', res2)
	cv2.imshow('Rectified', new_img)
	cv2.waitKey(0)

	cv2.imshow('Warp1 EpipoleLines', res1c)
	cv2.imshow('Warp2 EpipoleLines', res2c)
	cv2.imshow('Rectified EpipoleLines', new_imgc)
	cv2.waitKey(0)

	resized1 = cv2.cvtColor(res1, cv2.COLOR_BGR2GRAY)
	resized2 = cv2.cvtColor(res2, cv2.COLOR_BGR2GRAY)

	time1 = time.time()
	dimg, dpimg, dcimg, dpcimg = getDisparityMap(resized1, resized2, b, f, vmin, vmax, ndisp, windowsize)

	cv2.imwrite(f'DisparityHeat{flag}.png', dcimg)
	cv2.imwrite(f'DepthHeat{flag}.png', dpcimg)

	cv2.imwrite(f'DisparityGray{flag}.png', dimg)
	cv2.imwrite(f'DepthGray{flag}.png', dpimg)
	time2 = time.time()
	print(f'Time taken : {time2-time1}')

## Disparity Map
def getDisparityMap(img1, img2, b, f, vmin, vmax, ndisp, winsize) :
	# left on right
	img1c = img1.copy()
	h, w = img1c.shape[0], img1c.shape[1]
	windowSize = winsize
	halfWindow = math.floor((windowSize)/2)
	disparity = np.zeros((h, w), np.uint8)
	stride = 1

	print('Search : ', ndisp)
	for r in range(halfWindow, h-halfWindow, stride) :
		for c in range(halfWindow, w-halfWindow, stride) :
			pimg1 = img1[r-halfWindow : r+halfWindow, c-halfWindow : c+halfWindow]
			min_ssd = 15000
			disp = 0
			searchDistance = ndisp

			## search on same parallel line in second image
			## search before and after some distance from that point
			for distance in range(-searchDistance, searchDistance):
				c_dash = c + distance
				if (c_dash < w - halfWindow) and (c_dash > halfWindow):
					pimg2 = img2[r - halfWindow: r + halfWindow, c_dash - halfWindow : c_dash + halfWindow]
					ssd = np.sum((pimg1-pimg2)**2)
					if ssd < min_ssd:
						min_ssd = ssd
						disp = np.abs(distance)
			disparity[r, c] = disp

		print(r+1)

	## Rescaling the disparity map between 0-255
	disparity[disparity < 1] = 1
	disparity[disparity > np.mean(disparity)] = np.mean(disparity)
	disparity = imgnormalize(disparity)
	disparity = np.uint8(disparity * (255 / np.max(disparity)))

	## Depth
	depth = 255-disparity
	# depth = (b * f) / (disparity+1e-10)
	# depth[depth > b*f] = b*f
	# depth = np.uint8(depth * 255 / np.max(depth))

	disparityim = cv2.applyColorMap(disparity, cv2.COLORMAP_JET)
	depthim = cv2.applyColorMap(depth, cv2.COLORMAP_JET)

	return disparity, depth, disparityim, depthim

def imgnormalize(image, maxm = 255, minm = 0):
	image = ((image-image.min())*((maxm-minm)/(image.max()-image.min())))+minm
	return image

def getEpipoleLine(srcFinal, dstFinal, F) :
	els = np.zeros((srcFinal.shape[0], 3))
	eld = np.zeros((dstFinal.shape[0], 3))

	for i in range(srcFinal.shape[0]) :
		els[i] = F@np.array([dstFinal[i][0], dstFinal[i][1], 1])

	for i in range(dstFinal.shape[0]) :
		eld[i] = F@np.array([srcFinal[i][0], srcFinal[i][1], 1])

	return eld, els

def getEpipoles(els, eld) :
	Usi, Ssi, Vsi = np.linalg.svd(els)
	eps = Vsi[-1,:]

	Udi, Sdi, Vdi = np.linalg.svd(eld)
	epd = Vdi[-1,:]
	return eps, epd

def rectify(srcFinal, dstFinal, Fmatrix, e1, e2, img1, img2) :
	width = img2.shape[1]
	height = img2.shape[0]

	points1 = np.column_stack((srcFinal, np.ones(srcFinal.shape[0])))
	points2 = np.column_stack((dstFinal, np.ones(dstFinal.shape[0])))

	T = np.identity(3)
	T[0][2] = -width/2
	T[1][2] = -height/2

	e = T.dot(e2)
	e1_prime = e[0]
	e2_prime = e[1]

	if e1_prime >= 0:
		alpha = 1.0
	else:
		alpha = -1.0

	R = np.identity(3)
	R[0][0] = alpha*e1_prime/(e1_prime**2 + e2_prime**2)**0.5
	R[1][0] = -alpha*e2_prime/(e1_prime**2 + e2_prime**2)**0.5
	R[0][2] = alpha*e2_prime/(e1_prime**2 + e2_prime**2)**0.5
	R[1][1] = alpha*e1_prime/(e1_prime**2 + e2_prime**2)**0.5

	G = np.identity(3)
	f = R.dot(e)[0]
	G = np.identity(3)
	G[2][0] = - 1.0 / f

	H2 = np.linalg.inv(T)@G@R@T

	ex = np.zeros((3, 3))
	ex[0][1] = -e2[2]
	ex[0][2] = e2[1]
	ex[1][0] = e2[2]
	ex[1][2] = -e2[0]
	ex[2][0] = -e2[1]
	ex[2][1] = e2[0]

	v = np.array([1, 1, 1])
	M = ex@Fmatrix + np.outer(e2, v)
	print(M.shape)
	print(points1.shape)
	print(H2.shape)

	points1_hat = H2.dot(M.dot(points1.T)).T
	points2_hat = H2.dot(points2.T).T

	W = points1_hat / points1_hat[:, 2].reshape(-1, 1)
	b = (points2_hat / points2_hat[:, 2].reshape(-1, 1))[:, 0]

	# least square problem
	a1, a2, a3 = np.linalg.lstsq(W, b)[0]
	HA = np.identity(3)
	HA[0] = np.array([a1, a2, a3])
	H1 = HA@H2@M

	print('---Homography---')
	print(H1)
	print(H2)
	return H1, H2

def drawEpipoleLine(els, eld, img1, img2, srcFinal, dstFinal) :
	# plotting epipole line
	new_img = np.concatenate((img1, img2), axis=1)
	r = 4
	thickness = 2
	numpts = 50
	for i in range(numpts) :
		elsi = els[i]
		a, b, c = elsi[0], elsi[1], elsi[2]
		y1coord = int((a*0+c)/(-b))
		y2coord = int((a*img1.shape[1]+c)/(-b))
		cv2.line(img2, (0, y1coord), (img2.shape[1], y2coord), (0, 0, 255), 2)
		cv2.circle(img2, (int(dstFinal[i][0]), int(dstFinal[i][1])), r, (0,0,255), thickness)

	for i in range(numpts) :
		eldi = eld[i]
		a, b, c = eldi[0], eldi[1], eldi[2]
		y1coord = int((a*0+c)/(-b))
		y2coord = int((a*img2.shape[1]+c)/(-b))
		cv2.line(img1, (0, y1coord), (img1.shape[1], y2coord), (0, 255, 0), 2)
		cv2.circle(img1, (int(srcFinal[i][0]), int(srcFinal[i][1])), r, (0, 255, 0), thickness)

	new_img = np.hstack((img1, img2))
	resized = cv2.resize(new_img, (1440, 720), interpolation = cv2.INTER_AREA)
	# resized = cv2.resize(new_img, (900, 600), interpolation = cv2.INTER_AREA)
	cv2.imshow('NewImage', resized)
	cv2.waitKey(0)

if __name__ == "__main__" :

	Parser = argparse.ArgumentParser()
	Parser.add_argument('--DirPath', default="./octagon", help='base path where data files exist')
	Parser.add_argument('--Flag', default="2", help='flag to get the info about the folder')
	Parser.add_argument('--Scaling', default="33", help='scaling percent where data files exist')
	Parser.add_argument('--WindowSize', default="11", help='window size for disparity')
	Args = Parser.parse_args("")
	DirPath = Args.DirPath
	Flag = Args.Flag
	Scaling = Args.Scaling
	WindowSize = Args.WindowSize
	path = DirPath
	flag = Flag
	scaling = Scaling
	stereo(path, int(scaling), int(WindowSize), int(flag))


import math

def redux_sqr(x, eps):
    arg = 1
    return redux_sqr_inner(x, eps, arg)

def redux_sqr_inner(x, eps, arg):
    if x < 0:
        raise ValueError("Can't evaluate log(negative)")
    if x == 0:
        raise ValueError("Can't evaluate log(0)")
    if 0 < x <= 1:
        if x < 1 - eps:
            arg /= 2
            x *= x
            return redux_sqr_inner(x, eps, arg)
        return x, arg
    if x > 1 + eps:
        arg *= 2
        x = math.sqrt(x)
        return redux_sqr_inner(x, eps, arg)
    return x, arg

def redux_div(x, eps):
    arg = 1
    return redux_div_inner(x, eps, arg)

def redux_div_inner(x, eps, arg):
    if x < 0:
        raise ValueError("Can't evaluate log(negative)")
    if x == 0:
        raise ValueError("Can't evaluate log(0)")
    if 0 < x < 1:
        if 0.5 <= x:
            return x, arg
        arg -= arg/2
        x *= 2
        return redux_div_inner(x, eps, arg)
    if 1 <= x <= 2:
        return x, arg
    if x > 1 + eps:
        if (arg == 0): arg += 1
        else: arg += arg
        x /= 2
        return redux_div_inner(x, eps, arg)
    return x, arg

def redux(x, eps):
    if x > 2:
        x, div = redux_div(x, eps)
    else:
        div = 0
    x, sqr = redux_sqr(x, eps)
    return x, div, sqr

def main(x, eps):
    y, arg_div, arg_sqr = redux(x, eps)
    print("Param: ", x)
    print("Function: log(", x, ")")
    print("Reduced: ", arg_sqr, "* log(", y, ") + log(", arg_div, ")")

main(10, 0.05)


""" Этот узел используется для издания трансформаций между системами координат,
связанными с черепашкой и координатной системой world """

import math
import numpy as np
import rclpy
from rclpy.node import Node
from geometry_msgs.msg import TransformStamped
from tf2_ros import TransformBroadcaster
from turtlesim.msg import Pose

def quaternion_from_euler(ai, aj, ak):
    # Деление на 2 приводит к получению половины угла для каждой из трех осей
    ai /= 2.0
    aj /= 2.0
    ak /= 2.0

    # Косинусы половинных углов
    ci = math.cos(ai)
    si = math.sin(ai)
    cj = math.cos(aj)

    # Синусы половинных углов
    sj = math.sin(aj)
    ck = math.cos(ak)
    sk = math.sin(ak)

    # Произведения косинусов и синусов половинных углов
    cc = ci*ck
    cs = ci*sk
    sc = si*ck
    ss = si*sk

    # Вычисления коэффициентов кватерниона
    # Преобразование углов Эйлера в кватернионы
    q = np.empty((4, ))
    q[0] = cj*sc - sj*cs
    q[1] = cj*ss + sj*cc
    q[2] = cj*cs - sj*sc
    q[3] = cj*cc + sj*ss

    return q

# Класс FramePublisher
class FramePublisher(Node):

    def __init__(self):
        super().__init__('turtle_tf2_frame_publisher')

        # Объявляем и получаем параметр turtlename
        self.turtlename = self.declare_parameter(
            'turtlename', 'turtle').get_parameter_value().string_value

        # Инициализируем вещателя трансформаций
        self.tf_broadcaster = TransformBroadcaster(self)

        # Подписываемся на тему turtle{1}{2}/pose
        # Вызываем функцию handle_turtle_pose
        self.subscription = self.create_subscription(
            Pose,
            f'/{self.turtlename}/pose',
            self.handle_turtle_pose,
            1)
        self.subscription  # предотвращаем предупреждение о неиспользованной переменной

    def handle_turtle_pose(self, msg):
        t = TransformStamped()

        # Читаем содержимое сообщения
        # Присваиваем его соответствующим переменным трансформации
        t.header.stamp = self.get_clock().now().to_msg()
        t.header.frame_id = 'world'
        t.child_frame_id = self.turtlename

        # Черепаха существует только в двух измерениях, поэтому мы получаем координаты x и y из сообщения
        # и устанавливаем координату z в 0
        t.transform.translation.x = msg.x
        t.transform.translation.y = msg.y
        t.transform.translation.z = 0.0

        # По той же причине черепаха может вращаться только вокруг одной оси,
        # и поэтому мы устанавливаем вращение по x и y в 0 и получаем
        # угол вращения вокруг оси z из сообщения
        q = quaternion_from_euler(0, 0, msg.theta)
        t.transform.rotation.x = q[0]
        t.transform.rotation.y = q[1]
        t.transform.rotation.z = q[2]
        t.transform.rotation.w = q[3]

        # Отправляем трансформацию
        self.tf_broadcaster.sendTransform(t)

def main():
    rclpy.init()
    node = FramePublisher()

    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass

    rclpy.shutdown()


import numpy as np
import itertools
import matplotlib.pyplot as plt
import random

from matplotlib.patches import Circle, Arc
from matplotlib.animation import FuncAnimation

class Interval():
    def __init__(self, a, b, mat, letters, color):
        self.a = a
        self.b = b
        self.mat = mat
        self.color = color
        self.letters = letters

    # Draw image of the interval
    def draw(self, ax):
        theta2, theta1 = get_arc_params(rp1_interval(self.a, self.b))
        ax.add_patch(Arc((0,0), 2., 2., theta1=theta1, theta2=theta2, color=self.color, linewidth=10))

    # Draw image of the interval under all letters except inv(self.mat)
    # Check containment of the images within the interval
    def draw_image(self, ax):
        I = rp1_interval(self.a, self.b)
        for mat in letters:
            if not np.allclose(mat, np.linalg.inv(self.mat)):
                theta2, theta1 = get_arc_params(mat @ I)
                ax.add_patch(Arc((0,0), 2., 2., theta1=theta1, theta2=theta2, color='orange', linewidth=7))

def rp1_to_s1(v):
    """map a point in R^2 - {0} to S1 via the homeo RP1 -> S1"""
    x, y = v[0], v[1]

    return np.row_stack([
        2*x*y / (x*x + y*y),
        (x*x - y*y) / (x*x + y*y)
    ])

# TODO Make this
def s1_to_rp1(v):
    pass

def get_arc_params(interval):
    """map a pair of points in R^2 - {0} to a pair of circle angles"""
    x, y = rp1_to_s1(interval)
    return np.arctan2(y,x) * 180 / np.pi

def rp1_interval(theta1, theta2):
    """get a pair of points in RP1 representing the pair of angles theta1, theta2

    note: theta1, theta2 parameterize the double cover of RP^1"""
    return np.array([
        [np.cos(theta1), np.cos(theta2)],
        [np.sin(theta1), np.sin(theta2)]
    ])

def contains(a, b, c, d):
    # Check if (a,b) is contained in (c,d)
    return a < c and b > d

def non_triv_intersect(a, b, c, d):
    # Check if (a,b) intersects (c,d) nontrivially
    return a < c < b or a < d < b

def rand_matrix(n):
    # n determines how large you want the entries to be. Pick n in (0, 5).
    # random matrix generation
    a = float(np.random.rand(1) * n)
    c = float(np.random.rand(1) * n) #* np.random.choice(-1, 1) * 5)
    d = float(np.random.rand(1) * n)
    b = (a * d - 1) / c

    return np.array([[a, b], [c, d]])

def pair_of_free_group_generators(n):
    # Create a random pair of matrices that are guaranteed to generate a free group in SL(2,R)
    # n determines how large you want the entries to be. Pick n in (0, 5).

    # file:///C:/Users/abhay/Downloads/1028999969.pdf PDF pg 2 / pg 162
    # Title: Pairs of Real 2-by-2 Matrices that Generate Free Products by R.C. Lyndon & J.L. Ullman

    a, c, e, g = float(np.random.rand(1) * n) , float(np.random.rand(1) * n) , float(np.random.rand(1) * n) , float(np.random.rand(1) * n)
    d, h = float(np.random.rand(1) * n) + a + 2 , float(np.random.rand(1) * n) + e + 2
    b, f = (a * d + 1) / c , (e * h + 1) / g

    R = rand_matrix(1)

    return R @ np.array([[-a , b] , [-c , d]]) @ np.linalg.inv(R) , R @ np.array([[ -e , -f] , [ g , h]]) @ np.linalg.inv(R)

def generators_up_to_conjugacy(eig,n):
    # Classifies generators of a free group up to conjugacy by specifying eigenvalue

    a = float(np.random.rand(1) * n)
    c = float(np.random.rand(1) * n)# * np.random.choice(-1, 1) * 5)
    d = (eig**2 + 1) / eig - a
    b = (a * d - 1) / c

    return np.array([ [a, b], [c, d] ])

def create_intervals(generators, epsilon):
    # Find theta for eigenvectors on S1 via RP1 -> S1
    # Create interval objects with initial values around eigenvectors
    colors = [np.array([0,0,1]), np.array([1,0,0])]
    intervals = []
    iter = 0

    for mat in generators:

        e1 = np.arctan2(np.linalg.eig(mat)[1][1][0], np.linalg.eig(mat)[1][0][0])
        e2 = np.arctan2(np.linalg.eig(mat)[1][1][1], np.linalg.eig(mat)[1][0][1])

        if np.linalg.eig(mat)[0][0] > 1:
            intervals.append(Interval(e1 - epsilon, e1 + epsilon, mat, letters, colors[iter]))
            intervals.append(Interval(e2 - epsilon, e2 + epsilon, np.linalg.inv(mat), letters, colors[iter]))
        else:
            intervals.append(Interval(e1 - epsilon, e1 + epsilon, np.linalg.inv(mat), letters, colors[iter]))
            intervals.append(Interval(e2 - epsilon, e2 + epsilon, mat, letters, colors[iter]))

        iter += 1
    return intervals

if __name__ == '__main__':

    # Check out 0.2 or 0.25!
    epsilon = 0.25

    print()
    print()
    print("How would you like to generate 2 matrices in SL(2,R) to test if Ping-Pong intervals exist?" )
    print()
    print("1. Keep generating 2 random matrices in SL(2,R) as potential generators of a Free group until a successful Ping-Pong interval is found.")
    print()
    print("2. Generate a pair of matrices in SL(2,R) that already generate a free group.")
    print()
    print("3. Specify two eigenvalues to create two corresponding matrices in SL(2,R).")
    print("   Matrices with the specified eigenvalues will continue to be generated until a successful Ping-Pong interval is found.")
    print("   This will classify generators of a free group in conjugacy classes.")
    print()
    print("4. Enter manual inputs for 2 potential generators in SL(2,R).")
    print()

    val = int(input("Enter choice 1, 2, 3, or 4 and press Enter: "))
    print("-"*145)

    if val == 3:

        print()
        print("Please enter eigenvalues in (0 , ∞) / {1}.")

        repeat = True
        while repeat:

            print()
            eA = float(input("Enter an eigenvalue to genenerate the first matrix in SL(2,R) and press Enter: "))
            eB = float(input("Enter an eigenvalue to genenerate the second matrix in SL(2,R) and press Enter: "))

            if eA == 1 or eB == 1:
                print()
                print("Please do not enter 1 as an eigenvalue. Enter eigenvalues in (0 , ∞) / {1}. Try again!")
            else:
                print("-"*145)
                repeat = False

    if val == 4:

        print()
        print( np.array( [["a", "b"],
                          ["c", "d"]] )  )
        print()
        print( np.array( [["e", "f"],
                          ["g", "h"]] )  )
        print()
        print("Please fill in the letters above for the two matrices:")
        a, b, c, d = input("Enter the values of a, b, c, d. Seperate the values by a space and then press Enter: ").split()
        e, f, g, h = input("Enter the values of e, f, g, h. Seperate the values by a space and then press Enter: ").split()
        a, b, c, d, e, f, g, h = float(a), float(b), float(c), float(d), float(e), float(f), float(g), float(h)
        print("-"*145)

    print()

    done = False
    while not done:

        i = 1

        if val == 1:
            # random matrix generation
            print('checking')

            A, B = rand_matrix(7), rand_matrix(7)
            eA, eB = np.linalg.eig(A)[0][0], np.linalg.eig(B)[0][0]

        elif val == 2:
            # Create a random pair of matrices that are
            # guaranteed to generate a free group in SL(2,R)

            A, B = pair_of_free_group_generators(5)
            eA, eB = np.linalg.eig(A)[0][0], np.linalg.eig(B)[0][0]

        elif val == 3:
            # generate a pair of matrices by their eigenvalues (classifying them in conjugacy classes)
            print('checking')

            A = generators_up_to_conjugacy(eA,3)
            B = generators_up_to_conjugacy(eB,3)

        elif val == 4:

            A = np.array([[a, b],
                          [c, d]])
            B = np.array([[e, f],
                          [g, h]])
            eA, eB = np.linalg.eig(A)[0][0], np.linalg.eig(B)[0][0]

        if np.iscomplex(eA) == True or np.iscomplex(eB) == True:
            continue
        elif np.abs(eA - float(1)) < 0.0001 or np.abs(eB - float(1)) < 0.0001:
            continue
        else:
            pass

        # Get all letters (this includes generators and their inverses)
        generators = [A, B]
        letters = []
        for g in generators:
            letters += [g]
            letters += [np.linalg.inv(g)]

        ##########################################################################################################
        # TODO Make sure intervals don't overlap
        for epsilon in np.arange(0.01, 0.4, 0.01):
            intervals = create_intervals(generators, epsilon)
            all_fully_contained = True
            for interval in intervals:
                fully_contained = True
                for other_interval in intervals:
                    I = rp1_interval(other_interval.a, other_interval.b)
                    if not np.allclose(other_interval.mat, np.linalg.inv(interval.mat)):
                        theta2, theta1 = get_arc_params(interval.mat @ I)
                        b, a = get_arc_params(rp1_interval(interval.a, interval.b))
                        # print(a, b, theta1, theta2)
                        if a > b:
                            # Interval crosses the break point
                            if theta1 > theta2:
                                # Inner interval crosses the break point
                                fully_contained = fully_contained and contains(theta2, theta1, b, a)
                            else:
                                if theta1 > 0:
                                    # Inner interval completely above break point
                                    fully_contained = fully_contained and contains(theta2-360, theta1, b, a)
                                else:
                                    # Inner interval completely below break point
                                    fully_contained = fully_contained and contains(theta2, theta1+360, b, a)
                        else:
                            fully_contained = fully_contained and contains(a, b, theta1, theta2)
                all_fully_contained = all_fully_contained and fully_contained

            for pair in itertools.combinations(intervals, 2):
                if non_triv_intersect(pair[0].a, pair[0].b, pair[1].a, pair[1].b):
                    if i == 1:
                        print('Intervals are intersecting!')
                        print()
                        i += 1
                    intervals = create_intervals(generators, epsilon - 0.01)
                    done = False

            if all_fully_contained and not done:
                intervals = create_intervals(generators, epsilon + 0.01)
                done = True

            if done:
                break
    ##########################################################################################################
    print('A = ')
    print(A)
    print('Determinant = ', np.linalg.det(A))
    print()
    print('Eigenvalues = ', np.linalg.eig(A)[0][0],' , ', np.linalg.eig(A)[0][1], ";")
    print('Eigenvectors = [', np.linalg.eig(A)[1][0][0] , ',' , np.linalg.eig(A)[1][1][0], '] ,', '[', np.linalg.eig(A)[1][0][1] , ',', np.linalg.eig(A)[1][1][1], '].')
    print()
    print('B = ')
    print(B)
    print('Determinant = ', np.linalg.det(B))
    print()
    print('Eigenvalues = ', np.linalg.eig(B)[0][0],' , ', np.linalg.eig(B)[0][1], ";")
    print('Eigenvectors = [', np.linalg.eig(B)[1][0][0] , ',' , np.linalg.eig(B)[1][1][0], '] ,', '[', np.linalg.eig(B)[1][0][1] , ',', np.linalg.eig(B)[1][1][1], '].')
    print()
    print('epsilon = ', epsilon)

    if not all_fully_contained:
        print('Ping Pong Intervals Not Found.')

    print("-"*145)
    print()

    # verify acrtan2 gives correct angles

    '''# A coordinates
    x1, y1, x2, y2 = np.linalg.eig(A)[1][0][0], np.linalg.eig(A)[1][1][0], np.linalg.eig(A)[1][0][1], np.linalg.eig(A)[1][1][1]
    v1, v2 = [x1, y1] , [x2, y2]
    # B coordinates
    x3, y3, x4, y4 = np.linalg.eig(B)[1][0][0], np.linalg.eig(B)[1][1][0], np.linalg.eig(B)[1][0][1], np.linalg.eig(B)[1][1][1]
    v3, v4 = [x3, y3] , [x4, y4]

    print(rp1_to_s1(v1), np.arctan2(y1,x1) * 180 / np.pi)
    print(rp1_to_s1(v2), np.arctan2(y2,x2) * 180 / np.pi)
    print(rp1_to_s1(v3), np.arctan2(y3,x3) * 180 / np.pi)
    print(rp1_to_s1(v4), np.arctan2(y4,x4) * 180 / np.pi)'''

    fig, ax = plt.subplots(figsize=(5, 5))

    # Plot data
    ax.set_xlim((-1.2, 1.2))
    ax.set_ylim((-1.2, 1.2))
    ax.axis("off")
    ax.set_aspect("equal")

    # RP1
    rp1 = Circle((0,0), 1.0, fill=False)
    ax.add_patch(rp1)

    # Draw intervals
    # Draw images of intervals under the respective transforms

    for interval in intervals:
        interval.draw(ax)
    for interval in intervals:
        interval.draw_image(ax)
    plt.show()


from django.shortcuts import render
import xlrd
import datetime

def read_from_excel(self,file):
    wb=xlrd.open_workbook(filename=file.path)
    mode=wb.datemode
    excel=[]
    for s in wb.sheets():
        for row in range(1,s.nrows):
            data=[]
            for col in range(s.ncols):
                value=(s.cell(row,col).value)
                data.append(value)
            data.append(mode)
            excel.append(data)
        return excel


from ...common.helper import send_success_response, send_error_response
from ..workbench.services import data_management as data_service
from ...common.security import *
from ...common.repository.models_managers.access_manager import build_permission_tree
from ...common.error_manager import ErrorManager
from ...common import runtime_storage as rt
TOOL = 'forecast'

@forbidden_view
def get_dashboard_data(req):
    try:
        user_id = req.user
        lang = rt.language(user_id)
    except KeyError as e:
        msg = ErrorManager.get_error_msg(e, lang)
        return send_error_response(msg)
    #try:
    project = rt.get_state(user_id)._project_id
    wb = rt.get_wb(user_id)
    session = req.dbsession
    permission_tree = build_permission_tree(session, project_name=project)
    data = data_service.get_entity_data(permission_tree=permission_tree, container=wb.default_container,
                                        config=wb.data_config, entities_ids=wb.selection, lang=lang)
    #except Exception as e:
    #msg = req.get_error_msg(e, lang)
    #return send_error_response(msg)
    #else:
    return send_success_response(data)

@forbidden_view
def get_cagrs_for_period(req):
    # Get parameters from request.
    try:
        user_id = req.user
        entities_ids = req.json_body['data']['entities_ids']
        ts = req.json_body['data']['timescale']
        start = req.json_body['data']['start']
        end = req.json_body['data']['end']
        lang = rt.language(user_id)
    except KeyError as e:
        msg = req.get_error_msg(e, lang)
        return send_error_response(msg)
    try:
        wb = rt.get_wb(user_id)
        cagrs = data_service.get_cagrs(wb.default_container, wb.config, entities_ids, (start, end))
        return send_success_response(cagrs)
    except Exception as e:
        msg = req.get_error_msg(e, lang)
        return send_error_response(msg)

@forbidden_view
def get_decomposition_for_period(req):
    """
    Return decomposition data for period

    :param req:
    :type req:
    :return:
    :rtype:
    """
    # Get parameters from request.
    try:
        user_id = req.get_user
        entities_ids = req.json_body['data']['entities_ids']
        ts = req.json_body['data']['timescale']
        start = req.json_body['data']['start']
        end = req.json_body['data']['end']
        lang = rt.language(user_id)
    except KeyError as e:
        msg = req.get_error_msg(e, lang)
        return send_error_response(msg)
    try:
        wb = rt.get_wb(user_id)
        dec_data = data_service.get_decomposition(wb.container['default'], wb.config,
                                                    entities_ids, (start, end))
        return send_success_response(dec_data)
    except Exception as e:
        msg = req.get_error_msg(e, lang)
        return send_error_response(msg)


#!/usr/bin/env python
# coding: utf-8

# In[1]:

import pandas as pd
import csv
import json
import time
import os
from pymongo import MongoClient
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt

# In[2]:

pd.set_option('display.max_rows', None)
pd.set_option('display.max_columns', None)
pd.set_option('display.width', None)

# In[3]:

spotify_csv = pd.read_csv('Spotify 2010 - 2019 Top 100.csv', encoding = 'utf-8')
print('Last modification date:', time.ctime(os.path.getctime('Spotify 2010 - 2019 Top 100.csv')))
spotify_csv.info()

# In[4]:

display(spotify_csv.iloc[1:11])

# In[5]:

display(spotify_csv[['title','duration']])

# In[6]:

spotify_csv.duration.describe()

# In[7]:

spotify_csv.sort_values(['beats per minute'], ascending = 0)

# In[8]:

spotify_csv.loc[(spotify_csv['top year'] == 2019)]

# In[9]:

spotify_csv.loc[(spotify_csv['popularity'] < 50)]

# In[10]:

songs_to_dance = spotify_csv.title.loc[spotify_csv['danceability rate'] >= 70]
songs_to_dance.count()

# In[11]:

spotify_csv.loc[(spotify_csv['acoustic rate'] == spotify_csv['acoustic rate'].max())]

# In[12]:

spotify_csv.loc[(spotify_csv['acoustic rate'] == spotify_csv['acoustic rate'].min())]

# In[13]:

spotify_csv[(spotify_csv['genre'].str.contains('hip hop') | (spotify_csv['genre'].str.contains('pop'))
    & spotify_csv['artist type'].str.contains('Solo'))]

# In[14]:

spotify_csv.groupby(['artist type']).mean()

# In[15]:

spotify_csv.sort_values(by = ['date added to list'], ascending = 0)

# In[16]:

spotify_csv.groupby(['genre']).sum()

# In[17]:

spotify_csv.groupby(['top year']).count()

# In[18]:

spotify_csv['beats per minute'].hist()

# In[19]:

def search_songs_by_duration_range(min_duration, max_duration):
    searched_songs = spotify_csv.loc[(spotify_csv.duration >= min_duration) & (spotify_csv.duration <= max_duration)]
    sorted_songs = searched_songs.sort_values(by = ['duration'], ascending = 0)
    display(sorted_songs)

search_songs_by_duration_range(100, 220)

# In[20]:

def search_songs_by_genre(genre_to_find):
    display(spotify_csv.loc[spotify_csv['genre'].str.contains(genre_to_find)])

search_songs_by_genre('dance pop')

# In[21]:

def search_songs_by_title_keyword(keyword):
    display(spotify_csv.loc[(spotify_csv['title'].str.contains(keyword))])

search_songs_by_title_keyword('You')

# In[22]:

def convert_csv_to_json(csv_file_path, json_file_path):
    json_array = []
    with open(csv_file_path, encoding = 'utf-8') as csv_file:
        csv_dict_reader = csv.DictReader(csv_file)
        for row in csv_dict_reader:
            json_array.append(row)

    with open(json_file_path, 'w', encoding = 'utf-8') as json_file:
        json_text = json.dumps(json_array, indent = 4)
        json_file.write(json_text)

csv_file_path = r'Spotify 2010 - 2019 Top 100.csv'
json_file_path = r'SpotifyTop100Songs.json'

convert_csv_to_json(csv_file_path, json_file_path)

# In[23]:

spotify_json = pd.read_json('SpotifyTop100Songs.json', encoding = 'utf-8')

# In[24]:

mongo_client = MongoClient('mongodb://localhost:27017')
spotify_database = mongo_client['SpotifyTop100Songs']

# In[25]:

songs_collection = spotify_database.Top100SpotifySongs
songs_dict = spotify_json.to_dict('records')

# In[26]:

songs_collection.insert_many(songs_dict)

# In[27]:

all_docs_number = songs_collection.count_documents({})
print('The total number of documents in Top100SpotifySongs collection:', all_docs_number)

# In[28]:

first_100_songs = songs_collection.find({}).limit(100)
for song in first_100_songs:
    print(song)

# In[29]:

edm_songs = songs_collection.find({'genre':'edm'})
for edm_song in edm_songs:
    display(edm_song)

# In[30]:

loud_songs = songs_collection.find({'decibel':{'$lt':-5}, 'beats per minute':{'$gt': 100}}).sort('beats per minute', -1)
for loud_song in loud_songs:
    display(loud_song)

# In[31]:

year2016_or_more_songs = songs_collection.find({'year released':{'$gte':2016}}).sort('year released')
for year2016_or_more_song in year2016_or_more_songs:
    display(year2016_or_more_song)

# In[32]:

group_songs = songs_collection.find({'artist type':{'$ne':'Solo'}})
for group_song in group_songs:
    display(group_song)

# In[33]:

rihanna_songs = songs_collection.find({'artist':{'$in':['Rihanna']}})
for rihanna_song in rihanna_songs:
    display(rihanna_song)

# In[34]:

no_pop_songs = songs_collection.find({'genre':{'$nin':['dance pop', 'pop soul','pop rap', 'art pop', 'pop', 'barbadian pop',
    'acoustic pop', 'belgian pop', 'baroque pop', 'indie pop', 'australian pop','canadian pop', 'folk-pop', 'austrian pop',
    'irish pop', 'chill pop', 'indie pop rap', 'electropop']}})
for no_pop_song in no_pop_songs:
    print(no_pop_song)

# In[35]:

solo_and_duo_songs = songs_collection.find({'$nor':[{'artist type': 'Band/Group'}, {'artist type': 'Trio'}]})
for solo_and_duo_song in solo_and_duo_songs:
    print(solo_and_duo_song)

# In[36]:

year_2018_and_2019_songs = songs_collection.find({'$or':[{'top year': 2018}, {'top year': 2019}]})
for year_2018_and_2019_song in year_2018_and_2019_songs:
    print(year_2018_and_2019_song)

# In[37]:

query = {
    'decibel': { '$gte': -5 },
    'popularity': { '$gt': 60 },
    'duration': { '$lt': 200 },
}

query_list = list(songs_collection.find(query))
decibel = [x['decibel'] for x in query_list]
popularity = [x['popularity'] for x in query_list]
duration = [x['duration'] for x in query_list]

plt.clf()
fig = plt.figure()

ax = fig.add_subplot(111, projection = '3d')
ax.scatter(decibel, popularity, duration)

ax.set_xlabel('Decibel')
ax.set_ylabel('Popularity')
ax.set_zlabel('Duration')

plt.show()


#! /usr/bin/env python
import random
import json
import requests as req
from urllib.parse import quote

class Translator:

    def __init__(self, url=["https://translate.projectsegfau.lt", "https://mozhi.aryak.me"], engine="google"):
        try:
            self.urls = url
            self.engine = engine
        except req.exceptions.RequestException as er:
            print(er)
            exit()
        except req.exceptions.ConnectionError as er:
            print(er)
            exit()
        except req.exceptions.Timeout as er:
            print(er)
            exit()

    def build_url(self, endpoint, query_params):
        current_url = random.choice(self.urls)
        url = f"{current_url}/api/{endpoint}"
        query_string = "&".join([f"{key}={value}" for key, value in query_params.items()])
        url += f"?engine={self.engine}&{query_string}"
        return url

    def languages(self):
        query_params = {
            "engine": self.engine
        }
        url = self.build_url("source_languages", query_params)
        all_languages = req.get(url).json()
        lang = {}
        for language in all_languages:
            name = language['Name']
            code = language['Id']
            lang[name] = code
        return lang

    def translate(self, source, target, text):
        query_params = {
            "from": source,
            "to": target,
            "text": quote(text)
        }
        url = self.build_url("translate", query_params)
        # print(url)
        r = req.get(url)
        r = r.json()
        result = r['translated-text']
        return result


from pytest import fixture

from qtrex.errors import ExecutionError
from qtrex.executor import BigQueryExecutor
from qtrex.models import QueryRef

@fixture
def valid_bq_query_ref() -> QueryRef:
    return QueryRef(
        "/tmp/1/valid_query.sql",
        "SELECT SUM(x) AS value FROM UNNEST([1,2,3,4,5]) AS x",
        "valid_query.sql",
    )

@fixture
def invalid_query_ref() -> QueryRef:
    return QueryRef("/tmp/bad.sql", "SELECT ALL THE ROWS FROM MY TABLE", "bad.sql")

def test_bigquery_executor_can_dry(valid_bq_query_ref):
    ex = BigQueryExecutor()

    res = ex.execute(valid_bq_query_ref, dry_run=True)
    assert res.df is None
    assert res.error is None

def test_bigquery_executor_can_execute_succesfully(valid_bq_query_ref):
    ex = BigQueryExecutor()
    res = ex.execute(valid_bq_query_ref, dry_run=False)
    assert res.df is not None
    assert len(res.df) > 0
    assert res.error is None

def test_bigquery_executor_returns_error_in_result_with_invalid_query(
    invalid_query_ref,
) -> None:
    ex = BigQueryExecutor()
    res = ex.execute(invalid_query_ref)
    assert res.error is not None
    assert isinstance(res.error, ExecutionError)


#!/bin/env python
import asyncio
import json
import gzip
import re
import chardet
from sunday.core import Logger, Fetch, getException, getParser
from sunday.utils import currentTimestamp, mergeObj, image
from os import path, makedirs, listdir, mknod
from mitmproxy import options, http
from mitmproxy.tools import dump
from urllib.parse import urlparse, unquote
from pydash import omit, get
from sunday.tools.proxy.params import CMDINFO
from datetime import datetime
from shutil import copyfile

SundayException = getException()

logger = Logger('TOOLS PROXY').getLogger()

imageTypeList = ['png', 'gif', 'jpg', 'ico']

class BytesEncoder(json.JSONEncoder):
    def default(self, obj):
        if isinstance(obj, bytes):
            return obj.decode()
        elif hasattr(obj, '__dict__') and type(obj.__dict__) is dict:
            return obj.__dict__
        return json.JSONEncoder.default(self, obj)

def grenPath(urlInfo):
    url = urlInfo.netloc + urlInfo.path
    if url[-1] == '/': url += 'index.html'
    return url

class BaseClass:
    def __init__(self, dataPath, collectList, closeList, proxyList, setting):
        self.dataPath = dataPath
        self.collectList = collectList
        self.closeList = closeList
        self.proxyList = proxyList
        self.setting = setting

    def getSetting(self, url, key, defval=None):
        if url not in self.setting: return None
        return self.setting[url].get(key, defval)

    def getParams(self, flow):
        params = mergeObj(
                dict(flow.request.query),
                dict(flow.request.urlencoded_form),
                { 'header_method': flow.request.method })
        try:
            if flow.request.text: params.update(flow.request.json())
        except Exception as e:
            pass
        return params

    def checkUrlInclude(self, url, urlarr):
        for urlitem in urlarr:
            if urlitem == url: return True
            try:
                if re.search(urlitem, url):
                    return True
            except Exception as e:
                logger.error(f'匹配{url}失败，请检查{urlitem}是否为正则表达式')
        return False

class Collect(BaseClass):
    def __init__(self, *args, **kwargs):
        super(Collect, self).__init__(*args, **kwargs)
        logger.info(f'采集接口 => {self.dataPath}')

    def parseData(self, urlInfo, flow):
        url = grenPath(urlInfo)
        logger.warning('处理链接%s' % url)
        filecwd = path.join(self.dataPath, url)
        # info/main/format
        nowtime = datetime.today().isoformat()
        filecwdInfo = path.join(filecwd, 'info')
        filecwdFormat = path.join(filecwd, 'format')
        filecwdMain = path.join(filecwd, 'main')
        filecwdCurr = path.join(filecwd, nowtime)
        filecwdCurrInfo = f"{filecwdCurr}.info"
        if not path.exists(filecwd): makedirs(filecwd)
        content = flow.response.content
        try:
            if url.split('.').pop() in imageTypeList:
                with open(filecwdCurr, 'wb+') as currf, open(filecwdMain, 'wb+') as mainf:
                    currf.write(content)
                    mainf.write(content)
            else:
                encodeCfg = chardet.detect(content)
                if encodeCfg['confidence'] >= 0.8:
                    content = content.decode(encodeCfg['encoding'])
                else:
                    content = content.decode('utf-8')
                with open(filecwdCurr, 'w+') as currf, open(filecwdMain, 'w+') as mainf:
                    currf.write(content)
                    mainf.write(content)
        except Exception as e:
            logger.error(f'{url}返回内容解析失败')
            with open(filecwdCurr, 'wb+') as currf, open(filecwdMain, 'wb+') as mainf:
                currf.write(content)
                mainf.write(content)
        params = self.getParams(flow)
        skeys = self.getSetting(url, 'superkey', None)
        if skeys is not None:
            # 根据入参做文件映射
            superkeyCfg = path.join(filecwd, 'config')
            key = '@@'.join([params[skey] for skey in skeys if type(params.get(skey)) == str])
            obj = None
            if not path.exists(superkeyCfg): open(superkeyCfg, 'a').close()
            with open(superkeyCfg, 'r+') as superf:
                text = superf.read()
                if text:
                    obj = json.loads(text)
                    if key not in obj:
                        obj[key] = nowtime
                    else:
                        obj = None
                else:
                    obj = { key: nowtime }
            if obj:
                with open(superkeyCfg, 'w+') as superf:
                    superf.write(json.dumps(obj, indent=4))
        info = {
                'url': url,
                'url_full': unquote(flow.request.url),
                'params': params,
                'request': flow.request.data.__dict__,
                'response': omit(flow.response.data.__dict__, ['content']),
                }
        with open(filecwdCurrInfo, 'w+') as infof:
            infof.write(json.dumps(info, indent=4, cls=BytesEncoder))
        if self.getSetting(url, 'format', None):
            copyfile(filecwdCurr, filecwdFormat)
        if not path.exists(filecwdInfo):
            copyfile(filecwdCurrInfo, filecwdInfo)
        logger.info('文件写入成功!')

    def response(self, flow):
        urlInfo = urlparse(flow.request.url)
        if urlInfo.netloc:
            url = grenPath(urlInfo)
            if len(self.collectList) == 0 or url in self.collectList:
                try:
                    self.parseData(urlInfo, flow)
                except Exception as e:
                    __import__('ipdb').set_trace()

class Playback(BaseClass):
    def __init__(self, *args, **kwargs):
        super(Playback, self).__init__(*args, **kwargs)
        logger.info(f'回放接口 => {self.dataPath}')
        self.fetch = Fetch()
        self.headerList = [
                'Location', 'location',
                'Content-Type', 'content-type',
                'Content-Encoding', 'content-encoding',
                'Cache-Control', 'cache-control']
        self.responseHandle = {}

    def getCollectPath(self, url):
        if len(self.collectList) == 0 or url in self.collectList: return url
        for coll in self.collectList:
            if url.find(coll) > -1 and url.find(coll) + len(coll) == len(url):
                return coll
        return False

    def response(self, flow):
        urlInfo = urlparse(flow.request.url)
        url = grenPath(urlInfo)
        if url not in self.responseHandle: return
        filepath = self.responseHandle[url]
        logger.info('本地: ' + filepath)
        with open(filepath, 'r') as ff:
            # content = bytes(ff.read(), 'utf-8')
            content = ff.read()
            if content:
                jsonpKey = self.getSetting(url, 'jsonp')
                if jsonpKey is not None:
                    params = self.getParams(flow)
                    content = re.sub(r'\b(.*?)\(', f'{params.get(jsonpKey, jsonpKey)}(', content, 1)
                flow.response.headers.update({'sunday_flag': 'proxy'})
                flow.response.content = bytes(content, 'utf-8')

    def request(self, flow):
        urlInfo = urlparse(flow.request.url)
        url = grenPath(urlInfo)
        # if 'ac.dun.163.com/v2/config/js' == url: __import__('ipdb').set_trace()
        collectPath = self.getCollectPath(url)
        # logger.warning('链接: ' + url)
        # if url in self.closeList or url.split('.').pop() in ['png', 'gif']:
        if not collectPath:
            logger.debug('网络: ' + url)
            return
        if self.checkUrlInclude(url, self.closeList):
            logger.error('拦截: ' + url)
            flow.response = http.Response.make(400, str.encode(''))
        elif url.split('.').pop() in imageTypeList:
            logger.info('图片: ' + url)
            mode = url.split('.').pop().lower()
            if mode == 'jpg': mode = 'jpeg'
            img_byte = getattr(image, f'grenImage{mode.title()}')(150)
            flow.response = http.Response.make(200, img_byte, {
                'Content-Type': f'image/{mode}'
                })
        else:
            filepath = ''
            filepath_tmp = path.join(self.dataPath, collectPath)
            if path.isdir(filepath_tmp):
                filepath_format = path.join(filepath_tmp, 'format')
                filepath_main = path.join(filepath_tmp, 'main')
                filepath_info = path.join(filepath_tmp, 'info')
            else:
                collectPathSplit = collectPath.split('/')
                temp = collectPathSplit[-1].split('.')
                temp[-1] = 'format.' + temp[-1]
                collectPathSplit[-1] = '.'.join(temp)
                filepath_format = path.join(self.dataPath, *collectPathSplit)
                filepath_main = filepath_tmp
                filepath_info = filepath_tmp + '.info'
            superkeyCfg = path.join(filepath_tmp, 'config')
            skeys = self.getSetting(url, 'superkey', None)
            if skeys is not None and path.exists(superkeyCfg):
                # 根据入参做文件映射
                params = self.getParams(flow)
                key = '@@'.join([params[skey] for skey in skeys if type(params.get(skey)) == str])
                with open(superkeyCfg, 'r+') as superf:
                    text = superf.read()
                    if text:
                        obj = json.loads(text)
                        if key in obj:
                            filepath = path.join(filepath_tmp, obj[key])
            if filepath:
                pass
            elif path.exists(filepath_format) and path.isfile(filepath_format):
                filepath = filepath_format
            elif path.exists(filepath_main) and path.isfile(filepath_main):
                filepath = filepath_main
            elif path.exists(filepath_info) and path.isdir(filepath_tmp):
                for name in listdir(filepath_tmp):
                    try:
                        datetime.fromisoformat(name)
                        filepath = path.join(filepath_tmp, name)
                        copyfile(filepath, filepath_main)
                        break
                    except Exception as e:
                        pass
            if url in self.proxyList:
                logger.warning('代理: ' + url)
                data = flow.request.data.content
                headers = dict(flow.request.headers)
                targeturl = flow.request.url
                res = getattr(self.fetch, flow.request.method.lower())(targeturl, data=data, headers=headers)
                flow.response = http.Response.make(res.status_code, res.content, { **dict(res.headers), "sunday_flag": "proxy" })
            elif filepath:
                if self.getSetting(url, 'response', None):
                    self.responseHandle[url] = filepath
                    return
                logger.info('本地: ' + filepath)
                info = {}
                content = ''
                content_type = 'str'
                try:
                    with open(filepath, 'r') as ff: content = ff.read()
                except Exception as e:
                    content_type = 'bin'
                    with open(filepath, 'rb') as ff: content = ff.read()
                if content:
                    with open(filepath_info, 'r') as fi: info = json.load(fi)
                    jsonpKey = self.getSetting(url, 'jsonp')
                    if jsonpKey is not None:
                        params = self.getParams(flow)
                        content = re.sub(r'\b(.*?)\(', f'{params.get(jsonpKey, jsonpKey)}(', content, 1)
                    fields = {key.lower(): val for key, val in get(info, 'response.headers.fields') if key in self.headerList}
                    status_code = get(info, 'response.status_code')
                    if 'content-type' in fields:
                        fields['content-type'] = re.sub(r'charset=[A-Za-z0-9-_]*\b', 'charset=UTF8', fields['content-type'])
                    if type(content) != bytes: content = bytes(content, 'utf-8')
                    flow.response = http.Response.make(status_code, content, {
                        "sunday_flag": "proxy",
                        "Access-Control-Allow-Origin": "*",
                        **fields })
            else:
                logger.debug('网络: ' + url)

class Proxy():
    def __init__(self, name='playback', host='0.0.0.0', port=7758, collectList=None, closeList=None, proxyList=None, dataPath=None, setting=None, isLog=False, **kwargs):
        self.name = name
        self.host = host
        self.port = int(port)
        self.collectList = collectList or []
        self.closeList = closeList or []
        self.proxyList = proxyList or []
        self.setting = setting or {}
        self.configFile = None
        self.dataPath = dataPath
        self.runpath = path.realpath(path.curdir)
        self.isLog = isLog

    def addCloseUrl(self, url):
        if type(url) == str: url = [url]
        if type(url) != list: return
        logger.debug('add close url %s' % url)
        self.closeList.extend(list(filter(lambda item: item not in self.closeList, url)))

    def addProxyUrl(self, url):
        if type(url) == str: url = [url]
        if type(url) != list: return
        logger.debug('add proxy url %s' % url)
        self.proxyList.extend(list(filter(lambda item: item not in self.proxyList, url)))

    def addCollectUrl(self, url):
        if type(url) == str: url = [url]
        if type(url) != list: return
        logger.debug('add collect url %s' % url)
        self.collectList.extend(list(filter(lambda item: item not in self.collectList, url)))

    def init(self):
        if self.configFile:
            try:
                configStr = self.configFile[0].read()
                configObj = json.loads(configStr)
                self.collectList = configObj.get('collectList', [])
                self.proxyList = configObj.get('proxyList', [])
                self.closeList = configObj.get('closeList', [])
                self.setting = configObj.get('setting', {})
            except Exception as e:
                raise SundayException(-1, '配置文件解析失败，请检查文件%s内容是否为JSON格式' % self.configFile.name)

    async def run(self):
        self.init()
        logger.info('捕获处理的链接有: %s' % self.collectList)
        logger.info('拦截处理的链接有: %s' % self.closeList)
        logger.info('代理处理的链接有: %s' % self.proxyList)
        logger.info('特定链接强化配置: %s' % self.setting)
        opts = options.Options(listen_host=self.host, listen_port=self.port)
        master = dump.DumpMaster(
            opts,
            with_termlog=self.isLog,
            with_dumper=False,
        )
        if self.name not in ['collect', 'playback']:
            raise SundayException(-1, '传入name值不正确，请检查')
        Addon = Collect if self.name == 'collect' else Playback
        master.addons.add(
            Addon(path.join(self.runpath, self.dataPath), self.collectList, self.closeList, self.proxyList, self.setting),
            )
        await master.run()

def runcmd():
    parser = getParser(**CMDINFO)
    handle = parser.parse_args(namespace=Proxy())
    asyncio.run(handle.run())

if __name__ == '__main__':
    runcmd()


from ipywidgets import dlink
from sepal_ui import sepalwidgets as sw

from component.message import cm

class SurfaceSelect(sw.Col):
    def __init__(self):
        # define a title
        title = sw.Html(tag="h4", children=[cm.surface_select.label])

        # define the slider with the value vizualizer
        slider = sw.Slider(
            v_model=0, min=0, max=100, thumb_label=True, class_="mt-5", step=0.5
        )
        number = sw.TextField(suffix="ha", readonly=True, xs2=True)

        # create the widget
        super().__init__(children=[title, slider])

        # add links
        dlink((slider, "v_model"), (self, "v_model"))
        dlink((slider, "v_model"), (number, "v_model"))


def generous_lamb_generator():
    """
    Returns a sequence of numbers, where the next element
    doubles the value of its predecessor.
    The sequence starts with 1.
    """
    prev = 1
    nextelem = prev + prev
    while True:
        yield prev
        prev = nextelem
        nextelem = prev + prev

def stingy_lamb_generator():
    """
    Returns the fibonacci-like sequence that starts with
    1, 1

    Example: 1, 1, 2, 3, 5, 8, 13, 21, ...
    """
    prevprev, prev = 1,1
    nextelem = prevprev + prev
    while True:
        yield prevprev
        prevprev = prev
        prev = nextelem
        nextelem = prevprev + prev

def num_henchmen_paid(total_lambs, generator):
    henchmen_count = 0
    sum = 0
    for lambs in generator:
        henchmen_count += 1
        sum += lambs
        # Once we go over the total_lambs that we have
        # to give, we can back off one.
        if (sum > total_lambs):
            henchmen_count -= 1
            break
    return henchmen_count

def answer(total_lambs):
    """
    See readme.txt for description.
    Input:
      total_lambs (int)
    Output:
      the difference in the min and max number of henchmen
      who can share the total_lambs passed as input (int)
    """
    max = num_henchmen_paid(total_lambs, stingy_lamb_generator())
    min = num_henchmen_paid(total_lambs, generous_lamb_generator())
    return max - min


import numpy as np

class MakePascal:

    def get_triangle(self, num_rows: int = 30):
        pascal = list([] for i in range(num_rows))
        for row in range(num_rows):
            for col in range(row + 1):
                choose = np.math.comb(row, col)
                pascal[row].append(choose)
        return pascal

if __name__ == "__main__":
    MakePascal()


import random
import time
from datetime import datetime

import numpy as np
import torchvision
import torchvision.transforms as transforms
from functions.algorithms.manifool import manifool
from models import *

#Parameter
num_trials = 5
maxIt = 100
mode = 'affine'
step_sizes = np.logspace(-2,np.log10(1),20)
rng_seed = 13
gamma = 0
torch.set_num_threads(8)

mean = (0.4914, 0.4822, 0.4465)
std = (0.2023, 0.1994, 0.2010)
transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize(mean = mean,
                                                     std = std)
                                ])

#Getting the network and the dataset
pretrained = torch.load('./models/cifar_net.t7')
net = pretrained['net']
net.train(mode=False)

dataset = torchvision.datasets.CIFAR10(root='./data', train=True, download=True,
            transform = transform)
random.seed(rng_seed)
im_ind = random.sample(range(len(dataset)),num_trials)

num_of_fail = 0
total_mf = 0
successful_dur_manifool = 0
t = time.time()
geo_scores = []
for i,k in enumerate(im_ind):

    im = dataset.__getitem__(k)[0]

    t0 = time.time()
    out = manifool(im, net, mode,
                   maxIter=maxIt,
                   cuda_on=True,
                   step_sizes = step_sizes,
                   gamma = gamma,
                   numerical=True,
                   verbose = False)

    geo_sc = out['geo_dist']
    tar = out['target']
    k_org = out['org_label']

    t1 = time.time()

    if (k_org != tar):
        geo_scores.append(geo_sc)
        total_mf += geo_sc
        successful_dur_manifool += t1 - t0

    num_of_fail += (k_org == tar)

    print('Iteration {}, Image {}.'
    ' Current Avg. score: {}'.format(i,k,total_mf/(i-num_of_fail+1)))

elapsed = time.time()-t

fail_percent = num_of_fail/num_trials*100
avg_mf = total_mf/(num_trials - num_of_fail)
avg_succ_dur = successful_dur_manifool/(num_trials-num_of_fail)

file_name = 'CIFAR10_geo_scores_var_'+mode
np.save(file_name, geo_scores)

info_str = ("\n"+"-"*13+"Manifool CIFAR10 Demo"+"-"*13+"\nTransformation: {}\n"
            "Number of images: {}\nGamma: {}\nMaximum number of iterations: {}".format(
            mode, num_trials, gamma, maxIt))

log_str = ("\n"+"-"*20+"Results"+"-"*21+"\nNet not fooled for {}%  of images\nTime elapsed for whole "
          "loop: {:.2f} seconds\nAverage duration for an image: {:.4f} seconds\n"
          "Average duration for successul computations: {:.4f} seconds\n"
          "Average Score(Calculated Directly): {}\n\n".format(
          fail_percent, elapsed, elapsed/num_trials, avg_succ_dur, avg_mf))

print(info_str)
print(log_str)

text_file = open('CIFAR10_wholeDemo_var_log.txt','a')
text_file.write(str(datetime.now()))
text_file.write(info_str)
text_file.write(log_str)


class Solution(object):
    def minCost(self, costs):
        """
        :type costs: List[List[int]]
        :rtype: int
        """
        lastMinCost = [0,0,0]

        for cost in costs:
            newMinCost = [0,0,0]
            newMinCost[0] = cost[0] + min(lastMinCost[1], lastMinCost[2])
            newMinCost[1] = cost[1] + min(lastMinCost[2], lastMinCost[0])
            newMinCost[2] = cost[2] + min(lastMinCost[0], lastMinCost[1])

            lastMinCost = newMinCost

        return min(lastMinCost)


import numpy as np
from utils import logger
from Models.utils import loadModel, saveModel
from Models.GeoSoCa.lib.SocialCorrelation import SocialCorrelation

modelName = 'GeoSoCa'

def socialCalculations(datasetName: str, users: dict, pois: dict, trainingMatrix, socialRelations, groundTruth):
    """
    This function is used to calculate the social correlation between users and pois.

    Parameters
    ----------
    datasetName : str
        The name of the dataset.
    users : dict
        The dictionary of users.
    pois : dict
        The dictionary of pois.
    trainingMatrix : np.array
        The training matrix of the dataset.
    socialRelations : dict
        The dictionary containing the social relations of the dataset.
    groundTruth : dict
        The dictionary containing the ground truth of the dataset.

    Returns
    -------
    socialCorrelation : dict
        The dictionary containing the social correlation of the dataset.
    """
    # Initializing parameters
    userCount = users['count']
    logDuration = 1 if userCount < 20 else 10
    SCScores = np.zeros((userCount, pois['count']))
    # Checking for existing model
    logger('Preparing Social Correlation matrix ...')
    loadedModel = loadModel(modelName, datasetName,
                            f'SC_{userCount}User')
    if loadedModel == []:  # It should be created
        # Creating object to AKDE Class
        SC = SocialCorrelation()
        # Social Correlation Calculations
        loadNumpyArray = loadModel(modelName, datasetName, 'Beta')
        if loadNumpyArray == []:  # It should be created
            SC.computeBeta(trainingMatrix, socialRelations)
            saveModel(SC.X, modelName, datasetName, 'Beta')
        else:  # It should be loaded
            SC.loadModel(loadNumpyArray)
        # Calculating SC scores
        print("Now, training the model for each user ...")
        for counter, uid in enumerate(users['list']):
            # Adding log to console
            if (counter % logDuration == 0):
                print(f'User#{counter} processed ...')
            if uid in groundTruth:
                for lid in pois['list']:
                    SCScores[uid, lid] = SC.predict(uid, lid)
        saveModel(SCScores, modelName, datasetName,
                  f'SC_{userCount}User')
    else:  # It should be loaded
        SCScores = loadedModel
    # Returning the scores
    return SCScores


import re

CYCLE = 0
X = 1

with open('input.txt') as f:
    instructions = f.read().splitlines()

instruct_type_cycle_count = {
    'addx': 2,
    'noop': 1
}

for inx in instructions:
    command = re.sub(r'\s-?\d*', '', inx)
    for i in range(instruct_type_cycle_count[command]):
        CYCLE += 1

        if CYCLE % 40 == 1:
            print()
            print(f"Cycle {format(CYCLE, '3d')} ", end='')

        crt_pos = (CYCLE - 1) % 40
        if crt_pos in [X-1, X, X+1]:
            print('#', end='')
        else:
            print('.', end='')

        if command == 'addx':
            if i == 1:
                _, arg = inx.split(' ')
                X += int(arg)

print()


def rotate90(array,n):
    nums = array.split(" ")
    result = []
    for x in range(n):
        temp = []
        for y in range(n):
            temp.append(nums[x + n * y])
        temp.reverse()
        for num in temp:
            result.append(num)
    print(" ".join(result) + " ")

n = eval(input())
while(n != 0):
    n = n - 1
    number = eval(input())
    rotate90(input(),number)


"""
Originally: https://github.com/bobloy/Fox-V3/blob/master/hangman/hangman.py (Thanks mate!)
Heavily altered to fit my needs
"""

import discord
from discord.ext import commands

import asyncio
from collections import defaultdict
from random import randint
from typing import Union

class Hangman(commands.Cog):
    """Lets anyone play a game of hangman with custom phrases"""

    navigate = "🔼🔽"
    letters = "🇦🇧🇨🇩🇪🇫🇬🇭🇮🇯🇰🇱🇲🇳🇴🇵🇶🇷🇸🇹🇺🇻🇼🇽🇾🇿"

    def __init__(self, bot):
        super().__init__()
        self.bot = bot

        self.the_data = defaultdict(
            lambda: {
                "running": False,
                "hangman": 0,
                "guesses": [],
                "trackmessage": False,
                "answer": "",
            }
        )
        self.answer_path = "/app/bot/cogs/hangman/hanganswers.txt"

        self.winbool = defaultdict(lambda: False)

        self.hanglist = {}

    async def red_delete_data_for_user(self, **kwargs):
        """Nothing to delete"""
        return

    async def _update_hanglist(self):
        for guild in self.bot.guilds:
            self.hanglist[guild] = (
                """>
                   \\_________
                    |/
                    |
                    |
                    |
                    |
                    |
                    |\\___
                    """,
                """>
                   \\_________
                    |/   |
                    |
                    |
                    |
                    |
                    |
                    |\\___
                    H""",
                """>
                   \\_________
                    |/   |
                    |   """
                + "😁"
                + """
                    |
                    |
                    |
                    |
                    |\\___
                    HA""",
                """>
                   \\________
                    |/   |
                    |   """
                + "🙄"
                + """
                    |    |
                    |    |
                    |
                    |
                    |\\___
                    HAN""",
                """>
                   \\_________
                    |/   |
                    |   """
                + "😬"
                + """
                    |   /|
                    |     |
                    |
                    |
                    |\\___
                    HANG""",
                """>
                   \\_________
                    |/   |
                    |   """
                + "😔"
                + """
                    |   /|\\
                    |     |
                    |
                    |
                    |\\___
                    HANGM""",
                """>
                   \\________
                    |/   |
                    |   """
                + "😣"
                + """
                    |   /|\\
                    |     |
                    |   /
                    |
                    |\\___
                    HANGMA""",
                """>
                   \\________
                    |/   |
                    |   """
                + "💀"
                + """
                    |   /|\\
                    |     |
                    |   / \\
                    |
                    |\\___
                    HANGMAN""",
            )

    @commands.command(aliases=["hang"])
    async def hangman(self, ctx, guess: str = None):
        """Play a game of hangman against the bot!"""
        if guess is None:
            if self.the_data[ctx.guild]["running"]:
                await ctx.send(
                    "Game of hangman is already running!\nEnter your guess!"
                )
                await self._printgame(ctx.channel)
            else:
                await ctx.send("Starting a game of hangman!")
                self._startgame(ctx.guild)
                await self._printgame(ctx.channel)
        elif not self.the_data[ctx.guild]["running"]:
            await ctx.send(
                "Game of hangman is not yet running!\nStarting a game of hangman!"
            )
            self._startgame(ctx.guild)
            await self._printgame(ctx.channel)
        else:
            await ctx.send("Guess by reacting to the message")

    def _startgame(self, guild):
        """Starts a new game of hangman"""
        self.the_data[guild]["answer"] = self._getphrase().upper()
        self.the_data[guild]["hangman"] = 0
        self.the_data[guild]["guesses"] = []
        self.winbool[guild] = False
        self.the_data[guild]["running"] = True
        self.the_data[guild]["trackmessage"] = False

    def _stopgame(self, guild):
        """Stops the game in current state"""
        self.the_data[guild]["running"] = False
        self.the_data[guild]["trackmessage"] = False

    async def _checkdone(self, channel):
        if self.winbool[channel.guild]:
            await channel.send("You Win!")
            self._stopgame(channel.guild)
        elif self.the_data[channel.guild]["hangman"] >= 7:
            await channel.send(
                "You Lose!\nThe Answer was: **" + self.the_data[channel.guild]["answer"] + "**"
            )

            self._stopgame(channel.guild)

    def _getphrase(self):
        """Get a new phrase for the game and returns it"""

        with open(self.answer_path, "r") as phrasefile:
            phrases = phrasefile.readlines()

        outphrase = ""
        while outphrase == "":
            outphrase = phrases[randint(0, len(phrases) - 1)].partition(" (")[0]
        return outphrase

    def _hideanswer(self, guild):
        """Returns the obscured answer"""
        out_str = ""

        self.winbool[guild] = True
        for i in self.the_data[guild]["answer"]:
            if i in [" ", "-"]:
                out_str += i * 2
            elif i in self.the_data[guild]["guesses"] or i not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ":
                out_str += "__" + i + "__ "
            else:
                out_str += "**\\_** "
                self.winbool[guild] = False

        return out_str

    def _guesslist(self, guild):
        """Returns the current letter list"""
        out_str = "".join(str(i) + "," for i in self.the_data[guild]["guesses"])
        out_str = out_str[:-1]

        return out_str

    async def _guessletter(self, guess, message):
        """Checks the guess on a letter and prints game if acceptable guess"""
        channel = message.channel
        if guess.upper() not in "ABCDEFGHIJKLMNOPQRSTUVWXYZ" or len(guess) != 1:
            await channel.send("Invalid guess. Only A-Z is accepted")
            return

        if guess.upper() in self.the_data[channel.guild]["guesses"]:
            await channel.send("Already guessed that! Try again")
            return
        if guess.upper() not in self.the_data[channel.guild]["answer"]:
            self.the_data[channel.guild]["hangman"] += 1

        self.the_data[channel.guild]["guesses"].append(guess.upper())

        await self._reprintgame(message)

    @commands.Cog.listener("on_reaction_add")
    async def on_react(self, reaction, user: Union[discord.User, discord.Member]):
        """Thanks to flapjack reactpoll for guidelines
        https://github.com/flapjax/FlapJack-Cogs/blob/master/reactpoll/reactpoll.py"""
        guild: discord.Guild = getattr(user, "guild", None)
        if guild is None:
            return

        if reaction.message.id != self.the_data[guild]["trackmessage"]:
            return

        if user.bot:
            return  # Don't react to bot reactions

        message = reaction.message
        emoji = reaction.emoji

        if str(emoji) in self.letters:
            letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[self.letters.index(str(emoji))]
            await self._guessletter(letter, message)
            await message.remove_reaction(emoji, user)
            await asyncio.sleep(1)
            await message.remove_reaction(emoji, self.bot.user)

        if str(emoji) in self.navigate:
            if str(emoji) == self.navigate[0]:
                await self._reactmessage_am(message)

            if str(emoji) == self.navigate[-1]:
                await self._reactmessage_nz(message)

    async def _try_clear_reactions(self, message):
        try:
            await message.clear_reactions()
        except discord.Forbidden:
            pass

    async def _reactmessage_menu(self, message):
        """React with menu options"""
        await self._try_clear_reactions(message)

        await message.add_reaction(self.navigate[0])
        await message.add_reaction(self.navigate[-1])

    async def _reactmessage_am(self, message):
        await self._try_clear_reactions(message)

        for x in range(len(self.letters)):
            if x in [
                i for i, b in enumerate("ABCDEFGHIJKLM") if b not in self._guesslist(message.guild)
            ]:
                await message.add_reaction(self.letters[x])

        await message.add_reaction(self.navigate[-1])

    async def _reactmessage_nz(self, message):
        await self._try_clear_reactions(message)

        for x in range(len(self.letters)):
            if x in [
                i for i, b in enumerate("NOPQRSTUVWXYZ") if b not in self._guesslist(message.guild)
            ]:
                await message.add_reaction(self.letters[x + 13])

        await message.add_reaction(self.navigate[0])

    async def _make_say(self, guild):
        c_say = "Guess this: " + str(self._hideanswer(guild)) + "\n"
        c_say += "Used Letters: " + str(self._guesslist(guild)) + "\n"
        c_say += self.hanglist[guild][self.the_data[guild]["hangman"]] + "\n"
        c_say += "React with {} - {} to guess".format(self.letters[0], self.letters[-1])

        return c_say

    async def _reprintgame(self, message):
        if message.guild not in self.hanglist:
            await self._update_hanglist()

        c_say = await self._make_say(message.guild)

        await message.edit(content=c_say)
        self.the_data[message.guild]["trackmessage"] = message.id

        await self._checkdone(message.channel)

    async def _printgame(self, channel):
        """Print the current state of game"""
        if channel.guild not in self.hanglist:
            await self._update_hanglist()

        c_say = await self._make_say(channel.guild)

        message = await channel.send(c_say)

        self.the_data[channel.guild]["trackmessage"] = message.id

        await self._reactmessage_menu(message)
        await self._checkdone(channel)


import numpy as np
import pygame
from consts import WindowConsts, ScreenConsts, PlayerConsts, BackgroundsConsts, EnemyConsts
from kg_logger import KGLogger, log_function
from objects.bullet import Bullet
from objects.enemy import Enemy
from objects.explosion import Explosion
from objects.player import Player
from objects.star import Star
from utils.timer import Timer

class Game:
    def __init__(self):
        self.logger = KGLogger()
        pygame.init()
        self.screen = pygame.display.set_mode(size=WindowConsts.WINDOW_SIZE)
        self.delta_axis = PlayerConsts.PLAYER_INIT_DELTA_AXIS
        pygame.display.set_caption(WindowConsts.WINDOW_TITLE)
        icon = pygame.image.load(WindowConsts.WINDOW_ICON_PATH)
        pygame.display.set_icon(icon)
        self._load_images()
        self._load_objects()
        self.running = True
        self._player_score = 0
        self._timer = Timer()

    @log_function
    def _load_images(self):
        self._bullet_img = pygame.image.load(BackgroundsConsts.BULLET_IMAGE_PATH)
        self._star_img = pygame.image.load(BackgroundsConsts.STAR_IMAGE_PATH)
        self.small_explosion_img = pygame.image.load(BackgroundsConsts.SMALL_EXPLOSION_IMAGE_PATH)
        self.medium_explosion_img = pygame.image.load(BackgroundsConsts.MEDIUM_EXPLOSION_IMAGE_PATH)
        self.large_explosion_img = pygame.image.load(BackgroundsConsts.LARGE_EXPLOSION_IMAGE_PATH)
        self.player_img = pygame.image.load(PlayerConsts.PLAYER_IMAGE_PATH)

    @log_function
    def _load_objects(self):
        self._stars = []
        self._enemies = []
        self.add_enemy_each_seconds = 3
        self._player = Player()
        self._bullets = []
        self._explosions = []

    @log_function
    def _quit_running(self):
        self.running = False

    def show_score(self):
        font = pygame.font.Font(ScreenConsts.MAIN_FONT_FAMILY, ScreenConsts.MAIN_FONT_SIZE)
        score = font.render(f"Score: {str(self._player_score)}", True, ScreenConsts.WHITE)
        self.screen.blit(score, ScreenConsts.SCORE_AXIS)

    def _set_player(self):
        self.screen.blit(self.player_img, self._player.get_axis())
        self._player.set_speed(speed=PlayerConsts.PLAYER_SPEED + self._speed_depends_time())

    @log_function
    def _create_new_enemies(self):
        new_enemy = Enemy(speed=EnemyConsts.ENEMY_SPEED + self._speed_depends_time())
        self._enemies.append(new_enemy)
        self.logger.info(f"Created enemy")

    def _set_enemy(self):
        if self._timer.time_pass_in_seconds() > self.add_enemy_each_seconds:
            self.add_enemy_each_seconds += EnemyConsts.SET_ENEMY_EVERY_SECONDS + np.random.randint(0, 3)
            if len(self._enemies) < EnemyConsts.MAX_ENEMY_ON_SCREEN:
                self._create_new_enemies()
            self.logger.debug(f"Next enemy will appears at {self.add_enemy_each_seconds} sec")
        for enemy in self._enemies:
            if enemy.is_done():
                self._player_score += 3
                self._enemies.remove(enemy)
            enemy.random_move()
            enemy.handle_out_of_board()
            self.screen.blit(enemy.img, enemy.get_axis())

    def _create_new_star(self):
        if len(self._stars) < BackgroundsConsts.NUM_OF_STARS:
            new_star = Star()
            self._stars.append(new_star)

    def _set_starts(self):
        self._create_new_star()
        for star in self._stars:
            if star.is_done():
                self._stars.remove(star)
            star.move()
            self.screen.blit(self._star_img, star.get_axis())

    def _shoot_bullet(self):
        bullet_axis = self._player.get_middle_axis()
        bullet = Bullet(axis=bullet_axis)
        self._bullets.append(bullet)

    def _add_explosion(self, axis: np.array, explosion_type: str):
        explosion = Explosion(axis=axis, explosion_type=explosion_type)
        self._explosions.append(explosion)

    def _get_explosion_image(self, explosion_type):
        if explosion_type == BackgroundsConsts.S_EXPLOSION:
            return self.small_explosion_img
        elif explosion_type == BackgroundsConsts.M_EXPLOSION:
            return self.medium_explosion_img
        elif explosion_type == BackgroundsConsts.L_EXPLOSION:
            return self.large_explosion_img

    def _set_explosion(self):
        for explosion in self._explosions:
            if explosion.is_done():
                self._explosions.remove(explosion)
            explosion.move()
            self.screen.blit(self._get_explosion_image(explosion.explosion_type), explosion.get_axis())

    def _bullet_hit_enemy(self, bullet: Bullet):
        for enemy in self._enemies:
            if enemy.is_axis_in_enemy(other_axis=bullet.get_axis()):
                self.logger.error(f"hit enemy")
                bullet.set_done()
                enemy.hit()
                enemy_axis = enemy.get_middle_axis()
                explosion_type = BackgroundsConsts.S_EXPLOSION
                if enemy.damage == 2:
                    explosion_type = BackgroundsConsts.S_EXPLOSION
                elif enemy.damage == 1:
                    explosion_type = BackgroundsConsts.M_EXPLOSION
                elif enemy.damage == 0:
                    explosion_type = BackgroundsConsts.L_EXPLOSION
                self._add_explosion(axis=enemy_axis, explosion_type=explosion_type)
                self._player_score += 1

    def _set_bullets(self):
        for bullet in self._bullets:
            if bullet.is_done():
                self._bullets.remove(bullet)
            if not bullet.is_done():
                self._bullet_hit_enemy(bullet=bullet)
            bullet.move()
            self.screen.blit(self._bullet_img, bullet.get_axis())

    def _speed_depends_time(self) -> float:
        return self._timer.time_pass_in_seconds() / 500

    def _handle_key_events(self, event):
        if event.type == pygame.KEYUP:
            if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
                self.delta_axis[0] = 0.
            if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
                self.delta_axis[1] = 0.

        if event.type == pygame.KEYDOWN:
            if event.key == pygame.K_LEFT:
                self.delta_axis[0] -= self._player.speed
            if event.key == pygame.K_RIGHT:
                self.delta_axis[0] += self._player.speed
            if event.key == pygame.K_UP:
                self.delta_axis[1] -= self._player.speed
            if event.key == pygame.K_DOWN:
                self.delta_axis[1] += self._player.speed
            if event.key == pygame.K_SPACE:
                self._shoot_bullet()

    def run(self):
        while self.running:
            self.screen.fill(ScreenConsts.SCREEN_INIT_RGB)
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self._quit_running()
                self._handle_key_events(event)

            self._player.add_axis(self.delta_axis)
            self._player.handle_out_of_board()
            self._set_player()
            self._set_enemy()
            self._set_starts()
            self._set_bullets()
            self._set_explosion()
            self.show_score()
            self._timer.print_seconds()
            pygame.display.update()
        self.logger.info(f"Time played in total seconds: {self._timer.time_pass_in_seconds()}")


from mockify import _utils

from ._base import BaseMock
from ._mock import Mock

__all__ = export = _utils.ExportList()  # pylint: disable=invalid-all-format

@export
class MockFactory(BaseMock):
    """A factory class used to create groups of related mocks.

    This class allows to create mocks using class given by *mock_class*
    ensuring that:

    * names of created mocks are **unique**,
    * all mocks share one common session object.

    Instances of this class keep track of created mocks. Moreover, functions
    that would accept :class:`Mock` instances will also accept
    :class:`MockFactory` instances, so you can later f.e. check if all
    created mocks are satisfied using just a factory object. That makes it
    easy to manage multiple mocks in large test suites.

    See :ref:`managing-multiple-mocks` for more details.

    .. versionchanged:: 0.8
        Now it inherits from :class:`mockify.mock.BaseMock`, as this class is
        more or less special kind of mock.

    .. versionadded:: 0.6

    :param name:
        This is optional.

        Name of this factory to be used as a common prefix for all created
        mocks and nested factories.

    :param mock_class:
        The class that will be used by this factory to create mocks.

        By default it will use :class:`Mock` class.

    .. versionchanged:: 0.9
        Removed parameter ``session`` in favour of ``**kwargs``; session
        handling is now done by :class:`BaseMock` class.
    """

    def __init__(self, name=None, mock_class=None, **kwargs):
        super().__init__(name, **kwargs)
        self._mock_class = mock_class or Mock
        self._factories = {}
        self._mocks = {}

    def __m_children__(self):
        yield from self._mocks.values()
        yield from self._factories.values()

    def __m_expectations__(self):
        for mock in self._mocks.values():
            yield from mock.__m_expectations__()

    def __repr__(self):
        return "<{self.__module__}.{self.__class__.__name__}({self.__m_fullname__!r})>".format(
            self=self
        )

    def mock(self, name):
        """Create and return mock of given *name*.

        This method will raise :exc:`TypeError` if *name* is already used by
        either mock or child factory.
        """
        self._raise_if_name_is_in_use(name)
        self._mocks[name] = tmp = self._mock_class(name, parent=self)
        return tmp

    def factory(self, name):
        """Create and return child factory.

        Child factory will use session from its parent, and will prefix all
        mocks and grandchild factories with given *name*.

        This method will raise :exc:`TypeError` if *name* is already used by
        either mock or child factory.

        :rtype: MockFactory
        """
        self._raise_if_name_is_in_use(name)
        self._factories[name] = tmp =\
            self.__class__(
                name=name,
                mock_class=self._mock_class,
                parent=self)
        return tmp

    def _raise_if_name_is_in_use(self, name):
        if name in self._mocks or name in self._factories:
            raise TypeError("Name {!r} is already in use".format(name))


# -*- coding: utf-8 -*-
import scrapy
from time import sleep
from selenium import webdriver
from scrapy.selector import Selector
from selenium.common.exceptions import NoSuchElementException
from selenium.webdriver import ActionChains
from selenium.webdriver.chrome.options import Options
import csv

class MinimalSpider(scrapy.Spider):
    name = 'minimal'
    allowed_domains = ['tokopedia.com']

    def start_requests(self):
        # open csv file
        with open('./csvfiles/produri.csv', newline='') as csvfile:
            record = csv.reader(csvfile, delimiter=',')
            for i in record:
                # [id, uri, helpfulreviewerscnt]
                yield scrapy.Request(i[0], callback=self.parse)

    def parse(self, response):
        user_agent = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36'
        options = webdriver.ChromeOptions()
        options.add_argument(f'user-agent={user_agent}')
        options.add_argument('--headless')
        self.driver = webdriver.Chrome(chrome_options=options,
                executable_path='/usr/bin/chromedriver')
        self.driver.get(response.request.url)

        # sleep for 10 seconds
        sleep(10)
        self.logger.info('Sleeping for 10 seconds')

        # extract source page
        selProdPage = Selector(text=self.driver.page_source)

        # Minimal beli
        minpurchase = selProdPage.xpath(
                '//div[contains(@class, "product-content-container")]/descendant::dd[contains(@class, "pull-left m-0 border-none")]/text()'
                ).extract()[1]
        minpurchase = minpurchase.strip()

        yield {
                'uri':response.request.url,
                'minpurchase':minpurchase
                }

        self.driver.quit()


# ========================================================= OOP EMAIL PROGRAM =========================================================#

# Define email class
# Initiate constructor function using from_address and email_contents as parameters
# Set read and spam values as False when first opening inbox

# ========================================================= CLASS =========================================================#

class Email:

    def __init__(self, from_address, email_contents):
        self.from_address = from_address
        self.email_contents = email_contents
        self.has_been_read = False
        self.is_spam = False

# Define method to mark_as_read and mark_as_spam as True

    def mark_as_read(self):
        self.has_been_read = True

    def mark_as_spam(self):
        self.is_spam = True

# ========================================================= FUNCTIONS =========================================================#

# Set an empty inbox variable for all mails to be stored in

inbox = []
spambox= []
unreadbox = []

# Define a function that adds new mail to the inbox ==> in this case is used with the 'send' option as per task instruction
# Add the new message as a new Email object and append it to inbox[]
# This will reflect on inbox count
# Set functions for counting spam and unread

def add_email(from_address, email_contents):
    add_message = Email(from_address, email_contents)
    inbox.append(add_message)

def get_count(inbox):
    return len(inbox)

def get_count_spam(spambox):
    return len(spambox)

def get_count_unread(unreadbox):
    return len(unreadbox)

# Set a function that pulls the email the user has selected from the index
# Use the enumerate function to index each mail
# Print using format

def get_email(index):
    get_message = inbox[index]
    for i, email in enumerate(inbox):
        print("-------------------------------------------")
        print(f'''\nFrom\n\n{get_message.from_address}\nContents\n\n{get_message.email_contents}''')
        print("-------------------------------------------")
        return inbox

# Set a function that pulls unread emails
# Use the enumerate function to index each mail
# If the mail has an attribute of not being read, append to unread inbox
# Print using format

def get_unread_emails():
    unread = []
    for i , email in enumerate(inbox):
        if not email.has_been_read:
            un_email = inbox[i]
            unread.append(un_email)
            unreadbox.append(unread)
            print(f"{i + 1}. From {un_email.from_address} Contents {un_email.email_contents}")
    return unread

# Set a function that pulls spam emails
# Use the enumerate function to index each mail
# If the mail has an attribute of being spam, append to spam inbox
# Print using format

def get_spam_emails():
    spam =[]
    for i, email in enumerate(inbox):
        if email.is_spam:
            get_spam = inbox[i]
            spam.append(get_spam)
            spambox.append(spam)
            print(f"{i + 1}. From {get_spam.from_address} Contents {get_spam.email_contents}")
    return spam

# Set a function that allows user to select indexed mail to spam
# Mark as spam

def add_spam(index):
    add_user_spam = inbox[index]
    add_user_spam.mark_as_spam()
    print("\nEmail added to spam\n")
    print("\nPlease select another option from the menu\n")

# Set a function that allows user to select indexed mail to mark as read
# Mark as read

def mark_read(index):
    read_message = inbox[index]
    read_message.mark_as_read()
    print("\nEmail marked as read")
    print("\nPlease select another option from the menu\n")

# Set a function that allows user to select indexed mail to delete
# Using try-except in case there is an error whilst deleting
# Delete using remove() function

def delete(index):
    try:
        inbox.remove(inbox[index])
        print("\nEmail deleted\n")
        print("\nPlease select another option from the menu\n")
    except:
        print("\nSorry email could not be deleted\n")
        print("\nPlease select another option from the menu\n")

# ========================================================= OOP EMAIL PROGRAM =========================================================#

# Print output menu and mailbox

print(input('''\n==============================WELCOME TO HYPERIONDEV MAILBOX==============================\n
                            Please push enter to continue'''))

user_choice = ""

# Using a loop for each menu selection as well as lower() to avoid errors

while user_choice != "quit":

    user_choice = input('''\nWhat would you like to do:
                            read
                            mark spam
                            send
                            delete
                            quit\n\n''').lower()

# If user_choice is read, prompt accordingly
# Get index from user to mark as read
# Minus 1 from selection because of indexing

    if user_choice == "read":
        print(f"\nINBOX {get_count(inbox)}\n")
        get_unread_emails()
        num = int(input("\nPlease select the index you would like to mark as read:\n\n"))
        get_email(num-1)
        mark_read(num-1)

# If user_choice is mark as spam, prompt accordingly
# Get index from user to mark as spam
# Minus 1 from selection because of indexing
# Print spam emails

    elif user_choice == "mark spam":
        print(f"\nSPAM {get_count_spam(spambox)}\n")
        get_unread_emails()
        user_mark_spam = int(input("\nPlease select the index you would like to mark as spam:\n\n"))
        get_email(user_mark_spam-1)
        add_spam(user_mark_spam-1)
        get_spam_emails()

# If user_choice is send, prompt accordingly
# Get from_address and email_contents from user

    elif user_choice == "send":
        from_address = input("\nPlease enter your email address:\n\n")
        email_contents = input("\nPlease type out your email:\n\n")
        add_email(from_address, email_contents)

# If user_choice is delete, prompt accordingly
# Get index from user to mark as spam
# Minus 1 from selection because of indexing

    elif user_choice == "delete":
        get_unread_emails()
        user_delete = int(input("\nPlease enter the index you would like to delete:\n"))
        delete(user_delete-1)

# If user_choice is quit, say goodbye

    elif user_choice == "quit":
        print("Goodbye")

# Else , in case of typos, prompt accordingly

    else:
        print("Oops - incorrect input")

# ========================================================= END OF EMAIL PROGRAM =========================================================#


import sys

input = sys.stdin.readline

lstA = list(input().rstrip())
lstB = []
for _ in range(int(input())):
    command = input().split()

    if command[0] == 'L':
        if lstA:
            lstB.append(lstA.pop())

    elif command[0] == 'D':
        if lstB:
            lstA.append(lstB.pop())

    elif command[0] == 'B':
        if lstA:
            lstA.pop()

    else:
        lstA.append(command[1])

lstA.extend(reversed(lstB))
print(''.join(lstA))