*! version 1.2.3  04aug2020
*! Sebastian Kripfganz, www.kripfganz.de

*==================================================*
****** sequential linear panel data estimation ******

*** citation ***

/*	Kripfganz, S., and C. Schwarz (2019).
	Estimation of linear dynamic panel data models with time-invariant regressors.
	Journal of Applied Econometrics 34: 526-546.		*/

*** version history at the end of the file ***

program define xtseqreg, eclass prop(xt)
	version 12.1
	if replay() {
		if "`e(cmd)'" != "xtseqreg" {
			error 301
		}
		syntax [, Level(passthru) Combine noHEader noOMITted noTABle]
	}
	else {
		_xt, treq
		syntax anything(id="varlist") [if] [in] [, Level(passthru) Combine noHEader noOMITted noTABle *]
		gettoken depvar anything : anything , match(paren) bind
		if `: word count `depvar'' != 1 | `"`paren'"' != "" {
			error 198
		}
		gettoken indepvars1 anything : anything, match(paren) bind
		if `"`paren'"' == "" {
			loc anything		`"`indepvars1' `anything'"'
			loc indepvars1		""
		}
		xtseqreg_gmm (`indepvars1') `depvar' `anything' `if' `in', `options'

		eret loc predict	"xtseqreg_p"
		eret loc estat_cmd	"xtseqreg_estat"
		eret loc tvar		"`_dta[_TStvar]'"
		eret loc ivar		"`_dta[_TSpanel]'"
		eret loc cmdline 	`"xtseqreg `0'"'
		eret loc cmd		"xtseqreg"
	}

	xtseqreg_combine , `combine' `header' `omitted'
	loc header			"`r(header)'"
	loc omitted			"`r(omitted)'"
	if r(combine) {
		tempname b b2 V V2
		mat `b'				= r(b)
		mat `V'				= r(V)
		mat `b2'			= e(b)
		mat `V2'			= e(V)

		eret repost b = `b', ren
		eret repost V = `V'
		eret hidden mat V2	= `V2'
		eret hidden mat b2	= `b2'
	}
	xtseqreg_display , `level' `header' `omitted' `table'
end

*==================================================*
***** estimation *****
program define xtseqreg_gmm, eclass sort
	version 12.1
	gettoken firstvars 0 : 0, match(paren) bind
	syntax varlist(num ts) [if] [in] [,	noCONStant			///
										TEffects			///
										TWOstep				///
										Wmatrix(string)		///
										VCE(passthru)		///
										First(string)		///
										Both				///
										noCOMMONesample		///
										*]					// GMMIV() IV() parsed separately
	marksample touse
	gettoken depvar indepvars : varlist
	if "`indepvars'" == "" & "`constant'" != "" {
		error 102
	}
	if "`both'" != "" {
		if `"`first'"' != "" {
			di as err "options first() and both may not be combined"
			exit 184
		}
		if "`firstvars'" == "" {
			error 102
		}
		if "`commonesample'" == "" {
			qui xtseqreg `depvar' `firstvars' if `touse', `gmm' `constant' `teffects' `twostep' wmatrix(`wmatrix') `vce' `options'
		}
		else {
			qui xtseqreg `depvar' `firstvars' `if' `in', `gmm' `constant' `teffects' `twostep' wmatrix(`wmatrix') `vce' `options'
		}
		if "`constant'" != "" {
			loc first			", `constant'"
		}
	}
	tempvar dtouse
	qui gen byte `dtouse' = `touse'
	markout `dtouse' D.(`varlist')
	qui replace `dtouse' = 0 if !(L.`touse')

	*--------------------------------------------------*
	*** time effects ***
	if "`teffects'" != "" {
		if "`both'" != "" {
			sum `_dta[_TStvar]' if e(sample), mean
		}
		else {
			sum `_dta[_TStvar]' if `touse', mean
		}
		loc tdelta			= int(`_dta[_TSdelta]')
		cap _rmcoll i(`= r(min)+`tdelta'*("`constant'" == "")'(`tdelta')`= r(max)')bn.`_dta[_TStvar]' if `touse', exp `constant'
		if _rc != 0 {
			error 451
		}
		loc teffects		"`r(varlist)'"
		if "`both'" != "" {
			loc firstvars		"`firstvars' `teffects'"
		}
		else {
			loc indepvars		"`indepvars' `teffects'"
		}
	}

	*--------------------------------------------------*
	*** syntax parsing of options for instruments ***
	if `"`options'"' == "" {
		loc ivvars			"`indepvars'"
	}
	else {
		tempvar obs
		qui by `_dta[_TSpanel]': gen `obs' = _n `if' `in'
		sum `obs', mean
		loc maxlag			= r(max) - 1
		drop `obs'
		while `"`options'"' != "" {
			xtseqreg_parse_options , maxlag(`maxlag') `both' `options'
			loc options			`"`s(options)'"'
			if `s(equation)' == 1 & "`firstvars'" != "" {
				if "`both'" == "" {
					di as txt "note: specified instruments for equation #1 ignored"
				}
				continue
			}
			else if `s(equation)' == 2 & "`firstvars'" == "" {
				di as txt "note: specified instruments for equation #2 ignored"
				continue
			}
			if "`s(model)'" == "" {
				loc gmmivvars		"`gmmivvars' `s(gmmiv)'"
				loc ivvars			"`ivvars' `s(iv)'"
			}
			else if `s(ec)' {
				loc ecgmmivvars		"`ecgmmivvars' `s(gmmiv)'"
				loc ecivvars		"`ecivvars' `s(iv)'"
			}
			else {
				loc dgmmivvars		"`dgmmivvars' `s(gmmiv)'"
				loc divvars			"`divvars' `s(iv)'"
			}
		}
		if "`teffects'" != "" & "`both'" == "" {
			loc ivvars "`ivvars' `teffects'"
		}
	}
	if "`constant'" == "" {
		loc indepvars		"`indepvars' `touse'"
		loc ivvars			"`ivvars' `touse'"
	}
	loc gmmivvars		: list retok gmmivvars
	loc ivvars			: list retok ivvars
	loc dgmmivvars		: list retok dgmmivvars
	loc divvars			: list retok divvars
	loc ecgmmivvars		: list retok ecgmmivvars
	loc ecivvars		: list retok ecivvars

	*--------------------------------------------------*
	*** type of weighting matrix ***
	xtseqreg_parse_wmatrix `wmatrix'
	loc wmatrix			"`s(wmatrix)'"
	loc ratio			= `s(ratio)'

	*--------------------------------------------------*
	*** type of variance-covariance matrices ***
	if `"`vce'"' != "" {
		xtseqreg_parse_vce if `touse' , `vce' `twostep'
		loc vce				"`s(vce)'"
		loc vcediff			= `s(vcediff)'
		loc inconsistent	= `s(inconsistent)'
		loc clustvar		"`s(clustvar)'"
	}
	else {
		loc vce				"conventional"
		loc vcediff			= 0
		loc inconsistent	= 0
	}
	loc twostep			= ("`twostep'" != "")

	*--------------------------------------------------*
	*** syntax parsing of first-stage regression ***
	xtseqreg_parse_first (`firstvars') `first'
	loc eqname			`"`s(eqname)'"'
	loc firstvars		"`s(varlist)'"
	if "`firstvars'" != "" {
		if "`s(depvar)'" != "`depvar'" {
			di as txt "note: dependent variable `e(depvar)' from `e(cmd)' does not match with `depvar'"
		}
		tempname b1 V1
		mat `b1'			= `s(b)'
		mat `V1'			= `s(V)'
		if `"`eqname'"' != "" {
			mat `b1'			= `b1'[1, `"`eqname':"']
			mat `V1'			= `V1'[`"`eqname':"', `"`eqname':"']
		}
		loc coeflist		"`s(coeflist)'"
		if `: word count `coeflist'' < colsof(`b1') {
			tempname aux
			foreach coef of loc coeflist {
				mat `aux'			= (nullmat(`aux'), `b1'[1, `"`eqname':`coef'"'])
			}
			mat `b1'			= `aux'
		}
		if `: word count `firstvars'' < colsof(`V1') {
			tempname aux
			foreach coef of loc coeflist {
				mat `aux'			= (nullmat(`aux'), `V1'[`"`eqname':"', `"`eqname':`coef'"'])
			}
			mat `V1'			= `aux'
			mat drop `aux'
			foreach coef of loc coeflist {
				mat `aux'			= (nullmat(`aux') \ `V1'[`"`eqname':`coef'"', `"`eqname':"'])
			}
			mat `V1'			= `aux'
		}
		loc firstvars		: subinstr loc firstvars "_cons" "`touse'", all w
		markout `touse' `firstvars'
		tempvar touse1
		qui gen byte `touse1' = e(sample)
		if "`commonesample'" == "" {
			qui replace `touse' = 0 if !`touse1'
			qui replace `dtouse' = 0 if !`touse1'
		}
		if "`vce'" == "robust" & !(inlist("`e(vce)'", "robust", "cluster") | inlist("`e(vcetype)'", "Robust")) {
			di as txt "note: first-stage standard errors may not be robust"
		}
	}
	else if `inconsistent' {
		di as err "vcetype `vce' not allowed with argument 'inconsistent'"
		exit 198
	}
	sum `touse', mean
	if r(sum) == 0 {
		error 2000
	}

	*--------------------------------------------------*
	*** influence function ***
	if "`firstvars'" != "" & !`inconsistent' {
		if "`s(influence)'" == "" {
			loc V0				"`s(V_modelbased)'"
			if "`e(cmd)'" == "xtseqreg" {
				if `: word count `s(eqnames)'' > 1 {
					di as err "cannot compute scores after xtseqreg with two equations"
					exit 321
				}
				forv k = 1/`: word count `s(coefs1)'' {
					tempvar influence`k'
					loc influence_all	"`influence_all' `influence`k''"
					loc var				: word `k' of `s(coefs1)'
					if `: list var in coeflist' {
						loc influence		"`influence' `influence`k''"
					}
				}
				qui predict double `influence_all' if `touse1', score
			}
			else {
				loc scores			"`s(scores)'"
				loc eqnames			`"`s(eqnames)'"'
				loc coeflist		: subinstr loc coeflist "_cons" "`touse1'", all w
				forv e = 1/`: word count `eqnames'' {
					loc coefs`e'		"`s(coefs`e')'"
					if "`scores'" == "" {
						tempvar score`e'
						loc scorevars		"`scorevars' `score`e''"
					}
					else {
						loc score`e'		: word `e' of `scores'
					}
				}
				if "`scorevars'" != "" {
					cap predict double `scorevars' if `touse1', score
					if _rc != 0 {
						di as err "cannot compute scores after `e(cmd)'"
						exit 321
					}
				}
				forv e = 1/`: word count `eqnames'' {
					loc eq				: word `e' of `eqnames'
					loc coefs`e'		: subinstr loc coefs`e' "_cons" "`touse1'", all w
					forv k = 1/`: word count `coefs`e''' {
						loc var				: word `k' of `coefs`e''
						tempvar influence`e'_`k'
						qui gen double `influence`e'_`k'' = `var' * `score`e'' if `touse1'
						loc influence_all	"`influence_all' `influence`e'_`k''"
						if (`"`eq'"' == `"`eqname'"' & `: list var in coeflist') | `"`eqname'"' == "" {
							loc influence		"`influence' `influence`e'_`k''"
						}
					}
				}
				if "`scores'" == "" {
					drop `scorevars'
				}
			}
			mata: xtseqreg_influence("`influence_all'", "`V0'", "", "`touse1'")
			cap drop `: list influence_all - influence'
		}
		else {
			loc influence		"`s(influence)'"
		}
	}
	else {
		loc influence		""
	}

	*--------------------------------------------------*
	*** estimation ***
	foreach tsvarlist in depvar indepvars ivvars gmmivvars divvars dgmmivvars ecivvars ecgmmivvars firstvars {
		fvrevar ``tsvarlist'' if `touse'
		loc t`tsvarlist'	"`r(varlist)'"
	}
	if "`clustvar'" != "" {
		sort `clustvar' `_dta[_TSpanel]' `_dta[_TStvar]'
	}
	mata: xtseqreg_est(	"`tdepvar'",					/// dependent variable
						"`tindepvars'",					/// regressor variables
						"`tivvars'",					/// standard instruments
						"`tgmmivvars'",					/// GMM instruments
						"`tdivvars'",					/// standard instruments for first-differenced model
						"`tdgmmivvars'",				/// GMM instruments for first-differenced model
						"`tecivvars'",					/// collapsed GMM homoskedasticity instruments for first-differenced model
						"`tecgmmivvars'",				/// GMM homoskedasticity instruments for first-differenced model
						"`tfirstvars'",					/// first-stage regressor variables
						"`influence'",					/// first-stage influence function variables
						"`_dta[_TSpanel]'",				/// panel identifier
						"`_dta[_TStvar]'",				/// time identifier
						"`touse'",						/// marker variable
						"`dtouse'",						/// marker variable for first-differenced model
						"`b1'",							/// first-stage coefficient vector
						"`V1'",							/// first-stage variance-covariance matrix
						"`vce'",						/// type of the variance-covariance matrix
						"`clustvar'",					/// cluster variable
						`vcediff',						/// error variance from first-differenced residuals
						"`wmatrix'",					/// type of the weighting matrix
						`ratio',						/// random-effects variance ratio
						`twostep',						/// two-step estimation
						`inconsistent')
	tempname b V W
	mat `b'				= r(b)
	mat `V'				= r(V)
	if "`firstvars'" == "" {
		if !("`vce'" != "robust" & `twostep') {
			tempname V0
			mat `V0'			= r(V0)
		}
		if `twostep' & "`vce'" == "robust" {
			tempname b1 W1 V01
			mat `b1'			= r(b1)
			mat `V01'			= r(V1)
			mat `W1'			= r(W1)
		}
	}
	mat `W'				= r(W)
	loc N				= r(N)
	loc N_g				= r(N_g)
	loc N_clust			= r(N_clust)
	loc T_min			= r(T_min)
	loc T_max			= r(T_max)
	loc	k_all			= r(rank)
	loc	k				= r(rank2)
	loc k_Z				= r(zrank)
	loc sigma2e			= r(sigma2e)
	loc sigma2u			= r(sigma2u)
	loc J				= r(J)

	loc indepvars		: subinstr loc indepvars "`touse'" "_cons", w
	if "`firstvars'" != "" {
		loc firstvars		: subinstr loc firstvars "`touse'" "_cons", w
		foreach var of loc firstvars {
			loc regnames		"`regnames' _first:`var'"
		}
		foreach var of loc indepvars {
			loc regnames		"`regnames' _second:`var'"
		}
	}
	else {
		loc regnames		"`indepvars'"
	}
	mat coln `b'		= `regnames'
	mat rown `V'		= `regnames'
	mat coln `V'		= `regnames'
	loc firstvars		= ("`firstvars'" != "")
	if !`firstvars' {
		if !("`vce'" != "robust" & `twostep') {
			mat rown `V0'		= `regnames'
			mat coln `V0'		= `regnames'
		}
		if `twostep' & "`vce'" == "robust" {
			mat coln `b1'		= `regnames'
			mat rown `V01'		= `regnames'
			mat coln `V01'		= `regnames'
		}
	}
	loc ivvars			: subinstr loc ivvars "`touse'" "_cons", w

	*--------------------------------------------------*
	*** prior estimation results ***
	if `firstvars' {
		loc k1				= e(rank)
		if "`both'" != "" | "`e(cmd)'" == "xtseqreg" {
			loc ecgmmivvars1	"`e(ecgmmivvars_1)'"
			loc ecivvars1		"`e(ecivvars_1)'"
			loc dgmmivvars1		"`e(dgmmivvars_1)'"
			loc divvars1		"`e(divvars_1)'"
			loc gmmivvars1		"`e(gmmivvars_1)'"
			loc ivvars1			"`e(ivvars_1)'"
		}
		tempvar ttouse1
		qui by `_dta[_TSpanel]': egen `ttouse1' = total(`touse1') if `touse1'
		qui xtsum `ttouse1'
		loc N1				= r(N)
		loc N_g1			= r(n)
		loc T_min1			= r(min)
		loc T_max1			= r(max)
		if "`both'" == "" {
			qui replace `ttouse1' = `touse'
			qui replace `ttouse1' = `touse1' if `touse1' & !`touse'
			qui xtsum `ttouse1' if `ttouse1'
		}
		else {
			qui replace `touse' = `touse1' if `touse1' & !`touse'
			qui xtsum `touse' if `touse'
		}
		loc N12				= r(N)
		loc N_g12			= r(n)
	}
	else {
		loc N12				= `N'
	}
	tempname stats
	if `firstvars' {
		if "`both'" == "" & "`e(cmd)'" != "xtseqreg" {
			mat `stats'			= (`N1', `N_g1', `T_min1', `N1' / `N_g1', `T_max1', `k1', ., .)
		}
		else {
			mat `stats'			= e(stats)
		}
	}
	mat `stats'			= (nullmat(`stats') \ `N', `N_g', `T_min', `N' / `N_g', `T_max', `k', `k_Z', `J')
	loc statistics		"N N_g g_min g_avg g_max rank zrank chi2_J"
	mat coln `stats'	= `statistics'

	*--------------------------------------------------*
	*** current estimation results ***
	eret post `b' `V', dep(`depvar') o(`N12') e(`touse')
	if `firstvars' {
		eret sca N_g		= `N_g12'
		eret sca rank		= `k_all'
	}
	else {
		eret sca N_g		= `N_g'
		if "`vce'" == "robust" {
			eret sca N_clust	= `N_clust'
		}
		eret sca rank		= `k'
	}
	if "`vce'" != "robust" & !(`twostep' & "`vce'" == "conventional") {
		eret sca sigma2e		= `sigma2e'
		eret sca sigma2u		= `sigma2u'
	}
	eret sca twostep	= `twostep'
	if `inconsistent' {
		eret loc vcetype	"Inconsistent"
	}
	else if `twostep' & "`vce'" == "robust" {
		eret loc vcetype	"WC-Robust"
	}
	else if "`vce'" == "robust" {
		eret loc vcetype	"Robust"
	}
	if "`clustvar'" == "" {
		eret loc vce		"`vce'"
	}
	else {
		eret loc vce		"cluster"
		eret loc clustvar	"`clustvar'"
	}
	eret loc wmatrix	"`wmatrix', ratio(`ratio')"
	if `firstvars' {
		eret loc ecgmmivvars_2 "`ecgmmivvars'"
		eret loc ecivvars_2	"`ecivvars'"
		eret loc dgmmivvars_2 "`dgmmivvars'"
		eret loc divvars_2	"`divvars'"
		eret loc gmmivvars_2 "`gmmivvars'"
		eret loc ivvars_2	"`ivvars'"
		if "`both'" != "" {
			eret loc ecgmmivvars_1 "`ecgmmivvars1'"
			eret loc ecivvars_1	"`ecivvars1'"
			eret loc dgmmivvars_1 "`dgmmivvars1'"
			eret loc divvars_1	"`divvars1'"
			eret loc gmmivvars_1 "`gmmivvars1'"
			eret loc ivvars_1	"`ivvars1'"
		}
	}
	else {
		eret loc ecgmmivvars_1 "`ecgmmivvars'"
		eret loc ecivvars_1	"`ecivvars'"
		eret loc dgmmivvars_1 "`dgmmivvars'"
		eret loc divvars_1	"`divvars'"
		eret loc gmmivvars_1 "`gmmivvars'"
		eret loc ivvars_1	"`ivvars'"
	}
	eret loc teffects	"`teffects'"
	eret mat stats		= `stats'
	if !`firstvars' & (`twostep' & "`vce'" == "robust") {
		eret mat W_onestep		= `W1'
		eret mat V_onestep		= `V01'
		eret mat b_onestep		= `b1'
	}
	eret mat W			= `W'
	if !`firstvars' & !("`vce'" != "robust" & `twostep') {
		eret mat V_modelbased	= `V0'
	}
end

*==================================================*
**** display of estimation results ****
program define xtseqreg_display
	version 12.1
	syntax [, Level(cilevel) noHEader noOMITted noTABle]

	if "`header'" == "" {
		di _n as txt "Group variable: " as res abbrev("`e(ivar)'", 12) _col(46) as txt "Number of obs" _col(68) "=" _col(70) as res %9.0f e(N)
		di as txt "Time variable: " as res abbrev("`e(tvar)'", 12) _col(46) as txt "Number of groups" _col(68) "=" _col(70) as res %9.0f e(N_g)
		if rowsof(e(stats)) == 1 {
			di _n _col(46) as txt "Obs per group:" _col(64) "min =" _col(70) as res %9.0g el(e(stats), 1, 3)
			di _col(64) as txt "avg =" _col(70) as res %9.0g el(e(stats), 1, 4)
			di _col(64) as txt "max =" _col(70) as res %9.0g el(e(stats), 1, 5)
			di _n _col(46) as txt "Number of instruments =" _col(70) as res %9.0f el(e(stats), 1, 7)
		}
		else {
			di _n as txt "{hline 78}"
			di as txt "Equation " as res "_first" _col(46) as txt "Equation " as res "_second"
			di as txt "Number of obs" _col(23) "=" _col(25) as res %9.0f el(e(stats), 1, 1) _col(46) as txt "Number of obs" _col(68) "=" _col(70) as res %9.0f el(e(stats), 2, 1)
			di as txt "Number of groups" _col(23) "=" _col(25) as res %9.0f el(e(stats), 1, 2) _col(46) as txt "Number of groups" _col(68) "=" _col(70) as res %9.0f el(e(stats), 2, 2)
			di _n as txt "Obs per group:" _col(19) "min =" _col(25) as res %9.0g el(e(stats), 1, 3) _col(46) as txt "Obs per group:" _col(64) "min =" _col(70) as res %9.0g el(e(stats), 2, 3)
			di _col(19) as txt "avg =" _col(25) as res %9.0g el(e(stats), 1, 4) _col(64) as txt "avg =" _col(70) as res %9.0g el(e(stats), 2, 4)
			di _col(19) as txt "max =" _col(25) as res %9.0g el(e(stats), 1, 5) _col(64) as txt "max =" _col(70) as res %9.0g el(e(stats), 2, 5)
			if el(e(stats), 1, 7) < . {
				di _n as txt "Number of instruments =" _col(25) as res %9.0f el(e(stats), 1, 7) _col(46) as txt "Number of instruments =" _col(70) as res %9.0f el(e(stats), 2, 7)
			}
			else {
				di _n _col(46) as txt "Number of instruments =" _col(70) as res %9.0f el(e(stats), 2, 7)
			}
		}
	}
	if "`table'" == "" {
		di ""
		eret di, l(`level') `omitted'
	}
end

*==================================================*
**** syntax parsing of options for instruments ****
program define xtseqreg_parse_options, sclass
	version 12.1
	sret clear
	syntax , MAXLAG(integer) [Both GMMiv(string) IV(string) *]

	*--------------------------------------------------*
	*** GMM instruments ***
	if `"`gmmiv'"' != "" {
		gettoken gmmivvars gmmiv : gmmiv, p(",")
		if "`gmmiv'" == "" {
			loc gmmiv			","
		}
		xtseqreg_parse_gmmiv `gmmivvars' `gmmiv' maxlag(`maxlag')
		if "`both'" != "" & `"`s(equation)'"' == "" {
			di as err "option gmmiv() incorrectly specified -- equation() required"
			exit 198
		}
	}

	*--------------------------------------------------*
	*** standard instruments ***
	if "`gmmivvars'" == "" {
		if `"`iv'"' != "" {
			xtseqreg_parse_iv `iv'
			if "`both'" != "" & `"`s(equation)'"' == "" {
				di as err "option iv() incorrectly specified -- equation() required"
				exit 198
			}
		}
		else if `"`options'"' == "" {
			error 198
		}
		else {
			di as err `"`options' invalid"'
			exit 198
		}
	}
	else if `"`iv'"' != "" {
		loc options			`"iv(`iv') `options'"'
	}

	*--------------------------------------------------*
	*** equation for sequential estimation ***
	if `"`s(equation)'"' == "" {
		loc eq				= .
	}
	else if `: word count `s(equation)'' > 1 {
		di as err "option equation() incorrectly specified"
		exit 198
	}
	else if substr(`"`s(equation)'"', 1, 1) == "#" {
		loc eq				= substr(`"`s(equation)'"', 2, .)
		cap conf integer num `eq'
		if _rc != 0 | (`eq' != 1 & `eq' != 2) {
			di as err "option equation() incorrectly specified -- equation `s(equation)' not found"
			exit 303
		}
	}
	else if `"`s(equation)'"' == "_first" {
		loc eq				= 1
	}
	else if `"`s(equation)'"' == "_second" {
		loc eq				= 2
	}
	else {
		di as err "option equation() incorrectly specified -- equation `s(equation)' not found"
		exit 303
	}

	sret loc equation		= `eq'
	sret loc options		`"`options'"'
end

*==================================================*
**** syntax parsing for GMM instruments ****
program define xtseqreg_parse_gmmiv, sclass
	version 12.1
	syntax varlist(num ts), MAXLag(integer) [Lagrange(numlist max=2 int miss) Difference Collapse EC Model(string) EQuation(string)]

	if "`ec'" == "" {
		if "`difference'" != "" {
			loc --maxlag
		}
		gettoken lag1 lag2 : lagrange
		if "`lag1'" == "" {
			loc lag1			= 1
		}
		else if `lag1' == . {
			loc lag1			= - `maxlag'
		}
		if "`lag2'" == "" {
			loc lag2			= `maxlag'
		}
		else if `lag2' == . {
			loc lag2			= `maxlag'
		}
		if `lag1' > `lag2' {
			di as err "option lagrange() incorrectly specified -- invalid numlist has elements out of order"
			exit 124
		}
		foreach var of loc varlist {
			if "`difference'" == "" {
				loc gmmivvars		"`gmmivvars' L(`lag1'/`lag2').`var'"
			}
			else {
				loc gmmivvars		"`gmmivvars' L(`lag1'/`lag2')D.`var'"
			}
		}
	}
	else if "`lagrange'" != "" {
		di as err "options ec and lagrange() may not be combined"
		exit 184
	}
	else if "`difference'" == "" {
		loc gmmivvars		"`varlist'"
	}
	else {
		foreach var of loc varlist {
			loc gmmivvars		"`gmmivvars' D.`var'"
		}
	}
	xtseqreg_parse_model , `model' `ec'

	if ("`collapse'" == "") {
		sret loc gmmiv		"`gmmivvars'"
	}
	else {
		sret loc iv			"`gmmivvars'"
	}
	sret loc equation	`"`equation'"'
end

*==================================================*
**** syntax parsing for standard instruments ****
program define xtseqreg_parse_iv, sclass
	version 12.1
	syntax varlist(num ts) [, Difference Model(string) EQuation(string)]

	if "`difference'" == "" {
		loc ivvars			"`varlist'"
	}
	else {
		foreach var of loc varlist {
			loc ivvars			"`ivvars' D.`var'"
		}
	}
	xtseqreg_parse_model , `model'

	sret loc iv			"`ivvars'"
	sret loc equation	`"`equation'"'
end

*==================================================*
**** syntax parsing for the model equations ****
program define xtseqreg_parse_model, sclass
	version 12.1
	syntax [, Level Difference EC]

	if "`difference'" != "" {
		if "`level'" != "" {
			di as err "option model() incorrectly specified"
			exit 198
		}
	}
	else if "`ec'" != "" {
		if "`level'" != "" {
			di as err "options ec and model(level) may not be combined"
			exit 184
		}
		loc difference		"difference"
	}

	sret loc model		"`difference'"
	sret loc ec			= ("`ec'" != "")
end

*==================================================*
**** syntax parsing for weighting matrix ****
program define xtseqreg_parse_wmatrix, sclass
	version 12.1
	syntax [anything] , [Ratio(numlist max=1 >=0)]

	loc length			: length loc anything
	if `"`anything'"' == "" | `"`anything'"' == substr("unadjusted", 1, max(2, `length')) {
		loc anything		"unadjusted"
	}
	else if `"`anything'"' == substr("independent", 1, max(3, `length')) {
		loc anything		"independent"
	}
	else if `"`anything'"' == substr("separate", 1, max(3, `length')) {
		loc anything		"separate"
	}
	else if `"`anything'"' == substr("identity", 1, max(2, `length')) {		// undocumented wmat_type
		if "`ratio'" != "" {
			di as err "option ratio() not allowed with wmatrix(identity)"
			error 198
		}
		loc anything		"identity"
	}
	else {
		di as err "option wmatrix() incorrectly specified"
		exit 198
	}
	if "`ratio'" == "" {
		loc ratio			= 0
	}

	sret loc wmatrix	"`anything'"
	sret loc ratio		= `ratio'
end

*==================================================*
**** syntax parsing for variance-covariance matrix ****
program define xtseqreg_parse_vce, sclass
	version 12.1
	syntax [if] [in] , [VCE(passthru) TWOstep]

	loc vcediff			= 0
	loc inconsistent	= 0
	cap _vce_parse , opt(CONVENTIONAL EC Robust) argopt(CLuster) : , `vce'
	if "`r(vce)'" == "" {
		cap _vce_parse , : , `vce'
	}
	if _rc != 0 {
		cap _vce_parse , argopt(CONVENTIONAL EC Robust CLuster) : , `vce'
		if _rc != 0 {
			if "`r(vce)'" == "cluster" {
				di as err "option vce(cluster) incorrectly specified"
				exit 198
			}
			_vce_parse , : , `vce'
		}
		loc vceargs			"`r(vceargs)'"
		loc vceargs			: subinstr loc vceargs "," ""
		loc vceargs			: list retokenize vceargs
		foreach vcearg of loc vceargs {
			if "`vcearg'" == substr("difference", 1, max(1, `: length loc vcearg')) & ("`r(vce)'" == "" | "`r(vce)'" == "conventional") {
				if "`twostep'" != "" {
					di as err "invalid vce() option"
					exit 198
				}
				loc vcediff			= 1
			}
			else if "`vcearg'" == "inconsistent" {		// undocumented vce argument
				loc inconsistent	= 1
			}
			else {
				di as err "invalid vce() option"
				exit 198
			}
		}
	}
	loc vce				"`r(vce)'"
	if "`vce'" == "cluster" {
		if `: word count `r(cluster)'' > 1 {
			di as err "option vce(cluster) incorrectly specified -- too many arguments"
			exit 198
		}
		loc clustvar		"`r(cluster)'"
		if "`clustvar'" != "`_dta[_TSpanel]'" {
			xtseqreg_cluster `clustvar' `if' `in'
		}
	}

	if "`vce'" == "" {
		sret loc vce		"conventional"
	}
	else if "`vce'" == "cluster" {
		sret loc vce		"robust"
		sret loc clustvar	"`clustvar'"
	}
	else {
		sret loc vce		"`vce'"
	}
	sret loc vcediff		= `vcediff'
	sret loc inconsistent	= `inconsistent'
end

*==================================================*
**** check if panel identifier is nested within cluster identifier ****
// (inspired by _xtreg_chk_cl2.ado)
program define xtseqreg_cluster, rclass sort
	syntax varname [if] [in]
	marksample touse
	sort `varlist'

	tempname aux aux2
	qui by `varlist': gen long `aux' = cond(_n == 1, 1, 0) if `touse'
	qui replace `aux' = sum(`aux')
	sort `_dta[_TSpanel]' `varlist'
	qui by `_dta[_TSpanel]': gen long `aux2' = `aux'[1] - `aux'[_N]
	qui count if `aux2' != 0 & `touse'
	if r(N) > 0 {
		di as err "panels are not nested within clusters"
		exit 498
	}
end

*==================================================*
**** syntax parsing of the first-stage regression ****
program define xtseqreg_parse_first, sclass
	version 12.1
	sret clear
	gettoken indepvars 0 : 0, match(paren) bind
	syntax [name] , [noCONStant EQuation(string) COPY B(name) V(namelist max=2) SCores(varlist num) INFluence(varlist num)]

	*--------------------------------------------------*
	*** first-stage variables ***
	if "`indepvars'" == "" {
		if `: word count `0'' > 0 {
			error 102
		}
		exit
	}
	else {
		qui _rmcoll `indepvars', exp `constant'
		loc indepvars		"`r(varlist)'"
	}

	*--------------------------------------------------*
	*** first-stage estimation results ***
	if "`namelist'" != "" {
		qui est res `namelist'
	}
	tempvar touse
	qui gen byte `touse' = e(sample)
	sum `touse', mean
	if r(mean) == 0 {
		error 301
	}
	if "`b'" == "" {
		loc b				"e(b)"
	}
	tempname b1
	mat `b1'			= `b'
	loc copy			= ("`copy'" != "")
	if `copy' {
		loc eqnames			: coleq e(b), q
	}
	else {
		loc eqnames			: coleq `b1', q
	}
	loc eqnames			: list uniq eqnames
	if "`influence'" == "" {
		if "`v'" == "" {
			loc v1				"e(V)"
			cap conf mat e(V_modelbased)
			if _rc == 0 {
				loc v0				"e(V_modelbased)"
			}
			else {
				if inlist("`e(vce)'", "robust", "cluster", "bootstrap", "jackknife") | inlist("`e(vcetype)'", "Robust", "WC-Robust", "Bootstrap", "Jackknife") {
					di as err `"cannot compute scores based on `= cond("`e(vce)'" != "", "vce(`e(vce)')", lower("`e(vcetype)' std. err."))' after `e(cmd)'"'
					exit 322
				}
				loc v0				"e(V)"
			}
		}
		else {
			foreach vname of loc v {
				conf mat `vname'
				if rowsof(`vname') != colsof(`vname') | colsof(`vname') != colsof(`b1') {
					di as err "option v() incorrectly specified -- conformability error"
					exit 503
				}
				if "`v1'" == "" {
					loc v1				"`vname'"
				}
				else {
					loc v0				"`vname'"
				}
			}
			if "`v0'" == "" {
				loc v0				"`v1'"
			}
		}
		if ("`: colf `v1''" != "`: colf `b1''" | "`: colf `v0''" != "`: colf `b1''") & !`copy' {
			di as err "option b() or v() incorrectly specified -- column names do not match"
			exit 322
		}
	}

	*--------------------------------------------------*
	*** first-stage scores ***
	if "`scores'" != "" {
		if "`influence'" != "" {
			di as err "options scores() and influence() may not be combined"
			exit 184
		}
		if `: word count `scores'' < `: word count `eqnames'' {
			di as err "option scores() incorrectly specified -- too few variables specified"
			exit 102
		}
		if `: word count `scores'' > `: word count `eqnames'' {
			di as err "option scores() incorrectly specified -- too many variables specified"
			exit 103
		}
	}

	*--------------------------------------------------*
	*** first-stage coefficients and variables ***
	if `"`equation'"' == "" {
		if !`copy' {
			loc eqname			: word 1 of `eqnames'
		}
	}
	else if `copy' {
		di as err "options equation() and copy may not be combined"
		exit 184
	}
	else if `: word count `equation'' > 1 {
		di as err "option equation() incorrectly specified"
		exit 198
	}
	else if substr(`"`equation'"', 1, 1) == "#" {
		loc eq				= substr(`"`equation'"', 2, .)
		cap conf integer num `eq'
		loc eqname			: word `eq' of `eqnames'
		if _rc != 0 | "`eqname'" == "" {
			di as err "option equation() incorrectly specified -- equation `equation' not found"
			exit 303
		}
	}
	else if `: list equation in eqnames' {
		loc eqname			`"`equation'"'
		loc eq				: posof `"`eqname'"' in eqnames
	}
	else {
		di as err "option first() incorrectly specified -- equation `equation' not found"
		exit 303
	}
	if `: word count `e(depvar)'' > 1 {
		loc depvar			: word `eq' of `e(depvar)'
	}
	else {
		loc depvar			"`e(depvar)'"
	}
	tempname beq
	if `copy' {
		if "`constant'" == "" {
			loc indepvars		"`indepvars' _cons"
		}
		loc bvars			: coln `b1'
		if `: word count `indepvars'' != `: word count `bvars'' {
			di as err "option first() incorrectly specified"
			exit 322
		}
	}
	else {
		mat `beq'			= `b1'[1, `"`eqname':"']
		loc bvars			: coln `beq'
		loc cons			"_cons"
		if `: list cons in bvars' {
			loc indepvars		"`indepvars' _cons"
		}
		if (`: word count `indepvars'' < `: word count `bvars'' & !`: list indepvars in bvars') | (`: word count `indepvars'' > `: word count `bvars'') {
			di as err "option first() incorrectly specified -- variable names do not match"
			exit 322
		}
		if "`constant'" != "" {
			loc indepvars		: list indepvars - cons
			loc bvars			: list bvars - cons
		}
		if `: word count `indepvars'' == `: word count `bvars'' & !`: list indepvars === bvars' {
			di as txt "note: first-stage variable names do not match with coefficient list from `e(cmd)'"
		}
		else {
			loc drop			: list bvars - indepvars
			loc indepvars		: list bvars - drop
			loc bvars			"`indepvars'"
		}
	}

	*--------------------------------------------------*
	*** first-stage influence functions ***
	if "`influence'" == "" {
		loc e				= 1
		foreach eq of loc eqnames {
			mat `beq'			= `b1'[1, "`eq':"]
			loc coefs`e'		: coln `beq'
			sret loc coefs`e'	"`coefs`e''"
			loc ++e
		}
		sret loc scores		"`scores'"
		sret loc eqnames	`"`eqnames'"'
		sret loc V_modelbased "`v0'"
	}
	else {
		if `: word count `influence'' < `: word count `indepvars'' {
			di as err "option influence() incorrectly specified -- too few variables specified"
			exit 102
		}
		if `: word count `influence'' > `: word count `indepvars'' {
			di as err "option influence() incorrectly specified -- too many variables specified"
			exit 103
		}
		sret loc influence	"`influence'"
	}
	sret loc V			"`v1'"
	sret loc b			"`b'"
	sret loc depvar		"`depvar'"
	sret loc varlist	"`indepvars'"
	sret loc coeflist	"`bvars'"
	sret loc eqname		`"`eqname'"'
end

*==================================================*
**** combine first-stage and second-stage results ****
program define xtseqreg_combine, rclass
	version 12.1
	syntax , [Combine noHEader noOMITted]
	loc combine			= ("`combine'" != "")

	if `combine' & rowsof(e(stats)) != 2 {
		di as err "option combine not allowed"
		exit 198
	}
	if `combine' {
		loc header			"noheader"
		loc omitted			"noomitted"
	}

	tempname b V
	cap conf mat e(b2)
	if _rc != 0 & !`combine' {
		loc combine			= 0
	}
	else if _rc == 0 {
		mat `b'				= e(b2)
		loc eqnames			: coleq `b', q
		loc eqnames			: list uniq eqnames
		if `: word count `eqnames'' + `combine' != 2 {
			loc combine			= 0
		}
		else {
			loc combine			= 1
			mat `V'				= e(V2)
		}
	}
	else {
		tempname b1 b2
		mat `b'				= e(b)
		mat `b1'			= `b'[1, "_first:"]
		mat `b2'			= `b'[1, "_second:"]
		mat coleq `b'		= ""
		loc regnames		: coln `b'
		loc dupnames		: list dups regnames
		if "`dupnames'" != "" {
			if "`dupnames'" != "_cons" {
				di as err "cannot combine equations -- repeated variable names"
				exit 321
			}
			mat coleq `b1'		= ""
			mat coleq `b2'		= ""
			loc regnames1		: coln `b1'
			loc regnames2		: coln `b2'
			loc regnum1			: word count `regnames1'
			loc regnum2			: word count `regnames2'
			if "`: word `regnum1' of `regnames1''" != "_cons" {
				di as err "option combine not allowed -- _cons is not ordered last in equation #1"
				exit 198
			}
			if `regnum1' > 1 {
				mat `b'				= `b1'[1, "`: word 1 of `regnames1''" .. "`: word `= `regnum1' - 1' of `regnames1''"]
				if `regnum2' > 1 {
					mat `b'				= (`b', `b2'[1, "`: word 1 of `regnames2''" .. "`: word `= `regnum2' - 1' of `regnames2''"])
				}
				mat `b'				= (`b', `b1'[1, "_cons"] + `b2'[1, "_cons"], 0)
			}
			else if `regnum2' > 1 {
				mat `b'				= (`b2'[1, "`: word 1 of `regnames2''" .. "`: word `= `regnum2' - 1' of `regnames2''"], `b1'[1, "_cons"] + `b2'[1, "_cons"], 0)
			}
			else {
				mat `b'				= (`b1'[1, "_cons"] + `b2'[1, "_cons"], 0)
			}
			tempname g
			mat `g'				= (I(`= `regnum1' - 1'), J(`= `regnum1' - 1', `= 1 + `regnum2'', 0) \ J(1, `= `regnum1' + `regnum2' - 2', 0), 1, 0 \ J(`regnum2', `= `regnum1' - 1', 0), I(`regnum2'), J(`regnum2', 1, 0))
			mat `V'				= `g'' * e(V) * `g'
			loc cons			"_cons"
			loc regnames		"`: list regnames1 - cons' `regnames2' o._cons"
			mat coln `b'		= `regnames'
			mat coln `V'		= `regnames'
			mat rown `V'		= `regnames'
		}
		else {
			mat `V'				= e(V)
			mat coleq `V'		= ""
			mat roweq `V'		= ""
		}
	}

	ret sca combine		= `combine'
	ret loc header		"`header'"
	ret loc omitted		"`omitted'"
	if `combine' {
		ret mat b			= `b'
		ret mat V			= `V'
	}
end

*==================================================*
*** version history ***
* version 1.2.3  04aug2020  bug with if-condition fixed
* version 1.2.2  02oct2017  bug fixed with option vce(cluster) and time-series operators
* version 1.2.1  28sep2017  bug fixed with option vce(cluster) if data set is not sorted by cluster
* version 1.2.0  13sep2017  option vce(cluster) added
* version 1.1.2  04jun2017  reported weighting matrix rescaled by number of groups
* version 1.1.1  31may2017  option scores replaces option influence for predict; difference-in-Hansen test added to estat overid; documentation for estat hausman added; improved display options; bug fixed for predict with Windmeijer-corrected scores
* version 1.1.0  11apr2017  predict computes influence functions now with additional Windmeijer correction term; undocumented postestimation command estat hausman added
* version 1.0.3  28mar2017  bug fixed with option noconstant and zero instruments; suboption copy added for option first()
* version 1.0.2  27feb2017  postestimation command estat serial added; bug fixed with second-stage standard-error correction after first-stage one-step GMM estimation
* version 1.0.1  15feb2017  option teffects added; covariance between first-stage and second-stage coefficients corrected
* version 1.0.0  12feb2017  available online at www.kripfganz.de
* version 0.2.0  04nov2015
* version 0.1.1  07feb2014
* version 0.1.0  14oct2013
* version 0.0.5  16may2013
* version 0.0.4  07may2012
* version 0.0.3  25apr2012
* version 0.0.2  20apr2012
* version 0.0.1  13apr2012