from PerfectModel import PerfectModel
from AlmostPerfectModel import AlmostPerfectModel
from GRUModel import GRUModel
import json
from utils import *
from keras.models import load_model
import math

# 测试底层为散列表的布隆过滤器，这里称之为完美模型
# 主要是测试fit（对应传统布隆过滤器里的add）和predict（对应传统布隆过滤器里的check）
# 测试的过程是使用fit加入positives集合中的数据，然后通过predict判断positives每一个元素是否存在（理应存在）
# 使用fit加入negatives集合中的数据，然后通过predict判断negatives每一个元素是否存在（理应存在）
# 作者还使用某种方法（可能是held-out留出法，论文里提到过）把negatives分配为测试集和训练集，但是实际上没啥用233，作者也说了，意思意思
def test_perfect_model(positives, negatives):
    print(len(positives))

	# 前80%的训练集
    negatives_train = negatives[0: int(len(negatives) * .8)]
    # 后20%的测试集
    negatives_test = negatives[int(len(negatives) * .8): ]

	# 打印出训练集和测试集的长度
    print(len(negatives_train), len(negatives_test))

    model = PerfectModel()
    # should shuffle but doesn't matter in this case
    # X为存在的值加上前80%的不存在的值，
    # y为len(positives)个1加上len(negative_train)个0，
    # 相当于掩码吧，借助这个可以把合并的数据分开
    # 将positives加入一个table，其它不加，
    # 好像没啥用哈哈，直接加positives就可了，非得合并一下再分开加入table
    model.fit(positives + negatives_train, [1 for _ in range(len(positives))] + [0 for _ in range(len(negatives_train))])

	# 遍历positives，检查每一个应该存在的元素是否真的存在
	# 如果model.predict(x) == 1不成立，那说明出大问题了，本来应该存在的数据不存在
	# 说明数据有问题
    for x in positives:
        assert(model.predict(x) == 1)

	# 
    for x in negatives_test + negatives_train:
    # 如果x在table中，x是negatives集合中的值，没有被加入过
    # 之所以放在前面，应该是因为如果assert(model.predict(x) == 0)的话就直接退出了，
    # 没有机会知道不应该出现的情况的x值
        if model.predict(x) != 0: 
            print(x)                    # 打印x
            print(x in positives)       # 打印x是否在positives中
            print(x in negatives_train) # 打印x是否在negatives_train，训练集中
            print(x in negatives_test)  # 打印x是否在negatives_test，测试集中
        assert(model.predict(x) == 0)   # 检查x是不是不在table中


def test_almost_perfect_model(positives, negatives):
    print(len(positives))

	# 分配negatives为训练集和测试集
    negatives_train = negatives[0: int(len(negatives) * .8)]
    negatives_test = negatives[int(len(negatives) * .8): ]

    print(len(negatives_train), len(negatives_test))

	# 输入假阳率，初始化模型
    model = AlmostPerfectModel(0.05)
    print("Using almost perfect false positive rate of 0.05")
    # should shuffle but doesn't matter in this case
    # 和上面一样，把positives和negatives作为训练集的一部分合并又分开，把positives部分加入到模型中
    model.fit(positives + negatives_train, [1 for _ in range(len(positives))] + [0 for _ in range(len(negatives_train))])

	# 这会check（predict）的时候返回的不仅仅是0或1那么绝对的值了
	# 而是一种类似概率的模糊值
	# 当这个概率低于我们自己设定的一个阈值的时候，就认为不存在
	# positives因为是已经被加入到了模型中，所以check出来的值都应该大于阈值
    for x in positives:
        assert(model.predict(x) >= 0.5)

	# 下面check训练集中有多少存在的
    false_positives_train = 0.0
    for x in negatives_train:
        if model.predict(x) >= 0.5: # 如果大于阈值，说明这一个x存在
         false_positives_train += 1 # 记下，存在的个数加一

	# 接着check测试集中有多少存在的
    false_positives_test = 0.0
    for x in negatives_test: 
        if model.predict(x) >= 0.5: # 如果大于阈值，说明这一个x存在
         false_positives_test += 1  # 记下，存在的个数加一

	# 输出训练集和测试集中存在的元素的个数占各自集合总个数的比例
    print(false_positives_train / len(negatives_train), "false positive rate for train.")
    print(false_positives_test / len(negatives_test), "false positive rate for test.") 


def test_gru_model(positives, negatives, data_fraction=1.0, fp_rate=0.01, lr=0.001, pca_embedding_dim=None, maxlen=50, gru_size=16, batch_size=1024, hidden_size=None, second_gru_size=None, decay=0.0001, epochs=30):
    positives = positives[:int(data_fraction * len(positives))]
    negatives = negatives[:int(data_fraction * len(negatives))]

    negatives_train = negatives[0: int(len(negatives) * .8)]
    negatives_dev = negatives[int(len(negatives) * .8): int(len(negatives) * .9)]
    negatives_test = negatives[int(len(negatives) * .9): ]

    print("Split sizes:")
    print(len(positives), len(negatives_train), len(negatives_dev), len(negatives_test))

    model = GRUModel('../data/glove.6B.50d-char.txt', 50, lr=lr, pca_embedding_dim=pca_embedding_dim, maxlen=maxlen, gru_size=gru_size, batch_size=batch_size, hidden_size=hidden_size, second_gru_size=second_gru_size, decay=decay, epochs=epochs)
    shuffled = shuffle_for_training(negatives_train, positives)

    model.fit(shuffled[0], shuffled[1])
    print(model.model.summary())
    print("Params", model.model.count_params())
    # model.save('model_test.h5')
    # model = load_model('model_test.h5')

    print("Using threshold 0.5")

    threshold = 0.5

    evaluate_model(model, positives, negatives_train, negatives_dev, negatives_test, threshold)

    print("Getting threshold for fp_rate", fp_rate)

    preds = model.predicts(negatives_dev)
    preds.sort()
    fp_index = math.ceil((len(negatives_dev) * (1 - fp_rate)))
    threshold = preds[fp_index]

    print("Using threshold", threshold) 

    evaluate_model(model, positives, negatives_train, negatives_dev, negatives_test, threshold)

if __name__=='__main__':

    with open('../data/dataset.json', 'r') as f:
        dataset = json.load(f)

    positives = dataset['positives']
    negatives = dataset['negatives']

    print("Testing perfect...")
    test_perfect_model(positives, negatives)
    print("Testing almost perfect...")
    test_almost_perfect_model(positives, negatives)
    print("Testing GRU model...")
    test_gru_model(positives, negatives, data_fraction=0.5, fp_rate=0.01, pca_embedding_dim=16, lr=0.001, maxlen=50, gru_size=16)


