Introduction
Welcome to your crash course in spatial statistics for public health!
We’re going to cover a lot today in terms of analysis of spatial data.
We’ll explore mapping, clustering, and how relationships between
variables vary across geography. Buckle up!
The objectives of this guide are to teach you to:
- Use multiple methods to identify spatial autocorrelation
- Account for spatial autcorrelation in regression using spatial lag
models
- Conduct geographically weighted regression to model how associations
vary across space
Load packages
As always, we need to load our packages. We have a couple of new
friends joining us this week.
spdep: This package handles spatial
dependence — creating neighborhood structures and calculating statistics
like Moran’s I and Local Indicators of Spatial Association (LISA). Think
of it as your go-to for spatial autocorrelation.
spatialreg: This package builds on
spdep and provides maximum likelihood estimation for
spatial regression models like spatial lag and spatial error models.
It’s your go-to tool for formal spatial regressions in R.
GWmodel: A robust package for running
Geographically Weighted Regression (GWR), including model diagnostics,
bandwidth selection, and mapping local coefficients. It’s the main
engine behind spatially varying relationships.
spgwr: A lighter package also used for
GWR. In this tutorial, we use it mainly for its convenient
gwr.sel() function to find the best adaptive bandwidth via
cross-validation.
Together, these packages help us detect patterns, clusters, and
relationships that change across space — essential for spatial health
analysis.
Data Collection and Preparation
First, we need data. We’ll grab county-level diabetes prevalence for
2021 from the CDC and merge it with county geometries and median
household income from the American Community Survey 2021. For the
diabetes data, I downloaded this from the CDC
US Diabetes Surveillance System. It comes in a little ugly, so we
will have to clean up the column names, reformat the FIPS codes, and
filter to just California counties.
# Load and clean CDC diabetes CSV
download.file("https://raw.githubusercontent.com/pjames-ucdavis/SPH215/refs/heads/main/DiabetesAtlas_CountyData.csv", destfile = "DiabetesAtlas_CountyData.csv", mode = "wb")
diab_raw <- read_csv("DiabetesAtlas_CountyData.csv", skip = 2) |>
slice(1:(n() - 1)) # remove last row
# Clean column names
names(diab_raw) <- str_trim(names(diab_raw))
# Format FIPS codes with leading zeros
diab_raw <- diab_raw |>
mutate(CountyFIPS = str_pad(as.character(as.integer(CountyFIPS)), 5, pad = "0"))
# Filter for California counties only
ca_diabetes <- diab_raw |>
filter(str_starts(CountyFIPS, "06")) |>
mutate(Percentage = as.numeric(Percentage)) |>
select(FIPS = CountyFIPS, County, Diabetes = Percentage)
Now lets get our good ole median income data from the US Census.
Because it’s 2021 Diabetes data, we will pull the 2017-2021 five year
estimates from the ACS.
# Download median income and geometries for California counties
ca_geo <- get_acs(
geography = "county",
variables = c(income = "B19013_001"), # Median household income
state = "CA",
geometry = TRUE,
year = 2021
) %>%
rename(income = estimate) %>%
select(GEOID, NAME, income, geometry)
Now let’s join our diabetes dataset with our income dataset at the
county level. Then we will take a quick look at our data.
# Merge diabetes and ACS income data
ca_diab_data <- ca_geo |>
left_join(ca_diabetes, by = c("GEOID" = "FIPS"))
glimpse(ca_diab_data)
## Rows: 58
## Columns: 6
## $ GEOID <chr> "06059", "06111", "06063", "06015", "06023", "06043", "06037"…
## $ NAME <chr> "Orange County, California", "Ventura County, California", "P…
## $ income <dbl> 100485, 94150, 57885, 53280, 53350, 53304, 76367, 76066, 6700…
## $ County <chr> "Orange County", "Ventura County", "Plumas County", "Del Nort…
## $ Diabetes <dbl> 8.0, 7.8, 7.5, 8.1, 9.0, 7.3, 9.9, 9.8, 8.4, 7.9, 7.8, 8.5, 9…
## $ geometry <MULTIPOLYGON [°]> MULTIPOLYGON (((-118.1144 3..., MULTIPOLYGON (((…
summary(ca_diab_data$Diabetes)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 5.800 7.325 7.900 8.059 8.600 11.200
summary(ca_diab_data$income)
## Min. 1st Qu. Median Mean 3rd Qu. Max.
## 42206 58833 70037 75898 89429 140258
🗺️ Exploratory Mapping
Let’s make a map and see what diabetes prevalence looks like across
counties in California.
tmap_mode("view")
tm_shape(ca_diab_data) +
tm_polygons(fill = "Diabetes",
fill.scale = tm_scale_intervals(style = "quantile",
values = "brewer.reds"),
fill.legend = tm_legend(title = "% Diagnosed Diabetes"))
Methods to Identify Spatial Autocorrelation
Now let’s ask: are diabetes rates clustered in space, or scattered
randomly like confetti in a strong breeze? This is where spatial
autocorrelation steps in. We’ll start with Moran’s I — the granddaddy of
spatial pattern detection.
Global Moran’s I
Moran’s I is a measure of overall spatial autocorrelation — that is,
how similar or dissimilar values (like diabetes prevalence) are in
nearby areas (i.e., in nearby counties).
- Values close to +1 mean strong positive spatial
autocorrelation — high values cluster with other high values, and low
with low (think: like attracts like).
- Values near 0 mean no spatial pattern — just
noise.
- Values near -1 mean high and low values are
actively repelling each other (rare in public health).
Let’s calculate Moran’s I and find out if diabetes prevalence in
California follows any spatial logic. First, we use
poly2nb() in the spdep package to figure
out which polygons are neighbors–that is which counties are adjacent to
each other. Next, we use nb2listw() from
spdep to create a spatial weights object. We are
basically giving weights to each neighbor. Then we run the Moran’s I
test using moran.test() and the weights we just
created.
nb <- poly2nb(ca_diab_data)
lw <- nb2listw(nb, style = "W")
moran.test(ca_diab_data$Diabetes, lw)
##
## Moran I test under randomisation
##
## data: ca_diab_data$Diabetes
## weights: lw
##
## Moran I statistic standard deviate = 3.7581, p-value = 8.559e-05
## alternative hypothesis: greater
## sample estimates:
## Moran I statistic Expectation Variance
## 0.296544968 -0.017543860 0.006984903
Interpreting Moran’s I
- A Moran’s I value of ~0.3 means moderate positive
spatial autocorrelation — counties with similar diabetes rates are near
each other.
- A p-value < 0.001 tells us this clustering is
statistically significant — not random.
👉 Take home: Diabetes rates aren’t randomly
distributed across California. There are clusters of high and low
values, and we’ve got statistical proof! This result tells us that there
is statistically significant spatial clustering of diabetes prevalence
across California counties. Counties with high diabetes rates tend to be
near other high-rate counties, and the same is true for low-rate
counties. The Moran’s I value of ~0.3 indicates moderate positive
spatial autocorrelation, and the tiny p-value confirms that this pattern
is not due to chance.
Local Moran’s I (LISA)
While Moran’s I tells us whether there’s spatial autocorrelation in
general, LISA zooms in and tells us where it’s
happening.
LISA stands for Local Indicators of Spatial Association, and it helps
us detect:
- High-High clusters: High diabetes rates surrounded
by other high-rate counties (hot clusters).
- Low-Low clusters: Healthy zones with low diabetes
prevalence.
- High-Low or Low-High outliers: Counties that buck
the trend — like a donut hole in a cake.
Mapping LISA helps target interventions more precisely, and explain
those spatial oddballs.
To run LISA, we use the weights we just created, but now use the
function localmoran(). We will then pull out Ii,
which is the local Moran’s I statistic for each county — a measure of
local spatial autocorrelation. A high positive value indicates strong
clustering with neighbors (e.g., a high-rate county surrounded by other
high-rate counties). We also pull out Pval, which gives the
p-value for that local statistic, testing whether the observed local
clustering is statistically significant. Small p-values suggest that the
clustering is unlikely due to random chance.
local_moran <- localmoran(ca_diab_data$Diabetes, lw)
ca_diab_data$Ii <- local_moran[, "Ii"]
ca_diab_data$Pval <- local_moran[, "Pr(z != E(Ii))"]
# Map LISA
tm_shape(ca_diab_data) +
tm_polygons(fill = "Ii",
fill.scale = tm_scale_intervals(style = "quantile",
values = "brewer.pu_or"),
fill.legend = tm_legend(title = "Local Moran's I")) +
tm_title_out(text = "Local Spatial Clustering of Diabetes")
Interpreting the LISA Map
This map shows where spatial clustering of diabetes is strongest —
not just overall, but locally. Here’s how to read
it:
- Dark purple counties have strong local clustering —
likely high-high or low-low patterns. These are areas where diabetes
rates mirror their neighbors and form significant clusters.
- Orange or tan counties may be spatial
outliers — places where diabetes rates differ from
nearby counties. For example, a healthy county surrounded by high-rate
neighbors.
- White areas or muted tones indicate weak or
non-significant clustering.
👉 Take home: Counties with strong clustering (dark
purple) may benefit from regional strategies, while outliers may need
more localized investigation. This helps tailor public health
interventions to spatial realities.
🔥 Hot Spot Analysis (Gi*)
Now we spice things up with the Getis-Ord Gi* statistic — pronounced
“gee-eye-star.” This method identifies statistically significant
hot and cold spots in the data.
- A hot spot is a county with a high value (e.g.,
diabetes rate) surrounded by other high-value neighbors.
- A cold spot is the opposite: a low-value county in
a sea of low values.
Gi* doesn’t just look at one value — it considers the neighborhood
around each observation too. This is powerful for public health mapping,
especially when prioritizing interventions or allocating resources.
Hot and cold spots are areas with
significantly higher or lower rates than expected.
We run Gi* by using the function localG() from the
spdep package and using the same weights as before. We
can then map the gi_star values by county.
gi_star <- localG(ca_diab_data$Diabetes, lw)
ca_diab_data$Gi_star <- as.numeric(gi_star)
tm_shape(ca_diab_data) +
tm_polygons(fill = "Gi_star",
fill.scale = tm_scale_intervals(style = "pretty",
values = "-brewer.rd_bu"),
fill.legend = tm_legend(title = "Gi* Hot Spots"))
Interpreting the Gi* Map
This map uses the Getis-Ord Gi* statistic to highlight
statistically significant spatial clusters of diabetes
— also known as hot and cold spots:
- Dark red counties (Gi* > 2) are hot
spots: areas with high diabetes prevalence that are also
surrounded by other high-prevalence counties. These are prime targets
for regional public health efforts.
- Lighter shades (e.g., peach or sky blue) represent
less intense clustering — not as statistically extreme.
- Dark blue counties (Gi* < -2) are cold
spots: clusters of lower-than-average diabetes rates. Great for
understanding what’s going right.
👉 Take home: The red-hot lower half of the state
(e.g., Inyo, Orange) may benefit from coordinated, regional
interventions, while blue-cold northern counties may offer models of
prevention or service delivery worth emulating.
Spatial Lag Model
Now that we’ve explored autocorrelation and hot spots, let’s try a
classic spatial regression: the Spatial Lag Model. This
model is useful when the outcome in one location might be influenced by
nearby locations — like diabetes in one county being affected by its
neighbors, which we have seen above!
Based on the exploratory mapping, Moran scatterplot, and the global
Moran’s I, there appears to be spatial autocorrelation in diabetes. This
means that if there is a spatial lag process going on and we fit a
normal regression model our coefficients will be biased and inefficient.
That is, the coefficient sizes and signs are not close to their true
value and its standard errors are underestimated. This means trouble.
Big trouble. Real big trouble.
In a spatial lag model, we include a spatially lagged
dependent variable as a predictor: - It captures the
“spillover” effect of nearby values. - It helps address spatial
dependence that ordinary regression would miss.
We’ll use the lagsarlm() function from the
spatialreg package and will use the same spatial weights as
before.
# Define neighbors and spatial weights
nb <- poly2nb(ca_diab_data)
lw <- nb2listw(nb, style = "W")
# Run spatial lag model
lag_model <- lagsarlm(Diabetes ~ income, data = ca_diab_data, listw = lw)
summary(lag_model)
##
## Call:lagsarlm(formula = Diabetes ~ income, data = ca_diab_data, listw = lw)
##
## Residuals:
## Min 1Q Median 3Q Max
## -1.75134 -0.66941 -0.17221 0.51176 2.63300
##
## Type: lag
## Coefficients: (asymptotic standard errors)
## Estimate Std. Error z value Pr(>|z|)
## (Intercept) 4.7377e+00 1.3120e+00 3.6112 0.0003048
## income -8.0421e-06 5.8702e-06 -1.3700 0.1706906
##
## Rho: 0.4808, LR test value: 9.6355, p-value: 0.0019085
## Asymptotic standard error: 0.14347
## z-value: 3.3512, p-value: 0.00080451
## Wald statistic: 11.231, p-value: 0.00080451
##
## Log likelihood: -84.05424 for lag model
## ML residual variance (sigma squared): 1.0024, (sigma: 1.0012)
## Number of observations: 58
## Number of parameters estimated: 4
## AIC: 176.11, (AIC for lm: 183.74)
## LM test for residual autocorrelation
## test value: 0.034085, p-value: 0.85353
Interpreting the Spatial Lag Model
Let’s break down the results of our spatial lag model:
- The spatial lag coefficient (rho) is about
0.48 and statistically significant (p <
0.002). That means diabetes rates in a county are strongly
influenced by neighboring counties — there’s real spatial
autocorrelation.
- The income coefficient is
negative, suggesting that higher income is associated
with lower diabetes, but it’s not statistically
significant (p = 0.171). So, in this model, income
alone doesn’t explain much variation in diabetes once spatial effects
are accounted for.
- The AIC (Akaike Information Criterion) is lower for
the spatial lag model than for the regular OLS model (176 vs. 184),
suggesting that the spatial lag model is a better fit—this means we
really should be accounting for spatial autocorrelation!
- The residual autocorrelation test is not
significant (p = 0.85), which means the model has successfully
addressed spatial dependence in the residuals.
👉 Take home: There is strong spatial dependence in
diabetes rates, and including spatial effects accounts for this spatial
autocorrelation. However, income may not be a key driver of diabetes
once those spatial influences are considered.
📉 Geographically Weighted Regression (GWR)
GWR — Geographically Weighted Regression — is like regular regression
but with a twist: the coefficients can change depending on
where you are.
Traditional regression assumes one-size-fits-all relationships. But
in public health, the impact of income on diabetes may differ from San
Francisco to Fresno.
GWR fits a separate regression model–no more assuming one
relationship is consistent across all areas in your analysis!
We will run GWR first by setting up our data to have the geometric
centroid of each county using st_centroid() from the
sf package. Then, we will use the
gwr.sel() function from spgwr and will use
the geometric centroid of our counties to find the optimal bandwidth for
our GWR model. We will then model the GWR using the gwr()
function from the GWmodel package. We will use the
bandwidths and centroids that we set up in the previous steps.
coords <- st_coordinates(st_centroid(ca_diab_data))
ca_diab_data$X <- coords[, 1]
ca_diab_data$Y <- coords[, 2]
bw <- gwr.sel(Diabetes ~ income, data = ca_diab_data, coords = coords, adapt = TRUE)
## Adaptive q: 0.381966 CV score: 64.59459
## Adaptive q: 0.618034 CV score: 67.46468
## Adaptive q: 0.236068 CV score: 60.33242
## Adaptive q: 0.145898 CV score: 54.84612
## Adaptive q: 0.09016994 CV score: 51.36862
## Adaptive q: 0.05572809 CV score: 52.55573
## Adaptive q: 0.0889909 CV score: 51.25455
## Adaptive q: 0.07731792 CV score: 51.04182
## Adaptive q: 0.06907134 CV score: 51.67776
## Adaptive q: 0.08166285 CV score: 50.95911
## Adaptive q: 0.08136232 CV score: 50.96031
## Adaptive q: 0.08205407 CV score: 50.95845
## Adaptive q: 0.08210682 CV score: 50.95844
## Adaptive q: 0.08214751 CV score: 50.95844
## Adaptive q: 0.08210682 CV score: 50.95844
# Run GWR using adaptive bandwidth
model <- gwr(Diabetes ~ income, data = ca_diab_data, coords = coords,
adapt = bw, hatmatrix = TRUE)
ca_diab_data$gwr_income <- model$SDF$income # Store model coefficients
Interpreting the GWR results
The output shows the model testing different ‘adaptive q’ values,
which represent the proportion of neighboring counties used in each
local model. It chooses the q value with the lowest cross-validation
(CV) score — best predictive fit. For example: q = 0.082 means ~8% of
counties (≈5 counties) are used in each local regression. Lower CV =
better model. It’s finding the ‘just right’ neighborhood size for each
regression.
🗾 Map Local GWR Coefficients
Let’s map those locally varying regression coefficients and see where
income matters more (or less) for diabetes.
tm_shape(ca_diab_data) +
tm_polygons(fill = "gwr_income",
tm_scale_intervals(style = "quantile",
values = "brewer.rd_yl_gn"),
fill.legend = tm_legend(title = "GWR: Income-Diabetes Association"))
🧩 Interpreting the GWR Map
This map shows how the relationship between income and diabetes
varies across California — it’s a spatial view of the
regression coefficient between income and diabetes.
All the values are negative, meaning higher
income is associated with lower diabetes prevalence —
but the strength of this relationship varies across
space.
Dark red counties (most negative values): These
are the places where income has the strongest inverse
relationship with diabetes. In other words, improving economic
conditions here could have a big public health
payoff.
Lighter yellow to green counties: These areas
still show a negative relationship, but it’s weaker.
That might mean other social or environmental factors are playing a
bigger role than income in these counties.
Dark green areas: The weakest negative
associations — here, income might matter less for diabetes prevention,
or other factors may dominate.
👉 Take home: This map helps public health
professionals tailor interventions. In counties where income is a strong
driver of diabetes, economic support programs might reduce prevalence.
In others, different strategies may be needed.
Take home
- Moran’s I: Measures overall spatial autocorrelation
(is there a pattern?).
- LISA/Gi: Reveal local clusters and hot/cold spots
(where’s the action?).
- Spatial Lag Model: Quantifies spatial
autocorrelation and adjusts for spatial autocorrelation in regression
models.
- GWR: Shows where exposure-outcome relationships are
strong, weak, or flipping direction.
Summary
Boom! You just crunched real public health data using cutting-edge
spatial tools to look at spatial clustering / autocorrelation. And we
even learned some regression approaches to account for spatial
autocorrelation. Well done! We’ve just scratched the surface on these
spatial statistics approaches, but we’ve come so far from Week 1! I’m
proud of you!
LS0tDQp0aXRsZTogJ0xhYiA4OiBTcGF0aWFsIFN0YXRpc3RpY3MgYW5kIEdlb2dyYXBoaWNhbGx5IFdlaWdodGVkIFJlZ3Jlc3Npb24gaW4gUHVibGljDQogIEhlYWx0aCcNCi0tLQ0KDQpcDQoNCiMgSW50cm9kdWN0aW9uDQoNCldlbGNvbWUgdG8geW91ciBjcmFzaCBjb3Vyc2UgaW4gc3BhdGlhbCBzdGF0aXN0aWNzIGZvciBwdWJsaWMgaGVhbHRoISBXZSdyZSBnb2luZyB0byBjb3ZlciBhIGxvdCB0b2RheSBpbiB0ZXJtcyBvZiBhbmFseXNpcyBvZiBzcGF0aWFsIGRhdGEuIFdlJ2xsIGV4cGxvcmUgbWFwcGluZywgY2x1c3RlcmluZywgYW5kIGhvdyByZWxhdGlvbnNoaXBzIGJldHdlZW4gdmFyaWFibGVzIHZhcnkgYWNyb3NzIGdlb2dyYXBoeS4gQnVja2xlIHVwIQ0KDQpUaGUgb2JqZWN0aXZlcyBvZiB0aGlzIGd1aWRlIGFyZSB0byB0ZWFjaCB5b3UgdG86DQoNCjEuIFVzZSBtdWx0aXBsZSBtZXRob2RzIHRvIGlkZW50aWZ5IHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uDQoyLiBBY2NvdW50IGZvciBzcGF0aWFsIGF1dGNvcnJlbGF0aW9uIGluIHJlZ3Jlc3Npb24gdXNpbmcgc3BhdGlhbCBsYWcgbW9kZWxzDQozLiBDb25kdWN0IGdlb2dyYXBoaWNhbGx5IHdlaWdodGVkIHJlZ3Jlc3Npb24gdG8gbW9kZWwgaG93IGFzc29jaWF0aW9ucyB2YXJ5IGFjcm9zcyBzcGFjZQ0KDQpcDQoNCiMgTG9hZCBwYWNrYWdlcw0KDQpBcyBhbHdheXMsIHdlIG5lZWQgdG8gbG9hZCBvdXIgcGFja2FnZXMuIFdlIGhhdmUgYSBjb3VwbGUgb2YgbmV3IGZyaWVuZHMgam9pbmluZyB1cyB0aGlzIHdlZWsuDQoNCi0gKipgc3BkZXBgKio6IFRoaXMgcGFja2FnZSBoYW5kbGVzIHNwYXRpYWwgZGVwZW5kZW5jZSDigJQgY3JlYXRpbmcgbmVpZ2hib3Job29kIHN0cnVjdHVyZXMgYW5kIGNhbGN1bGF0aW5nIHN0YXRpc3RpY3MgbGlrZSBNb3JhbidzIEkgYW5kIExvY2FsIEluZGljYXRvcnMgb2YgU3BhdGlhbCBBc3NvY2lhdGlvbiAoTElTQSkuIFRoaW5rIG9mIGl0IGFzIHlvdXIgZ28tdG8gZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uLg0KLSAqKmBzcGF0aWFscmVnYCoqOiBUaGlzIHBhY2thZ2UgYnVpbGRzIG9uICoqc3BkZXAqKiBhbmQgcHJvdmlkZXMgbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRpb24gZm9yIHNwYXRpYWwgcmVncmVzc2lvbiBtb2RlbHMgbGlrZSBzcGF0aWFsIGxhZyBhbmQgc3BhdGlhbCBlcnJvciBtb2RlbHMuIEl0J3MgeW91ciBnby10byB0b29sIGZvciBmb3JtYWwgc3BhdGlhbCByZWdyZXNzaW9ucyBpbiBSLg0KLSAqKmBHV21vZGVsYCoqOiBBIHJvYnVzdCBwYWNrYWdlIGZvciBydW5uaW5nIEdlb2dyYXBoaWNhbGx5IFdlaWdodGVkIFJlZ3Jlc3Npb24gKEdXUiksIGluY2x1ZGluZyBtb2RlbCBkaWFnbm9zdGljcywgYmFuZHdpZHRoIHNlbGVjdGlvbiwgYW5kIG1hcHBpbmcgbG9jYWwgY29lZmZpY2llbnRzLiBJdOKAmXMgdGhlIG1haW4gZW5naW5lIGJlaGluZCBzcGF0aWFsbHkgdmFyeWluZyByZWxhdGlvbnNoaXBzLg0KLSAqKmBzcGd3cmAqKjogQSBsaWdodGVyIHBhY2thZ2UgYWxzbyB1c2VkIGZvciBHV1IuIEluIHRoaXMgdHV0b3JpYWwsIHdlIHVzZSBpdCBtYWlubHkgZm9yIGl0cyBjb252ZW5pZW50IGBnd3Iuc2VsKClgIGZ1bmN0aW9uIHRvIGZpbmQgdGhlIGJlc3QgYWRhcHRpdmUgYmFuZHdpZHRoIHZpYSBjcm9zcy12YWxpZGF0aW9uLg0KDQpUb2dldGhlciwgdGhlc2UgcGFja2FnZXMgaGVscCB1cyBkZXRlY3QgcGF0dGVybnMsIGNsdXN0ZXJzLCBhbmQgcmVsYXRpb25zaGlwcyB0aGF0IGNoYW5nZSBhY3Jvc3Mgc3BhY2Ug4oCUIGVzc2VudGlhbCBmb3Igc3BhdGlhbCBoZWFsdGggYW5hbHlzaXMuDQoNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0UpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkodGlkeWNlbnN1cykNCmxpYnJhcnkoc2YpDQpsaWJyYXJ5KHRtYXApDQpsaWJyYXJ5KHNwZGVwKQ0KbGlicmFyeShHV21vZGVsKQ0KbGlicmFyeShzcGF0aWFscmVnKQ0KbGlicmFyeShzcGd3cikNCmBgYA0KDQoNClwNCg0KIyBEYXRhIENvbGxlY3Rpb24gYW5kIFByZXBhcmF0aW9uDQoNCkZpcnN0LCB3ZSBuZWVkIGRhdGEuIFdlJ2xsIGdyYWIgY291bnR5LWxldmVsIGRpYWJldGVzIHByZXZhbGVuY2UgZm9yIDIwMjEgZnJvbSB0aGUgQ0RDIGFuZCBtZXJnZSBpdCB3aXRoIGNvdW50eSBnZW9tZXRyaWVzIGFuZCBtZWRpYW4gaG91c2Vob2xkIGluY29tZSBmcm9tIHRoZSBBbWVyaWNhbiBDb21tdW5pdHkgU3VydmV5IDIwMjEuIEZvciB0aGUgZGlhYmV0ZXMgZGF0YSwgSSBkb3dubG9hZGVkIHRoaXMgZnJvbSB0aGUgW0NEQyBVUyBEaWFiZXRlcyBTdXJ2ZWlsbGFuY2UgU3lzdGVtXShodHRwczovL2dpcy5jZGMuZ292L2dyYXNwL2RpYWJldGVzL2RpYWJldGVzYXRsYXMtc3VydmVpbGxhbmNlLmh0bWwjKS4gSXQgY29tZXMgaW4gYSBsaXR0bGUgdWdseSwgc28gd2Ugd2lsbCBoYXZlIHRvIGNsZWFuIHVwIHRoZSBjb2x1bW4gbmFtZXMsIHJlZm9ybWF0IHRoZSBGSVBTIGNvZGVzLCBhbmQgZmlsdGVyIHRvIGp1c3QgQ2FsaWZvcm5pYSBjb3VudGllcy4NCg0KYGBge3IgbG9hZC1kYXRhfQ0KIyBMb2FkIGFuZCBjbGVhbiBDREMgZGlhYmV0ZXMgQ1NWDQpkb3dubG9hZC5maWxlKCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vcGphbWVzLXVjZGF2aXMvU1BIMjE1L3JlZnMvaGVhZHMvbWFpbi9EaWFiZXRlc0F0bGFzX0NvdW50eURhdGEuY3N2IiwgZGVzdGZpbGUgPSAiRGlhYmV0ZXNBdGxhc19Db3VudHlEYXRhLmNzdiIsIG1vZGUgPSAid2IiKQ0KZGlhYl9yYXcgPC0gcmVhZF9jc3YoIkRpYWJldGVzQXRsYXNfQ291bnR5RGF0YS5jc3YiLCBza2lwID0gMikgfD4gDQogIHNsaWNlKDE6KG4oKSAtIDEpKSAgIyByZW1vdmUgbGFzdCByb3cNCg0KIyBDbGVhbiBjb2x1bW4gbmFtZXMNCm5hbWVzKGRpYWJfcmF3KSA8LSBzdHJfdHJpbShuYW1lcyhkaWFiX3JhdykpDQoNCiMgRm9ybWF0IEZJUFMgY29kZXMgd2l0aCBsZWFkaW5nIHplcm9zDQpkaWFiX3JhdyA8LSBkaWFiX3JhdyB8PiANCiAgbXV0YXRlKENvdW50eUZJUFMgPSBzdHJfcGFkKGFzLmNoYXJhY3Rlcihhcy5pbnRlZ2VyKENvdW50eUZJUFMpKSwgNSwgcGFkID0gIjAiKSkNCg0KIyBGaWx0ZXIgZm9yIENhbGlmb3JuaWEgY291bnRpZXMgb25seQ0KY2FfZGlhYmV0ZXMgPC0gZGlhYl9yYXcgfD4gDQogIGZpbHRlcihzdHJfc3RhcnRzKENvdW50eUZJUFMsICIwNiIpKSB8PiANCiAgbXV0YXRlKFBlcmNlbnRhZ2UgPSBhcy5udW1lcmljKFBlcmNlbnRhZ2UpKSB8PiANCiAgc2VsZWN0KEZJUFMgPSBDb3VudHlGSVBTLCBDb3VudHksIERpYWJldGVzID0gUGVyY2VudGFnZSkNCmBgYA0KDQpcDQoNCk5vdyBsZXRzIGdldCBvdXIgZ29vZCBvbGUgbWVkaWFuIGluY29tZSBkYXRhIGZyb20gdGhlIFVTIENlbnN1cy4gQmVjYXVzZSBpdCdzIDIwMjEgRGlhYmV0ZXMgZGF0YSwgd2Ugd2lsbCBwdWxsIHRoZSAyMDE3LTIwMjEgZml2ZSB5ZWFyIGVzdGltYXRlcyBmcm9tIHRoZSBBQ1MuDQpgYGB7ciBnZXQtYWNzLCByZXN1bHRzPUZBTFNFfQ0KIyBEb3dubG9hZCBtZWRpYW4gaW5jb21lIGFuZCBnZW9tZXRyaWVzIGZvciBDYWxpZm9ybmlhIGNvdW50aWVzDQpjYV9nZW8gPC0gZ2V0X2FjcygNCiAgZ2VvZ3JhcGh5ID0gImNvdW50eSIsDQogIHZhcmlhYmxlcyA9IGMoaW5jb21lID0gIkIxOTAxM18wMDEiKSwgICMgTWVkaWFuIGhvdXNlaG9sZCBpbmNvbWUNCiAgc3RhdGUgPSAiQ0EiLA0KICBnZW9tZXRyeSA9IFRSVUUsDQogIHllYXIgPSAyMDIxDQopICU+JQ0KICByZW5hbWUoaW5jb21lID0gZXN0aW1hdGUpICU+JQ0KICBzZWxlY3QoR0VPSUQsIE5BTUUsIGluY29tZSwgZ2VvbWV0cnkpDQpgYGANCg0KXA0KDQpOb3cgbGV0J3Mgam9pbiBvdXIgZGlhYmV0ZXMgZGF0YXNldCB3aXRoIG91ciBpbmNvbWUgZGF0YXNldCBhdCB0aGUgY291bnR5IGxldmVsLiBUaGVuIHdlIHdpbGwgdGFrZSBhIHF1aWNrIGxvb2sgYXQgb3VyIGRhdGEuDQpgYGB7cn0NCiMgTWVyZ2UgZGlhYmV0ZXMgYW5kIEFDUyBpbmNvbWUgZGF0YQ0KY2FfZGlhYl9kYXRhIDwtIGNhX2dlbyB8PiANCiAgbGVmdF9qb2luKGNhX2RpYWJldGVzLCBieSA9IGMoIkdFT0lEIiA9ICJGSVBTIikpDQpnbGltcHNlKGNhX2RpYWJfZGF0YSkNCnN1bW1hcnkoY2FfZGlhYl9kYXRhJERpYWJldGVzKQ0Kc3VtbWFyeShjYV9kaWFiX2RhdGEkaW5jb21lKQ0KYGBgDQoNClwNCg0KIyDwn5e677iPIEV4cGxvcmF0b3J5IE1hcHBpbmcNCg0KTGV0J3MgbWFrZSBhIG1hcCBhbmQgc2VlIHdoYXQgZGlhYmV0ZXMgcHJldmFsZW5jZSBsb29rcyBsaWtlIGFjcm9zcyBjb3VudGllcyBpbiBDYWxpZm9ybmlhLiANCg0KYGBge3IgZXhwbG9yYXRvcnktbWFwcGluZ30NCg0KdG1hcF9tb2RlKCJ2aWV3IikNCnRtX3NoYXBlKGNhX2RpYWJfZGF0YSkgKw0KICB0bV9wb2x5Z29ucyhmaWxsID0gIkRpYWJldGVzIiwgDQogICAgICAgICAgICAgIGZpbGwuc2NhbGUgPSB0bV9zY2FsZV9pbnRlcnZhbHMoc3R5bGUgPSAicXVhbnRpbGUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9ICJicmV3ZXIucmVkcyIpLA0KICAgICAgICAgICAgICBmaWxsLmxlZ2VuZCA9IHRtX2xlZ2VuZCh0aXRsZSA9ICIlIERpYWdub3NlZCBEaWFiZXRlcyIpKQ0KYGBgDQoNClwNCg0KIyBNZXRob2RzIHRvIElkZW50aWZ5IFNwYXRpYWwgQXV0b2NvcnJlbGF0aW9uDQoNCk5vdyBsZXTigJlzIGFzazogYXJlIGRpYWJldGVzIHJhdGVzIGNsdXN0ZXJlZCBpbiBzcGFjZSwgb3Igc2NhdHRlcmVkIHJhbmRvbWx5IGxpa2UgY29uZmV0dGkgaW4gYSBzdHJvbmcgYnJlZXplPyBUaGlzIGlzIHdoZXJlIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIHN0ZXBzIGluLiBXZSdsbCBzdGFydCB3aXRoIE1vcmFu4oCZcyBJIOKAlCB0aGUgZ3JhbmRkYWRkeSBvZiBzcGF0aWFsIHBhdHRlcm4gZGV0ZWN0aW9uLg0KDQpcDQoNCiMjIEdsb2JhbCBNb3JhbuKAmXMgSQ0KDQpNb3JhbuKAmXMgSSBpcyBhIG1lYXN1cmUgb2Ygb3ZlcmFsbCBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiDigJQgdGhhdCBpcywgaG93IHNpbWlsYXIgb3IgZGlzc2ltaWxhciB2YWx1ZXMgKGxpa2UgZGlhYmV0ZXMgcHJldmFsZW5jZSkgYXJlIGluIG5lYXJieSBhcmVhcyAoaS5lLiwgaW4gbmVhcmJ5IGNvdW50aWVzKS4gDQoNCi0gVmFsdWVzIGNsb3NlIHRvICoqKzEqKiBtZWFuIHN0cm9uZyBwb3NpdGl2ZSBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiDigJQgaGlnaCB2YWx1ZXMgY2x1c3RlciB3aXRoIG90aGVyIGhpZ2ggdmFsdWVzLCBhbmQgbG93IHdpdGggbG93ICh0aGluazogbGlrZSBhdHRyYWN0cyBsaWtlKS4NCi0gVmFsdWVzIG5lYXIgKiowKiogbWVhbiBubyBzcGF0aWFsIHBhdHRlcm4g4oCUIGp1c3Qgbm9pc2UuDQotIFZhbHVlcyBuZWFyICoqLTEqKiBtZWFuIGhpZ2ggYW5kIGxvdyB2YWx1ZXMgYXJlIGFjdGl2ZWx5IHJlcGVsbGluZyBlYWNoIG90aGVyIChyYXJlIGluIHB1YmxpYyBoZWFsdGgpLg0KDQpMZXQncyBjYWxjdWxhdGUgTW9yYW7igJlzIEkgYW5kIGZpbmQgb3V0IGlmIGRpYWJldGVzIHByZXZhbGVuY2UgaW4gQ2FsaWZvcm5pYSBmb2xsb3dzIGFueSBzcGF0aWFsIGxvZ2ljLiBGaXJzdCwgd2UgdXNlIGBwb2x5Mm5iKClgIGluIHRoZSAqKnNwZGVwKiogcGFja2FnZSB0byBmaWd1cmUgb3V0IHdoaWNoIHBvbHlnb25zIGFyZSBuZWlnaGJvcnMtLXRoYXQgaXMgd2hpY2ggY291bnRpZXMgYXJlIGFkamFjZW50IHRvIGVhY2ggb3RoZXIuIE5leHQsIHdlIHVzZSBgbmIybGlzdHcoKWAgZnJvbSAqKnNwZGVwKiogdG8gY3JlYXRlIGEgc3BhdGlhbCB3ZWlnaHRzIG9iamVjdC4gV2UgYXJlIGJhc2ljYWxseSBnaXZpbmcgd2VpZ2h0cyB0byBlYWNoIG5laWdoYm9yLiBUaGVuIHdlIHJ1biB0aGUgTW9yYW4ncyBJIHRlc3QgdXNpbmcgYG1vcmFuLnRlc3QoKWAgYW5kIHRoZSB3ZWlnaHRzIHdlIGp1c3QgY3JlYXRlZC4gDQoNCmBgYHtyIG1vcmFucy1pfQ0KbmIgPC0gcG9seTJuYihjYV9kaWFiX2RhdGEpIA0KbHcgPC0gbmIybGlzdHcobmIsIHN0eWxlID0gIlciKQ0KbW9yYW4udGVzdChjYV9kaWFiX2RhdGEkRGlhYmV0ZXMsIGx3KQ0KYGBgDQoNCiMjIyBJbnRlcnByZXRpbmcgTW9yYW4ncyBJDQoNCi0gQSAqKk1vcmFu4oCZcyBJIHZhbHVlIG9mIH4wLjMqKiBtZWFucyBtb2RlcmF0ZSBwb3NpdGl2ZSBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiDigJQgY291bnRpZXMgd2l0aCBzaW1pbGFyIGRpYWJldGVzIHJhdGVzIGFyZSBuZWFyIGVhY2ggb3RoZXIuDQotIEEgKipwLXZhbHVlIDwgMC4wMDEqKiB0ZWxscyB1cyB0aGlzIGNsdXN0ZXJpbmcgaXMgKipzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50Kiog4oCUIG5vdCByYW5kb20uDQoNCvCfkYkgKipUYWtlIGhvbWU6KiogRGlhYmV0ZXMgcmF0ZXMgYXJlbuKAmXQgcmFuZG9tbHkgZGlzdHJpYnV0ZWQgYWNyb3NzIENhbGlmb3JuaWEuIFRoZXJlIGFyZSBjbHVzdGVycyBvZiBoaWdoIGFuZCBsb3cgdmFsdWVzLCBhbmQgd2XigJl2ZSBnb3Qgc3RhdGlzdGljYWwgcHJvb2YhIFRoaXMgcmVzdWx0IHRlbGxzIHVzIHRoYXQgdGhlcmUgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudCBzcGF0aWFsIGNsdXN0ZXJpbmcgb2YgZGlhYmV0ZXMgcHJldmFsZW5jZSBhY3Jvc3MgQ2FsaWZvcm5pYSBjb3VudGllcy4gQ291bnRpZXMgd2l0aCBoaWdoIGRpYWJldGVzIHJhdGVzIHRlbmQgdG8gYmUgbmVhciBvdGhlciBoaWdoLXJhdGUgY291bnRpZXMsIGFuZCB0aGUgc2FtZSBpcyB0cnVlIGZvciBsb3ctcmF0ZSBjb3VudGllcy4gVGhlIE1vcmFu4oCZcyBJIHZhbHVlIG9mIH4wLjMgaW5kaWNhdGVzIG1vZGVyYXRlIHBvc2l0aXZlIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uLCBhbmQgdGhlIHRpbnkgcC12YWx1ZSBjb25maXJtcyB0aGF0IHRoaXMgcGF0dGVybiBpcyBub3QgZHVlIHRvIGNoYW5jZS4NCg0KXA0KDQojIyBMb2NhbCBNb3JhbuKAmXMgSSAoTElTQSkNCg0KV2hpbGUgTW9yYW7igJlzIEkgdGVsbHMgdXMgd2hldGhlciB0aGVyZSdzIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIGluIGdlbmVyYWwsIExJU0Egem9vbXMgaW4gYW5kIHRlbGxzIHVzICoqd2hlcmUqKiBpdCdzIGhhcHBlbmluZy4gDQoNCkxJU0Egc3RhbmRzIGZvciBMb2NhbCBJbmRpY2F0b3JzIG9mIFNwYXRpYWwgQXNzb2NpYXRpb24sIGFuZCBpdCBoZWxwcyB1cyBkZXRlY3Q6DQoNCi0gKipIaWdoLUhpZ2ggY2x1c3RlcnMqKjogSGlnaCBkaWFiZXRlcyByYXRlcyBzdXJyb3VuZGVkIGJ5IG90aGVyIGhpZ2gtcmF0ZSBjb3VudGllcyAoaG90IGNsdXN0ZXJzKS4NCi0gKipMb3ctTG93IGNsdXN0ZXJzKio6IEhlYWx0aHkgem9uZXMgd2l0aCBsb3cgZGlhYmV0ZXMgcHJldmFsZW5jZS4NCi0gKipIaWdoLUxvdyBvciBMb3ctSGlnaCBvdXRsaWVycyoqOiBDb3VudGllcyB0aGF0IGJ1Y2sgdGhlIHRyZW5kIOKAlCBsaWtlIGEgZG9udXQgaG9sZSBpbiBhIGNha2UuDQoNCk1hcHBpbmcgTElTQSBoZWxwcyB0YXJnZXQgaW50ZXJ2ZW50aW9ucyBtb3JlIHByZWNpc2VseSwgYW5kIGV4cGxhaW4gdGhvc2Ugc3BhdGlhbCBvZGRiYWxscy4NCg0KVG8gcnVuIExJU0EsIHdlIHVzZSB0aGUgd2VpZ2h0cyB3ZSBqdXN0IGNyZWF0ZWQsIGJ1dCBub3cgdXNlIHRoZSBmdW5jdGlvbiBgbG9jYWxtb3JhbigpYC4gV2Ugd2lsbCB0aGVuIHB1bGwgb3V0ICpJaSosIHdoaWNoIGlzIHRoZSBsb2NhbCBNb3JhbidzIEkgc3RhdGlzdGljIGZvciBlYWNoIGNvdW50eSDigJQgYSBtZWFzdXJlIG9mIGxvY2FsIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uLiBBIGhpZ2ggcG9zaXRpdmUgdmFsdWUgaW5kaWNhdGVzIHN0cm9uZyBjbHVzdGVyaW5nIHdpdGggbmVpZ2hib3JzIChlLmcuLCBhIGhpZ2gtcmF0ZSBjb3VudHkgc3Vycm91bmRlZCBieSBvdGhlciBoaWdoLXJhdGUgY291bnRpZXMpLiBXZSBhbHNvIHB1bGwgb3V0ICpQdmFsKiwgd2hpY2ggZ2l2ZXMgdGhlIHAtdmFsdWUgZm9yIHRoYXQgbG9jYWwgc3RhdGlzdGljLCB0ZXN0aW5nIHdoZXRoZXIgdGhlIG9ic2VydmVkIGxvY2FsIGNsdXN0ZXJpbmcgaXMgc3RhdGlzdGljYWxseSBzaWduaWZpY2FudC4gU21hbGwgcC12YWx1ZXMgc3VnZ2VzdCB0aGF0IHRoZSBjbHVzdGVyaW5nIGlzIHVubGlrZWx5IGR1ZSB0byByYW5kb20gY2hhbmNlLg0KDQpgYGB7ciBsaXNhfQ0KbG9jYWxfbW9yYW4gPC0gbG9jYWxtb3JhbihjYV9kaWFiX2RhdGEkRGlhYmV0ZXMsIGx3KQ0KY2FfZGlhYl9kYXRhJElpIDwtIGxvY2FsX21vcmFuWywgIklpIl0NCmNhX2RpYWJfZGF0YSRQdmFsIDwtIGxvY2FsX21vcmFuWywgIlByKHogIT0gRShJaSkpIl0NCg0KIyBNYXAgTElTQQ0KdG1fc2hhcGUoY2FfZGlhYl9kYXRhKSArDQogIHRtX3BvbHlnb25zKGZpbGwgPSAiSWkiLCANCiAgICAgICAgICAgICAgZmlsbC5zY2FsZSA9IHRtX3NjYWxlX2ludGVydmFscyhzdHlsZSA9ICJxdWFudGlsZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhbHVlcyA9ICJicmV3ZXIucHVfb3IiKSwgDQogICAgICAgICAgICAgIGZpbGwubGVnZW5kID0gdG1fbGVnZW5kKHRpdGxlID0gIkxvY2FsIE1vcmFuJ3MgSSIpKSArDQogIHRtX3RpdGxlX291dCh0ZXh0ID0gIkxvY2FsIFNwYXRpYWwgQ2x1c3RlcmluZyBvZiBEaWFiZXRlcyIpDQpgYGANCg0KXA0KDQojIyMgSW50ZXJwcmV0aW5nIHRoZSBMSVNBIE1hcA0KDQpUaGlzIG1hcCBzaG93cyB3aGVyZSBzcGF0aWFsIGNsdXN0ZXJpbmcgb2YgZGlhYmV0ZXMgaXMgc3Ryb25nZXN0IOKAlCBub3QganVzdCBvdmVyYWxsLCBidXQgKipsb2NhbGx5KiouIEhlcmUncyBob3cgdG8gcmVhZCBpdDoNCg0KLSAqKkRhcmsgcHVycGxlIGNvdW50aWVzKiogaGF2ZSBzdHJvbmcgbG9jYWwgY2x1c3RlcmluZyDigJQgbGlrZWx5IGhpZ2gtaGlnaCBvciBsb3ctbG93IHBhdHRlcm5zLiBUaGVzZSBhcmUgYXJlYXMgd2hlcmUgZGlhYmV0ZXMgcmF0ZXMgbWlycm9yIHRoZWlyIG5laWdoYm9ycyBhbmQgZm9ybSBzaWduaWZpY2FudCBjbHVzdGVycy4NCi0gKipPcmFuZ2Ugb3IgdGFuIGNvdW50aWVzKiogbWF5IGJlIHNwYXRpYWwgKipvdXRsaWVycyoqIOKAlCBwbGFjZXMgd2hlcmUgZGlhYmV0ZXMgcmF0ZXMgZGlmZmVyIGZyb20gbmVhcmJ5IGNvdW50aWVzLiBGb3IgZXhhbXBsZSwgYSBoZWFsdGh5IGNvdW50eSBzdXJyb3VuZGVkIGJ5IGhpZ2gtcmF0ZSBuZWlnaGJvcnMuDQotICoqV2hpdGUgYXJlYXMgb3IgbXV0ZWQgdG9uZXMqKiBpbmRpY2F0ZSB3ZWFrIG9yIG5vbi1zaWduaWZpY2FudCBjbHVzdGVyaW5nLg0KDQrwn5GJICoqVGFrZSBob21lOioqIENvdW50aWVzIHdpdGggc3Ryb25nIGNsdXN0ZXJpbmcgKGRhcmsgcHVycGxlKSBtYXkgYmVuZWZpdCBmcm9tIHJlZ2lvbmFsIHN0cmF0ZWdpZXMsIHdoaWxlIG91dGxpZXJzIG1heSBuZWVkIG1vcmUgbG9jYWxpemVkIGludmVzdGlnYXRpb24uIFRoaXMgaGVscHMgdGFpbG9yIHB1YmxpYyBoZWFsdGggaW50ZXJ2ZW50aW9ucyB0byBzcGF0aWFsIHJlYWxpdGllcy4NCg0KXA0KDQojIyDwn5SlIEhvdCBTcG90IEFuYWx5c2lzIChHaSopDQoNCk5vdyB3ZSBzcGljZSB0aGluZ3MgdXAgd2l0aCB0aGUgR2V0aXMtT3JkIEdpKiBzdGF0aXN0aWMg4oCUIHByb25vdW5jZWQgImdlZS1leWUtc3Rhci4iIFRoaXMgbWV0aG9kIGlkZW50aWZpZXMgKipzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGhvdCBhbmQgY29sZCBzcG90cyoqIGluIHRoZSBkYXRhLg0KDQotIEEgKipob3Qgc3BvdCoqIGlzIGEgY291bnR5IHdpdGggYSBoaWdoIHZhbHVlIChlLmcuLCBkaWFiZXRlcyByYXRlKSBzdXJyb3VuZGVkIGJ5IG90aGVyIGhpZ2gtdmFsdWUgbmVpZ2hib3JzLg0KLSBBICoqY29sZCBzcG90KiogaXMgdGhlIG9wcG9zaXRlOiBhIGxvdy12YWx1ZSBjb3VudHkgaW4gYSBzZWEgb2YgbG93IHZhbHVlcy4NCg0KR2kqIGRvZXNuJ3QganVzdCBsb29rIGF0IG9uZSB2YWx1ZSDigJQgaXQgY29uc2lkZXJzIHRoZSBuZWlnaGJvcmhvb2QgYXJvdW5kIGVhY2ggb2JzZXJ2YXRpb24gdG9vLiBUaGlzIGlzIHBvd2VyZnVsIGZvciBwdWJsaWMgaGVhbHRoIG1hcHBpbmcsIGVzcGVjaWFsbHkgd2hlbiBwcmlvcml0aXppbmcgaW50ZXJ2ZW50aW9ucyBvciBhbGxvY2F0aW5nIHJlc291cmNlcy4gKipIb3QqKiBhbmQgKipjb2xkKiogc3BvdHMgYXJlIGFyZWFzIHdpdGggc2lnbmlmaWNhbnRseSBoaWdoZXIgb3IgbG93ZXIgcmF0ZXMgdGhhbiBleHBlY3RlZC4NCg0KV2UgcnVuIEdpKiBieSB1c2luZyB0aGUgZnVuY3Rpb24gYGxvY2FsRygpYCBmcm9tIHRoZSAqKnNwZGVwKiogcGFja2FnZSBhbmQgdXNpbmcgdGhlIHNhbWUgd2VpZ2h0cyBhcyBiZWZvcmUuIFdlIGNhbiB0aGVuIG1hcCB0aGUgKmdpX3N0YXIqIHZhbHVlcyBieSBjb3VudHkuDQoNCmBgYHtyIGdpLXN0YXJ9DQpnaV9zdGFyIDwtIGxvY2FsRyhjYV9kaWFiX2RhdGEkRGlhYmV0ZXMsIGx3KQ0KY2FfZGlhYl9kYXRhJEdpX3N0YXIgPC0gYXMubnVtZXJpYyhnaV9zdGFyKQ0KDQp0bV9zaGFwZShjYV9kaWFiX2RhdGEpICsNCiAgdG1fcG9seWdvbnMoZmlsbCA9ICJHaV9zdGFyIiwgDQogICAgICAgICAgICAgIGZpbGwuc2NhbGUgPSB0bV9zY2FsZV9pbnRlcnZhbHMoc3R5bGUgPSAicHJldHR5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSAiLWJyZXdlci5yZF9idSIpLA0KICAgICAgICAgICAgICBmaWxsLmxlZ2VuZCA9IHRtX2xlZ2VuZCh0aXRsZSA9ICJHaSogSG90IFNwb3RzIikpDQpgYGANCg0KIyMjIEludGVycHJldGluZyB0aGUgR2kqIE1hcA0KDQpUaGlzIG1hcCB1c2VzIHRoZSBHZXRpcy1PcmQgR2kqIHN0YXRpc3RpYyB0byBoaWdobGlnaHQgKipzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IHNwYXRpYWwgY2x1c3RlcnMqKiBvZiBkaWFiZXRlcyDigJQgYWxzbyBrbm93biBhcyBob3QgYW5kIGNvbGQgc3BvdHM6DQoNCi0gKipEYXJrIHJlZCBjb3VudGllcyoqIChHaSogPiAyKSBhcmUgKipob3Qgc3BvdHMqKjogYXJlYXMgd2l0aCBoaWdoIGRpYWJldGVzIHByZXZhbGVuY2UgdGhhdCBhcmUgYWxzbyBzdXJyb3VuZGVkIGJ5IG90aGVyIGhpZ2gtcHJldmFsZW5jZSBjb3VudGllcy4gVGhlc2UgYXJlIHByaW1lIHRhcmdldHMgZm9yIHJlZ2lvbmFsIHB1YmxpYyBoZWFsdGggZWZmb3J0cy4NCi0gKipMaWdodGVyIHNoYWRlcyoqIChlLmcuLCBwZWFjaCBvciBza3kgYmx1ZSkgcmVwcmVzZW50IGxlc3MgaW50ZW5zZSBjbHVzdGVyaW5nIOKAlCBub3QgYXMgc3RhdGlzdGljYWxseSBleHRyZW1lLg0KLSAqKkRhcmsgYmx1ZSBjb3VudGllcyoqIChHaSogPCAtMikgYXJlICoqY29sZCBzcG90cyoqOiBjbHVzdGVycyBvZiBsb3dlci10aGFuLWF2ZXJhZ2UgZGlhYmV0ZXMgcmF0ZXMuIEdyZWF0IGZvciB1bmRlcnN0YW5kaW5nIHdoYXTigJlzIGdvaW5nIHJpZ2h0Lg0KDQoNCvCfkYkgKipUYWtlIGhvbWU6KiogVGhlIHJlZC1ob3QgbG93ZXIgaGFsZiBvZiB0aGUgc3RhdGUgKGUuZy4sIElueW8sIE9yYW5nZSkgbWF5IGJlbmVmaXQgZnJvbSBjb29yZGluYXRlZCwgcmVnaW9uYWwgaW50ZXJ2ZW50aW9ucywgd2hpbGUgYmx1ZS1jb2xkIG5vcnRoZXJuIGNvdW50aWVzIG1heSBvZmZlciBtb2RlbHMgb2YgcHJldmVudGlvbiBvciBzZXJ2aWNlIGRlbGl2ZXJ5IHdvcnRoIGVtdWxhdGluZy4NCg0KXA0KDQojIFNwYXRpYWwgTGFnIE1vZGVsDQoNCk5vdyB0aGF0IHdlJ3ZlIGV4cGxvcmVkIGF1dG9jb3JyZWxhdGlvbiBhbmQgaG90IHNwb3RzLCBsZXTigJlzIHRyeSBhIGNsYXNzaWMgc3BhdGlhbCByZWdyZXNzaW9uOiB0aGUgKipTcGF0aWFsIExhZyBNb2RlbCoqLiBUaGlzIG1vZGVsIGlzIHVzZWZ1bCB3aGVuIHRoZSBvdXRjb21lIGluIG9uZSBsb2NhdGlvbiBtaWdodCBiZSBpbmZsdWVuY2VkIGJ5IG5lYXJieSBsb2NhdGlvbnMg4oCUIGxpa2UgZGlhYmV0ZXMgaW4gb25lIGNvdW50eSBiZWluZyBhZmZlY3RlZCBieSBpdHMgbmVpZ2hib3JzLCB3aGljaCB3ZSBoYXZlIHNlZW4gYWJvdmUhDQoNCkJhc2VkIG9uIHRoZSBleHBsb3JhdG9yeSBtYXBwaW5nLCBNb3JhbiBzY2F0dGVycGxvdCwgYW5kIHRoZSBnbG9iYWwgTW9yYW7igJlzIEksIHRoZXJlIGFwcGVhcnMgdG8gYmUgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gaW4gZGlhYmV0ZXMuIFRoaXMgbWVhbnMgdGhhdCBpZiB0aGVyZSBpcyBhIHNwYXRpYWwgbGFnIHByb2Nlc3MgZ29pbmcgb24gYW5kIHdlIGZpdCBhIG5vcm1hbCByZWdyZXNzaW9uIG1vZGVsIG91ciBjb2VmZmljaWVudHMgd2lsbCBiZSBiaWFzZWQgYW5kIGluZWZmaWNpZW50LiBUaGF0IGlzLCB0aGUgY29lZmZpY2llbnQgc2l6ZXMgYW5kIHNpZ25zIGFyZSBub3QgY2xvc2UgdG8gdGhlaXIgdHJ1ZSB2YWx1ZSBhbmQgaXRzIHN0YW5kYXJkIGVycm9ycyBhcmUgdW5kZXJlc3RpbWF0ZWQuIFRoaXMgbWVhbnMgdHJvdWJsZS4gQmlnIHRyb3VibGUuIFJlYWwgYmlnIHRyb3VibGUuDQoNCkluIGEgc3BhdGlhbCBsYWcgbW9kZWwsIHdlIGluY2x1ZGUgYSAqKnNwYXRpYWxseSBsYWdnZWQgZGVwZW5kZW50IHZhcmlhYmxlKiogYXMgYSBwcmVkaWN0b3I6DQotIEl0IGNhcHR1cmVzIHRoZSAic3BpbGxvdmVyIiBlZmZlY3Qgb2YgbmVhcmJ5IHZhbHVlcy4NCi0gSXQgaGVscHMgYWRkcmVzcyBzcGF0aWFsIGRlcGVuZGVuY2UgdGhhdCBvcmRpbmFyeSByZWdyZXNzaW9uIHdvdWxkIG1pc3MuDQoNCldlJ2xsIHVzZSB0aGUgYGxhZ3NhcmxtKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBzcGF0aWFscmVnYCBwYWNrYWdlIGFuZCB3aWxsIHVzZSB0aGUgc2FtZSBzcGF0aWFsIHdlaWdodHMgYXMgYmVmb3JlLg0KDQpgYGB7ciBzcGF0aWFsLWxhZy1tb2RlbH0NCiMgRGVmaW5lIG5laWdoYm9ycyBhbmQgc3BhdGlhbCB3ZWlnaHRzDQpuYiA8LSBwb2x5Mm5iKGNhX2RpYWJfZGF0YSkNCmx3IDwtIG5iMmxpc3R3KG5iLCBzdHlsZSA9ICJXIikNCg0KIyBSdW4gc3BhdGlhbCBsYWcgbW9kZWwNCmxhZ19tb2RlbCA8LSBsYWdzYXJsbShEaWFiZXRlcyB+IGluY29tZSwgZGF0YSA9IGNhX2RpYWJfZGF0YSwgbGlzdHcgPSBsdykNCnN1bW1hcnkobGFnX21vZGVsKQ0KYGBgDQoNCiMjIEludGVycHJldGluZyB0aGUgU3BhdGlhbCBMYWcgTW9kZWwNCg0KTGV04oCZcyBicmVhayBkb3duIHRoZSByZXN1bHRzIG9mIG91ciBzcGF0aWFsIGxhZyBtb2RlbDoNCg0KLSBUaGUgKipzcGF0aWFsIGxhZyBjb2VmZmljaWVudCAocmhvKSoqIGlzIGFib3V0ICoqMC40OCoqIGFuZCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50ICgqcCA8IDAuMDAyKikuIFRoYXQgbWVhbnMgZGlhYmV0ZXMgcmF0ZXMgaW4gYSBjb3VudHkgYXJlICoqc3Ryb25nbHkgaW5mbHVlbmNlZCBieSBuZWlnaGJvcmluZyBjb3VudGllcyoqIOKAlCB0aGVyZSdzIHJlYWwgc3BhdGlhbCBhdXRvY29ycmVsYXRpb24uDQotIFRoZSAqKmluY29tZSBjb2VmZmljaWVudCoqIGlzICoqbmVnYXRpdmUqKiwgc3VnZ2VzdGluZyB0aGF0IGhpZ2hlciBpbmNvbWUgaXMgYXNzb2NpYXRlZCB3aXRoIGxvd2VyIGRpYWJldGVzLCBidXQgaXTigJlzICoqbm90IHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQqKiAoKnAgPSAwLjE3MSopLiBTbywgaW4gdGhpcyBtb2RlbCwgaW5jb21lIGFsb25lIGRvZXNuJ3QgZXhwbGFpbiBtdWNoIHZhcmlhdGlvbiBpbiBkaWFiZXRlcyBvbmNlIHNwYXRpYWwgZWZmZWN0cyBhcmUgYWNjb3VudGVkIGZvci4NCi0gVGhlICoqQUlDIChBa2Fpa2UgSW5mb3JtYXRpb24gQ3JpdGVyaW9uKSoqIGlzIGxvd2VyIGZvciB0aGUgc3BhdGlhbCBsYWcgbW9kZWwgdGhhbiBmb3IgdGhlIHJlZ3VsYXIgT0xTIG1vZGVsICgxNzYgdnMuIDE4NCksIHN1Z2dlc3RpbmcgdGhhdCB0aGUgc3BhdGlhbCBsYWcgbW9kZWwgaXMgYSBiZXR0ZXIgZml0LS0tdGhpcyBtZWFucyB3ZSByZWFsbHkgc2hvdWxkIGJlIGFjY291bnRpbmcgZm9yIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIQ0KLSBUaGUgKipyZXNpZHVhbCBhdXRvY29ycmVsYXRpb24gdGVzdCoqIGlzIG5vdCBzaWduaWZpY2FudCAoKnAgPSAwLjg1KiksIHdoaWNoIG1lYW5zIHRoZSBtb2RlbCBoYXMgc3VjY2Vzc2Z1bGx5IGFkZHJlc3NlZCBzcGF0aWFsIGRlcGVuZGVuY2UgaW4gdGhlIHJlc2lkdWFscy4NCg0K8J+RiSAqKlRha2UgaG9tZToqKiBUaGVyZSBpcyBzdHJvbmcgc3BhdGlhbCBkZXBlbmRlbmNlIGluIGRpYWJldGVzIHJhdGVzLCBhbmQgaW5jbHVkaW5nIHNwYXRpYWwgZWZmZWN0cyBhY2NvdW50cyBmb3IgdGhpcyBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbi4gSG93ZXZlciwgaW5jb21lIG1heSBub3QgYmUgYSBrZXkgZHJpdmVyIG9mIGRpYWJldGVzIG9uY2UgdGhvc2Ugc3BhdGlhbCBpbmZsdWVuY2VzIGFyZSBjb25zaWRlcmVkLg0KDQpcDQoNCiMg8J+TiSBHZW9ncmFwaGljYWxseSBXZWlnaHRlZCBSZWdyZXNzaW9uIChHV1IpDQoNCkdXUiDigJQgR2VvZ3JhcGhpY2FsbHkgV2VpZ2h0ZWQgUmVncmVzc2lvbiDigJQgaXMgbGlrZSByZWd1bGFyIHJlZ3Jlc3Npb24gYnV0IHdpdGggYSB0d2lzdDogdGhlIGNvZWZmaWNpZW50cyBjYW4gY2hhbmdlIGRlcGVuZGluZyBvbiAqKndoZXJlKiogeW91IGFyZS4NCg0KVHJhZGl0aW9uYWwgcmVncmVzc2lvbiBhc3N1bWVzIG9uZS1zaXplLWZpdHMtYWxsIHJlbGF0aW9uc2hpcHMuIEJ1dCBpbiBwdWJsaWMgaGVhbHRoLCB0aGUgaW1wYWN0IG9mIGluY29tZSBvbiBkaWFiZXRlcyBtYXkgZGlmZmVyIGZyb20gU2FuIEZyYW5jaXNjbyB0byBGcmVzbm8uDQoNCkdXUiBmaXRzIGEgc2VwYXJhdGUgcmVncmVzc2lvbiBtb2RlbC0tbm8gbW9yZSBhc3N1bWluZyBvbmUgcmVsYXRpb25zaGlwIGlzIGNvbnNpc3RlbnQgYWNyb3NzIGFsbCBhcmVhcyBpbiB5b3VyIGFuYWx5c2lzIQ0KDQpXZSB3aWxsIHJ1biBHV1IgZmlyc3QgYnkgc2V0dGluZyB1cCBvdXIgZGF0YSB0byBoYXZlIHRoZSBnZW9tZXRyaWMgY2VudHJvaWQgb2YgZWFjaCBjb3VudHkgdXNpbmcgYHN0X2NlbnRyb2lkKClgIGZyb20gdGhlICoqc2YqKiBwYWNrYWdlLiBUaGVuLCB3ZSB3aWxsIHVzZSB0aGUgYGd3ci5zZWwoKWAgZnVuY3Rpb24gZnJvbSAqKnNwZ3dyKiogYW5kIHdpbGwgdXNlIHRoZSBnZW9tZXRyaWMgY2VudHJvaWQgb2Ygb3VyIGNvdW50aWVzIHRvIGZpbmQgdGhlIG9wdGltYWwgYmFuZHdpZHRoIGZvciBvdXIgR1dSIG1vZGVsLiBXZSB3aWxsIHRoZW4gbW9kZWwgdGhlIEdXUiB1c2luZyB0aGUgYGd3cigpYCBmdW5jdGlvbiBmcm9tIHRoZSAqKkdXbW9kZWwqKiBwYWNrYWdlLiBXZSB3aWxsIHVzZSB0aGUgYmFuZHdpZHRocyBhbmQgY2VudHJvaWRzIHRoYXQgd2Ugc2V0IHVwIGluIHRoZSBwcmV2aW91cyBzdGVwcy4NCg0KYGBge3IgZ3dyfQ0KY29vcmRzIDwtIHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKGNhX2RpYWJfZGF0YSkpDQpjYV9kaWFiX2RhdGEkWCA8LSBjb29yZHNbLCAxXQ0KY2FfZGlhYl9kYXRhJFkgPC0gY29vcmRzWywgMl0NCg0KYncgPC0gZ3dyLnNlbChEaWFiZXRlcyB+IGluY29tZSwgZGF0YSA9IGNhX2RpYWJfZGF0YSwgY29vcmRzID0gY29vcmRzLCBhZGFwdCA9IFRSVUUpDQoNCiMgUnVuIEdXUiB1c2luZyBhZGFwdGl2ZSBiYW5kd2lkdGgNCm1vZGVsIDwtIGd3cihEaWFiZXRlcyB+IGluY29tZSwgZGF0YSA9IGNhX2RpYWJfZGF0YSwgY29vcmRzID0gY29vcmRzLCANCiAgICAgICAgICAgICBhZGFwdCA9IGJ3LCBoYXRtYXRyaXggPSBUUlVFKQ0KDQpjYV9kaWFiX2RhdGEkZ3dyX2luY29tZSA8LSBtb2RlbCRTREYkaW5jb21lICMgU3RvcmUgbW9kZWwgY29lZmZpY2llbnRzDQpgYGANCg0KXA0KDQojIyBJbnRlcnByZXRpbmcgdGhlIEdXUiByZXN1bHRzDQoNClRoZSBvdXRwdXQgc2hvd3MgdGhlIG1vZGVsIHRlc3RpbmcgZGlmZmVyZW50ICdhZGFwdGl2ZSBxJyB2YWx1ZXMsIHdoaWNoIHJlcHJlc2VudCB0aGUgcHJvcG9ydGlvbiBvZiBuZWlnaGJvcmluZyBjb3VudGllcyB1c2VkIGluIGVhY2ggbG9jYWwgbW9kZWwuIEl0IGNob29zZXMgdGhlIHEgdmFsdWUgd2l0aCB0aGUgbG93ZXN0IGNyb3NzLXZhbGlkYXRpb24gKENWKSBzY29yZSDigJQgYmVzdCBwcmVkaWN0aXZlIGZpdC4gRm9yIGV4YW1wbGU6IHEgPSAwLjA4MiBtZWFucyB+OCUgb2YgY291bnRpZXMgKOKJiDUgY291bnRpZXMpIGFyZSB1c2VkIGluIGVhY2ggbG9jYWwgcmVncmVzc2lvbi4gTG93ZXIgQ1YgPSBiZXR0ZXIgbW9kZWwuIEl0J3MgZmluZGluZyB0aGUgJ2p1c3QgcmlnaHQnIG5laWdoYm9yaG9vZCBzaXplIGZvciBlYWNoIHJlZ3Jlc3Npb24uDQoNClwNCg0KIyMg8J+XviBNYXAgTG9jYWwgR1dSIENvZWZmaWNpZW50cw0KDQpMZXTigJlzIG1hcCB0aG9zZSBsb2NhbGx5IHZhcnlpbmcgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYW5kIHNlZSB3aGVyZSBpbmNvbWUgbWF0dGVycyBtb3JlIChvciBsZXNzKSBmb3IgZGlhYmV0ZXMuDQoNCmBgYHtyIGd3ci1tYXB9DQp0bV9zaGFwZShjYV9kaWFiX2RhdGEpICsNCiAgdG1fcG9seWdvbnMoZmlsbCA9ICJnd3JfaW5jb21lIiwgDQogICAgICAgICAgICAgIHRtX3NjYWxlX2ludGVydmFscyhzdHlsZSA9ICJxdWFudGlsZSIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXMgPSAiYnJld2VyLnJkX3lsX2duIiksIA0KICAgICAgICAgICAgICBmaWxsLmxlZ2VuZCA9IHRtX2xlZ2VuZCh0aXRsZSA9ICJHV1I6IEluY29tZS1EaWFiZXRlcyBBc3NvY2lhdGlvbiIpKQ0KYGBgDQoNClwNCg0KIyMjIPCfp6kgSW50ZXJwcmV0aW5nIHRoZSBHV1IgTWFwDQoNClRoaXMgbWFwIHNob3dzIGhvdyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gaW5jb21lIGFuZCBkaWFiZXRlcyAqKnZhcmllcyBhY3Jvc3MgQ2FsaWZvcm5pYSoqIOKAlCBpdCdzIGEgc3BhdGlhbCB2aWV3IG9mIHRoZSByZWdyZXNzaW9uIGNvZWZmaWNpZW50IGJldHdlZW4gaW5jb21lIGFuZCBkaWFiZXRlcy4NCg0KLSBBbGwgdGhlIHZhbHVlcyBhcmUgKipuZWdhdGl2ZSoqLCBtZWFuaW5nIGhpZ2hlciBpbmNvbWUgaXMgYXNzb2NpYXRlZCB3aXRoICoqbG93ZXIgZGlhYmV0ZXMgcHJldmFsZW5jZSoqIOKAlCBidXQgKip0aGUgc3RyZW5ndGgqKiBvZiB0aGlzIHJlbGF0aW9uc2hpcCB2YXJpZXMgYWNyb3NzIHNwYWNlLg0KDQotICoqRGFyayByZWQgY291bnRpZXMqKiAobW9zdCBuZWdhdGl2ZSB2YWx1ZXMpOiBUaGVzZSBhcmUgdGhlIHBsYWNlcyB3aGVyZSBpbmNvbWUgaGFzIHRoZSAqKnN0cm9uZ2VzdCBpbnZlcnNlIHJlbGF0aW9uc2hpcCoqIHdpdGggZGlhYmV0ZXMuIEluIG90aGVyIHdvcmRzLCBpbXByb3ZpbmcgZWNvbm9taWMgY29uZGl0aW9ucyBoZXJlIGNvdWxkIGhhdmUgYSAqKmJpZyBwdWJsaWMgaGVhbHRoIHBheW9mZioqLg0KLSAqKkxpZ2h0ZXIgeWVsbG93IHRvIGdyZWVuIGNvdW50aWVzKio6IFRoZXNlIGFyZWFzIHN0aWxsIHNob3cgYSBuZWdhdGl2ZSByZWxhdGlvbnNoaXAsIGJ1dCBpdCdzICoqd2Vha2VyKiouIFRoYXQgbWlnaHQgbWVhbiBvdGhlciBzb2NpYWwgb3IgZW52aXJvbm1lbnRhbCBmYWN0b3JzIGFyZSBwbGF5aW5nIGEgYmlnZ2VyIHJvbGUgdGhhbiBpbmNvbWUgaW4gdGhlc2UgY291bnRpZXMuDQotICoqRGFyayBncmVlbiBhcmVhcyoqOiBUaGUgd2Vha2VzdCBuZWdhdGl2ZSBhc3NvY2lhdGlvbnMg4oCUIGhlcmUsIGluY29tZSBtaWdodCBtYXR0ZXIgbGVzcyBmb3IgZGlhYmV0ZXMgcHJldmVudGlvbiwgb3Igb3RoZXIgZmFjdG9ycyBtYXkgZG9taW5hdGUuDQoNCvCfkYkgKipUYWtlIGhvbWUqKjogVGhpcyBtYXAgaGVscHMgcHVibGljIGhlYWx0aCBwcm9mZXNzaW9uYWxzIHRhaWxvciBpbnRlcnZlbnRpb25zLiBJbiBjb3VudGllcyB3aGVyZSBpbmNvbWUgaXMgYSBzdHJvbmcgZHJpdmVyIG9mIGRpYWJldGVzLCBlY29ub21pYyBzdXBwb3J0IHByb2dyYW1zIG1pZ2h0IHJlZHVjZSBwcmV2YWxlbmNlLiBJbiBvdGhlcnMsIGRpZmZlcmVudCBzdHJhdGVnaWVzIG1heSBiZSBuZWVkZWQuDQoNClwNCg0KIyBUYWtlIGhvbWUNCg0KLSAqKk1vcmFuJ3MgSSoqOiBNZWFzdXJlcyBvdmVyYWxsIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uIChpcyB0aGVyZSBhIHBhdHRlcm4/KS4NCi0gKipMSVNBL0dpKio6IFJldmVhbCBsb2NhbCBjbHVzdGVycyBhbmQgaG90L2NvbGQgc3BvdHMgKHdoZXJl4oCZcyB0aGUgYWN0aW9uPykuDQotICoqU3BhdGlhbCBMYWcgTW9kZWwqKjogUXVhbnRpZmllcyBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBhbmQgYWRqdXN0cyBmb3Igc3BhdGlhbCBhdXRvY29ycmVsYXRpb24gaW4gcmVncmVzc2lvbiBtb2RlbHMuDQotICoqR1dSKio6IFNob3dzIHdoZXJlIGV4cG9zdXJlLW91dGNvbWUgcmVsYXRpb25zaGlwcyBhcmUgc3Ryb25nLCB3ZWFrLCBvciBmbGlwcGluZyBkaXJlY3Rpb24uDQoNCiMgU3VtbWFyeQ0KDQpCb29tISBZb3UganVzdCBjcnVuY2hlZCByZWFsIHB1YmxpYyBoZWFsdGggZGF0YSB1c2luZyBjdXR0aW5nLWVkZ2Ugc3BhdGlhbCB0b29scyB0byBsb29rIGF0IHNwYXRpYWwgY2x1c3RlcmluZyAvIGF1dG9jb3JyZWxhdGlvbi4gQW5kIHdlIGV2ZW4gbGVhcm5lZCBzb21lIHJlZ3Jlc3Npb24gYXBwcm9hY2hlcyB0byBhY2NvdW50IGZvciBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbi4gV2VsbCBkb25lISBXZSd2ZSBqdXN0IHNjcmF0Y2hlZCB0aGUgc3VyZmFjZSBvbiB0aGVzZSBzcGF0aWFsIHN0YXRpc3RpY3MgYXBwcm9hY2hlcywgYnV0IHdlJ3ZlIGNvbWUgc28gZmFyIGZyb20gV2VlayAxISBJJ20gcHJvdWQgb2YgeW91IQ0KDQo=