import logging
import re
import time
import pubchempy as pcp
import numpy as np
from matchms.metadata_utils import is_valid_inchikey

logger = logging.getLogger("matchms")

def pubchem_metadata_lookup(spectrum_in, name_search_depth=10, match_precursor_mz=False,
                            formula_search=False,
                            mass_tolerance=2.0,
                            allowed_differences=[(18.03, 0.01)],
                            min_formula_length=6,
                            formula_search_depth=25,
                            pause_per_request=0,
                            verbose=2):
    """
    Parameters
    ----------
    spectrum_in
        Matchms type spectrum as input.
    name_search_depth: int
        How many of the most relevant name matches to explore deeper. Default = 10.
    """
    if spectrum_in is None:
        return None

    # Only run search if no valid-looking inchikey is found
    if is_valid_inchikey(spectrum_in.get("inchikey")):
        return spectrum_in

    spectrum = spectrum_in.clone()

    def _plausible_name(compound_name):
        return (isinstance(compound_name, str) and len(compound_name) > 4)

    # Only run search if (more or less) plausible name is found
    compound_name = spectrum.get("compound_name")
    if not _plausible_name(compound_name):
        logger.info("No plausible compound name found (%s)", compound_name)
        return spectrum

    # Start pubchem search
    time.sleep(pause_per_request)
    inchi = spectrum.get("inchi")
    parent_mass = spectrum.get("parent_mass")
    if isinstance(parent_mass, np.ndarray):
        parent_mass = parent_mass[0]
    formula = spectrum.get("formula")

    # 1) Search for matching compound name
    results_pubchem = pubchem_name_search(compound_name, name_search_depth=name_search_depth,
                                          verbose=verbose)

    if len(results_pubchem) > 0:
        logger.info("Found potential matches for compound name (%s) on PubChem",
                   compound_name)

        # 1a) Search for matching inchi
        if likely_has_inchi(inchi):
            inchi_pubchem, inchikey_pubchem, smiles_pubchem = find_pubchem_inchi_match(results_pubchem, inchi,
                                                                                verbose=verbose)
        # 1b) Search for matching parent mass
        if not likely_has_inchi(inchi) or inchikey_pubchem is None:
            inchi_pubchem, inchikey_pubchem, smiles_pubchem = find_pubchem_mass_match(results_pubchem,
                                                                                parent_mass,
                                                                                given_mass="parent mass",
                                                                                mass_tolerance=mass_tolerance,
                                                                                allowed_differences=allowed_differences,
                                                                                verbose=verbose)

        # 1c) Search for matching precursor mass (optional)
        if match_precursor_mz and inchikey_pubchem is None:
            precursor_mz = spectrum.get("precursor_mz")
            inchi_pubchem, inchikey_pubchem, smiles_pubchem = find_pubchem_mass_match(results_pubchem,
                                                                                precursor_mz,
                                                                                given_mass="precursor mass",
                                                                                mass_tolerance=mass_tolerance,
                                                                                allowed_differences=allowed_differences,
                                                                                verbose=verbose)

        if inchikey_pubchem is not None and inchi_pubchem is not None:
            logger.info("Matching compound name: %s", compound_name)
            spectrum.set("inchikey", inchikey_pubchem)
            spectrum.set("inchi", inchi_pubchem)
            spectrum.set("smiles", smiles_pubchem)
            return spectrum

        if verbose >= 2:
            logger.info("No matches found for compound name: %s", compound_name)

    else:
        logger.info("No matches for compound name (%s) on PubChem",
                   compound_name)

    # 2) Search for matching formula
    if formula_search and formula and len(formula) >= min_formula_length:
        results_pubchem = pubchem_formula_search(formula, formula_search_depth=formula_search_depth,
                                                 verbose=verbose)

        if len(results_pubchem) > 0:
            inchikey_pubchem = None
            logger.info("Found potential matches for formula (%s) on PubChem",
                       formula)
            # 2a) Search for matching inchi
            if likely_has_inchi(inchi):
                inchi_pubchem, inchikey_pubchem, smiles_pubchem = find_pubchem_inchi_match(results_pubchem, inchi,
                                                                                verbose=verbose)
            # 2b) Search for matching parent mass
            if inchikey_pubchem is None:
                inchi_pubchem, inchikey_pubchem, smiles_pubchem = find_pubchem_mass_match(results_pubchem,
                                                                                parent_mass,
                                                                                given_mass="parent mass",
                                                                                mass_tolerance=mass_tolerance,
                                                                                allowed_differences=allowed_differences,
                                                                                verbose=verbose)
            # 2c) Search for matching precursor mass (optional)
            if match_precursor_mz and inchikey_pubchem is None:
                precursor_mz = spectrum.get("precursor_mz")
                inchi_pubchem, inchikey_pubchem, smiles_pubchem = find_pubchem_mass_match(results_pubchem,
                                                                                precursor_mz,
                                                                                given_mass="precursor mass",
                                                                                mass_tolerance=mass_tolerance,
                                                                                allowed_differences=allowed_differences,
                                                                                verbose=verbose)
            if inchikey_pubchem is not None and inchi_pubchem is not None:
                logger.info("Matching formula: %s", formula)
                if verbose >= 1:
                    logger.info("Matching formula: %s", formula)
                spectrum.set("inchikey", inchikey_pubchem)
                spectrum.set("inchi", inchi_pubchem)
                spectrum.set("smiles", smiles_pubchem)
                return spectrum

            if verbose >= 2:
                logger.info("No matches found for formula: %s", formula)
        else:
            logger.info("No matches for formula (%s) on PubChem",
                       formula)

    return spectrum

def likely_has_inchi(inchi):
    """Quick test to avoid excess in-depth testing"""
    if inchi is None:
        return False
    inchi = inchi.strip('"')
    regexp = r"(InChI=1|1)(S\/|\/)[0-9, A-Z, a-z,\.]{2,}\/(c|h)[0-9]"
    if not re.search(regexp, inchi):
        return False
    return True

def likely_inchi_match(inchi_1, inchi_2, min_agreement=3):
    """Try to match defective inchi to non-defective ones.
    Compares inchi parts seperately. Match is found if at least the first
    'min_agreement' parts are a good enough match.
    The main 'defects' this method accounts for are missing '-' in the inchi.
    In addition, differences between '-', '+', and '?'will be ignored.
    Parameters
    ----------
    inchi_1: str
        inchi of molecule.
    inchi_2: str
        inchi of molecule.
    min_agreement: int
        Minimum number of first parts that MUST be a match between both input
        inchi to finally consider it a match. Default is min_agreement=3.
    """
    if min_agreement < 2:
        logger.warning("Warning! 'min_agreement' < 2 has no discriminative power. Should be => 2.")
    if min_agreement == 2:
        logger.warning("Warning! 'min_agreement' == 2 has little discriminative power",
                       "(only looking at structure formula. Better use > 2.")
    agreement = 0

    # Remove spaces and '"' to account for different notations.
    # Remove everything with little discriminative power.
    ignore_lst = ['"', ' ', '-', '+', '?']
    for ignore in ignore_lst:
        inchi_1 = inchi_1.replace(ignore, '')
        inchi_2 = inchi_2.replace(ignore, '')

    # Split inchi in parts.
    inchi_1_parts = inchi_1.split('/')
    inchi_2_parts = inchi_2.split('/')

    # Check if both inchi have sufficient parts (seperated by '/')
    if len(inchi_1_parts) >= min_agreement and len(
            inchi_2_parts) >= min_agreement:
        # Count how many parts agree well
        for i in range(min_agreement):
            agreement += (inchi_1_parts[i] == inchi_2_parts[i])

    return bool(agreement == min_agreement)

def likely_inchikey_match(inchikey_1, inchikey_2, min_agreement=1):
    """Try to match inchikeys.
    Compares inchikey parts seperately. Match is found if at least the first
    'min_agreement' parts are a good enough match.
    Parameters
    ----------
    inchikey_1: str
        inchikey of molecule.
    inchikey_2: str
        inchikey of molecule.
    min_agreement: int
        Minimum number of first parts that MUST be a match between both input
        inchikey to finally consider it a match. Default is min_agreement=1.
    """
    if min_agreement not in [1, 2, 3]:
        logger.error("Warning! 'min_agreement' should be 1, 2, or 3.")
    agreement = 0

    # Harmonize strings
    inchikey_1 = inchikey_1.upper().replace('"', '').replace(' ', '')
    inchikey_2 = inchikey_2.upper().replace('"', '').replace(' ', '')

    # Split inchikey in parts.
    inchikey_1_parts = inchikey_1.split('-')
    inchikey_2_parts = inchikey_2.split('-')

    # Check if both inchikey have sufficient parts (seperated by '/')
    if len(inchikey_1_parts) >= min_agreement and len(
            inchikey_2_parts) >= min_agreement:
        # Count how many parts mostly agree
        for i in range(min_agreement):
            agreement += (inchikey_1_parts[i] == inchikey_2_parts[i])

    return agreement == min_agreement

def pubchem_name_search(compound_name: str, name_search_depth=10, verbose=1):
    """Search pubmed for compound name"""
    results_pubchem = pcp.get_compounds(compound_name,
                                        'name',
                                        listkey_count=name_search_depth)
    if len(results_pubchem) == 0 and "_" in compound_name:
        results_pubchem = pcp.get_compounds(compound_name.replace("_", " "),
                                            'name',
                                            listkey_count=name_search_depth)
    if len(results_pubchem) == 0:
        return []

    logger.debug("Found at least %s compounds of that name on pubchem.", len(results_pubchem))
    return results_pubchem

def pubchem_formula_search(compound_formula: str, formula_search_depth=25, verbose=1):
    """Search pubmed for compound formula"""
    sids_pubchem = pcp.get_sids(compound_formula,
                                'formula',
                                listkey_count=formula_search_depth)

    results_pubchem = []
    for sid in sids_pubchem:
        result = pcp.Compound.from_cid(sid['CID'])
        results_pubchem.append(result)

    logger.debug("Found at least %s compounds of with formula: %s.",
                 len(results_pubchem), compound_formula)
    return results_pubchem

def find_pubchem_inchi_match(results_pubchem,
                             inchi,
                             min_inchi_match=3,
                             verbose=1):
    """Searches pubmed matches for inchi match.
    Then check if inchi can be matched to (defective) input inchi.
    Outputs found inchi and found inchikey (will be None if none is found).
    Parameters
    ----------
    results_pubchem: List[dict]
        List of name search results from Pubchem.
    inchi: str
        Inchi (correct, or defective...). Set to None to ignore.
    min_inchi_match: int
        Minimum number of first parts that MUST be a match between both input
        inchi to finally consider it a match. Default is min_inchi_match=3.
    """

    inchi_pubchem = None
    inchikey_pubchem = None
    smiles_pubchem = None

    # Loop through first 'name_search_depth' results found on pubchem. Stop once first match is found.
    for result in results_pubchem:
        inchi_pubchem = '"' + result.inchi + '"'
        inchikey_pubchem = result.inchikey
        smiles_pubchem = result.isomeric_smiles
        if smiles_pubchem is None:
            smiles_pubchem = result.canonical_smiles

        match_inchi = likely_inchi_match(inchi, inchi_pubchem,
                                         min_agreement=min_inchi_match)

        if match_inchi:
            logger.info("Matching inchi: %s", inchi)
            if verbose >= 1:
                logger.info("Found matching compound for inchi: %s (Pubchem: %s)",
                            inchi, inchi_pubchem)
            break

    if not match_inchi:
        inchi_pubchem = None
        inchikey_pubchem = None
        smiles_pubchem = None

        if verbose >= 2:
            logger.info("No matches found for inchi %s.", inchi)

    return inchi_pubchem, inchikey_pubchem, smiles_pubchem

def find_pubchem_mass_match(results_pubchem,
                            parent_mass,
                            mass_tolerance,
                            given_mass="parent mass",
                            allowed_differences=[(18.03, 0.01)],
                            verbose=1):
    """Searches pubmed matches for inchi match.
    Then check if inchi can be matched to (defective) input inchi.
    Outputs found inchi and found inchikey (will be None if none is found).
    Parameters
    ----------
    results_pubchem: List[dict]
        List of name search results from Pubchem.
    parent_mass: float
        Spectrum"s guessed parent mass.
    mass_tolerance: float
        Acceptable mass difference between query compound and pubchem result.
    given_mass
        String to specify the type of the given mass (e.g. "parent mass").
    """
    inchi_pubchem = None
    inchikey_pubchem = None
    smiles_pubchem = None
    mass_difference = None
    lowest_mass_difference = [np.inf, None]

    for result in results_pubchem:
        inchi_pubchem = '"' + result.inchi + '"'
        inchikey_pubchem = result.inchikey
        smiles_pubchem = result.isomeric_smiles
        if smiles_pubchem is None:
            smiles_pubchem = result.canonical_smiles

        pubchem_mass = float(results_pubchem[0].exact_mass)
        mass_difference = np.abs(pubchem_mass - parent_mass)
        if mass_difference < lowest_mass_difference[0]:
            lowest_mass_difference[0] = mass_difference
            lowest_mass_difference[1] = inchi_pubchem
        match_mass = (mass_difference <= mass_tolerance)
        for diff in allowed_differences:
            match_mass = match_mass or np.isclose(mass_difference, diff[0], atol=diff[1])

        if match_mass:
            logger.info("Matching molecular weight (%s vs %s of %s)",
                        pubchem_mass, given_mass, parent_mass)
            break

    if not match_mass:
        inchi_pubchem = None
        inchikey_pubchem = None
        smiles_pubchem = None

        logger.info("No matching molecular weight (best mass difference was %s for inchi: %s)",
                    lowest_mass_difference[0], lowest_mass_difference[1])

    return inchi_pubchem, inchikey_pubchem, smiles_pubchem


import io, tempfile, os
from . import *
import nutils.log, nutils.core, nutils.parallel

log_stdout = '''\
iterator > iter 0 (0%) > a
iterator > iter 1 (33%) > b
iterator > iter 2 (67%) > c
levels > error
levels > warning
levels > user
levels > info
forked > [1] error
forked > [1] warning
forked > [1] user
forked > [1] info
exception > ValueError('test',)
  File "??", line ??, in ??
    raise ValueError( 'test' )
test.png
nonexistent.png
'''

log_stdout3 = '''\
levels > error
levels > warning
levels > user
forked > [1] error
forked > [1] warning
forked > [1] user
exception > ValueError('test',)
  File "??", line ??, in ??
    raise ValueError( 'test' )
'''

log_rich_output = '''\
\033[K\033[1;30miterator\033[0m\r\
\033[K\033[1;30miterator · iter 0 (0%)\033[0m\r\
\033[K\033[1;30miterator · iter 0 (0%) · \033[0ma
\033[K\033[1;30miterator · iter 1 (33%)\033[0m\r\
\033[K\033[1;30miterator · iter 1 (33%) · \033[0mb
\033[K\033[1;30miterator · iter 2 (67%)\033[0m\r\
\033[K\033[1;30miterator · iter 2 (67%) · \033[0mc
\033[K\033[1;30miterator · iter 3 (100%)\033[0m\r\
\033[K\033[1;30mempty\033[0m\r\
\033[K\033[1;30mempty · empty\033[0m\r\
\033[K\033[1;30mlevels\033[0m\r\
\033[K\033[1;30mlevels · \033[1;31merror\033[0m
\033[K\033[1;30mlevels · \033[0;31mwarning\033[0m
\033[K\033[1;30mlevels · \033[0;33muser\033[0m
\033[K\033[1;30mlevels · \033[0minfo
\033[K\033[1;30mforked · \033[1;31m[1] error\033[0m
\033[K\033[1;30mforked · \033[0;31m[1] warning\033[0m
\033[K\033[1;30mforked · \033[0;33m[1] user\033[0m
\033[K\033[1;30mforked · \033[0m[1] info
\033[K\033[1;30mexception\033[0m\r\
\033[K\033[1;30mexception · \033[1;31mValueError(\'test\',)
  File "??", line ??, in ??
    raise ValueError( \'test\' )\033[0m
\033[K\033[1;30m\033[0mtest.png
\033[K\033[1;30m\033[0mnonexistent.png
\033[K'''

log_html = '''\
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd">

<title>test</title>

<link rel="stylesheet" type="text/css" href="style.css">
<meta charset="UTF-8" />

iterator

iter 0 (0%)

a

iter 1 (33%)

b

iter 2 (67%)

c

levels

error

warning

user

info

exception

ValueError('test',)
  File "??", line ??, in ??
    raise ValueError( 'test' )

test.png

nonexistent.png

'''

log_indent = '''\
c iterator
 c iter 0 (0%)
  i a
 c iter 1 (33%)
  i b
 c iter 2 (67%)
  i c
c levels
 e error
 w warning
 u user
 i info
c exception
 e ValueError('test',)
 |   File "??", line ??, in ??
 |     raise ValueError( 'test' )
i test.png
i nonexistent.png
'''

def generate_log():
  with nutils.log.context( 'iterator' ):
    for i in nutils.log.iter( 'iter', 'abc' ):
      nutils.log.info( i )
  with nutils.log.context( 'empty' ):
    with nutils.log.context( 'empty' ):
      pass
  with nutils.log.context( 'levels' ):
    for level in ( 'error', 'warning', 'user', 'info' ):
      getattr( nutils.log, level )( level )
  nutils.parallel.procid = 1
  with nutils.log.context( 'forked' ):
    for level in ( 'error', 'warning', 'user', 'info' ):
      getattr( nutils.log, level )( level )
  nutils.parallel.procid = None
  with nutils.log.context( 'exception' ):
    nutils.log.error(
      "ValueError('test',)\n" \
      '  File "??", line ??, in ??\n' \
      "    raise ValueError( 'test' )")
  with nutils.core.open_in_outdir( 'test.png', 'w' ) as f:
    pass
  nutils.log.info( 'test.png' )
  nutils.log.info( 'nonexistent.png' )

@parametrize
class logoutput(ContextTestCase):

  def setUpContext(self, stack):
    super().setUpContext(stack)
    self.outdir = stack.enter_context(tempfile.TemporaryDirectory())
    if self.progressfile == 'seekable':
      self.progressfileobj = stack.enter_context(open(os.path.join(self.outdir, 'progress.json'), 'w' ))
    elif self.progressfile == 'stream':
      self.progressfileobj = progressfile=io.StringIO()
    else:
      self.progressfileobj = None

  def test(self):
    __outdir__ = self.outdir
    __verbose__ = self.verbose
    # Make sure all progress information is written, regardless the speed of
    # this computer.
    __progressinterval__ = -1
    stream = io.StringIO()
    kwargs = dict(title='test') if self.logcls == nutils.log.HtmlLog else {}
    if self.progressfileobj is not None:
      kwargs.update(progressfile=self.progressfileobj)
    with self.logcls(stream, **kwargs) as __log__:
      generate_log()
    self.assertEqual(stream.getvalue(), self.logout)

_logoutput = lambda name, logcls, logout, verbose=len(nutils.log.LEVELS), progressfile=False: logoutput(name, logcls=logcls, logout=logout, verbose=verbose, progressfile=progressfile)
_logoutput('stdout', nutils.log.StdoutLog, log_stdout)
_logoutput('stdout-verbose3', nutils.log.StdoutLog, log_stdout3, verbose=3)
_logoutput('rich_output', nutils.log.RichOutputLog, log_rich_output)
_logoutput('html', nutils.log.HtmlLog, log_html)
_logoutput('indent', nutils.log.IndentLog, log_indent)
_logoutput('indent-progress-seekable', nutils.log.IndentLog, log_indent, progressfile='seekable')
_logoutput('indent-progress-stream', nutils.log.IndentLog, log_indent, progressfile='stream')

class tee_stdout_html(ContextTestCase):

  def setUpContext(self, stack):
    super().setUpContext(stack)
    self.outdir = stack.enter_context(tempfile.TemporaryDirectory())

  def test(self):
    __outdir__ = self.outdir
    __verbose__ = len(nutils.log.LEVELS)
    stream_stdout = io.StringIO()
    stream_html = io.StringIO()
    __log__ = nutils.log.TeeLog(
      nutils.log.StdoutLog(stream_stdout),
      nutils.log.HtmlLog(stream_html, title='test'))
    with __log__:
      generate_log()
    self.assertEqual(stream_stdout.getvalue(), log_stdout)
    self.assertEqual(stream_html.getvalue(), log_html)

class html_post_mortem(ContextTestCase):

  def setUpContext(self, stack):
    super().setUpContext(stack)
    self.outdir = stack.enter_context(tempfile.TemporaryDirectory())

  def test(self):
    class TestException(Exception): pass

    virtual_module = dict(TestException=TestException)
    exec('''\
def generate_exception(level=0):
  if level == 1:
    raise TestException
  else:
    generate_exception(level+1)
''', virtual_module)
    __outdir__ = self.outdir
    stream = io.StringIO()
    with self.assertRaises(TestException):
      with nutils.log.HtmlLog(stream, title='test') as __log__:
        virtual_module['generate_exception']()
    self.assertIn('', stream.getvalue())

class move_outdir(ContextTestCase):

  def setUpContext(self, stack):
    super().setUpContext(stack)
    tmpdir = stack.enter_context(tempfile.TemporaryDirectory())
    self.outdira = os.path.join(tmpdir, 'a')
    self.outdirb = os.path.join(tmpdir, 'b')
    os.mkdir(self.outdira)
    self.outdirfd = os.open(self.outdira, flags=os.O_RDONLY)
    stack.callback(os.close, self.outdirfd)

  def test(self):
    __outdirfd__ = self.outdirfd
    os.rename(self.outdira, self.outdirb)
    __verbose__ = len(nutils.log.LEVELS)
    stream = io.StringIO()
    with nutils.log.HtmlLog(stream, title='test') as __log__:
      generate_log()
    self.assertEqual(stream.getvalue(), log_html)


from keras.preprocessing.image import load_img,img_to_array,array_to_img
from mnist_model import mnist_model
from keras import backend as K
import numpy as np
import scipy
import os.path

def gradient_descent(x, iterations, step, min_loss=None):
    for i in range(iterations):
        outs = fetch_loss_and_grads([x])
        loss_value, grad_values=outs[0],outs[1]
        if min_loss is not None and loss_value <= min_loss:
            return x,i
            break
        print('..Loss value at', i, ':', loss_value)
        '''print('grad')
        print(grad_values)'''
        x -= step * grad_values

    return x,iterations

def save_img(img, fname):
    pil_img = np.copy(img)
    pil_img=pil_img.reshape(pil_img.shape[1],pil_img.shape[2])
    scipy.misc.imsave(fname, pil_img)

step=1e-3
iterations=1000
min_loss=0.1
model = mnist_model()

Input=model.input
preds=model.output

a=np.random.rand(1,28,28,1)
f_origin=a.copy()

for j in range(10):
    f=f_origin.copy()
    loss=1-preds[0][j]
    grads=K.gradients(loss,Input)[0]
    grads/=K.mean(grads)
    outputs=[loss,grads]
    fetch_loss_and_grads = K.function([Input], outputs)
    f,nums = gradient_descent(f,iterations=iterations,step=step,min_loss=min_loss)

    if nums<iterations:
        print('class: ',j)
        save_img(f, fname='../../result/mnist/noise_fool_'+str(j)+'.png')


import random
from pprint import pprint
from game import Clue, Play, Discard, Move

from game import Card, Tokens, Rules, Suit, Rank
from typing import NamedTuple, List, Tuple, Callable

def make_io_player(name: str) -> Callable:
    """
    Aur's basic IO player generator

    usage:
        h = Hanabi([make_io_player('Aur'), make_io_player('Ofer')])
    """
    def io_player(state: None, log: List[NamedTuple], hands: List[List[Card]],
                  rules: Rules, tokens: Tokens, slots: List[int],
                  discard_pile: List[List[int]]) -> Tuple[None, Move, str]:
        print(f"{name}'s turn")
        pprint(log[-len(hands):])
        pprint(hands)
        pprint(tokens)
        pprint(slots)
        pprint(discard_pile)
        print('What will it be?')
        print('[c]lue <player><type><n>\t\t[p]lay <card>\t\t[d]iscard <card>')
        move = input().split()
        try:
            if move[0] == 'c':
                _, (player, clue_type, param) = move
                return state, Clue.create(
                    int(player),
                    {'s': 'suit', 'n': 'rank'}[clue_type],
                    {'s': Suit, 'n': Rank}[clue_type].from_str(param)), ''
            elif move[0] == 'p':
                _, card_id = move
                return state, Play.create(int(card_id)), ''
            elif move[0] == 'd':
                _, card_id = move
                return state, Discard.create(int(card_id)), ''
            else:
                raise ValueError("not a valid move type")
        except (ValueError, KeyError):
            print('illegal move')
            return io_player(state, log, hands, rules, tokens, slots, discard_pile)
    return io_player

def random_player(state: None, log: List[NamedTuple], hands: List[List[Card]],
                  rules: Rules, tokens: Tokens, slots: List[int],
                  discard_pile: List[List[int]]) -> Tuple[None, Move, str]:
    """
    Tsvika and Ofer's random player

    Usage:
        h = Hanabi([random_player] * 3)
    """
    my_id = len(log) % len(hands)

    possible_actions = []
    if tokens.lives > 1:
        possible_actions.append(Play)

    if tokens.clues > 0:
        possible_actions.append(Clue)

    if tokens.clues < rules.max_tokens.clues:
        possible_actions.append(Discard)

    action = random.choice(possible_actions)

    if action == Play:
        return state, Play.create(random.choice(hands[my_id]).id), ''
    if action == Discard:
        return state, Discard.create(random.choice(hands[my_id]).id), ''
    if action == Clue:
        player = random.choice([i for i in range(len(hands)) if i != my_id])
        clue_type = random.choice(['suit', 'rank'])
        return state, Clue.create(
            player, clue_type,
            getattr(random.choice(hands[player]).data, clue_type)), ''


import argparse

def create_parser():
    parser = argparse.ArgumentParser(
        prog="srm",
        description="Smart rm",
        add_help=False,
    )

    parent_group = parser.add_argument_group(title="Params")

    parent_group.add_argument('--help', '-h', action='help', help='Help')

    subparsers = parser.add_subparsers(dest='command',
                                       title="Commands")

    # rm parser
    rm_parser = subparsers.add_parser('rm', add_help=False,
                                      help="rm [OPTION]... [FILE]...",
                                      description="Remove file or dir")

    rm_group = rm_parser.add_argument_group(title='Params')

    rm_group.add_argument("file", nargs='*')
    rm_group.add_argument("-r", "-R", "--recursive", action="store_const", const=True,
                          default=False, help="remove directories and their contents recursively")
    rm_group.add_argument("-i", "--interactive", action="store_const", const=True, default=False,
                          help="prompt before every removal")
    rm_group.add_argument("-d", "--directory", action="store_const", const=True, default=False,
                          help="remove empty directories")
    rm_group.add_argument("-f", "--force", action="store_const", const=True, default=False,
                          help="ignore nonexistent files and arguments, never prompt")
    rm_group.add_argument("-v", "--verbose", action="store_const", const=True, default=False,
                          help="explain what is being done")
    rm_group.add_argument("-s", "--silent", action="store_const", const=True, default=False,
                          help="silent")
    rm_group.add_argument("--dryrun", action="store_const", const=True, default=False,
                          help="dry-run")
    rm_group.add_argument('--help', '-h', action='help', help='Help')

    # trash parser
    trash_parser = subparsers.add_parser('trash', add_help=False,
                                         help="trash [OPTION]... [FILE]...",
                                         description="Remove and recovery file or dir")

    trash_parser.add_argument("file", nargs='*')
    trash_parser.add_argument("-r", "--recovery", action="store_const", const=True, default=False,
                              help="Recovery file or dir")
    trash_parser.add_argument("-d", "--delete", action="store_const", const=True, default=False,
                              help="Remove from trash file or dir")

    # config parser
    config_parser = subparsers.add_parser('cfg', add_help=False,
                                          help="cfg [SECTION]... [FIELD]... [VALUE]...",
                                          description="Assigns a value to the field")

    config_parser.add_argument("section", type=str)
    config_parser.add_argument("field", type=str)
    config_parser.add_argument("value", type=str)

    # show
    show_parser = subparsers.add_parser('show', add_help=False,
                                        help="show",
                                        description="Show files in trash")

    show_parser.add_argument("-f", "--force", action="store_const", const=True, default=False,
                             help="show full")

    return parser


from struct import pack

class StreamHandler():
    def __init__(self):
        self._length,self._lengthIndex,self._temp=0,0,b''
        self._readingLength=True
        self.chunk=b''
    def delimitedStream(self,chunk):
        self.chunk+=chunk
        while (len(self.chunk)):
            if self._readingLength:
                b=self.chunk[0]
                self._length+=(b & 0x7f) << (7 * self._lengthIndex)
                if (b & (1<<7)):
                    self._lengthIndex += 1
                    self._readingLength = True
                else:
                    self._lengthIndex=0
                    self._readingLength=False
                self.chunk=self.chunk[1:]
            else:
                if self._length<=len(self.chunk):
                    self._temp+=self.chunk[:self._length]
                    self.chunk=self.chunk[self._length:]
                    self._length=0
                    self._readingLength = True
                else:
                    # print ('break')
                    break
        return self._temp

def delimitedStream(chunk):
    _length,_lengthIndex,_temp=0,0,b''
    _readingLength=True
    while (len(chunk)):
        if _readingLength:
            b=chunk[0]
            _length+=(b & 0x7f) << (7 * _lengthIndex)
            if (b & (1<<7)):
                _lengthIndex += 1
                _readingLength = True
            else:
                _lengthIndex=0
                _readingLength=False
            chunk=chunk[1:]
        else:
            if _length<=len(chunk):
                _temp+=chunk[:_length]
                chunk=chunk[_length:]
                _length=0
                _readingLength = True
            else:
                # print ('break')
                break
    return _temp

def delimitingStream(chunk):
    _length=len(chunk)
    _temp=b''
    _b=[]
    while (_length > 0x7f):
        _b.append((1<<7)+(_length & 0x7f))
        _length>>=7
    _b.append(_length)
    _temp+=pack('<%sB'%len(_b),*_b)
    _temp+=chunk
    return _temp

if __name__=='__main__':
    from wire_pb2 import *
    n=StreamHandler()
    # test
    bbb=b"\xc1\x01\x10\x12\x1a\xbc\x01\x08\x00\x125\n\t\xe6\xb5\x8f\xe8\xa7\x88\xe5\x99\xa8\x12$com.android.browser/.BrowserActivity\x18\x00 \x01\x12;\n\x0c\xe6\x89\x8b\xe6\x9c\xba\xe7\x99\xbe\xe5\xba\xa6\x12'com.baidu.searchbox/.BoxBrowserActivity\x18\x00 \x00\x12D\n\x0c\xe6\x89\x8b\xe6\x9c\xba\xe6\xb7\x98\xe5\xae\x9d\x120com.taobao.taobao/com.taobao.tao.BrowserActivity\x18\x00 \x00"
    # bbb=b'\x04full\x12\x04good\x1a\x03usb d(d1\x00\x00\x00\x00\x00\x00@@9\x98n\x12\x83\xc0\xca\x10'
    bbb=b"+\x10\x0e\x1a'\n\x04full\x12\x04good\x1a\x03usb d(d1\x00\x00\x00\x00\x00\x00<@9\xcd\xcc\xcc\xcc\xcc\xcc\x10@\x12\x10\x0f\x1a\x0e\x08\x01\x12\x04WIFI\x1a\x00 \x00(\x00\x06\x10\x11\x1a\x02\x08\x00\x06\x10\r\x1a\x02\x08\x00"
    sss=delimitedStream(bbb)

    envelop=Envelope()
    envelop.ParseFromString(sss)
    print (envelop.type,envelop.message)
    # print (sss)


# -*- coding:utf-8 -*-
# @time:2017/8/12 0012 0:31
from collections import Counter
from math import log
import math
hmm_model = {i: Counter() for i in 'sbme'}

with open('dict_small.txt', encoding='utf-8') as f:
    for line in f:
        lines = line.split(' ')
        if len(lines[0]) == 1:
            #  single word
            hmm_model['s'][lines[0]] += int(lines[1])
        else:
            #  not single word
            hmm_model['b'][lines[0][0]] += int(lines[1])
            hmm_model['e'][lines[0][-1]] += int(lines[1])
            for m in lines[0][1:-1]:
                hmm_model['m'][m] += int(lines[1])
log_total = {i: log(sum(hmm_model[i].values())) for i in 'sbme'}
# for k, v in hmm_model.items():
#    print(k, v)
trans = {'ss': 0.3,
         'sb': 0.7,
         'bm': 0.3,
         'be': 0.7,
         'mm': 0.3,
         'me': 0.7,
         'es': 0.3,
         'eb': 0.7
         }
trans = {i: log(j) for i, j in trans.items()}

def viterbi(nodes):
    paths = nodes[0]
    for l in range(1, len(nodes)):
        paths_ = paths
        paths = {}
        for i in nodes[l]:
            nows = {}
            for j in paths_:
                if j[-1]+i in trans:
                    nows[j+i] = paths_[j]+nodes[l][i]+trans[j[-1]+i]
            k = list(nows.values()).index(max(list(nows.values())))
            paths[list(nows.keys())[k]] = list(nows.values())[k]
    return list(paths.keys())[list(paths.values()).index(max(list(paths.values())))]

def hmm_cut(s):
    nodes = [{i:log(j[t]+1)-log_total[i] for i, j in hmm_model.items()} for t in s]
    tags = viterbi(nodes)
    words = [s[0]]
    for i in range(1, len(s)):
        if tags[i] in ['b', 's']:
            words.append(s[i])
        else:
            words[-1] += s[i]
    return words

# print(' '.join(hmm_cut('今天天气不错')))
# print(' '.join(hmm_cut('李想是一个好孩子')))

print(' '.join(hmm_cut('你是直接用的他的模型还是用代码和训练数据集，还是说你这个只是从他论文上截图下来的')))
# print("小明 硕士 毕业 于 中 国科 学院 计算 所")
# print(' '.join(hmm_cut('已结婚的和尚未结婚的青年都要实行计划生育')))


import pytest
from . import MockRADPort
from radicl.api import RAD_API

class TestApi:
    @pytest.fixture()
    def mock_port(self, payload):
        yield MockRADPort(payload)

    @pytest.fixture()
    def mock_api(self, mock_port):
        return RAD_API(mock_port, debug=True)

    @pytest.mark.parametrize('payload, expected', [
        [b'\x9f\x01\x02\x00\x01\x03', 3]
    ])
    def test_getHWID(self, mock_api, payload, expected):
        ret = mock_api.getHWID()
        assert ret['data'] == expected

    @pytest.mark.parametrize('payload, expected', [
        [b'\x9f\x02\x02\x00\x01\x01', 1]
    ])
    def test_getHWREV(self, mock_api, payload, expected):
        ret = mock_api.getHWREV()
        assert ret['data'] == expected

    @pytest.mark.parametrize('payload, expected', [
        [b'\x9f\x03\x02\x00\x02\x01.', 1.46]
    ])
    def test_getFWREV(self, mock_api, payload, expected):
        ret = mock_api.getFWREV()
        assert ret['data'] == expected

    @pytest.mark.parametrize('payload, expected', [
        [b'\x9f\t\x02\x00\x04\x01.\x03\x00', '1.46.3.0']
    ])
    def test_getFullFWREV(self, mock_api, payload, expected):
        ret = mock_api.getFullFWREV()
        assert ret['data'] == expected


#!/usr/bin/env python
# -*- coding: iso-8859-15 -*-

# Compression of a sequence of images.

# Examples:
#
#  * Show default parameters:
#
#    mctf compress --help
#
#  * Compress using the default parameters:
#
#    mctf compress
#
#  * Using a GOP_size=4:
#
#    TRLs GOP_size
#    ---- --------
#       1        1
#       2        2
#       3        4
#       4        8
#       5       16
#       6       32
#       7       64
#
#    mctf compress --TRLs=3
#
#  * Controlling quantization:
#
#    mctf compress --quality=50

import sys
import os
from GOP import GOP
from subprocess import check_call
from subprocess import CalledProcessError
from arguments_parser import arguments_parser
import logging

logging.basicConfig()
log = logging.getLogger("compress")

parser = arguments_parser(description="Encodes a sequence of pictures into a MCJ2K stream")
parser.always_B()
parser.block_overlaping()
parser.block_size()
parser.border_size()
parser.GOPs()
parser.min_block_size()
parser.motion_layers()
parser.motion_quantization()
parser.motion_quantization_step()
parser.pixels_in_x()
parser.pixels_in_y()
parser.search_range()
parser.subpixel_accuracy()
parser.layers()
parser.quality()
#parser.quantization()
#parser.quantization_step()
#parser.quantization_max()
#parser.quantization_min()
parser.SRLs()
parser.TRLs()
parser.update_factor()

args = parser.parse_known_args()[0]
always_B = int(args.always_B)
block_overlaping = int(args.block_overlaping)
block_size = int(args.block_size)
min_block_size = int(args.min_block_size)
border_size = int(args.border_size)
GOPs = int(args.GOPs)
motion_layers = str(args.motion_layers); log.debug("motion_layers={}".format(motion_layers))
motion_quantization = str(args.motion_quantization); log.debug("motion_quantization={}".format(motion_quantization))
motion_quantization_step = str(args.motion_quantization_step); log.debug("motion_quantization_step={}".format(motion_quantization_step))
pixels_in_x = int(args.pixels_in_x)
pixels_in_y = int(args.pixels_in_y)
layers = str(args.layers)
quality = float(args.quality)
#quantization = str(args.quantization)
#quantization_step = str(args.quantization_step)
#quantization_max = int(args.quantization_max)
#quantization_min = int(args.quantization_min)
search_range = int(args.search_range)
subpixel_accuracy = int(args.subpixel_accuracy)
TRLs = int(args.TRLs)
SRLs = int(args.SRLs)
update_factor = float(args.update_factor)

MCTF_QUANTIZER       = os.environ["MCTF_QUANTIZER"]

if TRLs > 1:
    try:
        # Temporal analysis of image sequence. Temporal decorrelation.
        check_call("mctf analyze"
                   + " --always_B="          + str(always_B)
                   + " --block_overlaping="  + str(block_overlaping)
                   + " --block_size="        + str(block_size)
                   + " --min_block_size="    + str(min_block_size)
                   + " --border_size="       + str(border_size)
                   + " --GOPs="              + str(GOPs)
                   + " --pixels_in_x="       + str(pixels_in_x)
                   + " --pixels_in_y="       + str(pixels_in_y)
                   + " --search_range="      + str(search_range)
                   + " --subpixel_accuracy=" + str(subpixel_accuracy)
                   + " --TRLs="              + str(TRLs)
                   + " --update_factor="     + str(update_factor)
                   , shell=True)
    except CalledProcessError:
        sys.exit(-1)

    try:
        # Compress the fields of motion. A layer quality is used without loss.
        check_call("mctf motion_compress"
                   + " --block_size="               + str(block_size)
                   + " --GOPs="                     + str(GOPs)
                   + " --min_block_size="           + str(min_block_size)
                   + " --pixels_in_x="              + str(pixels_in_x)
                   + " --pixels_in_y="              + str(pixels_in_y)
                   + " --motion_layers="            + str(motion_layers)
                   + " --motion_quantization="      + str(motion_quantization)
                   + " --motion_quantization_step=" + str(motion_quantization_step)
                   + " --SRLs="                     + str(SRLs)
                   + " --TRLs="                     + str(TRLs)
                   , shell=True)
    except CalledProcessError:
        sys.exit(-1)

try:
    # Compressed textures. Quality layers are used, with loss.
    check_call("mctf texture_compress__"         + MCTF_QUANTIZER
               + " --GOPs="                      + str(GOPs)
               + " --pixels_in_x="               + str(pixels_in_x)
               + " --pixels_in_y="               + str(pixels_in_y)
               + " --layers="                    + str(layers)
#               + " --quantization="              + str(quantization)
#               + " --quantization_step="         + str(quantization_step)
#               + " --quantization_max="          + str(quantization_max)
#               + " --quantization_min="          + str(quantization_min)
               + " --quality="                   + str(quality)
               + " --SRLs="                      + str(SRLs)
               + " --TRLs="                      + str(TRLs)
               , shell=True)
except CalledProcessError:
    sys.exit(-1)


from collections import defaultdict
from functools import reduce
import json

class User:
    def __init__(self, data) -> None:
        self.name = data["name"]
        self.owes = defaultdict(float, data.get("owes", {}))
        self.owed_by = defaultdict(float, data.get("owed_by", {}))

    def __repr__(self) -> str:
        return f"<User name={self.name}, owes={self.owes}, owed_by={self.owed_by} balance={self.get_balance()}>"

    def get_balance(self, other_user=None):
        """
        compute balance for user, optionally filtering by relationship with a single other user
        """
        credits = [
            amount
            for name, amount
            in self.owed_by.items()
            if (other_user is None or other_user.name == name)
        ]

        debts = [
            amount
            for name, amount
            in self.owes.items()
            if (other_user is None or other_user.name == name)
        ]

        return sum(credits) - sum(debts)

    @property
    def data(self):
        return {
            "name": self.name,
            "owes": self.owes,
            "owed_by": self.owed_by,
            "balance": self.get_balance(),
        }
    def set_new_balance_with(self, other_user, amount):
        if amount > 0:
            # the other user owes this user money
            self.update_owed_by(other_user, amount)
            self.clear_owes(other_user)
        elif amount < 0:
            # this user owes money to the other user
            self.update_owes(other_user, amount)
            self.clear_owed_by(other_user)
        else:
            # the two users are even.
            self.clear_owed_by(other_user)
            self.clear_owes(other_user)

    def update_owed_by(self, other_user, amount):
        self.owed_by[other_user.name] = abs(amount)

    def clear_owed_by(self, other_user):
        """
        clear record of other_user owing user if exists
        """
        self.owed_by.pop(other_user.name, None)

    def update_owes(self, other_user, amount):
        self.owes[other_user.name] = abs(amount)

    def clear_owes(self, other_user):
        """
        clear record of user owing other_user if exists
        """
        self.owes.pop(other_user.name, None)

    def create_debt(self, lender, amount):
        """
        compute the new balance for both users and update
        """
        my_balance = self.get_balance(other_user=lender)
        my_new_balance = my_balance - amount

        other_balance = lender.get_balance(other_user=self)
        other_new_balance = other_balance + amount

        self.set_new_balance_with(other_user=lender, amount=my_new_balance)
        lender.set_new_balance_with(other_user=self, amount=other_new_balance)

class Database:
    """
    should use objects internally, but return dicts
    """
    def __init__(self, data) -> None:
        self.users = [
            User(data)
            for data in data["users"]
        ]

    def get_users(self, names_to_include=None):
        return sorted(
            (
                user
                for user
                in self.users
                if user.name in names_to_include
            ),
            key=(lambda user: user.name)
        )

    def create_user(self, user_name):
        user = User({"name": user_name})
        self.users.append(user)
        return user

    def create_iou(self, iou):
        """
        example: {"lender": "Adam", "borrower": "Bob", "amount": 3.0}
        """

        """
        TODO: the net balance between users should be shown.
        Instead of {'name': 'Adam', 'owes': {'Bob': 3.0}, 'owed_by': {'Bob': 2.0}, 'balance': -1.0}
        show {'name': 'Adam', 'owes': {'Bob': 1.0}, 'owed_by': {}, 'balance': -1.0}
        """
        amount = iou["amount"]
        lender = next(
            user
            for user in self.users
            if user.name == iou["lender"]
        )

        borrower = next(
            user
            for user in self.users
            if user.name == iou["borrower"]
        )

        borrower.create_debt(lender=lender, amount=amount)
        return self.get_users([lender.name, borrower.name])

class RestAPI:
    def __init__(self, database=None):
        self.database = Database(database)

    def get(self, url, payload=None):
        if url == "/users":
            return self.get_users(payload)

    def get_users(self, payload):
        if payload:
            names_to_include = json.loads(payload)["users"]
            users = self.database.get_users(names_to_include)
        else:
            users = self.database.get_users()

        return json.dumps({
            "users": [u.data for u in users]
        })

    def post(self, url, payload=None):
        if url == "/add":
            user_name = json.loads(payload)["user"]
            user = self.database.create_user(user_name)
            return json.dumps(user.data)
        if url == "/iou":
            iou_payload = json.loads(payload)
            users = self.database.create_iou(iou_payload)
            return json.dumps({
                "users": [u.data for u in users]
            })


from src.dog import Dog
from src.person import Person

barker = Dog(4, 74)
print(barker.age)

greeter = 'Elon Musk'
test = Person('test', 23)
test.greets(greeter)

print(f'Hello, {greeter}! My name is {test.name}, nice to meet '\
f'you!')


import ast
import os
from collections.abc import Mapping
from pkg_resources import resource_stream

import yaml

ENV_VAR_PREFIX = "SURROUND_"

class Config(Mapping):

    def __init__(self, project_root=None):
        self._storage = self.__load_defaults()

        # Set framework paths
        if project_root:
            self._storage["project_root"] = project_root
            self._storage["output_path"] = os.path.join(project_root, "output")
            self._storage["data_path"] = os.path.join(project_root, "data")
            self._storage["models_path"] = os.path.join(project_root, "models")

            # Load project config
            config_path = os.path.join(project_root, os.path.basename(project_root), 'config.yaml')
            if os.path.exists(config_path):
                self.read_config_files([config_path])

    def read_config_files(self, yaml_files):
        configs = []
        try:
            for path in yaml_files:
                with open(path) as afile:
                    configs.append(yaml.safe_load(afile.read()))
        except IOError as err:
            err.strerror = 'Unable to load configuration file (%s)' % err.strerror
            raise

        self.__merge_configs(configs)
        self.__insert_environment_variables()
        return True

    def read_from_dict(self, config_dict):
        if not isinstance(config_dict, dict):
            return TypeError("config_dict should be a dict")

        self.__merge_configs([config_dict])
        self.__insert_environment_variables()
        return True

    def get_path(self, path):
        if not isinstance(path, str):
            raise TypeError("path should be a string")
        if not "." in path:
            return self._storage[path]
        return self.__iterate_over_dict(self._storage, path.split("."))

    def __load_defaults(self):
        try:
            with resource_stream(__name__, "defaults.yaml") as f:
                config = yaml.safe_load(f)
        except IOError as err:
            err.strerror = 'Unable to load default config file'
            raise
        return config

    def __merge_configs(self, configs):
        """ Merges a list of dictionaries into the dictionary of this class. Note that lists are
        overriden completely not extended.
        """
        if not isinstance(configs, list):
            raise TypeError("configs should be a list")

        def extend_dict(target, src):
            if isinstance(src, dict):
                for k, v in src.items():
                    if k in target:
                        if isinstance(target[k], dict):
                            extend_dict(target[k], v)
                        else:
                            target[k] = v
                    else:
                        target[k] = v

        for config in configs:
            extend_dict(self._storage, config)

    def __insert_environment_variables(self):
        for var in os.environ:
            if not var.startswith(ENV_VAR_PREFIX) or len(var) == len(ENV_VAR_PREFIX):
                continue
            surround_variables = [n.lower() for n in var[len(ENV_VAR_PREFIX):].split("_") if n]
            self.__override_or_add_var(self._storage, surround_variables, os.getenv(var))

    def __override_or_add_var(self, config, key_list, value):
        if len(key_list) > 1:
            key = key_list[0]
            if not key in config:
                config[key] = dict()
            self.__override_or_add_var(config[key], key_list[1:], value)
        else:
            new_key = key_list[0]
            if new_key in config:
                the_type = type(config[new_key])
            else:
                the_type = type(ast.literal_eval(value))
            config[new_key] = the_type(value)
        return config

    def __iterate_over_dict(self, dictionary, key_list):
        key = key_list[0] if not key_list == [] else ""
        if key in dictionary:
            if len(key_list) > 1:
                return self.__iterate_over_dict(dictionary[key], key_list[1:])
            return dictionary[key]
        return None

    def __getitem__(self, key):
        return self._storage[key]

    def __iter__(self):
        return iter(self._storage)

    def __len__(self):
        return len(self._storage)


from .base import *

DEBUG = False

DATABASES['default']['HOST'] = '172.18.0.90'

ALLOWED_HOSTS += [
    '89.223.29.100',
    'filebox.sdubovitsky.tk'
]

STATIC_ROOT = os.path.join(BASE_DIR, '/dockershare/filebox/files/static/')
MEDIA_ROOT = os.path.join(BASE_DIR, '/dockershare/filebox/files/media/')

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'files/static_dev/'),
)

UPLOAD_DIR = MEDIA_ROOT + 'uploads/'


from tkinter import *
from random import *

root = Tk()

canvas = Canvas(root, width='300', height='300', background="black")
canvas.pack()

# draw the night sky:
# - The background should be black
# - The stars should be small squares
# - The stars should have random positions on the canvas
# - The stars should have random color (some shade of grey)

def night_sky(N):
    for x in range(N):
        x = randint(5, 300)
        y = randint(5, 300)
        color = choice(['lightgrey', 'grey', 'silver', 'lightslategrey'])
        canvas.create_rectangle(x, y, x+5, y+5, fill=color)

night_sky(100)

root.mainloop()


#!/usr/bin/env python

# dictionary of residue names and their corresponding chemical names as they will be dispalyed in the table
res_to_name = {}
res_to_name["ACN"] = "Acetamide"
res_to_name["ACH"] = "Acetic Acid"
res_to_name["ATO"] = "Acetone"
res_to_name["BUT"] = "Butanol"
res_to_name["DMF"] = "Dimethyl Formamide"
res_to_name["DMS"] = "Dimethyl Sulfoxide"
res_to_name["DMP"] = "2,3-Dimercapto-1-propanol"
res_to_name["EAC"] = "Ethyl Acetate"
res_to_name["ETH"] = "Ethanol"
res_to_name["GCL"] = "Ethylene Glycol"
res_to_name["GLY"] = "Glycerol"
res_to_name["MET"] = "Methanol"
res_to_name["PR"] = "Propanol"
res_to_name["PCB"] = "Propylene Carbonate"
res_to_name["PG"] = "Propylene Glycol"
res_to_name["RIB"] = "Ribose"
res_to_name["SOH"] = "Mercaptoethanol"
res_to_name["TET"] = "Tetrose"
res_to_name["THF"] = "Tetrahydrofuran"
res_to_name["URE"] = "Urea"

abbreviation = {}
abbreviation["Acetamide"] = "AcN"
abbreviation["ACN"] = "AcN"
abbreviation["Acetic Acid"] = "AcOH"
abbreviation["ACH"] = "AcOH"
abbreviation["Acetone"] = "ACE"
abbreviation["ATO"] = "ACE"
abbreviation["Butanol"] = "BuOH"
abbreviation["BUT"] = "BuOH"
abbreviation["Dimethyl Formamide"] = "DMF"
abbreviation["DMF"] = "DMF"
abbreviation["Dimethyl Sulfoxide"] = "DMSO"
abbreviation["DMS"] = "DMSO"
abbreviation["2,3-Dimercapto-1-propanol"] = "DMP"
abbreviation["DMP"] = "DMP"
abbreviation["Ethyl Acetate"] = "EAC"
abbreviation["EAC"] = "EAC"
abbreviation["Ethanol"] = "EtOH"
abbreviation["ETH"] = "EtOH"
abbreviation["Ethylene Glycol"] = "EG"
abbreviation["GCL"] = "EG"
abbreviation["Glycerol"] = "GLY"
abbreviation["GLY"] = "GLY"
abbreviation["Methanol"] = "MeOH"
abbreviation["MET"] = "MeOH"
abbreviation["Propanol"] = "PrOH"
abbreviation["PR"] = "PrOH"
abbreviation["Propylene Carbonate"] = "PC"
abbreviation["PCB"] = "PC"
abbreviation["Propylene Glycol"] = "PG"
abbreviation["PG"] = "PG"
abbreviation["Ribose"] = "RIB"
abbreviation["RIB"] = "RIB"
abbreviation["Mercaptoethanol"] = "ME"
abbreviation["SOH"] = "ME"
abbreviation["Tetrose"] = "TET"
abbreviation["TET"] = "TET"
abbreviation["Tetrahydrofuran"] = "THF"
abbreviation["THF"] = "THF"
abbreviation["Urea"] = "URE"
abbreviation["URE"] = "URE"

colors = ['blue', 'red', 'xkcd:green', 'xkcd:orange', 'xkcd:goldenrod']
color_dict = {}
color_dict['MET'] = colors[0]
color_dict['ETH'] = colors[0]
color_dict['PR'] = colors[0]
color_dict['BUT'] = colors[0]
color_dict['GCL'] = colors[1]
color_dict['PG'] = colors[1]
color_dict['GLY'] = colors[1]
color_dict['TET'] = colors[1]
color_dict['RIB'] = colors[1]
color_dict['ACH'] = colors[2]
color_dict['URE'] = colors[2]
color_dict['ACN'] = colors[2]
color_dict['ATO'] = colors[2]
color_dict['SOH'] = colors[3]
color_dict['DMP'] = colors[3]
color_dict['DMS'] = colors[3]
color_dict['THF'] = colors[4]
color_dict['PCB'] = colors[4]
color_dict['EAC'] = colors[4]
color_dict['DMF'] = colors[4]


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

import time
import configparser

path="D:\\python_update_test\\"
dir_list=["hdx_server_2","hdx_server_3"]

#server_list=["BackServer","DBServer","GameServer","GateServer","LogServer","ValidateServer"]
server_list=["ValidateServer","DBServer","GameServer","GateServer"]

for dir in dir_list:
    for server in server_list:
        #print(path+dir+"\\"+server+"\\win32\\ServerConfig.ini")
        fullconfigpath=path+dir+"\\"+server+"\\win32\\ServerConfig.ini"

        if server=="ValidateServer":
            config = configparser.ConfigParser()
            config.read(fullconfigpath)
            config.set("MysqlDBConfig","sql_var_pool_count","300000")
            config.set("MysqlDBConfig","sql_blob_var_pool","2000")
            config.set("MysqlDBConfig","sql_Execute_pool_count","6000")
            config.set("MysqlDBConfig","sql_Execute_noreturn_pool_count","2")
            config.write(open(fullconfigpath, "r+"))
        elif server=="DBServer":
            print('oh no,你猜的太低了')
        elif server=="GameServer":
            print('oh no,你猜的太低了')
        elif server=="GateServer":
            print('oh no,你猜的太低了')

#这个方法不行，会把注释全部清除掉。

'''
config = configparser.ConfigParser()

config.read('config_file.ini')
section = input("please enter section:")
key = input("please enter key:")
value = input("please enter value:")

config.set(section,key,value)
#config.set("MYSQLD", "md5", "fuck you..........")
#第一个是中括号里面的，第二个是key，第三个是需要修改的新值

config.write(open('config_file.ini', "r+"))
config.clear()
print("配置文件更新成功，3秒后自动退出...")
time.sleep(3)'''


N = int(input())
prime = []
nl = [True] * (N+1)
nl[0] = nl[1] = False # 소수 아님

for i in range(2,N+1):
    if nl[i] == True:
        j = 2
        k = j*i
        while( k <= N ):
            nl[k] = False
            j = j + 1
            if k > N:
                break
            k = j*i

for i in range(2,N+1):
    if nl[i] == True:
        prime.append(i)

start, end = -1, -1
answer, s = 0 ,0
le = len(prime)
while(True):
    if start == le or (end == le):
        break
    else:
        if s >= N :
            if s == N:
                answer += 1
            start += 1
            s = s - prime[start]

        elif s < N:
            end += 1
            if end != le:
                s = s + prime[end]

print(answer)


from abc import abstractmethod
from typing import Any, Callable, List, Optional, Tuple, Union

import numpy as np
import gym
from tqdm import trange

from ..base import ImplBase, LearnableBase
from ..logger import D3RLPyLogger
from ..dataset import Transition
from ..online.buffers import Buffer
from ..online.explorers import Explorer
from ..preprocessing.stack import StackedObservation
from ..metrics.scorer import evaluate_on_environment
from ..argument_utility import ScalerArg

class AlgoImplBase(ImplBase):
    @abstractmethod
    def build(self) -> None:
        pass

    @abstractmethod
    def save_policy(self, fname: str, as_onnx: bool) -> None:
        pass

    @abstractmethod
    def predict_best_action(
        self, x: Union[np.ndarray, List[Any]]
    ) -> np.ndarray:
        pass

    @abstractmethod
    def predict_value(
        self,
        x: Union[np.ndarray, List[Any]],
        action: Union[np.ndarray, List[Any]],
        with_std: bool,
    ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
        pass

    @abstractmethod
    def sample_action(self, x: Union[np.ndarray, List[Any]]) -> np.ndarray:
        pass

class DataGenerator:
    def generate(
        self, algo: "AlgoBase", transitions: List[Transition]
    ) -> List[Transition]:
        pass

class AlgoBase(LearnableBase):

    _generator: Optional[DataGenerator]
    _impl: Optional[AlgoImplBase]

    def __init__(
        self,
        batch_size: int,
        n_frames: int,
        n_steps: int,
        gamma: float,
        scaler: ScalerArg,
        generator: Optional[DataGenerator],
    ):
        super().__init__(batch_size, n_frames, n_steps, gamma, scaler)
        self._generator = generator

    def save_policy(self, fname: str, as_onnx: bool = False) -> None:
        """Save the greedy-policy computational graph as TorchScript or ONNX.

        .. code-block:: python

            # save as TorchScript
            algo.save_policy('policy.pt')

            # save as ONNX
            algo.save_policy('policy.onnx', as_onnx=True)

        The artifacts saved with this method will work without d3rlpy.
        This method is especially useful to deploy the learned policy to
        production environments or embedding systems.

        See also

            * https://pytorch.org/tutorials/beginner/Intro_to_TorchScript_tutorial.html (for Python).
            * https://pytorch.org/tutorials/advanced/cpp_export.html (for C++).
            * https://onnx.ai (for ONNX)

        Args:
            fname: destination file path.
            as_onnx: flag to save as ONNX format.

        """
        assert self._impl is not None
        self._impl.save_policy(fname, as_onnx)

    def predict(self, x: Union[np.ndarray, List[Any]]) -> np.ndarray:
        """Returns greedy actions.

        .. code-block:: python

            # 100 observations with shape of (10,)
            x = np.random.random((100, 10))

            actions = algo.predict(x)
            # actions.shape == (100, action size) for continuous control
            # actions.shape == (100,) for discrete control

        Args:
            x: observations

        Returns:
            greedy actions

        """
        assert self._impl is not None
        return self._impl.predict_best_action(x)

    def predict_value(
        self,
        x: Union[np.ndarray, List[Any]],
        action: Union[np.ndarray, List[Any]],
        with_std: bool = False,
    ) -> Union[np.ndarray, Tuple[np.ndarray, np.ndarray]]:
        """Returns predicted action-values.

        .. code-block:: python

            # 100 observations with shape of (10,)
            x = np.random.random((100, 10))

            # for continuous control
            # 100 actions with shape of (2,)
            actions = np.random.random((100, 2))

            # for discrete control
            # 100 actions in integer values
            actions = np.random.randint(2, size=100)

            values = algo.predict_value(x, actions)
            # values.shape == (100,)

            values, stds = algo.predict_value(x, actions, with_std=True)
            # stds.shape  == (100,)

        Args:
            x: observations
            action: actions
            with_std: flag to return standard deviation of ensemble
                estimation. This deviation reflects uncertainty for the given
                observations. This uncertainty will be more accurate if you
                enable ``bootstrap`` flag and increase ``n_critics`` value.

        Returns:
            predicted action-values

        """
        assert self._impl is not None
        return self._impl.predict_value(x, action, with_std)

    def sample_action(self, x: Union[np.ndarray, List[Any]]) -> np.ndarray:
        """Returns sampled actions.

        The sampled actions are identical to the output of `predict` method if
        the policy is deterministic.

        Args:
            x: observations.

        Returns:
            sampled actions.

        """
        assert self._impl is not None
        return self._impl.sample_action(x)

    def fit_online(
        self,
        env: gym.Env,
        buffer: Buffer,
        explorer: Optional[Explorer] = None,
        n_steps: int = 1000000,
        n_steps_per_epoch: int = 10000,
        update_interval: int = 1,
        update_start_step: int = 0,
        eval_env: Optional[gym.Env] = None,
        eval_epsilon: float = 0.0,
        save_metrics: bool = True,
        experiment_name: Optional[str] = None,
        with_timestamp: bool = True,
        logdir: str = "d3rlpy_logs",
        verbose: bool = True,
        show_progress: bool = True,
        tensorboard: bool = True,
    ) -> None:
        """Start training loop of online deep reinforcement learning.

        Args:
            env: gym-like environment.
            buffer : replay buffer.
            explorer: action explorer.
            n_steps: the number of total steps to train.
            n_steps_per_epoch: the number of steps per epoch.
            update_interval: the number of steps per update.
            update_start_step: the steps before starting updates.
            eval_env: gym-like environment. If None, evaluation is skipped.
            eval_epsilon: :math:`\\epsilon`-greedy factor during evaluation.
            save_metrics: flag to record metrics. If False, the log
                directory is not created and the model parameters are not saved.
            experiment_name: experiment name for logging. If not passed,
                the directory name will be ``{class name}_online_{timestamp}``.
            with_timestamp: flag to add timestamp string to the last of
                directory name.
            logdir: root directory name to save logs.
            verbose: flag to show logged information on stdout.
            show_progress: flag to show progress bar for iterations.
            tensorboard: flag to save logged information in tensorboard
                (additional to the csv data)

        """
        # setup logger
        if experiment_name is None:
            experiment_name = self.__class__.__name__ + "_online"

        logger = D3RLPyLogger(
            experiment_name,
            save_metrics=save_metrics,
            root_dir=logdir,
            verbose=verbose,
            tensorboard=tensorboard,
            with_timestamp=with_timestamp,
        )

        self._active_logger = logger

        observation_shape = env.observation_space.shape
        is_image = len(observation_shape) == 3

        # prepare stacked observation
        if is_image:
            stacked_frame = StackedObservation(observation_shape, self.n_frames)
            n_channels = observation_shape[0]
            image_size = observation_shape[1:]
            observation_shape = (n_channels * self.n_frames, *image_size)

        # setup algorithm
        if self._impl is None:
            self.build_with_env(env)

        # save hyperparameters
        self._save_params(logger)
        batch_size = self.batch_size

        # switch based on show_progress flag
        xrange = trange if show_progress else range

        # setup evaluation scorer
        eval_scorer: Optional[Callable[..., float]]
        if eval_env:
            eval_scorer = evaluate_on_environment(
                eval_env, epsilon=eval_epsilon
            )
        else:
            eval_scorer = None

        # start training loop
        observation, reward, terminal = env.reset(), 0.0, False
        for total_step in xrange(n_steps):
            with logger.measure_time("step"):
                # stack observation if necessary
                if is_image:
                    stacked_frame.append(observation)
                    fed_observation = stacked_frame.eval()
                else:
                    observation = observation.astype("f4")
                    fed_observation = observation

                # sample exploration action
                with logger.measure_time("inference"):
                    if explorer:
                        action = explorer.sample(
                            self, fed_observation, total_step
                        )
                    else:
                        action = self.sample_action([fed_observation])[0]

                # store observation
                buffer.append(observation, action, reward, terminal)

                # get next observation
                if terminal:
                    observation, reward, terminal = env.reset(), 0.0, False
                    # for image observation
                    if is_image:
                        stacked_frame.clear()
                else:
                    with logger.measure_time("environment_step"):
                        observation, reward, terminal, _ = env.step(action)

                # psuedo epoch count
                epoch = total_step // n_steps_per_epoch

                if total_step > update_start_step and len(buffer) > batch_size:
                    if total_step % update_interval == 0:
                        # sample mini-batch
                        with logger.measure_time("sample_batch"):
                            batch = buffer.sample(
                                batch_size=batch_size,
                                n_frames=self._n_frames,
                                n_steps=self._n_steps,
                                gamma=self._gamma,
                            )

                        # update parameters
                        with logger.measure_time("algorithm_update"):
                            loss = self.update(epoch, total_step, batch)

                        # record metrics
                        for name, val in zip(self._get_loss_labels(), loss):
                            if val:
                                logger.add_metric(name, val)

            if epoch > 0 and total_step % n_steps_per_epoch == 0:
                # evaluation
                if eval_scorer:
                    logger.add_metric("evaluation", eval_scorer(self))

                # save metrics
                logger.commit(epoch, total_step)
                logger.save_model(total_step, self)

    def _generate_new_data(
        self, transitions: List[Transition]
    ) -> List[Transition]:
        new_data = []
        if self._generator:
            new_data += self._generator.generate(self, transitions)
        return new_data


import math, sys

class HexGrid:
    # this class supports either "flat_on_top" or "pointy_on_top" schemes, set during initialization

    # see also https://www.redblobgames.com/grids/hexagons/
    FLAT_ON_TOP = "flat_on_top"
    """
    FLAT_ON_TOP means the flat edge of the hex is on the top and the farthest left/right bits are pointy. Uses
    this grid positioning (odd-q)

    [0,0]  [1,0]
       [1,0]
    [0,1]  [1,1]
       [1,1]
    [0,2]  [1,2]
    """

    POINTY_ON_TOP = "pointy_on_top" # NOTE - not yet supported!!!
    """
    POINTY_ON_TOP means the flat edge of the hex is on the side and the farthest top/down bits are pointy. Uses
    this grid positioning (odd-r):

    [0,0]  [1,0]
       [0,1]  [1,1]
    [0,2]  [1,2]
       [0,3]  [1,3]
    [0,4]  [1,4]
    """

    def __init__(self, render_mode, radius, padding):
        self.radius = radius
        self.drawable_radius = radius - padding
        self.render_mode = render_mode

        self.point_to_point = radius*2

        if self.render_mode == HexGrid.FLAT_ON_TOP:
            self.half_hex_height = math.sqrt(radius*radius - ((radius/2) * (radius/2)))
            self.hex_height = self.half_hex_height * 2
        elif self.render_mode == HexGrid.POINTY_ON_TOP:
            self.half_hex_width = math.sqrt(radius*radius - ((radius/2) * (radius/2)))
            self.hex_width = self.half_hex_width * 2
        else:
            print("only flat allowed!")
            sys.exit(1)

    # pass in something like 4,2 and get back x/y coordinates. render_mode will affect this so decide early!
    def get_origin_for_coords(self, x_grid_slot, y_grid_slot):
        X_OFFSET = -95
        if self.render_mode == HexGrid.FLAT_ON_TOP:
            # normal (even x-vals) columns have x positions of radius, radius + radius*3, radius + radius*6, etc.
            # normal (even x-vals) columns have y positions of radius + (hex_height*y_idx)

            # offset (odd x-vals) columns have x positions of 2.5radius, 5.5 radius, 8.5 radius, etc.
            # offset columns have y positions of radius+half_hex_height+(hex_height*y_idx)

            if x_grid_slot % 2 == 0:
                return (self.radius + (self.radius * (x_grid_slot/2) * 3) + X_OFFSET, self.radius + (self.hex_height * y_grid_slot))
            else:
                return ((2.5 * self.radius) + (self.radius * ((x_grid_slot-1)/2) * 3) + X_OFFSET, self.radius + self.half_hex_height + (self.hex_height * y_grid_slot))
        elif self.render_mode == HexGrid.POINTY_ON_TOP:
            x_pos = self.radius + (x_grid_slot * self.hex_width) + (self.half_hex_width if y_grid_slot % 2 == 1 else 0)
            x_pos += X_OFFSET
            distance_between_dots = self.radius + self.radius/2
            y_pos = (self.radius) + (y_grid_slot * distance_between_dots)
            return (x_pos, y_pos)

    def generate_bordered_hex_svg_at_slot(self, slot_info, border_color, fill_color, border_amount, radius = None):
        if radius is None:
            radius = self.drawable_radius

        back_hex = self.generate_hex_svg_at_slot(slot_info, { "fill": border_color }, radius)
        front_hex = self.generate_hex_svg_at_slot(slot_info, { "fill": fill_color }, radius - border_amount)

        return back_hex + front_hex

    def generate_colored_hex_svg_at_slot(self, slot_info, color, radius = None):
        return self.generate_hex_svg_at_slot(slot_info, { "fill": color }, radius)

    def generate_hex_svg_at_slot(self, slot_info, styling, radius = None):
        if radius is None:
            radius = self.drawable_radius

        origin_x, origin_y = self.get_origin_for_coords(slot_info[0], slot_info[1])
        return self.raw_draw(origin_x, origin_y, styling, radius)

    def raw_draw(self, origin_x, origin_y, styling, radius):
        if self.render_mode == HexGrid.FLAT_ON_TOP:
            top_y = origin_y - (math.sqrt(3) * radius) / 2
            bottom_y = origin_y + (math.sqrt(3) * radius) / 2
            points = [
                (origin_x + radius, origin_y),
                (origin_x + radius / 2, bottom_y),
                (origin_x - radius / 2, bottom_y),
                (origin_x - radius, origin_y),
                (origin_x - radius / 2, top_y),
                (origin_x + radius / 2, top_y)
            ]
        elif self.render_mode == HexGrid.POINTY_ON_TOP:
            left_x = origin_x - (math.sqrt(3) * radius) / 2
            right_x = origin_x + (math.sqrt(3) * radius) / 2
            points = [
                (origin_x, origin_y - radius),
                (right_x, origin_y - radius/2),
                (right_x, origin_y + radius/2),
                (origin_x, origin_y + radius),
                (left_x, origin_y + radius/2),
                (left_x, origin_y - radius/2),
            ]

        points_as_string = ""
        for p in points:
            if points_as_string != "":
                points_as_string += " "
            points_as_string += f"{p[0]},{p[1]}"

        style = ""
        for key in styling:
            if style != "":
                style += "; "
            style += f"{key}: {styling[key]}"

        rv = f'<polygon style="{style}" points="{points_as_string}"></polygon>\n'
        return rv


# -*- coding: utf-8 -*-
"""
Created on Sun Feb  2 16:31:22 2020

@author: laura
"""

#%% IMPORTS
import operator
#%% DATA
data = []
with(open("Data - Day02.txt", "r")) as file:
    for line in file:
        data.append(line)
#%% GOAL 1
"""You arrive at Easter Bunny Headquarters under cover of darkness. However, you left in such a rush that you forgot to use the bathroom! Fancy office buildings like this one usually have keypad locks on their bathrooms, so you search the front desk for the code.

"In order to improve security," the document you find says, "bathroom codes will no longer be written down. Instead, please memorize and follow the procedure below to access the bathrooms."

The document goes on to explain that each button to be pressed can be found by starting on the previous button and moving to adjacent buttons on the keypad: U moves up, D moves down, L moves left, and R moves right. Each line of instructions corresponds to one button, starting at the previous button (or, for the first line, the "5" button); press whatever button you're on at the end of each line. If a move doesn't lead to a button, ignore it.

You can't hold it much longer, so you decide to figure out the code as you walk to the bathroom. You picture a keypad like this:

1 2 3
4 5 6
7 8 9

Your puzzle input is the instructions from the document you found at the front desk. What is the bathroom code?
"""

#%% SOLUTION 1
instructions = []
for i in range(len(data)):
    instructions.append(list(data[i]))

def FindLoc(instructions):
    location = [0,0]
    locations = []
    for i in instructions:
        for j in i:
            if j == "U":
                move = [0, 1]
            elif j == "R":
                move = [1, 0]
            elif j == "L":
                move = [-1, 0]
            elif j == "D":
                move = [0, -1]

            location = list(map(operator.add, location, move))

            if location[0] == 2:
                location[0] = 1
            elif location[0] == -2:
                location[0] = -1

            if location[1] == 2:
                location[1] = 1
            elif location[1] == -2:
                location[1] = -1

        locations.append(location)
    return locations

def ConvertLoc(locs):
    numbers = []
    for i in locs:
        if i == [-1, 1]:
            n = 1
        elif i == [0, 1]:
            n = 2
        elif i == [1, 1]:
            n = 3
        elif i == [-1, 0]:
            n = 4
        elif i == [0, 0]:
            n = 5
        elif i == [1, 0]:
            n = 6
        elif i == [-1, -1]:
            n = 7
        elif i == [0, -1]:
            n = 8
        elif i == [1, -1]:
            n = 9
        numbers.append(str(n))
    print("code:", "".join(numbers))

ConvertLoc(FindLoc(instructions))

#%% GOAL 2
"""You finally arrive at the bathroom (it's a several minute walk from the lobby so visitors can behold the many fancy conference rooms and water coolers on this floor) and go to punch in the code. Much to your bladder's dismay, the keypad is not at all like you imagined it. Instead, you are confronted with the result of hundreds of man-hours of bathroom-keypad-design meetings:

    1
  2 3 4
5 6 7 8 9
  A B C
    D

So, given the actual keypad layout, the code would be 5DB3.

Using the same instructions in your puzzle input, what is the correct bathroom code?
"""

#%% SOLUTION 2
def FindLoc2(instructions, location = [-2, 0]):

    locations = []
    for i in instructions:
        for j in i:
            if j == "U" and location not in [[-2,0], [-1,1], [0,2], [1,1], [2,0]]:
                move = [0, 1]
            elif j == "R" and location not in [[0,2], [1,1], [2,0], [1,-1], [0,-2]]:
                move = [1, 0]
            elif j == "L" and location not in [[0,2], [-1,1], [-2,0], [-1,-1], [0, -2]]:
                move = [-1, 0]
            elif j == "D" and location not in [[-2,0], [-1,-1], [0,-2], [1,-1], [2,0]]:
                move = [0, -1]
            else:
                move = [0,0]

            location = list(map(operator.add, location, move))

        locations.append(location)

    return locations

def ConvertLoc2(locs):
    numbers = []
    for i in locs:
        if i == [0, 2]:
            n = 1
        elif i == [-1, 1]:
            n = 2
        elif i == [0, 1]:
            n = 3
        elif i == [1, 1]:
            n = 4
        elif i == [-2, 0]:
            n = 5
        elif i == [-1, 0]:
            n = 6
        elif i == [0, 0]:
            n = 7
        elif i == [1, 0]:
            n = 8
        elif i == [2, 0]:
            n = 9
        elif i == [-1, -1]:
            n = "A"
        elif i == [0, -1]:
            n = "B"
        elif i == [1, -1]:
            n = "C"
        elif i == [0, -2]:
            n = "D"
        else:
            n = "unknown"
        numbers.append(str(n))
    print("code2:", "".join(numbers))

ConvertLoc2(FindLoc2(instructions))