*! version 2.3  Thursday, July 3, 2003 at 12:22        (SJ3-3: st0000)
/*
   milincom calculates individual lincoms and then combines them
   using Rubin's rule.

   Syntax :
           milincom [, INDIV] : expression [, Level(#) OR HR IRr RRr EForm]

   Call :
          _mi_RUBIN
*/

program define milincom, rclass
    version 7
    preserve

    global mi_combine3 T

    cap assert "$mimps"~=""&"$mi_sf"~=""
    if _rc {
        display as error "please set up your data with -{help miset}- first"
        exit 198
    }

    if "$mi_combine1"=="F" | "$mi_combine2"=="F" {
        di "{err} no variable defined due to combining not possible"
        exit 111
    }

/* Parsing args */

    * Process INDIV option if it is present.
    gettoken indiv express_optn: 0, parse(":")
    if trim("`indiv'")=="" | trim("`indiv'")=="," | trim("`indiv'")=="`0'" { error 198 }
    if "`indiv'"==":" { local indiv = ""}
    else {gettoken colon express_optn: express_optn, parse(":")}

    local 0 `indiv'
    syntax [, INDIV]

    * Assign the linear combination of coefs to `expression' and process the options.
    gettoken expression options: express_optn, parse(",")
    local 0 `options'
    syntax [, Level(integer $S_level) OR HR IRr RRr EForm]

    * Assign lincom options to `options'
    gettoken comma options: 0, parse(",")
    local options = trim("`options'")

    * Make OR the default option after -logistic-, as in -lincom-.
    if "`e(cmd)'"=="logistic" & "`options'"=="" {
        local options "or"
        local or "or"
    }

/* Execute lincom in individual datasets */

    local obslist=e(obs_mi1)
    forvalues i=2/$mimps{
        local obs`i'=e(obs_mi`i')
        local obslist `obslist' , `obs`i''
    }
    local miobs=min(`obslist')
    tempfile lncmfile combfile
    MILCM `"`expression'"' _mimodel $mimps `lncmfile' `indiv' `options'
    qui use `lncmfile', clear

/* Combine individual lincoms */

    qui _mi_RUBIN 1 `lncmfile' `combfile' `level'

/* Trap any potential errors and display messages.
The flag $mi_combine3 are set in _mi_RUBIN.ado  */

    if "$mi_combine3"=="F" {
        display as error "combining estimates is not possible: no variation between datasets"
        exit 498
    }

    qui use `combfile', clear
    tempname df mn se t p l u
    scalar `df' = midof in 1
    scalar `mn' = avest in 1
    scalar `se' = totalv in 1
    scalar `se' = sqrt(`se')
    scalar `t' = `mn'/`se'
    scalar `p' = 2* ttail(`df', abs(`t'))
    scalar `l' = milb in 1
    scalar `u' = miub in 1

/* Exponential transformations on overall combined results if eform, or, irr, rrr, hr options are specified*/
    if `"`or'"'!="" | `"`hr'"'!="" | `"`irr'"'!="" |`"`rrr'"'!="" |`"`eform'"'!="" {
        scalar `mn' = exp(`mn')
        scalar `se' = `mn'*`se'
        scalar `l' = exp(`l')
        scalar `u' = exp(`u')
    }
    if `df'>99999 {  local fmtdf %9.2e }
    else {  local fmtdf %9.2f }

/* Display final results */

    if "`optindiv'"=="individual"{
        local diovall ->
        local di
    }
    else { local di di }
    qui use _mitemp1, clear
    local z `e(depvar)'
    local fmt : format `z'
    if substr("`fmt'",-1,1)=="f" {
        local fmt="%9."+substr("`fmt'",-2,2)
    }
    else if substr("`fmt'",-2,2)=="fc" {
        local fmt="%9."+substr("`fmt'",-3,3)
    }
    else local fmt "%9.0g"

    `di'
    di in gr "`diovall' Overall estimates " _n
    di in gr "{hline 13}{c TT}{hline 64}"
    local t0 = abbrev("`z'",12)
    if `"`or'"'!="" {
        local tt " Odds Ratio"
    }
    else if `"`hr'"'!="" {
        local tt " Haz. Ratio"
    }
    else if `"`irr'"'!="" {
        local tt "        IRR"
    }
    else if `"`rrr'"'!="" {
        local tt "        RRR"
    }
    else if `"`eform'"'!="" {
        local tt "     exp(b)"
    }
    else {
        local tt "      Coef."
    }

    #delimit ;
    di in smcl in gr
    %12s "`t0'" _col(14)"{c |}`tt'  Std. Err.    t   P>|t|  [`level'% Conf. Interval]  MI.df"
        _n "{hline 13}{c +}{hline 64}" ;
    #delimit cr

    di in smcl in gr /*
   */  "         (1)"  _col(14) "{c |}"  /*
   */ _col(17)  in ye  `fmt'   `mn`i''   /*
   */ _col(27)  `fmt'  `se'     /*
   */ _col(36)    %7.2f    `t'  /*
   */ _col(42)    %7.3f    `p'  /*
   */ _col(51)  `fmt'  `l'      /*
   */ _col(61)  `fmt'  `u'      /*
   */ _col(69)  `fmtdf'   `df'
   di in gr "{hline 13}{c BT}{hline 64}"

/* Return overall results */

    ret scalar MI_estimate = `mn'
    ret scalar MI_se = `se'
    ret scalar MI_df = `df'
end

program define MILCM
    args expression model m results indiv options
/* expression -- the origional linear combination of variables
   model -- prefix of est hold name
   m -- # of multiple datasets
   results -- data file holding lincom results
   indiv -- MI option indiv or not
   options -- lincom options
*/
    tempfile result
    tempname memhold
    postfile `memhold' parm tt obs estimate se lb ub using `result'

/* Do lincom for each dataset */
    forvalues t=1/`m' {
        qui use $mi_sf`t', clear
        est unhold `model'`t'
        est hold `model'`t', copy
        _clearcmd
        cap lincom `expression'
        if _rc {
            cap noisily lincom `expression'
            exit _rc
        }
        * Run lincon for each dataset with options if INDIV is specified
        if "`indiv'" == "indiv" {
            __mydis $mi_sf `t'
            di in gr "-> applying lincom to `r(this)'.dta"
            lincom `expression',`options'
            display
        }
        * Run lincon for each dataset without options for the purpose of combining results with _mi_RUBIN
        qui lincom `expression'
        post `memhold' (1) (`t') (100) (r(estimate)) (r(se)) (1) (2)
        _restorecmd
    }
    postclose `memhold'
    qui use `result', clear
    qui gen str12 parm2=string(parm)
    qui drop parm
    rename parm2 parm
    order parm tt est se obs lb ub
    gen dumyid = 1
    qui save `results'
    erase `result'
end

program define __mydis, rclass
    args data i
    local short=abbrev("`data'`i'",12)
    return local this "`short'"
end

* These programs manipulate e(cmd) to avoid default behaviour of -lincom- after -logistic-
program define _clearcmd, eclass
    if "`e(cmd)'" == "logistic" {
        estimates local cmd = ""
        global S__flag 1
    }
    else {global S__flag 0}
end

program define _restorecmd, eclass
    if $S__flag == 1 {
        estimates local cmd = "logistic"
        macro drop S__flag
    }
end