*! version 1.1.0 , 18apr2022
*! Author: Mustafa Coban, Institute for Employment Research (Germany)
*! Website: mustafacoban.de
*! Support: mustafa.coban@iab.de


*!***********************************************************!
*!     recursive bivariate probit regression     		 	*!
*!***********************************************************!


/*	ESTIMATION	*/

program define rbiprobit, eclass prop(ml_score svyb svyj svyr)
	version 11

	if replay(){
		if ("`e(cmd)'" != "rbiprobit") error 301
		Display `0'
		exit `rc'
	}
	else{

		gettoken key: 0, parse(" =,[")

		*!	postestimation commands
		if `"`key'"' == "margdec"{

			cap confirm variable `key'
			if !_rc{

				dis as err "Please rename the variable {bf:`key'} in the data."
				dis as err "This might lead to conflict with the postestimation command"  ///
							"{bf: rbiprobit margdec}"
				exit 119
			}

			gettoken pfx 0 : 0, parse(" =,[")
			rbiprobit_margdec `0'
		}
		else if `"`key'"' == "tmeffects"{

			cap confirm variable `key'
			if !_rc{

				dis as err 	"Please rename the variable {bf:`key'} in the data."
				dis as err 	"This might lead to conflict with the postestimation command"  ///
							"{bf: rbiprobit tmeffects}"
				exit 119
			}

			gettoken pfx 0 : 0, parse(" =,[")
			rbiprobit_tmeffects `0'
		}
		else{

			*!	rbiprobit estimation
			_vce_parserun rbiprobit, mark(OFFset CLuster): `0'
			if "`s(exit)'" != ""{
				ereturn local cmdline "rbiprobit `0'"
				exit
			}

			Estimate `0'
			ereturn local cmdline "rbiprobit `0'"
		}
	}

end



program define Estimate, eclass

	*!	syntax based on biprobit and heckprobit
	gettoken dep1 0 : 0, parse(" =,[")
	_fv_check_depvar `dep1'

	tsunab dep1: `dep1'
	rmTS `dep1'

	confirm variable `r(rmTS)'
	local dep1n 	`r(rmTS)'
	local dep1eqn 	"`dep1'"


	*!	allow "=" after depvar
	gettoken equals rest : 0, parse(" =")
	if "`equals'" == "=" {
		local 0 `"`rest'"'
	}



	*! parse indepvars
	syntax [varlist(numeric default=none ts fv)] [if] [in] [pw fw iw] , /*
			*/	ENDOGenous(string) [	/*
			*/	noCONStant OFFset(varname numeric) Robust Cluster	/*
			*/	VCE(passthru) SCore(string)	ITERate(passthru)	/*
			*/	moptobj(passthru) MLOpts(string) FROM(string)	/*
			*/	Level(cilevel) noSKIP LRMODEL noLOG	/*
			*/	* ]

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

	local indep1 	`varlist'
	local nocons1	`constant'
	local offset1	`offset'
	local option0 	`options'


	*!	parse depvar_en, indepvars_en and enopts
	EndogEq dep2 indep2 option2 : `"`endogenous'"'

	marksample 	touse
	markout		`touse' `dep1' `dep2' `indep1' `indep2' `offset1' `offset2'


	_get_diopts diopts option0, `option0'
	mlopts	stdopts, `option0'
	local	coll `s(collinear)'
	local	cns	`s(constraints)'

	if "`score'" != ""{
		local wdcnt: word count `score'
		if `wdcnt' == 1 & bsubstr("`score'",-1,1) == "*"{
			*!	User defined STUB*
			local score = bsubstr("`score'",1,length("`score'")-1)
			local score `score'1 `score'2 `score'3
			local wdcnt
		}
	}

	local scvar `"`score'"'

	if "`scvar'" != ""{
		local wdcnt: word count `scvar'
		if `wdcnt' != 3{
			noi dis in red "score() requires that 3 variables be specified"
			exit 198
		}
		confirm new var `scvar'
		local scvar1: word 1 of `scvar'
		local scvar2: word 2 of `scvar'
		local scvar3: word 3 of `scvar'
		tempvar	sc1 sc2 sc3
		local scopt "score(`sc1' `sc2' `sc3')"
	}


	local wtype `weight'
	local wtexp `"`exp'"'
	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')"
	}

	if `"`robust'"' != "" {
		local crtype crittype("log pseudolikelihood")
	}


	if _caller() < 15{
		local parm atanrho:_cons
	}
	else{
		local parm /:atanrho
	}

	local diparm "diparm(atanrho, tanh label(rho))"

	if "`log'" == ""{
		local log "noisily"
	}
	else{
		local log "quietly"
	}

	if "`level'" != ""{
		local level "level(`level')"
	}

	_rmcoll i.`dep2' `indep1' `wgt' if `touse' , `nocons1' `coll'
	local indep1 "`r(varlist)'"

	_rmcoll `indep2' `wgt' if `touse' , `nocons2' `coll'
	local indep2 "`r(varlist)'"


	if "`offset1'" != "" {
		local offo1 "offset(`offset1')"
	}
	if "`offset2'" != "" {
		local offo2 "offset(`offset2')"
	}


	if "`lrmodel'" != ""{
		_check_lrmodel , `skip' `constant' constraints(`cns')	/*
					*/	options(`clopt' `robust' `nocons1' `nocons2')	/*
					*/	indep(`indep1' `indep2')
		local skip	noskip
	}
	local skip = cond("`skip'" != "", "", "skip")



	*!	Taken from "bicop" and modified a bit
	qui{

		count	if `touse'
		local N = r(N)
		if `N' == 0{
			error 2000
		}

		tab `dep1n'	if `touse'
		local nthr1 = r(r)

		tab `dep2n'	if `touse'
		local nthr2 = r(r)

		if `nthr1' != 2{
			if `nthr1' == 1{
				dis in red "There is no variation in `dep1'"
				exit 2000
			}
			else if `nthr1' > 2{
				dis in red "There are more than two groups in `dep1'"
				exit 2000
			}
		}

		if `nthr2' != 2{
			if `nthr2' == 1{
				dis in red "There is no variation in `dep2'"
				exit 2000
			}
			else if `nthr2' > 2{
				dis in red "There are more than two groups in `dep2'"
				exit 2000
			}
		}
	}



	*!	0/1 values for depvar and depvar_en
	qui: levelsof `dep1n'
	if "`r(levels)'" != "0 1"{
		dis in green "{bf:`dep1'} does not vary; remember:"
        dis in green "0 = negative outcome, 1 = positive outcome"
		exit 2000
	}

	qui: levelsof `dep2n'
	if "`r(levels)'" != "0 1"{
		dis in green "{bf:`dep2'} does not vary; remember:"
        dis in green "0 = negative outcome, 1 = positive outcome"
		exit 2000
	}



	if "`from'" == ""{

		`log' dis in green _n "Univariate Probits for starting values"

		*!	eq.1: univariate probit (taken from biprobit)
		`log' dis in green _n "Fitting comparison outcome equation:"
		cap `log' probit `dep1' `indep1' `wgt' if `touse' , /*
					*/	`offo1' `nocons1' nocoef asis `stdopts'	`crtype' /*
					*/	iter(`=min(1000,c(maxiter))')

		if _rc == 0{
			tempname cb1
			mat `cb1' 	= e(b)
			local ll_1	= e(ll)
			mat coleq `cb1' = `dep1eqn'
		}


		*!	eq.2: univariate probit (taken from biprobit)
		`log' dis in green _n "Fitting comparison treatment equation:"
		cap `log' probit `dep2' `indep2' `wgt' if `touse' , /*
					*/	`offo2' `nocons2' nocoef asis `stdopts'	`crtype' /*
					*/	iter(`=min(1000,c(maxiter))')

		local ll_str = e(crittype)

		if _rc == 0{
			tempname cb2
			mat `cb2'	= e(b)
			local ll_2 	= e(ll)
			mat coleq `cb2' = `dep2eqn'
		}


		if "`ll_1'" == ""{
			local ll_1 = 0
		}
		if "`ll_2'" == ""{
			local ll_2 = 0
		}

		local ll_p = `ll_1' + `ll_2'

		*!	stack coefficient estimates
		if "`cb1'`cb2'" != ""{
			tempname from

			if "`cb1'" != "" & "`cb2'" != ""{
				mat `from' = `cb1', `cb2'
				`log' dis in green _n "Comparison:    `ll_str' = " in yellow %10.0g `ll_p'
			}
			else if "`cb1'" != ""{
				mat `from' = `cb1'
				local lrtest "nolrtest"
			}
			else{
				mat `from' = `cb2'
				local lrtest "nolrtest"
			}
			local getvals 1
		}
	}




	if "`nocons1'`nocons2'" != ""{
		local skip "skip"
	}
	if "`indep1'`indep2" == ""{
		local skip = "skip"
	}
	if "`robust'" != ""{
		local skip = "skip"
	}


	*!	constans-only model
	tempname a0
	if "`skip'" == ""{

		*!	eq.1: univariate probit (taken from biprobit)
		cap qui probit `dep1' i.`dep2' `wgt' if `touse' , /*
					*/	`offo1' asis `stdopts' iter(`=min(1000,c(maxiter))')

		if _rc == 0{
			tempname ccb1
			mat `ccb1' 	= e(b)
			mat coleq `ccb1' = `dep1eqn'
		}


		*!	eq.2: univariate probit (taken from biprobit)
		cap qui probit `dep2' `wgt' if `touse' , /*
					*/	`offo1' asis `stdopts' iter(`=min(1000,c(maxiter))')

		if _rc == 0{
			tempname ccb2
			mat `ccb2' 	= e(b)
			mat coleq `ccb2' = `dep2eqn'
		}

		*!	stack coefficient estimates
		tempname a
		mat `a' = (-.3)
		mat colnames `a' = `parm'

		if "`cb1'`cb2'" != ""{
			tempname from0

			if "`cb1'" != "" & "`cb2'" != ""{
				mat `from0' = `ccb1', `ccb2'
			}
			else if "`ccb1'" != ""{
				mat `from0' = `ccb1'
			}
			else{
				mat `from0' = `ccb2'
			}
		}

		mat `from0' = `from0',`a'



		*!	constans-only model estimation
		`log' dis in green _n "Fitting constant-only model"

		#d ;
		`log' ml model lf1 rbiprobit_lf1
			(`dep1eqn': `dep1' = i.`dep2', `nocons1' `offo1')
			(`dep2eqn': `dep2' = , `nocons2' `offo2')
			/atanrho
			if `touse' `wgt' ,
			maximize init(`from0') search(off) wald(0) `iterate'
			`mlopts' `stdopts' nooutput	missing	nopreserve collinear
			`level' `diparm' `lrtest' `diopts' nocnsnotes `crtype' negh
			;
		#d cr


		*!	initial value for atanrho (taken from biprobit)
		local cont "continue"
		if "`getvals'" != ""{
			mat `a0' = e(b)
			mat `a0' = `a0'[1,5]
			mat colnames `a0' = `parm'
			mat `from' = `from',`a0'
		}
	}
	else{
		if "`getvals'" != ""{
			mat `a0' = (0)
			mat colnames `a0' = `parm'
			mat `from' = `from', `a0'
		}
	}

	if "`cns'" != "" | "`skip'" != ""{
		local cont `cont' wald(2)
	}



	*!	full model estimation
	`log' dis in green _n "Fitting full model:"
	local title "Recursive Bivariate Probit Regression"

	#d ;
	`log' ml model lf1 rbiprobit_lf1
			(`dep1eqn': `dep1' = `indep1', `nocons1' `offo1')
			(`dep2eqn': `dep2' = `indep2', `nocons2' `offo2')
			/atanrho
			if `touse' `wgt' ,
			maximize init(`from') search(off) `cont' `robust' `clopt'
			`scopt' `iterate' `moptobj' `mlopts' `stdopts' nooutput
			missing	nopreserve collinear title(`title')	`level' `diparm'
			`lrtest' `diopts' negh
			;
	#d cr

	*!	constraints matrix
	tempname junk
	capture matrix `junk' = e(Cns)
	if !_rc{
		local hascns hascns
	}

	*!	scores
	if "`scvar'" != ""{
		rename `sc1' `scvar1'
		rename `sc2' `scvar2'
		rename `sc3' `scvar3'
		ereturn local scorevars `scvar'
	}

	local r = _b[/atanrho]
	ereturn scalar rho = (exp(2*`r')-1) / (1+exp(2*`r'))

	*! lr-test or wald test
	if "`ll_p'" != "" & "`robust'`lrtest'`hascns'" == ""{
		ereturn scalar	ll_c 	= `ll_p'
		ereturn scalar	chi2_c	= abs(-2*(e(ll_c) - e(ll)))	//
		ereturn local	chi2_ct "LR"
	}
	else{
		qui test _b[/atanrho] = 0
		ereturn scalar chi2_c = r(chi2)
		ereturn local chi2_ct "Wald"
	}



	*!	stored results
	ereturn scalar k_aux = 1
	ereturn scalar k_eq_model = 2
	ereturn local marginsok		"default P11 P10 P01 P00 PMARG1 PMARG2 PCOND1 PCOND2 PCOND10 XB1 XB2 PMARGCOND1"
	ereturn local marginsnotok	"STDP1 STDP2"
	ereturn hidden local marginsderiv 	"default P11 P10 P01 P00 PMARG1 PMARG2 PCOND1 PCOND2 PCOND10 PMARGCOND1"
	ereturn local predict	"rbiprobit_p"
	ereturn local cmd 		"rbiprobit"


	*! display results
	Display, `level' `diopts'
	exit `e(rc)'
end




/* DISPLAY */

program define Display

	version 11
	syntax [, Level(cilevel) *]
	_get_diopts diopts, `options'

	ml display, level(`level') nofootnote `diopts'
	DispLr
	_prefix_footnote

end


*!	taken from biprobit
program define DispLr

	version 11

	if "`e(ll_c)'`e(chi2_c)'" == "" {
		exit
	}

	local chi : di %8.0g e(chi2_c)
	local chi = trim("`chi'")

	if "`e(ll_c)'"=="" {
		di in green "Wald test of rho=0: " ///
			in green "chi2(" in ye "1" in gr ") = " ///
			in ye "`chi'" ///
			in green _col(59) "Prob > chi2 = " in ye %6.4f ///
			chiprob(1,e(chi2_c))
		exit
	}

	di in green "LR test of rho=0: " ///
		in green "chi2(" in ye "1" in gr ") = " in ye `chi' ///
		in green _col(59) "Prob > chi2 = " in ye %6.4f ///
	chiprob(1,e(chi2_c))
end






/* AUXILIARY SUB-PROGRAMS */

*!	taken from biprobit
program define rmTS, rclass

	version 11

	local tsnm = cond( match("`0'", "*.*"),  		/*
			*/ bsubstr("`0'", 			/*
			*/	  (index("`0'",".")+1),.),     	/*
			*/ "`0'")

	return local rmTS `tsnm'
end




*!	parse endog() option
program define EndogEq

	version 11

	args dep2 indep2 option2 colon endog_eq

	gettoken dep rest: endog_eq, parse(" =")
	_fv_check_depvar `dep'

	tsunab dep: `dep'
	rmTS `dep'
	confirm variable `r(rmTS)'

	c_local dep2n	`r(rmTS)'
	c_local `dep2' 	`dep'
	c_local dep2eqn `dep'



	*!	allow "=" after depvar_en
	gettoken equals 0 : rest, parse(" =")
	if "`equals'" != "=" {
		local 0 `"`rest'"'
	}


	*!	parse indepvar_en (based on biprobit and heckprobit)
	syntax [varlist(numeric default=none ts fv)] [, /*
			*/	noCONStant	OFFset(varname numeric) ]


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


	c_local `indep2'	`varlist'
	c_local offset2		`offset'
	c_local nocons2		`constant'
end