Introduction
In this session we draw together some of what we have learned about mapping and creating spatial weights in R, using it to undertake spatial forms of regression, specifically a spatially lagged error model, a lagged y model, a mixed model and Geographically Weighted Regression. The session is more an introduction to how to fit these sorts of models in R rather than an in-depth tutorial into their specification and use. Some of the models fit the (simulated) data better than others. However, that is not to say they would necessarily be preferred when using other data and in other analytical contexts. Which model to choose will depend on the purpose of the analysis and the theoretical basis for the modelling.
Please note:
This session assumes an intermediate knowledge of linear regression modelling with some understanding of how to fit and interpret these models in R. If you do not have this knowledge then as you are reading keep in mind the foundational aim of the session which is to use a spatial weights matrix to help quantify geographical patterns in data and then, if those patterns exist, to help model them in a geographical way, doing so by incorporating that matrix into the regression models.
You are strongly advised to have read https://www.dropbox.com/s/tlz7nenwkdny4ds/Harris_chapter-revised.pdf?dl=0 and https://www.dropbox.com/s/5uh9grmtilrup7b/Wiley-AAG5.pdf?dl=0 before undertaking this session.
To get started,
rm(list=ls())
# Remove eveything currently in the workspace
if(!"sp" %in% installed.packages()) install.packages("sp")
require(sp)
load("session5.RData")
This loads a workspace containing an sp object of Beijing’s districts (spatial polygons), synthetic land parcel and district data (spatial points), and a spatial weights object for the land parcels with a Gaussian decay to the 35th neighbour. The choice of 35 was determined using a correlation approach – how correlated is the value at each location with its first, second, third, …, etc. neighbour? See Session 4 for further details.
Using a Moran’s test, it is shown that the (log) of the land price values show significant spatial variation,
if(!"spdep" %in% installed.packages()) install.packages("spdep")
require(spdep)
moran.test(combined.map$LNPRICE, spknear35gaus)
… our task is to try and explain that variation through a model-based approach.
OLS regression
We begin by fitting a regression model. It is possible that the regression model will explain the patterning of the land prices: land prices could be higher the closer the land is to the Central Business District, for example, or closer to rivers. And, to some extent it does. A test of the residuals, however, reveals an apparent spatial dependencies in them that violates the assumption of independence. It isn’t especially strong but it is present.
model1 <- lm(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map)
lm.morantest(model1, listw2U(spknear35gaus))
In addition to their apparent lack of independence we may also note that the residuals appear to show evidence of heteroskedasticity – of non-constant variance (they are therefore not independent nor identically distributed). Heteroskedasticity is sometimes indicative of spatial variation – that a ‘one size fits all’ approach to modelling will not work. But it can also be because the model is incorrectly specified (missing variables perhaps, non-linear relationships, unmodelled interactions amongst the variables, etc.).
A plot to check for heteroskedasticty is,
plot(model1, which = 1)
Unfortunately, that same code won’t work for the spatial models we produce below. Instead, we can write a function to produce what we need.
hetero.plot <- function(model) {
fm <- as.character(formula(model))
plot(residuals(model) ~ fitted(model),
xlab = paste("Fitted values\n",fm[2],fm[1],fm[3]),
ylab = "Residuals")
abline(h=0, lty="dotted")
lines(lowess(fitted(model), residuals(model)), col="red")
}
# And, now to run it...
hetero.plot(model1)
There are no quick fixes for the violated assumption of independent and identically distributed errors (heteroskedasticity). The violation suggests we cannot take on trust the standard errors, t- and p-values shown under the model summary. It is likely that the standard errors for at least some of the predictor variables have been under-estimated (because if we have spatial dependencies in the residuals then they likely arise from spatial dependencies in the data which in turn mean we have less degrees of freedom than we think we have).
summary(model1)
If we have doubts about this model because of the spatial patterning of the errors (a pattern which is likely but not necessarily caused by the patterning of the Y variable) then we need to consider other approaches.
Spatial Econometric Models
Spatial Error Model
One option is to fit a spatial simultaneous autoregressive error model which decomposes the error (the residuals) into two parts: a spatially lagged component and a remaining error: y=Xβ+λWξ+ε
Fitting the model and comparing it with the standard regression model we find that two of the predictor variables (DELE and POPDEN) no longer are significant at a conventional level and that the standard errors for many have risen. The lambda (a measure of spatial autocorrelation) is significant. The model fits the data better than the previous model: the AIC score is lower and the log likelihood value greater, as is the pseudo-R2 value:
if(! "spatialreg" %in% installed.packages()) install.packages("spatialreg")
require(spatialreg)
model2 <- errorsarlm(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map, listw = spknear35gaus)
summary(model2)
The AIC is given in the model output above but we can also obtain it using,
AIC(model1)
AIC(model2)
As a rule of thumb, a reduction in the AIC score of ten or more is strong evidence in favour of the model with the lower AIC value – which here is the spatial error model. (You can this rule of thumb at https://stats.stackexchange.com/questions/282686/what-percentage-decrease-in-aic-is-significant)
We can also use the log likelihood values to compare the models:
logLik(model1)
logLik(model2)
Here, higher is better so the evidence is again in favour of the spatial error model. More formally,
LR <- 2 * (logLik(model2) - logLik(model1))
# This is the likelihood ratio
dfchg <- attr(logLik(model2), "df") - attr(logLik(model1), "df")
# This is the difference in the degrees of freedom
LR > qchisq(0.99, df = dfchg)
# This compares the likelihood ratio with a chi-square
# distribution
… which passes a statistical test at a 99 per cent confidence in favour of the spatial error model. (If you want to read up on this test: https://en.wikipedia.org/wiki/Likelihood-ratio_test)
Finally, the pseudo-R-squared for the spatial error model is
cor(combined.map$LNPRICE, fitted(model2))^2
which is higher than the R-squared value for the original model; that was
summary(model1)$r.squared
(The R-squared value can be calculate as the square of the correlation between the Y values and what the model predicts for them, the fitted values)
In summary, the spatial error model appears to fit the data better although the problem of heteroskedasticity remains:
hetero.plot(model2)
A spatially lagged y model
Although the spatial error model (above) fits the data better than the standard OLS model, it tells us only that there is an unexplained spatial structure to the residuals, not what caused them. It may offer better estimates of the model parameters and their statistical significance but it does not presuppose any particular spatial process generating the patterns in the land price values. A different model that explicitly tests for whether the land value at a point is functionally dependent on the values of neighbouring points is the spatially lagged y model: y=ρWy+Xβ+ε. It models an ‘overspill’ or chain effect where the outcome (the Y value) at any location is affected by the outcomes at surrounding locations.
The model is fitted in R using,
model3 <- lagsarlm(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map, spknear35gaus)
summary(model3)
The model is an improvement on the OLS model. However, there is significant autocorrelation remaining in the residuals (see the bottom lines of the model output) and it does not appear to fit the data as well as the error model: the lagged y model has a greater AIC, lower log likelihood and lower pseudo-R2, each indicating a poorer fit.
AIC(model3)
logLik(model3)
cor(combined.map$LNPRICE, fitted(model3))^2
The heteroskedasticity remains:
hetero.plot(model3)
(potentially related to the significant autocorrelation in the residuals).
Note that the beta estimates of the lagged y-model cannot be interpreted in the same way as for a standard OLS model. For example, the beta estimate of 0.004 for the POPDEN variable does not mean that if (hypothetically) we increased that variable by one unit at each location we should then expect the (log) of the land parcel to everywhere increase by 0.004 holding the other X variables constant. That is the correct interpretation for a standard (OLS) regression model and also for the spatial error model but not for where the lag of the Y variable is included as a predictor variable. The reason is because if we did raise the POPDEN value it would start something akin to a ‘chain reaction’ through the feedback of Y via the lagged Y values. The total impact is a sum of the direct effect – what is predicted to happen through the 1 unit change in POPDEN – and the indirect effect, which is that caused by ‘the chain reaction’ / feedback / ‘overspill’ in the system:
impacts(model3, listw = spknear35gaus)
In fact, an increase of 1 unit in, for example, the POPDEN variable, will play out slightly differently in different places (because they have different neighbours with different Y values and at different distances from each other). The code below, which is from Ward & Gleditsch (2008, pp.47), will calculate the impact (of a one unit change in POPDEN) at each location but below I have limited it to the first ten else it will take a long time to run.
n <- nrow(combined.map)
I <- matrix(0, nrow=n, ncol=n)
diag(I) <- 1
rho <- model3$rho
beta <- model3$coefficients["POPDEN"]
weights.matrix <- listw2mat(spknear35gaus)
results <- rep(NA, times=10)
for (i in 1:10) {
xvector <- rep(0, times=n)
xvector[i] <- 1
impact <- solve(I - rho * weights.matrix) %*% xvector * beta
results[i] <- impact[i]
}
results
Choosing between the models using Lagrange Multiplier (LM) Tests
Before fitting the spatial error and lagged y models (above), we could have looked for evidence in support of them using the function lm.LMtests(…). This tests the basic OLS specification against the more general spatial error and lagged y models. Robust tests also are given. There is evidence for both of the spatial models in favour of the simpler OLS model but it is stronger (in purely statistical terms) for the error model.
lm.LMtests(model1, spknear35gaus, test="all")
Anselin and Rey (2014, p.110) offer the following decision tree that can, in the absence of a more theoretical basis for the model choice (e.g. the type of process being modelled), be used in conjunction with the test results above to help select the model.
browseURL("anselin_rey.jpg")
Using the decision tree, there seems to be greater evidence in favour of the spatial error model than the spatially lagged y model, although potentially both are ‘beaten’ by a third model, the spatial autoregressive moving average (SARMA) model! We won’t dwell on this other than to note that it can account for spatial dependence among the error terms as well as spatial dependence in the Y variable. To fit it in R:
model4 <- spautolm(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map, spknear35gaus, family = "SMA")
summary(model4)
On balance it may fit the data less well than model 2 given its greater complexity (remember, a lower value is preferred for AIC scores, and a higher value for log likelihood ones):
fit <- matrix(nrow = 2, ncol = 2)
fit[1,] <- c(AIC(model2), AIC(model4))
fit[2,] <- c(logLik(model2), logLik(model4))
colnames(fit) <- c("Model2", "Model4")
rownames(fit) <- c("AIC", "logLik")
fit
Including lagged X variables
So far we have accommodated the spatial dependencies in the data by consideration to the error and with consideration to the dependent (y) variable. Attention now turns to the predictor variables. An extension to the lagged y model is to lag all the included x variables too (this is sometimes referred to as the Spatial Durbin model)
model5 <- lagsarlm(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map, spknear35gaus, Durbin = TRUE)
summary(model5)
What we find is that the lag of the distance to the CBD (measured on a log scale) is significant but note that the direction of the relationship is different from that for the original DCBD variable – the sign has reversed. The same has happened to the dummy variable indicating the sale of the land parcel is in the years 2008 to 2009. Taking the first case, what this may suggest is that the relationship between land price value and the (log of) distance to the CBD is not linear. Adding the square of this variable to the original OLS model improves the model fit: the difference of about 5 in the AIC scores is medium evidence in favour of the new model.
model1b <- update(model1, . ~ . + I(DCBD^2))
# Adds the square of the DCBD variable
AIC(model1b)
AIC(model1)
LR <- 2 * (logLik(model1b) - logLik(model1))
dfchg <- attr(logLik(model1b), "df") - attr(logLik(model1), "df")
LR > qchisq(0.99, df = dfchg)
We could also try adding the square of the DCBD variable to the spatial error model:
model2b <- update(model2, . ~ . + I(DCBD^2))
AIC(model2b)
AIC(model2)
LR <- 2 * (logLik(model2b) - logLik(model2))
dfchg <- attr(logLik(model2b), "df") - attr(logLik(model2), "df")
LR > qchisq(0.99, df = dfchg)
… it isn’t a success. Perhaps adding the spatial lag of the DCBD variable will work better?
lag.DCBD <- lag.listw(spknear35gaus, combined.map$DCBD)
model2c <- update(model2, . ~ . + lag.DCBD)
AIC(model2c)
AIC(model2)
LR <- 2 * (logLik(model2b) - logLik(model2))
dfchg <- attr(logLik(model2c), "df") - attr(logLik(model2), "df")
LR > qchisq(0.99, df = dfchg)
No, it doesn’t!
Including lagged X variables in the OLS model
We can, if we wish, include specific lagged X variables in the OLS model. The process is to create them then include them in the model. The lag of DCBD and the lag of Y0809 are the most obvious candidates to include (from model 4, above). To create the lagged variables and add them to the model:
lag.DCBD <- lag.listw(spknear35gaus, combined.map$DCBD)
lag.Y0809 <- lag.listw(spknear35gaus, combined.map$Y0809)
model1c <- update(model1, .~. + lag.DCBD + lag.Y0809)
AIC(model1c)
AIC(model1)
The result seems to fit the data better than the original OLS model (AIC score of 2848.3 Vs 2865.7 – remember, the lower the better) but actually the lag of DCBD appears not to be significant in this model whilst significant spatial autocorrelation appears to remain:
summary(model1c)
lm.morantest(model1c, spknear35gaus)
Taking Stock
At this point, it is worth looking back and taking stock. Which of the models appears to fit the data best?
If your head is spinning a bit don’t worry because that serves to make a useful point. There are lots of models that could be fitted to the data and we have by no means exhausted the possibilities above. Therefore we need a model fitting strategy. Sometimes that will be informed by theory or by other studies. Otherwise, I would consider the following:
- Map the Y variable and look for a spatial pattern
- Formally test for spatial dependency in the Y variable by constructing a spatial weights matrix and using global and/or localised Moran’s values
- for (2) you will need to give consideration to how you specify the weights matrix – you may want to try different weighting schemes. What do the tell you about the scale of the spatial pattern in the data? (Is it localised clustering - locations only related to their most immediate neighbours or…?)
- Fit the OLS model and go through the usual process of refining that model (e.g. drop insignificant variables)
- Test the residuals for a spatial pattern using the Moran’s test for regression residuals.
- If there is a pattern, either choose a spatial regression model on theoretical grounds (because it captures the process you want to model) or use the Lagrange multiplier tests.
- Consider also the possibility that the spatial pattern exists because the OLS model was mis-specified (e.g. the relationship between one or more Xs and the Y variable is non-linear).
- Compare your spatial regression model with the results of a Geographically Weighted Regression (see below).
Geographically Weighted Regression
An alternative way of attempting to explain the spatial variation in the land value prices is to allow the effect sizes of the predictor variables to themselves vary over space. For the previous spatial regression models they were fixed – there was one estimate of, for example, the effect of distance to the CBD on land prices. But what if being close to the CBD matters more in some places than it does in others?
Geographically Weighted Regression (GWR) is a method where the estimate of βx at point location i is not simply the global estimate for all points in the study region but a local estimate based on surrounding points weighted by the inverse of their distance away (hence geographically weighted). If there are 1000 locations in a study region then GWR will fit 1000 localised models. The results of those models are then compared to see how much the regression coefficients vary across the study region.
Fitting the GWR model
The stages of fitting a Geographically Weighted Regression model are first to load the GWR library, calculate a distance matrix containing the distances between points (this is needed for the inverse distance weighting), calibrate the bandwidth for the local model fitting (or you can specify a bandwidth yourself), fit the model then look for evidence of spatial variations in the estimates.
For the first couple of stages:
if(! "GWmodel" %in% installed.packages()) install.packages("GWmodel")
require(GWmodel)
# Install and load the package
distances <- gw.dist(dp.locat=coordinates(combined.map))
# Calculate the distances between the points
Here the bandwidth decreases to zero at the 30th neighbour, encouragingly similar to the 35th neighbour value we have used for our spatial weights throughout this session. Rather less encouragingly, it seems surprisingly sensitive to the shape of the kernel (the way the weighting tapers with distance). For comparison:
bw2 <- bw.gwr(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map, adaptive=T, dMat=distances, kernel = "bisquare")
bw2
This sensitivity to the choice of kernel function is worth investigating further. However, we will skip over it (!) and use the Gaussian version to next fit the model:
gwr.model <- gwr.basic(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map, adaptive=T, dMat=distances, bw=bw, kernel = "gaussian")
gwr.model
Looking at the results we can see that the model has a better fit to the data than any other fitted thus far although this is perhaps not surprising (as a GWR is really lots of models fitted one after another across the study region it is always likely to out-perform a one size fits all approach). The summary of the GWR estimates is of how the (local) beta estimates vary across the study region - of, for example, how the effect of distance to the CBD varies from one place to another. From the inter-quartile range (shown in the output), the effect of distance to CBD on land value prices is typically found to be from -0.682 to -0.030 (in some locations, as the distance from the CBD increases, the log of the land price values drops by 0.682 but in others the drop is much less, dropping by 0.030).
The function montecarlo.gwr(…) uses a randomisation approach to undertake significance testing for the variability of the estimated local beta estimates (the regression parameters) - i.e. of how significantly they exhibit a geographical variation. The default number of simulations is 99 (which, with the actual estimates, gives 100 sets of values in total). That is quite a low number but can be used for illustrative purposes. In practice it would be better to raise it to 999, to 9999 or even more. Even with 99 it will take a little while to run.
montecarlo.gwr(LNPRICE ~ DCBD + DELE + DRIVER + DPARK + POPDEN + JOBDEN + Y0405 + Y0607 + Y0809, data=combined.map, adaptive=T, bw=bw, kernel = "gaussian")
All but one (or marginally two) of the variables appear to show significant spatial variation (p < 0.01).
The localised estimates (the result from each fitting of the regression model as it moves across the study region) are stored in the Spatial Data Frame (SDF):
names(gwr.model)
head(gwr.model$SDF)
The estimates for the effect of distance to CBD on land price, for example, are founded in the column, gwr.model\(SDF\)DCBD
head(gwr.model$SDF$DCBD)
Since each one of these estimates corresponds to a location in the original map (and in the same order), it is straightforward to map them using the native plot functions for sf objects (see https://r-spatial.github.io/sf/articles/sf5.html)
if(! "rgeos" %in% installed.packages()) install.packages("rgeos")
# This package is needed to convert the sp objects into sf
if(! "tmap" %in% installed.packages()) install.packages("tmap")
combined.map$DCBD_beta <- gwr.model$SDF$DCBD
# Create a new variable in the map data which are local GWR
# estimates for the DCBD variable
sf_map <- st_as_sf(combined.map)
sf_districts <- st_as_sf(districts)
# Converstion to simple features
plot(sf_map["DCBD_beta"], reset = FALSE, pch = 19, cex = 0.8)
# pch controls the plotting 'character' - see ?points
# cex is the character expansion (i.e. size)
# You need reset = FALSE to add further elements to the map
plot(st_geometry(sf_districts), add = TRUE)
However, not all of those regression coefficients are necessarily significant. To highlight those that that are (p < 0.05):
if(! "tidyverse" %in% installed.packages()) install.packages("tidyverse")
require(tidyverse)
sf_map$DCBD_beta_tval <- gwr.model$SDF$DCBD_TV
sf_map %>%
filter(abs(sf_map$DCBD_beta_tval) > 1.96) -> sub.map
plot(sub.map["DCBD_beta"], reset = FALSE, pch = 19, cex = 0.8)
plot(st_geometry(sf_districts), add = TRUE)
Arguably a more stringent definition of significance should be applied as GWR undertakes a process of repeat model fitting. To adjust the p-values for multiple hypothesis tests see the help menu, ?gwr.t.adjust(gwm.Obj).
Geographically Weighted Statistics
In closing, it should be noted that GWR is an extension of a broader class of statistics – geographically weighted statistics.
For example, to calculate geographically weighted summary statistics for the land price variable:
gwstats <- gwss(combined.map, vars = "LNPRICE", adaptive=T, bw=bw,
kernel = "gaussian")
head(gwstats$SDF)
names(gwstats$SDF)
What has been calculated is the geographically weighted mean land price around each location and the geographically weighted standard deviation amongst other values. The geographically weighted median, and interquartile range could also be calculated (see ?gwss).
The geographically weighted standard deviation, for example, shows how much the land prices vary locally around each location. We can, of course, map them:
sf_map$GWSD <- gwstats$SDF$LNPRICE_LSD
plot(sf_map["GWSD"], reset = FALSE, pch = 19, cex = 0.8)
plot(st_geometry(sf_districts), add = TRUE)
The areas shown in yellow have the most local variation in the land prices.
You can - and should! - read more about geographically weighted regression in the further reading (see below).
LS0tCnRpdGxlOiAiTWFwcGluZyBBbmQgTW9kZWxsaW5nIEdlb2dyYXBoaWMgRGF0YSBJbiBSIgpzdWJ0aXRsZTogIlNlc3Npb24gNTogU3BhdGlhbCBhbmQgR2VvZ3JhcGhpY2FsbHkgV2VpZ2h0ZWQgUmVncmVzc2lvbiBBbmFseXNpcyIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyMgSW50cm9kdWN0aW9uCgpJbiB0aGlzIHNlc3Npb24gd2UgZHJhdyB0b2dldGhlciBzb21lIG9mIHdoYXQgd2UgaGF2ZSBsZWFybmVkIGFib3V0IG1hcHBpbmcgYW5kIGNyZWF0aW5nIHNwYXRpYWwgd2VpZ2h0cyBpbiBSLCB1c2luZyBpdCB0byB1bmRlcnRha2Ugc3BhdGlhbCBmb3JtcyBvZiByZWdyZXNzaW9uLCBzcGVjaWZpY2FsbHkgYSBzcGF0aWFsbHkgbGFnZ2VkIGVycm9yIG1vZGVsLCBhIGxhZ2dlZCB5IG1vZGVsLCBhIG1peGVkIG1vZGVsIGFuZCBHZW9ncmFwaGljYWxseSBXZWlnaHRlZCBSZWdyZXNzaW9uLiBUaGUgc2Vzc2lvbiBpcyBtb3JlIGFuIGludHJvZHVjdGlvbiB0byBob3cgdG8gZml0IHRoZXNlIHNvcnRzIG9mIG1vZGVscyBpbiBSIHJhdGhlciB0aGFuIGFuIGluLWRlcHRoIHR1dG9yaWFsIGludG8gdGhlaXIgc3BlY2lmaWNhdGlvbiBhbmQgdXNlLiBTb21lIG9mIHRoZSBtb2RlbHMgZml0IHRoZSAoc2ltdWxhdGVkKSBkYXRhIGJldHRlciB0aGFuIG90aGVycy4gSG93ZXZlciwgdGhhdCBpcyBub3QgdG8gc2F5IHRoZXkgd291bGQgbmVjZXNzYXJpbHkgYmUgcHJlZmVycmVkIHdoZW4gdXNpbmcgb3RoZXIgZGF0YSBhbmQgaW4gb3RoZXIgYW5hbHl0aWNhbCBjb250ZXh0cy4gV2hpY2ggbW9kZWwgdG8gY2hvb3NlIHdpbGwgZGVwZW5kIG9uIHRoZSBwdXJwb3NlIG9mIHRoZSBhbmFseXNpcyBhbmQgdGhlIHRoZW9yZXRpY2FsIGJhc2lzIGZvciB0aGUgbW9kZWxsaW5nLgoKKipQbGVhc2Ugbm90ZToqKgoKVGhpcyBzZXNzaW9uIGFzc3VtZXMgYW4gaW50ZXJtZWRpYXRlIGtub3dsZWRnZSBvZiBsaW5lYXIgcmVncmVzc2lvbiBtb2RlbGxpbmcgd2l0aCBzb21lIHVuZGVyc3RhbmRpbmcgb2YgaG93IHRvIGZpdCBhbmQgaW50ZXJwcmV0IHRoZXNlIG1vZGVscyBpbiBSLiBJZiB5b3UgZG8gbm90IGhhdmUgdGhpcyBrbm93bGVkZ2UgdGhlbiBhcyB5b3UgYXJlIHJlYWRpbmcga2VlcCBpbiBtaW5kIHRoZSBmb3VuZGF0aW9uYWwgYWltIG9mIHRoZSBzZXNzaW9uIHdoaWNoIGlzIHRvIHVzZSBhIHNwYXRpYWwgd2VpZ2h0cyBtYXRyaXggdG8gaGVscCBxdWFudGlmeSBnZW9ncmFwaGljYWwgcGF0dGVybnMgaW4gZGF0YSBhbmQgdGhlbiwgaWYgdGhvc2UgcGF0dGVybnMgZXhpc3QsIHRvIGhlbHAgbW9kZWwgdGhlbSBpbiBhIGdlb2dyYXBoaWNhbCB3YXksIGRvaW5nIHNvIGJ5IGluY29ycG9yYXRpbmcgdGhhdCBtYXRyaXggaW50byB0aGUgcmVncmVzc2lvbiBtb2RlbHMuCgpZb3UgYXJlICpzdHJvbmdseSogYWR2aXNlZCB0byBoYXZlIHJlYWQgaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy90bHo3bmVud2tkbnk0ZHMvSGFycmlzX2NoYXB0ZXItcmV2aXNlZC5wZGY/ZGw9MAphbmQKaHR0cHM6Ly93d3cuZHJvcGJveC5jb20vcy81dWg5Z3JtdGlscnVwN2IvV2lsZXktQUFHNS5wZGY/ZGw9MApiZWZvcmUgdW5kZXJ0YWtpbmcgdGhpcyBzZXNzaW9uLgoKVG8gZ2V0IHN0YXJ0ZWQsCgpgYGB7cn0Kcm0obGlzdD1scygpKQojIFJlbW92ZSBldmV5dGhpbmcgY3VycmVudGx5IGluIHRoZSB3b3Jrc3BhY2UKaWYoISJzcCIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkgaW5zdGFsbC5wYWNrYWdlcygic3AiKQpyZXF1aXJlKHNwKQpsb2FkKCJzZXNzaW9uNS5SRGF0YSIpCmBgYAoKVGhpcyBsb2FkcyBhIHdvcmtzcGFjZSBjb250YWluaW5nIGFuIHNwIG9iamVjdCBvZiBCZWlqaW5nJ3MgZGlzdHJpY3RzIChzcGF0aWFsIHBvbHlnb25zKSwgc3ludGhldGljIGxhbmQgcGFyY2VsIGFuZCBkaXN0cmljdCBkYXRhIChzcGF0aWFsIHBvaW50cyksIGFuZCBhIHNwYXRpYWwgd2VpZ2h0cyBvYmplY3QgZm9yIHRoZSBsYW5kIHBhcmNlbHMgd2l0aCBhIEdhdXNzaWFuIGRlY2F5IHRvIHRoZSAzNXRoIG5laWdoYm91ci4gVGhlIGNob2ljZSBvZiAzNSB3YXMgZGV0ZXJtaW5lZCB1c2luZyBhIGNvcnJlbGF0aW9uIGFwcHJvYWNoIC0tIGhvdyBjb3JyZWxhdGVkIGlzIHRoZSB2YWx1ZSBhdCBlYWNoIGxvY2F0aW9uIHdpdGggaXRzIGZpcnN0LCBzZWNvbmQsIHRoaXJkLCAuLi4sIGV0Yy4gbmVpZ2hib3VyPyBTZWUgU2Vzc2lvbiA0IGZvciBmdXJ0aGVyIGRldGFpbHMuCgpVc2luZyBhIE1vcmFuJ3MgdGVzdCwgaXQgaXMgc2hvd24gdGhhdCB0aGUgKGxvZykgb2YgdGhlIGxhbmQgcHJpY2UgdmFsdWVzIHNob3cgc2lnbmlmaWNhbnQgc3BhdGlhbCB2YXJpYXRpb24sCgpgYGB7cn0KaWYoISJzcGRlcCIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkgaW5zdGFsbC5wYWNrYWdlcygic3BkZXAiKQpyZXF1aXJlKHNwZGVwKQptb3Jhbi50ZXN0KGNvbWJpbmVkLm1hcCRMTlBSSUNFLCBzcGtuZWFyMzVnYXVzKQpgYGAKCi4uLiBvdXIgdGFzayBpcyB0byB0cnkgYW5kIGV4cGxhaW4gdGhhdCB2YXJpYXRpb24gdGhyb3VnaCBhIG1vZGVsLWJhc2VkIGFwcHJvYWNoLgoKIyMgT0xTIHJlZ3Jlc3Npb24KCldlIGJlZ2luIGJ5IGZpdHRpbmcgYSByZWdyZXNzaW9uIG1vZGVsLiBJdCBpcyBwb3NzaWJsZSB0aGF0IHRoZSByZWdyZXNzaW9uIG1vZGVsIHdpbGwgZXhwbGFpbiB0aGUgcGF0dGVybmluZyBvZiB0aGUgbGFuZCBwcmljZXM6IGxhbmQgcHJpY2VzIGNvdWxkIGJlIGhpZ2hlciB0aGUgY2xvc2VyIHRoZSBsYW5kIGlzIHRvIHRoZSBDZW50cmFsIEJ1c2luZXNzIERpc3RyaWN0LCBmb3IgZXhhbXBsZSwgb3IgY2xvc2VyIHRvIHJpdmVycy4gQW5kLCB0byBzb21lIGV4dGVudCBpdCBkb2VzLiBBIHRlc3Qgb2YgdGhlIHJlc2lkdWFscywgaG93ZXZlciwgcmV2ZWFscyBhbiBhcHBhcmVudCBzcGF0aWFsIGRlcGVuZGVuY2llcyBpbiB0aGVtIHRoYXQgdmlvbGF0ZXMgdGhlIGFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbmNlLiBJdCBpc24ndCBlc3BlY2lhbGx5IHN0cm9uZyBidXQgaXQgaXMgcHJlc2VudC4KCmBgYHtyfQptb2RlbDEgPC0gbG0oTE5QUklDRSB+IERDQkQgKyBERUxFICsgRFJJVkVSICsgRFBBUksgKyBQT1BERU4gKyBKT0JERU4gKyBZMDQwNSArIFkwNjA3ICsgWTA4MDksIGRhdGE9Y29tYmluZWQubWFwKQpsbS5tb3JhbnRlc3QobW9kZWwxLCBsaXN0dzJVKHNwa25lYXIzNWdhdXMpKQpgYGAKCkluIGFkZGl0aW9uIHRvIHRoZWlyIGFwcGFyZW50IGxhY2sgb2YgaW5kZXBlbmRlbmNlIHdlIG1heSBhbHNvIG5vdGUgdGhhdCB0aGUgcmVzaWR1YWxzIGFwcGVhciB0byBzaG93IGV2aWRlbmNlIG9mIGhldGVyb3NrZWRhc3RpY2l0eSDigJMgb2Ygbm9uLWNvbnN0YW50IHZhcmlhbmNlICh0aGV5IGFyZSB0aGVyZWZvcmUgbm90IGluZGVwZW5kZW50IG5vciBpZGVudGljYWxseSBkaXN0cmlidXRlZCkuIEhldGVyb3NrZWRhc3RpY2l0eSBpcyBzb21ldGltZXMgaW5kaWNhdGl2ZSBvZiBzcGF0aWFsIHZhcmlhdGlvbiAtLSB0aGF0IGEgJ29uZSBzaXplIGZpdHMgYWxsJyBhcHByb2FjaCB0byBtb2RlbGxpbmcgd2lsbCBub3Qgd29yay4gQnV0IGl0IGNhbiBhbHNvIGJlIGJlY2F1c2UgdGhlIG1vZGVsIGlzIGluY29ycmVjdGx5IHNwZWNpZmllZCAobWlzc2luZyB2YXJpYWJsZXMgcGVyaGFwcywgbm9uLWxpbmVhciByZWxhdGlvbnNoaXBzLCB1bm1vZGVsbGVkIGludGVyYWN0aW9ucyBhbW9uZ3N0IHRoZSB2YXJpYWJsZXMsIGV0Yy4pLgoKQSBwbG90IHRvIGNoZWNrIGZvciBoZXRlcm9za2VkYXN0aWN0eSBpcywKCmBgYHtyfQpwbG90KG1vZGVsMSwgd2hpY2ggPSAxKQpgYGAKClVuZm9ydHVuYXRlbHksIHRoYXQgc2FtZSBjb2RlIHdvbid0IHdvcmsgZm9yIHRoZSBzcGF0aWFsIG1vZGVscyB3ZSBwcm9kdWNlIGJlbG93LiBJbnN0ZWFkLCB3ZSBjYW4gd3JpdGUgYSBmdW5jdGlvbiB0byBwcm9kdWNlIHdoYXQgd2UgbmVlZC4KCmBgYHtyfQpoZXRlcm8ucGxvdCA8LSBmdW5jdGlvbihtb2RlbCkgewogIGZtIDwtIGFzLmNoYXJhY3Rlcihmb3JtdWxhKG1vZGVsKSkKICBwbG90KHJlc2lkdWFscyhtb2RlbCkgfiBmaXR0ZWQobW9kZWwpLAogICAgICAgeGxhYiA9IHBhc3RlKCJGaXR0ZWQgdmFsdWVzXG4iLGZtWzJdLGZtWzFdLGZtWzNdKSwKICAgICAgIHlsYWIgPSAiUmVzaWR1YWxzIikKICBhYmxpbmUoaD0wLCBsdHk9ImRvdHRlZCIpCQogIGxpbmVzKGxvd2VzcyhmaXR0ZWQobW9kZWwpLCByZXNpZHVhbHMobW9kZWwpKSwgY29sPSJyZWQiKQkJCn0KIyBBbmQsIG5vdyB0byBydW4gaXQuLi4KaGV0ZXJvLnBsb3QobW9kZWwxKQpgYGAKClRoZXJlIGFyZSBubyBxdWljayBmaXhlcyBmb3IgdGhlIHZpb2xhdGVkIGFzc3VtcHRpb24gb2YgaW5kZXBlbmRlbnQgYW5kIGlkZW50aWNhbGx5IGRpc3RyaWJ1dGVkIGVycm9ycyAoaGV0ZXJvc2tlZGFzdGljaXR5KS4gVGhlIHZpb2xhdGlvbiBzdWdnZXN0cyB3ZSBjYW5ub3QgdGFrZSBvbiB0cnVzdCB0aGUgc3RhbmRhcmQgZXJyb3JzLCB0LSBhbmQgcC12YWx1ZXMgc2hvd24gdW5kZXIgdGhlIG1vZGVsIHN1bW1hcnkuIEl0IGlzIGxpa2VseSB0aGF0IHRoZSBzdGFuZGFyZCBlcnJvcnMgIGZvciBhdCBsZWFzdCBzb21lIG9mIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzIGhhdmUgYmVlbiB1bmRlci1lc3RpbWF0ZWQgKGJlY2F1c2UgaWYgd2UgaGF2ZSBzcGF0aWFsIGRlcGVuZGVuY2llcyBpbiB0aGUgcmVzaWR1YWxzIHRoZW4gdGhleSBsaWtlbHkgYXJpc2UgZnJvbSBzcGF0aWFsIGRlcGVuZGVuY2llcyBpbiB0aGUgZGF0YSB3aGljaCBpbiB0dXJuIG1lYW4gd2UgaGF2ZSBsZXNzIGRlZ3JlZXMgb2YgZnJlZWRvbSB0aGFuIHdlIHRoaW5rIHdlIGhhdmUpLgoKYGBge3J9CnN1bW1hcnkobW9kZWwxKQpgYGAKCklmIHdlIGhhdmUgZG91YnRzIGFib3V0IHRoaXMgbW9kZWwgYmVjYXVzZSBvZiB0aGUgc3BhdGlhbCBwYXR0ZXJuaW5nIG9mIHRoZSBlcnJvcnMgKGEgcGF0dGVybiB3aGljaCBpcyBsaWtlbHkgYnV0IG5vdCBuZWNlc3NhcmlseSBjYXVzZWQgYnkgdGhlIHBhdHRlcm5pbmcgb2YgdGhlIFkgdmFyaWFibGUpIHRoZW4gd2UgbmVlZCB0byBjb25zaWRlciBvdGhlciBhcHByb2FjaGVzLgoKIyMgU3BhdGlhbCBFY29ub21ldHJpYyBNb2RlbHMKCiMjIyBTcGF0aWFsIEVycm9yIE1vZGVsCgpPbmUgb3B0aW9uIGlzIHRvIGZpdCBhIHNwYXRpYWwgc2ltdWx0YW5lb3VzIGF1dG9yZWdyZXNzaXZlIGVycm9yIG1vZGVsIHdoaWNoIGRlY29tcG9zZXMgdGhlIGVycm9yICh0aGUgcmVzaWR1YWxzKSBpbnRvIHR3byBwYXJ0czogYSBzcGF0aWFsbHkgbGFnZ2VkIGNvbXBvbmVudCBhbmQgYSByZW1haW5pbmcgZXJyb3I6IHk9WM6yK867V86+K861CgpGaXR0aW5nIHRoZSBtb2RlbCBhbmQgY29tcGFyaW5nIGl0IHdpdGggdGhlIHN0YW5kYXJkIHJlZ3Jlc3Npb24gbW9kZWwgd2UgZmluZCB0aGF0IHR3byBvZiB0aGUgcHJlZGljdG9yIHZhcmlhYmxlcyAoREVMRSBhbmQgUE9QREVOKSBubyBsb25nZXIgYXJlIHNpZ25pZmljYW50IGF0IGEgY29udmVudGlvbmFsIGxldmVsIGFuZCB0aGF0IHRoZSBzdGFuZGFyZCBlcnJvcnMgZm9yIG1hbnkgaGF2ZSByaXNlbi4gVGhlIGxhbWJkYSAoYSBtZWFzdXJlIG9mIHNwYXRpYWwgYXV0b2NvcnJlbGF0aW9uKSBpcyBzaWduaWZpY2FudC4gVGhlIG1vZGVsIGZpdHMgdGhlIGRhdGEgYmV0dGVyIHRoYW4gdGhlIHByZXZpb3VzIG1vZGVsOiB0aGUgQUlDIHNjb3JlIGlzICpsb3dlciogYW5kIHRoZSBsb2cgbGlrZWxpaG9vZCB2YWx1ZSAqZ3JlYXRlciosIGFzIGlzIHRoZSBwc2V1ZG8tUjIgdmFsdWU6CgpgYGB7cn0KaWYoISAic3BhdGlhbHJlZyIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkgaW5zdGFsbC5wYWNrYWdlcygic3BhdGlhbHJlZyIpCnJlcXVpcmUoc3BhdGlhbHJlZykKbW9kZWwyIDwtIGVycm9yc2FybG0oTE5QUklDRSB+IERDQkQgKyBERUxFICsgRFJJVkVSICsgRFBBUksgKyBQT1BERU4gKyBKT0JERU4gKyBZMDQwNSArIFkwNjA3ICsgWTA4MDksIGRhdGE9Y29tYmluZWQubWFwLCBsaXN0dyA9IHNwa25lYXIzNWdhdXMpCnN1bW1hcnkobW9kZWwyKQpgYGAKClRoZSBBSUMgaXMgZ2l2ZW4gaW4gdGhlIG1vZGVsIG91dHB1dCBhYm92ZSBidXQgd2UgY2FuIGFsc28gb2J0YWluIGl0IHVzaW5nLAoKYGBge3J9CkFJQyhtb2RlbDEpCkFJQyhtb2RlbDIpCmBgYAoKQXMgYSBydWxlIG9mIHRodW1iLCBhIHJlZHVjdGlvbiBpbiB0aGUgQUlDIHNjb3JlIG9mIHRlbiBvciBtb3JlIGlzIHN0cm9uZyBldmlkZW5jZSBpbiBmYXZvdXIgb2YgdGhlIG1vZGVsIHdpdGggdGhlIGxvd2VyIEFJQyB2YWx1ZSAtLSB3aGljaCBoZXJlIGlzIHRoZSBzcGF0aWFsIGVycm9yIG1vZGVsLiAoWW91IGNhbiB0aGlzIHJ1bGUgb2YgdGh1bWIgYXQgaHR0cHM6Ly9zdGF0cy5zdGFja2V4Y2hhbmdlLmNvbS9xdWVzdGlvbnMvMjgyNjg2L3doYXQtcGVyY2VudGFnZS1kZWNyZWFzZS1pbi1haWMtaXMtc2lnbmlmaWNhbnQpCgpXZSBjYW4gYWxzbyB1c2UgdGhlIGxvZyBsaWtlbGlob29kIHZhbHVlcyB0byBjb21wYXJlIHRoZSBtb2RlbHM6CgpgYGB7cn0KbG9nTGlrKG1vZGVsMSkKbG9nTGlrKG1vZGVsMikKYGBgCgpIZXJlLCBoaWdoZXIgaXMgYmV0dGVyIHNvIHRoZSBldmlkZW5jZSBpcyBhZ2FpbiBpbiBmYXZvdXIgb2YgdGhlIHNwYXRpYWwgZXJyb3IgbW9kZWwuIE1vcmUgZm9ybWFsbHksCgpgYGB7cn0KTFIgPC0gMiAqIChsb2dMaWsobW9kZWwyKSAtIGxvZ0xpayhtb2RlbDEpKSAKICAgICAgIyBUaGlzIGlzIHRoZSBsaWtlbGlob29kIHJhdGlvCmRmY2hnIDwtIGF0dHIobG9nTGlrKG1vZGVsMiksICJkZiIpIC0gYXR0cihsb2dMaWsobW9kZWwxKSwgImRmIikKICAgICAgIyBUaGlzIGlzIHRoZSBkaWZmZXJlbmNlIGluIHRoZSBkZWdyZWVzIG9mIGZyZWVkb20KTFIgPiBxY2hpc3EoMC45OSwgZGYgPSBkZmNoZykKICAgICAgIyBUaGlzIGNvbXBhcmVzIHRoZSBsaWtlbGlob29kIHJhdGlvIHdpdGggYSBjaGktc3F1YXJlCiAgICAgICMgZGlzdHJpYnV0aW9uCmBgYAoKLi4uIHdoaWNoIHBhc3NlcyBhIHN0YXRpc3RpY2FsIHRlc3QgYXQgYSA5OSBwZXIgY2VudCBjb25maWRlbmNlIGluIGZhdm91ciBvZiB0aGUgc3BhdGlhbCBlcnJvciBtb2RlbC4gKElmIHlvdSB3YW50IHRvIHJlYWQgdXAgb24gdGhpcyB0ZXN0OiBodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaWtlbGlob29kLXJhdGlvX3Rlc3QpCgpGaW5hbGx5LCB0aGUgcHNldWRvLVItc3F1YXJlZCBmb3IgdGhlIHNwYXRpYWwgZXJyb3IgbW9kZWwgaXMKCmBgYHtyfQpjb3IoY29tYmluZWQubWFwJExOUFJJQ0UsIGZpdHRlZChtb2RlbDIpKV4yCmBgYAoKd2hpY2ggaXMgaGlnaGVyIHRoYW4gdGhlIFItc3F1YXJlZCB2YWx1ZSBmb3IgdGhlIG9yaWdpbmFsIG1vZGVsOyB0aGF0IHdhcwoKYGBge3J9CnN1bW1hcnkobW9kZWwxKSRyLnNxdWFyZWQKYGBgCgooVGhlIFItc3F1YXJlZCB2YWx1ZSBjYW4gYmUgY2FsY3VsYXRlIGFzIHRoZSBzcXVhcmUgb2YgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIFkgdmFsdWVzIGFuZCB3aGF0IHRoZSBtb2RlbCBwcmVkaWN0cyBmb3IgdGhlbSwgdGhlIGZpdHRlZCB2YWx1ZXMpCgpJbiBzdW1tYXJ5LCB0aGUgc3BhdGlhbCBlcnJvciBtb2RlbCBhcHBlYXJzIHRvIGZpdCB0aGUgZGF0YSBiZXR0ZXIgYWx0aG91Z2ggdGhlIHByb2JsZW0gb2YgaGV0ZXJvc2tlZGFzdGljaXR5IHJlbWFpbnM6CgpgYGB7cn0KaGV0ZXJvLnBsb3QobW9kZWwyKQpgYGAKCiMjIyBBIHNwYXRpYWxseSBsYWdnZWQgeSBtb2RlbAoKQWx0aG91Z2ggdGhlIHNwYXRpYWwgZXJyb3IgbW9kZWwgKGFib3ZlKSBmaXRzIHRoZSBkYXRhIGJldHRlciB0aGFuIHRoZSBzdGFuZGFyZCBPTFMgbW9kZWwsIGl0IHRlbGxzIHVzIG9ubHkgdGhhdCB0aGVyZSBpcyBhbiB1bmV4cGxhaW5lZCBzcGF0aWFsIHN0cnVjdHVyZSB0byB0aGUgcmVzaWR1YWxzLCBub3Qgd2hhdCBjYXVzZWQgdGhlbS4gSXQgbWF5IG9mZmVyIGJldHRlciBlc3RpbWF0ZXMgb2YgdGhlIG1vZGVsIHBhcmFtZXRlcnMgYW5kIHRoZWlyIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBidXQgaXQgZG9lcyBub3QgcHJlc3VwcG9zZSBhbnkgcGFydGljdWxhciBzcGF0aWFsIHByb2Nlc3MgZ2VuZXJhdGluZyB0aGUgcGF0dGVybnMgaW4gdGhlIGxhbmQgcHJpY2UgdmFsdWVzLiBBIGRpZmZlcmVudCBtb2RlbCB0aGF0IGV4cGxpY2l0bHkgdGVzdHMgZm9yIHdoZXRoZXIgdGhlIGxhbmQgdmFsdWUgYXQgYSBwb2ludCBpcyBmdW5jdGlvbmFsbHkgZGVwZW5kZW50IG9uIHRoZSB2YWx1ZXMgb2YgbmVpZ2hib3VyaW5nIHBvaW50cyBpcyB0aGUgc3BhdGlhbGx5IGxhZ2dlZCB5IG1vZGVsOiB5Pc+BV3krWM6yK861LiBJdCBtb2RlbHMgYW4gJ292ZXJzcGlsbCcgb3IgY2hhaW4gZWZmZWN0IHdoZXJlIHRoZSBvdXRjb21lICh0aGUgWSB2YWx1ZSkgYXQgYW55IGxvY2F0aW9uIGlzIGFmZmVjdGVkIGJ5IHRoZSBvdXRjb21lcyBhdCBzdXJyb3VuZGluZyBsb2NhdGlvbnMuCgpUaGUgbW9kZWwgaXMgZml0dGVkIGluIFIgdXNpbmcsCgpgYGB7cn0KbW9kZWwzIDwtIGxhZ3NhcmxtKExOUFJJQ0UgfiBEQ0JEICsgREVMRSArIERSSVZFUiArIERQQVJLICsgUE9QREVOICsgSk9CREVOICsgWTA0MDUgKyBZMDYwNyArIFkwODA5LCBkYXRhPWNvbWJpbmVkLm1hcCwgc3BrbmVhcjM1Z2F1cykKc3VtbWFyeShtb2RlbDMpCmBgYAoKVGhlIG1vZGVsIGlzIGFuIGltcHJvdmVtZW50IG9uIHRoZSBPTFMgbW9kZWwuIEhvd2V2ZXIsIHRoZXJlIGlzIHNpZ25pZmljYW50IGF1dG9jb3JyZWxhdGlvbiByZW1haW5pbmcgaW4gdGhlIHJlc2lkdWFscyAoc2VlIHRoZSBib3R0b20gbGluZXMgb2YgdGhlIG1vZGVsIG91dHB1dCkgYW5kIGl0IGRvZXMgbm90IGFwcGVhciB0byBmaXQgdGhlIGRhdGEgYXMgd2VsbCBhcyB0aGUgZXJyb3IgbW9kZWw6IHRoZSBsYWdnZWQgeSBtb2RlbCBoYXMgYSBncmVhdGVyIEFJQywgbG93ZXIgbG9nIGxpa2VsaWhvb2QgYW5kIGxvd2VyIHBzZXVkby1SMiwgZWFjaCBpbmRpY2F0aW5nIGEgcG9vcmVyIGZpdC4KCmBgYHtyfQpBSUMobW9kZWwzKQpsb2dMaWsobW9kZWwzKQpjb3IoY29tYmluZWQubWFwJExOUFJJQ0UsIGZpdHRlZChtb2RlbDMpKV4yCmBgYAoKVGhlIGhldGVyb3NrZWRhc3RpY2l0eSByZW1haW5zOgoKYGBge3J9CmhldGVyby5wbG90KG1vZGVsMykKYGBgCgoocG90ZW50aWFsbHkgcmVsYXRlZCB0byB0aGUgc2lnbmlmaWNhbnQgYXV0b2NvcnJlbGF0aW9uIGluIHRoZSByZXNpZHVhbHMpLgoKTm90ZSB0aGF0IHRoZSBiZXRhIGVzdGltYXRlcyBvZiB0aGUgbGFnZ2VkIHktbW9kZWwgY2Fubm90IGJlIGludGVycHJldGVkIGluIHRoZSBzYW1lIHdheSBhcyBmb3IgYSBzdGFuZGFyZCBPTFMgbW9kZWwuIEZvciBleGFtcGxlLCB0aGUgYmV0YSBlc3RpbWF0ZSBvZiAwLjAwNCBmb3IgdGhlIFBPUERFTiB2YXJpYWJsZSBkb2VzIG5vdCBtZWFuIHRoYXQgaWYgKGh5cG90aGV0aWNhbGx5KSB3ZSBpbmNyZWFzZWQgdGhhdCB2YXJpYWJsZSBieSBvbmUgdW5pdCBhdCBlYWNoIGxvY2F0aW9uIHdlIHNob3VsZCB0aGVuIGV4cGVjdCB0aGUgKGxvZykgb2YgdGhlIGxhbmQgcGFyY2VsIHRvIGV2ZXJ5d2hlcmUgaW5jcmVhc2UgYnkgMC4wMDQgaG9sZGluZyB0aGUgb3RoZXIgWCB2YXJpYWJsZXMgY29uc3RhbnQuIFRoYXQgaXMgdGhlIGNvcnJlY3QgaW50ZXJwcmV0YXRpb24gZm9yIGEgc3RhbmRhcmQgKE9MUykgcmVncmVzc2lvbiBtb2RlbCBhbmQgYWxzbyBmb3IgdGhlIHNwYXRpYWwgZXJyb3IgbW9kZWwgYnV0IG5vdCBmb3Igd2hlcmUgdGhlIGxhZyBvZiB0aGUgWSB2YXJpYWJsZSBpcyBpbmNsdWRlZCBhcyBhIHByZWRpY3RvciB2YXJpYWJsZS4gVGhlIHJlYXNvbiBpcyBiZWNhdXNlIGlmIHdlIGRpZCByYWlzZSB0aGUgUE9QREVOIHZhbHVlIGl0IHdvdWxkIHN0YXJ0IHNvbWV0aGluZyBha2luIHRvIGEgJ2NoYWluIHJlYWN0aW9uJyB0aHJvdWdoIHRoZSBmZWVkYmFjayBvZiBZIHZpYSB0aGUgbGFnZ2VkIFkgdmFsdWVzLiBUaGUgdG90YWwgaW1wYWN0IGlzIGEgc3VtIG9mIHRoZSBkaXJlY3QgZWZmZWN0IC0tIHdoYXQgaXMgcHJlZGljdGVkIHRvIGhhcHBlbiB0aHJvdWdoIHRoZSAxIHVuaXQgY2hhbmdlIGluIFBPUERFTiAtLSBhbmQgdGhlIGluZGlyZWN0IGVmZmVjdCwgd2hpY2ggaXMgdGhhdCBjYXVzZWQgYnkgJ3RoZSBjaGFpbiByZWFjdGlvbicgLyBmZWVkYmFjayAvICdvdmVyc3BpbGwnIGluIHRoZSBzeXN0ZW06CgpgYGB7cn0KaW1wYWN0cyhtb2RlbDMsIGxpc3R3ID0gc3BrbmVhcjM1Z2F1cykKYGBgCgpJbiBmYWN0LCBhbiBpbmNyZWFzZSBvZiAxIHVuaXQgaW4sIGZvciBleGFtcGxlLCB0aGUgUE9QREVOIHZhcmlhYmxlLCB3aWxsIHBsYXkgb3V0IHNsaWdodGx5IGRpZmZlcmVudGx5IGluIGRpZmZlcmVudCBwbGFjZXMgKGJlY2F1c2UgdGhleSBoYXZlIGRpZmZlcmVudCBuZWlnaGJvdXJzIHdpdGggZGlmZmVyZW50IFkgdmFsdWVzIGFuZCBhdCBkaWZmZXJlbnQgZGlzdGFuY2VzIGZyb20gZWFjaCBvdGhlcikuIFRoZSBjb2RlIGJlbG93LCB3aGljaCBpcyBmcm9tIFdhcmQgJiBHbGVkaXRzY2ggKDIwMDgsIHBwLjQ3KSwgd2lsbCBjYWxjdWxhdGUgdGhlIGltcGFjdCAob2YgYSBvbmUgdW5pdCBjaGFuZ2UgaW4gUE9QREVOKSBhdCBlYWNoIGxvY2F0aW9uIGJ1dCBiZWxvdyBJIGhhdmUgbGltaXRlZCBpdCB0byB0aGUgZmlyc3QgdGVuIGVsc2UgaXQgd2lsbCB0YWtlIGEgbG9uZyB0aW1lIHRvIHJ1bi4KCmBgYHtyfQpuIDwtIG5yb3coY29tYmluZWQubWFwKQpJIDwtIG1hdHJpeCgwLCBucm93PW4sIG5jb2w9bikKZGlhZyhJKSA8LSAxCnJobyA8LSBtb2RlbDMkcmhvCmJldGEgPC0gbW9kZWwzJGNvZWZmaWNpZW50c1siUE9QREVOIl0Kd2VpZ2h0cy5tYXRyaXggPC0gbGlzdHcybWF0KHNwa25lYXIzNWdhdXMpCnJlc3VsdHMgPC0gcmVwKE5BLCB0aW1lcz0xMCkKZm9yIChpIGluIDE6MTApIHsKICB4dmVjdG9yIDwtIHJlcCgwLCB0aW1lcz1uKQogIHh2ZWN0b3JbaV0gPC0gMQogIGltcGFjdCA8LSBzb2x2ZShJIC0gcmhvICogd2VpZ2h0cy5tYXRyaXgpICUqJSB4dmVjdG9yICogYmV0YQogIHJlc3VsdHNbaV0gPC0gaW1wYWN0W2ldCn0KcmVzdWx0cwpgYGAKCiMjIyBDaG9vc2luZyBiZXR3ZWVuIHRoZSBtb2RlbHMgdXNpbmcgTGFncmFuZ2UgTXVsdGlwbGllciAoTE0pIFRlc3RzCgpCZWZvcmUgZml0dGluZyB0aGUgc3BhdGlhbCBlcnJvciBhbmQgbGFnZ2VkIHkgbW9kZWxzIChhYm92ZSksIHdlIGNvdWxkIGhhdmUgbG9va2VkIGZvciBldmlkZW5jZSBpbiBzdXBwb3J0IG9mIHRoZW0gdXNpbmcgdGhlIGZ1bmN0aW9uIGxtLkxNdGVzdHMoLi4uKS4gVGhpcyB0ZXN0cyB0aGUgYmFzaWMgT0xTIHNwZWNpZmljYXRpb24gYWdhaW5zdCB0aGUgbW9yZSBnZW5lcmFsIHNwYXRpYWwgZXJyb3IgYW5kIGxhZ2dlZCB5IG1vZGVscy4gUm9idXN0IHRlc3RzIGFsc28gYXJlIGdpdmVuLiBUaGVyZSBpcyBldmlkZW5jZSBmb3IgYm90aCBvZiB0aGUgc3BhdGlhbCBtb2RlbHMgaW4gZmF2b3VyIG9mIHRoZSBzaW1wbGVyIE9MUyBtb2RlbCBidXQgaXQgaXMgc3Ryb25nZXIgKGluIHB1cmVseSBzdGF0aXN0aWNhbCB0ZXJtcykgZm9yIHRoZSBlcnJvciBtb2RlbC4KCmBgYHtyfQpsbS5MTXRlc3RzKG1vZGVsMSwgc3BrbmVhcjM1Z2F1cywgdGVzdD0iYWxsIikKYGBgCgpBbnNlbGluIGFuZCBSZXkgKDIwMTQsIHAuMTEwKSBvZmZlciB0aGUgZm9sbG93aW5nIGRlY2lzaW9uIHRyZWUgdGhhdCBjYW4sIGluIHRoZSBhYnNlbmNlIG9mIGEgbW9yZSB0aGVvcmV0aWNhbCBiYXNpcyBmb3IgdGhlIG1vZGVsIGNob2ljZSAoZS5nLiB0aGUgdHlwZSBvZiBwcm9jZXNzIGJlaW5nIG1vZGVsbGVkKSwgYmUgdXNlZCBpbiBjb25qdW5jdGlvbiB3aXRoIHRoZSB0ZXN0IHJlc3VsdHMgYWJvdmUgdG8gaGVscCBzZWxlY3QgdGhlIG1vZGVsLgoKYGBge3J9CmJyb3dzZVVSTCgiYW5zZWxpbl9yZXkuanBnIikKYGBgCgpVc2luZyB0aGUgZGVjaXNpb24gdHJlZSwgdGhlcmUgc2VlbXMgdG8gYmUgZ3JlYXRlciBldmlkZW5jZSBpbiBmYXZvdXIgb2YgdGhlIHNwYXRpYWwgZXJyb3IgbW9kZWwgdGhhbiB0aGUgc3BhdGlhbGx5IGxhZ2dlZCB5IG1vZGVsLCBhbHRob3VnaCBwb3RlbnRpYWxseSBib3RoIGFyZSAnYmVhdGVuJyBieSBhIHRoaXJkIG1vZGVsLCB0aGUgc3BhdGlhbCBhdXRvcmVncmVzc2l2ZSBtb3ZpbmcgYXZlcmFnZSAoU0FSTUEpIG1vZGVsISBXZSB3b24ndCBkd2VsbCBvbiB0aGlzIG90aGVyIHRoYW4gdG8gbm90ZSB0aGF0IGl0IGNhbiBhY2NvdW50IGZvciBzcGF0aWFsIGRlcGVuZGVuY2UgYW1vbmcgdGhlIGVycm9yIHRlcm1zIGFzIHdlbGwgYXMgc3BhdGlhbCBkZXBlbmRlbmNlIGluIHRoZSBZIHZhcmlhYmxlLiBUbyBmaXQgaXQgaW4gUjoKCmBgYHtyfQptb2RlbDQgPC0gc3BhdXRvbG0oTE5QUklDRSB+IERDQkQgKyBERUxFICsgRFJJVkVSICsgRFBBUksgKyBQT1BERU4gKyBKT0JERU4gKyBZMDQwNSArIFkwNjA3ICsgWTA4MDksIGRhdGE9Y29tYmluZWQubWFwLCBzcGtuZWFyMzVnYXVzLCBmYW1pbHkgPSAiU01BIikKc3VtbWFyeShtb2RlbDQpCmBgYAoKT24gYmFsYW5jZSBpdCBtYXkgZml0IHRoZSBkYXRhIGxlc3Mgd2VsbCB0aGFuIG1vZGVsIDIgZ2l2ZW4gaXRzIGdyZWF0ZXIgY29tcGxleGl0eSAocmVtZW1iZXIsIGEgbG93ZXIgdmFsdWUgaXMgcHJlZmVycmVkIGZvciBBSUMgc2NvcmVzLCBhbmQgYSBoaWdoZXIgdmFsdWUgZm9yIGxvZyBsaWtlbGlob29kIG9uZXMpOgoKYGBge3J9CmZpdCA8LSBtYXRyaXgobnJvdyA9IDIsIG5jb2wgPSAyKQpmaXRbMSxdIDwtIGMoQUlDKG1vZGVsMiksIEFJQyhtb2RlbDQpKQpmaXRbMixdIDwtIGMobG9nTGlrKG1vZGVsMiksIGxvZ0xpayhtb2RlbDQpKQpjb2xuYW1lcyhmaXQpIDwtIGMoIk1vZGVsMiIsICJNb2RlbDQiKQpyb3duYW1lcyhmaXQpIDwtIGMoIkFJQyIsICJsb2dMaWsiKQpmaXQKYGBgCgojIyMgSW5jbHVkaW5nIGxhZ2dlZCBYIHZhcmlhYmxlcwoKU28gZmFyIHdlIGhhdmUgYWNjb21tb2RhdGVkIHRoZSBzcGF0aWFsIGRlcGVuZGVuY2llcyBpbiB0aGUgZGF0YSBieSBjb25zaWRlcmF0aW9uIHRvIHRoZSBlcnJvciBhbmQgd2l0aCBjb25zaWRlcmF0aW9uIHRvIHRoZSBkZXBlbmRlbnQgKHkpIHZhcmlhYmxlLiBBdHRlbnRpb24gbm93IHR1cm5zIHRvIHRoZSBwcmVkaWN0b3IgdmFyaWFibGVzLiBBbiBleHRlbnNpb24gdG8gdGhlIGxhZ2dlZCB5IG1vZGVsIGlzIHRvIGxhZyBhbGwgdGhlIGluY2x1ZGVkIHggdmFyaWFibGVzIHRvbyAodGhpcyBpcyBzb21ldGltZXMgcmVmZXJyZWQgdG8gYXMgdGhlIFNwYXRpYWwgRHVyYmluIG1vZGVsKQoKYGBge3J9Cm1vZGVsNSA8LSBsYWdzYXJsbShMTlBSSUNFIH4gRENCRCArIERFTEUgKyBEUklWRVIgKyBEUEFSSyArIFBPUERFTiArIEpPQkRFTiArIFkwNDA1ICsgWTA2MDcgKyBZMDgwOSwgZGF0YT1jb21iaW5lZC5tYXAsIHNwa25lYXIzNWdhdXMsIER1cmJpbiA9IFRSVUUpCnN1bW1hcnkobW9kZWw1KQpgYGAKCldoYXQgd2UgZmluZCBpcyB0aGF0IHRoZSBsYWcgb2YgdGhlIGRpc3RhbmNlIHRvIHRoZSBDQkQgKG1lYXN1cmVkIG9uIGEgbG9nIHNjYWxlKSBpcyBzaWduaWZpY2FudCBidXQgbm90ZSB0aGF0IHRoZSBkaXJlY3Rpb24gb2YgdGhlIHJlbGF0aW9uc2hpcCBpcyBkaWZmZXJlbnQgZnJvbSB0aGF0IGZvciB0aGUgb3JpZ2luYWwgRENCRCB2YXJpYWJsZSDigJMgIHRoZSBzaWduIGhhcyByZXZlcnNlZC4gVGhlIHNhbWUgaGFzIGhhcHBlbmVkIHRvIHRoZSBkdW1teSB2YXJpYWJsZSBpbmRpY2F0aW5nIHRoZSBzYWxlIG9mIHRoZSBsYW5kIHBhcmNlbCBpcyBpbiB0aGUgeWVhcnMgMjAwOCB0byAyMDA5LiBUYWtpbmcgdGhlIGZpcnN0IGNhc2UsIHdoYXQgdGhpcyBtYXkgc3VnZ2VzdCBpcyB0aGF0IHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBsYW5kIHByaWNlIHZhbHVlIGFuZCB0aGUgKGxvZyBvZikgZGlzdGFuY2UgdG8gdGhlIENCRCBpcyBub3QgbGluZWFyLiBBZGRpbmcgdGhlIHNxdWFyZSBvZiB0aGlzIHZhcmlhYmxlIHRvIHRoZSBvcmlnaW5hbCBPTFMgbW9kZWwgaW1wcm92ZXMgdGhlIG1vZGVsIGZpdDogdGhlIGRpZmZlcmVuY2Ugb2YgYWJvdXQgNSBpbiB0aGUgQUlDIHNjb3JlcyBpcyBtZWRpdW0gZXZpZGVuY2UgaW4gZmF2b3VyIG9mIHRoZSBuZXcgbW9kZWwuCgpgYGB7cn0KbW9kZWwxYiA8LSB1cGRhdGUobW9kZWwxLCAuIH4gLiArIEkoRENCRF4yKSkKICAgICMgQWRkcyB0aGUgc3F1YXJlIG9mIHRoZSBEQ0JEIHZhcmlhYmxlCkFJQyhtb2RlbDFiKQpBSUMobW9kZWwxKQpMUiA8LSAyICogKGxvZ0xpayhtb2RlbDFiKSAtIGxvZ0xpayhtb2RlbDEpKSAKZGZjaGcgPC0gYXR0cihsb2dMaWsobW9kZWwxYiksICJkZiIpIC0gYXR0cihsb2dMaWsobW9kZWwxKSwgImRmIikKTFIgPiBxY2hpc3EoMC45OSwgZGYgPSBkZmNoZykKYGBgCgpXZSBjb3VsZCBhbHNvIHRyeSBhZGRpbmcgdGhlIHNxdWFyZSBvZiB0aGUgRENCRCB2YXJpYWJsZSB0byB0aGUgc3BhdGlhbCBlcnJvciBtb2RlbDoKCmBgYHtyfQptb2RlbDJiIDwtIHVwZGF0ZShtb2RlbDIsIC4gfiAuICsgSShEQ0JEXjIpKQpBSUMobW9kZWwyYikKQUlDKG1vZGVsMikKTFIgPC0gMiAqIChsb2dMaWsobW9kZWwyYikgLSBsb2dMaWsobW9kZWwyKSkgCmRmY2hnIDwtIGF0dHIobG9nTGlrKG1vZGVsMmIpLCAiZGYiKSAtIGF0dHIobG9nTGlrKG1vZGVsMiksICJkZiIpCkxSID4gcWNoaXNxKDAuOTksIGRmID0gZGZjaGcpCmBgYAoKLi4uIGl0IGlzbid0IGEgc3VjY2Vzcy4gUGVyaGFwcyBhZGRpbmcgdGhlIHNwYXRpYWwgbGFnIG9mIHRoZSBEQ0JEIHZhcmlhYmxlIHdpbGwgd29yayBiZXR0ZXI/CgpgYGB7cn0KbGFnLkRDQkQgPC0gbGFnLmxpc3R3KHNwa25lYXIzNWdhdXMsIGNvbWJpbmVkLm1hcCREQ0JEKQptb2RlbDJjIDwtIHVwZGF0ZShtb2RlbDIsIC4gfiAuICsgbGFnLkRDQkQpCkFJQyhtb2RlbDJjKQpBSUMobW9kZWwyKQpMUiA8LSAyICogKGxvZ0xpayhtb2RlbDJiKSAtIGxvZ0xpayhtb2RlbDIpKSAKZGZjaGcgPC0gYXR0cihsb2dMaWsobW9kZWwyYyksICJkZiIpIC0gYXR0cihsb2dMaWsobW9kZWwyKSwgImRmIikKTFIgPiBxY2hpc3EoMC45OSwgZGYgPSBkZmNoZykKYGBgCgpObywgaXQgZG9lc24ndCEKCiMjIyBJbmNsdWRpbmcgbGFnZ2VkIFggdmFyaWFibGVzIGluIHRoZSBPTFMgbW9kZWwKCldlIGNhbiwgaWYgd2Ugd2lzaCwgaW5jbHVkZSBzcGVjaWZpYyBsYWdnZWQgWCB2YXJpYWJsZXMgaW4gdGhlIE9MUyBtb2RlbC4gVGhlIHByb2Nlc3MgaXMgdG8gY3JlYXRlIHRoZW0gdGhlbiBpbmNsdWRlIHRoZW0gaW4gdGhlIG1vZGVsLiBUaGUgbGFnIG9mIERDQkQgYW5kIHRoZSBsYWcgb2YgWTA4MDkgYXJlIHRoZSBtb3N0IG9idmlvdXMgY2FuZGlkYXRlcyB0byBpbmNsdWRlIChmcm9tIG1vZGVsIDQsIGFib3ZlKS4gVG8gY3JlYXRlIHRoZSBsYWdnZWQgdmFyaWFibGVzIGFuZCBhZGQgdGhlbSB0byB0aGUgbW9kZWw6CgpgYGB7cn0KbGFnLkRDQkQgPC0gbGFnLmxpc3R3KHNwa25lYXIzNWdhdXMsIGNvbWJpbmVkLm1hcCREQ0JEKQpsYWcuWTA4MDkgPC0gbGFnLmxpc3R3KHNwa25lYXIzNWdhdXMsIGNvbWJpbmVkLm1hcCRZMDgwOSkKbW9kZWwxYyA8LSB1cGRhdGUobW9kZWwxLCAufi4gKyBsYWcuRENCRCArIGxhZy5ZMDgwOSkKQUlDKG1vZGVsMWMpCkFJQyhtb2RlbDEpCmBgYAoKVGhlIHJlc3VsdCBzZWVtcyB0byBmaXQgdGhlIGRhdGEgYmV0dGVyIHRoYW4gdGhlIG9yaWdpbmFsIE9MUyBtb2RlbCAoQUlDIHNjb3JlIG9mIDI4NDguMyBWcyAyODY1Ljcg4oCTIHJlbWVtYmVyLCB0aGUgbG93ZXIgdGhlIGJldHRlcikgYnV0IGFjdHVhbGx5IHRoZSBsYWcgb2YgRENCRCBhcHBlYXJzIG5vdCB0byBiZSBzaWduaWZpY2FudCBpbiB0aGlzIG1vZGVsIHdoaWxzdCBzaWduaWZpY2FudCBzcGF0aWFsIGF1dG9jb3JyZWxhdGlvbiBhcHBlYXJzIHRvIHJlbWFpbjoKCmBgYHtyfQpzdW1tYXJ5KG1vZGVsMWMpCmxtLm1vcmFudGVzdChtb2RlbDFjLCBzcGtuZWFyMzVnYXVzKQpgYGAKCiMjIyBUYWtpbmcgU3RvY2sKCkF0IHRoaXMgcG9pbnQsIGl0IGlzIHdvcnRoIGxvb2tpbmcgYmFjayBhbmQgdGFraW5nIHN0b2NrLiBXaGljaCBvZiB0aGUgbW9kZWxzIGFwcGVhcnMgdG8gZml0IHRoZSBkYXRhIGJlc3Q/CgpJZiB5b3VyIGhlYWQgaXMgc3Bpbm5pbmcgYSBiaXQgZG9uJ3Qgd29ycnkgYmVjYXVzZSB0aGF0IHNlcnZlcyB0byBtYWtlIGEgdXNlZnVsIHBvaW50LiBUaGVyZSBhcmUgbG90cyBvZiBtb2RlbHMgdGhhdCBjb3VsZCBiZSBmaXR0ZWQgdG8gdGhlIGRhdGEgYW5kIHdlIGhhdmUgYnkgbm8gbWVhbnMgZXhoYXVzdGVkIHRoZSBwb3NzaWJpbGl0aWVzIGFib3ZlLiBUaGVyZWZvcmUgd2UgbmVlZCBhIG1vZGVsIGZpdHRpbmcgc3RyYXRlZ3kuIFNvbWV0aW1lcyB0aGF0IHdpbGwgYmUgaW5mb3JtZWQgYnkgdGhlb3J5IG9yIGJ5IG90aGVyIHN0dWRpZXMuIE90aGVyd2lzZSwgSSB3b3VsZCBjb25zaWRlciB0aGUgZm9sbG93aW5nOgoKKDEpIE1hcCB0aGUgWSB2YXJpYWJsZSBhbmQgbG9vayBmb3IgYSBzcGF0aWFsIHBhdHRlcm4KKDIpIEZvcm1hbGx5IHRlc3QgZm9yIHNwYXRpYWwgZGVwZW5kZW5jeSBpbiB0aGUgWSB2YXJpYWJsZSBieSBjb25zdHJ1Y3RpbmcgYSBzcGF0aWFsIHdlaWdodHMgbWF0cml4IGFuZCB1c2luZyBnbG9iYWwgYW5kL29yIGxvY2FsaXNlZCBNb3JhbidzIHZhbHVlcwooMykgZm9yICgyKSB5b3Ugd2lsbCBuZWVkIHRvIGdpdmUgY29uc2lkZXJhdGlvbiB0byBob3cgeW91IHNwZWNpZnkgdGhlIHdlaWdodHMgbWF0cml4IC0tIHlvdSBtYXkgd2FudCB0byB0cnkgZGlmZmVyZW50IHdlaWdodGluZyBzY2hlbWVzLiBXaGF0IGRvIHRoZSB0ZWxsIHlvdSBhYm91dCB0aGUgc2NhbGUgb2YgdGhlIHNwYXRpYWwgcGF0dGVybiBpbiB0aGUgZGF0YT8gKElzIGl0IGxvY2FsaXNlZCBjbHVzdGVyaW5nIC0gbG9jYXRpb25zIG9ubHkgcmVsYXRlZCB0byB0aGVpciBtb3N0IGltbWVkaWF0ZSBuZWlnaGJvdXJzIG9yLi4uPykKKDQpIEZpdCB0aGUgT0xTIG1vZGVsIGFuZCBnbyB0aHJvdWdoIHRoZSB1c3VhbCBwcm9jZXNzIG9mIHJlZmluaW5nIHRoYXQgbW9kZWwgKGUuZy4gZHJvcCBpbnNpZ25pZmljYW50IHZhcmlhYmxlcykKKDUpIFRlc3QgdGhlIHJlc2lkdWFscyBmb3IgYSBzcGF0aWFsIHBhdHRlcm4gdXNpbmcgdGhlIE1vcmFuJ3MgdGVzdCBmb3IgcmVncmVzc2lvbiByZXNpZHVhbHMuIAooNikgSWYgdGhlcmUgaXMgYSBwYXR0ZXJuLCBlaXRoZXIgY2hvb3NlIGEgc3BhdGlhbCByZWdyZXNzaW9uIG1vZGVsIG9uIHRoZW9yZXRpY2FsIGdyb3VuZHMgKGJlY2F1c2UgaXQgY2FwdHVyZXMgdGhlIHByb2Nlc3MgeW91IHdhbnQgdG8gbW9kZWwpIG9yIHVzZSB0aGUgTGFncmFuZ2UgbXVsdGlwbGllciB0ZXN0cy4KKDcpIENvbnNpZGVyIGFsc28gdGhlIHBvc3NpYmlsaXR5IHRoYXQgdGhlIHNwYXRpYWwgcGF0dGVybiBleGlzdHMgYmVjYXVzZSB0aGUgT0xTIG1vZGVsIHdhcyBtaXMtc3BlY2lmaWVkIChlLmcuIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBvbmUgb3IgbW9yZSBYcyBhbmQgdGhlIFkgdmFyaWFibGUgaXMgbm9uLWxpbmVhcikuCig4KSBDb21wYXJlIHlvdXIgc3BhdGlhbCByZWdyZXNzaW9uIG1vZGVsIHdpdGggdGhlIHJlc3VsdHMgb2YgYSBHZW9ncmFwaGljYWxseSBXZWlnaHRlZCBSZWdyZXNzaW9uIChzZWUgYmVsb3cpLgoKIyMgR2VvZ3JhcGhpY2FsbHkgV2VpZ2h0ZWQgUmVncmVzc2lvbgoKQW4gYWx0ZXJuYXRpdmUgd2F5IG9mIGF0dGVtcHRpbmcgdG8gZXhwbGFpbiB0aGUgc3BhdGlhbCB2YXJpYXRpb24gaW4gdGhlIGxhbmQgdmFsdWUgcHJpY2VzIGlzIHRvIGFsbG93IHRoZSBlZmZlY3Qgc2l6ZXMgb2YgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMgdG8gdGhlbXNlbHZlcyB2YXJ5IG92ZXIgc3BhY2UuIEZvciB0aGUgcHJldmlvdXMgc3BhdGlhbCByZWdyZXNzaW9uIG1vZGVscyB0aGV5IHdlcmUgZml4ZWQgLS0gdGhlcmUgd2FzIG9uZSBlc3RpbWF0ZSBvZiwgZm9yIGV4YW1wbGUsIHRoZSBlZmZlY3Qgb2YgZGlzdGFuY2UgdG8gdGhlIENCRCBvbiBsYW5kIHByaWNlcy4gQnV0IHdoYXQgaWYgYmVpbmcgY2xvc2UgdG8gdGhlIENCRCBtYXR0ZXJzIG1vcmUgaW4gc29tZSBwbGFjZXMgdGhhbiBpdCBkb2VzIGluIG90aGVycz8KCkdlb2dyYXBoaWNhbGx5IFdlaWdodGVkIFJlZ3Jlc3Npb24gKEdXUikgaXMgYSBtZXRob2Qgd2hlcmUgdGhlIGVzdGltYXRlIG9mIM6yeCBhdCBwb2ludCBsb2NhdGlvbiBpIGlzIG5vdCBzaW1wbHkgdGhlIGdsb2JhbCBlc3RpbWF0ZSBmb3IgYWxsIHBvaW50cyBpbiB0aGUgc3R1ZHkgcmVnaW9uIGJ1dCBhIGxvY2FsIGVzdGltYXRlIGJhc2VkIG9uIHN1cnJvdW5kaW5nIHBvaW50cyB3ZWlnaHRlZCBieSB0aGUgaW52ZXJzZSBvZiB0aGVpciBkaXN0YW5jZSBhd2F5IChoZW5jZSBnZW9ncmFwaGljYWxseSB3ZWlnaHRlZCkuIElmIHRoZXJlIGFyZSAxMDAwIGxvY2F0aW9ucyBpbiBhIHN0dWR5IHJlZ2lvbiB0aGVuIEdXUiB3aWxsIGZpdCAxMDAwIGxvY2FsaXNlZCBtb2RlbHMuIFRoZSByZXN1bHRzIG9mIHRob3NlIG1vZGVscyBhcmUgdGhlbiBjb21wYXJlZCB0byBzZWUgaG93IG11Y2ggdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIHZhcnkgYWNyb3NzIHRoZSBzdHVkeSByZWdpb24uCgojIyMgRml0dGluZyB0aGUgR1dSIG1vZGVsCgpUaGUgc3RhZ2VzIG9mIGZpdHRpbmcgYSBHZW9ncmFwaGljYWxseSBXZWlnaHRlZCBSZWdyZXNzaW9uIG1vZGVsIGFyZSBmaXJzdCB0byBsb2FkIHRoZSBHV1IgbGlicmFyeSwgY2FsY3VsYXRlIGEgZGlzdGFuY2UgbWF0cml4IGNvbnRhaW5pbmcgdGhlIGRpc3RhbmNlcyBiZXR3ZWVuIHBvaW50cyAodGhpcyBpcyBuZWVkZWQgZm9yIHRoZSBpbnZlcnNlIGRpc3RhbmNlIHdlaWdodGluZyksIGNhbGlicmF0ZSB0aGUgYmFuZHdpZHRoIGZvciB0aGUgbG9jYWwgbW9kZWwgZml0dGluZyAob3IgeW91IGNhbiBzcGVjaWZ5IGEgYmFuZHdpZHRoIHlvdXJzZWxmKSwgZml0IHRoZSBtb2RlbCB0aGVuIGxvb2sgZm9yIGV2aWRlbmNlIG9mIHNwYXRpYWwgdmFyaWF0aW9ucyBpbiB0aGUgZXN0aW1hdGVzLgoKRm9yIHRoZSBmaXJzdCBjb3VwbGUgb2Ygc3RhZ2VzOgoKYGBge3J9CmlmKCEgIkdXbW9kZWwiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpIGluc3RhbGwucGFja2FnZXMoIkdXbW9kZWwiKQpyZXF1aXJlKEdXbW9kZWwpCiMgSW5zdGFsbCBhbmQgbG9hZCB0aGUgcGFja2FnZQpkaXN0YW5jZXMgPC0gZ3cuZGlzdChkcC5sb2NhdD1jb29yZGluYXRlcyhjb21iaW5lZC5tYXApKQojIENhbGN1bGF0ZSB0aGUgZGlzdGFuY2VzIGJldHdlZW4gdGhlIHBvaW50cwpgYGAKCkhlcmUgdGhlIGJhbmR3aWR0aCBkZWNyZWFzZXMgdG8gemVybyBhdCB0aGUgMzB0aCBuZWlnaGJvdXIsIGVuY291cmFnaW5nbHkgc2ltaWxhciB0byB0aGUgMzV0aCBuZWlnaGJvdXIgdmFsdWUgd2UgaGF2ZSB1c2VkIGZvciBvdXIgc3BhdGlhbCB3ZWlnaHRzIHRocm91Z2hvdXQgdGhpcyBzZXNzaW9uLiBSYXRoZXIgbGVzcyBlbmNvdXJhZ2luZ2x5LCBpdCBzZWVtcyBzdXJwcmlzaW5nbHkgc2Vuc2l0aXZlIHRvIHRoZSBzaGFwZSBvZiB0aGUga2VybmVsICh0aGUgd2F5IHRoZSB3ZWlnaHRpbmcgdGFwZXJzIHdpdGggZGlzdGFuY2UpLiBGb3IgY29tcGFyaXNvbjoKCmBgYHtyfQpidzIgPC0gYncuZ3dyKExOUFJJQ0UgfiBEQ0JEICsgREVMRSArIERSSVZFUiArIERQQVJLICsgUE9QREVOICsgSk9CREVOICsgWTA0MDUgKyBZMDYwNyArIFkwODA5LCBkYXRhPWNvbWJpbmVkLm1hcCwgYWRhcHRpdmU9VCwgZE1hdD1kaXN0YW5jZXMsIGtlcm5lbCA9ICJiaXNxdWFyZSIpCmJ3MgpgYGAKClRoaXMgc2Vuc2l0aXZpdHkgdG8gdGhlIGNob2ljZSBvZiBrZXJuZWwgZnVuY3Rpb24gaXMgd29ydGggaW52ZXN0aWdhdGluZyBmdXJ0aGVyLiBIb3dldmVyLCB3ZSB3aWxsIHNraXAgb3ZlciBpdCAoISkgYW5kIHVzZSB0aGUgR2F1c3NpYW4gdmVyc2lvbiB0byBuZXh0IGZpdCB0aGUgbW9kZWw6CgpgYGB7cn0KZ3dyLm1vZGVsIDwtIGd3ci5iYXNpYyhMTlBSSUNFIH4gRENCRCArIERFTEUgKyBEUklWRVIgKyBEUEFSSyArIFBPUERFTiArIEpPQkRFTiArIFkwNDA1ICsgWTA2MDcgKyBZMDgwOSwgZGF0YT1jb21iaW5lZC5tYXAsIGFkYXB0aXZlPVQsIGRNYXQ9ZGlzdGFuY2VzLCBidz1idywga2VybmVsID0gImdhdXNzaWFuIikKZ3dyLm1vZGVsCmBgYAoKTG9va2luZyBhdCB0aGUgcmVzdWx0cyB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1vZGVsIGhhcyBhIGJldHRlciBmaXQgdG8gdGhlIGRhdGEgdGhhbiBhbnkgb3RoZXIgZml0dGVkIHRodXMgZmFyIGFsdGhvdWdoIHRoaXMgaXMgcGVyaGFwcyBub3Qgc3VycHJpc2luZyAoYXMgYSBHV1IgaXMgcmVhbGx5IGxvdHMgb2YgbW9kZWxzIGZpdHRlZCBvbmUgYWZ0ZXIgYW5vdGhlciBhY3Jvc3MgdGhlIHN0dWR5IHJlZ2lvbiBpdCBpcyBhbHdheXMgbGlrZWx5IHRvIG91dC1wZXJmb3JtIGEgb25lIHNpemUgZml0cyBhbGwgYXBwcm9hY2gpLiBUaGUgc3VtbWFyeSBvZiB0aGUgR1dSIGVzdGltYXRlcyBpcyBvZiBob3cgdGhlIChsb2NhbCkgYmV0YSBlc3RpbWF0ZXMgdmFyeSBhY3Jvc3MgdGhlIHN0dWR5IHJlZ2lvbiAtIG9mLCBmb3IgZXhhbXBsZSwgaG93IHRoZSBlZmZlY3Qgb2YgZGlzdGFuY2UgdG8gdGhlIENCRCB2YXJpZXMgZnJvbSBvbmUgcGxhY2UgdG8gYW5vdGhlci4gRnJvbSB0aGUgaW50ZXItcXVhcnRpbGUgcmFuZ2UgKHNob3duIGluIHRoZSBvdXRwdXQpLCB0aGUgZWZmZWN0IG9mIGRpc3RhbmNlIHRvIENCRCBvbiBsYW5kIHZhbHVlIHByaWNlcyBpcyB0eXBpY2FsbHkgZm91bmQgdG8gYmUgZnJvbSAtMC42ODIgdG8gLTAuMDMwIChpbiBzb21lIGxvY2F0aW9ucywgYXMgdGhlIGRpc3RhbmNlIGZyb20gdGhlIENCRCBpbmNyZWFzZXMsIHRoZSBsb2cgb2YgdGhlIGxhbmQgcHJpY2UgdmFsdWVzIGRyb3BzIGJ5IDAuNjgyIGJ1dCBpbiBvdGhlcnMgdGhlIGRyb3AgaXMgbXVjaCBsZXNzLCBkcm9wcGluZyBieSAwLjAzMCkuCgpUaGUgZnVuY3Rpb24gbW9udGVjYXJsby5nd3IoLi4uKSB1c2VzIGEgcmFuZG9taXNhdGlvbiBhcHByb2FjaCB0byB1bmRlcnRha2Ugc2lnbmlmaWNhbmNlIHRlc3RpbmcgZm9yIHRoZSB2YXJpYWJpbGl0eSBvZiB0aGUgZXN0aW1hdGVkIGxvY2FsIGJldGEgZXN0aW1hdGVzICh0aGUgcmVncmVzc2lvbiBwYXJhbWV0ZXJzKSAtIGkuZS4gb2YgaG93IHNpZ25pZmljYW50bHkgdGhleSBleGhpYml0IGEgZ2VvZ3JhcGhpY2FsIHZhcmlhdGlvbi4gVGhlIGRlZmF1bHQgbnVtYmVyIG9mIHNpbXVsYXRpb25zIGlzIDk5ICh3aGljaCwgd2l0aCB0aGUgYWN0dWFsIGVzdGltYXRlcywgZ2l2ZXMgMTAwIHNldHMgb2YgdmFsdWVzIGluIHRvdGFsKS4gVGhhdCBpcyBxdWl0ZSBhIGxvdyBudW1iZXIgYnV0IGNhbiBiZSB1c2VkIGZvciBpbGx1c3RyYXRpdmUgcHVycG9zZXMuIEluIHByYWN0aWNlIGl0IHdvdWxkIGJlIGJldHRlciB0byByYWlzZSBpdCB0byA5OTksIHRvIDk5OTkgb3IgZXZlbiBtb3JlLiBFdmVuIHdpdGggOTkgaXQgd2lsbCB0YWtlIGEgbGl0dGxlIHdoaWxlIHRvIHJ1bi4KCmBgYHtyfQptb250ZWNhcmxvLmd3cihMTlBSSUNFIH4gRENCRCArIERFTEUgKyBEUklWRVIgKyBEUEFSSyArIFBPUERFTiArIEpPQkRFTiArIFkwNDA1ICsgWTA2MDcgKyBZMDgwOSwgZGF0YT1jb21iaW5lZC5tYXAsIGFkYXB0aXZlPVQsIGJ3PWJ3LCBrZXJuZWwgPSAiZ2F1c3NpYW4iKQpgYGAKCkFsbCBidXQgb25lIChvciBtYXJnaW5hbGx5IHR3bykgb2YgdGhlIHZhcmlhYmxlcyBhcHBlYXIgdG8gc2hvdyBzaWduaWZpY2FudCBzcGF0aWFsIHZhcmlhdGlvbiAocCA8IDAuMDEpLgoKVGhlIGxvY2FsaXNlZCBlc3RpbWF0ZXMgKHRoZSByZXN1bHQgZnJvbSBlYWNoIGZpdHRpbmcgb2YgdGhlIHJlZ3Jlc3Npb24gbW9kZWwgYXMgaXQgbW92ZXMgYWNyb3NzIHRoZSBzdHVkeSByZWdpb24pIGFyZSBzdG9yZWQgaW4gdGhlIFNwYXRpYWwgRGF0YSBGcmFtZSAoU0RGKToKCmBgYHtyfQpuYW1lcyhnd3IubW9kZWwpCmhlYWQoZ3dyLm1vZGVsJFNERikKYGBgCgpUaGUgZXN0aW1hdGVzIGZvciB0aGUgZWZmZWN0IG9mIGRpc3RhbmNlIHRvIENCRCBvbiBsYW5kIHByaWNlLCBmb3IgZXhhbXBsZSwgYXJlIGZvdW5kZWQgaW4gdGhlIGNvbHVtbiwgZ3dyLm1vZGVsJFNERiREQ0JECgpgYGB7cn0KaGVhZChnd3IubW9kZWwkU0RGJERDQkQpCmBgYAoKU2luY2UgZWFjaCBvbmUgb2YgdGhlc2UgZXN0aW1hdGVzIGNvcnJlc3BvbmRzIHRvIGEgbG9jYXRpb24gaW4gdGhlIG9yaWdpbmFsIG1hcCAoYW5kIGluIHRoZSBzYW1lIG9yZGVyKSwgaXQgaXMgc3RyYWlnaHRmb3J3YXJkIHRvIG1hcCB0aGVtIHVzaW5nIHRoZSBuYXRpdmUgcGxvdCBmdW5jdGlvbnMgZm9yIHNmIG9iamVjdHMgKHNlZSBodHRwczovL3Itc3BhdGlhbC5naXRodWIuaW8vc2YvYXJ0aWNsZXMvc2Y1Lmh0bWwpCgpgYGB7cn0KaWYoISAicmdlb3MiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpIGluc3RhbGwucGFja2FnZXMoInJnZW9zIikKIyBUaGlzIHBhY2thZ2UgaXMgbmVlZGVkIHRvIGNvbnZlcnQgdGhlIHNwIG9iamVjdHMgaW50byBzZgppZighICJ0bWFwIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSBpbnN0YWxsLnBhY2thZ2VzKCJ0bWFwIikKY29tYmluZWQubWFwJERDQkRfYmV0YSA8LSBnd3IubW9kZWwkU0RGJERDQkQKIyBDcmVhdGUgYSBuZXcgdmFyaWFibGUgaW4gdGhlIG1hcCBkYXRhIHdoaWNoIGFyZSBsb2NhbCBHV1IKIyBlc3RpbWF0ZXMgZm9yIHRoZSBEQ0JEIHZhcmlhYmxlCnNmX21hcCA8LSBzdF9hc19zZihjb21iaW5lZC5tYXApCnNmX2Rpc3RyaWN0cyA8LSBzdF9hc19zZihkaXN0cmljdHMpCiMgQ29udmVyc3Rpb24gdG8gc2ltcGxlIGZlYXR1cmVzCnBsb3Qoc2ZfbWFwWyJEQ0JEX2JldGEiXSwgcmVzZXQgPSBGQUxTRSwgcGNoID0gMTksIGNleCA9IDAuOCkKIyBwY2ggY29udHJvbHMgdGhlIHBsb3R0aW5nICdjaGFyYWN0ZXInIC0gc2VlID9wb2ludHMKIyBjZXggaXMgdGhlIGNoYXJhY3RlciBleHBhbnNpb24gKGkuZS4gc2l6ZSkKIyBZb3UgbmVlZCByZXNldCA9IEZBTFNFIHRvIGFkZCBmdXJ0aGVyIGVsZW1lbnRzIHRvIHRoZSBtYXAKcGxvdChzdF9nZW9tZXRyeShzZl9kaXN0cmljdHMpLCBhZGQgPSBUUlVFKQpgYGAKCkhvd2V2ZXIsIG5vdCBhbGwgb2YgdGhvc2UgcmVncmVzc2lvbiBjb2VmZmljaWVudHMgYXJlIG5lY2Vzc2FyaWx5IHNpZ25pZmljYW50LiBUbyBoaWdobGlnaHQgdGhvc2UgdGhhdCB0aGF0IGFyZSAocCA8IDAuMDUpOgoKYGBge3J9CmlmKCEgInRpZHl2ZXJzZSIgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKSkgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKcmVxdWlyZSh0aWR5dmVyc2UpCnNmX21hcCREQ0JEX2JldGFfdHZhbCA8LSBnd3IubW9kZWwkU0RGJERDQkRfVFYKc2ZfbWFwICU+JQogIGZpbHRlcihhYnMoc2ZfbWFwJERDQkRfYmV0YV90dmFsKSA+IDEuOTYpIC0+IHN1Yi5tYXAKcGxvdChzdWIubWFwWyJEQ0JEX2JldGEiXSwgcmVzZXQgPSBGQUxTRSwgcGNoID0gMTksIGNleCA9IDAuOCkKcGxvdChzdF9nZW9tZXRyeShzZl9kaXN0cmljdHMpLCBhZGQgPSBUUlVFKQpgYGAKCkFyZ3VhYmx5IGEgbW9yZSBzdHJpbmdlbnQgZGVmaW5pdGlvbiBvZiBzaWduaWZpY2FuY2Ugc2hvdWxkIGJlIGFwcGxpZWQgYXMgR1dSIHVuZGVydGFrZXMgYSBwcm9jZXNzIG9mIHJlcGVhdCBtb2RlbCBmaXR0aW5nLiBUbyBhZGp1c3QgdGhlIHAtdmFsdWVzIGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIHRlc3RzIHNlZSB0aGUgaGVscCBtZW51LCAgP2d3ci50LmFkanVzdChnd20uT2JqKS4KCiMjIEdlb2dyYXBoaWNhbGx5IFdlaWdodGVkIFN0YXRpc3RpY3MKCkluIGNsb3NpbmcsIGl0IHNob3VsZCBiZSBub3RlZCB0aGF0IEdXUiBpcyBhbiBleHRlbnNpb24gb2YgYSBicm9hZGVyIGNsYXNzIG9mIHN0YXRpc3RpY3MgLS0gZ2VvZ3JhcGhpY2FsbHkgd2VpZ2h0ZWQgc3RhdGlzdGljcy4KCkZvciBleGFtcGxlLCB0byBjYWxjdWxhdGUgZ2VvZ3JhcGhpY2FsbHkgd2VpZ2h0ZWQgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciB0aGUgbGFuZCBwcmljZSB2YXJpYWJsZToKCmBgYHtyfQpnd3N0YXRzIDwtIGd3c3MoY29tYmluZWQubWFwLCB2YXJzID0gIkxOUFJJQ0UiLCBhZGFwdGl2ZT1ULCBidz1idywKICAgICBrZXJuZWwgPSAiZ2F1c3NpYW4iKQpoZWFkKGd3c3RhdHMkU0RGKQpuYW1lcyhnd3N0YXRzJFNERikKYGBgCgpXaGF0IGhhcyBiZWVuIGNhbGN1bGF0ZWQgaXMgdGhlIGdlb2dyYXBoaWNhbGx5IHdlaWdodGVkIG1lYW4gbGFuZCBwcmljZSBhcm91bmQgZWFjaCBsb2NhdGlvbiBhbmQgdGhlIGdlb2dyYXBoaWNhbGx5IHdlaWdodGVkIHN0YW5kYXJkIGRldmlhdGlvbiBhbW9uZ3N0IG90aGVyIHZhbHVlcy4gVGhlIGdlb2dyYXBoaWNhbGx5IHdlaWdodGVkICBtZWRpYW4sIGFuZCBpbnRlcnF1YXJ0aWxlIHJhbmdlIGNvdWxkIGFsc28gYmUgY2FsY3VsYXRlZCAoc2VlID9nd3NzKS4KClRoZSBnZW9ncmFwaGljYWxseSB3ZWlnaHRlZCBzdGFuZGFyZCBkZXZpYXRpb24sIGZvciBleGFtcGxlLCBzaG93cyBob3cgbXVjaCB0aGUgbGFuZCBwcmljZXMgdmFyeSBsb2NhbGx5IGFyb3VuZCBlYWNoIGxvY2F0aW9uLiBXZSBjYW4sIG9mIGNvdXJzZSwgbWFwIHRoZW06CgpgYGB7cn0Kc2ZfbWFwJEdXU0QgPC0gZ3dzdGF0cyRTREYkTE5QUklDRV9MU0QKcGxvdChzZl9tYXBbIkdXU0QiXSwgcmVzZXQgPSBGQUxTRSwgcGNoID0gMTksIGNleCA9IDAuOCkKcGxvdChzdF9nZW9tZXRyeShzZl9kaXN0cmljdHMpLCBhZGQgPSBUUlVFKQpgYGAKClRoZSBhcmVhcyBzaG93biBpbiB5ZWxsb3cgaGF2ZSB0aGUgbW9zdCBsb2NhbCB2YXJpYXRpb24gaW4gdGhlIGxhbmQgcHJpY2VzLgoKWW91IGNhbiAtIGFuZCBzaG91bGQhIC0gcmVhZCBtb3JlIGFib3V0IGdlb2dyYXBoaWNhbGx5IHdlaWdodGVkIHJlZ3Jlc3Npb24gaW4gdGhlIGZ1cnRoZXIgcmVhZGluZyAoc2VlIGJlbG93KS4KCiMjIENvbmNsdXNpb24KCldlIGhhdmUgY292ZXJlZCBhIGxvdCBvZiBncm91bmQgaW4gdGhpcyBzZXNzaW9uLCBkcmF3aW5nIG9uIGEgcmFuZ2Ugb2Ygc3RhdGlzdGljYWwgbWV0aG9kcywgbWFueSBvciBhbGwgb2Ygd2hpY2ggbWF5IGJlIG5ldyB0byB5b3UuIFRoZSBpbXBvcnRhbnQgdGFrZSBob21lIG1lc3NhZ2VzIGFyZToKCihhKSBIb3cgdGhleSBpbmNvcnBvcmF0ZSBzcGF0aWFsIHJlbGF0aW9uc2hpcHMsIGVuY29kZWQgYnkgbWVhbnMgb2YgYSBzcGF0aWFsIChvciBkaXN0YW5jZSkgd2VpZ2h0cyBtYXRyaXggaW50byB0aGVpciBjYWxjdWxhdGlvbnMKKGIpIFRoZSBwcm9jZXNzIG9mIGRldGVjdGluZyBhIHNwYXRpYWwgcGF0dGVybiBpbiBhIHZhcmlhYmxlIG9mIGludGVyZXN0LCBmaXR0aW5nIGEgbm9uLXNwYXRpYWwgbW9kZWwgdG8gZXhwbGFpbiBpdCBhbmQgdGhlbiB0cnlpbmcgc29tZSBzcGF0aWFsIHZlcnNpb25zIGluc3RlYWQuCgpJdCBpcyAoYikgdGhhdCB3aWxsIGZvcm0gdGhlIGJhc2lzIGZvciB5b3VyIGZvcnRoY29taW5nIGFzc2lnbm1lbnQuCgojIyBGdXRoZXIgUmVhZGluZwoKR29sbGluaSBJLCBMdSBCLCBDaGFybHRvbiBNLCBCcnVuc2RvbiBDICYgSGFycmlzIFAgKDIwMTUpIFtHV21vZGVsOiBBbiBSIFBhY2thZ2UgZm9yIEV4cGxvcmluZyBTcGF0aWFsIEhldGVyb2dlbmVpdHkgVXNpbmcgR2VvZ3JhcGhpY2FsbHkgV2VpZ2h0ZWQgTW9kZWxzXShodHRwczovL3d3dy5qc3RhdHNvZnQub3JnL2luZGV4LnBocC9qc3MvYXJ0aWNsZS92aWV3L3YwNjNpMTcvdjYzaTE3LnBkZikuIEpvdXJuYWwgb2YgU3RhdGlzdGljYWwgU29mdHdhcmUsIDYzIChqdXN0IGZvY3VzIG9uIHRoZSBzZWN0aW9ucyBhYm91dCBnZW9ncmFwaGljYWxseSB3ZWlnaHRlZCBzdW1tYXJ5IHN0YXRpc3RpY3MgYW5kIGRvbid0IHdvcnJ5IHRvbyBtdWNoIGFib3V0IHRoZSBjb2RlIHVzZWQgdG8gbWFwIHRoZSByZXN1bHRzIGFzIGl0IGlzIGEgYml0IGRhdGVkKQoKIyMgUmVmZXJlbmNlcwoKQW5zZWxpbiBMICYgUmV5IFNKICgyMDE0KSBNb2Rlcm4gU3BhdGlhbCBFY29ub21ldHJpY3MgaW4gUHJhY3RpY2U6IEEgR3VpZGUgdG8gR2VvRGEsIEdlb0RhU3BhY2UgYW5kIFB5U0FMLiBDaGljYWdvOiBHZW9EYSBQcmVzcy4KCldhcmQgTSAmIEdsZWRpdHNjaGUgS1MgKDIwMDgpIFNwYXRpYWwgUmVncmVzc2lvbiBNb2RlbHMuIExvbmRvbjogU2FnZS4KCg==