0.1 Preliminaries

Load libraries, functions and data.

# Load helper functions
  
knitr::opts_chunk$set(fig.path = "figs/", echo = TRUE, message = FALSE, warnings = FALSE)

# load helper functions, data and do preliminary processing of the ensemble.
source('JULES-ES-1p0-common.R')
library(sensitivity)
# Helper functions

# rotate a matric 90 degrees clockwise for plotting
rotate <- function(x) t(apply(x, 2, rev))



sensvar = function(oaat_pred, n, d){
  # Calculate variance as a global sensitivity meansure
  out = rep(NA,d)
  for(i in 1:d){
    ix = seq(from = ((i*n) - (n-1)), to =  (i*n), by = 1)
    out[i] = var(oaat_pred$mean[ix])
  }
  out
}


twoStep_sens <- function(X, y, n=21, predtype = 'UK', nugget=NULL, nuggetEstim=FALSE, noiseVar=NULL, seed=NULL, trace=FALSE, maxit=100,
                        REPORT=10, factr=1e7, pgtol=0.0, parinit=NULL, popsize=100){
  # Sensitivity analysis with twoStep emulator. 
  # Calculates the variance of the output varied one at a time across each input.
  d = ncol(X)
  X_norm <- normalize(X)
  X_oaat <- oaat_design(X_norm, n, med = TRUE)
  colnames(X_oaat) = colnames(X)
  
  twoStep_em = twoStep_glmnet(X=X, y=y, nugget=nugget, nuggetEstim=nuggetEstim, noiseVar=noiseVar,
                              seed=seed, trace=trace, maxit=maxit,
                              REPORT=REPORT, factr=factr, pgtol=pgtol,
                              parinit=parinit, popsize=popsize)
  
  oaat_pred = predict(twoStep_em$emulator, newdata = X_oaat, type = predtype)
  
  sens = sensvar(oaat_pred = oaat_pred, n=n, d=d)
  out = sens
  out
}



oaatSensvarKm <- function(X, y, n = 21, med = TRUE, hold = NULL,  formula = ~., predtype = 'UK', ...){
  # one-at-a-time sensitivity summary with a standard dicekriging emulator
  
  d = ncol(X)
  X_norm <- normalize(X)
  X_oaat <- oaat_design(X_norm, n, med = med, hold = hold)
  colnames(X_oaat) = colnames(X)
  

  em <- km(formula = formula, design = X, response = y, ...)
  
  oaat_pred = predict(em, newdata = X_oaat, type = predtype)
  
  sens = sensvar(oaat_pred = oaat_pred, n=n, d=d)
  out = sens
  out
  
}

oaatSensvarKmList <- function(X, em_list, n = 21, med = TRUE, hold = NULL,  formula = ~., predtype = 'UK', ...){
  # one-at-a-time sensitivity summary with a standard dicekriging emulator
  
  d = ncol(X)
  X_oaat <- oaat_design(X, n, med = med, hold = hold)
  colnames(X_oaat) = colnames(X)
  
  em <- em_list[[i]]
  
  oaat_pred = predict(em, newdata = X_oaat, type = predtype)
  
  sens = sensvar(oaat_pred = oaat_pred, n=n, d=d)
  out = sens
  out
  
}





oaatSensvarSummaryPlot <- function(oat_sens_mat){
  
  # relies on rotate(), fields,  which need sorting for transfer to package
  
  ynames <- rownames(oat_sens_mat)
  xnames <- colnames(oat_sens_mat)
  
  normsens <- normalize(t(oat_sens_mat))
  normsens_mean <- apply(normsens,1, mean)
  
  sort_ix <- sort(normsens_mean, decreasing = TRUE, index.return = TRUE)
  
  par(mar = c(15,12,5,1), mfrow = c(1,2))
  
  layout(matrix(c(1,1,2), ncol = 3, nrow = 1))
  
  image(rotate(normsens[sort_ix$ix, ]), axes = FALSE, col = blues)
  
  axis(1, at = seq(from = 0, to = 1, length.out = length(ynames)), labels = ynames, las = 3, cex.axis = 1.2)
  axis(2, at = seq(from = 1, to = 0, length.out = length(xnames)), labels = xnames[sort_ix$ix], las = 1, cex.axis = 1.2)
  mtext('One-at-a-time sensitivity', side = 3, adj = 0, line = 2, cex = 1)
  
  lab_ix <- (1:length(xnames)) - 0.5
  
  par(yaxs = 'i', mar = c(15,1,5,5))
  plot(rev(normsens_mean[sort_ix$ix]), lab_ix, xlab = 'mean oaat variance (normalized)', ylab = '', ylim = c(0,length(xnames)), type = 'n', yaxt = 'n')
  abline(h = lab_ix, col = 'grey', lty = 'dashed')
  points( rev(normsens_mean[sort_ix$ix]),lab_ix, col = zissou5[1], pch = 19, cex = 1.5)
  
  image.plot(legend.only = TRUE,
             zlim = c(0,1),
             col = blues,
             legend.args = list(text = 'Relative sensitivity', side = 3, line = 1),
             horizontal = TRUE
  )
  
}




sensMatSummaryPlot <- function(sens_mat, col = blues, maintext = 'Sensitivity Matrix', xlab = 'sensitivity summary'){
  # Summary plots of a sensitivity matrix
  
  
  # relies on rotate(), fields,  which need sorting for transfer to package
  
  ynames <- rownames(sens_mat)
  xnames <- colnames(sens_mat)
  
  normsens <- normalize(t(sens_mat))
  normsens_mean <- apply(normsens,1, mean)
  
  sort_ix <- sort(normsens_mean, decreasing = TRUE, index.return = TRUE)
  
  par(mar = c(15,12,5,1), mfrow = c(1,2))
  
  layout(matrix(c(1,1,2), ncol = 3, nrow = 1))
  
  image(rotate(normsens[sort_ix$ix, ]), axes = FALSE, col = col)
  
  axis(1, at = seq(from = 0, to = 1, length.out = length(ynames)), labels = ynames, las = 3, cex.axis = 1.2)
  axis(2, at = seq(from = 1, to = 0, length.out = length(xnames)), labels = xnames[sort_ix$ix], las = 1, cex.axis = 1.2)
  mtext(maintext, side = 3, adj = 0, line = 2, cex = 1)
  
  lab_ix <- (1:length(xnames)) - 0.5
  
  par(yaxs = 'i', mar = c(15,1,5,5))
  plot(rev(normsens_mean[sort_ix$ix]), lab_ix, xlab = xlab, ylab = '', ylim = c(0,length(xnames)), type = 'n', yaxt = 'n')
  abline(h = lab_ix, col = 'grey', lty = 'dashed')
  points( rev(normsens_mean[sort_ix$ix]),lab_ix, col = zissou5[1], pch = 19, cex = 1.5)
  
  image.plot(legend.only = TRUE,
             zlim = c(0,1),
             col = col,
             legend.args = list(text = 'Relative sensitivity', side = 3, line = 1),
             horizontal = TRUE
  )
}




bp_convert <- function(fastmodel){
  # get the FAST summary into an easier format for barplot
  fast_summ <- print(fastmodel)
  fast_diff <- fast_summ[ ,2] - fast_summ[ ,1]
  fast_bp <- t(cbind(fast_summ[ ,1], fast_diff))
  fast_bp
}


multiFAST<- function(X, Y, fit_list = NULL, n = 1000){
  
  # Generate a design for the FAST99 analysis
  X_fast <- fast99(model = NULL, factors = colnames(X), n = n,
                   q = "qunif", q.arg = list(min = 0, max = 1))
  
  
  if(is.null(fit_list)){
    fit_list <- createKmFitList(X = X, Y = Y)
  }
  
  else{
  fit_list <- fit_list
  }
  
  fast_tell_list <- vector(mode = 'list', length = ncol(Y))
  
  
  for(i in 1:ncol(Y)){
    
    fit <- fit_list[[i]]
    # Predict the response at the FAST99 design points using the emulator
    pred_fast <- predict(fit, newdata = X_fast$X, type = 'UK')
    
    # Calculate the sensitivity indices
    fast_tell <- tell(X_fast, pred_fast$mean)
    
    fast_tell_list[[i]] <- fast_tell
    
  }
  
  return(list(fit_list = fit_list, fast_tell_list = fast_tell_list))
  
}

oaatSensvarRank <- function(oat_sens_mat){
  
  ynames <- rownames(oat_sens_mat)
  xnames <- colnames(oat_sens_mat)
  
  normsens <- normalize(t(oat_sens_mat))
  normsens_mean <- apply(normsens,1, mean)
  
  rank <- rank(-normsens_mean)
  
  
  return(list(mean = normsens_mean, rank = rank))
  
}

SensRank <- function(sens_mat){
  
  # summarising and ranking parameters in a sensitivity matrix
  
  ynames <- rownames(sens_mat)
  xnames <- colnames(sens_mat)
  
  normsens <- normalize(t(sens_mat))
  normsens_mean <- apply(normsens,1, mean)
  
  rank <- rank(-normsens_mean)
  
  return(list(mean = normsens_mean, rank = rank))
  
}

to find the “standard” value in normalized space, we can normalize a vector of “1s” with respect to the original design


X_standard <- matrix(rep(1,d), ncol = d, nrow = 1)

X_standard_norm <- normalize(X_standard, wrt = lhs)

X_level1a_unnorm <- unnormalize(X_level1a, un.mins = lhs_min, un.maxes = lhs_max)

X_level1a_wave01_unnorm <- unnormalize(X_level1a_wave01, un.mins = lhs_min, un.maxes = lhs_max)

1 Sensitivity at level1a constraint (f0_io and b_wl_io truncated)

Define constraint level 1a as those members that run, and have F0_io <0.9 & b_wl_io > 0.15 (normalised).

1.1 Modern value sensitivity

#if (file.exists("oaat_level1a_Y.rdata")) {
#  load("oaat_level1a_Y.rdata")
#} else {
  
oat_var_sensmat_level1a_Y <- matrix(NA, nrow = length(y_names_sum), ncol = ncol(X_level1a))

for(i in 1:length(y_names_sum)){
  
  yname <- y_names_sum[i]
  y <- Y_level1a[, yname]
  oat <- oaatSensvarKmList(X = X_level1a, em_list = emlist_km_Y_level1a, med = FALSE, hold = X_standard_norm)
  oat_var_sensmat_level1a_Y[i, ] <- oat
}

#save(y_names_sum, oat_var_sensmat_level1a_Y, file = "oaat_level1a_Y.rdata")
#}

rownames(oat_var_sensmat_level1a_Y) <- y_names_sum
colnames(oat_var_sensmat_level1a_Y) <- colnames(X_level1a)

#normsens_level1a_Y <- normalize(t(oat_var_sensmat_level1a_Y))

#pdf(file = 'figs/oat_var_sensmat_level1a_Y.pdf', width = 7, height = 8)
oaatSensvarSummaryPlot(oat_var_sensmat_level1a_Y)


#dev.off()

1.2 Anomaly (Change 1850 - 2013) sensitivities

#if (file.exists("oaat_level1a_YAnom.rdata")) {
#  load("oaat_level1a_YAnom.rdata")
#} else {
  
oat_var_sensmat_level1a_YAnom <- matrix(NA, nrow = length(y_names_sum), ncol = ncol(X_level1a))

for(i in 1:length(y_names_sum)){
  
  yname <- y_names_sum[i]
  y <- YAnom_level1a[, yname]
  oat <- oaatSensvarKmList(X = X_level1a, em_list = emlist_km_YAnom_level1a,  med = FALSE, hold = X_standard_norm)
  oat_var_sensmat_level1a_YAnom[i, ] <- oat
}

#save(y_names_sum, oat_var_sensmat_level1a_YAnom, file = "oaat_level1a_YAnom.rdata")
#}


rownames(oat_var_sensmat_level1a_YAnom) <- y_names_sum
colnames(oat_var_sensmat_level1a_YAnom) <- colnames(X_level1a)

# Normalise sensitivities
#normsens_level1a_YAnom <- normalize(t(oat_var_sensmat_level1a_YAnom))

#pdf(file = 'figs/oat_var_sensmat_level1a_YAnom.pdf', width = 7, height = 8)
oaatSensvarSummaryPlot(oat_var_sensmat_level1a_YAnom)

#dev.off()

1.3 One-at-a-time sensitivity analysis of constraining variables for understanding model response

“Constraining variables” being those we use to constrain the model (npp, nbp, cSoil and cVeg). It’s hard to maintain a high vegetation carbon in particular.

Further idea: What parameter values might you choose to do this, and what might be the trade-offs you have to make?

#fit_list_const_level1a <- createKmFitList(X = X_level1a, Y = Y_const_level1a_scaled)

Y_const_level1a_scaled_list <- mat2list(Y_const_level1a_scaled)
fit_list_const_level1a <- mclapply(X = Y_const_level1a_scaled_list, FUN = km, formula = ~., design = X_level1a,
                                   mc.cores = 4, control = list(trace = FALSE))
# Check that oatSensVar and the plotting make sense

oaat_sens_cVeg <- oaatSensvarKm(X = X_level1a, y = Y_const_level1a_scaled[,"cVeg_lnd_sum"])

optimisation start
------------------
* estimation method   : MLE 
* optimisation method : BFGS 
* analytical gradient : used
* trend model : ~alpha_io + a_wl_io + bio_hum_cn + b_wl_io + dcatch_dlai_io + 
    dqcrit_io + dz0v_dh_io + f0_io + fd_io + g_area_io + g_root_io + 
    g_wood_io + gs_nvg_io + hw_sw_io + kaps_roth + knl_io + lai_max_io + 
    lai_min_io + lma_io + n_inorg_turnover + nmass_io + nr_io + 
    retran_l_io + retran_r_io + r_grow_io + rootd_ft_io + sigl_io + 
    sorp + tleaf_of_io + tlow_io + tupp_io + l_vg_soil
* covariance model : 
  - type :  matern5_2 
  - nugget : NO
  - parameters lower bounds :  1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 1e-10 
  - parameters upper bounds :  2 2 1.999244 1.698141 1.994798 1.990622 2 1.782043 2 2 2 1.998084 2 2 2 1.99598 2 1.996551 1.997502 1.99279 1.985766 1.99623 1.983347 1.991191 2 1.990947 1.995643 1.994232 1.992247 2 2 2 
  - best initial criterion value(s) :  -2313.187 

N = 32, M = 5 machine precision = 2.22045e-16
At X0, 0 variables are exactly at the bounds
At iterate     0  f=       2313.2  |proj g|=       1.9662
At iterate     1  f =       2312.6  |proj g|=        1.7711
At iterate     2  f =       2312.1  |proj g|=        1.5621
At iterate     3  f =       2311.8  |proj g|=        1.3975
At iterate     4  f =       2311.4  |proj g|=         1.946
At iterate     5  f =       2310.6  |proj g|=        1.1194
At iterate     6  f =       2310.4  |proj g|=        1.3714
At iterate     7  f =       2310.2  |proj g|=        1.9506
At iterate     8  f =       2309.9  |proj g|=        1.1649
At iterate     9  f =         2309  |proj g|=        1.3938
At iterate    10  f =       2307.7  |proj g|=        1.0686
At iterate    11  f =       2307.5  |proj g|=        1.7489
At iterate    12  f =       2307.1  |proj g|=        1.9538
At iterate    13  f =       2306.9  |proj g|=        1.9458
At iterate    14  f =       2306.5  |proj g|=        1.9419
At iterate    15  f =         2306  |proj g|=        1.4037
At iterate    16  f =       2305.9  |proj g|=        1.2844
At iterate    17  f =       2305.8  |proj g|=        1.5794
At iterate    18  f =       2305.4  |proj g|=        1.6413
At iterate    19  f =       2305.4  |proj g|=        1.9447
At iterate    20  f =       2305.3  |proj g|=        1.9431
At iterate    21  f =       2305.1  |proj g|=       0.80895
At iterate    22  f =       2304.5  |proj g|=       0.76408
At iterate    23  f =       2304.1  |proj g|=        1.0295
At iterate    24  f =       2303.9  |proj g|=        1.0252
At iterate    25  f =       2303.9  |proj g|=        1.9504
At iterate    26  f =       2303.9  |proj g|=        1.9504
At iterate    27  f =       2303.9  |proj g|=        0.9316
At iterate    28  f =       2303.8  |proj g|=       0.92937
At iterate    29  f =       2303.8  |proj g|=        1.7509
At iterate    30  f =       2303.8  |proj g|=        1.7595
At iterate    31  f =       2303.7  |proj g|=        1.7673
At iterate    32  f =       2303.6  |proj g|=        1.7592
At iterate    33  f =       2303.6  |proj g|=        1.9504
At iterate    34  f =       2303.5  |proj g|=        1.9501
At iterate    35  f =       2303.5  |proj g|=        1.4488
At iterate    36  f =       2303.4  |proj g|=       0.94581
At iterate    37  f =       2303.3  |proj g|=       0.89706
At iterate    38  f =       2303.1  |proj g|=       0.83388
At iterate    39  f =       2302.9  |proj g|=        0.8892
At iterate    40  f =       2302.8  |proj g|=         1.768
At iterate    41  f =       2302.8  |proj g|=        1.9483
At iterate    42  f =       2302.7  |proj g|=        1.9487
At iterate    43  f =       2302.7  |proj g|=        0.3197
At iterate    44  f =       2302.7  |proj g|=       0.29872
At iterate    45  f =       2302.6  |proj g|=       0.36766
At iterate    46  f =       2302.6  |proj g|=         0.878
At iterate    47  f =       2302.6  |proj g|=       0.87982
At iterate    48  f =       2302.6  |proj g|=        0.9486
At iterate    49  f =       2302.6  |proj g|=       0.75616
At iterate    50  f =       2302.6  |proj g|=       0.63227
At iterate    51  f =       2302.6  |proj g|=        1.9481
At iterate    52  f =       2302.5  |proj g|=       0.52027
At iterate    53  f =       2302.5  |proj g|=       0.28047
At iterate    54  f =       2302.5  |proj g|=       0.27537
At iterate    55  f =       2302.5  |proj g|=       0.27236
At iterate    56  f =       2302.5  |proj g|=        1.9485
At iterate    57  f =       2302.5  |proj g|=        1.2162
At iterate    58  f =       2302.5  |proj g|=       0.40663
At iterate    59  f =       2302.5  |proj g|=       0.70448
At iterate    60  f =       2302.5  |proj g|=       0.94228
At iterate    61  f =       2302.5  |proj g|=       0.92322
At iterate    62  f =       2302.5  |proj g|=        1.4126
At iterate    63  f =       2302.5  |proj g|=        1.9498
At iterate    64  f =       2302.4  |proj g|=        1.7444
At iterate    65  f =       2302.4  |proj g|=        1.2583
At iterate    66  f =       2302.4  |proj g|=       0.89898
At iterate    67  f =       2302.4  |proj g|=        1.4319
At iterate    68  f =       2302.4  |proj g|=       0.63223
At iterate    69  f =       2302.4  |proj g|=        1.1094
At iterate    70  f =       2302.3  |proj g|=       0.28589
At iterate    71  f =       2302.3  |proj g|=        1.9504
At iterate    72  f =       2302.3  |proj g|=       0.54066
At iterate    73  f =       2302.3  |proj g|=       0.35811
At iterate    74  f =       2302.3  |proj g|=       0.36032
At iterate    75  f =       2302.3  |proj g|=       0.91612
At iterate    76  f =       2302.3  |proj g|=        1.6242
At iterate    77  f =       2302.3  |proj g|=       0.39104
At iterate    78  f =       2302.3  |proj g|=        1.1859
At iterate    79  f =       2302.3  |proj g|=       0.50952
At iterate    80  f =       2302.3  |proj g|=        1.9502
At iterate    81  f =       2302.3  |proj g|=        1.9501
At iterate    82  f =       2302.3  |proj g|=       0.74862
At iterate    83  f =       2302.3  |proj g|=        1.0332
At iterate    84  f =       2302.3  |proj g|=       0.58104
At iterate    85  f =       2302.3  |proj g|=       0.50235
At iterate    86  f =       2302.3  |proj g|=       0.40363
At iterate    87  f =       2302.3  |proj g|=       0.42349
At iterate    88  f =       2302.3  |proj g|=       0.40638
At iterate    89  f =       2302.3  |proj g|=       0.30942
At iterate    90  f =       2302.2  |proj g|=       0.37206
At iterate    91  f =       2302.2  |proj g|=       0.32353
At iterate    92  f =       2302.2  |proj g|=         1.773
At iterate    93  f =       2302.2  |proj g|=        1.4087
At iterate    94  f =       2302.2  |proj g|=       0.13526
At iterate    95  f =       2302.2  |proj g|=        0.2696
At iterate    96  f =       2302.2  |proj g|=       0.27163
At iterate    97  f =       2302.2  |proj g|=       0.26918
At iterate    98  f =       2302.2  |proj g|=        1.1204
At iterate    99  f =       2302.1  |proj g|=       0.39905
At iterate   100  f =       2302.1  |proj g|=        1.4841
At iterate   101  f =       2302.1  |proj g|=       0.20603
final  value 2302.148961 
stopped after 101 iterations
X_oaat_level1a <- oaat_design(X_level1a, n=21, med = FALSE, hold = X_standard_norm)

colnames(X_oaat_level1a) = colnames(X)

y_oaat <- predict.km(fit_list_const_level1a[[4]], newdata = X_oaat_level1a, type = 'UK')

First, what parameters affect vegetation carbon and how? How sure are we about that?



oaatLinePlot(X_oaat = X_oaat_level1a, y_oaat_mean = y_oaat$mean, y_oaat_sd = y_oaat$sd, 
             n_oaat = 21,nr = 6, nc = 6) 

NA
NA

Y_oaat_const_level1a_scaled <- matrix(ncol = ncol(Y_const_level1a_scaled), nrow = nrow(X_oaat_level1a))

for(i in 1:ncol(Y_const_level1a_scaled)){

  y_oaat <- predict.km(fit_list_const_level1a[[i]], newdata = X_oaat_level1a, type = 'UK')
  Y_oaat_const_level1a_scaled[,i] <- y_oaat$mean
}

What might be the trade-offs for a high (or accurate) vegetation carbon? are they acceptable? Plot the oaat sensitivity of the other 3 outputs we’re calibrating on.

Plotting these graphs in the original input space (multiplication factors) and providing the standard has the pleasing side effect of showing what you could do to standard inputs to increase or decrease a particular output.

Y_oaat_const_level1a_scaled_norm <- normalize(Y_oaat_const_level1a_scaled)

        oaatLinePlotMulti <- function(X_oaat, Y_oaat, n_oaat, nr, nc, cols, ...){
  
          par(mfrow = c(nr,nc), oma = c(0.1,0.1,3,0.1), mar = c(2,2,3,1), las = 1)
  
          for(i in 1:ncol(X_oaat)){
            ix <- seq(from = ((i*n_oaat) - (n_oaat-1)), to =  (i*n_oaat), by = 1)
    
            plot(X_oaat[ix,i], Y_oaat[ix,1],
                 ylim = c(0,1),
                 xlab = colnames(X_oaat)[i],
                 type= 'n',
                 bty = 'n')
  
            for(j in 1:ncol(Y_oaat)){
              lines(X_oaat[ix,i], Y_oaat[ix, j], lty = 'solid', col = cols[j], ...)
              abline(v = 1, lty = 'dashed', col = 'grey')
              mtext(colnames(X_oaat)[i], side = 3, line = 0.5)
  
            }
  
          }
    
        }

X_oaat_level1a_unnorm <- unnormalize(X_oaat_level1a, un.mins = lhs_min, un.maxes = lhs_max)        
#pdf(file = 'figs/Y_oaat_const_level1a_scaled_norm.pdf', width = 10, height = 10)
oaatLinePlotMulti(X_oaat = X_oaat_level1a_unnorm, Y_oaat = Y_oaat_const_level1a_scaled_norm ,  n_oaat = 21, nr = 6, nc = 6,
                  lwd = 3, col = cbPal[c(1,2,6,8)])
  
reset()
legend('top', c('nbp', 'npp', 'csoil', 'cveg'), col = cbPal[c(1,2,6,8)], lty = 'solid', lwd = 3, horiz = TRUE)

#dev.off()

1.4 Update for wave01

# Build list of emulators for both waves, standard constraint parameters.

Y_const_level1a_wave01_scaled_list <- mat2list(Y_const_level1a_wave01_scaled)
fit_list_const_level1a_wave01 <- mclapply(X = Y_const_level1a_wave01_scaled_list, FUN = km, formula = ~., design = X_level1a_wave01,
                                   mc.cores = 4, control = list(trace = FALSE))

Y_oaat_const_level1a_wave01_scaled <- matrix(ncol = ncol(Y_const_level1a_wave01_scaled), nrow = nrow(X_oaat_level1a))

for(i in 1:ncol(Y_oaat_const_level1a_wave01_scaled)){

  y_oaat <- predict.km(fit_list_const_level1a_wave01[[i]], newdata = X_oaat_level1a, type = 'UK')
  Y_oaat_const_level1a_wave01_scaled[,i] <- y_oaat$mean
}
Y_oaat_const_level1a_wave01_scaled_norm <- normalize(Y_oaat_const_level1a_wave01_scaled)

#pdf(file = 'figs/Y_oaat_const_level1a_wave01_scaled_norm.pdf', width = 10, height = 10)
oaatLinePlotMulti(X_oaat = X_oaat_level1a_unnorm, Y_oaat = Y_oaat_const_level1a_wave01_scaled_norm ,  n_oaat = 21, nr = 6, nc = 6,
                  lwd = 3, col = cbPal[c(1,2,6,8)])
  
reset()
legend('top', c('nbp', 'npp', 'csoil', 'cveg'), col = cbPal[c(1,2,6,8)], lty = 'solid', lwd = 3, horiz = TRUE)

#dev.off()
Y_sum_level1a_wave01_list <- mat2list(Y_sum_level1a_wave01)

if (file.exists("emlist_km_Y_level1a_wave01_2022-05-24.rdata")) {
  load("emlist_km_Y_level1a_wave01_2022-05-24.rdata")
} else {
  
  # Here, the list is a list version of the matrix Y_
  emlist_km_Y_level1a_wave01 <- mclapply(X = Y_sum_level1a_wave01_list, FUN = km, formula = ~., design = X_level1a_wave01, mc.cores = 4) 
  
  save( emlist_km_Y_level1a_wave01, file = "emlist_km_Y_level1a_2022-05-24.rdata")
  
}
#if (file.exists("oaat_level1a_Y.rdata")) {
#  load("oaat_level1a_Y.rdata")
#} else {
  
oat_var_sensmat_level1a_wave01_Y <- matrix(NA, nrow = length(y_names_sum), ncol = ncol(X_level1a))

for(i in 1:length(y_names_sum)){
  
  yname <- y_names_sum[i]
  y <- Y_level1a[, yname]
  oat <- oaatSensvarKmList(X = X_level1a_wave01, em_list = emlist_km_Y_level1a_wave01, med = FALSE, hold = X_standard_norm)
  oat_var_sensmat_level1a_wave01_Y[i, ] <- oat
}

#save(y_names_sum, oat_var_sensmat_level1a_Y, file = "oaat_level1a_Y.rdata")
#}

rownames(oat_var_sensmat_level1a_wave01_Y) <- y_names_sum
colnames(oat_var_sensmat_level1a_wave01_Y) <- colnames(X_level1a)

#normsens_level1a_Y <- normalize(t(oat_var_sensmat_level1a_Y))

#pdf(file = 'figs/oat_var_sensmat_level1a_wave01_Y.pdf', width = 7, height = 8)
oaatSensvarSummaryPlot(oat_var_sensmat_level1a_wave01_Y)


#dev.off()

1.5 Anomaly wave01

YAnom_sum_level1a_wave01_list <- mat2list(YAnom_sum_level1a_wave01)

if (file.exists("emlist_km_Y_level1a_wave01_2022-05-25.rdata")) {
  load("emlist_km_Y_level1a_wave01_2022-05-25.rdata")
} else {
  
  # Here, the list is a list version of the matrix Y_
  emlist_km_YAnom_level1a_wave01 <- mclapply(X = YAnom_sum_level1a_wave01_list, FUN = km, formula = ~., design = X_level1a_wave01, mc.cores = 4) 
  
  save( emlist_km_YAnom_level1a_wave01, file = "emlist_km_YAnom_level1a_2022-05-25.rdata")
  
}
  
oat_var_sensmat_level1a_wave01_YAnom <- matrix(NA, nrow = length(y_names_sum), ncol = ncol(X_level1a))

for(i in 1:length(y_names_sum)){
  
  yname <- y_names_sum[i]
  y <- Y_level1a[, yname]
  oat <- oaatSensvarKmList(X = X_level1a_wave01, em_list = emlist_km_YAnom_level1a_wave01, med = FALSE, hold = X_standard_norm)
  oat_var_sensmat_level1a_wave01_YAnom[i, ] <- oat
}

#save(y_names_sum, oat_var_sensmat_level1a_Y, file = "oaat_level1a_Y.rdata")
#}

rownames(oat_var_sensmat_level1a_wave01_YAnom) <- y_names_sum
colnames(oat_var_sensmat_level1a_wave01_YAnom) <- colnames(X_level1a)

#normsens_level1a_Y <- normalize(t(oat_var_sensmat_level1a_Y))

#pdf(file = 'figs/oat_var_sensmat_level1a_wave01_YAnom.pdf', width = 7, height = 8)
oaatSensvarSummaryPlot(oat_var_sensmat_level1a_wave01_YAnom)


#dev.off()

2 FAST sensitivity analysis

We use a FAST99 algorithm by Saltelli et al (2000), from the R package “sensitivity”

# Need to think about how mins and maxes are dealt with - we have a truncated input design

# Generate a design for the FAST99 analysis
X_fast <- fast99(model = NULL, factors = colnames(X_level1a_wave01), n = 3000,
                 q = "qunif", q.arg = list(min = 0, max = 1))

Create a list of sensitivity analyses, one for each column of the “sum” (modern) output matrix. (This now uses wave01 data)


MF_Y_sum_level1a <- multiFAST(X = X_level1a_wave01, Y = Y_sum_level1a_wave01, fit_list = emlist_km_Y_level1a_wave01, n = 1000)

Create a sensitivity summary matrix from the list of sensitivity analyses.



FAST_total_Y_sum_level1a <- matrix(nrow = length(MF_Y_sum_level1a$fast_tell_list), ncol = d)

for(i in 1:length(MF_Y_sum_level1a$fast_tell_list)){
 
  # sum the direct effect and interaction terms to get a total
  FAST_total_Y_sum_level1a[i, ] <- apply(bp_convert(MF_Y_sum_level1a$fast_tell_list[[i]]),2,sum)
}
colnames(FAST_total_Y_sum_level1a) <- colnames(X_level1a)
rownames(FAST_total_Y_sum_level1a) <- colnames(Y_sum_level1a)

Plot the summary matrix

#pdf(file = 'figs/FAST_sensmat_Y_level1a_wave01.pdf', width = 7, height = 8)
sensMatSummaryPlot(FAST_total_Y_sum_level1a)

#dev.off()

Now create a list of sensitivity analyses for the anomaly at the end of the run.


MF_YAnom_sum_level1a <- multiFAST(X = X_level1a, Y = YAnom_sum_level1a, fit_list = emlist_km_YAnom_level1a)

Create the sensitivity summary matrix for the anomaly

Plot the sensitivity summary matrix


#pdf(file = 'figs/FAST_sensmat_YAnom_level1a_wave01.pdf', width = 7, height = 8)
sensMatSummaryPlot(FAST_total_YAnom_sum_level1a)

#dev.off()

knit_exit()

3 Monte Carlo Filtering

# ---------------------------------------------------------------------------------
# Monte carlo filtering for sensitivity analysis
# ---------------------------------------------------------------------------------

# Uniform sample from across parameter space
# Split the sample into 'behavioural' (NROY) and 'Non behavioural (Ruled Out)
# Build cdfs of the marginal distributions in each case
# Perform a KS test to see if the smaples are drawn from different distributions
# The KS statistic is an indicator of the importance of the parameter in splitting the
# samples.

# "Not in" function
'%!in%' <- function(x,y)!('%in%'(x,y))

mcf = function(X, nroy_ix){

  ## Monte Carlo Filtering function
  ## X   ............... Complete sample from input space
  ## nroy.ix ........... index of cases of X which are NROY (Not Ruled Out Yet), or 'behavioural'.

  ## produces ks statistic for each column of the input matrix X
  ## A larger ks statistic means that input is more important for
  ## determining if a sample is NROY or not

  X_nroy = X[nroy_ix, ]

  ref = 1:nrow(X)
  ro_ix = which(ref %!in% nroy_ix)
  X_ro = X[ro_ix, ]

  kss = rep(NA, length = ncol(X))
  for(i in 1:ncol(X)){

    ks = ks.test(X_ro[,i], X_nroy[,i])
    kss[i] = ks$statistic

  }

  out = kss
  out
}

This repeats some code from the constraint analysis in order to do MCF using the observations (constraints) we have.


# nbp  npp  csoil  cveg
Y_lower <- c(-10, 35, 750, 300)
Y_upper <- c(10, 80, 3000, 800)

# I'm going to set it so that + 4sd aligns approximately with the original limits
# given by Andy Wiltshire. This gives room for uncertainty from the emulator
Y_target = Y_upper - (abs(Y_upper - (Y_lower)) / 2 )# abs() to fix the problem with negative numbers


# standard deviation is derived from the limits and the central target
# (this distance is assumed to be 4 standard deviations.
Y_sd = (Y_upper - Y_target) / 4
names(Y_sd) = colnames(Y_const_level1a_wave01_scaled)


p = ncol(Y_const_level1a_wave01_scaled)

obs_sd_list = as.list(rep(0.01,p))
disc_list =  as.list(rep(0,p)) 
disc_sd_list =  as.list(Y_sd)
thres = 3

mins_aug = apply(X_level1a, 2, FUN = min)
maxes_aug =apply(X_level1a, 2, FUN = max)

# convert Y_target for ingestion into function
Y_target = matrix(Y_target, nrow = 1)
# First build an emulator list for the Y

emlist_Y_const_level1a_wave01_scaled <- mclapply(X = Y_const_level1a_wave01_scaled_list, FUN = km, formula = ~., 
                                                 design = X_level1a_wave01, mc.cores = 4,
                                          control = list(trace = FALSE)) 
# Samples from a uniform distribution across all of input space
nsamp_unif <- 10000  
X_unif <- samp_unif(nsamp_unif, mins = (rep(0, d)), maxes = rep(1,d))

Y_unif <- matrix(nrow = nsamp_unif, ncol = ncol(Y_const_level1a_wave01_scaled))
colnames(Y_unif) <- colnames(Y_const_level1a_wave01_scaled)

# Build an emulator for each output individually
for(i in 1:ncol(Y_const_level1a_wave01_scaled)){
  em <- emlist_Y_const_level1a_wave01_scaled[[i]]
  pred <- predict(em, newdata = X_unif, type = 'UK')
  Y_unif[,i] <- pred$mean
}
# This uses MCF with the constraints set by AW, rather than with a formal history match.

mcf_nbp = mcf(X_unif, which(Y_unif[,'nbp_lnd_sum'] > 0))
mcf_npp = mcf(X_unif, which(Y_unif[,'npp_nlim_lnd_sum'] > 35 & Y_unif[,'npp_nlim_lnd_sum'] < 80))
mcf_cSoil = mcf(X_unif, which(Y_unif[,'cSoil_lnd_sum'] > 750 & Y_unif[,'cSoil_lnd_sum'] < 3000))
mcf_cVeg <- mcf(X_unif, which(Y_unif[,'cVeg_lnd_sum'] > 300 & Y_unif[,'cVeg_lnd_sum'] < 800))

mcf_all_const <- mcf(X_unif, which(Y_unif[,'cVeg_lnd_sum'] > 300 & Y_unif[,'cVeg_lnd_sum'] < 800 & Y_unif[,'cSoil_lnd_sum'] > 750 & Y_unif[,'cSoil_lnd_sum'] < 3000 & Y_unif[,'npp_nlim_lnd_sum'] > 35 & Y_unif[,'npp_nlim_lnd_sum'] < 80 & Y_unif[,'nbp_lnd_sum'] > 0))

mcf_summary <- matrix(rbind(mcf_nbp, mcf_npp, mcf_cSoil, mcf_cVeg, mcf_all_const), nrow = ncol(Y_const_level1a_wave01_scaled)+1)
colnames(mcf_summary) <- colnames(X_level1a)
rownames(mcf_summary) <- c('nbp', 'npp', 'cSoil', 'cVeg', 'all')
#pdf(file = 'figs/MCF_sensmat_Yconst_level1a_wave01.pdf', width = 6, height = 8 )
sensMatSummaryPlot(mcf_summary)
#dev.off()
# using all together is quite similar to using the mean
plot(1:32, mcf_all_const, ylim = c(0,0.7), pch = 19)
points(1:32, mcf_npp, col = 'red', pch = 19)
points(1:32, mcf_cVeg, col = 'green', pch = 19)
points(1:32, mcf_cSoil, col = 'brown', pch = 19)
points(1:32, mcf_nbp, col = 'gold', pch = 19)

legend('topleft', legend = c('all', 'npp', 'cVeg', 'cSoil', 'nbp'), 
       col = c('black','red','green', 'brown', 'gold' ),
       pch = 19) 

sensrank_Y_level1a_mcf <- SensRank(mcf_summary[1:4, ])

3.0.1 Ranking sensitivity of the parameters.

The idea here is to summarise the relative importance of the input parameters. The sensitivity measures are normalised





sensrank_Y_level1a_oat <- SensRank(oat_var_sensmat_level1a_wave01_Y)
sensrank_YAnom_level1a_oat <- SensRank(oat_var_sensmat_level1a_wave01_YAnom)

sensrank_FAST <- SensRank(FAST_total_Y_sum_level1a)
sensrank_FAST_YAnom <- SensRank(FAST_total_YAnom_sum_level1a)

  
sens_ranks <- cbind(sensrank_Y_level1a_oat$rank,sensrank_FAST$rank,  sensrank_YAnom_level1a_oat$rank, sensrank_FAST_YAnom$rank, sensrank_Y_level1a_mcf$rank)
colnames(sens_ranks) <- c('OAT_modern_value', 'FAST_modern_value', 'OAT_anomaly', 'FAST_anomaly', 'MCF_modern_value')

min_rank <- apply(sens_ranks,1, min)

all_ranks <- cbind(sens_ranks, min_rank)

#plot(sens_ranks[,1], sens_ranks[,2], xlab = 'modern value rank', ylab = 'anomaly rank')


rank_ix <- sort(min_rank, decreasing = FALSE, index.return = TRUE)

# All ranks is the table of rankings, with min_rank being the highest ranking
sens_table <- all_ranks[rank_ix$ix, ]
  
sens_table

library(xtable)
xtable(sens_table, digits = 0)
knit_exit()

code from here is only for reference (to be amended)



# First, use MCF on the design
#
# Calculate the implausibility of each input point for
# each set of observations

run.impl.amaz = impl(em = Y_tropics,
  em.sd = 0,
  disc = 0,
  disc.sd = 0,
  obs = obs_amazon,
  obs.sd = 0.075)

run.nroy.ix.amaz = which(run.impl.amaz < 3)

run.impl.seasia = impl(em = Y_tropics,
  em.sd = 0,
  disc = 0,
  disc.sd = 0,
  obs = obs_seasia,
  obs.sd = 0.075)

run.nroy.ix.seasia = which(run.impl.seasia < 3)

run.impl.congo = impl(em = Y_tropics,
  em.sd = 0,
  disc = 0,
  disc.sd = 0,
  obs = obs_congo,
  obs.sd = 0.075)

run.nroy.ix.congo = which(run.impl.congo < 3)


mcf.amaz = mcf(X_tropics, run.nroy.ix.amaz)
mcf.seasia = mcf(X_tropics, run.nroy.ix.seasia)
mcf.congo = mcf(X_tropics, run.nroy.ix.congo)

dev.new(width = 6, height = 6)
par(mar = c(8,4,3,1))
plot(1:ncol(X_tropics), mcf.amaz, col = col.amaz, pch = 19,
     ylim = c(0,0.4),
     ylab = 'MCF sensitivity', xlab = '',
     axes = FALSE,
     pty = 'n'
     )
abline(v = 1:ncol(X_tropics), lty = 'dashed', col = 'lightgrey')
points(1:ncol(X_tropics), mcf.amaz, col = col.amaz, pch = 19)
points(1:ncol(X_tropics), mcf.seasia, col = col.seasia, pch = 19)
points(1:ncol(X_tropics), mcf.congo, col = col.congo, pch = 19)

axis(side = 1, labels = colnames(X_tropics), las = 2, at = 1:ncol(X_tropics))
axis(side = 2)

# -------------------------------------------------------------------------
# Generate uncertainty estimates on the MCF by emulating and
# bootstrapping the samples.
# -------------------------------------------------------------------------

mcf.emboot = function(X, emfit, bootcol = c(8,9),
  disc, disc.sd, obs, obs.sd, thres = 3, n.mcf = 1000, n.reps = 3000){
  
  ## Function that does Monte Carlo Filtering using an emulated sample.
  ## Inputs for emulation are sampled from the unit cube apart from
  ## those in columns bootcol, which are bootstrapped from the design.
  ##

  em.mcfmat = matrix(nrow = n.reps, ncol = ncol(X))

  for(i in 1:n.reps){

                                        # Sample from uniform distributions for the
                                        # standard input parameters
    X.mcf = samp.unif(n = n.mcf, mins = rep(0, ncol(X)), maxes = rep(1, ncol(X)))
    
                                        # Sample from the model run inputs for the temp and precip
                                        # (here indicated by bootcol columns)
    X.tp.ix = sample(1:nrow(X), size = n.mcf, replace = TRUE)
    X.tp.runsamp = X[X.tp.ix , bootcol]
    
                                        # bind the samples together
    X.mcf[, bootcol] = X.tp.runsamp
    colnames(X.mcf) <- colnames(X)

    # Predict model output at the sampled inputs
    pred.mcf = predict(emfit, newdata = X.mcf, type = 'UK')

    # find the implausibility of the predicted inputs
    em.impl.mcf = impl(em = pred.mcf$mean,
    em.sd = pred.mcf$sd,
      disc = disc,
      disc.sd = disc.sd,
      obs = obs,
      obs.sd = obs.sd)

    # Which part of the sample is NROY (or "behavioural")
    em.nroy.ix = which(em.impl.mcf < thres)
    
    em.mcf= mcf(X.mcf, em.nroy.ix)
    em.mcfmat[i, ] = em.mcf
    
  }
  
  mcf.mean = apply(em.mcfmat, 2, mean)
  mcf.sd = apply(em.mcfmat, 2, sd)


  return(list(mean = mcf.mean, sd = mcf.sd))
}


# How big might the uncertainty bounds be if we use just 300 points
# (as in the ensemble) to estimate the MCF sensitivity analysis indices?

n.mcf.seq = c(seq(from = 100, to = 1000, by = 100), 1500, 2000, 3000)
mcf.seq.mean = matrix(NA, nrow = length(n.mcf.seq), ncol = ncol(X_tropics_norm))
mcf.seq.sd = matrix(NA, nrow = length(n.mcf.seq), ncol = ncol(X_tropics_norm))

for(i in 1:length(n.mcf.seq)){

mcf.em.amaz.seq = mcf.emboot(X = X_tropics_norm, em = tropics_fit,
  bootcol = c(8,9), disc = 0, disc.sd = 0, obs = obs_amazon, obs.sd = 0.05,
  thres = 3, n.mcf = n.mcf.seq[i], n.reps = 1000)

mcf.seq.mean[i, ] = mcf.em.amaz.seq$mean
mcf.seq.sd[i, ] = mcf.em.amaz.seq$sd

}

# What is our estimate of MCF uncertainty when we have 300 ensemble members?
mcf.em.amaz.300 = mcf.emboot(X = X_tropics_norm, em = tropics_fit,
  bootcol = c(8,9), disc = 0, disc.sd = 0, obs = obs_amazon, obs.sd = 0.05,
  thres = 3, n.mcf = 300, n.reps = 1000)

mcf.em.seasia.300 = mcf.emboot(X = X_tropics_norm, em = tropics_fit,
  bootcol = c(8,9), disc = 0, disc.sd = 0, obs = obs_seasia, obs.sd = 0.05,
  thres = 3, n.mcf = 300, n.reps = 1000)

mcf.em.congo.300 = mcf.emboot(X = X_tropics_norm, em = tropics_fit,
  bootcol = c(8,9), disc = 0, disc.sd = 0, obs = obs_congo, obs.sd = 0.05,
  thres = 3, n.mcf = 300, n.reps = 1000)



cbPal <- c("#000000", "#E69F00", "#56B4E9", "#009E73", "#F0E442", "#0072B2", "#D55E00", "#CC79A7")


# The Mean and the Standard deviation of the estimate
# of MCF sensitivity both drop as the number of MCF samples increases.

#dev.new(width = 10, height = 8)
pdf(width = 10, height = 8, file = 'graphics/mcf_mean_sd_vs_n.pdf')
par(mfrow = c(1,2), las = 1)

matplot(n.mcf.seq, mcf.seq.mean, main = 'Mean', xlab = 'Emulated Ensemble members', ylab = 'KS statistic Mean', type = 'o', col = cbPal)

matplot(n.mcf.seq, mcf.seq.sd, main = 'Standard deviation', xlab = 'Emulated Ensemble members', ylab = 'KS statistic standard deviation', type = 'o', col = cbPal)

legend('topright', pch = as.character(1:9), legend = colnames(X_tropics_norm), col = cbPal, text.col = cbPal)
dev.off()


# This puts the mean and estimate MCF sensitivity indices in context with their
# estimated uncertainty.
dev.new(width = 6, height = 10)
matplot(n.mcf.seq, mcf.seq.mean, main = 'Mean', xlab = 'Ensemble members', ylab = 'MCF Sensitivity Index', type = 'o', col = rep(cbPal,2), ylim = c(0,0.35), pch = 19, lwd = 1.2, lty = 'solid')

for(i in 1: ncol(mcf.seq.mean)){
  
arrows(x0 = n.mcf.seq, y0 = mcf.seq.mean[,i] - (mcf.seq.sd[,i]),
         x1 = n.mcf.seq, y1 = mcf.seq.mean[,i] + (mcf.seq.sd[, i]),
         length=0.05, angle=90, code=3, col = rep(cbPal,2)[i],lwd = 1.2
         )
}


# Calculate MCF indices with 5000 emulated ensemble members.
# Bootstrap uncertainty estimates.
mcf.em.amaz = mcf.emboot(X = X_tropics_norm, em = tropics_fit,
  bootcol = c(8,9), disc = 0, disc.sd = 0, obs = obs_amazon, obs.sd = 0.05,
  thres = 3, n.mcf = 5000, n.reps = 1000)

mcf.em.seasia = mcf.emboot(X = X_tropics_norm, em = tropics_fit,
  bootcol = c(8,9), disc = 0, disc.sd = 0, obs = obs_seasia, obs.sd = 0.05,
  thres = 3, n.mcf = 5000, n.reps = 1000)

mcf.em.congo = mcf.emboot(X = X_tropics_norm, em = tropics_fit,
  bootcol = c(8,9), disc = 0, disc.sd = 0, obs = obs_congo, obs.sd = 0.05,
  thres = 3, n.mcf = 5000, n.reps = 1000)


#dev.new(width = 9, height = 6)
pdf(file = 'graphics/mcf.pdf', width = 9, height = 6)
par(las = 1, mar = c(8,4,3,1))

ylim = c(0,0.27)
xlim = c(0.5,9.5)

plot((1:length(mcf.em.amaz$mean))-0.15, mcf.em.amaz$mean,
     pch = 19, col = col.amaz, ylim = ylim, xlim = xlim,
     pty = 'n', xaxs = 'i', yaxs = 'i',
     xlab = '', ylab = 'KS statistic',
     axes = FALSE)

i = seq(from = 1, to = 10, by = 2)
rect(i-0.5, ylim[1], i+0.5, ylim[2], col = "grey92", border=NA)

points((1:length(mcf.em.amaz$mean))-0.15, mcf.em.amaz$mean, pch = 19, col = col.amaz)

arrows(x0 = (1:length(mcf.em.amaz$mean))-0.15, y0 = mcf.em.amaz$mean - (2*mcf.em.amaz$sd ),
         x1 = (1:length(mcf.em.amaz$mean))-0.15, y1 = mcf.em.amaz$mean + (2*mcf.em.amaz$sd),
         col = col.amaz, length=0.05, angle=90, code=3)

points((1:length(mcf.em.seasia$mean)), mcf.em.seasia$mean, pch = 19, col = col.seasia)

arrows(x0 = 1:length(mcf.em.seasia$mean), y0 = mcf.em.seasia$mean - (2*mcf.em.seasia$sd ),
         x1 = 1:length(mcf.em.seasia$mean), y1 = mcf.em.seasia$mean + (2*mcf.em.seasia$sd),
         col = col.seasia, length=0.05, angle=90, code=3)

points((1:length(mcf.em.congo$mean))+0.15, mcf.em.congo$mean, pch = 19, col = col.congo)

arrows(x0 = (1:length(mcf.em.congo$mean))+0.15, y0 = mcf.em.congo$mean - (2*mcf.em.congo$sd ),
         x1 = (1:length(mcf.em.congo$mean))+0.15, y1 = mcf.em.congo$mean + (2*mcf.em.congo$sd),
         col = col.congo,length=0.05, angle=90, code=3)


axis(1, labels = colnames(X_tropics_norm), at = 1:9, las = 2)
axis(2)

legend('topleft',legend = c('Amazon','SE Asia', 'C Africa'),
       col = c(col.amaz, col.seasia, col.congo), pch = 19, bty = 'n')
text(0.5, 0.20, 'Error bars indicate \n \u00B1 2 standard deviations',
     pos  = 4, col = 'black',cex = 0.8 )

dev.off()


# Plot both the run-generated and emulated MCF sensitivity
#dev.new(width = 8, height = 6)
pdf(file = 'graphics/mcf_300_5000.pdf', width = 8, height = 6)
par(las = 1, mar = c(8,4,4,2))
ylim = c(0,0.43)
plot((1:length(mcf.em.amaz$mean))-0.2, mcf.em.amaz$mean,
     pch = 19, col = col.amaz, ylim = ylim, xlim = c(0.5,9.5),
     pty = 'n', xaxs = 'i', yaxs = 'i',
     xlab = '', ylab = 'KS statistic',
     axes = FALSE)

i = seq(from = 1, to = 10, by = 2)
rect(i-0.5, ylim[1], i+0.5, ylim[2], col = "lightgrey", border=NA)

points((1:length(mcf.em.amaz$mean))-0.2, mcf.em.amaz$mean, pch = 19, col = col.amaz)

arrows(x0 = (1:length(mcf.em.amaz$mean)) - 0.2, y0 = mcf.em.amaz$mean - (2*mcf.em.amaz$sd ),
         x1 = (1:length(mcf.em.amaz$mean)) - 0.2, y1 = mcf.em.amaz$mean + (2*mcf.em.amaz$sd),
         col = col.amaz, length=0.05, angle=90, code=3)

points((1:length(mcf.em.amaz$mean))-0.2, mcf.amaz, pch = 21, col = col.amaz)

arrows(x0 = 1:length(mcf.em.amaz$mean)-0.2, y0 = mcf.amaz - (2*mcf.em.amaz.300$sd ),
       x1 = 1:length(mcf.em.amaz$mean)-0.2, y1 = mcf.amaz + (2*mcf.em.amaz.300$sd),
       lty = 'dotted',
       col = col.amaz, length=0.05, angle=90, code=3)


points(1:length(mcf.em.seasia$mean), mcf.em.seasia$mean, pch = 19, col = col.seasia)

arrows(x0 = 1:length(mcf.em.seasia$mean), y0 = mcf.em.seasia$mean - (2*mcf.em.seasia$sd ),
         x1 = 1:length(mcf.em.seasia$mean), y1 = mcf.em.seasia$mean + (2*mcf.em.seasia$sd),
         col = col.seasia, length=0.05, angle=90, code=3)


points((1:length(mcf.em.amaz$mean)), mcf.seasia, pch = 21, col = col.seasia)

arrows(x0 = 1:length(mcf.em.amaz$mean), y0 = mcf.seasia - (2*mcf.em.seasia.300$sd ),
       x1 = 1:length(mcf.em.amaz$mean), y1 = mcf.seasia + (2*mcf.em.seasia.300$sd),
       lty = 'dotted',
       col = col.seasia, length=0.05, angle=90, code=3)


points((1:length(mcf.em.congo$mean))+0.2, mcf.em.congo$mean, pch = 19, col = col.congo)

arrows(x0 = (1:length(mcf.em.congo$mean))+0.2, y0 = mcf.em.congo$mean - (2*mcf.em.congo$sd ),
         x1 = (1:length(mcf.em.congo$mean))+0.2, y1 = mcf.em.congo$mean + (2*mcf.em.congo$sd),
         col = col.congo,length=0.05, angle=90, code=3)

points((1:length(mcf.em.congo$mean))+0.2, mcf.congo, pch = 21, col = col.congo)

arrows(x0 = 1:length(mcf.em.amaz$mean)+0.2, y0 = mcf.congo - (2*mcf.em.congo.300$sd ),
       x1 = 1:length(mcf.em.amaz$mean) +0.2, y1 = mcf.congo + (2*mcf.em.congo.300$sd),
       lty = 'dotted',
       col = col.congo, length=0.05, angle=90, code=3)

axis(1, labels = colnames(X_tropics_norm), at = 1:9, las = 2)
axis(2)

legend('topleft',legend = c('Amazon','SE Asia', 'C Africa'),
       col = c(col.amaz, col.seasia, col.congo), pch = 19, bty = 'n')
text(0.5, 0.32, 'Error bars indicate \n \u00B1 2 standard deviations',
     pos  = 4, col = 'black',cex = 0.8 )
text(0.5, 0.29, 'Open points & dotted lines indicate ensemble-only results',
     pos  = 4, col = 'black',cex = 0.8 )
dev.off()


# How does this sensitivity analysis measure up to the FAST99 version?
pdf(width = 12, height = 7, file = 'graphics/fast99_vs_mcf2.pdf')
#dev.new(width = 12, height = 7)
par(mfrow = c(1,2), mar = c(5,5,3,2), las = 1)

plot(print(fast.tell)[,1], mcf.amaz, col = col.amaz, pch = as.character(1:9),
     ylim = c(0,0.42), xlim = c(0,0.32),
     xlab = 'FAST99 first-order sensitivity',
     ylab = 'MCF sensitivity (KS statistic)',
     main = 'MCF using 300 ensemble members',
     pty = 'n'
     )

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.amaz - (2*mcf.em.amaz.300$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.amaz + (2*mcf.em.amaz.300$sd),
         col = col.amaz, length=0.05, angle=90, code=3,
       lty = 'solid', lwd = 0.8)

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.seasia - (2*mcf.em.seasia.300$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.seasia + (2*mcf.em.seasia.300$sd),
         col = col.seasia, length=0.05, angle=90, code=3,
       lty = 'solid', lwd = 0.8)

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.congo - (2*mcf.em.congo.300$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.congo + (2*mcf.em.congo.300$sd),
         col = col.congo, length=0.05, angle=90, code=3,
       lty = 'solid', lwd = 0.8)


points(print(fast.tell)[,1], mcf.amaz, col = col.amaz, pch = as.character(1:9), font = 2)
points(print(fast.tell)[,1], mcf.seasia, col = col.seasia, pch = as.character(1:9), font = 2)
points(print(fast.tell)[,1], mcf.congo, col = col.congo, pch = as.character(1:9), font = 2)

legend('topleft', pch = as.character(1:9), legend = colnames(X_tropics_norm), cex = 0.8, bty = 'n')

legend('top', lty = 'solid', legend = c('Amazon', 'SE Asia', 'C Africa'),
       text.col = c(col.amaz,col.seasia, col.congo),
       col = c(col.amaz,col.seasia, col.congo)
       , cex = 0.8, bty = 'n')

abline(0,1, lty = 'dashed')


plot(print(fast.tell)[,1], mcf.em.amaz$mean, col = col.amaz, pch = as.character(1:9),
     ylim = c(0,0.42), xlim = c(0,0.32),
     xlab = 'FAST99 first-order sensitivity',
     ylab = 'MCF sensitivity (KS statistic)',
     pty = 'n',
     main = 'MCF using 5000 emulated ensemble members'
     )

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.em.amaz$mean - (2*mcf.em.amaz$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.em.amaz$mean + (2*mcf.em.amaz$sd),
         col = col.amaz,length=0.05, angle=90, code=3)

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.em.seasia$mean - (2*mcf.em.seasia$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.em.seasia$mean + (2*mcf.em.seasia$sd),
         col = col.seasia,length=0.05, angle=90, code=3)

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.em.congo$mean - (2*mcf.em.congo$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.em.congo$mean + (2*mcf.em.congo$sd),
         col = col.congo,length=0.05, angle=90, code=3)

points(print(fast.tell)[,1], mcf.em.amaz$mean, col = col.amaz, pch = as.character(1:9), font = 2)
points(print(fast.tell)[,1], mcf.em.seasia$mean, col = col.seasia, pch = as.character(1:9), font = 2)
points(print(fast.tell)[,1], mcf.em.congo$mean, col = col.congo, pch = as.character(1:9), font = 2)

abline(0,1, lty = 'dashed')

dev.off()

# Just use the 5000 ensemble member example for the paper.
pdf(width = 7, height = 7, file = 'graphics/fast99_vs_mcf3.pdf')
#dev.new(width = 12, height = 7)
par(mar = c(5,5,3,2), las = 1)

plot(print(fast.tell)[,1], mcf.em.amaz$mean, col = col.amaz, pch = as.character(1:9),
     ylim = c(0,0.32), xlim = c(0,0.32),
     xlab = 'FAST99 first-order sensitivity',
     ylab = 'MCF sensitivity (KS statistic)',
     pty = 'n'
     )

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.em.amaz$mean - (2*mcf.em.amaz$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.em.amaz$mean + (2*mcf.em.amaz$sd),
         col = col.amaz,length=0.05, angle=90, code=3)

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.em.seasia$mean - (2*mcf.em.seasia$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.em.seasia$mean + (2*mcf.em.seasia$sd),
         col = col.seasia,length=0.05, angle=90, code=3)

arrows(x0 = print(fast.tell)[,1], y0 =  mcf.em.congo$mean - (2*mcf.em.congo$sd),
         x1 = print(fast.tell)[,1], y1 =  mcf.em.congo$mean + (2*mcf.em.congo$sd),
         col = col.congo,length=0.05, angle=90, code=3)

points(print(fast.tell)[,1], mcf.em.amaz$mean, col = col.amaz, pch = as.character(1:9), font = 2)
points(print(fast.tell)[,1], mcf.em.seasia$mean, col = col.seasia, pch = as.character(1:9), font = 2)
points(print(fast.tell)[,1], mcf.em.congo$mean, col = col.congo, pch = as.character(1:9), font = 2)

abline(0,1, lty = 'dashed')

legend('topleft', pch = as.character(1:9), legend = colnames(X_tropics_norm), cex = 0.8, bty = 'n')

legend('top', lty = 'solid', legend = c('Amazon', 'SE Asia', 'C Africa'),
       text.col = c(col.amaz,col.seasia, col.congo),
       col = c(col.amaz,col.seasia, col.congo)
       , cex = 0.8, bty = 'n')

dev.off()
LS0tCnRpdGxlOiAiU2Vuc2l0aXZpdHkgQW5hbHlzaXMgRWFydGggU3lzdGVtIGNvbmZpZ3VyYXRpb24gb2YgSlVMRVMiCmF1dGhvcjogIkRvdWcgTWNOZWFsbCIKZGF0ZTogIjA5LzA4LzIwMjEiCm91dHB1dDogCiAgICBodG1sX25vdGVib29rOgogICAgICAgIHRvYzogdHJ1ZQogICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgIHRvY19kZXB0aDogMwogICAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQotLS0KCgojIyBQcmVsaW1pbmFyaWVzCkxvYWQgbGlicmFyaWVzLCBmdW5jdGlvbnMgYW5kIGRhdGEuCgpgYGB7ciwgZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30KIyBMb2FkIGhlbHBlciBmdW5jdGlvbnMKICAKa25pdHI6Om9wdHNfY2h1bmskc2V0KGZpZy5wYXRoID0gImZpZ3MvIiwgZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZ3MgPSBGQUxTRSkKCiMgbG9hZCBoZWxwZXIgZnVuY3Rpb25zLCBkYXRhIGFuZCBkbyBwcmVsaW1pbmFyeSBwcm9jZXNzaW5nIG9mIHRoZSBlbnNlbWJsZS4Kc291cmNlKCdKVUxFUy1FUy0xcDAtY29tbW9uLlInKQoKbGlicmFyeShzZW5zaXRpdml0eSkKYGBgCgoKYGBge3J9CiMgSGVscGVyIGZ1bmN0aW9ucwoKIyByb3RhdGUgYSBtYXRyaWMgOTAgZGVncmVlcyBjbG9ja3dpc2UgZm9yIHBsb3R0aW5nCnJvdGF0ZSA8LSBmdW5jdGlvbih4KSB0KGFwcGx5KHgsIDIsIHJldikpCgoKCnNlbnN2YXIgPSBmdW5jdGlvbihvYWF0X3ByZWQsIG4sIGQpewogICMgQ2FsY3VsYXRlIHZhcmlhbmNlIGFzIGEgZ2xvYmFsIHNlbnNpdGl2aXR5IG1lYW5zdXJlCiAgb3V0ID0gcmVwKE5BLGQpCiAgZm9yKGkgaW4gMTpkKXsKICAgIGl4ID0gc2VxKGZyb20gPSAoKGkqbikgLSAobi0xKSksIHRvID0gIChpKm4pLCBieSA9IDEpCiAgICBvdXRbaV0gPSB2YXIob2FhdF9wcmVkJG1lYW5baXhdKQogIH0KICBvdXQKfQoKCnR3b1N0ZXBfc2VucyA8LSBmdW5jdGlvbihYLCB5LCBuPTIxLCBwcmVkdHlwZSA9ICdVSycsIG51Z2dldD1OVUxMLCBudWdnZXRFc3RpbT1GQUxTRSwgbm9pc2VWYXI9TlVMTCwgc2VlZD1OVUxMLCB0cmFjZT1GQUxTRSwgbWF4aXQ9MTAwLAogICAgICAgICAgICAgICAgICAgICAgICBSRVBPUlQ9MTAsIGZhY3RyPTFlNywgcGd0b2w9MC4wLCBwYXJpbml0PU5VTEwsIHBvcHNpemU9MTAwKXsKICAjIFNlbnNpdGl2aXR5IGFuYWx5c2lzIHdpdGggdHdvU3RlcCBlbXVsYXRvci4gCiAgIyBDYWxjdWxhdGVzIHRoZSB2YXJpYW5jZSBvZiB0aGUgb3V0cHV0IHZhcmllZCBvbmUgYXQgYSB0aW1lIGFjcm9zcyBlYWNoIGlucHV0LgogIGQgPSBuY29sKFgpCiAgWF9ub3JtIDwtIG5vcm1hbGl6ZShYKQogIFhfb2FhdCA8LSBvYWF0X2Rlc2lnbihYX25vcm0sIG4sIG1lZCA9IFRSVUUpCiAgY29sbmFtZXMoWF9vYWF0KSA9IGNvbG5hbWVzKFgpCiAgCiAgdHdvU3RlcF9lbSA9IHR3b1N0ZXBfZ2xtbmV0KFg9WCwgeT15LCBudWdnZXQ9bnVnZ2V0LCBudWdnZXRFc3RpbT1udWdnZXRFc3RpbSwgbm9pc2VWYXI9bm9pc2VWYXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlZWQ9c2VlZCwgdHJhY2U9dHJhY2UsIG1heGl0PW1heGl0LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBSRVBPUlQ9UkVQT1JULCBmYWN0cj1mYWN0ciwgcGd0b2w9cGd0b2wsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhcmluaXQ9cGFyaW5pdCwgcG9wc2l6ZT1wb3BzaXplKQogIAogIG9hYXRfcHJlZCA9IHByZWRpY3QodHdvU3RlcF9lbSRlbXVsYXRvciwgbmV3ZGF0YSA9IFhfb2FhdCwgdHlwZSA9IHByZWR0eXBlKQogIAogIHNlbnMgPSBzZW5zdmFyKG9hYXRfcHJlZCA9IG9hYXRfcHJlZCwgbj1uLCBkPWQpCiAgb3V0ID0gc2VucwogIG91dAp9CgoKCm9hYXRTZW5zdmFyS20gPC0gZnVuY3Rpb24oWCwgeSwgbiA9IDIxLCBtZWQgPSBUUlVFLCBob2xkID0gTlVMTCwgIGZvcm11bGEgPSB+LiwgcHJlZHR5cGUgPSAnVUsnLCAuLi4pewogICMgb25lLWF0LWEtdGltZSBzZW5zaXRpdml0eSBzdW1tYXJ5IHdpdGggYSBzdGFuZGFyZCBkaWNla3JpZ2luZyBlbXVsYXRvcgogIAogIGQgPSBuY29sKFgpCiAgWF9ub3JtIDwtIG5vcm1hbGl6ZShYKQogIFhfb2FhdCA8LSBvYWF0X2Rlc2lnbihYX25vcm0sIG4sIG1lZCA9IG1lZCwgaG9sZCA9IGhvbGQpCiAgY29sbmFtZXMoWF9vYWF0KSA9IGNvbG5hbWVzKFgpCiAgCgogIGVtIDwtIGttKGZvcm11bGEgPSBmb3JtdWxhLCBkZXNpZ24gPSBYLCByZXNwb25zZSA9IHksIC4uLikKICAKICBvYWF0X3ByZWQgPSBwcmVkaWN0KGVtLCBuZXdkYXRhID0gWF9vYWF0LCB0eXBlID0gcHJlZHR5cGUpCiAgCiAgc2VucyA9IHNlbnN2YXIob2FhdF9wcmVkID0gb2FhdF9wcmVkLCBuPW4sIGQ9ZCkKICBvdXQgPSBzZW5zCiAgb3V0CiAgCn0KCm9hYXRTZW5zdmFyS21MaXN0IDwtIGZ1bmN0aW9uKFgsIGVtX2xpc3QsIG4gPSAyMSwgbWVkID0gVFJVRSwgaG9sZCA9IE5VTEwsICBmb3JtdWxhID0gfi4sIHByZWR0eXBlID0gJ1VLJywgLi4uKXsKICAjIG9uZS1hdC1hLXRpbWUgc2Vuc2l0aXZpdHkgc3VtbWFyeSB3aXRoIGEgc3RhbmRhcmQgZGljZWtyaWdpbmcgZW11bGF0b3IKICAKICBkID0gbmNvbChYKQogIFhfb2FhdCA8LSBvYWF0X2Rlc2lnbihYLCBuLCBtZWQgPSBtZWQsIGhvbGQgPSBob2xkKQogIGNvbG5hbWVzKFhfb2FhdCkgPSBjb2xuYW1lcyhYKQogIAogIGVtIDwtIGVtX2xpc3RbW2ldXQogIAogIG9hYXRfcHJlZCA9IHByZWRpY3QoZW0sIG5ld2RhdGEgPSBYX29hYXQsIHR5cGUgPSBwcmVkdHlwZSkKICAKICBzZW5zID0gc2Vuc3ZhcihvYWF0X3ByZWQgPSBvYWF0X3ByZWQsIG49biwgZD1kKQogIG91dCA9IHNlbnMKICBvdXQKICAKfQoKCgoKCm9hYXRTZW5zdmFyU3VtbWFyeVBsb3QgPC0gZnVuY3Rpb24ob2F0X3NlbnNfbWF0KXsKICAKICAjIHJlbGllcyBvbiByb3RhdGUoKSwgZmllbGRzLCAgd2hpY2ggbmVlZCBzb3J0aW5nIGZvciB0cmFuc2ZlciB0byBwYWNrYWdlCiAgCiAgeW5hbWVzIDwtIHJvd25hbWVzKG9hdF9zZW5zX21hdCkKICB4bmFtZXMgPC0gY29sbmFtZXMob2F0X3NlbnNfbWF0KQogIAogIG5vcm1zZW5zIDwtIG5vcm1hbGl6ZSh0KG9hdF9zZW5zX21hdCkpCiAgbm9ybXNlbnNfbWVhbiA8LSBhcHBseShub3Jtc2VucywxLCBtZWFuKQogIAogIHNvcnRfaXggPC0gc29ydChub3Jtc2Vuc19tZWFuLCBkZWNyZWFzaW5nID0gVFJVRSwgaW5kZXgucmV0dXJuID0gVFJVRSkKICAKICBwYXIobWFyID0gYygxNSwxMiw1LDEpLCBtZnJvdyA9IGMoMSwyKSkKICAKICBsYXlvdXQobWF0cml4KGMoMSwxLDIpLCBuY29sID0gMywgbnJvdyA9IDEpKQogIAogIGltYWdlKHJvdGF0ZShub3Jtc2Vuc1tzb3J0X2l4JGl4LCBdKSwgYXhlcyA9IEZBTFNFLCBjb2wgPSBibHVlcykKICAKICBheGlzKDEsIGF0ID0gc2VxKGZyb20gPSAwLCB0byA9IDEsIGxlbmd0aC5vdXQgPSBsZW5ndGgoeW5hbWVzKSksIGxhYmVscyA9IHluYW1lcywgbGFzID0gMywgY2V4LmF4aXMgPSAxLjIpCiAgYXhpcygyLCBhdCA9IHNlcShmcm9tID0gMSwgdG8gPSAwLCBsZW5ndGgub3V0ID0gbGVuZ3RoKHhuYW1lcykpLCBsYWJlbHMgPSB4bmFtZXNbc29ydF9peCRpeF0sIGxhcyA9IDEsIGNleC5heGlzID0gMS4yKQogIG10ZXh0KCdPbmUtYXQtYS10aW1lIHNlbnNpdGl2aXR5Jywgc2lkZSA9IDMsIGFkaiA9IDAsIGxpbmUgPSAyLCBjZXggPSAxKQogIAogIGxhYl9peCA8LSAoMTpsZW5ndGgoeG5hbWVzKSkgLSAwLjUKICAKICBwYXIoeWF4cyA9ICdpJywgbWFyID0gYygxNSwxLDUsNSkpCiAgcGxvdChyZXYobm9ybXNlbnNfbWVhbltzb3J0X2l4JGl4XSksIGxhYl9peCwgeGxhYiA9ICdtZWFuIG9hYXQgdmFyaWFuY2UgKG5vcm1hbGl6ZWQpJywgeWxhYiA9ICcnLCB5bGltID0gYygwLGxlbmd0aCh4bmFtZXMpKSwgdHlwZSA9ICduJywgeWF4dCA9ICduJykKICBhYmxpbmUoaCA9IGxhYl9peCwgY29sID0gJ2dyZXknLCBsdHkgPSAnZGFzaGVkJykKICBwb2ludHMoIHJldihub3Jtc2Vuc19tZWFuW3NvcnRfaXgkaXhdKSxsYWJfaXgsIGNvbCA9IHppc3NvdTVbMV0sIHBjaCA9IDE5LCBjZXggPSAxLjUpCiAgCiAgaW1hZ2UucGxvdChsZWdlbmQub25seSA9IFRSVUUsCiAgICAgICAgICAgICB6bGltID0gYygwLDEpLAogICAgICAgICAgICAgY29sID0gYmx1ZXMsCiAgICAgICAgICAgICBsZWdlbmQuYXJncyA9IGxpc3QodGV4dCA9ICdSZWxhdGl2ZSBzZW5zaXRpdml0eScsIHNpZGUgPSAzLCBsaW5lID0gMSksCiAgICAgICAgICAgICBob3Jpem9udGFsID0gVFJVRQogICkKICAKfQoKCgoKc2Vuc01hdFN1bW1hcnlQbG90IDwtIGZ1bmN0aW9uKHNlbnNfbWF0LCBjb2wgPSBibHVlcywgbWFpbnRleHQgPSAnU2Vuc2l0aXZpdHkgTWF0cml4JywgeGxhYiA9ICdzZW5zaXRpdml0eSBzdW1tYXJ5Jyl7CiAgIyBTdW1tYXJ5IHBsb3RzIG9mIGEgc2Vuc2l0aXZpdHkgbWF0cml4CiAgCiAgCiAgIyByZWxpZXMgb24gcm90YXRlKCksIGZpZWxkcywgIHdoaWNoIG5lZWQgc29ydGluZyBmb3IgdHJhbnNmZXIgdG8gcGFja2FnZQogIAogIHluYW1lcyA8LSByb3duYW1lcyhzZW5zX21hdCkKICB4bmFtZXMgPC0gY29sbmFtZXMoc2Vuc19tYXQpCiAgCiAgbm9ybXNlbnMgPC0gbm9ybWFsaXplKHQoc2Vuc19tYXQpKQogIG5vcm1zZW5zX21lYW4gPC0gYXBwbHkobm9ybXNlbnMsMSwgbWVhbikKICAKICBzb3J0X2l4IDwtIHNvcnQobm9ybXNlbnNfbWVhbiwgZGVjcmVhc2luZyA9IFRSVUUsIGluZGV4LnJldHVybiA9IFRSVUUpCiAgCiAgcGFyKG1hciA9IGMoMTUsMTIsNSwxKSwgbWZyb3cgPSBjKDEsMikpCiAgCiAgbGF5b3V0KG1hdHJpeChjKDEsMSwyKSwgbmNvbCA9IDMsIG5yb3cgPSAxKSkKICAKICBpbWFnZShyb3RhdGUobm9ybXNlbnNbc29ydF9peCRpeCwgXSksIGF4ZXMgPSBGQUxTRSwgY29sID0gY29sKQogIAogIGF4aXMoMSwgYXQgPSBzZXEoZnJvbSA9IDAsIHRvID0gMSwgbGVuZ3RoLm91dCA9IGxlbmd0aCh5bmFtZXMpKSwgbGFiZWxzID0geW5hbWVzLCBsYXMgPSAzLCBjZXguYXhpcyA9IDEuMikKICBheGlzKDIsIGF0ID0gc2VxKGZyb20gPSAxLCB0byA9IDAsIGxlbmd0aC5vdXQgPSBsZW5ndGgoeG5hbWVzKSksIGxhYmVscyA9IHhuYW1lc1tzb3J0X2l4JGl4XSwgbGFzID0gMSwgY2V4LmF4aXMgPSAxLjIpCiAgbXRleHQobWFpbnRleHQsIHNpZGUgPSAzLCBhZGogPSAwLCBsaW5lID0gMiwgY2V4ID0gMSkKICAKICBsYWJfaXggPC0gKDE6bGVuZ3RoKHhuYW1lcykpIC0gMC41CiAgCiAgcGFyKHlheHMgPSAnaScsIG1hciA9IGMoMTUsMSw1LDUpKQogIHBsb3QocmV2KG5vcm1zZW5zX21lYW5bc29ydF9peCRpeF0pLCBsYWJfaXgsIHhsYWIgPSB4bGFiLCB5bGFiID0gJycsIHlsaW0gPSBjKDAsbGVuZ3RoKHhuYW1lcykpLCB0eXBlID0gJ24nLCB5YXh0ID0gJ24nKQogIGFibGluZShoID0gbGFiX2l4LCBjb2wgPSAnZ3JleScsIGx0eSA9ICdkYXNoZWQnKQogIHBvaW50cyggcmV2KG5vcm1zZW5zX21lYW5bc29ydF9peCRpeF0pLGxhYl9peCwgY29sID0gemlzc291NVsxXSwgcGNoID0gMTksIGNleCA9IDEuNSkKICAKICBpbWFnZS5wbG90KGxlZ2VuZC5vbmx5ID0gVFJVRSwKICAgICAgICAgICAgIHpsaW0gPSBjKDAsMSksCiAgICAgICAgICAgICBjb2wgPSBjb2wsCiAgICAgICAgICAgICBsZWdlbmQuYXJncyA9IGxpc3QodGV4dCA9ICdSZWxhdGl2ZSBzZW5zaXRpdml0eScsIHNpZGUgPSAzLCBsaW5lID0gMSksCiAgICAgICAgICAgICBob3Jpem9udGFsID0gVFJVRQogICkKfQoKCgoKYnBfY29udmVydCA8LSBmdW5jdGlvbihmYXN0bW9kZWwpewogICMgZ2V0IHRoZSBGQVNUIHN1bW1hcnkgaW50byBhbiBlYXNpZXIgZm9ybWF0IGZvciBiYXJwbG90CiAgZmFzdF9zdW1tIDwtIHByaW50KGZhc3Rtb2RlbCkKICBmYXN0X2RpZmYgPC0gZmFzdF9zdW1tWyAsMl0gLSBmYXN0X3N1bW1bICwxXQogIGZhc3RfYnAgPC0gdChjYmluZChmYXN0X3N1bW1bICwxXSwgZmFzdF9kaWZmKSkKICBmYXN0X2JwCn0KCgptdWx0aUZBU1Q8LSBmdW5jdGlvbihYLCBZLCBmaXRfbGlzdCA9IE5VTEwsIG4gPSAxMDAwKXsKICAKICAjIEdlbmVyYXRlIGEgZGVzaWduIGZvciB0aGUgRkFTVDk5IGFuYWx5c2lzCiAgWF9mYXN0IDwtIGZhc3Q5OShtb2RlbCA9IE5VTEwsIGZhY3RvcnMgPSBjb2xuYW1lcyhYKSwgbiA9IG4sCiAgICAgICAgICAgICAgICAgICBxID0gInF1bmlmIiwgcS5hcmcgPSBsaXN0KG1pbiA9IDAsIG1heCA9IDEpKQogIAogIAogIGlmKGlzLm51bGwoZml0X2xpc3QpKXsKICAgIGZpdF9saXN0IDwtIGNyZWF0ZUttRml0TGlzdChYID0gWCwgWSA9IFkpCiAgfQogIAogIGVsc2V7CiAgZml0X2xpc3QgPC0gZml0X2xpc3QKICB9CiAgCiAgZmFzdF90ZWxsX2xpc3QgPC0gdmVjdG9yKG1vZGUgPSAnbGlzdCcsIGxlbmd0aCA9IG5jb2woWSkpCiAgCiAgCiAgZm9yKGkgaW4gMTpuY29sKFkpKXsKICAgIAogICAgZml0IDwtIGZpdF9saXN0W1tpXV0KICAgICMgUHJlZGljdCB0aGUgcmVzcG9uc2UgYXQgdGhlIEZBU1Q5OSBkZXNpZ24gcG9pbnRzIHVzaW5nIHRoZSBlbXVsYXRvcgogICAgcHJlZF9mYXN0IDwtIHByZWRpY3QoZml0LCBuZXdkYXRhID0gWF9mYXN0JFgsIHR5cGUgPSAnVUsnKQogICAgCiAgICAjIENhbGN1bGF0ZSB0aGUgc2Vuc2l0aXZpdHkgaW5kaWNlcwogICAgZmFzdF90ZWxsIDwtIHRlbGwoWF9mYXN0LCBwcmVkX2Zhc3QkbWVhbikKICAgIAogICAgZmFzdF90ZWxsX2xpc3RbW2ldXSA8LSBmYXN0X3RlbGwKICAgIAogIH0KICAKICByZXR1cm4obGlzdChmaXRfbGlzdCA9IGZpdF9saXN0LCBmYXN0X3RlbGxfbGlzdCA9IGZhc3RfdGVsbF9saXN0KSkKICAKfQoKb2FhdFNlbnN2YXJSYW5rIDwtIGZ1bmN0aW9uKG9hdF9zZW5zX21hdCl7CiAgCiAgeW5hbWVzIDwtIHJvd25hbWVzKG9hdF9zZW5zX21hdCkKICB4bmFtZXMgPC0gY29sbmFtZXMob2F0X3NlbnNfbWF0KQogIAogIG5vcm1zZW5zIDwtIG5vcm1hbGl6ZSh0KG9hdF9zZW5zX21hdCkpCiAgbm9ybXNlbnNfbWVhbiA8LSBhcHBseShub3Jtc2VucywxLCBtZWFuKQogIAogIHJhbmsgPC0gcmFuaygtbm9ybXNlbnNfbWVhbikKICAKICAKICByZXR1cm4obGlzdChtZWFuID0gbm9ybXNlbnNfbWVhbiwgcmFuayA9IHJhbmspKQogIAp9CgpTZW5zUmFuayA8LSBmdW5jdGlvbihzZW5zX21hdCl7CiAgCiAgIyBzdW1tYXJpc2luZyBhbmQgcmFua2luZyBwYXJhbWV0ZXJzIGluIGEgc2Vuc2l0aXZpdHkgbWF0cml4CiAgCiAgeW5hbWVzIDwtIHJvd25hbWVzKHNlbnNfbWF0KQogIHhuYW1lcyA8LSBjb2xuYW1lcyhzZW5zX21hdCkKICAKICBub3Jtc2VucyA8LSBub3JtYWxpemUodChzZW5zX21hdCkpCiAgbm9ybXNlbnNfbWVhbiA8LSBhcHBseShub3Jtc2VucywxLCBtZWFuKQogIAogIHJhbmsgPC0gcmFuaygtbm9ybXNlbnNfbWVhbikKICAKICByZXR1cm4obGlzdChtZWFuID0gbm9ybXNlbnNfbWVhbiwgcmFuayA9IHJhbmspKQogIAp9CgpgYGAKCgoKCnRvIGZpbmQgdGhlICJzdGFuZGFyZCIgdmFsdWUgaW4gbm9ybWFsaXplZCBzcGFjZSwgd2UgY2FuIG5vcm1hbGl6ZSBhIHZlY3RvciBvZiAiMXMiIHdpdGggcmVzcGVjdCB0byB0aGUgb3JpZ2luYWwgZGVzaWduCmBgYHtyfQoKWF9zdGFuZGFyZCA8LSBtYXRyaXgocmVwKDEsZCksIG5jb2wgPSBkLCBucm93ID0gMSkKClhfc3RhbmRhcmRfbm9ybSA8LSBub3JtYWxpemUoWF9zdGFuZGFyZCwgd3J0ID0gbGhzKQoKWF9sZXZlbDFhX3Vubm9ybSA8LSB1bm5vcm1hbGl6ZShYX2xldmVsMWEsIHVuLm1pbnMgPSBsaHNfbWluLCB1bi5tYXhlcyA9IGxoc19tYXgpCgpYX2xldmVsMWFfd2F2ZTAxX3Vubm9ybSA8LSB1bm5vcm1hbGl6ZShYX2xldmVsMWFfd2F2ZTAxLCB1bi5taW5zID0gbGhzX21pbiwgdW4ubWF4ZXMgPSBsaHNfbWF4KQoKYGBgCgoKCiMgU2Vuc2l0aXZpdHkgYXQgbGV2ZWwxYSBjb25zdHJhaW50IChmMF9pbyBhbmQgYl93bF9pbyB0cnVuY2F0ZWQpCkRlZmluZSBjb25zdHJhaW50IGxldmVsIDFhIGFzIHRob3NlIG1lbWJlcnMgdGhhdCBydW4sIGFuZCBoYXZlIEYwX2lvIDwwLjkgJiBiX3dsX2lvID4gMC4xNSAobm9ybWFsaXNlZCkuCgojIyBNb2Rlcm4gdmFsdWUgc2Vuc2l0aXZpdHkKCmBgYHtyfQojaWYgKGZpbGUuZXhpc3RzKCJvYWF0X2xldmVsMWFfWS5yZGF0YSIpKSB7CiMgIGxvYWQoIm9hYXRfbGV2ZWwxYV9ZLnJkYXRhIikKI30gZWxzZSB7CiAgCm9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX1kgPC0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSwgbmNvbCA9IG5jb2woWF9sZXZlbDFhKSkKCmZvcihpIGluIDE6bGVuZ3RoKHlfbmFtZXNfc3VtKSl7CiAgCiAgeW5hbWUgPC0geV9uYW1lc19zdW1baV0KICB5IDwtIFlfbGV2ZWwxYVssIHluYW1lXQogIG9hdCA8LSBvYWF0U2Vuc3ZhckttTGlzdChYID0gWF9sZXZlbDFhLCBlbV9saXN0ID0gZW1saXN0X2ttX1lfbGV2ZWwxYSwgbWVkID0gRkFMU0UsIGhvbGQgPSBYX3N0YW5kYXJkX25vcm0pCiAgb2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfWVtpLCBdIDwtIG9hdAp9Cgojc2F2ZSh5X25hbWVzX3N1bSwgb2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfWSwgZmlsZSA9ICJvYWF0X2xldmVsMWFfWS5yZGF0YSIpCiN9Cgpyb3duYW1lcyhvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV9ZKSA8LSB5X25hbWVzX3N1bQpjb2xuYW1lcyhvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV9ZKSA8LSBjb2xuYW1lcyhYX2xldmVsMWEpCgojbm9ybXNlbnNfbGV2ZWwxYV9ZIDwtIG5vcm1hbGl6ZSh0KG9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX1kpKQoKYGBgCgoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA4fQoKI3BkZihmaWxlID0gJ2ZpZ3Mvb2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfWS5wZGYnLCB3aWR0aCA9IDcsIGhlaWdodCA9IDgpCm9hYXRTZW5zdmFyU3VtbWFyeVBsb3Qob2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfWSkKCiNkZXYub2ZmKCkKCmBgYAoKIyMgQW5vbWFseSAoQ2hhbmdlIDE4NTAgLSAyMDEzKSBzZW5zaXRpdml0aWVzCgpgYGB7cn0KI2lmIChmaWxlLmV4aXN0cygib2FhdF9sZXZlbDFhX1lBbm9tLnJkYXRhIikpIHsKIyAgbG9hZCgib2FhdF9sZXZlbDFhX1lBbm9tLnJkYXRhIikKI30gZWxzZSB7CiAgCm9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX1lBbm9tIDwtIG1hdHJpeChOQSwgbnJvdyA9IGxlbmd0aCh5X25hbWVzX3N1bSksIG5jb2wgPSBuY29sKFhfbGV2ZWwxYSkpCgpmb3IoaSBpbiAxOmxlbmd0aCh5X25hbWVzX3N1bSkpewogIAogIHluYW1lIDwtIHlfbmFtZXNfc3VtW2ldCiAgeSA8LSBZQW5vbV9sZXZlbDFhWywgeW5hbWVdCiAgb2F0IDwtIG9hYXRTZW5zdmFyS21MaXN0KFggPSBYX2xldmVsMWEsIGVtX2xpc3QgPSBlbWxpc3Rfa21fWUFub21fbGV2ZWwxYSwgIG1lZCA9IEZBTFNFLCBob2xkID0gWF9zdGFuZGFyZF9ub3JtKQogIG9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX1lBbm9tW2ksIF0gPC0gb2F0Cn0KCiNzYXZlKHlfbmFtZXNfc3VtLCBvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV9ZQW5vbSwgZmlsZSA9ICJvYWF0X2xldmVsMWFfWUFub20ucmRhdGEiKQojfQoKCnJvd25hbWVzKG9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX1lBbm9tKSA8LSB5X25hbWVzX3N1bQpjb2xuYW1lcyhvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV9ZQW5vbSkgPC0gY29sbmFtZXMoWF9sZXZlbDFhKQoKIyBOb3JtYWxpc2Ugc2Vuc2l0aXZpdGllcwojbm9ybXNlbnNfbGV2ZWwxYV9ZQW5vbSA8LSBub3JtYWxpemUodChvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV9ZQW5vbSkpCgpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDh9CgojcGRmKGZpbGUgPSAnZmlncy9vYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV9ZQW5vbS5wZGYnLCB3aWR0aCA9IDcsIGhlaWdodCA9IDgpCm9hYXRTZW5zdmFyU3VtbWFyeVBsb3Qob2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfWUFub20pCiNkZXYub2ZmKCkKCmBgYAoKCgojIyBPbmUtYXQtYS10aW1lIHNlbnNpdGl2aXR5IGFuYWx5c2lzIG9mIGNvbnN0cmFpbmluZyB2YXJpYWJsZXMgZm9yIHVuZGVyc3RhbmRpbmcgbW9kZWwgcmVzcG9uc2UKIkNvbnN0cmFpbmluZyB2YXJpYWJsZXMiIGJlaW5nIHRob3NlIHdlIHVzZSB0byBjb25zdHJhaW4gdGhlIG1vZGVsIChucHAsIG5icCwgY1NvaWwgYW5kIGNWZWcpLgpJdCdzIGhhcmQgdG8gbWFpbnRhaW4gYSBoaWdoIHZlZ2V0YXRpb24gY2FyYm9uIGluIHBhcnRpY3VsYXIuICAKCkZ1cnRoZXIgaWRlYTogV2hhdCBwYXJhbWV0ZXIgdmFsdWVzIG1pZ2h0IHlvdSBjaG9vc2UgdG8gZG8gdGhpcywgYW5kIHdoYXQgbWlnaHQgYmUgdGhlIHRyYWRlLW9mZnMgeW91IGhhdmUgdG8gbWFrZT8KCgpgYGB7cn0KI2ZpdF9saXN0X2NvbnN0X2xldmVsMWEgPC0gY3JlYXRlS21GaXRMaXN0KFggPSBYX2xldmVsMWEsIFkgPSBZX2NvbnN0X2xldmVsMWFfc2NhbGVkKQoKWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9saXN0IDwtIG1hdDJsaXN0KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpCmZpdF9saXN0X2NvbnN0X2xldmVsMWEgPC0gbWNsYXBwbHkoWCA9IFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRfbGlzdCwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG1jLmNvcmVzID0gNCwgY29udHJvbCA9IGxpc3QodHJhY2UgPSBGQUxTRSkpCgpgYGAKCgpgYGB7cn0KIyBDaGVjayB0aGF0IG9hdFNlbnNWYXIgYW5kIHRoZSBwbG90dGluZyBtYWtlIHNlbnNlCgpvYWF0X3NlbnNfY1ZlZyA8LSBvYWF0U2Vuc3ZhckttKFggPSBYX2xldmVsMWEsIHkgPSBZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywiY1ZlZ19sbmRfc3VtIl0pCgpYX29hYXRfbGV2ZWwxYSA8LSBvYWF0X2Rlc2lnbihYX2xldmVsMWEsIG49MjEsIG1lZCA9IEZBTFNFLCBob2xkID0gWF9zdGFuZGFyZF9ub3JtKQoKY29sbmFtZXMoWF9vYWF0X2xldmVsMWEpID0gY29sbmFtZXMoWCkKCnlfb2FhdCA8LSBwcmVkaWN0LmttKGZpdF9saXN0X2NvbnN0X2xldmVsMWFbWzRdXSwgbmV3ZGF0YSA9IFhfb2FhdF9sZXZlbDFhLCB0eXBlID0gJ1VLJykKCmBgYAoKCkZpcnN0LCB3aGF0IHBhcmFtZXRlcnMgYWZmZWN0IHZlZ2V0YXRpb24gY2FyYm9uIGFuZCBob3c/IEhvdyBzdXJlIGFyZSB3ZSBhYm91dCB0aGF0PwoKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMH0KCgpvYWF0TGluZVBsb3QoWF9vYWF0ID0gWF9vYWF0X2xldmVsMWEsIHlfb2FhdF9tZWFuID0geV9vYWF0JG1lYW4sIHlfb2FhdF9zZCA9IHlfb2FhdCRzZCwgCiAgICAgICAgICAgICBuX29hYXQgPSAyMSxuciA9IDYsIG5jID0gNikgCgoKYGBgCgoKYGBge3J9CgpZX29hYXRfY29uc3RfbGV2ZWwxYV9zY2FsZWQgPC0gbWF0cml4KG5jb2wgPSBuY29sKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpLCBucm93ID0gbnJvdyhYX29hYXRfbGV2ZWwxYSkpCgpmb3IoaSBpbiAxOm5jb2woWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkpewoKICB5X29hYXQgPC0gcHJlZGljdC5rbShmaXRfbGlzdF9jb25zdF9sZXZlbDFhW1tpXV0sIG5ld2RhdGEgPSBYX29hYXRfbGV2ZWwxYSwgdHlwZSA9ICdVSycpCiAgWV9vYWF0X2NvbnN0X2xldmVsMWFfc2NhbGVkWyxpXSA8LSB5X29hYXQkbWVhbgp9CgpgYGAKCgpXaGF0IG1pZ2h0IGJlIHRoZSB0cmFkZS1vZmZzIGZvciBhIGhpZ2ggKG9yIGFjY3VyYXRlKSB2ZWdldGF0aW9uIGNhcmJvbj8gYXJlIHRoZXkgYWNjZXB0YWJsZT8gUGxvdCB0aGUgb2FhdCBzZW5zaXRpdml0eSBvZiB0aGUgb3RoZXIgMyBvdXRwdXRzIHdlJ3JlIGNhbGlicmF0aW5nIG9uLiAKClBsb3R0aW5nIHRoZXNlIGdyYXBocyBpbiB0aGUgb3JpZ2luYWwgaW5wdXQgc3BhY2UgKG11bHRpcGxpY2F0aW9uIGZhY3RvcnMpIGFuZCBwcm92aWRpbmcgdGhlIHN0YW5kYXJkIGhhcyB0aGUgcGxlYXNpbmcgc2lkZSBlZmZlY3Qgb2Ygc2hvd2luZyB3aGF0IHlvdSBjb3VsZCBkbyB0byBzdGFuZGFyZCBpbnB1dHMgdG8gaW5jcmVhc2Ugb3IgZGVjcmVhc2UgYSBwYXJ0aWN1bGFyIG91dHB1dC4KCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQgPSAxMH0KWV9vYWF0X2NvbnN0X2xldmVsMWFfc2NhbGVkX25vcm0gPC0gbm9ybWFsaXplKFlfb2FhdF9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCiAgICAgICAgb2FhdExpbmVQbG90TXVsdGkgPC0gZnVuY3Rpb24oWF9vYWF0LCBZX29hYXQsIG5fb2FhdCwgbnIsIG5jLCBjb2xzLCAuLi4pewogIAogICAgICAgICAgcGFyKG1mcm93ID0gYyhucixuYyksIG9tYSA9IGMoMC4xLDAuMSwzLDAuMSksIG1hciA9IGMoMiwyLDMsMSksIGxhcyA9IDEpCiAgCiAgICAgICAgICBmb3IoaSBpbiAxOm5jb2woWF9vYWF0KSl7CiAgICAgICAgICAgIGl4IDwtIHNlcShmcm9tID0gKChpKm5fb2FhdCkgLSAobl9vYWF0LTEpKSwgdG8gPSAgKGkqbl9vYWF0KSwgYnkgPSAxKQogICAgCiAgICAgICAgICAgIHBsb3QoWF9vYWF0W2l4LGldLCBZX29hYXRbaXgsMV0sCiAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMCwxKSwKICAgICAgICAgICAgICAgICB4bGFiID0gY29sbmFtZXMoWF9vYWF0KVtpXSwKICAgICAgICAgICAgICAgICB0eXBlPSAnbicsCiAgICAgICAgICAgICAgICAgYnR5ID0gJ24nKQogIAogICAgICAgICAgICBmb3IoaiBpbiAxOm5jb2woWV9vYWF0KSl7CiAgICAgICAgICAgICAgbGluZXMoWF9vYWF0W2l4LGldLCBZX29hYXRbaXgsIGpdLCBsdHkgPSAnc29saWQnLCBjb2wgPSBjb2xzW2pdLCAuLi4pCiAgICAgICAgICAgICAgYWJsaW5lKHYgPSAxLCBsdHkgPSAnZGFzaGVkJywgY29sID0gJ2dyZXknKQogICAgICAgICAgICAgIG10ZXh0KGNvbG5hbWVzKFhfb2FhdClbaV0sIHNpZGUgPSAzLCBsaW5lID0gMC41KQogIAogICAgICAgICAgICB9CiAgCiAgICAgICAgICB9CiAgICAKICAgICAgICB9CgpYX29hYXRfbGV2ZWwxYV91bm5vcm0gPC0gdW5ub3JtYWxpemUoWF9vYWF0X2xldmVsMWEsIHVuLm1pbnMgPSBsaHNfbWluLCB1bi5tYXhlcyA9IGxoc19tYXgpICAgICAgICAKI3BkZihmaWxlID0gJ2ZpZ3MvWV9vYWF0X2NvbnN0X2xldmVsMWFfc2NhbGVkX25vcm0ucGRmJywgd2lkdGggPSAxMCwgaGVpZ2h0ID0gMTApCm9hYXRMaW5lUGxvdE11bHRpKFhfb2FhdCA9IFhfb2FhdF9sZXZlbDFhX3Vubm9ybSwgWV9vYWF0ID0gWV9vYWF0X2NvbnN0X2xldmVsMWFfc2NhbGVkX25vcm0gLCAgbl9vYWF0ID0gMjEsIG5yID0gNiwgbmMgPSA2LAogICAgICAgICAgICAgICAgICBsd2QgPSAzLCBjb2wgPSBjYlBhbFtjKDEsMiw2LDgpXSkKICAKcmVzZXQoKQpsZWdlbmQoJ3RvcCcsIGMoJ25icCcsICducHAnLCAnY3NvaWwnLCAnY3ZlZycpLCBjb2wgPSBjYlBhbFtjKDEsMiw2LDgpXSwgbHR5ID0gJ3NvbGlkJywgbHdkID0gMywgaG9yaXogPSBUUlVFKQojZGV2Lm9mZigpCgpgYGAKCgojIyBVcGRhdGUgZm9yIHdhdmUwMQoKCmBgYHtyfQojIEJ1aWxkIGxpc3Qgb2YgZW11bGF0b3JzIGZvciBib3RoIHdhdmVzLCBzdGFuZGFyZCBjb25zdHJhaW50IHBhcmFtZXRlcnMuCgpZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9saXN0IDwtIG1hdDJsaXN0KFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkKQpmaXRfbGlzdF9jb25zdF9sZXZlbDFhX3dhdmUwMSA8LSBtY2xhcHBseShYID0gWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfbGlzdCwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhX3dhdmUwMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYy5jb3JlcyA9IDQsIGNvbnRyb2wgPSBsaXN0KHRyYWNlID0gRkFMU0UpKQoKYGBgCgpgYGB7cn0KCllfb2FhdF9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQgPC0gbWF0cml4KG5jb2wgPSBuY29sKFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkKSwgbnJvdyA9IG5yb3coWF9vYWF0X2xldmVsMWEpKQoKZm9yKGkgaW4gMTpuY29sKFlfb2FhdF9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQpKXsKCiAgeV9vYWF0IDwtIHByZWRpY3Qua20oZml0X2xpc3RfY29uc3RfbGV2ZWwxYV93YXZlMDFbW2ldXSwgbmV3ZGF0YSA9IFhfb2FhdF9sZXZlbDFhLCB0eXBlID0gJ1VLJykKICBZX29hYXRfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkWyxpXSA8LSB5X29hYXQkbWVhbgp9CgpgYGAKCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQgPSAxMH0KWV9vYWF0X2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9ub3JtIDwtIG5vcm1hbGl6ZShZX29hYXRfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkKQoKI3BkZihmaWxlID0gJ2ZpZ3MvWV9vYWF0X2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9ub3JtLnBkZicsIHdpZHRoID0gMTAsIGhlaWdodCA9IDEwKQpvYWF0TGluZVBsb3RNdWx0aShYX29hYXQgPSBYX29hYXRfbGV2ZWwxYV91bm5vcm0sIFlfb2FhdCA9IFlfb2FhdF9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWRfbm9ybSAsICBuX29hYXQgPSAyMSwgbnIgPSA2LCBuYyA9IDYsCiAgICAgICAgICAgICAgICAgIGx3ZCA9IDMsIGNvbCA9IGNiUGFsW2MoMSwyLDYsOCldKQogIApyZXNldCgpCmxlZ2VuZCgndG9wJywgYygnbmJwJywgJ25wcCcsICdjc29pbCcsICdjdmVnJyksIGNvbCA9IGNiUGFsW2MoMSwyLDYsOCldLCBsdHkgPSAnc29saWQnLCBsd2QgPSAzLCBob3JpeiA9IFRSVUUpCiNkZXYub2ZmKCkKCmBgYAoKCmBgYHtyfQpZX3N1bV9sZXZlbDFhX3dhdmUwMV9saXN0IDwtIG1hdDJsaXN0KFlfc3VtX2xldmVsMWFfd2F2ZTAxKQoKaWYgKGZpbGUuZXhpc3RzKCJlbWxpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV8yMDIyLTA1LTI0LnJkYXRhIikpIHsKICBsb2FkKCJlbWxpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV8yMDIyLTA1LTI0LnJkYXRhIikKfSBlbHNlIHsKICAKICAjIEhlcmUsIHRoZSBsaXN0IGlzIGEgbGlzdCB2ZXJzaW9uIG9mIHRoZSBtYXRyaXggWV8KICBlbWxpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMSA8LSBtY2xhcHBseShYID0gWV9zdW1fbGV2ZWwxYV93YXZlMDFfbGlzdCwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhX3dhdmUwMSwgbWMuY29yZXMgPSA0KSAKICAKICBzYXZlKCBlbWxpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMSwgZmlsZSA9ICJlbWxpc3Rfa21fWV9sZXZlbDFhXzIwMjItMDUtMjQucmRhdGEiKQogIAp9CgoKCmBgYAoKYGBge3J9CiNpZiAoZmlsZS5leGlzdHMoIm9hYXRfbGV2ZWwxYV9ZLnJkYXRhIikpIHsKIyAgbG9hZCgib2FhdF9sZXZlbDFhX1kucmRhdGEiKQojfSBlbHNlIHsKICAKb2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfd2F2ZTAxX1kgPC0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSwgbmNvbCA9IG5jb2woWF9sZXZlbDFhKSkKCmZvcihpIGluIDE6bGVuZ3RoKHlfbmFtZXNfc3VtKSl7CiAgCiAgeW5hbWUgPC0geV9uYW1lc19zdW1baV0KICB5IDwtIFlfbGV2ZWwxYVssIHluYW1lXQogIG9hdCA8LSBvYWF0U2Vuc3ZhckttTGlzdChYID0gWF9sZXZlbDFhX3dhdmUwMSwgZW1fbGlzdCA9IGVtbGlzdF9rbV9ZX2xldmVsMWFfd2F2ZTAxLCBtZWQgPSBGQUxTRSwgaG9sZCA9IFhfc3RhbmRhcmRfbm9ybSkKICBvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV93YXZlMDFfWVtpLCBdIDwtIG9hdAp9Cgojc2F2ZSh5X25hbWVzX3N1bSwgb2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfWSwgZmlsZSA9ICJvYWF0X2xldmVsMWFfWS5yZGF0YSIpCiN9Cgpyb3duYW1lcyhvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV93YXZlMDFfWSkgPC0geV9uYW1lc19zdW0KY29sbmFtZXMob2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfd2F2ZTAxX1kpIDwtIGNvbG5hbWVzKFhfbGV2ZWwxYSkKCiNub3Jtc2Vuc19sZXZlbDFhX1kgPC0gbm9ybWFsaXplKHQob2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfWSkpCgpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDh9CgojcGRmKGZpbGUgPSAnZmlncy9vYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV93YXZlMDFfWS5wZGYnLCB3aWR0aCA9IDcsIGhlaWdodCA9IDgpCm9hYXRTZW5zdmFyU3VtbWFyeVBsb3Qob2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfd2F2ZTAxX1kpCgojZGV2Lm9mZigpCgpgYGAKCiMjIEFub21hbHkgd2F2ZTAxCgpgYGB7cn0KWUFub21fc3VtX2xldmVsMWFfd2F2ZTAxX2xpc3QgPC0gbWF0Mmxpc3QoWUFub21fc3VtX2xldmVsMWFfd2F2ZTAxKQoKaWYgKGZpbGUuZXhpc3RzKCJlbWxpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV8yMDIyLTA1LTI1LnJkYXRhIikpIHsKICBsb2FkKCJlbWxpc3Rfa21fWV9sZXZlbDFhX3dhdmUwMV8yMDIyLTA1LTI1LnJkYXRhIikKfSBlbHNlIHsKICAKICAjIEhlcmUsIHRoZSBsaXN0IGlzIGEgbGlzdCB2ZXJzaW9uIG9mIHRoZSBtYXRyaXggWV8KICBlbWxpc3Rfa21fWUFub21fbGV2ZWwxYV93YXZlMDEgPC0gbWNsYXBwbHkoWCA9IFlBbm9tX3N1bV9sZXZlbDFhX3dhdmUwMV9saXN0LCBGVU4gPSBrbSwgZm9ybXVsYSA9IH4uLCBkZXNpZ24gPSBYX2xldmVsMWFfd2F2ZTAxLCBtYy5jb3JlcyA9IDQpIAogIAogIHNhdmUoIGVtbGlzdF9rbV9ZQW5vbV9sZXZlbDFhX3dhdmUwMSwgZmlsZSA9ICJlbWxpc3Rfa21fWUFub21fbGV2ZWwxYV8yMDIyLTA1LTI1LnJkYXRhIikKICAKfQoKCgpgYGAKCmBgYHtyfQogIApvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV93YXZlMDFfWUFub20gPC0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSwgbmNvbCA9IG5jb2woWF9sZXZlbDFhKSkKCmZvcihpIGluIDE6bGVuZ3RoKHlfbmFtZXNfc3VtKSl7CiAgCiAgeW5hbWUgPC0geV9uYW1lc19zdW1baV0KICB5IDwtIFlfbGV2ZWwxYVssIHluYW1lXQogIG9hdCA8LSBvYWF0U2Vuc3ZhckttTGlzdChYID0gWF9sZXZlbDFhX3dhdmUwMSwgZW1fbGlzdCA9IGVtbGlzdF9rbV9ZQW5vbV9sZXZlbDFhX3dhdmUwMSwgbWVkID0gRkFMU0UsIGhvbGQgPSBYX3N0YW5kYXJkX25vcm0pCiAgb2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfd2F2ZTAxX1lBbm9tW2ksIF0gPC0gb2F0Cn0KCiNzYXZlKHlfbmFtZXNfc3VtLCBvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV9ZLCBmaWxlID0gIm9hYXRfbGV2ZWwxYV9ZLnJkYXRhIikKI30KCnJvd25hbWVzKG9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX3dhdmUwMV9ZQW5vbSkgPC0geV9uYW1lc19zdW0KY29sbmFtZXMob2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfd2F2ZTAxX1lBbm9tKSA8LSBjb2xuYW1lcyhYX2xldmVsMWEpCgojbm9ybXNlbnNfbGV2ZWwxYV9ZIDwtIG5vcm1hbGl6ZSh0KG9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX1kpKQoKCmBgYAoKCgpgYGB7ciwgZmlnLndpZHRoID0gNywgZmlnLmhlaWdodCA9IDh9CgojcGRmKGZpbGUgPSAnZmlncy9vYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV93YXZlMDFfWUFub20ucGRmJywgd2lkdGggPSA3LCBoZWlnaHQgPSA4KQpvYWF0U2Vuc3ZhclN1bW1hcnlQbG90KG9hdF92YXJfc2Vuc21hdF9sZXZlbDFhX3dhdmUwMV9ZQW5vbSkKCiNkZXYub2ZmKCkKCmBgYAoKCiMgRkFTVCBzZW5zaXRpdml0eSBhbmFseXNpcwpXZSB1c2UgYSBGQVNUOTkgYWxnb3JpdGhtIGJ5IFNhbHRlbGxpIGV0IGFsICgyMDAwKSwgZnJvbSB0aGUgUiBwYWNrYWdlICJzZW5zaXRpdml0eSIKCmBgYHtyfQojIE5lZWQgdG8gdGhpbmsgYWJvdXQgaG93IG1pbnMgYW5kIG1heGVzIGFyZSBkZWFsdCB3aXRoIC0gd2UgaGF2ZSBhIHRydW5jYXRlZCBpbnB1dCBkZXNpZ24KCiMgR2VuZXJhdGUgYSBkZXNpZ24gZm9yIHRoZSBGQVNUOTkgYW5hbHlzaXMKWF9mYXN0IDwtIGZhc3Q5OShtb2RlbCA9IE5VTEwsIGZhY3RvcnMgPSBjb2xuYW1lcyhYX2xldmVsMWFfd2F2ZTAxKSwgbiA9IDMwMDAsCiAgICAgICAgICAgICAgICAgcSA9ICJxdW5pZiIsIHEuYXJnID0gbGlzdChtaW4gPSAwLCBtYXggPSAxKSkKYGBgCgoKQ3JlYXRlIGEgbGlzdCBvZiBzZW5zaXRpdml0eSBhbmFseXNlcywgb25lIGZvciBlYWNoIGNvbHVtbiBvZiB0aGUgInN1bSIgKG1vZGVybikgb3V0cHV0IG1hdHJpeC4gCihUaGlzIG5vdyB1c2VzIHdhdmUwMSBkYXRhKQpgYGB7cn0KCk1GX1lfc3VtX2xldmVsMWEgPC0gbXVsdGlGQVNUKFggPSBYX2xldmVsMWFfd2F2ZTAxLCBZID0gWV9zdW1fbGV2ZWwxYV93YXZlMDEsIGZpdF9saXN0ID0gZW1saXN0X2ttX1lfbGV2ZWwxYV93YXZlMDEsIG4gPSAxMDAwKQoKYGBgCgoKQ3JlYXRlIGEgc2Vuc2l0aXZpdHkgc3VtbWFyeSBtYXRyaXggZnJvbSB0aGUgbGlzdCBvZiBzZW5zaXRpdml0eSBhbmFseXNlcy4KYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMCwgZWNobyA9IFRSVUUsIHJlc3VsdHMgPSAnaGlkZSd9CgoKRkFTVF90b3RhbF9ZX3N1bV9sZXZlbDFhIDwtIG1hdHJpeChucm93ID0gbGVuZ3RoKE1GX1lfc3VtX2xldmVsMWEkZmFzdF90ZWxsX2xpc3QpLCBuY29sID0gZCkKCmZvcihpIGluIDE6bGVuZ3RoKE1GX1lfc3VtX2xldmVsMWEkZmFzdF90ZWxsX2xpc3QpKXsKIAogICMgc3VtIHRoZSBkaXJlY3QgZWZmZWN0IGFuZCBpbnRlcmFjdGlvbiB0ZXJtcyB0byBnZXQgYSB0b3RhbAogIEZBU1RfdG90YWxfWV9zdW1fbGV2ZWwxYVtpLCBdIDwtIGFwcGx5KGJwX2NvbnZlcnQoTUZfWV9zdW1fbGV2ZWwxYSRmYXN0X3RlbGxfbGlzdFtbaV1dKSwyLHN1bSkKfQoKY29sbmFtZXMoRkFTVF90b3RhbF9ZX3N1bV9sZXZlbDFhKSA8LSBjb2xuYW1lcyhYX2xldmVsMWEpCnJvd25hbWVzKEZBU1RfdG90YWxfWV9zdW1fbGV2ZWwxYSkgPC0gY29sbmFtZXMoWV9zdW1fbGV2ZWwxYSkKYGBgCgpQbG90IHRoZSBzdW1tYXJ5IG1hdHJpeApgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDEwLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0V9CiNwZGYoZmlsZSA9ICdmaWdzL0ZBU1Rfc2Vuc21hdF9ZX2xldmVsMWFfd2F2ZTAxLnBkZicsIHdpZHRoID0gNywgaGVpZ2h0ID0gOCkKc2Vuc01hdFN1bW1hcnlQbG90KEZBU1RfdG90YWxfWV9zdW1fbGV2ZWwxYSkKI2Rldi5vZmYoKQoKYGBgCgoKTm93IGNyZWF0ZSBhIGxpc3Qgb2Ygc2Vuc2l0aXZpdHkgYW5hbHlzZXMgZm9yIHRoZSBhbm9tYWx5IGF0IHRoZSBlbmQgb2YgdGhlIHJ1bi4KYGBge3J9CgpNRl9ZQW5vbV9zdW1fbGV2ZWwxYSA8LSBtdWx0aUZBU1QoWCA9IFhfbGV2ZWwxYSwgWSA9IFlBbm9tX3N1bV9sZXZlbDFhLCBmaXRfbGlzdCA9IGVtbGlzdF9rbV9ZQW5vbV9sZXZlbDFhKQoKYGBgCgpDcmVhdGUgdGhlIHNlbnNpdGl2aXR5IHN1bW1hcnkgbWF0cml4IGZvciB0aGUgYW5vbWFseQpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQoKRkFTVF90b3RhbF9ZQW5vbV9zdW1fbGV2ZWwxYSA8LSBtYXRyaXgobnJvdyA9IGxlbmd0aChNRl9ZQW5vbV9zdW1fbGV2ZWwxYSRmYXN0X3RlbGxfbGlzdCksIG5jb2wgPSBkKQoKZm9yKGkgaW4gMTpsZW5ndGgoTUZfWUFub21fc3VtX2xldmVsMWEkZmFzdF90ZWxsX2xpc3QpKXsKIAogICMgc3VtIHRoZSBkaXJlY3QgZWZmZWN0IGFuZCBpbnRlcmFjdGlvbiB0ZXJtcyB0byBnZXQgYSB0b3RhbAogIEZBU1RfdG90YWxfWUFub21fc3VtX2xldmVsMWFbaSwgXSA8LSBhcHBseShicF9jb252ZXJ0KE1GX1lBbm9tX3N1bV9sZXZlbDFhJGZhc3RfdGVsbF9saXN0W1tpXV0pLDIsc3VtKQp9Cgpjb2xuYW1lcyhGQVNUX3RvdGFsX1lBbm9tX3N1bV9sZXZlbDFhKSA8LSBjb2xuYW1lcyhYX2xldmVsMWEpCnJvd25hbWVzKEZBU1RfdG90YWxfWUFub21fc3VtX2xldmVsMWEpIDwtIGNvbG5hbWVzKFlBbm9tX3N1bV9sZXZlbDFhKQoKYGBgCgoKUGxvdCB0aGUgc2Vuc2l0aXZpdHkgc3VtbWFyeSBtYXRyaXgKYGBge3IsIGZpZy53aWR0aCA9IDgsIGZpZy5oZWlnaHQgPSAxMH0KCiNwZGYoZmlsZSA9ICdmaWdzL0ZBU1Rfc2Vuc21hdF9ZQW5vbV9sZXZlbDFhX3dhdmUwMS5wZGYnLCB3aWR0aCA9IDcsIGhlaWdodCA9IDgpCnNlbnNNYXRTdW1tYXJ5UGxvdChGQVNUX3RvdGFsX1lBbm9tX3N1bV9sZXZlbDFhKQojZGV2Lm9mZigpCgpgYGAKCgpgYGB7cn0KCmtuaXRfZXhpdCgpCgpgYGAKCiMgTW9udGUgQ2FybG8gRmlsdGVyaW5nCmBgYHtyfQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQojIE1vbnRlIGNhcmxvIGZpbHRlcmluZyBmb3Igc2Vuc2l0aXZpdHkgYW5hbHlzaXMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMgVW5pZm9ybSBzYW1wbGUgZnJvbSBhY3Jvc3MgcGFyYW1ldGVyIHNwYWNlCiMgU3BsaXQgdGhlIHNhbXBsZSBpbnRvICdiZWhhdmlvdXJhbCcgKE5ST1kpIGFuZCAnTm9uIGJlaGF2aW91cmFsIChSdWxlZCBPdXQpCiMgQnVpbGQgY2RmcyBvZiB0aGUgbWFyZ2luYWwgZGlzdHJpYnV0aW9ucyBpbiBlYWNoIGNhc2UKIyBQZXJmb3JtIGEgS1MgdGVzdCB0byBzZWUgaWYgdGhlIHNtYXBsZXMgYXJlIGRyYXduIGZyb20gZGlmZmVyZW50IGRpc3RyaWJ1dGlvbnMKIyBUaGUgS1Mgc3RhdGlzdGljIGlzIGFuIGluZGljYXRvciBvZiB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgcGFyYW1ldGVyIGluIHNwbGl0dGluZyB0aGUKIyBzYW1wbGVzLgoKIyAiTm90IGluIiBmdW5jdGlvbgonJSFpbiUnIDwtIGZ1bmN0aW9uKHgseSkhKCclaW4lJyh4LHkpKQoKbWNmID0gZnVuY3Rpb24oWCwgbnJveV9peCl7CgogICMjIE1vbnRlIENhcmxvIEZpbHRlcmluZyBmdW5jdGlvbgogICMjIFggICAuLi4uLi4uLi4uLi4uLi4gQ29tcGxldGUgc2FtcGxlIGZyb20gaW5wdXQgc3BhY2UKICAjIyBucm95Lml4IC4uLi4uLi4uLi4uIGluZGV4IG9mIGNhc2VzIG9mIFggd2hpY2ggYXJlIE5ST1kgKE5vdCBSdWxlZCBPdXQgWWV0KSwgb3IgJ2JlaGF2aW91cmFsJy4KCiAgIyMgcHJvZHVjZXMga3Mgc3RhdGlzdGljIGZvciBlYWNoIGNvbHVtbiBvZiB0aGUgaW5wdXQgbWF0cml4IFgKICAjIyBBIGxhcmdlciBrcyBzdGF0aXN0aWMgbWVhbnMgdGhhdCBpbnB1dCBpcyBtb3JlIGltcG9ydGFudCBmb3IKICAjIyBkZXRlcm1pbmluZyBpZiBhIHNhbXBsZSBpcyBOUk9ZIG9yIG5vdAoKICBYX25yb3kgPSBYW25yb3lfaXgsIF0KCiAgcmVmID0gMTpucm93KFgpCiAgcm9faXggPSB3aGljaChyZWYgJSFpbiUgbnJveV9peCkKICBYX3JvID0gWFtyb19peCwgXQoKICBrc3MgPSByZXAoTkEsIGxlbmd0aCA9IG5jb2woWCkpCiAgZm9yKGkgaW4gMTpuY29sKFgpKXsKCiAgICBrcyA9IGtzLnRlc3QoWF9yb1ssaV0sIFhfbnJveVssaV0pCiAgICBrc3NbaV0gPSBrcyRzdGF0aXN0aWMKCiAgfQoKICBvdXQgPSBrc3MKICBvdXQKfQpgYGAKClRoaXMgcmVwZWF0cyBzb21lIGNvZGUgZnJvbSB0aGUgY29uc3RyYWludCBhbmFseXNpcyBpbiBvcmRlciB0byBkbyBNQ0YgdXNpbmcgdGhlIG9ic2VydmF0aW9ucyAoY29uc3RyYWludHMpIHdlIGhhdmUuCgpgYGB7cn0KCiMgbmJwICBucHAgIGNzb2lsICBjdmVnCllfbG93ZXIgPC0gYygtMTAsIDM1LCA3NTAsIDMwMCkKWV91cHBlciA8LSBjKDEwLCA4MCwgMzAwMCwgODAwKQoKIyBJJ20gZ29pbmcgdG8gc2V0IGl0IHNvIHRoYXQgKyA0c2QgYWxpZ25zIGFwcHJveGltYXRlbHkgd2l0aCB0aGUgb3JpZ2luYWwgbGltaXRzCiMgZ2l2ZW4gYnkgQW5keSBXaWx0c2hpcmUuIFRoaXMgZ2l2ZXMgcm9vbSBmb3IgdW5jZXJ0YWludHkgZnJvbSB0aGUgZW11bGF0b3IKWV90YXJnZXQgPSBZX3VwcGVyIC0gKGFicyhZX3VwcGVyIC0gKFlfbG93ZXIpKSAvIDIgKSMgYWJzKCkgdG8gZml4IHRoZSBwcm9ibGVtIHdpdGggbmVnYXRpdmUgbnVtYmVycwoKCiMgc3RhbmRhcmQgZGV2aWF0aW9uIGlzIGRlcml2ZWQgZnJvbSB0aGUgbGltaXRzIGFuZCB0aGUgY2VudHJhbCB0YXJnZXQKIyAodGhpcyBkaXN0YW5jZSBpcyBhc3N1bWVkIHRvIGJlIDQgc3RhbmRhcmQgZGV2aWF0aW9ucy4KWV9zZCA9IChZX3VwcGVyIC0gWV90YXJnZXQpIC8gNApuYW1lcyhZX3NkKSA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkKQoKCnAgPSBuY29sKFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkKQoKb2JzX3NkX2xpc3QgPSBhcy5saXN0KHJlcCgwLjAxLHApKQpkaXNjX2xpc3QgPSAgYXMubGlzdChyZXAoMCxwKSkgCmRpc2Nfc2RfbGlzdCA9ICBhcy5saXN0KFlfc2QpCnRocmVzID0gMwoKbWluc19hdWcgPSBhcHBseShYX2xldmVsMWEsIDIsIEZVTiA9IG1pbikKbWF4ZXNfYXVnID1hcHBseShYX2xldmVsMWEsIDIsIEZVTiA9IG1heCkKCiMgY29udmVydCBZX3RhcmdldCBmb3IgaW5nZXN0aW9uIGludG8gZnVuY3Rpb24KWV90YXJnZXQgPSBtYXRyaXgoWV90YXJnZXQsIG5yb3cgPSAxKQoKYGBgCgpgYGB7cn0KIyBGaXJzdCBidWlsZCBhbiBlbXVsYXRvciBsaXN0IGZvciB0aGUgWQoKZW1saXN0X1lfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkIDwtIG1jbGFwcGx5KFggPSBZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZF9saXN0LCBGVU4gPSBrbSwgZm9ybXVsYSA9IH4uLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IFhfbGV2ZWwxYV93YXZlMDEsIG1jLmNvcmVzID0gNCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29udHJvbCA9IGxpc3QodHJhY2UgPSBGQUxTRSkpIAoKYGBgCgoKCmBgYHtyfQojIFNhbXBsZXMgZnJvbSBhIHVuaWZvcm0gZGlzdHJpYnV0aW9uIGFjcm9zcyBhbGwgb2YgaW5wdXQgc3BhY2UKbnNhbXBfdW5pZiA8LSAxMDAwMCAgClhfdW5pZiA8LSBzYW1wX3VuaWYobnNhbXBfdW5pZiwgbWlucyA9IChyZXAoMCwgZCkpLCBtYXhlcyA9IHJlcCgxLGQpKQoKWV91bmlmIDwtIG1hdHJpeChucm93ID0gbnNhbXBfdW5pZiwgbmNvbCA9IG5jb2woWV9jb25zdF9sZXZlbDFhX3dhdmUwMV9zY2FsZWQpKQpjb2xuYW1lcyhZX3VuaWYpIDwtIGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV93YXZlMDFfc2NhbGVkKQoKIyBCdWlsZCBhbiBlbXVsYXRvciBmb3IgZWFjaCBvdXRwdXQgaW5kaXZpZHVhbGx5CmZvcihpIGluIDE6bmNvbChZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZCkpewogIGVtIDwtIGVtbGlzdF9ZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZFtbaV1dCiAgcHJlZCA8LSBwcmVkaWN0KGVtLCBuZXdkYXRhID0gWF91bmlmLCB0eXBlID0gJ1VLJykKICBZX3VuaWZbLGldIDwtIHByZWQkbWVhbgp9CgpgYGAKCmBgYHtyfQojIFRoaXMgdXNlcyBNQ0Ygd2l0aCB0aGUgY29uc3RyYWludHMgc2V0IGJ5IEFXLCByYXRoZXIgdGhhbiB3aXRoIGEgZm9ybWFsIGhpc3RvcnkgbWF0Y2guCgptY2ZfbmJwID0gbWNmKFhfdW5pZiwgd2hpY2goWV91bmlmWywnbmJwX2xuZF9zdW0nXSA+IDApKQptY2ZfbnBwID0gbWNmKFhfdW5pZiwgd2hpY2goWV91bmlmWywnbnBwX25saW1fbG5kX3N1bSddID4gMzUgJiBZX3VuaWZbLCducHBfbmxpbV9sbmRfc3VtJ10gPCA4MCkpCm1jZl9jU29pbCA9IG1jZihYX3VuaWYsIHdoaWNoKFlfdW5pZlssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfdW5pZlssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDApKQptY2ZfY1ZlZyA8LSBtY2YoWF91bmlmLCB3aGljaChZX3VuaWZbLCdjVmVnX2xuZF9zdW0nXSA+IDMwMCAmIFlfdW5pZlssJ2NWZWdfbG5kX3N1bSddIDwgODAwKSkKCm1jZl9hbGxfY29uc3QgPC0gbWNmKFhfdW5pZiwgd2hpY2goWV91bmlmWywnY1ZlZ19sbmRfc3VtJ10gPiAzMDAgJiBZX3VuaWZbLCdjVmVnX2xuZF9zdW0nXSA8IDgwMCAmIFlfdW5pZlssJ2NTb2lsX2xuZF9zdW0nXSA+IDc1MCAmIFlfdW5pZlssJ2NTb2lsX2xuZF9zdW0nXSA8IDMwMDAgJiBZX3VuaWZbLCducHBfbmxpbV9sbmRfc3VtJ10gPiAzNSAmIFlfdW5pZlssJ25wcF9ubGltX2xuZF9zdW0nXSA8IDgwICYgWV91bmlmWywnbmJwX2xuZF9zdW0nXSA+IDApKQoKbWNmX3N1bW1hcnkgPC0gbWF0cml4KHJiaW5kKG1jZl9uYnAsIG1jZl9ucHAsIG1jZl9jU29pbCwgbWNmX2NWZWcsIG1jZl9hbGxfY29uc3QpLCBucm93ID0gbmNvbChZX2NvbnN0X2xldmVsMWFfd2F2ZTAxX3NjYWxlZCkrMSkKY29sbmFtZXMobWNmX3N1bW1hcnkpIDwtIGNvbG5hbWVzKFhfbGV2ZWwxYSkKcm93bmFtZXMobWNmX3N1bW1hcnkpIDwtIGMoJ25icCcsICducHAnLCAnY1NvaWwnLCAnY1ZlZycsICdhbGwnKQoKYGBgCgoKYGBge3IsIGZpZy53aWR0aCA9IDYsIGZpZy5oZWlnaHQgPSA4fQojcGRmKGZpbGUgPSAnZmlncy9NQ0Zfc2Vuc21hdF9ZY29uc3RfbGV2ZWwxYV93YXZlMDEucGRmJywgd2lkdGggPSA2LCBoZWlnaHQgPSA4ICkKc2Vuc01hdFN1bW1hcnlQbG90KG1jZl9zdW1tYXJ5KQojZGV2Lm9mZigpCgpgYGAKCmBgYHtyfQojIHVzaW5nIGFsbCB0b2dldGhlciBpcyBxdWl0ZSBzaW1pbGFyIHRvIHVzaW5nIHRoZSBtZWFuCnBsb3QoMTozMiwgbWNmX2FsbF9jb25zdCwgeWxpbSA9IGMoMCwwLjcpLCBwY2ggPSAxOSkKcG9pbnRzKDE6MzIsIG1jZl9ucHAsIGNvbCA9ICdyZWQnLCBwY2ggPSAxOSkKcG9pbnRzKDE6MzIsIG1jZl9jVmVnLCBjb2wgPSAnZ3JlZW4nLCBwY2ggPSAxOSkKcG9pbnRzKDE6MzIsIG1jZl9jU29pbCwgY29sID0gJ2Jyb3duJywgcGNoID0gMTkpCnBvaW50cygxOjMyLCBtY2ZfbmJwLCBjb2wgPSAnZ29sZCcsIHBjaCA9IDE5KQoKbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0gYygnYWxsJywgJ25wcCcsICdjVmVnJywgJ2NTb2lsJywgJ25icCcpLCAKICAgICAgIGNvbCA9IGMoJ2JsYWNrJywncmVkJywnZ3JlZW4nLCAnYnJvd24nLCAnZ29sZCcgKSwKICAgICAgIHBjaCA9IDE5KSAKCgpgYGAKCgoKYGBge3J9CgpzZW5zcmFua19ZX2xldmVsMWFfbWNmIDwtIFNlbnNSYW5rKG1jZl9zdW1tYXJ5WzE6NCwgXSkKCmBgYAoKIyMjIFJhbmtpbmcgc2Vuc2l0aXZpdHkgb2YgdGhlIHBhcmFtZXRlcnMuCgpUaGUgaWRlYSBoZXJlIGlzIHRvIHN1bW1hcmlzZSB0aGUgcmVsYXRpdmUgaW1wb3J0YW5jZSBvZiB0aGUgaW5wdXQgcGFyYW1ldGVycy4gVGhlIHNlbnNpdGl2aXR5IG1lYXN1cmVzIGFyZSBub3JtYWxpc2VkCmBgYHtyfQoKCgoKc2Vuc3JhbmtfWV9sZXZlbDFhX29hdCA8LSBTZW5zUmFuayhvYXRfdmFyX3NlbnNtYXRfbGV2ZWwxYV93YXZlMDFfWSkKc2Vuc3JhbmtfWUFub21fbGV2ZWwxYV9vYXQgPC0gU2Vuc1Jhbmsob2F0X3Zhcl9zZW5zbWF0X2xldmVsMWFfd2F2ZTAxX1lBbm9tKQoKc2Vuc3JhbmtfRkFTVCA8LSBTZW5zUmFuayhGQVNUX3RvdGFsX1lfc3VtX2xldmVsMWEpCnNlbnNyYW5rX0ZBU1RfWUFub20gPC0gU2Vuc1JhbmsoRkFTVF90b3RhbF9ZQW5vbV9zdW1fbGV2ZWwxYSkKCiAgCnNlbnNfcmFua3MgPC0gY2JpbmQoc2Vuc3JhbmtfWV9sZXZlbDFhX29hdCRyYW5rLHNlbnNyYW5rX0ZBU1QkcmFuaywgIHNlbnNyYW5rX1lBbm9tX2xldmVsMWFfb2F0JHJhbmssIHNlbnNyYW5rX0ZBU1RfWUFub20kcmFuaywgc2Vuc3JhbmtfWV9sZXZlbDFhX21jZiRyYW5rKQpjb2xuYW1lcyhzZW5zX3JhbmtzKSA8LSBjKCdPQVRfbW9kZXJuX3ZhbHVlJywgJ0ZBU1RfbW9kZXJuX3ZhbHVlJywgJ09BVF9hbm9tYWx5JywgJ0ZBU1RfYW5vbWFseScsICdNQ0ZfbW9kZXJuX3ZhbHVlJykKCm1pbl9yYW5rIDwtIGFwcGx5KHNlbnNfcmFua3MsMSwgbWluKQoKYWxsX3JhbmtzIDwtIGNiaW5kKHNlbnNfcmFua3MsIG1pbl9yYW5rKQoKI3Bsb3Qoc2Vuc19yYW5rc1ssMV0sIHNlbnNfcmFua3NbLDJdLCB4bGFiID0gJ21vZGVybiB2YWx1ZSByYW5rJywgeWxhYiA9ICdhbm9tYWx5IHJhbmsnKQoKCnJhbmtfaXggPC0gc29ydChtaW5fcmFuaywgZGVjcmVhc2luZyA9IEZBTFNFLCBpbmRleC5yZXR1cm4gPSBUUlVFKQoKIyBBbGwgcmFua3MgaXMgdGhlIHRhYmxlIG9mIHJhbmtpbmdzLCB3aXRoIG1pbl9yYW5rIGJlaW5nIHRoZSBoaWdoZXN0IHJhbmtpbmcKc2Vuc190YWJsZSA8LSBhbGxfcmFua3NbcmFua19peCRpeCwgXQogIApgYGAKCmBgYHtyfQpzZW5zX3RhYmxlCmBgYAoKYGBge3J9CgpsaWJyYXJ5KHh0YWJsZSkKeHRhYmxlKHNlbnNfdGFibGUsIGRpZ2l0cyA9IDApCmBgYAoKCgpgYGB7cn0Ka25pdF9leGl0KCkKCmBgYAoKY29kZSBmcm9tIGhlcmUgaXMgb25seSBmb3IgcmVmZXJlbmNlICh0byBiZSBhbWVuZGVkKQoKYGBge3J9CgoKIyBGaXJzdCwgdXNlIE1DRiBvbiB0aGUgZGVzaWduCiMKIyBDYWxjdWxhdGUgdGhlIGltcGxhdXNpYmlsaXR5IG9mIGVhY2ggaW5wdXQgcG9pbnQgZm9yCiMgZWFjaCBzZXQgb2Ygb2JzZXJ2YXRpb25zCgpydW4uaW1wbC5hbWF6ID0gaW1wbChlbSA9IFlfdHJvcGljcywKICBlbS5zZCA9IDAsCiAgZGlzYyA9IDAsCiAgZGlzYy5zZCA9IDAsCiAgb2JzID0gb2JzX2FtYXpvbiwKICBvYnMuc2QgPSAwLjA3NSkKCnJ1bi5ucm95Lml4LmFtYXogPSB3aGljaChydW4uaW1wbC5hbWF6IDwgMykKCnJ1bi5pbXBsLnNlYXNpYSA9IGltcGwoZW0gPSBZX3Ryb3BpY3MsCiAgZW0uc2QgPSAwLAogIGRpc2MgPSAwLAogIGRpc2Muc2QgPSAwLAogIG9icyA9IG9ic19zZWFzaWEsCiAgb2JzLnNkID0gMC4wNzUpCgpydW4ubnJveS5peC5zZWFzaWEgPSB3aGljaChydW4uaW1wbC5zZWFzaWEgPCAzKQoKcnVuLmltcGwuY29uZ28gPSBpbXBsKGVtID0gWV90cm9waWNzLAogIGVtLnNkID0gMCwKICBkaXNjID0gMCwKICBkaXNjLnNkID0gMCwKICBvYnMgPSBvYnNfY29uZ28sCiAgb2JzLnNkID0gMC4wNzUpCgpydW4ubnJveS5peC5jb25nbyA9IHdoaWNoKHJ1bi5pbXBsLmNvbmdvIDwgMykKCgptY2YuYW1heiA9IG1jZihYX3Ryb3BpY3MsIHJ1bi5ucm95Lml4LmFtYXopCm1jZi5zZWFzaWEgPSBtY2YoWF90cm9waWNzLCBydW4ubnJveS5peC5zZWFzaWEpCm1jZi5jb25nbyA9IG1jZihYX3Ryb3BpY3MsIHJ1bi5ucm95Lml4LmNvbmdvKQoKZGV2Lm5ldyh3aWR0aCA9IDYsIGhlaWdodCA9IDYpCnBhcihtYXIgPSBjKDgsNCwzLDEpKQpwbG90KDE6bmNvbChYX3Ryb3BpY3MpLCBtY2YuYW1heiwgY29sID0gY29sLmFtYXosIHBjaCA9IDE5LAogICAgIHlsaW0gPSBjKDAsMC40KSwKICAgICB5bGFiID0gJ01DRiBzZW5zaXRpdml0eScsIHhsYWIgPSAnJywKICAgICBheGVzID0gRkFMU0UsCiAgICAgcHR5ID0gJ24nCiAgICAgKQphYmxpbmUodiA9IDE6bmNvbChYX3Ryb3BpY3MpLCBsdHkgPSAnZGFzaGVkJywgY29sID0gJ2xpZ2h0Z3JleScpCnBvaW50cygxOm5jb2woWF90cm9waWNzKSwgbWNmLmFtYXosIGNvbCA9IGNvbC5hbWF6LCBwY2ggPSAxOSkKcG9pbnRzKDE6bmNvbChYX3Ryb3BpY3MpLCBtY2Yuc2Vhc2lhLCBjb2wgPSBjb2wuc2Vhc2lhLCBwY2ggPSAxOSkKcG9pbnRzKDE6bmNvbChYX3Ryb3BpY3MpLCBtY2YuY29uZ28sIGNvbCA9IGNvbC5jb25nbywgcGNoID0gMTkpCgpheGlzKHNpZGUgPSAxLCBsYWJlbHMgPSBjb2xuYW1lcyhYX3Ryb3BpY3MpLCBsYXMgPSAyLCBhdCA9IDE6bmNvbChYX3Ryb3BpY3MpKQpheGlzKHNpZGUgPSAyKQoKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCiMgR2VuZXJhdGUgdW5jZXJ0YWludHkgZXN0aW1hdGVzIG9uIHRoZSBNQ0YgYnkgZW11bGF0aW5nIGFuZAojIGJvb3RzdHJhcHBpbmcgdGhlIHNhbXBsZXMuCiMgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKbWNmLmVtYm9vdCA9IGZ1bmN0aW9uKFgsIGVtZml0LCBib290Y29sID0gYyg4LDkpLAogIGRpc2MsIGRpc2Muc2QsIG9icywgb2JzLnNkLCB0aHJlcyA9IDMsIG4ubWNmID0gMTAwMCwgbi5yZXBzID0gMzAwMCl7CiAgCiAgIyMgRnVuY3Rpb24gdGhhdCBkb2VzIE1vbnRlIENhcmxvIEZpbHRlcmluZyB1c2luZyBhbiBlbXVsYXRlZCBzYW1wbGUuCiAgIyMgSW5wdXRzIGZvciBlbXVsYXRpb24gYXJlIHNhbXBsZWQgZnJvbSB0aGUgdW5pdCBjdWJlIGFwYXJ0IGZyb20KICAjIyB0aG9zZSBpbiBjb2x1bW5zIGJvb3Rjb2wsIHdoaWNoIGFyZSBib290c3RyYXBwZWQgZnJvbSB0aGUgZGVzaWduLgogICMjCgogIGVtLm1jZm1hdCA9IG1hdHJpeChucm93ID0gbi5yZXBzLCBuY29sID0gbmNvbChYKSkKCiAgZm9yKGkgaW4gMTpuLnJlcHMpewoKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgU2FtcGxlIGZyb20gdW5pZm9ybSBkaXN0cmlidXRpb25zIGZvciB0aGUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgc3RhbmRhcmQgaW5wdXQgcGFyYW1ldGVycwogICAgWC5tY2YgPSBzYW1wLnVuaWYobiA9IG4ubWNmLCBtaW5zID0gcmVwKDAsIG5jb2woWCkpLCBtYXhlcyA9IHJlcCgxLCBuY29sKFgpKSkKICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBTYW1wbGUgZnJvbSB0aGUgbW9kZWwgcnVuIGlucHV0cyBmb3IgdGhlIHRlbXAgYW5kIHByZWNpcAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAoaGVyZSBpbmRpY2F0ZWQgYnkgYm9vdGNvbCBjb2x1bW5zKQogICAgWC50cC5peCA9IHNhbXBsZSgxOm5yb3coWCksIHNpemUgPSBuLm1jZiwgcmVwbGFjZSA9IFRSVUUpCiAgICBYLnRwLnJ1bnNhbXAgPSBYW1gudHAuaXggLCBib290Y29sXQogICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGJpbmQgdGhlIHNhbXBsZXMgdG9nZXRoZXIKICAgIFgubWNmWywgYm9vdGNvbF0gPSBYLnRwLnJ1bnNhbXAKICAgIGNvbG5hbWVzKFgubWNmKSA8LSBjb2xuYW1lcyhYKQoKICAgICMgUHJlZGljdCBtb2RlbCBvdXRwdXQgYXQgdGhlIHNhbXBsZWQgaW5wdXRzCiAgICBwcmVkLm1jZiA9IHByZWRpY3QoZW1maXQsIG5ld2RhdGEgPSBYLm1jZiwgdHlwZSA9ICdVSycpCgogICAgIyBmaW5kIHRoZSBpbXBsYXVzaWJpbGl0eSBvZiB0aGUgcHJlZGljdGVkIGlucHV0cwogICAgZW0uaW1wbC5tY2YgPSBpbXBsKGVtID0gcHJlZC5tY2YkbWVhbiwKICAgIGVtLnNkID0gcHJlZC5tY2Ykc2QsCiAgICAgIGRpc2MgPSBkaXNjLAogICAgICBkaXNjLnNkID0gZGlzYy5zZCwKICAgICAgb2JzID0gb2JzLAogICAgICBvYnMuc2QgPSBvYnMuc2QpCgogICAgIyBXaGljaCBwYXJ0IG9mIHRoZSBzYW1wbGUgaXMgTlJPWSAob3IgImJlaGF2aW91cmFsIikKICAgIGVtLm5yb3kuaXggPSB3aGljaChlbS5pbXBsLm1jZiA8IHRocmVzKQogICAgCiAgICBlbS5tY2Y9IG1jZihYLm1jZiwgZW0ubnJveS5peCkKICAgIGVtLm1jZm1hdFtpLCBdID0gZW0ubWNmCiAgICAKICB9CiAgCiAgbWNmLm1lYW4gPSBhcHBseShlbS5tY2ZtYXQsIDIsIG1lYW4pCiAgbWNmLnNkID0gYXBwbHkoZW0ubWNmbWF0LCAyLCBzZCkKCgogIHJldHVybihsaXN0KG1lYW4gPSBtY2YubWVhbiwgc2QgPSBtY2Yuc2QpKQp9CgoKIyBIb3cgYmlnIG1pZ2h0IHRoZSB1bmNlcnRhaW50eSBib3VuZHMgYmUgaWYgd2UgdXNlIGp1c3QgMzAwIHBvaW50cwojIChhcyBpbiB0aGUgZW5zZW1ibGUpIHRvIGVzdGltYXRlIHRoZSBNQ0Ygc2Vuc2l0aXZpdHkgYW5hbHlzaXMgaW5kaWNlcz8KCm4ubWNmLnNlcSA9IGMoc2VxKGZyb20gPSAxMDAsIHRvID0gMTAwMCwgYnkgPSAxMDApLCAxNTAwLCAyMDAwLCAzMDAwKQptY2Yuc2VxLm1lYW4gPSBtYXRyaXgoTkEsIG5yb3cgPSBsZW5ndGgobi5tY2Yuc2VxKSwgbmNvbCA9IG5jb2woWF90cm9waWNzX25vcm0pKQptY2Yuc2VxLnNkID0gbWF0cml4KE5BLCBucm93ID0gbGVuZ3RoKG4ubWNmLnNlcSksIG5jb2wgPSBuY29sKFhfdHJvcGljc19ub3JtKSkKCmZvcihpIGluIDE6bGVuZ3RoKG4ubWNmLnNlcSkpewoKbWNmLmVtLmFtYXouc2VxID0gbWNmLmVtYm9vdChYID0gWF90cm9waWNzX25vcm0sIGVtID0gdHJvcGljc19maXQsCiAgYm9vdGNvbCA9IGMoOCw5KSwgZGlzYyA9IDAsIGRpc2Muc2QgPSAwLCBvYnMgPSBvYnNfYW1hem9uLCBvYnMuc2QgPSAwLjA1LAogIHRocmVzID0gMywgbi5tY2YgPSBuLm1jZi5zZXFbaV0sIG4ucmVwcyA9IDEwMDApCgptY2Yuc2VxLm1lYW5baSwgXSA9IG1jZi5lbS5hbWF6LnNlcSRtZWFuCm1jZi5zZXEuc2RbaSwgXSA9IG1jZi5lbS5hbWF6LnNlcSRzZAoKfQoKIyBXaGF0IGlzIG91ciBlc3RpbWF0ZSBvZiBNQ0YgdW5jZXJ0YWludHkgd2hlbiB3ZSBoYXZlIDMwMCBlbnNlbWJsZSBtZW1iZXJzPwptY2YuZW0uYW1hei4zMDAgPSBtY2YuZW1ib290KFggPSBYX3Ryb3BpY3Nfbm9ybSwgZW0gPSB0cm9waWNzX2ZpdCwKICBib290Y29sID0gYyg4LDkpLCBkaXNjID0gMCwgZGlzYy5zZCA9IDAsIG9icyA9IG9ic19hbWF6b24sIG9icy5zZCA9IDAuMDUsCiAgdGhyZXMgPSAzLCBuLm1jZiA9IDMwMCwgbi5yZXBzID0gMTAwMCkKCm1jZi5lbS5zZWFzaWEuMzAwID0gbWNmLmVtYm9vdChYID0gWF90cm9waWNzX25vcm0sIGVtID0gdHJvcGljc19maXQsCiAgYm9vdGNvbCA9IGMoOCw5KSwgZGlzYyA9IDAsIGRpc2Muc2QgPSAwLCBvYnMgPSBvYnNfc2Vhc2lhLCBvYnMuc2QgPSAwLjA1LAogIHRocmVzID0gMywgbi5tY2YgPSAzMDAsIG4ucmVwcyA9IDEwMDApCgptY2YuZW0uY29uZ28uMzAwID0gbWNmLmVtYm9vdChYID0gWF90cm9waWNzX25vcm0sIGVtID0gdHJvcGljc19maXQsCiAgYm9vdGNvbCA9IGMoOCw5KSwgZGlzYyA9IDAsIGRpc2Muc2QgPSAwLCBvYnMgPSBvYnNfY29uZ28sIG9icy5zZCA9IDAuMDUsCiAgdGhyZXMgPSAzLCBuLm1jZiA9IDMwMCwgbi5yZXBzID0gMTAwMCkKCgoKY2JQYWwgPC0gYygiIzAwMDAwMCIsICIjRTY5RjAwIiwgIiM1NkI0RTkiLCAiIzAwOUU3MyIsICIjRjBFNDQyIiwgIiMwMDcyQjIiLCAiI0Q1NUUwMCIsICIjQ0M3OUE3IikKCgojIFRoZSBNZWFuIGFuZCB0aGUgU3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBlc3RpbWF0ZQojIG9mIE1DRiBzZW5zaXRpdml0eSBib3RoIGRyb3AgYXMgdGhlIG51bWJlciBvZiBNQ0Ygc2FtcGxlcyBpbmNyZWFzZXMuCgojZGV2Lm5ldyh3aWR0aCA9IDEwLCBoZWlnaHQgPSA4KQpwZGYod2lkdGggPSAxMCwgaGVpZ2h0ID0gOCwgZmlsZSA9ICdncmFwaGljcy9tY2ZfbWVhbl9zZF92c19uLnBkZicpCnBhcihtZnJvdyA9IGMoMSwyKSwgbGFzID0gMSkKCm1hdHBsb3Qobi5tY2Yuc2VxLCBtY2Yuc2VxLm1lYW4sIG1haW4gPSAnTWVhbicsIHhsYWIgPSAnRW11bGF0ZWQgRW5zZW1ibGUgbWVtYmVycycsIHlsYWIgPSAnS1Mgc3RhdGlzdGljIE1lYW4nLCB0eXBlID0gJ28nLCBjb2wgPSBjYlBhbCkKCm1hdHBsb3Qobi5tY2Yuc2VxLCBtY2Yuc2VxLnNkLCBtYWluID0gJ1N0YW5kYXJkIGRldmlhdGlvbicsIHhsYWIgPSAnRW11bGF0ZWQgRW5zZW1ibGUgbWVtYmVycycsIHlsYWIgPSAnS1Mgc3RhdGlzdGljIHN0YW5kYXJkIGRldmlhdGlvbicsIHR5cGUgPSAnbycsIGNvbCA9IGNiUGFsKQoKbGVnZW5kKCd0b3ByaWdodCcsIHBjaCA9IGFzLmNoYXJhY3RlcigxOjkpLCBsZWdlbmQgPSBjb2xuYW1lcyhYX3Ryb3BpY3Nfbm9ybSksIGNvbCA9IGNiUGFsLCB0ZXh0LmNvbCA9IGNiUGFsKQpkZXYub2ZmKCkKCgojIFRoaXMgcHV0cyB0aGUgbWVhbiBhbmQgZXN0aW1hdGUgTUNGIHNlbnNpdGl2aXR5IGluZGljZXMgaW4gY29udGV4dCB3aXRoIHRoZWlyCiMgZXN0aW1hdGVkIHVuY2VydGFpbnR5LgpkZXYubmV3KHdpZHRoID0gNiwgaGVpZ2h0ID0gMTApCm1hdHBsb3Qobi5tY2Yuc2VxLCBtY2Yuc2VxLm1lYW4sIG1haW4gPSAnTWVhbicsIHhsYWIgPSAnRW5zZW1ibGUgbWVtYmVycycsIHlsYWIgPSAnTUNGIFNlbnNpdGl2aXR5IEluZGV4JywgdHlwZSA9ICdvJywgY29sID0gcmVwKGNiUGFsLDIpLCB5bGltID0gYygwLDAuMzUpLCBwY2ggPSAxOSwgbHdkID0gMS4yLCBsdHkgPSAnc29saWQnKQoKZm9yKGkgaW4gMTogbmNvbChtY2Yuc2VxLm1lYW4pKXsKICAKYXJyb3dzKHgwID0gbi5tY2Yuc2VxLCB5MCA9IG1jZi5zZXEubWVhblssaV0gLSAobWNmLnNlcS5zZFssaV0pLAogICAgICAgICB4MSA9IG4ubWNmLnNlcSwgeTEgPSBtY2Yuc2VxLm1lYW5bLGldICsgKG1jZi5zZXEuc2RbLCBpXSksCiAgICAgICAgIGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zLCBjb2wgPSByZXAoY2JQYWwsMilbaV0sbHdkID0gMS4yCiAgICAgICAgICkKfQoKCiMgQ2FsY3VsYXRlIE1DRiBpbmRpY2VzIHdpdGggNTAwMCBlbXVsYXRlZCBlbnNlbWJsZSBtZW1iZXJzLgojIEJvb3RzdHJhcCB1bmNlcnRhaW50eSBlc3RpbWF0ZXMuCm1jZi5lbS5hbWF6ID0gbWNmLmVtYm9vdChYID0gWF90cm9waWNzX25vcm0sIGVtID0gdHJvcGljc19maXQsCiAgYm9vdGNvbCA9IGMoOCw5KSwgZGlzYyA9IDAsIGRpc2Muc2QgPSAwLCBvYnMgPSBvYnNfYW1hem9uLCBvYnMuc2QgPSAwLjA1LAogIHRocmVzID0gMywgbi5tY2YgPSA1MDAwLCBuLnJlcHMgPSAxMDAwKQoKbWNmLmVtLnNlYXNpYSA9IG1jZi5lbWJvb3QoWCA9IFhfdHJvcGljc19ub3JtLCBlbSA9IHRyb3BpY3NfZml0LAogIGJvb3Rjb2wgPSBjKDgsOSksIGRpc2MgPSAwLCBkaXNjLnNkID0gMCwgb2JzID0gb2JzX3NlYXNpYSwgb2JzLnNkID0gMC4wNSwKICB0aHJlcyA9IDMsIG4ubWNmID0gNTAwMCwgbi5yZXBzID0gMTAwMCkKCm1jZi5lbS5jb25nbyA9IG1jZi5lbWJvb3QoWCA9IFhfdHJvcGljc19ub3JtLCBlbSA9IHRyb3BpY3NfZml0LAogIGJvb3Rjb2wgPSBjKDgsOSksIGRpc2MgPSAwLCBkaXNjLnNkID0gMCwgb2JzID0gb2JzX2NvbmdvLCBvYnMuc2QgPSAwLjA1LAogIHRocmVzID0gMywgbi5tY2YgPSA1MDAwLCBuLnJlcHMgPSAxMDAwKQoKCiNkZXYubmV3KHdpZHRoID0gOSwgaGVpZ2h0ID0gNikKcGRmKGZpbGUgPSAnZ3JhcGhpY3MvbWNmLnBkZicsIHdpZHRoID0gOSwgaGVpZ2h0ID0gNikKcGFyKGxhcyA9IDEsIG1hciA9IGMoOCw0LDMsMSkpCgp5bGltID0gYygwLDAuMjcpCnhsaW0gPSBjKDAuNSw5LjUpCgpwbG90KCgxOmxlbmd0aChtY2YuZW0uYW1heiRtZWFuKSktMC4xNSwgbWNmLmVtLmFtYXokbWVhbiwKICAgICBwY2ggPSAxOSwgY29sID0gY29sLmFtYXosIHlsaW0gPSB5bGltLCB4bGltID0geGxpbSwKICAgICBwdHkgPSAnbicsIHhheHMgPSAnaScsIHlheHMgPSAnaScsCiAgICAgeGxhYiA9ICcnLCB5bGFiID0gJ0tTIHN0YXRpc3RpYycsCiAgICAgYXhlcyA9IEZBTFNFKQoKaSA9IHNlcShmcm9tID0gMSwgdG8gPSAxMCwgYnkgPSAyKQpyZWN0KGktMC41LCB5bGltWzFdLCBpKzAuNSwgeWxpbVsyXSwgY29sID0gImdyZXk5MiIsIGJvcmRlcj1OQSkKCnBvaW50cygoMTpsZW5ndGgobWNmLmVtLmFtYXokbWVhbikpLTAuMTUsIG1jZi5lbS5hbWF6JG1lYW4sIHBjaCA9IDE5LCBjb2wgPSBjb2wuYW1heikKCmFycm93cyh4MCA9ICgxOmxlbmd0aChtY2YuZW0uYW1heiRtZWFuKSktMC4xNSwgeTAgPSBtY2YuZW0uYW1heiRtZWFuIC0gKDIqbWNmLmVtLmFtYXokc2QgKSwKICAgICAgICAgeDEgPSAoMTpsZW5ndGgobWNmLmVtLmFtYXokbWVhbikpLTAuMTUsIHkxID0gbWNmLmVtLmFtYXokbWVhbiArICgyKm1jZi5lbS5hbWF6JHNkKSwKICAgICAgICAgY29sID0gY29sLmFtYXosIGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKcG9pbnRzKCgxOmxlbmd0aChtY2YuZW0uc2Vhc2lhJG1lYW4pKSwgbWNmLmVtLnNlYXNpYSRtZWFuLCBwY2ggPSAxOSwgY29sID0gY29sLnNlYXNpYSkKCmFycm93cyh4MCA9IDE6bGVuZ3RoKG1jZi5lbS5zZWFzaWEkbWVhbiksIHkwID0gbWNmLmVtLnNlYXNpYSRtZWFuIC0gKDIqbWNmLmVtLnNlYXNpYSRzZCApLAogICAgICAgICB4MSA9IDE6bGVuZ3RoKG1jZi5lbS5zZWFzaWEkbWVhbiksIHkxID0gbWNmLmVtLnNlYXNpYSRtZWFuICsgKDIqbWNmLmVtLnNlYXNpYSRzZCksCiAgICAgICAgIGNvbCA9IGNvbC5zZWFzaWEsIGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKcG9pbnRzKCgxOmxlbmd0aChtY2YuZW0uY29uZ28kbWVhbikpKzAuMTUsIG1jZi5lbS5jb25nbyRtZWFuLCBwY2ggPSAxOSwgY29sID0gY29sLmNvbmdvKQoKYXJyb3dzKHgwID0gKDE6bGVuZ3RoKG1jZi5lbS5jb25nbyRtZWFuKSkrMC4xNSwgeTAgPSBtY2YuZW0uY29uZ28kbWVhbiAtICgyKm1jZi5lbS5jb25nbyRzZCApLAogICAgICAgICB4MSA9ICgxOmxlbmd0aChtY2YuZW0uY29uZ28kbWVhbikpKzAuMTUsIHkxID0gbWNmLmVtLmNvbmdvJG1lYW4gKyAoMiptY2YuZW0uY29uZ28kc2QpLAogICAgICAgICBjb2wgPSBjb2wuY29uZ28sbGVuZ3RoPTAuMDUsIGFuZ2xlPTkwLCBjb2RlPTMpCgoKYXhpcygxLCBsYWJlbHMgPSBjb2xuYW1lcyhYX3Ryb3BpY3Nfbm9ybSksIGF0ID0gMTo5LCBsYXMgPSAyKQpheGlzKDIpCgpsZWdlbmQoJ3RvcGxlZnQnLGxlZ2VuZCA9IGMoJ0FtYXpvbicsJ1NFIEFzaWEnLCAnQyBBZnJpY2EnKSwKICAgICAgIGNvbCA9IGMoY29sLmFtYXosIGNvbC5zZWFzaWEsIGNvbC5jb25nbyksIHBjaCA9IDE5LCBidHkgPSAnbicpCnRleHQoMC41LCAwLjIwLCAnRXJyb3IgYmFycyBpbmRpY2F0ZSBcbiBcdTAwQjEgMiBzdGFuZGFyZCBkZXZpYXRpb25zJywKICAgICBwb3MgID0gNCwgY29sID0gJ2JsYWNrJyxjZXggPSAwLjggKQoKZGV2Lm9mZigpCgoKIyBQbG90IGJvdGggdGhlIHJ1bi1nZW5lcmF0ZWQgYW5kIGVtdWxhdGVkIE1DRiBzZW5zaXRpdml0eQojZGV2Lm5ldyh3aWR0aCA9IDgsIGhlaWdodCA9IDYpCnBkZihmaWxlID0gJ2dyYXBoaWNzL21jZl8zMDBfNTAwMC5wZGYnLCB3aWR0aCA9IDgsIGhlaWdodCA9IDYpCnBhcihsYXMgPSAxLCBtYXIgPSBjKDgsNCw0LDIpKQp5bGltID0gYygwLDAuNDMpCnBsb3QoKDE6bGVuZ3RoKG1jZi5lbS5hbWF6JG1lYW4pKS0wLjIsIG1jZi5lbS5hbWF6JG1lYW4sCiAgICAgcGNoID0gMTksIGNvbCA9IGNvbC5hbWF6LCB5bGltID0geWxpbSwgeGxpbSA9IGMoMC41LDkuNSksCiAgICAgcHR5ID0gJ24nLCB4YXhzID0gJ2knLCB5YXhzID0gJ2knLAogICAgIHhsYWIgPSAnJywgeWxhYiA9ICdLUyBzdGF0aXN0aWMnLAogICAgIGF4ZXMgPSBGQUxTRSkKCmkgPSBzZXEoZnJvbSA9IDEsIHRvID0gMTAsIGJ5ID0gMikKcmVjdChpLTAuNSwgeWxpbVsxXSwgaSswLjUsIHlsaW1bMl0sIGNvbCA9ICJsaWdodGdyZXkiLCBib3JkZXI9TkEpCgpwb2ludHMoKDE6bGVuZ3RoKG1jZi5lbS5hbWF6JG1lYW4pKS0wLjIsIG1jZi5lbS5hbWF6JG1lYW4sIHBjaCA9IDE5LCBjb2wgPSBjb2wuYW1heikKCmFycm93cyh4MCA9ICgxOmxlbmd0aChtY2YuZW0uYW1heiRtZWFuKSkgLSAwLjIsIHkwID0gbWNmLmVtLmFtYXokbWVhbiAtICgyKm1jZi5lbS5hbWF6JHNkICksCiAgICAgICAgIHgxID0gKDE6bGVuZ3RoKG1jZi5lbS5hbWF6JG1lYW4pKSAtIDAuMiwgeTEgPSBtY2YuZW0uYW1heiRtZWFuICsgKDIqbWNmLmVtLmFtYXokc2QpLAogICAgICAgICBjb2wgPSBjb2wuYW1heiwgbGVuZ3RoPTAuMDUsIGFuZ2xlPTkwLCBjb2RlPTMpCgpwb2ludHMoKDE6bGVuZ3RoKG1jZi5lbS5hbWF6JG1lYW4pKS0wLjIsIG1jZi5hbWF6LCBwY2ggPSAyMSwgY29sID0gY29sLmFtYXopCgphcnJvd3MoeDAgPSAxOmxlbmd0aChtY2YuZW0uYW1heiRtZWFuKS0wLjIsIHkwID0gbWNmLmFtYXogLSAoMiptY2YuZW0uYW1hei4zMDAkc2QgKSwKICAgICAgIHgxID0gMTpsZW5ndGgobWNmLmVtLmFtYXokbWVhbiktMC4yLCB5MSA9IG1jZi5hbWF6ICsgKDIqbWNmLmVtLmFtYXouMzAwJHNkKSwKICAgICAgIGx0eSA9ICdkb3R0ZWQnLAogICAgICAgY29sID0gY29sLmFtYXosIGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKCnBvaW50cygxOmxlbmd0aChtY2YuZW0uc2Vhc2lhJG1lYW4pLCBtY2YuZW0uc2Vhc2lhJG1lYW4sIHBjaCA9IDE5LCBjb2wgPSBjb2wuc2Vhc2lhKQoKYXJyb3dzKHgwID0gMTpsZW5ndGgobWNmLmVtLnNlYXNpYSRtZWFuKSwgeTAgPSBtY2YuZW0uc2Vhc2lhJG1lYW4gLSAoMiptY2YuZW0uc2Vhc2lhJHNkICksCiAgICAgICAgIHgxID0gMTpsZW5ndGgobWNmLmVtLnNlYXNpYSRtZWFuKSwgeTEgPSBtY2YuZW0uc2Vhc2lhJG1lYW4gKyAoMiptY2YuZW0uc2Vhc2lhJHNkKSwKICAgICAgICAgY29sID0gY29sLnNlYXNpYSwgbGVuZ3RoPTAuMDUsIGFuZ2xlPTkwLCBjb2RlPTMpCgoKcG9pbnRzKCgxOmxlbmd0aChtY2YuZW0uYW1heiRtZWFuKSksIG1jZi5zZWFzaWEsIHBjaCA9IDIxLCBjb2wgPSBjb2wuc2Vhc2lhKQoKYXJyb3dzKHgwID0gMTpsZW5ndGgobWNmLmVtLmFtYXokbWVhbiksIHkwID0gbWNmLnNlYXNpYSAtICgyKm1jZi5lbS5zZWFzaWEuMzAwJHNkICksCiAgICAgICB4MSA9IDE6bGVuZ3RoKG1jZi5lbS5hbWF6JG1lYW4pLCB5MSA9IG1jZi5zZWFzaWEgKyAoMiptY2YuZW0uc2Vhc2lhLjMwMCRzZCksCiAgICAgICBsdHkgPSAnZG90dGVkJywKICAgICAgIGNvbCA9IGNvbC5zZWFzaWEsIGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKCnBvaW50cygoMTpsZW5ndGgobWNmLmVtLmNvbmdvJG1lYW4pKSswLjIsIG1jZi5lbS5jb25nbyRtZWFuLCBwY2ggPSAxOSwgY29sID0gY29sLmNvbmdvKQoKYXJyb3dzKHgwID0gKDE6bGVuZ3RoKG1jZi5lbS5jb25nbyRtZWFuKSkrMC4yLCB5MCA9IG1jZi5lbS5jb25nbyRtZWFuIC0gKDIqbWNmLmVtLmNvbmdvJHNkICksCiAgICAgICAgIHgxID0gKDE6bGVuZ3RoKG1jZi5lbS5jb25nbyRtZWFuKSkrMC4yLCB5MSA9IG1jZi5lbS5jb25nbyRtZWFuICsgKDIqbWNmLmVtLmNvbmdvJHNkKSwKICAgICAgICAgY29sID0gY29sLmNvbmdvLGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKcG9pbnRzKCgxOmxlbmd0aChtY2YuZW0uY29uZ28kbWVhbikpKzAuMiwgbWNmLmNvbmdvLCBwY2ggPSAyMSwgY29sID0gY29sLmNvbmdvKQoKYXJyb3dzKHgwID0gMTpsZW5ndGgobWNmLmVtLmFtYXokbWVhbikrMC4yLCB5MCA9IG1jZi5jb25nbyAtICgyKm1jZi5lbS5jb25nby4zMDAkc2QgKSwKICAgICAgIHgxID0gMTpsZW5ndGgobWNmLmVtLmFtYXokbWVhbikgKzAuMiwgeTEgPSBtY2YuY29uZ28gKyAoMiptY2YuZW0uY29uZ28uMzAwJHNkKSwKICAgICAgIGx0eSA9ICdkb3R0ZWQnLAogICAgICAgY29sID0gY29sLmNvbmdvLCBsZW5ndGg9MC4wNSwgYW5nbGU9OTAsIGNvZGU9MykKCmF4aXMoMSwgbGFiZWxzID0gY29sbmFtZXMoWF90cm9waWNzX25vcm0pLCBhdCA9IDE6OSwgbGFzID0gMikKYXhpcygyKQoKbGVnZW5kKCd0b3BsZWZ0JyxsZWdlbmQgPSBjKCdBbWF6b24nLCdTRSBBc2lhJywgJ0MgQWZyaWNhJyksCiAgICAgICBjb2wgPSBjKGNvbC5hbWF6LCBjb2wuc2Vhc2lhLCBjb2wuY29uZ28pLCBwY2ggPSAxOSwgYnR5ID0gJ24nKQp0ZXh0KDAuNSwgMC4zMiwgJ0Vycm9yIGJhcnMgaW5kaWNhdGUgXG4gXHUwMEIxIDIgc3RhbmRhcmQgZGV2aWF0aW9ucycsCiAgICAgcG9zICA9IDQsIGNvbCA9ICdibGFjaycsY2V4ID0gMC44ICkKdGV4dCgwLjUsIDAuMjksICdPcGVuIHBvaW50cyAmIGRvdHRlZCBsaW5lcyBpbmRpY2F0ZSBlbnNlbWJsZS1vbmx5IHJlc3VsdHMnLAogICAgIHBvcyAgPSA0LCBjb2wgPSAnYmxhY2snLGNleCA9IDAuOCApCmRldi5vZmYoKQoKCiMgSG93IGRvZXMgdGhpcyBzZW5zaXRpdml0eSBhbmFseXNpcyBtZWFzdXJlIHVwIHRvIHRoZSBGQVNUOTkgdmVyc2lvbj8KcGRmKHdpZHRoID0gMTIsIGhlaWdodCA9IDcsIGZpbGUgPSAnZ3JhcGhpY3MvZmFzdDk5X3ZzX21jZjIucGRmJykKI2Rldi5uZXcod2lkdGggPSAxMiwgaGVpZ2h0ID0gNykKcGFyKG1mcm93ID0gYygxLDIpLCBtYXIgPSBjKDUsNSwzLDIpLCBsYXMgPSAxKQoKcGxvdChwcmludChmYXN0LnRlbGwpWywxXSwgbWNmLmFtYXosIGNvbCA9IGNvbC5hbWF6LCBwY2ggPSBhcy5jaGFyYWN0ZXIoMTo5KSwKICAgICB5bGltID0gYygwLDAuNDIpLCB4bGltID0gYygwLDAuMzIpLAogICAgIHhsYWIgPSAnRkFTVDk5IGZpcnN0LW9yZGVyIHNlbnNpdGl2aXR5JywKICAgICB5bGFiID0gJ01DRiBzZW5zaXRpdml0eSAoS1Mgc3RhdGlzdGljKScsCiAgICAgbWFpbiA9ICdNQ0YgdXNpbmcgMzAwIGVuc2VtYmxlIG1lbWJlcnMnLAogICAgIHB0eSA9ICduJwogICAgICkKCmFycm93cyh4MCA9IHByaW50KGZhc3QudGVsbClbLDFdLCB5MCA9ICBtY2YuYW1heiAtICgyKm1jZi5lbS5hbWF6LjMwMCRzZCksCiAgICAgICAgIHgxID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkxID0gIG1jZi5hbWF6ICsgKDIqbWNmLmVtLmFtYXouMzAwJHNkKSwKICAgICAgICAgY29sID0gY29sLmFtYXosIGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zLAogICAgICAgbHR5ID0gJ3NvbGlkJywgbHdkID0gMC44KQoKYXJyb3dzKHgwID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkwID0gIG1jZi5zZWFzaWEgLSAoMiptY2YuZW0uc2Vhc2lhLjMwMCRzZCksCiAgICAgICAgIHgxID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkxID0gIG1jZi5zZWFzaWEgKyAoMiptY2YuZW0uc2Vhc2lhLjMwMCRzZCksCiAgICAgICAgIGNvbCA9IGNvbC5zZWFzaWEsIGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zLAogICAgICAgbHR5ID0gJ3NvbGlkJywgbHdkID0gMC44KQoKYXJyb3dzKHgwID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkwID0gIG1jZi5jb25nbyAtICgyKm1jZi5lbS5jb25nby4zMDAkc2QpLAogICAgICAgICB4MSA9IHByaW50KGZhc3QudGVsbClbLDFdLCB5MSA9ICBtY2YuY29uZ28gKyAoMiptY2YuZW0uY29uZ28uMzAwJHNkKSwKICAgICAgICAgY29sID0gY29sLmNvbmdvLCBsZW5ndGg9MC4wNSwgYW5nbGU9OTAsIGNvZGU9MywKICAgICAgIGx0eSA9ICdzb2xpZCcsIGx3ZCA9IDAuOCkKCgpwb2ludHMocHJpbnQoZmFzdC50ZWxsKVssMV0sIG1jZi5hbWF6LCBjb2wgPSBjb2wuYW1heiwgcGNoID0gYXMuY2hhcmFjdGVyKDE6OSksIGZvbnQgPSAyKQpwb2ludHMocHJpbnQoZmFzdC50ZWxsKVssMV0sIG1jZi5zZWFzaWEsIGNvbCA9IGNvbC5zZWFzaWEsIHBjaCA9IGFzLmNoYXJhY3RlcigxOjkpLCBmb250ID0gMikKcG9pbnRzKHByaW50KGZhc3QudGVsbClbLDFdLCBtY2YuY29uZ28sIGNvbCA9IGNvbC5jb25nbywgcGNoID0gYXMuY2hhcmFjdGVyKDE6OSksIGZvbnQgPSAyKQoKbGVnZW5kKCd0b3BsZWZ0JywgcGNoID0gYXMuY2hhcmFjdGVyKDE6OSksIGxlZ2VuZCA9IGNvbG5hbWVzKFhfdHJvcGljc19ub3JtKSwgY2V4ID0gMC44LCBidHkgPSAnbicpCgpsZWdlbmQoJ3RvcCcsIGx0eSA9ICdzb2xpZCcsIGxlZ2VuZCA9IGMoJ0FtYXpvbicsICdTRSBBc2lhJywgJ0MgQWZyaWNhJyksCiAgICAgICB0ZXh0LmNvbCA9IGMoY29sLmFtYXosY29sLnNlYXNpYSwgY29sLmNvbmdvKSwKICAgICAgIGNvbCA9IGMoY29sLmFtYXosY29sLnNlYXNpYSwgY29sLmNvbmdvKQogICAgICAgLCBjZXggPSAwLjgsIGJ0eSA9ICduJykKCmFibGluZSgwLDEsIGx0eSA9ICdkYXNoZWQnKQoKCnBsb3QocHJpbnQoZmFzdC50ZWxsKVssMV0sIG1jZi5lbS5hbWF6JG1lYW4sIGNvbCA9IGNvbC5hbWF6LCBwY2ggPSBhcy5jaGFyYWN0ZXIoMTo5KSwKICAgICB5bGltID0gYygwLDAuNDIpLCB4bGltID0gYygwLDAuMzIpLAogICAgIHhsYWIgPSAnRkFTVDk5IGZpcnN0LW9yZGVyIHNlbnNpdGl2aXR5JywKICAgICB5bGFiID0gJ01DRiBzZW5zaXRpdml0eSAoS1Mgc3RhdGlzdGljKScsCiAgICAgcHR5ID0gJ24nLAogICAgIG1haW4gPSAnTUNGIHVzaW5nIDUwMDAgZW11bGF0ZWQgZW5zZW1ibGUgbWVtYmVycycKICAgICApCgphcnJvd3MoeDAgPSBwcmludChmYXN0LnRlbGwpWywxXSwgeTAgPSAgbWNmLmVtLmFtYXokbWVhbiAtICgyKm1jZi5lbS5hbWF6JHNkKSwKICAgICAgICAgeDEgPSBwcmludChmYXN0LnRlbGwpWywxXSwgeTEgPSAgbWNmLmVtLmFtYXokbWVhbiArICgyKm1jZi5lbS5hbWF6JHNkKSwKICAgICAgICAgY29sID0gY29sLmFtYXosbGVuZ3RoPTAuMDUsIGFuZ2xlPTkwLCBjb2RlPTMpCgphcnJvd3MoeDAgPSBwcmludChmYXN0LnRlbGwpWywxXSwgeTAgPSAgbWNmLmVtLnNlYXNpYSRtZWFuIC0gKDIqbWNmLmVtLnNlYXNpYSRzZCksCiAgICAgICAgIHgxID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkxID0gIG1jZi5lbS5zZWFzaWEkbWVhbiArICgyKm1jZi5lbS5zZWFzaWEkc2QpLAogICAgICAgICBjb2wgPSBjb2wuc2Vhc2lhLGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKYXJyb3dzKHgwID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkwID0gIG1jZi5lbS5jb25nbyRtZWFuIC0gKDIqbWNmLmVtLmNvbmdvJHNkKSwKICAgICAgICAgeDEgPSBwcmludChmYXN0LnRlbGwpWywxXSwgeTEgPSAgbWNmLmVtLmNvbmdvJG1lYW4gKyAoMiptY2YuZW0uY29uZ28kc2QpLAogICAgICAgICBjb2wgPSBjb2wuY29uZ28sbGVuZ3RoPTAuMDUsIGFuZ2xlPTkwLCBjb2RlPTMpCgpwb2ludHMocHJpbnQoZmFzdC50ZWxsKVssMV0sIG1jZi5lbS5hbWF6JG1lYW4sIGNvbCA9IGNvbC5hbWF6LCBwY2ggPSBhcy5jaGFyYWN0ZXIoMTo5KSwgZm9udCA9IDIpCnBvaW50cyhwcmludChmYXN0LnRlbGwpWywxXSwgbWNmLmVtLnNlYXNpYSRtZWFuLCBjb2wgPSBjb2wuc2Vhc2lhLCBwY2ggPSBhcy5jaGFyYWN0ZXIoMTo5KSwgZm9udCA9IDIpCnBvaW50cyhwcmludChmYXN0LnRlbGwpWywxXSwgbWNmLmVtLmNvbmdvJG1lYW4sIGNvbCA9IGNvbC5jb25nbywgcGNoID0gYXMuY2hhcmFjdGVyKDE6OSksIGZvbnQgPSAyKQoKYWJsaW5lKDAsMSwgbHR5ID0gJ2Rhc2hlZCcpCgpkZXYub2ZmKCkKCiMgSnVzdCB1c2UgdGhlIDUwMDAgZW5zZW1ibGUgbWVtYmVyIGV4YW1wbGUgZm9yIHRoZSBwYXBlci4KcGRmKHdpZHRoID0gNywgaGVpZ2h0ID0gNywgZmlsZSA9ICdncmFwaGljcy9mYXN0OTlfdnNfbWNmMy5wZGYnKQojZGV2Lm5ldyh3aWR0aCA9IDEyLCBoZWlnaHQgPSA3KQpwYXIobWFyID0gYyg1LDUsMywyKSwgbGFzID0gMSkKCnBsb3QocHJpbnQoZmFzdC50ZWxsKVssMV0sIG1jZi5lbS5hbWF6JG1lYW4sIGNvbCA9IGNvbC5hbWF6LCBwY2ggPSBhcy5jaGFyYWN0ZXIoMTo5KSwKICAgICB5bGltID0gYygwLDAuMzIpLCB4bGltID0gYygwLDAuMzIpLAogICAgIHhsYWIgPSAnRkFTVDk5IGZpcnN0LW9yZGVyIHNlbnNpdGl2aXR5JywKICAgICB5bGFiID0gJ01DRiBzZW5zaXRpdml0eSAoS1Mgc3RhdGlzdGljKScsCiAgICAgcHR5ID0gJ24nCiAgICAgKQoKYXJyb3dzKHgwID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkwID0gIG1jZi5lbS5hbWF6JG1lYW4gLSAoMiptY2YuZW0uYW1heiRzZCksCiAgICAgICAgIHgxID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkxID0gIG1jZi5lbS5hbWF6JG1lYW4gKyAoMiptY2YuZW0uYW1heiRzZCksCiAgICAgICAgIGNvbCA9IGNvbC5hbWF6LGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKYXJyb3dzKHgwID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkwID0gIG1jZi5lbS5zZWFzaWEkbWVhbiAtICgyKm1jZi5lbS5zZWFzaWEkc2QpLAogICAgICAgICB4MSA9IHByaW50KGZhc3QudGVsbClbLDFdLCB5MSA9ICBtY2YuZW0uc2Vhc2lhJG1lYW4gKyAoMiptY2YuZW0uc2Vhc2lhJHNkKSwKICAgICAgICAgY29sID0gY29sLnNlYXNpYSxsZW5ndGg9MC4wNSwgYW5nbGU9OTAsIGNvZGU9MykKCmFycm93cyh4MCA9IHByaW50KGZhc3QudGVsbClbLDFdLCB5MCA9ICBtY2YuZW0uY29uZ28kbWVhbiAtICgyKm1jZi5lbS5jb25nbyRzZCksCiAgICAgICAgIHgxID0gcHJpbnQoZmFzdC50ZWxsKVssMV0sIHkxID0gIG1jZi5lbS5jb25nbyRtZWFuICsgKDIqbWNmLmVtLmNvbmdvJHNkKSwKICAgICAgICAgY29sID0gY29sLmNvbmdvLGxlbmd0aD0wLjA1LCBhbmdsZT05MCwgY29kZT0zKQoKcG9pbnRzKHByaW50KGZhc3QudGVsbClbLDFdLCBtY2YuZW0uYW1heiRtZWFuLCBjb2wgPSBjb2wuYW1heiwgcGNoID0gYXMuY2hhcmFjdGVyKDE6OSksIGZvbnQgPSAyKQpwb2ludHMocHJpbnQoZmFzdC50ZWxsKVssMV0sIG1jZi5lbS5zZWFzaWEkbWVhbiwgY29sID0gY29sLnNlYXNpYSwgcGNoID0gYXMuY2hhcmFjdGVyKDE6OSksIGZvbnQgPSAyKQpwb2ludHMocHJpbnQoZmFzdC50ZWxsKVssMV0sIG1jZi5lbS5jb25nbyRtZWFuLCBjb2wgPSBjb2wuY29uZ28sIHBjaCA9IGFzLmNoYXJhY3RlcigxOjkpLCBmb250ID0gMikKCmFibGluZSgwLDEsIGx0eSA9ICdkYXNoZWQnKQoKbGVnZW5kKCd0b3BsZWZ0JywgcGNoID0gYXMuY2hhcmFjdGVyKDE6OSksIGxlZ2VuZCA9IGNvbG5hbWVzKFhfdHJvcGljc19ub3JtKSwgY2V4ID0gMC44LCBidHkgPSAnbicpCgpsZWdlbmQoJ3RvcCcsIGx0eSA9ICdzb2xpZCcsIGxlZ2VuZCA9IGMoJ0FtYXpvbicsICdTRSBBc2lhJywgJ0MgQWZyaWNhJyksCiAgICAgICB0ZXh0LmNvbCA9IGMoY29sLmFtYXosY29sLnNlYXNpYSwgY29sLmNvbmdvKSwKICAgICAgIGNvbCA9IGMoY29sLmFtYXosY29sLnNlYXNpYSwgY29sLmNvbmdvKQogICAgICAgLCBjZXggPSAwLjgsIGJ0eSA9ICduJykKCmRldi5vZmYoKQoKCgpgYGAKCgoK