*! version 1.2.5  14sep2021
program define hetprobit_nodisplay, eclass byable(onecall) ///
		prop(ml_score svyb svyj svyr bayes)
	version 13.0
	if _by() {
		local BY `"by `_byvars'`_byrc0':"'
	}
	local vv : di "version " string(_caller()) ":"
	`vv' `BY' _vce_parserun hetprobit, mark(CLuster) : `0'
	if "`s(exit)'" != "" {
		version 10: ereturn local cmdline `"hetprobit `0'"'
		exit
	}

	version 6, missing
	if replay() {
		if "`e(cmd)'" != "hetprobit" {
			error 301
		}
		if _by() { error 190 }
		Display `0'
		exit
	}
	`vv' `BY' Estimate `0'
	version 10: ereturn local cmdline `"hetprobit `0'"'
end

program define Estimate, eclass byable(recall)
	version 6, missing
	syntax varlist(ts fv) [if] [in] [fweight pweight iweight] , /*
		*/ het(string)  [Robust CLuster(varname numeric) /*
		*/ SCore(string) OFFset(varname numeric) /*
		*/ FROM(string) noCONstant NOLOg LOg noSKIP/*undoc*/  /*
		*/ LRMODEL waldhet noLRtest /*UNDOCUMENTED*/ /*
		*/ Level(cilevel) MLMethod(string) ASIS DIFficult /*
		*/ DOOPT VCE(passthru) moptobj(passthru) * ]

	local fvops = "`s(fvops)'" == "true" | _caller() >= 11

	if _by() {
		_byoptnotallowed score() `"`score'"'
	}

	if c(userversion) >= 16 {
		local hstripe "lnsigma"
	}
	else {
		local hstripe "lnsigma2"
	}

	/* force difficult option in full model */
	local diff "difficult"
	local p_vars `varlist'
	_get_diopts diopts options, `options'
	mlopts mlopt, `options'
	local coll `s(collinear)'
	local cns `s(constraints)'
	local p_off `offset'
	local p_cons `constan'

	marksample touse

	if "`weight'" != "" { local wgt [`weight'`exp'] }
	if "`cluster'" != "" { local clopt "cluster(`cluster')" }
        _vce_parse, argopt(CLuster) opt(Robust oim opg) old: ///
                [`weight'`exp'], `vce' `clopt' `robust'
        local cluster `r(cluster)'
        local robust `r(robust)'
	if "`cluster'" != "" { local clopt "cluster(`cluster')" }
	local vceopt `r(vceopt)'
	if "`score'" != "" {
		local wcount : word count `score'
		if `wcount'==1 & bsubstr("`score'",-1,1)=="*" {
			local score = /*
			*/ bsubstr("`score'",1,length("`score'")-1)
			local score `score'1 `score'2
			local wcount 2
		}
		if `wcount' != 2 {
			di in red "score() requires two new variable names"
			exit 198
		}
		local s1 : word 1 of `score'
		local s2 : word 2 of `score'
		confirm new var `s1' `s2'
		tempvar sc1 sc2
		local scopt "score(`sc1' `sc2')"
	}

	local 0 `het'
	syntax varlist(numeric min=1 ts fv) [, OFFset(varname numeric) /*
			*/	noCONstant *]

	if !`fvops' {
		local fvops = "`s(fvops)'" == "true"
	}
	if `fvops' {
		if _caller() < 11 {
			local vv "version 11:"
		}
		else	local vv : di "version " string(_caller()) ":"
		local mm e2
		local negh negh
		local fvexp expand
		if "`cns'" != "" {
			local cnsopt constraint(`cns') `coll'
		}
	}
	else {
		local vv "version 8.1:"
		local mm d2
	}
	if "`mlmethod'" == "" {
		local mlmetho `mm'
	}

	local heti `varlist'
	local hetf `offset'
	local offset ""

	markout `touse' `cluster' `offset' `heti' `hetf', strok
	if strmatch(`"`from'"',"e(b)*") {
		tempname ehold
		_est hold `ehold', restore nullok
	}
	`vv' ///
	_rmcoll `heti' if `touse', constant `coll' `fvexp'
	if `"`ehold'"' != "" {
		_est unhold `ehold'
	}
	local heti "`r(varlist)'"

/* Check hetero arguments */
	if "`constan'`options'" != "" {
		noi di in red "Heteroskedastic option(s) not permitted: " _c
		noi di in red "`constan' `options'"
		exit 101
	}

/* process hetero offset */
	if "`hetf'" != "" {
		local hetoff "offset(`hetf')"
		markout `touse' `hetf'
	}

/* Check predictor equation */
	tokenize `p_vars'
	local dep `1'
	_fv_check_depvar `dep'
	tsunab dep : `dep'
	local depn : subinstr local dep "." "_"
	mac shift
	local ind `*'
	local indvar `*'
	if "`p_cons'" == "noconstant" & "`ind'" == "" {
		noi di in red "independent varlist required with " _c
		noi di in red "noconstant option."
		exit 100
	}

/* process predictor offset */
	if "`p_off'" != "" {
		local offset "offset(`p_off')"
		markout `touse' `p_off'
	}

/* check independent variables for collinearity */
	`vv' ///
	noi _rmcoll `dep' `ind' if `touse', `p_cons' `coll' `fvexp' ///
		probit `asis' touse(`touse') noskipline
	local ind "`r(varlist)'"
	gettoken dep ind : ind

	local mllog `log' `nolog'
	_parse_iterlog, `log' `nolog'
	local log "`s(nolog)'"
	if "`log'" != "" {
		local log quietly
	}
	else {
		local log noisily
	}

	if "`asis'`coll'" == "" {
		`log' di
		if _caller() >= 11 {
			local warn nowarning
		}
		`vv' ///
		`log' probit `dep' `ind' `wgt' if `touse', ///
			iter(0) `p_cons' nocoef nolog `doopt' `warn'
		qui replace `touse' = e(sample)
		tempname lb
		mat `lb' = e(b)
		local ind : colnames(`lb')
		tokenize `ind'
		local last : word count `ind'
		if "``last''" == "_cons" { local `last' }
		local ind "`*'"
	}
	else {
		`log' di
	}

	qui count if `touse'
	local nobs = r(N)
	qui count if `dep' == 0 & `touse'
	local nneg = r(N)
	local npos = `nobs' - `nneg'
	if r(N) == 0 | r(N) == `nobs' {
		noi di in red "outcome does not vary; remember:"
		noi di in red _col(35) "0 = negative outcome,"
		noi di in red _col(9) /*
		*/ "all other nonmissing values = positive outcome"
		exit 2000
	}

/* output options */
	if "`robust'`cluster'" != "" | "`weight'" == "pweight" {
		local robust "robust"
	}

	if "`lrmodel'" != "" {
		_check_lrmodel, `skip' `p_cons' constr(`cns') ///
			options(`cluster' `robust' ) indepvar(`indvar')
		local skip "noskip"
	}
	else if "`ind'" == "" | "`robust'`p_cons'" != "" {
		local skip ""
	}

	if "`waldhet'" != "" {
		local lrtest nolrtest
	}

	if "`robust'" != "" {
		local lrtest nolrtest
	}

	if "`from'" != "" {
		local init "init(`from')"
	}

/* code for LR test of full model with hetero vs. full model no hetero */
	local lr_OK 0
	if ("`lrtest'"=="") | ("`init'"=="") {
		tempname ll_c chi2_c
		* compute full probit without heteroskedastic equation

		`log' di in gr "Fitting probit model:"
		`vv' ///
		`log' capture `log' probit `dep' `ind' `wgt' if `touse', /*
				*/ `p_cons' `offset' nocoef asis /*
				*/ iter(`=min(1000,c(maxiter))') `doopt' /*
				*/ `cnsopt' `mllog'
		if _rc == 0 {
			if "`lrtest'" == "" {
				scalar `ll_c' = e(ll)
				scalar `chi2_c' = e(chi2)
				local lr_OK = !missing(e(ll))
			}
			if "`init'" == "" {
				tempname myb
				mat `myb' = get(_b)
				mat coleq `myb' = `depn'
				local init "init(`myb', skip)"
			}
		}
	}

/* constant only model */
	if "`skip'" != "" {
		local continu "continue"

		/* starting values from nonhetero probit */
		tempname p_1 p_2 Ival
		`vv' ///
		quietly probit `dep' if `touse' `wgt', `offset' `doopt' /*
			*/ `cnsopt'
		local fulll = e(ll)
		capture matrix `p_1' = e(b)
		if _rc {
			local confrom ""
		}
		else {
			matrix coleq `p_1' = `dep':
			local rcount : word count `heti'
			matrix `p_2' = J(1,`rcount',0)
			`vv' ///
			mat colnames `p_2' = `heti'
			mat coleq `p_2' = `hstripe'
			mat `Ival' = `p_1', `p_2'
			local confrom "init(`Ival', copy)"
		}

		`log' di _n in gr "Fitting constant-only model:"
		#delimit ;
		`vv'
		`log' ml model `mlmethod' hetpr_lf
			(`depn': `dep' = , `offset')
			(`hstripe': = `heti', nocons `hetoff')
			if `touse' `wgt',
			`confrom' max missing nopreserve wald(0) `mlopt'
			nooutput search(off) `diff' nocnsnotes `negh'
			collinear `mllog';
		#delimit cr

		/* use info for good starting values */
		if "`init'" == "" {
			tempname myh
			mat `myh' = e(b)
			local init "init(`myh')"
		}
	}
	else {
		local continu "wald(1)"
	}

	`log' display _n in gr "Fitting heteroskedastic probit model:"
	#delimit ;
	`vv'
	`log' ml model `mlmethod' hetpr_lf
		(`depn': `dep' = `ind', `p_cons' `offset')
		(`hstripe': `heti', nocons `hetoff') if `touse' `wgt',
		max missing nopreserve `mlopt'
		`continu' search(off)
		`scopt' `vceopt' nooutput `init'
		title(Heteroskedastic probit model) `diff' `negh'
		collinear `moptobj' `mllog';
	#delimit cr
	* if "`log'" == "quietly" { di _n }

	if "`scopt'" != "" {
		rename `sc1' `s1'
		if "`s2'" != "" {
			rename `sc2' `s2'
		}
		est local scorevars `s1' `s2'
	}

	// Test for homoskedasticity if no offset() term within het()
	if "`e(offset2)'" == "" {
		if "`waldhet'" != "" | "`lrtest'`cns'" != "" {
			qui test [`hstripe']
			est scalar chi2_c = r(chi2)
			est scalar df_m_c = r(df)
			est scalar p_c    = r(p)
			est local chi2_ct "Wald"
		}
		else {
			est scalar ll_c =  `ll_c'
			est scalar chi2_c = 2*(e(ll) - `ll_c')
			est scalar p_c = chi2tail(e(df_m_c),e(chi2_c))
			if "`p_cons'" == "noconstant" {
				local fulldof = e(rank)-e(df_m)
			}
			else{
				local fulldof = e(rank)-e(df_m)-1
			}
			est scalar df_m_c = `fulldof'
			est scalar p_c = chiprob(e(df_m_c),e(chi2_c))
			est local chi2_ct "LR"
		}
	}
	else {
		est scalar chi2_c = .
		est scalar df_m_c = .
		est scalar p_c    = .
	}

	est scalar N_s = `npos'
	est scalar N_f = `nneg'

	est local method "ml"
	est local predict "hetprobit_p"
	est local cmd "hetprobit"

	est hidden local hstripe `hstripe'

	*Display, level(`level') `lrtest' `diopts'
end

program define Display
	if "`e(prefix)'" == "svy" {
		_prefix_display `0'
		exit
	}
	syntax [, Level(cilevel) noLRtest waldhet *]

	_get_diopts diopts, `options'
	local crtype = upper(bsubstr(`"`e(crittype)'"',1,1)) + /*
		*/ bsubstr(`"`e(crittype)'"',2,.)

	di in gr _n "Heteroskedastic probit model" _col(49) /*
		*/ "Number of obs     =" in ye _col(69) %10.0gc e(N)
	di in gr _col(49) "Zero outcomes     =" /*
		*/ in ye _col(69) %10.0gc e(N_f)
	di in gr _col(49) "Nonzero outcomes  =" /*
		*/ in ye _col(69) %10.0gc e(N_s) _n
	if !missing(e(df_r)) {
		di in gr _col(49) "F(" %4.0f in ye `e(df_m)' /*
			*/ in gr "," in ye %7.0f e(df_r) /*
			*/ in gr ")" _col(67) "=" in ye _col(70) %9.2f e(F)
		di in gr "`crtype' = " in ye %9.0g e(ll) /*
			*/ in gr _col(49) "Prob > F" _col(67) "=" /*
			*/ in ye _col(70) /*
			*/ in ye %9.4f Ftail(e(df_m),e(df_r),e(F)) _n
	}
	else {
		if "`e(chi2type)'" == "Wald" & missing(e(chi2)) {
			di in smcl _col(49) 				/*
*/ "{help j_robustsingular##|_new:Wald chi2(`e(df_m)'){col 67}= }"	/*
*/				in ye _col(70) %9.2f e(chi2)
		}
		else {
		      di in gr _col(49) "`e(chi2type)' chi2(" in ye `e(df_m)' /*
			*/ in gr ")" _col(67) "=" in ye _col(70) %9.2f e(chi2)
		}
		di in gr "`crtype' = " in ye %9.0g e(ll) /*
			*/ in gr _col(49) "Prob > chi2" _col(67) "=" /*
			*/ in ye _col(70) /*
			*/ in ye %9.4f chiprob(e(df_m),e(chi2)) _n
	}

	version 10: ml di, noheader level(`level') `diopts'

	local hetoff "`e(offset2)'"
	if "`hetoff'" != "" {
		// We cannot test H0: lnsigma=0 unless the offset term is
		// constant, and for identifiability we do not allow
		// a constant term.
		version 16: di as txt ///
"Note: Test of homoskedasticity is not available when {bf:het()} " /*
*/ "has an offset term."
		exit
	}

	local sigtest "`e(chi2_ct)'"
	if "`waldhet'" != "" & "`sigtest'" == "LR" {
		// retain original LR hettest stats
		tempname c2cwald dfcwald pcwald
		qui test [lnsigma]
		local chi : di %8.2f r(chi2)
		local chi = trim("`chi'")
		scalar `c2cwald' = `r(chi2)'
		scalar `dfcwald' = r(df)
		scalar `pcwald'    = r(p)
		di as txt "Wald test of lnsigma=0: " "chi2(" ///
                   as res `dfcwald' as txt ") = " as res `chi' ///
		   as txt _col(59) "Prob > chi2 = " ///
                   as res %6.4f chi2tail(`dfcwald',`c2cwald')
	}
	else {
		if "`e(chi2_ct)'" == "LR" { local txt "LR" }
		else { local txt Wald }

		local chi : di %8.2f e(chi2_c)
		local chi = trim("`chi'")

		di in green "`txt' test of `e(hstripe)'=0: " ///
			"chi2(" in ye "`e(df_m_c)'" in gr ") = " in ye "`chi'" ///
			in green _col(59) "Prob > chi2 = " in ye %6.4f ///
			chiprob(e(df_m_c),e(chi2_c))
	}
end
exit

Note:  The above could be coupled with the _crcshdr.ado code if we save some
       additional words -- right now that program uses "Censored" and
       "Uncensored" instead of what we want here "Positive" and "Negative".