ACC_bootstrap <- function(var_exp, var_obs, sampmet, lon = NULL, lat = NULL,
                          lonlatbox = NULL, nrspa=100, nrdate=1,
                          blocspa=1, blocdate=1, center = TRUE) {
# Matrix var_exp & var_obs should have dimensions (nexp/nobs,  nsdates,  
# nltimes,  nlat,  nlon).
# ACC computes the Anomaly Correlation Coefficient for each jexp in 
# 1:nexp and each jobs in 1:nobs which gives nexp x nobs ACC for each 
# startdate and each leadtime.
# A domain can be selected by providing the list of longitudes/latitudes
# (lon/lat) of the grid together with the corner of the domain:
#   lonlatbox = c(lonmin,  lonmax,  latmin,  latmax)

# 
# Args:
#   var_exp: Matrix of experimental data.
#   var_obs: Matrix of observational data,  same dimensions as var_exp except 
#            along the first dimension.
#   sampmet: "bootstrap"/"permutation". If "bootstrap" set, observation
#            and experiment fields are re-sampled together. The original
#            correlation should be included with a 95% probability in the
#            confidence interval. In this case, the p-value can not be use to
#            estimate the significance. Instead of using the p-value, the
#            ACC or MACC will be considered significant if the confident
#            interval does not include 0.
#            If "permutation" set, a simple spatial permutation of the
#            experiment field is done. The confidence interval will be center
#            over 0 and can not be used directly as a "real confidence interval".
#            The p-value is used to estimate the significance of the result and
#            is defined as the percent of the sampled distribition higher than the
#            estimated correlation.
#   lon: Array of longitudes of the var_exp/var_obs grids,  optional.
#   lat: Array of latitudes of the var_exp/var_obs grids,  optional.
#   lonlatbox: Domain to select c(lonmin,  lonmax,  latmin,  latmax),  optional.
#   nrspa: number of sample for the bootstrap  (default: 1000)
#   blocspa: Length of spatial blocks used for resampling the field (default: 1)
#   center: TRUE/FALSE if TRUE (default) compute the center anomaly correlation
#           coefficient (using pearson correlation).
#           if FALSE, the mean map anomalies are not substracted from both
#           oberved and and experiement fields.
#   
#           
# 
# Returns:
#   $ACC: Matrix with c(nexp,  nobs,  nsdates,  nleadtimes,  4) dimensions.
#       The fivth dimension of length 4 corresponds to the lower limit of
#       the 95\% confidence interval, which is the 2.5 percentile of the
#       bootstrap distribution,  the computed ACC,  the upper limit of 
#       the 95\% confidence interval, which is the 97.5 percentile of the
#       bootstrap distribution,  and the p-value.
#       (see the detail of sampmet arguments for more details)
# $MACC:Matrix with c(nexp,  nobs,  nleadtimes,  4) dimensions.
#       The fourth dimension of length 4 corresponds to the lower limit of
#       the 95\% confidence interval, which is the 2.5 percentile of the
#       bootstrap distribution,  the computed ACC,  the upper limit of 
#       the 95\% confidence interval, which is the 97.5 percentile of the
#       bootstrap distribution,  and the p-value.
#       (see the detail of sampmet arguments for more details)
# History:
#   1.0  #  2013-08  (C.Prodhomme,  chloe.prodhomme@ic3.cat)  #  Original code
  
  dimsvar <- dim(var_exp)
  if (length(dimsvar) != 5) {
    stop("var_exp & var_obs should have dimensions
        (nexp/nsobs, nsdates, nltimes,  nlat,  nlon) ")
  }
  for (iind in 2:length(dimsvar)) {
    if (dim(var_obs)[iind] != dimsvar[iind]) {
      stop("var_exp & var_obs must have same dimensions except the first one
          (number of experiments or number of observational datasets) ")
    }
  }
  nexp <- dimsvar[1]
  nobs <- dim(var_obs)[1]
  nsdates <- dimsvar[2]
  nltimes <- dimsvar[3]
  nlat <- dimsvar[4]
  nlon <- dimsvar[5]

#number of blocks needed to reconstruct the field
  nblon1=floor((nlon - 1)/blocspa)+1
  nblat1=floor((nlat - 1)/blocspa)+1

#number of block to recontruct the time serie
  nbdate=floor((nsdates - 1)/blocdate)+1

#create the matrix to store the distribution
  distriACC <- array(NA,  dim=c(nexp, nobs, nsdates, nltimes,
                            nrspa ))
  distriMACC <- array(NA,  dim=c(nexp, nobs, nltimes,
                             nrspa * nrdate + 1))

#output
  ACC <- array(NA,  dim=c(nexp, nobs, nsdates, nltimes, 4))
  MACC <- array(NA,  dim=c(nexp, nobs, nltimes))

  ACCdiff <- array(NA,  dim=c(nexp-1, nobs, nsdates, nltimes, 4))
  MACCdiff <- array(NA,  dim=c(nexp-1, nobs, nltimes, 4))

  #the sampling must be different for each experiment
  #to compute the significance of difference
  
  for (iexp in 1:nexp) {
    #bootstrap
    for (irand in 1:nrspa) {
      exprand <- array(NA,
                       dim=c(1, nsdates, nltimes,
                         nblat1 * blocspa, nblon1 * blocspa))
      obsrand <- array(NA,
                       dim=c(nobs, nsdates, nltimes,
                         nblat1 * blocspa, nblon1 * blocspa))
      #choose of random points for exp
      vlonrand <- sample(1:(nlon - blocspa),  nblon1,  replace=T)
      vlatrand <- sample(1:(nlat - blocspa),  nblat1,  replace=T)

      #reconstruct the new random field
      #   #loops over the elements of the blocks
      for (iblock in 0:(blocspa - 1)) {
        for (jblock in 0:(blocspa - 1)) {
          exprand[, , , seq(1, nlat, blocspa)+jblock ,
                  seq(1, nlon, blocspa)+iblock]=
                    var_exp[iexp, , , vlatrand+jblock,
                            vlonrand+iblock]
        }
      }
      
      exprand <- array(exprand[, , , 1:nlat, 1:nlon],
                       dim=c(1, nsdates, nltimes,
                         nlat, nlon))
      
      # random the observation field
      if(sampmet == "bootstrap") {
        for (iblock in 0:(blocspa - 1)) {
          for (jblock in 0:(blocspa - 1)) {
            obsrand[, , , seq(1, nlat, blocspa)+jblock ,
                    seq(1, nlon, blocspa)+iblock]=
                      var_obs[, , , vlatrand+jblock,
                              vlonrand+iblock]
          }
        }
        obsrand <- array(obsrand[, , , 1:nlat, 1:nlon],
                         dim=c(nobs, nsdates, nltimes,
                           nlat, nlon))
      #  keep the original field 
      } else if (sampmet == "permutation") {
        obsrand=var_obs
      } else {
        stop("This sampling method is not available (only permutation or bootstrap)")
      }
      
      #calcultate the ACC
      ACC=ACC(exprand, obsrand, conf = FALSE, center = center)
      distriACC[iexp, , , , irand] <- (ACC$ACC)
      distriMACC[iexp, , ,  (irand - 1) * nrdate + 1] <- (ACC$MACC)

      #calculate the temporal bootstrap for MACC
      #   #if nrdate=1 there is no temporal bootstrap applied
      if (nrdate > 1){
        for ( idate in 2:nrdate) {

          exprandtime <- array(NA,
                           dim=c(1, nbdate * blocdate, nltimes,
                             nlat, nlon ))
          obsrandtime <- array(NA,
                           dim=c(nobs, nbdate * blocdate, nltimes,
                             nlat, nlon ))
          #choose of random time for exp
          timerand <- sample(1:(nsdates - blocdate),  nbdate,  replace=TRUE)
          
          #reconstruct the randomized in time field from the spatial
          #randomized field
          #   #loops over the elements of the blocks
          for (iblocdate in 0:(blocdate - 1)) {
            exprandtime[1, seq(1, nsdates, blocdate)+iblocdate, , , ]=
              exprand[iexp, timerand + iblocdate, , ,]
          }
          
          
          exprandtime <- array(exprandtime[, 1:nltimes, , , ],
                           dim=c(1, nsdates, nltimes,
                             nlat, nlon))
          
          #random the observation field
          if(sampmet == "bootstrap") {

            for (iblocdate in 0:(blocdate - 1)) {
              obsrandtime[, seq(1, nsdates, blocdate)+iblocdate, , , ]=
                obsrand[, timerand + iblocdate, , ,]
            }
            obsrandtime <- array(obsrandtime[, , 1:nltimes, , ],
                             dim=c(nobs, nsdates, nltimes,
                               nlat, nlon))
          #keep the original field 
          } else if (sampmet == "permutation") {
            obsrand=var_obs
          } else {
            stop("This sampling method is not available (only permutation or bootstrap)")
          }
          #calcultate the ACC
          ACC=ACC(exprand, obsrand, conf = FALSE, center = center)
          distriMACC[iexp, , ,  (irand - 1) * nrdate + 1 + idate] <- (ACC$MACC)
        }
      }
    }
  }
    #output definition
    ACC <- array(NA,  dim=c(nexp, nobs, nsdates, nltimes, 4))
    MACC <- array(NA,  dim=c(nexp, nobs, nltimes, 4))

#calcultate the ACC and MACC of original data
    ACCori <- ACC(var_exp,var_obs, conf = FALSE, center = center)
    ACC[, , , , 2] <- (ACCori$ACC)
    MACC[, , ,  2] <- (ACCori$MACC)


#calculate the p-value from the bootstrap distribution
    distriACCaux=abind(distriACC, array(ACC[, , , , 2], 
      dim=c(nexp, nobs, nsdates, nltimes, 1)), along=5)
    distriMACCaux=abind(distriMACC, array(MACC[ , , ,  2], 
      dim=c(nexp, nobs, nltimes, 1)), along=4)
    
    ACC[, , , , 4] <- apply(distriACCaux, c(1, 2, 3, 4),
                            function(x)
                            sum(abs(x[1:nrspa]) > abs(x[nrspa+1]),
                                na.rm = TRUE)
                            / nrspa)
    
    MACC[, , ,  4] <- apply(distriMACCaux, c(1, 2, 3),
                            function(x)
                            sum(abs(x[1:(nrspa * nrdate + 1)]) >
                                abs(x[nrspa * nrdate + 2]),
                                na.rm=TRUE)
                            / (nrspa * nrdate + 1))
    
#calcultate the confidence interval

    ACC[, , , , 3] <- apply(distriACC, c(1, 2, 3, 4),  function(x)
                            quantile(x,  c(0.975), na.rm=TRUE))
    ACC[, , , , 1] <- apply(distriACC, c(1, 2, 3, 4),  function(x)
                            quantile(x,  c(0.025), na.rm=TRUE))
    MACC[, , ,  1] <- apply(distriMACC, c(1, 2, 3),  function(x)
                            quantile(x,  c(0.025), na.rm=TRUE))
    MACC[, , ,  3] <- apply(distriMACC, c(1, 2, 3),  function(x)
                            quantile(x,  c(0.975), na.rm=TRUE))

#Calculate the significance of the difference
    if (nexp > 1) {
      
      ACCdiff[, , , , 2] =
        array(ACC[2:nexp, , , , 2], dim =
              c(nexp-1, nobs, nsdates, nltimes)) -
                InsertDim(
                  array(ACC[1, , , , 2], dim =
                        c(nobs, nsdates, nltimes)), 1, nexp - 1)

      ttest=apply(distriACC, c(2, 3, 4),
        function(x)
        apply(array(x[2:nexp,], dim=c(nexp-1, dim(x)[2])), c(1),
              function(y) {
                taux=t.test(x[1, ], y = y,
                  mu = mean(x-y, na.rm = TRUE))
                return(c(taux$conf.int[1], taux$conf.int[2], taux$p.value))
              })
        )
      ACCdiff[, , , , c(1, 3, 4)] = ttest



      MACCdiff[, , , 2] =
        array(MACC[2:nexp, , , 2], dim =
              c(nexp-1, nobs, nltimes)) -
                InsertDim(array(MACC[1, , , 2], dim =
                                c(nobs, nltimes)) ,1, nexp - 1)

      
      ttest=apply(distriMACC, c(2, 3),
        function(x)
        apply(array(x[2:nexp,], dim=c(nexp-1, dim(x)[2])), c(1),
              function(y) {
                taux=t.test(x[1, ], y = y,
                  mu = mean(x-y, na.rm = TRUE))
                return(c(taux$conf.int[1], taux$conf.int[2], taux$p.value))
              })
        )
      MACCdiff[, , , c(1, 3, 4)] = ttest
    }
    
    invisible(list(ACC = ACC,  MACC = MACC, ACCdiff = ACCdiff,
                   MACCdiff = MACCdiff))

  }

