dict = {}
for _ in range(int(input('input a number'))):
    name = input('enter a name')
    score = float(input('input a figure'))
    dict[name] = score

print(dict)
sorting=[]
for value in dict.values():
    sorting.append(value)
print(sorting)
new_value=list(set(sorting))
print(new_value)
new_value.sort()
print(new_value)
second_lowest=new_value[1]
print(second_lowest)


from djitellopy import Tello
import tensorflow as tf
from enum import Enum, auto
import numpy as np
import math
import cv2
import torch
import time

#          x1 (pixels)  y1 (pixels)  x2 (pixels)  y2 (pixels)   confidence        class
# tensor([[4.13370e+02, 2.51479e+02, 6.40000e+02, 4.80000e+02, 2.58489e-01, 0.00000e+00]])

model = torch.hub.load('ultralytics/yolov5', 'custom', path='C:\\Users\\user\\PycharmProjects\\LockheedMatin\\3rd_qualification\\main_dir\\last.pt', force_reload=True)

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

width = 640  # WIDTH OF THE IMAGE
height = 480  # HEIGHT OF THE IMAGE
deadZone = 60
threshold = 0.90

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

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

start_time = time.time()
hover_time = 0
init_hover_time = True
global init_digit_height
init_digit_height = False
global digit_height_1
digit_height_1 = False
global digit_height_2
digit_height_2 = False

default_drone_up_down = 10
slow_drone_up_down = 5
default_drone_right_left = 10
slow_drone_right_left = 5
default_drone_forward_backward = 10
slow_drone_forward_backward = 5
default_drone_yaw = 30
slow_drone_yaw = 5

QR_hover_detect = False
start_hover = False
global init_height
init_height = True

global detect_G
detect_G = False
digit_G = False

global detect_R
detect_R = False
digit_R = False

global detect_B
detect_B = False
digit_B = False

global searching_mode
searching_mode = False

global is_save_drone_height
is_save_drone_height = False
save_drone_height = None
global qr_data
qr_data = None

global is_done_mission
is_done_mission = True

lower_blue = np.array([100, 150, 0])
upper_blue = np.array([140, 255, 255])

lower_green = np.array([0, 0, 0])
upper_green = np.array([360, 255, 50])

lower_red = np.array([0, 50, 50])
upper_red = np.array([10, 255, 255])

f = open("dji_tello_main_test_log.txt", 'w')
global log_str
log_str = ''

frameWidth = width
frameHeight = height
global center_block_area
center_block_area = int((frameWidth / 3) * (frameHeight / 3))
# cap = cv2.VideoCapture(1)
# cap.set(3, frameWidth)
# cap.set(4, frameHeight)
# cap.set(10,200)

global imgContour
global dir

# drone, web_cam 구분용 클래스, 변수
class Cam(Enum):
    DRONE = auto()
    WEBCAM = auto()

# 명령용 enum 클래스
class Order(Enum):
    default_mode = auto()

    GO_LEFT = auto()
    TURN_LEFT = auto()

    GO_RIGHT = auto()
    TURN_RIGHT = auto()

    DO_SEARCH = auto()

    GO_UP = auto()
    GO_DOWN = auto()

    GO_FORWARD = auto()
    GO_BACKWARD = auto()

    GO_POS1 = auto()
    GO_POS2 = auto()
    GO_POS3 = auto()
    GO_POS4 = auto()

def stackImages(scale, imgArray):
    rows = len(imgArray)
    cols = len(imgArray[0])
    rowsAvailable = isinstance(imgArray[0], list)
    width = imgArray[0][0].shape[1]
    height = imgArray[0][0].shape[0]
    if rowsAvailable:
        for x in range(0, rows):
            for y in range(0, cols):
                if imgArray[x][y].shape[:2] == imgArray[0][0].shape[:2]:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (0, 0), None, scale, scale)
                else:
                    imgArray[x][y] = cv2.resize(imgArray[x][y], (imgArray[0][0].shape[1], imgArray[0][0].shape[0]),
                                                None, scale, scale)
                if len(imgArray[x][y].shape) == 2: imgArray[x][y] = cv2.cvtColor(imgArray[x][y], cv2.COLOR_GRAY2BGR)
        imageBlank = np.zeros((height, width, 3), np.uint8)
        hor = [imageBlank] * rows
        hor_con = [imageBlank] * rows
        for x in range(0, rows):
            hor[x] = np.hstack(imgArray[x])
        ver = np.vstack(hor)
    else:
        for x in range(0, rows):
            if imgArray[x].shape[:2] == imgArray[0].shape[:2]:
                imgArray[x] = cv2.resize(imgArray[x], (0, 0), None, scale, scale)
            else:
                imgArray[x] = cv2.resize(imgArray[x], (imgArray[0].shape[1], imgArray[0].shape[0]), None, scale, scale)
            if len(imgArray[x].shape) == 2: imgArray[x] = cv2.cvtColor(imgArray[x], cv2.COLOR_GRAY2BGR)
        hor = np.hstack(imgArray)
        ver = hor
    return ver

def getContours(img, imgContour, detect_color, xyxy, object_detect=False):
    global dir
    global searching_mode
    global detect_G
    global detect_R
    global detect_B
    global init_digit_height
    global log_str
    global center_block_area
    contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
    if not object_detect:
        if len(contours) != 0 and searching_mode:
            __cnt = contours[0]
            __area = cv2.contourArea(__cnt)

            for cnt in contours:
                area = cv2.contourArea(cnt)

                if __area <= area:
                    __area = area

                # areaMin = cv2.getTrackbarPos("Area", "Parameters")
                if __area > 250:
                    cv2.drawContours(imgContour, cnt, -1, (255, 0, 255), 7)
                    peri = cv2.arcLength(__cnt, True)
                    approx = cv2.approxPolyDP(__cnt, 0.02 * peri, True)
                    # print(len(approx))
                    x, y, w, h = cv2.boundingRect(approx)
                    cx = int(x + (w / 2))  # CENTER X OF THE OBJECT
                    cy = int(y + (h / 2))  # CENTER Y OF THE OBJECT

                    if cx > (int(frameWidth / 2) + deadZone) and cy < (int(frameHeight / 2) - deadZone):
                        cv2.putText(imgContour, ' GO POS1 ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.GO_POS1

                    elif cx < (int(frameWidth / 2) - deadZone) and cy < (int(frameHeight / 2) - deadZone):
                        cv2.putText(imgContour, ' GO POS2 ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.GO_POS2

                    elif cx < (int(frameWidth / 2) - deadZone) and cy > (int(frameHeight / 2) + deadZone):
                        cv2.putText(imgContour, ' GO POS3 ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.GO_POS3

                    elif cx > (int(frameWidth / 2) + deadZone) and cy > (int(frameHeight / 2) + deadZone):
                        cv2.putText(imgContour, ' GO POS4 ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.GO_POS4

                    elif cx < (int(frameWidth / 2) - deadZone):
                        cv2.putText(imgContour, ' TURN LEFT ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.TURN_LEFT

                    elif cx > (int(frameWidth / 2) + deadZone):
                        cv2.putText(imgContour, ' TURN RIGHT ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.TURN_RIGHT

                    elif cy < (int(frameHeight / 2) - deadZone):
                        cv2.putText(imgContour, ' GO UP ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.GO_UP

                    elif cy > (int(frameHeight / 2) + deadZone):
                        cv2.putText(imgContour, ' GO DOWN ', (20, 50), cv2.FONT_HERSHEY_COMPLEX, 1, (0, 0, 255), 3)
                        dir = Order.GO_DOWN

                    else:
                        if ((center_block_area - 8000) <= int(__area)) and (int(__area) <= (center_block_area + 4000)):
                            # dir = Order.default_mode
                            # QR 위치로 고도 조정
                            init_digit_height = True
                            searching_mode = False

                            if detect_color == 'G':
                                print('G 중심 영역 도달')
                                log_str += 'G 중심 영역 도달\n'
                                print(f'Area : {__area}')
                                log_str += f'Area : {__area}\n'
                                detect_G = True
                                print('detect Color : Green')
                                log_str += 'detect Color : Green\n'

                            elif detect_color == 'R':
                                print('R 중심 영역 도달')
                                log_str += 'R 중심 영역 도달\n'
                                print(f'Area : {__area}')
                                log_str += f'Area : {__area}\n'
                                detect_R = True
                                print('detect Color : Red')
                                log_str += 'detect Color : Red\n'

                            elif detect_color == 'B':
                                print('B 중심 영역 도달')
                                log_str += 'B 중심 영역 도달\n'
                                print(f'Area : {__area}')
                                log_str += f'Area : {__area}\n'
                                detect_B = True
                                print('detect Color : Blue')
                                log_str += 'detect Color : Blue\n'

                            print(f'center_block_area = {center_block_area}')
                            log_str += f'center_block_area = {center_block_area}\n'
                            dir = Order.default_mode

                        elif int(__area) > center_block_area + 4000:
                            dir = Order.GO_BACKWARD
                            print(f'기준치보다 큼, area = {__area}')
                            log_str += f'기준치보다 큼, area = {__area}\n'

                        elif int(__area) < center_block_area - 8000:
                            dir = Order.GO_FORWARD
                            print(f'기준치보다 작음, area = {__area}')
                            log_str += f'기준치보다 작음, area = {__area}\n'

                        else:
                            pass

                    cv2.line(imgContour, (int(frameWidth / 2), int(frameHeight / 2)), (cx, cy), (0, 0, 255), 3)
                    cv2.rectangle(imgContour, (x, y), (x + w, y + h), (0, 255, 0), 5)
                    cv2.putText(imgContour, "Points: " + str(len(approx)), (x + w + 20, y + 20), cv2.FONT_HERSHEY_COMPLEX,
                                .7, (0, 255, 0), 2)
                    cv2.putText(imgContour, "Area: " + str(int(__area)), (x + w + 20, y + 45), cv2.FONT_HERSHEY_COMPLEX,
                                0.7, (0, 255, 0), 2)
                    cv2.putText(imgContour, " " + str(int(x)) + " " + str(int(y)), (x - 20, y - 45),
                                cv2.FONT_HERSHEY_COMPLEX, 0.7, (0, 255, 0), 2)
                else:
                    dir = Order.default_mode
        elif searching_mode:
            dir = Order.DO_SEARCH
        elif not searching_mode:
            pass
        else:
            dir = Order.default_mode
            log_str += 'getContours에서 디폴트\n'
    else:
        print('진입')
        if len(xyxy) != 0:
            cv2.rectangle(imgContour, (xyxy[0], xyxy[1]), (xyxy[2], xyxy[3]), (0, 255, 0), 5)

def display(img):
    cv2.line(img, (int(frameWidth / 2) - deadZone, 0), (int(frameWidth / 2) - deadZone, frameHeight), (255, 255, 0), 3)
    cv2.line(img, (int(frameWidth / 2) + deadZone, 0), (int(frameWidth / 2) + deadZone, frameHeight), (255, 255, 0), 3)
    cv2.circle(img, (int(frameWidth / 2), int(frameHeight / 2)), 5, (0, 0, 255), 5)
    cv2.line(img, (0, int(frameHeight / 2) - deadZone), (frameWidth, int(frameHeight / 2) - deadZone), (255, 255, 0), 3)
    cv2.line(img, (0, int(frameHeight / 2) + deadZone), (frameWidth, int(frameHeight / 2) + deadZone), (255, 255, 0), 3)

def handwritten_do_daction(_drone, digit):
    global log_str
    global dir
    global init_height

    if digit == 1:
        print(f'digit = {digit}')
        log_str += f'digit = {digit}\n'
        _drone.move_back(30)
        _drone.move_forward(30)
        init_height = True

    elif digit == 2:
        print(f'digit = {digit}')
        log_str += f'digit = {digit}\n'
        # 안전거리 확보
        _drone.move_left(30)
        _drone.move_right(30)
        init_height = True

    elif digit == 3:
        print(f'digit = {digit}')
        log_str += f'digit = {digit}\n'
        _drone.rotate_clockwise(360)
        init_height = True

    elif digit == 4:
        print(f'digit = {digit}')
        log_str += f'digit = {digit}\n'
        # 안전거리 확보
        _drone.move_right(10)
        _drone.move_up(10)
        _drone.move_left(10)
        _drone.move_down(10)
        init_height = True

    elif digit == 5:
        print(f'num = {digit}')
        log_str += f'num = {digit}\n'
        _drone.flip_backward()
        init_height = True

    elif digit == 6:
        print(f'num = {digit}')
        log_str += f'num = {digit}\n'
        _drone.move_up(30)
        _drone.flip_backward()
        _drone.move_down(30)
        init_height = True

    elif digit == 7:
        print(f'num = {digit}')
        log_str += f'num = {digit}\n'
        _drone.flip_left()
        init_height = True

    elif digit == 8:
        print(f'num = {digit}')
        log_str += f'num = {digit}\n'
        _drone.move_up(30)
        _drone.move_down(30)
        init_height = True

    elif digit == 9:
        print(f'num = {digit}')
        log_str += f'num = {digit}\n'
        _drone.flip_backward()
        init_height = True

    else:
        pass

def empty(a):
    pass

cv2.namedWindow("Parameters")
cv2.resizeWindow("Parameters", 640, 240)
cv2.createTrackbar("Threshold1", "Parameters", 166, 255, empty)
cv2.createTrackbar("Threshold2", "Parameters", 171, 255, empty)
cv2.createTrackbar("Area", "Parameters", 1750, 30000, empty)

color = None
detect_color = None
cap = None
drone = None
WIDTH = None
HEIGHT = None
my_cam = Cam.WEBCAM
if my_cam == Cam.WEBCAM:
    cap = cv2.VideoCapture(0)
    WIDTH = cap.get(cv2.CAP_PROP_FRAME_WIDTH)
    HEIGHT = cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
elif my_cam == Cam.DRONE:
    # --------------------------------------------------------------------

    startCounter = True

    # CONNECT TO TELLO
    drone = Tello()
    drone.connect()
    drone.for_back_velocity = 0
    drone.left_right_velocity = 0
    drone.up_down_velocity = 0
    drone.yaw_velocity = 0
    drone.speed = 0

    print(drone.get_battery())
    log_str += ('drone_battery : ' + str(drone.get_battery()) + '%\n')

    drone.streamoff()
    drone.streamon()

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

recog_w = 250
recog_h = 150
object_detection = True

try:
    while True:
        if my_cam == Cam.DRONE:
            # GET THE IMAGE FROM TELLO
            frame_read = drone.get_frame_read()
            myFrame = frame_read.frame

            img = cv2.resize(myFrame, (width, height))
            copy_img = img.copy()
            imgContour = img.copy()
            imgHsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
            drone_height = drone.get_height()
            dir = Order.default_mode

            h_min = cv2.getTrackbarPos("HUE Min", "HSV")
            h_max = cv2.getTrackbarPos("HUE Max", "HSV")
            s_min = cv2.getTrackbarPos("SAT Min", "HSV")
            s_max = cv2.getTrackbarPos("SAT Max", "HSV")
            v_min = cv2.getTrackbarPos("VALUE Min", "HSV")
            v_max = cv2.getTrackbarPos("VALUE Max", "HSV")

            lower = np.array([h_min, s_min, v_min])
            upper = np.array([h_max, s_max, v_max])

            # takeoff
            if startCounter:
                drone.takeoff()
                startCounter = False

            # 0단계, 고도 초기화 단계
            if init_height and not startCounter:
                print(f'------ 고도 초기화 중 ({drone_height}) ------')
                log_str += f'------ 고도 초기화 중 ({drone_height}) ------\n'
                if drone_height == 60:
                    init_height = False
                    print('------ 고도 초기화 종료 ------')
                    log_str += '------ 고도 초기화 종료 ------\n'
                    if QR_hover_detect:
                        drone.move_right(20)
                        searching_mode = True

                elif drone_height < 60:
                    dir = Order.GO_UP
                else:
                    dir = Order.GO_DOWN
            else:
                # 1단계, 'hover' QR 탐색
                if not QR_hover_detect:
                    print('1단계 : hover QR 탐색중')
                    log_str += '1단계 : hover QR 탐색중\n'
                    qrDecoder = cv2.QRCodeDetector()

                    # hover QR을 찾는 중
                    data, bbox, rectifiedImage = qrDecoder.detectAndDecode(img)
                    if len(data) > 0 and data == 'hover':
                        print("Decoded Data : {}".format(data))
                        log_str += f'Decoded Data : {data}\n'
                        rectifiedImage = np.uint8(rectifiedImage)
                        start_hover = True
                        QR_hover_detect = True

                # 1.5단계, 'hover' QR 탐지 시 호버링
                if start_hover:
                    if init_hover_time:
                        print('호버링 시작')
                        log_str += '호버링 시작\n'
                        hover_time = time.time()
                        init_hover_time = False
                        dir = Order.default_mode
                    interval = time.time() - hover_time
                    if (5.0 <= interval) and (interval <= 5.1):
                        start_hover = False
                        print(f'호버링 끝, interval : {interval}')
                        log_str += f'호버링 끝, interval : {interval}\n'

                # 2단계, G R B 탐색 및 숫자 인식 후 미션 수행 단계
                if QR_hover_detect and not start_hover:
                    searching_mode = True
                    print('2단계 : 현재 ', end='')
                    log_str += '2단계 : 현재 '
                    # Green 탐색 단계
                    if not detect_G:
                        print('Green 탐색 중')
                        log_str += 'Green 탐색 중\n'
                        lower = lower_green
                        upper = upper_green
                        detect_color = 'G'

                    # Green에 접근했지만, 숫자를 못 읽었을 때
                    if detect_G and not digit_G:
                        searching_mode = False
                        digit = recognition_digit(img)
                        if init_digit_height:
                            print(f'------ 고도 초기화 중 ({drone_height}) ------')
                            log_str += f'------ 고도 초기화 중 ({drone_height}) ------\n'
                            if drone_height == 50:
                                init_digit_height = False
                                print('------ 고도 초기화 종료 ------')
                                log_str += '------ 고도 초기화 종료 ------\n'

                            elif drone_height < 50:
                                dir = Order.GO_UP
                            else:
                                dir = Order.GO_DOWN
                        # 숫자를 찾고 그에 따른 미션을 수행함
                        if digit is not None:
                            digit_G = True
                            init_height = False
                            handwritten_do_daction(drone, digit)
                            searching_mode = False

                    # Green 탐색 후 Red 탐색 단계
                    if detect_G and not detect_R and digit_G:
                        print('Red 탐색 중')
                        log_str += 'Red 탐색 중\n'
                        lower = lower_red
                        upper = upper_red
                        detect_color = 'R'

                    # Red에 접근했지만, 숫자를 못 읽었을 때
                    if detect_G and detect_R and not digit_R:
                        searching_mode = False
                        digit = recognition_digit(img)
                        if init_digit_height:
                            print(f'------ 고도 초기화 중 ({drone_height}) ------')
                            log_str += f'------ 고도 초기화 중 ({drone_height}) ------\n'
                            if drone_height == 50:
                                init_digit_height = False
                                print('------ 고도 초기화 종료 ------')
                                log_str += '------ 고도 초기화 종료 ------\n'

                            elif drone_height < 50:
                                dir = Order.GO_UP
                            else:
                                dir = Order.GO_DOWN
                        # 숫자를 찾고 그에 따른 미션을 수행함
                        if digit is not None:
                            digit_R = True
                            init_height = False
                            handwritten_do_daction(drone, digit)
                            searching_mode = False

                    # Green, Red 탐색 후 Blue 탐색 단계
                    if detect_G and detect_R and not detect_B and digit_G and digit_R:
                        print('Blue 탐색 중')
                        log_str += 'Blue 탐색 중\n'
                        lower = lower_blue
                        upper = upper_blue
                        detect_color = 'B'

                    # Blue에 접근했지만, 숫자를 못 읽었을 때
                    if detect_G and detect_R and detect_B and not digit_B:
                        searching_mode = False
                        digit = recognition_digit(img)
                        if init_digit_height:
                            print(f'------ 고도 초기화 중 ({drone_height}) ------')
                            log_str += f'------ 고도 초기화 중 ({drone_height}) ------\n'
                            if drone_height == 50:
                                init_digit_height = False
                                print('------ 고도 초기화 종료 ------')
                                log_str += '------ 고도 초기화 종료 ------\n'

                            elif drone_height < 50:
                                dir = Order.GO_UP
                            else:
                                dir = Order.GO_DOWN
                        # 숫자를 찾고 그에 따른 미션을 수행함
                        if digit is not None:
                            digit_B = True
                            init_height = False
                            handwritten_do_daction(drone, digit)
                            searching_mode = False

                    # G, B, R의 QR을 모두 감지 시 강제 종료
                    if digit_G and digit_R and digit_B:
                        raise KeyboardInterrupt

            mask = cv2.inRange(imgHsv, lower, upper)
            result = cv2.bitwise_and(img, img, mask=mask)
            mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)

            # imgBlur = cv2.GaussianBlur(result, (7, 7), 1)
            imgBlur = cv2.medianBlur(result, 5)
            imgGray = cv2.cvtColor(imgBlur, cv2.COLOR_BGR2GRAY)
            threshold1 = cv2.getTrackbarPos("Threshold1", "Parameters")
            threshold2 = cv2.getTrackbarPos("Threshold2", "Parameters")
            imgCanny = cv2.Canny(imgGray, 50, 50)
            kernel = np.ones((5, 5))
            imgDil = cv2.dilate(imgCanny, kernel, iterations=1)

            getContours(imgDil, imgContour, color)
            display(imgContour)

            # GO_LEFT
            if dir == Order.GO_LEFT:
                drone.left_right_velocity = -10
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = 0

            # TURN_LEFT
            elif dir == Order.TURN_LEFT:
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = -10

            # GO_RIGHT
            elif dir == Order.GO_RIGHT:
                drone.left_right_velocity = 10
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = 0

            # TURN_RIGHT
            elif dir == Order.TURN_RIGHT:
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = 10

            # GO_UP
            elif dir == Order.GO_UP:
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 15
                drone.yaw_velocity = 0

            # GO_DOWN
            elif dir == Order.GO_DOWN:
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = -15
                drone.yaw_velocity = 0

            # GO_FORWARD
            elif dir == Order.GO_FORWARD:
                drone.left_right_velocity = 0
                drone.for_back_velocity = 10
                drone.up_down_velocity = 0
                drone.yaw_velocity = 0

            # GO_BACKWARD
            elif dir == Order.GO_BACKWARD:
                drone.left_right_velocity = 0
                drone.for_back_velocity = -10
                drone.up_down_velocity = 0
                drone.yaw_velocity = 0

            # GO_POS1
            elif dir == Order.GO_POS1:
                # drone.up_down_velocity = 5
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = 10

            # GO_POS2
            elif dir == Order.GO_POS2:
                # drone.up_down_velocity = 5
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = -10

            # GO_POS3
            elif dir == Order.GO_POS3:
                # drone.up_down_velocity = -5
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = -10

            # GO_POS4
            elif dir == Order.GO_POS4:
                # drone.up_down_velocity = -5
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = 10

            # DO_SEARCH
            elif dir == Order.DO_SEARCH:
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = 30

            # default
            elif dir == Order.default_mode:
                drone.left_right_velocity = 0
                drone.for_back_velocity = 0
                drone.up_down_velocity = 0
                drone.yaw_velocity = 0

            # SEND VELOCITY VALUES TO TELLO
            if drone.send_rc_control:
                drone.send_rc_control(drone.left_right_velocity, drone.for_back_velocity, drone.up_down_velocity,
                                      drone.yaw_velocity)
            print(dir)
            log_str += str(dir) + '\n'

            stack = stackImages(0.9, ([recognition_area, result], [imgDil, imgContour]))
            cv2.imshow('Horizontal Stacking', stack)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                raise KeyboardInterrupt
        elif my_cam == Cam.WEBCAM:
            ret, frame = cap.read()
            frame_copy = frame.copy()

            img = cv2.resize(frame, (width, height))
            imgContour = img.copy()
            imgHsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)

            lower = lower_green
            upper = upper_green
            mask = cv2.inRange(imgHsv, lower, upper)
            result = cv2.bitwise_and(img, img, mask=mask)
            mask = cv2.cvtColor(mask, cv2.COLOR_GRAY2BGR)

            imgBlur = cv2.GaussianBlur(result, (7, 7), 1)
            imgGray = cv2.cvtColor(imgBlur, cv2.COLOR_BGR2GRAY)
            threshold1 = cv2.getTrackbarPos("Threshold1", "Parameters")
            threshold2 = cv2.getTrackbarPos("Threshold2", "Parameters")
            imgCanny = cv2.Canny(imgGray, threshold1, threshold2)
            kernel = np.ones((5, 5))
            imgDil = cv2.dilate(imgCanny, kernel, iterations=1)

            results = model(frame, size=640)
            xyxy = results.pandas().xyxy[0]
            xy_list = []
            for i in range(4):
                xy_list.append(int(xyxy[i].item()))

            getContours(imgDil, imgContour, detect_color, xyxy=xy_list, object_detect=object_detection)
            display(imgContour)

            print(f'dir : {dir}')

            stack = stackImages(0.9, ([img, result], [imgDil, imgContour]))

            cv2.imshow('Main', stack)

            if cv2.waitKey(1) & 0xFF == ord('q'):
                break

# 강제 종료에 대한 예외처리
except KeyboardInterrupt:
    print('KeyboardInterrupt 발생. 프로그램 종료합니다.')
    log_str += 'KeyboardInterrupt 발생. 프로그램 종료합니다.\n'
    log_str += ('time: ' + str(time.time() - start_time) + '\n')
    print(f'베터리 잔여량 : {drone.get_battery()}')
    log_str += f'베터리 잔여량 : {drone.get_battery()}\n'
    f.write(log_str)
    f.close()
    drone.land()
    cv2.destroyAllWindows()


from misterios_3 import get_page_3

def extraerEnlacesPagina(pagina):

	posicionEnlace = pagina.find('<a href=')

	if posicionEnlace == -1:
		return None, 0

	posicionComillasIniciales = pagina.find('"', posicionEnlace)
	posicionComillasFinales   = pagina.find('"', posicionComillasIniciales + 1)

	url = pagina[ posicionComillasIniciales + 1 : posicionComillasFinales ]

	return url, posicionComillasFinales

def imprimirEnlacesPagina(pagina):

	url = pagina

	while url:

		url, posicionComillasFinales = extraerEnlacesPagina(pagina)

		print(url)
		pagina = pagina[posicionComillasFinales:]

imprimirEnlacesPagina(get_page_3('http://xkcd.com/353/'))


# tools.py
# module for data processing
import pandas as pd
import numpy as np
import os
import matplotlib as plt
import string
from stop_words import get_stop_words
import nltk
import ssl
from sklearn.feature_extraction.text import CountVectorizer
import re

class tools:
    # Entrypoint for tools. Unique Identifier for a tools object is the
    # filepath to the dataset being observed by the tools.
    def __init__(self, filepath):
        self.filepath = None
        self.data = None
        self.data_shape = None
        self.stop_words = None

        self.all_comments = None
        self.non_toxic_comments = None
        self.toxic_comments = None

        self.comments_scores = None
        self.non_toxic_comments_scores = None
        self.toxic_comments_scores = None

        self.readData(filepath)
        self.processData()

    # Reads data from a given filepath into a pandas dataframe, then drops
    # entries from that dataframe that contain NaNs.
    def readData(self, filepath):
        df = pd.read_csv(filepath)
   #     df = df.dropna()
        self.data_shape = df.shape
        self.data = df
        self.filepath = filepath

    # Entrypoint for data processing.
    def processData(self):
        self.processTraining()
        self.buildStopWordDictionary()
        self.getGoodComments()
        self.getToxicComments()

    # Extract comment and toxicity score from all entries in the dataframe that
    # is being observed. Save this extracted data in self.comments_scores
    def processTraining(self):
        print("Processing training data...")
        cols = ['target', 'comment_text']
        comments_scores = self.data[cols]
        comments_scores['comment_text'] = self.processCommentList(comments_scores['comment_text'])
        self.comments_scores = comments_scores
        self.all_comments = comments_scores['comment_text']

    # Convert all comments from an iterable to lower case, remove punctuation
    def processCommentList(self, comments):
        print("Removing Punctuation...")
        punct = string.punctuation.replace('\'','')+"0123456789"
        outtab = "                                         "
        trantab = str.maketrans(punct, outtab)
        processed = []
        for comment in comments:
            comment = comment.lower().translate(trantab)
            comment = (comment.encode('ascii', 'ignore')).decode("utf-8")
            comment = (re.sub(r"[^a-zA-Z0-9]+", ' ', comment))
            processed.append(comment)
        return processed

    # Generate a stop word dictionary to be used by other methods. This uses
    # the english default stop words as well as all words from comments that
    # are identified as completely non-toxic
    def buildStopWordDictionary(self):
        print("Building Stop Word Dictionary...")
        stop_words = get_stop_words('english')
        stop_words.append('')
        for x in range(ord('b'), ord('z') + 1):
            stop_words.append(chr(x))

        self.stop_words = stop_words

    # Extract all comments that are completely non-toxic i.e. they have a
    # target score of 0.0
    def getGoodComments(self):
        print("About to get all good comments")
        good_comments = self.comments_scores[self.comments_scores['target'] == 0.0]
        self.non_toxic_comments = good_comments['comment_text']
        self.non_toxic_comments_scores = good_comments

    # Get every word in every good comment
    def getGoodWords(self):
        good_words = set()
        for comment in self.non_toxic_comments:
            for word in comment.split():
                good_words.add(word)
        return list(good_words)

    # Retrieve all comments with a toxicity score above 0.5 i.e. the most toxic
    # comments
    def getToxicComments(self):
        toxic_comments = good_comments = self.comments_scores[self.comments_scores['target'] >= 0.5]
        self.toxic_comments = toxic_comments['comment_text']
        self.toxic_comments_scores = toxic_comments

    def getAllComments(self):
        return self.all_comments

    def getMyToxicComments(self):
        return self.toxic_comments

    def getMyNonToxicComments(self):
        return self.non_toxic_comments
    def getProcessedComments(self):
        return self.processCommentList(self.all_comments)

    def getStops(self):
        return self.stop_words


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

from collections import OrderedDict
from datetime import datetime, timedelta

from fiqs.exceptions import MissingParameterException
from fiqs.fields import Field
from fiqs.models import Model

class Metric(object):
    def is_field_agg(self):
        return NotImplemented

    def is_doc_count(self):
        return False

    def is_computed(self):
        return False

class Count(Metric):
    def __init__(self, model_or_field):
        if isinstance(model_or_field, Field):
            self.model = model_or_field.model
        else:
            self.model = model_or_field

    def is_doc_count(self):
        return True

    def is_field_agg(self):
        return False

    def __str__(self):
        return 'doc_count'

    def order_by_key(self):
        return '_count'

class Aggregate(Metric):
    def reference(self):
        if hasattr(self, 'ref'):
            return self.ref
        return self.__class__.__name__.lower()

    def __init__(self, field, **kwargs):
        self.field = field
        self.params = kwargs

    def is_field_agg(self):
        return True

    def __str__(self):
        model = self.field.model.__name__.lower()
        op = self.__class__.__name__.lower()
        return '{}__{}__{}'.format(model, self.field.key, op)

    def agg_params(self):
        params = {
            'name': self.field.key,
            'field': self.field.get_storage_field(),
            'agg_type': self.reference(),
        }
        return params

    def choice_keys(self):
        return None

    def get_casted_value(self, v):
        return self.field.get_casted_value(v)

class Avg(Aggregate):
    def get_casted_value(self, v):
        """Average of an IntegerField does not have to be an integer"""
        return v

class Max(Aggregate):
    pass

class Min(Aggregate):
    pass

class Sum(Aggregate):
    pass

class Cardinality(Aggregate):
    pass

class Histogram(Aggregate):
    ref = 'histogram'

    def agg_params(self):
        params = super(Histogram, self).agg_params()
        params.update({
            'min_doc_count': 0,
        })

        if 'min' in self.params and 'max' not in self.params:
            raise MissingParameterException('cannot give min without max')

        if 'max' in self.params and 'min' not in self.params:
            raise MissingParameterException('cannot give max without min')

        if 'min' in self.params and 'max' in self.params:
            self.min = self.params.pop('min')
            self.max = self.params.pop('max')

            params['extended_bounds'] = {
                'min': self.min,
                'max': self.max,
            }

        params.update(self.params)

        if 'interval' not in params:
            raise MissingParameterException('missing interval parameter')

        self.interval = params['interval']

        return params

TIME_UNIT_CONVERSION = {
    'd': 'days',
    'day': 'days',
    'H': 'hours',
    'h': 'hours',
    'hour': 'hours',
    'm': 'minutes',
    'minute': 'minutes',
    's': 'seconds',
    'second': 'seconds',
}

def is_interval_standard(interval):
    for key, _ in TIME_UNIT_CONVERSION.items():
        if interval.endswith(key):
            return True

    return False

def is_interval_yearly(interval):
    # Naive approach
    return interval.endswith('y')

def is_interval_monthly(interval):
    # Naive approach
    return interval.endswith('M')

def is_interval_weekly(interval):
    # Naive approach
    return interval.endswith('w')

def is_interval_handled(interval):
    return is_interval_standard(interval) or\
           is_interval_weekly(interval) or\
           is_interval_monthly(interval) or\
           is_interval_yearly(interval)

def get_timedelta_from_interval(interval):
    # Some intervals are still missing: year, month, week
    for key, param in TIME_UNIT_CONVERSION.items():
        if interval.endswith(key):
            value = interval.rstrip(key)
            if not value:
                value = '1'

            return timedelta(**{
                param: int(value),
            })

    return None

def get_timedelta_from_timestring(timestring):
    if timestring.startswith('-'):
        minus = True
        timestring = timestring.lstrip('-')
    elif timestring.startswith('+'):
        minus = False
        timestring = timestring.lstrip('+')
    else:
        minus = False

    timedelta = get_timedelta_from_interval(timestring)
    if minus:
        return -timedelta
    else:
        return timedelta

def get_offset_date(d, timestring):
    timedelta = get_timedelta_from_timestring(timestring)

    return d + timedelta

def get_rounded_date_from_interval(d, interval):
    if is_interval_standard(interval):
        return get_rounded_date_from_timedelta(
            d, get_timedelta_from_interval(interval))

    if is_interval_weekly(interval):
        # Weekly intervals start on Monday
        rounded_date = d.replace(
            hour=0,
            minute=0,
            second=0,
            microsecond=0,
        )
        rounded_date -= timedelta(days=rounded_date.weekday())
        return rounded_date

    if is_interval_monthly(interval):
        return d.replace(
            day=1,
            hour=0,
            minute=0,
            second=0,
            microsecond=0,
        )

    if is_interval_yearly(interval):
        return d.replace(
            month=1,
            day=1,
            hour=0,
            minute=0,
            second=0,
            microsecond=0,
        )

    return d

def get_rounded_date_from_timedelta(d, delta):
    epoch = datetime(1970, 1, 1)
    nb_seconds = int((d - epoch).total_seconds())
    delta_seconds = int(delta.total_seconds())

    rounded_nb_seconds = (nb_seconds // delta_seconds) * delta_seconds
    return datetime.utcfromtimestamp(rounded_nb_seconds)

class DateHistogram(Histogram):
    ref = 'date_histogram'

    def choice_keys(self):
        if not hasattr(self, 'min') or not hasattr(self, 'max'):
            return None

        if not is_interval_handled(self.interval):
            return None

        start = get_rounded_date_from_interval(self.min, self.interval)
        agg_params = self.agg_params()
        if 'offset' in agg_params:
            start = get_offset_date(start, agg_params['offset'])

        end = self.max

        if is_interval_standard(self.interval):
            return self._choice_keys_standard(start, end, self.interval)

        elif is_interval_yearly(self.interval):
            return self._choice_keys_yearly(start, end, self.interval)

        elif is_interval_monthly(self.interval):
            return self._choice_keys_monthly(start, end, self.interval)

        elif is_interval_weekly(self.interval):
            return self._choice_keys_weekly(start, end, self.interval)

        else:
            return None

    def _choice_keys_standard(self, start, end, interval):
        delta = get_timedelta_from_interval(self.interval)

        choice_keys = []
        current = start
        while current <= end:
            choice_keys.append(current)
            current += delta

        return choice_keys

    def _choice_keys_yearly(self, start, end, interval):
        nb_years = int(interval.rstrip('y'))

        choice_keys = []
        current = start
        while current <= end:
            choice_keys.append(current)

            next_ = current
            for i in range(nb_years):
                # 370 days to be sure to change year
                next_ = next_ + timedelta(days=370)
                next_ = next_.replace(day=current.day, month=current.month)

            current = next_

        return choice_keys

    def _choice_keys_monthly(self, start, end, interval):
        nb_months = int(interval.rstrip('M'))

        choice_keys = []
        current = start
        while current <= end:
            choice_keys.append(current)

            next_ = current
            for i in range(nb_months):
                # 32 days to be sure to change month
                next_ = next_ + timedelta(days=32)
                next_ = next_.replace(day=current.day)

            current = next_

        return choice_keys

    def _choice_keys_weekly(self, start, end, interval):
        nb_weeks = int(interval.rstrip('w'))

        choice_keys = []
        current = start
        while current <= end:
            choice_keys.append(current)

            # I'm assuming weeks are always seven days long (?)
            current += timedelta(days=7 * nb_weeks)

        return choice_keys

class DateRange(Aggregate):
    ref = 'date_range'

    def agg_params(self):
        params = super(DateRange, self).agg_params()
        params.update(self.params)

        if 'ranges' not in params:
            raise MissingParameterException('missing ranges parameter')

        return params

    def _format_date(self, d):
        # We format the date like ES does
        return d.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3] + 'Z'

    def _format_date_range(self, date_range):
        start, end = date_range['from'], date_range['to']
        return '{}-{}'.format(self._format_date(start), self._format_date(end))

    def choice_keys(self):
        keys = []

        for date_range in self.params['ranges']:
            if 'key' in date_range:
                keys.append(date_range['key'])
            else:
                keys.append(self._format_date_range(date_range))

        return keys

    def get_casted_value(self, v):
        return v

class ReverseNested(Metric):
    def __init__(self, path_or_field_or_model,
                 *expressions, **named_expressions):

        # /!\ named_expressions may not be correctly ordered
        if isinstance(path_or_field_or_model, str):
            self.path = path_or_field_or_model or 'root'

        elif isinstance(path_or_field_or_model, Field):
            self.path = path_or_field_or_model.get_storage_field()

        elif issubclass(path_or_field_or_model, Model):
            self.path = 'root'

        self._expressions = OrderedDict()
        for exp in expressions:
            self._expressions[str(exp)] = exp
        self._expressions.update(named_expressions)

    def __str__(self):
        keys = '__'.join(self._expressions.keys())
        return 'reverse_nested_{}__{}'.format(self.path, keys)

    def reverse_agg_params(self):
        params = {
            'name': 'reverse_nested_{}'.format(self.path),
            'agg_type': 'reverse_nested',
        }

        if self.path != 'root':
            params['path'] = self.path

        return params

    def get_casted_value(self, v):
        return v

    def configure_aggregations(self, agg):
        reverse_agg_params = self.reverse_agg_params()
        reverse_nested_bucket = agg.bucket(
            **reverse_agg_params
        )

        for key, expression in self._expressions.items():
            if expression.is_field_agg():
                op = expression.__class__.__name__.lower()
                reverse_nested_bucket.metric(
                    key,
                    op,
                    field=expression.field.get_storage_field(),
                    **expression.params
                )

    @property
    def expressions(self):
        return {
            'reverse_nested_{}__{}'.format(self.path, key): expression
            for key, expression in self._expressions.items()
        }

    def create_empty_line(self):
        line = {}

        for key, expression in self.expressions.items():
            if expression.is_doc_count():
                continue
            line[key] = None

        line['reverse_nested_{}__doc_count'.format(self.path)] = 0

        return line

class Operation(Metric):
    def is_field_agg(self):
        return False

    def is_computed(self):
        return True

    def compute_one(self, row):
        raise NotImplementedError

    def compute(self, results, key=None):
        raise NotImplementedError

    def get_casted_value(self, v):
        return v

    @property
    def operands(self):
        return self._operands

    def __init__(self, *args):
        self._operands = args

def div_or_none(a, b, percentage=False):
    base = 100.0 if percentage else 1.0
    if b and a is not None:
        return base * a / b
    return None

def add_or_none(operands):
    if any([op is None for op in operands]):
        return None
    return sum(operands)

def sub_or_none(a, b):
    if a is not None and b is not None:
        return a - b
    return None

class Ratio(Operation):
    def __init__(self, dividend, divisor):
        super(Ratio, self).__init__(dividend, divisor)

        self.dividend = dividend
        self.divisor = divisor

    def __str__(self):
        return '{}__div__{}'.format(self.dividend, self.divisor)

    def compute_one(self, row):
        dividend = row[str(self.dividend)]

        if not self.divisor.is_computed():
            divisor = row[str(self.divisor)]
        else:
            divisor = self.divisor.compute_one(row)

        return div_or_none(dividend, divisor, percentage=True)

class Addition(Operation):
    def __str__(self):
        return '__add__'.join(['{}'.format(op) for op in self.operands])

    def compute_one(self, row):
        keys = [str(op) for op in self.operands]
        return add_or_none([row[key] for key in keys])

class Subtraction(Operation):
    def __init__(self, minuend, subtraend):
        super(Subtraction, self).__init__(minuend, subtraend)

        self.minuend = minuend
        self.subtraend = subtraend

    def __str__(self):
        return '{}__sub__{}'.format(self.minuend, self.subtraend)

    def compute_one(self, row):
        key_a = str(self.minuend)
        key_b = str(self.subtraend)

        return sub_or_none(row[key_a], row[key_b])


"""Performance metrics loggers

This module contains all data collectors that record events while simulations
are being executed and compute performance metrics.

Currently implemented data collectors allow users to measure cache hit ratio,
latency, path stretch and link load.

To create a new data collector, it is sufficient to create a new class
inheriting from the `DataCollector` class and override all required methods.
"""
import collections

from icarus.registry import register_data_collector
from icarus.tools import cdf
from icarus.util import Tree, inheritdoc

__all__ = [
    "DataCollector",
    "CollectorProxy",
    "CacheHitRatioCollector",
    "LinkLoadCollector",
    "LatencyCollector",
    "PathStretchCollector",
    "DummyCollector",
]

class DataCollector:
    """Object collecting notifications about simulation events and measuring
    relevant metrics.
    """

    def __init__(self, view, **params):
        """Constructor

        Parameters
        ----------
        view : NetworkView
            An instance of the network view
        params : keyworded parameters
            Collector parameters
        """
        self.view = view

    def start_session(self, timestamp, receiver, content):
        """Notifies the collector that a new network session started.

        A session refers to the retrieval of a content from a receiver, from
        the issuing of a content request to the delivery of the content.

        Parameters
        ----------
        timestamp : int
            The timestamp of the event
        receiver : any hashable type
            The receiver node requesting a content
        content : any hashable type
            The content identifier requested by the receiver
        """
        pass

    def cache_hit(self, node):
        """Reports that the requested content has been served by the cache at
        node *node*.

        Parameters
        ----------
        node : any hashable type
            The node whose cache served the content
        """
        pass

    def cache_miss(self, node):
        """Reports that the cache at node *node* has been looked up for
        requested content but there was a cache miss.

        Parameters
        ----------
        node : any hashable type
            The node whose cache served the content
        """
        pass

    def server_hit(self, node):
        """Reports that the requested content has been served by the server at
        node *node*.

        Parameters
        ----------
        node : any hashable type
            The server node which served the content
        """
        pass

    def request_hop(self, u, v, main_path=True):
        """Reports that a request has traversed the link *(u, v)*

        Parameters
        ----------
        u : any hashable type
            Origin node
        v : any hashable type
            Destination node
        main_path : bool, optional
            If *True*, indicates that link link is on the main path that will
            lead to hit a content. It is normally used to calculate latency
            correctly in multicast cases. Default value is *True*
        """
        pass

    def content_hop(self, u, v, main_path=True):
        """Reports that a content has traversed the link *(u, v)*

        Parameters
        ----------
        u : any hashable type
            Origin node
        v : any hashable type
            Destination node
        main_path : bool, optional
            If *True*, indicates that this link is being traversed by content
            that will be delivered to the receiver. This is needed to
            calculate latency correctly in multicast cases. Default value is
            *True*
        """
        pass

    def end_session(self, success=True):
        """Reports that the session is closed, i.e. the content has been
        successfully delivered to the receiver or a failure blocked the
        execution of the request

        Parameters
        ----------
        success : bool, optional
            *True* if the session was completed successfully, *False* otherwise
        """
        pass

    def results(self):
        """Returns the aggregated results measured by the collector.

        Returns
        -------
        results : dict
            Dictionary mapping metric with results.
        """
        pass

# Note: The implementation of CollectorProxy could be improved to avoid having
# to rewrite almost identical methods, for example by playing with __dict__
# attribute. However, it was implemented this way to make it more readable and
# easier to understand.
class CollectorProxy(DataCollector):
    """This class acts as a proxy for all concrete collectors towards the
    network controller.

    An instance of this class registers itself with the network controller and
    it receives notifications for all events. This class is responsible for
    dispatching events of interests to concrete collectors.
    """

    EVENTS = (
        "start_session",
        "end_session",
        "cache_hit",
        "cache_miss",
        "server_hit",
        "request_hop",
        "content_hop",
        "results",
    )

    def __init__(self, view, collectors):
        """Constructor

        Parameters
        ----------
        view : NetworkView
            An instance of the network view
        collector : list of DataCollector
            List of instances of DataCollector that will be notified of events
        """
        self.view = view
        self.collectors = {
            e: [c for c in collectors if e in type(c).__dict__] for e in self.EVENTS
        }

    @inheritdoc(DataCollector)
    def start_session(self, timestamp, receiver, content):
        for c in self.collectors["start_session"]:
            c.start_session(timestamp, receiver, content)

    @inheritdoc(DataCollector)
    def cache_hit(self, node):
        for c in self.collectors["cache_hit"]:
            c.cache_hit(node)

    @inheritdoc(DataCollector)
    def cache_miss(self, node):
        for c in self.collectors["cache_miss"]:
            c.cache_miss(node)

    @inheritdoc(DataCollector)
    def server_hit(self, node):
        for c in self.collectors["server_hit"]:
            c.server_hit(node)

    @inheritdoc(DataCollector)
    def request_hop(self, u, v, main_path=True):
        for c in self.collectors["request_hop"]:
            c.request_hop(u, v, main_path)

    @inheritdoc(DataCollector)
    def content_hop(self, u, v, main_path=True):
        for c in self.collectors["content_hop"]:
            c.content_hop(u, v, main_path)

    @inheritdoc(DataCollector)
    def end_session(self, success=True):
        for c in self.collectors["end_session"]:
            c.end_session(success)

    @inheritdoc(DataCollector)
    def results(self):
        return Tree(**{c.name: c.results() for c in self.collectors["results"]})

@register_data_collector("LINK_LOAD")
class LinkLoadCollector(DataCollector):
    """Data collector measuring the link load"""

    def __init__(self, view, req_size=150, content_size=1500):
        """Constructor

        Parameters
        ----------
        view : NetworkView
            The network view instance
        req_size : int
            Average size (in bytes) of a request
        content_size : int
            Average size (in byte) of a content
        """
        self.view = view
        self.req_count = collections.defaultdict(int)
        self.cont_count = collections.defaultdict(int)
        if req_size <= 0 or content_size <= 0:
            raise ValueError("req_size and content_size must be positive")
        self.req_size = req_size
        self.content_size = content_size
        self.t_start = -1
        self.t_end = 1

    @inheritdoc(DataCollector)
    def start_session(self, timestamp, receiver, content):
        if self.t_start < 0:
            self.t_start = timestamp
        self.t_end = timestamp

    @inheritdoc(DataCollector)
    def request_hop(self, u, v, main_path=True):
        self.req_count[(u, v)] += 1

    @inheritdoc(DataCollector)
    def content_hop(self, u, v, main_path=True):
        self.cont_count[(u, v)] += 1

    @inheritdoc(DataCollector)
    def results(self):
        duration = self.t_end - self.t_start
        used_links = set(self.req_count.keys()).union(set(self.cont_count.keys()))
        link_loads = {
            link: (
                self.req_size * self.req_count[link]
                + self.content_size * self.cont_count[link]
            )
            / duration
            for link in used_links
        }
        link_loads_int = {
            link: load
            for link, load in link_loads.items()
            if self.view.link_type(*link) == "internal"
        }
        link_loads_ext = {
            link: load
            for link, load in link_loads.items()
            if self.view.link_type(*link) == "external"
        }
        mean_load_int = (
            sum(link_loads_int.values()) / len(link_loads_int)
            if len(link_loads_int) > 0
            else 0
        )
        mean_load_ext = (
            sum(link_loads_ext.values()) / len(link_loads_ext)
            if len(link_loads_ext) > 0
            else 0
        )
        return Tree(
            {
                "MEAN_INTERNAL": mean_load_int,
                "MEAN_EXTERNAL": mean_load_ext,
                "PER_LINK_INTERNAL": link_loads_int,
                "PER_LINK_EXTERNAL": link_loads_ext,
            }
        )

@register_data_collector("LATENCY")
class LatencyCollector(DataCollector):
    """Data collector measuring latency, i.e. the delay taken to delivery a
    content.
    """

    def __init__(self, view, cdf=False):
        """Constructor

        Parameters
        ----------
        view : NetworkView
            The network view instance
        cdf : bool, optional
            If *True*, also collects a cdf of the latency
        """
        self.cdf = cdf
        self.view = view
        self.req_latency = 0.0
        self.sess_count = 0
        self.latency = 0.0
        if cdf:
            self.latency_data = collections.deque()

    @inheritdoc(DataCollector)
    def start_session(self, timestamp, receiver, content):
        self.sess_count += 1
        self.sess_latency = 0.0

    @inheritdoc(DataCollector)
    def request_hop(self, u, v, main_path=True):
        if main_path:
            self.sess_latency += self.view.link_delay(u, v)

    @inheritdoc(DataCollector)
    def content_hop(self, u, v, main_path=True):
        if main_path:
            self.sess_latency += self.view.link_delay(u, v)

    @inheritdoc(DataCollector)
    def end_session(self, success=True):
        if not success:
            return
        if self.cdf:
            self.latency_data.append(self.sess_latency)
        self.latency += self.sess_latency

    @inheritdoc(DataCollector)
    def results(self):
        results = Tree({"MEAN": self.latency / self.sess_count})
        if self.cdf:
            results["CDF"] = cdf(self.latency_data)
        return results

@register_data_collector("CACHE_HIT_RATIO")
class CacheHitRatioCollector(DataCollector):
    """Collector measuring the cache hit ratio, i.e. the portion of content
    requests served by a cache.
    """

    def __init__(self, view, off_path_hits=False, per_node=True, content_hits=False):
        """Constructor

        Parameters
        ----------
        view : NetworkView
            The NetworkView instance
        off_path_hits : bool, optional
            If *True* also records cache hits from caches not on located on the
            shortest path. This metric may be relevant only for some strategies
        content_hits : bool, optional
            If *True* also records cache hits per content instead of just
            globally
        """
        self.view = view
        self.off_path_hits = off_path_hits
        self.per_node = per_node
        self.cont_hits = content_hits
        self.sess_count = 0
        self.cache_hits = 0
        self.serv_hits = 0
        if off_path_hits:
            self.off_path_hit_count = 0
        if per_node:
            self.per_node_cache_hits = collections.defaultdict(int)
            self.per_node_server_hits = collections.defaultdict(int)
        if content_hits:
            self.curr_cont = None
            self.cont_cache_hits = collections.defaultdict(int)
            self.cont_serv_hits = collections.defaultdict(int)

    @inheritdoc(DataCollector)
    def start_session(self, timestamp, receiver, content):
        self.sess_count += 1
        if self.off_path_hits:
            source = self.view.content_source(content)
            self.curr_path = self.view.shortest_path(receiver, source)
        if self.cont_hits:
            self.curr_cont = content

    @inheritdoc(DataCollector)
    def cache_hit(self, node):
        self.cache_hits += 1
        if self.off_path_hits and node not in self.curr_path:
            self.off_path_hit_count += 1
        if self.cont_hits:
            self.cont_cache_hits[self.curr_cont] += 1
        if self.per_node:
            self.per_node_cache_hits[node] += 1

    @inheritdoc(DataCollector)
    def server_hit(self, node):
        self.serv_hits += 1
        if self.cont_hits:
            self.cont_serv_hits[self.curr_cont] += 1
        if self.per_node:
            self.per_node_server_hits[node] += 1

    @inheritdoc(DataCollector)
    def results(self):
        n_sess = self.cache_hits + self.serv_hits
        hit_ratio = self.cache_hits / n_sess
        results = Tree(**{"MEAN": hit_ratio})
        if self.off_path_hits:
            results["MEAN_OFF_PATH"] = self.off_path_hit_count / n_sess
            results["MEAN_ON_PATH"] = results["MEAN"] - results["MEAN_OFF_PATH"]
        if self.cont_hits:
            cont_set = set(
                list(self.cont_cache_hits.keys()) + list(self.cont_serv_hits.keys())
            )
            cont_hits = {
                i: (
                    self.cont_cache_hits[i]
                    / (self.cont_cache_hits[i] + self.cont_serv_hits[i])
                )
                for i in cont_set
            }
            results["PER_CONTENT"] = cont_hits
        if self.per_node:
            for v in self.per_node_cache_hits:
                self.per_node_cache_hits[v] /= n_sess
            for v in self.per_node_server_hits:
                self.per_node_server_hits[v] /= n_sess
            results["PER_NODE_CACHE_HIT_RATIO"] = self.per_node_cache_hits
            results["PER_NODE_SERVER_HIT_RATIO"] = self.per_node_server_hits
        return results

@register_data_collector("PATH_STRETCH")
class PathStretchCollector(DataCollector):
    """Collector measuring the path stretch, i.e. the ratio between the actual
    path length and the shortest path length.
    """

    def __init__(self, view, cdf=False):
        """Constructor

        Parameters
        ----------
        view : NetworkView
            The network view instance
        cdf : bool, optional
            If *True*, also collects a cdf of the path stretch
        """
        self.view = view
        self.cdf = cdf
        self.req_path_len = collections.defaultdict(int)
        self.cont_path_len = collections.defaultdict(int)
        self.sess_count = 0
        self.mean_req_stretch = 0.0
        self.mean_cont_stretch = 0.0
        self.mean_stretch = 0.0
        if self.cdf:
            self.req_stretch_data = collections.deque()
            self.cont_stretch_data = collections.deque()
            self.stretch_data = collections.deque()

    @inheritdoc(DataCollector)
    def start_session(self, timestamp, receiver, content):
        self.receiver = receiver
        self.source = self.view.content_source(content)
        self.req_path_len = 0
        self.cont_path_len = 0
        self.sess_count += 1

    @inheritdoc(DataCollector)
    def request_hop(self, u, v, main_path=True):
        self.req_path_len += 1

    @inheritdoc(DataCollector)
    def content_hop(self, u, v, main_path=True):
        self.cont_path_len += 1

    @inheritdoc(DataCollector)
    def end_session(self, success=True):
        if not success:
            return
        req_sp_len = len(self.view.shortest_path(self.receiver, self.source))
        cont_sp_len = len(self.view.shortest_path(self.source, self.receiver))
        req_stretch = self.req_path_len / req_sp_len
        cont_stretch = self.cont_path_len / cont_sp_len
        stretch = (self.req_path_len + self.cont_path_len) / (req_sp_len + cont_sp_len)
        self.mean_req_stretch += req_stretch
        self.mean_cont_stretch += cont_stretch
        self.mean_stretch += stretch
        if self.cdf:
            self.req_stretch_data.append(req_stretch)
            self.cont_stretch_data.append(cont_stretch)
            self.stretch_data.append(stretch)

    @inheritdoc(DataCollector)
    def results(self):
        results = Tree(
            {
                "MEAN": self.mean_stretch / self.sess_count,
                "MEAN_REQUEST": self.mean_req_stretch / self.sess_count,
                "MEAN_CONTENT": self.mean_cont_stretch / self.sess_count,
            }
        )
        if self.cdf:
            results["CDF"] = cdf(self.stretch_data)
            results["CDF_REQUEST"] = cdf(self.req_stretch_data)
            results["CDF_CONTENT"] = cdf(self.cont_stretch_data)
        return results

@register_data_collector("DUMMY")
class DummyCollector(DataCollector):
    """Dummy collector to be used for test cases only."""

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

        Parameters
        ----------
        view : NetworkView
            The network view instance
        output : stream
            Stream on which debug collector writes
        """
        self.view = view

    @inheritdoc(DataCollector)
    def start_session(self, timestamp, receiver, content):
        self.session = dict(
            timestamp=timestamp,
            receiver=receiver,
            content=content,
            cache_misses=[],
            request_hops=[],
            content_hops=[],
        )

    @inheritdoc(DataCollector)
    def cache_hit(self, node):
        self.session["serving_node"] = node

    @inheritdoc(DataCollector)
    def cache_miss(self, node):
        self.session["cache_misses"].append(node)

    @inheritdoc(DataCollector)
    def server_hit(self, node):
        self.session["serving_node"] = node

    @inheritdoc(DataCollector)
    def request_hop(self, u, v, main_path=True):
        self.session["request_hops"].append((u, v))

    @inheritdoc(DataCollector)
    def content_hop(self, u, v, main_path=True):
        self.session["content_hops"].append((u, v))

    @inheritdoc(DataCollector)
    def end_session(self, success=True):
        self.session["success"] = success

    def session_summary(self):
        """Return a summary of latest session

        Returns
        -------
        session : dict
            Summary of session
        """
        return self.session


# https://leetcode.com/problems/letter-combinations-of-a-phone-number/description/

class Solution:
    digit_char_mapping = {
        '2': 'abc',
        '3': 'def',
        '4': 'ghi',
        '5': 'jkl',
        '6': 'mno',
        '7': 'pqrs',
        '8': 'tuv',
        '9': 'wxyz'
    }
    def letterCombinations(self, digits):
        """
        :type digits: str
        :rtype: List[str]
        """
        if digits == '':
            return []
        num_digits = len(digits)
        self._combos = []
        self._backtracing(num_digits, digits, '', 0)
        return self._combos

    def _backtracing(self, num_digits, digits, letters, current_digit_idx):
        if current_digit_idx == len(digits):
            self._combos.append(letters)
        else:
            for char in self.digit_char_mapping[digits[current_digit_idx]]:
                self._backtracing(num_digits, digits, letters + char, current_digit_idx + 1)


import asyncio
import logging
from concurrent.futures.thread import ThreadPoolExecutor

from aiohttp import web

import caller_utils.caller_configuration as conf
from caller_utils.sms.twilio_sms import sms_twilio

logging.basicConfig(format=conf.get_log_formatter())

async def sms_sender_async(message, phone_number):
    """Trying to send a SMS text message through Twilio (maybe in future versions we can support more carriers)"""
    inner_loop = asyncio.get_running_loop()
    executor = ThreadPoolExecutor(max_workers=conf.get_num_of_cpus())
    logging.info(f"Sending the SMS message '{message}' to '{phone_number}'")
    return inner_loop.run_in_executor(
        executor,
        sms_twilio,
        message,
        phone_number,
        conf.get_twilio_account_sid(),
        conf.get_twilio_auth_token(),
        conf.get_twilio_sms_from(),
    )

async def send_the_sms(request):
    """Used to process the POST request"""

    try:
        message = request.rel_url.query["message"]
        phone = request.rel_url.query["phone"]
    except KeyError as err:
        message = None
        phone = None
        logging.exception(
            f"No 'message' or 'phone' parameter passed on: '{request.rel_url}'"
        )
        raise web.HTTPClientError(
            reason=conf.get_caller_sms_error(), body=None, text=None, content_type=None
        ) from err

    futures = await sms_sender_async(message, phone)

    try:
        await asyncio.ensure_future(futures)
        status_code = 200
    except Exception as err:
        status_code = 500
        logging.exception(f"Unable to send the SMS: '{err}'")

    return web.json_response({"status": status_code})

async def init_app():
    """Start the Application Web Server."""
    app = web.Application()

    # And... here our routes
    app.router.add_route("POST", f"/{conf.get_caller_sms_app_route()}", send_the_sms)
    return app

if __name__ == "__main__":
    loop = asyncio.new_event_loop()
    app = loop.run_until_complete(init_app())
    web.run_app(app, port=conf.get_caller_sms_port())


import queue
import math

class Solution:
    def numDistinctIslands(self, grid: List[List[int]]) -> int:
        n, m = len(grid), len(grid[0])
        if n == 0 or m == 0:
            return 0

        res_str = []
        for i in range(n):
            for j in range(m):
                if grid[i][j] == 1:
                    res_str.append(self.record_is(i, j, grid, n, m))
        s = set()
        for island in res_str:
            s.add(island)
        return len(s)

    def record_is(self, y, x, grid, n, m):
        q = queue.Queue()
        q.put([y, x])
        x_ = [0, 0, -1, 1]
        y_ = [-1, 1, 0, 0]
        x_min, x_max, y_min, y_max = x, x, y, y
        while q.qsize():
            cur = q.get()
            grid[cur[0]][cur[1]] = 3
            x_min = min(x_min, cur[1])
            x_max = max(x_max, cur[1])
            y_min = min(y_min, cur[0])
            y_max = max(y_max, cur[0])
            for i in range(4):
                if self.is_valid(cur[0]+y_[i], cur[1]+x_[i], n, m, grid):
                    q.put([cur[0]+y_[i], cur[1]+x_[i]])
        str_is = ""
        for i in range(y_min, y_max+1):
            for j in range(x_min, x_max+1):
                if grid[i][j] == 3:
                    str_is += str(grid[i][j])
                else:
                    str_is += "0"
            str_is += "/"

        return str_is

    def is_valid(self, y, x, n, m, grid):
        return (y >= 0 and y < n) and (x >= 0 and x < m) and (grid[y][x] == 1)

class Solution:
    def numDistinctIslands(self, grid: List[List[int]]) -> int:
        if not grid or not grid[0]:
            return 0
        m, n = len(grid), len(grid[0])
        visited = set()

        def dfs(i, j, direction=""):
            if 0 <= i < m and 0 <= j < n and grid[i][j] and (i, j) not in visited:
                visited.add((i, j))
                self.shape += direction
                # print(self.shape)
                dfs(i+1, j, "0")
                dfs(i-1, j, "1")
                dfs(i, j+1, "2")
                dfs(i, j-1, "3")
                self.shape += "/"  # when we are doing backtrack we must record it

        shapes = set()
        for i in range(m):
            for j in range(n):
                if grid[i][j] and (i, j) not in visited:
                    self.shape = ""
                    dfs(i, j)
                    shapes.add(self.shape)

        return len(shapes)


import sys
num_map=[0]*20000000
n=int(input())
n_num=[int(x) for x in input().split()]
for a in n_num:
	if a<0:
		a=(a*-1)+10000000
	num_map[a]+=1
m=int(input())
m_num=[int(x) for x in input().split()]
for a in m_num:
	if a<0:
		a=(a*-1)+10000000
	print(1 if num_map[a]==1 else 0,end=" ")


# This file contains functions that provide angle operations

from shapely.geometry import Point
import numpy as np
from math import pi

def angle_3_pts(point1, point2, point3):
    angle1 = angle_2_pts(point2, point1)
    angle2 = angle_2_pts(point2, point3)

    return angle2 - angle1

def angle_2_pts(point1, point2):
    x = point2.x - point1.x
    y = point2.y - point1.y
    return np.arctan2(y, x)

# converts an angle between 0 and 2Pi or in [-Pi, Pi] to the [0, Pi] interval
def angle_to_zero_pi(angle):
    if angle > pi:
        return angle - pi
    if angle < 0:
        return - angle
    return angle

def angle_between_2lines(line1, line2):
    """
    Return the angle between two lines that crosses at their end or start point. Takes shapely LineStrings as input.
    """
    # retrieve coordinates of lines nodes, number of node in lines and start and end node
    coords1 = line1.coords
    l1 = len(coords1)
    start1, end1 = coords1[0], coords1[-1]
    coords2 = line2.coords
    l2 = len(coords2)
    start2, end2 = coords2[0], coords2[-1]

    sgeom1 = sgeom2 = None
    current = None

    if start1 == start2:
        current = start1
        sgeom1 = True
        sgeom2 = True
    if start1 == end2:
        current = start1
        sgeom1 = True
        sgeom2 = False
    if end1 == end2:
        current = end1
        sgeom1 = False
        sgeom2 = False
    if end1 == start2:
        current = end1
        sgeom1 = False
        sgeom2 = True

    # In this case, lines are not crossing
    if current is None:
        return None

    previous = following = None

    if l1 > 2:
        if sgeom1:
            previous = coords1[2]
        else:
            previous = coords1[-3]
    else:
        if sgeom1:
            previous = coords1[1]
        else:
            previous = coords1[-2]

    if l2 > 2:
        if sgeom2:
            following = coords2[2]
        else:
            following = coords2[-3]
    else:
        if sgeom2:
            following = coords2[1]
        else:
            following = coords2[-2]

    angle = angle_3_pts(Point(previous), Point(current), Point(following))

    return angle


import sys
sys.path.append(r'../')
import numpy as np
from functions import *

algorithm = 'autoencoder'

for worm_num in range(5):
	### Load Data (and excluding behavioural neurons)
	b_neurons = [
		'AVAR',
		'AVAL',
		'SMDVR',
		'SMDVL',
		'SMDDR',
		'SMDDL',
		'RIBR',
		'RIBL',]
	data = Database(data_set_no=worm_num)
	data.exclude_neurons(b_neurons)
	X = data.neuron_traces.T
	B = data.states
	state_names = ['Dorsal turn', 'Forward', 'No state', 'Reverse-1', 'Reverse-2', 'Sustained reversal', 'Slowing', 'Ventral turn']

	### Preprocess and prepare data for BundLe Net
	time, X = preprocess_data(X, data.fps)
	X_, B_ = prep_data(X, B, win=1)

	## Train test split
	X_train, X_test, B_train_1, B_test_1 = timeseries_train_test_split(X_, B_)
	X0_tr = X_train[:,0,:,:]
	X1_tr = X_train[:,1,:,:]
	X0_tst = X_test[:,0,:,:]
	X1_tst = X_test[:,1,:,:]

	### Autoencoder architecture
	class Autoencoder(Model):
		def __init__(self, latent_dim):
			super(Autoencoder, self).__init__()
			self.latent_dim = latent_dim
			self.encoder = tf.keras.Sequential([
					layers.Flatten(),
					layers.Dense(50, activation='relu'),
					layers.Dense(30, activation='relu'),
					layers.Dense(25, activation='relu'),
					layers.Dense(10, activation='relu'),
					layers.Dense(latent_dim, activation='linear'),
			])
			self.decoder = tf.keras.Sequential([
					layers.Dense(latent_dim, activation='relu'),
					layers.Dense(10, activation='relu'),
					layers.Dense(25, activation='relu'),
					layers.Dense(30, activation='relu'),
					layers.Dense(50, activation='relu'),
					layers.Dense(X0_tr.shape[-1]*X0_tr.shape[-2], activation='linear'),
					layers.Reshape(X0_tr.shape[1:])
			])
		def call(self, x):
			encoded = self.encoder(x)
			decoded = self.decoder(encoded)
			return decoded

	### Deploy Autoencoder
	autoencoder = Autoencoder(latent_dim = 3)
	opt = tf.keras.optimizers.legacy.Adam(learning_rate=0.001)
	autoencoder.compile(optimizer=opt, loss='mse', metrics=['mse'])

	history = autoencoder.fit(X0_tr,
												X0_tr,
												epochs=150,
												batch_size=100,
												validation_data=(X0_tst, X0_tst),
												verbose=False
												)

	X0_pred = autoencoder(X0_tst).numpy()
	modelmse_tst = mean_squared_error(flat_partial(X0_tst), flat_partial(X0_pred))
	print('Test set mse:', modelmse_tst.round(8))

	### Projecting into latent space
	Y0_tr = autoencoder.encoder(X0_tr).numpy()
	Y1_tr = autoencoder.encoder(X1_tr).numpy()

	Y0_tst = autoencoder.encoder(X0_tst).numpy()
	Y1_tst = autoencoder.encoder(X1_tst).numpy()

	# Save the weights
	# model.save_weights('data/generated/BunDLeNet_model_worm_' + str(worm_num))
	np.savetxt('data/generated/saved_Y/Y0_tr__'+algorithm+'_worm_'+ str(worm_num), Y0_tr)
	np.savetxt('data/generated/saved_Y/Y1_tr__'+algorithm+'_worm_'+ str(worm_num), Y1_tr)
	np.savetxt('data/generated/saved_Y/Y0_tst__'+algorithm+'_worm_'+ str(worm_num), Y0_tst)
	np.savetxt('data/generated/saved_Y/Y1_tst__'+algorithm+'_worm_'+ str(worm_num), Y1_tst)
	np.savetxt('data/generated/saved_Y/B_train_1__'+algorithm+'_worm_'+ str(worm_num), B_train_1)
	np.savetxt('data/generated/saved_Y/B_test_1__'+algorithm+'_worm_'+ str(worm_num), B_test_1)


import numpy as np
import matplotlib.pyplot as plt
import magpylib as magpy

numSegments = 20

def createCoilMultiplex(idx,dxs,dys,deltaX,deltaY,topOrBot):
    # create a Magpylib collection of Loop Sources that form a coil
 #   coil = magpy.Collection()
    trajectory = []
    radius = 5
    firstPoint = 1
    for i in range(numSegments):        #loop over the segments getting created
    #    thisSegment = [radius*np.cos(2*np.pi*i/numSegments),radius*np.sin(2*np.pi*i/numSegments),z]
    #    trajectory.append(thisSegment)
        if (i == idx):
            x = 0.75*radius*np.sin(2*np.pi*i/numSegments) + dxs[i] + deltaX
            y = 0.75*radius*np.cos(2*np.pi*i/numSegments) + dys[i]  + deltaY
            if x > radius: x = radius
            if x < -radius: x = -radius
            if y > radius: y = radius
            if y < -radius: y = -radius
        else:
            x = 0.75*radius*np.sin(2*np.pi*i/numSegments) + dxs[i]
            y = 0.75*radius*np.cos(2*np.pi*i/numSegments) + dys[i]
            if x > radius: x = radius
            if x < -radius: x = -radius
            if y > radius: y = radius
            if y < -radius: y = -radius
        if topOrBot:
            z = -np.sqrt(radius*radius-x*x)
        else:
            z = np.sqrt(radius*radius-x*x)
        thisSegment = [x,y,z]
        trajectory.append(thisSegment)
        if firstPoint:
            fp = thisSegment
            firstPoint = 0
    trajectory.append(fp)
    coil = magpy.current.Line(1,trajectory)
    return coil

def showPlots(thisCoil,grid,amp,Bin):
    # create figure using Matplotlib
    fig = plt.figure(figsize=(8,4))
    ax1 = fig.add_subplot(121, projection='3d')  # 3D-axis
    ax2 = fig.add_subplot(122,)                  # 2D-axis
    # display the coil on ax1
    thisCoil.show(canvas=ax1)
    # display field in figure with matplotlib
    ax2.streamplot(grid[:,:,0], grid[:,:,2], Bin[:,:,0], Bin[:,:,2],
        density=2, color=np.log(amp), linewidth=1, cmap='autumn')

    plt.tight_layout()
    plt.show()

def findOffField(tB):
    totalOff = 0
    for i in range(tB.shape[0]):
        for j in range(tB.shape[1]):
            totalOff = totalOff + tB[i,j,0]*tB[i,j,0]
    return totalOff

if __name__ == "__main__":
    dxs = [0]*numSegments
#    dxs[0:5] = [3,3,3,3,3]
    dys = [0]*numSegments
    firstStep = 1
    finalOff = 999
    nOpts = 10
    delta = 0.2
    for opt in range(nOpts):            #loop over optimization steps
        for i in range(numSegments):    #loop over which segment gets moved
            finalOff = 999
            coil1 = createCoilMultiplex(i,dxs,dys,0,0,0) #create the two coils
            coil2 = createCoilMultiplex(i,dxs,dys,0,0,1)
            coll = magpy.Collection(coil1,coil2, style_label='coll')
            ts = np.linspace(-3,3,30)
            grid = np.array([[(x,0,z) for x in ts] for z in ts])
            tB = magpy.getB(coll,grid)
            tAmp =  np.linalg.norm(tB, axis=2)
            totalOff1 = findOffField(tB)
            if firstStep:
                firstStep = 0
                startOff = totalOff1
            if totalOff1 < finalOff :
                finalOff = totalOff1
                thisDelta = [0,0]
                finalColl = coll
                finalTAmp = tAmp
                finalTb = tB

            coil1 = createCoilMultiplex(i,dxs,dys,delta,0,0)
            coil2 = createCoilMultiplex(i,dxs,dys,delta,0,1)
            coll = magpy.Collection(coil1,coil2, style_label='coll')
            tB = magpy.getB(coll,grid)
            tAmp =  np.linalg.norm(tB, axis=2)
            totalOff2 = findOffField(tB)
            if totalOff2 < finalOff :
                finalOff = totalOff2
                thisDelta = [delta,0]
                finalColl = coll
                finalTAmp = tAmp
                finalTb = tB
            coil1 = createCoilMultiplex(i,dxs,dys,0,delta,0)
            coil2 = createCoilMultiplex(i,dxs,dys,0,delta,1)
            coll = magpy.Collection(coil1,coil2, style_label='coll')
            tB = magpy.getB(coll,grid)
            tAmp =  np.linalg.norm(tB, axis=2)
            totalOff3 = findOffField(tB)
            if totalOff3 < finalOff :
                finalOff = totalOff3
                thisDelta =  [0,delta]
                finalColl = coll
                finalTAmp = tAmp
                finalTb = tB

            coil1 = createCoilMultiplex(i,dxs,dys,-delta,0,0)
            coil2 = createCoilMultiplex(i,dxs,dys,-delta,0,1)
            coll = magpy.Collection(coil1,coil2, style_label='coll')
            tB = magpy.getB(coll,grid)
            tAmp =  np.linalg.norm(tB, axis=2)
            totalOff4 = findOffField(tB)
            if totalOff4 < finalOff :
                finalOff = totalOff4
                thisDelta =  [-delta,0]
                finalColl = coll
                finalTAmp = tAmp
                finalTb = tB

            coil1 = createCoilMultiplex(i,dxs,dys,0,-delta,0)
            coil2 = createCoilMultiplex(i,dxs,dys,0,-delta,1)
            coll = magpy.Collection(coil1,coil2, style_label='coll')
            tB = magpy.getB(coll,grid)
            tAmp =  np.linalg.norm(tB, axis=2)
            totalOff5 = findOffField(tB)
            if totalOff5 < finalOff :
                finalOff = totalOff5
                thisDelta =  [0,-delta]
                finalColl = coll
                finalTAmp = tAmp
                finalTb = tB

            dxs[i] = dxs[i] + thisDelta[0]
            dys[i] = dys[i] + thisDelta[1]
#            print(dxs,dys)

 #       print(finalOff)
    print(startOff)
    print(finalOff)

    showPlots(finalColl,grid,finalTAmp,finalTb)


import logging

def create_log(level: str):

    log_level = logging.INFO

    if level == "INFO":
        log_level = logging.INFO
    elif level == "DEBUG":
        log_level = logging.DEBUG

    log_formatter = logging.Formatter(
        fmt="%(asctime)s | %(levelname)7s | %(filename)-10s:%(lineno)-4s: %(message)s",
        datefmt="%Y-%m-%d %H:%M:%S",
    )

    # create logger
    logger = logging.getLogger()
    logger.setLevel(log_level)

    # create console handler
    consoleHandler = logging.StreamHandler()
    consoleHandler.setLevel(log_level)
    consoleHandler.setFormatter(log_formatter)

    # Add console handler to logger
    logger.addHandler(consoleHandler)

    logger.info("================> Running sensor <================")

    return logger


# 1912 연속합
import sys
input = sys.stdin.readline

n = int(input())
lst = list(map(int, input().split()))
dp = [0] * n
dp[0] = lst[0]
for i in range(1, n):
    dp[i] = max(dp[i-1] + lst[i], lst[i])
print(max(dp))
""" 처음 문제를 풀 떄, 막연히 max(이전최대 + 현재값, 현재값)으로 떠올림
문제 : 이전최대와 현재 사이 음수가 있을 경우???
but 이전최대 + 현재값이 아닌, 이전까지 sum이라고 생각하면 문제는 쉽게풀림
매 iteration마다 현재값과 이전까지 sum+현재값 중 큰것으로 결정되기 때문
즉, dp[i-1]이 양수면 더하고 음수면 안 더함.
마지막에 max(dp)로 출력하는 것을 꼭 확인한다. 어차피 이후 값에 음수가 섞여서 더 작아진다 해도 상관없음
"""

"""
# 내가 시도했던 답, 답은 나오지만 시간초과 매번 for문이 너무 많음.
# 점화식이 잘 정리되지 않음
def chainSum(arr: list):
    dp = [-1000] * n
    dp[0] = arr[0]
    if max(arr) < 0:
        return max(arr)
    for i in range(1, len(arr)):
        if arr[i] <= 0:
            dp[i] = dp[i-1]
            continue

        # 이전 최대값부터 현재 인덱스까지 합
        new_max = dp[i-1] + arr[i]
        for j in range(i-1, 0, -1):
            if dp[j] != dp[j-1]:
                break
            new_max += arr[j]

        # 가장 최근 음수 다음부터 현재 인덱스까지 합
        cur = 0
        for j in range(i, 0, -1):
            if arr[j] < 0:
                break
            cur += arr[j]

        dp[i] = max(new_max, cur)
    return max(dp)

n = int(input())
arr = list(map(int, input().split()))
print(chainSum(arr))
"""


from characters import characters
# print(len(characters))

#How many character names start with "A"?
def a_Names(characters):
        aNames = 0
        for names in characters:
                if names["name"][0] == "A":
                        aNames += 1
        print(aNames)
a_Names(characters)

#How many character names start with "Z"?
def z_Names(characters):
        zNames = 0
        for names in characters:
                if names["name"][0] == "Z":
                        zNames += 1
        print(zNames)
z_Names(characters)

# How many characters are dead?
def dead_characters():
        dead_characters = 0
        for i in characters:
                if i["died"] != '':
                        dead_characters += 1
        print(dead_characters)
dead_characters()

# How many characters are valyrian
def valyrian_characters():
        v_characters = 0
        for i in characters:
                if i["culture"] == "Valyrian":
                        v_characters += 1
        print(v_characters)
valyrian_characters()

#What actor plays "Hot Pie"
for i in characters:
        if i["name"] == "Hot Pie":
                actorName = i['playedBy']
print(actorName)

#How many characters are not in the show?
not_in_show = 0
for i in characters:
        if i["tvSeries"] == ['']:
                not_in_show += 1

print(not_in_show)
# Who has the most titles?

for i in characters:
        if i["titles"] != ['']:
                print("Name: %s Titles: %s" % (i["name"], len(i["titles"]))) # I dunno it works
                break

# Produce a list characters with the last name "Targaryn"
new_list = []
def targaryen_family():
        for i in characters:
                if i["name"] != '':
                        if "Targaryen" in i["name"]:
                                new_list = i["name"]
                                print(new_list)

print(targaryen_family())

# print(max_titles)
# print(maxcount)
# jon_snow = {"url":"https://anapioficeandfire.com/api/characters/583","name":"Jon Snow","gender":"Male","culture":"Northmen","born":"In 283 AC","died":"","titles":["Lord Commander of the Night's Watch"],"aliases":["Lord Snow","Ned Stark's Bastard","The Snow of Winterfell","The Crow-Come-Over","The 998th Lord Commander of the Night's Watch","The Bastard of Winterfell","The Black Bastard of the Wall","Lord Crow"],"father":"","mother":"","spouse":"","allegiances":["https://anapioficeandfire.com/api/houses/362"],"books":["https://anapioficeandfire.com/api/books/5"],"povBooks":["https://anapioficeandfire.com/api/books/1","https://anapioficeandfire.com/api/books/2","https://anapioficeandfire.com/api/books/3","https://anapioficeandfire.com/api/books/8"],"tvSeries":["Season 1","Season 2","Season 3","Season 4","Season 5","Season 6"],"playedBy":["Kit Harington"]}

# # print out the key names individually
# # for k in jon_snow:
# #     print(k)

# # print out just the values
# # for k in jon_snow:
# #     print(jon_snow[k])

# # print both the key and the value
# # for k in jon_snow:
# #    print("%s: %s" % (k, jon_snow[k]))


# coding: utf-8
from ptrlib.util.encoding import *
from ptrlib.console.color import Color
from abc import ABCMeta, abstractmethod
import re
import sys
import threading
import time
from logging import getLogger

logger = getLogger(__name__)

class Tube(metaclass=ABCMeta):
    def __init__(self):
        self.buf = b''

    @abstractmethod
    def _settimeout(self, timeout):
        pass

    @abstractmethod
    def _recv(self, size, timeout):
        """Receive raw data

        Receive raw data of maximum `size` bytes length through the socket.

        Args:
            size    (int): The data size to receive
            timeout (int): Timeout (in second)

        Returns:
            bytes: The received data
        """
        pass

    def unget(self, data):
        if isinstance(data, str):
            data = str2bytes(data)
        self.buf = data + self.buf

    def recv(self, size=4096, timeout=None):
        """Receive raw data with buffering

        Receive raw data of maximum `size` bytes length through the socket.

        Args:
            size    (int): The data size to receive
            timeout (int): Timeout (in second)

        Returns:
            bytes: The received data
        """
        self._settimeout(timeout)
        if not self.buf:
            data = self._recv(size, timeout=-1)
            if data is not None:
                self.buf += data

        data, self.buf = self.buf[:size], self.buf[size:]
        return data

    def recvonce(self, size, timeout=None):
        """Receive raw data with buffering

        Receive raw data of size `size` bytes length through the socket.

        Args:
            size    (int): The data size to receive
            timeout (int): Timeout (in second)

        Returns:
            bytes: The received data
        """
        self._settimeout(timeout)
        data = b''
        while len(data) < size:
            data += self.recv(size - len(data))

        if len(data) > size:
            self.unget(data[size:])
        return data[:size]

    def recvuntil(self, delim, size=4096, timeout=None):
        """Receive raw data until `delim` comes

        Args:
            size (int)   : The data size to receive at once
            delim (bytes): The delimiter bytes
            timeout (int): Timeout (in second)

        Returns:
            bytes: The received data
        """
        if isinstance(delim, str):
            delim = str2bytes(delim)
        data = b''

        self._settimeout(timeout)
        while data.find(delim) == -1:
            data += self.recv(size, -1)

        pos = data.find(delim) + len(delim)
        self.unget(data[pos:])
        return data[:pos]

    def recvline(self, size=4096, timeout=None, drop=True):
        line = self.recvuntil(b'\n', size, timeout)
        if drop:
            return line.rstrip()
        return line

    def recvlineafter(self, delim, size=4096, timeout=None, drop=True):
        self.recvuntil(delim, size, timeout)
        return self.recvline(size, timeout, drop)

    def recvregex(self, regex, size=4096, discard=True, timeout=None):
        """Receive until a pattern comes

        Receive data until a specified regex pattern matches.

        Args:
            regex (bytes): Regex
            size (int)   : Size to read at once
            timeout (int): Timeout (in second)

        Returns:
            tuple: If the given regex has multiple patterns to find,
                   it returns all matches. Otherwise, it returns the
                   match string. If discard is false, it also returns
                   all data received so far along with the matches.
        """
        if not isinstance(regex, bytes):
            regex = str2bytes(regex)

        p = re.compile(regex)
        data = b''

        self._settimeout(timeout)
        while p.search(data) is None:
            data += self.recv(size, timeout=-1)

        r = p.search(data)
        pos = r.end()
        self.unget(data[pos:])

        group = r.group()
        groups = r.groups()
        if groups:
            if discard:
                return groups
            else:
                return groups, data[:pos]
        else:
            if discard:
                return group
            else:
                return group, data[:pos]

    @abstractmethod
    def send(self, data):
        pass

    @abstractmethod
    def _socket(self):
        pass

    def sendline(self, data, timeout=None):
        """Send a line

        Send a line of data.

        Args:
            data (bytes) : Data to send
            timeout (int): Timeout (in second)
        """
        if isinstance(data, str):
            data = str2bytes(data)
        self.send(data + b'\n')

    def sendafter(self, delim, data, timeout=None):
        """Send raw data after a deliminater

        Send raw data after `delim` is received.

        Args:
            delim (bytes): The deliminater
            data (bytes) : Data to send
            timeout (int): Timeout (in second)

        Returns:
            bytes: Received bytes before `delim` comes.
        """
        if isinstance(data, str):
            data = str2bytes(data)
        recv_data = self.recvuntil(delim, timeout=timeout)
        self.send(data)
        return recv_data

    def sendlineafter(self, delim, data, timeout=None):
        """Send raw data after a deliminater

        Send raw data with newline after `delim` is received.

        Args:
            delim (bytes): The deliminater
            data (bytes) : Data to send
            timeout (int): Timeout (in second)

        Returns:
            bytes: Received bytes before `delim` comes.
        """
        if isinstance(data, str):
            data = str2bytes(data)
        recv_data = self.recvuntil(delim, timeout=timeout)
        self.sendline(data, timeout=timeout)
        return recv_data

    def sh(self, timeout=None):
        """Alias for interactive
        """
        self.interactive(timeout)

    def interactive(self, timeout=None):
        """Interactive mode
        """
        def thread_recv():
            while not flag.isSet():
                try:
                    data = self.recv(size=4096, timeout=0.1)
                    if data is not None:
                        sys.stdout.write(bytes2str(data))
                        sys.stdout.flush()
                except TimeoutError:
                    pass
                except EOFError:
                    logger.error("Receiver EOF")
                    break
                except ConnectionAbortedError:
                    logger.error("Receiver EOF")
                    break
                time.sleep(0.1)

        flag = threading.Event()
        th = threading.Thread(target=thread_recv)
        th.setDaemon(True)
        th.start()

        try:
            while not flag.isSet():
                data = input("{bold}{blue}[ptrlib]${end} ".format(
                    bold=Color.BOLD, blue=Color.BLUE, end=Color.END
                ))
                if self._socket() is None:
                    logger.error("Connection already closed")
                    break
                if data is None:
                    flag.set()
                else:
                    try:
                        self.sendline(data)
                    except ConnectionAbortedError:
                        logger.error("Sender EOF")
                        break
                time.sleep(0.1)
        except KeyboardInterrupt:
            flag.set()

        while th.is_alive():
            th.join(timeout = 0.1)
            time.sleep(0.1)

    def __enter__(self):
        return self

    def __exit__(self, e_type, e_value, traceback):
        self.close()

    @abstractmethod
    def close(self):
        pass

    @abstractmethod
    def shutdown(self, target):
        pass


#!/usr/env/bin python
import argparse

def get_arguments():
    parser = argparse.ArgumentParser()
    parser.add_argument("-i", "--interface", dest="interface", help="Interface to sniff on")
    args = parser.parse_args()
    if not args.interface:
        parser.error("No interface provided, use --help")
    # elif not args.target:
    #      parser.error("No target IP provided, use --help")
    return args

args = get_arguments()
print(args.interface)