Estimate quadratic model with demographics

clear
*2000:
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2000.csv", delimiter(",")
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) noquadratic
estat expenditure mu*
estat uncompensated ce*
outsheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_quaids_2000.csv", delimiter(",") replace

*2005:
clear
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2005.csv", delimiter(",")
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) noquadratic
estat expenditure mu*
estat uncompensated ce*
outsheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_quaids_2005.csv", delimiter(",") replace

*2011:
clear
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2011.csv", delimiter(",")
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) noquadratic
estat expenditure mu*
estat uncompensated ce*
outsheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_quaids_2011.csv", delimiter(",") replace

*If we introduce some selection, i.e. if we focus on those with w1>0:

clear
*2000:
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2000.csv", delimiter(",")
replace w1 = . if w1 == 0
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) noquadratic
estat expenditure mu*
estat uncompensated ce*
outsheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_quaids_2000.csv", delimiter(",") replace

*2005:
clear
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2005.csv", delimiter(",")
replace w1 = . if w1 == 0
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) noquadratic
estat expenditure mu*
estat uncompensated ce*
outsheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_quaids_2005.csv", delimiter(",") replace

*2011:
clear
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2011.csv", delimiter(",")
replace w1 = . if w1 == 0
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) noquadratic
estat expenditure mu*
estat uncompensated ce*
outsheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_quaids_2011.csv", delimiter(",") replace

*Still with selection, if we use demographics and the quadratic specification:

clear
*2000:
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2000.csv", delimiter(",")
replace w1 = . if w1 == 0
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) demographics(strate stalog nactifs nenfants dip14pr)
estat uncompensated, atmeans
matrix list r(uncompelas)
estat expenditure, atmeans
matrix list r(expelas)

*2005:
clear
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2005.csv", delimiter(",")
replace w1 = . if w1 == 0
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) demographics(strate stalog nactifs nenfants dip14pr) vce(bootstrap)
estat uncompensated, atmeans
matrix list r(uncompelas)
estat expenditure, atmeans
matrix list r(expelas)

*2011:
clear
insheet using "C:\Users\thomas.douenne\Documents\GitHub\openfisca-france-indirect-taxation\openfisca_france_indirect_taxation\almost_ideal_demand_system\data_frame_energy_2011.csv", delimiter(",")
replace w1 = . if w1 == 0
quaids w1-w3, anot(5) prices(p1-p3) expenditure(depenses_par_uc) demographics(strate stalog nactifs nenfants dip14pr)
estat uncompensated, atmeans
matrix list r(uncompelas)
estat expenditure, atmeans
matrix list r(expelas)

*To compute bootstrap standard errors, add vce(bootstrap) at the end of the quaids line


Better utilize capture blocks to restore correctly data/PWD/S_FN on errors

*! version 1.16.9000 17may2016
*! PARALLEL: Stata module for parallel computing
*! by George G. Vega [cre,aut], Brian Quistorff [ctb]
*!
*! Project website:
*!   https://github.com/gvegayon/parallel
*! Bug reports:
*!   https://github.com/gvegayon/parallel/issues
/*
Copyright (c) 2014  <George G. Vega>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// Syntax parser
program def parallel
    version 11.0

	// Checks wether if is parallel prefix or not
	if  (regexm(`"`0'"', "^(do|clean|setclusters|break|version|append|printlog|viewlog)")) {
	/* If not prefix */
		parallel_`0'
	}
	else if (regexm(`"`0'"', "^(bs|sim)[,]?[\s ]?")) {
	/* Prefix bootstrap or simulate */
		local cmd = regexs(1)
		local 0   = regexr(`"`0'"', "^(bs|sim)", "")
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_`cmd' `0', argopt(`argopt') `options'
	}
	else if (regexm(`"`0'"',"^([,]?.*[:])")) {
	/* if prefix */
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		// Gets the options (if these exists) of parallel
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_do `0', `options' prefix argopt(`argopt')
	}
	else {
		local val = regexm(`"`0'"', "^([a-zA-Z0-9_]*).*")
		local subcommand = regexs(1)
		di as result `"-`subcommand'-"' as error " invalid parallel subcommand" as text " ({stata h parallel:{it:help parallel}})"
		exit 198
	}
end

/* Returns the version of parallel */
program def parallel_version, rclass
	version 11.0
	di as result "parallel" as text " Stata module for parallel computing"
	di as result "vers" as text " 1.16.9000 17may2016"
	di as result "auth" as text " George G. Vega [cre,aut], Brian Quistorff [ctb]"

	return local pll_vers = "1.16.9000"
end

/* Take a look to logfiles */
program def parallel_printlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(print)
end

program def parallel_viewlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(view)
end

program def parallel_checklog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)] action(string)

	if ("`event'"=="") local event = "$LAST_PLL_ID"

	if ("`event'"!=".") {
		/* By default uses 1 */
		if ("`pll_instance'" == "") local pll_instance 1

		local pll_instance : di %04.0f `pll_instance'

		/* Does de file exists */
		local logname = "__pll`event'_do`pll_instance'.log"

		if (c(os) == "Windows") local logname = "`c(tmpdir)'`logname'"
		else local logname = "`c(tmpdir)'/`logname'"

		/* If the logfile does not exists, do nothing*/
		cap confirm file "`logname'"
		if (_rc) {
			di as result "No logfile for instance -`pll_instance'- of parallel process -`event'- found"
			exit
		}

		/* Showing the log in screen */
		if ("`action'"=="print") {
			di as result "{hline 80}"
			di as result %~80s "beginning of file -`logname'-"
			di as result "{hline 80}"
			type `"`logname'"'
			di as result "{hline 80}"
			di as result %~80s "end of file -`logname'-"
			di as result "{hline 80}"
		}
		else view `"`logname'"'
	}
	else {
		di as error "It seems that you haven't use -parallel- yet."
		exit 601
	}

end

////////////////////////////////////////////////////////////////////////////////
// Splits the dataset into clusters
program def parallel_spliter
	version 11.0
	syntax [namelist(name=xtstructure)] [,parallelid(string) sorting(integer 0) force(integer 0) keepusing(varlist) orig_cl_local(string)]
	//args xtstructure parallelid Sorting Force

	if length("$PLL_CLUSTERS") == 0 {
		di as err "Number of clusters not fixed." as text " Set the number of clusters with " _newline as err "-{cmd:parallel setclusters #}-"
		exit 198
	}

	//quietly {
		if (length("`xtstructure'")) {

			/* Checks wheather if the data is in the correct sorting */
			if !`sorting' & !`force' {
				error 5
			}

			/* Checking the data types */
			foreach var of varlist `xtstructure' {
				capture confirm string var `var'
				if (`=_rc') local numvars `numvars' `var'
				else local strvars `strvars' `var'
			}
			/* Do we have too many clusters? */
			egen _`parallelid'grp = group(`xtstructure'), missing
			summ _`parallelid'grp, meanonly
			local max_n_cl = `r(max)'
			drop _`parallelid'grp
		}
		else {
			local max_n_cl = _N
		}

		/* Do we have too many clusters? */
		if (`max_n_cl'<$PLL_CLUSTERS){
			c_local `orig_cl_local' ${PLL_CLUSTERS}
			global PLL_CLUSTERS = `max_n_cl'
			di "Small workload/num groups. Temporarily setting number of clusters to ${PLL_CLUSTERS}"
		}

		gen _`parallelid'cut = .

		/* Checking types of data */
		if (length("`numvars'")) local numvars st_data(.,"`numvars'")
		else local numvars J(0,0,.)

		if (length("`strvars'")) local strvars st_sdata(.,"`strvars'")
		else local strvars J(0,0,"")

		/* Processing in MATA */
		mata: st_store(., "_`parallelid'cut", parallel_divide_index(`numvars', `strvars'))

		if (length("`keepusing'")) {
			keep _`parallelid'cut `keepusing'
			gen __pllnobs`parallelid' = _n
		}

		qui save __pll`parallelid'_dataset, replace
		drop _all
	//}

end

////////////////////////////////////////////////////////////////////////////////
// MAIN PROGRAM
// There are three things we need to possibly restore on exit
// - Data/S_FN (this is taken care of by -preserve-)
// - PWD (non-indented capture block)
// - Temporaliy changed number of clusters (non-indented capture block)
program def parallel_do, rclass

	version 11.0

	#delimit ;
	syntax anything(name=dofile equalok everything)
		[, by(string)
		Keep
		KEEPLast
		prefix
		Force
		PROGrams(string)
		Mata
		NOGlobals
		KEEPTiming
		Seeds(string)
		NOData
		Randtype(string)
		Timeout(integer 60)
		PROCessors(integer 0)
		argopt(string)
		KEEPUsing(string)
		SETparallelid(string)
		];
	#delimit cr

	if length("$PLL_CLUSTERS") == 0 {
		di "{error:You haven't set the number of clusters}" _n "{error:Please set it with: {cmd:parallel setclusters} {it:#}}"
		exit 198
	}

	if `c(noisily)'==0 & "`programs'"!="" {
		di "{error:If you want to pass programs in memory (using the option programs)}" _n "{error:then the program must be run noisily.}"
		exit 198
	}

	// Initial checks
	foreach opt in macrolist keep keeplast prefix force mata noglobals keeptiming nodata {
		local `opt' = length("``opt''") > 0
	}

	/* Randtype */
	if ("`randtype'" == "") local randtype = "datetime"

	/* If no data parsing has to be done (because of no data!) */
	if (!(c(N)*c(k))) local nodata 1

	if (!`keeptiming') {
		timer clear 98
		timer clear 99
	}

	timer on 98

	// If they use ,programs() then we need "." in the path
	if "`programs'"!=""{

	}

	// Delets last parallel instance ran
	if (`keeplast' & length("`r(pll_id)'")) cap parallel_clean, e(`r(pll_id)') nologs

	if length("`by'") != 0 {
		local sortlist: sortedby
		local sorting = regexm("`sortlist'","^`by'")
	}
	else local sorting = 0

	// Gets the directory where to work at
	if (!`prefix') {

		/* First checks if the file exists */
		mata: parallel_normalizepath(`"`dofile'"',1)
		local pll_dir = "`filedir'"
		local dofile = "`filename'"
	}
	else local pll_dir = c(pwd)+"/"

	local initialdir = c(pwd)
	qui cd "`pll_dir'"
	capture noisily { //Start capture block for PWD

	/* Creates a unique ID for the process and secures it */
	if ("`setparallelid'"=="") mata: parallel_sandbox(5)
	else local parallelid = "`setparallelid'"

	/* Generates database clusters */
	capture noisily { //Start capture block for PLL_CLUSTERS
	if (!`nodata'){
		local sfn = "$S_FN" // be able to "fake" restore this
		preserve

		parallel_spliter `by' , parallelid(`parallelid') sorting(`sorting') force(`force') keepusing(`keepusing') orig_cl_local(orig_PLL_CLUSTERS)
	}

	global LAST_PLL_ID = "`parallelid'"
	global LAST_PLL_DIR = "`pll_dir'"
	global LAST_PLL_N = $PLL_CLUSTERS

	/* Starts building the files */
	local work_around_no_cwd = 0
	quietly {

		/* Saves mata objects */
		if (`mata') {
			mata: mata mlib create l__pll`parallelid'_mlib, replace
			cap mata: mata mlib add l__pll`parallelid'_mlib *()
			cap which l__pll`parallelid'_mlib.mlib
			if _rc!=0{
				local work_around_no_cwd = 1
				noi di "Note: In order to pass mata objects to the clusters, they are saved to a temporary mlib file in the current directly."
				noi di "      Your ado-path doesn't contain the current directory."
				noi di "      We have added the current directory to the end of the ado-path for the clusters.."
			}
			cap mata: mata matsave __pll`parallelid'_mata.mmat *, replace
			if (`=_rc') local matasave = 0
			else local matasave = 1
		}
		else local matasave = 0
	}

	/* Writing the dofile */
	mata: st_local("errornum", strofreal(parallel_write_do(strtrim(`"`dofile' `argopt'"'), "`parallelid'", $PLL_CLUSTERS, `prefix', `matasave', !`noglobals', "`seeds'", "`randtype'", `nodata', "`pll_dir'", "`programs'", `processors',`work_around_no_cwd')))
	if (`errornum') {
		/* Removes the sandbox file (unprotect the files) */
		mata: parallel_sandbox(2, "`parallelid'")

		error `errornum'
	}

	timer off 88
	cap timer list
	if (r(t88) == .) local pll_t_setu = 0
	else local pll_t_setu = r(t88)

	/* Running the dofiles */
	timer on 99
	mata: st_local("nerrors", strofreal(parallel_run("`parallelid'",$PLL_CLUSTERS,`"$PLL_STATA_PATH"',`=`timeout'*1000')))
	timer off 99

	cap timer list
	if (r(t99) == .) local pll_t_calc = 0
	else local pll_t_calc = r(t99)
	local pll_t_reps = r(nt99)

	timer on 97
	// Paste the databases
	if (!`nodata' & !`nerrors') {
		parallel_fusion `parallelid', clusters($PLL_CLUSTERS) keepusing(`keepusing')
		cap drop _`parallelid'cut

		global S_FN = "`sfn'" // Restores original S_FN (file name) value
		restore, not
	}

	return scalar pll_n = $PLL_CLUSTERS
	} //End capture block for $PLL_CLUSTERS
	if _rc {
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		exit _rc
	}
	if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'

	/* Removes the sandbox file (unprotect the files) */
	if ("`setparallelid'" == "") {
		mata: parallel_sandbox(2, "`parallelid'")
		if (!`keep' & !`keeplast') parallel_clean, e("`parallelid'") nologs
	}

	} //End capture block for PWD
	if _rc {
		qui cd "`initialdir'"
		exit _rc
	}
	qui cd "`initialdir'"

	timer off 97
	cap timer list
	if (r(t97) == .) local pll_t_fini = 0
	else local pll_t_fini = r(t97)

	qui timer list
	return local  pll_seeds="`pllseeds'"
	return scalar pll_errs = `nerrors'
	return local  pll_dir "`pll_dir'"
	return scalar pll_t_reps = `pll_t_reps'
	return scalar pll_t_setu = `pll_t_setu'
	return scalar pll_t_calc = `pll_t_calc'
	return scalar pll_t_fini = `pll_t_fini'
	return local pll_id = "`parallelid'"

	if `nerrors' {
		di as err "`nerrors' child processes encountered errors. Throwing last error."
		exit `pll_last_error'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Cleans all files generated by parallel
// If you want to keep the logs, then specify -, nologs-
program def parallel_clean
	version 11.0
	syntax [, Event(string) All Force nologs]

	if (length("`event'") != 0 & length("`all'") != 0) {
		di as error `"invalid syntax: Using -pll_id- and -all- jointly is not allowed."'
		exit 198
	}
	local do_logs = ("`logs'"!="nologs")
	mata: parallel_clean("`event'", `=length("`all'") > 0', `=length("`force'") > 0', `do_logs')
end

////////////////////////////////////////////////////////////////////////////////
// Sets the number of clusters as a global macro
program parallel_setclusters
	version 11.0
	syntax anything(name=nclusters)  [, Force Statapath(string asis) Gateway(string) Includefile(string) procexec(int 2)]

	local nclusters = int(real(`"`nclusters'"'))
	_assert `nclusters'!=., msg(`"Not allowed: "#" Should be a number"') rc(109)
	_assert `nclusters'>0,  msg(`"Not allowed: "#" Should be positive"') rc(109)
	_assert inlist(`procexec',0,1,2), msg("procexec() must be 0, 1, or 2") rc(198)

	global USE_PROCEXEC = `procexec'

	local force = (length("`force'")>0)
	mata: parallel_setclusters(`nclusters', `force')
	mata: st_local("error", strofreal(parallel_setstatapath(`"`statapath'"', `force')))
	_assert (!`error'), msg("Can not set Stata directory, try using -statapath()- option") rc(`error')

	if "`c(mode)'"=="batch" & "`c(os)'"=="Windows" & "$USE_PROCEXEC"=="0" {
		if `"`gateway'"'=="" local gateway "pll_gateway.sh"

		cap confirm file `"`gateway'"'
		_assert `=!_rc', msg(`"On Windows batch-mode with command-line process execution, requires a gateway file is required but not found (path: `c(pwd)')"') rc(`=_rc')
		global PLL_GATEWAY_FNAME `"`gateway'"'
	}

	if `"`includefile'"'!=""{
		cap confirm file `"`includefile'"'
		_assert `=!_rc', msg("Can not find the include file (`includefile').") msg(`=_rc')
		global PLL_INCLUDE_FILE `"`includefile'"'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Exports

// Exports a copy of programs
program def program_export
	version 11.0
	syntax using/ [,Programlist(string) Inname(string)]

	mata: program_export("`using'", "`programlist'", "`inname'")
end

////////////////////////////////////////////////////////////////////////////////
// Appends the clusterized dataset
program def parallel_fusion
	version 11.0
	syntax anything(name=parallelid) , clusters(integer) [keepusing(string)]

	cap use "__pll`parallelid'_dta0001.dta", clear
	if (_rc){
		di "{error:No dataset for instance 0001.}"
		exit _rc
	}
	local sortlist: sortedby

	forval i = 2/`clusters' {
		cap append using `"__pll`parallelid'_dta`=string(`i',"%04.0f")'.dta"'
		if (_rc){
			di "{error:No dataset for instance `=string(`i',"%04.0f")'.}"
			exit _rc
		}
	}

	/* If it just used a set of variables */
	if (length("`keepusing'")) {
		qui merge 1:1 __pllnobs`parallelid' using __pll`parallelid'_dataset, keep(3) nogen
		drop __pllnobs`parallelid'
	}

	//restore the sort
	if "`sortlist'"!="" sort `sortlist'

end

////////////////////////////////////////////////////////////////////////////////
// Checks whether the user pressed break inside a loop
program def parallel_break
	version 11.0
	mata: parallel_break()
end

*! version 1.16.9000 17may2016
*! PARALLEL: Stata module for parallel computing
*! by George G. Vega [cre,aut], Brian Quistorff [ctb]
*!
*! Project website:
*!   https://github.com/gvegayon/parallel
*! Bug reports:
*!   https://github.com/gvegayon/parallel/issues
/*
Copyright (c) 2014  <George G. Vega>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// Syntax parser
program def parallel
    version 11.0

	// Checks wether if is parallel prefix or not
	if  (regexm(`"`0'"', "^(do|clean|setclusters|break|version|append|printlog|viewlog)")) {
	/* If not prefix */
		parallel_`0'
	}
	else if (regexm(`"`0'"', "^(bs|sim)[,]?[\s ]?")) {
	/* Prefix bootstrap or simulate */
		local cmd = regexs(1)
		local 0   = regexr(`"`0'"', "^(bs|sim)", "")
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_`cmd' `0', argopt(`argopt') `options'
	}
	else if (regexm(`"`0'"',"^([,]?.*[:])")) {
	/* if prefix */
		gettoken x 0 : 0, parse(":") bind
		local 0 = regexr(`"`0'"', "^[:]", "")
		// Gets the options (if these exists) of parallel
		gettoken x options : x, parse(",") bind

		gettoken 0 argopt : 0, parse(",") bind
		parallel_do `0', `options' prefix argopt(`argopt')
	}
	else {
		local val = regexm(`"`0'"', "^([a-zA-Z0-9_]*).*")
		local subcommand = regexs(1)
		di as result `"-`subcommand'-"' as error " invalid parallel subcommand" as text " ({stata h parallel:{it:help parallel}})"
		exit 198
	}
end

/* Returns the version of parallel */
program def parallel_version, rclass
	version 11.0
	di as result "parallel" as text " Stata module for parallel computing"
	di as result "vers" as text " 1.16.9000 17may2016"
	di as result "auth" as text " George G. Vega [cre,aut], Brian Quistorff [ctb]"

	return local pll_vers = "1.16.9000"
end

/* Take a look to logfiles */
program def parallel_printlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(print)
end

program def parallel_viewlog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)]
	parallel_checklog `pll_instance', e(`event') action(view)
end

program def parallel_checklog
	version 11.0
	syntax [anything(name=pll_instance)] , [Event(string)] action(string)

	if ("`event'"=="") local event = "$LAST_PLL_ID"

	if ("`event'"!=".") {
		/* By default uses 1 */
		if ("`pll_instance'" == "") local pll_instance 1

		local pll_instance : di %04.0f `pll_instance'

		/* Does de file exists */
		local logname = "__pll`event'_do`pll_instance'.log"

		if (c(os) == "Windows") local logname = "`c(tmpdir)'`logname'"
		else local logname = "`c(tmpdir)'/`logname'"

		/* If the logfile does not exists, do nothing*/
		cap confirm file "`logname'"
		if (_rc) {
			di as result "No logfile for instance -`pll_instance'- of parallel process -`event'- found"
			exit
		}

		/* Showing the log in screen */
		if ("`action'"=="print") {
			di as result "{hline 80}"
			di as result %~80s "beginning of file -`logname'-"
			di as result "{hline 80}"
			type `"`logname'"'
			di as result "{hline 80}"
			di as result %~80s "end of file -`logname'-"
			di as result "{hline 80}"
		}
		else view `"`logname'"'
	}
	else {
		di as error "It seems that you haven't use -parallel- yet."
		exit 601
	}

end

////////////////////////////////////////////////////////////////////////////////
// Splits the dataset into clusters
program def parallel_spliter
	version 11.0
	syntax [namelist(name=xtstructure)] [,parallelid(string) sorting(integer 0) force(integer 0) keepusing(varlist) orig_cl_local(string)]
	//args xtstructure parallelid Sorting Force

	if length("$PLL_CLUSTERS") == 0 {
		di as err "Number of clusters not fixed." as text " Set the number of clusters with " _newline as err "-{cmd:parallel setclusters #}-"
		exit 198
	}

	quietly {
		if (length("`xtstructure'")) {

			/* Checks wheather if the data is in the correct sorting */
			if !`sorting' & !`force' {
				error 5
			}

			/* Checking the data types */
			foreach var of varlist `xtstructure' {
				capture confirm string var `var'
				if (`=_rc') local numvars `numvars' `var'
				else local strvars `strvars' `var'
			}
			/* Do we have too many clusters? */
			egen _`parallelid'grp = group(`xtstructure'), missing
			summ _`parallelid'grp, meanonly
			local max_n_cl = `r(max)'
			drop _`parallelid'grp
		}
		else {
			local max_n_cl = _N
		}

		/* Do we have too many clusters? */
		if (`max_n_cl'<$PLL_CLUSTERS){
			c_local `orig_cl_local' ${PLL_CLUSTERS}
			global PLL_CLUSTERS = `max_n_cl'
			di "Small workload/num groups. Temporarily setting number of clusters to ${PLL_CLUSTERS}"
		}

		gen _`parallelid'cut = .

		/* Checking types of data */
		if (length("`numvars'")) local numvars st_data(.,"`numvars'")
		else local numvars J(0,0,.)

		if (length("`strvars'")) local strvars st_sdata(.,"`strvars'")
		else local strvars J(0,0,"")

		/* Processing in MATA */
		capture {
		mata: st_store(., "_`parallelid'cut", parallel_divide_index(`numvars', `strvars'))
		}
		local main_rc = _rc
		if `main_rc' {
			cap drop _`parallelid'cut
			error `main_rc'
		}

		if (length("`keepusing'")) {
			keep _`parallelid'cut `keepusing'
			gen __pllnobs`parallelid' = _n
		}

		save __pll`parallelid'_dataset, replace
		drop _all
	}

end

////////////////////////////////////////////////////////////////////////////////
// MAIN PROGRAM
program def parallel_do, rclass

	version 11.0

	#delimit ;
	syntax anything(name=dofile equalok everything)
		[, by(string)
		Keep
		KEEPLast
		prefix
		Force
		PROGrams(string)
		Mata
		NOGlobals
		KEEPTiming
		Seeds(string)
		NOData
		Randtype(string)
		Timeout(integer 60)
		PROCessors(integer 0)
		argopt(string)
		KEEPUsing(string)
		SETparallelid(string)
		];
	#delimit cr

	if length("$PLL_CLUSTERS") == 0 {
		di "{error:You haven't set the number of clusters}" _n "{error:Please set it with: {cmd:parallel setclusters} {it:#}}"
		exit 198
	}

	if `c(noisily)'==0 & "`programs'"!="" {
		di "{error:If you want to pass programs in memory (using the option programs)}" _n "{error:then the program must be run noisily.}"
		exit 198
	}

	// Initial checks
	foreach opt in macrolist keep keeplast prefix force mata noglobals keeptiming nodata {
		local `opt' = length("``opt''") > 0
	}

	/* Randtype */
	if ("`randtype'" == "") local randtype = "datetime"

	/* If no data parsing has to be done (because of no data!) */
	if (!(c(N)*c(k))) local nodata 1

	if (!`keeptiming') {
		timer clear 98
		timer clear 99
	}

	timer on 98

	// If they use ,programs() then we need "." in the path
	if "`programs'"!=""{

	}

	// Delets last parallel instance ran
	if (`keeplast' & length("`r(pll_id)'")) cap parallel_clean, e(`r(pll_id)') nologs

	// Gets some global values
	local sfn = "$S_FN"

	// Gets the directory where to work at
	if (!`prefix') {

		/* First checks if the file exists */
		mata: parallel_normalizepath(`"`dofile'"',1)
		local pll_dir = "`filedir'"
		local dofile = "`filename'"
	}
	else local pll_dir = c(pwd)+"/"

	local initialdir = c(pwd)
	qui cd "`pll_dir'"

	if length("`by'") != 0 {
		local sortlist: sortedby
		local sorting = regexm("`sortlist'","^`by'")
	}
	else local sorting = 0

	/* Creates a unique ID for the process and secures it */
	if ("`setparallelid'"=="") mata: parallel_sandbox(5)
	else local parallelid = "`setparallelid'"

	/* Generates database clusters */
	if (!`nodata') parallel_spliter `by' , parallelid(`parallelid') sorting(`sorting') force(`force') keepusing(`keepusing') orig_cl_local(orig_PLL_CLUSTERS)

	global LAST_PLL_ID = "`parallelid'"
	global LAST_PLL_DIR = "`pll_dir'"
	global LAST_PLL_N = $PLL_CLUSTERS

	/* Starts building the files */
	local work_around_no_cwd = 0
	quietly {

		/* Saves mata objects */
		if (`mata') {
			mata: mata mlib create l__pll`parallelid'_mlib, replace
			cap mata: mata mlib add l__pll`parallelid'_mlib *()
			cap which l__pll`parallelid'_mlib.mlib
			if _rc!=0{
				local work_around_no_cwd = 1
				noi di "Note: In order to pass mata objects to the clusters, they are saved to a temporary mlib file in the current directly."
				noi di "      Your ado-path doesn't contain the current directory."
				noi di "      We have added the current directory to the end of the ado-path for the clusters.."
			}
			cap mata: mata matsave __pll`parallelid'_mata.mmat *, replace
			if (`=_rc') local matasave = 0
			else local matasave = 1
		}
		else local matasave = 0
	}

	/* Writing the dofile */
	mata: st_local("errornum", strofreal(parallel_write_do(strtrim(`"`dofile' `argopt'"'), "`parallelid'", $PLL_CLUSTERS, `prefix', `matasave', !`noglobals', "`seeds'", "`randtype'", `nodata', "`pll_dir'", "`programs'", `processors',`work_around_no_cwd')))

	/* Checking if every thing is ok */
	if (`errornum') {
		if (!`nodata') {
			qui use __pll`parallelid'_dataset, clear

			// Restores original S_FN (file name) value
			global S_FN = "`sfn'"

			/* Removing the cut variable if somehow still there*/
			cap drop _`parallelid'cut
		}

		/* Removes the sandbox file (unprotect the files) */
		mata: parallel_sandbox(2, "`parallelid'")

		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		error `errornum'
	}

	timer off 88
	cap timer list
	if (r(t88) == .) local pll_t_setu = 0
	else local pll_t_setu = r(t88)

	/* Running the dofiles */
	timer on 99
	mata: st_local("nerrors", strofreal(parallel_run("`parallelid'",$PLL_CLUSTERS,`"$PLL_STATA_PATH"',`=`timeout'*1000')))
	timer off 99

	/* If parallel finished with an error it restores the dataset */
	if (`nerrors' & !`nodata') {
		qui use __pll`parallelid'_dataset, clear
		global S_FN = "`sfn'"
		cap drop _`parallelid'cut
	}

	cap timer list
	if (r(t99) == .) local pll_t_calc = 0
	else local pll_t_calc = r(t99)
	local pll_t_reps = r(nt99)

	timer on 97
	// Paste the databases
	if (!`nodata' & !`nerrors') {
		parallel_fusion `parallelid', clusters($PLL_CLUSTERS) keepusing(`keepusing')

		// Restores original S_FN (file name) value
		global S_FN = "`sfn'"

		cap drop _`parallelid'cut
	}

	/* Removes the sandbox file (unprotect the files) */
	if ("`setparallelid'" == "") {
		mata: parallel_sandbox(2, "`parallelid'")
		if (!`keep' & !`keeplast') parallel_clean, e("`parallelid'") nologs
	}

	timer off 97
	cap timer list
	if (r(t97) == .) local pll_t_fini = 0
	else local pll_t_fini = r(t97)

	qui timer list
	return local  pll_seeds="`pllseeds'"
	local pllseeds ""
	return scalar pll_errs = `nerrors'
	return local  pll_dir "`pll_dir'"
	return scalar pll_t_reps = `pll_t_reps'
	return scalar pll_t_setu = `pll_t_setu'
	return scalar pll_t_calc = `pll_t_calc'
	return scalar pll_t_fini = `pll_t_fini'
	return local pll_id = "`parallelid'"
	return scalar pll_n = $PLL_CLUSTERS

	qui cd "`initialdir'"
	if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
	if `nerrors' {
		di as err "`nerrors' child processes encountered errors. Throwing last error."
		if "`orig_PLL_CLUSTERS'"!="" global PLL_CLUSTERS=`orig_PLL_CLUSTERS'
		exit `pll_last_error'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Cleans all files generated by parallel
// If you want to keep the logs, then specify -, nologs-
program def parallel_clean
	version 11.0
	syntax [, Event(string) All Force nologs]

	if (length("`event'") != 0 & length("`all'") != 0) {
		di as error `"invalid syntax: Using -pll_id- and -all- jointly is not allowed."'
		exit 198
	}
	local do_logs = ("`logs'"!="nologs")
	mata: parallel_clean("`event'", `=length("`all'") > 0', `=length("`force'") > 0', `do_logs')
end

////////////////////////////////////////////////////////////////////////////////
// Sets the number of clusters as a global macro
program parallel_setclusters
	version 11.0
	syntax anything(name=nclusters)  [, Force Statapath(string asis) Gateway(string) Includefile(string) procexec(int 2)]

	local nclusters = int(real(`"`nclusters'"'))
	_assert `nclusters'!=., msg(`"Not allowed: "#" Should be a number"') rc(109)
	_assert `nclusters'>0,  msg(`"Not allowed: "#" Should be positive"') rc(109)
	_assert inlist(`procexec',0,1,2), msg("procexec() must be 0, 1, or 2") rc(198)

	global USE_PROCEXEC = `procexec'

	local force = (length("`force'")>0)
	mata: parallel_setclusters(`nclusters', `force')
	mata: st_local("error", strofreal(parallel_setstatapath(`"`statapath'"', `force')))
	_assert (!`error'), msg("Can not set Stata directory, try using -statapath()- option") rc(`error')

	if "`c(mode)'"=="batch" & "`c(os)'"=="Windows" & "$USE_PROCEXEC"=="0" {
		if `"`gateway'"'=="" local gateway "pll_gateway.sh"

		cap confirm file `"`gateway'"'
		_assert `=!_rc', msg(`"On Windows batch-mode with command-line process execution, requires a gateway file is required but not found (path: `c(pwd)')"') rc(`=_rc')
		global PLL_GATEWAY_FNAME `"`gateway'"'
	}

	if `"`includefile'"'!=""{
		cap confirm file `"`includefile'"'
		_assert `=!_rc', msg("Can not find the include file (`includefile').") msg(`=_rc')
		global PLL_INCLUDE_FILE `"`includefile'"'
	}
end

////////////////////////////////////////////////////////////////////////////////
// Exports

// Exports a copy of programs
program def program_export
	version 11.0
	syntax using/ [,Programlist(string) Inname(string)]

	mata: program_export("`using'", "`programlist'", "`inname'")
end

////////////////////////////////////////////////////////////////////////////////
// Appends the clusterized dataset
program def parallel_fusion
	version 11.0
	syntax anything(name=parallelid) , clusters(integer) [keepusing(string)]

	cap use "__pll`parallelid'_dta0001.dta", clear
	if (_rc){
		di "{error:No dataset for instance 0001.}"
		exit _rc
	}
	local sortlist: sortedby

	forval i = 2/`clusters' {
		cap append using `"__pll`parallelid'_dta`=string(`i',"%04.0f")'.dta"'
		if (_rc){
			di "{error:No dataset for instance `=string(`i',"%04.0f")'.}"
			exit _rc
		}
	}

	/* If it just used a set of variables */
	if (length("`keepusing'")) {
		qui merge 1:1 __pllnobs`parallelid' using __pll`parallelid'_dataset, keep(3) nogen
		drop __pllnobs`parallelid'
	}

	//restore the sort
	if "`sortlist'"!="" sort `sortlist'

end

////////////////////////////////////////////////////////////////////////////////
// Checks whether the user pressed break inside a loop
program def parallel_break
	version 11.0
	mata: parallel_break()
end


Added timer to wave

Added timer to wave

onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /cpu_tb/cpu_DUT/clk
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/timer
add wave -noupdate /cpu_tb/cpu_DUT/reset
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/inM
add wave -noupdate /cpu_tb/cpu_DUT/instruction
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/outM
add wave -noupdate /cpu_tb/cpu_DUT/writeM
add wave -noupdate -radix unsigned /cpu_tb/cpu_DUT/addressM
add wave -noupdate -radix unsigned /cpu_tb/cpu_DUT/DRegister
add wave -noupdate -radix unsigned /cpu_tb/cpu_DUT/ARegister
add wave -noupdate -divider PC
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/pc
add wave -noupdate /cpu_tb/cpu_DUT/pc_inc
add wave -noupdate /cpu_tb/cpu_DUT/pc_load
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/pc_in
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/program_counter_inst/in
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/program_counter_inst/out
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/program_counter_inst/r_out
add wave -noupdate -divider ALU
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/alu_inst/x
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/alu_inst/y
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/zx
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/nx
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/zy
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/ny
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/f
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/no
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/alu_inst/out
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/zr
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/ng
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/r_zr
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/r_ng
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {80 ns} 0}
quietly wave cursor active 1
configure wave -namecolwidth 160
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 1
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ns
update
WaveRestoreZoom {1660 ns} {1920 ns}

onerror {resume}
quietly WaveActivateNextPane {} 0
add wave -noupdate /cpu_tb/cpu_DUT/clk
add wave -noupdate /cpu_tb/cpu_DUT/reset
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/inM
add wave -noupdate /cpu_tb/cpu_DUT/instruction
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/outM
add wave -noupdate /cpu_tb/cpu_DUT/writeM
add wave -noupdate -radix unsigned /cpu_tb/cpu_DUT/addressM
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/DRegister
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/ARegister
add wave -noupdate -divider PC
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/pc
add wave -noupdate /cpu_tb/cpu_DUT/pc_inc
add wave -noupdate /cpu_tb/cpu_DUT/pc_load
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/pc_in
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/program_counter_inst/in
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/program_counter_inst/out
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/program_counter_inst/r_out
add wave -noupdate -divider ALU
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/alu_inst/x
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/alu_inst/y
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/zx
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/nx
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/zy
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/ny
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/f
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/no
add wave -noupdate -radix decimal /cpu_tb/cpu_DUT/alu_inst/out
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/zr
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/ng
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/r_zr
add wave -noupdate /cpu_tb/cpu_DUT/alu_inst/r_ng
TreeUpdate [SetDefaultTree]
WaveRestoreCursors {{Cursor 1} {80 ns} 0}
quietly wave cursor active 1
configure wave -namecolwidth 160
configure wave -valuecolwidth 100
configure wave -justifyvalue left
configure wave -signalnamewidth 1
configure wave -snapdistance 10
configure wave -datasetprefix 0
configure wave -rowmargin 4
configure wave -childrowmargin 2
configure wave -gridoffset 0
configure wave -gridperiod 1
configure wave -griddelta 40
configure wave -timeline 0
configure wave -timelineunits ns
update
WaveRestoreZoom {0 ns} {260 ns}


For ModelSim-AE, don't try to run vopt

For ModelSim-AE, don't try to run vopt

#
# Copyright (C) 2014, 2017 Chris McClelland
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
# and associated documentation files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright  notice and this permission notice  shall be included in all copies or
# substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
set IP_DIR "$env(MAKESTUFF)/ip"
if {![info exists ::env(FPGA)]} {
  puts "\nYou need to set the FPGA environment variable!\n"
  quit
}
if {![info exists ::env(EN_SWAP)]} {
  puts "\nYou need to set the EN_SWAP environment variable!\n"
  quit
}
if {![info exists ::env(NUM_ITERATIONS)]} {
  puts "\nYou need to set the NUM_ITERATIONS environment variable!\n"
  quit
}

file delete -force modelsim.ini
file delete -force work
vmap -modelsimini $IP_DIR/sim-libs/modelsim.ini -c
vlib work
vmap work_lib work
onbreak resume

vlog -sv $IP_DIR/reg-mux/reg_mux.sv              -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/buffer-fifo/buffer_fifo_impl.sv -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/buffer-fifo/buffer_fifo.sv      -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_xcvr_pkg.sv   -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_recv.sv       -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_send.sv       -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_xcvr.sv       -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv ../pcie_app_pkg.sv                      -hazards -lint -pedanticerrors -L makestuff
vlog -sv ../pcie_app.sv                          -hazards -lint -pedanticerrors -L makestuff

if {[lsearch {svgx} $env(FPGA)] >= 0} {
  # Do a Stratix V simulation
  vlog -sv $IP_DIR/pcie/stratixv/pcie_sv/testbench/pcie_sv_tb/simulation/submodules/altpcie_monitor_sv_dlhip_sim.sv -work pcie_sv
  vlog -sv -hazards -lint -pedanticerrors +incdir+$IP_DIR/pcie/stratixv/pcie_sv/testbench/pcie_sv_tb/simulation/submodules altpcietb_bfm_driver_chaining.sv -L makestuff
  vlog -sv -hazards -lint -pedanticerrors $IP_DIR/pcie/stratixv/pcie_sv.sv -work makestuff
  vlog -sv -hazards -lint -pedanticerrors pcie_sv_tb.sv -L makestuff
  if [ string match "*ModelSim ALTERA*" [ vsim -version ] ] {
    vsim -novopt -t ps \
      -gEN_SWAP=$env(EN_SWAP) \
      -gdut_pcie_tb/g_bfm_top_rp/altpcietb_bfm_top_rp/genblk1/drvr/NUM_ITERATIONS=$env(NUM_ITERATIONS) \
      -L work -L work_lib -L makestuff -L pcie_sv -L pcie_sv_tb \
      -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_mf -L altera_lnsim_ver \
      -L stratixiv_hssi_ver -L stratixiv_pcie_hip_ver -L stratixiv_ver \
      -L stratixv_ver -L stratixv_hssi_ver -L stratixv_pcie_hip_ver \
      pcie_sv_tb
  } else {
    vopt +acc pcie_sv_tb -o pcie_sv_tb_opt \
      -gEN_SWAP=$env(EN_SWAP) \
      -gdut_pcie_tb/g_bfm_top_rp/altpcietb_bfm_top_rp/genblk1/drvr/NUM_ITERATIONS=$env(NUM_ITERATIONS) \
      -L work -L work_lib -L makestuff -L pcie_sv -L pcie_sv_tb \
      -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_mf -L altera_lnsim_ver \
      -L stratixiv_hssi_ver -L stratixiv_pcie_hip_ver -L stratixiv_ver \
      -L stratixv_ver -L stratixv_hssi_ver -L stratixv_pcie_hip_ver
    vsim -t ps pcie_sv_tb_opt
  }
} elseif {[lsearch {cvgt} $env(FPGA)] >= 0} {
  # Do a Cyclone V simulation
  vlog -sv -hazards -lint -pedanticerrors +incdir+$IP_DIR/pcie/cyclonev/pcie_cv/testbench/pcie_cv_tb/simulation/submodules altpcietb_bfm_driver_chaining.sv -L makestuff
  vlog -sv -hazards -lint -pedanticerrors $IP_DIR/pcie/cyclonev/pcie_cv.sv -work makestuff
  vlog -sv -hazards -lint -pedanticerrors pcie_cv_tb.sv -L makestuff
  if [ string match "*ModelSim ALTERA*" [ vsim -version ] ] {
    vsim -novopt -t ps \
      -gEN_SWAP=$env(EN_SWAP) \
      -gdut_pcie_tb/g_bfm_top_rp/altpcietb_bfm_top_rp/genblk1/drvr/NUM_ITERATIONS=$env(NUM_ITERATIONS) \
      -L work -L work_lib -L makestuff -L pcie_cv -L pcie_cv_tb \
      -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_mf -L altera_lnsim_ver \
      -L stratixiv_hssi_ver -L stratixiv_pcie_hip_ver -L stratixiv_ver \
      -L cyclonev_ver -L cyclonev_hssi_ver -L cyclonev_pcie_hip_ver \
      pcie_cv_tb
  } else {
    vopt +acc pcie_cv_tb -o pcie_cv_tb_opt \
      -gEN_SWAP=$env(EN_SWAP) \
      -gdut_pcie_tb/g_bfm_top_rp/altpcietb_bfm_top_rp/genblk1/drvr/NUM_ITERATIONS=$env(NUM_ITERATIONS) \
      -L work -L work_lib -L makestuff -L pcie_cv -L pcie_cv_tb \
      -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_mf -L altera_lnsim_ver \
      -L stratixiv_hssi_ver -L stratixiv_pcie_hip_ver -L stratixiv_ver \
      -L cyclonev_ver -L cyclonev_hssi_ver -L cyclonev_pcie_hip_ver
    vsim -t ps pcie_cv_tb_opt
  }
} else {
  puts "\nUnrecognised FPGA: \"$env(FPGA)\"!\n"
  quit
}

add wave      pcie_app/pcieClk_in
add wave -hex pcie_app/cfgBusDev_in

add wave -div "RX Pipe"
add wave -hex pcie_app/tlp_inst/recv/rxData_in
add wave -hex pcie_app/tlp_inst/recv/hdr
add wave -hex pcie_app/tlp_inst/recv/rw0
add wave -hex pcie_app/tlp_inst/recv/rw1
add wave -hex pcie_app/tlp_inst/recv/rr0;
add wave -hex pcie_app/tlp_inst/recv/rr1;
add wave      pcie_app/rxValid_in
add wave      pcie_app/rxReady_out
add wave      pcie_app/rxSOP_in
add wave      pcie_app/rxEOP_in

add wave -div "Action Pipe"
add wave -hex pcie_app/tlp_inst/send/rr
add wave -hex pcie_app/tlp_inst/send/rw
add wave      pcie_app/tlp_inst/send/actValid_in
add wave      pcie_app/tlp_inst/send/actReady_out

add wave -div "TX Pipe"
add wave -hex pcie_app/tlp_inst/txData_out
add wave      pcie_app/tlp_inst/txValid_out
add wave      pcie_app/tlp_inst/txReady_in
add wave      pcie_app/tlp_inst/txSOP_out
add wave      pcie_app/tlp_inst/txEOP_out

add wave -div "Register Pipes"
add wave -hex pcie_app/tlp_inst/cpuChan_out
add wave -hex pcie_app/tlp_inst/cpuWrData_out
add wave      pcie_app/tlp_inst/cpuWrValid_out
add wave      pcie_app/tlp_inst/cpuWrReady_in
add wave -hex pcie_app/tlp_inst/cpuRdData_in
add wave      pcie_app/tlp_inst/cpuRdValid_in
add wave      pcie_app/tlp_inst/cpuRdReady_out

add wave -div "FPGA->CPU DMA Pipe"
add wave -hex pcie_app/tlp_inst/f2cData_in
add wave      pcie_app/tlp_inst/f2cValid_in
add wave      pcie_app/tlp_inst/f2cReady_out
add wave      pcie_app/tlp_inst/f2cReset_out

add wave -div "CPU->FPGA Pipe"
add wave -hex pcie_app/tlp_inst/c2fData_out
add wave      pcie_app/tlp_inst/c2fValid_out

add wave -div "Receiver Internals"
add wave      pcie_app/tlp_inst/recv/state
add wave -hex pcie_app/tlp_inst/recv/dwCount
add wave -hex pcie_app/tlp_inst/recv/firstBE
add wave -hex pcie_app/tlp_inst/recv/lastBE

add wave -div "Sender Internals"
add wave      pcie_app/tlp_inst/send/state
add wave -hex pcie_app/tlp_inst/send/f2cBase
add wave -radix unsigned pcie_app/tlp_inst/send/f2cWrPtr
add wave -radix unsigned pcie_app/tlp_inst/send/f2cRdPtr
add wave -radix unsigned pcie_app/tlp_inst/send/c2fWrPtr
add wave -radix unsigned pcie_app/tlp_inst/send/c2fRdPtr
add wave -hex pcie_app/tlp_inst/send/rdData
add wave -hex pcie_app/tlp_inst/send/reqID
add wave -hex pcie_app/tlp_inst/send/tag
add wave -hex pcie_app/tlp_inst/send/lowAddr
add wave -hex pcie_app/tlp_inst/send/qwCount

add wave -div "App Internals"
add wave -hex pcie_app/ckSum
add wave -div ""

if {[info exists ::env(GUI)] && $env(GUI)} {
  configure wave -namecolwidth 340
  configure wave -valuecolwidth 132
  run -all
  view wave
  bookmark add wave default {{56120ns} {56280ns}}
  bookmark goto wave default
  wave refresh
} else {
  run -all
  quit
}

#
# Copyright (C) 2014, 2017 Chris McClelland
#
# Permission is hereby granted, free of charge, to any person obtaining a copy of this software
# and associated documentation files (the "Software"), to deal in the Software without
# restriction, including without limitation the rights to use, copy, modify, merge, publish,
# distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright  notice and this permission notice  shall be included in all copies or
# substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
# BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#
set IP_DIR "$env(MAKESTUFF)/ip"
if {![info exists ::env(FPGA)]} {
  puts "\nYou need to set the FPGA environment variable!\n"
  quit
}
if {![info exists ::env(EN_SWAP)]} {
  puts "\nYou need to set the EN_SWAP environment variable!\n"
  quit
}
if {![info exists ::env(NUM_ITERATIONS)]} {
  puts "\nYou need to set the NUM_ITERATIONS environment variable!\n"
  quit
}

file delete -force modelsim.ini
file delete -force work
vmap -modelsimini $IP_DIR/sim-libs/modelsim.ini -c
vlib work
vmap work_lib work
onbreak resume

vlog -sv $IP_DIR/reg-mux/reg_mux.sv              -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/buffer-fifo/buffer_fifo_impl.sv -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/buffer-fifo/buffer_fifo.sv      -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_xcvr_pkg.sv   -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_recv.sv       -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_send.sv       -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv $IP_DIR/pcie/tlp-xcvr/tlp_xcvr.sv       -hazards -lint -pedanticerrors -work makestuff +define+SIMULATION
vlog -sv ../pcie_app_pkg.sv                      -hazards -lint -pedanticerrors -L makestuff
vlog -sv ../pcie_app.sv                          -hazards -lint -pedanticerrors -L makestuff

if {[lsearch {svgx} $env(FPGA)] >= 0} {
  # Do a Stratix V simulation
  vlog -sv $IP_DIR/pcie/stratixv/pcie_sv/testbench/pcie_sv_tb/simulation/submodules/altpcie_monitor_sv_dlhip_sim.sv -work pcie_sv
  vlog -sv -hazards -lint -pedanticerrors +incdir+$IP_DIR/pcie/stratixv/pcie_sv/testbench/pcie_sv_tb/simulation/submodules altpcietb_bfm_driver_chaining.sv -L makestuff
  vlog -sv -hazards -lint -pedanticerrors $IP_DIR/pcie/stratixv/pcie_sv.sv -work makestuff
  vlog -sv -hazards -lint -pedanticerrors pcie_sv_tb.sv -L makestuff
  vopt +acc pcie_sv_tb -o pcie_sv_tb_opt \
    -gEN_SWAP=$env(EN_SWAP) \
    -gdut_pcie_tb/g_bfm_top_rp/altpcietb_bfm_top_rp/genblk1/drvr/NUM_ITERATIONS=$env(NUM_ITERATIONS) \
    -L work -L work_lib -L makestuff -L pcie_sv -L pcie_sv_tb \
    -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_mf -L altera_lnsim_ver \
    -L stratixiv_hssi_ver -L stratixiv_pcie_hip_ver -L stratixiv_ver \
    -L stratixv_ver -L stratixv_hssi_ver -L stratixv_pcie_hip_ver
  vsim -t ps pcie_sv_tb_opt
} elseif {[lsearch {cvgt} $env(FPGA)] >= 0} {
  # Do a Cyclone V simulation
  vlog -sv -hazards -lint -pedanticerrors +incdir+$IP_DIR/pcie/cyclonev/pcie_cv/testbench/pcie_cv_tb/simulation/submodules altpcietb_bfm_driver_chaining.sv -L makestuff
  vlog -sv -hazards -lint -pedanticerrors $IP_DIR/pcie/cyclonev/pcie_cv.sv -work makestuff
  vlog -sv -hazards -lint -pedanticerrors pcie_cv_tb.sv -L makestuff
  vopt +acc pcie_cv_tb -o pcie_cv_tb_opt \
    -gEN_SWAP=$env(EN_SWAP) \
    -gdut_pcie_tb/g_bfm_top_rp/altpcietb_bfm_top_rp/genblk1/drvr/NUM_ITERATIONS=$env(NUM_ITERATIONS) \
    -L work -L work_lib -L makestuff -L pcie_cv -L pcie_cv_tb \
    -L altera_ver -L lpm_ver -L sgate_ver -L altera_mf_ver -L altera_mf -L altera_lnsim_ver \
    -L stratixiv_hssi_ver -L stratixiv_pcie_hip_ver -L stratixiv_ver \
    -L cyclonev_ver -L cyclonev_hssi_ver -L cyclonev_pcie_hip_ver
  vsim -t ps pcie_cv_tb_opt
} else {
  puts "\nUnrecognised FPGA: \"$env(FPGA)\"!\n"
  quit
}

add wave      pcie_app/pcieClk_in
add wave -hex pcie_app/cfgBusDev_in

add wave -div "RX Pipe"
add wave -hex pcie_app/tlp_inst/recv/rxData_in
add wave -hex pcie_app/tlp_inst/recv/hdr
add wave -hex pcie_app/tlp_inst/recv/rw0
add wave -hex pcie_app/tlp_inst/recv/rw1
add wave -hex pcie_app/tlp_inst/recv/rr0;
add wave -hex pcie_app/tlp_inst/recv/rr1;
add wave      pcie_app/rxValid_in
add wave      pcie_app/rxReady_out
add wave      pcie_app/rxSOP_in
add wave      pcie_app/rxEOP_in

add wave -div "Action Pipe"
add wave -hex pcie_app/tlp_inst/send/rr
add wave -hex pcie_app/tlp_inst/send/rw
add wave      pcie_app/tlp_inst/send/actValid_in
add wave      pcie_app/tlp_inst/send/actReady_out

add wave -div "TX Pipe"
add wave -hex pcie_app/tlp_inst/txData_out
add wave      pcie_app/tlp_inst/txValid_out
add wave      pcie_app/tlp_inst/txReady_in
add wave      pcie_app/tlp_inst/txSOP_out
add wave      pcie_app/tlp_inst/txEOP_out

add wave -div "Register Pipes"
add wave -hex pcie_app/tlp_inst/cpuChan_out
add wave -hex pcie_app/tlp_inst/cpuWrData_out
add wave      pcie_app/tlp_inst/cpuWrValid_out
add wave      pcie_app/tlp_inst/cpuWrReady_in
add wave -hex pcie_app/tlp_inst/cpuRdData_in
add wave      pcie_app/tlp_inst/cpuRdValid_in
add wave      pcie_app/tlp_inst/cpuRdReady_out

add wave -div "FPGA->CPU DMA Pipe"
add wave -hex pcie_app/tlp_inst/f2cData_in
add wave      pcie_app/tlp_inst/f2cValid_in
add wave      pcie_app/tlp_inst/f2cReady_out
add wave      pcie_app/tlp_inst/f2cReset_out

add wave -div "CPU->FPGA Pipe"
add wave -hex pcie_app/tlp_inst/c2fData_out
add wave      pcie_app/tlp_inst/c2fValid_out

add wave -div "Receiver Internals"
add wave      pcie_app/tlp_inst/recv/state
add wave -hex pcie_app/tlp_inst/recv/dwCount
add wave -hex pcie_app/tlp_inst/recv/firstBE
add wave -hex pcie_app/tlp_inst/recv/lastBE

add wave -div "Sender Internals"
add wave      pcie_app/tlp_inst/send/state
add wave -hex pcie_app/tlp_inst/send/f2cBase
add wave -radix unsigned pcie_app/tlp_inst/send/f2cWrPtr
add wave -radix unsigned pcie_app/tlp_inst/send/f2cRdPtr
add wave -radix unsigned pcie_app/tlp_inst/send/c2fWrPtr
add wave -radix unsigned pcie_app/tlp_inst/send/c2fRdPtr
add wave -hex pcie_app/tlp_inst/send/rdData
add wave -hex pcie_app/tlp_inst/send/reqID
add wave -hex pcie_app/tlp_inst/send/tag
add wave -hex pcie_app/tlp_inst/send/lowAddr
add wave -hex pcie_app/tlp_inst/send/qwCount

add wave -div "App Internals"
add wave -hex pcie_app/ckSum
add wave -div ""

if {[info exists ::env(GUI)] && $env(GUI)} {
  configure wave -namecolwidth 340
  configure wave -valuecolwidth 132
  run -all
  view wave
  bookmark add wave default {{56120ns} {56280ns}}
  bookmark goto wave default
  wave refresh
} else {
  run -all
  quit
}


add column name dictionary

add column name dictionary

cap program drop 	ieddtable
	program define	ieddtable

	syntax varlist, ///
					///
		t(varname numeric) tmt(varname numeric) 			///
		[ 													///
		COVARiates(varlist numeric)							///
		STARLevels(numlist descending min=3 max=3 >0 <1)	///
		STARSNOadd											///
		firstdiff											///
		rowlabtype(string) 									///
		rowlabtext(string)									///
		]

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

		Initiate the result matrix

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

	*Creates the template for the result matrix in a subfunction
	templateResultMatrix
	mat startRow = r(startRow)

	*Remove this when ready for production
	noi di "Start row to see headers, remove for production"
	matlist startRow // See the default row with its column names

	*Initiate the result matrix with a place holder row that will not be used for anything. Matrices cannot be initiated empty
	mat resultMat = startRow
	mat rownames resultMat = placeholder

	/*
		Column name dictionary
			Differences:
			- 2D : Second differnce coefficient (t*tmt == 1)
			- 1DC : First  differnce coefficient control (t == 0)
			- 1DT : First  differnce coefficient treatment (tmt == 0)

			for each coefficent these stats are also provided:
				- _err : Second differnce errors (type of errors is set in command)
				- _Stars :  Second Differene - The number of significance stars (sig level set in command)
				- _N : Second Difference - Number of observtions in the regression

			Group means:
			- C0 - Control time 0
			- T0 - Treatment time 0
			- C1 - Control time 1
			- T1 - Treatment time 1

			for each group these stats are also provided:
				- _mean : the mean of the group
				- _err : the error in the mean (type of errors is set in command)

	*/

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

		Loop over all variables and prepare the data

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

	foreach var of local varlist {

		*Each variable is a row in the result matrix
		mat `var' = startRow
		mat rownames `var' = `var'
		mat colnames `var' = `colnames'

		*Local that keeps track of which column to fill
		local colindex 0

		tempvar regsample

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

			Calculating 2nd difference and restict the sample
			for this output var

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

		*Run the regression to get the double difference
		qui reg `var' `tmt'#`t' `covariates'
		mat resTable = r(table)

		**This is why this is done first. All other calculations
		* for this outcome var should be restricted to this sample.
		gen `regsample' = e(sample)
		local N `e(N)'

		**Test that the dummy vars creates for valid groups for each
		* combination of time and treat in the sample used for this outcome var
		testDDdums `t' `tmt' `regsample' `var'  //comment in when this is made into a command

		//Get the second differnce
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,1,4)

		*Get the standard error of second difference
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,2,4)

		matlist resTable

		*Get the number of stars using sub-command countStars
		local ++colindex
		local pvalue = el(resTable,4,4)
		countStars `pvalue' `starlevels'
		mat `var'[1,`colindex'] = `r(stars)'

		*Get the N of second difference regression
		local ++colindex
		mat `var'[1,`colindex'] = `N'

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

			Calculating 1st differences

		*************/
		forvalues tmt01 = 0/1 {

			*Regress time against the outcome var one tmt group at the time
			qui reg `var' `t' `covariates' if `tmt' == `tmt01' & `regsample' == 1
			mat resTable = r(table)

			//Get the 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,1,1)

			*Get the standard error of 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,2,1)

			*Get the number of stars using sub-command countStars
			local ++colindex
			local pvalue = el(resTable,4,4)
			countStars `pvalue' `starlevels'
			mat `var'[1,`colindex'] = `r(stars)'

			*Get the N of first difference regression
			local ++colindex
			mat `var'[1,`colindex'] =  `e(N)'

		}

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

			Calculating standard means for all groups

		*************/
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				qui mean `var' if `tmt' == `t01' & `t' == `tmt01' & `regsample' == 1

				mat resTable = r(table)

				//Get the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,1,1)

				*Get the standard error of the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,2,1)

				*Get the N of the ordinary means regressions
				local ++colindex
				mat `var'[1,`colindex'] =  `e(N)'
			}
		}

		*Append this row to the result table
		mat 	resultMat = (resultMat \ `var')

	}

	*Remove placeholder row
	matrix resultMat = resultMat[2..., 1...]

	*Show the final matrix will all data needed to start building the output
	noi di "Matlist with results"
	matlist resultMat

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

		Call subcommands that prepares labels in the table

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

	prepRowLabels `varlist', rowlabtype("`rowlabtype'") rowlabtext("`rowlabtext'")
	return list
	di "`r(rowlabels)'"

end

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

	Write sub-commands for outputs

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

	******
	*Then the result matrix can be passed into subcommands that output in either Excel, LaTeX or in the main window.

	*The results can be accessed like this, which makes the sub-commands less sensitive to changing column order in the section above.
	//mat A = resultMat[3, "C0_Mean"] // returns a 1x1 matrix with the baseline mean for the countrol grop for the thrid outcome var
	//local a = el(A,1,1)				// returns the value

	//matlist A
	//di `a'

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

	Write sub-commands for input handling

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

**Program to prepare row labels for each outcome var, using variable name,
* variable label or manually entered labels depedning on user input. Variable
* names are used if no input.
cap program drop 	prepRowLabels
	program define	prepRowLabels, rclass

	syntax varlist, [rowlabtype(string) rowlabtext(string)]

	*First test input
	if "`rowlabtype'" == "varlab" | "`rowlabtype'" == "" | "`rowlabtype'" == "varname" {
		//All is good do nothing
	}
	else {
		noi display as error "{phang}Row label type [`rowlabtype'] is not a valid row label type. Enter either {it:varlab} or {it:varname} (the default if not specified is {it:varname}). See option {help ieddtable:rowlabtype} for details."
		error 198
	}

	/*************
		Parse through the manually entered labels
	*************/

	*Create a local with the rowlabel input to be tokenized
	local row_labels_to_tokenize `rowlabtext'

	while "`row_labels_to_tokenize'" != "" {

		*Parsing name and label pair
		gettoken nameAndLabel row_labels_to_tokenize : row_labels_to_tokenize, parse("@@")

		*Splitting name and label
		gettoken name label : nameAndLabel

		/*
			We store two locals, one with varnames and one with label. They are
			stored in the same order, so if we find varname we know we will find
			the corresponding label in the same order in the other local.
		*/

		*** Tests

		*Checking that the variables used in rowlabels() are included in the table
		local name_correct : list name in varlist
		if `name_correct' == 0 {
			noi display as error "{phang}Variable [`name'] listed in rowlabtext(`rowlabtext') is not found among the outcome variables."
			error 111
		}

		*Testing that no label is missing
		if "`label'" == "" {
			noi display as error "{phang}For variable [`name'] listed in rowlabtext(`rowlabels') you have not specified any label. Labels are requried for all variables listed in rowlabels(). Variables omitted from rowlabtext() will be assigned labels according to the rule in rowlabtype(). See also option {help ieddtable:rowlabtext}"
			error 198
		}

		*Storing the name in local to be used to get index of corresponding label in the second local
		local rowLabelNames `"`rowLabelNames' "`name'" "'

		*Removing leading or trailing spaces and store label with the same index as in the name local above
		local label = trim("`label'")
		local rowLabelLabels `"`rowLabelLabels' "`label'" "'

		*Parse char is not removed by gettoken, so remove it and start over
		local row_labels_to_tokenize = subinstr("`row_labels_to_tokenize'" ,"@@","",1)
	}

	**Set up locals to be returned. One with row labels without varname but in
	* final order, and one local with length of longest label
	local preppedRowLabels
	local maxLen 0

	*Loop over all varaibles and prepare the labels
	foreach var of local varlist {

		**Get the index of this variable in manually entered labels. Index is
		* zero if no manually entered label for this variable
		local rowLabPos : list posof "`var'" in rowLabelNames

		if `rowLabPos' == 0 {

			*No label was manually entered for this variable, use the other rules
			if "`rowlabtype'" == "varlab" {
				local this_label : variable label `var'		//Use variable label
			}
			else {

				local this_label `var'						//Default, use varname

			}
		}
		else {
			*Getting the manually defined label corresponding to this variable
			local this_label : word `rowLabPos' of `rowLabelLabels'
		}

		*Update the two locals that will be returned
		local preppedRowLabels "`preppedRowLabels' @@`this_label'"	//Prepare the final list
		local maxLen = max(strlen("`this_label'"),`maxLen')	//Store the length on the longest label, used on some outputs
	}

	*Return the locals
	return local rowlabels `preppedRowLabels'
	return local rowlabel_maxlen `maxLen'

end

cap program drop 	testDDdums
	program define	testDDdums

		args time treat samplevar outputvar

		*Test taht the dummies are dummies
		foreach dummy in `time' `treat' {

			cap assert inlist(`dummy',1,0) | missing(`dummy') | `samplevar' == 0
			if _rc {
				noi di as error "{phang}In the difference in differnce regression for outputvar [`outputvar'], the dummy [`dummy'] is not a real dummy as it has values differnt from 1 and 0. See tab below. The tab is restricted to the observations that are not excluded due to missing values etc. in the difference in differnce regression.{p_end}""
				noi tab `dummy' if `samplevar' == 1, missing
				error 480
			}
		}

		**Test that for only two of three dummies there are observations
		* that has only that dummy. I.e. the two that is not the
		* interaction. If the interaction is 1, all three shluld be 1.

		*Test that there is some observations in each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				count if `treat' == `t01' & `time' == `tmt01' & `samplevar' == 1

				//There got to be more than 1 observation in each group for the regression to make sense
				if `r(N)' < 2 {
					noi di as error "{phang}In the difference in differnce regression for outputvar [`outputvar'] there were not enough observations in each combination of treatment [`treat'] and time [`time']. The tab below is restricted to the observations that are not excluded due to missing values etc. in the difference in differnce regression.{p_end}""
					noi tab `treat' `time' if `samplevar' == 1, missing
					error 480

				}
			}
		}

	end

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

	Write sub-commands for stats

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

cap program drop 	templateResultMatrix
	program define	templateResultMatrix, rclass

	local 2ndDiff_cols 			2D 2D_err 2D_Stars 2D_N

	local 1stDiff_C_cols		1DC 1DC_err 1DC_stars 1DC_N
	local 1stDiff_T_cols		1DT 1DT_err 1DT_stars 1DT_N

	local basicMean_C0_cols		C0_mean C0_err C0_N
	local basicMean_T0_cols		T0_mean T0_err T0_N
	local basicMean_C1_cols		C1_mean C1_err C1_N
	local basicMean_T1_cols		T1_mean T1_err T1_N

	local colnames `2ndDiff_cols' `1stDiff_C_cols' `1stDiff_T_cols' `basicMean_C0_cols' `basicMean_T0_cols' `basicMean_C1_cols' `basicMean_T0_cols'

	*Define default row here. The results for each var will be one row that starts with all missing vlaues
	mat startRow = (.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.)
	mat colnames startRow = `colnames'

	return matrix startRow startRow

end

cap program drop 	countStars
	program define	countStars, rclass

	args pvalue star1 star2 star3

	*Test if custom star levels are used
	if "`star1'" == "" {

		*Set default levels for 1, 2 and 3 stars
		local star1 .1
		local star2 .05
		local star3 .01
	}

	local stars 0
	foreach star_p_level in `star1' `star2' `star3' {

		if `pvalue' < `star_p_level' local ++stars
	}

	return local stars `stars'

end

cap program drop 	ieddtable
	program define	ieddtable

	syntax varlist, ///
					///
		t(varname numeric) tmt(varname numeric) 			///
		[ 													///
		COVARiates(varlist numeric)							///
		STARLevels(numlist descending min=3 max=3 >0 <1)	///
		STARSNOadd											///
		firstdiff											///
		rowlabtype(string) 									///
		rowlabtext(string)									///
		]

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

		Initiate the result matrix

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

	*Creates the template for the result matrix in a subfunction
	templateResultMatrix
	mat startRow = r(startRow)

	*Remove this when ready for production
	noi di "Start row to see headers, remove for production"
	matlist startRow // See the default row with its column names

	*Initiate the result matrix with a place holder row that will not be used for anything. Matrices cannot be initiated empty
	mat resultMat = startRow
	mat rownames resultMat = placeholder

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

		Loop over all variables and prepare the data

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

	foreach var of local varlist {

		*Each variable is a row in the result matrix
		mat `var' = startRow
		mat rownames `var' = `var'
		mat colnames `var' = `colnames'

		*Local that keeps track of which column to fill
		local colindex 0

		tempvar regsample

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

			Calculating 2nd difference and restict the sample
			for this output var

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

		*Run the regression to get the double difference
		qui reg `var' `tmt'#`t' `covariates'
		mat resTable = r(table)

		**This is why this is done first. All other calculations
		* for this outcome var should be restricted to this sample.
		gen `regsample' = e(sample)
		local N `e(N)'

		**Test that the dummy vars creates for valid groups for each
		* combination of time and treat in the sample used for this outcome var
		testDDdums `t' `tmt' `regsample' `var'  //comment in when this is made into a command

		//Get the second differnce
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,1,4)

		*Get the standard error of second difference
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,2,4)

		matlist resTable

		*Get the number of stars using sub-command countStars
		local ++colindex
		local pvalue = el(resTable,4,4)
		countStars `pvalue' `starlevels'
		mat `var'[1,`colindex'] = `r(stars)'

		*Get the N of second difference regression
		local ++colindex
		mat `var'[1,`colindex'] = `N'

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

			Calculating 1st differences

		*************/
		forvalues tmt01 = 0/1 {

			*Regress time against the outcome var one tmt group at the time
			qui reg `var' `t' `covariates' if `tmt' == `tmt01' & `regsample' == 1
			mat resTable = r(table)

			//Get the 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,1,1)

			*Get the standard error of 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,2,1)

			*Get the number of stars using sub-command countStars
			local ++colindex
			local pvalue = el(resTable,4,4)
			countStars `pvalue' `starlevels'
			mat `var'[1,`colindex'] = `r(stars)'

			*Get the N of first difference regression
			local ++colindex
			mat `var'[1,`colindex'] =  `e(N)'

		}

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

			Calculating standard means for all groups

		*************/
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				qui mean `var' if `tmt' == `t01' & `t' == `tmt01' & `regsample' == 1

				mat resTable = r(table)

				//Get the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,1,1)

				*Get the standard error of the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,2,1)

				*Get the N of the ordinary means regressions
				local ++colindex
				mat `var'[1,`colindex'] =  `e(N)'
			}
		}

		*Append this row to the result table
		mat 	resultMat = (resultMat \ `var')

	}

	*Remove placeholder row
	matrix resultMat = resultMat[2..., 1...]

	*Show the final matrix will all data needed to start building the output
	noi di "Matlist with results"
	matlist resultMat

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

		Call subcommands that prepares labels in the table

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

	prepRowLabels `varlist', rowlabtype("`rowlabtype'") rowlabtext("`rowlabtext'")
	return list
	di "`r(rowlabels)'"

end

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

	Write sub-commands for outputs

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

	******
	*Then the result matrix can be passed into subcommands that output in either Excel, LaTeX or in the main window.

	*The results can be accessed like this, which makes the sub-commands less sensitive to changing column order in the section above.
	//mat A = resultMat[3, "C0_Mean"] // returns a 1x1 matrix with the baseline mean for the countrol grop for the thrid outcome var
	//local a = el(A,1,1)				// returns the value

	//matlist A
	//di `a'

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

	Write sub-commands for input handling

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

**Program to prepare row labels for each outcome var, using variable name,
* variable label or manually entered labels depedning on user input. Variable
* names are used if no input.
cap program drop 	prepRowLabels
	program define	prepRowLabels, rclass

	syntax varlist, [rowlabtype(string) rowlabtext(string)]

	*First test input
	if "`rowlabtype'" == "varlab" | "`rowlabtype'" == "" | "`rowlabtype'" == "varname" {
		//All is good do nothing
	}
	else {
		noi display as error "{phang}Row label type [`rowlabtype'] is not a valid row label type. Enter either {it:varlab} or {it:varname} (the default if not specified is {it:varname}). See option {help ieddtable:rowlabtype} for details."
		error 198
	}

	/*************
		Parse through the manually entered labels
	*************/

	*Create a local with the rowlabel input to be tokenized
	local row_labels_to_tokenize `rowlabtext'

	while "`row_labels_to_tokenize'" != "" {

		*Parsing name and label pair
		gettoken nameAndLabel row_labels_to_tokenize : row_labels_to_tokenize, parse("@@")

		*Splitting name and label
		gettoken name label : nameAndLabel

		/*
			We store two locals, one with varnames and one with label. They are
			stored in the same order, so if we find varname we know we will find
			the corresponding label in the same order in the other local.
		*/

		*** Tests

		*Checking that the variables used in rowlabels() are included in the table
		local name_correct : list name in varlist
		if `name_correct' == 0 {
			noi display as error "{phang}Variable [`name'] listed in rowlabtext(`rowlabtext') is not found among the outcome variables."
			error 111
		}

		*Testing that no label is missing
		if "`label'" == "" {
			noi display as error "{phang}For variable [`name'] listed in rowlabtext(`rowlabels') you have not specified any label. Labels are requried for all variables listed in rowlabels(). Variables omitted from rowlabtext() will be assigned labels according to the rule in rowlabtype(). See also option {help ieddtable:rowlabtext}"
			error 198
		}

		*Storing the name in local to be used to get index of corresponding label in the second local
		local rowLabelNames `"`rowLabelNames' "`name'" "'

		*Removing leading or trailing spaces and store label with the same index as in the name local above
		local label = trim("`label'")
		local rowLabelLabels `"`rowLabelLabels' "`label'" "'

		*Parse char is not removed by gettoken, so remove it and start over
		local row_labels_to_tokenize = subinstr("`row_labels_to_tokenize'" ,"@@","",1)
	}

	**Set up locals to be returned. One with row labels without varname but in
	* final order, and one local with length of longest label
	local preppedRowLabels
	local maxLen 0

	*Loop over all varaibles and prepare the labels
	foreach var of local varlist {

		**Get the index of this variable in manually entered labels. Index is
		* zero if no manually entered label for this variable
		local rowLabPos : list posof "`var'" in rowLabelNames

		if `rowLabPos' == 0 {

			*No label was manually entered for this variable, use the other rules
			if "`rowlabtype'" == "varlab" {
				local this_label : variable label `var'		//Use variable label
			}
			else {

				local this_label `var'						//Default, use varname

			}
		}
		else {
			*Getting the manually defined label corresponding to this variable
			local this_label : word `rowLabPos' of `rowLabelLabels'
		}

		*Update the two locals that will be returned
		local preppedRowLabels "`preppedRowLabels' @@`this_label'"	//Prepare the final list
		local maxLen = max(strlen("`this_label'"),`maxLen')	//Store the length on the longest label, used on some outputs
	}

	*Return the locals
	return local rowlabels `preppedRowLabels'
	return local rowlabel_maxlen `maxLen'

end

cap program drop 	testDDdums
	program define	testDDdums

		args time treat samplevar outputvar

		*Test taht the dummies are dummies
		foreach dummy in `time' `treat' {

			cap assert inlist(`dummy',1,0) | missing(`dummy') | `samplevar' == 0
			if _rc {
				noi di as error "{phang}In the difference in differnce regression for outputvar [`outputvar'], the dummy [`dummy'] is not a real dummy as it has values differnt from 1 and 0. See tab below. The tab is restricted to the observations that are not excluded due to missing values etc. in the difference in differnce regression.{p_end}""
				noi tab `dummy' if `samplevar' == 1, missing
				error 480
			}
		}

		**Test that for only two of three dummies there are observations
		* that has only that dummy. I.e. the two that is not the
		* interaction. If the interaction is 1, all three shluld be 1.

		*Test that there is some observations in each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				count if `treat' == `t01' & `time' == `tmt01' & `samplevar' == 1

				//There got to be more than 1 observation in each group for the regression to make sense
				if `r(N)' < 2 {
					noi di as error "{phang}In the difference in differnce regression for outputvar [`outputvar'] there were not enough observations in each combination of treatment [`treat'] and time [`time']. The tab below is restricted to the observations that are not excluded due to missing values etc. in the difference in differnce regression.{p_end}""
					noi tab `treat' `time' if `samplevar' == 1, missing
					error 480

				}
			}
		}

	end

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

	Write sub-commands for stats

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

cap program drop 	templateResultMatrix
	program define	templateResultMatrix, rclass

	local 2ndDiff_cols 			2D 2D_err 2D_Stars 2D_N

	local 1stDiff_C_cols		1DC 1DC_err 1DC_stars 1DC_N
	local 1stDiff_T_cols		1DT 1DT_err 1DT_stars 1DT_N

	local basicMean_C0_cols		C0_mean C0_err C0_N
	local basicMean_T0_cols		T0_mean T0_err T0_N
	local basicMean_C1_cols		C1_mean C1_err C1_N
	local basicMean_T1_cols		T1_mean T1_err T1_N

	local colnames `2ndDiff_cols' `1stDiff_C_cols' `1stDiff_T_cols' `basicMean_C0_cols' `basicMean_T0_cols' `basicMean_C1_cols' `basicMean_T0_cols'

	*Define default row here. The results for each var will be one row that starts with all missing vlaues
	mat startRow = (.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.)
	mat colnames startRow = `colnames'

	return matrix startRow startRow

end

cap program drop 	countStars
	program define	countStars, rclass

	args pvalue star1 star2 star3

	*Test if custom star levels are used
	if "`star1'" == "" {

		*Set default levels for 1, 2 and 3 stars
		local star1 .1
		local star2 .05
		local star3 .01
	}

	local stars 0
	foreach star_p_level in `star1' `star2' `star3' {

		if `pvalue' < `star_p_level' local ++stars
	}

	return local stars `stars'

end


ieddtable : sub-command to test dummies

ieddtable : sub-command to test dummies

	/* Create some dummy datat */

	clear

	*set seed 12345
	set obs 10000

	gen tmt = (runiform()<.5)
	gen t	= (runiform()<.5)

	gen var_a = runiform()
	gen var_b = runiform()
	gen var_c = runiform()
	gen var_d = runiform()

	gen cov_a = runiform()
	gen cov_b = runiform()

	local varlist 		var_a var_b var_c var_d
	local covariates 	cov_a cov_b

	/* the rest will be inside the command */

	local colnames 2ndDiff 2ndDiff_err 2ndDiff_N B1stDiff B1stDiff_err B1stDiff_N E1stDiff E1stDiff_err E1stDiff_N BC_Mean BC_err BC_N BT_Mean BT_err BT_N EC_Mean EC_err EC_N ET_Mean ET_err ET_N

	*Define default row here. The results for each var will be one row that starts with all missing vlaues
	mat startRow = (.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.)
	mat colnames startRow = `colnames'

	matlist startRow // See the default row with its column names

	*Initiate the result matrix with a place holder row that will not be used for anything. Matrices cannot be initiated empty
	mat resultMat = startRow
	mat rownames resultMat = placeholder

	*Loop over all varlist
	foreach var of local varlist {

		*Each variable is a row in the result matrix
		mat `var' = startRow
		mat rownames `var' = `var'
		mat colnames `var' = `colnames'

		*Local that keeps track of which column to fill
		local colindex 0

		tempvar regsample

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

			Calculating 2nd difference and restict the sample
			for this output var

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

		*Run the regression to get the double difference
		qui reg `var' tmt#t `covariates'
		mat resTable = r(table)

		**This is why this is done first. All other calculations
		* for this outcome var should be restricted to this sample.
		gen `regsample' = e(sample)
		local N `e(N)'

		**Test that the dummy vars creates for valid groups for each
		* combination of time and treat in the sample used for this outcome var
		//testDDdums t tmt `regsample' `var'  //comment in when this is made into a command

		//Get the second differnce
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,1,4)

		*Get the standard error of second difference
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,2,4)

		*Get the N of second difference regression
		local ++colindex
		mat `var'[1,`colindex'] = `N'

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

			Calculating 1st differences

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

		forvalues tmt01 = 0/1 {

			qui reg `var' t `covariates' if tmt == `tmt01' & `regsample' == 1
			mat resTable = r(table)

			//Get the 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,1,1)

			*Get the standard error of 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,2,1)

			*Get the N of first difference regression
			local ++colindex
			mat `var'[1,`colindex'] =  `e(N)'

		}

		*Get the means for each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				qui mean `var' if tmt == `t01' & t == `tmt01' & `regsample' == 1

				mat resTable = r(table)

				//Get the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,1,1)

				*Get the standard error of the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,2,1)

				*Get the N of the ordinary means regressions
				local ++colindex
				mat `var'[1,`colindex'] =  `e(N)'
			}
		}

		*Append this row to the result table
		mat 	resultMat = (resultMat \ `var')

	}

	*Remove placeholder row
	matrix resultMat = resultMat[2..., 1...]

	*Show the final matrix will all data needed to start building the output
	matlist resultMat

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

	Write sub-commands for outputs

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

	******
	*Then the result matrix can be passed into subcommands that output in either Excel, LaTeX or in the main window.

	*The results can be accessed like this, which makes the sub-commands less sensitive to changing column order in the section above.
	mat A = resultMat[3, "BC_Mean"] // returns a 1x1 matrix
	local a = el(A,1,1)				// returns the value

	matlist A
	di `a'

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

	Write sub-commands for input testing

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

cap program drop 	testDDdums
	program define	testDDdums

		args time treat samplevar outputvar

		**Test that for only two of three dummies there are observations
		* that has only that dummy. I.e. the two that is not the
		* interaction. If the interaction is 1, all three shluld be 1.

		*Test that there is some observations in each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				count if `treat' == `t01' & `time' == `tmt01' & `samplevar' == 1

				if `r(N)' < 2 {
					noi di as error "{phang}In the difference in differnce regression for outputvar `outputvar' there were not enough observations in each combination of treatment and time. The tab below is restricted to the observations that are not excluded due to missing values etc. in the difference in differnce regression.{p_end}""
					noi tab `treat' `time' if `samplevar' == 1, missing
					error 480

				}
			}
		}

	end

	/* Create some dummy datat */

	clear

	*set seed 12345
	set obs 10000

	gen tmt = (runiform()<.5)
	gen t	= (runiform()<.5)

	gen var_a = runiform()
	gen var_b = runiform()
	gen var_c = runiform()
	gen var_d = runiform()

	gen cov_a = runiform()
	gen cov_b = runiform()

	local varlist 		var_a var_b var_c var_d
	local covariates 	cov_a cov_b

	/* the rest will be inside the command */

	local colnames 2ndDiff 2ndDiff_err 2ndDiff_N B1stDiff B1stDiff_err B1stDiff_N E1stDiff E1stDiff_err E1stDiff_N BC_Mean BC_err BC_N BT_Mean BT_err BT_N EC_Mean EC_err EC_N ET_Mean ET_err ET_N

	*Define default row here. The results for each var will be one row that starts with all missing vlaues
	mat startRow = (.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.)
	mat colnames startRow = `colnames'

	matlist startRow // See the default row with its column names

	*Initiate the result matrix with a place holder row that will not be used for anything. Matrices cannot be initiated empty
	mat resultMat = startRow
	mat rownames resultMat = placeholder

	*Loop over all varlist
	foreach var of local varlist {

		*Each variable is a row in the result matrix
		mat `var' = startRow
		mat rownames `var' = `var'
		mat colnames `var' = `colnames'

		*Local that keeps track of which column to fill
		local colindex 0

		tempvar regsample

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

			Calculating 2nd difference and restict the sample
			for this output var

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

		*Run the regression to get the double difference
		qui reg `var' tmt#t `covariates'
		mat resTable = r(table)

		**This is why this is done first. All other calculations
		* for this outcome var should be restricted to this sample.
		gen `regsample' = e(sample)

		//Get the second differnce
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,1,4)

		*Get the standard error of second difference
		local ++colindex
		mat `var'[1,`colindex'] =  el(resTable,2,4)

		*Get the N of second difference regression
		local ++colindex
		mat `var'[1,`colindex'] =  `e(N)'

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

			Calculating 1st differences

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

		forvalues tmt01 = 0/1 {

			qui reg `var' t `covariates' if tmt == `tmt01' & `regsample' == 1
			mat resTable = r(table)

			//Get the 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,1,1)

			*Get the standard error of 1st diff
			local ++colindex
			mat `var'[1,`colindex'] =  el(resTable,2,1)

			*Get the N of first difference regression
			local ++colindex
			mat `var'[1,`colindex'] =  `e(N)'

		}

		*Get the means for each group
		forvalues tmt01 = 0/1 {
			forvalues t01 = 0/1 {

				*Summary stats on this group
				qui mean `var' if tmt == `t01' & t == `tmt01' & `regsample' == 1

				mat resTable = r(table)

				//Get the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,1,1)

				*Get the standard error of the mean
				local ++colindex
				mat `var'[1,`colindex'] =  el(resTable,2,1)

				*Get the N of the ordinary means regressions
				local ++colindex
				mat `var'[1,`colindex'] =  `e(N)'
			}
		}

		*Append this row to the result table
		mat 	resultMat = (resultMat \ `var')

	}

	*Remove placeholder row
	matrix resultMat = resultMat[2..., 1...]

	*Show the final matrix will all data needed to start building the output
	matlist resultMat

	******
	*Then the result matrix can be passed into subcommands that output in either Excel, LaTeX or in the main window.

	*The results can be accessed like this, which makes the sub-commands less sensitive to changing column order in the section above.
	mat A = resultMat[3, "BC_Mean"] // returns a 1x1 matrix
	local a = el(A,1,1)				// returns the value

	matlist A
	di `a'