import numpy as np
from numpy.testing import (
    assert_allclose,
    assert_array_equal,
)
import pytest

import statsmodels.datasets.interest_inflation.data as e6
from statsmodels.tools.testing import assert_equal
from statsmodels.tsa.vector_ar.tests.JMulTi_results.parse_jmulti_vecm_output import (
    dt_s_tup_to_string,
    load_results_jmulti,
    sublists,
)
from statsmodels.tsa.vector_ar.util import seasonal_dummies
from statsmodels.tsa.vector_ar.var_model import VARProcess
from statsmodels.tsa.vector_ar.vecm import (
    VECM,
    select_coint_rank,
    select_order,
)

pytestmark = pytest.mark.filterwarnings("ignore:in the future np.array_split")


class DataSet:
    """
    A class for representing the data in a data module.

    Parameters
    ----------
    data_module : module
        A module contained in the statsmodels/datasets directory.
    n_seasons : list
        A list of integers. Each int represents a number of seasons to test the
        model with.
    first_season : list
        A list of integers. Each int corresponds to the int with the same index
        in the n_seasons list and declares to which season the first data entry
        belongs.
    variable_names : list
        A list of strings. Each string names a variable.
    """

    def __init__(self, data_module, n_seasons, first_season, variable_names):
        self.data_module = data_module
        self.seasons = n_seasons
        self.first_season = first_season
        self.dt_s_list = [
            (det, s, self.first_season[i])
            for det in deterministic_terms_list
            for i, s in enumerate(self.seasons)
        ]
        self.variable_names = variable_names

    def __str__(self):
        return self.data_module.__str__()


atol = 0.0005  # absolute tolerance
rtol = 0  # relative tolerance
datasets = []
data = {}
results_ref = {}
results_sm = {}
results_sm_exog = {}
results_sm_exog_coint = {}
coint_rank = 1

debug_mode = False
dont_test_se_t_p = False
deterministic_terms_list = ["nc", "co", "colo", "ci", "cili"]

all_tests = [
    "Gamma",
    "alpha",
    "beta",
    "C",
    "det_coint",
    "Sigma_u",
    "VAR repr. A",
    "VAR to VEC representation",
    "log_like",
    "fc",
    "granger",
    "inst. causality",
    "impulse-response",
    "lag order",
    "test_norm",
    "whiteness",
    "summary",
    "exceptions",
    "select_coint_rank",
]
to_test = all_tests  # ["beta"]


def load_data(dataset, data_dict):
    dtset = dataset.data_module.load_pandas()
    variables = dataset.variable_names
    loaded = dtset.data[variables].astype(float).values
    data_dict[dataset] = loaded.reshape((-1, len(variables)))


def load_results_statsmodels(dataset):
    results_per_deterministic_terms = dict.fromkeys(dataset.dt_s_list)

    for dt_s_tup in dataset.dt_s_list:
        model = VECM(
            data[dataset],
            k_ar_diff=3,
            coint_rank=coint_rank,
            deterministic=dt_s_tup[0],
            seasons=dt_s_tup[1],
            first_season=dt_s_tup[2],
        )
        results_per_deterministic_terms[dt_s_tup] = model.fit(method="ml")
    return results_per_deterministic_terms


def load_results_statsmodels_exog(dataset):
    """
    Load data with seasonal terms in `exog`.

    Same as load_results_statsmodels() except that the seasonal term is
    provided to :class:`VECM`'s `__init__()` method via the `eoxg` parameter.
    This is to check whether the same results are produced no matter whether
    `exog` or `seasons` is being used.

    Parameters
    ----------
    dataset : DataSet
    """
    results_per_deterministic_terms = dict.fromkeys(dataset.dt_s_list)
    endog = data[dataset]
    for dt_s_tup in dataset.dt_s_list:
        det_string = dt_s_tup[0]
        seasons = dt_s_tup[1]
        first_season = dt_s_tup[2]
        if seasons == 0:
            exog = None
        else:
            exog = seasonal_dummies(
                seasons, len(data[dataset]), first_season, centered=True
            )
            if "lo" in dt_s_tup[0]:
                exog = np.hstack((exog, 1 + np.arange(len(endog)).reshape(-1, 1)))
                # remove "lo" since it's now already in exog.
                det_string = det_string[:-2]
        model = VECM(
            endog,
            exog,
            k_ar_diff=3,
            coint_rank=coint_rank,
            deterministic=det_string,
        )
        results_per_deterministic_terms[dt_s_tup] = model.fit(method="ml")
    return results_per_deterministic_terms


def load_results_statsmodels_exog_coint(dataset):
    """
    Load data with deterministic terms in `exog_coint`.

    Same as load_results_statsmodels() except that deterministic terms inside
    the cointegration relation are provided to :class:`VECM`'s `__init__()`
    method via the `eoxg_coint` parameter. This is to check whether the same
    results are produced no matter whether `exog_coint` or the `deterministic`
    argument is being used.

    Parameters
    ----------
    dataset : DataSet
    """
    results_per_deterministic_terms = dict.fromkeys(dataset.dt_s_list)
    endog = data[dataset]
    for dt_s_tup in dataset.dt_s_list:
        det_string = dt_s_tup[0]

        if "ci" not in det_string and "li" not in det_string:
            exog_coint = None
        else:
            exog_coint = []
            if "li" in det_string:
                exog_coint.append(1 + np.arange(len(endog)).reshape(-1, 1))
                det_string = det_string[:-2]
            if "ci" in det_string:
                exog_coint.append(np.ones(len(endog)).reshape(-1, 1))
                det_string = det_string[:-2]
            # reversing (such that constant is first and linear is second)
            exog_coint = exog_coint[::-1]
            exog_coint = np.hstack(exog_coint)
        model = VECM(
            endog,
            exog=None,
            exog_coint=exog_coint,
            k_ar_diff=3,
            coint_rank=coint_rank,
            deterministic=det_string,
            seasons=dt_s_tup[1],
            first_season=dt_s_tup[2],
        )
        results_per_deterministic_terms[dt_s_tup] = model.fit(method="ml")
    return results_per_deterministic_terms


def build_err_msg(ds, dt_s, parameter_str):
    dt = dt_s_tup_to_string(dt_s)
    seasons = dt_s[1]
    err_msg = "Error in " + parameter_str + " for:\n"
    err_msg += "- Dataset: " + ds.__str__() + "\n"
    err_msg += "- Deterministic terms: "
    err_msg += dt_s[0] if dt != "n" else "no det. terms"
    if seasons > 0:
        err_msg += ", seasons: " + str(seasons)
    return err_msg


def setup():
    datasets.append(
        DataSet(e6, [0, 4], [0, 1], ["Dp", "R"]),
        # DataSet(...) TODO: append more data sets for more test cases.
    )

    for ds in datasets:
        load_data(ds, data)
        results_ref[ds] = load_results_jmulti(ds)
        results_sm[ds] = load_results_statsmodels(ds)
        results_sm_exog[ds] = load_results_statsmodels_exog(ds)
        results_sm_exog_coint[ds] = load_results_statsmodels_exog_coint(ds)


setup()


def test_ml_gamma():
    if debug_mode:
        if "Gamma" not in to_test:  # pragma: no cover
            return
        print("\n\nGAMMA", end="")
    for ds in datasets:
        for dt in ds.dt_s_list:
            if debug_mode:
                print("\n" + dt_s_tup_to_string(dt) + ": ", end="")

            exog = results_sm_exog[ds][dt].exog is not None
            exog_coint = results_sm_exog_coint[ds][dt].exog_coint is not None

            # estimated parameter vector
            err_msg = build_err_msg(ds, dt, "Gamma")
            obtained = results_sm[ds][dt].gamma
            obtained_exog = results_sm_exog[ds][dt].gamma
            obtained_exog_coint = results_sm_exog_coint[ds][dt].gamma
            desired = results_ref[ds][dt]["est"]["Gamma"]
            assert_allclose(obtained, desired, rtol, atol, False, err_msg)
            if exog:
                assert_equal(obtained_exog, obtained, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(
                    obtained_exog_coint,
                    obtained,
                    "WITH EXOG_COINT: " + err_msg,
                )

            if debug_mode and dont_test_se_t_p:  # pragma: no cover
                continue
            # standard errors
            obt = results_sm[ds][dt].stderr_gamma
            obt_exog = results_sm_exog[ds][dt].stderr_gamma
            obt_exog_coint = results_sm_exog_coint[ds][dt].stderr_gamma
            des = results_ref[ds][dt]["se"]["Gamma"]
            assert_allclose(obt, des, rtol, atol, False, "STANDARD ERRORS\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)
            # t-values
            obt = results_sm[ds][dt].tvalues_gamma
            obt_exog = results_sm_exog[ds][dt].tvalues_gamma
            obt_exog_coint = results_sm_exog_coint[ds][dt].tvalues_gamma
            des = results_ref[ds][dt]["t"]["Gamma"]
            assert_allclose(obt, des, rtol, atol, False, "t-VALUES\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)
            # p-values
            obt = results_sm[ds][dt].pvalues_gamma
            obt_exog = results_sm_exog[ds][dt].pvalues_gamma
            obt_exog_coint = results_sm_exog_coint[ds][dt].pvalues_gamma
            des = results_ref[ds][dt]["p"]["Gamma"]
            assert_allclose(obt, des, rtol, atol, False, "p-VALUES\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)


def test_ml_alpha():
    if debug_mode:
        if "alpha" not in to_test:  # pragma: no cover
            return
        print("\n\nALPHA", end="")
    for ds in datasets:
        for dt in ds.dt_s_list:
            if debug_mode:
                print("\n" + dt_s_tup_to_string(dt) + ": ", end="")
            exog = results_sm_exog[ds][dt].exog is not None
            exog_coint = results_sm_exog_coint[ds][dt].exog_coint is not None

            err_msg = build_err_msg(ds, dt, "alpha")
            obtained = results_sm[ds][dt].alpha
            obtained_exog = results_sm_exog[ds][dt].alpha
            obtained_exog_coint = results_sm_exog_coint[ds][dt].alpha
            desired = results_ref[ds][dt]["est"]["alpha"]
            assert_allclose(obtained, desired, rtol, atol, False, err_msg)
            if exog:
                assert_equal(obtained_exog, obtained, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(
                    obtained_exog_coint,
                    obtained,
                    "WITH EXOG_COINT: " + err_msg,
                )

            if debug_mode and dont_test_se_t_p:  # pragma: no cover
                continue
            # standard errors
            obt = results_sm[ds][dt].stderr_alpha
            obt_exog = results_sm_exog[ds][dt].stderr_alpha
            obt_exog_coint = results_sm_exog_coint[ds][dt].stderr_alpha
            des = results_ref[ds][dt]["se"]["alpha"]
            assert_allclose(obt, des, rtol, atol, False, "STANDARD ERRORS\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)
            # t-values
            obt = results_sm[ds][dt].tvalues_alpha
            obt_exog = results_sm_exog[ds][dt].tvalues_alpha
            obt_exog_coint = results_sm_exog_coint[ds][dt].tvalues_alpha
            des = results_ref[ds][dt]["t"]["alpha"]
            assert_allclose(obt, des, rtol, atol, False, "t-VALUES\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)
            # p-values
            obt = results_sm[ds][dt].pvalues_alpha
            obt_exog = results_sm_exog[ds][dt].pvalues_alpha
            obt_exog_coint = results_sm_exog_coint[ds][dt].pvalues_alpha
            des = results_ref[ds][dt]["p"]["alpha"]
            assert_allclose(obt, des, rtol, atol, False, "p-VALUES\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)


def test_ml_beta():
    if debug_mode:
        if "beta" not in to_test:  # pragma: no cover
            return
        print("\n\nBETA", end="")
    for ds in datasets:
        for dt in ds.dt_s_list:
            if debug_mode:
                print("\n" + dt_s_tup_to_string(dt) + ": ", end="")

            exog = results_sm_exog[ds][dt].exog is not None
            exog_coint = results_sm_exog_coint[ds][dt].exog_coint is not None

            err_msg = build_err_msg(ds, dt, "beta")
            desired = results_ref[ds][dt]["est"]["beta"]
            rows = desired.shape[0]
            # - first coint_rank rows in JMulTi output have se=t_val=p_val=0
            # - beta includes deterministic terms in cointegration relation in
            #   sm, so we compare only the elements belonging to beta.
            obtained = results_sm[ds][dt].beta[coint_rank:rows]
            obtained_exog = results_sm_exog[ds][dt].beta[coint_rank:rows]
            obtained_exog_coint = results_sm_exog_coint[ds][dt].beta[coint_rank:rows]
            desired = desired[coint_rank:]
            assert_allclose(obtained, desired, rtol, atol, False, err_msg)
            if exog:
                assert_equal(obtained_exog, obtained, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(
                    obtained_exog_coint,
                    obtained,
                    "WITH EXOG_COINT: " + err_msg,
                )

            if debug_mode and dont_test_se_t_p:  # pragma: no cover
                continue
            # standard errors
            obt = results_sm[ds][dt].stderr_beta[coint_rank:rows]
            obt_exog = results_sm_exog[ds][dt].stderr_beta[coint_rank:rows]
            obt_exog_coint = results_sm_exog_coint[ds][dt].stderr_beta[coint_rank:rows]
            des = results_ref[ds][dt]["se"]["beta"][coint_rank:]
            assert_allclose(obt, des, rtol, atol, False, "STANDARD ERRORS\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)
            # t-values
            obt = results_sm[ds][dt].tvalues_beta[coint_rank:rows]
            obt_exog = results_sm_exog[ds][dt].tvalues_beta[coint_rank:rows]
            obt_exog_coint = results_sm_exog_coint[ds][dt].tvalues_beta[coint_rank:rows]
            des = results_ref[ds][dt]["t"]["beta"][coint_rank:]
            assert_allclose(obt, des, rtol, atol, False, "t-VALUES\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)
            # p-values
            obt = results_sm[ds][dt].pvalues_beta[coint_rank:rows]
            obt_exog = results_sm_exog[ds][dt].pvalues_beta[coint_rank:rows]
            obt_exog_coint = results_sm_exog_coint[ds][dt].pvalues_beta[coint_rank:rows]
            des = results_ref[ds][dt]["p"]["beta"][coint_rank:]
            assert_allclose(obt, des, rtol, atol, False, "p-VALUES\n" + err_msg)
            if exog:
                assert_equal(obt_exog, obt, "WITH EXOG: " + err_msg)
            if exog_coint:
                assert_equal(obt_exog_coint, obt, "WITH EXOG_COINT: " + err_msg)


def test_ml_c():  # test deterministic terms outside coint relation
    if debug_mode:
        if "C" not in to_test:  # pragma: no cover
            return
        print("\n\nDET_COEF", end="")
    for ds in datasets:
        for dt in ds.dt_s_list:
            if debug_mode:
                print("\n" + dt_s_tup_to_string(dt) + ": ", end="")

            exog = results_sm_exog[ds][dt].exog is not None
            exog_coint = results_sm_exog_coint[ds][dt].exog_coint is not None

            C_obt = results_sm[ds][dt].det_coef
            C_obt_exog = results_sm_exog[ds][dt].det_coef
            C_obt_exog_coint = results_sm_exog_coint[ds][dt].det_coef
            se_C_obt = results_sm[ds][dt].stderr_det_coef
            se_C_obt_exog = results_sm_exog[ds][dt].stderr_det_coef
            se_C_obt_exog_coint = results_sm_exog_coint[ds][dt].stderr_det_coef
            t_C_obt = results_sm[ds][dt].tvalues_det_coef
            t_C_obt_exog = results_sm_exog[ds][dt].tvalues_det_coef
            t_C_obt_exog_coint = results_sm_exog_coint[ds][dt].tvalues_det_coef
            p_C_obt = results_sm[ds][dt].pvalues_det_coef
            p_C_obt_exog = results_sm_exog[ds][dt].pvalues_det_coef
            p_C_obt_exog_coint = results_sm_exog_coint[ds][dt].pvalues_det_coef

            if "C" not in results_ref[ds][dt]["est"].keys():
                # case: there are no deterministic terms
                if (
                    C_obt.size == 0
                    and se_C_obt.size == 0
                    and t_C_obt.size == 0
                    and p_C_obt.size == 0
                ):
                    assert (True)
                    continue

            desired = results_ref[ds][dt]["est"]["C"]
            dt_string = dt_s_tup_to_string(dt)
            if "co" in dt_string:
                err_msg = build_err_msg(ds, dt, "CONST")
                const_obt = C_obt[:, :1]
                const_obt_exog = C_obt_exog[:, :1]
                const_obt_exog_coint = C_obt_exog_coint[:, :1]
                const_des = desired[:, :1]
                C_obt = C_obt[:, 1:]
                C_obt_exog = C_obt_exog[:, 1:]
                C_obt_exog_coint = C_obt_exog_coint[:, 1:]
                desired = desired[:, 1:]
                assert_allclose(const_obt, const_des, rtol, atol, False, err_msg)
                if exog:
                    assert_equal(const_obt_exog, const_obt, "WITH EXOG: " + err_msg)
                if exog_coint:
                    assert_equal(
                        const_obt_exog_coint,
                        const_obt,
                        "WITH EXOG_COINT: " + err_msg,
                    )
            if "s" in dt_string:
                err_msg = build_err_msg(ds, dt, "SEASONAL")
                if "lo" in dt_string:
                    seas_obt = C_obt[:, :-1]
                    seas_obt_exog = C_obt_exog[:, :-1]
                    seas_obt_exog_coint = C_obt_exog_coint[:, :-1]
                    seas_des = desired[:, :-1]
                else:
                    seas_obt = C_obt
                    seas_obt_exog = C_obt_exog
                    seas_obt_exog_coint = C_obt_exog_coint
                    seas_des = desired
                assert_allclose(seas_obt, seas_des, rtol, atol, False, err_msg)
                if exog:
                    assert_equal(seas_obt_exog, seas_obt, "WITH EXOG: " + err_msg)
                if exog_coint:
                    assert_equal(
                        seas_obt_exog_coint,
                        seas_obt,
                        "WITH EXOG_COINT: " + err_msg,
                    )
            if "lo" in dt_string:
                err_msg = build_err_msg(ds, dt, "LINEAR TREND")
                lt_obt = C_obt[:, -1:]
                lt_obt_exog = C_obt_exog[:, -1:]
                lt_obt_exog_coint = C_obt_exog_coint[:, -1:]
                lt_des = desired[:, -1:]
                assert_allclose(lt_obt, lt_des, rtol, atol, False, err_msg)
                if exog:
                    assert_equal(lt_obt_exog, lt_obt, "WITH EXOG: " + err_msg)
                if exog_coint:
                    assert_equal(
                        lt_obt_exog_coint,
                        lt_obt,
                        "WITH EXOG_COINT: " + err_msg,
                    )
            if debug_mode and dont_test_se_t_p:  # pragma: no cover
                continue
            # standard errors
            se_desired = results_ref[ds][dt]["se"]["C"]
            if "co" in dt_string:
                err_msg = build_err_msg(ds, dt, "SE CONST")
                se_const_obt = se_C_obt[:, 0][:, None]
                se_C_obt = se_C_obt[:, 1:]
                se_const_obt_exog = se_C_obt_exog[:, 0][:, None]
                se_C_obt_exog = se_C_obt_exog[:, 1:]
                se_const_obt_exog_coint = se_C_obt_exog_coint[:, 0][:, None]
                se_C_obt_exog_coint = se_C_obt_exog_coint[:, 1:]
                se_const_des = se_desired[:, 0][:, None]
                se_desired = se_desired[:, 1:]
                assert_allclose(se_const_obt, se_const_des, rtol, atol, False, err_msg)
                if exog:
                    assert_equal(
                        se_const_obt_exog,
                        se_const_obt,
                        "WITH EXOG: " + err_msg,
                    )
                if exog_coint:
                    assert_equal(
                        se_const_obt_exog_coint,
                        se_const_obt,
                        "WITH EXOG_COINT: " + err_msg,
                    )
            if "s" in dt_string:
                err_msg = build_err_msg(ds, dt, "SE SEASONAL")
                if "lo" in dt_string:
                    se_seas_obt = se_C_obt[:, :-1]
                    se_seas_obt_exog = se_C_obt_exog[:, :-1]
                    se_seas_obt_exog_coint = se_C_obt_exog_coint[:, :-1]
                    se_seas_des = se_desired[:, :-1]
                else:
                    se_seas_obt = se_C_obt
                    se_seas_obt_exog = se_C_obt_exog
                    se_seas_obt_exog_coint = se_C_obt_exog_coint
                    se_seas_des = se_desired
                assert_allclose(se_seas_obt, se_seas_des, rtol, atol, False, err_msg)
                if exog:
                    assert_equal(se_seas_obt_exog, se_seas_obt, "WITH EXOG: " + err_msg)
                if exog_coint:
                    assert_equal(
                        se_seas_obt_exog_coint,
                        se_seas_obt,
                        "WITH EXOG_COINT: " + err_msg,
                    )
                if "lo" in dt_string:
                    err_msg = build_err_msg(ds, dt, "SE LIN. TREND")
                    se_lt_obt = se_C_obt[:, -1:]
                    se_lt_obt_exog = se_C_obt_exog[:, -1:]
                    se_lt_obt_exog_coint = se_C_obt_exog_coint[:, -1:]
                    se_lt_des = se_desired[:, -1:]
                    assert_allclose(se_lt_obt, se_lt_des, rtol, atol, False, err_msg)
                    if exog:
                        assert_equal(se_lt_obt_exog, se_lt_obt, "WITH EXOG: " + err_msg)
                    if exog_coint:
                        assert_equal(
                            se_lt_obt_exog_coint,
                            se_lt_obt,
                            "WITH EXOG_COINT: " + err_msg,
                        )
            # t-values
            t_desired = results_ref[ds][dt]["t"]["C"]
            if "co" in dt_string:
                t_const_obt = t_C_obt[:, 0][:, None]
                t_C_obt = t_C_obt[:, 1:]
                t_const_obt_exog = t_C_obt_exog[:, 0][:, None]
                t_C_obt_exog = t_C_obt_exog[:, 1:]
                t_const_obt_exog_coint = t_C_obt_exog_coint[:, 0][:, None]
                t_C_obt_exog_coint = t_C_obt_exog_coint[:, 1:]
                t_const_des = t_desired[:, 0][:, None]
                t_desired = t_desired[:, 1:]
                assert_allclose(
                    t_const_obt,
                    t_const_des,
                    rtol,
                    atol,
                    False,
                    build_err_msg(ds, dt, "T CONST"),
                )
                if exog:
                    assert_equal(t_const_obt_exog, t_const_obt, "WITH EXOG: " + err_msg)
                if exog_coint:
                    assert_equal(
                        t_const_obt_exog_coint,
                        t_const_obt,
                        "WITH EXOG_COINT: " + err_msg,
                    )
            if "s" in dt_string:
                if "lo" in dt_string:
                    t_seas_obt = t_C_obt[:, :-1]
                    t_seas_obt_exog = t_C_obt_exog[:, :-1]
                    t_seas_obt_exog_coint = t_C_obt_exog_coint[:, :-1]
                    t_seas_des = t_desired[:, :-1]
                else:
                    t_seas_obt = t_C_obt
                    t_seas_obt_exog = t_C_obt_exog
                    t_seas_obt_exog_coint = t_C_obt_exog_coint
                    t_seas_des = t_desired
                assert_allclose(
                    t_seas_obt,
                    t_seas_des,
                    rtol,
                    atol,
                    False,
                    build_err_msg(ds, dt, "T SEASONAL"),
                )
                if exog:
                    assert_equal(t_seas_obt_exog, t_seas_obt, "WITH EXOG" + err_msg)
                if exog_coint:
                    assert_equal(
                        t_seas_obt_exog_coint,
                        t_seas_obt,
                        "WITH EXOG_COINT" + err_msg,
                    )
            if "lo" in dt_string:
                t_lt_obt = t_C_obt[:, -1:]
                t_lt_obt_exog = t_C_obt_exog[:, -1:]
                t_lt_obt_exog_coint = t_C_obt_exog_coint[:, -1:]
                t_lt_des = t_desired[:, -1:]
                assert_allclose(
                    t_lt_obt,
                    t_lt_des,
                    rtol,
                    atol,
                    False,
                    build_err_msg(ds, dt, "T LIN. TREND"),
                )
                if exog:
                    assert_equal(t_lt_obt_exog, t_lt_obt, "WITH EXOG" + err_msg)
                if exog_coint:
                    assert_equal(
                        t_lt_obt_exog_coint,
                        t_lt_obt,
                        "WITH EXOG_COINT" + err_msg,
                    )
            # p-values
            p_desired = results_ref[ds][dt]["p"]["C"]
            if "co" in dt_string:
                p_const_obt = p_C_obt[:, 0][:, None]
                p_C_obt = p_C_obt[:, 1:]
                p_const_obt_exog = p_C_obt_exog[:, 0][:, None]
                p_C_obt_exog = p_C_obt_exog[:, 1:]
                p_const_obt_exog_coint = p_C_obt_exog_coint[:, 0][:, None]
                p_const_des = p_desired[:, 0][:, None]
                p_desired = p_desired[:, 1:]
                assert_allclose(
                    p_const_obt,
                    p_const_des,
                    rtol,
                    atol,
                    False,
                    build_err_msg(ds, dt, "P CONST"),
                )
                if exog:
                    assert_equal(p_const_obt, p_const_obt_exog, "WITH EXOG" + err_msg)
                if exog_coint:
                    assert_equal(
                        p_const_obt,
                        p_const_obt_exog_coint,
                        "WITH EXOG_COINT" + err_msg,
                    )
            if "s" in dt_string:
                if "lo" in dt_string:
                    p_seas_obt = p_C_obt[:, :-1]
                    p_seas_obt_exog = p_C_obt_exog[:, :-1]
                    p_seas_obt_exog_coint = p_C_obt_exog_coint[:, :-1]
                    p_seas_des = p_desired[:, :-1]
                else:
                    p_seas_obt = p_C_obt
                    p_seas_obt_exog = p_C_obt_exog
                    p_seas_obt_exog_coint = p_C_obt_exog_coint
                    p_seas_des = p_desired
                assert_allclose(
                    p_seas_obt,
                    p_seas_des,
                    rtol,
                    atol,
                    False,
                    build_err_msg(ds, dt, "P SEASONAL"),
                )
                if exog:
                    assert_equal(p_seas_obt_exog, p_seas_obt, "WITH EXOG" + err_msg)
                if exog_coint:
                    assert_equal(
                        p_seas_obt_exog_coint,
                        p_seas_obt,
                        "WITH EXOG_COINT" + err_msg,
                    )
            if "lo" in dt_string:
                p_lt_obt = p_C_obt[:, -1:]
                p_lt_obt_exog = p_C_obt_exog[:, -1:]
                p_lt_obt_exog_coint = p_C_obt_exog_coint[:, -1:]
                p_lt_des = p_desired[:, -1:]
                assert_allclose(
                    p_lt_obt,
                    p_lt_des,
                    rtol,
                    atol,
                    False,
                    build_err_msg(ds, dt, "P LIN. TREND"),
                )
                if exog:
                    assert_equal(p_lt_obt_exog, p_lt_obt, "WITH EXOG" + err_msg)
                if exog_coint:
                    assert_equal(
                        p_lt_obt_exog_coint,
                        p_lt_obt,
                        "WITH EXOG_COINT" + err_msg,
                    )


def test_ml_det_terms_in_coint_relation():
    if debug_mode:
        if "det_coint" not in to_test:  # pragma: no cover
            return
        print("\n\nDET_COEF_COINT", end="")
    for ds in datasets:
        for dt in ds.dt_s_list:
            if debug_mode:
                print("\n" + dt_s_tup_to_string(dt) + ": ", end="")

            exog = results_sm_exog[ds][dt].exog is not None
            exog_coint = results_sm_exog_coint[ds][dt].exog_coint is not None

            err_msg = build_err_msg(ds, dt, "det terms in coint relation")
            dt_string = dt_s_tup_to_string(dt)
            obtained = results_sm[ds][dt].det_coef_coint
            obtained_exog = results_sm_exog[ds][dt].det_coef_coint
            obtained_exog_coint = results_sm_exog_coint[ds][dt].det_coef_coint
            if "ci" not in dt_string and "li" not in dt_string:
                if obtained.size > 0:
                    pytest.fail(
                        build_err_msg(
                            ds,
                            dt,
                            (
                                "There should not be any det terms in cointegration "
                                "for deterministic terms" + dt_string
                            ),
                        )
                    )

                else:
                    assert (True)
                continue
            desired = results_ref[ds][dt]["est"]["det_coint"]
            assert_allclose(obtained, desired, rtol, atol, False, err_msg)
            if exog:
                assert_equal(obtained_exog, obtained, "WITH EXOG" + err_msg)
            if exog_coint:
                assert_equal(obtained_exog_coint, obtained, "WITH EXOG_COINT" + err_msg)
            # standard errors
            se_obtained = results_sm[ds][dt].stderr_det_coef_coint
            se_obtained_exog = results_sm_exog[ds][dt].stderr_det_coef_coint
            se_obtained_exog_coint = results_sm_exog_coint[ds][dt].stderr_det_coef_coint
            se_desired = results_ref[ds][dt]["se"]["det_coint"]
            assert_allclose(
                se_obtained,
                se_desired,
                rtol,
                atol,
                False,
                "STANDARD ERRORS\n" + err_msg,
            )
            if exog:
                assert_equal(se_obtained_exog, se_obtained, "WITH EXOG" + err_msg)
            if exog_coint:
                assert_equal(
                    se_obtained_exog_coint,
                    se_obtained,
                    "WITH EXOG_COINT" + err_msg,
                )
            # t-values
            t_obtained = results_sm[ds][dt].tvalues_det_coef_coint
            t_obtained_exog = results_sm_exog[ds][dt].tvalues_det_coef_coint
            t_obtained_exog_coint = results_sm_exog_coint[ds][dt].tvalues_det_coef_coint
            t_desired = results_ref[ds][dt]["t"]["det_coint"]
            assert_allclose(
                t_obtained,
                t_desired,
                rtol,
                atol,
                False,
                "t-VALUES\n" + err_msg,
            )
            if exog:
