*! version 1.0	1.6.2026
*! Matthew Burnell


/*
DESCRIPTION
dmmixedpower is a power and sample size calculator routine for direct measures linear mixed models.
*/

program define dmmixedpower , rclass
	version 17.0
	syntax,	SCHEDule(numlist ascending >=0 max=100)	/// schedule list of time incl. baseline
			TRTSPEC(string)							/// how treatment effect specified (slope, intercept or factorised (factor, factor0))
			[   									///
			TEST(string)  							/// direct test of parameter or a lincom test
			CONTslope(string) 						/// the overall mean slope of the 'control' group
			EFFectiveness(string)  					/// proportionate effect on slope (not for other trtspec types)
			DIFFerence(string)   					/// the alternative hypothesis difference (numberlist for factor choice in trtspec)
			Alpha(real 0.05) 						/// alpha level of test
			TWOSided								/// request properly two-sided test
			POWer(string) 							/// required power
			n(string) 								/// provided sample size
			ARAtio(numlist integer >0  max=2)	/// allocation ratio of controls:treatment FORMULA VERSION */
			ALLORatio(numlist integer >0 <=10 max=2) 	///	allocation ratio of controls:treatment MULTIMATRIX VERSION - to be hidden in help file
 			DROPouts(numlist >=0 <=1))  			///	numlist values of p(making visit to k and no further) due to dropout
			DROP2(numlist >=0 <=1)					/// numlist values of p(making visit to k and no further) for Treatment Group if different to Control
			STRECruitment(numlist >=0 <=1) 			///	numlist values of p(making visit to k and no further) due to staggered recruitment
			COVariance(string) 						///	covariance matrix of random effect parameters
			ERRorvar(string) 						/// error term (or matrix for multivariate)
			AUTO 									/// speedy variance input using mixed model in memory
			///COVHET(string) 								covariance matrix of random effect parameters for treatment group if different
			SCAle(real 1)  							/// to specify schedule numlist in different scale to variance input
			NOSYNtax								/// prevent display of model syntax
			NOTABle									/// no table of counts
			NOHEADer								/// turn off header
			XMAT(numlist integer >=1 max=1)			/// return specific X matrix
			RMAT(numlist integer >=1 max=1)			/// return specific R matrix
			ZMAT(numlist integer >=1 max=1)			/// return specific Z matrix
			GMAT(numlist integer >=1 max=1)			/// return specific G matrix
			BVARN(numlist integer >=1 max=1)		/// return specific Beta covariance matrix
			FRAMES(string)							/// HIDDEN OTPION keep frames of generated X, R, Z and G matrices
			FRDROP									/// HIDDEN OTPION use to drop _mp frames from previous run of mixedpower when frames(keep) option used - might not (depending on _mp name) run otherwise unless manually dropped first.
			IVW										/// HIDDEN OTPION to use IVW instead of inverse-covariance weighting for calculating cohort-combined Betas with X-misspecification
			]




	local cframe=c(frame)
	if strmatch("`cframe'","_mp_2level*" )==1 | strmatch("`cframe'","_mp_multi*" ) ==1 | strmatch("`cframe'","_mp_direct*" )==1 | strmatch("`cframe'","_mp_3level*" )==1 {
		di as error "You are running mixedpower whilst currently active with a mixedpower derived frame. You need to first change (back) to a different frame, likely 'default'. See {help frames}, including {help frame dir} and {help frame change}"
		exit 198
	}
	else return local fr_current `cframe'

	*** see if previous command was trialcounts and return that cmdline
	local commandline `=r(cmdline)'
	gettoken command  : commandline
	if strmatch("`command'","trialcounts")==1 {
		return local trialcounts_cmd `commandline'
	}


**# required options

** Schedule list - inbuilt Stata syntax check is sufficient for parsing SCHEDule

	* Length of schedule list:
		local sched_length = 0
		foreach i of numlist `schedule' {
			local sched_length = `sched_length' + 1
			}

	* Decant the schedule numlist into locals
		tempname schedmat
		mat `schedmat'=J(1,`sched_length',0)
		local j = 1 // counter - position number
		foreach i of numlist `schedule' {
			local sched`j' = `i'
			mat `schedmat'[1,`j']=`sched`j''
			local j=`j'+1
		}


		* for inserting into prior code as model label
	local model direct





	if "`trtspec'"!="slope" {
			if "`trtspec'"!="intercept" {
				display as error "Currently only slope or intercept treatment effects allowed with dmmixedpower"
					exit 198
			}
		}


	if "`altcont'"!="" {
				display as error "Currently altcont() not allowed with dmmixedpower"
					exit 198
		}




				** And treatment specification type is chosen and correct - if not chosen then assume



				foreach trtsp in slope intercept factor factor0 slint {
					if "`trtspec'"=="`trtsp'" {
						local tscheck yes
					}
				}
				if 	"`tscheck'"=="yes" local trtspec "`trtspec'"
				else if strmatch("`trtspec'", "lateslope *") { // lateslope with time value at separation
					local lslt =subinstr("`trtspec'", "lateslope ", "", 1)
					xslopecheck, xnum(`lslt') option(trtspec) spec(lateslope #)
					local trtspec "lateslope"
				}
				else if strmatch("`trtspec'", "2slope *") { // 2slope with time value at changepoint
					local tslt =subinstr("`trtspec'", "2slope ", "", 1)
					xslopecheck, xnum(`tslt') option(trtspec) spec(2slope #)
					local trtspec "2slope"
				}
				else if strmatch("`trtspec'", "user(*)") { // user provided function
					local tuser =subinstr("`trtspec'", "user", "", 1)
					usercheck, func(`tuser') option(trtspec) xvalues(`schedule')
					local trtspec "user"
					local tuserf_n =`s(nfun)'
					local txfun_1 `s(xfun1)'
					local tfunc_1 `s(funct1)'
					if `tuserf_n'==2 {
						local txfun_2 `s(xfun2)'
						local trtspec "user2"
						local tfunc_2 `s(funct2)'
					}
				}
				else {
					display as error "trtspec() type must be one of 'slope', 'intercept', 'factor', 'factor0' (factorised including term at baseline), 'lateslope #' '2slope #', 'slint' (slope and intercept terms) or 'user(f1[; f2]"
					exit 198
				}

					/* define altcont type */
		if "`altcont'"==""	local altcont slint

		foreach contsp in noslope noint factor slint  {
			if "`altcont'"=="`contsp'" {
				local cscheck yes
			}
		}
		if 	"`cscheck'"=="yes" local altcont "`altcont'"
				else if strmatch("`altcont'", "2slope *") { // 2slope with time value at changepoint
					local Ctslt =subinstr("`altcont'", "2slope ", "", 1)
					xslopecheck, xnum(`Ctslt') option(altcont) spec(2slope #)
					local altcont "2slope"
				}
				else if strmatch("`altcont'", "user(*)") { // user provided function
					local cuser =subinstr("`altcont'", "user", "", 1)
					usercheck, func(`cuser') option(altcont) xvalues(`schedule')
					local altcont "user"
					local cuserf_n =`s(nfun)'
					local cxfun_1 `s(xfun1)'
					local cfunc_1 `s(funct1)'
					if `cuserf_n'==2 {
						local cxfun_2 `s(xfun2)'
						local altcont "user2"
						local cfunc_2 `s(funct2)'
					}

				}
				else {
					display as error "Alternative cont model - altcont() - types are one of 'noslope' (no slope i.e intercept only), 'noint' (no intercept i.e slope only), '2slope #', 'factor' or 'user(f1[; f2]. The default, not actively selected, is both slope and intercept terms"
					exit 198
				}


		** checking that if 2slope selected for both control and treatment - that changepoint is at same value of taken
		if "`trtspec'"=="2slope" & "`altcont'"=="2slope" {
				if `tslt'!=`Ctslt' {
					di as error "if '2slope' selected for both altcont() and trtpsec() then changepoint selected for both options must be the same"
					exit 198
			}
		}


	if "`actualtrt'"!="" {
		di as error "actualtrt() not allowed with dmmixedpower"
		exit 198
	}
	if "`actualcont'"!=""  {
		di as error "actualtrt() not allowed with dmmixedpower"
		exit 198
	}

					** Equivalent treatment and control checks for when actualtrt() and actualcont() specified


			/* define actualtrt type */

				foreach atrtsp in slope intercept factor factor0 slint {
					if "`actualtrt'"=="`atrtsp'" {
						local atscheck yes
					}
				}
				if 	"`atscheck'"=="yes" local actualtrt "`actualtrt'"
				else if strmatch("`actualtrt'", "lateslope *") { // lateslope with time value at separation
					local alslt =subinstr("`actualtrt'", "lateslope ", "", 1)
					xslopecheck, xnum(`alslt') option(actualtrt) spec(lateslope #)
					local actualtrt "lateslope"
				}
				else if strmatch("`actualtrt'", "2slope *") { // 2slope with time value at changepoint
					local atslt =subinstr("`actualtrt'", "2slope ", "", 1)
					xslopecheck, xnum(`atslt') option(actualtrt) spec(2slope #)
					local actualtrt "2slope"
				}
				else if strmatch("`actualtrt'", "user(*)") { // user provided function
					local atuser =subinstr("`actualtrt'", "user", "", 1)
					usercheck, func(`atuser') option(actualtrt) xvalues(`schedule')
					local actualtrt "user"
					local atuserf_n =`s(nfun)'
					local atxfun_1 `s(xfun1)'
					local atfunc_1 `s(funct1)'
					if `atuserf_n'==2 {
						local atxfun_2 `s(xfun2)'
						local actualtrt "user2"
						local atfunc_2 `s(funct2)'
					}
				}
				else if "`actualtrt'"=="" {
				}
				else {
					display as error "actualtrt() type must be one of 'slope', 'intercept', 'factor', 'factor0' (factorised including term at baseline), 'lateslope #' '2slope #', 'slint' (slope and intercept terms) or 'user(f1[; f2]"
					exit 198
				}

					/* define actualcont type */


		foreach acontsp in noslope noint factor slint  {
			if "`actualcont'"=="`acontsp'" {
				local acscheck yes
			}
		}
		if 	"`acscheck'"=="yes" local actualcont "`actualcont'"
				else if strmatch("`actualcont'", "2slope *") { // 2slope with time value at changepoint
					local aCtslt =subinstr("`actualcont'", "2slope ", "", 1)
					xslopecheck, xnum(`aCtslt') option(actualcont) spec(2slope #)
					local actualcont "2slope"
				}
					else if strmatch("`actualcont'", "user(*)") { // user provided function

					local acuser =subinstr("`actualcont'", "user", "", 1)
					usercheck, func(`acuser') option(actualcont) xvalues(`schedule')
					local actualcont "user"
					local acuserf_n =`s(nfun)'

					local acxfun_1 `s(xfun1)'
					local acfunc_1 `s(funct1)'
					if `acuserf_n'==2 {
						local acxfun_2 `s(xfun2)'
						local actualcont "user2"
						local acfunc_2 `s(funct2)'
					}
				}
				else if "`actualcont'"=="" {
				}
				else {
					display as error "Actual control model - actualcont - types are one of 'slint' (slope and intercept) 'noslope' (no slope i.e intercept only), 'noint' (no intercept i.e slope only), '2slope #', 'factor' or 'user(f1[; f2]. "
					exit 198
				}



			** Checking Test Options
	if "`trtspec'"=="slope" |  "`trtspec'"=="intercept" |  "`trtspec'"=="lateslope" | "`trtspec'"=="user"   {
		if "`test'"=="paired" {	// secret paired z-test
		}
		else if "`test'"!="" {
			dis as error  "test() is not specified when trtspec() is 'slope', 'intercept', 'lateslope' or 'user'"
					exit 198
		}
	}

	else if "`test'"!="ivw" & "`test'"!="factorslope" & "`test'"!="factorint" & strmatch("`test'","joint(*)")==0 {
				testcheck,  tsp(`trtspec') schl(`sched_length') test(`test')
				qui numlist "`test'"     // in unlikely event someone uses numlist style entry of test!
				local test=r(numlist)
			}

	if strmatch("`test'","joint(*)")==1 {
		local joint=subinstr("`test'", "joint(", "", 1)
		local joint=subinstr("`joint'", ")", "", 1)

		testcheck,  tsp(`trtspec') schl(`sched_length') test(`joint') xtra(joint())
		local jointlength : word count `joint'

			local df_jt=0
			forvalues jt=1/`jointlength' {
				local joint`jt' : word `jt' of `joint'
				 if `joint`jt''!=0 local df_jt=`df_jt'+1
			}
	}


	  ** Making sure either errorvar is specified (when auto not used)

	  if "`auto'"=="" {
		  if "`errorvar'"==""  {
			dis as error "You must specify errorvar() if not using auto option"
						exit 198
		  }
	  }

		local errmethod errmethod


				** Check either auto or (ERRorvar & COVariance) used

	if "`covariance'"!="" & "`auto'"!="" {
					dis as error "You cannot specify both covariance() (together with errorvar()) and auto"
					exit 198
				}
	if "`covariance'"=="" & "`auto'"=="" {
					dis as error "No covariance parameter specification: please specify either covariance() and errorvar() OR auto "
					exit 198
				}

				* check errorvar and auto not both specified
	if "`errorvar'"!="" & "`auto'"!="" {
					dis as error "You cannot specify both errorvar() (together with covariance()) and auto"
					exit 198
				}
				* Check  error variance included if covariance used
	if "`covariance'"!="" & "`errmethod'"=="" {
					dis as error "You must specify  errorvar() with covariance()"
					exit 198
				}
				* Check covariance included if errorvar used
	if "`covariance'"=="" & "`errorvar'"!="" {
					dis as error "You must specify covariance() with errorvar()"
					exit 198
				}

	if "`auto'"!=""  & "`e(cmd)'"!="mixed" {
		di as error "there is no mixed model fitted by {helpb mixed} in memory"
		exit 198
	}


				** Check Covariance values

				* Check COVariance is correctly specified in matrix format
		if "`covariance'"!=""  {
					tempname cm
					capture mat `cm'=  `covariance' // first checking matrix supplied in correct format
						if  _rc!=0 {
									dis as error "entry for covariance() not in matrix format"
						exit 198
								}
					capture confirm matrix `cm'  // first checking matrix supplied in correct format
					if  _rc!=0 {
									dis as error "matrix entry for covariance() not correct format"
						exit 198
								}
					}


		else if "`auto'"!="" & "`=e(redim)'"=="0" { // RE CHECK
			di as error "mixed model in memory has no random effects"
			exit 198
		}
		else if "`auto'"!="" & `=e(k_r)'==1 {  // RE CHECK
			di as error "mixed model in memory has no random effects"
			exit 198
		}


		if "`covhet'"!=""  {
			local covhet=strtrim("`covhet'")
			if strmatch("`covhet'", "auto") local chtype chauto
			else if strmatch("`covhet'", "input(*)") {  // checking the auto suboptions
				local chtype chinput
				local chmat=subinstr(`"`covhet'"', "input(", "", 1)
				local chmat=strtrim(subinstr("`chmat'", ")", "", 1))
				tempname chm
				capture mat `chm'=  `chmat' // first checking matrix supplied in correct format
					if  _rc!=0 {
								dis as error "entry for covhet() not in matrix format"
					exit 198
							}
				capture confirm matrix `chm'  // first checking matrix supplied in correct format
				if  _rc!=0 {
								dis as error "entry for covhet() not in matrix format"
					exit 198
							}
				}
			else {
				di as error "covhet() must be specified as 2mx2m symmetric matrix using input() suboption or use auto-input of variance parameters with auto suboption"
			}
		}



** CHECKING CORRECT INPUTS FOR TRTSPEC CHOICE - IF MODEL!=MULTI

* IF: trtspec=="slope" Check either used: Effectiveness + CONTSlope OR Difference

		if "`trtspec'"=="slope" {
				* check effectiveness and difference not both specified
					if "`effectiveness'"!="" & "`difference'"!="" {
					dis as error "You cannot specify both effectiveness() (together with contslope()) and difference()"
					exit 198
				}
				* check one of effectiveness and difference  specified
				if "`effectiveness'"=="" & "`difference'"=="" {
					dis as error "No target slope effect specification: please specify either effectiveness() and contslope() OR difference()"
					exit 198
				}

				* check contslope and difference not both specified
				if "`contslope'"!="" & "`difference'"!="" {
					dis as error "You cannot specify both contslope() (together with effectiveness()) and difference()"
					exit 198
				}
				* check contslope and effectiveness both specified
				if "`effectiveness'"!="" & "`contslope'"=="" {
					dis as error "You must specify both effectiveness() and contslope()"
					exit 198
				}
				* check contslope and both specified
				if "`effectiveness'"=="" & "`contslope'"!="" {
					dis as error "You must specify both effectiveness() and contslope()"
					exit 198
				}
				if 	"`effectiveness'"!="" & "`actualtrt'"!="" {
					dis as error "You cannot specify effectiveness() when also specifying actualtrt()"
					exit 198

				}
			}


		if "`trtspec'"=="intercept" | "`trtspec'"=="factor" | "`trtspec'"=="factor0" | "`trtspec'"=="slint" | "`trtspec'"=="lateslope" | "`trtspec'"=="2slope" | "`trtspec'"=="user" | "`trtspec'"=="user2" {
			if  "`difference'"=="" {
					dis as error "You must specify difference() when trtspec() is 'intercept'" //, 'factor', 'factor0', 'slint', 'lateslope', '2slope' or 'user'"
					exit 198
			}
			if  "`contslope'"!="" {
					dis as error "You must specify difference() ONLY when trtspec() is 'intercept'" //, 'factor', 'factor0', 'slint', 'lateslope', '2slope' or 'user'"
					exit 198
			}
			if  "`effectiveness'"!="" {
					dis as error "You must specify difference() ONLY when trtspec() is 'intercept'" //, 'factor', 'factor0', 'slint', 'lateslope', '2slope' or 'user'"
					exit 198
			}

		}




		* Now check effectiveness is a number >0 and <= 1  if model!=multi, then check contslope

		if "`effectiveness'"!="" { // Effectiveness is specified
				capture confirm number `effectiveness'
				if _rc!=0 {
					dis as error "Effectiveness() must be a number greater than 0 and less than or equal to 1"
					exit 198
				}
				* So eff is specified and it's a number
				if `effectiveness' > 1 {
					dis as error "Effectiveness() must be less than or equal 1"
					exit 198
				}
				if `effectiveness' <=0 {
					dis as error "Effectiveness() must be strictly greater than 0"
					exit 198
				}  // if Model!=multi



		* Now check contslope is a number - or instead variable name for slope term in mixed model in memory
			if "`contslope'"!="" { // contslope is specified
					capture confirm number `contslope'
					if _rc==0 {
						local ns_num=1
							}
					if _rc!=0 {
						local ns_num=0
						 local b_name "`contslope'"
						 tempname beta
						capture scalar `beta'=_b[`b_name'] // using the slope from mixed - doesn't always work if the variable isn't in the dataset! though can just create a fake variable  with that name...

						if "`contslope'"!="" & _rc==111 {
							dis as error "the overall (no treatment) contslope() must be a number or the name of the slope in a mixed model in memory"
							exit 198
						}

					}
				local difflength=1  // for presenting treatment effect in output

			}

		} // If statement "eff is specified"



		*** AGAIN MIGHT NEED TO GO MDIFF parsing code instead


		**Checking Difference is correctly specified for various trtspec choices or for actualtrt instead is specified

		*  check if using difference that it is a number if trtspec is slope, intercept or lateslope

		local treatsp `trtspec'
		if "`actualtrt'"!="" local treatsp `actualtrt'

		if "`treatsp'"=="slope" | "`treatsp'"=="intercept" | "`treatsp'"=="lateslope" | "`treatsp'"=="user"   {
			if "`difference'"!="" { // difference is specified
				local difflength : word count `difference'
				if `difflength'!=1     {
					dis as error  "length of supplied values in difference() option when using 'slope', 'intercept' must be one" //, 'lateslope' or 1 'user' function must be one - check if actualtrt() specified"
					exit 198
				}
				forvalues d=1/`difflength' {
					local diff`d' : word `d' of `difference'
					capture confirm number `diff`d''
						if _rc!=0 {
							dis as error "list of supplied values in difference() option must all be numbers"
							exit 198
					}

				}
			}
			local altvarX=1
		}



			* check difference  is 2 numbers if trtspec is slint (slope and intercept), user2  or 2slope
		if "`treatsp'"=="slint" | "`treatsp'"=="2slope" | "`treatsp'"=="user2"  {
			if "`difference'"!="" { // difference is specified
				local difflength : word count `difference'
				if `difflength' != 2 {
					dis as error  " list of difference() effects for 'slint', '2slope' or 2 'user' functions must be length 2 - check if actualtrt() specified"
					exit 198
				}
				forvalues d=1/`difflength' {
					local diff`d' : word `d' of `difference'
					capture confirm number `diff`d''
						if _rc!=0 {
							dis as error "all difference() elements must be specified as a number value"
							exit 198
					}

				}

			}
			local altvarX=2  // for later in picking out elements of 'test'
		}

				* check difference  is a number if trtspec is factor
		if "`treatsp'"=="factor" {
			if "`difference'"!="" { // difference is specified
				local difflength : word count `difference'
				if `difflength' != `sched_length'-1 {
					dis as error  "piecewise list of difference() for factor effects must be of length one less than schedule() - check if actualtrt() specified"
					exit 198
				}
				forvalues d=1/`difflength' {
					local diff`d' : word `d' of `difference'
					capture confirm number `diff`d''
						if _rc!=0 {
							dis as error "all difference() elements must be specified as a number value"
							exit 198
					}

				}
			}
			local altvarX=`sched_length'-1  // for later in picking out elements of 'tes
		}

				* check difference  is a number if trtspec is factor0
		if "`treatsp'"=="factor0" {
			if "`difference'"!="" { // difference is specified
				local difflength : word count  `difference'
				if `difflength' != `sched_length' {
					dis as error  "piecewise list of difference() effects in factor0 must be same length as schedule() - check if actualtrt() specified"
					exit 198
				}
				forvalues d=1/`difflength' {
					local diff`d' : word `d' of `difference'
					capture confirm number `diff`d''
						if _rc!=0 {
							dis as error "all difference() elements must be specified as a number value"
							exit 198
					}

				}

			}
			local altvarX=`sched_length'  // for later in picking out elements of 'test'
		}


		**cbeta - Control beta if actualcont() specified
		if "`cbeta'"!="" & "`actualcont'"=="" {
			di as error "cbeta() cannot be specified without actualcont()"
			exit 198
		}
		if "`actualcont'"!="" {
			if "`cbeta'"=="" {
				di as error "actualcont() cannot be specified without cbeta()"
				exit 198
			}
			local actcontlength : word count  `cbeta'
				if "`actualcont'"=="slint" {
					if `actcontlength'!=2 {
						di as error "cbeta() must be of length 2 if actualcont() is 'slint'"
						exit 198
					}
				}
				if "`actualcont'"=="noslope" {
					if `actcontlength'!=1 {
						di as error "cbeta() must be of length 1 if actualcont() is 'noslope'"
						exit 198
					}
				}
				if "`actualcont'"=="noint" {
					if `actcontlength'!=1 {
						di as error "cbeta() must be of length 1 if actualcont() is 'noint'"
						exit 198
					}
				}
				if "`actualcont'"=="factor" {
					if `actcontlength'!=`sched_length' {
						di as error "cbeta() must be equal to schedule() length if actualcont() is 'factor'"
						exit 198
					}
				}
				if "`actualcont'"=="2slope" {
					if `actcontlength'!=3 {
						di as error "cbeta() must be of length 3 if actualcont() is '2slope #'"
						exit 198
					}
				}
				if "`actualcont'"=="user" {
					if `actcontlength'!=2 {
						di as error "cbeta() must be of length 2 if actualcont() is 'user' with one function"
						exit 198
					}
				}
				if "`actualcont'"=="user2" {
					if `actcontlength'!=3 {
						di as error "cbeta() must be of length 3 if actualcont() is 'user' with two functions"
						exit 198
					}
				}
		}




* ALPHA: Should be between 0 and 1

	if `alpha'<=0 | `alpha'>1 {
					dis as error "Alpha() must be a number greater than 0 and less than 1"
					exit 198
				}

		* ALLORAtio - allocation ratio weightings MULTIMATRIX VERSION - inbuilt Stata syntax check is sufficient for parsing ALLORatio

		if "`alloratio'"!="" {
			local allratio `alloratio'
		}
		else {
			local allratio 1 1
		}
		local ar1: word 1 of `allratio' // for use in matrix creation
		local ar2: word 2 of `allratio'
		local arsum=`ar1'+`ar2' // for use later in power/SS calculations to reweight for 'number' in trial


			* ARAtio - allocation ratio Sigma adjust VERSION - inbuilt Stata syntax check is sufficient for parsing ARAtio

		if "`alloratio'"!="" & "`aratio'"!="" {
			dis as error "You are using both the ALLORatio() option and the secret ARatio() option - don't!"
							exit 198
		}

		if "`aratio'"!="" {

			local ar1: word 1 of `aratio'
			local ar2: word 2 of `aratio'
			local arsum=`ar1'+`ar2' // for use later in power/SS calculations to reweight for 'number' in trial

		}


*POWER AND N 	- TO BE CHANGED TO INCLUDE DETECTABLE DIFFERENCE??

	* Must specify one of power or N: 4 possible cases...
* 1. 1. Both power an n specified - error

	if "`power'"!="" & "`n'"!="" {
							display as error "Only one of power() and n() may be specified, not both"
							exit 198
							}

* 2. Power is specified, n is missing - okay - we'll calculate SAMPLE SIZE (N)

	if "`power'"!="" & "`n'"=="" {
							local given_power=real("`power'")
							if missing(`given_power') { // ...but power isn't a number
								dis as error "Power() must be a number greater than 0 and less than 1"
								exit 198
							}
							local power_or_n power
						}

* 3. Power is absent, n is specified - okay - we'll calculate POWER

	if "`power'"=="" & "`n'"!="" { //
							local given_n=floor(real("`n'"))
							if missing(`given_n') { // ...but n isn't a number
								dis as error "n() must be a numerical value"
								exit 198
							}
	if `given_n' != `n' { // The floor of n must be the same as n... ie n cannot be fractional
								dis as error "n() must be a whole number greater than or equal to 2"
								exit 198
							}
	if `given_n' < 2 { // N must be at least 2
								dis as error "n() must be at least 2"
								exit 198
							}
							local n = ceil(`given_n')
							local power_or_n n
						}

* 4. Both power and n absent: okay, use default value for power - we'll calculate SAMPLE SIZE (N)
	if "`power'"=="" & "`n'"=="" {
							local given_power = 0.8 // Default power is 80%
							local power_or_n power
						}
				* Check values
	if "`power_or_n'"=="power" { // Power is specified - we already know it's a number.
						if `given_power'>=1 {
							dis as error "Power() must be a value strictly less than 1"
							exit 198
						}
						if `given_power'<=0 {
							dis as error "Power() must be a value greater than 0"
							exit 198
						}
					}
					if "`power_or_n'" == "n" { // N is specified - we already know it's a number
						* Make sure it's even, or round down
						local actual_n = `arsum'* floor(`given_n' / `arsum')
						if "`model'"=="3level" & "`crt'"=="" local actual_n = `given_n'
						if `given_n'<`arsum' & "`model'"!="3level" & "`crt'"!="" {
							dis as error "n must be a number greater or equal to `arsum' - the allocation ratio sum"
							exit 198
						}
					}


* SCALE

	if `scale' <= 0 {
						dis as error "Scale() must be a positive number"
						exit 198
					}

* FRAMES

	* to keep or not
		if "`frames'"=="" {
			local frames "drop"
		}
		else if "`frames'"=="keep" {
			local frames "keep"
		}
		else  {
				display as error "only acceptable entry for frames() is 'keep'"
					exit 198
		}

* DROPPING _MP FRAMES
		if "`frdrop'"!="" {
			qui capture frame drop _mp*
		}

* DROPOUTS
	* Is there a dropout list specified?
		local drop_yes = 0
		if "`dropouts'"!="" local drop_yes = 1

	* Drop matrix must be same length as Schedule matrix
			* Length of dropout list
		if `drop_yes'==1 {
			local drop_length = 0
			foreach i of numlist `dropouts' {
				local drop_length = `drop_length ' + 1
			}
			* Is this equal to schedule matrix?
			if `sched_length' != `drop_length' {
				dis as error  "Dropouts() list must correspond in length (`drop_length') with visit schedule (`sched_length')"
				exit 198
			}
		}

	* Decant dropout numlist into locals and calculate people who attend all visits
		local dfrac_left = 1 // The proportion attending all visits - one unless dropouts specified

		if `drop_yes'==1 {
			local j = 1 // counter - position number
			foreach fr of numlist `dropouts' {
				local dfrac`j' = `fr'
				local j = `j' + 1
				local dfrac_left = `dfrac_left' - `fr'

					}
		* Check the drop matrix adds up = 1 (100%) (with tolerance allowed)
			if `dfrac_left' < -0.000001 | `dfrac_left' > 0.000001 {
				display as error  "Dropout probabilities must sum to 1: list sums to `=1-`dfrac_left''"
				exit 198
				}
			}


		tempname drops
		mat `drops'=J(1,`sched_length',0)
		if "`dropouts'"!="" {
			forvalues dr=1/`sched_length' {
				local drpo: word `dr' of `dropouts'
				mat `drops'[1,`dr']=`drpo'
			}
		}

		if "`dropouts'"=="" {
			mat `drops'[1,`sched_length']=1
		}
		 return mat dropouts=`drops'

		* DROP2 - Different Treatment dropout rate
	* Is there a 2nd dropout list specified?
		local drop2_yes = 0

		if "`drop2'"!="" local drop2_yes = 1

		if `drop2_yes'==1 & `drop_yes'==0 {
			di as error "Cannot specify drop2() if dropouts() not also selected"
			exit
		}

	* Drop2 matrix must be same length as Schedule matrix
			* Length of drop2 list
		if `drop2_yes'==1 {
			local drop2_length = 0
			foreach i of numlist `drop2' {
				local drop2_length = `drop2_length ' + 1
			}
			* Is this equal to schedule matrix?
			if `sched_length' != `drop2_length' {
				dis as error  "Drop2() list must correspond in length (`drop2_length') with visit schedule (`sched_length')"
				exit 198
			}
		}

	* Decant dropout numlist into locals and calculate people who attend all visits
		local dfrac2_left = 1 // The proportion attending all visits - one unless dropouts specified

		if `drop2_yes'==1 {
			local j = 1 // counter - position number
			foreach fr of numlist `drop2' {
				local d2frac`j' = `fr'
				local j = `j' + 1
				local dfrac2_left = `dfrac2_left' - `fr'

					}
		* Check the drop matrix adds up = 1 (100%) (with tolerance allowed)
			if `dfrac2_left' < -0.00001 | `dfrac_left' > 0.00001 {
				display as error  "Dropout probabilities must sum to 1: list sums to `=1-`dfrac2_left''"
				exit 198
				}
			}


		tempname drops drops2
		mat `drops'=J(1,`sched_length',0)
		if "`drop2'"!="" mat `drops2'=J(1,`sched_length',0)
		if "`dropouts'"!="" {
			forvalues dr=1/`sched_length' {
				local drpo: word `dr' of `dropouts'
				mat `drops'[1,`dr']=`drpo'
			}
		}
		if "`drop2'"!="" {
			forvalues dr2=1/`sched_length' {
				local drpo2: word `dr2' of `drop2'
				mat `drops2'[1,`dr2']=`drpo2'
			}
		}

		if "`dropouts'"=="" {
			mat `drops'[1,`sched_length']=1
		}
		if "`drop2'"!="" return mat dropouts2=`drops2'
		 return mat dropouts=`drops'

* STAGGERED RECRUITMENT

	* Is there a strec list specified?
		local strec_yes = 0
		if "`strecruitment'"!="" local strec_yes = 1

	* Strec numlist must same length as  Schedule numlist
		* Length of strec list
			if `strec_yes'==1 {
				local strec_length = 0
				foreach i of numlist `strecruitment' {
					local strec_length = `strec_length' + 1
				}
				* Is this equal to schedule numlist+1?
				if `sched_length' != `strec_length' {
					dis as error   "Staggered recruitment - strecruitment() - list must correspond in length (`strec_length') with visit schedule (`sched_length')"
					exit 198
				}
			}
	* Decant strec numlist into locals and calculate people who attend all visits
		local rfrac_left = 1 // The proportion attending all visits - one unless strec specified
			if `strec_yes'==1 {
				local j = 1 // counter - position number
				foreach rfr of numlist `strecruitment' {
					local rfrac`j' = `rfr'
					local j = `j' + 1
					local rfrac_left = `rfrac_left' - `rfr'
				}
		* Check the strec matrix adds up to less than 100%
			if `rfrac_left' < -0.00001 {
				display as error  "Staggered recruitment - strecruitment() - list of probabilities cannot exceed 1: list sums to `=1-`rfrac_left''"
				exit 198
			}

		}

		tempname strcs
		mat `strcs'=J(1,`sched_length',0)
       if "`strecruitment'"!="" {
			forvalues sr=1/`sched_length' {
				local strc: word `sr' of `strecruitment'
				mat `strcs'[1,`sr']=`strc'
			}
	   }
		if "`strecruitment'"=="" {
			mat `strcs'[1,`sched_length']=1
		}
		 return mat st_rec=`strcs'

** Now calculate weights integrating dropout and staggered recruitment
	if "`strecruitment'"!="" &  "`dropouts'"!="" {

		local count: word count `strecruitment'

		local finalwgts
		forval i = 1/`count' {
			local weight_`i'
			local w_inv = 1
			local w1: word `i' of `strecruitment'

			forval j = 1/`i' {
				local w2: word `j' of `dropouts'

				if `i' == `j' {
					local weight_`j' = `weight_`j'' + `w1'*`w_inv'
				}
				else {
					local weight_`j' = `weight_`j'' + `w1'*`w2'
				}

				local w_inv = `w_inv' - `w2'
			}
		}

		forvalues i=1/`count' {
			local  finalwgts "`finalwgts' `weight_`i''"
		}

	}

** Same, if drop2 used

	if "`strecruitment'"!="" &  "`drop2'"!="" {

		local count: word count `strecruitment'

		local finalwgts2
		forval i = 1/`count' {
			local weight_`i'
			local w_inv = 1
			local w1: word `i' of `strecruitment'

			forval j = 1/`i' {
				local w2: word `j' of `drop2'

				if `i' == `j' {
					local weight_`j' = `weight_`j'' + `w1'*`w_inv'
				}
				else {
					local weight_`j' = `weight_`j'' + `w1'*`w2'
				}

				local w_inv = `w_inv' - `w2'
			}
		}

		forvalues i=1/`count' {
			local  finalwgts2 "`finalwgts2' `weight_`i''"
		}

	}

** Choose which weights to use  - dropout only, strec only... or the integration of both version (above)

			local cohorts=0 // won't need separate cohorts i.e frames
	if "`strecruitment'"!="" &  "`dropouts'"!="" {
			local	pwgts "`finalwgts'"
			local cohorts=1 // will need etc.
		}
	if "`strecruitment'"=="" &  "`dropouts'"!="" {
			local	pwgts "`dropouts'"
			local cohorts=1
		}
	if "`strecruitment'"!="" &  "`dropouts'"=="" {
			local	pwgts "`strecruitment'"
			local cohorts=1
		}
	if "`strecruitment'"!="" &  "`drop2'"!="" {
			local	pwgts2 "`finalwgts2'"
		}
	if "`strecruitment'"=="" &  "`drop2'"!="" {
			local	pwgts2 "`drop2'"
		}



		tempname prwt prwt2
		local fwlist2
		local fw2list2
		local sumpwgts=0
		local sumpwgts2=0

		mat `prwt'=J(1,`sched_length',0)
		if `drop2_yes'==1 mat `prwt2'=J(1,`sched_length',0)
		if "`strecruitment'"=="" &  "`dropouts'"=="" {
			mat `prwt'[1,`sched_length']=1
			forvalues i=1/`sched_length' {
				local fw`i'=0
				if `i'==`sched_length' local fw`i'=1
				local fwlist2="`fwlist2' `fw`i''"
			}
			local pwgts "`fwlist2'"
			local sumpwgts=1
			local sumpwgts2=1
		}
		else {
			forvalues i=1/`sched_length' {
				local fw`i': word `i' of `pwgts'
				local sumpwgts=`sumpwgts'+`fw`i''
				local sumpwgts_`i'=`sumpwgts'
				if `drop2_yes'==1 {
					local fwT`i': word `i' of `pwgts2'
					local sumpwgts2=`sumpwgts2'+`fwT`i''
					local sumpwgts2_`i'=`sumpwgts2'
				}
			}
			forvalues pw=1/`sched_length' {
				local pwt: word `pw' of `pwgts'
				mat `prwt'[1,`pw']=`pwt'
				local fw`pw': word `pw' of `pwgts'
				local fw2`pw'=`fw`pw''*1/`sumpwgts'
				local fwlist2="`fwlist2' `fw2`pw''"
				if `drop2_yes'==1 {
					local pwt2: word `pw' of `pwgts2'
					mat `prwt2'[1,`pw']=`pwt2'
					local fwT`pw': word `pw' of `pwgts2'
					local fwT2`pw'=`fwT`pw''*1/`sumpwgts2'
					local fw2list2="`fw2list2' `fwT2`pw''"
				}

			}
		}

		if `drop2_yes'==0 local pwgts2 `pwgts' // will use for actual sigma rewighting, even though same as pwgts

		** `combined' weights - just to use to select non-zero cohorts

		local cwgts
		forvalues cw=1/`sched_length' {
			if  `drop2_yes'==1 local cwgt=(`fw`cw''+`fwT`cw'')/2
			else local cwgt=(`fw`cw'')
			local cwgts "`cwgts' `cwgt'"
		}



		** RETURN SOME STUFF incl. STREC and DROPOUTS, DROP" etc
		tempname crita scl dim

		if "`twosided'"!="" local tsided ts_yes
		else local tsided ts_no
		scalar `crita'=`alpha'
		scalar `scl'=`scale'
		return scalar alpha=`crita'
		return scalar scale=`scl'
		return local aratio `allratio'
		return local twosided `tsided'

		if "`actualtrt'"=="user2" return local acttrt_userfunc2 `atfunc_2'
		if "`actualtrt'"=="user" | "`actualtrt'"=="user2" return local acttrt_userfunc `atfunc_1'
		if "`actualcont'"=="user2" return local actcont_userfunc2 `acfunc_2'
		if "`actualcont'"=="user" | "`actualcont'"=="user2" return local actcont_userfunc `acfunc_1'
		return local trt_true `actualtrt'
		return local cont_true `actualcont'
		if "`trtspec'"=="user2" return local trt_userfunc2 `tfunc_2'
		if "`trtspec'"=="user" | "`trtspec'"=="user2" return local trt_userfunc `tfunc_1'
		if "`altcont'"=="user2" return local cont_userfunc2 `cfunc_2'
		if "`altcont'"=="user" | "`altcont'"=="user2" return local cont_userfunc `cfunc_1'

		return local trt_spec `trtspec'
		if "`altcont'"!="" return local cont_spec `altcont'
		else return local cont_spec slint
		if `drop2_yes'==1 return mat cohort_wgts2=`prwt2'
		return mat cohort_wgts=`prwt'

		if `drop2_yes'==1 return local fw_rescale2 `"`fw2list2'"'
		return local fw_rescale `"`fwlist2'"'
		if `drop2_yes'==1 return local final_wgts2 `"`pwgts2'"'
		return local final_wgts `"`pwgts'"'

* RETURNED MATRICES
		*XMAT

	if "`xmat'"!="" & `cohorts'==0 {
		if `xmat'!=`sched_length' {
				dis as error "with no dropout() or strec(), xmat() must contain a number equal to the schedule() length"
					exit 198
			}
	}
	if "`xmat'"!="" & `cohorts'==1 {
			if `xmat'>`sched_length' {
				dis as error "xmat() must contain a number not larger than the schedule() length"
					exit 198
			}
			forvalues xm=1/`sched_length' {
				local pw`xm': word `xm' of `pwgts'
				if `pw`xm''==0 & `xm'==`xmat' {
					dis as error "xmat() must contain a number that represents a present cohort i.e. a non-zero probability in strec() or dropout() list"
					exit 198
							}
			}
	}
			*RMAT
	if "`rmat'"!="" & `cohorts'==0 {
		if `rmat'!=`sched_length' {
				dis as error "with no dropout() or strec(), rmat() must contain a number equal to the schedule() length"
					exit 198
			}
	}
	if "`rmat'"!="" & `cohorts'==1 {
			if `rmat'>`sched_length' {
				dis as error "rmat() must contain a number not larger than the schedule() length"
					exit 198
			}
			forvalues rm=1/`sched_length' {
				local pw`rm': word `rm' of `pwgts'
				if `pw`rm''==0 & `rm'==`rmat'  {
					dis as error "rmat() must contain a number that represents a present cohort i.e. a non-zero probability in strec() or dropout() list"
					exit 198
							}
			}
	}

			*ZMAT
	if "`zmat'"!="" & "`marginal'"!=""	{
		dis as error "cannot request zmat() if model is marginal"
					exit 198
	}
	if "`zmat'"!="" & `cohorts'==0 {
		if `zmat'!=`sched_length' {
				dis as error "with no dropout() or strec(), zmat() must contain a number equal to the schedule() length"
					exit 198
			}
	}
	if "`zmat'"!="" & `cohorts'==1 {
			if `zmat'>`sched_length' {
				dis as error "zmat() must contain a number not larger than the schedule() length"
					exit 198
			}
			forvalues zm=1/`sched_length' {
				local pw`zm': word `zm' of `pwgts'
				if `pw`zm''==0  & `zm'==`zmat' {
					dis as error "zmat() must contain a number that represents a present cohort i.e. a non-zero probability in strec() or dropout() list"
					exit 198
							}
			}
	}

			*GMAT
	if "`gmat'"!="" & "`marginal'"!=""	{
		dis as error "cannot request gmat() if model is marginal"
					exit 198
	}
	if "`gmat'"!="" & `cohorts'==0 {
		if `gmat'!=`sched_length' {
				dis as error "with no dropout() or strec(), gmat() must contain a number equal to the schedule() length"
					exit 198
			}
	}
	if "`gmat'"!="" & `cohorts'==1 {
			if `gmat'>`sched_length' {
				dis as error "gmat() must contain a number not larger than the schedule() length"
					exit 198
			}
			forvalues gm=1/`sched_length' {
				local pw`gm': word `gm' of `pwgts'
				if `pw`gm''==0 & `gm'==`gmat'  {
					dis as error "gmat() must contain a number that represents a present cohort i.e. a non-zero probability in strec() or dropout() list"
					exit 198
							}
			}
	}

				*BVARN

	if "`bvarn'"!="" & `cohorts'==0   {
		if `bvarn'<`sched_length' {
				dis as error "with no dropout() or strec(), bvarn() must be the number equal to the schedule() length"
					exit 198
		}
	}
	if "`bvarn'"!="" & `cohorts'==1 {
			if `bvarn'>`sched_length' {
				dis as error "bvarn() must contain a number not larger than the schedule() length"
					exit 198
			}
			forvalues bv=1/`sched_length' {
				local pw`bv': word `bv' of `pwgts'
				if `pw`bv''==0 & `bv'==`bvarn'  {
					dis as error "bvarn() must contain a number that represents a present cohort i.e. a non-zero probability in strec() or dropout() list"
					exit 198
							}
			}
	}





********************************************************************************
**# DIRECT MODEL CALCULATIONS
********************************************************************************


** firstly, for 'direct' model need three timepoints, so:

	if `sched_length'<=1 {
			dis as error "for 'direct' measures model - model(direct) - schedule() length must be at least 2"
				exit 198
	}

** Check Covariance values

* Check user-inputted COVariance is a 2x2 symmetric matrix assuming user/matrix input
  * will maybe add in more complex variance matrices later - for now assumed equal random variances for each pair ("identity" matrix) so one variance specified and one slope variance with no covariance
	if "`covariance'"!=""  {
		tempname cov_mat
		mat `cov_mat'=`covariance'
		local col=colsof(`cov_mat')
		local sym= issymmetric(`cov_mat')

		if `sym' ==0 | `col'!=2 {
						dis as error "random effect matrix must be symmetric 2x 2"
			exit 198
					}
		}

*Check user-inputted random effect variances >0

	if "`covariance'"!="" { // version for if numbers entered directly as matrix
			local intvar= el(`cov_mat', 1, 1)
								if `intvar' < 0 {
				dis as error "random intercept variance must be a positive number, unless intentially zero (random slope model only or standard linear regression if slope variance also =0)"
				exit 198
			}

			local slvar= el(`cov_mat', 2, 2)
								if `slvar' <0 {
				dis as error "random slope variance must be a positive number, unless intentially zero (random intercept model only or standard linear regression if intercept variance also =0)"
				exit 198
			}

	}

*Check user-inputted COVariance has zero correlation
	 if "`covariance'"!=""  {
	 	tempname corr_mat
		local cov12=el(`cov_mat', 1,2)
		if `cov12'!=0 {
			mat `corr_mat'=corr(`cov_mat')
			local corr= el(`corr_mat', 1, 2)
			if `corr' !=0 {
				dis as error "correlation of random effects not allowed"
				exit 198
			}
		}
	 }

	/*
*Check user-inputted COVariance has implied correlation <=1 and >=-1 - WON@T ALLOW LATER
	 if "`covariance'"!=""  {
	 	tempname corr_mat
		local cov12=el(`cov_mat', 1,2)
		if `cov12'!=0 {
			mat `corr_mat'=corr(`cov_mat')
			local corr= el(`corr_mat', 1, 2)
			if `corr' >1 {
				dis as error "correlation of random effects is greater than 1 - check values"
				exit 198
			}
			if `corr' <-1 {
				dis as error "correlation of random effects is less than -1 - check values"
				exit 198
			}
		}
	 }
*/


** Rescale the variance terms that need rescaling (random intercept variance doesn't) - scale==1 if unspecified i.e no rescaling

* user-inputted version
	if "`covariance'"!="" {
		local var_slope=(`scale'*(sqrt(`slvar')))^2 // Variance of time (random slopes)  - rescaled if necessary

		local  var_int=`intvar' // Variance of ALL pairwise contrast (`direct') measures
		local corr_slopeint=`cov12'/(sqrt(`slvar')*sqrt(`intvar')) // correlation of random effects  - SHOULD BE ZERO
		local cov_slopeint=`corr_slopeint'*sqrt(`var_int')*sqrt(`var_slope')  // rescaled covariance, if necessary

		local covmat `var_int' `cov_slopeint' `cov_slopeint' `var_slope' // rescaled (if required) covariance matrix in numlist form

	}

* 'auto' auto-version

	if "`auto'"!="" { // version for if using mixed model in memory


			local var_slope=(`scale'*(exp(_b[lns1_2_1:_cons])))^2 // Variance of slope  - rescaled if necessary
			local var_int=(exp(_b[lns1_1_1:_cons]))^2 // Variance of ALL pairwise contrast (`direct') measures   - not need for rescaling

			local  cov_slopeint=0 // covariance of random effects is zero


		local covmat `var_int' `cov_slopeint' `cov_slopeint' `var_slope' // rescaled (if required) covariance matrix in numlist form
	}



** Error variances check

* user-inputted version
	if "`errorvar'"!="" { // version for if numbers entered directly
		capture confirm number `errorvar'
				if _rc!=0 {
					dis as error "errorvar() must be a number greater than 0"
					exit 198
				}
		if `errorvar' <-1 {
			dis as error "errorvar() must be a number greater than 0"
			exit 198
		}
		local var_res=`errorvar'
	}

	* 'auto' auto-version
	if "`auto'"!="" { // version for if using mixed model in memory
		local var_res=(exp(_b[lnsig_e:_cons]))^2
	}


** Rescale contslope - changes with scale, so an x% effect means something different (i.e rescale TTE)

	if "`contslope'"!="" {
		if `ns_num'==1 { // version if slope specified as number
			local ns_sc=`contslope'*`scale'
			local tte =  -(`ns_sc') * `effectiveness'
		}
		if `ns_num'==0 { // version if slope value taken from mixed model slope (`beta')
			local ns_sc=`beta'*`scale'
			local tte = -(`ns_sc') * `effectiveness'
		}
	}


** Rescale Slope Difference parameter - only matters if trtspec=slope because it's an interaction parameter
	if "`difference'"!="" & "`trtspec'"=="slope" {
		local diff_sc=`difference'*`scale'
		local tte=(`diff_sc')
		}

	* if trtspec=intercept
	if "`difference'"!="" & "`trtspec'"=="intercept" {
		local diff_sc=`difference'
		local tte=(`diff_sc')
		}
	/* let's ignore for now
	* if trtspec=factor
	if "`difference'"!="" & "`trtspec'"=="factor" {
		// needs filling
		}
*/

**** HEADER

	if "`noheader'"=="" {

		di _n "{it:dmmixedpower}" as text " - power and sample size calculator for direct measures linear mixed models (version 1.0): author M.Burnell"
		di as text "MRC Centre of Research Excellence in Clinical Trial Innovation, UCL, London WC1V 6LJ, UK" _n
	}



********** MODEL SYNTAX OUTPUT SECTION *****************************************

	if "`nosyntax'"=="" {
		di  "{hline}"
		local rilist
		if "`trtspec'"=="slope" local trtsyn  c.{it:time_diff}#1.{it:trt}
		if "`trtspec'"=="intercept" local trtsyn  i.{it:trt}
		forvalues v=1/`sched_length' {
			local rilist "`rilist' {it:itime_`v'}"
		}
		di as text  "Mixed model syntax:"
		di  as res "mixed{it: depvar} c.{it:time_diff} `trtsyn', nocons || {it:id_level2}: `rilist',  covariance(identity) noconstant collinear || {it:id_level2}: {it:time_diff}, noconstant collinear "
	}

***** MATRIX SECTIONS *****

* do we need to consider weighting of different schedule cohorts i.e. create multiple frames?

	if "`strecruitment'"=="" & "`dropouts'"=="" {
		local mframes=1  // either going to make just one frame - no pwgt cohorts
	}
	else {
		local mframes=`sched_length'  // ... or multiple frames (not necessarily sched_length amount, if any pwgts==0)
	}


* create new frame(s) with matrix contents and calculate variance of treatment effects

	local cframe=c(frame)

	local frlist // for creating numlist of frames to include when cohort==1 in following loop

	foreach n of numlist 1/`mframes' { // create a frame for each schedule cohort if necessary. Not going to use tempnames or tempdata as frames are new and will be dropped by program unless use frames(keep)

		local pwgts_`n': word `n' of `cwgts' // not needed anymore?...
		local pwgt0_`n': word `n' of `pwgts'
		local pwgt1_`n': word `n' of `pwgts2'
		if `cohorts'==0 {
			local pwgts_1=1 // not needed anymore?...
			local pwgt0_`n'=1
			local pwgt1_`n'=1
		}
		if `cohorts'==1 & `n'<=1  {
			local pwgts_`n'=0 //
		}

		if `pwgts_`n''>0  {
			if `cohorts'==1 & `n'>1  {
				local frlist "`frlist' `n'"
			}
**# Direct Model - X matrix and identifier variables plus intitial frame(s) set up
		 if "`frames'"=="keep"	{
		 	tempname _mp_`model'_`trtspec'_coh`cohorts'_`n'
			frame create `_mp_`model'_`trtspec'_coh`cohorts'_`n''
			frame change `_mp_`model'_`trtspec'_coh`cohorts'_`n''
			notes: mixedpower dataframe
			notes: model type: `model'
			notes: treatment specification: `trtspec'
			notes: cohort (0=represents full sample; 1=represents partial sample) : `cohorts'
			if `cohorts'==1 {
				notes: cohort schedule: up to visit `n'
			}
			if `cohorts'==0 {
				notes: cohort schedule: up to visit `sched_length' only
			}
			notes: probability weight of cohort: `pwgts_`n''
		 }
		 if "`frames'"=="drop" & "`fr_created'"!="yes" {
				tempname _mp_`model'_`trtspec'
				frame create `_mp_`model'_`trtspec''
				frame change `_mp_`model'_`trtspec''
				local fr_created yes
			}


	qui {

		local trwt1: word 1 of `allratio'
		local trwt2: word 2 of `allratio'
		local obsn=`sched_length'*(`trwt1'+`trwt2')

		set obs `=`obsn'+100'

		gen trt_cohort=.
		gen obs=.
		gen str time=""""
		gen str pairing=""""
		gen treat=.
		local vlim=2 // 2 X variables needed for 'slope' and 'trt slope'
			forvalues v=1/`vlim' {
					gen double X`v'=.
			}
	}
		local in=0 // local counter for inputting matrix values in appropriate row
		local trt_coh=0	// dataset marker identifying unique trt#cohorts based on ARAtio arguments
		local trt=-1 // identify trt and control groups (will be 0/1 values when inputted)
		local q=`n' // number of visits from schedule to use for this (nth) frame
		local arat=0 // for picking which ARatio weight to use
		if `cohorts'==0 {
			local q=`sched_length' // no dropout/strec then will use all visits
		}

		forvalues g=0/1 {  // loop over treatment
			local trt=`trt'+1
			local arat=`arat'+1
			local trwt: word `arat' of `allratio'
			forvalues ab=1/`trwt' { // loop over trt#cohort weights
				local trt_coh=`trt_coh'+1
				forvalues j=1/`q'	{ // loop over visits upto `n'
					forvalues k=`=`j'+1'/`q'	{

						local tj : word `j' of `schedule'
						local tk : word `k' of `schedule'
						local in=`in'+1
						qui replace X1=`tk'-`tj' in `in'
						if "`trtspec'"=="slope" {
							qui replace X2=(`tk'-`tj')*`g' in `in'
						}
						if "`trtspec'"=="intercept" {
							qui replace X2=1*`g' in `in'
						}
						qui replace trt_cohort=`trt_coh' in `in'
						qui replace obs=`j' in `in'
						qui replace time="`tk'-`tj'" in `in'
						qui replace pairing="`k'-`j'" in `in'
						qui replace treat=`trt' in `in'
						}
					}
				}


		}


		local in=`in'+1
		foreach var of varlist trt_cohort obs treat  X1-X2   {
				qui replace `var'=. in `in'/l
			}

			qui tostring treat, gen(trt_str)
			qui tostring trt_cohort, gen(ar_str)
			qui tostring obs, gen(vis_str)

			qui gen str15 rownames="t"+trt_str+"_"+"v"+pairing
			qui replace rownames="" if treat==.
			qui replace trt_str="" if treat==.
			qui replace ar_str="" if treat==.
			qui replace vis_str="" if treat==.



		tempname X	Xm
		mkmat X1-X2, matrix(`X') nomissing rownames(rownames)

		if "`xmat'"!="" {
			if `xmat'==`n' & `cohorts'==1 {
				mat `Xm'=`X'
				return matrix xmat=`Xm'
			}
			if `cohorts'==0 {
				mat `Xm'=`X'
				return matrix xmat=`Xm'
			}
		}


	**# Direct R matrix
		local arat=0
		local in=0
		forvalues g=0/1 { // loop over treatment
			local arat=`arat'+1
			local trwt: word `arat' of `allratio'

			forvalues ab=1/`trwt' { // loop over treatment#cohort weights
				forvalues j=1/`q'	{	// loop over visits upto `n'
					forvalues k=`=`j'+1'/`q'	{
					local in=`in'+1
					qui gen double R`in'=0
					qui replace R`in'=`var_res' in `in'
					}
				}
			}
		}

		local inl=`in'+1
		foreach var of varlist R1-R`in' {
			qui replace `var'=. in `inl'/l
		}

		tempname R Rm
		mkmat R1-R`in', matrix(`R') nomissing	rownames(rownames)

		if "`rmat'"!="" {
			if `rmat'==`n' & `cohorts'==1 {
				mat `Rm'=`R'
				return matrix rmat=`Rm'
			}
			if `cohorts'==0 {
				mat `Rm'=`R'
				return matrix rmat=`Rm'
			}
		}


	**# Direct Z matrix


		local in=0
		local nct=0
		local arat=0

		forvalues g=0/1 {

			local arat=`arat'+1
			local trwt: word `arat' of `allratio'
			forvalues ab=1/`trwt' {

				local nct=`nct'+1
				forvalues zn=1/`q' {
					local z`zn'=(`nct'-1)*(`q'+1)+`zn'
					local zzz`ab'`zn'=`z`zn'' // trying to label the right Z column for later, and doesn't get altered after loops thru ab
					gen double Z`z`zn''=0
					if `zn'==`q' {
					}
				}
				local zz2=(`nct'-1)*(`q'+1)+`q'+1
				gen double Z`zz2'=0

				forvalues j=1/`q'{
					forvalues k=`=`j'+1'/`q'	{
					local in=`in'+1
					local tj : word `j' of `schedule'
					local tk : word `k' of `schedule'
					qui replace Z`zzz`ab'`j''=-1 in `in'  // enter the negative contrast part for j
					qui replace Z`zzz`ab'`k''=1 in `in'  // enter the positive contrast part for k
					qui replace Z`zz2'=`tk'-`tj' in `in' // this one for random slope
					}

				}
			}
		}

		local inl=`in'+1
		foreach var of varlist Z1-Z`zz2' {
			qui replace `var'=. in `inl'/l
		}
		tempname Z Zm
		mkmat Z1-Z`zz2', matrix(`Z') nomissing	rownames(rownames)

		if "`zmat'"!="" {
			if `zmat'==`n' & `cohorts'==1 {
				mat `Zm'=`Z'
				return matrix zmat=`Zm'
			}
			if `cohorts'==0 {
				mat `Zm'=`Z'
				return matrix zmat=`Zm'
			}
		}

	**# Direct G matrix


		local arat=0
		local in=0

		forvalues g=0/1 {

			local arat=`arat'+1
			local trwt: word `arat' of `allratio'
			forvalues ab=1/`trwt' {

				local in=`in'+1
				forvalues gn=1/`q' {
				local g`gn'=`gn'+(`in'-1)*(`q'+1)
				qui gen double G`g`gn''=0
				}
				local g1=1+(`in'-1)*(`q'+1)
				 // for entering the cov parameters in pairs per row
				local gg2=`q'+1+(`in'-1)*(`q'+1)
				qui gen double G`gg2'=0

				local cell=0
				local rmax=`=`q'+1' // cov matrix is q+1xq+1 version
				local cmax=`=`q'+1'

				forvalues r=1/`rmax' {
					forvalues c=1/`cmax' {
						if `r'==`c' & `r'!=`rmax' {
							local value: word 1 of `covmat'
						}
						if `r'==`c' & `r'==`rmax' {
							local value: word 4 of `covmat'
						}
						if `r'!=`c'  {
							local value: word 2 of `covmat'
						}
						local in`r'`c'=(`g1')+(`r'-1)
						local gc=`g1'+(`c'-1)
						 qui replace G`gc'=`value' in `in`r'`c''
					}
				}

			}
		}


		local inl=`gg2'+1
		foreach var of varlist G1-G`gg2' {
			qui replace `var'=. in `inl'/l
		}
		tempname G Gm
		mkmat G1-G`gg2', matrix(`G') nomissing

		if "`gmat'"!="" {
			if `gmat'==`n' & `cohorts'==1 {
				mat `Gm'=`G'
				return matrix gmat=`Gm'
			}
			if `cohorts'==0 {
				mat `Gm'=`G'
				return matrix gmat=`Gm'
			}
		}

		tempname sigma sigma0 B_var sigmaC sigmaT
		mat `sigma'=`R'+`Z'*`G'*`Z''
		mat `sigma0'=`sigma'

		*** ARATIO section ***
		local k=(`q'*(`q'-1))/2

			if "`alloratio'"=="" {

				if `pwgt0_`n''>0 mat `sigmaC'=(1/`ar1')*(1/`pwgt0_`n'')*`sigma'[1..rowsof(`sigma'),1..`k']
				if `pwgt0_`n''==0 mat `sigmaC'=(1/`ar1')*(0)*`sigma'[1..rowsof(`sigma'),1..`k']

				if `pwgt1_`n''>0 mat `sigmaT'=(1/`ar2')*(1/`pwgt1_`n'')*`sigma'[1..rowsof(`sigma'),`=1+`k''..`=2*`k'']
				if `pwgt1_`n''==0 mat `sigmaT'=(1/`ar2')*(0)*`sigma'[1..rowsof(`sigma'),`=1+`k''..`=2*`k'']

				mat coljoin `sigma'=`sigmaC' `sigmaT'
				*mat li `sigma'
			}

		mat `B_var'=invsym(`X''*invsym(`sigma')*`X')

		if `cohorts'==1 {
			tempname B_var`n' sigma`n'
			mat `B_var`n''=`B_var'
			mat `sigma`n''=`sigma0'
		}

		tempname bvarin sigmain corrin
			if "`bvarn'"!=""  {
					if `cohorts'==1 & `bvarn'==`n' {
						mat `sigmain'=`sigma`n''
						mat `corrin'=corr(`sigma`n'')
						mat `bvarin'=`B_var`n''
						return matrix bvar_n=`bvarin'
						return matrix sigma_n=`sigmain'
						return matrix wcorr_n=`corrin'
					}
					if `cohorts'==0 & `bvarn'==`sched_length' {
						mat `corrin'=corr(`sigma0')
						mat `sigmain'=`sigma0'
						mat `bvarin'=`B_var'
						return matrix bvar_n=`bvarin'
						return matrix sigma_n=`sigmain'
						return matrix wcorr_n=`corrin'
					}
				}

			tempname nmcount

			qui egen `nmcount' = rownonmiss(_all), strok
			qui drop if `nmcount' == 0
			qui drop `nmcount'


			if "`frames'"=="drop"  {
					drop _all
				}
			if "`frames'"=="keep"  {
				frame rename `_mp_`model'_`trtspec'_coh`cohorts'_`n'' _mp_`model'_`trtspec'_coh`cohorts'_`n'
			}
		}
		else

	}

	***** Perform  back-calculation of X-covariance if needed *****
	if `cohorts'==1 {
		tempname B_var  // the overall back-calculated X-covariance matrix
		tempname wgtsum
		mat `wgtsum'=J(`vlim',`vlim',0)
		foreach n of numlist `frlist' {

			mat `wgtsum'=`wgtsum'+invsym(`B_var`n'')
		}
		mat `B_var'=invsym(`wgtsum')


	}

	tempname bvar
	mat `bvar'=`B_var'
	return mat beta_var=`bvar'

** Calculate effect size  - either for full design matrix (frame 1) or combined from schedule cohorts (frames 2 to n)
		local b_var=`B_var'[2,2]
		local effsize=`tte'/sqrt(`b_var')




	* CALCULATE THE MAIN RESULT: Either sample size or power
	qui tempname  sampfactor alpha_tail sampsize sampsize_C sampsize_T power z_power frac_ss frac_power
	if "`power_or_n'"=="power" { // Calc SAMPLE SIZE
		scalar `alpha_tail' = 1 - (0.5 * `alpha')
		scalar `sampfactor' = ( invnormal(`alpha_tail') + invnormal(`given_power'))^2
		scalar `sampsize' = 2*ceil( ( `sampfactor' / (`effsize')^2) )*(`arsum'/2)
		scalar `frac_ss' = 2*(`sampfactor' / (`effsize')^2)*(`arsum'/2)
		/*if  "`aratio'"!="" {
			scalar `sampsize' = `arsum'*ceil( (`arfactor'*2)*(`sampfactor' / ((`effsize')^2)/`arsum'))
			scalar `frac_ss' = 2*( (`arfactor')*(`sampfactor' / (`effsize')^2))
		}*/
		scalar `sampsize_C' =  `sampsize'*(`ar1'/`arsum')
		scalar `sampsize_T' =  `sampsize'*(`ar2'/`arsum')
		scalar `power' = normal(( abs(`effsize')*sqrt(`sampsize'/`arsum') ) - invnormal(1-`alpha'/2))
		scalar `frac_power' = `given_power'
	}
	else { // Calc POWER
		// Calc the power here
		scalar `sampsize' = `actual_n' // As specified by the user;
		scalar `frac_ss' = `given_n'
		scalar `sampsize_C' = `actual_n'*(`ar1'/`arsum')
		scalar `sampsize_T' = `actual_n'*(`ar2'/`arsum')
		scalar `z_power' = ( abs(`effsize')*sqrt(`actual_n'/`arsum') ) - invnormal(1-`alpha'/2) //
		scalar `frac_power' = normal(( abs(`effsize')*sqrt(`given_n'/`arsum') ) - invnormal(1-`alpha'/2))
		/*if  "`aratio'"!="" {
			scalar `z_power' = ( abs(`effsize')*sqrt(`actual_n'/(`arfactor'*2)) ) - invnormal(1-`alpha'/2)
			scalar `frac_power' = normal((abs(`effsize')*sqrt(`given_n'/(`arfactor'*2)) ) - invnormal(1-`alpha'/2))
		}*/
		scalar `power' = normal(`z_power')
	} // (((delta[`i']*`slope')*sqrt(`size')/`B_var1')-invnormal(1-`alpha'))



	if strmatch("`test'","joint(*)")==1 | "`twosided'"!="" {
		if "`twosided'"!="" {
			tempname W_jt
			mat `W_jt'=J(1,1,0)
			local df_jt=1
			local w11=`effsize'*`effsize'
			mat `W_jt'[1,1]=`w11'
			local star *
		}
		local alpha_jt= invchi2tail(`df_jt', `alpha')
		if "`power_or_n'"=="power" {
			scalar `sampsize'=`arsum'*ceil(npnchi2(`df_jt',`alpha_jt',`=1-`given_power'')/`W_jt'[1,1]-.000001) // might need a bit of tolerance adjustment!
			scalar `frac_ss' = `arsum'*(npnchi2(`df_jt',`alpha_jt',`=1-`given_power'')/`W_jt'[1,1]-.000001)
			scalar `sampsize_C' =  ceil(`sampsize'*(`ar1'/`arsum'))
			scalar `sampsize_T' =  ceil(`sampsize'*(`ar2'/`arsum'))
			scalar `power' = 1-nchi2(`df_jt',`W_jt'[1,1]*`sampsize'/`arsum',`alpha_jt')
			scalar `frac_power' = `given_power'
		}
		else {
			scalar `power'=1-nchi2(`df_jt',`W_jt'[1,1]*`actual_n'/`arsum',`alpha_jt')
			scalar `frac_power'=1-nchi2(`df_jt',`W_jt'[1,1]*`given_n'/`arsum',`alpha_jt')
			scalar `sampsize' = `actual_n'
			scalar `frac_ss' = `given_n'
			scalar `sampsize_C' = `actual_n'*(`ar1'/`arsum')
			scalar `sampsize_T' = `actual_n'*(`ar2'/`arsum')
		}
	}


***** DISPLAY OUTPUT SECTION ****************

			local schedrd
			forvalues t=1/`sched_length' {
				local schrd_`t': word `t' of `schedule'
				if mod(`schrd_`t'',0.1)!=0 local schrd_`t': di %3.2f `schrd_`t''
				else if mod(`schrd_`t'',1)!=0 local schrd_`t': di %2.1f `schrd_`t''
				local schedrd "`schedrd' `schrd_`t''"
			}

			local diffrd
			forvalues d=1/`difflength' {
				local diffrd_`d': word `d' of `tte'
				if mod(`diffrd_`d'',0.1)!=0 local diffrd_`d': di %3.2f `diffrd_`d''
				else if mod(`diffrd_`d'',1)!=0 local diffrd_`d': di %2.1f `diffrd_`d''
				local diffrd "`diffrd' `diffrd_`d''"
			}

			if "`actualtrt'"!="" local attext " {it:but with actual} {hi:`actualtrt'} {it:treatment effect}"
			local offset 25
			di  "{hline}"
			if "`power_or_n'"=="n" local calc_PorN power
			if "`power_or_n'"=="power" local calc_PorN sample size
			di _n as text "Calculating {hi:`calc_PorN'} for a {hi:direct measures} model with {hi:`trtspec'} treatment effect parameterisation`attext':"
			di _s(2) as text "visit schedule" _col(`=`offset'-2') "="  _col(`=`offset'-1') as res "`schedrd'"
			di _s(2) as text "treatment effect(s)" _col(`=`offset'-2') "="  _col(`=`offset'-1') as res "`diffrd'"
			di _s(2) as text "alpha`star'" _col(`=`offset'-2') "="  _col(`offset') as res %4.3f `alpha'
			di _s(2) as text "total sample size"  _col(`=`offset'-2') "="  _col(`offset') as res `sampsize'
			di _s(2) as text "n in control arm"  _col(`=`offset'-2') "="  _col(`offset') as res `sampsize_C'
			di _s(2) as text "n in treatment arm"  _col(`=`offset'-2') "="  _col(`offset') as res `sampsize_T'
			di _s(2) as text "power"  _col(`=`offset'-2') "="  _col(`offset') as res %5.4f  `power'


			if "`strecruitment'"!="" local table yes
			if "`dropouts'"!="" local table yes


			*if "`table'"=="yes" {

					local Clist
					local Tlist
					local headlist1
					local headlist2
					local cwidth=9
					if `sched_length'>12	local cwidth=6
					tempname CmatN TmatN CmatrdN TmatrdN
					foreach mat in `CmatN' `TmatN' `CmatrdN' `TmatrdN' {
						mat `mat'=J(1,`sched_length',0)
					}
					forvalues i=1/`sched_length' {
						local start=15
						if `i'==1 local sumCN_`i'=`sumpwgts'*`sampsize_C'
						local pwgts_`i': word `i' of `pwgts'

						if `i'>1 local sumCN_`i'=`sumCN_`=`i'-1'' -`pwgts_`=`i'-1''*`sampsize_C'
						local sumCNrd_`i': di %-5.0f abs(`sumCN_`i'')
						local pwgts2_`i': word `i' of `pwgts2'
						if `i'==1 & "`drop2'"!="" local sumTN_`i'=`sumpwgts2'*`sampsize_T'

						if `i'==1 & "`drop2'"=="" local sumTN_`i'=`sumpwgts'*`sampsize_T'

						if `i'>1 local sumTN_`i'=`sumTN_`=`i'-1'' -`pwgts2_`=`i'-1''*`sampsize_T'
						local sumTNrd_`i': di %-5.0f abs(`sumTN_`i'')
						local hdi`i' "visit `i'"
						if `sched_length'>12 local hdi`i' "v_`i'"
						local headlist1 "`headlist1' " _col(`=`start'+`cwidth'*(`i'-1)') "`hdi`i''"

						local h`i': word `i' of `schedule'
						local hd2i`i' "time=`h`i''"
						if `sched_length'>12 local hd2i`i' "t=`i'"
						local headlist2 "`headlist2' " 	_col(`=`start'+`cwidth'*(`i'-1)') "`hd2i`i''"
						local Clist "`Clist' "  _col(`=`start'+`cwidth'*(`i'-1)') "`sumCNrd_`i''"
						local Tlist "`Tlist' " _col(`=`start'+`cwidth'*(`i'-1)') "`sumTNrd_`i''"
						mat `CmatN'[1,`i']=`sumCN_`i''
						mat `TmatN'[1,`i']=`sumTN_`i''
						mat `CmatrdN'[1,`i']=`sumCNrd_`i''
						mat `TmatrdN'[1,`i']=`sumCNrd_`i''

					}
				*}


	if "`notable'"=="" {
		if "`strecruitment'"!="" local table yes
		if "`dropouts'"!="" local table yes

		if `sched_length'<=20 {
			if "`table'"=="yes" {
				local line =12
				di _n as text "Table of control and treatment group numbers (rounded) reaching each visit:"
					di  _col(`line') "{c |}"	"`headlist1'"
					di  _col(`line') "{c |}"	"`headlist2'"
					di  _dup(`=`line'-1') "{c -}" _col(`line') "{c +}"	_dup(`=`cwidth'*`=`sched_length'+1'-5') "{c -}"
					di  "control " _col(`line') "{c |}" "`Clist'"
					di  "treatment " _col(`line') "{c |}" "`Tlist'"
				}
			}
	}

	** back to returns

	return matrix trtrd_num=`TmatrdN'
	return matrix contrd_num=`CmatrdN'
	return matrix trt_num=`TmatN'
	return matrix cont_num=`CmatN'
	return matrix sched_list=`schedmat'

	tempname  var_eff eff teff setrial

	scalar `setrial'=sqrt(`b_var'/(`sampsize'/`arsum'))
	return scalar se_trial=`setrial'
	scalar `var_eff'=`b_var'
	return scalar var_trteff=`var_eff'
	scalar `teff'=`tte'
	return scalar trt_eff=`teff'
	scalar `eff'=`effsize'
	return scalar effectsize=`eff'
	return scalar power=`power'
	return scalar fractional_power=`frac_power'
	return scalar samplesize=`sampsize'
	return scalar fractional_ss=`frac_ss'
	return local final_wgts `"`pwgts'"'
	return local schedule "`schedule'"

	return local cmd mixedpower
	return local cmdline `"mixedpower `0'"'


	frame change `cframe'
	/*
	if "`frames'"=="drop" {
		frame drop _mp*
	}
	*/


********************************************************************************

	end




capture program drop xslopecheck
program xslopecheck, sclass
	version 17.0

	syntax, xnum(string) option(string) spec(string)

					capture confirm number `xnum'
						if _rc!=0 {
							dis as error "time value supplied with `option'(`spec') option must be a real number"
							exit 198
					}
					capture assert `xnum'>=0
						if _rc!=0 {
							dis as error "time value supplied with `option'(`spec') option must be a real number >=0"
							exit 198
					}

end

capture program drop testcheck
program testcheck, sclass
	version 17.0

	syntax,  tsp(string) schl(numlist integer >=1 min=1 max=1) [test(string) xtra(string)]

	if "`test'"=="" {
			dis as error  "test() must be specified when trtspec() is '`tsp''"
			exit 198
	}
	numlist "`test'"
	local test=r(numlist)

	local contentslength : word count `test'
			if "`tsp'"=="factor0" {
								if `contentslength' != `schl' {
					dis as error  "content list length for test(`xtra')=`contentslength' when trtspec() is '`tsp'' must be same as visit schedule=`schl'"
					exit 198
				}
			}
			if "`tsp'"=="factor" {
								if `contentslength' != `schl'-1 {
					dis as error  "content list length for test(`xtra')=`contentslength' when trtspec() is '`tsp'' must be one less than for visit schedule=`schl'"
					exit 198
				}
			}
			if "`tsp'"=="slint" | "`trtspec'"=="2slope" | "`trtspec'"=="user2" {
								if `contentslength' != 2 {
					dis as error  "content list length for test(`xtra'=`contentslength') when trtspec() is '`tsp'' must be 2"
					exit 198
				}
			}

				forvalues d=1/`contentslength' {
					local contents`d' : word `d' of `test'
					capture confirm number `contents`d''
						if _rc!=0 {
							dis as error "content list for test() must be specified as number values"
							exit 198
					}
				}
end

capture program drop usercheck
program usercheck, sclass
	version 17.0

	syntax,   func(string) option(string) xvalues(numlist)

		sreturn clear

		gettoken finit : func, parse("(") match(br)
		local numfunc=1
		if strmatch("`finit'", "*;*")	{
			local numfunc=2
			gettoken func1 finit: finit, parse(;)
			gettoken comma1 finit: finit, parse(;)
			if strmatch("`finit'", "*;*") {
				di as error "You have tried to specify (by use of semi-colon separation) too many user-defined functions in `option'"
				exit 198
			}
			else local func2 `finit'
		}
		else local func1 `finit'
		tempname _mp_userfunctionscheck
		frame create `_mp_userfunctionscheck'
		frame change `_mp_userfunctionscheck'

		local y1
		local y2
		local sl: word count `xvalues'
		qui set obs `sl'
		qui gen double x=.
		qui gen double yvar1=.
		if `numfunc'==2 qui gen double yvar2=.
		forvalues sch=1/`sl' {
			local xv1`sch': word `sch' of `xvalues'
			*local fn1=subinstr("`func1'","x","`xv1`sch''",.)

			qui replace x=`xv1`sch'' in `sch'
			capture qui replace yvar1=`func1' in `sch'
			if _rc!=0 {
				di as error "user-function_1 provided cannot be evaluated for `option'"
				exit 198
			}

			local y1`sch'=yvar1[`sch']
			if   `y1`sch''==. local y1`sch'=0
			local y1 "`y1' `y1`sch''"
			if `numfunc'==2 {

				capture qui replace yvar2=`func2' in `sch'
				if _rc!=0 {
					di as error "user-function_2 provided cannot be evaluated for `option'"
					exit 198
				}

				local y2`sch'=yvar2[`sch']
				if   `y2`sch''==. local y2`sch'=0
				local y2 "`y2' `y2`sch''"
			}


		}
		sreturn local nfun=`numfunc'
		sreturn local xfun1 "`y1'"
		sreturn local funct1 "`func1'"
		if `numfunc'==2 sreturn local xfun2 "`y2'"
		if `numfunc'==2 sreturn local funct2 "`func2'"
end