This note is an analysis of population growth in Marion and Polk counties’ 233 census block groups. These two counties share the state capitol of Oregon, Salem. The analysis will be fully Bayesian, using the R package rstan to interface with the Stan sampling engine.

We will fit a multilevel model to estimate separate growth rates for each block group.

You can click any heading in this page to return back here to the table of contents.

1 The data

For this study we’ll combine the population counts from the 2010 Census with the American Community Survey 5-year Estimates. From the Census Bureau website:

Every year, the U.S. Census Bureau contacts over 3.5 million households across the country to participate in the American Community Survey. Unlike the every-10-year census, this survey continues all year, every year. We randomly sample addresses in every state, the District of Columbia, and Puerto Rico.

The Census Bureau uses the responses from each sample of the population to extrapolate information about the total population, so there is some uncertainty around its estimates. Each estimate is reported along with a 90% margin of error.1 It’s extremely important that we incorporate this uncertainty in our analysis.

The Census Bureau released the 2013-2017 ACS 5-year Estimates on December 6, 2018.

We downloaded the population counts for the census block groups in Marion and Polk counties from the American FactFinder from the 2010 Census as well as each of the years in the 2013-2017 5-year Estimates. I’ve re-hosted the data here for convenience.

pop_2010 <- read.csv("pop_2010.csv")
pop_2013 <- read.csv("pop_2013.csv")
pop_2014 <- read.csv("pop_2014.csv")
pop_2015 <- read.csv("pop_2015.csv")
pop_2016 <- read.csv("pop_2016.csv")
pop_2017 <- read.csv("pop_2017.csv")

We’ll convert the 90% margins of error for each population count to standard errors by dividing them by 1.645, and we’ll take the standard errors of the 2010 Census data to be 0.

pop <- data.frame(
    GEOID = c(
        as.character(pop_2010$GEO.id),
        as.character(pop_2013$GEO.id),
        as.character(pop_2014$GEO.id),
        as.character(pop_2015$GEO.id),
        as.character(pop_2016$GEO.id),
        as.character(pop_2017$GEO.id)
    ),
    population = c(
        pop_2010$D001,
        pop_2013$HD01_VD01,
        pop_2014$HD01_VD01,
        pop_2015$HD01_VD01,
        pop_2016$HD01_VD01,
        pop_2017$HD01_VD01
    ),
    std_err = c(
        rep(0, nrow(pop_2010)),
        pop_2013$HD02_VD01,
        pop_2014$HD02_VD01,
        pop_2015$HD02_VD01,
        pop_2016$HD02_VD01,
        pop_2017$HD02_VD01
    )/1.645,
    year = c(
        rep(2010, nrow(pop_2010)),
        rep(2013, nrow(pop_2013)),
        rep(2014, nrow(pop_2014)),
        rep(2015, nrow(pop_2015)),
        rep(2016, nrow(pop_2016)),
        rep(2017, nrow(pop_2017))
    )
)
pop$year_s <- (pop$year - mean(pop$year))/sd(pop$year)
pop$pop_c <- pop$population - 1690
pop$bg_id <- as.integer(pop$GEOID)
# Take a peek at the last rows of 2010 data and the first rows of 2013 data
pop[230:239,]

For model fitting purposes we’ve standardized the years and approximately centered the populations. This won’t change the inferences of the model.

2 Bayes-adjusted population growth rates

2.1 The model

As there are 233 different census block groups in Marion & Polk counties, we will be computing 233 linear regressions of population versus year.

The Bayesian way to incorporate the standard errors of the population estimates is to model the population counts as random samples of normal distributions with unknown means and standard deviations equal to the standard errors. We then fit a normal linear regression on the unknown means.

If we let \(i\) range over the rows of our data, this model can be written as

\[ \begin{align} \textrm{population}_i &\sim \operatorname{Normal}(\mu_i, \textrm{standard error}_i), \\ \mu_i &\sim \operatorname{Normal}(\theta_i, \sigma), \\ \theta_i &= a_{\textrm{block group}_i} + b_{\textrm{block group}_i} \times \textrm{year}_i. \end{align} \]

By \(a_{\textrm{block group}_i}\) and \(b_{\textrm{block group}_i}\) we mean that each block group gets its own coefficients \(a\) and \(b\). More information about this type of model can be found in section 13.2 of Bayesian Statistics with Stan.

Because there is uncertainty in the data, there will be a lot of uncertainty in the coefficients. To compensate for this we should pool information between the coefficients by using a multilevel structure in the model.

We will place an adaptive bivariate normal distribution on the pairs of coefficients so that we can also model correlations between the intercepts and the slopes:

\[ (a,b)_{\textrm{block group}_i} \sim \operatorname{Normal}(\mathbf m, \mathbf \Sigma). \]

Here \(\mathbf m = (m_1, m_2)\) is a vector of length 2 and \(\mathbf \Sigma\) is a 2-by-2 covariance matrix.

library(rstan)
library(coda)

The model code below will look a bit different from what we wrote above. We will use a non-centered parameterization for the parameters \(a\) and \(b\)2 and we will combine the distributions for \(\textrm{population}_i\) and \(\mu_i\) like

\[ \textrm{population}_i \sim \operatorname{Normal}\!\left(\theta_i, \sqrt{\textrm{standard error}_i^2 + \sigma^2}\right). \]

We’ve chosen priors to be mildly informative based on runs of the model with far fewer iterations than we ask for here. The standard deviation of the residuals, \(\sigma\), is particularly troublesome, so we run 4 chains for 100,000 iterations each.

model_code <- "
data{
    int<lower=1> N;
    int<lower=1> N_bg_id;
    real pop_c[N];
    real year_s[N];
    vector<lower=0>[N] std_err;
    int bg_id[N];
}
transformed data{
    vector<lower=0>[N] se2;
    se2 = square(std_err);
}
parameters{
    real a;
    real b;
    vector[N_bg_id] b_bg;
    vector[N_bg_id] a_bg;
    real<lower=0> sigma;
    real<lower=0> sigma_a;
    real<lower=0> sigma_b;
    corr_matrix[2] Rho;
}
transformed parameters{
    vector[2] v_a_bgb_bg[N_bg_id];
    vector[2] Mu_00;
    real<lower=0> sigma2;
    for (j in 1:N_bg_id) {
        v_a_bgb_bg[j,1] = a_bg[j];
        v_a_bgb_bg[j,2] = b_bg[j];
    }
    Mu_00[1] = 0;
    Mu_00[2] = 0;
    sigma2 = square(sigma);
}
model{
    vector[N] mu;
    Rho ~ lkj_corr(1);
    sigma_b ~ gamma(7, 0.1);
    sigma_a ~ gamma(234, 0.3);
    sigma ~ gamma(5, 0.4);
    v_a_bgb_bg ~ multi_normal(Mu_00, Rho);
    b ~ student_t(3, 5, 10);
    a ~ student_t(3, -5, 20);
    for (i in 1:N) {
        mu[i] = a + a_bg[bg_id[i]]*sigma_a + (b + b_bg[bg_id[i]]*sigma_b)*year_s[i];
    }
    pop_c ~ normal(mu, sqrt(sigma2 + se2));
}
"

fit <- stan(
    model_code = model_code,
    data = list(
        N = nrow(pop),
        N_bg_id = max(pop$bg_id),
        pop_c = pop$pop_c,
        year_s = pop$year_s,
        std_err = pop$std_err,
        bg_id = pop$bg_id
    ),
    iter = 1e5,
    warmup = 5e3 ,
    chains = 4,
    cores = 4,
    control = list(adapt_delta = 0.999, max_treedepth = 15)
)

This model took just under 30 hours to fit on my laptop.

2.2 Results

Let’s start by looking at the posterior summary.

smry <- summary(
        fit,
        pars = c("a", "b", "sigma", "sigma_a", "sigma_b", "Rho[1,2]",
                 "a_bg[1]", "a_bg[100]", "b_bg[1]", "b_bg[100]"),
        probs = c(0.025, 0.975)
    )$summary
smry[,"n_eff"] <- round(smry[,"n_eff"])
print(round(smry, 2))
            mean se_mean    sd   2.5%  97.5%  n_eff Rhat
a          -4.10    0.11 22.64 -49.60  42.37  42043    1
b           7.73    0.01  4.62  -1.23  16.92 288478    1
sigma      11.27    0.08  4.18   4.24  20.39   2764    1
sigma_a   776.43    0.17 29.83 720.18 837.02  32534    1
sigma_b    69.65    0.01  4.74  60.74  79.38 147715    1
Rho[1,2]    0.31    0.00  0.07   0.16   0.44 396774    1
a_bg[1]    -1.37    0.00  0.08  -1.53  -1.23  45028    1
a_bg[100]  -0.06    0.00  0.10  -0.26   0.14 296235    1
b_bg[1]    -0.51    0.00  0.30  -1.11   0.07 749323    1
b_bg[100]   1.42    0.00  0.59   0.27   2.58 796050    1

Stan estimates that we only got around 2700 effective samples for sigma (or \(\sigma\)), but this is enough for our purposes. We’re most interested in the coefficients \(b_{\textrm{block group}_i}\), which are b + b_bg[i]*sigma_b in this implementation of the model.

It’s interesting to note that the model estimates a correlation of about 0.3 between the slopes and intercepts. This is not a huge corrlation, but it does tell us that block groups with larger than average populations tend to have more population growth, and block groups with smaller than average populations tend to have less growth (or may even have shrinking populations).

What’s the average yearly growth in these block groups?

samples <- extract(fit)
avg_growth <- samples$b/sd(pop$year)  # un-standardize the predictor
dimnames(avg_growth) <- NULL
avg_growth_mu <- mean(avg_growth)
avg_growth_sd <- sd(avg_growth)
avg_growth_PI <- HPDinterval(as.mcmc(avg_growth), prob = 0.89)[1,]
temp <- data.frame(
    mu = round(avg_growth_mu, 2),
    sd = round(avg_growth_sd, 2),
    PI = paste(round(avg_growth_PI[1], 1), "to", round(avg_growth_PI[2], 1))
)
rownames(temp) <- "average yearly growth"
colnames(temp) <- c("mean", "std dev", "89% probability interval")
temp

So the model estimates that the average growth of the block groups over the time period 2010-2017 is between 0.2 and 6.6 people per year with probability 89%, with the most likely estimate being around 3.41 people per year.

Finally, here are the summaries of the estimated growth rates for each of the 233 block groups.

scaled_rates <- sapply(1:max(pop$bg_id), function(i) samples$b + samples$b_bg[,i]*samples$sigma_b)
rates <- scaled_rates/sd(pop$year)
rates_mu <- apply(rates, 2, mean)
rates_sd <- apply(rates, 2, sd)
rates_PI <- apply(rates, 2, function(r) HPDinterval(as.mcmc(r), prob = 0.89)[1,])
results <- data.frame(
    GEOID = levels(pop$GEOID),
    rate = round(rates_mu, 2),
    std_dev = round(rates_sd, 2),
    HPDI_89 = paste(round(rates_PI[1,], 1), "to", round(rates_PI[2,], 1))
)
colnames(results) <- c("GEOID", "growth_rate", "std dev", "89% probability interval")
results[order(results$growth_rate),]

This table of estimates can be downloaded here.

3 Mapping the growth rates

library(geojsonio)
library(leaflet)

First we’ll make a map showing the raw growth rates (people per year) of each block group.

blockgroups <- geojson_read("popdata/blockgroups_growth.geojson", what = "sp")
pal <- colorNumeric("plasma", NULL)
leaflet(blockgroups) %>%
    addProviderTiles(providers$OpenStreetMap.DE) %>%
    addPolygons(
        stroke = FALSE,
        smoothFactor = 0.3,
        fillOpacity = 0.7,
        fillColor = ~pal(est_growth_rate),
        label = ~paste(
            ifelse(
                est_growth_rate > 0,
                paste0("+", round(est_growth_rate, 1)),
                round(est_growth_rate, 1)
            ),
            "people per year"
        ),
        labelOptions = labelOptions(textsize = "15px")
    ) %>%
    addLegend(pal = pal, values = ~est_growth_rate, opacity = 1.0, title = "people per year")

Some of these block groups are pretty big, which may skew their growth rates toward the upper and lower extremes. We’ll make a second map which instead shows changes in population densities, people per square mile (of land) per year.

leaflet(blockgroups) %>%
    addProviderTiles(providers$OpenStreetMap.DE) %>%
    addPolygons(
        stroke = FALSE,
        smoothFactor = 0.3,
        fillOpacity = 0.7,
        fillColor = ~pal(growth_rate_density),
        label = ~paste(
            ifelse(
                round(growth_rate_density, 1) > 0,
                paste0("+", round(growth_rate_density, 1)),
                round(growth_rate_density, 1)
            ),
            "people per square mile per year"
        ),
        labelOptions = labelOptions(textsize = "15px")
    ) %>%
    addLegend(
        pal = pal,
        values = ~growth_rate_density,
        bins = 8,
        opacity = 1.0,
        title = "people per square mile per year"
    )

The GeoJSON file used to make these maps can be downloaded here. It was built in QGIS by merging the shapefiles for the census block groups from the U.S. Census Bureau with the estimates from our model.

4 Appendix: Fitting the model in rethinking

We could also implement a version of the model which retains the unknown means \(\mu_i\), giving us estimates for the true populations of each block group which are informed by the linear regression on \(\textrm{year}\). This is relatively straightforward to implement using the rethinking package.3

library(rethinking)

# replace standard errors of 0 with 0.01
pop$std_err_bump <- sapply(pop$std_err, function(x) max(0.01, x))

fit2 <- map2stan(
    alist(
        pop_c_est ~ dnorm(mu, sigma),
        mu <- a + a_bg[bg_id] + (b + b_bg[bg_id])*year_s,
        pop_c ~ dnorm(pop_c_est, std_err_bump),
        a ~ dnorm(-5, 40),
        b ~ dnorm(8, 10),
        sigma ~ dgamma(4, 0.5),
        c(a_bg, b_bg)[bg_id] ~ dmvnormNC(sigma_bg, Rho),
        sigma_bg ~ dexp(0.0012),
        Rho ~ dlkjcorr(1)
    ),
    data = pop,
    iter = 4e3,
    start = list(pop_c_est = pop$pop_c),
    control = list(adapt_delta = 0.95, max_treedepth = 15)
)

The slope estimates from this are essentially the same as those from the first model. Running this small number of iterations took about 90 minutes to fit on my laptop. Getting enough iterations for a clear picture of sigma would probably take just as long as, if not longer than, it took for the first model.


Antonio R. Vargas

22 Dec 2018


  1. The Census Bureau publishes a Design and Methodology Report which includes details on how the estimates and margins of error are obtained. This is still on my “to-read” list.

  2. See the “Overthinking” box at the end of section 13.3 of the book Statistical Rethinking by McElreath and the paper Hamiltonian Monte Carlo for Hierarchical Models by Betancourt and Girolami.

  3. See section 14.1 in Statistical Rethinking.

LS0tDQp0aXRsZTogIkRhdGEgQW5hbHlzaXM6IFBvcHVsYXRpb24gR3Jvd3RoIGluIE1hcmlvbiAmIFBvbGsgQ291bnRpZXMiDQphdXRob3I6IEEuIFIuIFZhcmdhcw0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIHRvYzogdHJ1ZQ0KICAgIHRoZW1lOiByZWFkYWJsZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KLS0tDQoNClRoaXMgbm90ZSBpcyBhbiBhbmFseXNpcyBvZiBwb3B1bGF0aW9uIGdyb3d0aCBpbiBNYXJpb24gYW5kIFBvbGsgY291bnRpZXMnIDIzMyBjZW5zdXMgYmxvY2sgZ3JvdXBzLiBUaGUgYW5hbHlzaXMgd2lsbCBiZSBmdWxseSBCYXllc2lhbiwgdXNpbmcgdGhlIFIgcGFja2FnZSAqKnJzdGFuKiogdG8gaW50ZXJmYWNlIHdpdGggdGhlIFtTdGFuXShodHRwOi8vbWMtc3Rhbi5vcmcvKSBzYW1wbGluZyBlbmdpbmUuDQoNCldlIHdpbGwgZml0IGEgbXVsdGlsZXZlbCBtb2RlbCB0byBlc3RpbWF0ZSBzZXBhcmF0ZSBncm93dGggcmF0ZXMgZm9yIGVhY2ggYmxvY2sgZ3JvdXAuDQoNCllvdSBjYW4gY2xpY2sgYW55IGhlYWRpbmcgaW4gdGhpcyBwYWdlIHRvIHJldHVybiBiYWNrIGhlcmUgdG8gdGhlIHRhYmxlIG9mIGNvbnRlbnRzLg0KDQojIFRoZSBkYXRhDQoNCkZvciB0aGlzIHN0dWR5IHdlJ2xsIGNvbWJpbmUgdGhlIHBvcHVsYXRpb24gY291bnRzIGZyb20gdGhlIDIwMTAgQ2Vuc3VzIHdpdGggdGhlIEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkgNS15ZWFyIEVzdGltYXRlcy4gW0Zyb20gdGhlIENlbnN1cyBCdXJlYXUgd2Vic2l0ZV0oaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL2Fjcy9hYm91dC90b3AtcXVlc3Rpb25zLWFib3V0LXRoZS1zdXJ2ZXkuaHRtbCk6DQoNCj5FdmVyeSB5ZWFyLCB0aGUgVS5TLiBDZW5zdXMgQnVyZWF1IGNvbnRhY3RzIG92ZXIgMy41IG1pbGxpb24gaG91c2Vob2xkcyBhY3Jvc3MgdGhlIGNvdW50cnkgdG8gcGFydGljaXBhdGUgaW4gdGhlIEFtZXJpY2FuIENvbW11bml0eSBTdXJ2ZXkuIFVubGlrZSB0aGUgZXZlcnktMTAteWVhciBjZW5zdXMsIHRoaXMgc3VydmV5IGNvbnRpbnVlcyBhbGwgeWVhciwgZXZlcnkgeWVhci4gV2UgcmFuZG9tbHkgc2FtcGxlIGFkZHJlc3NlcyBpbiBldmVyeSBzdGF0ZSwgdGhlIERpc3RyaWN0IG9mIENvbHVtYmlhLCBhbmQgUHVlcnRvIFJpY28uDQoNClRoZSBDZW5zdXMgQnVyZWF1IHVzZXMgdGhlIHJlc3BvbnNlcyBmcm9tIGVhY2ggc2FtcGxlIG9mIHRoZSBwb3B1bGF0aW9uIHRvIGV4dHJhcG9sYXRlIGluZm9ybWF0aW9uIGFib3V0IHRoZSB0b3RhbCBwb3B1bGF0aW9uLCBzbyB0aGVyZSBpcyBzb21lIHVuY2VydGFpbnR5IGFyb3VuZCBpdHMgZXN0aW1hdGVzLiBFYWNoIGVzdGltYXRlIGlzIHJlcG9ydGVkIGFsb25nIHdpdGggYSA5MCUgbWFyZ2luIG9mIGVycm9yLlteMV0gSXQncyBleHRyZW1lbHkgaW1wb3J0YW50IHRoYXQgd2UgaW5jb3Jwb3JhdGUgdGhpcyB1bmNlcnRhaW50eSBpbiBvdXIgYW5hbHlzaXMuDQoNClteMV06IFRoZSBDZW5zdXMgQnVyZWF1IHB1Ymxpc2hlcyBhIFtEZXNpZ24gYW5kIE1ldGhvZG9sb2d5IFJlcG9ydF0oaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9wcm9ncmFtcy1zdXJ2ZXlzL2Fjcy9tZXRob2RvbG9neS9kZXNpZ24tYW5kLW1ldGhvZG9sb2d5Lmh0bWwpIHdoaWNoIGluY2x1ZGVzIGRldGFpbHMgb24gaG93IHRoZSBlc3RpbWF0ZXMgYW5kIG1hcmdpbnMgb2YgZXJyb3IgYXJlIG9idGFpbmVkLiBUaGlzIGlzIHN0aWxsIG9uIG15ICJ0by1yZWFkIiBsaXN0Lg0KDQpUaGUgQ2Vuc3VzIEJ1cmVhdSByZWxlYXNlZCB0aGUgMjAxMy0yMDE3IEFDUyA1LXllYXIgRXN0aW1hdGVzIG9uIERlY2VtYmVyIDYsIDIwMTguDQoNCldlIGRvd25sb2FkZWQgdGhlIHBvcHVsYXRpb24gY291bnRzIGZvciB0aGUgY2Vuc3VzIGJsb2NrIGdyb3VwcyBpbiBNYXJpb24gYW5kIFBvbGsgY291bnRpZXMgZnJvbSB0aGUgW0FtZXJpY2FuIEZhY3RGaW5kZXJdKGh0dHBzOi8vZmFjdGZpbmRlci5jZW5zdXMuZ292L2ZhY2VzL25hdi9qc2YvcGFnZXMvaW5kZXgueGh0bWwpIGZyb20gdGhlIDIwMTAgQ2Vuc3VzIGFzIHdlbGwgYXMgZWFjaCBvZiB0aGUgeWVhcnMgaW4gdGhlIDIwMTMtMjAxNyA1LXllYXIgRXN0aW1hdGVzLiBJJ3ZlIHJlLWhvc3RlZCB0aGUgZGF0YSBbaGVyZV0oaHR0cHM6Ly9tYXRoc3RhdC5kYWwuY2EvfmFudG9uaW92L3BvcGRhdGEvKSBmb3IgY29udmVuaWVuY2UuDQoNCmBgYHtyfQ0KcG9wXzIwMTAgPC0gcmVhZC5jc3YoInBvcF8yMDEwLmNzdiIpDQpwb3BfMjAxMyA8LSByZWFkLmNzdigicG9wXzIwMTMuY3N2IikNCnBvcF8yMDE0IDwtIHJlYWQuY3N2KCJwb3BfMjAxNC5jc3YiKQ0KcG9wXzIwMTUgPC0gcmVhZC5jc3YoInBvcF8yMDE1LmNzdiIpDQpwb3BfMjAxNiA8LSByZWFkLmNzdigicG9wXzIwMTYuY3N2IikNCnBvcF8yMDE3IDwtIHJlYWQuY3N2KCJwb3BfMjAxNy5jc3YiKQ0KYGBgDQoNCldlJ2xsIGNvbnZlcnQgdGhlIDkwJSBtYXJnaW5zIG9mIGVycm9yIGZvciBlYWNoIHBvcHVsYXRpb24gY291bnQgdG8gKnN0YW5kYXJkIGVycm9ycyogYnkgZGl2aWRpbmcgdGhlbSBieSAxLjY0NSwgYW5kIHdlJ2xsIHRha2UgdGhlIHN0YW5kYXJkIGVycm9ycyBvZiB0aGUgMjAxMCBDZW5zdXMgZGF0YSB0byBiZSAwLg0KDQpgYGB7cn0NCnBvcCA8LSBkYXRhLmZyYW1lKA0KICAgIEdFT0lEID0gYygNCiAgICAgICAgYXMuY2hhcmFjdGVyKHBvcF8yMDEwJEdFTy5pZCksDQogICAgICAgIGFzLmNoYXJhY3Rlcihwb3BfMjAxMyRHRU8uaWQpLA0KICAgICAgICBhcy5jaGFyYWN0ZXIocG9wXzIwMTQkR0VPLmlkKSwNCiAgICAgICAgYXMuY2hhcmFjdGVyKHBvcF8yMDE1JEdFTy5pZCksDQogICAgICAgIGFzLmNoYXJhY3Rlcihwb3BfMjAxNiRHRU8uaWQpLA0KICAgICAgICBhcy5jaGFyYWN0ZXIocG9wXzIwMTckR0VPLmlkKQ0KICAgICksDQogICAgcG9wdWxhdGlvbiA9IGMoDQogICAgICAgIHBvcF8yMDEwJEQwMDEsDQogICAgICAgIHBvcF8yMDEzJEhEMDFfVkQwMSwNCiAgICAgICAgcG9wXzIwMTQkSEQwMV9WRDAxLA0KICAgICAgICBwb3BfMjAxNSRIRDAxX1ZEMDEsDQogICAgICAgIHBvcF8yMDE2JEhEMDFfVkQwMSwNCiAgICAgICAgcG9wXzIwMTckSEQwMV9WRDAxDQogICAgKSwNCiAgICBzdGRfZXJyID0gYygNCiAgICAgICAgcmVwKDAsIG5yb3cocG9wXzIwMTApKSwNCiAgICAgICAgcG9wXzIwMTMkSEQwMl9WRDAxLA0KICAgICAgICBwb3BfMjAxNCRIRDAyX1ZEMDEsDQogICAgICAgIHBvcF8yMDE1JEhEMDJfVkQwMSwNCiAgICAgICAgcG9wXzIwMTYkSEQwMl9WRDAxLA0KICAgICAgICBwb3BfMjAxNyRIRDAyX1ZEMDENCiAgICApLzEuNjQ1LA0KICAgIHllYXIgPSBjKA0KICAgICAgICByZXAoMjAxMCwgbnJvdyhwb3BfMjAxMCkpLA0KICAgICAgICByZXAoMjAxMywgbnJvdyhwb3BfMjAxMykpLA0KICAgICAgICByZXAoMjAxNCwgbnJvdyhwb3BfMjAxNCkpLA0KICAgICAgICByZXAoMjAxNSwgbnJvdyhwb3BfMjAxNSkpLA0KICAgICAgICByZXAoMjAxNiwgbnJvdyhwb3BfMjAxNikpLA0KICAgICAgICByZXAoMjAxNywgbnJvdyhwb3BfMjAxNykpDQogICAgKQ0KKQ0KcG9wJHllYXJfcyA8LSAocG9wJHllYXIgLSBtZWFuKHBvcCR5ZWFyKSkvc2QocG9wJHllYXIpDQpwb3AkcG9wX2MgPC0gcG9wJHBvcHVsYXRpb24gLSAxNjkwDQpwb3AkYmdfaWQgPC0gYXMuaW50ZWdlcihwb3AkR0VPSUQpDQojIFRha2UgYSBwZWVrIGF0IHRoZSBsYXN0IHJvd3Mgb2YgMjAxMCBkYXRhIGFuZCB0aGUgZmlyc3Qgcm93cyBvZiAyMDEzIGRhdGENCnBvcFsyMzA6MjM5LF0NCmBgYA0KDQpGb3IgbW9kZWwgZml0dGluZyBwdXJwb3NlcyB3ZSd2ZSBzdGFuZGFyZGl6ZWQgdGhlIHllYXJzIGFuZCBhcHByb3hpbWF0ZWx5IGNlbnRlcmVkIHRoZSBwb3B1bGF0aW9ucy4gVGhpcyB3b24ndCBjaGFuZ2UgdGhlIGluZmVyZW5jZXMgb2YgdGhlIG1vZGVsLg0KDQojIEJheWVzLWFkanVzdGVkIHBvcHVsYXRpb24gZ3Jvd3RoIHJhdGVzDQoNCiMjIFRoZSBtb2RlbA0KDQpBcyB0aGVyZSBhcmUgMjMzIGRpZmZlcmVudCBjZW5zdXMgYmxvY2sgZ3JvdXBzIGluIE1hcmlvbiAmIFBvbGsgY291bnRpZXMsIHdlIHdpbGwgYmUgY29tcHV0aW5nIDIzMyBsaW5lYXIgcmVncmVzc2lvbnMgb2YgcG9wdWxhdGlvbiB2ZXJzdXMgeWVhci4NCg0KVGhlIEJheWVzaWFuIHdheSB0byBpbmNvcnBvcmF0ZSB0aGUgc3RhbmRhcmQgZXJyb3JzIG9mIHRoZSBwb3B1bGF0aW9uIGVzdGltYXRlcyBpcyB0byBtb2RlbCB0aGUgcG9wdWxhdGlvbiBjb3VudHMgYXMgcmFuZG9tIHNhbXBsZXMgb2Ygbm9ybWFsIGRpc3RyaWJ1dGlvbnMgd2l0aCB1bmtub3duIG1lYW5zIGFuZCBzdGFuZGFyZCBkZXZpYXRpb25zIGVxdWFsIHRvIHRoZSBzdGFuZGFyZCBlcnJvcnMuIFdlIHRoZW4gZml0IGEgbm9ybWFsIGxpbmVhciByZWdyZXNzaW9uIG9uIHRoZSB1bmtub3duIG1lYW5zLg0KDQpJZiB3ZSBsZXQgJGkkIHJhbmdlIG92ZXIgdGhlIHJvd3Mgb2Ygb3VyIGRhdGEsIHRoaXMgbW9kZWwgY2FuIGJlIHdyaXR0ZW4gYXMNCg0KJCQNClxiZWdpbnthbGlnbn0NClx0ZXh0cm17cG9wdWxhdGlvbn1faSAmXHNpbSBcb3BlcmF0b3JuYW1le05vcm1hbH0oXG11X2ksIFx0ZXh0cm17c3RhbmRhcmQgZXJyb3J9X2kpLCBcXA0KXG11X2kgJlxzaW0gXG9wZXJhdG9ybmFtZXtOb3JtYWx9KFx0aGV0YV9pLCBcc2lnbWEpLCBcXA0KXHRoZXRhX2kgJj0gYV97XHRleHRybXtibG9jayBncm91cH1faX0gKyBiX3tcdGV4dHJte2Jsb2NrIGdyb3VwfV9pfSBcdGltZXMgXHRleHRybXt5ZWFyfV9pLg0KXGVuZHthbGlnbn0NCiQkDQoNCkJ5ICRhX3tcdGV4dHJte2Jsb2NrIGdyb3VwfV9pfSQgYW5kICRiX3tcdGV4dHJte2Jsb2NrIGdyb3VwfV9pfSQgd2UgbWVhbiB0aGF0IGVhY2ggYmxvY2sgZ3JvdXAgZ2V0cyBpdHMgb3duIGNvZWZmaWNpZW50cyAkYSQgYW5kICRiJC4gTW9yZSBpbmZvcm1hdGlvbiBhYm91dCB0aGlzIHR5cGUgb2YgbW9kZWwgY2FuIGJlIGZvdW5kIGluIFtzZWN0aW9uIDEzLjIgb2YgQmF5ZXNpYW4gU3RhdGlzdGljcyB3aXRoIFN0YW5dKGh0dHBzOi8vbWMtc3Rhbi5vcmcvZG9jcy8yXzE4L2JheWVzLXN0YXRzLXN0YW4vbWVhc3VyZW1lbnQtZXJyb3ItYW5kLW1ldGEtYW5hbHlzaXMuaHRtbCkuDQoNCkJlY2F1c2UgdGhlcmUgaXMgdW5jZXJ0YWludHkgaW4gdGhlIGRhdGEsIHRoZXJlIHdpbGwgYmUgYSBsb3Qgb2YgdW5jZXJ0YWludHkgaW4gdGhlIGNvZWZmaWNpZW50cy4gVG8gY29tcGVuc2F0ZSBmb3IgdGhpcyB3ZSBzaG91bGQgcG9vbCBpbmZvcm1hdGlvbiBiZXR3ZWVuIHRoZSBjb2VmZmljaWVudHMgYnkgdXNpbmcgYSBtdWx0aWxldmVsIHN0cnVjdHVyZSBpbiB0aGUgbW9kZWwuDQoNCldlIHdpbGwgcGxhY2UgYW4gYWRhcHRpdmUgYml2YXJpYXRlIG5vcm1hbCBkaXN0cmlidXRpb24gb24gdGhlIHBhaXJzIG9mIGNvZWZmaWNpZW50cyBzbyB0aGF0IHdlIGNhbiBhbHNvIG1vZGVsIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIHRoZSBpbnRlcmNlcHRzIGFuZCB0aGUgc2xvcGVzOg0KDQokJA0KKGEsYilfe1x0ZXh0cm17YmxvY2sgZ3JvdXB9X2l9IFxzaW0gXG9wZXJhdG9ybmFtZXtOb3JtYWx9KFxtYXRoYmYgbSwgXG1hdGhiZiBcU2lnbWEpLg0KJCQNCg0KSGVyZSAkXG1hdGhiZiBtID0gKG1fMSwgbV8yKSQgaXMgYSB2ZWN0b3Igb2YgbGVuZ3RoIDIgYW5kICRcbWF0aGJmIFxTaWdtYSQgaXMgYSAyLWJ5LTIgY292YXJpYW5jZSBtYXRyaXguDQoNCmBgYHtyfQ0KbGlicmFyeShyc3RhbikNCmxpYnJhcnkoY29kYSkNCmBgYA0KDQpUaGUgbW9kZWwgY29kZSBiZWxvdyB3aWxsIGxvb2sgYSBiaXQgZGlmZmVyZW50IGZyb20gd2hhdCB3ZSB3cm90ZSBhYm92ZS4gV2Ugd2lsbCB1c2UgYSBub24tY2VudGVyZWQgcGFyYW1ldGVyaXphdGlvbiBmb3IgdGhlIHBhcmFtZXRlcnMgJGEkIGFuZCAkYiRbXjJdIGFuZCB3ZSB3aWxsIGNvbWJpbmUgdGhlIGRpc3RyaWJ1dGlvbnMgZm9yICRcdGV4dHJte3BvcHVsYXRpb259X2kkIGFuZCAkXG11X2kkIGxpa2UNCg0KJCQNClx0ZXh0cm17cG9wdWxhdGlvbn1faSBcc2ltIFxvcGVyYXRvcm5hbWV7Tm9ybWFsfVwhXGxlZnQoXHRoZXRhX2ksIFxzcXJ0e1x0ZXh0cm17c3RhbmRhcmQgZXJyb3J9X2leMiArIFxzaWdtYV4yfVxyaWdodCkuDQokJA0KDQpbXjJdOiBTZWUgdGhlICJPdmVydGhpbmtpbmciIGJveCBhdCB0aGUgZW5kIG9mIHNlY3Rpb24gMTMuMyBvZiB0aGUgYm9vayBbKlN0YXRpc3RpY2FsIFJldGhpbmtpbmcqXShodHRwczovL3hjZWxhYi5uZXQvcm0vc3RhdGlzdGljYWwtcmV0aGlua2luZy8pIGJ5IE1jRWxyZWF0aCBhbmQgdGhlIHBhcGVyIFtIYW1pbHRvbmlhbiBNb250ZSBDYXJsbyBmb3IgSGllcmFyY2hpY2FsIE1vZGVsc10oaHR0cHM6Ly9hcnhpdi5vcmcvYWJzLzEzMTIuMDkwNikgYnkgQmV0YW5jb3VydCBhbmQgR2lyb2xhbWkuDQoNCldlJ3ZlIGNob3NlbiBwcmlvcnMgdG8gYmUgbWlsZGx5IGluZm9ybWF0aXZlIGJhc2VkIG9uIHJ1bnMgb2YgdGhlIG1vZGVsIHdpdGggZmFyIGZld2VyIGl0ZXJhdGlvbnMgdGhhbiB3ZSBhc2sgZm9yIGhlcmUuIFRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIHJlc2lkdWFscywgJFxzaWdtYSQsIGlzIHBhcnRpY3VsYXJseSB0cm91Ymxlc29tZSwgc28gd2UgcnVuIDQgY2hhaW5zIGZvciAxMDAsMDAwIGl0ZXJhdGlvbnMgZWFjaC4NCg0KYGBge3J9DQptb2RlbF9jb2RlIDwtICINCmRhdGF7DQogICAgaW50PGxvd2VyPTE+IE47DQogICAgaW50PGxvd2VyPTE+IE5fYmdfaWQ7DQogICAgcmVhbCBwb3BfY1tOXTsNCiAgICByZWFsIHllYXJfc1tOXTsNCiAgICB2ZWN0b3I8bG93ZXI9MD5bTl0gc3RkX2VycjsNCiAgICBpbnQgYmdfaWRbTl07DQp9DQp0cmFuc2Zvcm1lZCBkYXRhew0KICAgIHZlY3Rvcjxsb3dlcj0wPltOXSBzZTI7DQogICAgc2UyID0gc3F1YXJlKHN0ZF9lcnIpOw0KfQ0KcGFyYW1ldGVyc3sNCiAgICByZWFsIGE7DQogICAgcmVhbCBiOw0KICAgIHZlY3RvcltOX2JnX2lkXSBiX2JnOw0KICAgIHZlY3RvcltOX2JnX2lkXSBhX2JnOw0KICAgIHJlYWw8bG93ZXI9MD4gc2lnbWE7DQogICAgcmVhbDxsb3dlcj0wPiBzaWdtYV9hOw0KICAgIHJlYWw8bG93ZXI9MD4gc2lnbWFfYjsNCiAgICBjb3JyX21hdHJpeFsyXSBSaG87DQp9DQp0cmFuc2Zvcm1lZCBwYXJhbWV0ZXJzew0KICAgIHZlY3RvclsyXSB2X2FfYmdiX2JnW05fYmdfaWRdOw0KICAgIHZlY3RvclsyXSBNdV8wMDsNCiAgICByZWFsPGxvd2VyPTA+IHNpZ21hMjsNCiAgICBmb3IgKGogaW4gMTpOX2JnX2lkKSB7DQogICAgICAgIHZfYV9iZ2JfYmdbaiwxXSA9IGFfYmdbal07DQogICAgICAgIHZfYV9iZ2JfYmdbaiwyXSA9IGJfYmdbal07DQogICAgfQ0KICAgIE11XzAwWzFdID0gMDsNCiAgICBNdV8wMFsyXSA9IDA7DQogICAgc2lnbWEyID0gc3F1YXJlKHNpZ21hKTsNCn0NCm1vZGVsew0KICAgIHZlY3RvcltOXSBtdTsNCiAgICBSaG8gfiBsa2pfY29ycigxKTsNCiAgICBzaWdtYV9iIH4gZ2FtbWEoNywgMC4xKTsNCiAgICBzaWdtYV9hIH4gZ2FtbWEoMjM0LCAwLjMpOw0KICAgIHNpZ21hIH4gZ2FtbWEoNSwgMC40KTsNCiAgICB2X2FfYmdiX2JnIH4gbXVsdGlfbm9ybWFsKE11XzAwLCBSaG8pOw0KICAgIGIgfiBzdHVkZW50X3QoMywgNSwgMTApOw0KICAgIGEgfiBzdHVkZW50X3QoMywgLTUsIDIwKTsNCiAgICBmb3IgKGkgaW4gMTpOKSB7DQogICAgICAgIG11W2ldID0gYSArIGFfYmdbYmdfaWRbaV1dKnNpZ21hX2EgKyAoYiArIGJfYmdbYmdfaWRbaV1dKnNpZ21hX2IpKnllYXJfc1tpXTsNCiAgICB9DQogICAgcG9wX2MgfiBub3JtYWwobXUsIHNxcnQoc2lnbWEyICsgc2UyKSk7DQp9DQoiDQoNCmZpdCA8LSBzdGFuKA0KICAgIG1vZGVsX2NvZGUgPSBtb2RlbF9jb2RlLA0KICAgIGRhdGEgPSBsaXN0KA0KICAgICAgICBOID0gbnJvdyhwb3ApLA0KICAgICAgICBOX2JnX2lkID0gbWF4KHBvcCRiZ19pZCksDQogICAgICAgIHBvcF9jID0gcG9wJHBvcF9jLA0KICAgICAgICB5ZWFyX3MgPSBwb3AkeWVhcl9zLA0KICAgICAgICBzdGRfZXJyID0gcG9wJHN0ZF9lcnIsDQogICAgICAgIGJnX2lkID0gcG9wJGJnX2lkDQogICAgKSwNCiAgICBpdGVyID0gMWU1LA0KICAgIHdhcm11cCA9IDVlMyAsDQogICAgY2hhaW5zID0gNCwNCiAgICBjb3JlcyA9IDQsDQogICAgY29udHJvbCA9IGxpc3QoYWRhcHRfZGVsdGEgPSAwLjk5OSwgbWF4X3RyZWVkZXB0aCA9IDE1KQ0KKQ0KYGBgDQoNClRoaXMgbW9kZWwgdG9vayBqdXN0IHVuZGVyIDMwIGhvdXJzIHRvIGZpdCBvbiBteSBsYXB0b3AuDQoNCiMjIFJlc3VsdHMNCg0KTGV0J3Mgc3RhcnQgYnkgbG9va2luZyBhdCB0aGUgcG9zdGVyaW9yIHN1bW1hcnkuDQoNCmBgYHtyfQ0Kc21yeSA8LSBzdW1tYXJ5KA0KICAgICAgICBmaXQsDQogICAgICAgIHBhcnMgPSBjKCJhIiwgImIiLCAic2lnbWEiLCAic2lnbWFfYSIsICJzaWdtYV9iIiwgIlJob1sxLDJdIiwNCiAgICAgICAgICAgICAgICAgImFfYmdbMV0iLCAiYV9iZ1sxMDBdIiwgImJfYmdbMV0iLCAiYl9iZ1sxMDBdIiksDQogICAgICAgIHByb2JzID0gYygwLjAyNSwgMC45NzUpDQogICAgKSRzdW1tYXJ5DQpzbXJ5Wywibl9lZmYiXSA8LSByb3VuZChzbXJ5Wywibl9lZmYiXSkNCnByaW50KHJvdW5kKHNtcnksIDIpKQ0KYGBgDQoNClN0YW4gZXN0aW1hdGVzIHRoYXQgd2Ugb25seSBnb3QgYXJvdW5kIDI3MDAgZWZmZWN0aXZlIHNhbXBsZXMgZm9yIGBzaWdtYWAgKG9yICRcc2lnbWEkKSwgYnV0IHRoaXMgaXMgZW5vdWdoIGZvciBvdXIgcHVycG9zZXMuIFdlJ3JlIG1vc3QgaW50ZXJlc3RlZCBpbiB0aGUgY29lZmZpY2llbnRzICRiX3tcdGV4dHJte2Jsb2NrIGdyb3VwfV9pfSQsIHdoaWNoIGFyZSBgYiArIGJfYmdbaV0qc2lnbWFfYmAgaW4gdGhpcyBpbXBsZW1lbnRhdGlvbiBvZiB0aGUgbW9kZWwuDQoNCkl0J3MgaW50ZXJlc3RpbmcgdG8gbm90ZSB0aGF0IHRoZSBtb2RlbCBlc3RpbWF0ZXMgYSBjb3JyZWxhdGlvbiBvZiBhYm91dCAwLjMgYmV0d2VlbiB0aGUgc2xvcGVzIGFuZCBpbnRlcmNlcHRzLiBUaGlzIGlzIG5vdCBhIGh1Z2UgY29ycmxhdGlvbiwgYnV0IGl0IGRvZXMgdGVsbCB1cyB0aGF0IGJsb2NrIGdyb3VwcyB3aXRoIGxhcmdlciB0aGFuIGF2ZXJhZ2UgcG9wdWxhdGlvbnMgdGVuZCB0byBoYXZlIG1vcmUgcG9wdWxhdGlvbiBncm93dGgsIGFuZCBibG9jayBncm91cHMgd2l0aCBzbWFsbGVyIHRoYW4gYXZlcmFnZSBwb3B1bGF0aW9ucyB0ZW5kIHRvIGhhdmUgbGVzcyBncm93dGggKG9yIG1heSBldmVuIGhhdmUgc2hyaW5raW5nIHBvcHVsYXRpb25zKS4NCg0KV2hhdCdzIHRoZSBhdmVyYWdlIHllYXJseSBncm93dGggaW4gdGhlc2UgYmxvY2sgZ3JvdXBzPw0KDQpgYGB7cn0NCnNhbXBsZXMgPC0gZXh0cmFjdChmaXQpDQpgYGANCg0KYGBge3J9DQphdmdfZ3Jvd3RoIDwtIHNhbXBsZXMkYi9zZChwb3AkeWVhcikgICMgdW4tc3RhbmRhcmRpemUgdGhlIHByZWRpY3Rvcg0KZGltbmFtZXMoYXZnX2dyb3d0aCkgPC0gTlVMTA0KDQphdmdfZ3Jvd3RoX211IDwtIG1lYW4oYXZnX2dyb3d0aCkNCmF2Z19ncm93dGhfc2QgPC0gc2QoYXZnX2dyb3d0aCkNCmF2Z19ncm93dGhfUEkgPC0gSFBEaW50ZXJ2YWwoYXMubWNtYyhhdmdfZ3Jvd3RoKSwgcHJvYiA9IDAuODkpWzEsXQ0KDQp0ZW1wIDwtIGRhdGEuZnJhbWUoDQogICAgbXUgPSByb3VuZChhdmdfZ3Jvd3RoX211LCAyKSwNCiAgICBzZCA9IHJvdW5kKGF2Z19ncm93dGhfc2QsIDIpLA0KICAgIFBJID0gcGFzdGUocm91bmQoYXZnX2dyb3d0aF9QSVsxXSwgMSksICJ0byIsIHJvdW5kKGF2Z19ncm93dGhfUElbMl0sIDEpKQ0KKQ0Kcm93bmFtZXModGVtcCkgPC0gImF2ZXJhZ2UgeWVhcmx5IGdyb3d0aCINCmNvbG5hbWVzKHRlbXApIDwtIGMoIm1lYW4iLCAic3RkIGRldiIsICI4OSUgcHJvYmFiaWxpdHkgaW50ZXJ2YWwiKQ0KdGVtcA0KYGBgDQoNClNvIHRoZSBtb2RlbCBlc3RpbWF0ZXMgdGhhdCB0aGUgYXZlcmFnZSBncm93dGggb2YgdGhlIGJsb2NrIGdyb3VwcyBvdmVyIHRoZSB0aW1lIHBlcmlvZCAyMDEwLTIwMTcgaXMgYmV0d2VlbiAwLjIgYW5kIDYuNiBwZW9wbGUgcGVyIHllYXIgd2l0aCBwcm9iYWJpbGl0eSA4OSUsIHdpdGggdGhlIG1vc3QgbGlrZWx5IGVzdGltYXRlIGJlaW5nIGFyb3VuZCAzLjQxIHBlb3BsZSBwZXIgeWVhci4NCg0KRmluYWxseSwgaGVyZSBhcmUgdGhlIHN1bW1hcmllcyBvZiB0aGUgZXN0aW1hdGVkIGdyb3d0aCByYXRlcyBmb3IgZWFjaCBvZiB0aGUgMjMzIGJsb2NrIGdyb3Vwcy4NCg0KYGBge3J9DQpzY2FsZWRfcmF0ZXMgPC0gc2FwcGx5KDE6bWF4KHBvcCRiZ19pZCksIGZ1bmN0aW9uKGkpIHNhbXBsZXMkYiArIHNhbXBsZXMkYl9iZ1ssaV0qc2FtcGxlcyRzaWdtYV9iKQ0KcmF0ZXMgPC0gc2NhbGVkX3JhdGVzL3NkKHBvcCR5ZWFyKQ0KDQpyYXRlc19tdSA8LSBhcHBseShyYXRlcywgMiwgbWVhbikNCnJhdGVzX3NkIDwtIGFwcGx5KHJhdGVzLCAyLCBzZCkNCnJhdGVzX1BJIDwtIGFwcGx5KHJhdGVzLCAyLCBmdW5jdGlvbihyKSBIUERpbnRlcnZhbChhcy5tY21jKHIpLCBwcm9iID0gMC44OSlbMSxdKQ0KDQpyZXN1bHRzIDwtIGRhdGEuZnJhbWUoDQogICAgR0VPSUQgPSBsZXZlbHMocG9wJEdFT0lEKSwNCiAgICByYXRlID0gcm91bmQocmF0ZXNfbXUsIDIpLA0KICAgIHN0ZF9kZXYgPSByb3VuZChyYXRlc19zZCwgMiksDQogICAgSFBESV84OSA9IHBhc3RlKHJvdW5kKHJhdGVzX1BJWzEsXSwgMSksICJ0byIsIHJvdW5kKHJhdGVzX1BJWzIsXSwgMSkpDQopDQpjb2xuYW1lcyhyZXN1bHRzKSA8LSBjKCJHRU9JRCIsICJncm93dGhfcmF0ZSIsICJzdGQgZGV2IiwgIjg5JSBwcm9iYWJpbGl0eSBpbnRlcnZhbCIpDQpyZXN1bHRzW29yZGVyKHJlc3VsdHMkZ3Jvd3RoX3JhdGUpLF0NCmBgYA0KDQpgYGB7ciBldmFsPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQ0Kd3JpdGUuY3N2KHJlc3VsdHMsICJncm93dGggZXN0aW1hdGVzLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KYGBgDQoNClRoaXMgdGFibGUgb2YgZXN0aW1hdGVzIGNhbiBiZSBkb3dubG9hZGVkIFtoZXJlXShodHRwczovL21hdGhzdGF0LmRhbC5jYS9+YW50b25pb3YvcG9wZGF0YS8pLg0KDQojIE1hcHBpbmcgdGhlIGdyb3d0aCByYXRlcw0KDQpgYGB7cn0NCmxpYnJhcnkoZ2VvanNvbmlvKQ0KbGlicmFyeShsZWFmbGV0KQ0KYGBgDQoNCkZpcnN0IHdlJ2xsIG1ha2UgYSBtYXAgc2hvd2luZyB0aGUgcmF3IGdyb3d0aCByYXRlcyAocGVvcGxlIHBlciB5ZWFyKSBvZiBlYWNoIGJsb2NrIGdyb3VwLg0KDQpgYGB7ciBmaWcud2lkdGg9OS40NywgZmlnLmhlaWdodD04fQ0KYmxvY2tncm91cHMgPC0gZ2VvanNvbl9yZWFkKCJwb3BkYXRhL2Jsb2NrZ3JvdXBzX2dyb3d0aC5nZW9qc29uIiwgd2hhdCA9ICJzcCIpDQpwYWwgPC0gY29sb3JOdW1lcmljKCJwbGFzbWEiLCBOVUxMKQ0KbGVhZmxldChibG9ja2dyb3VwcykgJT4lDQogICAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkT3BlblN0cmVldE1hcC5ERSkgJT4lDQogICAgYWRkUG9seWdvbnMoDQogICAgICAgIHN0cm9rZSA9IEZBTFNFLA0KICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLjMsDQogICAgICAgIGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICBmaWxsQ29sb3IgPSB+cGFsKGVzdF9ncm93dGhfcmF0ZSksDQogICAgICAgIGxhYmVsID0gfnBhc3RlKA0KICAgICAgICAgICAgaWZlbHNlKA0KICAgICAgICAgICAgICAgIGVzdF9ncm93dGhfcmF0ZSA+IDAsDQogICAgICAgICAgICAgICAgcGFzdGUwKCIrIiwgcm91bmQoZXN0X2dyb3d0aF9yYXRlLCAxKSksDQogICAgICAgICAgICAgICAgcm91bmQoZXN0X2dyb3d0aF9yYXRlLCAxKQ0KICAgICAgICAgICAgKSwNCiAgICAgICAgICAgICJwZW9wbGUgcGVyIHllYXIiDQogICAgICAgICksDQogICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucyh0ZXh0c2l6ZSA9ICIxNXB4IikNCiAgICApICU+JQ0KICAgIGFkZExlZ2VuZChwYWwgPSBwYWwsIHZhbHVlcyA9IH5lc3RfZ3Jvd3RoX3JhdGUsIG9wYWNpdHkgPSAxLjAsIHRpdGxlID0gInBlb3BsZSBwZXIgeWVhciIpDQpgYGANCg0KU29tZSBvZiB0aGVzZSBibG9jayBncm91cHMgYXJlIHByZXR0eSBiaWcsIHdoaWNoIG1heSBza2V3IHRoZWlyIGdyb3d0aCByYXRlcyB0b3dhcmQgdGhlIHVwcGVyIGFuZCBsb3dlciBleHRyZW1lcy4gV2UnbGwgbWFrZSBhIHNlY29uZCBtYXAgd2hpY2ggaW5zdGVhZCBzaG93cyBjaGFuZ2VzIGluIHBvcHVsYXRpb24gKmRlbnNpdGllcyosIHBlb3BsZSBwZXIgc3F1YXJlIG1pbGUgKG9mIGxhbmQpIHBlciB5ZWFyLg0KDQpgYGB7ciBmaWcud2lkdGg9OS40NywgZmlnLmhlaWdodD04fQ0KbGVhZmxldChibG9ja2dyb3VwcykgJT4lDQogICAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkT3BlblN0cmVldE1hcC5ERSkgJT4lDQogICAgYWRkUG9seWdvbnMoDQogICAgICAgIHN0cm9rZSA9IEZBTFNFLA0KICAgICAgICBzbW9vdGhGYWN0b3IgPSAwLjMsDQogICAgICAgIGZpbGxPcGFjaXR5ID0gMC43LA0KICAgICAgICBmaWxsQ29sb3IgPSB+cGFsKGdyb3d0aF9yYXRlX2RlbnNpdHkpLA0KICAgICAgICBsYWJlbCA9IH5wYXN0ZSgNCiAgICAgICAgICAgIGlmZWxzZSgNCiAgICAgICAgICAgICAgICByb3VuZChncm93dGhfcmF0ZV9kZW5zaXR5LCAxKSA+IDAsDQogICAgICAgICAgICAgICAgcGFzdGUwKCIrIiwgcm91bmQoZ3Jvd3RoX3JhdGVfZGVuc2l0eSwgMSkpLA0KICAgICAgICAgICAgICAgIHJvdW5kKGdyb3d0aF9yYXRlX2RlbnNpdHksIDEpDQogICAgICAgICAgICApLA0KICAgICAgICAgICAgInBlb3BsZSBwZXIgc3F1YXJlIG1pbGUgcGVyIHllYXIiDQogICAgICAgICksDQogICAgICAgIGxhYmVsT3B0aW9ucyA9IGxhYmVsT3B0aW9ucyh0ZXh0c2l6ZSA9ICIxNXB4IikNCiAgICApICU+JQ0KICAgIGFkZExlZ2VuZCgNCiAgICAgICAgcGFsID0gcGFsLA0KICAgICAgICB2YWx1ZXMgPSB+Z3Jvd3RoX3JhdGVfZGVuc2l0eSwNCiAgICAgICAgYmlucyA9IDgsDQogICAgICAgIG9wYWNpdHkgPSAxLjAsDQogICAgICAgIHRpdGxlID0gInBlb3BsZSBwZXIgc3F1YXJlIG1pbGUgcGVyIHllYXIiDQogICAgKQ0KYGBgDQoNClRoZSBHZW9KU09OIGZpbGUgdXNlZCB0byBtYWtlIHRoZXNlIG1hcHMgY2FuIGJlIGRvd25sb2FkZWQgW2hlcmVdKGh0dHBzOi8vbWF0aHN0YXQuZGFsLmNhL35hbnRvbmlvdi9wb3BkYXRhLykuIEl0IHdhcyBidWlsdCBpbiBRR0lTIGJ5IG1lcmdpbmcgdGhlIFtzaGFwZWZpbGVzIGZvciB0aGUgY2Vuc3VzIGJsb2NrIGdyb3Vwc10oaHR0cHM6Ly93d3cuY2Vuc3VzLmdvdi9nZW8vbWFwcy1kYXRhL2RhdGEvY2JmL2NiZl9ibGtncnAuaHRtbCkgZnJvbSB0aGUgVS5TLiBDZW5zdXMgQnVyZWF1IHdpdGggdGhlIGVzdGltYXRlcyBmcm9tIG91ciBtb2RlbC4NCg0KIyBBcHBlbmRpeDogRml0dGluZyB0aGUgbW9kZWwgaW4gKipyZXRoaW5raW5nKioNCg0KV2UgY291bGQgYWxzbyBpbXBsZW1lbnQgYSB2ZXJzaW9uIG9mIHRoZSBtb2RlbCB3aGljaCByZXRhaW5zIHRoZSB1bmtub3duIG1lYW5zICRcbXVfaSQsIGdpdmluZyB1cyBlc3RpbWF0ZXMgZm9yIHRoZSB0cnVlIHBvcHVsYXRpb25zIG9mIGVhY2ggYmxvY2sgZ3JvdXAgd2hpY2ggYXJlIGluZm9ybWVkIGJ5IHRoZSBsaW5lYXIgcmVncmVzc2lvbiBvbiAkXHRleHRybXt5ZWFyfSQuIFRoaXMgaXMgcmVsYXRpdmVseSBzdHJhaWdodGZvcndhcmQgdG8gaW1wbGVtZW50IHVzaW5nIFt0aGUgKipyZXRoaW5raW5nKiogcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL3JtY2VscmVhdGgvcmV0aGlua2luZykuW14zXQ0KDQpbXjNdOiBTZWUgc2VjdGlvbiAxNC4xIGluICpTdGF0aXN0aWNhbCBSZXRoaW5raW5nKi4NCg0KYGBge3J9DQpsaWJyYXJ5KHJldGhpbmtpbmcpDQoNCiMgcmVwbGFjZSBzdGFuZGFyZCBlcnJvcnMgb2YgMCB3aXRoIDAuMDENCnBvcCRzdGRfZXJyX2J1bXAgPC0gc2FwcGx5KHBvcCRzdGRfZXJyLCBmdW5jdGlvbih4KSBtYXgoMC4wMSwgeCkpDQoNCmZpdDIgPC0gbWFwMnN0YW4oDQogICAgYWxpc3QoDQogICAgICAgIHBvcF9jX2VzdCB+IGRub3JtKG11LCBzaWdtYSksDQogICAgICAgIG11IDwtIGEgKyBhX2JnW2JnX2lkXSArIChiICsgYl9iZ1tiZ19pZF0pKnllYXJfcywNCiAgICAgICAgcG9wX2MgfiBkbm9ybShwb3BfY19lc3QsIHN0ZF9lcnJfYnVtcCksDQogICAgICAgIGEgfiBkbm9ybSgtNSwgNDApLA0KICAgICAgICBiIH4gZG5vcm0oOCwgMTApLA0KICAgICAgICBzaWdtYSB+IGRnYW1tYSg0LCAwLjUpLA0KICAgICAgICBjKGFfYmcsIGJfYmcpW2JnX2lkXSB+IGRtdm5vcm1OQyhzaWdtYV9iZywgUmhvKSwNCiAgICAgICAgc2lnbWFfYmcgfiBkZXhwKDAuMDAxMiksDQogICAgICAgIFJobyB+IGRsa2pjb3JyKDEpDQogICAgKSwNCiAgICBkYXRhID0gcG9wLA0KICAgIGl0ZXIgPSA0ZTMsDQogICAgc3RhcnQgPSBsaXN0KHBvcF9jX2VzdCA9IHBvcCRwb3BfYyksDQogICAgY29udHJvbCA9IGxpc3QoYWRhcHRfZGVsdGEgPSAwLjk1LCBtYXhfdHJlZWRlcHRoID0gMTUpDQopDQpgYGANCg0KVGhlIHNsb3BlIGVzdGltYXRlcyBmcm9tIHRoaXMgYXJlIGVzc2VudGlhbGx5IHRoZSBzYW1lIGFzIHRob3NlIGZyb20gdGhlIGZpcnN0IG1vZGVsLiBSdW5uaW5nIHRoaXMgc21hbGwgbnVtYmVyIG9mIGl0ZXJhdGlvbnMgdG9vayBhYm91dCA5MCBtaW51dGVzIHRvIGZpdCBvbiBteSBsYXB0b3AuIEdldHRpbmcgZW5vdWdoIGl0ZXJhdGlvbnMgZm9yIGEgY2xlYXIgcGljdHVyZSBvZiBgc2lnbWFgIHdvdWxkIHByb2JhYmx5IHRha2UganVzdCBhcyBsb25nIGFzLCBpZiBub3QgbG9uZ2VyIHRoYW4sIGl0IHRvb2sgZm9yIHRoZSBmaXJzdCBtb2RlbC4NCg0KKioqDQoNCltBbnRvbmlvIFIuIFZhcmdhc10oaHR0cHM6Ly9tYXRoc3RhdC5kYWwuY2EvfmFudG9uaW92LykNCg0KMjIgRGVjIDIwMTgNCg0KYGBge2pzLCBlY2hvPUZBTFNFfQ0KLy8gVGhpcyBpcyBqYXZhc2NyaXB0IHRvIG1ha2UgdGhlIFRPQyBsaW5rcyBnbyBiYWNrIHRvIHRoZSBUT0Mgd2hlbiBjbGlja2VkLiANCiQoJ2gxLGgyLGgzJykuY2xpY2soZnVuY3Rpb24oKXsNCndpbmRvdy5zY3JvbGxUbygwLCAwKTsNCndpbmRvdy5sb2NhdGlvbi5oYXNoPSIiOw0KfSk7DQpgYGANCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=