*! _gvar_describe 1.0.1  21aug2026
*! gvar describe -- what is currently in memory: dimensions, the per-unit
*! specification, and the order of the global vector.
*! Author: Merwan Roudane
*! merwanroudane920@gmail.com
*! https://github.com/merwanroudane
*
* The order of x(t) is the part that matters operationally: every
* orthogonalised object depends on it, and {help gvar_irf:gvar irf} with
* type(sgirf) or type(oirf) is only interpretable once you have seen it.
* It is also the order in which every table in the package is laid out.

program define _gvar_describe, rclass
    version 14.0

    syntax [, ORDer VARlist STats noSUMmary ]

    _gvar_require setup

    mata: st_local("N",  strofreal(gvar_getN()))
    mata: st_local("K",  strofreal(gvar_getK()))
    mata: st_local("T",  strofreal(gvar_getT()))
    mata: st_local("cn", invtokens(gvar_getcname()'))
    mata: st_local("vn", invtokens(gvar_getvname()'))
    mata: st_local("gv", invtokens(gvar_getgvname()'))
    mata: st_local("hasf", strofreal(gvar_hasforeign()))
    mata: st_local("hase", strofreal(gvar_isestimated()))
    mata: st_local("hass", strofreal(gvar_issolved()))

    * ---- state --------------------------------------------------------------
    * describe IS the summary, so nosummary silences the whole thing and leaves
    * only r().  Previously it printed regardless, which made the option a lie
    * on the one command whose entire purpose is to print.
    if ("`summary'" == "nosummary") {
        local order   ""
        local varlist ""
        local stats   ""
    }

    if ("`summary'" != "nosummary") {
    _gvar_title "GVAR currently in memory"
    di as text "  Units " as result %-6s "`N'" ///
       as text "  Endogenous variables (K) " as result %-6s "`K'" ///
       as text "  Periods " as result "`T'"
    di as text "  Distinct domestic variables: " as result "`vn'"
    if ("`gv'" != "") {
        di as text "  Global variables: " as result "`gv'"
    }
    di ""
    di as text "  Stage reached:"
    di as text "    setup      " as result "yes"
    local s3 "no"
    if ("`hasf'" == "1") local s3 "yes"
    di as text "    foreign    " as result "`s3'"
    local s4 "no"
    if ("`hase'" == "1") local s4 "yes"
    di as text "    estimate   " as result "`s4'"
    local s5 "no"
    if ("`hass'" == "1") local s5 "yes"
    di as text "    solve      " as result "`s5'"
    if ("`hass'" == "1") {
        mata: st_local("mm", strofreal(gvar_maxmod()))
        di as text "    largest eigenvalue modulus " as result %8.6f `mm'
    }
    di ""
    }

    * ---- per-unit specification ---------------------------------------------
    if ("`summary'" != "nosummary") {
        di as text "{hline 74}"
        di as text %-12s "  Unit" _col(16) "k" _col(22) "k*" _col(30) "p" ///
                   _col(36) "q" _col(44) "case" _col(52) "rank" _col(62) "logL"
        di as text "{hline 74}"
        tempname KI KS LG CS RK
        mata: st_matrix("`KI'", gvar_getki())
        mata: st_matrix("`KS'", gvar_getksi())
        mata: st_matrix("`LG'", gvar_getlags())
        mata: st_matrix("`CS'", gvar_getcase())
        mata: st_matrix("`RK'", gvar_getrank())
        local haslog 0
        if ("`hase'" == "1") {
            tempname LL
            mata: st_matrix("`LL'", gvar_getlogl())
            local haslog 1
        }
        local sumk 0
        local sumr 0
        forvalues i = 1/`N' {
            local u : word `i' of `cn'
            local sumk = `sumk' + `KI'[`i', 1]
            local rr = `RK'[`i', 1]
            if (`rr' < .) local sumr = `sumr' + `rr'
            di as text "  " %-10s abbrev("`u'", 10)          ///
               _col(13) as result %5.0f `=`KI'[`i',1]'       ///
               _col(19) as result %5.0f `=`KS'[`i',1]'       ///
               _col(27) as result %5.0f `=`LG'[`i',1]'       ///
               _col(33) as result %5.0f `=`LG'[`i',2]'       ///
               _col(41) as result %5.0f `=`CS'[`i',1]'       ///
               _col(49) as result %5.0f `rr' _continue
            if (`haslog') di as result %14.2f `=`LL'[`i',1]'
            else          di ""
        }
        di as text "{hline 74}"
        di as text "  k  endogenous variables    k* weakly exogenous variables"
        di as text "  p, q  lag orders of the domestic and foreign blocks"
        di as text "  case  deterministic case 2, 3 or 4 (MacKinnon-Haug-Michelis)"
        di as text "  Total endogenous " as result `sumk' ///
           as text "   total cointegrating rank " as result `sumr' ///
           as text "   implied unit roots " as result `=`K' - `sumr''
        di ""
    }

    * ---- the order of x(t) --------------------------------------------------
    if ("`order'" != "") {
        mata: st_local("xl", gvar_getxlabels())
        di as text "{hline 74}"
        di as text "  {bf:Order of the global vector x(t)}"
        di as text "{hline 74}"
        di as text "  This is the Cholesky ordering used by {bf:type(oirf)}"
        di as text "  and the block boundary used by {bf:type(sgirf)}."
        di as text "  Change it with {bf:first()} and {bf:vorder()}; see"
        di as text "  {help gvar_irf:gvar irf}."
        di ""
        local j 0
        foreach l of local xl {
            local ++j
            di as text %5.0f `j' " " as result %-16s "`l'" _continue
            if (mod(`j', 4) == 0) di ""
        }
        if (mod(`j', 4) != 0) di ""
        di as text "{hline 74}"
        di ""
    }

    * ---- which unit owns which variable -------------------------------------
    if ("`varlist'" != "") {
        di as text "{hline 74}"
        di as text "  {bf:Domestic and weakly exogenous blocks by unit}"
        di as text "{hline 74}"
        forvalues i = 1/`N' {
            local u : word `i' of `cn'
            mata: st_local("yl", invtokens(gvar_getylist(`i')'))
            mata: st_local("sl", invtokens(gvar_getslist(`i')'))
            di as text "  " as result %-10s abbrev("`u'", 10) ///
               as text "  y : " as result "`yl'"
            di as text "  " %-10s "" as text "  y*: " as result "`sl'"
        }
        di as text "{hline 74}"
        di ""
    }

    * ---- descriptive statistics (Toolbox print_dstats.m) --------------------
    tempname DS
    if ("`stats'" != "") {
        _gvar_require foreign
        mata: st_matrix("`DS'", gvar_dstatstab())
        local nk = rowsof(`DS')
        _gvar_dstats_table `DS' "`cn'" "`vn'" "`gv'" `nk'
        return matrix dstats = `DS', copy
    }

    return scalar N = `N'
    return scalar K = `K'
    return scalar T = `T'
    return local  units    "`cn'"
    return local  domestic "`vn'"
    return local  global   "`gv'"
    return scalar estimated = ("`hase'" == "1")
    return scalar solved    = ("`hass'" == "1")
end

* ---------------------------------------------------------------------------
* Descriptive statistics of the variable blocks.
*
* Step -> source map
*   nine statistics per (block, unit, variable)  <- Toolbox print_dstats.m
*   the seven moments                            <- Toolbox dstats.m
*   Jarque-Bera and its probability              <- Toolbox jarquebera.m
*
* print_dstats.m writes three Excel sheets and is called unconditionally by
* gvar.m.  Dumping four hundred rows to the Stata console on every describe
* would be worse than useless, so the same table is produced by an option.
* Nothing about the numbers differs.
*
* Two things about the moments are the Toolbox's own conventions and are kept:
* dstats.m divides the third and fourth central moments by T and by the plain
* standard deviation, so the kurtosis column is the RAW fourth standardised
* moment -- 3 under normality, not 0.
* ---------------------------------------------------------------------------
program define _gvar_dstats_table
    version 14.0
    args D cn vn gv nk

    local vl "`vn' `gv'"
    local nvl : word count `vl'

    forvalues kind = 1/2 {
        local any 0
        forvalues q = 1/`nk' {
            if (`D'[`q', 1] == `kind') local any 1
        }
        if (`any' == 0) continue

        local klab "Domestic variables"
        if (`kind' == 2) local klab "Foreign-specific variables (x*)"

        di ""
        di as text "{hline 96}"
        di as text "  {bf:Descriptive statistics} -- `klab'"
        di as text "{hline 96}"
        di as text %-10s "  Unit" %-8s "var" _col(20) "mean" _col(31) "median" ///
           _col(42) "max" _col(52) "min" _col(62) "sd" _col(71) "skew" ///
           _col(80) "kurt" _col(88) "JB p"
        di as text "{hline 96}"

        local lastu .
        local nnorm 0
        local ntot  0
        forvalues q = 1/`nk' {
            if (`D'[`q', 1] != `kind') continue
            local ii = `D'[`q', 2]
            local jj = `D'[`q', 3]
            local u : word `ii' of `cn'
            local v : word `jj' of `vl'
            if ("`ii'" != "`lastu'") {
                di as text "  " %-8s abbrev("`u'", 8) _continue
                local lastu "`ii'"
            }
            else {
                di as text "  " %-8s "" _continue
            }
            local pv = `D'[`q', 12]
            local st ""
            if (`pv' < .) {
                local ++ntot
                if (`pv' < 0.05) local ++nnorm
                _gvar_stars `pv'
                local st "`r(stars)'"
            }
            di as text %-8s abbrev("`v'", 8) ///
               _col(16) as result %10.4f `=`D'[`q',4]'  ///
               _col(27) as result %10.4f `=`D'[`q',5]'  ///
               _col(38) as result %9.4f  `=`D'[`q',6]'  ///
               _col(48) as result %9.4f  `=`D'[`q',7]'  ///
               _col(58) as result %8.4f  `=`D'[`q',8]'  ///
               _col(67) as result %8.3f  `=`D'[`q',9]'  ///
               _col(76) as result %8.3f  `=`D'[`q',10]' ///
               _col(85) as result %7.3f  `pv' as text "`st'"
        }
        di as text "{hline 96}"
        di as text "  {bf:kurt} is the RAW fourth standardised moment, 3 under" ///
                   " normality, as in"
        di as text "  dstats.m -- not excess kurtosis.  {bf:JB p} is the" ///
                   " Jarque-Bera probability;"
        di as text "  * marks rejection of normality."
        if (`ntot' > 0) {
            di as text "  Non-normal at 5%: " as result `nnorm' as text " of " ///
               as result `ntot' as text " (" as result %4.1f ///
               `=100*`nnorm'/`ntot'' as text "%).  Quarterly macro series are"
            di as text "  routinely non-normal; this does not invalidate the" ///
                       " generalized"
            di as text "  responses, which need only the second moments."
        }
    }
end