*! version 1.0.2 EMZ 11July2019
* Changed the graph titles, x and y axes labels to reflect the updates in the reviewed paper.

*! version 1.0.1 EMZ 02April2019
*	Changed Theta_opt so that if it is negative, it will be set to zero.
*	Changed the graph legend so that only theta opt is shown (not the corresponding y value)
*		and it is written out as 'optimal theta' (instead of 'θ_opt')
*	Changed y axis title to be 'Ratio of clusters required'.
*	Added in legend option (legendoptions) so that the user can change the options of the legend,
*		for example its position on the graph.

* version 1.0.0 EMZ 21feb2019

* bcss: Baseline data Cluster Sample Size
* A program to generate plots examining the impact of varying amount of prospective/retrospective baseline data collection on cluster sample size with
* different cluster autocorrelation and intra-cluster correlation values
*****************************************************************************************************************************
* Pi is the cluster autocorrelation, the correlation between the underlying cluster population means at baseline and endline
* Rho is the intra-cluster correlation (ICC)
* Total is the cluster size (n_b+n_e) when prospective data collection is selected, where n_b is the number of baseline measurements and n_e is the number of endline measurements from each cluster
* Endline is the cluster size (n_e) when retrospective data collection is selected (i.e. the baseline data of size n_b is retrospective)
* 'Theta-opt' θ_opt=(mρπ+ρ-1)/[ρm(1+π)] is the optimum proportion of baseline measurements to maximise power
* The user also has the option to specify the prospective axes (x = a PROPORTION) and the retrospective axes (x = a RATIO)
* Note from paper: "small cluster size (n_e) of 50 and large of 200, and low ICC ρ=0.01 and high ρ=0.05"
* Example commands using deafult axes ranges:
* bcss, pi(0.5 0.6 0.7) rho(0.01) pro total(200)
* bcss, pi(0.5 0.6 0.7) rho(0.01) ret endline(200)
*****************************************************************************************************************************
*****************************************************************************************************************************

capture program drop bcss
program define bcss, rclass
version 15

syntax , PIlist(numlist) Rho(real) [PROspective Total(int 0) PROPXaxis(numlist min=2 max=2) PROPYaxis(numlist min=2 max=2) PROPYStep(numlist max=1)/*
                                */ RETrospective Endline(int 0) RETXaxis(numlist min=2 max=2) RETYaxis(numlist min=2 max=2) RETYStep(numlist max=1) LEGendoptions(string)]

* Range checks
****************

* pi
numlist "`pilist'", sort
local n_pi : word count `pilist'
local minpi : word 1 of `r(numlist)'
local maxpi : word `n_pi' of `r(numlist)'

if `minpi'<0 | `maxpi'>1 & `maxpi'!=. {
	di as err "pi is out of range"
	exit 198
}

* rho
if `rho'<0 | `rho'>1 & `rho'!=. {
	di as err "rho is out of range"
	exit 198
}

* total
if `total'<0 | `total'>1000000 & `total'!=. {
	di as err "total is out of range"
	exit 198
}


* endline
if `endline'<0 | `endline'>1000000 & `endline'!=. {
	di as err "endline is out of range"
	exit 198
}


//determine what type of graph the user would like to compute

	local type = 0 			// type marks whether prospective (1) or retrospective (2) or nothing (0) was selected
	if  "`prospective'" ~= "" & "`retrospective'" ~= "" {

         di as error "Either pro *or* ret must be specified, for prospective or retrospective baseine data respectively."
	     exit 198
}
	else if "`prospective'" ~= "" {
		local type = 1
	}
	else if "`retrospective'" ~= "" {
		local type = 2
	}

// ensure that 'total' is not missing and 'endline' is blank if prospective data collection
if `type' == 1 & `total'==0 {

         di as error "Total must be specified for prospective baseine data."
	     exit 198
}

if `type' == 1 & `endline'!=0 {

         di as error "Endline must be blank for prospective baseine data."
	     exit 198
}

// ensure that 'endline' is not missing and 'total' is blank if retrospective data collection
if `type' == 2 & `endline'==0 {

         di as error "Endline must be specified for retrospective baseine data."
	     exit 198
}

if `type' == 2 & `total'!=0 {

         di as error "Total must be blank for retrospective baseine data."
	     exit 198
}

// ensure that the correct set of graph axes are selected, if the user defines them
// for the prospective baseline data graph, if the user would like to select the axes ranges, propxaxis propyaxis propystep must be used
if "`retxaxis'"~="" | "`retyaxis'"~=""  | "`retystep'"~="" local rr = 1
else local rr = 0

if `type' == 1 & `rr' == 1  {

         di as error "Axes options with prefix p (instead of r) must be selected for prospective baseine data."
         exit 198
}


// for the retrospective baseline data graph, if the user would like to select the axes ranges, retxaxis retyaxis retystep must be used
if "`propxaxis'"~="" | "`propyaxis'"~=""  | "`propystep'"~="" local pp = 1
else local pp = 0

if `type' == 2 & `pp' == 1 {

         di as error "Axes options with prefix r (instead of p) must be selected for retrospective baseine data."
         exit 198
}


// if 1+ of the propective axis options have been filled out then all of them need to be specified
if `type' == 1 & `pp' == 1  {
    local countp = 0
    foreach varp in `propxaxis' `propyaxis' `propystep' {
       if "`varp'"~="" local countp= `countp' + 0.5
	 }

 if `countp'>=1 & `countp'<2.5 {
    di as error "All prospective axis options must be specified: propxaxis() propyaxis() propystep()"
         exit 198
 }
}


// if 1+ of the retrospective axis options have been filled out then all of them need to be specified
if `type' == 2 & `rr' == 1  {
    local countr = 0
    foreach varr in `retxaxis' `retyaxis' `retystep' {
       if "`varr'"~="" local countr= `countr' + 0.5
	 }

 if `countr'>=1 & `countr'<2.5 {
    di as error "All retrospective axis options must be specified: retxaxis() retyaxis() retystep()"
         exit 198
 }
}

// Setting the graphs (for type 0 = nothing, 1 = prospective, 2 = retrospective)

if `type' == 0 {

         di as error "Either pro or ret must be specified, for prospective or retrospective baseine data respectively."
         exit 198
}

else if `type' == 1 {					// prospective


set scheme s1color

* if x and y axis ranges left blank by the user, then use defaults

if `pp' == 0 {
     local pxmin=0
	 local pxmax=0.5
	 local pymin=1
	 local pymax=1.25
	 local pystep=0.05
      }
 else {

     numlist "`propxaxis'", sort
     local pxmin : word 1 of `r(numlist)'
     local pxmax : word 2 of `r(numlist)'

	 numlist "`propyaxis'", sort
     local pymin : word 1 of `r(numlist)'
     local pymax : word 2 of `r(numlist)'

	 local pystep="`propystep'"
	 }

* check prospective x axis range is between 0 and 1
if `pxmin'<0 | `pxmax'>1 & `pxmax'!=. {
	di as err "prospective x axis is out of range; needs to be between 0 and 1"
	exit 198
}

* check both prospective x axes not 0
if `pxmin'==0 & `pxmax'==0 {
	di as err "prospective x axis is out of range"
	exit 198
}


**********************
* PROSPECTIVE GRAPH
**********************


local graph_n 1

     foreach pi of local pilist {

        local graph_nplus1 = `graph_n'+1
		* colour scheme has 15 options, so mod 15
		local graph_colour = mod(`graph_n'+11,15)

	    // make theta opt and corresponding y-value
		local theta_opt`graph_n' = ((`total' * `rho' * `pi') + `rho' -1)/((`rho' * `total')*(1 + `pi'))
		* if theta opt is negative, then set to 0 as per Andrew Copas' instructions
		if `theta_opt`graph_n''<0 local theta_opt`graph_n' = 0
		local ytheta_opt`graph_n' = (1-`rho'+(`total'*`rho'*(1-`theta_opt`graph_n'')))*(1/(1-`theta_opt`graph_n''))*(1-((`pi'*`pi'*`rho'*`rho'*`total'*`total'*`theta_opt`graph_n''*(1-`theta_opt`graph_n''))/((1+(((`total'*(1-`theta_opt`graph_n''))-1)*`rho'))*(1+(((`total'*`theta_opt`graph_n'')-1)*`rho')))))/(1+((`total'-1)*`rho'))
		local roundtheta_opt`graph_n': di %5.3f `theta_opt`graph_n''
		local roundytheta_opt`graph_n': di %5.3f `ytheta_opt`graph_n''

		// Define graph
        local call "y = (1-`rho'+(`total'*`rho'*(1-x)))*(1/(1-x))*(1-((`pi'*`pi'*`rho'*`rho'*`total'*`total'*x*(1-x))/((1+(((`total'*(1-x))-1)*`rho'))*(1+(((`total'*x)-1)*`rho')))))/(1+((`total'-1)*`rho'))"


        // Make graph
		if "`legendoptions'"=="" local legendoptions "pos(10) ring(0) forcesize symxsize(8) symysize(1) rowgap(1) size(small) colgap(1) symplacement(left) textfirst cols(1) colfirst"
        local graphcommand "function `call', range(`pxmin' `pxmax') ylabel(`pymin'(`pystep')`pymax') color("scheme p`graph_colour'") || (scatteri `roundytheta_opt`graph_n'' `roundtheta_opt`graph_n'', mcolor("scheme p`graph_colour'") msymbol(d)), legend(on label(`graph_n' "pi =`pi'") label(`graph_nplus1' "Optimal theta = `roundtheta_opt`graph_n''") `legendoptions' )"

		// overlay graphs
		local aggregate_graphcommand `aggregate_graphcommand' `graphcommand' ||

        // Make graph for next value of pi
        * local ++graph_n
		local graph_n = `graph_n'+2

		}

graph twoway `aggregate_graphcommand' , title("total cluster size=`total', ICC=`rho'") ytitle("Proportionate change in clusters required") xtitle("Baseline data as a proportion of total")

}


else if `type' == 2 {					// retrospective


set scheme s1color


* if x and y axis ranges left blank by the user, then use defaults

if `rr' == 0 {
     local rxmin=0
	 local rxmax=2
	 local rymin=0
	 local rymax=1
	 local rystep=0.1
 }
 else {

     numlist "`retxaxis'", sort
     local rxmin : word 1 of `r(numlist)'
     local rxmax : word 2 of `r(numlist)'

	 numlist "`retyaxis'", sort
     local rymin : word 1 of `r(numlist)'
     local rymax : word 2 of `r(numlist)'

	 local rystep="`retystep'"
	 }

* check retrospective x axis is >=0
if `rxmin'<0 {
	di as err "retrospective x axis is out of range; needs to be >=0"
	exit 198
}

* check both retrospective x axes not 0
if `rxmin'==0 & `rxmax'==0 {
	di as err "retrospective x axis is out of range"
	exit 198
}

************************
* RETROSPECTIVE GRAPH
************************


local graph_n 1


     foreach pi of local pilist {

		local graph_colour = mod(`graph_n'+2,15)


		// Define graph
        local call "y = (1 - ((`pi'*`pi'*`rho'*`rho'*`endline'*`endline'*x)/((1+((`endline'-1)*`rho'))*(1+((((`endline'*x))-1)*`rho')))))"


        // Make graph
		if "`legendoptions'"=="" local legendoptions "pos(7) ring(0) forcesize symxsize(8) symysize(1) rowgap(1) size(small) colgap(1) symplacement(left) textfirst cols(1) colfirst"
        local graphcommand2 "function `call', range(`rxmin' `rxmax') yscale(range(`rxmin')) ylabel(`rymin'(`rystep')`rymax') color("scheme p`graph_colour'") , legend(on label(`graph_n' "pi =`pi'") `legendoptions')"

		// overlay graphs
		local aggregate_graphcommand2 `aggregate_graphcommand2' `graphcommand2' ||

        // Make graph for next value of pi
		local graph_n = `graph_n'+1

		}

graph twoway `aggregate_graphcommand2' , title("endline cluster size=`endline', ICC=`rho'") ytitle("Proportionate change in clusters required") xtitle("Baseline data as a ratio to endline")

}



end