*! baing v0.8 HNunez and JOtero 06jul2020


capture program drop baing
program define baing, rclass
version 14

syntax varlist(min=2 numeric ts fv) [if] [in] [, CRITeria(integer -1) MAXpc(integer -1) STAND PREfix(string)]

marksample touse
markout `touse' `tvar'

quietly tsreport if `touse'
if r(N_gaps) {
	display in red "sample may not contain gaps"
	exit
}

local numvars  : word count `varlist'
local var1 : word 1 of `varlist'

qui sum `var1' if `touse'
local nobs = r(N)
// display `numvars'
// display `nobs'

if `maxpc'==-1 {
// maximum number of assumed principal components is the number of variables
   local maxpc = `numvars'
}

tempname matsigmasq
matrix `matsigmasq' = J(`numvars',`maxpc',.)

if "`stand'" == "stand" {
	forvalues i = 1/`numvars' {
	tempvar newvar
	local var`i' : word `i' of `varlist'
	qui sum `var`i'' if `touse'
	qui gen double `newvar' = (`var`i''-r(mean))/r(sd)
	local newvarlist `newvarlist' `newvar'
	}
mkmat `newvarlist' if `touse', matrix(X)
qui pca `newvarlist' if `touse', components(`maxpc')
qui predict _s* if `touse', score

forvalues i = 1/`numvars' {
	local chosen ""
	local var`i' : word `i' of `newvarlist'

	forvalues j = 1/`maxpc' {
		local chosen `chosen' _s`j'
		qui reg `var`i'' `chosen' if `touse', noc
		matrix `matsigmasq'[`i',`j'] = e(rss)/e(N)
	}
}
}
else{
	mkmat `varlist' if `touse', matrix(X)
	qui pca `varlist' if `touse', components(`maxpc')
	qui predict _s* if `touse', score

	forvalues i = 1/`numvars' {
		local chosen ""
		local var`i' : word `i' of `varlist'
		forvalues j = 1/`maxpc' {
			local chosen `chosen' _s`j'
			qui reg `var`i'' `chosen' if `touse', noc
			matrix `matsigmasq'[`i',`j'] = e(rss)/e(N)
		}
	}
}

tempvar _vecsigmasq
qui g `_vecsigmasq' = .
// matrix list `matsigmasq'
mata: mata clear
mata: mmatsigmasq = st_matrix("`matsigmasq'")
// mata: mmatsigmasq
mata: avgsigmasqt = mean(mmatsigmasq)'
// mata: avgsigmasqt = avgsigmasq'; avgsigmasqt
mata: st_view(asig=.,.,st_local("_vecsigmasq"), .)
mata: asig[1..rows(avgsigmasqt),.] = avgsigmasqt
// su `_vecsigmasq'

// mata: avgsigmasqt
/*
mata: st_matrix("avgsigmasqt", avgsigmasqt)
// matrix list avgsigmasqt

svmat avgsigmasqt, names(_vecsigmasq)
*/

tempvar factors ic min_ic nfactors_ic
qui gen `factors' = .
qui gen `ic' = .

// IC criteria

if `criteria' == 2 {
	local scale = (`numvars'+`nobs')/(`numvars'*`nobs')*log(min(`numvars',`nobs'))
}
else if `criteria' == 3 {
    local scale = log(min(`numvars',`nobs'))/(min(`numvars',`nobs'))
}
else {
	local criteria = 1
	local scale = (`numvars'+`nobs')/(`numvars'*`nobs')*log((`numvars'*`nobs')/(`numvars'+`nobs'))
}


forvalues i = 1/`maxpc' {
	qui replace `factors' = `i' in `i'
	qui replace `ic' = log(`_vecsigmasq'[`i']) + `i'*`scale' in `i'
}
egen `min_ic' = min(`ic')
quietly gen `nfactors_ic' = `factors' if `min_ic' == `ic'
su `nfactors_ic', mean
display as result "Number of factors based on Bai & Ng (2002, Econometrica) IC"`criteria' " " r(mean)
// list _vecsigmasq `factors' `ic' `min_ic' `nfactors_ic' in 1/`maxpc'
return scalar baing_ic = r(mean)


// This is to compute and retrieve the common factors

if "`prefix'" != "" {
tempname T N

scalar `T' = rowsof(X)
scalar `N' = colsof(X)

	if `T'<`N' {
			matrix XX = X*X'/(`N'*`T')
			matrix U2 = .
			matrix s  = .
			matrix Vt = .
			mata: A = st_matrix("XX")
			mata: U2 = st_matrix("U2")
			mata: s = st_matrix("s")
			mata: Vt = st_matrix("Vt")

			mata: fullsvd(A,U2,s,Vt)

			mata: st_matrix("U2",U2)
			mata: st_matrix("s",s)
			mata: st_matrix("Vt",Vt)

			matrix lambda = U2[.,1..r(mean)]*sqrt(`T')
			matrix `prefix' = X*lambda/`T'
	}
	else {
			matrix XX = X'*X/(`N'*`T')
			matrix U2 = .
			matrix s  = .
			matrix Vt = .
			mata: A = st_matrix("XX")
			mata: U2 = st_matrix("U2")
			mata: s = st_matrix("s")
			mata: Vt = st_matrix("Vt")

			mata: fullsvd(A,U2,s,Vt)

			mata: st_matrix("U2",U2)
			mata: st_matrix("s",s)
			mata: st_matrix("Vt",Vt)

			matrix lambda = U2[.,1..r(mean)]*sqrt(`N')
			matrix `prefix' = X*lambda/`N'
	}

svmat `prefix'

}

drop _s* // _vecsigmasq
end