*! version 1.0.0  24oct2007
program survci, sortpreserve
	version 10.0
	if "`e(cmd2)'" != "stcox" {
		error 301
	}
	syntax [if] [in], [ BASESurv(varname)  AT(numlist) CLtype(string) ///
			    Level(cilevel) OUTfile(string) SURVONLY HL 	  ///
			    LEGend(passthru) * ]
	if `"`basesurv'"' == "" {
		if "`e(bases)'" == "" {
			di as err "basesurv() must be specified with stcox"
			exit 198
		}
		local basesurv `e(bases)'
		di as txt `"Note: variable `e(bases)' is used as a baseline survivor function"'
	}
	if `"`hl'"' != "" {
		local cltype loglog
	}
	marksample touse
	qui replace `touse' = 0 if !e(sample)
	local xvars: colnames e(b)
	local nxvars: word count `xvars'
	if `"`at'"' == "" {
		tokenize `xvars'
		forvalues i=1/`nxvars' {
			qui summ ``i'' if `touse', meanonly
			if r(min)==0 & r(max)==1 {
				local at `at' 0
			}
			else {
				local at `at' `=r(mean)'
			}
		}
	}
	else {
		if `: word count `at''<`nxvars' {
			di as err `"at() must contain `nxvars' values or nothing"'
			exit 198
		}
	}
	local holdvar `xvars'
	tokenize `at'
	forvalues i=1/`nxvars' {
		gettoken var holdvar: holdvar
		local ex0b `ex0b' `plus' _b[`var']*``i''
		local plus +
		local note `note'`semic' `var'=``i''
		local semic ;
		tempvar xbar`i' c`i'
		local strvars `strvars' `xbar`i''
	}
	if `nxvars'!=0 {
		local ex0b = exp(`ex0b')
	}
	else {
		local ex0b=1
	}
	tempvar exb lns0 time chat nfail a ahat s se lb ub
	qui {
		gen double `lns0' = ln(`basesurv') if `touse'
		predict double `exb' if `touse', hr
		gen `time' = -_t
		sort `time' _d `touse'
		gen double `chat' = sum(`exb') if `touse'
		tokenize `xvars'
		forvalues i = 1/`nxvars' {
			gen double `c`i'' = sum(``i''*`exb') if `touse'
		}
		by `time' _d: gen long `nfail' = _N if _n==_N & _d==1 & `touse'
		sort _t `nfail' `touse'
		tokenize `at'
		forvalues i = 1/`nxvars' {
			if `"`hl'"' != "" {
gen double `xbar`i'' = sum(`nfail'*(`c`i''/`chat')/(`chat')) ///
							if `touse' & `nfail'<.
replace `xbar`i'' = `xbar`i''/abs(`lns0') - ``i'' if `touse' & `nfail'<.
			}
			else {
gen double `xbar`i'' = sum(`nfail'*(`c`i''/`chat'-``i'')/(`chat')) ///
							if `touse' & `nfail'<.
			}
		}
		gen double `ahat' = sum(`nfail'/(`chat'*(`chat'))) ///
							if `touse' & `nfail'<.
		if `"`hl'"' != "" {
			replace `ahat' = `ahat'/(`lns0')^2 ///
							if `touse' & `nfail'<.
		}
		tempvar touse2
		gen byte `touse2' = `touse'*(`nfail'<.)
		mata: _workSE(`"`strvars'"')
		if `"`hl'"' != "" {
			gen double `se' = sqrt(`ahat'+`bhat') if `touse'
		}
		else {
			gen double `se' = `ex0b'*sqrt(`ahat'+`bhat') if `touse'
		}
		// must have only one nonmis obs per failure time
		by _t: replace `se' = sum(`se') if `touse' & _d==1
		replace `se' = `se'[_n-1] if `touse' & `se'>=. & _d==0
		gen double `s' = `basesurv'^`ex0b'
		local z = invnormal(1-(1-`level'/100)/2)
		if `"`cltype'"'=="" | `"`cltype'"'=="loglog" {
			local cltype loglog
			if `"`hl'"' == "" {
				gen double `lb'=`s'^exp(`z'*`se'/abs(ln(`s')))
				gen double `ub'=`s'^exp(-`z'*`se'/abs(ln(`s')))
			}
			else {
				gen double `lb' = `s'^exp(`z'*`se')
				gen double `ub' = `s'^exp(-`z'*`se')
			}
			local cinote loglog based
		}
		else if `"`cltype'"' == "normal" {
			gen double `lb' = `s'-`z'*`se'*`s'
			gen double `ub' = `s'+`z'*`se'*`s'
			local cinote normal based
		}
		else if `"`cltype'"' == "log" {
			gen double `lb' = `s'*exp(-`z'*`se')
			gen double `ub' = `s'*exp(`z'*`se')
			local cinote log based
		}
		else {
			di as err `"cltype(): `cltype' is not allowed"'
			exit 198
		}
		if `"`hl'"' != "" {
			replace `se' = `se'*abs(ln(`s'))
		}
		replace `se' = `se'*`s'
	}
	if `"`outfile'"' != "" {
		_prefix_saving `outfile'
		local saving `"`s(filename)'"'
		local replace `"`s(replace)'"'
		preserve
		qui keep if `touse'
		keep _t _d `s' `se' `lb' `ub'
		order _t _d `s' `se' `lb' `ub'
		rename `s' _surv
		label variable _surv "S_hat(t,x0,b)"
		rename `lb' _lb
		label variable _lb "`level'% lower bounds (pointwise, `cltype' based)"
		rename `ub' _ub
		label variable _ub "`level'% upper bounds (pointwise, `cltype' based)"
		rename `se' _se
		label variable _se "SE of S_hat(t,x0,b)"
		sort _t _d
		label data "Survival estimates adjusted for `note'"
		compress
		save `saving', `replace'
		restore
	}
	local title Covariate-adjusted survival estimate
	local xtitle analysis time
	if `"`legend'"' == "" {
		local legend legend(label(1 "`level'% CI (`cinote')") label(2 "Survivor function"))
	}
	if `"`survonly'"'=="" {
		local cigraph `cigraph' (rline `lb' `ub' _t if `touse', ///
								sort connect(J))
	}
	local plots `cigraph' (line `s' _t if `touse', sort connect(J))
	twoway `plots' , title(`title') xtitle(`xtitle') `legend' ///
				note(Covariates: `note') `options'
end

version 10
mata:
mata set matastrict on

void _workSE(string scalar vars)
{
	real rowvector indvar, touse2
	real colvector r, Bhat
	real matrix Xc, V
	string scalar bhat

	indvar = st_varindex(tokens(vars))
	st_view(touse2, ., st_local("touse2"))
	r = select(range(1,rows(touse2),1), touse2)
	st_view(Xc, ., indvar, st_local("touse2"))
	V = st_matrix("e(V)")
	if (V == J(0,0,.)) Bhat = J(rows(Xc),1,0) /* no covariates */
	else Bhat = rowsum(Xc*V:*Xc)
	(void) st_addvar("double", bhat = st_tempname())
	st_store(r,bhat, Bhat)
	st_local("bhat", bhat)
}
end