"""
Testcases for encoders:
- Exclude
- Log10
- Normalization

Separate linear-reduction tests:
- PCA
- Karuhnen-Loeve
"""
import numpy as np
from profit.sur.encoders import Encoder


def test_exclude():

    COLUMNS= [2]
    CONFIG = {'class': 'Exclude', 'columns': COLUMNS, 'parameters': {}}
    SIZE = (10, 4)
    n = SIZE[0] * SIZE[1]
    X = np.linspace(0, n-1, n).reshape(SIZE)

    enc = Encoder['Exclude'](COLUMNS)
    assert enc.repr == CONFIG
    X_enc = enc.encode(X)
    assert np.all(X_enc == X[:, [0, 1, 3]])
    X_dec = enc.decode(X_enc)
    assert np.all(X_dec == X)


def test_log10():
    from profit.sur.encoders import Log10Encoder

    COLUMNS= [2, 3]
    CONFIG = {'class': 'Log10', 'columns': COLUMNS, 'parameters': {}}
    SIZE = (10, 4)
    n = SIZE[0] * SIZE[1]
    X = np.linspace(0, n-1, n).reshape(SIZE)
    X_log = X.copy()
    X_log[:, COLUMNS] = np.log10(X_log[:, COLUMNS])

    enc = Log10Encoder(COLUMNS)
    assert enc.repr == CONFIG
    X_enc = enc.encode(X)
    assert np.all(X_enc == X_log)
    X_dec = enc.decode(X_enc)
    assert np.allclose(X_dec, X, atol=1e-7)


def test_normalization():

    COLUMNS= [0, 1, 2, 3]
    CONFIG = {'class': 'Normalization', 'columns': COLUMNS, 'parameters': {}}
    SIZE = (10, 4)
    n = SIZE[0] * SIZE[1]
    X = np.linspace(0, n-1, n).reshape(SIZE)
    norm_box = np.hstack([np.linspace(0, 1, SIZE[0]).reshape(-1, 1) for _ in range(SIZE[1])])

    enc = Encoder['Normalization'](COLUMNS)
    assert enc.repr == CONFIG
    X_enc = enc.encode(X)
    assert np.allclose(X_enc, norm_box)
    X_dec = enc.decode(X_enc)
    assert np.allclose(X_dec, X)

    y = np.sin(X[:, [1]])
    enc = Encoder['Normalization']([0])
    y_enc = enc.encode(y)
    assert np.allclose(np.max(y_enc), 1)
    assert np.allclose(np.min(y_enc), 0)
    y_dec = enc.decode(y_enc)
    assert np.allclose(y_dec, y)

    hyperparameters = {'length_scale': np.array([1.3, 0.2, 2.4]),
                       'sigma_f': np.array([0.7]),
                       'sigma_n': np.array([0.05])}

    xmean = 1.7
    xstd = 2.8
    xmin = 0.5
    xmax = 3
    xmax_centered = (xmax - xmean) / xstd
    xmin_centered = (xmin - xmean) / xstd
    scaling = (xmax_centered - xmin_centered) * xstd

    enc = Encoder['Normalization'](COLUMNS, parameters={'xmean': xmean, 'xstd': xstd, 'xmin': xmin, 'xmax': xmax,
                                                        'xmin_centered': xmin_centered, 'xmax_centered': xmax_centered})
    length_scale_dec = enc.decode_hyperparameters(hyperparameters['length_scale'])
    sigma_f_dec = np.sqrt(enc.decode_variance(hyperparameters['sigma_f']**2))
    sigma_n_dec = np.sqrt(enc.decode_variance(hyperparameters['sigma_n']**2))
    assert np.allclose(length_scale_dec, hyperparameters['length_scale'] * scaling)
    assert np.allclose(sigma_f_dec, hyperparameters['sigma_f'] * scaling)
    assert np.allclose(sigma_n_dec, hyperparameters['sigma_n'] * scaling)
