ACC <- function(var_exp, var_obs, lon = NULL, lat = NULL,
                lonlatbox = NULL, conf = TRUE, conftype = "parametric",
                nrmemb=100, nrspa=0, nrdate=0,
                blocspa=1, blocdate=1) {
  # Matrix var_exp & var_obs should have dimensions (nexp/nobs, nsdates, 
  # nltimes, nlat, nlon) or (nexp/nobs, nmember, nsdates, nltimes, nlat, nlon)
  # ACC computes the Anomaly Correlation Coefficient for the ensemble mean of
  # 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 and the second if it corresponds to
  #                                          the members dimension
  #   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.
  #   conf: TRUE/FALSE 
  #         confidence intervals or significance level provided or not
  #   conftype: "parametric" provides a confidence interval for the ACC computed 
  #                           by a Fisher transformation and a significance level 
  #                           for the ACC from a one-sided student-T distribution
  #              "bootstrap" provides a confidence interval for the ACC and MACC
  #                           computed from bootstrapping . 
  #                           To guarantee the statistical robustness of the result,
  #                           make sure that your experiments/oservations/startdates/
  #                           leadtimes always have the same number of members
  #                           (even if it is one, see nrmemb for more detail).
  #   nrmemb: number of drawing with replacement over the members (default: 100).
  #           If there is only one member in both observation and model, set to one.
  #   nrspa: number of drawing with replacement over the spatial dimensions.
  #          The re-sampling is done by blocs of size of blocspa
  #          (see blocpa for more details). If nrspa = 0 (Default)
  #          no spatial bootstraping is performed.
  #   nrdate: number of drawing with replacement over the stardates dimensions. 
  #          The re-sampling is done by blocs of size of blocdate.
  #          (only available if nrspa>0). If nrdate = 0 (Default)
  #          no temporal bootstraping is performed.
  #   blocspa:
  #   Size of the square bloc for the spatial re-sampling (Default: 1)
  #   blocdate}{
  #   Size of the bloc for the temporal re-sampling (Default: 1)
  #
  # Returns:
  #   $ACC: Anomaly Correlation Coefficient 
  #         if conf equal TRUE:
  #           Matrix with c(nexp, nobs, nsdates, nleadtimes, 4) dimensions.
  #           The fifth dimension of length 4 corresponds to the lower limit of
  #           the 95% confidence interval, the computed ACC, the upper limit of 
  #           the 95% confidence interval and the 95% significance level given by 
  #           a one-sided T-test (only for the parametric option).
  #           If the confidence interval is estimated with the bootstrap option,
  #           The size of the distribution used to calculate it will be:
  #           nrmemb*(nrspa + 1)
  #         if conf equal FALSE:
  #           Matrix with c(nexp, nobs, nsdates, nleadtimes) dimensions.
  #   $MACC: Mean Anomaly Correlation Coefficient 
  #          if conf equal TRUE:
  #            Matrix with c(nexp, nobs, nleadtimes, 3) dimensions.
  #            The fourth dimension of length 3 corresponds to the lower limit
  #            of the 95% confidence interval, the computed MACC and the upper 
  #            limit of the 95% confidence interval
  #           If the confidence interval is estimated with the bootstrap option,
  #           The size of the distribution used to calculate it will be:
  #           nrmemb*(nrspa + 1)*(nrdate +1)
  #          if conf equal FALSE:
  #            Matrix with c(nexp, nobs, nleadtimes) dimensions.
  #   $ACCdiff: Difference of anomaly Correlation Coefficient
  #         if conf equal TRUE and conftype="bootstrap":
  #           The difference between the first experience of the var_exp
  #           matrix and all other experiment is computed.
  #           The results have the dimensions:
  #           Matrix with c(nexp-1, nobs, nsdates, nleadtimes, 3) dimensions.
  #           The fourth dimension of length 3 corresponds to the lower limit
  #           of the 95% confidence interval of the difference,
  #           the difference and the upper 
  #           limit of the 95% confidence interval of the difference.
  #           If the confidence interval is estimated with the bootstrap option,
  #           The size of the distribution used to calculate it will be:
  #           nrmemb*(nrspa + 1)
  #           
  #   $MACCdiff: Difference of mean anomaly Correlation Coefficient
  #         if conf equal TRUE and conftype="bootstrap":
  #            The difference between the first experience of the var_exp
  #           matrix and all other experiment is computed.
  #           The results have the dimensions:
  #           Matrix with c(nexp-1, nobs, nleadtimes, 3) dimensions.
  #           The fourth dimension of length 3 corresponds to the lower limit
  #           of the 95% confidence interval of the difference,
  #           the difference and the upper 
  #           limit of the 95% confidence interval of the difference.
  #           If the confidence interval is estimated with the bootstrap option,
  #           The size of the distribution used to calculate it will be:
  #           nrmemb*(nrspa + 1)*(nrdate +1)
  # 
  # History:
  #   1.0  #  2013-08  (V. Guemas, vguemas@ic3.cat)  #  Original code
  #   1.1  #  2014-05  (C. Prodhomme, chloe.prodhomme@ic3.cat)  # optimisation
  #   1.2  #  2014-08  (V. Guemas, virginie.guemas@ic3.cat) # Bug-fixes : handling of
  #                              NA & selection of domain + Simplification of code
  #   1.3.0  #  2014-08  (V. Guemas, virginie.guemas@ic3.cat) # Boostrapping over members
  #   1.3.1  #  2014-09  (C. Prodhomme, chloe.prodhomme@ic3.cat) # Add comments
  #                                                            and minor style changes
  #   1.4    # 2014-09 (C. Prodhomme, chloe.prodhomme@ic3.cat) # Add spatial and time bootstrap

  # Security checks and getting dimensions
  dimsvar <- dim(var_exp)
  if (length(dimsvar) == 5) {
    checkfirst <- 2
  }else if (length(dimsvar) == 6) {
    checkfirst <- 3
    nmembexp <- dimsvar[2]
    nmembobs <- dim(var_obs)[2]
  }else{
    stop("var_exp & var_obs should have dimensions (nexp/nsobs, nsdates, nltimes, nlat, nlon) 
                          or  dimensions (nexp/nsobs, nmembers, nsdates, nltimes, nlat, nlon) ")
  }
  for (iind in checkfirst: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[checkfirst]
  nltimes <- dimsvar[checkfirst+1]
  nlat <- dimsvar[checkfirst+2]
  nlon <- dimsvar[checkfirst+3]

  # Selecting the domain
  if (is.null(lon) == FALSE & is.null(lat) == FALSE & 
      is.null(lonlatbox) == FALSE) {
    for (jind in 1:2) {
      while (lonlatbox[jind] < 0) {
        lonlatbox[jind] <- lonlatbox[jind] + 360
      }
      while (lonlatbox[jind] > 360) {
        lonlatbox[jind] <- lonlatbox[jind] - 360
      }
    }
    indlon <- which((lon >= lonlatbox[1] & lon <= lonlatbox[2]) | 
                    (lonlatbox[1] > lonlatbox[2] & (lon > lonlatbox[1] | 
                                                    lon < lonlatbox[2])))
    indlat <- which(lat >= lonlatbox[3] & lat <= lonlatbox[4])
  } else {
    indlon <- 1:nlon
    indlat <- 1:nlat
  }

  # Defining the outputs
  if(conf == TRUE) {
    ACC <- array(NA, dim = c(nexp, nobs, nsdates, nltimes, 4))
  } else {
    ACC <- array(NA, dim = c(nexp, nobs, nsdates, nltimes))
  }         
  MACCaux <- array(0, dim = c(nexp, nobs, nsdates, nltimes, 3))

  # Selecting the domain and preparing the ensemble-mean
  if (length(dimsvar) == 6) {
    var_exp <- array(var_exp[,,,,indlat, indlon], 
                     dim = c(nexp, nmembexp, nsdates, nltimes, 
                                length(indlat), length(indlon)))
    var_obs <- array(var_obs[,,,,indlat, indlon], 
                     dim = c(nobs, nmembobs, nsdates, nltimes, 
                                length(indlat), length(indlon)))
    tmp01 <- Mean1Dim(var_exp,2)
    tmp02 <- Mean1Dim(var_obs,2)
  }else{
    var_exp <- array(var_exp[,,,indlat, indlon], 
                     dim = c(nexp, nsdates, nltimes, 
                                length(indlat), length(indlon)))
    var_obs <- array(var_obs[,,,indlat, indlon], 
                     dim = c(nobs, nsdates, nltimes, 
                                length(indlat), length(indlon)))
    tmp01 <- var_exp
    tmp02 <- var_obs
  } 

  for( iobs in 1:nobs) {
    for( iexp in 1:nexp) {

      # tmp1 and tmp2 are splitted to handle NA before building tmp
      tmp1 <- array(tmp01[iexp, , , , ], dim = c(1, nsdates, nltimes,
                                    length(indlon) * length(indlat))) 
      tmp2 <- array(tmp02[iobs, , , , ], dim = c(1, nsdates, nltimes,
                                    length(indlon) * length(indlat)))

      # Variance(tmp1)should not take into account any point 
      # that is not available in tmp2 and therefore not accounted for 
      # in covariance(tmp1,tmp2) and vice-versa 
      tmp1[ is.na(tmp2) ] <- NA
      tmp2[ is.na(tmp1) ] <- NA

      tmp <- abind(tmp1, tmp2, along = 1)

      top <- apply(tmp, c(2, 3), function(x)
                      sum(x[1, ]*x[2, ], na.rm = TRUE) )

      bottom1 <- apply(tmp, c(2, 3), function(x)
                      sum(x[1, ]*x[1, ], na.rm = TRUE) )

      bottom2 <- apply(tmp, c(2, 3), function(x)
                      sum(x[2, ]*x[2, ], na.rm = TRUE) )
    
      bottom <- sqrt(bottom1 * bottom2 )
      
      ACCaux <- top / bottom

      #handle NA
      tmpallNA <- which(is.na(bottom) | bottom == 0)
      ACCaux[tmpallNA] <- NA
      
      top[tmpallNA] = NA
      bottom1[tmpallNA] = NA
      bottom2[tmpallNA] = NA

      #store the value to calculate the MACC
      MACCaux[iexp, iobs, , , 1] <- top
      MACCaux[iexp, iobs, , , 2] <- bottom1
      MACCaux[iexp, iobs, , , 3] <- bottom2
      
      if (conf == TRUE) {
        ACC[iexp, iobs, , , 2] <- ACCaux
        
        #calculate parametric confidence interval
        if (conftype == "parametric") {
        
          eno <- Mean1Dim( Eno(tmp2, 4), 1)
          t <- apply(eno, c(1, 2), 
                   function(x) qt(0.95, x - 2))
          enot <- abind(eno, t, along = 3)

          ACC[iexp, iobs, , , 4] <- apply(enot, c(1, 2), function(x)
                 sqrt((x[2] * x[2]) / ((x[2] * x[2]) + x[1] - 2)) )
        
          correno <- abind(ACCaux, eno, along = 3)
        
          ACC[iexp, iobs, , , 1] <- apply(correno, c(1, 2), function(x)
                  tanh(atanh(x[1]) + qnorm(0.975) / sqrt(x[2] - 3)) )
          ACC[iexp, iobs, , , 3] <- apply(correno, c(1, 2), function(x)
                  tanh(atanh(x[1]) + qnorm(0.025) / sqrt(x[2] - 3)) )
        }
      } else {
        ACC[iexp, iobs, , ] <- ACCaux
      }

    }
  }

  #   #na.rm should be TRUE to obtain a MACC even if a few
  #   #start dates are missing 
  topfinal <- apply(MACCaux, c(1, 2, 4), function(x) 
                                 sum(x[, 1], na.rm = TRUE) )
               
  bottomfinal <- apply(MACCaux, c(1, 2, 4), function(x) 
               sqrt(sum(x[, 2], na.rm = TRUE) * sum(x[, 3], na.rm = TRUE)))

  #to avoid that some NA are called NaN or Inf
  tmpNA <- which(is.na(bottomfinal) | bottomfinal == 0)

  MACC <- topfinal / bottomfinal 
  MACC[tmpNA] <- NA  
  
  if (conf == TRUE & conftype == "bootstrap") {
    if (length(dimsvar) != 6) {
      stop("Var_exp and var_obs must have a member dimension")
    }
    
    if ((nmembexp == 1) && (nmembobs == 1) && (nrmemb != 1)) {
      stop("the bootstrap over member is useless because var_exp and var_obs has only one member")
    }
      
    ndraw = nrmemb * (nrspa + 1)
    #create the matrix to store the random values
    ACC_draw  = array(dim=c(nexp,nobs,nsdates,nltimes,ndraw))

    #in case of temporal randoming the MACC distribution
    #is larger than ACC's one
    MACC_draw = array(dim=c(nexp,nobs,nltimes,ndraw * (nrdate + 1)))

    #put the member dimension first
    var_exp <- aperm(var_exp, c(2, 1, 3, 4, 5, 6))
    var_obs <- aperm(var_obs, c(2, 1, 3, 4, 5, 6))

    for (jmemb in 1:nrmemb) {

      #choose a randomly member index for each point of the matrix
      

      indexp <- array(sample(nmembexp, size = (nexp*nmembexp*nsdates*nltimes), 
                             replace = TRUE), dim = c(nmembexp, nexp, nsdates, nltimes, 
                                                    length(indlat), length(indlon)) )
      indobs <- array(sample(nmembobs, size = (nobs*nmembobs*nsdates*nltimes), 
                             replace = TRUE), dim = c(nmembobs, nobs, nsdates, nltimes,
                                                    length(indlat), length(indlon)) )

      #combine maxtrix of data and random index
      varindexp <- abind(var_exp, indexp, along = 7 )
      varindobs <- abind(var_obs, indobs, along = 7 )

      #select randomly the members for each point of the matrix
      #and put back the members dimension in second
      varexpmemb <- aperm( array( 
                    apply( varindexp, c(2, 3, 4, 5, 6), function(x) x[,1][x[,2]] ),
                                 dim = c(nmembexp, nexp, nsdates, nltimes, 
                                       length(indlat), length(indlon))),
                           c(2, 1, 3, 4, 5, 6)) 
      varobsmemb <- aperm( array(
                    apply( varindobs, c(2, 3, 4, 5, 6), function(x) x[,1][x[,2]] ),
                                 dim = c(nmembobs, nobs, nsdates, nltimes, 
                                       length(indlat), length(indlon))),
                           c(2, 1, 3, 4, 5, 6)) 

      
      #calculate the ACC of the randomized field
      tmpACC <- ACC(varexpmemb, varobsmemb, conf = FALSE)

      ACC_draw[,,,,jmemb] <- tmpACC$ACC
      MACC_draw[,,,jmemb] <- tmpACC$MACC
      #spatial bootstrap
      if (nrspa > 0) {
        #number of blocks needed to reconstruct the field
        nblon1=floor((length(indlon) - 1)/blocspa)+1
        nblat1=floor((length(indlat) - 1)/blocspa)+1
        
        for (jspa in 1:(nrspa)) {

          #construct the random field
          exprand <- array(NA,
                           dim=c(nexp, nmembexp,  nsdates, nltimes,
                             nblat1 * blocspa, nblon1 * blocspa))
          obsrand <- array(NA,
                           dim=c(nobs, nmembobs, nsdates, nltimes,
                             nblat1 * blocspa, nblon1 * blocspa))
          
          #choose of random points for exp
          vlonrand <- sample(1:(length(indlon) - blocspa),  nblon1,  replace=T)
          vlatrand <- sample(1:(length(indlat) - blocspa),  nblat1,  replace=T)

          
          #reconstruct the new random fields
          #   #loops over the elements of the blocks
          for (iblock in 0:(blocspa - 1)) {
            for (jblock in 0:(blocspa - 1)) {
              exprand[, , , , seq(1, length(indlat), blocspa)+jblock ,
                      seq(1, length(indlon), blocspa)+iblock]=
                        varexpmemb[, , , , vlatrand+jblock,
                                   vlonrand+iblock]
              
              obsrand[, , , , seq(1, length(indlat), blocspa)+jblock ,
                      seq(1, length(indlon), blocspa)+iblock]=
                        varobsmemb[, , , , vlatrand+jblock,
                                   vlonrand+iblock]
            }
          }
        
          #remove the useleses element of the last block

          exprand <- array(exprand[, , , , 1:length(indlat), 1:length(indlon)],
                           dim=c(nexp, nmembexp, nsdates, nltimes,
                             length(indlat), length(indlon)))
          
          obsrand <- array(obsrand[, , , , 1:length(indlat), 1:length(indlon)],
                           dim=c(nobs, nmembobs, nsdates, nltimes,
                             length(indlat), length(indlon)))

          
          #calcultate the ACC
          tmpACC=ACC(exprand, obsrand, conf = FALSE)
          ACC_draw[ , , , , jspa + (jmemb -1) * nrspa + nrmemb] <- (tmpACC$ACC)
          MACC_draw[, , , jspa + (jmemb -1) * nrspa + nrmemb] <- (tmpACC$MACC)
          #calculate the temporal bootstrap for MACC
          #   #if nrdate=0 there is no temporal bootstrap applied
          if (nrdate > 0){
            nbdate=floor((nsdates - 1)/blocdate)+1
            for ( idate in 1:(nrdate)) {

              #number of block to recontruct the time serie
              exprandtime <- array(NA,
                                   dim=c(nexp, nmembexp, nbdate * blocdate, nltimes,
                                     length(indlat), length(indlon) ))
              obsrandtime <- array(NA,
                                   dim=c(nobs, nmembobs, nbdate * blocdate, nltimes,
                                     length(indlat), length(indlon) ))
              #choose of random time for exp
              timerand <- sample(1:(nsdates - blocdate),  nbdate,  replace=TRUE)
          
              #reconstruct the randomized in time fields from the spatial
              #randomized field
              #   #loops over the elements of the blocks
              for (iblocdate in 0:(blocdate - 1)) {
                exprandtime[, , seq(1, nsdates, blocdate)+iblocdate, , , ]=
                  exprand[, , timerand + iblocdate, , ,]
                obsrandtime[, , seq(1, nsdates, blocdate)+iblocdate, , , ]=
                  obsrand[, , timerand + iblocdate, , ,]
              }
          
            # remove the useleses element of the last block
              exprandtime <- array(exprandtime[, , 1:nsdates, , , ],
                                   dim=c(nexp, nmembexp, nsdates,
                                     nltimes,
                                     length(indlat), length(indlon)))
          
              obsrandtime <- array(obsrandtime[, , 1:nsdates, , , ],
                                   dim=c(nobs,  nmembexp, nsdates,
                                     nltimes,
                                     length(indlat), length(indlon)))

              ACCtmp=ACC(exprandtime, obsrandtime, conf = FALSE)
              iMACC=( nrmemb * ( nrspa + 1) + idate +
                     (jspa - 1) * nrdate +
                     (jmemb - 1) * nrdate * nrmemb )
              MACC_draw[ , , , iMACC] <- (ACCtmp$MACC)

            } #loop nrdate
          } #test nrdate

        } # loop nrspa
      } # test nrspa
    } #loop nrmemb
    
    
    #calculate the confidence interval
    ACC[ , , , , 3] <- apply(ACC_draw, c(1, 2, 3, 4), function(x)
                                       quantile(x, 0.975, na.rm = TRUE))  
    ACC[ , , , , 1] <- apply(ACC_draw, c(1, 2, 3, 4), function(x)
                                       quantile(x, 0.025, na.rm = TRUE))  

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

    # difference

    if (nexp > 1){
      ACCdiff <- array(NA, dim = c(nexp-1, nobs, nsdates, nltimes, 3))
      MACCdiff <- array(NA, dim = c(nexp-1, nobs, nltimes, 3))

      ACC_ref <- array(ACC[1, , , , 2], dim=c(nobs, nsdates, nltimes))
      MACC_ref <- array(MACC[1, , , 2], dim=c(nobs, nltimes))
      
      ACC_comp <- array(ACC[2:nexp, , , , 2], dim=c(nexp-1, nobs, nsdates, nltimes))
      MACC_comp <- array(MACC[2:nexp, , , 2], dim=c(nexp-1, nobs, nltimes))

      ACCdiff[, , , , 2] <- ACC_comp - InsertDim(ACC_ref, 1, nexp-1)
      MACCdiff[, , , 2] <- MACC_comp - InsertDim(MACC_ref, 1, nexp-1)
      
      # confidence interval
      ACCdrawref <- array(ACC_draw[1, , , , ], dim=c(nobs, nsdates, nltimes, ndraw))
      ACCdrawcomp <- array(ACC_draw[2:nexp, , , , ], dim=c(nobs, nsdates, nltimes, ndraw))

    
      MACCdrawref <- array(ACC_draw[1, , , , ], dim=c(nobs, nltimes, ndraw))
      MACCdrawcomp <- array(MACC_draw[2:nexp, , , ], dim=c(nobs, nltimes, ndraw))


    
    #calc the difference the the drawing
      ACCdiffdraw <- array(ACC_draw[2:(nexp-1), , , , ],
                           dim=c(nexp-1, nobs, nsdates, nltimes, ndraw)) -
                             InsertDim(ACCdrawref, 1, nexp-1)
      MACCdiffdraw <- array(MACC_draw[2:(nexp-1), , , ],
                            dim=c(nexp-1, nobs, nltimes, ndraw)) -
                              InsertDim(MACCdrawref, 1, nexp-1) 

    #calculate the confidence interval for the differences
      ACCdiff[ , , , , 3] <- apply(ACCdiffdraw, c(1, 2, 3, 4), function(x)
                                   quantile(x, 0.975, na.rm = TRUE))  
      ACCdiff[ , , , , 1] <- apply(ACCdiffdraw, c(1, 2, 3, 4), function(x)
                                   quantile(x, 0.025, na.rm = TRUE))  
      
      MACCdiff[ , , , 3] <- apply(MACCdiffdraw, c(1, 2, 3), function(x)
                                  quantile(x, 0.975, na.rm = TRUE))  
      MACCdiff[ , , , 1] <- apply(MACCdiffdraw, c(1, 2, 3), function(x)
                                  quantile(x, 0.025, na.rm = TRUE))


      
      invisible(list(ACC = ACC, MACC = MACC,
                     ACCdiff = ACCdiff, MACCdiff = MACCdiff))
    
    }else{
    
      invisible(list(ACC = ACC, MACC = MACC))
    }
  }else{
    
    invisible(list(ACC = ACC, MACC = MACC))
  }
  
   
 
}
