"""
..
    Copyright (c) 2014-2016, Magni developers.
    All rights reserved.
    See LICENSE.rst for further information.

Module providing unittests for `magni.cs.phase_transition`.

Routine listings
----------------
TestDataGenerateVector(unittest.TestCase)
    Tests of the _data.generate_vector function.
TestDataGenerateMatrix(unittest.TestCase)
    Tests of the _data.generate_matrix function.
class TestSimulationSimulate(unittest.TestCase)
    Tests of the _simulation._simulate function.

"""

from __future__ import division
import unittest

import numpy as np

import magni


class TestDataGenerateVector(unittest.TestCase):
    """
    Tests of the _data.generate_vector function.

    **Testing Strategy**
    Vectors generated by the function are compared to known "true" vectors.

    """

    def setUp(self):
        self.n = 17
        self.k = 8
        self.seed = 6021
        np.random.seed(self.seed)

    def tearDown(self):
        magni.cs.phase_transition.config.reset()

    def test_default(self):
        x_true = np.array([[1.], [1.], [1.], [-1.], [-1.], [1.], [-1.], [-1.],
                           [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]
                           ])

        x = magni.cs.phase_transition._data.generate_vector(self.n, self.k)

        self.assertTrue(np.allclose(x_true, x))

    def test_rademacher(self):
        magni.cs.phase_transition.config.update(coefficients='rademacher')
        x_true = np.array([[1.], [1.], [1.], [-1.], [-1.], [1.], [-1.], [-1.],
                           [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]
                           ])

        x = magni.cs.phase_transition._data.generate_vector(self.n, self.k)

        self.assertTrue(np.allclose(x_true, x))
        self.assertEqual(
            magni.cs.phase_transition.config['coefficients'],
            'rademacher')

    def test_gaussian(self):
        magni.cs.phase_transition.config.update(coefficients='gaussian')
        x_true = np.array([[0.97267375], [0.95762797], [-1.26236611],
                           [-0.71956614], [1.96767297], [0.5739882],
                           [0.49924214], [-0.98133296],
                           [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.], [0.]
                           ])

        x = magni.cs.phase_transition._data.generate_vector(self.n, self.k)

        self.assertTrue(np.allclose(x_true, x))
        self.assertEqual(
            magni.cs.phase_transition.config['coefficients'],
            'gaussian')


class TestDataGenerateMatrix(unittest.TestCase):
    """
    Tests of the _data.generate_matrix function.

    **Testing Strategy**
    Matrices generated by the function are compared to known "true" matrices.

    """

    def setUp(self):
        self.m = 3
        self.n = 7
        self.seed = 6021

    def tearDown(self):
        magni.cs.phase_transition.config.reset()

    def test_default(self):
        np.random.seed(self.seed)
        A_true = np.array([[0.6794507, 0.37005419, -0.89952085, -0.52267009,
                            0.84951646, 0.49956156, 0.55013383],
                           [-0.6854995, 0.9246622, -0.05085668, -0.38754668,
                            -0.49532825, -0.8434809, -0.80649929],
                           [-0.261605, -0.0897759, -0.43390764,  0.75935733,
                            -0.18158115, -0.19742902, -0.216591]
                           ])

        A = magni.cs.phase_transition._data.generate_matrix(self.m, self.n)

        self.assertTrue(np.allclose(A_true, A))


class TestSimulationSimulate(unittest.TestCase):
    """
    Tests of the _simulation._simulate function.

    **Testing Strategy**
    Phase transition distance simulations are compared to known "true"
    distances.

    """

    _backup = magni.cs.phase_transition._simulation._backup

    def setUp(self):
        self.seed = 6021
        self.delta = [0.2]
        self.rho = [0.1]
        self.monte_carlo = 1
        self.coefficients = 'rademacher'
        self.n = magni.cs.phase_transition.config['problem_size']

        class BackupStub():
            def set(self, path, ij_tup, stat_time, stat_dist):
                self.path = path
                self.ij_tup = ij_tup
                self.stat_time = stat_time
                self.stat_dist = stat_dist

        self.backup_stub = BackupStub()

        magni.cs.phase_transition._simulation._backup = self.backup_stub

    def tearDown(self):
        magni.cs.reconstruction.it.config.reset()
        magni.cs.phase_transition.config.reset()
        magni.cs.phase_transition._simulation._backup = self.__class__._backup

    def test_default(self):
        magni.cs.phase_transition.config.update(
            {'seed': self.seed, 'delta': self.delta, 'rho': self.rho,
             'monte_carlo': self.monte_carlo,
             'coefficients': self.coefficients})

        path = 'path'
        ij_tup = (0, 0)

        self.assertFalse(self.backup_stub.__dict__)

        magni.cs.phase_transition._simulation._simulate(
            magni.cs.reconstruction.it.run, ij_tup, [self.seed], path)

        self.assertEqual(self.backup_stub.path, path)
        self.assertEqual(self.backup_stub.ij_tup, ij_tup)
        self.assertTrue(np.allclose(self.backup_stub.stat_dist,
                                    np.array([6.8865e-6])))
