*! version 0.1  23aug2023  Liyang Sun, lsun20@cemfi.es
*! version 0.0  25apr2021  Liyang Sun, lsun20@mit.edu


capture program drop manyweakivpretest
program define manyweakivpretest, eclass sortpreserve
	version 13
	set more off

	_iv_parse `0'

	local lhs `s(lhs)'
	local endog `s(endog)'
	local covariates `s(exog)'
	local instr `s(inst)'
	local 0 `s(zero)'

	syntax [if] [in] [aweight fweight], ///
	[NOConstant]

// 	* Mark sample (reflects the if/in conditions, and includes only nonmissing observations)
// 	marksample touse
// 	markout `touse' `by' `xq' `covariates', strok
	tempname x h xtilde

	qui regress `endog' `covariates', `noconstant' // partial out controls from X (if empty, then partial out the constant term)
	qui predict double `x', residual

// 	dis "`instr'"
// 		dis "`covariates'"
// 		dis "`noconstant'"
	if "`covariates'" == "" & "`noconstant'" != "" {
		local instr_partialed "`instr'" // nothing to partial out
	}
	else if "`covariates'" == "" & "`noconstant'" == "" {
		local instr_partialed ""
		local k = 1
		foreach z of varlist `instr' {
			tempvar z`k'
	// 			dis "`z'"
			qui regress `z', `noconstant'
			qui predict double `z`k'', residual // partial out the constant
			local instr_partialed "`instr_partialed' `z`k''"
		local k = `k' + 1

		}
	}
	else {
		qui mvreg `instr' = `covariates', `noconstant' // partial out controls from Z
		local instr_partialed ""
		local k = 1
		foreach z of varlist `instr' {
			tempvar z`k'
	// 			dis "`z'"
			qui predict double `z`k'', residual equation(#`k') // partial out controls from Z
			local instr_partialed "`instr_partialed' `z`k''"
		local k = `k' + 1

		}
	}



	** now the endogenous varibale and instruments have controls partialled out

	** first-stage regression
	qui regress `x' `instr_partialed', nocons // the constant term is already partialled out
	qui predict double `h', hat // leverage Z_i'(Z'Z)^-1 Z_i
	qui predict double `xtilde', // predicted value Z\hat{pi}
	** move to mata for matrix calculation
	mata: Fhat_fun("`instr_partialed'","`xtilde'","`x'","`h'")
	di
	di in smcl "{help manyweakiv##manyweakiv:Many weak identification test}"

	if `r(F)' == . {
	// if Sigma1_hh is negative, it is also a situation that the asymptotic approximation underlying the test fails and hints strong identification
		dis in gr "Unfortunately the asymptotic approximation underlying this test fails, which might be due to strong identification. Please consult alternative solutions."
	}
	else {
			di in ye "The many-instruments F test statistic" _col(65) %8.2f `r(F)'
	di

	di in gr "Critical values for:"
	di in gr "Ho:   weakly identified so that a nominal 5% JIVE t-test has "
	di in gr _col(30) "maximal actual size larger than 10%" in ye _col(60) %6.2f 4.14
	di in gr "Ho:   weakly identified so that a nominal 2% JIVE t-test has "
	di in gr _col(30) "maximal actual size larger than 5%" in ye _col(60) %6.2f 9.98
    di in gr "Source: Mikusheva and Sun (2022). "
	di in gr "NB: Critical values are for heteroskedatic errors."
	di
	}
	ereturn clear
	ereturn scalar Fhat = `r(F)'
	ereturn scalar Sigma_hh = `r(Sigma1_hh)'





end


mata:
void Fhat_fun(
        string scalar Z_name,			///
		string scalar Zhat_name,		///
		string scalar X_name,			///
		string scalar H_name			///
)
{
			Z = st_data(.,Z_name)
			Zhat = st_data(.,Zhat_name)
			X = st_data(.,X_name)
			H = st_data(.,H_name)
			N = rows(Z)
			K = cols(Z)
			M_diag = J(N,1,1)-H
			ZZ_inv = qrinv(Z'*Z)
			ZZZ_inv = Z*ZZ_inv

			XMX = X:*X - X:*Zhat

			Sigma1_hh = 0
			for (i=1; i<=N; i++) {
				if (mod(i,10000) == 0) {
					printf("Finished %g observations\n",i)
					printf("Sigma1_hh=%g\n",Sigma1_hh)

				}
				Zi = Z[i,]
				Pi = ZZZ_inv * Zi'
				PP = Pi:^2
				PP_off = (PP):/((1-H[i])*M_diag + PP)
				PP_off[i]=0
				Sigma1_hh = Sigma1_hh + XMX[i]*PP_off'*XMX
			}
// 			Sigma1_hh
			Fhat = (X'*Zhat - sum(X:*X:*H))/sqrt(K)/sqrt(2*Sigma1_hh/K)
// 			Fhat
			st_numscalar("r(F)", Fhat)
			st_numscalar("r(Sigma1_hh)", Sigma1_hh)
}
end