*! Timothy Neal -- 10/09/15
*! This is the first public version of xtkr, used to conduct Keane-Runkle regressions on dynamic panel data sets (N >> T).
*! If there are any questions, issues, or comparatibility problems with this procedure, please email timothy.neal@unsw.edu.au.
program define xtkr, eclass prop(xt)
	version 11

	*! Parses ivregress syntax
	_iv_parse `0'
	local Y `s(lhs)'
	local endo `s(endog)'
	local Xs `s(exog)'
	local instr `s(inst)'
	local 0 `s(zero)'

	syntax [if] [in] [, NOCONS TDUM]

	qui {
	*! Mark the sample that is usable, identify the panel and time variable, and calculate other panel statistics.
	marksample touse
	xtset
	local ivar `r(panelvar)'
	local tvar `r(timevar)'
	tempname is t m
	quie levels `ivar' if `touse', local(ids)
	local is=wordcount("`ids'")
	tempvar constant e e2 samplevar
	local Yname = "`Y'"
	if "`nocons'" == "" {
		local names = "`Xs' `endo' constant"
		local instrlist = "`Xs' constant `instr'"
	}
	else {
		local names = "`Xs' `endo'"
		local instrlist = "`Xs' `instr'"
	}

	*! Time-demean the data (if specified)
	if "`tdum'" != "" {
		local i = 1
		foreach x in `Y' `Xs' `endo' `instr' {
			tempvar td`i' hold timemeans
			local `i' += 1
			gen `hold' = `x'
			bysort `tvar': egen `timemeans' = mean(`hold')
			sort `ivar' `tvar'
			qui gen `td`i'' = `hold' - `timemeans'
			drop `timemeans'
			drop `hold'
			local demeanlist "`demeanlist' `td`i''"
		}

		*! Understand the number of variables in each
		local numxs = wordcount("`Xs'")
		local numendo = wordcount("`endo'")
		local numinstr = wordcount("`instr'")

		*! Replace original variable macros with time-demeaned versions
		tokenize `demeanlist'
		local Y = "`1'"
		local i = 1
		if `numxs' > 0 {
			local Xs ""
			forvalues j = 1/`numxs' {
				local i = `i' + 1
				local Xs "`Xs' ``i''"
			}
		}
		if `numendo' > 0 {
			local endo ""
			forvalues j = 1/`numendo' {
				local i = `i' + 1
				local endo "`endo' ``i''"
			}
		}
		if `numinstr' > 0 {
			local instr ""
			forvalues j = 1/`numinstr' {
				local i = `i' + 1
				local instr "`instr' ``i''"
			}
		}
	}

	*! Set up a list of all variables that will need to be transformed
	local transformvarlist "`transformvarlist' `Y' `Xs' `endo'"
	if "`nocons'" == "" {
		gen `constant' = 1
		local transformvarlist "`transformvarlist' `constant'"
	}
	*! Do an initial 2SLS regression on the data to get the residuals.
	if "`nocons'" == "" {
		ivregress 2sls `Y' `Xs' `constant' (`endo' = `instr') if `touse', nocons
	}
	else {
		ivregress 2sls `Y' `Xs' (`endo' = `instr') if `touse', nocons
	}

	*! Prepare the data for the mata command
	local krnum = wordcount("`transformvarlist'")
	predict double `e' if e(sample), resid
	local t = e(N)/`is'
	gen `samplevar' = 0
	replace `samplevar' = 1 if e(sample)

	*! Generate new vars to hold the transformed variables
	local l = 0
	foreach v of local transformvarlist {
		local l = `l' + 1
		tempvar kr_`l'
		qui gen `kr_`l'' = .
		local krvars "`krvars' `kr_`l''"
	}

	*! Run Mata command for the transformation
	mata: krtransform("`transformvarlist'", "`krvars'", "`e'", "`samplevar'", `t', `is', `krnum')

	*! 2SLS on transformed variables to get the beta coefficients
	tokenize `krvars'
	local krY `1'
	mac shift
	local krX `*'
	if "`nocons'" == "" {
		ivregress 2sls `krY' (`krX' = `instr' `Xs' `constant') if `touse', nocons
	}
	else {
		ivregress 2sls `krY' (`krX' = `instr' `Xs') if `touse', nocons
	}

	*! Standard error and t-statistic calculation
	predict double `e2' if e(sample), resid
	local Obs = e(N)
	mata: varcorrect("`e2'")

	*! Put column and row labels on the result matrices
	matrix b = e(b)
	matrix colnames b = `names'
	matrix rownames V = `names'
	matrix colnames V = `names'

	*! Display results
	noi {
		di _newline
		di in gr "Keane-Runkle (1992) Regression"
		di in gr "Number of Obs: `Obs'" _col(37) "Number of Panel Units: `is'"
		ereturn post b V, depname("`Yname'") obs(`Obs') esample(`samplevar')
		noi ereturn display
		di in gr "Instruments: `instrlist'"
		if ("`tdum'" != "") di in gr "Data has been time-demeaned."
	}
}

*! Mata functions:
*! 'krtransform' takes the dependent variable, exogenous variables, and endogenous variables and applies the Keane-Runkle transformation (panel forward factorisation).
*! 'varcorrect' corrects the variance-covariance matrix from the second 2sls
end

mata:
void krtransform(string scalar varlist, string scalar newvarlist, string scalar residuals, string scalar touse, real scalar T, real scalar N, real scalar M)
{
	real vector e, ehold, temphold
	real matrix eshape1, cmom, sigmat, vars, varshold, Phat, Qhat

	st_view(e, ., tokens(residuals), touse)
	st_view(vars, ., tokens(varlist), touse)
	st_view(newvars, ., tokens(newvarlist), touse)
	ehold = e
	eshape1 = colshape(ehold, T)
	cmom = cross(eshape1, eshape1)

 	sigmat = cmom / N
	Phat = cholesky(invsym(sigmat))

	Qhat = I(N) # Phat

	for (i = 1; i<= M; i++) {
		temphold = vars[.,i]
		newvars[.,i] = cross(Qhat,temphold)
	}
}

void varcorrect(string scalar residuals)
{
	real scalar Var
	real vector e
	real matrix V, Vadj

	V = st_matrix("e(V)")
	st_view(e, ., tokens(residuals))
	Var = variance(e)
	Vadj = V / Var
	st_matrix("V",Vadj)
}
end