# Copyright (c) 2020 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
MPC Metrics
"""

import paddle.fluid.metrics
from paddle.fluid.metrics import MetricBase

import numpy as np
import scipy


__all__ = [
    'KSstatistic',
    'Auc',
]


def _is_numpy_(var):
    return isinstance(var, (np.ndarray, np.generic))


class KSstatistic(MetricBase):
    """
    The is for binary classification.
    Refer to https://en.wikipedia.org/wiki/Kolmogorov%E2%80%93Smirnov_test#Kolmogorov%E2%80%93Smirnov_statistic
    Please notice that the KS statistic is implemented with scipy.

    The `KSstatistic` function creates 2 local variables, `data1`, `data2`
    which is predictions of positive samples and negative samples respectively
    that are used to compute the KS statistic.

    Args:
        name (str, optional): Metric name. For details, please refer to :ref:`api_guide_Name`. Default is None.

    Examples:
        .. code-block:: python

            import paddle_fl.mpc
            import numpy as np
            # init the KSstatistic
            ks = paddle_fl.mpc.metrics.KSstatistic('ks')

            # suppose that batch_size is 128
            batch_num = 100
            batch_size = 128

            for batch_id in range(batch_num):

                class0_preds = np.random.random(size = (batch_size, 1))
                class1_preds = 1 - class0_preds

                preds = np.concatenate((class0_preds, class1_preds), axis=1)

                labels = np.random.randint(2, size = (batch_size, 1))
                ks.update(preds = preds, labels = labels)

                # shall be some score closing to 0.1 as the preds are randomly assigned
                print("ks statistic for iteration %d is %.2f" % (batch_id, ks.eval()))

    """

    def __init__(self, name=None):
        super(KSstatistic, self).__init__(name=name)
        self._data1 = []
        self._data2 = []

    def update(self, preds, labels):
        """
        Update the auc curve with the given predictions and labels.

        Args:
             preds (numpy.array): an numpy array in the shape of
             (batch_size, 2), preds[i][j] denotes the probability of
             classifying the instance i into the class j.

             labels (numpy.array): an numpy array in the shape of
             (batch_size, 1), labels[i] is either o or 1, representing
             the label of the instance i.
        """
        if not _is_numpy_(labels):
            raise ValueError("The 'labels' must be a numpy ndarray.")
        if not _is_numpy_(preds):
            raise ValueError("The 'predictions' must be a numpy ndarray.")

        data1 = [preds[i, 1] for i, lbl in enumerate(labels) if lbl]
        data2 = [preds[i, 1] for i, lbl in enumerate(labels) if not lbl]

        self._data1 += data1
        self._data2 += data2

    def eval(self):
        """
        Return the area (a float score) under auc curve

        Return:
            float: the area under auc curve
        """

        return scipy.stats.ks_2samp(self._data1, self._data2).statistic


Auc = paddle.fluid.metrics.Auc
