# 영재가 몇 개의 상자를 실을 수 있는지 return

from collections import deque

def solution(order):
    container = deque([i + 1 for i in range(len(order))])
    sub_container = []
    cur = 0

    while container:  # 1. 주 컨테이너에서 담을 수 있는 박스의 개수 찾기
        if order[cur] != container[0]:   # 원하는 상자와 주 컨테이너에서 꺼낼 수 있는 상자가 다른 경우
            if sub_container and order[cur] == sub_container[-1]:   # 보조 컨테이너가 비어 있지 않다면 마지막 원소와 같은지 검사
                cur += 1         # 같다면 다음 원하는 상자로 이동
                sub_container.pop()  # 보조 컨테이너에서 제거
            else:                    # 보조 컨테이너가 비어 있는 경우
                sub_container.append(container.popleft())   # 주 컨테이너에서 꺼내서 추가
        else:           # 원하는 상자와 주 컨테이너에서 꺼낼 수 있는 상자가 같다면 바로 꺼내기
            cur += 1
            container.popleft()

    while sub_container:   # 2. 보조 컨테이너에서 담을 수 있는 박스의 개수 찾기
        if order[cur] == sub_container[-1]:  # 보조 컨테이너서 꺼낼 수 있는 박스와 원하는 박스가 같은 경우
            cur += 1
            sub_container.pop()
        else:            # 바로 꺼낼 수 없다면 더이상 진행 불가능
            break
    return cur

print(solution([4, 3, 1, 2, 5]))


import sys
from random import randint
from math import sqrt, ceil
from fractions import gcd
import decimal

decimal.getcontext().prec = 1000

n = int(sys.argv[1])

#first step

def rndnum(n):
	while True:
		#pick a random number a less than n
		#optional to use ceil(sqrt(n)) instead of n-1
		a = randint(3,ceil(sqrt(n)))

		#is a a factor of n?
		if(n%a==0):
			print(a)
			print(n/a)
			print("Done!!!")
			sys.exit()

		#check a is coprime with n
		if (gcd(a, n) == 1):
			break
		else:
			print(gcd(a, n))
			print(n/(gcd(a, n)))
			print("Done!!!")
			sys.exit()
	return(a)

#step 2 (the most annoying) (needs quantum computing) :(
while True:
	a = rndnum(n)
	print("a = " +str(a))

	#find the period
	b = a**2
	for i in range(2,n):
		b = (b*a)%n
		if (b==1):
			r = i
			break

	print("r = " + str(r))
	#check if its a good r
	if ((i/2)%2==0 and (decimal.Decimal((decimal.Decimal(a)**(decimal.Decimal(r/2)))+1))%decimal.Decimal(n)!=decimal.Decimal(0)):
		break

#step 3
print(int(gcd((a**(r/2)-1),n)))
print(int(gcd((a**(r/2)+1),n)))
print("Done!!!")


import os
import shutil
import imghdr
import mimetypes
import errno
import stat

def handle_remove_readonly(func, path, exc):
    excvalue = exc[1]
    if func in (os.rmdir, os.remove) and excvalue.errno == errno.EACCES:
        os.chmod(path, stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO)  # 0777
        func(path)
    else:
        raise

def organize_files():
    current_directory = os.getcwd()  # Mevcut çalışma dizinini alır
    image_folder = os.path.join(current_directory, "Resimler")  # Resimleri taşıyacak klasörün yolu
    video_folder = os.path.join(current_directory, "Videolar")  # Videoları taşıyacak klasörün yolu
    other_folder = os.path.join(current_directory, "DigerDosyalar")  # Diğer dosyaları taşıyacak klasörün yolu

    # Klasörlerin oluşturulması
    if not os.path.exists(image_folder):
        os.makedirs(image_folder)
    if not os.path.exists(video_folder):
        os.makedirs(video_folder)
    if not os.path.exists(other_folder):
        os.makedirs(other_folder)

    # Tüm dosya ve klasörlerin taraması
    for root, dirs, files in os.walk(current_directory):
        for filename in files:
            if filename == "FolderOrganizer_Python.py":
                continue  # FolderOrganizer_Python.py dosyasını taşıma, diğer dosyalara devam et
            file_path = os.path.join(root, filename)
            if os.path.exists(file_path):  # Dosyanın varlığını kontrol et
                file_type, _ = mimetypes.guess_type(file_path)
                if file_type is not None:
                    file_type = file_type.split('/')[0]  # Dosya türünün ilk bölümünü alır (örneğin, 'image' veya 'video')
                    if file_type == 'image':
                        shutil.move(file_path, os.path.join(image_folder, filename))  # Resim dosyalarını taşır
                    elif file_type == 'video':
                        shutil.move(file_path, os.path.join(video_folder, filename))  # Video dosyalarını taşır
                    else:
                        destination_path = os.path.join(other_folder, filename)
                        if file_path != destination_path:  # Aynı dosyanın kendisini taşımamak için kontrol et
                            shutil.copy2(file_path, destination_path)  # Diğer dosyaları kopyalar
                            os.remove(file_path)  # Orijinal dosyayı siler
                else:
                    file_type = imghdr.what(file_path)
                    if file_type is not None:
                        shutil.move(file_path, os.path.join(image_folder, filename))  # Resim dosyalarını taşır
                    else:
                        destination_path = os.path.join(other_folder, filename)
                        if file_path != destination_path:  # Aynı dosyanın kendisini taşımamak için kontrol et
                            shutil.copy2(file_path, destination_path)  # Diğer dosyaları kopyalar
                            os.remove(file_path)  # Orijinal dosyayı siler

    # Boş klasörleri silme
    for root, dirs, files in os.walk(current_directory, topdown=False):
        for dir_name in dirs:
            folder_path = os.path.join(root, dir_name)
            if not os.listdir(folder_path):
                shutil.rmtree(folder_path, onerror=handle_remove_readonly)  # Klasörü ve içeriğini siler

    # İç içe geçmiş boş klasörleri silme
    for root, dirs, files in os.walk(current_directory, topdown=True):
        for dir_name in dirs:
            folder_path = os.path.join(root, dir_name)
            if not os.listdir(folder_path):
                try:
                    os.rmdir(folder_path)  # Klasörü siler
                except OSError as e:
                    handle_remove_readonly(os.rmdir,folder_path,(None,e))

    print("Dosyalar başarıyla taşındı ve boş klasörler silindi!")

organize_files()


from django.shortcuts import redirect, get_object_or_404

from django.core.exceptions import PermissionDenied

from utilities import *

from bugs.models import Bug

def assign_bug(request, bug_id, user_id):
    user_profile = get_user_profile(request.user)
    desgination = user_profile.designation
    if request.user.id != user_id or desgination != DEVELOPER:
        raise PermissionDenied()

    bug = get_object_or_404(Bug, uuid=bug_id)
    if not bug.assigned_to:
        bug.assigned_to = user_profile
        bug.save()
    else:
        raise PermissionDenied()
    return redirect('detail-bug', pk=bug.uuid)


# https://www.facebookrecruiting.com/portal/coding_practice_question/?problem_id=510655302929581&ppid=454615229006519&practice_plan=0
# Time Complexity: O(n*log(n)), Space complexity: O(k)
import functools
import heapq
from typing import Optional, List

class Solution:
    def findMaxProduct(self, arr: Optional[List]) -> Optional[List]:
        heap, n, k = [], 0, 3
        for i, num in enumerate(arr):
            if n < k:
                heapq.heappush(heap, num)
                n += 1
                arr[i] = -1 if n < k else functools.reduce(lambda a, b: a * b, heap)
            else:
                heapq.heappushpop(heap, num)
                arr[i] = functools.reduce(lambda a, b: a * b, heap)

        return arr

if __name__ == "__main__":
    sol = Solution()
    print(sol.findMaxProduct(arr=[1, 2, 3, 4, 5]))  # [-1, -1, 6, 24, 60]


import discord
import os
import json
import random
from math import ceil

client = discord.Client()

# questions:
# [question1, question2...]

# topics:
# topic: [id1, id2]...

# already_asked:
# [id1, id2]

directory = os.path.dirname(__file__)

ask_command = "!askme"
datafile = directory + "\\data.json"

help_message = f"""
- {ask_command}: asks random question
- {ask_command}: [topic]: asks random question about topic
- {ask_command}: add [question]: adds question to list
- {ask_command}: list [page]: lists questions on page x
- {ask_command}: topics: lists available topics
- {ask_command}: topic [topic]: lists question about topic
- {ask_command}: set [question_id] [topic]: sets question to topic
- {ask_command}: unset [question_id] [topic]: unsets question from topic
"""

class Questions:
    def __init__(self, datafile):
        self.datafile = datafile
        self.questions = []
        self.topics = {}
        data = self.load_data(datafile)
        if data:
            self.questions = data['questions']
            self.topics = {topic: list(map(int, indexes)) for topic, indexes in data['topics'].items()}
        self.free = [i for i in range(len(self.questions))]  # id of questions that were not used

    def load_data(self, datafile):
        try:
            with open(datafile, 'r', encoding="utf-8") as f:
                return json.load(f)
        except Exception as error:
            if os.path.exists(datafile):
                print("Can't open data.json file")
                raise error
            else:
                return None

    def get_question(self, topic=None, question_id=None):
        if len(self.free) == 0:
            print("No more questions! Resetting the queue")
            self.free = [i for i in range(len(self.questions))]
        if question_id is not None:
            if 1 <= question_id <= len(self.questions):
                return self.questions[question_id - 1]
            return f"Invalid id, range from 1 to {len(self.questions)}"
        if topic:
            if topic in self.topics:
                random_question_id = int(random.choice(self.topics[topic])) - 1
                try:
                    self.free.remove(random_question_id)
                except ValueError:  # it can happen if someone firstly pull question that is in topic list
                    pass
                return self.questions[random_question_id]
            else:
                print('No topic in the topic database:', topic)
                return "No " + topic + " in the topic database!"
        else:
            random_question_id = random.choice(self.free)
            self.free.remove(random_question_id)
            return self.questions[random_question_id]

    def update_data(self):
        data = {
            "questions": self.questions,
            "topics": self.topics
        }
        with open(self.datafile, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, ensure_ascii=False)

    def add_question(self, question, topic=None):
        if question:
            self.questions.append(question)
            question_id = len(self.questions)
            if topic in self.topics:
                self.topics[topic].append(question_id)
            self.free.append(question_id-1)
            self.update_data()
            return "Added question with id " + str(question_id)
        return "No question given!"

    def remove_question(self, remove_id):
        if 0 < remove_id and remove_id <= len(self.questions):
            self.questions.pop(remove_id-1)
            # indexes are rearranged so clean up dirt
            index_to_del = len(self.questions) + 1
            if index_to_del in self.free:
                self.free.remove(index_to_del)
            for topic, questions_ids in self.topics.items():
                if remove_id in questions_ids:
                    self.topics[topic].remove(remove_id)
                # deincrement each question with id above id to remove
                for index, question_id in enumerate(questions_ids):
                    if question_id > remove_id:
                        self.topics[topic][index] -= 1
            self.update_data()
            return "Successfully removed question " + str(remove_id)
        else:
            return "Bad id to remove: " + str(remove_id)

    def get_questions(self, page, amount_on_page=10):
        start = (page-1)*amount_on_page
        stop = page*amount_on_page
        questions_list = self.questions[start:stop]
        if len(questions_list) == 0:
            return ["No questions on this page!"]
        return questions_list

    def get_questions_amount(self):
        return len(self.questions)

    def get_topic_list(self):
        return [topic for topic in self.topics]

    def get_topic_questions(self, topic):
        if topic in self.topics:
            return [self.questions[q-1] for q in self.topics[topic]]
        else:
            return ["Topic not in the database!"]

    def set_topic(self, question_id, topic):
        if topic in self.topics:
            if question_id in self.topics[topic]:
                return "Question already in the topic list!"
            if question_id < 1 or question_id > len(self.questions):
                return f"Invalid question id, choose between 1 and {len(self.questions)}"
            self.topics[topic].append(question_id)
            self.update_data()
            return f"Question {question_id} set to the topic {topic}"
        else:
            self.topics[topic] = [question_id]
            self.update_data()
            return f"Added topic {topic} and set {question_id} there"

    def unset_topic(self, question_id, topic):
        if topic in self.topics:
            if question_id in self.topics[topic]:
                self.topics[topic].remove(question_id)
                self.update_data()
                return f"Question {question_id} removed from the topic {topic}"
            return "Question not in the topic list!"
        else:
            return "Topic not in the database!"

@client.event
async def on_ready():
    print('Logged in as {0.user}'.format(client))

@client.event
async def on_message(message):
    if message.author == client.user:
        return

    if message.content.startswith(ask_command):
        context = message.content.split(ask_command)[1].strip()
        if context.startswith("add"):  # add question
            await add_question(message, context)
        elif context.startswith("remove"):
            await remove_question(message, context)
        elif context.startswith("id"):  # select question by id
            await select_by_id(message, context)
        elif context.startswith("list"):  # get list of questions
            await list_questions(message, context)
        elif context.startswith("topics"):
            await print_topics(message, context)
        elif context.startswith("topic"):
            await print_topic(message, context)
        elif context.startswith("set"):
            await set_topic(message, context)
        elif context.startswith("unset"):
            await unset_topic(message, context)
        elif context.startswith("help"):
            await help(message, context)
        else:  # return question (by topic if given)
            await return_question(message, context)

async def add_question(message, context):
    question = context.split("add", maxsplit=1)[-1].strip()
    result = q.add_question(question)
    await message.channel.send(result)

async def remove_question(message, context):
    question_id = int(context.split("remove")[-1].strip())
    result = q.remove_question(question_id)
    await message.channel.send(result)

async def select_by_id(message, context):
    question_id = int(context.split("id")[-1].strip())
    result = q.get_question(question_id=question_id)
    await message.channel.send(result)

async def list_questions(message, context):
    amount = 10
    page = context.split("list")[-1].strip()
    if page:
        page = int(page)
        result = q.get_questions(page, amount)
        questions_list = ""
        start = (page-1)*amount+1
        for i, question in enumerate(result, start=start):
            questions_list += f"{i}. {question}\n"
        await message.channel.send(questions_list)
    else:
        await message.channel.send(f"Choose page from 1 to {ceil(q.get_questions_amount()/amount)}")

async def return_question(message, context):
    topic = message.content.split(ask_command)[-1].strip()
    question = q.get_question(topic)
    await message.channel.send(question)

async def print_topics(message, context):
    topics_list = q.get_topic_list()
    topics_str = "\n".join(["- " + topic for topic in topics_list])
    await message.channel.send(topics_str)

async def print_topic(message, context):
    topic = context.split("topic")[-1].strip()
    topic_questions = q.get_topic_questions(topic)
    questions_str = "\n".join([str(i) + ". " + q for i, q in enumerate(topic_questions, start=1)])
    await message.channel.send(questions_str)

async def set_topic(message, context):
    args = context.split("set")[-1].split()
    question_id = int(args[0])
    topic = args[1]
    result = q.set_topic(question_id, topic)
    await message.channel.send(result)

async def unset_topic(message, context):
    args = context.split("set")[-1].split()
    question_id = int(args[0])
    topic = args[1]
    result = q.unset_topic(question_id, topic)
    await message.channel.send(result)

async def help(message, context):
    await message.channel.send(help_message)

if __name__ == "__main__":
    q = Questions(datafile)
    client.run(os.environ['TOKEN'])

# TODO:
# - add questions by chat ✔
# - handle questions ids better ✔
# - list questions ✔
# - remove questions by chat ✔
# - manage topics ✔
# - add help ✔
# - check indexes when setting question id to topic ✔
# - ensure indexes is passed as int to topics variable ✔
# - modify database to run simultaneously on many servers at once
# - list questions by topic in one function (?)
# - handle free questions in better way (?) // what about theme questions
# - add multiple questions by chat at once

# KNOWN BUGS:
# - if keyword add in add question message, content is eaten (if someone types !askme add added) in db it figures as "ed") ✔


import psutil
import time
from time import sleep
from datetime import datetime as dt
import json
import os

# or maybe https://docs.python.org/2/library/configparser.html
class Settings:
    def __init__(self):
        try:
            with open('config.json') as json_file:
                cfg = json.loads(json_file.read())
                self.scan_interval = cfg["scan_interval"]
                self.cpu_interval_threshold = cfg["cpu_interval_threshold"]
                self.cpu_load_threshold = cfg["cpu_load_threshold"]
                self.net_interval_threshold = cfg["net_interval_threshold"]
                self.net_in_threshold = cfg["net_in_threshold"]
                self.net_out_threshold = cfg["net_out_threshold"]
                self.update_interval = cfg["update_interval"]
                # during this timeframe there is a sleep instead of a shutdown
                self.sleep_timefames = cfg["sleep_timeframes"]
        except ValueError as e:
            print('failure to read config.json')
            print(e)
            exit()
        if type(self.sleep_timefames) is not list:
            raise ValueError("sleep_timeframes must be a list of timeframes")

setts = Settings()

def get_net_usage(interval=1):  # change the inf variable according to the interface
    net_in, net_out = 0, 0

    ret = psutil.net_io_counters(pernic=True, nowrap=True)
    for key, value in ret.items():
        net_in -= value.bytes_recv
        net_out -= value.bytes_sent

    time.sleep(interval)
    ret = psutil.net_io_counters(pernic=True, nowrap=True)
    for key, value in ret.items():
        net_in += value.bytes_recv
        net_out += value.bytes_sent

    net_in = round(net_in / 1024 / interval, 3)
    net_out = round(net_out / 1024 / interval, 3)

    print(f"Current net-usage:\nIN: {net_in} Kb/s, OUT: {net_out} Kb/s")
    return net_in, net_out

def main():

    low_cpu_intervals = low_net_intervals = 0
    # 0.61 MB/s
    # 0.01

    # When interval is 0.0 or None compares system CPU times elapsed since last call or module import
    psutil.cpu_percent()

    while True:
        net_in, net_out = get_net_usage(setts.update_interval)
        if net_in < setts.net_in_threshold or net_out < setts.net_out_threshold:
            low_net_intervals += 1
        else:
            low_net_intervals = 0
        print(psutil.cpu_percent())
        if psutil.cpu_percent() < setts.cpu_load_threshold:
            low_cpu_intervals += 1
        else:
            low_cpu_intervals = 0

        if True or low_net_intervals > setts.net_interval_threshold and low_cpu_intervals > setts.cpu_interval_threshold:
            # maybe make a popup prompts here
            now = dt.now()
            day_hour = now.hour + now.minute / 60

            for timeframe in setts.sleep_timefames:
                if timeframe[0] < day_hour < timeframe[1]:
                    os.system("timeout /t 1")
            os.system("shutdown /s /t 1")
            sleep(5)

        print(f"low_cpu_intervals {low_cpu_intervals} \n low_network_intervals {low_net_intervals}")

if __name__ == '__main__':
    main()
    sleep(5)


from fastapi import FastAPI, Query, WebSocket
from fastapi.middleware.cors import CORSMiddleware
from models import SignupModel, LoginModel
from database import (
    create_user,
    fetch_user,
    update_online,
    fetch_user_on_username,
    get_all_user,
    add_followers,
    add_following
)
app = FastAPI()

origins = ["*"]

app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

@app.get("/")
async def main():
    return {"message": "Hello World"}

@app.post("/random-chat/signin", tags=["user credentials"])
def signin(signupData: SignupModel):
    userId = create_user(signupData)
    return {"status": "OK", "userid": userId}

@app.patch("/random-chat/login", tags=["user credentials"])
def login(loginData: LoginModel):
    user = fetch_user(dict(loginData))
    if user:
        update_status = update_online(user.get("email"), True)
        return {"status": "OK", "data": "Login Successfull"}
    else:
        return {"status": "FAIL", 'data': "Invalid credentials"}

@app.patch("/random-chat/logout", tags=["user credentials"])
def logout(name: str):
    user = fetch_user_on_username(name)
    if user:
        update_status = update_online(user.get("email"), False)
        return {"status": "OK", "data": "Logout Successfull"}
    else:
        return {"status": "FAIL", 'data': "Invalid credentials"}

@app.get("/random-chat/friends")
def get_all_users(name: str = Query(None)):
    users = get_all_user(name)
    data = []
    for user in users:
        user["_id"] = str(user["_id"])
        data.append(user)
    return {"users": data}

@app.patch("/random-chat/add-following/{user}")
def add_followingfriend(user, friend_name):
    userId = fetch_user_on_username(user).get("_id")
    count = add_following(userId, friend_name)
    return {"data": "friend added"}

@app.patch("/random-chat/add-follower/{user}")
def add_followingfriend(user, friend_name):
    userId = fetch_user_on_username(user).get("_id")
    count = add_followers(userId, friend_name)
    return {"data": "friend added"}

########################################## -- WEBSOCKETS -- ####################################################
connections = {}

@app.websocket("/ws/{username}/{reciver}")
async def mutual_chat(ws: WebSocket, username: str, reciver: str):
    print("Accepting...")
    await ws.accept()
    print("Accepted...")
    connections[username] = [ws, reciver]
    while True:
        try:
            data = await ws.receive_json()
            for name, connection in connections.items():
                if (name == username and connection[1] == reciver) or name == reciver and connection[1] == username:
                    await connection[0].send_json(data)
        except:
            del connections[username]
            break


import sys
import traceback

from PyQt5 import uic, QtCore
from PyQt5.QtWidgets import QMainWindow, QApplication, QPushButton, QLabel, QFileDialog, QMessageBox

from OCR_From_Image import run_OCR
from SnagIt import SnippingTool
from Spell_Checker import spell_check_and_correct

# Move the styles to a separate CSS file and load it here
with open('styles.css', 'r') as f:
    STYLESHEET = f.read()

def save_to_file(filename, content):
    """Save content to a specified file."""
    with open(filename, 'w') as f:
        f.write(content)

def exit_program():
    """Exit the application."""
    sys.exit()

class OCRApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.snipping_tool_instance = None
        self.screenshot_path = None

        # Load the UI and initialize widgets
        self.fname = None
        self.ocr_result = None
        self._load_ui()
        self._initialize_widgets()
        self.show()

    def _load_ui(self):
        """Load the UI file and set up the interface."""
        uic.loadUi("OCR_From_image.ui", self)

    def _initialize_widgets(self):
        """Initialize and configure widgets."""
        self.label = self.findChild(QLabel, "Label_Open_File")
        self.label.setTextInteractionFlags(QtCore.Qt.TextSelectableByMouse | QtCore.Qt.LinksAccessibleByMouse)

        self.buttonConvert = self.findChild(QPushButton, "Button_Convert")
        self.buttonConvert.hide()

        for button_name, signal_handler in [("btnCapture", self.start_snipping),
                                            ("Button_Open_File", self.select_file),
                                            ("Button_Convert", self.convert),
                                            ("Button_Quit", exit_program)]:
            button = self.findChild(QPushButton, button_name)
            button.clicked.connect(signal_handler)

        # Apply the stylesheet for the buttons
        self.buttonConvert.setStyleSheet(STYLESHEET)
        self.findChild(QPushButton, "btnCapture").setStyleSheet(STYLESHEET)

    def convert(self):
        """Convert the selected file using OCR."""
        corrected_sentence = []
        try:
            self.ocr_result = run_OCR(self.screenshot_path, r'C:\\Program Files\\Tesseract-OCR\\tesseract',
                                      'text_extracted.txt')

            for i, result in enumerate(self.ocr_result.split('\n'), 0):
                if len(result) > 0:
                    corrected_sentence.append(spell_check_and_correct(result))
                    self.label.setText(corrected_sentence[-1])
        except Exception as e:
            QMessageBox.critical(self, "Error", f"Failed to perform OCR with error: {e}")
            traceback.print_exc()
            pass

    def start_snipping(self):
        self.snipping_tool_instance = SnippingTool(self)
        self.snipping_tool_instance.startSnipping()

    def handle_screenshot(self, pixmap):
        """Handle the captured screenshot."""
        self.screenshot_path = "screenshot.png"
        pixmap.save(self.screenshot_path)
        self.label.setText(f'Screenshot saved at {self.screenshot_path}')
        self.buttonConvert.show()

    def select_file(self):
        """Open a file dialog and select a file for OCR conversion."""
        file_filter = "Image Files (*.jpg *.png *.bmp *.jfif *.gif);;All Files (*)"
        self.fname, _ = QFileDialog.getOpenFileName(self, "Select File to Convert", "", file_filter)

        if self.fname:
            self.screenshot_path = self.fname
            self.label.setText(f'File to Convert = {self.screenshot_path}')
            self.buttonConvert.show()

def main():
    app = QApplication(sys.argv)
    ocr_window = OCRApp()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()


from typing import NamedTuple, Deque
from collections import deque

class Node(NamedTuple):
    number: int
    n: int

def num_ops(number):
    """
    Input: an integer number, the target number
    Output: the minimum number of operations required to reach to n from 1.

    Two operations rules:
    1.  multiply by 2
    2.  int. divide by 3

    The base number is 1. Meaning the operation will always start with 1
    These rules can be run in any order, and can be run independently.

    [Hint] the data structure is the key to solve it efficiently.
    """

    def _get_new_nodes(old_node):
        value, n = old_node
        return [Node(value * 2, n + 1), Node(value // 3, n + 1)]

    explored_nodes = {1: 0}
    unexplored_nodes: Deque[Node] = deque([Node(2, 1), Node(0, 1)])

    while True:
        current_node = unexplored_nodes.pop()
        if current_node.number == number:
            return current_node.n
        elif current_node.number in explored_nodes:
            continue
        else:
            explored_nodes.update([current_node])
            unexplored_nodes.extendleft(_get_new_nodes(current_node))


from datetime import date
import dateutil.parser

class Address:
    def __init__(
        self,
        id: str,
        firstName: str,
        lastName: str,
        birthday: str = None,
        street: str = None,
        zip: str = None,
        city: str = None,
        country: str = None,
        email: str = None,
        phone: str = None,
    ):
        self.id = int(id) if not id is None else None
        self.firstName = firstName
        self.lastName = lastName
        self._birthday: date = (
            dateutil.parser.parse(birthday).date() if not birthday is None else None
        )
        self.street = street
        self.zip = int(zip) if not zip is None else None
        self.city = city
        self.country = country
        self.email = email
        self.phone = phone

    def __repr__(self):
        return f"<Address> with id { self.id }"

    @property
    def birthday(self):
        return (
            self._birthday.strftime("%d.%m.%Y") if not self._birthday is None else None
        )

    def list_attr_without_id(self):
        return [
            self.firstName,
            self.lastName,
            self.birthday,
            self.street,
            self.zip,
            self.city,
            self.country,
            self.email,
            self.phone,
        ]

    # def list_attr_with_id(self):
    #     return [
    #         self.id,
    #         self.firstName,
    #         self.lastName,
    #         self.birthday,
    #         self.street,
    #         self.zip,
    #         self.city,
    #         self.country,
    #         self.email,
    #         self.phone,
    #     ]

    # @classmethod
    # def without_id(
    #     cls,
    #     firstName: str,
    #     lastName: str,
    #     birthday: str = None,
    #     street: str = None,
    #     zip: str = None,
    #     city: str = None,
    #     country: str = None,
    #     email: str = None,
    #     phone: str = None,
    # ):
    #     birthday = (
    # # datetime.fromisoformat(birthday).date() if not birthday is None else None
    #         date.isoformat(datetime.strptime(birthday, "%d.%m.%Y"))
    #         if not birthday is None
    #         else None
    #     )

    #     return cls(
    #         None,
    #         firstName,
    #         lastName,
    #         birthday,
    #         street,
    #         zip,
    #         city,
    #         country,
    #         email,
    #         phone,
    #     )