*! Author(s) : Michael J Grayling
*! Date      : 31 Jan 2019
*! Version   : 0.9

/* Version history:
   31/01/19 v0.9 Initial version complete.
*/

/* To do:
   - Add option for plotting.
*/

program define sim_mams, rclass
version 15.0
syntax , l(numlist miss) u(numlist miss) [n(integer 39) tau(numlist) ///
         sd(real 1) RATio(real 1) SEParate REPlicates(integer 100000)]

preserve

///// Check input variables ////////////////////////////////////////////////////

local lentau:list sizeof tau
local lenl:list sizeof l
local lenu:list sizeof u
if (`lenl' != `lenu') {
  di "{error}The lower and upper stopping boundaries (l and u) must be numlists"
  di " of the same length."
  exit(198)
}
forvalues i = 1/`lenl' {
  local li:word `i' of `l'
  local ui:word `i' of `u'
  if (`i' < `lenl') {
    if ((`li' != .) & (`ui' != .)) {
      if (`li' >= `ui') {
        di "{error}Each lower interim stopping boundary (in l) must be "
		di "strictly less than the corresponding upper interim stopping "
		di "boundary (in u)."
        exit(198)
      }
    }
  }
  else {
    if (`li' != `ui') {
      di "{error}The final lower stopping boundary (in l) must be equal to the "
	  di "final upper stopping boundary (in u)."
      exit(198)
    }
  }
}
if (`n' < 1) {
  di "{error}The stage-wise group size in the control arm (n) must be an "
  di "integer greater than or equal to 1."
  exit(198)
}
if (`sd' <= 0) {
  di "{error}The true value of the standard deviation of the responses (sd) "
  di "must be a real strictly greater than 0."
  exit(198)
}
if (`ratio' <= 0) {
  di "{error}The allocation ratio between the experimental arms and the control"
  di " arm (ratio) must be a real strictly greater than 0."
  exit(198)
}

else if (mod(`ratio'*`n', 1) != 0) {
  di "{error}The product of the allocation ratio between the experimental arms "
  di " and the control arm (ratio) and the stage-wise group size in the control"
  di " arm (n) must be an integer greater than or equal to 1."
  exit(198)
}
if (`replicates' < 1) {
  di "{error}The number of replicate simulations to conduct (replicates) must "
  di "be an integer greater than or equal to 1."
  exit(198)
}

///// Perform main computations ////////////////////////////////////////////////

if (`lentau' > 0) {
  local matatau ""
  foreach i of local tau {
    if "`matatau'" == "" local matatau "`i'"
    else local matatau "`matatau',`i'"
  }
  mat tau = (`matatau')
}
else {
  mat tau = J(1, 3, 0)
}
local matal ""
foreach i of local l {
  if "`matal'" == "" local matal "`i'"
  else local matal "`matal',`i'"
}
mat l     = (`matal')
local matau ""
foreach i of local u {
  if "`matau'" == "" local matau "`i'"
  else local matau "`matau',`i'"
}
mat u     = (`matau')
mata: sim_mams_void(`n', `sd', `ratio', "`separate'", `replicates')

///// Output ///////////////////////////////////////////////////////////////////

return scalar max_N = opchar[1, 7]
return scalar min_N = opchar[1, 6]
return scalar MSS   = opchar[1, 5]
return scalar SDSS  = opchar[1, 4]
return scalar ESS   = opchar[1, 3]
return scalar P_H1  = opchar[1, 2]
return scalar P_HA  = opchar[1, 1]

restore

end

///// Mata /////////////////////////////////////////////////////////////////////

mata:

void sim_mams_void(real scalar n, real scalar sd, real scalar ratio,
                   string separate, real scalar replicates) {
  tau   = st_matrix("tau")
  l     = st_matrix("l")
  u     = st_matrix("u")
  K     = length(tau)
  J     = length(l)
  printf("{txt}{dup 54:{c -}}\n")
  printf("{inp}%g{txt}-stage {inp}%g{txt}-experimental treatment MAMS trial ",
         J, K)
  printf("simulation\n")
  printf("{dup 54:{c -}}\n")
  printf("The hypotheses to be tested will be:\n\n")
  if (K == 1) {
    printf("   H1: τ{inp}1{txt} = μ1 - μ0 ≤ 0.\n\n")
  }
  else if (K == 2) {
    printf("   Hk: τk = μk - μ0 ≤ 0, k = 1, {inp}2{txt}.\n\n")
  }
  else if (K == 3) {
    printf("   Hk: τk = μk - μ0 ≤ 0, k = 1, 2, {inp}3{txt}.\n\n")
  }
  else {
    printf("   Hk: τk = μk - μ0 ≤ 0, k = 1, ..., {inp}%g{txt}.\n\n", K)
  }
  printf("The stage-wise group size in the control arm, n, will be: ")
  printf("{inp}%g{txt}.\n\n", n)
  printf("The stage-wise allocation ratio between the (remaining) experimental")
  printf(" arms and\nthe shared control arm, ratio, will be: {inp}%g{txt}.\n\n",
         ratio)
  printf("The lower stopping boundaries, l, will be: ")
  for (j = 1; j <= J; j++) {
    if (j == 1) {
      printf("{inp}(%g, ", round(l[j], .01))
    }
    else if ((j > 1) & (j < J)) {
      printf("%g, ", round(l[j], .01))
    }
    else {
      printf("%g){txt}.\n\n", round(l[j], .01))
    }
  }
  printf("The upper stopping boundaries, u, will be: ")
  for (j = 1; j <= J; j++) {
    if (j == 1) {
      printf("{inp}(%g, ", round(u[j], .01))
    }
    else if ((j > 1) & (j < J)) {
      printf("%g, ", round(u[j], .01))
    }
    else {
      printf("%g){txt}.\n\n", round(u[j], .01))
    }
  }
  printf("The trial will be simulated under the scenario where:\n\n")
  if (K == 1) {
    printf("   τ = τ{inp}1{txt} = {inp}%g{txt}.\n\n", tau)
  }
  else if (K == 2) {
    printf("   τ = (τ1, τ{inp}2{txt}) = {inp}(%g, %g){txt}.\n\n",
	       tau[1], tau[2])
  }
  else {
    printf("   τ = (")
	for (k = 1; k <= K - 1; k++) {
	  printf("τ%g, ", k)
	}
	printf("τ{inp}%g{txt}) = {inp}(", K)
	for (k = 1; k <= K - 1; k++) {
	  printf("%g, ", tau[k])
	}
	printf("%g){txt}.\n\n", tau[K])
  }
  printf("Beginning the required simulations.\n\n")
  opchar = sim_mams_mata(tau, n, l, u, sd, ratio, separate, replicates, K, J)
  printf("Completed the required simulations.\n\n")
  printf("The estimated operating characteristics of the trial are:\n\n")
  printf("   r()   | Variable\n")
  printf("   {dup 5:{c -}}   {dup 8:{c -}}\n")
  printf("   P_HA  | P_HA(tau) = {res}%g{txt},\n", round(opchar[1], .0001))
  printf("   P_H1  | P_H1(tau) = {res}%g{txt},\n", round(opchar[2], .0001))
  printf("   ESS   | ESS(tau)  = {res}%g{txt},\n", round(opchar[3], .01))
  printf("   SDSS  | SDSS(tau) = {res}%g{txt},\n", round(opchar[4], .01))
  printf("   MSS   | MSS(tau)  = {res}%g{txt},\n", round(opchar[5], .01))
  printf("   min_N | min N     = {res}%g{txt},\n", opchar[6])
  printf("   max_N | max N     = {res}%g{txt}.", opchar[7])
  st_matrix("opchar", opchar)
}

real vector sim_mams_mata(real vector tau, real scalar n, real vector l,
                          real vector u, real scalar sd, real scalar ratio,
						  string separate, real scalar replicates,
					      real scalar K, real scalar J) {
  seq_J                     = 1::J
  seq_K                     = 1::K
  r0n                       = seq_J*n
  r0n_diff                  = r0n :- (0 \ r0n[1::(J - 1)])
  sd_sqrt_r0n_diff          = sd*sqrt(r0n_diff)
  r                         = seq_J*ratio
  rn                        = r*n
  rn_mat                    = J(1, K, rn)
  rn_diff                   = r0n_diff*ratio
  sd_sqrt_rn_diff           = J(1, K, sd*sqrt(rn_diff))
  rn_diff_tau               = rn_diff*tau
  denominator               = (sd*sqrt((J(1, K, r) :+ J(1, K, seq_J)):/
                                         (J(1, K, rn):*J(1, K, seq_J))))
  one_1K                    = J(1, K, 1)
  rej_any                   = rej_one = 0
  sample_size               = J(replicates, 1, 0)
  if (separate == "") {
    for (i = 1; i <= replicates; i++) {
	  hat_mu_k              = rnormal(J, K, 0, 1):*sd_sqrt_rn_diff :+
                                rn_diff_tau
      for (k = 1; k <= K; k++) {
	    hat_mu_k[, k]       = runningsum(hat_mu_k[, k])
      }
	  Z_jk                  =
	    (hat_mu_k:/rn_mat :-
		   J(1, K, runningsum(rnormal(1, 1, 0, sd_sqrt_r0n_diff)):/r0n)):/
	      denominator
	  remaining             = seq_K
	  remaining_num         = K
	  remaining_logical     = one_1K
	  for (j = 1; j <= J; j++) {
	    sample_size[i]      = sample_size[i] + r0n_diff[j] +
		                        remaining_num*rn_diff[j]
		if (max(Z_jk[j, remaining]) > u[j]) {
		  rej_any           = rej_any + 1
		  rej_one           = rej_one + ((remaining_logical[1] == 1) &
		                                    (Z_jk[j, 1] > u[j]))
		  break
		}
		else if (max(Z_jk[j, remaining]) <= l[j]) {
		  break
		}
		else if (j < J) {
		  remaining_logical = ((Z_jk[j, ] :> l[j]) :& (remaining_logical))
		  remaining         = which_condition(remaining_logical, "e", 1)
		  remaining_num     = sum(remaining_logical)
		}
	  }
    }
  }
  else {
    zero_JK                 = J(J, K, 0)
    for (i = 1; i <= replicates; i++) {
	  rej_mat               = zero_JK
      hat_mu_k              = rnormal(J, K, 0, 1):*sd_sqrt_rn_diff :+
                                rn_diff_tau
      for (k = 1; k <= K; k++) {
	    hat_mu_k[, k]  = runningsum(hat_mu_k[, k])
      }
	  Z_jk                  =
	    (hat_mu_k:/rn_mat :-
		   J(1, K, runningsum(rnormal(1, 1, 0, sd_sqrt_r0n_diff)):/r0n)):/
	      denominator
	  remaining             = seq_K
	  remaining_num         = K
	  remaining_logical     = one_1K
	  for (j = 1; j <= J; j++) {
		sample_size[i]      = sample_size[i] + r0n_diff[j] +
		                        remaining_num*rn_diff[j]
		for (k = 1; k <= remaining_num; k++) {
		  if (Z_jk[j, remaining[k]] > u[j]) {
		    rej_mat[j, remaining[k]] = 1
		  }
		}
		if ((j == J) | all((Z_jk[j, remaining] :> u[j]) :|
		                     (Z_jk[j, remaining] :<= l[j]))) {
		  rej_any           = rej_any + (sum(rej_mat) > 0)
		  rej_one           = rej_one + (sum(rej_mat[, 1]) > 0)
		  break
		}
		else {
		  remaining_logical = ((Z_jk[j, ] :> l[j]) :& (Z_jk[j, ] :<= u[j]) :&
								 (remaining_logical))
		  remaining         = which_condition(remaining_logical, "e", 1)
		  remaining_num     = sum(remaining_logical)
		}
	  }
    }
  }
  ess                       = mean(sample_size)
  sort_sample_size          = sort(sample_size, 1)
  unique_sample_size        = uniqrows(sample_size)
  prob_unique_sample_size   = J(length(unique_sample_size), 1, 0)
  return((rej_any/replicates, rej_one/replicates, mean(sample_size),
          sqrt(sum((sample_size :- ess):^2)/(replicates - 1)),
		  0.5*(sort_sample_size[ceil(0.5*replicates)] +
		         sort_sample_size[ceil(0.5*replicates + 1)]), min(sample_size),
		  max(sample_size)))
}

function which_condition(real vector check, string type, real scalar value) {
  match       = .
  for (i = 1; i <= length(check); i++) {
    if (type == "ge") {
	  if (check[i] >= value) {
	    match = (match, i)
	  }
	}
	else if (type == "g") {
	  if (check[i] > value) {
	    match = (match, i)
	  }
	}
	else if (type == "le") {
	  if (check[i] <= value) {
	    match = (match, i)
	  }
	}
	else if (type == "l") {
	  if (check[i] < value) {
	    match = (match, i)
	  }
	}
	else if (type == "e") {
	  if (check[i] == value) {
	    match = (match, i)
	  }
	}
  }
  if (length(match) > 1) {
    match     = match[2::length(match)]
    if (rows(check) > 1) {
	  return(match')
	}
	else {
	  return(match)
	}
  }
  else {
    return(J(0, 0, .))
  }
}

end