knitr::opts_chunk$set(echo = TRUE,
                      fig.align = 'center')

# Loading needed packages
pacman::p_load(readxl, tidyverse, car, lme4, lmerTest, doBy, gee, geepack)


# Custom theme for text
text_theme <- theme(axis.title = element_text(size = 12),
                    axis.text = element_text(size = 10),
                    plot.title = element_text(size = 16, hjust = 0.5),
                    plot.subtitle = element_text(size = 12, hjust = 0.5),
                    plot.caption = element_text(size = 8))

Reading in the data:

toe <- 
  read_excel("toe fungues.xlsx") |> 
  # Cleaning the data
  janitor::clean_names() |> 
  mutate(
    subject_id = factor(subject_id),
    severe = response,
    response = factor(response, labels = c('none/mild', 'moderate/severe')),
    treatment = factor(treatment, labels = c("I", "T")),
    visit0 = factor(visit - 1) # Number of visits after the initial visit
  )

tibble(toe)
## # A tibble: 1,908 × 7
##    subject_id response        treatment  month visit severe visit0
##    <fct>      <fct>           <fct>      <dbl> <dbl>  <dbl> <fct> 
##  1 1          moderate/severe T          0         1      1 0     
##  2 1          moderate/severe T          0.857     2      1 1     
##  3 1          moderate/severe T          3.54      3      1 2     
##  4 1          none/mild       T          4.54      4      0 3     
##  5 1          none/mild       T          7.54      5      0 4     
##  6 1          none/mild       T         10.0       6      0 5     
##  7 1          none/mild       T         13.1       7      0 6     
##  8 2          none/mild       I          0         1      0 0     
##  9 2          none/mild       I          0.964     2      0 1     
## 10 2          moderate/severe I          2         3      1 2     
## # ℹ 1,898 more rows

The toe data set has 5 variables on over 1900 measurements of patients with a certain toe fungus. Each patient takes one of two different medications and reports monthly (ish). Each visit the severity of their condition is recorded.

The five variables are:

  1. subject_id: The unique ID number for each patient

  2. response: The severity of the condition

  3. treatment: Which of the two treatments the patient is assigned

    • 250 mg of terbinafine (T)
    • 200 mg of itraconzaole (I)
  4. month: Time in months since the start of the experiment

  5. visit0: The visit number for that measurement with initial visit as 0.

summary(toe)
##    subject_id              response    treatment     month       
##  1      :   7   none/mild      :1500   I:937     Min.   : 0.000  
##  3      :   7   moderate/severe: 408   T:971     1st Qu.: 1.000  
##  4      :   7                                    Median : 3.000  
##  6      :   7                                    Mean   : 4.691  
##  7      :   7                                    3rd Qu.: 8.893  
##  9      :   7                                    Max.   :18.500  
##  (Other):1866                                                    
##      visit           severe       visit0 
##  Min.   :1.000   Min.   :0.0000   0:294  
##  1st Qu.:2.000   1st Qu.:0.0000   1:288  
##  Median :4.000   Median :0.0000   2:283  
##  Mean   :3.896   Mean   :0.2138   3:272  
##  3rd Qu.:6.000   3rd Qu.:0.0000   4:263  
##  Max.   :7.000   Max.   :1.0000   5:244  
##                                   6:264

Each patient is scheduled to visit after a month for the first three visits, then once every three months for the last 3 visits, but that doesn’t always (almost never) happens. So let’s look at the average time and standard deviation of each visit in months:

toe |> 
  summarize(
    .by = visit0,
    month_avg = mean(month),
    month_sd = sd(month)
  )
## # A tibble: 7 × 3
##   visit0 month_avg month_sd
##   <fct>      <dbl>    <dbl>
## 1 0           0       0    
## 2 1           1.03    0.115
## 3 2           2.06    0.202
## 4 3           3.13    0.273
## 5 4           6.28    0.541
## 6 5           9.31    0.656
## 7 6          12.5     0.895

While the average is close to the scheduled time, the spread in time between visits grows the longer the study goes on (less consistently showing during the scheduled appointment).

EDA: Plotting the data

Let’s start by creating some data visualizations to get an idea of how the data look

Box plots of Response vs Month separated by Treatment

ggplot(
  data = toe,
  mapping = aes(
    x = month, 
    y = response, 
    fill = treatment
  )
) + 
  geom_boxplot() +
  theme_bw()

The basic box plot doesn’t indicate much of a difference between the two treatments on severity over time. Let’s look at the ‘success percent’ (none/mild) by visit and treatment, ignoring the subject

Scatterplot for success pct by visit and treatment, ignoring subject

toe |> 
  mutate(
    .by = visit0,
    avg_month = round(mean(month))
  ) |> 
  # Counting the number of patients at each visit/response/treatment combo
  summarize(
    .by = c(avg_month, treatment, response),
    n_obs = n()
  ) |> 
  # Proportion of patients with each response for each visit treatment combo
  mutate(
    .by = c(avg_month, treatment),
    bad_prop = n_obs/sum(n_obs)
  ) |> 
  # only moderate and severe rows
  filter(
    response == 'moderate/severe'
  ) |> 
  # Line Plot
  ggplot(
    mapping = aes(
      x = avg_month, 
      y = bad_prop, 
      color = treatment, 
      weight = n_obs
    )
  ) + 
  geom_point(
    size=2,
    show.legend = F
  ) + 
  
  geom_line() + 
  
  scale_x_continuous(
    breaks = toe |> 
      summarize(.by = visit0, month_avg = mean(month)) |> 
      pull(month_avg) |> 
      round(),
    minor_breaks = NULL
  ) + 
  
  labs(
    x = "Months after Initival Visit",
    y = "Proportion with Moderate or Severe Separation",
    title = "Proportion of Patients with Moderate or Severe Separation",
    subtitle = "By Number of Visits and Treatment",
    color = "Medication"
  ) + 
  theme_bw() + 
  text_theme + 
  
  theme(
    legend.position = 'inside',
    legend.position.inside = c(0.9, 0.85),
    legend.key = element_rect(fill=NA)
  ) + 
  
  scale_color_discrete(
    labels = c("Itraconzaole", "Terbinafine"),
    guide= guide_legend(reverse=T)
  )

We can see at the start of the study that both medications had about a 38% moderate or severe separation rate. After the subjects started taking their randomly assigned medication, Terbinafine performed better during each month, but both leveled out after 6 months, not seeing substantial improvement after then.

This graph ignores that the data are repeated measurements on 294 unique subjects. We can use a Generalized Estimating Equation (GEE) to estimate the effect of the mediation of separation severity, accounting for the correlation structure for each patient!

Important: We’ll look at the number of missing cases per visit, but we won’t include the missingness in our analysis

Missing values graph:

toe |> 
  # Long way of including a missing value in long format data :(
  # Pivot to wide to include if the value is missing
  pivot_wider(
    id_cols = c(subject_id, treatment),
    values_from = response,
    names_from = visit0
  ) |> 
  # Pivot back to long
  pivot_longer(
    cols = c(-subject_id, -treatment),
    names_to = 'visit0',
    values_to = 'response'
  ) |> 
  # Calculating the missing percentage of each combo of treatment and visit
  mutate(
    .by = c(treatment, visit0),
    missing_prop = mean(is.na(response))
  ) |> 
  # line graph of the missing percentage of visit
  ggplot(
    mapping = aes(
      x = visit0,
      y = missing_prop,
      color = treatment,
      group = treatment
    )
  ) +
  geom_line(
    linewidth = 1
  ) + 
  
  labs(
    x = "Visit Number",
    y = "Proportion of No-Show Patients",
    title = "Proportion of No-Show Patients by Treatment and Visit",
    color = "Medication"
  ) + 
  theme_bw() + 
  text_theme + 
  
  theme(
    legend.position = 'inside',
    legend.position.inside = c(0.8, 0.15),
    legend.key = element_rect(fill = NA)
  ) + 
  
  scale_color_discrete(
    labels = c("Itraconzaole", "Terbinafine"),
    guide= guide_legend(reverse = T)
  )

The fifth visit had almost 20% of the nearly 300 patients not show up. It would be a good idea to follow up with them to see why they missed the fifth visit, especially for those who arrived for the final visit.

Fitting the model using geeglm()

We can use the function geeglm() to fit the “model” a very similar way we would using glm() with formula, family, and data

It also has some additional arguments that we need to supply:

Independence Model

toe_indep <- 
  geeglm(
    formula = severe ~ treatment * month, 
    family = binomial(link = "logit"),
    id = subject_id, 
    data = toe, 
    std.err = "san.se", 
    corstr = 'independence',
    scale.fix = T  # Assumes a 'constant' variance 
  )

# Estimates and results
broom::tidy(toe_indep)
## # A tibble: 4 × 5
##   term              estimate std.error   statistic       p.value
##   <chr>                <dbl>     <dbl>       <dbl>         <dbl>
## 1 (Intercept)      -0.557       0.171  10.6        0.00115      
## 2 treatmentT       -0.000582    0.251   0.00000538 0.998        
## 3 month            -0.170       0.0292 34.1        0.00000000522
## 4 treatmentT:month -0.0672      0.0521  1.66       0.197
# Fit stats
broom::glance(toe_indep)
## # A tibble: 1 × 5
##   df.residual n.clusters max.cluster.size alpha gamma
##         <int>      <int>            <int> <dbl> <dbl>
## 1        1904        294                7    NA  1.04

How does it compare to a fixed glm? Let’s take a look!

glm(
  formula = severe ~ treatment * month, 
  family = binomial(link="logit"),
  data = toe
) |> 
  broom::tidy()
## # A tibble: 4 × 5
##   term              estimate std.error statistic  p.value
##   <chr>                <dbl>     <dbl>     <dbl>    <dbl>
## 1 (Intercept)      -0.557       0.109   -5.11    3.25e- 7
## 2 treatmentT       -0.000582    0.156   -0.00373 9.97e- 1
## 3 month            -0.170       0.0236  -7.21    5.58e-13
## 4 treatmentT:month -0.0672      0.0375  -1.79    7.32e- 2

Note the estimates are the same (yay!), but the standard errors are different (boo!)

Let’s look at a couple of different models

Model for toe data using exchangeable correlation structure

We can easily update a saved model in R using the update() function. You give it a model and the arguments of the original function you want to change.

If we want to update the correlation structure, we can use update()!

toe_ex <- update(toe_indep, corstr = "exchangeable") 

broom::tidy(toe_ex)
## # A tibble: 4 × 5
##   term             estimate std.error statistic      p.value
##   <chr>               <dbl>     <dbl>     <dbl>        <dbl>
## 1 (Intercept)      -0.584      0.172  11.5      0.000691    
## 2 treatmentT        0.00681    0.260   0.000683 0.979       
## 3 month            -0.171      0.0301 32.5      0.0000000121
## 4 treatmentT:month -0.0781     0.0543  2.07     0.150
broom::glance(toe_ex)
## # A tibble: 1 × 5
##   df.residual n.clusters max.cluster.size alpha gamma
##         <int>      <int>            <int> <dbl> <dbl>
## 1        1904        294                7 0.441  1.04

model for toe data using AR1 model

toe_ar <- update(toe_indep, corstr= "ar1") 
broom::tidy(toe_ar)
## # A tibble: 4 × 5
##   term             estimate std.error statistic     p.value
##   <chr>               <dbl>     <dbl>     <dbl>       <dbl>
## 1 (Intercept)       -0.642     0.168    14.5    0.000137   
## 2 treatmentT         0.0677    0.251     0.0725 0.788      
## 3 month             -0.138     0.0274   25.5    0.000000432
## 4 treatmentT:month  -0.0957    0.0515    3.45   0.0631
broom::glance(toe_ar)
## # A tibble: 1 × 5
##   df.residual n.clusters max.cluster.size alpha gamma
##         <int>      <int>            <int> <dbl> <dbl>
## 1        1904        294                7 0.675  1.04

Model using undefined structure

To build an unstructured correlation matrix, we can use corstr = 'unstructured'.

toe_un <- update(toe_indep, corstr = "unstructured") 
broom::tidy(toe_un)
## # A tibble: 4 × 5
##   term             estimate std.error statistic     p.value
##   <chr>               <dbl>     <dbl>     <dbl>       <dbl>
## 1 (Intercept)       -0.737     0.166    19.6    0.00000954 
## 2 treatmentT         0.0367    0.246     0.0222 0.882      
## 3 month             -0.132     0.0264   25.2    0.000000518
## 4 treatmentT:month  -0.0900    0.0483    3.47   0.0624

Let’s compare the test statistics of all 4 models:

data.frame(
  Independence = coef(toe_indep) / (vcov(toe_indep) |> diag() |> sqrt()), 
  exchangeable = coef(toe_ex)    / (vcov(toe_ex) |> diag() |> sqrt()),
  AR1          = coef(toe_ar)    / (vcov(toe_ar) |> diag() |> sqrt()), 
  Unstructured = coef(toe_un)    / (vcov(toe_un) |> diag() |> sqrt())
) 
##                  Independence exchangeable        AR1 Unstructured
## (Intercept)       -3.25188211  -3.39308188 -3.8137693    -4.427395
## treatmentT        -0.00231878   0.02612795  0.2691934     0.148830
## month             -5.83995826  -5.69828761 -5.0541191    -5.019508
## treatmentT:month  -1.28985769  -1.43868806 -1.8585728    -1.863199

While none of the results drastically disagree with one another, which correlation structure should we use?

Like we do with other model selection, we can use an Information Criterion to help decide!

Using QIC() to compare which correlation structure is preferred

Quasi Information Criterion (QIC) is a recommended method to compare models with the same mean structure (same formula provided by geeglm()) but different cor_str.

If we want to compare models with different mean structures but the same correlation structure, we can use QICu (which we aren’t, but in case you want to!)

QIC(toe_indep, toe_ex, toe_ar, toe_un) |> 
  data.frame() 
##                QIC     QICu Quasi.Lik       CIC params     QICC
## toe_indep 1836.731 1824.015 -908.0075 10.357955      4 1836.869
## toe_ex    1838.471 1824.693 -908.3463 10.889287      4 1838.679
## toe_ar    1838.033 1826.013 -909.0066 10.009813      4 1838.241
## toe_un    1840.188 1829.012 -910.5062  9.587764      4 1845.039

Like other information criterion, we want to use ones that have a smaller value.

According to QIC, the independent model is the “best”, followed by the exchangable and arima models, then the unstructured.

Despite the results of QIC, we’ll move forward with the arima model since it is likely that the responses for each individual are associated and it is the typical model used for longitudinal data.

Fitting 3 different models with AR1 correlation structures:

We’ll fit three different mean structures for the AR1 model: Interaction, Additive, and Month only. We already have the interaction model, toe_ar. Now we just need to fit the other 2

Additive model

We can remove the interaction term using update() and -treatment:month. The : in a formula indicates just the interaction term. Otherwise using A*B will remove A, B, and the interaction!

toe_add_ar1 <- update(toe_ar, formula = . ~ .- treatment:month)

Just Month, No effect for different treatments

toe_mon_ar1 <- update(toe_ar, formula = severe ~ month)

Using ANOVA to compare the results

We can compare the results of the different models using anova():

anova(toe_ar, toe_mon_ar1)
## Analysis of 'Wald statistic' Table
## 
## Model 1 severe ~ treatment * month 
## Model 2 severe ~ month
##   Df     X2 P(>|Chi|)
## 1  2 4.0938    0.1291

No strong evidence of an effect of treatment over time

#Seeing if we need to include treatment compared to the interaction model anova(toe_ar, toe_ARMon)

Creating a Wald confidence interval for each parameter

We can create a confidence interval for each parameter using the esticon() function in the doBy package

esticon() takes a model, linear combination matrix, and additionally can specify a vector of \(H_0\) values to test the combinations

But we don’t need beta0 to create a confidence interval for model effects

Can also specify joint.test = T if you’d like to test all of the hypotheses using an omnibus (one combined) test. Use F if you want to test individual combinations

95% CI on the odds scale for the parameters:

# Getting the vector of coefficients for the ar model
gee_ar_coef <- coef(toe_ar)

# Getting the comparison matrix (a diagonal matrix where each row corresponds to 1 parameter)
diag_comp_mat <- diag(length(gee_ar_coef))
diag_comp_mat
##      [,1] [,2] [,3] [,4]
## [1,]    1    0    0    0
## [2,]    0    1    0    0
## [3,]    0    0    1    0
## [4,]    0    0    0    1

esticon() gives the estimates and the standard errors of the parameters if we use a diagonal comparison matrix, which is what we created in diag_comp_mat

model_est <- 
  esticon(
    obj = toe_ar, 
    L = diag_comp_mat,
    beta0 = rep(0, length(gee_ar_coef)) # Vector of 0s for the null hypotheses
  ) 
# Adding the names of the effects to the rows
row.names(model_est) <- names(gee_ar_coef) 
round(model_est,4)
##                  estimate std.error statistic p.value   beta0 df
## (Intercept)       -0.6419    0.1683   14.5448  0.0001  0.0000  1
## treatmentT         0.0677    0.2515    0.0725  0.7878  0.0000  1
## month             -0.1383    0.0274   25.5441  0.0000  0.0000  1
## treatmentT:month  -0.0957    0.0515    3.4543  0.0631  0.0000  1

If you want a confidence interval, we can extract the upper and lower bounds of the confidence intervals using obj$lwr and obj$upr

model_odds_CI <- 
  data.frame(
    Estimate = model_est$estimate,
    Lower    = model_est$lwr,
    Upper    = model_est$upr
  ) |>
  # Exponentiating to calculate the odds ratio
  exp()

row.names(model_odds_CI) <- row.names(model_est)
round(model_odds_CI,4)
##                  Estimate  Lower  Upper
## (Intercept)        0.5263 0.3784 0.7320
## treatmentT         1.0700 0.6537 1.7516
## month              0.8708 0.8253 0.9188
## treatmentT:month   0.9088 0.8216 1.0052

We can also look at combinations of factors as well. Let’s check 3 Combinations using the Interaction Model:

  1. The odds ratio of Moderate or Severe Separation (MSS) for the two treatments after 6 months

  2. The odds a person taking itraconzaole has moderate or severe separation after 6 months (intercept + slope*6)

  3. The odds ratio that someone taking terbinafine has MSS a month later (slope + interaction)

# Combination matrix (cm):
toe_comp_mat <- 
  rbind(
    c(0,1,0,6), # Odds ratio of MSS after 6 Months for Terb vs Itra 
    c(0,0,6,0), # Odds ratio of itraconzaole user has MSS after 6 months 
    c(0,0,1,1)  # Odds ratio of terbinafine user has MSS after an additional month
  )

toe_comp_mat
##      [,1] [,2] [,3] [,4]
## [1,]    0    1    0    6
## [2,]    0    0    6    0
## [3,]    0    0    1    1
# CI for the additive model
toe_est <- esticon(toe_ar, L = toe_comp_mat) 
row.names(toe_est) <- c("OR Trt: 6 Months", "OR Itra: 6 Months", "OR Terb: Month") 
round(toe_est,4) |> data.frame()
##                   estimate std.error statistic p.value beta0 df     lwr     upr
## OR Trt: 6 Months   -0.5063    0.2807    3.2539  0.0713     0  1 -1.0565  0.0438
## OR Itra: 6 Months  -0.8299    0.1642   25.5441  0.0000     0  1 -1.1518 -0.5081
## OR Terb: Month     -0.2340    0.0436   28.8076  0.0000     0  1 -0.3194 -0.1485
# Can get the upper and lower estimates using \$lwr and $upr
toe_odds_CI <- 
  data.frame(
    Estimate = toe_est$estimate,
    Lower    = toe_est$lwr,
    Upper    = toe_est$upr
  ) |>
  exp()

row.names(toe_odds_CI) <- row.names(toe_est) 
round(toe_odds_CI,4)
##                   Estimate  Lower  Upper
## OR Trt: 6 Months    0.6027 0.3477 1.0448
## OR Itra: 6 Months   0.4361 0.3161 0.6016
## OR Terb: Month      0.7914 0.7266 0.8620

Scatterplot with GEE Estimates

trt_label <- c("Itraconzaole", "Terbinafine")
names(trt_label) <- c("I","T")

# Scatterplot for success pct by visit and treatment, ignoring subject

data.frame(
  toe, 
  gee_MSS   = predict(toe_ar,    type = "response"), 
  logit_MSS = predict(toe_indep, type = "response")
) |>
  mutate(
    .by = visit0,
    avg_month = mean(month)
  ) |>
  summarize(
    .by = c(avg_month, treatment),
    n_obs = n(), 
    prop_0 = sum(severe == 1)/n_obs, 
    pred_gee = mean(gee_MSS), 
    pred_logit = mean(logit_MSS)
  ) |>
  mutate(
    treatment = if_else(treatment == "I", "Itraconzaole", "Terbinafine")
  ) |>
  ggplot(
    mapping = aes(
      x = avg_month, 
      y = prop_0
    )
  ) +
  
  facet_wrap(
    facets = vars(treatment),
    nrow = 2
  ) +
  
  # Observed Proportions
  
  geom_line(
    mapping = aes(color="Observed")
  ) + 
  geom_point(
    mapping = aes(color="Observed")
  ) +
  
  # GEE Proportions with Unstructured Correlations
  
  geom_line(
    mapping = aes(
      y = pred_gee, 
      color = "GEE"
    ), 
    linetype="dashed"
  ) +
  geom_point(
    mapping = aes(
      y = pred_gee,
      color = "GEE"
    ), 
    shape = 15
  ) +
  
  # Logistic Proportions
  
  geom_line(
    mapping = aes(
      y = pred_logit, 
      color = "Logistic"
    ), 
    linetype="longdash"
  ) +
  geom_point(
    mapping = aes(
      y = pred_logit,
      color = "Logistic"
    ), 
    shape = 17
  ) +
  # Average month per visit on the x-axis
  scale_x_continuous(
    breaks = toe |>
      summarize(.by = visit0, avg_month = mean(month)) |>
      pull(avg_month) |>
      round(), 
    minor_breaks = NULL
  ) +
  
  labs(
    x = "Months after Initival Visit", 
    y = "Proportion with Moderate or Severe Separation", 
    title = "Proportion of Patients with Moderate or Severe Separation", 
    subtitle = "By Month and Treatment"
  ) +
  theme_bw() +
  text_theme + 
  theme(legend.position="bottom") +
  
  scale_colour_manual(
    name = 'Probability Type', 
    values = c('Observed'='#F8766D',
               'GEE'='#00BFC4', 
               "Logistic"="#C77CFF")
  )

LS0tDQp0aXRsZTogJ0dlbmVyYWxpemVkIEVzdGltYXRpbmcgRXF1YXRpb25zOiBUb2UgRnVuZ3VzJw0KYXV0aG9yOiAiQ2hhcHRlciA5Ig0KZGF0ZTogIlNUQVQgNTM1MCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICB0aGVtZTogbHVtZW4NCiAgICBmaV9jYXB0aW9uOiB0cnVlDQplZGl0b3Jfb3B0aW9uczogDQogIG1hcmtkb3duOiANCiAgICB3cmFwOiA3Mg0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPVR9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsDQogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gJ2NlbnRlcicpDQoNCiMgTG9hZGluZyBuZWVkZWQgcGFja2FnZXMNCnBhY21hbjo6cF9sb2FkKHJlYWR4bCwgdGlkeXZlcnNlLCBjYXIsIGxtZTQsIGxtZXJUZXN0LCBkb0J5LCBnZWUsIGdlZXBhY2spDQoNCg0KIyBDdXN0b20gdGhlbWUgZm9yIHRleHQNCnRleHRfdGhlbWUgPC0gdGhlbWUoYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpLA0KICAgICAgICAgICAgICAgICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICAgICAgICAgICAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTYsIGhqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICAgcGxvdC5zdWJ0aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGhqdXN0ID0gMC41KSwNCiAgICAgICAgICAgICAgICAgICAgcGxvdC5jYXB0aW9uID0gZWxlbWVudF90ZXh0KHNpemUgPSA4KSkNCmBgYA0KDQojIyBSZWFkaW5nIGluIHRoZSBkYXRhOg0KDQpgYGB7cn0NCnRvZSA8LSANCiAgcmVhZF9leGNlbCgidG9lIGZ1bmd1ZXMueGxzeCIpIHw+IA0KICAjIENsZWFuaW5nIHRoZSBkYXRhDQogIGphbml0b3I6OmNsZWFuX25hbWVzKCkgfD4gDQogIG11dGF0ZSgNCiAgICBzdWJqZWN0X2lkID0gZmFjdG9yKHN1YmplY3RfaWQpLA0KICAgIHNldmVyZSA9IHJlc3BvbnNlLA0KICAgIHJlc3BvbnNlID0gZmFjdG9yKHJlc3BvbnNlLCBsYWJlbHMgPSBjKCdub25lL21pbGQnLCAnbW9kZXJhdGUvc2V2ZXJlJykpLA0KICAgIHRyZWF0bWVudCA9IGZhY3Rvcih0cmVhdG1lbnQsIGxhYmVscyA9IGMoIkkiLCAiVCIpKSwNCiAgICB2aXNpdDAgPSBmYWN0b3IodmlzaXQgLSAxKSAjIE51bWJlciBvZiB2aXNpdHMgYWZ0ZXIgdGhlIGluaXRpYWwgdmlzaXQNCiAgKQ0KDQp0aWJibGUodG9lKQ0KYGBgDQoNClRoZSBgdG9lYCBkYXRhIHNldCBoYXMgNSB2YXJpYWJsZXMgb24gb3ZlciAxOTAwIG1lYXN1cmVtZW50cyBvZiBwYXRpZW50cw0Kd2l0aCBhIGNlcnRhaW4gdG9lIGZ1bmd1cy4gRWFjaCBwYXRpZW50IHRha2VzIG9uZSBvZiB0d28gZGlmZmVyZW50DQptZWRpY2F0aW9ucyBhbmQgcmVwb3J0cyBtb250aGx5IChpc2gpLiBFYWNoIHZpc2l0IHRoZSBzZXZlcml0eSBvZiB0aGVpcg0KY29uZGl0aW9uIGlzIHJlY29yZGVkLg0KDQpUaGUgZml2ZSB2YXJpYWJsZXMgYXJlOg0KDQoxKSAgYHN1YmplY3RfaWRgOiBUaGUgdW5pcXVlIElEIG51bWJlciBmb3IgZWFjaCBwYXRpZW50DQoNCjIpICBgcmVzcG9uc2VgOiBUaGUgc2V2ZXJpdHkgb2YgdGhlIGNvbmRpdGlvbg0KDQozKSAgYHRyZWF0bWVudGA6IFdoaWNoIG9mIHRoZSB0d28gdHJlYXRtZW50cyB0aGUgcGF0aWVudCBpcyBhc3NpZ25lZA0KDQogICAgLSAgIDI1MCBtZyBvZiB0ZXJiaW5hZmluZSAoVCkNCiAgICAtICAgMjAwIG1nIG9mIGl0cmFjb256YW9sZSAoSSkNCg0KNCkgIGBtb250aGA6IFRpbWUgaW4gbW9udGhzIHNpbmNlIHRoZSBzdGFydCBvZiB0aGUgZXhwZXJpbWVudA0KDQo1KSAgYHZpc2l0MGA6IFRoZSB2aXNpdCBudW1iZXIgZm9yIHRoYXQgbWVhc3VyZW1lbnQgd2l0aCBpbml0aWFsIHZpc2l0DQogICAgYXMgMC4NCg0KYGBge3J9DQpzdW1tYXJ5KHRvZSkNCmBgYA0KDQpFYWNoIHBhdGllbnQgaXMgc2NoZWR1bGVkIHRvIHZpc2l0IGFmdGVyIGEgbW9udGggZm9yIHRoZSBmaXJzdCB0aHJlZQ0KdmlzaXRzLCB0aGVuIG9uY2UgZXZlcnkgdGhyZWUgbW9udGhzIGZvciB0aGUgbGFzdCAzIHZpc2l0cywgYnV0IHRoYXQNCmRvZXNuJ3QgYWx3YXlzIChhbG1vc3QgbmV2ZXIpIGhhcHBlbnMuIFNvIGxldCdzIGxvb2sgYXQgdGhlIGF2ZXJhZ2UgdGltZQ0KYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBlYWNoIHZpc2l0IGluIG1vbnRoczoNCg0KYGBge3IgdGltZSBieSB2aXNpdH0NCnRvZSB8PiANCiAgc3VtbWFyaXplKA0KICAgIC5ieSA9IHZpc2l0MCwNCiAgICBtb250aF9hdmcgPSBtZWFuKG1vbnRoKSwNCiAgICBtb250aF9zZCA9IHNkKG1vbnRoKQ0KICApDQpgYGANCg0KV2hpbGUgdGhlIGF2ZXJhZ2UgaXMgY2xvc2UgdG8gdGhlIHNjaGVkdWxlZCB0aW1lLCB0aGUgc3ByZWFkIGluIHRpbWUNCmJldHdlZW4gdmlzaXRzIGdyb3dzIHRoZSBsb25nZXIgdGhlIHN0dWR5IGdvZXMgb24gKGxlc3MgY29uc2lzdGVudGx5DQpzaG93aW5nIGR1cmluZyB0aGUgc2NoZWR1bGVkIGFwcG9pbnRtZW50KS4NCg0KIyMgRURBOiBQbG90dGluZyB0aGUgZGF0YQ0KDQpMZXQncyBzdGFydCBieSBjcmVhdGluZyBzb21lIGRhdGEgdmlzdWFsaXphdGlvbnMgdG8gZ2V0IGFuIGlkZWEgb2YgaG93DQp0aGUgZGF0YSBsb29rDQoNCiMjIyMgQm94IHBsb3RzIG9mIFJlc3BvbnNlIHZzIE1vbnRoIHNlcGFyYXRlZCBieSBUcmVhdG1lbnQNCg0KYGBge3IgYm94cGxvdDF9DQpnZ3Bsb3QoDQogIGRhdGEgPSB0b2UsDQogIG1hcHBpbmcgPSBhZXMoDQogICAgeCA9IG1vbnRoLCANCiAgICB5ID0gcmVzcG9uc2UsIA0KICAgIGZpbGwgPSB0cmVhdG1lbnQNCiAgKQ0KKSArIA0KICBnZW9tX2JveHBsb3QoKSArDQogIHRoZW1lX2J3KCkNCmBgYA0KDQpUaGUgYmFzaWMgYm94IHBsb3QgZG9lc24ndCBpbmRpY2F0ZSBtdWNoIG9mIGEgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSB0d28NCnRyZWF0bWVudHMgb24gc2V2ZXJpdHkgb3ZlciB0aW1lLiBMZXQncyBsb29rIGF0IHRoZSAnc3VjY2VzcyBwZXJjZW50Jw0KKG5vbmUvbWlsZCkgYnkgdmlzaXQgYW5kIHRyZWF0bWVudCwgaWdub3JpbmcgdGhlIHN1YmplY3QNCg0KIyMjIyBTY2F0dGVycGxvdCBmb3Igc3VjY2VzcyBwY3QgYnkgdmlzaXQgYW5kIHRyZWF0bWVudCwgaWdub3Jpbmcgc3ViamVjdA0KDQpgYGB7ciBzdWNjZXNzIHNjYXR0ZXJwbG90fQ0KdG9lIHw+IA0KICBtdXRhdGUoDQogICAgLmJ5ID0gdmlzaXQwLA0KICAgIGF2Z19tb250aCA9IHJvdW5kKG1lYW4obW9udGgpKQ0KICApIHw+IA0KICAjIENvdW50aW5nIHRoZSBudW1iZXIgb2YgcGF0aWVudHMgYXQgZWFjaCB2aXNpdC9yZXNwb25zZS90cmVhdG1lbnQgY29tYm8NCiAgc3VtbWFyaXplKA0KICAgIC5ieSA9IGMoYXZnX21vbnRoLCB0cmVhdG1lbnQsIHJlc3BvbnNlKSwNCiAgICBuX29icyA9IG4oKQ0KICApIHw+IA0KICAjIFByb3BvcnRpb24gb2YgcGF0aWVudHMgd2l0aCBlYWNoIHJlc3BvbnNlIGZvciBlYWNoIHZpc2l0IHRyZWF0bWVudCBjb21ibw0KICBtdXRhdGUoDQogICAgLmJ5ID0gYyhhdmdfbW9udGgsIHRyZWF0bWVudCksDQogICAgYmFkX3Byb3AgPSBuX29icy9zdW0obl9vYnMpDQogICkgfD4gDQogICMgb25seSBtb2RlcmF0ZSBhbmQgc2V2ZXJlIHJvd3MNCiAgZmlsdGVyKA0KICAgIHJlc3BvbnNlID09ICdtb2RlcmF0ZS9zZXZlcmUnDQogICkgfD4gDQogICMgTGluZSBQbG90DQogIGdncGxvdCgNCiAgICBtYXBwaW5nID0gYWVzKA0KICAgICAgeCA9IGF2Z19tb250aCwgDQogICAgICB5ID0gYmFkX3Byb3AsIA0KICAgICAgY29sb3IgPSB0cmVhdG1lbnQsIA0KICAgICAgd2VpZ2h0ID0gbl9vYnMNCiAgICApDQogICkgKyANCiAgZ2VvbV9wb2ludCgNCiAgICBzaXplPTIsDQogICAgc2hvdy5sZWdlbmQgPSBGDQogICkgKyANCiAgDQogIGdlb21fbGluZSgpICsgDQogIA0KICBzY2FsZV94X2NvbnRpbnVvdXMoDQogICAgYnJlYWtzID0gdG9lIHw+IA0KICAgICAgc3VtbWFyaXplKC5ieSA9IHZpc2l0MCwgbW9udGhfYXZnID0gbWVhbihtb250aCkpIHw+IA0KICAgICAgcHVsbChtb250aF9hdmcpIHw+IA0KICAgICAgcm91bmQoKSwNCiAgICBtaW5vcl9icmVha3MgPSBOVUxMDQogICkgKyANCiAgDQogIGxhYnMoDQogICAgeCA9ICJNb250aHMgYWZ0ZXIgSW5pdGl2YWwgVmlzaXQiLA0KICAgIHkgPSAiUHJvcG9ydGlvbiB3aXRoIE1vZGVyYXRlIG9yIFNldmVyZSBTZXBhcmF0aW9uIiwNCiAgICB0aXRsZSA9ICJQcm9wb3J0aW9uIG9mIFBhdGllbnRzIHdpdGggTW9kZXJhdGUgb3IgU2V2ZXJlIFNlcGFyYXRpb24iLA0KICAgIHN1YnRpdGxlID0gIkJ5IE51bWJlciBvZiBWaXNpdHMgYW5kIFRyZWF0bWVudCIsDQogICAgY29sb3IgPSAiTWVkaWNhdGlvbiINCiAgKSArIA0KICB0aGVtZV9idygpICsgDQogIHRleHRfdGhlbWUgKyANCiAgDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdpbnNpZGUnLA0KICAgIGxlZ2VuZC5wb3NpdGlvbi5pbnNpZGUgPSBjKDAuOSwgMC44NSksDQogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsPU5BKQ0KICApICsgDQogIA0KICBzY2FsZV9jb2xvcl9kaXNjcmV0ZSgNCiAgICBsYWJlbHMgPSBjKCJJdHJhY29uemFvbGUiLCAiVGVyYmluYWZpbmUiKSwNCiAgICBndWlkZT0gZ3VpZGVfbGVnZW5kKHJldmVyc2U9VCkNCiAgKQ0KDQpgYGANCg0KV2UgY2FuIHNlZSBhdCB0aGUgc3RhcnQgb2YgdGhlIHN0dWR5IHRoYXQgYm90aCBtZWRpY2F0aW9ucyBoYWQgYWJvdXQgYQ0KMzglIG1vZGVyYXRlIG9yIHNldmVyZSBzZXBhcmF0aW9uIHJhdGUuIEFmdGVyIHRoZSBzdWJqZWN0cyBzdGFydGVkDQp0YWtpbmcgdGhlaXIgcmFuZG9tbHkgYXNzaWduZWQgbWVkaWNhdGlvbiwgVGVyYmluYWZpbmUgcGVyZm9ybWVkIGJldHRlcg0KZHVyaW5nIGVhY2ggbW9udGgsIGJ1dCBib3RoIGxldmVsZWQgb3V0IGFmdGVyIDYgbW9udGhzLCBub3Qgc2VlaW5nDQpzdWJzdGFudGlhbCBpbXByb3ZlbWVudCBhZnRlciB0aGVuLg0KDQpUaGlzIGdyYXBoIGlnbm9yZXMgdGhhdCB0aGUgZGF0YSBhcmUgcmVwZWF0ZWQgbWVhc3VyZW1lbnRzIG9uIDI5NCB1bmlxdWUNCnN1YmplY3RzLiBXZSBjYW4gdXNlIGEgKipHZW5lcmFsaXplZCBFc3RpbWF0aW5nIEVxdWF0aW9uIChHRUUpKiogdG8NCmVzdGltYXRlIHRoZSBlZmZlY3Qgb2YgdGhlIG1lZGlhdGlvbiBvZiBzZXBhcmF0aW9uIHNldmVyaXR5LCBhY2NvdW50aW5nDQpmb3IgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSBmb3IgZWFjaCBwYXRpZW50IQ0KDQpJbXBvcnRhbnQ6IFdlJ2xsIGxvb2sgYXQgdGhlIG51bWJlciBvZiBtaXNzaW5nIGNhc2VzIHBlciB2aXNpdCwgYnV0IHdlDQp3b24ndCBpbmNsdWRlIHRoZSBtaXNzaW5nbmVzcyBpbiBvdXIgYW5hbHlzaXMNCg0KIyMjIyBNaXNzaW5nIHZhbHVlcyBncmFwaDoNCg0KYGBge3J9DQp0b2UgfD4gDQogICMgTG9uZyB3YXkgb2YgaW5jbHVkaW5nIGEgbWlzc2luZyB2YWx1ZSBpbiBsb25nIGZvcm1hdCBkYXRhIDooDQogICMgUGl2b3QgdG8gd2lkZSB0byBpbmNsdWRlIGlmIHRoZSB2YWx1ZSBpcyBtaXNzaW5nDQogIHBpdm90X3dpZGVyKA0KICAgIGlkX2NvbHMgPSBjKHN1YmplY3RfaWQsIHRyZWF0bWVudCksDQogICAgdmFsdWVzX2Zyb20gPSByZXNwb25zZSwNCiAgICBuYW1lc19mcm9tID0gdmlzaXQwDQogICkgfD4gDQogICMgUGl2b3QgYmFjayB0byBsb25nDQogIHBpdm90X2xvbmdlcigNCiAgICBjb2xzID0gYygtc3ViamVjdF9pZCwgLXRyZWF0bWVudCksDQogICAgbmFtZXNfdG8gPSAndmlzaXQwJywNCiAgICB2YWx1ZXNfdG8gPSAncmVzcG9uc2UnDQogICkgfD4gDQogICMgQ2FsY3VsYXRpbmcgdGhlIG1pc3NpbmcgcGVyY2VudGFnZSBvZiBlYWNoIGNvbWJvIG9mIHRyZWF0bWVudCBhbmQgdmlzaXQNCiAgbXV0YXRlKA0KICAgIC5ieSA9IGModHJlYXRtZW50LCB2aXNpdDApLA0KICAgIG1pc3NpbmdfcHJvcCA9IG1lYW4oaXMubmEocmVzcG9uc2UpKQ0KICApIHw+IA0KICAjIGxpbmUgZ3JhcGggb2YgdGhlIG1pc3NpbmcgcGVyY2VudGFnZSBvZiB2aXNpdA0KICBnZ3Bsb3QoDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHggPSB2aXNpdDAsDQogICAgICB5ID0gbWlzc2luZ19wcm9wLA0KICAgICAgY29sb3IgPSB0cmVhdG1lbnQsDQogICAgICBncm91cCA9IHRyZWF0bWVudA0KICAgICkNCiAgKSArDQogIGdlb21fbGluZSgNCiAgICBsaW5ld2lkdGggPSAxDQogICkgKyANCiAgDQogIGxhYnMoDQogICAgeCA9ICJWaXNpdCBOdW1iZXIiLA0KICAgIHkgPSAiUHJvcG9ydGlvbiBvZiBOby1TaG93IFBhdGllbnRzIiwNCiAgICB0aXRsZSA9ICJQcm9wb3J0aW9uIG9mIE5vLVNob3cgUGF0aWVudHMgYnkgVHJlYXRtZW50IGFuZCBWaXNpdCIsDQogICAgY29sb3IgPSAiTWVkaWNhdGlvbiINCiAgKSArIA0KICB0aGVtZV9idygpICsgDQogIHRleHRfdGhlbWUgKyANCiAgDQogIHRoZW1lKA0KICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICdpbnNpZGUnLA0KICAgIGxlZ2VuZC5wb3NpdGlvbi5pbnNpZGUgPSBjKDAuOCwgMC4xNSksDQogICAgbGVnZW5kLmtleSA9IGVsZW1lbnRfcmVjdChmaWxsID0gTkEpDQogICkgKyANCiAgDQogIHNjYWxlX2NvbG9yX2Rpc2NyZXRlKA0KICAgIGxhYmVscyA9IGMoIkl0cmFjb256YW9sZSIsICJUZXJiaW5hZmluZSIpLA0KICAgIGd1aWRlPSBndWlkZV9sZWdlbmQocmV2ZXJzZSA9IFQpDQogICkNCmBgYA0KDQpUaGUgZmlmdGggdmlzaXQgaGFkIGFsbW9zdCAyMCUgb2YgdGhlIG5lYXJseSAzMDAgcGF0aWVudHMgbm90IHNob3cgdXAuDQpJdCB3b3VsZCBiZSBhIGdvb2QgaWRlYSB0byBmb2xsb3cgdXAgd2l0aCB0aGVtIHRvIHNlZSB3aHkgdGhleSBtaXNzZWQNCnRoZSBmaWZ0aCB2aXNpdCwgZXNwZWNpYWxseSBmb3IgdGhvc2Ugd2hvIGFycml2ZWQgZm9yIHRoZSBmaW5hbCB2aXNpdC4NCg0KIyMgRml0dGluZyB0aGUgbW9kZWwgdXNpbmcgYGdlZWdsbSgpYA0KDQpXZSBjYW4gdXNlIHRoZSBmdW5jdGlvbiBgZ2VlZ2xtKClgIHRvIGZpdCB0aGUgIm1vZGVsIiBhIHZlcnkgc2ltaWxhciB3YXkNCndlIHdvdWxkIHVzaW5nIGBnbG0oKWAgd2l0aCBgZm9ybXVsYWAsIGBmYW1pbHlgLCBhbmQgYGRhdGFgDQoNCkl0IGFsc28gaGFzIHNvbWUgYWRkaXRpb25hbCBhcmd1bWVudHMgdGhhdCB3ZSBuZWVkIHRvIHN1cHBseToNCg0KLSAgIGBpZGAgPSBjb2x1bW4gdGhhdCBzdG9yZXMgdGhlIGNsdXN0ZXIgaWQNCg0KICAgIC0gICBgc3ViamVjdF9pZGANCg0KLSAgIGBzdGQuZXJyID0gInNhbi5zZSJgOiB0ZWxscyBpdCB0byBjYWxjdWxhdGUgdGhlIHN0YW5kYXJkIGVycm9ycw0KICAgIHVzaW5nIHRoZSBzYW5kd2ljaCBlc3RpbWF0b3INCg0KLSAgIGBjb3JzdHJgID0gdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSB0byB1c2UNCg0KICAgIC0gICBub3Qgc3BlY2lmeWluZyBhc3N1bWVzIGFuIGluZGVwZW5kZW50IG1vZGVsIChpZSBhbGwgY29ycmVsYXRpb25zID0gMCkNCg0KICAgIC0gICBmb3IgY2x1c3RlcmVkIGRhdGEgKGxpa2UgY2hpbGRyZW4gaW4gY2xhc3Nyb29tcyksIHdlIHVzZSAnZXhjaGFuZ2FibGUnIHNpbmNlIGVhY2ggaW5kaXZpZHVhbCBpbiBhIGNsdXN0ZXIgYXJlIGV4Y2hhbmdhYmxlIHdpdGggYW55b25lIGVsc2UgaW4gdGhlIGNsdXN0ZXINCg0KICAgIC0gICBmb3IgbG9uZ2l0dWRpbmFsIGRhdGEsIHdlIHR5cGljYWxseSB1c2UgJ2FyMScgc2luY2Ugb2JzZXJ2YXRpb25zIG5lYXIgZWFjaCBvdGhlciBpbiB0aW1lIGFyZSBtb3JlIHNpbWlsYXIgdGhhbiB0aG9zZSBmYXIgYXBhcnQgaW4gdGltZQ0KDQojIyMgSW5kZXBlbmRlbmNlIE1vZGVsDQoNCmBgYHtyIGluZGVwX21vZGVsfQ0KDQp0b2VfaW5kZXAgPC0gDQogIGdlZWdsbSgNCiAgICBmb3JtdWxhID0gc2V2ZXJlIH4gdHJlYXRtZW50ICogbW9udGgsIA0KICAgIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSwNCiAgICBpZCA9IHN1YmplY3RfaWQsIA0KICAgIGRhdGEgPSB0b2UsIA0KICAgIHN0ZC5lcnIgPSAic2FuLnNlIiwgDQogICAgY29yc3RyID0gJ2luZGVwZW5kZW5jZScsDQogICAgc2NhbGUuZml4ID0gVCAgIyBBc3N1bWVzIGEgJ2NvbnN0YW50JyB2YXJpYW5jZSANCiAgKQ0KDQojIEVzdGltYXRlcyBhbmQgcmVzdWx0cw0KYnJvb206OnRpZHkodG9lX2luZGVwKQ0KDQojIEZpdCBzdGF0cw0KYnJvb206OmdsYW5jZSh0b2VfaW5kZXApDQpgYGANCg0KSG93IGRvZXMgaXQgY29tcGFyZSB0byBhIGZpeGVkIGdsbT8gTGV0J3MgdGFrZSBhIGxvb2shDQoNCmBgYHtyIGdsbX0NCmdsbSgNCiAgZm9ybXVsYSA9IHNldmVyZSB+IHRyZWF0bWVudCAqIG1vbnRoLCANCiAgZmFtaWx5ID0gYmlub21pYWwobGluaz0ibG9naXQiKSwNCiAgZGF0YSA9IHRvZQ0KKSB8PiANCiAgYnJvb206OnRpZHkoKQ0KYGBgDQoNCk5vdGUgdGhlIGVzdGltYXRlcyBhcmUgdGhlIHNhbWUgKHlheSEpLCBidXQgdGhlIHN0YW5kYXJkIGVycm9ycyBhcmUNCmRpZmZlcmVudCAoYm9vISkNCg0KTGV0J3MgbG9vayBhdCBhIGNvdXBsZSBvZiBkaWZmZXJlbnQgbW9kZWxzDQoNCiMjIyMgTW9kZWwgZm9yIHRvZSBkYXRhIHVzaW5nIGV4Y2hhbmdlYWJsZSBjb3JyZWxhdGlvbiBzdHJ1Y3R1cmUNCg0KV2UgY2FuIGVhc2lseSB1cGRhdGUgYSBzYXZlZCBtb2RlbCBpbiBSIHVzaW5nIHRoZSBgdXBkYXRlKClgIGZ1bmN0aW9uLg0KWW91IGdpdmUgaXQgYSBtb2RlbCBhbmQgdGhlIGFyZ3VtZW50cyBvZiB0aGUgb3JpZ2luYWwgZnVuY3Rpb24geW91IHdhbnQNCnRvIGNoYW5nZS4NCg0KSWYgd2Ugd2FudCB0byB1cGRhdGUgdGhlIGNvcnJlbGF0aW9uIHN0cnVjdHVyZSwgd2UgY2FuIHVzZSBgdXBkYXRlKClgIQ0KDQpgYGB7ciBjb3JfZXhjaH0NCnRvZV9leCA8LSB1cGRhdGUodG9lX2luZGVwLCBjb3JzdHIgPSAiZXhjaGFuZ2VhYmxlIikgDQoNCmJyb29tOjp0aWR5KHRvZV9leCkNCmJyb29tOjpnbGFuY2UodG9lX2V4KQ0KYGBgDQoNCiMjIyMgbW9kZWwgZm9yIHRvZSBkYXRhIHVzaW5nIEFSMSBtb2RlbA0KDQpgYGB7ciBnZWVfYXJpbWExfQ0KdG9lX2FyIDwtIHVwZGF0ZSh0b2VfaW5kZXAsIGNvcnN0cj0gImFyMSIpIA0KYnJvb206OnRpZHkodG9lX2FyKQ0KYnJvb206OmdsYW5jZSh0b2VfYXIpDQpgYGANCg0KIyMjIyBNb2RlbCB1c2luZyB1bmRlZmluZWQgc3RydWN0dXJlDQoNClRvIGJ1aWxkIGFuIHVuc3RydWN0dXJlZCBjb3JyZWxhdGlvbiBtYXRyaXgsIHdlIGNhbiB1c2UNCmBjb3JzdHIgPSAndW5zdHJ1Y3R1cmVkJ2AuDQoNCmBgYHtyIGdlZV91bnN0cnVjfQ0KdG9lX3VuIDwtIHVwZGF0ZSh0b2VfaW5kZXAsIGNvcnN0ciA9ICJ1bnN0cnVjdHVyZWQiKSANCmJyb29tOjp0aWR5KHRvZV91bikNCmBgYA0KDQpMZXQncyBjb21wYXJlIHRoZSB0ZXN0IHN0YXRpc3RpY3Mgb2YgYWxsIDQgbW9kZWxzOg0KDQpgYGB7ciBnZWVfY29tcGFyZX0NCmRhdGEuZnJhbWUoDQogIEluZGVwZW5kZW5jZSA9IGNvZWYodG9lX2luZGVwKSAvICh2Y292KHRvZV9pbmRlcCkgfD4gZGlhZygpIHw+IHNxcnQoKSksIA0KICBleGNoYW5nZWFibGUgPSBjb2VmKHRvZV9leCkgICAgLyAodmNvdih0b2VfZXgpIHw+IGRpYWcoKSB8PiBzcXJ0KCkpLA0KICBBUjEgICAgICAgICAgPSBjb2VmKHRvZV9hcikgICAgLyAodmNvdih0b2VfYXIpIHw+IGRpYWcoKSB8PiBzcXJ0KCkpLCANCiAgVW5zdHJ1Y3R1cmVkID0gY29lZih0b2VfdW4pICAgIC8gKHZjb3YodG9lX3VuKSB8PiBkaWFnKCkgfD4gc3FydCgpKQ0KKSANCg0KDQpgYGANCg0KV2hpbGUgbm9uZSBvZiB0aGUgcmVzdWx0cyBkcmFzdGljYWxseSBkaXNhZ3JlZSB3aXRoIG9uZSBhbm90aGVyLCB3aGljaA0KY29ycmVsYXRpb24gc3RydWN0dXJlIHNob3VsZCB3ZSB1c2U/DQoNCkxpa2Ugd2UgZG8gd2l0aCBvdGhlciBtb2RlbCBzZWxlY3Rpb24sIHdlIGNhbiB1c2UgYW4gKipJbmZvcm1hdGlvbg0KQ3JpdGVyaW9uKiogdG8gaGVscCBkZWNpZGUhDQoNCiMjIyBVc2luZyBgUUlDKClgIHRvIGNvbXBhcmUgd2hpY2ggY29ycmVsYXRpb24gc3RydWN0dXJlIGlzIHByZWZlcnJlZA0KDQoqKlF1YXNpIEluZm9ybWF0aW9uIENyaXRlcmlvbiAoUUlDKSoqIGlzIGEgcmVjb21tZW5kZWQgbWV0aG9kIHRvIGNvbXBhcmUNCm1vZGVscyB3aXRoIHRoZSBzYW1lIG1lYW4gc3RydWN0dXJlIChzYW1lIGZvcm11bGEgcHJvdmlkZWQgYnkNCmBnZWVnbG0oKWApIGJ1dCBkaWZmZXJlbnQgYGNvcl9zdHJgLg0KDQpJZiB3ZSB3YW50IHRvIGNvbXBhcmUgbW9kZWxzIHdpdGggZGlmZmVyZW50IG1lYW4gc3RydWN0dXJlcyBidXQgdGhlIHNhbWUNCmNvcnJlbGF0aW9uIHN0cnVjdHVyZSwgd2UgY2FuIHVzZSAqKlFJQ3UqKiAod2hpY2ggd2UgYXJlbid0LCBidXQgaW4gY2FzZQ0KeW91IHdhbnQgdG8hKQ0KDQpgYGB7ciBxaWN9DQpRSUModG9lX2luZGVwLCB0b2VfZXgsIHRvZV9hciwgdG9lX3VuKSB8PiANCiAgZGF0YS5mcmFtZSgpIA0KYGBgDQoNCkxpa2Ugb3RoZXIgaW5mb3JtYXRpb24gY3JpdGVyaW9uLCB3ZSB3YW50IHRvIHVzZSBvbmVzIHRoYXQgaGF2ZSBhDQpzbWFsbGVyIHZhbHVlLg0KDQpBY2NvcmRpbmcgdG8gUUlDLCB0aGUgaW5kZXBlbmRlbnQgbW9kZWwgaXMgdGhlICJiZXN0IiwgZm9sbG93ZWQgYnkgdGhlDQpleGNoYW5nYWJsZSBhbmQgYXJpbWEgbW9kZWxzLCB0aGVuIHRoZSB1bnN0cnVjdHVyZWQuDQoNCkRlc3BpdGUgdGhlIHJlc3VsdHMgb2YgUUlDLCB3ZSdsbCBtb3ZlIGZvcndhcmQgd2l0aCB0aGUgYXJpbWEgbW9kZWwNCnNpbmNlIGl0IGlzIGxpa2VseSB0aGF0IHRoZSByZXNwb25zZXMgZm9yIGVhY2ggaW5kaXZpZHVhbCBhcmUgYXNzb2NpYXRlZA0KYW5kIGl0IGlzIHRoZSB0eXBpY2FsIG1vZGVsIHVzZWQgZm9yIGxvbmdpdHVkaW5hbCBkYXRhLg0KDQojIyBGaXR0aW5nIDMgZGlmZmVyZW50IG1vZGVscyB3aXRoIEFSMSBjb3JyZWxhdGlvbiBzdHJ1Y3R1cmVzOg0KDQpXZSdsbCBmaXQgdGhyZWUgZGlmZmVyZW50IG1lYW4gc3RydWN0dXJlcyBmb3IgdGhlIEFSMSBtb2RlbDoNCkludGVyYWN0aW9uLCBBZGRpdGl2ZSwgYW5kIE1vbnRoIG9ubHkuIFdlIGFscmVhZHkgaGF2ZSB0aGUgaW50ZXJhY3Rpb24NCm1vZGVsLCBgdG9lX2FyYC4gTm93IHdlIGp1c3QgbmVlZCB0byBmaXQgdGhlIG90aGVyIDINCg0KIyMjIEFkZGl0aXZlIG1vZGVsDQoNCldlIGNhbiByZW1vdmUgdGhlIGludGVyYWN0aW9uIHRlcm0gdXNpbmcgYHVwZGF0ZSgpYCBhbmQNCmAtdHJlYXRtZW50Om1vbnRoYC4gVGhlIGA6YCBpbiBhIGZvcm11bGEgaW5kaWNhdGVzIGp1c3QgdGhlIGludGVyYWN0aW9uDQp0ZXJtLiBPdGhlcndpc2UgdXNpbmcgYEEqQmAgd2lsbCByZW1vdmUgYEFgLCBgQmAsIGFuZCB0aGUgaW50ZXJhY3Rpb24hDQoNCmBgYHtyIGdlZV9hZGRpdGl2ZV9hcjF9DQp0b2VfYWRkX2FyMSA8LSB1cGRhdGUodG9lX2FyLCBmb3JtdWxhID0gLiB+IC4tIHRyZWF0bWVudDptb250aCkNCmBgYA0KDQojIyMgSnVzdCBNb250aCwgTm8gZWZmZWN0IGZvciBkaWZmZXJlbnQgdHJlYXRtZW50cw0KDQpgYGB7ciBnZWVfbW9udGhfYXIxfQ0KdG9lX21vbl9hcjEgPC0gdXBkYXRlKHRvZV9hciwgZm9ybXVsYSA9IHNldmVyZSB+IG1vbnRoKQ0KYGBgDQoNCiMjIyBVc2luZyBBTk9WQSB0byBjb21wYXJlIHRoZSByZXN1bHRzDQoNCldlIGNhbiBjb21wYXJlIHRoZSByZXN1bHRzIG9mIHRoZSBkaWZmZXJlbnQgbW9kZWxzIHVzaW5nIGBhbm92YSgpYDoNCg0KYGBge3IgZ2VlX2NvbXBhcmVfYXIxfQ0KYW5vdmEodG9lX2FyLCB0b2VfbW9uX2FyMSkNCmBgYA0KDQpObyBzdHJvbmcgZXZpZGVuY2Ugb2YgYW4gZWZmZWN0IG9mIHRyZWF0bWVudCBvdmVyIHRpbWUNCg0KI1NlZWluZyBpZiB3ZSBuZWVkIHRvIGluY2x1ZGUgdHJlYXRtZW50IGNvbXBhcmVkIHRvIHRoZSBpbnRlcmFjdGlvbg0KbW9kZWwgYW5vdmEodG9lX2FyLCB0b2VfQVJNb24pDQoNCiMjIyBDcmVhdGluZyBhIFdhbGQgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgZWFjaCBwYXJhbWV0ZXINCg0KV2UgY2FuIGNyZWF0ZSBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgZm9yIGVhY2ggcGFyYW1ldGVyIHVzaW5nIHRoZQ0KYGVzdGljb24oKWAgZnVuY3Rpb24gaW4gdGhlIGBkb0J5YCBwYWNrYWdlDQoNCmBlc3RpY29uKClgIHRha2VzIGEgbW9kZWwsIGxpbmVhciBjb21iaW5hdGlvbiBtYXRyaXgsIGFuZCBhZGRpdGlvbmFsbHkNCmNhbiBzcGVjaWZ5IGEgdmVjdG9yIG9mICRIXzAkIHZhbHVlcyB0byB0ZXN0IHRoZSBjb21iaW5hdGlvbnMNCg0KQnV0IHdlIGRvbid0IG5lZWQgYmV0YTAgdG8gY3JlYXRlIGEgY29uZmlkZW5jZSBpbnRlcnZhbCBmb3IgbW9kZWwNCmVmZmVjdHMNCg0KQ2FuIGFsc28gc3BlY2lmeSBgam9pbnQudGVzdCA9IFRgIGlmIHlvdSdkIGxpa2UgdG8gdGVzdCBhbGwgb2YgdGhlDQpoeXBvdGhlc2VzIHVzaW5nIGFuIG9tbmlidXMgKG9uZSBjb21iaW5lZCkgdGVzdC4gVXNlIGBGYCBpZiB5b3Ugd2FudCB0bw0KdGVzdCBpbmRpdmlkdWFsIGNvbWJpbmF0aW9ucw0KDQojIyMjIDk1JSBDSSBvbiB0aGUgb2RkcyBzY2FsZSBmb3IgdGhlIHBhcmFtZXRlcnM6DQoNCmBgYHtyIGNvbXBfbWF0cml4fQ0KIyBHZXR0aW5nIHRoZSB2ZWN0b3Igb2YgY29lZmZpY2llbnRzIGZvciB0aGUgYXIgbW9kZWwNCmdlZV9hcl9jb2VmIDwtIGNvZWYodG9lX2FyKQ0KDQojIEdldHRpbmcgdGhlIGNvbXBhcmlzb24gbWF0cml4IChhIGRpYWdvbmFsIG1hdHJpeCB3aGVyZSBlYWNoIHJvdyBjb3JyZXNwb25kcyB0byAxIHBhcmFtZXRlcikNCmRpYWdfY29tcF9tYXQgPC0gZGlhZyhsZW5ndGgoZ2VlX2FyX2NvZWYpKQ0KZGlhZ19jb21wX21hdA0KYGBgDQoNCmBlc3RpY29uKClgIGdpdmVzIHRoZSBlc3RpbWF0ZXMgYW5kIHRoZSBzdGFuZGFyZCBlcnJvcnMgb2YgdGhlDQpwYXJhbWV0ZXJzIGlmIHdlIHVzZSBhIGRpYWdvbmFsIGNvbXBhcmlzb24gbWF0cml4LCB3aGljaCBpcyB3aGF0IHdlDQpjcmVhdGVkIGluIGBkaWFnX2NvbXBfbWF0YA0KDQpgYGB7ciBnZWVfY29uZl9pbnR9DQptb2RlbF9lc3QgPC0gDQogIGVzdGljb24oDQogICAgb2JqID0gdG9lX2FyLCANCiAgICBMID0gZGlhZ19jb21wX21hdCwNCiAgICBiZXRhMCA9IHJlcCgwLCBsZW5ndGgoZ2VlX2FyX2NvZWYpKSAjIFZlY3RvciBvZiAwcyBmb3IgdGhlIG51bGwgaHlwb3RoZXNlcw0KICApIA0KIyBBZGRpbmcgdGhlIG5hbWVzIG9mIHRoZSBlZmZlY3RzIHRvIHRoZSByb3dzDQpyb3cubmFtZXMobW9kZWxfZXN0KSA8LSBuYW1lcyhnZWVfYXJfY29lZikgDQpyb3VuZChtb2RlbF9lc3QsNCkNCmBgYA0KDQpJZiB5b3Ugd2FudCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwsIHdlIGNhbiBleHRyYWN0IHRoZSB1cHBlciBhbmQgbG93ZXINCmJvdW5kcyBvZiB0aGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgdXNpbmcgYG9iaiRsd3JgIGFuZCBgb2JqJHVwcmANCg0KYGBge3J9DQptb2RlbF9vZGRzX0NJIDwtIA0KICBkYXRhLmZyYW1lKA0KICAgIEVzdGltYXRlID0gbW9kZWxfZXN0JGVzdGltYXRlLA0KICAgIExvd2VyICAgID0gbW9kZWxfZXN0JGx3ciwNCiAgICBVcHBlciAgICA9IG1vZGVsX2VzdCR1cHINCiAgKSB8Pg0KICAjIEV4cG9uZW50aWF0aW5nIHRvIGNhbGN1bGF0ZSB0aGUgb2RkcyByYXRpbw0KICBleHAoKQ0KDQpyb3cubmFtZXMobW9kZWxfb2Rkc19DSSkgPC0gcm93Lm5hbWVzKG1vZGVsX2VzdCkNCnJvdW5kKG1vZGVsX29kZHNfQ0ksNCkNCmBgYA0KDQpXZSBjYW4gYWxzbyBsb29rIGF0IGNvbWJpbmF0aW9ucyBvZiBmYWN0b3JzIGFzIHdlbGwuIExldCdzIGNoZWNrIDMNCkNvbWJpbmF0aW9ucyB1c2luZyB0aGUgSW50ZXJhY3Rpb24gTW9kZWw6DQoNCjEpICBUaGUgb2RkcyByYXRpbyBvZiBNb2RlcmF0ZSBvciBTZXZlcmUgU2VwYXJhdGlvbiAoTVNTKSBmb3IgdGhlIHR3bw0KICAgIHRyZWF0bWVudHMgYWZ0ZXIgNiBtb250aHMNCg0KMikgIFRoZSBvZGRzIGEgcGVyc29uIHRha2luZyBpdHJhY29uemFvbGUgaGFzIG1vZGVyYXRlIG9yIHNldmVyZQ0KICAgIHNlcGFyYXRpb24gYWZ0ZXIgNiBtb250aHMgKGludGVyY2VwdCArIHNsb3BlXCo2KQ0KDQozKSAgVGhlIG9kZHMgcmF0aW8gdGhhdCBzb21lb25lIHRha2luZyB0ZXJiaW5hZmluZSBoYXMgTVNTIGEgbW9udGggbGF0ZXINCiAgICAoc2xvcGUgKyBpbnRlcmFjdGlvbikNCg0KYGBge3IgY29udHJhc3RfaW50ZXJ2YWxzX2NvbXBfbWF0fQ0KIyBDb21iaW5hdGlvbiBtYXRyaXggKGNtKToNCnRvZV9jb21wX21hdCA8LSANCiAgcmJpbmQoDQogICAgYygwLDEsMCw2KSwgIyBPZGRzIHJhdGlvIG9mIE1TUyBhZnRlciA2IE1vbnRocyBmb3IgVGVyYiB2cyBJdHJhIA0KICAgIGMoMCwwLDYsMCksICMgT2RkcyByYXRpbyBvZiBpdHJhY29uemFvbGUgdXNlciBoYXMgTVNTIGFmdGVyIDYgbW9udGhzIA0KICAgIGMoMCwwLDEsMSkgICMgT2RkcyByYXRpbyBvZiB0ZXJiaW5hZmluZSB1c2VyIGhhcyBNU1MgYWZ0ZXIgYW4gYWRkaXRpb25hbCBtb250aA0KICApDQoNCnRvZV9jb21wX21hdA0KDQoNCiMgQ0kgZm9yIHRoZSBhZGRpdGl2ZSBtb2RlbA0KdG9lX2VzdCA8LSBlc3RpY29uKHRvZV9hciwgTCA9IHRvZV9jb21wX21hdCkgDQpyb3cubmFtZXModG9lX2VzdCkgPC0gYygiT1IgVHJ0OiA2IE1vbnRocyIsICJPUiBJdHJhOiA2IE1vbnRocyIsICJPUiBUZXJiOiBNb250aCIpIA0Kcm91bmQodG9lX2VzdCw0KSB8PiBkYXRhLmZyYW1lKCkNCg0KIyBDYW4gZ2V0IHRoZSB1cHBlciBhbmQgbG93ZXIgZXN0aW1hdGVzIHVzaW5nIFwkbHdyIGFuZCAkdXByDQp0b2Vfb2Rkc19DSSA8LSANCiAgZGF0YS5mcmFtZSgNCiAgICBFc3RpbWF0ZSA9IHRvZV9lc3QkZXN0aW1hdGUsDQogICAgTG93ZXIgICAgPSB0b2VfZXN0JGx3ciwNCiAgICBVcHBlciAgICA9IHRvZV9lc3QkdXByDQogICkgfD4NCiAgZXhwKCkNCg0Kcm93Lm5hbWVzKHRvZV9vZGRzX0NJKSA8LSByb3cubmFtZXModG9lX2VzdCkgDQpyb3VuZCh0b2Vfb2Rkc19DSSw0KQ0KYGBgDQoNCiMjIFNjYXR0ZXJwbG90IHdpdGggR0VFIEVzdGltYXRlcw0KDQpgYGB7ciBncmFwaHNfb2JzX2dlZV9nbG0sIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9DQp0cnRfbGFiZWwgPC0gYygiSXRyYWNvbnphb2xlIiwgIlRlcmJpbmFmaW5lIikNCm5hbWVzKHRydF9sYWJlbCkgPC0gYygiSSIsIlQiKQ0KDQojIFNjYXR0ZXJwbG90IGZvciBzdWNjZXNzIHBjdCBieSB2aXNpdCBhbmQgdHJlYXRtZW50LCBpZ25vcmluZyBzdWJqZWN0DQoNCmRhdGEuZnJhbWUoDQogIHRvZSwgDQogIGdlZV9NU1MgICA9IHByZWRpY3QodG9lX2FyLCAgICB0eXBlID0gInJlc3BvbnNlIiksIA0KICBsb2dpdF9NU1MgPSBwcmVkaWN0KHRvZV9pbmRlcCwgdHlwZSA9ICJyZXNwb25zZSIpDQopIHw+DQogIG11dGF0ZSgNCiAgICAuYnkgPSB2aXNpdDAsDQogICAgYXZnX21vbnRoID0gbWVhbihtb250aCkNCiAgKSB8Pg0KICBzdW1tYXJpemUoDQogICAgLmJ5ID0gYyhhdmdfbW9udGgsIHRyZWF0bWVudCksDQogICAgbl9vYnMgPSBuKCksIA0KICAgIHByb3BfMCA9IHN1bShzZXZlcmUgPT0gMSkvbl9vYnMsIA0KICAgIHByZWRfZ2VlID0gbWVhbihnZWVfTVNTKSwgDQogICAgcHJlZF9sb2dpdCA9IG1lYW4obG9naXRfTVNTKQ0KICApIHw+DQogIG11dGF0ZSgNCiAgICB0cmVhdG1lbnQgPSBpZl9lbHNlKHRyZWF0bWVudCA9PSAiSSIsICJJdHJhY29uemFvbGUiLCAiVGVyYmluYWZpbmUiKQ0KICApIHw+DQogIGdncGxvdCgNCiAgICBtYXBwaW5nID0gYWVzKA0KICAgICAgeCA9IGF2Z19tb250aCwgDQogICAgICB5ID0gcHJvcF8wDQogICAgKQ0KICApICsNCiAgDQogIGZhY2V0X3dyYXAoDQogICAgZmFjZXRzID0gdmFycyh0cmVhdG1lbnQpLA0KICAgIG5yb3cgPSAyDQogICkgKw0KICANCiAgIyBPYnNlcnZlZCBQcm9wb3J0aW9ucw0KICANCiAgZ2VvbV9saW5lKA0KICAgIG1hcHBpbmcgPSBhZXMoY29sb3I9Ik9ic2VydmVkIikNCiAgKSArIA0KICBnZW9tX3BvaW50KA0KICAgIG1hcHBpbmcgPSBhZXMoY29sb3I9Ik9ic2VydmVkIikNCiAgKSArDQogIA0KICAjIEdFRSBQcm9wb3J0aW9ucyB3aXRoIFVuc3RydWN0dXJlZCBDb3JyZWxhdGlvbnMNCiAgDQogIGdlb21fbGluZSgNCiAgICBtYXBwaW5nID0gYWVzKA0KICAgICAgeSA9IHByZWRfZ2VlLCANCiAgICAgIGNvbG9yID0gIkdFRSINCiAgICApLCANCiAgICBsaW5ldHlwZT0iZGFzaGVkIg0KICApICsNCiAgZ2VvbV9wb2ludCgNCiAgICBtYXBwaW5nID0gYWVzKA0KICAgICAgeSA9IHByZWRfZ2VlLA0KICAgICAgY29sb3IgPSAiR0VFIg0KICAgICksIA0KICAgIHNoYXBlID0gMTUNCiAgKSArDQogIA0KICAjIExvZ2lzdGljIFByb3BvcnRpb25zDQogIA0KICBnZW9tX2xpbmUoDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHkgPSBwcmVkX2xvZ2l0LCANCiAgICAgIGNvbG9yID0gIkxvZ2lzdGljIg0KICAgICksIA0KICAgIGxpbmV0eXBlPSJsb25nZGFzaCINCiAgKSArDQogIGdlb21fcG9pbnQoDQogICAgbWFwcGluZyA9IGFlcygNCiAgICAgIHkgPSBwcmVkX2xvZ2l0LA0KICAgICAgY29sb3IgPSAiTG9naXN0aWMiDQogICAgKSwgDQogICAgc2hhcGUgPSAxNw0KICApICsNCiAgIyBBdmVyYWdlIG1vbnRoIHBlciB2aXNpdCBvbiB0aGUgeC1heGlzDQogIHNjYWxlX3hfY29udGludW91cygNCiAgICBicmVha3MgPSB0b2UgfD4NCiAgICAgIHN1bW1hcml6ZSguYnkgPSB2aXNpdDAsIGF2Z19tb250aCA9IG1lYW4obW9udGgpKSB8Pg0KICAgICAgcHVsbChhdmdfbW9udGgpIHw+DQogICAgICByb3VuZCgpLCANCiAgICBtaW5vcl9icmVha3MgPSBOVUxMDQogICkgKw0KICANCiAgbGFicygNCiAgICB4ID0gIk1vbnRocyBhZnRlciBJbml0aXZhbCBWaXNpdCIsIA0KICAgIHkgPSAiUHJvcG9ydGlvbiB3aXRoIE1vZGVyYXRlIG9yIFNldmVyZSBTZXBhcmF0aW9uIiwgDQogICAgdGl0bGUgPSAiUHJvcG9ydGlvbiBvZiBQYXRpZW50cyB3aXRoIE1vZGVyYXRlIG9yIFNldmVyZSBTZXBhcmF0aW9uIiwgDQogICAgc3VidGl0bGUgPSAiQnkgTW9udGggYW5kIFRyZWF0bWVudCINCiAgKSArDQogIHRoZW1lX2J3KCkgKw0KICB0ZXh0X3RoZW1lICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0iYm90dG9tIikgKw0KICANCiAgc2NhbGVfY29sb3VyX21hbnVhbCgNCiAgICBuYW1lID0gJ1Byb2JhYmlsaXR5IFR5cGUnLCANCiAgICB2YWx1ZXMgPSBjKCdPYnNlcnZlZCc9JyNGODc2NkQnLA0KICAgICAgICAgICAgICAgJ0dFRSc9JyMwMEJGQzQnLCANCiAgICAgICAgICAgICAgICJMb2dpc3RpYyI9IiNDNzdDRkYiKQ0KICApDQpgYGANCg==