#  Copyright 2020 Modelling, Simulation and Design Lab (MSDL) at
#  McGill University and the University of Antwerp (http://msdl.cs.mcgill.ca/)
#
#  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.

"""Test that the external math functions are correct.

See Also:
    :mod:`pypdevsbbl.extra.mathutils`

Warning:
    The execution of these tests might take a bit.
    This is because all :class:`pypdevsbbl.extra.mathutils.BoxplotAlgorithm`
    instances are tested at the same time.
"""

import unittest
from pypdevsbbl.extra.mathutils import *
import numpy.random as rng
import pandas as pd

class TestMathutils(unittest.TestCase):
    def test_algorithms(self):
        algos = [
            BoxplotAlgorithm.MEDIAN,
            BoxplotAlgorithm.TUKEY,
            BoxplotAlgorithm.MINITAB,
            BoxplotAlgorithm.FREUND_PERLES,
            BoxplotAlgorithm.MOORE_MCCABE,
            BoxplotAlgorithm.MENDENHALL_SINCICH,
            BoxplotAlgorithm.FAME,
            BoxplotAlgorithm.P_SQUARE,
        ]
        cols = [a.name for a in algos]

        N = 10000
        Q1_tests = {A.name: [] for A in algos}
        Q3_tests = {A.name: [] for A in algos}
        EC = []
        errors = []

        for l in range(N):
            x = rng.uniform(1.0, 100.0, 1000)
            x = [float(n) for n in x]

            frame = pd.DataFrame(columns=cols)

            for r in ["Q1", "Q3", "IQR"]:
                frame.loc[r] = 0

            for A in algos:
                r = compute_boxplot(x, A)
                frame.at["Q1", A.name] = r["Q1"]
                frame.at["Q3", A.name] = r["Q3"]
                frame.at["IQR", A.name] = r["Q3"] - r["Q1"]

                Q1T = binprob(frame.at["Q1", A.name], len(x), 0.25)
                Q3T = binprob(frame.at["Q3", A.name], len(x), 0.75)
                frame.at["Q1 test", A.name] = Q1T
                frame.at["Q3 test", A.name] = Q3T

                Q1_tests[A.name].append(Q1T)
                Q3_tests[A.name].append(Q3T)
                if abs(Q1T) > 5 or abs(Q3T) > 5:
                    EC.append(r)

        frame = pd.DataFrame(columns=cols)
        frame.loc["Q1 mean"] = 0
        frame.loc["Q1 std"] = 0
        frame.loc["Q3 mean"] = None
        frame.loc["Q3 std"] = None
        for A in algos:
            frame.at["Q1 mean", A.name] = mean(Q1_tests[A.name])
            frame.at["Q1 std", A.name] = std(Q1_tests[A.name])
            frame.at["Q3 mean", A.name] = mean(Q3_tests[A.name])
            frame.at["Q3 std", A.name] = std(Q3_tests[A.name])

        if len(EC) > 0:
            print("\n%i EDGE CASES FOUND:" % len(EC))
            for ec in EC:
                print("\t", ec)
            print()

        assert len(errors) == 0

if __name__ == '__main__':
    unittest.main()
