import tensorflow.contrib.slim as slim
import tensorflow as tf
import numpy as np

# Inception-Renset-A
def block35(net, scale=1.0, activation_fn=tf.nn.relu, scope=None, reuse=None):
    """Builds the 35x35 resnet block."""
    with tf.variable_scope(scope, 'Block35', [net], reuse=reuse):
        with tf.variable_scope('Branch_0'):
            tower_conv = slim.conv2d(net, 32, 1, scope='Conv2d_1x1')
        with tf.variable_scope('Branch_1'):
            tower_conv1_0 = slim.conv2d(net, 32, 1, scope='Conv2d_0a_1x1')
            tower_conv1_1 = slim.conv2d(tower_conv1_0, 32, 3, scope='Conv2d_0b_3x3')
        with tf.variable_scope('Branch_2'):
            tower_conv2_0 = slim.conv2d(net, 32, 1, scope='Conv2d_0a_1x1')
            tower_conv2_1 = slim.conv2d(tower_conv2_0, 48, 3, scope='Conv2d_0b_3x3')
            tower_conv2_2 = slim.conv2d(tower_conv2_1, 64, 3, scope='Conv2d_0c_3x3')
        mixed = tf.concat([tower_conv, tower_conv1_1, tower_conv2_2], 3)
        up = slim.conv2d(mixed, net.get_shape()[3], 1, normalizer_fn=None,
                         activation_fn=None, scope='Conv2d_1x1')
        net += scale * up
        if activation_fn:
            net = activation_fn(net)
    return net

# Inception-Renset-B
def block17(net, scale=1.0, activation_fn=tf.nn.relu, scope=None, reuse=None):
    """Builds the 17x17 resnet block."""
    with tf.variable_scope(scope, 'Block17', [net], reuse=reuse):
        with tf.variable_scope('Branch_0'):
            tower_conv = slim.conv2d(net, 192, 1, scope='Conv2d_1x1')
        with tf.variable_scope('Branch_1'):
            tower_conv1_0 = slim.conv2d(net, 128, 1, scope='Conv2d_0a_1x1')
            tower_conv1_1 = slim.conv2d(tower_conv1_0, 160, [1, 7],
                                        scope='Conv2d_0b_1x7')
            tower_conv1_2 = slim.conv2d(tower_conv1_1, 192, [7, 1],
                                        scope='Conv2d_0c_7x1')
        mixed = tf.concat([tower_conv, tower_conv1_2], 3)
        up = slim.conv2d(mixed, net.get_shape()[3], 1, normalizer_fn=None,
                         activation_fn=None, scope='Conv2d_1x1')
        net += scale * up
        if activation_fn:
            net = activation_fn(net)
    return net

# Inception-Resnet-C
def block8(net, scale=1.0, activation_fn=tf.nn.relu, scope=None, reuse=None):
    """Builds the 8x8 resnet block."""
    with tf.variable_scope(scope, 'Block8', [net], reuse=reuse):
        with tf.variable_scope('Branch_0'):
            tower_conv = slim.conv2d(net, 192, 1, scope='Conv2d_1x1')
        with tf.variable_scope('Branch_1'):
            tower_conv1_0 = slim.conv2d(net, 192, 1, scope='Conv2d_0a_1x1')
            tower_conv1_1 = slim.conv2d(tower_conv1_0, 224, [1, 3],
                                        scope='Conv2d_0b_1x3')
            tower_conv1_2 = slim.conv2d(tower_conv1_1, 256, [3, 1],
                                        scope='Conv2d_0c_3x1')
        mixed = tf.concat([tower_conv, tower_conv1_2], 3)
        up = slim.conv2d(mixed, net.get_shape()[3], 1, normalizer_fn=None,
                         activation_fn=None, scope='Conv2d_1x1')
        net += scale * up
        if activation_fn:
            net = activation_fn(net)
    return net

def inference(images, keep_probability, phase_train=True,
              bottleneck_layer_size=128, weight_decay=0.0, reuse=None):
    batch_norm_params = {
        # Decay for the moving averages.
        'decay': 0.995,
        # epsilon to prevent 0s in variance.
        'epsilon': 0.001,
        # force in-place updates of mean and variance estimates
        'updates_collections': None,
        # Moving averages ends up in the trainable variables collection
        'variables_collections': [ tf.GraphKeys.TRAINABLE_VARIABLES ],
    }
    with slim.arg_scope([slim.conv2d, slim.fully_connected],
                        weights_initializer=tf.truncated_normal_initializer(stddev=0.1),
                        weights_regularizer=slim.l2_regularizer(weight_decay),
                        normalizer_fn=slim.batch_norm,
                        normalizer_params=batch_norm_params):
        return inception_resnet_v2(images, is_training=phase_train,
              dropout_keep_prob=keep_probability, bottleneck_layer_size=bottleneck_layer_size, reuse=reuse)

def inception_resnet_v2(inputs, is_training=True,
                        dropout_keep_prob=0.8,
                        bottleneck_layer_size=128,
                        reuse=None,
                        scope='InceptionResnetV2'):
    """Creates the Inception Resnet V2 model.
    Args:
      inputs: a 4-D tensor of size [batch_size, height, width, 3].
      num_classes: number of predicted classes.
      is_training: whether is training or not.
      dropout_keep_prob: float, the fraction to keep before final layer.
      reuse: whether or not the network and its variables should be reused. To be
        able to reuse 'scope' must be given.
      scope: Optional variable_scope.
    Returns:
      logits: the logits outputs of the model.
      end_points: the set of end_points from the inception model.
    """
    end_points = {}

    with tf.variable_scope(scope, 'InceptionResnetV2', [inputs], reuse=reuse):
        with slim.arg_scope([slim.batch_norm, slim.dropout],
                            is_training=is_training):
            with slim.arg_scope([slim.conv2d, slim.max_pool2d, slim.avg_pool2d],
                                stride=1, padding='SAME'):

                # 149 x 149 x 32
                net = slim.conv2d(inputs, 32, 3, stride=2, padding='VALID',
                                  scope='Conv2d_1a_3x3')
                end_points['Conv2d_1a_3x3'] = net
                # 147 x 147 x 32
                net = slim.conv2d(net, 32, 3, padding='VALID',
                                  scope='Conv2d_2a_3x3')
                end_points['Conv2d_2a_3x3'] = net
                # 147 x 147 x 64
                net = slim.conv2d(net, 64, 3, scope='Conv2d_2b_3x3')
                end_points['Conv2d_2b_3x3'] = net
                # 73 x 73 x 64
                net = slim.max_pool2d(net, 3, stride=2, padding='VALID',
                                      scope='MaxPool_3a_3x3')
                end_points['MaxPool_3a_3x3'] = net
                # 73 x 73 x 80
                net = slim.conv2d(net, 80, 1, padding='VALID',
                                  scope='Conv2d_3b_1x1')
                end_points['Conv2d_3b_1x1'] = net
                # 71 x 71 x 192
                net = slim.conv2d(net, 192, 3, padding='VALID',
                                  scope='Conv2d_4a_3x3')
                end_points['Conv2d_4a_3x3'] = net
                # 35 x 35 x 192
                net = slim.max_pool2d(net, 3, stride=2, padding='VALID',
                                      scope='MaxPool_5a_3x3')
                end_points['MaxPool_5a_3x3'] = net

                # 35 x 35 x 320
                with tf.variable_scope('Mixed_5b'):
                    with tf.variable_scope('Branch_0'):
                        tower_conv = slim.conv2d(net, 96, 1, scope='Conv2d_1x1')
                    with tf.variable_scope('Branch_1'):
                        tower_conv1_0 = slim.conv2d(net, 48, 1, scope='Conv2d_0a_1x1')
                        tower_conv1_1 = slim.conv2d(tower_conv1_0, 64, 5,
                                                    scope='Conv2d_0b_5x5')
                    with tf.variable_scope('Branch_2'):
                        tower_conv2_0 = slim.conv2d(net, 64, 1, scope='Conv2d_0a_1x1')
                        tower_conv2_1 = slim.conv2d(tower_conv2_0, 96, 3,
                                                    scope='Conv2d_0b_3x3')
                        tower_conv2_2 = slim.conv2d(tower_conv2_1, 96, 3,
                                                    scope='Conv2d_0c_3x3')
                    with tf.variable_scope('Branch_3'):
                        tower_pool = slim.avg_pool2d(net, 3, stride=1, padding='SAME',
                                                     scope='AvgPool_0a_3x3')
                        tower_pool_1 = slim.conv2d(tower_pool, 64, 1,
                                                   scope='Conv2d_0b_1x1')
                    net = tf.concat([tower_conv, tower_conv1_1,
                                        tower_conv2_2, tower_pool_1], 3)

                end_points['Mixed_5b'] = net
                net = slim.repeat(net, 10, block35, scale=0.17)

                # 17 x 17 x 1024
                with tf.variable_scope('Mixed_6a'):
                    with tf.variable_scope('Branch_0'):
                        tower_conv = slim.conv2d(net, 384, 3, stride=2, padding='VALID',
                                                 scope='Conv2d_1a_3x3')
                    with tf.variable_scope('Branch_1'):
                        tower_conv1_0 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1')
                        tower_conv1_1 = slim.conv2d(tower_conv1_0, 256, 3,
                                                    scope='Conv2d_0b_3x3')
                        tower_conv1_2 = slim.conv2d(tower_conv1_1, 384, 3,
                                                    stride=2, padding='VALID',
                                                    scope='Conv2d_1a_3x3')
                    with tf.variable_scope('Branch_2'):
                        tower_pool = slim.max_pool2d(net, 3, stride=2, padding='VALID',
                                                     scope='MaxPool_1a_3x3')
                    net = tf.concat([tower_conv, tower_conv1_2, tower_pool], 3)

                end_points['Mixed_6a'] = net
                net = slim.repeat(net, 20, block17, scale=0.10)

                with tf.variable_scope('Mixed_7a'):
                    with tf.variable_scope('Branch_0'):
                        tower_conv = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1')
                        tower_conv_1 = slim.conv2d(tower_conv, 384, 3, stride=2,
                                                   padding='VALID', scope='Conv2d_1a_3x3')
                    with tf.variable_scope('Branch_1'):
                        tower_conv1 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1')
                        tower_conv1_1 = slim.conv2d(tower_conv1, 288, 3, stride=2,
                                                    padding='VALID', scope='Conv2d_1a_3x3')
                    with tf.variable_scope('Branch_2'):
                        tower_conv2 = slim.conv2d(net, 256, 1, scope='Conv2d_0a_1x1')
                        tower_conv2_1 = slim.conv2d(tower_conv2, 288, 3,
                                                    scope='Conv2d_0b_3x3')
                        tower_conv2_2 = slim.conv2d(tower_conv2_1, 320, 3, stride=2,
                                                    padding='VALID', scope='Conv2d_1a_3x3')
                    with tf.variable_scope('Branch_3'):
                        tower_pool = slim.max_pool2d(net, 3, stride=2, padding='VALID',
                                                     scope='MaxPool_1a_3x3')
                    net = tf.concat([tower_conv_1, tower_conv1_1,
                                        tower_conv2_2, tower_pool], 3)

                end_points['Mixed_7a'] = net

                net = slim.repeat(net, 9, block8, scale=0.20)
                net = block8(net, activation_fn=None)

                net = slim.conv2d(net, 1536, 1, scope='Conv2d_7b_1x1')
                end_points['Conv2d_7b_1x1'] = net

                with tf.variable_scope('Logits'):
                    end_points['PrePool'] = net
                    #pylint: disable=no-member
                    net = slim.avg_pool2d(net, net.get_shape()[1:3], padding='VALID',
                                          scope='AvgPool_1a_8x8')
                    net = slim.flatten(net)

                    net = slim.dropout(net, dropout_keep_prob, is_training=is_training,
                                       scope='Dropout')

                    end_points['PreLogitsFlatten'] = net

                net = slim.fully_connected(net, bottleneck_layer_size, activation_fn=None,
                        scope='Bottleneck', reuse=False)

    return net, end_points

image = tf.placeholder(tf.float32, [None, 224, 224, 1])
land_mark = tf.placeholder(tf.float32, [None, 22])
disease_class = tf.placeholder(tf.float32, [None, 11, 5])

net, end_points = inception_resnet_v2(image)
print(net.shape)

predict_land_mark = tf.layers.dense(net, 22, activation=tf.nn.relu)
predict_logits = tf.layers.dense(net, 55, activation=tf.nn.relu)

predict_logits = tf.squeeze(predict_logits)
predict_land_mark = tf.squeeze(predict_land_mark)
print('predict_logits', predict_logits.shape)
print('predict_land_mark', predict_land_mark.shape)

predict_logits = tf.reshape(predict_logits, [-1, 11, 5])
predict_logits = tf.nn.sigmoid(predict_logits)

loss_box = tf.sqrt(tf.reduce_mean((predict_land_mark - land_mark)** 2))

loss_class = tf.nn.sigmoid_cross_entropy_with_logits(labels=disease_class, logits=predict_logits)
loss_class = tf.reduce_mean(
    tf.reduce_sum(loss_class, axis=1)
)

one_hot_prediction = tf.cast(tf.greater(predict_logits, 0.5), tf.float32)
print('one_hot_prediction', one_hot_prediction.shape)
accuracy = tf.reduce_mean(tf.cast(tf.equal(disease_class,one_hot_prediction), tf.float32))
#用梯度迭代算法
loss = loss_box + loss_class
train_step = tf.train.GradientDescentOptimizer(1e-3).minimize(loss)

# #定义会话
sess = tf.Session()
#初始化所有变量
sess.run(tf.global_variables_initializer())
saver = tf.train.Saver()
#saver.restore(sess, tf.train.latest_checkpoint("model"))

train_files = np.load(r"2020-06-14\lumbar_train150\data.npz")
train_file_image = train_files["data"]
train_file_label = train_files["land_marks"]
train_file_class = train_files['disease_class']

test_files = np.load(r"2020-06-14\lumbar_train51\data.npz")
test_file_image = test_files["data"]
test_file_label = test_files["land_marks"]
test_file_class = test_files['disease_class']

train_file_image = train_file_image.astype('float32')
train_file_image -= np.mean(train_file_image, axis=1, keepdims=True)
train_file_image /= (np.max(np.abs(train_file_image), axis=1, keepdims=True) + 1e-6)

test_file_image = test_file_image.astype('float32')
test_file_image -= np.mean(test_file_image, axis=1, keepdims=True)
test_file_image /= (np.max(np.abs(test_file_image), axis=1, keepdims=True) + 1e-6)

train_file_image = np.reshape(train_file_image, [-1, 224, 224, 1])
test_file_image = np.reshape(test_file_image, [-1, 224, 224, 1])

for itr in range(2000):
    idx = np.random.randint(0, len(train_file_label), [5])
    batch_xs, batch_ys, batch_zs = train_file_image[idx], train_file_label[idx], train_file_class[idx]
    sess.run(train_step,
    feed_dict={image: batch_xs, land_mark: batch_ys, disease_class:batch_zs})
    if itr % 10 == 0:
      print('itr', itr)
      accu_list = []
      loss_list = []
      for num in range(15):
        num_acc, num_loss = sess.run([accuracy, loss_box], feed_dict={image: train_file_image[num * 10:(num + 1) * 10],
                          land_mark: train_file_label[num * 10:(num + 1) * 10], disease_class: train_file_class[num * 10:(num + 1) * 10]})
        accu_list.append(num_acc)
        loss_list.append(num_loss)
      # train_acc, train_loss = sess.run([accuracy, loss], feed_dict={image: batch_xs,
      #                     land_mark: batch_ys, disease_class: batch_zs})
      print('training accuracy %f, loss: %f' % (np.mean(accu_list), np.mean(loss_list)))

      test_accu, test_loss, p_class, p_land_mark = sess.run([accuracy, loss_box, one_hot_prediction, predict_land_mark], feed_dict={image: test_file_image,
                          land_mark: test_file_label, disease_class: test_file_class})
      print('test accuracy %f, loss: %f' % (test_accu, test_loss))

    if itr == 999 or itr == 1999:
      print('p_class', p_class.shape)
      np.savez('predict-%d.npz' % itr, disease_class=p_class, land_mark=p_land_mark)
      pass
      saver.save(sess, "model/a", global_step=itr)


import docx
import uuid
import os, re

fileList = []

def scanDir(filePath):
    files = os.listdir(filePath)

    # 遍历所有文件
    for file in files:
        # 把文件路径和文件名结合起来
        file_d = os.path.join(filePath, file)
        # 判断该文件是单个文件还是文件夹
        if os.path.isdir(file_d):  # 如果是文件夹则递归调用 scanDir() 函数
            scanDir(file_d)
        else:
            if (file_d.endswith(".pdf") == False):
                fileList.append(file_d)

def copyImgFromWord(word_path, result_path):
    count = 0
    try:
        doc = docx.Document(word_path)
        dict_rel = doc.part._rels
        for rel in dict_rel:
            rel = dict_rel[rel]
            if "image" in rel.target_ref:
                if not os.path.exists(result_path):
                    os.makedirs(result_path)
                img_name = re.findall("/(.*)", rel.target_ref)[0]
                word_name = os.path.splitext(word_path)[0]
                if os.sep in word_name:
                    new_name = word_name.split('\\')[-1]
                else:
                    new_name = word_name.split('/')[-1]
                img_name = f'{new_name}-'+'.'+f'{img_name}'
                count += 1
                with open(f'{result_path}/{uuid.uuid1()}{img_name}', "wb") as f:
                    f.write(rel.target_part.blob)
    except:
        pass

if __name__ == "__main__":
    scanDir("E:\\img\\20220324")

    for word_path in fileList:
        copyImgFromWord(word_path, "E:\\img\\20220324Img")


def friendly_number(number, base=1000, decimals=0, suffix='',
                    powers=['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']):
    """
    Format a number as friendly text, using common suffixes.
    """
    from math import floor, ceil
    number = float(number)
    power, trail = 0, ''
    while abs(number) >= base and power < len(powers)-1:
        number = number / base
        power += 1
    if decimals:
        number = round(number, decimals)
    else:
        if number >= 0:
            number = floor(number)
        else:
            number = ceil(number)
    number = str(number)
    try:
        x = len(number[number.index('.')+1:])
        if x < decimals:
            trail = '0'*(decimals - x)
    except ValueError:
        if decimals == 0:
            trail = ''
        else:
            trail = '.' + '0' * decimals

    return str(number) + trail + powers[power] + suffix

print(friendly_number(10**32))

"""

    """


import os
import sys
from PyQt5.QtWidgets import *

import Directoryui

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        self.ui = Directoryui.Ui_MainWindow()
        self.ui.setupUi(self)
        self.counter = 0
        self.list_dirs = []
        self.setWindowTitle('Directory List')

        self.ui.pushButtonList.clicked.connect(self.pushButtonListHandler)
        self.ui.pushButtonNext.clicked.connect(self.pushButtonNextHandler)
        self.ui.pushButtonPrev.clicked.connect(self.pushButtonPrevHandler)
        self.ui.pushButtonFirst.clicked.connect(self.pushButtonFirstHandler)
        self.ui.pushButtonLast.clicked.connect(self.pushButtonLastHandler)
        self.ui.lineEditDir.setPlaceholderText('Enter full path')

    def pushButtonListHandler(self):
        path = self.ui.lineEditDir.text()
        try:
            dentries = os.scandir(path)
            dir_list = []
            for de in dentries:
                if de.is_dir():
                    dir_list.append(de.name)
            self.list_dirs = dir_list[::]
            dir_list.clear()
            self.ui.lineEditFileName.setText(self.list_dirs[0])
            self.counter = 0
            length_text = rf'{self.ui.lineEditDir.text()}\{self.ui.lineEditFileName.text()}'
            self.ui.lineEditLenght.setText(str(os.path.getsize(length_text)))
        except:
            print('Not avalieble directory')

    def pushButtonNextHandler(self):
        try:
            if len(self.list_dirs) - 1 > self.counter:
                self.counter += 1
            self.ui.lineEditFileName.setText(self.list_dirs[self.counter])
            length_text = rf'{self.ui.lineEditDir.text()}\{self.ui.lineEditFileName.text()}'
            self.ui.lineEditLenght.setText(str(os.path.getsize(length_text)))
        except:
            print('List except')

    def pushButtonPrevHandler(self):
        try:
            if self.counter > 0:
                self.counter -= 1
            self.ui.lineEditFileName.setText(self.list_dirs[self.counter])
            length_text = rf'{self.ui.lineEditDir.text()}\{self.ui.lineEditFileName.text()}'
            self.ui.lineEditLenght.setText(str(os.path.getsize(length_text)))
        except:
            print('List except')

    def pushButtonFirstHandler(self):
        try:
            self.ui.lineEditFileName.setText(self.list_dirs[0])
            length_text = rf'{self.ui.lineEditDir.text()}\{self.ui.lineEditFileName.text()}'
            self.ui.lineEditLenght.setText(str(os.path.getsize(length_text)))
        except:
            pass

    def pushButtonLastHandler(self):
        try:
            self.ui.lineEditFileName.setText(self.list_dirs[len(self.list_dirs) - 1])
            length_text = rf'{self.ui.lineEditDir.text()}\{self.ui.lineEditFileName.text()}'
            self.ui.lineEditLenght.setText(str(os.path.getsize(length_text)))
        except:
            pass

app = QApplication(sys.argv)
mainWindow = MainWindow()
mainWindow.show()
app.exec()


import imp
from app.helper.shippingdatapagination import ShippingdataPaginate
from app.models.sdct import SdctModel
from app.msmodel.shippingmodel import  Shippingmodel
from app.schemas.shipping import ShippingdataSchema
from app.service.shippingservice import ShippingService
from app.validator.shippingvalidater import Shippingvalidater
from flask import jsonify, request, session
from flask_restplus import Resource, fields, Namespace
import validator
import os
import bcrypt
import json
from app.db import db
import sys, os
from app.models.users import UsersModel
from app.helper.decorator import decorator
from webargs import fields, validate
from webargs.flaskparser import use_args, use_kwargs, parser, abort
from app.helper.errorhandling import Errorhandling

SHIPPINGS_DATA_NOT_FOUND = "No Shipping data found."
SHIPPING_DATA_NOT_FOUND = "Invoice Not found."

shipping_data_ns = Namespace('shipping-data', description='Shipping data related operations')
shipping_datas_ns = Namespace('shipping-datas', description='Shipping datas related operations')

shipping_schema = ShippingdataSchema()
shippings_list_schema = ShippingdataSchema(many=True)
shippings_pagination = ShippingdataPaginate()
shipping_validator = Shippingvalidater()
error_handling = Errorhandling()

shipping_service = ShippingService()

class ShippingsdataList(Resource):
    shippinglist_args = {
        "filter": fields.Str(required=False),
        "order_by": fields.Str(missing="SDCT_ID",required=False),
        "order_direction": fields.Str(missing="Asc", validate=validate.OneOf(["Asc", "Desc","asc","desc"])),
        "limit": fields.Int(missing=25, required=False),
        "ship_id": fields.Int(required=False),
        "page": fields.Int(missing=0, required=False),
        "active": fields.Str(
            missing="all", validate=validate.OneOf(["active", "inactive" , "all"])
        )
    }
    @use_kwargs(shippinglist_args, location="query")
    @shipping_datas_ns.doc('Get all shipping data')
    def get(self, **kwargs):
        filter_data = {}
        if "order_direction" in kwargs:
            order_direction = kwargs['order_direction']

        if "order_by" in kwargs:
            order_by = kwargs['order_by']
        if "ship_id" in kwargs:
            ship_id = kwargs['ship_id']
            filter_data["ship_id"] = ship_id
        if  kwargs['active'] != "all":
            active = kwargs['active']
            filter_data["active"] = active

        if "filter" in kwargs:
            filter_data["filter"] = kwargs['filter']

        shippings = SdctModel.find_all_by_filter(filter_data, order_by, order_direction)
        if len(shippings) == 0:
            return {'message':  SHIPPINGS_DATA_NOT_FOUND}, 404
        shipping_list = shippings_list_schema.dump(shippings)

        shippings = shippings_pagination.paginator(shipping_list, kwargs['page'], kwargs['limit'],  len(shipping_list))
        return shippings, 200

class Shippingdata(Resource):
    @shipping_data_ns.doc('Get Shipping Detail')
    def get(self, id):
        shipping = SdctModel.find_by_id(id)

        if shipping:
            shipping = shipping_schema.dump(shipping)
            return shipping
        return {'message':  SHIPPINGS_DATA_NOT_FOUND}, 404

    @shipping_data_ns.doc('Create a Shipping')
    def post(self):
        data = request.get_json(force=True)

        rule = shipping_validator.rule()

        result, req_obj, errors = validator.validate(data, rule, return_info=True)

        if result == False:
            error = error_handling.error(errors, data)
            return error, 400

        shipping_data = Shippingmodel(**data)
        add_shipping = shipping_service.create(shipping_data)

        if add_shipping:
            return shipping_schema.dump(add_shipping)

        return {'message':  'Unable to save Shipping data'}, 400


"""
科学与未来 爬虫
"""

import scrapy
import time
import logging
import re
from riskweb.items import ArticleItem
logger = logging.getLogger(__name__)

class MediaAftenpostenSpider(scrapy.Spider):
    name = 'media_sciencesetavenir'
    allowed_domains = ['sciencesetavenir.fr']

    def start_requests(self):
        BASEURL = "https://www.sciencesetavenir.fr"
        start_paths = [
            '/espace/',
            '/sante/',
            '/nutrition/',
            '/nature-environnement/',
            '/animaux/',
            '/high-tech/',
            '/archeo-paleo/'
        ]
        # 发送请求
        for start_path in start_paths:
            yield scrapy.Request(BASEURL+start_path, self.parse_dictionary,meta={'url':start_path}, dont_filter=True)

    def parse_dictionary(self, response):
        BASEURL = "https://www.sciencesetavenir.fr"
        #文章详情链接
        if not re.search( r'page', response.url):
            article_urls = response.xpath('//div[@class="alaune"]/div[@class="txt"]/a/@href | //div[@class="overlay-hover"]/a[1]/@href').extract()
            article_urls.extend(response.xpath('//div[@class="content-main"]//ul[@class="list-img list-img-sqr"]/li/a/@href').extract())
            for article_url in article_urls:
                yield scrapy.Request(article_url, self.parse_article,meta={'topic':response.meta['url'][1:-1]}, dont_filter=False)
            next_url = BASEURL + response.meta['url'] + 'page-' + '2'
            yield scrapy.Request(next_url,self.parse_dictionary,meta={'url':response.meta['url'],'page':3},dont_filter=True)
        else:
            if response.xpath('//div[@class="content-main"]//div[@class="item"]'):
                article_urls = response.xpath('//div[@class="item"]/a/@href').extract()
                for article_url in article_urls:
                    yield scrapy.Request(article_url, self.parse_article,meta={'topic':response.meta['url'][1:-1]}, dont_filter=False)
                next_url = BASEURL + response.meta['url'] + 'page-' + str(response.meta['page'])
                yield scrapy.Request(next_url,self.parse_dictionary,meta={'url':response.meta['url'],'page':response.meta['page']+1},dont_filter=True)

        #scrapy crawl media_sciencesetavenir
    def parse_article(self, response):
        #调取参数
        item = ArticleItem()
        #进入每篇文章获取标题
        item['title'] = response.xpath('//div[@class="article-start"]/h1/text()').extract_first().replace('\xa0','')
        item['media_name'] = '科学与未来'
        item['url'] = response.url
        item['topic'] ='|'.join(response.xpath('//p[contains(@class,"article-tags")]/a/text()').extract()).strip()
        item['author'] = '|'.join(response.xpath('//*[@class="signature"]/text()').extract()).strip()

        # 匹配文章的发布时间
        publish_date = response.xpath('//*[contains(@class,"article-infos")]//a/text()').extract()[-1]
        #10.08.2021 à 11h25

        item['publish_date'] = time.strftime("%Y/%m/%d %H:%M", time.strptime(publish_date, "%d.%m.%Y à %Hh%M")) #08.08.2021 à 16h00

        raw_sentences = response.xpath('//div[contains(@class,"corps")]//p | //div[contains(@class,"corps")]//h2').xpath('string(.)').extract()

        raw_sentences = list(filter(lambda sentence: sentence.strip(), raw_sentences))
        item['content'] = '\n'.join(raw_sentences).strip().replace('\xa0','')
        return item


import factory
import random

from django.core.management.base import BaseCommand
from django.conf import settings

import donate.core.factory as core_factory
from donate.utility.faker.helpers import reseed

from wagtail_factories import ImageFactory

class Command(BaseCommand):
    help = 'Generate fake data for local development and testing purposes' \
           'and load it into the database'

    def add_arguments(self, parser):
        parser.add_argument(
            '--seed',
            action='store',
            dest='seed',
            help='A seed value to pass to Faker before generating data',
        )

    def handle(self, *args, **options):

        faker = factory.faker.Faker._get_faker(locale='en-US')

        # Seed Faker with the provided seed value or a pseudorandom int between 0 and five million
        if options['seed']:
            seed = options['seed']
        elif settings.RANDOM_SEED is not None:
            seed = settings.RANDOM_SEED
        else:
            seed = random.randint(0, 5000000)

        self.stdout.write(f'Seeding random numbers with: {seed}')

        reseed(seed)

        self.stdout.write('Generating Images')
        for i in range(20):
            ImageFactory.create(
                file__width=1080,
                file__height=720,
                file__color=faker.safe_color_name()
            )

        factories = [
            core_factory,
        ]
        for app_factory in factories:
            app_factory.generate(seed)

        self.stdout.write(self.style.SUCCESS('Done!'))


from flask import request, jsonify
from sqlalchemy.exc import PendingRollbackError

from app.exception.InvalidDataError import InvalidDataError
from app.models.categories_models import CategoriesModel
from app.models.eisenhower_model import EisenhowerModel
from app.models.task_model import TaskModel
from app.configs.database import db

def create_task():
    data = request.get_json()
    importance = data["importance"]
    urgency = data["urgency"]
    EisenhowerModel.create_eisenhower()

    data_keys = data.keys()
    keys = TaskModel.keys
    wrong_keys = data_keys - keys

    if len(wrong_keys) > 0:
        return {"error": f"wrong key: {wrong_keys}"}, 400

    try:
        data["name"] = data["name"].title()
        type_eisenhower = TaskModel.eisenhower_type(importance, urgency)

        eisenhower = EisenhowerModel.query.filter_by(type=type_eisenhower).first()
        data["eisenhower_id"] = eisenhower.id

        add_categories = data.pop("categories")

        task = TaskModel(**data)

        for item in add_categories:
            try:
                category = CategoriesModel.query.filter_by(name=item.title()).one()
                task.categories.append(category)

            except:
                new_category = CategoriesModel(name=item.title())
                db.session.add(new_category)
                db.session.commit()

                category = CategoriesModel.query.filter_by(name=item.title()).first()
                task.categories.append(category)

        db.session.add(task)
        db.session.commit()

        return jsonify({
            "id": task.id,
            "name": task.name,
            "description": task.description,
            "duration": task.duration,
            "classification": eisenhower.type,
            "categories": [category.name for category in task.categories]
        }), 201

    except (InvalidDataError) as e:
        return {"msg": e.__dict__["description"]}, 400

    except PendingRollbackError as e:
        name = data["name"]
        return jsonify({"error": f"name: {name}; already registered"}), 409

    except KeyError as e:
        return jsonify({
            "msg":{
                "valid_options": {
                    "importance": [1, 2],
                    "urgency": [1, 2]
                },
                "recieved_options":{
                    "importance": importance,
                    "urgency": urgency
                }
            }
        }), 400

def update_task(id):
    data  = request.get_json()
    data_keys = data.keys()
    keys = TaskModel.keys
    wrong_keys = data_keys - keys

    if len(wrong_keys) > 0:
        return {"error": f"wrong key: {wrong_keys}"}, 400

    try:
        task = TaskModel.query.get(id)

        if task == None:
            return {"error": "task not found!"}, 404

        for key, value in data.items():
            setattr(task, key, value)

        type_eisenhower = TaskModel.eisenhower_type(task.importance, task.urgency)
        eisenhower = EisenhowerModel.query.filter_by(type=type_eisenhower).first()
        data["eisenhower_id"] = eisenhower.id

        db.session.add(task)
        db.session.commit()

        return jsonify({
            "id": task.id,
            "name": task.name,
            "description": task.description,
            "duration": task.duration,
            "classification": eisenhower.type,
            "categories": [category.name for category in task.categories]
        }), 200

    except TypeError as e:
        return jsonify({
            "msg":{
                "valid_options": {
                    "importance": [1, 2],
                    "urgency": [1, 2]
                },
                "recieved_options":{
                    "importance": data["importance"],
                    "urgency": data["urgency"]
                }
            }
        }), 400

    except InvalidDataError as e :
        return {"error": e.args[0]}, 400

def delete_task(id):
    task_to_delete = TaskModel.query.get(id)

    if task_to_delete == None:
            return {"error": "task not found!"}, 404

    db.session.delete(task_to_delete)
    db.session.commit()

    return "", 204


"""
    @author : Sakshi Tantak
"""

# Imports
import os
from tqdm import tqdm
import pandas as pd
from sklearn.model_selection import train_test_split

import spacy
import spacy_transformers
from spacy.tokens import DocBin

TRAIN = os.path.join('/home/sakshi/projects/twt-sentiment-analysis/data/processed_data', 'train.csv')
TEST = os.path.join('/home/sakshi/projects/twt-sentiment-analysis/data/processed_data', 'test.csv')

nlp = spacy.load('en_core_web_trf')

def convert2doc(data):
    texts = []
    for doc, label in tqdm(nlp.pipe(data, as_tuples = True), total = len(data)):
        if label == 0:
            if label == 0:
                doc.cats['positive'] = 0
                doc.cats['negative'] = 1
            else:
                doc.cats['positive'] = 1
                doc.cats['negative'] = 0
            texts.append(doc)
    return texts

def load_data_and_prepare_for_training(dirpath):
    df_train = pd.read_csv(TRAIN)
    df_test = pd.read_csv(TEST)

    df_train0 = df_train.loc[df_train['sentiment'] == 0]
    df_train4 = df_train.loc[df_train['sentiment'] == 4]

    df_train0 = df_train0.sample(frac = 1).iloc[:5000]
    df_train4 = df_train4.sample(frac = 1).iloc[:5000]

    df_train = pd.concat([df_train0, df_train4])

    df_train['sentiment'] = df_train['sentiment'].replace(4, 1)
    df_test = df_test.loc[df_test['sentiment'] != 2]
    df_test['sentiment'] = df_test['sentiment'].replace(4, 1)

    train, dev = train_test_split(df_train, train_size = 0.85, random_state = 42, shuffle = True)

    train['tuple'] = train.apply(lambda x : (x['cleaned_tweet'], x['sentiment']), axis = 1)
    train_tuples = train['tuple'].to_list()

    dev['tuple'] = dev.apply(lambda x : (x['cleaned_tweet'], x['sentiment']), axis = 1)
    dev_tuples = dev['tuple'].to_list()

    test = df_test
    test['tuple'] = df_test.apply(lambda x : (x['cleaned_tweet'], x['sentiment']), axis = 1)
    test_tuples = df_test['tuple'].to_list()

    train_doc_bin = DocBin(docs = convert2doc(train_tuples))
    dev_doc_bin = DocBin(docs = convert2doc(dev_tuples))
    test_doc_bin = DocBin(docs = convert2doc(test_tuples))

    train_doc_bin.to_disk(os.path.join(dirpath, 'train.spacy'))
    dev_doc_bin.to_disk(os.path.join(dirpath, 'dev.spacy'))
    test_doc_bin.to_disk(os.path.join(dirpath, 'test.spacy'))

if __name__ == '__main__':
    dirpath = '/home/sakshi/projects/twt-sentiment-analysis/data/processed_data/spacy'
    load_data_and_prepare_for_training(dirpath)

"""
To train spacy classifier

run the following commands after running this file
1. download base_config.cfg from spacy quickstarts for textcat pipeline
2. add the train, dev, test paths in base_config.cfg
3. to generate final config, fire
   python -m spacy init fill-config base_config.cfg config.cfg
4. to train, fire
    python -m spacy train config.cfg --verbose --output /home/sakshi/projects/twt-sentiment-analysis/models/spacy-classifier
"""


#!/usr/bin/env python3

import itertools

import networkx as nx
import numpy as np

from pgmpy.base import UndirectedGraph
from pgmpy.global_vars import logger
from pgmpy.independencies import Independencies

class DAG(nx.DiGraph):
    """
    Base class for all Directed Graphical Models.

    Each node in the graph can represent either a random variable, `Factor`,
    or a cluster of random variables. Edges in the graph represent the
    dependencies between these.

    Parameters
    ----------
    data: input graph
        Data to initialize graph. If data=None (default) an empty graph is
        created. The data can be an edge list or any Networkx graph object.

    Examples
    --------
    Create an empty DAG with no nodes and no edges

    >>> from pgmpy.base import DAG
    >>> G = DAG()

    G can be grown in several ways:

    **Nodes:**

    Add one node at a time:

    >>> G.add_node(node='a')

    Add the nodes from any container (a list, set or tuple or the nodes
    from another graph).

    >>> G.add_nodes_from(nodes=['a', 'b'])

    **Edges:**

    G can also be grown by adding edges.

    Add one edge,

    >>> G.add_edge(u='a', v='b')

    a list of edges,

    >>> G.add_edges_from(ebunch=[('a', 'b'), ('b', 'c')])

    If some edges connect nodes not yet in the model, the nodes
    are added automatically. There are no errors when adding
    nodes or edges that already exist.

    **Shortcuts:**

    Many common graph features allow python syntax for speed reporting.

    >>> 'a' in G     # check if node in graph
    True
    >>> len(G)  # number of nodes in graph
    3
    """

    def __init__(self, ebunch=None, latents=set()):
        super(DAG, self).__init__(ebunch)
        self.latents = set(latents)
        cycles = []
        try:
            cycles = list(nx.find_cycle(self))
        except nx.NetworkXNoCycle:
            pass
        else:
            out_str = "Cycles are not allowed in a DAG."
            out_str += "\nEdges indicating the path taken for a loop: "
            out_str += "".join([f"({u},{v}) " for (u, v) in cycles])
            raise ValueError(out_str)

    def add_node(self, node, weight=None, latent=False):
        """
        Adds a single node to the Graph.

        Parameters
        ----------
        node: str, int, or any hashable python object.
            The node to add to the graph.

        weight: int, float
            The weight of the node.

        latent: boolean (default: False)
            Specifies whether the variable is latent or not.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG()
        >>> G.add_node(node='A')
        >>> sorted(G.nodes())
        ['A']

        Adding a node with some weight.
        >>> G.add_node(node='B', weight=0.3)

        The weight of these nodes can be accessed as:
        >>> G.nodes['B']
        {'weight': 0.3}
        >>> G.nodes['A']
        {'weight': None}
        """

        # Check for networkx 2.0 syntax
        if isinstance(node, tuple) and len(node) == 2 and isinstance(node[1], dict):
            node, attrs = node
            if attrs.get("weight", None) is not None:
                attrs["weight"] = weight
        else:
            attrs = {"weight": weight}

        if latent:
            self.latents.add(node)

        super(DAG, self).add_node(node, weight=weight)

    def add_nodes_from(self, nodes, weights=None, latent=False):
        """
        Add multiple nodes to the Graph.

        **The behviour of adding weights is different than in networkx.

        Parameters
        ----------
        nodes: iterable container
            A container of nodes (list, dict, set, or any hashable python
            object).

        weights: list, tuple (default=None)
            A container of weights (int, float). The weight value at index i
            is associated with the variable at index i.

        latent: list, tuple (default=False)
            A container of boolean. The value at index i tells whether the
            node at index i is latent or not.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG()
        >>> G.add_nodes_from(nodes=['A', 'B', 'C'])
        >>> G.nodes()
        NodeView(('A', 'B', 'C'))

        Adding nodes with weights:
        >>> G.add_nodes_from(nodes=['D', 'E'], weights=[0.3, 0.6])
        >>> G.nodes['D']
        {'weight': 0.3}
        >>> G.nodes['E']
        {'weight': 0.6}
        >>> G.nodes['A']
        {'weight': None}
        """
        nodes = list(nodes)

        if isinstance(latent, bool):
            latent = [latent] * len(nodes)

        if weights:
            if len(nodes) != len(weights):
                raise ValueError(
                    "The number of elements in nodes and weights" "should be equal."
                )
            for index in range(len(nodes)):
                self.add_node(
                    node=nodes[index], weight=weights[index], latent=latent[index]
                )
        else:
            for index in range(len(nodes)):
                self.add_node(node=nodes[index], latent=latent[index])

    def add_edge(self, u, v, weight=None):
        """
        Add an edge between u and v.

        The nodes u and v will be automatically added if they are
        not already in the graph.

        Parameters
        ----------
        u, v : nodes
            Nodes can be any hashable Python object.

        weight: int, float (default=None)
            The weight of the edge

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG()
        >>> G.add_nodes_from(nodes=['Alice', 'Bob', 'Charles'])
        >>> G.add_edge(u='Alice', v='Bob')
        >>> G.nodes()
        NodeView(('Alice', 'Bob', 'Charles'))
        >>> G.edges()
        OutEdgeView([('Alice', 'Bob')])

        When the node is not already present in the graph:
        >>> G.add_edge(u='Alice', v='Ankur')
        >>> G.nodes()
        NodeView(('Alice', 'Ankur', 'Bob', 'Charles'))
        >>> G.edges()
        OutEdgeView([('Alice', 'Bob'), ('Alice', 'Ankur')])

        Adding edges with weight:
        >>> G.add_edge('Ankur', 'Maria', weight=0.1)
        >>> G.edge['Ankur']['Maria']
        {'weight': 0.1}
        """
        super(DAG, self).add_edge(u, v, weight=weight)

    def add_edges_from(self, ebunch, weights=None):
        """
        Add all the edges in ebunch.

        If nodes referred in the ebunch are not already present, they
        will be automatically added. Node names can be any hashable python
        object.

        **The behavior of adding weights is different than networkx.

        Parameters
        ----------
        ebunch : container of edges
            Each edge given in the container will be added to the graph.
            The edges must be given as 2-tuples (u, v).

        weights: list, tuple (default=None)
            A container of weights (int, float). The weight value at index i
            is associated with the edge at index i.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG()
        >>> G.add_nodes_from(nodes=['Alice', 'Bob', 'Charles'])
        >>> G.add_edges_from(ebunch=[('Alice', 'Bob'), ('Bob', 'Charles')])
        >>> G.nodes()
        NodeView(('Alice', 'Bob', 'Charles'))
        >>> G.edges()
        OutEdgeView([('Alice', 'Bob'), ('Bob', 'Charles')])

        When the node is not already in the model:
        >>> G.add_edges_from(ebunch=[('Alice', 'Ankur')])
        >>> G.nodes()
        NodeView(('Alice', 'Bob', 'Charles', 'Ankur'))
        >>> G.edges()
        OutEdgeView([('Alice', 'Bob'), ('Bob', 'Charles'), ('Alice', 'Ankur')])

        Adding edges with weights:
        >>> G.add_edges_from([('Ankur', 'Maria'), ('Maria', 'Mason')],
        ...                  weights=[0.3, 0.5])
        >>> G.edge['Ankur']['Maria']
        {'weight': 0.3}
        >>> G.edge['Maria']['Mason']
        {'weight': 0.5}
        """
        ebunch = list(ebunch)

        if weights:
            if len(ebunch) != len(weights):
                raise ValueError(
                    "The number of elements in ebunch and weights" "should be equal"
                )
            for index in range(len(ebunch)):
                self.add_edge(ebunch[index][0], ebunch[index][1], weight=weights[index])
        else:
            for edge in ebunch:
                self.add_edge(edge[0], edge[1])

    def get_parents(self, node):
        """
        Returns a list of parents of node.

        Throws an error if the node is not present in the graph.

        Parameters
        ----------
        node: string, int or any hashable python object.
            The node whose parents would be returned.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG(ebunch=[('diff', 'grade'), ('intel', 'grade')])
        >>> G.get_parents(node='grade')
        ['diff', 'intel']
        """
        return list(self.predecessors(node))

    def moralize(self):
        """
        Removes all the immoralities in the DAG and creates a moral
        graph (UndirectedGraph).

        A v-structure X->Z<-Y is an immorality if there is no directed edge
        between X and Y.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG(ebunch=[('diff', 'grade'), ('intel', 'grade')])
        >>> moral_graph = G.moralize()
        >>> moral_graph.edges()
        EdgeView([('intel', 'grade'), ('intel', 'diff'), ('grade', 'diff')])
        """
        moral_graph = UndirectedGraph()
        moral_graph.add_nodes_from(self.nodes())
        moral_graph.add_edges_from(self.to_undirected().edges())

        for node in self.nodes():
            moral_graph.add_edges_from(
                itertools.combinations(self.get_parents(node), 2)
            )

        return moral_graph

    def get_leaves(self):
        """
        Returns a list of leaves of the graph.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> graph = DAG([('A', 'B'), ('B', 'C'), ('B', 'D')])
        >>> graph.get_leaves()
        ['C', 'D']
        """
        return [node for node, out_degree in self.out_degree_iter() if out_degree == 0]

    def out_degree_iter(self, nbunch=None, weight=None):
        if nx.__version__.startswith("1"):
            return super(DAG, self).out_degree_iter(nbunch, weight)
        else:
            return iter(self.out_degree(nbunch, weight))

    def in_degree_iter(self, nbunch=None, weight=None):
        if nx.__version__.startswith("1"):
            return super(DAG, self).in_degree_iter(nbunch, weight)
        else:
            return iter(self.in_degree(nbunch, weight))

    def get_roots(self):
        """
        Returns a list of roots of the graph.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> graph = DAG([('A', 'B'), ('B', 'C'), ('B', 'D'), ('E', 'B')])
        >>> graph.get_roots()
        ['A', 'E']
        """
        return [
            node for node, in_degree in dict(self.in_degree()).items() if in_degree == 0
        ]

    def get_children(self, node):
        """
        Returns a list of children of node.
        Throws an error if the node is not present in the graph.

        Parameters
        ----------
        node: string, int or any hashable python object.
            The node whose children would be returned.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> g = DAG(ebunch=[('A', 'B'), ('C', 'B'), ('B', 'D'),
                                      ('B', 'E'), ('B', 'F'), ('E', 'G')])
        >>> g.get_children(node='B')
        ['D', 'E', 'F']
        """
        return list(self.successors(node))

    def get_independencies(self, latex=False, include_latents=False):
        """
        Computes independencies in the DAG, by checking d-seperation.

        Parameters
        ----------
        latex: boolean
            If latex=True then latex string of the independence assertion
            would be created.

        include_latents: boolean
            If True, includes latent variables in the independencies. Otherwise,
            only generates independencies on observed variables.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> chain = DAG([('X', 'Y'), ('Y', 'Z')])
        >>> chain.get_independencies()
        (X \u27C2 Z | Y)
        (Z \u27C2 X | Y)
        """
        nodes = set(self.nodes())
        if not include_latents:
            nodes = set(self.nodes()) - self.latents

        independencies = Independencies()
        for start in nodes:
            if not include_latents:
                rest = set(self.nodes()) - {start} - self.latents
            else:
                rest = set(self.nodes()) - {start}

            for r in range(len(rest)):
                for observed in itertools.combinations(rest, r):
                    d_seperated_variables = (
                        rest
                        - set(observed)
                        - set(
                            self.active_trail_nodes(
                                start,
                                observed=observed,
                                include_latents=include_latents,
                            )[start]
                        )
                    )
                    if d_seperated_variables:
                        independencies.add_assertions(
                            [start, d_seperated_variables, observed]
                        )
        independencies.reduce()

        if not latex:
            return independencies
        else:
            return independencies.latex_string()

    def local_independencies(self, variables):
        """
        Returns an instance of Independencies containing the local independencies
        of each of the variables.

        Parameters
        ----------
        variables: str or array like
            variables whose local independencies are to be found.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> student = DAG()
        >>> student.add_edges_from([('diff', 'grade'), ('intel', 'grade'),
        >>>                         ('grade', 'letter'), ('intel', 'SAT')])
        >>> ind = student.local_independencies('grade')
        >>> ind
        (grade \u27C2 SAT | diff, intel)
        """

        independencies = Independencies()
        for variable in (
            variables if isinstance(variables, (list, tuple)) else [variables]
        ):
            non_descendents = (
                set(self.nodes())
                - {variable}
                - set(nx.dfs_preorder_nodes(self, variable))
            )
            parents = set(self.get_parents(variable))
            if non_descendents - parents:
                independencies.add_assertions(
                    [variable, non_descendents - parents, parents]
                )
        return independencies

    def is_iequivalent(self, model):
        """
        Checks whether the given model is I-equivalent

        Two graphs G1 and G2 are said to be I-equivalent if they have same skeleton
        and have same set of immoralities.

        Parameters
        ----------
        model : A DAG object, for which you want to check I-equivalence

        Returns
        --------
        I-equivalence: boolean
            True if both are I-equivalent, False otherwise

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> G = DAG()
        >>> G.add_edges_from([('V', 'W'), ('W', 'X'),
        ...                   ('X', 'Y'), ('Z', 'Y')])
        >>> G1 = DAG()
        >>> G1.add_edges_from([('W', 'V'), ('X', 'W'),
        ...                    ('X', 'Y'), ('Z', 'Y')])
        >>> G.is_iequivalent(G1)
        True

        """
        if not isinstance(model, DAG):
            raise TypeError(
                f"Model must be an instance of DAG. Got type: {type(model)}"
            )

        if (self.to_undirected().edges() == model.to_undirected().edges()) and (
            self.get_immoralities() == model.get_immoralities()
        ):
            return True
        return False

    def get_immoralities(self):
        """
        Finds all the immoralities in the model
        A v-structure X -> Z <- Y is an immorality if there is no direct edge between X and Y .

        Returns
        -------
        Immoralities: set
            A set of all the immoralities in the model

        Examples
        ---------
        >>> from pgmpy.base import DAG
        >>> student = DAG()
        >>> student.add_edges_from([('diff', 'grade'), ('intel', 'grade'),
        ...                         ('intel', 'SAT'), ('grade', 'letter')])
        >>> student.get_immoralities()
        {('diff', 'intel')}
        """
        immoralities = set()
        for node in self.nodes():
            for parents in itertools.combinations(self.predecessors(node), 2):
                if not self.has_edge(parents[0], parents[1]) and not self.has_edge(
                    parents[1], parents[0]
                ):
                    immoralities.add(tuple(sorted(parents)))
        return immoralities

    def is_dconnected(self, start, end, observed=None):
        """
        Returns True if there is an active trail (i.e. d-connection) between
        `start` and `end` node given that `observed` is observed.

        Parameters
        ----------
        start, end : int, str, any hashable python object.
            The nodes in the DAG between which to check the d-connection/active trail.

        observed : list, array-like (optional)
            If given the active trail would be computed assuming these nodes to
            be observed.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> student = DAG()
        >>> student.add_nodes_from(['diff', 'intel', 'grades', 'letter', 'sat'])
        >>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades'), ('grades', 'letter'),
        ...                         ('intel', 'sat')])
        >>> student.is_dconnected('diff', 'intel')
        False
        >>> student.is_dconnected('grades', 'sat')
        True
        """
        if end in self.active_trail_nodes(start, observed)[start]:
            return True
        else:
            return False

    def minimal_dseparator(self, start, end):
        """
        Finds the minimal d-separating set for `start` and `end`.

        Parameters
        ----------
        start: node
            The first node.

        end: node
            The second node.

        Examples
        --------
        >>> dag = DAG([('A', 'B'), ('B', 'C')])
        >>> dag.minimal_dseparator(start='A', end='C')
        {'B'}

        References
        ----------
        [1] Algorithm 4, Page 10: Tian, Jin, Azaria Paz, and Judea Pearl. Finding minimal d-separators. Computer Science Department, University of California, 1998.
        """
        if (end in self.neighbors(start)) or (start in self.neighbors(end)):
            raise ValueError(
                "No possible separators because start and end are adjacent"
            )
        an_graph = self.get_ancestral_graph([start, end])
        separator = set(
            itertools.chain(self.predecessors(start), self.predecessors(end))
        )
        # If any of the parents were latents, take the latent's parent
        while len(separator.intersection(self.latents)) != 0:
            separator_copy = separator.copy()
            for u in separator:
                if u in self.latents:
                    separator_copy.remove(u)
                    separator_copy.update(set(self.predecessors(u)))
            separator = separator_copy
        # Remove the start and end nodes in case it reaches there while removing latents.
        separator.difference_update({start, end})

        # If the initial set is not able to d-separate, no d-separator is possible.
        if an_graph.is_dconnected(start, end, observed=separator):
            return None

        # Go through the separator set, remove one element and check if it remains
        # a dseparating set.
        minimal_separator = separator.copy()

        for u in separator:
            if not an_graph.is_dconnected(start, end, observed=minimal_separator - {u}):
                minimal_separator.remove(u)

        return minimal_separator

    def get_markov_blanket(self, node):
        """
        Returns a markov blanket for a random variable. In the case
        of Bayesian Networks, the markov blanket is the set of
        node's parents, its children and its children's other parents.

        Returns
        -------
        Markov Blanket: list
            List of nodes in the markov blanket of `node`.

        Parameters
        ----------
        node: string, int or any hashable python object.
              The node whose markov blanket would be returned.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> from pgmpy.factors.discrete import TabularCPD
        >>> G = DAG([('x', 'y'), ('z', 'y'), ('y', 'w'), ('y', 'v'), ('u', 'w'),
                               ('s', 'v'), ('w', 't'), ('w', 'm'), ('v', 'n'), ('v', 'q')])
        >>> G.get_markov_blanket('y')
        ['s', 'w', 'x', 'u', 'z', 'v']
        """
        children = self.get_children(node)
        parents = self.get_parents(node)
        blanket_nodes = children + parents
        for child_node in children:
            blanket_nodes.extend(self.get_parents(child_node))
        blanket_nodes = set(blanket_nodes)
        blanket_nodes.discard(node)
        return list(blanket_nodes)

    def active_trail_nodes(self, variables, observed=None, include_latents=False):
        """
        Returns a dictionary with the given variables as keys and all the nodes reachable
        from that respective variable as values.

        Parameters
        ----------
        variables: str or array like
            variables whose active trails are to be found.

        observed : List of nodes (optional)
            If given the active trails would be computed assuming these nodes to be
            observed.

        include_latents: boolean (default: False)
            Whether to include the latent variables in the returned active trail nodes.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> student = DAG()
        >>> student.add_nodes_from(['diff', 'intel', 'grades'])
        >>> student.add_edges_from([('diff', 'grades'), ('intel', 'grades')])
        >>> student.active_trail_nodes('diff')
        {'diff': {'diff', 'grades'}}
        >>> student.active_trail_nodes(['diff', 'intel'], observed='grades')
        {'diff': {'diff', 'intel'}, 'intel': {'diff', 'intel'}}

        References
        ----------
        Details of the algorithm can be found in 'Probabilistic Graphical Model
        Principles and Techniques' - Koller and Friedman
        Page 75 Algorithm 3.1
        """
        if observed:
            if isinstance(observed, set):
                observed = list(observed)

            observed_list = (
                observed if isinstance(observed, (list, tuple)) else [observed]
            )
        else:
            observed_list = []
        ancestors_list = self._get_ancestors_of(observed_list)

        # Direction of flow of information
        # up ->  from parent to child
        # down -> from child to parent

        active_trails = {}
        for start in variables if isinstance(variables, list) else [variables]:
            visit_list = set()
            visit_list.add((start, "up"))
            traversed_list = set()
            active_nodes = set()
            while visit_list:
                node, direction = visit_list.pop()
                if (node, direction) not in traversed_list:
                    if node not in observed_list:
                        active_nodes.add(node)
                    traversed_list.add((node, direction))
                    if direction == "up" and node not in observed_list:
                        for parent in self.predecessors(node):
                            visit_list.add((parent, "up"))
                        for child in self.successors(node):
                            visit_list.add((child, "down"))
                    elif direction == "down":
                        if node not in observed_list:
                            for child in self.successors(node):
                                visit_list.add((child, "down"))
                        if node in ancestors_list:
                            for parent in self.predecessors(node):
                                visit_list.add((parent, "up"))
            if include_latents:
                active_trails[start] = active_nodes
            else:
                active_trails[start] = active_nodes - self.latents

        return active_trails

    def _get_ancestors_of(self, nodes):
        """
        Returns a dictionary of all ancestors of all the observed nodes including the
        node itself.

        Parameters
        ----------
        nodes: string, list-type
            name of all the observed nodes

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> model = DAG([('D', 'G'), ('I', 'G'), ('G', 'L'),
        ...                        ('I', 'L')])
        >>> model._get_ancestors_of('G')
        {'D', 'G', 'I'}
        >>> model._get_ancestors_of(['G', 'I'])
        {'D', 'G', 'I'}
        """
        if not isinstance(nodes, (list, tuple)):
            nodes = [nodes]

        for node in nodes:
            if node not in self.nodes():
                raise ValueError(f"Node {node} not in not in graph")

        ancestors_list = set()
        nodes_list = set(nodes)
        while nodes_list:
            node = nodes_list.pop()
            if node not in ancestors_list:
                nodes_list.update(self.predecessors(node))
            ancestors_list.add(node)
        return ancestors_list

    # TODO: Commented out till the method is implemented.
    #     def to_pdag(self):
    #         """
    #         Returns the PDAG (the equivalence class of DAG; also known as CPDAG) of the DAG.
    #
    #         Returns
    #         -------
    #         Partially oriented DAG: pgmpy.base.PDAG
    #             An instance of pgmpy.base.PDAG.
    #
    #         Examples
    #         --------
    #
    #         """
    #         pass

    def do(self, nodes, inplace=False):
        """
        Applies the do operator to the graph and returns a new DAG with the
        transformed graph.

        The do-operator, do(X = x) has the effect of removing all edges from
        the parents of X and setting X to the given value x.

        Parameters
        ----------
        nodes : list, array-like
            The names of the nodes to apply the do-operator for.

        inplace: boolean (default: False)
            If inplace=True, makes the changes to the current object,
            otherwise returns a new instance.

        Returns
        -------
        Modified DAG: pgmpy.base.DAG
            A new instance of DAG modified by the do-operator

        Examples
        --------
        Initialize a DAG
        >>> graph = DAG()
        >>> graph.add_edges_from([('X', 'A'),
        ...                       ('A', 'Y'),
        ...                       ('A', 'B')])
        >>> # Applying the do-operator will return a new DAG with the desired structure.
        >>> graph_do_A = graph.do('A')
        >>> # Which we can verify is missing the edges we would expect.
        >>> graph_do_A.edges
        OutEdgeView([('A', 'B'), ('A', 'Y')])

        References
        ----------
        Causality: Models, Reasoning, and Inference, Judea Pearl (2000). p.70.
        """
        dag = self if inplace else self.copy()

        if isinstance(nodes, (str, int)):
            nodes = [nodes]
        else:
            nodes = list(nodes)

        if not set(nodes).issubset(set(self.nodes())):
            raise ValueError(
                f"Nodes not found in the model: {set(nodes) - set(self.nodes)}"
            )

        for node in nodes:
            parents = list(dag.predecessors(node))
            for parent in parents:
                dag.remove_edge(parent, node)
        return dag

    def get_ancestral_graph(self, nodes):
        """
        Returns the ancestral graph of the given `nodes`. The ancestral graph only
        contains the nodes which are ancestors of atleast one of the variables in
        node.

        Parameters
        ----------
        node: iterable
            List of nodes whose ancestral graph needs to be computed.

        Returns
        -------
        Ancestral Graph: pgmpy.base.DAG

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> dag = DAG([('A', 'C'), ('B', 'C'), ('D', 'A'), ('D', 'B')])
        >>> anc_dag = dag.get_ancestral_graph(nodes=['A', 'B'])
        >>> anc_dag.edges()
        OutEdgeView([('D', 'A'), ('D', 'B')])
        """
        return self.subgraph(nodes=self._get_ancestors_of(nodes=nodes))

    def to_daft(
        self,
        node_pos="circular",
        latex=True,
        pgm_params={},
        edge_params={},
        node_params={},
    ):
        """
        Returns a daft (https://docs.daft-pgm.org/en/latest/) object which can be rendered for
        publication quality plots. The returned object's render method can be called to see the plots.

        Parameters
        ----------
        node_pos: str or dict (default: circular)
            If str: Must be one of the following: circular, kamada_kawai, planar, random, shell, sprint,
                spectral, spiral. Please refer: https://networkx.org/documentation/stable//reference/drawing.html#module-networkx.drawing.layout for details on these layouts.

            If dict should be of the form {node: (x coordinate, y coordinate)} describing the x and y coordinate of each
            node.

            If no argument is provided uses circular layout.

        latex: boolean
            Whether to use latex for rendering the node names.

        pgm_params: dict (optional)
            Any additional parameters that need to be passed to `daft.PGM` initializer.
            Should be of the form: {param_name: param_value}

        edge_params: dict (optional)
            Any additional edge parameters that need to be passed to `daft.add_edge` method.
            Should be of the form: {(u1, v1): {param_name: param_value}, (u2, v2): {...} }

        node_params: dict (optional)
            Any additional node parameters that need to be passed to `daft.add_node` method.
            Should be of the form: {node1: {param_name: param_value}, node2: {...} }

        Returns
        -------
        Daft object: daft.PGM object
            Daft object for plotting the DAG.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> dag = DAG([('a', 'b'), ('b', 'c'), ('d', 'c')])
        >>> dag.to_daft(node_pos={'a': (0, 0), 'b': (1, 0), 'c': (2, 0), 'd': (1, 1)})
        <daft.PGM at 0x7fc756e936d0>
        >>> dag.to_daft(node_pos="circular")
        <daft.PGM at 0x7f9bb48c5eb0>
        >>> dag.to_daft(node_pos="circular", pgm_params={'observed_style': 'inner'})
        <daft.PGM at 0x7f9bb48b0bb0>
        >>> dag.to_daft(node_pos="circular",
        ...             edge_params={('a', 'b'): {'label': 2}},
        ...             node_params={'a': {'shape': 'rectangle'}})
        <daft.PGM at 0x7f9bb48b0bb0>
        """
        try:
            from daft import PGM
        except ImportError as e:
            raise ImportError(
                "Package daft required. Please visit: https://docs.daft-pgm.org/en/latest/ for installation instructions."
            )

        if isinstance(node_pos, str):
            supported_layouts = {
                "circular": nx.circular_layout,
                "kamada_kawai": nx.kamada_kawai_layout,
                "planar": nx.planar_layout,
                "random": nx.random_layout,
                "shell": nx.shell_layout,
                "spring": nx.spring_layout,
                "spectral": nx.spectral_layout,
                "spiral": nx.spiral_layout,
            }
            if node_pos not in supported_layouts.keys():
                raise ValueError(
                    "Unknown node_pos argument. Please refer docstring for accepted values"
                )
            else:
                node_pos = supported_layouts[node_pos](self)
        elif isinstance(node_pos, dict):
            for node in self.nodes():
                if node not in node_pos.keys():
                    raise ValueError(f"No position specified for {node}.")
        else:
            raise ValueError(
                "Argument node_pos not valid. Please refer to the docstring."
            )

        daft_pgm = PGM(**pgm_params)
        for node in self.nodes():
            try:
                extra_params = node_params[node]
            except KeyError:
                extra_params = dict()

            if latex:
                daft_pgm.add_node(
                    node,
                    rf"${node}$",
                    node_pos[node][0],
                    node_pos[node][1],
                    observed=True,
                    **extra_params,
                )
            else:
                daft_pgm.add_node(
                    node,
                    f"{node}",
                    node_pos[node][0],
                    node_pos[node][1],
                    observed=True,
                    **extra_params,
                )

        for u, v in self.edges():
            try:
                extra_params = edge_params[(u, v)]
            except KeyError:
                extra_params = dict()
            daft_pgm.add_edge(u, v, **extra_params)

        return daft_pgm

    @staticmethod
    def get_random(n_nodes=5, edge_prob=0.5, latents=False):
        """
        Returns a randomly generated DAG with `n_nodes` number of nodes with
        edge probability being `edge_prob`.

        Parameters
        ----------
        n_nodes: int
            The number of nodes in the randomly generated DAG.

        edge_prob: float
            The probability of edge between any two nodes in the topologically
            sorted DAG.

        latents: bool (default: False)
            If True, includes latent variables in the generated DAG.

        Returns
        -------
        Random DAG: pgmpy.base.DAG
            The randomly generated DAG.

        Examples
        --------
        >>> from pgmpy.base import DAG
        >>> random_dag = DAG.get_random(n_nodes=10, edge_prob=0.3)
        >>> random_dag.nodes()
        NodeView((0, 1, 2, 3, 4, 5, 6, 7, 8, 9))
        >>> random_dag.edges()
        OutEdgeView([(0, 6), (1, 6), (1, 7), (7, 9), (2, 5), (2, 7), (2, 8), (5, 9), (3, 7)])
        """
        # Step 1: Generate a matrix of 0 and 1. Prob of choosing 1 = edge_prob
        adj_mat = np.random.choice(
            [0, 1], size=(n_nodes, n_nodes), p=[1 - edge_prob, edge_prob]
        )

        # Step 2: Use the upper triangular part of the matrix as adjacency.
        nodes = list(range(n_nodes))
        edges = nx.convert_matrix.from_numpy_array(
            np.triu(adj_mat, k=1), create_using=nx.DiGraph
        ).edges()

        dag = DAG(edges)
        dag.add_nodes_from(nodes)
        if latents:
            dag.latents = set(
                np.random.choice(
                    dag.nodes(), np.random.randint(low=0, high=len(dag.nodes()))
                )
            )
        return dag

    def to_graphviz(self):
        """
        Retuns a pygraphviz object for the DAG. pygraphviz is useful for
        visualizing the network structure.

        Examples
        --------
        >>> from pgmpy.utils import get_example_model
        >>> model = get_example_model('alarm')
        >>> model.to_graphviz()
        <AGraph <Swig Object of type 'Agraph_t *' at 0x7fdea4cde040>>
        """
        return nx.nx_agraph.to_agraph(self)

class PDAG(nx.DiGraph):
    """
    Class for representing PDAGs (also known as CPDAG). PDAGs are the equivalence classes of
    DAGs and contain both directed and undirected edges.

    Note: In this class, undirected edges are represented using two edges in both direction i.e.
    an undirected edge between X - Y is represented using X -> Y and X <- Y.
    """

    def __init__(self, directed_ebunch=[], undirected_ebunch=[], latents=[]):
        """
        Initializes a PDAG class.

        Parameters
        ----------
        directed_ebunch: list, array-like of 2-tuples
            List of directed edges in the PDAG.

        undirected_ebunch: list, array-like of 2-tuples
            List of undirected edges in the PDAG.

        latents: list, array-like
            List of nodes which are latent variables.

        Returns
        -------
        An instance of the PDAG object.

        Examples
        --------
        """
        super(PDAG, self).__init__(
            directed_ebunch
            + undirected_ebunch
            + [(Y, X) for (X, Y) in undirected_ebunch]
        )
        self.latents = set(latents)
        self.directed_edges = set(directed_ebunch)
        self.undirected_edges = set(undirected_ebunch)
        # TODO: Fix the cycle issue
        # import pdb; pdb.set_trace()
        # try:
        #     # Filter out undirected edges as they also form a cycle in
        #     # themself when represented using directed edges.
        #     cycles = filter(lambda t: len(t) > 2, nx.simple_cycles(self))
        #     if cycles:
        #         out_str = "Cycles are not allowed in a PDAG. "
        #         out_str += "The following path forms a loop: "
        #         out_str += "".join(["({u},{v}) ".format(u=u, v=v) for (u, v) in cycles])
        #         raise ValueError(out_str)
        # except nx.NetworkXNoCycle:
        #     pass

    def copy(self):
        """
        Returns a copy of the object instance.

        Returns
        -------
        Copy of PDAG: pgmpy.dag.PDAG
            Returns a copy of self.
        """
        return PDAG(
            directed_ebunch=list(self.directed_edges.copy()),
            undirected_ebunch=list(self.undirected_edges.copy()),
            latents=self.latents,
        )

    def to_dag(self, required_edges=[]):
        """
        Returns one possible DAG which is represented using the PDAG.

        Parameters
        ----------
        required_edges: list, array-like of 2-tuples
            The list of edges that should be included in the DAG.

        Returns
        -------
        Returns an instance of DAG.

        Examples
        --------

        """
        # Add required edges if it doesn't form a new v-structure or an opposite edge
        # is already present in the network.
        dag = DAG()
        # Add all the nodes and the directed edges
        dag.add_nodes_from(self.nodes())
        dag.add_edges_from(self.directed_edges)
        dag.latents = self.latents

        pdag = self.copy()
        while pdag.number_of_nodes() > 0:
            # find node with (1) no directed outgoing edges and
            #                (2) the set of undirected neighbors is either empty or
            #                    undirected neighbors + parents of X are a clique
            found = False
            for X in pdag.nodes():
                directed_outgoing_edges = set(pdag.successors(X)) - set(
                    pdag.predecessors(X)
                )
                undirected_neighbors = set(pdag.successors(X)) & set(
                    pdag.predecessors(X)
                )
                neighbors_are_clique = all(
                    (
                        pdag.has_edge(Y, Z)
                        for Z in pdag.predecessors(X)
                        for Y in undirected_neighbors
                        if not Y == Z
                    )
                )

                if not directed_outgoing_edges and (
                    not undirected_neighbors or neighbors_are_clique
                ):
                    found = True
                    # add all edges of X as outgoing edges to dag
                    for Y in pdag.predecessors(X):
                        dag.add_edge(Y, X)
                    pdag.remove_node(X)
                    break

            if not found:
                logger.warning(
                    "PDAG has no faithful extension (= no oriented DAG with the "
                    + "same v-structures as PDAG). Remaining undirected PDAG edges "
                    + "oriented arbitrarily."
                )
                for X, Y in pdag.edges():
                    if not dag.has_edge(Y, X):
                        try:
                            dag.add_edge(X, Y)
                        except ValueError:
                            pass
                break
        return dag

    def to_graphviz(self):
        """
        Retuns a pygraphviz object for the DAG. pygraphviz is useful for
        visualizing the network structure.

        Examples
        --------
        >>> from pgmpy.utils import get_example_model
        >>> model = get_example_model('alarm')
        >>> model.to_graphviz()
        <AGraph <Swig Object of type 'Agraph_t *' at 0x7fdea4cde040>>
        """
        return nx.nx_agraph.to_agraph(self)


from collections import deque
from enum import Enum, auto

from random import choice, shuffle

# noinspection PyArgumentList
class OP(Enum):
    JUMP = auto()
    DISPLAY = auto()
    PUSH = auto()
    POP = auto()
    LABEL = auto()
    JUMP_IF_TRUE = auto()
    JUMP_IF_FALSE = auto()
    PUSH_RANDOM = auto()
    SHUFFLE = auto()
    BRANCH_IF_TRUE = auto()
    EXIT = auto()
    PUSH_RANDOM_BOOL = auto()
    PRINT_JOIN = auto()
    BRANCH_IF_FALSE = auto()
    SHOW_STACK = auto()
    STORE = auto()
    LOAD = auto()
    DUPLICATE = auto()
    PRINT = auto()
    CLEAR = auto()

def safe_get(items, index, default):
    return items[index] if 0 <= index < len(items) else default

def run(codes):
    stack = deque()
    labels = {}
    variables = {}

    def pop():
        return stack.pop()

    def push(value):
        stack.append(value)

    for i, (opcode, *args) in enumerate(codes):
        if opcode is OP.LABEL:
            labels[args[0]] = i

    codes_length = len(codes)
    ptr = 0
    while 0 <= ptr < codes_length:
        code, *args = codes[ptr]
        if code is OP.JUMP:
            ptr = labels[args[0]]
            continue
        elif code is OP.PUSH:
            push(args[0])
        elif code is OP.POP:
            pop()
        elif code is OP.DISPLAY:
            print(pop(), end='\n' if safe_get(args, 0, True) else '')
        elif code is OP.JUMP_IF_TRUE:
            if pop():
                ptr = labels[args[0]]
        elif code is OP.JUMP_IF_FALSE:
            if not pop():
                ptr = labels[args[0]]
        elif code is OP.PUSH_RANDOM:
            push(choice(args))
        elif code is OP.SHUFFLE:
            shuffle(stack)
        elif code is OP.BRANCH_IF_TRUE:
            ptr = labels[args[0] if pop() else args[1]]
        elif code is OP.BRANCH_IF_FALSE:
            ptr = labels[args[0] if not pop() else args[1]]
        elif code is OP.EXIT:
            quit(0)
        elif code is OP.PUSH_RANDOM_BOOL:
            push(choice((True, False)))
        elif code is OP.PRINT_JOIN:
            print(' '.join(args))
        elif code is OP.SHOW_STACK:
            print(list(stack))
        elif code is OP.STORE:
            variables[args[0]] = pop()
        elif code is OP.LOAD:
            push(variables[args[0]])
        elif code is OP.DUPLICATE:
            value = pop()
            push(value)
            push(value)
        elif code is OP.PRINT:
            print(args[0], end='\n' if safe_get(args, 1, True) else '')
        elif code is OP.CLEAR:
            del variables[args[0]]
        ptr += 1