from __future__ import print_function
import torch
import torch.nn as nn
from torch.autograd import Variable
import numpy as np
import torch.nn.functional as F
import torchvision
import torchvision.transforms as transforms
import time
from scipy import misc


def tpr95(in_scores, out_scores, start=None, end=None):
    if start is None:
        start = min(np.min(in_scores), np.min(out_scores)) - 1e-5
        end = max(np.max(in_scores), np.max(out_scores)) + 1e-5
    deltas = np.linspace(start, end, 100000)
    total = 0.0
    fpr = 0.0
    for delta in deltas:
        tpr = np.sum(np.sum(in_scores >= delta)) / np.float(len(in_scores))
        error2 = np.sum(np.sum(out_scores >= delta) / np.float(len(out_scores)))
        if tpr <= 0.9505 and tpr >= 0.9495:
            fpr += error2
            total += 1
    fpr95 = fpr / total
    return fpr95

def auroc(in_scores, out_scores, start=None, end=None):
    if start is None:
        start = min(np.min(in_scores), np.min(out_scores)) - 1e-5
        end = max(np.max(in_scores), np.max(out_scores)) + 1e-5
    deltas = np.linspace(start, end, 100000)
    auroc = 0.0
    fpr_tmp = 1.0
    for delta in deltas:
        tpr = np.sum(np.sum(in_scores >= delta)) / np.float(len(in_scores))
        fpr = np.sum(np.sum(out_scores >= delta) / np.float(len(out_scores)))
        auroc += (fpr_tmp - fpr) * tpr
        fpr_tmp = fpr
    auroc += fpr * tpr
    return auroc

def aupr_in(in_scores, out_scores, start=None, end=None):
    if start is None:
        start = min(np.min(in_scores), np.min(out_scores)) - 1e-5
        end = max(np.max(in_scores), np.max(out_scores)) + 1e-5
    deltas = np.linspace(start, end, 100000)
    precisions = []
    recalls = []
    aupr = 0.0
    recall_tmp = 1.0
    for delta in deltas:
        tp = np.sum(np.sum(in_scores >= delta)) / np.float(len(in_scores))
        fp = np.sum(np.sum(out_scores >= delta) / np.float(len(out_scores)))
        if tp + fp == 0: continue
        precision = tp / (tp + fp)
        recall = tp
        precisions.append(precision)
        recalls.append(recall)
        aupr += (recall_tmp - recall) * precision
        recall_tmp = recall
    aupr += recall * precision
    return aupr

def aupr_out(in_scores, out_scores, start=None, end=None):
    if start is None:
        start = min(np.min(in_scores), np.min(out_scores)) - 1e-5
        end = max(np.max(in_scores), np.max(out_scores)) + 1e-5
    deltas = np.linspace(start, end, 100000)[::-1]
    precisions = []
    recalls = []
    aupr = 0.0
    recall_tmp = 1.0
    for delta in deltas:
        fp = np.sum(np.sum(in_scores < delta)) / np.float(len(in_scores))
        tp = np.sum(np.sum(out_scores < delta) / np.float(len(out_scores)))
        if tp + fp == 0: break
        precision = tp / (tp + fp)
        recall = tp
        precisions.append(precision)
        recalls.append(recall)
        aupr += (recall_tmp - recall) * precision
        recall_tmp = recall
    aupr += recall * precision
    return aupr

