# FILE:         interface.py
#
# SUMMARY:      Inheritable class interface/protocol support for Python; implements class `Interface' and conformance functions
# USAGE:        from interface import *; inherit from `Interface'; call one of the conformance functions described below
# KEYWORDS:     interface, utility
#
# AUTHOR:       Robert Weiner
# LICENSE:      Available under the terms of the MIT License
#
# DESCRIPTION:
"""
See the "Interface" class documentation string for details on what
interfaces are and how to use them.

The following are public functions defined in this module, not
methods.  (They are functions since many must work on objects of any
type; the ones that take operands of more specific types, such as
`extends' are functions so that they may be called similarly; type
checks ensure that they raise exceptions if called with an invalid
argument).

     assert_implements(aClass)
         call after a class definition to assert interface conformance

     extends(interface, *interfaces):
         boolean test of whether an interface extends one or more other interfaces (via inheritance);
         this is false if any arg is not an interface

     implements(obj, *interfaces)
         boolean test of whether a class or instance implements one or more interfaces;
         this is false if any of `*interfaces' is not an interface

     interface_names(obj)
         return a list of interface names which obj:
             implements, if `obj' is a class or instance;
             extends, if `obj' is an interface
         the list begins with `obj' itself if it is an interface

     interfaces(obj)
         return a list of interface objects which obj:
             implements, if `obj' is a class or instance;
             extends, if `obj' is an interface
         the list begins with `obj' itself if it is an interface

     is_interface(obj)
         boolean test of whether `obj' is an interface or class of interface

     -----

     ancestor_names(obj, exclude_interfaces=0)
         return a list of ancestor class or interface names of `obj' (class, interface or instance),
         including `obj' itself; ancestors are returned in depth-first order, left to right;
         with optional `exclude_interfaces', interface ancestors are removed

     ancestors(obj, exclude_interfaces=0)
         return a list of ancestor class or interface objects of `obj' (class, interface or instance),
         including `obj' itself; ancestors are returned in depth-first order, left to right;
         with optional `exclude_interfaces', interface ancestors are removed

     flatten (*objs)
         return a single-level list of all atomic objects in `*obj' (anything other than
         sequences, dictionaries and Indexable types) in original order

     unique(*sequences)
         return a flattened list with duplicates removed from any number of atomic or `sequence' args;
         does not sort the elements
"""
# DESCRIP-END.

## ------------------------------------------------------------------------
## Required modules and version levels
## ------------------------------------------------------------------------

import sys
from types import *
from inspect import isclass

def require_python_version(min_version):
    """
    Raises an error if Python version is less than `min_version'.
    Require: `min_version' is a float or string of only digits and periods
    Return:  python version number split into a tuple
    Raise:   SystemError if Python version is less than `min_version'.
    """
    assert type(min_version) in (float, str)

    if type(min_version) is float:
        # Don't use `min_version` here since has a buggy float conversion in V2.0.
        min_version = "%s" % min_version
    # python_version_tuple would be (2, 0) for 2.0 final, for example
    python_version_tuple = tuple(map(int, sys.version.split()[0].split('.')))
    min_version_tuple = tuple(map(int, min_version.split('.')))
    if python_version_tuple >= min_version_tuple:
        return python_version_tuple

    raise SystemError("(%s): Requires Python %s or greater; running Python %s" % \
          (__name__, min_version, sys.version[0].split()))

require_python_version('3.0.0')

## ------------------------------------------------------------------------
## Classes
## ------------------------------------------------------------------------

class Interface:
    """
    A protocol to which classes may conform by implementing its method signatures, pre- and post- conditions and attributes.
    This is the top-level class from which all interfaces inherit.

    An "interface" is a class which inherits from "Interface" and provides
    a series of "stub methods" (which may have doc strings as well as pre-
    and post-condition assertions).

    A stub method is distinguished from other methods by naming its first argument,
    `iself', short for "interface self".  Each stub method exists to provide a
    calling protocol to which any "implementor" must conform.

    The implementor may either overload the stub method, replacing it completely
    or more commonly may overload its "body method", keeping the stub method's
    signature, doc string and pre- and post-conditions.  Body method names are
    computed from the interface stub method name by adding a ``_body'' to the
    name:
          stub      =>     stub_body
        __stub      =>   __stub_body
        __stub__    =>   __stub_body__

    At the start of each interface definition, each stub body within the
    class should be equated to the "Interface.error" method so that if the
    stub (or the stub body) is called, an exception will be signaled:

        stub_body1 = stub_body2 = Interface.error

    An interface i2 "extends" another interface i1 iff i2 inherits from i1.

    A class "implements" an interface (and thus conforms to it) iff:

       it inherits from the interface;

       it or its ancestors redefine/implement all of the stub or body methods
       declared by the interface, keeping the number of arguments per method the
       same and renaming the first arg of each stub method from `iself' to `self';

       and its definition is followed by a call to:
          assert_implements(aClass)
       this marks the class as a non-interface and confirms that it does in fact
       implement the interfaces from which it inherits.
    """

    ## Interface attributes

    # The next attribute is used to test whether or not a class is an interface.
    # Call "assert_implements(aClass)" after class definition to disable this
    # flag in classes which implement the interface.
    interface_flag = True

    # Interfaces set their method stub bodies to this method so that an error is
    # triggered if an implementor fails to redefine the stub and mistakenly calls
    # the stub method.
    def error(self, *unused):
        raise InterfaceError("(%s): failed to implement the above interface stub method" % self.__name__)

    # Define this method in each interface to prevent instantiation of interfaces.
    def __init__(self, *args):
        """
        Initializes a newly created instance.
        The arguments passed are those from the class constructor expression.
        If a base class has an __init__() method, the derived class's
        __init__() method must explicitly call it to ensure proper
        initialization of the base class part of the instance.  For example,
        "BaseClass.__init__(self, [args ...])".
        """
        raise InterfaceError('(%s): an interface may not be instantiated; if an implementor, define "__init__" to eliminate this error' \
              % self.__class__)

class InterfaceError(Exception):
    "Class of Interface-related exceptions."

    def __init__(self, value=None):
        self.value = value

    def __str__(self):
        return repr(self.value)

## ------------------------------------------------------------------------
## Public functions
## ------------------------------------------------------------------------

def assert_implements(aClass):
    """
    If `aClass' fails to implement any of its interfaces, print all non-conformance issues.
    This should be called immediately after class definitions to validate proper interface conformance.
    Return: True if `aClass' implements all interfaces or if assertions are disabled (__debug__ is False)
    Raise: TypeError if `aClass' is not a class
           InterfaceError if __debug__ is enabled and `aClass' fails to conform to any of its interfaces
    """

    # Optimize away this entire function whenever assertions are ignored, i.e. when __debug__ is False
    # or "python -O" is used.
    if not __debug__:
        return True

    # Must allow for interfaces here because `aClass' is not declared
    # a non-interface until the end of this method.
    if not (isclass(aClass) or is_interface(aClass)):
        raise TypeError("(assert_implements): aClass arg `%s' is not a class" % aClass)

    # Add all ancestor interfaces to the implementation check
    interface_list = interfaces(aClass)
    errors = []
    for interface in interface_list:
        idict = interface.__dict__

        # Set istub_tuples to interface's stub methods given as (name, method) tuples.
        # Remove non-stub attributes and body methods.
        istub_tuples = [item for item in idict.items() \
                        if type(item[1]) is FunctionType and \
                        item[1].__code__.co_varnames[0] is 'iself']

        # Ensure all interface stub methods are redefined by the current class
        # and that the number of arguments to each remains the same.
        for stub_name, stub_method in istub_tuples:
            body_name = interface_body_name(stub_name)
            impl_method = getattr(aClass, stub_name)
            try:
                if hasattr(aClass, body_name):
                    body_method = getattr(aClass, body_name)
                    body_argcount = body_method.__code__.co_argcount
                else:
                    body_method = body_argcount = None

                body_method_unimplemented = body_method and body_method is Interface.error \
                                            or issubclass(interface, body_method.__class__)
                if impl_method is stub_method and not body_method or body_method_unimplemented:
                    # method is not implemented
                    errors.append("(%s): failed to define %s interface method %s or %s" % \
                                  (aClass.__name__, interface.__name__, body_name, stub_name))

                impl_method_argcount = impl_method.__code__.co_argcount
                stub_argcount = stub_method.__code__.co_argcount
                if impl_method != stub_method and impl_method_argcount != stub_argcount:
                    # impl_method has wrong number of args
                    errors.append("(%s.%s): takes %d args, instead of %d specified by %s.%s" % \
                                  (aClass.__name__, stub_name, impl_method_argcount, \
                                   stub_argcount, interface.__name__, stub_name))
                elif body_method and not body_method_unimplemented and body_argcount != stub_argcount:
                    # body_method has wrong number of args
                    errors.append("(%s.%s): takes %d args, instead of %d specified by %s.%s" % \
                                  (aClass.__name__, body_name, body_argcount,
                                   stub_argcount, interface.__name__, body_name))

            except AttributeError:
                if hasattr(aClass, stub_name) or hasattr(aClass, body_name):
                    pass
                else:
                    errors.append("(%s): failed to define %s interface method %s or %s" % \
                                  (aClass.__name__, interface.__name__, body_name, stub_name))

    if errors:
        print("assert_implements(%s) errors:" % aClass.__name__)
        for e in errors: print("  ", e)
        raise InterfaceError("(assert_implements): failed for %s" % aClass.__name__)
    elif interface_list:
        # Since interfaces don't implement other interfaces (they only extend them),
        # `aClass' must be a regular class, so disable the interface attribute.
        aClass.interface_flag = False
        return True
    else:
        return False

def extends(interface, *interfaces):
    """
    Return: True if `interface' inherits from all interfaces in `interfaces', else False.
    Raise: TypeError, if any argument is not an interface.
    """
    if not is_interface(interface):
        raise TypeError('(extends): first arg %s must be an interface; try using "implements" instead' % interface)
    if False in map(is_interface, interfaces):
        raise TypeError('(extends): some arg from interfaces is not an interface: %s' % interfaces)
    return all([issubclass(interface, anc_interface) for anc_interface in interfaces])

def implements(obj, *interfaces):
    "Return: True if obj (a class or instance) implements all interfaces in `interfaces', else False."
    if isclass(obj):
        return __class_implements(obj, interfaces)
    elif isinstance(obj, object):
        return __instance_implements(obj, interfaces)
    elif is_interface(obj):
        return False
    else:
        # !!! Later will have to add support for Python builtin types.
        # For now, just signal an error for any other input.
        raise TypeError("(implements): obj arg `%s' must be a class or instance" % obj)

def interface_body_name(imethod_name):
    "Return: the name of the body method for interface method `imethod_name'."
    if type(imethod_name) != str:
        raise TypeError("(%s.body_name): `imethod_name' must be a string: %s" % \
              (self.__name__, imethod_name))
    if len(imethod_name) < 3 or imethod_name[-2:] != "__":
        return imethod_name + "_body"
    else:
        return imethod_name[:-2] + "_body" + imethod_name[-2:]

def interface_names(obj):
    """
    Return: a list of interface names to which `obj' is conformant.
    The list begins with `obj' itself if it is an interface.
    Names are returned in depth-first order, left to right.
    """
    return [o.__name__ for o in interfaces(obj)]

def interfaces(obj):
    """
    Return: a list of interface objects to which `obj' conforms.
    The list begins with `obj' itself if it is an interface.
    Interfaces are returned in depth-first order, left to right.
    """
    if not isclass(obj) and isinstance(obj, object):
        obj = obj.__class__
    if isclass(obj) or is_interface(obj):
        return [i for i in ancestors(obj) if is_interface(i)]
    else:
        # !!! Later will have to add support for Python builtin types.
        # For now, just return None
        return None

def is_interface(obj):
    """
    Return: True if `obj' is an interface or class of interface (inherits from Interface and contains stub methods), else False.
    """
    return issubclass(obj, Interface) if isclass(obj) else getattr(obj, 'interface_flag', False)

## -----

def ancestor_names(obj, exclude_interfaces=0):
    """
    Return: a list of ancestor class or interface names of `obj', starting with `obj' itself.
    Ancestors are returned in depth-first order, left to right.
    With optional `exclude_interfaces', interface ancestors are removed.
    """
    return [a.__name__ for a in ancestors(obj, exclude_interfaces)]

def ancestors(obj, exclude_interfaces=0):
    """
    Return: a list of ancestor class and interface objects of `obj', starting with `obj' itself.
    Ancestors are returned in depth-first order, left to right.
    With optional `exclude_interfaces', interface ancestors are removed.
    """
    assert isclass(obj) or is_interface(obj) or isinstance(obj, object), \
           "(ancestors): obj arg `%s' must be a class, interface or instance" % obj

    if not isclass(obj) and isinstance(obj, object):
        obj = obj.__class__

    if exclude_interfaces:
        repeated_ancestors = __get_ancestors_no_interfaces(obj)
    else:
        repeated_ancestors = __get_ancestors(obj)

    return list(set(repeated_ancestors))

def flatten (*objs):
    """
    Return: a single-level list of all atoms in `*objs' in original order.
    Any object type other than a sequence, dictionary or Indexable type is considered atomic
    by this function.

    SeeAlso: `sys.setrecursionlimit' to increase recursion limit if need be.
    """
    ## Test case:   flatten(('a', 'b', ('c', 'd')), 'e', ('f', ('g', ['h', ('i', 'j'), ['k', 'l', 'm']], ('n'))))
    ## Should produce: => (a b c d e f g h i j k l m n)
    if len(objs) == 1: objs = objs[0]
    if objs in (None, (), [], {}, set()):
        return []
    elif type(objs) in (tuple, list, set, slice):
# !!!     Add next line after Indexable interface is defined and loaded:
#         or implements(objs, Indexable):
        if objs in (None, (), [], set()):
            return []
        else:
            return flatten(objs[0]) + flatten(objs[1:])
    elif type(objs) is dict:
        return flatten(objs.values())
    else:
        return [objs]

def unique(*sequences):
    """
    Return: a flattened list with duplicates removed from any number of atomic or `sequence' args.
    Do not sort the elements.
    """
    return list(set(flatten(*sequences)))

## ------------------------------------------------------------------------
## Private functions
## ------------------------------------------------------------------------

def __class_implements(aClass, interface_seq):
    "Return: True if `aClass' implements all interfaces in `interface_seq', else False."
    # Add all ancestor interfaces to the implementation check
    if interface_seq:
        interface_seq = unique(interfaces(*interface_seq))
    for interface in interface_seq:
        if not is_interface(interface):
            raise TypeError( \
                  "(__class_implements): aClass arg = `%s', interface arg `%s' must be an Interface" % \
                  (aClass.__name__, interface))
        if not (issubclass(aClass, interface) and __implements(aClass, interface)):
            return False

    return True

def __get_ancestors(aClass):
    if aClass in (None, (), [], {}, set()):
        return []
    else:
        return [aClass] + flatten([__get_ancestors(c) for c in aClass.__bases__])

def __get_ancestors_no_interfaces(aClass):
    if aClass in (None, (), [], {}, set()) or is_interface(aClass):
        return []
    else:
        return [aClass] + flatten([__get_ancestors_no_interfaces(c) for c in aClass.__bases__])

def __implements(obj, interface):
    idict = interface.__dict__

    # To be an interface implementor, either the interface has no methods
    # to implement (e.g. Interface) or a class must implement at least one
    # of the interface's methods.  This flag tracks whether `obj' has done so.
    implemented_a_method = False

    # Set istub_tuples to interface's stub methods given as (name, method) tuples.
    # Remove non-stub attributes
    istub_tuples = [item for item in idict.items() \
                    if type(item[1]) is FunctionType and \
                    item[1].__code__.co_varnames[0] is 'iself']

    # Ensure all interface stub methods are redefined by the current class
    # and that the number of arguments to each remains the same.
    try:
        for stub_name, stub_method in istub_tuples:
            impl_method = getattr(obj, stub_name)
            body_name = interface_body_name(stub_name)
            if hasattr(obj, body_name):
                body_method = getattr(obj, body_name)
                body_argcount = body_method.__code__.co_argcount
            else:
                body_method = body_argcount = None

            body_method_unimplemented = body_method and body_method is Interface.error \
                                        or issubclass(interface, body_method.__class__)
            if impl_method == stub_method and not body_method or body_method_unimplemented:
                # method is not implemented
                return False
            else:
                implemented_a_method = True

            impl_method_argcount = impl_method.__code__.co_argcount
            stub_argcount = stub_method.__code__.co_argcount
            if impl_method != stub_method and \
               impl_method_argcount != stub_argcount or \
               body_method and not body_method_unimplemented and \
               body_argcount != stub_argcount:
                # method has wrong number of args
                return False
    except AttributeError:
        return False

    return not istub_tuples or implemented_a_method

def __instance_implements(instance, interface_seq):
    "Return: True if `instance' implements `interface_seq', else False."
    # Add all ancestor interfaces to the implementation check
    if interface_seq:
        interface_seq = unique(interfaces(*interface_seq))
    for interface in interface_seq:
        if not is_interface(interface):
            raise TypeError( \
                  "(__instance_implements): instance arg = `%s', interface arg `%s' must be an Interface" % \
                  (instance, interface))
        if not (isinstance(instance, interface) and __implements(instance, interface)):
            return False

    return True


class Solution:
    def canReach(self, s: str, minJump: int, maxJump: int) -> bool:
        # graph problem, use bfs to decide whether we can reach a node
        # longest is a helper variable that stores the longest index passed by in last step
        q = collections.deque([])
        q.append(0)
        longest = 0
        while q:
            idx = q.popleft()
            # we can skip those index before longest, to prevent repeated computation
            for j in range(max(idx + minJump, longest+1), min(idx + maxJump, len(s) - 1) + 1):
                if s[j] == '0':
                    if j == len(s) - 1:
                        return True
                    q.append(j)
            longest = idx + maxJump
        return False


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

"""Core functionality

License: See LICENSE file.

"""

import base64
import binascii
import datetime as dt
import logging
import os
import shutil
import sqlite3
import uuid
import filetype

from pathlib import Path

from typing import List
from typing import Tuple

from .__version__ import __version__
from .addressbook import Addressbook
from .addressbook import make_addressbook
from .dbproto import StructuredGroupCall
from .dbproto import StructuredGroupDataV1
from .dbproto import StructuredGroupDataV2
from .dbproto import StructuredMemberRole
from .dbproto import StructuredMentions
from .dbproto import StructuredReactions
from .exceptions import DatabaseEmptyError
from .exceptions import DatabaseNotFoundError
from .exceptions import DatabaseVersionNotFoundError
from .html import dump_thread
from .models import Attachment
from .models import GroupCallData
from .models import GroupUpdateData
from .models import MemberInfo
from .models import Mention
from .models import MMSMessageRecord
from .models import Quote
from .models import Reaction
from .models import Recipient
from .models import SMSMessageRecord
from .models import Thread
from .types import is_group_call
from .types import is_group_ctrl
from .types import is_group_v2_data
from .versioninfo import VersionInfo

logger = logging.getLogger(__name__)

def check_backup(backup_dir: Path) -> Tuple[Path, VersionInfo]:
    """Check that we have the necessary files and return VersionInfo"""
    db_file = backup_dir / "database.sqlite"
    if not db_file.exists():
        raise DatabaseNotFoundError(db_file)
    db_version_file = backup_dir / "DatabaseVersion.sbf"
    if not db_version_file.exists():
        raise DatabaseVersionNotFoundError(db_version_file)
    with open(db_version_file, "r") as fp:
        version_str = fp.read()

    version = version_str.split(":")[-1].strip()
    versioninfo = VersionInfo(version)

    if not versioninfo.is_tested_version():
        logger.warn("This database version is untested, please report errors.")
    return db_file, versioninfo

def get_color(db, recipient_id):
    """Extract recipient color from the database"""
    query = db.execute(
        "SELECT color FROM recipient_preferences WHERE recipient_ids=?",
        (recipient_id,),
    )
    color = query.fetchone()[0]
    return color

def get_sms_records(db, thread, addressbook):
    """Collect all the SMS records for a given thread"""
    sms_records = []
    sms_qry = db.execute(
        "SELECT _id, address, date, date_sent, body, type, "
        "delivery_receipt_count, read_receipt_count "
        "FROM sms WHERE thread_id=?",
        (thread._id,),
    )
    qry_res = sms_qry.fetchall()
    for (
        _id,
        address,
        date,
        date_sent,
        body,
        _type,
        delivery_receipt_count,
        read_receipt_count,
    ) in qry_res:
        data = get_data_from_body(_type, body, addressbook, _id)
        sms_auth = addressbook.get_recipient_by_address(str(address))
        sms = SMSMessageRecord(
            _id=_id,
            data=data,
            delivery_receipt_count=delivery_receipt_count,
            read_receipt_count=read_receipt_count,
            addressRecipient=sms_auth,
            recipient=thread.recipient,
            dateSent=date_sent,
            dateReceived=date,
            threadId=thread._id,
            body=body,
            _type=_type,
        )
        sms_records.append(sms)
    return sms_records

def get_attachment_filename(_id, unique_id, backup_dir, thread_dir):
    """Get the absolute path of an attachment, warn if it doesn't exist"""
    fname = f"Attachment_{_id}_{unique_id}.bin"
    source = os.path.abspath(os.path.join(backup_dir, fname))
    if not os.path.exists(source):
        logger.warn(
            f"Couldn't find attachment '{source}'. "
            "Maybe it was deleted or never downloaded?"
        )
        return None

    filetype_kind = filetype.guess(source)
    if filetype_kind is None:
        new_fname = fname
    else:
        extension = filetype_kind.extension
        new_fname =f"Attachment_{_id}_{unique_id}.{extension}"

    # Copying here is a bit of a side-effect
    target_dir = os.path.abspath(os.path.join(thread_dir, "attachments"))
    os.makedirs(target_dir, exist_ok=True)
    target = os.path.join(target_dir, new_fname)
    shutil.copy(source, target)
    url = "/".join([".", "attachments", new_fname])
    return url

def add_mms_attachments(db, mms, backup_dir, thread_dir):
    """Add all attachment objects to MMS message"""
    qry = db.execute(
        "SELECT _id, ct, unique_id, voice_note, width, height, quote "
        "FROM part WHERE mid=?",
        (mms._id,),
    )
    for _id, ct, unique_id, voice_note, width, height, quote in qry:
        a = Attachment(
            contentType=ct,
            unique_id=unique_id,
            fileName=get_attachment_filename(
                _id, unique_id, backup_dir, thread_dir
            ),
            voiceNote=voice_note,
            width=width,
            height=height,
            quote=quote,
        )
        mms.attachments.append(a)

def decode_body(body):
    """Decode a base64-encoded message body."""
    try:
        return base64.b64decode(body)
    except (TypeError, ValueError, binascii.Error) as e:
        logger.warn(f"Failed to decode body for message '{body}': {str(e)}")
        return None

def get_group_call_data(rawbody, addressbook, mid):
    """Get the data for a group call."""

    if not rawbody:
        return None

    try:
        structured_call = StructuredGroupCall.loads(rawbody)
    except (ValueError, IndexError, TypeError) as e:
        logger.warn(
            f"Failed to load group call data for message {mid}:\n"
            f"Error message: {str(e)}"
        )
        return None

    timestamp = dt.datetime.fromtimestamp(structured_call.when // 1000)
    timestamp = timestamp.replace(
        microsecond=(structured_call.when % 1000) * 1000
    )
    recipient = addressbook.get_recipient_by_uuid(structured_call.by)
    if recipient:
        initiator = recipient.name

    group_call_data = GroupCallData(
        initiator=initiator,
        timestamp=timestamp,
    )

    return group_call_data

def get_group_update_data_v1(rawbody, addressbook, mid):
    """Get the data for a Group V1 update.

    There are two lists of members:
    - One list by structures with telephone numbers and/or UUIDs
    - Another list by telephone numbers

    Some groups use the first (sometimes without filling the UUID field),
    some use the second, and some both.

    Merge both lists and indicate for each member if they were found by phone
    or by UUID (preferable)."""

    if not rawbody:
        return None

    try:
        structured_group_data = StructuredGroupDataV1.loads(rawbody)
    except (ValueError, IndexError, TypeError) as e:
        logger.warn(
            f"Failed to load group update data (v1) for message {mid}:\n"
            f"Error message: {str(e)}"
        )
        return None

    members = dict()
    for member in structured_group_data.members:
        name = None
        match_from_phone = False
        if member.uuid:
            name = addressbook.get_recipient_by_uuid(member.uuid).name
        elif member.phone:
            name = addressbook.get_recipient_by_phone(member.phone).name
            match_from_phone = True

        member_info = MemberInfo(
            name=name,
            phone=member.phone,
            match_from_phone=match_from_phone,
            admin=False,
        )
        members[member.phone] = member_info

    for phone_member in structured_group_data.phone_members:
        if not members.get(phone_member):
            name = addressbook.get_recipient_by_phone(phone_member).name
            member_info = MemberInfo(
                name=name,
                phone=phone_member,
                match_from_phone=True,
                admin=False,
            )
            members[phone_member] = member_info

    members_list = list()
    for key, value in members.items():
        members_list.append(value)

    group_update_data = GroupUpdateData(
        group_name=structured_group_data.group_name,
        change_by=None,
        members=members_list,
    )
    return group_update_data

def get_member_by_raw_uuid(raw_uuid: bytes, what: str, addressbook, mid: str):
    """Find a recipient from a binary UUID. Output their name from the
    addressbook or the textual UUID if not found."""

    try:
        text_uuid = str(uuid.UUID(bytes=raw_uuid))
    except ValueError as e:
        logger.warn(f"Failed to parse {what} UUID for message {mid}: {str(e)}")
        return None

    recipient = addressbook.get_recipient_by_uuid(text_uuid)
    member_name = recipient.name if recipient else text_uuid
    return member_name

def get_group_update_data_v2(rawbody, addressbook, mid):
    """Get the data for a Group V2 update.

    Group V2 updates use UUIDs exclusively to identify members. The update
    messages contain the old state, the new state, and a description of the
    changes. Parse the change description to print out:
    - The person who made the change (editor)
    - A new group name
    - Added (new) members
    - Deleted members

    Also parse the new state to print out the resulting list of members."""

    if not rawbody:
        return None

    try:
        structured_group_data = StructuredGroupDataV2.loads(rawbody)
    except (ValueError, IndexError, TypeError) as e:
        logger.warn(
            f"Failed to load group update data (v2) for message {mid}:\n"
            f"Error message: {str(e)}"
        )
        return None

    change = structured_group_data.change
    deleted_members = []
    new_members = []
    members = []
    change_by = None
    editor = None
    if (not change is None) and (not change.by is None):
        editor = get_member_by_raw_uuid(
            change.by, "update editor", addressbook, mid
        )
        if editor:
            change_by = MemberInfo(
                name=editor,
                phone=None,
                match_from_phone=False,
                admin=False,
            )

    if (not change is None) and (not change.new_members is None):
        for member in change.new_members:
            name = (
                get_member_by_raw_uuid(
                    member.uuid, "new member", addressbook, mid
                )
                or "Unknown"
            )
            admin = member.role == StructuredMemberRole.MEMBER_ROLE_ADMIN
            new_members.append(
                MemberInfo(
                    name=name, phone=None, match_from_phone=False, admin=admin
                )
            )

    if (not change is None) and (not change.deleted_members is None):
        for member in change.deleted_members:
            name = (
                get_member_by_raw_uuid(
                    member, "deleted member", addressbook, mid
                )
                or "Unknown"
            )
            deleted_members.append(
                MemberInfo(
                    name=name, phone=None, match_from_phone=False, admin=False
                )
            )

    state = structured_group_data.state
    for member in state.members:
        name = (
            get_member_by_raw_uuid(member.uuid, "member", addressbook, mid)
            or "Unknown"
        )
        admin = member.role == StructuredMemberRole.MEMBER_ROLE_ADMIN
        members.append(
            MemberInfo(
                name=name, phone=None, match_from_phone=False, admin=admin
            )
        )

    if (change is None) or (change.new_title is None):
        group_name = None
    else:
        group_name = change.new_title.value

    group_update_data = GroupUpdateData(
        group_name=group_name,
        change_by=change_by,
        members=members,
        new_members=new_members,
        deleted_members=deleted_members,
    )
    return group_update_data

def get_data_from_body(_type, body, addressbook, mid):
    """Decode data in the message body and provide a structured representation."""
    data = None
    if is_group_call(_type):
        data = get_group_call_data(decode_body(body), addressbook, mid)
    elif is_group_ctrl(_type):
        if is_group_v2_data(_type):
            data = get_group_update_data_v2(
                decode_body(body), addressbook, mid
            )
        else:
            data = get_group_update_data_v1(
                decode_body(body), addressbook, mid
            )

    return data

def get_mms_mentions(encoded_mentions, addressbook, mid):
    """Decode mentions encoded in a SQL blob."""
    mentions = {}
    if not encoded_mentions:
        return mentions

    try:
        structured_mentions = StructuredMentions.loads(encoded_mentions)
    except (ValueError, IndexError, TypeError) as e:
        logger.warn(
            f"Failed to load quote mentions for message {mid}:\n"
            f"Error message: {str(e)}"
        )
        return mentions

    for structured_mention in structured_mentions.mentions:
        recipient = addressbook.get_recipient_by_uuid(
            structured_mention.who_uuid
        )
        name = recipient.name
        mention = Mention(
            mention_id=-1, name=name, length=structured_mention.length
        )
        range_start = (
            0 if structured_mention.start is None else structured_mention.start
        )
        mentions[range_start] = mention

    return mentions

def get_mms_reactions(encoded_reactions, addressbook, mid):
    """Decode reactions encoded in a SQL blob."""
    reactions = []
    if not encoded_reactions:
        return reactions

    try:
        structured_reactions = StructuredReactions.loads(encoded_reactions)
    except (ValueError, IndexError, TypeError) as e:
        logger.warn(
            f"Failed to load reactions for message {mid}:\n"
            f"Error message: {str(e)}"
        )
        return reactions

    for structured_reaction in structured_reactions.reactions:
        recipient = addressbook.get_recipient_by_address(
            str(structured_reaction.who)
        )
        reaction = Reaction(
            recipient=recipient,
            what=structured_reaction.what,
            time_sent=dt.datetime.fromtimestamp(
                structured_reaction.time_sent // 1000
            ),
            time_received=dt.datetime.fromtimestamp(
                structured_reaction.time_received // 1000
            ),
        )
        reaction.time_sent = reaction.time_sent.replace(
            microsecond=(structured_reaction.time_sent % 1000) * 1000
        )
        reaction.time_received = reaction.time_received.replace(
            microsecond=(structured_reaction.time_received % 1000) * 1000
        )
        reactions.append(reaction)

    return reactions

def get_mms_records(
    db, thread, addressbook, backup_dir, thread_dir, versioninfo
):
    """Collect all MMS records for a given thread"""
    mms_records = []

    reaction_expr = versioninfo.get_reactions_query_column()
    quote_mentions_expr = versioninfo.get_quote_mentions_query_column()
    viewed_receipt_count_expr = versioninfo.get_viewed_receipt_count_column()

    qry = db.execute(
        "SELECT _id, address, date, date_received, body, quote_id, "
        f"quote_author, quote_body, {quote_mentions_expr}, msg_box, {reaction_expr}, "
        f"delivery_receipt_count, read_receipt_count, {viewed_receipt_count_expr} "
        "FROM mms WHERE thread_id=?",
        (thread._id,),
    )
    qry_res = qry.fetchall()
    for (
        _id,
        address,
        date,
        date_received,
        body,
        quote_id,
        quote_author,
        quote_body,
        quote_mentions,
        msg_box,
        reactions,
        delivery_receipt_count,
        read_receipt_count,
        viewed_receipt_count,
    ) in qry_res:
        quote = get_mms_quote(
            addressbook,
            quote_id,
            quote_author,
            quote_body,
            quote_mentions,
            _id,
        )

        decoded_reactions = get_mms_reactions(reactions, addressbook, _id)

        data = get_data_from_body(msg_box, body, addressbook, _id)
        mms_auth = addressbook.get_recipient_by_address(str(address))
        mms = MMSMessageRecord(
            _id=_id,
            data=data,
            delivery_receipt_count=delivery_receipt_count,
            read_receipt_count=read_receipt_count,
            addressRecipient=mms_auth,
            recipient=thread.recipient,
            dateSent=date,
            dateReceived=date_received,
            threadId=thread._id,
            body=body,
            quote=quote,
            attachments=[],
            reactions=decoded_reactions,
            _type=msg_box,
            viewed_receipt_count=viewed_receipt_count,
        )
        mms_records.append(mms)

    for mms in mms_records:
        add_mms_attachments(db, mms, backup_dir, thread_dir)

    return mms_records

def get_mms_quote(
    addressbook, quote_id, quote_author, quote_body, quote_mentions, mid
):
    """Retrieve quote (replied message) from a MMS message."""
    quote = None
    if quote_id:
        quote_auth = addressbook.get_recipient_by_address(quote_author)
        decoded_mentions = get_mms_mentions(quote_mentions, addressbook, mid)
        quote = Quote(
            _id=quote_id,
            author=quote_auth,
            text=quote_body,
            mentions=decoded_mentions,
        )
    return quote

def get_mentions(db, addressbook, thread_id, versioninfo):
    """Retrieve all mentions in the DB for the requested thread into a dictionary."""
    mentions = {}

    if versioninfo.are_mentions_supported():
        query = db.execute(
            "SELECT _id, message_id, recipient_id, range_start, range_length "
            "FROM mention WHERE thread_id=?",
            (thread_id,),
        )
        mentions_data = query.fetchall()

        for (
            _id,
            message_id,
            recipient_id,
            range_start,
            range_length,
        ) in mentions_data:
            name = addressbook.get_recipient_by_address(str(recipient_id)).name
            mention = Mention(
                mention_id=_id,
                name=name,
                length=range_length,
            )
            if not message_id in mentions.keys():
                mentions[message_id] = {}
            mentions[message_id][range_start] = mention

    return mentions

def get_members(
    db: sqlite3.Cursor,
    addressbook: Addressbook,
    thread_id: int,
    versioninfo: VersionInfo,
) -> List[Recipient]:
    """Retrieve the thread members from the database

    Returns
    -------
    members: List[Recipient]
        A list of Recipients for each member in the group.
    """
    thread_rid_column = versioninfo.get_thread_recipient_id_column()
    if versioninfo.is_addressbook_using_rids():
        query = db.execute(
            "SELECT r._id, g.members "
            "FROM thread t "
            "LEFT JOIN recipient r "
            f"ON t.{thread_rid_column} = r._id "
            "LEFT JOIN groups g "
            "ON g.group_id = r.group_id "
            "WHERE t._id = :thread_id",
            {"thread_id": thread_id},
        )
        query_result = query.fetchall()
        recipient_id, thread_members = query_result[0]
    else:
        query = db.execute(
            "SELECT t.recipient_ids, g.members "
            "FROM thread t "
            "LEFT JOIN groups g "
            "ON t.recipient_ids = g.group_id "
            "WHERE t._id = :thread_id",
            {"thread_id": thread_id},
        )
        query_result = query.fetchall()
        recipient_id, thread_members = query_result[0]

    if not thread_members is None:
        member_addresses = thread_members.split(",")
        members = []
        for address in member_addresses:
            recipient = addressbook.get_recipient_by_address(address)
            members.append(recipient)
    else:
        members = [addressbook.get_recipient_by_address(recipient_id)]
    return members

def populate_thread(
    db, thread, addressbook, backup_dir, thread_dir, versioninfo=None
):
    """Populate a thread with all corresponding messages"""
    sms_records = get_sms_records(db, thread, addressbook)
    mms_records = get_mms_records(
        db,
        thread,
        addressbook,
        backup_dir,
        thread_dir,
        versioninfo,
    )
    thread.sms = sms_records
    thread.mms = mms_records
    thread.mentions = get_mentions(db, addressbook, thread._id, versioninfo)
    thread.members = get_members(db, addressbook, thread._id, versioninfo)

def process_backup(backup_dir: Path, output_dir: Path):
    """Main functionality to convert database into HTML"""

    logger.info(f"This is signal2html version {__version__}")

    # Verify backup and open database
    db_file, versioninfo = check_backup(backup_dir)
    db_conn = sqlite3.connect(db_file)
    db = db_conn.cursor()

    # Check if database is empty
    qry = db.execute("SELECT COUNT(*) FROM sqlite_schema")
    record = qry.fetchone()
    if record == (0,):
        raise DatabaseEmptyError()

    # Get and index all contact and group names
    addressbook = make_addressbook(db, versioninfo)

    # Start by getting the Threads from the database
    recipient_id_expr = versioninfo.get_thread_recipient_id_column()

    query = db.execute(f"SELECT _id, {recipient_id_expr} FROM thread")
    threads = query.fetchall()

    # Combine the recipient objects and the thread info into Thread objects
    for _id, recipient_id in threads:
        recipient = addressbook.get_recipient_by_address(str(recipient_id))
        if recipient is None:
            logger.warn(f"No recipient with address {recipient_id}")

        t = Thread(_id=_id, recipient=recipient)
        thread_dir = t.get_thread_dir(output_dir, make_dir=False)
        populate_thread(
            db, t, addressbook, backup_dir, thread_dir, versioninfo=versioninfo
        )
        dump_thread(t, output_dir)

    db.close()


import csv
import h5py
from collections import defaultdict
import numpy as np
import os

def extract_detections(path_data, path_csv, path_output):

    CUBE_SZ = 54

    # parse the detections csv
    with open(path_csv, "r") as f:
        reader = csv.reader(f)
        list_detections = list(reader)
    list_detections.pop(0)
    detections = defaultdict(lambda: [])
    for entry in list_detections:
        detections[entry[0]].append((int(float(entry[1])),
                                     int(float(entry[2])),
                                     int(float(entry[3]))))

    log_filename = "{}.log".format(os.path.splitext(path_output)[0])

    # crop detected ROIs and write to hdf5 file
    i_counter = 0
    n_counter = len(detections.keys())
    with open(log_filename, "w") as log_file:
        with h5py.File(path_output, "w") as f_h5:
            for id, coords in detections.items():

            # load CT scan (ct_scan is [z, x, y])
                try:

                    # detections.csv seriesuid string is missing chars
                    file_id = [f for f in os.listdir(path_data) if id in f]
                    assert(len(file_id) == 1)
                    id_ = os.path.splitext(file_id[0])[0]

                    ct_scan = np.load(os.path.join(path_data,
                                                   "{}.npy".format(id_)))
                    crops = np.zeros((CUBE_SZ, CUBE_SZ, CUBE_SZ, len(coords)))

                    # pad ct_scan and crop
                    i_counter += 1
                    if i_counter % 10 == 0:
                        print("*** extracting {}/{}" \
                            .format(i_counter, n_counter))
                    ct_scan_shape = ct_scan.shape
                    ct_scan = np.pad(ct_scan, CUBE_SZ, "constant")
                    for i, xyz in enumerate(coords):

                        # fix offset in x,y (detections in 512x512 window)
                        xyz = list(xyz)
                        xyz[0] = xyz[0] - int((512-ct_scan_shape[1])/2)
                        xyz[1] = xyz[1] - int((512-ct_scan_shape[2])/2)

                        try:
                            crops[:, :, :, i] = ct_scan[
                                xyz[2] + CUBE_SZ : xyz[2] + 2 * CUBE_SZ,
                                xyz[0] + CUBE_SZ : xyz[0] + 2 * CUBE_SZ,
                                xyz[1] + CUBE_SZ : xyz[1] + 2 * CUBE_SZ]

                        except ValueError:
                            print("*** ERROR in {}".format(i_counter))
                            log_file.write("Error in {}, shape: {}, xyz: {}\n" \
                                .format(id_, ct_scan_shape, xyz))

                    # write
                    f_h5.create_dataset(id_,
                                        shape=crops.shape,
                                        dtype=np.int16,
                                        data=crops)

                except IOError:

                    print("*** ERROR in {}".format(i_counter))
                    log_file.write("File {}.npy not found!\n" \
                        .format(id, ct_scan_shape, xyz))


class Solution:
    def reverseParentheses(self, s: str) -> str:
        stack=[]
        for char in s:                      #O(n)
            if char==')':
                revWord=""
                while stack[-1]!='(': revWord+=stack.pop()          #O(n)
                stack.pop()
                for char in revWord: stack.append(char)       #O(n)
            else:stack.append(char)
        return "".join(stack)

    #analysis: Time O(n^2)
    #Space O(n)
    #ref: 11/22/2022P2:track1-cpGrind75;3QuestionPerDay(perQuestion-x3pomo),perDay-x10pomo;35hoursperWeek;15hrPerTopic+15hrPerTopic+5hrBuffer;Day45/46:1.(P1:doPaypalLeetcodeQuestion4/5(https://github.com/hxu296/leetcode-company-wise-problems-2022/blob/main/companies/Paypal.csv),2.happyNumberTimed25Mins-x1pomo(5:30-6:00),3.implement-x2pomo(6:00-7:00),4.squaresOfASortedArrayTimed25Mins-x1pomo(7:00-7:30),5.implement-x2pomo(7:30-8:30),6.reverse-substrings-between-each-pair-of-parenthesesTimed25Mins-x1pomo(8:30-9:00),6.implement-x2pomo(9:00-10:00),7.basicCalculatorAnswers-x1pomo(10:00-10:30),8.basicCalculator2Timed25Mins-x1pomo(10:30-11:00),9.implement-x2pomo(11:00-12:00)=x13pomo(5:30-12:00)


# implementation of card game - Memory

import simplegui
import random

CARD_SIZE = (50, 100)
TEXT_DISPLACEMENT = (13, 65)
DECK_SIZE = 16

deck = None
exposed = None
state = None
first_card_idx = None
second_card_idx = None
turns = None

# helper function to initialize globals
def new_game():
    """ Start a new game. """
    global deck
    global exposed
    global state
    global turns

    # builds deck
    deck = range(DECK_SIZE / 2) + range(DECK_SIZE / 2)
    # and shuffles
    random.shuffle(deck)

    # every card starts hidden
    exposed = [False] * DECK_SIZE
    state = 0
    turns = 0

    label.set_text("Turns = " + str(turns))

def memory_state_machine(card_idx):
    """ Flip cards and check for pairs."""

    global state
    global first_card_idx
    global second_card_idx
    global turns

    # first click
    if state == 0:
        # register first exposed card
        first_card_idx = card_idx
        state = 1
    # even clicks
    elif state == 1:
        # register second exposed card
        second_card_idx = card_idx
        # update the number of turns
        turns += 1
        label.set_text("Turns = " + str(turns))
        state = 2
    # odd clicks
    else:
        # if the two last exposed cards have different values
        if deck[first_card_idx] != deck[second_card_idx]:
            # turn previous cards down
            exposed[first_card_idx] = False
            exposed[second_card_idx] = False
        # register the first exposed card
        first_card_idx = card_idx
        state = 1

    # always flip current card
    exposed[card_idx] = True

# define event handlers
def mouseclick(pos):
    """ Pick a card. """

    select_card_idx = pos[0] / CARD_SIZE[0]

    # ignore clicks on exposed cards
    if not exposed[select_card_idx]:
        # flip card and check for pairs
        memory_state_machine(select_card_idx)

# cards are logically 50x100 pixels in size
def draw(canvas):
    """ Draw the game board. """

    # card position
    pos = (0,0)

    # display cards
    for card_index in range(DECK_SIZE):
        card = deck[card_index]
        if exposed[card_index]:
            # exposed card
            canvas.draw_text(
                str(card),
                (pos[0]+ TEXT_DISPLACEMENT[0], pos[1] + TEXT_DISPLACEMENT[1]),
                40,
                'White')
        else:
            # hidden card
            canvas.draw_polygon([
                pos,
                (pos[0], pos[1] +  CARD_SIZE[1]),
                (pos[0] + CARD_SIZE[0], pos[1] + CARD_SIZE[1]),
                (pos[0] + CARD_SIZE[0], pos[1])],
                1,
                'Brown',
                'Green')
        #updates card position
        pos = (pos[0] + CARD_SIZE[0], pos[1])

# create frame and add a button and labels
frame = simplegui.create_frame("Memory", 800, 100)
frame.add_button("Reset", new_game)
label = frame.add_label("Turns = " + str(turns))

# register event handlers
frame.set_mouseclick_handler(mouseclick)
frame.set_draw_handler(draw)

# get things rolling
new_game()
frame.start()


import os

from .config import (
    file_replacements,
    file_regex,
    gc,
)
from .file_load_save import (
    pickleload_nestedlist_list,
)

def regular_replacements():
    return repl_regular

def regex_replacements():
    return repl_regex

def update_regular():
    global repl_regular
    repl_regular = {}
    loaded = pickleload_nestedlist_list(file_replacements)
    if loaded:
        for k, v in loaded:
            repl_regular[k] = v

def update_regex():
    global repl_regex
    repl_regex = {}
    loaded = pickleload_nestedlist_list(file_regex)
    if loaded:
        for k, v in loaded:
            repl_regex[k] = v

update_regular()
update_regex()


from src.utils.general import pause_input
import pyautogui

def in_battle():
    """Determines if a pokemon battle is occurring.

    Args:
        NONE

    Returns:
        (x, y) - The fight button x and y coordinates as a tuple. Otherwise returns None.
    """
    fight_button = pyautogui.locateOnScreen('../../assets/fight.PNG', confidence=0.85)
    battle_chatbox = pyautogui.locateOnScreen('../../assets/battle_chatbox.PNG', confidence=0.85)

    if fight_button == None and battle_chatbox == None:
        print('Not in battle...')
        return None
    elif fight_button != None:
        print('In battle, returning FIGHT option...')
        return pyautogui.center(fight_button)
    elif fight_button == None and battle_chatbox != None:
        print('In battle, waiting for FIGHT option...')
        return None

def run():
    """Run away from a wild pokemon encounter.

    Args:
        NONE

    Returns:
        (x, y) - The run button x and y coordinates as a tuple. Otherwise returns None.
    """
    run_button = pyautogui.locateOnScreen('../../assets/run.PNG', confidence=0.85)

    if run_button != None:
        point = pyautogui.center(run_button)
        pyautogui.click(point.x, point.y)
        print('Ran away from battle!')

#TODO
def check_pp(next_move):
    """Check if the pokemon is out of pp. Need about one second delay before calling this function.

    Args:
        next_move - Index of next move to use instead.

    Returns:
        move - The move to use if no pp. Otherwise keep using previous move.
    """
    no_pp = pyautogui.locateOnScreen('../../assets/no_pp.PNG', confidence=0.75)

    if no_pp == None:
        return False

    pause_input(3)
    pyautogui.press('z')
    select_move(next_move)
    return True

def select_move(move):
    """Select the pokemon's battle move.

    Args:
        move - One of the following: 0, 1, 2, 3.

    Returns:
        NONE
    """
    if move == 0:
        pyautogui.press('left')
        pyautogui.press('up')

    if move == 1:
        pyautogui.press('right')
        pyautogui.press('up')

    if move == 2:
        pyautogui.press('left')
        pyautogui.press('down')

    if move == 3:
        pyautogui.press('right')
        pyautogui.press('down')

    pyautogui.press('z')
    #Second press is for horde battles
    pyautogui.press('z')
    print("Your pokemon used move: " + str(move) + '.')

def cancel_learning_move():
    """Give up on learning new move on level up.

    Args:
        NONE

    Return:
        boolean - True on successful cancellation, otherwise false.
    """
    new_move = pyautogui.locateOnScreen('../../assets/new_move.PNG', confidence=0.9)

    if new_move != None:
        cancel_button = pyautogui.locateOnScreen('../../assets/cancel.PNG', confidence=0.9)

        if cancel_button != None:
            cancel_point = pyautogui.center(cancel_button)
            pyautogui.click(cancel_point.x, cancel_point.y)
            pause_input(2)
            yes_button = pyautogui.locateOnScreen('../../assets/yes.PNG', confidence=0.9)
            point = pyautogui.center(yes_button)
            pyautogui.click(point.x, point.y)

        return True

    return False

def is_pokemon_fainted():
    """Determines if a pokemon has fainted.

    Args:
        NONE

    Returns:
        NONE
    """
    is_fainted = pyautogui.locateOnScreen('../../assets/fainted.PNG', confidence=0.8)

    if is_fainted == None:
        return False

    return True

def switch_fainted_pokemon_out(pokemon):
    """Switch the fainted pokemon out.

    Args:
        pokemon - Index of the new pokemon to switch in.

    returns:
        boolean - True if successfully switched pokemon in. Otherwise false.
    """
    if pokemon == 1:
        pyautogui.press('right')
        pyautogui.press('z')
        return True

    if pokemon == 2:
        pyautogui.press('right')
        pyautogui.press('right')
        pyautogui.press('z')
        return True

    if pokemon == 3:
        pyautogui.press('down')
        pyautogui.press('z')
        return True

    if pokemon == 4:
        pyautogui.press('down')
        pyautogui.press('right')
        pyautogui.press('z')
        return True

    if pokemon == 5:
        pyautogui.press('down')
        pyautogui.press('right')
        pyautogui.press('right')
        pyautogui.press('z')
        return True

    return False


import requests

import config

ids = []
estado = 'ERROR'

headers = {'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36'}

for id in ids:
    r = requests.post(config.url + '/set_status.php', data={'id': id, 'status': estado}, headers=headers)

    if r.status_code == 200:
        print(str(id), r.json())
    else:
        print(str(id), r.status_code)


# Problem 22
# Names score

n_file = open('extras/prob22_names.txt')

names = [string.replace('"', '') for string in str(n_file.read()).split(',')]

print(sum(map(lambda x, y: x * sum(((ord(ch) - 64) for ch in y)),
              [i for i in range(1, len(names) + 1)],
              sorted(names))))


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

from datetime import datetime, timedelta
from odoo import api, fields, models, _
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, float_compare
from odoo.exceptions import UserError

class SaleWorkorder(models.Model):
    _name = "sale.workorder"
    _description = 'Sale Work Order'
    _order = 'id desc'

    name = fields.Char(string='Work Order Reference', required=True, copy=False, readonly=True, index=True, default=lambda self: _('New'))
    manufacturing_ids = fields.One2many('mrp.production', 'sale_workorder_id', string='Manufacturing Lines')
    order_id = fields.Many2one('sale.order', string='Sale Order')
    requested_date = fields.Datetime(string='Requested Date',
    readonly=False, store=True, index=True, copy=False)
    commitment_date = fields.Datetime(string='Commitment Date',
    readonly=False, store=True, index=True, copy=False)
    partner_id = fields.Many2one('res.partner', string='Customer')
    partner_shipping_id = fields.Many2one('res.partner', string='Delivery Address')
    user_id = fields.Many2one('res.users', string='Salesperson')
    completed_wo = fields.Boolean('Hide Complete',compute="_completed_wo",store=False,readonly=True)
    client_order_ref = fields.Char(string="Customer PO",related="order_id.client_order_ref")
    completed_by = fields.Many2one('res.users', string='Completed By')
    state = fields.Selection([
        ('open', 'Open'),
        ('done', 'Completed'),
        ], string='Status', default="open",copy=False)
    carrier_id = fields.Many2one('delivery.carrier',string="Delivery Method")
    carrier_code = fields.Many2one('delivery.carrier.code',string="Delivery Carrier Code",related="carrier_id.carrier_code")
    shipping_account_id = fields.Many2one('customer.shipping.account',string="Customer Account")
    order_type = fields.Selection([
        ('order', 'Sales Order'),
        ('rma', 'RMA'),
        ], string='Order Type (Technical)', related="order_id.order_type")

    @api.constrains('partner_id')
    def set_carrier(self):
        for record in self:
            if record.partner_id and record.partner_id.commercial_partner_id and record.partner_id.commercial_partner_id.property_delivery_carrier_id:
                carrier = record.partner_id.commercial_partner_id.property_delivery_carrier_id
                record.carrier_id = carrier
                account = record.check_accounts()
                if account:
                    record.shipping_account_id = account

    @api.onchange('carrier_id')
    def carrier_onchange(self):
        for record in self:
            if record.carrier_id:
                account = record.check_accounts()
                if account:
                    record.shipping_account_id = account
                else:
                    record.shipping_account_id = False

    def check_accounts(self):
        for record in self:
            if record.partner_id.commercial_partner_id.shipping_account_ids and record.carrier_id:
                valid_accounts = record.partner_id.commercial_partner_id.shipping_account_ids.filtered(lambda x: x.carrier_id.carrier_code.id == record.carrier_id.carrier_code.id)
                if valid_accounts:
                    return valid_accounts[0]
                else:
                    return False

    @api.model
    def create(self, vals):
        if vals.get('name', _('New')) == _('New'):
            vals['name'] = self.env['ir.sequence'].next_by_code('sale.workorder.sequence') or _('New')
        result = super(SaleWorkorder, self).create(vals)
        return result

    @api.multi
    def delete_workorder(self):
        for wo in self:
            for mo in wo.manufacturing_ids:
                mo.action_cancel()
            wo.unlink()

    @api.multi
    def print_sale_workorder(self):
        for wo in self:
            active_id = wo.id
            datas = {'ids' : [active_id]}
            return {
                'type': 'ir.actions.report.xml',
                'report_name': 'sh_mrp_mod.report_workorder',
                'datas': datas,
            }

    @api.multi
    def complete_workorder(self):
        for wo in self:
            if any(m.state not in ['done','cancel'] for m in wo.manufacturing_ids):
                for m in wo.manufacturing_ids:
                    for w in m.workorder_ids:
                        if w.state not in ['done','cancel']:
                            w.record_production()
                    if m.state not in ['done','cancel']:
                        m.state = 'done'
                        m.button_mark_done()
            for p in wo.order_id.picking_ids.filtered(lambda x:x.state not in ['cancel','draft','done']):
                p.write({'requested_date':wo.requested_date,'commitment_date':wo.commitment_date,'carrier_id':wo.carrier_id.id,'customer_ship_account':wo.shipping_account_id.id,'sale_workorder_id':wo.id})
                p.action_assign()
                for operation in p.pack_operation_ids:
                    operation.qty_done = operation.product_qty
                p.do_transfer()
            wo.completed_by = wo.env.user
            wo.state = 'done'
            return True

    @api.multi
    def _completed_wo(self):
        for wo in self:
            if any(m.state not in ['done','cancel'] for m in wo.manufacturing_ids):
                wo.completed_wo = False
            else:
                wo.completed_wo = True


from unicodedata import name
from unittest import result

def lesser_of_two_evens (a,b):
    if a%2==0 and b%2==0 :
        if a>b:
            return b
        elif a< b:
            return a
    elif a%2!=0 or b%2!=0:
        if a>b:
            return a
        elif a< b:
            return b
        else :
            return b
print(lesser_of_two_evens(2,4))
print(lesser_of_two_evens(2,5))

def animal_crackers(text):
    ss=text.split()
    if ss[0][0]==ss[1][0]:
            print ('true')
    else :
            print('false')
print(animal_crackers('Levelheaded Llama'))
print(animal_crackers('Crazy Kangaroo'))

def makes_twenty (num1,number2):
    sum =int(num1+number2)
    if sum ==20 or number2==20 or num1 ==20:
        return('true')
    else:
        return('false')
print(makes_twenty(20,10))
print(makes_twenty(12,8))
print(makes_twenty(2,3))

'''def old_macdonald(name1):
    result=''
    for ss in range(len(name1)):
        if ss==0 or ss==3:
            result +=name1[ss].capatalize()
        else:
            result +=name1[ss]
    print (result)
print(old_macdonald('macdonald'))'''

'''def almost_there (number):
    if number in range (90,111) or number in range(190,211):
        print ('true')
    else:
        print('false')

print(almost_there(90))
print(almost_there(104))
print(almost_there(150))
print(almost_there(209))'''

def has_33(nums):
    result=False
    for nn in range(len(nums)-1):

        if nums[nn:nn+2]==[3,3]:
            return'true'

    return result
print(has_33([1, 3, 3]))
print(has_33([1, 3, 1, 3]))
print(has_33([3, 1, 3]))

def paper_doll(name1):
    result=''
    for nn in name1:
        for nm in range(3):
            result+=nn
    return result
print(paper_doll('Hello'))
print(paper_doll('Mississippi'))

def blackjack(a,b,c):
    sum=a+c+b
    if sum<=21:
        return sum
    elif a==11 or b==11 or c==11:
        return sum-10
    elif sum >21:
        return 'bust'
print(blackjack(5,6,7))
print(blackjack(9,9,9))
print(blackjack(9,9,11))

def summer_69(arr):
    sum=0
    for num in arr:
        if num in range(6,10):
            continue
        else:
            sum=sum+num
    return sum
print(summer_69([1, 3, 5]))
print(summer_69([4, 5, 6, 7, 8, 9]))

def count_primes(num):
    count=0
    for ss in range (2,num+1):
        pri=True
        for n in range (2,num):
            if (ss%n==0):
                pri=False
                break
        if pri:
            count+=1
    return count
print(count_primes(100))


import datetime
import json
import logging
import os
import time
from collections import OrderedDict, defaultdict
from functools import partial
from pprint import pformat

import numpy as np
import torch
import detectron2.utils.comm as comm
from detectron2.checkpoint import DetectionCheckpointer
from detectron2.config import get_cfg
from detectron2.data import MetadataCatalog
from detectron2.engine import DefaultTrainer, default_argument_parser, default_setup, hooks, launch, PeriodicWriter
from detectron2.evaluation import (
    CityscapesInstanceEvaluator,
    CityscapesSemSegEvaluator,
    COCOEvaluator,
    COCOPanopticEvaluator,
    DatasetEvaluators,
    LVISEvaluator,
    PascalVOCDetectionEvaluator,
    SemSegEvaluator,
    verify_results,
)
from detectron2.layers import get_norm
from detectron2.modeling import GeneralizedRCNNWithTTA
from detectron2.modeling.roi_heads import ROI_HEADS_REGISTRY, Res5ROIHeads
from detectron2.solver.build import maybe_add_gradient_clipping
from detectron2.utils.events import EventWriter

from lr_decay import get_default_optimizer_params, lr_factor_func

# [modification] for better logging
def _ex_repr(self):
    d = vars(self)
    ex = ', '.join(f'{k}={v}' for k, v in d.items() if not k.startswith('__') and k not in [
        'trainer', 'before_train', 'after_train', 'before_step', 'after_step', 'state_dict',
        '_model', '_data_loader', 'logger',
    ])
    return f'{type(self).__name__}({ex})'
hooks.HookBase.__repr__ = _ex_repr
EventWriter.__repr__ = _ex_repr

# [modification] add norm
@ROI_HEADS_REGISTRY.register()
class Res5ROIHeadsExtraNorm(Res5ROIHeads):
    """
    As described in the MOCO paper, there is an extra BN layer
    following the res5 stage.
    """

    def _build_res5_block(self, cfg):
        seq, out_channels = super()._build_res5_block(cfg)
        norm = cfg.MODEL.RESNETS.NORM
        norm = get_norm(norm, out_channels)
        seq.add_module("norm", norm)
        return seq, out_channels

class Trainer(DefaultTrainer):
    """
    We use the "DefaultTrainer" which contains pre-defined default logic for
    standard training workflow. They may not work for you, especially if you
    are working on a new research project. In that case you can write your
    own training loop. You can use "tools/plain_train_net.py" as an example.
    """

    # [modification] override the `build_optimizer` for using Adam and layer-wise lr decay
    lr_decay_ratio: float = 1.0
    @classmethod
    def build_optimizer(cls, cfg, model):
        is_resnet50 = int(cfg.MODEL.RESNETS.DEPTH) == 50
        if comm.is_main_process():
            dbg = defaultdict(list)
            for module_name, module in model.named_modules():
                for module_param_name, value in module.named_parameters(recurse=False):
                    if not value.requires_grad:
                        continue
                    lrf = lr_factor_func(f"{module_name}.{module_param_name}", is_resnet50=is_resnet50, dec=cls.lr_decay_ratio, debug=True)
                    dbg[lrf].append(f"{module_name}.{module_param_name}")
            for k in sorted(dbg.keys()):
                print(f'[{k}] {sorted(dbg[k])}')
            print()

        params = get_default_optimizer_params(
            model,
            base_lr=cfg.SOLVER.BASE_LR,
            weight_decay_norm=cfg.SOLVER.WEIGHT_DECAY_NORM,
            bias_lr_factor=cfg.SOLVER.BIAS_LR_FACTOR,
            weight_decay_bias=cfg.SOLVER.WEIGHT_DECAY_BIAS,
            lr_factor_func=partial(lr_factor_func, is_resnet50=is_resnet50, dec=cls.lr_decay_ratio, debug=False)
        )

        opt_clz = {
            'sgd': partial(torch.optim.SGD, momentum=cfg.SOLVER.MOMENTUM, nesterov=cfg.SOLVER.NESTEROV),
            'adamw': torch.optim.AdamW,
            'adam': torch.optim.AdamW,
        }[cfg.SOLVER.OPTIMIZER.lower()]
        return maybe_add_gradient_clipping(cfg, opt_clz)(params, lr=cfg.SOLVER.BASE_LR, weight_decay=cfg.SOLVER.WEIGHT_DECAY)

    @classmethod
    def build_evaluator(cls, cfg, dataset_name, output_folder=None):
        return build_evaluator(cfg, dataset_name, output_folder)

    @classmethod
    def test_with_TTA(cls, cfg, model):
        logger = logging.getLogger("detectron2.trainer")
        # In the end of training, run an evaluation with TTA
        # Only support some R-CNN models.
        logger.info("Running inference with test-time augmentation ...")
        model = GeneralizedRCNNWithTTA(cfg, model)
        evaluators = [
            cls.build_evaluator(
                cfg, name, output_folder=os.path.join(cfg.OUTPUT_DIR, "inference_TTA")
            )
            for name in cfg.DATASETS.TEST
        ]
        res = cls.test(cfg, model, evaluators)
        res = OrderedDict({k + "_TTA": v for k, v in res.items()})
        return res

def setup(args):
    """
    Create configs and perform basic setups.
    """
    cfg = get_cfg()
    # [modification] we add these two new keys
    cfg.SOLVER.OPTIMIZER, cfg.SOLVER.LR_DECAY = 'sgd', 1.0  # by default using SGD and no lr_decay
    cfg.merge_from_file(args.config_file)
    cfg.merge_from_list(args.opts)
    cfg.freeze()
    default_setup(cfg, args)
    return cfg

def main(args):
    cfg = setup(args)
    os.makedirs(cfg.OUTPUT_DIR, exist_ok=True)

    # [modification] for implementing lr decay and for logging
    Trainer.lr_decay_ratio = cfg.SOLVER.LR_DECAY

    if args.eval_only:
        model = Trainer.build_model(cfg)
        DetectionCheckpointer(model, save_dir=cfg.OUTPUT_DIR).resume_or_load(
            cfg.MODEL.WEIGHTS, resume=args.resume
        )
        res = Trainer.test(cfg, model)
        if cfg.TEST.AUG.ENABLED:
            res.update(Trainer.test_with_TTA(cfg, model))
        if comm.is_main_process():
            verify_results(cfg, res)
        return res

    # [modification] just skip some warnings
    import warnings
    comm.synchronize()
    warnings.filterwarnings('ignore', category=UserWarning)
    _ = np.arange(3, dtype=np.int).astype(np.bool)
    _ = np.array(torch.ones(3, dtype=torch.int32).numpy(), dtype=np.int)
    _ = np.array(torch.ones(3, dtype=torch.int64).numpy(), dtype=np.int)
    _ = np.array(torch.ones(3, dtype=torch.long).numpy(), dtype=np.int)
    _ = torch.rand(100) // 5
    _ = torch.meshgrid(torch.ones(1))
    warnings.resetwarnings()
    comm.synchronize()

    """
    If you'd like to do anything fancier than the standard training logic,
    consider writing your own training loop (see plain_train_net.py) or
    subclassing the trainer.
    """
    trainer = Trainer(cfg)
    trainer.resume_or_load(resume=args.resume)
    for h in trainer._hooks:
        if isinstance(h, PeriodicWriter):
            h._period = 1000  # [modification] less logging

    # [modification] we add some hooks for logging
    is_local_master = comm.get_rank() % args.num_gpus == 0
    if comm.is_main_process():
        print(f'[default hooks] {pformat(trainer._hooks, indent=2, width=300)}')
    ex_hooks = [
        hooks.EvalHook(0, lambda: trainer.test_with_TTA(cfg, trainer.model)) if cfg.TEST.AUG.ENABLED else None,
        LogHook(cfg.TEST.EVAL_PERIOD, args.config_file, cfg.OUTPUT_DIR, is_local_master) if comm.is_main_process() else None,
    ]
    trainer.register_hooks(ex_hooks)
    if comm.is_main_process():
        print(f'[extra hooks] {pformat(ex_hooks, indent=2, width=300)}')

    return trainer.train()

# [modification] we add a hook for logging results to `cfg.OUTPUT_DIR/d2_coco_log.txt`
class LogHook(hooks.HookBase):
    def __init__(self, eval_period, config_file, output_dir, is_local_master):
        self.eval_period = eval_period
        self.log_period = eval_period // 4
        self.log = {}

        self.is_master = comm.is_main_process()
        self.is_local_master = is_local_master

        self.config_file = config_file
        self.out_dir = output_dir
        self.log_txt_name = os.path.join(self.out_dir, 'd2_coco_log.txt')

    def __write_to_log_file(self, d):
        if self.is_local_master:
            self.log.update(d)
            with open(self.log_txt_name, 'w') as fp:
                json.dump(self.log, fp)
                fp.write('\n')

    def update_and_write_to_local_log(self):
        stat = self.trainer.storage.latest()
        self.log['boxAP'], self.log['bAP50'], self.log['bAP75'] = stat['bbox/AP'][0], stat['bbox/AP50'][0], stat['bbox/AP75'][0]
        self.log['mskAP'], self.log['mAP50'], self.log['mAP75'] = stat['segm/AP'][0], stat['segm/AP50'][0], stat['segm/AP75'][0]
        self.log['bAP-l'], self.log['bAP-m'], self.log['bAP-s'] = stat['bbox/APl'][0], stat['bbox/APm'][0], stat['bbox/APs'][0]
        self.log['mAP-l'], self.log['mAP-m'], self.log['mAP-s'] = stat['segm/APl'][0], stat['segm/APm'][0], stat['segm/APs'][0]
        all_ap = sorted([(v[0], k.split('AP-')[-1].strip()) for k, v in stat.items() if k.startswith('bbox/AP-')])
        all_ap = [tu[1] for tu in all_ap]
        self.log['easy'] = ' | '.join(all_ap[-7:])
        self.log['hard'] = ' | '.join(all_ap[:7])
        for k in self.log.keys():
            if 'AP' in k:
                self.log[k] = round(self.log[k], 3)
        self.__write_to_log_file({})

    def after_step(self):
        next_iter = self.trainer.iter + 1
        if self.eval_period > 0 and next_iter % self.eval_period == 0:
            self.update_and_write_to_local_log()

        if self.log_period > 0 and next_iter % self.log_period == 0:
            stat = self.trainer.storage.latest()
            remain_secs = round(stat['eta_seconds'][0])
            d = {
                'cfg': self.config_file,
                'rema': str(datetime.timedelta(seconds=remain_secs)), 'fini': time.strftime("%m-%d %H:%M", time.localtime(time.time() + remain_secs)),
                'cur_iter': f'{next_iter}/{self.trainer.max_iter}',
            }
            self.__write_to_log_file(d)

    def after_train(self):
        self.update_and_write_to_local_log()
        last_boxAP, last_mskAP = round(self.log['boxAP'], 3), round(self.log['mskAP'], 3)
        self.__write_to_log_file({
            'rema': '-', 'fini': time.strftime("%m-%d %H:%M", time.localtime(time.time() - 120)),
            'last_boxAP': last_boxAP,
            'last_mskAP': last_mskAP,
        })
        time.sleep(5)
        if self.is_master:
            print(f'\n[finished] ========== last_boxAP={last_boxAP}, last_mskAP={last_mskAP} ==========\n')

if __name__ == "__main__":
    args = default_argument_parser().parse_args()
    print("Command Line Args:", args)
    launch(
        main,
        args.num_gpus,
        num_machines=args.num_machines,
        machine_rank=args.machine_rank,
        dist_url=args.dist_url,
        args=(args,),
    )

def build_evaluator(cfg, dataset_name, output_folder=None):
    """
    Create evaluator(s) for a given dataset.
    This uses the special metadata "evaluator_type" associated with each builtin dataset.
    For your own dataset, you can simply create an evaluator manually in your
    script and do not have to worry about the hacky if-else logic here.
    """
    if output_folder is None:
        output_folder = os.path.join(cfg.OUTPUT_DIR, "inference")
    evaluator_list = []
    evaluator_type = MetadataCatalog.get(dataset_name).evaluator_type
    if evaluator_type in ["sem_seg", "coco_panoptic_seg"]:
        evaluator_list.append(
            SemSegEvaluator(
                dataset_name,
                distributed=True,
                output_dir=output_folder,
            )
        )
    if evaluator_type in ["coco", "coco_panoptic_seg"]:
        evaluator_list.append(COCOEvaluator(dataset_name, output_dir=output_folder))
    if evaluator_type == "coco_panoptic_seg":
        evaluator_list.append(COCOPanopticEvaluator(dataset_name, output_folder))
    if evaluator_type == "cityscapes_instance":
        return CityscapesInstanceEvaluator(dataset_name)
    if evaluator_type == "cityscapes_sem_seg":
        return CityscapesSemSegEvaluator(dataset_name)
    elif evaluator_type == "pascal_voc":
        return PascalVOCDetectionEvaluator(dataset_name)
    elif evaluator_type == "lvis":
        return LVISEvaluator(dataset_name, output_dir=output_folder)
    if len(evaluator_list) == 0:
        raise NotImplementedError(
            "no Evaluator for the dataset {} with the type {}".format(dataset_name, evaluator_type)
        )
    elif len(evaluator_list) == 1:
        return evaluator_list[0]
    return DatasetEvaluators(evaluator_list)


"""
=====
Distributed by: Notre Dame SCAI Lab (MIT Liscense)
- Associated publication:
url: https://arxiv.org/abs/2010.03957
doi:
github: https://github.com/zabaras/transformer-physx
=====
"""
import torch
from typing import Dict, List
from dataclasses import dataclass

Tensor = torch.Tensor

@dataclass
class DataCollator:
    """
    Data collator used for training datasets.
    Combines examples in a minibatch into one tensor.

    Args:
        examples (List[Dict[str, Tensor]]): List of training examples. An example
            should be a dictionary of tensors from the dataset.

        Returns:
            Dict[str, Tensor]: Minibatch dictionary of combined example data tensors
    """
    # Default collator
    def __call__(self, examples:List[Dict[str, Tensor]]) -> Dict[str, Tensor]:
        mini_batch = {}
        for key in examples[0].keys():
            mini_batch[key] = self._tensorize_batch([example[key] for example in examples])

        return mini_batch

    def _tensorize_batch(self, examples: List[Tensor]) -> Tensor:
        if not torch.is_tensor(examples[0]):
            return examples

        length_of_first = examples[0].size(0)
        are_tensors_same_length = all(x.size(0) == length_of_first for x in examples)

        if are_tensors_same_length:
            return torch.stack(examples, dim=0)
        else:
            raise ValueError("Padding not currently supported for physics transformers")
            return


import numpy as N
import scipy.io

def load_city_dist_matrix():
    return scipy.io.loadmat("dataset/city_dist_matrix.mat")["dist"]

def compute_transporation_matrix(city_dist, city_pop):
    t_mat = N.outer(1/N.maximum(city_pop,1), city_pop) / N.maximum(city_dist,1)
    N.fill_diagonal(t_mat, 0)
    return t_mat

def create_transfer_matrix(city_dist, city_pop, mobility=0.001, mobility_pop=0.05, t_mat=None):
    """
    mobility: tendency to leave a city
    mobility_pop: tendency to leave a city due to population
    """
    #assert 0 <= mobility     and mobility <= 1
    assert 0 <= mobility_pop and mobility_pop <= 1

    n = city_pop.shape[0]
    assert city_dist.shape == (n, n)

    city_pop = N.maximum(city_pop, 1)
    stay = mobility * (1 - mobility_pop) * city_pop / city_pop.sum()
    assert N.all(stay >= 0)

    if t_mat is None:
        t_mat = compute_transporation_matrix(city_dist, city_pop)

    prob = (1 - stay)[:,N.newaxis] * t_mat / t_mat.sum(axis=1)[:,N.newaxis]
    prob[range(n),range(n)] = stay

    return prob


from django.shortcuts import render,HttpResponse
from .models import Picture,Location,Category

# Create your views here.
def welcome(request):
    pictures = Picture.objects.all()
    kenya=Picture.filter_by_location(location='Kenya')
    india=Picture.filter_by_location(location='India')
    morocco=Picture.filter_by_location(location='Morocco')
    canada=Picture.filter_by_location(location='Canada')
    return render(request,'all-pictures/index.html',{'pictures':pictures, 'kenya':kenya,'india':india,'morocco':morocco,'canada':canada})

def search_results(request):
    if 'picture' in request.GET and request.GET["picture"]:
        search_term = request.GET.get("picture")
        searched_pictures = Picture.search_by_category(search_term)
        message = f"{search_term}"

        return render(request, 'all-pictures/search.html',{"message":message,"pictures": searched_pictures})

    else:
        message = "You haven't searched for any term"
        return render(request, 'all-pictures/search.html',{"message":message})


def ask_user(answers_dict):
    while True:
        question = input('Пользователь: ').lower()
        if question in answers_dict:
            print (f'Программа: {answers_dict[question]}')
        else:
            break

def main():
    questions_and_answers = {
        "как тебя зовут?": "Скайнет",
        "сколько тебе лет?": "25",
        "как дела?": "Хорошо",
        "что делаешь?": "Уничтожаю человечество",
    }
    ask_user(questions_and_answers)

if __name__ == "__main__":
    main()


import torch
from torch.utils.data import DataLoader, TensorDataset
import deepspeed
import numpy as np
import torch.nn as nn
import json
from model import MyModel
args={'batch_size':256, 'num_epochs':150}

def evaluate(model, criterion_class, criterion_seg, dataloader):
    model.eval()
    total_loss = 0.0
    correct_classifications = 0
    total_samples = 0

    with torch.no_grad():
        for inputs, labels_class, labels_seg in dataloader:
            # Move data to the appropriate device
            inputs, labels_class, labels_seg = inputs.to(model.local_rank), labels_class.to(model.local_rank), labels_seg.to(model.local_rank)

            # Forward pass
            outputs_class, outputs_seg = model(inputs)

            # Calculate loss
            loss_class = criterion_class(outputs_class, labels_class)
            loss_seg = criterion_seg(outputs_seg, labels_seg)
            loss = loss_class + loss_seg
            total_loss += loss.item()

            # Calculate accuracy for classification
            predicted = outputs_class > 0.5
            correct_classifications += (predicted == labels_class).sum().item()
            total_samples += labels_class.size(0)

    # Calculate average loss and accuracy
    avg_loss = total_loss / len(dataloader)
    accuracy = 100 * correct_classifications / total_samples

    return avg_loss, accuracy

X_train, Y_train, Z_train = np.load('data/train/X.npy'), np.load('data/train/Y.npy'), np.load('data/train/Z.npy')
X_val, Y_val, Z_val = np.load('data/val/X.npy'), np.load('data/val/Y.npy'), np.load('data/val/Z.npy')
# Convert numpy arrays to PyTorch tensors
X_train_tensor = torch.tensor(X_train, dtype=torch.float32)
Y_train_tensor = torch.tensor(Y_train, dtype=torch.float32).view(-1, 1)
Z_train_tensor = torch.tensor(Z_train, dtype=torch.float32)

X_val_tensor = torch.tensor(X_val, dtype=torch.float32)
Y_val_tensor = torch.tensor(Y_val, dtype=torch.float32).view(-1, 1)
Z_val_tensor = torch.tensor(Z_val, dtype=torch.float32)
# Create DataLoader
train_dataset = TensorDataset(X_train_tensor, Y_train_tensor, Z_train_tensor)
train_loader = DataLoader(train_dataset, batch_size=args['batch_size'], shuffle=True)
val_dataset = TensorDataset(X_val_tensor, Y_val_tensor, Z_val_tensor)
val_loader = DataLoader(val_dataset, batch_size=args['batch_size'], shuffle=False)
# Configure DeepSpeed
deepspeed.init_distributed()

# Load DeepSpeed configuration file manually
with open('deepspeed_config.json') as f:
    config_file = json.load(f)

# Instantiate the model, loss functions
model = MyModel()
model_engine, model, _, _ = deepspeed.initialize(model=model, config_params=config_file)
criterion_class = nn.BCELoss()
criterion_seg = nn.BCELoss()
best_accuracy = 0
# Train the model
for epoch in range(args['num_epochs']):
    running_loss = 0.0
    for inputs, labels_class, labels_seg in train_loader:
        inputs, labels_class, labels_seg = inputs.to(model_engine.local_rank), labels_class.to(model_engine.local_rank), labels_seg.to(model_engine.local_rank)
        model_engine.zero_grad()
        outputs_class, outputs_seg = model_engine(inputs)
        loss_class = criterion_class(outputs_class, labels_class)
        loss_seg = criterion_seg(outputs_seg, labels_seg)
        loss = loss_class + loss_seg
        model_engine.backward(loss)
        model_engine.step()
        running_loss += loss.item()
    print(f'Epoch {epoch+1}, Loss: {running_loss / len(train_loader)}')
    if epoch%10 == 0:
        _, accuracy = evaluate(model_engine, criterion_class, criterion_seg, val_loader)
        if accuracy > best_accuracy:
            print('save model')
            best_accuracy = accuracy
            model_engine.save_checkpoint('models/', 0)
        model_engine.train()

print('Finished Training')


def solution(numbers):
    a = max(numbers)
    cd = []
    curr = 0
    i = 0
    cnt = 0
    while curr <= (a+a):
        cd.append(curr)
        i += 1
        curr = pow(i,2)
    for k in cd:
        key_match = set()
        for num in numbers:
            key_match.add(num)
            if (k - num) in key_match:
                cnt += 1
    return cnt

numbers = [-20000, 20000, 0, 1, 4, 9, 16]
print(solution(numbers))

# 0 1 4 9 16 25
# 2 3 4 5


import logging
import os
import shutil

from aidegen import constant
from aidegen import templates
from aidegen.idea import iml
from aidegen.idea import xml_gen
from aidegen.lib import common_util
from aidegen.lib import config
from aidegen.lib import project_config
from aidegen.project import project_splitter

# FACET_SECTION is a part of iml, which defines the framework of the project.
_MODULE_SECTION = ('            <module fileurl="file:///$PROJECT_DIR$/%s.iml"'
                   ' filepath="$PROJECT_DIR$/%s.iml" />')
_SUB_MODULES_SECTION = ('            <module fileurl="file:///{IML}" '
                        'filepath="{IML}" />')
_MODULE_TOKEN = '@MODULES@'
_ENABLE_DEBUGGER_MODULE_TOKEN = '@ENABLE_DEBUGGER_MODULE@'
_IDEA_FOLDER = '.idea'
_MODULES_XML = 'modules.xml'
_COPYRIGHT_FOLDER = 'copyright'
_CODE_STYLE_FOLDER = 'codeStyles'
_APACHE_2_XML = 'Apache_2.xml'
_PROFILES_SETTINGS_XML = 'profiles_settings.xml'
_CODE_STYLE_CONFIG_XML = 'codeStyleConfig.xml'
_JSON_SCHEMAS_CONFIG_XML = 'jsonSchemas.xml'
_PROJECT_XML = 'Project.xml'
_COMPILE_XML = 'compiler.xml'
_MISC_XML = 'misc.xml'
_CONFIG_JSON = 'config.json'
_GIT_FOLDER_NAME = '.git'
# Support gitignore by symbolic link to aidegen/data/gitignore_template.
_GITIGNORE_FILE_NAME = '.gitignore'
_GITIGNORE_REL_PATH = 'tools/asuite/aidegen/data/gitignore_template'
_GITIGNORE_ABS_PATH = os.path.join(common_util.get_android_root_dir(),
                                   _GITIGNORE_REL_PATH)
# Support code style by symbolic link to aidegen/data/AndroidStyle_aidegen.xml.
_CODE_STYLE_REL_PATH = 'tools/asuite/aidegen/data/AndroidStyle_aidegen.xml'
_CODE_STYLE_SRC_PATH = os.path.join(common_util.get_android_root_dir(),
                                    _CODE_STYLE_REL_PATH)
_TEST_MAPPING_CONFIG_PATH = ('tools/tradefederation/core/src/com/android/'
                             'tradefed/util/testmapping/TEST_MAPPING.config'
                             '.json')

class ProjectFileGenerator:
    """Project file generator.

    Attributes:
        project_info: A instance of ProjectInfo.
    """

    def __init__(self, project_info):
        """ProjectFileGenerator initialize.

        Args:
            project_info: A instance of ProjectInfo.
        """
        self.project_info = project_info

    def generate_intellij_project_file(self, iml_path_list=None):
        """Generates IntelliJ project file.

        # TODO(b/155346505): Move this method to idea folder.

        Args:
            iml_path_list: An optional list of submodule's iml paths, the
                           default value is None.
        """
        if self.project_info.is_main_project:
            self._generate_modules_xml(iml_path_list)
            self._copy_constant_project_files()

    @classmethod
    def generate_ide_project_files(cls, projects):
        """Generate IDE project files by a list of ProjectInfo instances.

        It deals with the sources by ProjectSplitter to create iml files for
        each project and generate_intellij_project_file only creates
        the other project files under .idea/.

        Args:
            projects: A list of ProjectInfo instances.
        """
        # Initialization
        iml.IMLGenerator.USED_NAME_CACHE.clear()
        proj_splitter = project_splitter.ProjectSplitter(projects)
        proj_splitter.get_dependencies()
        proj_splitter.revise_source_folders()
        iml_paths = [proj_splitter.gen_framework_srcjars_iml()]
        proj_splitter.gen_projects_iml()
        iml_paths += [project.iml_path for project in projects]
        ProjectFileGenerator(
            projects[0]).generate_intellij_project_file(iml_paths)
        _merge_project_vcs_xmls(projects)

    def _copy_constant_project_files(self):
        """Copy project files to target path with error handling.

        This function would copy compiler.xml, misc.xml, codeStyles folder and
        copyright folder to target folder. Since these files aren't mandatory in
        IntelliJ, it only logs when an IOError occurred.
        """
        target_path = self.project_info.project_absolute_path
        idea_dir = os.path.join(target_path, _IDEA_FOLDER)
        copyright_dir = os.path.join(idea_dir, _COPYRIGHT_FOLDER)
        code_style_dir = os.path.join(idea_dir, _CODE_STYLE_FOLDER)
        common_util.file_generate(
            os.path.join(idea_dir, _COMPILE_XML), templates.XML_COMPILER)
        common_util.file_generate(
            os.path.join(idea_dir, _MISC_XML), templates.XML_MISC)
        common_util.file_generate(
            os.path.join(copyright_dir, _APACHE_2_XML), templates.XML_APACHE_2)
        common_util.file_generate(
            os.path.join(copyright_dir, _PROFILES_SETTINGS_XML),
            templates.XML_PROFILES_SETTINGS)
        common_util.file_generate(
            os.path.join(code_style_dir, _CODE_STYLE_CONFIG_XML),
            templates.XML_CODE_STYLE_CONFIG)
        code_style_target_path = os.path.join(code_style_dir, _PROJECT_XML)
        if not os.path.exists(code_style_target_path):
            try:
                shutil.copy2(_CODE_STYLE_SRC_PATH, code_style_target_path)
            except (OSError, SystemError) as err:
                logging.warning('%s can\'t copy the project files\n %s',
                                code_style_target_path, err)
        # Create .gitignore if it doesn't exist.
        _generate_git_ignore(target_path)
        # Create jsonSchemas.xml for TEST_MAPPING.
        _generate_test_mapping_schema(idea_dir)
        # Create config.json for Asuite plugin
        lunch_target = common_util.get_lunch_target()
        if lunch_target:
            common_util.file_generate(
                os.path.join(idea_dir, _CONFIG_JSON), lunch_target)

    def _generate_modules_xml(self, iml_path_list=None):
        """Generate modules.xml file.

        IntelliJ uses modules.xml to import which modules should be loaded to
        project. In multiple modules case, we will pass iml_path_list of
        submodules' dependencies and their iml file paths to add them into main
        module's module.xml file. The dependencies.iml file contains all shared
        dependencies source folders and jar files.

        Args:
            iml_path_list: A list of submodule iml paths.
        """
        module_path = self.project_info.project_absolute_path

        # b/121256503: Prevent duplicated iml names from breaking IDEA.
        module_name = iml.IMLGenerator.get_unique_iml_name(module_path)

        if iml_path_list is not None:
            module_list = [
                _MODULE_SECTION % (module_name, module_name),
                _MODULE_SECTION % (constant.KEY_DEPENDENCIES,
                                   constant.KEY_DEPENDENCIES)
            ]
            for iml_path in iml_path_list:
                module_list.append(_SUB_MODULES_SECTION.format(IML=iml_path))
        else:
            module_list = [
                _MODULE_SECTION % (module_name, module_name)
            ]
        module = '\n'.join(module_list)
        content = self._remove_debugger_token(templates.XML_MODULES)
        content = content.replace(_MODULE_TOKEN, module)
        target_path = os.path.join(module_path, _IDEA_FOLDER, _MODULES_XML)
        common_util.file_generate(target_path, content)

    def _remove_debugger_token(self, content):
        """Remove the token _ENABLE_DEBUGGER_MODULE_TOKEN.

        Remove the token _ENABLE_DEBUGGER_MODULE_TOKEN in 2 cases:
        1. Sub projects don't need to be filled in the enable debugger module
           so we remove the token here. For the main project, the enable
           debugger module will be appended if it exists at the time launching
           IDE.
        2. When there is no need to launch IDE.

        Args:
            content: The content of module.xml.

        Returns:
            String: The content of module.xml.
        """
        if (not project_config.ProjectConfig.get_instance().is_launch_ide or
                not self.project_info.is_main_project):
            content = content.replace(_ENABLE_DEBUGGER_MODULE_TOKEN, '')
        return content

def _merge_project_vcs_xmls(projects):
    """Merge sub projects' git paths into main project's vcs.xml.

    After all projects' vcs.xml are generated, collect the git path of each
    projects and write them into main project's vcs.xml.

    Args:
        projects: A list of ProjectInfo instances.
    """
    main_project_absolute_path = projects[0].project_absolute_path
    if main_project_absolute_path != common_util.get_android_root_dir():
        git_paths = [common_util.find_git_root(project.project_relative_path)
                     for project in projects if project.project_relative_path]
        xml_gen.gen_vcs_xml(main_project_absolute_path, git_paths)
    else:
        ignore_gits = sorted(_get_all_git_path(main_project_absolute_path))
        xml_gen.write_ignore_git_dirs_file(main_project_absolute_path,
                                           ignore_gits)

def _get_all_git_path(root_path):
    """Traverse all subdirectories to get all git folder's path.

    Args:
        root_path: A string of path to traverse.

    Yields:
        A git folder's path.
    """
    for dir_path, dir_names, _ in os.walk(root_path):
        if _GIT_FOLDER_NAME in dir_names:
            yield dir_path

def _generate_git_ignore(target_folder):
    """Generate .gitignore file.

    In target_folder, if there's no .gitignore file, uses symlink() to generate
    one to hide project content files from git.

    Args:
        target_folder: An absolute path string of target folder.
    """
    # TODO(b/133639849): Provide a common method to create symbolic link.
    # TODO(b/133641803): Move out aidegen artifacts from Android repo.
    try:
        gitignore_abs_path = os.path.join(target_folder, _GITIGNORE_FILE_NAME)
        rel_target = os.path.relpath(gitignore_abs_path, os.getcwd())
        rel_source = os.path.relpath(_GITIGNORE_ABS_PATH, target_folder)
        logging.debug('Relative target symlink path: %s.', rel_target)
        logging.debug('Relative ignore_template source path: %s.', rel_source)
        if not os.path.exists(gitignore_abs_path):
            os.symlink(rel_source, rel_target)
    except OSError as err:
        logging.error('Not support to run aidegen on Windows.\n %s', err)

def _generate_test_mapping_schema(idea_dir):
    """Create jsonSchemas.xml for TEST_MAPPING.

    Args:
        idea_dir: An absolute path string of target .idea folder.
    """
    config_path = os.path.join(
        common_util.get_android_root_dir(), _TEST_MAPPING_CONFIG_PATH)
    if os.path.isfile(config_path):
        common_util.file_generate(
            os.path.join(idea_dir, _JSON_SCHEMAS_CONFIG_XML),
            templates.TEST_MAPPING_SCHEMAS_XML.format(SCHEMA_PATH=config_path))
    else:
        logging.warning('Can\'t find TEST_MAPPING.config.json')

def _filter_out_source_paths(source_paths, module_relpaths):
    """Filter out the source paths which belong to the target module.

    The source_paths is a union set of all source paths of all target modules.
    For generating the dependencies.iml, we only need the source paths outside
    the target modules.

    Args:
        source_paths: A set contains the source folder paths.
        module_relpaths: A list, contains the relative paths of target modules
                         except the main module.

    Returns: A set of source paths.
    """
    return {x for x in source_paths if not any(
        {common_util.is_source_under_relative_path(x, y)
         for y in module_relpaths})}

def update_enable_debugger(module_path, enable_debugger_module_abspath=None):
    """Append the enable_debugger module's info in modules.xml file.

    Args:
        module_path: A string of the folder path contains IDE project content,
                     e.g., the folder contains the .idea folder.
        enable_debugger_module_abspath: A string of the im file path of enable
                                        debugger module.
    """
    replace_string = ''
    if enable_debugger_module_abspath:
        replace_string = _SUB_MODULES_SECTION.format(
            IML=enable_debugger_module_abspath)
    target_path = os.path.join(module_path, _IDEA_FOLDER, _MODULES_XML)
    content = common_util.read_file_content(target_path)
    content = content.replace(_ENABLE_DEBUGGER_MODULE_TOKEN, replace_string)
    common_util.file_generate(target_path, content)

def gen_enable_debugger_module(module_abspath, android_sdk_version):
    """Generate the enable_debugger module under AIDEGen config folder.

    Skip generating the enable_debugger module in IntelliJ once the attemption
    of getting the Android SDK version is failed.

    Args:
        module_abspath: the absolute path of the main project.
        android_sdk_version: A string, the Android SDK version in jdk.table.xml.
    """
    if not android_sdk_version:
        return
    with config.AidegenConfig() as aconf:
        if aconf.create_enable_debugger_module(android_sdk_version):
            update_enable_debugger(module_abspath,
                                   config.AidegenConfig.DEBUG_ENABLED_FILE_PATH)