# Code reused from https://github.com/jennyzhang0215/DKVMN.git
import numpy as np
import torch
import math
import pickle
from sklearn import metrics
from utils import model_isPid_type
# from model.prompt import *
from model.llmkt import *

from tqdm import tqdm
import json

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

transpose_data_model = {'akt',"sakt","dkt","SparseKT","Denoise","LLM4KT"}


def format_student_records_text(input_q, input_pid, target,concept_text):
        # 加载技能映射
    with open(concept_text, 'r', encoding='utf-8') as f:
        skill_mapping = json.load(f)

    # 反转字典以便通过值查找键
    reversed_skill_mapping = {v: k for k, v in skill_mapping.items()}
    formatted_records = []
    for i in range(len(input_q)):
        student_record = []
        nonzero_count = (input_q[i] != 0).sum().item()
        for t in range(len(input_q[i])):
            qid = input_pid[i][t]
            cid = input_q[i][t]
            correct = target[i][t]

            status = "correctly" if correct == 1 else "incorrectly"
            
            
            if qid !=0:
                if t == 0:
                    # student_record.append(
                    #     f"The student has in chronological order answered [ITEM]"
                    # )
                    student_record.append(
                        f"Please predict the future performance of the student according to his interaction records. The student has in chronological order answered [ITEM]"
                    )
                else:
                    qid1 = input_pid[i][t-1]
                    cid1 = input_q[i][t-1]
                    correct1 = target[i][t-1]
                    cid_str = reversed_skill_mapping.get(cid1, f" ")  # 获取对应的字符串
                    status = "correctly" if correct1 == 1 else "incorrectly"
                    t=t-1
                    student_record.append(
                        f" question QID={qid1} involving concept CID={cid1} {cid_str} at timestep {t} {status},[ITEM]"
                    )
                    # student_record.append(
                    #     f" question involving concept CID={cid1} at timestep {t-1} {status},[ITEM]"
                    # )
            else:
                student_record.append(
                    "[None]"
                )
                
        formatted_records.append(student_record)
    return formatted_records
    


def format_student_query_text(input_q, input_pid,concept_text):
    with open(concept_text, 'r', encoding='utf-8') as f:
        skill_mapping = json.load(f)

    # 反转字典以便通过值查找键
    reversed_skill_mapping = {v: k for k, v in skill_mapping.items()}
    formatted_records = []
    for i in range(len(input_q)):
        student_record = []
        for t in range(len(input_q[i])):
            qid = input_pid[i][t]
            cid = input_q[i][t]
            cid_str=reversed_skill_mapping.get(cid, f" ")  # 获取对应的字符串
            
            
            if qid !=0:
           
                student_record.append(
                    # f"Pquestion QID={qid} involving concept CID={cid} at timestep {t}.[QUERY]"
                      f"Predict whether the student can answer question QID={qid}, involving concept CID={cid} {cid_str}, at timestep {t}, Yes or No.[QUERY]"
                )
                # student_record.append(
                #     # f"Pquestion QID={qid} involving concept CID={cid} at timestep {t}.[QUERY]"
                #       f"Predict whether the student can answer question involving concept CID={cid}, at timestep {t}, correctly or No.[QUERY]"
                # )

            else:
                student_record.append(
                    "[None1]"
                )             
            
            
        formatted_records.append(student_record)
    return formatted_records


def format_student_records_concept(input_q, target):
    formatted_records = []
    for i in range(len(input_q)):
        student_record = []
        nonzero_count = (input_q[i] != 0).sum().item()
        for t in range(len(input_q[i])):
            cid = input_q[i][t]
            correct = target[i][t]
      
            # status = "correctly" if correct == 1 else "incorrectly"
           
            if cid !=0:
                if t == 0:
                    # student_record.append(
                    #     f"The student has in chronological order answered [ITEM]"
                    # )
                    student_record.append(
                        f"Please predict the furture performance of the student according to his interaction records.[ITEM]"
                    )
                else:
                   
                    cid1 = input_q[i][t-1]
                    correct1 = target[i][t-1]
                    status = "correctly" if correct1 == 1 else "incorrectly"
                    # student_record.append(
                    #     f" question involving concept CID={cid1} at timestep {t-1} {status},[ITEM]"
                    # )
                    t=t-1
                    student_record.append(
                        f"The student has answered question involving concept CID={cid1}  {status} at timestep {t}.[ITEM]"
                    )
            else:
                student_record.append(
                    "[None]"
                )
                
        formatted_records.append(student_record)
    return formatted_records

def format_student_query_concept(input_q):
    formatted_records = []
    for i in range(len(input_q)):
        student_record = []
        for t in range(len(input_q[i])):
           
            cid = input_q[i][t]

            if cid !=0:
           
                student_record.append(
                    # f"Pquestion QID={qid} involving concept CID={cid} at timestep {t}.[QUERY]"
                      f"Predict whether the student can answer question involving concept CID={cid}, at timestep {t}, Yes or No.[QUERY]"
                )

            else:
                student_record.append(
                    "[None1]"
                )             
            
            
        formatted_records.append(student_record)
    return formatted_records


def format_student_records(input_q, input_pid, target):
    formatted_records = []
    for i in range(len(input_q)):
        student_record = []
        nonzero_count = (input_q[i] != 0).sum().item()
        for t in range(len(input_q[i])):
            qid = input_pid[i][t]
            cid = input_q[i][t]
            correct = target[i][t]
      
            status = "correctly" if correct == 1 else "incorrectly"
            
            
            if qid !=0:
                if t == 0:
                    # student_record.append(
                    #     f"The student has in chronological order answered [ITEM]"
                    # )
                    student_record.append(
                        f"Please predict the future performance of the student according to his interaction records. The student has in chronological order answered [ITEM]"
                    )
                else:
                    qid1 = input_pid[i][t-1]
                    cid1 = input_q[i][t-1]
                    correct1 = target[i][t-1]
                    status = "correctly" if correct1 == 1 else "incorrectly"
                    t=t-1
                    student_record.append(
                        f" question QID={qid1} involving concept CID={cid1} at timestep {t} {status},[ITEM]"
                    )
                    # student_record.append(
                    #     f" question involving concept CID={cid1} at timestep {t-1} {status},[ITEM]"
                    # )
            else:
                student_record.append(
                    "[None]"
                )
                
        formatted_records.append(student_record)
    return formatted_records

def format_student_query(input_q, input_pid):
    formatted_records = []
    for i in range(len(input_q)):
        student_record = []
        for t in range(len(input_q[i])):
            qid = input_pid[i][t]
            cid = input_q[i][t]
            
            qid1 = input_pid[i][t]
            cid1 = input_q[i][t]
            
            
            if qid !=0:
           
                student_record.append(
                    # f"Pquestion QID={qid} involving concept CID={cid} at timestep {t}.[QUERY]"
                      f"Predict whether the student can answer question QID={qid}, involving concept CID={cid}, at timestep {t}, Yes or No.[QUERY]"
                )
                # student_record.append(
                #     # f"Pquestion QID={qid} involving concept CID={cid} at timestep {t}.[QUERY]"
                #       f"Predict whether the student can answer question involving concept CID={cid}, at timestep {t}, correctly or No.[QUERY]"
                # )

            else:
                student_record.append(
                    "[None1]"
                )             
            
            
        formatted_records.append(student_record)
    return formatted_records

def binaryEntropy(target, pred, mod="avg"):
    loss = target * np.log(np.maximum(1e-10, pred)) + \
        (1.0 - target) * np.log(np.maximum(1e-10, 1.0-pred))
    if mod == 'avg':
        return np.average(loss)*(-1.0)
    elif mod == 'sum':
        return - loss.sum()
    else:
        assert False
        
def get_embeddings_by_ids(sentence_ids, cls_embeddings):
    embeddings = []
    for sentence_id in sentence_ids:
        if sentence_id in cls_embeddings:
            embeddings.append(cls_embeddings[sentence_id])
        else:
            print(f"警告：编号为{sentence_id}的句子不存在。")
    
    return torch.stack(embeddings)

def compute_auc(all_target, all_pred):
    #fpr, tpr, thresholds = metrics.roc_curve(all_target, all_pred, pos_label=1.0)
    return metrics.roc_auc_score(all_target, all_pred)


def compute_accuracy(all_target, all_pred):
    all_pred[all_pred > 0.5] = 1.0
    all_pred[all_pred <= 0.5] = 0.0
    return metrics.accuracy_score(all_target, all_pred)


def text_to_embedding(texts, model, tokenizer):
    outputs_embed = []
    for text in tqdm(texts,desc="Text to Embedding"):
        item_embeddings = model(text)
        outputs_embed.append(item_embeddings)
    outputs = torch.stack(outputs_embed)
    return outputs


import torch

def get_participation_tensor(input_q):
    """
    根据学生的答题序列生成一个参与状态张量。
    
    参数:
        input_q (torch.Tensor): 形状为 [batch_size, seq_length] 的张量，
                                记录每个学生在每个时间步的题目编号。
    
    返回:
        participated (torch.Tensor): 形状与 input_q 相同的张量，
                                      每个位置记录学生是否之前参与过这道题目：
                                      1 表示之前参与过，
                                      0 表示之前未参与过，
                                      -1 表示题目编号为 0。
    """
    batch_size, seq_length = input_q.size()
    participated = torch.zeros_like(input_q)  # 初始化结果张量

    for i in range(batch_size):  # 遍历每个学生
        seen_questions = set()  # 用于记录当前学生之前见过的题目编号
        for j in range(seq_length):  # 遍历每个时间步
            question_id = input_q[i, j].item()  # 当前时间步的题目编号
            if question_id == 0:
                participated[i, j] = -1  # 题目编号为 0，标记为 -1
            elif question_id in seen_questions:
                participated[i, j] = 1  # 之前见过这道题，标记为 1
            else:
                participated[i, j] = 0  # 之前未见过这道题，标记为 0
            seen_questions.add(question_id)  # 将当前题目编号加入已见过的集合

    return participated

# 示例使用
input_q = torch.tensor([[1, 2, 1, 4], [0, 2, 2, 0]])
participated = get_participation_tensor(input_q)
print("输入的答题序列：\n", input_q)
print("生成的参与状态张量：\n", participated)

def train(net, params,  optimizer, q_data, qa_data, pid_data,  label):
    net.train()
    pid_flag, model_type = model_isPid_type(params.model)
    N = int(math.ceil(len(q_data) / params.batch_size))
    shape = q_data.shape
    user_data = np.arange(1, shape[0] + 1).reshape(shape[0],1)
    
    user_data = user_data.T
    q_data = q_data.T  # Shape: (200,3633)
    qa_data = qa_data.T  # Shape: (200,3633)
    # Shuffle the data
    shuffled_ind = np.arange(q_data.shape[1])
    np.random.shuffle(shuffled_ind)
    q_data = q_data[:, shuffled_ind]
    qa_data = qa_data[:, shuffled_ind]
    user_data = user_data[:, shuffled_ind]
    
    if params.dataset in ["assist2009_pid","assist2017_pid"]:
        id_to_text=True
    else:
        id_to_text=False
    
    if pid_flag:
        pid_data = pid_data.T
        pid_data = pid_data[:, shuffled_ind]

    pred_list = []
    target_list = []

    element_count = 0
    true_el = 0
    """
    加载大模型，完成对ID数据文本转换
    """
    # 加载保存的嵌入向量

    
    # tokenizer, item_model=load_bert_model()
    # Item_LLM = ItemLLM(model=item_model, tokenizer=tokenizer)
    for idx in tqdm(range(N), desc="Training Progress"):
        # print('idx:', idx)
        optimizer.zero_grad()

        q_one_seq = q_data[:, idx*params.batch_size:(idx+1)*params.batch_size]
        if pid_flag:
            pid_one_seq = pid_data[:, idx *
                                   params.batch_size:(idx+1) * params.batch_size]

        qa_one_seq = qa_data[:, idx *
                             params.batch_size:(idx+1) * params.batch_size]

        user_one_seq = user_data[:, idx *
                                    params.batch_size:(idx+1) * params.batch_size]
                                 
        if model_type in transpose_data_model:
            input_q = np.transpose(q_one_seq[:, :])  # Shape (bs, seqlen)
            input_qa = np.transpose(qa_one_seq[:, :])  # Shape (bs, seqlen)
            target = np.transpose(qa_one_seq[:, :])
            
            user_seq= np.transpose(user_one_seq[:, :])
            
            if pid_flag:
                # Shape (seqlen, batch_size)
                input_pid = np.transpose(pid_one_seq[:, :])
        else:
            input_q = (q_one_seq[:, :])  # Shape (seqlen, batch_size)
            input_qa = (qa_one_seq[:, :])  # Shape (seqlen, batch_size)
            target = (qa_one_seq[:, :])
            if pid_flag:
                input_pid = (pid_one_seq[:, :])  # Shape (seqlen, batch_size)
        target = (target - 1) / params.n_question
        target_1 = np.floor(target)
        el = np.sum(target_1 >= -.9)
        element_count += el

        input_q = torch.from_numpy(input_q).long().to(device)
        input_qa = torch.from_numpy(input_qa).long().to(device)
        target = torch.from_numpy(target_1).float().to(device)
        user_order = torch.from_numpy(user_seq).float().to(device)
        if pid_flag:
            input_pid = torch.from_numpy(input_pid).long().to(device)

        if pid_flag:
            if id_to_text:
                text_dir=params.concept_name
                user_seq=user_seq.reshape(-1)
                texts_qa=format_student_records_text(input_q, input_pid, target,text_dir)
                texts_q=format_student_query_text(input_q, input_pid,text_dir)
                meo_target=get_participation_tensor(input_q)
                loss, pred, true_ct = net(texts_q, texts_qa, target,meo_target)
            else:
                user_seq=user_seq.reshape(-1)
                texts_qa=format_student_records(input_q, input_pid, target)
                texts_q=format_student_query(input_q, input_pid)
                meo_target=get_participation_tensor(input_q)
                loss, pred, true_ct = net(texts_q, texts_qa, target,meo_target)
        else:
            texts_qa=format_student_records_concept(input_q, target)
            texts_q=format_student_query_concept(input_q)
            meo_target=get_participation_tensor(input_q)
            loss, pred, true_ct = net(texts_q, texts_qa, target,meo_target)
        pred = pred.detach().cpu().numpy()  # (seqlen * batch_size, 1)
        loss.backward()
        true_el += true_ct.cpu().numpy()

        if params.maxgradnorm > 0.:
            torch.nn.utils.clip_grad_norm_(
                net.parameters(), max_norm=params.maxgradnorm)

        optimizer.step()

        # correct: 1.0; wrong 0.0; padding -1.0
        target = target_1.reshape((-1,))

        nopadding_index = np.flatnonzero(target >= -0.9)
        nopadding_index = nopadding_index.tolist()
        pred_nopadding = pred[nopadding_index]
        target_nopadding = target[nopadding_index]

        pred_list.append(pred_nopadding)
        target_list.append(target_nopadding)

    all_pred = np.concatenate(pred_list, axis=0)
    all_target = np.concatenate(target_list, axis=0)

    loss = binaryEntropy(all_target, all_pred)
    auc = compute_auc(all_target, all_pred)
    accuracy = compute_accuracy(all_target, all_pred)

    return loss, accuracy, auc


def test(net, params, optimizer, q_data, qa_data, pid_data, label):
    # dataArray: [ array([[],[],..])] Shape: (3633, 200)
    pid_flag, model_type = model_isPid_type(params.model)
    net.eval()
    N = int(math.ceil(float(len(q_data)) / float(params.batch_size)))
    shape = q_data.shape
    user_data = np.arange(1, shape[0] + 1).reshape(shape[0],1)
    
    user_data = user_data.T
    q_data = q_data.T  # Shape: (200,3633)
    qa_data = qa_data.T  # Shape: (200,3633)
    
    if params.dataset in ["assist2009_pid","assist2017_pid"]:
        id_to_text=True
    else:
        id_to_text=False
    
    if pid_flag:
        pid_data = pid_data.T
    seq_num = q_data.shape[1]
    pred_list = []
    target_list = []

    count = 0
    true_el = 0
    element_count = 0
    """
    加载大模型，完成对ID数据文本转换
    """
       
    for idx in tqdm(range(N), desc="Valid Progress"):

        q_one_seq = q_data[:, idx*params.batch_size:(idx+1)*params.batch_size]
        if pid_flag:
            pid_one_seq = pid_data[:, idx *
                                   params.batch_size:(idx+1) * params.batch_size]
        input_q = q_one_seq[:, :]  # Shape (seqlen, batch_size)
        qa_one_seq = qa_data[:, idx *
                             params.batch_size:(idx+1) * params.batch_size]
        input_qa = qa_one_seq[:, :]  # Shape (seqlen, batch_size)

        user_one_seq = user_data[:, idx *
                                    params.batch_size:(idx+1) * params.batch_size]
        # print 'seq_num', seq_num
        if model_type in transpose_data_model:
            # Shape (seqlen, batch_size)
            input_q = np.transpose(q_one_seq[:, :])
            # Shape (seqlen, batch_size)
            input_qa = np.transpose(qa_one_seq[:, :])
            target = np.transpose(qa_one_seq[:, :])
            user_seq= np.transpose(user_one_seq[:, :])
            if pid_flag:
                input_pid = np.transpose(pid_one_seq[:, :])
        else:
            input_q = (q_one_seq[:, :])  # Shape (seqlen, batch_size)
            input_qa = (qa_one_seq[:, :])  # Shape (seqlen, batch_size)
            target = (qa_one_seq[:, :])
            if pid_flag:
                input_pid = (pid_one_seq[:, :])
        target = (target - 1) / params.n_question
        target_1 = np.floor(target)
        #target = np.random.randint(0,2, size = (target.shape[0],target.shape[1]))

        input_q = torch.from_numpy(input_q).long().to(device)
        input_qa = torch.from_numpy(input_qa).long().to(device)
        target = torch.from_numpy(target_1).float().to(device)
        user_order = torch.from_numpy(user_seq).float().to(device)
        if pid_flag:
            input_pid = torch.from_numpy(input_pid).long().to(device)

        with torch.no_grad():
            if pid_flag:
                if id_to_text:
                    text_dir=params.concept_name
                    user_seq=user_seq.reshape(-1)
                    texts_qa=format_student_records_text(input_q, input_pid, target,text_dir)
                    texts_q=format_student_query_text(input_q, input_pid,text_dir)
                    meo_target=get_participation_tensor(input_q)
                    loss, pred, ct = net(texts_q, texts_qa, target,meo_target)            
                    # loss, pred, ct = net(texts_q, texts_qa, target)
                else:
                    user_seq=user_seq.reshape(-1)
                    texts_qa=format_student_records(input_q, input_pid, target)
                    texts_q=format_student_query(input_q, input_pid)
                    meo_target=get_participation_tensor(input_q)
                    loss, pred, ct = net(texts_q, texts_qa, target,meo_target)            
                    # loss, pred, ct = net(texts_q, texts_qa, target)
            else:
                texts_qa=format_student_records_concept(input_q, target)
                texts_q=format_student_query_concept(input_q)
                meo_target=get_participation_tensor(input_q)
                loss, pred, ct = net(texts_q, texts_qa, target,meo_target)
        pred = pred.detach().cpu().numpy()  # (seqlen * batch_size, 1)
        true_el += ct.cpu().numpy()
        #target = target.cpu().numpy()
        if (idx + 1) * params.batch_size > seq_num:
            real_batch_size = seq_num - idx * params.batch_size
            count += real_batch_size
        else:
            count += params.batch_size

        # correct: 1.0; wrong 0.0; padding -1.0
        target = target_1.reshape((-1,))
        nopadding_index = np.flatnonzero(target >= -0.9)
        nopadding_index = nopadding_index.tolist()
        pred_nopadding = pred[nopadding_index]
        target_nopadding = target[nopadding_index]

        element_count += pred_nopadding.shape[0]
        # print avg_loss
        pred_list.append(pred_nopadding)
        target_list.append(target_nopadding)

    assert count == seq_num, "Seq not matching"

    all_pred = np.concatenate(pred_list, axis=0)
    all_target = np.concatenate(target_list, axis=0)
    loss = binaryEntropy(all_target, all_pred)
    auc = compute_auc(all_target, all_pred)
    accuracy = compute_accuracy(all_target, all_pred)

    return loss, accuracy, auc



def valid(net, params, optimizer, q_data, qa_data, pid_data, label):
    # dataArray: [ array([[],[],..])] Shape: (3633, 200)
    pid_flag, model_type = model_isPid_type(params.model)
    net.eval()
    N = int(math.ceil(float(len(q_data)) / float(params.batch_size)))
    shape = q_data.shape
    user_data = np.arange(1, shape[0] + 1).reshape(shape[0],1)
    
    user_data = user_data.T
    q_data = q_data.T  # Shape: (200,3633)
    qa_data = qa_data.T  # Shape: (200,3633)
    if pid_flag:
        pid_data = pid_data.T
    seq_num = q_data.shape[1]
    pred_list = []
    target_list = []

    count = 0
    true_el = 0
    element_count = 0

    if params.dataset in ["assist2009_pid","assist2017_pid"]:
        id_to_text=True
    else:
        id_to_text=False
        
    for idx in tqdm(range(N), desc="Testing Progress"):

        q_one_seq = q_data[:, idx*params.batch_size:(idx+1)*params.batch_size]
        if pid_flag:
            pid_one_seq = pid_data[:, idx *
                                   params.batch_size:(idx+1) * params.batch_size]
        input_q = q_one_seq[:, :]  # Shape (seqlen, batch_size)
        qa_one_seq = qa_data[:, idx *
                             params.batch_size:(idx+1) * params.batch_size]
        input_qa = qa_one_seq[:, :]  # Shape (seqlen, batch_size)

        user_one_seq = user_data[:, idx *
                                    params.batch_size:(idx+1) * params.batch_size]
        # print 'seq_num', seq_num
        if model_type in transpose_data_model:
            # Shape (seqlen, batch_size)
            input_q = np.transpose(q_one_seq[:, :])
            # Shape (seqlen, batch_size)
            input_qa = np.transpose(qa_one_seq[:, :])
            target = np.transpose(qa_one_seq[:, :])
            user_seq= np.transpose(user_one_seq[:, :])
            if pid_flag:
                input_pid = np.transpose(pid_one_seq[:, :])
        else:
            input_q = (q_one_seq[:, :])  # Shape (seqlen, batch_size)
            input_qa = (qa_one_seq[:, :])  # Shape (seqlen, batch_size)
            target = (qa_one_seq[:, :])
            if pid_flag:
                input_pid = (pid_one_seq[:, :])
        target = (target - 1) / params.n_question
        target_1 = np.floor(target)
        #target = np.random.randint(0,2, size = (target.shape[0],target.shape[1]))

        input_q = torch.from_numpy(input_q).long().to(device)
        input_qa = torch.from_numpy(input_qa).long().to(device)
        target = torch.from_numpy(target_1).float().to(device)
        user_order = torch.from_numpy(user_seq).float().to(device)
        if pid_flag:
            input_pid = torch.from_numpy(input_pid).long().to(device)

        with torch.no_grad():
            if pid_flag:
                
                if id_to_text:
                    text_dir=params.concept_name
                    user_seq=user_seq.reshape(-1)
                    texts_qa=format_student_records_text(input_q, input_pid, target,text_dir)
                    texts_q=format_student_query_text(input_q, input_pid,text_dir)
                    meo_target=get_participation_tensor(input_q)
                    loss, pred, ct = net(texts_q, texts_qa, target,meo_target)
                else:   
                    user_seq=user_seq.reshape(-1)
                    texts_qa=format_student_records(input_q, input_pid, target)
                    texts_q=format_student_query(input_q, input_pid)
                    meo_target=get_participation_tensor(input_q)
                    loss, pred, ct = net(texts_q, texts_qa, target,meo_target)
            else:
                texts_qa=format_student_records_concept(input_q, target)
                texts_q=format_student_query_concept(input_q)
                meo_target=get_participation_tensor(input_q)
                loss, pred, ct = net(texts_q, texts_qa, target,meo_target)
        pred = pred.detach().cpu().numpy()  # (seqlen * batch_size, 1)
        true_el += ct.cpu().numpy()
        #target = target.cpu().numpy()
        if (idx + 1) * params.batch_size > seq_num:
            real_batch_size = seq_num - idx * params.batch_size
            count += real_batch_size
        else:
            count += params.batch_size

        # correct: 1.0; wrong 0.0; padding -1.0
        target = target_1.reshape((-1,))
        nopadding_index = np.flatnonzero(target >= -0.9)
        nopadding_index = nopadding_index.tolist()
        pred_nopadding = pred[nopadding_index]
        target_nopadding = target[nopadding_index]

        element_count += pred_nopadding.shape[0]
        # print avg_loss
        pred_list.append(pred_nopadding)
        target_list.append(target_nopadding)

    assert count == seq_num, "Seq not matching"

    all_pred = np.concatenate(pred_list, axis=0)
    all_target = np.concatenate(target_list, axis=0)
    loss = binaryEntropy(all_target, all_pred)
    auc = compute_auc(all_target, all_pred)
    accuracy = compute_accuracy(all_target, all_pred)

    return loss, accuracy, auc
