Add *undocumented) display options

Add *undocumented) display options

You can hide the header, the table, or the footnote results with the noHEader, noTABle, and noFOOTnote options. EG:

```
sysuse auto
reghdfe price weight, a(turn) nohead notable nofoot
```

*! version 5.7.1 20mar2019

program reghdfe, eclass
	* Intercept old+version
	cap syntax, version old
	if !c(rc) {
		reghdfe_old, version
		exit
	}

	* Intercept old
	cap syntax anything(everything) [fw aw pw/], [*] old
	if !c(rc) {
		di as error "(running historical version of reghdfe)"
		if ("`weight'"!="") local weightexp [`weight'=`exp']
		reghdfe_old `anything' `weightexp', `options'
		exit
	}

	* Aux. subcommands
	cap syntax, [*]
	if inlist("`options'", "check", "compile", "reload", "update", "version", "requirements", "store_alphas") {
		if ("`options'"=="compile") loc args force
		if ("`options'"=="check") loc options compile
		if ("`options'"=="update") {
			loc args 1
			loc options reload
		}
		loc subcmd = proper("`options'")
		`subcmd' `args'
	}
	else if replay() {
		Replay `0'
	}
	else {
		Cleanup 0
		ms_get_version ftools, min_version("2.36.1") // Compile // takes 0.01s to run this useful check (ensures .mlib exists)
		cap noi Estimate `0'
		Cleanup `c(rc)'
	}
end

program Compile
	args force

	* Check dependencies
	ftools, check // in case lftools.mlib does not exist or is outdated
	ms_get_version ftools, min_version("2.34.0")
	ms_get_version reghdfe // save local package_version
	loc list_objects "FixedEffects() fixed_effects() BipartiteGraph()"
	loc list_functions "reghdfe_*() transform_*() accelerate_*() panelmean() panelsolve_*() lsmr()"
	loc list_misc "weighted_quadcolsum() safe_divide() check_convergence() precompute_inv_xx() _st_data_wrapper()"
	// TODO: prefix everything with reghdfe_*

	ms_compile_mata, ///
		package(reghdfe) ///
		version(`package_version') ///
		fun("`list_objects' `list_functions' `list_misc'") ///
		verbose ///
		`force'
end

program Reload
	* Internal debugging tool.
	* Updates dependencies and reghdfe from local path or from github
	* Usage:
	* 	reghdfe, update // from c:\git\..
	* 	reghdfe, reload // from github

	args online
	if ("`online'" == "") loc online 0

	di as text _n "{bf:reghdfe: updating required packages}"
	di as text "{hline 64}"

	* -ftools- https://github.com/sergiocorreia/ftools/
	cap ado uninstall ftools
	if (`online') net install ftools, from("https://github.com/sergiocorreia/ftools/raw/master/src/")
	if (!`online') net install ftools, from("c:\git\ftools\src")
	di as text "{hline 64}"
	ftools, compile
	di as text "{hline 64}"

	* Update -reghdfe-
	di as text _n  _n "{bf:reghdfe: updating self}"
	di as text "{hline 64}"
	qui ado uninstall reghdfe
	if (`online') net install reghdfe, from("https://github.com/sergiocorreia/reghdfe/raw/master/src/")
	if (!`online') net install reghdfe, from("c:\git\reghdfe\src")
	qui which reghdfe
	di as text "{hline 64}"
	reghdfe, compile
	di as text "{hline 64}"

	* Cleaning up
	di as text _n "{bf:Note:} You need to run {stata program drop _all} now."
end

program Version
	which reghdfe
	Requirements
end

program Requirements
	di as text _n "Required packages installed?"
	loc reqs ftools
	// ivreg2 avar tuples group3hdfe
	if (c(stata_version)<13) loc reqs `reqs' boottest

	loc ftools_github "https://github.com/sergiocorreia/ftools/raw/master/src/"

	loc error 0

	foreach req of local reqs {
		loc fn `req'.ado
		cap findfile `fn'
		if (_rc) {
			loc error 1
			di as text "{lalign 20:- `req'}" as error "not" _c
			di as text "    {stata ssc install `req':install from SSC}" _c
			if inlist("`req'", "ftools") {
				loc github ``req'_github'
				di as text `"    {stata `"net install `req', from(`"`github'"')"':install from github}"'
			}
			else {
				di as text // newline
			}
		}
		else {
			di as text "{lalign 20:- `req'}" as text "yes"
		}
	}

	if (`error') exit 601
end

program Store_Alphas, eclass
	mata: st_local("save_any_fe", strofreal(HDFE.save_any_fe))
	assert inlist(`save_any_fe', 0, 1)
	if (`save_any_fe') {
		_assert e(depvar) != "", msg("e(depvar) is empty")
		_assert e(resid) != "", msg("e(resid) is empty")
		// we can't use -confirm var- because it might have TS operators
		fvrevar `e(depvar)', list
		confirm numeric var `e(resid)', exact
		tempvar d
		if (e(rank)) {
			qui _predict double `d' if e(sample), xb
		}
		else if (e(report_constant)) {
			gen double `d' = _b[_cons] if e(sample)
		}
		else {
			gen double `d' = 0 if e(sample)
		}
		qui replace `d' = `e(depvar)' - `d' - `e(resid)' if e(sample)

		mata: HDFE.store_alphas("`d'")
		drop `d'

		// Drop resid if we don't want to save it; and update e(resid)
		cap drop __temp_reghdfe_resid__
		if (!c(rc)) ereturn local resid
	}
end

program Cleanup
	args rc
	cap mata: mata drop HDFE
	cap mata: mata drop hdfe_*
	cap drop __temp_reghdfe_resid__
	cap matrix drop reghdfe_statsmatrix
	if (`rc' == 132) {
		di as text "- If you got the {it:parentheses unbalanced} error, note that IV/2SLS was moved to {help ivreghdfe}"
		di as smcl `"- Latest version: {browse "https://github.com/sergiocorreia/ivreghdfe":https://github.com/sergiocorreia/ivreghdfe}"'
		di as smcl `"- SSC version: {stata "net describe ivreghdfe, from(http://fmwww.bc.edu/RePEc/bocode/i)"}"'
		di as smcl `"- Note: the older functionality can still be accessed through the {it:old} option"'
	}
	if (`rc') exit `rc'
end

program Parse
	* Trim whitespace (caused by "///" line continuations; aesthetic only)
	mata: st_local("0", stritrim(st_local("0")))

	* Main syntax
	#d;
	syntax varlist(fv ts numeric) [if] [in] [aw pw fw/] , [

		/* Model */
		Absorb(string) NOAbsorb
		SUmmarize SUmmarize2(string asis) /* simulate implicit options */

		/* Standard Errors */
		VCE(string) CLuster(string)

		/* Diagnostic */
		Verbose(numlist min=1 max=1 >=-1 <=5 integer)
		TIMEit

		/* Speedup and memory Tricks */
		NOSAMPle /* do not save e(sample) */
		COMPACT /* use as little memory as possible but is slower */

		/* Extra display options (based on regress) */
		noHEader noTABle noFOOTnote

		/* Undocumented */
		KEEPSINgletons
		OLD /* use latest v3 */
		NOTES(string) /* NOTES(key=value ...), will be stored on e() */

		] [*] /* capture optimization options, display options, etc. */
		;
	#d cr

	* Unused
	* SAVEcache
	* USEcache
	* CLEARcache

	* Convert options to boolean
	if ("`verbose'" == "") loc verbose 0
	loc timeit = ("`timeit'"!="")
	loc drop_singletons = ("`keepsingletons'" == "")
	loc compact = ("`compact'" != "")

	if (`timeit') timer on 29

	* Sanity checks
	if (`verbose'>-1 & "`keepsingletons'"!="") {
		loc url "http://scorreia.com/reghdfe/nested_within_cluster.pdf"
		loc msg "WARNING: Singleton observations not dropped; statistical significance is biased"
		di as error `"`msg' {browse "`url'":(link)}"'
	}
	if ("`cluster'"!="") {
		_assert ("`vce'"==""), msg("cannot specify both cluster() and vce()")
		loc vce cluster `cluster'
		loc cluster // clear it to avoid bugs in subsequent lines
	}

	* Split varlist into <depvar> and <indepvars>
	ms_parse_varlist `varlist'
	if (`verbose' > 0) {
		di as text _n "## Parsing varlist: {res}`varlist'"
		return list
	}
	loc depvar `r(depvar)'
	loc indepvars `r(indepvars)'
	loc fe_format "`r(fe_format)'"
	loc basevars `r(basevars)'

	* Parse Weights
	if ("`weight'"!="") {
		unab exp : `exp', min(1) max(1) // simple weights only
	}

	* Parse VCE
	ms_parse_vce, vce(`vce') weighttype(`weight')
	if (`verbose' > 0) {
		di as text _n "## Parsing vce({res}`vce'{txt})"
		sreturn list
	}
	loc vcetype = "`s(vcetype)'"
	loc num_clusters = `s(num_clusters)'
	loc clustervars = "`s(clustervars)'"
	loc base_clustervars = "`s(base_clustervars)'"
	loc vceextra = "`s(vceextra)'"

	* Select sample (except for absvars)
	loc varlist `depvar' `indepvars' `base_clustervars'
	tempvar touse
	marksample touse, strok // based on varlist + cluster + if + in + weight

	* Parse noabsorb
	_assert  ("`absorb'`noabsorb'" != ""), msg("option {bf:absorb()} or {bf:noabsorb} required")
	if ("`noabsorb'" != "") {
		_assert ("`absorb'" == ""), msg("{bf:absorb()} and {bf:noabsorb} are mutually exclusive")
	}

	if (`timeit') timer off 29

	* Construct HDFE object
	// SYNTAX: fixed_effects(absvars | , touse, wtype, wtvar, dropsing, verbose)
	ms_add_comma, loc(absorb) cmd(`"`absorb'"') opt(`"`options'"')
	if (`timeit') timer on 20
	mata: HDFE = fixed_effects(`"`absorb'"', "`touse'", "`weight'", "`exp'", `drop_singletons', `verbose')
	if (`timeit') timer off 20
	mata: HDFE.cmdline = "reghdfe " + st_local("0")
	loc options `s(options)'

	mata: st_local("N", strofreal(HDFE.N))
	if (`N' == 0) error 2000

	* Fill out HDFE object
	* mata: HDFE.varlist = "`base_varlist'"
	mata: HDFE.depvar = "`depvar'"
	mata: HDFE.indepvars = "`indepvars'"
	mata: HDFE.vcetype = "`vcetype'"
	mata: HDFE.num_clusters = `num_clusters'
	mata: HDFE.clustervars = tokens("`clustervars'")
	mata: HDFE.base_clustervars = tokens("`base_clustervars'")
	mata: HDFE.vceextra = "`vceextra'"

	* Preserve memory
	mata: HDFE.compact = `compact'
	if (`compact') {
		loc panelvar "`_dta[_TSpanel]'"
		loc timevar "`_dta[_TStvar]'"
		mata: HDFE.panelvar = "`panelvar'"
		mata: HDFE.timevar = "`timevar'"
		c_local keepvars `basevars' `base_clustervars' `panelvar' `timevar' // `exp'
	}

	* Parse summarize
	if ("`summarize'" != "") {
		_assert ("`summarize2'" == ""), msg("summarize() syntax error")
		loc summarize2 mean min max  // default values
	}
	ParseSummarize `summarize2'
	mata: HDFE.summarize_stats = "`s(stats)'"
	mata: HDFE.summarize_quietly = `s(quietly)'

	* Parse misc options
	mata: HDFE.notes = `"`notes'"'
	mata: HDFE.store_sample = ("`nosample'"=="")
	mata: HDFE.timeit = `timeit'

	* Parse Coef Table Options (do this last!)
	_get_diopts diopts options, `options' // store in `diopts', and the rest back to `options'
	loc diopts `diopts' `header' `table' `footnote'
	_assert (`"`options'"'==""), msg(`"invalid options: `options'"')
	if ("`hascons'"!="") di in ye "(option ignored: `hascons')"
	if ("`tsscons'"!="") di in ye "(option ignored: `tsscons')"
	mata: HDFE.diopts = `"`diopts'"'
end

program ParseSummarize, sclass
	sreturn clear
	syntax [namelist(name=stats)] , [QUIetly]
	local quietly = ("`quietly'"!="")
	sreturn loc stats "`stats'"
	sreturn loc quietly = `quietly'
end

// --------------------------------------------------------------------------

program Estimate, eclass
	ereturn clear

	* Parse and fill out HDFE object
	Parse `0'
	mata: st_local("timeit", strofreal(HDFE.timeit))
	mata: st_local("compact", strofreal(HDFE.compact))
	mata: st_local("verbose", strofreal(HDFE.verbose))

	* Compute degrees-of-freedom
	if (`timeit') timer on 21
	mata: HDFE.estimate_dof()
	if (`timeit') timer off 21

	* Save updated e(sample) (singletons reduce sample);
	* required to parse factor variables to partial out
	if (`timeit') timer on 29
	tempvar touse
	mata: HDFE.save_touse("`touse'")
	if (`timeit') timer off 29

	* Expand varlists
	if (`timeit') timer on 22
	mata: st_local("depvar", HDFE.depvar)
	mata: st_local("indepvars", HDFE.indepvars)
	if (`verbose' > 0) di as text _n "## Parsing and expanding indepvars: {res}`indepvars'"
	ms_expand_varlist `indepvars' if `touse'
	if (`verbose' > 0) return list
	mata: HDFE.fullindepvars = "`r(fullvarlist)'"
	mata: HDFE.indepvars = "`r(varlist)'"
	mata: HDFE.not_basevar = strtoreal(tokens("`r(not_omitted)'"))
	mata: HDFE.varlist = "`depvar' `r(varlist)'"
	if (`timeit') timer off 22

	* Stats
	mata: st_local("stats", HDFE.summarize_stats)
	if ("`stats'" != "") Stats `touse'

	* Condition number
	mata: HDFE.estimate_cond()

	* Preserve
	if (`compact') {
		if (`verbose' > 0) di as text "## Preserving dataset"
		preserve
		keep `keepvars'
	}

	* Partial out; save TSS of depvar
	if (`timeit') timer on 23
	// SYNTAX: partial_out(Varlist/Matrix | , Save TSS if HDFE.tss is missing? [0], Standardize data? [1], First col is depvar? [1])
	// Note: standardize=2 will standardize, partial out, and return the data standardized!
	mata: hdfe_variables = HDFE.partial_out(HDFE.varlist, 1, 2, .)
	if (`timeit') timer off 23

	* Regress
	if (`timeit') timer on 24
	tempname b V N rank df_r
	mata: reghdfe_post_ols(HDFE, hdfe_variables, "`b'", "`V'", "`N'", "`rank'", "`df_r'")
	mata: hdfe_variables = .
	* Restore
	if (`compact') {
		if (`verbose' > 0) di as text "## Restoring dataset"
		restore
		mata: st_local("residuals", HDFE.residuals)
		if ("`residuals'" != "") mata: HDFE.save_variable(HDFE.residuals, HDFE.residuals_vector, "Residuals")
	}
	RegressOLS `touse' `b' `V' `N' `rank' `df_r'
	if (`timeit') timer off 24

	* (optional) Store FEs
	if (`timeit') timer on 29
	reghdfe, store_alphas
	if (`timeit') timer off 29

	* View estimation tables
	mata: st_local("diopts", HDFE.diopts)
	Replay, `diopts'

	if (`timeit') {
		di as text _n "{bf: Timer results:}"
		timer list
		di as text "Legend: 20: Create HDFE object; 21: Estimate DoF; 22: expand varlists; 23: partial out; 24: regress; 29: rest"
		di
	}
end

program RegressOLS, eclass
	args touse b V N rank df_r

	mata: st_local("store_sample", strofreal(HDFE.store_sample))
	if (`store_sample') loc esample "esample(`touse')"

	mata: st_local("indepvars", HDFE.fullindepvars)
	if ("`indepvars'" != "") {
		matrix colnames `b' = `indepvars'
		matrix colnames `V' = `indepvars'
		matrix rownames `V' = `indepvars'
		_ms_findomitted `b' `V'
		ereturn post `b' `V', `esample' buildfvinfo depname(`depvar')
	}
	else {
		ereturn post, `esample' buildfvinfo depname(`depvar')
	}

	ereturn scalar N       = `N'
	ereturn scalar rank    = `rank'
	ereturn scalar df_r    = `df_r'
	ereturn local  cmd     "reghdfe"
	mata: HDFE.post()

	* Post stats
	cap conf matrix reghdfe_statsmatrix
	if (!c(rc)) {
		ereturn matrix summarize = reghdfe_statsmatrix
		mata: st_local("summarize_quietly", strofreal(HDFE.summarize_quietly))
		ereturn scalar summarize_quietly = `summarize_quietly'
	}
end

program Replay, rclass
	syntax [, noHEader noTABle noFOOTnote *]

	if `"`e(cmd)'"' != "reghdfe"  {
	        error 301
	}

	_get_diopts options, `options'
	if ("`header'" == "") {
		reghdfe_header // _coef_table_header
		di ""
	}
	if ("`table'" == "") {
		_coef_table, `options' // ereturn display, `options'
		return add // adds r(level), r(table), etc. to ereturn (before the footnote deletes them)
	}
	if ("`footnote'" == "") {
		reghdfe_footnote
	}

	* Replay stats
	if (e(summarize_quietly)==0) {
		di as text _n "{sf:Regression Summary Statistics:}" _c
		matlist e(summarize)', border(top bottom) rowtitle(Variable) // twidth(18)
	}
end

program Stats
	args touse
	* Optional weights
	mata: st_local("weight", sprintf("[%s=%s]", HDFE.weight_type, HDFE.weight_var))
	assert "`weight'" != ""
	if ("`weight'" == "[=]") loc weight
	loc weight : subinstr local weight "[pweight" "[aweight"

	mata: st_local("stats", HDFE.summarize_stats)
	mata: st_local("varlist", HDFE.varlist)
	mata: st_local("cvars", invtokens(HDFE.cvars))
	loc full_varlist `varlist' `cvars'

	* quick workaround b/c -tabstat- does not support factor variables
	fvrevar `full_varlist', list
	loc full_varlist `r(varlist)'

	qui tabstat `full_varlist' if `touse' `weight' , stat(`stats') col(stat) save
	matrix reghdfe_statsmatrix = r(StatTotal)
end

findfile "reghdfe.mata"
include "`r(fn)'"

exit

*! version 5.7.0 20mar2019

program reghdfe, eclass
	* Intercept old+version
	cap syntax, version old
	if !c(rc) {
		reghdfe_old, version
		exit
	}

	* Intercept old
	cap syntax anything(everything) [fw aw pw/], [*] old
	if !c(rc) {
		di as error "(running historical version of reghdfe)"
		if ("`weight'"!="") local weightexp [`weight'=`exp']
		reghdfe_old `anything' `weightexp', `options'
		exit
	}

	* Aux. subcommands
	cap syntax, [*]
	if inlist("`options'", "check", "compile", "reload", "update", "version", "requirements", "store_alphas") {
		if ("`options'"=="compile") loc args force
		if ("`options'"=="check") loc options compile
		if ("`options'"=="update") {
			loc args 1
			loc options reload
		}
		loc subcmd = proper("`options'")
		`subcmd' `args'
	}
	else if replay() {
		Replay `0'
	}
	else {
		Cleanup 0
		ms_get_version ftools, min_version("2.36.1") // Compile // takes 0.01s to run this useful check (ensures .mlib exists)
		cap noi Estimate `0'
		Cleanup `c(rc)'
	}
end

program Compile
	args force

	* Check dependencies
	ftools, check // in case lftools.mlib does not exist or is outdated
	ms_get_version ftools, min_version("2.34.0")
	ms_get_version reghdfe // save local package_version
	loc list_objects "FixedEffects() fixed_effects() BipartiteGraph()"
	loc list_functions "reghdfe_*() transform_*() accelerate_*() panelmean() panelsolve_*() lsmr()"
	loc list_misc "weighted_quadcolsum() safe_divide() check_convergence() precompute_inv_xx() _st_data_wrapper()"
	// TODO: prefix everything with reghdfe_*

	ms_compile_mata, ///
		package(reghdfe) ///
		version(`package_version') ///
		fun("`list_objects' `list_functions' `list_misc'") ///
		verbose ///
		`force'
end

program Reload
	* Internal debugging tool.
	* Updates dependencies and reghdfe from local path or from github
	* Usage:
	* 	reghdfe, update // from c:\git\..
	* 	reghdfe, reload // from github

	args online
	if ("`online'" == "") loc online 0

	di as text _n "{bf:reghdfe: updating required packages}"
	di as text "{hline 64}"

	* -ftools- https://github.com/sergiocorreia/ftools/
	cap ado uninstall ftools
	if (`online') net install ftools, from("https://github.com/sergiocorreia/ftools/raw/master/src/")
	if (!`online') net install ftools, from("c:\git\ftools\src")
	di as text "{hline 64}"
	ftools, compile
	di as text "{hline 64}"

	* Update -reghdfe-
	di as text _n  _n "{bf:reghdfe: updating self}"
	di as text "{hline 64}"
	qui ado uninstall reghdfe
	if (`online') net install reghdfe, from("https://github.com/sergiocorreia/reghdfe/raw/master/src/")
	if (!`online') net install reghdfe, from("c:\git\reghdfe\src")
	qui which reghdfe
	di as text "{hline 64}"
	reghdfe, compile
	di as text "{hline 64}"

	* Cleaning up
	di as text _n "{bf:Note:} You need to run {stata program drop _all} now."
end

program Version
	which reghdfe
	Requirements
end

program Requirements
	di as text _n "Required packages installed?"
	loc reqs ftools
	// ivreg2 avar tuples group3hdfe
	if (c(stata_version)<13) loc reqs `reqs' boottest

	loc ftools_github "https://github.com/sergiocorreia/ftools/raw/master/src/"

	loc error 0

	foreach req of local reqs {
		loc fn `req'.ado
		cap findfile `fn'
		if (_rc) {
			loc error 1
			di as text "{lalign 20:- `req'}" as error "not" _c
			di as text "    {stata ssc install `req':install from SSC}" _c
			if inlist("`req'", "ftools") {
				loc github ``req'_github'
				di as text `"    {stata `"net install `req', from(`"`github'"')"':install from github}"'
			}
			else {
				di as text // newline
			}
		}
		else {
			di as text "{lalign 20:- `req'}" as text "yes"
		}
	}

	if (`error') exit 601
end

program Store_Alphas, eclass
	mata: st_local("save_any_fe", strofreal(HDFE.save_any_fe))
	assert inlist(`save_any_fe', 0, 1)
	if (`save_any_fe') {
		_assert e(depvar) != "", msg("e(depvar) is empty")
		_assert e(resid) != "", msg("e(resid) is empty")
		// we can't use -confirm var- because it might have TS operators
		fvrevar `e(depvar)', list
		confirm numeric var `e(resid)', exact
		tempvar d
		if (e(rank)) {
			qui _predict double `d' if e(sample), xb
		}
		else if (e(report_constant)) {
			gen double `d' = _b[_cons] if e(sample)
		}
		else {
			gen double `d' = 0 if e(sample)
		}
		qui replace `d' = `e(depvar)' - `d' - `e(resid)' if e(sample)

		mata: HDFE.store_alphas("`d'")
		drop `d'

		// Drop resid if we don't want to save it; and update e(resid)
		cap drop __temp_reghdfe_resid__
		if (!c(rc)) ereturn local resid
	}
end

program Cleanup
	args rc
	cap mata: mata drop HDFE
	cap mata: mata drop hdfe_*
	cap drop __temp_reghdfe_resid__
	cap matrix drop reghdfe_statsmatrix
	if (`rc' == 132) {
		di as text "- If you got the {it:parentheses unbalanced} error, note that IV/2SLS was moved to {help ivreghdfe}"
		di as smcl `"- Latest version: {browse "https://github.com/sergiocorreia/ivreghdfe":https://github.com/sergiocorreia/ivreghdfe}"'
		di as smcl `"- SSC version: {stata "net describe ivreghdfe, from(http://fmwww.bc.edu/RePEc/bocode/i)"}"'
		di as smcl `"- Note: the older functionality can still be accessed through the {it:old} option"'
	}
	if (`rc') exit `rc'
end

program Parse
	* Trim whitespace (caused by "///" line continuations; aesthetic only)
	mata: st_local("0", stritrim(st_local("0")))

	* Main syntax
	#d;
	syntax varlist(fv ts numeric) [if] [in] [aw pw fw/] , [

		/* Model */
		Absorb(string) NOAbsorb
		SUmmarize SUmmarize2(string asis) /* simulate implicit options */

		/* Standard Errors */
		VCE(string) CLuster(string)

		/* Diagnostic */
		Verbose(numlist min=1 max=1 >=-1 <=5 integer)
		TIMEit

		/* Speedup and memory Tricks */
		NOSAMPle /* do not save e(sample) */
		COMPACT /* use as little memory as possible but is slower */

		/* Undocumented */
		KEEPSINgletons
		OLD /* use latest v3 */
		NOTES(string) /* NOTES(key=value ...), will be stored on e() */

		] [*] /* capture optimization options, display options, etc. */
		;
	#d cr

	* Unused
	* SAVEcache
	* USEcache
	* CLEARcache

	* Convert options to boolean
	if ("`verbose'" == "") loc verbose 0
	loc timeit = ("`timeit'"!="")
	loc drop_singletons = ("`keepsingletons'" == "")
	loc compact = ("`compact'" != "")

	if (`timeit') timer on 29

	* Sanity checks
	if (`verbose'>-1 & "`keepsingletons'"!="") {
		loc url "http://scorreia.com/reghdfe/nested_within_cluster.pdf"
		loc msg "WARNING: Singleton observations not dropped; statistical significance is biased"
		di as error `"`msg' {browse "`url'":(link)}"'
	}
	if ("`cluster'"!="") {
		_assert ("`vce'"==""), msg("cannot specify both cluster() and vce()")
		loc vce cluster `cluster'
		loc cluster // clear it to avoid bugs in subsequent lines
	}

	* Split varlist into <depvar> and <indepvars>
	ms_parse_varlist `varlist'
	if (`verbose' > 0) {
		di as text _n "## Parsing varlist: {res}`varlist'"
		return list
	}
	loc depvar `r(depvar)'
	loc indepvars `r(indepvars)'
	loc fe_format "`r(fe_format)'"
	loc basevars `r(basevars)'

	* Parse Weights
	if ("`weight'"!="") {
		unab exp : `exp', min(1) max(1) // simple weights only
	}

	* Parse VCE
	ms_parse_vce, vce(`vce') weighttype(`weight')
	if (`verbose' > 0) {
		di as text _n "## Parsing vce({res}`vce'{txt})"
		sreturn list
	}
	loc vcetype = "`s(vcetype)'"
	loc num_clusters = `s(num_clusters)'
	loc clustervars = "`s(clustervars)'"
	loc base_clustervars = "`s(base_clustervars)'"
	loc vceextra = "`s(vceextra)'"

	* Select sample (except for absvars)
	loc varlist `depvar' `indepvars' `base_clustervars'
	tempvar touse
	marksample touse, strok // based on varlist + cluster + if + in + weight

	* Parse noabsorb
	_assert  ("`absorb'`noabsorb'" != ""), msg("option {bf:absorb()} or {bf:noabsorb} required")
	if ("`noabsorb'" != "") {
		_assert ("`absorb'" == ""), msg("{bf:absorb()} and {bf:noabsorb} are mutually exclusive")
	}

	if (`timeit') timer off 29

	* Construct HDFE object
	// SYNTAX: fixed_effects(absvars | , touse, wtype, wtvar, dropsing, verbose)
	ms_add_comma, loc(absorb) cmd(`"`absorb'"') opt(`"`options'"')
	if (`timeit') timer on 20
	mata: HDFE = fixed_effects(`"`absorb'"', "`touse'", "`weight'", "`exp'", `drop_singletons', `verbose')
	if (`timeit') timer off 20
	mata: HDFE.cmdline = "reghdfe " + st_local("0")
	loc options `s(options)'

	mata: st_local("N", strofreal(HDFE.N))
	if (`N' == 0) error 2000

	* Fill out HDFE object
	* mata: HDFE.varlist = "`base_varlist'"
	mata: HDFE.depvar = "`depvar'"
	mata: HDFE.indepvars = "`indepvars'"
	mata: HDFE.vcetype = "`vcetype'"
	mata: HDFE.num_clusters = `num_clusters'
	mata: HDFE.clustervars = tokens("`clustervars'")
	mata: HDFE.base_clustervars = tokens("`base_clustervars'")
	mata: HDFE.vceextra = "`vceextra'"

	* Preserve memory
	mata: HDFE.compact = `compact'
	if (`compact') {
		loc panelvar "`_dta[_TSpanel]'"
		loc timevar "`_dta[_TStvar]'"
		mata: HDFE.panelvar = "`panelvar'"
		mata: HDFE.timevar = "`timevar'"
		c_local keepvars `basevars' `base_clustervars' `panelvar' `timevar' // `exp'
	}

	* Parse summarize
	if ("`summarize'" != "") {
		_assert ("`summarize2'" == ""), msg("summarize() syntax error")
		loc summarize2 mean min max  // default values
	}
	ParseSummarize `summarize2'
	mata: HDFE.summarize_stats = "`s(stats)'"
	mata: HDFE.summarize_quietly = `s(quietly)'

	* Parse misc options
	mata: HDFE.notes = `"`notes'"'
	mata: HDFE.store_sample = ("`nosample'"=="")
	mata: HDFE.timeit = `timeit'

	* Parse Coef Table Options (do this last!)
	_get_diopts diopts options, `options' // store in `diopts', and the rest back to `options'
	_assert (`"`options'"'==""), msg(`"invalid options: `options'"')
	if ("`hascons'"!="") di in ye "(option ignored: `hascons')"
	if ("`tsscons'"!="") di in ye "(option ignored: `tsscons')"
	mata: HDFE.diopts = `"`diopts'"'
end

program ParseSummarize, sclass
	sreturn clear
	syntax [namelist(name=stats)] , [QUIetly]
	local quietly = ("`quietly'"!="")
	sreturn loc stats "`stats'"
	sreturn loc quietly = `quietly'
end

// --------------------------------------------------------------------------

program Estimate, eclass
	ereturn clear

	* Parse and fill out HDFE object
	Parse `0'
	mata: st_local("timeit", strofreal(HDFE.timeit))
	mata: st_local("compact", strofreal(HDFE.compact))
	mata: st_local("verbose", strofreal(HDFE.verbose))

	* Compute degrees-of-freedom
	if (`timeit') timer on 21
	mata: HDFE.estimate_dof()
	if (`timeit') timer off 21

	* Save updated e(sample) (singletons reduce sample);
	* required to parse factor variables to partial out
	if (`timeit') timer on 29
	tempvar touse
	mata: HDFE.save_touse("`touse'")
	if (`timeit') timer off 29

	* Expand varlists
	if (`timeit') timer on 22
	mata: st_local("depvar", HDFE.depvar)
	mata: st_local("indepvars", HDFE.indepvars)
	if (`verbose' > 0) di as text _n "## Parsing and expanding indepvars: {res}`indepvars'"
	ms_expand_varlist `indepvars' if `touse'
	if (`verbose' > 0) return list
	mata: HDFE.fullindepvars = "`r(fullvarlist)'"
	mata: HDFE.indepvars = "`r(varlist)'"
	mata: HDFE.not_basevar = strtoreal(tokens("`r(not_omitted)'"))
	mata: HDFE.varlist = "`depvar' `r(varlist)'"
	if (`timeit') timer off 22

	* Stats
	mata: st_local("stats", HDFE.summarize_stats)
	if ("`stats'" != "") Stats `touse'

	* Condition number
	mata: HDFE.estimate_cond()

	* Preserve
	if (`compact') {
		if (`verbose' > 0) di as text "## Preserving dataset"
		preserve
		keep `keepvars'
	}

	* Partial out; save TSS of depvar
	if (`timeit') timer on 23
	// SYNTAX: partial_out(Varlist/Matrix | , Save TSS if HDFE.tss is missing? [0], Standardize data? [1], First col is depvar? [1])
	// Note: standardize=2 will standardize, partial out, and return the data standardized!
	mata: hdfe_variables = HDFE.partial_out(HDFE.varlist, 1, 2, .)
	if (`timeit') timer off 23

	* Regress
	if (`timeit') timer on 24
	tempname b V N rank df_r
	mata: reghdfe_post_ols(HDFE, hdfe_variables, "`b'", "`V'", "`N'", "`rank'", "`df_r'")
	mata: hdfe_variables = .
	* Restore
	if (`compact') {
		if (`verbose' > 0) di as text "## Restoring dataset"
		restore
		mata: st_local("residuals", HDFE.residuals)
		if ("`residuals'" != "") mata: HDFE.save_variable(HDFE.residuals, HDFE.residuals_vector, "Residuals")
	}
	RegressOLS `touse' `b' `V' `N' `rank' `df_r'
	if (`timeit') timer off 24

	* (optional) Store FEs
	if (`timeit') timer on 29
	reghdfe, store_alphas
	if (`timeit') timer off 29

	* View estimation tables
	mata: st_local("diopts", HDFE.diopts)
	Replay, `diopts'

	if (`timeit') {
		di as text _n "{bf: Timer results:}"
		timer list
		di as text "Legend: 20: Create HDFE object; 21: Estimate DoF; 22: expand varlists; 23: partial out; 24: regress; 29: rest"
		di
	}
end

program RegressOLS, eclass
	args touse b V N rank df_r

	mata: st_local("store_sample", strofreal(HDFE.store_sample))
	if (`store_sample') loc esample "esample(`touse')"

	mata: st_local("indepvars", HDFE.fullindepvars)
	if ("`indepvars'" != "") {
		matrix colnames `b' = `indepvars'
		matrix colnames `V' = `indepvars'
		matrix rownames `V' = `indepvars'
		_ms_findomitted `b' `V'
		ereturn post `b' `V', `esample' buildfvinfo depname(`depvar')
	}
	else {
		ereturn post, `esample' buildfvinfo depname(`depvar')
	}

	ereturn scalar N       = `N'
	ereturn scalar rank    = `rank'
	ereturn scalar df_r    = `df_r'
	ereturn local  cmd     "reghdfe"
	mata: HDFE.post()

	* Post stats
	cap conf matrix reghdfe_statsmatrix
	if (!c(rc)) {
		ereturn matrix summarize = reghdfe_statsmatrix
		mata: st_local("summarize_quietly", strofreal(HDFE.summarize_quietly))
		ereturn scalar summarize_quietly = `summarize_quietly'
	}
end

program Replay, rclass
	syntax [, *]

	if `"`e(cmd)'"' != "reghdfe"  {
	        error 301
	}

	_get_diopts options, `options'
	reghdfe_header // _coef_table_header
	di ""
	_coef_table, `options' // ereturn display, `options'
	return add // adds r(level), r(table), etc. to ereturn (before the footnote deletes them)
	reghdfe_footnote

	* Replay stats
	if (e(summarize_quietly)==0) {
		di as text _n "{sf:Regression Summary Statistics:}" _c
		matlist e(summarize)', border(top bottom) rowtitle(Variable) // twidth(18)
	}
end

program Stats
	args touse
	* Optional weights
	mata: st_local("weight", sprintf("[%s=%s]", HDFE.weight_type, HDFE.weight_var))
	assert "`weight'" != ""
	if ("`weight'" == "[=]") loc weight
	loc weight : subinstr local weight "[pweight" "[aweight"

	mata: st_local("stats", HDFE.summarize_stats)
	mata: st_local("varlist", HDFE.varlist)
	mata: st_local("cvars", invtokens(HDFE.cvars))
	loc full_varlist `varlist' `cvars'

	* quick workaround b/c -tabstat- does not support factor variables
	fvrevar `full_varlist', list
	loc full_varlist `r(varlist)'

	qui tabstat `full_varlist' if `touse' `weight' , stat(`stats') col(stat) save
	matrix reghdfe_statsmatrix = r(StatTotal)
end

findfile "reghdfe.mata"
include "`r(fn)'"

exit


ieboilstart : used test command name in final .ado file

ieboilstart : used test command name in final .ado file

	*! version 2.0 24Oct2016  Kristoffer Bjarkefur kbjarkefur@worldbank.org

	capture program drop ieboilstart
	program ieboilstart , rclass

	qui {

		syntax using ,  Versionnumber(string) [noclear maxvar(numlist) matsize(numlist) QUIetdo CUSTom(string) ]

		*Potentional updates
		*set trace on
		*set scrollbufsize #

		version 10.0
		version `versionnumber'

		/*********************************

			Check file name

		*********************************/

		*Parse out the file name
		tokenize `"`using'"'
		local 		textfile "`2'"

		*Find index for where the file type suffix start
		local dot_index 	= strpos("`textfile'",".")

		*Extract the file index
		local file_suffix 	= substr("`textfile'", `dot_index', .)

		*If no file format suffix is specified, use the default .xlsx
		if "`file_suffix'" == "" {

			local textfile `"`textfile'.do"'
		}

		*If a file format suffix is specified make sure that it is one of the two allowed.
		else if "`file_suffix'" != ".do" {

			noi display as error "{phang}The file format specified in using is other than .do. Only the .do format is allowed. If no format is specified .do is assumed.{p_end}"
			error 198
		}

		/*********************************

			Check version

		*********************************/

		if "`maxvar'" != "" & "`clear'" != "" {

			di as error "{phang}It is not possible to set allowed maximum numbers of variables without clearing the data. noclear and maxvar() can therefore not be specified at the same time{p_end}"
			di ""
			error 198
			exit
		}

		local stata_versions "10.0 10.1 11.0 11.1 11.2 12.0 12.1 13.0 13.1 14.0 14.1"

		if `:list versionnumber in stata_versions' == 0 {

			di as error "{phang}Only recent major releases are allowed. One decimal must always be included. The releases currently allowed are:{break}`stata_versions'{p_end}"
			di ""
			error 198
			exit
		}

		/*********************************

			Check input for maxvar and matsize
			if specified, other wise set
			maximum value allowed.

		*********************************/

		local stata_types ic se mp
		foreach maxlocal in maxvar matsize {

			if "`maxlocal'" == "maxvar" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 32767
					local min 2048
				}
				else {
					local max 2047
					local min 2047
				}
			}

			if "`maxlocal'" == "matsize" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 11000
					local min 10
				}
				else {
					local max 800
					local min 10
				}
			}

			if c(MP) == 1 | c(SE) == 1 {
				local vusing "Stata SE and Stata MP"
			}
			else {
				local vusing "Stata IC"
			}

			*Test if user set maxvar
			if "``maxlocal''" != "" {

				if ``maxlocal'' >= `min' & ``maxlocal'' <= `max' {

					local `maxlocal' ``maxlocal''
				}
				else {

					di as error "{phang}`maxlocal' must be between `min' and `max' (inclusive) if you are using `vusing'. You entered ``maxlocal''.{p_end}"
					if ``maxlocal'' < `min' {
						di ""
						error 910
					}
					else {
						di ""
						error 912
					}
					exit
				}
			}
			else {

				*User did not specify value, use max value allowed
				local `maxlocal' `max'
			}
		}

		/*********************************

			Prepare a local with all settings

		*********************************/

		local return_string ""

		*Set verison number
		local return_string `return_string' version `versionnumber' ;

		*Set basic memory limits
		if "`clear'" == "" {
			local return_string `return_string' clear all ;
			local return_string `return_string' set maxvar 	`maxvar' ;
		}

		local return_string `return_string'  set matsize 	`matsize' ;

		*Set advanced memory limits
		local return_string `return_string' set niceness	5 ;

		if "`clear'" == "" {
			*These settings cannot be modified with data in memory
			local return_string `return_string' set min_memory	0 ;
			local return_string `return_string' set max_memory	. ;

			if c(bit) == 64 {
				local return_string `return_string' set segmentsize	32m ;
			}
			else {
				local return_string `return_string' set segmentsize	16m ;
			}
		}

		*Set default options
		local return_string `return_string' set more 		off ;
		local return_string `return_string' pause 			on 	;
		local return_string `return_string' set varabbrev 	off ;

		/*********************************

			Add custom lines of code

		*********************************/

		if "`custom'" != "" {

			*Add a comment that list where
			local return_string `return_string' ** The lines of code below was added manually when iboilstart ;
			local return_string `return_string' *  was used to create  this do-file. If there are any issues ;
			local return_string `return_string' *  with the code below, please reconfigure ieboilstart and ;
			local return_string `return_string' *  generate a new do-file. ;

			*Create a local with the rowlabel input to be tokenized
			local custom_code_lines `custom'

			while "`custom_code_lines'" != "" {

				*Parsing name and label pair
				gettoken code_line custom_code_lines : custom_code_lines, parse("@")

				*Removing leadning or trailing spaces
				local code_line = trim("`code_line'")

				local return_string `return_string' `code_line';

				*Parse char is not removed by gettoken
				local custom_code_lines = subinstr("`custom_code_lines'" ,"@","",1)
			}
		}

		/*********************************

			Create the do-file

		*********************************/

		*Create a local with the date
		local		date = subinstr(c(current_date)," ","",.)

		*Write the title rows defined above
		capture file close settingsfile

		*Set up the file to be written
		file open  settingsfile using "`textfile'", text write replace

		if "`quietdo'" != "" file write settingsfile "quietly {" _n

		*Write the standard message in the opening file.
		file write settingsfile ///
																		_n ///
			`" 	/*************************************************"' 	_n ///
			`" 	This file is was prepared by command ieboilstart "' 	_n ///
			`" 	on `date'."'											_n ///
																		_n ///
			`" 	Run this file to set the recommended settings for "' 	_n ///
			`" 	this project to your instance of Stata"' 				_n ///
			`" 	*************************************************/"' 	_n ///
																		_n ///
																				_n ///
			"	*See help file for ieboilstart for details on these settings"	_n ///
			_n

		** Write each setting prepared above to the do-file.
		while ("`return_string'" != "") {

			*Tokenize the first setting
			gettoken setting return_string : return_string, parse(";")

			*Write it to file
			file write settingsfile "	`setting'" _n

			*Remove the parse character
			local return_string = subinstr("`return_string'",";","",1)
		}

		if "`quietdo'" != "" file write settingsfile "}" _n

		*Close file
		file close settingsfile

		/*********************************

			Create return value and output message

		*********************************/

		return local ieboil_dofile "`textfile'"

		noi di "{phang}Due to how settings in Stata work, a command can not change any settings itself. ieboilstart therefore only prepares a do-file with all recommended settings. See {help ieboilstart :help ieboilstart} for how to apply the settings ieboilstart has prepared. If you do not run the do-file, ieboilstart does not affect any of your settings.{p_end}"
		noi di ""
		noi di "{phang}Do-file with all settings saved to: `textfile'{p_end}"
	}
	end

	*! version 2.0 24Oct2016  Kristoffer Bjarkefur kbjarkefur@worldbank.org

	capture program drop ieboilstart2
	program ieboilstart2 , rclass

	qui {

		syntax using ,  Versionnumber(string) [noclear maxvar(numlist) matsize(numlist) QUIetdo CUSTom(string) ]

		*Potentional updates
		*set trace on
		*set scrollbufsize #

		version 10.0
		version `versionnumber'

		/*********************************

			Check file name

		*********************************/

		*Parse out the file name
		tokenize `"`using'"'
		local 		textfile "`2'"

		*Find index for where the file type suffix start
		local dot_index 	= strpos("`textfile'",".")

		*Extract the file index
		local file_suffix 	= substr("`textfile'", `dot_index', .)

		*If no file format suffix is specified, use the default .xlsx
		if "`file_suffix'" == "" {

			local textfile `"`textfile'.do"'
		}

		*If a file format suffix is specified make sure that it is one of the two allowed.
		else if "`file_suffix'" != ".do" {

			noi display as error "{phang}The file format specified in using is other than .do. Only the .do format is allowed. If no format is specified .do is assumed.{p_end}"
			error 198
		}

		/*********************************

			Check version

		*********************************/

		if "`maxvar'" != "" & "`clear'" != "" {

			di as error "{phang}It is not possible to set allowed maximum numbers of variables without clearing the data. noclear and maxvar() can therefore not be specified at the same time{p_end}"
			di ""
			error 198
			exit
		}

		local stata_versions "10.0 10.1 11.0 11.1 11.2 12.0 12.1 13.0 13.1 14.0 14.1"

		if `:list versionnumber in stata_versions' == 0 {

			di as error "{phang}Only recent major releases are allowed. One decimal must always be included. The releases currently allowed are:{break}`stata_versions'{p_end}"
			di ""
			error 198
			exit
		}

		/*********************************

			Check input for maxvar and matsize
			if specified, other wise set
			maximum value allowed.

		*********************************/

		local stata_types ic se mp
		foreach maxlocal in maxvar matsize {

			if "`maxlocal'" == "maxvar" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 32767
					local min 2048
				}
				else {
					local max 2047
					local min 2047
				}
			}

			if "`maxlocal'" == "matsize" {
				if c(MP) == 1 | c(SE) == 1 {
					local max 11000
					local min 10
				}
				else {
					local max 800
					local min 10
				}
			}

			if c(MP) == 1 | c(SE) == 1 {
				local vusing "Stata SE and Stata MP"
			}
			else {
				local vusing "Stata IC"
			}

			*Test if user set maxvar
			if "``maxlocal''" != "" {

				if ``maxlocal'' >= `min' & ``maxlocal'' <= `max' {

					local `maxlocal' ``maxlocal''
				}
				else {

					di as error "{phang}`maxlocal' must be between `min' and `max' (inclusive) if you are using `vusing'. You entered ``maxlocal''.{p_end}"
					if ``maxlocal'' < `min' {
						di ""
						error 910
					}
					else {
						di ""
						error 912
					}
					exit
				}
			}
			else {

				*User did not specify value, use max value allowed
				local `maxlocal' `max'
			}
		}

		/*********************************

			Prepare a local with all settings

		*********************************/

		local return_string ""

		*Set verison number
		local return_string `return_string' version `versionnumber' ;

		*Set basic memory limits
		if "`clear'" == "" {
			local return_string `return_string' clear all ;
			local return_string `return_string' set maxvar 	`maxvar' ;
		}

		local return_string `return_string'  set matsize 	`matsize' ;

		*Set advanced memory limits
		local return_string `return_string' set niceness	5 ;

		if "`clear'" == "" {
			*These settings cannot be modified with data in memory
			local return_string `return_string' set min_memory	0 ;
			local return_string `return_string' set max_memory	. ;

			if c(bit) == 64 {
				local return_string `return_string' set segmentsize	32m ;
			}
			else {
				local return_string `return_string' set segmentsize	16m ;
			}
		}

		*Set default options
		local return_string `return_string' set more 		off ;
		local return_string `return_string' pause 			on 	;
		local return_string `return_string' set varabbrev 	off ;

		/*********************************

			Add custom lines of code

		*********************************/

		if "`custom'" != "" {

			*Add a comment that list where
			local return_string `return_string' ** The lines of code below was added manually when iboilstart ;
			local return_string `return_string' *  was used to create  this do-file. If there are any issues ;
			local return_string `return_string' *  with the code below, please reconfigure ieboilstart and ;
			local return_string `return_string' *  generate a new do-file. ;

			*Create a local with the rowlabel input to be tokenized
			local custom_code_lines `custom'

			while "`custom_code_lines'" != "" {

				*Parsing name and label pair
				gettoken code_line custom_code_lines : custom_code_lines, parse("@")

				*Removing leadning or trailing spaces
				local code_line = trim("`code_line'")

				local return_string `return_string' `code_line';

				*Parse char is not removed by gettoken
				local custom_code_lines = subinstr("`custom_code_lines'" ,"@","",1)
			}
		}

		/*********************************

			Create the do-file

		*********************************/

		*Create a local with the date
		local		date = subinstr(c(current_date)," ","",.)

		*Write the title rows defined above
		capture file close settingsfile

		*Set up the file to be written
		file open  settingsfile using "`textfile'", text write replace

		if "`quietdo'" != "" file write settingsfile "quietly {" _n

		*Write the standard message in the opening file.
		file write settingsfile ///
																		_n ///
			`" 	/*************************************************"' 	_n ///
			`" 	This file is was prepared by command ieboilstart "' 	_n ///
			`" 	on `date'."'											_n ///
																		_n ///
			`" 	Run this file to set the recommended settings for "' 	_n ///
			`" 	this project to your instance of Stata"' 				_n ///
			`" 	*************************************************/"' 	_n ///
																		_n ///
																				_n ///
			"	*See help file for ieboilstart for details on these settings"	_n ///
			_n

		** Write each setting prepared above to the do-file.
		while ("`return_string'" != "") {

			*Tokenize the first setting
			gettoken setting return_string : return_string, parse(";")

			*Write it to file
			file write settingsfile "	`setting'" _n

			*Remove the parse character
			local return_string = subinstr("`return_string'",";","",1)
		}

		if "`quietdo'" != "" file write settingsfile "}" _n

		*Close file
		file close settingsfile

		/*********************************

			Create return value and output message

		*********************************/

		return local ieboil_dofile "`textfile'"

		noi di "{phang}Due to how settings in Stata work, a command can not change any settings itself. ieboilstart therefore only prepares a do-file with all recommended settings. See {help ieboilstart :help ieboilstart} for how to apply the settings ieboilstart has prepared. If you do not run the do-file, ieboilstart does not affect any of your settings.{p_end}"
		noi di ""
		noi di "{phang}Do-file with all settings saved to: `textfile'{p_end}"
	}
	end


Implement variations according to REST /related

#!/usr/bin/env python3
from collections import OrderedDict
import os
import json
import yaml
import re
from pathlib import Path
import redo
redo.ifchange('comap.py'); import comap

project = redo.base
project_path = Path('projects') / project

with redo.ifchange(evaluation_config=project_path / 'evaluation-config.yaml') \
        as files:
    evaluation_config = yaml.load(files['evaluation_config'])

databases = OrderedDict(evaluation_config['databases'])
outcome_ids = evaluation_config['outcome_ids']
concept_filenames = {
    outcome_id: '{}.{}.concepts.json'.format(project, outcome_id)
    for outcome_id in outcome_ids
}
child_concept_filenames = {
    outcome_id: '{}.{}.child-concepts.json'.format(project, outcome_id)
    for outcome_id in outcome_ids
}
casedef_paths = {
    outcome_id: project_path / 'case-definitions' / (outcome_id + '.yaml')
    for outcome_id in outcome_ids
}

with redo.ifchange(coding_systems='config/coding_systems.yaml',
                   references=(project_path / 'reference.yaml').as_posix(),
                   concepts=concept_filenames,
                   child_concepts=child_concept_filenames,
                   casedefs=casedef_paths) as files:
    coding_systems = yaml.load(files['coding_systems'])
    references = yaml.load(files['references'])
    concepts_by_outcome = {}
    for outcome_id, f in files['concepts'].items():
        concepts_by_outcome[outcome_id] = json.load(f)
    child_concepts_by_outcome = {}
    for outcome_id, f in files['child_concepts'].items():
        child_concepts_by_outcome[outcome_id] = json.load(f).items()
    outcomes = {
        outcome_id: yaml.load(f)
        for outcome_id, f in files['casedefs'].items()
    }

class Variation(object):

    """Identity"""

    def __init__(self):
        self.outcome_id = None
        self.coding_system = None

    def description(self):
        return self.__doc__

    def vary_codes(self, generated, reference, coding_systems):
        return generated, reference

    def vary_concepts(self, concepts, outcome_id, reference_mapping):
        return concepts

class VariationChain(Variation):

    """Chain of variations that are applied to the input left-to-right."""

    def __init__(self, *variations):
        self.variations = variations

    def set_context(self, *args, **kwargs):
        for variation in self.variations:
            variation.set_context(*args, **kwargs)

    def description(self):
        res = '; '.join(v.description() for v in self.variations)
        if res:
            return res[0].upper() + res[1:]
        else:
            return ""

    def vary_codes(self, generated, reference, coding_system):
        for variation in self.variations:
            generated, reference = variation.vary_codes(generated, reference, coding_system)
        return generated, reference

    def vary_concepts(self, concepts, outcome_id, reference_mapping):
        for variation in self.variations:
            concepts = variation.vary_concepts(concepts, outcome_id, reference_mapping)
        return concepts

class CodeNormalization(Variation):

    def __init__(self, coding_system_prefixes=['']):
        """`coding_systems` is None or a list of prefixes and this
        normalization is applied only if the list contains a prefix of the
        current coding systems."""
        self.coding_system_prefixes = coding_system_prefixes

    def vary_codes(self, generated, reference, coding_system):
        if any(coding_system.startswith(prefix)
               for prefix in self.coding_system_prefixes):
            return [self.normalize(c) for c in generated], \
                [self.normalize(c) for c in reference]
        else:
            return generated, reference

class NormalizeCodeCase(CodeNormalization):

    """Normalize code case in ICD, ICPC"""

    def __init__(self):
        super().__init__(['ICD', 'ICPC'])

    def normalize(self, code):
        return code.upper()

class NormalizeCodeSuffixDot(CodeNormalization):

    """Drop suffix dots in codes in ICPC"""

    def __init__(self):
        super().__init__(['ICPC'])

    def normalize(self, code):
        if code[-1] == '.':
            return code[:-1]
        else:
            return code

class NormalizeCodeXDD(CodeNormalization):

    """Normalize codes ICD and ICPC to 3 letters"""

    def __init__(self):
        super().__init__(['ICD', 'ICPC'])

    def normalize(self, code):
        code = code.upper()
        m1 = re.match(comap.RegexHierarchy.TYPES['LDD'], code)
        try:
            return m1.group('parent')
        except:
            m2 = re.match(comap.RegexHierarchy.TYPES['DDD'], code)
            try:
                return m2.group('parent')
            except:
                return code

class NormalizeRead2Codes(CodeNormalization):

    """Normalize READ2 codes to 5-byte"""

    read_re = re.compile(r'(?P<code>[A-Z\d][A-Za-z0-9.]{4}).*')

    def __init__(self):
        super().__init__(['RCD2'])

    def normalize(self, code):
        m = self.read_re.match(code)
        assert m, "Not a READ2 code: {}".format(code)
        return m.group('code')

class IncludeRelatedCodes(Variation):

    """Include related codes"""

    hierarchies = {
        'ICD9CM': comap.RegexHierarchy('DDD'),
        'ICD10CM': comap.RegexHierarchy('LDD'),
        'ICPC': comap.RegexHierarchy('LDD'),
        'RCD2': comap.Read2Hierarchy(),
    }

    def __init__(self, direction):
        """ direction : 'FN-FP' | 'FN' """
        self.direction = direction

    def description(self):
        return super().description() + ' ({})'.format(self.direction)

    def vary_codes(self, generated, reference, coding_system):
        hierarchy = self.hierarchies[coding_system]
        codes = comap.confusion_matrix(generated, reference)
        related_codes, derivated_codes = {
            'FN': comap.include_related,
            'FN-FP': comap.include_related_bidirect,
        }[self.direction](hierarchy, codes=codes)
        return comap.from_confusion_matrix(related_codes)

class IncludeChildConcepts(Variation):

    """Include child concepts"""

    def vary_concepts(self, concepts, outcome_id, reference_mapping):
        cuis = [c['cui'] for c in concepts]
        child_concepts_by_parent = child_concepts_by_outcome[outcome_id]
        child_concepts = [
            child_concept
            for parent_id, concepts_for_parent in child_concepts_by_parent
            for child_concept in concepts_for_parent
            if child_concept not in cuis
        ]
        return concepts + child_concepts

class IncludeRelatedConcepts(Variation):

    """Include related concepts"""

    def __init__(self, *relations):
        self.relations = list(relations)
        self.client = comap.ComapClient()

    def description(self):
        return super().description() + ' ({})'.format(', '.join(self.relations))

    def vary_concepts(self, concepts, outcome_id, reference_mapping):
        cuis = [c['cui'] for c in concepts]
        coding_systems = [databases[database] for database in reference_mapping.keys()]
        related = self.client.related(cuis, self.relations, coding_systems)
        concepts_and_related = {c['cui']: c for c in concepts}
        for cui in related.keys():
            for rel in related[cui].keys():
                for new_concept in related[cui][rel]:
                    new_cui = new_concept['cui']
                    codes = set((sourceConcept['id'], sourceConcept['codingSystem'])
                                for sourceConcept in new_concept['sourceConcepts'])
                    if new_cui not in concepts_and_related:
                        concepts_and_related[new_cui] = new_concept
        return list(concepts_and_related.values())

def create_results(references0, concepts_by_outcome, variations):

    mappings = {} # { outcome_id : { database: { codes?: { code }, comment?: str } } }
    for outcome_id in outcome_ids:
        mappings[outcome_id] = {}
        for database in databases.keys():
            mapping = references0[database]['mappings'][outcome_id]
            if 'inclusion' in mapping:
                codes = set(mapping['inclusion'] + mapping.get('exclusion', []))
            else:
                codes = None
            mappings[outcome_id][database] = OrderedDict([
                ('codes', codes)
            ])
            if 'comment' in mapping:
                mappings[outcome_id][database]['comment'] = mapping['comment']

    varied_concepts_by_outcome = {} # { variation_id: { outcome_id: { concept } } }
    for variation_id, variation in variations.items():
        varied_concepts_by_outcome[variation_id] = {}
        for outcome_id in outcome_ids:
            concepts = concepts_by_outcome[outcome_id]
            varied_concepts_by_outcome[variation_id][outcome_id] = \
                variation.vary_concepts(concepts, outcome_id, mappings[outcome_id])

    res = OrderedDict()
    for outcome_id in outcome_ids:
        res[outcome_id] = OrderedDict()
        for database, coding_system in databases.items():
            res[outcome_id][database] = OrderedDict([])
            mapping = mappings[outcome_id][database]
            if mapping['codes'] is not None:
                res[outcome_id][database]['variations'] = OrderedDict()
                for variation_id, variation in variations.items():
                    generated_codes = [
                        code['id']
                        for concept in varied_concepts_by_outcome[variation_id][outcome_id]
                        for code in concept['sourceConcepts']
                        if code['codingSystem'] == coding_system
                    ]
                    varied_generated_codes, varied_reference_codes = \
                        variation.vary_codes(generated_codes, mapping['codes'], coding_system)
                    varied_codes = comap.confusion_matrix(generated=varied_generated_codes,
                                                          reference=varied_reference_codes)
                    measures = comap.measures(codes=varied_codes)

                    res[outcome_id][database]['variations'][variation_id] = \
                        OrderedDict([
                            ('name', variation.description()),
                            ('comparison', OrderedDict([
                                ('codes', varied_codes),
                                ('measures', measures),
                            ])),
                    ])
            if 'comment' in mapping:
                res[outcome_id][database]['comment'] = mapping['comment']
    return res

default_variations = VariationChain(NormalizeCodeCase(), NormalizeCodeSuffixDot(), NormalizeRead2Codes())
variations = OrderedDict([
    ('baseline', default_variations),
    ('3-letter-codes', VariationChain(default_variations, NormalizeCodeXDD())),
    ('related-FN', VariationChain(default_variations, IncludeRelatedCodes('FN'))),
    ('related-FN-FP', VariationChain(default_variations, IncludeRelatedCodes('FN-FP'))),
    ('related-concepts-RN', VariationChain(default_variations, IncludeRelatedConcepts('RN'))),
    ('related-concepts-RN-RB', VariationChain(default_variations, IncludeRelatedConcepts('RN', 'RB'))),
    ('related-concepts-CHD', VariationChain(default_variations, IncludeRelatedConcepts('CHD'))),
    ('related-concepts-CHD-PAR', VariationChain(default_variations, IncludeRelatedConcepts('CHD', 'PAR'))),
    ('hyponyms', VariationChain(default_variations, IncludeChildConcepts())),
    ('child-concepts-related-FN-FP', VariationChain(default_variations, IncludeChildConcepts(), IncludeRelatedCodes('FN-FP'))),
])

evaluation = create_results(references, concepts_by_outcome, variations)

# Output results as YAML

with redo.output() as f:
    yaml.dump(evaluation, f)

if os.getenv('DOCTEST'):
    import doctest
    doctest.testmod()
    doctest.testmod(comap)

#!/usr/bin/env python3
from collections import OrderedDict
import os
import json
import yaml
import re
from pathlib import Path
import redo
redo.ifchange('comap.py'); import comap

project = redo.base
project_path = Path('projects') / project

with redo.ifchange(evaluation_config=project_path / 'evaluation-config.yaml') \
        as files:
    evaluation_config = yaml.load(files['evaluation_config'])

databases = evaluation_config['databases']
outcome_ids = evaluation_config['outcome_ids']
concept_filenames = {
    outcome_id: '{}.{}.concepts.json'.format(project, outcome_id)
    for outcome_id in outcome_ids
}
child_concept_filenames = {
    outcome_id: '{}.{}.child-concepts.json'.format(project, outcome_id)
    for outcome_id in outcome_ids
}
casedef_paths = {
    outcome_id: project_path / 'case-definitions' / (outcome_id + '.yaml')
    for outcome_id in outcome_ids
}

with redo.ifchange(coding_systems='config/coding_systems.yaml',
                   references=(project_path / 'reference.yaml').as_posix(),
                   concepts=concept_filenames,
                   child_concepts=child_concept_filenames,
                   casedefs=casedef_paths) as files:
    coding_systems = yaml.load(files['coding_systems'])
    references = yaml.load(files['references'])
    concepts_by_outcome = {}
    for outcome_id, f in files['concepts'].items():
        concepts_by_outcome[outcome_id] = json.load(f)
    child_concepts_by_outcome = {}
    for outcome_id, f in files['child_concepts'].items():
        child_concepts_by_outcome[outcome_id] = json.load(f).items()
    outcomes = {
        outcome_id: yaml.load(f)
        for outcome_id, f in files['casedefs'].items()
    }

class AbstractVariation(object):

    def description(self):
        return self.__doc__

    def set_context(self, outcome_id, coding_system):
        self.outcome_id = outcome_id
        self.coding_system = coding_system

    def vary_codes(self, generated, reference):
        return generated, reference

    def vary_concepts(self, concepts, reference):
        return concepts

class VariationChain(AbstractVariation):

    """Chain of variations that are applied to the input left-to-right."""

    def __init__(self, *variations):
        self.variations = variations

    def set_context(self, *args, **kwargs):
        for variation in self.variations:
            variation.set_context(*args, **kwargs)

    def description(self):
        res = '; '.join(v.description() for v in self.variations)
        if res:
            return res[0].upper() + res[1:]
        else:
            return ""

    def vary_codes(self, generated, reference):
        for variation in self.variations:
            generated, reference = variation.vary_codes(generated, reference)
        return generated, reference

    def vary_concepts(self, concepts, reference):
        for variation in self.variations:
            concepts = variation.vary_concepts(concepts, reference)
        return concepts

class Identity(AbstractVariation):

    """Identity"""

    pass

class CodeNormalization(AbstractVariation):

    def __init__(self, coding_system_prefixes=['']):
        """`coding_systems` is None or a list of prefixes and this
        normalization is applied only if the list contains a prefix of the
        current coding systems."""
        self.coding_system_prefixes = coding_system_prefixes

    def vary_codes(self, generated, reference):
        if any(self.coding_system.startswith(prefix)
               for prefix in self.coding_system_prefixes):
            return [self.normalize(c) for c in generated], \
                [self.normalize(c) for c in reference]
        else:
            return generated, reference

class NormalizeCodeCase(CodeNormalization):

    """Normalize code case in ICD, ICPC"""

    def __init__(self):
        super().__init__(['ICD', 'ICPC'])

    def normalize(self, code):
        return code.upper()

class NormalizeCodeSuffixDot(CodeNormalization):

    """Drop suffix dots in codes in ICPC"""

    def __init__(self):
        super().__init__(['ICPC'])

    def normalize(self, code):
        if code[-1] == '.':
            return code[:-1]
        else:
            return code

class NormalizeCodeXDD(CodeNormalization):

    """Normalize codes ICD and ICPC to 3 letters"""

    def __init__(self):
        super().__init__(['ICD', 'ICPC'])

    def normalize(self, code):
        code = code.upper()
        m1 = re.match(comap.RegexHierarchy.TYPES['LDD'], code)
        try:
            return m1.group('parent')
        except:
            m2 = re.match(comap.RegexHierarchy.TYPES['DDD'], code)
            try:
                return m2.group('parent')
            except:
                return code

class NormalizeRead2Codes(CodeNormalization):

    """Normalize READ2 codes to 5-byte"""

    read_re = re.compile(r'(?P<code>[A-Z\d][A-Za-z0-9.]{4}).*')

    def __init__(self):
        super().__init__(['RCD2'])

    def normalize(self, code):
        m = self.read_re.match(code)
        assert m, "Not a READ2 code: {}".format(code)
        return m.group('code')

class IncludeRelatedCodes(AbstractVariation):

    """Include related codes"""

    hierarchies = {
        'ICD9CM': comap.RegexHierarchy('DDD'),
        'ICD10CM': comap.RegexHierarchy('LDD'),
        'ICPC': comap.RegexHierarchy('LDD'),
        'RCD2': comap.Read2Hierarchy(),
    }

    def __init__(self, direction):
        """ direction : 'FN-FP' | 'FN' """
        self.direction = direction

    def description(self):
        return super().description() + ' ({})'.format(self.direction)

    def vary_codes(self, generated, reference):
        hierarchy = self.hierarchies[self.coding_system]
        codes = comap.confusion_matrix(generated, reference)
        related_codes, derivated_codes = {
            'FN': comap.include_related,
            'FN-FP': comap.include_related_bidirect,
        }[self.direction](hierarchy, codes=codes)
        return comap.from_confusion_matrix(related_codes)

class IncludeChildConcepts(AbstractVariation):

    """Include child concepts"""

    def vary_concepts(self, concepts, reference):
        cuis = [c['cui'] for c in concepts]
        child_concepts_by_parent = child_concepts_by_outcome[self.outcome_id]
        child_concepts = [
            child_concept
            for parent_id, concepts_for_parent in child_concepts_by_parent
            for child_concept in concepts_for_parent
            if child_concept not in cuis
        ]
        return concepts + child_concepts

def create_results(references, concepts_by_outcome, variations):

    res = OrderedDict()
    for outcome_id in outcome_ids:
        res[outcome_id] = OrderedDict()
        for database, coding_system in databases:
            mapping = references[database]['mappings'][outcome_id]
            res[outcome_id][database] = OrderedDict([])

            if 'inclusion' in mapping:
                reference = mapping['inclusion'] + mapping.get('exclusion', [])
            else:
                print("No mapping for {} in {}".format(outcome_id, database))
                reference = None

            if reference is not None:
                res[outcome_id][database]['variations'] = OrderedDict()
                for variation_id, variation in variations.items():
                    variation.set_context(outcome_id, coding_system)
                    generated_concepts = variation.vary_concepts(
                        concepts_by_outcome[outcome_id], reference)
                    generated = [
                        code['id']
                        for concept in generated_concepts
                        for code in concept['sourceConcepts']
                        if code['codingSystem'] == coding_system
                    ]
                    varied_generated, varied_reference = \
                        variation.vary_codes(generated, reference)
                    codes = comap.confusion_matrix(generated=varied_generated,
                                                  reference=varied_reference)
                    measures = comap.measures(codes=codes)
                    for key, values in codes.items():
                        try:
                            sorted(values)
                        except:
                            print([v for v in values if type(v) != str])
                            raise
                    comparison = OrderedDict([
                        ('codes', OrderedDict([
                            (key, sorted(value))
                            for key, value in codes.items()
                        ])),
                        ('measures', OrderedDict([
                            (key, value) # and round(value, 2))
                            for key, value in measures.items()
                        ])),
                    ])
                    res[outcome_id][database]['variations'][variation_id] = OrderedDict([
                        ('name', variation.description()),
                        ('comparison', comparison),
                    ])

            if 'comment' in mapping:
                res[outcome_id][database]['comment'] = mapping['comment']
    return res

default_variations = VariationChain(NormalizeCodeCase(), NormalizeCodeSuffixDot(), NormalizeRead2Codes())
variations = OrderedDict([
    ('baseline', default_variations),
    ('3-letter-codes', VariationChain(default_variations, NormalizeCodeXDD())),
    ('related-FN', VariationChain(default_variations, IncludeRelatedCodes('FN'))),
    ('related-FN-FP', VariationChain(default_variations, IncludeRelatedCodes('FN-FP'))),
    ('child-concepts', VariationChain(default_variations, IncludeChildConcepts())),
    ('child-concepts-related-FN-FP', VariationChain(default_variations, IncludeChildConcepts(), IncludeRelatedCodes('FN-FP'))),
])

evaluation = create_results(references, concepts_by_outcome, variations)

# Output results as YAML

with redo.output() as f:
    yaml.dump(evaluation, f)

if os.getenv('DOCTEST'):
    import doctest
    doctest.testmod()
    doctest.testmod(comap)


fix axes

fix axes

program meanline

use ${stash}master, clear

local var `1'
local varlabel `2'
local isb `3'

local time year

if "`var'" == "traf"{
    drop if year < 2008
}

drop if isbaseball != `isb'

collapse (mean) mean=`var' (semean) se=`var' , by(`time' treat)

if "`var'" == "img" {
    local scale "yscale(range(-0.3 2.2)) ylabel(0 (0.4) 2)"
}

if "`var'" == "text" {
    local scale "yscale(range(0 20000)) ylabel(0 (5000) 20000)"
}

if "`var'" == "bd" {
    local scale "yscale(range(0 0.9)) ylabel(0 (0.2) 0.8)"
}

sort `time' treat
label variable mean "`varlabel'"

gen lo = mean - 1.96*se
gen hi = mean + 1.96*se

tw (connected mean `time' if treat == 0, symbol(X)) (connected mean `time' if treat == 1, msize(small)) (rcap lo hi  `time' if treat == 0, lcolor(gs10)) (rcap lo hi  `time' if treat == 1,lcolor(gs10)), legend(order(2 "Out-of-Copyright" 1 "In-Copyright" )) xtitle("Year") title("`varlabel'") xline(2008.5) `scale'

graph export ${tables}meanline_`var'_`isb'.eps, replace
shell epstopdf ${tables}meanline_`var'_`isb'.eps

end

program meanline

use ${stash}master, clear

local var `1'
local varlabel `2'
local isb `3'

local time year

if "`var'" == "traf"{
    drop if year < 2008
}

drop if isbaseball != `isb'

collapse (mean) mean=`var' (semean) se=`var' , by(`time' treat)

sort `time' treat
label variable mean "`varlabel'"

gen lo = mean - 1.96*se
gen hi = mean + 1.96*se

**tw (connected mean `time' if treat == 0, symbol(X) ) (connected mean `time' if treat == 1, msize(small)), legend(order(2 "Before 1964" 1 "After 1964" )) xtitle("Year") title("`varlabel'") xline(2008.5)

tw (connected mean `time' if treat == 0, symbol(X)) (connected mean `time' if treat == 1, msize(small)) (rcap lo hi  `time' if treat == 0, lcolor(gs10)) (rcap lo hi  `time' if treat == 1,lcolor(gs10)), legend(order(2 "Before 1964" 1 "After 1964" )) xtitle("Year") title("`varlabel'") xline(2008.5)

graph export ${tables}meanline_`var'_`isb'.eps, replace
shell epstopdf ${tables}meanline_`var'_`isb'.eps

end


ieddtab help : edit acknowledgement section

{smcl}
{* 26 Apr 2018}{...}
{hline}
help for {hi:ieddtab}
{hline}

{title:Title}

{phang2}{cmdab:ieddtab} {hline 2} This command runs a Diff-in-Diff regression and displays the baseline values, the two 1st differences and the 2nd difference.

{phang2}For a more descriptive discussion on the intended usage and work flow of this
command please see the {browse "https://dimewiki.worldbank.org/wiki/ieddtab":DIME Wiki}.

{title:Syntax}

{phang2} {cmdab:ieddtab} {it:varlist},
	{cmdab:t(}{it:varname}{cmd:)} {cmdab:tmt(}{it:varname}{cmd:)}
	[
		{cmdab:covar:iates(}{it:varlist}{cmd:)} {cmdab:starl:evels(}{it:numlist}{cmd:)}
		{cmdab:err:ortype(}{it:string}{cmd:)} {cmdab:rowl:abtype(}{it:string}{cmd:)}
		{cmdab:rowlabtext(}{it:label_string}{cmd:)} {cmdab:dif:ormat(}{it:{help format:%fmt}}{cmd:)}
		{cmdab:replace} {cmdab:savet:ex(}{it:filepath}{cmd:)} {cmdab:onerow}
		{cmdab:tblnon:ote} {cmdab:tbln:ote(}{it:string}{cmd:)}  {cmdab:texdoc:ument}
		{cmdab:texc:aption(}{it:string}{cmd:)} {cmdab:texl:abel(}{it:string}{cmd:)}
		{cmdab:texn:otewidth(}{it:numlist}{cmd:)} {cmdab:texvspace(}{it:string}{cmd:)}
	]

{marker opts}{...}
{synoptset 24}{...}
{synopthdr:options}
{synoptline}
{pstd}{it:Required options:}{p_end}
{synopt :{cmdab:t(}{it:varname}{cmd:)}}Time dummy to use in diff-in-diff regression{p_end}
{synopt :{cmdab:tmt(}{it:varname}{cmd:)}}Treatment dummy to use in diff-in-diff regression{p_end}

{pstd}{it:Statistics options:}{p_end}
{synopt :{cmdab:covar:iates(}{it:varlist}{cmd:)}}Covariates to use in diff-in-diff regression{p_end}
{synopt :{cmdab:starl:evels(}{it:numlist}{cmd:)}}Significance levels used for significance stars, default values are .1, .05 and .01{p_end}
{synopt :{cmdab:err:ortype(}{it:string}{cmd:)}}Type of errors to display, default is standard errors.{p_end}

{pstd}{it:Output options:}{p_end}
{synopt :{cmdab:rowl:abtype(}{it:string}{cmd:)}}Indicate what to use as row titles, default is variable name.{p_end}
{synopt :{cmdab:rowlabtext(}{it:label_string}{cmd:)}}Manually enter the row titles using label strings (see below).{p_end}
{synopt :{cmdab:tblnon:ote}}Disable that the automatically generated note is displayed below the table.{p_end}
{synopt :{cmdab:tbln:ote(}{it:string}{cmd:)}}Manually add a note to be displayed below the regression result table.{p_end}
{synopt :{cmdab:onerow}}Display the number of observations on one row at the last row of the table.{p_end}
{synopt :{cmdab:dif:ormat(}{it:{help format:%fmt}}{cmd:)}}Set the rounding format of the calculated statistics in the table.{p_end}
{synopt :{cmdab:replace}}Replace the file on disk if it already exist. Has no effect if no option with file path is used.{p_end}

{pstd}{it:LaTeX options:}{p_end}
{synopt :{cmdab:savet:ex(}{it:filepath}{cmd:)}}Generate a LaTeX table of the result and save to the location of the file path.{p_end}
{synopt :{cmdab:texdoc:ument}}Creates a stand-alone TeX document.{p_end}
{synopt :{cmdab:texc:aption(}{it:string}{cmd:)}}Specify table's caption on TeX file.{p_end}
{synopt :{cmdab:texl:abel(}{it:string}{cmd:)}}Specify table's label, used for meta-reference across TeX file.{p_end}
{synopt :{cmdab:texn:otewidth(}{it:numlist}{cmd:)}}Manually entre the width of the note on the TeX file.{p_end}
{synopt :{cmd:texvspace(}{it:string}{cmd:)}}Manually set size of the line space between two rows on TeX output.{p_end}
{synoptline}

{marker desc}
{title:Description}

{pstd}{cmdab:commandname} is a command that makes it easy to run and display results of Difference-in Difference (diff-in-diff) regressions. The table that presents the results from the diff-in-diff regression also presents the mean when the variable in {inp:t()} is 0 (i.e. baseline) for the two groups defined by the variable {inp:tmt()} is 0 and 1 (i.e. control and treatment), and the table also presents the coefficient of the first difference regression in control and treatment.{p_end}

{pstd}The sample for each row in the table is defined by the sample included in the second difffernce regression shown below, where {it:outcome_var} is a variable the varlist (one per row) for {inp:ieddtab}, {inp:`t'#`tmt'} means the dummy listed in {inp:t()}, the dummy listed in {inp:tmt()} and the interaction of the two, and where {inp:`covariates'} is the list of covariates included in {inp:covariates()} if any. This means that any observation that has any missing value in either of the two dummies or in any of the covariates will be omitted from all statistics presented in the table. The coefficient presented in the table for the diff-in-diff regression is the interaction of the time and treatment variable.{p_end}

{pstd}{inp:regress} {it:outcome_var} {inp:`t'#`tmt' `covariates'}{p_end}

{pstd}The baseline means are then calculated using the following code where the first line is control and the second line is treatment, and the variable {inp:regsample} is dummy indicating if the observation was included in the second difference regression.{p_end}

{pstd}{inp:mean} {it:outcome_var} {inp:if `tmt' == 0 & `t' == 0 & regsample == 1}{p_end}
{pstd}{inp:mean} {it:outcome_var} {inp:if `tmt' == 1 & `t' == 0 & regsample == 1}{p_end}

{pstd}The first difference coefficients are then calculated using the following code where the first line is control and the second line is treatment. The coefficient displayed in the table is the coefficient of the variable `t' which is the variable listed in {inp:t()}.{p_end}

{pstd}{inp:regress} {it:outcome_var} {inp: `t' `covariates' if `tmt' == 0 & regsample == 1}{p_end}
{pstd}{inp:regress} {it:outcome_var} {inp: `t' `covariates' if `tmt' == 1 & regsample == 1}{p_end}

{marker optslong}
{title:Options}

{pstd}{it:{ul:{hi:Required options:}}}{p_end}
{phang}{cmdab:t(}{it:varname}{cmd:)} indicates which variable should be used as the time dummy to use in diff-in-diff regression. This must be a dummy variable, i.e. only have 0, 1 or missing as values, where 0 is baseline and 1 is follow-up.{p_end}

{phang}{cmdab:tmt(}{it:varname}{cmd:)} indicates which variable should be used as the treatment dummy to use in diff-in-diff regression. This must be a dummy variable, i.e. only have 0, 1 or missing as values.{p_end}

{pstd}{it:{ul:{hi:Statistics options:}}}{p_end}
{phang}{cmdab:covar:iates(}{it:varlist}{cmd:)} lists the variables that should be included as covariates (independent variables not reported in the table) in the two first difference regressions and the second diffrence regression. Unless the option {cmdab:tblnon:ote} is used a list of covariate variables is included below the table.{p_end}

{phang}{cmdab:starl:evels(}{it:numlist}{cmd:)} sets the significance levels used for significance stars. Exactly three values must be listed if this option is used, all three values must be descending order, and must be between 0 and 1. The default values are .1, .05 and .01{p_end}

{phang}{cmdab:err:ortype(}{it:string}{cmd:)} sets the type of error to display. Allowed values for this iption is {inp:se} for standard errors, {inp:sd} for standard deviation and {inp:errhide} for not displaying any errors in the table. The default is to display standard errors.{p_end}

{pstd}{it:{ul:{hi:Output options:}}}{p_end}
{phang}{cmdab:rowl:abtype(}{it:string}{cmd:)} indicates what to use as row titles. The allowed values are {inp:varname} using the variable name as row titles, {inp:varlab} using the variable labels as row titles (varname will still be used if the variable does not have a variable label). The default is to use the variable name.{p_end}

{phang}{cmdab:rowlabtext(}{it:label_string}{cmd:)} manually specifies the row titles using label strings. A label string is a list of variable names followed by the row title for that variable separated by "@@". For example {it:varA Row title variable A @@ varB Row title variable B}, where {it:varA} and {it:varB} are outcome variables used in this command. For variable not listed in {cmdab:rowlabtext()} row titles will be determined by the input value or default value in {cmdab:rowl:abtype()}.{p_end}

{phang}{cmdab:tblnon:ote} disable that the command automatically generates and displays a note below the table describing the output in the table. The note includes description on how number of calculations are calculated, the significance levels used for stars and which covariates were uses if any were used.{p_end}

{phang}{cmdab:tbln:ote(}{it:string}{cmd:)} is used to manually add a note to be displayed below the regression result table. This note is put before the automatically generated note, unless {cmdab:tblnon:ote} is displayed when only the manually added note is displayed.{p_end}

{phang}{cmdab:onerow} indicated that the number of observations should be displayed on one row at the last row of the table instead on each row. This requires that the number of observations are the same across all rows for each column.{p_end}

{phang}{cmdab:dif:ormat(}{it:{help format:%fmt}}{cmd:)} sets the number formatting/rounding rule for all calculated statistics in the table, that is all numbers in the table apart from the number of observations. Only valid {help format:Stata number formats} are allowed. The default is {it:%9.2f}.{p_end}

{phang}{cmdab:replace} if an option is used that output a file and a file with that name already exists at that location, then Stata will throw an error unless this option is used. If this option is used then Stata overwrites the file on disk with the new output. This option has no effect if no option with file path is used.{p_end}

{pstd}{it:{ul:{hi:LaTeX options:}}}{p_end}
{phang}{cmdab:savet:ex(}{it:filepath}{cmd:)} saves the table in TeX format to the location of the file path.{p_end}

{phang}{cmdab:texdoc:ument} creates a stand-alone TeX document that can be readily compiled, without the need to import it to a different file.
 As default, {cmd:savetex()} creates a fragmented TeX file consisting only of a tabular environment.

{phang}{cmdab:texc:aption(}{it:string}{cmd:)} writes table's caption in TeX file. Can only be used with option texdocument. {p_end}

{phang}{cmdab:texl:abel(}{it:string}{cmd:)} specifies table's label, used for meta-reference across TeX file. Can only be used with option texdocument.{p_end}

{phang}{cmdab:texn:otewidth(}{it:numlist}{cmd:)} manually adjusts the width of the note to fit the size of the table.
The note width is a multiple of text width. If not specified, default is one, which makes the table width equal to text width.{p_end}

{phang}{cmd:texvspace(}{it:string}{cmd:)} sets the size of the line space between two variable rows. {it:string} must consist of a numeric value
and one of the following units: "cm", "mm", "pt", "in", "ex" or "em". Note that the resulting line space displayed will be equal to the
specified value minus the height of one line of text. Default is "3ex". For more information on units,
{browse "https://en.wikibooks.org/wiki/LaTeX/Lengths":check LaTeX lengths manual}. {p_end}

{marker optslong}
{title:Examples}

{pstd}All the examples below can be run on the Stata's built in census data set, by first running this code:{p_end}

{pstd}*Open the built in data set{p_end}
{pstd}{inp:sysuse census}{p_end}

{pstd}*Calculate rates from absolute numbers{p_end}
{pstd}{inp:replace death = 100 * death / pop}{p_end}
{pstd}{inp:replace marriage = 100 * marriage / pop}{p_end}
{pstd}{inp:replace divorce = 100 * divorce / pop}{p_end}

{pstd}*Randomly assign time and treatment dummies{p_end}
{pstd}{inp:gen t	= (runiform()<.5)}{p_end}
{pstd}{inp:gen tmt = (runiform()<.5)}{p_end}

{pstd} {hi:Example 1.}

{pstd} {inp:ieddtab} {it:death marriage divorce} , {inp:t(}{it:t}{inp:)} {inp:tmt(}{it:tmt}{inp:)}

{pstd}This is the most basic way to run this command with three variables. This will output a table with the baseline means for tmt = 0 and tmt = 1, the first difference regression coefficient for tmt = 0 and tmt = 1 as well as the 2nd difference regression coefficient for tmt = 0 and tmt = 1.

{pstd} {hi:Example 2.}

{pstd} {inp:ieddtab} {it:death marriage divorce} ,  {inp:t(}{it:t}{inp:)} {inp:tmt(}{it:tmt}{inp:)}  {inp:rowlabtext(}{it:"death Death Rate @@ divorce Divorce Rate"}{inp:)} {inp:rowlabtype(}{it:"varlab"}{inp:)}

{pstd}The table generated by example 2 will have the same statistics as in example 1 but the row title for the variables death and divorce are entered manually and the row title for marriage will be its variable label instead of its variable name.

{pstd} {hi:Example 3.}

{pstd} {inp:ieddtab} {it:death marriage divorce} ,  {inp:t(}{it:t}{inp:)} {inp:tmt(}{it:tmt}{inp:)}  {inp:rowlabtype(}{it:"varlab"}{inp:)} {inp:savetex(}{it:"DID table.tex"}{inp:)} {inp:replace}

{pstd}The table will be saved in the current directory under the name "DID table.tex". It will will have the same statistics as in examples 1 and 2, and the row titles will be its variable labels.

{title:Acknowledgements}

{phang}This command was initally suggested by Esteban J. Quinones{p_end}

{phang}We would like to acknowledge the help in testing and proofreading we received in relation to this command and help file from (in alphabetic order):{p_end}
{pmore}Benjamin Daniel{break}Jonas Guthoff{break}Sakina Shibuya{break}

{title:Author}

{phang}All commands in ietoolkit is developed by DIME Analytics at DECIE, The World Bank's unit for Development Impact Evaluations.{p_end}

{phang}Main author: Kristoffer Bjarkefur, Luiza Cardoso De Andrade, DIME Analytics, The World Bank Group{p_end}

{phang}Please send bug-reports, suggestions and requests for clarifications
		 writing "ietoolkit ieddtab" in the subject line to:{break}
		 dimeanalytics@worldbank.org{p_end}

{phang}You can also see the code, make comments to the code, see the version
		 history of the code, and submit additions or edits to the code through
		 the github repository of ietoolkit:{break}
{browse "https://github.com/worldbank/ietoolkit"}{p_end}

{smcl}
{* 26 Apr 2018}{...}
{hline}
help for {hi:ieddtab}
{hline}

{title:Title}

{phang2}{cmdab:ieddtab} {hline 2} This command runs a Diff-in-Diff regression and displays the baseline values, the two 1st differences and the 2nd difference.

{phang2}For a more descriptive discussion on the intended usage and work flow of this
command please see the {browse "https://dimewiki.worldbank.org/wiki/ieddtab":DIME Wiki}.

{title:Syntax}

{phang2} {cmdab:ieddtab} {it:varlist},
	{cmdab:t(}{it:varname}{cmd:)} {cmdab:tmt(}{it:varname}{cmd:)}
	[
		{cmdab:covar:iates(}{it:varlist}{cmd:)} {cmdab:starl:evels(}{it:numlist}{cmd:)}
		{cmdab:err:ortype(}{it:string}{cmd:)} {cmdab:rowl:abtype(}{it:string}{cmd:)}
		{cmdab:rowlabtext(}{it:label_string}{cmd:)} {cmdab:dif:ormat(}{it:{help format:%fmt}}{cmd:)}
		{cmdab:replace} {cmdab:savet:ex(}{it:filepath}{cmd:)} {cmdab:onerow}
		{cmdab:tblnon:ote} {cmdab:tbln:ote(}{it:string}{cmd:)}  {cmdab:texdoc:ument}
		{cmdab:texc:aption(}{it:string}{cmd:)} {cmdab:texl:abel(}{it:string}{cmd:)}
		{cmdab:texn:otewidth(}{it:numlist}{cmd:)} {cmdab:texvspace(}{it:string}{cmd:)}
	]

{marker opts}{...}
{synoptset 24}{...}
{synopthdr:options}
{synoptline}
{pstd}{it:Required options:}{p_end}
{synopt :{cmdab:t(}{it:varname}{cmd:)}}Time dummy to use in diff-in-diff regression{p_end}
{synopt :{cmdab:tmt(}{it:varname}{cmd:)}}Treatment dummy to use in diff-in-diff regression{p_end}

{pstd}{it:Statistics options:}{p_end}
{synopt :{cmdab:covar:iates(}{it:varlist}{cmd:)}}Covariates to use in diff-in-diff regression{p_end}
{synopt :{cmdab:starl:evels(}{it:numlist}{cmd:)}}Significance levels used for significance stars, default values are .1, .05 and .01{p_end}
{synopt :{cmdab:err:ortype(}{it:string}{cmd:)}}Type of errors to display, default is standard errors.{p_end}

{pstd}{it:Output options:}{p_end}
{synopt :{cmdab:rowl:abtype(}{it:string}{cmd:)}}Indicate what to use as row titles, default is variable name.{p_end}
{synopt :{cmdab:rowlabtext(}{it:label_string}{cmd:)}}Manually enter the row titles using label strings (see below).{p_end}
{synopt :{cmdab:tblnon:ote}}Disable that the automatically generated note is displayed below the table.{p_end}
{synopt :{cmdab:tbln:ote(}{it:string}{cmd:)}}Manually add a note to be displayed below the regression result table.{p_end}
{synopt :{cmdab:onerow}}Display the number of observations on one row at the last row of the table.{p_end}
{synopt :{cmdab:dif:ormat(}{it:{help format:%fmt}}{cmd:)}}Set the rounding format of the calculated statistics in the table.{p_end}
{synopt :{cmdab:replace}}Replace the file on disk if it already exist. Has no effect if no option with file path is used.{p_end}

{pstd}{it:LaTeX options:}{p_end}
{synopt :{cmdab:savet:ex(}{it:filepath}{cmd:)}}Generate a LaTeX table of the result and save to the location of the file path.{p_end}
{synopt :{cmdab:texdoc:ument}}Creates a stand-alone TeX document.{p_end}
{synopt :{cmdab:texc:aption(}{it:string}{cmd:)}}Specify table's caption on TeX file.{p_end}
{synopt :{cmdab:texl:abel(}{it:string}{cmd:)}}Specify table's label, used for meta-reference across TeX file.{p_end}
{synopt :{cmdab:texn:otewidth(}{it:numlist}{cmd:)}}Manually entre the width of the note on the TeX file.{p_end}
{synopt :{cmd:texvspace(}{it:string}{cmd:)}}Manually set size of the line space between two rows on TeX output.{p_end}
{synoptline}

{marker desc}
{title:Description}

{pstd}{cmdab:commandname} is a command that makes it easy to run and display results of Difference-in Difference (diff-in-diff) regressions. The table that presents the results from the diff-in-diff regression also presents the mean when the variable in {inp:t()} is 0 (i.e. baseline) for the two groups defined by the variable {inp:tmt()} is 0 and 1 (i.e. control and treatment), and the table also presents the coefficient of the first difference regression in control and treatment.{p_end}

{pstd}The sample for each row in the table is defined by the sample included in the second difffernce regression shown below, where {it:outcome_var} is a variable the varlist (one per row) for {inp:ieddtab}, {inp:`t'#`tmt'} means the dummy listed in {inp:t()}, the dummy listed in {inp:tmt()} and the interaction of the two, and where {inp:`covariates'} is the list of covariates included in {inp:covariates()} if any. This means that any observation that has any missing value in either of the two dummies or in any of the covariates will be omitted from all statistics presented in the table. The coefficient presented in the table for the diff-in-diff regression is the interaction of the time and treatment variable.{p_end}

{pstd}{inp:regress} {it:outcome_var} {inp:`t'#`tmt' `covariates'}{p_end}

{pstd}The baseline means are then calculated using the following code where the first line is control and the second line is treatment, and the variable {inp:regsample} is dummy indicating if the observation was included in the second difference regression.{p_end}

{pstd}{inp:mean} {it:outcome_var} {inp:if `tmt' == 0 & `t' == 0 & regsample == 1}{p_end}
{pstd}{inp:mean} {it:outcome_var} {inp:if `tmt' == 1 & `t' == 0 & regsample == 1}{p_end}

{pstd}The first difference coefficients are then calculated using the following code where the first line is control and the second line is treatment. The coefficient displayed in the table is the coefficient of the variable `t' which is the variable listed in {inp:t()}.{p_end}

{pstd}{inp:regress} {it:outcome_var} {inp: `t' `covariates' if `tmt' == 0 & regsample == 1}{p_end}
{pstd}{inp:regress} {it:outcome_var} {inp: `t' `covariates' if `tmt' == 1 & regsample == 1}{p_end}

{marker optslong}
{title:Options}

{pstd}{it:{ul:{hi:Required options:}}}{p_end}
{phang}{cmdab:t(}{it:varname}{cmd:)} indicates which variable should be used as the time dummy to use in diff-in-diff regression. This must be a dummy variable, i.e. only have 0, 1 or missing as values, where 0 is baseline and 1 is follow-up.{p_end}

{phang}{cmdab:tmt(}{it:varname}{cmd:)} indicates which variable should be used as the treatment dummy to use in diff-in-diff regression. This must be a dummy variable, i.e. only have 0, 1 or missing as values.{p_end}

{pstd}{it:{ul:{hi:Statistics options:}}}{p_end}
{phang}{cmdab:covar:iates(}{it:varlist}{cmd:)} lists the variables that should be included as covariates (independent variables not reported in the table) in the two first difference regressions and the second diffrence regression. Unless the option {cmdab:tblnon:ote} is used a list of covariate variables is included below the table.{p_end}

{phang}{cmdab:starl:evels(}{it:numlist}{cmd:)} sets the significance levels used for significance stars. Exactly three values must be listed if this option is used, all three values must be descending order, and must be between 0 and 1. The default values are .1, .05 and .01{p_end}

{phang}{cmdab:err:ortype(}{it:string}{cmd:)} sets the type of error to display. Allowed values for this iption is {inp:se} for standard errors, {inp:sd} for standard deviation and {inp:errhide} for not displaying any errors in the table. The default is to display standard errors.{p_end}

{pstd}{it:{ul:{hi:Output options:}}}{p_end}
{phang}{cmdab:rowl:abtype(}{it:string}{cmd:)} indicates what to use as row titles. The allowed values are {inp:varname} using the variable name as row titles, {inp:varlab} using the variable labels as row titles (varname will still be used if the variable does not have a variable label). The default is to use the variable name.{p_end}

{phang}{cmdab:rowlabtext(}{it:label_string}{cmd:)} manually specifies the row titles using label strings. A label string is a list of variable names followed by the row title for that variable separated by "@@". For example {it:varA Row title variable A @@ varB Row title variable B}, where {it:varA} and {it:varB} are outcome variables used in this command. For variable not listed in {cmdab:rowlabtext()} row titles will be determined by the input value or default value in {cmdab:rowl:abtype()}.{p_end}

{phang}{cmdab:tblnon:ote} disable that the command automatically generates and displays a note below the table describing the output in the table. The note includes description on how number of calculations are calculated, the significance levels used for stars and which covariates were uses if any were used.{p_end}

{phang}{cmdab:tbln:ote(}{it:string}{cmd:)} is used to manually add a note to be displayed below the regression result table. This note is put before the automatically generated note, unless {cmdab:tblnon:ote} is displayed when only the manually added note is displayed.{p_end}

{phang}{cmdab:onerow} indicated that the number of observations should be displayed on one row at the last row of the table instead on each row. This requires that the number of observations are the same across all rows for each column.{p_end}

{phang}{cmdab:dif:ormat(}{it:{help format:%fmt}}{cmd:)} sets the number formatting/rounding rule for all calculated statistics in the table, that is all numbers in the table apart from the number of observations. Only valid {help format:Stata number formats} are allowed. The default is {it:%9.2f}.{p_end}

{phang}{cmdab:replace} if an option is used that output a file and a file with that name already exists at that location, then Stata will throw an error unless this option is used. If this option is used then Stata overwrites the file on disk with the new output. This option has no effect if no option with file path is used.{p_end}

{pstd}{it:{ul:{hi:LaTeX options:}}}{p_end}
{phang}{cmdab:savet:ex(}{it:filepath}{cmd:)} saves the table in TeX format to the location of the file path.{p_end}

{phang}{cmdab:texdoc:ument} creates a stand-alone TeX document that can be readily compiled, without the need to import it to a different file.
 As default, {cmd:savetex()} creates a fragmented TeX file consisting only of a tabular environment.

{phang}{cmdab:texc:aption(}{it:string}{cmd:)} writes table's caption in TeX file. Can only be used with option texdocument. {p_end}

{phang}{cmdab:texl:abel(}{it:string}{cmd:)} specifies table's label, used for meta-reference across TeX file. Can only be used with option texdocument.{p_end}

{phang}{cmdab:texn:otewidth(}{it:numlist}{cmd:)} manually adjusts the width of the note to fit the size of the table.
The note width is a multiple of text width. If not specified, default is one, which makes the table width equal to text width.{p_end}

{phang}{cmd:texvspace(}{it:string}{cmd:)} sets the size of the line space between two variable rows. {it:string} must consist of a numeric value
and one of the following units: "cm", "mm", "pt", "in", "ex" or "em". Note that the resulting line space displayed will be equal to the
specified value minus the height of one line of text. Default is "3ex". For more information on units,
{browse "https://en.wikibooks.org/wiki/LaTeX/Lengths":check LaTeX lengths manual}. {p_end}

{marker optslong}
{title:Examples}

{pstd}All the examples below can be run on the Stata's built in census data set, by first running this code:{p_end}

{pstd}*Open the built in data set{p_end}
{pstd}{inp:sysuse census}{p_end}

{pstd}*Calculate rates from absolute numbers{p_end}
{pstd}{inp:replace death = 100 * death / pop}{p_end}
{pstd}{inp:replace marriage = 100 * marriage / pop}{p_end}
{pstd}{inp:replace divorce = 100 * divorce / pop}{p_end}

{pstd}*Randomly assign time and treatment dummies{p_end}
{pstd}{inp:gen t	= (runiform()<.5)}{p_end}
{pstd}{inp:gen tmt = (runiform()<.5)}{p_end}

{pstd} {hi:Example 1.}

{pstd} {inp:ieddtab} {it:death marriage divorce} , {inp:t(}{it:t}{inp:)} {inp:tmt(}{it:tmt}{inp:)}

{pstd}This is the most basic way to run this command with three variables. This will output a table with the baseline means for tmt = 0 and tmt = 1, the first difference regression coefficient for tmt = 0 and tmt = 1 as well as the 2nd difference regression coefficient for tmt = 0 and tmt = 1.

{pstd} {hi:Example 2.}

{pstd} {inp:ieddtab} {it:death marriage divorce} ,  {inp:t(}{it:t}{inp:)} {inp:tmt(}{it:tmt}{inp:)}  {inp:rowlabtext(}{it:"death Death Rate @@ divorce Divorce Rate"}{inp:)} {inp:rowlabtype(}{it:"varlab"}{inp:)}

{pstd}The table generated by example 2 will have the same statistics as in example 1 but the row title for the variables death and divorce are entered manually and the row title for marriage will be its variable label instead of its variable name.

{pstd} {hi:Example 3.}

{pstd} {inp:ieddtab} {it:death marriage divorce} ,  {inp:t(}{it:t}{inp:)} {inp:tmt(}{it:tmt}{inp:)}  {inp:rowlabtype(}{it:"varlab"}{inp:)} {inp:savetex(}{it:"DID table.tex"}{inp:)} {inp:replace}

{pstd}The table will be saved in the current directory under the name "DID table.tex". It will will have the same statistics as in examples 1 and 2, and the row titles will be its variable labels.

{title:Acknowledgements}

{phang}We would like to acknowledge the help in testing and proofreading we received in relation to this command and help file from (in alphabetic order):{p_end}
{pmore}Esteban J. Quinones

{title:Author}

{phang}All commands in ietoolkit is developed by DIME Analytics at DECIE, The World Bank's unit for Development Impact Evaluations.{p_end}

{phang}Main author: Kristoffer Bjarkefur, Luiza Cardoso De Andrade, DIME Analytics, The World Bank Group{p_end}

{phang}Please send bug-reports, suggestions and requests for clarifications
		 writing "ietoolkit ieddtab" in the subject line to:{break}
		 dimeanalytics@worldbank.org{p_end}

{phang}You can also see the code, make comments to the code, see the version
		 history of the code, and submit additions or edits to the code through
		 the github repository of ietoolkit:{break}
{browse "https://github.com/worldbank/ietoolkit"}{p_end}