*! _xtcspqardl_ecm v1.0.0 — ECM reparameterization for CS-PQARDL
*! Error Correction Model: CSA enter the ECT only (not SR dynamics)
*! Called internally by xtcspqardl.ado
*! Author: Dr Merwan Roudane (merwanroudane920@gmail.com)
*! Date: February 2026
*!
*! ECM form:
*!   Δy_it = φ_i(τ)[y_{i,t-1} - θ_i(τ)'x_{i,t-1} - δ_i(τ)'z̄_{t-1}]
*!         + Σ c_{ij}(τ) Δy_{i,t-j} + Σ d_{im}(τ) Δx_{k,i,t-m} + e_it(τ)
*!
*! CSA in ECT absorbs unobserved common factors; Δz̄ dropped from SR

capture program drop _xtcspqardl_ecm
program define _xtcspqardl_ecm, rclass
	version 15.1
	syntax , DEPVAR(string) INDEPVARS(string) ///
		P(integer) QLAGS(string) ///
		TAU(numlist >0 <1 sort) IVAR(string) TVAR(string) ///
		TOUSE(string) CSAVARS(string) [NOCONStant]

	* Parse
	local k : word count `indepvars'
	local ntau : word count `tau'
	local n_csa : word count `csavars'

	* Parse q lags per variable
	local nqlags : word count `qlags'
	if `nqlags' == 1 {
		forvalues j = 1/`k' {
			local q`j' = `qlags'
		}
	}
	else if `nqlags' == `k' {
		forvalues j = 1/`k' {
			local q`j' : word `j' of `qlags'
		}
	}
	else {
		di as err "qlags() must have 1 or k elements"
		exit 198
	}

	* Get panel IDs
	qui levelsof `ivar' if `touse', local(ids)
	local npanels : word count `ids'

	* ================================================================
	* CONSTRUCT ECM VARIABLES
	* ================================================================

	* --- Dependent variable: Δy_it ---
	tempvar dy_plain
	qui gen double `dy_plain' = D.`depvar' if `touse'

	* --- ECT level components ---
	* y_{i,t-1}
	tempvar ly_ecm
	qui gen double `ly_ecm' = L.`depvar' if `touse'

	* x_{k,i,t-1} for each indepvar — levels entering ECT
	local lx_ecm ""
	forvalues j = 1/`k' {
		local xvar : word `j' of `indepvars'
		tempvar lx_ecm`j'
		qui gen double `lx_ecm`j'' = L.`xvar' if `touse'
		local lx_ecm "`lx_ecm' `lx_ecm`j''"
	}

	* z̄_{t-1} — CSA level entering ECT (lag 0 + lagged CSA already include lags)
	* CSA vars passed are: z̄_t, z̄_{t-1}, ..., z̄_{t-pT}
	* For ECT, we want the lagged levels: L.z̄_t, L.z̄_{t-1}, ..., L.z̄_{t-pT}
	* But the CSA vars already contain contemporaneous and lags
	* In ECM form, CSA enter at level in the ECT
	local csa_ecm ""
	local ci = 0
	foreach csav of local csavars {
		local ++ci
		tempvar csa_ecm`ci'
		qui gen double `csa_ecm`ci'' = `csav' if `touse'
		local csa_ecm "`csa_ecm' `csa_ecm`ci''"
	}

	* --- Short-run dynamics (CSA excluded) ---

	* SR: ΔΔy_{i,t-j} for j = 1,...,p-1
	local sr_dy ""
	local ncoefs_ar = `p' - 1
	if `p' > 1 {
		forvalues lag = 1/`= `p' - 1' {
			tempvar ddy_lag`lag'
			qui gen double `ddy_lag`lag'' = L`lag'.`dy_plain' if `touse'
			local sr_dy "`sr_dy' `ddy_lag`lag''"
		}
	}

	* SR: x_{k,i,t-m} for m = 0,...,q_k-1 (user passes `dx` manually)
	local sr_dx ""
	local ncoefs_sr = 0
	forvalues j = 1/`k' {
		local xvar : word `j' of `indepvars'

		* Contemporaneous variable (user should pass differenced form e.g. dx)
		tempvar sr_`j'_0
		qui gen double `sr_`j'_0' = `xvar' if `touse'
		local sr_dx "`sr_dx' `sr_`j'_0'"
		local ++ncoefs_sr

		* Additional lags
		if `q`j'' > 1 {
			forvalues m = 1/`= `q`j'' - 1' {
				tempvar sr_`j'_`m'
				qui gen double `sr_`j'_`m'' = L`m'.`xvar' if `touse'
				local sr_dx "`sr_dx' `sr_`j'_`m''"
				local ++ncoefs_sr
			}
		}
	}

	* ECT regressors: y_{t-1}, x_{k,t-1}, CSA
	local ect_regs "`ly_ecm' `lx_ecm' `csa_ecm'"
	local ncoefs_ect = 1 + `k' + `n_csa'

	* Full regressor list for qreg: ECT + AR SR + DL SR
	local fullreg "`ect_regs' `sr_dy' `sr_dx'"
	local ncoefs_total = `ncoefs_ect' + `ncoefs_ar' + `ncoefs_sr'

	* ================================================================
	* Storage matrices
	* ================================================================
	local beta_dim = `k' * `ntau'

	tempname phi_all beta_all theta_all halflife_all csa_all
	tempname ar_all sr_all

	* φ_i(τ) = coefficient on y_{i,t-1} in ECM — speed of adjustment
	matrix `phi_all' = J(`npanels', `ntau', .)

	* β_i(τ) = -coef(x_{k,t-1})/coef(y_{t-1}) — long-run coeff
	matrix `beta_all' = J(`npanels', max(`beta_dim',1), .)

	* θ_i(τ) = raw LR coefficients (x in ECT)
	matrix `theta_all' = J(`npanels', max(`beta_dim',1), .)

	* Half-life
	matrix `halflife_all' = J(`npanels', `ntau', .)

	* AR dynamics
	if `ncoefs_ar' > 0 {
		matrix `ar_all' = J(`npanels', `ncoefs_ar' * `ntau', .)
	}
	else {
		matrix `ar_all' = J(`npanels', `ntau', 0)
	}

	* SR dynamics
	if `ncoefs_sr' > 0 {
		matrix `sr_all' = J(`npanels', `ncoefs_sr' * `ntau', .)
	}
	else {
		matrix `sr_all' = J(1, 1, .)
	}

	* CSA nuisance
	matrix `csa_all' = J(`npanels', `n_csa' * `ntau', .)

	* ================================================================
	* Loop over panels
	* ================================================================
	local pi = 0
	local success_count = 0

	foreach i of local ids {
		local ++pi

		qui count if `touse' & `ivar' == `i'
		local ni = r(N)

		if `ni' < `ncoefs_total' + 5 {
			continue
		}

		local any_tau_ok = 0
		local ti = 0

		foreach tauval of local tau {
			local ++ti
			local tq = round(`tauval' * 100)

			* Run quantile regression: Δy on ECT + SR (no CSA in SR)
			capture qui qreg `dy_plain' `fullreg' ///
				if `touse' & `ivar' == `i', quantile(`tq')

			if _rc != 0 & _rc != 498 {
				continue
			}
			if e(N) < 5 continue

			local any_tau_ok = 1

			tempname b_qr
			matrix `b_qr' = e(b)

			* φ_i(τ) = coefficient on y_{i,t-1} (first in ECT)
			local phi_val = `b_qr'[1, 1]
			matrix `phi_all'[`pi', `ti'] = `phi_val'

			* Half-life = ln(2)/|φ|
			if `phi_val' < 0 {
				matrix `halflife_all'[`pi', `ti'] = ln(2) / abs(`phi_val')
			}

			* θ_i(τ) = raw coefficient on x_{k,t-1} = b_qr[1, 1+j]
			* β_i(τ) = -θ_x / φ  (long-run multiplier)
			if abs(`phi_val') > 1e-10 {
				forvalues j = 1/`k' {
					local bcol = (`ti' - 1) * `k' + `j'
					local bpos = 1 + `j'
					local theta_val = `b_qr'[1, `bpos']
					matrix `theta_all'[`pi', `bcol'] = `theta_val'
					matrix `beta_all'[`pi', `bcol'] = -`theta_val' / `phi_val'
				}
			}

			* CSA nuisance coefficients in ECT
			forvalues j = 1/`n_csa' {
				local ccol = (`ti' - 1) * `n_csa' + `j'
				local bpos = 1 + `k' + `j'
				capture matrix `csa_all'[`pi', `ccol'] = `b_qr'[1, `bpos']
			}

			* AR lag coefficients
			if `ncoefs_ar' > 0 {
				forvalues j = 1/`ncoefs_ar' {
					local acol = (`ti' - 1) * `ncoefs_ar' + `j'
					local bpos = `ncoefs_ect' + `j'
					matrix `ar_all'[`pi', `acol'] = `b_qr'[1, `bpos']
				}
			}

			* SR impact coefficients
			if `ncoefs_sr' > 0 {
				forvalues j = 1/`ncoefs_sr' {
					local scol = (`ti' - 1) * `ncoefs_sr' + `j'
					local bpos = `ncoefs_ect' + `ncoefs_ar' + `j'
					matrix `sr_all'[`pi', `scol'] = `b_qr'[1, `bpos']
				}
			}
		}

		if `any_tau_ok' {
			local ++success_count
		}
	}

	* ================================================================
	* Compute Mean Group averages
	* ================================================================
	tempname phi_mg beta_mg theta_mg halflife_mg ar_mg sr_mg csa_coef_mg
	matrix `phi_mg' = J(1, `ntau', .)
	matrix `beta_mg' = J(1, max(`beta_dim',1), .)
	matrix `theta_mg' = J(1, max(`beta_dim',1), .)
	matrix `halflife_mg' = J(1, `ntau', .)

	if `ncoefs_ar' > 0 {
		matrix `ar_mg' = J(1, `ncoefs_ar' * `ntau', .)
	}
	else {
		matrix `ar_mg' = J(1, `ntau', .)
	}
	if `ncoefs_sr' > 0 {
		matrix `sr_mg' = J(1, `ncoefs_sr' * `ntau', .)
	}
	else {
		matrix `sr_mg' = J(1, 1, .)
	}
	matrix `csa_coef_mg' = J(1, max(`n_csa' * `ntau', 1), .)

	* Phi & halflife averages
	forvalues t = 1/`ntau' {
		local cnt = 0
		local cnt_hl = 0
		local sum_p = 0
		local sum_hl = 0
		forvalues i = 1/`npanels' {
			if `phi_all'[`i', `t'] != . {
				local ++cnt
				local sum_p = `sum_p' + `phi_all'[`i', `t']
			}
			if `halflife_all'[`i', `t'] != . {
				local ++cnt_hl
				local sum_hl = `sum_hl' + `halflife_all'[`i', `t']
			}
		}
		if `cnt' > 0 matrix `phi_mg'[1, `t'] = `sum_p' / `cnt'
		if `cnt_hl' > 0 matrix `halflife_mg'[1, `t'] = `sum_hl' / `cnt_hl'
	}

	* Beta & theta averages
	local bdim = max(`beta_dim', 1)
	forvalues c = 1/`bdim' {
		local cnt = 0
		local sum_b = 0
		local cnt2 = 0
		local sum_t = 0
		forvalues i = 1/`npanels' {
			if `beta_all'[`i', `c'] != . {
				local ++cnt
				local sum_b = `sum_b' + `beta_all'[`i', `c']
			}
			if `theta_all'[`i', `c'] != . {
				local ++cnt2
				local sum_t = `sum_t' + `theta_all'[`i', `c']
			}
		}
		if `cnt' > 0 matrix `beta_mg'[1, `c'] = `sum_b' / `cnt'
		if `cnt2' > 0 matrix `theta_mg'[1, `c'] = `sum_t' / `cnt2'
	}

	* AR averages
	local ar_cols = colsof(`ar_mg')
	forvalues c = 1/`ar_cols' {
		local cnt = 0
		local sum_a = 0
		forvalues i = 1/`npanels' {
			if `ar_all'[`i', `c'] != . {
				local ++cnt
				local sum_a = `sum_a' + `ar_all'[`i', `c']
			}
		}
		if `cnt' > 0 matrix `ar_mg'[1, `c'] = `sum_a' / `cnt'
	}

	* SR averages
	if `ncoefs_sr' > 0 {
		local sr_dim = `ncoefs_sr' * `ntau'
		forvalues c = 1/`sr_dim' {
			local cnt = 0
			local sum_s = 0
			forvalues i = 1/`npanels' {
				if `sr_all'[`i', `c'] != . {
					local ++cnt
					local sum_s = `sum_s' + `sr_all'[`i', `c']
				}
			}
			if `cnt' > 0 matrix `sr_mg'[1, `c'] = `sum_s' / `cnt'
		}
	}

	* CSA averages
	local csa_dim = `n_csa' * `ntau'
	if `csa_dim' > 0 {
		forvalues c = 1/`csa_dim' {
			local cnt = 0
			local sum_c = 0
			forvalues i = 1/`npanels' {
				if `csa_all'[`i', `c'] != . {
					local ++cnt
					local sum_c = `sum_c' + `csa_all'[`i', `c']
				}
			}
			if `cnt' > 0 matrix `csa_coef_mg'[1, `c'] = `sum_c' / `cnt'
		}
	}

	* ================================================================
	* MG variance
	* ================================================================
	tempname beta_V phi_V sr_V
	matrix `beta_V' = J(max(`beta_dim',1), max(`beta_dim',1), 0)
	matrix `phi_V' = J(`ntau', `ntau', 0)
	matrix `sr_V' = J(max(`ncoefs_sr' * `ntau', 1), max(`ncoefs_sr' * `ntau', 1), 0)

	* Beta variance: per-quantile block
	forvalues t = 1/`ntau' {
		local col_start = (`t' - 1) * `k' + 1
		local col_end = `t' * `k'

		if `k' < 1 continue

		local nv = 0
		forvalues i = 1/`npanels' {
			local ok = 1
			forvalues c = `col_start'/`col_end' {
				if `beta_all'[`i', `c'] == . local ok = 0
			}
			if `ok' local ++nv
		}

		if `nv' > 1 {
			forvalues i = 1/`npanels' {
				local ok = 1
				forvalues c = `col_start'/`col_end' {
					if `beta_all'[`i', `c'] == . local ok = 0
				}
				if `ok' {
					forvalues r = `col_start'/`col_end' {
						forvalues c = `col_start'/`col_end' {
							local dev_r = `beta_all'[`i', `r'] - `beta_mg'[1, `r']
							local dev_c = `beta_all'[`i', `c'] - `beta_mg'[1, `c']
							matrix `beta_V'[`r', `c'] = `beta_V'[`r', `c'] + ///
								`dev_r' * `dev_c'
						}
					}
				}
			}
			local scale = 1 / (`nv' * (`nv' - 1))
			forvalues r = `col_start'/`col_end' {
				forvalues c = `col_start'/`col_end' {
					matrix `beta_V'[`r', `c'] = `scale' * `beta_V'[`r', `c']
				}
			}
		}
	}

	* Phi variance: per-quantile diagonal
	forvalues t = 1/`ntau' {
		local nv = 0
		local ss = 0
		forvalues i = 1/`npanels' {
			if `phi_all'[`i', `t'] != . {
				local ++nv
				local dev = `phi_all'[`i', `t'] - `phi_mg'[1, `t']
				local ss = `ss' + `dev' * `dev'
			}
		}
		if `nv' > 1 {
			matrix `phi_V'[`t', `t'] = `ss' / (`nv' * (`nv' - 1))
		}
	}

	* SR variance: per-element diagonal (MG between-group variance)
	if `ncoefs_sr' > 0 {
		local sr_dim = `ncoefs_sr' * `ntau'
		forvalues c = 1/`sr_dim' {
			local nv = 0
			local ss = 0
			forvalues i = 1/`npanels' {
				if `sr_all'[`i', `c'] != . {
					local ++nv
					local dev = `sr_all'[`i', `c'] - `sr_mg'[1, `c']
					local ss = `ss' + `dev' * `dev'
				}
			}
			if `nv' > 1 {
				matrix `sr_V'[`c', `c'] = `ss' / (`nv' * (`nv' - 1))
			}
		}
	}

	* CSA variance: per-element diagonal (MG between-group variance)
	tempname csa_V
	local csa_vdim = max(`n_csa' * `ntau', 1)
	matrix `csa_V' = J(`csa_vdim', `csa_vdim', 0)

	forvalues c = 1/`csa_vdim' {
		local nv = 0
		local ss = 0
		forvalues i = 1/`npanels' {
			if `csa_all'[`i', `c'] != . {
				local ++nv
				local dev = `csa_all'[`i', `c'] - `csa_coef_mg'[1, `c']
				local ss = `ss' + `dev' * `dev'
			}
		}
		if `nv' > 1 {
			matrix `csa_V'[`c', `c'] = `ss' / (`nv' * (`nv' - 1))
		}
	}

	* ================================================================
	* Return results
	* ================================================================
	return scalar npanels = `npanels'
	return scalar valid_panels = `success_count'
	return scalar ntau = `ntau'
	return scalar k = `k'
	return scalar p = `p'
	return scalar ncoefs_sr = `ncoefs_sr'
	return scalar ncoefs_ar = `ncoefs_ar'
	return scalar n_csa = `n_csa'

	return matrix phi_all = `phi_all'
	return matrix beta_all = `beta_all'
	return matrix theta_all = `theta_all'
	return matrix halflife_all = `halflife_all'
	return matrix ar_all = `ar_all'
	return matrix sr_all = `sr_all'
	return matrix csa_all = `csa_all'

	return matrix phi_mg = `phi_mg'
	return matrix beta_mg = `beta_mg'
	return matrix theta_mg = `theta_mg'
	return matrix halflife_mg = `halflife_mg'
	return matrix ar_mg = `ar_mg'
	return matrix sr_mg = `sr_mg'
	return matrix csa_coef_mg = `csa_coef_mg'

	return matrix beta_V = `beta_V'
	return matrix phi_V = `phi_V'
	return matrix sr_V = `sr_V'
	return matrix csa_V = `csa_V'
end