Jump to Lab Quesions
Data Reading
In this lab we are looking a spatial interaction data.
We will start by reading in a spatial dataset for all municipalities in the Netherlands in geojson format.
munis <- st_read("gem_2016.geojson",crs = 28992)
We do not need parts of the municipalities that are water or sea, so we can filter them out here
munis <- munis %>%
filter(WATER == 'NEE') %>%
select(GM_CODE, GM_NAAM)
There are a lot of municipalities in the Netherlands. In this lab, we will be focusing on the province of Utrecht. To do so, the goal is to load in a dataset of the provinces and select only those municipalities that fall within this province.
Here, we transform the provinces.geojson into the proper coordinate reference system (CRS), which in this case is the Dutch national CRS (28992)
Then we filter out the provinces dataset such that we only have the data belonging to the province of Utrecht
Lastly, we filter out the information from the munis dataset such that we only have data relating to the Utrecht province
* This is done by using st_centroid to get the geometric center of the munis geometries
* Then st_intersects is used to find out which points in munis intersects with the utrecht provices
* We filter those information out and store in back in the munis dataframe.
provinces <- st_read("provinces.geojson") %>%
st_transform(crs = 28992)
utrecht <- provinces %>%
filter(name == 'Utrecht')
munis <- munis %>%
filter(st_intersects(utrecht,st_centroid(munis),sparse =F))
Now we have a nice, clean dataset for municipalities.
Next, we read in a table with information on the number of commuters in between each municipality
commute <- read_csv("commuting.csv") %>%
select(source,sink,weight) %>%
rename(interaction = weight)
Then, we filter the table to match the municipalities in Utrecht
To do this we first need to make a new column and extract out the munis id from the GM_Code and then do the filter
munis <- munis %>%
mutate(id = as.numeric(str_replace(GM_CODE,"GM","")))
commute <- commute %>%
filter(source %in% munis$id & sink %in% munis$id)
From Points to Lines
We now have a clean table of commuting relationshsips.
To create a simple plot that draws a line for each of these relations between the municipalities, we need to do a few things:
1. Find the centroid for each municipality
2. Join both the source and the sink centroid to our table
3. Create a line feature based on these two points/centroids
munis.centroid <- st_centroid(munis) %>%
select(id)
commute <- commute %>%
left_join(munis.centroid, by = c("source" = "id")) %>%
left_join(munis.centroid, by = c("sink" = "id")) %>%
rowwise() %>%
mutate(geometry = st_combine(c(geometry.x,geometry.y)) %>% st_cast("LINESTRING")) %>%
select(-geometry.x,-geometry.y) %>% st_as_sf(crs = 28992)
Now that we have a table with commuting relations that is enhance with a spatial line string for each relation, we can use this to plot them!
ggplot(commute) + geom_sf() + labs(title = "Plot of Spacial Relations of commutes \n")

We also know the number of people commuting over each line so that would be a useful variable to bind to the width of the line.
We will also filter out some of the smaller connections and commuters that never leave their own municipality.
commute <- commute %>%
filter(sink != source) %>%
filter(interaction >20) %>%
mutate(lineWidth = interaction / max(interaction) * 10)
ggplot(commute) + geom_sf(aes(size = lineWidth)) + scale_size_identity() + labs(title = "Plot of Spacial Relations of commutes", subtitle="With interaction corresponding to line width")

The pattern of the lines show that there are lots of people travelling between certain regions. These regions are most likely the major municipalities within Utrecht. It seems that there are multiple large employment centres in the area.
Modelling Spatial Relations
Now that we can plot the actual number of commuters, let us see if we can model the commuting relationship based on some other information.
In this case, we care going to use the distance between municipalities, the number of residents, and the number of jobs in each municipality.
residents <- read_csv("residents.csv") %>%
select(id, weight)
jobs <- read_csv("jobs.csv") %>%
select(id, weight)
We can create a distance matrix from the centroids we created earlier.
dist <- st_distance(x = munis.centroid, y = munis.centroid)
We need to reformat the resulting distance matrix to a table with three columns (from, to and the distance between those points)
rownames(dist) <- munis.centroid$id
colnames(dist) <- munis.centroid$id
dist <- list(
source = rownames(dist)[row(dist)] %||% row(dist),
sink = colnames(dist)[col(dist)] %||% col(dist),
distance = dist
) %>%
map_dfc(as.vector) %>%
mutate(source = as.numeric(source)) %>%
mutate(sink = as.numeric(sink))
Now we join them together
commute <- commute %>%
left_join(dist) %>%
left_join(residents, by = c('source' = 'id')) %>%
rename(residents = weight) %>%
left_join(jobs,by = c('sink'='id')) %>%
rename(jobs=weight)
Let’s do a visual inspection to see if everything went well.
Comparing the number of commuters and the distance:
ggplot(commute,aes(interaction,distance))+
geom_point()+
labs(title="Visual comparision of number of commuters vs distance\n")

It looks like the relationship is not linear, perhaps a log-log plot would help:
ggplot(commute,aes(log(interaction),log(distance)))+
geom_point()+
labs(title="Log-log plot of number of commuters vs distance\n")

A naive linear model based on the available information can be estimated using an OLS regression
lm(data=commute, formula = interaction ~ residents+jobs+distance) %>% summary()
Call:
lm(formula = interaction ~ residents + jobs + distance, data = commute)
Residuals:
Min 1Q Median 3Q Max
-15770 -1150 -309 922 40734
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 2.553e+03 5.241e+02 4.872 1.47e-06 ***
residents 3.234e-02 3.423e-03 9.449 < 2e-16 ***
jobs 9.666e-02 4.960e-03 19.486 < 2e-16 ***
distance -1.965e-01 2.255e-02 -8.713 < 2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 4632 on 515 degrees of freedom
Multiple R-squared: 0.5032, Adjusted R-squared: 0.5003
F-statistic: 173.9 on 3 and 515 DF, p-value: < 2.2e-16
The summary above shows that the model does not really fit very well. Not only does if have a low adjusted \(R^2\) of 0.5003, the p-values for all parameters and the regression itself is very small.
Lets look at the distribution of the commuters
ggplot(commute,aes(interaction))+
geom_histogram()+
labs(title="Distribution of the interactions of commuters\n")

It looks like it follows the Poisson distribution quite closely.
From the monday’s lecture, in spatial interaction modelling we generally thinkg of spatial interactions as:
\(T_{ij} = k\frac{V^\mu_i W^\alpha_j}{d_{ij}^\beta}\)
or rewritten:
\(T_{ij}= kV^\mu_iW^\alpha_jd^{-\beta}_{ij}\)
we can rewrite this again to estimate it with linear estimators:
\(ln T_{ij} = k + \mu ln V_i + \alpha ln W_j - \beta ln d_{ij}\)
model <- glm(data = commute,formula = interaction ~log(residents) + log(jobs) + log (distance), family = poisson())
GLM doesn’t provide \(r^2\) by default so we make our own function
r2 <- function(empirical, fitted){
return(cor(empirical, fitted)^2)
}
r2(commute$interaction,fitted(model))
[1] 0.8639754
The \(R^2\) value is pretty close to 1 now, this means that the model is a much better fit now.
Let’s see if we can interpret the rest of the model information.
tidy(model)
All coefficients have high test-statistic values and a p-value of 0. This means that they are statistically significant. Our model is:
\(Interaction = \beta_0 + \beta_1 log(residents) + \beta_2 log(jobs) + \beta_3 log(distance)\)
\(= Interaction = 3.357 + 0.8585 log(residents) + 0.9836 log(jobs) - 1.5120 log(distance)\)
Adding the fitted values, as well as their residuals back to the original data table so we can (visually) inspect them.
commute <- commute %>%
mutate(fitted = fitted(model)) %>%
mutate(residual = interaction - fitted) %>%
mutate(residualSign = sign(residual))
commute %>%
mutate(lineWidth = fitted / max(fitted) * 10) %>%
ggplot() + geom_sf(aes(size = lineWidth))+
scale_size_identity()+
labs(title = "Plot of Model of Spacial Relations of Commutes", subtitle="With model values corresponding to line width")

Comparing this with the earlier plot of the actual values, they are actually pretty accurate. Given some lineWidths are different in absolute size but the difference in the lineWidths all correspond to the actual values.

For residuals, they can be positive or negative, but since line widths can only be positive, we will use the color aesthetic to indicate whether the residual is positive or negative:
commute %>%
mutate(lineWidth = abs(residual) / max(residual) * 10) %>%
ggplot() +geom_sf(aes(size=lineWidth,color = factor(residualSign))) +
scale_size_identity()+
labs(title = "Plot of residuals of models", subtitle="With residual values corresponding to line width",color="Residual Sign")

Back to top
Lab Questions
Create a plot visualizing all commuting relations in the Netherlands
(use a decent filter for this, for example only showing lines with more than 750 commuters)

Create a summary table for the destinations of commuting in the Netherlands
(Tip: use the group_by() and summarize() functions. Which municipalities have the top 3 of most incoming commuters?)
(Tip: the names of the municipalities are contained in the ‘munis’ object – you can join them to your summary table for easier reading.)
Summary table for the destinations of commuting in the Netherlands
nethertable <- nether_commute%>%
filter(sink != source) %>%
group_by(sink) %>%
summarise(inc_commuters = sum(interaction)) %>%
rename(destination = sink)
nethertable
Simple feature collection with 390 features and 2 fields
geometry type: MULTILINESTRING
dimension: XY
bbox: xmin: 24537.79 ymin: 309545.2 xmax: 271400.9 ymax: 612030
epsg (SRID): 28992
proj4string: +proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.2369,50.0087,465.658,-0.406857,0.350733,-1.87035,4.0812 +units=m +no_defs
adding names by joining municipalities and sorting by incoming commuters
nether_munis= data.frame(id = netherlands$id,name=netherlands$GM_NAAM)
nether_commute_name <- nethertable %>%
left_join(nether_munis,by = c('destination'='id'))
nether_commute_name[order(-nether_commute_name$inc_commuters),]
Simple feature collection with 390 features and 3 fields
geometry type: MULTILINESTRING
dimension: XY
bbox: xmin: 24537.79 ymin: 309545.2 xmax: 271400.9 ymax: 612030
epsg (SRID): 28992
proj4string: +proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.2369,50.0087,465.658,-0.406857,0.350733,-1.87035,4.0812 +units=m +no_defs
The municipalities which have the top 3 of most incoming commuters are Amsterdam,Rotterdam and Utrecht.
Run a spatial interaction model for the data for the entire country.
What’s the model fit (R2)?
#create a distance matrix from the centroids
dist_all <- st_distance(x = netherlands.centroid, y = netherlands.centroid)
#reformat the resulting distance matrix to a table with three columns
rownames(dist_all) <- netherlands.centroid$id
colnames(dist_all) <- netherlands.centroid$id
dist_all <- list(
source = rownames(dist_all)[row(dist_all)] %||% row(dist_all),
sink = colnames(dist_all)[col(dist_all)] %||% col(dist_all),
distance = dist_all
) %>%
map_dfc(as.vector) %>%
mutate(source = as.numeric(source)) %>%
mutate(sink = as.numeric(sink))
#join
nether_commute_all <- nether_commute %>%
left_join(dist_all) %>%
left_join(residents, by = c('source' = 'id')) %>%
rename(residents = weight) %>%
left_join(jobs,by = c('sink'='id')) %>%
rename(jobs=weight)
nether_commute_all <- nether_commute_all%>%
filter(sink != source) %>%
filter(interaction >0)
model2 <- glm(data = nether_commute_all,formula = interaction ~log(residents) + log(jobs) + log (distance), family = poisson())
summary(model2)
Call:
glm(formula = interaction ~ log(residents) + log(jobs) + log(distance),
family = poisson(), data = nether_commute_all)
Deviance Residuals:
Min 1Q Median 3Q Max
-1521.30 -15.76 -10.89 -5.03 439.49
Coefficients:
Estimate Std. Error z value Pr(>|z|)
(Intercept) 8.0928051 0.0032170 2516 <2e-16 ***
log(residents) 0.6382339 0.0001860 3432 <2e-16 ***
log(jobs) 0.8232155 0.0001391 5917 <2e-16 ***
log(distance) -1.6026525 0.0001715 -9346 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
(Dispersion parameter for poisson family taken to be 1)
Null deviance: 111112446 on 43191 degrees of freedom
Residual deviance: 24988156 on 43188 degrees of freedom
AIC: 25239767
Number of Fisher Scoring iterations: 5
r2(nether_commute_all$interaction,fitted(model2))
[1] 0.09636535
The model fit for R2 is Interactions = 8.0928051 + 0.6382339log(residents) + 0.8232155log(jobs) -1.6026525log(distance).
With an \(R^2\) value of 0.09636535.
Can you explain the different fit compared to the model for Utrecht?
Include the model parameters in your notebook and explain what the estimates of the coefficients mean.
This model is a weaker fit with an \(R^2\) value of 0.09636535 (vs Utrecht’s 0.8639754). This is possibly due to having such a large dataset which could contain large number of outliers, influencing the model. Other factors which are not accounted for could also have influenced the interactions when looking at it from a nationwide perspective versus within a municipality.
Look at the residuals and describe where you see the largest residuals.
Another way to look at this is to go back to your table of municipalities with most incoming commuters and compare the top 3 with the top 3 based on the fitted values.
#combining the model values with the table
nether_commute_all <- nether_commute_all %>%
mutate(fitted = fitted(model2)) %>%
mutate(residual = interaction - fitted) %>%
mutate(residualSign = sign(residual))
#Summary table for the destinations
#By Residuals
nethertable2 <- nether_commute_all%>%
filter(sink != source) %>%
group_by(sink) %>%
summarise(residual = sum(residual)) %>%
rename(destination = sink)
nether_commute_name2 <- nethertable2 %>%
left_join(nether_munis,by = c('destination'='id'))
nether_commute_name2[order(-nether_commute_name2$residual),]
Simple feature collection with 390 features and 3 fields
geometry type: MULTILINESTRING
dimension: XY
bbox: xmin: 24537.79 ymin: 309545.2 xmax: 271400.9 ymax: 612030
epsg (SRID): 28992
proj4string: +proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.2369,50.0087,465.658,-0.406857,0.350733,-1.87035,4.0812 +units=m +no_defs
The largest residuals are Amsterdam,Groningen, and Utrecht
Which municipality has risen to the top?
Groningen has replaced Rotterdam as 2nd place. Amsterdan and Utrecht still take 1st and 3rd respectively. In fact, Rotterndam is in last(599th) place with the largest negative residual. Meaning that the model severely underestimated the commutes to Rotterdam. Converse
Can you think of an explanation?
Groningen, though the capital city of the Groningen Province, does not actually have much commutes to the area for its given jobs, distance and residents as it is actually a university city, with close to a fourth of its population being students. Hence, the model overestimates the interaction by a large extent.
Rotterdam on the other hand, is a sea port much like Singapore and is actually the largest port in Europe (source: wikipedia). This means that there is another factor which accounts for commutes to the area which is not accounted for by our model - the large number of ships arriving at port in Rotterdam!
LS0tDQp0aXRsZTogIkxhYiAxMDogU3BhdGlhbCBJbnRlcmFjdGlvbiBNb2RlbGluZyBpbiBSIg0KYXV0aG9yOiAiSnVzdGluaWFuIFNpYWgiDQpkYXRlOiAiNSBBcHJpbCAyMDE4Ig0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCltKdW1wIHRvIExhYiBRdWVzaW9uc10oI0xhYlEpeyN0b3B9DQoNCiNEYXRhIFJlYWRpbmcNCmBgYHtyIHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRSxlY2hvPUZBTFNFfQ0KI0xvYWQgbGlicmFyaWVzDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeShkZXZ0b29scykNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShicm9vbSkNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoY293cGxvdCkNCmBgYA0KICANCkluIHRoaXMgbGFiIHdlIGFyZSBsb29raW5nIGEgc3BhdGlhbCBpbnRlcmFjdGlvbiBkYXRhLiAgDQpXZSB3aWxsIHN0YXJ0IGJ5IHJlYWRpbmcgaW4gYSBzcGF0aWFsIGRhdGFzZXQgZm9yIGFsbCBtdW5pY2lwYWxpdGllcyBpbiB0aGUgTmV0aGVybGFuZHMgaW4gZ2VvanNvbiBmb3JtYXQuICAgDQpgYGB7ciBjYWNoZT1UUlVFLHJlc3VsdHM9J2hpZGUnLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCm11bmlzIDwtIHN0X3JlYWQoImdlbV8yMDE2Lmdlb2pzb24iLGNycyA9IDI4OTkyKQ0KYGBgDQogIA0KV2UgZG8gbm90IG5lZWQgcGFydHMgb2YgdGhlIG11bmljaXBhbGl0aWVzIHRoYXQgYXJlIHdhdGVyIG9yIHNlYSwgc28gd2UgY2FuIGZpbHRlciB0aGVtIG91dCBoZXJlICANCmBgYHtyIHJlc3VsdHM9J2hpZGUnLHdhcm5pbmc9RkFMU0V9DQptdW5pcyA8LSBtdW5pcyAlPiUNCiAgZmlsdGVyKFdBVEVSID09ICdORUUnKSAlPiUNCiAgc2VsZWN0KEdNX0NPREUsIEdNX05BQU0pDQpgYGANCiAgDQpUaGVyZSBhcmUgYSBsb3Qgb2YgbXVuaWNpcGFsaXRpZXMgaW4gdGhlIE5ldGhlcmxhbmRzLiBJbiB0aGlzIGxhYiwgd2Ugd2lsbCBiZSBmb2N1c2luZyBvbiB0aGUgcHJvdmluY2Ugb2YgVXRyZWNodC4gVG8gZG8gc28sIHRoZSBnb2FsIGlzIHRvIGxvYWQgaW4gYSBkYXRhc2V0IG9mIHRoZSBwcm92aW5jZXMgYW5kIHNlbGVjdCBvbmx5IHRob3NlIG11bmljaXBhbGl0aWVzIHRoYXQgZmFsbCB3aXRoaW4gdGhpcyBwcm92aW5jZS4gIA0KSGVyZSwgd2UgdHJhbnNmb3JtIHRoZSBwcm92aW5jZXMuZ2VvanNvbiBpbnRvIHRoZSBwcm9wZXIgY29vcmRpbmF0ZSByZWZlcmVuY2Ugc3lzdGVtIChDUlMpLCB3aGljaCBpbiB0aGlzIGNhc2UgaXMgdGhlIER1dGNoIG5hdGlvbmFsIENSUyAoMjg5OTIpICANClRoZW4gd2UgZmlsdGVyIG91dCB0aGUgcHJvdmluY2VzIGRhdGFzZXQgc3VjaCB0aGF0IHdlIG9ubHkgaGF2ZSB0aGUgZGF0YSBiZWxvbmdpbmcgdG8gdGhlIHByb3ZpbmNlIG9mIFV0cmVjaHQgIA0KTGFzdGx5LCB3ZSBmaWx0ZXIgb3V0IHRoZSBpbmZvcm1hdGlvbiBmcm9tIHRoZSBtdW5pcyBkYXRhc2V0IHN1Y2ggdGhhdCB3ZSBvbmx5IGhhdmUgZGF0YSByZWxhdGluZyB0byB0aGUgVXRyZWNodCBwcm92aW5jZSAgICANCiAgICAqIFRoaXMgaXMgZG9uZSBieSB1c2luZyBzdF9jZW50cm9pZCB0byBnZXQgdGhlIGdlb21ldHJpYyBjZW50ZXIgb2YgdGhlIG11bmlzIGdlb21ldHJpZXMgICANCiAgICAqIFRoZW4gc3RfaW50ZXJzZWN0cyBpcyB1c2VkIHRvIGZpbmQgb3V0IHdoaWNoIHBvaW50cyBpbiBtdW5pcyBpbnRlcnNlY3RzIHdpdGggdGhlIHV0cmVjaHQgcHJvdmljZXMgIA0KICAgICogV2UgZmlsdGVyIHRob3NlIGluZm9ybWF0aW9uIG91dCBhbmQgc3RvcmUgaW4gYmFjayBpbiB0aGUgbXVuaXMgZGF0YWZyYW1lLiAgICANCiAgICAgIA0KYGBge3IgcmVzdWx0cz0naGlkZScsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KcHJvdmluY2VzIDwtIHN0X3JlYWQoInByb3ZpbmNlcy5nZW9qc29uIikgJT4lIA0KICBzdF90cmFuc2Zvcm0oY3JzID0gMjg5OTIpDQoNCnV0cmVjaHQgPC0gcHJvdmluY2VzICU+JSANCiAgZmlsdGVyKG5hbWUgPT0gJ1V0cmVjaHQnKQ0KDQptdW5pcyA8LSBtdW5pcyAlPiUgDQogIGZpbHRlcihzdF9pbnRlcnNlY3RzKHV0cmVjaHQsc3RfY2VudHJvaWQobXVuaXMpLHNwYXJzZSA9RikpDQpgYGANCiAgDQpOb3cgd2UgaGF2ZSBhIG5pY2UsIGNsZWFuIGRhdGFzZXQgZm9yIG11bmljaXBhbGl0aWVzLiAgDQpOZXh0LCB3ZSByZWFkIGluIGEgdGFibGUgd2l0aCBpbmZvcm1hdGlvbiBvbiB0aGUgbnVtYmVyIG9mIGNvbW11dGVycyBpbiBiZXR3ZWVuIGVhY2ggbXVuaWNpcGFsaXR5ICANCmBgYHtyIHJlc3VsdHM9J2hpZGUnLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCmNvbW11dGUgPC0gIHJlYWRfY3N2KCJjb21tdXRpbmcuY3N2IikgJT4lIA0KICBzZWxlY3Qoc291cmNlLHNpbmssd2VpZ2h0KSAlPiUgDQogIHJlbmFtZShpbnRlcmFjdGlvbiA9IHdlaWdodCkNCmBgYA0KICANClRoZW4sIHdlIGZpbHRlciB0aGUgdGFibGUgdG8gbWF0Y2ggdGhlIG11bmljaXBhbGl0aWVzIGluIFV0cmVjaHQgIA0KVG8gZG8gdGhpcyB3ZSBmaXJzdCBuZWVkIHRvIG1ha2UgYSBuZXcgY29sdW1uIGFuZCBleHRyYWN0IG91dCB0aGUgbXVuaXMgaWQgZnJvbSB0aGUgR01fQ29kZSBhbmQgdGhlbiBkbyB0aGUgZmlsdGVyICANCmBgYHtyIHJlc3VsdHM9J2hpZGUnLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCm11bmlzIDwtIG11bmlzICU+JSANCiAgbXV0YXRlKGlkID0gYXMubnVtZXJpYyhzdHJfcmVwbGFjZShHTV9DT0RFLCJHTSIsIiIpKSkNCg0KY29tbXV0ZSA8LSBjb21tdXRlICU+JSANCiAgZmlsdGVyKHNvdXJjZSAlaW4lIG11bmlzJGlkICYgc2luayAlaW4lIG11bmlzJGlkKQ0KYGBgDQoNCioqKiAgDQoNCiNGcm9tIFBvaW50cyB0byBMaW5lcyAgDQpXZSBub3cgaGF2ZSBhIGNsZWFuIHRhYmxlIG9mIGNvbW11dGluZyByZWxhdGlvbnNoc2lwcy4gIA0KVG8gY3JlYXRlIGEgc2ltcGxlIHBsb3QgdGhhdCBkcmF3cyBhIGxpbmUgZm9yIGVhY2ggb2YgdGhlc2UgcmVsYXRpb25zIGJldHdlZW4gdGhlIG11bmljaXBhbGl0aWVzLCB3ZSBuZWVkIHRvIGRvIGEgZmV3IHRoaW5nczogIA0KMS4gRmluZCB0aGUgY2VudHJvaWQgZm9yIGVhY2ggbXVuaWNpcGFsaXR5ICANCjIuIEpvaW4gYm90aCB0aGUgc291cmNlIGFuZCB0aGUgc2luayBjZW50cm9pZCB0byBvdXIgdGFibGUgIA0KMy4gQ3JlYXRlIGEgbGluZSBmZWF0dXJlIGJhc2VkIG9uIHRoZXNlIHR3byBwb2ludHMvY2VudHJvaWRzICANCg0KYGBge3IgcmVzdWx0cz0naGlkZScsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KbXVuaXMuY2VudHJvaWQgPC0gIHN0X2NlbnRyb2lkKG11bmlzKSAlPiUgDQogIHNlbGVjdChpZCkNCg0KY29tbXV0ZSA8LSBjb21tdXRlICU+JSANCiAgbGVmdF9qb2luKG11bmlzLmNlbnRyb2lkLCBieSA9IGMoInNvdXJjZSIgPSAiaWQiKSkgJT4lIA0KICBsZWZ0X2pvaW4obXVuaXMuY2VudHJvaWQsIGJ5ID0gYygic2luayIgPSAiaWQiKSkgJT4lIA0KICByb3d3aXNlKCkgJT4lIA0KICBtdXRhdGUoZ2VvbWV0cnkgPSBzdF9jb21iaW5lKGMoZ2VvbWV0cnkueCxnZW9tZXRyeS55KSkgJT4lICBzdF9jYXN0KCJMSU5FU1RSSU5HIikpICU+JSANCiAgc2VsZWN0KC1nZW9tZXRyeS54LC1nZW9tZXRyeS55KSAlPiUgc3RfYXNfc2YoY3JzID0gMjg5OTIpDQpgYGANCiAgDQogIE5vdyB0aGF0IHdlIGhhdmUgYSB0YWJsZSB3aXRoIGNvbW11dGluZyByZWxhdGlvbnMgdGhhdCBpcyBlbmhhbmNlIHdpdGggYSBzcGF0aWFsIGxpbmUgc3RyaW5nIGZvciBlYWNoIHJlbGF0aW9uLCB3ZSBjYW4gdXNlIHRoaXMgdG8gcGxvdCB0aGVtIQ0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoY29tbXV0ZSkgKyBnZW9tX3NmKCkgKyBsYWJzKHRpdGxlID0gIlBsb3Qgb2YgU3BhY2lhbCBSZWxhdGlvbnMgb2YgY29tbXV0ZXMgXG4iKQ0KYGBgDQogIA0KV2UgYWxzbyBrbm93IHRoZSBudW1iZXIgb2YgcGVvcGxlIGNvbW11dGluZyBvdmVyIGVhY2ggbGluZSBzbyB0aGF0IHdvdWxkIGJlIGEgdXNlZnVsIHZhcmlhYmxlIHRvIGJpbmQgdG8gdGhlIHdpZHRoIG9mIHRoZSBsaW5lLiAgDQpXZSB3aWxsIGFsc28gZmlsdGVyIG91dCBzb21lIG9mIHRoZSBzbWFsbGVyIGNvbm5lY3Rpb25zIGFuZCBjb21tdXRlcnMgdGhhdCBuZXZlciBsZWF2ZSB0aGVpciBvd24gbXVuaWNpcGFsaXR5LiAgDQogICAgDQpgYGB7ciByZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQpjb21tdXRlIDwtIGNvbW11dGUgJT4lIA0KICBmaWx0ZXIoc2luayAhPSBzb3VyY2UpICU+JSANCiAgZmlsdGVyKGludGVyYWN0aW9uID4yMCkgJT4lIA0KICBtdXRhdGUobGluZVdpZHRoID0gaW50ZXJhY3Rpb24gLyBtYXgoaW50ZXJhY3Rpb24pICogMTApDQoNCmdncGxvdChjb21tdXRlKSArIGdlb21fc2YoYWVzKHNpemUgPSBsaW5lV2lkdGgpKSArIHNjYWxlX3NpemVfaWRlbnRpdHkoKSArIGxhYnModGl0bGUgPSAiUGxvdCBvZiBTcGFjaWFsIFJlbGF0aW9ucyBvZiBjb21tdXRlcyIsIHN1YnRpdGxlPSJXaXRoIGludGVyYWN0aW9uIGNvcnJlc3BvbmRpbmcgdG8gbGluZSB3aWR0aCIpDQpgYGANCmBgYHtyIGluY2x1ZGU9RkFMU0V9DQpwMSA8LSBnZ3Bsb3QoY29tbXV0ZSkgKyBnZW9tX3NmKGFlcyhzaXplID0gbGluZVdpZHRoLGNvbG9yPWxpbmVXaWR0aCkpICsgc2NhbGVfc2l6ZV9pZGVudGl0eSgpICsgbGFicyhzdWJ0aXRsZT0iICBBY3R1YWwgVmFsdWVzIikgKyB0aGVtZShheGlzLnRleHQgPSBlbGVtZW50X2JsYW5rKCksbGVnZW5kLnBvc2l0aW9uPSJub25lIikgKyBzY2FsZV9jb2xvcl9jb250aW51b3VzKHRyYW5zPSdyZXZlcnNlJykNCmBgYA0KDQoNClRoZSBwYXR0ZXJuIG9mIHRoZSBsaW5lcyBzaG93IHRoYXQgdGhlcmUgYXJlIGxvdHMgb2YgcGVvcGxlIHRyYXZlbGxpbmcgYmV0d2VlbiBjZXJ0YWluIHJlZ2lvbnMuIFRoZXNlIHJlZ2lvbnMgYXJlIG1vc3QgbGlrZWx5IHRoZSBtYWpvciBtdW5pY2lwYWxpdGllcyB3aXRoaW4gVXRyZWNodC4gSXQgc2VlbXMgdGhhdCB0aGVyZSBhcmUgbXVsdGlwbGUgbGFyZ2UgZW1wbG95bWVudCBjZW50cmVzIGluIHRoZSBhcmVhLiAgDQoNCioqKiAgDQoNCiNNb2RlbGxpbmcgU3BhdGlhbCBSZWxhdGlvbnMgIA0KTm93ICB0aGF0IHdlIGNhbiBwbG90IHRoZSBhY3R1YWwgbnVtYmVyIG9mIGNvbW11dGVycywgbGV0IHVzIHNlZSBpZiB3ZSBjYW4gbW9kZWwgdGhlIGNvbW11dGluZyByZWxhdGlvbnNoaXAgYmFzZWQgb24gc29tZSBvdGhlciBpbmZvcm1hdGlvbi4gIA0KSW4gdGhpcyBjYXNlLCB3ZSBjYXJlIGdvaW5nIHRvIHVzZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiBtdW5pY2lwYWxpdGllcywgdGhlIG51bWJlciBvZiByZXNpZGVudHMsIGFuZCB0aGUgbnVtYmVyIG9mIGpvYnMgaW4gZWFjaCBtdW5pY2lwYWxpdHkuICANCmBgYHtyIHJlc3VsdHM9J2hpZGUnLHdhcm5pbmc9RkFMU0UsbWVzc2FnZT1GQUxTRX0NCnJlc2lkZW50cyA8LSAgcmVhZF9jc3YoInJlc2lkZW50cy5jc3YiKSAlPiUgDQogIHNlbGVjdChpZCwgd2VpZ2h0KQ0KDQpqb2JzIDwtIHJlYWRfY3N2KCJqb2JzLmNzdiIpICU+JSANCiAgc2VsZWN0KGlkLCB3ZWlnaHQpDQpgYGANCg0KV2UgY2FuIGNyZWF0ZSBhIGRpc3RhbmNlIG1hdHJpeCBmcm9tIHRoZSBjZW50cm9pZHMgd2UgY3JlYXRlZCBlYXJsaWVyLiAgDQpgYGB7ciByZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQpkaXN0IDwtIHN0X2Rpc3RhbmNlKHggPSBtdW5pcy5jZW50cm9pZCwgeSA9IG11bmlzLmNlbnRyb2lkKQ0KYGBgDQoNCldlIG5lZWQgdG8gcmVmb3JtYXQgdGhlIHJlc3VsdGluZyBkaXN0YW5jZSBtYXRyaXggdG8gYSB0YWJsZSB3aXRoIHRocmVlIGNvbHVtbnMgKGZyb20sIHRvIGFuZCB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0aG9zZSBwb2ludHMpDQpgYGB7ciByZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQpyb3duYW1lcyhkaXN0KSA8LSBtdW5pcy5jZW50cm9pZCRpZA0KY29sbmFtZXMoZGlzdCkgPC0gbXVuaXMuY2VudHJvaWQkaWQNCmRpc3QgPC0gbGlzdCgNCiAgc291cmNlID0gcm93bmFtZXMoZGlzdClbcm93KGRpc3QpXSAlfHwlIHJvdyhkaXN0KSwNCiAgc2luayA9IGNvbG5hbWVzKGRpc3QpW2NvbChkaXN0KV0gJXx8JSBjb2woZGlzdCksDQogIGRpc3RhbmNlID0gZGlzdA0KKSAlPiUgDQogIG1hcF9kZmMoYXMudmVjdG9yKSAlPiUgDQogIG11dGF0ZShzb3VyY2UgPSBhcy5udW1lcmljKHNvdXJjZSkpICU+JSANCiAgbXV0YXRlKHNpbmsgPSBhcy5udW1lcmljKHNpbmspKQ0KYGBgDQoNCk5vdyB3ZSBqb2luIHRoZW0gdG9nZXRoZXIgIA0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQpjb21tdXRlIDwtIGNvbW11dGUgJT4lIA0KICBsZWZ0X2pvaW4oZGlzdCkgJT4lIA0KICBsZWZ0X2pvaW4ocmVzaWRlbnRzLCBieSA9IGMoJ3NvdXJjZScgPSAnaWQnKSkgJT4lIA0KICByZW5hbWUocmVzaWRlbnRzID0gd2VpZ2h0KSAlPiUgDQogIGxlZnRfam9pbihqb2JzLGJ5ID0gYygnc2luayc9J2lkJykpICU+JSANCiAgcmVuYW1lKGpvYnM9d2VpZ2h0KQ0KYGBgDQoNCkxldCdzIGRvIGEgdmlzdWFsIGluc3BlY3Rpb24gdG8gc2VlIGlmIGV2ZXJ5dGhpbmcgd2VudCB3ZWxsLiAgDQpDb21wYXJpbmcgdGhlIG51bWJlciBvZiBjb21tdXRlcnMgYW5kIHRoZSBkaXN0YW5jZTogIA0KDQpgYGB7ciByZXN1bHRzPSdoaWRlJyx3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQpnZ3Bsb3QoY29tbXV0ZSxhZXMoaW50ZXJhY3Rpb24sZGlzdGFuY2UpKSsgDQogIGdlb21fcG9pbnQoKSsNCiAgbGFicyh0aXRsZT0iVmlzdWFsIGNvbXBhcmlzaW9uIG9mIG51bWJlciBvZiBjb21tdXRlcnMgdnMgZGlzdGFuY2VcbiIpDQpgYGANCg0KSXQgbG9va3MgbGlrZSB0aGUgcmVsYXRpb25zaGlwIGlzIG5vdCBsaW5lYXIsIHBlcmhhcHMgYSBsb2ctbG9nIHBsb3Qgd291bGQgaGVscDogIA0KYGBge3IgcmVzdWx0cz0naGlkZScsd2FybmluZz1GQUxTRSxtZXNzYWdlPUZBTFNFfQ0KZ2dwbG90KGNvbW11dGUsYWVzKGxvZyhpbnRlcmFjdGlvbiksbG9nKGRpc3RhbmNlKSkpKyANCiAgZ2VvbV9wb2ludCgpKw0KICBsYWJzKHRpdGxlPSJMb2ctbG9nIHBsb3Qgb2YgbnVtYmVyIG9mIGNvbW11dGVycyB2cyBkaXN0YW5jZVxuIikNCmBgYA0KDQpBIG5haXZlIGxpbmVhciBtb2RlbCBiYXNlZCBvbiB0aGUgYXZhaWxhYmxlIGluZm9ybWF0aW9uIGNhbiBiZSBlc3RpbWF0ZWQgdXNpbmcgYW4gT0xTIHJlZ3Jlc3Npb24NCmBgYHtyfQ0KbG0oZGF0YT1jb21tdXRlLCBmb3JtdWxhID0gaW50ZXJhY3Rpb24gfiByZXNpZGVudHMram9icytkaXN0YW5jZSkgJT4lIHN1bW1hcnkoKQ0KYGBgDQpUaGUgc3VtbWFyeSBhYm92ZSBzaG93cyB0aGF0IHRoZSBtb2RlbCBkb2VzIG5vdCByZWFsbHkgZml0IHZlcnkgd2VsbC4gTm90IG9ubHkgZG9lcyBpZiBoYXZlIGEgbG93IGFkanVzdGVkICRSXjIkIG9mIDAuNTAwMywgdGhlIHAtdmFsdWVzIGZvciBhbGwgcGFyYW1ldGVycyBhbmQgdGhlIHJlZ3Jlc3Npb24gaXRzZWxmIGlzIHZlcnkgc21hbGwuICANCkxldHMgbG9vayBhdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBjb21tdXRlcnMNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpnZ3Bsb3QoY29tbXV0ZSxhZXMoaW50ZXJhY3Rpb24pKSsNCiAgZ2VvbV9oaXN0b2dyYW0oKSsNCiAgbGFicyh0aXRsZT0iRGlzdHJpYnV0aW9uIG9mIHRoZSBpbnRlcmFjdGlvbnMgb2YgY29tbXV0ZXJzXG4iKQ0KYGBgDQpJdCBsb29rcyBsaWtlIGl0IGZvbGxvd3MgdGhlIFBvaXNzb24gZGlzdHJpYnV0aW9uIHF1aXRlIGNsb3NlbHkuICANCkZyb20gdGhlIG1vbmRheSdzIGxlY3R1cmUsIGluIHNwYXRpYWwgaW50ZXJhY3Rpb24gbW9kZWxsaW5nIHdlIGdlbmVyYWxseSB0aGlua2cgb2Ygc3BhdGlhbCBpbnRlcmFjdGlvbnMgYXM6ICANCiRUX3tpan0gPSBrXGZyYWN7Vl5cbXVfaSBXXlxhbHBoYV9qfXtkX3tpan1eXGJldGF9JCAgDQpvciByZXdyaXR0ZW46ICANCiRUX3tpan09IGtWXlxtdV9pV15cYWxwaGFfamReey1cYmV0YX1fe2lqfSQgIA0Kd2UgY2FuIHJld3JpdGUgdGhpcyBhZ2FpbiB0byBlc3RpbWF0ZSBpdCB3aXRoIGxpbmVhciBlc3RpbWF0b3JzOiAgDQokbG4gVF97aWp9ID0gayArIFxtdSBsbiBWX2kgKyBcYWxwaGEgbG4gV19qIC0gXGJldGEgbG4gZF97aWp9JCAgDQpgYGB7cn0NCm1vZGVsIDwtIGdsbShkYXRhID0gY29tbXV0ZSxmb3JtdWxhID0gaW50ZXJhY3Rpb24gfmxvZyhyZXNpZGVudHMpICsgbG9nKGpvYnMpICsgbG9nIChkaXN0YW5jZSksIGZhbWlseSA9IHBvaXNzb24oKSkNCmBgYA0KDQpHTE0gZG9lc24ndCBwcm92aWRlICRyXjIkIGJ5IGRlZmF1bHQgc28gd2UgbWFrZSBvdXIgb3duIGZ1bmN0aW9uDQpgYGB7cn0NCnIyIDwtIGZ1bmN0aW9uKGVtcGlyaWNhbCwgZml0dGVkKXsNCiAgcmV0dXJuKGNvcihlbXBpcmljYWwsIGZpdHRlZCleMikNCn0NCg0KcjIoY29tbXV0ZSRpbnRlcmFjdGlvbixmaXR0ZWQobW9kZWwpKQ0KYGBgDQpUaGUgJFJeMiQgdmFsdWUgaXMgcHJldHR5IGNsb3NlIHRvIDEgbm93LCB0aGlzIG1lYW5zIHRoYXQgdGhlIG1vZGVsIGlzIGEgbXVjaCBiZXR0ZXIgZml0IG5vdy4gIA0KTGV0J3Mgc2VlIGlmIHdlIGNhbiBpbnRlcnByZXQgdGhlIHJlc3Qgb2YgdGhlIG1vZGVsIGluZm9ybWF0aW9uLiAgDQpgYGB7cn0NCnRpZHkobW9kZWwpDQpgYGANCkFsbCBjb2VmZmljaWVudHMgaGF2ZSBoaWdoIHRlc3Qtc3RhdGlzdGljIHZhbHVlcyBhbmQgYSBwLXZhbHVlIG9mIDAuIFRoaXMgbWVhbnMgdGhhdCB0aGV5IGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LiBPdXIgbW9kZWwgaXM6ICANCiZuYnNwOyAgDQokSW50ZXJhY3Rpb24gPSBcYmV0YV8wICsgXGJldGFfMSBsb2cocmVzaWRlbnRzKSArIFxiZXRhXzIgbG9nKGpvYnMpICsgXGJldGFfMyBsb2coZGlzdGFuY2UpJCAgDQombmJzcDsgIA0KJD0gSW50ZXJhY3Rpb24gPSAzLjM1NyArIDAuODU4NSBsb2cocmVzaWRlbnRzKSArIDAuOTgzNiBsb2coam9icykgLSAxLjUxMjAgbG9nKGRpc3RhbmNlKSQgIA0KDQo8YnIgLz4gIA0KICANCkFkZGluZyB0aGUgZml0dGVkIHZhbHVlcywgYXMgd2VsbCBhcyB0aGVpciByZXNpZHVhbHMgYmFjayB0byB0aGUgb3JpZ2luYWwgZGF0YSB0YWJsZSBzbyB3ZSBjYW4gKHZpc3VhbGx5KSBpbnNwZWN0IHRoZW0uDQpgYGB7cn0NCmNvbW11dGUgPC0gY29tbXV0ZSAlPiUgDQogIG11dGF0ZShmaXR0ZWQgPSBmaXR0ZWQobW9kZWwpKSAlPiUgDQogIG11dGF0ZShyZXNpZHVhbCA9IGludGVyYWN0aW9uIC0gZml0dGVkKSAlPiUgDQogIG11dGF0ZShyZXNpZHVhbFNpZ24gPSBzaWduKHJlc2lkdWFsKSkNCg0KY29tbXV0ZSAlPiUgDQogIG11dGF0ZShsaW5lV2lkdGggPSBmaXR0ZWQgLyBtYXgoZml0dGVkKSAqIDEwKSAlPiUgDQogIGdncGxvdCgpICsgZ2VvbV9zZihhZXMoc2l6ZSA9IGxpbmVXaWR0aCkpKw0KICBzY2FsZV9zaXplX2lkZW50aXR5KCkrDQogIGxhYnModGl0bGUgPSAiUGxvdCBvZiBNb2RlbCBvZiBTcGFjaWFsIFJlbGF0aW9ucyBvZiBDb21tdXRlcyIsIHN1YnRpdGxlPSJXaXRoIG1vZGVsIHZhbHVlcyBjb3JyZXNwb25kaW5nIHRvIGxpbmUgd2lkdGgiKQ0KYGBgDQo8YnIgLz4NCkNvbXBhcmluZyB0aGlzIHdpdGggdGhlIGVhcmxpZXIgcGxvdCBvZiB0aGUgYWN0dWFsIHZhbHVlcywgdGhleSBhcmUgYWN0dWFsbHkgcHJldHR5IGFjY3VyYXRlLiBHaXZlbiBzb21lIGxpbmVXaWR0aHMgYXJlIGRpZmZlcmVudCBpbiBhYnNvbHV0ZSBzaXplIGJ1dCB0aGUgZGlmZmVyZW5jZSBpbiB0aGUgbGluZVdpZHRocyBhbGwgY29ycmVzcG9uZCB0byB0aGUgYWN0dWFsIHZhbHVlcy4gIA0KYGBge3IgZWNobz1GQUxTRX0NCnAyIDwtIGNvbW11dGUgJT4lIA0KICBtdXRhdGUobGluZVdpZHRoID0gZml0dGVkIC8gbWF4KGZpdHRlZCkgKiAxMCkgJT4lIA0KICBnZ3Bsb3QoKSArIGdlb21fc2YoYWVzKHNpemUgPSBsaW5lV2lkdGgsY29sb3I9bGluZVdpZHRoKSkrDQogIHNjYWxlX3NpemVfaWRlbnRpdHkoKSsNCiAgbGFicyhzdWJ0aXRsZT0iTW9kZWwgVmFsdWVzIikrDQogIHRoZW1lKGF4aXMudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSxsZWdlbmQucG9zaXRpb249Im5vbmUiKSsNCiAgc2NhbGVfY29sb3JfY29udGludW91cyh0cmFucz0ncmV2ZXJzZScpDQoNCnBsb3RfZ3JpZChwMSxwMixhbGlnbiA9ICJoIiwgbmNvbD0yLCBzY2FsZSA9IDEuMSkgDQpgYGANCg0KPGJyIC8+DQoNCkZvciByZXNpZHVhbHMsIHRoZXkgY2FuIGJlIHBvc2l0aXZlIG9yIG5lZ2F0aXZlLCBidXQgc2luY2UgbGluZSB3aWR0aHMgY2FuIG9ubHkgYmUgcG9zaXRpdmUsIHdlIHdpbGwgdXNlIHRoZSBjb2xvciBhZXN0aGV0aWMgdG8gaW5kaWNhdGUgd2hldGhlciB0aGUgcmVzaWR1YWwgaXMgcG9zaXRpdmUgb3IgbmVnYXRpdmU6DQpgYGB7cn0NCmNvbW11dGUgJT4lIA0KICBtdXRhdGUobGluZVdpZHRoID0gYWJzKHJlc2lkdWFsKSAvIG1heChyZXNpZHVhbCkgKiAxMCkgJT4lIA0KICBnZ3Bsb3QoKSArZ2VvbV9zZihhZXMoc2l6ZT1saW5lV2lkdGgsY29sb3IgPSBmYWN0b3IocmVzaWR1YWxTaWduKSkpICsNCiAgc2NhbGVfc2l6ZV9pZGVudGl0eSgpKw0KICBsYWJzKHRpdGxlID0gIlBsb3Qgb2YgUmVzaWR1YWxzIG9mIE1vZGVscyIsIHN1YnRpdGxlPSJXaXRoIHJlc2lkdWFsIHZhbHVlcyBjb3JyZXNwb25kaW5nIHRvIGxpbmUgd2lkdGgiLGNvbG9yPSJSZXNpZHVhbCBTaWduIikNCmBgYA0KW0JhY2sgdG8gdG9wXSgjdG9wKQ0KDQoqKioNCg0KI0xhYiBRdWVzdGlvbnMgeyNMYWJRfQ0KQ3JlYXRlIGEgcGxvdCB2aXN1YWxpemluZyBhbGwgY29tbXV0aW5nIHJlbGF0aW9ucyBpbiB0aGUgTmV0aGVybGFuZHMgIA0KXyh1c2UgYSBkZWNlbnQgZmlsdGVyIGZvciB0aGlzLCBmb3IgZXhhbXBsZSBvbmx5IHNob3dpbmcgbGluZXMgd2l0aCBtb3JlIHRoYW4gNzUwIGNvbW11dGVycylfDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcmVzdWx0cz0iaGlkZSJ9DQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGU9VFJVRSkNCiNSZWFkIGluIGluZm9ybWF0aW9uDQpuZXRoZXJsYW5kcyA8LSBzdF9yZWFkKCJnZW1fMjAxNi5nZW9qc29uIixjcnMgPSAyODk5MikNCiNSZW1vdmUgd2F0ZXIgYW5kIHNlYQ0KbmV0aGVybGFuZHMgPC0gbmV0aGVybGFuZHMgJT4lDQogIGZpbHRlcihXQVRFUiA9PSAnTkVFJykgJT4lDQogIHNlbGVjdChHTV9DT0RFLCBHTV9OQUFNKQ0KDQojcmVhZCBpbiBjb21tdXRlIGluZm9ybWF0aW9uDQpuZXRoZXJfY29tbXV0ZSA8LSAgcmVhZF9jc3YoImNvbW11dGluZy5jc3YiKSAlPiUgDQogIHNlbGVjdChzb3VyY2Usc2luayx3ZWlnaHQpICU+JSANCiAgcmVuYW1lKGludGVyYWN0aW9uID0gd2VpZ2h0KQ0KDQojZmlsdGVyaW5nIHRhYmxlDQpuZXRoZXJsYW5kcyA8LSBuZXRoZXJsYW5kcyAlPiUgDQogIG11dGF0ZShpZCA9IGFzLm51bWVyaWMoc3RyX3JlcGxhY2UoR01fQ09ERSwiR00iLCIiKSkpDQpuZXRoZXJfY29tbXV0ZSA8LSBuZXRoZXJfY29tbXV0ZSAlPiUgDQogIGZpbHRlcihzb3VyY2UgJWluJSBuZXRoZXJsYW5kcyRpZCAmIHNpbmsgJWluJSBuZXRoZXJsYW5kcyRpZCkNCg0KI3VzZSBjZW50cm9pZCB0byBqb2luIHNvdXJjZSBhbmQgc2luayB0byB0YWJsZSBwbHVzIGNyZWF0ZSBsaW5lDQpuZXRoZXJsYW5kcy5jZW50cm9pZCA8LSAgc3RfY2VudHJvaWQobmV0aGVybGFuZHMpICU+JSANCiAgc2VsZWN0KGlkKQ0KbmV0aGVyX2NvbW11dGUgPC0gbmV0aGVyX2NvbW11dGUgJT4lIA0KICBsZWZ0X2pvaW4obmV0aGVybGFuZHMuY2VudHJvaWQsIGJ5ID0gYygic291cmNlIiA9ICJpZCIpKSAlPiUgDQogIGxlZnRfam9pbihuZXRoZXJsYW5kcy5jZW50cm9pZCwgYnkgPSBjKCJzaW5rIiA9ICJpZCIpKSAlPiUgDQogIHJvd3dpc2UoKSAlPiUgDQogIG11dGF0ZShnZW9tZXRyeSA9IHN0X2NvbWJpbmUoYyhnZW9tZXRyeS54LGdlb21ldHJ5LnkpKSAlPiUgIHN0X2Nhc3QoIkxJTkVTVFJJTkciKSkgJT4lIA0KICBzZWxlY3QoLWdlb21ldHJ5LngsLWdlb21ldHJ5LnkpICU+JSBzdF9hc19zZihjcnMgPSAyODk5MikNCg0KI3RyaW0gY29tbXV0ZXMgKGtlZXAgb25seSBsaW5lcyB3aXRoIG1vcmUgdGhhbiA3NTAgY29tbXV0ZXJzKQ0KbmV0aGVyX2NvbW11dGVfdHJpbSA8LSBuZXRoZXJfY29tbXV0ZSU+JSANCiAgZmlsdGVyKHNpbmsgIT0gc291cmNlKSAlPiUgDQogIGZpbHRlcihpbnRlcmFjdGlvbiA+MjAwMCkgJT4lIA0KICBtdXRhdGUobGluZVdpZHRoID0gaW50ZXJhY3Rpb24gLyBtYXgoaW50ZXJhY3Rpb24pICogMTApDQpgYGANCmBgYHtyIGVjaG89RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoY2FjaGU9VFJVRSkNCmdncGxvdChuZXRoZXJfY29tbXV0ZV90cmltKSArIGdlb21fc2YoYWVzKHNpemUgPSBsaW5lV2lkdGgpKSsNCiAgc2NhbGVfc2l6ZV9pZGVudGl0eSgpKw0KICBsYWJzKHRpdGxlID0gIlBsb3Qgb2YgYWxsIGNvbW11dGluZyByZWxhdGlvbnMgaW4gdGhlIE5ldGhlcmxhbmRzIixzdWJ0aXRsZT0id2l0aCA+IDIwMDAgY29tbXV0ZXJzIikrDQogIHRoZW1lX2dyZXkoKQ0KICANCmBgYA0KDQoNCjxiciAvPg0KDQpDcmVhdGUgYSBzdW1tYXJ5IHRhYmxlIGZvciB0aGUgZGVzdGluYXRpb25zIG9mIGNvbW11dGluZyBpbiB0aGUgTmV0aGVybGFuZHMgIA0KXyhUaXA6IHVzZSB0aGUgZ3JvdXBfYnkoKSBhbmQgc3VtbWFyaXplKCkgZnVuY3Rpb25zLiBXaGljaCBtdW5pY2lwYWxpdGllcyBoYXZlIHRoZSB0b3AgMyBvZiBtb3N0IGluY29taW5nIGNvbW11dGVycz8pICANCihUaXA6IHRoZSBuYW1lcyBvZiB0aGUgbXVuaWNpcGFsaXRpZXMgYXJlIGNvbnRhaW5lZCBpbiB0aGUg4oCYbXVuaXPigJkgb2JqZWN0IOKAkyB5b3UgY2FuIGpvaW4gdGhlbSB0byB5b3VyIHN1bW1hcnkgdGFibGUgZm9yIGVhc2llciByZWFkaW5nLilfICANCg0KPGJyIC8+DQoNCipTdW1tYXJ5IHRhYmxlIGZvciB0aGUgZGVzdGluYXRpb25zIG9mIGNvbW11dGluZyBpbiB0aGUgTmV0aGVybGFuZHMqDQpgYGB7cn0NCm5ldGhlcnRhYmxlIDwtIG5ldGhlcl9jb21tdXRlJT4lIA0KICBmaWx0ZXIoc2luayAhPSBzb3VyY2UpICU+JQ0KICBncm91cF9ieShzaW5rKSAlPiUgDQogIHN1bW1hcmlzZShpbmNfY29tbXV0ZXJzID0gc3VtKGludGVyYWN0aW9uKSkgJT4lIA0KICByZW5hbWUoZGVzdGluYXRpb24gPSBzaW5rKQ0KDQpuZXRoZXJ0YWJsZQ0KYGBgDQogIA0KKmFkZGluZyBuYW1lcyBieSBqb2luaW5nIG11bmljaXBhbGl0aWVzIGFuZCBzb3J0aW5nIGJ5IGluY29taW5nIGNvbW11dGVycyoNCmBgYHtyfQ0KbmV0aGVyX211bmlzPSBkYXRhLmZyYW1lKGlkID0gbmV0aGVybGFuZHMkaWQsbmFtZT1uZXRoZXJsYW5kcyRHTV9OQUFNKQ0KbmV0aGVyX2NvbW11dGVfbmFtZSA8LSBuZXRoZXJ0YWJsZSAlPiUgDQogIGxlZnRfam9pbihuZXRoZXJfbXVuaXMsYnkgPSBjKCdkZXN0aW5hdGlvbic9J2lkJykpDQogIA0KbmV0aGVyX2NvbW11dGVfbmFtZVtvcmRlcigtbmV0aGVyX2NvbW11dGVfbmFtZSRpbmNfY29tbXV0ZXJzKSxdDQpgYGANClRoZSBtdW5pY2lwYWxpdGllcyB3aGljaCBoYXZlIHRoZSB0b3AgMyBvZiBtb3N0IGluY29taW5nIGNvbW11dGVycyBhcmUgKkFtc3RlcmRhbSosKlJvdHRlcmRhbSogYW5kICpVdHJlY2h0Ki4NCg0KPGJyIC8+DQoNCiMjIyMjUnVuIGEgc3BhdGlhbCBpbnRlcmFjdGlvbiBtb2RlbCBmb3IgdGhlIGRhdGEgZm9yIHRoZSBlbnRpcmUgY291bnRyeS4gIA0KV2hhdOKAmXMgdGhlIG1vZGVsIGZpdCAoUjIpPyAgDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KI2NyZWF0ZSBhIGRpc3RhbmNlIG1hdHJpeCBmcm9tIHRoZSBjZW50cm9pZHMNCmRpc3RfYWxsIDwtIHN0X2Rpc3RhbmNlKHggPSBuZXRoZXJsYW5kcy5jZW50cm9pZCwgeSA9IG5ldGhlcmxhbmRzLmNlbnRyb2lkKQ0KDQojcmVmb3JtYXQgdGhlIHJlc3VsdGluZyBkaXN0YW5jZSBtYXRyaXggdG8gYSB0YWJsZSB3aXRoIHRocmVlIGNvbHVtbnMNCnJvd25hbWVzKGRpc3RfYWxsKSA8LSBuZXRoZXJsYW5kcy5jZW50cm9pZCRpZA0KY29sbmFtZXMoZGlzdF9hbGwpIDwtIG5ldGhlcmxhbmRzLmNlbnRyb2lkJGlkDQpkaXN0X2FsbCA8LSBsaXN0KA0KICBzb3VyY2UgPSByb3duYW1lcyhkaXN0X2FsbClbcm93KGRpc3RfYWxsKV0gJXx8JSByb3coZGlzdF9hbGwpLA0KICBzaW5rID0gY29sbmFtZXMoZGlzdF9hbGwpW2NvbChkaXN0X2FsbCldICV8fCUgY29sKGRpc3RfYWxsKSwNCiAgZGlzdGFuY2UgPSBkaXN0X2FsbA0KKSAlPiUgDQogIG1hcF9kZmMoYXMudmVjdG9yKSAlPiUgDQogIG11dGF0ZShzb3VyY2UgPSBhcy5udW1lcmljKHNvdXJjZSkpICU+JSANCiAgbXV0YXRlKHNpbmsgPSBhcy5udW1lcmljKHNpbmspKQ0KDQojam9pbg0KbmV0aGVyX2NvbW11dGVfYWxsIDwtIG5ldGhlcl9jb21tdXRlICU+JSANCiAgbGVmdF9qb2luKGRpc3RfYWxsKSAlPiUgDQogIGxlZnRfam9pbihyZXNpZGVudHMsIGJ5ID0gYygnc291cmNlJyA9ICdpZCcpKSAlPiUgDQogIHJlbmFtZShyZXNpZGVudHMgPSB3ZWlnaHQpICU+JSANCiAgbGVmdF9qb2luKGpvYnMsYnkgPSBjKCdzaW5rJz0naWQnKSkgJT4lIA0KICByZW5hbWUoam9icz13ZWlnaHQpDQoNCm5ldGhlcl9jb21tdXRlX2FsbCA8LSBuZXRoZXJfY29tbXV0ZV9hbGwlPiUgDQogIGZpbHRlcihzaW5rICE9IHNvdXJjZSkgJT4lIA0KICBmaWx0ZXIoaW50ZXJhY3Rpb24gPjApDQoNCm1vZGVsMiA8LSBnbG0oZGF0YSA9IG5ldGhlcl9jb21tdXRlX2FsbCxmb3JtdWxhID0gaW50ZXJhY3Rpb24gfmxvZyhyZXNpZGVudHMpICsgbG9nKGpvYnMpICsgbG9nIChkaXN0YW5jZSksIGZhbWlseSA9IHBvaXNzb24oKSkNCg0Kc3VtbWFyeShtb2RlbDIpDQoNCnIyKG5ldGhlcl9jb21tdXRlX2FsbCRpbnRlcmFjdGlvbixmaXR0ZWQobW9kZWwyKSkNCg0KYGBgDQpUaGUgbW9kZWwgZml0IGZvciBSMiBpcyBJbnRlcmFjdGlvbnMgPSA4LjA5MjgwNTEgKyAwLjYzODIzMzlsb2cocmVzaWRlbnRzKSArIDAuODIzMjE1NWxvZyhqb2JzKSAtMS42MDI2NTI1bG9nKGRpc3RhbmNlKS4gIA0KV2l0aCBhbiAkUl4yJCB2YWx1ZSBvZiAwLjA5NjM2NTM1Lg0KDQo8YnIgLz4NCg0KQ2FuIHlvdSBleHBsYWluIHRoZSBkaWZmZXJlbnQgZml0IGNvbXBhcmVkIHRvIHRoZSBtb2RlbCBmb3IgVXRyZWNodD8gIA0KX0luY2x1ZGUgdGhlIG1vZGVsIHBhcmFtZXRlcnMgaW4geW91ciBub3RlYm9vayBhbmQgZXhwbGFpbiB3aGF0IHRoZSBlc3RpbWF0ZXMgb2YgdGhlIGNvZWZmaWNpZW50cyBtZWFuLl8gIA0KDQpUaGlzIG1vZGVsIGlzIGEgd2Vha2VyIGZpdCB3aXRoIGFuICRSXjIkIHZhbHVlIG9mIDAuMDk2MzY1MzUgKHZzIFV0cmVjaHQncyAwLjg2Mzk3NTQpLiBUaGlzIGlzIHBvc3NpYmx5IGR1ZSB0byBoYXZpbmcgc3VjaCBhIGxhcmdlIGRhdGFzZXQgd2hpY2ggY291bGQgY29udGFpbiBsYXJnZSBudW1iZXIgb2Ygb3V0bGllcnMsIGluZmx1ZW5jaW5nIHRoZSBtb2RlbC4gT3RoZXIgZmFjdG9ycyB3aGljaCBhcmUgbm90IGFjY291bnRlZCBmb3IgY291bGQgYWxzbyBoYXZlIGluZmx1ZW5jZWQgdGhlIGludGVyYWN0aW9ucyB3aGVuIGxvb2tpbmcgYXQgaXQgZnJvbSBhIG5hdGlvbndpZGUgcGVyc3BlY3RpdmUgdmVyc3VzIHdpdGhpbiBhIG11bmljaXBhbGl0eS4gIA0KDQo8YnIgLz4NCg0KTG9vayBhdCB0aGUgcmVzaWR1YWxzIGFuZCBkZXNjcmliZSB3aGVyZSB5b3Ugc2VlIHRoZSBsYXJnZXN0IHJlc2lkdWFscy4gIA0KX0Fub3RoZXIgd2F5IHRvIGxvb2sgYXQgdGhpcyBpcyB0byBnbyBiYWNrIHRvIHlvdXIgdGFibGUgb2YgbXVuaWNpcGFsaXRpZXMgd2l0aCBtb3N0IGluY29taW5nIGNvbW11dGVycyBhbmQgY29tcGFyZSB0aGUgdG9wIDMgd2l0aCB0aGUgdG9wIDMgYmFzZWQgb24gdGhlIGZpdHRlZCB2YWx1ZXMuXyAgDQpgYGB7cn0NCiNjb21iaW5pbmcgdGhlIG1vZGVsIHZhbHVlcyB3aXRoIHRoZSB0YWJsZQ0KbmV0aGVyX2NvbW11dGVfYWxsIDwtIG5ldGhlcl9jb21tdXRlX2FsbCAlPiUgDQogIG11dGF0ZShmaXR0ZWQgPSBmaXR0ZWQobW9kZWwyKSkgJT4lIA0KICBtdXRhdGUocmVzaWR1YWwgPSBpbnRlcmFjdGlvbiAtIGZpdHRlZCkgJT4lIA0KICBtdXRhdGUocmVzaWR1YWxTaWduID0gc2lnbihyZXNpZHVhbCkpDQoNCiNTdW1tYXJ5IHRhYmxlIGZvciB0aGUgZGVzdGluYXRpb25zDQojQnkgUmVzaWR1YWxzDQpuZXRoZXJ0YWJsZTIgPC0gbmV0aGVyX2NvbW11dGVfYWxsJT4lIA0KICBmaWx0ZXIoc2luayAhPSBzb3VyY2UpICU+JQ0KICBncm91cF9ieShzaW5rKSAlPiUgDQogIHN1bW1hcmlzZShyZXNpZHVhbCA9IHN1bShyZXNpZHVhbCkpICU+JSANCiAgcmVuYW1lKGRlc3RpbmF0aW9uID0gc2luaykNCg0KbmV0aGVyX2NvbW11dGVfbmFtZTIgPC0gbmV0aGVydGFibGUyICU+JSANCiAgbGVmdF9qb2luKG5ldGhlcl9tdW5pcyxieSA9IGMoJ2Rlc3RpbmF0aW9uJz0naWQnKSkNCiAgDQpuZXRoZXJfY29tbXV0ZV9uYW1lMltvcmRlcigtbmV0aGVyX2NvbW11dGVfbmFtZTIkcmVzaWR1YWwpLF0NCmBgYA0KYGBge3IgZXZhbD1GQUxTRSwgaW5jbHVkZT1GQUxTRX0NCiNCeSBmaXR0ZWQgdmFsdWVzDQpuZXRoZXJ0YWJsZTMgPC0gbmV0aGVyX2NvbW11dGVfYWxsJT4lIA0KICBmaWx0ZXIoc2luayAhPSBzb3VyY2UpICU+JQ0KICBncm91cF9ieShzaW5rKSAlPiUgDQogIHN1bW1hcmlzZShpbmNfY29tbXV0ZXJzX2ZpdHRlZCA9IHN1bShmaXR0ZWQpKSAlPiUgDQogIHJlbmFtZShkZXN0aW5hdGlvbiA9IHNpbmspDQoNCm5ldGhlcl9jb21tdXRlX25hbWUzIDwtIG5ldGhlcnRhYmxlMyAlPiUgDQogIGxlZnRfam9pbihuZXRoZXJfbXVuaXMsYnkgPSBjKCdkZXN0aW5hdGlvbic9J2lkJykpDQoNCm5ldGhlcl9jb21tdXRlX25hbWUzW29yZGVyKC1uZXRoZXJfY29tbXV0ZV9uYW1lMyRpbmNfY29tbXV0ZXJzX2ZpdHRlZCksXQ0KYGBgDQoNCg0KVGhlIGxhcmdlc3QgcmVzaWR1YWxzIGFyZSAqQW1zdGVyZGFtKiwqR3JvbmluZ2VuKiwgYW5kICpVdHJlY2h0KiAgDQoNCjxiciAvPiANCg0KV2hpY2ggbXVuaWNpcGFsaXR5IGhhcyByaXNlbiB0byB0aGUgdG9wPyAgDQpHcm9uaW5nZW4gaGFzIHJlcGxhY2VkIFJvdHRlcmRhbSBhcyAybmQgcGxhY2UuIEFtc3RlcmRhbiBhbmQgVXRyZWNodCBzdGlsbCB0YWtlIDFzdCBhbmQgM3JkIHJlc3BlY3RpdmVseS4gSW4gZmFjdCwgUm90dGVybmRhbSBpcyBpbiBsYXN0KDU5OXRoKSBwbGFjZSB3aXRoIHRoZSBsYXJnZXN0IG5lZ2F0aXZlIHJlc2lkdWFsLiBNZWFuaW5nIHRoYXQgdGhlIG1vZGVsIHNldmVyZWx5IHVuZGVyZXN0aW1hdGVkIHRoZSBjb21tdXRlcyB0byBSb3R0ZXJkYW0uIENvbnZlcnNlDQoNCkNhbiB5b3UgdGhpbmsgb2YgYW4gZXhwbGFuYXRpb24/ICANCkdyb25pbmdlbiwgdGhvdWdoIHRoZSBjYXBpdGFsIGNpdHkgb2YgdGhlIEdyb25pbmdlbiBQcm92aW5jZSwgZG9lcyBub3QgYWN0dWFsbHkgaGF2ZSBtdWNoIGNvbW11dGVzIHRvIHRoZSBhcmVhIGZvciBpdHMgZ2l2ZW4gam9icywgZGlzdGFuY2UgYW5kIHJlc2lkZW50cyBhcyBpdCBpcyBhY3R1YWxseSBhIHVuaXZlcnNpdHkgY2l0eSwgd2l0aCBjbG9zZSB0byBhIGZvdXJ0aCBvZiBpdHMgcG9wdWxhdGlvbiBiZWluZyBzdHVkZW50cy4gSGVuY2UsIHRoZSBtb2RlbCBvdmVyZXN0aW1hdGVzIHRoZSBpbnRlcmFjdGlvbiBieSBhIGxhcmdlIGV4dGVudC4gIA0KUm90dGVyZGFtIG9uIHRoZSBvdGhlciBoYW5kLCBpcyBhIHNlYSBwb3J0IG11Y2ggbGlrZSBTaW5nYXBvcmUgYW5kIGlzIGFjdHVhbGx5IHRoZSBsYXJnZXN0IHBvcnQgaW4gRXVyb3BlIChzb3VyY2U6IHdpa2lwZWRpYSkuIFRoaXMgbWVhbnMgdGhhdCB0aGVyZSBpcyBhbm90aGVyIGZhY3RvciB3aGljaCBhY2NvdW50cyBmb3IgY29tbXV0ZXMgdG8gdGhlIGFyZWEgd2hpY2ggaXMgbm90IGFjY291bnRlZCBmb3IgYnkgb3VyIG1vZGVsIC0gdGhlIGxhcmdlIG51bWJlciBvZiBzaGlwcyBhcnJpdmluZyBhdCBwb3J0IGluIFJvdHRlcmRhbSENCg==