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

/* Version history:
   31/01/19 v0.9 Complete.
*/

/* To do:

   - Add option for plotting.
   - Add option for quantile substitution.
   - Add option for unequal spacing of interim analyses and different ratios in
     each stage.
   - Allow different sd in each arm.

*/

program define des_mams, rclass
version 15.0
syntax , [k(integer 3) j(integer 2) ALPha(real 0.05) beta(real 0.2) ///
          DELta(real 0.5) delta0(real 0) sd(real 1) RATio(real 1) ///
		  LSHape(string) USHape(string) lfix(real 0) ufix(real 2) SEParate ///
		  NO_sample_size n_start(integer 1) n_stop(integer -1)]

preserve

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

if ("`lshape'" == "") {
  local lshape "pocock"
}
if ("`ushape'" == "") {
  local ushape "`lshape'"
}
if (`k' < 1) {
  di "{error}The number of experimental arms (k) must be an integer greater "
  di "than or equal to 1."
  exit(198)
}
if (`j' < 2) {
  di "{error}The maximum number of allowed stages (j) must be an integer "
  di "greater than or equal to 2."
  exit(198)
}
if ((`alpha' <= 0) | (`alpha' >= 1)) {
  di "{error}The desired familywise error-rate (alpha) must be a real strictly "
  di "between 0 and 1."
  exit(198)
}
if ((`beta' <= 0) | (`beta' >= 1)) {
  di "{error}The desired type-II error-rate (beta) must be a real strictly "
  di "between 0 and 1."
  exit(198)
}
if (`delta' <= 0) {
  di "{error}The interesting treatment effect (delta) must be a real strictly"
  di " greater than 0."
  exit(198)
}
if (`sd' <= 0) {
  di "{error}The assumed 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)
}
if (("`lshape'" != "fixed") & ("`lshape'" != "obf") & ///
      ("`lshape'" != "pocock") & ("`lshape'" != "triangular")) {
  di "{error}The lower stopping boundary shape (lshape) must be one of fixed, "
  di "obf, pocock, or triangular."
  exit(198)
}
if (("`ushape'" != "fixed") & ("`ushape'" != "obf") & ///
      ("`ushape'" != "pocock") & ("`ushape'" != "triangular")) {
  di "{error}The upper stopping boundary shape (ushape) must be one of fixed, "
  di "obf, pocock, or triangular."
  exit(198)
}
if (`n_start' < 1) {
  di "{error}The starting value in the search for the required stage-wise group"
  di " size (n_start) must be an integer greater than or equal to 1."
  exit(198)
}
if ((`n_stop' != -1) & (`n_stop' < `n_start')) {
  di "{error}The stopping value in the search for the required stage-wise group"
  di " size (n_stop) must either be equal to -1 or be an integer greater than "
  di "or equal to n_start."
  exit(198)
}
if (("`lshape'" == "fixed") & ("`ushape'" == "fixed") & (`ufix' <= `lfix')) {
  di "{error}The fixed upper stopping boundary value (ufix) must be strictly "
  di "greater than the fixed lower stopping boundary value (lfix) when both "
  di "boundary shapes are fixed (lshape and ushape)."
  exit(198)
}
if ((`delta0' >= `delta') & ("`separate'" == "")) {
  di "WARNING: For designs with a simultaneous stopping rule, the uninteresting"
  di "treatment effect (delta0) should in general be strictly smaller than the "
  di "interesting treatment effect (delta)."
}
if (("`lshape'" != "fixed") & (`lfix' != 0)) {
  di "WARNING: lfix has been changed from default but this will have no effect "
  di "given the choice of lshape."
}
if (("`ushape'" != "fixed") & (`ufix' != 2)) {
  di "WARNING: ufix has been changed from default but this will have no effect "
  di "given the choice of ushape."
}
if (("`separate'" != "") & (`delta0' != 0)) {
  di "WARNING: delta0 has been changed from default but this will have no "
  di "effect given the choice of separate."
}
if ((`k' == 1) & (`delta0' != 0)) {
  di "WARNING: delta0 has been changed from default but this will have no "
  di "effect given the choice of k."
}
if ((`k' == 1) & ("`separate'" != "")) {
  di "WARNING: separate has been changed from default but this will have no "
  di "effect given the choice of k."
}
if ("`no_sample_size'" != "") {
  if (`beta' != 0.2) {
    di "WARNING: beta has been changed from default but this will have no "
	di "effect given the choice of no_sample_size."
  }
  if (`delta' != 0.5) {
    di "WARNING: delta has been changed from default but this will have no "
	di "effect given the choice of no_sample_size."
  }
  if (`delta0' != 0) {
    di "WARNING: delta0 has been changed from default but this will have no "
	di "effect given the choice of no_sample_size."
  }
  if ("`separate'" != "") {
    di "WARNING: separate has been changed from default but this will have no "
	di "effect given the choice of no_sample_size."
  }
  if (`n_start' != 1) {
    di "WARNING: n_start has been changed from default but this will have no "
	di "effect given the choice of no_sample_size."
  }
  if (`n_stop' != -1) {
    di "WARNING: n_stop has been changed from default but this will have no "
	di "effect given the choice of no_sample_size."
  }
}

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

mata: des_mams_void(`k', `j', `alpha', `beta', `delta', `delta0', `sd', ///
                    `ratio', "`lshape'", "`ushape'", `lfix', `ufix', ///
					"`separate'", "`no_sample_size'", `n_start', `n_stop')

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

return mat    u        = u
return mat    l        = l
return scalar max_N    = opchar[1, 10]
return scalar min_N    = opchar[1, 9]
return scalar MSS_LFC  = opchar[1, 8]
return scalar MSS_HG   = opchar[1, 7]
return scalar SDSS_LFC = opchar[1, 6]
return scalar SDSS_HG  = opchar[1, 5]
return scalar ESS_LFC  = opchar[1, 4]
return scalar ESS_HG   = opchar[1, 3]
return scalar P_LFC    = opchar[1, 2]
return scalar P_HG     = opchar[1, 1]
return scalar n        = n[1, 1]

restore

end

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

mata:

void des_mams_void(real scalar K, real scalar J, real scalar alpha,
                   real scalar beta, real scalar delta, real scalar delta0,
			       real scalar sd, real scalar ratio, string lshape,
				   string ushape, real scalar lfix, real scalar ufix,
				   string separate, string no_sample_size, real scalar n_start,
				   real scalar n_stop) {
  printf("{txt}{dup 50:{c -}}\n")
  printf("{inp}%g{txt}-stage {inp}%g{txt}-experimental treatment MAMS ", J, K)
  printf("trial design\n")
  printf("{dup 50:{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 stopping boundaries will be determined to control the ")
  printf("familywise error-rate\nunder the global null hypothesis, HG, ")
  printf("given by:\n\n")
  if (K == 1) {
    printf("   HG: τ{inp}1{txt} = 0.\n\n")
  }
  else if (K == 2) {
    printf("   HG: τ1 = τ{inp}2{txt} = 0.\n\n")
  }
  else if (K == 3) {
    printf("   HG: τ1 = τ2 = τ{inp}3{txt} = 0.\n\n")
  }
  else {
    printf("   HG: τ1 = ... = τ{inp}%g{txt} = 0.\n\n", K)
  }
  printf("Specifically, if P_HA() is the power function for rejecting any null")
  printf(" hypothesis,\nthe stopping boundaries will be chosen such that:\n\n")
  printf("{txt}   P_HA(HG) ≤ α = {inp}%g{txt}.\n\n", alpha)
  if (no_sample_size == "") {
    printf("The required sample size will be determined to control the type-II")
	printf(" error-rate\nunder the least favourable configuration, LFC, given ")
	printf("by:\n\n")
	if (K == 1) {
      printf("   LFC: τ{inp}1{txt} = δ = %g.\n\n", delta)
    }
    else if (K == 2) {
      printf("   LFC: τ1 = δ = {inp}%g{txt}, τ{inp}2{txt} = δ0 = ", delta)
	  printf("{inp}%g{txt}.\n\n", delta0)
    }
    else if (K == 3) {
      printf("   LFC: τ1 = δ = {inp}%g{txt}, τ2 = τ{inp}3{txt} = δ0 = ", delta)
	  printf("{inp}%g{txt}.\n\n", delta0)
    }
    else {
      printf("   LFC: τ1 = δ = {inp}%g{txt}, τ2 = ... = τ{inp}%g{txt} = ",
	         delta, K)
      printf("δ0 = {inp}%g{txt}.\n\n", delta0)
    }
	printf("Specifically, if P_H1() is the power function for rejecting H1, ")
	printf("the sample size\nwill be chosen such that:\n\n")
	printf("   P_H1(LFC) ≥ 1 - β = 1 - {inp}%g{txt}.\n\n", beta)
  }
  else {
    printf("The required sample size {inp}will not{txt} be determined.\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}:1.",
         ratio)
  printf("\n\n")
  if (lshape == "fixed") {
    printf("The lower stopping boundary shape will be: ")
	printf("{inp}fixed{txt}.\n\n")
  }
  else if (lshape == "obf") {
    printf("The lower stopping boundary shape will be: ")
	printf("{inp}O'Brien-Fleming{txt}.\n\n")
  }
  else if (lshape == "pocock") {
    printf("The lower stopping boundary shape will be: ")
	printf("{inp}Pocock{txt}.\n\n")
  }
  else {
    printf("The lower stopping boundary shape will be: ")
	printf("{inp}triangular{txt}.\n\n")
  }
  if (ushape == "fixed") {
    printf("The upper stopping boundary shape will be:")
	printf("{inp}fixed{txt}.\n\n")
  }
  else if (ushape == "obf") {
    printf("The upper stopping boundary shape will be: ")
	printf("{inp}O'Brien-Fleming{txt}.\n\n")
  }
  else if (ushape == "pocock") {
    printf("The upper stopping boundary shape will be: ")
	printf("{inp}Pocock{txt}.\n\n")
  }
  else {
    printf("The upper stopping boundary shape will be: ")
	printf("{inp}triangular{txt}.\n\n")
  }
  if (separate != "") {
    printf("The stopping rule will be: {inp}separate{txt}.\n\n")
  }
  else {
    printf("The stopping rule will be: {inp}simultaneous{txt}.\n\n")
  }
  printf("Now determining the stopping boundaries.\n\n")
  a                                  = b = c = 1
  if (separate != "") {
    d                                = K
  }
  else {
    d                                = 1
  }
  delta_1                            = delta
  delta_0                            = delta0
  seq_K                              = 1::K
  if (J == K) {
    seq_J                            = seq_K
  } else {
    seq_J                            = 1::J
  }
  if (c == K) {
    seq_c                            = seq_K
  } else if (c == J) {
    seq_c                            = seq_J
  } else {
    seq_c                            = 1::c
  }
  if (K - c == J) {
    seq_K_min_c                      = seq_J
  } else if (K - c == c) {
    seq_K_min_c                      = seq_c
  } else {
    seq_K_min_c                      = 1::(K - c)
  }
  cardinality_xi                     = J(2, 1, 0)
  xi_HG                              = perm_combs(1::(2*J), K, 0, 1)
  cardinality_xi[1]                  = rows(xi_HG)
  psi_HG                             = (mod(xi_HG, 2) :== 0)
  omega_HG                           = ceil(0.5*xi_HG)
  if (d < K) {
    retain                           = J(cardinality_xi[1], 1, 1)
    for (i = 1; i <= cardinality_xi[1]; i++) {
      for (j = 1; j <= J; j++) {
        le                           = which_condition(omega_HG[i, seq_K], "le",
		                                               j)
        g                            = which_condition(omega_HG[i, seq_K], "g",
		                                               j)
	    if ((length(le) > 0) & (length(g) > 0)) {
	      if (sum(psi_HG[i, le]) >= d) {
	        retain[i]                = 0
		    break
	      }
	    }
      }
    }
    rows_keep                        = which_condition(retain, "e", 1)
    xi_HG                            = xi_HG[rows_keep, ]
    psi_HG                           = psi_HG[rows_keep, ]
    omega_HG                         = omega_HG[rows_keep, ]
    cardinality_xi[1]                = rows(xi_HG)
  }
  deg_xi_HG                          = deg_a_fwer_xi_HG = J(cardinality_xi[1],
                                                            1, 0)
  for (i = 1; i <= cardinality_xi[1]; i++) {
    deg_xi_HG[i]                     = rows(perm_combs(xi_HG[i, ]', K, 1, 0))
  }
  a_fwer_indices                     =
    which_condition(rowsum(mod(xi_HG, 2) :== 0), "ge", a)
  deg_a_fwer_xi_HG[a_fwer_indices]   = deg_xi_HG[a_fwer_indices]
  xi_HG                              = (psi_HG, omega_HG, deg_xi_HG,
                                        deg_a_fwer_xi_HG,
	  								    ratio*rowsum(omega_HG) +
					                      rowmax(omega_HG),
					                    J(cardinality_xi[1], 1, 0))
  xi_LFC_eff                         = perm_combs(1::(2*J), c, 0, 1)
  psi_LFC_eff                        = (mod(xi_LFC_eff, 2) :== 0)
  omega_LFC_eff                      = ceil(0.5*xi_LFC_eff)
  cardinality_xi[2]                  = rows(xi_LFC_eff)
  if (c > d) {
    retain                           = J(cardinality_xi[2], 1, 1)
    for (i = 1; i <= cardinality_xi[2]; i++) {
      for (j = 1; j <= J; j++) {
        le                           = which_condition(omega_LFC_eff[i, seq_c],
		                                               "le", j)
        g                            = which_condition(omega_LFC_eff[i, seq_c],
		                                               "g", j)
	    if ((length(le) > 0) & (length(g) > 0)) {
	      if (sum(psi_LFC_eff[i, le]) >= d) {
	        retain[i]                = 0
		    break
	      }
	    }
      }
    }
    xi_LFC_eff                       =
	  xi_LFC_eff[which_condition(retain, "e", 1), ]
    cardinality_xi[2]                = rows(xi_LFC_eff)
  }
  deg_xi_LFC_eff                     = deg_bc_power_xi_LFC_eff =
    J(cardinality_xi[2], 1, 0)
  for (i = 1; i <= cardinality_xi[2]; i++) {
    deg_xi_LFC_eff[i]                = rows(perm_combs(xi_LFC_eff[i, ]', c, 1,
	                                                   0))
  }
  bc_power_indices                   =
    which_condition(rowsum(mod(xi_LFC_eff, 2) :== 0), "ge", b)
  deg_bc_power_xi_LFC_eff[bc_power_indices] = deg_xi_LFC_eff[bc_power_indices]
  if (c < K) {
    xi_LFC_fut                       = perm_combs(1::(2*J), K - c, 0, 1)
    psi_LFC_fut                      = (mod(xi_LFC_fut, 2) :== 0)
    omega_LFC_fut                    = ceil(0.5*xi_LFC_fut)
    cardinality_xi_LFC_fut           = rows(xi_LFC_fut)
    if (K - c > d) {
      retain                         = J(cardinality_xi_LFC_fut, 1, 1)
      for (i = 1; i <= cardinality_xi_LFC_fut; i++) {
        for (j = 1; j <= J; j++) {
          le                         =
		    which_condition(omega_LFC_fut[i, seq_K_min_c], "le", j)
          g                          =
		    which_condition(omega_LFC_fut[i, seq_K_min_c], "g", j)
	      if ((length(le) > 0) & (length(g) > 0)) {
	        if (sum(psi_LFC_fut[i, le]) >= d) {
	          retain[i]              = 0
			  break
	        }
	      }
        }
      }
	  xi_LFC_fut                     =
	    xi_LFC_fut[which_condition(retain, "e", 1), ]
      cardinality_xi_LFC_fut         = rows(xi_LFC_fut)
    }
    deg_xi_LFC_fut                   = J(cardinality_xi_LFC_fut, 1, 0)
    for (i = 1; i <= cardinality_xi_LFC_fut; i++) {
      deg_xi_LFC_fut[i]              = rows(perm_combs(xi_LFC_fut[i, ]', K - c,
	                                                   1, 0))
    }
    cardinality_xi_LFC_eff           = cardinality_xi[2]
    cardinality_xi[2]                = cardinality_xi[2]*cardinality_xi_LFC_fut
    xi_LFC                           = J(cardinality_xi[2], K, 0)
    for (i = 1; i <= cardinality_xi_LFC_eff; i++) {
      index_range                    =
	    (1 + (i - 1)*cardinality_xi_LFC_fut)::(i*cardinality_xi_LFC_fut)
	  xi_LFC[index_range, ]          = (J(cardinality_xi_LFC_fut, 1,
	                                      xi_LFC_eff[i, ]), xi_LFC_fut)
    }
    omega_LFC                        = ceil(0.5*xi_LFC)
	xi_LFC                           =
      (mod(xi_LFC, 2) :== 0, omega_LFC,
	   vec(J(cardinality_xi_LFC_fut, 1, deg_xi_LFC_eff)'):*
         vec(J(cardinality_xi_LFC_eff, 1, deg_xi_LFC_fut)'),
	   vec(J(1, cardinality_xi_LFC_fut, deg_bc_power_xi_LFC_eff)'):*
         vec(J(cardinality_xi_LFC_eff, 1, deg_xi_LFC_fut)'),
       ratio*rowsum(omega_LFC) + rowmax(omega_LFC), J(cardinality_xi[2], 1, 0))
    if (d < K) {
      retain                         = J(cardinality_xi[2], 1, 1)
      for (i = 1; i <= cardinality_xi[2]; i++) {
        for (j = 1; j <= J; j++) {
          le                         = which_condition(xi_LFC[i, K :+ seq_K],
		                                               "le", j)
          g                          = which_condition(xi_LFC[i, K :+ seq_K],
		                                               "g", j)
	      if ((length(le) > 0) & (length(g) > 0)) {
	        if (sum(xi_LFC[i, le]) >= d) {
	          retain[i]              = 0
			  break
	        }
	      }
        }
      }
	  xi_LFC                         = xi_LFC[which_condition(retain, "e", 1), ]
      cardinality_xi[2]              = rows(xi_LFC)
    }
  }
  else {
    omega_LFC                        = ceil(0.5*xi_LFC_eff)
    xi_LFC                           =
      (mod(xi_LFC_eff, 2) :== 0, omega_LFC, deg_xi_LFC_eff,
       deg_bc_power_xi_LFC_eff, ratio*rowsum(omega_LFC) + rowmax(omega_LFC),
	   J(cardinality_xi[2], 1, 0))
  }
  Lambda                             = J(K*J, K*J, 0)
  add                                = ratio/(ratio + 1)
  minus                              = 1 - add
  Lambda_j                           = J(K, K, add) + I(K)*minus
  for (j = 1; j <= J; j++) {
    index_range                      = (1 + (j - 1)*K)::(j*K)
    Lambda[index_range, index_range] = Lambda_j
  }
  if (J > 1) {
    for (j1 = 1; j1 <= J; j1++) {
      for (j2 = 1; j2 <= j1 - 1; j2++) {
	    range_j1                     = (1 + (j1 - 1)*K)::(j1*K)
        range_j2                     = (1 + (j2 - 1)*K)::(j2*K)
	    Lambda[range_j2, range_j1]   = Lambda[range_j1, range_j2] =
		  sqrt(j2/j1)*Lambda_j
	  }
    }
  }
  sqrt_I_div_n                            =
    vec(J(K, 1, sqrt((1/(sd^2 + sd^2/ratio))*seq_J')))
  means_HG                           = J(cardinality_xi[1], 1, 0)
  l_indices_HG                       = u_indices_HG = Lambdas_HG =
    J(cardinality_xi[1], J*K, 0)
  for (i = 1; i <= cardinality_xi[1]; i++) {
	relevant_indices                 = .
    psi                              = xi_HG[i, seq_K]
    omega                            = xi_HG[i, K :+ seq_K]
    max_omega                        = max(omega)
    sum_omega                        = sum(omega)
	vec_omega                        = 1::sum_omega
    l_i                              = u_i = J(1, J*K, 0)
    for (k = 1; k <= K; k++) {
	  indices_k                      = range(k, k + (omega[k] - 1)*K, K)'
	  relevant_indices               = (relevant_indices, indices_k)
	  l_i_k                          = u_i_k = J(1, omega[k], 0)
	  for (j = 1; j <= omega[k]; j++) {
	    if (omega[k] > j) {
	      l_i_k[j]                   = j
	    }
	    else if ((psi[k] == 0) & (omega[k] == j)) {
	      l_i_k[j]                   = 2*J + 1
	    }
	    else {
	      l_i_k[j]                   = J + j
	    }
		if ((psi[k] == 1) & (omega[k] == j)) {
	      u_i_k[j]                   = 2*J + 2
	    }
	    else if ((omega[k] > j) |
		           ((psi[k] == 0) & (omega[k] == j) & (max_omega == j) &
			  		  (length(which_condition(psi, "e", 1)) >= d))) {
	      u_i_k[j]                   = J + j
	    }
		else {
		  u_i_k[j]                   = j
		}
	  }
	  l_i[indices_k]                 = l_i_k
	  u_i[indices_k]                 = u_i_k
    }
    sort_indices                     =
      sort(relevant_indices[vec_omega :+ 1]', 1)'
    means_HG[i]                      = sum_omega
    l_indices_HG[i, vec_omega]       = l_i[sort_indices]
    u_indices_HG[i, vec_omega]       = u_i[sort_indices]
    Lambdas_HG[i, vec_omega]         = sort_indices
  }
  delta_LFC                          = J(1, J, (J(1, c, delta_1),
                                                J(1, K - c, delta_0)))
  dim_LFC                            = J(rows(xi_LFC), 1, 0)
  means_div_sqrt_n_LFC               = l_indices_LFC = u_indices_LFC =
    Lambdas_LFC                      = J(rows(xi_LFC), J*K, 0)
  for (i = 1; i <= rows(xi_LFC); i++) {
	relevant_indices                 = .
    psi                              = xi_LFC[i, seq_K]
    omega                            = xi_LFC[i, K :+ seq_K]
    max_omega                        = max(omega)
    sum_omega                        = sum(omega)
	vec_omega                        = 1::sum_omega
    l_i                              = u_i = J(1, J*K, 0)
    for (k = 1; k <= K; k++) {
      indices_k                      = range(k, k + (omega[k] - 1)*K, K)'
	  relevant_indices               = (relevant_indices, indices_k)
	  l_i_k                          = u_i_k = J(1, omega[k], 0)
	  for (j = 1; j <= omega[k]; j++) {
	    if (omega[k] > j) {
	      l_i_k[j]                   = j
	    }
	    else if ((psi[k] == 0) & (omega[k] == j)) {
	      l_i_k[j]                   = 2*J + 1
	    }
	    else {
	      l_i_k[j]                   = J + j
	    }
	    if ((psi[k] == 1) & (omega[k] == j)) {
	      u_i_k[j]                   = 2*J + 2
	    }
	    else if ((omega[k] > j) |
		           ((psi[k] == 0) & (omega[k] == j) & (max_omega == j) &
			  		  (length(which_condition(psi, "e", 1)) >= d))) {
	      u_i_k[j]                   = J + j
	    }
		else {
		  u_i_k[j]                   = j
		}
	  }
	  l_i[indices_k]                 = l_i_k
	  u_i[indices_k]                 = u_i_k
    }
    dim_LFC[i]                       = sum_omega
	sort_indices                     =
      sort(relevant_indices[vec_omega :+ 1]', 1)'
    means_div_sqrt_n_LFC[i, vec_omega] = delta_LFC[sort_indices]:*
                                           (sqrt_I_div_n[sort_indices]')
    l_indices_LFC[i, vec_omega]      = l_i[sort_indices]
    u_indices_LFC[i, vec_omega]      = u_i[sort_indices]
    Lambdas_LFC[i, vec_omega]        = sort_indices
  }
  xi_HG_all                          = xi_HG
  means_HG_all                       = means_HG
  l_indices_HG_all                   = l_indices_HG
  u_indices_HG_all                   = u_indices_HG
  Lambdas_HG_all                     = Lambdas_HG
  xi_LFC_all                         = xi_LFC
  means_div_sqrt_n_LFC_all           = means_div_sqrt_n_LFC
  l_indices_LFC_all                  = l_indices_LFC
  u_indices_LFC_all                  = u_indices_LFC
  Lambdas_LFC_all                    = Lambdas_LFC
  dim_LFC_all                        = dim_LFC
  cardinality_xi_all                 = (rows(xi_HG_all), rows(xi_LFC_all))
  keep_HG                            = which_condition(xi_HG[, 2*K + 2], "g", 0)
  xi_HG                              = xi_HG[keep_HG, ]
  means_HG                           = means_HG[keep_HG]
  l_indices_HG                       = l_indices_HG[keep_HG, ]
  u_indices_HG                       = u_indices_HG[keep_HG, ]
  Lambdas_HG                         = Lambdas_HG[keep_HG, ]
  keep_LFC                           = which_condition(xi_LFC[, 2*K + 2], "g",
                                                       0)
  xi_LFC                             = xi_LFC[keep_LFC, ]
  means_div_sqrt_n_LFC               = means_div_sqrt_n_LFC[keep_LFC, ]
  l_indices_LFC                      = l_indices_LFC[keep_LFC, ]
  u_indices_LFC                      = u_indices_LFC[keep_LFC, ]
  Lambdas_LFC                        = Lambdas_LFC[keep_LFC, ]
  dim_LFC                            = dim_LFC[keep_LFC]
  cardinality_xi                     = (rows(xi_HG), rows(xi_LFC))
  C                                  =
    brent_root_C(K, J, alpha, ratio, lshape, ushape, lfix, ufix, Lambda, xi_HG,
	             means_HG, l_indices_HG, u_indices_HG, Lambdas_HG,
				 cardinality_xi)[1]
  if (ushape == "obf") {
    u                                = C*sqrt(J:/seq_J)
  }
  else if (ushape == "pocock") {
    u                                = J(J, 1, C)
  }
  else if (ushape == "fixed") {
    u                                = (J(J - 1, 1, ufix) \ C)
  }
  else if (ushape == "triangular") {
    u                                = C*(1 :+ seq_J/J):/sqrt(ratio*seq_J)
  }
  if (lshape == "obf") {
    l                                = (-C*sqrt(J:/(1::(J - 1))) \ u[J])
  }
  else if (lshape == "pocock") {
    l                                = (J(J - 1, 1, -C) \ u[J])
  }
  else if (lshape == "fixed") {
    l                                = (J(J - 1, 1, lfix) \ u[J])
  }
  else if (lshape == "triangular") {
    if (ushape == "triangular") {
	  l                              = -C*(1 :- 3*seq_J/J):/sqrt(ratio*seq_J)
	}
	else {
	  l                              =
	  -C*((1 :- 3*seq_J/J):/sqrt(ratio*seq_J))/(2/sqrt(J))
	}
  }
  printf("Lower stopping boundaries, l, determined to be: ")
  for (j = 1; j <= J; j++) {
    if (j == 1) {
      printf("{res}(%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("Upper stopping boundaries, u, determined to be: ")
  for (j = 1; j <= J; j++) {
    if (j == 1) {
      printf("{res}(%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))
    }
  }
  if (no_sample_size == "") {
    printf("Now determining the required sample size.\n\n")
    bounds                           = (l \ u \ . \ .)
	if (n_stop == -1) {
	  q                              = invmvnormal_mata(1 - alpha, J(1, K, 0),
	                                                    Lambda_j, "lower", 100,
	                                                    1e-4, "pmvnormal", 12,
									  		  	        1000)[1]
	  n_stop                         =
	    ceil((sd*(q*sqrt(1 + 1/ratio) +
		            invnormal(1 - beta)*sqrt(1 + 1/ratio))/delta)^2)
	  while (mod(ratio*n_stop, 1) != 0) {
	    n_stop                       = n_stop + 1
	  }
	}
	if (n_stop < n_start) {
	  printf("{err}Computed value of n_stop is smaller than the specified ")
	  printf("value of n_start.\n")
      exit(198)
    }
	power_check                      = 0
	n                                = n_start
	while (mod(ratio*n, 1) != 0) {
	  n                              = n + 1
	}
	if (separate != "") {
	  means_div_sqrt_n               =
		delta*sqrt(seq_J*(1 + 1/ratio)^-1)/sd
      Lambda1                        = I(J)
      for (j1 = 2; j1 <= J; j1++) {
        for (j2 = 1; j2 <= j1 - 1; j2++) {
	      Lambda1[j2, j1]            = Lambda1[j1, j2] = sqrt(j2/j1)
	    }
      }
      while ((power_check == 0) & (n <= n_stop)) {
	    power_check                  =
		  (mams_find_n_sep(n, J, beta, l, u, means_div_sqrt_n, Lambda1) < 0)
		n                            = n + 1
		while (mod(ratio*n, 1) != 0) {
	      n                          = n + 1
	    }
	  }
	}
	else {
	  while ((power_check == 0) & (n <= n_stop)) {
	    power_check                  =
		  (mams_find_n_sim(n, bounds, K, J, beta, Lambda, xi_LFC,
			               means_div_sqrt_n_LFC, l_indices_LFC, u_indices_LFC,
						   Lambdas_LFC, dim_LFC, cardinality_xi) < 0)
		n                            = n + 1
		while (mod(ratio*n, 1) != 0) {
	      n                          = n + 1
	    }
	  }
	}
	if (n > n_stop) {
	  printf("{err}The group size was limited by n_stop. Consider increasing ")
      printf("its value and re-running des_mams.")
      exit(198)
	}
	if (n == n_start) {
	  printf("WARNING: The required power was met with n_start. Consider ")
      printf("decreasing its value and re-running des_mams.")
	}
	n                                = n - 1
	while (mod(ratio*n, 1) != 0) {
	  n                              = n - 1
	}
	printf("Required stage-wise group size in the control arm, n, determined ")
	printf("to be: {res}%g{txt}.\n\n", n)
	opchar                           =
	  (mams_opchar(n, bounds, K, J, Lambda, xi_HG_all, xi_LFC_all, means_HG_all,
	               l_indices_HG_all, u_indices_HG_all, Lambdas_HG_all,
				   means_div_sqrt_n_LFC_all, l_indices_LFC_all,
				   u_indices_LFC_all, Lambdas_LFC_all, dim_LFC_all,
				   cardinality_xi_all), n*(1 + ratio*K), n*J*(1 + ratio*K))
	printf("The operating characteristics of the design are:\n\n")
	printf("   r()      | Variable\n")
	printf("   {dup 8:{c -}}   {dup 8:{c -}}\n")
    printf("   P_HG     | P_HA(HG)  = {res}%g{txt},\n", round(opchar[1], .001))
    printf("   P_LFC    | P_H1(LFC) = {res}%g{txt},\n", round(opchar[2], .001))
	printf("   ESS_HG   | ESS(HG)   = {res}%g{txt},\n", round(opchar[3], .01))
    printf("   ESS_LFC  | ESS(LFC)  = {res}%g{txt},\n", round(opchar[4], .01))
	printf("   SDSS_HG  | SDSS(HG)  = {res}%g{txt},\n", round(opchar[5], .01))
    printf("   SDSS_LFC | SDSS(LFC) = {res}%g{txt},\n", round(opchar[6], .01))
	printf("   MSS_HG   | MSS(HG)   = {res}%g{txt},\n", round(opchar[7], .01))
    printf("   MSS_LFC  | MSS(LFC)  = {res}%g{txt},\n", round(opchar[8], .01))
	printf("   min_N    | min N     = {res}%g{txt},\n", opchar[9])
	printf("   max_N    | max N     = {res}%g{txt}.", opchar[10])
  }
  else {
    n                                = .
	opchar                           = J(1, 9, .)
  }
  st_matrix("n", n)
  st_matrix("l", l)
  st_matrix("u", u)
  st_matrix("opchar", opchar)
}

real colvector brent_root_C(real scalar K, real scalar J, real scalar alpha,
                            real scalar ratio, string lshape, string ushape,
							real lfix, real ufix, real matrix Lambda,
							real matrix xi_HG, real vector means_HG,
							real matrix l_indices_HG, real matrix u_indices_HG,
							real matrix Lambdas_HG,
							real vector cardinality_xi) {
  max_iter   = 100
  half_tol   = 5e-5
  a          = invnormal(1 - alpha)/2
  b          = 5
  fa         = mams_find_C(a, K, J, alpha, ratio, lshape, ushape, lfix, ufix,
                           Lambda, xi_HG, means_HG, l_indices_HG, u_indices_HG,
						   Lambdas_HG, cardinality_xi)
  fb         = mams_find_C(b, K, J, alpha, ratio, lshape, ushape, lfix, ufix,
                           Lambda, xi_HG, means_HG, l_indices_HG, u_indices_HG,
						   Lambdas_HG, cardinality_xi)
  if (((fa > 0) & (fb > 0)) | ((fa < 0) & (fb < 0))) {
    return((. \ . \ 2 \ 0))
  }
  c          = b
  fc         = fb
  for (iter = 1; iter <= max_iter; iter++) {
    if (((fb > 0) & (fc > 0)) | ((fb < 0) & (fc < 0))) {
      c      = a
	  fc     = fa
	  d      = b - a
	  e      = d
	}
	if (abs(fc) < abs(fb)) {
	  a      = b
	  b      = c
	  c      = a
	  fa     = fb
	  fb     = fc
	  fc     = fa
	}
	tol1     = 6e-8*abs(b) + half_tol
	xm       = 0.5*(c - b)
	if ((abs(xm) <= tol1) | (fb == 0)) {
	  return((b \ fb \ 0 \ iter))
	}
	if ((abs(e) >= tol1) & (abs(fa) > abs(fb))) {
	  s      = fb/fa
	  if (a == c) {
	    p    = 2*xm*s
		q    = 1 - s
	  }
	  else {
	    q    = fa/fc
		r    = fb/fc
		p    = s*(2*xm*q*(q - r) - (b - a)*(r - 1))
        q    = (q - 1)*(r - 1)*(s - 1)
	  }
	  if (p > 0) {
	    q    = -q
	  }
	  p      = abs(p)
	  if (2*p < min((3*xm*q - abs(tol1*q), abs(e*q)))) {
	    e    = d
		d    = p/q
	  }
	  else {
	    e    = d = xm
	  }
	}
	else {
	  e      = d = xm
	}
	a        = b
	fa       = fb
	if (abs(d) > tol1) {
	  b      = b + d
	}
	else {
	  b      = b + tol1*sign(xm)
	}
	fb       = mams_find_C(b, K, J, alpha, ratio, lshape, ushape, lfix, ufix,
	                       Lambda, xi_HG, means_HG, l_indices_HG, u_indices_HG,
						   Lambdas_HG, cardinality_xi)
  }
  return((b \ fb \ 1 \ max_iter))
}

real colvector invmvnormal_mata(real scalar p, real vector mean,
                                real matrix Sigma, string tail,
								real scalar max_iter, real scalar tolerance,
								string integrator, real scalar shifts,
								real scalar samples) {

  if ((p == 0) | (p == 1)) {
    return((. \ 0 \ 0 \ 0 \ 0))
  }
  else {
    if (rows(Sigma) == 1) {
      if (tail == "upper") {
        p          = 1 - p
      }
      else if (tail == "both") {
        p          = 0.5 + 0.5*p
      }
      return((invnormal(p) + mean \ 0 \ 0 \ 0 \ 0))
    }
    else {
	  if (tail == "both") {
	    a          = 10^-6
	  }
	  else {
	    a          = -10
	  }
	  b            = 10
	  fa           = invmvnormal_mata_int(a, p, mean, Sigma, tail, integrator,
	                                      shifts, samples)
	  if (fa == 0) {
	    return((a \ 0 \ 0 \ 0 \ 0))
	  }
	  fb           = invmvnormal_mata_int(b, p, mean, Sigma, tail, integrator,
	                                      shifts, samples)
	  if (fb == 0) {
	    return((b \ 0 \ 0 \ 0 \ 0))
	  }
	  if (tail == "both") {
	    while ((fa > 0) & (a >= 10^-20)) {
	      a        = a/2
	      fa       = invmvnormal_mata_int(a, p, mean, Sigma, tail, integrator,
	                                      shifts, samples)
	    }
	    while ((fb < 0) & (b <= 10^6)) {
	      b        = 2*b
	      fb       = invmvnormal_mata_int(b, p, mean, Sigma, tail, integrator,
	                                      shifts, samples)
	    }
	  }
	  else {
	    while ((((fa > 0) & (fb > 0)) | ((fa < 0) & (fb < 0))) & (a >= -10^6)) {
	      a        = 2*a
	      b        = 2*b
	      fa       = invmvnormal_mata_int(a, p, mean, Sigma, tail, integrator,
	                                      shifts, samples)
	      fb       = invmvnormal_mata_int(b, p, mean, Sigma, tail, integrator,
	                                      shifts, samples)
	    }
	  }
	  if (fa == 0) {
	    return((a \ 0 \ 0 \ 0 \ 0))
	  }
	  else if (fb == 0) {
	    return((b \ 0 \ 0 \ 0 \ 0))
	  }
	  else if (((fa < 0) & (fb > 0)) | ((fa > 0) & (fb < 0))) {
        half_tol   = 0.5*tolerance
		c          = b
        fc         = fb
        for (iter = 1; iter <= max_iter; iter++) {
          if (((fb > 0) & (fc > 0)) | ((fb < 0) & (fc < 0))) {
            c      = a
	        fc     = fa
	        d      = b - a
	        e      = d
	      }
	      if (abs(fc) < abs(fb)) {
	        a      = b
	        b      = c
	        c      = a
	        fa     = fb
	        fb     = fc
	        fc     = fa
	      }
	      tol1     = 6e-8*abs(b) + half_tol
	      xm       = 0.5*(c - b)
		  if ((abs(xm) <= tol1) | (fb == 0)) {
	        return((b \ abs(0.5*(b - a)) \ 0 \ fb \ iter))
	      }
	      if ((abs(e) >= tol1) & (abs(fa) > abs(fb))) {
	        s      = fb/fa
	        if (a == c) {
	          pi   = 2*xm*s
		      q    = 1 - s
	        }
	        else {
	          q    = fa/fc
		      r    = fb/fc
		      pi   = s*(2*xm*q*(q - r) - (b - a)*(r - 1))
              q    = (q - 1)*(r - 1)*(s - 1)
	        }
	        if (pi > 0) {
	          q    = -q
	        }
	        pi     = abs(pi)
	        if (2*pi < min((3*xm*q - abs(tol1*q), abs(e*q)))) {
	          e    = d
		      d    = pi/q
	        }
	        else {
	          e    = d = xm
	        }
	      }
	      else {
	        e      = d = xm
	      }
	      a        = b
	      fa       = fb
		  if (abs(d) > tol1) {
	        b      = b + d
	      }
	      else {
	        b      = b + tol1*sign(xm)
	      }
	      fb       = invmvnormal_mata_int(b, p, mean, Sigma, tail, integrator,
	                                      shifts, samples)
        }
		return((b \ abs(0.5*(b - a)) \ 1 \ fb \ iter))
	  }
	  else {
	    return((. \ . \ 2 \ . \ .))
	  }
    }
  }
}

real scalar invmvnormal_mata_int(real scalar q, real scalar p, real vector mean,
                                 real matrix Sigma, string tail,
								 string integrator, real scalar shifts,
								 real scalar samples) {
  k          = rows(Sigma)
  if (tail == "lower") {
    a        = J(1, k, .)
    b        = J(1, k, q)
  }
  else if (tail == "upper") {
    a        = J(1, k, q)
    b        = J(1, k, .)
  }
  else {
    a        = J(1, k, -q)
    b        = J(1, k, q)
  }
  if (integrator != "mvnormal") {
    return(pmvnormal_mata(a, b, mean, Sigma, shifts, samples, 3)[1] - p)
  }
  else {
    for (i = 1; i <= k; i++) {
	  if (a[i] == .) {
	    a[i] = -8e307
	  }
	  if (b[i] == .) {
	    b[i] = 8e307
	  }
	}
	return(mvnormalcv(a, b, mean, vech(Sigma)') - p)
  }
}

real scalar mams_find_C(real scalar C, real scalar K, real scalar J,
                        real scalar alpha, real scalar ratio, string lshape,
						string ushape, real lfix, real ufix, real matrix Lambda,
						real matrix xi_HG, real vector means_HG,
						real matrix l_indices_HG, real matrix u_indices_HG,
						real matrix Lambdas_HG, real vector cardinality_xi) {
  if (ushape == "obf") {
    u                 = C*sqrt(J:/(1::J))
  }
  else if (ushape == "pocock") {
    u                 = J(J, 1, C)
  }
  else if (ushape == "fixed") {
    u                 = (J(J - 1, 1, upper_fix) \ C)
  }
  else if (ushape == "triangular") {
    u                 = C*(1 :+ (1::J)/J):/sqrt(ratio*(1::J))
  }
  if (lshape == "obf") {
    l                 = (-C*sqrt(J:/(1::(J - 1))) \ u[J])
  }
  else if (lshape == "pocock") {
    l                 = (J(J - 1, 1, -C) \ u[J])
  }
  else if (lshape == "fixed") {
    l                 = (J(J - 1, 1, lower_fix) \ u[J])
  }
  else if (lshape == "triangular") {
    if (upper_shape == "triangular") {
	  l               = -C*(1 :- 3*(1::J)/J):/sqrt(ratio*(1::J))
	}
	else {
	  l               =
	  -C*((1 :- 3*(1::J)/J):/sqrt(ratio*(1::J)))/(-1*(1 - 3)/sqrt(J))
	}
  }
  bounds              = (l \ u \ . \ .)
  for (i = 1; i <= cardinality_xi[1]; i++) {
    xi_HG[i, 2*K + 4] = pmvnormal_mata(bounds[l_indices_HG[i, 1::means_HG[i]]]',
	                                   bounds[u_indices_HG[i, 1::means_HG[i]]]',
						 			   J(1, means_HG[i], 0),
						 			   Lambda[Lambdas_HG[i, 1::means_HG[i]],
								     		  Lambdas_HG[i, 1::means_HG[i]]],
									   12, 1000, 3)[1]
  }
  return(alpha - sum(xi_HG[, 2*K + 2]:*xi_HG[, 2*K + 4]))
}

real scalar mams_find_n_sep(real scalar n, real scalar J, real scalar beta,
                            real vector l, real vector u,
							real vector means_div_sqrt_n, real matrix Lambda) {
  delta_sqrt_I = sqrt(n)*means_div_sqrt_n
  pi           = 1 - normal(u[1] - delta_sqrt_I[1])
  if (J > 1) {
    for (j = 2; j <= J; j++) {
      pi       = pi + pmvnormal_mata((l[1::(j - 1)] \ u[j])',
		                             (u[1::(j - 1)] \ .)', delta_sqrt_I[1::j]',
									 Lambda[1::j, 1::j], 12, 1000, 3)[1]
    }
  }
  return(1 - beta - pi)
}

real scalar mams_find_n_sim(real scalar n, real colvector bounds, real scalar K,
                            real scalar J, real scalar beta, real matrix Lambda,
						    real matrix xi_LFC,
							real matrix means_div_sqrt_n_LFC,
						    real matrix l_indices_LFC,
							real matrix u_indices_LFC, real matrix Lambdas_LFC,
							real vector dim_LFC, real vector cardinality_xi) {
  for (i = 1; i <= cardinality_xi[2]; i++) {
    xi_LFC[i, 2*K + 4] =
	  pmvnormal_mata(bounds[l_indices_LFC[i, 1::dim_LFC[i]]]',
	                 bounds[u_indices_LFC[i, 1::dim_LFC[i]]]',
					 sqrt(n)*means_div_sqrt_n_LFC[i, 1::dim_LFC[i]],
					 Lambda[Lambdas_LFC[i, 1::dim_LFC[i]],
							Lambdas_LFC[i, 1::dim_LFC[i]]],
					 12, 1000, 3)[1]
  }
  return(1 - beta - sum(xi_LFC[, 2*K + 2]:*xi_LFC[, 2*K + 4]))
}

real vector mams_opchar(real scalar n, real colvector bounds, real scalar K,
                        real scalar J, real matrix Lambda, real matrix xi_HG,
						real matrix xi_LFC, real vector means_HG,
						real matrix l_indices_HG, real matrix u_indices_HG,
						real matrix Lambdas_HG,
						real matrix means_div_sqrt_n_LFC,
						real matrix l_indices_LFC, real matrix u_indices_LFC,
						real matrix Lambdas_LFC, real vector dim_LFC,
						real vector cardinality_xi) {
  for (i = 1; i <= cardinality_xi[1]; i++) {
    xi_HG[i, 2*K + 4]  =
	  pmvnormal_mata(bounds[l_indices_HG[i, 1::means_HG[i]]]',
	                 bounds[u_indices_HG[i, 1::means_HG[i]]]',
					 J(1, means_HG[i], 0),
					 Lambda[Lambdas_HG[i, 1::means_HG[i]],
							Lambdas_HG[i, 1::means_HG[i]]],
					 12, 1000, 3)[1]
  }
  unique_n_HG          = sort(uniqrows(xi_HG[, 2*K + 3]), 1)
  prob_n_HG            = J(length(unique_n_HG), 1, 0)
  for (i = 1; i <= length(unique_n_HG); i++) {
    which_i            = which_condition(xi_HG[, 2*K + 3], "e", unique_n_HG[i])
    prob_n_HG[i]       = sum(xi_HG[which_i, 2*K + 1]:*xi_HG[which_i, 2*K + 4])
  }
  unique_n_HG          = n*unique_n_HG
  running_prob_n_HG    = runningsum(prob_n_HG)
  if (any(running_prob_n_HG :== 0.5)) {
    which_0_5          = which_condition(running_prob_n_HG, "e", 0.5)[1]
    mss_HG             = 0.5*(unique_n_HG[which_0_5] +
	                            unique_n_HG[which_0_5 + 1])
  }
  else {
    mss_HG             = unique_n_HG[which_condition(running_prob_n_HG, "g",
	                                                 0.5)[1]]
  }
  ess_HG               = n*sum(xi_HG[, 2*K + 1]:*xi_HG[, 2*K + 3]:*
                                 xi_HG[, 2*K + 4])
  sdss_HG             = sqrt(sum(unique_n_HG:^2 :* prob_n_HG) :- ess_HG^2)
  for (i = 1; i <= cardinality_xi[2]; i++) {
    xi_LFC[i, 2*K + 4] =
	  pmvnormal_mata(bounds[l_indices_LFC[i, 1::dim_LFC[i]]]',
	                 bounds[u_indices_LFC[i, 1::dim_LFC[i]]]',
					 sqrt(n)*means_div_sqrt_n_LFC[i, 1::dim_LFC[i]],
					 Lambda[Lambdas_LFC[i, 1::dim_LFC[i]],
							Lambdas_LFC[i, 1::dim_LFC[i]]],
					 12, 1000, 3)[1]
  }
  unique_n_LFC         = sort(uniqrows(xi_LFC[, 2*K + 3]), 1)
  prob_n_LFC           = J(length(unique_n_LFC), 1, 0)
  prob_n_LFC           = prob_n_LFC[order(unique_n_LFC, 1)]
  for (i = 1; i <= length(unique_n_LFC); i++) {
    which_i            = which_condition(xi_LFC[, 2*K + 3], "e",
	                                     unique_n_LFC[i])
    prob_n_LFC[i]      = sum(xi_LFC[which_i, 2*K + 1]:*xi_LFC[which_i, 2*K + 4])
  }
  unique_n_LFC         = n*unique_n_LFC
  running_prob_n_LFC   = runningsum(prob_n_LFC)
  if (any(running_prob_n_LFC :== 0.5)) {
    which_0_5          = which_condition(running_prob_n_LFC, "e", 0.5)[1]
    mss_LFC            = 0.5*(unique_n_LFC[which_0_5] +
	                            unique_n_LFC[which_0_5 + 1])
  }
  else {
    mss_LFC            = unique_n_LFC[which_condition(running_prob_n_LFC, "g",
	                                                  0.5)[1]]
  }
  ess_LFC              = n*sum(xi_LFC[, 2*K + 1]:*xi_LFC[, 2*K + 3]:*
                                 xi_LFC[, 2*K + 4])
  sdss_LFC             = sqrt(sum(unique_n_LFC:^2 :* prob_n_LFC) :- ess_LFC^2)
  return((sum(xi_HG[, 2*K + 2]:*xi_HG[, 2*K + 4]),
          sum(xi_LFC[, 2*K + 2]:*xi_LFC[, 2*K + 4]), ess_HG, ess_LFC, sdss_HG,
		  sdss_LFC, mss_HG, mss_LFC))
}

real matrix perm_combs(real colvector n, real scalar k, real scalar ordered,
                       real scalar replace) {
  if (k == 1) {
    return(n)
  }
  ln                            = length(n)
  if (ln == 1) {
    return(J(1, k, n))
  }
  l                             = ln^k
  allperms                      = J(l, k, 0)
  d                             = n[2::ln] :- n[1::(ln - 1)]
  vl                            = (-sum(d) \ d)
  ln_pow_k_min_1                = l/ln
  allperms[1::l, k]             = J(ln_pow_k_min_1, 1, vl)
  elts                          = count = 1
  while (elts[count] + ln_pow_k_min_1 <= l) {
    elts                        = (elts \ elts[count] + ln_pow_k_min_1)
    count                       = count + 1
  }
  allperms[elts, 1]             = vl
  if (k > 2) {
    for (i = 2; i <= k - 1; i++) {
      elts                      = count = 1
	  ln_pow_i_min_1            = ln^(i - 1)
      while (elts[count] + ln_pow_i_min_1 <= l) {
        elts                    = (elts \ elts[count] + ln_pow_i_min_1)
        count                   = count + 1
      }
      allperms[elts, k - i + 1] = J(count/ln, 1, vl)
    }
  }
  allperms[1, 1::k]             = J(1, k, n[1])
  origallperms                  = allperms
  for (i = 2; i <= l; i++) {
    for (j = 1; j <=k; j++) {
      allperms[i, j]            = sum(origallperms[1::i, j])
    }
  }
  if (ordered == 0) {
    keep                        = J(l, 1, 1)
	for (i = 1; i <= l; i++) {
	  for (j = 2; j <= k; j++) {
	    if (allperms[i, j] < allperms[i, j - 1]) {
		  keep[i]               = 0
		  break
		}
	  }
	}
	allperms                    = allperms[which_condition(keep, "e", 1), ]
  }
  if (replace == 0) {
    if (ordered == 0) {
	  rows_allperms             = rows(allperms)
	} else {
	  rows_allperms             = l
	}
    keep                        = J(rows_allperms, 1, 1)
	for (i = 1; i <= rows_allperms; i++) {
	  for (j = 1; j <= ln; j++) {
	    if (length(which_condition(allperms[i, ], "e", n[j])) >
		      length(which_condition(n, "e", n[j]))) {
		  keep[i]               = 0
		  break
		}
	  }
	}
	allperms                    = allperms[which_condition(keep, "e", 1), ]
  }
  return(uniqrows(allperms))
}

real colvector pmvnormal_mata(real vector lower, real vector upper,
                              real vector mean, real matrix Sigma,
							  real scalar shifts, real scalar samples,
							  real scalar alpha) {
  k                             = rows(Sigma)
  if (k == 1) {
    if ((lower == .) & (upper == .)) {
	  I                         = 1
	}
	else if ((lower != .) & (upper == .)) {
	  I                         = 1 - normal((lower - mean)/sqrt(Sigma))
	}
	else if ((lower == .) & (upper != .)) {
	  I                         = normal((upper - mean)/sqrt(Sigma))
	}
	else {
	  sqrt_Sigma                = sqrt(Sigma)
      I                         = normal((upper - mean)/sqrt_Sigma) -
	                                normal((lower - mean)/sqrt_Sigma)
	}
	E                           = 0
  }
  else if (k == 2) {
    if (lower[1] == .) lower[1] = -8e307
	if (lower[2] == .) lower[2] = -8e307
	if (upper[1] == .) upper[1] = 8e307
	if (upper[2] == .) upper[2] = 8e307
	sqrt_Sigma                  = sqrt((Sigma[1, 1], Sigma[2, 2]))
	a                           = (lower - mean):/(sqrt_Sigma[1], sqrt_Sigma[2])
	b                           = (upper - mean):/(sqrt_Sigma[1], sqrt_Sigma[2])
	r                           = Sigma[1, 2]/(sqrt_Sigma[1]*sqrt_Sigma[2])
	I                           =
	  binormal(b[1], b[2], r) + binormal(a[1], a[2], r) -
	    binormal(b[1], a[2], r) - binormal(a[1], b[2], r)
	E                           = 0
  }
  else {
    a                           = lower - mean
    b                           = upper - mean
	C         = J(k, k, 0)
    zero_k_min_2                = J(1, k - 2, 0)
	zero_k_min_1                = (zero_k_min_2, 0)
    y                           = zero_k_min_1
    atilde                      = (a[1], zero_k_min_2)
    btilde                      = (b[1], zero_k_min_2)
	sqrt_Sigma11                = sqrt(Sigma[1, 1])
    args                        = J(1, k, 1)
	for (j = 1; j <= k; j++) {
      if ((a[j] != .) & (b[j] != .)) {
        args[j]                 = normal(b[j]/sqrt_Sigma11) -
			                        normal(a[j]/sqrt_Sigma11)
      }
      else if ((a[j] == .) & (b[j] != .)) {
        args[j]                 = normal(b[j]/sqrt_Sigma11)
      }
      else if ((b[j] == .) & (a[j] != .)) {
        args[j]                 = 1 - normal(a[j]/sqrt_Sigma11)
      }
    }
	ii                          = ww = .
    minindex(args, 1, ii, ww)
    if (ii[1] != 1) {
	  tempa                     = a
      tempb                     = b
      tempa[1]                  = a[ii[1]]
      tempa[ii[1]]              = a[1]
      a                         = tempa
      tempb[1]                  = b[ii[1]]
      tempb[ii[1]]              = b[1]
      b                         = tempb
      tempSigma                 = Sigma
      tempSigma[1, ]            = Sigma[ii[1], ]
      tempSigma[ii[1], ]        = Sigma[1, ]
      Sigma                     = tempSigma
      Sigma[, 1]                = tempSigma[, ii[1]]
      Sigma[, ii[1]]            = tempSigma[, 1]
	  C[1, 1]                   = sqrt(Sigma[1, 1])
	  C[2::k, 1]                = Sigma[2::k, 1]/C[1, 1]
	}
	else {
	  C[, 1]                     = (sqrt_Sigma11 \ Sigma[2::k, 1]/sqrt_Sigma11)
	}
    if (atilde[1] != btilde[1]) {
	  y[1]                      = (normalden(atilde[1]) - normalden(btilde[1]))/
                                    (normal(btilde[1]) - normal(atilde[1]))
	}
	for (i = 2; i <= k - 1; i++) {
      args                      = J(1, k - i + 1, 1)
	  i_vec                     = 1::(i - 1)
      for (j = 1; j <= k - i + 1; j++) {
        s                       = j + i - 1
		if ((a[s] != .) & (b[s] != .)) {
		    Cy                  = sum(C[s, i_vec]:*y[i_vec])
		    denom               = sqrt(Sigma[s, s] - sum(C[s, i_vec]:^2))
			args[j]             = normal((b[s] - Cy)/denom) -
                                    normal((a[s] - Cy)/denom)
          }
          else if ((a[s] == .) & (b[s] != .)) {
	        args[j]             =
			  normal((b[s] - sum(C[s, i_vec]:*y[i_vec]))/
			           sqrt(Sigma[s, s] - sum(C[s, i_vec]:^2)))
          }
          else if ((b[s] == .) & (a[s] != .)) {
  	        args[j]             =
			  1 - normal((a[s] - sum(C[s, i_vec]:*y[i_vec]))/
			               sqrt(Sigma[s, s] - sum(C[s, i_vec]:^2)))
          }
      }
	  ii                        = ww = .
      minindex(args, 1, ii, ww)
      m                         = i - 1 + ii[1]
	  if (i != m) {
	    tempa                   = a
        tempb                   = b
        tempa[i]                = a[m]
        tempa[m]                = a[i]
        a                       = tempa
        tempb[i]                = b[m]
        tempb[m]                = b[i]
        b                       = tempb
        tempSigma               = Sigma
        tempSigma[i, ]          = Sigma[m, ]
        tempSigma[m, ]          = Sigma[i, ]
        Sigma                   = tempSigma
        Sigma[, i]              = tempSigma[, m]
        Sigma[, m]              = tempSigma[, i]
		tempC                   = C
        tempC[i, ]              = C[m, ]
        tempC[m, ]              = C[i, ]
        C                       = tempC
        C[, i]                  = tempC[, m]
        C[, m]                  = tempC[, i]
	  }
      C[i, i]                   = sqrt(Sigma[i, i] - sum(C[i, i_vec]:^2))
      i_vec2                    = (i + 1)::k
	  C[i_vec2, i]              =
	    (Sigma[i_vec2, i] - rowsum(J(k - i, 1, C[i, i_vec]):*C[i_vec2, i_vec]))/
		  C[i, i]
	  Cy                        = sum(C[i, i_vec]:*y[i_vec])
      atilde[i]                 = (a[i] - Cy)/C[i, i]
      btilde[i]                 = (b[i] - Cy)/C[i, i]
	  if (atilde[i] != btilde[i]) {
	    y[i]                    = (normalden(atilde[i]) - normalden(btilde[i]))/
                                    (normal(btilde[i]) - normal(atilde[i]))
	  }
    }
    C[k, k] = sqrt(Sigma[k, k] - sum(C[k, 1::(k - 1)]:^2))
    I                           = V = 0
    if (a[1] != .) {
      d                         = J(samples, 1, (normal(a[1]/C[1, 1]),
	                                             J(1, k - 1, 0)))
    }
	else {
	  d                         = J(samples, k, 0)
	}
    if (b[1] != .) {
      e                         = J(samples, 1, (normal(b[1]/C[1, 1]),
	                                             J(1, k - 1, 1)))
    }
	else {
	  e                         = J(samples, 1, J(1, k, 1))
	}
    f                           = (e[, 1] - d[, 1], J(samples, k - 1, 0))
	y = J(samples, k - 1, 0)
	Delta                       = runiform(shifts, k - 1)
	samples_sqrt_primes         =
	  (1::samples)*sqrt((2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
	                     53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
						 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
						 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
						 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
						 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
						 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
						 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
						 499, 503, 509, 521, 523, 541)[1::(k - 1)])
	Ii                          = J(1, shifts, 0)
	for (i = 1; i <= shifts; i++) {
	  for (l = 2; l <= k; l++) {
		l_vec                 = 1::(l - 1)
		y[, l - 1]            =
		  invnormal(d[, l - 1] + abs(2*mod(samples_sqrt_primes[, l - 1] :+
       				                     Delta[i, l - 1], 1) :- 1):*
								   (e[, l - 1] - d[, l - 1]))
		if ((a[l] != .) & (b[l] != .)) {
		  Cy                  = rowsum(J(samples, 1, C[l, l_vec]):*y[, l_vec])
		  d[, l]                = normal((a[l] :- Cy)/C[l, l])
	      e[, l]                = normal((b[l] :- Cy)/C[l, l])
		  f[, l]                = (e[, l] :- d[, l]):*f[, l - 1]
		}
		else if ((a[l] != .) & (b[l] == .)) {
          d[, l]                =
	        normal((a[l] :- rowsum(J(samples, 1, C[l, l_vec]):*y[, l_vec]))/
			         C[l, l])
		  f[, l]                = (1 :- d[, l]):*f[, l - 1]
		}
        else if ((a[l] == .) & (b[l] != .)) {
          e[, l]                =
	        normal((b[l] :- rowsum(J(samples, 1, C[l, l_vec]):*y[, l_vec]))/
			         C[l, l])
		  f[, l]                = e[, l]:*f[, l - 1]
        }
		else {
		  f[, l]                = f[, l - 1]
		}
      }
	  for (j = 1; j <= samples; j++) {
	    Ii[i]                   = Ii[i] + (f[j, k] - Ii[i])/j
	  }
	  del                       = (Ii[i] - I)/i
	  I                         = I + del
	  V                         = (i - 2)*V/i + del^2
      E                         = alpha*sqrt(V)
	}
  }
  return((I \ E))
}

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