from __future__ import print_function
import numpy as np
import time
import sys
import traceback
import math
# from spacy import en
import threading
# from multiprocessing.pool import ThreadPool
import Queue
import tensorflow as tf
from itertools import compress
import cPickle as pickle

from collections import defaultdict, Counter
from lib.data.dataset_utils import print_words_with_scores, print_words_with_labels
import pdb

import logging
logger = logging.getLogger('root')

QUERY_STOP_WORDS = []
LUCENE_SPECIAL_CHARS = u"+-&|!(){}[]^\"~*?:\\"
OTHER_SPECIAL_CHARS = u"/',.@#$%"
NEG_SAMPLE_W_REVIEW = False
NEG_SAMPLE_W_NONE_ANSWER = True

data_queue = Queue.Queue(5)
rslt_queue = Queue.Queue(5)
eval_lock = threading.Lock()

def np_softmax(x, axis=0):
  """Compute softmax values for each sets of scores in x."""
  e_x = np.exp(x - np.max(x, axis=axis, keepdims=True))
  return e_x / e_x.sum(axis=axis, keepdims=True)

def f1_score(prec, reca):
  prec_x_reca = prec * reca
  if prec_x_reca == 0.0:
    f1 = 0.
  else:
    f1 = 2. * prec_x_reca / (prec + reca)
  return f1

def rerank_performance(vals, batchid, stats, at_n):
  true_answer_id = vals['data_sample_id'][batchid]
  cand_answer_id = vals['non_answer_data_sample_id'][batchid]
  pairwise_match_scores = vals['neg_pairwise_match_scores_normalized'][batchid]
  semantic_match_scores = vals['neg_semantic_match_scores_normalized'][batchid]
  ensemble_match_scores = vals['neg_ensemble_match_scores'][batchid]
  assert 'pairwise_MAP_better_cnt_at_{}'.format(at_n) in stats
  assert 'pairwise_MAP_worse_cnt_at_{}'.format(at_n) in stats
  assert 'pairwise_MAP_equal_cnt_at_{}'.format(at_n) in stats
  assert 'semantic_MAP_better_cnt_at_{}'.format(at_n) in stats
  assert 'semantic_MAP_worse_cnt_at_{}'.format(at_n) in stats
  assert 'semantic_MAP_equal_cnt_at_{}'.format(at_n) in stats
  assert 'ensemble_MAP_better_cnt_at_{}'.format(at_n) in stats
  assert 'ensemble_MAP_worse_cnt_at_{}'.format(at_n) in stats
  assert 'ensemble_MAP_equal_cnt_at_{}'.format(at_n) in stats
  assert 'MAP_na_cnt_at_{}'.format(at_n) in stats
  if len(cand_answer_id) < at_n:
    logger.warning("Candidate answer number {} is less than top-{}".format(len(cand_answer_id), at_n))

  cand_answer_id_at_n = cand_answer_id[: at_n]
  pairwise_match_scores_at_n = pairwise_match_scores[: at_n]
  semantic_match_scores_at_n = semantic_match_scores[: at_n]
  ensemble_match_scores_at_n = ensemble_match_scores[: at_n]
  error_sample_ids = set([])

  if true_answer_id in cand_answer_id_at_n:
    # CANDIDATE MAP
    true_answer_location = np.where(cand_answer_id_at_n == true_answer_id)[0][0]
    candidate_MAP = 1.0 / (1.0 + true_answer_location)
    # PAIRWISE MATCH MAP
    cnt = 0.0
    for _, s in enumerate(pairwise_match_scores_at_n):
      if pairwise_match_scores_at_n[true_answer_location] < s:
        cnt += 1.0
        error_sample_ids.add(_)
    pairwise_MAP = 1.0 / (1.0 + cnt)
    # SEMANTIC MATCH MAP
    cnt = 0.0
    for _, s in enumerate(semantic_match_scores_at_n):
      if semantic_match_scores_at_n[true_answer_location] < s:
        cnt += 1.0
        error_sample_ids.add(_)
    semantic_MAP = 1.0 / (1.0 + cnt)
    # ENSEMBLE MATCH MAP
    cnt = 0.0
    for _, s in enumerate(ensemble_match_scores_at_n):
      if ensemble_match_scores_at_n[true_answer_location] < s:
        cnt += 1.0
        error_sample_ids.add(_)
    ensemble_MAP = 1.0 / (1.0 + cnt)
    na_cnt = 0.0
  else:
    candidate_MAP = 0.0
    pairwise_MAP = 0.0
    semantic_MAP = 0.0
    ensemble_MAP = 0.0
    na_cnt = 1.0
    true_answer_location = None

  return na_cnt, candidate_MAP, pairwise_MAP, semantic_MAP, ensemble_MAP, \
      true_answer_location, error_sample_ids

def run_graph(session,
              model,
              handle,
              timer,
              is_training,
              print_qualitative,
              minibatch_max_num):
  cnt = 0
  while True:
    cnt += 1
    if minibatch_max_num is not None and cnt > minibatch_max_num:
      rslt_queue.put('DATA_END')
      break
    try:
      start_time = time.time()
      # Fetch results
      fetches = {
          'data_sample_id': model.data_sample_id,
          'non_answer_data_sample_id': model.non_answer_data_sample_id,
          'l2_regu_loss': model.l2_regu_loss,
          'focus_match_loss': model.focus_match_loss,
          'pairwise_match_loss': model.pairwise_match_loss,
          # 'semantic_match_loss': model.semantic_match_loss,
          'answer_focus_classify_accuracy': model.answer_focus_classify_accuracy,
          'answer_pairwise_classify_accuracy': model.answer_pairwise_classify_accuracy,
          # 'answer_semantic_classify_accuracy': model.answer_semantic_classify_accuracy,
          # 'answer_ensemble_classify_accuracy': model.answer_ensemble_classify_accuracy,
          'merged_summary_op': model.merged_summary_op,
          'global_step': model.global_step,
          'question_focus_attn': model.question_focus_attn}
      # fetches['question_focus_loss'] = model.question_focus_loss
      # fetches['question_focus_prec'] = model.question_focus_prec
      # fetches['question_focus_reca'] = model.question_focus_reca
      # fetches['question_focus_f1'] = model.question_focus_f1
      if print_qualitative:
        fetches['question_focus_binary_pred'] = model.question_focus_binary_pred
        fetches['pairwise_match_logits_2d'] = model.pairwise_match_logits_2d
        fetches['pairwise_match_logits_diff'] = model.pairwise_match_logits_diff
        # fetches['answer_attn'] = model.answer_attn
        # fetches['neg_pairwise_match_scores_normalized'] = model.neg_pairwise_match_scores_normalized
        fetches['neg_pairwise_match_scores_normalized'] = model.non_answer_pairwise_classify_logits
        # fetches['neg_semantic_match_scores_normalized'] = model.neg_semantic_match_scores_normalized
        # fetches['neg_ensemble_match_scores'] = model.neg_ensemble_match_scores
        fetches['question_exact_match'] = model.question_exact_match

      if is_training:
        fetches['eval_op'] = model.train_op
      # Feed inputs
      feed = {model.handle: handle}
      try:
        vals = session.run(fetches=fetches, feed_dict=feed)
      except ValueError:
        excinfo = sys.exc_info()
        traceback.print_exception(*excinfo)

      timer.append(time.time() - start_time)
      # pdb.set_trace()
      rslt_queue.put(vals)

      # if True:  # TO DEBUG DATASET SAMPLING
      #   for id, neg_ids in zip(vals['data_sample_id'], vals['non_answer_data_sample_id']):
      #     print('{0:3d} : {1}'.format(id, ' '.join(map(str, neg_ids))))

    except tf.errors.OutOfRangeError:
      rslt_queue.put('DATA_END')
      break

def eval_stat(stats,
              display_sentence,
              get_label,
              timer,
              qsize,
              summary_writer,
              query_save_file,
              eval_rerank,
              at_n_list):
  step = 0
  while True:
    qsize.append(rslt_queue.qsize())
    rslt = rslt_queue.get()
    if rslt == "DATA_END":
      rslt_queue.put("DATA_END")
      return
    else:
      start_time = time.time()
      vals = rslt

      # SUM UP METRIC VALUES
      with eval_lock:
        stats["batch"] += 1
        stats["l2_regu_losses"] += vals["l2_regu_loss"]
        # stats["question_focus_losses"] += vals["question_focus_loss"]
        # stats["question_focus_prec"] += vals["question_focus_prec"]
        # stats["question_focus_reca"] += vals["question_focus_reca"]
        # stats["question_focus_f1"] += vals["question_focus_f1"]
        stats["focus_match_losses"] += vals["focus_match_loss"]
        stats["pairwise_match_losses"] += vals["pairwise_match_loss"]
        # stats["semantic_match_losses"] += vals["semantic_match_loss"]
        stats["answer_focus_classify_accuracy"] += vals["answer_focus_classify_accuracy"]
        stats["answer_pairwise_classify_accuracy"] += vals["answer_pairwise_classify_accuracy"]
        # stats["answer_semantic_classify_accuracy"] += vals["answer_semantic_classify_accuracy"]
        # stats["answer_ensemble_classify_accuracy"] += vals["answer_ensemble_classify_accuracy"]
        if summary_writer is not None:
          summary_writer.add_summary(vals['merged_summary_op'], vals['global_step'])
        step += 1

      # SAVE QUERY
      if query_save_file is not None:
        pickle.dump((vals['data_sample_id'], vals['question_focus_attn']), query_save_file)

      # EVALUATE SEMANTIC RERANK
      candidate_MAPs, pairwise_MAPs, semantic_MAPs, ensemble_MAPs = [], [], [], []
      true_answer_location, error_sample_ids = [], []
      if eval_rerank:
        batch_size = len(vals['data_sample_id'])
        for batchid in xrange(batch_size):
          candidate_MAPs.append([])
          pairwise_MAPs.append([])
          semantic_MAPs.append([])
          ensemble_MAPs.append([])
          for _, at_n in enumerate(at_n_list):
            na_cnt, candidate_MAP, pairwise_MAP, semantic_MAP, ensemble_MAP, \
                true_ans_loc, err_ids = \
                    rerank_performance(vals, batchid, stats, at_n)
            candidate_MAPs[-1].append(candidate_MAP)
            pairwise_MAPs[-1].append(pairwise_MAP)
            semantic_MAPs[-1].append(semantic_MAP)
            ensemble_MAPs[-1].append(ensemble_MAP)
            # SAVE RESULTS
            with eval_lock:
              stats['MAP_na_cnt_at_{}'.format(at_n)] += na_cnt
              if na_cnt == 0.0:
                if pairwise_MAP > candidate_MAP:
                  stats['pairwise_MAP_better_cnt_at_{}'.format(at_n)] += 1.0
                elif pairwise_MAP < candidate_MAP:
                  stats['pairwise_MAP_worse_cnt_at_{}'.format(at_n)] += 1.0
                else:
                  stats['pairwise_MAP_equal_cnt_at_{}'.format(at_n)] += 1.0
                if semantic_MAP > candidate_MAP:
                  stats['semantic_MAP_better_cnt_at_{}'.format(at_n)] += 1.0
                elif semantic_MAP < candidate_MAP:
                  stats['semantic_MAP_worse_cnt_at_{}'.format(at_n)] += 1.0
                else:
                  stats['semantic_MAP_equal_cnt_at_{}'.format(at_n)] += 1.0
                if ensemble_MAP > candidate_MAP:
                  stats['ensemble_MAP_better_cnt_at_{}'.format(at_n)] += 1.0
                elif ensemble_MAP < candidate_MAP:
                  stats['ensemble_MAP_worse_cnt_at_{}'.format(at_n)] += 1.0
                else:
                  stats['ensemble_MAP_equal_cnt_at_{}'.format(at_n)] += 1.0
                stats['candidate_MAP_at_{}'.format(at_n)].append(candidate_MAP)
                stats['pairwise_match_MAP_at_{}'.format(at_n)].append(pairwise_MAP)
                stats['semantic_match_MAP_at_{}'.format(at_n)].append(semantic_MAP)
                stats['ensemble_match_MAP_at_{}'.format(at_n)].append(ensemble_MAP)
              stats['candidate_MAP_at_{}_all'.format(at_n)].append(candidate_MAP)
              stats['pairwise_match_MAP_at_{}_all'.format(at_n)].append(pairwise_MAP)
              stats['semantic_match_MAP_at_{}_all'.format(at_n)].append(semantic_MAP)
              stats['ensemble_match_MAP_at_{}_all'.format(at_n)].append(ensemble_MAP)
          true_answer_location.append(true_ans_loc)
          error_sample_ids.append(err_ids)

      # DISPLAY QUALITATIVE RESULTS
      # if display_sentence is not None and get_label is not None:
      if display_sentence is not None:
        # if mode == 'question_focus':
        #   question = display_sentence('question', vals['data_sample_id'], exclude_pad=False)
        #   question_label = get_label('question_focus', vals['data_sample_id'])
        #   answer = display_sentence('answer', vals['data_sample_id'], exclude_pad=False)
        #   answer_label = get_label('answer_focus', vals['data_sample_id'])
        question = display_sentence('question', vals['data_sample_id'], exclude_pad=False)
        answer = display_sentence('answer', vals['data_sample_id'], exclude_pad=False)
        # question_label = get_label('question_focus', vals['data_sample_id'])
        # answer_label = get_label('answer_focus', vals['data_sample_id'])
        question_wo_pad = display_sentence('question', vals['data_sample_id'], exclude_pad=True)
        answer_wo_pad = display_sentence('answer', vals['data_sample_id'], exclude_pad=True)

        for batchid, (q, a) in enumerate(zip(question, answer)):
          try:
            # logger.debug('\n\n')
            # q_and_s = print_words_with_scores(q,
            #                                   vals['question_exact_match'][batchid],
            #                                   # vals['question_focus_binary_pred'][batchid],
            #                                   vals['question_focus_attn'][batchid])
            # logger.debug('[Question]:\t{}'.format(q_and_s))
            # # q_and_s = print_words_with_scores(q,
            # #                                   question_label[batchid],
            # #                                   vals['question_focus_binary_pred'][batchid],
            # #                                   vals['question_focus_attn'][batchid])
            # # logger.debug('[Question]:\t{}'.format(q_and_s))
            # # a_and_s = print_words_with_scores(a,
            # #                                   # answer_label[batchid],
            # #                                   vals['answer_attn'][batchid])
            # a_and_s = a
            # logger.debug('[Answer]:\t{}'.format(a_and_s))
            # # PRINT QUESTION QUALITATIVE
            # pw_scores = vals['pairwise_match_logits_2d'][batchid]
            # logits_diff = vals['pairwise_match_logits_diff'][batchid]  # FIXME
            # top_a_word_ids_match_q = np.flip(np.argsort(pw_scores, axis=1), axis=1)
            # # for i, (qw, l, ids) in enumerate(zip(question[batchid],
            # #                                      question_label[batchid],
            # #                                      top_a_word_ids_match_q)):
            # for i, (qw, ids) in enumerate(zip(question[batchid],
            #                                   top_a_word_ids_match_q)):
            #   if qw == '<pad>': break
            #   pw_match_info = '{0: <20} :\t{1: 5.4f} * {2:5.4f} = {3: 5.4f} |'.format(
            #       qw,
            #       vals['question_focus_attn'][batchid, i],
            #       logits_diff[i],
            #       vals['question_focus_attn'][batchid, i] * logits_diff[i])
            #   for _ in xrange(5):
            #     pw_match_info += '{0: <10} {1: <5.4f} | '.format(answer[batchid][ids[_]],
            #                                                      pw_scores[i, ids[_]])
            #   logger.debug(pw_match_info)
            # # # PRINT ANSWER PAIRWISE QUALITATIVE
            # # logger.debug('----- -----')
            # # top_q_word_ids_match_a = np.flip(np.argsort(pw_scores, axis=0), axis=0)
            # # for i, (aw, l, ids) in enumerate(zip(answer[batchid],
            # #                                      answer_label[batchid],
            # #                                      np.transpose(top_q_word_ids_match_a))):
            # #   if aw == '<pad>': break
            # #   pw_match_info = '{0: <20} :\t|'.format(aw + ('*' if l else ''))
            # #   for _ in xrange(5):
            # #     pw_match_info += '{0: <10} {1: <5.4f} | '.format(question[batchid][ids[_]],
            # #                                                      pw_scores[ids[_], i])
            # #   logger.debug(pw_match_info)

            # # # PRINT MORE ANSWER PAIRWISE QUALITATIVE
            # # logger.debug('----- -----')
            # # pw_scores_weighted = pw_scores * \
            # #                      np.expand_dims(vals['question_focus_attn'][batchid], axis=1)
            # # top_q_word_ids_match_a = np.flip(np.argsort(pw_scores_weighted, axis=0), axis=0)
            # # for i, (aw, l, ids) in enumerate(zip(answer[batchid],
            # #                                      answer_label[batchid],
            # #                                      np.transpose(top_q_word_ids_match_a))):
            # #   if aw == '<pad>': break
            # #   pw_match_info = '{0: <20} :\t|'.format(aw + ('*' if l else ''))
            # #   for _ in xrange(5):
            # #     pw_match_info += '{0: <10} {1: <5.4f} | '.format(question[batchid][ids[_]],
            # #                                                      pw_scores_weighted[ids[_], i])
            # #   logger.debug(pw_match_info)

            logger.debug('[Question]:\t{}'.format(q))
            logger.debug('[Answer]:\t{}'.format(a))
            pw_scores = vals['pairwise_match_logits_2d'][batchid]
            qlen = len(question_wo_pad[batchid])
            alen = len(answer_wo_pad[batchid])
            pw_scores = pw_scores[:qlen, :alen] + 10.0
            # pw_scores_normalized = pw_scores / pw_scores.sum(axis=1, keepdims=True)
            pw_scores_normalized = np_softmax(pw_scores, axis=1)

            # PRINT PAIRWISE INFORMATION (HEATMAP IF VISUALIZED)
            # pdb.set_trace()
            display_str = "\n{0:10} ".format("")
            for qw in question_wo_pad[batchid]:
              display_str += "{0:>10} ".format(qw[:10])
            display_str += "\n"
            for i, aw in enumerate(answer_wo_pad[batchid]):
              display_str += "{0:>10} ".format(aw[:10])
              for j in xrange(len(question_wo_pad[batchid])):
                display_str += "{0:10.5f} ".format(pw_scores_normalized[j, i])
              display_str += "\n"
            logger.debug(display_str)

            # PRINT RANKING QUALITATIVE
            if eval_rerank:
              non_answers = display_sentence('answer', vals['non_answer_data_sample_id'][batchid],
                                             exclude_pad=True)
              cnt = 0
              # for _, (na, s1, s2, s3) in enumerate(zip(
              #     non_answers,
              #     vals['neg_pairwise_match_scores_normalized'][batchid],
              #     vals['neg_semantic_match_scores_normalized'][batchid],
              #     vals['neg_ensemble_match_scores'][batchid]
              #     )):
              for _, (na, s1) in enumerate(zip(non_answers, vals['neg_pairwise_match_scores_normalized'][batchid])):
                if _ > 10 and \
                   _ != true_answer_location[batchid] and \
                   _ not in error_sample_ids[batchid]:
                  continue
                # logger.debug('{0:.5f} {1:.5f} {2:.5f} | {3}'.format(s1, s2, s3, u' '.join(na)))
                logger.debug('{0:.5f} | {3}'.format(s1, u' '.join(na)))

              for _, at_n in enumerate(at_n_list):
                # logger.debug('MAP at {0}: '.format(at_n) +
                #              'candidate {0:.5f}; pairwise {1:.5f}; semantic {2:.5f}; ensemble {3:.5f}'.format(
                #                  candidate_MAPs[batchid][_], pairwise_MAPs[batchid][_],
                #                  semantic_MAPs[batchid][_], ensemble_MAPs[batchid][_]))
                logger.debug('MAP at {0}: '.format(at_n) +
                             'candidate {0:.5f}; pairwise {1:.5f}'.format(
                                 candidate_MAPs[batchid][_], pairwise_MAPs[batchid][_])),

          except UnicodeError:
            excinfo = sys.exc_info()
            logger.debug('Unicode error: {}'.format(excinfo[1]))
          except ValueError:
            excinfo = sys.exc_info()
            traceback.print_exception(*excinfo)
            pdb.set_trace()
          except IndexError:
            excinfo = sys.exc_info()
            traceback.print_exception(*excinfo)
            pdb.set_trace()
          except:
            traceback.print_exc()
            pdb.set_trace()

      timer.append(time.time() - start_time)

def run_epoch(session,
              model,
              data_handle,  # return a data generator
              display_sentence=None,
              get_label=None,
              is_training=False,
              query_save_file=None,
              summary_writer=None,
              minibatch_max_num=None,
              eval_rerank=False,
              at_n_list=None):
  """Runs the model on the given data."""
  start_time = time.time()
  stats = {"batch": 0.0,
           "l2_regu_losses": 0.0,
           "question_focus_losses": 0.0,
           "question_focus_prec": 0.0,
           "question_focus_reca": 0.0,
           "question_focus_f1": 0.0,
           #
           "focus_match_losses": 0.0,
           "pairwise_match_losses": 0.0,
           "semantic_match_losses": 0.0,
           "answer_focus_classify_accuracy": 0.0,
           "answer_pairwise_classify_accuracy": 0.0,
           "answer_semantic_classify_accuracy": 0.0,
           "answer_ensemble_classify_accuracy": 0.0}
  if eval_rerank:
    assert at_n_list
    for at_n in at_n_list:
      stats["candidate_MAP_at_{}".format(at_n)] = []
      stats["pairwise_match_MAP_at_{}".format(at_n)] = []
      stats["semantic_match_MAP_at_{}".format(at_n)] = []
      stats["ensemble_match_MAP_at_{}".format(at_n)] = []
      stats["candidate_MAP_at_{}_all".format(at_n)] = []
      stats["pairwise_match_MAP_at_{}_all".format(at_n)] = []
      stats["semantic_match_MAP_at_{}_all".format(at_n)] = []
      stats["ensemble_match_MAP_at_{}_all".format(at_n)] = []
      stats["pairwise_MAP_better_cnt_at_{}".format(at_n)] = 0.0
      stats["pairwise_MAP_worse_cnt_at_{}".format(at_n)] = 0.0
      stats["pairwise_MAP_equal_cnt_at_{}".format(at_n)] = 0.0
      stats["semantic_MAP_better_cnt_at_{}".format(at_n)] = 0.0
      stats["semantic_MAP_worse_cnt_at_{}".format(at_n)] = 0.0
      stats["semantic_MAP_equal_cnt_at_{}".format(at_n)] = 0.0
      stats["ensemble_MAP_better_cnt_at_{}".format(at_n)] = 0.0
      stats["ensemble_MAP_worse_cnt_at_{}".format(at_n)] = 0.0
      stats["ensemble_MAP_equal_cnt_at_{}".format(at_n)] = 0.0
      stats["MAP_na_cnt_at_{}".format(at_n)] = 0.0

  run_graph_timer, eval_stat_timers, eval_stat_qsizes = [], [], []

  # Run tensorflow graph in a separate thread
  # if display_sentence is None or get_label is None:
  if display_sentence is None:
    print_qualitative = False
  else:
    print_qualitative = True
  run_graph_thread = threading.Thread(
      target=run_graph,
      args=(session,
            model,
            data_handle,
            run_graph_timer,
            is_training,
            print_qualitative,
            minibatch_max_num
            ))
  run_graph_thread.start()

  # Evaluation in other threads
  if display_sentence is not None:  # print qualitative results
    eval_thread_num = 1
  else:
    eval_thread_num = 4
  eval_stat_threads = []
  for _ in xrange(eval_thread_num):
    eval_stat_timers.append([])
    eval_stat_qsizes.append([])
    eval_stat_threads.append(threading.Thread(
        target=eval_stat,
        args=(stats,
              display_sentence,
              get_label,
              eval_stat_timers[_],
              eval_stat_qsizes[_],
              summary_writer,
              query_save_file,
              eval_rerank,
              at_n_list)))
  for eval_stat_thread in eval_stat_threads:
    eval_stat_thread.start()

  # Threading barrier
  run_graph_thread.join()
  for eval_stat_thread in eval_stat_threads:
    eval_stat_thread.join()
  assert (rslt_queue.qsize() == 1)
  rslt = rslt_queue.get()
  assert (rslt == "DATA_END")

  eval_stat_timer = [t for est in eval_stat_timers for t in est]
  eval_stat_qsize = [q for esq in eval_stat_qsizes for q in esq]

  logger.info("Time : %.0f seconds" % (time.time() - start_time))
  assert (len(run_graph_timer) == len(eval_stat_timer))
  logger.info("Time run graph : %f" % (np.mean(run_graph_timer)))
  logger.info("Time eval stat : %f, qsize : %.3f" % (np.mean(eval_stat_timer), np.mean(eval_stat_qsize)))
  rslt = {
      "l2_regu_loss": (stats["l2_regu_losses"] / stats["batch"]),
      "question_focus_loss": (stats["question_focus_losses"] / stats["batch"]),
      "question_focus_prec": (stats["question_focus_prec"] / stats["batch"]),
      "question_focus_reca": (stats["question_focus_reca"] / stats["batch"]),
      "question_focus_f1": (stats["question_focus_f1"] / stats["batch"]),
      #
      "focus_match_loss": (stats["focus_match_losses"] / stats["batch"]),
      "pairwise_match_loss": (stats["pairwise_match_losses"] / stats["batch"]),
      "semantic_match_loss": (stats["semantic_match_losses"] / stats["batch"]),
      "answer_focus_classify_accuracy": (stats["answer_focus_classify_accuracy"] / stats["batch"]),
      "answer_pairwise_classify_accuracy": (stats["answer_pairwise_classify_accuracy"] / stats["batch"]),
      "answer_semantic_classify_accuracy": (stats["answer_semantic_classify_accuracy"] / stats["batch"]),
      "answer_ensemble_classify_accuracy": (stats["answer_ensemble_classify_accuracy"] / stats["batch"])}

  # The following metrics evaluated how the reranking answer candidates improve the final results
  if eval_rerank:
    for at_n in at_n_list:
      rslt["candidate_MAP_at_{}".format(at_n)] = np.mean(stats["candidate_MAP_at_{}".format(at_n)])
      rslt["pairwise_match_MAP_at_{}".format(at_n)] = np.mean(stats["pairwise_match_MAP_at_{}".format(at_n)])
      rslt["semantic_match_MAP_at_{}".format(at_n)] = np.mean(stats["semantic_match_MAP_at_{}".format(at_n)])
      rslt["ensemble_match_MAP_at_{}".format(at_n)] = np.mean(stats["ensemble_match_MAP_at_{}".format(at_n)])
      rslt["candidate_MAP_at_{}_all".format(at_n)] = np.mean(stats["candidate_MAP_at_{}_all".format(at_n)])
      rslt["pairwise_match_MAP_at_{}_all".format(at_n)] = np.mean(stats["pairwise_match_MAP_at_{}_all".format(at_n)])
      rslt["semantic_match_MAP_at_{}_all".format(at_n)] = np.mean(stats["semantic_match_MAP_at_{}_all".format(at_n)])
      rslt["ensemble_match_MAP_at_{}_all".format(at_n)] = np.mean(stats["ensemble_match_MAP_at_{}_all".format(at_n)])
      rslt["pairwise_MAP_better_cnt_at_{}".format(at_n)] = stats["pairwise_MAP_better_cnt_at_{}".format(at_n)]
      rslt["pairwise_MAP_worse_cnt_at_{}".format(at_n)] = stats["pairwise_MAP_worse_cnt_at_{}".format(at_n)]
      rslt["pairwise_MAP_equal_cnt_at_{}".format(at_n)] = stats["pairwise_MAP_equal_cnt_at_{}".format(at_n)]
      rslt["semantic_MAP_better_cnt_at_{}".format(at_n)] = stats["semantic_MAP_better_cnt_at_{}".format(at_n)]
      rslt["semantic_MAP_worse_cnt_at_{}".format(at_n)] = stats["semantic_MAP_worse_cnt_at_{}".format(at_n)]
      rslt["semantic_MAP_equal_cnt_at_{}".format(at_n)] = stats["semantic_MAP_equal_cnt_at_{}".format(at_n)]
      rslt["ensemble_MAP_better_cnt_at_{}".format(at_n)] = stats["ensemble_MAP_better_cnt_at_{}".format(at_n)]
      rslt["ensemble_MAP_worse_cnt_at_{}".format(at_n)] = stats["ensemble_MAP_worse_cnt_at_{}".format(at_n)]
      rslt["ensemble_MAP_equal_cnt_at_{}".format(at_n)] = stats["ensemble_MAP_equal_cnt_at_{}".format(at_n)]
      rslt["MAP_na_cnt_at_{}".format(at_n)] = stats["MAP_na_cnt_at_{}".format(at_n)]

  return rslt


import numpy as np
import sys
import time
import itertools

def solve(M):
    n, _ = M.shape
    nodes = list(range(n))
    X = ring(nodes)
    best_matrix = X
    best_score = score(M, X)
    finish = False
    steps = 1
    while not finish:
        finish = True
        for pair in itertools.combinations(nodes, 2):
            copy = nodes.copy()
            i = pair[0]
            j = pair[1]
            copy[i], copy[j] = copy[j], copy[i]
            X = ring(copy)
            f = score(M, X)
            steps += 1
            if f < best_score:
                best_score = f
                best_matrix = X
                nodes = copy
                finish = False
                break
    return best_matrix, best_score, steps

in_file = sys.argv[1]
out_file = sys.argv[2]

M = np.loadtxt(in_file)
tic = time.perf_counter()
X, best_score, steps = solve(M)
toc = time.perf_counter()

def ring(nodes):
    n = len(nodes)
    A = np.zeros((n,n))
    for i in range(n):
        j = (i + 1) % n
        u = nodes[i]
        v = nodes[j]
        A[u,v] = A[v,u] = 1
    return A

def score(M, X):
    return np.sum(np.multiply(M, X))

with open(out_file, 'w') as f:
    f.write("Score: {0}\n".format(best_score))
    f.write("Solution:\n {0}\n".format(X))
    f.write("Steps: {0}\n".format(steps))
    f.write("Time: {0:0.4f} s\n".format(toc - tic))


import glob
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
import os
import subprocess
import minepy as mp
import argparse
import shutil
import time
import matplotlib.pyplot as plt

def stand_sca(data):
    """
    标准差标准化
    :param data:传入的数据
    :return:标准化之后的数据
    """
    new_data=(data-data.mean())/data.std()
    return new_data

'''
@写libsvm数据
    write_data_sns(classifier, df, feature)
    write_subdata_sns(classifier, df, feature)
    write_data_hsvs(classifier, df, feature)
    write_subdata_hsvs(classifier, df, feature)
    writesns(classifier, feature)
    write2hsvs(classifier, feature)
'''

def write_data_sns(classifier, df, feature):

    if not os.path.exists('libsvmdata/' + classifier):
        os.mkdir('libsvmdata/' + classifier)

    data_file = open('libsvmdata/'  + classifier + '/test' + '.txt', 'w')

    y = df.loc[:, 'mode']
    X = df.loc[:, feature]
    X = stand_sca(X) #标准化

    X, _, y, _ = train_test_split(X, y, train_size=10000, stratify=y)

    for i in X.index:
        label = y[i]
        if(label == 2000):
            label = 0
        else:
            label = 1
        data_file.write(str(label) + ' ')
        n = 0
        for m in feature:
            data_file.write(str(n+1) + ':' + str(X.loc[i, m]) + ' ')
            n = n + 1
        data_file.write('\n')
    data_file.close()

def write_subdata_sns(classifier, df, feature):
    X = df.loc[:, feature]
    y = df.loc[:, 'mode']
    X = stand_sca(X) #标准化

    if not os.path.exists('libsvmdata/' + classifier):
        os.mkdir('libsvmdata/' + classifier)
    for i in range(30):
        X_train, _, y_train, _ = train_test_split(X, y, train_size=200, stratify=y)
        data_file = open('libsvmdata/'  + classifier + '/train' + str(i) + '.txt', 'w')
        for m in X_train.index:
            label = y_train[m]
            if(label == 2000):
                label = 0
            else:
                label = 1
            data_file.write(str(label) + ' ')
            n = 0
            for k in feature:
                data_file.write(str(n+1) + ':' + str(X_train.loc[m, k]) + ' ')
                n = n + 1
            data_file.write('\n')
        data_file.close()

def write_data_hsvs(classifier, df, feature):

    if not os.path.exists('libsvmdata/' + classifier):
        os.mkdir('libsvmdata/' + classifier)

    y = df.loc[:, 'mode']
    X = df.loc[:, feature]
    X = stand_sca(X) #标准化
    X, _, y, _ = train_test_split(X, y, train_size=10000, stratify=y)
    data_file = open('libsvmdata/'  + classifier + '/test' + '.txt', 'w')

    for i in X.index:
        data_file.write(str(df.loc[i, 'mode']) + ' ')
        n = 0
        for m in feature:
            data_file.write(str(n+1) + ':' + str(X.loc[i, m]) + ' ')
            n = n + 1
        data_file.write('\n')
    data_file.close()

def write_subdata_hsvs(classifier, df, feature):
    X = df.loc[:, feature]
    y = df.loc[:, 'mode']
    X = stand_sca(X) #标准化

    if not os.path.exists('libsvmdata/' + classifier):
        os.mkdir('libsvmdata/' + classifier)
    for i in range(30):
        X_train, _, y_train, _ = train_test_split(X, y, train_size=200, stratify=y)
        data_file = open('libsvmdata/'  + classifier + '/train' + str(i) + '.txt', 'w')
        for m in X_train.index:
            data_file.write(str(y_train[m]) + ' ')
            n = 0
            for k in feature:
                data_file.write(str(n+1) + ':' + str(X_train.loc[m, k]) + ' ')
                n = n + 1
            data_file.write('\n')
        data_file.close()

def writesns(classifier, feature):
    path = "csv_data/" + classifier + "_0.csv"
    df = pd.read_csv(path)
    df['ngrad'] = df.loc[:, 'ngradx'] + df.loc[:, 'ngrady']
    df['MaxDiffgrad'] = df.loc[:, 'MaxDiffgradx'] + df.loc[:, 'MaxDiffgrady']
    df['Inconsgradh'] = df.loc[:, 'Inconsgradxh'] + df.loc[:, 'Inconsgradyh']
    df['Inconsgradv'] = df.loc[:, 'Inconsgradxv'] + df.loc[:, 'Inconsgradyv']
    write_subdata_sns(classifier, df, feature)
    write_data_sns(classifier, df, feature)

def writehsvs(classifier, feature):
    path = "csv_data/" + classifier + "_0.csv"
    df = pd.read_csv(path)
    write_subdata_hsvs(classifier, df, feature)
    write_data_hsvs(classifier, df, feature)

def cal_mean_std_sns(classifier, feature):
    print(classifier)
    path = "csv_data/" + classifier + "_0.csv"
    df = pd.read_csv(path)
    mea = np.array(df[feature].mean())
    st = np.array(df[feature].std())
    print(classifier + 'mean = {', mea[0], ',', mea[1], ',', mea[2],',', mea[3],',', mea[4],',', mea[5],',', mea[6],',', mea[7],'}')
    print(classifier + 'std = {', st[0], ',', st[1], ',', st[2],',', st[3],',', st[4],',', st[5],',', st[6],',', st[7],'}')

def cal_mean_std_hsvs(classifier, feature):
    print(classifier)
    path = "csv_data/" + classifier + "_0.csv"
    df = pd.read_csv(path)
    mea = np.array(df[feature].mean())
    st = np.array(df[feature].std())
    print(classifier + 'mean = {', mea[0], ',', mea[1], ',', mea[2],',', mea[3],',', mea[4],',', mea[5], '}')
    print(classifier + 'std = {', st[0], ',', st[1], ',', st[2],',', st[3],',', st[4],',', st[5],'}')

'''
Run libsvm
    run_ParaSearch(exe, train_file, test_file, model_file, predict_file, log_file)
    run_th(exe, test_file, model_file, predict_file, log_file)
    train_sub_set(exe, classifier)
    select_th(exe, classifier)
'''

def run_predict(exe, test_file, model_file, predict_file, log_file):

    cmd = exe + ' -b 1 ' + test_file + ' ' + model_file + ' ' + predict_file + ' > ' + log_file
    subprocess.Popen(cmd, shell=True)

def run_ParaSearch(exe, train_file, test_file, model_file, predict_file, log_file):

    cmd = exe + ' -i ' + train_file + ' -e ' + test_file + ' -m ' + model_file + ' -p ' + predict_file + ' > ' + log_file
    subprocess.Popen(cmd, shell=True)

def run_th(exe, test_file, model_file, predict_file, log_file):

    cmd = exe + ' -e ' + test_file + ' -m ' + model_file + ' -p ' + predict_file + ' > ' + log_file
    subprocess.Popen(cmd, shell=True)

def train_sub_set(exe, classifier):

    path = 'libsvmdata/' + classifier + '/'
    log_path = path + 'log/'

    if not os.path.exists(log_path):
        os.mkdir(log_path)

    test_file = path + 'test.txt'
    for i in range(30):
        train_file = path + 'train' + str(i) + '.txt'
        model_file = log_path + 'model' + str(i) + '.txt'
        predict_file = log_path + 'predict' + str(i) + '.txt'
        log_file = log_path + 'log' + str(i) + '.txt'
        run_ParaSearch(exe, train_file, test_file, model_file, predict_file, log_file)

def select_th(exe, classifier):

    path = 'libsvmdata/' + classifier + '/'
    log_path = path + 'log/'
    model_path = 'model/'

    if not os.path.exists(log_path):
        os.mkdir(log_path)

    test_file = path + 'test.txt'
    model_file = model_path + classifier + '.txt'
    predict_file = log_path + 'predict' + '.txt'
    log_file = log_path + 'log' + '.txt'
    run_th(exe, test_file, model_file, predict_file, log_file)

def read_log(logs_path):

    output_path = 'model/'
    classifer_path = os.listdir(logs_path)
    row = 1
    for classifer in classifer_path:
        #print(classifer)
        logs = os.listdir(logs_path + classifer+'/log/')
        max_acc = 0
        max_seq = 0
        for log_name in logs:
            if log_name.startswith('log') and not log_name.startswith('log.txt'):
                _, num = log_name.split('log')
                num, _ = num.split('.txt')
                file = open(logs_path + classifer + '/log/' + log_name, 'r')
                line = ' '
                while not line.startswith('Summary:'):
                    line = file.readline()
                line = file.readline()
                line = line.strip()
                before, _ = line.split('%')
                _, acc = before.split('=')
                acc = acc.strip()
                if float(acc) > max_acc:
                    max_acc =  float(acc)
                    max_seq = num
        print(classifer + ': ', max_acc, max_seq)
        shutil.copyfile(logs_path + classifer + '/log/model' + str(max_seq) + '.txt', output_path + classifer + '.txt')

def readth_log(logs_path):

    classifer_path = os.listdir(logs_path)
    row = 1
    df = pd.DataFrame()
    for classifer in classifer_path:
        #print(classifer)
        logs = os.listdir(logs_path + classifer+'/log/')
        th = []
        acc = []
        for log_name in logs:
            if log_name.startswith('log.txt'):
                file = open(logs_path + classifer + '/log/' + log_name, 'r')
                line = ' '
                while not line.startswith('th = 1'):
                    if line.startswith('th'):
                        line = line.strip()
                        before, _ = line.split('%')
                        before, ac = before.split('Accuracy = ')
                        _, t = before.split('= ')
                        acc.append(float(ac.strip()))
                        th.append(float(t.strip()))
                        #print(line)
                    line = file.readline()
                #print(line)
        df['th'] = th
        df[classifer] = acc
    #print(df)
    #plt.figure(num='th')
    fig, ax = plt.subplots()
    #ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_64x64'], label = '64x64', linewidth=2.0)
    ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_32x32'], label = '32x32', linewidth=2.0)
    ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_16x16'], label = '16x16', linewidth=2.0)
    #ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_8x8'], label = '8x8', linewidth=2.0)
    ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_32x16'], label = '32x16', linewidth=2.0)
    ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_32x8'], label = '32x8', linewidth=2.0)
    #ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_32x4'], label = '32x4', linewidth=2.0)
    ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_16x8'], label = '16x8', linewidth=2.0)
    #ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_16x4'], label = '16x4', linewidth=2.0)
    #ax.plot(df.loc[:, 'th'], df.loc[:, 's-ns_8x4'], label = '8x4', linewidth=2.0)

    #df.plot.line(x= 'th', y='s-ns_64x64')
    ax.legend(fontsize=15)

    plt.xlim((0.5, 1.01))
    plt.ylim((70, 101))
    #设置坐标轴名称
    plt.xlabel('Threshold',fontsize=15)
    plt.ylabel('Accuracy (%)',fontsize=15)

    #设置坐标轴刻度
    my_x_ticks = np.arange(0.5, 1.01, 0.1)
    my_y_ticks = np.arange(70, 101, 5)
    plt.xticks(my_x_ticks,fontsize=15)
    plt.yticks(my_y_ticks,fontsize=15)
    plt.grid()
    plt.savefig(fname = 'snsth.png', dpi=500)
    #print(df)

    fig, ax2 = plt.subplots()
    ax2.plot(df.loc[:, 'th'], df.loc[:, 'hs-vs_32x32'], label = '32x32', linewidth=2.0)
    ax2.plot(df.loc[:, 'th'], df.loc[:, 'hs-vs_16x16'], label = '16x16', linewidth=2.0)
    ax2.plot(df.loc[:, 'th'], df.loc[:, 'hs-vs_32x16'], label = '32x16', linewidth=2.0)
    ax2.plot(df.loc[:, 'th'], df.loc[:, 'hs-vs_32x8'], label = '32x8', linewidth=2.0)
    ax2.plot(df.loc[:, 'th'], df.loc[:, 'hs-vs_16x8'], label = '16x8', linewidth=2.0)
    #ax2.plot(df.loc[:, 'th'], df.loc[:, 'hs-vs_8x8'], label = '8x8', linewidth=2.0)

    #df.plot.line(x= 'th', y='s-ns_64x64')
    ax2.legend(fontsize=15)

    plt.xlim((0.5, 1.01))
    plt.ylim((65, 101))
    #设置坐标轴名称
    plt.xlabel('Threshold', fontsize=15)
    plt.ylabel('Accuracy (%)', fontsize=15)

    #设置坐标轴刻度
    my_x_ticks = np.arange(0.5, 1.01, 0.1)
    my_y_ticks = np.arange(65, 101, 5)
    plt.xticks(my_x_ticks, fontsize=15)
    plt.yticks(my_y_ticks, fontsize=15)
    plt.grid()
    plt.savefig(fname = 'hsvsth.png', dpi=500)

def read_csv_data(read_path):

    read_csv = glob.glob(os.path.join(read_path,'*.csv')) # read the address of every csv file

    df = None
    seq_name = []

    for i, path in enumerate(read_csv):     #  Loop reading every csv file

        before, file_name = path.split(read_path)
        file_name, csv = file_name.split('.')
        seq_name.append(file_name)

        month = pd.read_csv(path)
        if df is None:          # if df is empty
            df = month
        else:
            df = pd.concat([df,month], ignore_index = True)  # emerge every csv data

    return df, seq_name

def balance_set(set1, set2, size = 0):

    # balance trainning data set
    if size > set1.shape[0] or size > set2.shape[0]:
        print("the size is too large")
        return

    if size == 0:
        if set1.shape[0] < set2.shape[0]:
            set2 = set2.sample(n=int(len(set1)), replace=False, axis=0)
        if set1.shape[0] > set2.shape[0]:
            set1 = set1.sample(n=int(len(set2)), replace=False, axis=0)
    else:
        set1 = set1.sample(n=size, replace=False, axis=0)
        set2 = set2.sample(n=size, replace=False, axis=0)
    df = pd.concat([set2, set1], axis=0)

    return df

def save_block_set_sns(df, w, h, file_name, data_size = 0, isbalance_set = True):
    df = df[df['w'] == w]
    df = df[df['h'] == h]

    if isbalance_set:
        if data_size == 0:
            df = balance_set(df[df['mode'] == 2000], df[df['mode'] != 2000])
        else:
            df = balance_set(df[df['mode'] == 2000], df[df['mode'] != 2000], data_size)
    file_name = file_name + '_' + str(w) + 'x' +str(h) + '_' +str(data_size) + '.csv'
    df.to_csv('csv_data/' + file_name)

def save_block_set_hsvs(df, w, h, file_name, data_size = 0, isbalance_set = True):
    df = df[df['w'] == w]
    df = df[df['h'] == h]

    for i in df.index:
        if df.loc[i, 'mode'] == 2 or df.loc[i, 'mode'] == 4:
            df.loc[i, 'mode'] = 100  # HS
        if df.loc[i, 'mode'] == 3 or df.loc[i, 'mode'] == 5:
            df.loc[i, 'mode'] = 200  # VS

    if isbalance_set:
        if data_size == 0:
            df = balance_set(df[df['mode'] == 100], df[df['mode'] == 200])
        else:
            df = balance_set(df[df['mode'] == 100], df[df['mode'] == 200], data_size)
    file_name = file_name + '_' + str(w) + 'x' +str(h) + '_' +str(data_size) + '.csv'
    df.to_csv('csv_data/' + file_name)

def mic(x, y):

    # calculate the maximal information coefficient

    x = np.array(x)
    y = np.array(y)
    mine = mp.MINE(alpha=0.6, c=15, est="mic_approx")
    mine.compute_score(x, y)

    return mine.mic()

def feature_train(classifier, feature):

    if classifier.startswith('s'):
        writesns(classifier, feature)
    else:
        writehsvs(classifier, feature)
    print(feature)

    ParaSearchs = 'libsvmexe/ParaSs'
    train_sub_set(ParaSearchs, classifier)
    time.sleep(5)
    while 1:
        file_handle = os.popen('ps -a | grep ' + 'ParaSs' + '| wc -l')
        Numproc = int(file_handle.read())
        if Numproc == 0:
            break
        time.sleep(1)
    #read_log('libsvmdata/')

###################################################################################################################################

# 保存块data sns
if 0:
    read_path = 'train/'      # the path of csv file
    df, seq_name = read_csv_data(read_path)

    df = df[df['chType'] == 0]

    save_block_set_sns(df, 64, 64,'s-ns')
    save_block_set_sns(df, 32, 32,'s-ns')
    save_block_set_sns(df, 16, 16,'s-ns')
    save_block_set_sns(df, 8, 8,'s-ns')
    save_block_set_sns(df, 32, 16,'s-ns')
    save_block_set_sns(df, 32, 8,'s-ns')
    save_block_set_sns(df, 32, 4,'s-ns')
    save_block_set_sns(df, 16, 8,'s-ns')
    save_block_set_sns(df, 16, 4,'s-ns')
    save_block_set_sns(df, 8, 4,'s-ns')

# 保存块data hsvs
if 0:
    read_path = 'train/'      # the path of csv file
    df, seq_name = read_csv_data(read_path)

    df = df[df['chType'] == 0]

    save_block_set_hsvs(df, 32, 32,'hs-vs')
    save_block_set_hsvs(df, 16, 16,'hs-vs')
    save_block_set_hsvs(df, 8, 8,'hs-vs')
    save_block_set_hsvs(df, 32, 16,'hs-vs')
    save_block_set_hsvs(df, 32, 8,'hs-vs')
    save_block_set_hsvs(df, 16, 8,'hs-vs')

# 计算 mic
if 0:
    train_set_path = 'csv_data/hs-vs_16x8_0.csv'
    print(train_set_path)
    df = pd.read_csv(train_set_path)

    y = df.loc[:, 'mode']

    feature = ['qp', 'var', 'ndvarh','ndvarv','ndva','MaxDiffVar','InconsVarH','InconsVarV','ngradx','ngrady','ndgradxh','ndgradxv','ndgradyh','ndgradyv','ndgradx','ndgrady','gmx']
    for f in feature:
        print(f + ": ", mic(y, df.loc[:, f]))

# 计算 mic 标准化
if 0:
    parser = argparse.ArgumentParser(
      formatter_class=argparse.ArgumentDefaultsHelpFormatter)
    parser.add_argument("--classifier", default='s-ns_64x64')
    args = parser.parse_args()
    classifier = args.classifier
    train_set_path = 'csv_data/' + classifier + '_0.csv'
    print(train_set_path)
    df = pd.read_csv(train_set_path)

    y = df.loc[:, 'mode']
    if classifier.startswith('s-ns'):
        y[y != 2000] = 1
        y[y == 2000] = 0
    df['ngrad'] = df.loc[:, 'ngradx'] + df.loc[:, 'ngrady']
    df['MaxDiffgrad'] = df.loc[:, 'MaxDiffgradx'] + df.loc[:, 'MaxDiffgrady']
    df['Inconsgradh'] = df.loc[:, 'Inconsgradxh'] + df.loc[:, 'Inconsgradyh']
    df['Inconsgradv'] = df.loc[:, 'Inconsgradxv'] + df.loc[:, 'Inconsgradyv']
    df = stand_sca(df)

    feature = ['qp', 'var','ngradx','ngrady','gmx','ndva', 'ndgradx','ndgrady','MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady']
    for f in feature:
        print(f + ": ", mic(y, df.loc[:, f]))

# 写libsvm data sns
if 0:
    # writesns('s-ns_64x64', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    writesns('s-ns_32x32', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # writesns('s-ns_16x16', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    # # writesns('s-ns_8x8', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    # writesns('s-ns_32x16', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    # writesns('s-ns_32x8', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    # # writesns('s-ns_32x4', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    # writesns('s-ns_16x8', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    # writesns('s-ns_16x4', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])
    # writesns('s-ns_8x4', ['qp', 'var', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx', 'ngrad', 'MaxDiffgrad', 'Inconsgradh', 'Inconsgradv'])

# 写libsvm data hsvs
if 0:
    writehsvs('hs-vs_32x32', ['qp', 'ndva', 'ngradx', 'ngrady', 'ndgradx', 'ndgrady'])
    # writehsvs('hs-vs_16x16', ['qp', 'ndva', 'ngradx', 'ngrady', 'ndgradx2', 'ndgrady2'])
    # writehsvs('hs-vs_8x8', ['qp', 'ndva', 'ngradx', 'ngrady', 'ndgradx2', 'ndgrady2'])
    # writehsvs('hs-vs_32x16', ['qp', 'ndva', 'ngradx', 'ngrady', 'ndgradx2', 'ndgrady2'])
    # writehsvs('hs-vs_32x8', ['qp', 'ndva', 'ngradx', 'ngrady', 'ndgradx2', 'ndgrady2'])
    # writehsvs('hs-vs_16x8', ['qp', 'ndva', 'ngradx', 'ngrady', 'ndgradx2', 'ndgrady2'])

# 计算均值和方差 sns
if 0:
    # cal_mean_std_sns('s-ns_64x64', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # cal_mean_std_sns('s-ns_32x32', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # cal_mean_std_sns('s-ns_16x16', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # cal_mean_std_sns('s-ns_32x16', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # cal_mean_std_sns('s-ns_32x8', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # cal_mean_std_sns('s-ns_16x8', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    cal_mean_std_sns('s-ns_8x8', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    cal_mean_std_sns('s-ns_32x4', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    cal_mean_std_sns('s-ns_16x4', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    cal_mean_std_sns('s-ns_8x4', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])

# 计算均值和方差 hsvs
if 0:
    # cal_mean_std_hsvs('hs-vs_32x32', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # cal_mean_std_hsvs('hs-vs_16x16', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # cal_mean_std_hsvs('hs-vs_32x16', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # cal_mean_std_hsvs('hs-vs_32x8', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # cal_mean_std_hsvs('hs-vs_16x8', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    cal_mean_std_hsvs('hs-vs_8x8', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])

# Run ParaSearch
if 0:
    ParaSearch = 'libsvmexe/ParaSearch'

# Run ParaSearch 子集
if 0:
    ParaSearch = 'libsvmexe/ParaSearch'
    ParaSearchs = 'libsvmexe/ParaPro'

    # train_sub_set(ParaSearchs, 's-ns_64x64')
    train_sub_set(ParaSearchs, 's-ns_32x32')
    # train_sub_set(ParaSearchs, 's-ns_16x16')
    # # train_sub_set(ParaSearchs, 's-ns_8x8')
    # train_sub_set(ParaSearchs, 's-ns_32x16')
    # train_sub_set(ParaSearchs, 's-ns_32x8')
    # # # train_sub_set(ParaSearchs, 's-ns_32x4')
    # train_sub_set(ParaSearchs, 's-ns_16x8')
    # train_sub_set(ParaSearchs, 's-ns_16x4')
    # train_sub_set(ParaSearchs, 's-ns_8x4')

    # train_sub_set(ParaSearchs, 'hs-vs_32x32')
    # train_sub_set(ParaSearchs, 'hs-vs_16x16')
    # train_sub_set(ParaSearchs, 'hs-vs_8x8')
    # train_sub_set(ParaSearchs, 'hs-vs_32x16')
    # train_sub_set(ParaSearchs, 'hs-vs_32x8')
    # train_sub_set(ParaSearchs, 'hs-vs_16x8')

# Run th
if 0:
    th = 'libsvmexe/th'
    # select_th(th, 's-ns_64x64')
    # select_th(th, 's-ns_32x32')
    # select_th(th, 's-ns_16x16')
    select_th(th, 's-ns_8x8')
    # select_th(th, 's-ns_32x16')
    # select_th(th, 's-ns_32x8')
    select_th(th, 's-ns_32x4')
    # select_th(th, 's-ns_16x8')
    select_th(th, 's-ns_16x4')
    select_th(th, 's-ns_8x4')
    # select_th(th, 'hs-vs_32x32')
    # select_th(th, 'hs-vs_16x16')
    select_th(th, 'hs-vs_8x8')
    # select_th(th, 'hs-vs_32x16')
    #select_th(th, 'hs-vs_32x8')
    #select_th(th, 'hs-vs_16x8')

# Run th log
if 0:
    readth_log('libsvmdata/')

# Run predict
if 0:
    predict = "libsvmexe/predict"
    for i in range(20):
        run_predict(predict, 'libsvmexe/test.txt', 'libsvmdata/s-ns_16x16/log/model' + str(i) + '.txt', 'libsvmexe/predict' + str(i) + '.txt', 'libsvmexe/log.txt')

# Run log
if 0:
    read_log('libsvmdata/')

#feature select
if 0:
    #feature_train('s-ns_64x64', ['qp', 'var', 'ngradx', 'ngrady', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx'])
    # fe = ['var', 'ngradx', 'ngrady', 'MaxDiffVar', 'InconsVarH', 'InconsVarV', 'gmx']
    # for f in fe:
    #     feature_train('s-ns_32x16', ['qp', f])
    # feature_train('s-ns_64x64', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # feature_train('s-ns_16x16', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # feature_train('s-ns_32x16', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # feature_train('s-ns_32x8', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # feature_train('s-ns_16x8', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    #feature_train('s-ns_16x4', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    #feature_train('s-ns_8x8', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    #feature_train('s-ns_8x4', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    #feature_train('s-ns_32x4', ['qp', 'var','ngradx', 'ngrady', 'gmx', 'MaxDiffVar', 'MaxDiffgradx', 'MaxDiffgrady'])
    # feature_train('hs-vs_32x32', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # feature_train('hs-vs_32x16', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # feature_train('hs-vs_32x8', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # feature_train('hs-vs_16x16', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    # feature_train('hs-vs_16x8', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])
    feature_train('hs-vs_8x8', ['qp', 'ngradx', 'ngrady', 'ndva', 'ndgradx2', 'ndgrady2'])

if 1:

    fig = plt.figure()
    # sns - 0.02, 0.41, 0.40, 0.44, 0.53, 0.36, 0.31, 0.33, 0.45, 0.41, 0.45
    # hsvs 0.002, 0.05, 0.11, 0.12, 0.03, 0.12, 0.11, 0.12, 0.05, 0.11, 0.07
    b1 = plt.bar([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11], [0.02, 0.41, 0.40, 0.44, 0.53, 0.36, 0.31, 0.33, 0.45, 0.41, 0.45], width=0.3)
    b2 = plt.bar([1+0.3, 2+0.3, 3+0.3, 4+0.3, 5+0.3, 6+0.3, 7+0.3, 8+0.3, 9+0.3, 10+0.3, 11+0.3], [0.002, 0.05, 0.11, 0.12, 0.03, 0.12, 0.11, 0.12, 0.05, 0.11, 0.07], width=0.3)
    # plt.bar([1, 2, 3, 4], [4, 5, 6, 7], bottom=[4, 5, 6, 7], width=0.3)

    plt.xlim(0, 12)
    plt.xticks([1.15, 2.15, 3.15, 4.15, 5.15, 6.15, 7.15, 8.15, 9.15, 10.15, 11.15], ['QP', 'Var', '$\mathregular{Grad_x}$', '$\mathregular{Grad_y}$', '$\mathregular{Grad_m}$', '$\mathregular{F_b}$(Var)', '$\mathregular{F_b}$$\mathregular{Grad_x}$', '$\mathregular{F_b}$$\mathregular{Grad_y}$', '$\mathregular{F_q}$(Var)', '$\mathregular{F_q}$$\mathregular{Grad_x}$', '$\mathregular{F_q}$$\mathregular{Grad_y}$'])
    plt.grid(True)

    plt.legend(fontsize=12)

    #设置坐标轴名称
    plt.ylabel('MIC',fontsize=12)

    fig.autofmt_xdate(rotation=35)

    print(type(b1))

    plt.legend((b1, b2), ('S-NS', 'HS-VS'))

    plt.show()


import time
import cv2
from cvzone.HandTrackingModule import HandDetector
import numpy as np
import math

cap =cv2.VideoCapture(0)
dector = HandDetector(maxHands=1)

offset =20
imgSize = 300
counter = 0

folder = "Data/B"

while True :
    succuss,img =cap.read()
    hands,img = dector.findHands(img)
    if hands:
        hands = hands[0]
        x, y, w,h =hands['bbox']

        imgWhite = np.ones((imgSize,imgSize,3),np.uint8)*225
        imgCrop = img[y-offset:y+h+offset, x-offset:x+w+offset]
        imgCropShape = imgCrop.shape
        # imgWhite[0:imgCropShape[0],0:imgCropShape[1]] = imgCrop
        aspectRatio = h/w

        if aspectRatio >1:
            k = imgSize/h
            wCal = math.ceil(k*w)
            ImgResize = cv2.resize(imgCrop,(wCal,imgSize))
            ImgResizeShape = ImgResize.shape
            wGap = math.ceil((imgSize- wCal)/2)
            imgWhite[:, wGap:wCal +wGap] = ImgResize
        else:
            k = imgSize / w
            hCal = math.ceil(k * h)
            ImgResize = cv2.resize(imgCrop, (imgSize, hCal))
            ImgResizeShape = ImgResize.shape
            hGap = math.ceil((imgSize - hCal) / 2)
            imgWhite[hGap:hCal + hGap,:] = ImgResize

        cv2.imshow('imageCrop', imgCrop)
        cv2.imshow('imageWhite', imgWhite)

    cv2.imshow('image', img)
    key = cv2.waitKey(4)
    if key == ord("s"):
        counter +=1
        cv2.imwrite(f'{folder}/imge_{time.time()} .jpg', imgWhite)
        print(counter)


# class Interval:
#     def __init__(self, s=0, e=0):
#         self.start = s
#         self.end = e

class Solution:
    def merge(self, intervals):
        merged = []
        for interval in sorted(intervals, key=lambda x: x.start):
            if merged and interval.start <= merged[-1].end:
                merged[-1].end = max(interval.end, merged[-1].end)
            else:
                merged.append(interval)
        return merged


#!/usr/bin/env python
# -*- coding: utf-8 -*-
__all__=["keywords"]
from os.path import *

dir = dirname(dirname(__file__))
if not dir: dir="."

file = join(dir,"keywords.txt")
if exists(file):
	# separated by " " space
    keywords = open(file).read().lstrip().rstrip()

if __name__=="__main__":
	for k in __all__:
		if k in globals():
			print("%s: %s" % (k,globals()[k]))


class Solution:
	def __init__(self, i, j, k):
		self.start = i
		# +1 here so we dont have to do it in the loop below
		self.end = j + 1
		self.div = k

	def b_d_conversion(self):
		c = 0
		for i in range(self.start, self.end):
			rev_num = int(str(i)[::-1])

			if abs(i - rev_num) % self.div == 0:
				c += 1
		return c

	def b_d_no_conversion(self):
		c = 0

		for i in range(self.start, self.end):
			rev_num = 0
			temp = i
			while temp > 0:
				rev_num = rev_num*10 + temp % 10
				temp //= 10

			if abs(i - rev_num) % self.div == 0:
				c += 1
		return c

test = Solution(20, 23, 6)
run = test.b_d_conversion()

print(run)


import os, json
rootdir = os.getcwd()

for subdir, dirs, files in os.walk(rootdir):
    """
    Change the name to whatever you want to mass change in the 'if file =="Name.json" to what each json file is named in its directory.
    You can also just completely remove this if statement and it will try to perfom all operations.
    The way this works is it goes into each folder and then open each Json that you attempt to open if it exists, if it does exist then it opens the json, reads it, and then overwrites it with whatever changes you made

    copy and paste this print statement to after this comment block to see what all the directory names are
    print('Subdir: {}\t Dirs: {}\tFiles{}'.format(subdir,dirs,files))
    """

    if 'Boomerang' in subdir:
        #print('Doing stuff to Boomerang Monkeys only')
        for file in files:
            try:
                if file == 'AttackModel_Attack_.json':
                    to_read_from = open(os.path.join(subdir, file),'r')
                    to_write=json.load(to_read_from)
                    to_read_from.close()

                    to_write['range']= 99.0
                    to_write['attackThroughWalls'] = True
                    to_write['weapons'][0]['projectile']['pierce'] = 1000
                    to_write['weapons'][0]['projectile']['display'] = '98010195051b16341bab67a674472835'
                    to_write['weapons'][0]['rateFrames'] = 0
                    with open(os.path.join(subdir, file),'w') as t:
                        json.dump(to_write,t,indent=4)
                    t.close()

                elif file == 'AttackModel_OrbitAttack_.json':
                    to_read_from = open(os.path.join(subdir, file),'r')
                    to_write=json.load(to_read_from)
                    to_read_from.close()

                    to_write['weapons'][0]['rateFrames'] = 0
                    to_write['weapons'][0]['animation'] = 0
                    with open(os.path.join(subdir, file),'w') as t:
                        json.dump(to_write,t,indent=4)
                    t.close()

                elif file =='OrbitModel_.json':
                    to_read_from = open(os.path.join(subdir, file),'r')
                    to_write=json.load(to_read_from)
                    to_read_from.close()

                    to_write['projectile']['display'] = '98010195051b16341bab67a674472835'
                    to_write['projectile']["hasDamageModifiers"] = True
                    to_write['projectile']["radius"] = 9
                    to_write['projectile']["scale"] = 1.5
                    with open(os.path.join(subdir, file),'w') as t:
                        json.dump(to_write,t,indent=4)
                    t.close()
            except Exception as e:
                print(e)


"""
Faça um programa que faça 5 perguntas para uma pessoa sobre um crime. As perguntas são:
1- "Telefonou para a vítima?"
2- "Esteve no local do crime?"
3- "Mora perto da vítima?"
4- "Devia para a vítima?"
5- "Já trabalhou com a vítima?"
O programa deve no final emitir uma classificação sobre a participação da pessoa no crime.
Se a pessoa responder positivamente a 2 questões ela deve ser classificada como "Suspeita", entre 3 e 4 como "Cúmplice" e 5 como "Assassino".
Caso contrário, ele será classificado como "Inocente".
"""

#Input
p1 = input("Telefonou para a vítima? (S/N)\n").lower()
p2 = input("Esteve no local do crime? (S/N)\n").lower()
p3 = input("Mora perto da vítima? (S/N)\n").lower()
p4 = input("Devia para a vítima? (S/N)\n").lower()
p5 = input("Já trabalhou com a vítima? (S/N)\n").lower()
#Processamento
def pergunta(p):
    if p == "s": return 1
    elif p == "n": return 0
def condicoes():
    if total == 5: return "Assassino"
    elif total == 3 or total == 4: return "Cúmplice"
    elif total == 2: return "Suspeita"
    else: return "Inocente"
total = pergunta(p1)+pergunta(p2)+pergunta(p3)+pergunta(p4)+pergunta(p5)
#Output
print(condicoes())


import common
import management_roles
import role
import scanner_stack_roles

def create_scanner_stack_resources(context):
    prefix = common.get_prefix(context, 'scanner')

    properties = context.properties

    project_id = context.env['project']
    region = properties['region']
    management_service_account_id = properties['managementServiceAccountID']

    scanner_topic_name = f'{prefix}-scanner-topic'
    scanner_topic = {
        'name': scanner_topic_name,
        'type': 'pubsub.v1.topic',
        'properties': {
            'name': f"projects/{project_id}/topics/{scanner_topic_name}",
            'topic': scanner_topic_name
        },
        'accessControl': {
            'gcpIamPolicy': {
                'bindings': [
                    {
                        'role': role.get_role_name_ref(project_id, management_roles.PUBSUB_MANAGEMENT_ROLE),
                        'members': [
                            f"serviceAccount:{management_service_account_id}"
                        ]
                    },
                    {
                        'role': role.get_role_name_ref(project_id, management_roles.PUBSUB_IAM_MANAGEMENT_ROLE),
                        'members': [
                            f"serviceAccount:{management_service_account_id}"
                        ]
                    },
                    {
                        'role': 'roles/pubsub.publisher',
                        'members': [
                            f'serviceAccount:$(ref.{prefix}-scanner-service-account.email)',
                        ]
                    }
                ]
            }
        }
    }

    pattern_updater_topic_name = f'{prefix}-pattern-updater-topic'
    pattern_updater_topic = {
        'name': pattern_updater_topic_name,
        'type': 'pubsub.v1.topic',
        'properties': {
            'name': f"projects/{project_id}/topics/{pattern_updater_topic_name}",
            'topic': pattern_updater_topic_name
        }
    }

    pattern_update_bucket_name = f'{prefix}-pattern-update-bucket'
    pattern_update_bucket= {
        'name': pattern_update_bucket_name,
        'type': 'gcp-types/storage-v1:buckets',
        'properties': {
            'name': pattern_update_bucket_name,
            'iamConfiguration': {
                'uniformBucketLevelAccess' : {
                    'enabled': True
                }
            }
        }
    }

    pattern_update_bucket_scanner_role_binding = {
        'name': 'pattern-update-bucket-scanner-role-binding',
        'type': 'gcp-types/storage-v1:virtual.buckets.iamMemberBinding',
        'properties':
            {
                'bucket': f"$(ref.{pattern_update_bucket['name']}.name)",
                'role': role.get_role_name_ref(project_id, scanner_stack_roles.GET_PATTERN_UPDATE_SCANNER_ROLE),
                'member': f'serviceAccount:$(ref.{prefix}-scanner-service-account.email)'
            }
    }

    pattern_update_bucket_role_binding = {
        'name': 'pattern-update-bucket-role-binding',
        'type': 'gcp-types/storage-v1:virtual.buckets.iamMemberBinding',
        'properties':
            {
                'bucket': f"$(ref.{pattern_update_bucket['name']}.name)",
                'role': role.get_role_name_ref(project_id, scanner_stack_roles.PATTERN_UPDATE_ROLE),
                'member': f'serviceAccount:$(ref.{prefix}-pattern-updater-service-account.email)'
            }
    }

    pattern_updater = {
        'name': f'{prefix}-pattern-updater',
        'type': 'gcp-types/cloudfunctions-v1:projects.locations.functions',
        'properties': {
            'parent': f'projects/{project_id}/locations/{region}',
            'function': f'{prefix}-pattern-updater',
            'entryPoint': 'main',
            'serviceAccountEmail': f'$(ref.{prefix}-pattern-updater-service-account.email)',
            'sourceArchiveUrl': f"gs://{properties['artifactBucket']}/gcp-scanner-pattern-updater.zip",
            'runtime': 'python38',
            'availableMemoryMb': 2048,
            'timeout': '120s',
            'environmentVariables': {
                'LD_LIBRARY_PATH': './:./lib',
                'PATTERN_UPDATE_BUCKET': pattern_update_bucket['name']
            },
            'eventTrigger': {
                'eventType': 'providers/cloud.pubsub/eventTypes/topic.publish',
                'resource': f"$(ref.{pattern_updater_topic_name}.name)",
                'failurePolicy': {
                    'retry': {}
                }
            }
        }
    }

    pattern_updater_scheduler = {
        'name': f'{prefix}-scheduler',
        'type': 'gcp-types/cloudscheduler-v1:projects.locations.jobs',
        'properties': {
            'parent': f'projects/{project_id}/locations/{region}',
            'name': f'{prefix}-pattern-update',
            'description': 'pattern update',
            'schedule': "0 * * * *",
            'timeZone': 'UTC',
            'pubsubTarget': {
                'topicName': f"projects/{project_id}/topics/{pattern_updater_topic['name']}",
                'data': 'UGF0dGVybiBVcGRhdGU=',
            }
        },
        'metadata': {
            'dependsOn': [pattern_updater_topic['name']]
        }
    }

    scanner = {
        'name': f'{prefix}-scanner',
        # https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions
        'type': 'gcp-types/cloudfunctions-v1:projects.locations.functions',
        'properties': {
            'parent': f'projects/{project_id}/locations/{region}',
            'function': f'{prefix}-scanner',
            'entryPoint': 'main',
            'sourceArchiveUrl': f"gs://{properties['artifactBucket']}/gcp-scanner.zip",
            'serviceAccountEmail': f'$(ref.{prefix}-scanner-service-account.email)',
            'runtime': 'python38',
            'availableMemoryMb': 2048,
            'timeout': '120s',
            'environmentVariables': {
                'LD_LIBRARY_PATH': '/workspace:/workspace/lib',
                'PATTERN_PATH': './patterns',
                'PROJECT_ID': project_id,
                'REGION': region,
                'DEPLOYMENT_NAME': properties['deploymentName'],
                'PATTERN_UPDATE_BUCKET': pattern_update_bucket['name']
            },
            'secretEnvironmentVariables': [
                {
                    'key': 'SCANNER_SECRETS',
                    'projectId': project_id,
                    'secret': f'{properties["scannerSecretsName"]}',
                    'version': 'latest'
                }
            ],
            'eventTrigger': {
                'eventType': 'providers/cloud.pubsub/eventTypes/topic.publish',
                'resource': f"projects/{project_id}/topics/{scanner_topic['name']}",
                'failurePolicy': {
                    'retry': {}
                }
            }
        },
        'metadata': {
            'dependsOn': [scanner_topic['name']]
        }
    }

    scanner_management_account_role_binding = {
        'name': 'scanner-management-account-role-binding',
        'type': 'gcp-types/cloudfunctions-v1:virtual.projects.locations.functions.iamMemberBinding',
        'properties': {
            'resource': f"$(ref.{scanner['name']}.name)",
            'role': role.get_role_name_ref(project_id, management_roles.CLOUD_FUNCTION_MANAGEMENT_ROLE),
            'member': f'serviceAccount:{management_service_account_id}'
        }
    }

    pattern_updater_management_account_role_binding = {
        'name': 'pattern-updater-management-account-role-binding',
        'type': 'gcp-types/cloudfunctions-v1:virtual.projects.locations.functions.iamMemberBinding',
        'properties': {
            'resource': f"$(ref.{pattern_updater['name']}.name)",
            'role': role.get_role_name_ref(project_id, management_roles.CLOUD_FUNCTION_MANAGEMENT_ROLE),
            'member': f'serviceAccount:{management_service_account_id}'
        }
    }

    scanner_topic_dlt_name = f'{prefix}-scanner-topic-dlt'
    scanner_topic_dlt = {
        'name': scanner_topic_dlt_name,
        'type': 'pubsub.v1.topic',
        'properties': {
            'name': f"projects/{project_id}/topics/{scanner_topic_dlt_name}",
            'topic': scanner_topic_dlt_name
        },
        'accessControl': {
            'gcpIamPolicy': {
                'bindings': [
                    {
                        'role': role.get_role_name_ref(project_id, management_roles.PUBSUB_MANAGEMENT_ROLE),
                        'members': [
                            f"serviceAccount:{management_service_account_id}"
                        ]
                    },
                    {
                        'role': role.get_role_name_ref(project_id, management_roles.PUBSUB_IAM_MANAGEMENT_ROLE),
                        'members': [
                            f"serviceAccount:{management_service_account_id}"
                        ]
                    },
                    {
                        'role': 'roles/pubsub.publisher',
                        'members': [
                            f"serviceAccount:service-{context.env['project_number']}@gcp-sa-pubsub.iam.gserviceaccount.com",
                        ]
                    }
                ]
            }
        }
    }

    scanner_dlt = {
        'name': f'{prefix}-scanner-dlt',
        # https://cloud.google.com/functions/docs/reference/rest/v1/projects.locations.functions
        'type': 'gcp-types/cloudfunctions-v1:projects.locations.functions',
        'properties': {
            'parent': f'projects/{project_id}/locations/{region}',
            'function': f'{prefix}-scanner-dlt',
            'entryPoint': 'main',
            'sourceArchiveUrl': f"gs://{properties['artifactBucket']}/gcp-scanner-dlt.zip",
            'serviceAccountEmail': f'$(ref.{prefix}-scanner-service-account.email)',
            'runtime': 'python38',
            'eventTrigger': {
                'eventType': 'providers/cloud.pubsub/eventTypes/topic.publish',
                'resource': f"projects/{project_id}/topics/{scanner_topic_dlt['name']}",
            },
            'environmentVariables': {}
        },
        'metadata': {
            'dependsOn': [scanner_topic_dlt['name']]
        }
    }

    scanner_dlt_management_account_role_binding = {
        'name': 'scanner-dlt-management-account-role-binding',
        'type': 'gcp-types/cloudfunctions-v1:virtual.projects.locations.functions.iamMemberBinding',
        'properties': {
            'resource': f"$(ref.{scanner_dlt['name']}.name)",
            'role': role.get_role_name_ref(project_id, management_roles.CLOUD_FUNCTION_MANAGEMENT_ROLE),
            'member': f'serviceAccount:{management_service_account_id}'
        }
    }

    scanner_stack_function_update_role_binding = {
        'name':'scanner-stack-function-update-role-binding',
        'type': 'gcp-types/cloudresourcemanager-v1:virtual.projects.iamMemberBinding',
        'properties': {
            'resource': project_id,
            'role': role.get_role_name_ref(project_id, management_roles.SOURCE_CODE_SET_ROLE),
            'member': f'serviceAccount:{management_service_account_id}'
        }
    }

    resources = [
        scanner,
        scanner_dlt,
        scanner_topic,
        scanner_topic_dlt,
        scanner_management_account_role_binding,
        scanner_dlt_management_account_role_binding,
        pattern_updater_topic,
        pattern_update_bucket,
        pattern_updater,
        pattern_updater_scheduler,
        pattern_update_bucket_scanner_role_binding,
        pattern_update_bucket_role_binding,
        pattern_updater_management_account_role_binding
    ]

    if properties['functionAutoUpdate']:
        resources.append(scanner_stack_function_update_role_binding)

    outputs = [{
        'name': 'scannerTopic',
        'value': scanner_topic['name']
    },{
        'name': 'scannerTopicDLT',
        'value': scanner_topic_dlt['name']
    },{
        'name': 'scannerProjectID',
        'value': project_id
    },{
        'name': 'scannerFunctionName',
        'value': '$(ref.{}.name)'.format(scanner['name'])
    },{
        'name': 'patternUpdaterFunctionName',
        'value': '$(ref.{}.name)'.format(pattern_updater['name'])
    },{
        'name': 'scannerDLTFunctionName',
        'value': '$(ref.{}.name)'.format(scanner_dlt['name'])
    },{
        'name': 'region',
        'value': region
    },{
        'name': 'scannerSecretsName',
        'value': properties['scannerSecretsName']
    },{
        'name': 'patternUpdateSchedulerJobName',
        'value': pattern_updater_scheduler['properties']['name']
    },{
        'name': 'patternUpdateBucket',
        'value': '$(ref.{}.name)'.format(pattern_update_bucket['name'])
    },{
        'name': 'functionAutoUpdate',
        'value': properties['functionAutoUpdate']
    }]
    return (resources, outputs)

def generate_config(context):
    """ Entry point for the deployment resources. """

    resources, outputs = create_scanner_stack_resources(context)

    return {
        'resources': resources,
        'outputs': outputs
    }


# -*- encoding: utf-8 -*-
'''
@FILE           :validBracket.py
@TIME           :2020/05/02 13:43:37
@AUTHOR         :Toulon Du
@EMAIL          :seaduhe@gmail.com
@DESCRIPTION    :
@VERSION        :1.0
'''

"""
给定一个只包括 '('，')'，'{'，'}'，'['，']' 的字符串，判断字符串是否有效。

有效字符串需满足：

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。

示例 1:

输入: "()"
输出: true
示例 2:

输入: "()[]{}"
输出: true
示例 3:

输入: "(]"
输出: false
示例 4:

输入: "([)]"
输出: false
示例 5:

输入: "{[]}"
输出: true

来源：力扣（LeetCode）
链接：https://leetcode-cn.com/problems/valid-parentheses
著作权归领扣网络所有。商业转载请联系官方授权，非商业转载请注明出处。
"""
def isValid(s):
    """
    :type s: str
    :rtype: bool
    """
    brackets_store = []
    brackets = {'[':1,']':-1,'{':2,'}':-2,'(':3,')':-3}
    for c in s:
        if c in brackets:
            if (len(brackets_store)>0) and (brackets[c] + brackets[brackets_store[-1]]==0):
                brackets_store.pop()
            else:
                brackets_store.append(c)
    return len(brackets_store)==0


"""

This module contains GranularityLevel and Program class.

"""
import os
import shutil
import json
from enum import Enum
from distutils.dir_util import copy_tree
from .logger import Logger

class GranularityLevel(Enum):
    """

    GranularityLevel represents the granularity levels of program.

    """
    LINE = 'line'
    AST = 'AST'

    @classmethod
    def is_valid(cls, value):
        """
        :param value: The value of enum to check

        :return: Whether there is an enum that has a value equal to the `value`
        :rtype: bool

        .. hint::
            There are some examples,
            ::
                GranularityLevel.is_valid('line')
                >> True
                GranularityLevel.is_valid('random_text')
                >> False
        """
        return any(value == item.value for item in cls)

class Program(object):
    """

    Program encapsulates the original source code.
    Currently, PYGGI stores the source code as a list of code lines,
    as lines are the only supported unit of modifications.
    For modifications at other granularity levels,
    this class needs to process and store the source code accordingly
    (for example, by parsing and storing the AST).

    """
    CONFIG_FILE_NAME = 'PYGGI_CONFIG'
    TMP_DIR = "./pyggi_tmp/"

    def __init__(self, path, granularity_level=GranularityLevel.LINE,
                 config_file_name=CONFIG_FILE_NAME):
        assert isinstance(granularity_level, GranularityLevel)
        self.path = path.strip()
        if self.path.endswith('/'):
            self.path = self.path[:-1]
        self.name = os.path.basename(self.path)
        self.logger = Logger(self.name)
        self.granularity_level = granularity_level
        with open(os.path.join(self.path, config_file_name)) as config_file:
            config = json.load(config_file)
            self.test_command = config['test_command']
            self.target_files = config['target_files']
        Program.clean_tmp_dir(self.tmp_path)
        copy_tree(self.path, self.tmp_path)
        self.contents = Program.parse(self.granularity_level, self.path, self.target_files)
        self.modification_weights = dict()
        self._modification_points = None

    def __str__(self):
        if self.granularity_level == GranularityLevel.LINE:
            code = ''
            for k in sorted(self.contents.keys()):
                idx = 0
                for line in self.contents[k]:
                    code += "{}\t: {}\t: {}\n".format(k, idx, line)
                    idx += 1
            return code
        return self.target_files

    def reset_tmp_dir(self):
        Program.clean_tmp_dir(self.tmp_path)
        copy_tree(self.path, self.tmp_path)

    @property
    def tmp_path(self):
        """
        :return: The path of the temporary dirctory
        :rtype: str
        """
        return os.path.join(Program.TMP_DIR, self.name)

    @property
    def modification_points(self):
        """
        :return: The list of position of modification points for each target program
        :rtype: dict(str, ?)
        """
        assert isinstance(self.granularity_level, GranularityLevel)

        if self._modification_points:
            return self._modification_points

        self._modification_points = dict()
        if self.granularity_level == GranularityLevel.LINE:
            for target_file in self.target_files:
                self._modification_points[target_file] = list(range(len(self.contents[target_file])))
        elif self.granularity_level == GranularityLevel.AST:
            for target_file in self.target_files:
                if Program.is_python_code(target_file):
                    from .helper import stmt_python
                    self._modification_points[target_file] = stmt_python.get_modification_points(
                        self.contents[target_file])
        return self._modification_points

    def select_modification_point(self, target_file, method="random"):
        """
        :param str target_file: The modification point is chosen within target_file
        :param str method: The way how to choose a modification point, *'random'* or *'weighted'*
        :return: The **index** of modification point
        :rtype: int
        """
        import random
        assert target_file in self.target_files
        assert method in ['random', 'weighted']
        candidates = self.modification_points[target_file]
        if method == 'random' or target_file not in self.modification_weights:
            return random.randrange(len(candidates))
        elif method == 'weighted':
            cumulated_weights = sum(self.modification_weights[target_file])
            list_of_prob = list(map(lambda w: float(w)/cumulated_weights, self.modification_weights[target_file]))
            return random.choices(list(range(len(candidates))), weights=list_of_prob, k=1)[0]

    def set_modification_weights(self, target_file, weights):
        """
        :param str target_file: The path to file
        :param weights: The modification weight([0,1]) of each modification points
        :type weights: list(float)
        :return: None
        :rtype: None
        """
        from copy import deepcopy
        assert target_file in self.target_files
        assert len(self.modification_points[target_file]) == len(weights)
        assert not list(filter(lambda w: w < 0 or w > 1, weights))
        self.modification_weights[target_file] = deepcopy(weights)

    def write_to_tmp_dir(self, new_contents):
        """
        Write new contents to the temporary directory of program

        :param new_contents: The new contents of the program.
          Refer to *apply* method of :py:class:`.patch.Patch`
        :type new_contents: dict(str, ?)
        :rtype: None
        """
        for target_file in new_contents:
            with open(os.path.join(self.tmp_path, target_file), 'w') as tmp_file:
                tmp_file.write(Program.to_source(self.granularity_level, new_contents[target_file]))

    def print_modification_points(self, target_file, indices=None):
        """
        Print the source of each modification points

        :param target_file: The path to target file
        :type target_file: str
        :return: None
        :rtype: None
        """
        title_format = "=" * 25 + " {} {} " + "=" * 25
        if not indices:
            indices = range(len(self.modification_points[target_file]))
        if self.granularity_level == GranularityLevel.LINE:
            def print_modification_point(contents, modification_points, i):
                print(title_format.format('line', i))
                print(contents[modification_points[i]])
        elif self.granularity_level == GranularityLevel.AST:
            if Program.is_python_code(target_file):
                def print_modification_point(contents, modification_points, i):
                    import astor
                    from .helper import stmt_python
                    print(title_format.format('node', i))
                    blk, idx = stmt_python.pos_2_block_n_index(contents, modification_points[i])
                    print(astor.to_source(blk[idx]))
        for i in indices:
            print_modification_point(self.contents[target_file], self.modification_points[target_file], i)

    @classmethod
    def to_source(cls, granularity_level, contents_of_file):
        """
        Change contents of file to the source code

        :param granularity_level: The parsing level of the program
        :type granularity_level: :py:class:`GranularityLevel`
        :param contents_of_file: The contents of the file which is the parsed form of source code
        :type contents_of_file: ?
        :return: The source code
        :rtype: str
        """
        if granularity_level == GranularityLevel.LINE:
            return '\n'.join(contents_of_file) + '\n'
        elif granularity_level == GranularityLevel.AST:
            import astor
            return astor.to_source(contents_of_file)
        return ''

    @classmethod
    def clean_tmp_dir(cls, tmp_path):
        """
        Clean the temporary project directory if it exists.

        :param str tmp_path: The path of directory to clean.
        :return: None
        """
        if os.path.exists(tmp_path):
            shutil.rmtree(tmp_path)
        if not os.path.exists(Program.TMP_DIR):
            os.mkdir(Program.TMP_DIR)
        os.mkdir(tmp_path)

    @classmethod
    def parse(cls, granularity_level, path, target_files):
        """
        :param granularity_level: The granularity level of a program
        :type granularity_level: :py:class:`.program.GranularityLevel`
        :param str path: The project root path
        :param target_files: The paths to target files from the project root
        :type target_files: list(str)

        :return: The contents of the files, see `Hint`
        :rtype: dict(str, list(str))

        .. hint::
            - key: the file name
            - value: the contents of the file
        """
        assert isinstance(granularity_level, GranularityLevel)
        if granularity_level == GranularityLevel.LINE:
            contents = {}
            for target in target_files:
                with open(os.path.join(path, target), 'r') as target_file:
                    contents[target] = list(
                        map(str.rstrip, target_file.readlines()))
            return contents
        elif granularity_level == GranularityLevel.AST:
            import ast
            import astor
            contents = {}
            for target in target_files:
                if cls.is_python_code(target):
                    root = astor.parse_file(os.path.join(path, target))
                    contents[target] = root
                else:
                    raise Exception('Program', '{} file is not supported'.format(cls.get_file_extension(target)))
            return contents
        return None

    @staticmethod
    def is_python_code(source_path):
        """
        :param source_path: The path of the source file
        :type source_path: str
        :return: whether the file's extention is *.py* or not
        :rtype: bool
        """
        _, file_extension = os.path.splitext(source_path)
        return file_extension == '.py'

    @staticmethod
    def is_java_code(source_path):
        """
        :param source_path: The path of the source file
        :type source_path: str
        :return: whether the file's extention is *.java* or not
        :rtype: bool
        """
        _, file_extension = os.path.splitext(source_path)
        return file_extension == '.java'

    @staticmethod
    def get_file_extension(file_path):
        """
        :param file_path: The path of file
        :type file_path: str
        :return: file extension
        :rtype: str
        """
        _, file_extension = os.path.splitext(file_path)
        return file_extension

    @staticmethod
    def have_the_same_file_extension(file_path_1, file_path_2):
        """
        :param file_path_1: The path of file 1
        :type file_path_1: str
        :param file_path_2: The path of file 2
        :type file_path_2: str
        :return: same or not
        :rtype: bool
        """
        return Program.get_file_extension(file_path_1) == Program.get_file_extension(file_path_2)


from fastapi import APIRouter, Depends, HTTPException
from fastapi.responses import JSONResponse, HTMLResponse
from ..dependencies import lock
from ..tiny.catalog import catalog
from ..tiny.initialization import server

router = APIRouter(
    prefix="/databases",
    tags=["databases"],
    responses={404: {"description": "Not found"}},
)

@router.get("/", response_class=JSONResponse)
async def get_databases():
    """Returns the list of databases provided through the current TinyOlap API instance."""
    try:
        return JSONResponse(content={"databases": [{"id": server.databases[db].name,
                               "description": server.databases[db].description,
                               "in_memory": server.databases[db].in_memory} for db in server.databases]},
                             status_code=200)

    except Exception as e:
        raise HTTPException(status_code=500, detail="Internal server error. " + str(e))

@router.get("/{database_id}", response_class=JSONResponse)
async def get_database_info(database_id: str):
    """Returns the list of cubes and dimensions defined in a specific database.
     :param database_id: id (the name) of the database to return.
    """
    try:
        if database_id in server.databases:
            database = server[database_id]
            with lock[database].gen_rlock():
                return JSONResponse(catalog(db=server.databases[database_id], full_catalog=False),
                                    status_code=200)
        else:
            raise HTTPException(status_code=404, detail=f"TinyOlap database_id '{database_id}' not found.")
    except Exception as e:
        raise HTTPException(status_code=500, detail="Internal server error. " + str(e))

@router.get("/{database_id}/catalog", response_class=JSONResponse)
async def get_database_catalog(database_id: str):
    """Returns the catalog of a specific database, containing all relevant meta and
    master data, including members of dimension and rules."""
    try:
        if database_id in server.databases:
            database = server[database_id]
            with lock["db"].gen_rlock():
                return JSONResponse(content=catalog(db=server.databases[database_id], full_catalog=True),
                                    status_code=200)
        else:
            raise HTTPException(status_code=404, detail=f"TinyOlap database_id '{database_id}' not found.")
    except Exception as e:
        raise HTTPException(status_code=500, detail="Internal server error. " + str(e))


def isThuannghich(str):
    str1 = str[::-1]
    if int(str) != int(str1):
        return 0
    return 1

for t in range(int(input())):
    n = input()
    sum = 0
    for i in range(len(n)):
        sum += int(n[i])
    if len(str(sum)) > 1 and isThuannghich(str(sum)) == 1:
        print("YES")
    else:
        print("NO")


import asyncio
from collections import defaultdict
from typing import Awaitable, Optional
from nonebot import on_message, on_request, get_bot, logger, get_driver
from nonebot.typing import T_State
from nonebot.rule import keyword, to_me, Rule
from nonebot.adapters import Bot, Event
from nonebot.adapters.onebot.v11 import GroupMessageEvent, GroupRequestEvent
from nonebot.adapters.onebot.v11 import MessageSegment, Message, permission, GroupMessageEvent
from nonebot.permission import Permission
from src.common.config import BotConfig, GroupConfig

import random
import time
# from .pseudorandom import roulette_randomizer

roulette_status = defaultdict(int)  # 0 关闭 1 开启
roulette_time = defaultdict(int)
roulette_count = defaultdict(int)
timeout = 300
roulette_player = defaultdict(list)
role_cache = defaultdict(lambda: defaultdict(str))

async def am_I_admin(bot: Bot, event: GroupMessageEvent, state: T_State) -> bool:
    info = await get_bot(str(event.self_id)).call_api('get_group_member_info', **{
        'user_id': event.self_id,
        'group_id': event.group_id
    })
    role = info['role']
    role_cache[event.self_id][event.group_id] = role
    return role == 'admin' or role == 'owner'

async def am_I_admin_by_cache(bot: Bot, event: GroupMessageEvent, state: T_State) -> bool:
    role = role_cache[event.self_id][event.group_id]
    return role == 'admin' or role == 'owner'

def can_roulette_start(group_id: int) -> bool:
    if roulette_status[group_id] == 0 or time.time() - roulette_time[group_id] > timeout:
        return True

    return False

async def participate_in_roulette(bot: Bot, event: GroupMessageEvent, state: T_State) -> bool:
    '''
    牛牛自己是否参与轮盘
    '''
    if BotConfig(event.self_id, event.group_id).drunkenness() <= 0:
        return False

    if GroupConfig(event.group_id).roulette_mode() == 1:
        # 没法禁言自己
        return False

    # 群主退不了群（除非解散），所以群主牛牛不参与游戏
    if role_cache[event.self_id][event.group_id] == 'owner':
        return False

    return random.random() < 0.1667

async def roulette(messagae_handle, bot: Bot, event: GroupMessageEvent, state: T_State):
    rand = random.randint(1, 6)
    logger.info('Roulette rand: {}'.format(rand))
    roulette_status[event.group_id] = rand
    roulette_count[event.group_id] = 0
    roulette_time[event.group_id] = time.time()
    partin = await participate_in_roulette(bot, event, state)
    if partin:
        roulette_player[event.group_id] = [event.self_id, event.user_id, ]
    else:
        roulette_player[event.group_id] = [event.user_id, ]

    mode = GroupConfig(event.group_id).roulette_mode()
    if mode == 0:
        type_msg = '踢出群聊'
    elif mode == 1:
        type_msg = '禁言'
    await messagae_handle.finish(
        f'这是一把充满荣耀与死亡的左轮手枪，六个弹槽只有一颗子弹，中弹的那个人将会被{type_msg}。勇敢的战士们啊，扣动你们的扳机吧！')

async def is_roulette_type_msg(bot: Bot, event: GroupMessageEvent, state: T_State) -> bool:
    if event.get_plaintext().strip() in ['牛牛轮盘踢人', '牛牛轮盘禁言', '牛牛踢人轮盘', '牛牛禁言轮盘']:
        if can_roulette_start(event.group_id):
            admin = await am_I_admin(bot, event, state)
            return admin
    return False

async def is_config_admin(event: GroupMessageEvent) -> bool:
    return BotConfig(event.self_id).is_admin_of_bot(event.user_id)

IsAdmin = permission.GROUP_OWNER | permission.GROUP_ADMIN | Permission(
    is_config_admin)

roulette_type_msg = on_message(
    priority=5,
    block=True,
    rule=Rule(is_roulette_type_msg),
    permission=IsAdmin
)

@roulette_type_msg.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
    plaintext = event.get_plaintext().strip()
    if '踢人' in plaintext:
        mode = 0
    elif '禁言' in plaintext:
        mode = 1
    GroupConfig(event.group_id).set_roulette_mode(mode)

    await roulette(roulette_type_msg, bot, event, state)

async def is_roulette_msg(bot: Bot, event: GroupMessageEvent, state: T_State) -> bool:
    if event.get_plaintext().strip() in ['牛牛轮盘']:
        if can_roulette_start(event.group_id):
            admin = await am_I_admin(bot, event, state)
            return admin

    return False

roulette_msg = on_message(
    priority=5,
    block=True,
    rule=Rule(is_roulette_msg),
    permission=permission.GROUP
)

@roulette_msg.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
    await roulette(roulette_msg, bot, event, state)

async def is_shot_msg(bot: Bot, event: GroupMessageEvent, state: T_State) -> bool:
    if roulette_status[event.group_id] != 0 and event.get_plaintext().strip() == '牛牛开枪':
        admin = await am_I_admin_by_cache(bot, event, state)
        return admin

    return False

kicked_users = defaultdict(set)

async def shot(self_id: int, user_id: int, group_id: int) -> Optional[Awaitable[None]]:
    mode = GroupConfig(group_id).roulette_mode()
    self_role = role_cache[self_id][group_id]

    if self_id == user_id:
        if mode == 0:   # 踢人
            if self_role == 'owner':      # 牛牛是群主不能退群，不然群就解散了
                return None

            async def group_leave():
                await get_bot(str(self_id)).call_api('set_group_leave', **{
                    'group_id': group_id
                })
            return group_leave
        elif mode == 1:   # 牛牛没法禁言自己
            return None

    user_info = await get_bot(str(self_id)).call_api('get_group_member_info', **{
        'user_id': user_id,
        'group_id': group_id
    })
    user_role = user_info['role']

    if user_role == "owner":
        return None
    elif user_role == "admin" and self_role != "owner":
        return None

    if mode == 0:   # 踢人
        async def group_kick():
            kicked_users[group_id].add(user_id)
            await get_bot(str(self_id)).call_api('set_group_kick', **{
                'user_id': user_id,
                'group_id': group_id
            })
        return group_kick

    elif mode == 1:           # 禁言
        async def group_ban():
            await get_bot(str(self_id)).call_api('set_group_ban', **{
                'user_id': user_id,
                'group_id': group_id,
                'duration': random.randint(5, 20) * 60
            })
        return group_ban

shot_msg = on_message(
    priority=5,
    block=True,
    rule=Rule(is_shot_msg),
    permission=permission.GROUP
)

shot_text = [
    '无需退路。',
    '英雄们啊，为这最强大的信念，请站在我们这边。',
    '颤抖吧，在真正的勇敢面前。',
    '哭嚎吧，为你们不堪一击的信念。',
    '现在可没有后悔的余地了。',
    "你将在此跪拜。"]

@shot_msg.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
    roulette_status[event.group_id] -= 1
    roulette_count[event.group_id] += 1
    shot_msg_count = roulette_count[event.group_id]
    roulette_time[event.group_id] = time.time()
    roulette_player[event.group_id].append(event.user_id)

    if shot_msg_count == 6 and random.random() < 0.125:
        roulette_status[event.group_id] = 0
        roulette_player[event.group_id] = []
        await roulette_msg.finish('我的手中的这把武器，找了无数工匠都难以修缮如新。不......不该如此......')

    elif roulette_status[event.group_id] > 0:
        await roulette_msg.finish(shot_text[shot_msg_count - 1] + f"( {shot_msg_count} / 6 )")

    roulette_status[event.group_id] = 0

    async def let_the_bullets_fly():
        await asyncio.sleep(random.randint(5, 20))

    if BotConfig(event.self_id, event.group_id).drunkenness() <= 0:
        roulette_player[event.group_id] = []
        shot_awaitable = await shot(event.self_id, event.user_id, event.group_id)
        if shot_awaitable:
            reply_msg = MessageSegment.text('米诺斯英雄们的故事......有喜剧，便也会有悲剧。舍弃了荣耀，') + MessageSegment.at(
                event.user_id) + MessageSegment.text('选择回归平凡......')
            await roulette_msg.send(reply_msg)
            await let_the_bullets_fly()
            await shot_awaitable()
        else:
            reply_msg = '听啊，悲鸣停止了。这是幸福的和平到来前的宁静。'
            await roulette_msg.finish(reply_msg)

    else:
        player = roulette_player[event.group_id]
        rand_list = player[-random.randint(1, min(len(player), 6)):][::-1]
        roulette_player[event.group_id] = []
        shot_awaitable_list = []
        for user_id in rand_list:
            shot_awaitable = await shot(event.self_id, user_id, event.group_id)
            if not shot_awaitable:
                continue

            shot_awaitable_list.append(shot_awaitable)

            reply_msg = MessageSegment.text('米诺斯英雄们的故事......有喜剧，便也会有悲剧。舍弃了荣耀，') + MessageSegment.at(
                user_id) + MessageSegment.text(f'选择回归平凡...... ( {len(shot_awaitable_list)} / 6 )')
            await roulette_msg.send(reply_msg)

        if not shot_awaitable_list:
            return

        await let_the_bullets_fly()
        for shot_awaitable in shot_awaitable_list:
            await shot_awaitable()

request_cmd = on_request(
    priority=15,
    block=False,
)

@request_cmd.handle()
async def _(bot: Bot, event: GroupRequestEvent, state: T_State):
    if event.sub_type == 'add' and event.user_id in kicked_users[event.group_id]:
        kicked_users[event.group_id].remove(event.user_id)
        await event.approve(bot)

async def is_drink_msg(bot: Bot, event: GroupMessageEvent, state: T_State) -> bool:
    if roulette_status[event.group_id] != 0 and event.get_plaintext().strip() in ['牛牛喝酒', '牛牛干杯', '牛牛继续喝']:
        admin = await am_I_admin_by_cache(bot, event, state)
        return admin

    return False

drink_msg = on_message(
    priority=4,
    block=False,
    rule=Rule(is_drink_msg),
    permission=permission.GROUP
)

@drink_msg.handle()
async def _(bot: Bot, event: GroupMessageEvent, state: T_State):
    roulette_player[event.group_id].append(event.user_id)