*! version 1.0.0  09aug2007
program stpowplot
	version 10.0
	gettoken cmd 0 : 0, parse(" ,")
	local len = length(`"`cmd'"')
	if `"`cmd'"' == "," | `"`cmd'"' == "" {
		di as err "logrank subcommand must be specified"
		exit 198
	}
	else if `"`cmd'"' == substr("logrank",1,`len') & `len'>=3 {
		local cmd logrank
		local plottest Log-rank test
	}
	else if `"`cmd'"' == substr("exponential",1,`len') & `len'>=3 {
		di as err "exponential is not yet available with stpowplot"
		exit 198
		local cmd exponential
		local plottest Exponential test
	}
	else if `"`cmd'"' == "cox" {
		local cmd cox
		local plottest Cox PH model
		di as err "cox is not yet available with stpowplot"
		exit 198
	}
	else {
		di as err "unknown stpower subcommand `cmd'"
		exit 198
	}
	// get plot#opts()
	syntax [anything] [, * ]
	local 0 , `options'
	tokenize `"`options'"'
	local i = 1
	while `"``i''"' != "" {
		if substr(`"``i''"',1,4) == "plot" {
			if substr(`"``i''"',1,5) == "ploto" {
				syntax [, PLOTOpts(string) * ]
			}
			else {
				local k = substr(`"``i''"',5,1)
				cap confirm number `k'
				if _rc {
di as err `"option ``i'' is incorrectly specified"'
exit 198
				}
				syntax [, PLOT`k'opts(string) * ]
			}
			local 0 , `options'
		}
		local ++i
	}
	local  0 `anything', `options'
	syntax [anything] , Yaxis(string) Xaxis(string) 		     ///
			[ OVER(string) LEGend(string asis) BY(string) 	     ///
			  Alpha(string) Power(string)  Beta(string) 	     ///
			  n(string) HRATio(string) p1(string) NRATio(string) ///
			  BYOPts(string) OUTfile(string) * ]
	// type of computation
	if `"`n'"' != "" {
		if `"`power'"' != "" {
			local compute "effect size"
		}
		else {
			local compute power
		}
	}
	else {
		local compute "sample size"
	}
	di as txt `"Estimating `compute' ..."'
	gettoken comma rest : rest, parse(", ")

	// get twoway opts
	_get_gropts , graphopts(`options') gettwoway  getallowed(addplot)
	// only twoway options are allowed; macro options must contain
	// -stpower- options only
        local options `"`s(graphopts)'"' /*stpower options only*/
        local twopts `"`s(twowayopts)'"'
        local addplot `"`s(addplot)'"'

	// parse extended numlists in options
	local numopts alpha power beta n hratio p1 nratio
	foreach opt of local numopts {
		if `"``opt''"' != "" {
			ChkExtNumlist ``opt'', optname(`opt')
			local nextopt `s(nextopt)'
			if `"`nextopt'"' == "" { /* ext. numlist not specified */
				local nextopt `opt'(``opt'')
			}
			local options `options' `nextopt'
		}
	}
	// parse extended numlists in arguments
	gettoken arg1 anything : anything, match(parent)
	if `"`arg1'"' != "" {
		ChkExtNumlist `arg1'
		if `"`s(nextopt)'"' != "" {
			local arg1 `s(nextopt)'
		}
		else {
			local arg1 (`arg1')
		}
		gettoken arg2 anything: anything, match(parent)
		if `"`arg2'"' != "" {
			ChkExtNumlist `arg2'
			if `"`s(nextopt)'"' != "" {
				local arg2 `s(nextopt)'
			}
			else {
				local arg2 (`arg2')
			}
		}
	}
	// check xaxis() and yaxis() columns
	local nwords: word count `yaxis'
	if `nwords' > 1 {
		di as err "yaxis(): only one column name may be specified"
		exit 198
	}
	ChkXYcols, `yaxis'
	local ylab `s(lab)'
	local nwords: word count `xaxis'
	if `nwords' > 1 {
		di as err "xaxis(): only one column name may be specified"
		exit 198
	}
	ChkXYcols, `xaxis'
	local xlab `s(lab)'

	tempfile holdtabvals
	local cols alpha power beta n n1 n2 e hr loghr s1 s2 p1 nratio w
	stpower `cmd' `arg1' `arg2', `options' table saving(`holdtabvals') ///
				      columns(`cols') nooutput
	if r(onesided) == 1 {
		local sides one sided
	}
	else {
		local sides two sided
	}
	local method = r(method)
	// if any dataset is open
	qui preserve
	qui use `holdtabvals', clear
	LabelVars
	// get plotting range
	qui summ `xaxis', meanonly
	if r(min) == r(max) {
		if `"`xaxis'"' != "hr" {
			local xrange 0 `r=(max)'
		}
		else {
			local xrange `=(r(max)>1)' `=r(max)'
		}
	}
	else {
		local xrange `=r(min)' `=r(max)'
	}
	qui summ `yaxis', meanonly
	if r(min) == r(max) {
		if `"`yaxis'"' != "hr" {
			local yrange 0 `r=(max)'
		}
		else {
			local yrange `=(r(max)>1)' `=r(max)'
		}
	}
	else {
		local yrange `=r(min)' `=r(max)'
	}
	// obtain study parameters to be reported in the note
	local exclude loghr p1 n e n1 n2 beta
	if `"`xaxis'"' == "beta" | `"`yaxis'"' == "beta" {
		local exclude `exclude' power
	}
	else if `"`yaxis'"' == "loghr" {
		local exclude `exclude' hr
	}
	else if `"`xaxis'"' == "p1" {
		local exclude `exclude' nratio
	}
	GetNote, exclude(`by' `over' `xaxis' `yaxis' `exclude') level(`sides')
	local keepvars `s(varlist)'
	local note note(`"Other study parameters: `s(note)'"')
	// get titles and subtitles
	gettoken first rest: xlab
	local first = lower(`"`first'"')
	gettoken rest: rest, parse(",")
	local title title(`"`ylab' vs `first' `rest'"')
	local subti subtitle(`"`plottest', `method' method"')
	local singlepnt = 0
	// check if yaxis() and xaxis() do not vary
	tempvar ymin ymax xmin xmax
	if `"`by'"' != "" | `"`over'"' != "" {
		tempvar byover
		qui egen `byover' = group(`by' `over')
		qui by `byover', sort: egen double `ymin' = min(`yaxis')
		qui by `byover', sort: egen double `ymax' = max(`yaxis')
		qui by `byover', sort: egen double `xmin' = min(`yaxis')
		qui by `byover', sort: egen double `xmax' = max(`yaxis')
	}
	else {
		qui egen double `ymin' = min(`yaxis')
		qui egen double `ymax' = max(`yaxis')
		qui egen double `xmin' = min(`yaxis')
		qui egen double `xmax' = max(`yaxis')
	}
	cap assert `ymin'==`ymax' & `xmin'==`xmax'
	local singlepnt = _rc
	cap isid `yaxis' `byover'
	if `"`over'"' != "" {
		if `"`plotopts'"' != "" {
			di as err "plotopts() and over() may not be combined"
			exit 198
		}
		tempvar group
		qui egen `group' = group(`over')
		qui tab `group'
		cap assert r(r) <= 10
		if _rc {
			// !!
			di as err "too many categories in over();"
			di as err "use combination of by() and over()"
			exit 198
		}
	}
	local yvar `yaxis'
	local xvar `xaxis'
	local yplotopts ytitle(`"`ylab'"') ysc(range(`yrange')) ylab(#10, grid)
	local xplotopts xtitle(`"`xlab'"') xsc(range(`xrange')) xlab(#10, grid)
	if c(N) == 1 | !`singlepnt' {
		// scatter plot if only one data point or values do not vary
		local plottype scatter
	}
	else {
		local plottype line
	}
	if `"`over'"' != "" { // overlaid plots
		qui levelsof `group', local(overcat)
		tokenize `overcat'
		local i = 1
		while `"``i''"' != "" {
			GetGroupLabel `over' if `group'==``i''
			local lgnd `lgnd' label(`i' `"`s(label)'"')
			label define overlab ``i'' `"`s(label)'"', add
			local plotyx `plotyx' `sep' 		///
				(`plottype' `yvar' `xvar' 	///
				  if `group' == ``i'', sort `plot`i'opts')
			local sep "||"
			local ++i
		}
		label values `group' overlab
	}
	else {
		sort `xvar' `yvar'
		local plotyx `plottype' `yvar' `xvar', `plotopts'
	}
	if `"`by'"' != "" { // separate plots
		tempvar bygroup
		qui egen `bygroup' = group(`by')
		qui levelsof `bygroup', local(bycat)
		tokenize `bycat'
		local i = 1
		while `"``i''"' != "" {
			GetGroupLabel `by' if `bygroup'==``i''
			label define bylab ``i'' `"`s(label)'"', add
			local ++i
		}
		label values `bygroup' bylab
		ParseByOpts , `byopts'
	        local bylgnd `"`s(bylgnd)'"'
	        local byttl `"`s(byttl)'"'
	        local byopts `"`s(byopts)'"'
		if `"`byttl'"' == "" {
			local byttl `"`title'"'
		}
		local byplotopts by(`bygroup', ///
			`byttl' `subti' `note' `bylgnd' `byopts')

	}
	else {
		if `"`byopts'"' != "" {
			di as err "byopts() requires specifying by()"
			exit 198
		}
		local twopts `title' `subti' `note' `twopts'
	}
	/* produce graphs */
	local legend legend(`lgnd' `legend')
	twoway `plotyx' || `addplot' || , ///
			`byplotopts' `yplotopts' `xplotopts' `legend' `twopts'
	if `"`outfile'"' != "" {
		_prefix_saving `outfile'
		local filename `"`s(filename)'"'
		local replace `"`s(replace)'"'
		label variable alpha "Alpha (`sides')"
		if `"`over'"' != "" {
			rename `group' over
			local groupvars over
		}
		if `"`by'"' != "" {
			rename `bygroup' by
			local groupvars `groupvars' by
		}
		order `yvar' `xvar' `groupvars' `by' `over' `keepvars'
		keep `xvar' `yvar' `by' `over' `groupvars' `keepvars'
		qui compress
		local plottest = lower(`"`plottest'"')
		local datalabel Estimated `compute' for the `plottest',
		local datalabel `datalabel' `method' method
		label data `"`datalabel'"'
		save `filename', `replace'
	}
	qui restore
end

program ChkXYcols, sclass
	version 10.0
	cap syntax [, Alpha Power Beta n n1 n2 e HR LOGHR s1 s2 p1 NRATio w ]
	if _rc == 198 {
		di as err "invalid column name in yaxis() or xaxis()"
		exit 198
	}
	else if _rc != 0 {
		exit _rc
	}
	if `"`alpha'"' != "" {
		sreturn local lab Significance level
	}
	else if `"`power'"' != "" {
		sreturn local lab Power
	}
	else if `"`beta'"' != "" {
		sreturn local lab Probability of a type II error
	}
	else if `"`n'"' != "" {
		sreturn local lab Sample size
	}
	else if `"`n1'"' != "" {
		sreturn local lab Control-group sample size
	}
	else if `"`n2'"' != "" {
		sreturn local lab Experimental-group sample size
	}
	else if `"`e'"' != "" {
		sreturn local lab Number of events
	}
	else if `"`hr'"' != "" {
		sreturn local lab Hazard ratio
	}
	else if `"`loghr'"' != "" {
		sreturn local lab Log hazard ratio
		local opt loghr
	}
	else if `"`s1'"' != "" {
		sreturn local lab Survival of the control group
	}
	else if `"`s2'"' != "" {
		sreturn local lab Survival of the experimental group
	}
	else if `"`p1'"' != "" {
		sreturn local lab Prop. of subjects (control group)
	}
	else if `"`nratio'"' != "" {
		sreturn local lab Ratio of sample sizes, N2/N1
	}
	else if `"`w'"' != "" {
		sreturn local lab Proportion of withdrawals
	}
end

program ChkExtNumlist, sclass
	version 10
	syntax [anything] [, optname(string) ]
	gettoken first rest: anything
	local k = length(`"`first'"')
	if `"`first'"' == substr("range",1,`k') {
		gettoken num1 rest: rest
		gettoken num2 rest: rest
		cap numlist `"`num1' `num2'"', min(2) max(2) sort
		if _rc {
di as err `"extended numlist: range must contain two numeric values"'
exit 198
		}
		tokenize `"`r(numlist)'"'
		local min = `1'
		local max = `2'
		if `"`rest'"' != "" {
			gettoken first np: rest
			local k = length(`"`first'"')
			if `"`first'"' == substr("npoints",1,`k') & `k'>1 {
			}
			else {
				di as err "extended numlist incorrectly specified"
				exit 198
			}
			cap confirm number `np'
			if _rc {
				di as err "extended numlist: number of points" ///
					  " must be integer greater than 1"
				exit 198
			}
			cap assert `np'>1
			if _rc {
				di as err "extended numlist: number of points" ///
					  " must be integer greater than 1"
				exit 198
			}
		}
		else { // default is 20 points
			local np = 20
		}
		// build numlist
		local step = (`max'-`min')/(`np'-1)
		if `"`optname'"' == "n" {
			local step = int(`step')
		}
		if `step'< 1e-4 {
			di as err "computed step size is too small (< 1e-4);"
			di as err "reduce number of points or increase range"
			exit 198
		}
		sreturn local nextopt `optname'(`min'(`step')`max')
	}
	else {
		cap numlist `"`anything'"'
		if _rc {
			di as err "extended numlist incorrectly specified"
			exit 198
		}
		sreturn local nextopt
	}
end

program define GetGroupLabel, sclass
	version 10.0
	syntax varlist [if]
	foreach var of local varlist {
		sum `var' `if', mean
		local ll `"`: var label `var'' = `=string(round(r(min),0.01))'"'
		local lab `"`lab'`sep'`ll'"'
		local sep "; "
	}
	sreturn local label `"`lab'"'
end

program LabelVars
	version 10.0
	syntax [varlist]
	foreach var of local varlist {
		if `"`var'"' == "hr" {
			label variable `var' "HR"
		}
		else if `"`var'"' == "loghr" {
			label variable `var' "ln(HR)"
		}
		else if `"`var'"' == "nratio" {
			label variable `var' "N2/N1"
		}
		else {
			label variable `var' `"`=proper("`var'")'"'
		}
	}
end

program GetNote, sclass
	version 10.0
	syntax [varlist] [, EXCLUDE(string) level(string) ]
	// must specify full names in by(), over(), ...
	local varlist: list varlist - exclude
	foreach var of local varlist {
		if `"`var'"' == "alpha" {
			local dilevel " (`level')"
		}
		else {
			local dilevel
		}
		sum `var' `if', mean
		if (r(min)==r(max) & r(min)!=0) {
			local ll `"`: var label `var'' = `=string(round(r(min),0.01))'"'
			local lab `"`lab'`sep'`ll'`dilevel'"'
			local sep "; "
			local tokeep `tokeep' `var'
		}
	}
	sreturn local note `"`lab'"'
	sreturn local varlist `tokeep'
end

program define ParseByOpts, sclass
	version 10
        syntax [, LEGend(passthru) TItle(passthru) MISSing total * ]

        if "`total'" != "" {
                display as error                 ///
                        "total option not allowed inside byopts()"
                exit 191
        }

        if "`missing'" != "" {
                display as error                 ///
                        "missing option not allowed inside byopts()"
                exit 191
        }

        sreturn local bylgnd `"`legend'"'
        sreturn local byttl `"`title'"'
        sreturn local byopts `"`options'"'
end