0.1 Preliminaries

Load libraries, functions and data.

0.2 Specific helper functions


# How about MAE over the range
prop_mae <- function(Y, Ypred){
  # mean absolute error as a proportion of the range of output
  
  absdiff <- abs(diff(range(Y)))
  
  mae <- MAE(Y, Ypred)
  
  propmae <- (mae / absdiff) * 100
  
  propmae
  
}



withinConstraints <- function(X, Xrange){
  # return the index of a matrix that conforms to constraints.  
  
  # X         ........  Matrix to be tested
  # Xrange    ........ Range matrix. Each column has min value in row 1 and max value in row 2
   
  
  kept_list <-vector(mode = 'list', length = ncol(X))
  
  for(i in 1:ncol(X)){

    kept_ix <- which(X[ ,i] > Xrange[1, i] & X[ ,i] < Xrange[2, i])
    
    kept_list[[i]] <- kept_ix
  }
  
  out <- kept_list[[1]]
  # run along the list and just keep the intersection
  # with each iteration
  for(i in 1:length(kept_list)){
    
    out <- intersect(out, kept_list[[i]])
  }
  
  out
  
}
AW_const <- matrix(c(0, 1e12, 35, 80, 750, 3000, 300, 800), byrow = FALSE, nrow = 2)

1 One-step-ahead analysis of emulators


oneStep <- function(X, y, nvec, nreps ){
  
   # one-step-ahead prediction of the ensemble
   # nvec is a vector of ensemble sizes at which we would like to test prediction
  
  p <- nrow(X)
  
  # error matrix with repeat samples in the rows and columns matching nvec
  errmat <- matrix(nrow = nreps, ncol = length(nvec))

  for (j in 1:length(nvec)){
    
    n <- nvec[j]
    
    print(n)
    
    for(i in 1:nreps){
      
    ix <- sample(1:p, size = n+1, replace = FALSE) # we can use the last sample as the target
    ix_train <- head(ix, length(ix) - 1)
    ix_target <- tail(ix, 1)
    
    X_train <- X[ix_train, ]
    X_target <- matrix(X[ix_target, ], nrow = 1)
    
    colnames(X_target) <- colnames(X)
    colnames(X_train) <- colnames(X)
    
    y_train <- y[ix_train]
    y_target <- y[ix_target]
    # n is the number of ensemble members we are building the emulator with
    # sample i time
    
    em <- km(~., design = X_train, response = y_train)
    
    pred_target <- predict(em, newdata = X_target, type = 'UK')
    
    err <- pred_target$mean - y_target
    
    errmat[i,j] <- err
    
    }
    
  }
  errmat
}
NA

1.1 Leave-one-out analysis of emulators

The leave-one-out section in a for loop can be replaced by using lapply (or mclapply for speed).

[move to core code]

First, build a list of emulators and then perform a leave-one-out.

Apply the leave-one-out test to each of the list members.


loolist_km_Y_level1a <- mclapply(X = emlist_km_Y_level1a, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)

loolist_km_YAnom_level1a <- mclapply(X = emlist_km_YAnom_level1a, FUN = leaveOneOut.km, type = 'UK', trend.reestim = TRUE)
loostats_km_Y_level1a <- lapply(emlist_km_Y_level1a, FUN = kmLooStats)
loostats_km_YAnom_level1a <- lapply(emlist_km_YAnom_level1a, FUN = kmLooStats)

test4all <- cbind(test4, test4b)

nvecall <- c(nvec,nvecb)

test4all_mae <- apply(abs(test4all), 2, mean)

plot(nvecall, test4all_mae, ylim = c(0,1.2e6))
for(i in 1:ncol(test4all)){
  
  points(rnorm(100, mean = nvecall[i], sd = 2), test4all[,i], pch = 20, cex = 0.8)
  points(nvecall, test4all_mae,col = 'red', pch = 19)
}

# Here, the list is a list version of the matrix Y_
emlist_km_Y_level1a <- mclapply(X = Y_sum_level1a_list, FUN = km, formula = ~., design = X_level1a, mc.cores = 4) 

1.2 How well do we predict the constrained members?

# Identify all of the members which fall under a "level 2" constraint.
level2_ix <- withinConstraints(Y_const_level1a_scaled, AW_const)

# Build an emulator for the scaled output

Y_const_level1a_scaled_list <- mat2list(Y_const_level1a_scaled)

emlist_km_Y_const_level1a_scaled <- mclapply(X = Y_const_level1a_scaled_list, FUN = km, formula = ~., design = X_level1a, mc.cores = 4) 

#pdf(file = 'figs/kmloostats_Y_level1a.pdf', width = 12, height = 12)
par(mfrow = c(4,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y_level1a)){
  
  y <- Y_level1a[, y_names_sum[i]]
  loo <- loolist_km_Y_level1a[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = '' , ylim = ylim, col = makeTransparent('black', 70),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent('black', 70))
  abline(0,1)
  legend('topleft', legend = y_names_sum[i], bty = 'n', text.font = 2  )
  legend('bottomright',legend = paste('pmae =',round(loostats_km_Y_level1a[[i]]$pmae,2),'%') , bty = 'n', text.font = 2)

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Level 1a ensemble outputs', side = 3, line = 0, outer = TRUE, cex = 2)


#dev.off()
# Would the prediction be in the constrained?


pred_km_Y_const_level1a_scaled <- matrix(ncol = ncol(Y_const_level1a_scaled), nrow = nrow(Y_const_level1a_scaled))
colnames(pred_km_Y_const_level1a_scaled) <- colnames(Y_const_level1a_scaled)


for(i in 1:ncol(Y_const_level1a_scaled)){
  
  pred <- loolist_km_Y_const_level1a_scaled[[i]]$mean
  pred_km_Y_const_level1a_scaled[, i] <- pred
  
}

pred_level2_ix <- withinConstraints(pred_km_Y_const_level1a_scaled, AW_const)

Plotting the predictions, with a special focus on the constrained members. The message is, to do better on predicting the members, we need to do better with the cVeg emulator. Many of its members are way too low, and this is dragging down the predictions. Some are false positives though. Could we plot Loo error vs the parameters?

# Identify all of the members which fall under a "level 2" constraint.
level2_ix <- withinConstraints(Y_const_level1a_scaled, AW_const)

# Build an emulator for the scaled output

Y_const_level1a_scaled_list <- mat2list(Y_const_level1a_scaled)

emlist_km_Y_const_level1a_scaled <- mclapply(X = Y_const_level1a_scaled_list, FUN = km, formula = ~., design = X_level1a, mc.cores = 4) 
library(verification)


model <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))

model[level2_ix] <- TRUE
emulator[pred_level2_ix] <- TRUE

ver <- verify(obs = model, pred = emulator, frcst.type = 'binary')

# ETS runs from -1/3 to 1, with 0 showing no skill, so we have some skill. Could be better.
ver$ETS
ver$HSS

1.3 A better emulator for cVeg

It’s clear from other experiments that a major barrier to a good prediction of a constrained is a better emulator for cVeg.

Some experiments to create a better emulator

# more multistarts


y <- Y_const_level1a_scaled[, 'cVeg_lnd_sum']
# change the output

# This breaks
#m2 <- km(~.^2, design=X_level1a, response=y, multistart = 4)

# True positive is things in both observed and predicted.

tp_ix <- intersect(level2_ix, pred_level2_ix)

# False positive is things in predicted but not in observed.
fp_ix <- setdiff(pred_level2_ix, level2_ix)

# False negative is things in observed but not predicted
fn_ix <- setdiff(level2_ix, pred_level2_ix)

# true negative is things not in observed or predicted

tn_ix <- setdiff(1:nrow(Y_const_level1a_scaled), union(level2_ix, pred_level2_ix))

#should be 362
#length(c(tp_ix, fp_ix, fn_ix, tn_ix))

clines_lower <- c(0, 35, 750, 300)
clines_upper <- c(NA, 80, 3000, 800)

# correctly predicted in constrained group = red
colvec <- rep('black', nrow(Y_const_level1a_scaled))
pchvec <- rep(21, nrow(Y_const_level1a_scaled))

colvec[tp_ix] <- 'blue'
pchvec[tp_ix] <- 19

colvec[fp_ix] <- 'red'
pchvec[fp_ix] <- 19

colvec[fn_ix] <- 'gold'
pchvec[fn_ix] <- 19

colvec[tn_ix] <- 'darkgrey'
pchvec[tn_ix] <- 21
  

pdf(width = 10, height = 10, file = 'figs/Y_const_loo.pdf')
par(mfrow = c(2,2), oma = c(0.1,0.1,4,0.1))
for(i in 1:4){
plot(Y_const_level1a_scaled[ ,i], pred_km_Y_const_level1a_scaled[ ,i], type = 'n', las = 1, main = colnames(Y_const_level1a_scaled)[i],
     xlab = 'model', ylab = 'emulator')
  
abline(0,1)
abline(v = clines_lower[i], col = 'darkgrey')
abline(v = clines_upper[i], col = 'darkgrey', lty = 'dashed')

abline(h = clines_lower[i], col = 'darkgrey')
abline(h = clines_upper[i], col = 'darkgrey', lty = 'dashed')

points(Y_const_level1a_scaled[ ,i], pred_km_Y_const_level1a_scaled[ ,i], col = colvec, pch = pchvec)

}

reset()

legend('top', legend = c('True positive', 'False Positive', 'False Negative', 'True Negative', 'lower bound', 'upper bound'), pch = c(19,19, 19, 21, NA, NA), col = c('blue', 'red', 'gold', 'darkgrey', 'darkgrey', 'darkgrey'), lty = c(NA,NA,NA,NA, 'solid', 'dashed'), horiz = TRUE)

dev.off()
null device 
          1 
library(verification)
Loading required package: boot
Loading required package: CircStats
Loading required package: dtw
Loading required package: proxy

Attaching package: ‘proxy’

The following object is masked from ‘package:spam’:

    as.matrix

The following objects are masked from ‘package:stats’:

    as.dist, dist

The following object is masked from ‘package:base’:

    as.matrix

Loaded dtw v1.22-3. See ?dtw for help, citation("dtw") for use in publication.
model <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))

model[level2_ix] <- TRUE
emulator[pred_level2_ix] <- TRUE

ver <- verify(obs = model, pred = emulator, frcst.type = 'binary')

# ETS runs from -1/3 to 1, with 0 showing no skill, so we have some skill. Could be better.
ver$ETS
[1] 0.3477098
ver$HSS
[1] 0.5160008

How good are the leave-one-out predictions of the transformed data?

It appears the square root transformation outperforms the standard - but mostly at low values (which we don’t think are realistic).

# more multistarts


y <- Y_const_level1a_scaled[, 'cVeg_lnd_sum']
# change the output

# This breaks
#m2 <- km(~.^2, design=X_level1a, response=y, multistart = 4)
loo_m_stan <- leaveOneOut.km(m_stan, type = 'UK', trend.reestim = TRUE)

loo_m_logy <- leaveOneOut.km(m_logy, type = 'UK', trend.reestim = TRUE)

loo_m_sqrty <- leaveOneOut.km(m_sqrty, type = 'UK', trend.reestim = TRUE)
# Make a new matrix of predictions, and find which enemble members would pass the constraint.

pred_km_Y_const_level1a_scaled_cVeg_logy <- pred_km_Y_const_level1a_scaled
pred_km_Y_const_level1a_scaled_cVeg_logy[, 'cVeg_lnd_sum'] <- exp(loo_m_logy$mean)

pred_level2_logy_ix <- withinConstraints(pred_km_Y_const_level1a_scaled_cVeg_logy, AW_const)


emulator_logy <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator_logy[pred_level2_logy_ix] <- TRUE
ver_logy <- verify(obs = model, pred = emulator_logy, frcst.type = 'binary')
# ETS runs from -1/3 to 1, with 0 showing no skill, so we have some skill. Could be better.

plot(y, loo_m_stan$mean,col = makeTransparent('black', 150), pch = 19)
points(y, exp(loo_m_logy$mean), col = makeTransparent('red', 150), pch = 19)
points(y, (loo_m_sqrty$mean)^2, col = makeTransparent('blue', 150), pch = 19)
abline(0,1)
abline(h = c(300, 800))
abline(v = c(300, 800))



errSummary <- function(obs, pred){
  
    err <- pred - obs
    mae <- mean(abs(err))
    rmse <- sqrt(mean(err^2))
    maxerr <- max(err)
    absdiff <- abs(diff(range(obs)))
    pmae <- (mae/absdiff) * 100
    return(list(mae = mae, pmae = pmae, maxerr = maxerr))
}


errSummary(y, loo_m_stan$mean)
$mae
[1] 86.09347

$pmae
[1] 4.241667

$maxerr
[1] 472.4355
errSummary(y, exp(loo_m_logy$mean))
$mae
[1] 68.824

$pmae
[1] 3.390832

$maxerr
[1] 1135.581
errSummary(y, (loo_m_sqrty$mean)^2)
$mae
[1] 61.27904

$pmae
[1] 3.019106

$maxerr
[1] 461.909
ver$tab

ver_logy$tab

ver_sqrty$tab
# Make a new matrix of predictions, and find which enemble members would pass the constraint.

pred_km_Y_const_level1a_scaled_cVeg_sqrty <- pred_km_Y_const_level1a_scaled
pred_km_Y_const_level1a_scaled_cVeg_sqrty[, 'cVeg_lnd_sum'] <- (loo_m_sqrty$mean)^2

pred_level2_sqrty_ix <- withinConstraints(pred_km_Y_const_level1a_scaled_cVeg_sqrty, AW_const)

emulator_sqrty <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator_sqrty[pred_level2_sqrty_ix] <- TRUE
ver_sqrty<- verify(obs = model, pred = emulator_sqrty, frcst.type = 'binary')
ver$tab
   
      0   1
  0 311  14
  1  17  20
ver_logy$tab
   
      0   1
  0 313  12
  1  24  13
ver_sqrty$tab
   
      0   1
  0 315  10
  1  21  16
plot(y, (loo_m_sqrt_gauss_gen$mean)^2)
abline(0,1)
pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen <- pred_km_Y_const_level1a_scaled
pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen[, 'cVeg_lnd_sum'] <- exp(loo_m_logy_gauss_gen$mean)

pred_level2_logy_gauss_gen_ix <- withinConstraints(pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen, AW_const)


emulator_logy_gauss_gen <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator_logy_gauss_gen[pred_level2_logy_gauss_gen_ix] <- TRUE
ver_logy_gauss_gen <- verify(obs = model, pred = emulator_logy_gauss_gen, frcst.type = 'binary')

pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen <- pred_km_Y_const_level1a_scaled
pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen[, 'cVeg_lnd_sum'] <- exp(loo_m_logy_gauss_gen$mean)

pred_level2_logy_gauss_gen_ix <- withinConstraints(pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen, AW_const)


emulator_logy_gauss_gen <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator_logy_gauss_gen[pred_level2_logy_gauss_gen_ix] <- TRUE
ver_logy_gauss_gen <- verify(obs = model, pred = emulator_logy_gauss_gen, frcst.type = 'binary')
plot(y, loo_m_stan_gauss_gen$mean)
abline(0,1)

2 How about truncating the design?

(I don’t think I have this right yet.)

pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen <- pred_km_Y_const_level1a_scaled
pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen[, 'cVeg_lnd_sum'] <- exp(loo_m_logy_gauss_gen$mean)

pred_level2_logy_gauss_gen_ix <- withinConstraints(pred_km_Y_const_level1a_scaled_cVeg_logy_gauss_gen, AW_const)


emulator_logy_gauss_gen <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator_logy_gauss_gen[pred_level2_logy_gauss_gen_ix] <- TRUE
ver_logy_gauss_gen <- verify(obs = model, pred = emulator_logy_gauss_gen, frcst.type = 'binary')

2.1 Modelling emulator error in cVeg

pred_km_Y_const_level1a_scaled_cVeg_stan_gauss_gen <- pred_km_Y_const_level1a_scaled
pred_km_Y_const_level1a_scaled_cVeg_stan_gauss_gen[, 'cVeg_lnd_sum'] <- (loo_m_stan_gauss_gen$mean)

pred_level2_stan_gauss_gen_ix <- withinConstraints(pred_km_Y_const_level1a_scaled_cVeg_stan_gauss_gen, AW_const)


emulator_stan_gauss_gen <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator_stan_gauss_gen[pred_level2_stan_gauss_gen_ix] <- TRUE
ver_stan_gauss_gen <- verify(obs = model, pred = emulator_stan_gauss_gen, frcst.type = 'binary')

First, plot and emulate the value of cVeg

mins <- apply(X_level1a,2,FUN = min)
maxes <- apply(X_level1a,2,FUN = max)
  
nsamp_unif <- 1000  
X_unif <- samp_unif(nsamp_unif, mins = mins, maxes = maxes)

pred_cVeg_stan_unif <- predict(m_cVeg_stan, newdata = X_unif, type = 'UK')

There is a really important threshold for cVeg in b_wl_io. This is actually picked up nicely in the History matching section, so I’m not worried that it’s being missed.


DM_const <- AW_const <- matrix(c(0, 1e12, 10, 120, 500, 5000, 100, 1000), byrow = FALSE, nrow = 2)

level1b_ix <- withinConstraints(Y_const_level1a_scaled, DM_const) 

X_level1b <- X_level1a[level1b_ix , ]

y_level1b <- Y_const_level1a_scaled[level1b_ix, 'cVeg_lnd_sum']

m_stan_level1b <- km(~., design = X_level1b, response = y_level1b )

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 :  1.866821 1.998562 1.993715 1.625276 1.907336 1.985483 1.987869 1.752652 1.930075 1.979062 2 1.911669 2 1.983312 1.945102 1.959521 1.932368 1.971158 1.929582 1.968588 1.928133 1.968285 1.928935 1.964885 1.985963 1.980488 1.98846 1.965067 1.963628 1.976007 1.944043 1.957393 
  - best initial criterion value(s) :  -698.8532 

N = 32, M = 5 machine precision = 2.22045e-16
At X0, 0 variables are exactly at the bounds
At iterate     0  f=       698.85  |proj g|=        1.893
At iterate     1  f =       698.68  |proj g|=       0.87708
At iterate     2  f =       698.47  |proj g|=       0.75146
At iterate     3  f =       698.39  |proj g|=        0.7534
At iterate     4  f =       698.33  |proj g|=       0.80178
At iterate     5  f =       698.15  |proj g|=        1.8415
At iterate     6  f =       697.95  |proj g|=        1.8491
At iterate     7  f =       697.54  |proj g|=        1.8746
At iterate     8  f =       696.94  |proj g|=        1.6954
At iterate     9  f =       696.64  |proj g|=        1.8521
At iterate    10  f =       696.54  |proj g|=        1.4949
At iterate    11  f =       696.47  |proj g|=        1.4526
At iterate    12  f =       696.38  |proj g|=        1.8729
At iterate    13  f =       696.28  |proj g|=        1.1266
At iterate    14  f =        696.2  |proj g|=       0.86561
At iterate    15  f =       695.92  |proj g|=        1.8725
At iterate    16  f =       695.78  |proj g|=        1.8655
At iterate    17  f =       695.63  |proj g|=        1.2476
At iterate    18  f =       695.26  |proj g|=       0.40643
At iterate    19  f =       695.18  |proj g|=       0.40228
At iterate    20  f =       695.04  |proj g|=        1.8677
At iterate    21  f =       694.64  |proj g|=        1.8651
At iterate    22  f =       694.51  |proj g|=        1.8663
At iterate    23  f =       694.45  |proj g|=       0.63258
At iterate    24  f =       694.42  |proj g|=       0.63243
At iterate    25  f =        694.4  |proj g|=       0.65058
At iterate    26  f =       694.39  |proj g|=         1.864
At iterate    27  f =       694.36  |proj g|=        1.8624
At iterate    28  f =       694.31  |proj g|=        1.2125
At iterate    29  f =       694.26  |proj g|=       0.63557
At iterate    30  f =       694.16  |proj g|=       0.46324
At iterate    31  f =       694.11  |proj g|=        1.8746
At iterate    32  f =       693.98  |proj g|=         1.868
At iterate    33  f =       693.89  |proj g|=       0.29336
At iterate    34  f =       693.85  |proj g|=       0.24402
At iterate    35  f =       693.83  |proj g|=       0.28975
At iterate    36  f =       693.81  |proj g|=        1.8644
At iterate    37  f =        693.8  |proj g|=        1.8657
At iterate    38  f =       693.77  |proj g|=        1.8678
At iterate    39  f =       693.72  |proj g|=        1.8672
At iterate    40  f =       693.68  |proj g|=        0.2783
At iterate    41  f =       693.66  |proj g|=       0.26836
At iterate    42  f =       693.63  |proj g|=       0.27367
At iterate    43  f =       693.51  |proj g|=         1.861
At iterate    44  f =       693.47  |proj g|=        1.8564
At iterate    45  f =       693.44  |proj g|=        1.0159
At iterate    46  f =       693.42  |proj g|=       0.26437
At iterate    47  f =        693.4  |proj g|=        1.7395
At iterate    48  f =       693.39  |proj g|=        1.0814
At iterate    49  f =       693.38  |proj g|=       0.67703
At iterate    50  f =       693.37  |proj g|=       0.62383
At iterate    51  f =       693.36  |proj g|=       0.67295
At iterate    52  f =       693.36  |proj g|=       0.62037
At iterate    53  f =       693.34  |proj g|=       0.49582
At iterate    54  f =       693.31  |proj g|=        1.1353
At iterate    55  f =        693.3  |proj g|=       0.45097
At iterate    56  f =       693.27  |proj g|=        1.8567
At iterate    57  f =       693.26  |proj g|=        1.6931
At iterate    58  f =       693.23  |proj g|=        1.3271
At iterate    59  f =       693.22  |proj g|=       0.71561
At iterate    60  f =       693.21  |proj g|=          1.84
At iterate    61  f =       693.18  |proj g|=        1.2344
At iterate    62  f =       693.17  |proj g|=       0.13801
At iterate    63  f =       693.17  |proj g|=       0.82232
At iterate    64  f =       693.16  |proj g|=       0.15622
At iterate    65  f =       693.16  |proj g|=       0.14535
At iterate    66  f =       693.14  |proj g|=        1.4028
At iterate    67  f =       693.13  |proj g|=       0.92428
At iterate    68  f =       693.12  |proj g|=        1.5166
At iterate    69  f =       693.12  |proj g|=        1.1582
At iterate    70  f =        693.1  |proj g|=        1.8639
At iterate    71  f =        693.1  |proj g|=        1.0532
At iterate    72  f =       693.09  |proj g|=       0.28912
At iterate    73  f =       693.09  |proj g|=       0.19426
At iterate    74  f =       693.09  |proj g|=       0.12491
At iterate    75  f =       693.09  |proj g|=       0.27997
At iterate    76  f =       693.09  |proj g|=        1.2754
At iterate    77  f =       693.08  |proj g|=        1.8634
At iterate    78  f =       693.07  |proj g|=        1.6847
At iterate    79  f =       693.07  |proj g|=        0.1567
At iterate    80  f =       693.06  |proj g|=       0.19142
At iterate    81  f =       693.06  |proj g|=       0.65331
At iterate    82  f =       693.05  |proj g|=       0.52327
At iterate    83  f =       693.05  |proj g|=       0.19058
At iterate    84  f =       693.05  |proj g|=       0.11956
At iterate    85  f =       693.04  |proj g|=        1.8636
At iterate    86  f =       693.04  |proj g|=       0.59169
At iterate    87  f =       693.03  |proj g|=       0.54006
At iterate    88  f =       693.03  |proj g|=       0.42748
At iterate    89  f =       693.03  |proj g|=       0.28833
At iterate    90  f =       693.03  |proj g|=       0.21177
At iterate    91  f =       693.03  |proj g|=       0.26261
At iterate    92  f =       693.03  |proj g|=       0.26001
At iterate    93  f =       693.03  |proj g|=       0.26053
At iterate    94  f =       693.02  |proj g|=       0.78214
At iterate    95  f =       693.02  |proj g|=       0.94871
At iterate    96  f =       693.02  |proj g|=        1.2453
At iterate    97  f =       693.02  |proj g|=       0.29434
At iterate    98  f =       693.02  |proj g|=      0.066866
At iterate    99  f =       693.02  |proj g|=      0.067068
At iterate   100  f =       693.02  |proj g|=       0.11515
At iterate   101  f =       693.02  |proj g|=       0.15044
final  value 693.017000 
stopped after 101 iterations
loo_m_stan_level1b <- leaveOneOut.km(m_stan_level1b, type = 'UK', trend.reestim = TRUE)

pred_km_Y_const_level1a_scaled_cVeg_stan_level1b <- pred_km_Y_const_level1a_scaled[level1b_ix, ]
pred_km_Y_const_level1a_scaled_cVeg_stan_level1b[, 'cVeg_lnd_sum'] <- (loo_m_stan_level1b$mean)

pred_level2_stan_level1b_ix <- withinConstraints(pred_km_Y_const_level1a_scaled_cVeg_stan_level1b, AW_const)


emulator_stan_level1b <- vector(mode = "logical", length = nrow(Y_const_level1a_scaled))
emulator_stan_level1b[pred_level2_stan_level1b_ix] <- TRUE
ver_stan_level1b <- verify(obs = model, pred = emulator_stan_level1b, frcst.type = 'binary')
cVeg_level1a <- Y_const_level1a_scaled[, 'cVeg_lnd_sum']


require(foreach)

# below an example for a computer with 2 cores, but also work with 1 core

nCores <- 4
require(doParallel)
cl <-  makeCluster(nCores) 
registerDoParallel(cl)

m_cVeg_stan <- km(~., design = X_level1a, response = cVeg_level1a, multistart = 4   )

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 1.998154 1.778477 2 2 2 1.998084 2 2 2 1.99598 2 1.989889 1.997502 1.989483 1.98997 1.987916 1.983347 1.988424 2 1.990947 1.995643 1.994232 1.992247 2 2 2 
  - best initial criterion value(s) :  -2315.161 -2315.2 -2315.245 -2315.484 


* The 4 best values (multistart) obtained are:
 2271.948 2271.948 2271.948 2296.345 
* The model corresponding to the best one (2271.948) is stored. 
stopCluster(cl)

Then use the leave-one-out differences to get an idea of parameters where the emulator is going wrong. (these should be roughly the same)

2.1.1 Comparison of level 0 and level 1 emulators for NPP (modern value)

# Plot the regular km emulator. Doesn’t look great.


cVeg_level1a_trunc <- cVeg_level1a
cVeg_level1a_trunc[cVeg_level1a > 800] <- 800
par(oma = c(0,0,0,3), bg = 'white')
pairs(X_level1a,
      labels = 1:d,
      gap = 0, lower.panel = NULL, xlim = c(0,1), ylim = c(0,1),
      panel = cpoints,
      z = cVeg_level1a_trunc,
      col = byr,
      cex.labels = 1,
      col.axis = 'white',
      pch = 20
      )

image.plot(legend.only = TRUE,
           zlim = range(cVeg_level1a_trunc),
           col = byr,
           legend.args = list(text = 'cVeg', side = 3, line = 1),
           horizontal = TRUE
)

  legend('left', legend = paste(1:d, colnames(lhs)), cex = 0.9, bty = 'n')

plot(em_npp_level1)

2.1.2 Leave-one-out summaries of NPP emulators

par(mfrow = c(2,2))
plot(X_level1a[, 'b_wl_io'], cVeg_level1a)
plot(X_level1a[, 'kaps_roth'], cVeg_level1a)
plot(X_level1a[, 'lai_max_io'], cVeg_level1a)
plot(X_level1a[, 'retran_l_io'], cVeg_level1a)


# It doesn't look like Multistart makes a big difference at all for NPP
errstats_npp_level0 <- kmLooStats(km = em_npp_level0)
errstats_npp_level0_ms <- kmLooStats(km = em_npp_level0_ms)

errstats_npp_level1 <- kmLooStats(km = em_npp_level1)
errstats_npp_level1_ms <- kmLooStats(km = em_npp_level1_ms)


# proportional mean absolute error
errstats_npp_level0$pmae
errstats_npp_level0_ms$pmae


errstats_npp_level1$pmae
errstats_npp_level1_ms$pmae

2.2 How good are the emulators for each output?

The emulators appear to be at least capturing the broad response for all of the output variables.

First, plot the straight kriging emulators

#tic()
#fit_list_Y_sum_level1a <- createKmFitList(X = X_level1a, Y = Y_sum_level1a)
#toc(log = TRUE)

#tic()
#fit_list_Y_sum_level1a_par <- createKmFitListParallel(X = X_level1a, Y = Y_sum_level1a, multistart = 4)
#toc(log = TRUE)


#Ytest <- vector(mode = 'list', length = ncol(Y_sum_level1a))
#for(i in 1:2){
  
#  Ytest[[i]] <- Y_sum_level1a[, i]
#}



#tic()
#test <- lapply(X = Y_sum_level1a_list, FUN = km, formula = ~., design = X_level1a)
#toc(log = TRUE)

#tic()
#partest <- mclapply(X = Y_sum_level1a_list, FUN = km, formula = ~., design = X_level1a, mc.cores = 4) 
#toc(log = TRUE)
par(mfrow = c(4,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_Y)){
  
  y <- Y_level1[, y_names_sum[i]]
  loo <- loolist_km_Y[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = y_names_sum[i] , ylim = ylim, col = makeTransparent('black', 70),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent('black', 70))
  abline(0,1)
  legend('bottomright',legend = paste('mae =',round(loostats_km_Y[[i]]$pmae,2),'%') , bty = 'n')

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Modern Value (Y)', side = 3, line = 0, outer = TRUE, cex = 2) 


loostats_km_Ylevel1a <- vector(mode = 'list', length = length(y_names_sum))

for(i in 1:length(emlist_km_Ylevel1a)){
  
  loostats <- kmLooStats(emlist_km_Ylevel1a[[i]])
  loostats_km_Ylevel1a[[i]] <- loostats
  print(loostats$pmae)
}
# level1 vs level 1a
km_pmae_level1 <- sapply(loostats_km_Y, function(x) x$pmae)
km_pmae_level1a <- sapply(loostats_km_Ylevel1a, function(x) x$pmae)

plot(km_pmae_level1 , km_pmae_level1a )
abline(0,1)

3 km emulators for change in variables over time

par(mfrow = c(4,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_km_YAnom)){
  
  y <- YAnom_level1[, y_names_sum[i]]
  loo <- loolist_km_YAnom[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = y_names_sum[i] , ylim = ylim, col = makeTransparent('black', 70),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent('black', 70))
  abline(0,1)
  

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Change over time (YAnom)', side = 3, line = 1, outer = TRUE, cex = 2) 

Next, plot the twostep glmnet/km emulators

# Twostep glmnet emulators

if (file.exists("ts_emulators_Y.rdata")) {
  load("ts_emulators_Y.rdata")
} else {
  
  emlist_twoStep_glmnet_Y <- vector(mode = 'list', length = length(y_names_sum))
  
  for(i in 1:length(y_names_sum)){
    
    yname <- y_names_sum[i]
    y <- Y_level1[, yname]
    
    em <- twoStep_glmnet(X = X_level1, y = y)
    emlist_twoStep_glmnet_Y[[i]] <- em
  }
  
  loolist_twoStep_glmnet_Y <- vector(mode = 'list', length = length(y_names_sum))
  
  for(i in 1:length(y_names_sum)){
    
    loo <- leaveOneOut.km(model = emlist_twoStep_glmnet_Y[[i]]$emulator, type = 'UK', trend.reestim = TRUE)
    loolist_twoStep_glmnet_Y[[i]] <- loo
  }
  
  
  
  save(emlist_twoStep_glmnet_Y, loolist_twoStep_glmnet_Y, file = "ts_emulators_Y.rdata")

}
# Can get numerical performance using

loostats_twoStep_glmnet_Y <- vector(mode = 'list', length = length(y_names_sum))

for(i in 1:length(emlist_twoStep_glmnet_Y)){
  
  loostats <- kmLooStats(emlist_twoStep_glmnet_Y[[i]]$emulator)
  loostats_twoStep_glmnet_Y[[i]] <- loostats
  print(loostats$pmae)
}

3.1 TwoStep emulator performance for modern values

par(mfrow = c(4,4), mar = c(3,4,2,2), oma = c(4,4,4,0.1))
for(i in 1:length(loolist_twoStep_glmnet_Y)){
  
  y <- Y_level1[, y_names_sum[i]]
  loo <- loolist_twoStep_glmnet_Y[[i]]
  ylim <- range(c(loo$mean - (2*loo$sd), loo$mean + (2*loo$sd)) )
  plot(y, loo$mean, xlab = '', ylab = '', main = y_names_sum[i] , ylim = ylim, col = makeTransparent('black', 70),
       pch = 19)
  segments(x0 = y, y0 = loo$mean - (2*loo$sd)  , x1 = y , y1 = loo$mean + (2*loo$sd), col = makeTransparent('black', 70))
  abline(0,1)
  legend('bottomright',legend = paste('mae =',round(loostats_twoStep_glmnet_Y[[i]]$pmae,2),'%') , bty = 'n')

}

mtext('Actual', side = 1, line = 1, outer = TRUE, cex = 2 )
mtext('Predicted', side = 2, line = 0, outer = TRUE, cex = 2) 
mtext('Modern Value (Y)', side = 3, line = 0, outer = TRUE, cex = 2) 

4 Compare Leave-one-out statistics for the two types of emulator

We use the leave-one-out Mean Absolute Error, expressed as a percentage of the range of the output across the ensemble. We find that the twostep emulatorisn’t significantly more accurate, and is indeed less accurate for tree fraction.



km_pmae <- sapply(loostats_km_Y, '[[', 'pmae')
ts_pmae <- sapply(loostats_twoStep_glmnet_Y, '[[', 'pmae')

par(mar = c(12,4,2,1), las =1 )
plot(1:length(y_names_sum), km_pmae,
     ylim = c(0,15), pch = 19,
     axes = FALSE, xlab = '', 
     ylab = 'LOO MAE (% of range)',
     cex = 1.2)
points(1:length(y_names_sum),ts_pmae , col= 'red', pch = 19, cex = 1.2)
legend('topleft', c('km emulator', 'twoStep emulator'), pch = 19, col = c('black', 'red'), pt.cex = 1.2)
axis (2)
par(las = 2)
axis(1, at = 1:length(y_names_sum), labels = y_names_sum)
LS0tCnRpdGxlOiAiVGVzdGluZyBlbXVsYXRvcnMgb2YgdGhlIEVhcnRoIFN5c3RlbSBjb25maWd1cmF0aW9uIG9mIEpVTEVTIgphdXRob3I6ICJEb3VnIE1jTmVhbGwiCmRhdGU6ICIwOS8wOC8yMDIxIgpvdXRwdXQ6IAogICAgaHRtbF9ub3RlYm9vazoKICAgICAgICB0b2M6IHRydWUKICAgICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgICB0b2NfZGVwdGg6IDMKICAgICAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKLS0tCgojIyBQcmVsaW1pbmFyaWVzCkxvYWQgbGlicmFyaWVzLCBmdW5jdGlvbnMgYW5kIGRhdGEuCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmc9RkFMU0UsIHJlc3VsdHMgPSAnaGlkZSd9CiMgTG9hZCBoZWxwZXIgZnVuY3Rpb25zCgprbml0cjo6b3B0c19jaHVuayRzZXQoZmlnLnBhdGggPSAiZmlncy8iLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZ3MgPSBGQUxTRSkKCiMgbG9hZCBoZWxwZXIgZnVuY3Rpb25zLCBkYXRhIGFuZCBkbyBwcmVsaW1pbmFyeSBwcm9jZXNzaW5nIG9mIHRoZSBlbnNlbWJsZS4Kc291cmNlKCdKVUxFUy1FUy0xcDAtY29tbW9uLlInKQoKYGBgCgojIyBTcGVjaWZpYyBoZWxwZXIgZnVuY3Rpb25zCmBgYHtyfQoKIyBIb3cgYWJvdXQgTUFFIG92ZXIgdGhlIHJhbmdlCnByb3BfbWFlIDwtIGZ1bmN0aW9uKFksIFlwcmVkKXsKICAjIG1lYW4gYWJzb2x1dGUgZXJyb3IgYXMgYSBwcm9wb3J0aW9uIG9mIHRoZSByYW5nZSBvZiBvdXRwdXQKICAKICBhYnNkaWZmIDwtIGFicyhkaWZmKHJhbmdlKFkpKSkKICAKICBtYWUgPC0gTUFFKFksIFlwcmVkKQogIAogIHByb3BtYWUgPC0gKG1hZSAvIGFic2RpZmYpICogMTAwCiAgCiAgcHJvcG1hZQogIAp9CgoKCndpdGhpbkNvbnN0cmFpbnRzIDwtIGZ1bmN0aW9uKFgsIFhyYW5nZSl7CiAgIyByZXR1cm4gdGhlIGluZGV4IG9mIGEgbWF0cml4IHRoYXQgY29uZm9ybXMgdG8gY29uc3RyYWludHMuICAKICAKICAjIFggICAgICAgICAuLi4uLi4uLiAgTWF0cml4IHRvIGJlIHRlc3RlZAogICMgWHJhbmdlICAgIC4uLi4uLi4uIFJhbmdlIG1hdHJpeC4gRWFjaCBjb2x1bW4gaGFzIG1pbiB2YWx1ZSBpbiByb3cgMSBhbmQgbWF4IHZhbHVlIGluIHJvdyAyCiAgIAogIAogIGtlcHRfbGlzdCA8LXZlY3Rvcihtb2RlID0gJ2xpc3QnLCBsZW5ndGggPSBuY29sKFgpKQogIAogIGZvcihpIGluIDE6bmNvbChYKSl7CgogICAga2VwdF9peCA8LSB3aGljaChYWyAsaV0gPiBYcmFuZ2VbMSwgaV0gJiBYWyAsaV0gPCBYcmFuZ2VbMiwgaV0pCiAgICAKICAgIGtlcHRfbGlzdFtbaV1dIDwtIGtlcHRfaXgKICB9CiAgCiAgb3V0IDwtIGtlcHRfbGlzdFtbMV1dCiAgIyBydW4gYWxvbmcgdGhlIGxpc3QgYW5kIGp1c3Qga2VlcCB0aGUgaW50ZXJzZWN0aW9uCiAgIyB3aXRoIGVhY2ggaXRlcmF0aW9uCiAgZm9yKGkgaW4gMTpsZW5ndGgoa2VwdF9saXN0KSl7CiAgICAKICAgIG91dCA8LSBpbnRlcnNlY3Qob3V0LCBrZXB0X2xpc3RbW2ldXSkKICB9CiAgCiAgb3V0CiAgCn0KCmBgYAoKYGBge3J9CkFXX2NvbnN0IDwtIG1hdHJpeChjKDAsIDFlMTIsIDM1LCA4MCwgNzUwLCAzMDAwLCAzMDAsIDgwMCksIGJ5cm93ID0gRkFMU0UsIG5yb3cgPSAyKQpgYGAKCgojIE9uZS1zdGVwLWFoZWFkIGFuYWx5c2lzIG9mIGVtdWxhdG9ycwoKYGBge3J9CgpvbmVTdGVwIDwtIGZ1bmN0aW9uKFgsIHksIG52ZWMsIG5yZXBzICl7CiAgCiAgICMgb25lLXN0ZXAtYWhlYWQgcHJlZGljdGlvbiBvZiB0aGUgZW5zZW1ibGUKICAgIyBudmVjIGlzIGEgdmVjdG9yIG9mIGVuc2VtYmxlIHNpemVzIGF0IHdoaWNoIHdlIHdvdWxkIGxpa2UgdG8gdGVzdCBwcmVkaWN0aW9uCiAgCiAgcCA8LSBucm93KFgpCiAgCiAgIyBlcnJvciBtYXRyaXggd2l0aCByZXBlYXQgc2FtcGxlcyBpbiB0aGUgcm93cyBhbmQgY29sdW1ucyBtYXRjaGluZyBudmVjCiAgZXJybWF0IDwtIG1hdHJpeChucm93ID0gbnJlcHMsIG5jb2wgPSBsZW5ndGgobnZlYykpCgogIGZvciAoaiBpbiAxOmxlbmd0aChudmVjKSl7CiAgICAKICAgIG4gPC0gbnZlY1tqXQogICAgCiAgICBmb3IoaSBpbiAxOm5yZXBzKXsKICAgICAgCiAgICBpeCA8LSBzYW1wbGUoMTpwLCBzaXplID0gbisxLCByZXBsYWNlID0gRkFMU0UpICMgd2UgY2FuIHVzZSB0aGUgbGFzdCBzYW1wbGUgYXMgdGhlIHRhcmdldAogICAgaXhfdHJhaW4gPC0gaGVhZChpeCwgbGVuZ3RoKGl4KSAtIDEpCiAgICBpeF90YXJnZXQgPC0gdGFpbChpeCwgMSkKICAgIAogICAgWF90cmFpbiA8LSBYW2l4X3RyYWluLCBdCiAgICBYX3RhcmdldCA8LSBtYXRyaXgoWFtpeF90YXJnZXQsIF0sIG5yb3cgPSAxKQogICAgCiAgICAKICAgIGNvbG5hbWVzKFhfdGFyZ2V0KSA8LSBjb2xuYW1lcyhYKQogICAgY29sbmFtZXMoWF90cmFpbikgPC0gY29sbmFtZXMoWCkKICAgIAogICAgeV90cmFpbiA8LSB5W2l4X3RyYWluXQogICAgeV90YXJnZXQgPC0geVtpeF90YXJnZXRdCiAgICAjIG4gaXMgdGhlIG51bWJlciBvZiBlbnNlbWJsZSBtZW1iZXJzIHdlIGFyZSBidWlsZGluZyB0aGUgZW11bGF0b3Igd2l0aAogICAgIyBzYW1wbGUgaSB0aW1lCiAgICAKICAgIGVtIDwtIGttKH4uLCBkZXNpZ24gPSBYX3RyYWluLCByZXNwb25zZSA9IHlfdHJhaW4pCiAgICAKICAgIHByZWRfdGFyZ2V0IDwtIHByZWRpY3QoZW0sIG5ld2RhdGEgPSBYX3RhcmdldCwgdHlwZSA9ICdVSycpCiAgICAKICAgIGVyciA8LSBwcmVkX3RhcmdldCRtZWFuIC0geV90YXJnZXQKICAgIAogICAgZXJybWF0W2ksal0gPC0gZXJyCiAgICAKICAgIH0KICAgIAogIAogICAgCiAgfQogIGVycm1hdAp9CgpgYGAKCgpgYGB7cn0KCnRlc3QzIDwtIG9uZVN0ZXAoWCA9ICBYX2xldmVsMWEsIHkgPSBZX2xldmVsMWFbLCAnbnBwX25saW1fbG5kX3N1bSddLCBudmVjID0gYyg1MCwxMDAsMjAwKSwgbnJlcHMgPSAxMCkKCmBgYAoKCgojIyBMZWF2ZS1vbmUtb3V0IGFuYWx5c2lzIG9mIGVtdWxhdG9ycwoKVGhlIGxlYXZlLW9uZS1vdXQgc2VjdGlvbiBpbiBhIGZvciBsb29wIGNhbiBiZSByZXBsYWNlZCBieSB1c2luZyBsYXBwbHkgKG9yIG1jbGFwcGx5IGZvciBzcGVlZCkuICAKClttb3ZlIHRvIGNvcmUgY29kZV0KCkZpcnN0LCBidWlsZCBhIGxpc3Qgb2YgZW11bGF0b3JzIGFuZCB0aGVuIHBlcmZvcm0gYSBsZWF2ZS1vbmUtb3V0LiAKCgpBcHBseSB0aGUgbGVhdmUtb25lLW91dCB0ZXN0IHRvIGVhY2ggb2YgdGhlIGxpc3QgbWVtYmVycy4KCmBgYHtyfQoKbG9vbGlzdF9rbV9ZX2xldmVsMWEgPC0gbWNsYXBwbHkoWCA9IGVtbGlzdF9rbV9ZX2xldmVsMWEsIEZVTiA9IGxlYXZlT25lT3V0LmttLCB0eXBlID0gJ1VLJywgdHJlbmQucmVlc3RpbSA9IFRSVUUpCgpsb29saXN0X2ttX1lBbm9tX2xldmVsMWEgPC0gbWNsYXBwbHkoWCA9IGVtbGlzdF9rbV9ZQW5vbV9sZXZlbDFhLCBGVU4gPSBsZWF2ZU9uZU91dC5rbSwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQoKYGBgCgoKYGBge3J9Cmxvb3N0YXRzX2ttX1lfbGV2ZWwxYSA8LSBsYXBwbHkoZW1saXN0X2ttX1lfbGV2ZWwxYSwgRlVOID0ga21Mb29TdGF0cykKbG9vc3RhdHNfa21fWUFub21fbGV2ZWwxYSA8LSBsYXBwbHkoZW1saXN0X2ttX1lBbm9tX2xldmVsMWEsIEZVTiA9IGttTG9vU3RhdHMpCgpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZX2xldmVsMWEucGRmJywgd2lkdGggPSAxMiwgaGVpZ2h0ID0gMTIpCnBhcihtZnJvdyA9IGMoNCw0KSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lfbGV2ZWwxYSkpewogIAogIHkgPC0gWV9sZXZlbDFhWywgeV9uYW1lc19zdW1baV1dCiAgbG9vIDwtIGxvb2xpc3Rfa21fWV9sZXZlbDFhW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW4gLSAoMipsb28kc2QpLCBsb28kbWVhbiArICgyKmxvbyRzZCkpICkKICBwbG90KHksIGxvbyRtZWFuLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9ICcnICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCgnYmxhY2snLCA3MCksCiAgICAgICBwY2ggPSAxOSkKICBzZWdtZW50cyh4MCA9IHksIHkwID0gbG9vJG1lYW4gLSAoMipsb28kc2QpICAsIHgxID0geSAsIHkxID0gbG9vJG1lYW4gKyAoMipsb28kc2QpLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJywgNzApKQogIGFibGluZSgwLDEpCiAgbGVnZW5kKCd0b3BsZWZ0JywgbGVnZW5kID0geV9uYW1lc19zdW1baV0sIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMiAgKQogIGxlZ2VuZCgnYm90dG9tcmlnaHQnLGxlZ2VuZCA9IHBhc3RlKCdwbWFlID0nLHJvdW5kKGxvb3N0YXRzX2ttX1lfbGV2ZWwxYVtbaV1dJHBtYWUsMiksJyUnKSAsIGJ0eSA9ICduJywgdGV4dC5mb250ID0gMikKCn0KCm10ZXh0KCdBY3R1YWwnLCBzaWRlID0gMSwgbGluZSA9IDEsIG91dGVyID0gVFJVRSwgY2V4ID0gMiApCm10ZXh0KCdQcmVkaWN0ZWQnLCBzaWRlID0gMiwgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikgCm10ZXh0KCdMZXZlbCAxYSBlbnNlbWJsZSBvdXRwdXRzJywgc2lkZSA9IDMsIGxpbmUgPSAwLCBvdXRlciA9IFRSVUUsIGNleCA9IDIpCgojZGV2Lm9mZigpCgpgYGAKCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyfQoKI3BkZihmaWxlID0gJ2ZpZ3Mva21sb29zdGF0c19ZQW5vbV9sZXZlbDFhLnBkZicsIHdpZHRoID0gMTIsIGhlaWdodCA9IDEyKQpwYXIobWZyb3cgPSBjKDQsNCksIG1hciA9IGMoMyw0LDIsMiksIG9tYSA9IGMoNCw0LDQsMC4xKSkKZm9yKGkgaW4gMTpsZW5ndGgobG9vbGlzdF9rbV9ZQW5vbV9sZXZlbDFhKSl7CiAgCiAgeSA8LSBZQW5vbV9sZXZlbDFhWywgeV9uYW1lc19zdW1baV1dCiAgbG9vIDwtIGxvb2xpc3Rfa21fWUFub21fbGV2ZWwxYVtbaV1dCiAgeWxpbSA8LSByYW5nZShjKGxvbyRtZWFuIC0gKDIqbG9vJHNkKSwgbG9vJG1lYW4gKyAoMipsb28kc2QpKSApCiAgcGxvdCh5LCBsb28kbWVhbiwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSAnJyAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJywgNzApLAogICAgICAgcGNoID0gMTkpCiAgc2VnbWVudHMoeDAgPSB5LCB5MCA9IGxvbyRtZWFuIC0gKDIqbG9vJHNkKSAgLCB4MSA9IHkgLCB5MSA9IGxvbyRtZWFuICsgKDIqbG9vJHNkKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KCdibGFjaycsIDcwKSkKICBhYmxpbmUoMCwxKQogIGxlZ2VuZCgndG9wbGVmdCcsIGxlZ2VuZCA9IHlfbmFtZXNfc3VtW2ldLCBidHkgPSAnbicsIHRleHQuZm9udCA9IDIgICkKICBsZWdlbmQoJ2JvdHRvbXJpZ2h0JyxsZWdlbmQgPSBwYXN0ZSgncG1hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZQW5vbV9sZXZlbDFhW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nLCB0ZXh0LmZvbnQgPSAyKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0xldmVsIDFhIGVuc2VtYmxlIG91dHB1dHMnLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikKCiNkZXYub2ZmKCkKCmBgYAoKCiMjIEhvdyB3ZWxsIGRvIHdlIHByZWRpY3QgdGhlIGNvbnN0cmFpbmVkIG1lbWJlcnM/CgpgYGB7cn0KIyBJZGVudGlmeSBhbGwgb2YgdGhlIG1lbWJlcnMgd2hpY2ggZmFsbCB1bmRlciBhICJsZXZlbCAyIiBjb25zdHJhaW50LgpsZXZlbDJfaXggPC0gd2l0aGluQ29uc3RyYWludHMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCwgQVdfY29uc3QpCgojIEJ1aWxkIGFuIGVtdWxhdG9yIGZvciB0aGUgc2NhbGVkIG91dHB1dAoKWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9saXN0IDwtIG1hdDJsaXN0KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpCgplbWxpc3Rfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZCA8LSBtY2xhcHBseShYID0gWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9saXN0LCBGVU4gPSBrbSwgZm9ybXVsYSA9IH4uLCBkZXNpZ24gPSBYX2xldmVsMWEsIG1jLmNvcmVzID0gNCkgCgpgYGAKCmBgYHtyfQoKbG9vbGlzdF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkICA8LSBtY2xhcHBseShYID0gZW1saXN0X2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWQsIEZVTiA9IGxlYXZlT25lT3V0LmttLCB0eXBlID0gJ1VLJywgdHJlbmQucmVlc3RpbSA9IFRSVUUpCgpgYGAKCgpgYGB7cn0KIyBXb3VsZCB0aGUgcHJlZGljdGlvbiBiZSBpbiB0aGUgY29uc3RyYWluZWQ/CgoKcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkIDwtIG1hdHJpeChuY29sID0gbmNvbChZX2NvbnN0X2xldmVsMWFfc2NhbGVkKSwgbnJvdyA9IG5yb3coWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkpCmNvbG5hbWVzKHByZWRfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkgPC0gY29sbmFtZXMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkKCgpmb3IoaSBpbiAxOm5jb2woWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkpewogIAogIHByZWQgPC0gbG9vbGlzdF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkW1tpXV0kbWVhbgogIHByZWRfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZFssIGldIDwtIHByZWQKICAKfQoKcHJlZF9sZXZlbDJfaXggPC0gd2l0aGluQ29uc3RyYWludHMocHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkLCBBV19jb25zdCkKCmBgYAoKUGxvdHRpbmcgdGhlIHByZWRpY3Rpb25zLCB3aXRoIGEgc3BlY2lhbCBmb2N1cyBvbiB0aGUgY29uc3RyYWluZWQgbWVtYmVycy4gClRoZSBtZXNzYWdlIGlzLCB0byBkbyBiZXR0ZXIgb24gcHJlZGljdGluZyB0aGUgbWVtYmVycywgd2UgbmVlZCB0byBkbyBiZXR0ZXIgd2l0aCB0aGUgY1ZlZyBlbXVsYXRvci4gTWFueSBvZiBpdHMgbWVtYmVycyBhcmUgd2F5IHRvbyBsb3csIGFuZCB0aGlzIGlzIGRyYWdnaW5nIGRvd24gdGhlIHByZWRpY3Rpb25zLiBTb21lIGFyZSBmYWxzZSBwb3NpdGl2ZXMgdGhvdWdoLgpDb3VsZCB3ZSBwbG90IExvbyBlcnJvciB2cyB0aGUgcGFyYW1ldGVycz8KCmBgYHtyLCBmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDEwfQoKIyBUcnVlIHBvc2l0aXZlIGlzIHRoaW5ncyBpbiBib3RoIG9ic2VydmVkIGFuZCBwcmVkaWN0ZWQuCgp0cF9peCA8LSBpbnRlcnNlY3QobGV2ZWwyX2l4LCBwcmVkX2xldmVsMl9peCkKCiMgRmFsc2UgcG9zaXRpdmUgaXMgdGhpbmdzIGluIHByZWRpY3RlZCBidXQgbm90IGluIG9ic2VydmVkLgpmcF9peCA8LSBzZXRkaWZmKHByZWRfbGV2ZWwyX2l4LCBsZXZlbDJfaXgpCgojIEZhbHNlIG5lZ2F0aXZlIGlzIHRoaW5ncyBpbiBvYnNlcnZlZCBidXQgbm90IHByZWRpY3RlZApmbl9peCA8LSBzZXRkaWZmKGxldmVsMl9peCwgcHJlZF9sZXZlbDJfaXgpCgojIHRydWUgbmVnYXRpdmUgaXMgdGhpbmdzIG5vdCBpbiBvYnNlcnZlZCBvciBwcmVkaWN0ZWQKCnRuX2l4IDwtIHNldGRpZmYoMTpucm93KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpLCB1bmlvbihsZXZlbDJfaXgsIHByZWRfbGV2ZWwyX2l4KSkKCiNzaG91bGQgYmUgMzYyCiNsZW5ndGgoYyh0cF9peCwgZnBfaXgsIGZuX2l4LCB0bl9peCkpCgpjbGluZXNfbG93ZXIgPC0gYygwLCAzNSwgNzUwLCAzMDApCmNsaW5lc191cHBlciA8LSBjKE5BLCA4MCwgMzAwMCwgODAwKQoKIyBjb3JyZWN0bHkgcHJlZGljdGVkIGluIGNvbnN0cmFpbmVkIGdyb3VwID0gcmVkCmNvbHZlYyA8LSByZXAoJ2JsYWNrJywgbnJvdyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKSkKcGNodmVjIDwtIHJlcCgyMSwgbnJvdyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKSkKCmNvbHZlY1t0cF9peF0gPC0gJ2JsdWUnCnBjaHZlY1t0cF9peF0gPC0gMTkKCmNvbHZlY1tmcF9peF0gPC0gJ3JlZCcKcGNodmVjW2ZwX2l4XSA8LSAxOQoKY29sdmVjW2ZuX2l4XSA8LSAnZ29sZCcKcGNodmVjW2ZuX2l4XSA8LSAxOQoKY29sdmVjW3RuX2l4XSA8LSAnZGFya2dyZXknCnBjaHZlY1t0bl9peF0gPC0gMjEKICAKCiNwZGYod2lkdGggPSAxMCwgaGVpZ2h0ID0gMTAsIGZpbGUgPSAnZmlncy9ZX2NvbnN0X2xvby5wZGYnKQpwYXIobWZyb3cgPSBjKDIsMiksIG9tYSA9IGMoMC4xLDAuMSw0LDAuMSkpCmZvcihpIGluIDE6NCl7CnBsb3QoWV9jb25zdF9sZXZlbDFhX3NjYWxlZFsgLGldLCBwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRbICxpXSwgdHlwZSA9ICduJywgbGFzID0gMSwgbWFpbiA9IGNvbG5hbWVzKFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpW2ldLAogICAgIHhsYWIgPSAnbW9kZWwnLCB5bGFiID0gJ2VtdWxhdG9yJykKICAKYWJsaW5lKDAsMSkKYWJsaW5lKHYgPSBjbGluZXNfbG93ZXJbaV0sIGNvbCA9ICdkYXJrZ3JleScpCmFibGluZSh2ID0gY2xpbmVzX3VwcGVyW2ldLCBjb2wgPSAnZGFya2dyZXknLCBsdHkgPSAnZGFzaGVkJykKCmFibGluZShoID0gY2xpbmVzX2xvd2VyW2ldLCBjb2wgPSAnZGFya2dyZXknKQphYmxpbmUoaCA9IGNsaW5lc191cHBlcltpXSwgY29sID0gJ2RhcmtncmV5JywgbHR5ID0gJ2Rhc2hlZCcpCgpwb2ludHMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZFsgLGldLCBwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRbICxpXSwgY29sID0gY29sdmVjLCBwY2ggPSBwY2h2ZWMpCgp9CgpyZXNldCgpCgpsZWdlbmQoJ3RvcCcsIGxlZ2VuZCA9IGMoJ1RydWUgcG9zaXRpdmUnLCAnRmFsc2UgUG9zaXRpdmUnLCAnRmFsc2UgTmVnYXRpdmUnLCAnVHJ1ZSBOZWdhdGl2ZScsICdsb3dlciBib3VuZCcsICd1cHBlciBib3VuZCcpLCBwY2ggPSBjKDE5LDE5LCAxOSwgMjEsIE5BLCBOQSksIGNvbCA9IGMoJ2JsdWUnLCAncmVkJywgJ2dvbGQnLCAnZGFya2dyZXknLCAnZGFya2dyZXknLCAnZGFya2dyZXknKSwgbHR5ID0gYyhOQSxOQSxOQSxOQSwgJ3NvbGlkJywgJ2Rhc2hlZCcpLCBob3JpeiA9IFRSVUUpCgojZGV2Lm9mZigpCgpgYGAKCgpgYGB7cn0KbGlicmFyeSh2ZXJpZmljYXRpb24pCgoKbW9kZWwgPC0gdmVjdG9yKG1vZGUgPSAibG9naWNhbCIsIGxlbmd0aCA9IG5yb3coWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkpCmVtdWxhdG9yIDwtIHZlY3Rvcihtb2RlID0gImxvZ2ljYWwiLCBsZW5ndGggPSBucm93KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpKQoKbW9kZWxbbGV2ZWwyX2l4XSA8LSBUUlVFCmVtdWxhdG9yW3ByZWRfbGV2ZWwyX2l4XSA8LSBUUlVFCgp2ZXIgPC0gdmVyaWZ5KG9icyA9IG1vZGVsLCBwcmVkID0gZW11bGF0b3IsIGZyY3N0LnR5cGUgPSAnYmluYXJ5JykKCiMgRVRTIHJ1bnMgZnJvbSAtMS8zIHRvIDEsIHdpdGggMCBzaG93aW5nIG5vIHNraWxsLCBzbyB3ZSBoYXZlIHNvbWUgc2tpbGwuIENvdWxkIGJlIGJldHRlci4KdmVyJEVUUwp2ZXIkSFNTCgpgYGAKCiMjIEEgYmV0dGVyIGVtdWxhdG9yIGZvciBjVmVnCkl0J3MgY2xlYXIgZnJvbSBvdGhlciBleHBlcmltZW50cyB0aGF0IGEgbWFqb3IgYmFycmllciB0byBhIGdvb2QgcHJlZGljdGlvbiBvZiBhIGNvbnN0cmFpbmVkIGlzIGEgYmV0dGVyIGVtdWxhdG9yIGZvciBjVmVnLgoKU29tZSBleHBlcmltZW50cyB0byBjcmVhdGUgYSBiZXR0ZXIgZW11bGF0b3IKCmBgYHtyfQojIG1vcmUgbXVsdGlzdGFydHMKCgp5IDwtIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbLCAnY1ZlZ19sbmRfc3VtJ10KIyBjaGFuZ2UgdGhlIG91dHB1dAoKIyBUaGlzIGJyZWFrcwojbTIgPC0ga20ofi5eMiwgZGVzaWduPVhfbGV2ZWwxYSwgcmVzcG9uc2U9eSwgbXVsdGlzdGFydCA9IDQpCgoKYGBgCgoKCmBgYHtyfQpyZXF1aXJlKGZvcmVhY2gpCgojIGJlbG93IGFuIGV4YW1wbGUgZm9yIGEgY29tcHV0ZXIgd2l0aCAyIGNvcmVzLCBidXQgYWxzbyB3b3JrIHdpdGggMSBjb3JlCgpuQ29yZXMgPC0gNApyZXF1aXJlKGRvUGFyYWxsZWwpCmNsIDwtICBtYWtlQ2x1c3RlcihuQ29yZXMpIApyZWdpc3RlckRvUGFyYWxsZWwoY2wpCgojIGtyaWdpbmcgbW9kZWwgMSwgd2l0aCA0IHN0YXJ0aW5nIHBvaW50cyAKbV9zdGFuIDwtIGttKH4uLCBkZXNpZ249WF9sZXZlbDFhLCByZXNwb25zZSA9IHksIG11bHRpc3RhcnQ9NCkKbV9sb2d5IDwtIGttKH4uLCBkZXNpZ249WF9sZXZlbDFhLCByZXNwb25zZSA9IGxvZyh5KSwgbXVsdGlzdGFydD00KQptX3NxcnR5IDwtIGttKH4uLCBkZXNpZ249WF9sZXZlbDFhLCByZXNwb25zZSA9IHNxcnQoeSksIG11bHRpc3RhcnQ9NCkKCgpzdG9wQ2x1c3RlcihjbCkKCgpgYGAKCmBgYHtyfQpsb29fbV9zdGFuIDwtIGxlYXZlT25lT3V0LmttKG1fc3RhbiwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQoKbG9vX21fbG9neSA8LSBsZWF2ZU9uZU91dC5rbShtX2xvZ3ksIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKCmxvb19tX3NxcnR5IDwtIGxlYXZlT25lT3V0LmttKG1fc3FydHksIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKCgpgYGAKCkhvdyBnb29kIGFyZSB0aGUgbGVhdmUtb25lLW91dCBwcmVkaWN0aW9ucyBvZiB0aGUgdHJhbnNmb3JtZWQgZGF0YT8KCkl0IGFwcGVhcnMgdGhlIHNxdWFyZSByb290IHRyYW5zZm9ybWF0aW9uIG91dHBlcmZvcm1zIHRoZSBzdGFuZGFyZCAtIGJ1dCBtb3N0bHkgYXQgbG93IHZhbHVlcyAod2hpY2ggd2UgZG9uJ3QgdGhpbmsgYXJlIHJlYWxpc3RpYykuCgpgYGB7cn0KCnBsb3QoeSwgbG9vX21fc3RhbiRtZWFuLGNvbCA9IG1ha2VUcmFuc3BhcmVudCgnYmxhY2snLCAxNTApLCBwY2ggPSAxOSkKcG9pbnRzKHksIGV4cChsb29fbV9sb2d5JG1lYW4pLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ3JlZCcsIDE1MCksIHBjaCA9IDE5KQpwb2ludHMoeSwgKGxvb19tX3NxcnR5JG1lYW4pXjIsIGNvbCA9IG1ha2VUcmFuc3BhcmVudCgnYmx1ZScsIDE1MCksIHBjaCA9IDE5KQphYmxpbmUoMCwxKQphYmxpbmUoaCA9IGMoMzAwLCA4MDApKQphYmxpbmUodiA9IGMoMzAwLCA4MDApKQoKCmVyclN1bW1hcnkgPC0gZnVuY3Rpb24ob2JzLCBwcmVkKXsKICAKICAgIGVyciA8LSBwcmVkIC0gb2JzCiAgICBtYWUgPC0gbWVhbihhYnMoZXJyKSkKICAgIHJtc2UgPC0gc3FydChtZWFuKGVycl4yKSkKICAgIG1heGVyciA8LSBtYXgoZXJyKQogICAgYWJzZGlmZiA8LSBhYnMoZGlmZihyYW5nZShvYnMpKSkKICAgIHBtYWUgPC0gKG1hZS9hYnNkaWZmKSAqIDEwMAogICAgcmV0dXJuKGxpc3QobWFlID0gbWFlLCBwbWFlID0gcG1hZSwgbWF4ZXJyID0gbWF4ZXJyKSkKfQoKCmVyclN1bW1hcnkoeSwgbG9vX21fc3RhbiRtZWFuKQoKZXJyU3VtbWFyeSh5LCBleHAobG9vX21fbG9neSRtZWFuKSkKCmVyclN1bW1hcnkoeSwgKGxvb19tX3NxcnR5JG1lYW4pXjIpCgoKYGBgCgoKYGBge3J9CiMgTWFrZSBhIG5ldyBtYXRyaXggb2YgcHJlZGljdGlvbnMsIGFuZCBmaW5kIHdoaWNoIGVuZW1ibGUgbWVtYmVycyB3b3VsZCBwYXNzIHRoZSBjb25zdHJhaW50LgoKcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfc3FydHkgPC0gcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkCnByZWRfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9jVmVnX3NxcnR5WywgJ2NWZWdfbG5kX3N1bSddIDwtIChsb29fbV9zcXJ0eSRtZWFuKV4yCgpwcmVkX2xldmVsMl9zcXJ0eV9peCA8LSB3aXRoaW5Db25zdHJhaW50cyhwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRfY1ZlZ19zcXJ0eSwgQVdfY29uc3QpCgplbXVsYXRvcl9zcXJ0eSA8LSB2ZWN0b3IobW9kZSA9ICJsb2dpY2FsIiwgbGVuZ3RoID0gbnJvdyhZX2NvbnN0X2xldmVsMWFfc2NhbGVkKSkKZW11bGF0b3Jfc3FydHlbcHJlZF9sZXZlbDJfc3FydHlfaXhdIDwtIFRSVUUKdmVyX3NxcnR5PC0gdmVyaWZ5KG9icyA9IG1vZGVsLCBwcmVkID0gZW11bGF0b3Jfc3FydHksIGZyY3N0LnR5cGUgPSAnYmluYXJ5JykKYGBgCgoKYGBge3J9CiMgTWFrZSBhIG5ldyBtYXRyaXggb2YgcHJlZGljdGlvbnMsIGFuZCBmaW5kIHdoaWNoIGVuZW1ibGUgbWVtYmVycyB3b3VsZCBwYXNzIHRoZSBjb25zdHJhaW50LgoKcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfbG9neSA8LSBwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWQKcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfbG9neVssICdjVmVnX2xuZF9zdW0nXSA8LSBleHAobG9vX21fbG9neSRtZWFuKQoKcHJlZF9sZXZlbDJfbG9neV9peCA8LSB3aXRoaW5Db25zdHJhaW50cyhwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRfY1ZlZ19sb2d5LCBBV19jb25zdCkKCgplbXVsYXRvcl9sb2d5IDwtIHZlY3Rvcihtb2RlID0gImxvZ2ljYWwiLCBsZW5ndGggPSBucm93KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpKQplbXVsYXRvcl9sb2d5W3ByZWRfbGV2ZWwyX2xvZ3lfaXhdIDwtIFRSVUUKdmVyX2xvZ3kgPC0gdmVyaWZ5KG9icyA9IG1vZGVsLCBwcmVkID0gZW11bGF0b3JfbG9neSwgZnJjc3QudHlwZSA9ICdiaW5hcnknKQojIEVUUyBydW5zIGZyb20gLTEvMyB0byAxLCB3aXRoIDAgc2hvd2luZyBubyBza2lsbCwgc28gd2UgaGF2ZSBzb21lIHNraWxsLiBDb3VsZCBiZSBiZXR0ZXIuCmBgYAoKCmBgYHtyfQp2ZXIkRVRTCnZlciRIU1MKCnZlcl9zcXJ0eSRFVFMKdmVyX3NxcnR5JEhTUwoKCnZlcl9sb2d5JEVUUwp2ZXJfbG9neSRIU1MKCmBgYAoKCmBgYHtyfQp2ZXIkdGFiCgp2ZXJfbG9neSR0YWIKCnZlcl9zcXJ0eSR0YWIKCgpgYGAKCgpgYGB7cn0KCm5Db3JlcyA8LSA0CnJlcXVpcmUoZG9QYXJhbGxlbCkKY2wgPC0gIG1ha2VDbHVzdGVyKG5Db3JlcykgCnJlZ2lzdGVyRG9QYXJhbGxlbChjbCkKCiMga3JpZ2luZyBtb2RlbCAxLCB3aXRoIDQgc3RhcnRpbmcgcG9pbnRzIAptX3N0YW5fZ2F1c3MgPC0ga20ofi4sIGRlc2lnbj1YX2xldmVsMWEsIHJlc3BvbnNlID0geSwgbXVsdGlzdGFydD00LCBjb3Z0eXBlID0gJ2dhdXNzJyApCmxvb19tX3N0YW5fZ2F1c3MgPC0gbGVhdmVPbmVPdXQua20obV9zdGFuX2dhdXNzICwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQpwbG90KHksIGxvb19tX3N0YW5fZ2F1c3MkbWVhbikKCgptX3N0YW5fZ2VuIDwtIGttKH4uLCBkZXNpZ249WF9sZXZlbDFhLCByZXNwb25zZSA9IHksIG9wdGltLm1ldGhvZCA9ICdnZW4nICkKbG9vX21fc3Rhbl9nZW4gPC0gbGVhdmVPbmVPdXQua20obV9zdGFuX2dlbiwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQpwbG90KHksIGxvb19tX3N0YW5fZ2VuJG1lYW4pCgoKbV9zdGFuX2dhdXNzX2dlbiA8LSBrbSh+LiwgZGVzaWduPVhfbGV2ZWwxYSwgcmVzcG9uc2UgPSB5LCBvcHRpbS5tZXRob2QgPSAnZ2VuJyApCmxvb19tX3N0YW5fZ2F1c3NfZ2VuIDwtIGxlYXZlT25lT3V0LmttKG1fc3Rhbl9nYXVzc19nZW4sIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKcGxvdCh5LCBsb29fbV9zdGFuX2dhdXNzX2dlbiRtZWFuKQoKCm1fbG9neV9nYXVzc19nZW4gPC0ga20ofi4sIGRlc2lnbj1YX2xldmVsMWEsIHJlc3BvbnNlID0gbG9nKHkpLCBjb3Z0eXBlID0gJ2dhdXNzJywgb3B0aW0ubWV0aG9kID0gJ2dlbicpCmxvb19tX2xvZ3lfZ2F1c3NfZ2VuIDwtIGxlYXZlT25lT3V0LmttKG1fbG9neV9nYXVzc19nZW4sIHRyZW5kLnJlZXN0aW0gPSBUUlVFLCB0eXBlID0gJ1VLJykKcGxvdCh5LCBleHAobG9vX21fbG9neV9nYXVzc19nZW4kbWVhbikpCgoKbV9zcXJ0X2dhdXNzX2dlbiA8LSBrbSh+LiwgZGVzaWduPVhfbGV2ZWwxYSwgcmVzcG9uc2UgPSBzcXJ0KHkpLCBjb3Z0eXBlID0gJ2dhdXNzJywgb3B0aW0ubWV0aG9kID0gJ2dlbicpCmxvb19tX3NxcnRfZ2F1c3NfZ2VuIDwtIGxlYXZlT25lT3V0LmttKG1fc3FydF9nYXVzc19nZW4sIHRyZW5kLnJlZXN0aW0gPSBUUlVFLCB0eXBlID0gJ1VLJykKcGxvdCh5LCAobG9vX21fc3FydF9nYXVzc19nZW4kbWVhbileMikKCgpzdG9wQ2x1c3RlcihjbCkKCgpgYGAKCmBgYHtyfQpwbG90KHksIGxvb19tX3N0YW5fZ2F1c3NfZ2VuJG1lYW4pCmFibGluZSgwLDEpCmBgYAoKCmBgYHtyfQpwbG90KHksIChsb29fbV9zcXJ0X2dhdXNzX2dlbiRtZWFuKV4yKQphYmxpbmUoMCwxKQpgYGAKCgpgYGB7cn0KcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfbG9neV9nYXVzc19nZW4gPC0gcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkCnByZWRfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9jVmVnX2xvZ3lfZ2F1c3NfZ2VuWywgJ2NWZWdfbG5kX3N1bSddIDwtIGV4cChsb29fbV9sb2d5X2dhdXNzX2dlbiRtZWFuKQoKcHJlZF9sZXZlbDJfbG9neV9nYXVzc19nZW5faXggPC0gd2l0aGluQ29uc3RyYWludHMocHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfbG9neV9nYXVzc19nZW4sIEFXX2NvbnN0KQoKCmVtdWxhdG9yX2xvZ3lfZ2F1c3NfZ2VuIDwtIHZlY3Rvcihtb2RlID0gImxvZ2ljYWwiLCBsZW5ndGggPSBucm93KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpKQplbXVsYXRvcl9sb2d5X2dhdXNzX2dlbltwcmVkX2xldmVsMl9sb2d5X2dhdXNzX2dlbl9peF0gPC0gVFJVRQp2ZXJfbG9neV9nYXVzc19nZW4gPC0gdmVyaWZ5KG9icyA9IG1vZGVsLCBwcmVkID0gZW11bGF0b3JfbG9neV9nYXVzc19nZW4sIGZyY3N0LnR5cGUgPSAnYmluYXJ5JykKYGBgCgoKCgoKYGBge3J9CgpwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRfY1ZlZ19sb2d5X2dhdXNzX2dlbiA8LSBwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWQKcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfbG9neV9nYXVzc19nZW5bLCAnY1ZlZ19sbmRfc3VtJ10gPC0gZXhwKGxvb19tX2xvZ3lfZ2F1c3NfZ2VuJG1lYW4pCgpwcmVkX2xldmVsMl9sb2d5X2dhdXNzX2dlbl9peCA8LSB3aXRoaW5Db25zdHJhaW50cyhwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRfY1ZlZ19sb2d5X2dhdXNzX2dlbiwgQVdfY29uc3QpCgoKZW11bGF0b3JfbG9neV9nYXVzc19nZW4gPC0gdmVjdG9yKG1vZGUgPSAibG9naWNhbCIsIGxlbmd0aCA9IG5yb3coWV9jb25zdF9sZXZlbDFhX3NjYWxlZCkpCmVtdWxhdG9yX2xvZ3lfZ2F1c3NfZ2VuW3ByZWRfbGV2ZWwyX2xvZ3lfZ2F1c3NfZ2VuX2l4XSA8LSBUUlVFCnZlcl9sb2d5X2dhdXNzX2dlbiA8LSB2ZXJpZnkob2JzID0gbW9kZWwsIHByZWQgPSBlbXVsYXRvcl9sb2d5X2dhdXNzX2dlbiwgZnJjc3QudHlwZSA9ICdiaW5hcnknKQoKCmBgYAoKCgpgYGB7cn0KcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfc3Rhbl9nYXVzc19nZW4gPC0gcHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkCnByZWRfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9jVmVnX3N0YW5fZ2F1c3NfZ2VuWywgJ2NWZWdfbG5kX3N1bSddIDwtIChsb29fbV9zdGFuX2dhdXNzX2dlbiRtZWFuKQoKcHJlZF9sZXZlbDJfc3Rhbl9nYXVzc19nZW5faXggPC0gd2l0aGluQ29uc3RyYWludHMocHJlZF9rbV9ZX2NvbnN0X2xldmVsMWFfc2NhbGVkX2NWZWdfc3Rhbl9nYXVzc19nZW4sIEFXX2NvbnN0KQoKCmVtdWxhdG9yX3N0YW5fZ2F1c3NfZ2VuIDwtIHZlY3Rvcihtb2RlID0gImxvZ2ljYWwiLCBsZW5ndGggPSBucm93KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpKQplbXVsYXRvcl9zdGFuX2dhdXNzX2dlbltwcmVkX2xldmVsMl9zdGFuX2dhdXNzX2dlbl9peF0gPC0gVFJVRQp2ZXJfc3Rhbl9nYXVzc19nZW4gPC0gdmVyaWZ5KG9icyA9IG1vZGVsLCBwcmVkID0gZW11bGF0b3Jfc3Rhbl9nYXVzc19nZW4sIGZyY3N0LnR5cGUgPSAnYmluYXJ5JykKYGBgCgogCiMgSG93IGFib3V0IHRydW5jYXRpbmcgdGhlIGRlc2lnbj8gCgooSSBkb24ndCB0aGluayBJIGhhdmUgdGhpcyByaWdodCB5ZXQuKQogCmBgYHtyfQoKRE1fY29uc3QgPC0gQVdfY29uc3QgPC0gbWF0cml4KGMoMCwgMWUxMiwgMTAsIDEyMCwgNTAwLCA1MDAwLCAxMDAsIDEwMDApLCBieXJvdyA9IEZBTFNFLCBucm93ID0gMikKCmxldmVsMWJfaXggPC0gd2l0aGluQ29uc3RyYWludHMoWV9jb25zdF9sZXZlbDFhX3NjYWxlZCwgRE1fY29uc3QpIAoKWF9sZXZlbDFiIDwtIFhfbGV2ZWwxYVtsZXZlbDFiX2l4ICwgXQoKeV9sZXZlbDFiIDwtIFlfY29uc3RfbGV2ZWwxYV9zY2FsZWRbbGV2ZWwxYl9peCwgJ2NWZWdfbG5kX3N1bSddCgptX3N0YW5fbGV2ZWwxYiA8LSBrbSh+LiwgZGVzaWduID0gWF9sZXZlbDFiLCByZXNwb25zZSA9IHlfbGV2ZWwxYiApCgpsb29fbV9zdGFuX2xldmVsMWIgPC0gbGVhdmVPbmVPdXQua20obV9zdGFuX2xldmVsMWIsIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKCnByZWRfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9jVmVnX3N0YW5fbGV2ZWwxYiA8LSBwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRbbGV2ZWwxYl9peCwgXQpwcmVkX2ttX1lfY29uc3RfbGV2ZWwxYV9zY2FsZWRfY1ZlZ19zdGFuX2xldmVsMWJbLCAnY1ZlZ19sbmRfc3VtJ10gPC0gKGxvb19tX3N0YW5fbGV2ZWwxYiRtZWFuKQoKcHJlZF9sZXZlbDJfc3Rhbl9sZXZlbDFiX2l4IDwtIHdpdGhpbkNvbnN0cmFpbnRzKHByZWRfa21fWV9jb25zdF9sZXZlbDFhX3NjYWxlZF9jVmVnX3N0YW5fbGV2ZWwxYiwgQVdfY29uc3QpCgoKZW11bGF0b3Jfc3Rhbl9sZXZlbDFiIDwtIHZlY3Rvcihtb2RlID0gImxvZ2ljYWwiLCBsZW5ndGggPSBucm93KFlfY29uc3RfbGV2ZWwxYV9zY2FsZWQpKQplbXVsYXRvcl9zdGFuX2xldmVsMWJbcHJlZF9sZXZlbDJfc3Rhbl9sZXZlbDFiX2l4XSA8LSBUUlVFCnZlcl9zdGFuX2xldmVsMWIgPC0gdmVyaWZ5KG9icyA9IG1vZGVsLCBwcmVkID0gZW11bGF0b3Jfc3Rhbl9sZXZlbDFiLCBmcmNzdC50eXBlID0gJ2JpbmFyeScpCgpgYGAKIAojIyBNb2RlbGxpbmcgZW11bGF0b3IgZXJyb3IgaW4gY1ZlZwoKYGBge3J9CmNWZWdfbGV2ZWwxYSA8LSBZX2NvbnN0X2xldmVsMWFfc2NhbGVkWywgJ2NWZWdfbG5kX3N1bSddCgoKcmVxdWlyZShmb3JlYWNoKQoKIyBiZWxvdyBhbiBleGFtcGxlIGZvciBhIGNvbXB1dGVyIHdpdGggMiBjb3JlcywgYnV0IGFsc28gd29yayB3aXRoIDEgY29yZQoKbkNvcmVzIDwtIDQKcmVxdWlyZShkb1BhcmFsbGVsKQpjbCA8LSAgbWFrZUNsdXN0ZXIobkNvcmVzKSAKcmVnaXN0ZXJEb1BhcmFsbGVsKGNsKQoKbV9jVmVnX3N0YW4gPC0ga20ofi4sIGRlc2lnbiA9IFhfbGV2ZWwxYSwgcmVzcG9uc2UgPSBjVmVnX2xldmVsMWEsIG11bHRpc3RhcnQgPSA0ICAgKQoKc3RvcENsdXN0ZXIoY2wpCgpgYGAKCkZpcnN0LCBwbG90IGFuZCBlbXVsYXRlIHRoZSB2YWx1ZSBvZiBjVmVnCmBgYHtyfQptaW5zIDwtIGFwcGx5KFhfbGV2ZWwxYSwyLEZVTiA9IG1pbikKbWF4ZXMgPC0gYXBwbHkoWF9sZXZlbDFhLDIsRlVOID0gbWF4KQogIApuc2FtcF91bmlmIDwtIDEwMDAgIApYX3VuaWYgPC0gc2FtcF91bmlmKG5zYW1wX3VuaWYsIG1pbnMgPSBtaW5zLCBtYXhlcyA9IG1heGVzKQoKcHJlZF9jVmVnX3N0YW5fdW5pZiA8LSBwcmVkaWN0KG1fY1ZlZ19zdGFuLCBuZXdkYXRhID0gWF91bmlmLCB0eXBlID0gJ1VLJykKYGBgCgpUaGVyZSBpcyBhIHJlYWxseSBpbXBvcnRhbnQgdGhyZXNob2xkIGZvciBjVmVnIGluIGJfd2xfaW8uIFRoaXMgaXMgYWN0dWFsbHkgcGlja2VkIHVwIG5pY2VseSBpbiB0aGUgSGlzdG9yeSBtYXRjaGluZyBzZWN0aW9uLCBzbyBJJ20gbm90IHdvcnJpZWQgdGhhdCBpdCdzIGJlaW5nIG1pc3NlZC4KCmBgYHtyLCBmaWcud2lkdGggPSAxMiwgZmlnLmhlaWdodCA9IDEyLCB3YXJuaW5nPSBGQUxTRSwgbWVzYWFnZSA9IEZBTFNFfQoKY1ZlZ19sZXZlbDFhX3RydW5jIDwtIGNWZWdfbGV2ZWwxYQpjVmVnX2xldmVsMWFfdHJ1bmNbY1ZlZ19sZXZlbDFhID4gODAwXSA8LSA4MDAKcGFyKG9tYSA9IGMoMCwwLDAsMyksIGJnID0gJ3doaXRlJykKcGFpcnMoWF9sZXZlbDFhLAogICAgICBsYWJlbHMgPSAxOmQsCiAgICAgIGdhcCA9IDAsIGxvd2VyLnBhbmVsID0gTlVMTCwgeGxpbSA9IGMoMCwxKSwgeWxpbSA9IGMoMCwxKSwKICAgICAgcGFuZWwgPSBjcG9pbnRzLAogICAgICB6ID0gY1ZlZ19sZXZlbDFhX3RydW5jLAogICAgICBjb2wgPSBieXIsCiAgICAgIGNleC5sYWJlbHMgPSAxLAogICAgICBjb2wuYXhpcyA9ICd3aGl0ZScsCiAgICAgIHBjaCA9IDIwCiAgICAgICkKCmltYWdlLnBsb3QobGVnZW5kLm9ubHkgPSBUUlVFLAogICAgICAgICAgIHpsaW0gPSByYW5nZShjVmVnX2xldmVsMWFfdHJ1bmMpLAogICAgICAgICAgIGNvbCA9IGJ5ciwKICAgICAgICAgICBsZWdlbmQuYXJncyA9IGxpc3QodGV4dCA9ICdjVmVnJywgc2lkZSA9IDMsIGxpbmUgPSAxKSwKICAgICAgICAgICBob3Jpem9udGFsID0gVFJVRQopCgogIGxlZ2VuZCgnbGVmdCcsIGxlZ2VuZCA9IHBhc3RlKDE6ZCwgY29sbmFtZXMobGhzKSksIGNleCA9IDAuOSwgYnR5ID0gJ24nKQoKYGBgCgoKYGBge3IsIGZpZy53aWR0aCA9IDEwLCBmaWcuaGVpZ2h0ID0gMTB9CnBhcihtZnJvdyA9IGMoMiwyKSkKcGxvdChYX2xldmVsMWFbLCAnYl93bF9pbyddLCBjVmVnX2xldmVsMWEpCnBsb3QoWF9sZXZlbDFhWywgJ2thcHNfcm90aCddLCBjVmVnX2xldmVsMWEpCnBsb3QoWF9sZXZlbDFhWywgJ2xhaV9tYXhfaW8nXSwgY1ZlZ19sZXZlbDFhKQpwbG90KFhfbGV2ZWwxYVssICdyZXRyYW5fbF9pbyddLCBjVmVnX2xldmVsMWEpCgpgYGAKClRoZW4gdXNlIHRoZSBsZWF2ZS1vbmUtb3V0IGRpZmZlcmVuY2VzIHRvIGdldCBhbiBpZGVhIG9mIHBhcmFtZXRlcnMgd2hlcmUgdGhlIGVtdWxhdG9yIGlzIGdvaW5nIHdyb25nLiAodGhlc2Ugc2hvdWxkIGJlIHJvdWdobHkgdGhlIHNhbWUpCgpgYGB7cn0KCmBgYAoKCgoKIyMjIENvbXBhcmlzb24gb2YgbGV2ZWwgMCBhbmQgbGV2ZWwgMSBlbXVsYXRvcnMgZm9yIE5QUCAobW9kZXJuIHZhbHVlKQoKYGBge3IsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA3LCByZXN1bHRzID0gJ2hpZGUnLCB3YXJuaW5nPUZBTFNFfQp0aWMoKQplbV9ucHBfbGV2ZWwwIDwtIGttKH4uLCBkZXNpZ24gPSBYX2xldmVsMCwgIHJlc3BvbnNlID0geV9sZXZlbDApCnRvYygpCmBgYAoKCmBgYHtyLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gNywgcmVzdWx0cyA9ICdoaWRlJywgd2FybmluZz1GQUxTRX0KZW1fbnBwX2xldmVsMSA8LSBrbSh+LiwgZGVzaWduID0gWF9sZXZlbDEsICByZXNwb25zZSA9IHlfbGV2ZWwxKQoKZW1fbnBwX2xldmVsMF9tcyA8LSBrbSh+LiwgZGVzaWduID0gWF9sZXZlbDAsICByZXNwb25zZSA9IHlfbGV2ZWwwLCBtdWx0aXN0YXJ0ID0gNCkKZW1fbnBwX2xldmVsMV9tcyA8LSBrbSh+LiwgZGVzaWduID0gWF9sZXZlbDEsICByZXNwb25zZSA9IHlfbGV2ZWwxLCBtdWx0aXN0YXJ0ID0gNCkKCgpgYGAKCgogIyBQbG90IHRoZSByZWd1bGFyIGttIGVtdWxhdG9yLiBEb2Vzbid0IGxvb2sgZ3JlYXQuCgpgYGB7ciBwbG90LWVtLW5wcC1sZXZlbDAsIGZpZy53aWR0aCA9IDcsIGZpZy5oZWlnaHQgPSA3fQpwbG90KGVtX25wcF9sZXZlbDApCmBgYAoKYGBge3IgcGxvdC1lbS1ucHAtbGV2ZWwxLCBmaWcud2lkdGggPSA3LCBmaWcuaGVpZ2h0ID0gN30KcGxvdChlbV9ucHBfbGV2ZWwxKQpgYGAKCgojIyMgTGVhdmUtb25lLW91dCBzdW1tYXJpZXMgb2YgTlBQIGVtdWxhdG9ycwpgYGB7cn0KCmxvb19ucHBfbGV2ZWwwIDwtIGxlYXZlT25lT3V0LmttKGVtX25wcF9sZXZlbDAsIHR5cGUgPSAnVUsnLCB0cmVuZC5yZWVzdGltID0gVFJVRSkKbG9vX25wcF9sZXZlbDEgPC0gbGVhdmVPbmVPdXQua20oZW1fbnBwX2xldmVsMSwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQoKUk1TRSAoIGxvb19ucHBfbGV2ZWwwJG1lYW4sIHlfbGV2ZWwwKQpSTVNFICggbG9vX25wcF9sZXZlbDEkbWVhbiwgeV9sZXZlbDEpCgoKCnByb3BfbWFlKHlfbGV2ZWwwLCBsb29fbnBwX2xldmVsMCRtZWFuKQoKcHJvcF9tYWUoeV9sZXZlbDEsIGxvb19ucHBfbGV2ZWwxJG1lYW4pCgoKCgoKCmBgYAoKCgpgYGB7cn0KCiMgSXQgZG9lc24ndCBsb29rIGxpa2UgTXVsdGlzdGFydCBtYWtlcyBhIGJpZyBkaWZmZXJlbmNlIGF0IGFsbCBmb3IgTlBQCmVycnN0YXRzX25wcF9sZXZlbDAgPC0ga21Mb29TdGF0cyhrbSA9IGVtX25wcF9sZXZlbDApCmVycnN0YXRzX25wcF9sZXZlbDBfbXMgPC0ga21Mb29TdGF0cyhrbSA9IGVtX25wcF9sZXZlbDBfbXMpCgplcnJzdGF0c19ucHBfbGV2ZWwxIDwtIGttTG9vU3RhdHMoa20gPSBlbV9ucHBfbGV2ZWwxKQplcnJzdGF0c19ucHBfbGV2ZWwxX21zIDwtIGttTG9vU3RhdHMoa20gPSBlbV9ucHBfbGV2ZWwxX21zKQoKCiMgcHJvcG9ydGlvbmFsIG1lYW4gYWJzb2x1dGUgZXJyb3IKZXJyc3RhdHNfbnBwX2xldmVsMCRwbWFlCmVycnN0YXRzX25wcF9sZXZlbDBfbXMkcG1hZQoKCmVycnN0YXRzX25wcF9sZXZlbDEkcG1hZQplcnJzdGF0c19ucHBfbGV2ZWwxX21zJHBtYWUKCgoKCmBgYAoKCgojIyBIb3cgZ29vZCBhcmUgdGhlIGVtdWxhdG9ycyBmb3IgZWFjaCBvdXRwdXQ/ClRoZSBlbXVsYXRvcnMgYXBwZWFyIHRvIGJlIGF0IGxlYXN0IGNhcHR1cmluZyB0aGUgYnJvYWQgcmVzcG9uc2UgZm9yIGFsbCBvZiB0aGUgb3V0cHV0IHZhcmlhYmxlcy4KCkZpcnN0LCBwbG90IHRoZSBzdHJhaWdodCBrcmlnaW5nIGVtdWxhdG9ycwoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgcmVzdWx0cyA9ICdoaWRlJ30KCmlmIChmaWxlLmV4aXN0cygia21fZW11bGF0b3JzX1kucmRhdGEiKSkgewogIGxvYWQoImttX2VtdWxhdG9yc19ZLnJkYXRhIikKfSBlbHNlIHsKICAKICBlbWxpc3Rfa21fWSA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSkKICAKICBmb3IoaSBpbiAxOmxlbmd0aCh5X25hbWVzX3N1bSkpewogICAgeW5hbWUgPC0geV9uYW1lc19zdW1baV0KICAgIHkgPC0gWV9sZXZlbDFbLCB5bmFtZV0KICAgIAogICAgZW0gPC0ga20ofi4sIGRlc2lnbiA9IFhfbGV2ZWwxLCByZXNwb25zZSA9IHkpCiAgICBlbWxpc3Rfa21fWVtbaV1dIDwtIGVtCiAgfQogIAogIGxvb2xpc3Rfa21fWSA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSkKICAKICBmb3IoaSBpbiAxOmxlbmd0aCh5X25hbWVzX3N1bSkpewogICAgCiAgICBsb28gPC0gbGVhdmVPbmVPdXQua20obW9kZWwgPSBlbWxpc3Rfa21fWVtbaV1dLCB0eXBlID0gJ1VLJywgdHJlbmQucmVlc3RpbSA9IFRSVUUpCiAgICBsb29saXN0X2ttX1lbW2ldXSA8LSBsb28KICB9CiAgCiAgc2F2ZShlbWxpc3Rfa21fWSxsb29saXN0X2ttX1ksIGZpbGUgPSAia21fZW11bGF0b3JzX1kucmRhdGEiKQp9CgpgYGAKCgoKCgpgYGB7cn0KI3RpYygpCiNmaXRfbGlzdF9ZX3N1bV9sZXZlbDFhIDwtIGNyZWF0ZUttRml0TGlzdChYID0gWF9sZXZlbDFhLCBZID0gWV9zdW1fbGV2ZWwxYSkKI3RvYyhsb2cgPSBUUlVFKQoKI3RpYygpCiNmaXRfbGlzdF9ZX3N1bV9sZXZlbDFhX3BhciA8LSBjcmVhdGVLbUZpdExpc3RQYXJhbGxlbChYID0gWF9sZXZlbDFhLCBZID0gWV9zdW1fbGV2ZWwxYSwgbXVsdGlzdGFydCA9IDQpCiN0b2MobG9nID0gVFJVRSkKCmBgYAoKCmBgYHtyfQoKCiNZdGVzdCA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbmNvbChZX3N1bV9sZXZlbDFhKSkKI2ZvcihpIGluIDE6Mil7CiAgCiMgIFl0ZXN0W1tpXV0gPC0gWV9zdW1fbGV2ZWwxYVssIGldCiN9CgoKCiN0aWMoKQojdGVzdCA8LSBsYXBwbHkoWCA9IFlfc3VtX2xldmVsMWFfbGlzdCwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhKQojdG9jKGxvZyA9IFRSVUUpCgojdGljKCkKI3BhcnRlc3QgPC0gbWNsYXBwbHkoWCA9IFlfc3VtX2xldmVsMWFfbGlzdCwgRlVOID0ga20sIGZvcm11bGEgPSB+LiwgZGVzaWduID0gWF9sZXZlbDFhLCBtYy5jb3JlcyA9IDQpIAojdG9jKGxvZyA9IFRSVUUpCgoKYGBgCgoKCgoKCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KcGFyKG1mcm93ID0gYyg0LDQpLCBtYXIgPSBjKDMsNCwyLDIpLCBvbWEgPSBjKDQsNCw0LDAuMSkpCmZvcihpIGluIDE6bGVuZ3RoKGxvb2xpc3Rfa21fWSkpewogIAogIHkgPC0gWV9sZXZlbDFbLCB5X25hbWVzX3N1bVtpXV0KICBsb28gPC0gbG9vbGlzdF9rbV9ZW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW4gLSAoMipsb28kc2QpLCBsb28kbWVhbiArICgyKmxvbyRzZCkpICkKICBwbG90KHksIGxvbyRtZWFuLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9IHlfbmFtZXNfc3VtW2ldICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCgnYmxhY2snLCA3MCksCiAgICAgICBwY2ggPSAxOSkKICBzZWdtZW50cyh4MCA9IHksIHkwID0gbG9vJG1lYW4gLSAoMipsb28kc2QpICAsIHgxID0geSAsIHkxID0gbG9vJG1lYW4gKyAoMipsb28kc2QpLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJywgNzApKQogIGFibGluZSgwLDEpCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ21hZSA9Jyxyb3VuZChsb29zdGF0c19rbV9ZW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ01vZGVybiBWYWx1ZSAoWSknLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikgCgpgYGAKCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2LCByZXN1bHRzID0gJ2hpZGUnfQoKcmVxdWlyZShmb3JlYWNoKQpuQ29yZXMgPC0gNApyZXF1aXJlKGRvUGFyYWxsZWwpCmNsIDwtICBtYWtlQ2x1c3RlcihuQ29yZXMpIApyZWdpc3RlckRvUGFyYWxsZWwoY2wpCgppZiAoZmlsZS5leGlzdHMoImttX2VtdWxhdG9yc19ZbGV2ZWwxYS5yZGF0YSIpKSB7CiAgbG9hZCgia21fZW11bGF0b3JzX1lsZXZlbDFhLnJkYXRhIikKfSBlbHNlIHsKICAKICBlbWxpc3Rfa21fWWxldmVsMWEgPC0gdmVjdG9yKG1vZGUgPSAnbGlzdCcsIGxlbmd0aCA9IGxlbmd0aCh5X25hbWVzX3N1bSkpCiAgCiAgZm9yKGkgaW4gMTpsZW5ndGgoeV9uYW1lc19zdW0pKXsKICAgIHluYW1lIDwtIHlfbmFtZXNfc3VtW2ldCiAgICB5IDwtIFlfbGV2ZWwxYVssIHluYW1lXQogICAgCiAgICBlbSA8LSBrbSh+LiwgZGVzaWduID0gWF9sZXZlbDFhLCByZXNwb25zZSA9IHksIG11bHRpc3RhcnQgPSA0KQogICAgZW1saXN0X2ttX1lsZXZlbDFhW1tpXV0gPC0gZW0KICB9CiAgCiAgbG9vbGlzdF9rbV9ZbGV2ZWwxYSA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSkKICAKICBmb3IoaSBpbiAxOmxlbmd0aCh5X25hbWVzX3N1bSkpewogICAgCiAgICBsb28gPC0gbGVhdmVPbmVPdXQua20obW9kZWwgPSBlbWxpc3Rfa21fWWxldmVsMWFbW2ldXSwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQogICAgbG9vbGlzdF9rbV9ZbGV2ZWwxYVtbaV1dIDwtIGxvbwogIH0KICAKICBzYXZlKGVtbGlzdF9rbV9ZbGV2ZWwxYSxsb29saXN0X2ttX1lsZXZlbDFhLCBmaWxlID0gImttX2VtdWxhdG9yc19ZbGV2ZWwxYS5yZGF0YSIpCn0KCnN0b3BDbHVzdGVyKGNsKQoKCgpgYGAKCgpgYGB7cn0KCgpsb29zdGF0c19rbV9ZbGV2ZWwxYSA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSkKCmZvcihpIGluIDE6bGVuZ3RoKGVtbGlzdF9rbV9ZbGV2ZWwxYSkpewogIAogIGxvb3N0YXRzIDwtIGttTG9vU3RhdHMoZW1saXN0X2ttX1lsZXZlbDFhW1tpXV0pCiAgbG9vc3RhdHNfa21fWWxldmVsMWFbW2ldXSA8LSBsb29zdGF0cwogIHByaW50KGxvb3N0YXRzJHBtYWUpCn0KCmBgYAoKCgpgYGB7cn0KIyBsZXZlbDEgdnMgbGV2ZWwgMWEKa21fcG1hZV9sZXZlbDEgPC0gc2FwcGx5KGxvb3N0YXRzX2ttX1ksIGZ1bmN0aW9uKHgpIHgkcG1hZSkKa21fcG1hZV9sZXZlbDFhIDwtIHNhcHBseShsb29zdGF0c19rbV9ZbGV2ZWwxYSwgZnVuY3Rpb24oeCkgeCRwbWFlKQoKcGxvdChrbV9wbWFlX2xldmVsMSAsIGttX3BtYWVfbGV2ZWwxYSApCmFibGluZSgwLDEpCgpgYGAKCgoKCgoKCgoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gNiwgcmVzdWx0cyA9ICdoaWRlJ30KCmlmIChmaWxlLmV4aXN0cygia21fZW11bGF0b3JzX1lBbm9tLnJkYXRhIikpIHsKICBsb2FkKCJrbV9lbXVsYXRvcnNfWUFub20ucmRhdGEiKQp9IGVsc2UgewogIAogIGVtbGlzdF9rbV9ZQW5vbSA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSkKICAKICBmb3IoaSBpbiAxOmxlbmd0aCh5X25hbWVzX3N1bSkpewogICAgeW5hbWUgPC0geV9uYW1lc19zdW1baV0KICAgIHkgPC0gWUFub21fbGV2ZWwxWywgeW5hbWVdCiAgICAKICAgIGVtIDwtIGttKH4uLCBkZXNpZ24gPSBYX2xldmVsMSwgcmVzcG9uc2UgPSB5KQogICAgZW1saXN0X2ttX1lBbm9tW1tpXV0gPC0gZW0KICB9CiAgCiAgbG9vbGlzdF9rbV9ZQW5vbSA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSkKICAKICBmb3IoaSBpbiAxOmxlbmd0aCh5X25hbWVzX3N1bSkpewogICAgCiAgICBsb28gPC0gbGVhdmVPbmVPdXQua20obW9kZWwgPSBlbWxpc3Rfa21fWUFub21bW2ldXSwgdHlwZSA9ICdVSycsIHRyZW5kLnJlZXN0aW0gPSBUUlVFKQogICAgbG9vbGlzdF9rbV9ZQW5vbVtbaV1dIDwtIGxvbwogIH0KICAKICBzYXZlKGVtbGlzdF9rbV9ZQW5vbSxsb29saXN0X2ttX1lBbm9tLCBmaWxlID0gImttX2VtdWxhdG9yc19ZQW5vbS5yZGF0YSIpCn0KCmBgYAoKCiMga20gZW11bGF0b3JzIGZvciBjaGFuZ2UgaW4gdmFyaWFibGVzIG92ZXIgdGltZQoKYGBge3IsIGZpZy53aWR0aCA9IDEyLCBmaWcuaGVpZ2h0ID0gMTJ9CnBhcihtZnJvdyA9IGMoNCw0KSwgbWFyID0gYygzLDQsMiwyKSwgb21hID0gYyg0LDQsNCwwLjEpKQpmb3IoaSBpbiAxOmxlbmd0aChsb29saXN0X2ttX1lBbm9tKSl7CiAgCiAgeSA8LSBZQW5vbV9sZXZlbDFbLCB5X25hbWVzX3N1bVtpXV0KICBsb28gPC0gbG9vbGlzdF9rbV9ZQW5vbVtbaV1dCiAgeWxpbSA8LSByYW5nZShjKGxvbyRtZWFuIC0gKDIqbG9vJHNkKSwgbG9vJG1lYW4gKyAoMipsb28kc2QpKSApCiAgcGxvdCh5LCBsb28kbWVhbiwgeGxhYiA9ICcnLCB5bGFiID0gJycsIG1haW4gPSB5X25hbWVzX3N1bVtpXSAsIHlsaW0gPSB5bGltLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJywgNzApLAogICAgICAgcGNoID0gMTkpCiAgc2VnbWVudHMoeDAgPSB5LCB5MCA9IGxvbyRtZWFuIC0gKDIqbG9vJHNkKSAgLCB4MSA9IHkgLCB5MSA9IGxvbyRtZWFuICsgKDIqbG9vJHNkKSwgY29sID0gbWFrZVRyYW5zcGFyZW50KCdibGFjaycsIDcwKSkKICBhYmxpbmUoMCwxKQogIAoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ0NoYW5nZSBvdmVyIHRpbWUgKFlBbm9tKScsIHNpZGUgPSAzLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKCmBgYAoKTmV4dCwgcGxvdCB0aGUgdHdvc3RlcCBnbG1uZXQva20gZW11bGF0b3JzCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSA2fQojIFR3b3N0ZXAgZ2xtbmV0IGVtdWxhdG9ycwoKaWYgKGZpbGUuZXhpc3RzKCJ0c19lbXVsYXRvcnNfWS5yZGF0YSIpKSB7CiAgbG9hZCgidHNfZW11bGF0b3JzX1kucmRhdGEiKQp9IGVsc2UgewogIAogIGVtbGlzdF90d29TdGVwX2dsbW5ldF9ZIDwtIHZlY3Rvcihtb2RlID0gJ2xpc3QnLCBsZW5ndGggPSBsZW5ndGgoeV9uYW1lc19zdW0pKQogIAogIGZvcihpIGluIDE6bGVuZ3RoKHlfbmFtZXNfc3VtKSl7CiAgICAKICAgIHluYW1lIDwtIHlfbmFtZXNfc3VtW2ldCiAgICB5IDwtIFlfbGV2ZWwxWywgeW5hbWVdCiAgICAKICAgIGVtIDwtIHR3b1N0ZXBfZ2xtbmV0KFggPSBYX2xldmVsMSwgeSA9IHkpCiAgICBlbWxpc3RfdHdvU3RlcF9nbG1uZXRfWVtbaV1dIDwtIGVtCiAgfQogIAogIGxvb2xpc3RfdHdvU3RlcF9nbG1uZXRfWSA8LSB2ZWN0b3IobW9kZSA9ICdsaXN0JywgbGVuZ3RoID0gbGVuZ3RoKHlfbmFtZXNfc3VtKSkKICAKICBmb3IoaSBpbiAxOmxlbmd0aCh5X25hbWVzX3N1bSkpewogICAgCiAgICBsb28gPC0gbGVhdmVPbmVPdXQua20obW9kZWwgPSBlbWxpc3RfdHdvU3RlcF9nbG1uZXRfWVtbaV1dJGVtdWxhdG9yLCB0eXBlID0gJ1VLJywgdHJlbmQucmVlc3RpbSA9IFRSVUUpCiAgICBsb29saXN0X3R3b1N0ZXBfZ2xtbmV0X1lbW2ldXSA8LSBsb28KICB9CiAgCiAgCiAgCiAgc2F2ZShlbWxpc3RfdHdvU3RlcF9nbG1uZXRfWSwgbG9vbGlzdF90d29TdGVwX2dsbW5ldF9ZLCBmaWxlID0gInRzX2VtdWxhdG9yc19ZLnJkYXRhIikKCn0KCgpgYGAKCmBgYHtyfQojIENhbiBnZXQgbnVtZXJpY2FsIHBlcmZvcm1hbmNlIHVzaW5nCgpsb29zdGF0c190d29TdGVwX2dsbW5ldF9ZIDwtIHZlY3Rvcihtb2RlID0gJ2xpc3QnLCBsZW5ndGggPSBsZW5ndGgoeV9uYW1lc19zdW0pKQoKZm9yKGkgaW4gMTpsZW5ndGgoZW1saXN0X3R3b1N0ZXBfZ2xtbmV0X1kpKXsKICAKICBsb29zdGF0cyA8LSBrbUxvb1N0YXRzKGVtbGlzdF90d29TdGVwX2dsbW5ldF9ZW1tpXV0kZW11bGF0b3IpCiAgbG9vc3RhdHNfdHdvU3RlcF9nbG1uZXRfWVtbaV1dIDwtIGxvb3N0YXRzCiAgcHJpbnQobG9vc3RhdHMkcG1hZSkKfQoKCmBgYAoKCgojIyBUd29TdGVwIGVtdWxhdG9yIHBlcmZvcm1hbmNlIGZvciBtb2Rlcm4gdmFsdWVzCgpgYGB7ciwgZmlnLndpZHRoID0gMTIsIGZpZy5oZWlnaHQgPSAxMn0KcGFyKG1mcm93ID0gYyg0LDQpLCBtYXIgPSBjKDMsNCwyLDIpLCBvbWEgPSBjKDQsNCw0LDAuMSkpCmZvcihpIGluIDE6bGVuZ3RoKGxvb2xpc3RfdHdvU3RlcF9nbG1uZXRfWSkpewogIAogIHkgPC0gWV9sZXZlbDFbLCB5X25hbWVzX3N1bVtpXV0KICBsb28gPC0gbG9vbGlzdF90d29TdGVwX2dsbW5ldF9ZW1tpXV0KICB5bGltIDwtIHJhbmdlKGMobG9vJG1lYW4gLSAoMipsb28kc2QpLCBsb28kbWVhbiArICgyKmxvbyRzZCkpICkKICBwbG90KHksIGxvbyRtZWFuLCB4bGFiID0gJycsIHlsYWIgPSAnJywgbWFpbiA9IHlfbmFtZXNfc3VtW2ldICwgeWxpbSA9IHlsaW0sIGNvbCA9IG1ha2VUcmFuc3BhcmVudCgnYmxhY2snLCA3MCksCiAgICAgICBwY2ggPSAxOSkKICBzZWdtZW50cyh4MCA9IHksIHkwID0gbG9vJG1lYW4gLSAoMipsb28kc2QpICAsIHgxID0geSAsIHkxID0gbG9vJG1lYW4gKyAoMipsb28kc2QpLCBjb2wgPSBtYWtlVHJhbnNwYXJlbnQoJ2JsYWNrJywgNzApKQogIGFibGluZSgwLDEpCiAgbGVnZW5kKCdib3R0b21yaWdodCcsbGVnZW5kID0gcGFzdGUoJ21hZSA9Jyxyb3VuZChsb29zdGF0c190d29TdGVwX2dsbW5ldF9ZW1tpXV0kcG1hZSwyKSwnJScpICwgYnR5ID0gJ24nKQoKfQoKbXRleHQoJ0FjdHVhbCcsIHNpZGUgPSAxLCBsaW5lID0gMSwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyICkKbXRleHQoJ1ByZWRpY3RlZCcsIHNpZGUgPSAyLCBsaW5lID0gMCwgb3V0ZXIgPSBUUlVFLCBjZXggPSAyKSAKbXRleHQoJ01vZGVybiBWYWx1ZSAoWSknLCBzaWRlID0gMywgbGluZSA9IDAsIG91dGVyID0gVFJVRSwgY2V4ID0gMikgCgpgYGAKCiMgQ29tcGFyZSBMZWF2ZS1vbmUtb3V0IHN0YXRpc3RpY3MgZm9yIHRoZSB0d28gdHlwZXMgb2YgZW11bGF0b3IKCldlIHVzZSB0aGUgbGVhdmUtb25lLW91dCBNZWFuIEFic29sdXRlIEVycm9yLCBleHByZXNzZWQgYXMgYSBwZXJjZW50YWdlIG9mIHRoZSByYW5nZSBvZiB0aGUgb3V0cHV0IGFjcm9zcyB0aGUgZW5zZW1ibGUuIFdlIGZpbmQgdGhhdCB0aGUgdHdvc3RlcCBlbXVsYXRvcmlzbid0IHNpZ25pZmljYW50bHkgbW9yZSBhY2N1cmF0ZSwgYW5kIGlzIGluZGVlZCAqbGVzcyogYWNjdXJhdGUgZm9yIHRyZWUgZnJhY3Rpb24uCgpgYGB7ciwgZmlnLndpZHRoID0gOCwgZmlnLmhlaWdodCA9IDd9CgoKa21fcG1hZSA8LSBzYXBwbHkobG9vc3RhdHNfa21fWSwgJ1tbJywgJ3BtYWUnKQp0c19wbWFlIDwtIHNhcHBseShsb29zdGF0c190d29TdGVwX2dsbW5ldF9ZLCAnW1snLCAncG1hZScpCgpwYXIobWFyID0gYygxMiw0LDIsMSksIGxhcyA9MSApCnBsb3QoMTpsZW5ndGgoeV9uYW1lc19zdW0pLCBrbV9wbWFlLAogICAgIHlsaW0gPSBjKDAsMTUpLCBwY2ggPSAxOSwKICAgICBheGVzID0gRkFMU0UsIHhsYWIgPSAnJywgCiAgICAgeWxhYiA9ICdMT08gTUFFICglIG9mIHJhbmdlKScsCiAgICAgY2V4ID0gMS4yKQpwb2ludHMoMTpsZW5ndGgoeV9uYW1lc19zdW0pLHRzX3BtYWUgLCBjb2w9ICdyZWQnLCBwY2ggPSAxOSwgY2V4ID0gMS4yKQpsZWdlbmQoJ3RvcGxlZnQnLCBjKCdrbSBlbXVsYXRvcicsICd0d29TdGVwIGVtdWxhdG9yJyksIHBjaCA9IDE5LCBjb2wgPSBjKCdibGFjaycsICdyZWQnKSwgcHQuY2V4ID0gMS4yKQpheGlzICgyKQpwYXIobGFzID0gMikKYXhpcygxLCBhdCA9IDE6bGVuZ3RoKHlfbmFtZXNfc3VtKSwgbGFiZWxzID0geV9uYW1lc19zdW0pCgpgYGAKCgoK