Soil phosphorous availability limits the contribution of small, individual grassland remnants to the conservation of landscape-scale biodiversity

Data analyses for Plue & Baeten (2021) Applied Vegetation Science

Here we provide code to reproduce the figures that appear in the main text of the study where we question under which conditions small remnant grasslands may exhibit the largest conservation value.

library(tidyverse)
library(nlme)
library(vegan)
library(ggExtra)
library(gridExtra)
library(Hmsc)

# function to trim lme outputs
trim <- function(model){
  round(summary(model)$tTable, 3)
}

Data preparation

Read the data from different .csv files and create some new variables that will be used in the analyses. Brief descriptions of the files and variables:

  • Soil data: soil.csv
    • P-Olsen (mg/kg), pH, N (%), and C (%)
  • Plant community data: vegdat.csv
    • abundance (class) of each species; note that for the analyses these were transformed to presence/absence data
  • Plot-level descriptors or meta-data: meta.csv
    • landscape name (Landscape)
    • grassland type (Type; MI = Midfield islet = remnant grassland, SNG = semi-natural grassland),
    • size of an MI as a class (Size; S = small, B = big) and as an actual size (Area; m²)
    • distance of an MI to the SNG as a class (Distance; 1 = closest to SNG, 3 = far from SNG) and as an actual distance (DistCont; km)
    • position of the plot in an MI (M = mid of the islet, E = towards the edge)
  • Plant traits: planttraits.csv
    • grassland specialists are species that decline after 5 or 10 years when grassland management stops (specialist_5 and specialist_10)
    • non-specialists are generalist or forest species
soil <- read.csv("data/soil.csv", header = T) %>%
  rename(P = P_Olsen_mg.kg,
         pH = pH_H2O,
         N = N_percentage,
         C = C_percentage)

vegdat <- read.csv("data/vegdat.csv", header = T, colClasses = "character") %>%
  gather(species, cover, `Achillea.millefolium`:`Viola.sp.`) %>%
  mutate(plot = as.integer(plot)) %>%
  mutate(pres = case_when(
    cover == "R" ~ 1,
    cover == "O" ~ 1,
    cover == "F" ~ 1,
    cover == "C" ~ 1,
    cover == "A" ~ 1,
    cover == "" ~ 0)
  ) %>%
  select(-cover)

meta <- read.csv("data/plot_metadata.csv", header = T)

TR <- read.csv("data/planttraits.csv", header = T) %>%
  mutate(species = gsub(" ", ".", species)) %>%
  mutate(specialist = specialist_5 + specialist_10) %>%
  mutate(specialist_name =ifelse(specialist == 1, "grassland specialists", "non-specialists"))

Create a single dataset by joining the soil, vegetation, meta, and trait data. Then create an ID variable that identifies each patch, combining landscape name etc… One plot (ID = 161) showed extreme C percentage values, most probably due to wrong sampling in the field. This plot was removed before the analyses.

dat <- vegdat %>%
  left_join(soil) %>%
  left_join(meta) %>%
  left_join(TR) %>%
  mutate(ID = case_when(
    Type == "MI" ~ paste(Landscape, Type, Size, Distance),
    Type == "SNG" ~ paste(Landscape, Type))) %>%
  mutate(Type = factor(Type, levels = c("SNG", "MI"))) %>% 
  filter(plot != 161)

head(dat)

Landscape level richness patterns

This is the analysis for research question 1 and Figure 2.

First we calculate the habitat amount for remnant grasslands and semi-natural grasslands in every landscape.

habitat_amount <- meta %>%
  group_by(Landscape, Type) %>%
  summarize(sum_area = sum(Area)) %>%
  # MI are counted twice this way (two plots per patch)
  # SNG are counted six times (same area around each plot)
  mutate(amount = ifelse(Type == "MI", sum_area/2, sum_area/6) / 10000)

Calculate and test for differences in mean habitat amount between grassland types.

habitat_amount %>% 
  group_by(Type) %>% 
  summarize(mn = round(mean(amount), 1))

trim(lme(log(amount) ~ Type, random=~ 1 | Landscape, data = habitat_amount))
            Value Std.Error DF t-value p-value
(Intercept) 0.055     0.265  8   0.206   0.842
TypeSNG     1.905     0.364  8   5.238   0.001

To account for the difference in the number of vegetation plots sampled in the semi-natural grasslands (n = 6 per landscape) and remnant grasslands (n = 12 per landscape), the species richness of the remnant grasslands needed to be rescaled. We calculated the average number of species across six plots, through sample-based rarefaction (n = 100 permutations).

We run the same analyses on (1) all species, (2) the subset of specialists, and (3) the subset of non-specialists. To avoid code replication, we first create two helperfunctions: one for rarefaction and one for creating plots.

# rarefaction function
f_gamma <- function(vegdata){
  # species accumulation curves
  spec_acc <- vegdata %>%
    group_map( ~ specaccum(comm = .x, method = "random", permutations = 100))
  
  # extract mean richness across 6 plots
  gamma <- c()
  for(i in 1:18){
    gamma[i] <- spec_acc[[i]]$richness[6]
  }
  
  # only 5 SNG plots in Nyckleby
  gamma[13] <- spec_acc[[13]]$richness[5]
  
  # combine into data frame
  data.frame(gamma, group_keys(vegdata)) %>%
    left_join(habitat_amount)
}

# plot of landscape level richness
f_plot_gamma <- function(gamma_out, title){
  landscape_SR <- ggplot(gamma_out, aes(x = amount, y = gamma)) + 
    geom_point(aes(color = Type), size = 7, alpha = 0.5) +
    scale_color_manual("", labels = c("remnant grasslands", "semi-natural grasslands"),
                       values = c("#E7B800", "#FC4E07")) +
    scale_x_log10("Habitat amount (ha)") +
    scale_y_continuous("Species richness", limits = c(0,80)) +
    theme_bw() +
    theme(legend.position = "none") +
    ggtitle(title)
  
  ggMarginal(landscape_SR, groupColour = T, groupFill = T, margins = "y", bw = 5)
}
  1. Analysis on the full dataset, i.e. including all species

Calculate richness

vegdat_all <- dat %>%
  select(plot, Landscape, Type, species, pres) %>%
  spread(species, pres) %>%
  select(-plot) %>%
  group_by(Landscape, Type)

gamma_out_all <- f_gamma(vegdat_all)

Plot richness against habitat amount

gamma_plot_all <- f_plot_gamma(gamma_out_all, "a) all species")

Test differences in richness between grassland types, either accounting or not accounting for habitat amount.

trim(lme(gamma ~ scale(log(amount)) * Type, random=~ 1 | Landscape, data = gamma_out_all))
                            Value Std.Error DF t-value p-value
(Intercept)                49.991     7.194  8   6.949   0.000
scale(log(amount))          7.888     8.318  6   0.948   0.380
TypeSNG                    -7.514     7.221  6  -1.041   0.338
scale(log(amount)):TypeSNG  2.979     8.561  6   0.348   0.740
trim(lme(gamma ~ Type, random=~ 1 | Landscape, data = gamma_out_all))
             Value Std.Error DF t-value p-value
(Intercept) 43.966     3.759  8  11.696   0.000
TypeSNG      6.812     3.886  8   1.753   0.118
  1. Analysis on the subset of specialist species

Calculate richness

vegdat_spec <- dat %>%
  filter(specialist_name == "grassland specialists") %>%
  select(plot, Landscape, Type, species, pres) %>%
  spread(species, pres) %>%
  select(-plot) %>%
  group_by(Landscape, Type)

gamma_out_spec <- f_gamma(vegdat_spec)

Plot richness against habitat amount

gamma_plot_spec <- f_plot_gamma(gamma_out_spec, "b) grassland specialists")

Test differences in richness between grassland types, either accounting or not accounting for habitat amount.

trim(lme(gamma ~ scale(log(amount)) * Type, random=~ 1 | Landscape, data = gamma_out_spec))
                            Value Std.Error DF t-value p-value
(Intercept)                15.471     4.899  8   3.158   0.013
scale(log(amount))         -0.240     5.935  6  -0.040   0.969
TypeSNG                     6.106     5.164  6   1.182   0.282
scale(log(amount)):TypeSNG  5.303     6.149  6   0.862   0.422
trim(lme(gamma ~ Type, random=~ 1 | Landscape, data = gamma_out_spec))
             Value Std.Error DF t-value p-value
(Intercept) 15.654     2.015  8   7.769   0.000
TypeSNG      9.790     2.217  8   4.416   0.002
  1. Analysis on the subset of non-specialist species

Calculate richness

vegdat_nonspec <- dat %>%
  filter(specialist_name == "non-specialists") %>%
  select(plot, Landscape, Type, species, pres) %>%
  spread(species, pres) %>%
  select(-plot) %>%
  group_by(Landscape, Type)

gamma_out_nonspec <- f_gamma(vegdat_nonspec)

Plot richness against habitat amount

gamma_plot_nonspec <- f_plot_gamma(gamma_out_nonspec, "c) non-specialists")

Test differences in richness between grassland types, either accounting or not accounting for habitat amount.

trim(lme(gamma ~ scale(log(amount)) * Type, random=~ 1 | Landscape, data = gamma_out_nonspec))
                             Value Std.Error DF t-value p-value
(Intercept)                 31.366     4.835  8   6.487   0.000
scale(log(amount))           4.089     5.913  6   0.692   0.515
TypeSNG                    -10.318     5.155  6  -2.001   0.092
scale(log(amount)):TypeSNG   1.085     6.147  6   0.177   0.866
trim(lme(gamma ~ Type, random=~ 1 | Landscape, data = gamma_out_nonspec))
             Value Std.Error DF t-value p-value
(Intercept) 28.242     1.922  8  14.696   0.000
TypeSNG     -3.242     2.332  8  -1.390   0.202

Finally, we combine the plots for the three subsets of data into one multi-panel graph.

grid.arrange(gamma_plot_all, gamma_plot_spec, gamma_plot_nonspec, nrow = 1)

Plot-level analyses

Species richness patterns

This is the analysis for research question 2 and Figure 3.

Calculate the mean species richness in the two grassland types.

dat %>%
  group_by(plot, Type) %>%
  summarize(SR = sum(pres)) %>%
  group_by(Type) %>%
  summarize(mn_SR = round(mean(SR), 1))

We run the same analyses on (1) all species, (2) the subset of specialists, and (3) the subset of non-specialists. To avoid code replication, we first create two helperfunctions: one for making predictions from the statistical model and one for creating plots.

# predict along soil P gradient
f_predict <- function(model){
  
  # create new dataset with gradient in P
  newdat <- expand.grid(Type = c("MI","SNG"), 
                      pH_c = 0, 
                      CN_c = 0, 
                      P_c = seq(-1.2, 2.2, 0.1), 
                      fit = NA)
  
  # predictions and 95% CI's
  newdat$fit <- predict(model, newdata = newdat, level = 0)

  des <- model.matrix(formula(model)[-2], newdat)
  predvar <- diag( des %*% vcov(model) %*% t(des) )

  newdat$lower = with(newdat, fit - 2*sqrt(predvar) )
  newdat$upper = with(newdat, fit + 2*sqrt(predvar) )

  # transform predictions to 'response' scale
  # back-transform standardized log(P) to original soil P
  # mean log(P) concentrations was 3.055
  mn_log_P_resp <- 3.055

  newdat %>%
    mutate(fit_resp = exp(fit),
           lower_resp = exp(lower),
           upper_resp = exp(upper),
           P_resp = exp(P_c) * exp(mn_log_P_resp)
  )
}


# plot of plot-level richness
f_plot_SR <- function(SR_dat, SR_out, title){
  ggplot(SR_out, aes(P_resp, fit_resp, col = Type)) +
    geom_line(size = 1.5) +
    geom_ribbon(aes(y = NULL, ymin = lower_resp, ymax = upper_resp), linetype = 0, alpha = 0.15) +
    geom_line(aes(P_resp, lower_resp), linetype = 2, alpha = 0.5) +
    geom_line(aes(P_resp, upper_resp), linetype = 2, alpha = 0.5) +
    geom_point(data = SR_dat, aes(P, SR+1), alpha = 0.5) +
    ggtitle(title) +
    theme_bw() +
    theme(legend.position = "none") +
    scale_x_log10("P concentration (mg/kg)") +
    scale_y_continuous("Species richness", trans = "log", breaks = c(3, 10, 30), limits = c(1,40)) +
    scale_color_manual(values = c("#E7B800", "#FC4E07"))  
}
  1. Analysis on the full dataset, i.e. including all species

Fit model

dat_SR_all <- dat %>%
  mutate(CN_c = C/N -  mean(C/N),
         P_c = log(P) - mean(log(P)),
         pH_c = pH - mean(pH, na.rm = T)) %>%
  group_by(plot, ID, Landscape, P, P_c, pH_c, CN_c, Type) %>%
  summarize(SR = sum(pres))

m_SR_all <- lme(log(SR+1) ~ 1 + Type * (P_c + pH_c + CN_c), 
                random=~ 1 | Landscape/ID, data = dat_SR_all)
trim(m_SR_all)
             Value Std.Error DF t-value p-value
(Intercept)  3.156     0.100 92  31.648   0.000
TypeMI      -0.395     0.093 53  -4.239   0.000
P_c         -0.057     0.060 92  -0.941   0.349
pH_c         0.109     0.196 92   0.556   0.580
CN_c         0.023     0.031 92   0.737   0.463
TypeMI:P_c  -0.155     0.074 92  -2.100   0.038
TypeMI:pH_c -0.093     0.215 92  -0.432   0.667
TypeMI:CN_c  0.016     0.037 92   0.439   0.662

Predictions and plot

pred_all <- f_predict(m_SR_all)
SR_plot_all <- f_plot_SR(dat_SR_all, pred_all, "a) all species")
  1. Analysis on the subset of specialist species

Fit model.

dat_SR_spec <- dat %>%
  filter(specialist_name == "grassland specialists") %>%
  mutate(CN_c = C/N -  mean(C/N),
         P_c = log(P) - mean(log(P)),
         pH_c = pH - mean(pH, na.rm = T)) %>%
  group_by(plot, ID, Landscape, P, P_c, pH_c, CN_c, Type) %>%
  summarize(SR = sum(pres))

m_SR_spec <- lme(log(SR+1) ~ 1 + Type * (P_c + pH_c + CN_c), 
                random=~ 1 | Landscape/ID, data = dat_SR_spec)
trim(m_SR_spec)
             Value Std.Error DF t-value p-value
(Intercept)  2.606     0.129 92  20.140   0.000
TypeMI      -0.997     0.136 53  -7.325   0.000
P_c         -0.043     0.102 92  -0.418   0.677
pH_c         0.344     0.335 92   1.029   0.306
CN_c         0.073     0.054 92   1.343   0.183
TypeMI:P_c  -0.397     0.122 92  -3.258   0.002
TypeMI:pH_c -0.361     0.362 92  -0.998   0.321
TypeMI:CN_c  0.068     0.063 92   1.074   0.286

Predictions and plot.

pred_spec <- f_predict(m_SR_spec)
SR_plot_spec <- f_plot_SR(dat_SR_spec, pred_spec, "b) grassland specialists")
  1. Analysis on the subset of non-specialist species

Fit model.

dat_SR_nonspec <- dat %>%
  filter(specialist_name == "non-specialists") %>%
  mutate(CN_c = C/N -  mean(C/N),
         P_c = log(P) - mean(log(P)),
         pH_c = pH - mean(pH, na.rm = T)) %>%
  group_by(plot, ID, Landscape, P, P_c, pH_c, CN_c, Type) %>%
  summarize(SR = sum(pres))

m_SR_nonspec <- lme(log(SR+1) ~ 1 + Type * (P_c + pH_c + CN_c), 
                random=~ 1 | Landscape/ID, data = dat_SR_nonspec)
trim(m_SR_nonspec)
             Value Std.Error DF t-value p-value
(Intercept)  2.353     0.091 92  25.968   0.000
TypeMI       0.083     0.083 53   0.995   0.324
P_c         -0.120     0.063 92  -1.914   0.059
pH_c        -0.197     0.205 92  -0.958   0.341
CN_c        -0.029     0.033 92  -0.885   0.378
TypeMI:P_c  -0.023     0.075 92  -0.308   0.759
TypeMI:pH_c  0.215     0.222 92   0.972   0.334
TypeMI:CN_c  0.039     0.039 92   0.996   0.322

Predictions and plot.

pred_nonspec <- f_predict(m_SR_nonspec)
SR_plot_nonspec <- f_plot_SR(dat_SR_nonspec, pred_nonspec, "c) non-specialists")

Finally, we combine the plots for the three subsets of data into one multi-panel graph.

grid.arrange(SR_plot_all, SR_plot_spec, SR_plot_nonspec, nrow = 1)

Community composition patterns

This is the analysis for research question 3 and Figure 4.

First we prepare the data input for the species community model.

# select multivariate species data
Y_dat <- dat %>%
  select(plot, species, pres) %>%
  spread(species, pres) %>%
  select(-plot)

Y_dat_freq <- Y_dat[, colSums(Y_dat) > 2]

Y <- data.matrix(Y_dat_freq)

# environmental predictors
env <- dat %>%
  select(plot, Type, P, pH, N, C) %>%
  mutate(CN_c = C/N -  mean(C/N),
         P_c = log(P) - mean(log(P)),
         pH_c = pH - mean(pH, na.rm = T)) %>%
  group_by(plot, Type) %>%
  summarize(P = mean(P_c),
            CN = mean(CN_c),
            pH = mean(pH_c))

XData <- env %>%
  group_by() %>%
  select(-plot)

XData <- as.data.frame(XData)

# plant traits
TrData <- dat %>%
  filter(species %in% colnames(Y)) %>%
  select(species, specialist_name) %>%
  distinct() %>%
  column_to_rownames("species")

# spatio-temporal context
studyDesign <- data.frame(patch = factor(dat$ID[1:161]), landscape = factor(dat$Landscape[1:161]))
rL <- HmscRandomLevel(units = studyDesign$pach)
rL2 <- HmscRandomLevel(units = studyDesign$landscape)

rm(Y_dat, Y_dat_freq, env)

Code to set-up and run the HMSC model. Note that it takes few hours of sampling. So you only want to do this once and save the output. This way, you could reload the model output whenever needed. This was also done here.

m <- Hmsc(Y = Y, 
          XData = XData, XFormula = ~ Type * (P + pH + CN), 
          TrData = TrData, TrFormula = ~ specialist_name,
          studyDesign = studyDesign, 
          ranLevels = list(patch = rL, landscape = rL2), distr = "probit")

# only run next lines when you want to resample the model
# m <- sampleMcmc(m, thin = 10, samples = 1000, transient = 500, nChains = 2, verbose = 500)
# save(m, "output/hmsc_out.RData")

load("output/hmsc_out.RData")

To make predictions from this model, we first construct a gradient in soil P, while keeping the other predictors at a constant value.

Gradient_MI = constructGradient(m,focalVariable = "P",
                                non.focalVariables = list("Type" = list(3, "MI"),
                                                          "pH" = list(3, 0.38),
                                                          "CN" = list(3, 0)))

Gradient_SNG = constructGradient(m,focalVariable = "P",
                                 non.focalVariables = list("Type" = list(3, "SNG"),
                                                           "pH" = list(3, 0.38),
                                                           "CN" = list(3, 0)))

Make predictions. Then, wrangle the output for creating a plot.

predY_MI = predict(m, XData=Gradient_MI$XDataNew, studyDesign=Gradient_MI$studyDesignNew,
                   ranLevels=Gradient_MI$rLNew, expected=TRUE)

predY_SNG = predict(m, XData=Gradient_SNG$XDataNew, studyDesign=Gradient_SNG$studyDesignNew,
                    ranLevels=Gradient_SNG$rLNew, expected=TRUE)

predY_MI_bind <- abind::abind(predY_MI, along = 3)
qpredY_MI <- apply(predY_MI_bind, c(1, 2), quantile, probs = c(.5), na.rm = TRUE) %>%
  as.data.frame() %>%
  mutate(P = Gradient_MI$XDataNew$P,
         type = "MI") %>%
  gather(species, pred, 1:115) %>%
  left_join(TR) %>%
  select(P, type, species, pred, specialist_name)

predY_SNG_bind <- abind::abind(predY_SNG, along = 3)
qpredY_SNG <- apply(predY_SNG_bind, c(1, 2), quantile, probs = c(.5), na.rm = TRUE) %>%
  as.data.frame() %>%
  mutate(P = Gradient_SNG$XDataNew$P,
         type = "SNG") %>%
  gather(species, pred, 1:115) %>%
  left_join(TR) %>%
  select(P, type, species, pred, specialist_name)

qpredY <- rbind(qpredY_MI, qpredY_SNG)

qpredY_summary <- qpredY %>%
  group_by(P, specialist_name, type) %>%
  summarize(mn_pred = mean(pred))

Back-transform standardised log(P) to original soil P and create the plot.

# Back-transform standardized P values
mn_log_P_resp <- mean(log(dat$P))
qpredY$P_resp <- exp(qpredY$P) * exp(mn_log_P_resp)
qpredY_summary$P_resp <- exp(qpredY_summary$P) * exp(mn_log_P_resp)

type_labs <- c("remnant grasslands", "semi-natural grasslands")
names(type_labs) <- c("MI", "SNG")

ggplot(qpredY, aes(P_resp, pred, col = type)) +
  geom_line(aes(group = species)) +
  geom_line(data = qpredY_summary, aes(P_resp, mn_pred), size = 1.1, colour = "black") +
  facet_grid(type ~ specialist_name, labeller = labeller(type = type_labs)) +
  theme_bw() +
  theme(legend.position = "none") +
  scale_x_log10("P concentration (mg/kg)") +
  scale_y_continuous("Probability of species occurrence") +
  scale_color_manual("", labels = c("remnant grasslands", "semi-natural grasslands"), values = c("#E7B800", "#FC4E07"))

Variance partitioning

Analysis for research question 4 and Figure 5.

Using the output from the species community model, we can calculate the relative importance of the different predictors. We do this for the two grassland types separately.

  1. semi-natural grasslands
VP_SNG = computeVariancePartitioning(m, group = c(1,0,2,2,2), groupnames=c("type","abiotic"))

variances_SNG <- data.frame(factor = rownames(VP_SNG$vals), VP_SNG$vals, row.names = NULL) %>%
  tidyr::gather(species, value, 2:116) %>%
  left_join(TR) %>%
  mutate(factor = factor(factor, levels = c("type", "abiotic", "Random: landscape","Random: patch"),
                         labels = c("type", "abtioic", "landscape","patch")))

variances_summary_SNG <- variances_SNG %>%
  group_by(specialist, factor) %>%
  summarize(n = n_distinct(species),
            mn_value = mean(value, na.rm = T),
            se_value = sd(value, na.rm = T)/sqrt(n))

VARS_graph_a <- ggplot(variances_summary_SNG, aes(factor(specialist), mn_value, col = factor)) +
  geom_point(position = position_dodge(width = 0.4)) +
  geom_errorbar(aes(ymin = mn_value - 2 * se_value, ymax = mn_value + 2 * se_value), position = position_dodge(width = 0.4), width = 0.5) +
  theme_bw() +
  scale_x_discrete("", labels = c("non-specialists","grassland specialists")) +
  scale_y_continuous("Variance proportion", limits = c(0, 0.8)) +
  scale_colour_discrete("") +
  ggtitle("a) semi-natural grasslands") +
  theme(legend.position = "none")
  1. remnant grasslands
VP_MI = computeVariancePartitioning(m, group = c(1,1,2,2,2,2,2,2), groupnames=c("type","abiotic"))

variances_MI <- data.frame(factor = rownames(VP_MI$vals), VP_MI$vals, row.names = NULL) %>%
  tidyr::gather(species, value, 2:116) %>%
  left_join(TR) %>%
  mutate(factor = factor(factor, levels = c("type", "abiotic", "Random: landscape","Random: patch"),
                         labels = c("type", "abtioic", "landscape","patch")))

variances_summary_MI <- variances_MI %>%
  group_by(specialist, factor) %>%
  summarize(n = n_distinct(species),
            mn_value = mean(value, na.rm = T),
            se_value = sd(value, na.rm = T)/sqrt(n))

VARS_graph_b <- ggplot(variances_summary_MI, aes(factor(specialist), mn_value, col = factor)) +
  geom_point(position = position_dodge(width = 0.4)) +
  geom_errorbar(aes(ymin = mn_value - 2 * se_value, ymax = mn_value + 2 * se_value), position = position_dodge(width = 0.4), width = 0.5) +
  theme_bw() +
  scale_x_discrete("", labels = c("non-specialists","grassland specialists")) +
  scale_y_continuous("", limits = c(0, 0.8)) +
  scale_colour_discrete("") +
  ggtitle("b) midfield islets") +
  theme(legend.margin = margin(.9, .9, .9, .9), legend.position = c(0.8, 0.82))

Arrange the two graphs into a multipanel figure.

grid.arrange(VARS_graph_a, VARS_graph_b, nrow = 1)

LS0tDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCi0tLQ0KDQojIyBTb2lsIHBob3NwaG9yb3VzIGF2YWlsYWJpbGl0eSBsaW1pdHMgdGhlIGNvbnRyaWJ1dGlvbiBvZiBzbWFsbCwgaW5kaXZpZHVhbCBncmFzc2xhbmQgcmVtbmFudHMgdG8gdGhlIGNvbnNlcnZhdGlvbiBvZiBsYW5kc2NhcGUtc2NhbGUgYmlvZGl2ZXJzaXR5DQogDQoqRGF0YSBhbmFseXNlcyBmb3IgUGx1ZSAmIEJhZXRlbiAoMjAyMSkgQXBwbGllZCBWZWdldGF0aW9uIFNjaWVuY2UqDQpcDQpcDQpIZXJlIHdlIHByb3ZpZGUgY29kZSB0byByZXByb2R1Y2UgdGhlIGZpZ3VyZXMgdGhhdCBhcHBlYXIgaW4gdGhlIG1haW4gdGV4dCBvZiB0aGUgc3R1ZHkgd2hlcmUgd2UgcXVlc3Rpb24gdW5kZXIgd2hpY2ggY29uZGl0aW9ucyBzbWFsbCByZW1uYW50IGdyYXNzbGFuZHMgbWF5IGV4aGliaXQgdGhlIGxhcmdlc3QgY29uc2VydmF0aW9uIHZhbHVlLg0KYGBge3IgbWVzc2FnZSAgPSBGLCBlcnJvciA9IEZ9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobmxtZSkNCmxpYnJhcnkodmVnYW4pDQpsaWJyYXJ5KGdnRXh0cmEpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkoSG1zYykNCg0KIyBmdW5jdGlvbiB0byB0cmltIGxtZSBvdXRwdXRzDQp0cmltIDwtIGZ1bmN0aW9uKG1vZGVsKXsNCiAgcm91bmQoc3VtbWFyeShtb2RlbCkkdFRhYmxlLCAzKQ0KfQ0KYGBgDQoNCiMjIyBEYXRhIHByZXBhcmF0aW9uDQoNClJlYWQgdGhlIGRhdGEgZnJvbSBkaWZmZXJlbnQgLmNzdiBmaWxlcyBhbmQgY3JlYXRlIHNvbWUgbmV3IHZhcmlhYmxlcyB0aGF0IHdpbGwgYmUgdXNlZCBpbiB0aGUgYW5hbHlzZXMuIEJyaWVmIGRlc2NyaXB0aW9ucyBvZiB0aGUgZmlsZXMgYW5kIHZhcmlhYmxlczoNCg0KLSAqKlNvaWwqKiBkYXRhOiBgc29pbC5jc3ZgDQogICAgLSBQLU9sc2VuIChtZy9rZyksIHBILCBOICglKSwgYW5kIEMgKCUpDQotICoqUGxhbnQgY29tbXVuaXR5KiogZGF0YTogYHZlZ2RhdC5jc3ZgIA0KICAgIC0gYWJ1bmRhbmNlIChjbGFzcykgb2YgZWFjaCBzcGVjaWVzOyBub3RlIHRoYXQgZm9yIHRoZSBhbmFseXNlcyB0aGVzZSB3ZXJlIHRyYW5zZm9ybWVkIHRvIHByZXNlbmNlL2Fic2VuY2UgZGF0YQ0KLSAqKlBsb3QtbGV2ZWwgZGVzY3JpcHRvcnMqKiBvciBtZXRhLWRhdGE6IGBtZXRhLmNzdmAgDQogICAgLSBsYW5kc2NhcGUgbmFtZSAoTGFuZHNjYXBlKQ0KICAgIC0gZ3Jhc3NsYW5kIHR5cGUgKFR5cGU7IE1JID0gTWlkZmllbGQgaXNsZXQgPSByZW1uYW50IGdyYXNzbGFuZCwgU05HID0gc2VtaS1uYXR1cmFsIGdyYXNzbGFuZCksIA0KICAgIC0gc2l6ZSBvZiBhbiBNSSBhcyBhIGNsYXNzIChTaXplOyBTID0gc21hbGwsIEIgPSBiaWcpIGFuZCBhcyBhbiBhY3R1YWwgc2l6ZSAoQXJlYTsgbcKyKQ0KICAgIC0gZGlzdGFuY2Ugb2YgYW4gTUkgdG8gdGhlIFNORyBhcyBhIGNsYXNzIChEaXN0YW5jZTsgMSA9IGNsb3Nlc3QgdG8gU05HLCAzID0gZmFyIGZyb20gU05HKSBhbmQgYXMgYW4gYWN0dWFsIGRpc3RhbmNlIChEaXN0Q29udDsga20pDQogICAgLSBwb3NpdGlvbiBvZiB0aGUgcGxvdCBpbiBhbiBNSSAoTSA9IG1pZCBvZiB0aGUgaXNsZXQsIEUgPSB0b3dhcmRzIHRoZSBlZGdlKQ0KLSAqKlBsYW50IHRyYWl0cyoqOiBgcGxhbnR0cmFpdHMuY3N2YA0KICAgIC0gZ3Jhc3NsYW5kIHNwZWNpYWxpc3RzIGFyZSBzcGVjaWVzIHRoYXQgZGVjbGluZSBhZnRlciA1IG9yIDEwIHllYXJzIHdoZW4gZ3Jhc3NsYW5kIG1hbmFnZW1lbnQgc3RvcHMgKHNwZWNpYWxpc3RfNSBhbmQgc3BlY2lhbGlzdF8xMCkNCiAgICAtIG5vbi1zcGVjaWFsaXN0cyBhcmUgZ2VuZXJhbGlzdCBvciBmb3Jlc3Qgc3BlY2llcw0KYGBge3J9DQpzb2lsIDwtIHJlYWQuY3N2KCJkYXRhL3NvaWwuY3N2IiwgaGVhZGVyID0gVCkgJT4lDQogIHJlbmFtZShQID0gUF9PbHNlbl9tZy5rZywNCiAgICAgICAgIHBIID0gcEhfSDJPLA0KICAgICAgICAgTiA9IE5fcGVyY2VudGFnZSwNCiAgICAgICAgIEMgPSBDX3BlcmNlbnRhZ2UpDQoNCnZlZ2RhdCA8LSByZWFkLmNzdigiZGF0YS92ZWdkYXQuY3N2IiwgaGVhZGVyID0gVCwgY29sQ2xhc3NlcyA9ICJjaGFyYWN0ZXIiKSAlPiUNCiAgZ2F0aGVyKHNwZWNpZXMsIGNvdmVyLCBgQWNoaWxsZWEubWlsbGVmb2xpdW1gOmBWaW9sYS5zcC5gKSAlPiUNCiAgbXV0YXRlKHBsb3QgPSBhcy5pbnRlZ2VyKHBsb3QpKSAlPiUNCiAgbXV0YXRlKHByZXMgPSBjYXNlX3doZW4oDQogICAgY292ZXIgPT0gIlIiIH4gMSwNCiAgICBjb3ZlciA9PSAiTyIgfiAxLA0KICAgIGNvdmVyID09ICJGIiB+IDEsDQogICAgY292ZXIgPT0gIkMiIH4gMSwNCiAgICBjb3ZlciA9PSAiQSIgfiAxLA0KICAgIGNvdmVyID09ICIiIH4gMCkNCiAgKSAlPiUNCiAgc2VsZWN0KC1jb3ZlcikNCg0KbWV0YSA8LSByZWFkLmNzdigiZGF0YS9wbG90X21ldGFkYXRhLmNzdiIsIGhlYWRlciA9IFQpDQoNClRSIDwtIHJlYWQuY3N2KCJkYXRhL3BsYW50dHJhaXRzLmNzdiIsIGhlYWRlciA9IFQpICU+JQ0KICBtdXRhdGUoc3BlY2llcyA9IGdzdWIoIiAiLCAiLiIsIHNwZWNpZXMpKSAlPiUNCiAgbXV0YXRlKHNwZWNpYWxpc3QgPSBzcGVjaWFsaXN0XzUgKyBzcGVjaWFsaXN0XzEwKSAlPiUNCiAgbXV0YXRlKHNwZWNpYWxpc3RfbmFtZSA9aWZlbHNlKHNwZWNpYWxpc3QgPT0gMSwgImdyYXNzbGFuZCBzcGVjaWFsaXN0cyIsICJub24tc3BlY2lhbGlzdHMiKSkNCmBgYA0KDQpDcmVhdGUgYSBzaW5nbGUgZGF0YXNldCBieSBqb2luaW5nIHRoZSBzb2lsLCB2ZWdldGF0aW9uLCBtZXRhLCBhbmQgdHJhaXQgZGF0YS4gVGhlbiBjcmVhdGUgYW4gSUQgdmFyaWFibGUgdGhhdCBpZGVudGlmaWVzIGVhY2ggcGF0Y2gsIGNvbWJpbmluZyBsYW5kc2NhcGUgbmFtZSBldGMuLi4gT25lIHBsb3QgKElEID0gMTYxKSBzaG93ZWQgZXh0cmVtZSBDIHBlcmNlbnRhZ2UgdmFsdWVzLCBtb3N0IHByb2JhYmx5IGR1ZSB0byB3cm9uZyBzYW1wbGluZyBpbiB0aGUgZmllbGQuIFRoaXMgcGxvdCB3YXMgcmVtb3ZlZCBiZWZvcmUgdGhlIGFuYWx5c2VzLg0KYGBge3IgbWVzc2FnZT1GfQ0KZGF0IDwtIHZlZ2RhdCAlPiUNCiAgbGVmdF9qb2luKHNvaWwpICU+JQ0KICBsZWZ0X2pvaW4obWV0YSkgJT4lDQogIGxlZnRfam9pbihUUikgJT4lDQogIG11dGF0ZShJRCA9IGNhc2Vfd2hlbigNCiAgICBUeXBlID09ICJNSSIgfiBwYXN0ZShMYW5kc2NhcGUsIFR5cGUsIFNpemUsIERpc3RhbmNlKSwNCiAgICBUeXBlID09ICJTTkciIH4gcGFzdGUoTGFuZHNjYXBlLCBUeXBlKSkpICU+JQ0KICBtdXRhdGUoVHlwZSA9IGZhY3RvcihUeXBlLCBsZXZlbHMgPSBjKCJTTkciLCAiTUkiKSkpICU+JSANCiAgZmlsdGVyKHBsb3QgIT0gMTYxKQ0KDQpoZWFkKGRhdCkNCmBgYA0KDQojIyMgTGFuZHNjYXBlIGxldmVsIHJpY2huZXNzIHBhdHRlcm5zDQoNCipUaGlzIGlzIHRoZSBhbmFseXNpcyBmb3IgcmVzZWFyY2ggcXVlc3Rpb24gMSBhbmQgRmlndXJlIDIuKg0KDQpGaXJzdCB3ZSBjYWxjdWxhdGUgdGhlIGhhYml0YXQgYW1vdW50IGZvciByZW1uYW50IGdyYXNzbGFuZHMgYW5kIHNlbWktbmF0dXJhbCBncmFzc2xhbmRzIGluIGV2ZXJ5IGxhbmRzY2FwZS4NCmBgYHtyIG1lc3NhZ2UgPSBGfQ0KaGFiaXRhdF9hbW91bnQgPC0gbWV0YSAlPiUNCiAgZ3JvdXBfYnkoTGFuZHNjYXBlLCBUeXBlKSAlPiUNCiAgc3VtbWFyaXplKHN1bV9hcmVhID0gc3VtKEFyZWEpKSAlPiUNCiAgIyBNSSBhcmUgY291bnRlZCB0d2ljZSB0aGlzIHdheSAodHdvIHBsb3RzIHBlciBwYXRjaCkNCiAgIyBTTkcgYXJlIGNvdW50ZWQgc2l4IHRpbWVzIChzYW1lIGFyZWEgYXJvdW5kIGVhY2ggcGxvdCkNCiAgbXV0YXRlKGFtb3VudCA9IGlmZWxzZShUeXBlID09ICJNSSIsIHN1bV9hcmVhLzIsIHN1bV9hcmVhLzYpIC8gMTAwMDApDQpgYGANCg0KQ2FsY3VsYXRlIGFuZCB0ZXN0IGZvciBkaWZmZXJlbmNlcyBpbiBtZWFuIGhhYml0YXQgYW1vdW50IGJldHdlZW4gZ3Jhc3NsYW5kIHR5cGVzLg0KYGBge3J9DQpoYWJpdGF0X2Ftb3VudCAlPiUgDQogIGdyb3VwX2J5KFR5cGUpICU+JSANCiAgc3VtbWFyaXplKG1uID0gcm91bmQobWVhbihhbW91bnQpLCAxKSkNCg0KdHJpbShsbWUobG9nKGFtb3VudCkgfiBUeXBlLCByYW5kb209fiAxIHwgTGFuZHNjYXBlLCBkYXRhID0gaGFiaXRhdF9hbW91bnQpKQ0KYGBgDQoNClRvIGFjY291bnQgZm9yIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBudW1iZXIgb2YgdmVnZXRhdGlvbiBwbG90cyBzYW1wbGVkIGluIHRoZSBzZW1pLW5hdHVyYWwgZ3Jhc3NsYW5kcyAobiA9IDYgcGVyIGxhbmRzY2FwZSkgYW5kIHJlbW5hbnQgZ3Jhc3NsYW5kcyAobiA9IDEyIHBlciBsYW5kc2NhcGUpLCB0aGUgc3BlY2llcyByaWNobmVzcyBvZiB0aGUgcmVtbmFudCBncmFzc2xhbmRzIG5lZWRlZCB0byBiZSByZXNjYWxlZC4gV2UgY2FsY3VsYXRlZCB0aGUgYXZlcmFnZSBudW1iZXIgb2Ygc3BlY2llcyBhY3Jvc3Mgc2l4IHBsb3RzLCB0aHJvdWdoIHNhbXBsZS1iYXNlZCByYXJlZmFjdGlvbiAobiA9IDEwMCBwZXJtdXRhdGlvbnMpLg0KDQpXZSBydW4gdGhlIHNhbWUgYW5hbHlzZXMgb24gKDEpIGFsbCBzcGVjaWVzLCAoMikgdGhlIHN1YnNldCBvZiBzcGVjaWFsaXN0cywgYW5kICgzKSB0aGUgc3Vic2V0IG9mIG5vbi1zcGVjaWFsaXN0cy4gVG8gYXZvaWQgY29kZSByZXBsaWNhdGlvbiwgd2UgZmlyc3QgY3JlYXRlIHR3byBoZWxwZXJmdW5jdGlvbnM6IG9uZSBmb3IgcmFyZWZhY3Rpb24gYW5kIG9uZSBmb3IgY3JlYXRpbmcgcGxvdHMuDQpgYGB7cn0NCiMgcmFyZWZhY3Rpb24gZnVuY3Rpb24NCmZfZ2FtbWEgPC0gZnVuY3Rpb24odmVnZGF0YSl7DQogICMgc3BlY2llcyBhY2N1bXVsYXRpb24gY3VydmVzDQogIHNwZWNfYWNjIDwtIHZlZ2RhdGEgJT4lDQogICAgZ3JvdXBfbWFwKCB+IHNwZWNhY2N1bShjb21tID0gLngsIG1ldGhvZCA9ICJyYW5kb20iLCBwZXJtdXRhdGlvbnMgPSAxMDApKQ0KICANCiAgIyBleHRyYWN0IG1lYW4gcmljaG5lc3MgYWNyb3NzIDYgcGxvdHMNCiAgZ2FtbWEgPC0gYygpDQogIGZvcihpIGluIDE6MTgpew0KICAgIGdhbW1hW2ldIDwtIHNwZWNfYWNjW1tpXV0kcmljaG5lc3NbNl0NCiAgfQ0KICANCiAgIyBvbmx5IDUgU05HIHBsb3RzIGluIE55Y2tsZWJ5DQogIGdhbW1hWzEzXSA8LSBzcGVjX2FjY1tbMTNdXSRyaWNobmVzc1s1XQ0KICANCiAgIyBjb21iaW5lIGludG8gZGF0YSBmcmFtZQ0KICBkYXRhLmZyYW1lKGdhbW1hLCBncm91cF9rZXlzKHZlZ2RhdGEpKSAlPiUNCiAgICBsZWZ0X2pvaW4oaGFiaXRhdF9hbW91bnQpDQp9DQoNCiMgcGxvdCBvZiBsYW5kc2NhcGUgbGV2ZWwgcmljaG5lc3MNCmZfcGxvdF9nYW1tYSA8LSBmdW5jdGlvbihnYW1tYV9vdXQsIHRpdGxlKXsNCiAgbGFuZHNjYXBlX1NSIDwtIGdncGxvdChnYW1tYV9vdXQsIGFlcyh4ID0gYW1vdW50LCB5ID0gZ2FtbWEpKSArIA0KICAgIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gVHlwZSksIHNpemUgPSA3LCBhbHBoYSA9IDAuNSkgKw0KICAgIHNjYWxlX2NvbG9yX21hbnVhbCgiIiwgbGFiZWxzID0gYygicmVtbmFudCBncmFzc2xhbmRzIiwgInNlbWktbmF0dXJhbCBncmFzc2xhbmRzIiksDQogICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9IGMoIiNFN0I4MDAiLCAiI0ZDNEUwNyIpKSArDQogICAgc2NhbGVfeF9sb2cxMCgiSGFiaXRhdCBhbW91bnQgKGhhKSIpICsNCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoIlNwZWNpZXMgcmljaG5lc3MiLCBsaW1pdHMgPSBjKDAsODApKSArDQogICAgdGhlbWVfYncoKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgZ2d0aXRsZSh0aXRsZSkNCiAgDQogIGdnTWFyZ2luYWwobGFuZHNjYXBlX1NSLCBncm91cENvbG91ciA9IFQsIGdyb3VwRmlsbCA9IFQsIG1hcmdpbnMgPSAieSIsIGJ3ID0gNSkNCn0NCmBgYA0KDQooMSkgQW5hbHlzaXMgb24gdGhlIGZ1bGwgZGF0YXNldCwgaS5lLiBpbmNsdWRpbmcgYWxsIHNwZWNpZXMNCg0KQ2FsY3VsYXRlIHJpY2huZXNzDQpgYGB7ciBtZXNzYWdlID0gRn0NCnZlZ2RhdF9hbGwgPC0gZGF0ICU+JQ0KICBzZWxlY3QocGxvdCwgTGFuZHNjYXBlLCBUeXBlLCBzcGVjaWVzLCBwcmVzKSAlPiUNCiAgc3ByZWFkKHNwZWNpZXMsIHByZXMpICU+JQ0KICBzZWxlY3QoLXBsb3QpICU+JQ0KICBncm91cF9ieShMYW5kc2NhcGUsIFR5cGUpDQoNCmdhbW1hX291dF9hbGwgPC0gZl9nYW1tYSh2ZWdkYXRfYWxsKQ0KYGBgDQoNClBsb3QgcmljaG5lc3MgYWdhaW5zdCBoYWJpdGF0IGFtb3VudA0KYGBge3IgbWVzc2FnZSA9IEZ9DQpnYW1tYV9wbG90X2FsbCA8LSBmX3Bsb3RfZ2FtbWEoZ2FtbWFfb3V0X2FsbCwgImEpIGFsbCBzcGVjaWVzIikNCg0KYGBgDQoNClRlc3QgZGlmZmVyZW5jZXMgaW4gcmljaG5lc3MgYmV0d2VlbiBncmFzc2xhbmQgdHlwZXMsIGVpdGhlciBhY2NvdW50aW5nIG9yIG5vdCBhY2NvdW50aW5nIGZvciBoYWJpdGF0IGFtb3VudC4NCmBgYHtyfQ0KdHJpbShsbWUoZ2FtbWEgfiBzY2FsZShsb2coYW1vdW50KSkgKiBUeXBlLCByYW5kb209fiAxIHwgTGFuZHNjYXBlLCBkYXRhID0gZ2FtbWFfb3V0X2FsbCkpDQp0cmltKGxtZShnYW1tYSB+IFR5cGUsIHJhbmRvbT1+IDEgfCBMYW5kc2NhcGUsIGRhdGEgPSBnYW1tYV9vdXRfYWxsKSkNCmBgYA0KDQooMikgQW5hbHlzaXMgb24gdGhlIHN1YnNldCBvZiBzcGVjaWFsaXN0IHNwZWNpZXMNCg0KQ2FsY3VsYXRlIHJpY2huZXNzDQpgYGB7ciBtZXNzYWdlID0gRn0NCnZlZ2RhdF9zcGVjIDwtIGRhdCAlPiUNCiAgZmlsdGVyKHNwZWNpYWxpc3RfbmFtZSA9PSAiZ3Jhc3NsYW5kIHNwZWNpYWxpc3RzIikgJT4lDQogIHNlbGVjdChwbG90LCBMYW5kc2NhcGUsIFR5cGUsIHNwZWNpZXMsIHByZXMpICU+JQ0KICBzcHJlYWQoc3BlY2llcywgcHJlcykgJT4lDQogIHNlbGVjdCgtcGxvdCkgJT4lDQogIGdyb3VwX2J5KExhbmRzY2FwZSwgVHlwZSkNCg0KZ2FtbWFfb3V0X3NwZWMgPC0gZl9nYW1tYSh2ZWdkYXRfc3BlYykNCmBgYA0KDQoNClBsb3QgcmljaG5lc3MgYWdhaW5zdCBoYWJpdGF0IGFtb3VudA0KYGBge3J9DQpnYW1tYV9wbG90X3NwZWMgPC0gZl9wbG90X2dhbW1hKGdhbW1hX291dF9zcGVjLCAiYikgZ3Jhc3NsYW5kIHNwZWNpYWxpc3RzIikNCmBgYA0KDQpUZXN0IGRpZmZlcmVuY2VzIGluIHJpY2huZXNzIGJldHdlZW4gZ3Jhc3NsYW5kIHR5cGVzLCBlaXRoZXIgYWNjb3VudGluZyBvciBub3QgYWNjb3VudGluZyBmb3IgaGFiaXRhdCBhbW91bnQuDQpgYGB7cn0NCnRyaW0obG1lKGdhbW1hIH4gc2NhbGUobG9nKGFtb3VudCkpICogVHlwZSwgcmFuZG9tPX4gMSB8IExhbmRzY2FwZSwgZGF0YSA9IGdhbW1hX291dF9zcGVjKSkNCnRyaW0obG1lKGdhbW1hIH4gVHlwZSwgcmFuZG9tPX4gMSB8IExhbmRzY2FwZSwgZGF0YSA9IGdhbW1hX291dF9zcGVjKSkNCmBgYA0KDQooMykgQW5hbHlzaXMgb24gdGhlIHN1YnNldCBvZiBub24tc3BlY2lhbGlzdCBzcGVjaWVzDQoNCkNhbGN1bGF0ZSByaWNobmVzcw0KYGBge3IgbWVzc2FnZSA9IEZ9DQp2ZWdkYXRfbm9uc3BlYyA8LSBkYXQgJT4lDQogIGZpbHRlcihzcGVjaWFsaXN0X25hbWUgPT0gIm5vbi1zcGVjaWFsaXN0cyIpICU+JQ0KICBzZWxlY3QocGxvdCwgTGFuZHNjYXBlLCBUeXBlLCBzcGVjaWVzLCBwcmVzKSAlPiUNCiAgc3ByZWFkKHNwZWNpZXMsIHByZXMpICU+JQ0KICBzZWxlY3QoLXBsb3QpICU+JQ0KICBncm91cF9ieShMYW5kc2NhcGUsIFR5cGUpDQoNCmdhbW1hX291dF9ub25zcGVjIDwtIGZfZ2FtbWEodmVnZGF0X25vbnNwZWMpDQpgYGANCg0KUGxvdCByaWNobmVzcyBhZ2FpbnN0IGhhYml0YXQgYW1vdW50DQpgYGB7cn0NCmdhbW1hX3Bsb3Rfbm9uc3BlYyA8LSBmX3Bsb3RfZ2FtbWEoZ2FtbWFfb3V0X25vbnNwZWMsICJjKSBub24tc3BlY2lhbGlzdHMiKQ0KYGBgDQoNClRlc3QgZGlmZmVyZW5jZXMgaW4gcmljaG5lc3MgYmV0d2VlbiBncmFzc2xhbmQgdHlwZXMsIGVpdGhlciBhY2NvdW50aW5nIG9yIG5vdCBhY2NvdW50aW5nIGZvciBoYWJpdGF0IGFtb3VudC4NCmBgYHtyfQ0KdHJpbShsbWUoZ2FtbWEgfiBzY2FsZShsb2coYW1vdW50KSkgKiBUeXBlLCByYW5kb209fiAxIHwgTGFuZHNjYXBlLCBkYXRhID0gZ2FtbWFfb3V0X25vbnNwZWMpKQ0KdHJpbShsbWUoZ2FtbWEgfiBUeXBlLCByYW5kb209fiAxIHwgTGFuZHNjYXBlLCBkYXRhID0gZ2FtbWFfb3V0X25vbnNwZWMpKQ0KYGBgDQoNCkZpbmFsbHksIHdlIGNvbWJpbmUgdGhlIHBsb3RzIGZvciB0aGUgdGhyZWUgc3Vic2V0cyBvZiBkYXRhIGludG8gb25lIG11bHRpLXBhbmVsIGdyYXBoLg0KYGBge3IgZmlnLndpZHRoID0gMTMsIGZpZy5oZWlnaHQgPSA0fQ0KZ3JpZC5hcnJhbmdlKGdhbW1hX3Bsb3RfYWxsLCBnYW1tYV9wbG90X3NwZWMsIGdhbW1hX3Bsb3Rfbm9uc3BlYywgbnJvdyA9IDEpDQpgYGANCg0KIyMjIFBsb3QtbGV2ZWwgYW5hbHlzZXMNCg0KIyMjIyBTcGVjaWVzIHJpY2huZXNzIHBhdHRlcm5zDQoNCipUaGlzIGlzIHRoZSBhbmFseXNpcyBmb3IgcmVzZWFyY2ggcXVlc3Rpb24gMiBhbmQgRmlndXJlIDMuKg0KDQpDYWxjdWxhdGUgdGhlIG1lYW4gc3BlY2llcyByaWNobmVzcyBpbiB0aGUgdHdvIGdyYXNzbGFuZCB0eXBlcy4NCmBgYHtyIG1lc3NhZ2UgPSBGfQ0KZGF0ICU+JQ0KICBncm91cF9ieShwbG90LCBUeXBlKSAlPiUNCiAgc3VtbWFyaXplKFNSID0gc3VtKHByZXMpKSAlPiUNCiAgZ3JvdXBfYnkoVHlwZSkgJT4lDQogIHN1bW1hcml6ZShtbl9TUiA9IHJvdW5kKG1lYW4oU1IpLCAxKSkNCmBgYA0KDQoNCldlIHJ1biB0aGUgc2FtZSBhbmFseXNlcyBvbiAoMSkgYWxsIHNwZWNpZXMsICgyKSB0aGUgc3Vic2V0IG9mIHNwZWNpYWxpc3RzLCBhbmQgKDMpIHRoZSBzdWJzZXQgb2Ygbm9uLXNwZWNpYWxpc3RzLiBUbyBhdm9pZCBjb2RlIHJlcGxpY2F0aW9uLCB3ZSBmaXJzdCBjcmVhdGUgdHdvIGhlbHBlcmZ1bmN0aW9uczogb25lIGZvciBtYWtpbmcgcHJlZGljdGlvbnMgZnJvbSB0aGUgc3RhdGlzdGljYWwgbW9kZWwgYW5kIG9uZSBmb3IgY3JlYXRpbmcgcGxvdHMuDQpgYGB7cn0NCiMgcHJlZGljdCBhbG9uZyBzb2lsIFAgZ3JhZGllbnQNCmZfcHJlZGljdCA8LSBmdW5jdGlvbihtb2RlbCl7DQogIA0KICAjIGNyZWF0ZSBuZXcgZGF0YXNldCB3aXRoIGdyYWRpZW50IGluIFANCiAgbmV3ZGF0IDwtIGV4cGFuZC5ncmlkKFR5cGUgPSBjKCJNSSIsIlNORyIpLCANCiAgICAgICAgICAgICAgICAgICAgICBwSF9jID0gMCwgDQogICAgICAgICAgICAgICAgICAgICAgQ05fYyA9IDAsIA0KICAgICAgICAgICAgICAgICAgICAgIFBfYyA9IHNlcSgtMS4yLCAyLjIsIDAuMSksIA0KICAgICAgICAgICAgICAgICAgICAgIGZpdCA9IE5BKQ0KICANCiAgIyBwcmVkaWN0aW9ucyBhbmQgOTUlIENJJ3MNCiAgbmV3ZGF0JGZpdCA8LSBwcmVkaWN0KG1vZGVsLCBuZXdkYXRhID0gbmV3ZGF0LCBsZXZlbCA9IDApDQoNCiAgZGVzIDwtIG1vZGVsLm1hdHJpeChmb3JtdWxhKG1vZGVsKVstMl0sIG5ld2RhdCkNCiAgcHJlZHZhciA8LSBkaWFnKCBkZXMgJSolIHZjb3YobW9kZWwpICUqJSB0KGRlcykgKQ0KDQogIG5ld2RhdCRsb3dlciA9IHdpdGgobmV3ZGF0LCBmaXQgLSAyKnNxcnQocHJlZHZhcikgKQ0KICBuZXdkYXQkdXBwZXIgPSB3aXRoKG5ld2RhdCwgZml0ICsgMipzcXJ0KHByZWR2YXIpICkNCg0KICAjIHRyYW5zZm9ybSBwcmVkaWN0aW9ucyB0byAncmVzcG9uc2UnIHNjYWxlDQogICMgYmFjay10cmFuc2Zvcm0gc3RhbmRhcmRpemVkIGxvZyhQKSB0byBvcmlnaW5hbCBzb2lsIFANCiAgIyBtZWFuIGxvZyhQKSBjb25jZW50cmF0aW9ucyB3YXMgMy4wNTUNCiAgbW5fbG9nX1BfcmVzcCA8LSAzLjA1NQ0KDQogIG5ld2RhdCAlPiUNCiAgICBtdXRhdGUoZml0X3Jlc3AgPSBleHAoZml0KSwNCiAgICAgICAgICAgbG93ZXJfcmVzcCA9IGV4cChsb3dlciksDQogICAgICAgICAgIHVwcGVyX3Jlc3AgPSBleHAodXBwZXIpLA0KICAgICAgICAgICBQX3Jlc3AgPSBleHAoUF9jKSAqIGV4cChtbl9sb2dfUF9yZXNwKQ0KICApDQp9DQoNCg0KIyBwbG90IG9mIHBsb3QtbGV2ZWwgcmljaG5lc3MNCmZfcGxvdF9TUiA8LSBmdW5jdGlvbihTUl9kYXQsIFNSX291dCwgdGl0bGUpew0KICBnZ3Bsb3QoU1Jfb3V0LCBhZXMoUF9yZXNwLCBmaXRfcmVzcCwgY29sID0gVHlwZSkpICsNCiAgICBnZW9tX2xpbmUoc2l6ZSA9IDEuNSkgKw0KICAgIGdlb21fcmliYm9uKGFlcyh5ID0gTlVMTCwgeW1pbiA9IGxvd2VyX3Jlc3AsIHltYXggPSB1cHBlcl9yZXNwKSwgbGluZXR5cGUgPSAwLCBhbHBoYSA9IDAuMTUpICsNCiAgICBnZW9tX2xpbmUoYWVzKFBfcmVzcCwgbG93ZXJfcmVzcCksIGxpbmV0eXBlID0gMiwgYWxwaGEgPSAwLjUpICsNCiAgICBnZW9tX2xpbmUoYWVzKFBfcmVzcCwgdXBwZXJfcmVzcCksIGxpbmV0eXBlID0gMiwgYWxwaGEgPSAwLjUpICsNCiAgICBnZW9tX3BvaW50KGRhdGEgPSBTUl9kYXQsIGFlcyhQLCBTUisxKSwgYWxwaGEgPSAwLjUpICsNCiAgICBnZ3RpdGxlKHRpdGxlKSArDQogICAgdGhlbWVfYncoKSArDQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArDQogICAgc2NhbGVfeF9sb2cxMCgiUCBjb25jZW50cmF0aW9uIChtZy9rZykiKSArDQogICAgc2NhbGVfeV9jb250aW51b3VzKCJTcGVjaWVzIHJpY2huZXNzIiwgdHJhbnMgPSAibG9nIiwgYnJlYWtzID0gYygzLCAxMCwgMzApLCBsaW1pdHMgPSBjKDEsNDApKSArDQogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIiNFN0I4MDAiLCAiI0ZDNEUwNyIpKSAgDQp9DQpgYGANCg0KKDEpIEFuYWx5c2lzIG9uIHRoZSBmdWxsIGRhdGFzZXQsIGkuZS4gaW5jbHVkaW5nIGFsbCBzcGVjaWVzDQoNCkZpdCBtb2RlbA0KYGBge3IgbWVzc2FnZSA9IEZ9DQpkYXRfU1JfYWxsIDwtIGRhdCAlPiUNCiAgbXV0YXRlKENOX2MgPSBDL04gLSAgbWVhbihDL04pLA0KICAgICAgICAgUF9jID0gbG9nKFApIC0gbWVhbihsb2coUCkpLA0KICAgICAgICAgcEhfYyA9IHBIIC0gbWVhbihwSCwgbmEucm0gPSBUKSkgJT4lDQogIGdyb3VwX2J5KHBsb3QsIElELCBMYW5kc2NhcGUsIFAsIFBfYywgcEhfYywgQ05fYywgVHlwZSkgJT4lDQogIHN1bW1hcml6ZShTUiA9IHN1bShwcmVzKSkNCg0KbV9TUl9hbGwgPC0gbG1lKGxvZyhTUisxKSB+IDEgKyBUeXBlICogKFBfYyArIHBIX2MgKyBDTl9jKSwgDQogICAgICAgICAgICAgICAgcmFuZG9tPX4gMSB8IExhbmRzY2FwZS9JRCwgZGF0YSA9IGRhdF9TUl9hbGwpDQp0cmltKG1fU1JfYWxsKQ0KYGBgDQpQcmVkaWN0aW9ucyBhbmQgcGxvdA0KYGBge3J9DQpwcmVkX2FsbCA8LSBmX3ByZWRpY3QobV9TUl9hbGwpDQpTUl9wbG90X2FsbCA8LSBmX3Bsb3RfU1IoZGF0X1NSX2FsbCwgcHJlZF9hbGwsICJhKSBhbGwgc3BlY2llcyIpDQpgYGANCg0KKDIpIEFuYWx5c2lzIG9uIHRoZSBzdWJzZXQgb2Ygc3BlY2lhbGlzdCBzcGVjaWVzDQoNCkZpdCBtb2RlbC4NCmBgYHtyIG1lc3NhZ2UgPSBGfQ0KZGF0X1NSX3NwZWMgPC0gZGF0ICU+JQ0KICBmaWx0ZXIoc3BlY2lhbGlzdF9uYW1lID09ICJncmFzc2xhbmQgc3BlY2lhbGlzdHMiKSAlPiUNCiAgbXV0YXRlKENOX2MgPSBDL04gLSAgbWVhbihDL04pLA0KICAgICAgICAgUF9jID0gbG9nKFApIC0gbWVhbihsb2coUCkpLA0KICAgICAgICAgcEhfYyA9IHBIIC0gbWVhbihwSCwgbmEucm0gPSBUKSkgJT4lDQogIGdyb3VwX2J5KHBsb3QsIElELCBMYW5kc2NhcGUsIFAsIFBfYywgcEhfYywgQ05fYywgVHlwZSkgJT4lDQogIHN1bW1hcml6ZShTUiA9IHN1bShwcmVzKSkNCg0KbV9TUl9zcGVjIDwtIGxtZShsb2coU1IrMSkgfiAxICsgVHlwZSAqIChQX2MgKyBwSF9jICsgQ05fYyksIA0KICAgICAgICAgICAgICAgIHJhbmRvbT1+IDEgfCBMYW5kc2NhcGUvSUQsIGRhdGEgPSBkYXRfU1Jfc3BlYykNCnRyaW0obV9TUl9zcGVjKQ0KYGBgDQpQcmVkaWN0aW9ucyBhbmQgcGxvdC4NCmBgYHtyfQ0KcHJlZF9zcGVjIDwtIGZfcHJlZGljdChtX1NSX3NwZWMpDQpTUl9wbG90X3NwZWMgPC0gZl9wbG90X1NSKGRhdF9TUl9zcGVjLCBwcmVkX3NwZWMsICJiKSBncmFzc2xhbmQgc3BlY2lhbGlzdHMiKQ0KYGBgDQoNCg0KKDMpIEFuYWx5c2lzIG9uIHRoZSBzdWJzZXQgb2Ygbm9uLXNwZWNpYWxpc3Qgc3BlY2llcw0KDQpGaXQgbW9kZWwuDQpgYGB7ciBtZXNzYWdlID0gRn0NCmRhdF9TUl9ub25zcGVjIDwtIGRhdCAlPiUNCiAgZmlsdGVyKHNwZWNpYWxpc3RfbmFtZSA9PSAibm9uLXNwZWNpYWxpc3RzIikgJT4lDQogIG11dGF0ZShDTl9jID0gQy9OIC0gIG1lYW4oQy9OKSwNCiAgICAgICAgIFBfYyA9IGxvZyhQKSAtIG1lYW4obG9nKFApKSwNCiAgICAgICAgIHBIX2MgPSBwSCAtIG1lYW4ocEgsIG5hLnJtID0gVCkpICU+JQ0KICBncm91cF9ieShwbG90LCBJRCwgTGFuZHNjYXBlLCBQLCBQX2MsIHBIX2MsIENOX2MsIFR5cGUpICU+JQ0KICBzdW1tYXJpemUoU1IgPSBzdW0ocHJlcykpDQoNCm1fU1Jfbm9uc3BlYyA8LSBsbWUobG9nKFNSKzEpIH4gMSArIFR5cGUgKiAoUF9jICsgcEhfYyArIENOX2MpLCANCiAgICAgICAgICAgICAgICByYW5kb209fiAxIHwgTGFuZHNjYXBlL0lELCBkYXRhID0gZGF0X1NSX25vbnNwZWMpDQp0cmltKG1fU1Jfbm9uc3BlYykNCmBgYA0KUHJlZGljdGlvbnMgYW5kIHBsb3QuDQpgYGB7cn0NCnByZWRfbm9uc3BlYyA8LSBmX3ByZWRpY3QobV9TUl9ub25zcGVjKQ0KU1JfcGxvdF9ub25zcGVjIDwtIGZfcGxvdF9TUihkYXRfU1Jfbm9uc3BlYywgcHJlZF9ub25zcGVjLCAiYykgbm9uLXNwZWNpYWxpc3RzIikNCmBgYA0KDQoNCkZpbmFsbHksIHdlIGNvbWJpbmUgdGhlIHBsb3RzIGZvciB0aGUgdGhyZWUgc3Vic2V0cyBvZiBkYXRhIGludG8gb25lIG11bHRpLXBhbmVsIGdyYXBoLg0KYGBge3IgZmlnLndpZHRoID0gMTMsIGZpZy5oZWlnaHQgPSA0LCBtZXNzYWdlID0gRiwgd2FybmluZyA9IEZ9DQpncmlkLmFycmFuZ2UoU1JfcGxvdF9hbGwsIFNSX3Bsb3Rfc3BlYywgU1JfcGxvdF9ub25zcGVjLCBucm93ID0gMSkNCmBgYA0KDQojIyMjIENvbW11bml0eSBjb21wb3NpdGlvbiBwYXR0ZXJucw0KDQoqVGhpcyBpcyB0aGUgYW5hbHlzaXMgZm9yIHJlc2VhcmNoIHF1ZXN0aW9uIDMgYW5kIEZpZ3VyZSA0LioNCg0KRmlyc3Qgd2UgcHJlcGFyZSB0aGUgZGF0YSBpbnB1dCBmb3IgdGhlIHNwZWNpZXMgY29tbXVuaXR5IG1vZGVsLg0KYGBge3IgbWVzc2FnZSA9IEZ9DQojIHNlbGVjdCBtdWx0aXZhcmlhdGUgc3BlY2llcyBkYXRhDQpZX2RhdCA8LSBkYXQgJT4lDQogIHNlbGVjdChwbG90LCBzcGVjaWVzLCBwcmVzKSAlPiUNCiAgc3ByZWFkKHNwZWNpZXMsIHByZXMpICU+JQ0KICBzZWxlY3QoLXBsb3QpDQoNCllfZGF0X2ZyZXEgPC0gWV9kYXRbLCBjb2xTdW1zKFlfZGF0KSA+IDJdDQoNClkgPC0gZGF0YS5tYXRyaXgoWV9kYXRfZnJlcSkNCg0KIyBlbnZpcm9ubWVudGFsIHByZWRpY3RvcnMNCmVudiA8LSBkYXQgJT4lDQogIHNlbGVjdChwbG90LCBUeXBlLCBQLCBwSCwgTiwgQykgJT4lDQogIG11dGF0ZShDTl9jID0gQy9OIC0gIG1lYW4oQy9OKSwNCiAgICAgICAgIFBfYyA9IGxvZyhQKSAtIG1lYW4obG9nKFApKSwNCiAgICAgICAgIHBIX2MgPSBwSCAtIG1lYW4ocEgsIG5hLnJtID0gVCkpICU+JQ0KICBncm91cF9ieShwbG90LCBUeXBlKSAlPiUNCiAgc3VtbWFyaXplKFAgPSBtZWFuKFBfYyksDQogICAgICAgICAgICBDTiA9IG1lYW4oQ05fYyksDQogICAgICAgICAgICBwSCA9IG1lYW4ocEhfYykpDQoNClhEYXRhIDwtIGVudiAlPiUNCiAgZ3JvdXBfYnkoKSAlPiUNCiAgc2VsZWN0KC1wbG90KQ0KDQpYRGF0YSA8LSBhcy5kYXRhLmZyYW1lKFhEYXRhKQ0KDQojIHBsYW50IHRyYWl0cw0KVHJEYXRhIDwtIGRhdCAlPiUNCiAgZmlsdGVyKHNwZWNpZXMgJWluJSBjb2xuYW1lcyhZKSkgJT4lDQogIHNlbGVjdChzcGVjaWVzLCBzcGVjaWFsaXN0X25hbWUpICU+JQ0KICBkaXN0aW5jdCgpICU+JQ0KICBjb2x1bW5fdG9fcm93bmFtZXMoInNwZWNpZXMiKQ0KDQojIHNwYXRpby10ZW1wb3JhbCBjb250ZXh0DQpzdHVkeURlc2lnbiA8LSBkYXRhLmZyYW1lKHBhdGNoID0gZmFjdG9yKGRhdCRJRFsxOjE2MV0pLCBsYW5kc2NhcGUgPSBmYWN0b3IoZGF0JExhbmRzY2FwZVsxOjE2MV0pKQ0KckwgPC0gSG1zY1JhbmRvbUxldmVsKHVuaXRzID0gc3R1ZHlEZXNpZ24kcGFjaCkNCnJMMiA8LSBIbXNjUmFuZG9tTGV2ZWwodW5pdHMgPSBzdHVkeURlc2lnbiRsYW5kc2NhcGUpDQoNCnJtKFlfZGF0LCBZX2RhdF9mcmVxLCBlbnYpDQpgYGANCg0KQ29kZSB0byBzZXQtdXAgYW5kIHJ1biB0aGUgSE1TQyBtb2RlbC4gTm90ZSB0aGF0IGl0IHRha2VzIGZldyBob3VycyBvZiBzYW1wbGluZy4gU28geW91IG9ubHkgd2FudCB0byBkbyB0aGlzIG9uY2UgYW5kIHNhdmUgdGhlIG91dHB1dC4gVGhpcyB3YXksIHlvdSBjb3VsZCByZWxvYWQgdGhlIG1vZGVsIG91dHB1dCB3aGVuZXZlciBuZWVkZWQuIFRoaXMgd2FzIGFsc28gZG9uZSBoZXJlLg0KYGBge3J9DQptIDwtIEhtc2MoWSA9IFksIA0KICAgICAgICAgIFhEYXRhID0gWERhdGEsIFhGb3JtdWxhID0gfiBUeXBlICogKFAgKyBwSCArIENOKSwgDQogICAgICAgICAgVHJEYXRhID0gVHJEYXRhLCBUckZvcm11bGEgPSB+IHNwZWNpYWxpc3RfbmFtZSwNCiAgICAgICAgICBzdHVkeURlc2lnbiA9IHN0dWR5RGVzaWduLCANCiAgICAgICAgICByYW5MZXZlbHMgPSBsaXN0KHBhdGNoID0gckwsIGxhbmRzY2FwZSA9IHJMMiksIGRpc3RyID0gInByb2JpdCIpDQoNCiMgb25seSBydW4gbmV4dCBsaW5lcyB3aGVuIHlvdSB3YW50IHRvIHJlc2FtcGxlIHRoZSBtb2RlbA0KIyBtIDwtIHNhbXBsZU1jbWMobSwgdGhpbiA9IDEwLCBzYW1wbGVzID0gMTAwMCwgdHJhbnNpZW50ID0gNTAwLCBuQ2hhaW5zID0gMiwgdmVyYm9zZSA9IDUwMCkNCiMgc2F2ZShtLCAib3V0cHV0L2htc2Nfb3V0LlJEYXRhIikNCg0KbG9hZCgib3V0cHV0L2htc2Nfb3V0LlJEYXRhIikNCmBgYA0KDQpUbyBtYWtlIHByZWRpY3Rpb25zIGZyb20gdGhpcyBtb2RlbCwgd2UgZmlyc3QgY29uc3RydWN0IGEgZ3JhZGllbnQgaW4gc29pbCBQLCB3aGlsZSBrZWVwaW5nIHRoZSBvdGhlciBwcmVkaWN0b3JzIGF0IGEgY29uc3RhbnQgdmFsdWUuDQpgYGB7cn0NCkdyYWRpZW50X01JID0gY29uc3RydWN0R3JhZGllbnQobSxmb2NhbFZhcmlhYmxlID0gIlAiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub24uZm9jYWxWYXJpYWJsZXMgPSBsaXN0KCJUeXBlIiA9IGxpc3QoMywgIk1JIiksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBIIiA9IGxpc3QoMywgMC4zOCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNOIiA9IGxpc3QoMywgMCkpKQ0KDQpHcmFkaWVudF9TTkcgPSBjb25zdHJ1Y3RHcmFkaWVudChtLGZvY2FsVmFyaWFibGUgPSAiUCIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub24uZm9jYWxWYXJpYWJsZXMgPSBsaXN0KCJUeXBlIiA9IGxpc3QoMywgIlNORyIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAicEgiID0gbGlzdCgzLCAwLjM4KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNOIiA9IGxpc3QoMywgMCkpKQ0KYGBgDQoNCk1ha2UgcHJlZGljdGlvbnMuIFRoZW4sIHdyYW5nbGUgdGhlIG91dHB1dCBmb3IgY3JlYXRpbmcgYSBwbG90Lg0KYGBge3IgbWVzc2FnZSA9IEZ9DQpwcmVkWV9NSSA9IHByZWRpY3QobSwgWERhdGE9R3JhZGllbnRfTUkkWERhdGFOZXcsIHN0dWR5RGVzaWduPUdyYWRpZW50X01JJHN0dWR5RGVzaWduTmV3LA0KICAgICAgICAgICAgICAgICAgIHJhbkxldmVscz1HcmFkaWVudF9NSSRyTE5ldywgZXhwZWN0ZWQ9VFJVRSkNCg0KcHJlZFlfU05HID0gcHJlZGljdChtLCBYRGF0YT1HcmFkaWVudF9TTkckWERhdGFOZXcsIHN0dWR5RGVzaWduPUdyYWRpZW50X1NORyRzdHVkeURlc2lnbk5ldywNCiAgICAgICAgICAgICAgICAgICAgcmFuTGV2ZWxzPUdyYWRpZW50X1NORyRyTE5ldywgZXhwZWN0ZWQ9VFJVRSkNCg0KcHJlZFlfTUlfYmluZCA8LSBhYmluZDo6YWJpbmQocHJlZFlfTUksIGFsb25nID0gMykNCnFwcmVkWV9NSSA8LSBhcHBseShwcmVkWV9NSV9iaW5kLCBjKDEsIDIpLCBxdWFudGlsZSwgcHJvYnMgPSBjKC41KSwgbmEucm0gPSBUUlVFKSAlPiUNCiAgYXMuZGF0YS5mcmFtZSgpICU+JQ0KICBtdXRhdGUoUCA9IEdyYWRpZW50X01JJFhEYXRhTmV3JFAsDQogICAgICAgICB0eXBlID0gIk1JIikgJT4lDQogIGdhdGhlcihzcGVjaWVzLCBwcmVkLCAxOjExNSkgJT4lDQogIGxlZnRfam9pbihUUikgJT4lDQogIHNlbGVjdChQLCB0eXBlLCBzcGVjaWVzLCBwcmVkLCBzcGVjaWFsaXN0X25hbWUpDQoNCnByZWRZX1NOR19iaW5kIDwtIGFiaW5kOjphYmluZChwcmVkWV9TTkcsIGFsb25nID0gMykNCnFwcmVkWV9TTkcgPC0gYXBwbHkocHJlZFlfU05HX2JpbmQsIGMoMSwgMiksIHF1YW50aWxlLCBwcm9icyA9IGMoLjUpLCBuYS5ybSA9IFRSVUUpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkgJT4lDQogIG11dGF0ZShQID0gR3JhZGllbnRfU05HJFhEYXRhTmV3JFAsDQogICAgICAgICB0eXBlID0gIlNORyIpICU+JQ0KICBnYXRoZXIoc3BlY2llcywgcHJlZCwgMToxMTUpICU+JQ0KICBsZWZ0X2pvaW4oVFIpICU+JQ0KICBzZWxlY3QoUCwgdHlwZSwgc3BlY2llcywgcHJlZCwgc3BlY2lhbGlzdF9uYW1lKQ0KDQpxcHJlZFkgPC0gcmJpbmQocXByZWRZX01JLCBxcHJlZFlfU05HKQ0KDQpxcHJlZFlfc3VtbWFyeSA8LSBxcHJlZFkgJT4lDQogIGdyb3VwX2J5KFAsIHNwZWNpYWxpc3RfbmFtZSwgdHlwZSkgJT4lDQogIHN1bW1hcml6ZShtbl9wcmVkID0gbWVhbihwcmVkKSkNCmBgYA0KDQpCYWNrLXRyYW5zZm9ybSBzdGFuZGFyZGlzZWQgbG9nKFApIHRvIG9yaWdpbmFsIHNvaWwgUCBhbmQgY3JlYXRlIHRoZSBwbG90Lg0KYGBge3IgZmlnLndpZHRoID0gNiwgZmlnLmhlaWdodCA9IDZ9DQojIEJhY2stdHJhbnNmb3JtIHN0YW5kYXJkaXplZCBQIHZhbHVlcw0KbW5fbG9nX1BfcmVzcCA8LSBtZWFuKGxvZyhkYXQkUCkpDQpxcHJlZFkkUF9yZXNwIDwtIGV4cChxcHJlZFkkUCkgKiBleHAobW5fbG9nX1BfcmVzcCkNCnFwcmVkWV9zdW1tYXJ5JFBfcmVzcCA8LSBleHAocXByZWRZX3N1bW1hcnkkUCkgKiBleHAobW5fbG9nX1BfcmVzcCkNCg0KdHlwZV9sYWJzIDwtIGMoInJlbW5hbnQgZ3Jhc3NsYW5kcyIsICJzZW1pLW5hdHVyYWwgZ3Jhc3NsYW5kcyIpDQpuYW1lcyh0eXBlX2xhYnMpIDwtIGMoIk1JIiwgIlNORyIpDQoNCmdncGxvdChxcHJlZFksIGFlcyhQX3Jlc3AsIHByZWQsIGNvbCA9IHR5cGUpKSArDQogIGdlb21fbGluZShhZXMoZ3JvdXAgPSBzcGVjaWVzKSkgKw0KICBnZW9tX2xpbmUoZGF0YSA9IHFwcmVkWV9zdW1tYXJ5LCBhZXMoUF9yZXNwLCBtbl9wcmVkKSwgc2l6ZSA9IDEuMSwgY29sb3VyID0gImJsYWNrIikgKw0KICBmYWNldF9ncmlkKHR5cGUgfiBzcGVjaWFsaXN0X25hbWUsIGxhYmVsbGVyID0gbGFiZWxsZXIodHlwZSA9IHR5cGVfbGFicykpICsNCiAgdGhlbWVfYncoKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKw0KICBzY2FsZV94X2xvZzEwKCJQIGNvbmNlbnRyYXRpb24gKG1nL2tnKSIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKCJQcm9iYWJpbGl0eSBvZiBzcGVjaWVzIG9jY3VycmVuY2UiKSArDQogIHNjYWxlX2NvbG9yX21hbnVhbCgiIiwgbGFiZWxzID0gYygicmVtbmFudCBncmFzc2xhbmRzIiwgInNlbWktbmF0dXJhbCBncmFzc2xhbmRzIiksIHZhbHVlcyA9IGMoIiNFN0I4MDAiLCAiI0ZDNEUwNyIpKQ0KYGBgDQoNCiMjIyMgVmFyaWFuY2UgcGFydGl0aW9uaW5nDQoNCipBbmFseXNpcyBmb3IgcmVzZWFyY2ggcXVlc3Rpb24gNCBhbmQgRmlndXJlIDUuKg0KDQpVc2luZyB0aGUgb3V0cHV0IGZyb20gdGhlIHNwZWNpZXMgY29tbXVuaXR5IG1vZGVsLCB3ZSBjYW4gY2FsY3VsYXRlIHRoZSByZWxhdGl2ZSBpbXBvcnRhbmNlIG9mIHRoZSBkaWZmZXJlbnQgcHJlZGljdG9ycy4gV2UgZG8gdGhpcyBmb3IgdGhlIHR3byBncmFzc2xhbmQgdHlwZXMgc2VwYXJhdGVseS4NCg0KKDEpIHNlbWktbmF0dXJhbCBncmFzc2xhbmRzDQpgYGB7ciBtZXNzYWdlID0gRn0NClZQX1NORyA9IGNvbXB1dGVWYXJpYW5jZVBhcnRpdGlvbmluZyhtLCBncm91cCA9IGMoMSwwLDIsMiwyKSwgZ3JvdXBuYW1lcz1jKCJ0eXBlIiwiYWJpb3RpYyIpKQ0KDQp2YXJpYW5jZXNfU05HIDwtIGRhdGEuZnJhbWUoZmFjdG9yID0gcm93bmFtZXMoVlBfU05HJHZhbHMpLCBWUF9TTkckdmFscywgcm93Lm5hbWVzID0gTlVMTCkgJT4lDQogIHRpZHlyOjpnYXRoZXIoc3BlY2llcywgdmFsdWUsIDI6MTE2KSAlPiUNCiAgbGVmdF9qb2luKFRSKSAlPiUNCiAgbXV0YXRlKGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGMoInR5cGUiLCAiYWJpb3RpYyIsICJSYW5kb206IGxhbmRzY2FwZSIsIlJhbmRvbTogcGF0Y2giKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJ0eXBlIiwgImFidGlvaWMiLCAibGFuZHNjYXBlIiwicGF0Y2giKSkpDQoNCnZhcmlhbmNlc19zdW1tYXJ5X1NORyA8LSB2YXJpYW5jZXNfU05HICU+JQ0KICBncm91cF9ieShzcGVjaWFsaXN0LCBmYWN0b3IpICU+JQ0KICBzdW1tYXJpemUobiA9IG5fZGlzdGluY3Qoc3BlY2llcyksDQogICAgICAgICAgICBtbl92YWx1ZSA9IG1lYW4odmFsdWUsIG5hLnJtID0gVCksDQogICAgICAgICAgICBzZV92YWx1ZSA9IHNkKHZhbHVlLCBuYS5ybSA9IFQpL3NxcnQobikpDQoNClZBUlNfZ3JhcGhfYSA8LSBnZ3Bsb3QodmFyaWFuY2VzX3N1bW1hcnlfU05HLCBhZXMoZmFjdG9yKHNwZWNpYWxpc3QpLCBtbl92YWx1ZSwgY29sID0gZmFjdG9yKSkgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjQpKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtbl92YWx1ZSAtIDIgKiBzZV92YWx1ZSwgeW1heCA9IG1uX3ZhbHVlICsgMiAqIHNlX3ZhbHVlKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNCksIHdpZHRoID0gMC41KSArDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKCIiLCBsYWJlbHMgPSBjKCJub24tc3BlY2lhbGlzdHMiLCJncmFzc2xhbmQgc3BlY2lhbGlzdHMiKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoIlZhcmlhbmNlIHByb3BvcnRpb24iLCBsaW1pdHMgPSBjKDAsIDAuOCkpICsNCiAgc2NhbGVfY29sb3VyX2Rpc2NyZXRlKCIiKSArDQogIGdndGl0bGUoImEpIHNlbWktbmF0dXJhbCBncmFzc2xhbmRzIikgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpDQpgYGANCg0KKDIpIHJlbW5hbnQgZ3Jhc3NsYW5kcw0KYGBge3IgbWVzc2FnZSA9IEZ9DQpWUF9NSSA9IGNvbXB1dGVWYXJpYW5jZVBhcnRpdGlvbmluZyhtLCBncm91cCA9IGMoMSwxLDIsMiwyLDIsMiwyKSwgZ3JvdXBuYW1lcz1jKCJ0eXBlIiwiYWJpb3RpYyIpKQ0KDQp2YXJpYW5jZXNfTUkgPC0gZGF0YS5mcmFtZShmYWN0b3IgPSByb3duYW1lcyhWUF9NSSR2YWxzKSwgVlBfTUkkdmFscywgcm93Lm5hbWVzID0gTlVMTCkgJT4lDQogIHRpZHlyOjpnYXRoZXIoc3BlY2llcywgdmFsdWUsIDI6MTE2KSAlPiUNCiAgbGVmdF9qb2luKFRSKSAlPiUNCiAgbXV0YXRlKGZhY3RvciA9IGZhY3RvcihmYWN0b3IsIGxldmVscyA9IGMoInR5cGUiLCAiYWJpb3RpYyIsICJSYW5kb206IGxhbmRzY2FwZSIsIlJhbmRvbTogcGF0Y2giKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJ0eXBlIiwgImFidGlvaWMiLCAibGFuZHNjYXBlIiwicGF0Y2giKSkpDQoNCnZhcmlhbmNlc19zdW1tYXJ5X01JIDwtIHZhcmlhbmNlc19NSSAlPiUNCiAgZ3JvdXBfYnkoc3BlY2lhbGlzdCwgZmFjdG9yKSAlPiUNCiAgc3VtbWFyaXplKG4gPSBuX2Rpc3RpbmN0KHNwZWNpZXMpLA0KICAgICAgICAgICAgbW5fdmFsdWUgPSBtZWFuKHZhbHVlLCBuYS5ybSA9IFQpLA0KICAgICAgICAgICAgc2VfdmFsdWUgPSBzZCh2YWx1ZSwgbmEucm0gPSBUKS9zcXJ0KG4pKQ0KDQpWQVJTX2dyYXBoX2IgPC0gZ2dwbG90KHZhcmlhbmNlc19zdW1tYXJ5X01JLCBhZXMoZmFjdG9yKHNwZWNpYWxpc3QpLCBtbl92YWx1ZSwgY29sID0gZmFjdG9yKSkgKw0KICBnZW9tX3BvaW50KHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2Uod2lkdGggPSAwLjQpKSArDQogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBtbl92YWx1ZSAtIDIgKiBzZV92YWx1ZSwgeW1heCA9IG1uX3ZhbHVlICsgMiAqIHNlX3ZhbHVlKSwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNCksIHdpZHRoID0gMC41KSArDQogIHRoZW1lX2J3KCkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKCIiLCBsYWJlbHMgPSBjKCJub24tc3BlY2lhbGlzdHMiLCJncmFzc2xhbmQgc3BlY2lhbGlzdHMiKSkgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoIiIsIGxpbWl0cyA9IGMoMCwgMC44KSkgKw0KICBzY2FsZV9jb2xvdXJfZGlzY3JldGUoIiIpICsNCiAgZ2d0aXRsZSgiYikgbWlkZmllbGQgaXNsZXRzIikgKw0KICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC45LCAuOSwgLjksIC45KSwgbGVnZW5kLnBvc2l0aW9uID0gYygwLjgsIDAuODIpKQ0KYGBgDQoNCkFycmFuZ2UgdGhlIHR3byBncmFwaHMgaW50byBhIG11bHRpcGFuZWwgZmlndXJlLg0KYGBge3J9DQpncmlkLmFycmFuZ2UoVkFSU19ncmFwaF9hLCBWQVJTX2dyYXBoX2IsIG5yb3cgPSAxKQ0KYGBgDQoNCg==