from collections.abc import Iterable
from typing import ClassVar, Literal
from typing_extensions import Self

from numpy import ndarray
from numpy.random import RandomState

from .._typing import ArrayLike, Float, Int, MatrixLike
from ..base import MultiOutputMixin, RegressorMixin
from ..model_selection import BaseCrossValidator
from ._base import LinearModel

SOLVE_TRIANGULAR_ARGS: dict = ...

def lars_path(
    X: None | MatrixLike,
    y: None | ArrayLike,
    Xy: None | MatrixLike | ArrayLike = None,
    *,
    Gram: None | MatrixLike | str = None,
    max_iter: Int = 500,
    alpha_min: Float = 0,
    method: Literal["lar", "lasso"] = "lar",
    copy_X: bool = True,
    eps: Float = ...,
    copy_Gram: bool = True,
    verbose: Int = 0,
    return_path: bool = True,
    return_n_iter: bool = False,
    positive: bool = False,
) -> tuple[ndarray, ndarray, ndarray, int]: ...
def lars_path_gram(
    Xy: MatrixLike | ArrayLike,
    Gram: MatrixLike,
    *,
    n_samples: float,
    max_iter: Int = 500,
    alpha_min: Float = 0,
    method: Literal["lar", "lasso"] = "lar",
    copy_X: bool = True,
    eps: Float = ...,
    copy_Gram: bool = True,
    verbose: Int = 0,
    return_path: bool = True,
    return_n_iter: bool = False,
    positive: bool = False,
) -> tuple[ndarray, ndarray, ndarray, int]: ...

###############################################################################
# Estimator classes

class Lars(MultiOutputMixin, RegressorMixin, LinearModel):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    n_iter_: ArrayLike | int = ...
    intercept_: float | ArrayLike = ...
    coef_: ArrayLike = ...
    coef_path_: ArrayLike | list[ArrayLike] = ...
    active_: list[list] | list = ...
    alphas_: ArrayLike | list[ArrayLike] = ...

    _parameter_constraints: ClassVar[dict] = ...

    method: ClassVar[str] = ...
    positive: ClassVar[bool] = ...

    def __init__(
        self,
        *,
        fit_intercept: bool = True,
        verbose: int | bool = False,
        normalize: str | bool = "deprecated",
        precompute: Literal["auto"] | ArrayLike | bool = "auto",
        n_nonzero_coefs: Int = 500,
        eps: Float = ...,
        copy_X: bool = True,
        fit_path: bool = True,
        jitter: None | Float = None,
        random_state: RandomState | None | Int = None,
    ) -> None: ...
    def fit(
        self,
        X: MatrixLike,
        y: MatrixLike | ArrayLike,
        Xy: None | MatrixLike | ArrayLike = None,
    ) -> Self: ...

class LassoLars(Lars):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    n_iter_: ArrayLike | int = ...
    intercept_: float | ArrayLike = ...
    coef_: ArrayLike = ...
    coef_path_: ArrayLike | list[ArrayLike] = ...
    active_: list[list] | list = ...
    alphas_: ArrayLike | list[ArrayLike] = ...

    _parameter_constraints: ClassVar[dict] = ...

    method: ClassVar[str] = ...

    def __init__(
        self,
        alpha: Float = 1.0,
        *,
        fit_intercept: bool = True,
        verbose: int | bool = False,
        normalize: str | bool = "deprecated",
        precompute: Literal["auto"] | ArrayLike | bool = "auto",
        max_iter: Int = 500,
        eps: Float = ...,
        copy_X: bool = True,
        fit_path: bool = True,
        positive: bool = False,
        jitter: None | Float = None,
        random_state: RandomState | None | Int = None,
    ) -> None: ...

class LarsCV(Lars):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    n_iter_: ArrayLike | int = ...
    mse_path_: ArrayLike = ...
    cv_alphas_: ArrayLike = ...
    alphas_: ArrayLike = ...
    alpha_: float = ...
    coef_path_: ArrayLike = ...
    intercept_: float = ...
    coef_: ArrayLike = ...
    active_: list[list] | list = ...

    _parameter_constraints: ClassVar[dict] = ...

    method: ClassVar[str] = ...

    def __init__(
        self,
        *,
        fit_intercept: bool = True,
        verbose: int | bool = False,
        max_iter: Int = 500,
        normalize: str | bool = "deprecated",
        precompute: Literal["auto"] | ArrayLike | bool = "auto",
        cv: int | BaseCrossValidator | Iterable | None = None,
        max_n_alphas: Int = 1000,
        n_jobs: None | int = None,
        eps: Float = ...,
        copy_X: bool = True,
    ) -> None: ...
    def fit(self, X: MatrixLike, y: ArrayLike) -> Self: ...

class LassoLarsCV(LarsCV):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    active_: list[int] = ...
    n_iter_: ArrayLike | int = ...
    mse_path_: ArrayLike = ...
    cv_alphas_: ArrayLike = ...
    alphas_: ArrayLike = ...
    alpha_: float = ...
    coef_path_: ArrayLike = ...
    intercept_: float = ...
    coef_: ArrayLike = ...

    _parameter_constraints: ClassVar[dict] = ...

    method: ClassVar[str] = ...

    def __init__(
        self,
        *,
        fit_intercept: bool = True,
        verbose: int | bool = False,
        max_iter: Int = 500,
        normalize: str | bool = "deprecated",
        precompute: Literal["auto"] | bool = "auto",
        cv: int | BaseCrossValidator | Iterable | None = None,
        max_n_alphas: Int = 1000,
        n_jobs: None | int = None,
        eps: Float = ...,
        copy_X: bool = True,
        positive: bool = False,
    ) -> None: ...

class LassoLarsIC(LassoLars):
    feature_names_in_: ndarray = ...
    n_features_in_: int = ...
    noise_variance_: float = ...
    criterion_: ArrayLike = ...
    n_iter_: int = ...
    alphas_: ArrayLike | list[ArrayLike] = ...
    alpha_: float = ...
    intercept_: float = ...
    coef_: ArrayLike = ...

    _parameter_constraints: ClassVar[dict] = ...

    def __init__(
        self,
        criterion: Literal["aic", "bic"] = "aic",
        *,
        fit_intercept: bool = True,
        verbose: int | bool = False,
        normalize: str | bool = "deprecated",
        precompute: Literal["auto"] | ArrayLike | bool = "auto",
        max_iter: Int = 500,
        eps: Float = ...,
        copy_X: bool = True,
        positive: bool = False,
        noise_variance: None | Float = None,
    ) -> None: ...
    def fit(self, X: MatrixLike, y: ArrayLike, copy_X: None | bool = None) -> Self: ...
