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:

  1. Use multiple methods to identify spatial autocorrelation
  2. Account for spatial autcorrelation in regression using spatial lag models
  3. 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=