*! version 7.7.7  06mar2023
program stcox, eclass byable(onecall) sort ///
		prop(st swml nohr hr svyb svyj svylb mi)
	version 8, missing
	local version : di "version " string(_caller()) ", missing:"
	if replay() {                  /* Branch off to stcox_fr as needed */
		syntax [, ESTImate *]
		if "`estimate'" == "" {
			if _by() {
				error 190
			}
			if `"`e(cmd2)'"' != "stcox" {
				error 301
			}
			if "`e(shared)'" != "" {
				stcox_fr `0'
				exit
			}
		}
	}
	if _by() {
		local by "by `_byvars'`_byrc0':"
	}
	syntax [varlist(default=none fv)] [if] [in] [, /*
		*/ FRailty(string) SHared(string) ESTImate *]
	local fvops = "`s(fvops)'" == "true" | _caller() >= 11
        if `fvops' {
                local version: di "version " ///
		string(max(11,_caller())) ", missing:"
        }

	if `"`shared'"' != "" | `"`frailty'"' != "" {
		`version' `by' stcox_fr `0'
		ereturn local cmdline `"stcox `0'"'
		exit
	}
	`version' `BY' _vce_parserun stcox, stdata noneedvarlist	///
		mark(STrata SHared OFFset tvc CLuster)			///
		numdepvars(0) : `0'
	if "`s(exit)'" != "" {
		ereturn local cmdline `"stcox `0'"'
		exit
	}
	`version' ///
	`by' stcox_7 `0'
	if (!replay() | "`estimate'" != "") {
		ereturn local cmdline `"stcox `0'"'
	}
	exit
end

program define stcox_7, eclass byable(recall)
	local version : di "version " string(_caller()) ", missing:"
	version 7, missing

	if _caller()<6 {
		if _by() { error 190 }
		ztcox_5 `0'
		exit
	}

	if replay() {
		syntax [, ESTImate noHR * ]
		if `"`estimate'"'=="" {
			if _by() { error 190 }
			if `"`e(cmd2)'"' != "stcox" {
				error 301
			}
			if "`e(prefix)'" == "svy" {
				_prefix_display, `hr' `options'
				exit
			}
			_get_diopts diopts, `options'
			local hr = cond(`"`hr'"'=="", "hr", "")

			local h = cond(`"`e(strata)'"'=="", /*
				*/ "Cox regression", /*
				*/ "Stratified Cox regression")


			local h1="no ties"
			if "`e(ties)'"=="breslow" {
				local h1="Breslow method for ties"
			}
			else if "`e(ties)'"=="efron" {
				local h1="Efron method for ties"
			}
			else if "`e(ties)'"=="partial" {
				local h1="exact partial likelihood method for ties"
			}
			else if "`e(ties)'"=="marginal" {
				local h1="exact marginal likelihood method for ties"
			}

			di _n as txt `"`h' with `h1'"'
			st_hcd
			di
			local offset `e(offset)'
			est local offset
			_coef_table, `hr' `options'
			est local offset `offset'
			`version' stcox_footnote
			if `"`e(converged)'"' == "0" {
				di as txt "Warning: Convergence not achieved."
			}
			exit
		}
	}
	st_is 2 analysis

	local oldbaseh = cond(_caller()<7,"BASEHazard(string)","")

	if _caller() < 14 {
		local TVCOPT TVC(varlist)
	}
	else {
		local TVCOPT TVC(varlist fv)
	}

	syntax [varlist(default=none fv)] [if] [in] [,CLuster(passthru) /*
	*/ CMD ESTImate noHR Level(cilevel) Robust noSHow NOLOG LOG/*
	*/ BREslow EFRon EXACTM EXACTP `oldbaseh' /*
	*/ BASEHC(passthru) BASEChazard(passthru) BASESurv(passthru) /*
	*/ MGale(passthru) esr(passthru) /*
	*/ SCHoenfeld(passthru) SCAledsch(passthru) `TVCOPT' /*
	*/ texp(string) altvce(name) VCE(passthru) * ]

	// NOTE: altvce() is an undocumented option set by _vce_parserun for
	// the purpose of generating an improved error message when this
	// command is called with an option that generates a variable along
	// with an alternative <vcetype> that resamples the data.

	local fvops = "`s(fvops)'" == "true" | _caller() >= 11
        if `fvops' {
                local version: di "version "string(max(11,_caller())) ", missing:"
        }

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

	_get_diopts diopts options, `options'
	_vce_parse, argopt(CLuster) opt(OIM Robust) old		///
		: [`weight'`exp'], `vce' `robust' `cluster'
	local cluster `r(cluster)'
	local robust `r(robust)'
	local vce = cond("`r(vce)'" != "", "`r(vce)'", "oim")

	if `"`basehazard'"' != "" {
		if `"`basehc'"' != "" {
			di as err /*
	*/ "options {bf:basehazard()} and {bf:basehc()} may not be specified together"
			exit 198
		}
		local basehc `"basehc(`basehazard')"'
	}
	if _by() {
		_byoptnotallowed basehc()      `"`basehc'"'
		_byoptnotallowed basechazard() `"`basechazard'"'
		_byoptnotallowed basesurv()    `"`basesurv'"'
		_byoptnotallowed mgale()       `"`mgale'"'
		_byoptnotallowed esr()         `"`esr'"'
		_byoptnotallowed schoenfeld()  `"`schoenfeld'"'
		_byoptnotallowed scaledsch()   `"`scaledsch'"'
	}

	if "`altvce'" != "" {
		_prefix_vcenotallowed "`altvce'" basehc()      `"`basehc'"'
		_prefix_vcenotallowed "`altvce'" basechazard() `"`basechazard'"'
		_prefix_vcenotallowed "`altvce'" basesurv()    `"`basesurv'"'
		_prefix_vcenotallowed "`altvce'" mgale()       `"`mgale'"'
		_prefix_vcenotallowed "`altvce'" esr()         `"`esr'"'
		_prefix_vcenotallowed "`altvce'" schoenfeld()  `"`schoenfeld'"'
		_prefix_vcenotallowed "`altvce'" scaledsch()   `"`scaledsch'"'
	}

	if "`tvc'" != "" {
		_tvc_notallowed basechazard()	`"`basechazard'"'
		_tvc_notallowed basehc()	`"`basehc'"'
		_tvc_notallowed basesurv()	`"`basesurv'"'
		_tvc_notallowed esr()		`"`esr'"'
		_tvc_notallowed mgale()		`"`mgale'"'
		_tvc_notallowed scaledsch()	`"`scaledsch'"'
		_tvc_notallowed schoenfeld()	`"`schoenfeld'"'
	}

	local passthru `basehc' `basechazard' `basesurv' `mgale' /*
		*/ `esr' `schoenfeld' `scaledsch'

	local id : char _dta[st_id]
	local w  : char _dta[st_w]
	local wt : char _dta[st_wt]
	local t0 `"t0(_t0)"'
	local d `"dead(_d)"'

	tempvar touse
	st_smpl `touse' `"`if'"' `"`in'"' `"`cluster'"'
	markout `touse' `varlist'
	if _by() {
		qui replace `touse'=0 if `_byindex'!=_byindex()
	}

	if `"`wt'"'=="pweight" {
		local robust `"robust"'
	}
	if `"`robust'"'!="" & `"`cluster'"'=="" & `"`id'"'!="" {
		local cluster `"`id'"'
	}
	if `"`cluster'"'!="" {
		local cluster `"cluster(`cluster')"'
	}

	st_show `show'

	if `"`texp'"' != "" & "`tvc'" == "" {
		di as err "option {bf:texp()} requires option {bf:tvc()}"
                exit 198
        }

	local fvtvc 0
	if `"`tvc'"'!="" & _caller() < 14 {
		tempvar foft1
		if `"`texp'"' == "" {
			local texp _t
		}
		local texp: subinstr local texp " " "", all
		cap gen double `foft1' = `texp' if `touse'
		if _rc {
			di as err "{p 0 0 2}option {bf:texp()} invalid{p_end}"
			exit 198
		}
		qui count if `touse' & missing(`foft1')
		if `r(N)' {
                        di as err "{p 0 0 2}option {bf:texp()} evaluates to missing for "
                        di as err "`r(N)' observations{p_end}"
			exit 459
		}
		FunctionOfTime `foft1' if `touse'

		// tvc() has a limit of 100 variables
		local tvcvars : word count `tvc'
		if `tvcvars' > 100 {
			di as err "option {bf:tvc()} may not contain more than 100 variables"
			exit 198
		}
		di
		version 11: _rmcoll `tvc', forcedrop
		local tvcvars `r(varlist)'
		local tvc `"tvc(`tvcvars')"'
		local texp `"texp(`texp')"'
		local noblank "noblank"
	}
	else if `"`tvc'"'!="" {
		tempvar foft1
		if `"`texp'"' == "" {
			local texp _t
		}
		local texp: subinstr local texp " " "", all
		cap gen double `foft1' = `texp' if `touse'
		if _rc {
			di as err "{p 0 0 2}option {bf:texp()} invalid{p_end}"
			exit 198
		}
		qui count if `touse' & missing(`foft1')
		if `r(N)' {
                        di as err "{p 0 0 2}option {bf:texp()} evaluates to missing for "
                        di as err "`r(N)' observations{p_end}"
			exit 459
		}
		FunctionOfTime `foft1' if `touse'

		// tvc() has a limit of 100 variables
		fvexpand `tvc' if `touse'
		local tvc `r(varlist)'
		local tvcvars : word count `tvc'
		if `tvcvars' > 100 {
			di as err "option {bf:tvc()} may not contain more than 100 variable"
			exit 198
		}
		di
		version 14: _rmcoll `tvc', expand
		local tvcvars `r(varlist)'
		fvrevar `tvcvars'
		local ttvcvars `r(varlist)'
		if !`: list tvcvars == ttvcvars' {
			local fvtvc 1
		}
		local tvc `"tvc(`ttvcvars')"'
		local texp `"texp(`texp')"'
		local noblank "noblank"
	}
	if `"`cmd'"'!="" {
		di _n as txt `"-> cox _t `varlist' `w' `if' `in',"' _c
	 	di as txt `" `robust' `cluster' `t0' `hr' `d' `tvc' `texp'"' _c
		di as txt `" `options'"' _c
		di as txt `" `breslow' `efron' `exactm' `exactp' `passthru'"'
		exit
	}

	`version' ///
	cox _t `varlist' `w' if `touse', `robust' `cluster' /*
	*/ `t0' `d' `tvc' `texp' /*
	*/ `options' nocoef `breslow' `efron' `exactm' `exactp' /*
	*/ `passthru' `noblank' `log'

	if `fvtvc' {
		tempname b
		matrix `b' = e(b)
		local stripe : colna `b'
		local ntvc : list sizeof ttvcvars
		local TVCVARS : copy local tvcvars
		forval i = 1/`ntvc' {
			gettoken tvar ttvcvars : ttvcvars
			gettoken xvar TVCVARS : TVCVARS
			if "`tvar'" != "`xvar'" {
				local stripe : subinstr local stripe	///
					"`tvar'" "`xvar'", word all
			}
		}
		version 14: matrix colna `b' = `stripe'
		est repost b=`b', rename buildfvinfo ADDCONS
	}
	else {
		est repost, buildfvinfo ADDCONS
	}
	local chi2type "`e(chi2type)'"

	if e(N)==0 | e(N)>=. { exit 2001 }
	/* inherits e() stuff from -cox- */

	SaveOpt, `passthru'

	_post_vce_rank, checksize
	if "`chi2type'"=="Wald" {
		WaldTest
	}
	else {
		local df_m = cond("`e(rank)'"!="","`e(rank)'","0")
		est scalar df_m = `df_m'
		est local chi2type "LR"
	}

	global S_E_ll = e(ll)			/* double save */
	global S_E_chi2 = e(chi2)		/* double save */
	global S_E_mdf = e(df_m)		/* double save */


	st_hc `touse'

	if "`robust'" != "" {
		est local vce robust
	}
	est local vce "`vce'"
	est local estat_cmd stcox_estat
	est local predict stcox_p
	est local footnote stcox_footnote
	_ms_eq_info
	est hidden local k_eform = r(k_eq)
	est hidden local marginsprop addcons allcons
	est hidden local marginsfootnote _multirecordcheck
	est local marginsnotok	CSNell		///
				DEViance	///
				DFBeta		///
				ESR		///
				LDisplace	///
				LMax		///
				MGale		///
				SCAledsch	///
				SCHoenfeld	///
				SCores
	est local cmd2 "stcox"
	local offset `e(offset)'
	est hidden local offset1 `offset'
	est local offset `offset'
	global S_E_cmd2 "stcox"		/* double save */
	tempname b
	mat `b' = e(b)
	_ms_omit_info `b'
	local cols = colsof(`b')
	if `r(k_omit)' {
		if `r(k_omit)' == `cols' {
		  local varlist ""
		}
		else {
		  mata : ///
		  st_local("varlist",invtokens(select(st_matrixcolstripe ///
		  ("`b'")[.,2]',1:-st_matrix("r(omit)"))))
		}
	}
	else {
		local varlist `varlist' `tvcvars'
	}
	unopvarlist `varlist'
	local varlist `r(varlist)'
	signestimationsample _t _t0 _d `varlist' `e(clustvar)' ///
				       `e(offset)' `e(strata)'
	`version' ///
	stcox, `hr' level(`level') `diopts'
end


program define SaveOpt, eclass
	syntax [, MGale(string) BASEHC(string) BASEChazard(string) /*
		*/ BASESurv(string) SCHoenfeld(string) /*
		*/ SCAledsch(string) ESR(string) * ]
	est local mgale "`mgale'"
	est local basehc "`basehc'"
	est local baseh "`basedchazard'"
	est local basech "`basechazard'"
	est local bases "`basesurv'"

	SaveNm vl_sch "`schoenfeld'" "Schoenfeld"
	SaveNm vl_ssc "`scaledsch'" "scaled Schoenfeld"
	SaveNm vl_esr "`esr'"	  "efficient score"

	if "`mgale'"      != "" { label var `mgale' "martingale" }
	if "`basehc'"!= "" {
			label var `basehc' "baseline hazard contribution"
	}
	if "`basesurv'"   != "" { label var `basesurv' "baseline survivor function" }
	if "`basechazard'"!= "" {
		label var `basechazard' "cumulative baseline hazard"
	}
end

program define SaveNm, eclass
	args name base lname

	if "`base'" == "" { exit }

	tempname b
	mat `b' = get(_b)
	local p = colsof(`b')
	local names : colnames(`b')

	local j = index("`base'","*")
	if `j' {
		local base = bsubstr("`base'",1,`j'-1)
		local i 1
		while `i' <= `p' {
			local iname : word `i' of `names'
			label var `base'`i' "`lname' - `iname'"
			local list `list' `base'`i'
			local i = `i'+1
		}
	}
	else {
		tokenize `base'
		local i 1
		while `i' <= `p' {
			local iname : word `i' of `names'
			label var ``i'' "`lname' - `iname'"
			local list `list' ``i''
			local i = `i'+1
		}
	}
	est local `name' `list'
end

program WaldTest, eclass

	est local chi2type Wald

	tempname b
	mat `b' = e(b)
	local names : colnames `b'
	capture test `names'

	if !_rc {
		est scalar chi2 = r(chi2)
		est scalar df_m = r(df)
	}
	else {
		est scalar chi2 = 0
		est scalar df_m = 0
	}
end

program FunctionOfTime, sortpreserve
	syntax varname [if]

	marksample touse
	tempvar gr
	qui egen long `gr' = group(_t `touse')
	sort `gr'
	cap by `gr': assert `varlist' == `varlist'[1] if `touse'
	if _rc {
		di as err "{p 0 0 2}option {bf:texp()} is not a proper function of time "
		di as err "{p_end}"
		exit 459
	}
end

exit