*! version 1.0.0  28apr1999
program define ltable2
	version 6
	syntax varlist(min=1 max=2) [fw] [if] [in] [, /*
		*/ noAdjust BY(varname) noCONF Connect(string) /*
		*/ Failure Graph Hazard Intervals(string) /*
		*/ Level(integer $S_level) SAving(string) /*
		*/ SUrvival Symbol(string) noTAb Test TVid(varname) /*
		*/ XLAbel(string) YLAbel(string) factor(integer 1) * ]

	tempvar touse
	mark `touse' `if' `in' [`weight'`exp']
	markout `touse' `by', strok
	tokenize `varlist'

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

	if `"`hazard'"'=="" & `"`failure'"'=="" {
		local surviva "survival"
	}

	if `"`graph'"'!="" {
	     if (`"`hazard'"'!="")+(`"`surviva'"'!="")+(`"`failure'"'!="")>1 {
			error 198
		}
	}

	if `level'<10 | `level'>99 { local level 95 }

	tempvar Group I0 I1 dI Total tt td dead lost start atrisk fail
	tempvar F seF loF upF S loS upS H seH loH upH
	tempvar sv1 st ss
	tempvar Imid sellS

	local t `1'
	if `"`2'"'=="" {
		tempvar d
		quietly gen byte `d'=1
	}
	else local d `2'

	if `"`exp'"'!="" {
		tempvar wgt
		gen long `wgt' `exp'
		local wvar `wgt'
	}
	else 	local wgt 1

	preserve
	quietly {
		keep if `touse'
		drop if `wgt'<=0
		drop if `t'==.
		capture assert `t'>=0
		if _rc {
			noi di in red "time variable has negative values"
			exit 411
		}
		if `"`tvid'"'!="" {
			sort `tvid' `t' `d'
			by `tvid': keep if _n==_N
		}
		count if `d'==.
		if r(N) {
			noisily di in blu /*
				*/ `"(warning:  `d' has missing values; "' /*
				*/ r(N) " obs not used)"
			drop if `d'==.
		}

		local g `by'
		if `"`g'"'=="" {
  			gen `Group' = 1
  			local g `"`Group'"'
        	}
		else {
			capture drop if `g'==.
		}

		if _N==0 { error 2000 }
		keep `t' `d' `g' `wvar'


		/*
			Process intervals or set default intervals
		*/

		if `"`interva'"'=="" { local interva 1 }
		noisily _crcltab `t' `I0' `I1' `"`interva'"'

		sort `g' `I0' `t'

	/*-------------------- CALCULATE STATISTICS -------------------------*/
		by `g': 	gen `Total'= sum(`wgt')
		by `g':		replace `Total'=`Total'[_N]
		by `g': 	gen `tt'   = sum(`wgt'*`t')
		by `g': 	gen `td'   = sum(`wgt'*(`d'!=0))
		by `g' `I0':	gen `dead' = sum(`wgt'*(`d'!=0))
		by `g' `I0':	gen `lost' = sum(`wgt')
		by `g' `I0':	keep if _n==_N

		local gtot=_N

		gen `start'  = `Total'
		drop `Total'
		by `g': replace `start'  = `start'[_n-1]-`lost'[_n-1] if _n>1
		if `"`adjust'"'=="" {
			gen double `atrisk' = `start'-(`lost'-`dead')/2
		}
		else	gen double `atrisk' = `start'
		gen double `fail' = `dead'/`atrisk'

					/* Hazard calculation		*/
		gen `dI'  = `I1'-`I0'
		if `"`adjust'"'=="" {
			gen `H'   = 2*`fail'/((2-`fail')*`dI')
			gen `seH' = `H'*sqrt((1-(`dI'*`H'/2)^2) /*
			*/	     / (`atrisk'*`fail'))
		}
		else {
			gen `H' = `fail'/`dI'
			gen `seH' = `H'/sqrt(`dead')
		}

					/* Fail calculation		*/
		gen double `S'=.
		by `g': replace `S'=cond(_n==1,1,`S'[_n-1])*(1-`fail')
		gen `F' = 1-`S'
		by `g': gen `sv1' = sum(`fail'/(`atrisk'-`dead'))
		by `g': gen `seF' = `S'*sqrt(`sv1') if `S'!=0

		by `g': gen `sellS'= /*
			*/ sqrt(sum(`dead'/(`atrisk'*(`atrisk'-`dead'))) / /*
			*/ (sum(ln((`atrisk'-`dead')/`atrisk'))^2))

		local iz=invnorm(1-(1-`level'/100)/2)
		gen `upS' = `S'^exp(-`iz'*`sellS') if `S'!=0
		gen `loS' = `S'^exp(`iz'*`sellS') if `S'!=0
		*gen `upS' = max(min(`S'+`iz'*`seF',1),0)
		*gen `loS' = max(min(`S'-`iz'*`seF',1),0)

		gen `upF' = 1-`loS'
		gen `loF' = 1-`upS'
		* gen `upF' = max(min(`F'+`iz'*`seF',1),0)
		* gen `loF' = max(min(`F'-`iz'*`seF',1),0)
		if `"`adjust'"'=="" {
			gen `upH' = `H'+`iz'*`seH'
			gen `loH' = `H'-`iz'*`seH'
		}
		else {
			gen `upH' = (`H'/`dead') /*
			*/	*invgammap(`dead',(1+`level'/100)/2)
			gen `loH' = (`H'/`dead') /*
			*/	*invgammap(`dead',(1-`level'/100)/2)
		}
		replace `upH'=0 if `upH'<0
		replace `loH'=0 if `loH'<0


/*------------------------- GRAPH -----------------------------------*/
		if `"`graph'"'!="" {
			if `"`saving'"'!="" {
				local saving `"saving(`saving')"'
			}

			local tlbl : var label `t'
			if `"`tlbl'"'=="" { local tlbl `"`t'"' }
			label var `I1' `"`tlbl'"'

			if `"`connect'"'=="" { local connect "c(l||)" }
			else {
				local connect `"c(`connect'||)"'
			}

			if `"`symbol'"'=="" { local symbol O }
			if `"`xlabel'"'=="" { local xlabel xlab }
			else	local xlabel `"xlab(`xlabel')"'
			if `"`ylabel'"'=="" { local ylabel ylab }
			else	local ylabel `"ylab(`ylabel')"'

			if `"`failure'"'!="" {
				label var `F' "Proportion Failed"
				if `"`conf'"'=="" {
					noisily gr `F' `upF' `loF' `I1',/*
					*/ by(`g') `connect' s(`symbol'ii) /*
					*/ `xlabel' `ylabel' `saving' `options'
				}
				else {
					noisily gr `F' `I1',/*
					*/ by(`g') `connect' s(`symbol') /*
					*/ `xlabel' `ylabel' `saving' `options'
				}
			}
			else if `"`surviva'"'!="" {
				label var `S' "Proportion Surviving"
				if `"`conf'"'=="" {
					noisily gr `S' `upS' `loS' `I1',/*
					*/ by(`g') `connect' s(`symbol'ii) /*
					*/ `xlabel' `ylabel' `saving' `options'
				}
				else {
					noisily gr `S' `I1',/*
					*/ by(`g') `connect' s(`symbol') /*
					*/ `xlabel' `ylabel' `saving' `options'
				}
			}
			else {
				label var `H' "Hazard"
				gen `Imid'=(`I0'+`I1')/2
				label var `Imid' `"`tlbl'"'
				if `"`conf'"'=="" {
					noisily gr `H' `upH' `loH' `Imid',/*
					*/ by(`g') `connect' s(`symbol'ii) /*
					*/ `xlabel' `ylabel' `saving' `options'
				}
				else {
					noisily gr `H' `Imid',/*
					*/ by(`g') `connect' s(`symbol') /*
					*/ `xlabel' `ylabel' `saving' `options'
				}
				drop `Imid'
			}
		}


/*---------------------- TABLE PRESENTATION ----------------*/
		if `"`surviva'"'!="" & `"`tab'"'=="" {
			#delimit ;
			noisily di _n in gr
				_col(18) "Beg."
							_col(55) "Std."
				_n
				_col( 4) "Interval" 	_col(17) "Total"
				_col(25) "Deaths" 	_col(34) "Lost"
				_col(42) "Survival" 	_col(54) "Error"
				_col(64) `"[`level'% Conf. Int.]"'
				_n
				_dup(79) "-" ;
			#delimit cr
			local i 1
			while `i'<=_N {
				if `"`g'"'~=`"`Group'"' & `g'[`i']!=`g'[`i'-1] {
					* noisily di in gr `"`g' "' `g'[`i']
					noisily Ghdr "`g'" `i'
				}
				#delimit ;
				noisily di in gr
					%5.0f `I0'[`i']
					%6.0f `I1'[`i']
				in ye
					%10.0f `start'[`i']
				 	%9.0f  `dead'[`i']
				 	%7.0f  `lost'[`i']-`dead'[`i']
					%11.4f    `S'[`i']*`factor'
					%10.4f  `seF'[`i']*`factor'
					%11.4f  `loS'[`i']*`factor'
					%10.4f  `upS'[`i']*`factor'
				;
				#delimit cr
				local i=`i'+1
			}
			noi di in gr _dup(79) "-"
		}

		if `"`failure'"'!="" & `"`tab'"'=="" {
			#delimit ;
			noisily di _n in gr
				_col(18) "Beg."
				_col(44) "Cum." 	_col(55) "Std."
				_n
				_col( 4) "Interval" 	_col(17) "Total"
				_col(25) "Deaths" 	_col(34) "Lost"
				_col(42) "Failure" 	_col(54) "Error"
				_col(64) `"[`level'% Conf. Int.]"'
				_n
				_dup(79) "-" ;
			#delimit cr
			local i 1
			while `i'<=_N {
				if `"`g'"'~=`"`Group'"' & `g'[`i']!=`g'[`i'-1] {
					noisily di in gr `"`g' "' `g'[`i']
				}
				#delimit ;
				noisily di in gr
					%5.0f `I0'[`i']
					%6.0f `I1'[`i']
				in ye
					%10.0f `start'[`i']
				 	%9.0f  `dead'[`i']
				 	%7.0f  `lost'[`i']-`dead'[`i']
					%11.4f    `F'[`i']*`factor'
					%10.4f  `seF'[`i']*`factor'
					%11.4f  `loF'[`i']*`factor'
					%10.4f  `upF'[`i']*`factor'
				;
				#delimit cr
				local i=`i'+1
			}
			noi di in gr _dup(79) "-"
		}

		if `"`hazard'"'!="" & `"`tab'"'=="" {
			#delimit ;
			noisily di _n in gr
				_col(18) "Beg."
				_col(27) "Cum."		_col(36) "Std."
				_col(56) "Std."
				_n
				_col( 4) "Interval" 	_col(17) "Total"
				_col(25) "Failure"	_col(35) "Error"
				_col(44) "Hazard"	_col(55) "Error"
				_col(64) `"[`level'% Conf. Int.]"'
				_n
				_dup(79) "-" ;
			#delimit cr
			local i 1
			while `i'<=_N {
				if `"`g'"'~=`"`Group'"' & `g'[`i']!=`g'[`i'-1] {
					noisily di in gr `"`g' "' `g'[`i']
				}
				#delimit ;
				noisily di in gr
					%5.0f     `I0'[`i']
					%6.0f     `I1'[`i']
					%10.0f  `start'[`i']
					%10.4f     `F'[`i']*`factor'
				 	%8.4f    `seF'[`i']*`factor'
				in ye
					%10.4f   `H'[`i'] *`factor'
					%10.4f `seH'[`i']*`factor'
					%10.4f `loH'[`i']*`factor'
					%10.4f `upH'[`i']*`factor'
				;
				#delimit cr
				local i=`i'+1
			}
			noi di in gr _dup(79) "-"
		}
		if `factor'~=1 {
			noi di in gr "(rates multiplied by `factor')"
		}

/*--------------FIRST SET OF CHI SQUARE STATISICS -------------------*/
		if `"`g'"'~=`"`Group'"' & `"`test'"'!="" {
  			by `g': keep if _n==_N
  			gen `st'=sum(`td')*log(sum(`tt')/sum(`td'))
			gen `ss'= sum(`td'*log(`tt'/`td'))
			local chi2=2*(`st'[_N]-`ss'[_N])
			#delimit ;
  			noisily di _n(2) in gr
			"Likelihood-ratio test statistic of homogeneity "
				`"(group=`g'):"' _n
  				"chi2( "
				in ye _N-1	/* df */
				in gr " ) = "
				in ye `chi2'
				in gr ",   P = "
				in ye chiprob(_N-1,`chi2') ;
			#delimit cr

/*------------- SECOND SET OF CHI SQUARE STATISICS ------------------*/
			restore
			if `"`exp'"'!="" {
			        local wtopt= "[`weight'`exp']"
			}
			noisily di " "
  			noisily di in gr /*
			*/ `"Logrank test of homogeneity (group=`g'):"'
  			noisily logrank `t' `d' `wtopt' if `touse', by(`g')

		}
	}
end


program define _crcltab
	version 3.0
	local t "`1'"
	local I0 "`2'"
	local I1 "`3'"
	local interva "`4'"
	if "`interva'"=="w" {
		_crcltab `t' `I0' `I1' "0 7 15 30 60 90 180 360 540 720"
		exit
	}
	parse "`interva'", parse(", ")
	quietly gen float `I0'=0
	quietly gen float `I1'=.
	if "`2'"=="" {
		confirm number `1'
		if `1'<=0 | `1'== . {
			di in red "interval() invalid"
			exit 198
		}
		quietly replace `I0' = int(`t'/`1')*`1'
		quietly replace `I1' = `I0'+`1'
		exit
	}
	local last -1
	while "`1'"!="" {
		if "`1'"!="," {
			confirm number `1'
			if `1'<=`last' {
				di in red "intervals must be ascending"
				exit 198
			}
			if `last'!= -1 {
				quietly replace `I0'=`last' /*
						*/ if `t'>=`last' & `t'<`1'
				quietly replace `I1'=`1' /*
						*/ if `t'>=`last' & `t'<`1'
			}
			local last `1'
		}
		mac shift
	}
	if `last'==0 {
		di in red "intervals invalid"
		exit 198
	}
	quietly replace `I0'=`last' if `t'>=`last'
	quietly replace `I1'=. if `t'>=`last'
end

program define Ghdr /* varname idx */
	args varname i
	local typ : type `varname'
	if substr("`typ'",1,3)=="str" {
		di in ye `varname'[`i']
		exit
	}
	local val = `varname'[`i']
	local new : label (`varname') `val'
	if "`val'" != `"`new'"' {
		di in ye `"`new'"'
		exit
	}
	local fmt : format `varname'
	local res : di `fmt' `val'
	local res = trim(`"`res'"')
	di in ye `"`varname' = `res'"'
end