This is an R Markdown Notebook. When you execute code within the notebook, the results appear beneath the code.
Try executing this chunk by clicking the Run button within the chunk or by placing your cursor inside it and pressing Ctrl+Shift+Enter.
library(sf)
library(tidyverse)
library(broom)
#library(ggplot2)
devtools::install_github("tidyverse/ggplot2")
require(ggplot2)
We need these libraries to continue. geom_sf is only found in the newer version of ggplot2 that we install from github.
munis <- st_read("gem_2016.geojson", crs = 28992)
munis <- munis %>%
filter(WATER == 'NEE') %>%
select(GM_CODE, GM_NAAM)
munis.orig <- munis
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))
st_intersect filters geometrically intersecting shapes. We can reduce our huge dataset into a smaller and more focused dataset.
commute = read_csv("commuting.csv") %>%
select(source, sink, weight) %>%
rename(interaction = weight)
commute.orig <- commute
# This creates a field in munis that has ID of its GM_CODE without "GM".
# When this is done, we are able to match these id values to the ones in commute.
munis <- munis%>%
mutate(id= as.numeric(str_replace(GM_CODE, "GM", "")))
commute <- commute %>%
filter(source %in% munis$id & sink %in% munis$id)
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)
# this is the plot without the background polygons.
# ggplot(commute) + geom_sf()
ggplot(munis) + geom_sf() + geom_sf(data=commute)

commute <- commute %>%
filter(sink != source) %>%
filter(interaction > 20) %>%
mutate(lineWidth = interaction/ max(interaction) * 10)
ggplot(commute) + geom_sf(aes(size=lineWidth)) + scale_size_identity()

The commutes seem to be focused onto one area. It should have a single centre.
residents <- read_csv("residents.csv") %>%
select(id, weight)
jobs <- read_csv("jobs.csv") %>%
select(id, weight)
dist <- st_distance(x = munis.centroid, y=munis.centroid)
# solution from https://github.com/tidyverse/tidyr/issues/437
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))
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)
commute
Simple feature collection with 519 features and 7 fields
geometry type: LINESTRING
dimension: XY
bbox: xmin: 119109.2 ymin: 442367.9 xmax: 166907.8 ymax: 473542.3
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
ggplot(commute, aes(interaction, distance)) + geom_point()

ggplot(commute, aes(log(interaction), log(distance))) + geom_point()

The log plot seem to have a more linear relation than the normal plot. A log-log relationship would better describe the data points than a linear one.
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 model does not fit extremely well, but a certain level of correlation can be seen from the R value.
ggplot(commute, aes(interaction)) + geom_histogram()

This seems to be well estimated by a poisson distribution.
model <- glm(data = commute, formula = interaction ~ log(residents) + log(jobs) + log(distance),
family = poisson())
r2 <- function(empirical, fitted) {
return(cor(empirical, fitted)^2)
}
r2(commute$interaction, fitted(model))
[1] 0.8639754
tidy(model)
The low p values for all rows show that these results are statistically significant. The estimates show how much each value would change when another value changes. For example, for every increase in resident, an estimated 0.85853 count would increase.
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()

commute %>%
mutate(lineWidth = abs(residual) / max(residual) * 10) %>%
ggplot() + geom_sf(aes(size=lineWidth)) + scale_size_identity()

commute %>%
mutate(lineWidth = abs(residual) / max(residual) *10) %>%
ggplot() + geom_sf(aes(color = factor(residualSign), size=lineWidth)) + scale_size_identity()

The plots look similar as the previous one.

commute.orig.sinks <- commute.orig %>%
group_by(sink) %>%
summarise(sum(interaction))
commute.orig.sinks <-
commute.orig.sinks[order(commute.orig.sinks$"sum(interaction)", decreasing = TRUE),] %>%
left_join(munis.orig.centroid.nogeom, by=c("sink"="id"))
commute.orig.sinks
Simple feature collection with 350 features and 3 fields
geometry type: GEOMETRY
dimension: XY
bbox: xmin: 24537.79 ymin: 309545.2 xmax: 271400.9 ymax: 604580
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 top 3 destinations in Netherlands are Amsterdam, Rotterdam and ’s-Gravenhage.
dist <- st_distance(x = munis.orig.centroid, y=munis.orig.centroid)
# solution from https://github.com/tidyverse/tidyr/issues/437
rownames(dist) <- munis.orig.centroid$id
colnames(dist) <- munis.orig.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))
commute.orig <- commute.orig %>%
left_join(dist) %>%
left_join(residents, by = c('source' = 'id')) %>%
rename(residents = weight) %>%
left_join(jobs, by=c('sink' = 'id')) %>%
rename(jobs = weight)
Joining, by = c("source", "sink", "distance")
commute.orig
Simple feature collection with 2594 features and 9 fields
geometry type: LINESTRING
dimension: XY
bbox: xmin: 24537.79 ymin: 309545.2 xmax: 271400.9 ymax: 604580
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
ggplot(commute.orig, aes(interaction, distance)) + geom_point()

ggplot(commute.orig, aes(log(interaction), log(distance))) + geom_point()

lm(data = commute.orig, formula = interaction ~ residents + jobs + distance) %>%
summary()
Call:
lm(formula = interaction ~ residents + jobs + distance, data = commute.orig)
Residuals:
Min 1Q Median 3Q Max
-21961 -3370 -1490 1508 105061
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 7.347e+03 2.684e+02 27.37 <2e-16 ***
residents 2.217e-02 1.412e-03 15.70 <2e-16 ***
jobs 5.148e-02 1.989e-03 25.88 <2e-16 ***
distance -2.581e-01 1.220e-02 -21.16 <2e-16 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 8618 on 2590 degrees of freedom
Multiple R-squared: 0.2417, Adjusted R-squared: 0.2408
F-statistic: 275.2 on 3 and 2590 DF, p-value: < 2.2e-16
commute.orig <- commute.orig[commute.orig$distance != 0,]
model <- glm(data = commute.orig, formula = interaction ~ log(residents) + log(jobs) + log(distance),
family = poisson())
r2 <- function(empirical, fitted) {
return(cor(empirical, fitted)^2)
}
r2(commute.orig$interaction, fitted(model))
[1] 0.3696744
tidy(model)
The Rsquared value is very low. Even on the log log graph, which showed a relatively correlated graph for only Utrecht, did not have any prominent insights when plotted with data of the whole Netherlands. This shows that the distance does not have a close correlation with the amount of interactions a location have for the whole of Netherlands. This should be because the places are not interchangeable, if one’s job was in Amsterdam one would have to go to Amsterdam to work even if the distance is long.
It still, however, seems to fit poisson quite nicely.
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vay4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiANCg0KVHJ5IGV4ZWN1dGluZyB0aGlzIGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqUnVuKiBidXR0b24gd2l0aGluIHRoZSBjaHVuayBvciBieSBwbGFjaW5nIHlvdXIgY3Vyc29yIGluc2lkZSBpdCBhbmQgcHJlc3NpbmcgKkN0cmwrU2hpZnQrRW50ZXIqLiANCg0KYGBge3J9DQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGJyb29tKQ0KI2xpYnJhcnkoZ2dwbG90MikNCmRldnRvb2xzOjppbnN0YWxsX2dpdGh1YigidGlkeXZlcnNlL2dncGxvdDIiKQ0KcmVxdWlyZShnZ3Bsb3QyKQ0KYGBgDQpXZSBuZWVkIHRoZXNlIGxpYnJhcmllcyB0byBjb250aW51ZS4NCmdlb21fc2YgaXMgb25seSBmb3VuZCBpbiB0aGUgbmV3ZXIgdmVyc2lvbiBvZiBnZ3Bsb3QyIHRoYXQgd2UgaW5zdGFsbCBmcm9tIGdpdGh1Yi4NCg0KDQpgYGB7cn0NCm11bmlzIDwtIHN0X3JlYWQoImdlbV8yMDE2Lmdlb2pzb24iLCBjcnMgPSAyODk5MikNCg0KbXVuaXMgPC0gbXVuaXMgJT4lDQogIGZpbHRlcihXQVRFUiA9PSAnTkVFJykgJT4lDQogIHNlbGVjdChHTV9DT0RFLCBHTV9OQUFNKQ0KDQptdW5pcy5vcmlnIDwtIG11bmlzDQoNCnByb3ZpbmNlcyA8LSBzdF9yZWFkKCJwcm92aW5jZXMuZ2VvanNvbiIpICU+JQ0KICBzdF90cmFuc2Zvcm0oY3JzPTI4OTkyKSANCg0KdXRyZWNodCA8LSBwcm92aW5jZXMgJT4lDQogIGZpbHRlcihuYW1lPT0iVXRyZWNodCIpIA0KDQptdW5pcyA8LSBtdW5pcyAlPiUNCiAgZmlsdGVyKHN0X2ludGVyc2VjdHModXRyZWNodCwgc3RfY2VudHJvaWQobXVuaXMpLCBzcGFyc2U9RikpDQpgYGANCnN0X2ludGVyc2VjdCBmaWx0ZXJzIGdlb21ldHJpY2FsbHkgaW50ZXJzZWN0aW5nIHNoYXBlcy4NCldlIGNhbiByZWR1Y2Ugb3VyIGh1Z2UgZGF0YXNldCBpbnRvIGEgc21hbGxlciBhbmQgbW9yZSBmb2N1c2VkIGRhdGFzZXQuDQoNCmBgYHtyfQ0KDQpjb21tdXRlID0gcmVhZF9jc3YoImNvbW11dGluZy5jc3YiKSAlPiUNCiAgc2VsZWN0KHNvdXJjZSwgc2luaywgd2VpZ2h0KSAlPiUNCiAgcmVuYW1lKGludGVyYWN0aW9uID0gd2VpZ2h0KQ0KDQpjb21tdXRlLm9yaWcgPC0gY29tbXV0ZQ0KDQojIFRoaXMgY3JlYXRlcyBhIGZpZWxkIGluIG11bmlzIHRoYXQgaGFzIElEIG9mIGl0cyBHTV9DT0RFIHdpdGhvdXQgIkdNIi4NCiMgV2hlbiB0aGlzIGlzIGRvbmUsIHdlIGFyZSBhYmxlIHRvIG1hdGNoIHRoZXNlIGlkIHZhbHVlcyB0byB0aGUgb25lcyBpbiBjb21tdXRlLg0KbXVuaXMgPC0gbXVuaXMlPiUNCiAgbXV0YXRlKGlkPSBhcy5udW1lcmljKHN0cl9yZXBsYWNlKEdNX0NPREUsICJHTSIsICIiKSkpDQoNCmNvbW11dGUgPC0gY29tbXV0ZSAlPiUNCiAgZmlsdGVyKHNvdXJjZSAlaW4lIG11bmlzJGlkICYgc2luayAlaW4lIG11bmlzJGlkKQ0KDQptdW5pcy5jZW50cm9pZCA8LSANCiAgc3RfY2VudHJvaWQobXVuaXMpICU+JQ0KICBzZWxlY3QoaWQpDQoNCmNvbW11dGUgPC0gY29tbXV0ZSAlPiUgDQogIGxlZnRfam9pbihtdW5pcy5jZW50cm9pZCwgYnk9Yygic291cmNlIiA9ICJpZCIpKSAlPiUNCiAgbGVmdF9qb2luKG11bmlzLmNlbnRyb2lkLCBieT1jKCJzaW5rIiA9ICJpZCIpKSAlPiUNCiAgcm93d2lzZSgpICU+JQ0KICBtdXRhdGUoZ2VvbWV0cnkgPSBzdF9jb21iaW5lKGMoZ2VvbWV0cnkueCwgZ2VvbWV0cnkueSkpICU+JSANCiAgICAgICAgICAgc3RfY2FzdCgiTElORVNUUklORyIpKSAlPiUNCiAgc2VsZWN0KC1nZW9tZXRyeS54LCAtZ2VvbWV0cnkueSkgJT4lIHN0X2FzX3NmKGNycyA9IDI4OTkyKQ0KDQojIHRoaXMgaXMgdGhlIHBsb3Qgd2l0aG91dCB0aGUgYmFja2dyb3VuZCBwb2x5Z29ucy4NCiMgZ2dwbG90KGNvbW11dGUpICsgZ2VvbV9zZigpIA0KYGBgDQoNCmBgYHtyfQ0KZ2dwbG90KG11bmlzKSArIGdlb21fc2YoKSArIGdlb21fc2YoZGF0YT1jb21tdXRlKQ0KDQpgYGANCg0KYGBge3J9DQpjb21tdXRlIDwtIGNvbW11dGUgJT4lDQogIGZpbHRlcihzaW5rICE9IHNvdXJjZSkgJT4lDQogIGZpbHRlcihpbnRlcmFjdGlvbiA+IDIwKSAlPiUNCiAgbXV0YXRlKGxpbmVXaWR0aCA9IGludGVyYWN0aW9uLyBtYXgoaW50ZXJhY3Rpb24pICogMTApDQoNCmdncGxvdChjb21tdXRlKSArIGdlb21fc2YoYWVzKHNpemU9bGluZVdpZHRoKSkgKyBzY2FsZV9zaXplX2lkZW50aXR5KCkNCg0KYGBgDQpUaGUgY29tbXV0ZXMgc2VlbSB0byBiZSBmb2N1c2VkIG9udG8gb25lIGFyZWEuIEl0IHNob3VsZCBoYXZlIGEgc2luZ2xlIGNlbnRyZS4gDQoNCg0KYGBge3J9DQpyZXNpZGVudHMgPC0gcmVhZF9jc3YoInJlc2lkZW50cy5jc3YiKSAlPiUNCiAgc2VsZWN0KGlkLCB3ZWlnaHQpDQpqb2JzIDwtIHJlYWRfY3N2KCJqb2JzLmNzdiIpICU+JQ0KICBzZWxlY3QoaWQsIHdlaWdodCkNCmRpc3QgPC0gc3RfZGlzdGFuY2UoeCA9IG11bmlzLmNlbnRyb2lkLCB5PW11bmlzLmNlbnRyb2lkKQ0KIyBzb2x1dGlvbiBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvdGlkeXIvaXNzdWVzLzQzNw0Kcm93bmFtZXMoZGlzdCkgPC0gbXVuaXMuY2VudHJvaWQkaWQNCmNvbG5hbWVzKGRpc3QpIDwtIG11bmlzLmNlbnRyb2lkJGlkDQpkaXN0IDwtIGxpc3Qoc291cmNlID0gcm93bmFtZXMoZGlzdClbcm93KGRpc3QpXSAlfHwlIHJvdyhkaXN0KSwNCiAgICAgICAgICAgICBzaW5rID0gY29sbmFtZXMoZGlzdClbY29sKGRpc3QpXSAlfHwlIGNvbChkaXN0KSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RhbmNlID0gZGlzdCkgJT4lDQogIG1hcF9kZmMoYXMudmVjdG9yKSAlPiUNCiAgbXV0YXRlKHNvdXJjZSA9IGFzLm51bWVyaWMoc291cmNlKSkgJT4lDQogIG11dGF0ZShzaW5rID0gYXMubnVtZXJpYyhzaW5rKSkNCg0KY29tbXV0ZSA8LSBjb21tdXRlICU+JQ0KICBsZWZ0X2pvaW4oZGlzdCkgJT4lDQogIGxlZnRfam9pbihyZXNpZGVudHMsIGJ5ID0gYygnc291cmNlJyA9ICdpZCcpKSAlPiUNCiAgcmVuYW1lKHJlc2lkZW50cyA9IHdlaWdodCkgJT4lDQogIGxlZnRfam9pbihqb2JzLCBieT1jKCdzaW5rJyA9ICdpZCcpKSAlPiUNCiAgcmVuYW1lKGpvYnMgPSB3ZWlnaHQpDQpgYGANCg0KYGBge3J9DQpjb21tdXRlDQpgYGANCg0KYGBge3J9DQpnZ3Bsb3QoY29tbXV0ZSwgYWVzKGludGVyYWN0aW9uLCBkaXN0YW5jZSkpICsgZ2VvbV9wb2ludCgpDQoNCmdncGxvdChjb21tdXRlLCBhZXMobG9nKGludGVyYWN0aW9uKSwgbG9nKGRpc3RhbmNlKSkpICsgZ2VvbV9wb2ludCgpDQpgYGANClRoZSBsb2cgcGxvdCBzZWVtIHRvIGhhdmUgYSBtb3JlIGxpbmVhciByZWxhdGlvbiB0aGFuIHRoZSBub3JtYWwgcGxvdC4NCkEgbG9nLWxvZyByZWxhdGlvbnNoaXAgd291bGQgYmV0dGVyIGRlc2NyaWJlIHRoZSBkYXRhIHBvaW50cyB0aGFuIGEgbGluZWFyIG9uZS4NCg0KYGBge3J9DQpsbShkYXRhID0gY29tbXV0ZSwgZm9ybXVsYSA9IGludGVyYWN0aW9uIH4gcmVzaWRlbnRzICsgam9icyArIGRpc3RhbmNlKSAlPiUgDQogIHN1bW1hcnkoKQ0KYGBgDQpUaGUgbW9kZWwgZG9lcyBub3QgZml0IGV4dHJlbWVseSB3ZWxsLCBidXQgYSBjZXJ0YWluIGxldmVsIG9mIGNvcnJlbGF0aW9uIGNhbiBiZSBzZWVuIGZyb20gdGhlIFIgdmFsdWUuDQoNCmBgYHtyfQ0KZ2dwbG90KGNvbW11dGUsIGFlcyhpbnRlcmFjdGlvbikpICsgZ2VvbV9oaXN0b2dyYW0oKQ0KYGBgDQpUaGlzIHNlZW1zIHRvIGJlIHdlbGwgZXN0aW1hdGVkIGJ5IGEgcG9pc3NvbiBkaXN0cmlidXRpb24uIA0KDQpgYGB7cn0NCg0KbW9kZWwgPC0gZ2xtKGRhdGEgPSBjb21tdXRlLCBmb3JtdWxhID0gaW50ZXJhY3Rpb24gfiBsb2cocmVzaWRlbnRzKSArIGxvZyhqb2JzKSArIGxvZyhkaXN0YW5jZSksDQogICAgICAgICAgICAgZmFtaWx5ID0gcG9pc3NvbigpKQ0KcjIgPC0gZnVuY3Rpb24oZW1waXJpY2FsLCBmaXR0ZWQpIHsNCiAgcmV0dXJuKGNvcihlbXBpcmljYWwsIGZpdHRlZCleMikNCn0NCnIyKGNvbW11dGUkaW50ZXJhY3Rpb24sIGZpdHRlZChtb2RlbCkpDQoNCnRpZHkobW9kZWwpDQpgYGANClRoZSBsb3cgcCB2YWx1ZXMgZm9yIGFsbCByb3dzIHNob3cgdGhhdCB0aGVzZSByZXN1bHRzIGFyZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50Lg0KVGhlIGVzdGltYXRlcyBzaG93IGhvdyBtdWNoIGVhY2ggdmFsdWUgd291bGQgY2hhbmdlIHdoZW4gYW5vdGhlciB2YWx1ZSBjaGFuZ2VzLiANCkZvciBleGFtcGxlLCBmb3IgZXZlcnkgaW5jcmVhc2UgaW4gcmVzaWRlbnQsIGFuIGVzdGltYXRlZCAwLjg1ODUzIGNvdW50IHdvdWxkIGluY3JlYXNlLg0KDQpgYGB7cn0NCmNvbW11dGUgPC0gY29tbXV0ZSAlPiUNCiAgbXV0YXRlKGZpdHRlZCA9IGZpdHRlZChtb2RlbCkpICU+JQ0KICBtdXRhdGUocmVzaWR1YWwgPSBpbnRlcmFjdGlvbiAtIGZpdHRlZCkgJT4lDQogIG11dGF0ZShyZXNpZHVhbFNpZ24gPSBzaWduKHJlc2lkdWFsKSkNCg0KY29tbXV0ZSAlPiUNCiAgbXV0YXRlKGxpbmVXaWR0aCA9IGZpdHRlZC8gbWF4KGZpdHRlZCkqMTApICU+JQ0KICBnZ3Bsb3QoKSArIGdlb21fc2YoYWVzKHNpemUgPSBsaW5lV2lkdGgpKSArIHNjYWxlX3NpemVfaWRlbnRpdHkoKQ0KDQpjb21tdXRlICU+JQ0KICBtdXRhdGUobGluZVdpZHRoID0gYWJzKHJlc2lkdWFsKSAvIG1heChyZXNpZHVhbCkgKiAxMCkgJT4lDQogIGdncGxvdCgpICsgZ2VvbV9zZihhZXMoc2l6ZT1saW5lV2lkdGgpKSArIHNjYWxlX3NpemVfaWRlbnRpdHkoKQ0KDQpjb21tdXRlICU+JQ0KICBtdXRhdGUobGluZVdpZHRoID0gYWJzKHJlc2lkdWFsKSAvIG1heChyZXNpZHVhbCkgKjEwKSAlPiUNCiAgZ2dwbG90KCkgKyBnZW9tX3NmKGFlcyhjb2xvciA9IGZhY3RvcihyZXNpZHVhbFNpZ24pLCBzaXplPWxpbmVXaWR0aCkpICsgc2NhbGVfc2l6ZV9pZGVudGl0eSgpDQoNCmBgYA0KVGhlIHBsb3RzIGxvb2sgc2ltaWxhciBhcyB0aGUgcHJldmlvdXMgb25lLg0KDQpgYGB7cn0NCmNvbW11dGUub3JpZyA8LSBjb21tdXRlLm9yaWcgJT4lDQogIGZpbHRlcihpbnRlcmFjdGlvbiA+IDIwMDApICU+JQ0KICBtdXRhdGUobGluZVdpZHRoID0gaW50ZXJhY3Rpb24vIG1heChpbnRlcmFjdGlvbikgKiAxMCkNCg0KbXVuaXMub3JpZyA8LSBtdW5pcy5vcmlnICU+JQ0KICBtdXRhdGUoaWQ9IGFzLm51bWVyaWMoc3RyX3JlcGxhY2UoR01fQ09ERSwgIkdNIiwgIiIpKSkNCg0KbXVuaXMub3JpZy5jZW50cm9pZCA8LSANCiAgc3RfY2VudHJvaWQobXVuaXMub3JpZykgJT4lDQogIHNlbGVjdChpZCwgR01fTkFBTSkNCg0KbXVuaXMub3JpZy5jZW50cm9pZC5ub2dlb20gPC0gbXVuaXMub3JpZy5jZW50cm9pZA0Kc3RfZ2VvbWV0cnkobXVuaXMub3JpZy5jZW50cm9pZC5ub2dlb20pIDwtIE5VTEwNCg0KY29tbXV0ZS5vcmlnIDwtIGNvbW11dGUub3JpZyAlPiUNCiAgbGVmdF9qb2luKG11bmlzLm9yaWcuY2VudHJvaWQsIGJ5PWMoInNvdXJjZSIgPSAiaWQiKSkgJT4lDQogIGxlZnRfam9pbihtdW5pcy5vcmlnLmNlbnRyb2lkLCBieT1jKCJzaW5rIiA9ICJpZCIpKSAlPiUNCiAgcm93d2lzZSgpICU+JQ0KICBtdXRhdGUoZ2VvbWV0cnkgPSBzdF9jb21iaW5lKGMoZ2VvbWV0cnkueCwgIGdlb21ldHJ5LnkpKSAlPiUgDQogICAgICAgICAgIHN0X2Nhc3QoIkxJTkVTVFJJTkciKSkgJT4lDQogIHNlbGVjdCgtZ2VvbWV0cnkueCwgLWdlb21ldHJ5LnkpICU+JSBzdF9hc19zZihjcnMgPSAyODk5MikNCg0KZ2dwbG90KGNvbW11dGUub3JpZykgKyBnZW9tX3NmKGFlcyhzaXplPWxpbmVXaWR0aCkpICsgc2NhbGVfc2l6ZV9pZGVudGl0eSgpDQpgYGANCg0KYGBge3J9DQpjb21tdXRlLm9yaWcuc2lua3MgPC0gY29tbXV0ZS5vcmlnICU+JQ0KICBncm91cF9ieShzaW5rKSAlPiUNCiAgc3VtbWFyaXNlKHN1bShpbnRlcmFjdGlvbikpIA0KDQoNCmNvbW11dGUub3JpZy5zaW5rcyA8LSANCmNvbW11dGUub3JpZy5zaW5rc1tvcmRlcihjb21tdXRlLm9yaWcuc2lua3MkInN1bShpbnRlcmFjdGlvbikiLCBkZWNyZWFzaW5nID0gVFJVRSksXSAlPiUNCiAgbGVmdF9qb2luKG11bmlzLm9yaWcuY2VudHJvaWQubm9nZW9tLCBieT1jKCJzaW5rIj0iaWQiKSkNCg0KDQpjb21tdXRlLm9yaWcuc2lua3MNCmBgYA0KVGhlIHRvcCAzIGRlc3RpbmF0aW9ucyBpbiBOZXRoZXJsYW5kcyBhcmUgQW1zdGVyZGFtLCBSb3R0ZXJkYW0gYW5kICdzLUdyYXZlbmhhZ2UuDQoNCmBgYHtyfQ0KZGlzdCA8LSBzdF9kaXN0YW5jZSh4ID0gbXVuaXMub3JpZy5jZW50cm9pZCwgeT1tdW5pcy5vcmlnLmNlbnRyb2lkKQ0KIyBzb2x1dGlvbiBmcm9tIGh0dHBzOi8vZ2l0aHViLmNvbS90aWR5dmVyc2UvdGlkeXIvaXNzdWVzLzQzNw0Kcm93bmFtZXMoZGlzdCkgPC0gbXVuaXMub3JpZy5jZW50cm9pZCRpZA0KY29sbmFtZXMoZGlzdCkgPC0gbXVuaXMub3JpZy5jZW50cm9pZCRpZA0KZGlzdCA8LSBsaXN0KHNvdXJjZSA9IHJvd25hbWVzKGRpc3QpW3JvdyhkaXN0KV0gJXx8JSByb3coZGlzdCksDQogICAgICAgICAgICAgc2luayA9IGNvbG5hbWVzKGRpc3QpW2NvbChkaXN0KV0gJXx8JSBjb2woZGlzdCksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0YW5jZSA9IGRpc3QpICU+JQ0KICBtYXBfZGZjKGFzLnZlY3RvcikgJT4lDQogIG11dGF0ZShzb3VyY2UgPSBhcy5udW1lcmljKHNvdXJjZSkpICU+JQ0KICBtdXRhdGUoc2luayA9IGFzLm51bWVyaWMoc2luaykpDQoNCmNvbW11dGUub3JpZyA8LSBjb21tdXRlLm9yaWcgJT4lDQogIGxlZnRfam9pbihkaXN0KSAlPiUNCiAgbGVmdF9qb2luKHJlc2lkZW50cywgYnkgPSBjKCdzb3VyY2UnID0gJ2lkJykpICU+JQ0KICByZW5hbWUocmVzaWRlbnRzID0gd2VpZ2h0KSAlPiUNCiAgbGVmdF9qb2luKGpvYnMsIGJ5PWMoJ3NpbmsnID0gJ2lkJykpICU+JQ0KICByZW5hbWUoam9icyA9IHdlaWdodCkNCg0KY29tbXV0ZS5vcmlnDQpgYGANCmBgYHtyfQ0KDQpnZ3Bsb3QoY29tbXV0ZS5vcmlnLCBhZXMoaW50ZXJhY3Rpb24sIGRpc3RhbmNlKSkgKyBnZW9tX3BvaW50KCkNCg0KZ2dwbG90KGNvbW11dGUub3JpZywgYWVzKGxvZyhpbnRlcmFjdGlvbiksIGxvZyhkaXN0YW5jZSkpKSArIGdlb21fcG9pbnQoKQ0KDQpgYGANCmBgYHtyfQ0KbG0oZGF0YSA9IGNvbW11dGUub3JpZywgZm9ybXVsYSA9IGludGVyYWN0aW9uIH4gcmVzaWRlbnRzICsgam9icyArIGRpc3RhbmNlKSAlPiUgDQogIHN1bW1hcnkoKQ0KYGBgDQpgYGB7cn0NCiBjb21tdXRlLm9yaWcgPC0gY29tbXV0ZS5vcmlnW2NvbW11dGUub3JpZyRkaXN0YW5jZSAhPSAwLF0NCg0KbW9kZWwgPC0gZ2xtKGRhdGEgPSBjb21tdXRlLm9yaWcsIGZvcm11bGEgPSBpbnRlcmFjdGlvbiB+IGxvZyhyZXNpZGVudHMpICsgbG9nKGpvYnMpICsgbG9nKGRpc3RhbmNlKSwNCiAgICAgICAgICAgICBmYW1pbHkgPSBwb2lzc29uKCkpDQpyMiA8LSBmdW5jdGlvbihlbXBpcmljYWwsIGZpdHRlZCkgew0KICByZXR1cm4oY29yKGVtcGlyaWNhbCwgZml0dGVkKV4yKQ0KfQ0KcjIoY29tbXV0ZS5vcmlnJGludGVyYWN0aW9uLCBmaXR0ZWQobW9kZWwpKQ0KDQp0aWR5KG1vZGVsKQ0KDQpgYGANCg0KDQpUaGUgUnNxdWFyZWQgdmFsdWUgaXMgdmVyeSBsb3cuDQpFdmVuIG9uIHRoZSBsb2cgbG9nIGdyYXBoLCB3aGljaCBzaG93ZWQgYSByZWxhdGl2ZWx5IGNvcnJlbGF0ZWQgZ3JhcGggZm9yIG9ubHkgVXRyZWNodCwgZGlkIG5vdCBoYXZlIGFueSBwcm9taW5lbnQgaW5zaWdodHMgd2hlbiBwbG90dGVkIHdpdGggZGF0YSBvZiB0aGUgd2hvbGUgTmV0aGVybGFuZHMuIFRoaXMgc2hvd3MgdGhhdCB0aGUgZGlzdGFuY2UgZG9lcyBub3QgaGF2ZSBhIGNsb3NlIGNvcnJlbGF0aW9uIHdpdGggdGhlIGFtb3VudCBvZiBpbnRlcmFjdGlvbnMgYSBsb2NhdGlvbiBoYXZlIGZvciB0aGUgd2hvbGUgb2YgTmV0aGVybGFuZHMuIFRoaXMgc2hvdWxkIGJlIGJlY2F1c2UgdGhlIHBsYWNlcyBhcmUgbm90IGludGVyY2hhbmdlYWJsZSwgaWYgb25lJ3Mgam9iIHdhcyBpbiBBbXN0ZXJkYW0gb25lIHdvdWxkIGhhdmUgdG8gZ28gdG8gQW1zdGVyZGFtIHRvIHdvcmsgZXZlbiBpZiB0aGUgZGlzdGFuY2UgaXMgbG9uZy4gDQoNCkl0IHN0aWxsLCBob3dldmVyLCBzZWVtcyB0byBmaXQgcG9pc3NvbiBxdWl0ZSBuaWNlbHkuDQoNCg0K