import json
from sklearn.feature_extraction.text import CountVectorizer
from sklearn.metrics import euclidean_distances
import numpy as np
from sklearn.linear_model import LogisticRegression

def load():
    with open('C:/Users/Administrator/Desktop/backend-interview-1/samples/generated_test_cases.txt', 'r',
              encoding='utf-8') as json_file:
        data = json.load(json_file)
        return data

def get_word_embeddings():
    embeddings = {}
    with open('C:/Users/Administrator/Desktop/numberbatch-en.txt', encoding='utf-8') as em:
        for embed in em:
            em_line = embed.split(' ')
            if len(em_line) > 2:
                word = em_line[0]
                embedding = np.array(em_line[1:])
                embeddings[word] = embedding
    print('Word embeddings:', len(embeddings))
    return embeddings

def get_min(target_list):
    min_num = target_list[0]
    for i in target_list:
        if i < min_num:
            min_num = i
    return min_num

def get_min_index(target_list):
    min_num = target_list[0]
    for i in target_list:
        if i < min_num:
            min_num = i
    return target_list.index(min_num)

def find_match_document_distance(query, candidates, W):
    if len(query) == 0:
        return "The value of query is null."
    if len(candidates) == 0:
        return "The value of candidates is null."

    vec1 = CountVectorizer(stop_words="english").fit([str(query)])
    vec2 = CountVectorizer(stop_words="english").fit([str(candidates)])

    if len(vec1.get_feature_names()) == 0 or len(vec2.get_feature_names()) == 0:
        return -1

    # print("Query Features:", ", ".join(vec1.get_feature_names()))
    # print("Candidates Features:", ", ".join(vec2.get_feature_names()))

    W1 = [W[w] for w in vec1.get_feature_names()]
    W2 = [W[w] for w in vec2.get_feature_names()]

    result = []
    for i in range(0, len(W1)):
        for j in range(0, len(W2)):
            res = {}
            res['q'] = vec1.get_feature_names()[i]
            res['c'] = vec2.get_feature_names()[j]
            res['r'] = float(euclidean_distances([W1[i]], [W2[j]])[0][0])
            result.append(res)

    t_list = []
    for i in range(0, len(result)):
        t_list.append(float(result[i]['r']))

    return get_min(t_list)

def get_model(W):
    json_data = load()
    print(len(json_data))

    list_X = []
    list_Y = []
    try:
        for i in range(0, len(json_data)):
            aaa = find_match_document_distance(str(json_data[i]['query']), str(json_data[i]['candidates']), W)
            list_X.append(aaa)
            if json_data[i]['correct_index'] >= 0:
                list_Y.append("1")
            else:
                list_Y.append("-1")
    except Exception as e:
        print(i)
        print("word_embeddings中未含有该词")

    print(len(list_X))
    print(len(list_Y))

    x = np.array(list_X).reshape(-1, 1)
    y = np.array(list_Y)

    lr = LogisticRegression(C=1000.0, random_state=0)
    lr.fit(x, y)
    # print(lr.predict(x))
    from sklearn.model_selection import cross_val_score
    acy = cross_val_score(lr, x, y)
    print(acy.mean())
    return lr

def find_best_match_with_threshold(query, candidates, lr, W):

    if len(query) == 0:
        return "The value of query is null."
    if len(candidates) == 0:
        return "The value of candidates is null."

    vec1 = CountVectorizer(stop_words="english").fit([query])
    vec2 = CountVectorizer(stop_words="english").fit([str(candidates)])

    if len(vec1.get_feature_names()) == 0 or len(vec2.get_feature_names()) == 0:
        return -1

    print("Features:", ", ".join(vec1.get_feature_names()))
    print("Features:", ", ".join(vec2.get_feature_names()))

    W1 = [W[w] for w in vec1.get_feature_names()]
    W2 = [W[w] for w in vec2.get_feature_names()]

    result = []
    for i in range(0, len(W1)):
        for j in range(0, len(W2)):
            res = {}
            res['q'] = vec1.get_feature_names()[i]
            res['c'] = vec2.get_feature_names()[j]
            res['r'] = float(euclidean_distances([W1[i]], [W2[j]])[0][0])
            result.append(res)

    t_list = []
    for i in range(0, len(result)):
        t_list.append(float(result[i]['r']))

    # print(t_list)
    # print(lr.predict(get_min(t_list))[0])
    if lr.predict(get_min(t_list))[0] == "-1":
        return -1
    else:
        return candidates.index(result[get_min_index(t_list)]['c'])

W = get_word_embeddings()
lr = get_model(W)
query = "i am really hungry"
candidates = ['story', 'song', 'wake up', 'restart']
print(find_best_match_with_threshold(query, candidates, lr, W))


import numpy as np
from numpy import linalg as LA
#
# a = np.array( [[1,2],
# 			  [2,4]] )
# b = np.array( [[2,3],
# 				[3,4]])
#
# print(np.matmul(a,b))

# Excercise 1.1 => 3b
array_1 = np.array([0,-1,-2])
array_2 = np.array([1,-3])
array_3 = np.array([1,-3])
array_4 = np.array([1,-3])
array_5 = np.array([1,2])
array_6 = np.array([1,-2])

x = np.dot(np.transpose(array_3),array_4)
y = np.dot(array_5,np.transpose(array_6))
xy = np.dot(x,y)

infinit_norm = LA.norm(array_1, np.inf)
euclid_norm = LA.norm(array_2,2)
resultHalf = np.dot((infinit_norm**2),(euclid_norm**-2))
result = np.dot(resultHalf,xy)
print(result);
#
# # In[3]:
#
#
# x = 3 + 3j
# y = 4 + 5j
# print(x+y)
#
#
# # In[4]:
#
#
# # exp(2)
#
#
# # In[5]:
#
#
# np.exp(2)
#
#
# # In[6]:
#
#
# # print(2i + 4i)
#
#
# # In[7]:
#
#
# print(2j + 4j)
#
#
# # In[8]:
#
#
# print(a*2)
#
#
# # In[9]:
#
#
# z= a*2
#
#
# # In[10]:
#
#
# print()
#
#
# # In[11]:
#
#
# print(z)
#
#
# # In[12]:
#
#
# print(a)
#
#
# # In[13]:
#
#
# a1 = a
#
#
# # In[14]:
#
#
# print(a)
#
#
# # In[15]:
#
#
# print(a1)
#
#
# # In[16]:
#
#
# a1 = np.array([1,-3])
# a2 = np.array([[1], [-3]])
# print(np.matmul(a1,a2))
#
#
# # In[17]:
#
#
# print(max
#      )
#
#
# # In[18]:
#
#
# print(np.max)
#
# # In[27]:
#
#
# print(np.finfo(np.float64).max)
#
#
# # In[28]:
#
#
# print(np.finfo(np.float32).max)
#
#
# # In[29]:
#
#
# print(np.finfo(np.float64).tiny)
#
#
# # In[30]:
#
#
# print(np.finfo(np.float32).max)
#
#
# # In[31]:
#
#
# print(np.finfo(np.float64).precision)
#
#
# # In[32]:
#
#
# print(np.finfo(np.float32).precision)
#
#
# # In[33]:
#
#
# print(np.finfo(np.double).precision)
#
#
# # In[34]:
#
#
# print(np.finfo(np.double).tiny)
#
#
# # In[35]:
#
#
# print(np.finfo(np.float).max)
#
#
# # In[36]:
#
#
# print(np.finfo(np.float64).max)
#
#
# # In[37]:
#
#
# print(np.finfo(np.double).max)
#
#
# # In[44]:
#
#
# from numpy import linalg
# m0 = np.array([[0],[-1],[-2]])
# linalg.norm(m0, np.inf)
#
#
# # In[60]:
#
#
# from numpy import linalg as LA
# m0 = np.array([1,-3,2])
# m1 = np.array([[1],[-1]])
# m1 = m1.transpose()
# m2 = np.array([[1,2,0],
#               [-3, -2 ,1]])
# m3 = np.array([1,-3,2])
# res = LA.norm(m0, 1) + np.matmul(m1,np.matmul(m2,m3))
# print(res)
#
#
# # In[84]:
#
#
#
# m0 = np.array([0,-1,-2])
# m1 = np.array([1,-3])
# m2 = np.array([[1],[-3]])
# m3 = np.array([[1],[-3]])
# m4 = np.array([[1],[2]])
# m5 = np.array([[1],[-2]])
# res = np.power(LA.norm(m0, np.inf) ,2) * np.power(LA.norm(m1, 2), -2) * np.multiply(np.matmul(m2.transpose(),m3), np.matmul(m4,m5.transpose()) )
# print(res)
#
# # @ can be used instead of np.matmul


def uglyNumber(n):
    if n<=0:
        return False
    while not n%2:
        n//=2
    while not n%3:
        n//=3
    while not n%5:
        n//=5
    return n==1

assert True == uglyNumber(1)
assert False == uglyNumber(-2125563)
assert True == uglyNumber(1024)
assert False == uglyNumber(19)


import time
import tqdm
import torch
import numpy as np
import torch.nn as nn
import torch.optim as optim
from utils import *

def prepare_sequence(seq, word2idx):
    idxs = [word2idx[w] for w in seq]
    return torch.tensor(idxs, dtype=torch.long)

class BiLSTM_CRF_S(nn.Module):
    def __init__(self, vocab_size, label2idx, embedding_dim, hidden_size, num_layers, dropout_ratio=0.3):
        super(BiLSTM_CRF_S, self).__init__()
        self.vocab_size = vocab_size
        self.labelset_size = len(label2idx)
        self.embedding_dim = embedding_dim
        self.label2idx = label2idx
        self.hidden_size = hidden_size

        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        # 非BatchFirst在实际其实更方便...
        self.LSTM = nn.LSTM(
            input_size=embedding_dim,
            hidden_size=hidden_size // 2,
            num_layers=num_layers,
            bidirectional=True,
            # batch_first=True
        )

        # 把output转化为label
        self.output2label = nn.Linear(hidden_size, self.labelset_size)

        # 标签的转移得分
        # transitons[i, j] 表示 从 i 转移到 j 的得分
        self.transitions = nn.Parameter(
            torch.randn(self.labelset_size, self.labelset_size, requires_grad=True))

        # 不可能从STOP转移到其他标签，也不可能从其他标签转移到START
        # 必须要加detach
        self.transitions.detach()[label2idx[STOP_label], :] = -10000
        self.transitions.detach()[:, label2idx[START_label]] = -10000

        self.dropout1 = nn.Dropout(p=dropout_ratio)
        self.dropout2 = nn.Dropout(p=dropout_ratio)

        self.hidden = None
        self.seq_length = None
        self.batch_size = None

    def init_uniform(self):
        for ind in range(0, self.LSTM.num_layers):
            weight = eval('self.LSTM.weight_ih_l' + str(ind))
            bias = np.sqrt(6.0 / (weight.size(0) / 4 + weight.size(1)))
            nn.init.uniform_(weight, -bias, bias)
            weight = eval('self.LSTM.weight_hh_l' + str(ind))
            bias = np.sqrt(6.0 / (weight.size(0) / 4 + weight.size(1)))
            nn.init.uniform_(weight, -bias, bias)

        if self.LSTM.bias:
            for ind in range(0, self.LSTM.num_layers):
                weight = eval('self.LSTM.bias_ih_l' + str(ind))
                weight.data.zero_()
                weight.data[self.LSTM.hidden_size: 2 * self.LSTM.hidden_size] = 1
                weight = eval('self.LSTM.bias_hh_l' + str(ind))
                weight.data.zero_()
                weight.data[self.LSTM.hidden_size: 2 * self.LSTM.hidden_size] = 1

        bias = np.sqrt(3.0 / self.embedding_dim)
        nn.init.uniform_(self.embeddings.weight, -bias, bias)

    def init_hidden(self):
        r"""
        初始化隐藏层参数
        :param batch_size:  batch_size
        :return:
        """
        return (torch.randn(2, self.batch_size, self.hidden_size // 2),
                torch.randn(2, self.batch_size, self.hidden_size // 2))

    def _get_scores(self, sentences):
        '''
        得到序列的特征
        :param sentences: tensor [length, batch_size]
        :return: feats tensor [length, batch_size, labelset_size]
        '''

        self.hidden = self.init_hidden()
        # [length, batch_size] -> [length, batch_size, dim]
        sentences_embeddings = self.embeddings(sentences)
        sentences_embeddings = self.dropout1(sentences_embeddings)

        # outputs [length, batch_size, hidden_size]
        outputs, self.hidden = self.LSTM(sentences_embeddings, self.hidden)
        outputs = self.dropout2(outputs)

        # [length, batch_size, labelset_size]
        feats = self.output2label(outputs)
        return feats

    def _forward_all_logsumexp(self, scores, masks):
        r"""
        计算所有可能路径的log_sum_exp
        :param scores: tensor [length, batch_size, labelset_size]
            LSTM传过来的emit score
        :param masks: tensor [length, batch_size]
        :return: terminal_score: tensor [batch_size]
        """

        # 到当前单词，且状态为i的所有路径的log_sum_exp
        dp = torch.full((self.labelset_size, self.batch_size), -10000.)
        dp[self.label2idx[START_label]] = 0.

        for i in range(self.seq_length):
            # [batch_size, labelset_size]
            score = scores[i]
            # [batch_size] -> [batch_size, 1] -> [batch_size, labelset_size]
            mask = masks[i].unsqueeze(dim=1).expand(self.batch_size, self.labelset_size)
            # [labelset_size_from, batch_size, labelset_size_to]
            tmp = dp.transpose(0, 1).unsqueeze(dim=2).expand(self.labelset_size, self.batch_size, self.labelset_size) + \
                  score.unsqueeze(dim=0).expand(self.labelset_size, self.batch_size, self.labelset_size) + \
                  self.transitions.unsqueeze(dim=1).expand(self.labelset_size, self.batch_size, self.labelset_size)
            # [labelset_size_from, batch_size, labelset_size_to] -> [batch_size, labelset_size_to]
            tmp = log_sum_exp(tmp, dim=0)
            # mask为1的值更新，为0的不再更新
            dp.masked_scatter_(mask, tmp.masked_select(mask))

        # dp = dp + self.transitions[self.label2idx[STOP_label]]
        dp = log_sum_exp(dp, dim=1)

        return dp

    def _get_gold_score(self, scores: torch.tensor, labels, masks):
        '''
        计算出所提供的正确路径得分数
        :param scores: tensor [length, batch_size, labelset_size]
            LSTM传过来的emit score
        :param labels: tensor [length, batch_size]
            每个序列正确的路径, 已经加了start
        :param masks: tensor [length, batch_size]
        :return:
           scores: tensor [batch_size]
        '''

        dp = torch.zeros(self.batch_size)

        st = torch.full([1, self.batch_size], self.label2idx[START_label], dtype=torch.long)
        # [length + 1, batch_size]
        labels = torch.cat(
            [st, labels], dim=0
        )
        for i in range(self.seq_length):
            # [batch_size, labelset_size]
            score = scores[i]
            # [batch_size]
            mask = masks[i]
            tmp = dp + self.transitions[labels[i], labels[i + 1]] + score[
                range(self.batch_size), labels[i + 1]]
            # mask为1的值更新为新的tmp值，为0的不再更新
            dp.masked_scatter_(mask, tmp.masked_select(mask))

        # label最后一个永远是pad....
        # dp = dp + self.transitions[labels[-1], self.label2idx[STOP_label]]
        # print(time.time() - st)

        return dp

    def neg_log_likelihood(self, sentences, labels, masks):
        r"""
        计算正确路径的负对数似然概率

        :param sentences: tensor [length, batch_size]
        :param labels: tensor [length, batch_size]
            正确的label序列
        :param masks:tensor [length, batch_size]
        :return: FloatTensor
        """
        self.set_batch_seq_size(sentences)

        # [length, batch_size, labelset_size]
        feats = self._get_scores(sentences)

        forward_score = self._forward_all_logsumexp(feats, masks)
        gold_score = self._get_gold_score(feats, labels, masks)

        # print('forward_score: ', forward_score)
        # print('gold_score   :', gold_score)

        return (forward_score - gold_score).sum() / self.batch_size

    def _viterbi_decode(self, feats, masks):
        r'''
        使用维特比算法进行解码，找到最可能的序列结果

        :param feats: tensor [length, batch_size, labelset_size]
            LSTM传过来的特征向量
        :param masks: tensor [length, batch_size]
        :return: best_scores tensor [batch_size]
                best_paths tensor [length, batch_size]
        '''

        # 记录每个节点由哪个父节点转移过来
        parents = []
        # 到当前单词，且状态为i的所有路径中log_sum_exp最大的值
        dp = torch.full((self.labelset_size, self.batch_size), -10000.)
        # START_label has all of the score.
        dp[self.label2idx[START_label]] = 0.
        for i in range(feats.shape[1]):
            # [batch_size, labelset_size]
            feat = feats[i]
            # [batch_size] -> [batch_size, 1]
            mask = masks[i].unsqueeze(dim=1)
            # [labelset_size, batch_size, labelset_size]
            # TODO: 搞清楚这些维数！！
            tmp = dp.transpose(0, 1).unsqueeze(dim=2) + \
                  feat.unsqueeze(dim=0) + \
                  self.transitions.unsqueeze(dim=1)
            max_scores, best_choose = tmp.max(dim=0)
            # 添加路径信息，[batch_size, labelset_size]
            parents.append(best_choose)

            # 由于只保留了一条路径，可以省去log_sum_exp过程
            dp.masked_scatter_(mask, max_scores.masked_select(mask))

        # [batch_size, labelset_size]
        # terminal_var = dp + self.transitions[:, self.label2idx[STOP_label]]
        terminal_var = dp

        # [batch_size]
        best_scores, best_path_labels = terminal_var.max(dim=1)

        best_paths = [best_path_labels]
        for parent in reversed(parents):
            best_path_labels = parent[range(parent.shape[0]), best_paths[-1]]
            best_paths.append(best_path_labels)

        best_paths.pop()
        best_paths.reverse()

        # 转化为 [batch_size, length]
        best_paths = torch.stack(best_paths).transpose(0, 1)
        return best_scores, best_paths

    def set_batch_seq_size(self, sentence):
        """
        set batch size and sequence length
        """
        tmp = sentence.size()
        self.batch_size = tmp[1]
        self.seq_length = tmp[0]

    def predict(self, sentences, masks):
        r"""
        预测数据的最可能序列以及得分

        :param sentences: tensor [length, batch_size]
        :return:
            scores: tensor [batch_size]
            paths: list [tensor: [batch_size]....] with length=length
        """
        self.set_batch_seq_size(sentences)

        # Get the emission scores from the BiLSTM
        feats = self._get_scores(sentences)

        # Find the best path, given the features.
        scores, paths = self._viterbi_decode(feats, masks)
        return scores, paths

class BiLSTM_CRF_L(nn.Module):
    r"""
    Large LSTM，直接使用nn.Linear(hidden_dim, self.labelset_size * self.labelset_size)
        代替了转移矩阵，并且在制作数据集的时候采用label_i * labelset_size + label_(i + 1)
        的方法，可以一次计算出gold score，在之后也不用每次加trans，大大提高了运行速度，但是
        内存占用更大
    """

    def __init__(self, vocab_size, label2idx, embedding_dim, hidden_dim, num_layers, dropout_ratio):
        super(BiLSTM_CRF_L, self).__init__()
        self.embedding_dim = embedding_dim
        self.hidden_dim = hidden_dim
        self.vocab_size = vocab_size

        self.embeddings = nn.Embedding(vocab_size, embedding_dim)
        self.LSTM = nn.LSTM(embedding_dim, hidden_dim // 2,
                            num_layers=num_layers, bidirectional=True)
        self.num_layers = num_layers

        self.dropout1 = nn.Dropout(p=dropout_ratio)
        self.dropout2 = nn.Dropout(p=dropout_ratio)

        self.labelset_size = len(label2idx)
        self.label2idx = label2idx

        self.start_tag = label2idx[START_label]
        self.end_tag = label2idx[PAD_label]

        self.batch_size = 1
        self.seq_length = 1

        self.hidden2tag = nn.Linear(hidden_dim, self.labelset_size * self.labelset_size)

    def init_uniform(self):
        # LSTM
        r"""
        线性初始化网络
        :return:
        """
        for ind in range(0, self.LSTM.num_layers):
            weight = eval('self.LSTM.weight_ih_l' + str(ind))
            bias = np.sqrt(6.0 / (weight.size(0) / 4 + weight.size(1)))
            nn.init.uniform_(weight, -bias, bias)
            weight = eval('self.LSTM.weight_hh_l' + str(ind))
            bias = np.sqrt(6.0 / (weight.size(0) / 4 + weight.size(1)))
            nn.init.uniform_(weight, -bias, bias)

        if self.LSTM.bias:
            for ind in range(0, self.LSTM.num_layers):
                weight = eval('self.LSTM.bias_ih_l' + str(ind))
                weight.data.zero_()
                weight.data[self.LSTM.hidden_size: 2 * self.LSTM.hidden_size] = 1
                weight = eval('self.LSTM.bias_hh_l' + str(ind))
                weight.data.zero_()
                weight.data[self.LSTM.hidden_size: 2 * self.LSTM.hidden_size] = 1

        # embedding
        # nn.Embeddig.weight默认初始化方式就是N(0, 1)分布
        bias = np.sqrt(3.0 / self.embedding_dim)
        nn.init.uniform_(self.embeddings.weight, -bias, bias)

        # Linear
        bias = np.sqrt(6.0 / (self.hidden2tag.weight.size(0) +
                              self.hidden2tag.weight.size(1)))
        nn.init.uniform_(self.hidden2tag.weight, -bias, bias)

    def rand_init_hidden(self):
        """
        随机初始化hidden
        """
        return torch.Tensor(
            torch.randn(2 * self.num_layers, self.batch_size, self.hidden_dim // 2)), torch.Tensor(
            torch.randn(2 * self.num_layers, self.batch_size, self.hidden_dim // 2))

    def set_batch_seq_size(self, sentence):
        """
        :param sentence [length, batch_size]
        设置batch_size，seq_length
        """
        tmp = sentence.size()
        self.seq_length = tmp[0]
        self.batch_size = tmp[1]

    def load_pretrained_embedding(self, pre_embeddings):
        """
        加载预训练embedding
        """
        assert (pre_embeddings.size()[1] == self.embedding_dim)
        self.embeddings.weight = nn.Parameter(pre_embeddings)

    def _get_gold_score(self, scores, targets, masks):
        r"""
        计算正确路径得分

        :param scores: [length, batch_size, labelset_size, labelset_size]
        :param targets: [length, batch_size]
        :param masks: [length, batch_size]
        :return: gold_score tensor
        """
        # [length, batch_size] -> [length, batch_size, 1]
        targets = targets.unsqueeze(dim=2)
        gold_score = torch.gather(scores.view(
            self.seq_length, self.batch_size, -1), 2, targets).view(self.seq_length,
                                                                    self.batch_size)  # seq_len * batch_size
        gold_score = gold_score.masked_select(masks).sum()
        return gold_score

    def _get_all_logsumexp(self, scores, masks):
        r"""
        计算所有路径的得分之和

        :param scores: [length, batch_size, labelset_size, labelset_size]
        :param masks: [length, batch_size]
        :return:
        """
        seq_iter = enumerate(scores)
        # [batch_size, labelset_size_from, labelset_size_to]
        _, inivalues = seq_iter.__next__()

        # [batch_size, labelset_size_to], ps: 不加clone会报错
        # 到当前单词，且状态为i的所有路径的log_sum_exp
        dp = inivalues[:, self.start_tag, :].clone()

        # 从正式的第一个label开始迭代
        for idx, cur_values in seq_iter:
            # [batch_size] -> [batch_size, labelset_size]
            mask = masks[idx].view(self.batch_size, 1).expand(self.batch_size, self.labelset_size)
            # cur_values: [batch_size, labelset_size_from, labelset_size_to]
            cur_values = cur_values + dp.contiguous().view(self.batch_size, self.labelset_size,
                                                           1).expand(self.batch_size, self.labelset_size,
                                                                     self.labelset_size)
            # [batch_size, from_target, to_target] -> [batch_size, to_target]
            tmp = log_sum_exp(cur_values, dim=1)

            # 0保留自身值，1采用新的source值
            dp.masked_scatter_(mask, tmp.masked_select(mask))

        dp = dp[:, self.end_tag].sum()

        return dp

    def neg_log_likelihood(self, sentences, targets, masks, hidden=None):
        r"""
        计算损失函数
        :param sentences: [length, batch_size]
        :param targets: [length, batch_size]
        :param masks: [length, batch_size]
        :param hidden:
        :return:
        """
        # [length, batch_size, labelset_size, labelset_size]
        crf_scores = self.forward(sentences)
        gold_score = self._get_gold_score(crf_scores, targets, masks)

        forward_score = self._get_all_logsumexp(crf_scores, masks)

        loss = (forward_score - gold_score) / self.batch_size
        # print(loss)

        return loss

    def _viterbi_decode(self, crf_scores, masks):
        r'''
        使用维特比算法进行解码，找到最可能的序列结果

        :param crf_scores: tensor [length, batch_size, labelset_size, labelset_size]
            LSTM传过来的emit score + trans score
        :param masks: tensor [length, batch_size]
        :return: scores tensor [batch_size]
                 paths [batch_size, seq_length - 1]
        '''

        # 方便后面的mask fill
        masks = ~masks
        path = torch.LongTensor(self.seq_length - 1, self.batch_size)

        seq_iter = enumerate(crf_scores)
        # [batch_size, from_labelset_size, to_labelset_size]
        _, inivalues = seq_iter.__next__()
        # 只保留start的初始得分, [batch_size, to_labelset_size]
        forscores = inivalues[:, self.start_tag, :].clone()

        parents = []
        # 从正式的第一个label开始迭代
        for idx, cur_values in seq_iter:
            # [batch_size] -> [batch_size, labelset_size]
            mask = masks[idx].view(self.batch_size, 1).expand(self.batch_size, self.labelset_size)
            # cur_values: [batch_size, from_target, to_target]
            cur_values = cur_values + forscores.contiguous().view(self.batch_size, self.labelset_size,
                                                                  1).expand(self.batch_size, self.labelset_size,
                                                                            self.labelset_size)
            forscores, cur_parent = torch.max(cur_values, 1)
            # [batch_size, to_target], mask是1是直接pad
            cur_parent.masked_fill_(mask, self.end_tag)
            parents.append(cur_parent)

        pointer = parents[-1][:, self.end_tag]
        path[-1] = pointer
        for idx in range(len(parents) - 2, -1, -1):
            back_point = parents[idx]
            index = pointer.contiguous().view(-1, 1)
            pointer = torch.gather(back_point, 1, index).view(-1)
            path[idx] = pointer

        return forscores, path.transpose(0, 1)

    def predict(self, sentences, masks, hidden=None):
        r"""
        进行预测，计算得分和最优路径

        :param sentences: [length, batch_size]
        :param masks: [length, batch_size]
        :return:
        """
        self.eval()

        crf_scores = self.forward(sentences)
        scores, path = self._viterbi_decode(crf_scores, masks)

        return scores, path

    def forward(self, sentences, hidden=None):
        r"""
        计算crf_scores

        :param sentences: [length, batch_size]
        :param hidden: LSTM的初始隐藏层
        :return: crf_scores [length, batch_size, labelset_size_from, labelset_size_to]
            crf_scores[0, 0, 1, 10]: 第一个句的第一个单词 从label_1 -> label_10的emit_score + trans_score
        """
        self.set_batch_seq_size(sentences)
        embeds = self.embeddings(sentences)
        d_embeds = self.dropout1(embeds)
        # [length, batch_size, hidden_size]
        lstm_out, hidden = self.LSTM(d_embeds, hidden)
        lstm_out = lstm_out.view(-1, self.hidden_dim)

        d_lstm_out = self.dropout2(lstm_out)

        crf_scores = self.hidden2tag(d_lstm_out).view(-1, self.labelset_size, self.labelset_size)
        crf_scores = crf_scores.view(self.seq_length, self.batch_size, self.labelset_size, self.labelset_size)

        return crf_scores


import requests
from bs4 import BeautifulSoup

url = input("Entrer l'URL du site : ")
response = requests.get(url)

if response.status_code == 200:
    html_content = response.content
else:
    print("Erreur lors de la récupération de la page.")

soup = BeautifulSoup(html_content, "html.parser")

# Extraire le titre de la page
title = soup.title.text
print("Titre de la page :", title)

# Élément de la page à extraire
element = input("Entrez l'élément a extraire du site : ")
elements = soup.find_all(f"{element}")
for item in elements:
    print(item)

# Exemple : Extraire tous les liens de la page
# links = soup.find_all("a")
# for link in links:
#    print(link.get("href"))


import os
def read(archivo):
    try:
        archivo=archivo+".txt"
        print("El contenido es: ")
        file=open(archivo,"r")
        for line in file:
            print(line, end="")
    except FileNotFoundError:
        print("No se encontro")

def encontrar():
    count = 0
    for dirpath, dirnames, filenames in os.walk("."):
        for name in filenames:
            if ".txt" in name:
                print ("Archivo",count+1,":",name)
                count=count+1

def agregar(var):
    var=var+".txt"
    file=open(var,"a")
    while True:
        url=input("Escribe la url que quieres agregar al "+var+" ")
        file.write(url+"\n")
        con=input("Quieres agregar mas y/n ")
        if con!="y":
            break

def eliminar(var):
    read(var)
    var=var+".txt"
    dele=str(input("Que url quieres eliminar "))
    with open(var, "r") as f:
        lines = f.readlines()
    with open(var, "w") as f:
        for line in lines:
            if line.strip("\n") != dele:
                f.write(line)

if __name__=="__main__":
    arc=input("Escribe el archivo ")
    encontrar()
    read(arc)
    eliminar(arc)
    agregar(arc)


#The program should ask the user to enter three numbers (one number at a time) and should work out how many of these are even and odd. Finally, the program should display the number of even numbers and odd numbers entered.

# Ask user for numbers
print("Please enter the first whole number?")
first_number = int(input())

print("Please enter the second whole number?")
second_number = int(input())

print("Please enter the third whole number?")
third_number = int(input())

even_numbers = 0
odd_numbers = 0

# Determine which numbers are even and which are odd
if (first_number % 2 == 0):
    even_numbers = even_numbers + 1
else:
    odd_numbers = odd_numbers + 1

if (second_number % 2 == 0):
    even_numbers = even_numbers + 1
else:
    odd_numbers = odd_numbers + 1

if (third_number % 2 == 0):
    even_numbers = even_numbers + 1
else:
    odd_numbers = odd_numbers + 1

# Display result
print("There were {} even and {} odd numbers.".format(even_numbers, odd_numbers))


#!/usr/bin/env python

import gzip
import sys
import tarfile
import threading
import urllib.request
import zipfile

import lib.download.task as task
import lib.independence.fs as fs
import lib.ui.color as printer

# The greater purpose of (functions in) this file is
# to download a list of DownloadTasks

class Downloader(object):
    '''Object to facilitate downloading'''

    # Constructor
    # tasklist is the list of DownloadTasks which should be downloaded
    def __init__(self, tasklist):
        self.tasklist = tasklist

    # Returns True if the name suggests it is a tar archive, otherwise False
    def is_tar(self, name):
        return name.endswith('.tar') or name.endswith('tar.xz')\
        or name.endswith('tar.gz') or name.endswith('.tgz')

    # Returns True if the name suggests it is a zip archive, otherwise False
    def is_zip(self, name):
        return name.endswith('.zip')

    # Returns True if the name suggests it is a gzip archive, otherwise False
    def is_gzip(self, name):
        return name.endswith('.gz')

    # Extract output of downloaded file, if it has a compatible format
    # task is the task for which the file is downloaded
    def extract(self, task):
        target = fs.join(task.directory,task.name)
        basicstring = printer.format('extracter', printer.Color.CAN)
        extractstring = printer.format('extracting', printer.Color.YEL)
        print('[{0}] {1} {2}'.format(basicstring, extractstring, task.name))

        if self.is_tar(task.url):
            ttar = tarfile.open(target, 'r')
            ttar.extractall(path=task.directory)
        elif self.is_zip(task.url):
            tzip = zipfile.ZipFile(target, 'r')
            tzip.extractall(task.directory)
            tzip.close()
        elif self.is_gzip(task.url):
            with gzip.open(target, 'rb') as f_in:
                with open(task.directory, 'wb') as f_out:
                    fs.cp(f_in, f_out)
        else:
            return

        finishedstring = printer.format('extracted', printer.Color.GRN)
        print('[{0}] {1} {2}'.format(basicstring, finishedstring, task.name))

        fs.rm(fs.join(task.directory, task.name))

        dircontents = fs.ls(task.directory)
        if len(dircontents) == 1 and fs.isdir(task.directory,dircontents[0]):
            subdircontents = fs.ls(task.directory,dircontents[0])
            for file in subdircontents:
                path = fs.join(task.directory,dircontents[0])
                fs.mv(fs.join(path,file), task.directory)
            fs.rm(task.directory, dircontents[0], ignore_errors=True)

    # Downloads a DownloadTask and prints some user information
    # task is the downloadtask which contains the download information
    def download(self, task):
        basicstring = printer.format('downloader', printer.Color.CAN)
        downloadstring = printer.format('downloading', printer.Color.YEL)
        print('[{0}] {1} {2}'.format(basicstring, downloadstring, task.name))

        u = urllib.request.urlopen(task.url)
        with open(fs.join(task.directory,task.name), 'wb') as out_file:
            out_file.write(u.read())

        finishedstring = printer.format('downloaded', printer.Color.GRN)
        print('[{0}] {1} {2}'.format(basicstring, finishedstring, task.name))

    # Parallel function, which is called to download all tasks
    # task is the downloadtask which should be operated on
    def parallel_exec(self, task):
        self.download(task)

        if self.is_tar(task.url)\
        or self.is_zip(task.url)\
        or self.is_gzip(task.url):
            self.extract(task)

    # Main function to call. Each DownloadTask will be performed
    def download_all(self):
        threads = []
        for task in self.tasklist:
            threads.append(threading.Thread(target=self.parallel_exec, args=(task,)))

        for thread in threads:
            thread.start()
        for thread in threads:
            thread.join()


import mtcnn
from mtcnn.mtcnn import MTCNN
import cv2

detector = MTCNN()  # MTCNN is CNN based algorithm

video = cv2.VideoCapture(0)
video.set(3,2000)
video.set(4,3000) # Same as previous technique

while (True):
    ret, frame = video.read()
    if ret == True:
        location = detector.detect_faces(frame)  # dectect faces frame by frame
        if len(location) > 0:
            for face in location:
                x, y, width, height = face['box']
                x2, y2 = x + width, y + height
                cv2.rectangle(frame, (x, y), (x2, y2), (0, 0, 255), 4) # Bounding box width and height
        cv2.imshow("Output",frame) # its will show frame's and update it frame by frame to same output file named as "Output"
        if cv2.waitKey(1) & 0xFF == ord(' '):   # same as previous
            break
    else:
        break

video.release()  # releasing camera port
cv2.destroyAllWindows() # destroying all windows

# Lets run and see , Results
# as you can see, it is giving precise result

# so we will go with MTCNN Algorithm


#  -*-coding:utf8-*-

################################################################################
#
#
#
################################################################################
"""
模块用法说明:达人推荐详情页

Authors: Turinblueice
Date: 2016/9/10
"""

from base import base_frame_view
from util import log

from gui_widgets.basic_widgets import frame_layout
from gui_widgets.basic_widgets import image_view
from gui_widgets.basic_widgets import text_view
from gui_widgets.basic_widgets import linear_layout
from gui_widgets.basic_widgets import recycler_view
from gui_widgets.basic_widgets import radio_button

from appium.webdriver import WebElement
from appium.webdriver.common import touch_action
from selenium.webdriver.common.touch_actions import TouchActions
from activities import activities

from appium.webdriver.common.mobileby import MobileBy
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import TimeoutException
import time

class TalentRecommendActivity(base_frame_view.BaseFrameView):

    """
        Summary:
            达人推荐页面

        Attributes:

    """
    name = '.module.square.activity.EretarActivity'  # 裁剪图片activity名称

    def __init__(self, parent):
        super(TalentRecommendActivity, self).__init__(parent)

        #  等待初始化
        self.wait_for_element_present(self.base_parent, id='com.jiuyan.infashion:id/login_tv_title')

        self._scroll_view = recycler_view.RecyclerView(self.parent, id='com.jiuyan.infashion:id/square_rv_tag')

    @property
    def talent_recommend(self):
        """
            Summary:
                达人推荐按钮
        """
        id_ = 'com.jiuyan.infashion:id/login_tv_title'
        return text_view.TextView(self.parent, id=id_)

    @property
    def talent_apply(self):
        """
            Summary:
                申请达人按钮
        """
        id_ = 'com.jiuyan.infashion:id/login_tv_title_right'
        return text_view.TextView(self.parent, id=id_)

    @property
    def back_button(self):
        """
            Summary:
                返回按钮
        """
        id_ = 'com.jiuyan.infashion:id/login_tv_title_left'
        return text_view.TextView(self.parent, id=id_)

    @property
    def talent_list(self):
        """
            Summary:
                达人列表
        :return:
        """
        xpath_ = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                 'android.widget.LinearLayout'
        return TalentContainerList(self.base_parent, xpath=xpath_).item_list

    @property
    def category_list(self):
        """
            Summary:
                种类列表
        :return:
        """
        xpath_ = '//android.widget.ListView[@resource-id="com.jiuyan.infashion:id/square_rv_menu"]/' \
                 'android.widget.LinearLayout'
        return TalentCategoryList(self.base_parent, xpath=xpath_).item_list

    # **************************操作方法*****************************
    def wait_for_talent(self, timeout=10):
        """
            Summary:
                显示等待达人加载完毕
        :return:
        """
        xpath_ = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                 'android.widget.LinearLayout'
        if self.wait_for_element_present(self.base_parent, timeout=timeout, xpath=xpath_):
            log.logger.info("达人列表已记载")
            return True
        log.logger.error("达人列表加载失败")
        return False

    def tap_back_button(self):
        """
            Summary:
                点击返回按钮
        """
        log.logger.info("开始点击返回按钮")
        self.back_button.tap()
        log.logger.info("完成返回按钮点击")
        if self.wait_activity(activities.ActivityNames.SQUARE_CATEGORY, 10):
            log.logger.info("成功返回到话题分类页面")
            return True
        log.logger.error("返回失败")
        return False

class TalentContainer(base_frame_view.BaseFrameView):
    """
        Summary:
            达人推荐
    """
    def __init__(self, parent, item=None, index=None, **kwargs):
        super(TalentContainer, self).__init__(parent)
        self._layout_view = item if isinstance(item, WebElement) else self.find_element(**kwargs)
        self._index = index
        self._xpath = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                      'android.widget.LinearLayout[{}]'.format(self._index+1)

    @property
    def talent_name(self):
        """
            Summary:
                达人姓名
        """
        id_ = 'com.jiuyan.infashion:id/square_tv_name'
        return text_view.TextView(self._layout_view, id=id_).text

    @property
    def talent_avatar(self):
        """
            Summary:
                达人头像
        """
        id_ = 'com.jiuyan.infashion:id/transition_avatar_id'
        return image_view.ImageView(self._layout_view, id=id_)

    @property
    def follow_button(self):
        """
            Summary:
                关注按钮
        """
        id_ = 'com.jiuyan.infashion:id/square_tv_attention'
        return radio_button.RadioButton(self._layout_view, id=id_)

    @property
    def image_list(self):
        """
            Summary:
                图片列表
        """
        return image_view.ImageViewList(self._layout_view, id='com.jiuyan.infashion:id/login_iv_pic').image_list

    # ********************操作方法*************************

    def tap_avatar(self):
        """
            Summary:
                点击头像

        """
        curr_name = self.talent_name
        log.logger.info("开始点击\"{}\"的头像".format(curr_name))
        self.talent_avatar.tap()
        log.logger.info("点击完毕")
        if self.base_parent.wait_activity(activities.ActivityNames.DIARY_INFO, 10):
            log.logger.info("成功进入好友in记页面")
            return True
        log.logger.error("进入好友in记页面失败")
        return False

    def tap_image(self, index):
        """
            点击图片
        Args:
            index:
                图片序号
        Returns:

        """
        log.logger.info("点击第{}张".format(index+1))
        self.image_list[index].tap()
        log.logger.info("完成点击")
        if self.base_parent.wait_activity(activities.ActivityNames.FRIEND_PHOTO_DETAIL, 10):
            log.logger.info("成功进入好友照片页面")
            return True
        log.logger.error("进入好友照片页面失败")
        return False

    def tap_follow_button(self):
        """
            Summary:
                点击关注按钮
        """
        log.logger.info("开始点击关注")
        self.follow_button.tap()
        time.sleep(2)
        log.logger.info("点击完毕")
        try:
            WebDriverWait(self.base_parent, 10).until(
                EC.element_located_to_be_selected(
                    (MobileBy.XPATH, self._xpath+'/android.widget.RelativeLayout[1]/android.widget.RadioButton[1]')
                )
            )
            return True
        except:
            return False

class TalentContainerList(base_frame_view.BaseFrameView):

    def __init__(self, parent, **kwargs):
        super(TalentContainerList, self).__init__(parent)
        self.__list = self.find_elements(**kwargs)

    @property
    def item_list(self):

        if self.__list:
            return [TalentContainer(item.parent, item, index) for index, item in enumerate(self.__list)]
        return None

class TalentCategory(base_frame_view.BaseFrameView):
    """
        Summary:
            达人分类
    """
    def __init__(self, parent, item=None, **kwargs):
        super(TalentCategory, self).__init__(parent)
        self._layout_view = item if isinstance(item, WebElement) else self.find_element(**kwargs)

    @property
    def title(self):
        """
            Summary:
                达人分类的名称
        """
        id_ = 'com.jiuyan.infashion:id/square_tv_tag_menu'
        return text_view.TextView(self._layout_view, id=id_).text

    # ********************操作方法*************************

    def tap(self):
        """
            Summary:
                点击分类
        """
        title_ = self.title
        log.logger.info("开始点击\"{}\"".format(title_))
        self._layout_view.click()
        xpath_ = '//android.support.v7.widget.RecyclerView[@resource-id="com.jiuyan.infashion:id/square_rv_tag"]/' \
                 'android.widget.LinearLayout'
        if self.wait_for_element_present(self.base_parent, xpath=xpath_):
            # 点击左侧不同的达人类别后,等待右侧达人初始化加载
            log.logger.info("\"{}\"的达人已加载成功".format(title_))
            return True
        log.logger.error("达人列表初始化失败")
        return False

class TalentCategoryList(base_frame_view.BaseFrameView):

    def __init__(self, parent, **kwargs):
        super(TalentCategoryList, self).__init__(parent)
        self.__list = self.find_elements(**kwargs)

    @property
    def item_list(self):

        if self.__list:
            return [TalentCategory(item.parent, item) for item in self.__list]
        return None


from io import BytesIO
from PIL import Image
from uuid import uuid4

from django.core.files import File

JPEG_IMAGE_QUALITY = 100

def crop_image(image):
    im = Image.open(image)

    (height, width) = (im.height, im.width)
    shortest_side = min(height, width)
    dimensions = (0, 0, shortest_side, shortest_side)
    image_name = _generate_random_file_name()

    im = im.convert("RGB")
    im = im.crop(dimensions)

    _bytes = BytesIO()

    im.save(_bytes, "JPEG", quality=JPEG_IMAGE_QUALITY)

    return File(_bytes, image_name)

def _generate_random_file_name():
    return str(uuid4()) + ".jpg"


import yaml

from populate.populator.common.errors import ConfigurationError

from .projects_manager import ProjectsManager

def project_constructor(loader, node):
    if isinstance(node, yaml.ScalarNode):
        item = loader.construct_scalar(node)
        if not isinstance(item, str) or not item:
            raise ConfigurationError(
                'value {} cannot be interpreted as project'.format(item))
    elif isinstance(node, yaml.MappingNode):
        item = loader.construct_mapping(node)
        if not isinstance(item, dict) or not item:
            raise ConfigurationError(
                'value {} cannot be interpreted as project'.format(item))
    return ProjectsManager().get_object(item)


import tensorflow as tf
from model.model_builder import ModelBuilder
from utils.model_post_processing import merge_post_process
from tensorflow.keras.models import Model
from tensorflow.python.framework.convert_to_constants import convert_variables_to_constants_v2
from utils.priors import *
import argparse

parser = argparse.ArgumentParser()
parser.add_argument("--backbone_name",       type=str,    help="Pretrained backbone name\
                                                            |   model_name    : description | \
                                                            [ 1. mobilenetv2       : MobileNetV2 ]\
                                                            [ 2. mobilenetv3s      : MobileNetV3-Small ] \
                                                            [ 3. mobilenetv3l      : MobileNetV3-Large ] \
                                                            [ 4. efficient_lite_v0 : EfficientNet-Lite-B0 ]\
                                                            [ 5. efficientnetv2b0  : EfficientNet-V2-B0 ]\
                                                            [ 6. efficientnetv2b3  : EfficientNet-V2-B3 ]",
                    default='efficient_lite_v0')
parser.add_argument("--checkpoint_dir",      type=str,    help="Set the model storage directory",
                    default='./checkpoints/')
parser.add_argument("--model_weights",       type=str,    help="Saved model weights directory",
                    default='0906/_0906_efficient_lite_v0_display-detection_e200_lr0.001_b32_without-norm-small_prior-adam_best_loss.h5')
parser.add_argument("--num_classes",         type=int,    help="Set num classes for model and post-processing",
                    default=4)
parser.add_argument("--image_size",          type=tuple,  help="Set image size for priors and post-processing",
                    default=(300, 300))
parser.add_argument("--gpu_num",             type=int,    help="Set GPU number to use(When without distribute training)",
                    default=0)
parser.add_argument("--frozen_dir",          type=str,    help="Path to save frozen graph transformation result",
                    default='./checkpoints/converted_frozen_graph/')
parser.add_argument("--frozen_name",         type=str,    help="Frozen graph file name to save",
                    default='frozen_graph')
parser.add_argument("--include_postprocess",   help="Frozen graph file name to save",
                    action='store_true')
parser.add_argument("--load_keras_model",   help="Load model from Saved format(.pb) or Keras(.h5)",
                    action='store_true')

args = parser.parse_args()

if __name__ == '__main__':
    tf.config.set_soft_device_placement(True)
    tf.config.run_functions_eagerly(True)

    gpu_number = '/device:GPU:' + str(args.gpu_num)
    with tf.device(gpu_number):

        spec_list = convert_spec_list()
        priors = create_priors_boxes(specs=spec_list, image_size=args.image_size[0], clamp=True)
        target_transform = MatchingPriors(priors, center_variance, size_variance, iou_threshold)

        if args.load_keras_model:
            model = tf.keras.models.load_model('./checkpoints/pruning', compile=False)

        else:
            model = ModelBuilder(image_size=args.image_size,
                                 num_classes=args.num_classes,
                                 include_preprocessing=args.include_postprocess).build_model(args.backbone_name)
            model.load_weights(args.checkpoint_dir + args.model_weights, by_name=True)

        model.summary()

        if args.include_postprocess:
            detection_output = merge_post_process(detections=model.output,
                                                  target_transform=target_transform,
                                                  confidence_threshold=0.5,
                                                  classes=args.num_classes)
            model = Model(inputs=model.input, outputs=detection_output)

        # model.summary()

        #path of the directory where you want to save your model
        frozen_out_path = args.frozen_dir
        # name of the .pb file
        frozen_graph_filename = args.frozen_name
        # Convert Keras model to ConcreteFunction
        full_model = tf.function(lambda x: model(x))
        full_model = full_model.get_concrete_function(
            tf.TensorSpec(model.inputs[0].shape, model.inputs[0].dtype))
        # Get frozen ConcreteFunction
        frozen_func = convert_variables_to_constants_v2(full_model)
        frozen_func.graph.as_graph_def()
        layers = [op.name for op in frozen_func.graph.get_operations()]

        print("Frozen model layers: ")
        # for layer in layers:
        #     print(layer)

        print("Frozen model inputs: {0}".format(frozen_func.inputs))
        print("Frozen model outputs: {0}".format(frozen_func.outputs))

        # Save frozen graph to disk
        tf.io.write_graph(graph_or_graph_def=frozen_func.graph,
                        logdir=frozen_out_path,
                        name=f"{frozen_graph_filename}.pb",
                        as_text=False)
        tf.io.write_graph(graph_or_graph_def=frozen_func.graph,
                        logdir=frozen_out_path,
                        name=f"{frozen_graph_filename}.pbtxt",
                        as_text=True)


import bitstring
import collections
import math
from array import array
import os

""" HGR   = 280 * 192
    C64   = 40*24 chars => 320 * 192 ; display 3min 20 (2000 images) sec instead of 3min 40 (2200)
    Video = 192 * 160 (24*20)
"""

if os.name == 'nt':
    IMG_PREFIX = r'c:/PORT-STC/PRIVATE/tmp'
    FFMPEG = r'c:\PORT-STC\opt\ffmpeg-20181221-53d3a1c-win64-static\bin\ffmpeg' # -i bad_apple_original.mp4 -an  -vf fps=10,scale=36:46  c:\port-stc\private\tmp\bad_apple%05d.png'
else:
    IMG_PREFIX = '/tmp'
    FFMPEG = 'ffmpeg'

DISK_SIZE = 143360

class SpecialTiles:
    def __init__( self, black, white, transparent):
        self.black = black
        self.white = white
        self.transparent = transparent

        # self.others = [x for x in range(self.transparent)]
        # self.others.remove( self.white)
        # self.others.remove( self.black)

    def all(self):
        return (self.black, self.white, self.transparent)

class Stripe:
    def __init__( self, data, special_tiles):
        self.data = data
        self._hash = hash(array('B',data).tobytes())
        self.cycles = None # Number of cycles needed to decompress the stripe

        self.stripe_id = None
        self.compressed = self._compress_stripe2( self.data, special_tiles)
        self.stripe_id2 = None

        decomp = self._decompress_stripe( self.compressed, special_tiles)
        assert data == decomp, "{} != {}, compressed={}".format( hex_byte(data), decomp, hex_byte(self.compressed))

        #self.compressed = self._compress_stripe2( self.data, transparent_tile)
        self.label = None
        self.frequency = 0

    def __str__(self):
        return "Stripe freq:{}x {} [hash:{}]".format( self.frequency, ",".join( ['$'+format(b,'02X') for b in self.data] ) ,self._hash)

    def __hash__(self):
        return self._hash

    def has_no_count(self):
        return self.compressed[0] & 128 == 128

    def _decompress_stripe( self, data, special_tiles):

        self.cycles = 80

        r = []
        if data[0] & 128 == 128:

            self.cycles += len(data) * (63+26)

            #print("decompress raw bytes : {}".format(data))
            r.append( data[0] & 127 )

            if data[1] == 255:
                return r

            i = 1
            while True:
                r.append( data[i])
                if data[i] & 128:
                    r[-1] = r[-1] & 127
                    return r
                i += 1

            return r
        else:
            self.cycles += 111

            #print("decompress byte run")
            cmd = data[0] >> 5
            cnt = (data[0] & 31) + 1

            if cmd == 0:
                color = special_tiles.white
                self.cycles += cnt * 19
            elif cmd == 1:
                color = special_tiles.black
                self.cycles += cnt * 19
            elif cmd == 2:
                color = special_tiles.transparent
                self.cycles += cnt * 19

            r = []
            r.extend( [color] * cnt)
            r.append( data[1])
            return r

    def _compress_stripe2( self, values, special_tiles):
        # Some calculations :
        # There are 2200 frames
        # There are 3760 different stripes.
        # If want to know how many stripes there are in a frame at run time, I can :
        # 1/ Have a map frame -> nb stripes; which optimistically is 2200 bytes
        # 2/ I can count the length of the stripes until a I reach a frame, but this is colstly => I need to be able to tell the size
        #    of a stripe => I add a length byte => 3288 stripes need one => cost 3288 bytes
        # 3/ I can add a special strip to mark the end of a frame, but if Huffman compression
        #    doesn't work well enough (less than 8 bits for that stripe), it might be bigger than 2200.

        if len(values) <= 2 or values[0] not in special_tiles.all():
            r = [v for v in values ]
            r[0] = r[0] | 128

            # There are two ways of marking the end of a stream of tiles (see below).
            # Optimizing it this way let me spare +/- 5 kb out of 29 kb in the
            # stripe dictionary.

            if len(values) == 1:
                r.append(255)
            else:
                r[-1] = r[-1] | 128 # BAsically : data byte | MARK, data_byte, ..., data_byte | MARK => I spare a counter byte.
            return r
        else:
            # We always encode : a repetition of one tile followed by a single tile.
            # I cannot use the topmost bit because it's used for stream of stripes
            # (cf above)
            if values[0] == special_tiles.white:
                cmd = 0b000 << 5 # 0x00 = 0
            elif values[0] == special_tiles.black:
                cmd = 0b001 << 5 # 100000 = 0x20
            elif values[0] == special_tiles.transparent:
                cmd = 0b010 << 5 # 1000000 = 0x40

            assert cmd & 128 == 0
            # Run over black or white bytes
            i = 0
            while i+1 < len(values) and values[i+1] == values[0]:
                i += 1
            assert len(values) > 2, "shorter run should be compressed differently"
            assert values[i] == values[0], "this is not a run of bytes that are all the same"
            assert i in (len(values)-1, len(values)-2), "{} not in {}".format( i, (len(values)-2, len(values)-1))
            assert len(values) - 1 - 1 < 2 ** 5

            # -1 because the last tile is put apart, -1 because the cnt of repetition is never 0 (so we save 1 increment)
            repeat_byte = cmd | (len(values) - 1 - 1)
            additional_tile_byte = values[ len(values) - 1]
            return [ repeat_byte, additional_tile_byte]

def ffmpeg( params):
    print(params)
    os.system("{} {}".format( FFMPEG, params))

def make_delta_frames_stream( frames, special_tiles, bytes_per_frame):

    assert len( frames) % bytes_per_frame == 0

    stats_change = []

    delta_frames_stream = []
    delta_frames_stream.extend( frames[0:bytes_per_frame] )

    for i in range(bytes_per_frame, len( frames), bytes_per_frame):
        old_f = frames[i-bytes_per_frame:i]
        f = frames[i:i+bytes_per_frame]

        delta_frame = []
        for j in range( len( f)):
            if f[j] == old_f[j]:
                delta_frame.append( special_tiles.transparent)
            else:
                delta_frame.append( f[j])

        # Compute some stats

        unchanged = 0
        for i in delta_frame:
            if i == special_tiles.transparent:
                unchanged += 1

        stats_change.append(100.0 * unchanged/len(delta_frame))
        delta_frames_stream.extend( delta_frame)

    avg = sum(stats_change)/len(stats_change)
    stddev = sum( [ math.fabs( i - avg) for i in stats_change ])/len(stats_change)
    print( "unchanged avg:{}, stddev:{}".format( avg, stddev))

    return delta_frames_stream

def peek( data, i, scan_value, strip_max_len):
    cnt = 0
    while i+cnt < len(data) and data[i+cnt] == scan_value and (cnt < strip_max_len):
        cnt += 1

    return cnt

def pack_line( data, i, predicate, max_len):

    cnt = 0
    stripe = []
    while i < len(data) and predicate(data[i])  and (cnt < max_len): # and (data[i] in scan_values)
        stripe.append(data[i])
        i += 1
        cnt += 1

    return stripe, i

def pack_line_one_pixel_stop( data, i, scan_values, stop_values, max_i, strip_max_len):
    """ Somehow, picking an additional, different, pixel after a long run
    is really efficient (ie a 8% increase in compression).

    Picking 1 more is 10% more efficient.
    Picking 2 more is 30% less efficient.
    Picking n more is totally not efficient (like 100% less efficient)
    """
    cnt = 0
    stripe = []
    while i < len(data) and data[i] == scan_values and (cnt < strip_max_len) and i < max_i: # and (data[i] in scan_values)
        stripe.append(data[i])
        i += 1
        cnt += 1

    stop_value_cnt = 0
    while i < len(data) and (cnt < strip_max_len) and i < max_i: # and (data[i] in scan_values)
        stripe.append(data[i])
        i += 1
        cnt += 1
        stop_value_cnt += 1
        if stop_value_cnt == 1:
            break

    return stripe, i

def super_pack_line( data, i, scan_value, max_stripe_length):

    shorter_len = max_stripe_length // 2
    shorter_len = 4
    bigger_len = max_stripe_length*4
    if bigger_len > 31:
        bigger_len = 31

    # 31 : gives a few bytes better compression than 32.

    assert shorter_len < bigger_len

    cnt = peek( data, i, scan_value, bigger_len)
    #print(cnt)

    if cnt > shorter_len:
        # Simple tile repetition
        stripe, i = pack_line( data, i, lambda d:d == scan_value, bigger_len)
    else:
        others = set( range(256))
        others.remove( scan_value)
        stripe, i = pack_line_one_pixel_stop( data, i, scan_value, others, i+shorter_len, max_stripe_length )

    #print("{} {}".format( scan_value, len(stripe)))

    return stripe, i

def make_stripes(data_stream, special_tiles, bytes_per_frame, max_stripe_length):

    assert len(data_stream) % bytes_per_frame == 0

    all_stripes_codes = []

    # others = set( range(256))
    # others.remove( special_tiles.white)
    # others.remove( special_tiles.black)
    # others.remove( special_tiles.transparent)

    for ndx in range( 0, len(data_stream), bytes_per_frame):
        #print(ndx)
        data = data_stream[ ndx:ndx+bytes_per_frame]
        i = 0
        while i < len(data):
            if data[i] == special_tiles.transparent:
                #print("transparent")

                stripe, i = super_pack_line( data, i, special_tiles.transparent, max_stripe_length)
                #stripe, i = pack_line( data, i, [transparent_tile])

            elif data[i] == special_tiles.white:
                #print("white")
                #stripe, i = pack_line( data, i, OTHERS + [WHITE], BLACK)
                #stripe, i = pack_line( data, i, WHITE, [])
                #stripe, i = pack_line_one_pixel_stop( data, i, WHITE, OTHERS, i+MAX_STRIPE_LENGTH )
                stripe, i = super_pack_line( data, i, special_tiles.white, max_stripe_length)

            elif data[i] == special_tiles.black:
                #print("black")
                #stripe, i = pack_line( data, i, OTHERS + [BLACK], WHITE)
                #stripe, i = pack_line( data, i, BLACK, [])
                #stripe, i = pack_line_one_pixel_stop( data, i, BLACK, OTHERS, i+MAX_STRIPE_LENGTH)
                stripe, i = super_pack_line( data, i, special_tiles.black, max_stripe_length)

            else:
                #stripe, i = pack_line( data, i, OTHERS, [BLACK[0], WHITE[0]])
                stripe, i = pack_line( data, i, lambda d : d not in special_tiles.all(), 4)

            all_stripes_codes.append( Stripe(stripe, special_tiles))

    return all_stripes_codes

def simplify_stripes( all_stripes):
    unique_stripes = dict()
    stripe_id = 1
    for s in all_stripes:
        h = hash(s)
        if h not in unique_stripes:
            unique_stripes[ h] = s
            s.stripe_id = stripe_id
            stripe_id += 1

    for i in range( len( all_stripes)):
        all_stripes[i] = unique_stripes[ hash(all_stripes[i])]

    return unique_stripes

def compute_stripes_frequencies( all_stripes):
    ndx = 0
    for s, freq in collections.Counter( all_stripes ).items():
        s.frequency = freq
        s.label = ndx
        ndx += 1

def hex_byte(b_or_list, prefix="!byte "):

    if type(b_or_list) in (list, bytes):
        return prefix + ",".join( ['$'+format(b,'02X') for b in b_or_list] )
    else:
        return '$'+format(b_or_list,'02X')

def array_to_asm( fo, a, line_prefix, label = ""):

    if type(a[0]) == str:
        fmt = "{}"
    elif line_prefix == '!word':
        fmt = "${:04x}"
    elif line_prefix == '!byte':
        fmt = "${:02x}"
    else:
        raise Exception("Unknown format {}".format( line_prefix))

    if label:
        label = "\t{}:".format(label)
    else:
        label = ""

    fo.write("{}; {} values\n".format(label, len(a)))
    for i in range( 0, len( a), 10):
        end = min( i + 10, len( a))
        fo.write("\t{} {}\n".format( line_prefix, ", ".join( [ fmt.format(x) for x in a[i:end]])))

def stats_unique_stipes( unique_stripes):
    print("{} unique stripes, stored with {} bytes, representing {} stripes".format(
        len(unique_stripes),
        sum( [ len(s.compressed) for s in unique_stripes]),
        sum( [ s.frequency for s in unique_stripes])))

    f1 = [s for s in filter( lambda s:s.frequency == 1, sorted( unique_stripes, key=lambda s:s.frequency)) ]
    f1_size = sum( [ len(s.data) for s in f1 ])
    print( "{} frequency-1 stripes, totalling {} bytes. Other stripes + index table = {} bytes => total {} bytes".format(
        len(f1), f1_size,
        sum( [ len(s.compressed) for s in unique_stripes]) - f1_size + 2*(len(unique_stripes) - len(f1)),
        f1_size + sum( [ len(s.compressed) for s in unique_stripes]) - f1_size + 2*(len(unique_stripes) - len(f1))))

    f2 = [s for s in filter( lambda s:s.frequency == 2, sorted( unique_stripes, key=lambda s:s.frequency)) ]
    print( "{} frequency-2 stripes, totalling {} bytes".format( len(f2), sum( [ len(s.data) for s in f2 ])))

    # with open("stats.csv","w") as fo:
    #     for s in sorted( unique_stripes, key=lambda s:s.frequency * 100000 + len(s.compressed)):
    #         fo.write("{};{};{};{};\"{}\"\n".format( s.frequency, len(s.compressed), len(s.data), s.has_no_count(), (hex_byte(s.data))))

def simple_huffman( unique_stripes, all_stripes):
    sid = 1

    # Sort stripes, most frequent first

    for s in sorted( unique_stripes.values(), key=lambda s:s.frequency, reverse=True):
        s.stripe_id2 = sid
        sid += 1

    # for s in all_stripes[0:100]:
    #     print("({},{})".format( s.stripe_id, s.stripe_id2 ))

    stream = bitstring.BitArray()

    d1 = (2 ** 3)
    d2 = (2 ** 6) + d1
    d3 = (2 ** 9) + d2

    d1_count = d2_count = d3_count = d4_count = 0
    d1_len = d2_len = d3_len = d4_len = 0

    ndx = 0
    warn = False
    for s in all_stripes:
        sid = s.stripe_id2 - 1

        if sid < d1:
            # 0xxxb => 8 values
            bits = bitstring.BitArray(length=4, uint=sid)
            d1_count += 1
            d1_len += len(s.compressed)
        elif d1 <= sid < d2:
            # 10yy yyyy => 64 values
            bits = bitstring.BitArray(length=8, uint=0b10000000 + sid - d1)
            d2_count += 1
            d2_len += len(s.compressed)
        elif d2 <= sid < d3:
            # 110z zzzz zzzz 12 bits, 9 significant => 512 values
            bits = bitstring.BitArray(length=12, uint=0b110000000000 + sid - d2)
            d3_count += 1
            d3_len += len(s.compressed)
        elif d3 <= sid < 2 ** 13:
            # 111z zzzz zzzz zzzz 16 bits, 13 significant => 8192 values
            bits = bitstring.BitArray(length=16, uint=0b1110000000000000 + sid - d3)
            d4_count += 1
            d4_len += len(s.compressed)
        else:
            # Error !
            warn = True
            bits = bitstring.BitArray(length=16, uint=0b1111111111111111)

        # if ndx < 300:
        #     print("s# {} (b: {}) -> {} / {}".format( hex(ndx), len(stream.tobytes()), hex(bits.uint), sid))

        stream.append( bits)
        ndx += 1

    if warn:
        print("Too many stripes for the compressor ! (8192) {}".format( len(unique_stripes)))

    print("{} * 4 bits for {} bytes, {} * 8 bits for {} bytes, {} * 12 bits for {} bytes, {} * 16 bits for {} bytes".format(d1_count,d1_len,d2_count,d2_len,d3_count,d3_len,d4_count,d4_len))
    b = stream.tobytes()
    print("Bit stream simple huffman : {} stripes, {} bits, {} bytes".format( len( all_stripes), len( stream), len(b)))

    with open("cstripes.data","bw") as fo:
        fo.write( b)

    # # Allow some wrapping so that the ASM code is simpler
    # extra_bytes = 3

    # too_much = len(b) - DISK_SIZE

    # MAX = 4096

    # if too_much <= 0:
    #     too_much = MAX

    # if too_much > MAX:
    #     too_much = MAX

    # with open("compressed.a","w") as fo:
    #     array_to_asm( fo, b[0:too_much + extra_bytes], '!byte')

    # with open("cstripes.dsk","bw") as fo:
    #     fo.write( disk_2_dos( b[too_much:]))

    print("Some stripes:")
    for i in range(20):
        print( '{:04} '.format(i*16) + ' '.join([ "${:04x}".format(s.stripe_id2 - 1) for s in all_stripes[i*16:(i+1)*16]]))

    return

    # Test decompression

    #print( hex_word([s.stripe_id2 for s in all_stripes[0:500]]))
    #print( hex_byte( stream.tobytes()[0:1000]))

    decomp_stream = []
    max_l = len( stream)
    ndx = 0
    while ndx < max_l:
        half_byte = stream[ndx:ndx+4].uint

        if   half_byte & 0b1000 == 0:
            s = half_byte

        elif half_byte & 0b1100 == 0b1000:
            s = (half_byte & 0b0011)
            ndx += 4
            s = (s << 4) + stream[ndx:ndx+4].uint
            s += d1

        elif half_byte & 0b1110 == 0b1100:
            s = (half_byte & 0b0001)
            #print( hex(s))
            ndx += 4
            s = (s << 4) + stream[ndx:ndx+4].uint
            #print( hex(s))
            ndx += 4
            s = (s << 4) + stream[ndx:ndx+4].uint
            #print( hex(s))
            s += d2
            #print( hex(d2))
            #print( hex(s))

        elif half_byte & 0b1110 == 0b1110:
            s = (half_byte & 0b0001)
            ndx += 4
            s = (s << 4) + stream[ndx:ndx+4].uint
            ndx += 4
            s = (s << 4) + stream[ndx:ndx+4].uint
            ndx += 4
            s = (s << 4) + stream[ndx:ndx+4].uint
            s += d3

        decomp_stream.append(s)
        ndx += 4

    a = [s.stripe_id2 for s in all_stripes]
    b = decomp_stream

    for i in range( len(a)):
        if a[i] != b[i]:
            print(i)

def unique_stripes_to_asm( fo, unique_stripes):
    def stripe_id(stripe):
        return stripe.stripe_id2

    sorted_stripes = sorted( unique_stripes.values(), key=stripe_id)
    fo.write('\n')
    for s in sorted_stripes:
        fo.write("stripe{}\t{}\t; [${:X}] {}\n".format( stripe_id(s), hex_byte(s.compressed), stripe_id(s) - 1, hex_byte(s.data, '')))

    fo.write('stripes_indices:\n')
    array_to_asm( fo, ["stripe{}".format( stripe_id(s)) for s in sorted_stripes], "!word")

def stripes_to_disk( stripes):

    disk = bytearray()

    for s in stripes[0:min( (len(stripes) // 2) - 1, (DISK_SIZE//2) - 1)]:
        sid = (s.stripe_id2 - 1) * 2
        assert sid < 65536
        disk.append( sid & 0xFF)
        disk.append( sid >> 8)

    disk.append( 0xFF)
    disk.append( 0xFF)
    #disk.extend( bytearray( 143360 - len(disk)))

    with open("stripes.dsk","bw") as fo:
        fo.write( disk_2_dos( disk))

def disk_2_dos( disk):

    disk = bytearray( disk)

    dos_sector= [0x0, 0xd, 0xb, 0x9, 0x7, 0x5, 0x3, 0x1,
                 0xe, 0xc, 0xa, 0x8, 0x6, 0x4, 0x2, 0xf]

    prodos_sector = [0x0, 0x8, 0x1, 0x9, 0x2, 0xa, 0x3, 0xb,
                     0x4, 0xc, 0x5, 0xd, 0x6, 0xe, 0x7, 0xf]

    # Dos order : https://en.wikipedia.org/wiki/Apple_DOS

    dos_sector= [0x0, 0x7, 0xe, 0x6, 0xd, 0x5, 0xc, 0x4,
                 0xb, 0x3, 0xa, 0x2, 0x9, 0x1, 0x8, 0xf]

    if len(disk) > DISK_SIZE:
        print("Disk image too big by {} bytes, truncating...".format(len(disk) - DISK_SIZE))
        disk = disk[0:DISK_SIZE]
    elif len(disk) < DISK_SIZE:
        print("Disk image too small ({}), extending to disk size...".format(len(disk)))
        disk.extend( bytearray( DISK_SIZE - len(disk)))
    else:
        print("disk_2_dos : putting {} bytes on a disk of {}".format(len(disk), DISK_SIZE))

    # dos_sector = list( range( 16))
    disk_dos = bytearray( DISK_SIZE)

    for track in range(35):
        for sector in range(16):
            track_offset = track * 16 * 256

            dos_ofs = track_offset + dos_sector[sector]*256
            dsk_ofs = track_offset + sector*256
            disk_dos[ dos_ofs:dos_ofs+256] =  disk[ dsk_ofs:dsk_ofs+256] # [sector for i in range(256)]

    return disk_dos


import unittest
import os
import lsst.utils.tests

import pandas as pd
import numpy as np
import asyncio
import matplotlib.pyplot as plt
from astropy.time import TimeDelta

from lsst.utils import getPackageDir
from lsst.summit.utils.enums import PowerState
from lsst.summit.utils.efdUtils import makeEfdClient, getDayObsStartTime, calcNextDay
from lsst.summit.utils.tmaUtils import (
    getSlewsFromEventList,
    getTracksFromEventList,
    getAzimuthElevationDataForEvent,
    plotEvent,
    getCommandsDuringEvent,
    TMAStateMachine,
    TMAEvent,
    TMAEventMaker,
    TMAState,
    AxisMotionState,
    getAxisAndType,
    _initializeTma,
)
from utils import getVcr

__all__ = [
    'writeNewTmaEventTestTruthValues',
]

vcr = getVcr()

def getTmaEventTestTruthValues():
    """Get the current truth values for the TMA event test cases.

    Returns
    -------
    seqNums : `np.array` of `int`
        The sequence numbers of the events.
    startRows : `np.array` of `int`
        The _startRow numbers of the events.
    endRows : `np.array` of `int`
        The _endRow numbers of the events.
    types : `np.array` of `str`
        The event types, as a string, i.e. the ``TMAEvent.name`` of the event's
        ``event.type``.
    endReasons : `np.array` of `str`
        The event end reasons, as a string, i.e. the ``TMAEvent.name`` of the
        event's ``event.endReason``.
    """
    packageDir = getPackageDir("summit_utils")
    dataFilename = os.path.join(packageDir, "tests", "data", "tmaEventData.txt")

    seqNums, startRows, endRows, types, endReasons = np.genfromtxt(dataFilename,
                                                                   delimiter=',',
                                                                   dtype=None,
                                                                   names=True,
                                                                   encoding='utf-8',
                                                                   unpack=True
                                                                   )
    return seqNums, startRows, endRows, types, endReasons

def writeNewTmaEventTestTruthValues():
    """This function is used to write out the truth values for the test cases.

    If the internal event creation logic changes, these values can change, and
    will need to be updated. Run this function, and check the new values into
    git.

    Note: if you have cause to update values with this function, make sure to
    update the version number on the TMAEvent class.
    """
    dayObs = 20230531  # obviously must match the day in the test class

    eventMaker = TMAEventMaker()
    events = eventMaker.getEvents(dayObs)

    packageDir = getPackageDir("summit_utils")
    dataFilename = os.path.join(packageDir, "tests", "data", "tmaEventData.txt")

    columnHeader = "seqNum,startRow,endRow,type,endReason"
    with open(dataFilename, 'w') as f:
        f.write(columnHeader + '\n')
        for event in events:
            line = (f"{event.seqNum},{event._startRow},{event._endRow},{event.type.name},"
                    f"{event.endReason.name}")
            f.write(line + '\n')

def makeValid(tma):
    """Helper function to turn a TMA into a valid state.
    """
    for name, value in tma._parts.items():
        if value == tma._UNINITIALIZED_VALUE:
            tma._parts[name] = 1

def _turnOn(tma):
    """Helper function to turn TMA axes on for testing.

    Do not call directly in normal usage or code, as this just arbitrarily
    sets values to turn the axes on.

    Parameters
    ----------
    tma : `lsst.summit.utils.tmaUtils.TMAStateMachine`
        The TMA state machine model to initialize.
    """
    tma._parts['azimuthSystemState'] = PowerState.ON
    tma._parts['elevationSystemState'] = PowerState.ON

class TmaUtilsTestCase(lsst.utils.tests.TestCase):

    def test_tmaInit(self):
        tma = TMAStateMachine()
        self.assertFalse(tma._isValid)

        # setting one axis should not make things valid
        tma._parts['azimuthMotionState'] = 1
        self.assertFalse(tma._isValid)

        # setting all the other components should make things valid
        tma._parts['azimuthInPosition'] = 1
        tma._parts['azimuthSystemState'] = 1
        tma._parts['elevationInPosition'] = 1
        tma._parts['elevationMotionState'] = 1
        tma._parts['elevationSystemState'] = 1
        self.assertTrue(tma._isValid)

    def test_tmaReferences(self):
        """Check the linkage between the component lists and the _parts dict.
        """
        tma = TMAStateMachine()

        # setting one axis should not make things valid
        self.assertEqual(tma._parts['azimuthMotionState'], tma._UNINITIALIZED_VALUE)
        self.assertEqual(tma._parts['elevationMotionState'], tma._UNINITIALIZED_VALUE)
        tma.motion[0] = AxisMotionState.TRACKING  # set azimuth to 0
        tma.motion[1] = AxisMotionState.TRACKING  # set azimuth to 0
        self.assertEqual(tma._parts['azimuthMotionState'], AxisMotionState.TRACKING)
        self.assertEqual(tma._parts['elevationMotionState'], AxisMotionState.TRACKING)

    def test_getAxisAndType(self):
        # check both the long and short form names work
        for s in ['azimuthMotionState', 'lsst.sal.MTMount.logevent_azimuthMotionState']:
            self.assertEqual(getAxisAndType(s), ('azimuth', 'MotionState'))

        # check in position, and use elevation instead of azimuth to test that
        for s in ['elevationInPosition', 'lsst.sal.MTMount.logevent_elevationInPosition']:
            self.assertEqual(getAxisAndType(s), ('elevation', 'InPosition'))

        for s in ['azimuthSystemState', 'lsst.sal.MTMount.logevent_azimuthSystemState']:
            self.assertEqual(getAxisAndType(s), ('azimuth', 'SystemState'))

    def test_initStateLogic(self):
        tma = TMAStateMachine()
        self.assertFalse(tma._isValid)
        self.assertFalse(tma.isMoving)
        self.assertFalse(tma.canMove)
        self.assertFalse(tma.isTracking)
        self.assertFalse(tma.isSlewing)
        self.assertEqual(tma.state, TMAState.UNINITIALIZED)

        _initializeTma(tma)  # we're valid, but still aren't moving and can't
        self.assertTrue(tma._isValid)
        self.assertNotEqual(tma.state, TMAState.UNINITIALIZED)
        self.assertTrue(tma.canMove)
        self.assertTrue(tma.isNotMoving)
        self.assertFalse(tma.isMoving)
        self.assertFalse(tma.isTracking)
        self.assertFalse(tma.isSlewing)

        _turnOn(tma)  # can now move, still valid, but not in motion
        self.assertTrue(tma._isValid)
        self.assertTrue(tma.canMove)
        self.assertTrue(tma.isNotMoving)
        self.assertFalse(tma.isMoving)
        self.assertFalse(tma.isTracking)
        self.assertFalse(tma.isSlewing)

        # consider manipulating the axes by hand here and testing these?
        # it's likely not worth it, given how much this exercised elsewhere,
        # but these are the only functions not yet being directly tested
        # tma._axesInFault()
        # tma._axesOff()
        # tma._axesOn()
        # tma._axesInMotion()
        # tma._axesTRACKING()
        # tma._axesInPosition()

@vcr.use_cassette()
class TMAEventMakerTestCase(lsst.utils.tests.TestCase):

    @classmethod
    @vcr.use_cassette()
    def setUpClass(cls):
        try:
            cls.client = makeEfdClient(testing=True)
        except RuntimeError:
            raise unittest.SkipTest("Could not instantiate an EFD client")

        cls.dayObs = 20230531
        # get a sample expRecord here to test expRecordToTimespan
        cls.tmaEventMaker = TMAEventMaker(cls.client)
        cls.events = cls.tmaEventMaker.getEvents(cls.dayObs)  # does the fetch
        cls.sampleData = cls.tmaEventMaker._data[cls.dayObs]  # pull the data from the object and test length

    @vcr.use_cassette()
    def tearDown(self):
        loop = asyncio.get_event_loop()
        loop.run_until_complete(self.client.influx_client.close())

    @vcr.use_cassette()
    def test_events(self):
        data = self.sampleData
        self.assertIsInstance(data, pd.DataFrame)
        self.assertEqual(len(data), 993)

    @vcr.use_cassette()
    def test_rowDataForValues(self):
        rowsFor = set(self.sampleData['rowFor'])
        self.assertEqual(len(rowsFor), 6)

        # hard coding these ensures that you can't extend the axes/model
        # without being explicit about it here.
        correct = {'azimuthInPosition',
                   'azimuthMotionState',
                   'azimuthSystemState',
                   'elevationInPosition',
                   'elevationMotionState',
                   'elevationSystemState'}
        self.assertSetEqual(rowsFor, correct)

    @vcr.use_cassette()
    def test_monotonicTimeInDataframe(self):
        # ensure that each row is later than the previous
        times = self.sampleData['private_efdStamp']
        self.assertTrue(np.all(np.diff(times) > 0))

    @vcr.use_cassette()
    def test_monotonicTimeApplicationOfRows(self):
        # ensure you can apply rows in the correct order
        tma = TMAStateMachine()
        row1 = self.sampleData.iloc[0]
        row2 = self.sampleData.iloc[1]

        # just running this check it is OK
        tma.apply(row1)
        tma.apply(row2)

        # and that if you apply them in reverse order then things will raise
        tma = TMAStateMachine()
        with self.assertRaises(ValueError):
            tma.apply(row2)
            tma.apply(row1)

    @vcr.use_cassette()
    def test_fullDaySequence(self):
        # make sure we can apply all the data from the day without falling
        # through the logic sieve
        for engineering in (True, False):
            tma = TMAStateMachine(engineeringMode=engineering)

            _initializeTma(tma)

            for rowNum, row in self.sampleData.iterrows():
                tma.apply(row)

    @vcr.use_cassette()
    def test_endToEnd(self):
        eventMaker = self.tmaEventMaker
        events = eventMaker.getEvents(self.dayObs)
        self.assertIsInstance(events, list)
        self.assertEqual(len(events), 200)
        self.assertIsInstance(events[0], TMAEvent)

        slews = [e for e in events if e.type == TMAState.SLEWING]
        tracks = [e for e in events if e.type == TMAState.TRACKING]
        self.assertEqual(len(slews), 157)
        self.assertEqual(len(tracks), 43)

        seqNums, startRows, endRows, types, endReasons = getTmaEventTestTruthValues()
        for eventNum, event in enumerate(events):
            self.assertEqual(event.seqNum, seqNums[eventNum])
            self.assertEqual(event._startRow, startRows[eventNum])
            self.assertEqual(event._endRow, endRows[eventNum])
            self.assertEqual(event.type.name, types[eventNum])
            self.assertEqual(event.endReason.name, endReasons[eventNum])

    @vcr.use_cassette()
    def test_noDataBehaviour(self):
        eventMaker = self.tmaEventMaker
        noDataDayObs = 19500101  # do not use 19700101 - there is data for that day!
        with self.assertLogs(level='WARNING') as cm:
            correctMsg = f"No EFD data found for dayObs={noDataDayObs}"
            events = eventMaker.getEvents(noDataDayObs)
            self.assertIsInstance(events, list)
            self.assertEqual(len(events), 0)
            msg = cm.output[0]
            self.assertIn(correctMsg, msg)

    @vcr.use_cassette()
    def test_helperFunctions(self):
        eventMaker = self.tmaEventMaker
        events = eventMaker.getEvents(self.dayObs)

        slews = [e for e in events if e.type == TMAState.SLEWING]
        tracks = [e for e in events if e.type == TMAState.TRACKING]
        foundSlews = getSlewsFromEventList(events)
        foundTracks = getTracksFromEventList(events)
        self.assertEqual(slews, foundSlews)
        self.assertEqual(tracks, foundTracks)

    @vcr.use_cassette()
    def test_getEvent(self):
        # test the singular event getter, and what happens if the event doesn't
        # exist for the day
        eventMaker = self.tmaEventMaker
        events = eventMaker.getEvents(self.dayObs)
        nEvents = len(events)

        event = eventMaker.getEvent(self.dayObs, 0)
        self.assertIsInstance(event, TMAEvent)
        self.assertEqual(event, events[0])
        event = eventMaker.getEvent(self.dayObs, 100)
        self.assertIsInstance(event, TMAEvent)
        self.assertEqual(event, events[100])

        with self.assertLogs(level='WARNING') as cm:
            correctMsg = f"Event {nEvents+1} not found for {self.dayObs}"
            event = eventMaker.getEvent(self.dayObs, nEvents+1)
            msg = cm.output[0]
            self.assertIn(correctMsg, msg)

    @vcr.use_cassette()
    def test_printing(self):
        eventMaker = self.tmaEventMaker
        events = eventMaker.getEvents(self.dayObs)

        # test str(), repr(), and _ipython_display_() for an event
        print(str(events[0]))
        print(repr(events[0]))
        print(events[0]._ipython_display_())

        # spot-check both a slow and a track to print
        slews = [e for e in events if e.type == TMAState.SLEWING]
        tracks = [e for e in events if e.type == TMAState.TRACKING]
        eventMaker.printEventDetails(slews[0])
        eventMaker.printEventDetails(tracks[0])
        eventMaker.printEventDetails(events[-1])

        # check the full day trick works
        eventMaker.printFullDayStateEvolution(self.dayObs)

        tma = TMAStateMachine()
        _initializeTma(tma)  # the uninitialized state contains wrong types for printing
        eventMaker.printTmaDetailedState(tma)

    @vcr.use_cassette()
    def test_getAxisData(self):
        eventMaker = self.tmaEventMaker
        events = eventMaker.getEvents(self.dayObs)

        azData, elData = getAzimuthElevationDataForEvent(self.client, events[0])
        self.assertIsInstance(azData, pd.DataFrame)
        self.assertIsInstance(elData, pd.DataFrame)

        paddedAzData, paddedElData = getAzimuthElevationDataForEvent(self.client,
                                                                     events[0],
                                                                     prePadding=2,
                                                                     postPadding=1)
        self.assertGreater(len(paddedAzData), len(azData))
        self.assertGreater(len(paddedElData), len(elData))

        # just check this doesn't raise when called, and check we can pass the
        # data in
        plotEvent(self.client, events[0], azimuthData=azData, elevationData=elData)

    @vcr.use_cassette()
    def test_plottingAndCommands(self):
        eventMaker = self.tmaEventMaker
        events = eventMaker.getEvents(self.dayObs)
        event = events[28]  # this one has commands, and we'll check that later

        # check we _can_ plot without a figure, and then stop doing that
        plotEvent(self.client, event)

        fig = plt.figure(figsize=(10, 8))
        # just check this doesn't raise when called
        plotEvent(self.client, event, fig=fig)
        plt.close(fig)

        commandsToPlot = ['raDecTarget', 'moveToTarget', 'startTracking', 'stopTracking']
        commands = getCommandsDuringEvent(self.client, event, commandsToPlot, doLog=False)
        self.assertTrue(not all([time is None for time in commands.values()]))  # at least one command

        plotEvent(self.client, event, fig=fig, commands=commands)

        del fig

    @vcr.use_cassette()
    def test_findEvent(self):
        eventMaker = self.tmaEventMaker
        events = eventMaker.getEvents(self.dayObs)
        event = events[28]  # this one has a contiguous event before it

        time = event.begin
        found = eventMaker.findEvent(time)
        self.assertEqual(found, event)

        dt = TimeDelta(0.01, format='sec')
        # must be just inside to get the same event back, because if a moment
        # is shared it gives the one which starts with the moment (whilst
        # logging info messages about it)
        time = event.end - dt
        found = eventMaker.findEvent(time)
        self.assertEqual(found, event)

        # now check that if we're a hair after, we don't get the same event
        time = event.end + dt
        found = eventMaker.findEvent(time)
        self.assertNotEqual(found, event)

        # Now check the cases which don't find an event at all. It would be
        # nice to check the log messages here, but it seems too fragile to be
        # worth it
        dt = TimeDelta(1, format='sec')
        tooEarlyOnDay = getDayObsStartTime(self.dayObs) + dt  # 1 second after start of day
        found = eventMaker.findEvent(tooEarlyOnDay)
        self.assertIsNone(found)

        # 1 second before end of day and this day does not end with an open
        # event
        tooLateOnDay = getDayObsStartTime(calcNextDay(self.dayObs)) - dt
        found = eventMaker.findEvent(tooLateOnDay)
        self.assertIsNone(found)

        # going just inside the last event of the day should be fine
        lastEvent = events[-1]
        found = eventMaker.findEvent(lastEvent.end - dt)
        self.assertEqual(found, lastEvent)

        # going at the very end of the last event of the day should actually
        # find nothing, because the last moment of an event isn't actually in
        # the event itself, because of how contiguous events are defined to
        # behave (being half-open intervals)
        found = eventMaker.findEvent(lastEvent.end)
        self.assertIsNone(found, lastEvent)

class TestMemory(lsst.utils.tests.MemoryTestCase):
    pass

def setup_module(module):
    lsst.utils.tests.init()

if __name__ == "__main__":
    lsst.utils.tests.init()
    unittest.main()


from cassiopeia import Queue, Summoner, SummonersRiftArea

def test_summonersrift_map():
    summoner = Summoner(name="Kalturi", region="NA")
    match = summoner.match_history(queues=[Queue.ranked_solo_fives])[0]
    for frame in match.timeline.frames:
        for event in frame.events:
            if event.type == "CHAMPION_KILL":
                SummonersRiftArea.from_position(event.position)

def test_from_match():
    summoner = Summoner(name="Kalturi", region="NA")
    match_history = summoner.match_history

    match = match_history[0]
    timeline = match.timeline
    for frame in timeline.frames[:-1]:
        for pf in frame.participant_frames.values():
            print(pf.position.location)


"""
A simple tool for converting 512x64x512 icemap files into vxl.

NOTE: this does NOT do the icemap footer variant. (Yet.)

GreaseMonkey, 2012 - Public Domain

"""

from __future__ import print_function

import sys, struct

# Backwards compatibility - make new code work on old version, not vice-versa
PY2 = sys.version_info[0] == 2
PY3 = sys.version_info[0] == 3
if PY2:
	# This script didn't use range() anyway, so no problem overwriting it in Py2
	import __builtin__
	range = getattr(__builtin__, "xrange")
	_ord = ord
else:
	_ord = lambda x: x

USAGE_MSG = """
usage:
	python2 icemap2vxl.py in.icemap out.vxl

note: icemap file MUST be 512x64x512,
and the type information will be LOST!
"""

if len(sys.argv) <= 2:
	print(USAGE_MSG)
	exit()

infp = open(sys.argv[1],"rb")
if infp.read(8) != "IceMap\x1A\x01":
	raise Exception("not an IceMap v1 file")

while True:
	tag = infp.read(7)
	taglen = _ord(infp.read(1))

	if tag == b" "*7:
		break

	if taglen == 255:
		taglen, = struct.unpack("<I", infp.read(4))

	if tag == "MapData":
		xlen, ylen, zlen = struct.unpack("<HHH", infp.read(6))
		if xlen != 512 or ylen != 64 or zlen != 512:
			raise Exception("not a 512x64x512 map")

		outfp = open(sys.argv[2],"wb")

		for z in range(512):
			for x in range(512):
				k = True
				while k:
					cblk = infp.read(4)
					outfp.write(cblk)
					n = _ord(cblk[0])
					if n == 0:
						n = _ord(cblk[2])-_ord(cblk[1])+1
						k = False
					else:
						n = n - 1

					for i in range(n):
						s = infp.read(4)
						outfp.write(s[:3]+b"\x7F")

		outfp.close()
		infp.close()
		break
	else:
		infp.seek(taglen, 1) # SEEK_CUR


import json
from PIL import Image
import os

def main(json_file_path):

    json_file = open (json_file_path)
    json_string = json_file.read()
    json_data = json.loads(json_string)

    image = json_data[0]
    #for image in json_data:
    image_file_path = image['image_path']
    image_to_crop = Image.open(image_file_path)

    cropped_images = []

    image_rects = image['rects']

    for r in image_rects:
        score = r['score']

        if score > 0.0:

            diff_x = r['x2']-r['x1']
            diff_y = r['y2']-r['y1']

            if diff_x < diff_y:
                wider_x = diff_y-diff_x + 10
                wider_y = 10
            else:
                wider_y = diff_x - diff_y + 10
                wider_x = 10

            new_image = image_to_crop.crop((r['x1']-wider_x/2, r['y1']-wider_y/2, r['x2']+wider_x/2, r['y2']+wider_y/2))

            #new_image = transform_image_to_square(new_image) #transform the image to square
            cropped_images.append(new_image)

    return cropped_images

def transform_image_to_square(img):
    longer_side = max(img.size)
    horizontal_padding = (longer_side - img.size[0]) / 2
    vertical_padding = (longer_side - img.size[1]) / 2

    square_img = img.crop(
        (
            -horizontal_padding,
            -vertical_padding,
            img.size[0] + horizontal_padding,
            img.size[1] + vertical_padding
        )

    )

    return square_img


import random
while 1:
    a=input('''Press Enter to play <--- Rock! Paper! and Sciccor! -->
            To 'QUIT' press any key and press enter !''')
    if a=='':
        l=["Rock","Paper","Scissor"]
        a=random.randint(0,2)
        print(l[a])
    else:
        break


import os
from logging import (
    CRITICAL,
    DEBUG,
    ERROR,
    getLogger,
    INFO,
    Logger,
    WARNING,
)
from pathlib import Path

from rich.logging import RichHandler
from rich.highlighter import NullHighlighter

from .config import BodyworkConfig
from .constants import (
    DEFAULT_LOG_LEVEL,
    DEFAULT_LOG_LEVEL_ENV_VAR,
    DEFAULT_PROJECT_DIR,
    LOG_TIME_FORMAT,
    PROJECT_CONFIG_FILENAME,
)
from .cli.terminal import console
from .exceptions import BodyworkConfigError

def bodywork_log_factory(
    log_level: str = None,
    config_file_path: Path = DEFAULT_PROJECT_DIR / PROJECT_CONFIG_FILENAME,
) -> Logger:
    """Create a standardised Bodywork logger.

    If a log level is specified as an argument, then it will take
    precedence overall all other methods of setting the log-level. Next
    in the waterfall of priority is the log-level set in the project
    config file, and then after that the level set by the
    BODYWORK_LOG_LEVEL environment variable. Failing that, the default
    log level (INFO) will be used.

    :param log_level: The minimum severity level of messages to log,
        defaults to None.
    :param config_file_path: Path to project config file, defaults
        DEFAULT_PROJECT_DIR/PROJECT_CONFIG_FILENAME.
    """
    log_level_mapping = {
        "DEBUG": DEBUG,
        "INFO": INFO,
        "WARNING": WARNING,
        "ERROR": ERROR,
        "CRITICAL": CRITICAL,
    }
    log = getLogger("bodywork")
    if log_level is not None:
        log.setLevel(log_level_mapping[log_level])
    else:
        try:
            bodywork_config = BodyworkConfig(config_file_path)
            log.setLevel(bodywork_config.logging.log_level)
        except BodyworkConfigError:
            try:
                log_level_from_env_var = os.environ[DEFAULT_LOG_LEVEL_ENV_VAR]
                log.setLevel(log_level_mapping[log_level_from_env_var])
            except KeyError:
                log.setLevel(log_level_mapping[DEFAULT_LOG_LEVEL])
    if not log.hasHandlers():
        log_handler = RichHandler(
            console=console,
            show_path=False,
            highlighter=NullHighlighter(),
            omit_repeated_times=False,
            rich_tracebacks=True,
            log_time_format=LOG_TIME_FORMAT,
        )
        log.addHandler(log_handler)
    return log