from pathlib import Path
from unittest.mock import mock_open, patch

import pytest

from src.parse import parse_mot15_gt, parse_mot15_ini

@pytest.fixture
def seq_info():
    return """[Sequence]
name=Venice-1
imDir=img1
frameRate=30
seqLength=450
imWidth=1920
imHeight=1080
imExt=.jpg
"""

@pytest.fixture
def mot15_gt():
    return """1,1,415,449,129,269,1,-1,-1,-1
3,1,581,443,97,269,1,-1,-1,-1
3,2,1347,415,153,377,1,-1,-1,-1
3,3,1429,423,161,339,1,-1,-1,-1
6,1,413,449,131,269,1,-1,-1,-1
6,2,579,443,97,269,1,-1,-1,-1
6,3,1353,413,155,379,1,-1,-1,-1
6,4,1437,421,157,341,1,-1,-1,-1
"""

def test_parse_mot_15_ini(seq_info):
    """Check if MOT15 seqinfo.ini is parsed properly."""
    test_path = Path("dummy/path")
    with patch("builtins.open", mock_open(read_data=seq_info)):
        parsed = parse_mot15_ini(test_path)

    assert parsed["name"] == "Venice-1"
    assert parsed["directory"] == test_path.parent / "img1"
    assert parsed["framerate"] == 30.0
    assert parsed["length"] == 450
    assert parsed["width"] == 1920
    assert parsed["height"] == 1080
    assert parsed["extension"] == ".jpg"

@pytest.mark.parametrize("length", [7, 9, 10])
def test_parse_mot15_gt(length, mot15_gt):
    """Test if MOT15 ground truth is parsed properly."""
    with patch("builtins.open", mock_open(read_data=mot15_gt)):
        parsed = list(parse_mot15_gt(Path("dummy/path"), length))

    assert len(parsed) == length

    assert parsed[0]["bboxes"] == [[415, 449, 129, 269]]
    assert parsed[0]["classes"] == [0]
    assert parsed[0]["ids"] == [0]

    assert parsed[1]["bboxes"] == parsed[1]["classes"] == parsed[1]["ids"] == []

    assert parsed[2]["bboxes"] == [
        [581, 443, 97, 269],
        [1347, 415, 153, 377],
        [1429, 423, 161, 339],
    ]
    assert parsed[2]["classes"] == [0, 0, 0]
    assert parsed[2]["ids"] == [0, 1, 2]

    assert parsed[3]["bboxes"] == parsed[3]["classes"] == parsed[3]["ids"] == []
    assert parsed[4]["bboxes"] == parsed[4]["classes"] == parsed[4]["ids"] == []

    assert parsed[5]["bboxes"] == [
        [413, 449, 131, 269],
        [579, 443, 97, 269],
        [1353, 413, 155, 379],
        [1437, 421, 157, 341],
    ]
    assert parsed[5]["classes"] == [0, 0, 0, 0]
    assert parsed[5]["ids"] == [0, 1, 2, 3]

    for i in range(6, length):
        assert parsed[i]["bboxes"] == parsed[i]["classes"] == parsed[i]["ids"] == []


import praw
import time
import datetime
import re
import threading
import random
import os
import string
import random
from variables import sub_listing, responses, master_dict

class Modmail(object):

	def __init__(self, subreddit, subreddits, reddit):
		self.subreddit = subreddit
		self.subreddits = subreddits
		self.reddit = reddit
		self.report_at = subreddits[subreddit]['report']
		self.remove_at = subreddits[subreddit]['remove']
		self.verbose = subreddits[subreddit]['verbose']
		self.reports = self.get_reports()

	def save(self, sub_id):
		with open('saved.txt','a') as f:
			f.write(str(sub_id) + ' ')
			f.close()

	def check(self, sub_id):
		with open('saved.txt','r') as f:
			if str(sub_id) in f.read().split(' '):
				return True
			else:
				return False

	def modmailed(self, sub_id):
		with open('modmailed.txt','r') as f:
			if str(sub_id) in f.read().split(' '):
				return True
			else:
				return False

	def save_modmail(self, sub_id):
		with open('modmailed.txt','a') as f:
			f.write(str(sub_id) + ' ')
			f.close()

	def get_reports(self, limit=25):
		try:
			user_reports = {}
			queue = self.reddit.request('GET','r/{}/about/reports?limit={}'.format(self.subreddit, limit))['data']['children']
			for item in queue:
				if len(item['data']['user_reports']) < 1:
					pass
				else:
					user_reports[item['data']['id']] = item['data']['user_reports']
			return user_reports
		except:
			return {'id':'nothing'}

	def check_answered(self, sub_id):
		with open('answered.txt') as f:
			if f.read().split(' ').count(sub_id) > 0:
				return True
			else:
				return False

	def save_answered(self, sub_id):
		with open('answered.txt', 'a') as f:
			f.write(str(sub_id) + ' ')
			f.close()

	def check_reports(self):
		for report in self.reports:
			count = 0
			for item in self.reports[report]:
				count += int(item[-1])
			if count >= self.report_at:
				self.report_modmail(count=count, sub_id=report)

	def save_record(self, author):
		with open('records.txt') as f:
			f.write(author+'\n')
			f.close()

	def report_modmail(self, count, sub_id):
		try:
			print('Threshold met in /r/{}'.format(self.subreddit))
			submission = self.reddit.submission(sub_id)
			user = _reddit_.redditor(str(submission.author))
			created = time.strftime(('%Y %m %d'),time.localtime(user.created_utc)).split(' ')
			now = datetime.date.today()
			years = int(now.year) - int(created[0])
			months = int(now.month) - int(created[1])
			try:
				if months < 0:
					months = 12 + months
					years -= 1
				if years < 1:
					info = '[{} | {} | {} months](/u/{})'.format(user.link_karma, user.comment_karma, months)
				else:
					info = '[{} | {} | {} years {} months]'.format(user.link_karma, user.comment_karma, years, months)
			except:
				info = '[Unavailable]'
			u_reports = submission.user_reports
			reports = ''
			for report in u_reports:
				report = str(report)
				report = report.replace('[',''); report = report.replace(']','')
				reports += '*{}*\n\n'.format(report)
			if count >= self.remove_at and self.check(submission.id) is False:
				submission.mod.remove()
				try:
					print('Preparing to send modmail')
					self.reddit.subreddit(self.subreddit).message('Post Removal','[{}]({}) by /u/{} {} has been removed. [Image/video]({})\n\n[Report reasons:]({})\n\n{}\n\n---'.format(submission.title,submission.permalink, submission.author, info,submission.url,'https://www.reddit.com/r/{}/about/rules'.format(self.subreddit), reports))
					self.save(submission.id)
					print('Sent modmail to /r/{}'.format(self.subreddit))
				except:
					pass
			elif count >= self.report_at and count < self.remove_at and self.modmailed(submission.id) is False:
				print('Reporting')
				things = ''; count = 1
				try:
					rules = self.reddit.request('GET','r/{}/about/rules'.format(self.subreddit))
				except:
					rules = ['No personal attacks','Threatening or harassing','Spam']
				sub_rules = [rule['violation_reason'] for rule in rules['rules']]
				for rule in sub_rules:
					things += '^({}. {})\n\n'.format(count, rule)
					count+= 1
				try:
					title = re.sub('\(','',submission.title)
					title = re.sub('\)','',title)
				except Exception as e:
					print(e)
					title = 'Unable to retrieve title'
				try:
					author = str(submission.author)
				except:
					author = '[deleted]'
				if self.verbose is True:
					if submission.is_self == True:
						self.reddit.subreddit(self.subreddit).message('Reports','[{}]({}) by /u/{} {} has received {} reports.\n\nHere are the report reasons:\n\n{}\n\n---\n\nRemoval Reasons:\n\n{}\n\n---\n\nKEYS: !ap = approve + ignore | !rm = remove | !ban = ban'.format(title,submission.permalink, author, info, self.report_at, reports, things))
					else:
						if submission.is_self != True:
							self.reddit.subreddit(self.subreddit).message('Reports','[{}]({}) by /u/{} {} has received {} reports.\n\n[Image/video]({})\n\nHere are the report reasons:\n\n{}\n\n---\n\nRemoval Reasons:\n\n{}\n\n---\n\nKEYS: !ap = approve + ignore | !rm = remove | !ban = ban'.format(title,submission.permalink, submission.author, info, self.report_at, submission.url,reports, things))
				else:
					if submission.is_self == True:
						self.reddit.subreddit(self.subreddit).message('Reports','[{}]({}) by /u/{} {} has received {} reports.\n\n[Report reasons:]({})\n\n{}'.format(title,submission.permalink, submission.author, info, self.report_at, '/r/{}/about/rules'.format(submission.subreddit.display_name), reports))
					else:
						self.reddit.subreddit(self.subreddit).message('Reports','[{}]({}) by /u/{} {} has received {} reports.\n\n[Image/video]({})\n\n[Report reasons:]({})\n\n{}'.format(title,submission.permalink,submission.author, info, self.report_at, submission.url,'/r/{}/about/rules'.format(submission.subreddit.display_name),reports))
				self.save_modmail(submission.id)
				print('Report successful')
			else:
				print('Already modmailed')
		except Exception as e:
			print(e)

	def check_modmail(self):
		for message in self.reddit.subreddit(self.subreddit).mod.inbox(limit=2):
			if message.subject == "Post Removal" or message.subject == "Reports":
				try:
					permalink = message.body[re.search('\(',message.body).span()[1]:re.search('\)',message.body).span()[0]]
					submission = self.reddit.submission(url='https://www/reddit.com'+permalink)
					for reply in message.replies:
						if '!rm' in reply.body.split(' ') and self.check_answered(submission.id) is False:
							print('About to remove')
							self.save_answered(submission.id)
							reply.reply('Submission removed! {}'.format(random.choice(responses)))
							removal_prefix = 'Hello, /u/{}, unfortunately your submission has been removed for breaking the following subreddit rules:\n\n'.format(submission.author)
							message_mods = '\n\n---\n\n*If you have any questions, please [message the mods](https://www.reddit.com/message/compose?to={})*'.format(submission.subreddit)
							rules = self.reddit.request('GET','r/{}/about/rules'.format(submission.subreddit))
							sub_rules = [rule['violation_reason'] for rule in rules['rules']]
							try:
								reason = int(reply.body[re.search('!rm',reply.body).span()[1]+1:re.search('\d+',reply.body).span()[1]])
							except:
								reason = None
							if reason == None:
								try:
									reason = reply.body[re.search('\<',reply.body).span()[1]:re.search('\>',reply.body).span()[0]]
								except:
									reason = None
							if reason == None:
								default_response = '*Hello, /u/{}, your submission has been removed because it violates our [subreddit rules]({}). If you have any questions or concerns, or feel that this was done in error, please [message the moderators]({}). Best of luck in the future!*\n\n---\n\n^(I am a bot, and this action was performed automatically. For more information on me, follow) [^this ^link](https://www.reddit.com/r/AutoReporterBot/wiki/index)'.format(submission.author,'/r/{}/about/rules'.format(submission.subreddit), "https://www.reddit.com/message/compose?to=%2Fr%2F{}&subject=Removal%20Appeal&message=After%20having%20read%20the%20rules%2C%20I%20still%20have%20a%20question%20regarding%20the%20removal%20of%20my%20submission%0A%0A{}".format(submission.subreddit,submission.permalink))
								try:
									submission.mod.remove()
									(submission.reply(default_response).mod.distinguish(sticky=True))
								except Exception as e:
									reply.reply('*The submission was removed but there was an error in leaving a comment. Thanks. I am a bot.*')
							else:
								try:
									comment = submission.reply('{}* **{}**{}'.format(removal_prefix,sub_rules[reason-1],message_mods))
								except:
									comment = submission.reply('{}* **{}**{}'.format(removal_prefix,reason,message_mods))
								comment.mod.distinguish(sticky=True)
								submission.mod.remove()
							print('Removed')
						elif '!ap' in reply.body.split(' ') and self.check_answered(submission.id) is False:
							print('APPROVE')
							self.save_answered(submission.id)
							reply.reply('Submission approved! *{}*'.format(random.choice(responses)))
							submission.mod.approve()
							print('Approved')
							self.save_answered(submission.id)
						elif '!ban' in reply.body.split(' ') and self.check_answered(submission.id) is False:
							self.save_answered(submission.id)
							reply.reply('/u/{} has been banned! Enjoy your power you cancer mods! {}'.format(submission.author, random.choice(responses)))
							try:
								days = reply.body[re.search('!ban',reply.body).span()[1]+1:re.search('\d+',reply.body).span()[0]]
							except:
								days = None
							print("Banning user")
							self.reddit.subreddit(self.subreddit).banned.add(submission.author, ban_reason=str(submission.permalink)+' '+str(reply.author), duration=days)
							submission.mod.remove()
						elif '!shadow' in reply.body.split(' ') and self.check_answered(submission.id) is False:
							reply.reply('/u/{} has been shadowbanned. I am a bot'.format(submission.author))
							print('Banning')
							submission.mod.remove()
							wiki = self.reddit.request('GET','r/{}/wiki/config/automoderator'.format(self.subreddit))['data']['content_md']
							banned = 'type: any\nauthor: {}\naction: remove\naction_reason: shadowbanned user\n#Bot shadowbanned by /u/{}\n---'.format(submission.author, reply.author)
							self.reddit.subreddit(self.subreddit).wiki['config/automoderator'].edit(content=wiki + '\n\n' + banned, reason='Shadowbanned user')
							print('Banned')
							self.save_answered(submission.id)
						elif '!watch' in reply.body.split(' ') and self.check_answered(submission.id) is False:
							submission.mod.remove()
							reply.reply('/u/{} has been watchlisted. I am a bot'.format(submission.author))
							wiki = self.reddit.request('GET','r/{}/wiki/config/automoderator'.format(self.subreddit))['data']['content_md']
							banned = 'type: any\nauthor: {}\naction: report\naction_reason: shadowbanned user\n#Bot shadowbanned by /u/{}'.format(submission.author, reply.author)
							self.reddit.subreddit(self.subreddit).wiki['config/automoderator'].edit(content=wiki + '\n\n' + banned, reason='Shadowbanned user')
							self.save_answered(submission.id)
						elif '!globalshadowban' in reply.body.split(' ') and self.check_answered(submission.id) is False:
							submission.mod.remove()
							self.save_answered(submission.id)
							reply.reply('/u/{} has been global banned! Oh you cancerous bastard! I am a bot'.format(submission.author))
							banned = 'type: any\nauthor: {}\naction: report\naction_reason: shadowbanned user\n#Bot shadowbanned by /u/{}'.format(submission.author, reply.author)
							for subreddit in reddit.user.moderator_subreddits():
								try:
									wiki = self.reddit.request('GET','r/{}/wiki/config/automoderator'.format(subreddit))['data']['content_md']
									_reddit_.subreddit(subreddit).wiki['config/automoderator'].edit(content=wiki + '\n\n' + banned, reason='Shadowbanned user')
								except:
									pass
							print('Global banned')
						elif '!spam' in reply.body.split(' ') and self.check_answered(submission.id) is False:
							self.reply('Submission removed as spam! I am a bot')
							submission.mod.remove(spam=True)
							self.save_answered(submission.id)
						else:
							pass
				except:
					pass

class nukeCode(object):

	def __init__(self, subreddit):
		self.subreddit = subreddit
		self.alphanumeric = [letter for letter in string.printable if letter not in [i for i in "{|}~' \t\n\r\x0b\x0c"]]
		self.code = [random.randint(0,9) for x in range(10)]
		self.replace = random.randint(0,3)
		self.values = self.get_code()
		self.today_code = self.set_code()
		self.today = datetime.date.today().day
		self.old = self.is_new_day()
		self.submit()

	def get_code(self):
		settings = []
		for v in range(self.replace):
			settings.append(random.randint(0,9))
		return settings

	def set_code(self):
		for place in self.values:
			self.code[place] = random.choice(self.alphanumeric)
		code = ''.join([str(item) for item in self.code])
		return code

	def is_new_day(self):
		last_submission = r.subreddit(self.subreddit).new(limit=1)
		new_made = [submission.created_utc for submission in last_submission]
		made_day = time.strftime('%d',time.localtime(new_made[0]))
		if int(made_day) != (self.today):
			return True
		else:
			return False

	def submit(self):
		if self.old is True:
			try:
				submission = r.subreddit(self.subreddit).submit('{}'.format(time.strftime('%A, %B %d, %Y')), selftext=self.today_code)
				print('Submitted nukeCode')
				submission.reply('Pinging /u/just-a-traveler and /u/randoh12')
				print('Replied to nukeCode')
				return True
			except Exception as e:
				print(e)
				return False

	def current_code(self):
		try:
			submissions = r.subreddit('BotMasters').new(limit=1)
			for submission in submissions:
				code = submission.selftext
			return code
		except:
			print('nukeCode')

class botAdmin(object):

	def __init__(self, message, subreddits, reddit):
		self.message = message
		self.reddit = reddit
		self.subreddits = subreddits
		self.subject = message.subject
		self.author = message.author
		self.body = message.body
		self.approved_code = nukeCode('BotMasters').current_code()
		self.preview = '*A message concerning the AutoReporterBot, from Skynet*\n\n'

	def isApproved(self):
		if self.approved_code in self.subject:
			return True
		else:
			return False

	def isBroadcast(self):
		if '!broadcast' in self.subject:
			return True
		else:
			return False

	def announcement(self):
		print('Today\'s approved code: {}'.format(self.approved_code))
		listing = ''
		for subreddit in self.subreddits:
			listing += '\n\n* /r/{}'.format(subreddit)
		if self.isBroadcast() is True and self.isApproved() is True:
			for subreddit in self.subreddits:
				self.reddit.subreddit(subreddit).message('Official Bot Announcement',self.preview+self.body)
				print('Message sent to /r/{}'.format(subreddit))
			self.message.reply('*Thank you! Your message will be delivered momentarily to the following subreddits:*\n\n'+listing)
		else:
			pass

	def isInvite(self):
		if 'invitation to moderate' in self.subject:
			try:
				self.reddit.subreddit(self.message.subreddit.display_name).mod.accept_invite()
				print('Accepted invite to /r/{}'.format(self.message.subreddit))
			except Exception as e:
				pass

	def isQuery(self):
		if self.isApproved() is True and '!query' in self.subject:
			pass

def announcement_thread(reddit):
	while True:
		for message in reddit.inbox.unread(limit=1):
			botAdmin(message, sub_listing, reddit).announcement()
			botAdmin(message, sub_listing, reddit).isInvite()
			if any(title in message.subject for title in ['!broadcast','invitation to moderate']):
				message.mark_read()

def thread_starter(sub, subreddits, reddit):
	while True:
		try:
			print('Parsing /r/{} [{}]'.format(sub,str(threading.activeCount())))
			Modmail(sub, subreddits, reddit).check_reports()
			Modmail(sub, subreddits, reddit).check_modmail()
		except:
			Modmail(sub, subreddits, reddit).check_reports()
			Modmail(sub, subreddits, reddit).check_modmail()

def main(subreddits,reddit):
	try:
		for subreddit in subreddits:
			threading.Thread(target=thread_starter, kwargs={'sub':subreddit,'subreddits':subreddits,'reddit':reddit}).start()
		threading.Thread(target=announcement_thread, kwargs={'reddit':reddit}).start()
	except:
		main()

if __name__ == '__main__':
	main()


from .ellipsoid_gait_generator import EllipsoidGaitGenerator
from .kinematics import Kinematics

class GaitManager():
    """Connects the Kinematics class and the EllipsoidGaitGenerator class together to have a simple gait interface."""

    def __init__(self, robot, time_step):
        self.time_step = time_step
        self.gait_generator = EllipsoidGaitGenerator(robot, self.time_step)
        self.kinematics = Kinematics()
        joints = ['HipYawPitch', 'HipRoll', 'HipPitch', 'KneePitch', 'AnklePitch', 'AnkleRoll']
        self.L_leg_motors = []
        for joint in joints:
            motor = robot.getDevice(f'L{joint}')
            position_sensor = motor.getPositionSensor()
            position_sensor.enable(time_step)
            self.L_leg_motors.append(motor)

        self.R_leg_motors = []
        for joint in joints:
            motor = robot.getDevice(f'R{joint}')
            position_sensor = motor.getPositionSensor()
            position_sensor.enable(time_step)
            self.R_leg_motors.append(motor)

    def update_theta(self):
        self.gait_generator.update_theta()

    def command_to_motors(self, desired_radius=None, heading_angle=0):
        """
        Compute the desired positions of the robot's legs for a desired radius (R > 0 is a right turn)
        and a desired heading angle (in radians. 0 is straight on, > 0 is turning left).
        Send the commands to the motors.
        """
        if not desired_radius:
            desired_radius = 1e3
        x, y, z, yaw = self.gait_generator.compute_leg_position(
            is_left=False, desired_radius=desired_radius, heading_angle=heading_angle)
        right_target_commands = self.kinematics.inverse_leg(x * 1e3, y * 1e3, z * 1e3, 0, 0, yaw, is_left=False)
        for command, motor in zip(right_target_commands, self.R_leg_motors):
            motor.setPosition(command)

        x, y, z, yaw = self.gait_generator.compute_leg_position(
            is_left=True, desired_radius=desired_radius, heading_angle=heading_angle)
        left_target_commands = self.kinematics.inverse_leg(x * 1e3, y * 1e3, z * 1e3, 0, 0, yaw, is_left=True)
        for command, motor in zip(left_target_commands, self.L_leg_motors):
            motor.setPosition(command)


import helper as my
import operator
import math
import statistics

# =============================================================================
# SECTION 1
# =============================================================================
def distance(v, w):
    """
    Calculates the distance between m-dimensional vectors v and w, where
    the distance is the square root of the sum of squared differences for each
    point i in m.

    Parameters
    ----------
    v : LIST
        Vector V for i = 1, 2, ..., m.
    w : LIST
        Vector V for i = 1, 2, ..., m.

    Returns
    -------
    FLOAT
        Square root of sum of squared difference between v_i and w_i.

    """
    diff = [i**2 for i in list(map(operator.sub, v, w))]
    return math.sqrt(sum(diff))

def distances(v, centres):
    """
    For data point v, distances() calculates the distance between v and all of
    centres.

    Parameters
    ----------
    v : LIST
        m-dimensional vector.
    centres : LIST of LISTS
        List of centres of length n, each containing a centre j of length m.

    Returns
    -------
    FLOAT
        The minimum distance between v and any centre.
    centre : LIST
        Returns the centre for which the distance is minimised.

    """
    d = [distance(v, w) for w in centres]
    centre = d.index(min(d))
    return min(d), centre

def max_distance(dataset, centres):
    """
    Calculates the farthest distance from any datapoint and its assigned
    centre.

    Parameters
    ----------
    dataset : LIST of LISTS
        Dataset of length n containing n vectors of length m.
    centres : LIST of LISTS
        List of k centres of length m.

    Returns
    -------
    FLOAT
        Maximum distance.
    index : INT
        Index of datapoint which has max distance.
    centre : TYPE
        Datapoint which has max distance.

    """
    d = []
    for data in dataset:
        distances = [distance(data, centre) for centre in centres]
        d.append(min(distances))
    index = d.index(max(d))
    return max(d), index, dataset[index]

def farthest_first_traversal(data, k):
    """

    Parameters
    ----------
    data : TYPE
        DESCRIPTION.
    k : TYPE
        DESCRIPTION.

    Returns
    -------
    centres : TYPE
        DESCRIPTION.

    """
    centres = [data[0]]
    while len(centres) < k:
        temp = max_distance(data, centres)
        centres.append(data[temp[1]])
    return centres

def distortion(dataset, centres):
    """

    Parameters
    ----------
    dataset : TYPE
        DESCRIPTION.
    centres : TYPE
        DESCRIPTION.

    Returns
    -------
    TYPE
        DESCRIPTION.

    """
    d = []
    for data in dataset:
        d.append(distances(data, centres)[0]**2)
    return round(sum(d)/len(dataset), 3)

def gravity(cluster):
    """

    Parameters
    ----------
    cluster : TYPE
        DESCRIPTION.

    Returns
    -------
    res : TYPE
        DESCRIPTION.

    """
    # n = len(cluster)
    # res = [sum(x)/n for x in zip(*cluster)]
    res = [round(statistics.mean(x), 3) for x in zip(*cluster)]
    return res

def centres_to_clusters(dataset, centres):
    """

    Parameters
    ----------
    dataset : TYPE
        DESCRIPTION.
    centres : TYPE
        DESCRIPTION.

    Returns
    -------
    clusters : TYPE
        DESCRIPTION.

    """
    clusters = {new_list: [] for new_list in range(len(centres))}
    for data in dataset:
        clusters[distances(data, centres)[1]].append(data)
    return clusters

def new_centres(clusters):
    """

    Parameters
    ----------
    clusters : TYPE
        DESCRIPTION.

    Returns
    -------
    centres : TYPE
        DESCRIPTION.

    """
    centres = []
    for i in clusters:
        centres.append(gravity(clusters[i]))
    return centres

def lloyd(dataset, k):
    """

    Parameters
    ----------
    dataset : TYPE
        DESCRIPTION.
    k : TYPE
        DESCRIPTION.

    Returns
    -------
    centres : TYPE
        DESCRIPTION.

    """
    centres = dataset[:k]
    count = 0
    while count < 100:
        clusters = centres_to_clusters(dataset, centres)
        centres = new_centres(clusters)
        count += 1
    return centres

# =============================================================================
# SECTION 2
# =============================================================================
def gravity_centres(dataset, hidden_vector):
    import numpy as np
    denom = sum(hidden_vector)
    num = []
    for i, data in enumerate(dataset):
        value = np.dot(hidden_vector[i], data)
        num.append(value)
    return sum(num)/denom

# =============================================================================
# Testing
# =============================================================================
if __name__ == "__main__":
    my.flash()


"""
Class Beam
"""

import numpy as np
import matplotlib.pyplot as plt

from .classes_elements import FIG_HEIGHT, FIG_DPI
from . import functions_general as fg

class Beam:
    """
    Beam Object
    Defines a light beam that interacts with optical elements
    Contains a list of positions and a current direction

    beam = Beam(x,y,z, dx,dy,dz)
     x,y,z = initial position
     dx,dy,dz = initial direction

    beam.new( (x,y,z), (dx,dy,dz) ) - adds new position and changes direction

    """

    def __init__(self, x=0, y=0, z=0, dx=0, dy=0, dz=1, beam_id=0):
        self.x, self.y, self.z = x, y, z
        self.dx, self.dy, self.dz = dx, dy, dz
        self.positions = [np.array([x, y, z], dtype=np.float)]
        direction = np.asarray([dx, dy, dz], dtype=np.float)
        self.direction = direction/np.sqrt(np.sum(np.square(direction)))
        self.beam_id = beam_id
        # Future parameters:
        self.energy_kev = 8.0
        self.intensity = 100.0
        self.polarisation = (1, 0, 0)  # P1, P2, P3 Poincare-Stokes parameters
        self.phase = 0.0  # 0-2pi

    def __repr__(self):
        current_pos, current_dir = self.current()
        return "Beam(%2d, pos: %s, dir: %s)" % (self.beam_id, current_pos, current_dir)

    def reset(self):
        """Reset Beam to original position & direction"""
        self.positions = [np.array([self.x, self.y, self.z], dtype=np.float)]
        direction = np.asarray([self.dx, self.dy, self.dz], dtype=np.float)
        self.direction = direction / np.sqrt(np.sum(np.square(direction)))

    def xyz(self):
        """Return array of positions"""
        return np.array(self.positions)

    def add_position(self, x, y, z):
        """Add a new position"""
        self.positions += [np.array([x, y, z], dtype=np.float)]

    def new_direction(self, dx, dy, dz):
        """Define the direction after the last point"""
        direction = np.asarray([dx, dy, dz], dtype=np.float)
        self.direction = direction/np.sqrt(np.sum(np.square(direction)))

    def current(self):
        """Return current position and direction"""
        return self.positions[-1], self.direction

    def new(self, position, direction):
        """Add a new beam path"""
        self.add_position(*position)
        if direction is None:
            self.direction = None
        else:
            self.new_direction(*direction)

    def extrude(self, distance=1.0):
        """Add a new beam position along current direction"""
        pos, direction = self.current()
        if direction is None:
            return
        new = pos + distance*direction
        self.add_position(*new)

    def total_distance(self):
        """Calculate total distance travelled by beam"""
        distance = 0
        for n in range(1, len(self.positions)):
            distance += fg.mag(self.positions[n] - self.positions[n-1])
        return distance

    def intersect_xy(self, beam):
        """Returns the intersection position with another beam in the xy plane"""
        pos1 = self.positions[-1][[0, 1]]
        dir1 = self.direction[[0, 1]]
        pos2 = beam.positions[-1][[0, 1]]
        dir2 = beam.direction[[0, 1]]
        return fg.vector_intersection(pos1, dir1, pos2, dir2)

    def intersect_xz(self, beam):
        """Returns the intersection position with another beam in the xz plane"""
        pos1 = self.positions[-1][[0, 2]]
        dir1 = self.direction[[0, 2]]
        pos2 = beam.positions[-1][[0, 2]]
        dir2 = beam.direction[[0, 2]]
        return fg.vector_intersection(pos1, dir1, pos2, dir2)

    def intersect_yz(self, beam):
        """Returns the intersection position with another beam in the yz plane"""
        pos1 = self.positions[-1][[1, 2]]
        dir1 = self.direction[[1, 2]]
        pos2 = beam.positions[-1][[1, 2]]
        dir2 = beam.direction[[1, 2]]
        return fg.vector_intersection(pos1, dir1, pos2, dir2)

    def intersect(self, beam):
        """Returns the point of intersectin with another beam in 3D"""
        pos1 = self.positions[-1]
        dir1 = self.direction
        pos2 = beam.positions[-1]
        dir2 = beam.direction
        intersect = fg.vector_intersection3d(pos1, dir1, pos2, dir2)
        distance = fg.distance2line(pos1, pos1 + dir1, intersect)
        if distance > 1e-6:
            print('Vectors dont intersect')
        return intersect, distance

    def angle(self, beam, deg=True):
        """Returns the angle in degrees between who beams"""
        return fg.ang(self.direction, beam.direction, deg)

class Interaction:
    """
    Beam interaction on optical element
    :param position: (x, y, z)
    :param incident: (dx, dy, dz)
    :param scattered: (dx, dy, dz)
    """

    def __init__(self, position, incident, scattered):
        self.position = np.asarray(position, dtype=np.float).reshape(3)
        self.incident = np.asarray(incident, dtype=np.float) / np.sqrt(np.sum(np.square(incident)))
        self.scattered = np.asarray(scattered, dtype=np.float) / np.sqrt(np.sum(np.square(scattered)))

    def plot(self, axes=None, length=1):
        if axes is None:
            fig = plt.figure(figsize=[FIG_HEIGHT, FIG_HEIGHT], dpi=FIG_DPI)
            ax = fig.add_subplot(111, projection='3d')
        else:
            ax = axes

        pos = [self.position - length * self.incident,
               self.position,
               self.position + length * self.scattered]
        ax.plot(pos[:, 0], pos[:, 1], pos[:, 2], 'r-+', lw=1)

        if axes is None:
            ax.set_xlabel('z')
            ax.set_ylabel('x')
            ax.set_zlabel('y')
            # ax.set_xlim([-1, 1])
            # ax.set_ylim([-1, 1])
            # ax.set_zlim([-1, 1])


import time
import logging
from PySide2 import QtWidgets, QtCore, QtGui

from forms.ui_log import Ui_LogWidget
from forms.ui_log_title import Ui_LogTitleWidget

class LogHandler(logging.Handler):
    def __init__(self, callback):
        super().__init__()
        self._callback = callback

    def emit(self, record):
        if self._callback:
            self._callback(record)

class LogWidget(QtWidgets.QWidget):
    sig_new_log = QtCore.Signal(object)

    colors = {
        logging.DEBUG: QtGui.QColor(116, 185, 255),
        logging.INFO: QtCore.Qt.green,
        logging.WARNING: QtCore.Qt.yellow,
        logging.ERROR: QtCore.Qt.red,
        logging.CRITICAL: QtCore.Qt.red
    }

    def __init__(self, parent=None):
        super().__init__(parent)
        self._ui = Ui_LogWidget()
        self._ui.setupUi(self)
        self._ui.combobox_level.setView(QtWidgets.QListView())

        self._handler = LogHandler(
            lambda record: self.new_log(record)
        )

        logger = logging.getLogger()
        logger.addHandler(self._handler)

        self._levels = {
            logging.getLevelName(logging.NOTSET) : logging.NOTSET,
            logging.getLevelName(logging.DEBUG)  : logging.DEBUG,
            logging.getLevelName(logging.INFO)   : logging.INFO,
            logging.getLevelName(logging.WARNING): logging.WARNING,
            logging.getLevelName(logging.ERROR)  : logging.ERROR
        }
        self._ui.combobox_level.addItems(self._levels.keys())
        self._ui.combobox_level.setCurrentText(logging.getLevelName(self.get_loglevel()))
        self._ui.combobox_level.currentIndexChanged.connect(self._on_loglevel_changed)
        self._ui.btn_clean.clicked.connect(self._on_log_clean)

        self.sig_new_log.connect(self._on_log)

    def set_loglevel(self, level):
        self._handler.setLevel(level)

    def get_loglevel(self):
        return self._handler.level

    def log_clean(self):
        self._ui.textbrowser.clear()

    def new_log(self, record: logging.LogRecord):
        self.sig_new_log.emit(record)

    def _on_log(self, record: logging.LogRecord):
        if record.name == "zerorpc.gevent_zmq":
            return

        sec = time.strftime(f"%H:%M:%S", time.gmtime(record.created))
        msec = f'{int(record.msecs):03d}'
        timestamp = f"  {sec}.{msec}"
        color = self.colors.get(record.levelno, QtCore.Qt.white)
        self._ui.textbrowser.setTextColor(color)
        self._ui.textbrowser.insertPlainText(record.levelname.ljust(8))
        self._ui.textbrowser.insertPlainText(timestamp)
        self._ui.textbrowser.insertPlainText("  " + record.name)

        if record.levelno >= logging.ERROR:
            self._ui.textbrowser.insertPlainText(f"  {record.funcName}:{record.lineno}")

        self._ui.textbrowser.insertPlainText(": ")
        self._ui.textbrowser.setTextColor(QtCore.Qt.white)
        self._ui.textbrowser.insertPlainText(record.msg)
        self._ui.textbrowser.insertPlainText('\n')
        self._ui.textbrowser.moveCursor(self._ui.textbrowser.textCursor().End)

        QtWidgets.QApplication.instance().processEvents()

    def _on_loglevel_changed(self):
        name = self._ui.combobox_level.currentText()
        if name not in self._levels:
            return
        self.set_loglevel(self._levels[name])

    def _on_log_clean(self):
        self.log_clean()


import pandas as pd
import numpy as np
import json

def get_sheet_name(filepath: str):
    xl = pd.ExcelFile(filepath)
    # print(xl.sheet_names)
    return xl.sheet_names

def sheet_to_dict(filepath: str, sheet_name: str):
    df = pd.read_excel(filepath, sheet_name=sheet_name)
    df.replace({np.NAN: None}, inplace=True)

    headers = [header for header in df.columns.ravel()]

    data = []
    for row in df.index:
        tmp = {}
        for idx, header in enumerate(headers):
            tmp[idx] = df[header][row]
        data.append(tmp)

    result = {
        "headers": [{idx: header} for idx, header in enumerate(headers)],
        "data": data,
    }
    return result

def save_json(data: dict, filepath):
    json_string = json.dumps(data, ensure_ascii=False, indent=4)
    with open(filepath, "w", encoding="utf-8") as f:
        f.write(json_string)

def excel_to_json(filepath: str):
    sheet_list = get_sheet_name(filepath)

    result = []
    for sheet in sheet_list:
        result.append(sheet_to_dict(filepath, sheet))

    return result


class Solution:
    def combinationSum(self, candidates: List[int], target: int) -> List[List[int]]:
        self.ans = []
        self.backtrack(0, [], target, candidates)
        return self.ans

    def backtrack(self, start, curr_array, target, candidates):
        if target < 0:
            return
        if target == 0:
            self.ans.append(curr_array[:])

        for i in range(start, len(candidates)):
            curr_array.append(candidates[i])
            self.backtrack(i, curr_array, target - candidates[i], candidates)
            curr_array.pop()


from CustomExceptions import InsufficientFundsException, NegativeBalanceException
from asyncio import gather, sleep
from discord import Embed
from discord.ext import commands
from Configuration import load_config
from CurrencyUtils import *
import CurrencyManager as currency

requirements = {'general': [], 'server': ['react_confirm']}

async def handle_insufficient_funds(ctx, exception: InsufficientFundsException = None, delete: bool = False):
	sent_msg = await ctx.send(embed=Embed(title='Insufficient Funds.', color=0xff0000))
	if delete:
		await sleep(5)
		await sent_msg.delete()

class SimpleColorRoles(commands.Cog):
	def __init__(self, bot):
		self.bot = bot
		self.cfg = load_config()

	@commands.command(name='PurchaseColor', aliases=['BuyColor'])
	async def purchase_default_color(self, ctx):
		await self.purchase_color(ctx, premium=False)

	@commands.command(name='PurchasePremiumColor', aliases=['BuyPremiumColor'])
	async def purchase_premium_color(self, ctx):
		await self.purchase_color(ctx, premium=True)

	async def purchase_color(self, ctx, premium: bool):
		default_color_price = self.cfg.price_default_color
		premium_color_price = self.cfg.price_premium_color

		role_price = premium_color_price if premium else default_color_price

		try:
			new_balance = await currency.addToMemberBalance(ctx.author.id, -role_price)
		except NegativeBalanceException as e:
			await handle_insufficient_funds(ctx, exception=e, delete=True)
			return

		default_color_role = self.cfg.role_default_color
		premium_color_role = self.cfg.role_premium_color

		purchased_role_id = premium_color_role if premium else default_color_role

		role = ctx.guild.get_role(purchased_role_id)

		await gather(
			ctx.author.add_roles(role, reason='Purchased role'),
			transaction_log(self.bot, ctx.author, -role_price, title='User bought a Color role.'),
			ctx.channel.send(embed=Embed(
				title='Congrats!',
				description=f'New balance: {new_balance} {pluralise(new_balance)}',
				color=0x00ff00
			))
		)

def setup(bot):
	bot.add_cog(SimpleColorRoles(bot))


'''
초기(시작)좌표를 설정
L은 현재 x 좌표를 1만큼 감소
R은 현재 x 좌표를 1만큼 증가
U는 현재 y 좌표를 1만큼 감소
D는 현재 y 좌표를 1만큼 증가
'''

def final_position(N, Plan):
    x, y = 1,1
    for a in Plan:
        if a == "L":
            if y > 1: #공간을 벗어나지 않게 제한
                y -= 1
        elif a == 'R':
            if y < N:
                y += 1
        elif a == 'U':
            if x > 1:
                x -= 1
        else:
            if x < N:
                x +=1
        # print(x,y)
    return x,y

N = 5
Plan = ['R', 'R', 'R', 'U', 'D', 'D']
print(final_position(N, Plan))


from master import master_config
from master.factory import annotator_factory, chromeos_factory

from buildbot.schedulers.basic import SingleBranchScheduler as Scheduler

def Builder(factory_obj, dname, sname, flavor, board):
  cbb_name = '%s-tot-chromium-pfq-informational' % (board,)
  builder = {
      'name': '%s (%s)' % (dname, flavor),
      'builddir': '%s-tot-chromeos-%s' % (flavor, sname),
      'category': '2chromium',
      'factory': chromeos_factory.ChromiteRecipeFactory(
          factory_obj, 'cros/cbuildbot'),
      'gatekeeper': 'pfq',
      'scheduler': 'chromium_cros',
      'notify_on_missing': True,
      'properties': {
          'cbb_config': cbb_name,
      },
  }
  return builder

def Update(_config, active_master, c):
  factory_obj = annotator_factory.AnnotatorFactory(
      active_master=active_master)

  builders = [
      Builder(factory_obj, 'X86', 'x86', 'chromium', 'x86-generic'),
      Builder(factory_obj, 'AMD64', 'amd64', 'chromium', 'amd64-generic'),
      Builder(factory_obj, 'Daisy', 'daisy', 'chromium', 'daisy'),
  ]

  c['schedulers'] += [
      Scheduler(name='chromium_cros',
                branch='master',
                treeStableTimer=60,
                builderNames=[b['name'] for b in builders],
      ),
  ]
  c['builders'] += builders


# Library to control MQTT
import paho.mqtt.client as mqtt
# Library to control the LEDs
from gpiozero import LED
# Library to control Servo's
from gpiozero import AngularServo

# Temperature
red_temp = LED(27)  # red temp. alarm LED
green_temp = LED(18)  # green temp. status LED

tempThresholdValueHigh = 25.00  # °C
tempThresholdValueLow = 20.00  # °C

# Soil Moisture
# you can adjust the threshold value, 0 - 4095 (12 bits), lower value is wetter.
soilThresholdValue = 3000
servo_soil = AngularServo(22, min_angle=0, max_angle=180)  # Servo(22)

# Rain
# you can adjust the threshold value, 0 - 4095 (12 bits), lower value is wetter.
rainThresholdValue = 2000
servo_rain = AngularServo(23, min_angle=0, max_angle=180)  # Servo(23)

# Greenhouse Dynamic Roof
yellow_roof = LED(21)  # Dynamic Roof open is on, closed is off.

# Heating
red_heating = LED(5)  # Heating on is on, heating off is off.

# Sprinklers
blue_sprinklers = LED(26)  # Sprinklers on is on, sprinklers off, is off.

# The callback for when the client connects to the broker
def on_connect(client, userdata, flags, rc):
    # Print result of connection attempt
    # Connection Return Codes:
    # 0: Connection successful
    # 1: Connection refused – incorrect protocol version
    # 2: Connection refused – invalid client identifier
    # 3: Connection refused – server unavailable
    # 4: Connection refused – bad username or password
    # 5: Connection refused – not authorised
    # 6-255: Currently unused.
    if rc == 0:
        print("\nConnection is good \nReturned code =", rc)
    else:
        print("Bad connection Returned code= ", rc)
    # Subscribe to the topics by using a wild card,
    # receive any messages published in it.
    client.subscribe("home/esp32/#")

# The callback for when a PUBLISH message is received from the server.
def on_message(client, userdata, msg):
    # Without the command below, the message will be decoded in Bytes ('bXXX'),
    # so we need to convert it to UTF-8 before using it.
    msg.payload = msg.payload.decode("utf-8")
    # Print a received msg
    print("Message received >>> " + msg.topic + " >>> " + str(msg.payload))
    print("Topic is: " + msg.topic)
    print("Payload is: " + msg.payload)

    # Temperature Control
    if msg.topic == "home/esp32/dht11_temp":
        if float(msg.payload) > tempThresholdValueHigh:
            print(" - To hot, Opening Greenhouse Roof")
            green_temp.off()
            red_temp.on()
            yellow_roof.on()
            red_heating.off()
        elif float(msg.payload) < tempThresholdValueLow:
            print(" - To cold, Greenhouse Heating On")
            green_temp.off()
            red_temp.on()
            yellow_roof.off()
            red_heating.on()
        else:
            print(" - Temperature is OK")
            green_temp.on()
            red_temp.off()
            yellow_roof.off()
            red_heating.off()

    # Soil Control
    if msg.topic == "home/esp32/soil":
        if int(msg.payload) > soilThresholdValue:
            print(" - Watering your plant")
            servo_soil.angle = 0
            blue_sprinklers.on()
        else:
            print(" - Plants don't need watering")
            servo_soil.angle = 180
            blue_sprinklers.off()

    # Rain Control
    if msg.topic == "home/esp32/rain":
        if int(msg.payload) < rainThresholdValue:
            print(" - It's raining")
            servo_rain.angle = 180
        else:
            print(" - It's dry")
            servo_rain.angle = 0

# broker is located on this machine (loopback address)
broker = "127.0.0.1"
# Create instance of client
# !!!!! with client ID “digi_mqtt_test” !!!!
client = mqtt.Client()
# Define callback function for successful connection
client.on_connect = on_connect
# Define callback function for receipt of a message
client.on_message = on_message
# Define username and password stored in Broker password file
client.username_pw_set("koen", "k03n123")
# Connect to (broker, port, keepalive-time)
client.connect(broker, 1883, 60)

# The Topics and payloads, you would like to publish
# Should be located before "client.loop_forever()"

client.publish("home/RPi4B/Lighting", 1)  # publish

# Start networking daemon
client.loop_forever(1)


import numpy as np
import lightkurve as lk
import pymc3 as pm
import arviz as az
import matplotlib.pyplot as plt
import photutils
import corner
import matplotlib

def plot_sources(img_dir, image, sources, fwhm, ann_rads):
	positions = [(x, y) for x, y in zip(
		sources['xcentroid'], sources['ycentroid'])]
	apertures = photutils.CircularAperture(positions, r = fwhm/2)
	annuli = photutils.CircularAnnulus(
		positions, r_in = ann_rads[0], r_out = ann_rads[1])
	plt.figure(figsize = (10, 8))
	plt.imshow(image, cmap = 'Greys', vmin = -5000, vmax = 5000,
		origin = 'lower')
	plt.colorbar()
	apertures.plot(color = 'blue', lw = 1.5, alpha = 0.5)
	annuli.plot(color = 'green', lw = 1.5, alpha = 0.5)
	plt.savefig(img_dir + 'source_plot.png')
	plt.close()
	return None

def plot_outlier_rejection(plot_dir, x, quick_detrend, filtered, mask,
	tag = ''):
	plt.plot(x, quick_detrend, 'k.')
	plt.plot(x, filtered, 'r-')
	plt.plot(x[~mask], quick_detrend[~mask], 'r.')
	plt.savefig(f'{plot_dir}outlier_rejection'+tag+'.png')
	plt.close()
	return None

def plot_initial_map(plot_dir, x, ys, yerrs, compars, map_soln, gp = False,
	baseline_off = False):
	lc = map_soln["light_curve"]
	vec = x - np.median(x)
	systematics = np.dot(map_soln["weights"], compars)
	if gp:
		baseline = map_soln["gp_pred"]
	elif baseline_off:
		baseline = 0.
	else:
		baseline = np.poly1d(map_soln["baseline"])(vec)

	detrended_data = (ys[0] - baseline)/systematics
	true_err= np.sqrt(yerrs[0]**2 + map_soln["jitter"]**2)

	plt.errorbar(x, detrended_data, yerr = true_err,
		color = 'k', marker = '.', linestyle = 'None')
	plt.plot(x, lc, 'r-', zorder = 10)
	plt.savefig(f'{plot_dir}initial_map_soln.png')
	plt.close()
	return None

def plot_quickfit(plot_dir, x, ys, yerrs):
	plt.errorbar(x, ys[0]/np.mean(ys[1:], axis = 0), yerr = yerrs[0],
		marker = '.', linestyle = 'None')
	plt.xlabel("Time [BJD]")
	plt.ylabel("Normalized Flux (Quick Detrend)")
	plt.savefig(f'{plot_dir}quickfit.png')
	plt.close()
	return None

def plot_covariates(plot_dir, x, covariate_names, covs):
	for name, cov in zip(covariate_names, covs):
		plt.plot(x, cov, 'C0-')
		plt.xlabel("Time [BJD]")
		plt.ylabel(f"{name}")
		plt.savefig(f'{plot_dir}{name}.png')
		plt.close()
	return None

def plot_white_light_curves(plot_dir, x, ys):
	for i, y in enumerate(ys):
		fmtstr = 'k.' if i == 0 else '.'
		lblstr = f'Comp {i}' if i > 0 else 'Target'
		plt.plot(x, y, fmtstr, label = lblstr)
	plt.legend(loc = 'best')
	plt.savefig(f'{plot_dir}white_light_curves.png')
	plt.legend(loc = 'best')
	plt.close()
	return None

def plot_aperture_opt(plot_dir, apertures, rmses):
	plt.plot(apertures, rmses)
	plt.xlabel("Aperture Size [px]")
	plt.ylabel("Per-Point RMS")
	plt.savefig(f'{plot_dir}rms_vs_aperture.png')
	plt.close()
	return None

def gen_rms(lc, binsize):
	binned = lc.bin(time_bin_size = binsize)
	return(np.nanstd(binned.flux))

def gen_photon_noise(lc, photon_noise, binsize, texp):
	binned = lc.bin(time_bin_size = binsize)
	length_arr = len(binned.time)
	length_factor = np.sqrt(length_arr/(length_arr - 1))
	white_noise = photon_noise/np.sqrt(binsize/texp)*length_factor
	return white_noise

def gen_photon_noise_errors(lc, photon_noise, binsize):
	binned = lc.bin(time_bin_size = binsize)
	length_arr = len(binned.time)
	rms = np.nanstd(binned.flux)
	return rms/np.sqrt(2*length_arr)

def represent_noise_stats(dump_dir, new_map, resid, yerrs):
	filename = open(f'{dump_dir}noise_stats.txt', 'w')
	shot_noise = yerrs[0]
	sigma_extra = float(new_map['jitter'])
	rms = np.nanstd(resid)
	npts = len(resid)

	print("Mean shot noise (data + bkg): ", np.mean(shot_noise)*1e6, "ppm",
		file = filename)
	print("Extra noise: ", sigma_extra*1e6, "ppm", file = filename)
	print("Per point RMS: ", rms*1e6, "ppm", file = filename)
	print("Number of points: ", npts, file = filename)
	return None

def corner_plot(plot_dir, samples, varnames):
	corner.corner(samples, var_names = varnames)
	plt.savefig(f'{plot_dir}cornerplt.pdf', dpi = 200,
		bbox_inches = 'tight')
	plt.close()
	return None

def trace_plot(plot_dir, data, varnames):
	az.plot_trace(data, var_names = varnames)
	plt.savefig(f'{plot_dir}traceplt.pdf')
	plt.close()
	return None

def tripleplot(plot_dir, dump_dir, x, ys, yerrs, compars, new_map,
	trace, texp, phase = 'primary', bin_time = 5, gp = False,
	baseline_off = False):
	#bin_time in mins

	matplotlib.rcParams['mathtext.fontset'] = 'cm'
	matplotlib.rcParams['font.family'] = 'STIXGeneral'
	matplotlib.rcParams['font.size'] = 20
	fig, ax = plt.subplots(3, 1, figsize = (8, 12),
		gridspec_kw={'height_ratios': [3, 1, 3]})

	#MAP lightcurve and reduction
	systematics = np.dot(np.array(new_map[f"weights"]), compars)
	vec = x - np.median(x)
	if gp:
		baseline = np.array(new_map['gp_pred'])
	elif baseline_off:
		baseline = 0.
	else:
		baseline = np.poly1d(np.array(new_map[f'baseline']))(vec)

	detrended_data = (ys[0] - baseline)/systematics
	lc = np.array(new_map[f'light_curve'])
	true_err = np.sqrt(yerrs[0]**2 + float(new_map[f"jitter"])**2)
	map_t0 = float(new_map["t0"])
	map_p = float(new_map['period'])
	if phase == 'primary':
		x_fold = (x - map_t0 + 0.5 * map_p) % map_p - 0.5 * map_p
	else:
		x_fold = (x - map_t0) % map_p - 0.5 * map_p

	if gp:
		ax[0].plot(x_fold, 1+baseline, 'b-')

	#setting the light curves for plotting and residuals
	plot_lc = lk.LightCurve(time = x_fold, flux = detrended_data,
		flux_err = true_err)
	bin_lc = plot_lc.bin(time_bin_size = bin_time / 1440.)
	plot_resid = lk.LightCurve(time = x_fold, flux = detrended_data - lc,
		flux_err = true_err)
	bin_resid = plot_resid.bin(time_bin_size = bin_time / 1440.)
	represent_noise_stats(dump_dir, new_map, plot_resid.flux, yerrs)

	#plotting light curve and residuals, unbinned and binned
	ax[0].errorbar(x_fold, detrended_data, yerr = true_err,
		color = 'k', marker = '.', linestyle = 'None', alpha = 0.1)
	ax[0].errorbar(bin_lc.time.value, bin_lc.flux.value,
		yerr = bin_lc.flux_err.value,
		color = 'k', marker = '.', linestyle = 'None', ms = 5)
	ax[1].errorbar(plot_resid.time.value, plot_resid.flux.value,
		yerr = plot_resid.flux_err.value, color = 'k', marker = '.',
		linestyle = 'None', alpha = 0.1)
	ax[1].errorbar(bin_resid.time.value, bin_resid.flux.value,
		yerr = bin_resid.flux_err.value, color = 'k', marker = '.',
		linestyle = 'None', ms = 5)

	#MAP wirc light curve
	ax[0].plot(x_fold, lc, f'r-', zorder = 10, lw = 2)
	#68 percentile on the confidence interval
	stacked = trace.posterior.stack(draws=("chain", "draw"))
	lcsamps = stacked.light_curve.values
	lower = np.percentile(lcsamps, 16, axis = 1)
	upper = np.percentile(lcsamps, 84, axis = 1)
	ax[0].fill_between(x_fold, lower, upper,
		alpha = 0.3, facecolor = f'r', lw = 1)

	#rms vs binsize
	tsep = np.median(np.ediff1d(x))
	binsizes = np.arange(1, 30) * tsep
	rmses = [gen_rms(plot_resid, bs) for bs in binsizes]
	photon_noise = np.median(yerrs[0])
	photon_noises = [gen_photon_noise(
		plot_resid, photon_noise, bs, tsep) for bs in binsizes]
	photon_noise_errors = [gen_photon_noise_errors(
		plot_resid, photon_noise, bs) for bs in binsizes]

	ax[2].errorbar(binsizes*1440., rmses, yerr = photon_noise_errors,
		color = 'k')
	ax[2].plot(binsizes*1440., photon_noises, 'r-')
	ax[2].plot(binsizes*1440.,
		np.array(photon_noises)*rmses[0]/photon_noises[0], 'r--')

	ax[0].set_ylabel("Relative Flux")
	ax[1].set_ylabel("Residual")
	ax[1].set_xlabel("Time from Eclipse Center [d]")
	ax[2].set_ylabel("RMS")
	ax[2].set_xlabel("Binsize [min]")
	ax[2].set_xscale('log')
	ax[2].set_yscale('log')

	plt.tight_layout()
	plt.savefig(f'{plot_dir}triple_plot.pdf', dpi = 200,
		bbox_inches = 'tight')
	plt.close()
	return None


import random, sys
from deap import base, creator, tools

class GeneticAlgorithm():
    def __init__(self, cipher):
        self.cipher = cipher
        self.evolutions = 100
        self.pop_size = 3000
        self.mutindpb = 0.8
        self.cxindpb = 0.8
        self.mutletterpb = 2/26
        self.cxletterpb = 2/26
        self.survpb = 1000/5000
        self.ciphertext = None
        self.fitness = None
        self.weights = None
        self.toolbox = base.Toolbox()

    def __load_tools(self):
        creator.create("FitnessMax", base.Fitness, weights=self.weights)
        creator.create("Individual", list, fitness=creator.FitnessMax)
        self.toolbox.register("indices", random.sample, list('abcdefghijklmnopqrstuvwxyz'), 26)
        self.toolbox.register("individual", tools.initIterate, creator.Individual, self.toolbox.indices)
        # self.toolbox.register("mate", self.cipher.mate, indpb=self.cxletterpb)
        self.toolbox.register("select", tools.selBest)
        self.toolbox.register("mutate", tools.mutShuffleIndexes, indpb=self.mutletterpb)
        self.toolbox.register("evaluate", self.__get_fitness)

    def __get_key(self, ind):
        return ''.join(ind)

    def __get_decipher(self, ind):
        key = self.__get_key(ind)
        return self.cipher.decipher(key, self.ciphertext)

    def __get_fitness(self, inds):
        # input(inds)
        guesses = [self.__get_decipher(ind) for ind in inds]
        # input(guesses)
        return self.fitness(guesses)

    def set_fitness(self, fitness, weights):
        self.weights = weights
        self.fitness = fitness
        self.__load_tools()

    def set_ciphertext(self, ciphertext):
        self.ciphertext = ciphertext

    def crack(self):
        sys.stdout.write('\n')
        sys.stdout.flush()
        pop = []
        for i in range(self.pop_size):
            ind = self.toolbox.individual()
            pop.append(ind)
        fit = self.toolbox.evaluate(pop)
        for i in range(self.pop_size):
            pop[i].fitness.values = [fit[0][i][0]]

        # Modification of the algorithm shown here:
        # https://deap.readthedocs.io/en/master/examples/ga_onemax.html

        champ_counter = 0
        old_champ = self.toolbox.select(pop, 1)[0]
        for g in range(self.evolutions):
            new_champ = self.toolbox.select(pop, 1)[0]
            if new_champ.fitness.values == old_champ.fitness.values:
                champ_counter += 1
                if champ_counter > 8:
                    break
            else:
                champ_counter = 0
                old_champ = new_champ
            # Select the next generation individuals
            for ind in pop:
                if not ind.fitness.values:
                    input("WTF")
            survival_count = int(self.pop_size * self.survpb)
            offspring = self.toolbox.select(pop, survival_count)
            # review = [i for i in offspring if self.toolbox.evaluate(i)[0] > 0.5]
            # for i in review:
            #     print(self.__decipher(i), self.toolbox.evaluate(i), i.fitness.values)
            for o in self.toolbox.select(offspring, 1):
                sys.stdout.write('\r')
                sys.stdout.flush()
                out_line = 'Cracking --> ' + self.__get_decipher(o) + ' | ' + str(round(o.fitness.values[0], 2))
                sys.stdout.write(out_line)
                sys.stdout.flush()

            # Clone the selected individuals
            next_gen = [self.toolbox.clone(offspring[i % survival_count]) for i in range(self.pop_size)]

            # # Apply crossover on the next_gen
            # for ind1, ind2 in zip(next_gen[::2], next_gen[1::2]):
            #     if random.random() < self.cxindpb:
            #         self.toolbox.mate(ind1, ind2)
            #         del ind1.fitness.values
            #         del ind2.fitness.values

            # Apply mutation on the next_gen
            for mutant in next_gen:
                if random.random() < self.mutindpb:
                    self.toolbox.mutate(mutant)
                    del mutant.fitness.values

            # Evaluate the individuals with an invalid fitness
            invalid_ind = [ind for ind in next_gen if not ind.fitness.valid]
            fit = self.toolbox.evaluate(invalid_ind)
            for i in range(len(invalid_ind)):
                invalid_ind[i].fitness.values = [fit[0][i][0]]

            pop[:] = next_gen
        winner = self.toolbox.select(pop, 1)[0]
        print('')
        return self.__get_key(winner), self.__get_decipher(winner)


import tkinter as tk
from tkinter import ttk
from tkcalendar import DateEntry
from tkinter import messagebox
from collections import deque
from tkinter import BooleanVar
import datetime
import sqlite3

# Connect to database
conn = sqlite3.connect('user.db')
conn.commit()
cursor = conn.cursor()

# Initialize the task list
tasks = deque()

# Function to add a new task
def add_task():
    global cursor
    task_name = entry_task_name.get()
    due_date = calendar_due_date.get() if due_date_checkbox.get() else ""
    category = entry_category.get() if entry_category.get() else ""
    reminder_date = calendar_reminder_date.get() if reminder_date_checkbox.get() else ""
    description = entry_description.get("1.0", tk.END).strip()
    is_complete = False
    created_date = datetime.date.today()  # Get the current date

    if task_name and category:
        # Check if the category already exists in the CATEGORY table
        cursor.execute("SELECT category_id FROM CATEGORY WHERE category_name=?", (category,))
        result = cursor.fetchone()

        if result:
            # If the category exists, retrieve its category_id
            category_id = result[0]
        else:
            # If the category doesn't exist, insert it into the CATEGORY table and retrieve its newly generated category_id
            cursor.execute("INSERT INTO CATEGORY (category_name) VALUES (?)", (category,))
            category_id = cursor.lastrowid

        if task_name:
            cursor.execute(
                "INSERT INTO TASK (task_name, due_date, description, status, created_date) "
                "VALUES (?, ?, ?, ?, ?)",
                (task_name, due_date, description, 'incomplete', created_date)
            )
            task_id = cursor.lastrowid

            cursor.execute(
                "INSERT INTO REMINDER (task_id, reminder_date) VALUES (?, ?)",
                (task_id, reminder_date)
            )

            conn.commit()
            tasks.append((task_name, is_complete, due_date, category, reminder_date, description))
            update_task_display(tasks)
            clear_entry_fields()
    else:
        messagebox.showerror("Invalid Task", "Please enter a task name.")

#function to retrieve the task_id from the database based on the task_name
def get_task_id_by_name(task_name):
    cursor.execute("SELECT task_id FROM TASK WHERE task_name=?", (task_name,))
    result = cursor.fetchone()
    if result:
        task_id = result[0]
        return task_id
    else:
        return None

# Function to delete a task
def delete_task():
    selected_items = task_treeview.selection()
    if selected_items:
        for item in selected_items:
            task_name = task_treeview.item(item)["text"]
            task_id = get_task_id_by_name(task_name)  # Get task_id using task_name

            task_treeview.delete(item)
            remove_task_by_name(task_name)

            # Delete task data from the "TASK" table
            cursor.execute("DELETE FROM TASK WHERE task_id=?", (task_id,))

            # Delete task data from the "reminder" table
            cursor.execute("DELETE FROM REMINDER WHERE task_id=?", (task_id,))

            # Delete task data from the "category" table
            cursor.execute("DELETE FROM CATEGORY WHERE task_id=?", (task_id,))

        conn.commit()
        messagebox.showinfo("Task Deleted", "Task has been deleted successfully.")
    else:
        messagebox.showerror("Invalid Selection", "Please select a task to delete.")

# Function to remove a task by its name from the tasks deque
def remove_task_by_name(task_name):
    index = -1
    for i, task in enumerate(tasks):
        if task[0] == task_name:
            index = i
            break
    if index != -1:
        tasks.remove(tasks[index])

# Function to edit a task
def edit_task():
    selected_item = task_treeview.selection()
    if selected_item:
        task_name = task_treeview.item(selected_item)["text"]
        task_index = get_task_index_by_name(task_name)
        selected_task = tasks[task_index]

        edit_window = tk.Toplevel(window)
        edit_window.title("Edit Task")

        label_edit_task_name = tk.Label(edit_window, text="Task Name:")
        label_edit_task_name.grid(row=0, column=0)
        entry_edit_task_name = tk.Entry(edit_window, width=30)
        entry_edit_task_name.insert(tk.END, selected_task[0])
        entry_edit_task_name.grid(row=0, column=1, columnspan=3)

        label_edit_complete = tk.Label(edit_window, text="Complete:")
        label_edit_complete.grid(row=0, column=2)
        complete_var = BooleanVar()
        complete_var.set(selected_task[1])
        checkbox_edit_complete = tk.Checkbutton(edit_window, variable=complete_var)
        checkbox_edit_complete.grid(row=0, column=3)

        label_edit_due_date = tk.Label(edit_window, text="Due Date:")
        label_edit_due_date.grid(row=1, column=0)
        calendar_edit_due_date = DateEntry(edit_window, date_pattern="yyyy-mm-dd")
        calendar_edit_due_date.set_date(selected_task[2] if selected_task[2] else None)
        calendar_edit_due_date.grid(row=1, column=1)

        label_edit_category = tk.Label(edit_window, text="Category:")
        label_edit_category.grid(row=1, column=2)
        entry_edit_category = tk.Entry(edit_window, width=15)
        entry_edit_category.insert(tk.END, selected_task[3])
        entry_edit_category.grid(row=1, column=3)

        label_edit_reminder_date = tk.Label(edit_window, text="Reminder Date:")
        label_edit_reminder_date.grid(row=2, column=0)
        calendar_edit_reminder_date = DateEntry(edit_window, date_pattern="yyyy-mm-dd")
        calendar_edit_reminder_date.set_date(selected_task[4] if selected_task[4] else None)
        calendar_edit_reminder_date.grid(row=2, column=1)

        label_edit_description = tk.Label(edit_window, text="Description:")
        label_edit_description.grid(row=3, column=0)
        entry_edit_description = tk.Text(edit_window, height=4, width=30)
        entry_edit_description.insert(tk.END, selected_task[5])
        entry_edit_description.grid(row=3, column=1, columnspan=3, padx=5, pady=5)

        def save_edited_task():
            edited_task_name = entry_edit_task_name.get()
            edited_is_complete = complete_var.get()
            edited_due_date = calendar_edit_due_date.get() if due_date_checkbox.get() else ""
            edited_category = entry_edit_category.get() if entry_edit_category.get() else ""
            edited_reminder_date = calendar_edit_reminder_date.get() if reminder_date_checkbox.get() else ""
            edited_description = entry_edit_description.get("1.0", tk.END).strip()

            if edited_task_name:
                # Update task data in the TASK table
                cursor.execute(
                    "UPDATE TASK SET task_name=?, status=?, due_date=?, description=? WHERE task_id=?",
                    (edited_task_name, edited_is_complete, edited_due_date, edited_description, selected_task[0]))

                # Update category data in the CATEGORY table
                cursor.execute("UPDATE CATEGORY SET category_name=? WHERE task_id=?",
                               (edited_category, selected_task[0]))

                # Update reminder data in the REMINDER table
                cursor.execute("UPDATE REMINDER SET reminder_date=? WHERE task_id=?",
                               (edited_reminder_date, selected_task[0]))

                conn.commit()

                if edited_task_name:
                    tasks[task_index] = (
                    edited_task_name,
                    edited_is_complete,
                    edited_due_date,
                    edited_category,
                    edited_reminder_date,
                    edited_description
                )
                update_task_display(tasks)
                edit_window.destroy()
            else:
                messagebox.showerror("Invalid Task", "Please enter a task name.")

        button_save_edited_task = tk.Button(edit_window, text="Save Task", command=save_edited_task)
        button_save_edited_task.grid(row=4, column=0, pady=5)

    else:
        messagebox.showerror("Invalid Selection", "Please select a task to edit.")

# Function to get the index of a task by its name in the tasks deque
def get_task_index_by_name(task_name):
    for i, task in enumerate(tasks):
        if task[0] == task_name:
            return i
    return -1

# Function to update the task list display
def update_task_display(task_list):
    task_treeview.delete(*task_treeview.get_children())
    for task in task_list:
        task_name = task[0]
        task_details = task[1:]
        task_treeview.insert("", tk.END, text=task_name, values=(task_details[0],) + task_details[1:])

# Function to clear the entry fields
def clear_entry_fields():
    entry_task_name.delete(0, tk.END)
    calendar_due_date.set_date(None)
    entry_category.delete(0, tk.END)
    calendar_reminder_date.set_date(None)
    entry_description.delete("1.0", tk.END)

# Function to search for tasks
def search_tasks():
    search_term = entry_search.get()
    if search_term:
        search_results = [task for task in tasks if search_term.lower() in task[0].lower()]
        update_task_display(search_results)
    else:
        update_task_display(tasks)

# Create the main window
window = tk.Tk()
window.title("Task Manager")

# Create and position the entry fields
label_task_name = tk.Label(window, text="Task Name:")
label_task_name.grid(row=0, column=0)
entry_task_name = tk.Entry(window, width=30)
entry_task_name.grid(row=0, column=1, padx=5)

label_due_date = tk.Label(window, text="Due Date:")
label_due_date.grid(row=0, column=2)
calendar_due_date = DateEntry(window, date_pattern="yyyy-mm-dd")
calendar_due_date.grid(row=0, column=3)

due_date_checkbox = tk.BooleanVar()
checkbox_due_date = tk.Checkbutton(
    window,
    variable=due_date_checkbox,
)
checkbox_due_date.grid(row=0, column=4)

label_category = tk.Label(window, text="Category:")
label_category.grid(row=1, column=0)
entry_category = tk.Entry(window, width=15)
entry_category.grid(row=1, column=1, padx=5)

label_reminder_date = tk.Label(window, text="Reminder Date:")
label_reminder_date.grid(row=1, column=2)
calendar_reminder_date = DateEntry(window, date_pattern="yyyy-mm-dd")
calendar_reminder_date.grid(row=1, column=3)

reminder_date_checkbox = tk.BooleanVar()
checkbox_reminder_date = tk.Checkbutton(
    window,
    variable=reminder_date_checkbox,
)
checkbox_reminder_date.grid(row=1, column=4)

label_description = tk.Label(window, text="Description:")
label_description.grid(row=2, column=0)
entry_description = tk.Text(window, height=4, width=30)
entry_description.grid(row=2, column=1, columnspan=3, padx=5, pady=5)

# Create and position the buttons
button_add_task = tk.Button(window, text="Add Task", command=add_task)
button_add_task.grid(row=3, column=0, pady=5)

button_delete_task = tk.Button(window, text="Delete Task", command=delete_task)
button_delete_task.grid(row=3, column=1, pady=5)

button_edit_task = tk.Button(window, text="Edit Task", command=edit_task)
button_edit_task.grid(row=3, column=2, pady=5)

# Create and position the search field and button
label_search = tk.Label(window, text="Search:")
label_search.grid(row=5, column=0)
entry_search = tk.Entry(window, width=30)
entry_search.grid(row=5, column=1, padx=5)

button_search = tk.Button(window, text="Search", command=search_tasks)
button_search.grid(row=5, column=2, padx=5)

task_treeview = ttk.Treeview(window)
# Fetch the data
query = "SELECT task_name, description, due_date, status FROM TASK"
cursor.execute(query)
results = cursor.fetchall()

# Display the data in the task_treeview widget
for row in results:
    column1_value, column2_value, column3_value, column4_value = row
    task_treeview.insert("", tk.END, text=column1_value, values=(column2_value, column3_value, column4_value))

# Function to pin/unpin a task
def update_pin_task():
    selected_item = task_treeview.selection()
    if selected_item:
        task_name = task_treeview.item(selected_item)["text"]
        task_index = get_task_index_by_name(task_name)
        selected_task = tasks[task_index]
        is_pinned = selected_task[6] if len(selected_task) >= 7 else False
        tasks[task_index] = selected_task[:6] + (not is_pinned,)
        update_task_display(tasks)

# Create and position the pin/unpin button
button_pin_task = tk.Button(window, text="Pin/Unpin Task", command=update_pin_task)
button_pin_task.grid(row=3, column=3, pady=5)

# Function to update the task list display
def update_task_display(task_list):
    task_treeview.delete(*task_treeview.get_children())
    for task in task_list:
        task_name = task[0]
        task_details = task[1:]
        is_pinned = task[6] if len(task) >= 7 else False
        pin_status = "Pinned" if is_pinned else "Unpinned"
        task_treeview.insert("", tk.END, text=task_name, values=(task_details[0],) + task_details[1:] + (pin_status,))

# Create and position the task list
task_treeview = ttk.Treeview(window)
task_treeview["columns"] = ("Complete", "Due Date", "Category", "Reminder Date", "Description", "Pin Status")

task_treeview.column("#0", width=150, minwidth=150, anchor=tk.W)
task_treeview.column("Complete", width=60, minwidth=60, anchor=tk.CENTER)
task_treeview.column("Due Date", width=100, minwidth=100, anchor=tk.CENTER)
task_treeview.column("Category", width=80, minwidth=80, anchor=tk.CENTER)
task_treeview.column("Reminder Date", width=100, minwidth=100, anchor=tk.CENTER)
task_treeview.column("Description", width=200, minwidth=200, anchor=tk.W)
task_treeview.column("Pin Status", width=80, minwidth=80, anchor=tk.CENTER)

task_treeview.heading("#0", text="Task Name", anchor=tk.W)
task_treeview.heading("Complete", text="Complete", anchor=tk.CENTER)
task_treeview.heading("Due Date", text="Due Date", anchor=tk.CENTER)
task_treeview.heading("Category", text="Category", anchor=tk.CENTER)
task_treeview.heading("Reminder Date", text="Reminder Date", anchor=tk.CENTER)
task_treeview.heading("Description", text="Description", anchor=tk.W)
task_treeview.heading("Pin Status", text="Pin Status", anchor=tk.CENTER)

task_treeview.grid(row=4, column=0, columnspan=5, padx=5, pady=5)

# Function to get the index of a task by its name in the tasks deque
def get_task_index_by_name(task_name):
    for i, task in enumerate(tasks):
        if task[0] == task_name:
            return i
    return -1

# Create and position the pin/unpin button
button_pin_task = tk.Button(window, text="Pin/Unpin Task", command=update_pin_task)
button_pin_task.grid(row=3, column=3, pady=5)

# Function to update the task list display
def update_task_display(task_list):
    task_treeview.delete(*task_treeview.get_children())
    for task in task_list:
        task_name = task[0]
        task_details = task[1:]
        task_treeview.insert("", tk.END, text=task_name, values=(task_details[0],) + task_details[1:])

# Function to clear the entry fields
def clear_entry_fields():
    entry_task_name.delete(0, tk.END)
    calendar_due_date.set_date(None)
    entry_category.delete(0, tk.END)
    calendar_reminder_date.set_date(None)
    entry_description.delete("1.0", tk.END)

# Create and position the pin/unpin button
button_pin_task = tk.Button(window, text="Pin/Unpin Task", command=update_pin_task)
button_pin_task.grid(row=3, column=3, pady=5)

# Function to save the data
def save_data():
    # Delete all existing data in the table
    cursor.execute('DELETE FROM TASK')

    # Insert the data from the Treeview into the table
    for item in task_treeview.get_children():
        values = task_treeview.item(item)['values']
        cursor.execute('INSERT INTO TASK VALUES (?, ?, ?, ?, ?, ?, ?)', values)

    # Commit the changes and close the connection
    conn.commit()

# Function to load the data
def load_data():
    # Clear the Treeview
    task_treeview.delete(*task_treeview.get_children())

    # Retrieve the data from the table with desired column order
    cursor.execute('SELECT task_name,status, due_date FROM TASK')
    data = cursor.fetchall()

    # Insert the data into the Treeview with the desired column order
    for item_data in data:
        # Extract the desired columns from the fetched data
        task_name=item_data[0]
        status = item_data[1]
        due_date = item_data[2]

        # Insert the extracted columns into the Treeview in the desired order
        task_treeview.insert("", tk.END, values=(task_name,status,due_date))

# Save the data before closing the application
window.protocol("WM_DELETE_WINDOW", save_data)

# Load the data when the application starts
load_data()

# Run the main loop
window.mainloop()

# Close the connection after the main loop
conn.close()


def read_input():
    with open('d1000000_sample_ts1_input.txt') as f:
        lines = f.readlines()

    cont = 0
    listas = []
    for i in range(int(lines[0].split("\n")[0])):
        listas.append([int(j) for j in lines[2+cont*2].split("\n")[0].split(" ")])
        cont += 1

    return listas

def read_input2():
    listas = []
    #q = input()
    for i in range(int(input())):
        _ = int(input())
        q = input().split("\n")[0]
        listas.append([int(x) for x in q.split(" ")])

    return listas

def write_output(text):
    with open('Test output.txt', 'w') as f:
        f.write(text)

if __name__ == '__main__':
    listas = read_input2()
    text = ""
    for case, lista in enumerate(listas):
        lista.sort()
        count = 1
        if lista[0] < 1:
            #Case #1:
            text = text + "Case #" + str(case+1) + ": 0\n"
            break
        for i in lista[1:]:
            if i > count:
                count += 1
        text = text + "Case #" + str(case+1) + ": " + str(count) + "\n"

    text = text[0:-1]
    print(text)


# -*- coding: utf-8 -*-
"""
Created on Sat May  9 15:14:41 2020

@author: IVAALEX
"""

from lxml import etree
from io import StringIO
import os
from XMLio import XMLio

class ValidationResult(object):
    def __init__(self, XMLvalid, errorText):
        self.XMLvalid = XMLvalid
        self.errorText = errorText
    def __str__(self):
        if self.XMLvalid:
            return "Check XML against XSD was successful"
        else:
            return "XML is not valid. Error below: \n " + self.errorText

class XMLValidator(object):

    def __init__(self):
        pass

    @classmethod
    def fromFilePath(cls,xsdSchemePath, xmlDocumentPath):
        """ os.path, os.path -> XMLValidator
            Creates XMLValidator object, which reads XSD and XML documents from
            path, provided in parameters
        """
        validator = cls()
        validator._readXMLandXSD(xsdSchemePath,xmlDocumentPath)
        return validator
    @classmethod
    def fromData(cls,xsdScheme, xmlDocument):
        """ StringIO, StringIO -> XMLValidator
            Creates XMLValidator object with assigned xsdScheme and xmlDocument
        """
        validator = cls()
        validator.setxsdScheme(xsdScheme)
        validator.setxmlDocument(xmlDocument)
        return validator

    def _readXMLandXSD(self, xsdSchemePath, xmlDocumentPath):
        """ os.path, os.path -> None
            Reads XML and XSD files to object variables self.xsdScheme and self.xmlDocument
        """
        try:

            xmlReader = XMLio()
            self.xsdScheme = xmlReader.readXML(xsdSchemePath)
            self.xmlDocument = xmlReader.readXML(xmlDocumentPath)

            # xsdScheme = open(xsdSchemePath,'r')
            # xmlDocument = open(xmlDocumentPath,'r')

            # self.xsdScheme = StringIO(xsdScheme.read())
            # self.xmlDocument = StringIO(xmlDocument.read())

            # xsdScheme.close()
            # xmlDocument.close()
        except Exception as e:
            print(e)

    def setxsdScheme(self, xsdScheme):
        self.xsdScheme = xsdScheme

    def setxmlDocument(self, xmlDocument):
        self.xmlDocument = xmlDocument

    def validateXML(self):
        """ None -> ValidationResult
            Check XML against XSD structure, and return ValidationResult object.
            It consists of two fields - XMLvalid and errorText
            If XML is valid, XMLvalid = true and errorText is empty
            Otherwise, XMLvalid = false, a errorText filled with error from exception
        """
        errorText = ""
        result = ValidationResult(False, errorText)
        try:
            xmlschema_doc = etree.parse(self.xsdScheme)
            xmlschema = etree.XMLSchema(xmlschema_doc)
            doc = etree.parse(self.xmlDocument)
            #XMLvalid = xmlschema.validate(doc)
            xmlschema.assertValid(doc)
            result = ValidationResult(True, errorText)
        except Exception as e:
            result = ValidationResult(False, str(e))
        finally:
            return result

if __name__ == '__main__':

    # f = StringIO('''<?xml version="1.0" encoding="UTF-8"?>
    # <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    #  	<xsd:element name="Файл" type="AType"/>
    #  	<xsd:complexType name="AType">
    # 		<xsd:sequence>
    #  			<xsd:element name="b" type="xsd:string" />
    # 		</xsd:sequence>
    #  	</xsd:complexType>
    # </xsd:schema>
    # ''')

    # xmlschema_doc = etree.parse(f)
    # xmlschema = etree.XMLSchema(xmlschema_doc)

    # valid = StringIO('''
    # <Файл>
    #  	<b/>
    # </Файл>
    # ''')
    # doc = etree.parse(valid)
    # print(xmlschema.validate(doc))
    # try:
    #     print(xmlschema.assertValid(doc))
    # except Exception as e:
    #     print(e)
    # validator = XMLValidator.fromData(f,valid)
    # print(validator.validateXML())

    script_dir = os.path.dirname(__file__)

    xsd_path = "Docs/scheme1.xsd"
    abs_xsd_path = os.path.join(script_dir, xsd_path)

    xml_path = "Docs/УПД_GMPT.xml"
    abs_xml_path = os.path.join(script_dir, xml_path )

    validator2 = XMLValidator.fromFilePath(xsd_path, xml_path)
    result = validator2.validateXML()
    print(result)


from Task1.car import car
from Task1.auto import Auto

def main():
    car1 = car("Ferrari", 320, 100)
    car2 = car("Bentley", 280, 2500)
    car3 = car("Lada Calina", 130, 10000)
    print(car1, ", ",  car2, ", ", car3)
    auto1 = Auto("red", 154, "ferrari")
    auto2 = Auto("green", 232, "bmw")

    print(auto2.get_color())
    auto2.set_color("black")
    print(auto2.get_color())
    print(auto2)
    auto1.increase_speed(76)
    print(auto1)

main()


import pandas as pd
import numpy as np
import seaborn as sns
import pandas_utils

stations = pd.read_csv('../data/stations_10_12_to_11_13.csv',
                       index_col=0)

trips = pd.read_csv('../data/hubwaydata_10_12_to_11_13.csv',
                    index_col=0,
                    parse_dates=['start_date', 'end_date'])

# sigma.js requres the node IDs to be strings.
stations = stations.set_index(stations.index.map(str))
trips.start_station = trips.start_station.map(str)
trips.end_station = trips.end_station.map(str)

def get_trips_for_date(d):
    start_trips = trips[trips.start_date == d].start_station
    end_trips = trips[trips.end_date == d].end_station
    return (list(start_trips),
            list(end_trips))

start_date = sorted(trips.start_date)[0]
end_date = sorted(trips.end_date)[-1]
timerange = pd.date_range(start_date, end_date, freq='min')

starting_ending = timerange.map(get_trips_for_date)

# separate into two lists
starting, ending = zip(*starting_ending)

res = pd.DataFrame({'date': timerange,
					'starting': starting,
					'ending': ending})

res.to_csv('trip_timeseries.csv')


import urllib.error
import urllib.request

cores_fundo = {
    'limpa': '\033[m',
    'preto_branco': '\033[1;40m'
}

cabecalho = ' SITE ESTÁ ACESSÍVEL? '
print('+', '-' * len(cabecalho), '+')
print(f"| {cores_fundo['preto_branco']}{cabecalho}{cores_fundo['limpa']} |")
print('+', '-' * len(cabecalho), '+', '\n')

try:
    response = urllib.request.urlopen('http://www.pudim.com.br')
except urllib.error.URLError:
    print("O site Pudim não está acessível no momento.")
else:
    print("Consegui acessar o site com sucesso!")

# import requests
#
# try:
#     response = requests.get('http://pudim.com.br/')
# except requests.ConnectionError:
#     print("O site Pudim não está acessível no momento.")
# else:
#     print("Consegui acessar o site com sucesso!")

# import urllib.error
# import urllib.request
#
# url = input('Digite ou cole o link: ')
#
# try:
#     response = urllib.request.urlopen(url)
# except urllib.error.URLError:
#     print("O site Pudim não está acessível no momento.")
# else:
#     print("Consegui acessar o site com sucesso!")


"""
YARA rules Scanner class definitions

[mjdorma@gmail.com]
"""

from __future__ import print_function
import sys
import os
import traceback
from glob import glob
from threading import Thread
from threading import Lock as ThreadLock
from threading import Event as ThreadEvent
if sys.version_info[0] < 3: #major
    from Queue import Queue as ThreadQueue
    from Queue import Empty as ThreadEmpty
else:
    from queue import Queue as ThreadQueue
    from queue import Empty as ThreadEmpty
from multiprocessing import Process
from multiprocessing import Lock as ProcessLock
from multiprocessing import Value
from multiprocessing import Event as ProcessEvent
from multiprocessing.queues import JoinableQueue as ProcessQueue
from multiprocessing.queues import Empty as ProcessEmpty

import time
import atexit

import yara

EXECUTE_THREAD = 0
EXECUTE_PROCESS = 1
DEFAULT_EXECUTE_POOL = 4
DEFAULT_EXECUTE_TYPE = EXECUTE_THREAD
DEFAULT_STREAM_CHUNK_SIZE = 2**20
DEFAULT_STREAM_READAHEAD_LIMIT = 2**32
DEFAULT_STREAM_CHUNK_OVERLAP = 1

class Counter(object):
    def __init__(self, Lock, init=0):
        self._value = Value('i', init)
        self.lock = Lock()

    def inc(self):
        with self.lock:
            self._value.value += 1

    @property
    def value(self):
        return self._value.value

class Scanner(object):
    enqueuer = None

    def __init__(self, rules=None,
                       execute_type=DEFAULT_EXECUTE_TYPE,
                       execute_pool=DEFAULT_EXECUTE_POOL,
                       stream_chunk_size=DEFAULT_STREAM_CHUNK_SIZE,
                       stream_chunk_overlap=DEFAULT_STREAM_CHUNK_OVERLAP,
                       stream_readahead_limit=DEFAULT_STREAM_READAHEAD_LIMIT,
                       stream_chunk_read_max=None,
                       **kwargs):
        """Scanner - base Scanner class

        rules - rules object we'll be using to match against

        optional kwargs:
            execute_type - type of execution pool
            execute_pool - number of Threads or Process to utilise for pooled
                           execution
            stream_chunk_size - size in bytes to read from a stream
            stream_chunk_overlap - percentage read overlap per chunk
            steram_readahead_limit - size in bytes limit for stream read ahead
            stream_chunk_read_max - max number of chunks to read from a stream

        Note:
            Define an enqueuer function if the enqueue operation will take
            a long time.  This function is executed asynchronously
        """
        if execute_type == EXECUTE_THREAD:
            self.Queue = ThreadQueue
            self.Event = ThreadEvent
            self.Empty = ThreadEmpty
            self.Lock = ThreadLock
            self.Execute = Thread
        else:
            self.Queue = ProcessQueue
            self.Event = ProcessEvent
            self.Empty = ProcessEmpty
            self.Lock = ProcessLock
            self.Execute = Process
        self._execute_type = execute_type

        if rules is None:
            raise ValueError("No rules object passed into __init__")
        self._rules = rules
        self._chunk_size = stream_chunk_size
        self._chunk_overlap = int((stream_chunk_size * \
                                  stream_chunk_overlap) / 100)
        self._stream_chunk_read_max = stream_chunk_read_max
        self._max_sq_size = int((stream_readahead_limit / \
                             (stream_chunk_size + self._chunk_overlap)) + 1)
        self._jq = self.Queue()
        self._rq = self.Queue()
        self._empty = self.Event()
        self._pool = []
        self._scanned = Counter(self.Lock)
        self._matches = Counter(self.Lock)
        self._errors = Counter(self.Lock)
        self.quit = self.Event()
        atexit.register(self.quit.set)

        if self.enqueuer is not None:
            t = Thread(target=self._enqueuer)
            t.daemon = True
            t.start()

        for i in range(execute_pool):
            t = self.Execute(target=self._run)
            t.daemon = True
            self._pool.append(t)

        for p in self._pool:
            p.start()

    @property
    def scanned(self):
        return self._scanned.value

    @property
    def matches(self):
        return self._matches.value

    @property
    def errors(self):
        return self._errors.value

    @property
    def sq_size(self):
        """contains the current scan queue size"""
        if self._execute_type == EXECUTE_THREAD:
            return self._jq.unfinished_tasks
        else:
            return self._jq.qsize()

    @property
    def rq_size(self):
        """contains the current result queue size"""
        if self._execute_type == EXECUTE_THREAD:
            return self._rq.unfinished_tasks
        else:
            return self._rq.qsize()

    def enqueue_path(self, tag, filepath, **match_kwargs):
        self._jq.put(("match_path", tag, (filepath,), match_kwargs))

    def enqueue_data(self, tag, data, **match_kwargs):
        self._jq.put(("match_data", tag, (data,), match_kwargs))

    def enqueue_proc(self, tag, pid, **match_kwargs):
        self._jq.put(("match_proc", tag, (pid,), match_kwargs))

    def enqueue_stream(self, stream, basetag='stream'):
        data = stream.read(self._chunk_size + self._chunk_overlap)
        read_bytes = self._chunk_size - self._chunk_overlap
        read_max = self._stream_chunk_read_max
        chunk_id = 0
        chunk_start = 0
        while data and not self.quit.is_set():
            chunk_end = chunk_start + len(data)
            tag = "%s[%s:%s]" % (basetag, chunk_start, chunk_end)
            self.enqueue_data(tag, data)
            if read_max is not None:
                read_max =- 1
                if read_max <= 0:
                    break
            while self.sq_size > self._max_sq_size and \
                        not self.quit.is_set():
                time.sleep(0.1)
            if self._chunk_overlap > 0:
                overlap = data[-1 * self._chunk_overlap:]
                data = stream.read(self._chunk_size)
                if not data:
                    break
                data = overlap + data
            else:
                data = stream.read(self._chunk_size)
            chunk_id += 1
            chunk_start = (chunk_id * self._chunk_size) - self._chunk_overlap

    def enqueue_end(self):
        """queue the exit condition.  Threads will complete once
        they have exhausted the queues up to queue end"""
        self._jq.put(None)

    def _enqueuer(self):
        try:
            self.enqueuer()
            self.enqueue_end()
        except:
            # abort current operations
            self.quit.set()
            self._rq.put(None)
            print("Error in enqueuer: %s" % traceback.format_exc(),
                    file=sys.stderr)

    def _run(self):
        try:
            while not self._empty.is_set() and not self.quit.is_set():
                try:
                    job = self._jq.get(timeout=0.1)
                except self.Empty:
                    continue
                if job is None:
                    self._empty.set()
                    self._rq.put(None)
                    self._jq.task_done()
                    break
                try:
                    self._scanned.inc()
                    f, t, a, k = job
                    f = getattr(self._rules, f)
                    r = f(*a, **k)
                    if r:
                        self._matches.inc()
                except Exception:
                    self._errors.inc()
                    r = traceback.format_exc()
                finally:
                    self._rq.put((t, r))
                    self._jq.task_done()
        except Exception:
            print(traceback.format_exc(), file=sys.__stderr__)

    def join(self, timeout=None):
        for t in self._pool:
            t.join(timeout=timeout)

    def is_alive(self):
        for t in self._pool:
            if t.is_alive():
                return True
        return False

    def dequeue(self, timeout=None):
        r = self._rq.get(timeout=timeout)
        self._rq.task_done()
        return r

    def __iter__(self):
        while True:
            r = self.dequeue()
            if r is None:
                # Wait until the workers are complete then purge the queue
                self.join()
                for i in range(self._rq.qsize()):
                    yield self.dequeue()
                break
            yield r

class PathScanner(Scanner):
    def __init__(self, args=[], recurse_dirs=False,
                filesize_gt=None, filesize_lt=None,
                path_end_include=None, path_end_exclude=None,
                path_contains_include=None, path_contains_exclude=None,
                **scanner_kwargs):
        """Enqueue paths for scanning

        args - a list of glob'able paths to be scanned

        optional kwargs:
            recurse_dirs - walk down all directories
            filesize_gt - exclude files which are greater than
            filesize_lt - exclude files which are less than
            path_end_include - include paths that end with
            path_end_exclude - exclude paths that end with
            path_contains_include - include paths that contain
            path_contains_exclude - exclude paths that contain

        scanner_kwargs - see Scanner definition for options

        """
        self._paths = []
        for path in args:
            paths = glob(path)
            if not paths:
                raise ValueError("Error reading path '%s'" % path)
            self._paths.extend(paths)
        self._recurse_dirs = recurse_dirs
        self._filesize_gt=filesize_gt
        self._filesize_lt=filesize_lt
        self._path_end_include = path_end_include
        self._path_end_exclude = path_end_exclude
        self._path_contains_include = path_contains_include
        self._path_contains_exclude = path_contains_exclude
        Scanner.__init__(self, **scanner_kwargs)

    def enqueuer(self):
        for path in self.paths:
            self.enqueue_path(path, path)

    def exclude_path(self, path):
        def do_test(pathtest, tests):
            return bool([a for a in filter(lambda test:pathtest(test), tests)])
        if self._filesize_gt is not None:
            filesize = os.path.getsize(path)
            if filesize > self._filesize_gt:
                return True
        if self._filesize_lt is not None:
            filesize = os.path.getsize(path)
            if filesize < self._filesize_lt:
                return True
        if self._path_contains_exclude is not None:
            if do_test(path.__contains__, self._path_contains_exclude):
                return True
        if self._path_end_exclude is not None:
            if do_test(path.endswith, self._path_end_exclude):
                return True
        exclude_on_not_include = False
        if self._path_contains_include is not None:
            if do_test(path.__contains__, self._path_contains_include):
                return False
            else:
                exclude_on_not_include = True
        if self._path_end_include is not None:
            if do_test(path.endswith, self._path_end_include):
                return False
            else:
                exclude_on_not_include = True
        return False or exclude_on_not_include

    @property
    def paths(self):
        if self._recurse_dirs == True:
            listdir = os.walk
        else:
            def listdir(d):
                ls = [(f, os.path.join(d, f)) for f in os.listdir(d)]
                filenames = [f for f, _ in \
                                filter(lambda o: not os.path.isdir(o[1]), ls) ]
                return [(d, [] , filenames)]
        for p in self._paths:
            if self.quit.is_set():
               return
            if os.path.isdir(p):
                for dirpath, dirnames, filenames in listdir(p):
                    for filename in filenames:
                        if self.quit.is_set():
                            return
                        a = os.path.join(dirpath, filename)
                        if self.exclude_path(a):
                            continue
                        yield a
            else:
                if self.exclude_path(p):
                    continue
                yield p

class PidScanner(Scanner):
    def __init__(self, args=[], **scanner_kwargs):
        """Enqueue pids for scanning

        args - list of process ids to scan

        scanner_kwargs - see Scanner definition for options
        """
        pids = []
        for pid in args:
            try:
                if type(pid) is not int:
                    pid = int(pid)
                pids.append(pid)
            except ValueError:
                raise ValueError("PID %s was not an int" % (pid))
        self._pids = pids
        Scanner.__init__(self, **scanner_kwargs)

    def enqueuer(self):
        for pid in self._pids:
            self.enqueue_proc("%s"%pid, pid)

class FileChunkScanner(PathScanner):
    """Enqueue chunks of data from paths"""
    def enqueuer(self):
        for path in self.paths:
            try:
                with open(path, 'rb') as f:
                    self.enqueue_stream(f, basetag=path)
            except Exception as exc:
                print("Failed to enqueue %s - %s" % (path,
                                traceback.format_exc()),
                            file=sys.stderr)

class StdinScanner(Scanner):
    """Enqueue chunks of data from 'stream'"""
    def enqueuer(self):
        try:
            self.enqueue_stream(sys.stdin)
        except Exception as exc:
            print("Error reading stream - %s" % (exc), file=sys.stderr)

class SyncScanner(Scanner):
    def __init__(self, **scanner_kwargs):
        """Synchronised matching - Use the Scanner's scan pool to process
        match jobs synchronously

        scanner_kwargs - see Scanner definition for options
        """
        self._scan_id = 0
        self._new_results = Event()
        self.enqueuer = self.dequeuer # dequeuing thread
        self.results = {}
        Scanner.__init__(self, **scanner_kwargs)

    def dequeuer(self):
        try:
            while not self.quit.is_set():
                self._new_results.clear()
                ret = self.dequeue()
                if ret is None:
                    break
                scan_id, res = ret
                self.results[scan_id] = res
                self._new_results.set()
        finally:
            self._new_results.set()

    def _sync_scan(self, enqueue_fnc, args, match_kwargs):
        results = {}
        scan_ids = []
        for arg in args:
            self._scan_id += 1
            scan_id = self._scan_id
            scan_ids.append(scan_id)
            results[scan_id] = None
            a = (scan_id, arg)
            enqueue_fnc(*a, **match_kwargs)

        while not self.quit.is_set():
            for scan_id in scan_ids:
                if scan_id in self.results:
                    results[scan_id] = self.results.pop(scan_id)
                    if len(results) == len(scan_ids):
                        return [results[i] for i in scan_ids]
            while not self._new_results.wait(timeout=1):
                pass

    def match_paths(self, path_list, **match_kwargs):
        return self._sync_scan(self.enqueue_path, path_list, match_kwargs)

    def match_procs(self, pid_list, **match_kwargs):
        return self._sync_scan(self.enqueue_proc, pid_list, match_kwargs)

    def match_data(self, data_list, **match_kwargs):
        return self._sync_scan(self.enqueue_data, data_list, match_kwargs)


# 주유소
# https://www.acmicpc.net/problem/13305

n = int(input())
distances = list(map(int, input().split()))
oil_prices = list(map(int, input().split()))

# 첫 번째 도시에서 두 번째 도시까지 가기위한 비용
answer = oil_prices[0] * distances[0]

# 현재 까지 중 가장 싼 값의 기름 비용
oil_cost = oil_prices[0]
for i in range(1,n-1):
    # 만약 i번째 도시의 기름값이 지금까지 주유하던 비용보다 싸면 이제부터 i번째 도시에서 주유
    if oil_cost > oil_prices[i]:
        oil_cost = oil_prices[i]
    answer += distances[i] * oil_cost

print(answer)