################################################################################
#  Licensed to the Apache Software Foundation (ASF) under one
#  or more contributor license agreements.  See the NOTICE file
#  distributed with this work for additional information
#  regarding copyright ownership.  The ASF licenses this file
#  to you under the Apache License, Version 2.0 (the
#  "License"); you may not use this file except in compliance
#  with the License.  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing, software
#  distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
# limitations under the License.
################################################################################

import signal
import sys
import time
import threading

import random
import uuid

from kafka.errors import NoBrokersAvailable

from entities_pb2 import Transaction, ConfirmFraud, CustomThreshold

from kafka import KafkaProducer
from kafka import KafkaConsumer

KAFKA_BROKER = "kafka-broker:9092"
PREFIXES = [
    "active", "arc", "auto", "app", "avi", "base", "co", "con", "core", "clear", "en", "echo",
    "even", "ever", "fair", "go", "high", "hyper", "in", "inter", "iso", "jump", "live", "make",
    "mass", "meta", "matter", "omni", "on", "one", "open", "over", "out", "re", "real", "peak",
    "pure", "shift", "silver", "solid", "spark", "start", "true", "up", "vibe"
]

WORD_SUFFIXES = [
    "arc", "atlas", "base", "bay", "boost", "capsule", "case", "center", "cast", "click", "dash",
    "deck", "dock", "dot", "drop", "engine", "flow", "glow", "grid", "gram", "graph", "hub",
    "focus", "kit", "lab", "level", "layer", "light", "line", "logic", "load", "loop", "ment",
    "method", "mode", "mark", "ness", "now", "pass", "port", "post", "press", "prime", "push",
    "rise", "scape", "scale", "scan", "scout", "sense", "set", "shift", "ship", "side", "signal",
    "snap", "scope", "space", "span", "spark", "spot", "start", "storm", "stripe", "sync", "tap",
    "tilt", "ture", "type", "view", "verge", "vibe", "ware", "yard", "up"
]

def random_transaction():
    """Generate infinite sequence of random Transactions."""
    while True:
        transaction = Transaction()
        transaction.account = "0x%08X" % random.randint(0x100000, 0x1000000)
        transaction.merchant = random.choice(PREFIXES).capitalize() + random.choice(WORD_SUFFIXES)
        transaction.amount = random.randint(1, 1000)
        transaction.timestamp.seconds = int(time.time())

        yield transaction

def produce():
    if len(sys.argv) == 2:
        delay_seconds = int(sys.argv[1])
    else:
        delay_seconds = 1
    producer = KafkaProducer(bootstrap_servers=[KAFKA_BROKER])
    for transaction in random_transaction():
        key = uuid.uuid4().hex.encode('utf-8')
        val = transaction.SerializeToString()
        producer.send(topic='transactions', key=key, value=val)
        producer.flush()
        time.sleep(delay_seconds)

def random_confirmed_fraud():
    """Generate infinite sequence of random fraud confirmations."""
    while True:
        confirm_fraud = ConfirmFraud()
        confirm_fraud.account = "0x%08X" % random.randint(0x100000, 0x1000000)

        yield confirm_fraud

def produce_confirmed():
    if len(sys.argv) == 2:
        delay_seconds = int(sys.argv[1]) * 10
    else:
        delay_seconds = 10
    producer = KafkaProducer(bootstrap_servers=[KAFKA_BROKER])
    for confirmed in random_confirmed_fraud():
        key = confirmed.account.encode('utf-8')
        val = confirmed.SerializeToString()
        producer.send(topic='confirmed', key=key, value=val)
        producer.flush()
        time.sleep(delay_seconds)

def random_threshold():
    """Generate infinite sequence of custom thresholds."""
    while True:
        threshold = CustomThreshold()
        threshold.account = "0x%08X" % random.randint(0x100000, 0x1000000)
        threshold.threshold = random.randint(1, 100)

        yield threshold

def produce_threshold():
    if len(sys.argv) == 2:
        delay_seconds = int(sys.argv[1]) * 10
    else:
        delay_seconds = 10
    producer = KafkaProducer(bootstrap_servers=[KAFKA_BROKER])
    for threshold in random_confirmed_fraud():
        key = threshold.account.encode('utf-8')
        val = threshold.SerializeToString()
        producer.send(topic='threshold', key=key, value=val)
        producer.flush()
        time.sleep(delay_seconds)

def consume():
    consumer = KafkaConsumer(
        'alerts',
        bootstrap_servers=[KAFKA_BROKER],
        auto_offset_reset='earliest',
        group_id='group-id')
    for message in consumer:
        value = Transaction()
        try:
            value.ParseFromString(message.value)
            print(f"Suspected Fraud for account id {value.account} at {value.merchant} for {value.amount:d} USD", flush=True)
        except Exception:
            continue

def handler(number, frame):
    sys.exit(0)

def safe_loop(fn):
    while True:
        try:
            fn()
        except SystemExit:
            print("Good bye!")
            return
        except NoBrokersAvailable:
            time.sleep(2)
            continue
        except Exception as e:
            print(e, flush=True)
            return

def main():
    signal.signal(signal.SIGTERM, handler)

    producer = threading.Thread(target=safe_loop, args=[produce])
    producer.start()

    confirmed = threading.Thread(target=safe_loop, args=[produce_confirmed])
    confirmed.start()

    threshold = threading.Thread(target=safe_loop, args=[produce_threshold])
    threshold.start()

    consumer = threading.Thread(target=safe_loop, args=[consume])
    consumer.start()

    producer.join()
    confirmed.join()
    threshold.join()
    consumer.join()

if __name__ == "__main__":
    main()


# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue Mar 12 15:37:47 2019

@author: python
"""
import numpy as np
import pandas as pd
import sqlalchemy as sa
from sqlalchemy import and_
from gateway.database import engine, metadata
from gateway.driver.tools import unpack_df_to_component_dict

ADJUSTMENT_COLUMNS_TYPE = {
                'sid_bonus': int,
                'sid_transfer': int,
                'bonus': np.float64,
                'right_bonus': int,
                'right_price': np.float64
                        }

class SQLiteAdjustmentReader(object):
    """
        1 获取所有的分红 配股 数据用于pipeloader
        2.形成特定的格式的dataframe
    """
    adjustment_tables = frozenset(['equity_splits', 'equity_rights'])

    def __init__(self):
        self.engine = engine
        metadata.reflect(bind=engine)
        for tbl in self.adjustment_tables:
            setattr(self, tbl, metadata.tables[tbl])

    def __enter__(self):
        return self

    @staticmethod
    def _adjust_frame_type(df):
        for col, col_type in ADJUSTMENT_COLUMNS_TYPE.items():
            try:
                df[col] = df[col].astype(col_type)
            except KeyError:
                pass
            except TypeError:
                raise TypeError('%s cannot mutate into %s' % (col, col_type))
        return df

    def _get_dividends_with_ex_date(self, sessions):
        sdate, edate = sessions
        sql_dialect = sa.select([self.equity_splits.c.sid,
                                self.equity_splits.c.ex_date,
                                sa.cast(self.equity_splits.c.sid_bonus, sa.Numeric(5, 2)),
                                sa.cast(self.equity_splits.c.sid_transfer, sa.Numeric(5, 2)),
                                sa.cast(self.equity_splits.c.bonus, sa.Numeric(5, 2))]).\
            where(and_(self.equity_splits.c.pay_date.between(sdate, edate), self.equity_splits.c.progress.like('实施')))
        rp = self.engine.execute(sql_dialect)
        divdends = pd.DataFrame(rp.fetchall(), columns=['sid', 'ex_date', 'sid_bonus',
                                                        'sid_transfer', 'bonus'])
        divdends.set_index('sid', inplace=True)
        adjust_divdends = self._adjust_frame_type(divdends)
        unpack_divdends = unpack_df_to_component_dict(adjust_divdends, 'ex_date')
        return unpack_divdends

    def _get_rights_with_ex_date(self, sessions):
        sdate, edate = sessions
        sql = sa.select([self.equity_rights.c.sid,
                         self.equity_rights.c.ex_date,
                         sa.cast(self.equity_rights.c.rights_bonus, sa.Numeric(5, 2)),
                         sa.cast(self.equity_rights.c.rights_price, sa.Numeric(5, 2))]).\
            where(self.equity_rights.c.pay_date.between(sdate, edate))
        rp = self.engine.execute(sql)
        rights = pd.DataFrame(rp.fetchall(), columns=['sid', 'ex_date',
                                                      'rights_bonus', 'rights_price'])
        rights.set_index('sid', inplace=True)
        adjust_rights = self._adjust_frame_type(rights)
        unpack_rights = unpack_df_to_component_dict(adjust_rights, 'ex_date')
        return unpack_rights

    def _load_adjustments_from_sqlite(self, sessions):
        adjustments = dict()
        adjustments['dividends'] = self._get_dividends_with_ex_date(sessions)
        adjustments['rights'] = self._get_rights_with_ex_date(sessions)
        return adjustments

    def load_pricing_adjustments(self, sessions):
        pricing_adjustments = self._load_adjustments_from_sqlite(sessions)
        return pricing_adjustments

    def retrieve_pay_date_dividends(self, assets, date):
        sql_dialect = sa.select([self.equity_splits.c.sid,
                                 sa.cast(self.equity_splits.c.sid_bonus, sa.Numeric(5, 2)),
                                 sa.cast(self.equity_splits.c.sid_transfer, sa.Numeric(5, 2)),
                                 sa.cast(self.equity_splits.c.bonus, sa.Numeric(5, 2))]).\
                                where(sa.and_(self.equity_splits.c.progress.like('实施'),
                                              self.equity_splits.c.pay_date == date))
        rp = self.engine.execute(sql_dialect)
        dividends = pd.DataFrame(rp.fetchall(), columns=['sid', 'sid_bonus',
                                                         'sid_transfer', 'bonus'])
        dividends.set_index('sid', inplace=True)
        sids = [asset.sid for asset in assets]
        dividends = dividends.reindex(sids)
        dividends.dropna(how='all', inplace=True)
        adjust_dividends = self._adjust_frame_type(dividends)
        return adjust_dividends

    def retrieve_ex_date_rights(self, assets, date):
        sql = sa.select([self.equity_rights.c.sid,
                         sa.cast(self.equity_rights.c.rights_bonus, sa.Numeric(5, 2)),
                         sa.cast(self.equity_rights.c.rights_price, sa.Numeric(5, 2))]).\
                        where(self.equity_rights.c.ex_date == date)
        rp = self.engine.execute(sql)
        rights = pd.DataFrame(rp.fetchall(), columns=['sid', 'right_bonus', 'right_price'])
        rights.set_index('sid', inplace=True)
        sids = [asset.sid for asset in assets]
        rights = rights.reindex(sids)
        rights.dropna(how='all', inplace=True)
        adjust_rights = self._adjust_frame_type(rights)
        return adjust_rights

    def __exit__(self, *exc_info):
        self.close()

    def close(self):
        return self.engine.dispose()


""" The main run file.

Please use the runfile (./run) instead.
Usage: ./run -g GEN -m MOD -o OUTPUT
"""

import os
import sys

from keras.callbacks import ModelCheckpoint
from keras.optimizers import Adam

from ml.generators.generator import Generator
from ml.generators.mnist_generator import MnistGenerator
from ml.generators.single_generator import SingleGenerator
from ml.generators.mip_generator import MipGenerator

from ml.models.alexnet3d import AlexNet3DBuilder
from ml.models.alexnet2d import AlexNet2DBuilder
from ml.models.simple import SimpleNetBuilder
from ml.models.all_conv_model import AllConvModelBuilder
from ml.models.fusion_net3d import FusionNet3dBuilder

os.environ['TF_CPP_MIN_LOG_LEVEL'] = '2'

# Parsing parameters
generator = sys.argv[1]
augment_data = sys.argv[2].upper() == 'TRUE'
extend_dims = sys.argv[3].upper() == 'TRUE'
model = sys.argv[4]
dims = [int(i) for i in sys.argv[5].split(',')]
if extend_dims:
    dims.append(1)
epochs = int(sys.argv[6])
batch_size = int(sys.argv[7])

# Generators
if generator == 'default':
    Gen = Generator
    num_classes = 2
    loss = 'binary_crossentropy'
elif generator == 'mnist':
    Gen = MnistGenerator
    num_classes = 10
    loss = 'sparse_categorical_crossentropy'
elif generator == 'single':
    Gen = SingleGenerator
    num_classes = 2
    loss = 'binary_crossentropy'
elif generator == 'mip':
    Gen = MipGenerator
    num_classes = 2
    loss = 'binary_crossentropy'
else:
    raise ValueError('Invalid Generator')

# Model
if model == 'alexnet2d':
    Mod = AlexNet2DBuilder
elif model == 'alexnet3d':
    Mod = AlexNet3DBuilder
elif model == 'simple':
    Mod = SimpleNetBuilder
elif model == 'all_conv':
    Mod = AllConvModelBuilder
elif model == 'fusionnet3d':
    Mod = FusionNet3dBuilder
else:
    raise ValueError('Invalid Model')

# Creation
training_gen = Gen(
    dims=dims,
    batch_size=batch_size,
    augment_data=augment_data,
    extend_dims=extend_dims
)
validation_gen = Gen(
    dims=dims,
    batch_size=batch_size,
    augment_data=augment_data,
    extend_dims=extend_dims,
    validation=True
)

model = Mod.build(dims, num_classes=num_classes)
model.compile(optimizer=Adam(lr=1e-5),
              loss=loss,
              metrics=['accuracy'])
mc_callback = ModelCheckpoint(filepath=f'tmp/{model}_weights.hdf5', verbose=1)
print('Model has been compiled.')

# Training
model.fit_generator(
    generator=training_gen.generate(),
    steps_per_epoch=training_gen.get_steps_per_epoch(),
    validation_data=validation_gen.generate(),
    validation_steps=validation_gen.get_steps_per_epoch(),
    epochs=epochs,
    callbacks=[mc_callback],
    verbose=1,
    max_queue_size=1)
print('Model has been fit.')


import json
import os
import sys
import time
from shutil import rmtree

import requests

from earth_compose import compose
from env import BASE_URL, CACHE_DIR, IMAGE_DIR, IMG_PATH, DEL_BEFORE, OS
from logger import log

if OS == 'Windows':
    import win32.lib.win32con as win32con
    from win32 import win32api, win32gui

temp_dir = ''

def create_dir(path):
    '''
    创建临时文件夹

    Args:
        path: 保存图片每个区域的临时目录
    '''
    if not os.path.exists(path):
        os.makedirs(path)
        global temp_dir
    temp_dir = path

def get_latest_timestamp():
    '''
    根据当前时间从 himawari8 官网获取最新时间戳

    Returns:
        latest_timestamp: 来自 himawari8 的最新时间戳
    '''
    timestamp = round(time.time())
    log.info('Fetching Latest Timestamp.')
    url = BASE_URL + '/img/FULL_24h/latest.json?_=' + str(timestamp)
    req = requests.get(url)
    latest_timestamp = ''

    if (req.status_code == 200):
        latest_timestamp = json.loads(req.content).get('date')
        log.info('Latest Timestamp: %s.' % latest_timestamp)
        latest_timestamp = str(latest_timestamp).replace(
            '-', '/').replace(' ', '/').replace(':', '')

    del req
    return latest_timestamp

def get_earth_part(path, timestamp, x, y):
    '''
    获取每个部分的图片

    发送请求从 himawari8 api 获取图片

    Args:
        path: 请求 URL 的一部分
        timestamp: UNIX 时间戳
    '''
    file = '%d_%d.png' % (x, y)
    if not os.path.exists(temp_dir + '%03d_%03d.png' % (x, y)):
        url = BASE_URL + path + timestamp + '_' + file
        req = requests.get(url, stream=True)

        if req.status_code == 200:
            open(temp_dir + '%03d_%03d.png' % (x, y), 'wb+').write(req.content)
        del req

def get_earth_by_size(timestamp, size):
    '''
    根据时间戳和大小获取每个区域的图片

    Args:
        timestamp: 从 himawari8 api 获取的最新时间戳
        size: 取值范围为 [2, 4, 8, 16, 20], 图片分辨率为 (550 * size) ^ 2
    '''
    i, total = 1, size * size
    path = IMG_PATH.replace('$size', str(size))
    create_dir(CACHE_DIR + "%s/%dd/" % (timestamp, size))
    # 获取每个区域
    for y in range(0, size, 1):
        for x in range(0, size, 1):
            log.info('Getting Parts: %d/%d.' % (i, total))
            get_earth_part(path, timestamp, x, y)
            i = i + 1

def set_wallpaper_gnome(path):
    '''
    设置 Gnome 桌面环境的壁纸
    '''
    abs_path = os.path.abspath(path)
    os.system("gsettings set org.gnome.desktop.background primary-color '#000000'")
    os.system("gsettings set org.gnome.desktop.background picture-options 'scaled'")
    os.system(
        'gsettings set org.gnome.desktop.background picture-uri file://' + abs_path)
    log.info('Current Wallpaper: %s.' % abs_path)

def set_wallpaper_windows(path):
    '''调用 win32 api 设置桌面壁纸
    '''
    BACKGROUND_COLOR = win32api.RGB(0, 0, 0)
    win32api.SetSysColors((win32con.COLOR_DESKTOP,), (BACKGROUND_COLOR,))
    abs_path = os.path.abspath(path)
    reg_key = win32api.RegOpenKeyEx(win32con.HKEY_CURRENT_USER,
                                    'Control Panel\\Desktop',
                                    0,
                                    win32con.KEY_SET_VALUE)
    win32api.RegSetValueEx(reg_key, "WallpaperStyle", 0, win32con.REG_SZ, "6")
    win32gui.SystemParametersInfo(win32con.SPI_SETDESKWALLPAPER,
                                  abs_path,
                                  win32con.SPIF_SENDWININICHANGE)
    win32api.RegCloseKey(reg_key)
    log.info('Current Wallpaper: %s.' % abs_path)

def set_wallpaper(path):
    if OS == 'Windows':
        set_wallpaper_windows(path)
    elif OS == 'Linux':
        set_wallpaper_gnome(path)

def del_old_images(before):
    '''
    删除旧图片

    before 分钟以前的图片将被删除
    '''
    if before <= 0:
        return
    images = os.listdir(IMAGE_DIR)
    for file_name in images:
        path = os.path.join(IMAGE_DIR, file_name)
        if (os.path.isfile(path)):
            ctime = os.path.getctime(path)
            t = (time.time() - ctime) / 60
            if (t > before):
                os.remove(path)

def get_earth(size):
    if size in [2, 4, 8, 16, 20]:
        time = get_latest_timestamp()
        filename = time.replace('/', '-')
        earth_path = IMAGE_DIR + '%s.png' % filename
        # 如果本地图片已经是最新就不从服务器获取
        if not os.path.exists(earth_path):
            if time != '':
                get_earth_by_size(time, size)
                compose(temp_dir, size, filename)
                rmtree(CACHE_DIR)
        set_wallpaper(earth_path)
        del_old_images(DEL_BEFORE)
    else:
        log.error('Invalid size: %d.' % size)


#    Indices
#    0123456789.................33

frase = 'o rato roeu a roupa do rei de roma'  # Iterável

tamanho_frase = len(frase)
print(tamanho_frase)
contador = 0
nova_string = ''

input_do_usuario = input('Qual letra deseja colocar em maiuscula: ')

# Iteração <- Iterar
while contador < tamanho_frase:
    letra = frase[contador]
    if letra == input_do_usuario:
        nova_string += input_do_usuario.upper()
    else:
        nova_string += letra
    contador += 1

print(nova_string)


from tensorflow.keras.preprocessing import image
from tensorflow.keras import layers
import numpy as np
from tensorflow.keras import Sequential
import matplotlib.pyplot as plt
#data set generator
train=image.ImageDataGenerator(rescale=1./255
                               ,
                               horizontal_flip=True
                               ,
                               shear_range=.1,
                               zoom_range=.1)
train_set=train.flow_from_directory('C:\\Users\\ahmed\\PycharmProjects\\untitled\\catanddog\\train\\',
                          target_size=(100,100),
                          batch_size=25,
                          class_mode='binary')
model=Sequential()
model.add(layers.Conv2D(200,(3,3),input_shape=(100,100,3),activation = 'relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2),strides=2)) #if stride not given it equal to pool filter size
model.add(layers.Conv2D(32,(3,3),activation = 'relu'))
model.add(layers.MaxPooling2D(pool_size=(2,2),strides=2))
model.add(layers.Flatten())
model.add(layers.Dense(units=128,activation='relu'))
model.add(layers.Dense(units=128,activation='relu'))

model.add(layers.Dense(units=1,activation='sigmoid'))
model.compile(optimizer='adam',loss='mse')
model.fit_generator(train_set,epochs=200 , steps_per_epoch=10)
#1
for repeat in range(1,20):
    img1=image.load_img('C:\\Users\\ahmed\\PycharmProjects\\untitled\\catanddog\\test1\\{}.jpg'.format(repeat),target_size=(100, 100))
    img=image.img_to_array(img1)
    img = img/255
    img=np.expand_dims(img,axis=0)
    prediction=model.predict_classes(img)
    plt.text(20, 62, prediction, color='red', fontsize=18, bbox=dict(facecolor='white', alpha=0.8))
    plt.imshow(img1)
    plt.show()
#2


import logging
import random

import requests
import vk_api
from vk_api import bot_longpoll
from vk_api.bot_longpoll import VkBotEventType
from os import environ as env
import Lang
import Styles
import Tools
from Achievement import Achievement

# Logging
logging.basicConfig(format='%(levelname)s: %(message)s', level=logging.INFO)
logger = logging.getLogger(__name__)

# Context #
CHAT_DATA = {}
vk = None

def get_chat_data(key: str, obj: dict, default=None):
    return CHAT_DATA.get(obj['peer_id'], {}).get(key, default)

def edit_chat_data(key: str, val, obj: dict, default=None) -> None:
    # TODO: save to db
    CHAT_DATA.update((obj['peer_id'][key], val))
# ======= #

# API #
def reply_text(text: str, obj: dict) -> None:
    vk.messages.send(peer_id=obj['peer_id'], random_id=random.randint(0, 100000), message=text)

def reply_photo(photo, obj) -> None:
    server_url = vk.photos.getMessagesUploadServer(peer_id=obj['peer_id'])['upload_url']
    upload_req = requests.post(server_url, files={
        'photo': ('photo.png', photo.read(), 'image/png')
    }).json()
    photo = vk.photos.saveMessagesPhoto(**upload_req)[0]
    photo = 'photo' + str(photo['owner_id']) + '_' + str(photo['id']) + '_' + photo['access_key']

    # Sending achievement to user
    vk.messages.send(peer_id=obj['peer_id'], random_id=random.randint(0, 100000), attachment=photo)
# === #

def handle_button(obj: dict) -> None:
    pass

def handle_command(obj: dict) -> None:
    def unknown_command():
        reply_text(Lang.get('command.null', get_chat_data('lang', obj)), obj)

    commands = {}

    command = obj['text'][1:].split(' ', 1)[0]
    commands.get(command, unknown_command)()

def handle_attachment(obj: dict):
    # edit_chat_data('image_url', get_photo(obj), obj)
    # reply_text(Lang.get("achievement.require_name", get_chat_data('lang', obj)), obj)
    reply_text("No", obj)

def get_photo(obj):
    for a in obj['attachments']:
        if a['type'] == 'photo':
            sizes = a['photo']['sizes']
            return sizes[1 if len(sizes) > 1 else 0]['url']
    return None

def create_achievement(obj: dict) -> None:
    lang = get_chat_data('lang', obj)

    # Checking name & description
    vals = Achievement.parse_message(obj['text'])
    for err in Achievement.check_values(**vals):
        reply_text(Lang.get(err, lang, **Achievement.LIMITS), obj)
        vals = None
    if not vals:
        return

    # Icon & style
    image = get_photo(obj)
    if not image and get_chat_data('image_url', obj):
        image = get_chat_data('image_url', obj)
        edit_chat_data('image_url', None, obj)

    vals['icon'] = Tools.download_image(image or Tools.search_image(vals['name']))
    vals['style'] = Styles.get(get_chat_data('style', obj))

    # Setting language
    ach_lang = get_chat_data('ach_lang', obj, 'RUS')
    if vals['style'].change_lang(ach_lang) != 'ok':
        reply_text(Lang.get('error.achievement.no_lang', lang, lang=ach_lang, style=vals['style'].get_name(lang)), obj)

    ach = Achievement(**vals)
    gen = ach.generate()
    if type(gen) is str:
        reply_text(Lang.get(gen, lang), obj)
    else:
        reply_photo(gen, obj)

    # TODO: add buttons (other images and styles)

def main() -> None:
    vk_session = vk_api.VkApi(token=env.get('VK_TOKEN'))
    global vk
    vk = vk_session.get_api()

    long_poll = vk_api.bot_longpoll.VkBotLongPoll(
        vk=vk_session,
        group_id=int(env.get('VK_GROUP_ID')),
        wait=int(env.get('VK_TIMEOUT', 40))
    )

    for event in long_poll.listen():
        if event.type == VkBotEventType.MESSAGE_NEW:
            obj = event.object.message
            # Reading message
            vk.messages.markAsRead(messages_ids=[obj['id']], peer_id=obj['peer_id'])

            if 'payload' in obj.keys():
                handle_button(obj)
            elif len(obj.get('text', '')) > 0:
                if obj['text'][0] == '/':
                    handle_command(obj)
                else:
                    create_achievement(obj)
            elif len(obj['attachments']) > 0:
                handle_attachment(obj)

if __name__ == "__main__":
    logger.info("Starting VK bot")
    main()


# 方法1：栈
# 时间复杂度：O(N)
# 空间复杂度：O(N)
# 将字符串中的字符压入栈中，再次遍历S，确保非字符位置不变，若是字符则获取栈顶元素
class Solution:
    def reverseOnlyLetters(self, S: str) -> str:
        stack = [c for c in S if c.isalpha()]
        ans = []
        for x in S:
            if not x.isalpha():
                ans.append(x)
            else:
                ans.append(stack.pop())

        return "".join(ans)

# 方法2：反转指针
# 时间复杂度：O(N)
# 空间复杂度：O(N)
# 方法二实际上就是栈方法的一种改写，去掉了栈，用时间换空间。做了一次反向遍历
class Solution:
    def reverseOnlyLetters(self, S: str) -> str:
        ans = []
        j = len(S)-1
        for i, x in enumerate(S):
            if x.isalpha():
                while not S[j].isalpha():
                    j-=1
                ans.append(S[j])
                j-=1
            else:
                ans.append(x)
        return "".join(ans)


import logging
from copy import copy
import numpy as np
import pycqed.analysis_v2.base_analysis as ba
import pycqed.analysis.tools.data_manipulation as dm_tools

class Single_Qubit_RoundsToEvent_Analysis(ba.BaseDataAnalysis):

    def __init__(self, t_start: str=None, t_stop: str=None,
                 label: str='',
                 data_file_path: str=None,
                 options_dict: dict=None, extract_only: bool=False,
                 extract_metadata: bool=True,
                 do_fitting: bool=True, auto=True):
        super().__init__(t_start=t_start, t_stop=t_stop,
                         label=label,
                         data_file_path=data_file_path,
                         options_dict=options_dict,
                         extract_only=extract_only, do_fitting=do_fitting)
        self.single_timestamp = False
        self.extract_metadata = extract_metadata
        exp_meta_str = 'Experimental Data.Experimental Metadata.'
        self.params_dict = {
            'measurementstring': 'measurementstring',
            'measured_values': 'measured_values'}
        if self.extract_metadata:
            self.params_dict['depletion_time'] = exp_meta_str+'depletion_time'
            self.params_dict['net_gate'] = exp_meta_str+'net_gate'
            self.params_dict['sequence_type'] = exp_meta_str+'sequence_type'
            self.params_dict['feedback'] = exp_meta_str+'feedback'

        self.numeric_params = []
        if auto:
            self.run_analysis()

    def process_data(self):
        """
        """
        # this option is used when the data is not extracted from the datafiles
        net_gate = self.raw_data_dict.get('net_gate', ['pi'])
        feedback = self.raw_data_dict.get('feedback', [False])[0]

        # N.B. flipping is the default (net_gate = pi-pulse)
        net_pulse_pat = 'flipping'
        if not len(set(net_gate)) == 1:
            logging.warning('Different net pulses in dataset.')
        if net_gate[0] == 'i':
            net_pulse_pat = 'constant'
        if feedback:
            net_pulse_pat = 'FB_to_ground'

        exp_pattern = self.options_dict.get('exp_pattern', net_pulse_pat)

        raw_dat = self.raw_data_dict['measured_values']
        nr_expts = self.raw_data_dict['nr_experiments']
        raw_pat = [raw_dat[i][0] for i in range(nr_expts)]

        s_events = []
        d_events = []
        for i in range(nr_expts):
            err_marker = getattr(dm_tools,
                                 'mark_errors_{}'.format(exp_pattern))
            events = err_marker(raw_pat[i])
            s_events.append(events[0])
            d_events.append(events[1])

        self.proc_data_dict['raw_pattern'] = raw_pat
        self.proc_data_dict['exp_pattern'] = exp_pattern

        self.proc_data_dict['single_events'] = s_events
        self.proc_data_dict['double_events'] = d_events

        self.proc_data_dict['frac_zero'] = 1 - np.mean(raw_pat, axis=1)
        self.proc_data_dict['frac_one'] = np.mean(raw_pat, axis=1)
        self.proc_data_dict['frac_no_error'] = 1-np.mean(s_events, axis=1)
        self.proc_data_dict['frac_single'] = np.mean(s_events, axis=1)
        self.proc_data_dict['frac_double'] = np.mean(d_events, axis=1)

    def prepare_plots(self):
        self.plot_dicts['err_frac'] = {
            'plotfn': self.plot_line,
            'xvals': self.raw_data_dict['datetime'],
            'yvals': self.proc_data_dict['frac_single'],
            'ylabel': 'Fraction',
            'yunit': '',
            'yrange': (0, 1),  # FIXME change to ylim in base analysis
            'setlabel': 'Single errors',
            'title': (self.raw_data_dict['timestamps'][0] + ' -  ' +
                      self.raw_data_dict['timestamps'][-1] + '\n' +
                      'Rounds to event analysis'),
            'do_legend': True,
            'legend_pos': 'upper right'}
        self.plot_dicts['double_err_frac'] = {
            'ax_id': 'err_frac',
            'plotfn': self.plot_line,
            'xvals': self.raw_data_dict['datetime'],
            'yvals': self.proc_data_dict['frac_double'],
            'setlabel': 'Double errors',
            'do_legend': True,
            'legend_pos': 'upper right'}

        dataset_idx = self.options_dict.get('typ_data_idx', 0)
        start_idx = self.options_dict.get('typ_start_idx', 10)
        stop_idx = self.options_dict.get('typ_stop_idx', 70)
        raw_pat = self.proc_data_dict['raw_pattern'][
            dataset_idx][start_idx:stop_idx]
        s_events = self.proc_data_dict['single_events'][
            dataset_idx][start_idx:stop_idx]
        d_events = self.proc_data_dict['double_events'][
            dataset_idx][start_idx-1:stop_idx-1]

        title_map = {'constant': 'Typical trace constant',
                     'FB_to_ground': 'Typical trace feedback',
                     'flipping': 'Typical trace alternating'}

        self.plot_dicts['typical_trace'] = {
            'plotfn': self.plot_line,
            'xvals': np.arange(len(raw_pat)),
            'yvals': raw_pat,
            'xlabel': 'Measurement #',
            'ylabel': 'Declared state',
            'yrange': (-0.05, 1.05),  # FIXME change to ylim in base analysis
            'setlabel': 'Raw trace',
            'title': title_map[self.proc_data_dict['exp_pattern']],
            'do_legend': True,
            'legend_pos': 'right'}

        if self.proc_data_dict['exp_pattern'] == 'constant':
            event_pat = .5 * np.ones(len(raw_pat))
        else:
            event_pat = raw_pat

        self.plot_dicts['typical_pat_single'] = {
            'ax_id': 'typical_trace',
            'plotfn': self.plot_line,
            'xvals': np.arange(len(raw_pat))[s_events > .5]+.5,
            'yvals': event_pat[s_events > .5],
            'marker': 'x',
            'line_kws': {'color': 'r', 'markersize': 7},
            'linestyle': '',
            'setlabel': 'Error', 'do_legend': True,
            'legend_pos': 'right'}

        self.plot_dicts['typical_pat_double'] = {
            'ax_id': 'typical_trace',
            'plotfn': self.plot_line,
            'xvals': np.arange(len(raw_pat))[d_events > .5],
            'yvals': raw_pat[d_events > .5],
            'marker': '|',
            'linestyle': '',
            'line_kws': {'markerfacecolor': 'None', 'markeredgecolor': 'red',
                         'markersize': 15},
            'setlabel': 'Double events', 'do_legend': True, 'legend_pos': 'right'}

class One_Qubit_Paritycheck_Analysis(ba.BaseDataAnalysis):
    """
    Analysis for the (repeated) parity check or indirect measurement
    of a single qubit corresponding to the following circuit

                          ^(N)
    Data: M -|-(x)-0-----|-M
             |     |     |
    Anc.: M -|--X--0-X-M-|-M

    This analysis gives

    relevant options_dict parameters
        ch_idx_anc (int) specifies the readout channel for the ancilla qubit
        ch_idx_data (int) specifies the readout channel for the data qubit.

        post_sel_th_anc  (float)
        post_sel_th_data (float)
        dig_th_anc       (float)
        dig_th_data      (float)

        exp_pat_0       (str)
        exp_pat_1       (str)

        nr_of_meas (int) : number of measurement per prepared state.
            used to determine the period for data binning. Includes
            the initialization measurement.

    """

    def __init__(self, t_start: str=None, t_stop: str=None,
                 label:str ='', data_file_path: str=None,
                 options_dict: dict=None, extract_only: bool=False,
                 do_fitting: bool=True, auto=True):

        super().__init__(t_start=t_start, t_stop=t_stop,
                         label=label, data_file_path=data_file_path,
                         options_dict=options_dict,
                         extract_only=extract_only, do_fitting=do_fitting)

        # self.single_timestamp = False
        self.params_dict = {'xlabel': 'sweep_name',
                            'xunit': 'sweep_unit',
                            'xvals': 'sweep_points',
                            'measurementstring': 'measurementstring',
                            'value_names': 'value_names',
                            'value_units': 'value_units',
                            'measured_values': 'measured_values'}
        self.numeric_params = []
        if auto:
            self.run_analysis()

    def process_data(self):

        # Shorthand reference to proc_data_dict to keep code more readable
        dat_dict = self.proc_data_dict

        ch_idx_anc = self.options_dict.get('ch_idx_anc', 0)
        ch_idx_data = self.options_dict.get('ch_idx_data', 1)
        nr_of_meas = self.options_dict.get('nr_of_meas', 5)
        post_sel_th_anc = self.options_dict.get('post_sel_th_anc', 0)
        post_sel_th_data = self.options_dict.get('post_sel_th_data', 0)

        dig_th_anc = self.options_dict.get('dig_th_anc', post_sel_th_anc)
        dig_th_data = self.options_dict.get('dig_th_anc', post_sel_th_data)

        post_select = self.options_dict.get('post_select', True)

        nr_bins = self.options_dict.get('nr_bins', 100)

        shots_anc = list(self.raw_data_dict['measured_values_ord_dict'].values())\
            [ch_idx_anc][0]
        shots_data = list(self.raw_data_dict['measured_values_ord_dict'].values())\
            [ch_idx_data][0]

        dat_dict['shots_anc'] = shots_anc
        dat_dict['shots_data'] = shots_data

        # Data binning
        (prep_0_anc, meas_0_anc, trace_0_anc, prep_1_anc, meas_1_anc,
            trace_1_anc) = repeated_parity_data_binning(
            dat_dict['shots_anc'], nr_of_meas)
        (prep_0_data, meas_0_data, trace_0_data, prep_1_data, meas_1_data,
            trace_1_data) = repeated_parity_data_binning(
            dat_dict['shots_data'], nr_of_meas)

        dat_dict['trace_0_anc'] = trace_0_anc
        # Digitizing traces
        dat_dict['trace_0_anc_dig'] = dm_tools.digitize(
            trace_0_anc, dig_th_anc, zero_state=0)
        dat_dict['trace_0_data_dig'] = dm_tools.digitize(
            trace_0_data, dig_th_data, zero_state=0)
        dat_dict['trace_1_anc_dig'] = dm_tools.digitize(
            trace_1_anc, dig_th_anc, zero_state=0)
        dat_dict['trace_1_data_dig'] = dm_tools.digitize(
            trace_1_data, dig_th_data, zero_state=0)

        # post selection
        if post_select:
            dat_dict['post_sel_idx_0'] = dm_tools.get_post_select_indices(
                (post_sel_th_anc, post_sel_th_data), (prep_0_anc, prep_0_data))
            dat_dict['post_sel_idx_1'] = dm_tools.get_post_select_indices(
                (post_sel_th_anc, post_sel_th_data), (prep_1_anc, prep_1_data))

            for arr in [meas_0_anc, meas_0_data, trace_0_anc, trace_0_data]:
                arr[dat_dict['post_sel_idx_0']] = np.nan
            for arr in [meas_1_anc, meas_1_data, trace_1_anc, trace_1_data]:
                arr[dat_dict['post_sel_idx_1']] = np.nan

        # Counting the rounds to event
        dat_dict['RTE_0'] = count_RTE(dat_dict['trace_0_anc_dig'],
                                      exp_pattern='alternating', init_state=0)
        dat_dict['RTE_1'] = count_RTE(dat_dict['trace_1_anc_dig'],
                                      exp_pattern='constant', init_state=0)

        dat_dict['RTE_0_ps'] = copy(dat_dict['RTE_0'])
        dat_dict['RTE_0_ps'][dat_dict['post_sel_idx_0']] = np.nan
        dat_dict['RTE_1_ps'] = copy(dat_dict['RTE_1'])
        dat_dict['RTE_1_ps'][dat_dict['post_sel_idx_1']] = np.nan

        dat_dict['mRTE_0_ps'] = np.nanmean(dat_dict['RTE_0_ps'])
        dat_dict['mRTE_1_ps'] = np.nanmean(dat_dict['RTE_1_ps'])

        # Histogramming
        dat_dict['prep_0_anc_hist'] = np.histogram(
            prep_0_anc, bins=nr_bins,
            range=(np.min(shots_anc), np.max(shots_anc)))
        dat_dict['prep_1_anc_hist'] = np.histogram(
            prep_1_anc, bins=nr_bins,
            range=(np.min(shots_anc), np.max(shots_anc)))

        dat_dict['meas_0_anc_hist'] = np.histogram(
            meas_0_anc, bins=nr_bins,
            range=(np.min(shots_anc), np.max(shots_anc)))
        dat_dict['meas_1_anc_hist'] = np.histogram(
            meas_1_anc, bins=nr_bins,
            range=(np.min(shots_anc), np.max(shots_anc)))

        dat_dict['prep_0_data_hist'] = np.histogram(
            prep_0_data, bins=nr_bins,
            range=(np.min(shots_data), np.max(shots_data)))
        dat_dict['prep_1_data_hist'] = np.histogram(
            prep_1_data, bins=nr_bins,
            range=(np.min(shots_data), np.max(shots_data)))
        dat_dict['meas_0_data_hist'] = np.histogram(
            meas_0_data, bins=nr_bins,
            range=(np.min(shots_data), np.max(shots_data)))
        dat_dict['meas_1_data_hist'] = np.histogram(
            meas_1_data, bins=nr_bins,
            range=(np.min(shots_data), np.max(shots_data)))

        # Histogramming RTE

        dat_dict['prep_0_RTE_hist'] = np.histogram(
            dat_dict['RTE_0_ps'], bins=nr_of_meas+1,
            range=(-0.5, nr_of_meas+0.5))
        dat_dict['prep_1_RTE_hist'] = np.histogram(
            dat_dict['RTE_1_ps'], bins=nr_of_meas+1,
            range=(-0.5, nr_of_meas+0.5))

        F_ass_1 = np.sum(meas_1_anc<dig_th_anc) /(
            np.sum(meas_1_anc>dig_th_anc) + np.sum(meas_1_anc<dig_th_anc))
        F_ass_0 = np.sum(meas_0_anc>dig_th_anc) /(
            np.sum(meas_0_anc>dig_th_anc) + np.sum(meas_0_anc<dig_th_anc))

        F_ass_avg = 1-(1-F_ass_0+1-F_ass_1)/2
        self.proc_data_dict['F_ass'] = F_ass_avg

    def prepare_plots(self):
        self.prepare_ancilla_ro_histogram()
        self.prepare_data_ro_histogram()
        self.prepare_typical_trace_plot()
        self.prepare_RTE_histogram()

    def prepare_ancilla_ro_histogram(self):
        #########################################
        # Ancilla qubit figures
        #########################################
        self.plot_dicts['meas_0_anc'] = {
                    'xlabel': 'anc dac value',
                    'title': 'Ancilla qubit histograms' + '\n'+self.timestamps[0],
                    'ylabel': 'Counts',
                    'plotfn': self.plot_bar,
                    'xvals': self.proc_data_dict['meas_0_anc_hist'][1],
                    'yvals': self.proc_data_dict['meas_0_anc_hist'][0],
                    'ax_id': '1D_histogram_anc',
                    'bar_kws': {'log': False, 'alpha': .4, 'facecolor': 'C0',
                                'edgecolor': 'C0'},
                    'setlabel': 'Data prep. in: |0>'}

        self.plot_dicts['meas_1_anc'] = {
                    'plotfn': self.plot_bar,
                    'xvals': self.proc_data_dict['meas_1_anc_hist'][1],
                    'yvals': self.proc_data_dict['meas_1_anc_hist'][0],
                    'ax_id': '1D_histogram_anc',
                    'bar_kws': {'log': False, 'alpha': .4, 'facecolor': 'C3',
                                'edgecolor': 'C3'},
                    'setlabel': 'Data prep. in: |1>'}

        if not self.presentation_mode:
            self.plot_dicts['text_msg'] = {
                'ax_id': '1D_histogram_anc',
                'ypos': 0.6,
                'plotfn': self.plot_text,
                'box_props': 'fancy',
                'text_string': 'F avg. ass.: {:.2f} %'.format(
                    self.proc_data_dict['F_ass']*100)}
            max_cnts = np.max([np.max(self.proc_data_dict['meas_0_anc_hist'][0]),
                               np.max(self.proc_data_dict['meas_1_anc_hist'][0])])
            ps_th = self.options_dict.get('post_sel_th_anc', 0)
            dig_th = self.options_dict.get('dig_th_anc', ps_th)
            self.plot_dicts['post_sel_th_anc'] = {
                'ax_id': '1D_histogram_anc',
                'plotfn': self.plot_vlines,
                'x': ps_th,
                'ymin': 0,
                'ymax': max_cnts*1.05,
                'colors': '.3',
                'linestyles': 'dashed',
                'line_kws': {'linewidth': .8},
                'setlabel': 'Post sel. thres. {:.2f}'.format(ps_th),
                'do_legend': True}

            ps_th = self.options_dict.get('dig_th_anc', 0)
            self.plot_dicts['dig_th_anc'] = {
                'ax_id': '1D_histogram_anc',
                'plotfn': self.plot_vlines,
                'x': dig_th,
                'ymin': 0,
                'ymax': max_cnts*1.05,
                'colors': '.3',
                'linestyles': 'solid',
                'line_kws': {'linewidth': .8},
                'setlabel': 'Digitization thres. {:.2f}'.format(dig_th),
                'do_legend': True}

    def prepare_data_ro_histogram(self):
        #########################################
        # Data qubit figures
        #########################################
        self.plot_dicts['meas_0_data'] = {
                    'xlabel': 'data dac value',
                    'title': 'Data qubit histograms' + '\n'+self.timestamps[0],
                    'ylabel': 'Counts',
                    'plotfn': self.plot_bar,
                    'xvals': self.proc_data_dict['meas_0_data_hist'][1],
                    'yvals': self.proc_data_dict['meas_0_data_hist'][0],
                    'ax_id': '1D_histogram_data',
                    'bar_kws': {'log': False, 'alpha': .4, 'facecolor': 'C0',
                                'edgecolor': 'C0'},
                    'setlabel': 'Data prep. in: |0>'}
        self.plot_dicts['meas_1_data'] = {
                    'plotfn': self.plot_bar,
                    'xvals': self.proc_data_dict['meas_1_data_hist'][1],
                    'yvals': self.proc_data_dict['meas_1_data_hist'][0],
                    'ax_id': '1D_histogram_data',
                    'bar_kws': {'log': False, 'alpha': .4, 'facecolor': 'C3',
                                'edgecolor': 'C3'},
                    'setlabel': 'Data prep. in: |1>'}

        max_cnts = np.max([np.max(self.proc_data_dict['meas_0_data_hist'][0]),
                           np.max(self.proc_data_dict['meas_1_data_hist'][0])])
        ps_th = self.options_dict.get('post_sel_th_data', 0)
        dig_th = self.options_dict.get('dig_th_data', ps_th)
        self.plot_dicts['post_sel_th_data'] = {
            'ax_id': '1D_histogram_data',
            'plotfn': self.plot_vlines,
            'x': ps_th,
            'ymin': 0,
            'ymax': max_cnts*1.05,
            'colors': '.3',
            'linestyles': 'dashed',
            'line_kws': {'linewidth': .8},
            'setlabel': 'Post sel. thres. {:.2f}'.format(ps_th),
            'do_legend': True}

        ps_th = self.options_dict.get('dig_th_data', 0)
        self.plot_dicts['dig_th_data'] = {
            'ax_id': '1D_histogram_data',
            'plotfn': self.plot_vlines,
            'x': dig_th,
            'ymin': 0,
            'ymax': max_cnts*1.05,
            'colors': '.3',
            'linestyles': 'solid',
            'line_kws': {'linewidth': .8},
            'setlabel': 'Digitization thres. {:.2f}'.format(dig_th),
            'do_legend': True}

    def prepare_typical_trace_plot(self):

        idx = self.options_dict.get('idx_typ_0', 0)
        raw_pat = self.proc_data_dict['trace_0_anc_dig'][idx, :]
        self.plot_dicts['typical_trace_0'] = {
            'plotfn': self.plot_line,
            'ax_id': 'typical_trace',
            'xvals': np.arange(len(raw_pat))+1,
            'yvals': raw_pat,
            'xlabel': 'Measurement #',
            'ylabel': 'Declared state',
            'yrange': (-.05, 1.05),  # FIXME change to ylim in base analysis
            'setlabel': 'Initial state |0>',
            'title': 'Typical trace of Ancilla outcomes',
            'do_legend': True,
            'legend_pos': 'right'}

        idx = self.options_dict.get('idx_typ_1', 1)
        raw_pat = self.proc_data_dict['trace_1_anc_dig'][idx, :]
        self.plot_dicts['typical_trace_1'] = {
            'plotfn': self.plot_line,
            'ax_id': 'typical_trace',
            'xvals': np.arange(len(raw_pat))+1,
            'yvals': raw_pat,
            'xlabel': 'Measurement #',
            'ylabel': 'Declared state',
            'yrange': (-.05, 1.05),  # FIXME change to ylim in base analysis
            'setlabel': 'Initial state |1>',
            'title': 'Typical trace of Ancilla outcomes',
            'do_legend': True,
            'legend_pos': 'right'}

    def prepare_RTE_histogram(self):
        self.plot_dicts['RTE_0'] = {
                    'xlabel': 'Measurement #',
                    'title': 'RTE histogram' + '\n'+self.timestamps[0],
                    'ylabel': 'Counts',
                    'plotfn': self.plot_bar,
                    'xvals': self.proc_data_dict['prep_0_RTE_hist'][1],
                    'yvals': self.proc_data_dict['prep_0_RTE_hist'][0],
                    'ax_id': 'RTE_histogram_data',
                    'bar_kws': {'log': False, 'alpha': .4, 'facecolor': 'C0',
                                'edgecolor': 'C0'},
                    'setlabel': 'Data prep. in: |0>\n Mean RTE: {:.2f}'.format(
                        self.proc_data_dict['mRTE_0_ps'])}
        self.plot_dicts['RTE_1'] = {
                    'plotfn': self.plot_bar,
                    'xvals': self.proc_data_dict['prep_1_RTE_hist'][1],
                    'yvals': self.proc_data_dict['prep_1_RTE_hist'][0],
                    'ax_id': 'RTE_histogram_data',
                    'bar_kws': {'log': False, 'alpha': .4, 'facecolor': 'C3',
                                'edgecolor': 'C3'},
                    'setlabel': 'Data prep. in: |1>\n Mean RTE: {:.2f}'.format(
                        self.proc_data_dict['mRTE_1_ps']),
                    'do_legend':True}

def repeated_parity_data_binning(shots, nr_of_meas:int):
    """
    Used for data binning of the repeated parity check experiment.
    Assumes the data qubit is alternatively prepared in 0 and 1.

    Args:
        shots (1D array) : array containing all measured values of 1 qubit
        nr_of_meas (int) : number of measurement per prepared state.
            used to determine the period for data binning. Includes
            the initialization measurement.

    Returns
        prep_0  (1D array)  outcomes of the initialization measurement
        meas_0  (1D array)  outcomes of the first measurement
        trace_0 (2D array)  traces

        prep_1  (1D array)
        meas_1  (1D array)
        trace_1 (2D array)
    """

    prep_0 = copy(shots[::nr_of_meas*2])
    meas_0 = copy(shots[1::nr_of_meas*2])

    prep_1 = copy(shots[nr_of_meas::nr_of_meas*2])
    meas_1 = copy(shots[nr_of_meas+1::nr_of_meas*2])

    trace_0 = np.zeros((len(prep_0), nr_of_meas-1))
    trace_1 = np.zeros((len(prep_1), nr_of_meas-1))
    for i in range(len(prep_0)):
        trace_0[i, :] = shots[1+(2*i)*nr_of_meas: (2*i+1)*nr_of_meas]

        trace_1[i, :] = shots[1+(2*i+1)*nr_of_meas: (2*i+2)*nr_of_meas]

    return (prep_0, meas_0, trace_0, prep_1, meas_1, trace_1)

def count_RTE(traces, exp_pattern: str, init_state: int):
    """
    Args:
        traces (list of 1D arrays): of declared states as 0 and 1
        exp_pattern (str) : "constant" or "alternating"
    """
    RTE = np.zeros(len(traces))

    RTE_larger_than_length = False
    if init_state not in (0, 1):
        raise ValueError('Initial state should be 0 or 1')
    if exp_pattern not in ['constant', 'alternating']:
        raise ValueError("exp_pattern should be 'constant' or 'alternating'")

    for i, t in enumerate(traces):
        if exp_pattern == 'constant':
            if init_state == 0:
                try:
                    RTE[i] = min(np.where( t >0.5)[0])+1
                except ValueError:
                    RTE[i] = len(t) + 1
                    RTE_larger_than_length = True

            else:
                try:
                    RTE[i] = min(np.where(t < 0.5)[0])+1
                except ValueError:
                    RTE[i] = len(t)+1
                    RTE_larger_than_length = True

        elif exp_pattern == 'alternating':
            try:
                t = np.append([init_state], t)
                dt = dm_tools.binary_derivative(t)
                RTE[i] = min(np.where(dt<0.5)[0])+1 # Detect first time there is no flip
            except ValueError:
                RTE[i] = len(t)+1
                RTE_larger_than_length = True
    if RTE_larger_than_length:
        print('Sequences with no errors measured')
    return RTE


import pyinotify
import os
import subprocess, sys

class EventHandler(pyinotify.ProcessEvent):
    def my_init(self, **kwargs):
        self.runnable_script = kwargs.get('runnable_script')
        self.xargs = kwargs.get('xargs')

    def process_IN_CREATE(self, event):
        print("New file detected: ", event.pathname)
        print("Running script: ", self.runnable_script)
        args = [self.runnable_script, os.path.abspath(event.pathname)]
        args.extend(self.xargs)
        print("Like: ", args)
        subprocess.Popen(args, stderr=subprocess.STDOUT)
        print("Finished with new file")

def new_created_watcher(watched_dir, run_name, output_path, script, script_xargs):
    """
    This function takes 4 arguments:
    -watched_dir: the directory being watched for changes
    -run_name: a unique name for this watching job
    -output_path: path for output logs
    -script: a script to run when the action happens
    -script_xargs: a list of any extra args the script will need to run
    """
    wm = pyinotify.WatchManager()
    wm.add_watch(watched_dir, pyinotify.IN_CREATE, rec=True)

    handler = EventHandler(runnable_script=script, xargs=script_xargs)
    notifier = pyinotify.Notifier(wm,handler)

    try:
        notifier.loop(daemonize=True,  pid_file=os.path.join('/tmp', run_name + '.pid'), stdout=output_path)
    except pyinotify.NotifierError as err:
        print("error:", err)


#!/usr/bin/python3

import gi
import os

gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
from pathlib import Path

host_lists = []
current_host = ["", "", "", "", ""]
home_user = str(Path.home())
home_app = os.path.dirname(os.path.realpath(__file__))

def isNotBlank (myString):
    if myString and myString.strip():
        return True #myString is not None AND myString is not empty or blank
    return False #myString is None OR myString is empty or blank

def generate_host_list():

  # clean up and parse .ssh/config
  parse_ssh_command = "cat " + home_user + "/.ssh/config | grep -v '^#' | awk 'NF' > " + home_user + "/.ssh/config-remotehelper"
  os.system(parse_ssh_command)

  with open(home_user + "/.ssh/config-remotehelper") as fp:
      for line in fp:
          if line.startswith("Host"):
            current_host = ["", "", "", "", ""]
            current_host[0] = line.rstrip().lstrip().split(" ")[1]
          if line.startswith("  HostName"):
            current_host[1] = line.rstrip().lstrip().split(" ")[1]
          if line.startswith("  Port"):
            current_host[2] = line.rstrip().lstrip().split(" ")[1]
          if line.startswith("  User"):
            current_host[3] = line.rstrip().lstrip().split(" ")[1]
          if line.startswith("  IdentityFile"):
            current_host[4] = line.rstrip().lstrip().split(" ")[1].replace("~",home_user)

          # Add host to list only if they have all the data need it
          if isNotBlank(current_host[0]) and isNotBlank(current_host[1]) and isNotBlank(current_host[2]) and isNotBlank(current_host[3]) and isNotBlank(current_host[4]):
            # ignore duplicate host
            if (current_host[0],current_host[1],int(current_host[2]),current_host[3],current_host[4]) not in host_lists:
              host_lists.append((current_host[0],current_host[1],int(current_host[2]),current_host[3],current_host[4]))
            current_host = ["", "", "", "", ""]

class MainWindow(Gtk.Window):

    def __init__(self):
        Gtk.Window.__init__(self, title="RemoteHelper")
        self.set_border_width(10)
        self.set_icon_from_file(home_app + '/icon.png')

        #Setting up the self.grid in which the elements are to be positionned
        self.grid = Gtk.Grid()
        self.grid.set_column_homogeneous(True)
        self.grid.set_row_homogeneous(True)
        self.add(self.grid)

        entrySearch = Gtk.Entry()
        entrySearch.set_placeholder_text("Type to filter by hostname...")
        entrySearch.connect("changed", self.on_entrySearch_changed)

        buttonSSH = Gtk.Button(label="SSH")
        buttonSSH.connect("clicked", self.on_buttonSSH_clicked)

        buttonSFTP = Gtk.Button(label="SFTP")
        buttonSFTP.connect("clicked", self.on_buttonSFTP_clicked)

        #Creating the ListStore model
        self.host_listsstore = Gtk.ListStore(str, str, int, str, str)
        for host_ref in host_lists:
            self.host_listsstore.append(list(host_ref))
        self.current_filter_host = None

        #Creating the filter, feeding it with the liststore model
        self.host_filter = self.host_listsstore.filter_new()
        #setting the filter function, note that we're not using the
        self.host_filter.set_visible_func(self.host_filter_func)

        #creating the treeview, making it use the filter as a model, and adding the columns
        self.treeview = Gtk.TreeView.new_with_model(self.host_filter)

        for i, column_title in enumerate(["Hostname", "IP", "Port", "UserName", "Key Path"]):
            renderer = Gtk.CellRendererText()
            column = Gtk.TreeViewColumn(column_title, renderer, text=i)
            self.treeview.append_column(column)

        select = self.treeview.get_selection()
        select.connect("changed", self.on_tree_selection_changed)

        #setting up the layout, putting the treeview in a scrollwindow, and the buttons in a row
        self.scrollable_treelist = Gtk.ScrolledWindow()
        self.scrollable_treelist.set_vexpand(True)
        self.grid.attach(self.scrollable_treelist, 0, 0, 8, 10)

        self.grid.attach_next_to(entrySearch, self.scrollable_treelist, Gtk.PositionType.TOP, 1, 1)
        self.grid.attach_next_to(buttonSSH, entrySearch, Gtk.PositionType.RIGHT, 1, 1)
        self.grid.attach_next_to(buttonSFTP, buttonSSH, Gtk.PositionType.RIGHT, 1, 1)

        self.scrollable_treelist.add(self.treeview)
        self.show_all()

    def host_filter_func(self, model, iter, data):
        if self.current_filter_host is None or self.current_filter_host == "None" or self.current_filter_host == "":
            return True
        else:
            value = model.get_value(iter, 0).lower()
            return True if self.current_filter_host in value else False

    def on_buttonSSH_clicked(self, widget):
      ssh_command = "/usr/bin/xfce4-terminal --tab -T " + current_host[3] + "@" + current_host[0] + " -e 'ssh -X -o StrictHostKeyChecking=no -p {} -i " + current_host[4] + " " + current_host[3] + "@" + current_host[1] + "' &"
      os.system(ssh_command.format(current_host[2]))

    def on_buttonSFTP_clicked(self, widget):
      if os.path.exists(home_user + "/.ssh/filezilla"):
        os.remove(home_user + "/.ssh/filezilla")
      os.symlink(current_host[4], home_user + "/.ssh/filezilla")
      sftp_command = "/usr/bin/filezilla sftp://" + current_host[3] + ":@" + current_host[1] + ":{} &"
      os.system(sftp_command.format(current_host[2]))

    def on_tree_selection_changed(self, selection):
      model, treeiter = selection.get_selected()
      if treeiter is not None:
          current_host[0] = model[treeiter][0]
          current_host[1] = model[treeiter][1]
          current_host[2] = model[treeiter][2]
          current_host[3] = model[treeiter][3]
          current_host[4] = model[treeiter][4]

    def on_entrySearch_changed(self, widget):
      self.current_filter_host = widget.get_text()
      self.host_filter.refilter()

generate_host_list()

win = MainWindow()
win.connect("destroy", Gtk.main_quit)
win.show_all()
Gtk.main()


# -*- coding: utf-8 -*-
# @Time : 2018/07/26 14:00
# @Author : zhoutaotao
# @File : dynamicGenerationScript.py
# @Software: dialogManagerServerDev
# @Desc:动态生成用户执行脚本类

import os
import sys
import traceback
class DynamicGenerationScript(object):
    """用户自定义执行脚本类

    支持
    Attributes:
        basePath,当前脚本所在路径
        strDefault,用户默认脚本处理函数字符串
    """
    def __init__(self):
        """类中用到的常量初始化下"""
        self.basePath = os.getcwd()+"/utils/dialogManager/src/userScript"
        self.strDefault = """\ndef handler(self,msg):\n    return msg"""

    def _processStr(self, str, script):
        """字符串处理,把用户的handler函数整合到自动产生的class里面.生产一个类的字符串,供写入到的动态产生的py文件中
        """
        arr = str.split("\n")
        ret = ''
        ret = """#!coding=utf-8\nimport os\nimport sys\n"""
        ret = ret + "class "+script+"""(object):\n    def __init__(self):\n        pass\n"""

        for item in arr:
            # print('    '+item)
            ret = ret + '\n    '+item
        return ret

    def _addInit(self, id, scriptName):
        """添加__init__.py文件包管理

        添加包管理语句,例如from .Weather import *
        先判断__init.py文件是否存在,
             1)若不存在,先创建,再导入
             2)若存在,直接导入
        判断是否已导入,防止重复导入

        Args:
            id,用户id
            scriptName,用户自定义脚本包名称(文件名和类名)

        Returns:
            无
        """
        path = os.path.join(self.basePath, id)
        if not os.path.exists(path):
            os.makedirs(path)
            os.mknod(os.path.join(path, "__init__.py"))
        filePath = os.path.join(path, "__init__.py")
        ret = """try:\n    from ."""+scriptName+""" import *\nexcept:\n    pass\n"""
        w = open(filePath, 'a+')
        w.seek(0)
        content = w.read()
        if(content.find(ret) < 0):
            w.seek(2)
            w.write(ret)
        w.close()

    def _addInitHandler(self, id, scriptName):
        """添加初始化默认用户处理脚本

        添加始化默认用户处理脚本,默认是返回原始数据,例如
        def handler(self,msg):
            return msg

        Args:
            id,用户id
            scriptName,用户自定义脚本包名称(文件名和类名)

        Returns:
            无
        """
        ret = self._processStr(self.strDefault, script=scriptName)
        path = os.path.join(self.basePath, id)
        if not os.path.exists(path):
            os.makedirs(path)
            # os.mknod(os.path.join(path, "__init__.py"))
        filePath = os.path.join(path, scriptName+".py")
        w = open(filePath, 'w')
        w.write(ret)
        w.close()

    def addDefaultHandler(self, id, scriptName):
        """添加用户默认脚本处理

        添加__init__.py包管理
        添加handler初始默认处理脚本

        Args:
            id,用户id
            scriptName,用户自定义脚本包名称(文件名和类名)
        """
        # self._addInit(id, scriptName)
        self._addInitHandler(id, scriptName)

    def addUserHandler(self,id, scriptName, userScript):
        """添加用户自定义脚本处理

        添加__init__.py包管理
        添加handler初始默认处理脚本
        @example:
        DynamicGenerationScript().addUserHandler(id='user111',
                                                    scriptName='Weather',
                                                    userScript='xxxx')
        Args:
            id,用户id
            scriptName,用户自定义脚本包名称(文件名和类名)
            userScript,用户自定义脚本内容,例如:
                def handler(self,data):
                    print(data)
                    ret = {}
                    ret['text'] = 'user handler'
                    return ret
        """
        # self._addInit(id, scriptName)
        ret = self._processStr(userScript, scriptName)
        path = os.path.join(self.basePath, id)
        if not os.path.exists(path):
            os.makedirs(path)
            # os.mknod(os.path.join(path, "__init__.py"))
        filePath = os.path.join(path, scriptName+".py")
        w = open(filePath, 'w')
        w.write(ret)
        w.close()

    def callUserHandler(self, id, scriptName,msg):
        """调用用户脚本
        @example:
        DynamicGenerationScript().callUserHandler(id='user111',
                                                    scriptName='Weather',
                                                    msg={'text':'你好'})
        Args:
            id,{string},用户id
            scriptName,{string},用户自定义脚本包名称(文件名和类名)
            msg,{json}, 语义解析返回的结果,json格式

        Returns:
            返回用户处理后的内容,json格式,例如:
            {
                'status': '400',
                'msg': {
                        'text': '用户模块调用异常,请检查用户自定义执行脚本'
                        }
            }
        """

        package = "utils.dialogManager.src.userScript."+id+"."+scriptName
        try:
            moudle = __import__(package,fromlist=True)
            # 使用getattr()获取moudle的类
            moudleClass = getattr(moudle, scriptName)
            data = moudleClass().handler(msg)
            # ret = {}
            # ret['status'] = '200'
            # ret['msg'] = data
            data['status']='200'
            ret = data
            return ret
        except:
            traceback.print_exc()
            return {'status': '400','text': '用户模块调用异常,请检查用户自定义执行脚本'}


"""
Main file  for declaring all constants needed for  VSA Automation
"""

import os
from enum import Enum

APP_TYPE = 106
Ip_regex = "(^169\.)"
AppName = "VIRTUAL SERVER"
AutomationAppType = "Q_VIRTUAL_SERVER"
PseuDoclientPropList = 'Virtual Server Host', 'Virtual Server User'
vm_pattern_names = {'[OS]': 'GuestOS',
                    '[DNS]': 'server_name',
                    '[DS]': 'Datastore',
                    '[VM]': 'vm_name',
                    '[HN]': 'server_clientname'}
content_types = {'Virtual Machine': "[VM]", '1': "[HN]"}
disk_count_command = "(get-disk | select number, size).count"

class hypervisor_type(Enum):
    """
    Enum class for declaring allt he Hypervior types
    """

    VIRTUAL_CENTER = "VMware"
    MS_VIRTUAL_SERVER = "Hyper-V"
    AZURE = "Azure"
    AZURE_V2 = "Azure Resource Manager"
    Fusion_Compute = "FusionCompute"

def on_premise_hypervisor(instance_name):
    """
    :param instance_name:  Instance name of the Instance need to be checked
    :return:
        True if the Hypervisor is on premise else false

    """
    vendor = {"vmware": True, 'hyper-v': True, 'azure resource manager': False, 'Azure': False, 'fusioncompute':False}
    return vendor[instance_name]

def is_dynamic_type(vm_name, vm_type):
    """
    check whether the VM string passed contain dynamic VM

    Args:
            vm_name: name of the VM eg: VM1, VM*
            vm_type  : Type of the Input like VM name, HostName

    Returns:
            Bool value based on dynamic or not
    """

    is_not_dynamic = False
    vm_name_type = ['9', '10']
    if vm_type in vm_name_type:
        if '*' not in vm_name:
            is_not_dynamic = True

    return is_not_dynamic

def is_windows(os_name):
    """
    check for the os in Windows or Unix flavours

    Args:
    os_name - Nmae of the OS

    returns:
            bool value based whether the OS is windows or not
    """
    return bool(os_name == "Windows")

def get_live_browse_db_path(base_dir):
    """
    Get the db path for the live browse
    Args:
            base_dir - base directory where the contentstore is installed

    return:

    """

    return os.path.join(base_dir, "PseudoMount", "Persistent", "PseudoMountDB")

def get_live_browse_mount_path(base_dir, GUID, os_name):
    """
    Get the devices mount path for the live browse
    Args:
            base_dir - base directory where the contentstore is installed

            GUID - GUID of the VM browsed

    return:
        devices mount path for the live browse

    """

    if os_name == "Windows":
        return os.path.join(base_dir, "PseudoMount", "Persistent", "PseudoDevices", GUID)

    else:
        return "/opt/FBR/cvblk_mounts"

def get_folder_to_be_compared(folder_name, _driveletter):
    """
    return the default folder restore path
    Args:
            FolderName - name of the folder
            _driveletter- drive letter where data was copied

    """

    if _driveletter is None:
        return "C:\\TestData\\{0}\\".format(folder_name)
    else:
        pid = str(os.getpid())
        return os.path.join(_driveletter, "\\" + folder_name, "TestData", pid)

def BrowseFilters():
    """
    :return: Browse Filters for XML
    """
    return r"""<?xml version='1.0' encoding='UTF-8'?><databrowse_Query type="0"
    queryId="0"><dataParam><sortParam ascending="1"><sortBy val="38"
    /><sortBy val="0" /></sortParam><paging firstNode="0" pageSize="100"
    skipNode="0" /></dataParam></databrowse_Query>"""


'''
In this test model we don't have a MIP generator, just random Poisson input for both excitation and inhibition.
'''

import nest
import numpy as np
import pylab as pl
import scipy
import scipy.io as sio
import os

import time

start_time = time.time()

''' Parameters '''

# Data
# folder_path = '/media/filipovic/04ae9579-3cb8-48ac-8fe5-f439a9f44a82/data/neuronal_variability/KLD_test/100smpls/tau_i/'
folder_path = '/export/filipovic/work/EuroSPIN/Projects/Neuronal_variability/Python/data/data4/'#'../data/data/'
# folder_path = '/home/markof/work/EuroSPIN/Projects/Neuronal_variability/Python/data/data/'
# for the_file in os.listdir(folder_path):
#     file_path = os.path.join(folder_path, the_file)
#     if os.path.isfile(file_path):
#         os.remove(file_path)

# Simulation and neurons
trials = 1  # number of repetitions/trials for each parameter set
nr_neurons = 100  # equivalent to the number of trials
sim_time = 1000.
exc_shift = 0.  # time delay for excitatory input
inh_shift = 5.  # time delay for inhibitory input
tot_nr_ex = 100  # this many excitatory input to a single neuron/trial
tot_nr_in = 25  # this many inhibitory input to a single neuron/trial

# Set up "noise" parameters
rate_sd = [0., 0.]  # the lower limit for correlation and rate variations is always zero;
                    # the second element we preallocate here
# Synapses
J_exc = [3.2*4]#[round(ii, 1) for ii in np.arange(2.2, 10.7, 0.5)]  # 3.2 will give 0.5 mV @-54mV
J_inh = [-1.35*4]#[round(ii, 2) for ii in np.arange(-0.35, -8.85, -0.5)]  # -1.35 will give 0.5 mV @-54mV
# External input parameters
rate = [10./4]#[round(ii, 1) for ii in np.arange(10., 201., 10.)]  # excitatory and inhibitory populations input rate  [100.]#
rate_sd_max = [0.]#[round(ii, 1) for ii in np.arange(0.1, 1.1, 0.1)]  # maximum factors for drawing the random SD of the input rate
poi_rate = [2000./4]#[2000., 3000., 4000., 5000.]  # [Hz] excitatory background rate
# Internal neuron parameters
gl = [16.7]#[round(ii, 1) for ii in np.arange(8.7, 22.7, 2.0)]  # default is [16.7]#
cm = [250.]#[round(ii) for ii in np.arange(100., 400., 25.)]  # default is [250.]#
tau_e = [0.3]#[round(ii, 1) for ii in np.arange(0.1, 0.7, 0.1)]  # default is [0.3]#
tau_i = [2.0]#[round(ii, 1) for ii in np.arange(1.7, 2.4, 0.1)]  # default is [2.0]#

''' Simulations '''

for gg in xrange(len(J_exc)):
    for hh in xrange(len(J_inh)):
        for ii in xrange(len(rate)):
            for kk in xrange(len(poi_rate)):
                for ll in xrange(len(gl)):
                    for mm in xrange(len(cm)):
                        for nn in xrange(len(tau_e)):
                            for oo in xrange(len(tau_i)):
                                for qq in xrange(len(rate_sd_max)):

                                    n = 1

                                    for rr in xrange(trials):

                                        rate_sd[1] = rate_sd_max[qq]

                                        neuron_params = {'V_th': 1e4,
                                                         'V_reset': -70.0,
                                                         't_ref': 2.0,
                                                         'g_L': gl[ll],
                                                         'C_m': cm[mm],
                                                         'E_ex': 0.0,
                                                         'E_in': -80.0,
                                                         'tau_syn_ex': tau_e[nn],
                                                         'tau_syn_in': tau_i[oo],
                                                         'E_L': -70.}

                                        # generate file name
                                        f_name = 'mem_pot' + '_pwc_' + '{:.2f}'.format(0.00) + \
                                                 '_irate_' + str(rate[ii]) + \
                                                 '_poirate_' + '{:.1f}'.format(poi_rate[kk]) + \
                                                 '_gl_' + '{:.1f}'.format(gl[ll]) + \
                                                 '_cm_' + '{:.1f}'.format(cm[mm]) + \
                                                 '_taue_' + '{:.1f}'.format(tau_e[nn]) + \
                                                 '_taui_' + '{:.1f}'.format(tau_i[oo]) + \
                                                 '_pwcsd_' + '{:.2f}'.format(0.00) + \
                                                 '_ratesd_' + '{:.1f}'.format(rate_sd_max[qq]) + \
                                                 '-{}'.format(n)

                                        # set up NEST
                                        nest.ResetKernel()
                                        nest.SetStatus([0], {'resolution':0.1, 'data_path': folder_path, 'overwrite_files': True})

                                        # create and set up stuff
                                        # neuron
                                        main_neuron = nest.Create('iaf_cond_alpha', nr_neurons, neuron_params)
                                        # generators
                                        poiss_ex = nest.Create('poisson_generator', tot_nr_ex, {'rate': rate[ii]})
                                        poiss_in = nest.Create('poisson_generator', tot_nr_in, {'rate': rate[ii]})
                                        poi_gen = nest.Create('poisson_generator', 1, {'rate': poi_rate[kk]})
                                        # devices
                                        multi = nest.Create('multimeter', params={'withtime': True, 'interval': 0.1, 'record_from': ['V_m']})
                                        nest.SetStatus(multi, {'label': f_name, 'to_file': True})

                                        # set up synapses
                                        # neurons/trials
                                        syn_param_static_exc = {'weight': J_exc[gg], 'delay': exc_shift+1.0}
                                        syn_param_static_inh = {'weight': J_inh[hh], 'delay': inh_shift+1.0}
                                        nest.CopyModel("static_synapse", "syn_exc", syn_param_static_exc)
                                        nest.CopyModel("static_synapse", "syn_inh", syn_param_static_inh)

                                        # set up each neuron/trial
                                        nest.Connect(poiss_ex, main_neuron, syn_spec={'model': 'syn_exc'})
                                        nest.Connect(poiss_in, main_neuron, syn_spec={'model': 'syn_inh'})
                                        # for nr in range(nr_neurons):
                                        #     # define random variables
                                        #     rand_seed = np.int(np.random.random_integers(0, 500, 1))    # seed for the MIP generator
                                        #     # connect excitatory pool
                                        #     ini_ex = nr * exc_inp_per_neuron
                                        #     fin_ex = ini_ex + exc_inp_per_neuron-1
                                        #     nest.Connect(poiss_ex[ini_ex:fin_ex], [main_neuron[nr]], syn_spec={'model': 'syn_exc'})
                                        #     # connect inhibitory pool
                                        #     ini_in = nr * inh_inp_per_neuron
                                        #     fin_in = ini_in + inh_inp_per_neuron-1
                                        #     nest.Connect(poiss_in[ini_in:fin_in], [main_neuron[nr]], syn_spec={'model': 'syn_inh'})

                                        # make additional connections
                                        nest.Connect(poi_gen, main_neuron, syn_spec={'model': 'syn_exc'})  # background Poisson input to all the neurons
                                        nest.Connect(multi, main_neuron)  # from multimeter to all the neurons/trials

                                        # simulate
                                        nest.Simulate(sim_time)

                                        n += 1

print("--- %s seconds ---\n" % (time.time() - start_time))


__author__ = 'gwd'

from numpy import *
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
x = random.rand(50,30)
#basic
f1 = plt.figure(1)
plt.subplot(211)
plt.scatter(x[:,1],x[:,0])

# with label
plt.subplot(212)
label = list(ones(20))+list(2*ones(15))+list(3*ones(15))
label = array(label)
plt.scatter(x[:,1],x[:,0],15.0*label,15.0*label)

def find(label,num):
    return np.where(label == num)

# with legend
f2 = plt.figure(2)

idx_1 =np.where(label == 1)
p1 = plt.scatter(x[idx_1,1], x[idx_1,0], marker ='x', color = 'm', label='1', s = 30)
idx_2 =np.where(label == 2)
p2 = plt.scatter(x[idx_2,1], x[idx_2,0], marker = '+', color ='c', label='2', s = 50)
idx_3 =np.where(label == 3)
p3 = plt.scatter(x[idx_3,1], x[idx_3,0], marker = 'o', color = 'r', label='3', s = 15)
plt.legend(loc ='upper right')
plt.show()
print('end');


from optimizedf.optimize_df import optimize_df
import numpy as np
import pandas as pd
import os

import dask.dataframe as dd
import pickle

# from pysus.utilities.readdbc import read_dbc
import wget
import random
from zipfile import ZipFile
import re
from dbfread import DBF

import warnings
warnings.filterwarnings('ignore')

from v1.utils import CACHEPATH, RAW_DATA, PRODUCED_DATASETS, ALL_FILES

'''
def _download_rdc_file(filename):
    ''''''
    # Download rdc files from the datasus ftp if it doesn't exist and save locally in .parquet file.
    ''''''
    if not filename.startswith('RD') and not filename.endswith('.dbc'):
        raise NameError('filename must be a valid SIH SUS .dbc file')

    if filename == 'RDAC0909.dbc':
        return None

    ftp_file = 'ftp://ftp.datasus.gov.br/dissemin/publicos/SIHSUS/200801_/Dados/' + filename
    parquet_file = filename.split('.')[0] + '.parquet'
    local_file = os.path.join(RAW_DATA, parquet_file)
    if not os.path.exists(local_file):
        wget.download(ftp_file)
        df = read_dbc(filename, encoding='iso-8859-1')
        df.to_parquet(local_file, engine='fastparquet')
        os.unlink(filename)

def _make_all_files_parquet():

    df = dd.read_parquet(os.path.join(RAW_DATA, 'RD*.parquet'))

    LIST_HEMATO = ['R71', 'D500', 'D508', 'D510', 'D518', 'D520', 'D521', 'D339',
                   'D550', 'D551', 'D561', 'D563', 'D568', 'D569', 'D570', 'D571',
                   'D572', 'D573', 'D578', 'D582', 'D580', 'D581', 'D589', 'D590',
                   'D591', 'D593', 'D594', 'D595', 'D599', 'D65', 'D66', 'D67',
                   'D680', 'M250', 'M362', 'D681', 'D682', 'D683', 'D684', 'D688',
                   'D693', 'D689', 'D690', 'D691', 'D692', 'D695', 'D698', 'D70',
                   'D72', 'D721', 'D728', 'D730', 'D731', 'D732', 'D750', 'D751',
                   'D45', 'D473', 'D752', 'D760', 'D761', 'D762', 'D890', 'D891',
                   'E831', 'K80', 'M250', 'M311', 'M329', 'I260', 'I74', 'I780',
                   'I82', 'D595', 'D600', 'D601', 'D610', 'D611', 'D613', 'D619',
                   'D641', 'D642', 'D648', 'D649', 'D460', 'D461', 'D462', 'D463',
                   'D464', 'D471', 'C819', 'C811', 'C812', 'C813', 'C829', 'C820',
                   'C821', 'C822', 'C830', 'C833', 'C837', 'C835', 'C844', 'C845',
                   'C915', 'C840', 'C841', 'C950', 'C910', 'C920', 'C925', 'C942',
                   'C911', 'C921', 'C90', 'C902', 'C901', 'C880', 'D472']

    COLS = 'N_AIH UF_ZI DT_INTER DT_SAIDA US_TOT DIAS_PERM ANO_CMPT DIAG_PRINC MORTE IDADE CNES\
            SEXO ETNIA COMPLEX CAR_INT MUNIC_RES MUNIC_MOV NASC'.split()

    df = df[COLS]
    df = df[df['DIAG_PRINC'].isin(LIST_HEMATO)]
    df['ETNIA'] = df['ETNIA'].fillna('0000')
    df = df.compute()
    df.UF_ZI = df.UF_ZI.str[:2]
    df.to_parquet(ALL_FILES)#, skipna=False) # 6 minutos

def make_all_dataset(force_download=False):
    ''''''''
    Make all hematologic internations from Brazil in the interval 2008 to 2017.
    When all in cache, takes only about 20" to parse all dataset.

    Download from DATASUS ftp when not availiable locally and save each year in a .parquet format.
    It takes about 45' to parse all .dbc files again using all files loccally (force_download='soft'),
    and ~8 hours to download them (force_download='deep' or first download).

    Parameters
    ----------
    force_download: bool, default False

        string (optional), default 'no'
        'deep' = Force new download of the data.
        'soft' = use .dbc files already downloaded and make the parquet files again, util to change
        columns or CIDs, p. e.

    Returns
    -------
        pandas.DataFrame
    ''''''
    if force_download and os.path.exists(ALL_FILES):
        os.unlink(ALL_FILES)

    if not os.path.exists(ALL_FILES):
        _download_all_files()
        _make_all_files_parquet()

    df = dd.read_parquet(ALL_FILES).compute()

    for c in ['NASC', 'DT_INTER', 'DT_SAIDA']:
        df[c] = pd.to_datetime(df[c])

    # optimizing
    p_file = os.path.join(PRODUCED_DATASETS, 'optimized_columns.p')
    if not os.path.exists(p_file):
        opt_columns = optimize_df(df)
        pickle.dump(opt_columns, open(p_file, 'wb'))
    opt_columns = pickle.load(open(p_file, 'rb'))
    df = df.astype(opt_columns)

    return df

def _download_all_files():
    initial_year = 8
    final_year = 18

    UFS = ['AC', 'AL', 'AP', 'AM', 'BA', 'CE', 'DF', 'ES', 'GO', 'MA', 'MT',
            'MS', 'MG', 'PA', 'PB', 'PR', 'PE', 'PI', 'RJ', 'RN', 'RS', 'RO',
            'RR', 'SC', 'SP', 'SE', 'TO']

    n = 0
    print("{} doesn't exist, fetching files...".format(ALL_FILES.split('/')[-1]))
    for year in range(final_year, initial_year - 1, -1):
        y = str(year).zfill(2)
        print('\nAno: 20{}'.format(y))
        for month in range(1, 13):
            print()
            for uf in UFS:
                m = str(month).zfill(2)
                filename = 'RD{}{}{}.dbc'.format(uf, y, m)
                print('.', end = ' ')
                df = _download_rdc_file(filename)
    print()
'''

def download_zip(url, force_download=False, prefix=None):
    '''
    Download TAB_SIH.zip and save in cache.

    Returns
    -------
        local path to the downloaded file
    '''
    if prefix != None:
        filename = '{}_'.format(prefix) + wget.detect_filename(url)
        print('filename', filename)
    else:
        filename = wget.detect_filename(url)

    local_file = os.path.join(PRODUCED_DATASETS, filename)
    if not os.path.exists(local_file) or force_download:
        wget.download(url, local_file)
    return local_file

def download_layout(force_download=False):
    '''
    Download the PDF with the layout of the data and save it in the local folder.
    '''
    url = 'ftp://ftp.datasus.gov.br/dissemin/publicos/SIHSUS/200801_/Doc/IT_SIHSUS_1603.pdf'
    filename = wget.detect_filename(url)
    if not os.path.exists(filename) or force_download:
        wget.download(url, filename)

def make_maps(force_download=False):

    CID_URL = 'http://www.datasus.gov.br/cid10/V2008/downloads/CID10CSV.zip'
    local_zipfile = download_zip(CID_URL, force_download)
    with ZipFile(local_zipfile) as zfile:
        l = zfile.namelist()

        with zfile.open('CID-10-SUBCATEGORIAS.CSV') as cid_file:
            cid_map = pd.read_csv(cid_file, sep=';', encoding='iso-8859-1', usecols=['SUBCAT', 'DESCRICAO'],
                                 index_col='SUBCAT', squeeze=True)

    TAB_SIH_URL = 'ftp://ftp.datasus.gov.br/dissemin/publicos/SIHSUS/200801_/Auxiliar/TAB_SIH.zip'
    local_zipfile = download_zip(TAB_SIH_URL, force_download)
    with ZipFile(local_zipfile) as zfile:
        l = zfile.namelist()

        # br_municip.cnv
        with zfile.open('br_municip.cnv') as file:
            mun_map = pd.read_table(file, sep=b'\s{2,}', engine='python', encoding='iso-8859-1',
                                   skiprows=1, header=None, skipfooter=3).applymap(func_decode)
            mun_map[1] = mun_map[1].str.split(' ', 1).str.get(1)
            mun_map = pd.Series(mun_map[1].tolist(), index=mun_map[2].tolist())
            for i, mun in mun_map[mun_map.str.startswith('Município')].items():
                for i2 in i.split(','):
                    mun_map[str(i2).zfill(6)] = mun
                mun_map.drop(i, inplace=True)
            mun_map = mun_map.to_dict()
            for i in ['520000', '529999']:
                mun_map[i] = 'Município ignorado - GO'

            list_brasilia = []
            for i in range(530000, 530009 + 1):
                list_brasilia.append(str(i))
            for i in range(530011, 539999 + 1):
                list_brasilia.append(str(i))
            list_brasilia.append('530010')

            for i in list_brasilia:
                mun_map[i] = 'Brasília'

            list_ign = []
            for i in range(1, 9999):
                list_ign.append(str(i).zfill(6))

            list_ign = list_ign + ['000000', '999999']
            for i in list_ign:
                mun_map[i] = 'Ignorado ou exterior'

        # CARATEND.CNV
        with zfile.open('CARATEND.CNV') as file:
            caratend_map = pd.read_table(file, sep=b'\s{2,}', engine='python', encoding='iso-8859-1',
                                       skiprows=1, header=None).applymap(func_decode)
            caratend_map = { '01': 'Eletivo',
                             '02': 'Urgência',
                             '03': 'Acidente no local trabalho ou a serv da empresa',
                             '04': 'Acidente no trajeto para o trabalho',
                             '05': 'Outros tipo de acidente de trânsito',
                             '06': 'Out tp lesões e envenen por agent quím físicos'}
            #fiz manualmente porque não funcionou o encoding e o decode

        # COMPLEX2.CNV
        with zfile.open('COMPLEX2.CNV') as file:
            comp_map = pd.read_table(file, sep=b'\s{2,}', engine='python', encoding='iso-8859-1',
                                       skiprows=1, header=None).applymap(func_decode)
            comp_map = {'00': 'Não se aplica',
                        '01': 'Atenção básica',
                        '02': 'Média complexidade',
                        '03': 'Alta complexidade',
                        '99': 'Não se aplica'}
            #fiz manualmente porque não funcionou o encoding e o decode

        # FINANC.CNV
        with zfile.open('FINANC.CNV') as file:
            df = pd.read_table(file, sep=b'\s{2,}', engine='python', encoding='iso-8859-1',
                                   skiprows=1, header=None).applymap(func_decode)
            temp = pd.DataFrame([{1:'Não discriminado', 2:'00'}, {1:'Não discriminado', 2:'99'},
                                 {1:'04 Fundo de Ações Estratégicas e Compensações FAEC', 2:'04'}])
            df = pd.concat([df, temp], ignore_index=True).drop(0, axis=1).drop([0, 3])
            financ_map = pd.Series(df[1].tolist(), index=df[2].tolist())

        # ETNIA
        with zfile.open('etnia.cnv') as et_file:
            df_et = pd.read_table(et_file, sep=b'\s{2,}', engine='python', encoding='iso-8859-1',
                                   skiprows=1, header=None).applymap(func_decode)
            ni = pd.DataFrame([{1:'NÃO INFORMADO', 2:'0000'}, {1:'NÃO INFORMADO', 2:'9999'}])
            df_et = pd.concat([df_et, ni], ignore_index=True).drop(0, axis=1).drop(0)
            et_map = pd.Series(df_et[1].tolist(), df_et[2].tolist())

        # SEX
        with zfile.open('SEXO.CNV') as sex_file:
            df = pd.read_table(sex_file, sep=b'\s{2,}', engine='python',
                               skiprows=1, header=None).applymap(func_decode)
            sex_map = {df.loc[0, 2][0]: df.loc[0, 1],
                       df.loc[0, 2][2]: df.loc[0, 1],
                       df.loc[1, 2]:    df.loc[1, 1],
                       df.loc[2, 2][0]: df.loc[2, 1],
                       df.loc[2, 2][2]: df.loc[2, 1]}

        # UF
        with zfile.open('br_ufsigla.cnv') as uf:
            df = pd.read_table(uf, sep=b'\s{2,}', engine='python',
                               skiprows=1, header=None).applymap(func_decode).dropna(axis=1)
            pe = pd.DataFrame([{1:'PE', 2:'20'}, {1:'PE', 2:'26'}])
            df = pd.concat([df, pe], ignore_index=True)
            df.drop(12, inplace=True)
            uf_map = pd.Series(df[1].tolist(), df[2].tolist())

        # CNES
        list_df = []
        list_cnes = [item for item in l if re.search('\S+CNES\S+', item)]
        for file in list_cnes:
            zfile.extract(file)
            dbf = DBF(file, encoding='utf-8')
            cnes_temp = pd.DataFrame(list(dbf))
            list_df.append(cnes_temp)
            os.unlink(file)
        cnes_br = pd.concat(list_df, sort=False).drop_duplicates()
        cnes_br['UF_ZI'] = cnes_br.UF_ZI.map(uf_map)
        cnes_map = pd.Series(cnes_br.NOMEFANT.tolist(), index=cnes_br.CNES.tolist())

        # CBO.dbf
        file = 'CBO.dbf'
        zfile.extract(file)
        dbf = DBF(file, encoding='iso-8859-1')
        cbo_map = pd.DataFrame(list(dbf))
        cbo_map = pd.Series(cbo_map.DS_CBO.tolist(), index=cbo_map.CBO.tolist())
        os.unlink(file)

    return sex_map, cnes_map, uf_map, cid_map, et_map, financ_map, cbo_map, comp_map, mun_map, caratend_map

def func_decode(x):
    if type(x) == bytes:
        try:
            return x.decode()
        except UnicodeDecodeError:
            return x.decode(encoding='iso-8859-1')


import logging

from PySide.QtGui import *

from maya import cmds

from ... import misc
from .. import maya_decorators
from ..base_ui import MayaQWidget
from .. import constants

from ..skeletonBuilder import spaceSwitching, baseRigPart

import switching

logger = logging.getLogger(__name__)

class BaseSwitchingWidget(MayaQWidget):
    SWITCH_BUTTON_LBL = '<no label set>'
    OPTIONS_LBL = '<no label set>'

    def __init__(self):
        super(BaseSwitchingWidget, self).__init__()

        self.options = QComboBox()

        self.performSwitch = QPushButton(self.SWITCH_BUTTON_LBL)
        self.performSwitch.clicked.connect(self.switch)

        hlayout = QHBoxLayout()
        hlayout.addWidget(QLabel(self.OPTIONS_LBL))
        hlayout.addWidget(self.options, 1)

        layout = QVBoxLayout()
        layout.addLayout(hlayout)
        layout.addWidget(self.performSwitch)
        self.setLayout(layout)

        self.setSelectionChangeCB(self.updateWidgets)
        self.updateWidgets()

class SpaceSwitchingWidget(BaseSwitchingWidget):
    SWITCH_BUTTON_LBL = 'Perform Space Switch'
    OPTIONS_LBL = 'Available Parents'

    def updateWidgets(self):

        # clear any existing parents
        self.options.clear()

        enableUI = False

        # now populate the UI based on the selection
        sel = cmds.ls(sl=True)
        if sel:
            spaceNames = []

            # so we have a selection, but do we have any space switching nodes selected?
            for node in sel:
                if spaceSwitching.SpaceSwitchNode.IsA(node):

                    # extend the list of names
                    spaceNames += spaceSwitching.SpaceSwitchNode(node).names

            # remove any duplicate names
            spaceNames = misc.removeDupes(spaceNames)

            if spaceNames:
                enableUI = True

                # add the space names to the list
                for name in spaceNames:
                    self.options.addItem(name)

        self.options.setEnabled(enableUI)
        self.performSwitch.setEnabled(enableUI)

    def switch(self):
        selectedSpace = self.options.currentText()

        selectedNodes = cmds.ls(sl=True)

        # get a list of nodes we're doing the space switch on.  It may be that not all selected nodes
        # have the selected space
        parentIdxNodeMap = {}
        for node in selectedNodes:
            spaces, names = spaceSwitching.getSpaceTargetsNames(node)
            if selectedSpace in names:
                parentIdx = names.index(selectedSpace)
                parentIdxNodeMap.setdefault(parentIdx, [])
                parentIdxNodeMap[parentIdx].append(node)

        with maya_decorators.UndoBlockContext():
            for idx, nodes in parentIdxNodeMap.iteritems():
                switching.switchParentForAnimatedControls(nodes, idx)

class IkFkSwitchingWidget(BaseSwitchingWidget):
    SWITCH_BUTTON_LBL = 'Switch Now'
    OPTIONS_LBL = 'Switch to..'

    SWITCH_METHODS = (('FK', switching.switchToFkForAnimatedControl),
                      ('IK', switching.switchToIkForAnimatedControl),
                      )

    SWITCH_METHOD_DICT = dict(SWITCH_METHODS)

    def __init__(self):
        super(IkFkSwitchingWidget, self).__init__()

        for methodLabelStr, method in self.SWITCH_METHODS:
            self.options.addItem(methodLabelStr)

        self.updateWidgets()

    def updateWidgets(self):
        enableUI = False

        # have we got a selection?
        sel = cmds.ls(sl=True)
        if sel:

            # are any of these selected nodes parts?
            for node in sel:
                try:
                    part = baseRigPart.RigPart.InitFromItem(node)
                except baseRigPart.RigPartError:
                    continue

                # make sure the part supports ik fk switching (not all parts do!)
                if hasattr(part, 'switchToFk') and hasattr(part, 'switchToIk'):
                    enableUI = True

                    # we only need one...
                    break

        self.options.setEnabled(enableUI)
        self.performSwitch.setEnabled(enableUI)

    def switch(self):
        selectedLbl = self.options.currentText()
        switchMethod = self.SWITCH_METHOD_DICT.get(selectedLbl)
        if not callable(switchMethod):
            logger.error("No valid selection in the options box!")
            return

        with maya_decorators.UndoBlockContext():
            for node in cmds.ls(sl=True):
                switchMethod(node)

class RooSwitchingWidget(BaseSwitchingWidget):
    SWITCH_BUTTON_LBL = 'Switch Now'
    OPTIONS_LBL = 'Switch order to..'

    def __init__(self):
        super(RooSwitchingWidget, self).__init__()

        for rooStr in constants.MAYA_ROTATE_ORDER_STRS:
            self.options.addItem(rooStr)

        self.updateWidgets()

    def updateWidgets(self):
        nodes = switching.onlyTransforms(cmds.ls(sl=True))
        enableUI = bool(nodes)

        self.options.setEnabled(enableUI)
        self.performSwitch.setEnabled(enableUI)

        # Select a sensible default
        if nodes:
            roIdx = cmds.getAttr('%s.ro' % nodes[0])
            self.options.setCurrentIndex(roIdx)

    def switch(self):
        selectedRoo = self.options.currentText()
        switching.switchRooForAnimatedControls(cmds.ls(sl=True), selectedRoo)

class SwitchingTool(MayaQWidget):
    def __init__(self):
        super(SwitchingTool, self).__init__()

        layout = QVBoxLayout()
        layout.addWidget(SpaceSwitchingWidget())
        layout.addWidget(IkFkSwitchingWidget())
        layout.addWidget(RooSwitchingWidget())
        layout.addStretch()
        self.setLayout(layout)

#end


'''
Created on Aug 25, 2021

@author: immanueltrummer
'''
import argparse
import gym
from gym.spaces import Box, Discrete
import heapq
import numpy as np
import os
import pandas as pd
from stable_baselines3 import A2C

class CordulaGym(gym.Env):
    """ RL environment for detecting correlations. """

    step_s = 0.05
    s_per_pred = 26.0/11886

    def __init__(self, pred_path, data_scaling):
        """ Initializes environment with given input file.

        Args:
            pred_path: path to pre-generated predictions
            data_sacling: scaling factor for data processing
        """
        self.df = pd.read_csv(pred_path, sep=',')
        self.df = self.df.sample(frac=1)
        self.nr_pairs = self.df.shape[0]
        self.data_scaling = data_scaling
        self.pred_per_step = round(
            self.step_s * self.data_scaling/self.s_per_pred)
        self.next_pair = 0
        self.heap = []
        self.sim_time = 0.0
        self.sim_hits = 0
        self.log_hits = []
        self.observation_space = Box(low=0, high=1.0, shape=(2,))
        self.action_space = Discrete(3)

    def reset(self):
        return self._observe()

    def step(self, action):
        cur_nr_hits = 0
        if action == 0: # Apply NLP to next batch of column pairs
            nr_remaining = self.nr_pairs - self.next_pair
            batch_size = min(nr_remaining, self.pred_per_step)
            last_pair = self.next_pair + batch_size - 1
            batch = self.df.iloc[self.next_pair:last_pair,:]
            for _, row in batch.iterrows():
                pred = row['predictions']
                label = row['labels']
                cost = row['time']
                heapq.heappush(self.heap, (-pred, label, cost))

            self.next_pair += batch_size + 1
            self.sim_time += batch_size * self.s_per_pred

        elif action == 1: # Process items without predictions
            deadline = self.sim_time + self.step_s
            while self.sim_time < deadline and self.next_pair < self.nr_pairs:
                row = self.df.loc[self.next_pair]
                label = row['labels']
                cost = row['time']
                self.sim_time += cost * self.data_scaling
                self.next_pair += 1
                if label:
                    cur_nr_hits += 1

        elif action == 2: # Process items from heap
            deadline = self.sim_time + self.step_s
            while self.sim_time < deadline and self.heap:
                pred, label, cost = heapq.heappop(self.heap)
                self.sim_time += cost * self.data_scaling
                if label:
                    cur_nr_hits += 1

        self.sim_hits += cur_nr_hits
        self._log()
        return self._observe(), cur_nr_hits, False, {}

    def _log(self):
        self.log_hits.append((self.sim_time, self.sim_hits))

    def _observe(self):
        heap_size = len(self.heap) / self.nr_pairs
        if heap_size > 0:
            pred, _, _ = self.heap[0]
        else:
            pred = 0
        return np.array([heap_size, -pred])

if __name__ == '__main__':

    os.environ['KMP_DUPLICATE_LIB_OK']='True'

    parser = argparse.ArgumentParser()
    parser.add_argument('in_path', type=str, help='Path to input prediction file')
    parser.add_argument('out_dir', type=str, help='Directory for output files')
    args = parser.parse_args()

    for scale in [1, 10, 100, 1000]:
        env = CordulaGym(args.in_path, scale)
        model = A2C(
            'MlpPolicy', env, verbose=True,
            gamma=1.0, normalize_advantage=True)
        model.learn(total_timesteps=20000)

        log_df = pd.DataFrame(env.log_hits, columns=['ctime','chits'])
        log_df['crows'] = 0
        log_df['step'] = 0
        log_df.to_csv(f'{args.out_dir}/alltables_F{scale}_rl.csv')

    #
    #
    # with open('results/alltablesrl.csv', 'w') as file:
        # for


'''
The state is a list of 2 items: the board, the path
The target for 8-puzzle is: (zero is the hole)
012
345
678
'''

from board import *

class State:
    def __init__(self, board, solution):
        self.board = board
        self.solution = solution

    @staticmethod
    def random_start(n):
        return State(Board(n).randomize(), [])

    def get_next_states(self):
        next_states = []
        solution_is_empty = self.solution == []
        # move_is_backwards = self.solution[-1] + move == 5
        for move in range(1,5):    # 1 - 4
            if (self.board.move_is_legal(move) and (solution_is_empty or self.solution[-1] + move != 5)):     # see note at top of board.py
                next_states.append(State(self.board.do_move(move), self.solution + [move]))
        return next_states

    def solution_len(self):
        return len(self.solution)

    def is_solved(self):
        return self.board.is_solved()

    def print_solution(self):
        str = ""
        for move in self.solution:
            if move == 1:
                str += " "
            elif move == 2:
                str += " "
            elif move == 3:
                str += " "
            elif move == 4:
                str += " "
        print(str)