ieimpgraph : dummies not in memory

ieimpgraph : dummies not in memory

cap	program drop	ieimpgraph
	program define 	ieimpgraph

	syntax ganything

	mat list e(b)

	foreach var of local varlist {

		local beta = _b[`var']

		di "`beta'"

	}

end

cap	program drop	tobeused
	program define 	tobeused

	capture file close `textfile'_`date'
	file open  `textfile'_`date' using `textfile'_`date'.txt, text write replace
	file write `textfile'_`date' ///
		"Variable" 	_tab "Tmt-Status"	_tab "mean" _tab "coeff" _tab "conf_int_min" _tab "conf_int_max" _tab "obs" _tab "star" _n
	file close `textfile'_`date'

	local REG_RESULTS ""
	foreach depvar of local depvars {

		rename lag2_`depvar' lagvar

		local regname `depvar'
			eststo `regname' : areg `depvar' tmt_reg tmt_sdp tmt_inc lagvar NoCropBoroBL NoCropBL incorrect_no_bl NoPlotDataBL NoHarvValueDataBL if t == 2, a(d_code) cl(v_code)
		local  REG_RESULTS "`REG_RESULTS' `regname'"

		foreach tmt_arm in reg sdp inc {
			scalar coeff_`tmt_arm' 		= _b[tmt_`tmt_arm']
			scalar coeff_se_`tmt_arm' 	= _se[tmt_`tmt_arm']

			sum `depvar' if e(sample) & tmt_`tmt_arm' == 1
			scalar obs_`tmt_arm'	= r(N)
		}

		sum `depvar' if e(sample) & tmt_reg == 0 & tmt_sdp == 0 & tmt_inc == 0
		scalar ctl_N		= r(N)
		scalar ctl_mean	  	= r(mean)
		scalar ctl_mean_sd 	= r(sd)

		estadd scalar ctl_mean_table	=r(mean)
		estadd scalar ctl_N_table		=r(N)
		estadd scalar ctl_mean_sd_table	=r(sd)

		sum lagvar if e(sample) & !(NoCropBoroBL == 1 | NoCropBL == 1 | incorrect_no_bl == 1 | NoPlotDataBL == 1 | NoHarvValueDataBL == 1)
		scalar BL_mean 		= r(mean)
		scalar BL_N 	= r(N)

		estadd scalar BL_mean_table	=r(mean)
		estadd scalar BL_N_table	=r(N)

		rename lagvar lag2_`depvar'

		foreach tmt_arm in reg sdp inc {
			scalar  conf_int_min_`tmt_arm'   =coeff_`tmt_arm'-(1.96*coeff_se_`tmt_arm') + ctl_mean
			scalar  conf_int_max_`tmt_arm'   =coeff_`tmt_arm'+(1.96*coeff_se_`tmt_arm') + ctl_mean

			test    tmt_`tmt_arm'

			scalar  pvalue =r(p)
			local star_`tmt_arm'
			if pvalue < 0.10 {
				local star_`tmt_arm' "*"
			}
			if pvalue < 0.05 {
				local star_`tmt_arm' "**"
			}
			if pvalue < 0.01 {
				local star_`tmt_arm' "***"
			}

			scalar tmt_mean_`tmt_arm' = ctl_mean + coeff_`tmt_arm'
		}

		file open  `textfile'_`date' using `textfile'_`date'.txt, text write append
		file write `textfile'_`date' ///
			"`regname'" _tab "Control"  _tab %9.3f (ctl_mean)      _tab  		 			_tab 		 			  	  _tab 			 				_tab %9.3f (ctl_N) 	   _tab			  	  _n ///
			"`regname'" _tab "reg"		_tab %9.3f (tmt_mean_reg)  _tab  %9.3f (coeff_reg)  _tab %9.3f (conf_int_min_reg) _tab %9.3f (conf_int_max_reg) _tab %9.3f (obs_reg)   _tab "`star_reg'"  _n ///
			"`regname'" _tab "sdp"		_tab %9.3f (tmt_mean_sdp)  _tab  %9.3f (coeff_sdp)  _tab %9.3f (conf_int_min_sdp) _tab %9.3f (conf_int_max_sdp) _tab %9.3f (obs_sdp)   _tab "`star_sdp'"  _n ///
			"`regname'" _tab "inc"		_tab %9.3f (tmt_mean_inc)  _tab  %9.3f (coeff_inc)  _tab %9.3f (conf_int_min_inc) _tab %9.3f (conf_int_max_inc) _tab %9.3f (obs_inc)   _tab "`star_inc'"  _n
		file close `textfile'_`date'

	}
	esttab `REG_RESULTS' using "$csv/outcomeVar_AllTreat_`date'.csv",      ///
		title(`tabeleFileName') stats(BL_mean_table BL_N_table ctl_mean_table ctl_N_table ctl_mean_sd_table N) mtitles nonumbers plain br  star(* 0.10 ** 0.05 *** 0.01) ///
		b(a3)  se(%9.2f) alignment(l) replace

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

		Produce the graphs

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

	insheet using `textfile'_`date'.txt, clear tab

	pause on

	cd "$graphs/gRaw"

	gen tmt = .
	replace tmt = 1 if tmtstatus == "Control"
	replace tmt = 2 if tmtstatus == "reg"
	replace tmt = 3 if tmtstatus == "sdp"
	replace tmt = 4 if tmtstatus == "inc"

	gen placement = tmt

	*gen 	placement = tmt if demo_code == 1
	*replace placement = tmt + 5 if demo_code == 2

	foreach percentVar in mean coeff conf_int_min conf_int_max {

		replace `percentVar' =  `percentVar' * 100 if variable == "c1_commerce"
	}

	gen 	labelString 		= ""
	gen 	labelCategory		= .
	local 	continVars_Large  	c1_harv_value c1_net_yield c1_gross_yield c1_total_earnings c2_inp_total_spending c1_IAAP_harv_value c1_net_yield_nonLabor c1_net_yield_allLabor
	local 	continVars_Small  	c1_total_plotsize
	local 	percentage_Vars   	c1_commerce

	foreach L_var of local continVars_Large {

		replace labelCategory = 1 if variable == "`L_var'"
	}
	foreach S_var of local continVars_Small {

		replace labelCategory = 2 if variable == "`S_var'"
	}
	foreach P_var of local percentage_Vars {

		replace labelCategory = 3 if variable == "`P_var'"
	}

	tempvar labelString_temp decimal_postion

	tostring mean,	gen(`labelString_temp') force
	gen 	`decimal_postion' = strpos(`labelString_temp',".")

	*For continious values without decimal point (means include all in practice)
	replace `decimal_postion' = 10 if  `decimal_postion' == 0

	replace labelString = substr(`labelString_temp',1,(`decimal_postion'-1)) 		if labelCategory == 1
	replace labelString = substr(`labelString_temp',1,4) 							if labelCategory == 2
	replace labelString = substr(`labelString_temp',1,`decimal_postion'+1) + "%"	if labelCategory == 3

	tempfile restart_loop
	save 	`restart_loop', replace

	levelsof variable
	foreach level in `r(levels)' {
		use `restart_loop', clear
		di "`level'"
		keep if variable == "`level'"

		foreach tmt in reg sdp inc {
			preserve
				keep if tmtstatus == "`tmt'"
				local obs_`tmt'  = obs[1]
				local star_`tmt' = star[1]
			restore
		}

		preserve
			keep if tmtstatus == "Control"
			local obs_ctrl  = obs[1]
		restore

		local title "ERROR"

		if "`level'" == "c1_IAAP_harv_value"	local title "Harvest value IAPP crops, Taka"
		if "`level'" == "c1_commerce"			local title "Commercialization, earnings/production %"
		if "`level'" == "c1_gross_yield"		local title "Gross yield, Taka/Ha"
		if "`level'" == "c1_gross_yield_comp"	local title "Gross yield, plots with completed harvest, Taka/Ha"
		if "`level'" == "c1_gross_yield_harv" 	local title "Gross yield, actaully harvested all plots, Taka/Ha"
		if "`level'" == "c1_harv_value"			local title "Total value all harvest, Taka"
		if "`level'" == "c1_harv_value_int"		local title "Total value harvest intercropped, Taka"
		if "`level'" == "c1_harv_value_mono" 	local title "Total value harvest monocropped, Taka"
		if "`level'" == "c1_net_yield"			local title "Net yield, Taka/Ha"
		if "`level'" == "c1_total_earnings"		local title "Total earnings all crop sales, Taka"
		if "`level'" == "c1_total_plotsize"		local title "Total plotsize, Ha"
		if "`level'" == "c2_inp_total_spending"	local title "Total input spending, Taka"

		local noyscale ylabel(#3) ytitle("")
		*if "`title'" != "Boro" & "`title'" != "Lentil" & "`title'" != "Mustard" local noyscale ysc(off)

		if "`level'" == "c2_inp_total_spending"	local noyscale ylabel(#2) ytitle("")
		if "`level'" == "c1_gross_yield"		local noyscale ylabel(#2) ytitle("")

		local graphname	gph_`textfile'_`level'
		sort placement
		graph twoway (bar mean placement if tmt == 1, color("17 33 44")	 ) ///
			(bar mean placement if tmt == 2 , color("228 202 194") 		) ///
			(bar mean placement if tmt == 3 , color("215 107 96" ) 		 ) ///
			(bar mean placement if tmt == 4 , color("156 36  24" ) 		) ///
			(rcap conf_int_max conf_int_min placement, lc(gs) 						) ///
			(scatter  mean placement, msym(none) mlab(labelString) mlabs(medium) mlabpos(10) mlabcolor(black))	, ///
			legend(order(1 2 3 4) lab(1 "Control") lab(2 "Regular Treatment") lab(3 "Shared Demo Treatment") lab(4 "Incentives Treatment")) ///
			 plotregion(margin(b+3 t+3)) ///
			xlabel(	1 `" (N = `obs_ctrl') "' ///
					2 `" "(N = `obs_reg')" " `star_reg' " "' ///
					3 `" "(N = `obs_sdp')" " `star_sdp' " "' ///
					4 `" "(N = `obs_inc')" " `star_inc' " "', noticks labsize(medsmall)) ///
			graphregion( lcolor("182 222 255") fcolor("182 222 255")) ///
			plotregion(fcolor("247 247 247") ) ///
		   	xtitle("") ``level'_lab' `noyscale'  ///
			title("`title'") saving(`graphname'.gph, replace)
			graph export `graphname'.png, replace
		*pause
	}

	*Main outcomes
	cd "$graphs/gRaw"
	grc1leg gph_ag_outcome_log_c1_harv_value.gph 	///
			gph_ag_outcome_log_c1_net_yield.gph 	///
			gph_ag_outcome_log_c1_gross_yield.gph 	///
			gph_ag_outcome_log_c1_total_earnings.gph 	///
		,saving(gph_ag_outcome_log_MAIN.png, replace) /*ycom*/ ///
		cols(2) imargin(small) graphregion( lcolor("182 222 255") fcolor("182 222 255"))
	graph use gph_ag_outcome_log_MAIN.png
	graph display, xsize(8.5)

	cd "$graphs/"
	graph export gph_ag_outcome_log_MAIN.eps, preview(on) replace

	pause

	*Other ag outcomes
	cd "$graphs/gRaw"
	grc1leg gph_ag_outcome_log_c2_inp_total_spending.gph 	///
			gph_ag_outcome_log_c1_total_plotsize.gph 	///
			gph_ag_outcome_log_c1_IAAP_harv_value.gph 	///
			gph_ag_outcome_log_c1_commerce.gph 	///
		,saving(gph_ag_outcome_log_other.png, replace) /*ycom*/ ///
		cols(2) imargin(small) graphregion( lcolor("182 222 255") fcolor("182 222 255"))
	graph use gph_ag_outcome_log_other.png
	graph display, xsize(8.5)

		cd "$graphs/"
	graph export gph_ag_outcome_log_other.eps, preview(on) replace

	cap log close `logname'
end

cap	program drop	ieimpgraph
	program define 	ieimpgraph

	syntax varlist

	mat list e(b)

	foreach var of local varlist {

		local beta = _b[`var']

		di "`beta'"

	}

end

cap	program drop	tobeused
	program define 	tobeused

	capture file close `textfile'_`date'
	file open  `textfile'_`date' using `textfile'_`date'.txt, text write replace
	file write `textfile'_`date' ///
		"Variable" 	_tab "Tmt-Status"	_tab "mean" _tab "coeff" _tab "conf_int_min" _tab "conf_int_max" _tab "obs" _tab "star" _n
	file close `textfile'_`date'

	local REG_RESULTS ""
	foreach depvar of local depvars {

		rename lag2_`depvar' lagvar

		local regname `depvar'
			eststo `regname' : areg `depvar' tmt_reg tmt_sdp tmt_inc lagvar NoCropBoroBL NoCropBL incorrect_no_bl NoPlotDataBL NoHarvValueDataBL if t == 2, a(d_code) cl(v_code)
		local  REG_RESULTS "`REG_RESULTS' `regname'"

		foreach tmt_arm in reg sdp inc {
			scalar coeff_`tmt_arm' 		= _b[tmt_`tmt_arm']
			scalar coeff_se_`tmt_arm' 	= _se[tmt_`tmt_arm']

			sum `depvar' if e(sample) & tmt_`tmt_arm' == 1
			scalar obs_`tmt_arm'	= r(N)
		}

		sum `depvar' if e(sample) & tmt_reg == 0 & tmt_sdp == 0 & tmt_inc == 0
		scalar ctl_N		= r(N)
		scalar ctl_mean	  	= r(mean)
		scalar ctl_mean_sd 	= r(sd)

		estadd scalar ctl_mean_table	=r(mean)
		estadd scalar ctl_N_table		=r(N)
		estadd scalar ctl_mean_sd_table	=r(sd)

		sum lagvar if e(sample) & !(NoCropBoroBL == 1 | NoCropBL == 1 | incorrect_no_bl == 1 | NoPlotDataBL == 1 | NoHarvValueDataBL == 1)
		scalar BL_mean 		= r(mean)
		scalar BL_N 	= r(N)

		estadd scalar BL_mean_table	=r(mean)
		estadd scalar BL_N_table	=r(N)

		rename lagvar lag2_`depvar'

		foreach tmt_arm in reg sdp inc {
			scalar  conf_int_min_`tmt_arm'   =coeff_`tmt_arm'-(1.96*coeff_se_`tmt_arm') + ctl_mean
			scalar  conf_int_max_`tmt_arm'   =coeff_`tmt_arm'+(1.96*coeff_se_`tmt_arm') + ctl_mean

			test    tmt_`tmt_arm'

			scalar  pvalue =r(p)
			local star_`tmt_arm'
			if pvalue < 0.10 {
				local star_`tmt_arm' "*"
			}
			if pvalue < 0.05 {
				local star_`tmt_arm' "**"
			}
			if pvalue < 0.01 {
				local star_`tmt_arm' "***"
			}

			scalar tmt_mean_`tmt_arm' = ctl_mean + coeff_`tmt_arm'
		}

		file open  `textfile'_`date' using `textfile'_`date'.txt, text write append
		file write `textfile'_`date' ///
			"`regname'" _tab "Control"  _tab %9.3f (ctl_mean)      _tab  		 			_tab 		 			  	  _tab 			 				_tab %9.3f (ctl_N) 	   _tab			  	  _n ///
			"`regname'" _tab "reg"		_tab %9.3f (tmt_mean_reg)  _tab  %9.3f (coeff_reg)  _tab %9.3f (conf_int_min_reg) _tab %9.3f (conf_int_max_reg) _tab %9.3f (obs_reg)   _tab "`star_reg'"  _n ///
			"`regname'" _tab "sdp"		_tab %9.3f (tmt_mean_sdp)  _tab  %9.3f (coeff_sdp)  _tab %9.3f (conf_int_min_sdp) _tab %9.3f (conf_int_max_sdp) _tab %9.3f (obs_sdp)   _tab "`star_sdp'"  _n ///
			"`regname'" _tab "inc"		_tab %9.3f (tmt_mean_inc)  _tab  %9.3f (coeff_inc)  _tab %9.3f (conf_int_min_inc) _tab %9.3f (conf_int_max_inc) _tab %9.3f (obs_inc)   _tab "`star_inc'"  _n
		file close `textfile'_`date'

	}
	esttab `REG_RESULTS' using "$csv/outcomeVar_AllTreat_`date'.csv",      ///
		title(`tabeleFileName') stats(BL_mean_table BL_N_table ctl_mean_table ctl_N_table ctl_mean_sd_table N) mtitles nonumbers plain br  star(* 0.10 ** 0.05 *** 0.01) ///
		b(a3)  se(%9.2f) alignment(l) replace

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

		Produce the graphs

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

	insheet using `textfile'_`date'.txt, clear tab

	pause on

	cd "$graphs/gRaw"

	gen tmt = .
	replace tmt = 1 if tmtstatus == "Control"
	replace tmt = 2 if tmtstatus == "reg"
	replace tmt = 3 if tmtstatus == "sdp"
	replace tmt = 4 if tmtstatus == "inc"

	gen placement = tmt

	*gen 	placement = tmt if demo_code == 1
	*replace placement = tmt + 5 if demo_code == 2

	foreach percentVar in mean coeff conf_int_min conf_int_max {

		replace `percentVar' =  `percentVar' * 100 if variable == "c1_commerce"
	}

	gen 	labelString 		= ""
	gen 	labelCategory		= .
	local 	continVars_Large  	c1_harv_value c1_net_yield c1_gross_yield c1_total_earnings c2_inp_total_spending c1_IAAP_harv_value c1_net_yield_nonLabor c1_net_yield_allLabor
	local 	continVars_Small  	c1_total_plotsize
	local 	percentage_Vars   	c1_commerce

	foreach L_var of local continVars_Large {

		replace labelCategory = 1 if variable == "`L_var'"
	}
	foreach S_var of local continVars_Small {

		replace labelCategory = 2 if variable == "`S_var'"
	}
	foreach P_var of local percentage_Vars {

		replace labelCategory = 3 if variable == "`P_var'"
	}

	tempvar labelString_temp decimal_postion

	tostring mean,	gen(`labelString_temp') force
	gen 	`decimal_postion' = strpos(`labelString_temp',".")

	*For continious values without decimal point (means include all in practice)
	replace `decimal_postion' = 10 if  `decimal_postion' == 0

	replace labelString = substr(`labelString_temp',1,(`decimal_postion'-1)) 		if labelCategory == 1
	replace labelString = substr(`labelString_temp',1,4) 							if labelCategory == 2
	replace labelString = substr(`labelString_temp',1,`decimal_postion'+1) + "%"	if labelCategory == 3

	tempfile restart_loop
	save 	`restart_loop', replace

	levelsof variable
	foreach level in `r(levels)' {
		use `restart_loop', clear
		di "`level'"
		keep if variable == "`level'"

		foreach tmt in reg sdp inc {
			preserve
				keep if tmtstatus == "`tmt'"
				local obs_`tmt'  = obs[1]
				local star_`tmt' = star[1]
			restore
		}

		preserve
			keep if tmtstatus == "Control"
			local obs_ctrl  = obs[1]
		restore

		local title "ERROR"

		if "`level'" == "c1_IAAP_harv_value"	local title "Harvest value IAPP crops, Taka"
		if "`level'" == "c1_commerce"			local title "Commercialization, earnings/production %"
		if "`level'" == "c1_gross_yield"		local title "Gross yield, Taka/Ha"
		if "`level'" == "c1_gross_yield_comp"	local title "Gross yield, plots with completed harvest, Taka/Ha"
		if "`level'" == "c1_gross_yield_harv" 	local title "Gross yield, actaully harvested all plots, Taka/Ha"
		if "`level'" == "c1_harv_value"			local title "Total value all harvest, Taka"
		if "`level'" == "c1_harv_value_int"		local title "Total value harvest intercropped, Taka"
		if "`level'" == "c1_harv_value_mono" 	local title "Total value harvest monocropped, Taka"
		if "`level'" == "c1_net_yield"			local title "Net yield, Taka/Ha"
		if "`level'" == "c1_total_earnings"		local title "Total earnings all crop sales, Taka"
		if "`level'" == "c1_total_plotsize"		local title "Total plotsize, Ha"
		if "`level'" == "c2_inp_total_spending"	local title "Total input spending, Taka"

		local noyscale ylabel(#3) ytitle("")
		*if "`title'" != "Boro" & "`title'" != "Lentil" & "`title'" != "Mustard" local noyscale ysc(off)

		if "`level'" == "c2_inp_total_spending"	local noyscale ylabel(#2) ytitle("")
		if "`level'" == "c1_gross_yield"		local noyscale ylabel(#2) ytitle("")

		local graphname	gph_`textfile'_`level'
		sort placement
		graph twoway (bar mean placement if tmt == 1, color("17 33 44")	 ) ///
			(bar mean placement if tmt == 2 , color("228 202 194") 		) ///
			(bar mean placement if tmt == 3 , color("215 107 96" ) 		 ) ///
			(bar mean placement if tmt == 4 , color("156 36  24" ) 		) ///
			(rcap conf_int_max conf_int_min placement, lc(gs) 						) ///
			(scatter  mean placement, msym(none) mlab(labelString) mlabs(medium) mlabpos(10) mlabcolor(black))	, ///
			legend(order(1 2 3 4) lab(1 "Control") lab(2 "Regular Treatment") lab(3 "Shared Demo Treatment") lab(4 "Incentives Treatment")) ///
			 plotregion(margin(b+3 t+3)) ///
			xlabel(	1 `" (N = `obs_ctrl') "' ///
					2 `" "(N = `obs_reg')" " `star_reg' " "' ///
					3 `" "(N = `obs_sdp')" " `star_sdp' " "' ///
					4 `" "(N = `obs_inc')" " `star_inc' " "', noticks labsize(medsmall)) ///
			graphregion( lcolor("182 222 255") fcolor("182 222 255")) ///
			plotregion(fcolor("247 247 247") ) ///
		   	xtitle("") ``level'_lab' `noyscale'  ///
			title("`title'") saving(`graphname'.gph, replace)
			graph export `graphname'.png, replace
		*pause
	}

	*Main outcomes
	cd "$graphs/gRaw"
	grc1leg gph_ag_outcome_log_c1_harv_value.gph 	///
			gph_ag_outcome_log_c1_net_yield.gph 	///
			gph_ag_outcome_log_c1_gross_yield.gph 	///
			gph_ag_outcome_log_c1_total_earnings.gph 	///
		,saving(gph_ag_outcome_log_MAIN.png, replace) /*ycom*/ ///
		cols(2) imargin(small) graphregion( lcolor("182 222 255") fcolor("182 222 255"))
	graph use gph_ag_outcome_log_MAIN.png
	graph display, xsize(8.5)

	cd "$graphs/"
	graph export gph_ag_outcome_log_MAIN.eps, preview(on) replace

	pause

	*Other ag outcomes
	cd "$graphs/gRaw"
	grc1leg gph_ag_outcome_log_c2_inp_total_spending.gph 	///
			gph_ag_outcome_log_c1_total_plotsize.gph 	///
			gph_ag_outcome_log_c1_IAAP_harv_value.gph 	///
			gph_ag_outcome_log_c1_commerce.gph 	///
		,saving(gph_ag_outcome_log_other.png, replace) /*ycom*/ ///
		cols(2) imargin(small) graphregion( lcolor("182 222 255") fcolor("182 222 255"))
	graph use gph_ag_outcome_log_other.png
	graph display, xsize(8.5)

		cd "$graphs/"
	graph export gph_ag_outcome_log_other.eps, preview(on) replace

	cap log close `logname'
end


iematch : helpfile

iematch : helpfile

{smcl}
{* }{...}
{hline}
help for {hi:iematch}
{hline}

{title:Title}

{phang2}{cmdab:iematch} {hline 2} Matching base observations towards target observations using on a single continous variable.

{title:Syntax}

{phang2}
{cmdab:iematch}
, {cmdab:grp:dummy(}{it:varname}{cmd:)} {cmdab:match:var(}{it:varname}{cmd:)}
[{cmdab:id:var(}{it:varlist}{cmd:)} {cmdab:m1} {cmdab:maxdiff(}{it:numlist}{cmd:)} {cmdab:seed(}{it:numlist}{cmd:)}
{cmdab:matchid:name(}{it:string}{cmd:)} {cmdab:matchdi:ffname(}{it:string}{cmd:)}
{cmdab:matchre:sultname(}{it:string}{cmd:)} {cmdab:matchco:untname(}{it:string}{cmd:)}
]

{marker opts}{...}
{synoptset 22}{...}
{synopthdr:options}
{synoptline}
{synopt :{cmdab:grp:dummy(}{it:varname}{cmd:)}}The group dummy variable where 1 indicates base observations and 0 target observations{p_end}
{synopt :{cmdab:match:var(}{it:varname}{cmd:)}}The variable with a continous value to match on{p_end}
{synopt :{cmdab:id:var(}{it:varlist}{cmd:)}}The uniquely and fully identifying ID varaible. Used to indicate which target observation a base observation is match with. If omitted an ID variable will be created.{p_end}
{synopt :{cmdab:m1}}Allows many-to-one matches. The default is to allow only one-to-one matches{p_end}
{synopt :{cmdab:maxdiff(}{it:numlist}{cmd:)}}Set a maximum difference allowed in {cmdab:matchvar()}. If a base observation has no match within this difference then it will remain unmatched{p_end}
{synopt :{cmdab:seed(}{it:numlist}{cmd:)}}If seed is not set outside the command (recommended), then this option can be used to set the seed to make matches stable even when multiple observations have the same value in {cmdab:matchvar()}{p_end}
{synopt :{cmdab:matchid:name(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates which target observation each base observation is matched with. The default is _matchID{p_end}
{synopt :{cmdab:matchdi:ffname(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates the differnece in {cmdab:matchvar()} in each match pair/group. The default is _matchDiff{p_end}
{synopt :{cmdab:matchre:sultname(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates if an observation was matched, or provide a reason why it was not. The default is _matchOrReason{p_end}
{synopt :{cmdab:matchco:untname(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates how many observations a target obsersvation is matched with in a many-to-one matches. The default is _matchCount{p_end}
{synoptline}

{title:Description}

{pstd}{cmdab:iematch} matches base observations towards base observations in terms
	of nearest value in {cmd:matchvar()}. Base observations are observations with
	value 1 in {cmd:grpdummy()} and target observations are observations with value
	0. In a regular p-score match for example, base observations are treatment and target
	is control, however, there are many other examples of mathcing where it could
	be different.

. Observations with missing
	values will be excluded frmo the matching.

{pstd}The algorithm used in {cmdab:iematch} when performing a one-to-one match is
	based on the Stable Marriage algorithm. The algorithm startes by evaluating which
	observation from the other group in {cmd:grpdummy()} is closest in terms of the
	variable in {cmd:matchvar()}. If two observations in differnt groups prefer each
	other, then they are a match and they are removed from the data set. The process
	is repeated until all observations with value 1 in group dummy are mathced. This
	algorthm always has a solution and it is implemented to keep the matches stable
	even when mutiple observations share value in {cmd:matchvar()}.

{pstd}When performing a many-to-one match the algorithm does the first step in the one-to-one algorithm, and assign all observations with value 1

{pstd}For example,

{title:Options}

{phang}{cmdab:grp:dummy(}{it:varname}{cmd:)}

{phang}{cmdab:match:var(}{it:varname}{cmd:)}

{phang}{cmdab:id:var(}{it:varlist}{cmd:)}

{phang}{cmdab:m1}

{phang}{cmdab:maxdiff(}{it:numlist}{cmd:)}

{phang}{cmdab:seed(}{it:numlist}{cmd:)}

{phang}{cmdab:matchid:name(}{it:string}{cmd:)} The names _ID, _matchDiff, _matchOrReason and _matchCount are not allowed.

{phang}{cmdab:matchdi:ffname(}{it:string}{cmd:)} The names _ID, _matchID, _matchOrReason and _matchCount are not allowed.

{phang}{cmdab:matchre:sultname(}{it:string}{cmd:)} The names _ID, _matchID, _matchDiff and _matchCount are not allowed.

{phang}{cmdab:matchco:untname(}{it:string}{cmd:)} The names _ID, _matchID, _matchDiff and _matchOrReason are not allowed.

{title:Stored results}

{pstd}
{cmdab:iematch} stores the following results in {hi:r()}:

{title:Examples}

{pstd}
A series of examples on how to specify command, and how to evaluate output:

{pstd}{hi:Example 1.}

{pstd}{hi:Example 2.}

{title:Acknowledgements}

{phang}I would like to acknowledge the help in testing and proofreading I received in relation to this command and help file from (in alphabetic order):{p_end}

{title:Author}

{phang}Kristoffer Bjrkefur, The World Bank, DECIE

{phang}Please send bug-reports, suggestions and requests for clarifications
		 writing "ietools iematch" in the subject line to:{break}
		 kbjarkefur@worldbank.org

{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"}

{smcl}
{* }{...}
{hline}
help for {hi:iematch}
{hline}

{title:Title}

{phang2}{cmdab:iematch} {hline 2} Matching base observations towards target observations using on a single continous variable.

{title:Syntax}

{phang2}
{cmdab:iematch}
, {cmdab:grp:dummy(}{it:varname}{cmd:)} {cmdab:match:var(}{it:varname}{cmd:)}
[{cmdab:id:var(}{it:varlist}{cmd:)} {cmdab:m1} {cmdab:maxdiff(}{it:numlist}{cmd:)} {cmdab:seed(}{it:numlist}{cmd:)}
{cmdab:matchid:name(}{it:string}{cmd:)} {cmdab:matchdi:ffname(}{it:string}{cmd:)}
{cmdab:matchre:sultname(}{it:string}{cmd:)} {cmdab:matchco:untname(}{it:string}{cmd:)}
]

{marker opts}{...}
{synoptset 22}{...}
{synopthdr:options}
{synoptline}
{synopt :{cmdab:grp:dummy(}{it:varname}{cmd:)}}The group dummy variable where 1 indicates base observations and 0 target observations{p_end}
{synopt :{cmdab:match:var(}{it:varname}{cmd:)}}The variable with a continous value to match on{p_end}
{synopt :{cmdab:id:var(}{it:varlist}{cmd:)}}The uniquely and fully identifying ID varaible. Used to indicate which target observation a base observation is match with. If omitted an ID variable will be created.{p_end}
{synopt :{cmdab:m1}}Allows many-to-one matches. The default is to allow only one-to-one matches{p_end}
{synopt :{cmdab:maxdiff(}{it:numlist}{cmd:)}}Set a maximum difference allowed in {cmdab:matchvar()}. If a base observation has no match within this difference then it will remain unmatched{p_end}
{synopt :{cmdab:seed(}{it:numlist}{cmd:)}}If seed is not set outside the command (recommended), then this option can be used to set the seed to make matches stable even when multiple observations have the same value in {cmdab:matchvar()}{p_end}
{synopt :{cmdab:matchid:name(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates which target observation each base observation is matched with. The default is _matchID{p_end}
{synopt :{cmdab:matchdi:ffname(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates the differnece in {cmdab:matchvar()} in each match pair/group. The default is _matchDiff{p_end}
{synopt :{cmdab:matchre:sultname(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates if an observation was matched, or provide a reason why it was not. The default is _matchOrReason{p_end}
{synopt :{cmdab:matchco:untname(}{it:string}{cmd:)}}Manually sets the name of the variable that indicates how many observations a target obsersvation is matched with in a many-to-one matches. The default is _matchCount{p_end}
{synoptline}

{title:Description}

{pstd}{cmdab:iematch} matches base observations towards base observations in terms of nearest value in {cmd:matchvar()}. Base observations are observations with value 1 in {cmd:grpdummy()} and target observations are observations with value 0.

. Observations with missing
	values will be excluded frmo the matching.

{pstd}The algorithm used in {cmdab:iematch} when performing a one-to-one match is
	based on the Stable Marriage algorithm. The algorithm startes by evaluating which
	observation from the other group in {cmd:grpdummy()} is closest in terms of the
	variable in {cmd:matchvar()}. If two observations in differnt groups prefer each
	other, then they are a match and they are removed from the data set. The process
	is repeated until all observations with value 1 in group dummy are mathced. This
	algorthm always has a solution and it is implemented to keep the matches stable
	even when mutiple observations share value in {cmd:matchvar()}.

{pstd}When performing a many-to-one match the algorithm does the first step in the one-to-one algorithm, and assign all observations with value 1

{pstd}For example,

{title:Options}

{phang}{cmdab:grp:dummy(}{it:varname}{cmd:)}

{phang}{cmdab:match:var(}{it:varname}{cmd:)}

{phang}{cmdab:id:var(}{it:varlist}{cmd:)}

{phang}{cmdab:m1}

{phang}{cmdab:maxdiff(}{it:numlist}{cmd:)}

{phang}{cmdab:seed(}{it:numlist}{cmd:)}

{phang}{cmdab:matchid:name(}{it:string}{cmd:)} The names _ID, _matchDiff, _matchOrReason and _matchCount are not allowed.

{phang}{cmdab:matchdi:ffname(}{it:string}{cmd:)} The names _ID, _matchID, _matchOrReason and _matchCount are not allowed.

{phang}{cmdab:matchre:sultname(}{it:string}{cmd:)} The names _ID, _matchID, _matchDiff and _matchCount are not allowed.

{phang}{cmdab:matchco:untname(}{it:string}{cmd:)} The names _ID, _matchID, _matchDiff and _matchOrReason are not allowed.

{title:Stored results}

{pstd}
{cmdab:iematch} stores the following results in {hi:r()}:

{title:Examples}

{pstd}
A series of examples on how to specify command, and how to evaluate output:

{pstd}{hi:Example 1.}

{pstd}{hi:Example 2.}

{title:Acknowledgements}

{phang}I would like to acknowledge the help in testing and proofreading I received in relation to this command and help file from (in alphabetic order):{p_end}

{title:Author}

{phang}Kristoffer Bjrkefur, The World Bank, DECIE

{phang}Please send bug-reports, suggestions and requests for clarifications
		 writing "ietools iematch" in the subject line to:{break}
		 kbjarkefur@worldbank.org

{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"}


added pctile labels

added pctile labels

program chart_qual

use ${stash}master, clear

local var `1'
local isbaseball `2'

keep if isbaseball == `isbaseball'

gen tvar = treat

xtreg `var' tvar##post##quality i.$fe, vce(robust) fe level(90)

qui parmest, label list(parm estimate min* max* p) saving(${stash}pars_tmp, replace) level(90)

clear
use ${stash}pars_tmp, clear

keep if regexm(parm, "[0-9].*tvar.*post.*quality") == 1 | parm == "1.tvar#1.post"

drop if estimate == 0

gen quality = regexs(1) if regexm(parm, ".*post.*\#([0-9]+)\.quality")
replace quality = "1" if quality == ""

replace estimate = estimate + estimate[1] if _n > 1
replace max90 = max90 + estimate[1] if _n > 1
replace min90 = min90 + estimate[1] if _n > 1

list quality estimate max min

destring quality, replace

label define qualitylabel 1 "Top 25 pctile" 2 "25-50 pctile" 3 "50-75 pctile" 4 "Bottom 25 pctile"

label values quality qualitylabel

graph twoway (scatter estimate quality) (rcap min max quality), legend(off) title("") xtitle("") xscale(r(0 5)) yline(0, lcolor(gs10)) xlabel(1 "Top 25 pctile" 2 "25-50 pctile" 3 "50-75 pctile" 4 "Bottom 25 pctile")

graph export "${tables}quality_`var'_`isbaseball'.eps", replace

shell epstopdf  "${tables}quality_`var'_`isbaseball'.eps"

end

program chart_qual

use ${stash}master, clear

local isbaseball `1'
local isbaseball "1"

local var img
keep if isbaseball == `isb'

gen tvar = treat

xtreg `var' tvar##post##quality i.$fe, vce(robust) fe level(90)

qui parmest, label list(parm estimate min* max* p) saving(${stash}pars_tmp, replace) level(90)

clear
use ${stash}pars_tmp, clear

keep if regexm(parm, "[0-9].*tvar.*post.*quality") == 1
drop if estimate == 0

gen quality = regexs(1) if regexm(parm, ".*post.*\#([0-9]+)\.quality")
list quality estimate max min

gen xaxis = 0
destring quality, replace

graph twoway (bar estimate quality, msize(small) lpattern(solid) lcolor(edkblue) lwidth(thin) barwidth(0.08)) (rcap min max quality) (line xaxis quality), legend(off) title("") xtitle("")

graph export "${tables}quality_`var'_`isbaseball'.eps", replace

shell epstopdf  "${tables}quality_`var'_`isbaseball'.eps"

end


minor mata bugfixes

minor mata bugfixes

// Common functions ---------------------------------------------------------
mata:

// --------------------------------------------------------------------------
// BUGBUG: not sure if this is still used...
// --------------------------------------------------------------------------
`StringRowVector' clean_tokens(`String' vars)
{
	`StringRowVector' 		ans
	`Integer'				i
	ans = tokens(vars)
	for (i=1; i<=cols(ans); i++) {
		ans[i] = invtokens(tokens(ans[i]))
	}
	return(ans)
}

// --------------------------------------------------------------------------
// Each col of A will have stdev of 1 unless stdev is quite close to 0
// --------------------------------------------------------------------------
`RowVector' function reghdfe_standardize(`Matrix' A)
{
	`RowVector'				stdevs, means
	`Integer'				i, K, N

	// We don't need to good accuracy for the stdevs, so we have a few alternatives:
	// Note: cross(1,A) is the same as colsum(A), but faster
	// Note: cross(A, A) is very fast, but we only need the main diagonals
	// [A: 1sec] stdevs = sqrt( (colsum(A:*A) - (cross(1, A) :^ 2 / N)) / (N-1) )
	// [B: .61s] stdevs = sqrt( (diagonal(cross(A, A))' - (cross(1, A) :^ 2 / N)) / (N-1) )
	// [C: .80s] stdevs = diagonal(sqrt(variance(A)))'
	// [D: .67s] means = cross(1, A) / N; stdevs =  sqrt(diagonal(crossdev(A, means, A, means))' / (N-1))

	assert_msg(!isfleeting(A), "input cannot be fleeting")
	N = rows(A)
	K = cols(A)

	stdevs = J(1, K, .)

	// (A) Very precise

	// (B) Precise
	//means = cross(1, A) / N
	//stdevs =  sqrt(diagonal(crossdev(A, means, A, means))' / (N-1))

	// (C) 20% faster; don't use it if you care about accuracy
	stdevs = sqrt( (diagonal(cross(A, A))' - (cross(1, A) :^ 2 / N)) / (N-1) )
	assert_msg(!missing(stdevs), "stdevs are missing; is N==1?") // Shouldn't happen as we don't expect N==1

	stdevs = colmax(( stdevs \ J(1, K, 1e-3) ))
	A = A :/ stdevs
	return(stdevs)
}

// --------------------------------------------------------------------------
// Divide two row vectors but adjust the denominator if it's too small
// --------------------------------------------------------------------------
`RowVector' safe_divide(`RowVector' numerator, `RowVector' denominator, | `Real' epsi) {
	 // If the denominator goes below machine precision, the division explodes
	 if (args()<3 | epsi==.) epsi = epsilon(1)
	return( numerator :/ colmax(denominator \ J(1,cols(denominator),epsi)) )
}

// If X is not square...
// `Matrix' R
// real colvector tau, p

// _hqrdp(A, tau, R, p=.)
// B = hqrdmultq1t(A, tau, B)
// rank = _solveupper(R, B, tol)
// B = B[invorder(p),.]
// +- +-

// invsym(makesymmetric(..))

// --------------------------------------------------------------------------
// Robust solver for Ax=b
// --------------------------------------------------------------------------
// Mata utility for sequential use of solvers
// Default is cholesky;
// if that fails, use QR;
// if overridden, use QR.
// Author: Schaffer, Mark E <M.E.Schaffer@hw.ac.uk>
// --------------------------------------------------------------------------
// Warning:
// cholqrsolve calls qrsolve which calls _qrsolve which calls ...
// Does all the indirection makes it too slow to use within a panel?
// --------------------------------------------------------------------------
`Matrix' function reghdfe_cholqrsolve(`Matrix' A,
                                      `Matrix' B,
                                    | `Boolean' useqr)
{
	`Matrix' C
	if (args()<3 | useqr==.) useqr = 0

	if (!useqr) {
		C = cholsolve(A, B)
		if (hasmissing(C)) useqr = 1
	}

	if (useqr) {
		C = qrsolve(A, B)
	}

	return(C)
}

// --------------------------------------------------------------------------
// OLS Regression
// --------------------------------------------------------------------------
`Void' function reghdfe_post_ols(`FixedEffects' S,
                                 `Variables' X,
                                 `String' bname,
                                 `String' Vname,
                                 `String' nname,
                                 `String' rname,
                                 `String' dfrname)
{
	`Integer'				N
	`Integer'				rank
	`Integer'				df_r
	`Vector'				b
	`Matrix'				V
	`Variable'				resid
	`Real'					eps
	`Integer'				i

	if (S.timeit) timer_on(90)
	reghdfe_solve_ols(S, X, b=., V=., N=., rank=., df_r=., resid=.)
	if (S.timeit) timer_off(90)

	st_matrix(bname, b')

	// Add "o." prefix to omitted regressors
	eps = sqrt(epsilon(1))
	for (i=1; i<=rows(b); i++) {
		if (b[i]==0 & S.verbose > -1) {
			printf("{txt}note: %s omitted because of collinearity\n", tokens(S.indepvars)[i])
			//stata(sprintf("_ms_put_omit %s", indepvars[i]))
			//indepvars[i] = st_global("s(ospec)")
			// This is now one in reghdfe.ado with -_ms_findomitted-
		}
	}

	st_matrix(Vname, V)
	st_numscalar(nname, N)
	st_numscalar(rname, rank)
	st_numscalar(dfrname, df_r)

	// Need to save resids if saving FEs, even if temporarily
	if (S.residuals == "" & S.save_any_fe) {
		S.residuals = "__temp_reghdfe_resid__"
	}

	if (S.residuals != "") {
		if (S.verbose > 0) printf("\n{txt} ## Storing residuals in {res}%s{txt}\n\n", S.residuals)
		S.save_variable(S.residuals, resid, "Residuals")
	}
}

`Void' function reghdfe_solve_ols(`FixedEffects' S,
                                  `Variables' X,
                                  `Vector' b,
                                  `Matrix' V,
                                  `Integer' N,
                                  `Integer' rank,
                                  `Integer' df_r,
                                  `Vector' resid)
{
	// Hack: the first col of X is actually y!
	`Integer'				K, KK
	`Matrix'				xx, inv_xx, W, inv_V, just_X
	`Vector' 				xy, w
	`Integer'				used_df_r
	`RowVector'             kept

	if (S.vcetype == "unadjusted" & S.weight_type=="pweight") S.vcetype = "robust"
	if (S.verbose > 0) printf("\n{txt} ## Solving least-squares regression of partialled-out variables\n\n")

	// Weight FAQ:
	// - fweight: obs. i represents w[i] duplicate obs. (there is no loss of info wrt to having the "full" dataset)
	// - aweight: obs. i represents w[i] distinct obs. that were mean-collapsed (so there is loss of info and hetero)
	//	 soln: normalize them so they sum to N (the true number of obs in our sample), and then treat them as fweight
	// - pweight: each obs. represents only one obs. from the pop, that was drawn from w[i] individuals
	//	          we want to make inference on the population, so if we interviewed 100% of the men and only 10% of women,
	//            then without weighting we would be over-representing men, which leads to a loss of efficiency +-+-
	// 			  it is the same as aweight + robust
	// We need to pick N and w
	N = rows(X) // Default; will change with fweights
	w = 1
	if (S.weight_type=="fweight") {
		N = sum(S.weight)
		w = S.weight
	}
	else if (S.weight_type=="aweight" | S.weight_type=="pweight") {
		w = S.weight * (N / sum(S.weight))
	}

	// Build core matrices
	if (S.timeit) timer_on(91)
	K = cols(X) - 1
	xx = quadcross(X, w, X)
	S.tss_within = xx[1,1]
	xy = K ? xx[| 2 , 1 \ K+1 , 1 |] : J(0, 1, .)
	xx = K ? xx[| 2 , 2 \ K+1 , K+1 |] : J(0, 0, .)
	if (S.timeit) timer_off(91)

	// This matrix indicates what regressors are not collinear
	assert_msg(cols(S.kept)==K+1, "partial_out() was run with a different set of vars")
	kept = K ? S.kept[2..K+1] : J(1, 0, .)

	// Bread of the robust VCV matrix
	// Compute this early so we can update the list of collinear regressors
	if (S.timeit) timer_on(95)
	inv_xx = reghdfe_rmcoll(tokens(S.indepvars), xx, kept)
	S.df_m = rank = K - diag0cnt(inv_xx)
	KK = rank + S.df_a
	S.df_r = N - KK // replaced when clustering
	if (S.timeit) timer_off(95)

	// Compute betas
	if (S.has_weights) {
		b = reghdfe_cholqrsolve(xx, xy, 1) // qr or chol?
	}
	else {
		// This is more numerically accurate but doesn't handle weights
		// See: http://www.stata.com/statalist/archive/2012-02/msg00956.html
		b = J(K, 1, 0)
		if (cols(kept)) {
			b[kept] = qrsolve(X[., 1:+kept], X[., 1])
		}
	}

	if (S.timeit) timer_on(92)
	resid = X * (1 \ -b) // y - X * b
	if (S.timeit) timer_off(92)

	if (S.timeit) timer_on(93)
	if (S.vcetype != "unadjusted") {
		just_X = K ? X[., 2..K+1] : J(N, 0, .)
	}
	if (S.timeit) timer_off(93)

	if (S.timeit) timer_on(94)
	S.rss = quadcross(resid, w, resid) // do before reghdfe_robust() modifies w
	if (S.timeit) timer_off(94)

	// Compute full VCE
	if (S.timeit) timer_on(96)
	assert_msg(anyof( ("unadjusted", "robust", "cluster") , S.vcetype), S.vcetype)
	if (S.vcetype == "unadjusted") {
		if (S.verbose > 0) {
			printf("{txt}    - Small-sample-adjustment: q = N / (N-df_m-df_a) = %g / (%g - %g - %g) = %g\n", N, N, rank, S.df_a, N / S.df_r )
		}
		V = S.rss / S.df_r * inv_xx
	}
	else if (S.vcetype == "robust") {
		V = reghdfe_robust(S, just_X, inv_xx, resid, w, N, KK)
	}
	else {
		V = reghdfe_cluster(S, just_X, inv_xx, resid, w, N, KK)
	}
	if (S.timeit) timer_off(96)

	// Wald test: joint significance
	if (S.timeit) timer_on(97)
	inv_V = invsym(V[kept, kept]) // this might not be of full rank but numerical inaccuracies hide it
	if (diag0cnt(inv_V)) {
		if (S.verbose > -1) printf("{txt}(Warning: missing F statistic; dropped variables due to collinearity or too few clusters)\n")
		W = .
	}
	else if (length(b[kept])==0) {
		W = .
	}
	else {
		W = b[kept]' * inv_V * b[kept] / S.df_m
		if (missing(W) & S.verbose > -1) printf("{txt}(Warning: missing F statistic)\n")
	}
	if (S.timeit) timer_off(97)

	// V can be missing if b is completely absorbed by the FEs
	if (missing(V)) {
		if (S.verbose > 0) printf("{txt}    - VCE has missing values, setting it to zeroes (are your regressors all collinear?)\n")
		V = J(rows(V), rows(V), 0)
	}

	// Results
	S.title = "Linear regression"
	// S.model = "ols"

	if (S.weight_type!="") S.sumweights = quadsum(S.weight)

	used_df_r = N - KK - S.df_a_nested
	S.r2 = 1 - S.rss / S.tss
	S.r2_a = 1 - (S.rss / used_df_r) / (S.tss / (N - S.has_intercept ) )
	S.r2_within = 1 - S.rss / S.tss_within
	S.r2_a_within = 1 - (S.rss / used_df_r) / (S.tss_within / (used_df_r + rank))

	S.ll = - 0.5 * N * (1 + ln(2 * pi()) + ln(S.rss / N))
	S.ll_0 = - 0.5 * N * (1 + ln(2 * pi()) + ln(S.tss_within / N))

	S.rmse = sqrt(S.rss / used_df_r)
	if (used_df_r==0) S.rmse = sqrt(S.rss)
	S.F = W
	df_r = S.df_r // reghdfe_cluster might have updated it (this gets returned to the caller function)
}

// --------------------------------------------------------------------------
// Robust VCE
// --------------------------------------------------------------------------
// Advice: Delegate complicated regressions to -avar- and specialized routines
// BUGBUG: do we standardize X again? so V is well behaved?
// Notes:
// - robust is the same as cluster robust where cluster==_n
// - cluster just "collapses" X_i * e_i for each group, and builds M from that

`Matrix' reghdfe_robust(`FixedEffects' S,
                        `Variables' X,
						`Matrix' D,
						`Variable' resid,
						`Variable' w,
						`Integer' N,
						`Integer' K)
{
	`Matrix'				M, V
	`Integer'				dof_adj

	if (S.verbose > 0) printf("\n{txt} ## Estimating Robust Variance-Covariance Matrix of the Estimators (VCE)\n\n")
	if (S.verbose > 0) printf("{txt}    - VCE type: {res}%s{txt}\n", S.vcetype)
	if (S.verbose > 0) printf("{txt}    - Weight type: {res}%s{txt}\n", S.weight_type=="" ? "<none>" : S.weight_type)

	if (S.weight_type=="") {
		w = resid :^ 2
	}
	else if (S.weight_type=="fweight") {
		w = resid :^ 2 :* w
	}
	else if (S.weight_type=="aweight" | S.weight_type=="pweight") {
		w = (resid :* w) :^ 2
	}

	dof_adj = N / (N - K)
	M = quadcross(X, w, X)
	if (S.verbose > 0) {
		printf("{txt}    - Small-sample-adjustment: q = N / (N-df_m-df_a) = %g / (%g - %g - %g) = %g\n", N, N, K-S.df_a, S.df_a, N / (N-K) )
	}
	V = D * M * D * dof_adj
	return(V)
}

`Matrix' reghdfe_cluster(`FixedEffects' S,
                        `Variables' X,
						`Matrix' D,
						`Variable' resid,
						`Variable' w,
						`Integer' N,
						`Integer' K)
{
	`Matrix' 				M, V
	`Integer'				dof_adj, N_clust, df_r, nested_adj
	`Integer'				Q, q, g, sign, i, j
	pointer(`Factor') rowvector FPlist
	`FactorPointer'			FP
	`Varlist'				vars
	`String'				var, var_with_spaces
	`Boolean'				clustervar_is_absvar, required_fix
	`Matrix'				tuples
	`RowVector'				tuple
	`RowVector'				N_clust_list
	`Matrix'				joined_levels

	w = resid :* w
	//if (S.weight_type=="") {
	//	w = resid
	//}
	//else if (S.weight_type=="fweight") {
	//	w = resid :* w
	//}
	//else if (S.weight_type=="aweight" | S.weight_type=="pweight") {
	//	w = resid :* w
	//}

	vars = S.clustervars
	Q = cols(vars)
	if (S.verbose > 0) printf("\n{txt} ## Estimating Cluster Robust Variance-Covariance Matrix of the Estimators (VCE)\n\n")
	if (S.verbose > 0) printf("{txt}    - VCE type: {res}%s{txt} (%g-way clustering)\n", S.vcetype, Q)
	if (S.verbose > 0) printf("{txt}    - Cluster variables: {res}%s{txt}\n", invtokens(vars))
	if (S.verbose > 0) printf("{txt}    - Weight type: {res}%s{txt}\n", S.weight_type=="" ? "<none>" : S.weight_type)
	assert_msg(0 < Q & Q < 10)

	// Get or build factors associated with the clustervars
	FPlist = J(1, Q, NULL)
	N_clust_list = J(1, Q, .)
	for (q=1; q<=Q; q++) {
		var = vars[q]
		clustervar_is_absvar = 0
		for (g=1; g<=S.G; g++) {
			if (invtokens(S.factors[g].varlist, "#") == var) {
				clustervar_is_absvar = 1
				FP = &(S.factors[g])
				break
			}
		}
		var_with_spaces = subinstr(var, "#", " ")
		if (!clustervar_is_absvar) FP = &(factor(var_with_spaces, S.sample, ., "", ., ., ., 0))
		N_clust_list[q] = (*FP).num_levels
		if (S.verbose > 0) printf("{txt}    - {res}%s{txt} has {res}%g{txt} levels\n", var, N_clust_list[q])
		FPlist[q] = FP
	}

	// Build the meat part of the V matrix
	if (S.verbose > 0) printf("{txt}    - Computing the 'meat' of the VCE\n")
	M = J(cols(X), cols(X), 0)
	tuples = .
	for (q=1; q<=Q; q++) {
		tuples = reghdfe_choose_n_k(Q, q, tuples)
		sign = mod(q, 2) ? 1 : -1 // + with odd number of variables, - with even
		for (j=1; j<=rows(tuples); j++) {
			tuple = tuples[j, .]
			if (S.verbose > 0) printf("{txt}      - Level %g/%g; sublevel %g/%g; M = M %s ClusterVCE(%s)\n", q, Q, j, rows(tuples), sign > 0 ? "+" : "-" , invtokens(strofreal(tuple)))
			if (q==1) {
				assert(tuple==j)
				FP =  FPlist[j]
			}
			else if (q==2) {
				FP = &join_factors( *FPlist[tuple[1]] , *FPlist[tuple[2]] , ., ., 1)
			}
			else {
				joined_levels = (*FPlist[tuple[1]]).levels
				for (i=2; i<=cols(tuple); i++) {
					joined_levels = joined_levels, (*FPlist[tuple[i]]).levels
				}
				FP = &_factor(joined_levels, ., ., "", ., ., ., 0)
			}
			M = M + sign * reghdfe_vce_cluster_meat(FP, X, w)
		}
	}

	// Build VCE
	N_clust = min(N_clust_list)
	nested_adj = (S.df_a==0) // minor adj. so we match xtreg when the absvar is nested within cluster
	// (when ..nested.., df_a is zero so we divide N-1 by something that can potentially be N (!))
	// so we either add the 1 back, or change the numerator (and the N_clust-1 factor!)
	dof_adj = (N - 1) / (N - nested_adj - K) * N_clust / (N_clust - 1) // adjust for more than 1 cluster
	if (S.verbose > 0) {
		printf("{txt}    - Small-sample-adjustment: q = (%g - 1) / (%g - %g) * %g / (%g - 1) = %g\n", N, N, K+nested_adj, N_clust, N_clust, dof_adj)
	}
	V = D * M * D * dof_adj
	if (Q > 1) {
		required_fix = reghdfe_fix_psd(V)
		if (required_fix) printf("{txt}Warning: VCV matrix was non-positive semi-definite; adjustment from Cameron, Gelbach & Miller applied.\n")
	}

	// Store e()
	assert(!missing(S.df_r))
	df_r = N_clust - 1
	if (S.df_r > df_r) {
		S.df_r = df_r
	}
	else if (S.verbose > 0) {
		printf("{txt}    - Unclustered df_r (N - df_m - df_a = %g) are {it:lower} than clustered df_r (N_clust-1 = %g)\n", S.df_r, df_r)
		printf("{txt}      Thus, we set e(df_r) as the former.\n")
		printf("{txt}      This breaks consistency with areg but ensures internal consistency\n")
		printf("{txt}      between vce(robust) and vce(cluster _n)\n")
	}

	S.N_clust = N_clust
	S.N_clust_list = N_clust_list

	return(V)
}

`Matrix' reghdfe_vce_cluster_meat(`FactorPointer' FP,
                                  `Variables' X,
                                  `Variable' resid)
{
	`Integer'				i, N_clust
	`Variables'				X_sorted
	`Variable'				resid_sorted
	`Matrix'				X_tmp
	`Vector'				resid_tmp
	`RowVector'				Xe_tmp
	`Matrix'				M

	if (cols(X)==0) return(J(0,0,0))

	N_clust = (*FP).num_levels
	(*FP).panelsetup()
	X_sorted = (*FP).sort(X)
	resid_sorted = (*FP).sort(resid)
	M = J(cols(X), cols(X), 0)

	for (i=1; i<=N_clust; i++) {
		X_tmp = panelsubmatrix(X_sorted, i, (*FP).info)
		resid_tmp = panelsubmatrix(resid_sorted, i, (*FP).info)
		Xe_tmp = quadcross(1, resid_tmp, X_tmp) // Faster than colsum(e_tmp :* X_tmp)
		M = M + quadcross(Xe_tmp, Xe_tmp)
	}

	return(M)
}

// Enumerate all combinations of K integers from N integers
// Kroneker approach based on njc's tuples.ado
`Matrix' reghdfe_choose_n_k(`Integer' n, `Integer' k, `Matrix' prev_ans)
{
	`RowVector' v
	`Integer' q
	`Matrix' candidate
	`Matrix' ans
	v = 1::n
	if (k==1) return(v)

	q = rows(prev_ans)
	assert(q==comb(n, k-1))
	assert(cols(prev_ans)==k-1)
	candidate = v # J(q, 1, 1)
	candidate = candidate , J(n, 1, prev_ans)
	ans = select(candidate, candidate[., 1] :< candidate[., 2])
	return(ans)
}

// --------------------------------------------------------------------------
// Fix non-positive VCV
// --------------------------------------------------------------------------
// If the VCV matrix is not positive-semidefinite, use the fix from
// Cameron, Gelbach & Miller - Robust Inference with Multi-way Clustering (JBES 2011)
// 1) Use eigendecomposition V = U Lambda U' where U are the eigenvectors and Lambda = diag(eigenvalues)
// 2) Replace negative eigenvalues into zero and obtain FixedLambda
// 3) Recover FixedV = U * FixedLambda * U'
`Boolean' function reghdfe_fix_psd(`Matrix' V) {
	`Matrix'				U
	`Matrix'				lambda
	`Boolean' 				required_fix

	if (!issymmetric(V)) _makesymmetric(V)
	if (!issymmetric(V)) exit(error(505))
	symeigensystem(V, U=., lambda=.)
	if (min(lambda)<0) {
		lambda = lambda :* (lambda :>= 0)
		// V = U * diag(lambda) * U'
		V = quadcross(U', lambda, U')
		required_fix = 1
	}
	else {
		required_fix = 0
	}
	return(required_fix)
}

// --------------------------------------------------------------------------
// Remove collinear variables
// --------------------------------------------------------------------------
// Based on ivreg2's s_rmcoll2
`Matrix' reghdfe_rmcoll(`Varlist' varnames,
                        `Matrix' xx,
                        `RowVector' kept)
{
	`Integer'				K
	`Matrix'				inv_xx, smat
	`RowVector'				vl_drop, vl_keep

	assert(rows(xx)==cols(xx))
	K = cols(xx)
	inv_xx = K ? invsym(xx, 1..K) : J(0, 0, .)
	st_numscalar("r(k_omitted)", diag0cnt(inv_xx))
	smat = (diagonal(inv_xx) :== 0)'
	vl_drop = select(varnames, smat)
	vl_keep = select(varnames, !smat)
	if (cols(vl_keep)) st_global("r(varlist)", invtokens(vl_keep))
	if (cols(vl_drop)) st_global("r(omitted)", invtokens(vl_drop))
	kept = `selectindex'(!smat) // Return it, so we can exclude these variables from the joint Wald test
	return(inv_xx)
}

end

// Common functions ---------------------------------------------------------
mata:

// --------------------------------------------------------------------------
// BUGBUG: not sure if this is still used...
// --------------------------------------------------------------------------
`StringRowVector' clean_tokens(`String' vars)
{
	`StringRowVector' 		ans
	`Integer'				i
	ans = tokens(vars)
	for (i=1; i<=cols(ans); i++) {
		ans[i] = invtokens(tokens(ans[i]))
	}
	return(ans)
}

// --------------------------------------------------------------------------
// Each col of A will have stdev of 1 unless stdev is quite close to 0
// --------------------------------------------------------------------------
`RowVector' function reghdfe_standardize(`Matrix' A)
{
	`RowVector'				stdevs, means
	`Integer'				i, K, N

	// We don't need to good accuracy for the stdevs, so we have a few alternatives:
	// Note: cross(1,A) is the same as colsum(A), but faster
	// Note: cross(A, A) is very fast, but we only need the main diagonals
	// [A: 1sec] stdevs = sqrt( (colsum(A:*A) - (cross(1, A) :^ 2 / N)) / (N-1) )
	// [B: .61s] stdevs = sqrt( (diagonal(cross(A, A))' - (cross(1, A) :^ 2 / N)) / (N-1) )
	// [C: .80s] stdevs = diagonal(sqrt(variance(A)))'
	// [D: .67s] means = cross(1, A) / N; stdevs =  sqrt(diagonal(crossdev(A, means, A, means))' / (N-1))

	assert(!isfleeting(A))
	N = rows(A)
	K = cols(A)

	stdevs = J(1, K, .)

	// (A) Very precise

	// (B) Precise
	//means = cross(1, A) / N
	//stdevs =  sqrt(diagonal(crossdev(A, means, A, means))' / (N-1))

	// (C) 20% faster; don't use it if you care about accuracy
	stdevs = sqrt( (diagonal(cross(A, A))' - (cross(1, A) :^ 2 / N)) / (N-1) )
	assert(!missing(stdevs)) // Shouldn't happen as we don't expect N==1

	stdevs = colmax(( stdevs \ J(1, K, 1e-3) ))
	A = A :/ stdevs
	return(stdevs)
}

// --------------------------------------------------------------------------
// Divide two row vectors but adjust the denominator if it's too small
// --------------------------------------------------------------------------
`RowVector' safe_divide(`RowVector' numerator, `RowVector' denominator, | `Real' epsi) {
	 // If the denominator goes below machine precision, the division explodes
	 if (args()<3 | epsi==.) epsi = epsilon(1)
	return( numerator :/ colmax(denominator \ J(1,cols(denominator),epsi)) )
}

// If X is not square...
// `Matrix' R
// real colvector tau, p

// _hqrdp(A, tau, R, p=.)
// B = hqrdmultq1t(A, tau, B)
// rank = _solveupper(R, B, tol)
// B = B[invorder(p),.]
// +- +-

// invsym(makesymmetric(..))

// --------------------------------------------------------------------------
// Robust solver for Ax=b
// --------------------------------------------------------------------------
// Mata utility for sequential use of solvers
// Default is cholesky;
// if that fails, use QR;
// if overridden, use QR.
// Author: Schaffer, Mark E <M.E.Schaffer@hw.ac.uk>
// --------------------------------------------------------------------------
// Warning:
// cholqrsolve calls qrsolve which calls _qrsolve which calls ...
// Does all the indirection makes it too slow to use within a panel?
// --------------------------------------------------------------------------
`Matrix' function reghdfe_cholqrsolve(`Matrix' A,
                                      `Matrix' B,
                                    | `Boolean' useqr)
{
	`Matrix' C
	if (args()<3 | useqr==.) useqr = 0

	if (!useqr) {
		C = cholsolve(A, B)
		if (hasmissing(C)) useqr = 1
	}

	if (useqr) {
		C = qrsolve(A, B)
	}

	return(C)
}

// --------------------------------------------------------------------------
// OLS Regression
// --------------------------------------------------------------------------
`Void' function reghdfe_post_ols(`FixedEffects' S,
                                 `Variables' X,
                                 `String' bname,
                                 `String' Vname,
                                 `String' nname,
                                 `String' rname,
                                 `String' dfrname)
{
	`Integer'				N
	`Integer'				rank
	`Integer'				df_r
	`Vector'				b
	`Matrix'				V
	`Variable'				resid
	`Real'					eps
	`Integer'				i

	if (S.timeit) timer_on(90)
	reghdfe_solve_ols(S, X, b=., V=., N=., rank=., df_r=., resid=.)
	if (S.timeit) timer_off(90)

	st_matrix(bname, b')

	// Add "o." prefix to omitted regressors
	eps = sqrt(epsilon(1))
	for (i=1; i<=rows(b); i++) {
		if (b[i]==0 & S.verbose > -1) {
			printf("{txt}note: %s omitted because of collinearity\n", tokens(S.indepvars)[i])
			//stata(sprintf("_ms_put_omit %s", indepvars[i]))
			//indepvars[i] = st_global("s(ospec)")
			// This is now one in reghdfe.ado with -_ms_findomitted-
		}
	}

	st_matrix(Vname, V)
	st_numscalar(nname, N)
	st_numscalar(rname, rank)
	st_numscalar(dfrname, df_r)

	// Need to save resids if saving FEs, even if temporarily
	if (S.residuals == "" & S.save_any_fe) {
		S.residuals = "__temp_reghdfe_resid__"
	}

	if (S.residuals != "") {
		if (S.verbose > 0) printf("\n{txt} ## Storing residuals in {res}%s{txt}\n\n", S.residuals)
		S.save_variable(S.residuals, resid, "Residuals")
	}
}

`Void' function reghdfe_solve_ols(`FixedEffects' S,
                                  `Variables' X,
                                  `Vector' b,
                                  `Matrix' V,
                                  `Integer' N,
                                  `Integer' rank,
                                  `Integer' df_r,
                                  `Vector' resid)
{
	// Hack: the first col of X is actually y!
	`Integer'				K, KK
	`Matrix'				xx, inv_xx, W, inv_V
	`Vector' 				xy, w
	`Integer'				used_df_r

	if (S.vcetype == "unadjusted" & S.weight_type=="pweight") S.vcetype = "robust"
	if (S.verbose > 0) printf("\n{txt} ## Solving least-squares regression of partialled-out variables\n\n")

	// Weight FAQ:
	// - fweight: obs. i represents w[i] duplicate obs. (there is no loss of info wrt to having the "full" dataset)
	// - aweight: obs. i represents w[i] distinct obs. that were mean-collapsed (so there is loss of info and hetero)
	//	 soln: normalize them so they sum to N (the true number of obs in our sample), and then treat them as fweight
	// - pweight: each obs. represents only one obs. from the pop, that was drawn from w[i] individuals
	//	          we want to make inference on the population, so if we interviewed 100% of the men and only 10% of women,
	//            then without weighting we would be over-representing men, which leads to a loss of efficiency +-+-
	// 			  it is the same as aweight + robust
	// We need to pick N and w
	N = rows(X) // Default; will change with fweights
	w = 1
	if (S.weight_type=="fweight") {
		N = sum(S.weight)
		w = S.weight
	}
	else if (S.weight_type=="aweight" | S.weight_type=="pweight") {
		w = S.weight * (N / sum(S.weight))
	}

	// Build core matrices
	if (S.timeit) timer_on(91)
	K = cols(X) - 1
	xx = quadcross(X, w, X)
	S.tss_within = xx[1,1]
	xy = K ? xx[| 2 , 1 \ K+1 , 1 |] : J(0, 1, .)
	xx = K ? xx[| 2 , 2 \ K+1 , K+1 |] : J(0, 0, .)
	if (S.timeit) timer_off(91)

	// This matrix indicates what regressors are not collinear
	assert_msg(cols(S.kept)==K+1, "partial_out() was run with a different set of vars")
	S.kept = K ? S.kept[2..K+1] : J(1, 0, .)

	// Bread of the robust VCV matrix
	// Compute this early so we can update the list of collinear regressors
	if (S.timeit) timer_on(95)
	inv_xx = reghdfe_rmcoll(tokens(S.indepvars), xx, S.kept)
	S.df_m = rank = K - diag0cnt(inv_xx)
	KK = rank + S.df_a
	S.df_r = N - KK // replaced when clustering
	if (S.timeit) timer_off(95)

	// Compute betas
	if (S.has_weights) {
		b = reghdfe_cholqrsolve(xx, xy, 1) // qr or chol?
	}
	else {
		// This is more numerically accurate but doesn't handle weights
		// See: http://www.stata.com/statalist/archive/2012-02/msg00956.html
		b = J(K, 1, 0)
		if (cols(S.kept)) {
			b[S.kept] = qrsolve(X[., 1:+S.kept], X[., 1])
		}
	}

	if (S.timeit) timer_on(92)
	resid = X * (1 \ -b) // y - X * b
	if (S.timeit) timer_off(92)

	if (S.timeit) timer_on(93)
	// From here on, X is only used for robust VCE
	if (S.vcetype == "unadjusted") {
		X = .
	}
	else {
		X = K ? X[., 2..K+1] : J(N, 0, .)
	}
	if (S.timeit) timer_off(93)

	if (S.timeit) timer_on(94)
	S.rss = quadcross(resid, w, resid) // do before reghdfe_robust() modifies w
	if (S.timeit) timer_off(94)

	// Compute full VCE
	if (S.timeit) timer_on(96)
	assert_msg(anyof( ("unadjusted", "robust", "cluster") , S.vcetype), S.vcetype)
	if (S.vcetype == "unadjusted") {
		if (S.verbose > 0) {
			printf("{txt}    - Small-sample-adjustment: q = N / (N-df_m-df_a) = %g / (%g - %g - %g) = %g\n", N, N, rank, S.df_a, N / S.df_r )
		}
		V = S.rss / S.df_r * inv_xx
	}
	else if (S.vcetype == "robust") {
		V = reghdfe_robust(S, X, inv_xx, resid, w, N, KK)
	}
	else {
		V = reghdfe_cluster(S, X, inv_xx, resid, w, N, KK)
	}
	if (S.timeit) timer_off(96)

	// Wald test: joint significance
	if (S.timeit) timer_on(97)
	inv_V = invsym(V[S.kept, S.kept]) // this might not be of full rank but numerical inaccuracies hide it
	if (diag0cnt(inv_V)) {
		if (S.verbose > -1) printf("{txt}(Warning: missing F statistic; dropped variables due to collinearity or too few clusters)\n")
		W = .
	}
	else if (length(b[S.kept])==0) {
		W = .
	}
	else {
		W = b[S.kept]' * inv_V * b[S.kept] / S.df_m
		if (missing(W) & S.verbose > -1) printf("{txt}(Warning: missing F statistic)\n")
	}
	if (S.timeit) timer_off(97)

	// V can be missing if b is completely absorbed by the FEs
	if (missing(V)) {
		if (S.verbose > 0) printf("{txt}    - VCE has missing values, setting it to zeroes (are your regressors all collinear?)\n")
		V = J(rows(V), rows(V), 0)
	}

	// Results
	S.title = "Linear regression"
	// S.model = "ols"

	if (S.weight_type!="") S.sumweights = quadsum(S.weight)

	used_df_r = N - KK - S.df_a_nested
	S.r2 = 1 - S.rss / S.tss
	S.r2_a = 1 - (S.rss / used_df_r) / (S.tss / (N - S.has_intercept ) )
	S.r2_within = 1 - S.rss / S.tss_within
	S.r2_a_within = 1 - (S.rss / used_df_r) / (S.tss_within / (used_df_r + rank))

	S.ll = - 0.5 * N * (1 + ln(2 * pi()) + ln(S.rss / N))
	S.ll_0 = - 0.5 * N * (1 + ln(2 * pi()) + ln(S.tss_within / N))

	S.rmse = sqrt(S.rss / used_df_r)
	if (used_df_r==0) S.rmse = sqrt(S.rss)
	S.F = W
	df_r = S.df_r // reghdfe_cluster might have updated it (this gets returned to the caller function)
}

// --------------------------------------------------------------------------
// Robust VCE
// --------------------------------------------------------------------------
// Advice: Delegate complicated regressions to -avar- and specialized routines
// BUGBUG: do we standardize X again? so V is well behaved?
// Notes:
// - robust is the same as cluster robust where cluster==_n
// - cluster just "collapses" X_i * e_i for each group, and builds M from that

`Matrix' reghdfe_robust(`FixedEffects' S,
                        `Variables' X,
						`Matrix' D,
						`Variable' resid,
						`Variable' w,
						`Integer' N,
						`Integer' K)
{
	`Matrix'				M, V
	`Integer'				dof_adj

	if (S.verbose > 0) printf("\n{txt} ## Estimating Robust Variance-Covariance Matrix of the Estimators (VCE)\n\n")
	if (S.verbose > 0) printf("{txt}    - VCE type: {res}%s{txt}\n", S.vcetype)
	if (S.verbose > 0) printf("{txt}    - Weight type: {res}%s{txt}\n", S.weight_type=="" ? "<none>" : S.weight_type)

	if (S.weight_type=="") {
		w = resid :^ 2
	}
	else if (S.weight_type=="fweight") {
		w = resid :^ 2 :* w
	}
	else if (S.weight_type=="aweight" | S.weight_type=="pweight") {
		w = (resid :* w) :^ 2
	}

	dof_adj = N / (N - K)
	M = quadcross(X, w, X)
	if (S.verbose > 0) {
		printf("{txt}    - Small-sample-adjustment: q = N / (N-df_m-df_a) = %g / (%g - %g - %g) = %g\n", N, N, K-S.df_a, S.df_a, N / (N-K) )
	}
	V = D * M * D * dof_adj
	return(V)
}

`Matrix' reghdfe_cluster(`FixedEffects' S,
                        `Variables' X,
						`Matrix' D,
						`Variable' resid,
						`Variable' w,
						`Integer' N,
						`Integer' K)
{
	`Matrix' 				M, V
	`Integer'				dof_adj, N_clust, df_r, nested_adj
	`Integer'				Q, q, g, sign, i, j
	pointer(`Factor') rowvector FPlist
	`FactorPointer'			FP
	`Varlist'				vars
	`String'				var, var_with_spaces
	`Boolean'				clustervar_is_absvar, required_fix
	`Matrix'				tuples
	`RowVector'				tuple
	`RowVector'				N_clust_list
	`Matrix'				joined_levels

	w = resid :* w
	//if (S.weight_type=="") {
	//	w = resid
	//}
	//else if (S.weight_type=="fweight") {
	//	w = resid :* w
	//}
	//else if (S.weight_type=="aweight" | S.weight_type=="pweight") {
	//	w = resid :* w
	//}

	vars = S.clustervars
	Q = cols(vars)
	if (S.verbose > 0) printf("\n{txt} ## Estimating Cluster Robust Variance-Covariance Matrix of the Estimators (VCE)\n\n")
	if (S.verbose > 0) printf("{txt}    - VCE type: {res}%s{txt} (%g-way clustering)\n", S.vcetype, Q)
	if (S.verbose > 0) printf("{txt}    - Cluster variables: {res}%s{txt}\n", invtokens(vars))
	if (S.verbose > 0) printf("{txt}    - Weight type: {res}%s{txt}\n", S.weight_type=="" ? "<none>" : S.weight_type)
	assert_msg(0 < Q & Q < 10)

	// Get or build factors associated with the clustervars
	FPlist = J(1, Q, NULL)
	N_clust_list = J(1, Q, .)
	for (q=1; q<=Q; q++) {
		var = vars[q]
		clustervar_is_absvar = 0
		for (g=1; g<=S.G; g++) {
			if (invtokens(S.factors[g].varlist, "#") == var) {
				clustervar_is_absvar = 1
				FP = &(S.factors[g])
				break
			}
		}
		var_with_spaces = subinstr(var, "#", " ")
		if (!clustervar_is_absvar) FP = &(factor(var_with_spaces, S.sample, ., "", ., ., ., 0))
		N_clust_list[q] = (*FP).num_levels
		if (S.verbose > 0) printf("{txt}    - {res}%s{txt} has {res}%g{txt} levels\n", var, N_clust_list[q])
		FPlist[q] = FP
	}

	// Build the meat part of the V matrix
	if (S.verbose > 0) printf("{txt}    - Computing the 'meat' of the VCE\n")
	M = J(cols(X), cols(X), 0)
	tuples = .
	for (q=1; q<=Q; q++) {
		tuples = reghdfe_choose_n_k(Q, q, tuples)
		sign = mod(q, 2) ? 1 : -1 // + with odd number of variables, - with even
		for (j=1; j<=rows(tuples); j++) {
			tuple = tuples[j, .]
			if (S.verbose > 0) printf("{txt}      - Level %g/%g; sublevel %g/%g; M = M %s ClusterVCE(%s)\n", q, Q, j, rows(tuples), sign > 0 ? "+" : "-" , invtokens(strofreal(tuple)))
			if (q==1) {
				assert(tuple==j)
				FP =  FPlist[j]
			}
			else if (q==2) {
				FP = &join_factors( *FPlist[tuple[1]] , *FPlist[tuple[2]] , ., ., 1)
			}
			else {
				joined_levels = (*FPlist[tuple[1]]).levels
				for (i=2; i<=cols(tuple); i++) {
					joined_levels = joined_levels, (*FPlist[tuple[i]]).levels
				}
				FP = &_factor(joined_levels, ., ., "", ., ., ., 0)
			}
			M = M + sign * reghdfe_vce_cluster_meat(FP, X, w)
		}
	}

	// Build VCE
	N_clust = min(N_clust_list)
	nested_adj = (S.df_a==0) // minor adj. so we match xtreg when the absvar is nested within cluster
	// (when ..nested.., df_a is zero so we divide N-1 by something that can potentially be N (!))
	// so we either add the 1 back, or change the numerator (and the N_clust-1 factor!)
	dof_adj = (N - 1) / (N - nested_adj - K) * N_clust / (N_clust - 1) // adjust for more than 1 cluster
	if (S.verbose > 0) {
		printf("{txt}    - Small-sample-adjustment: q = (%g - 1) / (%g - %g) * %g / (%g - 1) = %g\n", N, N, K+nested_adj, N_clust, N_clust, dof_adj)
	}
	V = D * M * D * dof_adj
	if (Q > 1) {
		required_fix = reghdfe_fix_psd(V)
		if (required_fix) printf("{txt}Warning: VCV matrix was non-positive semi-definite; adjustment from Cameron, Gelbach & Miller applied.\n")
	}

	// Store e()
	assert(!missing(S.df_r))
	df_r = N_clust - 1
	if (S.df_r > df_r) {
		S.df_r = df_r
	}
	else if (S.verbose > 0) {
		printf("{txt}    - Unclustered df_r (N - df_m - df_a = %g) are {it:lower} than clustered df_r (N_clust-1 = %g)\n", S.df_r, df_r)
		printf("{txt}      Thus, we set e(df_r) as the former.\n")
		printf("{txt}      This breaks consistency with areg but ensures internal consistency\n")
		printf("{txt}      between vce(robust) and vce(cluster _n)\n")
	}

	S.N_clust = N_clust
	S.N_clust_list = N_clust_list

	return(V)
}

`Matrix' reghdfe_vce_cluster_meat(`FactorPointer' FP,
                                  `Variables' X,
                                  `Variable' resid)
{
	`Integer'				i, N_clust
	`Variables'				X_sorted
	`Variable'				resid_sorted
	`Matrix'				X_tmp
	`Vector'				resid_tmp
	`RowVector'				Xe_tmp
	`Matrix'				M

	if (cols(X)==0) return(J(0,0,0))

	N_clust = (*FP).num_levels
	(*FP).panelsetup()
	X_sorted = (*FP).sort(X)
	resid_sorted = (*FP).sort(resid)
	M = J(cols(X), cols(X), 0)

	for (i=1; i<=N_clust; i++) {
		X_tmp = panelsubmatrix(X_sorted, i, (*FP).info)
		resid_tmp = panelsubmatrix(resid_sorted, i, (*FP).info)
		Xe_tmp = quadcross(1, resid_tmp, X_tmp) // Faster than colsum(e_tmp :* X_tmp)
		M = M + quadcross(Xe_tmp, Xe_tmp)
	}

	return(M)
}

// Enumerate all combinations of K integers from N integers
// Kroneker approach based on njc's tuples.ado
`Matrix' reghdfe_choose_n_k(`Integer' n, `Integer' k, `Matrix' prev_ans)
{
	`RowVector' v
	`Integer' q
	`Matrix' candidate
	`Matrix' ans
	v = 1::n
	if (k==1) return(v)

	q = rows(prev_ans)
	assert(q==comb(n, k-1))
	assert(cols(prev_ans)==k-1)
	candidate = v # J(q, 1, 1)
	candidate = candidate , J(n, 1, prev_ans)
	ans = select(candidate, candidate[., 1] :< candidate[., 2])
	return(ans)
}

// --------------------------------------------------------------------------
// Fix non-positive VCV
// --------------------------------------------------------------------------
// If the VCV matrix is not positive-semidefinite, use the fix from
// Cameron, Gelbach & Miller - Robust Inference with Multi-way Clustering (JBES 2011)
// 1) Use eigendecomposition V = U Lambda U' where U are the eigenvectors and Lambda = diag(eigenvalues)
// 2) Replace negative eigenvalues into zero and obtain FixedLambda
// 3) Recover FixedV = U * FixedLambda * U'
`Boolean' function reghdfe_fix_psd(`Matrix' V) {
	`Matrix'				U
	`Matrix'				lambda
	`Boolean' 				required_fix

	if (!issymmetric(V)) _makesymmetric(V)
	if (!issymmetric(V)) exit(error(505))
	symeigensystem(V, U=., lambda=.)
	if (min(lambda)<0) {
		lambda = lambda :* (lambda :>= 0)
		// V = U * diag(lambda) * U'
		V = quadcross(U', lambda, U')
		required_fix = 1
	}
	else {
		required_fix = 0
	}
	return(required_fix)
}

// --------------------------------------------------------------------------
// Remove collinear variables
// --------------------------------------------------------------------------
// Based on ivreg2's s_rmcoll2
`Matrix' reghdfe_rmcoll(`Varlist' varnames,
                        `Matrix' xx,
                        `RowVector' kept)
{
	`Integer'				K
	`Matrix'				inv_xx, smat
	`RowVector'				vl_drop, vl_keep

	assert(rows(xx)==cols(xx))
	K = cols(xx)
	inv_xx = K ? invsym(xx, 1..K) : J(0, 0, .)
	st_numscalar("r(k_omitted)", diag0cnt(inv_xx))
	smat = (diagonal(inv_xx) :== 0)'
	vl_drop = select(varnames, smat)
	vl_keep = select(varnames, !smat)
	if (cols(vl_keep)) st_global("r(varlist)", invtokens(vl_keep))
	if (cols(vl_drop)) st_global("r(omitted)", invtokens(vl_drop))
	kept = `selectindex'(!smat) // Return it, so we can exclude these variables from the joint Wald test
	return(inv_xx)
}

end


Reversed options

Reversed options

Correct the use of `noconfbars'

cap	program drop	ieimpgraph
	program define 	ieimpgraph
	preserve
	syntax varlist, [noconfbars TItle(string) save(string) ]

	mat beta_ = e(b)

	local counter = 0
	//local textfile = "Mrijan"
	//local date = "$S_DATE"

	qui{

		if "`confbars'" 			!= "" {
		local CONFINT_BAR 	= 0
		}
		else if "`confbars'" 			== "" {
		local CONFINT_BAR 	= 1
		}

		noi di "`CONFINT_BAR'"

		foreach var of local varlist{
			cap assert inlist(`var',0,1) | missing(`var')
				if _rc {
					noi display as error "{phang} The variable `var' is not a dummy. Treatment variable needs to be a dummy(0 or 1) variable. {p_end}"
					noi display ""
					error 149
					}
				else {
					}

		local counter = `counter' + 1
		di `counter'

		scalar coeff_`var' = _b[`var']
		//noi display coeff_`var'

		scalar coeff_se_`var' = _se[`var']
		//noi display coeff_se_`var'

		count if e(sample) == 1 & `var' == 1 //Count one tmt group at the time
		scalar obs_`var' = r(N)
		//noi display obs_`var'

	}

	local count: word count `varlist'
	local graphCount = `count' + 1
	//tokenize "`varlist'"
	//noi di `count'

	//Make all vars tempvars (maybe do later)
	//Make sure that missing is properly handled
	tempvar anyTMT
	egen `anyTMT' = rowmax(`varlist') if e(sample) == 1
	tempvar control
	gen `control' = (`anyTMT' == 0) if !missing(`anyTMT')

	sum `e(depvar)' if e(sample) == 1 & `control' == 1
	scalar ctl_N		= r(N)
	scalar ctl_mean	  	= r(mean)
	scalar ctl_mean_sd 	= r(sd)

		foreach var of local varlist {
			scalar  conf_int_min_`var'   =	coeff_`var'-(1.96*coeff_se_`var') + ctl_mean
			scalar  conf_int_max_`var'   =	coeff_`var'+(1.96*coeff_se_`var') + ctl_mean

			test `var'
			local star_`var' " "

			scalar  pvalue =r(p)
			if pvalue < 0.10 {
				local star_`var' "*"
			}
			if pvalue < 0.05 {
				local star_`var' "**"
			}
			if pvalue < 0.01 {
				local star_`var' "***"
			}
			*noi display "`star_`var''"
			scalar tmt_mean_`var' = ctl_mean + coeff_`var'
		}

	tempname 	newHandle
	tempfile	newTextFile
	cap file close 	`newHandle'
	file open  	`newHandle' using "`newTextFile'", text write append

	*Write headers and control value
	file write `newHandle' ///
		"order" 	_tab "xLabeled"	_tab "mean" _tab "coeff" _tab "conf_int_min" _tab "conf_int_max" _tab "obs" _tab "star" _n 	///
		%9.3f (1) _tab "Control"  _tab %9.3f (ctl_mean)      _tab  		 			_tab 		 			  	  _tab 			 				_tab %9.3f (ctl_N) 	   _tab			  	  _n

	tempvar newCounter
	gen `newCounter' = 2  //First tmt group starts at 2 (1 is control)

	foreach var in `varlist' {

		file write `newHandle' ///
			%9.3f (`newCounter') _tab "`var'" _tab %9.3f (tmt_mean_`var') _tab  %9.3f (coeff_`var')  _tab %9.3f (conf_int_min_`var') _tab %9.3f (conf_int_max_`var') _tab %9.3f (obs_`var')   _tab "`star_`var''"  _n

		replace `newCounter' = `newCounter' + 1
	}

	file close `newHandle'

	copy "`newTextFile'"  "mainMasterDofile.txt" , replace

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

		Create the graph

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

	insheet using mainMasterDofile.txt, clear

	local tmtGroupBars 	""
	local xAxisLabels 	`"xlabel( "'
	local legendLabels	`"lab(1 "`obsLabelControl'")"'
	local legendNumbers	""

	forval tmtGroupCount = 1/`graphCount' {

		************
		*Create the bar for this group

		colorPicker `tmtGroupCount' `graphCount'

		local tmtGroupBars `"`tmtGroupBars' (bar mean order if order == `tmtGroupCount', color("`r(color)'")) "'

		************
		*Create labels etc. for this group

		local obs 			= obs[`tmtGroupCount']
		local stars 		= star[`tmtGroupCount']
		local legendLabel 	= xlabeled[`tmtGroupCount']

		local xAxisLabels 	`"`xAxisLabels' `tmtGroupCount' "(N = `obs') `stars'" "'
		local legendLabels 	`"`legendLabels' lab(`tmtGroupCount' "`legendLabel'") "'
		local legendNumbers	`"`legendNumbers' `tmtGroupCount'"'
	}

	*Close or comple some strings
	local xAxisLabels `"`xAxisLabels' ,noticks labsize(medsmall)) "'
	local legendOption `"legend(order(`legendNumbers') `legendLabels')"'

	*local graphname gph_new

	*Create the confidence interval bars
	noi di "`CONFINT_BAR'"
	if `CONFINT_BAR' == 0 {
	//local confIntGraph == `"(scatter mean order,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black)), xtitle("") ytitle("`e(depvar)'") "'
	local confIntGraph = `", xtitle("") ytitle("`e(depvar)'") "'
	}
	else if `CONFINT_BAR' == 1 {
	local confIntGraph = `"(rcap conf_int_max conf_int_min order, lc(gs)) (scatter mean order,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black)), xtitle("") ytitle("`e(depvar)'")  "'
	}

	if "`save'" != "" {
		local saveOption saving("`save'", replace)
	}

	noi di `" graph twoway `tmtGroupBars' `confIntGraph' `legendOption' `xAxisLabels' `saveOption' title("`title'")   "'
	graph twoway `tmtGroupBars' `confIntGraph' `legendOption' `xAxisLabels' `saveOption' title("`title'")
	//noi di 	`"graph twoway `tmtGroupBars' `legendOption' (scatter mean order,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black))), xtitle("") ytitle("`e(depvar)'") `xAxisLabels' `saveOption' title("`title'")  "'

	restore
}

end

cap	program drop	colorPicker
	program define 	colorPicker , rclass

	args groupCount totalNumGroups

	if `totalNumGroups' == 2 {

		if `groupCount' == 1 return local color "215 25 28"
		if `groupCount' == 2 return local color "43 123 182"
	}
	else if `totalNumGroups' == 3 {

		if `groupCount' == 1 return local color "215 25 28"
		if `groupCount' == 2 return local color "255 255 191"
		if `groupCount' == 3 return local color "43 123 182"
	}
	else if `totalNumGroups' == 4 {

		if `groupCount' == 1 return local color "215 25 28"
		if `groupCount' == 2 return local color "255 255 191"
		if `groupCount' == 3 return local color "171 217 233"
		if `groupCount' == 4 return local color "43 123 182"
	}
	else {

		*For five or more colors we repeat the same pattern

		local colourNum = mod(`groupCount', 5)
		if `colourNum' == 1 return local color "215 25 28"
		if `colourNum' == 2 return local color "253 174 93"
		if `colourNum' == 3 return local color "255 255 191"
		if `colourNum' == 4 return local color "171 217 233"
		if `colourNum' == 0 return local color "43 123 182"

	}

end

cap	program drop	ieimpgraph
	program define 	ieimpgraph
	preserve
	syntax varlist, [noconfbars TItle(string) save(string) ]

	mat beta_ = e(b)

	local counter = 0
	//local textfile = "Mrijan"
	//local date = "$S_DATE"

	qui{

		if "`confbars'" 			!= "" {
		local CONFINT_BAR 	= 1
		}
		else if "`confbars'" 			== "" {
		local CONFINT_BAR 	= 0
		}

		noi di "`CONFINT_BAR'"

		foreach var of local varlist{
			cap assert inlist(`var',0,1) | missing(`var')
				if _rc {
					noi display as error "{phang} The variable `var' is not a dummy. Treatment variable needs to be a dummy(0 or 1) variable. {p_end}"
					noi display ""
					error 149
					}
				else {
					}

		local counter = `counter' + 1
		di `counter'

		scalar coeff_`var' = _b[`var']
		//noi display coeff_`var'

		scalar coeff_se_`var' = _se[`var']
		//noi display coeff_se_`var'

		count if e(sample) == 1 & `var' == 1 //Count one tmt group at the time
		scalar obs_`var' = r(N)
		//noi display obs_`var'

	}

	local count: word count `varlist'
	local graphCount = `count' + 1
	//tokenize "`varlist'"
	//noi di `count'

	//Make all vars tempvars (maybe do later)
	//Make sure that missing is properly handled
	tempvar anyTMT
	egen `anyTMT' = rowmax(`varlist') if e(sample) == 1
	tempvar control
	gen `control' = (`anyTMT' == 0) if !missing(`anyTMT')

	sum `e(depvar)' if e(sample) == 1 & `control' == 1
	scalar ctl_N		= r(N)
	scalar ctl_mean	  	= r(mean)
	scalar ctl_mean_sd 	= r(sd)

		foreach var of local varlist {
			scalar  conf_int_min_`var'   =	coeff_`var'-(1.96*coeff_se_`var') + ctl_mean
			scalar  conf_int_max_`var'   =	coeff_`var'+(1.96*coeff_se_`var') + ctl_mean

			test `var'
			local star_`var' " "

			scalar  pvalue =r(p)
			if pvalue < 0.10 {
				local star_`var' "*"
			}
			if pvalue < 0.05 {
				local star_`var' "**"
			}
			if pvalue < 0.01 {
				local star_`var' "***"
			}
			*noi display "`star_`var''"
			scalar tmt_mean_`var' = ctl_mean + coeff_`var'
		}

	tempname 	newHandle
	tempfile	newTextFile
	cap file close 	`newHandle'
	file open  	`newHandle' using "`newTextFile'", text write append

	*Write headers and control value
	file write `newHandle' ///
		"order" 	_tab "xLabeled"	_tab "mean" _tab "coeff" _tab "conf_int_min" _tab "conf_int_max" _tab "obs" _tab "star" _n 	///
		%9.3f (1) _tab "Control"  _tab %9.3f (ctl_mean)      _tab  		 			_tab 		 			  	  _tab 			 				_tab %9.3f (ctl_N) 	   _tab			  	  _n

	tempvar newCounter
	gen `newCounter' = 2  //First tmt group starts at 2 (1 is control)

	foreach var in `varlist' {

		file write `newHandle' ///
			%9.3f (`newCounter') _tab "`var'" _tab %9.3f (tmt_mean_`var') _tab  %9.3f (coeff_`var')  _tab %9.3f (conf_int_min_`var') _tab %9.3f (conf_int_max_`var') _tab %9.3f (obs_`var')   _tab "`star_`var''"  _n

		replace `newCounter' = `newCounter' + 1
	}

	file close `newHandle'

	copy "`newTextFile'"  "mainMasterDofile.txt" , replace

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

		Create the graph

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

	insheet using mainMasterDofile.txt, clear

	local tmtGroupBars 	""
	local xAxisLabels 	`"xlabel( "'
	local legendLabels	`"lab(1 "`obsLabelControl'")"'
	local legendNumbers	""

	forval tmtGroupCount = 1/`graphCount' {

		************
		*Create the bar for this group

		colorPicker `tmtGroupCount' `graphCount'

		local tmtGroupBars `"`tmtGroupBars' (bar mean order if order == `tmtGroupCount', color("`r(color)'")) "'

		************
		*Create labels etc. for this group

		local obs 			= obs[`tmtGroupCount']
		local stars 		= star[`tmtGroupCount']
		local legendLabel 	= xlabeled[`tmtGroupCount']

		local xAxisLabels 	`"`xAxisLabels' `tmtGroupCount' "(N = `obs') `stars'" "'
		local legendLabels 	`"`legendLabels' lab(`tmtGroupCount' "`legendLabel'") "'
		local legendNumbers	`"`legendNumbers' `tmtGroupCount'"'
	}

	*Close or comple some strings
	local xAxisLabels `"`xAxisLabels' ,noticks labsize(medsmall)) "'
	local legendOption `"legend(order(`legendNumbers') `legendLabels')"'

	*local graphname gph_new

	*Create the confidence interval bars
	noi di "`CONFINT_BAR'"
	if `CONFINT_BAR' == 0 {
	//local confIntGraph == `"(scatter mean order,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black)), xtitle("") ytitle("`e(depvar)'") "'
	local confIntGraph = `", xtitle("") ytitle("`e(depvar)'") "'
	}
	else if `CONFINT_BAR' == 1 {
	local confIntGraph = `"(rcap conf_int_max conf_int_min order, lc(gs)) (scatter mean order,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black)), xtitle("") ytitle("`e(depvar)'")  "'
	}

	if "`save'" != "" {
		local saveOption saving("`save'", replace)
	}

	noi di `" graph twoway `tmtGroupBars' `confIntGraph' `legendOption' `xAxisLabels' `saveOption' title("`title'")   "'
	graph twoway `tmtGroupBars' `confIntGraph' `legendOption' `xAxisLabels' `saveOption' title("`title'")
	//noi di 	`"graph twoway `tmtGroupBars' `legendOption' (scatter mean order,  msym(none)  mlabs(medium) mlabpos(10) mlabcolor(black))), xtitle("") ytitle("`e(depvar)'") `xAxisLabels' `saveOption' title("`title'")  "'

	restore
}

end

cap	program drop	colorPicker
	program define 	colorPicker , rclass

	args groupCount totalNumGroups

	if `totalNumGroups' == 2 {

		if `groupCount' == 1 return local color "215 25 28"
		if `groupCount' == 2 return local color "43 123 182"
	}
	else if `totalNumGroups' == 3 {

		if `groupCount' == 1 return local color "215 25 28"
		if `groupCount' == 2 return local color "255 255 191"
		if `groupCount' == 3 return local color "43 123 182"
	}
	else if `totalNumGroups' == 4 {

		if `groupCount' == 1 return local color "215 25 28"
		if `groupCount' == 2 return local color "255 255 191"
		if `groupCount' == 3 return local color "171 217 233"
		if `groupCount' == 4 return local color "43 123 182"
	}
	else {

		*For five or more colors we repeat the same pattern

		local colourNum = mod(`groupCount', 5)
		if `colourNum' == 1 return local color "215 25 28"
		if `colourNum' == 2 return local color "253 174 93"
		if `colourNum' == 3 return local color "255 255 191"
		if `colourNum' == 4 return local color "171 217 233"
		if `colourNum' == 0 return local color "43 123 182"

	}

end