*! _gvar_overid 1.0.1  21aug2026
*! gvar overid -- likelihood-ratio test of over-identifying restrictions on
*! the cointegrating vectors.
*! Author: Merwan Roudane
*! merwanroudane920@gmail.com
*! https://github.com/merwanroudane
*
* Step -> source map
*   restricted ML estimation of the VECMX*   <- Toolbox mlcoint_r.m
*   restriction input sheet                  <- Toolbox overid_restr.m
*   the test                                 <- gvar.m:1479-1481
*       overid_LR  = -2*(logl_r - logl)
*       overid_dgf = rows(beta)*cols(beta) - cols(beta)^2 - nunrestrpar
*
* The r^2 term removes the just-identifying normalisation; nunrestrpar is the
* number of coefficients left freely estimated, so only the genuinely
* over-identifying restrictions enter the degrees of freedom.
*
* Restrictions are read from a dataset with one row per restricted
* coefficient:
*
*     unit      relation   term        value
*     usa       1          y            1
*     usa       1          r          -1
*     usa       1          _trend       0
*     euro      1          y            1
*     euro      1          y*          -1
*
* {bf:term} names a row of beta: a domestic variable, a foreign variable
* written {it:name}{bf:*}, or {bf:_trend} (case 4) / {bf:_cons} (case 2).
* Every row of a restricted unit's beta must be supplied -- mlcoint_r.m takes
* beta as GIVEN and estimates the rest conditional on it, so a partially
* specified vector is not meaningful.  Coefficients you want estimated freely
* are declared through {bf:free()}, which feeds nunrestrpar.

program define _gvar_overid, rclass
    version 14.0

    syntax [using/] [,                          ///
        FREE(string)                            ///
        LIST                                    ///
        REPLACE                                 ///
        noSUMmary                               ///
        SAVing(name)                            ///
    ]

    _gvar_require vecmx

    mata: st_local("N",  strofreal(gvar_getN()))
    mata: st_local("cn", invtokens(gvar_getcname()'))

    * ---- show the beta layout and stop -------------------------------------
    if ("`list'" != "") {
        _gvar_title "Layout of beta, by unit"
        di as text "  Every row below must appear in the restriction file for"
        di as text "  a unit you restrict.  Rank is the number of relations."
        di ""
        di as text "{hline 78}"
        di as text %-10s "  Unit" %6s "rank" "  beta rows in order"
        di as text "{hline 78}"
        tempname RK
        mata: st_matrix("`RK'", gvar_getrank())
        forvalues i = 1/`N' {
            local u : word `i' of `cn'
            mata: st_local("br", gvar_betarownames(`i'))
            di as text "  " %-8s abbrev("`u'", 8) ///
               as result %6.0f `=`RK'[`i',1]' as text "  `br'"
        }
        di as text "{hline 78}"
        di ""
        exit
    }

    if ("`using'" == "") {
        di as err "specify a restriction dataset:"
        di as err "    {bf:gvar overid using} {it:filename}"
        di as err "with variables {bf:unit relation term value}."
        di as err "Run {bf:gvar overid, list} to see the beta layout first."
        exit 198
    }

    * ---- read the restrictions ---------------------------------------------
    preserve
    quietly use "`using'", clear
    foreach v in unit relation term value {
        capture confirm variable `v'
        if (_rc) {
            di as err "the restriction file must contain {bf:`v'}"
            exit 111
        }
    }
    quietly count
    local nrow = r(N)
    if (`nrow' == 0) {
        di as err "the restriction file is empty"
        exit 2000
    }

    tempname RESTR
    matrix `RESTR' = J(`nrow', 4, .)
    local bad 0
    forvalues q = 1/`nrow' {
        local uu = unit[`q']
        local rr = relation[`q']
        local tt = term[`q']
        local vv = value[`q']

        local ui : list posof "`uu'" in cn
        if (`ui' == 0) {
            di as err "row `q': unit {bf:`uu'} is not in the model"
            local ++bad
            continue
        }
        mata: st_local("br", gvar_betarownames(`ui'))
        local ri : list posof "`tt'" in br
        if (`ri' == 0) {
            di as err "row `q': {bf:`tt'} is not a beta row of unit {bf:`uu'}"
            di as err "   its rows are: `br'"
            local ++bad
            continue
        }
        matrix `RESTR'[`q', 1] = `ui'
        matrix `RESTR'[`q', 2] = `ri'
        matrix `RESTR'[`q', 3] = `rr'
        matrix `RESTR'[`q', 4] = `vv'
    }
    restore
    if (`bad' > 0) {
        di as err "`bad' restriction row(s) could not be matched"
        exit 111
    }

    * ---- free-parameter counts ---------------------------------------------
    tempname NUNR
    matrix `NUNR' = J(`N', 1, 0)
    if ("`free'" != "") {
        local nf : word count `free'
        if (mod(`nf', 2) != 0) {
            di as err "free() takes pairs: {bf:free(}{it:unit} {it:count}" ///
                      " [{it:unit} {it:count} ...]{bf:)}"
            exit 198
        }
        forvalues q = 1(2)`nf' {
            local uu : word `q' of `free'
            local cc : word `=`q'+1' of `free'
            local ui : list posof "`uu'" in cn
            if (`ui' == 0) {
                di as err "free(): unit {bf:`uu'} is not in the model"
                exit 111
            }
            matrix `NUNR'[`ui', 1] = `cc'
        }
    }

    * ---- the test ------------------------------------------------------------
    local st 0
    if ("`replace'" != "") local st 1
    tempname R
    mata: st_matrix("`R'", gvar_overidrun(st_matrix("`RESTR'"), ///
                                          st_matrix("`NUNR'"), `st'))
    local nr = rowsof(`R')
    if (`nr' == 0 | colsof(`R') < 6) {
        di as err "no unit carried a usable restriction"
        di as err "(a unit with rank 0 cannot be restricted)"
        exit 459
    }

    * counters live outside the summary guard: -return- must always have
    * something to read.  Same trap as gvar wetest and gvar stability.
    local nrej 0
    forvalues q = 1/`nr' {
        local pv = `R'[`q', 4]
        if (`pv' < . & `pv' < 0.05) local ++nrej
    }

    if ("`summary'" != "nosummary") {
        _gvar_title "Over-identifying restrictions on the cointegrating vectors"
        di as text "  LR = -2(logL restricted - logL unrestricted)."
        di as text "  d.f. = rows(beta)*rank - rank^2 - free parameters."
        di ""
        di as text "{hline 84}"
        di as text %-12s "  Unit" _col(16) "logL unrestr" _col(32) "logL restr" ///
                   _col(46) "LR" _col(56) "d.f." _col(66) "p (chi2)"
        di as text "{hline 84}"
        forvalues q = 1/`nr' {
            local i = `R'[`q', 1]
            local u : word `i' of `cn'
            local pv = `R'[`q', 4]
            local mk " "
            if (`pv' < . & `pv' < 0.05) local mk "*"
            di as text "  " %-10s abbrev("`u'", 10)      ///
               _col(14) as result %14.3f `=`R'[`q',5]'   ///
               _col(30) as result %13.3f `=`R'[`q',6]'   ///
               _col(44) as result %10.3f `=`R'[`q',2]'   ///
               _col(55) as result %6.0f  `=`R'[`q',3]'   ///
               _col(64) as result %9.3f  `pv' as text "`mk'"
        }
        di as text "{hline 84}"
        di as text "  * rejects the restrictions at 5% on the asymptotic" ///
                   " chi-squared: " as result `nrej' as text " of " ///
                   as result `nr' as text "."
        di ""
        di as text "  {bf:Read the chi-squared p-value with care.}  The GVAR"
        di as text "  Toolbox does not use it: gvar.m takes overid_LR_95cv and"
        di as text "  overid_LR_99cv from bootstrap_GVAR.m, because the country"
        di as text "  models are estimated conditional on weakly exogenous"
        di as text "  regressors whose own uncertainty the asymptotic"
        di as text "  distribution ignores."
        if (`st' == 1) {
            di as text "  The restricted estimates have been installed in the"
            di as text "  model; re-run {bf:gvar solve} to propagate them."
        }
        else {
            di as text "  Add {bf:replace} to install the restricted estimates."
        }
        di ""
    }

    if ("`saving'" != "") {
        matrix `saving' = `R'
    }
    return matrix overid = `R', copy
    return scalar nunits = `nr'
    return scalar nrej   = `nrej'
end