R/get.oc.comb.R

#'
#' Generate operating characteristics for drug combination trials
#'
#' Obtain the operating characteristics of the BOIN design or waterfall design for drug combination
#'  trials. The BOIN design is to find a MTD, and the waterfall design is to find the MTD contour
#'  (i.e., multple MTDs in the dose matrix)
#'
#' @usage get.oc.comb(target, p.true, ncohort, cohortsize, n.earlystop=NULL, startdose=c(1, 1),
#'                    titration=FALSE,p.saf=0.6*target, p.tox=1.4*target, cutoff.eli=0.95,
#'                    extrasafe=FALSE,offset=0.05, ntrial=1000, mtd.contour=FALSE,
#'                    boundMTD=FALSE, seed=6)
#'
#' @param target the target DLT rate
#' @param p.true a \code{J*K} matrix \code{(J<=K)} containing the true toxicity probabilities of
#'               combinations with \code{J} dose levels of agent A and \code{K} dose levels of agent B
#' @param ncohort a \code{1*J} vector specifying the number of cohorts for each of \code{J} subtrials
#'                if \code{mtd.contour=TRUE}; Otherwise, a scalar specifying the total number of cohorts for
#'                the trial.
#' @param cohortsize the cohort size
#' @param n.earlystop the early stopping parameter. If the number of patients treated at the current
#'                    dose reaches \code{n.earlystop}, stop the trial or subtrial and select the MTD based on
#'                    the observed data. When the waterfall design is used to find the MTD contour,
#'                    \code{n.earlystop=12} by default.
#' @param startdose the starting dose combination level for drug combination trial
#' @param titration set \code{titration=TRUE} to perform dose escalation with cohort size = 1 to accelerate dose escalation at the begining of the trial.
#' @param p.saf the highest toxicity probability that is deemed subtherapeutic (i.e. below the MTD)
#'              such that dose escalation should be undertaken.
#'              The default value is \code{p.saf=0.6*target}.
#' @param p.tox the lowest toxicity probability that is deemed overly toxic such that deescalation
#'              is required. The default value is \code{p.tox=1.4*target}.
#' @param cutoff.eli the cutoff to eliminate an overly toxic dose for safety. We recommend the
#'                   default value of (\code{cutoff.eli=0.95}) for general use.
#' @param extrasafe set \code{extrasafe=TRUE} to impose a more stringent stopping rule
#' @param offset a small positive number (between 0 and 0.5) to control how strict the stopping
#'               rule is when \code{extrasafe=TRUE}. A larger value leads to a more strict stopping
#'               rule. The default value \code{offset=0.05} generally works well.
#' @param ntrial the total number of trials to be simulated
#' @param mtd.contour set \code{mtd.contour=TRUE} to select the MTD contour (claiming multiple MTDs).
#'                    Otherwise, BOIN design is used to search for a single MTD.
#' @param boundMTD set \code{boundMTD=TRUE} to impose the condition: the isotonic estimate of toxicity
#'                 probability for the selected MTD must be less than de-escalation boundary.
#' @param seed the random seed for simulation
#'
#' @details The operating characteristics of the BOIN design or waterfall design are generated by
#' simulating trials under the prespecified true toxicity probabilities of the investigational dose
#' combinations. If \code{titration=TRUE}, we perform dose escalation with cohort size = 1 at the begining of the trial:
#' starting from \code{startdose}, if no toxicity is observed, we escalate the dose;
#' otherwise, the titration is completed and we switch to cohort size = \code{cohortsize}.
#' Titration accelerates the dose escalation and is useful when low doses are believed to be safe.
#'
#'
#' The BOIN and waterfall designs have two built-in stopping rules:
#' (1) stop the trial/subtrial if the lowest dose is eliminated due to toxicity, and no dose should
#' be selected as the MTD; and (2) stop the trial/subtrial and select the MTD if the number of
#' patients treated at the current dose reaches \code{n.earlystop}. The first stopping rule is a safety
#' rule to protect patients from the case in which all doses are overly toxic. The rationale for
#' the second stopping rule is that when there is a large number (i.e., \code{n.earlystop}) of
#' patients assigned to a dose, it means that the dose-finding algorithm has approximately converged.
#' Thus, we can stop the trial/subtrial early and select the MTD to save sample size and reduce the
#' trial duration.
#'
#'
#'  For some applications, investigators may prefer a more strict safety stopping rule than rule
#'  (1) for extra safety when the lowest dose is overly toxic.
#'  This can be achieved by setting \code{extrasafe=TRUE},
#'  which imposes the following more strict safety stopping rule:
#'  stop the trial if (i) the number of patients treated at the lowest dose \code{>=3},
#' and (ii) \eqn{Pr(toxicity\ rate\ of\ the\ lowest\ dose > \code{target} | data) > \code{cutoff.eli}-\code{offset}}.
#' As a tradeoff, the strong stopping rule will decrease the MTD selection percentage
#'  when the lowest dose actually is the MTD.
#'
#' @return \code{get.oc.comb()} returns the operating characteristics of the BOIN combination or
#' waterfall design as a list. For the BOIN combination design, including:
#' (1) true toxicity probability at each dose level (\code{$p.true}),
#' (2) selection percentage at each dose level (\code{$selpercent}),
#' (3) the number of patients treated at each dose level (\code{$npatients})
#' (4) the number of toxicities observed at each dose level (\code{$ntox}),
#' (5) the total number of patients in the trial (\code{$totaln}),
#' (6) the total number of toxicities observed for the trial (\code{$totaltox})
#' (7) the pecentage of correct selection (\code{$pcs}),
#' (8) the total percentage of patients treated at the MTD (\code{$npercent}).
#' (9) the percentage of early stopping without selecting the MTD (\code{$percentstop})
#' For the the waterfall design, including:
#' (1) true toxicity probability at each dose level (\code{$p.true}),
#' (2) selection percentage of dose combinations (\code{$selpercent}),
#' (3) the number of patients treated at each dose combination (\code{$npatients})
#' (4) the number of toxicities observed at each dose combination (\code{$ntox}),
#' (5) the total number of patients in the trial (\code{$totaln}),
#' (6) the total number of toxicities observed for the trial (\code{$totaltox})
#' (7) the total percentage of correct selection at the MTD contour (\code{$pcs.contour}),
#' (8) the total percentage of patients treated at MTD contour
#' (\code{$npercent.contour})
#' (9) the total percentage of patients treated above MTD contour
#' (\code{$npercent.above.contour})
#' (10) the total percentage of patients treated below MTD contour
#' (\code{$npercent.below.contour})
#'
#'
#' @note We should avoid setting the values of \code{p.saf} and \code{p.tox} very close to the
#'      \code{target}. This is because the small sample sizes of typical phase I trials prevent us from
#'       differentiating the target DLT rate from the rates close to it. The default values provided by
#'       \code{get.oc()} are strongly recommended, and generally yield excellent operating characteristics.
#'
#'
#' @author Suyu Liu, Liangcai Zhang, Yanhong Zhou, and Ying Yuan
#'
#' @references Liu S. and Yuan, Y. (2015). Bayesian Optimal Interval Designs for Phase I Clinical
#'             Trials, \emph{Journal of the Royal Statistical Society: Series C}, 64, 507-523.
#'
#'            Lin R. and Yin, G. (2017). Bayesian Optimal Interval Designs for Dose Finding in
#'            Drug-combination Trials, \emph{Statistical Methods in Medical Research}, 26, 2155-2167.
#'
#'            Yan, F., Zhang, L., Zhou, Y., Pan, H., Liu, S. and Yuan, Y. (2020).BOIN: An R Package
#'            for Designing Single-Agent and Drug-Combination Dose-Finding Trials Using Bayesian Optimal
#'            Interval Designs. \emph{Journal of Statistical Software}, 94(13),1-32.<doi:10.18637/jss.v094.i13>.
#'
#'
#'            Zhang L. and Yuan, Y. (2016). A Simple Bayesian Design to Identify the Maximum
#'            Tolerated Dose Contour for Drug Combination Trials, \emph{Statistics in Medicine}, 35, 4924-4936.
#'
#' @seealso  Tutorial: \url{http://odin.mdacc.tmc.edu/~yyuan/Software/BOIN/BOIN2.6_tutorial.pdf}
#'
#'           Paper: \url{http://odin.mdacc.tmc.edu/~yyuan/Software/BOIN/paper.pdf}
#'
#' @examples
#'
#' ###### drug-combination trial ######
#'
#' ##### combination trial to find a single MTD  ######
#'
#' ## get the operating characteristics for BOIN design
#' p.true <- matrix(c(0.01,0.03,0.10,0.20,0.30,
#'                  0.03,0.05,0.15,0.30,0.60,
#'                  0.08,0.10,0.30,0.60,0.75), byrow=TRUE, ncol=5)
#'
#' oc.comb <- get.oc.comb(target=0.3, p.true, ncohort=20, cohortsize=3,
#'    n.earlystop=12, startdose=c(1,1), ntrial=100)
#'
#' summary(oc.comb)
#' plot(oc.comb)
#'
#'
#' ## get the operating characteristics with titration for BOIN design
#' oc.comb <- get.oc.comb(target=0.3, p.true, ncohort=20, cohortsize=3,
#'    n.earlystop=12, startdose=c(1,1), titration=TRUE, ntrial=100)
#' summary(oc.comb)
#' plot(oc.comb)
#'
#'
#' ##### combination trial to find the MTD contour ######
#'
#' ## find the MTD contour using waterfall design
#' oc.comb <- get.oc.comb(target=0.3, p.true, ncohort=c(10,5,5), cohortsize=3,
#'    n.earlystop=12, startdose=c(1,1), ntrial=100, mtd.contour=TRUE)
#'
#' summary(oc.comb)
#' plot(oc.comb)
#'
#' @export
get.oc.comb <- function (target, p.true, ncohort, cohortsize, n.earlystop = NULL,
                         startdose = c(1, 1), titration = FALSE, p.saf = 0.6 * target,
                         p.tox = 1.4 * target, cutoff.eli = 0.95, extrasafe = FALSE,
                         offset = 0.05, ntrial = 1000, mtd.contour = FALSE, boundMTD=FALSE,seed = 6)
{
  get.oc.comb.boin <- function(target, p.true, ncohort, cohortsize,
                               n.earlystop = 100, startdose = c(1, 1), titration = FALSE,
                               p.saf = 0.6 * target, p.tox = 1.4 * target, cutoff.eli = 0.95,
                               extrasafe = FALSE, offset = 0.05, boundMTD=FALSE,ntrial = 1000) {
    JJ = nrow(p.true)
    KK = ncol(p.true)
    if (JJ > KK)
      stop("p.true should be arranged in a way (i.e., rotated) such that\n          the number of rows is less than or equal to the number of columns.")
    if (JJ > KK)
      p.true = t(p.true)
    if (target < 0.05) {
      stop("the target is too low!")
    }
    if (target > 0.6) {
     stop("the target is too high!")

    }
    if ((target - p.saf) < (0.1 * target)) {
      stop("the probability deemed safe cannot be higher than or too close to the target!")
    }
    if ((p.tox - target) < (0.1 * target)) {
      stop("the probability deemed toxic cannot be lower than or too close to the target!")
    }
    if (offset >= 0.5) {
      stop("the offset is too large!")
    }
    if (n.earlystop <= 6) {
      warning("the value of n.earlystop is too low to ensure good operating characteristics. Recommend n.earlystop = 9 to 18")
    }
    ndose = length(p.true)
    npts = ncohort * cohortsize
    Y <- array(matrix(rep(0, length(p.true) * ntrial), dim(p.true)[1]),
               dim = c(dim(p.true), ntrial))
    N <- array(matrix(rep(0, length(p.true) * ntrial), dim(p.true)[1]),
               dim = c(dim(p.true), ntrial))
    dselect = matrix(rep(0, 2 * ntrial), ncol = 2)
    if (cohortsize > 1) {
      temp = get.boundary(target, ncohort, cohortsize,
                          n.earlystop=100, p.saf, p.tox, cutoff.eli, extrasafe)$full_boundary_tab
    }else {
      temp = get.boundary(target, ncohort, cohortsize,
                          n.earlystop=100, p.saf, p.tox, cutoff.eli, extrasafe)$boundary_tab
    }
    b.e = temp[2, ]
    b.d = temp[3, ]
    b.elim = temp[4, ]
    lambda1 = log((1 - p.saf)/(1 - target))/log(target *
                                                  (1 - p.saf)/(p.saf * (1 - target)))
    lambda2 = log((1 - target)/(1 - p.tox))/log(p.tox * (1 -
                                                           target)/(target * (1 - p.tox)))
    if (cohortsize == 1)
      titration = FALSE
    for (trial in 1:ntrial) {
      y <- matrix(rep(0, ndose), dim(p.true)[1], dim(p.true)[2])
      n <- matrix(rep(0, ndose), dim(p.true)[1], dim(p.true)[2])
      earlystop = 0
      d = startdose
      elimi = matrix(rep(0, ndose), dim(p.true)[1], dim(p.true)[2])
      ft=TRUE #flag used to determine whether or not to add cohortsize-1 patients to a dose for the first time when titration is triggered.
      if (titration) {
        tmpa = d[1]
        tmpb = d[2]
        y[tmpa, tmpb] <- (runif(1) < p.true[tmpa, tmpb])
        n[tmpa, tmpb] <- 1
        while (tmpa <= dim(p.true)[1] & tmpb <= dim(p.true)[2]) {
          if (tmpa == dim(p.true)[1] & tmpb == dim(p.true)[2]) {
            break
          }
          if (sum(y) == 1) {
            y[tmpa, tmpb] = 1
            break
          }
          if (tmpa < dim(p.true)[1] & tmpb < dim(p.true)[2]) {
            tmp.candidate = rbind(c(tmpa + 1, tmpb),
                                  c(tmpa, tmpb + 1))
            tmp.sel = rbinom(1, 1, prob = c(0.5, 0.5)) +
              1
            tmpa = tmp.candidate[tmp.sel, 1]
            tmpb = tmp.candidate[tmp.sel, 2]
          }
          else if (tmpa == dim(p.true)[1]) {
            tmpb = tmpb + 1
          }
          else {
            tmpa = tmpa + 1
          }
          y[tmpa, tmpb] <- (runif(1) < p.true[tmpa, tmpb])
          n[tmpa, tmpb] <- 1
        }
        if (sum(y) == 0) {
          d = c(dim(p.true)[1], dim(p.true)[2])
        }
        else {
          d = c(tmpa, tmpb)
        }
      }
      for (pp in 1:ncohort) {

        if (titration & n[d[1], d[2]] < cohortsize & ft) {
          ft=FALSE

          y[d[1], d[2]] = y[d[1], d[2]] + sum(runif(cohortsize -
                                                      1) < p.true[d[1], d[2]])
          n[d[1], d[2]] = n[d[1], d[2]] + cohortsize -1
        }
        else {
          y[d[1], d[2]] = y[d[1], d[2]] + sum(runif(cohortsize) <
                                                p.true[d[1], d[2]])
          n[d[1], d[2]] = n[d[1], d[2]] + cohortsize
        }



        nc = n[d[1], d[2]]
        if (!is.na(b.elim[nc])) {
          if (y[d[1], d[2]] >= b.elim[nc]) {
            for (i in min(d[1], dim(p.true)[1]):dim(p.true)[1]) {
              for (j in min(d[2], dim(p.true)[2]):dim(p.true)[2]) {
                elimi[i, j] = 1
              }
            }
            if (d[1] == 1 && d[2] == 1) {
              d = c(99, 99)
              earlystop = 1
              break
            }
          }
          if (extrasafe) {
            if (d[1] == 1 && d[2] == 1 && n[1, 1] >=
                3) {
              if (1 - pbeta(target, y[1, 1] + 1, n[1,
                                                   1] - y[1, 1] + 1) > cutoff.eli - offset) {
                d = c(99, 99)
                earlystop = 1
                break
              }
            }
          }
        }

        if (n[d[1],d[2]] >= n.earlystop  && (y[d[1],d[2]]>b.e[n[d[1],d[2]]] ||
                                             (d[1]==dim(p.true)[1] && d[2]==dim(p.true)[2]) ||
                                             ( d[1]==dim(p.true)[1] && d[2]<dim(p.true)[2] && elimi[d[1],d[2]+1]==1 ) ||
                                             ( d[1]<dim(p.true)[1] && d[2]==dim(p.true)[2] && elimi[d[1]+1,d[2]]==1 ) ||
                                             ( d[1]<dim(p.true)[1] && d[2]<dim(p.true)[2] && elimi[d[1]+1,d[2]]==1 && elimi[d[1],d[2]+1]==1 ) )  &&
            (y[d[1],d[2]]<b.d[n[d[1],d[2]]] || (d[1]==1 && d[2]==1) ) ) break;


        if (y[d[1], d[2]] <= b.e[nc]) {
          elevel = matrix(c(1, 0, 0, 1), 2)
          pr_H0 = rep(0, length(elevel)/2)
          nn = pr_H0
          for (i in seq(1, length(elevel)/2, by = 1)) {
            if (d[1] + elevel[1, i] <= dim(p.true)[1] &&
                d[2] + elevel[2, i] <= dim(p.true)[2]) {
              if (elimi[d[1] + elevel[1, i], d[2] + elevel[2,
                                                           i]] == 0) {
                yn = y[d[1] + elevel[1, i], d[2] + elevel[2,
                                                          i]]
                nn[i] = n[d[1] + elevel[1, i], d[2] +
                            elevel[2, i]]
                pr_H0[i] <- pbeta(lambda2, yn + 0.5,
                                  nn[i] - yn + 0.5) - pbeta(lambda1,
                                                            yn + 0.5, nn[i] - yn + 0.5)
              }
            }
          }
          pr_H0 = pr_H0 + nn * 5e-04
          if (max(pr_H0) == 0) {
            d = d
          }
          else {
            k = which(pr_H0 == max(pr_H0))[as.integer(runif(1) *
                                                        length(which(pr_H0 == max(pr_H0))) + 1)]
            d = d + c(elevel[1, k], elevel[2, k])
          }
        }
        else if (y[d[1], d[2]] >= b.d[nc]) {
          delevel = matrix(c(-1, 0, 0, -1), 2)
          pr_H0 = rep(0, length(delevel)/2)
          nn = pr_H0
          for (i in seq(1, length(delevel)/2, by = 1)) {
            if (d[1] + delevel[1, i] > 0 && d[2] + delevel[2,
                                                           i] > 0) {
              yn = y[d[1] + delevel[1, i], d[2] + delevel[2,
                                                          i]]
              nn[i] = n[d[1] + delevel[1, i], d[2] +
                          delevel[2, i]]
              pr_H0[i] = pbeta(lambda2, yn + 0.5, nn[i] -
                                 yn + 0.5) - pbeta(lambda1, yn + 0.5,
                                                   nn[i] - yn + 0.5)
            }
          }
          pr_H0 = pr_H0 + nn * 5e-04
          if (max(pr_H0) == 0) {
            d = d
          }
          else {
            k = which(pr_H0 == max(pr_H0))[as.integer(runif(1) *
                                                        length(which(pr_H0 == max(pr_H0))) + 1)]
            d = d + c(delevel[1, k], delevel[2, k])
          }
        }
        else {
          d = d
        }


      }
      Y[, , trial] = y
      N[, , trial] = n
      if (earlystop == 1) {
        dselect[trial, ] = c(99, 99)
      }else {
        selcomb = select.mtd.comb.boin(target, n, y,
                                       cutoff.eli, extrasafe, offset,
                                       boundMTD=boundMTD,p.tox=p.tox,mtd.contour = FALSE)$MTD
        dselect[trial, 1] = selcomb[1]
        dselect[trial, 2] = selcomb[2]
      }
    }
    selpercent = matrix(rep(0, ndose), dim(p.true)[1], dim(p.true)[2])
    nptsdose = apply(N, c(1, 2), mean, digits = 2, format = "f")
    ntoxdose = apply(Y, c(1, 2), mean, digits = 2, format = "f")
    for (i in 1:dim(p.true)[1]) for (j in 1:dim(p.true)[2]) {
      {
        selpercent[i, j] = sum(dselect[, 1] == i & dselect[,
                                                           2] == j)/ntrial * 100
      }
    }
    if (JJ <= KK) {
      rownames(p.true) = paste("DoseA", 1:dim(p.true)[1],
                               sep = "")
      colnames(p.true) = paste("DoseB", 1:dim(p.true)[2],
                               sep = "")
      rownames(selpercent) = paste("DoseA", 1:dim(p.true)[1],
                                   sep = "")
      colnames(selpercent) = paste("DoseB", 1:dim(p.true)[2],
                                   sep = "")
      out = list(p.true = round(p.true, 2), selpercent = round(selpercent,2),
                 npatients = round(apply(N, c(1, 2), mean),2), ntox = round(apply(Y, c(1, 2), mean), 2),
                 totaltox = round(sum(Y)/ntrial, 1), totaln = round(sum(N)/ntrial,1),
                 pcs = paste(round(sum(selpercent[which(abs(p.true -target) == min(abs(p.true - target)), arr.ind = TRUE)]),1), "%", sep = ""),
                 npercent = paste(round(sum(nptsdose[which(abs(p.true -target) == min(abs(p.true - target)), arr.ind = TRUE)])/sum(nptsdose) *100, 1), "%", sep = ""),
                 percentstop=100-sum(round(selpercent,2)),flowchart = FALSE)
      rownames(out$npatients) = paste("DoseA", 1:dim(p.true)[1],
                                      sep = "")
      colnames(out$npatients) = paste("DoseB", 1:dim(p.true)[2],
                                      sep = "")
      rownames(out$ntox) = paste("DoseA", 1:dim(p.true)[1],
                                 sep = "")
      colnames(out$ntox) = paste("DoseB", 1:dim(p.true)[2],
                                 sep = "")

      return(out)
    }
    else {
      colnames(p.true) = paste("DoseB", 1:dim(t(p.true))[1],
                               sep = "")
      rownames(p.true) = paste("DoseA", 1:dim(t(p.true))[2],
                               sep = "")
      colnames(selpercent) = paste("DoseB", 1:dim(t(p.true))[1],
                                   sep = "")
      rownames(selpercent) = paste("DoseA", 1:dim(t(p.true))[2],
                                   sep = "")
      colnames(npatients) = paste("DoseB", 1:dim(t(p.true))[1],
                                  sep = "")
      rownames(npatients) = paste("DoseA", 1:dim(t(p.true))[2],
                                  sep = "")
      colnames(ntox) = paste("DoseB", 1:dim(t(p.true))[1],
                             sep = "")
      rownames(ntox) = paste("DoseA", 1:dim(t(p.true))[2],
                             sep = "")
      out = list(p.true = round(t(p.true), 2), selpercent = round(t(selpercent),2),
                 npatients = round(t(apply(N, c(1, 2), mean)), 2), ntox = round(t(apply(Y, c(1, 2), mean)),2),
                 totaltox = round(sum(Y)/ntrial, 1), totaln = round(sum(N)/ntrial,1), pcs = paste(round(sum(selpercent[which(abs(p.true -target) == min(abs(p.true - target)), arr.ind = TRUE)]),
                 1), "%"), npercent = paste(round(sum(nptsdose[which(abs(p.true -target) == min(abs(p.true - target)), arr.ind = TRUE)])/sum(nptsdose) *100, 1), "%"),
                 percentstop=100-sum(round(selpercent,2)),
                 flowchart = FALSE)

      return(out)
    }
  }
  select.mtd.comb.boin <- function(target, npts, ntox, cutoff.eli = 0.95,
                                   extrasafe = FALSE, offset = 0.05,
                                   boundMTD=FALSE,p.tox=1.4*target,
                                   mtd.contour = FALSE) {
    lambda_d = log((1 - target)/(1 - p.tox))/log(p.tox * (1 -target)/(target * (1 - p.tox)))
    y = ntox
    n = npts
    if (nrow(n) > ncol(n) | nrow(y) > ncol(y)) {
      stop("npts and ntox should be arranged in a way (i.e., rotated) such that for each of them, the number of rows is less than or equal to the number of columns.")
    }
    elimi = matrix(0, dim(n)[1], dim(n)[2])
    if (extrasafe) {
      if (n[1, 1] >= 3) {
        if (1 - pbeta(target, y[1, 1] + 1, n[1, 1] -
                      y[1, 1] + 1) > cutoff.eli - offset) {
          elimi[, ] = 1
        }
      }
    }
    for (i in 1:dim(n)[1]) {
      for (j in 1:dim(n)[2]) {
        if (n[i, j] >= 3) {
          if (1 - pbeta(target, y[i, j] + 1, n[i, j] -
                        y[i, j] + 1) > cutoff.eli) {
            elimi[i:dim(n)[1], j] = 1
            elimi[i, j:dim(n)[2]] = 1
            break
          }
        }
      }
    }

    selectdose=NULL
    if (elimi[1] == 1) {

      selectdose = c(99, 99)
      selectdoses = matrix(selectdose, nrow = 1)

    }else {

      phat = (y + 0.05)/(n + 0.1)
      phat = Iso::biviso(phat, n + 0.1, warn = TRUE)[,
                                                     ]
      phat.out = phat
      phat.out[n == 0] = NA
      phat[elimi == 1] = 1.1
      phat = phat * (n != 0) + (1e-05) * (matrix(rep(1:dim(n)[1],
                                                     each = dim(n)[2], len = length(n)), dim(n)[1],
                                                 byrow = T) + matrix(rep(1:dim(n)[2], each = dim(n)[1],
                                                                         len = length(n)), dim(n)[1]))


      if(boundMTD){

        if(all(phat[n!=0]>=lambda_d)){
          selectdose = c(99, 99)


          selectdoses = matrix(selectdose, nrow = 1)
        }else{
          phat[phat>=lambda_d]=10}}

      if(is.null(selectdose)){

      phat[n == 0] = 10
      selectdose = which(abs(phat - target) == min(abs(phat -
                                                         target)), arr.ind = TRUE)
      if (length(selectdose) > 2)
        selectdose = selectdose[1, ]
      aa = function(x) as.numeric(as.character(x))
      if (mtd.contour == TRUE) {
        selectdoses = cbind(row = 1:dim(n)[1], col = rep(99,
                                                         dim(n)[1]))
        for (k in dim(n)[1]:1) {
          kn = n[k, ]
          ky = y[k, ]
          kelimi = elimi[k, ]
          kphat = phat[k, ]
          if (kelimi[1] == 1 || sum(n[kelimi == 0]) ==
              0) {
            kseldose = 99
          }
          else {
            adm.set = (kn != 0) & (kelimi == 0)
            adm.index = which(adm.set == T)
            y.adm = ky[adm.set]
            n.adm = kn[adm.set]
            selectd = sort(abs(kphat[adm.set] - target),
                           index.return = T)$ix[1]
            kseldose = adm.index[selectd]
          }
          selectdoses[k, 2] = ifelse(is.na(kseldose),
                                     99, kseldose)
          if (k < dim(n)[1])
            if (selectdoses[k + 1, 2] == dim(n)[2])
              selectdoses[k, 2] = dim(n)[2]
          if (k < dim(n)[1])
            if (aa(selectdoses[k + 1, 2]) == dim(n)[2] &
                aa(selectdoses[k + 1, 2]) == aa(selectdoses[k,
                                                            2]))
              selectdoses[k, 2] = 99
        }
      }
      else {
        selectdoses = matrix(99, nrow = 1, ncol = 2)
        selectdoses[1, ] = matrix(selectdose, nrow = 1)
      }
      selectdoses = matrix(selectdoses[selectdoses[, 2] !=
                                         99, ], ncol = 2)
      }

      colnames(selectdoses) = c("DoseA", "DoseB")
    }
    if (mtd.contour == FALSE) {
      if (selectdoses[1, 1] == 99 && selectdoses[1, 2] ==
          99) {

        out=list(target = target, MTD = 99, p_est = matrix(NA,
                                                              nrow = dim(npts)[1], ncol = dim(npts)[2]))

        return(out)
      }
      else {
        out=list(target = target, MTD = selectdoses,
                    p_est = round(phat.out, 2))

        return(out)
      }
    }
    else {
      if (length(selectdoses) == 0) {
        out=list(target = target, MTD = 99, p_est = matrix(NA,
                                                              nrow = dim(npts)[1], ncol = dim(npts)[2]))


        return(out)

      }
      else {
        out=list(target = target, MTD = selectdoses,
                    p_est = round(phat.out, 2))

        return(out)
      }
    }
  }
  waterfall.subtrial.mtd <- function(target,npts, ntox, cutoff.eli = 0.95,
                                     extrasafe = FALSE, offset = 0.05, boundMTD=FALSE, p.tox, temp) {

    lambda_d = log((1 - target)/(1 - p.tox))/log(p.tox * (1 -target)/(target * (1 - p.tox)))

    b.e = temp[2, ]
    pava <- function(x, wt = rep(1, length(x))) {
      n <- length(x)
      if (n <= 1)
        return(x)
      if (any(is.na(x)) || any(is.na(wt))) {
        stop("Missing values in 'x' or 'wt' not allowed")
      }
      lvlsets <- (1:n)
      repeat {
        viol <- (as.vector(diff(x)) < 0)
        if (!(any(viol)))
          break
        i <- min((1:(n - 1))[viol])
        lvl1 <- lvlsets[i]
        lvl2 <- lvlsets[i + 1]
        ilvl <- (lvlsets == lvl1 | lvlsets == lvl2)
        x[ilvl] <- sum(x[ilvl] * wt[ilvl])/sum(wt[ilvl])
        lvlsets[ilvl] <- lvl1
      }
      x
    }
    y = ntox
    n = npts
    ndose = length(n)
    elimi = rep(0, ndose)
    is.escalation = 0
    for (i in 1:ndose) {
      if (n[i] >= 3) {
        if (1 - pbeta(target, y[i] + 1, n[i] - y[i] +
                      1) > cutoff.eli) {
          elimi[i:ndose] = 1
          break
        }
      }
    }
    if (extrasafe) {
      if (n[1] >= 3) {
        if (1 - pbeta(target, y[1] + 1, n[1] - y[1] +
                      1) > cutoff.eli - offset) {
          elimi[1:ndose] = 1
        }
      }
    }
    if (elimi[1] == 1 || sum(n[elimi == 0]) == 0) {
      selectdose = 99
    }
    else {
      adm.set = (n != 0) & (elimi == 0)
      adm.index = which(adm.set == T)
      y.adm = y[adm.set]
      n.adm = n[adm.set]
      phat = (y.adm + 0.05)/(n.adm + 0.1)
      phat.var = (y.adm + 0.05) * (n.adm - y.adm + 0.05)/((n.adm +
                                                             0.1)^2 * (n.adm + 0.1 + 1))
      phat = pava(phat, wt = 1/phat.var)
      phat = phat + (1:length(phat)) * 1e-10
      if(boundMTD){
        if(all(phat>=lambda_d)){selectdose=99}else{
          phat=phat[phat<lambda_d]
          selectd = sort(abs(phat - target), index.return = T)$ix[1]
          selectdose = adm.index[selectd]
        }

      }else{
        selectd = sort(abs(phat - target), index.return = T)$ix[1]
        selectdose = adm.index[selectd]
      }
      # selectd = sort(abs(phat - target), index.return = T)$ix[1]
      # selectdose = adm.index[selectd]
      if(selectdose!=99){
        if (y[selectdose] <= b.e[n[selectdose]]) {
          is.escalation = 1
        }
      }

    }
    list(selectdose = selectdose, is.escalation = is.escalation)
  }

  waterfall.subtrial <- function(target, p.true, dosespace,
                                 npts, ntox, elimi, ncohort, cohortsize, n.earlystop = 20,
                                 startdose = 1, p.saf = 0.6 * target, p.tox = 1.4 * target,
                                 cutoff.eli = 0.95, extrasafe = FALSE, offset = 0.05,
                                 totaln, titration.first.trial = FALSE, temp,boundMTD=FALSE) {
    ndoses1 = nrow(p.true)
    ndoses2 = ncol(p.true)
    p.truee = p.true[dosespace]
    npts = npts
    ntox = ntox
    elimi = elimi
    if (target < 0.05) {
      stop(" the target is too low! ")

    }
    if (target > 0.6) {
      stop("the target is too high!")

    }
    if ((target - p.saf) < (0.1 * target)) {
      stop("the probability deemed safe cannot be higher than or too close to the target! ")

    }
    if ((p.tox - target) < (0.1 * target)) {
      stop("the probability deemed toxic cannot be lower than or too close to the target!")

    }
    if (offset >= 0.5) {
      stop("the offset is too large! ")

    }
    ndose = length(p.truee)
    selectdose = 0
    is.escalation = 0
    b.e = temp[2, ]
    b.d = temp[3, ]
    b.elim = temp[4, ]
    lambda1 = log((1 - p.saf)/(1 - target))/log(target *
                                                  (1 - p.saf)/(p.saf * (1 - target)))
    lambda2 = log((1 - target)/(1 - p.tox))/log(p.tox * (1 -
                                                           target)/(target * (1 - p.tox)))
    y <- rep(0, ndose)
    n <- rep(0, ndose)
    earlystop = 0
    d = startdose
    elm = rep(0, ndose)
    if (titration.first.trial) {
      z <- (runif(ndose) < p.truee)
      if (sum(z) == 0) {
        d = ndose
        n[1:ndose] = 1
      }
      else {
        d = which(z == 1)[1]
        n[1:d] = 1
        y[d] = 1
      }
    }
    ft=TRUE #flag used to determine whether or not to add cohortsize-1 patients to a dose for the first time when titration is triggered.
    for (icohort in 1:ncohort) {
      if (titration.first.trial & n[d] < cohortsize & ft) {
        ft=FALSE
        y[d] = y[d] + sum(runif(cohortsize - 1) < p.truee[d])
        n[d] = n[d] + cohortsize - 1
      }else {
        y[d] = y[d] + sum(runif(cohortsize) < p.truee[d])
        n[d] = n[d] + cohortsize
      }
      if (!is.na(b.elim[n[d]])) {
        if (y[d] >= b.elim[n[d]]) {
          elm[d:ndose] = 1
          if (d == 1) {
            earlystop = 1
            break
          }
        }
        if (extrasafe) {
          if (d == 1 && n[1] >= 3) {
            if (1 - pbeta(target, y[1] + 0.05, n[1] -
                          y[1] + 0.1) > cutoff.eli - offset) {
              earlystop = 1
              break
            }
          }
        }
      }
      if (y[d] <= b.e[n[d]] && d != ndose) {
        if (elm[d + 1] == 0)
          d = d + 1
      }
      else if (y[d] >= b.d[n[d]] && d != 1) {
        d = d - 1
      }
      else {
        d = d
      }
      if (n[d] >= n.earlystop)
        break
      if (sum(n) >= (ncohort * cohortsize))
        break
    }
    if (earlystop == 1) {
      selectdose = 99
      elm = rep(1, ndose)
    }
    else {

      wsmtd = waterfall.subtrial.mtd(target, n, y, cutoff.eli,
                                     extrasafe, offset, boundMTD=boundMTD, p.tox,temp)
      selectdose = wsmtd$selectdose
      is.escalation = wsmtd$is.escalation
    }
    npts[dosespace] = n
    ntox[dosespace] = y
    elimi[dosespace] = elm
    list(ncohort = icohort, ntotal = icohort * cohortsize,
         startdose = startdose, npts = npts, ntox = ntox,
         totaltox = sum(ntox), totaln = sum(npts), pctearlystop = sum(selectdose ==
                                                                        99) * 100, selectdose = selectdose, is.escalation = is.escalation,
         elimi = elimi)
  }
  get.oc.comb.waterfall <- function(p.true, target, ncohort,
                                    cohortsize, n.earlystop = 12, cutoff.eli = 0.95, p.saf = 0.6 *
                                      target, p.tox = 1.4 * target, titration = FALSE,
                                    extrasafe = FALSE, offset = 0.05, boundMTD=FALSE,ntrial = 1000) {
    temp = get.boundary(target, ncohort = 150, cohortsize = 1,
                        cutoff.eli = cutoff.eli, extrasafe = extrasafe)$boundary_tab
    JJ = nrow(p.true)
    KK = ncol(p.true)
    if (JJ > KK)
      p.true = t(p.true)
    true.mtd.position = cbind(1:JJ, apply(p.true, 1, function(x) {
      flaga = rep(0, length(x))
      tmp = which.min(abs(x - target))
      flaga[tmp] = 1
      flagb = (x <= target + 0.05)
      ifelse(sum(flaga & flagb) > 0, which(flaga & flagb),
             99)
    }))
    true.mtd.pos = apply(true.mtd.position, 1, function(x) (x[2] -
                                                              1) * JJ + x[1])
    true.mtd.pos.new = true.mtd.pos[true.mtd.pos <= JJ *
                                      KK]
    nMTDs = paste(sort(true.mtd.pos.new), collapse = ",")
    greater.than.contour = rep(1, length(JJ * KK))
    less.than.contour = 1 - greater.than.contour
    if (sum(true.mtd.position[, 2] <= KK) > 0) {
      tmp = true.mtd.position[true.mtd.position[, 2] <=
                                JJ * KK, ]
      if (length(tmp) == 2)
        tmp = matrix(tmp, ncol = 2)
      ONES = matrix(1, nrow = JJ, ncol = KK)
      for (kkk in 1:nrow(tmp)) {
        tmpa = tmp[kkk, 1]
        tmpb = tmp[kkk, 2]
        ONES[1:tmpa, 1:tmpb] = 0
      }
      ONES[true.mtd.pos.new] = 1
      less.than.contour = which(ONES == 0)
      ONES = matrix(1, nrow = JJ, ncol = KK)
      for (kkk in 1:nrow(tmp)) {
        tmpa = tmp[kkk, 1]
        tmpb = tmp[kkk, 2]
        ONES[tmpa:JJ, tmpb:KK] = 0
      }
      ONES[true.mtd.pos.new] = 1
      greater.than.contour = which(ONES == 0)
    }
    at.contour = c(1:(JJ * KK))[-c(greater.than.contour,
                                   less.than.contour)]
    aa = function(x) as.numeric(as.character(x))
    if (target < 0.05) {
      stop(" the target is too low! ")

    }
    if (target > 0.6) {
      stop("the target is too high!")

    }
    if ((target - p.saf) < (0.1 * target)) {
      stop("the probability deemed safe cannot be higher than or too close to the target! ")

    }
    if ((p.tox - target) < (0.1 * target)) {
      stop("the probability deemed toxic cannot be lower than or too close to the target!")

    }
    ndoses1 <- nrow(p.true)
    ndoses2 <- ncol(p.true)
    ntrial.phase1 = NULL
    ntrial.mtd = NULL
    ntrial.nt = NULL
    ntrial.yt = NULL
    ntrial.ne = NULL
    ntrial.ye = NULL
    if (cohortsize == 1)
      titration = FALSE
    titration.first.trial = FALSE
    for (trial in 1:ntrial) {
      trial.result = NULL
      ntox = matrix(rep(0, (ndoses2) * ndoses1), ncol = ndoses2)
      colnames(ntox) = paste("ntoxDoseB", 1:ndoses2, sep = "")
      npts = matrix(rep(0, (ndoses2) * ndoses1), ncol = ndoses2)
      colnames(npts) = paste("nptsDoseB", 1:ndoses2, sep = "")
      elimi = matrix(0, nrow = ndoses1, ncol = ndoses2)
      colnames(elimi) = paste("elimiDoseB", 1:ndoses2,
                              sep = "")
      mtd = cbind(selectdoseA = 1:ndoses1, selectdoseB = rep(NA,
                                                             ndoses1))
      trial.result = data.frame(cbind(trial = rep(trial,
                                                  ndoses1), mtd, npts, ntox, elimi))
      totaln = 0
      startdose = 1
      dosespace = c(1:(ndoses1 - 1), (1:ndoses2) * ndoses1)
      subtriali = 1
      while (totaln < sum(ncohort) * cohortsize) {
        if (titration & subtriali == 1) {
          titration.first.trial = TRUE
        }
        else {
          titration.first.trial = FALSE
        }

        subtrial = waterfall.subtrial(target, p.true = p.true,
                                      dosespace = dosespace, npts = npts, ntox = ntox,
                                      elimi = elimi, ncohort = ncohort[subtriali],
                                      cohortsize = cohortsize, n.earlystop = n.earlystop,
                                      startdose = startdose, p.saf = p.saf, p.tox = p.tox,
                                      cutoff.eli = cutoff.eli, extrasafe = extrasafe,
                                      offset = offset, totaln = totaln,
                                      titration.first.trial = titration.first.trial,
                                      temp = temp,boundMTD=boundMTD)
        selectdose = ifelse(subtrial$selectdose == 99,
                            99, dosespace[subtrial$selectdose])
        if (selectdose == 99)
          break
        dj = ifelse(selectdose%%ndoses1 == 0, selectdose%/%ndoses1,
                    selectdose%/%ndoses1 + 1)
        di = selectdose - (dj - 1) * ndoses1
        totaln = aa(subtrial$totaln)
        npts = subtrial$npts
        ntox = subtrial$ntox
        elimi = subtrial$elimi
        if ((subtriali == 1) & (selectdose < ndoses1)) {
          for (a in (di + 1):ndoses1) for (b in 1:ndoses2) elimi[a,
                                                                 b] = 1
          if (subtrial$is.escalation == 1) {
            startdose = 1
            dosespace1 = c(di + ((2:ndoses2) - 1) * ndoses1)
            subtriali = subtriali + 1
            subtrial1 = waterfall.subtrial(target, p.true = p.true,
                                           dosespace = dosespace1, npts = npts, ntox = ntox,
                                           elimi = elimi, ncohort = ncohort[subtriali],
                                           cohortsize = cohortsize, n.earlystop = n.earlystop,
                                           startdose = startdose, p.saf = p.saf, p.tox = p.tox,
                                           cutoff.eli, extrasafe, offset, totaln = totaln,
                                           temp = temp,boundMTD=boundMTD)
            selectdose1 = ifelse(subtrial1$selectdose ==
                                   99, selectdose, dosespace1[subtrial1$selectdose])
            if (selectdose1 == 99)
              break
            dj = ifelse(selectdose1%%ndoses1 == 0, selectdose1%/%ndoses1,
                        selectdose1%/%ndoses1 + 1)
            di = selectdose1 - (dj - 1) * ndoses1
            totaln = aa(subtrial1$totaln)
            npts = subtrial1$npts
            ntox = subtrial1$ntox
            elimi = subtrial1$elimi
          }
        }
        if (di - 1 == 0)
          break
        subtriali = subtriali + 1
        if (dj < ndoses2)
          elimi[di, (dj + 1):ndoses2] = 1
        startdose = dj
        dosespace = di - 1 + ((2:ndoses2) - 1) * ndoses1
        if (dj == ndoses2)
          startdose = dj - 1
      }
      npts = t(apply(npts, 1, aa))
      ntox = t(apply(ntox, 1, aa))
      elimi = t(apply(elimi, 1, aa))
      phat = (ntox + 0.05)/(npts + 0.1)
      phat = t(apply(phat, 1, aa))
      colnames(phat) = paste("phat", 1:ndoses2, sep = "")
      phat[elimi == 1] = 1.1
      phat = Iso::biviso(phat, npts + 0.1, warn = TRUE)[,
                                                        ]
      phat = phat + (1e-05) * (matrix(rep(1:dim(npts)[1],
                                          each = dim(npts)[2], len = length(npts)), dim(npts)[1],
                                      byrow = T) + matrix(rep(1:dim(npts)[2], each = dim(npts)[1],
                                                              len = length(npts)), dim(npts)[1]))
      colnames(phat) = paste("phat", 1:ndoses2, sep = "")
      for (k in ndoses1:1) {
        kn = npts[k, ]
        ky = ntox[k, ]
        kelimi = elimi[k, ]
        kphat = phat[k, ]
        if (kelimi[1] == 1 || sum(npts[kelimi == 0]) ==
            0) {
          kseldose = 99
        }
        else {
          adm.set = (kn != 0) & (kelimi == 0)
          adm.index = which(adm.set == T)
          y.adm = ky[adm.set]
          n.adm = kn[adm.set]
          selectd = sort(abs(kphat[adm.set] - target),
                         index.return = T)$ix[1]
          kseldose = adm.index[selectd]
        }
        mtd[k, 2] = kseldose
        if (k < ndoses1)
          if (mtd[k, 2] - mtd[k + 1, 2] <= 0 & mtd[k +
                                                   1, 2] != 99)
            mtd[k, 2] = mtd[k + 1, 2]
      }
      trial.result[1:ndoses1, grep("nptsDoseB", colnames(trial.result))] = npts
      trial.result[1:ndoses1, grep("ntoxDoseB", colnames(trial.result))] = ntox
      trial.result[1:ndoses1, grep("selectdose", colnames(trial.result))] = mtd
      trial.result[1:ndoses1, grep("elimiDoseB", colnames(trial.result))] = elimi
      ntrial.mtd = rbind(ntrial.mtd, cbind(trial = rep(trial,
                                                       nrow(mtd)), mtd))
      ntrial.nt = rbind(ntrial.nt, cbind(trial = rep(trial,
                                                     nrow(npts)), npts))
      ntrial.yt = rbind(ntrial.yt, cbind(trial = rep(trial,
                                                     nrow(ntox)), ntox))
      trial.result = cbind(trial.result, phat)
      ntrial.phase1 = rbind(ntrial.phase1, trial.result)
    }
    ntrial.mtd = data.frame(ntrial.mtd)
    colnames(ntrial.mtd) = c("trial", "doseA", "doseB")
    alltoxpercent = round(sum(sapply(1:ntrial, function(x) ifelse(sum(ntrial.mtd$doseB[ntrial.mtd$trial ==
                                                                                         x] == 99) == ndoses1, 1, 0)), na.rm = T) * 100/1000,
                          3)
    stoppercent = round(sum(sapply(1:ntrial, function(x) sum(ntrial.mtd$doseB[ntrial.mtd$trial ==
                                                                                x] == 99))) * 100/1000, 1)
    selpercent = matrix(0, nrow = ndoses1, ncol = ndoses2)
    nselpercent = 0
    selpercent1 = 0
    selpercent2 = 0
    mtdtable = NULL
    mtdlist = list()
    for (triali in 1:ntrial) {
      mtddata = unique(as.matrix(ntrial.mtd[ntrial.mtd$trial ==
                                              triali, 2:3]))
      mtddata = mtddata[!is.na(mtddata[, 2]) & mtddata[,
                                                       2] <= KK, ]
      if (length(mtddata) > 0) {
        if (length(mtddata) == 2)
          mtddata = matrix(mtddata, ncol = 2)
        mtdlevel = aa(t(apply(mtddata, 1, function(x) (x[2] -
                                                         1) * ndoses1 + x[1])))
        mtdlevel[mtdlevel > ndoses1 * ndoses2] = 99
        if (sum(mtdlevel <= ndoses1 * ndoses2) > 0) {
          selpercent[mtdlevel[mtdlevel <= ndoses1 * ndoses2]] = selpercent[mtdlevel[mtdlevel <=
                                                                                      ndoses1 * ndoses2]] + 1
          mtdlist[[triali]] = mtdlevel[mtdlevel <= ndoses1 *
                                         ndoses2]
          mtdtable = c(mtdtable, paste(sort(aa(mtdlevel[mtdlevel <=
                                                          ndoses1 * ndoses2])), collapse = ","))
          if (paste(sort(aa(mtdlevel[mtdlevel <= ndoses1 *
                                     ndoses2])), collapse = ",") == nMTDs)
            nselpercent = nselpercent + 1
          if (sum(mtdlevel <= ndoses1 * ndoses2) == 1 &
              sum(is.element(mtdlevel[mtdlevel <= ndoses1 *
                                      ndoses2], which(p.true == target)) == F) ==
              0)
            selpercent1 = selpercent1 + 1
          if (sum(mtdlevel <= ndoses1 * ndoses2) == 2 &
              sum(is.element(mtdlevel[mtdlevel <= ndoses1 *
                                      ndoses2], which(p.true == target)) == F) ==
              0)
            selpercent2 = selpercent2 + 1
        }
        else {
          mtdlist[[triali]] = 99
          mtdtable = c(mtdtable, 99)
        }
      }
    }
    selpercent = round(selpercent * 100/ntrial, 2)
    if (JJ > KK) {
      nptsdose = matrix(0, nrow = ndoses1, ncol = ndoses2)
      for (i in seq(1, nrow(ntrial.nt), by = ndoses1)) nptsdose = nptsdose +
          ntrial.nt[i + 0:(ndoses1 - 1), -1]
      ntoxdose = matrix(0, nrow = ndoses1, ncol = ndoses2)
      for (i in seq(1, nrow(ntrial.yt), by = ndoses1)) ntoxdose = ntoxdose +
        ntrial.yt[i + 0:(ndoses1 - 1), -1]
      colnames(p.true) = paste("DoseB", 1:dim(t(p.true))[1],
                               sep = "")
      rownames(p.true) = paste("DoseA", 1:dim(t(p.true))[2],
                               sep = "")
      colnames(selpercent) = paste("DoseB", 1:dim(t(p.true))[1],
                                   sep = "")
      rownames(selpercent) = paste("DoseA", 1:dim(t(p.true))[2],
                                   sep = "")
      colnames(nptsdose) = paste("DoseB", 1:dim(t(p.true))[1],
                                 sep = "")
      rownames(nptsdose) = paste("DoseA", 1:dim(t(p.true))[2],
                                 sep = "")
      colnames(ntoxdose) = paste("DoseB", 1:dim(t(p.true))[1],
                                 sep = "")
      rownames(ntoxdose) = paste("DoseA", 1:dim(t(p.true))[2],
                                 sep = "")
      out = list(p.true = round(t(p.true), 2), selpercent = round(t(selpercent),
                                                                  2), npatients = round(t(nptsdose)/ntrial, 2),
                 ntox = round(t(ntoxdose)/ntrial, 2), totaltox = round(sum(ntoxdose/ntrial),
                                                                       1), totaln = round(sum(nptsdose/ntrial), 1))
      out2 = list()
      if (length(at.contour) > 0) {
        out2 = list(npercent.contour = paste(round(100 *
                                                     sum(nptsdose[at.contour])/sum(nptsdose), 1),
                                             "%", sep = ""), npercent.above.contour = paste(round(100 *
                                                                                                    sum(nptsdose[greater.than.contour])/sum(nptsdose),
                                                                                                  1), "%", sep = ""), npercent.below.contour = paste(round(100 *
                                                                                                                                                             sum(nptsdose[less.than.contour])/sum(nptsdose),
                                                                                                                                                           1), "%", sep = ""), pcs.contour = paste(round(100 *
                                                                                                                                                                                                           nselpercent/ntrial, 1), "%", sep = ""), flowchart = FALSE)
      }
      outnew = c(out, out2)
      class(outnew)<-"boin"
      return(outnew)
    }
    else {
      nptsdose = matrix(0, nrow = ndoses1, ncol = ndoses2)
      for (i in seq(1, nrow(ntrial.nt), by = ndoses1)) nptsdose = nptsdose +
          ntrial.nt[i + 0:(ndoses1 - 1), -1]
      ntoxdose = matrix(0, nrow = ndoses1, ncol = ndoses2)
      for (i in seq(1, nrow(ntrial.yt), by = ndoses1)) ntoxdose = ntoxdose +
        ntrial.yt[i + 0:(ndoses1 - 1), -1]
      out2 = list()
      if (length(at.contour) > 0) {
        out2 = list(npercent.contour = paste(round(100 *
                                                     sum(nptsdose[at.contour])/sum(nptsdose), 1),
                                             "%", sep = ""), npercent.above.contour = paste(round(100 *
                                                                                                    sum(nptsdose[greater.than.contour])/sum(nptsdose),
                                                                                                  1), "%", sep = ""), npercent.below.contour = paste(round(100 *
                                                                                                                                                             sum(nptsdose[less.than.contour])/sum(nptsdose),
                                                                                                                                                           1), "%", sep = ""), pcs.contour = paste(round(100 *
                                                                                                                                                                                                           nselpercent/ntrial, 1), "%", sep = ""))
      }
      rownames(p.true) = paste("DoseA", 1:dim(p.true)[1],
                               sep = "")
      colnames(p.true) = paste("DoseB", 1:dim(p.true)[2],
                               sep = "")
      rownames(selpercent) = paste("DoseA", 1:dim(p.true)[1],
                                   sep = "")
      colnames(selpercent) = paste("DoseB", 1:dim(p.true)[2],
                                   sep = "")
      rownames(nptsdose) = paste("DoseA", 1:dim(p.true)[1],
                                 sep = "")
      colnames(nptsdose) = paste("DoseB", 1:dim(p.true)[2],
                                 sep = "")
      rownames(ntoxdose) = paste("DoseA", 1:dim(p.true)[1],
                                 sep = "")
      colnames(ntoxdose) = paste("DoseB", 1:dim(p.true)[2],
                                 sep = "")
      out = list(p.true = apply(formatC(p.true, digits = 2,
                                        format = "f", width = 5), c(1, 2), as.numeric),
                 selpercent = apply(formatC(selpercent, digits = 2,
                                            format = "f", width = 5), c(1, 2), as.numeric),
                 npatients = apply(formatC(nptsdose/ntrial, digits = 2,
                                           format = "f", width = 5), c(1, 2), as.numeric),
                 ntox = apply(formatC(ntoxdose/ntrial, digits = 2,
                                      format = "f", width = 5), c(1, 2), as.numeric),
                 totaltox = as.numeric(formatC(sum(ntoxdose/ntrial),
                                               digits = 1, format = "f")), totaln = as.numeric(formatC(sum(nptsdose/ntrial),
                                                                                                       digits = 1, format = "f")), flowchart = FALSE)
      outnew = c(out, out2)
      class(outnew)<-"boin"
      return(outnew)
    }
  }
  set.seed(seed)
  JJ = nrow(p.true)
  KK = ncol(p.true)
  if (JJ > KK) {
    stop("p.true should be arranged in a way (i.e., rotated) such that the number of rows is less than or equal to the number of columns.")

  }
  if (mtd.contour == FALSE) {
    if (is.null(n.earlystop) == TRUE)
      n.earlystop = 100
    if (n.earlystop <= 6) {
      warning("the value of n.earlystop is too low to ensure good operating characteristics. Recommend n.earlystop = 9 to 18 ")
    }
    if (length(ncohort) > 1) {
      warning("ncohort is the total number of cohorts for the trial. Please enter a scalar.")
    }
    if (((JJ * KK) <= 4) & (sum(ncohort) <= 6)) {
      warning("the sample size is too small, which may lead to poor operating characteristics. Suggest to increase the number of cohort.")
    }
    if (((JJ * KK) > 4) & (sum(ncohort) <= 8)) {
      warning("the sample size is too small, which may lead to poor operating characteristics. Suggest to increase the number of cohort.")
    }
    out=get.oc.comb.boin(target = target, p.true = p.true,
                            ncohort = sum(ncohort), cohortsize = cohortsize,
                            n.earlystop = n.earlystop, startdose = startdose,
                            titration = titration,
                            p.saf = p.saf, p.tox = p.tox,
                            cutoff.eli = cutoff.eli, extrasafe = extrasafe,
                            offset = offset,boundMTD=boundMTD,
                            ntrial = ntrial)
    class(out)<-"boin"
    return(out)
  }
  if (mtd.contour == TRUE) {
    if (missing(ncohort) == TRUE) {
      constSeq = rep(round(4/cohortsize, 2), 20)
      dosespaceSeq = c(JJ + KK - 1, rep(KK - 1, JJ - 1))
      ncohort = ceiling(constSeq[1:JJ] * dosespaceSeq)
    }
    if (length(ncohort) != JJ) {
      stop("The vector length of ncohort doesn't match the number of subtrials (the number of dose matrix rows).\n           Please enter the number of ncohorts of each subtrial.")
    }
    if (((JJ * KK) <= 4) & (sum(ncohort) <= 6)) {
      warning("the sample size is too small, which may lead to poor operating characteristics. Suggest to increase the number of cohort.")
    }
    if (((JJ * KK) > 4) & (sum(ncohort) <= 8)) {
      warning("the sample size is too small, which may lead to poor operating characteristics. Suggest to increase the number of cohort.")
    }
    if (is.null(n.earlystop) == TRUE)
      n.earlystop = 12
    if (n.earlystop <= 6) {
      warning("the value of n.earlystop is too low to ensure good operating characteristics. Recommend n.earlystop = 9 to 18 ")
    }
    out=get.oc.comb.waterfall(p.true = p.true, target = target,
                          ncohort = ncohort, cohortsize = cohortsize, n.earlystop = n.earlystop,
                          cutoff.eli = cutoff.eli, p.saf = p.saf, p.tox = p.tox,
                          titration = titration,
                          extrasafe = extrasafe, offset = offset,boundMTD=boundMTD,
                          ntrial = ntrial)
    class(out)<-"boin"
    return(out)
  }
}

Try the BOIN package in your browser

Any scripts or data that you put into this service are public.

BOIN documentation built on Jan. 20, 2021, 1:06 a.m.