import socket
import threading

server_port = 9015

clients = []

def listen_to_client(client_id):

    client = clients[client_id]

    while True:
        prefix_msg = 'Friend ' + str(client_id) + ": "
        rcv_msg = client.recv(1024).decode('utf-8')
        msg = prefix_msg + rcv_msg
        for c in clients:
            if c == client:
                continue
            c.send(msg.encode('utf-8'))

def listen_to_client_UDP(UDP_socket):

    while True:
        msg, client = UDP_socket.recvfrom(1024)
        for c in clients:
            if c == client:
                continue
            c.send(msg)

def do_server_stuff():

    TCP_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    TCP_socket.bind(('localhost', server_port))
    TCP_socket.listen()

    UDP_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    UDP_socket.bind(('localhost', server_port))

    udp_thread = threading.Thread(target=listen_to_client_UDP, args=(UDP_socket,))
    udp_thread.start()
    client_id = 0

    print("Chat™ is readyy")
    while True:

        just_connected_client, addr = TCP_socket.accept()
        clients.append(just_connected_client)

        thread = threading.Thread(target=listen_to_client, args=(client_id,))
        client_id += 1
        thread.start()

if __name__ == '__main__':
    do_server_stuff()


'''
Faça um programa que mostre a tabuada de varios numeros
um de cada vez, para cada valor digitado pelo usuario
O programa será interrompido quando o número solicitado for negativo
'''

numero = 0
while True:
    print(f'==========-TABUADA-===========')
    numero = int(input('Diga um número: '))
    print(f'==============================')
    if numero < 0:
        break
    for i in range(1, 11):
        print(f'{numero} x {i} = {i*numero}')
    print(f'\n')


import argparse
import datetime
import json
import multiprocessing
import os
import subprocess
import sys
import tempfile
import time
import urlparse

from distutils import version

import requests
import yaml

from cloudlib import logger

PYTHON_PACKAGES = {
    'base_release': dict(),
    'known_release': dict(),
    'from_git': dict(),
    'required_packages': dict(),
    'built_files': list()
}

GIT_REPOS = []

GIT_REQUIREMENTS_MAP = {
    'github.com': 'https://raw.githubusercontent.com/%(path)s/%(branch)s'
                  '/%(file)s',
    'openstack.org': 'https://git.openstack.org/cgit/%(path)s/plain'
                     '/%(file)s?id=%(branch)s'
}

VERSION_DESCRIPTORS = [
    '>=', '<=', '==', '!=', '<', '>'
]

# Defines constant for use later.
LOG = None

class IndicatorThread(object):
    """Creates a visual indicator while normally performing actions."""

    def __init__(self, note=None, system=True, debug=False, quiet=False):
        """System Operations Available on Load."""

        self.quiet = quiet
        self.debug = debug
        self.system = system
        self.note = note
        if self.note is None:
            self.note = 'Please Wait... '
        self.job = None

    def __enter__(self):
        if all([self.debug is False, self.quiet is False]):
            return self.indicator_thread()

    def __exit__(self, exc_type, exc_val, exc_tb):
        self.system = False
        if all([self.debug is False, self.quiet is False]):
            print('Done.')
            self.job.terminate()

    def indicator(self):
        """Produce the spinner."""

        while self.system:
            busy_chars = ['|', '/', '-', '\\']
            for bc in busy_chars:
                note = self.note
                sys.stdout.write('\rProcessing - [ %s ] - %s' % (bc, note))
                sys.stdout.flush()
                time.sleep(.1)
                self.system = self.system

    def indicator_thread(self):
        """indicate that we are performing work in a thread."""

        self.job = multiprocessing.Process(target=self.indicator)
        self.job.start()
        return self.job

class LoggerWriter(object):
    @property
    def fileno(self):
        return LOG.handlers[0].stream.fileno

def get_file_names(path, ext=None):
    """Return a list of all files in the vars/repo_packages directory.

    :param path: ``str``  $PATH to search for files
    :param ext: ``str`` extension filter for specific files.
    """

    paths = os.walk(os.path.abspath(path))
    files = list()
    for fpath, _, afiles in paths:
        for afile in afiles:
            if ext is not None:
                if afile.endswith(ext):
                    files.append(os.path.join(fpath, afile))
            else:
                files.append(os.path.join(fpath, afile))
    else:
        return files

def requirements_parse(pkgs):
    """Parse all requirements.

    :param pkgs: ``list`` list of all requirements to parse.
    """
    for pkg in pkgs:
        LOG.debug('Parsing python dependencies: %s', pkg)
        if '==' in pkg:
            required_packages = PYTHON_PACKAGES['required_packages']
            pkg_name = '-'.join(pkg.split('=='))
            if pkg_name not in required_packages:
                required_packages[pkg_name] = pkg

        split_pkg = pkg.split(',')
        for version_descriptor in VERSION_DESCRIPTORS:
            if version_descriptor in split_pkg[0]:
                name, ver = split_pkg[0].split(version_descriptor)
                ver = '%s%s' % (version_descriptor, ver)
                if len(split_pkg) > 1:
                    versions = split_pkg[1:]
                    versions.insert(0, ver)
                else:
                    versions = [ver]

                break
        else:
            name = split_pkg[0]
            versions = None

        base_release = PYTHON_PACKAGES['base_release']
        if name in base_release:
            saved_versions = base_release[name]
            if versions is not None:
                if '==' in versions:
                    _lv = version.LooseVersion
                    if _lv(versions) < _lv(saved_versions):
                        versions = saved_versions
                        LOG.debug(
                            'New version found for replacement: [ %s ]',
                            versions
                        )

        if isinstance(versions, list):
            base_release[name.lower()] = '%s%s' % (name, ','.join(versions))
        elif versions is not None:
            base_release[name.lower()] = '%s%s' % (name, versions)
        else:
            base_release[name.lower()] = name

def package_dict(var_file):
    """Process variable file for Python requirements.

    :param var_file: ``str`` path to yaml file.
    """
    LOG.debug('Opening [ %s ]', var_file)
    with open(var_file, 'rb') as f:
        package_vars = yaml.safe_load(f.read())

    pip_pkgs = package_vars.get('service_pip_dependencies')
    if pip_pkgs:
        requirements_parse(pkgs=pip_pkgs)

    git_repo = package_vars.get('git_repo')
    if git_repo:
        if git_repo not in GIT_REPOS:
            GIT_REPOS.append(git_repo)

        LOG.debug('Building git type package [ %s ]', git_repo)
        git_url = urlparse.urlsplit(git_repo)
        repo_name = os.path.basename(git_url.path)
        repo = PYTHON_PACKAGES['from_git'][repo_name] = {}
        repo['branch'] = package_vars.get('git_install_branch', 'master')
        repo['full_url'] = git_repo
        repo['project'] = repo_name

        setup_file = None
        for k, v in GIT_REQUIREMENTS_MAP.iteritems():
            if k in git_repo:
                requirements_request = v % {
                    'path': git_url.path.lstrip('/'),
                    'file': package_vars.get(
                        'requirements_file', 'requirements.txt'
                    ),
                    'branch': repo['branch']
                }
                req = requests.get(requirements_request)
                if req.status_code == 200:
                    requirements = [
                        i.split()[0] for i in req.text.splitlines()
                        if i
                        if not i.startswith('#')
                    ]
                    repo['requirements'] = requirements
                    requirements_parse(pkgs=requirements)

                setup_request = v % {
                    'path': git_url.path.lstrip('/'),
                    'file': 'setup.py',
                    'branch': repo['branch']
                }
                setup = requests.head(setup_request)
                if setup.status_code == 200:
                    setup_file = True
                break

        git_req = 'git+%s@%s'
        known_release = PYTHON_PACKAGES['known_release']
        if setup_file is True:
            known_release[repo_name] = git_req % (
                repo['full_url'], repo['branch']
            )

        git_repo_plugins = package_vars.get('git_repo_plugins')
        if git_repo_plugins:
            for grp in git_repo_plugins:
                LOG.debug(
                    'Building git type package with plugins [ %s ]',
                    git_repo_plugins
                )
                plugin = '%s/%s' % (
                    grp['path'].strip('/'),
                    grp['package'].lstrip('/')
                )
                known_release[grp['package']] = git_req % (
                    git_url.geturl(),
                    '%s#egg=%s&subdirectory=%s' % (
                        repo['branch'],
                        grp['package'].strip('/'),
                        plugin
                    )
                )

def retryloop(attempts, timeout=None, delay=None, backoff=1, obj=None):
    """Enter the amount of retries you want to perform.

    The timeout allows the application to quit on "X".
    delay allows the loop to wait on fail. Useful for making REST calls.

    Example:
        Function for retring an action.
        for retry in retryloop(attempts=10, timeout=30, delay=1, backoff=1):
            something
            if somecondition:
                retry()

    :param attempts:
    :param timeout:
    :param delay:
    :param backoff:
    """

    starttime = time.time()
    success = set()
    for _ in range(attempts):
        success.add(True)
        yield success.clear
        if success:
            return
        duration = time.time() - starttime
        if timeout is not None and duration > timeout:
            break
        if delay:
            time.sleep(delay)
            delay *= backoff

    error = (
        'RetryError: FAILED TO PROCESS [ %s ] after [ %s ] Attempts' % (
            obj,
            attempts
        )
    )
    _error_handler(msg=error)

def build_wheel(wheel_dir, build_dir, link_dir, dist=None, pkg_name=None,
                make_opts=None):
    """Execute python wheel build command.

    :param wheel_dir: ``str`` $PATH to local save directory
    :param build_dir: ``str`` $PATH to temp build directory
    :param dist: ``str`` $PATH to requirements file
    :param pkg_name: ``str`` name of package to build
    """
    command = [
        'pip',
        'wheel',
        '--find-links',
        link_dir,
        '--timeout',
        '120',
        '--wheel-dir',
        wheel_dir,
        '--allow-all-external',
        '--build',
        build_dir
    ]

    if make_opts is not None:
        for make_opt in make_opts:
            command.append(make_opt)

    if dist is not None:
        command.extend(['--requirement', dist])
    elif pkg_name is not None:
        command.append(pkg_name)
    else:
        raise SyntaxError('neither "dist" or "pkg_name" was specified')

    build_command = ' '.join(command)
    LOG.info('Command: %s' % build_command)
    output, unused_err = None, None
    for retry in retryloop(3, obj=build_command, delay=2, backoff=1):
        try:
            process = subprocess.Popen(
                command,
                stdout=subprocess.PIPE,
                stderr=LoggerWriter()
            )
            output, unused_err = process.communicate()
            retcode = process.poll()

            LOG.info('Command return code: [ %s ]', retcode)
            if retcode:
                raise subprocess.CalledProcessError(
                    retcode, build_command, output=output
                )
        except subprocess.CalledProcessError as exp:
            LOG.warn(
                'Process failure. stderr: [ %s ], stdout [ %s ], Exception'
                ' [ %s ]. Removing build directory for retry. Check log for'
                ' more details.',
                unused_err,
                output,
                str(exp)
            )
            remove_dirs(directory=build_dir)
            retry()
        finally:
            # Ensure the build directories are clean
            remove_dirs(directory=build_dir)

def remove_dirs(directory):
    """Delete a directory recursively.

    :param directory: ``str`` $PATH to directory.
    """
    LOG.info('Removing directory [ %s ]', directory)
    for file_name in get_file_names(path=directory):
        LOG.debug('Removing file [ %s ]', file_name)
        os.remove(file_name)

    dir_names = []
    for dir_name, _, _ in os.walk(directory):
        dir_names.append(dir_name)

    dir_names = sorted(dir_names, reverse=True)
    for dir_name in dir_names:
        try:
            LOG.debug('Removing subdirectory [ %s ]', dir_name)
            os.removedirs(dir_name)
        except OSError:
            pass

def copy_file(src, dst):
    LOG.debug('Copying [ %s ] -> [ %s ]', src, dst)
    with open(src, 'rb') as open_src:
        with open(dst, 'wb') as open_dst:
            while True:
                buf = open_src.read(24 * 1024)
                if not buf:
                    break
                else:
                    open_dst.write(buf)

def _requirements_maker(name, wheel_dir, release, build_dir, make_opts,
                        link_dir=None, iterate=False):

    if link_dir is None:
        link_dir = wheel_dir

    if iterate is True:
        for pkg in sorted(release.values()):
            build_wheel(
                wheel_dir=wheel_dir,
                build_dir=build_dir,
                link_dir=link_dir,
                pkg_name=pkg,
                make_opts=make_opts
            )
    else:
        requirements_file_lines = []
        for value in sorted(set(release.values())):
            requirements_file_lines.append('%s\n' % value)

        requirements_file = os.path.join(wheel_dir, name)
        with open(requirements_file, 'wb') as f:
            f.writelines(requirements_file_lines)

        build_wheel(
            wheel_dir=wheel_dir,
            build_dir=build_dir,
            link_dir=link_dir,
            dist=requirements_file,
            make_opts=make_opts
        )

def _make_wheels(wheel_dir, build_dir, temp_store_dir):
    LOG.info('Building base packages')
    _requirements_maker(
        name='rpc_base_requirements.txt',
        wheel_dir=temp_store_dir,
        release=PYTHON_PACKAGES['base_release'],
        build_dir=build_dir,
        make_opts=None,
        link_dir=wheel_dir
    )

    LOG.info('Building known absolute packages')
    _requirements_maker(
        name='rpc_known_requirements.txt',
        wheel_dir=temp_store_dir,
        release=PYTHON_PACKAGES['known_release'],
        build_dir=build_dir,
        make_opts=['--no-deps'],
        link_dir=wheel_dir
    )

    LOG.info('Building required packages')
    _requirements_maker(
        name='rpc_required_requirements.txt',
        wheel_dir=temp_store_dir,
        release=PYTHON_PACKAGES['required_packages'],
        build_dir=build_dir,
        make_opts=None,
        link_dir=wheel_dir,
        iterate=True
    )

    built_wheels = get_file_names(temp_store_dir)
    PYTHON_PACKAGES['built_files'] = [
        os.path.basename(i) for i in built_wheels
    ]

    LOG.info('Moving built packages into place')
    for built_wheel in built_wheels:
        wheel_file = os.path.join(wheel_dir, os.path.basename(built_wheel))
        if os.path.exists(wheel_file):
            if os.path.getsize(wheel_file) != os.path.getsize(built_wheel):
                copy_file(src=built_wheel, dst=wheel_file)
        else:
            copy_file(src=built_wheel, dst=wheel_file)

def make_wheels(wheel_dir, build_dir):
    """Build wheels of all installed packages that don't already have one.

    :param wheel_dir: ``str`` $PATH to local save directory
    :param build_dir: ``str`` $PATH to temp build directory
    """

    temp_store_dir = os.path.join(
        tempfile.mkdtemp(prefix='rpc_wheels_temp_storage')
    )
    _mkdirs(path=temp_store_dir)
    try:
        _make_wheels(
            wheel_dir=wheel_dir,
            build_dir=build_dir,
            temp_store_dir=temp_store_dir
        )
    finally:
        remove_dirs(directory=temp_store_dir)
        remove_dirs(
            directory=os.path.join(
                tempfile.gettempdir(),
                'pip_build_root'
            )
        )

def ensure_consistency():
    """Iterate through the known data set and remove duplicates."""

    LOG.info('Ensuring the package list is consistent')
    for key in PYTHON_PACKAGES['known_release'].keys():
        PYTHON_PACKAGES['base_release'].pop(key, None)

def new_setup(user_args, input_path):
    """Discover all yaml files in the input directory."""

    LOG.info('Discovering input file(s)')
    var_files = None
    if os.path.isdir(user_args['input']):
        var_files = get_file_names(path=input_path, ext='.yml')
    else:
        if not input_path.endswith(('.yml', '.yaml')):
            error = (
                'The file you specified, [ %s ] does not have a valid yaml'
                ' extension. Please check your file and try again.'
                % input_path
            )
            _error_handler(msg=error)
        else:
            var_files = [input_path]

    # Populate the package dict
    LOG.info('Building the package list')
    for var_file in var_files:
        package_dict(var_file=var_file)

    # Ensure no general packages take precedence over the explicit ones
    ensure_consistency()

def _error_handler(msg, system_exit=True):
    """Handle and error logging and exit the application if needed.

    :param msg: ``str`` message to log
    :param system_exit: ``bol`` if true the system will exit with an error.
    """
    LOG.error(msg)
    if system_exit is True:
        raise SystemExit(msg)

def _user_args():
    """Setup argument Parsing."""

    parser = argparse.ArgumentParser(
        usage='%(prog)s',
        description='Rackspace Openstack, Python wheel builder',
        epilog='Python package builder Licensed "Apache 2.0"'
    )
    file_input = parser.add_mutually_exclusive_group(required=True)
    file_input.add_argument(
        '-i',
        '--input',
        help='Path to the directory where the repo_packages/ file or filess'
             ' are. This can be set to a directory or a file. If the path is'
             ' a directory all .yml files will be scanned for python packages'
             ' and git repositories.',
        default=None
    )
    file_input.add_argument(
        '--pre-input',
        help='Path to a already built json file which contains the python'
             ' packages and git repositories required.',
        default=None
    )
    parser.add_argument(
        '-o',
        '--output',
        help='Path to the location where the built Python package files will'
             ' be stored.',
        required=True,
        default=None
    )
    parser.add_argument(
        '-g',
        '--git-repos',
        help='Path to where to store all of the git repositories.',
        required=False,
        default=None
    )
    parser.add_argument(
        '--build-dir',
        help='Path to temporary build directory. If unset a auto generated'
             ' temporary directory will be used.',
        required=False,
        default=None
    )
    parser.add_argument(
        '--link-dir',
        help='Path to the build links for all built wheels.',
        required=False,
        default=None
    )
    parser.add_argument(
        '-r',
        '--release',
        help='Name of the release. Used for generating the json report.',
        required=True,
        default=None
    )
    opts = parser.add_mutually_exclusive_group()
    opts.add_argument(
        '--debug',
        help='Enable debug mode',
        action='store_true',
        default=False
    )
    opts.add_argument(
        '--quiet',
        help='Enables quiet mode, this disables all stdout',
        action='store_true',
        default=False
    )

    return vars(parser.parse_args())

def _get_abs_path(path):
    """Return the absolute path for a given path.

    :param path: ``str``  $PATH to be created
    :returns: ``str``
    """
    return os.path.abspath(
        os.path.expanduser(
            path
        )
    )

def _mkdirs(path):
    """Create a directory.

    :param path: ``str``  $PATH to be created
    """
    if not os.path.exists(path):
        LOG.info('Creating directory [ %s ]' % path)
        os.makedirs(path)
    else:
        if not os.path.isdir(path):
            error = (
                'Path [ %s ] can not be created, it exists and is not already'
                ' a directory.' % path
            )
            _error_handler(msg=error)

def _store_git_repos(git_repos_path):
    """Clone and or update all git repos.

    :param git_repos_path: ``str`` Path to where to store the git repos
    """
    _mkdirs(git_repos_path)
    for retry in retryloop(3, delay=2, backoff=1):
        for git_repo in GIT_REPOS:
            repo_name = os.path.basename(git_repo)
            if repo_name.endswith('.git'):
                repo_name = repo_name.rstrip('git')

            repo_path_name = os.path.join(git_repos_path, repo_name)
            if os.path.isdir(repo_path_name):
                os.chdir(repo_path_name)
                LOG.debug('Updating git repo [ %s ]', repo_path_name)
                commands = [
                    ['git', 'fetch', '-p', 'origin'],
                    ['git', 'pull']
                ]
            else:
                LOG.debug('Cloning into git repo [ %s ]', repo_path_name)
                commands = [
                    ['git', 'clone', git_repo, repo_path_name]
                ]

            for command in commands:
                try:
                    ret_data = subprocess.check_call(
                        command,
                        stdout=LoggerWriter(),
                        stderr=LoggerWriter()
                    )
                    if ret_data:
                        raise subprocess.CalledProcessError(
                            ret_data, command
                        )
                except subprocess.CalledProcessError as exp:
                    LOG.warn('Process failure. Error: [ %s ]', str(exp))
                    retry()
                else:
                    LOG.debug('Command return code: [ %s ]', ret_data)

def main():
    """Run the main app.

    This application will create all Python wheel files from within an
    environment.  The purpose is to create pre-compiled python wheels from
    the RPC playbooks.
    """

    # Parse input arguments
    user_args = _user_args()

    # Load the logging
    _logging = logger.LogSetup(debug_logging=user_args['debug'])
    if user_args['quiet'] is True or user_args['debug'] is False:
        stream = False
    else:
        stream = True

    _logging.default_logger(
        name='rpc_wheel_builder',
        enable_stream=stream
    )

    global LOG
    LOG = logger.getLogger(name='rpc_wheel_builder')

    # Create the output path
    output_path = _get_abs_path(path=user_args['output'])
    LOG.info('Getting output path')
    _mkdirs(path=output_path)

    # Create the build path
    LOG.info('Getting build path')
    indicator_kwargs = {
        'debug': user_args['debug'],
        'quiet': user_args['quiet'],
        'note': 'Gather dependencies... '
    }
    with IndicatorThread(**indicator_kwargs):
        if user_args['build_dir'] is not None:
            build_path = _get_abs_path(path=user_args['build_dir'])
            _mkdirs(path=build_path)
        else:
            build_path = tempfile.mkdtemp(prefix='rpc_wheels_build_')
            pre_input = user_args['pre_input']
            if pre_input:
                pre_input_path = _get_abs_path(path=user_args['pre_input'])
                with open(pre_input_path, 'rb') as f:
                    global PYTHON_PACKAGES
                    PYTHON_PACKAGES = json.loads(f.read())
            else:
                # Get the input path
                LOG.info('Getting input path')
                new_setup(
                    user_args=user_args,
                    input_path=_get_abs_path(path=user_args['input'])
                )

    indicator_kwargs['note'] = 'Building wheels... '
    with IndicatorThread(**indicator_kwargs):
        # Create all of the python package wheels
        make_wheels(
            wheel_dir=output_path,
            build_dir=build_path
        )

    indicator_kwargs['note'] = 'Generating build log... '
    with IndicatorThread(**indicator_kwargs):
        # Get a timestamp and create a report file
        utctime = datetime.datetime.utcnow()
        utctime = utctime.strftime("%Y%m%d_%H%M%S")
        backup_name = '%s-build-report-%s.json' % (
            user_args['release'],
            utctime
        )
        output_report_file = os.path.join(
            output_path,
            'json-reports',
            backup_name
        )

        # Make the directory if needed
        _mkdirs(path=os.path.dirname(output_report_file))

        # Generate a timestamped report file
        LOG.info('Generating packaging report [ %s ]', output_report_file)
        with open(output_report_file, 'wb') as f:
            f.write(
                json.dumps(
                    PYTHON_PACKAGES,
                    indent=2,
                    sort_keys=True
                )
            )

    # If link_dir is defined create a link to all built wheels.
    links_path = user_args.get('link_dir')
    if links_path:
        indicator_kwargs['note'] = 'Creating file links... '
        with IndicatorThread(**indicator_kwargs):
            links_path = _get_abs_path(path=links_path)
            LOG.info('Creating Links at [ %s ]', links_path)
            _mkdirs(path=links_path)

            # Change working directory.
            os.chdir(links_path)

            # Create all the links
            for inode in PYTHON_PACKAGES['built_files']:
                try:
                    dest_link = os.path.join(links_path, inode)

                    # Remove the destination inode if it exists
                    if os.path.exists(dest_link):
                        os.remove(dest_link)

                    # Create the link using the relative path
                    os.symlink(os.path.relpath(
                        os.path.join(output_path, inode)), dest_link
                    )
                except OSError as exp:
                    LOG.warn(
                        'Error Creating Link: [ %s ] Error: [ %s ]',
                        inode,
                        exp
                    )
                else:
                    LOG.debug('Link Created: [ %s ]', dest_link)

    # if git_repos was defined save all of the sources to the defined location
    git_repos_path = user_args.get('git_repos')
    if git_repos_path:
        indicator_kwargs['note'] = 'Storing updated git sources...'
        with IndicatorThread(**indicator_kwargs):
            LOG.info('Updating git sources [ %s ]', links_path)
            _store_git_repos(_get_abs_path(path=git_repos_path))

if __name__ == "__main__":
    main()


import re

def has_SSL(ip):
    patternSB = "\[(\w*)\]"
    matchesSB = re.findall(patternSB, ip)
    patternNotSB = "(?:^|\])(\w+)(?:$|\[)"
    matchesNotSB = re.findall(patternNotSB, ip)
    for p in matchesNotSB:
        for i in range(len(p) - 2):
            if p[i] == p[i + 2] and p[i] != p[i + 1]:
                for q in matchesSB:
                    for j in range(len(q) - 2):
                        if q[j] == q[j + 2] and q[j] == p[i + 1] and q[j + 1] == p[i]:
                            return True
    return False

with open('input', 'r') as f:
    ips = [l.strip() for l in f]

ip_tls = [ip for ip in ips if has_SSL(ip)]
tls_count = len(ip_tls)
print(tls_count)


# Django
from django.db.models import Q
from django.contrib.auth.models import Permission, Group

# Local Django
from Core.variables import GROUP_DEFAULT, GROUP_PARK

def create_group(group_name, permissions):
    try:
        group = Group.objects.get(name=group_name)

        group.permissions.clear()
        group.permissions.add(*permissions)
    except Group.DoesNotExist:
        group = Group.objects.create(name=group_name)
        group.permissions.add(*permissions)

def default():
    user_permissions = [
        p for p in Permission.objects.filter(
            Q(content_type__app_label__in=['Users'])
            & Q(codename__icontains='User')
        )
    ]
    car_permissions = [
        p for p in Permission.objects.filter(
            Q(content_type__app_label__in=['Car'])
        )
    ]

    rezervation_permissions = [
        p for p in Permission.objects.filter(
            Q(content_type__app_label__in=['Rezervation'])
        )
    ]

    create_group(GROUP_DEFAULT, (user_permissions + car_permissions + rezervation_permissions))

def park():
    user_permissions = [
        p for p in Permission.objects.filter(
            Q(content_type__app_label__in=['Users'])
            & Q(codename__icontains='User')
        )
    ]
    park_permissions = [
        p for p in Permission.objects.filter(
            Q(content_type__app_label__in=['Park'])
        )
    ]
    create_group(GROUP_PARK, (user_permissions + park_permissions))


from typing import Any

from PyQt5.QtWidgets import (
    QWidget,
    QDoubleSpinBox,
    QCheckBox,
    QPushButton,
    QVBoxLayout,
    QHBoxLayout,
    QFormLayout,
    QGroupBox)

import pyqtgraph

from sipyco.pc_rpc import BestEffortClient

import logging
logger = logging.getLogger(__name__)

class InterferogramWidget(QWidget):
    """
    Widget which connects to a :class:`WavemeterServer` and displays its interferograms. Shows the plot as well as an
    optional integer control allowing updates to be limited to every nth call to :meth:`update_data` (or entirely
    disabled by entering negative values for n).

    :param rpc_host: host of the :class:`WavemeterServer`
    :param rpc_port: port of the :class:`WavemeterServer`
    :param rpc_target: :class:`WavemeterServer` target name
    :param channel_id: channel number for which to display the interferograms
    :param if0_pen: pen for the 1st interferogram
    :param if1_pen: pen for the 2nd interferogram
    :param bgr_color: background color
    :param update_interval: number of calls to :meth:`update_data` after which to actually update
    :param show_interval_spinbox: display a GUI element to change the `update_interval` during operation
    :param show_exposure_ctrl: show controls for wavemeter exposure times
    """
    def __init__(self, rpc_host: str = "::1", rpc_port: int = 3280, rpc_target: str = "wavemeter_server",
                 channel_id: int = 1, if0_pen: Any = pyqtgraph.mkPen(color="g", width=1),
                 if1_pen: Any = pyqtgraph.mkPen(color="b", width=1), bgr_color: str = "w",
                 update_interval: int = 1, show_interval_spinbox: bool = False, show_exposure_ctrl: bool = False,
                 **kwargs):
        super().__init__(**kwargs)
        self.if_plot = pyqtgraph.PlotWidget(background=bgr_color)

        self.if0_pen = if0_pen
        self.if1_pen = if1_pen

        self._rpc_client = BestEffortClient(rpc_host, rpc_port, rpc_target)
        self.channel_id = channel_id

        layout = QVBoxLayout()
        layout.addWidget(self.if_plot)

        self.update_interval_spinbox = QDoubleSpinBox()
        self.update_interval_spinbox.setDecimals(0)
        self.update_interval_spinbox.setRange(-1, 1e10)
        self.update_interval_spinbox.setValue(update_interval)
        if show_interval_spinbox:
            update_interval_layout = QFormLayout()
            update_interval_layout.addRow("# values between updates (-1 to disable)", self.update_interval_spinbox)
            update_int_gb = QGroupBox()
            update_int_gb.setLayout(update_interval_layout)
            layout.addWidget(update_int_gb)

        if show_exposure_ctrl:
            exposure_gb = QGroupBox("Exposure")
            exposure_gb_layout = QHBoxLayout()
            exposure_gb.setLayout(exposure_gb_layout)

            control_form = QFormLayout()

            self._exp_time1 = QDoubleSpinBox()
            self._exp_time1.setRange(1, 2000)
            self._exp_time1.setSingleStep(1)
            self._exp_time1.setDecimals(0)
            self._exp_time1.setSuffix(" ms")
            self._exp_time1.setValue(1)
            control_form.addRow("T1", self._exp_time1)

            exposure_gb_layout.addLayout(control_form)
            control_form2 = QFormLayout()

            self._exp_time2 = QDoubleSpinBox()
            self._exp_time2.setRange(0, 2000)
            self._exp_time2.setSingleStep(1)
            self._exp_time2.setDecimals(0)
            self._exp_time2.setSuffix(" ms")
            self._exp_time2.setValue(1)
            control_form2.addRow("T2", self._exp_time2)

            exposure_gb_layout.addLayout(control_form2)

            self._exp_auto = QCheckBox()
            self._exp_auto.setText("Auto adj.")
            exposure_gb_layout.addWidget(self._exp_auto)

            exposure_get_button = QPushButton("Get")

            def exposure_get():
                if self._rpc_client is not None:
                    logger.info("sending RPC get_exposure_time({}, 1)".format(self.channel_id))
                    time1 = self._rpc_client.get_exposure_time(self.channel_id, 1)
                    logger.info("sending RPC get_exposure_time({}, 2)".format(self.channel_id))
                    time2 = self._rpc_client.get_exposure_time(self.channel_id, 2)
                    logger.info("sending RPC get_exposure_auto_adjust({})".format(self.channel_id))
                    auto = self._rpc_client.get_exposure_auto_adjust(self.channel_id)
                    self._exp_time1.setValue(time1)
                    self._exp_time2.setValue(time2)
                    self._exp_auto.setChecked(auto)

            exposure_get_button.clicked.connect(exposure_get)

            exposure_gb_layout.addWidget(exposure_get_button)
            exposure_set_button = QPushButton("Set")

            def exposure_set():
                time1 = int(self._exp_time1.value())
                time2 = int(self._exp_time2.value())
                auto = bool(self._exp_auto.isChecked())
                if self._rpc_client is not None:
                    logger.info("sending RPC set_exposure_time({}, 1, {})".format(self.channel_id, time1))
                    self._rpc_client.set_exposure_time(self.channel_id, 1, time1)
                    logger.info("sending RPC set_exposure_time({}, 2, {})".format(self.channel_id, time2))
                    self._rpc_client.set_exposure_time(self.channel_id, 2, time2)
                    logger.info("sending RPC set_exposure_auto_adjust({}, {})".format(self.channel_id, auto))
                    self._rpc_client.set_exposure_auto_adjust(self.channel_id, auto)

            exposure_set_button.clicked.connect(exposure_set)

            exposure_gb_layout.addWidget(exposure_set_button)

            layout.addWidget(exposure_gb)
            exposure_get()

        self.setLayout(layout)
        self.value_counter = update_interval + 1

    def update_data(self):
        self.value_counter += 1
        if self.value_counter > self.update_interval_spinbox.value() > 0 and self._rpc_client is not None:
            if_0 = self._rpc_client.get_interferogram(self.channel_id, 0)
            if_1 = self._rpc_client.get_interferogram(self.channel_id, 1)
            if if_0 is not None and if_1 is not None:
                self.if_plot.clear()
                self.if_plot.plot(if_0, pen=self.if0_pen)
                self.if_plot.plot(if_1, pen=self.if1_pen)
            self.value_counter = 0


import turtle
from random import randint

my_turtle = turtle.Turtle()
turtle.colormode(255)
screen = turtle.Screen()
screen.title("Spirograph")

# Generating random rgb colors:
def rand_rgb():
    colors = (randint(0, 255), randint(0, 255), randint(0, 255))
    return colors

# Drawing the spirograph
def draw_spirograph(size_of_gap):
    for _ in range(int(360 / size_of_gap)):
        my_turtle.pencolor(rand_rgb())
        my_turtle.speed('fastest')
        my_turtle.circle(100)
        my_turtle.setheading(my_turtle.heading() + size_of_gap)

draw_spirograph(3)
screen.exitonclick()


from __future__ import division
from random import shuffle, choice, randint
from numpy import mean, log10, array, linspace, log10
from numpy.random import uniform, binomial
import  matplotlib.pyplot as plt
from scipy.stats.kde import gaussian_kde
import numpy as np

def get_kdens_choose_kernel(_list,kernel):
    """ Finds the kernel density function across a sample of SADs """
    density = gaussian_kde(_list)
    n = len(_list)
    #xs = np.linspace(0, 1, n)
    xs = linspace(min(_list), max(_list), n)
    density.covariance_factor = lambda : kernel
    density._compute_covariance()
    D = [xs,density(xs)]
    return D

nr = 20

es1 = []
es2 = []
es3 = []

for i in range(1000):

    ps = np.array(range(1, 21))/sum(range(1, 21))
    en = np.random.choice(range(1, 20+1), size = 1, replace=False, p = ps)
    es = 10**uniform(0, 3, en)
    es = es.tolist() + [0]*(nr-en)
    es = array(es)/sum(es)

    es1.append(np.var(es))

    ls = filter(lambda a: a != 0, es)
    es2.append(np.var(ls))
    es3.append(len(ls))

fig = plt.figure()

fig.add_subplot(2, 2, 1)
kernel = 0.5

D = get_kdens_choose_kernel(es1, kernel)
plt.plot(D[0],D[1],color = 'r', lw=1, alpha = 0.99, label= 'es1')

fig.add_subplot(2, 2, 2)

D = get_kdens_choose_kernel(es2, kernel)
plt.plot(D[0],D[1],color = 'b', lw=1, alpha = 0.99, label= 'es2')

fig.add_subplot(2, 2, 3)

D = get_kdens_choose_kernel(es3, kernel)
plt.plot(D[0],D[1],color = '0.3', lw=1, alpha = 0.99, label= 'es3')

plt.legend(loc=2, fontsize=10, frameon=False)
plt.show()


import re,os,sys
import xml.etree.ElementTree as ET
from rdkit import Chem
from rdkit.Chem import rdChemReactions as Reactions
from rdkit.Chem.Scaffolds.MurckoScaffold import *
srcPath = os.path.split(os.path.realpath(__file__))[0]
sys.path.insert(1, srcPath)
from SyntOn_Classifier import BBClassifier
from UsefulFunctions import *

def mainSynthonsGenerator(initSmi, keepPG=False, Classes=None, returnDict=False, returnBoolAndDict=False):
    solventsToIgnore = ["OC(=O)C(=O)O", "CC(=O)O", "OS(=O)(=O)O", "[O-]Cl(=O)(=O)=O", "OP(=O)(O)O", "OC(=O)C(F)(F)F",
                        "OS(=O)(=O)C(F)(F)F", "OC(=O)O", "[O-]S(=O)(=O)C(F)(F)F", "OC=O", "OC(=O)/C=C\C(=O)O", "[O-]C(=O)C(F)(F)F",
                        "OC(=O)/C=C/C(=O)O"]
    canonicalSmilesOfSolventsToIgnore = set([Chem.MolToSmiles(Chem.MolFromSmiles(x), canonical=True) for x in solventsToIgnore])
    initMol = readMol(initSmi)
    query = Chem.MolFromSmarts(
        "[#6]-[#6]-[#8]-[#6].[#6]-[#8]-[#6](-[#6])=O.[#6]-[#8]-[#6](-[#6])=O.[#6]-[#8]-[#6](-[#6])=O")
    if initMol == None or initMol.HasSubstructMatch(query):
        finalSynthon = {}
        azoles = False
    elif len(initSmi.split(".")) > 1: # case of input mixtures
        finalSynthon = {}
        azoles = False
        for smi in initSmi.split("."):
            mol = readMol(smi)
            if Chem.MolToSmiles(mol, canonical=True) not in canonicalSmilesOfSolventsToIgnore:
                nAzoles, nFinalSynthon = mainSynthonsGenerator(smi, keepPG, returnBoolAndDict=True)
                if nAzoles:
                    azoles = True
                if nFinalSynthon:
                    for newSynth in nFinalSynthon:
                        if newSynth not in finalSynthon:
                            finalSynthon[newSynth] = nFinalSynthon[newSynth].copy()
    else:
        if Classes == None:
            AllClasses = BBClassifier(mol=initMol)
            Classes = [clas for clas in AllClasses if "MedChemHighlights" not in clas and "DEL" not in clas]
        BBmarks = os.path.join(os.path.split(os.path.split(os.path.realpath(__file__))[0])[0], "config" , "BB_Marks.xml")
        tree = ET.parse(BBmarks)
        BB_Marks = tree.getroot()
        MarksSetup = __getReactionSMARTS(BB_Marks)
        polyfunc = False
        polyfuncName = []
        keepSynthonsWithPG = keepPG
        molsToWorkWith = {initSmi: set()}
        ind = 0
        finalSynthon = {}
        polyfuncInd = []
        synthonsAfterMonofuncClasses = {}
        for Cls in Classes:
            if "Bifunctional" in Cls or "Trifunctional" in Cls:
                polyfunc = True
                polyfuncName.append(Cls)
                if not ("Nboc" in Cls or "Ncbz" in Cls or "Nfmoc" in Cls  or "Ester" in Cls or "TFAc" in Cls):
                    keepSynthonsWithPG = True
                    break
        while ind < len(Classes):
            if "Bifunctional" in Classes[ind] or "Trifunctional" in Classes[ind]:
                polyfuncInd.append(ind)
                ind += 1
                continue
            else:
                if polyfunc and "Bifunctional" not in Classes[ind]:
                    ignoreThisClass = False
                    for subName in [y for x in polyfuncName for y in x.split("_")]:
                        if subName in Classes[ind] and "Bifunctional_NbnDi_Amines" not in polyfuncName:
                            ignoreThisClass = True
                            ind += 1
                            break
                    if ignoreThisClass:
                        continue
                for mol in molsToWorkWith:
                    synthons = __synthonsAssignement(Classes[ind], molsToWorkWith[mol], mol, MarksSetup, keepSynthonsWithPG)
                    if synthons:
                        for synth in synthons:
                            if synth not in synthonsAfterMonofuncClasses:
                                synthonsAfterMonofuncClasses[synth] = synthons[synth].copy()
                            else:
                                synthonsAfterMonofuncClasses[synth].update(synthons[synth])
                for synth in synthonsAfterMonofuncClasses:
                    if synth not in molsToWorkWith:
                        molsToWorkWith[synth] = synthonsAfterMonofuncClasses[synth].copy()
                    else:
                        molsToWorkWith[synth].update(synthonsAfterMonofuncClasses[synth])

            ind += 1
        if keepSynthonsWithPG or not polyfunc:
            if synthonsAfterMonofuncClasses:
                for synth in synthonsAfterMonofuncClasses:
                    if synth not in finalSynthon:
                        finalSynthon[synth] = synthonsAfterMonofuncClasses[synth].copy()
                    else:
                        finalSynthon[synth].update(synthonsAfterMonofuncClasses[synth])
        if polyfunc:
            for i, ind in enumerate(polyfuncInd):
                if i < len(polyfuncInd) - 1 :
                    extraMols = {}
                for mol in molsToWorkWith:
                    synthons = __synthonsAssignement(Classes[ind], molsToWorkWith[mol], mol, MarksSetup, keepSynthonsWithPG)
                    if synthons:
                        for synth in synthons:
                            if synth not in finalSynthon:
                                finalSynthon[synth] = synthons[synth].copy()
                            else:
                                finalSynthon[synth].update(synthons[synth])
                            if i < len(polyfuncInd) - 1 and synth not in molsToWorkWith:
                                if synth not in extraMols:
                                    extraMols[synth] = synthons[synth].copy()
                                else:
                                    extraMols[synth].update(synthons[synth])
                if i < len(polyfuncInd) - 1 and extraMols:
                    for synth in extraMols:
                        if synth not in molsToWorkWith:
                            molsToWorkWith[synth] = extraMols[synth].copy()
            """for mol in extraMols:
                for ind in polyfuncInd:
                    synthons = __synthonsAssignement(Classes[ind], extraMols[mol], mol, MarksSetup, keepSynthonsWithPG)
                    if synthons:
                        for synth in synthons:
                            if synth not in finalSynthon:
                                finalSynthon[synth] = synthons[synth].copy()"""

        for clas in Classes:
            if "Trifunctional" in clas and "Ester" in clas and "Acid" in clas:
                additionalSynthons = __generateBiacideSynthonForTrifunctional(finalSynthon, clas)
                if additionalSynthons:
                    for synth in additionalSynthons:
                        if synth not in finalSynthon:
                            finalSynthon[synth] = additionalSynthons[synth].copy()
                        else:
                            finalSynthon[synth].update(additionalSynthons[synth])

        additionalSynthons = __azolesSynthonPostGeneration(finalSynthon)
        if additionalSynthons:
            azoles = True
            for synth in additionalSynthons:
                if synth not in finalSynthon:
                    finalSynthon[synth] = additionalSynthons[synth].copy()
                else:
                    finalSynthon[synth].update(additionalSynthons[synth])
        else:
            azoles = False
        if not finalSynthon and "Esters_Esters" in Classes:
            ReactionLIST = "[C;$(C(=O)[#6]):1][O:2]>>*[C;+0:1]|[O;!R;$(O(C(=O)[#6])[CX4,c]):1][C;$(C(=O)):2]>>*[O;+0:1]"
            LabelList = "*C->C:10,*[13C]->13C:10,*[13CH]->13C:10|*O->O:20"
            finalSynthon = __NormalSynthonsGenerator(LabelList, ReactionLIST,
                                    set(), "Esters_Esters", initMol,
                                    func=1)
        if "Ketones_Ketones" in Classes:
            newSynthToAdd = {}
            for synthon in finalSynthon:
                if "Ketones_Ketones" in finalSynthon[synthon]:
                    synthMol = readMol(synthon)
                    newClasses = BBClassifier(mol=synthMol)
                    for cls in newClasses:
                        if "Alcohols" in cls:
                            nAzoles, nFinalSynthon = mainSynthonsGenerator( synthon, keepPG, [cls], returnBoolAndDict=True)
                            for newSynth in nFinalSynthon:
                                if newSynth not in finalSynthon and newSynth not in newSynthToAdd:
                                    newSynthToAdd[newSynth] = set()
                                    newSynthToAdd[newSynth].update(finalSynthon[synthon])
                                    newSynthToAdd[newSynth].update(nFinalSynthon[newSynth])
            if newSynthToAdd:
                for newSynth in newSynthToAdd:
                    finalSynthon[newSynth] = newSynthToAdd[newSynth]
    if returnDict:
        return finalSynthon
    elif returnBoolAndDict:
        return azoles, finalSynthon
    else:
        print("\n\n\n___________________________________________________________________________________________________")
        print("All generated synthons (" + str(len(finalSynthon)) +"): " + ".".join([x for x in finalSynthon]))
        print("Col1-Synton Col2-RespectiveBBsClass")
        for synth in finalSynthon:
            print(synth + " " + "+".join(finalSynthon[synth]))

def __synthonsAssignement(CurrentClass, PreviousClasses, molSmi, MarksSetup, keepSynthonsWithPG=True):
    additionalBifuncClasses = ["Aminoacids_N-AliphaticAmino_Acid", "Aminoacids_N-AromaticAmino_Acid", "Reagents_DiAmines"]

    PGBifunctional = ["Bifunctional_Acid_Ester","Bifunctional_Acid_Nitro", "Bifunctional_Aldehyde_Ester", "Bifunctional_Amine_Ester",
                      "Bifunctional_Ester_Isocyanates", "Bifunctional_Ester_SO2X", "Bifunctional_Aldehyde_Nitro",

                      "Bifunctional_NbocAmino_Acid", "Bifunctional_NcbzAmino_Acid", "Bifunctional_Isothiocyanates_Acid", "Bifunctional_NfmocAmino_Acid",
                      "Bifunctional_Aldehyde_Nboc", "Bifunctional_NTFAcAmino_Acid",

                      "Bifunctional_Boronics_Ncbz", "Bifunctional_Boronics_Nfmoc",

                      "Bifunctional_NbnDi_Amines", "Bifunctional_NbocDi_Amines", 'Bifunctional_NcbzDi_Amines', "Bifunctional_NfmocDi_Amines",
                      "Bifunctional_NTFAcDi_Amines", 'Bifunctional_Di_Amines_NotherCarbamates',

                      'Trifunctional_Acid_Aldehyde_Nitro', "Trifunctional_Acid_ArylHalide_Ester",
                      "Trifunctional_Acid_ArylHalide_Nitro", 'Trifunctional_Amines_ArylHalide_Nitro',
                      "Trifunctional_NbocAmino_Acid_AlkyneCH", "Trifunctional_NbocAmino_Acid_ArylHalide",
                      "Trifunctional_NfmocAmino_Acid_AlkyneCH", "Trifunctional_NfmocAmino_Acid_ArylHalide"]

    __FirstReactionAsPreparation = ["Bifunctional_Acid_Aldehyde", "Bifunctional_Aldehyde_ArylHalide",
                             "Bifunctional_Aldehyde_SO2X", "Bifunctional_Boronics_Acid",
                             "Bifunctional_Boronics_Aldehyde", "Bifunctional_Hydroxy_Aldehyde",

                             'Trifunctional_Acid_Aldehyde_ArylHalide', "Trifunctional_Acid_Aldehyde_Acetylenes",
                                  'Trifunctional_Acid_Aldehyde_Nitro', 'Trifunctional_Amines_ArylHalide_Nitro',
                                  "Trifunctional_NbocAmino_Acid_AlkyneCH", "Trifunctional_NfmocAmino_Acid_AlkyneCH",
                                  "Trifunctional_Di_Esters_Amino"]

    PolymerReagents = ["Reagents_PoliOxiranes", "Esters_PoliEsters", "Reagents_PoliIsocyanates", "SulfonylHalides_Poli_Sulfonylhalides"]

    trifuncClassesWithTwoPGs = ['Trifunctional_Acid_Ester_Nitro', "Trifunctional_NbocAmino_Acid_Ester",
                                    "Trifunctional_NbocAmino_Acid_Nitro", "Trifunctional_Amines_Nboc_Ester",
                                "Trifunctional_Nboc_NCbz_Amino_Acid", "Trifunctional_Nboc_Nfmoc_Amino_Acid",
                                "Trifunctional_NfmocAmino_Acid_Ester", "Trifunctional_NfmocAmino_Acid_Nitro",
                                "Trifunctional_Di_Esters_Amino"]

    if CurrentClass in trifuncClassesWithTwoPGs or "Trifunctional" in CurrentClass:
        func = 3
    elif "Bifunctional" in CurrentClass or CurrentClass in additionalBifuncClasses:
        func = 2
    else:
        func = 1
    if CurrentClass in __FirstReactionAsPreparation:
        firstReactionAsPrep = True
    else:
        firstReactionAsPrep=False
    if CurrentClass in trifuncClassesWithTwoPGs:
        twoPGs=True
    else:
        twoPGs=False
    labledSynthons = {}
    mol = readMol(molSmi)
    if CurrentClass in PolymerReagents:
        MolsToWorkWith = {Chem.MolToSmiles(mol, canonical=True): PreviousClasses}
        for i in range(len(MarksSetup[CurrentClass]['Labels'].split("|"))):
            synthons = __SynthonsGeneratorsForPolymerReagents(MarksSetup[CurrentClass]['Labels'].split("|")[i],
                                    MarksSetup[CurrentClass]['SMARTS'].split("|")[i], CurrentClass, MolsToWorkWith)
            if synthons:
                for synth in synthons:
                    if synth not in labledSynthons:
                        labledSynthons[synth] = synthons[synth].copy()
                    else:
                        labledSynthons[synth].update(synthons[synth])
        return labledSynthons
    elif CurrentClass in PGBifunctional or twoPGs:
        synthons = __ProtectiveGroupRemoval(MarksSetup[CurrentClass]['Labels'], MarksSetup[CurrentClass]['SMARTS'],
                                          mol, keepSynthonsWithPG,
                                          firstReactionAsPrep, func, PreviousClasses, CurrentClass, twoPGs)

    elif firstReactionAsPrep:
        synthons = __FirstReactionAsPrep(MarksSetup[CurrentClass]['Labels'], MarksSetup[CurrentClass]['SMARTS'],
                                        PreviousClasses, CurrentClass, mol, func)
    else:
        synthons = __NormalSynthonsGenerator(MarksSetup[CurrentClass]['Labels'], MarksSetup[CurrentClass]['SMARTS'],
                                           PreviousClasses, CurrentClass, mol,
                                                 func=func)
    if synthons:
        for synth in synthons:
            if synth not in labledSynthons:
                labledSynthons[synth] = synthons[synth].copy()
            else:
                labledSynthons[synth].update(synthons[synth])

    return labledSynthons

def __azolesSynthonPostGeneration(labledSynthons):
    pat = re.compile("\[\w*:\w*\]")
    Class = "nHAzoles_nHAzoles"
    additionalSynthons = {}
    maxMark = 0
    for molSmiles in labledSynthons:
        marksPrevious = [molSmiles[m.start():m.start() + 2] + molSmiles[m.end() - 4:m.end()] for m in
                         re.finditer(pat, molSmiles)]
        if len(marksPrevious)>maxMark:
            maxMark=len(marksPrevious)
    for molSmiles in labledSynthons:
        query = Chem.MolFromSmarts("[nHr5;!$(nc=O)]")
        mol = readMol(molSmiles)
        marksPrevious = [molSmiles[m.start():m.start() + 2] + molSmiles[m.end() - 4:m.end()] for m in re.finditer(pat, molSmiles)]
        if len(marksPrevious)==maxMark and mol.HasSubstructMatch(query):
            cuttingRule = Reactions.ReactionFromSmarts("[nH;r5:1]>>*[n:1]")
            label = "*n->n:20"
            products = cuttingRule.RunReactants((mol,))
            for productSet in products:
                for product in productSet:
                    labledSynthon = __getBBLabledSmiles(product, label)
                    if marksPrevious:
                        for synth in labledSynthon:
                            marksNew = [synth[m.start():m.start() + 2] + synth[m.end() - 4:m.end()] for m in
                                     re.finditer(pat, synth)]
                            if len(marksNew)>len(marksPrevious):
                                if synth not in additionalSynthons:
                                    additionalSynthons[synth] = labledSynthons[molSmiles].copy()
                                else:
                                    additionalSynthons[synth].update(labledSynthons[molSmiles])
                                additionalSynthons[synth].add(Class)
                    else:
                        for synth in labledSynthon:
                            if synth not in additionalSynthons:
                                additionalSynthons[synth] = labledSynthons[molSmiles].copy()
                            else:
                                additionalSynthons[synth].update(labledSynthons[molSmiles])
                            additionalSynthons[synth].add(Class)
    return additionalSynthons

def __generateBiacideSynthonForTrifunctional(labledSynthons, Class):
    additionalSynthons = {}
    for molSmiles in labledSynthons:
        query = Chem.MolFromSmarts(
            "[O;$(O=C([#6])[OD1])].[O;$(O([CH3])C([#6])=O),$(O([CH2][CH3])C([#6])=O),$(O([CH2]c1[cH][cH][cH][cH][cH]1)C([#6])=O),$(O(C([CH3])([CH3])[CH3])C([#6])=O),$(O([CH2][CH]=[CH2])C([#6])=O)]")
        mol = readMol(molSmiles)
        pat = re.compile("\[\w*:\w*\]")
        marksPrevious = [molSmiles[m.start():m.start() + 2] + molSmiles[m.end() - 4:m.end()] for m in re.finditer(pat, molSmiles)]
        if mol.HasSubstructMatch(query):
            cuttingRule = Reactions.ReactionFromSmarts(
                "[O;$(O(C)C([#6])=O):1][C;$([CH3]),$([CH2][CH3]),$([CH2]c1[cH][cH][cH][cH][cH]1),$(C([CH3])([CH3])[CH3]),$([CH2][CH]=[CH2]):2]>>[OH:1]")
            label = "No"
            products = cuttingRule.RunReactants((mol,))
            for productSet in products:
                for product in productSet:
                    labledSynthon = __getBBLabledSmiles(product, label)
                    if marksPrevious:
                        for synth in labledSynthon:
                            marksNew = [synth[m.start():m.start() + 2] + synth[m.end() - 4:m.end()] for m in
                                     re.finditer(pat, synth)]
                            if len(marksNew)>len(marksPrevious):
                                if synth not in additionalSynthons:
                                    additionalSynthons[synth] = labledSynthons[molSmiles].copy()
                                else:
                                    additionalSynthons[synth].update(labledSynthons[molSmiles])
                                additionalSynthons[synth].add(Class)
                    else:
                        for synth in labledSynthon:
                            if synth not in additionalSynthons:
                                additionalSynthons[synth] = labledSynthons[molSmiles].copy()
                            else:
                                additionalSynthons[synth].update(labledSynthons[molSmiles])
                            additionalSynthons[synth].add(Class)
    return additionalSynthons

def __SynthonsGeneratorsForPolymerReagents(Label, rule, Class, MolsToWorkWith, finalSynthons=None, firstLaunch=True, Deprotection=False):
    if finalSynthons==None:
        finalSynthons = {}
    newMolsToWorkWith = {}
    cuttingRule = Reactions.ReactionFromSmarts(rule)
    for molSmiles in MolsToWorkWith:
        pat = re.compile("\[\w*:\w*\]")
        marksPrevious = [molSmiles[m.start():m.start() + 2] + molSmiles[m.end() - 4:m.end()] for m in re.finditer(pat, molSmiles)]
        products = cuttingRule.RunReactants((Chem.MolFromSmiles(molSmiles),))
        if not products and not firstLaunch:
            if molSmiles not in finalSynthons:
                finalSynthons[molSmiles] = MolsToWorkWith[molSmiles].copy()
            else:
                finalSynthons[molSmiles].update(MolsToWorkWith[molSmiles])
            continue
        for productSet in products:
            for product in productSet:
                labledSynthons = __getBBLabledSmiles(product, Label)
                if marksPrevious and not Deprotection:
                    for synth in labledSynthons:
                        marksNew = [synth[m.start():m.start() + 2] + synth[m.end() - 4:m.end()] for m in
                                        re.finditer(pat, synth)]
                        if len(marksNew) > len(marksPrevious):
                            if synth not in newMolsToWorkWith:
                                newMolsToWorkWith[synth] = MolsToWorkWith[molSmiles].copy()
                            else:
                                newMolsToWorkWith[synth].update(MolsToWorkWith[molSmiles])
                                newMolsToWorkWith[synth].add(Class)
                else:
                    for synth in labledSynthons:
                        if synth not in newMolsToWorkWith:
                            newMolsToWorkWith[synth] = MolsToWorkWith[molSmiles].copy()
                        else:
                            newMolsToWorkWith[synth].update(MolsToWorkWith[molSmiles])
                            newMolsToWorkWith[synth].add(Class)
    if newMolsToWorkWith:
        __SynthonsGeneratorsForPolymerReagents(Label, rule, Class, newMolsToWorkWith, finalSynthons, firstLaunch=False,
                                             Deprotection=Deprotection)
    if firstLaunch:
        return finalSynthons

def __ProtectiveGroupRemoval(LabelsLIST, ReactionLIST, mol, keepSynthonsWithPG, firstReactionAsPrep, func, PreviousClasses,
                           CurrentClass, twoPGs=False):
    LabelsLISTBeforePGRemoval = LabelsLIST.split("|No|")[0]
    ReactionLISTBeforePGRemoval = ReactionLIST.split("|")[:len(LabelsLISTBeforePGRemoval.split("|"))]
    firstStopInd = len(LabelsLISTBeforePGRemoval.split("|"))
    finalSynthons = {}
    if firstReactionAsPrep:
        synthonsBeforeFirstPGremoval = __FirstReactionAsPrep(LabelsLISTBeforePGRemoval, "|".join(ReactionLISTBeforePGRemoval),
                                                           PreviousClasses, CurrentClass, mol, func)

    else:
        if "Ester" in CurrentClass and "Acid" in CurrentClass and func==3 and not twoPGs:
            synthonsBeforeFirstPGremoval = __NormalSynthonsGenerator(LabelsLISTBeforePGRemoval,
                                                                   "|".join(ReactionLISTBeforePGRemoval),
                                                                PreviousClasses, CurrentClass, mol, func=3)
        elif (func==3 and twoPGs) or func==2:

            synthonsBeforeFirstPGremoval = __NormalSynthonsGenerator(LabelsLISTBeforePGRemoval, "|".join(ReactionLISTBeforePGRemoval),
                                                                   PreviousClasses, CurrentClass, mol, func=1)

        elif func==3:
            synthonsBeforeFirstPGremoval = __NormalSynthonsGenerator(LabelsLISTBeforePGRemoval,
                                                                   "|".join(ReactionLISTBeforePGRemoval),
                                                                   PreviousClasses, CurrentClass, mol, func=2)

    if CurrentClass == "Trifunctional_Di_Esters_Amino":

        SynthonsWithoutPG = __SynthonsGeneratorsForPolymerReagents(LabelsLIST.split("|")[2],
                                            ReactionLIST.split("|")[2],  CurrentClass,
                                             synthonsBeforeFirstPGremoval, Deprotection=True)

        if SynthonsWithoutPG:
            for synth in SynthonsWithoutPG:
                if synth not in finalSynthons:
                    finalSynthons[synth] = SynthonsWithoutPG[synth].copy()
                else:
                    finalSynthons[synth].update(SynthonsWithoutPG[synth])
        lastSynthons = __SynthonsGeneratorsForPolymerReagents(LabelsLIST.split("|")[3],
                                                        ReactionLIST.split("|")[3],
                                                                        CurrentClass, SynthonsWithoutPG)
        if lastSynthons:
            for synth in lastSynthons:
                if synth not in finalSynthons:
                    finalSynthons[synth] = lastSynthons[synth].copy()
                else:
                    finalSynthons[synth].update(lastSynthons[synth])
        return finalSynthons
    PGremovalRule = ReactionLIST.split("|")[firstStopInd]
    if keepSynthonsWithPG or PGremovalRule =="[N;+0,+1;$([N+](=O)([#6])[O-]),$(N(=O)([#6])=O):1](=[O:2])=,-[O;+0,-1:3]>>[NH2,+0:1]":
        for synth in synthonsBeforeFirstPGremoval:
            if synth not in finalSynthons:
                finalSynthons[synth] = synthonsBeforeFirstPGremoval[synth].copy()
            else:
                finalSynthons[synth].update(synthonsBeforeFirstPGremoval[synth])
    cuttingRule = Reactions.ReactionFromSmarts(PGremovalRule)
    PGlable = LabelsLIST.split("|")[firstStopInd]
    SynthonsWithoutPG = {}
    for smi in synthonsBeforeFirstPGremoval:
        if func==3 and not twoPGs and smi.count(":")<2 and "Ester" not in CurrentClass and "AlkyneCH" not in CurrentClass: #this force algorythm to use for the PG removal step only synthons where all unprotected functional groups has been transformed into synthons
            continue
        products = cuttingRule.RunReactants((Chem.MolFromSmiles(smi),))
        for productSet in products:
            for product in productSet:
                labledSynthon = __getBBLabledSmiles(product, PGlable)
                for synth in labledSynthon:
                    if synth not in SynthonsWithoutPG:
                        SynthonsWithoutPG[synth] = set()
                    SynthonsWithoutPG[synth].update(PreviousClasses)
                    SynthonsWithoutPG[synth].add(CurrentClass)
    LabelsLISTBetweenPGRemoval = LabelsLIST.split("|No|")[1]
    ReactionLISTBetweenPGRemoval = ReactionLIST.split("|")[len(LabelsLISTBeforePGRemoval.split("|")) + 1:len(
        LabelsLISTBeforePGRemoval.split("|")) + len(LabelsLISTBetweenPGRemoval.split("|")) + 1]
    synthonsBetweenPGremoval = SynthonsWithoutPG.copy()
    for newSynthon in SynthonsWithoutPG:
        newSynthonsBetweenPGremoval = __NormalSynthonsGenerator(LabelsLISTBetweenPGRemoval,
                                                              "|".join(ReactionLISTBetweenPGRemoval),
                                                              PreviousClasses, CurrentClass, Chem.MolFromSmiles(newSynthon), func=1)
        if newSynthonsBetweenPGremoval:
            for synth in newSynthonsBetweenPGremoval:
                if synth not in synthonsBetweenPGremoval:
                    synthonsBetweenPGremoval[synth] = newSynthonsBetweenPGremoval[synth].copy()
                else:
                    synthonsBetweenPGremoval[synth].update(newSynthonsBetweenPGremoval[synth])
    if len(LabelsLIST.split("|No|"))==3:
        secondStopInd = len(LabelsLISTBeforePGRemoval.split("|")) + len(LabelsLISTBetweenPGRemoval.split("|")) + 1
        PGremovalRule = ReactionLIST.split("|")[secondStopInd]
    if len(LabelsLIST.split("|No|"))==2 or keepSynthonsWithPG or \
            PGremovalRule =="[N;+0,+1;$([N+](=O)([#6])[O-]),$(N(=O)([#6])=O):1](=[O:2])=,-[O;+0,-1:3]>>[NH2,+0:1]":
        for synth in SynthonsWithoutPG:
            if synth not in finalSynthons:
                finalSynthons[synth] = SynthonsWithoutPG[synth].copy()
            else:
                finalSynthons[synth].update(SynthonsWithoutPG[synth])
        for synth in synthonsBetweenPGremoval:
            if synth not in finalSynthons:
                finalSynthons[synth] = synthonsBetweenPGremoval[synth].copy()
            else:
                finalSynthons[synth].update(synthonsBetweenPGremoval[synth])
    if len(LabelsLIST.split("|No|"))==3:
        cuttingRule = Reactions.ReactionFromSmarts(PGremovalRule)
        PGlable = LabelsLIST.split("|")[secondStopInd]
        SynthonsWithout2PG = {}
        for mol in synthonsBetweenPGremoval:
            products = cuttingRule.RunReactants((Chem.MolFromSmiles(mol),))
            for productSet in products:
                for product in productSet:
                    labledSynthon = __getBBLabledSmiles(product, PGlable)
                    if labledSynthon:
                        for synth in labledSynthon:
                            if synth not in SynthonsWithout2PG:
                                SynthonsWithout2PG[synth] = set()
                            SynthonsWithout2PG[synth].update(PreviousClasses)
                            SynthonsWithout2PG[synth].add(CurrentClass)

        for synth in SynthonsWithout2PG:
            if synth not in finalSynthons:
                finalSynthons[synth] = SynthonsWithout2PG[synth].copy()
            else:
                finalSynthons[synth].update(SynthonsWithout2PG[synth])
        LabelsLast = LabelsLIST.split("|No|")[2]
        ReactionLast = ReactionLIST.split("|")[len(LabelsLISTBeforePGRemoval.split("|")) + 1 + len(LabelsLISTBetweenPGRemoval.split("|"))+1:]
        for newSynthon in SynthonsWithout2PG:
            lastSynthons = __NormalSynthonsGenerator(LabelsLast,"|".join(ReactionLast),
                                                        PreviousClasses, CurrentClass, Chem.MolFromSmiles(newSynthon), func=1)
            #lastSynthons.extend(__FirstReactionAsPrep(LabelsLast, "|".join(ReactionLast),Chem.MolFromSmiles(newSynthon), func=1, Class=Class)
            if lastSynthons:
                for synth in lastSynthons:
                    if synth not in finalSynthons:
                        finalSynthons[synth] = lastSynthons[synth].copy()
                    else:
                        finalSynthons[synth].update(lastSynthons[synth])
    return finalSynthons

def __NormalSynthonsGenerator(LabelsLIST, ReactionLIST, PreviousClasses, CurrentClass, mol, func=1, usedInds = None):
    if usedInds == None:
        usedInds = []
    labledSynthons = {}
    pat = re.compile("\[\w*:\w*\]")
    molSmiles = Chem.MolToSmiles(mol, canonical=True)
    marksPrevious = [molSmiles[m.start():m.start() + 2] + molSmiles[m.end() - 4:m.end()] for m in re.finditer(pat, molSmiles)]
    for ind, rule in enumerate(ReactionLIST.split("|")):
        if ind not in usedInds:
            try:
                cuttingRule = Reactions.ReactionFromSmarts(rule)
            except:
                print("########################")
                print(Chem.MolToSmiles(mol, canonical=True))
                print(rule)
                exit()
            products = cuttingRule.RunReactants((mol,))
            Label = LabelsLIST.split("|")[ind]
            for productSet in products:
                for product in productSet:
                    labledSynthon = __getBBLabledSmiles(product, Label)
                    if labledSynthon==None:
                        print(Chem.MolToSmiles(mol, canonical=True))
                        exit()
                    if marksPrevious:
                        for synth in labledSynthon:
                            marksNew = [synth[m.start():m.start() + 2] + synth[m.end() - 4:m.end()] for m in
                                     re.finditer(pat, synth)]
                            if len(marksNew)>len(marksPrevious):
                                if synth not in labledSynthons:
                                    labledSynthons[synth] = set()
                                labledSynthons[synth].update(PreviousClasses)
                                labledSynthons[synth].add(CurrentClass)
                    else:
                        for synth in labledSynthon:
                            if synth not in labledSynthons:
                                labledSynthons[synth] = set()
                            labledSynthons[synth].update(PreviousClasses)
                            labledSynthons[synth].add(CurrentClass)
                    newSynthons = None
                    if func == 2:
                        newMol = Chem.MolFromSmiles(labledSynthon[0])
                        usedInds.append(ind)
                        newSynthons = __NormalSynthonsGenerator(LabelsLIST, ReactionLIST, PreviousClasses, CurrentClass, newMol, func=1,
                                                              usedInds=usedInds)
                    elif func == 3:
                        newMol = Chem.MolFromSmiles(labledSynthon[0])
                        usedInds.append(ind)
                        newSynthons = __NormalSynthonsGenerator(LabelsLIST, ReactionLIST, PreviousClasses, CurrentClass, newMol, func=2,
                                                              usedInds=usedInds)
                    if newSynthons:
                        for synth in newSynthons:
                            if synth not in labledSynthons:
                                labledSynthons[synth] = newSynthons[synth].copy()
                            else:
                                labledSynthons[synth].update(newSynthons[synth])
    return labledSynthons

def __FirstReactionAsPrep(LabelsLIST, ReactionLIST, PreviousClasses, CurrentClass, mol, func):
    rule = ReactionLIST.split("|")[0]
    cuttingRule = Reactions.ReactionFromSmarts(rule)
    products = cuttingRule.RunReactants((mol,))
    if not products:
        if len(LabelsLIST.split("|"))>1:
            return __FirstReactionAsPrep("|".join(LabelsLIST.split("|")[1:]), "|".join(ReactionLIST.split("|")[1:]),
                                       PreviousClasses, CurrentClass, mol, func)
        else:
            return {Chem.MolToSmiles(mol, canonical=True): PreviousClasses}
    else:
        Label = LabelsLIST.split("|")[0]
        synthonsAsInpForTheNextStep = {}
        for productSet in products:
            for product in productSet:
                labledSynthon = __getBBLabledSmiles(product, Label)
                if labledSynthon:
                    for synth in labledSynthon:
                        if synth not in synthonsAsInpForTheNextStep:
                            synthonsAsInpForTheNextStep[synth] = set()
                        synthonsAsInpForTheNextStep[synth].update(PreviousClasses)
                        synthonsAsInpForTheNextStep[synth].add(CurrentClass)

        if len(ReactionLIST.split("|"))==1:
            return synthonsAsInpForTheNextStep
        if not synthonsAsInpForTheNextStep and "Boronics" in CurrentClass:
            rule = ReactionLIST.split("|")[1]
            cuttingRule = Reactions.ReactionFromSmarts(rule)
            products = cuttingRule.RunReactants((mol,))
            Label = LabelsLIST.split("|")[1]
            for productSet in products:
                for product in productSet:
                    labledSynthon = __getBBLabledSmiles(product, Label)
                    if labledSynthon:
                        for synth in labledSynthon:
                            if synth not in synthonsAsInpForTheNextStep:
                                synthonsAsInpForTheNextStep[synth] = set()
                            synthonsAsInpForTheNextStep[synth].update(PreviousClasses)
                            synthonsAsInpForTheNextStep[synth].add(CurrentClass)
            for synth in synthonsAsInpForTheNextStep:
                labledSynthons = __NormalSynthonsGenerator("|".join(LabelsLIST.split("|")[1:]), "|".join(ReactionLIST.split("|")[1:]),
                                            PreviousClasses, CurrentClass, Chem.MolFromSmiles(synth), func=func - 1)
                if labledSynthons:
                    for synth in labledSynthons:
                        if synth not in synthonsAsInpForTheNextStep:
                            synthonsAsInpForTheNextStep[synth] = labledSynthons[synth].copy()
                        else:
                            synthonsAsInpForTheNextStep[synth].update(labledSynthons[synth])
            return synthonsAsInpForTheNextStep
        lastSynthons = {}
        for synth in synthonsAsInpForTheNextStep:
            labledSynthons = __NormalSynthonsGenerator("|".join(LabelsLIST.split("|")[1:]), "|".join(ReactionLIST.split("|")[1:]),
                                                          PreviousClasses, CurrentClass, Chem.MolFromSmiles(synth), func=func-1)
            if labledSynthons:
                for synth in labledSynthons:
                    if synth not in lastSynthons:
                        lastSynthons[synth] = labledSynthons[synth].copy()
                    else:
                        lastSynthons[synth].update(labledSynthons[synth])
        for synth in lastSynthons:
            if synth not in synthonsAsInpForTheNextStep:
                synthonsAsInpForTheNextStep[synth] = lastSynthons[synth]
            else:
                synthonsAsInpForTheNextStep[synth].update(lastSynthons[synth])
        return synthonsAsInpForTheNextStep

def __getBBLabledSmiles(productMolecule: Chem.rdchem.Mol, Label:str):
    productSmiles = Chem.MolToSmiles(productMolecule, canonical=True)
    labeledSmilesList = []
    if Label != "No":
        for sublabel in Label.split(","):
            if productSmiles.find(sublabel.split("->")[0]) != -1:
                labeledSmiles = checkLable(productSmiles, sublabel)
                if labeledSmiles==None:
                    return None
                if "*" in labeledSmiles:
                    productSmiles = labeledSmiles
                    continue
                elif labeledSmiles:
                    labeledSmilesList.append(labeledSmiles)
        if labeledSmilesList:
            return list(set(labeledSmilesList))
    #print("WARNING! No lable was assigned to the smiles: " + productSmiles)
    return [productSmiles]

def __getReactionSMARTS(BB_Marks: ET.Element):
    MarksSetup = {}
    for child in BB_Marks:
        for subCh in child:
            if subCh.get('SMARTS'):
                MarksSetup[child.tag + "_" + subCh.tag] = {}
                MarksSetup[child.tag + "_" + subCh.tag]["SMARTS"] = subCh.get('SMARTS')
                MarksSetup[child.tag + "_" + subCh.tag]["Labels"] = subCh.get('Labels')
    return MarksSetup

def generateScaffoldForBB(smiles, returnObjects=False):
    scaffold = None
    mol = None
    PGdict = {"NCbz": "[N:1][C;$(C(=O)O[CH2]c1[cH][cH][cH][cH][cH]1):2]>>[N:1]",
              "NFmoc": "[N:1][C;$(C(=O)O[CH2][CH]1c2[cH][cH][cH][cH]c2-c3[cH][cH][cH][cH]c13):2]>>[N:1]",
              "NBnz": "[N;+0;$(N[CH2]c1[cH][cH][cH][cH][cH]1);!$(N[C,S,P]=[O,S,N]):1][C;$([CH2]c1[cH][cH][cH][cH][cH]1):2]>>[N:1]",
              "COOBnz": "[O;$(O(C)C([#6])=O):1][C;$([CH2]c1[cH][cH][cH][cH][cH]1):2]>>[OH:1]",
              "Boronics": "[B;$(B(O@C)O@C):1][#6:2]>>[#6:2]",
              "Oxiranes": "[C:1]1[O:2][C:3]1>>[C:1]([OH:2])[C;+0:3]"}
    mol = readMol(smiles)
    if mol:
        for pg in PGdict:
            mol = __removePGforScaffolds(PGdict[pg], mol)
        scaffold = MurckoScaffoldSmiles(mol=mol)
    if returnObjects:
        return scaffold,mol
    else:
        return scaffold

def __removePGforScaffolds(reactionRule, mol):
    q = Chem.MolFromSmarts(reactionRule.split(">>")[0])
    cuttingRule = Reactions.ReactionFromSmarts(reactionRule)
    while mol.HasSubstructMatch(q):
        products = cuttingRule.RunReactants((mol,))
        mol = products[0][0]
        mol.UpdatePropertyCache()
        Chem.GetSymmSSSR(mol)
        mol.GetRingInfo().NumRings()
    return mol

"""def __checkLable(productSmiles:str, Label:str):
    goodValenceSmiles = None
    if Label.split("->")[0][1] == "S":
        hCount = 1
        out = productSmiles.replace(Label.split("->")[0],
                                        "[" + Label.split("->")[1].split(":")[0] + "H" + str(hCount) + ":" +
                                        Label.split("->")[1].split(":")[1] + "]")
        goodValenceSmiles = out
    else:
        for hCount in range(1, 5):
            if "+" in Label:
                out = productSmiles.replace(Label.split("->")[0],
                                                "[" + Label.split("->")[1].split(":")[0] + "H" + str(hCount) + "+:" +
                                                Label.split("->")[1].split(":")[1] + "]")
            else:
                out = productSmiles.replace(Label.split("->")[0],
                                                "[" + Label.split("->")[1].split(":")[0] + "H" + str(hCount) + ":" +
                                                Label.split("->")[1].split(":")[1] + "]")

            newMol = Chem.MolFromSmiles(out)
            if not newMol:
                if "[nH1:" in out:
                    modifiedSmiles = out.replace("[nH1:", "[n:", 1)
                    check2 = CheckMolStructure(modifiedSmiles, Label)
                    if check2:
                        break
                    else:
                        goodValenceSmiles = modifiedSmiles
                else:
                    break
            else:
                goodValenceSmiles = out
                check = CheckMolStructure(goodValenceSmiles, Label)
                if check:
                    break
    if not goodValenceSmiles:
        print("Problem with structure check: " + productSmiles + " " + out)
        return None
    return generateMajorTautFromSynthonSmiles(goodValenceSmiles)"""


A = list(map(int, input().split()))
B = list(map(int, input().split()))
Ascore = 0
Bscore = 0
score = []
for i in range(len(A)):
    if A[i] > B[i]:
        Ascore += 3
        score.append('A')
    elif B[i] > A[i]:
        Bscore += 3
        score.append('B')
    else:
        Ascore += 1
        Bscore += 1
        score.append('D')

print(Ascore, Bscore)
if Ascore > Bscore:
    print('A')
if Bscore > Ascore:
    print('B')
if Ascore == Bscore:
    for i in range(9, -1, -1):
        if score[i] != 'D':
            print(score[i])
            break
        if i == 0:
            print('D')


#this fixes exp not beeing able to import because it is not in the pythonpath
import os,sys,inspect

current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parent_dir = os.path.dirname(current_dir)
sys.path.insert(0, parent_dir)

import exp
from SpikingNeuroController import SpikingNeurocontroller
from globalvalues import gv
import numpy as np

def configure_training(expenv):
    # fully connected net with relative input encoding
    gv.num_hidden_neurons = 2
    gv.manual_num_inhibitory = 0
    gv.w0_min = 200.  # Minimum initial random value
    gv.w0_max = 800.  # Maximum initial random value
    gv.num_episodes = 10
    gv.errsig_factor = 0.03
    gv.render = False
    gv.structural_plasticity = False
    gv.random_reconnect = True
    expenv.createLineFollowing(SpikingNeurocontroller(num_inputneurons=2, neuron_labels=["error pos", "error neg"]))
    expenv.agent.actor.obsfactor = np.array([800 for x in range(2)])  # clamped anyway

if __name__ == "__main__":
    args = exp.parseargs()
    exp.trainingrun(configure_training, num_processes=args.processes, gridsearchpath=args.gridsearch)


import requests
from termcolor import colored
import pyfiglet
from random import randint

#title of program is formatted and printed
title_string = 'Dad Jokes ! !'

text = pyfiglet.figlet_format(title_string)

title = colored(text, color='cyan', on_color='on_white', attrs=['blink'])
print(text)

#prompt to get topic from user
topic = input('Want to here a joke? Give me a topic: ')

url = "https://icanhazdadjoke.com/search"

#gets response from dad joke api
response = requests.get(
    url,
    headers={"Accept":"application/json"},
    params={"term": topic})

#converts json data to python dictionary
data = response.json()

#grabs number of jokes in query
num_jokes = data['total_jokes']

if num_jokes == 0:
    print("There are no jokes about {}".format(topic))
else:
    #index of joke chosen
    random_num = randint(0, num_jokes)
    if num_jokes == 1:
        print("I've got one joke about a {}. Here it is: \n{}".format(topic, data['results'][0]['joke']))
    else:
        print("I've got {} jokes about {}. Here's one: \n{}".format(num_jokes, topic, data['results'][random_num]['joke']))


"""
Module of Shtonda Yelizaveta, contains functions for data loading
"""
import Information
import Builder
import json

def load_ini(path):
    """
    :param path: relative path to .ini file
    opens .ini file, processes  and prints 'OK' if there are no errors
    :return: list of keys from .ini file
    """
    with open(path, 'r', encoding='utf8') as f:
        r = json.load(f)
        check_ini_file(r)
        main_file = r['input']['csv']
        additional_file = r['input']['json']
        input_encoding = r['input']['encoding']
        output_file = r['output']['fname']
        output_encoding = r['output']['encoding']
        print('OK')
        return [main_file, additional_file, input_encoding, output_file, output_encoding]

def check_ini_file(r):
    """
    :param r: dictionary, loaded from .ini file
    checks existence of needed keys and raises error if some key is absent
    :return: True if there are no errors
    """
    input_keys = ['csv', 'json', 'encoding']
    output_keys = ['fname', 'encoding']
    input_list = []
    output_list = []
    for i in r['input']:
        input_list.append(i)
    for i in r['output']:
        output_list.append(i)
    if not input_list >= input_keys:
        raise ValueError('Something wrong with input keys in .ini file')
    if not output_list >= output_keys:
        raise ValueError('Something wrong with output keys in .ini file')
    return True

def load(main_file, addition_file, input_encoding):
    """
    :param main_file: relative path to .csv file
    :param addition_file: relative path to .json file
    :param input_encoding: type of encoding of main_file and addition_file
    creates instance of the class Information, calls functions for loading main_file and addition_file, checks
    their accordance and prints appropriate text
    :return: instance of the class Information
    """
    data = Information.Information()
    load_data(data, main_file, input_encoding)
    if fit(data, load_stat(addition_file, input_encoding)):
        print('OK')
    else:
        print('UPS')
    return data

def load_data(data, main_file, input_encoding):
    """
    :param data: object of class Information
    :param main_file: relative path to .csv file
    :param input_encoding: type of encoding of main_file
    clears data, creates instance of the class Builder, pass parameters to Builder method and prints 'OK'
    if there are no errors
    """
    print(f'input-csv {main_file}: ', end='')
    data.clear()
    obj = Builder.Builder()
    obj.load(data, main_file, input_encoding)
    print('OK')

def load_stat(additional_file, input_encoding):
    """
    :param additional_file: relative path to .json file
    :param input_encoding: type of encoding of additional_file
    opens additional file, checks existence of needed keys and raises error if some key is absent
    :return: dictionary loaded from additional_file
    """
    print(f'input-json {additional_file}: ', end='')
    keys = ["сума довжин всіх прізвищ", "кількість записів"]
    with open(additional_file, 'r', encoding=input_encoding) as f:
        add_data = json.load(f)
        for key in keys:
            if key not in add_data:
                raise ValueError('There are no needed keys in additional file')
        add_data["surnames_sum"] = add_data.pop("сума довжин всіх прізвищ")
        add_data["records_sum"] = add_data.pop("кількість записів")
    print('OK')
    return add_data

def fit(data, add_data):
    """
    :param data: object of class Information
    :param add_data: dictionary loaded from additional_file, returned in load_stat() function
    checks accordance of data from additional_file to corresponding data in lst
    :return: True if data is according, False otherwise
    """
    print('json?=csv: ', end='')
    record_sum = data.records_sum
    surname_sum = data.surnames_length
    return surname_sum == add_data["surnames_sum"] and record_sum == add_data["records_sum"]