/***************************************************************************************************
v0.1 07/08/2015: first release
v0.2 07/09/2015: correct normalization for loadings
v0.3 04/12/2017: correct weight
v0.4 09/01/2021: remove error when N < T + preserve tsset
***************************************************************************************************/
program define regife, sortpreserve
	version 12.0
	syntax [varlist(min=1 numeric fv ts)] [if] [in] [aweight fweight pweight iweight] , [Factors(string) ife(string) vce(string) Absorb(string) RESiduals(string) * ]

	if "`factors'" != ""{
		di as  txt "The option factors() was renamed to ife(). In the future, please use the syntax ife(`factors') to specify the factor model."
		local ife `factors'
	}
	if "`ife'" == ""{
		di as error "option ife() required"
		exit 111
	}


	local optionlist `options'

	/* syntax absorb */
	while (regexm("`absorb'", "[ ][ ]+")) {
		local absorb : subinstr local absorb "  " " ", all
	}
	local absorb : subinstr local absorb " =" "=", all
	local absorb : subinstr local absorb "= " "=", all
	

	if "`residuals'" ~= ""{
		confirm new variable `residuals'
	}

	cap which reghdfe.ado
	if _rc {
		di as error "reghdfe.ado required: {stata ssc install reghdfe}"
		exit 111
	}


	tokenize `absorb'
	while "`1'" ~= ""{	 
		if regexm("`1'", "(.+)=(.+)"){
			cap confirm new variable `=regexs(1)'
			if _rc{
				if _rc == 198{
					di as error "variable `=regexs(1)' is not a valid name"
				}
				else if _rc == 110{
					di as error "variable `=regexs(1)' already defined"
				}
				exit 198
			}

			local absorbvars `absorbvars' `=regexs(2)'
		}
		else{
			local absorbvars `absorbvars' `1'
		}
		macro shift
	}

	/* syntax factors */
	if regexm("`ife'", "(.*),(.*)"){
		local factors  = regexs(1)
		local dimension = regexs(2)
	}
	else{
		di as error "dimensions should be specified within the option factors"
		exit
	}




	while (regexm("`factors'", "[ ][ ]+")) {
		local factors : subinstr local factors "  " " ", all
	}
	local factors : subinstr local factors " =" "=", all
	local factors : subinstr local factors "= " "=", all

	cap assert `: word count `factors'' == 2
	if _rc{
		di as error "There must be exactly two variables in the option factors"
		exit 
	}

	forv i = 1/2{	 
		local f : word `i' of `factors'
		if regexm("`f'", "(.+)=(.+)"){
			local id`i'gen `=regexs(1)'
			local id`i' = regexs(2)
			forval d = 1/`dimension'{
				confirm new variable `id`i'gen'`d'
			}
		}
		else{
			local id`i' `f'
		}
		confirm var `id`i''
	}

	local id `id1'
	local time `id2'



	if ("`weight'"!=""){
		local wtype `weight'
		local wvar  `=subinstr("`exp'","=","", .)'
	}


	/* touse */
	marksample touse
	markout `touse' `id' `time' `wvar', strok

	/*syntax varlist */  
	fvrevar `varlist' if `touse'
	local varlist = r(varlist)
	foreach v in `varlist'{
		local tvar : char `v'[tsrevar]
		local fvar : char `v'[fvrevar]
		if "`tvar'`fvar'" ~=""{
			local namelist `namelist'  `tvar'`fvar'
		}
		else{
			local namelist `namelist' `v'
		}
	}
	tokenize `varlist'
	local y `1'
	macro shift 
	local x `*'
	tokenize `namelist'
	local yname `1'
	macro shift 
	local xname `*'


	* syntax errors
	if "`vce'" ~= ""{
		local 0 `vce'
		syntax anything [, CLuster(varname) *]
		if "`anything'" == "bootstrap"{
			local bootstrap "yes"
			local bootstrapoptions `options'
		}
	}


	* mean weight
	if "`wvar'" ~= ""{
		tempvar wvar2
		qui bys `touse' `id': gen double `wvar2' = sum(`wvar') if `touse'
		qui by `touse' `id': replace `wvar2' = `wvar2'[_N]/_N if `touse'
		local wvar = "`wvar2'"
	}


	if "`bootstrap'" ~= "yes" {
		innerregife, dimension(`dimension') id(`id') time(`time') idgen(`id1gen') timegen(`id2gen') resgen(`residuals') y(`y') x(`x') yname(`yname') xname(`xname') touse(`touse') wtype(`wtype') wvar(`wvar')  absorbvars(`absorbvars') absorb(`absorb') vce(`vce')  `optionlist'
	}
	else{
		/* get bstart */
		qui innerregife, dimension(`dimension') id(`id') time(`time') idgen(`id1gen') timegen(`id2gen') resgen(`resgen') y(`y') x(`x') yname(`yname') xname(`xname') touse(`touse') wtype(`wtype') wvar(`wvar')  absorbvars(`absorbvars') absorb(`absorb')  `optionlist' 
		tempname bstart 
		matrix `bstart' = e(b)
		if "`cluster'" == ""{
			bootstrap,  `bootstrapoptions' : ///
			innerregife, dimension(`dimension') id(`id') time(`time') y(`y') x(`x') yname(`yname') xname(`xname') touse(`touse') wtype(`wtype') wvar(`wvar') absorb(`absorb') absorbvars(`absorbvars') fast bstart(`bstart') `optionlist'
		}
		else{
			tempvar clusterid
			cap tsset
			cap local tssettimevar = r(timevar)
			cap local tssetpanelvar = r(panelvar)
			tsset, clear
			if "`id'" == "`cluster'"{
				local absorbvars = substr("`absorbvars'", "`id'", "`clusterid'")
				bootstrap, cluster(`cluster') idcluster(`clusterid') `bootstrapoptions': ///
				innerregife, dimension(`dimension') id(`clusterid') time(`time') y(`y') x(`x') yname(`yname') xname(`xname') touse(`touse') wtype(`wtype') wvar(`wvar') absorb(`absorb') absorbvars(`absorbvars')  fast bstart(`bstart') `optionlist'
			}
			else if "`time'" == "`cluster'"{
				local absorb = substr("`absorb'", "`time'", "`clusterid'")
				bootstrap, cluster(`cluster') idcluster(`clusterid') `bootstrapoptions': ///
				innerregife, dimension(`dimension') id(`id') time(`clusterid') y(`y') x(`x') yname(`yname') xname(`xname') touse(`touse') wtype(`wtype') wvar(`wvar') absorb(`absorb') absorbvars(`absorbvars')  fast bstart(`bstart') `optionlist'
			}
			else{
				bootstrap, cluster(`cluster') idcluster(`clusterid') `bootstrapoptions': ///
				innerregife, dimension(`dimension') id(`id') time(`time') y(`y') x(`x') yname(`yname') xname(`xname') touse(`touse') wtype(`wtype') wvar(`wvar') absorb(`absorb') absorbvars(`absorbvars')  fast  bstart(`bstart')`optionlist'
			}
			if ("`tssettimevar'" != "") {
				tsset `tssetpanelvar' `tssettimevar' 
			}
		}
	}

end