import matplotlib.pyplot as plt

with open("day9.txt") as f:
    data = [[int(x) for x in y.strip()] for y in f]

fig, axe = plt.subplots(dpi=800)
axe.set_title("Basins visualised")
im = axe.imshow(data, cmap="hot_r")
axe.figure.colorbar(im, ax=axe)
fig.savefig("img/Day9_Visualization.png")
plt.close(fig)


import azlmbr.bus as bus
import azlmbr.editor as editor
import azlmbr.entity as entity
import azlmbr.math as math

# Open a level (any level should work)
editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'OpenLevelNoPrompt', 'Base')

def GetSetCompareTest(component, path, value):
    oldObj = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentProperty', component, path)

    if(oldObj.IsSuccess()):
        oldValue = oldObj.GetValue()

    oldValueCompared = editor.EditorComponentAPIBus(bus.Broadcast, 'CompareComponentProperty', component, path, oldValue)

    editor.EditorComponentAPIBus(bus.Broadcast, 'SetComponentProperty', component, path, value)

    newObj = editor.EditorComponentAPIBus(bus.Broadcast, 'GetComponentProperty', component, path)

    if(newObj.IsSuccess()):
        newValue = newObj.GetValue()

    newValueCompared = editor.EditorComponentAPIBus(bus.Broadcast, 'CompareComponentProperty', component, path, newValue)

    isOldNewValueSame = editor.EditorComponentAPIBus(bus.Broadcast, 'CompareComponentProperty', component, path, oldValue)

    if not(newValue == oldValue and oldValueCompared and newValueCompared and not isOldNewValueSame):
        print("GetSetCompareTest " + path + ": SUCCESS")
    else:
        print("GetSetCompareTest " + path + ": FAILURE")

def PteTest(pte, path, value):
    oldObj = pte.get_value(path)

    if(oldObj.IsSuccess()):
        oldValue = oldObj.GetValue()
    oldValueCompared = pte.compare_value(path, oldValue)

    pte.set_value(path, value)

    newObj = pte.get_value(path)

    if(newObj.IsSuccess()):
        newValue = newObj.GetValue()

    newValueCompared = pte.compare_value(path, newValue)

    isOldNewValueSame = pte.compare_value(path, oldValue)

    if not(newValue == oldValue and oldValueCompared and newValueCompared and not isOldNewValueSame):
        print("PteTest " + path + ": SUCCESS")
    else:
        print("PteTest " + path + ": FAILURE")

# Create new Entity
entityId = editor.ToolsApplicationRequestBus(bus.Broadcast, 'CreateNewEntity', entity.EntityId())

if (entityId):
    print("New entity with no parent created")

# Get Component Type for Environment Probe
typeIdsList = editor.EditorComponentAPIBus(bus.Broadcast, 'FindComponentTypeIdsByEntityType', ["Environment Probe"], entity.EntityType().Game)

componentOutcome = editor.EditorComponentAPIBus(bus.Broadcast, 'AddComponentsOfType', entityId, typeIdsList)

if (componentOutcome.IsSuccess()):
    print("Environment Probe component added to entity")

components = componentOutcome.GetValue()
component = components[0]

hasComponent = editor.EditorComponentAPIBus(bus.Broadcast, 'HasComponentOfType', entityId, typeIdsList[0])

if(hasComponent):
    print("Entity has an Environment Probe component")

# Test BuildComponentPropertyList
paths = editor.EditorComponentAPIBus(bus.Broadcast, 'BuildComponentPropertyList', component)

if(len(paths) > 0):
    print("get_paths_list works")

# Tests for GetComponentProperty/SetComponentProperty

GetSetCompareTest(component, "Settings|General Settings|Visible", False)
GetSetCompareTest(component, "Settings|Animation|Style", 2.0)
GetSetCompareTest(component, "Settings|Environment Probe Settings|Box height", 42)

color = math.Color()
color.r = 0.4
color.g = 0.5
color.b = 0.6

GetSetCompareTest(component, "Settings|General Settings|Color", color)

vec3 = math.Vector3()
vec3.x = 1.0
vec3.y = 2.0
vec3.z = 3.0

GetSetCompareTest(component, "Settings|Environment Probe Settings|Area dimensions", vec3)

# Tests for BuildComponentPropertyTreeEditor
pteObj = editor.EditorComponentAPIBus(bus.Broadcast, 'BuildComponentPropertyTreeEditor', component)

if(pteObj.IsSuccess()):
    print("")
    pte = pteObj.GetValue()

PteTest(pte, "Settings|General Settings|Visible", True)
PteTest(pte, "Settings|Animation|Style", 4)
PteTest(pte, "Settings|Environment Probe Settings|Box height", 48.0)

color = math.Color()
color.r = 0.9
color.g = 0.1
color.b = 0.3

PteTest(pte, "Settings|General Settings|Color", color)

vec3 = math.Vector3()
vec3.x = 7.0
vec3.y = 4.0
vec3.z = 1.0

PteTest(pte, "Settings|Environment Probe Settings|Area dimensions", vec3)

editor.EditorToolsApplicationRequestBus(bus.Broadcast, 'ExitNoPrompt')


#!/usr/bin/env python3

from __future__ import annotations

"""
Tool to help automate changes needed in commits during and after releases
"""

import argparse
import logging
import sys
from datetime import datetime
from pathlib import Path
from subprocess import PIPE, run
from typing import List

LOG = logging.getLogger(__name__)
NEW_VERSION_CHANGELOG_TEMPLATE = """\
## Unreleased

### Highlights

<!-- Include any especially major or disruptive changes here -->

### Stable style

<!-- Changes that affect Black's stable style -->

### Preview style

<!-- Changes that affect Black's preview style -->

### Configuration

<!-- Changes to how Black can be configured -->

### Packaging

<!-- Changes to how Black is packaged, such as dependency requirements -->

### Parser

<!-- Changes to the parser or to version autodetection -->

### Performance

<!-- Changes that improve Black's performance. -->

### Output

<!-- Changes to Black's terminal output and error messages -->

### _Blackd_

<!-- Changes to blackd -->

### Integrations

<!-- For example, Docker, GitHub Actions, pre-commit, editors -->

### Documentation

<!-- Major changes to documentation and policies. Small docs changes
     don't need a changelog entry. -->
"""

class NoGitTagsError(Exception): ...  # noqa: E701,E761

# TODO: Do better with alpha + beta releases
# Maybe we vendor packaging library
def get_git_tags(versions_only: bool = True) -> List[str]:
    """Pull out all tags or calvers only"""
    cp = run(["git", "tag"], stdout=PIPE, stderr=PIPE, check=True, encoding="utf8")
    if not cp.stdout:
        LOG.error(f"Returned no git tags stdout: {cp.stderr}")
        raise NoGitTagsError
    git_tags = cp.stdout.splitlines()
    if versions_only:
        return [t for t in git_tags if t[0].isdigit()]
    return git_tags

# TODO: Support sorting alhpa/beta releases correctly
def tuple_calver(calver: str) -> tuple[int, ...]:  # mypy can't notice maxsplit below
    """Convert a calver string into a tuple of ints for sorting"""
    try:
        return tuple(map(int, calver.split(".", maxsplit=2)))
    except ValueError:
        return (0, 0, 0)

class SourceFiles:
    def __init__(self, black_repo_dir: Path):
        # File path fun all pathlib to be platform agnostic
        self.black_repo_path = black_repo_dir
        self.changes_path = self.black_repo_path / "CHANGES.md"
        self.docs_path = self.black_repo_path / "docs"
        self.version_doc_paths = (
            self.docs_path / "integrations" / "source_version_control.md",
            self.docs_path / "usage_and_configuration" / "the_basics.md",
        )
        self.current_version = self.get_current_version()
        self.next_version = self.get_next_version()

    def __str__(self) -> str:
        return f"""\
> SourceFiles ENV:
  Repo path: {self.black_repo_path}
  CHANGES.md path: {self.changes_path}
  docs path: {self.docs_path}
  Current version: {self.current_version}
  Next version: {self.next_version}
"""

    def add_template_to_changes(self) -> int:
        """Add the template to CHANGES.md if it does not exist"""
        LOG.info(f"Adding template to {self.changes_path}")

        with self.changes_path.open("r") as cfp:
            changes_string = cfp.read()

        if "## Unreleased" in changes_string:
            LOG.error(f"{self.changes_path} already has unreleased template")
            return 1

        templated_changes_string = changes_string.replace(
            "# Change Log\n",
            f"# Change Log\n\n{NEW_VERSION_CHANGELOG_TEMPLATE}",
        )

        with self.changes_path.open("w") as cfp:
            cfp.write(templated_changes_string)

        LOG.info(f"Added template to {self.changes_path}")
        return 0

    def cleanup_changes_template_for_release(self) -> None:
        LOG.info(f"Cleaning up {self.changes_path}")

        with self.changes_path.open("r") as cfp:
            changes_string = cfp.read()

        # Change Unreleased to next version
        versioned_changes = changes_string.replace(
            "## Unreleased", f"## {self.next_version}"
        )

        # Remove all comments (subheadings are harder - Human required still)
        no_comments_changes = []
        for line in versioned_changes.splitlines():
            if line.startswith("<!--") or line.endswith("-->"):
                continue
            no_comments_changes.append(line)

        with self.changes_path.open("w") as cfp:
            cfp.write("\n".join(no_comments_changes) + "\n")

        LOG.debug(f"Finished Cleaning up {self.changes_path}")

    def get_current_version(self) -> str:
        """Get the latest git (version) tag as latest version"""
        return sorted(get_git_tags(), key=lambda k: tuple_calver(k))[-1]

    def get_next_version(self) -> str:
        """Workout the year and month + version number we need to move to"""
        base_calver = datetime.today().strftime("%y.%m")
        calver_parts = base_calver.split(".")
        base_calver = f"{calver_parts[0]}.{int(calver_parts[1])}"  # Remove leading 0
        git_tags = get_git_tags()
        same_month_releases = [t for t in git_tags if t.startswith(base_calver)]
        if len(same_month_releases) < 1:
            return f"{base_calver}.0"
        same_month_version = same_month_releases[-1].split(".", 2)[-1]
        return f"{base_calver}.{int(same_month_version) + 1}"

    def update_repo_for_release(self) -> int:
        """Update CHANGES.md + doc files ready for release"""
        self.cleanup_changes_template_for_release()
        self.update_version_in_docs()
        return 0  # return 0 if no exceptions hit

    def update_version_in_docs(self) -> None:
        for doc_path in self.version_doc_paths:
            LOG.info(f"Updating black version to {self.next_version} in {doc_path}")

            with doc_path.open("r") as dfp:
                doc_string = dfp.read()

            next_version_doc = doc_string.replace(
                self.current_version, self.next_version
            )

            with doc_path.open("w") as dfp:
                dfp.write(next_version_doc)

            LOG.debug(
                f"Finished updating black version to {self.next_version} in {doc_path}"
            )

def _handle_debug(debug: bool) -> None:
    """Turn on debugging if asked otherwise INFO default"""
    log_level = logging.DEBUG if debug else logging.INFO
    logging.basicConfig(
        format="[%(asctime)s] %(levelname)s: %(message)s (%(filename)s:%(lineno)d)",
        level=log_level,
    )

def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-a",
        "--add-changes-template",
        action="store_true",
        help="Add the Unreleased template to CHANGES.md",
    )
    parser.add_argument(
        "-d", "--debug", action="store_true", help="Verbose debug output"
    )
    args = parser.parse_args()
    _handle_debug(args.debug)
    return args

def main() -> int:
    args = parse_args()

    # Need parent.parent cause script is in scripts/ directory
    sf = SourceFiles(Path(__file__).parent.parent)

    if args.add_changes_template:
        return sf.add_template_to_changes()

    LOG.info(f"Current version detected to be {sf.current_version}")
    LOG.info(f"Next version will be {sf.next_version}")
    return sf.update_repo_for_release()

if __name__ == "__main__":  # pragma: no cover
    sys.exit(main())


from os import write

ruta = open("/content/drive/MyDrive/34635/miMascota.txt","w")
option = int(input("""
1. Agregar Mascota
2. Salir
"""))
while(option!=2):
  nombre = str(input("cual es el nombre de la mascota?\n"))
  edad = str(input("Cual es la edad del animal?\n"))
  tipo = str(input("Cual es el tipo de mascota?\n"))
  mascota = {
      "nombre":nombre,
      "edad":edad,
      "tipo":tipo,
  }
  ruta.write(f'{mascota["nombre"]}, {mascota["edad"]}, {mascota["tipo"]}\n')
  option = int(input("""
    1. Agregar Mascota
    2. Salir
  """))
else:
  ruta.close()


import json
import requests
from time import sleep
from datetime import datetime
from kafka import KafkaProducer

# INCOMPLETE FILE - PENDING THE DECISION TO USE WEATHER DATA IN THE PROJECT

def api_to_kafka(endpoint: str):
    """Executes the API request to Open Weather"""
    response = requests.get(endpoint)
    weather_json = json.dumps(response.json(), indent=3)
    weather_dict = (json.loads(weather_json))
    parse_data(weather_dict)

def parse_data(river_dict: dict):
    """Parses out the necessary information to produce events to Kafka"""
    pass

if __name__ == "__main__":

    gallatin = {'latitude': 45.4973, 'longitude': -111.2707083}
    jefferson = {'latitude': 45.6132833, 'longitude': -112.3293972}
    lowerMadison = {'latitude': 45.49023056, 'longitude': -111.6345056}
    upperMadison = {'latitude': 44.88865556, 'longitude': -111.580886}

    weather_endpoint = "https://api.openweathermap.org/data/2.5/onecall?lat=" + str(gallatin["latitude"]) + "&lon=" + str(gallatin["longitude"]) + "&exclude=daily&appid=8d09ba3f05951c0a3af24e56b5b449d5"
    response = requests.get(weather_endpoint)
    weather_json = json.dumps(response.json(), indent=3)
    print(weather_json)
    weather_dict = (json.loads(weather_json))


"""
Given a string s containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid.

An input string is valid if:

Open brackets must be closed by the same type of brackets.
Open brackets must be closed in the correct order.

Example 1:

Input: s = "()"
Output: true
Example 2:

Input: s = "()[]{}"
Output: true
Example 3:

Input: s = "(]"
Output: false
Example 4:

Input: s = "([)]"
Output: false
Example 5:

Input: s = "{[]}"
Output: true

"""

class Solution:
    def isValid(self, s: str) -> bool:
        slen = len(s)
        an =[]

        if (len(s) == 0):
            return True
        #입력 받은 str의 개수가 홀수 인 경우
        elif (len(s) % 2 !=0):
            return False

        for i in range(0,slen):
            #오픈괄호 리스트에 추가
            if (s[i] in ['(','{','['] ):
                an.append(s[i])
            else:
                #print("an > ", an, i, len(an))
                if (len(an) == 0):
                    return False
                #문자와 an에서 마지막의 문자가 같지 않은 경우
                elif (s[i] == ')' and an.pop() !='(') :
                    return False
                elif (s[i] == '}' and an.pop() !='{') :
                    return False
                elif (s[i] == ']' and an.pop() !='[') :
                    return False
        #for문 끝나고 an안에 개수가 0이 아닌 경우
        if (len(an) != 0):
            return False
        return True

s = Solution()
print(s.isValid("()"))
print(s.isValid("()[]{}"))
print(s.isValid("(]"))
print(s.isValid("(("))
print(s.isValid("([)]"))
print(s.isValid("{[]}"))


"""

.. module:: driver
   :synopsis: manages the HTPolyNet application, provides the command-line interface entry point

.. moduleauthor: Cameron F. Abrams, <cfa22@drexel.edu>

"""
import logging
import os
import argparse as ap
import textwrap
import shutil
import glob
from .banner import banner, banner_message
from .runtime import Runtime,logrotate
from .command import Command
import HTPolyNet.projectfilesystem as pfs
import HTPolyNet.software as software
from .plot import plots
from .stringthings import my_logger
from .inputcheck import input_check
from .postsim import postsim
from .analyze import analyze

logger=logging.getLogger(__name__)

def info(args):
    """info handles the info subcommmand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    print('This is some information on your installed version of HTPolyNet')
    l=pfs.lib_setup()
    software.sw_setup()
    print(l.info())
    print(software.to_string())
    possibles=l.get_example_names()
    print('Available examples using htpolynet fetch-example')
    for p in possibles:
        if p[0].isdecimal():
            print(f'   {p}')

def run(args):
    """run handles the run subcommand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    logrotate(args.diag)
    ''' set up logger with all debug+-level messages going to the diagnostic log file and info to console '''
    loglevel_numeric=getattr(logging, args.loglevel.upper())
    logging.basicConfig(filename=args.diag,filemode='w',format='%(asctime)s %(name)s.%(funcName)s %(levelname)s> %(message)s',level=loglevel_numeric)
    console=logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter=logging.Formatter('%(levelname)s> %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    if not args.no_banner: banner(logger.info)
    my_logger('HTPolyNet runtime begins',logger.info)
    userlib=args.lib if os.path.exists(args.lib) else None
    software.sw_setup()
    pfs.pfs_setup(root=os.getcwd(),topdirs=['molecules','systems','plots'],verbose=True,projdir=args.proj,reProject=args.restart,userlibrary=userlib)
    a=Runtime(cfgfile=args.config,restart=args.restart)
    a.do_workflow(force_checkin=args.force_checkin,force_parameterization=args.force_parameterization)
    my_logger('HTPolyNet runtime ends',logger.info)

def parameterize(args):
    """parameterize handles the parameterize subcommand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    logrotate(args.diag)
    ''' set up logger with all debug+-level messages going to the diagnostic log file and info to console '''
    loglevel_numeric=getattr(logging, args.loglevel.upper())
    logging.basicConfig(filename=args.diag,filemode='w',format='%(asctime)s %(name)s.%(funcName)s %(levelname)s> %(message)s',level=loglevel_numeric)
    console=logging.StreamHandler()
    console.setLevel(logging.INFO)
    formatter=logging.Formatter('%(levelname)s> %(message)s')
    console.setFormatter(formatter)
    logging.getLogger('').addHandler(console)
    if not args.no_banner: banner(logger.info)
    my_logger('HTPolyNet parameterization begins',logger.info)
    userlib=args.lib
    if not os.path.exists(args.lib):
        userlib=None
    software.sw_setup()
    pfs.pfs_setup(root=os.getcwd(),topdirs=['molecules','systems','plots'],verbose=True,projdir=args.proj,reProject=args.restart,userlibrary=userlib)
    a=Runtime(cfgfile=args.config,restart=args.restart)
    a.generate_molecules(force_checkin=args.force_checkin,force_parameterization=args.force_parameterization)
    my_logger('HTPolynet parameterization ends',logger.info)

def fetch_example(args):
    """fetch_example handles the fetch-example subcommand

    :param args: parsed arguments
    :type args: argparse.Namespace
    """
    l=pfs.system()
    nm=args.n
    kp=args.k
    possibles=l.get_example_names()
    if nm.isdecimal():
        fullname=[x for x in possibles if x.startswith(nm)][0]
    else:
        if nm=='all':
            for fullname in possibles:
                shutil.copy(os.path.join(l.get_example_depot_location(),f'{fullname}.tgz'),'.')
                os.system(f'tar zxf {fullname}.tgz')
                if not kp:
                    os.remove(f'{fullname}.tgz')
            return
        fullname=nm
    fullpath=os.path.join(l.get_example_depot_location(),f'{fullname}.tgz')
    assert os.path.exists(fullpath),f'{fullpath}: not found.'
    shutil.copy(fullpath,'.')
    os.system(f'tar zxf {fullname}.tgz')
    if not kp:
        os.remove(f'{fullname}.tgz')

def pack_example(args):
    l=pfs.system()
    requires=['README.md','run.sh','lib/molecules']
    for r in requires:
        assert os.path.exists(r),f'{r}: not found.'
    ls=glob.glob('*.yaml')+glob.glob('*.yml')
    assert len(ls)>0,f'No yaml config file found.'
    bn=os.path.basename(os.getcwd())
    inspectname=bn.split('-')
    if len(inspectname)>0:
        firstfield=inspectname[0]
        if firstfield.isdigit():
            existing_n=int(firstfield)
            use_n=False
        else:
            use_n=True
    n=args.n
    existing_examples=l.get_example_names()
    numbers=[int(x.split('-')[0]) for x in existing_examples]
    assert not n in numbers,f'Choose an index other than {n}; an example with this index already exists.'
    if not use_n:
        assert not existing_n in numbers,f'Choose an index other than {n}; an example with this index already exists. To do this, rename the directory.'
    if n==-1:
        n=str(max(numbers)+1)
    if use_n:
        newname=f'{n}-{os.path.basename(os.getcwd())}'
    else:
        newname=f'{os.path.basename(os.getcwd())}'
    depot_location=l.get_example_depot_location()
    os.chdir('..')
    c=Command(f'tar --exclude="*/*/*/*/*" -zvcf {depot_location}/{newname}.tgz {bn}/README.md {bn}/run.sh {bn}/*.yaml {bn}/lib/molecules/')
    o,e=c.run()
    print(o)
    print(e)

def cli():
    """cli Command-line interface
    """
    l=pfs.lib_setup()
    example_names=l.get_example_names()
    example_ids=[x.split('-')[0] for x in example_names]
    # declare subcommands and their handlers
    commands={}
    commands['run']=run
    commands['parameterize']=parameterize
    commands['info']=info
    commands['plots']=plots
    commands['fetch-example']=fetch_example
    commands['input-check']=input_check
    commands['postsim']=postsim
    commands['analyze']=analyze
    commands['pack-example']=pack_example
    # declare subcommand helpstrings
    helps={}
    helps['run']='build a system using instructions in the config file and any required molecular structure inputs'
    helps['parameterize']='parameterize monomers and oligomer templates using instructions in the config file'
    helps['info']='print some information to the console'
    helps['plots']='generate some plots that summarize aspects of the current completed build'
    helps['fetch-example']='fetch and unpack example(s) from HTPolyNet.HTPolyNet.resources: '+', '.join([f'"{x}"' for x in l.get_example_names()])
    helps['pack-example']='pack current directory as a tarball to the resources/example_depot'
    helps['input-check']='reports number of atoms that would be in initial system based on config'
    helps['postsim']='perform specified postcure MD simulations on final results in one or more project directory'
    helps['analyze']='perform \'gmx <command>\' style analyses specified in the cfg file'
    parser=ap.ArgumentParser(description=textwrap.dedent(banner_message),formatter_class=ap.RawDescriptionHelpFormatter)
    # Subparsers, one per subcommand
    subparsers=parser.add_subparsers()
    subparsers.required=True
    command_parsers={}
    for k in commands:
        command_parsers[k]=subparsers.add_parser(k,help=helps[k])
        command_parsers[k].set_defaults(func=commands[k])
    ######## run ########
    command_parsers['run'].add_argument('config',type=str,default=None,help='input configuration file in YAML format')
    command_parsers['run'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['run'].add_argument('-proj',type=str,default='next',help='project directory; "next" (default) generates next directory.  Anything other than "next": if it exists, "-restart" must be included as a parameter; if not, it is created as a new project')
    command_parsers['run'].add_argument('-diag',type=str,default='htpolynet_runtime_diagnostics.log',help='diagnostic log file')
    command_parsers['run'].add_argument('-restart',default=False,action='store_true',help='restart in latest proj dir')
    command_parsers['run'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['run'].add_argument('--force-parameterization',default=False,action='store_true',help='force GAFF parameterization of any input mol2 structures')
    command_parsers['run'].add_argument('--force-checkin',default=False,action='store_true',help='force check-in of any generated parameter files to the system library')
    command_parsers['run'].add_argument('--loglevel',type=str,default='debug',help='Log level for messages written to diagnostic log (debug|info)')
    ######## parameterize ########
    command_parsers['parameterize'].add_argument('config',type=str,default=None,help='input configuration file in YAML format')
    command_parsers['parameterize'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['parameterize'].add_argument('-proj',type=str,default='next',help='project directory; "next" (default) generates next directory.  Anything other than "next": if it exists, "-restart" must be included as a parameter; if not, it is created as a new project')
    command_parsers['parameterize'].add_argument('-diag',type=str,default='htpolynet_runtime_diagnostics.log',help='diagnostic log file')
    command_parsers['parameterize'].add_argument('-restart',default=False,action='store_true',help='restart in latest proj dir')
    command_parsers['parameterize'].add_argument('--force-parameterization',default=False,action='store_true',help='force GAFF parameterization of any input mol2 structures')
    command_parsers['parameterize'].add_argument('--force-checkin',default=False,action='store_true',help='force check-in of any generated parameter files to the system library')
    command_parsers['parameterize'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['parameterize'].add_argument('--loglevel',type=str,default='debug',help='Log level for messages written to diagnostic log (debug|info)')
    ######## plots ########
    command_parsers['plots'].add_argument('source',type=str,choices=['diag','build','post'],default='build',help='source of data to plot')
    command_parsers['plots'].add_argument('--diags',type=str,default=[],nargs='+',help='names of diagnostic log files (1 or more)')
    command_parsers['plots'].add_argument('--proj',nargs='+',type=str,default=[],help='name of project director[y/ies]')
    command_parsers['plots'].add_argument('--cfg',type=str,nargs='+',default=[],help='name input config files')
    command_parsers['plots'].add_argument('--buildplot',type=str,nargs='+',default=['t'],choices=['t','g','n','c'],help='type of build plot to generate: t: traces (select using --traces); g: 2-D graph representations iteration by iteration; n: homo-N between crosslinks; c: cluster-size distributions')
    command_parsers['plots'].add_argument('--traces',type=str,nargs='+',default=['t','d','p'],choices=['t','d','p'],help='type of traces to plot from build: t: temperature; d: density; p: potential energy')
    command_parsers['plots'].add_argument('--n_points',type=int,nargs=2,default=[10,20],help='number of [cold-side,hot-side] data points in the Tg analysis to fit lines to')

    command_parsers['plots'].add_argument('--plotfile',type=str,default='',help='name of plot file to generate')
    command_parsers['plots'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['plots'].add_argument('--loglevel',type=str,default='info',help='Log level for messages written to diagnostic log (debug|info)')
    ######## fetch-example ########
    command_parsers['fetch-example'].add_argument('-n',type=str,choices=example_ids+['all'],help='number of example tarball to unpack from '+', '.join(example_names))
    command_parsers['fetch-example'].add_argument('-k',default=False,action='store_true',help='keep tarballs')
    ######## pack-example
    command_parsers['pack-example'].add_argument('-n',type=int,default=-1,help='desired index (integer) for this example')
    ######## input-check ########
    command_parsers['input-check'].add_argument('config',type=str,default=None,help='input configuration file in YAML format')
    command_parsers['input-check'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    ######## postsim ########
    command_parsers['postsim'].add_argument('-proj',type=str,default='',nargs='+',help='name of project directory')
    command_parsers['postsim'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['postsim'].add_argument('-ocfg',type=str,default='',help='original HTPolyNet config file used to generate project(s)')
    command_parsers['postsim'].add_argument('-cfg',type=str,default='',help='config file for specifying the MD simulations to perform')
    command_parsers['postsim'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['postsim'].add_argument('--loglevel',type=str,default='info',help='Log level for messages written to diagnostic log (debug|info)')

    ######## analyze ########
    command_parsers['analyze'].add_argument('-proj',type=str,default='',nargs='+',help='name of project directory')
    command_parsers['analyze'].add_argument('-lib',type=str,default='lib',help='local user library of molecular structures and parameterizations')
    command_parsers['analyze'].add_argument('-ocfg',type=str,default='',help='original HTPolyNet config file used to generate project(s)')
    command_parsers['analyze'].add_argument('-cfg',type=str,default='',help='config file for specifying the analyses to perform')
    command_parsers['analyze'].add_argument('--no-banner',default=False,action='store_true',help='turn off the banner')
    command_parsers['analyze'].add_argument('--loglevel',type=str,default='info',help='Log level for messages written to diagnostic log (debug|info)')
    args=parser.parse_args()
    args.func(args)


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# https://leetcode.com/problems/super-ugly-number/description/

"""
Write a program to find the nth super ugly number.

Super ugly numbers are positive numbers whose all prime factors are
in the given prime list primes of size k.

For example, [1, 2, 4, 7, 8, 13, 14, 16, 19, 26, 28, 32] is the sequence
of the first 12 super ugly numbers given primes = [2, 7, 13, 19] of size 4.

Note:
(1) 1 is a super ugly number for any given primes.
(2) The given numbers in primes are in ascending order.
(3) 0 < k ≤ 100, 0 < n ≤ 106, 0 < primes[i] < 1000.
(4) The nth super ugly number is guaranteed to fit in a 32-bit signed integer.
"""
import heapq

class Solution(object):

    def nthSuperUglyNumber(self, n, primes):
        """
        :type n: int
        :type primes: List[int]
        :rtype: int
        """
        uglies = [1]

        def gen(prime):
            # because it's generator, uglies can be modified during loop.
            for ugly in uglies:
                yield ugly * prime

        generators = map(gen, primes)  # generators

        print("tmp: {0}".format(generators))

        # https://docs.python.org/2/library/heapq.html
        merged = heapq.merge(*generators)

        while len(uglies) < n:
            ugly = next(merged)

            if ugly != uglies[-1]:
                uglies.append(ugly)

        return uglies[-1]

def build_primes():
    prime = [2, 7, 13, 19]
    return prime

if __name__ == "__main__":
    primes = build_primes()
    print("primes: {0}".format(primes))

    s = Solution()
    result = s.nthSuperUglyNumber(3, primes)
    print(result)


import numpy as np
from numpy import cos as cos
from numpy import sin as sin
from numpy import pi as PI

import config

#Returns the intersections of 2 lists of intervals
#Not used for MGA-1DSM problem
def inter_intersec(l1,l2):
    ll1 = len(l1)
    ll2 = len(l2)
    i=0
    j=0
    l1v = False
    l2v = False
    l_inter = []
    while i<ll1 and j<ll2:
        if l1[i]<l2[j]:
            l1v = not l1v
            if l2v:
                l_inter += [l1[i]]
            i+=1
        elif l1[i]==l2[j]:
            l1v = not l1v
            l2v = not l2v
            if l1v == l2v:
              l_inter += [l1[i]]
            i+=1
            j+=1
        else:
            l2v = not l2v
            if l1v:
                l_inter += [l2[j]]
            j+=1
    return

#Returns the closest approach of two bodies
#Not used for MGA-1DSM problem
def findClosestApp(nt, body_c, body_p, t_lim, D, UT, tarplan, parplan, closest_app):
    resu = [0,'']
    resumod = False
    approach = [0,0,'']
    appmod = False
    R = 1000
    dR = 1
    sq_dist_cp = float('inf')

    it=0
    while abs(R/dR)>10:
        it+=1
        sq_dist_cp, R, dR = NewtRaphTimeClos(body_c, body_p, body_c.SOI, nt)
        nt = nt - R/dR
        if it > config.n_it:
            print('more than '+str(config.n_it)+' iterations for time of closest approach')
        break
#    print(it)
#    print("dist : "+str(np.sqrt(sq_dist_cp)))

    if sq_dist_cp<D*D and nt>UT:
        nt1 = nt - body_p.T*D/(2*PI*body_p.SMA)

        H = 1000
        R = 1

        it=0
        while abs(H/(2*R))>1:
            it+=1
            H, R = NewtRaphTimeInter(body_c, body_p, body_c.SOI, nt1, D)
            nt1 = nt1 - H/(2*R)
            if it > config.n_it:
                print('more than '+str(config.n_it)+' iterations for SOI entrance')
            break
        if nt1<t_lim and nt1>UT:
            resu = [nt1, body_p]
            resumod = True
            t_lim = nt1
            if tarplan or parplan:
                approach = [3,0,body_p]
                appmod = True
        else:
            [rc,drc] = body_c.GetCartFromOrb(t_lim)
            [rp,drp] = body_p.GetCartFromOrb(t_lim)
            dist_cp = np.sqrt(np.dot(rc-rp,rc-rp))
            if dist_cp<closest_app:
                approach = [3,dist_cp, body_p]
                appmod = True
    elif nt>UT and (tarplan or parplan) and nt<t_lim:
        dist_cp = np.sqrt(sq_dist_cp)
        if dist_cp<closest_app:
            approach = [3,dist_cp, body_p]
            appmod = True
    return (resumod, resu, appmod, approach)

#Newton-Raphson iteration for the closest approach between two bodies
#Not used for MGA-1DSM problem
def NewtRaphTimeClos(body_c, body_p, SOI, nt):
    [rc,drc] = body_c.GetCartFromOrb(nt)
    nrc = np.linalg.norm(rc)
    ddrc = -SOI.mu*rc/nrc**3
    [rp,drp] = body_p.GetCartFromOrb(nt)
    nrp = np.linalg.norm(rp)
    ddrp = -SOI.mu*rp/nrp**3
    sq_dist_cp = np.dot(rc-rp,rc-rp)
    R = np.dot(drc-drp,rc-rp)
    dR = np.dot(ddrc-ddrp,rc-rp) + np.dot(drc-drp,drc-drp)

    return (sq_dist_cp, R, dR)

#Newton-Raphson iteration for SOI entrance
#Not used for MGA-1DSM problem
def NewtRaphTimeInter(body_c, body_p, SOI, nt, D):
    [rc,drc] = body_c.GetCartFromOrb(nt)
    [rp,drp] = body_p.GetCartFromOrb(nt)

    H = np.dot(rc-rp,rc-rp) - D*D
    R = np.dot(drc-drp,rc-rp)

    return (H, R)

#Returns the final position of a craft from a list of burn times and delta-V vectors
#Not used for MGA-1DSM problem
def MoveToEnd(body_c, startt, endt, burns, target, trace):
    pos_vel_dict = {}
    orb_dict = {}
    if trace:
        pos_vel_dict[body_c.SOI.name] = [[startt,body_c.GetCartFromOrb(startt)]]
        orb_dict[body_c.SOI.name] = [[body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE,0,2*PI]]

    mindist_w_targ = [0,float('inf')]
    mindist_w_targ_par = [0,float('inf')]
    r_burns = [burn for burn in burns if burn[0]>startt and burn[0]<endt]
    s_burns = sorted(r_burns)
    DV_tot = 0
    for burn in burns:
        DV_tot += np.linalg.norm(burn[1])
    i_b = 0
    next_op = [endt,'','NA']
    currt = startt
    fin = False
    inter_score = []
    it = 0
    while not fin:
        it+=1
        next_op = [endt,'end','NA']
        if i_b<len(s_burns):
            if s_burns[i_b][0]<next_op[0]:
                next_op = [s_burns[i_b][0],'burn',s_burns[i_b][1]]
        ttoleave = TimeToLeaveSOI(body_c, currt) + currt
        if ttoleave<next_op[0]:
            next_op = [ttoleave,'leaveSOI','NA']
        ttocrash = TimeToCrash(body_c, currt) + currt
        if ttocrash<next_op[0]:
            next_op = [ttocrash,'crash','NA']
        [ttoenter, nextSOI, approach] = TimeToEnterSOI(body_c, currt, next_op[0], target[0])
        if approach[2] != 'NA':     #Attempt to reward approach of the target/the parent body of the target
            if approach[2].name == target[0].name:
                if approach[0]>mindist_w_targ[0]:
                    mindist_w_targ = [approach[0], approach[1]]
                elif approach[1]<mindist_w_targ[1] and approach[0]==mindist_w_targ[0]:
                    mindist_w_targ[1] = approach[1]
            elif approach[2].name == target[0].SOI.name:
                if approach[0]>mindist_w_targ_par[0]:
                    mindist_w_targ_par = [approach[0], approach[1]]
                elif approach[1]<mindist_w_targ_par[1] and approach[0]==mindist_w_targ_par[0]:
                    mindist_w_targ_par[1] = approach[1]
        if ttoenter<next_op[0]:
                next_op = [ttoenter,'enterSOI',nextSOI]

        if trace:
            if body_c.SOI.name in pos_vel_dict:
                pos_vel_dict[body_c.SOI.name] += [[currt,body_c.GetCartFromOrb(currt)]]
                pos_vel_dict[body_c.SOI.name] += [[next_op[0],body_c.GetCartFromOrb(next_op[0])]]
            else:
                pos_vel_dict[body_c.SOI.name] = [[currt,body_c.GetCartFromOrb(currt)]]
                pos_vel_dict[body_c.SOI.name] += [[next_op[0],body_c.GetCartFromOrb(next_op[0])]]
            if body_c.SOI.name in orb_dict:
                orb_dict[body_c.SOI.name] += [[body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE,0,2*PI]]
            else:
                orb_dict[body_c.SOI.name] = [[body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE,0,2*PI]]

        if next_op[1]=='enterSOI':
            EnterSOI(body_c,next_op[2],next_op[0])
            if next_op[2].SOI.code == 0:
                per = (1-body_c.ECC)*body_c.SMA
                inter_score += [[next_op[2].alt_lim,per]]
            if next_op[2].name == target[0].name:
                end_pos = body_c.GetCartFromOrb(next_op[0])
                end_orb = [body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE, body_c.EPH, body_c.MNA, body_c.SOI]
                resu = True
                fin = True
                if trace:
                    if body_c.SOI.name in pos_vel_dict:
                        pos_vel_dict[body_c.SOI.name] += [[next_op[0],body_c.GetCartFromOrb(next_op[0])]]
                    else:
                        pos_vel_dict[body_c.SOI.name] = [[next_op[0],body_c.GetCartFromOrb(next_op[0])]]
                    if body_c.SOI.name in orb_dict:
                        orb_dict[body_c.SOI.name] += [[body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE,0,2*PI]]
                    else:
                        orb_dict[body_c.SOI.name] = [[body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE,0,2*PI]]
        elif next_op[1]=='leaveSOI':
            LeaveSOI(body_c,next_op[0])
        elif next_op[1]=='burn':
            Burn(body_c,False,next_op[2],next_op[0])
            i_b += 1
        elif next_op[1]=='end':
            end_pos = body_c.GetCartFromOrb(next_op[0])
            end_orb = [body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE, body_c.EPH, body_c.MNA, body_c.SOI]
            resu = True
            fin = True
        elif next_op[1]=='crash':
            end_pos = body_c.GetCartFromOrb(next_op[0])
            end_orb = [body_c.SMA, body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE, body_c.EPH, body_c.MNA, body_c.SOI]
            resu = False
            fin = True

        currt = next_op[0]
        if it > config.n_it*10:
            print(next_op)
            if next_op[2]!='NA':
                print(next_op[2].name)
            print(body_c.ECC, body_c.INC, body_c.LAN, body_c.LPE, body_c.MNA, body_c.SMA, body_c.SOI.name)
            #print('more than '+str(config.n_it*10)+' iterations for move to end')
            if it > config.n_it*10.2:
                print(burns)
                break

    return (resu, end_pos, end_orb, pos_vel_dict, orb_dict, mindist_w_targ, mindist_w_targ_par, DV_tot, currt, inter_score)

#Does a burn from a burn time and a delta-V vector, either in fixed coordinates or in prograde/normal/radial coordinates
#Not used for MGA-1DSM problem
def Burn(body_c, cart, DV, UT):
    body_c.GetCartFromOrb(UT)
    if cart:
        body_c.Vc = body_c.Vc + DV
    else:
        vec_tang = body_c.Vc/np.linalg.norm(body_c.Vc)
        vec_n = np.cross(body_c.X,vec_tang)
        vec_norm = vec_n/np.linalg.norm(vec_n)
        vec_rad = np.cross(vec_tang, vec_norm)
        dVcC = DV[2]*vec_tang + DV[0]*vec_rad + DV[1]*vec_norm
        body_c.Vc = body_c.Vc + dVcC
    body_c.GetOrbFromCart(UT)

#Leave the current SOI for the parent SOI
#Not used for MGA-1DSM problem
def LeaveSOI(body_c, UT):
    nextSOI = body_c.SOI.SOI
    [pos_c, vel_c] = body_c.GetCartFromOrb(UT)
    [pos_plan, vel_plan] = body_c.SOI.GetCartFromOrb(UT)
    nextPos = pos_c + pos_plan
    nextVel = vel_c + vel_plan
    body_c.X = nextPos
    body_c.Vc = nextVel
    body_c.SOI = nextSOI
    body_c.GetOrbFromCart(UT)

#Leave the current SOI for one of the children SOI
#Not used for MGA-1DSM problem
def EnterSOI(body_c, nextSOI, UT):
    [pos_c, vel_c] = body_c.GetCartFromOrb(UT)
    [pos_plan, vel_plan] = nextSOI.GetCartFromOrb(UT)
    nextPos = pos_c - pos_plan
    nextVel = vel_c - vel_plan
    body_c.X = nextPos
    body_c.Vc = nextVel
    body_c.SOI = nextSOI
    body_c.GetOrbFromCart(UT)

#Returns the time before collision with the orbited body (or inf is the periapsis is high enough)
#Not used for MGA-1DSM problem
def TimeToCrash(body_c, UT):
    per = (1-body_c.ECC)*body_c.SMA
    if per > body_c.SOI.alt_lim:
        t = float('inf')
    else:
        r = body_c.SOI.alt_lim
        n = np.sqrt(body_c.SOI.mu/abs(body_c.SMA)**3)
        if body_c.SMA < 0:
            N = body_c.MNA + n*(UT - body_c.EPH)
            if N<0:
                H = -np.arccosh((body_c.SMA-r)/(body_c.ECC*body_c.SMA))
                N1 = body_c.ECC*np.sinh(H) - H
                t = (N1-N)/n
            else:
                t = float('inf')
        else:
            M = (body_c.MNA + n*(UT - body_c.EPH))%(2*PI)
            E2 = 2*PI - np.arccos((body_c.SMA-r)/(body_c.ECC*body_c.SMA))
            M2 = E2 - body_c.ECC*sin(E2)
            t = ((M2-M)%(2*PI))/n
    return t

#Returns the time before leaving the SOI of the orbited body (or inf if not applicable)
#Not used for MGA-1DSM problem
def TimeToLeaveSOI(body_c, UT):
    if body_c.SOI.code == 0:
        t = float('inf')
    else:
        r = body_c.SOI.SOI_rad*1.00001                  # /!\ futur problemes ?
        n = np.sqrt(body_c.SOI.mu/abs(body_c.SMA)**3)
        apo = (1+body_c.ECC)*body_c.SMA
        per = (1-body_c.ECC)*body_c.SMA
        if r < per:
            t = 0
        else:
            if apo < 0:
                N = body_c.MNA + n*(UT - body_c.EPH)
                H = np.arccosh((body_c.SMA-r)/(body_c.ECC*body_c.SMA))
                N1 = body_c.ECC*np.sinh(H) - H
                if (abs(N)<N1):
                    t = (N1-N)/n
                else:
                    t = 0
            elif apo > r:
                M = (body_c.MNA + n*(UT - body_c.EPH))%(2*PI)
                E1 = np.arccos((body_c.SMA-r)/(body_c.ECC*body_c.SMA))
                E2 = 2*PI - E1
                M1 = E1 - body_c.ECC*sin(E1)
                M2 = E2 - body_c.ECC*sin(E2)
                if (M>=M1) and (M<=M2):
                    t = 0
                else:
                    t = ((M1-M)%(2*PI))/n
            else:
                t = float("inf")
    return t

#Returns the time before encountering another body, the body and a minimal distance.
#Based on (Hoots, 1984) http://adsabs.harvard.edu/full/1984CeMec..33..143H
#Bit of a mess
#Not used for MGA-1DSM problem
def TimeToEnterSOI(body_c, UT, nextt, targ):
    SOI = body_c.SOI
    SOI_poss = SOI.enfants
    SOI_poss_bis = []
    SOI_poss_ter = []
    n = np.sqrt(SOI.mu/abs(body_c.SMA)**3)
    approach = [0, float('inf'), 'NA']
    app_calc = [False, float('inf'),float('inf')]
    sun = SOI.solarSyst.retSun()
    resu = [float('inf'), sun.SOI]

#        print([pl.code for pl in SOI_poss])

    if body_c.ECC >= 1:
        apo = float('inf')
        Mc = body_c.MNA + n*(UT - body_c.EPH)
    else:
        apo = (1+body_c.ECC)*body_c.SMA
        Mc = (body_c.MNA + n*(UT - body_c.EPH))%(2*PI)
    tfp_c = Mc/n
    per = (1-body_c.ECC)*body_c.SMA
    Wc = np.array([sin(body_c.LAN)*sin(body_c.INC), cos(body_c.LAN)*sin(body_c.INC), cos(body_c.INC)])
    p_c = body_c.SMA*(1-body_c.ECC**2)
    for plan in SOI_poss:
        D = plan.SOI_rad
        if not(min(apo, plan.apo) < (max(per, plan.per)-D)):
            SOI_poss_bis.append(plan)
        elif plan.name == targ.name or plan.name == targ.SOI.name:
            approach = [1,(max(per, plan.per)-D)-min(apo, plan.apo), plan]

#        print([pl.code for pl in SOI_poss_bis])

    for plan in SOI_poss_bis:
        D = plan.SOI_rad
        p_p = plan.SMA*(1-plan.ECC**2)

        Wp = np.array([sin(plan.LAN)*sin(plan.INC), cos(plan.LAN)*sin(plan.INC), cos(plan.INC)])
        K = np.cross(Wp, Wc)
        cosIr = np.dot(Wp,Wc)
        sinIr = np.linalg.norm(K)

        if sinIr!=0:
            if (1/sinIr)*sin(body_c.INC)*sin(body_c.LAN-plan.LAN)>0:
                Dp = np.arccos((1/sinIr)*(sin(body_c.INC)*cos(plan.INC)*cos(body_c.LAN-plan.LAN)-sin(plan.INC)*cos(body_c.INC)))
            else:
                Dp = -np.arccos((1/sinIr)*(sin(body_c.INC)*cos(plan.INC)*cos(body_c.LAN-plan.LAN)-sin(plan.INC)*cos(body_c.INC)))
            if (1/sinIr)*sin(plan.INC)*sin(body_c.LAN-plan.LAN)>0:
                Dc = np.arccos((1/sinIr)*(sin(body_c.INC)*cos(plan.INC)-sin(plan.INC)*cos(body_c.INC)*cos(body_c.LAN-plan.LAN)))
            else:
                Dc = -np.arccos((1/sinIr)*(sin(body_c.INC)*cos(plan.INC)-sin(plan.INC)*cos(body_c.INC)*cos(body_c.LAN-plan.LAN)))
        else:
            Dp = plan.LPE
            Dc = body_c.LPE

        if apo-per < plan.apo+D-(plan.per-D):
            quasicirc=True
            fc_init1 = Dc-body_c.LPE
            fp_init1 = Dp-plan.LPE
        else:
            quasicirc=False
            #mean_r_p = min([max([(plan.per+plan.apo)/2, per-D]), apo-D])
            mean_r_p = (max([per, plan.per-D])+min(apo, plan.apo+D))/2
            fc_init1 = np.arccos((p_c-mean_r_p)/(body_c.ECC*mean_r_p))
            Oc_init1 = np.array([mean_r_p*cos(fc_init1), mean_r_p*sin(fc_init1), 0])
            Xc_init1 = Oc_init1@body_c.Mat_Rot
            fp_init1 = (np.arctan2(Xc_init1[1],Xc_init1[0])-plan.LAN-plan.LPE)%(2*PI)

        ayp = plan.ECC*sin(plan.LPE-Dp)
        axp = plan.ECC*cos(plan.LPE-Dp)
        ayc = body_c.ECC*sin(body_c.LPE-Dc)
        axc = body_c.ECC*cos(body_c.LPE-Dc)

        (fpsol1, fcsol1, rpsol1, rcsol1, Dsol1) = NewtMinDist(body_c, plan, fc_init1, fp_init1, ayc, axc, ayp, axp, Dp, Dc, cosIr, D)

        if quasicirc:
            fc_init2 = fcsol1 + PI
            fp_init2 = fpsol1 + PI
        else:
            fc_init2 = 2*PI - fc_init1
            Oc_init2 = np.array([mean_r_p*cos(fc_init2), mean_r_p*sin(fc_init2), 0])
            Xc_init2 = Oc_init2@body_c.Mat_Rot
            fp_init2 = (np.arctan2(Xc_init2[1],Xc_init2[0])-plan.LAN-plan.LPE)%(2*PI)

        (fpsol2, fcsol2, rpsol2, rcsol2, Dsol2) = NewtMinDist(body_c, plan, fc_init2, fp_init2, ayc, axc, ayp, axp, Dp, Dc, cosIr, D)

#            print(Dsol1, Dsol2)
        if Dsol1<D or Dsol2<D:
            SOI_poss_ter.append([plan, [ayc, axc, D, sinIr, Dc, quasicirc, ayp, axp, p_p, Dp]])
        if plan.name == targ.name or plan.name == targ.SOI.name:
            #approach = [2,min(Dsol1, Dsol2), plan]
            app_calc = [True, fcsol1, fcsol2, plan]

#        print([pl[0].name for pl in SOI_poss_ter])

    t_lim = nextt
    for plan_and_var in SOI_poss_ter:
        plan = plan_and_var[0]
        [ayc, axc, D, sinIr, Dc, quasicirc, ayp, axp, p_p, Dp] = plan_and_var[1]
        tarplan = False
        parplan = False
        closest_app = float('inf')
        if plan.name == targ.name:
            tarplan = True
        elif plan.name == targ.SOI.name:
            parplan = True

        P_p = plan.T
        Mp = (plan.MNA + 2*PI*UT/P_p)%(2*PI)
        tfp_p = Mp*P_p/(2*PI)

        vuln_ang_c = getVulnWin(body_c, ayc, axc, D, sinIr, p_c, Dc, quasicirc, plan.apo, plan.per)
        vuln_ang_p = getVulnWin(plan, ayp, axp, D, sinIr, p_p, Dp, True, apo, per)

        if body_c.ECC>=1:
            vuln_t_c = []
            marq = True
            prevt = 0
            for f in vuln_ang_c:
                E = (2*np.arctanh(np.sqrt((body_c.ECC-1)/(1+body_c.ECC))*np.tan(f/2)))
                M = body_c.ECC*np.sinh(E)-E
                t = M*np.sqrt(-body_c.SMA**3/SOI.mu)
                if marq:
                    prevt = t
                else:
                    vuln_t_c += [[(t+prevt)/2,abs(t-prevt)/2]]
                marq = not marq
            if len(vuln_t_c)==2:
                if vuln_t_c[1][0]<0:
                    win1 = vuln_t_c[1]
                    win2 = vuln_t_c[0]
                    vuln_t_c = [win1,win2]
        else:
            vuln_t_c = []
            marq = True
            prevt = 0
            for f in vuln_ang_c:
                E = (2*np.arctan2(np.sqrt(1-body_c.ECC)*sin(f/2),np.sqrt(1+body_c.ECC)*cos(f/2)))%(2*PI)
                if f>=2*PI:
                    E += 2*PI
                M = E - body_c.ECC*sin(E)
                t = M*body_c.T/(2*PI)
                if marq:
                    prevt = t
                else:
                    vuln_t_c += [[(t+prevt)/2,abs(t-prevt)/2]]
                marq = not marq

        if vuln_ang_p==[0,2*PI]:
            vuln_t_p_b = [[P_p/2,P_p/2]]
            vuln_t_p = [[P_p/2,P_p/2]]
        else:
            vuln_t_p_b = []
            marq = True
            prevt = 0
            for f in vuln_ang_p:
                E = (2*np.arctan2(np.sqrt(1-plan.ECC)*sin(f/2),np.sqrt(1+plan.ECC)*cos(f/2)))%(2*PI)
                if f>=2*PI:
                    E += 2*PI
                M = E - plan.ECC*sin(E)
                t = M*P_p/(2*PI)
                if marq:
                    prevt = t
                else:
                    vuln_t_p_b += [[(t+prevt)/2,abs(t-prevt)/2]]
                marq = not marq
            vuln_t_p = vuln_t_p_b[:]
            if vuln_t_p_b[-1][0]>=P_p/2:
                vuln_t_p = [[vuln_t_p_b[-1][0]-P_p, vuln_t_p_b[-1][1]]] + vuln_t_p
            if vuln_t_p_b[0][0]<=P_p/2:
                vuln_t_p = vuln_t_p + [[vuln_t_p_b[0][0]+P_p, vuln_t_p_b[0][1]]]

#            print("vuln_t_p :")
#            print(vuln_t_p)
#            print("vuln_t_c :")
#            print(vuln_t_c)
#            for windp in vuln_t_p:
#                pos_vel_list += [plan.GetCartFromOrb(windp[0]-tfp_p)]
#            for windc in vuln_t_c:
#                pos_vel_list += [body_c.GetCartFromOrb(windc[0]-tfp_c, SOI)]

#            print(tfp_p, tfp_c)
        rap_c = 0
        rap_p = 0
        for vuln_win_t_c in vuln_t_c:
            rap_c += 2*vuln_win_t_c[1]
        for vuln_win_t_p in vuln_t_p_b:
            rap_p += 2*vuln_win_t_p[1]

#            print(vuln_ang_c)
#            print(plan.name)
#            print(rap_c/body_c.T, rap_p/P_p)
        if (rap_c/body_c.T)*(rap_p/P_p)>0.5:
            per_check = min(P_p, body_c.T)/5
            dt = UT-per_check
            R = 0
            prevR = 0
            fl_first = True
            while dt<t_lim+2*per_check:
                prevR = R
                [rc,drc] = body_c.GetCartFromOrb(dt)
                [rp,drp] = plan.GetCartFromOrb(dt)
                R = np.dot(drc-drp,rc-rp)
                if fl_first:
                    fl_first=False
                else:
                    if np.sign(R)!=np.sign(prevR):
                        nt = dt-per_check + per_check*abs(prevR)/(abs(R)+abs(prevR))

                        (resumod, resu1, appmod, approach1) = findClosestApp(nt, body_c, plan, t_lim, D, UT, tarplan, parplan, closest_app)
                        if resumod:
                            resu = resu1
                            t_lim = resu[0]
                        if appmod:
                            approach = approach1
                            closest_app = approach[1]
                dt += per_check

        elif body_c.ECC>=1:
            for v_c in vuln_t_c:
                if v_c[0]-tfp_c-v_c[1]<t_lim:
                    tc_in_p = (tfp_p+v_c[0]-tfp_c)%P_p
#                    print("tc_in_p : "+str(tc_in_p))
                    for v_p in vuln_t_p:
#                        print("v_p : "+str(v_p))
                        if abs(tc_in_p - v_p[0]) < v_c[1]+v_p[1]:
                            nt = UT + (max(tc_in_p-v_c[1], v_p[0]-v_p[1]) + min(tc_in_p+v_c[1], v_p[0]+v_p[1]))/2 - tc_in_p + v_c[0] - tfp_c
#                            print("nt : "+str(nt))
                            (resumod, resu1, appmod, approach1) = findClosestApp(nt, body_c, plan, t_lim, D, UT, tarplan, parplan, closest_app)
                            if resumod:
                                resu = resu1
                                t_lim = resu[0]
                            if appmod:
                                approach = approach1
                                closest_app = approach[1]

        else:
            dt = 0
            while UT+dt<t_lim+body_c.T/2:
                for v_c in vuln_t_c:
                    for v_p in vuln_t_p:
                        tc_in_p = (tfp_p+v_c[0]+dt-tfp_c)%P_p
                        if abs(tc_in_p - v_p[0]) < v_c[1]+v_p[1]:
                            nt = UT + (max(tc_in_p-v_c[1], v_p[0]-v_p[1]) + min(tc_in_p+v_c[1], v_p[0]+v_p[1]))/2 - tc_in_p + v_c[0] + dt - tfp_c

                            (resumod, resu1, appmod, approach1) = findClosestApp(nt, body_c, plan, t_lim, D, UT, tarplan, parplan, closest_app)
                            if resumod:
                                resu = resu1
                                t_lim = resu[0]
                            if appmod:
                                approach = approach1
                                closest_app = approach[1]
                dt += body_c.T

    if app_calc[0]:  ##################################### attention periodes courtes ! #########
        if resu[1] == 'NA' or not (resu[1].name == targ.name or resu[1].name == targ.SOI.name):
            dt = UT
            tsol = []
            if body_c.ECC>=1:
                for f in app_calc[1:3]:
                    E = (2*np.arctanh(np.sqrt((body_c.ECC-1)/(1+body_c.ECC))*np.tan(f/2)))
                    M = body_c.ECC*np.sinh(E)-E
                    t = M*np.sqrt(-body_c.SMA**3/SOI.mu)
                    tsol += [t]
                for ts in tsol:
                    abs_t = UT+ts-tfp_c
                    if UT<abs_t and abs_t<t_lim:
                        dist = np.linalg.norm(body_c.GetCartFromOrb(abs_t)[0]-app_calc[3].GetCartFromOrb(abs_t)[0])
                        if dist < approach[1]:
                            approach = [3, dist, app_calc[3]]
            else:
                for f in app_calc[1:3]:
                    E = (2*np.arctan2(np.sqrt(1-body_c.ECC)*sin(f/2),np.sqrt(1+body_c.ECC)*cos(f/2)))%(2*PI)
                    if f>=2*PI:
                        E += 2*PI
                    M = E - body_c.ECC*sin(E)
                    t = M*body_c.T/(2*PI)
                    tsol += [t]
                while dt < t_lim + body_c.T/2:
                    for ts in tsol:
                        abs_t = dt+ts-tfp_c
                        if UT<abs_t and abs_t<t_lim:
                            dist = np.linalg.norm(body_c.GetCartFromOrb(abs_t)[0]-app_calc[3].GetCartFromOrb(abs_t)[0])
                            if dist < approach[1]:
                                approach = [3, dist, app_calc[3]]
                    dt += body_c.T

    return [resu[0], resu[1], approach]

#Returns angular windows where an encounter is possible
#Not used for MGA-1DSM problem
def getVulnWin(body, ay, ax, D, sinIr, p, delt, quasicirc, apo, per):
    if body.ECC<1:
        inter1 = getVulnAng(body, ay, ax, D, sinIr, p, delt)
    else:
        inter1 = [0,2*PI]
    if not all(inter1[i] <= inter1[i+1] for i in range(len(inter1)-1)):
        print("liste des angles pas dans l'ordre")
    if not quasicirc:
        inter2 = getVulnRad(body, D, p, apo, per)
    else:
        inter2 = [0,2*PI]

    vuln_win_b = inter_intersec(inter1, inter2)

    if vuln_win_b[0]==0 and vuln_win_b[-1]==2*PI and len(vuln_win_b)>2:
        vuln_win = vuln_win_b[2:-1] + [vuln_win_b[1]+2*PI]
    else:
        vuln_win = vuln_win_b

#        for f in vuln_win:
#            pos_vel_list += [getCartFromf(body, f)]

    return vuln_win

#Returns angular windows where one body is close to the other body's orbit plane
#Not used for MGA-1DSM problem
def getVulnAng(body, ay, ax, D, sinIr, p, delt):
    Q1 = p*sinIr*(p*sinIr - 2*D*ay)-(1-body.ECC**2)*D**2
    Q2 = p*sinIr*(p*sinIr + 2*D*ay)-(1-body.ECC**2)*D**2

    if Q1<0:
        if Q2<0:
            return [0,2*PI]
        else:
            cosUrMo1 = (-ax*D**2 + (p*sinIr+D*ay)*np.sqrt(Q2))/(Q2 + D**2)
            cosUrMo2 = (-ax*D**2 - (p*sinIr+D*ay)*np.sqrt(Q2))/(Q2 + D**2)

            if abs(cosUrMo1)>1 or abs(cosUrMo2)>1:
                return [0,2*PI]
            else:
                if cosUrMo1<cosUrMo2:
                    Ur1 = 2*PI - np.arccos(cosUrMo2)
                    Ur2 = 2*PI - np.arccos(cosUrMo1)
                else:
                    Ur1 = 2*PI - np.arccos(cosUrMo1)
                    Ur2 = 2*PI - np.arccos(cosUrMo2)

                f_1 = (Ur1 + delt - body.LPE)%(2*PI)
                f_2 = (Ur2 + delt - body.LPE)%(2*PI)
                if f_2<f_1:
                    return [0,f_2,f_1,2*PI]
                else:
                    return [f_1,f_2]
    else:
        if Q2<0:
            cosUrPl1 = (-ax*D**2 + (p*sinIr-D*ay)*np.sqrt(Q1))/(Q1 + D**2)
            cosUrPl2 = (-ax*D**2 - (p*sinIr-D*ay)*np.sqrt(Q1))/(Q1 + D**2)

            if abs(cosUrPl1)>1 or abs(cosUrPl2)>1:
                return [0,2*PI]
            else:
                if cosUrPl1<cosUrPl2:
                    Ur1 = np.arccos(cosUrPl1)
                    Ur2 = np.arccos(cosUrPl2)
                else:
                    Ur1 = np.arccos(cosUrPl2)
                    Ur2 = np.arccos(cosUrPl1)

                f_1 = (Ur1 + delt - body.LPE)%(2*PI)
                f_2 = (Ur2 + delt - body.LPE)%(2*PI)
                if f_2<f_1:
                    return [0,f_2,f_1,2*PI]
                else:
                    return [f_1,f_2]
        else:
            cosUrPl1 = (-ax*D**2 + (p*sinIr-D*ay)*np.sqrt(Q1))/(Q1 + D**2)
            cosUrPl2 = (-ax*D**2 - (p*sinIr-D*ay)*np.sqrt(Q1))/(Q1 + D**2)
            cosUrMo1 = (-ax*D**2 + (p*sinIr+D*ay)*np.sqrt(Q2))/(Q2 + D**2)
            cosUrMo2 = (-ax*D**2 - (p*sinIr+D*ay)*np.sqrt(Q2))/(Q2 + D**2)

            if abs(cosUrPl1)>1 or abs(cosUrPl2)>1 or abs(cosUrMo1)>1 or abs(cosUrMo2)>1:
                return [0,2*PI]
            else:
                if cosUrPl1<cosUrPl2:
                    Ur1 = np.arccos(cosUrPl1)
                    Ur4 = np.arccos(cosUrPl2)
                else:
                    Ur1 = np.arccos(cosUrPl2)
                    Ur4 = np.arccos(cosUrPl1)

                if cosUrMo1<cosUrMo2:
                    Ur2 = 2*PI - np.arccos(cosUrMo1)
                    Ur3 = 2*PI - np.arccos(cosUrMo2)
                else:
                    Ur2 = 2*PI - np.arccos(cosUrMo2)
                    Ur3 = 2*PI - np.arccos(cosUrMo1)

#                UrPl1 = np.arccos(cosUrPl1)
#                UrPl2 = np.arccos(cosUrPl2)
#                UrMo1 = 2*PI - np.arccos(cosUrMo1)
#                UrMo2 = 2*PI - np.arccos(cosUrMo2)

            f_1 = (Ur1 + delt - body.LPE)%(2*PI)
            f_2 = (Ur2 + delt - body.LPE)%(2*PI)
            f_3 = (Ur3 + delt - body.LPE)%(2*PI)
            f_4 = (Ur4 + delt - body.LPE)%(2*PI)

            if f_2<f_1:
                return [0,f_2,f_3,f_4,f_1,2*PI]
            elif f_4<f_3:
                return [0,f_4,f_1,f_2,f_3,2*PI]
            elif f_3<f_2:
                return [f_3,f_4,f_1,f_2]
            else:
                return [f_1,f_2,f_3,f_4]

#Returns angular windows where the craft orbit radius is close to the potential encounter orbit radius
#Not used for MGA-1DSM problem
def getVulnRad(body, D, p, apo, per):
    r_int = per-D
    r_ext = apo+D
    cosf_int = (p-r_int)/(body.ECC*r_int)
    cosf_ext = (p-r_ext)/(body.ECC*r_ext)
    if abs(cosf_int)>1:
        if abs(cosf_ext)>1:
            return [0,2*PI]
        else:
            return [0, np.arccos(cosf_ext), 2*PI - np.arccos(cosf_ext), 2*PI]
    else:
        if abs(cosf_ext)>1:
            return [np.arccos(cosf_int), 2*PI - np.arccos(cosf_int)]
        else:
            f_1 = np.arccos(cosf_int)
            f_2 = np.arccos(cosf_ext)
            f_3 = 2*PI - np.arccos(cosf_ext)
            f_4 = 2*PI - np.arccos(cosf_int)
            return [f_1,f_2,f_3,f_4]

#Returns the true anomaly, radius for both orbits during their closest encounter, plus the minimal distance
#Not used for MGA-1DSM problem
def NewtMinDist(body_c, plan, fc_in,fp_in, ayc, axc, ayp, axp, Dp, Dc, cosIr, SOI_rad):
    e_c = body_c.ECC
    e_p = plan.ECC
    fc = fc_in
    fp = fp_in
    sq_rad = SOI_rad*SOI_rad

    Urp = fp + plan.LPE - Dp
    Urc = fc + body_c.LPE - Dc
    rp = plan.SMA*(1-e_p**2)/(1+e_p*cos(fp))
    rc = body_c.SMA*(1-e_c**2)/(1+e_c*cos(fc))
    cosGa = cos(Urc)*cos(Urp)+sin(Urc)*sin(Urp)*cosIr
    A = sin(Urc) + ayc
    B = cos(Urc) + axc
    C = sin(Urp) + ayp
    D = cos(Urp) + axp
    Eq1 = rc*e_c*sin(fc)+rp*(cos(Urp)*A-sin(Urp)*cosIr*B)
    Eq2 = rp*e_p*sin(fp)+rc*(cos(Urc)*C-sin(Urc)*cosIr*D)
    sq_dist_cp = rp*rp + rc*rc - 2*rc*rp*cosGa

    #pos_vel_list += [[0,getCartFromf(body_c, fc)]]
    #pos_vel_list += [[0,getCartFromf(plan, fp)]]

    it=0
    while (abs(Eq1)>1000 or abs(Eq2)>1000) and sq_dist_cp>sq_rad:
        it += 1
        cosEc = (e_c+cos(fc))/(1+e_c*cos(fc))
        cosEp = (e_p+cos(fp))/(1+e_p*cos(fp))
        F = rc*e_c*sin(fc)+rp*(A*cos(Urp)-B*cosIr*sin(Urp))
        G = rp*e_p*sin(fp)+rc*(C*cos(Urc)-D*cosIr*sin(Urc))
        dFdfc = rc*e_c*cosEc + rp*cosGa
        dFdfp = -rp*(A*C+B*D*cosIr)/(1+e_p*cos(fp))
        dGdfc = -rc*(A*C+B*D*cosIr)/(1+e_c*cos(fc))
        dGdfp = rp*e_p*cosEp + rc*cosGa
        fc = fc + 1*(F*dGdfp-G*dFdfp)/(dFdfp*dGdfc-dFdfc*dGdfp)
        fp = fp + 1*(G*dFdfc-F*dGdfc)/(dFdfp*dGdfc-dFdfc*dGdfp)
        Urp = fp + plan.LPE - Dp
        Urc = fc + body_c.LPE - Dc
        rp = plan.SMA*(1-e_p**2)/(1+e_p*cos(fp))
        rc = body_c.SMA*(1-e_c**2)/(1+e_c*cos(fc))
        cosGa = cos(Urc)*cos(Urp)+sin(Urc)*sin(Urp)*cosIr
        A = sin(Urc) + ayc
        B = cos(Urc) + axc
        C = sin(Urp) + ayp
        D = cos(Urp) + axp
        Eq1 = rc*e_c*sin(fc)+rp*(cos(Urp)*A-sin(Urp)*cosIr*B)
        Eq2 = rp*e_p*sin(fp)+rc*(cos(Urc)*C-sin(Urc)*cosIr*D)
        sq_dist_cp = rp*rp + rc*rc - 2*rc*rp*cosGa

        if it > config.n_it:
            break

    #print(it)
    #pos_vel_list += [[0,getCartFromf(body_c, fc)]]
    #pos_vel_list += [[0,getCartFromf(plan, fp)]]
    return(fp, fc, rp, rc, np.sqrt(sq_dist_cp))


# function - find social gaze time point
def find_socialgaze_timepoint(body_part_locs_merge, min_length, angle_thres):

    import pandas as pd
    import numpy as np
    import matplotlib.pyplot as plt
    import scipy
    import string
    import warnings
    import pickle

    animal_names_unique = pd.unique(pd.DataFrame(body_part_locs_merge.keys()).iloc[:,0])
    nanimals = animal_names_unique.shape[0]
    body_parts_unique = pd.unique(pd.DataFrame(body_part_locs_merge.keys()).iloc[:,1])
    nbodies = body_parts_unique.shape[0]

    warnings.filterwarnings('ignore')
    eye_direction_merge = {}
    eye_contact_or_not_merge = {}
    look_at_face_or_not_merge = {}

    for ianimal in np.arange(0,nanimals,1):

        iname = animal_names_unique[ianimal]
        #
        if (iname == animal_names_unique[0]):
            iname_other = animal_names_unique[1]
        elif (iname == animal_names_unique[1]):
            iname_other = animal_names_unique[0]

        eye_dir_frames = []
        eye_contact_frames = []
        look_at_face_frames = []

        for iframe in np.arange(0,min_length,1):

            # define the position of the animal's face part
            #
            # fill in the nan gaps
            if (iframe > 0):
                meaneye_loc_old = meaneye_loc
                meantuft_loc_old = meantuft_loc
                mass_loc_old = mass_loc
                whiblz_loc_old = whiblz_loc
            #
            lefteye_loc = np.array(body_part_locs_merge[(iname,'leftEye')])[iframe,:]
            righteye_loc = np.array(body_part_locs_merge[(iname,'rightEye')])[iframe,:]
            lefttuft_loc = np.array(body_part_locs_merge[(iname,'leftTuft')])[iframe,:]
            righttuft_loc = np.array(body_part_locs_merge[(iname,'rightTuft')])[iframe,:]
            whiblz_loc = np.array(body_part_locs_merge[(iname,'whiteBlaze')])[iframe,:]
            mouth_loc = np.array(body_part_locs_merge[(iname,'mouth')])[iframe,:]
            #
            meaneye_loc = np.nanmean(np.vstack((lefteye_loc,righteye_loc)),axis=0)
            #
            if ((np.sum(np.isnan(meaneye_loc))>0)&(iframe>0)):
                meaneye_loc = meaneye_loc_old
            #
            meantuft_loc = np.nanmean(np.vstack((lefttuft_loc,righttuft_loc)),axis=0)
            if ((np.sum(np.isnan(meantuft_loc))>0)&(iframe>0)):
                meantuft_loc = meantuft_loc_old
            #
            mass_loc = np.nanmean(np.vstack((lefteye_loc,righteye_loc,lefteye_loc,righteye_loc,whiblz_loc,mouth_loc)),axis=0)
            if ((np.sum(np.isnan(mass_loc))>0)&(iframe>0)):
                mass_loc = mass_loc_old
            #
            if ((np.sum(np.isnan(whiblz_loc))>0)&(iframe>0)):
                whiblz_loc = whiblz_loc_old

            # examine whether this animal is looking at the other's eyes or face
            #
            # fill in the nan gaps
            if (iframe > 0):
                meaneye_loc_other_old = meaneye_loc_other
                meantuft_loc_other_old = meantuft_loc_other
                mass_loc_other_old = mass_loc_other
                whiblz_loc_other_old = whiblz_loc_other
            #
            lefteye_loc_other = np.array(body_part_locs_merge[(iname_other,'leftEye')])[iframe,:]
            righteye_loc_other = np.array(body_part_locs_merge[(iname_other,'rightEye')])[iframe,:]
            lefttuft_loc_other = np.array(body_part_locs_merge[(iname_other,'leftTuft')])[iframe,:]
            righttuft_loc_other = np.array(body_part_locs_merge[(iname_other,'rightTuft')])[iframe,:]
            whiblz_loc_other = np.array(body_part_locs_merge[(iname_other,'whiteBlaze')])[iframe,:]
            mouth_loc_other = np.array(body_part_locs_merge[(iname_other,'mouth')])[iframe,:]
            #
            meaneye_loc_other = np.nanmean(np.vstack((lefteye_loc_other,righteye_loc_other)),axis=0)
            if ((np.sum(np.isnan(meaneye_loc_other))>0)&(iframe>0)):
                meaneye_loc_other = meaneye_loc_other_old
            #
            meantuft_loc_other = np.nanmean(np.vstack((lefttuft_loc_other,righttuft_loc_other)),axis=0)
            if ((np.sum(np.isnan(meantuft_loc_other))>0)&(iframe>0)):
                meantuft_loc_other = meantuft_loc_other_old
            #
            mass_loc_other = np.nanmean(np.vstack((lefteye_loc_other,righteye_loc_other,lefteye_loc_other,righteye_loc_other,whiblz_loc_other,mouth_loc_other)),axis=0)
            if ((np.sum(np.isnan(mass_loc_other))>0)&(iframe>0)):
                mass_loc_other = mass_loc_other_old
            #
            if ((np.sum(np.isnan(whiblz_loc_other))>0)&(iframe>0)):
                whiblz_loc_other = whiblz_loc_other_old

            # define important vectors for defining head gaze direction
            # fill in the nan gaps
            if (iframe > 0):
                eyesight_dir_old = eyesight_dir
            #
            Vect1 = lefteye_loc - righteye_loc
            Vect2 = whiblz_loc - mouth_loc
            Vect3 = lefttuft_loc - lefteye_loc
            Vect4 = righttuft_loc - righteye_loc
            Vect5 = meantuft_loc - meaneye_loc
            try:
                Vect1 = Vect1 / scipy.linalg.norm(Vect1)
                Vect2 = Vect2 / scipy.linalg.norm(Vect2)
                Vect3 = Vect3 / scipy.linalg.norm(Vect3)
                Vect4 = Vect4 / scipy.linalg.norm(Vect4)
                Vect5 = Vect5 / scipy.linalg.norm(Vect5)
            except:
                Vect1 = Vect1 / np.nanmax(np.absolute(Vect1))
                Vect2 = Vect2 / np.nanmax(np.absolute(Vect2))
                Vect3 = Vect3 / np.nanmax(np.absolute(Vect3))
                Vect4 = Vect4 / np.nanmax(np.absolute(Vect4))
                Vect5 = Vect5 / np.nanmax(np.absolute(Vect5))
            eyesight_dir = np.cross(Vect1, Vect2)
            try:
                eyesight_dir = eyesight_dir / scipy.linalg.norm(eyesight_dir)
            except:
                eyesight_dir = eyesight_dir / np.nanmax(np.absolute(eyesight_dir))
            #
            if (np.dot(eyesight_dir, Vect5)>0):
                eyesight_dir = -eyesight_dir
            #
            if ((np.sum(np.isnan(eyesight_dir))>0)&(iframe>0)):
                eyesight_dir = eyesight_dir_old
            eye_dir_frames.append(eyesight_dir)

            # where left eye is looking
            # vector between body part
            vect_face_eye = mass_loc_other - meaneye_loc
            vect_eye_eye = meaneye_loc_other - meaneye_loc
            try:
                vect_face_eye = vect_face_eye / scipy.linalg.norm(vect_face_eye)
            except:
                vect_face_eye = vect_face_eye / np.nanmax(np.absolute(vect_face_eye))
            try:
                vect_eye_eye = vect_eye_eye / scipy.linalg.norm(vect_eye_eye)
            except:
                vect_eye_eye = vect_eye_eye / np.nanmax(np.absolute(vect_eye_eye))

            angle_face_eye =  np.arccos(np.clip(np.dot(eyesight_dir/np.linalg.norm(eyesight_dir), vect_face_eye/np.linalg.norm(vect_face_eye)), -1.0, 1.0))
            angle_eye_eye =  np.arccos(np.clip(np.dot(eyesight_dir/np.linalg.norm(eyesight_dir), vect_eye_eye/np.linalg.norm(vect_eye_eye)), -1.0, 1.0))

            eye_contact_thres = ((angle_eye_eye>0)&(angle_eye_eye<angle_thres))
            eye_lookface_thres = ((angle_face_eye>0)&(angle_face_eye<angle_thres))

            eye_contact_frames.append(np.int(eye_contact_thres))
            look_at_face_frames.append(np.int(eye_lookface_thres))

        # save to the summarized data
        eye_direction_merge[(iname)] = eye_dir_frames
        eye_contact_or_not_merge[(iname)] = eye_contact_frames
        look_at_face_or_not_merge[(iname)] = look_at_face_frames

    return eye_direction_merge, eye_contact_or_not_merge, look_at_face_or_not_merge


# 一个对象的 属性 可以是 另外一个类创建的对象
# 需求
# 士兵 许三多 有一把 AK47
# 士兵 可以 开火
# 枪 能够 发射 子弹
# 枪 装填 装填子弹 —— 增加子弹数量
# 假设每个新兵都没有枪

class Gun:

    def __init__(self, modle):
        # 枪的型号
        self.modle = modle

        # 子弹数量
        self.bullet_count = 0

    def add_bullet(self, count):
        self.bullet_count += count

    def shoot(self):
        # 判断子弹数量
        if self.bullet_count <= 0:
            print("[%s] 没有子弹了，溜溜溜" % self.modle)

            return
        # 发射子弹
        self.bullet_count -= 1

        # 提示发射信息
        print("[%s] 打死你个龟孙... 剩[%d]" % (self.modle, self.bullet_count))

class Solder:
    def __init__(self, name):
        # 新兵姓名
        self.name = name

        # 枪 新兵没有枪，在定义属性时，如果 不知道设置什么初始值，可以设置为 None
        self.gun = None

    def fire(self):

        # 判断是否有枪
        if self.gun is None:  # 身份运算符用于比较两个对象的内存地址是否一致,是否是对同一个对象的引用
            print("[%s] 没有枪。。" % self.name)

            return

        # 高喊口号
        print("冲啊..[%s]" % self.name)

        # 装填子弹
        self.gun.add_bullet(50)

        # 发射子弹
        self.gun.shoot()

# 创建枪对象
ak47 = Gun("AK47")

# 创建许三多
xusanduo = Solder("许三多")

xusanduo.gun = ak47  # 一个对象的 属性 可以是 另外一个类创建的对象
xusanduo.fire()

print(xusanduo.gun)


import re
from datetime import datetime
from fastapi import HTTPException, status

DATE_FORMAT = "%Y-%m-%d"

def validate_date(date_str):
    try:
        datetime.strptime(date_str, DATE_FORMAT)
        return True
    except ValueError:
        return False

def validate(start_date, end_date):
    if start_date is None or not validate_date(start_date):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid start date",
        )
    if end_date is None or not validate_date(end_date):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Invalid end date",
        )
    if start_date > end_date:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Start date should be less than end date",
        )
    if start_date > datetime.today().strftime(DATE_FORMAT) or end_date > datetime.today().strftime(DATE_FORMAT):
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Start date and end date should be less than today's date",
        )


#!/usr/bin/env python
try:  # support either PyQt5 or 6
    from PyQt5 import uic
    from PyQt5.QtCore import *
    from PyQt5.QtGui import QSurfaceFormat
    from PyQt5.QtWidgets import QApplication, QMainWindow

    PyQtVersion = 5
except ImportError:
    print("trying Qt6")
    from PyQt6 import uic
    from PyQt6.QtCore import QEvent, Qt, QTimer
    from PyQt6.QtGui import QSurfaceFormat
    from PyQt6.QtWidgets import QApplication, QMainWindow

    PyQtVersion = 6

import math
import random
import sys

sys.path.insert(0, "../ByteImage")
import ByteImage

class MainWindow(QMainWindow):
    def __init__(self, parent=None):
        super(QMainWindow, self).__init__(parent)
        uic.loadUi("form.ui", self)  # Load the .ui file
        self.width.valueChanged.connect(self.transmit_size)
        self.height.valueChanged.connect(self.transmit_size)
        self.start_sim.clicked.connect(self.toggle_sim)
        self.timer = QTimer()
        self.timer.timeout.connect(self.transmit_data)

    def toggle_sim(self):
        if self.timer.isActive():
            self.timer.stop()
            self.start_sim.setText("Start")
        else:
            self.timer.start(self.timer_duration.value())
            self.start_sim.setText("Stop")

    def send_lines(self):
        rx = random.randint
        img = ByteImage.ByteImage(
            self.width.value(), self.height.value(), 255, 255, 255, 255
        )
        img.clear(255, 255, 255)
        cx = rx(1, img.width - 1)
        cy = rx(1, img.height - 1)
        for x in range(0, 1000):
            img.line(
                cx,
                cy,
                rx(1, img.width - 1),
                rx(1, img.height - 1),
                rx(0, 255),
                rx(0, 255),
                rx(0, 255),
            )
        with open(self.fifo_name.text(), "wb") as fifo:
            fifo.write(img.pixels)

    def send_check(self):
        rx = random.randint
        img = ByteImage.ByteImage(
            self.width.value(), self.height.value(), 255, 255, 255, 255
        )
        img.checker(
            rx(0, 255), rx(0, 255), rx(0, 255), 255, 255, 255, check_size=rx(5, 50)
        )
        with open(self.fifo_name.text(), "wb") as fifo:
            fifo.write(img.pixels)

    def send_pixel(self):
        img = ByteImage.ByteImage(
            self.width.value(), self.height.value(), 255, 255, 255, 255
        )
        img.random_pixels()
        with open(self.fifo_name.text(), "wb") as fifo:
            fifo.write(img.pixels)

    def transmit_data(self):
        if self.mode.currentIndex() == 0:
            self.send_lines()
        elif self.mode.currentIndex() == 1:
            self.send_check()
        elif self.mode.currentIndex() == 2:
            self.send_pixel()

    def transmit_size(self):
        with open("imagefifo", "wb") as fifo:
            fifo.write(f"resize {self.width.value()} {self.height.value()}".encode())

    if PyQtVersion == 5:

        def keyPressEvent(self, event):
            key = event.key()
            if key == Qt.Key_Escape:
                exit()
            self.update()

    else:  # Qt6 Versions

        def keyPressEvent(self, event):
            key = event.key()
            if key == Qt.Key.Key_Escape:
                exit()
            self.update()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = MainWindow()
    window.resize(200, 200)
    window.show()
    if PyQtVersion == 5:
        sys.exit(app.exec_())
    else:
        sys.exit(app.exec())


import deform
from pyramid.view import view_config

from dace.processinstance.core import DEFAULTMAPPING_ACTIONS_VIEWS
from pontus.default_behavior import Cancel
from pontus.form import FormView
from pontus.view import BasicView
from pontus.view_operation import MultipleView

from novaideo.content.processes.user_management.behaviors import (
    Remind)
from novaideo.content.person import Preregistration
from novaideo import _

class RemindViewStudyReport(BasicView):
    title = 'Alert for remove'
    name = 'alertforremove'
    template = 'novaideo:views/user_management/templates/alert_remind_registration.pt'

    def update(self):
        result = {}
        values = {'context': self.context}
        body = self.content(args=values, template=self.template)['body']
        item = self.adapt_item(body, self.viewid)
        result['coordinates'] = {self.coordinates: [item]}
        return result

class RemindView(FormView):
    title = _('Remove')
    name = 'remindform'
    formid = 'formremind'
    behaviors = [Remind, Cancel]
    validate_behaviors = False

    def before_update(self):
        self.action = self.request.resource_url(
            self.context, 'novaideoapi',
            query={'op': 'update_action_view',
                   'node_id': Remind.node_definition.id})
        self.schema.widget = deform.widget.FormWidget(
            css_class='deform novaideo-ajax-form')

@view_config(
    name='remind',
    context=Preregistration,
    renderer='pontus:templates/views_templates/grid.pt',
    )
class RemindViewMultipleView(MultipleView):
    title = _('Remind the user')
    name = 'remind'
    viewid = 'remind'
    template = 'pontus:templates/views_templates/simple_multipleview.pt'
    views = (RemindViewStudyReport, RemindView)
    validators = [Remind.get_validator()]

DEFAULTMAPPING_ACTIONS_VIEWS.update(
    {Remind: RemindViewMultipleView})


from __future__ import annotations

import asyncio
import logging
import os
import re
from collections import Counter
from typing import TYPE_CHECKING, Callable

import aiozipkin
import pytest
import sentry_sdk
from aiozipkin.transport import StubTransport

import walnats

from .helpers import UDPLogProtocol, fuzzy_match_counter, get_random_name

if TYPE_CHECKING:
    from _pytest.logging import LogCaptureFixture

async def noop(msg: str) -> None:
    pass

async def explode(msg: str) -> None:
    1 / 0

SENTRY_DSN = os.environ.get('SENTRY_DSN')
CI = bool(os.environ.get('CI'))

class MockMiddleware(walnats.middlewares.Middleware):
    def __init__(self) -> None:
        self.hist: list[str] = []

    def on_start(self, ctx) -> None:
        self.hist.append('on_start')

    def on_success(self, ctx) -> None:
        self.hist.append('on_success')

    def on_failure(self, ctx) -> None:
        self.hist.append('on_failure')

async def run_actor(
    handler: Callable,
    messages: str | list[str],
    *middlewares: walnats.middlewares.Middleware,
    trace_id: str | None = None,
    **kwargs,
) -> None:
    event = walnats.Event(get_random_name(), str)
    actor = walnats.Actor(get_random_name(), event, handler, middlewares=middlewares)
    events_reg = walnats.Events(actor.event)
    actors_reg = walnats.Actors(actor)
    if isinstance(messages, str):
        messages = [messages]
    async with events_reg.connect() as pub_conn, actors_reg.connect() as sub_conn:
        await pub_conn.register()
        await sub_conn.register()
        await asyncio.gather(
            *[pub_conn.emit(event, m, trace_id=trace_id) for m in messages],
        )
        await asyncio.sleep(.01)
        await sub_conn.listen(burst=True, batch=len(messages), **kwargs)

async def test_custom_sync() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')

        def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')

    await run_actor(handler, 'hi', Middleware())
    assert triggered == ['on_start', 'handler', 'on_success']

async def test_custom_sync__on_failure() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')

        def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')
        raise ZeroDivisionError

    await run_actor(handler, 'hi', Middleware())
    assert triggered == ['on_start', 'handler', 'on_failure']

async def test_custom_async() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        async def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')
            raise ctx.exception

        async def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        async def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')

    await run_actor(handler, 'hi', Middleware())
    assert len(triggered) == 3
    assert set(triggered) == {'on_start', 'handler', 'on_success'}

async def test_custom_async__on_failure() -> None:
    triggered = []

    class Middleware(walnats.middlewares.Middleware):
        async def on_failure(self, ctx: walnats.types.ErrorContext) -> None:
            triggered.append('on_failure')

        async def on_start(self, ctx: walnats.types.Context) -> None:
            triggered.append('on_start')

        async def on_success(self, ctx: walnats.types.OkContext) -> None:
            triggered.append('on_success')

    async def handler(msg: str) -> None:
        assert msg == 'hi'
        triggered.append('handler')
        raise ZeroDivisionError

    await run_actor(handler, 'hi', Middleware())
    assert len(triggered) == 3
    assert set(triggered) == {'on_start', 'handler', 'on_failure'}

async def test_ExtraLogMiddleware(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(noop, 'hi', walnats.middlewares.ExtraLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 2
    assert records[0].message == 'event received'
    assert records[1].message == 'event processed'

async def test_ExtraLogMiddleware__on_failure(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(explode, 'hi', walnats.middlewares.ExtraLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 3
    assert records[0].message == 'event received'
    assert records[1].message.startswith('Unhandled ZeroDivisionError in')
    assert records[2].message == 'actor failed'

async def test_TextLogMiddleware(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(noop, 'hi', walnats.middlewares.TextLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 2
    assert re.match(r'event .+: received by .+', records[0].message)
    assert re.match(r'event .+: processed by .+', records[1].message)

async def test_TextLogMiddleware__on_failure(caplog: LogCaptureFixture) -> None:
    caplog.set_level(logging.DEBUG)
    await run_actor(explode, 'hi', walnats.middlewares.TextLogMiddleware())
    records = []
    for record in caplog.records:
        if record.name.startswith('walnats'):
            records.append(record)
    assert len(records) == 3
    assert re.match(r'event .+: received by .+', records[0].message)
    assert records[1].message.startswith('Unhandled ZeroDivisionError in')
    assert re.match(r'event .+: actor .+ failed', records[2].message)

async def test_ErrorThresholdMiddleware__single_failure() -> None:
    mw = MockMiddleware()
    await run_actor(explode, 'hi', walnats.middlewares.ErrorThresholdMiddleware(mw))
    assert mw.hist == ['on_start']

async def test_ErrorThresholdMiddleware__multiple_failures() -> None:
    mw = MockMiddleware()
    await run_actor(
        explode, ['hi'] * 40,
        walnats.middlewares.ErrorThresholdMiddleware(mw, total_failures=1000),
    )
    assert Counter(mw.hist) == Counter(dict(on_start=40, on_failure=19))

async def test_ErrorThresholdMiddleware__multiple_failures_many_actors() -> None:
    mw = MockMiddleware()
    emw = walnats.middlewares.ErrorThresholdMiddleware(mw, actor_failures=100)
    await asyncio.gather(
        run_actor(explode, ['hi'] * 10, emw),
        run_actor(explode, ['hi'] * 30, emw),
    )
    assert Counter(mw.hist) == Counter(dict(on_start=40, on_failure=19))

async def test_ErrorThresholdMiddleware__on_success() -> None:
    mw = MockMiddleware()
    await run_actor(noop, 'hi', walnats.middlewares.ErrorThresholdMiddleware(mw))
    assert mw.hist == ['on_start', 'on_success']

@pytest.mark.skipif(CI, reason='the test fails on CI, see PR#2')
async def test_FrequencyMiddleware() -> None:
    switch = False

    async def handler(msg: str) -> None:
        nonlocal switch
        switch = not switch
        if switch:
            1 / 0

    mw = MockMiddleware()
    await run_actor(
        handler, ['hi'] * 40,
        walnats.middlewares.FrequencyMiddleware(mw),
    )
    assert Counter(mw.hist) == Counter(dict(on_success=1, on_failure=1, on_start=1))

async def test_StatsdMiddleware(udp_server: UDPLogProtocol) -> None:
    from datadog.dogstatsd import DogStatsd

    switch = False
    received = []

    async def handler(msg: str) -> None:
        nonlocal switch
        switch = not switch
        received.append(msg)
        if switch:
            1 / 0

    client = DogStatsd(port=udp_server.port, disable_telemetry=True)
    await run_actor(
        handler, ['hi'] * 40,
        walnats.middlewares.StatsdMiddleware(client),
    )
    assert len(received) == 40
    client.flush()
    await asyncio.sleep(.1)
    # remove numbers from `duration` metric, so it can be aggregated
    hist = [h.split(':0.')[0] for h in udp_server.hist]
    expected = [
        (r'walnats\..+\..+\.started:1\|c', 40),
        (r'walnats\..+\..+\.failed:1\|c', 20),
        (r'walnats\..+\..+\.processed:1\|c', 20),
        (r'walnats\..+\..+\.duration', 20),
    ]
    fuzzy_match_counter(hist, expected)

async def test_PrometheusMiddleware() -> None:
    switch = False

    async def handler(msg: str) -> None:
        nonlocal switch
        switch = not switch
        if switch:
            1 / 0

    await run_actor(
        handler, ['hi'] * 40,
        walnats.middlewares.PrometheusMiddleware(),
    )

async def test_SentryMiddleware__smoke() -> None:
    await run_actor(explode, 'hi', walnats.middlewares.SentryMiddleware())

@pytest.mark.skipif(not SENTRY_DSN, reason='SENTRY_DSN env var is not provided')
async def test_SentryMiddleware__real_sentry() -> None:
    with sentry_sdk.init(SENTRY_DSN):
        await run_actor(explode, 'hi', walnats.middlewares.SentryMiddleware())
        sentry_sdk.flush()

async def test_ZipkinMiddleware() -> None:
    endpoint = aiozipkin.create_endpoint('test_service')
    transport = StubTransport()
    async with aiozipkin.create_custom(endpoint, transport) as tracer:

        # emit a trace for normal operation
        await run_actor(noop, 'hi', walnats.middlewares.ZipkinMiddleware(tracer))
        assert len(transport.records) == 1
        r = transport.records[-1]
        tags = r.asdict()['tags']
        assert set(tags) == {'event'}

        # include exception on failure
        await run_actor(explode, 'hi', walnats.middlewares.ZipkinMiddleware(tracer))
        assert len(transport.records) == 2
        r = transport.records[-1]
        tags = r.asdict()['tags']
        assert set(tags) == {'event', 'error'}
        assert tags['error'] == 'division by zero'

        # use provided trace_id if available
        await run_actor(
            noop, 'hi', walnats.middlewares.ZipkinMiddleware(tracer),
            trace_id='123',
        )
        assert len(transport.records) == 3
        r = transport.records[-1]
        tags = r.asdict()['tags']
        assert set(tags) == {'event'}
        assert r.asdict()['traceId'] == '123'

        for r in transport.records:
            assert r.asdict()['kind'] == aiozipkin.CONSUMER

async def test_CurrentContextMiddleware() -> None:
    mw = walnats.middlewares.CurrentContextMiddleware()
    received = []

    async def handler(msg: str) -> None:
        assert msg == mw.context.message
        received.append(msg)

    await run_actor(handler, [f'{i}' for i in range(40)], mw)
    assert len(received) == 40

async def test_OpenTelemetryTraceMiddleware__smoke() -> None:
    import opentelemetry.trace

    tracer = opentelemetry.trace.get_tracer('tests')
    await run_actor(
        explode, 'hi',
        walnats.middlewares.OpenTelemetryTraceMiddleware(tracer),
    )

    tracer = opentelemetry.trace.get_tracer('tests')
    await run_actor(
        noop, 'hi',
        walnats.middlewares.OpenTelemetryTraceMiddleware(tracer),
    )


# Given an m x n board of characters and a list of strings words, return all
# words on the board.

# Each word must be constructed from letters of sequentially adjacent cells,
# where adjacent cells are horizontally or vertically neighboring. The same
# letter cell may not be used more than once in a word.

class TrieNode:
    def __init__(self):
        self.characters = dict()
        self.end = False
        self.word = ''

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        # Initialize trie.
        root = TrieNode()
        for word in words:
            curr = root
            for c in word:
                if c not in curr.characters:
                    curr.characters[c] = TrieNode()
                curr = curr.characters[c]
            curr.end = True
            curr.word = word

        m, n = len(board), len(board[0])
        directions = [[1, 0], [-1, 0], [0, 1], [0, -1]]
        result = []
        def dfs(r, c, curr):
            if curr.end:
                result.append(curr.word)
                curr.end = False
            if r < 0 or c < 0 or r >= m or c >= n:
                return
            if board[r][c] in curr.characters:
                temp = board[r][c]
                board[r][c] = '#' # To avoid cycles.
                for dr, dc in directions:
                    dfs(r + dr, c + dc, curr.characters[temp])
                board[r][c] = temp

        for r in range(m):
            for c in range(n):
                dfs(r, c, root)

        return result


'''
Author: Wenhan Shang
Email: whu_swh@whu.edu.cn
Date: 2022-01-05 13:45:20
Description: Waiting to add
FilePath: /PyScripts/AnalysisCodeSizeLog.py
'''
import os
import sys
import re

CSVFileList = ["AbstractedRegions", "IntersectRegionClosures", "SameRegions", "SimilarRegions"]
CSVPostFix = ".csv"

def writeFileWithContent(filePath, content):
    file = open(filePath, mode="w", encoding="utf-8")
    file.write(content)

def getResultDirPath(sourceFile):
    return sourceFile.replace("."+sourceFile.split(".")[-1], "") + "/"

def convertToCSVFiles(sourceFile):
    print("Converting the log file [{}] to some CSV files.".format(sourceFile))
    logFile = open(sourceFile)
    fileStr = logFile.read()
    fileStr = re.sub("[\n]+", "\n", fileStr)
    regx = "######[ |A-Z|a-z|0-9]* Begin ######\n|######[ |A-Z|a-z|0-9]* End ######\n"
    splitStr = re.split(regx, fileStr)
    csvStr = [ x for x in splitStr if x and x != "\n" and not x.startswith("######")]
    findedStr = re.findall(regx, fileStr)
    if len(findedStr)==2*len(csvStr):
        csvDirPath = getResultDirPath(sourceFile=sourceFile)
        os.makedirs(csvDirPath, exist_ok=True)
        for i in range(len(csvStr)):
            fileName = re.sub("\s+", "", re.sub("######| Begin ######| End ######","",findedStr[2*i])) + CSVPostFix
            csvFile = csvDirPath + fileName
            writeFileWithContent(filePath=csvFile, content=csvStr[i])

def synthesizeAllInfo():

    pass

def printUsage():
    print("USAGE:\tpython AnalysisCodeSizeLog.py OPTIONS param...\n\n" +
            "OPTIONS:\n"+
            "\t-csv\t\tConvert the log file specified by first param to some CSV files.\n" +
            "\t-h\t\tPrint usage of this file.\n")

if __name__=="__main__":
    if len(sys.argv) < 2:
        print("ERROR:\tparams not enough\n")
        printUsage()
        exit()
    option = sys.argv[1]
    if option.__eq__("-h"):
        printUsage()
        exit()
    if option.__eq__("-csv"):
        sourceFile = sys.argv[2]
        if not os.path.exists(sourceFile):
            print("ERROR:\tSpecified file does not exist!\n")
            printUsage()
            exit()
        convertToCSVFiles(sourceFile)


import datahandler

id = None
title = None
authors = []
year = None

def addAnArticle():
    global id, title, authors, year
    notExit = True
    while(notExit):
        print("Please enter a unique id or enter nothing to exit.")
        id = input("> ").strip()
        if(id == ""):
            notExit = False
            continue
        if(not datahandler.checkId(id)):
            print("Id already exists :(")
        break

    while(notExit):
        print("Please enter title or enter nothing to exit.")
        title = input("> ").strip()
        if(title == ""):
            notExit = False
            continue
        break

    count = 0
    while(notExit):
        prompt = """
        Please enter one of the authors' names.
        Enter nothing after all authors have been entered.
        """
        print(prompt)

        author = input("> ").strip()
        if((author == "") and (count > 0)):
            break
        elif(author == ""):
            print("Please enter at least one author name.")
            continue
        authors += [author]
        count += 1

    while(notExit):
        print("Please enter a year or enter nothing to exit.")
        year = input("> ").strip()
        if(year == ""):
            notExit = False
            continue
        try:
            intYear = int(year)
        except ValueError:
            print("invalid input. Please enter an integer number")
            continue
        break

    if(notExit):
        datahandler.addArticle(id, title, authors, year)
        print("Add successful :D")
        input("Enter any key to continue")


def instantiate(repo, name=None, filename=None):
    """
    Instantiate the generator and filename specification
    """

    default_transformers = repo.options.get('transformer', {})

    # If a name is specified, then lookup the options from dgit.json
    # if specfied. Otherwise it is initialized to an empty list of
    # files.
    transformers = {}
    if name is not None:
        # Handle the case generator is specified..
        if name in default_transformers:
            transformers = {
                name : default_transformers[name]
            }
        else:
            transformers = {
                name : {
                    'files': [],
                }
            }
    else:
        transformers = default_transformers

    #=========================================
    # Map the filename patterns to list of files
    #=========================================
    # Instantiate the files from the patterns specified
    input_matching_files = None
    if filename is not None:
        input_matching_files = repo.find_matching_files([filename])

    for t in transformers:
        for k in transformers[t]:
            if "files" not in k:
                continue
            if k == "files" and input_matching_files is not None:
                # Use the files specified on the command line..
                transformers[t][k] = input_matching_files
            else:
                # Try to match the specification
                if transformers[t][k] is None or len(transformers[t][k]) == 0:
                    transformers[t][k] = []
                else:
                    matching_files = repo.find_matching_files(transformers[t][k])
                    transformers[t][k] = matching_files

    return transformers