#main user-level function for multiple hypothesis testing

MTP<-function(X,W=NULL,Y=NULL,Z=NULL,Z.incl=NULL,Z.test=NULL,na.rm=TRUE,test="t.twosamp.unequalvar",robust=FALSE,standardize=TRUE,alternative="two.sided",psi0=0,typeone="fwer",k=0,q=0.1,fdr.method="conservative",alpha=0.05,smooth.null=FALSE,nulldist="boot.cs",B=1000,ic.quant.trans=FALSE,MVN.method="mvrnorm",penalty=1e-6,method="ss.maxT",get.cr=FALSE,get.cutoff=FALSE,get.adjp=TRUE,keep.nulldist=TRUE,keep.rawdist=FALSE,seed=NULL,cluster=1,type=NULL,dispatch=NULL,marg.null=NULL,marg.par=NULL,keep.margpar=TRUE,ncp=NULL,perm.mat=NULL,keep.index=FALSE,keep.label=FALSE){
  ##sanity checks / formatting
  #X
  if(missing(X)) stop("Argument X is missing")
  if(inherits(X,"eSet")){ 
    if(is.character(Y)) Y<-pData(X)[,Y]
    if(is.character(Z)){
      if(Z%in%Y){
        Z<-Z[!(Z%in%Y)]
	warning(paste("Outcome Y=",Y,"should not be included in the covariates Z=",Z,". Removing Y from Z",sep=""))
	}
      Z<-pData(X)[,Z]
    }
    X<-exprs(X)
  }
  X<-as.matrix(X)
  dx<-dim(X)
  if(length(dx)==0) stop("dim(X) must have positive length")
  p<-dx[1]
  n<-dx[2]
  #W
  if(!is.null(W)){
    W[W<=0]<-NA
    if(is.vector(W) & length(W)==n) W <- matrix(rep(W,p),nrow=p,ncol=n,byrow=TRUE)
    if(is.vector(W) & length(W)==p) W <- matrix(rep(W,n),nrow=p,ncol=n)
    if(test%in%c("f","f.block","f.twoway","t.cor","z.cor")){
      warning("Weights can not be used with F-tests or tests of correlation parameters, arg W is being ignored.")
      W<-NULL
    }
  }
  #Y
  if(!is.null(Y)){
    if(is.Surv(Y)){
      if(test!="coxph.YvsXZ") stop(paste("Test ",test," does not work with a survival object Y",sep=""))
    }
    else{
      Y<-as.matrix(Y)
      if(ncol(Y)!=1) stop("Argument Y must be a vector")
    }
    if(nrow(Y)!=n) stop("Outcome Y has length ",nrow(Y),", not equal to n=",n)
  }
  if(test=="t.pair") n <- dx[2]/2
  #Z
  if(!is.null(Z)){
    Z<-as.matrix(Z)
    if(nrow(Z)!=n) stop("Covariates in Z have length ",nrow(Z),", not equal to n=",n,"\n")
    #Z.incl tells which columns of Z to include in model
    if(is.null(Z.incl)) Z.incl<-(1:ncol(Z))
    if(length(Z.incl)>ncol(Z)) stop("Number of columns in Z.incl ",length(Z.incl)," exceeds ncol(Z)=",ncol(Z))
    if(is.logical(Z.incl)) Z.incl<-(1:ncol(Z))[Z.incl]
    if(is.character(Z.incl) & length(Z.incl)!=sum(Z.incl%in%colnames(Z))) stop(paste("Z.incl=",Z.incl," names columns not in Z",sep=""))
    Za<-Z[,Z.incl]
    #Z.test tells which column of Z to test for an association
    if(test=="lm.XvsZ"){
      if(is.null(Z.test)){
        warning(paste("Z.test not specified, testing for association with variable in first column of Z:",colnames(Z)[1],sep=""))
	Z.test<-1
      }
      if(is.logical(Z.test)) Z.test<-(1:ncol(Z))[Z.test]
      if(is.character(Z.test) & !(Z.test%in%colnames(Z))) stop(paste("Z.test=",Z.test," names a column not in Z",sep=""))
      if(is.numeric(Z.test) & !(Z.test%in%(1:ncol(Z)))) stop("Value of Z.test must be >0 and <",ncol(Z))
      if(Z.test%in%Z.incl){
        Z.incl<-Z.incl[!(Z.incl%in%Z.test)]
	Za<-Z[,Z.incl]
      }
      Za<-cbind(Z[,Z.test],Za)
    }
    Z<-Za
    rm(Za)
  }
  #test
  TESTS<-c("t.onesamp","t.twosamp.equalvar","t.twosamp.unequalvar","t.pair","f","f.block","f.twoway","lm.XvsZ","lm.YvsXZ","coxph.YvsXZ","t.cor","z.cor")
  test<-TESTS[pmatch(test,TESTS)]
  if(is.na(test)) stop(paste("Invalid test, try one of ",TESTS,sep=""))
  #robust + see below with choice of nulldist
  if(test=="coxph.YvsXZ" & robust==TRUE)
    warning("No robust version of coxph.YvsXZ, proceding with usual version")
  #temp until fix
  if((test=="t.onesamp" | test=="t.pair") & robust==TRUE)
    stop("Robust test statistics currently not available for one-sample or two-sample paired test statistics.")
  #alternative
  ALTS<-c("two.sided","less","greater")
  alternative<-ALTS[pmatch(alternative,ALTS)]
  if(is.na(alternative)) stop(paste("Invalid alternative, try one of ",ALTS,sep=""))
  #null values
  if(length(psi0)>1) stop(paste("In current implementation, all hypotheses must have the same null value. Number of null values: ",length(psi0),">1",sep=""))
  #Error rate
  ERROR<-c("fwer","gfwer","tppfp","fdr")
  typeone<-ERROR[pmatch(typeone,ERROR)]
  if(is.na(typeone)) stop(paste("Invalid typeone, try one of ",ERROR,sep=""))
  if(any(alpha<0) | any(alpha>1)) stop("Nominal level alpha must be between 0 and 1")
  nalpha<-length(alpha)
  reject<-
    if(nalpha) array(dim=c(p,nalpha),dimnames=list(rownames(X),paste("alpha=",alpha,sep="")))
    if(test=="z.cor" | test=="t.cor") matrix(nrow=0,ncol=0) # deprecated for correlations, rownames now represent p choose 2 edges - too weird and clunky in current state for output.
    else matrix(nrow=0,ncol=0)
  if(typeone=="gfwer"){
    if(get.cr==TRUE) warning("Confidence regions not currently implemented for gFWER")
    if(get.cutoff==TRUE) warning("Cut-offs not currently implemented for gFWER")
    get.cr<-get.cutoff<-FALSE
    if(k<0) stop("Number of false positives can not be negative")
    if(k>=p) stop(paste("Number of false positives must be less than number of tests=",p,sep=""))
    if(length(k)>1){
      k<-k[1]
      warning("can only compute gfwer(k) adjp for one value of k at a time (using first value), try fwer2gfwer() function for multiple k")
    }
  }
  if(typeone=="tppfp"){
    if(get.cr==TRUE) warning("Confidence regions not currently implemented for TPPFP")
    if(get.cutoff==TRUE) warning("Cut-offs not currently implemented for TPPFP")
    get.cr<-get.cutoff<-FALSE
    if(q<0) stop("Proportion of false positives, q, can not be negative")
    if(q>1) stop("Proportion of false positives, q, must be less than 1")
    if(length(q)>1){
      q<-q[1]
      warning("Can only compute tppfp adjp for one value of q at a time (using first value), try fwer2tppfp() function for multiple q")
    }
  }
  if(typeone=="fdr"){
    if(!nalpha) stop("Must specify a nominal level alpha for control of FDR")
    if(get.cr==TRUE) warning("Confidence regions not currently implemented for FDR")
    if(get.cutoff==TRUE) warning("Cut-offs not currently implemented for FDR")
    get.cr<-get.cutoff<-FALSE
  }		
  #null distribution
  NULLS<-c("boot","boot.cs","boot.ctr","boot.qt","ic","perm")
  nulldist<-NULLS[pmatch(nulldist,NULLS)]
  if(is.na(nulldist)) stop(paste("Invalid nulldist, try one of ",NULLS,sep=""))
  if(nulldist=="boot"){
    nulldist <- "boot.cs"
    warning("nulldist='boot' is deprecated and now corresponds to 'boot.cs'. Proceeding with default center and scaled null distribution.")
  }
  if(nulldist!="perm" & test=="f.block") stop("f.block test only available with permutation null distribution. Try test=f.twoway")
  if((nulldist=="perm" | nulldist=="ic") & keep.rawdist==TRUE) stop("Test statistics distribution estimation using keep.rawdist=TRUE is only available with a bootstrap-based null distribution")
  if(nulldist=="boot.qt" & robust==TRUE) stop("Quantile transform method requires parametric marginal nulldist.  Set robust=FALSE")
  if(nulldist=="boot.qt" & standardize==FALSE) stop("Quantile transform method requires standardized test statistics.  Set standardize=TRUE")
  if(nulldist=="ic" & robust==TRUE) stop("Influence curve null distributions available only for (parametric) t-statistics.  Set robust=FALSE")
  if(nulldist=="ic" & standardize==FALSE) stop("Influence curve null distributions available only for (standardized) t-statistics.  Set standardize=TRUE")
  if(nulldist=="ic" & (test=="f" | test=="f.twoway" | test=="f.block" | test=="coxph.YvsXZ")) stop("Influence curve null distributions available only for tests of mean, regression and correlation parameters. Cox PH also not yet implemented.")
  if(nulldist!="ic" & (test=="t.cor" | test=="z.cor")) stop("Tests of correlation parameters currently only implemented for influence curve null distributions")
  if((test!="t.cor" & test!="z.cor") & keep.index) warning("Matrix of indices only returned for tests of correlation parameters")
  ### specifically for sampling null test statistics with IC nulldist
  MVNS <- c("mvrnorm","Cholesky")
  MVN.method <- MVNS[pmatch(MVN.method,MVNS)]
  if(is.na(MVN.method)) stop("Invalid sampling method for IC-based MVN null test statistics.  Try either 'mvrnorm' or 'Cholesky'")
  #methods
  METHODS<-c("ss.maxT","ss.minP","sd.maxT","sd.minP")
  method<-METHODS[pmatch(method,METHODS)]
  if(is.na(method)) stop(paste("Invalid method, try one of ",METHODS,sep=""))
  #estimate and conf.reg
  ftest<-FALSE
  if(test=="f" | test=="f.block"){
    ftest<-TRUE
    if(get.cr) stop("Confidence intervals not available for F tests, try get.cr=FALSE")
    if(!is.null(W)) warning("Weighted F tests not yet implemented, proceding with unweighted version")
  }

  
  #permutation null distribution - self contained in this if statement
  if(nulldist=="perm"){
    if(method=="ss.minP" | method=="ss.maxT") stop("Only step-down procedures are currently available with permutation nulldist")
    if(smooth.null) warning("Kernal density p-values not available with permutation nulldist")
    if(get.cr) warning("Confidence regions not available with permutation nulldist")
    if(get.cutoff) warning("Cut-offs not available with permutation nulldist")
    #if(keep.nulldist) warning("keep.nulldist not available with permutation nulldist")
    ptest<-switch(test,
                  t.onesamp=stop("One sample t-test not available with permutation nulldist"),
                  t.twosamp.equalvar=ifelse(robust,"wilcoxon","t.equalvar"),
                  t.twosamp.unequalvar="t",
                  t.pair="pairt",
                  f="f",
                  f.block="blockf",
                  f.twoway=stop("f.twoway not available with permutation nulldist"),
                  lm.XvsZ=stop("lm.XvsZ not available with permutation nulldist"),
                  lm.YvsXZ=stop("lm.YvsXZ not available with permutation nulldist"),
                  coxph.YvsXZ=stop("coxph.YvsXZ not available with permutation nulldist"),
                  t.cor=stop("t.cor not available with permutation nulldist"),
                  z.cor=stop("z.cor not available with permutation nulldist")
                  )
    pside<-switch(alternative,two.sided="abs",less="lower",greater="upper")
    pnonpara<-
      if(robust)"y"
      else "n"
    if(any(is.na(Y))){
      bad<-is.na(Y)
      Y<-Y[!bad]
      X<-X[,!bad]
      warning("No NAs allowed in Y, these observations have been removed.")
    }
    presult<-switch(method,
                    sd.maxT=mt.maxT(X,classlabel=Y,test=ptest,side=pside,B=B,nonpara=pnonpara),
                    sd.minP=mt.minP(X,classlabel=Y,test=ptest,side=pside,B=B,nonpara=pnonpara)
                    )
    if(typeone=="fwer" & nalpha){
      for(a in 1:nalpha) reject[,a]<-(presult$adjp<=alpha[a])
    }
    if(typeone=="gfwer"){
      presult$adjp<-fwer2gfwer(presult$adjp,k)
      if(nalpha){
        for(a in 1:nalpha) reject[,a]<-(presult$adjp<=alpha[a])
      }
      if(!get.adjp)
        presult$adjp<-vector("numeric",0)
    }
    if(typeone=="tppfp"){
      presult$adjp<-fwer2tppfp(presult$adjp,q)
      if(nalpha){
        for(a in 1:nalpha) reject[,a]<-(presult$adjp<=alpha[a])
      }
      if(!get.adjp)
        presult$adjp<-vector("numeric",0)
    }
    if(typeone=="fdr"){
      temp<-fwer2fdr(presult$adjp,fdr.method,alpha)
      reject<-temp$reject
      if(!get.adjp) presult$adjp<-vector("numeric",0)
      else presult$adjp<-temp$adjp
      rm(temp)
    }			
    #output results
    orig<-order(presult$index)
    if(keep.label) label <- as.numeric(Y)
    else label <- vector("numeric",0)
    out<-new("MTP",statistic=presult$teststat[orig],estimate=vector("numeric",0),sampsize=n,rawp=presult$rawp[orig],adjp=presult$adjp[orig],conf.reg=array(dim=c(0,0,0)),cutoff=matrix(nrow=0,ncol=0),reject=as.matrix(reject[orig,]),rawdist=matrix(nrow=0,ncol=0),nulldist=matrix(nrow=0,ncol=0),nulldist.type="perm",marg.null=vector("character",0),marg.par=matrix(nrow=0,ncol=0),label=label,index=matrix(nrow=0,ncol=0),call=match.call(),seed=vector("integer",0))
  }
  
  else{ # This should apply to all other MTP calls using the bootstrap and IC nulldists.
    if(nulldist=="boot.qt"){ # get parameter vals for quantile transform.
      # Get parameter values for the quantile transformed nulldist
      if(!is.null(marg.par)){
        if(is.matrix(marg.par)) marg.par <- marg.par
        if(is.vector(marg.par)) marg.par <- matrix(rep(marg.par,p),nrow=p,ncol=length(marg.par),byrow=TRUE)
        }
      if(is.null(ncp)) ncp = 0
      if(!is.null(perm.mat)){ 
        if(dim(X)[1]!=dim(perm.mat)[1]) stop("perm.mat must same number of rows as X.")
        }
    
      nstats <- c("t.twosamp.unequalvar","z.cor","lm.XvsZ","lm.YvsXZ","coxph.lmYvsXZ")
      tstats <- c("t.onesamp","t.twosamp.equalvar","t.pair","t.cor")
      fstats <- c("f","f.block","f.twoway")
      
      # If default , set values of marg.null to pass on.
      if(is.null(marg.null)){
	  if(any(nstats == test)) marg.null="normal"
	  if(any(tstats == test)) marg.null="t"
	  if(any(fstats == test)) marg.null="f"
        }
      else{ # Check to see that user-supplied entries make sense.  
        MARGS <- c("normal","t","f","perm")
        marg.null <- MARGS[pmatch(marg.null,MARGS)]
        if(is.na(marg.null)) stop("Invalid marginal null distribution. Try one of: normal, t, f, or perm")
        if(any(tstats==test) & marg.null == "f") stop("Choice of test stat and marginal nulldist do not match")
        if(any(fstats==test) & (marg.null == "normal" | marg.null=="t")) stop("Choice of test stat and marginal nulldist do not match")
        if(marg.null=="perm" & is.null(perm.mat)) stop("Must supply a matrix of permutation test statistics if marg.null='perm'")
        if(marg.null=="f" & ncp < 0) stop("Cannot have negative noncentrality parameter with F distribution.")
      }
    
      # If default (=NULL), set values of marg.par. Return as m by 1 or 2 matrix.
      if(is.null(marg.par)){
		marg.par <- switch(test,
                          t.onesamp = n-1,
                          t.twosamp.equalvar = n-2,
                          t.twosamp.unequalvar = c(0,1),
                          t.pair = floor(n/2-1),
                          f = c(length(is.finite(unique(Y)))-1,dim(X)[2]- length(is.finite(unique(Y))) ),
                          f.twoway = {
                            c(length(is.finite(unique(Y)))-1, dim(X)[2]-(length(is.finite(unique(Y)))*length(gregexpr('12', paste(Y, collapse=""))[[1]]))-2)
                            },
                          lm.XvsZ = c(0,1),
                          lm.YvsXZ = c(0,1),
                          coxph.YvsXZ = c(0,1),
                          t.cor = n-2,
                          z.cor = c(0,1)
                          )
      marg.par <- matrix(rep(marg.par,dim(X)[1]),nrow=dim(X)[1],ncol=length(marg.par),byrow=TRUE)
              }
     else{ # Check that user-supplied values of marg.par make sense (marg.par != NULL)
       if((marg.null=="t" | marg.null=="f") & any(marg.par[,1]==0)) stop("Cannot have zero df with t or F distributions. Check marg.par settings")
       if(marg.null=="t" & dim(marg.par)[2]>1) stop("Too many parameters for t distribution.  marg.par should have length 1.")
       if((marg.null=="f" | marg.null=="normal") & dim(marg.par)[2]!=2) stop("Incorrect number of parameters defining marginal null distribution.  marg.par should have length 2.")
     }
    }

    ##making a closure for the particular test
    theta0<-0
    tau0<-1
    stat.closure<-switch(test,
                         t.onesamp=meanX(psi0,na.rm,standardize,alternative,robust),
                         t.twosamp.equalvar=diffmeanX(Y,psi0,var.equal=TRUE,na.rm,standardize,alternative,robust),
                         t.twosamp.unequalvar=diffmeanX(Y,psi0,var.equal=FALSE,na.rm,standardize,alternative,robust),
                         t.pair={
                           uY<-sort(unique(Y))
                           if(length(uY)!=2) stop("Must have two class labels for this test")
                           if(trunc(ncol(X)/2)!=ncol(X)/2) stop("Must have an even number of samples for this test")
                           X<-X[,Y==uY[2]]-X[,Y==uY[1]]
                           Y<-NULL
                           n<-dim(X)[2]
                           meanX(psi0,na.rm,standardize,alternative,robust)
                         },
                         f={
                           theta0<-1
                           tau0<-2/(length(unique(Y))-1)
                           FX(Y,na.rm,robust)
                         },
                         f.twoway={
                           theta0<-1
                           tau0 <- 2/((length(unique(Y))*length(gregexpr('12', paste(Y, collapse=""))[[1]]))-1)
                           twowayFX(Y,na.rm,robust)
                         },
                         lm.XvsZ=lmX(Z,n,psi0,na.rm,standardize,alternative,robust),
                         lm.YvsXZ=lmY(Y,Z,n,psi0,na.rm,standardize,alternative,robust),
                         coxph.YvsXZ=coxY(Y,Z,psi0,na.rm,standardize,alternative),
                         t.cor=NULL,
                         z.cor=NULL)
    ##computing observed test statistics
    if(test=="t.cor" | test=="z.cor") obs<-corr.Tn(X,test=test,alternative=alternative,use="pairwise")
    else obs<-get.Tn(X,stat.closure,W)
    ##or computing influence curves
    if(nulldist=="ic"){
      rawdistn <- matrix(nrow=0,ncol=0)
      nulldistn<-switch(test,
                        t.onesamp=corr.null(X,W,Y,Z,test="t.onesamp",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat),
                        t.pair=corr.null(X,W,Y,Z,test="t.pair",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat),
                        t.twosamp.equalvar=corr.null(X,W,Y,Z,test="t.twosamp.equalvar",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat),
                        t.twosamp.unequalvar=corr.null(X,W,Y,Z,test="t.twosamp.unequalvar",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat),
                        lm.XvsZ=corr.null(X,W,Y,Z,test="lm.XvsZ",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat),
                        lm.YvsXZ=corr.null(X,W,Y,Z,test="lm.YvsXZ",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat),
                        t.cor=corr.null(X,W,Y,Z,test="t.cor",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat),
                        z.cor=corr.null(X,W,Y,Z,test="z.cor",alternative,use="pairwise",B,MVN.method,penalty,ic.quant.trans,marg.null,marg.par,perm.mat)
                        )
    }

    ## Cluster Checking
    if ((!is.numeric(cluster))&(!inherits(cluster,c("MPIcluster", "PVMcluster", "SOCKcluster"))))
       stop("Cluster argument must be integer or cluster object")
    ## Create cluster if cluster > 1 and load required packages on nodes
    if(is.numeric(cluster)){
      if(cluster>1){
    ## Check installation of packages
      have_snow <- qRequire("snow")
      if(!have_snow) stop("The package snow is required to use a cluster. Either snow is not installed or it is not in the standard library location.")
      if (is.null(type))
         stop("Must specify type argument to use a cluster. Alternatively, provide a cluster object as the argument to cluster.")
      if (type=="SOCK")
         stop("Create desired cluster and specify cluster object as the argument to cluster directly.")
      if ((type!="PVM")&(type!="MPI"))
         stop("Type must be MPI or PVM")
      else if (type=="MPI"){
         have_rmpi <- qRequire("Rmpi")
         if(!have_rmpi) stop("The package Rmpi is required for the specified type. Either Rmpi is not installed or it is not in the standard library location.")
      }
      else if (type=="PVM"){
         have_rpvm <- qRequire("rpvm")
         if(!have_rpvm) stop("The package rpvm is required for the specified type. Either rpvm is not installed or it is not in the standard library location.")
      }
      cluster <- makeCluster(cluster, type)
      clusterEvalQ(cluster, {library(Biobase); library(multtest)})
      if (is.null(dispatch)) dispatch=0.05
      }
    }
    else if(inherits(cluster,c("MPIcluster", "PVMcluster", "SOCKcluster"))){
      clusterEvalQ(cluster, {library(Biobase); library(multtest)})
      if (is.null(dispatch)) dispatch=0.05
    }

    ##computing the nonparametric bootstrap (null) distribution
    if(nulldist=="boot.cs" | nulldist=="boot.ctr" | nulldist=="boot.qt"){
      nulldistn<-boot.null(X,Y,stat.closure,W,B,test,nulldist,theta0,tau0,marg.null,marg.par,ncp,perm.mat,alternative,seed,cluster,dispatch,keep.nulldist,keep.rawdist)
     if(inherits(cluster,c("MPIcluster", "PVMcluster", "SOCKcluster")))  stopCluster(cluster)
    rawdistn <- nulldistn$rawboot
    nulldistn <- nulldistn$muboot
    }

    
    ##performing multiple testing
    #rawp values
    rawp<-apply((obs[1,]/obs[2,])<=nulldistn,1,mean)
    if(smooth.null & (min(rawp,na.rm=TRUE)==0)){
      zeros<-(rawp==0)
      if(sum(zeros)==1){
        den<-density(nulldistn[zeros,],to=max(obs[1,zeros]/obs[2,zeros],nulldist[zeros,],na.rm=TRUE),na.rm=TRUE)
	rawp[zeros]<-sum(den$y[den$x>=(obs[1,zeros]/obs[2,zeros])])/sum(den$y)
      }
      else{
        den<-apply(nulldistn[zeros,],1,density,to=max(obs[1,zeros]/obs[2,zeros],nulldistn[zeros,],na.rm=TRUE),na.rm=TRUE)
	newp<-NULL
	stats<-obs[1,zeros]/obs[2,zeros]
	for(i in 1:length(den)){
          newp[i]<-sum(den[[i]]$y[den[[i]]$x>=stats[i]])/sum(den[[i]]$y)
	}
        rawp[zeros]<-newp		
      }
      rawp[rawp<0]<-0
    }
    #c, cr, adjp values
    pind<-ifelse(typeone!="fwer",TRUE,get.adjp)
    if(method=="ss.maxT") out<-ss.maxT(nulldistn,obs,alternative,get.cutoff,get.cr,pind,alpha)
    if(method=="ss.minP") out<-ss.minP(nulldistn,obs,rawp,alternative,get.cutoff,get.cr,pind,alpha)
    if(method=="sd.maxT") out<-sd.maxT(nulldistn,obs,alternative,get.cutoff,get.cr,pind,alpha)
    if(method=="sd.minP") out<-sd.minP(nulldistn,obs,rawp,alternative,get.cutoff,get.cr,pind,alpha)
    if(typeone=="fwer" & nalpha & (test!="t.cor" & test !="z.cor")){
      for(a in 1:nalpha) reject[,a]<-(out$adjp<=alpha[a])
    }
    #augmentation procedures
    if(typeone=="gfwer"){
      out$adjp<-as.numeric(fwer2gfwer(out$adjp,k))
      out$c<-matrix(nrow=0,ncol=0)
      out$cr<-array(dim=c(0,0,0))
      if(nalpha){
        for(a in 1:nalpha) reject[,a]<-(out$adjp<=alpha[a])
      }
      if(!get.adjp) out$adjp<-vector("numeric",0)
    }
    if(typeone=="tppfp"){
      out$adjp<-as.numeric(fwer2tppfp(out$adjp,q))
      out$c<-matrix(nrow=0,ncol=0)
      out$cr<-array(dim=c(0,0,0))
      if(nalpha){
        for(a in 1:nalpha) reject[,a]<-(out$adjp<=alpha[a])
      }
      if(!get.adjp) out$adjp<-vector("numeric",0)
    }
    if(typeone=="fdr"){
      out$c<-matrix(nrow=0,ncol=0)
      out$cr<-array(dim=c(0,0,0))
      temp<-fwer2fdr(out$adjp,fdr.method,alpha)
      reject<-temp$reject
      if(!get.adjp) out$adjp<-vector("numeric",0)
      else out$adjp<-temp$adjp
      rm(temp)
    }
    #output results
    if(!keep.nulldist) nulldistn<-matrix(nrow=0,ncol=0)
    if(!keep.rawdist) rawdist<-matrix(nrow=0,ncol=0)
    if(nulldist!="boot.qt"){  
      marg.null <- vector("character")
      marg.par <- matrix(nrow=0,ncol=0)
    }
    if(!keep.label) label <- vector("numeric",0)
    if(!keep.index) index <- matrix(nrow=0,ncol=0)
    if(test!="z.cor" & test !="t.cor") index <- matrix(nrow=0,ncol=0)
    if(keep.index & (test!="z.cor" | test !="t.cor")){
      index <- t(combn(p,2))
      colnames(index) <- c("Var1","Var2")
    }
    names(out$adjp)<-names(rawp)
    estimates <- obs[3,]*obs[1,]
    if(ftest) estimates <- vector("numeric",0)
    if(test=="t.onesamp" | test=="t.pair") estimates <- obs[3,]*obs[1,]/sqrt(n)
    out<-new("MTP",statistic=(obs[3,]*obs[1,]/obs[2,]),
      estimate=estimates,
      sampsize=n,rawp=rawp,adjp=out$adjp,conf.reg=out$cr,cutoff=out$c,reject=reject,
      rawdist=rawdistn,nulldist=nulldistn,nulldist.type=nulldist,
      marg.null=marg.null,marg.par=marg.par,label=label,index=index,
      call=match.call(),seed=as.integer(seed))
  }
  return(out)
}

#funtions to compute cutoffs and adjusted pvals

ss.maxT<-function(null,obs,alternative,get.cutoff,get.cr,get.adjp,alpha=0.05){
  p<-dim(null)[1]
  B<-dim(null)[2]
  nalpha<-length(alpha)
  mT<-apply(null,2,max)
  getc<-matrix(nrow=0,ncol=0)
  getcr<-array(dim=c(0,0,0))
  getp<-vector(mode="numeric")
  if(get.cutoff | get.cr){
    getc<-array(dim=c(p,nalpha),dimnames=list(dimnames(null)[[1]],paste("alpha=",alpha,sep="")))
    if(get.cr) getcr<-array(dim=c(p,2,nalpha),dimnames=list(dimnames(null)[[1]],c("LB","UB"),paste("alpha=",alpha,sep="")))
    for(a in 1:nalpha){
      getc[,a]<-rep(quantile(mT,pr=(1-alpha[a])),p)
      if(get.cr) getcr[,,a]<-cbind(ifelse(rep(alternative=="less",p),rep(-Inf,p),obs[3,]*obs[1,]-getc[,a]*obs[2,]),ifelse(rep(alternative=="greater",p),rep(Inf,p),obs[3,]*obs[1,]+getc[,a]*obs[2,]))
    }
  }
  if(get.adjp) getp<-apply((obs[1,]/obs[2,])<=matrix(mT,nrow=p,ncol=B,byrow=TRUE),1,mean)
  if(!get.cutoff) getc<-matrix(nrow=0,ncol=0)
  list(c=getc,cr=getcr,adjp=getp)
}

ss.minP<-function(null,obs,rawp,alternative,get.cutoff,get.cr,get.adjp,alpha=0.05){
  p<-dim(null)[1]
  B<-dim(null)[2]
  nalpha<-length(alpha)
  getc<-matrix(nrow=0,ncol=0)
  getcr<-array(dim=c(0,0,0))
  getp<-vector(mode="numeric")
  R<-apply(null,1,rank)
  if(get.cutoff | get.cr){
    getc<-array(dim=c(p,nalpha),dimnames=list(dimnames(null)[[1]],paste("alpha=",alpha,sep="")))
    if(get.cr) getcr<-array(dim=c(p,2,nalpha),dimnames=list(dimnames(null)[[1]],c("LB","UB"),paste("alpha=",alpha,sep="")))
    for(a in 1:nalpha){
      q<-quantile(apply(R,1,max),1-alpha[a])
      for(j in 1:p){
        getc[j,a]<-min(c(null[j,R[,j]>=q],max(null[j,])))
      }
      if(get.cr) getcr[,,a]<-cbind(ifelse(rep(alternative=="less",p),rep(-Inf,p),obs[3,]*obs[1,]-getc[,a]*obs[2,]),ifelse(rep(alternative=="greater",p),rep(Inf,p),obs[3,]*obs[1,]+getc[,a]*obs[2,]))
    }
  }
  if(get.adjp){
    R<-matrix(apply((B+1-R)/B,1,min),nrow=p,ncol=B,byrow=TRUE)
    getp<-apply(rawp>=R,1,mean)
  }
  if(!get.cutoff) getc<-matrix(nrow=0,ncol=0)
  list(c=getc,cr=getcr,adjp=getp)
}

sd.maxT<-function(null,obs,alternative,get.cutoff,get.cr,get.adjp,alpha=0.05){
  p<-dim(null)[1]
  B<-dim(null)[2]
  nalpha<-length(alpha)
  ord<-rev(order(obs[1,]/obs[2,]))
  mT<-null[ord[p],]
  getc<-matrix(nrow=0,ncol=0)
  getcr<-array(dim=c(0,0,0))
  getp<-vector(mode="numeric")
  if(get.cutoff | get.cr){
    getc<-array(dim=c(p,nalpha),dimnames=list(dimnames(null)[[1]],paste("alpha=",alpha,sep="")))
    for(a in 1:nalpha) getc[ord[p],a]<-quantile(mT,pr=1-alpha[a])
  }
  if(get.adjp) getp[ord[p]]<-mean((obs[1,]/obs[2,])[ord[p]]<=mT)
  for(j in (p-1):1){
    mT<-pmax(mT,null[ord[j],])
    if(get.adjp) getp[ord[j]]<-mean((obs[1,ord[j]]/obs[2,ord[j]])<=mT)
    if(get.cutoff | get.cr){
      for(a in 1:nalpha) getc[ord[j],a]<-quantile(mT,pr=(1-alpha[a]))
    }
  }
  c.ind<-rep(TRUE,nalpha)
  for(j in 2:p){
    if(get.adjp) getp[ord[j]]<-max(getp[ord[j]],getp[ord[j-1]])
    if(get.cutoff | get.cr){
      for(a in 1:nalpha){
        if(c.ind[a]){
          if((obs[1,]/obs[2,])[ord[j-1]]<=getc[ord[j-1],a]){
            getc[ord[j:p],a]<-Inf
            c.ind[a]<-FALSE
          }
	}
      }
    }
  }
  if(get.cr){
    getcr<-array(dim=c(p,2,nalpha),dimnames=list(dimnames(null)[[1]],c("LB","UB"),paste("alpha=",alpha,sep="")))
    for(a in 1:nalpha){
      getcr[,,a]<-cbind(ifelse(rep(alternative=="less",p),rep(-Inf,p),obs[3,]*obs[1,]-getc[,a]*obs[2,]),ifelse(rep(alternative=="greater",p),rep(Inf,p),obs[3,]*obs[1,]+getc[,a]*obs[2,]))
    }
  }
  if(!get.cutoff) getc<-matrix(nrow=0,ncol=0)
  list(c=getc,cr=getcr,adjp=getp)
}

sd.minP<-function(null,obs,rawp,alternative,get.cutoff,get.cr,get.adjp,alpha=0.05){
  p<-dim(null)[1]
  B<-dim(null)[2]
  nalpha<-length(alpha)
  ord<-order(rawp)
  R<-apply(null,1,rank) #B x p
  mR<-R[,ord[p]]
  getc<-matrix(nrow=0,ncol=0)
  getcr<-array(dim=c(0,0,0))
  getp<-vector(mode="numeric")
  if(get.cutoff | get.cr){
    getc<-array(dim=c(p,nalpha),dimnames=list(dimnames(null)[[1]],paste("alpha=",alpha,sep="")))
    for(a in 1:nalpha){
      q<-quantile(mR,pr=1-alpha[a])
      getc[ord[p],a]<-min(c(null[ord[p],R[,ord[p]]>=q],max(null[ord[p],])))
    }
  }
  if(get.adjp){
    mP<-(B+1-mR)/B
    getp[ord[p]]<-mean(rawp[ord[p]]>=mP)
  }
  for(j in (p-1):1){
    mR<-pmax(mR,R[,ord[j]])
    if(get.adjp){
      mP<-(B+1-mR)/B
      getp[ord[j]]<-mean(rawp[ord[j]]>=mP)
    }
    if(get.cutoff | get.cr){
      for(a in 1:nalpha){
        q<-quantile(mR,pr=1-alpha[a])
	getc[ord[j],a]<-min(c(null[ord[j],R[,ord[j]]>=q],max(null[ord[j],])))
      }
    }
  }
  c.ind<-rep(TRUE,nalpha)
  for(j in 2:p){
    if(get.adjp) getp[ord[j]]<-max(getp[ord[j]],getp[ord[j-1]])
    if(get.cutoff | get.cr){
      for(a in 1:nalpha){
        if(c.ind[a]){
          if((obs[1,]/obs[2,])[ord[j-1]]<=getc[ord[j-1],a]){
            getc[ord[j:p],a]<-Inf
            c.ind[a]<-FALSE
          }
	}
      }
    }
  }
  if(get.cr){
    getcr<-array(dim=c(p,2,nalpha),dimnames=list(dimnames(null)[[1]],c("LB","UB"),paste("alpha=",alpha,sep="")))
    for(a in 1:nalpha){
      getcr[,,a]<-cbind(ifelse(rep(alternative=="less",p),rep(-Inf,p),obs[3,]*obs[1,]-getc[,a]*obs[2,]),ifelse(rep(alternative=="greater",p),rep(Inf,p),obs[3,]*obs[1,]+getc[,a]*obs[2,]))
    }
  }
  if(!get.cutoff) getc<-matrix(nrow=0,ncol=0)
  list(c=getc,cr=getcr,adjp=getp)
}

#functions to convert FWER adjp to AMTP (gFWER, TPPFP) adjp:
fwer2gfwer<-function(adjp,k=0){
  ord<-order(adjp)
  m<-length(adjp)
  if(any(k>=m)) stop(paste("number of rejections k=",k," must be less than number of hypotheses=",m,sep=""))
  newp<-NULL
  for(j in k) newp<-rbind(newp,c(rep(0,j),adjp[ord[1:(m-j)]]))
  rownames(newp)<-k
  colnames(newp)<-ord
  newp<-matrix(newp[,order(ord)],ncol=m,byrow=FALSE)
  return(t(newp))
}

fwer2tppfp<-function(adjp,q=0.05){
  ord<-order(adjp)
  m<-length(adjp)
  newp<-NULL
  if(any(q>1)|any(q<0)) stop(paste("proportion of false positives q=",q," must be in [0,1]",sep=""))
  for(l in q) newp<-rbind(newp,adjp[ord][ceiling((1:m)*(1-l))])
  rownames(newp)<-q
  colnames(newp)<-names(ord)
  newp<-matrix(newp[,order(ord)],ncol=m,byrow=FALSE)
  return(t(newp))
}

#function to compute rejection indicator for FDR methods
fwer2fdr<-function(adjp,method="both",alpha=0.05){
  get.cons<-function(adjp,alpha,ord,M,nalpha){
    newp<-NULL
    for(m in 1:M){
      #try ceiling/floor
      k<-if(m%%2) 0:((m-1)/2) else 0:(m/2)
      f<-2*adjp[ord][m-k]
      u<-2*(k+1)/m
      l<-2*k/m
      if(sum(f<=u)){
        ind<-min(which(f<=u))
	newp[ord[m]]<-
	if(f[ind]>=l[ind]) f[ind]
	else l[ind]
      }
      else newp[ord[m]]<-1
    }
    newp[newp>1]<-1
    a<-alpha/2
    rejections<-matrix(nrow=M,ncol=nalpha)
    for(i in 1:nalpha) rejections[,i]<-(fwer2tppfp(adjp,a[i])<=a[i])
    return(list(reject=rejections,adjp=newp))
  }
  get.restr<-function(adjp,alpha,ord,M,nalpha){
    newp<-NULL
    ginv<-function(x) 1-(1-x)^2
    for(m in 1:M){
      k<-m:1
      f<-adjp[ord][k]
      u<-1-(k-1)/m
      l<-1-k/m
      if(sum(f<=u)){
        ind<-min(which(f<=u))
	newp[ord[m]]<-
          if(f[ind]>=l[ind]) ginv(f[ind])
          else ginv(l[ind])
      }
      else
	newp[ord[m]]<-1
    }
    newp[newp>1]<-1
    a<-1-sqrt(1-alpha)
    rejections<-matrix(nrow=M,ncol=nalpha)
    for(i in 1:nalpha) rejections[,i]<-(fwer2tppfp(adjp,a[i])<=a[i])
    return(list(reject=rejections,adjp=newp))
  }
  ord<-order(adjp)
  nalpha<-length(alpha)
  M<-length(adjp)
  if(method=="both"){
    rejections<-array(dim=c(M,nalpha,2),dimnames=list(NULL,paste("alpha=",alpha,sep=""),c("conservative","restricted")))
    newp<-matrix(nrow=M,ncol=2,dimnames=list(NULL,c("conservative","restricted")))
    temp<-get.cons(adjp,alpha,ord,M,nalpha)
    rejections[,,"conservative"]<-temp$reject
    newp[,"conservative"]<-temp$adjp
    temp<-get.restr(adjp,alpha,ord,M,nalpha)
    rejections[,,"restricted"]<-temp$reject
    newp[,"restricted"]<-temp$adjp
    rm(temp)
  }
  else{
    rejections<-matrix(nrow=M,ncol=nalpha,dimnames=list(NULL,paste("alpha=",alpha,sep="")))
    newp<-NULL
    if(method=="conservative") temp<-get.cons(adjp,alpha,ord,M,nalpha)
    else temp<-get.restr(adjp,alpha,ord,M,nalpha)
    rejections<-temp$reject
    newp<-temp$adjp
    rm(temp)
  }
  return(list(reject=rejections,adjp=newp))
}
