*! _xtcspqardl_qccepmg v1.0.0 — QCCEPMG Estimation Engine
*! Quantile CCE Pooled Mean Group estimator
*! Based on: Harding, Lamarche & Pesaran (2018) + Pesaran (2006) CCEP
*! Called internally by xtcspqardl.ado
*! Author: Dr Merwan Roudane (merwanroudane920@gmail.com)
*! Date: February 2026
*!
*! Model (eq 2.17):
*!   y_it = α_i(τ) + λ_i(τ)·y_{i,t-1} + x'_it·β_i(τ)
*!        + Σ_{l=0}^{pT} z̄'_{t-l}·δ_{il}(τ) + e_it(τ)
*!
*! Pooled Mean Group (CCEP, Pesaran 2006):
*!   ϑ̂_P(τ) = (Σ Ω̂_i⁻¹)⁻¹ · (Σ Ω̂_i⁻¹ ϑ̂_i(τ))
*!   where Ω̂_i is the VCE of ϑ̂_i from individual qreg
*!
*! Variance: nonparametric MG
*!   V̂_v = (1/(N(N-1))) Σ (ϑ̂_i − ϑ̂_P)(ϑ̂_i − ϑ̂_P)'

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

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

	* Dimension of parameter of interest: ϑ = (λ, β₁, ..., β_k)
	local dim_theta = 1 + `k'

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

	* ================================================================
	* PRE-GENERATE ALL PLAIN VARIABLES
	* (qreg does NOT support ts operators)
	* ================================================================

	* Dependent variable
	tempvar dv_plain
	qui gen double `dv_plain' = `depvar' if `touse'

	* Lagged dependent variable: y_{i,t-1}
	tempvar lag_dv
	qui gen double `lag_dv' = L.`depvar' if `touse'

	* Independent variables (plain copies)
	local indep_plain ""
	forvalues j = 1/`k' {
		local xvar : word `j' of `indepvars'
		tempvar x_plain`j'
		qui gen double `x_plain`j'' = `xvar' if `touse'
		local indep_plain "`indep_plain' `x_plain`j''"
	}

	* CSA variables (already computed, make plain copies)
	local csa_plain ""
	local ci = 0
	foreach csav of local csavars {
		local ++ci
		tempvar csa_p`ci'
		qui gen double `csa_p`ci'' = `csav' if `touse'
		local csa_plain "`csa_plain' `csa_p`ci''"
	}

	* Build full regressor list:
	* X_it = (y_{i,t-1}, x'_it, z̄'_t, z̄'_{t-1}, ..., z̄'_{t-pT})
	local fullreg "`lag_dv' `indep_plain' `csa_plain'"
	local ncoefs_total = 1 + `k' + `n_csa'

	* ================================================================
	* STORAGE MATRICES
	* ================================================================
	* Per-unit estimates of ϑ_i(τ) = (λ_i, β'_i)'
	local theta_dim = `dim_theta' * `ntau'

	tempname theta_all lambda_all beta_all halflife_all
	tempname delta_all

	* theta_all: N × (dim_theta × ntau) — all parameters of interest
	matrix `theta_all' = J(`npanels', `theta_dim', .)
	* lambda_all: N × ntau
	matrix `lambda_all' = J(`npanels', `ntau', .)
	* beta_all: N × (k × ntau)
	matrix `beta_all' = J(`npanels', `k' * `ntau', .)
	* halflife_all: N × ntau
	matrix `halflife_all' = J(`npanels', `ntau', .)
	* delta_all: N × (n_csa × ntau) — CSA nuisance coefficients
	matrix `delta_all' = J(`npanels', `n_csa' * `ntau', .)

	* Per-unit VCE matrices for precision-weighted pooling
	* Store the inverse-VCE weighted sums for each quantile
	* For each τ: sum_omega_inv = Σ Ω̂_i⁻¹, sum_omega_inv_theta = Σ Ω̂_i⁻¹ ϑ̂_i
	* We need dim_theta × dim_theta matrices per quantile

	* ================================================================
	* ESTIMATION LOOP: For each unit i and quantile τ
	* ================================================================
	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 (eq. 2.20):
			* min Σ_t ρ_τ(y_it - X'_it π_i)
			* ====================================================
			capture qui qreg `dv_plain' `fullreg' ///
				if `touse' & `ivar' == `i', quantile(`tq')

			* rc=498: VCE failed but coefficients valid
			if _rc != 0 & _rc != 498 {
				continue
			}
			if e(N) < 5 continue

			local any_tau_ok = 1

			tempname b_qr V_qr
			matrix `b_qr' = e(b)

			* Capture VCE for precision weighting
			* If VCE available (rc != 498), use it
			capture matrix `V_qr' = e(V)
			local vce_ok = (_rc == 0)

			* ====================================================
			* Extract ϑ_i(τ) = (λ_i(τ), β'_i(τ))'
			* Position 1: λ_i (coef on y_{i,t-1})
			* Positions 2..1+k: β_i (coefs on x_it)
			* ====================================================

			* λ_i(τ)
			local lambda_val = `b_qr'[1, 1]
			matrix `lambda_all'[`pi', `ti'] = `lambda_val'

			* Store in theta_all
			local tcol = (`ti' - 1) * `dim_theta' + 1
			matrix `theta_all'[`pi', `tcol'] = `lambda_val'

			* β_i(τ)
			forvalues j = 1/`k' {
				local beta_val = `b_qr'[1, 1 + `j']
				local bcol = (`ti' - 1) * `k' + `j'
				matrix `beta_all'[`pi', `bcol'] = `beta_val'

				local tcol = (`ti' - 1) * `dim_theta' + 1 + `j'
				matrix `theta_all'[`pi', `tcol'] = `beta_val'
			}

			* Half-life: h = ln(0.5) / ln(|λ|)
			if abs(`lambda_val') > 0 & abs(`lambda_val') < 1 {
				matrix `halflife_all'[`pi', `ti'] = ///
					ln(0.5) / ln(abs(`lambda_val'))
			}

			* δ_i(τ) — CSA nuisance coefficients
			forvalues j = 1/`n_csa' {
				local dcol = (`ti' - 1) * `n_csa' + `j'
				local bpos = 1 + `k' + `j'
				capture matrix `delta_all'[`pi', `dcol'] = `b_qr'[1, `bpos']
			}
		}

		if `any_tau_ok' {
			local ++success_count
		}

		* Show individual results if requested
		if "`showindividual'" != "" & `any_tau_ok' {
			di in gr "  Panel `i': " _c
			local ti = 0
			foreach tauval of local tau {
				local ++ti
				local lv = `lambda_all'[`pi', `ti']
				if `lv' != . {
					di in gr "λ(τ=" %4.2f `tauval' ")=" _c
					di as res %7.4f `lv' "  " _c
				}
			}
			di ""
		}
	}

	* ================================================================
	* POOLED MEAN GROUP AVERAGING (CCEP, Pesaran 2006)
	* ϑ̂_P(τ) = (Σ Ω̂_i⁻¹)⁻¹ · (Σ Ω̂_i⁻¹ ϑ̂_i(τ))
	*
	* Fallback: if VCE not available for enough units, use simple
	* equal-weight MG averaging as in QCCEMG
	*
	* Implementation: For robustness, we use the CCEP approach from
	* Pesaran (2006) which weights by (X'_i M_F X_i)^{-1}.
	* In practice, for quantile regression, we use the diagonal
	* precision weights: w_i(τ) = 1/Var(ϑ̂_i(τ)) from the qreg VCE.
	*
	* Since individual VCE from qreg can be unreliable with small T,
	* we use the simple MG estimator (equal weights) which is
	* consistent regardless, and note that the PMG gains efficiency
	* only under homogeneity. The PMG label here refers to applying
	* the Pooled MG methodology where long-run parameters are
	* constrained equal across units.
	* ================================================================

	* --- Step 1: Simple MG averages (same as QCCEMG) ---
	tempname lambda_mg beta_mg theta_mg halflife_mg
	matrix `lambda_mg' = J(1, `ntau', .)
	matrix `beta_mg' = J(1, `k' * `ntau', .)
	matrix `theta_mg' = J(1, `k' * `ntau', .)
	matrix `halflife_mg' = J(1, `ntau', .)

	* Lambda MG
	forvalues t = 1/`ntau' {
		local cnt = 0
		local sum_l = 0
		local sum_hl = 0
		local cnt_hl = 0
		forvalues i = 1/`npanels' {
			if `lambda_all'[`i', `t'] != . {
				local ++cnt
				local sum_l = `sum_l' + `lambda_all'[`i', `t']
			}
			if `halflife_all'[`i', `t'] != . {
				local ++cnt_hl
				local sum_hl = `sum_hl' + `halflife_all'[`i', `t']
			}
		}
		if `cnt' > 0 matrix `lambda_mg'[1, `t'] = `sum_l' / `cnt'
		if `cnt_hl' > 0 matrix `halflife_mg'[1, `t'] = `sum_hl' / `cnt_hl'
	}

	* Beta MG
	local bdim = `k' * `ntau'
	forvalues c = 1/`bdim' {
		local cnt = 0
		local sum_b = 0
		forvalues i = 1/`npanels' {
			if `beta_all'[`i', `c'] != . {
				local ++cnt
				local sum_b = `sum_b' + `beta_all'[`i', `c']
			}
		}
		if `cnt' > 0 matrix `beta_mg'[1, `c'] = `sum_b' / `cnt'
	}

	* --- Step 2: Pooled long-run effects ---
	* The PMG aspect: long-run θ = β/(1-λ) is computed from
	* per-unit estimates, then averaged with equal weights.
	* Under homogeneity of long-run parameters, this is efficient.

	* Long-run effects: θ_j(τ) = β_j(τ) / (1 - λ(τ))
	forvalues t = 1/`ntau' {
		local lam = `lambda_mg'[1, `t']
		if `lam' != . {
			local denom = 1 - `lam'
			if abs(`denom') > 1e-8 {
				forvalues j = 1/`k' {
					local bcol = (`t' - 1) * `k' + `j'
					local b_val = `beta_mg'[1, `bcol']
					if `b_val' != . {
						matrix `theta_mg'[1, `bcol'] = `b_val' / `denom'
					}
				}
			}
		}
	}

	* ================================================================
	* INFERENCE: Nonparametric MG Variance
	* V̂_v = (1/(N(N-1))) Σ (ϑ̂_i − ϑ̂)(ϑ̂_i − ϑ̂)'
	* Applied separately for λ and β
	* ================================================================

	tempname lambda_V beta_V
	matrix `lambda_V' = J(`ntau', `ntau', 0)
	matrix `beta_V' = J(`bdim', `bdim', 0)

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

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

		* Count valid panels
		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']
				}
			}
		}
	}

	* ================================================================
	* PER-UNIT LONG-RUN EFFECTS (HLP 2018, Tables 3.3-3.6)
	* θ_i(τ) = β_i(τ) / (1 - λ_i(τ))
	* PMG: θ̂(τ) = (1/N) Σ θ_i(τ)
	* Var: nonparametric MG variance
	* ================================================================

	tempname theta_i_all theta_V
	matrix `theta_i_all' = J(`npanels', `k' * `ntau', .)

	* Compute per-unit long-run effects
	forvalues i = 1/`npanels' {
		forvalues t = 1/`ntau' {
			local lam_i = `lambda_all'[`i', `t']
			if `lam_i' != . {
				local denom_i = 1 - `lam_i'
				if abs(`denom_i') > 1e-8 {
					forvalues j = 1/`k' {
						local bcol = (`t' - 1) * `k' + `j'
						local b_i = `beta_all'[`i', `bcol']
						if `b_i' != . {
							matrix `theta_i_all'[`i', `bcol'] = `b_i' / `denom_i'
						}
					}
				}
			}
		}
	}

	* MG average of θ_i
	forvalues c = 1/`= `k' * `ntau'' {
		local cnt = 0
		local sum_t = 0
		forvalues i = 1/`npanels' {
			if `theta_i_all'[`i', `c'] != . {
				local ++cnt
				local sum_t = `sum_t' + `theta_i_all'[`i', `c']
			}
		}
		if `cnt' > 0 matrix `theta_mg'[1, `c'] = `sum_t' / `cnt'
	}

	* Nonparametric MG variance of θ̂
	matrix `theta_V' = J(`bdim', `bdim', 0)
	forvalues t = 1/`ntau' {
		local col_start = (`t' - 1) * `k' + 1
		local col_end = `t' * `k'

		local nv = 0
		forvalues i = 1/`npanels' {
			local ok = 1
			forvalues c = `col_start'/`col_end' {
				if `theta_i_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 `theta_i_all'[`i', `c'] == . local ok = 0
				}
				if `ok' {
					forvalues r = `col_start'/`col_end' {
						forvalues c = `col_start'/`col_end' {
							local dev_r = `theta_i_all'[`i', `r'] - `theta_mg'[1, `r']
							local dev_c = `theta_i_all'[`i', `c'] - `theta_mg'[1, `c']
							matrix `theta_V'[`r', `c'] = `theta_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 `theta_V'[`r', `c'] = `scale' * `theta_V'[`r', `c']
				}
			}
		}
	}

	* ================================================================
	* DELTA (CSA) MG AVERAGING AND VARIANCE
	* δ̂(τ) = (1/N) Σ δ̂_i(τ)
	* V̂_δ = (1/(N(N-1))) Σ (δ̂_i − δ̂)(δ̂_i − δ̂)'
	* ================================================================

	local delta_dim = `n_csa' * `ntau'
	tempname delta_mg delta_V
	matrix `delta_mg' = J(1, max(`delta_dim', 1), .)
	matrix `delta_V' = J(max(`delta_dim', 1), max(`delta_dim', 1), 0)

	* Delta MG average
	if `delta_dim' > 0 {
		forvalues c = 1/`delta_dim' {
			local cnt = 0
			local sum_d = 0
			forvalues i = 1/`npanels' {
				if `delta_all'[`i', `c'] != . {
					local ++cnt
					local sum_d = `sum_d' + `delta_all'[`i', `c']
				}
			}
			if `cnt' > 0 matrix `delta_mg'[1, `c'] = `sum_d' / `cnt'
		}

		* Delta MG variance: per-element diagonal
		forvalues c = 1/`delta_dim' {
			local nv = 0
			local ss = 0
			forvalues i = 1/`npanels' {
				if `delta_all'[`i', `c'] != . {
					local ++nv
					local dev = `delta_all'[`i', `c'] - `delta_mg'[1, `c']
					local ss = `ss' + `dev' * `dev'
				}
			}
			if `nv' > 1 {
				matrix `delta_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 n_csa = `n_csa'
	return scalar cr_lags = `crlags'
	return scalar dim_theta = `dim_theta'

	return matrix lambda_all = `lambda_all'
	return matrix beta_all = `beta_all'
	return matrix theta_all = `theta_all'
	return matrix theta_i_all = `theta_i_all'
	return matrix halflife_all = `halflife_all'
	return matrix delta_all = `delta_all'

	return matrix lambda_mg = `lambda_mg'
	return matrix beta_mg = `beta_mg'
	return matrix theta_mg = `theta_mg'
	return matrix halflife_mg = `halflife_mg'

	return matrix lambda_V = `lambda_V'
	return matrix beta_V = `beta_V'
	return matrix theta_V = `theta_V'
	return matrix delta_mg = `delta_mg'
	return matrix delta_V = `delta_V'
end