import hashlib
import inspect
import operator
from typing import Callable, Optional, Union

import cloudpickle
import numpy as np

class _FakeArgSpec:
    def __init__(
        self,
        args=None,
        varargs=None,
        varkw=None,
        defaults=None,
        kwonlyargs=None,
        kwonlydefaults=None,
        annotations=None,
    ):
        self.args = args
        self.varargs = varargs
        self.varkw = varkw
        self.defaults = defaults
        self.kwonlyargs = kwonlyargs
        self.kwonlydefaults = kwonlydefaults
        self.annotations = annotations

def function_repr(
    func: Callable,
    argspec: Optional[Union[_FakeArgSpec, inspect.FullArgSpec]] = None,
) -> str:
    """Returns a human-readable string representation for a function."""
    if argspec is None:
        argspec = inspect.getfullargspec(func)
    args = [str(arg) for arg in argspec.args]

    if argspec.defaults:
        for i, val in enumerate(argspec.defaults[::-1]):
            args[-(i + 1)] = args[-(i + 1)] + f"={val!r}"

    if argspec.varargs:
        args.append("*" + argspec.varargs)

    if argspec.kwonlyargs:
        if not argspec.varargs:
            args.append("*")
        args.extend(argspec.kwonlyargs)
    if argspec.kwonlydefaults:
        for i, name in enumerate(args):
            if name in argspec.kwonlydefaults:
                args[i] = args[i] + f"={argspec.kwonlydefaults[name]!r}"
    if argspec.varkw:
        args.append("**" + argspec.varkw)

    if argspec.annotations:
        for i, name in enumerate(args):
            if name in argspec.annotations:
                args[i] = args[i] + f": {argspec.annotations[name].__name__!r}"

    return func.__name__ + "(" + ", ".join(args) + ")"

class Parameter:
    """A callable object that computes a scalar or vector quantity
    as a function of position coordinates x, y (and optionally z and time t).

    Addition, subtraction, multiplication, and division
    between multiple Parameters and/or real numbers (ints and floats)
    is supported. The result of any of these operations is a
    ``CompositeParameter`` object.

    Args:
        func: A callable/function that actually calculates the parameter's value.
            The function must take x, y (and optionally z) as the first and only
            positional arguments, and all other arguments must be keyword arguments.
            Therefore func should have a signature like
            ``func(x, y, z, a=1, b=2, c=True)``, ``func(x, y, *, a, b, c)``,
            ``func(x, y, z, *, a, b, c)``, or ``func(x, y, z, *, a, b=None, c=3)``.
            For time-dependent Parameters, ``func`` must also take time ``t`` as a
            keyword-only argument.
        time_dependent: Specifies that ``func`` is a function of time ``t``.
        kwargs: Keyword arguments for func.
    """

    __slots__ = ("func", "kwargs", "time_dependent", "_cache")

    def __init__(self, func: Callable, time_dependent: bool = False, **kwargs):
        argspec = inspect.getfullargspec(func)
        args = argspec.args
        num_args = 2
        if args[:num_args] != ["x", "y"]:
            raise ValueError(
                "The first function arguments must be x and y, "
                f"not {', '.join(args[:num_args])!r}."
            )
        if "z" in args:
            if args.index("z") != num_args:
                raise ValueError(
                    "If the function takes an argument z, "
                    "it must be the third argument (x, y, z)."
                )
            num_args = 3
        defaults = argspec.defaults or []
        if len(defaults) != len(args) - num_args:
            raise ValueError(
                "All arguments other than x, y, z must be keyword arguments."
            )
        self.time_dependent = time_dependent
        defaults_dict = dict(zip(args[num_args:], defaults))
        kwonlyargs = set(kwargs) - set(argspec.args[num_args:])
        if not kwonlyargs.issubset(set(argspec.kwonlyargs or [])):
            raise ValueError(
                f"Provided keyword-only arguments ({kwonlyargs!r}) "
                f"do not match the function signature: {function_repr(func)}."
            )
        defaults_dict.update(argspec.kwonlydefaults or {})

        self.func = func
        self.kwargs = defaults_dict
        self.kwargs.update(kwargs)
        self._cache = {}

        if self.time_dependent and "t" not in argspec.kwonlyargs:
            raise ValueError(
                "A time-dependent Parameter must take time t as a keyword argument."
            )

    def _hash_args(self, x, y, z, t) -> str:
        def _coerce_to_tuple(a):
            try:
                return tuple(_coerce_to_tuple(i) for i in a)
            except TypeError:
                return a

        def _to_tuple(items):
            results = []
            for key, value in items:
                if isinstance(value, dict):
                    value = _to_tuple(value.items())
                elif isinstance(value, (list, np.ndarray)):
                    value = _coerce_to_tuple(value)
                results.append((key, value))
            return tuple(results)

        return (
            hex(hash(_to_tuple(self.kwargs.items())))
            + hashlib.sha1(np.ascontiguousarray(x)).hexdigest()
            + hashlib.sha1(np.ascontiguousarray(y)).hexdigest()
            + hashlib.sha1(np.ascontiguousarray(z)).hexdigest()
            + hex(hash(t))
        )

    def __call__(
        self,
        x: Union[int, float, np.ndarray],
        y: Union[int, float, np.ndarray],
        z: Optional[Union[int, float, np.ndarray]] = None,
        t: Optional[float] = None,
    ) -> Union[int, float, np.ndarray]:
        cache_key = self._hash_args(x, y, z, t)
        if cache_key not in self._cache:
            kwargs = self.kwargs.copy()
            if t is not None:
                kwargs["t"] = t
            x, y = np.atleast_1d(x, y)
            if z is not None:
                kwargs["z"] = np.atleast_1d(z)
            result = np.asarray(self.func(x, y, **kwargs)).squeeze()
            if result.ndim == 0:
                result = result.item()
            self._cache[cache_key] = result
        return self._cache[cache_key]

    def _get_argspec(self) -> _FakeArgSpec:
        if self.kwargs:
            kwargs, kwarg_values = list(zip(*self.kwargs.items()))
        else:
            kwargs = []
            kwarg_values = []
        kwargs = list(kwargs)
        kwarg_values = list(kwarg_values)
        if self.time_dependent:
            kwargs.insert(0, "time_dependent")
            kwarg_values.insert(0, True)
        return _FakeArgSpec(args=kwargs, defaults=kwarg_values)

    def __repr__(self) -> str:
        func_repr = function_repr(self.func, argspec=self._get_argspec())
        return f"{self.__class__.__name__}<{func_repr}>"

    def __add__(self, other) -> "CompositeParameter":
        """self + other"""
        return CompositeParameter(self, other, operator.add)

    def __radd__(self, other) -> "CompositeParameter":
        """other + self"""
        return CompositeParameter(other, self, operator.add)

    def __sub__(self, other) -> "CompositeParameter":
        """self - other"""
        return CompositeParameter(self, other, operator.sub)

    def __rsub__(self, other) -> "CompositeParameter":
        """other - self"""
        return CompositeParameter(other, self, operator.sub)

    def __mul__(self, other) -> "CompositeParameter":
        """self * other"""
        return CompositeParameter(self, other, operator.mul)

    def __rmul__(self, other) -> "CompositeParameter":
        """other * self"""
        return CompositeParameter(other, self, operator.mul)

    def __truediv__(self, other) -> "CompositeParameter":
        """self / other"""
        return CompositeParameter(self, other, operator.truediv)

    def __rtruediv__(self, other) -> "CompositeParameter":
        """other / self"""
        return CompositeParameter(other, self, operator.truediv)

    def __pow__(self, other) -> "CompositeParameter":
        """self ** other"""
        return CompositeParameter(self, other, operator.pow)

    def __rpow__(self, other) -> "CompositeParameter":
        """other ** self"""
        return CompositeParameter(other, self, operator.pow)

    def __eq__(self, other) -> bool:
        if other is self:
            return True

        if not isinstance(other, Parameter):
            return False

        # Check if function bytecode is the same
        if self.func.__code__ != other.func.__code__:
            return False

        if set(self.kwargs) != set(other.kwargs):
            return False

        def array_safe_equals(a, b) -> bool:
            """Check if a and b are equal, even if they are numpy arrays."""
            if a is b:
                return True
            if isinstance(a, np.ndarray) and isinstance(b, np.ndarray):
                return a.shape == b.shape and np.allclose(a, b)
            try:
                return a == b
            except TypeError:
                return NotImplemented

        for key in self.kwargs:
            if not array_safe_equals(self.kwargs[key], other.kwargs[key]):
                return False

        return True

class CompositeParameter(Parameter):
    """A callable object that behaves like a Parameter
    (i.e. it computes a scalar or vector quantity as a function of
    position coordinates x, y, z). A CompositeParameter object is created as
    a result of mathematical operations between Parameters, CompositeParameters,
    and/or real numbers.

    Addition, subtraction, multiplication, division, and exponentiation
    between Parameters, CompositeParameters and real numbers (ints and floats)
    are supported. The result of any of these operations is a new
    CompositeParameter object.

    Args:
        left: The object on the left-hand side of the operator.
        right: The object on the right-hand side of the operator.
        operator_: The operator acting on left and right (or its string representation).
    """

    VALID_OPERATORS = {
        operator.add: "+",
        operator.sub: "-",
        operator.mul: "*",
        operator.truediv: "/",
        operator.pow: "**",
    }

    def __init__(
        self,
        left: Union[int, float, Parameter, "CompositeParameter"],
        right: Union[int, float, Parameter, "CompositeParameter"],
        operator_: Union[Callable, str],
    ):
        valid_types = (int, float, complex, Parameter, CompositeParameter)
        if not isinstance(left, valid_types):
            raise TypeError(
                f"Left must be a number, Parameter, or CompositeParameter, "
                f"not {type(left)!r}."
            )
        if not isinstance(right, valid_types):
            raise TypeError(
                f"Right must be a number, Parameter, or CompositeParameter, "
                f"not {type(right)!r}."
            )
        if isinstance(left, (int, float)) and isinstance(right, (int, float)):
            raise TypeError(
                "Either left or right must be a Parameter or CompositeParameter."
            )
        if isinstance(operator_, str):
            operators = {v: k for k, v in self.VALID_OPERATORS.items()}
            operator_ = operators.get(operator_.strip(), None)
        if operator_ not in self.VALID_OPERATORS:
            raise ValueError(
                f"Unknown operator, {operator_!r}. "
                f"Valid operators are {list(self.VALID_OPERATORS)!r}."
            )
        self.left = left
        self.right = right
        self.operator = operator_
        self.time_dependent = False
        if isinstance(self.left, Parameter) and self.left.time_dependent:
            self.time_dependent = True
        if isinstance(self.right, Parameter) and self.right.time_dependent:
            self.time_dependent = True

    def __call__(
        self,
        x: Union[int, float, np.ndarray],
        y: Union[int, float, np.ndarray],
        z: Optional[Union[int, float, np.ndarray]] = None,
        t: Optional[float] = None,
    ) -> Union[int, float, np.ndarray]:
        kwargs = dict() if t is None else dict(t=t)
        values = []
        for operand in (self.left, self.right):
            if isinstance(operand, Parameter):
                if operand.time_dependent:
                    value = operand(x, y, z, **kwargs)
                else:
                    value = operand(x, y, z)
            else:
                value = operand
            values.append(value)
        return self.operator(*values)

    def _bare_repr(self) -> str:
        op_str = self.VALID_OPERATORS[self.operator]
        if isinstance(self.left, CompositeParameter):
            left_repr = self.left._bare_repr()
        elif isinstance(self.left, Parameter):
            left_argspec = self.left._get_argspec()
            left_repr = function_repr(self.left.func, left_argspec)
        else:
            left_repr = str(self.left)

        if isinstance(self.right, CompositeParameter):
            right_repr = self.right._bare_repr()
        elif isinstance(self.right, Parameter):
            right_argspec = self.right._get_argspec()
            right_repr = function_repr(self.right.func, right_argspec)
        else:
            right_repr = str(self.right)

        return f"({left_repr} {op_str} {right_repr})"

    def __eq__(self, other) -> bool:
        if other is self:
            return True

        if not isinstance(other, type(self)):
            return False

        return (
            self.left == other.left
            and self.right == other.right
            and self.operator is other.operator
        )

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}<{self._bare_repr()}>"

    def __getstate__(self):
        state = self.__dict__.copy()
        state["left"] = cloudpickle.dumps(state["left"])
        state["right"] = cloudpickle.dumps(state["right"])
        return state

    def __setstate__(self, state):
        state["left"] = cloudpickle.loads(state["left"])
        state["right"] = cloudpickle.loads(state["right"])
        self.__dict__.update(state)

class Constant(Parameter):
    """A Parameter whose value doesn't depend on position or time."""

    def __init__(self, value: Union[int, float, complex], dimensions: int = 2):
        if dimensions not in (2, 3):
            raise ValueError(f"Dimensions must be 2 or 3, got {dimensions}.")
        if dimensions == 2:

            def constant(x, y, value=0):
                return value * np.ones_like(x)

        else:

            def constant(x, y, z, value=0):
                return value * np.ones_like(x)

        super().__init__(constant, value=value)


from odoo import api, models, fields, tools

import logging

_logger = logging.getLogger(__name__)

class MailActivityTeam(models.AbstractModel):
    _inherit = 'mail.activity.team'

    # image: all image fields are base64 encoded and PIL-supported
    image = fields.Binary(
        string="Image",
        attachment=True,
        help="This field holds the image used for this team, limited "
        "to 1024x1024px",
    )
    image_medium = fields.Binary(
        string="Medium-sized image",
        attachment=True,
        help="Medium-sized image of this team. It is automatically "
        "resized as a 128x128px image, with aspect ratio preserved. "
        "Use this field in form views or some kanban views."
    )
    image_small = fields.Binary(
        string="Small-sized image",
        attachment=True,
        help="Small-sized image of this team. It is automatically "
        "resized as a 64x64px image, with aspect ratio preserved. "
        "Use this field anywhere a small image is required."
    )

    @api.model_create_multi
    def create(self, vals_list):
        for vals in vals_list:
            tools.image_resize_images(vals)
        return super(MailActivityTeam, self).create(vals_list)

    @api.multi
    def write(self, vals):
        tools.image_resize_images(vals)
        return super(MailActivityTeam, self).write(vals)


class Solution:
    # @param A : list of integers
    # @return an integer
    def solve(self, A):
        length = len(A)
        left = 0
        right = length-1
        while left < right:
            mid = left + (right-left)//2
            if A[mid] == A[mid-1]:
                if (mid)%2 != 0:
                    left = mid+1
                else:
                    right = mid-1
            elif A[mid] == A[mid+1]:
                if mid%2 == 0:
                    left = mid+1
                else:
                    right = mid-1
            else:
                return A[mid]
        return A[left]

if __name__ == '__main__':
    print(Solution.solve(Solution,A =
    # [0,1, 1, 2, 2, 3, 3]))
    # [1, 1, 2, 2, 3]))
    [5, 11, 11, 100, 100]))


import cv2
from tracker import KCFTracker

def tracker(cam, frame, bbox):
    tracker = KCFTracker(True, True, True) # (hog, fixed_Window, multi_scale)
    tracker.init(bbox, frame)

    while True:
        ok, frame = cam.read()

        timer = cv2.getTickCount()
        bbox = tracker.update(frame)
        bbox = list(map(int, bbox))
        fps = cv2.getTickFrequency() / (cv2.getTickCount() - timer)

        # Tracking success
        p1 = (int(bbox[0]), int(bbox[1]))
        p2 = (int(bbox[0] + bbox[2]), int(bbox[1] + bbox[3]))
        cv2.rectangle(frame, p1, p2, (255, 0, 0), 2, 1)

        # Put FPS
        cv2.putText(frame, "FPS : " + str(int(fps)), (100, 50), cv2.FONT_HERSHEY_SIMPLEX, 0.75, (50, 170, 50), 2)

        cv2.imshow("Tracking", frame)

        # Exit if ESC pressed
        k = cv2.waitKey(1) & 0xff
        if k == 27:
            break

    cam.release()
    cv2.destroyAllWindows()

if __name__ == '__main__':
    video = cv2.VideoCapture(0)
    # ok, frame = video.read()
    ok, frame = video.read()
    bbox = cv2.selectROI('Select ROI', frame, False)

    if min(bbox) == 0: exit(0)
    tracker(video, frame, bbox)


#sample function
#accept a list of integers as its input
#output a new list consisting of any positive integers

#sample input: [1,-3,9,-2,7]
def sample_function_name(input_list):
    output_list = []

    for i in range(0, len(input_list)):
        if input_list[i] >= 0:
            output_list.append(input_list[i])

    return output_list

print(sample_function_nam([1,-3,9,-2,7]))


all_rucksacks = open("day3_input.txt").read().split("\n")

def get_item_prio(item: str) -> int:
    if 'a' <= item <= 'z':
        return ord(item) - 96
    if 'A' <= item <= 'Z':
        return ord(item) - 38
    else:
        breakpoint()

prio_score = 0
for rucksack in all_rucksacks:
    nb_items = len(rucksack) // 2
    first, second = rucksack[:nb_items], rucksack[nb_items:]
    wrong_item = [item for item in first if item in second][0]
    prio_score += get_item_prio(wrong_item)

print(prio_score)

prio_score = 0
i = 0
while i < len(all_rucksacks):
    common_item = [
        item
        for item in all_rucksacks[i]
        if item in all_rucksacks[i+1]
        and item in all_rucksacks[i+2]
    ][0]
    prio_score += get_item_prio(common_item)
    i += 3

print(prio_score)


from struct import unpack_from
from collections import namedtuple

from smbus2 import SMBus

Measurement = namedtuple("Measurement", "temperature,humidity,dust")

class DrsDust:

    REGISTER=0

    def __init__(self, bus=1, i2c_address=0x08):
        self.address = i2c_address
        self.bus = SMBus(bus)

    def measure(self):
        raw_data = self.bus.read_i2c_block_data(self.address, self.REGISTER, 12)
        return Measurement(*unpack_from('fff', bytes(raw_data)))

if __name__ == '__main__':
    ddust = DrsDust()
    m = ddust.measure()
    print("Temperature:  {:0.2f} \u2103".format(m.temperature))
    print("Humidity:     {:0.2f} %".format(m.humidity))
    print("Dust density: {:0.2f} ug/m\u00B3".format(m.dust))


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

# 作者：Sosai
# 时间：2021/10/29  10:11
# 名称：BST实现.PY
# 工具：PyCharm
class TreeNode:
    def __init__(self, val):
        self.data = val
        self.leftChild = None
        self.rightChild = None
        self.parent = None

class BinarySearchTree:
    def __init__(self, valList):
        self.root = None
        # 构造BST的方法
        self.root = self.insertNode(self.root, valList[0])  # 标记根节点，便于后续第一个传入根节点
        for i in range(1, len(valList)):  # 去除根节点使用过的数
            self.root = self.insertNode(self.root, valList[i])

    # BST搜索
    def searchNode(self, node, val):
        if node is None:  # 树为空
            return None
        else:  # 树不为空
            if val == node.data:
                return node
            elif val > node.data:
                find = self.searchNode(node.rightChild, val)
            elif val < node.data:
                find = self.searchNode(node.leftChild, val)
            else:
                raise "搜索时发生错误"
        return find

    # BST插入
    def insertNode(self, node, val):
        if node is None:  # 创造节点
            node = TreeNode(val)
        elif val > node.data:  # 值大于节点
            node.rightChild = self.insertNode(node.rightChild, val)  # node.rightChild = self...是为了连接父节点的右子节点
            node.rightChild.parent = node  # 将右子节点的父节点标记上
        elif val < node.data:  # 值小于节点
            node.leftChild = self.insertNode(node.leftChild, val)  # node.leftChild = self...是为了连接父节点的左子节点
            node.leftChild.parent = node  # 将左子节点的父节点标记上
        return node

    def postOrder(self, root):
        if root:
            self.postOrder(root.leftChild)
            print(root.data, end=" ")
            self.postOrder(root.rightChild)

    def __removeNode1_leaf__(self, node):  # 删除叶子节点
        if node.parent is None:  # 只有一个根节点
            self.root = None
        else:
            if node.parent.leftChild == node:  # 删除左子节点
                node.parent.leftChild = None
            else:  # 删除右子节点
                node.parent.rightChild = None

    def __removeNode2_left__(self, node):  # 有一个左子节点
        if node.parent is None:  # 要删除的是根节点
            self.root = node.leftChild
            node.leftChild.parent = None
        else:
            if node.leftChild and node == node.leftChild.parent:  # 后续节点是要删除节点的左节点
                node.leftChild.parent = node.parent
                node.parent.leftChild = node.leftChild
            else:  # 后续节点是要删除节点的右节点
                node.rightChild.parent = node.parent
                node.parent.leftChild = node.rightChild

    def __removeNode2_right__(self, node):  # 有一个右子节点
        if node.parent is None:  # 要删除的是根节点
            self.root = node.rightChild
            node.rightChild = None
        else:
            if node.leftChild and node == node.leftChild.parent:  # 后续节点是要删除节点的左节点
                node.leftChild.parent = node.parent
                node.parent.rightChild = node.leftChild
            else:  # 后续节点是要删除节点的右节点
                node.rightChild.parent = node.parent
                node.parent.rightChild = node.rightChild

    def deleteNode(self, val):
        if self.root:  # 不是空树
            node = self.searchNode(self.root, val)
            if node is None:  # 要删除的节点不存在
                raise "没有找到要删除的节点"
            else:
                if node.leftChild and node.rightChild is None:  # node是叶子节点
                    self.__removeNode1_leaf__(node)
                elif node.leftChild and not node.rightChild:  # node只有左子节点
                    self.__removeNode2_left__(node)
                elif not node.leftChild and node.rightChild:  # node只有右子节点
                    self.__removeNode2_right__(node)
                else:  # node两个子节点都有
                    minNode = node.rightChild
                    while minNode.leftChild:  # 找到最小节点
                        minNode = minNode.leftChild
                    node.data = minNode.data  # 替换
                    if minNode.rightChild:  # 移除最小节点
                        self.__removeNode2_right__(minNode)
                    else:
                        self.__removeNode1_leaf__(minNode)

if __name__ == '__main__':
    tree = BinarySearchTree([4, 5, 2, 3, 6])
    print(tree.searchNode(tree.root, 6))
    tree.postOrder(tree.root)
    print()
    tree.deleteNode(5)
    tree.postOrder(tree.root)
    tree.deleteNode(4)
    print()
    tree.postOrder(tree.root)

数据结构与算法/BST实现.py


# -*- coding: utf-8 -*-
"""
@file
@brief Function solving the TSP problem
"""

import random

def distance_point(p1, p2):
    """
    Returns the Euclidian distance between two points.
    Retourne la distance euclidienne entre deux points.

    @param      p1      point 1
    @param      p2      point 2
    @return             distance
    """
    d = 0
    for a, b in zip(p1, p2):
        d += (a - b) ** 2
    return d ** 0.5

def distance_circuit(points):
    """
    Computes the distance of this circuit.
    Calcule la longueur d'un circuit.

    @param      points      list of points, the circuit assumes they are giving in that order
    @return                 distance
    """
    d = 0
    for i in range(1, len(points)):
        d += distance_point(points[i - 1], points[i])
    return d + distance_point(points[0], points[-1])

def permutation(points, i, j):
    """
    Switches two points and returns a new path.
    Echange deux points et retourne le nouveau circuit.

    @param      points      circuit
    @param      i           first index
    @param      j           second index (< len(points))
    @return                 new circuit
    """
    points = points.copy()
    points[i], points[j] = points[j], points[i]
    return points

def reverse(points, i, j):
    """
    Reverses a sub part of circuit.
    Retourne une partie du circuit.

    @param      points      circuit
    @param      i           first index
    @param      j           second index (<= len(points))
    @return                 new circuit
    """
    points = points.copy()
    if i > j:
        i, j = j, i
    c = points[i:j]
    c.reverse()
    points[i:j] = c
    return points

def voyageur_commerce_simple(points):
    """
    Solves the TSP using basic permutations,
    points are 2D coordinates.
    Résoud le problème du voyageur de commerce.

    @param      points      list of points
    """
    d0 = distance_circuit(points)
    dnew = d0
    n = len(points) - 1
    first = True
    while dnew < d0 or first:
        first = False
        d0 = dnew

        # first pass : random
        for i in range(len(points)):
            h1 = random.randint(0, n)
            h2 = random.randint(0, n)
            p = permutation(points, h1, h2)
            d = distance_circuit(p)
            if d < dnew:
                dnew = d
                points = p

            h1 = random.randint(0, n)
            h2 = random.randint(h1 + 1, n + 1)
            p = reverse(points, h1, h2)
            d = distance_circuit(p)
            if d < dnew:
                dnew = d
                points = p

        # second pass : no reverse
        for i in range(len(points)):
            for j in range(i + 1, len(points) + 1):
                p = reverse(points, i, j)
                d = distance_circuit(p)
                if d < dnew:
                    dnew = d
                    points = p

    return points

def plot_circuit(points, ax=None, **kwargs):
    """
    Plots the circuit on a graph.
    Dessine la solution du voyageur de commerce.

    @param      points      points
    @param      ax          axe
    @param      kwargs      sent to ``plt.subplots``
    @return                 ax
    """
    if ax is None:
        import matplotlib.pyplot as plt
        _, ax = plt.subplots(**kwargs)

    ax.plot([_[0] for _ in points], [_[1] for _ in points], "o")

    p2 = points + [points[0]]
    ax.plot([_[0] for _ in p2], [_[1] for _ in p2], "-")
    return ax


def solution(n, arr1, arr2):
    answer = []

    for i in range(n):
        arr1_e = list(str(bin(arr1[i])[2:]).zfill(n))
        arr2_e = list(str(bin(arr2[i])[2:]).zfill(n))
        code = ''

        for j in range(len(arr1_e)):
            code += max(arr1_e[j], arr2_e[j])

        e = ''
        for c in code:
            if c == '1':
                e += "#"
            else:
                e += ' '

        answer.append(e)
    return answer


class Fooset(set):
    def __init__(self, s=(), foo=None):
        super(Fooset,self).__init__(s)
        if foo is None and hasattr(s, 'foo'):
            foo = s.foo
        self.foo = foo

    @classmethod
    def _wrap_methods(cls, names):
        def wrap_method_closure(name):
            def inner(self, *args):
                result = getattr(super(cls, self), name)(*args)
                if isinstance(result, set) and not hasattr(result, 'foo'):
                    print(result)
                    result = cls(result, foo=self.foo)
                    print(result)
                return result
            inner.fn_name = name
            setattr(cls, name, inner)
        for name in names:
            wrap_method_closure(name)

Fooset._wrap_methods(['__ror__', 'difference_update', '__isub__',
    'symmetric_difference', '__rsub__', '__and__', '__rand__', 'intersection',
    'difference', '__iand__', 'union', '__ixor__',
    'symmetric_difference_update', '__or__', 'copy', '__rxor__',
    'intersection_update', '__xor__', '__ior__', '__sub__',
])

if __name__ == '__main__':
    A = Fooset(('A', 'B', 'C'))
    B = Fooset(('B', 'C'))
    print(A)
    print(B)
    print(A - B)


import pygame, random
from functools import reduce

class Meteor(pygame.sprite.Sprite):
    """Clase para crear los sprites
    para generar los meteoros

    Args:
        pygame ([type]): [description]
    """
    def __init__(self) -> None:
        super().__init__()
        self.image = pygame.image.load("meteor.png").convert()
        # Sacamos el Fondo
        self.image.set_colorkey(BLACK)
        # Coordenadas de la Imagen
        self.rect = self.image.get_rect()

class Player(pygame.sprite.Sprite):
    """Clase para crear los sprites
    para generar el jugador

    Args:
        pygame ([type]): [description]
    """
    def __init__(self) -> None:
        super().__init__()
        self.image = pygame.image.load("player.png").convert()
        # Sacamos el Fondo
        self.image.set_colorkey(BLACK)
        # Coordenadas de la Imagen
        self.rect = self.image.get_rect()

# Iniciamos pygame
pygame.init()
# Definimos Tamaño de la Ventana
SCREEN_WIDTH = 900
SCREEN_HEIGHT = 600
SIZE = (SCREEN_WIDTH, SCREEN_HEIGHT)

# Creamos la Ventana
screen = pygame.display.set_mode(SIZE)
# Titulo de la Ventana
pygame.display.set_caption("Sprite")
# Control de los FPS
clock = pygame.time.Clock()
# Visibilidad del Mouse
pygame.mouse.set_visible(0)

# Definimos Colores

BLACK = (   0,   0,   0)
WHITE = ( 255, 255, 255)
GREEN = (   0, 255,   0)
RED =   ( 255,   0,   0)
BLUE =  (   0,   0, 255)

# Constante de Salida
gameOver = False
score = 0

# Lista del Grupo de Sprites
meteors = pygame.sprite.Group()
allSprites = pygame.sprite.Group()

# Creamos los meteoros
for i in range(50):
    meteor = Meteor()
    meteor.rect.x = random.randrange(900)
    meteor.rect.y = random.randrange(600)

    # Agregamos la instancia de Meteor a la lista de sprites
    meteors.add(meteor)
    allSprites.add(meteor)
# Jugador
player = Player()
allSprites.add(player)

# Estructura Principal
while not gameOver:
    # Recorremos todos los eventos
    for event in pygame.event.get():
        # Comprobamos si el evento es salir paramos la ejecución
        if event.type == pygame.QUIT:
            gameOver = True

    ### Inicio Lógica del Juego ###
    # Obtenemos la posición del mouse
    mousePosition = pygame.mouse.get_pos()
    player.rect.x = mousePosition[0]
    player.rect.y = mousePosition[1]

    # Colisiones
    meteorsHit = pygame.sprite.spritecollide(player, meteors, True)

    for meteor in meteorsHit:
        score += 1
        print(score)
    ### Fin Lógica del Juego    ###

    # Fondo
    screen.fill(WHITE)
    ### Inicio Zona de Dibujo ###
    # Dibujamos los sprites
    allSprites.draw(screen)
    ### Fin Zona de Dibujo    ###

    # Actualizar Pantalla
    pygame.display.flip()
    clock.tick(60)
# Salimos del Juego
pygame.quit()


inversores = [
    "1,Veriee,vvasilkov0@qq.com,Female,Besançon",
    "2,Lizbeth,locklin1@tiny.cc,Female,Jawand",
    "3,Tymon,thillum2@diigo.com,Male,Rîbniţa",
    "4,Teddie,tschofield3@ehow.com,Male,Tlogoagung",
    "5,Dom,dantonin4@squarespace.com,Male,Tonjongsari",
    "6,Armstrong,acreegan5@reverbnation.com,Male,Xianning",
    "7,Micky,mmaccall6@elpais.com,Female,Dongping",
    "8,Mickie,mprosser7@alexa.com,Male,Perm",
    "9,Nadya,ngerrett8@over-blog.com,Female,Champaign",
    '10,Donni,dboots9@digg.com,Female,Normanton',
    "11,Dalli,dmorecombea@wordpress.com,Male,Serra",
    '12,Fionna,fshanksterb@mashable.com,Female,Ccuntuma',
    "13,Melesa,mmacinnesc@si.edu,Female,Shihua",
    '14,Orelle,oshobbrookd@t.co,Female,Huangtan',
    '15,Gayelord,ghasloche@gmpg.org,Male,Sehwān',
    '16,Florencia,fforstf@addthis.com,Female,Buenavista',
    '17,Pam,pbelfeltg@lulu.com,Female,Maclear',
    '18,Ollie,okubalekh@feedburner.com,Female,Kebon',
    '19,Wyn,wlingeri@dropbox.com,Male,Gimry',
    '20,Vachel,vburbankj@topsy.com,Male,Bungoma'
    ]
inversiones = [
    'Monto: $374324.12 - Fecha:09/04/2022',
    'Monto: $423222.26 - Fecha:14/09/2021',
    'Monto: $591323.65 - Fecha:08/01/2022',
    'Monto: $279894.15 - Fecha:12/05/2022',
    'Monto: $884488.11 - Fecha:04/03/2022',
    'Monto: $136930.77 - Fecha:07/08/2022',
    'Monto: $925498.10 - Fecha:16/08/2021',
    'Monto: $634854.83 - Fecha:22/05/2022',
    'Monto: $682885.94 - Fecha:26/04/2022',
    'Monto: $748880.01 - Fecha:02/09/2022',
    'Monto: $490253.97 - Fecha:26/02/2022',
    'Monto: $953863.00 - Fecha:29/01/2022',
    'Monto: $473316.46 - Fecha:21/07/2022',
    'Monto: $193841.90 - Fecha:21/11/2021',
    'Monto: $325836.11 - Fecha:20/06/2022',
    'Monto: $632385.67 - Fecha:17/07/2022',
    'Monto: $137358.07 - Fecha:30/11/2021',
    'Monto: $320210.52 - Fecha:20/05/2022',
    'Monto: $558827.34 - Fecha:10/05/2022',
    'Monto: $140948.04 - Fecha:25/06/2022'
]

nombre_buscado =  input("ingrese nombre:")
correo_electronico = ""

for inversor in inversores:
    datos = inversor.split(",")
    nombre = datos[1]
    email = datos[2]
    if nombre == nombre_buscado:
        correo_electronico = email

if correo_electronico != "":
    print("El correo electrónico de", nombre_buscado, "es:", correo_electronico)

inversiones = [
    'Monto: $423222.26 - Fecha:14/09/2021',
    'Monto: $925498.10 - Fecha:16/08/2021',
    'Monto: $193841.90 - Fecha:21/11/2021',
    'Monto: $137358.07 - Fecha:30/11/2021'
]

total_inversiones = 0

for inversion in inversiones:
    monto = float(inversion.split('$')[1].split(' ')[0])
    total_inversiones += monto

print("El total de todas las inversiones es: $" + str(total_inversiones))
contador_femeninas = 0
for i in range(len(inversiones)):
    inversion = inversiones[i]
    inversor = inversores[i]
    datos_inversor = inversor.split(",")
    sexo = datos_inversor[3]
    datos_inversion = inversion.split("-")
    fecha = datos_inversion[1]
    dia, mes, año = fecha.split("/")
    año = int(año)
    mes = int(mes)

    if año == 2022 and mes <= 6 and sexo == "Female":
        contador_femeninas+= 1
print("La cantidad de inversoras mujeres en el primer semestre del",año, "es:", contador_femeninas)


from __future__ import annotations

import operator
from copy import copy
from itertools import chain
from collections.abc import Callable
from dataclasses import replace
from functools import partialmethod
from random import sample
from typing import Any, Hashable, Iterable, List, Optional, Sequence, Tuple, Union
from enum import Enum

import numpy as np

from .context import CacheKey, Context

class Empty(Enum):
    # See https://www.python.org/dev/peps/pep-0484/#support-for-singleton-types-in-unions
    empty = None

_empty = Empty.empty

class Resolveable:
    def __invert__(self):
        return self._resolve()

    def _resolve(self) -> Union[Any, Iterable[Any], Empty]:
        raise NotImplementedError

    @property
    def cache_key(self) -> Hashable:
        raise NotImplementedError

class BaseValue(Resolveable):
    constant: Optional[bool] = None  # Whether the value depends on independent variables
    name: Optional[str] = None

    def _operation(
        self,
        function: Callable[..., Union[float, bool]],
        other: Union[float, Value, Empty] = _empty,
        reverse: bool = False,
    ):
        if not isinstance(other, Resolveable):
            other = Value(other)
        if reverse:
            assert other.value is not _empty
            return Value(Operation(function, other, self))
        return Value(Operation(function, self, other))

    __neg__ = partialmethod(_operation, operator.neg)
    __pos__ = partialmethod(_operation, operator.pos)
    __abs__ = partialmethod(_operation, operator.abs)

    __add__ = partialmethod(_operation, operator.add)
    __floordiv__ = partialmethod(_operation, operator.floordiv)
    __mod__ = partialmethod(_operation, operator.mod)
    __mul__ = partialmethod(_operation, operator.mul)
    __pow__ = partialmethod(_operation, operator.pow)
    __sub__ = partialmethod(_operation, operator.sub)
    __truediv__ = partialmethod(_operation, operator.truediv)

    __radd__ = partialmethod(_operation, operator.add, reverse=True)
    __rfloordiv__ = partialmethod(_operation, operator.floordiv, reverse=True)
    __rmod__ = partialmethod(_operation, operator.mod, reverse=True)
    __rmul__ = partialmethod(_operation, operator.mul, reverse=True)
    __rpow__ = partialmethod(_operation, operator.pow, reverse=True)
    __rsub__ = partialmethod(_operation, operator.sub, reverse=True)
    __rtruediv__ = partialmethod(_operation, operator.truediv, reverse=True)

    # TODO: Decide whether the implementation of the below six methods is really what we want
    __lt__ = partialmethod(_operation, operator.lt)
    __le__ = partialmethod(_operation, operator.le)
    __eq__ = partialmethod(_operation, operator.eq)  # type: ignore
    __ne__ = partialmethod(_operation, operator.ne)  # type: ignore
    __ge__ = partialmethod(_operation, operator.ge)
    __gt__ = partialmethod(_operation, operator.gt)

class Value(BaseValue):
    def __init__(
        self,
        value: Union[float, Resolveable, Empty],
        constant: Optional[bool] = None,
        name: Optional[str] = None,
    ):
        self.value = value
        self.constant = constant
        self.name = name

    def __repr__(self):
        if self.name:
            return self.name
        return str(self.value)

    @property
    def cache_key(self):
        if isinstance(self.value, Resolveable):
            return self.value.cache_key
        return self.value

    def _resolve(self):
        if isinstance(self.value, Resolveable):
            return ~self.value
        return self.value

class Operation(Resolveable):
    FORMATS = {
        operator.neg: "-{this}",
        operator.pos: "{this}",
        operator.abs: "abs({this})",
        operator.add: "{this} + {other}",
        operator.floordiv: "{this} // {other}",
        operator.mod: "{this} % {other}",
        operator.mul: "{this} * {other}",
        operator.pow: "{this} ** {other}",
        operator.sub: "{this} - {other}",
        operator.truediv: "{this} / {other}",
        operator.lt: "{this} < {other}",
        operator.le: "{this} <= {other}",
        operator.eq: "{this} == {other}",
        operator.ne: "{this} != {other}",
        operator.ge: "{this} >= {other}",
        operator.gt: "{this} > {other}",
    }
    PRECEDENCE = {
        operator.lt: 6,
        operator.le: 6,
        operator.eq: 6,
        operator.ne: 6,
        operator.ge: 6,
        operator.gt: 6,
        operator.add: 11,
        operator.sub: 11,
        operator.floordiv: 12,
        operator.mod: 12,
        operator.mul: 12,
        operator.truediv: 12,
        operator.neg: 13,
        operator.pos: 13,
        operator.pow: 14,
        operator.abs: 16,
    }

    format: Optional[Callable[..., str]] = None
    precedence: int = -1

    def __init__(
        self,
        function: Callable[..., Any],
        this: BaseValue,
        other: BaseValue = Value(_empty),
    ):
        self.function = function
        self.this = this
        self.other = other
        if function in self.FORMATS:
            self.format = self.FORMATS[function].format
        if function in self.PRECEDENCE:
            self.precedence = self.PRECEDENCE[function]

    @property
    def cache_key(self):
        this_key = self.this.cache_key
        other_key = self.other.cache_key
        return CacheKey(function=self.function, nested=(this_key, other_key))

    def __repr__(self):
        if self.format:
            if self.other is _empty:
                return self.format(this=self.this)
            this, other = str(self.this), str(self.other)
            if (
                isinstance(self.this, Value)
                and isinstance(self.this.value, Operation)
                and self.this.value.precedence < self.precedence
            ):
                this = f"({this})"
            if (
                isinstance(self.other, Value)
                and isinstance(self.other.value, Operation)
                and self.other.value.precedence <= self.precedence
            ):
                other = f"({other})"
            return self.format(this=this, other=other)
        return f"{type(self).__name__}({self.function.__name__}, {self.this}, {self.other})"

    def _resolve(self):
        this, other = ~self.this, ~self.other
        if other is _empty:
            return self.function(this)
        return self.function(this, other)

class Distribution(BaseValue):
    constant = True

    def __init__(
        self,
        function: Callable[..., Iterable[float]],
        *args: float,
        name: Optional[str] = None,
        **kwargs: Empty,
    ):  # pylint: disable=super-init-not-called
        self.function = function
        self.name = name
        self.args = args
        self.kwargs = kwargs

    def __repr__(self):
        if self.name:
            return self.name
        name = self.function.__name__
        args = ", ".join(map(str, self.args))
        kwargs = ", ".join(f"{key}={value}" for key, value in self.kwargs.items())
        return f'{name}({", ".join(part for part in (args, kwargs) if part)})'

    @property
    def cache_key(self):
        with Context() as context:
            return CacheKey(
                sample_count=context.sample_count,
                function=self.function,
                args=self.args,
                kwargs=tuple(sorted(self.kwargs.items())),
            )

    def _resolve(self):
        with Context() as context:
            if self.cache_key in context.cache:
                return context.cache[self.cache_key]
            value = self.function(*self.args, size=context.sample_count, **self.kwargs)
            context.cache[self.cache_key] = value
            return value

class Mixture(BaseValue):
    def __init__(
        self, values: Sequence[BaseValue], name: Optional[str] = None
    ):  # pylint: disable=super-init-not-called
        self.values = values
        self.name = name

    def __repr__(self):
        if self.name:
            return self.name
        return f"{type(self).__name__}({self.values})"

    @property
    def cache_key(self):
        with Context() as context:
            return CacheKey(
                sample_count=context.sample_count,
                nested=tuple(replace(value.cache_key, sample_count=None) for value in self.values),
            )

    @staticmethod
    def _sample(*values: BaseValue):
        with Context() as context:
            remainder = context.sample_count % len(values)
            sample_counts = [context.sample_count // len(values)] * len(values)
        sample_counts = [
            sample_count + int(i < remainder) for i, sample_count in enumerate(sample_counts)
        ]
        sample_counts = sample(sample_counts, len(sample_counts))  # Shuffling
        samples = []
        for value, sample_count in zip(values, sample_counts):
            with Context(sample_count=sample_count):
                samples.append(~value)
        return np.concatenate(samples)

    def _resolve(self):
        with Context() as context:
            if self.cache_key in context.cache:
                return context.cache[self.cache_key]
            value = self._sample(*self.values)
            context.cache[self.cache_key] = value
            return value


import re, nltk, bs4
import pandas as pd
import numpy as np
from numpy.linalg import norm
import scipy as sp
from scipy.sparse import csr_matrix as csr
import random
from string import punctuation
from nltk.corpus import stopwords
from collections import defaultdict

file_path = 'C:\\path\\to\\both_files\\'

def tf(n=5, source='reuters'):
    # define number of relevant words n
    nltk.download('stopwords')
    punc = list(punctuation)
    stop_words = set(stopwords.words('english'))

    if source == 'reuters':
        file_path = './reut.html'

        def extract_reuters_news(path_file):
            file = open(path_file , 'r').read()
            soup = bs4.BeautifulSoup(file)
            all_bodies = [el.text for el in soup.find_all('content')] # replace body with content, otherwise bs4 wont find any body other then main body
            return all_bodies

        data = extract_reuters_news(file_path)
        NO_OF_ENTRIES = len(data)

    if source == 'imdb':
        data = pd.read_csv('./imdb.csv')
        NO_OF_ENTRIES = len(data) //2
        NO_OF_ENTRIES
        data = data.review.iloc[:NO_OF_ENTRIES]

    ## GLOBAL DICTS
    ## countains overall count of the term among all documents
    maximum_per_document = defaultdict(int) # maximum a term occurs in one doc. denominator for first equation
    number_docs_containing_term = defaultdict(int) ## How many documents contain a term --> denominator for second equation

    # bow_count will clean the input, create sets for every sentence and return a dict {word:count} & int(maximum count per doc)
    def bow_count(sentences):
        new_sentence = ''
        sentences = re.sub(r'<\s*br\s*\/s*>', '', sentences)
        sentences = re.sub(r'\n>', ' ', sentences)
        sentences = re.sub(r'\s+', ' ', sentences)
        sentences = re.sub(r'\.+\s*', '.', sentences)
        sentences = re.sub(r'who\'ll', 'who will', sentences)
        sentences = re.sub(r'[IiyouYousheSHE]\'ll', 'i will', sentences)
        sentences = re.sub(r'[wW]ouldn\'t', 'would not', sentences)
        sentences = re.sub(r'[mM]mustn\'t', 'must not', sentences)
        sentences = re.sub(r'[tT]hat\'s', 'that is', sentences)

        for el in sentences:
            if el.isspace() or el.isalpha() or el == '.': #or el.isnumeric():
                new_sentence += el.lower()

        new_sentences = new_sentence.split('.')
        new_sentences = [set(e for e in el.split() if e not in stop_words) for el in new_sentence.split('.')]
        temp_set = set()
        temp_count = defaultdict(int)

        for el in new_sentences:
            for l in el:

                temp_count[l] += 1
                temp_set.add(l)

        doc_max_term_count = [v for k,v in sorted(temp_count.items(), key= lambda x : x[1], reverse=True)][0]

        for term in temp_set:
            number_docs_containing_term[term] += 1

        return temp_count, doc_max_term_count ## returning a list of sets, where every set is a sentence

    docs = []
    for i,doc in enumerate(data):
        counted_terms, m = bow_count(doc)
        maximum_per_document[i] = m
        docs.append(counted_terms)

    def get_tf_idf(w,doc_index):
        tf_idf = {}
        tf = {}

        for k,v in w.items():
            tf[k] = v / maximum_per_document[doc_index]
            ni = number_docs_containing_term[k]
            from math import log
            idf = log(NO_OF_ENTRIES / ni)
            tf_idf[k] = tf[k] * idf

        return tf_idf

    result = []
    words_vector = set()

    for ind, words in enumerate(docs):
        ranked_words = get_tf_idf(words, ind)
        top_n = {k:v for k,v in sorted(ranked_words.items(), key=lambda x: (-x[1]) )[:n] }
        result.append(top_n)
        top_set = set([el for el in top_n.keys()])
        words_vector |= top_set

    all_word_vector = np.zeros(len(words_vector))

    ## global list that will then be stacked to sparse matrix
    similarity_to_stack = []
    ## create a similariy vector of all words -> which is then used to create per-result-datapoint-vectors --> stacking those to matrix
    def similarity_vector(words):
        doc_vec = all_word_vector.copy()

        for i,word in enumerate(words_vector):
            if word in words:
                doc_vec[i] = 1

        doc_vec_norm = np.linalg.norm(doc_vec)
        doc_vec /= doc_vec_norm

        return doc_vec # which is a vector that is normalized and can be compared to all others

    # iterate over all entries in result (dictonary with n entries of top words)
    for progress,r in enumerate(result):
        similarity_to_stack.append(similarity_vector(list(r.keys())))
        if progress%1000 == 0:
            print(progress, ' records analysed.')

    # stack all results similarity vectors to one matrix
    m = csr(np.vstack(similarity_to_stack))
    m.shape

    # print the stacked matrix:
    # m: number of datapoints, n: number of words in all_word_vector
    plt.spy(m, marker='.', markersize=1)

    # create a similarity vector, by multiplying each element with all others
    ref = m.dot(m.T).toarray()

    return ref, data

ref,data = tf()

# quick & dirty: identify similar articles/blogs/reviews
for ind,ary in enumerate(ref):
    for i,el in enumerate(ary):
        if el > .6 and ind != i :
            print(ind, ' and ', i)
            break


import cv2
import numpy as np
import os
from os.path import join
from time import time
from os import listdir
import matplotlib.pyplot as plt
from src.data.utils.make_dir import make_dir
import src.data.constants as c

# Set working directory to script location
c.setcwd(__file__)

files = c.RAW_FILES_GENERALIZE
kernel = c.MEDIAN_FILTER_KERNEL
threshold = c.SIMPLE_THRESHOLD
data_dir = c.DATA_DIR
mode = 'train'

# Create folder in case it doesn't exist yet
folder_name = c.MASK_DIR
folder = join(data_dir, mode, folder_name)
make_dir(folder)

# How often to print out with matplotlib
debug_every = c.DBG_EVERY

# Name of the folder in which the images will reside
imgs_path = join(c.DATA_DIR, mode, c.IMG_DIR)
masks_path = join(c.DATA_DIR, mode, c.MASK_DIR)
# List of filenames of the .npy images
# .jpg files are for visualizing the process
images = [image for image in listdir(imgs_path) if '.npy' in image]
masks = [mask for mask in listdir(masks_path) if '.npy' in mask]
# Get full image paths from filename list `images`
image_paths = sorted([join(imgs_path, image) for image in images])

# This is the index we will start on, in case there are already
# data files in there
# So, we are only adding to the existing list of files in /imgs/
# -1 for zero-indexing, +1 because we want to start at the next free index
img_idx = len(masks) - 1 + 1
idx = img_idx if img_idx > 0 else 0  # numbering for images

thresholds = []
idx = 0
tic = time()
for i, path in enumerate(image_paths):

    img = np.int16(np.load(path))
    # Convert to PIL image; requirement for the model
    img = cv2.normalize(img, img, alpha=0, beta=255,
                        dtype=cv2.CV_8UC1, norm_type=cv2.NORM_MINMAX)
    name = f'{idx:05d}'
    # Folder to save image to
    save = join(folder, name)
    idx = idx + 1

    filtered = cv2.medianBlur(img, kernel)
    _, thresh = cv2.threshold(filtered,
                              threshold, 255, cv2.THRESH_BINARY)
    # np.save(save, thresh)

    # Save as .jpg for debugging
    # if i % debug_every == 1:
    dirs = os.path.dirname(save)
    file = os.path.basename(save)
    plt.imsave(f'{dirs}/_{file}.{c.IMG_EXT}', thresh, cmap='gray')

toc = time()
print(f'annotate.py complete after {(toc-tic)/60: .1f} minutes.')


import math
def findCheapestPrice(n, flights, src, dst, K):
    """
    :type n: int
    :type flights: List[List[int]]
    :type src: int
    :type dst: int
    :type K: int
    :rtype: int
    """

    prices = {}
    for s, e, p in flights:
        prices[(s,e)] = p

    dp = [[math.inf for _ in range(n)] for _ in range(K+1)]
    for k in range(K+1):
        dp[k][dst] = 0

    for i in range(n):
        if (i,dst) in prices:
            dp[0][i] = prices[(i, dst)]

    for k in range(1, K+1):
        for i in range(n):
            cur = math.inf
            for g in range(n):
                t = 0
                if g == i:
                    t = dp[k - 1][g]
                elif (g,dst) in prices:
                    t = dp[k-1][g] + prices[(g, dst)]
                else:
                    t = math.inf
                if t < cur:
                    cur = t
            dp[k][i] = cur

    return dp[K][src]

# print(findCheapestPrice(n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, K = 1))
# print(findCheapestPrice(n = 3, flights = [[0,1,100],[1,2,100],[0,2,500]], src = 0, dst = 2, K = 0))
print(findCheapestPrice(5, [[1,2,10],[2,0,7],[1,3,8],[4,0,10],[3,4,2],[4,2,10],[0,3,3],[3,1,6],[2,4,5]], 0, 4, 1))
# print(findCheapestPrice(3, [[0,1,2],[1,2,1],[2,0,10]], 1, 2, 1))


from logic.areas import Area, SubArea
from maze_builder.types import Room, DoorIdentifier, Direction, DoorSubtype, Item

LEFT = Direction.LEFT
RIGHT = Direction.RIGHT
UP = Direction.UP
DOWN = Direction.DOWN
ELEVATOR = DoorSubtype.ELEVATOR

rooms = [
    Room(
        name='The Moat',
        rom_address=0x795FF,
        map=[
            [1, 1],
            [1, 1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18ADE, 0x18A36, 0),  # Crateria Kihunter Room
            DoorIdentifier(RIGHT, 1, 0, 0x18AEA, 0x189CA, 0),  # West Ocean
        ],
        items=[
            Item(0, 0, 0x78248),
        ],
        node_tiles={
            1: [(0, 0), (0, 1)],  # left door
            2: [(1, 0), (1, 1)],  # right door
            3: [(0, 0)],  # missile
        },
    ),
    Room(
        name='Landing Site',
        rom_address=0x791F8,
        map=[
            [0, 0, 1, 1, 1, 1, 1, 1, 1],
            [0, 0, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1],
            [0, 0, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 2, 0x1892E, 0x18946, 0),  # Gauntlet Entrance
            DoorIdentifier(LEFT, 0, 4, 0x18916, 0x1896A, 0),  # Parlor and Alcatraz
            DoorIdentifier(RIGHT, 8, 1, 0x1893A, 0x189B2, 0),  # Crateria Power Bomb Room
            DoorIdentifier(RIGHT, 8, 4, 0x18922, 0x18AC6, 0),  # Crateria Tube
        ],
        node_tiles={
            1: [(0, 2), (1, 2)],  # top left door
            2: [(0, 4), (1, 4), (2, 4)],  # bottom left door
            3: [(2, 0), (3, 0), (4, 0), (5, 0), (6, 0), (7, 0), (8, 0),  # top right door
                (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1), (8, 1),
                (3, 2), (4, 2), (5, 2)],
            4: [(6, 2), (7, 2), (8, 2),  # bottom right door
                (6, 3), (7, 3), (8, 3),
                (6, 4), (7, 4), (8, 4)],
            5: [(3, 3), (4, 3), (5, 3),  # Ship
                (3, 4), (4, 4), (5, 4), (6, 4)],
            6: [(4, 4)],  # Escape Zebes
            7: [(2, 2), (2, 3), (2, 4)],  # Gauntlet ledge
        },
    ),
    Room(
        name='Crateria Tube',
        rom_address=0x795D4,
        map=[[1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18AC6, 0x18922, None),  # Landing Site
            DoorIdentifier(RIGHT, 0, 0, 0x18AD2, 0x18A2A, None),  # Crateria Kihunter Room
        ],
        node_tiles={
            1: [(0, 0)],
            2: [(0, 0)],
        },
    ),
    Room(
        name='Parlor and Alcatraz',
        rom_address=0x792FD,
        map=[
            [1, 1, 1, 1, 1],
            [0, 1, 0, 1, 0],
            [0, 1, 0, 1, 0],
            [0, 1, 0, 0, 0],
            [0, 1, 0, 0, 0],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x1895E, 0x18BF2, 0),  # Terminator Room
            DoorIdentifier(LEFT, 1, 2, 0x1899A, 0x189BE, 0),  # Crateria Save Room
            DoorIdentifier(LEFT, 1, 3, 0x189A6, 0x18C8E, 0),  # Final Missile Bombway
            DoorIdentifier(RIGHT, 4, 0, 0x1896A, 0x18916, 0),  # Landing Site
            DoorIdentifier(RIGHT, 3, 2, 0x18982, 0x18BB6, 0),  # Flyway
            DoorIdentifier(RIGHT, 1, 3, 0x18976, 0x18BCE, 0),  # Pre-Map Flyway
            DoorIdentifier(DOWN, 1, 4, 0x1898E, 0x18B3E, 1),  # Climb
        ],
        node_tiles={
            1: [(0, 0)],  # top left door
            2: [(1, 2)],  # middle left door
            3: [(1, 3)],  # bottom left door
            4: [(4, 0)],  # top right door
            5: [(3, 1), (3, 2)],  # alcatraz door
            6: [(1, 3)],  # bottom right door
            7: [(1, 4)],  # bottom door
            8: [(1, 0), (2, 0), (3, 0), (4, 0), (1, 1), (1, 2), (1, 3), (1, 4)],  # junction
        },
    ),
    Room(
        name='Climb',
        rom_address=0x796BA,
        map=[
            [0, 1, 1],
            [0, 1, 0],
            [0, 1, 0],
            [0, 1, 0],
            [0, 1, 0],
            [0, 1, 0],
            [0, 1, 0],
            [0, 1, 1],
            [1, 1, 0],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 8, 0x18B6E, 0x1AB34, 0),  # Tourian Escape Room 4
            DoorIdentifier(RIGHT, 2, 0, 0x18B4A, 0x18C6A, 0),  # Crateria Super Room (top)
            DoorIdentifier(RIGHT, 2, 7, 0x18B56, 0x18C76, 0),  # Crateria Super Room (bottom)
            DoorIdentifier(RIGHT, 1, 8, 0x18B62, 0x18B7A, 0),  # Pit Room
            DoorIdentifier(UP, 1, 0, 0x18B3E, 0x1898E, 1),  # Parlor and Alcatraz
        ],
        node_tiles={
            1: [(1, 0)],  # top door
            2: [(0, 8)],  # bottom left door
            3: [(2, 0)],  # top right door
            4: [(2, 7)],  # middle right door
            5: [(1, 8)],  # bottom right door
            6: [(1, 0), (1, 1), (1, 2), (1, 3), (1, 4), (1, 5), (1, 6), (1, 7), (1, 8)],  # main junction
        },
    ),
    Room(
        name='Pit Room',
        rom_address=0x7975C,
        map=[
            [1, 1, 1],
            [1, 0, 0]
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18B7A, 0x18B62, 0),  # Climb
            DoorIdentifier(RIGHT, 2, 0, 0x18B86, 0x18B92, 0),  # Blue Brinstar Elevator Room
        ],
        items=[
            Item(0, 1, 0x783EE),
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(1, 0), (2, 0)],  # right door
            3: [(0, 1)],  # missile
        },
    ),
    Room(
        name='Flyway',
        rom_address=0x79879,
        map=[[1, 1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18BB6, 0x18982, 0),  # Parlor and Alcatraz
            DoorIdentifier(RIGHT, 2, 0, 0x18BC2, 0x18BAA, 0),  # Bomb Torizo Room
        ],
        node_tiles={
            1: [(0, 0), (1, 0)],  # left door
            2: [(2, 0)],  # right door
        },
    ),
    Room(
        name='Pre-Map Flyway',
        rom_address=0x798E2,
        map=[[1, 1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18BCE, 0x18976, 0),  # Parlor and Alcatraz
            DoorIdentifier(RIGHT, 2, 0, 0x18BDA, 0x18C2E, 0),  # Crateria Map Room
        ],
        node_tiles={
            1: [(0, 0), (1, 0)],  # left door
            2: [(2, 0)],  # right door
        },
    ),
    Room(
        name='Crateria Map Room',
        rom_address=0x79994,
        map=[[1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18C2E, 0x18BDA, 0),  # Pre-Map Flyway
        ],
        node_tiles={
            1: [(0, 0)],  # door
            2: [(0, 0)],  # map station
        },
    ),
    Room(
        name='Crateria Save Room',
        rom_address=0x793D5,
        map=[[1]],
        door_ids=[
            DoorIdentifier(RIGHT, 0, 0, 0x189BE, 0x1899A, 0),  # Parlor and Alcatraz
        ],
        node_tiles={
            1: [(0, 0)],  # door
            2: [(0, 0)],  # save station
        },
    ),
    Room(
        name='The Final Missile',
        rom_address=0x79A90,
        map=[[1]],
        door_ids=[
            DoorIdentifier(RIGHT, 0, 0, 0x18C9A, 0x18C82, 0),  # Final Missile Bombway
        ],
        items=[
            Item(0, 0, 0x78486),
        ],
        node_tiles={
            1: [(0, 0)],  # door
            2: [(0, 0)],  # missile
        },
    ),
    Room(
        name='Final Missile Bombway',
        rom_address=0x79A44,
        map=[[1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18C82, 0x18C9A, 0),  # The Final Missile
            DoorIdentifier(RIGHT, 1, 0, 0x18C8E, 0x189A6, 0),  # Parlor and Alcatraz
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(1, 0)],  # right door
        },
    ),
    Room(
        name='Bomb Torizo Room',
        rom_address=0x79804,
        map=[[1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18BAA, 0x18BC2, 0),  # Flyway
        ],
        items=[
            Item(0, 0, 0x78404),
        ],
        node_tiles={
            1: [(0, 0)],  # door
            2: [(0, 0)],  # bombs
            3: [(0, 0)],  # boss
            4: [(0, 0)],  # animals
        },
    ),
    Room(
        name='Terminator Room',
        rom_address=0x7990D,
        map=[
            [0, 0, 0, 1, 1, 1],
            [0, 1, 1, 1, 1, 0],
            [1, 1, 1, 0, 0, 0],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 2, 0x18BE6, 0x18C3A, 0),  # Green Pirates Shaft
            DoorIdentifier(RIGHT, 5, 0, 0x18BF2, 0x1895E, 0),  # Parlor and Alcatraz
        ],
        items=[
            Item(0, 2, 0x78432),
        ],
        node_tiles={
            1: [(0, 2)],  # left door
            2: [(1, 2), (1, 1), (2, 1), (2, 2), (3, 0), (3, 1), (4, 0), (4, 1), (5, 0)],  # right door
            3: [(0, 2)],  # etank
        },
    ),
    Room(
        name='Green Pirates Shaft',
        rom_address=0x799BD,
        map=[
            [1],
            [1],
            [1],
            [1],
            [1],
            [1],
            [1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 6, 0x18C46, 0x18C16, 0),  # Lower Mushrooms
            DoorIdentifier(RIGHT, 0, 0, 0x18C5E, 0x18B1A, 0),  # Gauntlet Energy Tank Room
            DoorIdentifier(RIGHT, 0, 4, 0x18C3A, 0x18BE6, 0),  # Terminator Room
            DoorIdentifier(RIGHT, 0, 6, 0x18C52, 0x191E6, 0),  # Statues Hallway
        ],
        parts=[[1], [0, 2, 3]],
        transient_part_connections=[(0, 1)],  # crumble blocks
        missing_part_connections=[(1, 0)],
        items=[
            Item(0, 1, 0x7846A),
            Item(0, 1, 0x78464),
        ],
        node_tiles={
            1: [(0, 0)],  # top right door
            2: [(0, 4), (0, 5)],  # middle right door
            3: [(0, 6)],  # left door
            4: [(0, 6)],  # bottom right door
            5: [(0, 1)],  # left missile
            6: [(0, 1)],  # right missile
            8: [(0, 1)],  # item junction
            9: [(0, 2), (0, 3)],  # middle junction
        },
    ),
    Room(
        name='Lower Mushrooms',
        rom_address=0x79969,
        map=[[1, 1, 1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18C22, 0x18BFE, 0),  # Green Brinstar Elevator Room
            DoorIdentifier(RIGHT, 3, 0, 0x18C16, 0x18C46, 0),  # Green Pirates Shaft
        ],
        node_tiles={
            1: [(0, 0), (1, 0), (2, 0)],  # left door
            2: [(3, 0)],  # right door
        },
    ),
    Room(
        name='Green Brinstar Elevator Room',
        rom_address=0x79938,
        map=[
            [1],
            [1],
            [1],
            [1],
        ],
        door_ids=[
            DoorIdentifier(RIGHT, 0, 0, 0x18BFE, 0x18C22, 0),  # Lower Mushrooms
            DoorIdentifier(DOWN, 0, 3, 0x18C0A, 0x18CA6, None, ELEVATOR),
        ],
        node_tiles={
            1: [(0, 0)],  # right door
            2: [(0, 1), (0, 2), (0, 3)],  # elevator
        },
    ),
    Room(
        name='Crateria Kihunter Room',
        rom_address=0x7948C,
        map=[
            [1, 1, 1],
            [0, 1, 0],
            [0, 1, 0],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18A2A, 0x18AD2, 0),  # Crateria Tube
            DoorIdentifier(RIGHT, 2, 0, 0x18A36, 0x18ADE, 0),  # The Moat
            DoorIdentifier(DOWN, 1, 2, 0x18A42, 0x18AF6, 1),  # Red Brinstar Elevator Room
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(1, 0), (2, 0)],  # right door
            3: [(1, 1), (1, 2)],  # bottom door
        },
    ),
    Room(
        name='Statues Hallway',
        rom_address=0x7A5ED,
        map=[[1, 1, 1, 1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x191E6, 0x18C52, 0),  # Green Pirates Shaft
            DoorIdentifier(RIGHT, 4, 0, 0x191F2, 0x19216, 0),  # Statues Room
        ],
        node_tiles={
            1: [(0, 0), (1, 0), (2, 0), (3, 0)],  # left door
            2: [(4, 0)],  # right door
        },
    ),
    Room(
        name='Red Brinstar Elevator Room',
        rom_address=0x7962A,
        map=[
            [1],
            [1],
            [1],
            [1],
        ],
        door_ids=[
            DoorIdentifier(UP, 0, 0, 0x18AF6, 0x18A42, 1),  # Crateria Kihunter Room
            DoorIdentifier(DOWN, 0, 3, 0x18B02, 0x190BA, None, ELEVATOR),  # Caterpillar Room
        ],
        node_tiles={
            1: [(0, 0)],  # top door
            2: [(0, 1), (0, 2), (0, 3)],  # elevator
        },
    ),
    Room(
        # TODO: add this to wiki.supermetroid.run/List_of_rooms because it is missing.
        name='Blue Brinstar Elevator Room',
        rom_address=0x797B5,
        map=[
            [1],
            [1],
            [1],
            [1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18B92, 0x18B86, 0),  # Pit Room
            DoorIdentifier(DOWN, 0, 3, 0x18B9E, 0x18EB6, None, ELEVATOR),  # Morph Ball Room
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(0, 1), (0, 2), (0, 3)],  # elevator
        },
    ),
    Room(
        name='Statues Room',
        rom_address=0x7A66A,
        map=[
            [1],
            [1],
            [1],  # This map tile and below aren't in the vanilla game (unlike for other elevators)
            [1],
            [1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x19216, 0x191F2, 0),  # Statues Hallway
            DoorIdentifier(DOWN, 0, 4, 0x19222, 0x1A990, None, ELEVATOR),  # Tourian First Room
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(0, 1), (0, 2), (0, 3), (0, 4)],  # elevator
            3: [(0, 0)],  # statues event
            4: [(0, 1)],  # underwater statues event
        },
    ),
    Room(
        name='Crateria Power Bomb Room',
        rom_address=0x793AA,
        map=[[1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x189B2, 0x1893A, 0),  # Landing Site
        ],
        items=[
            Item(1, 0, 0x781CC),
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(1, 0)],  # power bomb
        },
    ),
    Room(
        name='Crateria Super Room',
        rom_address=0x799F9,
        map=[
            [1, 1, 1, 1],
            [0, 0, 0, 1],
            [0, 0, 0, 1],
            [0, 0, 0, 1],
            [0, 0, 0, 1],
            [0, 0, 0, 1],
            [0, 0, 0, 1],
            [1, 1, 1, 1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18C6A, 0x18B4A, 0),  # Climb (top)
            DoorIdentifier(LEFT, 0, 7, 0x18C76, 0x18B56, 0),  # Climb (bottom)
        ],
        parts=[[0], [1]],
        transient_part_connections=[(1, 0)],  # speed blocks
        missing_part_connections=[(0, 1)],
        # missing_part_connections=[(0, 1), (1, 0)],
        items=[
            Item(3, 0, 0x78478),
        ],
        node_tiles={
            1: [(0, 0)],  # top left door
            2: [(0, 7), (1, 7), (2, 7)],  # bottom left door
            3: [(3, 0)],  # super missile
            4: [(1, 0), (2, 0)],  # right of spikes junction
            5: [(3, 7), (3, 6), (3, 5), (3, 4), (3, 3), (3, 2), (3, 1)],  # right of boyons junction
        },
    ),
    Room(
        name='Gauntlet Entrance',
        rom_address=0x792B3,
        map=[[1, 1, 1, 1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18952, 0x18B0E, 0),  # Gauntlet Energy Tank Room
            DoorIdentifier(RIGHT, 4, 0, 0x18946, 0x1892E, 0),  # Landing Site
        ],
        node_tiles={
            1: [(0, 0), (1, 0)],  # left door
            2: [(2, 0), (3, 0), (4, 0)],  # right door
        },
    ),
    Room(
        name='Gauntlet Energy Tank Room',
        rom_address=0x7965B,
        map=[[1, 1, 1, 1, 1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18B1A, 0x18C5E, 0),  # Green Pirates Shaft
            DoorIdentifier(RIGHT, 5, 0, 0x18B0E, 0x18952, 0),  # Gauntlet Entrance
        ],
        items=[
            Item(5, 0, 0x78264),
        ],
        node_tiles={
            1: [(0, 0), (1, 0)],  # left door
            2: [(5, 0)],  # right door
            3: [(4, 0)],  # etank
            4: [(2, 0), (3, 0)],  # center junction
        },
    ),
    Room(
        name='West Ocean',
        rom_address=0x793FE,
        twin_rom_address=0x7968F,
        map=[
            [1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 0, 0, 1, 0, 0],
            [1, 1, 1, 1, 1, 1, 0, 0],
            [1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 5, 2, 0x18B26, 0x18A1E, 0),  # Bowling Alley Path (east)
            DoorIdentifier(LEFT, 0, 4, 0x189CA, 0x18AEA, 0),  # The Moat
            DoorIdentifier(RIGHT, 7, 0, 0x189EE, 0x1A1E0, 0),  # Attic
            DoorIdentifier(RIGHT, 7, 1, 0x189FA, 0x1A18C, 0),  # Bowling Alley (top)
            DoorIdentifier(RIGHT, 2, 2, 0x189E2, 0x18A12, 0),  # Bowling Alley Path (west)
            DoorIdentifier(RIGHT, 5, 2, 0x18B32, 0x1A198, 0),  # Bowling Alley (middle)
            DoorIdentifier(RIGHT, 5, 3, 0x18A06, 0x1A300, 0),  # Gravity Suit Room
            DoorIdentifier(RIGHT, 7, 4, 0x189D6, 0x1A1B0, 0),  # Wrecked Ship Entrance
        ],
        parts=[[1, 6, 7], [2, 3, 4], [0, 5]],
        transient_part_connections=[(1, 0)],  # crumble block
        missing_part_connections=[(0, 2), (2, 1)],
        items=[
            Item(1, 0, 0x781EE),
            Item(0, 2, 0x781F4),
            Item(0, 5, 0x781E8),
        ],
        node_tiles={
            1: [(0, 4)],  # left door
            2: [(7, 0)],  # top right door
            3: [(7, 1)],  # middle right door
            4: [(5, 3)],  # right door (to gravity)
            5: [(6, 4), (7, 4)],  # bottom right door
            6: [(1, 2), (2, 2)],  # door to bowling alley path
            9: [(0, 0), (1, 0), (2, 0), (3, 0)],  # sky missile
            10: [(0, 2)],  # middle missile
            11: [(0, 5), (1, 5)],  # bottom missile
            12: [(4, 0), (5, 0), (6, 0), (7, 0),  # top junction
                 (0, 1), (1, 1), (2, 1), (3, 1), (4, 1), (5, 1), (6, 1), (7, 1)],
            13: [(0, 3), (1, 3), (2, 3), (3, 3), (4, 3),  # bottom junction
                 (0, 4), (1, 4), (2, 4), (3, 4), (4, 4), (5, 4),
                 (2, 5), (3, 5), (4, 5), (5, 5), (6, 5), (7, 5)],
            14: [(0, 2)],  # north-south junction
        },
        twin_node_tiles={
            1: [(5, 2)],
            2: [(5, 2)],
        },
    ),
    Room(
        name='Bowling Alley Path',
        rom_address=0x79461,
        map=[[1, 1]],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18A12, 0x189E2, 0),  # West Ocean
            DoorIdentifier(RIGHT, 1, 0, 0x18A1E, 0x18B26, 0),  # West Ocean (Homing Geemer Room)
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(1, 0)],  # right door
        },
    ),
    Room(
        name='East Ocean',
        rom_address=0x794FD,
        map=[  # This map could be trimmed, but it's like this in the game
            [0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0],
            [0, 0, 0, 0, 0, 0, 0],
            [1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 4, 0x18A66, 0x1A264, 0),  # Electric Death Room
            DoorIdentifier(RIGHT, 6, 4, 0x18A72, 0x18A7E, 0),  # Forgotten Highway Kago Room
        ],
        node_tiles={
            1: [(0, 4), (1, 4)],  # left door
            2: [(4, 4), (5, 4), (6, 4)],  # right door
            3: [(0, 5), (1, 5), (2, 4), (2, 5), (3, 4), (3, 5)],  # left junction
            4: [(4, 4), (4, 5), (5, 4), (5, 5), (6, 5)],  # right junction
        },
    ),
    Room(
        name='Forgotten Highway Kago Room',
        rom_address=0x79552,
        map=[
            [1],
            [1],
            [1],
            [1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 0, 0x18A7E, 0x18A72, 0),  # East Ocean
            DoorIdentifier(DOWN, 0, 3, 0x18A8A, 0x18A96, 1),  # Crab Maze
        ],
        node_tiles={
            1: [(0, 0)],  # left door
            2: [(0, 1), (0, 2), (0, 3)],  # bottom door
        },
    ),
    Room(
        name='Crab Maze',
        rom_address=0x7957D,
        map=[
            [1, 1, 1, 1],
            [1, 1, 1, 1],
        ],
        door_ids=[
            DoorIdentifier(LEFT, 0, 1, 0x18AAE, 0x18AA2, 0),  # Forgotten Highway Elbow
            DoorIdentifier(UP, 3, 0, 0x18A96, 0x18A8A, 1),  # Forgotten Highway Kago Room
        ],
        node_tiles={
            1: [(3, 0), (3, 1)],  # top right door
            2: [(0, 1), (1, 1)],  # bottom left door
            3: [(0, 0), (1, 0), (2, 0), (2, 1)],  # middle junction
        },
    ),
    Room(
        name='Forgotten Highway Elevator',
        rom_address=0x794CC,
        map=[
            [1],
            [1],
            [1],
            [1],
        ],
        door_ids=[
            DoorIdentifier(UP, 0, 0, 0x18A4E, 0x18ABA, 1),  # Forgotten Highway Elbow
            DoorIdentifier(DOWN, 0, 3, 0x18A5A, 0x1A594, 0, ELEVATOR),  # Maridia Elevator Room
        ],
        node_tiles={
            1: [(0, 0)],  # top door
            2: [(0, 1), (0, 2), (0, 3)],  # elevator
        },
    ),
    Room(
        name='Forgotten Highway Elbow',  # Add to list on wiki.supermetroid.run
        rom_address=0x795A8,
        map=[[1]],
        door_ids=[
            DoorIdentifier(RIGHT, 0, 0, 0x18AA2, 0x18AAE, 0),  # Crab Maze
            DoorIdentifier(DOWN, 0, 0, 0x18ABA, 0x18A4E, 1),  # Forgotten Highway Elevator
        ],
        node_tiles={
            1: [(0, 0)],  # right door
            2: [(0, 0)],  # bottom door
        },
    )
]

for room in rooms:
    room.area = Area.CRATERIA
    room.sub_area = SubArea.CRATERIA_AND_BLUE_BRINSTAR


def task01():
    s = 'Hello, user!'
    half_len = len(s) // 2
    if len(s) % 2 == 1:
        half_len += 1
    new_s = s[half_len:len(s)] + s[0:half_len]
    print(new_s)
def task02():
    s = 'Hello user'
    arr = s.split()
    new_s = arr[1] + ' ' + arr[0]
    print(new_s)
def task03():
    s = 'Hello, fuckn\' user!'
    res = s.find('f')
    r_res = s.rfind('f')
    if res != -1:
        print(res)
        if res != r_res:
            print(r_res)
def task04():
    s = 'Hello, fuckn\' user!'
    arr = list(s)
    res = s.find('f')
    arr.pop(res)
    s = "".join(arr)
    sec_res = s.find('f')
    if res == -1:
        print(-2)
    elif sec_res == -1:
        print(-1)
    else:
        print(sec_res)
def task05():
    s = 'Say hello healey'
    arr = s.split('h')
    s = arr[0] + arr[len(arr) - 1]
    print(s)
def task06():
    s = 'Say hello healey'
    arr = s.split('h')
    arr[1] = arr[1][::-1]
    arr[0] += 'h'
    arr[2] = 'h' + arr[2]
    s = ''.join(arr)
    print(s)
def task07():
    s = '1 plus 1 is two'
    s = s.replace('1', 'one')
    print(s)
def task08():
    s = 'ty @ ja @ ty @ ja @ ty @ ja @'
    s = s.replace('@', '')
    print(s)
def task09():
    s = 'Say hello healey hemming'
    find = s.find('h')
    rfind = s.rfind('h')
    s = s.replace('h', 'H')
    s = s[:find] + 'h' + s[find + 1:rfind] + 'h' + s[rfind + 1:]
    print(s)
def task10():
    s = 'Hello user'
    t = ''
    for i in range(len(s)):
        if i % 3 != 0:
            t = t + s[i]
    print(t)
def task11():
    f_input = open('resources/lab07_task11_a_dict.txt', 'r')
    f_output = open('resources/lab07_task11_b_dict.txt', 'w')
    a_dict = dict()
    b_dict = dict()
    s = f_input.read().split('\n')
    for item in s:
        key = item.split(" ")[0]
        value = item.split(" ")[1:]
        a_dict[key] = value
    for key, value in a_dict.items():
        for item in value:
            b_dict[item] = key
    for key, value in b_dict.items():
        f_output.write('{} {}\n'.format(key, value))
task01()
task02()
task03()
task04()
task05()
task06()
task07()
task08()
task09()
task10()
task11()


class OculusScript(Actor.Actor):
    def __init__(self):
        self.LeftHandContainer = Container(0)
        self.RightHandContainer = Container(0)
        self.CameraContainer = Container(0)

        self._MainCamera = Camera(0)

        self._CameraTransform = None
        self._LeftHandTransform = None
        self._RightHandTransform = None
        self.tick = 0

        self.leftindexTrigger=0
        self.rightindexTrigger = 0

        self.lefthandTrigger = 0
        self.righthandTrigger = 0

        self.switchhandle = 0

        self.CarContainer = Container(0)

        return

    def OnCreate(self, uid):
        self._MainCamera = self.CameraContainer.FindComponentByType("Camera")
        self._CameraTransform = self.CameraContainer.FindComponentByType("TransformGroup")
        self._LeftHandTransform = self.LeftHandContainer.FindComponentByType("TransformGroup")
        self._RightHandTransform = self.RightHandContainer.FindComponentByType("TransformGroup")

        self.CarScriptComponent = self.CarContainer.FindComponentByType("ScriptComponent")
        self.CarScript = self.CarScriptComponent.GetActor()

        return 0

    def OnDestroy(self):
        return 0

    def OnEnable(self):
        return 0

    def OnDisable(self):
        return 0

    def Update(self):
        doPrint = True;

        self.tick += 1

        if(self.tick <= 20):
            return

        self.tick = 0

        camPosition = self._CameraTransform.PropTransform.GetPosition()
        #camRotation = self._CameraTransform.PropTransform.GetRotation()

        #print(camPosition)

        hmdStatus = DeviceInput.GetHMDStatus("Oculus")
        hmdRot = DeviceInput.GetHMDOrientation("Oculus", False)
        hmdPos = DeviceInput.GetHMDPosition("Oculus", False)
        inputTime = DeviceInput.GetInputTime("Oculus")
        buttonInfo = DeviceInput.GetInputInfo("Oculus", 0)
        touchInfo = DeviceInput.GetInputInfo("Oculus", 1)
        controllerType = DeviceInput.GetControllerType("Oculus")

        #if doPrint:
            #print("hmd Stataus : " + str(hmdStatus))
            #print("Hmd Rot : " + str(hmdRot))
            #print("Hmd Pos : " + str(hmdPos))
            #print("Input Time : " + str(inputTime))
            #print("Button : " + str(buttonInfo))
            #print("Touch : " + str(touchInfo))
            #print("ControllerType : " + str(controllerType))

        #self.LPos = None
        #self.RPos = None

        for i in range(0,2):

            hand = i + 1

            handStatus = DeviceInput.GetHandStatus("Oculus", hand)

            if handStatus > 0:
                handRot = DeviceInput.GetHandOrientation("Oculus", hand)
                handPos = DeviceInput.GetHandPosition("Oculus", hand)
                tStick = DeviceInput.GetThumbStick("Oculus", hand, False);

                if hand == 1 :
                    self.lefthandTrigger = DeviceInput.GetTrigger("Oculus", hand, False, 0)
                    self.leftindexTrigger = DeviceInput.GetTrigger("Oculus", hand, False, 1)
                elif hand == 2 :
                    self.righthandTrigger = DeviceInput.GetTrigger("Oculus", hand, False, 0)
                    self.rightindexTrigger = DeviceInput.GetTrigger("Oculus", hand, False, 1)

                """if i == 0:
                    #LPos = handPos
                else:
                    #RPos = handPos"""

                if doPrint:
                    #if i == 0:
                    #    print("Left Hand >>")
                    #elif i == 1:
                    #    print("Right Hand >>")
                    #
                    #print("HandStatus : " + str(handStatus))

                    if handStatus > 0 :

                        print("HandRot : " + str(handRot))
                        print("HandPos : " + str(handPos))
                        #print("HandTrigger : " + str(handTrigger))
                        #print("IndexTrigger : " + str(indexTrigger))
                        #print("ThumbStick : " + str(tStick))

                #UpdatePosition
                nPos = camPosition + handPos
                nRot = handRot

                if hand == 1:
                    if self._LeftHandTransform!= None:
                        self._LeftHandTransform.PropTransform.SetPosition(nPos)
                        self._LeftHandTransform.PropTransform.SetRotation(nRot)
                elif hand == 2:
                    if self._RightHandTransform != None:
                        self._RightHandTransform.PropTransform.SetPosition(nPos)
                        self._RightHandTransform.PropTransform.SetRotation(nRot)

                #UseButton
                btnYCheck = buttonInfo & DeviceInput.ovrButton_Y
                btnXCheck = buttonInfo & DeviceInput.ovrButton_X
                btnBCheck = buttonInfo & DeviceInput.ovrButton_B
                btnACheck = buttonInfo & DeviceInput.ovrButton_A
                #if right hand's trigger pressed -> accel

                if self.leftindexTrigger > 0.8 :
                    self.CarScript.OnMessage("break_on",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    self.CarScript.OnMessage("accel_off",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                elif self.rightindexTrigger > 0.8 :
                    self.CarScript.OnMessage("accel_on",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    self.CarScript.OnMessage("break_off",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))

                if (self.righthandTrigger > 0.8 and (self.switchhandle==0 or self.switchhandle==1)):
                    self.CarScript.OnMessage("right_on",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    self.switchhandle = 1
                else :
                    self.CarScript.OnMessage("right_off",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    self.switchhandle = 0

                if (self.lefthandTrigger>0.8 and (self.switchhandle ==0 or self.switchhandle == 2)) :
                    self.CarScript.OnMessage("left_on",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    self.switchhandle= 2
                else :
                    self.CarScript.OnMessage("left_off",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    self.switchhandle = 0

                if btnYCheck > 0:
                    #turn engine on
                    DeviceInput.SetVibration("Oculus", 1, 0.5, 0.5)
                    self.CarScript.OnMessage("start",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    if doPrint :
                        print("btnYClick")

                if btnXCheck > 0:
                    DeviceInput.SetVibration("Oculus", 1, 0, 0)
                    self.CarScript.OnMessage("kill",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    if doPrint :
                        print("btnXCheck")

                if btnBCheck > 0:
                    #B button for forward gear
                    self.CarScript.OnMessage("forward",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    DeviceInput.SetVibration("Oculus", 2, 0.5, 0.5)
                    if doPrint :
                        print("btnBCheck")

                if btnACheck > 0:
                    #A button for rear gear
                    self.CarScript.OnMessage("rear",0,Math3d.Vector4(0,0,0,0),Math3d.Vector4(0,0,0,0))
                    DeviceInput.SetVibration("Oculus", 2, 0, 0)
                    if doPrint :
                        print("btnACheck")

        #hLength = (RPos - LPos).Length()
        #print("Dis = " + str(hLength))

        return

    def OnMessage(self, msg, number, Vector4_lparm, Vector4_wparam):
        return;


"""Model for Slide game - keeps data in a double array, such that Model[x][y]
refers to the value at (x,y) on the grid displayed to the user."""
import random
import slideexceptions
from Observable import Observable
from GlobalConstants import *

class Model:

    def __init__(self, num):
        """
            Initilizes the model to represent a start board -
            A board of the given size with only one randomly placed block of value 2
            Will throw TypeError if num is not an integer greater than or equal to 2
        """
        if num < 2:
            raise TypeError('Arugment number must be larger than 2')
        self.size = int(num)
        self.unoccupied_squares = []
        l = []
        # build the grid of empty squares, and add all to the list of unoccupied squares
        for i in range(num):
            m = []
            for j in range(num):
                coordinate = i, j
                self.unoccupied_squares.append(coordinate)
                m.append(EMPTY_SQUARE_VALUE)
            l.append(m)
        # the grid is observable, so the controller can listen for changes
        self.grid = l
        self.last_move = Observable(None)

    def subscribe_to_moves(self, func):
        self.last_move.addCallback(func)

    def add_block_at(self, x, y, value):
        """
            Adds a block at a specific point on the board
            Overwrites any value that is at the coordinate defined by (x,y)
            Does not check to see if coordinate is valid before adding
        """
        self.grid[x][y] = value
        if (x, y) in self.unoccupied_squares and value != EMPTY_SQUARE_VALUE:
            self.unoccupied_squares.remove((x, y))
        elif value == EMPTY_SQUARE_VALUE:
            self.unoccupied_squares.append((x, y))

    def add_new_block(self):
        """
            Adds a new block to a random non-occupied (value of zero) square on the board
            Returns the coordinates of the addition as a tuple
        """
        if len(self.unoccupied_squares) == 0:
            raise slideexceptions.AddBlockError("GameBoard is Full")
        new_x, new_y = self.unoccupied_squares.pop(int(random.random() * len(self.unoccupied_squares)))
        # randomly picks 2 or 4 as the value of the new block
        val = int(random.random() * 2 + 1) * 2
        self.grid[new_x][new_y] = val
        return (new_x, new_y)

    def value_at(self, x, y):
        """
            Gets the value of the grid at the coordinate (x,y) on the board.
            Arugments:
                x (int) - x coordinate
                y (int) - y coordinate
        """
        return self.grid[x][y]

    def count_blocks(self):
        """
            Counts the number of blocks on the board
            Returns an integer representing the number of blocks on the board
        """
        result = 0
        for i in range(self.size):
            for j in range(self.size):
                b = self.value_at(i, j)
                if b > EMPTY_SQUARE_VALUE:
                    result += 1
        return result

    def shift_blocks_left(self):
        """
            Shifts all blocks left and collapses same-valued blocks into their left neighbor
        """
        return self._shift_blocks(1, 1)

    def shift_blocks_right(self):
        """
            Shifts all blocks right and collapses same-valued blocks
        """
        return self._shift_blocks(1, 0)

    def shift_blocks_up(self):
        """
            Shifts all blocks up and collapses same-valued blocks
        """
        return self._shift_blocks(0, 1)

    def shift_blocks_down(self):
        """
            Shifts all blocks down and collapses same-valued blocks
        """
        return self._shift_blocks(0, 0)

    def _shift_blocks(self, shift_horizontal, shift_to_bottom_left):
        """
            Abstraction for shifting blocks in a direction
            Takes two arguements:
            shift_horizontal: Boolean - True means movement left-right, False means up-down
            shift_to_bottom_left: Boolean - True means movement toward origin(down,left), false means away(up,right)
            this means that the four direction have the following arguments:
            Left:  self._shift_blocks(1,1)
            Right: self._shift_blocks(1,0)
            Up:    self._shift_blocks(0,0)
            Down:  self._shift_blocks(0,1)
            returns True if no blocks have moved positions, otherwise returns false
        """
        no_merged = True
        no_moves = 0
        for outer_iteration_value in range(self.size):
            last_block_val = EMPTY_SQUARE_VALUE
            # initialize the last open grid square to be the first square that will be checked, which is the lowest
            # number on the axis if moving down or left, otherwise it is the highest
            if shift_to_bottom_left:
                last_open = 0
            else:
                last_open = self.size - 1
            for inner_iteration_value in range(self.size):
                # if the blocks are being shifted down or left, the algorithm should look for blocks
                # with a lower coordinate value on the axis of movement (Y and X respectively), and should
                # begin its search from the lowest value of the axis of non movement (X and Y respectively)
                # if the blocks are being shfited up or right, the opposite is true
                if shift_to_bottom_left:
                    directional_adjustment = -1
                else:
                    inner_iteration_value = self.size - inner_iteration_value - 1
                    directional_adjustment = 1
                # if the blocks are being shifted horizontailly, iterate first by y coord, then x coord, i.e.
                # check spot (0,0), then (0,1), then (0,2). Otherwise, do the reverse
                if shift_horizontal:
                    x_coord_old = inner_iteration_value
                    y_coord_old = outer_iteration_value
                else:
                    x_coord_old = outer_iteration_value
                    y_coord_old = inner_iteration_value
                val = self.grid[x_coord_old][y_coord_old]
                # Check for case where blocks could merge
                if val > EMPTY_SQUARE_VALUE and val == last_block_val:
                    last_block_val = val + val
                    m = self._block_merge(last_block_val, x_coord_old, y_coord_old, last_open,
                                          directional_adjustment, shift_horizontal, shift_to_bottom_left)
                    self.last_move.set(m)
                    no_merged = False
                # Check for case where block could slide and collide
                elif val > EMPTY_SQUARE_VALUE:
                    m = self._block_collide(val, x_coord_old, y_coord_old, last_open,
                                            shift_horizontal, shift_to_bottom_left)
                    last_block_val = val
                    last_open = last_open + (0 - directional_adjustment)
                    if m[0] != m[1]:
                        self.last_move.set(m)
                    else:
                        no_moves += 1
        return no_moves == self.count_blocks() and no_merged

    def _block_merge(self, new_val, x_coord_old, y_coord_old, last_open, directional_adjustment,
                     shift_horizontal, shift_to_bottom_left):
        """
            Handles blocks of same value colliding with eachother and becoming a single block of twice
            their value
            Returns a tuple of coordinates - (old coordinates, new coordinates)
        """
        if shift_horizontal:
            x_coord_new = last_open + directional_adjustment
            y_coord_new = y_coord_old
        else:
            x_coord_new = x_coord_old
            y_coord_new = last_open + directional_adjustment
        self.grid[x_coord_new][y_coord_new] = new_val
        self.grid[x_coord_old][y_coord_old] = EMPTY_SQUARE_VALUE
        self.unoccupied_squares.append((x_coord_old, y_coord_old))
        old_coords = (x_coord_old, y_coord_old)
        new_coords = (x_coord_new, y_coord_new)
        return (old_coords, new_coords)

    def _block_collide(self, new_val, x_coord_old, y_coord_old, last_open, shift_horizontal,
                       shift_to_bottom_left):
        """
            Handles blocks moving and colliding with their right-most obstacle
            (wall or block of different value)
            Returns a tuple of coordinates - (old coordinates, new coordinates)
        """
        if shift_horizontal:
            x_coord_new = last_open
            y_coord_new = y_coord_old
        else:
            x_coord_new = x_coord_old
            y_coord_new = last_open
        self.grid[x_coord_old][y_coord_old] = 0
        self.grid[x_coord_new][y_coord_new] = new_val
        self.unoccupied_squares.append((x_coord_old, y_coord_old))
        self.unoccupied_squares.remove((x_coord_new, y_coord_new))
        old_coords = (x_coord_old, y_coord_old)
        new_coords = (x_coord_new, y_coord_new)
        return (old_coords, new_coords)

    def game_state_check(self):
        """
            Returns the state of the game, which is one of the three values in the game_states
            class dictionary
        """
        column_maxes = []
        for column in self.grid:
            column_maxes.append(max(column))
        board_max = max(column_maxes)
        if board_max == GAME_GOAL:
            return GAME_STATE["Win"]
        if self.no_valid_moves():
            return GAME_STATE["Loss"]
        return GAME_STATE["Play"]

    def no_valid_moves(self):
        """
            Determine if there are any valid moves available
            Returns a boolean - True if any of the four moves are possible
        """
        if self.count_blocks() == (self.size * self.size):
            #The board is full of blocks, now check if any could be merged
            for i in range(self.size):
                for j in range(self.size):
                    val = self.grid[i][j]
                    left = (i > 0) and (self.grid[i - 1][j] == val)
                    right = (i < self.size - 1) and (self.grid[i + 1][j] == val)
                    down = (j > 0) and (self.grid[i][j - 1] == val)
                    up = (j < self.size - 1) and (self.grid[i][j + 1] == val)
                    if left or right or down or up:
                        #Theres a direction that you can move in!
                        return False
            # Theres no direction you can move in!
            return True
        # Board is not full, so obviously there is a spot a block could slide into
        return False


import cv2
import numpy as np
import sys

if (len(sys.argv) > 1):
    filename = sys.argv[1]
else:
    print('Pass a filename as first argument')
    sys.exit(0)

img1 = cv2.imread(filename, cv2.IMREAD_COLOR)
img2 = img1.copy()
DEFAULT = img1.copy()
DEFAULT2 = img2.copy()

def get_points_img1(event, x, y, flags, param):
    global img1_points, img1_click_n, img1, DEFAULT

    if event == cv2.EVENT_LBUTTONDOWN:

        if img1_click_n == 3:
            img1 = DEFAULT.copy()
            img1_points = [(-1,-1), (-1,-1), (-1,-1)]
            img1_click_n = 0

        if img1_click_n == 0:
            img1_points[0] = (x, y)

        elif img1_click_n == 1:
            img1_points[1] = (x, y)

        elif img1_click_n == 2:
            img1_points[2] = (x, y)

        cv2.circle(img1, (x, y), 5, (255,0,200), -1)
        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(img1, str(img1_click_n+1), (x+5, y+4), font, 0.5, (0,0,0), 1, cv2.LINE_AA)

        img1_click_n += 1

    if event == cv2.EVENT_RBUTTONDOWN:

        img1 = DEFAULT.copy()
        img1_points = [(-1,-1), (-1,-1), (-1,-1)]
        img1_click_n = 0

def get_points_img2(event, x, y, flags, param):
    global img2_points, img2_click_n, img2, DEFAULT2

    if event == cv2.EVENT_LBUTTONDOWN:

        if img2_click_n == 3:
            img2 = DEFAULT.copy()
            img2_points = [(-1,-1), (-1,-1), (-1,-1)]
            img2_click_n = 0

        if img2_click_n == 0:
            img2_points[0] = (x, y)

        elif img2_click_n == 1:
            img2_points[1] = (x, y)

        elif img2_click_n == 2:
            img2_points[2] = (x, y)

        cv2.circle(img2, (x, y), 5, (255,0,200), -1)
        font = cv2.FONT_HERSHEY_SIMPLEX
        cv2.putText(img2, str(img2_click_n+1), (x+5, y+4), font, 0.5, (0,0,0), 1, cv2.LINE_AA)

        img2_click_n += 1

    if event == cv2.EVENT_RBUTTONDOWN:

        img2 = DEFAULT2.copy()
        img2_points = [(-1,-1), (-1,-1), (-1,-1)]
        img2_click_n = 0

img1_click_n = 0
img1_points = [(-1,-1), (-1,-1), (-1,-1)]
img2_click_n = 0
img2_points = [(-1,-1), (-1,-1), (-1,-1)]

cv2.namedWindow('image1')
cv2.namedWindow('image2')
cv2.setMouseCallback('image1', get_points_img1)
cv2.setMouseCallback('image2', get_points_img2)

while(1):

    cv2.imshow('image1', img1)
    cv2.imshow('image2', img2)

    k = cv2.waitKey(1) & 0xFF

    if k == 27:
        break

    if (img1_click_n == 3 & img2_click_n == 3):

        pts1 = np.float32([img1_points[0], img1_points[1], img1_points[2]])
        pts2 = np.float32([img2_points[0], img2_points[1], img2_points[2]])
        M = cv2.getAffineTransform(pts1, pts2)
        img1_h, img1_w, img1_c = np.shape(img1)
        cv2.warpAffine(DEFAULT2, M, (img1_w, img1_h), img1)

cv2.destroyAllWindows()