/***********************************************************************************************
HISTORY
*! version 2.0.4 Ian White 1aug2023
	bug fix: MCSE for RMSE was (seriously) wrong
	released on SSC
version 2.0.3 Ian White 21jul2023
	moved the bsims=1 check before the big observation check (else useless)
	added hidden r(simsum_version)
* version 2.0.2 Ian White 22may2023
	better handling of bsims=1 case: abort unless new -force- is specified;
		MCSE correctly . for cover & power (but formulae otherwise unchanged)
version 2.0.1 Ian White 19jan2023
	gives warning, rather than failing, if estimates all equal, or SEs all zero
	improve reporting of estimate and SE checks: includes using method name, "estimate", "SE"
		instead of `beta`i'' which assumes original wide format
version 2.0 Ian White 13jan2023
	add null() option - only affects power
	tidy up and release on SSC as 2.0
version 0.19.1 Ian White 27sep2021
	remove unnecessary condition
version 0.19 Ian White 8jan2020
	new PMs: mean rmse ciwidth
	new parsing of disallowed PMs
	small changes to PM naming in output
	better error message if method values aren't valid
version 0.18.0 Ian White 6jan2020
	relprec MCSE formula corrected to agree with SJ article
		(denominator changed from n-2 to n-1)
	improve error message on specifying mse without true()
	allow graph(options) as well as graph
	not updating graph to v8 graphics as v7 graphics are quick
	run graph before checking extreme observations
version 0.17.3      2may2019
	Corrected error message with ref() and labelled methodvar
version 0.17.2      13may2016
    bug fix: if df() was missing, conf limits were missing and coverage was 1
        - corrected to give warning and give normal CIs for missing df
version 0.17.1      23nov2015	SENT TO SSC
    mse is output after relprec
    "statistic" -> "performance measure", "stat" -> "perfmeas"
version 0.17      20sep2013
    bug fix: format of b not used in output when data originally wide
    new transpose option for output: performance measures as columns, methods as rows
version 0.16.2      4apr2013
    mse uses bfmt not pctfmt
version 0.16.1      14nov2011
    bug fix: relprec works without true()
version 0.16        13sep2011
    bug fix: now works with method() and no true()
    corrected mcse for bias when true varies
version 0.15.1      13apr2011
    new mse option
version 0.14.1      24aug2010
    better error handling if called with no observations
version 0.14         7jun2010
    For SJ resubmission.
    byvar.ado included as subroutine
    changed to version 10
    dropped separate() option and restored sep() abbreviation
    incorporated listsepformat() into format()
    added abbreviate()
version 0.13.1 26may2010 - in wide format, unlabelled variables are listed by their name, not numbered; seprefix abbreviation lengthened to sepr() to avoid confusion with sep() - but what does the latter do?
version 0.13 8mar2010 - new listsep and listsepformat(bfmt pctfmt nfmt) options gives narrower & better formatted output; missing b <=> missing se; zero se changed to missing; better listing of problem observations
version 0.12 4mar2010 - much clearer error message if -byvar- not installed
version 0.11 23dec2009 - robust option; temporary byvar dropped from saving() file; keep before reshape means extra variables don't cause crash; ref(label) for long format; dflist() dropped; long() renamed methodvar(); only keeps variables that are needed (may leave problems if it crashes with clear option)
version 0.10 7sep2009 - df() updated to df(var) dflist(varlist) dfprefix(string) dfsuffix(string)
version 0.9 4sep2009 - renamed selist() as se()
version 0.8 12feb2009 - corrected error in resetting big values to missing; new nomemcheck option
version 0.7 8dec2009 - NEW SYNTAX simsum betalist, selist() [long(methodvar)]
version 0.6 13nov2008 - new options firstmethod() and refmethod()
version 0.5 13jun2008 - works without by(); renamed simsum (was simoutwide)
version 0.4  6jun2008 - modelse(mean) computes mean rather than RMSE; modelse_mcse corrected
version 0.3 18feb2008 - clearer listing of funny obs; df() option
version 0.2 19nov2007 - wide option, outsheet option, mcse option, if & in

NOTES ON MSE
    MSE  = mean of (beta-true)^2
         = bias^2 + k*empse^2 where k = (bsims-1)/bsims
    MCSE = SD of (beta-true)^2 / sqrt(bsims)
          (no robust option needed)
***********************************************************************************************/

prog def simsum, rclass
version 10

if _caller() >= 12 {
	local hidden hidden
}
return `hidden' local simsum_version "2.0.3"

syntax varlist [if] [in], ///
    [true(string) METHodvar(varname) id(varlist)                             /// main options
    se(varlist) SEPrefix(string) SESuffix(string)                            /// main options
    graph GRAPH2(string) noMEMcheck max(real 10) semax(real 100)             /// data checking options
    dropbig nolistbig listmiss                                               /// data checking options
    level(real $S_level) by(varlist) mcse robust                             /// calculation options
    MODELSEMethod(string) ref(string) null(real 0)                           /// calculation options
    df(string) DFPrefix(string) DFSuffix(string)                             /// degrees of freedom options
    bsims sesims bias mean empse relprec mse rmse                            /// performance measure options
    modelse ciwidth relerror cover power                                     /// performance measure options
	force																	 ///
    nolist listsep format(string) sepby(varlist) ABbreviate(passthru)        /// display options
    clear saving(string) gen(string) TRANSpose                               /// output data set options
    debug                                                                    /// undocumented options
    ]

// CHECK OPTIONS

if "`modelsemethod'"=="" local modelsemethod rmse
if "`modelsemethod'"!="rmse" & "`modelsemethod'"!="mean" {
    di as error "Syntax: modelsemethod(rmse|mean)"
    exit 498
}

if !mi("`graph2'") local graph graph

* SORT OUT BY
if "`by'"!="" {
    local byby by(`by')
    local byvar `by'
}
else {
    tempvar byvar
    gen `byvar'=0
}

* COUNT AND STORE BETA'S
local i 0
foreach beta of varlist `varlist' {
    local ++i
    local beta`i' `beta'
    local betalist `betalist' `beta'
}
local m `i'

* SORT OUT SE'S
if "`seprefix'"!="" | "`sesuffix'"!="" {
    if "`se'"!="" {
        di as error "Can't specify se() with seprefix() or sesuffix()"
        exit 498
    }
    forvalues i=1/`m' {
        local se`i' `seprefix'`beta`i''`sesuffix'
        confirm var `se`i''
        local selist `selist' `se`i''
    }
}
else if "`se'"!="" {
    local i 0
    foreach sevar of varlist `se' {
        local ++i
        local se`i' `sevar'
        local selist `selist' `se`i''
    }
    if `i'<`m' {
        di as error "Fewer variables in se(`se') than in `betalist'"
        exit 498
    }
    if `i'>`m' {
        di as error "More variables in se(`se') than in `betalist'"
        exit 498
    }
}
/*
else { // just working with beta's
    if "`sesims'`modelse'`relerror'`cover'`power'`ciwidth'"!="" {
        di as error "Can't compute `sesims' `modelse' `relerror' `cover' `power' `ciwidth' without standard errors"
        exit 498
    }
}
*/
* SORT OUT DF'S
if "`dfprefix'"!="" | "`dfsuffix'"!="" {
    if "`df'"!="" {
        di as error "Can't specify df() with dfprefix() or dfsuffix()"
        exit 498
    }
    forvalues i=1/`m' {
        local df`i' `dfprefix'`beta`i''`dfsuffix'
        confirm var `df`i''
        local dflist `dflist' `df`i''
    }
}
else if "`df'"!="" {
    cap confirm number `df'
    if !_rc local dftype number
    else {
        cap assert `df'==`df'
        if !_rc local dftype varname
        else {
            cap confirm var `df'
            if !_rc local dftype varlist
            else local dftype error
        }
    }
    if inlist("`dftype'","number","varname") {
        forvalues i=1/`m' {
            local df`i' `df'
        }
        if "`dftype'"=="varname" local dflist `df'
        cap assert !mi(`df')
        if _rc di as error "Warning: missing values in df()"
    }
    else if "`dftype'"=="varlist" {
        local i 0
        foreach dfvar of varlist `df' {
            local ++i
            local df`i' `dfvar'
            local dflist `dflist' `dfvar'
            cap assert !mi(`df`i'')
            if _rc di as error "Warning: missing values in df()"
        }
        if `i'!=`m' local dftype error
    }
    if "`dftype'"=="error" {
        di as error "df must contain number, string or varlist of same length as estimates"
        exit 498
    }

}

* PARSE PERFORMANCE MEASURES
* if no performance measures specified, use all available
* we'll strike out ineligible ones later
local allpms bsims sesims bias mean empse relprec mse rmse modelse ciwidth relerror cover power
foreach pm of local allpms { // find list of PMs specified
	if !mi("``pm''") local origoutput `origoutput' `pm'
}
if mi("`origoutput'") { // if nothing specified, specify all
	foreach pm of local allpms {
		local `pm' `pm'
	}
}
* strike out ineligible pms
    if "`se1'"=="" { // SE is not reported
        foreach perfmeas in sesims modelse ciwidth relerror cover power {
			if !mi("``perfmeas''") local droppm1 `droppm1' `perfmeas'
            local `perfmeas'
        }
		if !mi("`droppm1'") {
			if !mi("`origoutput'") di as error "" _c
			else di as text "" _c
			di "SE not reported, so ignoring performance measures: `droppm1'"
		}
    }
    if "`true'"=="" { // True parameter is not reported
        foreach perfmeas in bias mse rmse cover {
			if !mi("``perfmeas''") local droppm2 `droppm2' `perfmeas'
            local `perfmeas'
        }
		if !mi("`droppm2'") {
			if !mi("`origoutput'") di as error "" _c
			else di as text "" _c
			di "true() not specified, so ignoring performance measures: `droppm2'"
		}
    }
* find list of PMs specified
foreach pm of local allpms {
	if !mi("``pm''") local output `output' `pm'
}
if "`output'"=="" {
	di as error "No performance measures specified"
	exit 498
}
if inlist("`output'","bsims","sesims","bims sesims") & "`mcse'"=="mcse" {
    di as error "Only bsims and/or sesims specified - mcse ignored"
    local mcse
}
* END OF PARSING PERFORMANCE MEASURES

if "`methodvar'"!="" {
    if `m'>1 {
        di as error "Only one estimate variable allowed with long format"
        exit 498
    }
    if "`id'"=="" {
        di as error "id() is required with long format"
        exit 498
    }
}

if "`list'"=="nolist" & "`clear'"=="" & "`saving'"=="" {
    di as error "No output requested! Please specify clear or saving(), or don't specify nolist"
    exit 498
}

if "`gen'"=="" local gen perfmeas
cap confirm new variable `gen'num
local rc1=_rc
cap confirm new variable `gen'code
if _rc | `rc1' {
    di as error as smcl "{p}Variable `gen'num and/or `gen'code already exists. This is probably because the current data are -simsum- output. If this is what you want, use the gen() option.{p_end}"
    exit 498
}

if "`memcheck'"!="nomemcheck" {
    qui desc, short
    if r(width)/r(widthmax)>0.45 {
        di as error "simsum is memory-hungry and can fail slowly if memory is more than 50% occupied."
        di as error as smcl "Please increase the memory using {help memory:set memory}, or use the nomemcheck option."
        exit 498
    }
}

// SET UP
marksample touse, novarlist
qui count if `touse'
if r(N)==0 {
    di in red "no observations"
    exit 2000
}

* check true is specified if bias or cover chosen
if "`bias'"=="bias" | "`mse'"=="mse" | "`rmse'"=="rmse" | "`cover'"=="cover" {
    tempvar truevar
    qui gen `truevar' = `true'
    qui count if missing(`truevar') & `touse'
    if r(N)>0 {
        di as error "Missing values found for true value `true'"
        exit 498
    }
}

// START CALCULATION
preserve
qui keep if `touse'

// CONVERT FROM LONG FORMAT IF NECESSARY, EXTRACT METHOD LABELS AND FIND REFERENCE METHOD
if "`methodvar'"!="" {
    * DATA ARE LONG, CONVERTING TO WIDE
    local origformat long
    local betastub `betalist'
    qui levelsof `methodvar', local(methods)
    local label : val label `methodvar'
    local i 0
    foreach method in `methods' {
        if strpos("`method'"," ") {
			di as error `"Sorry, I can't handle method = "`method'". Please recode it without spaces."'
			exit 498
		}
        if strtoname("b`method'")!="b`method'" {
			di as error `"Sorry, I can't handle method = "`method'". Please recode it using standard characters."'
			exit 498
		}
		local ++i
        local beta`i' `betalist'`method'
        local newbetalist `newbetalist' `betalist'`method'
        if "`selist'"!="" local se`i' `selist'`method'
        if "`selist'"!="" local newselist `newselist' `selist'`method'
        if "`dftype'"=="number" local df`i' `df'
        if "`dftype'"=="varname" local df`i' `dflist'`method'
        if "`label'"!="" local label`i' : label `label' `method'
        else local label`i' "`method'"
        if "`label`i''"=="`ref'" local refmethod `i'
    }
    local m `i'
    if "`refmethod'"=="" {
        if "`ref'"!="" {
            if "`label'"=="" local offenders values
			else local offenders value labels
			di as error "Error in ref(): `ref' is not one of the `offenders' of `methodvar'"
            exit 498
        }
        else local refmethod 1
    }
    di as text "Reshaping data to wide format ..."
    keep `betalist' `selist' `dflist' `by' `byvar' `id' `methodvar' `touse' `truevar'
    cap confirm string var `methodvar'
    if _rc==0 local string string
    local bfmt0: format `betalist' // for later use

    qui reshape wide `betalist' `selist' `dflist', i(`by' `id') j(`methodvar') `string'

    local betalist `newbetalist'
    local selist `newselist'
}
else { // DATA ARE ALREADY WIDE
    local origformat wide
    forvalues i=1/`m' {
        local label`i' : var label `beta`i''
        if "`label`i''"=="" local label`i' "`beta`i''" /* corrected, v0.13.1 */
        if "`beta`i''"=="`ref'" local refmethod `i'
    }
    if "`refmethod'"=="" {
        if "`ref'"!="" {
            di as error "ref(`ref') is not one of the listed point estimates"
            exit 498
        }
        else local refmethod 1
    }
    keep `betalist' `selist' `dflist' `by' `byvar' `id' `touse' `truevar'
}

// CHECK FOR NON-REPLICATED BETA'S
if mi("`force'") {
	forvalues i=1/`m' {
		tempvar betafreq`i'
		egen `betafreq`i'' = count(`beta`i''), `byby'
		cap assert `betafreq`i''!=1
		if _rc {
			di as error "Only one observation for `beta`i''. If this is what you intended, use the force option."
			exit 498
		}
	}
}

// LIST MISSING/PROBLEM OBS
tempvar missing
gen `missing' = 0
forvalues i=1/`m' {
    qui replace `missing' = missing(`beta`i'') & `touse'
    if "`se`i''"!="" qui replace `missing' = 1 if missing(`se`i'') & `touse'
    if "`se`i''"!="" qui replace `missing' = 1 if `se`i''==0 & `touse'
    qui count if `missing'
    if r(N)>0 {
        if "`se`i''"!="" {
            qui replace `missing' = missing(`beta`i'') & missing(`se`i'') & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning for method `label`i'': " as result r(N) as text " observation(s) have both estimate and SE missing"
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                di as text "--> no action taken"
            }

            qui replace `missing' = !missing(`se`i'') & missing(`beta`i'') & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning for method `label`i'': " as result r(N) as text " observation(s) have estimate missing and SE observed"
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                qui replace `se`i'' = . if `missing'
                di as text "--> SE changed to missing"
            }

            qui replace `missing' = missing(`se`i'') & !missing(`beta`i'') & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning for method `label`i'': " as result r(N) as text " observation(s) have estimate observed and SE missing"
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                qui replace `beta`i'' = . if `missing'
                di as text "--> estimate changed to missing"
            }

            qui replace `missing' = (`se`i''==0) & `touse'
            qui count if `missing'
            if r(N)>0 {
                di as text _new "Warning for method `label`i'': " as result r(N) as text " observation(s) have zero values of SE"
                if "`listmiss'"=="listmiss" list `by' `id' `beta`i'' `se`i'' if `missing', sepby(`sepby')
                qui replace `beta`i'' = . if `missing'
                qui replace `se`i'' = . if `missing'
                di as text "--> estimate and SE changed to missing"
            }
        }
    }
}
drop `missing'

// OPTIONAL DESCRIPTIVE GRAPH
if "`graph'"=="graph" {
    tempfile graph
    set graphics off
    forvalues i=1/`m' {
        cap gr7 `se`i'' `beta`i'', xla yla b2title("`beta`i''") l1title("`se`i''") t1title("`label`i''") saving(`graph'`i', replace) `graph2'
        if !_rc local gphlist `gphlist' `graph'`i'
    }
    if "`selist'"=="" local title Point estimates by method
    else local title Std error vs. point estimate by method
    set graphics on
    gr7 using `gphlist', title(`title')
}

// CHECK FOR TOO-BIG OBS & OPTIONALLY LIST / DROP THEM
tempvar infb infse
gen `infb' = 0
gen `infse' = 0
forvalues i=1/`m' {
    qui summ `beta`i''
	if r(sd)<=0 {
		di as text "Warning for method `label`i'': estimate doesn't vary"
		continue
	}
    qui replace `infb' = (abs(`beta`i''-r(mean))/r(sd) > `max') & !missing(`beta`i'')
    if "`se`i''"!="" {
        qui summ `se`i''
		if r(mean)<=0 {
			di as text "Warning for method `label`i'': `se`i'' is always 0"
		}
        else qui replace `infse' = (`se`i''/r(mean) > `semax') & !missing(`se`i'')
    }
    qui count if `infb'
    local ninfb = r(N)
    qui count if `infse'
    local ninfse = r(N)
    if `ninfb'+`ninfse' > 0 {
        local text = cond("`dropbig'"=="dropbig","text","error")
        local warning = cond("`dropbig'"=="dropbig","Warning","Error")
		di as `text' `"`warning' for method `label`i'': "' as result `ninfb' as `text' `" observation(s) have standardised estimate > `max'"' _c
        if "`se`i''"!="" di as `text' `" and "' as result `ninfse' as `text' `" observation(s) have scaled SE > `semax'"'
		else di
        if "`listbig'"!="nolistbig" l `by' `id' `beta`i'' `se`i'' if `infb'|`infse', sepby(`sepby')
		else di as `text' "--> remove nolistbig option to list these observation(s)"
        if "`dropbig'"=="dropbig" {
            qui replace `beta`i'' = . if `infb'|`infse'
            if "`se`i''"!="" qui replace `se`i'' = . if `infb'|`infse'
            di as text `"--> estimate "' _c
            if "`se`i''"!="" di as text `"and SE "' _c
            di as text `"have been changed to missing values for these observation(s)"'
        }
		else {
			di as error "--> use dropbig option to drop these observation(s)"
			di as error "--> use max() option to change acceptable limit of point estimates"
			if "`se'"!="" di as error "--> use semax() option to change acceptable limit of standard errors"
			exit 498
		}
    }
}


// PROCESS RESULTS PART 1: PREPARE FOR -COLLAPSE-
di as text _newline "Starting to process results ..."
if `level'<1 local level=`level'*100
if "`robust'"=="robust" & ("`relprec'"=="relprec" | "`relerror'"=="relerror") {
    forvalues i=1/`m' {
        tempvar betamean`i'
        egen `betamean`i'' = mean(`beta`i''), `byby'
    }
}
forvalues i=1/`m' {
    tempvar crit`i'
    gen `crit`i'' = -invnorm((1-`level'/100)/2)
    if "`df`i''"!="" qui replace `crit`i'' = invttail(`df`i'',(1-`level'/100)/2) if !mi(`df`i'')
        // missing values in df`i' yield normal intervals
    assert !mi(`crit`i'')
    local collcount `collcount' bsims_`i'=`beta`i''
    if "`bias'"=="bias" {
        qui gen bias_`i' = `beta`i'' - `truevar'
        local collmean `collmean' bias_`i'
        local collsd `collsd' biassd_`i' = bias_`i'
    }
    if "`mean'"=="mean" {
        local collmean `collmean' mean_`i' = `beta`i''
        local collsd `collsd' meansd_`i' = `beta`i''
    }
    if "`relerror'"=="relerror" | "`modelse'"=="modelse" {
        qui gen var_`i'=`se`i''^2
    }
    if "`empse'"=="empse" | "`relerror'"=="relerror" | "`relprec'"=="relprec" | "`bias'"=="bias" {
        local collsd `collsd' empse_`i'=`beta`i''
    }
    if "`mse'"=="mse" | "`rmse'"=="rmse" {
        qui gen mse_`i' = (`beta`i'' - `truevar')^2
        local collmean `collmean' mse_`i'
        local collsd `collsd' msesd_`i'=mse_`i'
    }
    if "`relprec'"=="relprec" & `i'!=`refmethod' {
        qui byvar `byvar', r(rho N) gen unique: corr `beta`refmethod'' `beta`i''
        rename Rrho_ corr_`i'
        rename RN_ ncorr_`i'
        local collsum `collsum' corr_`i' ncorr_`i'
    }
    if "`modelse'"=="modelse" | "`relerror'"=="relerror" | "`sesims'"=="sesims" {
        local collcount `collcount' sesims_`i'=`se`i''
    }
    if "`modelse'"=="modelse" | "`relerror'"=="relerror" {
        local collmean `collmean' modelse_`i'=`se`i''
        local collmean `collmean' varmean_`i'=var_`i'
        local collsd `collsd' varsd_`i'=var_`i'
        local collsd `collsd' modelsesd_`i'=`se`i''
    }
	if "`ciwidth'"=="ciwidth" {
		qui gen ciwidth_`i'  = 2*(`crit`i'')*`se`i''
		local collmean `collmean' ciwidth_`i'
		local collsd `collsd' ciwidthsd_`i' = ciwidth_`i'
	}
    if "`cover'"=="cover" | "`power'"=="power" {
        if "`cover'"=="cover" local collcount `collcount' bothsims_`i'=cover_`i'
        else local collcount `collcount' bothsims_`i'=power_`i'
    }
    if "`cover'"=="cover" {
        qui gen cover_`i' = 100*(abs(`beta`i''-`truevar')<(`crit`i'')*`se`i'') if !missing(`beta`i'') & !missing(`se`i'')
        local collmean `collmean' cover_`i'
    }
    if "`power'"=="power" {
        qui gen power_`i' = 100*(abs(`beta`i''-`null')>=(`crit`i'')*`se`i'') if !missing(`beta`i'') & !missing(`se`i'')
        local collmean `collmean' power_`i'
    }
    if "`robust'"=="robust" {
        if "`empse'"=="empse" {
            tempvar empseT`i' empseB`i' empseTT`i' empseBB`i' empseTB`i'
            qui gen `empseT`i'' = (`beta`i''-`betamean`i'')^2
            qui gen `empseB`i'' = 1
            qui gen `empseTT`i'' = `empseT`i''^2
            qui gen `empseBB`i'' = `empseB`i''^2
            qui gen `empseTB`i'' = `empseT`i''*`empseB`i''
            local collsum `collsum' `empseT`i'' `empseB`i'' `empseTT`i'' `empseTB`i'' `empseBB`i''
        }
        if "`relprec'"=="relprec" {
            tempvar relprecT`i' relprecB`i' relprecTT`i' relprecBB`i' relprecTB`i'
            qui gen `relprecT`i'' = (`beta`refmethod''-`betamean`refmethod'')^2
            qui gen `relprecB`i'' = (`beta`i''-`betamean`i'')^2
            qui gen `relprecTT`i'' = `relprecT`i''^2
            qui gen `relprecBB`i'' = `relprecB`i''^2
            qui gen `relprecTB`i'' = `relprecT`i''*`relprecB`i''
            local collsum `collsum' `relprecT`i'' `relprecB`i'' `relprecTT`i'' `relprecTB`i'' `relprecBB`i''
        }
        if "`relerror'"=="relerror" {
            tempvar relerrorT`i' relerrorB`i' relerrorTT`i' relerrorBB`i' relerrorTB`i'
            qui gen `relerrorT`i'' = `se`i''^2
            qui gen `relerrorB`i'' = (`beta`i''-`betamean`i'')^2
            qui gen `relerrorTT`i'' = `relerrorT`i''^2
            qui gen `relerrorBB`i'' = `relerrorB`i''^2
            qui gen `relerrorTB`i'' = `relerrorT`i''*`relerrorB`i''
            local collsum `collsum' `relerrorT`i'' `relerrorB`i'' `relerrorTT`i'' `relerrorTB`i'' `relerrorBB`i''
        }
    }
}
if "`collmean'"!="" local collmean (mean) `collmean'
if "`collsd'"!="" local collsd (sd) `collsd'
if "`collcount'"!="" local collcount (count) `collcount'
if "`collsum'"!="" local collsum (sum) `collsum'

// PROCESS RESULTS PART 2: -COLLAPSE-
collapse `collmean' `collsd' `collcount' `collsum', by(`byvar')
if !mi("`debug'") {
	di as input "Data after collapse:"
	l
}

// PROCESS RESULTS PART 3: AFTER -COLLAPSE-
forvalues i=1/`m' {
    qui gen k_`i' = bsims_`i'/(bsims_`i'-1)
    if "`bias'"=="bias" {
        qui gen bias_mcse_`i' = biassd_`i' / sqrt(bsims_`i')
    }
    if "`mean'"=="mean" {
        qui gen mean_mcse_`i' = meansd_`i' / sqrt(bsims_`i')
    }
    if ("`empse'"=="empse"  | "`relerror'"=="relerror") & "`robust'"=="" {
        qui gen empse_mcse_`i' = empse_`i'/sqrt(2*(bsims_`i'-1))
    }
    else if ("`empse'"=="empse") & "`robust'"=="robust" {
        qui replace `empseTT`i''=`empseTT`i''*(k_`i'^2)
        qui replace `empseTB`i''=`empseTB`i''*k_`i'
        qui replace `empseT`i'' =`empseT`i'' *k_`i'
        qui gen empse_mcse_`i' = sqrt(k_`i') * sqrt(`empseTT`i'' -2*(`empseT`i''/`empseB`i'')*`empseTB`i'' +(`empseT`i''/`empseB`i'')^2*`empseBB`i'') / `empseB`i''
        qui replace empse_mcse_`i' = empse_mcse_`i' / (2*empse_`i')
    }
    if "`relprec'"=="relprec" {
        if `i'!=`refmethod' {
            qui gen relprec_`i' = 100 * ((empse_`refmethod'/empse_`i')^2-1)
            if "`robust'"=="" {
                qui gen relprec_mcse_`i' = 200 * (empse_`refmethod'/empse_`i')^2 * sqrt((1-(corr_`i')^2)/(ncorr_`i'-1))
            }
            else {
                qui gen relprec_mcse_`i' = 100 * sqrt(`relprecTT`i'' -2*(`relprecT`i''/`relprecB`i'')*`relprecTB`i'' +(`relprecT`i''/`relprecB`i'')^2*`relprecBB`i'') / `relprecB`i''
            }
        }
        else {
            qui gen relprec_`i' = .
            qui gen relprec_mcse_`i' = .
        }
    }
    if "`mse'"=="mse" {
        qui gen mse_mcse_`i' = msesd_`i' / sqrt(bsims_`i')
    }
    if "`rmse'"=="rmse" {
        qui gen rmse_`i' = sqrt(mse_`i')
        qui gen rmse_mcse_`i' = msesd_`i' / (2 * sqrt(bsims_`i') * rmse_`i')
    }
    if "`modelse'"=="modelse" | "`relerror'"=="relerror" {
        if "`modelsemethod'"=="rmse" {
            qui replace modelse_`i' = sqrt(varmean_`i')
            qui gen modelse_mcse_`i' = varsd_`i' / sqrt(4 * sesims_`i' * varmean_`i')
        }
        else if "`modelsemethod'"=="mean" {
            qui gen modelse_mcse_`i' = modelsesd_`i' / sqrt(sesims_`i')
        }
    }
	if "`ciwidth'"=="ciwidth" {
		qui gen ciwidth_mcse_`i' = ciwidthsd_`i' / sqrt(bsims_`i')
	}
    if "`relerror'"=="relerror" {
        qui gen relerror_`i' = 100*(modelse_`i'/empse_`i'-1)
        if "`robust'"=="" qui gen relerror_mcse_`i' = 100*(modelse_`i'/empse_`i') * sqrt((modelse_mcse_`i'/modelse_`i')^2 + (empse_mcse_`i'/empse_`i')^2 )
        else {
            qui gen relerror_mcse_`i' = sqrt(`relerrorTT`i'' -2*(`relerrorT`i''/`relerrorB`i'')*`relerrorTB`i'' +(`relerrorT`i''/`relerrorB`i'')^2*`relerrorBB`i'') / `relerrorB`i''
            qui replace relerror_mcse_`i' = relerror_mcse_`i' * 100 / (2*(1+relerror_`i'/100))
        }
    }
    if "`cover'"=="cover" {
        qui gen cover_mcse_`i' = sqrt(cover_`i'*(100-cover_`i')/bothsims_`i') if bothsims_`i'>1
    }
    if "`power'"=="power" {
        qui gen power_mcse_`i' = sqrt(power_`i'*(100-power_`i')/bothsims_`i') if bothsims_`i'>1
    }
    cap drop varmean_`i'
    cap drop varsd_`i'
}
if !mi("`debug'") {
	di as input "Data after post-processing:"
	l
}

// PREPARE FOR OUTPUT
local alpha=100-`level'
local bsimsname Non-missing point estimates
local sesimsname Non-missing standard errors
local biasname Bias in point estimate
local meanname Mean of point estimate
local empsename Empirical standard error
local relprecname % gain in precision relative to method `label`refmethod''
local msename Mean squared error
local rmsename Root mean squared error
if "`modelsemethod'" =="mean" local modelsename Mean model-based standard error `sebeta'
if "`modelsemethod'" =="rmse" local modelsename RMS model-based standard error `sebeta'
local ciwidthname Mean conf. interval width
local relerrorname Relative % error in standard error
local covername % coverage of nominal `level'% conf. interval
local powername % power of `alpha'% level test

local keeplist `byvar'
foreach name in `output' {
    local domcse = "`mcse'"=="mcse" & "`name'"!="bsims" & "`name'"!="sesims"
    forvalues i=1/`m' {
        rename `name'_`i' method`i'`name'
        local keeplist `keeplist' method`i'`name'
        if `domcse' {
            rename `name'_mcse_`i' method`i'`name'_mcse
            local keeplist `keeplist' method`i'`name'_mcse
        }
    }
}
forvalues i=1/`m' {
    local methodlist `methodlist' method`i'
}
keep `keeplist'
qui reshape long `methodlist', i(`byvar') j(`gen'code) string
forvalues i=1/`m' {
    char method`i'[varname] "`label`i''"
    label var method`i' "`label`i''"
}
local i 0
qui gen mcse = .
qui gen `gen'num = .
foreach perfmeas in bsims sesims bias mean empse relprec mse rmse modelse ciwidth relerror cover power {
    local ++i
    qui replace mcse=0 if `gen'code=="`perfmeas'"
    qui replace mcse=1 if `gen'code=="`perfmeas'_mcse"
    qui replace `gen'code="`perfmeas'" if `gen'code=="`perfmeas'_mcse"
    qui replace `gen'num = `i' if `gen'code=="`perfmeas'" | `gen'code=="`perfmeas'_mcse"
    * label performance measures
    if "`perfmeas'"=="bsims" local label "Non-missing point estimates"
    if "`perfmeas'"=="sesims" local label "Non-missing standard errors"
    if "`perfmeas'"=="bias" local label "Bias in point estimate"
    if "`perfmeas'"=="mean" local label "Mean of point estimate"
    if "`perfmeas'"=="empse" local label "Empirical standard error"
    if "`perfmeas'"=="relprec" local label "% precision gain relative to method `label`refmethod''"
    if "`perfmeas'"=="mse" local label "Mean squared error"
    if "`perfmeas'"=="rmse" local label "Root mean squared error"
    if "`perfmeas'"=="modelse" {
        if "`modelsemethod'" =="mean" local label "Mean model-based standard error"
        if "`modelsemethod'" =="rmse" local label "RMS model-based standard error"
    }
	if "`perfmeas'"=="ciwidth" local label "Mean conf. interval width"
    if "`perfmeas'"=="relerror" local label "Relative % error in standard error"
    if "`perfmeas'"=="cover" local label "% coverage of nominal `level'% conf. interval"
    if "`perfmeas'"=="power" local label "% power of `alpha'% level test"
    label def `gen'num  `i' "`label'", add
    label val `gen'num `gen'num
}

assert !mi(mcse)
foreach var in `methodlist' {
    rename `var' `var'_
    local methodlist2 `methodlist2' `var'_
}
qui reshape wide `methodlist2', i(`byvar' `gen'num) j(mcse)
local ids `gen'num `by'
local betas
forvalues i=1/`m' {
    rename method`i'_0 `beta`i''
    label var `beta`i'' "`label`i''"
    char `beta`i''[varname] "`label`i''"
    local betasnomcse `betasnomcse' `beta`i''
    local betas `betas' `beta`i''
    if "`mcse'"=="mcse" {
        rename method`i'_1 `beta`i''_mcse
        local betas `betas' `beta`i''_mcse
        label var `beta`i''_mcse "`label`i'' (MCse)"
        char `beta`i''_mcse[varname] "(MCse)"
    }
}
char `gen'num[varname] "Performance measure"
label var `gen'num "Performance measure"
label var `gen'code "Performance measure"
order `ids' `betas'
sort `gen'num `by'

// OUTPUT
tokenize `format'
local bfmt "`1'"
local pctfmt "`2'"
local nfmt "`3'"
if "`bfmt'"=="" {
    if mi("`methodvar'") local bfmt: format `beta1'
    else local bfmt `bfmt0'
}
if "`pctfmt'"=="" | "`pctfmt'"=="=" | "`pctfmt'"=="." local pctfmt `bfmt'
if "`nfmt'"=="" local nfmt %7.0f

if mi("`transpose'") {
    if "`list'"!="nolist" {
        if "`listsep'"=="" {
            qui format `betas' `bfmt'
            list `ids' `betas', noo subvarname sepby(`gen'num `sepby') `abbreviate'
        }
        else {
            foreach perfmeas in `output' {
                di as text _new "``perfmeas'name'"
                local thisbetas = cond(inlist("`perfmeas'","bsims","sesims"), "betasnomcse", "betas")
                if inlist("`perfmeas'","bsims","sesims") local format `nfmt'
                else if inlist("`perfmeas'","bias","empse","modelse","mse") local format `bfmt'
                else local format `pctfmt'
                qui format `betas' `format'
                list `by' ``thisbetas'' if `gen'code=="`perfmeas'", noo subvarname sepby(`gen'num `sepby') `abbreviate'
            }
        }
    }
    * format for output data set
    qui format `betas' `bfmt'
    char `gen'num[varname]
}
else {
    di "Transposing results ..."
    drop `gen'num
    if "`origformat'"=="long" {
    }
    else if "`origformat'"=="wide" {
        foreach var of varlist `betas' {
            rename `var' b_`var'
        }
        local betastub b_
    }
    else exit 499
    qui reshape long `betastub', i(`gen'code `by') j(method) string
    qui reshape wide `betastub', i(`by' method) j(`gen'code) string
    if "`mcse'"=="mcse" {
        gen type = cond(substr(method,length(method)-4,5)=="_mcse","mcse","est")
        qui replace method = substr(method,1,length(method)-5) if type=="mcse"
        local type type
        local sep2 method
    }
    sort `by' method `type'
    foreach varname of varlist `betastub'* {
    	local varname2 = substr("`varname'", 1+length("`betastub'"), .)
    	rename `varname' `varname2'
        label var `varname2'
    }
    cap format `bias' `empse' `mse' `modelse' `bfmt'
    cap format `relprec' `relerror' `cover' `power' `pctfmt'
    cap format `bsims' `sesims' `sesims' `nfmt'
    if "`list'"!="nolist" {
        l `by' method `type' `bsims' `sesims' `bias' `empse' `relprec' `mse' `modelse' `relerror' `cover' `power', sepby(`by' `sep2') noo
    }
}

// FINISH OFF
if "`saving'"!="" {
    if "`by'"=="" drop `byvar'
    save `saving'
}
if "`clear'"=="clear" {
    restore, not
    di as text "Results are now in memory."
}
end

********************** BYVAR (PATRICK ROYSTON) ********************************

* version 2.0.3 PR 03apr2007.
program define byvar, rclass sortpreserve
version 8

tokenize `"`0'"', parse(":")
if "`2'"!=":" {
    di as err "syntax error - maybe missing colon, or unmatched quotes somewhere"
    exit 198
}
local 0 `1'
mac shift 2 // skip the colon
local command `*'

syntax varlist [if] [in], [ E(str) R(str) B(str) SE(str) GRoup(str) GEnerate ///
 Tabulate REturn Missing Pause noLabel Unique SEParator(string) ]

if "`group'"!="" confirm new var `group'

if "`unique'"!="" & "`generate'"=="" {
    di as err "unique requires generate"
    exit 198
}

local bylist `varlist'

local s1 `e'
local L1 E
local s2 `r'
local L2 R
local s3 `b'
local L3 B
local s4 `se'
local L4 S
local tostore=`"`s1'`s2'`s3'`s4'"'!=""
if !`tostore' & "`generate'`return'`tabulate'"!="" {
    di as err "nothing to generate, return or tabulate"
    exit 198
}
if `tostore' & "`generate'`return'`tabulate'"=="" {
    di as txt "(tabulate assumed)"
    local tabulat tabulate
}

local tab="`tabulate'"!="" & `tostore'

* Extract multiple commands
local ncmd 0
if "`separator'"!="" local sep=substr("`separator'",1,1)
else local sep @
tokenize `"`command'"', parse("`sep'")
while `"`1'"'!="" {
    if `"`1'"'!="@" {
        local ++ncmd
        local command`ncmd' `1'
    }
    mac shift
}
forvalues i=1/`ncmd' {
    tokenize `"`command`i''"', parse(",")
    local cmd1 `1'
    mac shift
    local cmd2`i' `*'
    local pif=index(`"`cmd1'"', " if ")
    if `pif'>0 {
        local cmd11`i'=substr(`"`cmd1'"',1,`pif')
        local cmd12`i'=substr(`"`cmd1'"',`pif'+4,.)
        local ifalso`i' " & "
    }
    else local cmd11`i' `cmd1'
}

/* Number and count the groups  */
quietly {
/*
    Default is to exclude missing groups in bylist from analysis.
    Their corresponding group codes are set to missing and sorted to
    end of data.
*/
    tempvar grp first
    marksample touse, strok
    if "`missing'"=="" {
        markout `touse' `bylist', strok
        replace `touse'=. if `touse'==0
    }
    sort `touse' `bylist'
    by `touse' `bylist': gen byte `first'=1 if _n==1 & `touse'==1
    gen int `grp'=sum(`first') if `touse'==1
    drop `touse'
    sum `grp'
    local GRP=r(max)
    if `GRP'==0 noisily error 2000

    local itemlen 14
/*
    Extract group-defining values of bylist variables
    and store in macros
*/
    local nby : word count `bylist'
    if `tab' {
        noi di
        local dashes
    }
    tempvar index
    gen long `index'=.
    forvalues i=1/`GRP' {
        replace `index'=sum(_n*(`grp'==`i' & `first'==1))
        local j=`index'[_N]
        forvalues k=1/`nby' {
            local byvar`k' : word `k' of `bylist'
            local vallab: value label `byvar`k''
            local byval=`byvar`k''[`j']
            if "`vallab'"!="" & "`label'"!="nolabel" {
                local by`i'`k': label `vallab' `byval'
            }
            else local by`i'`k' `byval'
            if `i'==1 & `tab' {
                local dashes "`dashes'---------"
                noi di as txt %-9s "`byvar`k''" _c
            }
        }
    }
    drop `index'
}
if `tab' {
    di as txt " |" _c
    local dashes "`dashes'-+"
}
/*
    Parse and record items for storage
*/
forvalues j=1/4 {
    local i 1
    if `"`s`j''"'!="" {
        tokenize `"`s`j''"'
        * take care of embedded quotes
        local k 1
        while `"`1'"'!="" {
            local sk`k' `sk`k'' `1'
            * Count number of quotes in `1' - should be 2. Do not actually change `1' (discard `z').
            local z: subinstr local 1 `"""' "Q", all count(local nquote)    // "
            if `nquote'>0 & `nquote'!=2 {
                di as err "unmatched quotes in " `"`s`j''"'
                exit 198
            }
            local ++k
            mac shift
        }
        local l 1
        while `l'<`k' {
            * 1=item, [ 2="=", 3=description of item ], 4=null
            tokenize `"`sk`l''"', parse("=")
            local sk`l'
            if `"`4'"'!="" {
                di as err "invalid " `"`sk`l''"'
                exit 198
            }
            local st`j'`i' `1'
            if `"`3'"'=="" {
                if      `j'==1 local lab e(`1')
                else if `j'==2 local lab r(`1')
                else if `j'==3 local lab _b[`1']
                else if `j'==4 local lab _se[`1']
            }
            else {
                local lab=substr(`"`3'"',1,`itemlen'-1)
            }
            if "`generate'"!="" {
                mk_name `L`j'' `1' 6
                local `L`j''_`i' `s(name)'
                qui gen double ``L`j''_`i''=.
                lab var ``L`j''_`i'' `"`lab' by `bylist'"'
            }
            if `tab' {
                local dashes "`dashes'--------------"
                local skip=`itemlen'-length(`"`lab'"')
                di as txt _skip(`skip') `"`lab'"' _c
            }
            local ++i
            local ++l
        }
    }
    local n`j'=`i'-1
}
if `tab' di _n as txt "`dashes'"

/* Perform calcs        */
if `tab' local show quietly
else local show noisily
tempname thing
forvalues i=1/`GRP' { // i indexes members of groups implied by bylist
    if !`tab' {
        di as txt _n "-> " _c
        forvalues k=1/`nby' {
            di as txt "`byvar`k''==`by`i'`k'' " _c
        }
        di
    }
    forvalues j=1/`ncmd' {
*di `"capture `show' `cmd11`j'' if `grp'==`i' `ifalso`j'' `cmd12`j'' `cmd2`j''"'
        capture `show' `cmd11`j'' if `grp'==`i' `ifalso`j'' `cmd12`j'' `cmd2`j''
        local rc=_rc
    }

    if "`pause'"!="" more

    if `tab' {
        forvalues k=1/`nby' {
            di as res %-9s substr("`by`i'`k''",1,8) _c
        }
        di as txt " |" _c
    }
    forvalues k=1/4 {   // k indexes the 4 types of thing to be stored
        if `n`k''>0 {
            forvalues l=1/`n`k'' { /* l indexes # of thing */

                if `rc'==0 {
                    if `k'==1   scalar `thing'=e(`st`k'`l'')
                    else if `k'==2  scalar `thing'=r(`st`k'`l'')
                    else if `k'==3  scalar `thing'=_b[`st`k'`l'']
                    else if `k'==4  scalar `thing'=_se[`st`k'`l'']
                }
                else scalar `thing'=.
                if "`generate'"!="" {
                    if "`unique'"=="" qui replace ``L`k''_`l''=`thing' if `grp'==`i'
                    else qui replace ``L`k''_`l''=`thing' if `grp'==`i' & `first'==1
                }
                if "`return'"!="" {
                    * gp refers to subgroup (level) of the byvar(s)
                    local r `L`k''`l'gp`i'
                    return scalar `r'=`thing'
                }
                if `tab' di _skip(4) as res %10.0g `thing' _c
            }
        }
    }
    if `tab' di
}
quietly if "`generate'"!="" {
    forvalues k=1/4 {           /* k indexes type of thing stored */
        if `n`k''>0 {
            forvalues i=1/`n`k'' { /* i indexes item in list of things */
                compress ``L`k''_`i''
                // if "`firstonly'"!="" bysort `grp': replace ``L`k''_`i''=. if _n>1
                return local `L`k''_`i' ``L`k''_`i''
            }
        }
    }
}
if "`group'"!="" {
    cap drop `group'
    rename `grp' `group'
    lab var `group' "group by `bylist'"
}
return scalar byvar_g=`GRP'
end

program define mk_name, sclass
/* meaning make_unique_name <letter> <suggested_name> <#_chars> */
    version 8
    args letter base numchar
    sret clear
    local name = substr("`letter'`base'",1,`numchar'+1)
    xi_mkun2 `name'_
end

program define xi_mkun2, sclass
version 8
/* meaning make_unique_name <suggested_name> */
    args name

    local totry "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    local lentot=length("`totry'")
    local l 0
    local len = length("`name'")
    capture list `name'* in 1       /* try name out */
    while _rc==0 {
        if `l'==`lentot' {
            di as err "too many terms---limit is " `lentot'+1
            exit 499
        }
        local l=`l'+1
        local name = substr("`name'",1,`len'-1)+substr("`totry'",`l',1)
        capture list `name'* in 1
    }
    sret local name "`name'"
end

********************************************************************************************************