*! 1.4.0 MLB 01Sep2010
*! combine version 1.3.0 and 1.2.0
*! 1.3.0 MLB 02Aug2010
*! use weights and allow if condition for individual latent variables
*! 1.2.0 MLB 01Aug2010
*! added the beta option
*! 1.1.0 MLB 04Jan2010
*! analytic derivatives for the delta method
*! 1.0.8 MLB 21Dec2009
*! Allow user to specify key variable in each variable block to determine the sign of the latent variables
*! 1.0.7 MLB 14Dec2009
*! Improve the display of the results and add the equation() option
*! 1.0.6 MLB 29Aug2009
*! improved labeling of control variables when the -eform- option is specified
*! 1.0.5 MLB 02Feb2009
*! various smaller improvements
*! 1.0.4 MLB 31Jan2009
*! using Mata to create the nlcom command in order to bypass limits in # of recursive definition of macros
*! 1.0.2 MLB 26Jan2009
*! correct bug in covariance matrix
*! shorten the local containing the transformation for nlcom
*! 1.0.1 MLB 26Jan2009
*! makes calculation of covariance matrix and noheader option compatible with Stata 9.
*! 1.0.0 MLB 25Jan2009


program define sheafcoef, rclass
	version 9.0
	syntax , LATent(string) [EQuation(string) post iterate(passthru) level(passthru) eform beta]

// Default is to look at the first equation
// except in version 11 where with mlogit and mprobit where the first equation could be the base outcome
	if "`equation'" == "" {
		if c(stata_version)>= 11 & ( ("`e(cmd)'" == "mlogit" &  e(k_eq_base) == 1) | ///
		                             ("`e(cmd)'" == "mprobit" & e(i_base)== 1) ) {
			local equation "#2"
		}
		else {
			local equation "#1"
		}
	}
	else{
		if `: word count `equation'' > 1 {
			di as err "Only one equation can be specified in the equation() option"
			exit 198
		}
	}

// check if previous command stores coefficients in e(b)
	capture confirm matrix e(b)
	if _rc {
		di as err "previous estimation command did not store coefficients in e(b)"
		exit 198
	}

// beta can only be used with -regress-, -logit-, -probit-, -ologit-, or -oprobit-.
	if "`beta'" != "" & !inlist("`e(cmd)'", "regress", "logit", "logistic", "probit") {
		di as err "the beta option can only be specified after estimating a model with either"
		di as err "regress, logit, logistic, or probit"
		exit 198
	}
// beta may not be combined with the svy prefix or cluster option
	if "`beta'" != "" & "`e(prefix)'" == "svy" {
		di as err "the beta option may not be specified after an estimation command that used the svy prefix"
		exit 198
	}
	if "`beta'" != "" & "`e(clustvar)'" != "" {
		di as err "the beta option may not be specified after an estimation command that used clustered standard errors"
		exit 198
	}

// beta and eform cannot be specified together
	if "`beta'" != "" & "`eform'" != "" {
		di as err "the beta and eform options cannot be specified together"
		exit 198
	}

// breaking latent() up
	local k = 0
	local colon ":"
	while "`latent'" != "" {
		gettoken lat`++k' latent: latent, parse(;)
		local lat`k' : subinstr local lat`k' ":" " : "
		if `: list colon in lat`k'' {
			gettoken name`k' lat`k' : lat`k', parse(":")
			gettoken garbage lat`k' : lat`k', parse(":")
			local name`k' : list retokenize name`k'
		}
		else {
			local name`k' "lvar_`k'"
		}
		Parseif `lat`k''
		local lat`k' `s(lat)'
		local if`k' `s(if)'
		gettoken semicolon latent : latent, parse(;)
	}

// check if options
	forvalues i = 1/`k' {
		if "`if`i''" != "" {
			capture count `if`i'' & e(sample)
			if _rc {
				di as err "the if condition for latent variable `lvar_`i'' produced an error"
				exit 198
			}
			if r(N) == 0 {
				di as err "the if condition for latent variable `lvar`i'' in combination"
				di as err "with the estimation sample resulted in zero observations"
				exit 2000
			}
		}
	}
// turn if conditions into weigths
	// find weights used in last estimation command
	if "`e(wexp)'" != "" {
		local wname "`e(wexp)'"
		gettoken equal wname : wname, parse("=")
		tempname w
		qui gen double `w' = `wname'
	}
	else {
		tempname w
		qui gen byte `w' = 1
	}
	tempvar touse_w
	qui gen byte `touse_w' = 0 if e(sample)
	forvalues i = 1/`k' {
		quietly {
			replace `touse_w' = 0 if e(sample)
			replace `touse_w' = 1 `if`i''
			tempvar w_`i'
			gen double `w_`i'' = `w'*`touse_w'
		}
	}

// find the key variable
	local plus "+"
	local minus "-"
	forvalues i = 1/`k'{
		local k`i' : word count `lat`i''
		tokenize `lat`i''
		local minus`i' = 0
		forvalues j = 1/`k`i'' {
			local `j' : subinstr local `j' "+" " + ", all
			local `j' : subinstr local `j' "-" " - ", all
			local tminus`i' : list minus in `j'
			local minus`i' = `minus`i'' + `tminus`i''
			local plus`i' : list plus in `j'
			if `tminus`i'' | `plus`i'' {
				local `j' : subinstr local `j' "+" "", all
				local `j' : subinstr local `j' "-" "", all
				unab var : ``j''
				local key`i' "`key`i'' `var'"
			}
		}
		if `: word count `key`i''' > 1 {
			di as err "each block of variables in the latent() option can contain only one key variable"
			exit 198
		}
		if "`key`i''" == "" {
			local sign`i' = 1
		}
		else {
			local sign`i' = cond(`minus`i'', -1, 1)*sign([`equation']_b[`key`i''])
		}

		local lat`i' : subinstr local lat`i' "+" "", all
		local lat`i' : subinstr local lat`i' "-" "", all
		unab lat`i' : `lat`i''
		if `: word count `lat`i''' < 2 {
			di as err "latent variable `k' is determined by less than 2 variables"
			exit 198
		}
		local raw "`raw' `lat`i''"
	}
// check no dups inside latent()
	local dups : list dups raw
	if "`dups'" != "" {
		di as err "`: list uniq dups' appear multiple times in latent()"
		exit 198
	}

// check latent in varlist
	Indeplist, eq(`equation')
	local x "`r(X)'"
	local check : list raw - x
	if "`check'" != "" {
		di as err "`check' where not used as explanatory variables in last `e(cmd)' model"
		exit 198
	}

// collect the other variables
	local other : list x - raw

// collect the coeficient and var-cov matrices (left behind by -Indeplist-)
	tempname b v
	matrix `b' = r(b)
	matrix `v' = r(v)

// make matrices containing the locations of the relevant variables
	local cons "_cons"
	local vars : list x - cons
	local other_vars : list other - cons

	local colnames : colnames `b'
	forvalues i = 1/`k' {
		local k`i' : word count `lat`i''
		tempname ilat`i'
		matrix `ilat`i'' = J(1,`k`i'',.)
		tokenize `lat`i''
		forvalues j = 1/`k`i'' {
			matrix `ilat`i''[1,`j'] = `: list posof "``j''" in colnames'
		}
	}
	tempname iother
	local kother : word count `other'
	matrix `iother' = J(1,`kother',.)
	tokenize `other'
	forvalues i = 1 / `kother' {
		matrix `iother'[1,`i'] = `: list posof "``i''" in colnames'
	}
// similar but now for the v matrix
	forvalues i = 1/`k' {
		local v_var "`v_var' `lat`i''"
	}
	local v_var "`v_var' `other_vars'"
	tempname iv
	matrix `iv' = J(1, `:word count `v_var'',.)
	local i = 1
	foreach var of varlist `v_var' {
		matrix `iv'[1,`: list posof "`var'" in colnames'] = `i'
		local i = `i' + 1
	}


// perform delta method calculations
	tempvar touse
	gen byte `touse' = e(sample)
	capture di [`equation']_b[_cons]
	local nocons = _rc != 0

	mata: parse_sheaf()

	if "`beta'" != "" {
		tempname b_old v_old
		matrix `b_old' = `b'
		matrix `v_old' = `v'
		local old "b_old(`b_old') v_old(`v_old')"
		local pos_cons: list posof "main:_cons_b" in colname
		tempname sd_y
		if "`e(cmd)'" == "regress" {
			qui sum  `e(depvar)' if e(sample)
			scalar `sd_y' = r(sd)
		}
		if "`e(cmd)'" == "logit" | "`e(cmd)'" == "logistic" {
			tempvar xb
			predict double `xb', xb
			qui sum `xb' if e(sample)
			scalar `sd_y' = sqrt(r(Var) + (_pi^2)/3)
			drop `xb'
		}
		if "`e(cmd)'" == "probit" {
			tempvar xb
			predict double `xb', xb
			qui sum `xb' if e(sample)
			scalar `sd_y' = sqrt(r(Var) + 1)
			drop `xb'
		}

		mata : mk_beta()
		local cons "main:_cons_b"
		local  colname : list colname - cons
	}

	matrix  colnames `b' = `colname'
	matrix  colnames `v' = `colname'
	matrix  rownames `v' = `colname'

	tempname mod
	est store `mod'
	Post, b(`b') v(`v') depname(`e(depvar)') obs(`e(N)') esample(`touse') `old'
	ereturn display , `level'
	if "`eform'" != "" {
		di as txt "(_e) indicates the variables whose coefficients have been exponentiated"
	}
	if "`beta'" != "" {
		di as txt "(_b) indicates standardized coefficients"
	}
	if "`post'" == "" {
		qui est restore `mod'
	}
end

program define Parseif, sclass
	syntax anything [if]
	sreturn local lat `"`anything'"'
	sreturn local if `"`if'"'
end

program define Indeplist, rclass
		syntax , eq(string)
        version 7
		if strpos("`eq'", "#") {
			local eqnr = substr("`eq'",2,.)
			local eqns : coleq e(b), quoted
			local eqns : list uniq eqns
			local eqname : word `eqnr' of `eqns'
		}
		else {
			local eqname `eq'
		}
        tempname b v
        matrix `b' = e(b)
        matrix `b' = `b'[1,"`eqname':"]
		matrix `v' = e(V)
		matrix `v' = `v'["`eqname':","`eqname':"]
        local names : colnames `b'
        local dropped ""
        foreach var of local names {
            if [`eq']_b[`var'] == 0 & [`eq']_se[`var'] == 0 {
                local dropped "`dropped'`var' "
            }
        }
        local dropped : list retokenize dropped
        if "`dropped'" != "" {
            local names : list names - dropped
        }
        if "`names'" != "" {
            return local X "`names'"
        }
		return local eqname
		return matrix b = `b'
		return matrix v = `v'
end

program define Post, eclass
	syntax, b(name) v(name) depname(passthru) esample(passthru) obs(passthru) [b_old(name) v_old(name)]
	ereturn post `b' `v', `depname' `esample' `obs'
	ereturn local cmd "sheafcoef"
	if "`b_old'" != "" {
		ereturn matrix V_raw = `v_old'
		ereturn matrix b_raw = `b_old'
	}
end

mata:
void parse_sheaf() {
	k = strtoreal(st_local("k"))
	eq = "[" + st_local("equation") + "]"
	b = st_matrix(st_local("b"))
	V = st_matrix(st_local("v"))
	iother = st_matrix(st_local("iother"))

// ================= compute weighted variance matrix ============
	nvars= cols(tokens(st_local("vars")))
	v = J(nvars,nvars,0)
	x = .
	w = .
    start = 1
	for(i=1 ; i <= k ; i++) {
		st_view(x,.,tokens(st_local("lat" + strofreal(i))),st_local("touse"))
		st_view(w,.,tokens(st_local("w_"  + strofreal(i))),st_local("touse"))
		fine = start + cols(x) - 1
		v[|start, start \fine, fine|] = variance(x,w)
		start = start + cols(x)
	}
	if ( st_local("other_vars") != "" ) {
		st_view(x,.,tokens(st_local("other_vars")),st_local("touse"))
		st_view(w,.,st_local("w"),st_local("touse"))
		fine = start + cols(x) - 1
		v[|start, start \fine, fine|] = variance(x,w)
		start = start + cols(x)
	}
	p = st_matrix(st_local("iv"))
	v = v[p',p]

// =================== compute coefficients ====================
// effects OF latent variable (p)
	p = J(1,k, .)
	for(i=1 ; i<=k ; i++) {
		ilat = st_matrix(st_local("ilat" + strofreal(i)))

		p[1,i] = v[ilat[1],ilat[1]]* b[ilat[1]]^2
		for(j=2; j<=length(ilat); j++) {
			p[1,i] = p[1,i] + v[ilat[j],ilat[j]] *b[ilat[j]]^2
		}
		for(j=1 ; j <=length(ilat) ; j++) {
			for(l=1; l < j; l++) {
				p[1,i] = p[1,i] + 2*v[ilat[j],ilat[l]]*b[ilat[j]] * b[ilat[l]]
			}
		}
		p[1,i] = strtoreal(st_local("sign" + strofreal(i)))*sqrt(p[1,i])
	}
	if (st_local("eform")!="") {
		pp = exp(p)
	}
	else {
		pp = p
	}

// other control variables
	bother = J(1, length(iother),.)
	for(i=1 ; i <= length(iother) ; i++) {
		bother[1,i] = b[iother[i]]
		if (st_local("eform")!="") {
			bother[1,i] = exp(bother[1,i])
		}
	}

// effects ON latent variables (a)
	nl = 0
	for(i=1 ; i <= k; i++){
		ilat = st_matrix(st_local("ilat" + strofreal(i)))
		nl = nl + length(ilat)
	}
	a = J(1,nl,.)
	l = 1
	for(i=1; i <= k ; i++) {
		ilat = st_matrix(st_local("ilat" + strofreal(i)))
		for(j=1; j <= length(ilat) ; j++){
			a[1,l] = b[ilat[j]]/p[i]
			l = l + 1
		}
	}

// =================== compute standard errors ====================
	G = J(length(b)+k , length(b), 0)

	iG = 1
// d p / d x
	for(i=1 ; i <= k ; i++) {
		ilat = st_matrix(st_local("ilat" + strofreal(i)))
		for(j=1 ; j <= length(ilat); j++) {
			dpdx = 0
			for(l=1;l<=length(ilat);l++) {
				dpdx = dpdx + v[ilat[j],ilat[l]]*b[ilat[l]]
			}
			G[i,ilat[j]] = dpdx/p[i]
			if (st_local("eform")!="") {
				G[i,ilat[j]] = G[i,ilat[j]]*exp(p[i])
			}
		}
		iG = iG + 1
	}

// d other / d x
	for(i=1 ; i <= length(iother); i++){
		if (st_local("eform")!="") {
			G[iG,iother[i]] = bother[1,i]
		}
		else {
			G[iG,iother[i]] = 1
		}
		iG = iG + 1
	}
// d a / d x
	for(i = 1 ; i <= k; i++){
		ilat = st_matrix(st_local("ilat" + strofreal(i)))
		for(j = 1; j<= length(ilat); j++) {
			for(l = 1; l <= length(ilat) ; l++) {
				dadx = 0
				for(m=1; m <= length(ilat) ; m++) {
					dadx = dadx + v[ilat[l], ilat[m]]*b[ilat[m]]
				}
				G[iG, ilat[l]] = (j==l)/p[i] - ( b[ilat[j]]* dadx) / (p[i]^3)
			}
			iG = iG + 1
		}
	}
	st_matrix(st_local("b"), (pp, bother, a) )
	st_matrix(st_local("v"), G*V*G')

// ========================= column names ========================
	if (st_local("eform")!= "") {
		colname = "main:" + st_local("name1") + "_e"
		for(i = 2 ; i <= k ; i++) {
			colname = colname + " main:" + st_local("name" + strofreal(i)) + "_e"
		}
		other = tokens(st_local("other"))
		for(i=1; i<=length(other);i++){
			colname = colname + " main:" + other[1,i] + "_e"
		}
	}
	else if (st_local("beta") != "") {
		colname = "main:" + st_local("name1") + "_b"
		for(i = 2 ; i <= k ; i++) {
			colname = colname + " main:" + st_local("name" + strofreal(i)) + "_b"
		}
		other = tokens(st_local("other"))
		for(i=1; i<=length(other);i++){
			colname = colname + " main:" + other[1,i] + "_b"
		}
	}
	else{
		colname = "main:" + st_local("name1")
		for(i = 2 ; i <= k ; i++) {
			colname = colname + " main:" + st_local("name" + strofreal(i))
		}
		other = tokens(st_local("other"))
		for(i=1; i<=length(other);i++){
			colname = colname + " main:" + other[1,i]
		}
	}

	if (st_local("beta") != "") {
		for(i=1; i <= k; i++) {
			lat = tokens(st_local("lat" + strofreal(i)))
			for(j=1; j<=length(lat); j++){
				colname = colname + " on_" + st_local("name" + strofreal(i)) + ":" + lat[1,j] + "_b"
			}
		}
	}
	else {
		for(i=1; i <= k; i++) {
			lat = tokens(st_local("lat" + strofreal(i)))
			for(j=1; j<=length(lat); j++){
				colname = colname + " on_" + st_local("name" + strofreal(i)) + ":" + lat[1,j]
			}
		}
	}
	st_local("colname", colname)
}

end

mata
void mk_beta() {
	k = strtoreal(st_local("k"))
	b = st_matrix(st_local("b"))
	V = st_matrix(st_local("v"))
	iother = st_matrix(st_local("iother"))
	x = .
	st_view(x,.,tokens(st_local("vars")),st_local("touse"))
	v = variance(x)
	sd_y=.
	sd_y = st_numscalar(st_local("sd_y"))

//============================== remove the constant (standardized constant == 0)
	pos_cons = strtoreal(st_local("pos_cons"))
	if (pos_cons != 0) {
		before = pos_cons - 1
		after = pos_cons + 1
		fine = length(b)
		b = b[1 .. before], b[after .. fine]
		V = V[1 .. before  , 1 .. before], V[1 .. before  , after .. fine] \
			V[after .. fine, 1 .. before], V[after .. fine, after .. fine]
		}

//================================================== make a vector of multipliers
// to standarize effect of latent variables on dependent variable
	mult = J(1, fine - 1, .)
	for (i = 1; i <= k; i++) {
		mult[i] = 1/sd_y
	}

// to standardize effect of other variables on dependent variable
	if (pos_cons != 0 ) {
		l = length(iother) - 1
	}
	else {
		l = length(iother)
	}
	j = k + 1
	for(i=1 ; i <= l ; i++) {
		mult[1,j] = sqrt(v[iother[i], iother[i]]):/sd_y
		j = j + 1
	}

// to standardize effect of observed variables on latent variable
	for(i=1; i <= k ; i++) {
		ilat = st_matrix(st_local("ilat" + strofreal(i)))
		for(m=1; m <= length(ilat) ; m++){
			mult[1,j] = sqrt(v[ilat[m], ilat[m]])
			j = j + 1
		}
	}
//=========================================== return standardized effects and
//========================================== their variance-covariance matrix
	st_matrix(st_local("b"), (mult:*b) )
	st_matrix(st_local("v"), (mult:*V:*mult') )
}
end