*! version 2.3.8  13aug2021
*! Sebastian Kripfganz, www.kripfganz.de

*==================================================*
****** GMM linear dynamic panel data estimation ******

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

program define xtdpdgmm, eclass prop(xt)
	version 13.0
	if replay() {
		if "`e(cmd)'" != "xtdpdgmm" {
			error 301
		}
		xtdpdgmm_parse_display `0'
		if `"`s(options)'"' != "" {
			di as err `"`s(options)' invalid"'
			exit 198
		}
		xtdpdgmm_display `0'
	}
	else {
		_xt, treq
		syntax varlist(num ts fv) [if] [in] [, *]
		xtdpdgmm_parse_display , `options'
		loc diopts			`"`s(diopts)'"'
		xtdpdgmm_init , `s(options)'
		loc mopt			"`s(mopt)'"
		xtdpdgmm_gmm `varlist' `if' `in', mopt(`mopt') `s(options)'

		eret loc marginsok	"XB default"
		eret loc predict	"xtdpdgmm_p"
		eret loc estat_cmd	"xtdpdgmm_estat"
		eret loc tvar		"`_dta[_TStvar]'"
		eret loc ivar		"`_dta[_TSpanel]'"
		eret loc cmdline 	`"xtdpdgmm `0'"'
		eret loc cmd		"xtdpdgmm"
		eret hidden loc mopt	"`mopt'"			// undocumented
		xtdpdgmm_display , `diopts'
	}
end

program define xtdpdgmm_gmm, eclass
	version 13.0
	syntax varlist(num ts fv) [if] [in] , MOPT(name) [	noCONStant			///
														TEffects			///
														NL(str)				///
														Collapse			///
														noREScale			///
														Model(string)		///
														ONEstep				///
														TWOstep				///
														IGMM				///
														Wmatrix(str)		///
														OVERid				///
														FROM(passthru)		///
														VCE(passthru)		///
														SMall				///
														AUXiliary			///
														noSERial			/// historical since version 2.0.0
														IID					/// historical since version 2.0.0
														*]					// GMMIV() IV() parsed separately
	loc fv				= ("`s(fvops)'" == "true")
	if `fv' {
		fvexpand `varlist'
		loc varlist			"`r(varlist)'"
	}
	marksample touse
	gettoken depvar indepvars : varlist
	if "`indepvars'" == "" & "`constant'" != "" {
		error 102
	}
	sum `touse', mean
	if r(sum) == 0 {
		error 2000
	}
	if `fv' {
		_fv_check_depvar `depvar'
		fvrevar `indepvars'
		loc dindepvars		"D.(`r(varlist)')"
	}
	else {
		loc dindepvars		"D.(`indepvars')"
	}
	tempvar dtouse
	qui gen byte `dtouse' = `touse'
	markout `dtouse' D.`depvar' `dindepvars'
	qui replace `dtouse' = 0 if !(L.`touse')
	mata: xtdpdgmm_init_touse(`mopt', "", "`touse'")			// marker variable
	mata: xtdpdgmm_init_touse(`mopt', "diff", "`dtouse'")		// marker variable for first-differenced model
	mata: xtdpdgmm_init_by(`mopt', "`_dta[_TSpanel]'")			// panel identifier
	mata: xtdpdgmm_init_time(`mopt', "`_dta[_TStvar]'")			// time identifier
	tsrevar `depvar'
	mata: xtdpdgmm_init_depvar(`mopt', "`r(varlist)'")			// dependent variable
	if "`constant'" != "" {
		mata: xtdpdgmm_init_cons(`mopt', "off")			// constant term
	}

	*--------------------------------------------------*
	*** time effects ***
	loc tdelta			= `_dta[_TSdelta]'
	sum `_dta[_TStvar]' if `touse', mean
	loc tmin			= r(min)
	loc tmax			= r(max)
	if "`teffects'" != "" {
		cap _rmcoll i(`= `tmin'+`tdelta'*("`constant'" == "")'(`tdelta')`= `tmax'')bn.`_dta[_TStvar]' if `touse', exp `constant'
		if _rc != 0 {
			error 451
		}
		loc teffects		"`r(varlist)'"
		loc indepvars		"`indepvars' `teffects'"
	}
	mata: xtdpdgmm_init_indepvars(`mopt', "`indepvars'")		// independent variables

	*--------------------------------------------------*
	*** type of weighting matrix ***
	xtdpdgmm_parse_wmatrix `wmatrix'
	loc wmatrix			"`s(wmatrix)'"
	loc ratio			= `s(ratio)'
	if "`wmatrix'" == "unadjusted" {
		mata: xtdpdgmm_init_wmatrix(`mopt', "", `ratio')
	}
	else {
		mata: xtdpdgmm_init_wmatrix(`mopt', "independent", `ratio')
	}

	*--------------------------------------------------*
	*** syntax parsing of options for instruments ***
	if "`overid'" != "" {
		mata: xtdpdgmm_init_overid(`mopt', "on")
	}
	if "`serial'" != "" & "`iid'" != "" {
		di as err "options noserial and iid may not be combined"
		exit 184
	}
	if "`serial'`iid'" != "" {
		if "`nl'" != "" {
			di as err "options `serial'`iid' and nl() may not be combined"
			exit 184
		}
		loc nl				"`serial'`iid', norescale"
	}
	if "`nl'" != "" {
		loc nlsyntax		= cond("`collapse'" == "", "Collapse", "noCollapse")
		loc nlsyntax		= cond("`rescale'" == "", "`nlsyntax' noREScale", "`nlsyntax' REScale")
		xtdpdgmm_parse_nl `nlsyntax' `nl'
		loc nl				"`s(nl)'"
		if "`nl'" == "iid" {
			loc options			`"gmmiv(L.`depvar', iid `s(collapse)') `options'"'
		}
		tempvar nltouse
		qui gen byte `nltouse' = `touse'
		if "`nl'" == "noserial" {
		    tempvar maxt
			qui by `_dta[_TSpanel]': egen `maxt' = max(`_dta[_TStvar]') if `touse'
			qui replace `nltouse' = 0 if F`s(lag)'.`_dta[_TStvar]' > `maxt'
			drop `maxt'
		}
		mata: xtdpdgmm_init_nl(`mopt', "`nl'")
		mata: xtdpdgmm_init_nl_touse(`mopt', "`nltouse'")
		if "`s(collapse)'" != "" {
			mata: xtdpdgmm_init_nl_collapse(`mopt', "yes")
		}
		if "`s(rescale)'" == "" {
			mata: xtdpdgmm_init_nl_rescale(`mopt', "no")
		}
		mata: xtdpdgmm_init_nl_weight(`mopt', `s(weight)')
	}
	tempvar obs
	qui by `_dta[_TSpanel]': gen int `obs' = _n
	sum `obs', mean
	loc maxlag			= r(max) - r(min)
	loc ivnum			= 0
	if "`model'" == "" {
		loc model			"level"
	}
	else {
		xtdpdgmm_parse_model level , todo(2) `model'
		loc model			"`s(model)'"
	}
	while `"`options'"' != "" {
		xtdpdgmm_parse_options , maxlag(`maxlag') `options' `collapse' `rescale' model(`model')
		loc options			`"`s(options)'"'
		loc ivnames			"`s(varlist)'"
		loc transform		"`s(transform)'"
		if "`s(fvops)'" == "true" {
			fvrevar `s(varlist)'
			xtdpdgmm_parse_options , maxlag(`maxlag') `s(ivtype)'iv(`r(varlist)', l(`s(lag1)' `s(lag2)') `s(transform)' m(`s(model)') `s(rescale)') `collapse' `rescale' model(`model')
		}
		if "`s(model)'" == "iid" {
			if "`s(ivtype)'" == "gmm" {
				loc ecgmmivvars		"`ecgmmivvars' `s(ivlist)'"
			}
			else {
				loc ecivvars		"`ecivvars' `s(ivlist)'"
			}
		}
		else if "`s(model)'" == "diff" {
			if "`s(ivtype)'" == "gmm" {
				loc dgmmivvars		"`dgmmivvars' `s(ivlist)'"
			}
			else {
				loc divvars			"`divvars' `s(ivlist)'"
			}
		}
		else if "`s(model)'" == "mdev" {
			if "`s(ivtype)'" == "gmm" {
				loc mdgmmivvars		"`mdgmmivvars' `s(ivlist)'"
			}
			else {
				loc mdivvars		"`mdivvars' `s(ivlist)'"
			}
		}
		else if "`s(model)'" == "fodev" {
			if "`s(transform)'" == "bodev" {
				loc bodvarlist0		""
				loc bodvarlist1		""
				forv v = 1 / `: word count `s(varnames)'' {
					loc bodvarname		: word `v' of `s(varnames)'
					loc bodvarlist0		"`bodvarlist0' B.`bodvarname'"
					tempvar bodvar
					tsrevar `: word `v' of `s(varnames)'', l
					qui gen `: type `r(varlist)'' `bodvar' = `bodvarname' if `touse'
					loc bodvarlist1		"`bodvarlist1' `bodvar'"
				}
				mata: xtdpdgmm_bodev("`bodvarlist1'", "`_dta[_TSpanel]'", "`touse'", ("`s(rescale)'" != ""))
				if "`s(ivtype)'" == "gmm" | "`s(collapse)'" != "" {
					xtdpdgmm_parse_options , maxlag(`maxlag') gmmiv(`bodvarlist1', lagrange(`s(lag1)' `s(lag2)') `s(collapse)' `s(rescale)' model(fodev)) model(`model')
				}
				else {
					xtdpdgmm_parse_options , maxlag(`maxlag') iv(`bodvarlist1', lagrange(`s(lag1)' `s(lag2)') `s(rescale)' model(fodev)) model(`model')
				}
				if "`s(ivtype)'" == "gmm" {
					loc bodgmmivvars	"`bodgmmivvars' `bodvarlist1'"
					forv v = 1 / `: word count `bodvarlist0'' {
						loc bodvarname		: word `v' of `bodvarlist0'
						loc fodgmmivnames	"`fodgmmivnames' `s(lagrange)'`bodvarname'"
					}
					loc fodgmmivvars	"`fodgmmivvars' `s(ivlist)'"
				}
				else {
					loc bodivvars		"`bodivvars' `bodvarlist1'"
					forv v = 1 / `: word count `bodvarlist0'' {
						loc bodvarname		: word `v' of `bodvarlist0'
						loc fodivnames	"`fodivnames' `s(lagrange)'`bodvarname'"
					}
					loc fodivvars		"`fodivvars' `s(ivlist)'"
				}
			}
			else if "`s(ivtype)'" == "gmm" {
				loc fodgmmivnames	"`fodgmmivnames' `s(ivlist)'"
				loc fodgmmivvars	"`fodgmmivvars' `s(ivlist)'"
			}
			else {
				loc fodivnames		"`fodivnames' `s(ivlist)'"
				loc fodivvars		"`fodivvars' `s(ivlist)'"
			}
		}
		else if "`s(ivtype)'" == "gmm" {
			loc gmmivvars		"`gmmivvars' `s(ivlist)'"
		}
		else {
			loc ivvars			"`ivvars' `s(ivlist)'"
		}
		loc ++ivnum
		loc ivnames_`ivnum'	"`ivnames'"
		loc ivmodel_`ivnum'	"`s(model)'"
		loc ivtransform_`ivnum'	"`transform'"
		loc ivtype_`ivnum'	"`s(ivtype)'"
		loc ivlag1_`ivnum'	"`s(lag1)'"
		loc ivlag2_`ivnum'	"`s(lag2)'"
		mata: xtdpdgmm_init_ivvars(`mopt', `ivnum', "`s(ivlist)'")
		mata: xtdpdgmm_init_ivvars_model(`mopt', `ivnum', "`ivmodel_`ivnum''")
		if "`s(rescale)'" == "" {
			mata: xtdpdgmm_init_ivvars_rescale(`mopt', `ivnum', "no")
		}
		if "`wmatrix'" == "separate" {
			mata: xtdpdgmm_init_ivvars_separate(`mopt', `ivnum', "yes")
		}
		mata: xtdpdgmm_init_ivvars_type(`mopt', `ivnum', "`s(ivtype)'")
	}
	if "`teffects'" != "" {
		loc ivvars "`ivvars' `teffects'"
		loc ++ivnum
		loc ivnames_`ivnum'	"`teffects'"
		loc ivmodel_`ivnum'	"level"
		loc ivlag1_`ivnum'	= 0
		loc ivlag2_`ivnum'	= 0
		mata: xtdpdgmm_init_ivvars(`mopt', `ivnum', "`teffects'")
	}
	loc gmmivvars		: list retok gmmivvars
	loc ivvars			: list retok ivvars
	foreach m in d ec md fod {
		loc `m'gmmivvars	: list retok `m'gmmivvars
		loc `m'ivvars		: list retok `m'ivvars
	}

	*--------------------------------------------------*
	*** type of variance-covariance matrices ***
	if `"`vce'"' != "" {
		xtdpdgmm_parse_vce , `vce' `twostep'`igmm' model(`model')
		loc vce				"`s(vce)'"
		loc vcem			"`s(model)'"
		loc clustvar		"`s(clustvar)'"
		if "`vce'" == "robust" {
			mata: xtdpdgmm_init_vcetype(`mopt', "robust")
		}
		if "`clustvar'" != "" {
			mata: xtdpdgmm_init_cluster(`mopt', "`clustvar'")
		}
	}
	else {
		loc vce				"conventional"
	}
	if "`vcem'" == "" {
		loc vcem			"`model'"
	}
	mata: xtdpdgmm_init_vce_model(`mopt', "`vcem'")
	if "`vce'" == "conventional" {
		if ("`twostep'`igmm'" != "" | "`nl'" != "") {
			di as txt "note: standard errors can be severely biased in finite samples; use vce(robust)"
		}
		else {
			di as txt "note: standard errors may not be valid; use vce(robust)"
		}
	}

	*--------------------------------------------------*
	*** initial estimates ***
	if "`constant'" == "" {
		loc regnames		"`indepvars' _cons"
	}
	else {
		loc regnames		"`indepvars'"
	}
	if `"`from'"' != "" {
		tempname b0
		_mkvec `b0', `from' col(`regnames') first err("from()")
		mata: xtdpdgmm_init_coefs(`mopt', st_matrix("`b0'"))
	}

	*--------------------------------------------------*
	*** estimation ***
	di _n as txt "Generalized method of moments estimation"
	mata: xtdpdgmm(`mopt')

	mata: st_numscalar("r(N)", xtdpdgmm_result_N(`mopt'))
	mata: st_numscalar("r(rank)", xtdpdgmm_result_rank(`mopt'))
	mata: st_numscalar("r(zrank_nl)", xtdpdgmm_result_zrank(`mopt', "nonlinear"))
	mata: st_matrix("r(b)", xtdpdgmm_result_coefs(`mopt'))
	mata: st_matrix("r(V)", xtdpdgmm_result_V(`mopt'))
	mata: st_matrix("r(V_modelbased)", xtdpdgmm_result_V_oim(`mopt'))
	mata: st_matrix("r(W)", xtdpdgmm_result_wmatrix(`mopt'))
	loc N				= r(N)
	loc rank			= r(rank)
	loc zrank_nl		= r(zrank_nl)
	tempname b V W
	mat `b'				= r(b)
	mat `V'				= r(V)
	mat `W'				= r(W)
	if !("`vce'" != "robust" & "`twostep'`igmm'" != "") {
		tempname V0
		mat `V0'			= r(V_modelbased)
	}
	if "`twostep'`igmm'" != "" & "`vce'" == "robust" {
		mata: st_matrix("r(b)", xtdpdgmm_result_coefs(`mopt', 1))
		mata: st_matrix("r(V_modelbased)", xtdpdgmm_result_V_oim(`mopt', 1))
		mata: st_matrix("r(W)", xtdpdgmm_result_wmatrix(`mopt', 1))
		tempname b1 W1 V01
		mat `b1'			= r(b)
		mat `V01'			= r(V_modelbased)
		mat `W1'			= r(W)
	}
	if "`vce'" == "robust" {
		mata: st_numscalar("r(N_clust)", xtdpdgmm_result_Nclust(`mopt'))
		loc N_clust			= r(N_clust)
		if "`small'" != "" {
			loc df				= `N_clust' - 1
			mat `V'				= `N_clust' / `df' * (`N' - 1) / (`N' - `rank') * `V'
		}
	}
	else if "`small'" != "" {
		loc df				= `N' - `rank'
		mat `V'				= `N' / `df' * `V'
	}
	if "`auxiliary'" != "" {
		loc k_aux			: word count `regnames'
		mat coleq `b'		= `regnames'
		mat roweq `V'		= `regnames'
		mat coleq `V'		= `regnames'
		if !("`vce'" != "robust" & "`twostep'`igmm'" != "") {
			mat roweq `V0'		= `regnames'
			mat coleq `V0'		= `regnames'
		}
		if "`twostep'`igmm'" != "" & "`vce'" == "robust" {
			mat coleq `b1'		= `regnames'
			mat roweq `V01'		= `regnames'
			mat coleq `V01'		= `regnames'
		}
		loc regnames		""
		forv e = 1/`k_aux' {
			loc regnames		"`regnames' _cons"
		}
	}
	mat coln `b'		= `regnames'
	mat rown `V'		= `regnames'
	mat coln `V'		= `regnames'
	if !("`vce'" != "robust" & "`twostep'`igmm'" != "") {
		mat rown `V0'		= `regnames'
		mat coln `V0'		= `regnames'
	}
	if "`twostep'`igmm'" != "" & "`vce'" == "robust" {
		mat coln `b1'		= `regnames'
		mat rown `V01'		= `regnames'
		mat coln `V01'		= `regnames'
	}
	if "`constant'" == "" {
		loc ivvars			"`ivvars' _cons"
		loc ivvars			: list retok ivvars
	}
	if "`bodgmmivvars'" != "" {
		loc fodgmmivvars	: list retok fodgmmivnames
	}
	if "`bodivvars'" != "" {
		loc fodivvars		: list retok fodivnames
	}
	loc ivnames			""
	sum `obs' if `touse', mean
	loc maxlag			= r(max) - r(min) + 1
	forv j = 1 / `ivnum' {
		tempname nocol
		mata: st_matrix("`nocol'", xtdpdgmm_result_ivvars_nocol(`mopt', `j'))
		loc k				= 1
		loc l				= `ivlag1_`j''
		loc lags			= `ivlag2_`j'' - `ivlag1_`j'' + 1
		forv i = 1 / `= colsof(`nocol')' {
			loc n				= el(`nocol', 1, `i')
			if "`ivtype_`j''" == "gmm" {
				loc t				= `tmin' + `tdelta' * (cond(mod(`n', `maxlag'), mod(`n', `maxlag'), `maxlag') - 1)
				while `n' > ((`k' - 1) * `lags' + `l' - `ivlag1_`j'' + 1) * `maxlag' {
					if `l' < `ivlag2_`j'' {
						loc ++l
					}
					else {
						loc l				= `ivlag1_`j''
						loc ++k
					}
				}
				loc lag				= cond(`l' < 0, "F`= abs(`l')'", "L`l'")
				loc ivname			: word `k' of `ivnames_`j''
				if "`ivtransform_`j''" == "difference" {
					loc iveqnames		`"`iveqnames' "`j' `ivmodel_`j'' `t' `lag'.D""'
				}
				else if "`ivtransform_`j''" == "bodev" {
					loc iveqnames		`"`iveqnames' "`j' `ivmodel_`j'' `t' `lag'.B""'
				}
				else {
					loc iveqnames		`"`iveqnames' "`j' `ivmodel_`j'' `t' `lag'""'
				}
			}
			else {
				while `n' > (`k' - 1) * `lags' + `l' - `ivlag1_`j'' + 1 {
					if `l' < `ivlag2_`j'' {
						loc ++l
					}
					else {
						loc l				= `ivlag1_`j''
						loc ++k
					}
				}
				loc lag				= cond(`l' < 0, "F`= abs(`l')'", "L`l'")
				loc ivname			: word `k' of `ivnames_`j''
				if "`ivtransform_`j''" == "difference" {
					loc iveqnames		`"`iveqnames' "`j' `ivmodel_`j'' . `lag'.D""'
				}
				else if "`ivtransform_`j''" == "bodev" {
					loc iveqnames		`"`iveqnames' "`j' `ivmodel_`j'' . `lag'.B""'
				}
				else {
					loc iveqnames		`"`iveqnames' "`j' `ivmodel_`j'' . `lag'""'
				}
			}
			loc ivnames			"`ivnames' `ivname'"
		}
	}
	if "`constant'" == "" {
		loc iveqnames		`"`iveqnames' "`=`ivnum'+1' level . L0""'
		loc ivnames			"`ivnames' _cons"
	}
	if `zrank_nl' == 1 {
		loc iveqnames		`"`iveqnames' "nl `nl' . L0""'
		loc ivnames			"`ivnames' _cons"
	}
	else if `zrank_nl' > 1 {
		forv i = 1 / `zrank_nl' {
			loc t				= `tmin' + `tdelta' * `i'
			loc iveqnames		`"`iveqnames' "nl `nl' `t' L0""'
			loc ivnames			"`ivnames' _cons"
		}
	}
	mat rown `W'		= `ivnames'
	mat coln `W'		= `ivnames'
	mat rowe `W'		= `iveqnames'
	mat cole `W'		= `iveqnames'

	*--------------------------------------------------*
	*** current estimation results ***
	if "`small'" != "" {
		loc small			"dof(`df')"
	}
	if `fv' {
		loc fvopt			"buildfv"
	}
	eret post `b' `V', dep(`depvar') o(`N') `small' e(`touse') `fvopt' findomitted
	mata: st_numscalar("e(N_g)", xtdpdgmm_result_Ng(`mopt'))
	if "`vce'" == "robust" {
		eret sca N_clust	= `N_clust'
	}
	mata: st_numscalar("e(g_min)", xtdpdgmm_result_Tmin(`mopt'))
	eret sca g_avg		= e(N) / e(N_g)
	mata: st_numscalar("e(g_max)", xtdpdgmm_result_Tmax(`mopt'))
	mata: st_numscalar("e(f)", xtdpdgmm_result_value(`mopt'))
	mata: st_numscalar("e(chi2_J)", xtdpdgmm_result_overid(`mopt', 1))
	mata: st_numscalar("e(chi2_J_u)", xtdpdgmm_result_overid(`mopt', 2))
	eret sca rank		= `rank'
	mata: st_numscalar("e(zrank)", xtdpdgmm_result_zrank(`mopt', "linear"))
	eret sca zrank_nl	= `zrank_nl'
	if "`vce'" == "conventional" & "`twostep'`igmm'" == "" & "`nl'" == "" {
		mata: st_numscalar("e(sigma2e)", xtdpdgmm_result_sigma2(`mopt'))
	}
	mata: st_numscalar("e(steps)", xtdpdgmm_result_steps(`mopt'))
	mata: st_numscalar("e(ic)", xtdpdgmm_result_iterations(`mopt'))
	mata: st_numscalar("e(converged)", xtdpdgmm_result_converged(`mopt'))
	if "`auxiliary'" == "" {
		if "`vce'" == "robust" {
			if "`twostep'`igmm'" != "" | "`nl'" != "" {
				eret loc vcetype	"WC-Robust"
			}
			else {
				eret loc vcetype	"Robust"
			}
		}
		else if "`twostep'`igmm'" == "" & "`nl'" != "" {
			eret loc vcetype	"Robust"
		}
		if "`clustvar'" == "" {
			eret loc vce		"`vce'"
		}
		else {
			eret loc vce		"cluster"
			eret loc clustvar	"`clustvar'"
		}
	}
	eret loc estimator	"`onestep'`twostep'`igmm'"
	eret loc wmatrix	"`wmatrix', ratio(`ratio')"
	eret loc teffects	"`teffects'"
	mata: st_matrix("e(ilog)", xtdpdgmm_result_iterationlog(`mopt'))
	eret mat W			= `W'
	if !("`vce'" != "robust" & "`twostep'`igmm'" != "") {
		eret mat V_modelbased	= `V0'
	}

	*--------------------------------------------------*
	*** hidden estimation results ***					// undocumented
	if "`auxiliary'" != "" {
		eret hidden sca k_aux	= `k_aux'
	}
	if "`overid'" != "" {
		tempname J
		mata: st_matrix("`J'", xtdpdgmm_result_overid(`mopt', 0))
		eret hidden mat J	= `J'
	}

	*--------------------------------------------------*
	*** historical estimation results ***				// undocumented since version 2.0.0
	eret historical sca twostep		= (e(steps) == 2)
	eret historical loc nonlinear	"`nl'"
	foreach m in fod md ec d {
		eret historical loc `m'gmmivvars	"``m'gmmivvars'"
		eret historical loc `m'ivvars		"``m'ivvars'"
	}
	eret historical loc gmmivvars	"`gmmivvars'"
	eret historical loc ivvars		"`ivvars'"
	if "`twostep'" != "" & "`vce'" == "robust" {
		eret historical mat W_onestep	= `W1'
		eret historical mat V_onestep	= `V01'
		eret historical mat b_onestep	= `b1'
	}
end

*==================================================*
**** display of estimation results ****
program define xtdpdgmm_display
	version 13.0
	syntax [, noHEader noTABle noFOoter *]

	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)
		di _n as txt "Moment conditions:" _col(24) "linear =" _col(33) as res %7.0f e(zrank) _col(46) as txt "Obs per group:" _col(64) "min =" _col(70) as res %9.0g e(g_min)
		di _col(21) as txt "nonlinear =" _col(33) as res %7.0f e(zrank_nl) _col(64) as txt "avg =" _col(70) as res %9.0g e(g_avg)
		di _col(25) as txt "total =" _col(33) as res %7.0f e(zrank) + e(zrank_nl) _col(64) as txt "max =" _col(70) as res %9.0g e(g_max)
	}
	if "`table'" == "" {
		di ""
		_coef_table, `options'
	}
	if "`footer'" == "" {
		di as txt "Instruments corresponding to the linear moment conditions:"
		predict , iv nogenerate
	}
end

*==================================================*
**** syntax parsing of additional display options ****
program define xtdpdgmm_parse_display, sclass
	version 13.0
	sret clear
	syntax , [noHEader noTABle noFOoter noFOOTNote PLus *]		// noFOOTNote historical since version 2.3.0
	_get_diopts diopts options, `options'
	if "`footnote'" != "" {
	    loc footer			"nofooter"
	}

	sret loc diopts		`"`header' `table' `footer' `plus' `diopts'"'
	sret loc options	`"`options'"'
end

*==================================================*
**** syntax parsing of the optimization options ****
program define xtdpdgmm_init, sclass
	version 13.0
	sret clear
	loc maxiter			= c(maxiter)
	syntax [,	NL(passthru)						///
				ONEstep								///
				TWOstep								///
				IGMM								///
				noANalytic							///
				METHOD(string)						///
				ITERate(integer `maxiter')			///
				noLOg								///
				NODOTs								///
				DOTs								///
				SHOWSTEP							///
				SHOWTOLerance						///
				TOLerance(real 1e-6)				///
				LTOLerance(real 1e-7)				///
				NRTOLerance(real 1e-5)				///
				NONRTOLerance						///
				IGMMITerate(integer `maxiter')		///
				IGMMEPS(real 1e-6)					///
				IGMMWEPS(real 1e-6)					///
				*]

	tempname isinit
	sca `isinit'		= 1
	loc j				= 1
	while `isinit' {
		mata: st_numscalar("`isinit'", findexternal("xtdpdgmm_opt_`j'") != J(1, 1, NULL))
		if `isinit' {
			loc ++j
		}
		else {
			loc mopt			"xtdpdgmm_opt_`j'"
			mata: `mopt' = xtdpdgmm_init()
		}
	}
	if `"`method'"' == "" {
		loc method			"q1"
	}
	else {
		loc method			: subinstr loc method "quadratic" "q", all
		loc methods			"q0 q1 q1debug"
		if `: word count `method'' > 1 | !`: list method in methods' {
			di as err "option method() incorrectly specified -- invalid evaluator type"
			exit 198
		}
	}
	if "`onestep'" != "" & "`twostep'" != "" {
		di as err "options onestep and twostep may not be combined"
		exit 184
	}
	if "`onestep'`twostep'" != "" & "`igmm'" != "" {
		di as err "options `onestep'`twostep' and igmm may not be combined"
		exit 184
	}
	if "`onestep'`twostep'`igmm'" == "" {
		if "`nl'" == "" {
			loc onestep			"onestep"
		}
		else {
			loc twostep			"twostep"
		}
	}
	if "`dots'" != "" & "`nodots'" != "" {
		di as err "options dots and nodots may not be combined"
		exit 184
	}
	if `iterate' < 0 {
		di as err "option iterate() incorrectly specified -- outside of allowed range"
		exit 125
	}
	if `igmmiterate' < 0 {
		di as err "option igmmiterate() incorrectly specified -- outside of allowed range"
		exit 125
	}
	if "`igmm'" == "" {
		mata: xtdpdgmm_init_steps(`mopt', 1 + ("`twostep'" != ""))
	}
	else {
		if "`nodots'" == "" {
			loc dots			"dots"
		}
		mata: xtdpdgmm_init_steps(`mopt', `igmmiterate')
		mata: xtdpdgmm_init_igmm_eps(`mopt', `igmmeps')
		mata: xtdpdgmm_init_igmm_weps(`mopt', `igmmweps')
	}
	mata: xtdpdgmm_init_evaluatortype(`mopt', "`method'")
	if "`analytic'" == "" & "`nl'" == "" {
		mata: xtdpdgmm_init_technique(`mopt', "")
	}
	mata: xtdpdgmm_init_conv_maxiter(`mopt', `iterate')
	mata: xtdpdgmm_init_conv_ptol(`mopt', `tolerance')
	mata: xtdpdgmm_init_conv_vtol(`mopt', `ltolerance')
	if "`nonrtolerance'" == "" {
		mata: xtdpdgmm_init_conv_nrtol(`mopt', `nrtolerance')
	}
	else {
		mata: xtdpdgmm_init_conv_ignorenrtol(`mopt', "on")
	}
	if "`log'" != "" {
		mata: xtdpdgmm_init_tracelevel(`mopt', "none")
	}
	if "`dots'" != "" {
		mata: xtdpdgmm_init_igmm_dots(`mopt', "on")
	}
	if "`showstep'" != "" {
		mata: xtdpdgmm_init_trace_step(`mopt', "on")
	}
	if "`showtolerance'" != "" {
		mata: xtdpdgmm_init_trace_tol(`mopt', "on")
	}

	sret loc mopt		"`mopt'"
	sret loc method		`"`method'"'
	sret loc options	`"`nl' `onestep'`twostep'`igmm' `options'"'
end

*==================================================*
**** syntax parsing for nonlinear moment conditions ****
program define xtdpdgmm_parse_nl, sclass
	version 13.0
	gettoken csyntax 0 : 0
	gettoken rsyntax 0 : 0
	syntax anything [, `csyntax' `rsyntax' Lag(numlist int max=1 >0) Weight(real 1)]

	loc collapse		= cond(("`csyntax'" == "noCollapse" & "`collapse'" == "") | ("`csyntax'" == "Collapse" & "`collapse'" != ""), "collapse", "")
	loc rescale			= cond(("`rsyntax'" == "noREScale" & "`rescale'" == "") | ("`rsyntax'" == "REScale" & "`rescale'" != ""), "rescale", "")
	loc length			: length loc anything
	if `"`anything'"' == substr("noserial", 1, max(5, `length')) {
		loc anything		"noserial"
	}
	else if `"`anything'"' != "iid" {
		di as err "option nl() incorrectly specified"
		exit 198
	}
	else if "`lag'" != "" {
		di as err "option nl() incorrectly specified -- option lag() not allowed"
		exit 198
	}
	if "`lag'" == "" {
		loc lag				= 1
	}
	if `weight' < 0 {
		di as err "weight() invalid -- outside of allowed range"
		exit 198
	}

	sret loc weight		= `weight'
	sret loc lag		= `lag'
	sret loc rescale	"`rescale'"
	sret loc collapse	"`collapse'"
	sret loc nl			"`anything'"
end

*==================================================*
**** syntax parsing of options for instruments ****
program define xtdpdgmm_parse_options, sclass
	version 13.0
	sret clear
	syntax , MAXLAG(integer) [GMMiv(string) IV(string) Collapse noREScale Model(string) *]

	*--------------------------------------------------*
	*** GMM instruments ***
	loc collapse		= cond("`collapse'" == "", "Collapse", "noCollapse")
	loc rescale			= cond("`rescale'" == "", "noREScale", "REScale")
	if `"`gmmiv'"' != "" {
		gettoken gmmivvars gmmiv : gmmiv, p(",")
		if "`gmmiv'" == "" {
			loc gmmiv			","
		}
		xtdpdgmm_parse_gmmiv `collapse' `rescale' `model' `gmmivvars' `gmmiv' maxlag(`maxlag')
	}

	*--------------------------------------------------*
	*** standard instruments ***
	if "`gmmivvars'" == "" {
		if `"`iv'"' != "" {
			gettoken ivvars iv : iv, p(",")
			if "`iv'" == "" {
				loc iv				","
			}
			xtdpdgmm_parse_iv `rescale' `model' `ivvars' `iv' maxlag(`maxlag')
		}
		else if `"`options'"' == "" {
			error 198
		}
		else {
			di as err `"`options' invalid"'
			exit 198
		}
	}
	else if `"`iv'"' != "" {
		loc options			`"iv(`iv') `options'"'
	}

	sret loc options		`"`options'"'
end

*==================================================*
**** syntax parsing for GMM instruments ****
program define xtdpdgmm_parse_gmmiv, sclass
	version 13.0
	gettoken csyntax 0 : 0
	gettoken rsyntax 0 : 0
	gettoken mdefault 0 : 0
	syntax varlist(num ts fv), MAXLag(integer) [Lagrange(numlist max=2 int miss) Difference BODev `csyntax' `rsyntax' IID Model(string)		///
												EC]																							// historical since version 2.0.0

	if "`s(fvops)'" == "true" {
		fvexpand `varlist'
		loc varlist			"`r(varlist)'"
	}
	loc collapse		= cond(("`csyntax'" == "noCollapse" & "`collapse'" == "") | ("`csyntax'" == "Collapse" & "`collapse'" != ""), "collapse", "")
	loc rescale			= cond(("`rsyntax'" == "noREScale" & "`rescale'" == "") | ("`rsyntax'" == "REScale" & "`rescale'" != ""), "rescale", "")
	xtdpdgmm_parse_model `mdefault' , todo(`= ("`bodev'" != "")') `model' `iid' `ec'
	loc model			"`s(model)'"
	if "`bodev'" != "" & "`difference'" != "" {
		di as err "options difference and bodev may not be combined"
		exit 184
	}
	if "`iid'`ec'" == "" {
		if "`lagrange'" == "" {
			loc lagrange		= ("`model'" == substr("difference", 1, max(1, `: length loc model')))
		}
		xtdpdgmm_parse_lagrange , maxlag(`maxlag') lagrange(`lagrange') `difference' `bodev' model(`model') gmmiv
		foreach var of loc varlist {
			if "`difference'" == "" {
				loc gmmivvars		"`gmmivvars' `s(lagrange)'`var'"
				loc varnames		"`varnames' `var'"
			}
			else {
				loc gmmivvars		"`gmmivvars' `s(lagrange)'D.`var'"
				loc varnames		"`varnames' D.`var'"
			}
		}
	}
	else if "`lagrange'" != "" {
			di as err "options `iid'`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'"
			}
		}
		sret loc lag1		= 0
		sret loc lag2		= 0
		loc varnames		"`varlist'"
	}

	sret loc transform	"`difference'`bodev'"
	sret loc ivlist		"`gmmivvars'"
	sret loc rescale	"`rescale'"
	sret loc collapse	"`collapse'"
	if "`collapse'" == "" {
		sret loc ivtype		"gmm"
	}
	sret loc varnames	"`varnames'"
	sret loc varlist	"`varlist'"
end

*==================================================*
**** syntax parsing for standard instruments ****
program define xtdpdgmm_parse_iv, sclass
	version 13.0
	gettoken rsyntax 0 : 0
	gettoken mdefault 0 : 0
	syntax varlist(num ts fv), MAXLag(integer) [Lagrange(numlist max=2 int) Difference BODev `rsyntax' Model(string)]

	if "`s(fvops)'" == "true" {
		fvexpand `varlist'
		loc varlist			"`r(varlist)'"
	}
	loc rescale			= cond(("`rsyntax'" == "noREScale" & "`rescale'" == "") | ("`rsyntax'" == "REScale" & "`rescale'" != ""), "rescale", "")
	xtdpdgmm_parse_model `mdefault' , todo(`= ("`bodev'" != "")') `model'
	loc model			"`s(model)'"
	if "`bodev'" != "" & "`difference'" != "" {
		di as err "options difference and bodev may not be combined"
		exit 184
	}
	if "`lagrange'" == "" {
		loc lagrange		= 0
	}
	xtdpdgmm_parse_lagrange , maxlag(`maxlag') lagrange(`lagrange') `difference' `bodev' model(`model')
	foreach var of loc varlist {
		if "`difference'" == "" {
			loc ivvars			"`ivvars' `s(lagrange)'`var'"
			loc varnames		"`varnames' `var'"
		}
		else {
			loc ivvars			"`ivvars' `s(lagrange)'D.`var'"
			loc varnames		"`varnames' D.`var'"
		}
	}

	sret loc transform	"`difference'`bodev'"
	sret loc ivlist		"`ivvars'"
	sret loc rescale	"`rescale'"
	sret loc varnames	"`varnames'"
	sret loc varlist	"`varlist'"
end

*==================================================*
**** syntax parsing for the model equations ****
program define xtdpdgmm_parse_model, sclass
	version 13.0
	syntax anything , TODO(integer) [	Level Difference MDev FODev IID		///
										EC]									// historical since version 2.0.0

	if `: word count `level' `difference' `mdev' `fodev'' > 1 | (`todo' == 2 & "`iid'`ec'" != "") {
		di as err "option model() incorrectly specified"
		exit 198
	}
	if "`level'`difference'`mdev'`fodev'`iid'`ec'" == "" {
		if "`anything'" == "diff" {
			loc difference			"difference"
		}
		else {
			loc `anything'			"`anything'"
		}
	}
	if `todo' == 1 & "`fodev'" == "" {
		di as err "options bodev and model(`level'`difference'`mdev') may not be combined"
		exit 184
	}
	if "`difference'" != "" {
		loc difference		"diff"
	}
	if "`iid'`ec'" != "" {
		if "`level'`mdev'`fodev'" != "" {
			di as err "options `iid'`ec' and model(`level'`mdev'`fodev') may not be combined"
			exit 184
		}
		loc model			"iid"
	}
	else {
		loc model			"`level'`difference'`mdev'`fodev'"
	}

	sret loc model		"`model'"
end

*==================================================*
**** syntax parsing for the instrument lag range ****
program define xtdpdgmm_parse_lagrange, sclass
	version 13.0
	syntax , MAXLag(integer) [Lagrange(numlist max=2 int miss) Difference BODev Model(string) GMMiv]

	loc minlag			= - `maxlag'
	if "`difference'" != "" | "`bodev'" != "" {
		loc --maxlag
	}
	if "`model'" == "fodev" {
		loc --maxlag
	}
	if "`model'" == "diff" {
		loc ++minlag
	}
	gettoken lag1 lag2 : lagrange
	if "`gmmiv'" != "" {
		if `lag1' == . {
			loc lag1			= `minlag'
		}
		if "`lag2'" == "" {
			loc lag2			= `maxlag'
		}
		else if `lag2' == . {
			loc lag2			= `maxlag'
		}
	}
	else if "`lag2'" == "" {
		loc lag2			= `lag1'
	}
	if `lag1' < `minlag' | `lag2' > `maxlag' {
		di as err "lagrange() invalid -- invalid numlist has elements outside of allowed range"
		exit 125
	}
	else if `lag1' > `lag2' {
		di as err "lagrange() invalid -- invalid numlist has elements out of order"
		exit 124
	}
	loc lag2				: list retok lag2
	if `lag1' == `lag2' {
		if `lag1' == 0 {
			loc lagrange		""
		}
		else if `lag1' < 0 {
			loc lagrange		"L(`lag1')."
		}
		else if `lag1' > 0 {
			loc lagrange		"L`lag1'."
		}
	}
	else {
		loc lagrange		"L(`lag1'/`lag2')."
	}

	sret loc lag1		= `lag1'
	sret loc lag2		= `lag2'
	sret loc lagrange	"`lagrange'"
end

*==================================================*
**** syntax parsing for weighting matrix ****
program define xtdpdgmm_parse_wmatrix, sclass
	version 13.0
	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 {
		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 xtdpdgmm_parse_vce, sclass
	version 13.0
	sret clear
	syntax , [VCE(passthru) TWOstep IGMM Model(string)]

	cap _vce_parse , opt(CONVENTIONAL Robust) argopt(CLuster) : , `vce'
	if "`r(vce)'" == "" {
		cap _vce_parse , : , `vce'
	}
	if _rc != 0 {
		cap _vce_parse , argopt(CONVENTIONAL 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
		if "`vceargs'" != "" {
			if "`r(vce)'" == "robust" {
				di as err "option vce(robust) incorrectly specified"
				exit 198
			}
			xtdpdgmm_parse_vce_model `model' , `vceargs'
			loc model			"`s(model)'"
		}
	}
	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]'" {
			xtdpdgmm_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 model		"`model'"
end

*==================================================*
**** syntax parsing for standard instruments ****
program define xtdpdgmm_parse_vce_model, sclass
	version 13.0
	syntax anything , [	Model(string)						///
						Difference]							// historical since version 2.0.3

	if "`difference'" == "" {
		xtdpdgmm_parse_model `anything' , todo(2) `model'
	}
	else {
		if "`model'" != "" {
			di as err "options difference and model() may not be combined"
			exit 198
		}
		sret loc model		"diff"
	}
end

*==================================================*
**** check if panel identifier is nested within cluster identifier ****
// (inspired by _xtreg_chk_cl2.ado)
program define xtdpdgmm_cluster, rclass sort
	version 13.0
	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

*==================================================*
*** version history ***
* version 2.3.8  13aug2021  bug with option nl() fixed for postestimation commands
* version 2.3.7  30jul2021  bug with factor variables fixed in estat hausman
* version 2.3.6  11jul2021  bug fixed that was introduced in version 2.3.5
* version 2.3.5  07jul2021  bug fixed with labels of GMM-type instruments in dynamic models
* version 2.3.4  11may2021  bug fixed with labels of GMM-type instruments in static models
* version 2.3.3  16mar2021  bug with estat mmsc fixed that was introduced in version 2.3.2
* version 2.3.2  25jan2021  bug fixed with option nl(noserial); option n() added for estat mmsc and default changed if vce(cluster) specified
* version 2.3.1  08oct2020  bug fixed with lagged interaction terms as instruments
* version 2.3.0  26aug2020  suboption lag() added for option nl(noserial); bug with time-series operators in dependent variable fixed in postestimation command predict; option nofootnote replaced by nofooter
* version 2.2.7  21jul2020  bug with interaction terms fixed in estat serial; bug with option noomitted fixed; improved help files
* version 2.2.6  19apr2020  support for postestimation command margins added
* version 2.2.5  17apr2020  bug with postestimation commands fixed if _cons is omitted
* version 2.2.4  15apr2020  Stata 14.2 required for option overid with multiple sets of instruments for the same model transformation
* version 2.2.3  17oct2019  bug with option scores of predict fixed
* version 2.2.2  03sep2019  bug with default of suboption lagrange() fixed; bug with labels of generated instruments fixed
* version 2.2.1  20aug2019  bug with option difference of estat overid fixed
* version 2.2.0  07aug2019  factor variables supported; bug with option overid fixed
* version 2.1.1  20jul2019  option small added; bug with if-condition fixed
* version 2.1.0  19jun2019  option overid added together with option difference for postestimation command estat overid
* version 2.0.4  23apr2019  bug with option vce(cluster) fixed that was introduced in version 2.0.0
* version 2.0.3  20mar2019  suboptions model(mdev) and model(fodev) added for option vce(); option norescale added; option noanalytic replaces option analytic; option model() added
* version 2.0.2  17mar2019  bug with option from() fixed that was introduced in version 2.0.0
* version 2.0.1  11mar2019  bug with time series operators in indepvars fixed that was introduced in version 2.0.0 under Stata versions prior to 08mar2018
* version 2.0.0  10mar2019  Stata 13 required; option nl() replaces options noserial and iid; option igmm and related options added; estat overid reports two versions of the test; collinearity check on instruments performed; labels attached to generated instruments
* version 1.1.3  24sep2018  bug fixed if some groups have no first-differenced observations
* version 1.1.2  15sep2018  bug fixed with option lagrange()
* version 1.1.1  07sep2018  bug fixed with option lagrange(); option iv added to predict
* version 1.1.0  29aug2018  options iid, collapse, analytic, and vce(cluster) added; suboptions model(mdev), model(fodev), and bodev added; suboption lagrange() improved
* version 1.0.2  04may2018  option teffects added; postestimation command estat mmsc added
* version 1.0.1  21aug2017  improved help files
* version 1.0.0  31may2017  available online at www.kripfganz.de
* version 0.1.0  25may2017
* version 0.0.3  21may2017
* version 0.0.2  03may2017
* version 0.0.1  03apr2017