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

# In[1]:

from __future__ import absolute_import, division, print_function, unicode_literals
import tensorflow as tf
from tensorflow import keras
import numpy as np
import matplotlib.pyplot as plt

# In[2]:

fashion_mnist = keras.datasets.fashion_mnist

# In[3]:

(train_images, train_labels), (test_images, test_labels) = fashion_mnist.load_data()

# In[4]:

class_names = ['T-shirt/top', 'Trouser', 'Pullover', 'Dress', 'Coat',
               'Sandal', 'Shirt', 'Sneaker', 'Bag', 'Ankle boot']

# In[5]:

len(train_labels)

# In[6]:

test_images.shape
len(test_labels)

# In[7]:

train_images = train_images / 255.0

# In[8]:

test_images = test_images / 255.0

# In[19]:

plt.figure(figsize=(50,50))
for i in range(100):
    plt.subplot(10,10,i+1)
    plt.xticks([])
    plt.yticks([])
    plt.grid(False)
    plt.imshow(train_images[i], cmap=plt.cm.binary)
    #plt.xlabel(class_names[train_labels[i]]) // view with class labels
plt.show()

# In[20]:

model = keras.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
    keras.layers.Dense(128, activation='relu'),
    keras.layers.Dense(10, activation='softmax')
])

# In[21]:

model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

# In[22]:

model.fit(train_images, train_labels, epochs=10)

# In[23]:

test_loss, test_acc = model.evaluate(test_images,  test_labels, verbose=2)
print('\nTest accuracy:', test_acc)
print("Test loss is", test_loss)

# In[57]:

predictions = model.predict(test_images)
for i in range(9999):
    print(predictions[i])
for i in range(num_images):
    plot_value_array(i, predictions[i], test_labels)


"""
Kerj be egy egesz szamot (feltetelezheto, hoy pozitiv), es ird, ki, hogy 7-es szamrendszerben felirva mennyi a szamjegyeinek az osszege (decimalis formaban)

Pl.:
 1   (1)  -->  1
 2   (2)  -->  2
 3   (3)  -->  3
 4   (4)  -->  4
 5   (5)  -->  5
 6   (6)  -->  6
 7  (10)  -->  1
 8  (11)  -->  2
 9  (12)  -->  3
10  (13)  -->  4
11  (14)  -->  5
12  (15)  -->  6
13  (16)  -->  7
48  (66)  -->  12
49 (100)  -->  1
"""

szam = int(input())
maradek = []
osszeg = 0
while szam > 0:
    maradek.append(str(szam % 7))
    szam //= 7

for i in range(len(maradek)):
    osszeg += int(maradek[i])

print(osszeg)


import httplib
from socket import error as SocketError
from luigi.s3 import ReadableS3File

def _patched_s3_close(self):
    try:
        self.s3_key.close()
    except Exception:
        self.s3_key.close(fast=True)
    self.closed = True

def _patched_s3_iterator(self):
    key_iter = self.s3_key.__iter__()
    has_next = True
    index = 0
    while has_next:
        try:
            # grad the next chunk
            chunk = ''
            while True:
                try:
                    chunk = next(key_iter)
                    break
                except (httplib.IncompleteRead, SocketError) as e:
                    self.s3_key.close(fast=True)
                    self.s3_key.open(mode='r', headers={
                        'Range': 'bytes=%d-' % index
                        })
                    key_iter = self.s3_key.__iter__()
            index += len(chunk)

            # split on newlines, preserving the newline
            for line in chunk.splitlines(True):

                if not line.endswith(os.linesep):
                    # no newline, so store in buffer
                    self._add_to_buffer(line)
                else:
                    # newline found, send it out
                    if self.buffer:
                        self._add_to_buffer(line)
                        yield self._flush_buffer()
                    else:
                        yield line
        except StopIteration:
            # send out anything we have left in the buffer
            output = self._flush_buffer()
            if output:
                yield output
            has_next = False
    self.close()

def patch():
    ReadableS3File.close = _patched_s3_close
    ReadableS3File.__iter__ = _patched_s3_iterator

patch()


def getAvailableLetters(lettersGuessed):
    '''
    lettersGuessed: list, what letters have been guessed so far
    returns: string, comprised of letters that represents what letters have not
      yet been guessed.
    '''
    # FILL IN YOUR CODE HERE...
    out = ''
    alphabet = ['a','b','c','d','e','f','g','h','i','j','k','l','m','n','o',\
                'p','q','r','s','t','u','v','w','x','y','z']
    for i in lettersGuessed:
        alphabet.remove (i)
    return out.join(alphabet)

print (getAvailableLetters(['a','z','o']))


""" AVL Tree implemented on top of the standard BST. """

__author__ = 'Alexey Ignatiev'
__docformat__ = 'reStructuredText'

from bst import BinarySearchTree
from typing import TypeVar, Generic
from node import AVLTreeNode

K = TypeVar('K')
I = TypeVar('I')

class AVLTree(BinarySearchTree, Generic[K, I]):
    """ Self-balancing binary search tree using rebalancing by sub-tree
        rotations of Adelson-Velsky and Landis (AVL).
    """

    def __init__(self) -> None:
        """
            Initialises an empty Binary Search Tree
            :complexity: O(1)
        """

        BinarySearchTree.__init__(self)

    def get_height(self, current: AVLTreeNode) -> int:
        """
            Get the height of a node. Return current. Height if current is
            not None. Otherwise, return 0.
            :complexity: O(1)
        """
        if current is not None:
            return current.height
        return 0

    def get_weight(self, current: AVLTreeNode) -> int:
        """
            Get the weight of a node. Return current. Weight if current is
            not None. Otherwise, return 0.
            :complexity: O(1)
        """
        if current is not None:
            return current.weight
        return 0

    def get_balance(self, current: AVLTreeNode) -> int:
        """
            Compute the balance factor for the current sub-tree as the value
            (right.height - left.height). If current is None, return 0.
            :complexity: O(1)
        """

        if current is None:
            return 0
        return self.get_height(current.right) - self.get_height(current.left)

    def insert_aux(self, current: AVLTreeNode, key: K, item: I) -> AVLTreeNode:
        """
            Attempts to insert an item into the tree, it uses the Key to insert
            it. After insertion, performs sub-tree rotation whenever it becomes
            unbalanced.
            returns the new root of the subtree.
        """
        if current is None:
            current = AVLTreeNode(key, item)
            self.length += 1  # self.length added in insert_aux already
        elif key < current.key:
            current.left = self.insert_aux(current.left, key, item)
        elif key > current.key:
            current.right = self.insert_aux(current.right, key, item)
        else:
            # key == current.key:
            # it'll raise ValueError anyway
            current.right = self.insert_aux(current.right, key, item)

        current.height_update()  # always update height and weight before balancing
        current.weight_update()
        current = self.rebalance(current)
        return current

    def delete_aux(self, current: AVLTreeNode, key: K) -> AVLTreeNode:
        """
            Attempts to delete an item from the tree, it uses the Key to
            determine the node to delete. After deletion,
            performs sub-tree rotation whenever it becomes unbalanced.
            returns the new root of the subtree.
        """
        if current is None:
            raise ValueError("Empty tree, can't delete node")
        elif key < current.key:
            current.left = self.delete_aux(current.left, key)
        elif key > current.key:
            current.right = self.delete_aux(current.right, key)
        else:
            # actual key found to do deletion  (key == current.key)
            if self.is_leaf(current):  # if current is a leaf, no child to return (both left right is None)
                self.length -= 1
                return None
            elif current.left is None:  # there exist a right node
                self.length -= 1
                return current.right
            elif current.right is None: # there exist a left node
                self.length -= 1
                return current.left

            # Find the successor to replace
            successor = self.get_successor(current)
            current.key = successor.key
            current.item = successor.item
            current.right = self.delete_aux(current.right, successor.key)

        current.height_update()  # always update height and weight before balancing
        current.weight_update()
        current = self.rebalance(current)
        return current

    def left_rotate(self, current: AVLTreeNode) -> AVLTreeNode:
        """
            Perform left rotation of the sub-tree.
            Right child of the current node, i.e. of the root of the target
            sub-tree, should become the new root of the sub-tree.
            returns the new root of the subtree.
            Example:

                 current                                       child
                /       \                                      /   \
            l-tree     child           -------->        current     r-tree
                      /     \                           /     \
                 center     r-tree                 l-tree     center

            :complexity: O(1)
        """
        child = current.right
        center = current.right.left

        current.right = center
        current.height_update()
        current.weight_update()

        child.left = current
        child.height_update()
        child.weight_update()
        return child

    def right_rotate(self, current: AVLTreeNode) -> AVLTreeNode:
        """
            Perform right rotation of the sub-tree.
            Left child of the current node, i.e. of the root of the target
            sub-tree, should become the new root of the sub-tree.
            returns the new root of the subtree.
            Example:

                       current                                child
                      /       \                              /     \
                  child       r-tree     --------->     l-tree     current
                 /     \                                           /     \
            l-tree     center                                 center     r-tree

            :complexity: O(1)
        """
        child = current.left
        center = current.left.right

        current.left = center
        current.height_update()
        current.weight_update()

        child.right = current
        child.height_update()
        child.weight_update()
        return child

    def rebalance(self, current: AVLTreeNode) -> AVLTreeNode:
        """ Compute the balance of the current node.
            Do rebalancing of the sub-tree of this node if necessary.
            Rebalancing should be done either by:
            - one left rotate
            - one right rotate
            - a combination of left + right rotate
            - a combination of right + left rotate
            returns the new root of the subtree.
        """
        if self.get_balance(current) >= 2:
            child = current.right
            if self.get_height(child.left) > self.get_height(child.right):
                current.right = self.right_rotate(child)
            return self.left_rotate(current)

        if self.get_balance(current) <= -2:
            child = current.left
            if self.get_height(child.right) > self.get_height(child.left):
                current.left = self.left_rotate(child)
            return self.right_rotate(current)

        return current

    def kth_largest(self, k: int) -> AVLTreeNode:
        """
            Returns the kth largest element in the tree. k = 1 would return the largest.
            :pre: must be 1 < k < num of available potions
            :complexity: O(log(n)) , where n is length of tree
        """
        # Exception for k
        if k < 1:
            raise IndexError("Can't be less than 1")
        elif k > self.length:
            raise IndexError("Can't be more than the number of available potions")

        current = self.root
        iterator = self.get_weight(current.right) + 1

        if iterator == k:  # base case is when root is the only node in tree
            return current
        else:
            return self.kth_largest_aux(current, k, iterator)

    def kth_largest_aux(self,current: AVLTreeNode, k: int, iterator: int) -> AVLTreeNode:
        if current is None:
            raise IndexError('Empty tree')

        left = current.left
        right = current.right

        if iterator < k:  # if iterators node is smaller than k
            iterator += 1 + self.get_weight(left.right)
            return self.kth_largest_aux(left, k, iterator)  # iterate to left node
        elif iterator > k:  # if iterators node is larger than k
            iterator -= 1 + self.get_weight(right.left)
            return self.kth_largest_aux(right, k, iterator)  # iterate to right node
        else:  # iterators node == k
            return current


from plyer import notification
import time

def notifyMe(title, message):
    notification.notify(
        title = title,
        message = message,
        app_icon = "D:\\Resume projects\\Notifier\\clock-icon.png",
        timeout = 10,
    )

if __name__ == '__main__':
    while True:
        notifyMe("Hey Swasthik , take a break now !!")
        time.sleep(1200)


import numpy as np
import matplotlib.pyplot as plt
from distributions import NormalDistribution
from credits import Debt, Loan

class Agent:
    def __init__(self, name, money, income, expenses, income_changes,
                 expenses_changes, excess_spending, Loan, Debt, age=216):
        self.name = name
        self.age = age
        self.initial_age = age
        self.money = money
        self.income = income
        self.previncome = income
        self.expenses = expenses
        self.working = 0
        self.Debt = Debt
        self.Loan = Loan
        self.hire = 0.20
        self.fire = 0.01
        self.excess_spending = excess_spending
        self.excess_spending_per_month = 0
        self.income_changes = income_changes
        self.expenses_changes = expenses_changes
        # self.Loan.get_loan(NormalDistribution(2000,200), NormalDistribution(5*12,6),NormalDistribution(15,3))
        self.results = {"age": [], "money": [], "income": [], "expenses": [],
                        "working": [], "total_debt": [], "excess_spending": []}

    def update(self):
        self.age += 1

        self.working = self.working_possibility(
            self.working, self.hire, self.fire)
        if(self.working):
            if(self.income == 0):
                self.income = 0.9 * self.previncome
                # self.income = 1000 * (1.01**((self.age - self.initial_age)//12))
            self.income += self.income_changes.random()

        elif(not self.working):
            if(self.income):
                self.previncome = self.income
            self.income = 0
        self.expenses += self.expenses_changes.random()

        self.money = self.pay_credit(
            self.money, self.income, self.expenses, self.Debt.sum_debt(), self.Loan.sum_loan(self.age))
        self.excess_spending_per_month = abs(self.excess_spending.random())
        self.money *= self.excess_spending_per_month
        self.add_results(self.age, self.money, self.income, self.expenses,
                         self.working, self.excess_spending_per_month)

    def working_possibility(self, past_inf, hire, fire):
        if past_inf:
            return np.random.choice([0, 1], p=[fire, 1 - fire])
        else:
            return np.random.choice([1, 0], p=[hire, 1 - hire])

    def add_results(self, age, money, income, expenses, working, excess_spending):
        self.results["age"].append(age)
        self.results["money"].append(money)
        self.results["income"].append(income)
        self.results["expenses"].append(expenses)
        self.results["working"].append(working)
        self.results["total_debt"].append(
            self.Debt.sum_debt_amount() + self.Loan.sum_loan_amount(self.age))
        self.results["excess_spending"].append(excess_spending)

    def pay_credit(self, money, income, expenses, sum_debt, sum_loan):

        money += income - expenses - sum_debt - sum_loan
        if(money < 0):
            self.Debt.add_debt((money) * (-1), np.random.randint(1, 24))
            money = 0
        self.Debt.update()
        self.Loan.update(self.age)
        return money

    def plot(self, entry):
        plt.plot(range(self.initial_age, self.age),
                 self.results[entry], label=entry)
        plt.legend()

    def plot_histogram(self, entry):
        plt.hist(self.results[entry], bins=100)
        plt.show()

    def show(self):
        plt.show()


from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import Select
import time

driver = webdriver.Chrome(executable_path = r"C:\Users\daniel.vasev\Desktop\Boohoo\Lichna\Automation\Drivers\chromedriver_win32\chromedriver.exe")
driver.get("https://www.amazon.co.uk/")
driver.maximize_window()

#CAPTURE ALL COOKIES created by the browser
cookies = driver.get_cookies()
# how many cookies are store in the browser
print(len(cookies))
#print cookies
print(cookies)

#ADD NEW COOKIES to the browser
cookies = {"name":"DanielCookie","value":"123456789"}
driver.add_cookie(cookies)

cookies = driver.get_cookies()
print(len(cookies))
print(cookies)

#Deleting the cookies
driver.delete_cookie("DanielCookie")
cookies = driver.get_cookies()
print(len(cookies))
print(cookies)

time.sleep(5)
driver.quit()


from typing import Any, Dict

from perception_dataset.constants import EXTENSION_ENUM
from perception_dataset.t4_dataset.classes.abstract_class import AbstractRecord, AbstractTable

class InstanceRecord(AbstractRecord):
    def __init__(self, category_token: str, instance_name: str = ""):
        super().__init__()
        self._category_token: str = category_token
        self._instance_name: str = instance_name
        self._nbr_annotations: int = 0
        self._first_annotation_token: str = ""
        self._last_annotation_token: str = ""

    def to_dict(self) -> Dict[str, Any]:
        d = {
            "token": self.token,
            "category_token": self._category_token,
            "instance_name": self._instance_name,
            "nbr_annotations": self._nbr_annotations,
            "first_annotation_token": self._first_annotation_token,
            "last_annotation_token": self._last_annotation_token,
        }
        return d

    def set_annotation_info(
        self, nbr_annotations: int, first_annotation_token: str, last_annotation_token: str
    ):
        self._nbr_annotations = nbr_annotations
        self._first_annotation_token = first_annotation_token
        self._last_annotation_token = last_annotation_token

class InstanceTable(AbstractTable):
    """https://github.com/tier4/tier4_perception_dataset/blob/main/docs/t4_format_3d_detailed.md#instancejson"""

    FILENAME = "instance" + EXTENSION_ENUM.JSON.value

    def __init__(self):
        super().__init__()
        self._id_to_token: Dict[str, str] = {}

    def _to_record(self, category_token: str, instance_name: str = ""):
        return InstanceRecord(category_token=category_token, instance_name=instance_name)

    def get_token_from_id(self, instance_id: str, category_token: str, dataset_name: str) -> str:
        if instance_id in self._id_to_token:
            token = self._id_to_token[instance_id]
        else:
            token = self.insert_into_table(
                category_token=category_token, instance_name=dataset_name + "::" + str(instance_id)
            )
            self._id_to_token[instance_id] = token

        return token


"""Drop-in replacement for collections.AttributedDict by Raymond Hettinger

http://code.activestate.com/recipes/576693/

"""
try:
    try:
        from collections.abc import MutableMapping as DictMixin
    except ImportError:
        from collections import MutableMapping as DictMixin
    py_level = 3
except ImportError:
    # noinspection PyUnresolvedReferences,PyCompatibility
    from UserDict import DictMixin
    py_level = 2

# Modified from original to support Python 2.4, see
# http://code.google.com/p/dirtyjson/issues/detail?id=53
try:
    # noinspection PyStatementEffect
    all
except NameError:
    # noinspection PyShadowingBuiltins
    def all(seq):
        for elem in seq:
            if not elem:
                return False
        return True

class AttributedDict(dict, DictMixin):
    def __init__(self):
        super(AttributedDict, self).__init__()
        self.__attributes = {}
        self.clear()

    # noinspection PyAttributeOutsideInit
    def clear(self):
        self.__end = end = []
        end += [None, end, end]         # sentinel node for doubly linked list
        self.__map = {}                 # key --> [key, prev, next]
        dict.clear(self)

    def add_with_attributes(self, key, value, attributes):
        self.__setitem__(key, value)
        self.__attributes[key] = attributes

    def attributes(self, key):
        return self.__attributes.get(key)

    def __setitem__(self, key, value):
        if key not in self:
            end = self.__end
            curr = end[1]
            curr[2] = end[1] = self.__map[key] = [key, curr, end]
        dict.__setitem__(self, key, value)

    def __delitem__(self, key):
        dict.__delitem__(self, key)
        key, prev, next_entry = self.__map.pop(key)
        prev[2] = next_entry
        next_entry[1] = prev

    def __iter__(self):
        end = self.__end
        curr = end[2]
        while curr is not end:
            yield curr[0]
            curr = curr[2]

    def __reversed__(self):
        end = self.__end
        curr = end[1]
        while curr is not end:
            yield curr[0]
            curr = curr[1]

    def popitem(self, last=True):
        if not self:
            raise KeyError('dictionary is empty')
        # Modified from original to support Python 2.4, see
        # http://code.google.com/p/dirtyjson/issues/detail?id=53
        if py_level == 2:
            if last:
                # noinspection PyUnresolvedReferences
                key = reversed(self).next()
            else:
                # noinspection PyUnresolvedReferences
                key = iter(self).next()
        else:
            if last:
                key = next(reversed(self))
            else:
                key = next(iter(self))
        value = self.pop(key)
        return key, value

    def __reduce__(self):
        items = [[k, self[k]] for k in self]
        tmp = self.__map, self.__end
        del self.__map, self.__end
        inst_dict = vars(self).copy()
        self.__map, self.__end = tmp
        if inst_dict:
            return self.__class__, (items,), inst_dict
        return self.__class__, (items,)

    def keys(self):
        return list(self)

    setdefault = DictMixin.setdefault
    update = DictMixin.update
    pop = DictMixin.pop
    values = DictMixin.values
    items = DictMixin.items

    def __repr__(self):
        if not self:
            return '%s()' % (self.__class__.__name__,)
        return '%s(%s)' % (self.__class__.__name__, list(self.items()))

    def copy(self):
        return self.__class__(self)

    # noinspection PyMethodOverriding
    @classmethod
    def fromkeys(cls, iterable, value=None):
        d = cls()
        for key in iterable:
            d[key] = value
        return d

    def __eq__(self, other):
        if isinstance(other, AttributedDict):
            return len(self) == len(other) and all(
                p == q for p, q in zip(sorted(self.items()), sorted(other.items())))
        return dict.__eq__(self, other)

    def __ne__(self, other):
        return not self == other

class AttributedList(list):
    def __init__(self):
        super(AttributedList, self).__init__()
        self.__attributes = []

    def append(self, value, attributes=None):
        super(AttributedList, self).append(value)
        self.__attributes.append(attributes)

    def attributes(self, index):
        return self.__attributes[index]


# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
# You can obtain one at http://mozilla.org/MPL/2.0/.

import codecs
import fnmatch
import io
import json
import os
import shutil
import sys
import types

from six import StringIO, string_types

from .filters import DEFAULT_FILTERS, enabled, filterlist
from .filters import exists as _exists
from .ini import read_ini

__all__ = ["ManifestParser", "TestManifest", "convert"]

relpath = os.path.relpath

# path normalization

def normalize_path(path):
    """normalize a relative path"""
    if sys.platform.startswith("win"):
        return path.replace("/", os.path.sep)
    return path

def denormalize_path(path):
    """denormalize a relative path"""
    if sys.platform.startswith("win"):
        return path.replace(os.path.sep, "/")
    return path

# objects for parsing manifests

class ManifestParser(object):
    """read .ini manifests"""

    def __init__(
        self,
        manifests=(),
        defaults=None,
        strict=True,
        rootdir=None,
        finder=None,
        handle_defaults=True,
    ):
        """Creates a ManifestParser from the given manifest files.

        :param manifests: An iterable of file paths or file objects corresponding
                          to manifests. If a file path refers to a manifest file that
                          does not exist, an IOError is raised.
        :param defaults: Variables to pre-define in the environment for evaluating
                         expressions in manifests.
        :param strict: If False, the provided manifests may contain references to
                       listed (test) files that do not exist without raising an
                       IOError during reading, and certain errors in manifests
                       are not considered fatal. Those errors include duplicate
                       section names, redefining variables, and defining empty
                       variables.
        :param rootdir: The directory used as the basis for conversion to and from
                        relative paths during manifest reading.
        :param finder: If provided, this finder object will be used for filesystem
                       interactions. Finder objects are part of the mozpack package,
                       documented at
                       http://firefox-source-docs.mozilla.org/python/mozpack.html#module-mozpack.files
        :param handle_defaults: If not set, do not propagate manifest defaults to individual
                                test objects. Callers are expected to manage per-manifest
                                defaults themselves via the manifest_defaults member
                                variable in this case.
        """
        self._defaults = defaults or {}
        self.tests = []
        self.manifest_defaults = {}
        self.source_files = set()
        self.strict = strict
        self.rootdir = rootdir
        self._root = None
        self.finder = finder
        self._handle_defaults = handle_defaults
        if manifests:
            self.read(*manifests)

    def path_exists(self, path):
        if self.finder:
            return self.finder.get(path) is not None
        return os.path.exists(path)

    @property
    def root(self):
        if not self._root:
            if self.rootdir is None:
                self._root = ""
            else:
                assert os.path.isabs(self.rootdir)
                self._root = self.rootdir + os.path.sep
        return self._root

    def relative_to_root(self, path):
        # Microoptimization, because relpath is quite expensive.
        # We know that rootdir is an absolute path or empty. If path
        # starts with rootdir, then path is also absolute and the tail
        # of the path is the relative path (possibly non-normalized,
        # when here is unknown).
        # For this to work rootdir needs to be terminated with a path
        # separator, so that references to sibling directories with
        # a common prefix don't get misscomputed (e.g. /root and
        # /rootbeer/file).
        # When the rootdir is unknown, the relpath needs to be left
        # unchanged. We use an empty string as rootdir in that case,
        # which leaves relpath unchanged after slicing.
        if path.startswith(self.root):
            return path[len(self.root) :]
        else:
            return relpath(path, self.root)

    # methods for reading manifests

    def _read(self, root, filename, defaults, parentmanifest=None):
        """
        Internal recursive method for reading and parsing manifests.
        Stores all found tests in self.tests
        :param root: The base path
        :param filename: File object or string path for the base manifest file
        :param defaults: Options that apply to all items
        :param parentmanifest: Filename of the parent manifest, relative to rootdir (default None)
        """

        def read_file(type):
            include_file = section.split(type, 1)[-1]
            include_file = normalize_path(include_file)
            if not os.path.isabs(include_file):
                include_file = os.path.join(here, include_file)
            if not self.path_exists(include_file):
                message = "Included file '%s' does not exist" % include_file
                if self.strict:
                    raise IOError(message)
                else:
                    sys.stderr.write("%s\n" % message)
                    return
            return include_file

        # get directory of this file if not file-like object
        if isinstance(filename, string_types):
            # If we're using mercurial as our filesystem via a finder
            # during manifest reading, the getcwd() calls that happen
            # with abspath calls will not be meaningful, so absolute
            # paths are required.
            if self.finder:
                assert os.path.isabs(filename)
            filename = os.path.abspath(filename)
            filename_rel = self.relative_to_root(filename)
            self.source_files.add(filename)
            if self.finder:
                fp = codecs.getreader("utf-8")(self.finder.get(filename).open())
            else:
                fp = io.open(filename, encoding="utf-8")
            here = os.path.dirname(filename)
        else:
            fp = filename
            filename = here = None
            filename_rel = None
        defaults["here"] = here

        # read the configuration
        sections, defaults = read_ini(
            fp=fp,
            defaults=defaults,
            strict=self.strict,
            handle_defaults=self._handle_defaults,
        )
        if parentmanifest and filename:
            # A manifest can be read multiple times, via "include:", optionally
            # with section-specific variables. These variables only apply to
            # the included manifest when included via the same parent manifest,
            # so they must be associated with (parentmanifest, filename).
            #
            # |defaults| is a combination of variables, in the following order:
            # - The defaults of the ancestor manifests if self._handle_defaults
            #   is True.
            # - Any variables from the "[include:...]" section.
            # - The defaults of the included manifest.
            self.manifest_defaults[(parentmanifest, filename)] = defaults
        else:
            self.manifest_defaults[filename] = defaults

        # get the tests
        for section, data in sections:
            # a file to include
            # TODO: keep track of included file structure:
            # self.manifests = {'manifest.ini': 'relative/path.ini'}
            if section.startswith("include:"):
                include_file = read_file("include:")
                if include_file:
                    include_defaults = data.copy()
                    self._read(
                        root,
                        include_file,
                        include_defaults,
                        parentmanifest=filename_rel,
                    )
                continue

            # otherwise an item
            test = data.copy()
            test["name"] = section

            # Will be None if the manifest being read is a file-like object.
            test["manifest"] = filename
            test["manifest_relpath"] = None
            if filename:
                test["manifest_relpath"] = filename_rel

            # determine the path
            path = test.get("path", section)
            _relpath = path
            if "://" not in path:  # don't futz with URLs
                path = normalize_path(path)
                if here and not os.path.isabs(path):
                    # Profiling indicates 25% of manifest parsing is spent
                    # in this call to normpath, but almost all calls return
                    # their argument unmodified, so we avoid the call if
                    # '..' if not present in the path.
                    path = os.path.join(here, path)
                    if ".." in path:
                        path = os.path.normpath(path)
                _relpath = self.relative_to_root(path)

            test["path"] = path
            test["relpath"] = _relpath

            if parentmanifest is not None:
                # If a test was included by a parent manifest we may need to
                # indicate that in the test object for the sake of identifying
                # a test, particularly in the case a test file is included by
                # multiple manifests.
                test["ancestor_manifest"] = parentmanifest

            # append the item
            self.tests.append(test)

    def read(self, *filenames, **defaults):
        """
        read and add manifests from file paths or file-like objects

        filenames -- file paths or file-like objects to read as manifests
        defaults -- default variables
        """

        # ensure all files exist
        missing = [
            filename
            for filename in filenames
            if isinstance(filename, string_types) and not self.path_exists(filename)
        ]
        if missing:
            raise IOError("Missing files: %s" % ", ".join(missing))

        # default variables
        _defaults = defaults.copy() or self._defaults.copy()
        _defaults.setdefault("here", None)

        # process each file
        for filename in filenames:
            # set the per file defaults
            defaults = _defaults.copy()
            here = None
            if isinstance(filename, string_types):
                here = os.path.dirname(os.path.abspath(filename))
                defaults["here"] = here  # directory of master .ini file

            if self.rootdir is None:
                # set the root directory
                # == the directory of the first manifest given
                self.rootdir = here

            self._read(here, filename, defaults)

    # methods for querying manifests

    def query(self, *checks, **kw):
        """
        general query function for tests
        - checks : callable conditions to test if the test fulfills the query
        """
        tests = kw.get("tests", None)
        if tests is None:
            tests = self.tests
        retval = []
        for test in tests:
            for check in checks:
                if not check(test):
                    break
            else:
                retval.append(test)
        return retval

    def get(self, _key=None, inverse=False, tags=None, tests=None, **kwargs):
        # TODO: pass a dict instead of kwargs since you might hav
        # e.g. 'inverse' as a key in the dict

        # TODO: tags should just be part of kwargs with None values
        # (None == any is kinda weird, but probably still better)

        # fix up tags
        if tags:
            tags = set(tags)
        else:
            tags = set()

        # make some check functions
        if inverse:

            def has_tags(test):
                return not tags.intersection(test.keys())

            def dict_query(test):
                for key, value in list(kwargs.items()):
                    if test.get(key) == value:
                        return False
                return True

        else:

            def has_tags(test):
                return tags.issubset(test.keys())

            def dict_query(test):
                for key, value in list(kwargs.items()):
                    if test.get(key) != value:
                        return False
                return True

        # query the tests
        tests = self.query(has_tags, dict_query, tests=tests)

        # if a key is given, return only a list of that key
        # useful for keys like 'name' or 'path'
        if _key:
            return [test[_key] for test in tests]

        # return the tests
        return tests

    def manifests(self, tests=None):
        """
        return manifests in order in which they appear in the tests
        If |tests| is not set, the order of the manifests is unspecified.
        """
        if tests is None:
            manifests = []
            # Make sure to return all the manifests, even ones without tests.
            for manifest in list(self.manifest_defaults.keys()):
                if isinstance(manifest, tuple):
                    parentmanifest, manifest = manifest
                if manifest not in manifests:
                    manifests.append(manifest)
            return manifests

        manifests = []
        for test in tests:
            manifest = test.get("manifest")
            if not manifest:
                continue
            if manifest not in manifests:
                manifests.append(manifest)
        return manifests

    def paths(self):
        return [i["path"] for i in self.tests]

    # methods for auditing

    def missing(self, tests=None):
        """
        return list of tests that do not exist on the filesystem
        """
        if tests is None:
            tests = self.tests
        existing = list(_exists(tests, {}))
        return [t for t in tests if t not in existing]

    def check_missing(self, tests=None):
        missing = self.missing(tests=tests)
        if missing:
            missing_paths = [test["path"] for test in missing]
            if self.strict:
                raise IOError(
                    "Strict mode enabled, test paths must exist. "
                    "The following test(s) are missing: %s"
                    % json.dumps(missing_paths, indent=2)
                )
            print(
                "Warning: The following test(s) are missing: %s"
                % json.dumps(missing_paths, indent=2),
                file=sys.stderr,
            )
        return missing

    def verifyDirectory(self, directories, pattern=None, extensions=None):
        """
        checks what is on the filesystem vs what is in a manifest
        returns a 2-tuple of sets:
        (missing_from_filesystem, missing_from_manifest)
        """

        files = set([])
        if isinstance(directories, string_types):
            directories = [directories]

        # get files in directories
        for directory in directories:
            for dirpath, dirnames, filenames in os.walk(directory, topdown=True):

                # only add files that match a pattern
                if pattern:
                    filenames = fnmatch.filter(filenames, pattern)

                # only add files that have one of the extensions
                if extensions:
                    filenames = [
                        filename
                        for filename in filenames
                        if os.path.splitext(filename)[-1] in extensions
                    ]

                files.update(
                    [os.path.join(dirpath, filename) for filename in filenames]
                )

        paths = set(self.paths())
        missing_from_filesystem = paths.difference(files)
        missing_from_manifest = files.difference(paths)
        return (missing_from_filesystem, missing_from_manifest)

    # methods for output

    def write(
        self,
        fp=sys.stdout,
        rootdir=None,
        global_tags=None,
        global_kwargs=None,
        local_tags=None,
        local_kwargs=None,
    ):
        """
        write a manifest given a query
        global and local options will be munged to do the query
        globals will be written to the top of the file
        locals (if given) will be written per test
        """

        # open file if `fp` given as string
        close = False
        if isinstance(fp, string_types):
            fp = open(fp, "w")
            close = True

        # root directory
        if rootdir is None:
            rootdir = self.rootdir

        # sanitize input
        global_tags = global_tags or set()
        local_tags = local_tags or set()
        global_kwargs = global_kwargs or {}
        local_kwargs = local_kwargs or {}

        # create the query
        tags = set([])
        tags.update(global_tags)
        tags.update(local_tags)
        kwargs = {}
        kwargs.update(global_kwargs)
        kwargs.update(local_kwargs)

        # get matching tests
        tests = self.get(tags=tags, **kwargs)

        # print the .ini manifest
        if global_tags or global_kwargs:
            print("[DEFAULT]", file=fp)
            for tag in global_tags:
                print("%s =" % tag, file=fp)
            for key, value in list(global_kwargs.items()):
                print("%s = %s" % (key, value), file=fp)
            print(file=fp)

        for test in tests:
            test = test.copy()  # don't overwrite

            path = test["name"]
            if not os.path.isabs(path):
                path = test["path"]
                if self.rootdir:
                    path = relpath(test["path"], self.rootdir)
                path = denormalize_path(path)
            print("[%s]" % path, file=fp)

            # reserved keywords:
            reserved = [
                "path",
                "name",
                "here",
                "manifest",
                "manifest_relpath",
                "relpath",
                "ancestor_manifest",
            ]
            for key in sorted(test.keys()):
                if key in reserved:
                    continue
                if key in global_kwargs:
                    continue
                if key in global_tags and not test[key]:
                    continue
                print("%s = %s" % (key, test[key]), file=fp)
            print(file=fp)

        if close:
            # close the created file
            fp.close()

    def __str__(self):
        fp = StringIO()
        self.write(fp=fp)
        value = fp.getvalue()
        return value

    def copy(self, directory, rootdir=None, *tags, **kwargs):
        """
        copy the manifests and associated tests
        - directory : directory to copy to
        - rootdir : root directory to copy to (if not given from manifests)
        - tags : keywords the tests must have
        - kwargs : key, values the tests must match
        """
        # XXX note that copy does *not* filter the tests out of the
        # resulting manifest; it just stupidly copies them over.
        # ideally, it would reread the manifests and filter out the
        # tests that don't match *tags and **kwargs

        # destination
        if not os.path.exists(directory):
            os.path.makedirs(directory)
        else:
            # sanity check
            assert os.path.isdir(directory)

        # tests to copy
        tests = self.get(tags=tags, **kwargs)
        if not tests:
            return  # nothing to do!

        # root directory
        if rootdir is None:
            rootdir = self.rootdir

        # copy the manifests + tests
        manifests = [relpath(manifest, rootdir) for manifest in self.manifests()]
        for manifest in manifests:
            destination = os.path.join(directory, manifest)
            dirname = os.path.dirname(destination)
            if not os.path.exists(dirname):
                os.makedirs(dirname)
            else:
                # sanity check
                assert os.path.isdir(dirname)
            shutil.copy(os.path.join(rootdir, manifest), destination)

        missing = self.check_missing(tests)
        tests = [test for test in tests if test not in missing]
        for test in tests:
            if os.path.isabs(test["name"]):
                continue
            source = test["path"]
            destination = os.path.join(directory, relpath(test["path"], rootdir))
            shutil.copy(source, destination)
            # TODO: ensure that all of the tests are below the from_dir

    def update(self, from_dir, rootdir=None, *tags, **kwargs):
        """
        update the tests as listed in a manifest from a directory
        - from_dir : directory where the tests live
        - rootdir : root directory to copy to (if not given from manifests)
        - tags : keys the tests must have
        - kwargs : key, values the tests must match
        """

        # get the tests
        tests = self.get(tags=tags, **kwargs)

        # get the root directory
        if not rootdir:
            rootdir = self.rootdir

        # copy them!
        for test in tests:
            if not os.path.isabs(test["name"]):
                _relpath = relpath(test["path"], rootdir)
                source = os.path.join(from_dir, _relpath)
                if not os.path.exists(source):
                    message = "Missing test: '%s' does not exist!"
                    if self.strict:
                        raise IOError(message)
                    print(message + " Skipping.", file=sys.stderr)
                    continue
                destination = os.path.join(rootdir, _relpath)
                shutil.copy(source, destination)

    # directory importers

    @classmethod
    def _walk_directories(cls, directories, callback, pattern=None, ignore=()):
        """
        internal function to import directories
        """

        if isinstance(pattern, string_types):
            patterns = [pattern]
        else:
            patterns = pattern
        ignore = set(ignore)

        if not patterns:

            def accept_filename(filename):
                return True

        else:

            def accept_filename(filename):
                for pattern in patterns:
                    if fnmatch.fnmatch(filename, pattern):
                        return True

        if not ignore:

            def accept_dirname(dirname):
                return True

        else:

            def accept_dirname(dirname):
                return dirname not in ignore

        rootdirectories = directories[:]
        seen_directories = set()
        for rootdirectory in rootdirectories:
            # let's recurse directories using list
            directories = [os.path.realpath(rootdirectory)]
            while directories:
                directory = directories.pop(0)
                if directory in seen_directories:
                    # eliminate possible infinite recursion due to
                    # symbolic links
                    continue
                seen_directories.add(directory)

                files = []
                subdirs = []
                for name in sorted(os.listdir(directory)):
                    path = os.path.join(directory, name)
                    if os.path.isfile(path):
                        # os.path.isfile follow symbolic links, we don't
                        # need to handle them here.
                        if accept_filename(name):
                            files.append(name)
                        continue
                    elif os.path.islink(path):
                        # eliminate symbolic links
                        path = os.path.realpath(path)

                    # we must have a directory here
                    if accept_dirname(name):
                        subdirs.append(name)
                        # this subdir is added for recursion
                        directories.insert(0, path)

                # here we got all subdirs and files filtered, we can
                # call the callback function if directory is not empty
                if subdirs or files:
                    callback(rootdirectory, directory, subdirs, files)

    @classmethod
    def populate_directory_manifests(
        cls, directories, filename, pattern=None, ignore=(), overwrite=False
    ):
        """
        walks directories and writes manifests of name `filename` in-place;
        returns `cls` instance populated with the given manifests

        filename -- filename of manifests to write
        pattern -- shell pattern (glob) or patterns of filenames to match
        ignore -- directory names to ignore
        overwrite -- whether to overwrite existing files of given name
        """

        manifest_dict = {}

        if os.path.basename(filename) != filename:
            raise IOError("filename should not include directory name")

        # no need to hit directories more than once
        _directories = directories
        directories = []
        for directory in _directories:
            if directory not in directories:
                directories.append(directory)

        def callback(directory, dirpath, dirnames, filenames):
            """write a manifest for each directory"""

            manifest_path = os.path.join(dirpath, filename)
            if (dirnames or filenames) and not (
                os.path.exists(manifest_path) and overwrite
            ):
                with open(manifest_path, "w") as manifest:
                    for dirname in dirnames:
                        print(
                            "[include:%s]" % os.path.join(dirname, filename),
                            file=manifest,
                        )
                    for _filename in filenames:
                        print("[%s]" % _filename, file=manifest)

                # add to list of manifests
                manifest_dict.setdefault(directory, manifest_path)

        # walk the directories to gather files
        cls._walk_directories(directories, callback, pattern=pattern, ignore=ignore)
        # get manifests
        manifests = [manifest_dict[directory] for directory in _directories]

        # create a `cls` instance with the manifests
        return cls(manifests=manifests)

    @classmethod
    def from_directories(
        cls, directories, pattern=None, ignore=(), write=None, relative_to=None
    ):
        """
        convert directories to a simple manifest; returns ManifestParser instance

        pattern -- shell pattern (glob) or patterns of filenames to match
        ignore -- directory names to ignore
        write -- filename or file-like object of manifests to write;
                 if `None` then a StringIO instance will be created
        relative_to -- write paths relative to this path;
                       if false then the paths are absolute
        """

        # determine output
        opened_manifest_file = None  # name of opened manifest file
        absolute = not relative_to  # whether to output absolute path names as names
        if isinstance(write, string_types):
            opened_manifest_file = write
            write = open(write, "w")
        if write is None:
            write = StringIO()

        # walk the directories, generating manifests
        def callback(directory, dirpath, dirnames, filenames):

            # absolute paths
            filenames = [os.path.join(dirpath, filename) for filename in filenames]
            # ensure new manifest isn't added
            filenames = [
                filename for filename in filenames if filename != opened_manifest_file
            ]
            # normalize paths
            if not absolute and relative_to:
                filenames = [relpath(filename, relative_to) for filename in filenames]

            # write to manifest
            write_content = "\n".join(
                ["[{}]".format(denormalize_path(filename)) for filename in filenames]
            )
            print(write_content, file=write)

        cls._walk_directories(directories, callback, pattern=pattern, ignore=ignore)

        if opened_manifest_file:
            # close file
            write.close()
            manifests = [opened_manifest_file]
        else:
            # manifests/write is a file-like object;
            # rewind buffer
            write.flush()
            write.seek(0)
            manifests = [write]

        # make a ManifestParser instance
        return cls(manifests=manifests)

convert = ManifestParser.from_directories

class TestManifest(ManifestParser):
    """
    apply logic to manifests;  this is your integration layer :)
    specific harnesses may subclass from this if they need more logic
    """

    def __init__(self, *args, **kwargs):
        ManifestParser.__init__(self, *args, **kwargs)
        self.filters = filterlist(DEFAULT_FILTERS)
        self.last_used_filters = []

    def active_tests(
        self, exists=True, disabled=True, filters=None, noDefaultFilters=False, **values
    ):
        """
        Run all applied filters on the set of tests.

        :param exists: filter out non-existing tests (default True)
        :param disabled: whether to return disabled tests (default True)
        :param values: keys and values to filter on (e.g. `os = linux mac`)
        :param filters: list of filters to apply to the tests
        :returns: list of test objects that were not filtered out
        """
        tests = [i.copy() for i in self.tests]  # shallow copy

        # mark all tests as passing
        for test in tests:
            test["expected"] = test.get("expected", "pass")

        # make a copy so original doesn't get modified
        if noDefaultFilters:
            fltrs = []
        else:
            fltrs = self.filters[:]

        if exists:
            if self.strict:
                self.check_missing(tests)
            else:
                fltrs.append(_exists)

        if not disabled:
            fltrs.append(enabled)

        if filters:
            fltrs += filters

        self.last_used_filters = fltrs[:]
        for fn in fltrs:
            tests = fn(tests, values)
        return list(tests)

    def test_paths(self):
        return [test["path"] for test in self.active_tests()]

    def fmt_filters(self, filters=None):
        filters = filters or self.last_used_filters
        names = []
        for f in filters:
            if isinstance(f, types.FunctionType):
                names.append(f.__name__)
            else:
                names.append(str(f))
        return ", ".join(names)


from graphviz import Digraph
import random
import timeit
from memory_profiler import memory_usage

class Node:
    def __init__(self, interval, max_end):
        self.interval = interval
        self.max_end = max_end
        self.left = None
        self.right = None

class IntervalTree:
    def __init__(self):
        self.root = None

    def insert(self, node, interval):
        if node is None:
            max_end = max(interval)
            return Node(interval, max_end)

        low = interval[0]
        if low < node.interval[0]:
            node.left = self.insert(node.left, interval)
        else:
            node.right = self.insert(node.right, interval)

        if node.max_end < max(interval):
            node.max_end = max(interval)

        return node

    def delete(self, interval):
        self.root, _ = self.delete_interval(self.root, interval)

    def delete_interval(self, root, interval):
        if not root:
            return root, None

        deleted_node = None
        if root.interval == interval:
            deleted_node = root

            if root.left:
                max_node = root.left
                while max_node.right:
                    max_node = max_node.right

                root.interval = max_node.interval
                root.left, _ = self.delete_interval(root.left, max_node.interval)

            elif root.right:
                min_node = root.right
                while min_node.left:
                    min_node = min_node.left

                root.interval = min_node.interval
                root.right, _ = self.delete_interval(root.right, min_node.interval)

            else:
                root = None

        elif interval[0] < root.interval[0]:
            root.left, deleted_node = self.delete_interval(root.left, interval)

        else:
            root.right, deleted_node = self.delete_interval(root.right, interval)

        if root:
            root.max_end = max(
                root.interval[1],
                root.left.max_end if root.left else float("-inf"),
                root.right.max_end if root.right else float("-inf"),
            )

        return root, deleted_node

    def update(self, old_interval, new_interval):
        self.delete(old_interval)
        self.root = self.insert(self.root, new_interval)

    def search(self, node, interval):
        if node is None:
            return []

        intersecting_intervals = []
        low, high = interval
        node_low, node_high = node.interval

        if node_low <= high and low <= node_high:
            intersecting_intervals.append(node.interval)

        if node.left is not None and node.left.max_end >= low:
            intersecting_intervals.extend(self.search(node.left, interval))

        if node.right is not None and node_low <= high:  # Also check the right subtree
            intersecting_intervals.extend(self.search(node.right, interval))

        return intersecting_intervals

    def build(self, intervals):
        for interval in intervals:
            self.root = self.insert(self.root, interval)

# Function to visualize the Interval Tree
def visualize_tree(node, graph=None):
    if graph is None:
        graph = Digraph()

    if node is not None:
        graph.node(f"{id(node)}", f"{node.interval}\nMax End: {node.max_end}")

        if node.left:
            graph.node(
                f"{id(node.left)}",
                f"{node.left.interval}\nMax End: {node.left.max_end}",
            )
            graph.edge(f"{id(node)}", f"{id(node.left)}", "L")
            visualize_tree(node.left, graph)

        if node.right:
            graph.node(
                f"{id(node.right)}",
                f"{node.right.interval}\nMax End: {node.right.max_end}",
            )
            graph.edge(f"{id(node)}", f"{id(node.right)}", "R")
            visualize_tree(node.right, graph)

    return graph

def demo_interval_tree():
    print("Interval Tree:")

    # Create synthetic dataset
    intervals = [(random.randint(0, 50), random.randint(51, 100)) for _ in range(20)]

    # Initialize IntervalTree and build it
    tree = IntervalTree()

    # Measure build memory usage
    mem_usage_before = memory_usage()[0]
    build_time = timeit.timeit(lambda: tree.build(intervals), number=1)
    mem_usage_after = memory_usage()[0]
    mem_diff = mem_usage_after - mem_usage_before
    print(f"Memory used to build the tree: {max(0, mem_diff):.10f} MiB")
    print(f"Time taken to build the tree: {build_time:.10f} seconds")

    # Visualize the tree after build
    visualize_tree(tree.root).render(
        filename="interval_tree_build", view=True, format="png", cleanup=True
    )

    # Create a list of new intervals to insert
    new_intervals = [(15, 30), (32, 45), (5, 20)]

    # Measure time and memory and insert the new intervals
    for new_interval in new_intervals:
        mem_usage_before = memory_usage()[0]
        insert_time = timeit.timeit(
            lambda: tree.insert(tree.root, new_interval), number=1
        )
        mem_usage_after = memory_usage()[0]
        mem_diff = mem_usage_after - mem_usage_before
        print(
            f"Memory used to insert interval {new_interval}: {max(0, mem_diff):.10f} MiB"
        )
        print(
            f"Time taken to insert interval {new_interval}: {insert_time:.10f} seconds"
        )

    # Visualize the tree after insert
    visualize_tree(tree.root).render(
        filename="interval_tree_insert", view=True, format="png", cleanup=True
    )

    # Test deletion
    delete_interval = (32, 45)  # Modify with an actual interval from your tree
    mem_usage_before = memory_usage()[0]
    delete_time = timeit.timeit(lambda: tree.delete(delete_interval), number=1)
    mem_usage_after = memory_usage()[0]
    mem_diff = mem_usage_after - mem_usage_before
    print(
        f"Memory used to delete interval {delete_interval}: {max(0, mem_diff):.10f} MiB"
    )
    print(f"Time taken to delete an interval: {delete_time:.10f} seconds")

    # Visualize the tree after delete
    visualize_tree(tree.root).render(
        filename="interval_tree_delete", view=True, format="png", cleanup=True
    )

    # Test update
    old_interval = (15, 30)  # Modify with an actual interval from your tree
    new_interval = (24, 100)  # Modify with the desired new interval
    mem_usage_before = memory_usage()[0]
    update_time = timeit.timeit(
        lambda: tree.update(old_interval, new_interval), number=1
    )
    mem_usage_after = memory_usage()[0]
    mem_diff = mem_usage_after - mem_usage_before
    print(
        f"Memory used to update interval {old_interval} to {new_interval}: {max(0, mem_diff):.10f} MiB"
    )
    print(f"Time taken to update an interval: {update_time:.10f} seconds")

    # Visualize the tree after update
    visualize_tree(tree.root).render(
        filename="interval_tree_update", view=True, format="png", cleanup=True
    )

    # Test search
    search_interval = (10, 20)
    mem_usage_before = memory_usage()[0]
    search_time = timeit.timeit(
        lambda: tree.search(tree.root, search_interval), number=1
    )
    mem_usage_after = memory_usage()[0]
    mem_diff = mem_usage_after - mem_usage_before
    print(f"Memory used to search an interval: {max(0, mem_diff):.10f} MiB")
    print(f"Time taken to search an interval: {search_time:.10f} seconds")

    # Execute and print the result of the search
    found_interval = tree.search(tree.root, search_interval)
    print(f"Found interval that intersects with {search_interval}: {found_interval}")

# Call the demo function to run the code
if __name__ == "__main__":
    demo_interval_tree()


import socket

sock = socket.socket()
sock.bind(('', 9090))

sock.listen(1)

print('waiting for connection...')
conn, addr = sock.accept()

print('connected: ', addr)

data = conn.recv(1024)
uData = data.decode("utf-8")
print("Client > " + uData)
conn.send(uData.encode("utf-8"))

conn.close()


import sys
n, k = map(int, sys.stdin.readline().split())
dp = dict()
dp[0] = 1
dp[1] = 1

for i in range(2, n+1):
    if i in dp:
        continue
    else:
        dp[i] = dp[i-1] * i
print(dp)
result = (dp[n] // (dp[k] * dp[n-k])) % 10007
print(result)


import tensorflow as tf
from tensorflow import keras
from keras.utils import to_categorical
from keras.models import Sequential
from keras.layers import Dense, Flatten, Conv2D, Dropout, MaxPool2D
import numpy as np
import logging
import os

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

tf.get_logger().setLevel(logging.ERROR)

EPOCHS = 128
BATCH_SIZE = 32

if __name__ == '__main__':
	# load dataset
	cifar_dataset = keras.datasets.cifar10
	(train_images, train_labels), (test_images, test_labels) = cifar_dataset.load_data()

	# standardize dataset
	mean = np.mean(train_images)
	stddev = np.std(train_images)
	train_images = (train_images - mean) / stddev
	test_images = (test_images - mean) / stddev

	# change labels to one-hot
	train_labels = to_categorical(train_labels, num_classes=10)
	test_labels = to_categorical(test_labels, num_classes=10)

	# model with 2 convolutional and one fully connected layer
	model = Sequential()
	model.add(Conv2D(64, (4, 4),
									 activation='relu',
									 padding='same',
									 input_shape=(32, 32, 3)))
	model.add(Dropout(0.2))
	model.add(Conv2D(64, (2, 2),
									 strides=(2, 2),
									 activation='relu',
									 padding='same'))
	model.add(Dropout(0.2))
	model.add(Conv2D(32, (3, 3),
									 activation='relu',
									 padding='same'))
	model.add(Dropout(0.2))
	model.add(Conv2D(32, (3, 3),
									 activation='relu',
									 padding='same'))
	model.add(MaxPool2D(pool_size=(2, 2), strides=2))
	model.add(Dropout(0.2))
	model.add(Flatten())
	model.add(Dense(64, activation='relu'))
	model.add(Dropout(0.2))
	model.add(Dense(64, activation='relu'))
	model.add(Dropout(0.2))
	model.add(Dense(10, activation='softmax'))
	model.compile(loss='categorical_crossentropy',
								optimizer='adam',
								metrics=['accuracy'])
	model.summary()
	history = model.fit(train_images, train_labels,
											validation_data=(test_images, test_labels),
											epochs=EPOCHS,
											batch_size=BATCH_SIZE,
											verbose=2,
											shuffle=True)


# %%
import os
import glob

from collections import OrderedDict
import csv
# from datetime import datetime, date, timedelta
import datetime
from typing import Dict
from dagster_pandas import DataFrame
import pandas as pd
from faker import Faker
import random
from faker.providers import DynamicProvider
from faker_music import MusicProvider

EMPLOYEES = random.randint(2,4)
ACTIVATIONS = random.randint(45,175)
DEACTIVATIONS = random.randint(5,20)
CONTENT = 30

BACKFILL_DAYS = 2 # number of days to backfill_DAYS

PATH_TO_FAKE_DATA = '/home/dave/data-engineering/dbt-template-project/fake-data-generation/fake-data'

skill_provider = DynamicProvider(
     provider_name="skills",
     elements=["Big Ears", "Sining", "Song Writing", "Being Cool", "Big a Brain"],
)

subscription_type_provider = DynamicProvider(
     provider_name="subscription_type",
     elements=["Standard", "Big banana", "Speed"],
)

subscription_termination_reason_provider = DynamicProvider(
     provider_name="subscription_termination_reason",
     elements=["To expensive", "Big banana not big enough", "To Slow", "Poor Quality Content"],
)

web_event_type_provider = DynamicProvider(
     provider_name="web_event_type",
     elements=OrderedDict([("content_viewed",0.80), ("pricing_viewed", 0.05), ("content_shared", 0.05), ("comment_posted",0.1)]),
)

def fake_data_generation_content(current_timestamp, records, fake_data_employees):
    fake = Faker('en_AU')

    fake.add_provider(MusicProvider)

    content = []

    len_emp = len(fake_data_employees)

    fake.add_provider(skill_provider)

    for i in range(records):
        rnd_emp = random.randint(0, len_emp-1)

        content.append({
                "Content Name": fake.word(),
                "Content ID": fake.uuid4(),
                "Genre": fake.music_genre(),
                "Sub Genre": fake.music_subgenre(),
                "Instrument": fake.music_instrument(),
                "Instrument Category": fake.music_instrument_category(),
                "Email": fake_data_employees[rnd_emp]['Email'],
                "effective_from_ts": current_timestamp
                })

    return content

def fake_data_generation_employees(current_timestamp, records):
    fake = Faker('en_AU')

    employee = []

    fake.add_provider(skill_provider)

    for i in range(records):
        first_name = fake.first_name()
        last_name = fake.last_name()

        employee.append({
                "First Name": first_name,
                "Last Name": last_name,
                "Birth Date" : fake.date(pattern="%Y-%m-%d", end_datetime=datetime.date(1995, 1,1)),
                "Email": str.lower(f"{first_name}.{last_name}@fake_domain-2.com"),
                "Hobby": fake.word(),
                "Experience" : random.randint(0,15),
                "Start Date": current_timestamp,
                "Salary": random.randrange(75000,150000, 5000),
                "City" : fake.city(),
                "Nationality" : fake.country(),
                "Skill": fake.skills(),
                "effective_from_ts": current_timestamp
                })

    return employee

def fake_data_generation_subscription_events(current_timestamp, records):
    fake = Faker('en_AU')

    subscription_events = []

    fake.add_provider(subscription_type_provider)

    for i in range(records):
        first_name = fake.first_name()
        last_name = fake.last_name()

        # hours since activating
        n = random.randint(0,3)

        subscription_events.append({
                "Event ID": fake.uuid4(),
                "First Name": first_name,
                "Last Name": last_name,
                "Birth Date" : fake.date(pattern="%Y-%m-%d", end_datetime=datetime.date(1995, 1,1)),
                "Email": str.lower(f"{first_name}.{last_name}@fake_domain-2.com"),
                "Start Date": current_timestamp - + datetime.timedelta(hours=n),
                "City" : fake.city(),
                "Nationality" : fake.country(),
                "Subscription Type": fake.subscription_type(),
                "effective_from_ts": current_timestamp
                })

    return subscription_events

def fake_data_generation_subscription_deactivate_events(current_timestamp, records):
    fake = Faker('en_AU')

    subscription_events = []

    fake.add_provider(subscription_termination_reason_provider)

    for i in records:

        # disconnection hours since activating
        n = random.randint(0,15)

        subscription_events.append({
                "Event ID": fake.uuid4(),
                "Email": i['Email'],
                "End Date": i["Start Date"] + datetime.timedelta(hours=n),
                "Subscription Termination Reason": fake.subscription_termination_reason(),
                "effective_from_ts": current_timestamp
                })

    return subscription_events

def fake_data_web_events(current_timestamp, records, content):
    fake = Faker('en_AU')

    web_events = []

    content_len = len(content)

    fake.add_provider(web_event_type_provider)

    for i in records:
        for j in range(random.randint(0,15)):
            # start event
            n = random.randint(0,15)
            # event length
            l = random.randint(0,15)

            event_type = fake.web_event_type()

            event_start = i["Start Date"] + datetime.timedelta(hours=n)

            web_events.append({
                    "Event ID": fake.uuid4(),
                    "Email": i['Email'],
                    "Event Start": event_start,
                    "Event End": event_start + datetime.timedelta(seconds=n*60) if event_type == 'content_viewed' else None,
                    "Web Event Type": event_type,
                    "Content ID": content[random.randint(0,content_len-1)]['Content ID'] if 'content' in event_type else None,
                    "effective_from_ts": current_timestamp
                    })

    return web_events

def remove_fake_old_data():
    files = glob.glob(f'{PATH_TO_FAKE_DATA}/*.csv')

    for f in files:
        try:
            # f.unlink()
            os.remove(f)
        except OSError as e:
            print("Error: %s : %s" % (f, e.strerror))

# %%
def generate_fake_data() -> Dict[str, pd.DataFrame]:
    remove_fake_old_data()
    for i in range(BACKFILL_DAYS):

        current_execution_timestamp = datetime.datetime.now() - datetime.timedelta(days=i)

        fake_data_employees = fake_data_generation_employees(current_execution_timestamp, EMPLOYEES)
        fake_content = fake_data_generation_content(current_execution_timestamp, CONTENT, fake_data_employees)
        fake_sub_activate = fake_data_generation_subscription_events(current_execution_timestamp, ACTIVATIONS)
        fake_sub_deactivate = fake_data_generation_subscription_deactivate_events(current_execution_timestamp, fake_sub_activate[0:DEACTIVATIONS])
        fake_web_events = fake_data_web_events(current_execution_timestamp, fake_sub_activate, fake_content)

        file_partition = current_execution_timestamp.strftime("%Y%m%dT%H%M%S")

        df_fake_content = pd.DataFrame(fake_content)
        df_fake_data_employees = pd.DataFrame(fake_data_employees)
        df_fake_sub_activate = pd.DataFrame(fake_sub_activate)
        df_fake_sub_deactivate= pd.DataFrame(fake_sub_deactivate)
        df_fake_web_events = pd.DataFrame(fake_web_events)

        # df_fake_web_events.head()

        df_fake_content.to_csv(f'{PATH_TO_FAKE_DATA}/fake_content_{file_partition}.csv', index=False)
        df_fake_data_employees.to_csv(f'{PATH_TO_FAKE_DATA}/fake_data_employees_{file_partition}.csv', index=False)
        df_fake_sub_activate.to_csv(f'{PATH_TO_FAKE_DATA}/fake_sub_activate_{file_partition}.csv', index=False)
        df_fake_sub_deactivate.to_csv(f'{PATH_TO_FAKE_DATA}/fake_sub_deactivate_{file_partition}.csv', index=False)
        df_fake_web_events.to_csv(f'{PATH_TO_FAKE_DATA}/fake_web_events_{file_partition}.csv', index=False)

# %%

if __name__ == '__main__':
    dict_of_df = generate_fake_data()


# Author: Jonah Ada
# Date: February 5th, 2023
# Description:
# Helper module to define action token actions in transmuter device

from __future__ import annotations
# these imports will not be imported in the runtime, it is just to help coding to do type_checking
from typing import TYPE_CHECKING
if TYPE_CHECKING:
    from env.entities.player import Player
    from env.engine import Engine
    # from env.entities.energy import EnergyTile
    pass

from env.entities.action_tokens import ActionType
from env.entities.turn_state import TurnType
from random import randint
from env.helpers.logger import Logger

# index is the index of the transmuter tile that you want to take action on
# main game rules that defines how to get an action token given resources
def take_action(player: Player, engine: Engine, index: int):
    if index < 0 or index > 4:
        Logger.log('index is suppose to be between [0, 4]. Current index is not valid: ' + str(index), 'ACTION_LOGS')
        return False
    transmuter_tile = player.transmuter.active_tiles[index]  # this is TransmuterTile object
    bottom_energies = transmuter_tile.bottom  # this is a list
    num_energies = len(bottom_energies) - bottom_energies.count(0)
    if num_energies > 0:
        energy = transmuter_tile.release_bottom_energy()
        player.exhausted_energies[energy.energy_type].append(energy)

        action_token = player.transmuter.get_action_token(index)
        if action_token == 'ANY':
            new_index = _get_random_token()
            action_token = player.transmuter.get_action_token(new_index)
        if action_token.type == ActionType.MOVE:
            if _take_move_action(engine, action_token):
                Logger.log('Move action completed successfuly', 'ACTION_LOGS')
                return True
            else:
                Logger.log('Move action could NOT be completed', 'ACTION_LOGS')
                return False
        elif action_token.type == ActionType.FILL_TRANSFORMATIVE:
            if _take_transformative_action(player):
                Logger.log('Transformative action completed successfuly', 'ACTION_LOGS')
                return True
            else:
                Logger.log('Transformative action could NOT be completed', 'ACTION_LOGS')
                return False
        elif action_token.type == ActionType.FILL_SENTINENT:
            if _take_sentient_action(player):
                Logger.log('Sentient action completed successfuly', 'ACTION_LOGS')
                return True
            else:
                Logger.log('Sentient action could NOT be completed', 'ACTION_LOGS')
                return False
        elif action_token.type == ActionType.RELEASE_ENERGY:
            if _take_release_energy_action(player, action_token):
                Logger.log('Release energy action completed successfuly', 'ACTION_LOGS')
                return True
            else:
                Logger.log('Release energy action could NOT be completed', 'ACTION_LOGS')
                return False
        elif action_token.type == ActionType.RECHARGE:
            _take_recharge_action(player)
        else:
            Logger.log('Not a valid action', 'ACTION_LOGS')
            return False
    else:
        Logger.log('No energy in the bottom tiles', 'ACTION_LOGS')
        return False

# defines game rules to implement the action mask
def is_take_action_legal(player: Player, engine: Engine, index: int):
    is_legal = False
    if(not engine.turn.get_turn_type() == TurnType.ACTION_TURN):
        return False
    if index < 0 or index > 4:
        Logger.log('index is suppose to be between [0, 4]. Current index is not valid: ' + str(index), 'ACTION_LOGS')
        return False
    action_token = player.transmuter.get_action_token(index)
    if action_token == 'ANY':
        new_index = _get_random_token()
        action_token = player.transmuter.get_action_token(new_index)
    transmuter_tile = player.transmuter.active_tiles[index]  # this is TransmuterTile object
    bottom_energies = transmuter_tile.bottom  # this is a list
    num_energies = len(bottom_energies) - bottom_energies.count(0)
    if action_token.type == ActionType.MOVE or action_token.type == ActionType.RECHARGE:
        if num_energies > 0:
            is_legal = True
    elif action_token.type == ActionType.RELEASE_ENERGY:
        if num_energies > 0:
            available_tiles = []
            for i in range(0, len(action_token.transmuter_tiles)):
                if action_token.transmuter_tiles[i] == 1 and ((len(player.transmuter.active_tiles[i].top) - player.transmuter.active_tiles[i].top.count(0)) > 0):
                    available_tiles.append(i)
            if len(available_tiles) > 0:
                is_legal = True
    return is_legal

# helper function for movement action tokens
def _take_move_action(engine: Engine, action_token):
    # TODO: discuss how to implement this
    # move_times = action_token.move_times
    engine.turn.turn_type = TurnType.MOVE_TURN
    return True

# helper function to take transformative track action token
def _take_transformative_action(player: Player):
    player.transformative_track.is_token_enable = True

# helper function to take sentient track action token
def _take_sentient_action(player: Player):
    player.sentient_track.is_token_enable = True

# helper function to take release energy action token
def _take_release_energy_action(player: Player, action_token):
    available_tiles = []
    for i in range(0, len(action_token.transmuter_tiles)):
        # print('top tiles is:', player.transmuter.active_tiles[i].top, 'and condition is:', action_token.transmuter_tiles[i] == 1 and ((len(player.transmuter.active_tiles[i].top) - player.transmuter.active_tiles[i].top.count(0)) > 0))
        # print('action_token.transmuter_tiles[i] is:', action_token.transmuter_tiles[i])
        # print('cond 1 is:', action_token.transmuter_tiles[i] == 1, 'and cond2 is:', ((len(player.transmuter.active_tiles[i].top) - player.transmuter.active_tiles[i].top.count(0)) > 0))
        if action_token.transmuter_tiles[i] == 1 and ((len(player.transmuter.active_tiles[i].top) - player.transmuter.active_tiles[i].top.count(0)) > 0):
            available_tiles.append(i)
    # print('AVAILABLE TILES:', available_tiles)
    if len(available_tiles) <= 0:
        Logger.log('not enough energies', 'ACTION_LOGS')
        return False
    rand_num = randint(0, len(available_tiles) - 1)
    # print('rand_num is:', rand_num)
    index = available_tiles[rand_num]
    transmuter_tile = player.transmuter.active_tiles[index]
    if (len(transmuter_tile.top) - transmuter_tile.top.count(0)) > 0:
        Logger.log('energies BEFORE: ' + str(player.energies_released), 'ACTION_LOGS')
        energy = transmuter_tile.release_top_energy()
        player.energies_released[energy.energy_type].append(energy)
        Logger.log('energies AFTER: ' + str(player.energies_released), 'ACTION_LOGS')
        return True

# helper function to recharge player's channel marker action token
def _take_recharge_action(player: Player):
    player.channel_marker = True

# helper function to generate random integer within token indexes
def _get_random_token() -> int:
    return randint(0, 3)


#Exercise14
    #Create a function that takes a list and returns a list with no duplicates

def remove_duplicates(OGlist):
    OGlist = set(OGlist)
    return list(OGlist)

a = [1,2,3,4,5,4,3,2,1,]
b = remove_duplicates(a)

print(a)
print(b)


from math import gcd

class Rational:
  def __init__(self, num:int, den:int):
    """
    La forma de representar y de parsear fracciones es:
    a/b o simlpemente a donde a y b son enteros. o bien un entero seguido coma y de varios dígitos\n
    El numerador puede tener un menos para indicar el signo.

    A la hora de instanciar un racional, se deben de cumplir las sigueintes condiciones:\n
    1) El denominador debe de ser distinto de 0 \n
    2) Si se puede simplificar a un entero, entonces se simplifica a un entero
    3) el deniominador debe de ser positivo, para ello, si no lo es, se le cambia el signo a ambos números
    4) se ha de poner siempre como la fracción irreducible. Esto se hace dividiendo num y den por su mcd
    """
    if not isinstance(num, int):
      raise Exception("El numerador debe de ser un entero cacho de tonto.")
    elif not isinstance(den, int):
      raise Exception("El denominador debe de ser un entero, pedazo de mierda.")
    elif den == 0:
      raise Exception("El denominador no puede ser cero pedazo de imbécil.")
    if den < 0:
      num, den = (-1)*num, (-1)*den
    if num % den == 0:
      num, den = num //den, 1
    # ponemos como fracción irreducible
    c = gcd(num,den)
    num, den = num//c, den//c
    self.num = num
    self.den = den

  def __repr__(self):
    if self.den == 1:
      return str(self.num)
    else:
      return str(self.num)+"/"+str(self.den)

  def __str__(self):
    if self.den == 1:
      return str(self.num)
    else:
      return str(self.num)+"/"+str(self.den)

  def printLatex(self):
    """
      Pone el racional listo como para ser escrito en LaTeX
    """
    if self.den == 1:
      return str(self.num)
    else:
      return "frac{" + str(self.num) + "}{" + str(self.den) + "}"

  def __abs__(self):
    """
      Valor absoluto del racional
    """
    return Rational(abs(self.num),self.den)

  def parteEntera(self):
    """
      Devuleve la parte entera de un Racional.\n es decir si p <= x < p+1 se devuelve p
    """
    if self.den == 1:
      return Rational(self.num, 1)
    a = self.num // self.den
    return Rational(a,1)

  def parteSuperior(self):
    """
      Devueleve la parte entera superior, \n es decir si p < x <= p+1 se devuelve p+1
    """
    if self.den == 1:
      return Rational(self.num, 1)
    a = self.num // self.den
    return Rational(a+1,1)

  def __add__(self, n):
    """
      La suma de dos racionales
    """
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden sumar racionales gilipollas")
    a = self.num * n.den + self.den * n.num
    b = self.den * n.den
    return Rational(a,b)

  def __sub__(self, n):
    """
      La resta de dos racionales
    """
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden restar racionales gilipollas")
    a = self.num * n.den - self.den * n.num
    b = self.den * n.den
    return Rational(a,b)

  def __mul__(self, n):
    """
      La multiplicación de dos racionales
    """
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden multiplicar racionales gilipollas")
    a = self.num * n.num
    b = self.den * n.den
    return Rational(a,b)

  def __truediv__(self, n):
    """
      La división de dos racionales
    """
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden cocientar racionales gilipollas")
    # Comprobamos que por el que se divide no es cero
    if n.num == 0:
      raise Exception("Estás dividiendo por 0 soplapollas. ¿No has ido al cole o qué?")
    a = self.num * n.den
    b = self.den * n.num
    return Rational(a,b)

  def __mod__(self, n):
    if not isinstance(n, Rational):
      raise Exception("Tienes que meter un racioanl para hacer módulo gilipollas")
    # Comprobamos que n es un entero
    elif n.den != 1:
      raise Exception("Tienes que meter un entero para hacer módulo gilipollas")
    elif self.den != 1:
      raise Exception("Solo se pueden hacer módulos con enteros gilipollas")
    a = self.num % n.num
    return Rational(a,1)

  def __pow__(self, n):
    if not isinstance(n, Rational):
      raise Exception("Tienes que meter un racional gilipollas")
    elif n.den != 1:
      raise Exception("Tienes que meter un entero gilipollas")
    a = (self.num)^n.num
    b = (self.den)^n.num
    return Rational(a,b)

  def __lt__(self, n):
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden comparar racionales gilipollas")
    return self.num * n.den < self.den * n.num

  def __le__(self, n):
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden comparar racionales gilipollas")
    return self.num * n.den <= self.den * n.num

  def __eq__(self, n):
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden comparar racionales gilipollas")
    return self.num * n.den == self.den * n.num

  def __ne__(self, n):
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden comparar racionales gilipollas")
    return not (self == n)

  def __ge__(self, n):
    if not isinstance(n, Rational):
      raise Exception("Solo se pueden comparar racionales gilipollas")
    return self.num * n.den >= self.den * n.num

  def factorial(self):
    if self.den != 1:
      raise Exception("Deberías de saber que el factorial es solo para enteros hijo de puta")
    if self.num == 1 or self.num == 0:
      return Rational(1,1)
    cont = 1
    for i in range(1,self.num +1):
      cont *= i
    return Rational(cont,1)

  def phi(self):
    if self.den != 1:
      raise Exception("Tu abuela sabe que phi es para enteros y tú no, que lo haces con fracciones con la cara de saltamontes que tienes")
    pass

  def factores(self):
    """
      Lista de los enteros que dividen al entero dado
    """
    if self.den != 1:
      raise Exception("Te va a factorizar la fracción tu puta madre")
    if self == Rational(1,1):
      return [Rational(1,1)]
    divisores = [Rational(1,1)]
    for i in range(2,int(self.num/2)):
      if self.num % i == 0:
        c = Rational(i,1)
        divisores.append(c)
    divisores.append(Rational(self.num, 1))
    return divisores

  def factoresPrimos(self):
    if self.den != 1:
      raise Exception("Eres tan tonto que pensabas que Q era un DFU")
    lista = [elem for elem in self.factores() if elem.esPrimo()]
    return lista

  def esPrimo(self):
    if self.den != 1:
      raise Exception("Tú si que eres primo")
    return len(self.factores()) == 2

  def periodo(self):
    pass

  def lenPeriodo(self):
    pass

  def digitoEnPos(self, n:int):
    if not isinstance(n, int):
      raise Exception("Siempre el mismo fallo capullo.")
    # Caso trivial de que sea un entero
    if self.den == 1:
      return 0
    pass

  def ratio2float(self):
    return self.num / self.den

  def parteDecimal(self):
    return self - self.parteEntera()

  def esEntero(self):
    return self.den == 1

def min(a:Rational,b:Rational):
  if a >= b:
    return b
  else:
      return a

def max(a:Rational,b:Rational):
  if a >= b:
    return a
  else:
    return b

def mcd(a:Rational,b:Rational):
  if a.num != 1 or b.num != 1:
    raise Exception("Eres más tonto que alguien que no sabe lo que es un DIP")
  c = gcd(a.num, b.num)
  return Rational(c,1)

def mcm(a:Rational,b:Rational):
  if a.num != 1 or b.num != 1:
    raise Exception("Eres más tonto que alguien que no sabe lo que es un DIP")
  c = gcd(a.num, b.num)
  m = (a.num * b.num)//c
  return Rational(m,1)

def bezout(a:Rational, b:Rational):
  pass

def quoRem(a:Rational, b:Rational):
  if a.den != 1 or b.den != 1:
    raise Exception("No se puede hacer division euclidea de dos fracciones parguelas")
  c, r = a.num // b.num, a.num % b.num
  return (Rational(c,1), Rational(r,1))

a = Rational(47,8)
b = Rational(7,2)
print(a+b, a*b, a/b, a-b, a^Rational(4,1), a==b, a<=b)


"""litreview2 URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.conf import settings
from django.conf.urls.static import static
from django.contrib import admin
from django.urls import path
from litreview import views

import authentication.views

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', authentication.views.login_page, name="login"),
    path('register', authentication.views.signup_page, name="signup"),
    path('logout/', authentication.views.logout_user, name='logout'),
    path('flux', views.flux_page, name="flux"),
    path('ticket_creation', views.ticket_creation, name="ticket-creation"),
    path("own_posts", views.own_posts, name='posts'),
    path('review_after_ticket/<int:ticket_id>/', views.review_after_ticket, name='review-after-ticket'),
    path('review_from_scratch', views.review_from_scratch, name='review-from-scratch'),
    path('ticket/<int:ticket_id>/edit', views.edit_ticket, name='edit-ticket'),
    path("own_posts/<int:ticket_id>/delete", views.delete_ticket, name="delete-ticket"),
    path('review/<int:review_id>/edit', views.edit_review, name='edit-review'),
    path('review/<int:review_id>/delete', views.delete_review, name='delete-review'),
    path('follow-users', views.follow_user, name='follow-user'),
    path('unfollow/<int:relation_id>', views.end_follow, name='end-follow'),
]

if settings.DEBUG:
    urlpatterns += static(
        settings.MEDIA_URL, document_root=settings.MEDIA_ROOT
    )


#!/usr/bin/env python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import os
import tempfile
from unittest import mock

import mozunit

from mozperftest.system.profile import Profile, ProfileNotFoundError
from mozperftest.tests.support import get_running_env

def test_profile():
    mach_cmd, metadata, env = get_running_env()

    with Profile(env, mach_cmd) as profile:
        profile(metadata)
        profile_dir = env.get_arg("profile-directory")
        assert os.path.exists(profile_dir)

    assert not os.path.exists(profile_dir)

CALLS = [0]

def _return_profile(*args, **kw):
    if CALLS[0] == 0:
        CALLS[0] = 1
        raise ProfileNotFoundError()

    tempdir = tempfile.mkdtemp()

    return tempdir

@mock.patch("mozperftest.system.profile.get_profile", new=_return_profile)
def test_conditionedprofile():
    mach_cmd, metadata, env = get_running_env(profile_conditioned=True)

    with Profile(env, mach_cmd) as profile:
        profile(metadata)
        profile_dir = env.get_arg("profile-directory")
        assert os.path.exists(profile_dir)

    assert not os.path.exists(profile_dir)

if __name__ == "__main__":
    mozunit.main()