import logging
import numpy as np
import pyrosetta

from . import chemical
from . import geometry
from . import table
from . import util

from rif.geom import Ray
from rif.geom.ray_hash import RayToRay4dHash
from rif.hash import XformHash_bt24_BCC6_X3f

from pyrosetta.rosetta.core.conformation import ResidueFactory

logging.basicConfig(level=logging.WARN)
_logging_handler = "interactive"

# WARN(onalant): Constants recommended by Will Sheffler; do not change unless /absolutely/ sure
LEVER = 10
BOUND = 1000
MODE = "fa_standard"

def _init_pyrosetta():
    """Load PyRosetta with the necessary parameter files"""

    from os import path

    _dir = path.join(path.dirname(__file__), "data", "functional_groups")

    param_files = [path.join(_dir, x.resName + ".params") for x in chemical.functional_groups.values()]
    opts = [
        "-corrections:beta_nov16",
        "-ignore_waters false",
        "-mute core",
        "-extra_res_fa %s" % (" ".join(param_files)),
        # "-constant_seed",
        "-output_virtual"
    ]

    pyrosetta.init(extra_options=" ".join(opts), set_logging_handler=_logging_handler)

class PrivilegedResidues:

    def __init__(self, path = "/home/onalant/dump/2018-05-07_datatables/database.h5"):
        """
        Parameters
        ----------
        path : str, optional
            Path to an HDF5 database. Defaults to a pre-generated
            database.
        """

        self._data = table.GenericTable(path)

        cart_resl = self._data._table.attrs["cart_resl"]
        ori_resl = self._data._table.attrs["ori_resl"]
        cart_bound = self._data._table.attrs["cart_bound"]

        self._lattice = XformHash_bt24_BCC6_X3f(cart_resl, ori_resl, cart_bound)
        self._raygrid = RayToRay4dHash(ori_resl, LEVER, bound=BOUND)

    # bidentate: "sc_sc", "sc_scbb", "sc_bb"
    # network: "acceptor_acceptor", "acceptor_donor", "donor_acceptor", "donor_donor"
    def match(self, ray1, ray2, group):
        """Construct all of the matched structures for a given ray pair
        and group.

        Notes
        -----
        The following are the available search groups.

        Bidentates:
            - "sc_sc"
            - "sc_scbb"
            - "sc_bb"
        Networks:
            - "acceptor_acceptor"
            - "acceptor_donor"
            - "donor_acceptor"
            - "donor_donor"

        Parameters
        ----------
        ray1 : np.ndarray
        ray2 : np.ndarray
            Rays used to search in the underlying database.
        group : str
            Dataset to search in.

        Yields
        ------
        pyrosetta.Pose
            Functional group as placed by transform from table.
        """

        dummy_pose = pyrosetta.pose_from_sequence("A", "fa_standard")
        res_type_set = dummy_pose.conformation().residue_type_set_for_conf()

        hashed_rays = np.asscalar(self._raygrid.get_keys(*(util.numpy_to_rif(r) for r in [ray1, ray2])).squeeze())

        results = self._data[hashed_rays, group]
        try:
            ray_frame = geometry.rays_to_transform(ray1, ray2)
        except:
            return []

        for pos_info in results:
            try:
                stored_frame = self._lattice.get_center([pos_info["transform"]])["raw"].squeeze()
            except:
                continue

            resname = pos_info["residue"].decode("utf-8")
            pos_grp = chemical.functional_groups[resname]

            dummy_pose.replace_residue(1, ResidueFactory.create_residue(res_type_set.name_map(resname)), False)

            coords = [np.array([*dummy_pose.residues[1].xyz(atom)]) for atom in pos_grp.atoms]
            c = np.stack(coords)

            try:
                pos_frame = geometry.coords_to_transform(c)
            except:
                continue
            final = np.dot(np.dot(ray_frame, stored_frame), np.linalg.inv(pos_frame))
            dummy_pose.apply_transform(final)
            yield (hashed_rays, dummy_pose.clone())

    # NOTE(onalant): bring your own residue selector
    def search(self, pose, groups, selector):
        """Search for privileged interactions in a pose.

        Parameters
        ----------
        pose : pyrosetta.Pose
            Target structure.
        groups : list of str
            Datasets or groups to search for matches in.
        selector : pyrosetta.rosetta.core.select.residue_selector.ResidueSelector
            Residue selector to apply to the pose.

        Yields
        ------
        tuple of np.uint64 and pyrosetta.Pose
            Target ray pair hash and output pose.
        """

        pairs_of_rays = { }

        if np.any([x in groups for x in ["sc_sc", "bidentate"]]):
            pairs_of_rays["sc_sc"] = chemical.sc_sc_rays(pose, selector)
        if np.any([x in groups for x in ["sc_scbb", "bidentate"]]):
            pairs_of_rays["sc_scbb"] = chemical.sc_scbb_rays(pose, selector)
        if np.any([x in groups for x in ["sc_bb", "bidentate"]]):
            pairs_of_rays["sc_bb"] = chemical.sc_bb_rays(pose, selector)

        if np.any([x in groups for x in ["acceptor_acceptor", "network"]]):
            pairs_of_rays["acceptor_acceptor"] = chemical.acceptor_acceptor_rays(pose, selector)
        if np.any([x in groups for x in ["acceptor_donor", "network"]]):
            pairs_of_rays["acceptor_donor"] = chemical.donor_acceptor_rays(pose, selector)
        if np.any([x in groups for x in ["donor_acceptor", "network"]]):
            pairs_of_rays["donor_acceptor"] = chemical.donor_acceptor_rays(pose, selector)
        if np.any([x in groups for x in ["donor_donor", "network"]]):
            pairs_of_rays["donor_donor"] = chemical.donor_donor_rays(pose, selector)

        for group in pairs_of_rays:
            for (r1, r2) in pairs_of_rays[group]:
                yield from self.match(r1, r2, group)

_init_pyrosetta()


import os

import clip
import numpy as np
from PIL import Image

from .. import CLIPZeroShotClassifier

cur_dir = os.path.dirname(os.path.abspath(__file__))

def test_clipzeroshotclassifier():
    dog_image = os.path.join(cur_dir, 'imgs/dog.jpg')
    labels = ['cat','dog','human']
    executor = CLIPZeroShotClassifier(labels)
    _, preprocess = clip.load('ViT-B/32')
    im = Image.open(dog_image)
    im_tensor_clip_input = preprocess(im).unsqueeze(0)
    im_tensor_clip_np = im_tensor_clip_input.detach().numpy()
    batch = np.vstack([im_tensor_clip_input, im_tensor_clip_input])
    output = executor.predict(batch)
    assert output.shape == (len(batch), len(labels))
    for res in output:
        assert len(res) == len(labels)
        assert res[labels.index('dog')] == 1


import sys
sys.path.append('../rxnft_vae')
import rdkit
import rdkit.Chem as Chem
from rdkit.Chem import QED, Descriptors, rdmolops

import torch
import torch.nn as nn
import torch.optim as optim
import torch.optim.lr_scheduler as lr_scheduler
from torch.utils.data import DataLoader
from torch.autograd import Variable

import math, random, sys
from optparse import OptionParser
from collections import deque

from reaction_utils import get_mol_from_smiles, get_smiles_from_mol,read_multistep_rxns, get_template_order, get_qed_score,get_clogp_score
from reaction import ReactionTree, extract_starting_reactants, StartingReactants, Templates, extract_templates,stats
from fragment import FragmentVocab, FragmentTree, FragmentNode, can_be_decomposed
from vae import FTRXNVAE, set_batch_nodeID
from mpn import MPN,PP,Discriminator
from evaluate import Evaluator
import random
import numpy as np
import networkx as nx

from sparse_gp import SparseGP
import scipy.stats as sps
import sascorer

def decode_many_times(model, latent):
	prob_decode = True
	latent_size = model.latent_size
	ft_mean = latent[:, :latent_size]
	rxn_mean = latent[:, latent_size:]
	product_list=[]
	for i in range(50):
		generated_tree = model.fragment_decoder.decode(ft_mean, prob_decode)
		g_encoder_output, g_root_vec = model.fragment_encoder([generated_tree])
		product, reactions = model.rxn_decoder.decode(rxn_mean, g_encoder_output, prob_decode)
		if product != None:
			product_list.append([product, reactions])
	if len(product_list) == 0:
		return None
	else:
		return product_list

def run_bo(X_train, y_train, X_test, y_test, model, parameters, metric, randseed):
	random_seed = int(randseed)
	np.random.seed(random_seed)
	if metric =="logp":
		logp_m = parameters[0]
		logp_s = parameters[1]
		sascore_m = parameters[2]
		sascore_s = parameters[3]
		cycle_m = parameters[4]
		cycle_s = parameters[5]

	filename = "../Results/" + metric + str(random_seed) + ".txt"

	#print("maxmimum score :", np.min(y_train), X_train.shape)
	#print(y_train)
	with open(filename, "w") as writer:
		iteration = 0
		latents = []
		min_scores = []
		while iteration < 5:
			# fit the GP
			#print("maxmimum score :", np.min(y_train), X_train.shape)
			print(iteration)
			np.random.seed(iteration * random_seed)
			M = 500
			sgp = SparseGP(X_train, 0 * X_train, y_train, M)
			sgp.train_via_ADAM(X_train, 0 * X_train, y_train, X_test, X_test * 0, y_test, minibatch_size = 10 * M, max_iterations = 100, learning_rate = 0.001)

			pred, uncert = sgp.predict(X_test, 0 * X_test)
			error = np.sqrt(np.mean((pred - y_test)**2))
			testll = np.mean(sps.norm.logpdf(pred - y_test, scale = np.sqrt(uncert)))
			print('Test RMSE: ', error, ' Test ll: ', testll)

			pred, uncert = sgp.predict(X_train, 0 * X_train)
			error = np.sqrt(np.mean((pred - y_train)**2))
			trainll = np.mean(sps.norm.logpdf(pred - y_train, scale = np.sqrt(uncert)))
			print( 'Train RMSE: ', error, 'Train ll: ', trainll)
			#print( 'Train ll: ', trainll)

			next_inputs, values = sgp.batched_greedy_ei(60, np.min(X_train, 0), np.max(X_train, 0))
			valid_smiles =[]
			new_features =[]
			full_rxn_strs=[]
			values = values.flatten()
			for i in range(60):
				#print(i)
				latent = next_inputs[i].reshape((1,-1))
				#res = model.decode_many_times(torch.from_numpy(latent).float(), 50)
				res= decode_many_times(model, torch.from_numpy(latent).float())
				if res is not None:
					smiles_list = [re[0] for re in res]
					n_reactions = [len(re[1].split(" ")) for re in res]
					#print(n_reactions)
					for re in res:
						smiles = re[0]
						if len(re[1].split(" ")) > 0 and smiles not in valid_smiles:
							#print(smiles, re[1].split(" "))
							valid_smiles.append(smiles)
							new_features.append(latent)
							full_rxn_strs.append(re[1])
				#print(i, res)

			#new_features = np.vstack(new_features)
			scores =[]
			b_valid_smiles=[]
			b_full_rxn_strs=[]
			b_scores=[]
			b_new_features=[]
			for i in range(len(valid_smiles)):
				if metric =="logp":
					mol = rdkit.Chem.MolFromSmiles(valid_smiles[i])
					if mol is None:
						continue
					current_log_P_value = Descriptors.MolLogP(mol)
					current_SA_score = -sascorer.calculateScore(mol)
					cycle_list = nx.cycle_basis(nx.Graph(rdmolops.GetAdjacencyMatrix(mol)))
					if len(cycle_list) == 0:
						cycle_length = 0
					else:
						cycle_length = max([ len(j) for j in cycle_list ])
					if cycle_length <= 6:
						cycle_length = 0
					else:
						cycle_length = cycle_length - 6
					current_cycle_score = -cycle_length
					current_SA_score_normalized = (current_SA_score - sascore_m) / sascore_s
					current_log_P_value_normalized = (current_log_P_value - logp_m) / logp_s
					current_cycle_score_normalized = (current_cycle_score - cycle_m) / cycle_s
					score = current_SA_score_normalized + current_log_P_value_normalized + current_cycle_score_normalized
					scores.append(-score)
					b_valid_smiles.append(valid_smiles[i])
					b_full_rxn_strs.append(full_rxn_strs[i])
					b_new_features.append(new_features[i])
				if metric=="qed":
					mol = rdkit.Chem.MolFromSmiles(valid_smiles[i])
					if mol!=None:
						score = QED.qed(mol)
						scores.append(-score)
						b_valid_smiles.append(valid_smiles[i])
						b_full_rxn_strs.append(full_rxn_strs[i])
						b_new_features.append(new_features[i])
			new_features = np.vstack(b_new_features)
			if len(new_features) > 0:
				X_train = np.concatenate([ X_train, new_features ], 0)
				y_train = np.concatenate([ y_train, np.array(scores)[ :, None ] ], 0)
			iteration+=1

			for i in range(len(b_valid_smiles)):
				line = " ".join([b_valid_smiles[i], b_full_rxn_strs[i], str(scores[i])])
				writer.write(line + "\n")
			#print(iteration, min(scores))

parser = OptionParser()
parser.add_option("-w", "--hidden", dest="hidden_size", default=200)
parser.add_option("-l", "--latent", dest="latent_size", default=50)
parser.add_option("-d", "--depth", dest="depth", default=2)
parser.add_option("-s", "--save_dir", dest="save_path")
parser.add_option("-t", "--data_path", dest="data_path")
parser.add_option("-v", "--vocab_path", dest="vocab_path")
parser.add_option("-m", "--metric", dest="metric")
parser.add_option("-r", "--seed", dest="seed", default=1)
opts, _ = parser.parse_args()

# get parameters
hidden_size = int(opts.hidden_size)
latent_size = int(opts.latent_size)
depth = int(opts.depth)
vocab_path = opts.vocab_path
data_filename = opts.data_path
w_save_path = opts.save_path
metric = opts.metric
seed = int(opts.seed)

# load model
if torch.cuda.is_available():
	#device = torch.device("cuda:1")
	device = torch.device("cuda")
	torch.cuda.set_device(1)
else:
	device = torch.device("cpu")

print("hidden size:", hidden_size, "latent_size:", latent_size, "depth:", depth)
print("loading data.....")
data_filename = opts.data_path
routes, scores = read_multistep_rxns(data_filename)
rxn_trees = [ReactionTree(route) for route in routes]
molecules = [rxn_tree.molecule_nodes[0].smiles for rxn_tree in rxn_trees]
reactants = extract_starting_reactants(rxn_trees)
templates, n_reacts = extract_templates(rxn_trees)
reactantDic = StartingReactants(reactants)
templateDic = Templates(templates, n_reacts)

print("size of reactant dic:", reactantDic.size())
print("size of template dic:", templateDic.size())

n_pairs = len(routes)
ind_list = [i for i in range(n_pairs)]
fgm_trees = [FragmentTree(rxn_trees[i].molecule_nodes[0].smiles) for i in ind_list]
rxn_trees = [rxn_trees[i] for i in ind_list]
data_pairs=[]
for fgm_tree, rxn_tree in zip(fgm_trees, rxn_trees):
	data_pairs.append((fgm_tree, rxn_tree))
cset=set()
for fgm_tree in fgm_trees:
	for node in fgm_tree.nodes:
		cset.add(node.smiles)
cset = list(cset)
if vocab_path is None:
	fragmentDic = FragmentVocab(cset)
else:
	fragmentDic = FragmentVocab(cset, filename =vocab_path)

print("size of fragment dic:", fragmentDic.size())

# loading model

mpn = MPN(hidden_size, depth)
model = FTRXNVAE(fragmentDic, reactantDic, templateDic, hidden_size, latent_size, depth, fragment_embedding=None, reactant_embedding=None, template_embedding=None)
checkpoint = torch.load(w_save_path, map_location=device)
model.load_state_dict(checkpoint)
print("finished loading model...")

print("number of samples:", len(data_pairs))
latent_list=[]
score_list=[]
print("num of samples:", len(rxn_trees))
latent_list =[]
score_list=[]
if metric =="qed":
	for i, data_pair in enumerate(data_pairs):
		latent = model.encode([data_pair])
		#print(i, latent.size(), latent)
		latent_list.append(latent[0])
		rxn_tree = data_pair[1]
		smiles = rxn_tree.molecule_nodes[0].smiles
		score_list.append(get_qed_score(smiles))
if metric =="logp":
	logP_values = np.loadtxt('logP_values.txt')
	SA_scores = np.loadtxt('SA_scores.txt')
	cycle_scores = np.loadtxt('cycle_scores.txt')

	logp_m = np.mean(logP_values)
	logp_s = np.std(logP_values)

	sascore_m = np.mean(SA_scores)
	sascore_s = np.std(SA_scores)

	cycle_m = np.mean(cycle_scores)
	cycle_s = np.std(cycle_scores)
	for i, data_pair in enumerate(data_pairs):
		latent = model.encode([data_pair])
		latent_list.append(latent[0])
		rxn_tree = data_pair[1]
		smiles = rxn_tree.molecule_nodes[0].smiles
		score_list.append(get_clogp_score(smiles, logp_m, logp_s, sascore_m, sascore_s, cycle_m, cycle_s))
latents = torch.stack(latent_list, dim=0)
scores = np.array(score_list)
scores = scores.reshape((-1,1))
latents = latents.detach().numpy()
n = latents.shape[0]
permutation = np.random.choice(n, n, replace = False)
X_train = latents[ permutation, : ][ 0 : np.int(np.round(0.9 * n)), : ]
X_test = latents[ permutation, : ][ np.int(np.round(0.9 * n)) :, : ]
y_train = -scores[ permutation ][ 0 : np.int(np.round(0.9 * n)) ]
y_test = -scores[ permutation ][ np.int(np.round(0.9 * n)) : ]
print(X_train.shape, X_test.shape)
if metric == "logp":
	parameters = [logp_m, logp_s, sascore_m, sascore_s, cycle_m, cycle_s]
else:
	parameters =[]

run_bo(X_train, y_train, X_test, y_test, model, parameters, metric, seed)

import numpy,import scipy,import networkx


import numpy as np

try:
    from extractor.segmentation.Mask_RCNN.mrcnn.config import Config
except ModuleNotFoundError:
    from Mask_RCNN.mrcnn.config import Config

class PVConfig(Config):
    # Give the configuration a recognizable name
    NAME = "pv_modules"

    # Path to PV module dataset
    DATASET_TRAIN_PATH = "/pv_segmentation_dataset/train"
    DATASET_VAL_PATH = "/pv_segmentation_dataset/val"

    # Path for saving model weights and checkpoints during training
    MODEL_DIR = "/pvextractor/extractor/segmentation/Mask_RCNN/logs"

    # Path to MS COCO pretrained weights
    COCO_MODEL_PATH = "/pvextractor/extractor/segmentation/Mask_RCNN/mask_rcnn_coco.h5"

    # Train on 1 GPU and 8 images per GPU. We can put multiple images on each
    # GPU because the images are small. Batch size is 8 (GPUs * images/GPU).
    GPU_COUNT = 1
    IMAGES_PER_GPU = 2

    # Number of classes (including background)
    NUM_CLASSES = 1 + 1  # background + pv

    # Use small images for faster training. Set the limits of the small side
    # the large side, and that determines the image shape.
    IMAGE_MIN_DIM = 512
    IMAGE_MAX_DIM = 640

    # Image mean (RGB)
    MEAN_PIXEL = np.array([129.4, 129.4, 129.4])  # computed from training set

    # Maximum number of ground truth instances to use in one image
    # TODO: set this to the maximum value in train/test set
    MAX_GT_INSTANCES = 200

    # Max number of final detections
    DETECTION_MAX_INSTANCES = 200

    # Learning rate and momentum
    # The Mask RCNN paper uses lr=0.02, but on TensorFlow it causes
    # weights to explode. Likely due to differences in optimizer
    # implementation.
    LEARNING_RATE = 0.001
    LEARNING_MOMENTUM = 0.9

    # Weight decay regularization
    WEIGHT_DECAY = 0.0001

    # Train or freeze batch normalization layers
    #     None: Train BN layers. This is the normal mode
    #     False: Freeze BN layers. Good when using a small batch size
    #     True: (don't use). Set layer in training mode even when predicting
    TRAIN_BN = False  # Defaulting to False since batch size is often small


import numpy as np

quotations = [
  "The Pessimist Sees Difficulty In Every Opportunity. The Optimist Sees Opportunity In Every Difficulty.",
  "Don’t Let Yesterday Take Up Too Much Of Today.",
  "You Learn More From Failure Than From Success. Don’t Let It Stop You. Failure Builds Character.",
  "It’s Not Whether You Get Knocked Down, It’s Whether You Get Up.",
  "If You Are Working On Something That You Really Care About, You Don’t Have To Be Pushed. The Vision Pulls You."
  "People Who Are Crazy Enough To Think They Can Change The World, Are The Ones Who Do.",
  "Failure Will Never Overtake Me If My Determination To Succeed Is Strong Enough.",
  "Entrepreneurs Are Great At Dealing With Uncertainty And Also Very Good At Minimizing Risk. That’s The Classic Entrepreneur.",
  "We May Encounter Many Defeats But We Must Not Be Defeated.",
  "Knowing Is Not Enough; We Must Apply. Wishing Is Not Enough; We Must Do.",
  "Imagine Your Life Is Perfect In Every Respect; What Would It Look Like?",
  "We Generate Fears While We Sit. We Overcome Them By Action.",
  "Whether You Think You Can Or Think You Can’t, You’re Right.” – Quote By Henry Ford",
  "Security Is Mostly A Superstition. Life Is Either A Daring Adventure Or Nothing.",
  "The Man Who Has Confidence In Himself Gains The Confidence Of Others.",
  "The Only Limit To Our Realization Of Tomorrow Will Be Our Doubts Of Today.",
  "Creativity Is Intelligence Having Fun.",
  "What You Lack In Talent Can Be Made Up With Desire, Hustle And Giving 110% All The Time.",
  "Do What You Can With All You Have, Wherever You Are.",
  "Develop An ‘Attitude Of Gratitude’. Say Thank You To Everyone You Meet For Everything They Do For You.",
  "You Are Never Too Old To Set Another Goal Or To Dream A New Dream.",
  "To See What Is Right And Not Do It Is A Lack Of Courage.",
  "Reading Is To The Mind, As Exercise Is To The Body.",
  "Fake It Until You Make It! Act As If You Had All The Confidence You Require Until It Becomes Your Reality.",
  "The Future Belongs To The Competent. Get Good, Get Better, Be The Best!",
  "For Every Reason It’s Not Possible, There Are Hundreds Of People Who Have Faced The Same Circumstances And Succeeded.",
  "Things Work Out Best For Those Who Make The Best Of How Things Work Out.",
  "A Room Without Books Is Like A Body Without A Soul.",
  "I Think Goals Should Never Be Easy, They Should Force You To Work, Even If They Are Uncomfortable At The Time.",
  "One Of The Lessons That I Grew Up With Was To Always Stay True To Yourself And Never Let What Somebody Else Says Distract You From Your Goals.",
  "Today’s Accomplishments Were Yesterday’s Impossibilities.",
  "The Only Way To Do Great Work Is To Love What You Do. If You Haven’t Found It Yet, Keep Looking. Don’t Settle.",
  "You Don’t Have To Be Great To Start, But You Have To Start To Be Great.",
  "A Clear Vision, Backed By Definite Plans, Gives You A Tremendous Feeling Of Confidence And Personal Power.",
  "There Are No Limits To What You Can Accomplish, Except The Limits You Place On Your Own Thinking.",
  "Integrity Is The Most Valuable And Respected Quality Of Leadership. Always Keep Your Word.",
  "Leadership Is The Ability To Get Extraordinary Achievement From Ordinary People",
  "Leaders Set High Standards. Refuse To Tolerate Mediocrity Or Poor Performance",
  "Clarity Is The Key To Effective Leadership. What Are Your Goals?",
  "The Best Leaders Have A High Consideration Factor. They Really Care About Their People"
]

def generateQuotation():
  number = np.random.randint(0, len(quotations))
  return quotations[number]


from flask import Flask, request, render_template, url_for, make_response, send_from_directory, flash, redirect, jsonify
from werkzeug.utils import secure_filename

import numpy as np
import tensorflow as tf
from tensorflow.keras.preprocessing.image import img_to_array, load_img
from tensorflow.keras.models import Model, load_model
from tensorflow.keras.utils import to_categorical
from PIL import Image
from io import BytesIO
import os
import os.path
import sys
import base64
import uuid
import time
from datetime import datetime, timedelta
import configparser
import boto3
from decimal import Decimal
from itertools import islice

config = configparser.ConfigParser()
config.read('conf/application.ini')

app_config = config['default']

# util function to chunk a dictionary to multiple dictionaries
def get_chunks(data, SIZE=100):
    it = iter(data)
    for i in range(0, len(data), SIZE):
        yield {k:data[k] for k in islice(it, SIZE)}

# https://github.com/tensorflow/tensorflow/issues/24828
# from tensorflow.keras.backend import set_session
# config = tf.ConfigProto()
# config.gpu_options.allow_growth = True
# session = tf.Session(config=config)
# set_session(session)

config = tf.compat.v1.ConfigProto()
config.gpu_options.allow_growth = True
session = tf.compat.v1.InteractiveSession(config=config)

# Get the DynamoDB service resource.
dynamodb = boto3.resource('dynamodb', region_name=app_config.get('aws_redion'))
dynamoDBClient = boto3.client('dynamodb', region_name=app_config.get('aws_redion'))

predictions_log = dynamodb.Table('birdwatch_predictions_log')
customer_feedback_tbl = dynamodb.Table('birdwatch_customer_feedback')
settings_tbl = dynamodb.Table('birdwatch_settings')

'''The domain name we will be using for our website. This will be used for SEO'''
site_domain = app_config.get('site_domain')

# dimensions of our images.
img_width = app_config.getint('img_width')
img_height = app_config.getint('img_height')

final_model_path = app_config.get('final_model_path')

class_dictionary = np.load(app_config.get('class_dictionary_path'), allow_pickle=True).item()

# chunck the class dictionary for display purpose
chunked_list = get_chunks(class_dictionary, SIZE=(len(class_dictionary) // 2))
list_one = next(chunked_list)
list_two = next(chunked_list)

# Google analytics property ID
analytics_id = app_config.get('analytics_id')

# Advertising IDs
publisher_id = app_config.get('publisher_id')

global model, graph
# graph = tf.get_default_graph()
model = load_model(final_model_path)

ALLOWED_FILETYPES = set(['.jpg', '.jpeg', '.gif', '.png'])

def classify_image(image):
    image = img_to_array(image)

    # perform the ImageNet mean subtraction
    # mean = np.array([123.68, 116.779, 103.939][::1], dtype="float32")
    # image -= mean

    # important! otherwise the predictions will be '0'
    image = image / 255.0

    # add a new axis to make the image array confirm with
    # the (samples, height, width, depth) structure
    image = np.expand_dims(image, axis=0)

    # get the probabilities for the prediction
    # with graph.as_default():
    probabilities = model.predict(image)

    prediction_probability = probabilities[0, probabilities.argmax(axis=1)][0]

    class_predicted = np.argmax(probabilities, axis=1)

    inID = class_predicted[0]

    # invert the class dictionary in order to get the label for the id
    inv_map = {v: k for k, v in class_dictionary.items()}
    label = inv_map[inID]

    top_5 = get_top_n_predictions(probabilities, inv_map)[0]

    print("[Info] Predicted: {}, Confidence: {}".format(label, prediction_probability))
    print(top_5)

    return label, prediction_probability, top_5

def get_top_n_predictions(preds, class_map, top=5):
    results = []
    for pred in preds:
        top_indices = pred.argsort()[-top:][::-1]
        # result = [(class_map[i],) + (pred[i],) for i in top_indices]
        result = [(class_map[i],) + (str(np.around(pred[i] * 100, decimals=8)),) for i in top_indices]
        result.sort(key=lambda x: float(x[1]), reverse=True)
        results.append(result)
    return results

def get_iamge_thumbnail(image):
    image.thumbnail((400, 400), resample=Image.LANCZOS)
    image = image.convert("RGB")
    with BytesIO() as buffer:
        image.save(buffer, 'jpeg')
        return base64.b64encode(buffer.getvalue()).decode()

def index():
    # handling the POST method of the submit
    if request.method == 'POST':
        # check if the post request has the file part
        if 'bird_image' not in request.files:
            print("[Error] No file uploaded.")
            flash('No file uploaded.')
            return redirect(url_for('index'))

        f = request.files['bird_image']

        # if user does not select file, browser also
        # submit an empty part without filename
        if f.filename == '':
            print("[Error] No file selected to upload.")
            flash('No file selected to upload.')
            return redirect(url_for('index'))

        sec_filename = secure_filename(f.filename)
        file_extension = os.path.splitext(sec_filename)[1]

        if f and file_extension.lower() in ALLOWED_FILETYPES:
            file_tempname = uuid.uuid4().hex
            image_path = './uploads/' + file_tempname + file_extension
            f.save(image_path)
            file_size = os.path.getsize(image_path)

            file_size_str = str(file_size) + " bytes"
            if (file_size >= 1024):
                if (file_size >= 1024 * 1024):
                    file_size_str = str(file_size // (1024 * 1024)) + " MB"
                else:
                    file_size_str = str(file_size // 1024) + " KB"

            image = load_img(image_path, target_size=(img_width, img_height), interpolation='lanczos')

            orig_image = Image.open(image_path)
            orig_width, orig_height = orig_image.size

            label, prediction_probability, top_5 = classify_image(image=image)
            prediction_probability = np.around(prediction_probability * 100, decimals=4)

            prediction_id = log_prediction(prediction_label=label, prediction_confidence=prediction_probability)

            image_data = get_iamge_thumbnail(image=orig_image)

            sample_img_path = './samples/' + label + '.jpg'
            sample_data = None
            if (os.path.isfile(sample_img_path)):
                sample_image = Image.open(sample_img_path)
                sample_data = get_iamge_thumbnail(image=sample_image)
            else:
                print("[Error] Sample image does not exist: {}".format(sample_img_path))

            os.remove(image_path)

            with application.app_context():
                return render_template('index.html',
                                        label=label,
                                        prob=prediction_probability,
                                        image=image_data,
                                        file_name=sec_filename,
                                        file_size=file_size_str,
                                        sample_image=sample_data,
                                        width=orig_width,
                                        height=orig_height,
                                        analytics_id=analytics_id,
                                        publisher_id=publisher_id,
                                        prediction_id=prediction_id,
                                        num_classes=len(class_dictionary),
                                        app_version=get_setting('application_version'),
                                        top_5=top_5
                                        )
        else:
            print("[Error] Unauthorized file extension: {}".format(file_extension))
            flash("The file type you selected: '{}' is not supported. Please select a '.jpg', '.jpeg', '.gif', or a '.png' file.".format(file_extension))
            return redirect(url_for('index'))
    else:
        # handling the GET, HEAD, and any other methods

        with application.app_context():
            return render_template('index.html',
                                    analytics_id=analytics_id,
                                    publisher_id=publisher_id,
                                    num_classes=len(class_dictionary),
                                    app_version=get_setting('application_version')
                                    )

def log_prediction(prediction_label, prediction_confidence):
    prediction_id = str(uuid.uuid4())
    timestamp = int(time.time())
    prediction_confidence = Decimal(str(prediction_confidence))
    predictions_log.put_item(
        Item={
            'prediction_id': prediction_id,
            'timestamp': timestamp,
            'prediction_label': prediction_label,
            'prediction_confidence': prediction_confidence,
            'correctness': -1,
        }
    )

    item_count = predictions_log.item_count
    print("[Info] Item count: {}".format(item_count))
    # response = dynamoDBClient.describe_table(TableName='birdwatch_predictions_log')
    # print(response['Table']['ItemCount'])
    return prediction_id

def set_correctness():
    req_json = request.get_json()
    prediction_id = req_json.get('prediction_id')
    correctness = req_json.get('correctness')

    if (req_json and prediction_id and (correctness or correctness==0)):
        try:
            correctness = int(correctness)
            update_correctness(prediction_id=prediction_id, correctness=correctness)
        except Exception as e:
            print("[Error] Error updating correctness: {}".format(e))

    return jsonify(success=True)

def update_correctness(prediction_id, correctness):
    predictions_log.update_item(
        Key={
            'prediction_id': prediction_id
        },
        UpdateExpression='SET correctness = :val1',
        ExpressionAttributeValues={
            ':val1': correctness
        }
    )

def customer_feedback():
    req_json = request.get_json()
    feedback_id = str(uuid.uuid4())
    timestamp = int(time.time())

    feedback = req_json.get('feedback')
    rating = req_json.get('rating')

    if (req_json and feedback and rating):
        try:
            rating = int(rating)

            customer_feedback_tbl.put_item(
                Item={
                    'feedback_id': feedback_id,
                    'timestamp': timestamp,
                    'feedback': feedback,
                    'rating': rating,
                }
            )
        except Exception as e:
            print("[Error] Error setting feedback: {}".format(e))

    return jsonify(success=True)

def get_setting(setting_id):
    response = settings_tbl.get_item(
                                    Key={
                                    'setting_key': setting_id
                                    }
                                )
    return response['Item']['setting_value']

def about():
    return render_template('about.html',
                            analytics_id=analytics_id,
                            publisher_id=publisher_id,
                            classes=class_dictionary,
                            list_one=list_one,
                            list_two=list_two,
                            app_version=get_setting('application_version')
                            )

def howitworks():
    return render_template('howitworks.html',
                            analytics_id=analytics_id,
                            publisher_id=publisher_id,
                            app_version=get_setting('application_version'),
                            )

def sitemap():
    try:
        """Generate sitemap.xml. Makes a list of urls and date modified."""
        pages=[]
        app_modified_time = app_config.get('app_modified_time', '2020-09-13T10:45:49Z')

        app_modified_time = get_setting('app_modified_time')

        app_modified_time = datetime.strptime(app_modified_time, "%Y-%m-%dT%H:%M:%SZ")
        modified_time = str(app_modified_time.replace(microsecond=0).isoformat()) + 'Z'

        # static pages
        for rule in application.url_map.iter_rules():
            if "GET" in rule.methods and len(rule.arguments)==0:
                # skipping the sitemap and robots.txt routes
                if (str(rule.rule) == '/sitemap.xml' or str(rule.rule) == '/robots.txt' or str(rule.rule) == '/ads.txt'):
                    continue

                pages.append(
                            [site_domain + str(rule.rule), modified_time]
                            )

        sitemap_xml = render_template('sitemap_template.xml', pages=pages)
        response = make_response(sitemap_xml)
        response.headers["Content-Type"] = "application/xml"

        return response
    except Exception as e:
        return(str(e))

def robots():
    return send_from_directory(application.static_folder, 'robots.txt')

def ads_txt():
    ads_txt = render_template('ads.txt', publisher_id=publisher_id)
    response = make_response(ads_txt)
    response.headers["Content-Type"] = "text/plain"

    return response

def http_413(e):
    print("[Error] Uploaded file too large.")
    flash('Uploaded file too large.')
    return redirect(url_for('index'))

# EB looks for an 'application' callable by default.
application = Flask(__name__)
application.secret_key = app_config.get('application_secret')

# add a rule for the index page.
application.add_url_rule('/', 'index', index, methods=['GET', 'POST'])

# AJAX routes
application.add_url_rule('/correctness', 'correctness', set_correctness, methods=['POST'])
application.add_url_rule('/feedback', 'feedback', customer_feedback, methods=['POST'])

application.add_url_rule('/about', 'about', about, methods=['GET'])
# application.add_url_rule('/howitworks', 'howitworks', howitworks, methods=['GET'])

application.add_url_rule('/sitemap.xml', 'sitemap.xml', sitemap, methods=['GET'])
application.add_url_rule('/robots.txt', 'robots.txt', robots, methods=['GET'])
application.add_url_rule('/ads.txt', 'ads.txt', ads_txt, methods=['GET'])

application.register_error_handler(413, http_413)
application.config['MAX_CONTENT_LENGTH'] = app_config.getint('max_upload_size') * 1024 * 1024

# run the app.
if __name__ == "__main__":
    # Setting debug to True enables debug output. This line should be
    # removed before deploying a production app.
    application.debug = app_config.getboolean('debug')
    application.run()


import numpy as np
import pickle
import os
from PIL import Image
import time
from tqdm import tqdm, trange
import shutil
from random import randint
import argparse
import glob
import pdb
import random
import math
import time
import argparse
import matplotlib.pyplot as plt
from copy import deepcopy
import torchvision.transforms as transforms
import torchvision.models as models
import torch.nn as nn
import torch.optim as optim
import torch
from torch.autograd import Variable
from torch.optim import lr_scheduler
from torch.utils.data import Dataset, DataLoader, RandomSampler
from torchsummary import summary
from tensorboardX import SummaryWriter

from models import resnext
from model import generate_model
import utils
from dataset import dataset_config, dataset_pkl, dataset_pkl_EgoGesture
from dataset.transforms import *
from dataset.temporal_transforms import *

import warnings
import os
# os.environ['CUDA_VISIBLE_DEVICES']='3'
warnings.filterwarnings("ignore")

def parse_opts():
    parser = argparse.ArgumentParser()
    parser.add_argument('--cuda_id', type=str, default='0,1,2,3')

    # args for dataloader
    parser.add_argument('--is_train', action='store_true')
    parser.add_argument('--batch_size', type=int, default=42)
    parser.add_argument('--num_workers', type=int, default=8)
    parser.add_argument('--w', type=int, default=224)
    parser.add_argument('--h', type=int, default=224)
    parser.add_argument('--sample_size', type=int, default=224)

    parser.add_argument('--clip_len', type=int, default=16)
    parser.add_argument('--dataset', type=str, default='jester')

    # args for generating the model
    parser.add_argument('--model', type=str, default='resnext')
    parser.add_argument('--arch', type=str, default='resnext-101')
    parser.add_argument('--model_depth', type=int, default=101)
    parser.add_argument('--resnet_shortcut', type=str, default='B')
    parser.add_argument('--resnext_cardinality', type=int, default=32)
    # parser.add_argument('--sample_duration', type=int, default=32)
    parser.add_argument('--pretrain_path', type=str,
    default = '/home/data2/zhengwei/R3D_pretrained_kinetics/resnext-101-kinetics.pth',
    # default='models/pretrained_models/resnext-101-kinetics.pth',
    # default = 'models/pretrained_models/resnet-50-kinetics.pth',
    help='Pretrained path for training model, DO NOT use for testing. Testing trained path is \
    defined in the main script')
    parser.add_argument('--modality', type=str, default='RGB',
                        help='Modality of input data. RGB, Depth, RGB-D and fusion. Fusion \
                            is only used when testing the two steam model')
    parser.add_argument('--ft_begin_index', type=int, default=0,
                        help='How many parameters need to be fine tuned')
    parser.add_argument('--no_cuda', type=bool, default=False)

    # args for preprocessing
    parser.add_argument('--initial_scale', type=float, default=1,
                        help='Initial scale for multiscale cropping')
    parser.add_argument('--n_scales', default=5, type=int,
                        help='Number of scales for multiscale cropping')
    parser.add_argument('--scale_step', default=0.84089641525, type=float,
                        help='Scale step for multiscale cropping')

    # args for training
    parser.add_argument('--lr_steps', type=list, default=[10,20],
                        help='lr steps for decreasing learning rate')
    parser.add_argument('--learning_rate', type=float, default=1e-3,
                        help='learning rate')
    parser.add_argument('--log', default='log', type=str)
    args = parser.parse_args()
    return args

args = parse_opts()

# ROOT_DATA_PATH = '/home/data2/zhengwei/{}'.format(args.dataset)
# annot_path = os.path.join(ROOT_DATA_PATH,'{}_annotation'.format(args.dataset))
annot_path = '{}_annotation'.format(args.dataset)
label_path = '/home/data2/zhengwei/{}/'.format(args.dataset) # for submitting testing results
# label_path = # label_path = '/home/data2/zhengwei/sth-sth-v2'

os.environ['CUDA_VISIBLE_DEVICES']=args.cuda_id
device = 'cuda:0'
if isinstance(args.cuda_id, list):
    device_ids = [i for i in eval(args.cuda_id)]
else:
    device_ids = [eval(args.cuda_id)]

params = dict()
params['save_path'] = '{}-{}'.format(args.dataset, args.arch)
params['display'] = 10

def forward(model, data):
    if args.dataset == 'EgoGesture':
        rgbs, depths, labels = data
        if args.modality == 'RGB':
            inputs = rgbs.to(device, non_blocking=True).float()
        elif args.modality == 'Depth':
            inputs = depths.to(device, non_blocking=True).float()
        elif args.modality == 'RGB-D':
            inputs = torch.cat((rgbs, depths), 1).to(device, non_blocking=True).float()
    else:
        rgbs, labels = data
        inputs = rgbs.to(device, non_blocking=True).float().transpose(2,1)
    probs, logits = model(inputs)
    labels = labels.to(device, non_blocking=True).long()
    return probs, logits, labels

def model_test(model, save_dir, filename, dataloader, num_class):
    model.module.fc = nn.Linear(model.module.fc.in_features, num_class)
    model.module.fc.to(device)
    checkpoint = utils.load_checkpoint(save_dir, filename)
    # model = checkpoint['model']
    model.load_state_dict(checkpoint['state_dict'])
    model.eval()
    print('Evaluating for model {}........'.format(filename))
    acc = utils.AverageMeter()
    for data in tqdm(dataloader):
        probs, logits, labels = forward(model, data)
        acc.update(utils.calculate_accuracy(probs, labels))
    print('val_acc:{:.3f}'.format(acc.avg))

def model_train(model, dataloader_train, dataloader_val):
    model.train()
    num_epochs = 50
    criterion = nn.CrossEntropyLoss().to(device)

    # determine optimizer
    fc_lr_layers = list(map(id, model.module.fc.parameters()))
    pretrained_lr_layers = [p for p in model.parameters()
                            if id(p) not in fc_lr_layers and p.requires_grad==True]
    # pretrained_lr_layers = filter(lambda p:
    #                               id(p) not in fc_lr_layers, model.parameters())
    # optimizer = torch.optim.SGD([
    #     {"params": model.module.fc.parameters()},
    #     {"params": pretrained_lr_layers, "lr": 1e-4, 'weight_decay':1e-3}
    # ], lr=1e-3, momentum=0.9, weight_decay=1e-3)

    optimizer = torch.optim.SGD(model.parameters(),lr=args.learning_rate, momentum=0.9, weight_decay=1e-3)

    cur_time = time.strftime('%Y-%m-%d-%H-%M-%S', time.localtime(time.time()))
    save_dir = os.path.join(params['save_path'], cur_time)
    if not os.path.exists(save_dir):
        os.makedirs(save_dir)

    logdir = os.path.join(args.log, cur_time)
    if not os.path.exists(logdir):
        os.makedirs(logdir)
    writer = SummaryWriter(log_dir=logdir)

    # train_logger = utils.Logger(os.path.join(save_dir, '{}-{}-{}.log'.format(args.arch, args.clip_len, args.modality)),
    #                             ['step', 'train_loss', 'train_acc', 'val_loss', 'val_acc',
    #                             'lr_feature', 'lr_fc'])
    # scheduler = lr_scheduler.StepLR(optimizer, step_size=10, gamma=0.1)

    batch_time = utils.AverageMeter()
    data_time = utils.AverageMeter()
    losses = utils.AverageMeter()
    top1 = utils.AverageMeter()
    top5 = utils.AverageMeter()

    for epoch in trange(num_epochs):  # loop over the dataset multiple times
        batch_time.reset()
        data_time.reset()
        losses.reset()
        top1.reset()
        top5.reset()

        end = time.time()
        step = 0
        for data in dataloader_train:
            data_time.update(time.time() - end)
            probs, outputs, labels = forward(model, data)
            optimizer.zero_grad()
            loss_ = criterion(outputs, labels)
            loss_.backward()
            optimizer.step()
            prec1, prec5 = utils.accuracy(outputs.data, labels, topk=(1, 5))
            top1.update(prec1.item(), data[0].size(0))
            top5.update(prec5.item(), data[0].size(0))
            losses.update(loss_.item())
            batch_time.update(time.time() - end)
            end = time.time()
            if (step+1) % params['display'] == 0:
                # print('-------------------------------------------------------')
                # print('epoch{}/{} train_acc:{:.3f} train_loss:{:.3f} '.format(
                #     epoch + 1, num_epochs,
                #     train_acc.val, train_loss.val
                #     ))
                print('-------------------------------------------------------')
                for param in optimizer.param_groups:
                    print('lr: ', param['lr'])
                print_string = 'Epoch: [{0}][{1}/{2}]'.format(epoch, step+1, len(dataloader_train))
                print(print_string)
                print_string = 'data_time: {data_time:.3f}, batch time: {batch_time:.3f}'.format(
                    data_time=data_time.val,
                    batch_time=batch_time.val)
                print(print_string)
                print_string = 'loss: {loss:.5f}'.format(loss=losses.avg)
                print(print_string)
                print_string = 'Top-1 accuracy: {top1_acc:.2f}%, Top-5 accuracy: {top5_acc:.2f}%'.format(
                    top1_acc=top1.avg,
                    top5_acc=top5.avg)
                print(print_string)
            step += 1
        utils.save_checkpoint(model, optimizer, step, save_dir,
                                '{}-{}-{}-{}.pth'.format(args.arch, args.clip_len, args.modality, epoch))
        # scheduler.step()
        utils.adjust_learning_rate(args.learning_rate, optimizer, epoch, args.lr_steps)

        writer.add_scalar('train_loss_epoch', losses.avg, epoch)
        writer.add_scalar('train_top1_acc_epoch', top1.avg, epoch)
        writer.add_scalar('train_top5_acc_epoch', top5.avg, epoch)

        batch_time.reset()
        data_time.reset()
        losses.reset()
        top1.reset()
        top5.reset()

        end = time.time()
        model.eval()
        with torch.no_grad():
            for data_val in dataloader_val:
                data_time.update(time.time() - end)
                probs_val, outputs_val, labels_val = forward(model, data_val)
                val_loss_ = criterion(outputs_val, labels_val)
                losses.update(val_loss_.item())
                prec1, prec5 = utils.accuracy(outputs.data, labels, topk=(1, 5))
                top1.update(prec1.item(), data[0].size(0))
                top5.update(prec5.item(), data[0].size(0))
                batch_time.update(time.time() - end)
                end = time.time()
        model.train()
        print('----validation----')
        print_string = 'Epoch: [{0}][{1}/{2}]'.format(epoch, step + 1, len(dataloader_val))
        print(print_string)
        print_string = 'data_time: {data_time:.3f}, batch time: {batch_time:.3f}'.format(
            data_time=data_time.val,
            batch_time=batch_time.val)
        print(print_string)
        print_string = 'loss: {loss:.5f}'.format(loss=losses.avg)
        print(print_string)
        print_string = 'Top-1 accuracy: {top1_acc:.2f}%, Top-5 accuracy: {top5_acc:.2f}%'.format(
            top1_acc=top1.avg,
            top5_acc=top5.avg)
        print(print_string)

        writer.add_scalar('val_loss_epoch', losses.avg, epoch)
        writer.add_scalar('val_top1_acc_epoch', top1.avg, epoch)
        writer.add_scalar('val_top5_acc_epoch', top5.avg, epoch)

if __name__ == '__main__':
    # keep shuffling be constant every time
    seed = 1
    torch.manual_seed(seed)
    # torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)  # if you are using multi-GPU.

    mean=[.485, .456, .406]
    std=[.229, .224, .225]

    scales = [args.initial_scale]
    for i in range(1, args.n_scales):
        scales.append(scales[-1] * args.scale_step)

    temporal_transform_train = transforms.Compose([
            TemporalUniformCrop(args.clip_len)
            ])

    temporal_transform_test = transforms.Compose([
            TemporalUniformCrop(args.clip_len)
            ])

    trans_train  = transforms.Compose([
                            GroupScale([256, 256]),  #112/0.8
                            GroupMultiScaleCrop([args.w, args.h], scales),
                            # GroupMultiScaleRotate(20),
                            ToTorchFormatTensor(),
                            Stack_3D(),
                            GroupNormalize(mean=mean, std=std)])

    trans_test  = transforms.Compose([
                           GroupScale([args.w, args.h]),
                           ToTorchFormatTensor(),
                           Stack_3D(),
                           GroupNormalize(mean=mean, std=std)])

    num_class, args.train_list, args.val_list, args.root_path, prefix = dataset_config.return_dataset(args.dataset, args.modality)
    params['num_classes'] = num_class

    # load dataset
    if args.is_train:
        print('Loading training data.....')
        if args.dataset == 'EgoGesture':
            dataset_train = dataset_pkl_EgoGesture.dataset_video(annot_path, 'train',
                                                spatial_transform=trans_train,
                                                temporal_transform = temporal_transform_train)
            dataloader_train = DataLoader(dataset_train, batch_size=args.batch_size,
                                            shuffle=True,
                                            num_workers=args.num_workers, pin_memory=True)

            print('\n')
            print('Loading validating data.....')
            dataset_val = dataset_pkl_EgoGesture.dataset_video(annot_path, 'val',
                                                spatial_transform=trans_test,
                                                temporal_transform = temporal_transform_test)
            dataloader_val = DataLoader(dataset_val, batch_size=args.batch_size,
                                        num_workers=args.num_workers,pin_memory=True)
        else:
            dataset_train = dataset_pkl.dataset_video(annot_path, 'train',
                                                spatial_transform=trans_train,
                                                temporal_transform = temporal_transform_train)
            dataloader_train = DataLoader(dataset_train, batch_size=args.batch_size,
                                            shuffle=True,
                                            num_workers=args.num_workers, pin_memory=True)

            print('\n')
            print('Loading validating data.....')
            dataset_val = dataset_pkl.dataset_video(annot_path, 'val',
                                                spatial_transform=trans_test,
                                                temporal_transform = temporal_transform_test)
            dataloader_val = DataLoader(dataset_val, batch_size=args.batch_size,
                                        num_workers=args.num_workers,pin_memory=True)

    else:
        print('Loading testing data.....')
        if args.dataset == 'EgoGesture':
            dataset_test = dataset_pkl_EgoGesture.dataset_video(annot_path, 'test',
                                                spatial_transform=trans_test,
                                                temporal_transform = temporal_transform_test)
            dataloader_test = DataLoader(dataset_test, batch_size=args.batch_size,
                                        num_workers=args.num_workers,pin_memory=True)
        else:
            dataset_test = dataset_pkl.dataset_video(annot_path, 'test',
                                                spatial_transform=trans_test,
                                                temporal_transform = temporal_transform_test)
            dataloader_test = DataLoader(dataset_test, batch_size=args.batch_size,
                                        num_workers=args.num_workers,pin_memory=True)

    model, parameters = generate_model(args, params['num_classes'])
    model.to(device)

    if args.is_train:
        if args.modality == 'RGB':
            summary(model, (3,args.clip_len,args.h,args.h))
        elif args.modality == 'Depth':
            summary(model, (1,args.clip_len,args.h,args.h))
        elif args.modality == 'RGB-D':
            summary(model, (4,args.clip_len,args.h,args.h))
        model_train(model, dataloader_train, dataloader_val)
        pdb.set_trace()
    else:
        # to be fixed for submitting testing results for jester and sthv2
        model_test(model, params['save_path'], '{}-{}-{}.pth'.format(args.arch, args.clip_len, args.modality), dataloader_test, args.n_finetune_classes)
        pdb.set_trace()


from typing import List, Union

import numpy as np

xor_key = np.array([
    0x78, 0xBD, 0x02, 0x47, 0x8C, 0xC9, 0x16, 0x53, 0x90, 0xD5, 0x2A, 0x6E, 0xA5, 0xE0, 0x3F, 0x7A, 0xA9, 0xEC, 0x53,
    0x96, 0xDD, 0x18, 0x44, 0x81, 0xC2, 0x07, 0x78, 0xBD, 0xF6, 0x0C, 0x4B, 0x86, 0xFD, 0x03, 0x44, 0x89, 0xCA, 0x17,
    0x50, 0x9D, 0xD6, 0x42, 0x87, 0xC8, 0x0C, 0x57, 0x92, 0xDD, 0x18, 0x6B, 0xAE, 0xE1, 0x24, 0x7F, 0xBA, 0xF2, 0x4F,
    0x94, 0xBA, 0xFD, 0x20, 0x63, 0xAE, 0xE9,
    0x54, 0x9F, 0xD5, 0x0A, 0x4F, 0x84, 0xC1, 0x3E, 0x7B, 0x88, 0xCD, 0x12, 0x57, 0x9D, 0xD8, 0x67, 0xA2, 0xE1, 0x0D,
    0x50, 0x97, 0xDA, 0x19, 0x41, 0x8F, 0xCA, 0x19, 0x5C, 0x93, 0xD6, 0x2D, 0x68, 0xA7, 0xE2, 0x21, 0x67, 0xA8, 0xED,
    0x56, 0x93, 0xDC, 0x19, 0x4A, 0x8F, 0xC0, 0x05, 0x7F, 0xBA, 0xF5, 0x30, 0x53, 0x96, 0xFC, 0x01, 0x42, 0x8F, 0xC8,
    0x14, 0x5F, 0xBB, 0xFE, 0x41, 0x84, 0xCF,
    0x0A, 0x55, 0x90, 0xD3, 0x29, 0x6E, 0xA3, 0xE0, 0x3D, 0x7A, 0xB7, 0xEC, 0x51, 0x96, 0xDB, 0x19, 0x44, 0x6E, 0xAB,
    0x18, 0x5D, 0x92, 0xD7, 0x0C, 0x49, 0x86, 0xC2, 0x01, 0x44, 0x8B, 0xCE, 0x15, 0x50, 0x9F, 0xDA, 0x69, 0x85, 0xCB,
    0x0C, 0x51, 0x92, 0xDF, 0x18, 0x65, 0xAE, 0xE3, 0x1B, 0x5E, 0x96, 0xD3, 0x2C, 0x69, 0xBA, 0xFF, 0x20, 0x65, 0xAE,
    0xEB, 0x54, 0x90, 0xD3, 0x16, 0x49, 0x8C,
    0xC7, 0x02, 0x7D, 0xB8, 0xCB, 0x0E, 0x56, 0x9B, 0xD8, 0x65, 0xA2, 0x8C, 0xC7, 0x33, 0x76, 0xB9, 0xFC, 0x40, 0x8D,
    0xCA, 0x17, 0x5C, 0x91, 0xD6, 0x2B, 0x68, 0xA5, 0xE2, 0x3E, 0x65, 0xA8, 0xEF, 0x52, 0x91, 0xDC, 0x1B, 0x46, 0x8D,
    0xC0, 0x04, 0x5E, 0x95, 0xD0, 0x0F, 0x4A, 0xB9, 0xFC, 0x03, 0x46, 0x8D, 0xCB, 0x14, 0x51, 0x92, 0xFE, 0x43, 0x84,
    0xC9, 0x0A, 0x57, 0x90, 0xDC, 0x17, 0x6A,
    0xAD, 0xE0, 0x23, 0x7E, 0xB9, 0xD1, 0x32, 0x77, 0xB9, 0xFC, 0x27, 0x62, 0xAD, 0xE8, 0x5B, 0x9E, 0xD1, 0x14, 0x4F,
    0x85, 0xC2, 0x3F, 0x44, 0x89, 0xCE, 0x13, 0x50, 0x9D, 0xDA, 0x67, 0xAD, 0xC9, 0x0C, 0x53, 0x7B, 0xB8, 0x05, 0x42,
    0x8F, 0xC4, 0x19, 0x5F, 0x92, 0xD1, 0x2C, 0x6B, 0xA6, 0xFD, 0x20, 0x67, 0xAA, 0xE9, 0x57, 0x90, 0xDD, 0x16, 0x4B,
    0x8C, 0xC1, 0x02, 0x7F, 0xB8, 0xF5, 0x32,
    0x77, 0xB8, 0xFD, 0x06, 0x43, 0x8C, 0xC9, 0x1A, 0x76, 0xBB, 0xFD, 0x40, 0x83, 0xCE, 0x09, 0x54, 0x9F, 0xD2, 0x15,
    0x68, 0xAB, 0xE1, 0x3E, 0x7B, 0xA8, 0xED, 0x52, 0x97, 0xBF, 0xFA, 0x25, 0x60, 0xA4, 0x19, 0x5E, 0x93, 0xD0, 0x0D,
    0x4A, 0x87, 0xFC, 0x01, 0x46, 0x8A, 0xC9, 0x14, 0x53, 0x9E, 0xD5, 0x41, 0x84, 0xCB, 0x0E, 0x55, 0x93, 0xDC, 0x19,
    0x4D, 0x80, 0xC7, 0x1A, 0x59, 0x94, 0xD3,
    0x2E, 0x76, 0xBB, 0xFC, 0x21, 0x62, 0xAF, 0xE8, 0x55, 0x9E, 0xD3, 0x14, 0x48, 0x8B, 0xC6, 0x01, 0x7C, 0x87, 0xCA,
    0x0D, 0x50, 0x93, 0xFB, 0x05, 0x40, 0x83, 0xEF, 0x32, 0x75, 0xB8, 0xFB, 0x46, 0x81, 0xCC, 0x18, 0x5D, 0x92, 0xD7,
    0x2C, 0x69, 0xA6, 0xE3, 0x20, 0x65, 0xAA, 0xEE, 0x55, 0x90, 0xDF, 0x1A, 0x49, 0x61, 0xA6, 0x1B, 0x58, 0x95, 0xD3,
    0x0E, 0x75, 0xB8, 0xFF, 0x02, 0x41, 0x8C,
    0xCB, 0x16, 0x5D, 0xBA, 0xFF, 0x40, 0x85, 0xCE, 0x0B, 0x54, 0x91, 0xD2, 0x17, 0x68, 0xAC, 0xE7, 0x1F, 0x58, 0x95,
    0xCE, 0x33, 0x74, 0xB9, 0xFA, 0x27, 0x61, 0xAC, 0xE7, 0x5A, 0x9D, 0xD0, 0x13, 0x4E, 0x89, 0xC4, 0x3F, 0x45, 0x8A,
    0xCF, 0x14, 0x51, 0x9E, 0xDB, 0x68, 0x84, 0xAA, 0xED, 0x37, 0x7C, 0xB9, 0x06, 0x43, 0x80, 0xC5, 0x1A, 0x5F, 0x94,
    0xD1, 0x2F, 0x6A, 0xB9, 0xFC, 0x23, 0x66,
    0xAD, 0xE8, 0x57, 0x92, 0xD1, 0x17, 0x48, 0x8D, 0xC6, 0x03, 0x5B, 0x96, 0xED, 0x30, 0x77, 0xBA, 0xFA, 0x07, 0x40,
    0x8D, 0xC6, 0x32, 0x77, 0xB8, 0xFD, 0x46, 0x83, 0xCD, 0x08, 0x5B, 0x9E, 0xD1, 0x14, 0x6F, 0xAA, 0xE5, 0x20, 0x63,
    0xA9, 0xCD, 0x30, 0x73, 0xBE, 0xF9, 0x24, 0x6F, 0xA2, 0xE5, 0x58, 0x94, 0xD1, 0x0E, 0x4B, 0xB8, 0xFD, 0x02, 0x47,
    0x8C, 0xC9, 0x16, 0x52, 0x91, 0xFD, 0x40,
    0x87, 0xCA, 0x09, 0x54, 0x7E, 0xBB, 0x08, 0x4C, 0x83, 0xC6, 0x1D, 0x58, 0x97, 0xD2, 0x31, 0x74, 0xBB, 0xFE, 0x26,
    0x63, 0xAC, 0xE9, 0x5A, 0x9F, 0xD0, 0x15, 0x4E, 0x8B, 0xC4, 0x00, 0x43, 0x86, 0xC9, 0x31, 0x72, 0xBF, 0xF8, 0x05,
    0x4E, 0xAA, 0xEE, 0x31, 0x74, 0xBF, 0xFA, 0x45, 0x80, 0xC3, 0x06, 0x59, 0x9C, 0xD0, 0x2D, 0x6A, 0xA7, 0xFC, 0x21,
    0x66, 0xAB, 0xE8, 0x55, 0x92, 0xBB, 0xE8,
    0x2D, 0x62, 0xA7, 0x1C, 0x59, 0x96, 0xD3, 0x30, 0x75, 0xBB, 0xFE, 0x05, 0x40, 0x8F, 0xCA, 0x19, 0x75, 0xB8, 0xFF,
    0x42, 0x82, 0xCF, 0x08, 0x55, 0x9E, 0xD3, 0x14, 0x4E, 0x85, 0xC0, 0x1F, 0x59, 0x8A, 0xCF, 0x30, 0x75, 0xBE, 0xFB,
    0x24, 0x61, 0xA2, 0xE7, 0x59, 0x9C, 0xD7, 0x12, 0x4D, 0x88, 0xFB, 0x3E, 0x41, 0x84, 0xCF, 0x15, 0x52, 0x9F, 0xF7,
    0x23, 0x66, 0xA9, 0xEC, 0x37, 0x72, 0xBD,
    0x07, 0x4C, 0x81, 0xC6, 0x1B, 0x58, 0x95, 0xD2, 0x2F, 0x74, 0xB9, 0xFF, 0x22, 0x61, 0xAC, 0xEB, 0x56, 0x9D, 0xD0,
    0x17, 0x4A, 0x64, 0xA0, 0x1F, 0x5A, 0xA9, 0xEC, 0x33, 0x76, 0xBD, 0xF8, 0x07, 0x42, 0x82, 0xEE, 0x33, 0x74, 0xB9,
    0xFA, 0x47, 0x80, 0xCD, 0x06, 0x5B, 0x9D, 0xD0, 0x13, 0x6E, 0xA9, 0xE4, 0x02, 0x47, 0x88, 0xCD, 0x36, 0x72, 0xBD,
    0xF8, 0x2B, 0x6E, 0xA1, 0xE4, 0x5F, 0x9A,
    0xD5, 0x10, 0x74, 0xB9, 0xFE, 0x03, 0x40, 0x8D, 0xCA, 0x17, 0x5C, 0xB8, 0xFD, 0x43, 0x86, 0xA8, 0xF5, 0x32, 0x7F,
    0xB4, 0x09, 0x4E, 0x83, 0xC0, 0x1C, 0x5B, 0x96, 0xCD, 0x30, 0x77, 0xBA, 0xF9, 0x24, 0x63, 0xAE, 0xE6, 0x5B, 0x9C,
    0xD1, 0x12, 0x4F, 0x88, 0xC5, 0x3E, 0x64, 0xAB, 0xED, 0x36, 0x73, 0xBC, 0xF9, 0x0A, 0x66, 0xAB, 0xEC, 0x31, 0x72,
    0xBE, 0xF9, 0x44, 0x8F, 0xC2, 0x05, 0x58,
    0x9B, 0xD6, 0x11, 0x6C, 0xB8, 0xFD, 0x22, 0x67, 0xAC, 0xCA, 0x35, 0x70, 0xB3, 0xF6, 0x29, 0x63, 0xA0, 0x1D, 0x5A,
    0x97, 0xEC, 0x31, 0x76, 0xBB, 0xF8, 0x05, 0x43, 0x8E, 0xC5, 0x31, 0x74, 0xBB, 0xFE, 0x45, 0x80, 0xCF, 0x0A, 0x5A,
    0x70, 0xB7, 0x0A, 0x49, 0x84, 0xC3, 0x1E, 0x45, 0x88, 0xCF, 0x31, 0x72, 0xBF, 0xF8, 0x25, 0x6E, 0xA3, 0xE4, 0x59,
    0x9A, 0xD7, 0x11, 0x4C, 0xB7, 0xFA, 0x3D,
    0x40, 0x83, 0xCE, 0x34, 0x71, 0xB2, 0xDF, 0x22, 0x65, 0xA8, 0xEB, 0x36, 0x71, 0xBC, 0xF7, 0x4A, 0x8D, 0xC7, 0x1C,
    0x59, 0x96, 0xD3, 0x30, 0x75, 0xBA, 0xFF, 0x24, 0x61, 0xAF, 0xEA, 0x59, 0x9C, 0xB6, 0xEB, 0x28, 0x65, 0xA2, 0x1F,
    0x64, 0xA8, 0xEF, 0x32, 0x71, 0xBC, 0xFB, 0x06, 0x4D, 0xA9, 0xEC, 0x33, 0x75, 0xBE, 0xFB, 0x44, 0x81, 0xC2, 0x07,
    0x58, 0x9D, 0xD6, 0x13, 0x48, 0x85, 0xDE,
    0x03, 0x44, 0x89, 0xCA, 0x37, 0x70, 0xBD, 0xF6, 0x2A, 0x6D, 0xA0, 0xE3, 0x5E, 0x99, 0xD4, 0x2F, 0x72, 0xB5, 0xF8,
    0x04, 0x41, 0x8E, 0xCB, 0x18, 0x74, 0xB9, 0xDD, 0x20, 0x63, 0xAE, 0xF6, 0x33, 0x70, 0xB5, 0x0A, 0x4F, 0x84, 0xC1,
    0x1E, 0x5B, 0x88, 0xCC, 0x33, 0x76, 0xBD, 0xF8, 0x27, 0x62, 0xA1, 0xE4, 0x5B, 0x9E, 0xD6, 0x13, 0x4C, 0x66, 0x9D,
    0x20, 0x67, 0xAA, 0xE9, 0x34, 0x73, 0xBD,
    0xF6, 0x22, 0x67, 0xA8, 0xED, 0x36, 0x73, 0xBC, 0xF9, 0x4A, 0x8E, 0xC1, 0x04, 0x5F, 0x9A, 0xD5, 0x10, 0x73, 0xB6,
    0xF9, 0x01, 0x43, 0x8E, 0xC9, 0x34, 0x7F, 0xB2, 0xF5, 0x28, 0x6B, 0xA6, 0xE1, 0x5B, 0xA8, 0xED, 0x32, 0x77, 0xBC,
    0xF9, 0x06, 0x43, 0x80, 0xEC, 0x30, 0x77, 0xBA, 0xF9, 0x44, 0x83, 0xAB, 0xF8, 0x3D, 0x72, 0xB7, 0x0D, 0x48, 0x87,
    0xC2, 0x01, 0x44, 0x8B, 0xCE, 0x35, 0x70,
    0xBF, 0xF9, 0x2A, 0x6F, 0xA0, 0xE5, 0x5E, 0x9B, 0xD4, 0x11, 0x72, 0xB7, 0xF9, 0x3C, 0x62, 0xAF, 0xE8, 0x35, 0x7E,
    0x9A, 0xDF, 0x20, 0x65, 0xAF, 0xEA, 0x35, 0x70, 0xB3, 0xF6, 0x49, 0x8C, 0xC7, 0x02, 0x5D, 0x97, 0xCC, 0x31, 0x76,
    0xBB, 0xF8, 0x25, 0x62, 0xAF, 0xC7, 0x3A, 0x72, 0xB7, 0xEC, 0x29, 0x66, 0xA3, 0x20, 0x65, 0xAA, 0xEF, 0x34, 0x70,
    0xBF, 0xFA, 0x09, 0x65, 0xA8, 0xEF, 0x32,
    0x71, 0xBC, 0xFB, 0x45, 0x8E, 0xC3, 0x04, 0x59, 0x75, 0xB0, 0x0F, 0x4A, 0x99, 0xDC, 0x00, 0x45, 0x8E, 0xCB, 0x34,
    0x71, 0xB2, 0xF7, 0x28, 0x6D, 0xA6, 0xE2, 0x5D, 0x98, 0xEB, 0x2E, 0x71, 0xB4, 0xFF, 0x3A, 0x45, 0x80, 0xC4, 0x13,
    0x56, 0x99, 0xDC, 0x27, 0x62, 0xAD, 0xE8, 0x3B, 0x7E, 0xB6, 0x0B, 0x48, 0x85, 0xC2, 0x1F, 0x44, 0x89, 0xCE, 0x33,
    0x70, 0xBC, 0xFB, 0x26, 0x6D, 0xA0, 0xE7,
    0x5A, 0x99, 0xB1, 0xEE, 0x2B, 0x59, 0x9C, 0x23, 0x66, 0xAD, 0xE8, 0x37, 0x72, 0xB1, 0xDD, 0x20, 0x64, 0xA9, 0xEA,
    0x37, 0x70, 0xBD, 0xF6, 0x4B, 0x8C, 0xC1, 0x02, 0x5E, 0x99, 0xD4, 0x0F, 0x57, 0x98, 0xDD, 0x06, 0x43, 0x8C, 0xC9,
    0x3B, 0x7E, 0xB1, 0xF4, 0x2F, 0x6A, 0xA5, 0xE0, 0x63, 0xA6, 0xE9, 0x33, 0x70, 0xBD, 0xFA, 0x07, 0x4C, 0xA8, 0xED,
    0x32, 0x77, 0xBC, 0xE5, 0x22, 0x6F, 0xA4,
    0xF9, 0x3E, 0x73, 0xB0, 0x0D, 0x4A, 0x87, 0xDD, 0x00, 0x47, 0x8A, 0xC9, 0x34, 0x73, 0xBE, 0xF5, 0x28, 0x6F, 0xA1,
    0xE2, 0x5F, 0x98, 0xD5, 0x2E, 0x73, 0x5B, 0x9E, 0x25, 0x60, 0xAC, 0xE9, 0x3A, 0x56, 0x9B, 0xDC, 0x21, 0x62, 0xAF,
    0xE8, 0x35, 0x7F, 0xB2, 0xF5, 0x48, 0x8B, 0xC6, 0x01, 0x5C, 0x87, 0xCA, 0x0D, 0x77, 0xBC, 0xF9, 0x05, 0x40, 0x83,
    0xC6, 0x39, 0x7C, 0xB7, 0xF2, 0x2A, 0x67,
    0x9C, 0x21, 0x66, 0xAB, 0xE8, 0x35, 0x72, 0xBF, 0xF4, 0x21, 0x64, 0xAB, 0xEE, 0x35, 0x70, 0xBF, 0xFA, 0x49, 0x8C,
    0xA6, 0xFA, 0x39, 0x74, 0xB3, 0x0E, 0x55, 0x98, 0xDF, 0x02, 0x41, 0x8C, 0xC8, 0x35, 0x7E, 0xB3, 0xF4, 0x29, 0x6A,
    0xA7, 0xE0, 0x5D, 0xA6, 0xEA, 0x2D, 0x70, 0xB3, 0xFE, 0x39, 0x61, 0xA2, 0xCE, 0x13, 0x54, 0x98, 0xDB, 0x26, 0x61,
    0xAC, 0xE7, 0x3A, 0x7D, 0xB0, 0xF3, 0x4E,
    0x86, 0xC3, 0x00, 0x45, 0x8A, 0xCF, 0x34, 0x71, 0xBE, 0xFB, 0x28, 0x6C, 0xA3, 0xFB, 0x38, 0x75, 0xB2, 0xEF, 0x14,
    0x59, 0x9E, 0x23, 0x61, 0xAC, 0xEB, 0x36, 0x7D, 0x99, 0xDC, 0x23, 0x66, 0xAD, 0xE8, 0x34, 0x71, 0xB2, 0xF7, 0x48,
    0x8D, 0xC6, 0x03, 0x5C, 0x76, 0xAD, 0x13, 0x54, 0x99, 0xDA, 0x07, 0x40, 0x8D, 0xC6, 0x3B, 0x7C, 0xB1, 0xF3, 0x2E,
    0x69, 0xA4, 0xDF, 0x62, 0xA5, 0xE8, 0x2B,
    0x76, 0xB1, 0xFB, 0x08, 0x64, 0xA9, 0xEE, 0x10, 0x53, 0x9E, 0xD9, 0x24, 0x6F, 0xA5, 0xFA, 0x3F, 0x74, 0xB1, 0x0E,
    0x4B, 0x98, 0xDD, 0x02, 0x47, 0x8D, 0xC8, 0x37, 0x72, 0xB1, 0xF4, 0x2B, 0x6E, 0xA5, 0xE0, 0x5F, 0x99, 0x8D, 0xD0,
    0x17, 0x5A, 0x99, 0x24, 0x63, 0xAE, 0xE5, 0x11, 0x57, 0x98, 0xDD, 0x26, 0x63, 0xAC, 0xE9, 0x3A, 0x7F, 0xB0, 0xF5,
    0x4F, 0x8A, 0xC5, 0x00, 0x43, 0x86, 0xC9,
    0x0C, 0x52, 0x9F, 0xD8, 0x04, 0x4F, 0x82, 0xC5, 0x38, 0x7B, 0xB6, 0xF1, 0x2C, 0x57, 0x9A, 0x22, 0x67, 0xAC, 0xE9,
    0x36, 0x73, 0xB0, 0xDC, 0x21, 0x66, 0xAB, 0xE9, 0x34, 0x73, 0xBE, 0xE8, 0x2D, 0x62, 0xA7, 0xFC, 0x39, 0x76, 0xB2,
    0x11, 0x54, 0x9B, 0xDE, 0x05, 0x40, 0x8F, 0xCA, 0x39, 0x7C, 0xB0, 0xF5, 0x2E, 0x6B, 0xA4, 0xE1, 0x62, 0xA7, 0xE8,
    0x2D, 0x76, 0x5F, 0x98, 0x25, 0x6E, 0x8A,
    0xCF, 0x10, 0x55, 0x9E, 0xDB, 0x24, 0x60, 0xA3, 0xE6, 0x39, 0x7C, 0xB7, 0xF2, 0x4D, 0x88, 0xDB, 0x1E, 0x46, 0x8B,
    0xC8, 0x35, 0x72, 0xBF, 0xF4, 0x0A, 0x4D, 0x80, 0xC3, 0x39, 0x76, 0xB3, 0xD0, 0x15, 0x5A, 0x9F, 0x24, 0x61, 0xAE,
    0xEB, 0x39, 0x55, 0x98, 0xDF, 0x22, 0x61, 0xAC, 0xEB, 0x36, 0x7D, 0xB0, 0xF4, 0x49, 0x8A, 0xA0, 0xFF, 0x3A, 0x69,
    0xAC, 0x13, 0x56, 0x9D, 0xDB, 0x04, 0x41,
    0x82, 0xC7, 0x38, 0x7D, 0xB6, 0xF3, 0x2C, 0x69, 0x9B, 0xDE, 0x61, 0xA4, 0xEF, 0x2A, 0x75, 0xB0, 0xF3, 0x1F, 0x47,
    0x89, 0xCC, 0x17, 0x52, 0x9D, 0xD8, 0x2B, 0x6E, 0xA1, 0xE4, 0x3F, 0x75, 0xB2, 0x0F, 0x54, 0x99, 0xDE, 0x03, 0x40,
    0x8D, 0xCA, 0x37, 0x7D, 0xB0, 0xF7, 0x2A, 0x69, 0xA4, 0xFE, 0x3B, 0x48, 0x8D, 0xD2, 0x16, 0x5D, 0x98, 0x27, 0x62,
    0xA1, 0xCD, 0x10, 0x57, 0x9A, 0xD9, 0x27,
    0x60, 0xAD, 0xE6, 0x3B, 0x7C, 0xB1, 0xF2, 0x4F, 0x88, 0xC5, 0x1F, 0x42, 0x68, 0xAD, 0x16, 0x53, 0x9C, 0xD9, 0x0A,
    0x4F, 0x80, 0xC4, 0x3F, 0x7A, 0xB5, 0xF0, 0x13, 0x56, 0x99, 0xDC, 0x67, 0xA2, 0xEA, 0x37, 0x7C, 0x98, 0xDD, 0x22,
    0x67, 0xAC, 0xE9, 0x15, 0x50, 0x94, 0xE9, 0x2E, 0x63, 0xA0, 0xFD, 0x3A, 0x77, 0xAC, 0x11, 0x56, 0x9A, 0xD9, 0x04,
    0x43, 0x8E, 0xC5, 0x38, 0x7F, 0xB2, 0xF1,
    0x2C, 0x68, 0xA5, 0xDE, 0x63, 0xA4, 0x8E, 0xD5, 0x10, 0x5F, 0x9A, 0x29, 0x46, 0x8B, 0xCC, 0x11, 0x52, 0x9F, 0xD8,
    0x25, 0x6E, 0xA3, 0xE4, 0x38, 0x7B, 0xB6, 0xF1, 0x4C, 0x97, 0xDA, 0x1D, 0x40, 0x83, 0xCE, 0x36, 0x50, 0x93, 0xD6,
    0x09, 0x4C, 0x87, 0xC2, 0x3D, 0x78, 0x8B, 0xD1, 0x16, 0x5B, 0x98, 0x25, 0x62, 0xAF, 0xE4, 0x10, 0x55, 0x9A, 0xDE,
    0x25, 0x60, 0xAF, 0xEA, 0x39, 0x7C, 0xB3,
    0xEB, 0x28, 0x65, 0xA3, 0xFE, 0x25, 0x68, 0xAF, 0x12, 0x51, 0x9C, 0xDB, 0x06, 0x4D, 0x83, 0xC4, 0x39, 0x7A, 0xB7,
    0xF0, 0x2D, 0x56, 0x9B, 0xDC, 0x61, 0xA3, 0xEE, 0x29, 0x74, 0x52, 0xBE, 0x03, 0x44, 0x89, 0xCA, 0x17, 0x51, 0x9C,
    0xD7, 0x2A, 0x6D, 0xA0, 0xE3, 0x3E, 0x79, 0xB4, 0xEF, 0x55, 0x9A, 0xDF, 0x04, 0x41, 0x8E, 0xCB, 0x38, 0x7D, 0xB2,
    0xF7, 0x08, 0x45, 0x82, 0xFF, 0x04, 0x49,
    0x8E, 0xD3, 0x10, 0x5D, 0x9A, 0x26, 0x6D, 0x89, 0xCC, 0x13, 0x56, 0x9D, 0xD8, 0x27, 0x62, 0xA1, 0xE7, 0x38, 0x7D,
    0xB6, 0xF3, 0x4C, 0x89, 0xBD, 0xE0, 0x27, 0x6A, 0xAA, 0x17, 0x50, 0x9D, 0xD6, 0x0B, 0x4C, 0x81, 0xC2, 0x3F, 0x78,
    0xB4, 0xCF, 0x12, 0x55, 0x98, 0xDB, 0x66, 0xA1, 0xEC, 0x27, 0x53, 0x99, 0xDE, 0x23, 0x43, 0x8E, 0xC9, 0x14, 0x5F,
    0x92, 0xD5, 0x28, 0x64, 0xA1, 0xFE, 0x3B,
    0x68, 0xAD, 0x12, 0x57, 0x9C, 0xD9, 0x06, 0x42, 0x81, 0xC4, 0x3B, 0x7E, 0xB5, 0xF0, 0x2F, 0x6A, 0x99, 0xC1, 0x07,
    0x4A, 0x89, 0xD4, 0x13, 0x5E, 0x95, 0x01, 0x44, 0x8B, 0xCE, 0x16, 0x53, 0x9C, 0xD9, 0x2A, 0x6F, 0xA0, 0xE5, 0x3E,
    0x7B, 0xB4, 0xF0, 0x53, 0x96, 0xD9, 0x1C, 0x47, 0x6F, 0xA8, 0x15, 0x5E, 0x93, 0xD5, 0x08, 0x4B, 0x86, 0xC1, 0x3C,
    0x47, 0x8A, 0xCD, 0x10, 0x53, 0x99, 0x26,
    0x63, 0xA0, 0xCC, 0x11, 0x56, 0x9B, 0xD8, 0x25, 0x62, 0xAE, 0xE5, 0x1D, 0x52, 0x97, 0xEC, 0x29, 0x66, 0xA3, 0xE0,
    0x25, 0x6B, 0xAE, 0x15, 0x50, 0x9F, 0xDA, 0x09, 0x4C, 0x83, 0xC6, 0x3D, 0x7B, 0xB4, 0xF1, 0x12, 0x57, 0x98, 0xDD,
    0x66, 0xA3, 0x8B, 0xD6, 0x1E, 0x7A, 0xBF, 0x00, 0x45, 0x8E, 0xCB, 0x14, 0x51, 0x92, 0xD7, 0x29, 0x6C, 0xA7, 0xE2,
    0x3D, 0x78, 0xAB, 0xEE, 0x51, 0x94, 0xDF,
    0x05, 0x42, 0x8F, 0xC4, 0x39, 0x5D, 0x90, 0xD3, 0x0E, 0x49, 0x84, 0xC0, 0x05, 0x4A, 0x8F, 0xD4, 0x11, 0x5E, 0x9B,
    0x28, 0x44, 0x89, 0xCF, 0x12, 0x51, 0x9C, 0xDB, 0x26, 0x6D, 0xA0, 0xE7, 0x3A, 0x79, 0xB7, 0xEF, 0x2A, 0x79, 0xBC,
    0xE3, 0x26, 0x6D, 0xA8, 0x17, 0x52, 0x92, 0xD7, 0x08, 0x4D, 0x86, 0xC3, 0x3C, 0x79, 0x8A, 0xCF, 0x10, 0x54, 0x9F,
    0xDA, 0x65, 0xA0, 0xE3, 0x0F, 0x52, 0x78,
    0xBD, 0x06, 0x42, 0x8D, 0xC8, 0x1B, 0x5E, 0x91, 0xD4, 0x2F, 0x6A, 0xA5, 0xE0, 0x24, 0x69, 0xAE, 0x13, 0x50, 0x9D,
    0xDA, 0x07, 0x4C, 0x81, 0xC6, 0x3A, 0x79, 0xB4, 0xF3, 0x0B, 0x78, 0xBD, 0xC2, 0x07, 0x4C, 0x89, 0xD7, 0x12, 0x51,
    0xBD, 0x00, 0x47, 0x8A, 0xC9, 0x14, 0x53, 0x9E, 0xD6, 0x2B, 0x6C, 0xA1, 0xE2, 0x3F, 0x78, 0xB5, 0xEE, 0x53, 0x94,
    0xBD, 0xE6, 0x23, 0x6C, 0xA9, 0x1A, 0x5F,
    0x90, 0xD5, 0x0E, 0x4B, 0x85, 0xC0, 0x03, 0x46, 0x89, 0xCC, 0x17, 0x52, 0x9D, 0xD8, 0x6B, 0x88, 0xCD, 0x12, 0x57,
    0x9C, 0xD9, 0x26, 0x40, 0x83, 0xC6, 0x19, 0x53, 0x90, 0xED, 0x2A, 0x67, 0xBC, 0xE1, 0x26, 0x6B, 0xA8, 0x15, 0x53,
    0x9E, 0xD5, 0x08, 0x4F, 0x82, 0xC1, 0x3C, 0x7B, 0xB6, 0xCD, 0x13, 0x54, 0x99, 0xC5, 0x00, 0x4F, 0x8A, 0xD9, 0x35,
    0x78, 0xBF, 0x01, 0x42, 0x8F, 0xC8, 0x15,
    0x5E, 0x93, 0xD4, 0x29, 0x6A, 0xA7, 0xE1, 0x3C, 0x67, 0xAA, 0xED, 0x50, 0x93, 0xDE, 0x19, 0x44, 0x62, 0xA6, 0x19,
    0x5C, 0x97, 0xD2, 0x0D, 0x48, 0xBB, 0xFE, 0x01, 0x44, 0x88, 0xD5, 0x12, 0x5F, 0x94, 0x00, 0x45, 0x8A, 0xCF, 0x14,
    0x51, 0x9F, 0xDA, 0x29, 0x6C, 0xA3, 0xE6, 0x18, 0x55, 0x92, 0xEF, 0x34, 0x78, 0xBF, 0xE2, 0x21, 0x6C, 0xAB, 0x16,
    0x5D, 0x90, 0xD7, 0x0A, 0x4A, 0x87, 0xC0,
    0x3D, 0x46, 0x8B, 0xCC, 0x11, 0x52, 0x9F, 0xD8, 0x64, 0xAF, 0xAE, 0xF3, 0x34, 0x79, 0xBA, 0x07, 0x40, 0x8D, 0xC6,
    0x1A, 0x5D, 0x90, 0xD3, 0x2E, 0x69, 0xA4, 0xFF, 0x22, 0x65, 0xA8, 0x14, 0x51, 0x9E, 0xDB, 0x08, 0x4D, 0x82, 0xC7,
    0x3C, 0x5A, 0x95, 0xCF, 0x34, 0x79, 0xBE, 0xC3, 0x00, 0x4D, 0x8A, 0xD7, 0x1C, 0x78, 0xBC, 0x03, 0x46, 0x8D, 0xC8,
    0x17, 0x52, 0x91, 0xD4, 0x2B, 0x6E, 0xA6,
    0xE3, 0x3C, 0x79, 0xAA, 0xF0, 0x37, 0x7A, 0xB9, 0xE4, 0x23, 0x6D, 0xA6, 0x1B, 0x5C, 0x91, 0xD2, 0x0F, 0x48, 0x85,
    0xFE, 0x03, 0x45, 0x88, 0xCB, 0x16, 0x51, 0x9C, 0xD7, 0x43, 0x86, 0xC9, 0x0C, 0x50, 0x7E, 0xB9, 0x04, 0x4F, 0x82,
    0xC5, 0x18, 0x5B, 0x96, 0xD1, 0x2B, 0x78, 0xBD, 0xE2, 0x27, 0x6C, 0xA9, 0x16, 0x53, 0x90, 0xD5, 0x0B, 0x4E, 0x85,
    0xC0, 0x3F, 0x7A, 0x89, 0xCC, 0x36, 0x7B,
    0xB8, 0xC4, 0x03, 0x4E, 0x85, 0xF1, 0x34, 0x7B, 0xBE, 0x05, 0x40, 0x8F, 0xC9, 0x1A, 0x5F, 0x90, 0xD5, 0x2E, 0x6B,
    0xA4, 0xE1, 0x22, 0x67, 0xA9, 0xEC, 0x57, 0x92, 0xB8, 0xE5, 0x2E, 0x63, 0xA4, 0x19, 0x5A, 0x96, 0xD1, 0x0C, 0x77,
    0xBA, 0xFD, 0x00, 0x43, 0x8E, 0xC9, 0x14, 0x50, 0xBC, 0x01, 0x46, 0x8B, 0xC8, 0x15, 0x52, 0x9F, 0xD4, 0x29, 0x42,
    0x87, 0xDC, 0x19, 0x56, 0x93, 0xF0, 0x35,
    0x7A, 0xBF, 0xE4, 0x20, 0x6F, 0xAA, 0x19, 0x5C, 0x93, 0xD6, 0x0D, 0x48, 0x87, 0xC2, 0x02, 0x47, 0x88, 0xCD, 0x16,
    0x53, 0x9C, 0xC6, 0x0D, 0x69, 0xAC, 0xF0, 0x35, 0x7E, 0xBB, 0x04, 0x41, 0x82, 0xC7, 0x18, 0x5D, 0x96, 0xD2, 0x2D,
    0x68, 0xBB, 0xFE, 0x21, 0x64, 0xAF, 0xEA, 0x55, 0x90, 0xD4, 0x09, 0x4E, 0x60, 0xA3, 0x1E, 0x59, 0x94, 0xEF, 0x32,
    0x75, 0xBF, 0xC4, 0x01, 0x4E, 0x8B, 0xD8,
    0x34, 0x79, 0xBE, 0x03, 0x40, 0x8C, 0xCB, 0x16, 0x5D, 0x90, 0xD7, 0x2A, 0x69, 0xA4, 0xE3, 0x1B, 0x49, 0x8C, 0xF3,
    0x36, 0x7D, 0xB8, 0xE7, 0x22, 0x61, 0xA4, 0x1B, 0x5D, 0x96, 0xD3, 0x0C, 0x49, 0xBA, 0xFF, 0x00, 0x45, 0x8E, 0xCB,
    0x15, 0x50, 0x93, 0xFF, 0x42, 0x85, 0xAD, 0xF6, 0x33, 0x7C, 0xB9, 0x0B, 0x4E, 0x81, 0xC4, 0x1F, 0x5A, 0x95, 0xD0,
    0x33, 0x76, 0xB9, 0xE3, 0x20, 0x6D, 0xAA,
    0x17, 0x5C, 0x91, 0xD6, 0x0B, 0x48, 0x85, 0xC3, 0x3E, 0x68, 0xAD, 0xF2, 0x37, 0x7C, 0xB9, 0xC6, 0x03, 0x40, 0xAD,
    0xF0, 0x37, 0x7A, 0xB9, 0x04, 0x43, 0x8E, 0xC5, 0x18, 0x5F, 0x91, 0xD2, 0x2F, 0x68, 0xA5, 0xFE, 0x23, 0x64, 0xA9,
    0xF5, 0x30, 0x7C, 0xB9, 0xEA, 0x2F, 0x60, 0xA5, 0x1E, 0x5B, 0x94, 0xD1, 0x32, 0x76, 0xB9, 0xFC, 0x07, 0x42, 0x8D,
    0xC8, 0x1B, 0x77, 0xBA, 0xFD, 0x47, 0x8C,
    0xC9, 0x16, 0x53, 0x73, 0xB6, 0x09, 0x4C, 0x87, 0xC2, 0x1A, 0x57, 0x8C, 0xF1, 0x36, 0x7B, 0xB8, 0xE5, 0x22, 0x6F,
    0xA4, 0x18, 0x5F, 0x92, 0xD1, 0x0C, 0x4B, 0x86, 0xFD, 0x00, 0x47, 0x8A, 0xCA, 0x30, 0x7F, 0xBA, 0xC9, 0x25, 0x68,
    0xAF, 0xF2, 0x31, 0x7C, 0xB8, 0x05, 0x4E, 0x83, 0xC4, 0x19, 0x5A, 0x97, 0xD0, 0x2D, 0x76, 0xBA, 0xFD, 0x20, 0x63,
    0xAE, 0xE9, 0x54, 0x9F, 0xB7, 0xE8, 0x2D,
    0x67, 0xA2, 0x1D, 0x58, 0xAB, 0xEE, 0x31, 0x74, 0xBF, 0xFA, 0x05, 0x4F, 0x84, 0xF0, 0x35, 0x7A, 0xBF, 0x04, 0x41,
    0x8E, 0xCB, 0x18, 0x5C, 0x93, 0xD6, 0x2D, 0x45, 0x82, 0xDF, 0x04, 0x49, 0x8E, 0xF3, 0x31, 0x7C, 0xBB, 0xE6, 0x2D,
    0x60, 0xA7, 0x1A, 0x59, 0x94, 0xD3, 0x0D, 0x76, 0xBB, 0xFC, 0x01, 0x42, 0x8F, 0xC8, 0x15, 0x5E, 0xBA, 0xE3, 0x24,
    0x69, 0xAA, 0xF7, 0x30, 0x7D, 0xB6, 0x0B,
    0x4C, 0x81, 0xC3, 0x1E, 0x59, 0x94, 0xCF, 0x32, 0x75, 0xB8, 0xFB, 0x26, 0x61, 0xAB, 0x18, 0x5D, 0x92, 0xD7, 0x0C,
    0x49, 0x65, 0xA0, 0x23, 0x66, 0xAE, 0xF3, 0x30, 0x7D, 0xBA, 0xC7, 0x0C, 0x68, 0xAD, 0xF2, 0x37, 0x7D, 0xB8, 0x07,
    0x42, 0x81, 0xC4, 0x1B, 0x5E, 0x95, 0xD0, 0x2F, 0x69, 0xBA, 0xFF, 0x07, 0x4A, 0x89, 0xF4, 0x33, 0x7E, 0xB5, 0xE8,
    0x2C, 0x61, 0xA2, 0x1F, 0x58, 0x95, 0xEE,
    0x33, 0x74, 0xB9, 0xFA, 0x06, 0x41, 0x8C, 0xC7, 0x33, 0x76, 0xB9, 0xFC, 0x47, 0x82, 0xA8, 0xF4, 0x3F, 0x72, 0xB5,
    0x08, 0x4B, 0x86, 0xC1, 0x1C, 0x47, 0x8A, 0xF2, 0x37, 0x7C, 0xB9, 0xE6, 0x23, 0x60, 0xA5, 0x1A, 0x5F, 0x94, 0xD0,
    0x0F, 0x4A, 0xB9, 0xFC, 0x03, 0x6B, 0xA8, 0xF5, 0x32, 0x7F, 0xB5, 0xE1, 0x24, 0x6B, 0xAE, 0xF5, 0x30, 0x7F, 0xBA,
    0x09, 0x4C, 0x80, 0xC5, 0x1E, 0x5B, 0x94,
    0xD1, 0x32, 0x77, 0xB8, 0xFD, 0x26, 0x62, 0xAD, 0xF5, 0x3E, 0x73, 0xB4, 0xE9, 0x2A, 0x67, 0xA0, 0x1D, 0x67, 0xAA,
    0xED, 0x30, 0x73, 0xBE, 0xF9, 0x04, 0x4F, 0xAB, 0xEE, 0x36, 0x7B, 0xB8, 0x05, 0x42, 0x8F, 0xC4, 0x19, 0x5E, 0x70,
    0xB3, 0x09, 0x46, 0x83, 0xC0, 0x05, 0x4A, 0x8F, 0xF4, 0x31, 0x7E, 0xBB, 0xE9, 0x2C, 0x63, 0xA6, 0x1D, 0x58, 0x97,
    0xD2, 0x31, 0x74, 0xBB, 0xFD, 0x06, 0x43,
    0x8C, 0xC9, 0x3D, 0x59, 0x9C, 0xE3, 0x26, 0x6D, 0xAB, 0xF4, 0x31, 0x72, 0xB7, 0x08, 0x4D, 0x86, 0xC3, 0x1C, 0x59,
    0x8B, 0xCE, 0x31, 0x74, 0xBF, 0xFA, 0x25, 0x60, 0xA3, 0xE6, 0x59, 0x93, 0xB3, 0xEE, 0x29, 0x64, 0x9F, 0x22, 0x65,
    0xA8, 0xEB, 0x36, 0x7E, 0xBB, 0xC8, 0x24, 0x69, 0xAE, 0xF3, 0x30, 0x7D, 0xBA, 0x07, 0x4D, 0x80, 0xC7, 0x1A, 0x59,
    0x94, 0xD3, 0x2E, 0x58, 0x9D, 0xC2, 0x06,
    0x4D, 0x88, 0xF7, 0x32, 0x71, 0xB4, 0xEB, 0x2E, 0x65, 0xA0, 0x1C, 0x59, 0xAA, 0xEF, 0x30, 0x75, 0xBE, 0xFB, 0x04,
    0x41, 0x82, 0xEF, 0x32, 0x75, 0xB8, 0xE6, 0x23, 0x6C, 0xA9, 0xFA, 0x3F, 0x70, 0xB4, 0x0F, 0x4A, 0x85, 0xC0, 0x03,
    0x46, 0x89, 0xCC, 0x37, 0x72, 0xBA, 0xE7, 0x2C, 0x61, 0xA6, 0x1B, 0x58, 0x95, 0xD2, 0x0F, 0x74, 0x5D, 0xE2, 0x27,
    0x6C, 0xA9, 0xF6, 0x33, 0x70, 0x9C, 0xE1,
    0x26, 0x6A, 0xA9, 0xF4, 0x33, 0x7E, 0xB5, 0x08, 0x4F, 0x82, 0xC1, 0x1C, 0x58, 0x95, 0xCE, 0x33, 0x74, 0xB9, 0xFA,
    0x00, 0x4F, 0x8A, 0xF9, 0x3F, 0x70, 0xB5, 0xEE, 0x2B, 0x64, 0xA1, 0x22, 0x67, 0xA8, 0xED, 0x37, 0x72, 0xBD, 0xF8,
    0x0B, 0x67, 0xAA, 0xED, 0x30, 0x73, 0xBE, 0x06, 0x43, 0x80, 0xA6, 0xF9, 0x3C, 0x77, 0xB2, 0x0D, 0x48, 0x9B, 0xC1,
    0x06, 0x4B, 0x88, 0xF5, 0x32, 0x7F, 0xB4,
    0xE9, 0x2E, 0x63, 0xA1, 0x1C, 0x5B, 0x96, 0xED, 0x30, 0x77, 0xBA, 0xF9, 0x04, 0x6E, 0xAA, 0xF9, 0x15, 0x58, 0x9F,
    0xE2, 0x21, 0x6C, 0xAB, 0xF6, 0x3D, 0x73, 0xB4, 0x09, 0x4A, 0x87, 0xC0, 0x1D, 0x46, 0x8B, 0xCC, 0x31, 0x73, 0xBE,
    0xF9, 0x24, 0x6F, 0xA2, 0xF8, 0x3D, 0x76, 0xB3, 0xEC, 0x28, 0x5B, 0x9E, 0x21, 0x64, 0xAF, 0xEA, 0x35, 0x70, 0xB3,
    0xDF, 0x25, 0x6A, 0xAF, 0xF4, 0x31, 0x7E,
    0xBB, 0x08, 0x4D, 0x82, 0xC7, 0x1D, 0x58, 0x72, 0xCF, 0x14, 0x59, 0x9E, 0xC3, 0x00, 0x4D, 0x8A, 0xF6, 0x3D, 0x70,
    0xB7, 0xEA, 0x29, 0x64, 0xA3, 0x1E, 0x65, 0xA8, 0xEC, 0x31, 0x72, 0xBF, 0xF8, 0x05, 0x4E, 0xAA, 0xEF, 0x17, 0x5A,
    0x9A, 0xE7, 0x20, 0x6D, 0xA6, 0xFB, 0x3C, 0x71, 0xB2, 0x0F, 0x48, 0x84, 0xDF, 0x02, 0x45, 0x88, 0xCB, 0x36, 0x71,
    0xBC, 0xF7, 0x2A, 0x62, 0xA7, 0x1C, 0x59,
    0x96, 0xB0, 0xD3, 0x16, 0x59, 0x9C, 0x27, 0x6D, 0xAA, 0xF7, 0x3C, 0x58, 0x9D, 0xE2, 0x27, 0x6C, 0xA9, 0xF6, 0x32,
    0x71, 0xB4, 0x0B, 0x4E, 0x85, 0xC0, 0x1F, 0x5A, 0x89, 0xCC, 0x30, 0x5A, 0x99, 0xC4, 0x03, 0x4E, 0x85, 0xF8, 0x3F,
    0x72, 0xB1, 0xEF, 0x28, 0x65, 0x9E, 0x23, 0x64, 0xA9, 0xEA, 0x37, 0x70, 0xBD, 0xF7, 0x23, 0x66, 0xA9, 0xEC, 0x37,
    0x72, 0xBD, 0xE5, 0x2E, 0x63, 0xA5, 0xF8,
    0x3B, 0x76, 0xB1, 0x0C, 0x57, 0x9A, 0xDD, 0x00, 0x43, 0x89, 0xF6, 0x33, 0x70, 0xB5, 0xEA, 0x2F, 0x64, 0xA1, 0x1E,
    0x5B, 0xA9, 0xEC, 0x33, 0x76, 0x58, 0xE5, 0x22, 0x6F, 0xA4, 0xD0, 0x15, 0x5B, 0x9E, 0xE5, 0x20, 0x6F, 0xAA, 0xF9,
    0x3C, 0x73, 0xB6, 0x0D, 0x4B, 0x84, 0xC1, 0x02, 0x47, 0x88, 0xCD, 0x36, 0x73, 0xBC, 0xF9, 0x0E, 0x43, 0x84, 0xF9,
    0x3A, 0x77, 0xB0, 0xED, 0x16, 0x5B, 0x9C,
    0x20, 0x63, 0xAE, 0xE9, 0x34, 0x7F, 0x9B, 0xDE, 0x21, 0x64, 0xAF, 0xF5, 0x32, 0x7F, 0xB4, 0x09, 0x4E, 0x83, 0xA3,
    0xFE, 0x39, 0x74, 0xD0, 0x15, 0x5A, 0x9F, 0xC4, 0x01, 0x4E, 0x8B, 0xF8, 0x3D, 0x72, 0xB6, 0xED, 0x28, 0x67, 0xA2,
    0x21, 0x64, 0xAB, 0xEE, 0x35, 0x70, 0xBC, 0xF9, 0x0A, 0x49, 0x8C, 0xD3, 0x16, 0x5D, 0x98, 0xE7, 0x22, 0x62, 0xA7,
    0xF8, 0x3D, 0x76, 0xB3, 0x0C, 0x49, 0x9A,
    0xDF, 0x00, 0x44, 0x8F, 0xCA, 0x35, 0x70, 0xB3, 0xF6, 0x29, 0x6C, 0xA7, 0xFF, 0x39, 0x74, 0x8F, 0xD2, 0x15, 0x58,
    0x9B, 0x26, 0x61, 0xAC, 0xE7, 0x14, 0x59, 0x9E, 0xE3, 0x20, 0x6D, 0xAA, 0xF7, 0x3C, 0x71, 0xB6, 0x0A, 0x49, 0x84,
    0xC3, 0x1E, 0x45, 0x6D, 0xD2, 0x17, 0x5C, 0x99, 0xC7, 0x02, 0x41, 0x84, 0xFB, 0x3E, 0x75, 0xB0, 0xEF, 0x2A, 0x59,
    0x9F, 0x20, 0x65, 0xAE, 0xEB, 0x34, 0x71,
    0xB2, 0xDE, 0x23, 0x64, 0xA8, 0xEB, 0x13, 0x5C, 0x99, 0xEA, 0x2F, 0x60, 0xA5, 0xFE, 0x3B, 0x75, 0xB0, 0x13, 0x56,
    0x99, 0xDC, 0x07, 0x42, 0x8D, 0xC8, 0x3B, 0x71, 0xB6, 0xEB, 0x28, 0x65, 0xA2, 0x1F, 0x64, 0xA9, 0x8D, 0xD0, 0x1C,
    0x59, 0xE6, 0x23, 0x60, 0x8C, 0xD1, 0x16, 0x5B, 0x98, 0xE5, 0x23, 0x6E, 0xA5, 0xF8, 0x3F, 0x72, 0xB1, 0x0C, 0x4B,
    0x86, 0xDD, 0x03, 0x44, 0x89, 0xCA, 0x37,
    0x5F, 0x9A, 0xC9, 0x0C, 0x43, 0x86, 0xFE, 0x3B, 0x74, 0xB1, 0xD2, 0x17, 0x58, 0x9D, 0x26, 0x63, 0xAC, 0xE8, 0x3B,
    0x57, 0x9A, 0xDD, 0x20, 0x63, 0xAE, 0xE9, 0x34, 0x7F, 0xB5, 0xE9, 0x2C, 0x67, 0xA2, 0xFD, 0x38, 0x6B, 0xAE, 0x11,
    0x54, 0x98, 0xC5, 0x02, 0x4F, 0x84, 0xF9, 0x3E, 0x73, 0xB0, 0xED, 0x2A, 0x66, 0x9D, 0x20, 0x67, 0xAA, 0xE9, 0x34,
    0x73, 0x5B, 0xE8, 0x04, 0x48, 0x8F, 0xD2,
    0x11, 0x5C, 0x9B, 0xE6, 0x2D, 0x60, 0xA7, 0xFA, 0x3A, 0x77, 0xB0, 0x0D, 0x56, 0x9B, 0xDC, 0x01, 0x42, 0x8F, 0xC8,
    0x34, 0x7F, 0xB2, 0xF5, 0x0D, 0x46, 0x83, 0xFC, 0x39, 0x4A, 0x8F, 0xD1, 0x14, 0x5F, 0x9A, 0x25, 0x60, 0xA3, 0xCF,
    0x12, 0x55, 0x98, 0xE4, 0x21, 0x6E, 0xAB, 0xF8, 0x3D, 0x72, 0xB7, 0x0C, 0x49, 0x86, 0xBF, 0xE4, 0x29, 0x6E, 0xD3,
    0x10, 0x5D, 0x9A, 0xC7, 0x0C, 0x41, 0x87,
    0xFA, 0x39, 0x74, 0xB3, 0xEE, 0x15, 0x58, 0x9F, 0x22, 0x61, 0xAF, 0xE8, 0x35, 0x7E, 0x9A, 0xDF, 0x20, 0x4A, 0x89,
    0xD4, 0x13, 0x5D, 0x96, 0xEB, 0x2C, 0x61, 0xA2, 0xFF, 0x38, 0x75, 0xAE, 0x13, 0x55, 0x98, 0xDB, 0x06, 0x41, 0x8C,
    0xC7, 0x3A, 0x7D, 0xB0, 0xF3, 0x29, 0x66, 0xA3, 0xC3, 0x06, 0x49, 0x8C, 0xD7, 0x12, 0x5D, 0x98, 0x2C, 0x48, 0x8D,
    0xD2, 0x17, 0x5C, 0x99, 0xE6, 0x23, 0x60,
    0xA5, 0xFB, 0x3E, 0x75, 0xB0, 0x0F, 0x4A, 0x99, 0xDC, 0x03, 0x46, 0x68, 0xD4, 0x13, 0x5E, 0x95, 0xC8, 0x0F, 0x42,
    0x81, 0xFC, 0x3B, 0x76, 0x8E, 0xD3, 0x14, 0x59, 0x9A, 0x27, 0x60, 0xAD, 0xE6, 0x12, 0x57, 0x99, 0xDC, 0x27, 0x62,
    0xAD, 0xE8, 0x1E, 0x53, 0x94, 0xE9, 0x2A, 0x66, 0xA1, 0xFC, 0x27, 0x6A, 0xAD, 0x10, 0x53, 0x9E, 0xD9, 0x04, 0x40,
    0x85, 0xFA, 0x3F, 0x74, 0xB1, 0xEE, 0x2B,
    0x58, 0x9D, 0x22, 0x66, 0xAD, 0x95, 0xD2, 0x1F, 0x54, 0xC0, 0x05, 0x4A, 0x8F, 0xD4, 0x10, 0x5F, 0x9A, 0xE9, 0x2C,
    0x63, 0xA6, 0xFD, 0x38, 0x77, 0xB2, 0x12, 0x57, 0x98, 0xDD, 0x06, 0x43, 0x8C, 0xC9, 0x3A, 0x50, 0x97, 0xC9, 0x0A,
    0x47, 0x80, 0xFD, 0x06, 0x4B, 0x8C, 0xD1, 0x12, 0x5F, 0x99, 0x24, 0x6F, 0x8B, 0xCE, 0x11, 0x54, 0x9F, 0xDA, 0x25,
    0x60, 0xA4, 0xF9, 0x3E, 0x73, 0xB0, 0xEE,
    0x29, 0x64, 0xBF, 0xE2, 0x25, 0x6F, 0xD4, 0x11, 0x5E, 0x9B, 0xC8, 0x0D, 0x42, 0x87, 0xFC, 0x39, 0x77, 0xB2, 0xD1,
    0x14, 0x5B, 0x9E, 0x25, 0x60, 0xAF, 0xEA, 0x39, 0x56, 0x7C, 0xC3, 0x06, 0x4D, 0x88, 0xD7, 0x12, 0x51, 0x94, 0xEB,
    0x2D, 0x66, 0xA3, 0xFC, 0x39, 0x6A, 0xAF, 0x10, 0x55, 0x9E, 0xDB, 0x05, 0x40, 0x83, 0xC6, 0x39, 0x7C, 0xB7, 0xF2,
    0x08, 0x45, 0xBE, 0xC2, 0x05, 0x48, 0x8B
], np.uint8)

def xor_decode(data: Union[bytes, bytearray], key: List[int] = None, key_offset: int = 0):
    if key is None:
        key = xor_key

    if not data:
        return data
    buffer = np.frombuffer(data, np.uint8).copy()
    del data

    key_buffer = np.zeros_like(buffer)
    key_buffer[:] = key[(np.arange(buffer.shape[0]) + key_offset) & 4095]
    buffer ^= key_buffer
    return buffer.tobytes()


from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import os
import re
import math
import time
from absl import flags
from progressbar import ProgressBar
import absl.logging as _logging  # pylint: disable=unused-import
import csv

import tensorflow as tf
import model
import data_utils
from vocabulary import Vocab
from gpu_utils import assign_to_gpu, average_grads_and_vars
from postprocess import top_one_result, top_n_prob, gen_on_keyword, gen_diversity
import numpy as np
import pandas as pd
import multiprocessing
import random

# GPU config
flags.DEFINE_integer("num_core_per_host", default=8, help="Number of cores per host")
flags.DEFINE_integer("multiprocess", default=2, help="Number of processes")

# Experiment (data/checkpoint/directory) config
flags.DEFINE_string("corpus_info_path", default="", help="Path to corpus-info.json file.")
flags.DEFINE_string("model_dir", default=None, help="Estimator model_dir.")
flags.DEFINE_string("dataset", "tmall", help="Dataset name.")
flags.DEFINE_string("input_file_dir", default=None, help="Input file_dir.")
flags.DEFINE_string("output_file_dir", default=None, help="Output file_dir.")
flags.DEFINE_bool("do_sent_gen", default=False, help="Whether to generate sentence.")
flags.DEFINE_bool("do_sent_ppl_pred", default=False, help="Whether to predict sentence log probability.")
flags.DEFINE_integer("limit_len", default=50, help="Limited length of input sentence.")
flags.DEFINE_integer("gen_len", default=30, help="Number of token to generate.")

# Model config
flags.DEFINE_integer("mem_len", default=10, help="Number of steps to cache")
flags.DEFINE_bool("same_length", default=False, help="Same length attention")
flags.DEFINE_integer("clamp_len", default=-1, help="Clamp length")

flags.DEFINE_integer("n_layer", default=6, help="Number of layers.")
flags.DEFINE_integer("d_model", default=500, help="Dimension of the model.")
flags.DEFINE_integer("d_embed", default=500, help="Dimension of the embeddings.")
flags.DEFINE_integer("n_head", default=10, help="Number of attention heads.")
flags.DEFINE_integer("d_head", default=50, help="Dimension of each attention head.")
flags.DEFINE_integer("d_inner", default=1000, help="Dimension of inner hidden size in positionwise feed-forward.")
flags.DEFINE_float("dropout", default=0.1, help="Dropout rate.")
flags.DEFINE_float("dropatt", default=0.1, help="Attention dropout rate.")
flags.DEFINE_bool("untie_r", default=False, help="untie r_w_bias and r_r_bias")

# Adaptive Softmax / Embedding
flags.DEFINE_integer("div_val", default=1, help="Divide the embedding size by this val for each bin")
flags.DEFINE_bool("proj_share_all_but_first", default=False,
                  help="True to share all but first projs, False not to share.")
flags.DEFINE_bool("proj_same_dim", default=True, help="Project the bin with the same dimension.")

# Parameter initialization
flags.DEFINE_enum("init", default="normal", enum_values=["normal", "uniform"], help="Initialization method.")
flags.DEFINE_float("init_std", default=0.02, help="Initialization std when init is normal.")
flags.DEFINE_float("proj_init_std", default=0.01, help="Initialization std for embedding projection.")
flags.DEFINE_float("init_range", default=0.1, help="Initialization std when init is uniform.")

FLAGS = flags.FLAGS

def sent_gen(tmp_Vocab, input_txt, n_token, cutoffs, ps_device):

    test_list = tf.placeholder(tf.int64, shape=[1, None])
    dataset = tf.data.Dataset.from_tensors(test_list)
    # dataset = dataset.batch(1, drop_remainder=True)

    iterator = dataset.make_initializable_iterator()
    input_feed = iterator.get_next()

    inputs = tf.split(input_feed, FLAGS.num_core_per_host, 0)

    per_core_bsz = 1
    tower_mems, tower_losses, tower_new_mems = [], [], []
    tower_output = []
    tower_mems_id = []
    tower_new_mems_id = []
    tower_attn_prob = []

    for i in range(FLAGS.num_core_per_host):
        with tf.device(assign_to_gpu(i, ps_device)), \
             tf.variable_scope(tf.get_variable_scope(), reuse=tf.AUTO_REUSE):
            mems_i = [tf.placeholder(tf.float32,
                                     [FLAGS.mem_len, per_core_bsz, FLAGS.d_model])
                      for _ in range(FLAGS.n_layer)]

            mems_i_id = [tf.placeholder(tf.int64,
                                        [FLAGS.mem_len, per_core_bsz])
                         for _ in range(FLAGS.n_layer)]

            new_mems_i, output_i, new_mems_i_id, attn_prob_i = single_core_graph_for_inference(
                n_token=n_token,
                cutoffs=cutoffs,
                is_training=False,
                inp=inputs[i],
                mems=mems_i,
                mems_id=mems_i_id)

            tower_mems.append(mems_i)
            tower_new_mems.append(new_mems_i)
            tower_output.append(output_i)
            tower_mems_id.append(mems_i_id)
            tower_new_mems_id.append(new_mems_i_id)
            tower_attn_prob.append(attn_prob_i)

    # Evaluation loop
    tower_mems_np = [
        [np.zeros([FLAGS.mem_len, per_core_bsz, FLAGS.d_model], dtype=np.float32)
         for layer in range(FLAGS.n_layer)]
        for core in range(FLAGS.num_core_per_host)
    ]

    tower_mems_id_np = [
        [np.zeros([FLAGS.mem_len, per_core_bsz], dtype=np.float32)
         for layer in range(FLAGS.n_layer)]
        for core in range(FLAGS.num_core_per_host)
    ]

    saver = tf.train.Saver()

    with tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) as sess:
        sess.run(tf.global_variables_initializer())

        eval_ckpt_path = tf.train.latest_checkpoint(FLAGS.model_dir)

        saver.restore(sess, eval_ckpt_path)

        if input_txt == "":
            txt_gen = tmp_Vocab.get_sym(random.randint(3, len(tmp_Vocab.idx2sym) - 1))
        else:
            txt_gen = input_txt

        fetches = [tower_new_mems,
                   tower_output,
                   tower_new_mems_id,
                   tower_attn_prob,
                   'transformer/adaptive_embed/lookup_table:0']

        encoded_input = tmp_Vocab.encode_sents(txt_gen, ordered=True)

        progress = ProgressBar()
        for _ in progress(range(FLAGS.gen_len)):
            time.sleep(0.01)
            feed_dict = {}
            for i in range(FLAGS.num_core_per_host):
                for m, m_np in zip(tower_mems[i], tower_mems_np[i]):
                    feed_dict[m] = m_np

                for id, id_np in zip(tower_mems_id[i], tower_mems_id_np[i]):
                    feed_dict[id] = id_np

            sess.run(iterator.initializer, feed_dict={test_list: [encoded_input]})
            fetched = sess.run(fetches, feed_dict=feed_dict)

            tower_mems_np, output = fetched[:2]

            tower_mems_id_np = fetched[2]

            tmp_list = output[0][-1][0]
            tmp_list = tmp_list.tolist()

            index = top_one_result(tmp_list)

            txt_gen += tmp_Vocab.get_sym(index)
            if tmp_Vocab.get_sym(index) == "<eos>":
                break
            else:
                encoded_input = [index]

        return txt_gen

def sent_ppl(input_txt_list, n_token, cutoffs, ps_device):

    test_list = tf.placeholder(tf.int64, shape=[1, None])
    dataset = tf.data.Dataset.from_tensors(test_list)
    # dataset = dataset.batch(1, drop_remainder=True)

    iterator = dataset.make_initializable_iterator()
    input_feed = iterator.get_next()

    inputs = tf.split(input_feed, FLAGS.num_core_per_host, 0)

    per_core_bsz = 1
    tower_mems, tower_losses, tower_new_mems = [], [], []
    tower_output = []
    tower_mems_id = []
    tower_new_mems_id = []
    tower_attn_prob = []

    for i in range(FLAGS.num_core_per_host):
        with tf.device(assign_to_gpu(i, ps_device)), \
             tf.variable_scope(tf.get_variable_scope(), reuse=tf.AUTO_REUSE):
            mems_i = [tf.placeholder(tf.float32,
                                     [FLAGS.mem_len, per_core_bsz, FLAGS.d_model])
                      for _ in range(FLAGS.n_layer)]

            mems_i_id = [tf.placeholder(tf.int64,
                                        [FLAGS.mem_len, per_core_bsz])
                         for _ in range(FLAGS.n_layer)]

            new_mems_i, output_i, new_mems_i_id, attn_prob_i = single_core_graph_for_inference(
                n_token=n_token,
                cutoffs=cutoffs,
                is_training=False,
                inp=inputs[i],
                mems=mems_i,
                mems_id=mems_i_id)

            tower_mems.append(mems_i)
            tower_new_mems.append(new_mems_i)
            tower_output.append(output_i)
            tower_mems_id.append(mems_i_id)
            tower_new_mems_id.append(new_mems_i_id)
            tower_attn_prob.append(attn_prob_i)

    # Evaluation loop
    tower_mems_np = [
        [np.zeros([FLAGS.mem_len, per_core_bsz, FLAGS.d_model], dtype=np.float32)
         for layer in range(FLAGS.n_layer)]
        for core in range(FLAGS.num_core_per_host)
    ]

    tower_mems_id_np = [
        [np.zeros([FLAGS.mem_len, per_core_bsz], dtype=np.float32)
         for layer in range(FLAGS.n_layer)]
        for core in range(FLAGS.num_core_per_host)
    ]

    saver = tf.train.Saver()

    #with tf.Session(config=tf.ConfigProto(allow_soft_placement=True)) as sess:
    gpu_config = tf.ConfigProto(allow_soft_placement=True)
    gpu_config.gpu_options.allow_growth = True  # 按需分配内存
    gpu_config.gpu_options.per_process_gpu_memory_fraction = 0.2  # 限制单进程只能占用GPU显存一定比例
    with tf.Session(config=gpu_config) as sess:
        sess.run(tf.global_variables_initializer())

        eval_ckpt_path = tf.train.latest_checkpoint(FLAGS.model_dir)

        saver.restore(sess, eval_ckpt_path)

        fetches = [tower_new_mems,
                    tower_output,
                    tower_new_mems_id,
                    tower_attn_prob,
                    'transformer/adaptive_embed/lookup_table:0']

        sent_ppl_list = []

        def _cal_ppl(log_prob, sent_len):
            ppl = pow(math.exp((-1)*log_prob), 1/(sent_len-1))

            return ppl

        for i in range(len(input_txt_list)):
            #tf.logging.info('#time: {}'.format(time.time()))
            input_txt = input_txt_list[i]

            tower_mems_np = [
                [np.zeros([FLAGS.mem_len, per_core_bsz, FLAGS.d_model], dtype=np.float32)
                 for layer in range(FLAGS.n_layer)]
                for core in range(FLAGS.num_core_per_host)
            ]

            tower_mems_id_np = [
                [np.zeros([FLAGS.mem_len, per_core_bsz], dtype=np.float32)
                 for layer in range(FLAGS.n_layer)]
                for core in range(FLAGS.num_core_per_host)
            ]

            #print("Encoded Input:", encoded_input)

            log_prob = 0

            for token in range(1, len(input_txt)):
                tf.logging.info('#time: {}'.format(time.time()))
                feed_dict = {}
                for i in range(FLAGS.num_core_per_host):
                    for m, m_np in zip(tower_mems[i], tower_mems_np[i]):
                        feed_dict[m] = m_np

                    for id, id_np in zip(tower_mems_id[i], tower_mems_id_np[i]):
                        feed_dict[id] = id_np

                sess.run(iterator.initializer, feed_dict={test_list: [[input_txt[token-1]]]})
                fetched = sess.run(fetches, feed_dict=feed_dict)

                tower_mems_np, output = fetched[:2]

                tower_mems_id_np = fetched[2]

                tmp_list = output[0][-1][0]
                tmp_list = tmp_list.tolist()

                e_sum = sum([math.exp(i) for i in tmp_list])
                log_prob_list = [math.log(math.exp(i)) - math.log(e_sum) for i in tmp_list]

                log_prob = log_prob + log_prob_list[input_txt[token]]

            sent_ppl_list.append(_cal_ppl(log_prob, len(input_txt)))

        return sent_ppl_list

def single_core_graph_for_inference(n_token, cutoffs, is_training, inp, mems, mems_id):
    model_fn = get_model_fn_for_inference(
        n_token=n_token,
        cutoffs=cutoffs)

    model_ret = model_fn(
        inp=inp,
        mems=mems,
        mems_id=mems_id,
        is_training=is_training)

    return model_ret

def get_model_fn_for_inference(n_token, cutoffs):
    def model_fn(inp, mems, mems_id, is_training):
        inp = tf.transpose(inp, [1, 0])

        if FLAGS.init == "uniform":
            initializer = tf.initializers.random_uniform(
                minval=-FLAGS.init_range,
                maxval=FLAGS.init_range,
                seed=None)
        elif FLAGS.init == "normal":
            initializer = tf.initializers.random_normal(
                stddev=FLAGS.init_std,
                seed=None)
            proj_initializer = tf.initializers.random_normal(
                stddev=FLAGS.proj_init_std,
                seed=None)

        tie_projs = [False for _ in range(len(cutoffs) + 1)]
        if FLAGS.proj_share_all_but_first:
            for i in range(1, len(tie_projs)):
                tie_projs[i] = True
        new_mems, output, new_mems_id, attn_prob = model.transformer_inference(
            dec_inp=inp,
            mems=mems,
            mems_id=mems_id,
            n_token=n_token,
            n_layer=FLAGS.n_layer,
            d_model=FLAGS.d_model,
            d_embed=FLAGS.d_embed,
            n_head=FLAGS.n_head,
            d_head=FLAGS.d_head,
            d_inner=FLAGS.d_inner,
            dropout=FLAGS.dropout,
            dropatt=FLAGS.dropatt,
            initializer=initializer,
            proj_initializer=proj_initializer,
            is_training=is_training,
            mem_len=FLAGS.mem_len,
            cutoffs=cutoffs,
            div_val=FLAGS.div_val,
            tie_projs=tie_projs,
            input_perms=None,
            target_perms=None,
            head_target=None,
            same_length=FLAGS.same_length,
            clamp_len=FLAGS.clamp_len,
            use_tpu=False,
            untie_r=FLAGS.untie_r,
            proj_same_dim=FLAGS.proj_same_dim)

        # number of parameters
        num_params = sum([np.prod(v.shape) for v in tf.trainable_variables()])
        tf.logging.info('#params: {}'.format(num_params))

        return new_mems, output, new_mems_id, attn_prob

    return model_fn

def cut_pad(num_list, r_len, pad_value=0):
    if len(num_list) <= r_len:
        return num_list
    else:
        return num_list[:r_len]

def main(unused_argv):
    del unused_argv  # Unused

    tf.logging.set_verbosity(tf.logging.INFO)

    # Get corpus info
    corpus_info = data_utils.get_corpus_info(FLAGS.corpus_info_path)
    n_token = corpus_info["vocab_size"]
    cutoffs = corpus_info["cutoffs"][1:-1]
    tf.logging.info("n_token {}".format(n_token))

    tmp_Vocab = Vocab(special=["<bos>", "<eos>", "<UNK>"])
    tmp_Vocab.count_file("../data/{}/train.txt".format(FLAGS.dataset), add_eos=False)
    tmp_Vocab.build_vocab()

    if FLAGS.do_sent_ppl_pred:
        encoded_txt_input = []
        txt_input = []
        input_csv = []
        with open(FLAGS.input_file_dir, "r") as read_file:
            csv_reader = csv.reader(read_file)
            for line in csv_reader:
                if line[0].strip() != 0:
                    input_csv.append(line)

            for i in range(1, len(input_csv)):
                txt_input.append(input_csv[i][0].strip())
                encoded_txt_input.append(list(tmp_Vocab.encode_sents(input_csv[i][0].strip(), \
                    add_eos=True, ordered=True)))

        encoded_txt_input = [line[:FLAGS.limit_len] if len(line) > FLAGS.limit_len else line for line in encoded_txt_input]
        encoded_txt_input = np.array(encoded_txt_input)

        input_csv[0].append("ppl")

        pool = multiprocessing.Pool(FLAGS.multiprocess)

        parti_len = len(encoded_txt_input)//FLAGS.multiprocess
        pro_res_l = []

        for i in range(FLAGS.multiprocess):
            print("Setting process-%s" % i)
            ### 有空这里要写一个控制使用gpu:xx的步骤(gpu:1满了就用下一个)

            if i+1 == FLAGS.multiprocess:
                end = len(encoded_txt_input)
            else:
                end = (i+1)*parti_len
            pro_res_l.append(pool.apply_async(sent_ppl, \
                args=(encoded_txt_input[i*parti_len:end], n_token, cutoffs, "/gpu:1")))

        res_l = []

        for i in range(len(pro_res_l)):
            proc_i_res = pro_res_l[i].get()
            res_l.extend(proc_i_res)

        pool.close()
        pool.join()
        print('All subprocesses done.')

        tf.logging.info('#time: {}'.format(time.time()))

        for i in range(1, len(input_csv)):
            input_csv[i].append(res_l[i-1])
        output_df = pd.DataFrame(input_csv[1:], columns=input_csv[0])
        output_df.to_csv(FLAGS.output_file_dir, sep=",", index=False, encoding="utf-8-sig")

        with open("non_batch_ref_output.txt", "w") as write_res:
            for i in range(len(txt_input)):
                write_res.write(txt_input[i] + " " + str(encoded_txt_input[i]) + " " + str(res_l[i]) + "\n")

        # Check whether the length of result is right; Make sure multiprocess work well
        print(len(res_l))

    elif FLAGS.do_sent_gen:
        txt_gen_list = []
        with open(FLAGS.input_txt_dir, "r") as read_txt:
            for input_txt in read_txt:
                if len(input_txt.strip()) != 0:
                    txt_gen_list.append(sent_gen(tmp_Vocab, input_txt.strip(), n_token, cutoffs, "/gpu:1"))

        with open("sent_generation.txt", "w") as write_res:
            for line in txt_gen_list:
                write_res.write(line + "\n")

if __name__ == "__main__":
    tf.app.run()


import cv2
import tensorflow as tf
import numpy as np

tf.config.experimental.set_visible_devices([], 'GPU')

#labels
labels={0: 'angry', 1: 'disgust', 2: 'fear', 3: 'happy', 4: 'sad', 5: 'surprise', 6: 'neutral'}

#Load the model from h5 file
model=tf.keras.models.load_model('emotion.h5')

#Preprocess the image
def preprocess(img):
	img=cv2.resize(img,(64,64))
	img.astype('float32')
	img=img/255
	img=np.expand_dims(img,-1)
	img=np.expand_dims(img,0)

	return labels[np.argmax(model.predict(img))]

#Load the cascade model into cv2
cascade_path='cascade_haar.xml'
faceNet=cv2.CascadeClassifier(cascade_path)

#Video Camera object  1 for external webcam 0 for internal
cam=cv2.VideoCapture(1)

#Output image
fourcc = cv2.VideoWriter_fourcc(*'XVID')
out = cv2.VideoWriter('output.avi',fourcc, 20.0, (640,480))

while(True):
    ret,img=cam.read()

    #Convert to grayscale
    gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

    #Detect faces using HaarCascade
    faces=faceNet.detectMultiScale(gray,scaleFactor=1.3,minNeighbors=3,minSize=(64,64))

    #For each face process predictions
    for (x,y,w,h) in faces:
        cv2.rectangle(img,(x,y),(x+w,y+h),(0,0,255),2)

        #Preprocess and predict
        part=gray[y:y+h,x:x+w]
        prediction=preprocess(part)

        #Set out confidence score
        cv2.putText(img,str("Predicted: "+prediction),(x+5,y-5),cv2.FONT_HERSHEY_SIMPLEX,0.45,(0,255,0),2)

    out.write(img)
    cv2.imshow('Video',img)
    if cv2.waitKey(1) &0xFF == ord('q'):
        break

cam.release()
cv2.destroyAllWindows()