# Authors: Olivier Grisel <olivier.grisel@ensta.org>
#          Alexandre Gramfort <alexandre.gramfort@inria.fr>
# License: BSD 3 clause

import numpy as np
import pytest
from scipy import interpolate, sparse
from copy import deepcopy
import joblib

from sklearn.base import is_classifier
from sklearn.datasets import load_diabetes
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.pipeline import make_pipeline
from sklearn.preprocessing import StandardScaler

from sklearn.exceptions import ConvergenceWarning
from sklearn.utils._testing import assert_allclose
from sklearn.utils._testing import assert_array_almost_equal
from sklearn.utils._testing import assert_almost_equal
from sklearn.utils._testing import assert_raises
from sklearn.utils._testing import assert_raises_regex
from sklearn.utils._testing import assert_raise_message
from sklearn.utils._testing import assert_warns
from sklearn.utils._testing import assert_warns_message
from sklearn.utils._testing import ignore_warnings
from sklearn.utils._testing import assert_array_equal
from sklearn.utils._testing import TempMemmap
from sklearn.utils.fixes import parse_version

from sklearn.linear_model import (
    ARDRegression,
    BayesianRidge,
    ElasticNet,
    ElasticNetCV,
    enet_path,
    Lars,
    lars_path,
    Lasso,
    LassoCV,
    LassoLars,
    LassoLarsCV,
    LassoLarsIC,
    lasso_path,
    LinearRegression,
    MultiTaskElasticNet,
    MultiTaskElasticNetCV,
    MultiTaskLasso,
    MultiTaskLassoCV,
    OrthogonalMatchingPursuit,
    Ridge,
    RidgeClassifier,
    RidgeCV,
)

from sklearn.linear_model._coordinate_descent import _set_order
from sklearn.utils import check_array


@pytest.mark.parametrize('l1_ratio', (-1, 2, None, 10, 'something_wrong'))
def test_l1_ratio_param_invalid(l1_ratio):
    # Check that correct error is raised when l1_ratio in ElasticNet
    # is outside the correct range
    X = np.array([[-1.], [0.], [1.]])
    Y = [-1, 0, 1]       # just a straight line

    msg = "l1_ratio must be between 0 and 1; got l1_ratio="
    clf = ElasticNet(alpha=0.1, l1_ratio=l1_ratio)
    with pytest.raises(ValueError, match=msg):
        clf.fit(X, Y)


@pytest.mark.parametrize('order', ['C', 'F'])
@pytest.mark.parametrize('input_order', ['C', 'F'])
def test_set_order_dense(order, input_order):
    """Check that _set_order returns arrays with promised order."""
    X = np.array([[0], [0], [0]], order=input_order)
    y = np.array([0, 0, 0], order=input_order)
    X2, y2 = _set_order(X, y, order=order)
    if order == 'C':
        assert X2.flags['C_CONTIGUOUS']
        assert y2.flags['C_CONTIGUOUS']
    elif order == 'F':
        assert X2.flags['F_CONTIGUOUS']
        assert y2.flags['F_CONTIGUOUS']

    if order == input_order:
        assert X is X2
        assert y is y2


@pytest.mark.parametrize('order', ['C', 'F'])
@pytest.mark.parametrize('input_order', ['C', 'F'])
def test_set_order_sparse(order, input_order):
    """Check that _set_order returns sparse matrices in promised format."""
    X = sparse.coo_matrix(np.array([[0], [0], [0]]))
    y = sparse.coo_matrix(np.array([0, 0, 0]))
    sparse_format = "csc" if input_order == "F" else "csr"
    X = X.asformat(sparse_format)
    y = X.asformat(sparse_format)
    X2, y2 = _set_order(X, y, order=order)
    if order == 'C':
        assert sparse.isspmatrix_csr(X2)
        assert sparse.isspmatrix_csr(y2)
    elif order == 'F':
        assert sparse.isspmatrix_csc(X2)
        assert sparse.isspmatrix_csc(y2)


def test_lasso_zero():
    # Check that the lasso can handle zero data without crashing
    X = [[0], [0], [0]]
    y = [0, 0, 0]
    clf = Lasso(alpha=0.1).fit(X, y)
    pred = clf.predict([[1], [2], [3]])
    assert_array_almost_equal(clf.coef_, [0])
    assert_array_almost_equal(pred, [0, 0, 0])
    assert_almost_equal(clf.dual_gap_, 0)


def test_lasso_toy():
    # Test Lasso on a toy example for various values of alpha.
    # When validating this against glmnet notice that glmnet divides it
    # against nobs.

    X = [[-1], [0], [1]]
    Y = [-1, 0, 1]       # just a straight line
    T = [[2], [3], [4]]  # test sample

    clf = Lasso(alpha=1e-8)
    clf.fit(X, Y)