Introduction

We will simulate some data to illustrate the relationship between heritability estimates derived from a regression of offspring phenotype on mid-parent value and those derived from analysis of half-sibs. To do the simulation, we’ll assume that there are a large number of loci that affect the trait and that each locus has a small effect. With this assumption, we can approximate the genotype of each individual as a random variable drawn from a normal distribution. The R code below implements the folloowing simple algorithm:

  1. Select a genotype for the sire at random from a normal distribution with mean \(mu\) and variance \(\sigma^2_g\). Call that genotype \(x_p\).

  2. Select a genotype for the dam at random from a normal distribution with the same mean and variance. Call that genotype \(x_m\).

  3. Calculate the mid-parent genotypic value as \(x_{mp} = \frac{x_m + x_p}{2}\).

  4. Set the offspring genotype to \(x_{mp}\).

  5. Repeat steps #2 and #3 n_dams times.

  6. Repeat steps #1-#5 n_sires times.

  7. Set phenotypes for the maternal parent, the paternal parent, and the offspring by drawing them from normal distributions with the appropriate means and varience \(\sigma^2_e\)

  8. Record the paternal ID, the genotype of each parent, the mid-parent genotypic value, the phenotype of each parent, the mid-parent phenotypic value, and the offspring phenotype.

NOTE: I am using set.seed(1234) to ensure that if you run this code on your own, you’ll get the same results as I do. If you erase or comment out that line and rund the code, your results will differ slightly from mine.

library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ───────────────────────────── tidyverse 1.3.1 ──
✓ ggplot2 3.3.5     ✓ purrr   0.3.4
✓ tibble  3.1.4     ✓ dplyr   1.0.7
✓ tidyr   1.1.3     ✓ stringr 1.4.0
✓ readr   2.0.1     ✓ forcats 0.5.1
── Conflicts ──────────────────────────────── tidyverse_conflicts() ──
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
rm(list = ls())

set.seed(1234)

n_sires <- 100
n_dams <- 10
mu <- 50
sigma_2_g <- 25
sigma_2_e <- 9

dat <- tibble(pat_ID = NA,
              ind_ID = NA,
              mat_g = NA,
              pat_g = NA,
              mid_g = NA,
              mat_p = NA,
              pat_p = NA,
              mid_p = NA,
              off_p = NA)
ct <- 0
for (i in 1:n_sires) {
  patg <- rnorm(1, mu, sqrt(sigma_2_g))
  patp <- rnorm(1, patg, sqrt(sigma_2_e))
  for (j in 1:n_dams) {
    ct <- ct + 1
    matg <- rnorm(1, mu, sqrt(sigma_2_g))
    matp <- rnorm(1, matg, sqrt(sigma_2_e))
    midg <- (matg + patg)/2
    offp <- rnorm(1, midg, sqrt(sigma_2_e))
    dat <- add_row(dat,
                   pat_ID = i,
                   ind_ID = ct,
                   mat_g = matg,
                   pat_g = patg,
                   mid_g = midg,
                   mat_p = matp,
                   pat_p = patp,
                   mid_p = (matp + patp)/2,
                   off_p = offp)
  }
}
dat <- dat %>%
  filter(!is.na(pat_ID)) %>%
  mutate(pat_ID = factor(pat_ID),
         ind_ID = factor(ind_ID))
pat_g_var <- var(unique(dat[, c("pat_ID", "pat_g")])$pat_g)
pat_p_var <- var(unique(dat[, c("pat_ID", "pat_p")])$pat_p)
variances <- tibble(Parent = c("Sire", "Dam"),
                    Genotypic = round(c(pat_g_var, var(dat$mat_g)), 3),
                    Environmental = round(c(pat_p_var - pat_g_var,
                                            var(dat$mat_p) -
                                              var(dat$mat_g)), 3),
                    Phenotypic = round(c(pat_p_var, var(dat$mat_p)),
                                      3))
knitr::kable(variances)
Parent Genotypic Environmental Phenotypic
Sire 26.680 5.934 32.614
Dam 23.884 9.868 33.752

As you can see the simulated genotypic and environmental variances are pretty close to what we specified, i.e., \(\sigma^2_g = 25\) and \(\sigma^2_e = 9\).1 You can also see that the phenotypic variance is pretty close to \(\sigma^2_g + \sigma^2_e\), as we expect.

Offspring-midparent regression

If you’re familiar with regression in R, you know about lm() and glm(). I’m going to use a similar function from rstanarm, namely stan_glm() to run a Bayesian linear regression. In addition to providing an estimate of the slope of the regression of offspring on mid-parent value, it provides a sense of how precise that estimate is in the form of 90 percent credible intervals.2

library(rstanarm)
Loading required package: Rcpp
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
This is rstanarm version 2.21.1
- See https://mc-stan.org/rstanarm/articles/priors for changes to default priors!
- Default priors may change, so it's safest to specify priors, even if equivalent to the defaults.
- For execution on a local, multicore CPU with excess RAM we recommend calling
  options(mc.cores = parallel::detectCores())
## This line allows rstanarm to run several chains at the same time
## instead of running them sequentially
options(mc.cores = parallel::detectCores())

off_r <- stan_glm(off_p ~ mid_p,
                  data = dat,
                  refresh = 0)
summary(off_r, digits = 3)

Model Info:
 function:     stan_glm
 family:       gaussian [identity]
 formula:      off_p ~ mid_p
 algorithm:    sampling
 sample:       4000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 1000
 predictors:   2

Estimates:
              mean   sd     10%    50%    90% 
(Intercept) 12.970  1.323 11.273 12.986 14.608
mid_p        0.740  0.027  0.708  0.740  0.775
sigma        3.425  0.076  3.328  3.425  3.524

Fit Diagnostics:
           mean   sd     10%    50%    90% 
mean_PPD 49.663  0.149 49.474 49.662 49.853

The mean_ppd is the sample average posterior predictive distribution of the outcome variable (for details see help('summary.stanreg')).

MCMC diagnostics
              mcse  Rhat  n_eff
(Intercept)   0.020 1.000 4331 
mid_p         0.000 1.000 4335 
sigma         0.001 1.000 3749 
mean_PPD      0.002 1.000 3919 
log-posterior 0.028 1.002 1827 

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).

We see that the slope of the regression, the mid_p line, is 0.740 (0.708, 0.775), while we expect to see \(\frac{\sigma^2_g}{\sigma^2_g + \sigma^2_p} = \frac{25}{34} = 0.735\). That’s not bad at all. Now let’s look at the half sib analysis.

Half-sib analysis

Besides the fact that stan_glm() provides both point estimates and estimates of uncertainty it also sets us up nicely for using stan_glmer(), which sets us up for doing the same thing with variance components. That’s what we’ll be doing here. We are fitting a model in which each offspring’s phenotype is drawn from a normal distribution with a mean that depends on pat_ID.3

off_h <- stan_glmer(off_p ~ (1|pat_ID),
                    data = dat,
                    refresh = 0)
summary(off_h, digits = 3, 
        pars = c("sigma", "Sigma[pat_ID:(Intercept),(Intercept)]"))

Model Info:
 function:     stan_glmer
 family:       gaussian [identity]
 formula:      off_p ~ (1 | pat_ID)
 algorithm:    sampling
 sample:       4000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 1000
 groups:       pat_ID (100)

Estimates:
                                        mean   sd    10%   50%   90%
sigma                                 3.891  0.090 3.775 3.891 4.006
Sigma[pat_ID:(Intercept),(Intercept)] 5.531  1.027 4.310 5.429 6.907

MCMC diagnostics
                                      mcse  Rhat  n_eff
sigma                                 0.001 1.000 4085 
Sigma[pat_ID:(Intercept),(Intercept)] 0.030 1.001 1142 

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).

sigma is the standard deviation within a sibship, \(\sigma_w\), and Sigma[pat_ID:(Intercept),(Intercept)] is the variance among half-sib families, \(\sigma^2_{hs}\). The variance among half-sib families is the variance among mothers, which is the genetic variance. There are, however, 10 offspring in each half-sib family. Thus, we can estimate the broad-sense heritability with this little function.4

heritability <- function(sigma_w, sigma_hs, n_off) {
  h_2 = sigma_hs*n_off/(sigma_hs*n_off + sigma_w^2)
  return(h_2)
}

off_h_mat <- as.data.frame(off_h)
h_2 <- heritability(off_h_mat$sigma, 
                    off_h_mat$`Sigma[pat_ID:(Intercept),(Intercept)]`,
                    n_dams)
off_h_mat$h_2 <- h_2

library(ggplot2)
p <- ggplot(off_h_mat, aes(x = h_2)) +
  geom_histogram(bins = 100, alpha = 0.4) +
  geom_vline(xintercept = mean(off_h_mat$h_2), 
             color = "red",
             linetype = "dashed") +
  xlab("Heritability") +
  theme_bw()
p

As you can see, in this simulation the half-sib estimate is pretty close to what we expect to see, 0.781.5

Lab 12

As part of her dissertation, Nora Mitchell measured several traits in two closely related species of Protea

Protea punctata is a large, upright shrub. Protea punctata

Protea venusta is forms low, spreading mats. Protea venusta

She collected seed in the field from 20 individuals of P. punctata and 13 individuals of P. venusta. She brought the seed back to UConn and grew the offspring in our greenhouses, a total of 245 P. punctata and 192 P. venusta. It is reasonable to treat the offspring from a single mother as members of a half-sib family as in the simulation above. Using these data estimate the heritability of each trait in each species (3 traits, 2 species), and note any species/trait combinations where the heritability appears to be unusually high or unusually low.

Hints

  • Data file: The data file has 437 rows, one for each seedling that was measured, and 5 columns.
    • species: The species to which the seedling belongs (punctata or venusta).
    • mom_id: A number identifying the mother of the seedling. Seedlings sharing a mom_id had the same mother. IMPORTANT NOTE: You’ll need to specify as.factor(mom_id) in your analyses. Here’s how to do that if you have downloaded Protea_greenhouse.csv to your hard drive:
greenhouse <- read_csv("Protea_greenhouse.csv") %>%
  mutate(mom_id = as.factor(paste(species, mom_id, sep = "")))
Rows: 437 Columns: 5
── Column specification ──────────────────────────────────────────────
Delimiter: ","
chr (1): species
dbl (4): mom_id, lma, fwc, lwr

ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
  • lma: Leaf mass per area, a widely used measure of the “toughness” of leaves.
  • fwc: Fresh water content, the amount of fresh water in leaves.
  • lwr: Leaf length-width ratio.
  • Remember that you can take a subset of the data using subset(). Using subset() in R and you can select a column of data using $.
    • For example, if you’ve read Protea_greenhouse.csv into greenhouse you can run an analysis of lwr in Protea venusta like this:6
venusta <- subset(greenhouse, species == "venusta")
lwr <- stan_glmer(lwr ~ (1|mom_id),
                  data = venusta,
                  refresh = 0)
summary(lwr, 
        digits = 3,
        pars = c("sigma", "Sigma[mom_id:(Intercept),(Intercept)]"))

Model Info:
 function:     stan_glmer
 family:       gaussian [identity]
 formula:      lwr ~ (1 | mom_id)
 algorithm:    sampling
 sample:       4000 (posterior sample size)
 priors:       see help('prior_summary')
 observations: 156
 groups:       mom_id (13)

Estimates:
                                        mean   sd    10%   50%   90%
sigma                                 0.316  0.019 0.292 0.315 0.340
Sigma[mom_id:(Intercept),(Intercept)] 0.010  0.011 0.001 0.007 0.022

MCMC diagnostics
                                      mcse  Rhat  n_eff
sigma                                 0.000 1.000 3199 
Sigma[mom_id:(Intercept),(Intercept)] 0.000 1.003 1177 

For each parameter, mcse is Monte Carlo standard error, n_eff is a crude measure of effective sample size, and Rhat is the potential scale reduction factor on split chains (at convergence Rhat=1).
  • Once you have run an analysis for a particular species/trait combination, you can use the following function to report the heritability (including the 90 percent credible interval):
heritability <- function(fit, dat) {
  dat_sum <- dat %>%
    group_by(mom_id) %>%
    summarize(count = n())
  n_off <- mean(dat_sum$count)
  df <- as.data.frame(fit)
  sigma_hs <- df$`Sigma[mom_id:(Intercept),(Intercept)]`
  sigma_w <- df$sigma
  h_2 = sigma_hs*n_off/(sigma_hs*n_off + sigma_w^2)
  h_2_df <- tibble(Mean = mean(h_2),
                   lo = quantile(h_2, 0.05),
                   hi = quantile(h_2, 0.95))
  return(h_2_df)
}

## I'm using the lwr object produced by the analysis in the last bullet
## point to provide the variance component estimates. I'm using venusta
## to calculate the average number of progeny in each family.
##
heritability(lwr, venusta)

Some further exploration with Stan

library(rstan)
Loading required package: StanHeaders
rstan (Version 2.21.2, GitRev: 2e1f913d3ca3)
For execution on a local, multicore CPU with excess RAM we recommend calling
options(mc.cores = parallel::detectCores()).
To avoid recompilation of unchanged Stan programs, we recommend calling
rstan_options(auto_write = TRUE)

Attaching package: ‘rstan’

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

    extract
dat_sum <- dat %>%
  group_by(pat_ID) %>%
  summarize(pat_mean = mean(off_p), pat_sd = sd(off_p))

stan_data <- list(n_indiv = nrow(dat),
                  n_sires = length(unique(dat$pat_ID)),
                  mu_prior = mean(dat$off_p),
                  prior_within = 1/mean(dat_sum$pat_sd),
                  prior_among = 1/sd(dat_sum$pat_mean),
                  sire = as.numeric(dat$pat_ID),
                  pheno = dat$off_p)
fit <- stan("heritability.stan",
            data = stan_data,
            refresh = 0)
print(fit, digits = 3, pars = c("sigma_w", "sigma_s", "h_2", 
                                "sigma_s_2"))
Inference for Stan model: heritability.
4 chains, each with iter=2000; warmup=1000; thin=1; 
post-warmup draws per chain=1000, total post-warmup draws=4000.

           mean se_mean    sd  2.5%   25%   50%   75% 97.5% n_eff
sigma_w   3.890   0.001 0.090 3.719 3.828 3.889 3.951 4.070  5410
sigma_s   2.325   0.004 0.211 1.949 2.175 2.317 2.460 2.769  3059
h_2       0.778   0.001 0.033 0.710 0.757 0.781 0.801 0.838  3004
sigma_s_2 5.451   0.018 0.998 3.798 4.729 5.369 6.051 7.665  3103
           Rhat
sigma_w   1.000
sigma_s   1.001
h_2       1.001
sigma_s_2 1.001

Samples were drawn using NUTS(diag_e) at Sun Nov 14 17:39:59 2021.
For each parameter, n_eff is a crude measure of effective sample size,
and Rhat is the potential scale reduction factor on split chains (at 
convergence, Rhat=1).

sigma_w here corresponds to sigma from stan_glmer(), and sigma_s_2 here corresponds to Sigma[pat_ID:(Intercept),(Intercept)].The Sigma[pat_ID:(Intercept),(Intercept)] term from stan_glmer() is the variance among intercepts, not the standard deviation. Notice how closely the posterior means from Stan match crude estimates calculated directly from the data as well as the estimates from stan_glmer().

fit_df <- as.data.frame(fit)
dat_sum <- dat %>%
  group_by(pat_ID) %>%
  summarize(pat_mean = mean(off_p), pat_sd = sd(off_p))
result <- tibble(Estimate = c("Stan", "Direct", "stan_glmer()"),
                 sigma_w = c(mean(fit_df$sigma_w),
                             mean(dat_sum$pat_sd),
                             mean(off_h_mat$sigma)),
                 sigma_s = c(mean(fit_df$sigma_s),
                             sd(dat_sum$pat_mean),
                             mean(sqrt(off_h_mat$`Sigma[pat_ID:(Intercept),(Intercept)]`))))
result

  1. Notice that the environmental variance for sires isn’t too close to what we expect. That’s because there are only 100 sires, and there are 1000 dams. This difference will become important later.↩︎

  2. NOTE: The refresh = 0 in the call to stan_glm() prevents us from seeing a large number of messages about the progress of the analysis.↩︎

  3. We’re estimating the broad-sense heritabiity, because we don’t know how much of the variation among fathers is due to differences in their additive genotype. The estimated differences in genotype include both additive and dominance components.↩︎

  4. It’s the broad-sense heritability because differences among fathers may include both additive and dominance components.↩︎

  5. I spent most of Saturday and Sunday reassuring myself that I’d interpreted things correctly. I wrote a small script in Stan where I know exactly what’s going on and compared my results to those from stan_glmer(). You can see the results below the hints, and you can download the Stan code and run it yourself, if you’re interested. http://darwin.eeb.uconn.edu/eeb348-resources/heritability.stan↩︎

  6. Depending on which trait/species combination you are examining, you may need to increase digits to 8 or 9 in order to see what’s going on.↩︎

LS0tCnRpdGxlOiAiRXN0aW1hdGluZyBoZXJpdGFiaWxpdHkgZnJvbSBoYWxmLXNpYmxpbmdzIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQoKIyBJbnRyb2R1Y3Rpb24KCldlIHdpbGwgc2ltdWxhdGUgc29tZSBkYXRhIHRvIGlsbHVzdHJhdGUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhlcml0YWJpbGl0eSBlc3RpbWF0ZXMgZGVyaXZlZCBmcm9tIGEgcmVncmVzc2lvbiBvZiBvZmZzcHJpbmcgcGhlbm90eXBlIG9uIG1pZC1wYXJlbnQgdmFsdWUgYW5kIHRob3NlIGRlcml2ZWQgZnJvbSBhbmFseXNpcyBvZiBoYWxmLXNpYnMuIFRvIGRvIHRoZSBzaW11bGF0aW9uLCB3ZSdsbCBhc3N1bWUgdGhhdCB0aGVyZSBhcmUgYSBsYXJnZSBudW1iZXIgb2YgbG9jaSB0aGF0IGFmZmVjdCB0aGUgdHJhaXQgYW5kIHRoYXQgZWFjaCBsb2N1cyBoYXMgYSBzbWFsbCBlZmZlY3QuIFdpdGggdGhpcyBhc3N1bXB0aW9uLCB3ZSBjYW4gYXBwcm94aW1hdGUgdGhlIGdlbm90eXBlIG9mIGVhY2ggaW5kaXZpZHVhbCBhcyBhIHJhbmRvbSB2YXJpYWJsZSBkcmF3biBmcm9tIGEgbm9ybWFsIGRpc3RyaWJ1dGlvbi4gVGhlIGBSYCBjb2RlIGJlbG93IGltcGxlbWVudHMgdGhlIGZvbGxvb3dpbmcgc2ltcGxlIGFsZ29yaXRobToKCjEuIFNlbGVjdCBhIGdlbm90eXBlIGZvciB0aGUgc2lyZSBhdCByYW5kb20gZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBtZWFuICRtdSQgYW5kIHZhcmlhbmNlICRcc2lnbWFeMl9nJC4gQ2FsbCB0aGF0IGdlbm90eXBlICR4X3AkLgoKMi4gU2VsZWN0IGEgZ2Vub3R5cGUgZm9yIHRoZSBkYW0gYXQgcmFuZG9tIGZyb20gYSBub3JtYWwgZGlzdHJpYnV0aW9uIHdpdGggdGhlIHNhbWUgbWVhbiBhbmQgdmFyaWFuY2UuIENhbGwgdGhhdCBnZW5vdHlwZSAkeF9tJC4KCjMuIENhbGN1bGF0ZSB0aGUgbWlkLXBhcmVudCBnZW5vdHlwaWMgdmFsdWUgYXMgJHhfe21wfSA9IFxmcmFje3hfbSArIHhfcH17Mn0kLgoKNC4gU2V0IHRoZSBvZmZzcHJpbmcgZ2Vub3R5cGUgdG8gJHhfe21wfSQuCgo1LiBSZXBlYXQgc3RlcHMgIzIgYW5kICMzIGBuX2RhbXNgIHRpbWVzLgoKNi4gUmVwZWF0IHN0ZXBzICMxLSM1IGBuX3NpcmVzYCB0aW1lcy4KCjcuIFNldCBwaGVub3R5cGVzIGZvciB0aGUgbWF0ZXJuYWwgcGFyZW50LCB0aGUgcGF0ZXJuYWwgcGFyZW50LCBhbmQgdGhlIG9mZnNwcmluZyBieSBkcmF3aW5nIHRoZW0gZnJvbSBub3JtYWwgZGlzdHJpYnV0aW9ucyB3aXRoIHRoZSBhcHByb3ByaWF0ZSBtZWFucyBhbmQgdmFyaWVuY2UgJFxzaWdtYV4yX2UkCgo4LiBSZWNvcmQgdGhlIHBhdGVybmFsIElELCB0aGUgZ2Vub3R5cGUgb2YgZWFjaCBwYXJlbnQsIHRoZSBtaWQtcGFyZW50IGdlbm90eXBpYyB2YWx1ZSwgdGhlIHBoZW5vdHlwZSBvZiBlYWNoIHBhcmVudCwgdGhlIG1pZC1wYXJlbnQgcGhlbm90eXBpYyB2YWx1ZSwgYW5kIHRoZSBvZmZzcHJpbmcgcGhlbm90eXBlLiAKCk5PVEU6IEkgYW0gdXNpbmcgYHNldC5zZWVkKDEyMzQpYCB0byBlbnN1cmUgdGhhdCBpZiB5b3UgcnVuIHRoaXMgY29kZSBvbiB5b3VyIG93biwgeW91J2xsIGdldCB0aGUgc2FtZSByZXN1bHRzIGFzIEkgZG8uIElmIHlvdSBlcmFzZSBvciBjb21tZW50IG91dCB0aGF0IGxpbmUgYW5kIHJ1bmQgdGhlIGNvZGUsIHlvdXIgcmVzdWx0cyB3aWxsIGRpZmZlciBzbGlnaHRseSBmcm9tIG1pbmUuCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCgpybShsaXN0ID0gbHMoKSkKCnNldC5zZWVkKDEyMzQpCgpuX3NpcmVzIDwtIDEwMApuX2RhbXMgPC0gMTAKbXUgPC0gNTAKc2lnbWFfMl9nIDwtIDI1CnNpZ21hXzJfZSA8LSA5CgpkYXQgPC0gdGliYmxlKHBhdF9JRCA9IE5BLAogICAgICAgICAgICAgIGluZF9JRCA9IE5BLAogICAgICAgICAgICAgIG1hdF9nID0gTkEsCiAgICAgICAgICAgICAgcGF0X2cgPSBOQSwKICAgICAgICAgICAgICBtaWRfZyA9IE5BLAogICAgICAgICAgICAgIG1hdF9wID0gTkEsCiAgICAgICAgICAgICAgcGF0X3AgPSBOQSwKICAgICAgICAgICAgICBtaWRfcCA9IE5BLAogICAgICAgICAgICAgIG9mZl9wID0gTkEpCmN0IDwtIDAKZm9yIChpIGluIDE6bl9zaXJlcykgewogIHBhdGcgPC0gcm5vcm0oMSwgbXUsIHNxcnQoc2lnbWFfMl9nKSkKICBwYXRwIDwtIHJub3JtKDEsIHBhdGcsIHNxcnQoc2lnbWFfMl9lKSkKICBmb3IgKGogaW4gMTpuX2RhbXMpIHsKICAgIGN0IDwtIGN0ICsgMQogICAgbWF0ZyA8LSBybm9ybSgxLCBtdSwgc3FydChzaWdtYV8yX2cpKQogICAgbWF0cCA8LSBybm9ybSgxLCBtYXRnLCBzcXJ0KHNpZ21hXzJfZSkpCiAgICBtaWRnIDwtIChtYXRnICsgcGF0ZykvMgogICAgb2ZmcCA8LSBybm9ybSgxLCBtaWRnLCBzcXJ0KHNpZ21hXzJfZSkpCiAgICBkYXQgPC0gYWRkX3JvdyhkYXQsCiAgICAgICAgICAgICAgICAgICBwYXRfSUQgPSBpLAogICAgICAgICAgICAgICAgICAgaW5kX0lEID0gY3QsCiAgICAgICAgICAgICAgICAgICBtYXRfZyA9IG1hdGcsCiAgICAgICAgICAgICAgICAgICBwYXRfZyA9IHBhdGcsCiAgICAgICAgICAgICAgICAgICBtaWRfZyA9IG1pZGcsCiAgICAgICAgICAgICAgICAgICBtYXRfcCA9IG1hdHAsCiAgICAgICAgICAgICAgICAgICBwYXRfcCA9IHBhdHAsCiAgICAgICAgICAgICAgICAgICBtaWRfcCA9IChtYXRwICsgcGF0cCkvMiwKICAgICAgICAgICAgICAgICAgIG9mZl9wID0gb2ZmcCkKICB9Cn0KZGF0IDwtIGRhdCAlPiUKICBmaWx0ZXIoIWlzLm5hKHBhdF9JRCkpICU+JQogIG11dGF0ZShwYXRfSUQgPSBmYWN0b3IocGF0X0lEKSwKICAgICAgICAgaW5kX0lEID0gZmFjdG9yKGluZF9JRCkpCnBhdF9nX3ZhciA8LSB2YXIodW5pcXVlKGRhdFssIGMoInBhdF9JRCIsICJwYXRfZyIpXSkkcGF0X2cpCnBhdF9wX3ZhciA8LSB2YXIodW5pcXVlKGRhdFssIGMoInBhdF9JRCIsICJwYXRfcCIpXSkkcGF0X3ApCnZhcmlhbmNlcyA8LSB0aWJibGUoUGFyZW50ID0gYygiU2lyZSIsICJEYW0iKSwKICAgICAgICAgICAgICAgICAgICBHZW5vdHlwaWMgPSByb3VuZChjKHBhdF9nX3ZhciwgdmFyKGRhdCRtYXRfZykpLCAzKSwKICAgICAgICAgICAgICAgICAgICBFbnZpcm9ubWVudGFsID0gcm91bmQoYyhwYXRfcF92YXIgLSBwYXRfZ192YXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyKGRhdCRtYXRfcCkgLQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFyKGRhdCRtYXRfZykpLCAzKSwKICAgICAgICAgICAgICAgICAgICBQaGVub3R5cGljID0gcm91bmQoYyhwYXRfcF92YXIsIHZhcihkYXQkbWF0X3ApKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAzKSkKa25pdHI6OmthYmxlKHZhcmlhbmNlcykKYGBgCgpBcyB5b3UgY2FuIHNlZSB0aGUgc2ltdWxhdGVkIGdlbm90eXBpYyBhbmQgZW52aXJvbm1lbnRhbCB2YXJpYW5jZXMgYXJlIHByZXR0eSBjbG9zZSB0byB3aGF0IHdlIHNwZWNpZmllZCwgaS5lLiwgJFxzaWdtYV4yX2cgPSBgciBzaWdtYV8yX2dgJCBhbmQgJFxzaWdtYV4yX2UgPSBgciBzaWdtYV8yX2VgJC5eW05vdGljZSB0aGF0IHRoZSBlbnZpcm9ubWVudGFsIHZhcmlhbmNlIGZvciBzaXJlcyBpc24ndCB0b28gY2xvc2UgdG8gd2hhdCB3ZSBleHBlY3QuIFRoYXQncyBiZWNhdXNlIHRoZXJlIGFyZSBvbmx5IDEwMCBzaXJlcywgYW5kIHRoZXJlIGFyZSAxMDAwIGRhbXMuIFRoaXMgZGlmZmVyZW5jZSB3aWxsIGJlY29tZSBpbXBvcnRhbnQgbGF0ZXIuXSBZb3UgY2FuIGFsc28gc2VlIHRoYXQgdGhlIHBoZW5vdHlwaWMgdmFyaWFuY2UgaXMgcHJldHR5IGNsb3NlIHRvICRcc2lnbWFeMl9nICsgXHNpZ21hXjJfZSQsIGFzIHdlIGV4cGVjdC4KCiMjIE9mZnNwcmluZy1taWRwYXJlbnQgcmVncmVzc2lvbgoKSWYgeW91J3JlIGZhbWlsaWFyIHdpdGggcmVncmVzc2lvbiBpbiBgUmAsIHlvdSBrbm93IGFib3V0IGBsbSgpYCBhbmQgYGdsbSgpYC4gSSdtIGdvaW5nIHRvIHVzZSBhIHNpbWlsYXIgZnVuY3Rpb24gZnJvbSBgcnN0YW5hcm1gLCBuYW1lbHkgYHN0YW5fZ2xtKClgIHRvIHJ1biBhIEJheWVzaWFuIGxpbmVhciByZWdyZXNzaW9uLiBJbiBhZGRpdGlvbiB0byBwcm92aWRpbmcgYW4gZXN0aW1hdGUgb2YgdGhlIHNsb3BlIG9mIHRoZSByZWdyZXNzaW9uIG9mIG9mZnNwcmluZyBvbiBtaWQtcGFyZW50IHZhbHVlLCBpdCBwcm92aWRlcyBhIHNlbnNlIG9mIGhvdyBwcmVjaXNlIHRoYXQgZXN0aW1hdGUgaXMgaW4gdGhlIGZvcm0gb2YgOTAgcGVyY2VudCBjcmVkaWJsZSBpbnRlcnZhbHMuXltOT1RFOiBUaGUgYHJlZnJlc2ggPSAwYCBpbiB0aGUgY2FsbCB0byBgc3Rhbl9nbG0oKWAgcHJldmVudHMgdXMgZnJvbSBzZWVpbmcgYSBsYXJnZSBudW1iZXIgb2YgbWVzc2FnZXMgYWJvdXQgdGhlIHByb2dyZXNzIG9mIHRoZSBhbmFseXNpcy5dCgpgYGB7cn0KbGlicmFyeShyc3RhbmFybSkKCiMjIFRoaXMgbGluZSBhbGxvd3MgcnN0YW5hcm0gdG8gcnVuIHNldmVyYWwgY2hhaW5zIGF0IHRoZSBzYW1lIHRpbWUKIyMgaW5zdGVhZCBvZiBydW5uaW5nIHRoZW0gc2VxdWVudGlhbGx5Cm9wdGlvbnMobWMuY29yZXMgPSBwYXJhbGxlbDo6ZGV0ZWN0Q29yZXMoKSkKCm9mZl9yIDwtIHN0YW5fZ2xtKG9mZl9wIH4gbWlkX3AsCiAgICAgICAgICAgICAgICAgIGRhdGEgPSBkYXQsCiAgICAgICAgICAgICAgICAgIHJlZnJlc2ggPSAwKQpzdW1tYXJ5KG9mZl9yLCBkaWdpdHMgPSAzKQpgYGAKCldlIHNlZSB0aGF0IHRoZSBzbG9wZSBvZiB0aGUgcmVncmVzc2lvbiwgdGhlIGBtaWRfcGAgbGluZSwgaXMgMC43NDAgKDAuNzA4LCAwLjc3NSksIHdoaWxlIHdlIGV4cGVjdCB0byBzZWUgJFxmcmFje1xzaWdtYV4yX2d9e1xzaWdtYV4yX2cgKyBcc2lnbWFeMl9wfSA9IFxmcmFje2ByIHNpZ21hXzJfZ2B9e2ByIHNpZ21hXzJfZyArIHNpZ21hXzJfZWB9ICA9IGByIHJvdW5kKHNpZ21hXzJfZy8oc2lnbWFfMl9nICsgc2lnbWFfMl9lKSwgMylgJC4gVGhhdCdzIG5vdCBiYWQgYXQgYWxsLiBOb3cgbGV0J3MgbG9vayBhdCB0aGUgaGFsZiBzaWIgYW5hbHlzaXMuCgojIyBIYWxmLXNpYiBhbmFseXNpcwoKQmVzaWRlcyB0aGUgZmFjdCB0aGF0IGBzdGFuX2dsbSgpYCBwcm92aWRlcyBib3RoIHBvaW50IGVzdGltYXRlcyBhbmQgZXN0aW1hdGVzIG9mIHVuY2VydGFpbnR5IGl0IGFsc28gc2V0cyB1cyB1cCBuaWNlbHkgZm9yIHVzaW5nIGBzdGFuX2dsbWVyKClgLCB3aGljaCBzZXRzIHVzIHVwIGZvciBkb2luZyB0aGUgc2FtZSB0aGluZyB3aXRoIHZhcmlhbmNlIGNvbXBvbmVudHMuIFRoYXQncyB3aGF0IHdlJ2xsIGJlIGRvaW5nIGhlcmUuIFdlIGFyZSBmaXR0aW5nIGEgbW9kZWwgaW4gd2hpY2ggZWFjaCBvZmZzcHJpbmcncyBwaGVub3R5cGUgaXMgZHJhd24gZnJvbSBhIG5vcm1hbCBkaXN0cmlidXRpb24gd2l0aCBhIG1lYW4gdGhhdCBkZXBlbmRzIG9uIGBwYXRfSURgLl5bV2UncmUgZXN0aW1hdGluZyB0aGUgYnJvYWQtc2Vuc2UgaGVyaXRhYmlpdHksIGJlY2F1c2Ugd2UgZG9uJ3Qga25vdyBob3cgbXVjaCBvZiB0aGUgdmFyaWF0aW9uIGFtb25nIGZhdGhlcnMgaXMgZHVlIHRvIGRpZmZlcmVuY2VzIGluIHRoZWlyIGFkZGl0aXZlIGdlbm90eXBlLiBUaGUgZXN0aW1hdGVkIGRpZmZlcmVuY2VzIGluIGdlbm90eXBlIGluY2x1ZGUgYm90aCBhZGRpdGl2ZSBhbmQgZG9taW5hbmNlIGNvbXBvbmVudHMuXSAKCmBgYHtyfQpvZmZfaCA8LSBzdGFuX2dsbWVyKG9mZl9wIH4gKDF8cGF0X0lEKSwKICAgICAgICAgICAgICAgICAgICBkYXRhID0gZGF0LAogICAgICAgICAgICAgICAgICAgIHJlZnJlc2ggPSAwKQpzdW1tYXJ5KG9mZl9oLCBkaWdpdHMgPSAzLCAKICAgICAgICBwYXJzID0gYygic2lnbWEiLCAiU2lnbWFbcGF0X0lEOihJbnRlcmNlcHQpLChJbnRlcmNlcHQpXSIpKQpgYGAKCmBzaWdtYWAgaXMgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiB3aXRoaW4gYSBzaWJzaGlwLCAkXHNpZ21hX3ckLCBhbmQgYFNpZ21hW3BhdF9JRDooSW50ZXJjZXB0KSwoSW50ZXJjZXB0KV1gIGlzIHRoZSB2YXJpYW5jZSBhbW9uZyBoYWxmLXNpYiBmYW1pbGllcywgJFxzaWdtYV4yX3toc30kLiBUaGUgdmFyaWFuY2UgYW1vbmcgaGFsZi1zaWIgZmFtaWxpZXMgaXMgdGhlIHZhcmlhbmNlIGFtb25nIG1vdGhlcnMsIHdoaWNoIGlzIHRoZSBnZW5ldGljIHZhcmlhbmNlLiBUaGVyZSBhcmUsIGhvd2V2ZXIsIGByIG5fZGFtc2Agb2Zmc3ByaW5nIGluIGVhY2ggaGFsZi1zaWIgZmFtaWx5LiBUaHVzLCB3ZSBjYW4gZXN0aW1hdGUgdGhlIGJyb2FkLXNlbnNlIGhlcml0YWJpbGl0eSB3aXRoIHRoaXMgbGl0dGxlIGZ1bmN0aW9uLl5bSXQncyB0aGUgYnJvYWQtc2Vuc2UgaGVyaXRhYmlsaXR5IGJlY2F1c2UgZGlmZmVyZW5jZXMgYW1vbmcgZmF0aGVycyBtYXkgaW5jbHVkZSBib3RoIGFkZGl0aXZlIGFuZCBkb21pbmFuY2UgY29tcG9uZW50cy5dIAoKYGBge3J9Cmhlcml0YWJpbGl0eSA8LSBmdW5jdGlvbihzaWdtYV93LCBzaWdtYV9ocywgbl9vZmYpIHsKICBoXzIgPSBzaWdtYV9ocypuX29mZi8oc2lnbWFfaHMqbl9vZmYgKyBzaWdtYV93XjIpCiAgcmV0dXJuKGhfMikKfQoKb2ZmX2hfbWF0IDwtIGFzLmRhdGEuZnJhbWUob2ZmX2gpCmhfMiA8LSBoZXJpdGFiaWxpdHkob2ZmX2hfbWF0JHNpZ21hLCAKICAgICAgICAgICAgICAgICAgICBvZmZfaF9tYXQkYFNpZ21hW3BhdF9JRDooSW50ZXJjZXB0KSwoSW50ZXJjZXB0KV1gLAogICAgICAgICAgICAgICAgICAgIG5fZGFtcykKb2ZmX2hfbWF0JGhfMiA8LSBoXzIKCmxpYnJhcnkoZ2dwbG90MikKcCA8LSBnZ3Bsb3Qob2ZmX2hfbWF0LCBhZXMoeCA9IGhfMikpICsKICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTAwLCBhbHBoYSA9IDAuNCkgKwogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IG1lYW4ob2ZmX2hfbWF0JGhfMiksIAogICAgICAgICAgICAgY29sb3IgPSAicmVkIiwKICAgICAgICAgICAgIGxpbmV0eXBlID0gImRhc2hlZCIpICsKICB4bGFiKCJIZXJpdGFiaWxpdHkiKSArCiAgdGhlbWVfYncoKQpwCmBgYAoKQXMgeW91IGNhbiBzZWUsIGluIHRoaXMgc2ltdWxhdGlvbiB0aGUgaGFsZi1zaWIgZXN0aW1hdGUgaXMgcHJldHR5IGNsb3NlIHRvIHdoYXQgd2UgZXhwZWN0IHRvIHNlZSwgYHIgcm91bmQobWVhbihoXzIpLCAzKWAuXltJIHNwZW50IG1vc3Qgb2YgU2F0dXJkYXkgYW5kIFN1bmRheSByZWFzc3VyaW5nIG15c2VsZiB0aGF0IEknZCBpbnRlcnByZXRlZCB0aGluZ3MgY29ycmVjdGx5LiBJIHdyb3RlIGEgc21hbGwgc2NyaXB0IGluIGBTdGFuYCB3aGVyZSBJIGtub3cgZXhhY3RseSB3aGF0J3MgZ29pbmcgb24gYW5kIGNvbXBhcmVkIG15IHJlc3VsdHMgdG8gdGhvc2UgZnJvbSBgc3Rhbl9nbG1lcigpYC4gWW91IGNhbiBzZWUgdGhlIHJlc3VsdHMgYmVsb3cgdGhlIGhpbnRzLCBhbmQgeW91IGNhbiBkb3dubG9hZCB0aGUgYFN0YW5gIGNvZGUgYW5kIHJ1biBpdCB5b3Vyc2VsZiwgaWYgeW91J3JlIGludGVyZXN0ZWQuIFtodHRwOi8vZGFyd2luLmVlYi51Y29ubi5lZHUvZWViMzQ4LXJlc291cmNlcy9oZXJpdGFiaWxpdHkuc3Rhbl0oaHR0cDovL2Rhcndpbi5lZWIudWNvbm4uZWR1L2VlYjM0OC1yZXNvdXJjZXMvaGVyaXRhYmlsaXR5LnN0YW4pXQoKIyBMYWIgMTIKCkFzIHBhcnQgb2YgaGVyIFtkaXNzZXJ0YXRpb25dKGh0dHBzOi8vb3BlbmNvbW1vbnMudWNvbm4uZWR1L2Rpc3NlcnRhdGlvbnMvMTM3MC8pLCBOb3JhIE1pdGNoZWxsIG1lYXN1cmVkIHNldmVyYWwgdHJhaXRzIGluIHR3byBjbG9zZWx5IHJlbGF0ZWQgc3BlY2llcyBvZiBfUHJvdGVhXwoKX1Byb3RlYSBwdW5jdGF0YV8gaXMgYSBsYXJnZSwgdXByaWdodCBzaHJ1Yi4gIVtfUHJvdGVhIHB1bmN0YXRhX10oUHJvdGVhX3B1bmN0YXRhLnBuZykgCgpfUHJvdGVhIHZlbnVzdGFfIGlzIGZvcm1zIGxvdywgc3ByZWFkaW5nIG1hdHMuICFbX1Byb3RlYSB2ZW51c3RhX10oUHJvdGVhX3ZlbnVzdGEucG5nKQoKU2hlIGNvbGxlY3RlZCBzZWVkIGluIHRoZSBmaWVsZCBmcm9tIDIwIGluZGl2aWR1YWxzIG9mIF9QLiBwdW5jdGF0YV8gYW5kIDEzIGluZGl2aWR1YWxzIG9mIF9QLiB2ZW51c3RhXy4gU2hlIGJyb3VnaHQgdGhlIHNlZWQgYmFjayB0byBVQ29ubiBhbmQgZ3JldyB0aGUgb2Zmc3ByaW5nIGluIG91ciBncmVlbmhvdXNlcywgYSB0b3RhbCBvZiAyNDUgX1AuIHB1bmN0YXRhXyBhbmQgMTkyIF9QLiB2ZW51c3RhXy4gSXQgaXMgcmVhc29uYWJsZSB0byB0cmVhdCB0aGUgb2Zmc3ByaW5nIGZyb20gYSBzaW5nbGUgbW90aGVyIGFzIG1lbWJlcnMgb2YgYSBoYWxmLXNpYiBmYW1pbHkgYXMgaW4gdGhlIHNpbXVsYXRpb24gYWJvdmUuIFVzaW5nIHRoZXNlIGRhdGEgZXN0aW1hdGUgdGhlIGhlcml0YWJpbGl0eSBvZiBlYWNoIHRyYWl0IGluIGVhY2ggc3BlY2llcyAoMyB0cmFpdHMsIDIgc3BlY2llcyksIGFuZCBub3RlIGFueSBzcGVjaWVzL3RyYWl0IGNvbWJpbmF0aW9ucyB3aGVyZSB0aGUgaGVyaXRhYmlsaXR5IGFwcGVhcnMgdG8gYmUgdW51c3VhbGx5IGhpZ2ggb3IgdW51c3VhbGx5IGxvdy4KCiMjIEhpbnRzCgotIFtEYXRhIGZpbGVdKGh0dHA6Ly9kYXJ3aW4uZWViLnVjb25uLmVkdS9lZWIzNDgtcmVzb3VyY2VzL1Byb3RlYV9ncmVlbmhvdXNlLmNzdik6IFRoZSBkYXRhIGZpbGUgaGFzIDQzNyByb3dzLCBvbmUgZm9yIGVhY2ggc2VlZGxpbmcgdGhhdCB3YXMgbWVhc3VyZWQsIGFuZCA1IGNvbHVtbnMuCiAgLSBgc3BlY2llc2A6IFRoZSBzcGVjaWVzIHRvIHdoaWNoIHRoZSBzZWVkbGluZyBiZWxvbmdzIChgcHVuY3RhdGFgIG9yIGB2ZW51c3RhYCkuCiAgLSBgbW9tX2lkYDogQSBudW1iZXIgaWRlbnRpZnlpbmcgdGhlIG1vdGhlciBvZiB0aGUgc2VlZGxpbmcuIFNlZWRsaW5ncyBzaGFyaW5nIGEgYG1vbV9pZGAgaGFkIHRoZSBzYW1lIG1vdGhlci4gSU1QT1JUQU5UIE5PVEU6IFlvdSdsbCBuZWVkIHRvIHNwZWNpZnkgYGFzLmZhY3Rvcihtb21faWQpYCBpbiB5b3VyIGFuYWx5c2VzLiBIZXJlJ3MgaG93IHRvIGRvIHRoYXQgaWYgeW91IGhhdmUgZG93bmxvYWRlZCBgUHJvdGVhX2dyZWVuaG91c2UuY3N2YCB0byB5b3VyIGhhcmQgZHJpdmU6CmBgYHtyfQpncmVlbmhvdXNlIDwtIHJlYWRfY3N2KCJQcm90ZWFfZ3JlZW5ob3VzZS5jc3YiKSAlPiUKICBtdXRhdGUobW9tX2lkID0gYXMuZmFjdG9yKHBhc3RlKHNwZWNpZXMsIG1vbV9pZCwgc2VwID0gIiIpKSkKYGBgCiAgLSBgbG1hYDogTGVhZiBtYXNzIHBlciBhcmVhLCBhIHdpZGVseSB1c2VkIG1lYXN1cmUgb2YgdGhlICJ0b3VnaG5lc3MiIG9mIGxlYXZlcy4KICAtIGBmd2NgOiBGcmVzaCB3YXRlciBjb250ZW50LCB0aGUgYW1vdW50IG9mIGZyZXNoIHdhdGVyIGluIGxlYXZlcy4KICAtIGBsd3JgOiBMZWFmIGxlbmd0aC13aWR0aCByYXRpby4KLSBSZW1lbWJlciB0aGF0IHlvdSBjYW4gdGFrZSBhIHN1YnNldCBvZiB0aGUgZGF0YSB1c2luZyBgc3Vic2V0KClgLiBbVXNpbmcgYHN1YnNldCgpYCBpbiBgUmBdKGh0dHA6Ly9kYXJ3aW4uZWViLnVjb25uLmVkdS9lZWIzNDgtcmVzb3VyY2VzL3N1YnNldHRpbmcubmIuaHRtbCkgYW5kIHlvdSBjYW4gc2VsZWN0IGEgY29sdW1uIG9mIGRhdGEgdXNpbmcgYCRgLgogIC0gRm9yIGV4YW1wbGUsIGlmIHlvdSd2ZSByZWFkIGBQcm90ZWFfZ3JlZW5ob3VzZS5jc3ZgIGludG8gYGdyZWVuaG91c2VgIHlvdSBjYW4gcnVuIGFuIGFuYWx5c2lzIG9mIGBsd3JgIGluIF9Qcm90ZWEgdmVudXN0YV8gbGlrZSB0aGlzOl5bRGVwZW5kaW5nIG9uIHdoaWNoIHRyYWl0L3NwZWNpZXMgY29tYmluYXRpb24geW91IGFyZSBleGFtaW5pbmcsIHlvdSBtYXkgbmVlZCB0byBpbmNyZWFzZSBkaWdpdHMgdG8gOCBvciA5IGluIG9yZGVyIHRvIHNlZSB3aGF0J3MgZ29pbmcgb24uXQpgYGB7cn0KdmVudXN0YSA8LSBzdWJzZXQoZ3JlZW5ob3VzZSwgc3BlY2llcyA9PSAidmVudXN0YSIpCmx3ciA8LSBzdGFuX2dsbWVyKGx3ciB+ICgxfG1vbV9pZCksCiAgICAgICAgICAgICAgICAgIGRhdGEgPSB2ZW51c3RhLAogICAgICAgICAgICAgICAgICByZWZyZXNoID0gMCkKc3VtbWFyeShsd3IsIAogICAgICAgIGRpZ2l0cyA9IDMsCiAgICAgICAgcGFycyA9IGMoInNpZ21hIiwgIlNpZ21hW21vbV9pZDooSW50ZXJjZXB0KSwoSW50ZXJjZXB0KV0iKSkKYGBgCgoKCi0gT25jZSB5b3UgaGF2ZSBydW4gYW4gYW5hbHlzaXMgZm9yIGEgcGFydGljdWxhciBzcGVjaWVzL3RyYWl0IGNvbWJpbmF0aW9uLCB5b3UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uIHRvIHJlcG9ydCB0aGUgaGVyaXRhYmlsaXR5IChpbmNsdWRpbmcgdGhlIDkwIHBlcmNlbnQgY3JlZGlibGUgaW50ZXJ2YWwpOgoKYGBge3J9Cmhlcml0YWJpbGl0eSA8LSBmdW5jdGlvbihmaXQsIGRhdCkgewogIGRhdF9zdW0gPC0gZGF0ICU+JQogICAgZ3JvdXBfYnkobW9tX2lkKSAlPiUKICAgIHN1bW1hcml6ZShjb3VudCA9IG4oKSkKICBuX29mZiA8LSBtZWFuKGRhdF9zdW0kY291bnQpCiAgZGYgPC0gYXMuZGF0YS5mcmFtZShmaXQpCiAgc2lnbWFfaHMgPC0gZGYkYFNpZ21hW21vbV9pZDooSW50ZXJjZXB0KSwoSW50ZXJjZXB0KV1gCiAgc2lnbWFfdyA8LSBkZiRzaWdtYQogIGhfMiA9IHNpZ21hX2hzKm5fb2ZmLyhzaWdtYV9ocypuX29mZiArIHNpZ21hX3deMikKICBoXzJfZGYgPC0gdGliYmxlKE1lYW4gPSBtZWFuKGhfMiksCiAgICAgICAgICAgICAgICAgICBsbyA9IHF1YW50aWxlKGhfMiwgMC4wNSksCiAgICAgICAgICAgICAgICAgICBoaSA9IHF1YW50aWxlKGhfMiwgMC45NSkpCiAgcmV0dXJuKGhfMl9kZikKfQoKIyMgSSdtIHVzaW5nIHRoZSBsd3Igb2JqZWN0IHByb2R1Y2VkIGJ5IHRoZSBhbmFseXNpcyBpbiB0aGUgbGFzdCBidWxsZXQKIyMgcG9pbnQgdG8gcHJvdmlkZSB0aGUgdmFyaWFuY2UgY29tcG9uZW50IGVzdGltYXRlcy4gSSdtIHVzaW5nIHZlbnVzdGEKIyMgdG8gY2FsY3VsYXRlIHRoZSBhdmVyYWdlIG51bWJlciBvZiBwcm9nZW55IGluIGVhY2ggZmFtaWx5LgojIwpoZXJpdGFiaWxpdHkobHdyLCB2ZW51c3RhKQpgYGAKCiMjIFNvbWUgZnVydGhlciBleHBsb3JhdGlvbiB3aXRoIGBTdGFuYAoKYGBge3J9CmxpYnJhcnkocnN0YW4pCgpkYXRfc3VtIDwtIGRhdCAlPiUKICBncm91cF9ieShwYXRfSUQpICU+JQogIHN1bW1hcml6ZShwYXRfbWVhbiA9IG1lYW4ob2ZmX3ApLCBwYXRfc2QgPSBzZChvZmZfcCkpCgpzdGFuX2RhdGEgPC0gbGlzdChuX2luZGl2ID0gbnJvdyhkYXQpLAogICAgICAgICAgICAgICAgICBuX3NpcmVzID0gbGVuZ3RoKHVuaXF1ZShkYXQkcGF0X0lEKSksCiAgICAgICAgICAgICAgICAgIG11X3ByaW9yID0gbWVhbihkYXQkb2ZmX3ApLAogICAgICAgICAgICAgICAgICBwcmlvcl93aXRoaW4gPSAxL21lYW4oZGF0X3N1bSRwYXRfc2QpLAogICAgICAgICAgICAgICAgICBwcmlvcl9hbW9uZyA9IDEvc2QoZGF0X3N1bSRwYXRfbWVhbiksCiAgICAgICAgICAgICAgICAgIHNpcmUgPSBhcy5udW1lcmljKGRhdCRwYXRfSUQpLAogICAgICAgICAgICAgICAgICBwaGVubyA9IGRhdCRvZmZfcCkKZml0IDwtIHN0YW4oImhlcml0YWJpbGl0eS5zdGFuIiwKICAgICAgICAgICAgZGF0YSA9IHN0YW5fZGF0YSwKICAgICAgICAgICAgcmVmcmVzaCA9IDApCnByaW50KGZpdCwgZGlnaXRzID0gMywgcGFycyA9IGMoInNpZ21hX3ciLCAic2lnbWFfcyIsICJoXzIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2lnbWFfc18yIikpCmBgYAoKYHNpZ21hX3dgIGhlcmUgY29ycmVzcG9uZHMgdG8gYHNpZ21hYCBmcm9tIGBzdGFuX2dsbWVyKClgLCBhbmQgYHNpZ21hX3NfMmAgaGVyZSBjb3JyZXNwb25kcyB0byBgU2lnbWFbcGF0X0lEOihJbnRlcmNlcHQpLChJbnRlcmNlcHQpXWAuVGhlIGBTaWdtYVtwYXRfSUQ6KEludGVyY2VwdCksKEludGVyY2VwdCldYCB0ZXJtIGZyb20gYHN0YW5fZ2xtZXIoKWAgaXMgdGhlICoqKnZhcmlhbmNlKioqIGFtb25nIGludGVyY2VwdHMsIG5vdCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uLiBOb3RpY2UgaG93IGNsb3NlbHkgdGhlIHBvc3RlcmlvciBtZWFucyBmcm9tIGBTdGFuYCBtYXRjaCBjcnVkZSBlc3RpbWF0ZXMgY2FsY3VsYXRlZCBkaXJlY3RseSBmcm9tIHRoZSBkYXRhIGFzIHdlbGwgYXMgdGhlIGVzdGltYXRlcyBmcm9tIGBzdGFuX2dsbWVyKClgLgoKYGBge3J9CmZpdF9kZiA8LSBhcy5kYXRhLmZyYW1lKGZpdCkKZGF0X3N1bSA8LSBkYXQgJT4lCiAgZ3JvdXBfYnkocGF0X0lEKSAlPiUKICBzdW1tYXJpemUocGF0X21lYW4gPSBtZWFuKG9mZl9wKSwgcGF0X3NkID0gc2Qob2ZmX3ApKQpyZXN1bHQgPC0gdGliYmxlKEVzdGltYXRlID0gYygiU3RhbiIsICJEaXJlY3QiLCAic3Rhbl9nbG1lcigpIiksCiAgICAgICAgICAgICAgICAgc2lnbWFfdyA9IGMobWVhbihmaXRfZGYkc2lnbWFfdyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihkYXRfc3VtJHBhdF9zZCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVhbihvZmZfaF9tYXQkc2lnbWEpKSwKICAgICAgICAgICAgICAgICBzaWdtYV9zID0gYyhtZWFuKGZpdF9kZiRzaWdtYV9zKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzZChkYXRfc3VtJHBhdF9tZWFuKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtZWFuKHNxcnQob2ZmX2hfbWF0JGBTaWdtYVtwYXRfSUQ6KEludGVyY2VwdCksKEludGVyY2VwdCldYCkpKSkKcmVzdWx0CmBgYA==