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 Cmd+Shift+Enter.

install.packages(c("sf","devtools","tidyverse","broom"))
devtools::install_github("tidyverse/ggplot2")
lapply(c("sf","devtools","tidyverse","broom","ggplot2"),library,character.only=TRUE)
setwd("/Users/blossomtang/Documents/40.hass Making Maps/02221-Lab10")
munis <- st_read("gem_2016.geojson",crs = 28992)
munis <- munis %>%
  filter(WATER == 'NEE') %>%
  select(GM_CODE, GM_NAAM)

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))

The st_intersects function identifies whether the area of utrecht and the centroid of the selected city in munis intersect. Setting sparse=F returns a list of whether each row intersects or not, while setting it to T would only return the indexes of the rows that do intersect.

commute <- read_csv("commuting.csv") %>%
  select(source,sink,weight) %>%
  rename(interaction=weight)
Parsed with column specification:
cols(
  source = col_integer(),
  sink = col_integer(),
  count = col_integer(),
  weight = col_integer()
)

Now the %in% can match values as they both exist.

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)
Error in mutate_impl(.data, dots) : 
  Evaluation error: no applicable method for 'st_geometry' applied to an object of class "NULL".

This chunk matches the coordinates to the entries in commute, groups them by rows, combines the coordinates into a geometry column, then converts the data frame to a simple features (sf) object, which is a collection of points.

The region where many thick lines intersect could be a big city where people travel to every day for work. Alternatively, it could be a very dense housing area where many people live, and offices are for some reason in the outskirts of Utrecht.

residents <- read_csv("residents.csv") %>%
  select(id,weight) 
jobs <- read_csv("jobs.csv") %>%
  select(id,weight)
#couldn't get st_distance to work on my computer, so I got a distance matrix from a classmate
#dist <- st_distance(x=munis.centroid, y=munis.centroid)

#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
# ) 
# dist <- dist %>%
#   map_dfc(as.vector) %>%
#   mutate(source=as.numeric(source)) %>%
#   mutate(sink=as.numeric(sink))
dist <- read.csv("dist.csv")

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)

ggplot(commute, aes(interaction, distance)) + geom_point()
ggplot(commute, aes(log(interaction),log(distance))) + geom_point()
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 R squared value is 0.5003, which means the linear regression only explains about half the variation in data. But each factor has a very small p-value, indicating that they are each important to the model.

summary(model)

Call:
glm(formula = interaction ~ log(residents) + log(jobs) + log(distance), 
    family = poisson(), data = commute)

Deviance Residuals: 
    Min       1Q   Median       3Q      Max  
-94.452  -13.305   -7.521    2.780  119.174  

Coefficients:
                 Estimate Std. Error z value Pr(>|z|)    
(Intercept)     3.3571141  0.0211541   158.7   <2e-16 ***
log(residents)  0.8585323  0.0009390   914.3   <2e-16 ***
log(jobs)       0.9836158  0.0006917  1422.1   <2e-16 ***
log(distance)  -1.5120231  0.0017421  -867.9   <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: 3474337  on 518  degrees of freedom
Residual deviance:  251357  on 515  degrees of freedom
AIC: 255608

Number of Fisher Scoring iterations: 5

The R^2 value is 0.864, better than the previous linear model. From the summary of the model, we see that each of the factors are still significant as their p-values are very small. Here we are doing a level-log regression, so the coefficients mean that, say, if we increase number of residents by 1%, the interaction would increase by 0.859/100 units.

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()

The line widths are more balanced now, with previously invisible connections becoming more visible.

Assignment

Visualizing all commuting relations

Summary of commuting destinations

commute2 %>%
  arrange(desc(count))
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.4171,50.3319,465.5524,-0.398957,0.343988,-1.87740,4.0725 +units=m +no_defs

The municipalities with the most incoming number of commuters are Amsterdam, Rotterdam, and ’s-Gravenhage.

Spatial interaction

#add residents and jobs
commute <- commute %>%
  left_join(dist.w) %>%
  left_join(residents, by = c('source'='id')) %>%
  rename(residents=weight) %>%
  left_join(jobs, by=c('sink'='id')) %>%
  rename(jobs=weight)
Error in UseMethod("left_join") : 
  no applicable method for 'left_join' applied to an object of class "c('gg', 'ggplot')"

The R^2 value is 0.183, much lower than the fit for just Utrecht. This could be because there are factors other than residents, jobs, and distance that affect interaction when looking at the scale of a whole country instead of a city. It would take more for people to travel to a different city, maybe to visit family or there could be patterns of tourism or logistics aside from traveling for work. The three factors all have smaller magnitude coefficients than before, explaining less of the interaction variation although they are still significant. However, their signs remain the same, which makes sense - there is more interaction if there are more residents and jobs, but less between places far from each other.

The largest residuals are at a couple of cities on the West side of the Netherlands. There are also a lot of negative residuals around North Holland and the places with high interaction.

#finding top 3 based on fitted values
commute %>%
  mutate(fitted = fitted(model)) %>%
  group_by(sink) %>%
  summarize(sum = sum(fitted)) %>%
  left_join(as.data.frame(munis.w.centroid3), by=c("sink"="id")) %>%
  arrange(desc(sum))
Simple feature collection with 388 features and 3 fields
Active geometry column: geometry.x
geometry type:  GEOMETRY
dimension:      XY
bbox:           xmin: 24537.79 ymin: 309545.2 xmax: 271400.9 ymax: 607189.8
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.4171,50.3319,465.5524,-0.398957,0.343988,-1.87740,4.0725 +units=m +no_defs

This is a table of the top three destinations with the highest fitted interaction. When ordered by fitted interaction instead of actual counts, the top three destinations are Rotterdam, Amsterdam, and Utrecht. Maybe the fitting over-estimates the interaction for Rotterdam because it is quite central or has a large population/number of jobs without people actually traveling there.

LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4gCgpUcnkgZXhlY3V0aW5nIHRoaXMgY2h1bmsgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4gCgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcyhjKCJzZiIsImRldnRvb2xzIiwidGlkeXZlcnNlIiwiYnJvb20iKSkKZGV2dG9vbHM6Omluc3RhbGxfZ2l0aHViKCJ0aWR5dmVyc2UvZ2dwbG90MiIpCmxhcHBseShjKCJzZiIsImRldnRvb2xzIiwidGlkeXZlcnNlIiwiYnJvb20iLCJnZ3Bsb3QyIiksbGlicmFyeSxjaGFyYWN0ZXIub25seT1UUlVFKQoKYGBgCmBgYHtyfQpzZXR3ZCgiL1VzZXJzL2Jsb3Nzb210YW5nL0RvY3VtZW50cy80MC5oYXNzIE1ha2luZyBNYXBzLzAyMjIxLUxhYjEwIikKbXVuaXMgPC0gc3RfcmVhZCgiZ2VtXzIwMTYuZ2VvanNvbiIsY3JzID0gMjg5OTIpCm11bmlzIDwtIG11bmlzICU+JQogIGZpbHRlcihXQVRFUiA9PSAnTkVFJykgJT4lCiAgc2VsZWN0KEdNX0NPREUsIEdNX05BQU0pCgpwcm92aW5jZXMgPC0gc3RfcmVhZCgicHJvdmluY2VzLmdlb2pzb24iKSAlPiUKICBzdF90cmFuc2Zvcm0oY3JzID0gMjg5OTIpCnV0cmVjaHQgPC0gcHJvdmluY2VzICU+JQogIGZpbHRlcihuYW1lPT0nVXRyZWNodCcpCm11bmlzIDwtIG11bmlzICU+JQogIGZpbHRlcihzdF9pbnRlcnNlY3RzKHV0cmVjaHQsc3RfY2VudHJvaWQobXVuaXMpLHNwYXJzZT1GKSkKCmBgYAoKVGhlIHN0X2ludGVyc2VjdHMgZnVuY3Rpb24gaWRlbnRpZmllcyB3aGV0aGVyIHRoZSBhcmVhIG9mIHV0cmVjaHQgYW5kIHRoZSBjZW50cm9pZCBvZiB0aGUgc2VsZWN0ZWQgY2l0eSBpbiBtdW5pcyBpbnRlcnNlY3QuIFNldHRpbmcgc3BhcnNlPUYgcmV0dXJucyBhIGxpc3Qgb2Ygd2hldGhlciBlYWNoIHJvdyBpbnRlcnNlY3RzIG9yIG5vdCwgd2hpbGUgc2V0dGluZyBpdCB0byBUIHdvdWxkIG9ubHkgcmV0dXJuIHRoZSBpbmRleGVzIG9mIHRoZSByb3dzIHRoYXQgZG8gaW50ZXJzZWN0LgoKYGBge3J9CmNvbW11dGUgPC0gcmVhZF9jc3YoImNvbW11dGluZy5jc3YiKSAlPiUKICBzZWxlY3Qoc291cmNlLHNpbmssd2VpZ2h0KSAlPiUKICByZW5hbWUoaW50ZXJhY3Rpb249d2VpZ2h0KQoKI2NvbW11dGUgPC0gY29tbXV0ZSAlPiUKIyAgZmlsdGVyKHNvdXJjZSAlaW4lIG11bmlzJGlkICYgc2luayAlaW4lIG11bmlzJGlkKQojY29tbXV0ZSBoYXMgMCByb3dzIG5vdyBiZWNhdXNlIG11bmlzIGRvZXMgbm90IGhhdmUgYW4gaWQgY29sdW1uCgptdW5pcyA8LSBtdW5pcyAlPiUKICBtdXRhdGUoaWQ9YXMubnVtZXJpYyhzdHJfcmVwbGFjZShHTV9DT0RFLCAiR00iLCIiKSkpCmNvbW11dGUgPC0gY29tbXV0ZSAlPiUKIGZpbHRlcihzb3VyY2UgJWluJSBtdW5pcyRpZCAmIHNpbmsgJWluJSBtdW5pcyRpZCkKYGBgCk5vdyB0aGUgJWluJSBjYW4gbWF0Y2ggdmFsdWVzIGFzIHRoZXkgYm90aCBleGlzdC4KYGBge3J9Cm11bmlzLmNlbnRyb2lkIDwtIHN0X2NlbnRyb2lkKG11bmlzKSAlPiUKICBzZWxlY3QoaWQpCgpjb21tdXRlIDwtIGNvbW11dGUgJT4lCiAgbGVmdF9qb2luKG11bmlzLmNlbnRyb2lkLCBieT1jKCJzb3VyY2UiPSJpZCIpKSAlPiUKICBsZWZ0X2pvaW4obXVuaXMuY2VudHJvaWQsIGJ5PWMoInNpbmsiPSJpZCIpKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKGdlb21ldHJ5PXN0X2NvbWJpbmUoYyhnZW9tZXRyeS54LGdlb21ldHJ5LnkpKSAlPiUgc3RfY2FzdCgiTElORVNUUklORyIpKSAlPiUKICBzZWxlY3QoLWdlb21ldHJ5LngsIC1nZW9tZXRyeS55KSAlPiUKICBzdF9hc19zZihjcnM9Mjg5OTIpCmBgYApUaGlzIGNodW5rIG1hdGNoZXMgdGhlIGNvb3JkaW5hdGVzIHRvIHRoZSBlbnRyaWVzIGluIGNvbW11dGUsIGdyb3VwcyB0aGVtIGJ5IHJvd3MsIGNvbWJpbmVzIHRoZSBjb29yZGluYXRlcyBpbnRvIGEgZ2VvbWV0cnkgY29sdW1uLCB0aGVuIGNvbnZlcnRzIHRoZSBkYXRhIGZyYW1lIHRvIGEgc2ltcGxlIGZlYXR1cmVzIChzZikgb2JqZWN0LCB3aGljaCBpcyBhIGNvbGxlY3Rpb24gb2YgcG9pbnRzLgoKYGBge3J9CmdncGxvdChjb21tdXRlKSArIGdlb21fc2YoKQpnZ3Bsb3QobXVuaXMpICsgZ2VvbV9zZigpICsgZ2VvbV9zZihkYXRhPWNvbW11dGUpCmNvbW11dGUgPC0gY29tbXV0ZSAlPiUKICBmaWx0ZXIoc2luayAhPSBzb3VyY2UpICU+JQogIGZpbHRlcihpbnRlcmFjdGlvbiA+IDIwKSAlPiUKICBtdXRhdGUobGluZVdpZHRoPWFzLm51bWVyaWMoaW50ZXJhY3Rpb24pL21heChhcy5udW1lcmljKGludGVyYWN0aW9uKSkqMTApCmdncGxvdChjb21tdXRlKSArIGdlb21fc2YoYWVzKHNpemU9bGluZVdpZHRoKSkgKyBzY2FsZV9zaXplX2lkZW50aXR5KCkKYGBgClRoZSByZWdpb24gd2hlcmUgbWFueSB0aGljayBsaW5lcyBpbnRlcnNlY3QgY291bGQgYmUgYSBiaWcgY2l0eSB3aGVyZSBwZW9wbGUgdHJhdmVsIHRvIGV2ZXJ5IGRheSBmb3Igd29yay4gQWx0ZXJuYXRpdmVseSwgaXQgY291bGQgYmUgYSB2ZXJ5IGRlbnNlIGhvdXNpbmcgYXJlYSB3aGVyZSBtYW55IHBlb3BsZSBsaXZlLCBhbmQgb2ZmaWNlcyBhcmUgZm9yIHNvbWUgcmVhc29uIGluIHRoZSBvdXRza2lydHMgb2YgVXRyZWNodC4KCmBgYHtyfQpyZXNpZGVudHMgPC0gcmVhZF9jc3YoInJlc2lkZW50cy5jc3YiKSAlPiUKICBzZWxlY3QoaWQsd2VpZ2h0KSAKam9icyA8LSByZWFkX2Nzdigiam9icy5jc3YiKSAlPiUKICBzZWxlY3QoaWQsd2VpZ2h0KQojY291bGRuJ3QgZ2V0IHN0X2Rpc3RhbmNlIHRvIHdvcmsgb24gbXkgY29tcHV0ZXIsIHNvIEkgZ290IGEgZGlzdGFuY2UgbWF0cml4IGZyb20gYSBjbGFzc21hdGUKI2Rpc3QgPC0gc3RfZGlzdGFuY2UoeD1tdW5pcy5jZW50cm9pZCwgeT1tdW5pcy5jZW50cm9pZCkKCiNyb3duYW1lcyhkaXN0KSA8LSBtdW5pcy5jZW50cm9pZCRpZAojY29sbmFtZXMoZGlzdCkgPC0gbXVuaXMuY2VudHJvaWQkaWQKI2Rpc3QgPC0gbGlzdCgKIyAgIHNvdXJjZSA9IHJvd25hbWVzKGRpc3QpW3JvdyhkaXN0KV0gJXx8JSByb3coZGlzdCksCiMgICBzaW5rID0gY29sbmFtZXMoZGlzdClbY29sKGRpc3QpXSAlfHwlIGNvbChkaXN0KSwKIyAgIGRpc3RhbmNlID0gZGlzdAojICkgCiMgZGlzdCA8LSBkaXN0ICU+JQojICAgbWFwX2RmYyhhcy52ZWN0b3IpICU+JQojICAgbXV0YXRlKHNvdXJjZT1hcy5udW1lcmljKHNvdXJjZSkpICU+JQojICAgbXV0YXRlKHNpbms9YXMubnVtZXJpYyhzaW5rKSkKZGlzdCA8LSByZWFkLmNzdigiZGlzdC5jc3YiKQoKY29tbXV0ZSA8LSBjb21tdXRlICU+JQogIGxlZnRfam9pbihkaXN0KSAlPiUKICBsZWZ0X2pvaW4ocmVzaWRlbnRzLCBieSA9IGMoJ3NvdXJjZSc9J2lkJykpICU+JQogIHJlbmFtZShyZXNpZGVudHM9d2VpZ2h0KSAlPiUKICBsZWZ0X2pvaW4oam9icywgYnk9Yygnc2luayc9J2lkJykpICU+JQogIHJlbmFtZShqb2JzPXdlaWdodCkKCmdncGxvdChjb21tdXRlLCBhZXMoaW50ZXJhY3Rpb24sIGRpc3RhbmNlKSkgKyBnZW9tX3BvaW50KCkKZ2dwbG90KGNvbW11dGUsIGFlcyhsb2coaW50ZXJhY3Rpb24pLGxvZyhkaXN0YW5jZSkpKSArIGdlb21fcG9pbnQoKQoKYGBgCmBgYHtyfQpsbShkYXRhID0gY29tbXV0ZSwgZm9ybXVsYSA9IGludGVyYWN0aW9uIH4gcmVzaWRlbnRzICsgam9icyArIGRpc3RhbmNlKSAlPiUgc3VtbWFyeSgpCmBgYApUaGUgUiBzcXVhcmVkIHZhbHVlIGlzIDAuNTAwMywgd2hpY2ggbWVhbnMgdGhlIGxpbmVhciByZWdyZXNzaW9uIG9ubHkgZXhwbGFpbnMgYWJvdXQgaGFsZiB0aGUgdmFyaWF0aW9uIGluIGRhdGEuIEJ1dCBlYWNoIGZhY3RvciBoYXMgYSB2ZXJ5IHNtYWxsIHAtdmFsdWUsIGluZGljYXRpbmcgdGhhdCB0aGV5IGFyZSBlYWNoIGltcG9ydGFudCB0byB0aGUgbW9kZWwuCgpgYGB7cn0KZ2dwbG90KGNvbW11dGUsYWVzKGludGVyYWN0aW9uKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCgptb2RlbCA8LSBnbG0oZGF0YSA9IGNvbW11dGUsIGZvcm11bGEgPSBpbnRlcmFjdGlvbiB+IGxvZyhyZXNpZGVudHMpICsgbG9nKGpvYnMpICsgbG9nKGRpc3RhbmNlKSwgZmFtaWx5ID0gcG9pc3NvbigpKQoKcjIgPC0gZnVuY3Rpb24oZW1waXJpY2FsLCBmaXR0ZWQpIHsKICByZXR1cm4oY29yKGVtcGlyaWNhbCwgZml0dGVkKV4yKQp9CnIyKGNvbW11dGUkaW50ZXJhY3Rpb24sIGZpdHRlZChtb2RlbCkpCnN1bW1hcnkobW9kZWwpCgpgYGAKVGhlIFJeMiB2YWx1ZSBpcyAwLjg2NCwgYmV0dGVyIHRoYW4gdGhlIHByZXZpb3VzIGxpbmVhciBtb2RlbC4gRnJvbSB0aGUgc3VtbWFyeSBvZiB0aGUgbW9kZWwsIHdlIHNlZSB0aGF0IGVhY2ggb2YgdGhlIGZhY3RvcnMgYXJlIHN0aWxsIHNpZ25pZmljYW50IGFzIHRoZWlyIHAtdmFsdWVzIGFyZSB2ZXJ5IHNtYWxsLiBIZXJlIHdlIGFyZSBkb2luZyBhIGxldmVsLWxvZyByZWdyZXNzaW9uLCBzbyB0aGUgY29lZmZpY2llbnRzIG1lYW4gdGhhdCwgc2F5LCBpZiB3ZSBpbmNyZWFzZSBudW1iZXIgb2YgcmVzaWRlbnRzIGJ5IDElLCB0aGUgaW50ZXJhY3Rpb24gd291bGQgaW5jcmVhc2UgYnkgMC44NTkvMTAwIHVuaXRzLgoKYGBge3J9CmNvbW11dGUgPC0gY29tbXV0ZSAlPiUKICBtdXRhdGUoZml0dGVkID0gZml0dGVkKG1vZGVsKSkgJT4lCiAgbXV0YXRlKHJlc2lkdWFsID0gaW50ZXJhY3Rpb24gLSBmaXR0ZWQpICU+JQogIG11dGF0ZShyZXNpZHVhbFNpZ24gPSBzaWduKHJlc2lkdWFsKSkKCmNvbW11dGUgJT4lCiAgbXV0YXRlKGxpbmVXaWR0aCA9IGZpdHRlZCAvIG1heChmaXR0ZWQpKjEwKSAlPiUKICBnZ3Bsb3QoKSArIGdlb21fc2YoYWVzKHNpemU9bGluZVdpZHRoKSkgKwogIHNjYWxlX3NpemVfaWRlbnRpdHkoKQpgYGAKVGhlIGxpbmUgd2lkdGhzIGFyZSBtb3JlIGJhbGFuY2VkIG5vdywgd2l0aCBwcmV2aW91c2x5IGludmlzaWJsZSBjb25uZWN0aW9ucyBiZWNvbWluZyBtb3JlIHZpc2libGUuCmBgYHtyfQpjb21tdXRlICU+JQogIG11dGF0ZShsaW5lV2lkdGggPSBhYnMocmVzaWR1YWwpL21heChyZXNpZHVhbCkqMTApICU+JQogIGdncGxvdCgpICsgZ2VvbV9zZihhZXMoY29sb3IgPSBmYWN0b3IocmVzaWR1YWxTaWduKSxzaXplPWxpbmVXaWR0aCkpICsgc2NhbGVfc2l6ZV9pZGVudGl0eSgpCmBgYAoKPGI+QXNzaWdubWVudDwvYj4KClZpc3VhbGl6aW5nIGFsbCBjb21tdXRpbmcgcmVsYXRpb25zCmBgYHtyfQptdW5pcy53IDwtIHN0X3JlYWQoImdlbV8yMDE2Lmdlb2pzb24iLGNycyA9IDI4OTkyKQptdW5pcy53IDwtIG11bmlzLncgJT4lCiAgZmlsdGVyKFdBVEVSID09ICdORUUnKSAlPiUKICBzZWxlY3QoR01fQ09ERSwgR01fTkFBTSkKCmNvbW11dGUgPC0gcmVhZF9jc3YoImNvbW11dGluZy5jc3YiKSAlPiUKICBzZWxlY3Qoc291cmNlLHNpbmssd2VpZ2h0LGNvdW50KSAlPiUKICByZW5hbWUoaW50ZXJhY3Rpb249d2VpZ2h0KSAlPiUKICBmaWx0ZXIoY291bnQgPiA3NTApCgptdW5pcy53IDwtIG11bmlzLncgJT4lCiAgbXV0YXRlKGlkPWFzLm51bWVyaWMoc3RyX3JlcGxhY2UoR01fQ09ERSwgIkdNIiwiIikpKQpjb21tdXRlIDwtIGNvbW11dGUgJT4lCiBmaWx0ZXIoc291cmNlICVpbiUgbXVuaXMudyRpZCAmIHNpbmsgJWluJSBtdW5pcy53JGlkKQoKbXVuaXMudy5jZW50cm9pZCA8LSBzdF9jZW50cm9pZChtdW5pcy53KSAlPiUKICBzZWxlY3QoaWQpCgpjb21tdXRlIDwtIGNvbW11dGUgJT4lCiAgbGVmdF9qb2luKG11bmlzLncuY2VudHJvaWQsIGJ5PWMoInNvdXJjZSI9ImlkIikpICU+JQogIGxlZnRfam9pbihtdW5pcy53LmNlbnRyb2lkLCBieT1jKCJzaW5rIj0iaWQiKSkgJT4lCiAgcm93d2lzZSgpICU+JQogIG11dGF0ZShnZW9tZXRyeT1zdF9jb21iaW5lKGMoZ2VvbWV0cnkueCxnZW9tZXRyeS55KSkgJT4lIHN0X2Nhc3QoIkxJTkVTVFJJTkciKSkgJT4lCiAgc2VsZWN0KC1nZW9tZXRyeS54LCAtZ2VvbWV0cnkueSkgJT4lCiAgc3RfYXNfc2YoY3JzPTI4OTkyKQoKY29tbXV0ZSA8LSBjb21tdXRlICU+JQogIGZpbHRlcihzaW5rICE9IHNvdXJjZSkgJT4lCiAgZmlsdGVyKGludGVyYWN0aW9uID4gMjApICU+JQogIG11dGF0ZShsaW5lV2lkdGg9YXMubnVtZXJpYyhpbnRlcmFjdGlvbikvbWF4KGFzLm51bWVyaWMoaW50ZXJhY3Rpb24pKSoxMCkgCmdncGxvdChtdW5pcy53KSArIGdlb21fc2YoKSArIGdlb21fc2YoZGF0YT1jb21tdXRlLGFlcyhzaXplPWxpbmVXaWR0aCkpICsgc2NhbGVfc2l6ZV9pZGVudGl0eSgpCgpgYGAKClN1bW1hcnkgb2YgY29tbXV0aW5nIGRlc3RpbmF0aW9ucwpgYGB7cn0KCm11bmlzLncgPC0gc3RfcmVhZCgiZ2VtXzIwMTYuZ2VvanNvbiIsY3JzID0gMjg5OTIpCm11bmlzLncgPC0gbXVuaXMudyAlPiUKICBmaWx0ZXIoV0FURVIgPT0gJ05FRScpICU+JQogIHNlbGVjdChHTV9DT0RFLCBHTV9OQUFNKQoKY29tbXV0ZTIgPC0gcmVhZF9jc3YoImNvbW11dGluZy5jc3YiKSAlPiUKICBzZWxlY3Qoc291cmNlLHNpbmssd2VpZ2h0LGNvdW50KSAlPiUKICByZW5hbWUoaW50ZXJhY3Rpb249d2VpZ2h0KQoKbXVuaXMudyA8LSBtdW5pcy53ICU+JQogIG11dGF0ZShpZD1hcy5udW1lcmljKHN0cl9yZXBsYWNlKEdNX0NPREUsICJHTSIsIiIpKSkKY29tbXV0ZSA8LSBjb21tdXRlICU+JQogZmlsdGVyKHNvdXJjZSAlaW4lIG11bmlzLnckaWQgJiBzaW5rICVpbiUgbXVuaXMudyRpZCkKCm11bmlzLncuY2VudHJvaWQyIDwtIHN0X2NlbnRyb2lkKG11bmlzLncpICU+JQogIHNlbGVjdChpZCxHTV9OQUFNKQoKY29tbXV0ZTIgPC0gY29tbXV0ZTIgJT4lCiAgbGVmdF9qb2luKG11bmlzLncuY2VudHJvaWQyLCBieT1jKCJzb3VyY2UiPSJpZCIpKSAlPiUKICBsZWZ0X2pvaW4obXVuaXMudy5jZW50cm9pZDIsIGJ5PWMoInNpbmsiPSJpZCIpKSAlPiUKICByb3d3aXNlKCkgJT4lCiAgbXV0YXRlKGdlb21ldHJ5PXN0X2NvbWJpbmUoYyhnZW9tZXRyeS54LGdlb21ldHJ5LnkpKSAlPiUgc3RfY2FzdCgiTElORVNUUklORyIpKSAlPiUKICBzZWxlY3QoLWdlb21ldHJ5LngsIC1nZW9tZXRyeS55KSAlPiUKICBzdF9hc19zZihjcnM9Mjg5OTIpCgpjb21tdXRlMiA8LSBjb21tdXRlMiAlPiUKICBncm91cF9ieShHTV9OQUFNLnkpICU+JQogIHN1bW1hcml6ZShjb3VudCA9IHN1bShjb3VudCkpCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikKbGlicmFyeShkcGx5cikKY29tbXV0ZTIgJT4lCiAgYXJyYW5nZShkZXNjKGNvdW50KSkKCgoKCmBgYApUaGUgbXVuaWNpcGFsaXRpZXMgd2l0aCB0aGUgbW9zdCBpbmNvbWluZyBudW1iZXIgb2YgY29tbXV0ZXJzIGFyZSBBbXN0ZXJkYW0sIFJvdHRlcmRhbSwgYW5kICdzLUdyYXZlbmhhZ2UuCgpTcGF0aWFsIGludGVyYWN0aW9uCmBgYHtyfQojZGlzdCA8LSBzdF9kaXN0YW5jZSh4PW11bmlzLmNlbnRyb2lkLncsIHk9bXVuaXMuY2VudHJvaWQudykKCiNkaXN0YW5jZSBtYXRyaXgKIyBkaXN0MiA8LSByZWFkLmNzdigiZGlzdG9yaWcuY3N2IikKIyAKIyByb3duYW1lcyhkaXN0MikgPC0gbXVuaXMudy5jZW50cm9pZCRpZAojIGNvbG5hbWVzKGRpc3QyKSA8LSBtdW5pcy53LmNlbnRyb2lkJGlkCiMgZGlzdDIgPC0gbGlzdCgKIyAgIHNvdXJjZSA9IHJvd25hbWVzKGRpc3QyKVtyb3coZGlzdDIpXSAlfHwlIHJvdyhkaXN0MiksCiMgICBzaW5rID0gY29sbmFtZXMoZGlzdDIpW2NvbChkaXN0MildICV8fCUgY29sKGRpc3QyKSwKIyAgIGRpc3RhbmNlID0gZGlzdDIKIyApIAojIGRpc3QyICU+JQojICAgbWFwX2RmYyhhcy52ZWN0b3IpICU+JQojICAgbXV0YXRlKHNvdXJjZT1hcy5udW1lcmljKHNvdXJjZSkpICU+JQojICAgbXV0YXRlKHNpbms9YXMubnVtZXJpYyhzaW5rKSkKZGlzdC53IDwtIHJlYWQuY3N2KCJkaXN0Y29vbC5jc3YiKQoKCiNhZGQgcmVzaWRlbnRzIGFuZCBqb2JzCmNvbW11dGUgPC0gY29tbXV0ZSAlPiUKICBsZWZ0X2pvaW4oZGlzdC53KSAlPiUKICBsZWZ0X2pvaW4ocmVzaWRlbnRzLCBieSA9IGMoJ3NvdXJjZSc9J2lkJykpICU+JQogIHJlbmFtZShyZXNpZGVudHM9d2VpZ2h0KSAlPiUKICBsZWZ0X2pvaW4oam9icywgYnk9Yygnc2luayc9J2lkJykpICU+JQogIHJlbmFtZShqb2JzPXdlaWdodCkKCm1vZGVsIDwtIGdsbShkYXRhID0gY29tbXV0ZSwgZm9ybXVsYSA9IGludGVyYWN0aW9uIH4gbG9nKHJlc2lkZW50cykgKyBsb2coam9icykgKyBsb2coZGlzdGFuY2UpLCBmYW1pbHkgPSBwb2lzc29uKCkpCgpyMihjb21tdXRlJGludGVyYWN0aW9uLCBmaXR0ZWQobW9kZWwpKQpzdW1tYXJ5KG1vZGVsKQpgYGAKVGhlIFJeMiB2YWx1ZSBpcyAwLjE4MywgbXVjaCBsb3dlciB0aGFuIHRoZSBmaXQgZm9yIGp1c3QgVXRyZWNodC4gVGhpcyBjb3VsZCBiZSBiZWNhdXNlIHRoZXJlIGFyZSBmYWN0b3JzIG90aGVyIHRoYW4gcmVzaWRlbnRzLCBqb2JzLCBhbmQgZGlzdGFuY2UgdGhhdCBhZmZlY3QgaW50ZXJhY3Rpb24gd2hlbiBsb29raW5nIGF0IHRoZSBzY2FsZSBvZiBhIHdob2xlIGNvdW50cnkgaW5zdGVhZCBvZiBhIGNpdHkuIEl0IHdvdWxkIHRha2UgbW9yZSBmb3IgcGVvcGxlIHRvIHRyYXZlbCB0byBhIGRpZmZlcmVudCBjaXR5LCBtYXliZSB0byB2aXNpdCBmYW1pbHkgb3IgdGhlcmUgY291bGQgYmUgcGF0dGVybnMgb2YgdG91cmlzbSBvciBsb2dpc3RpY3MgYXNpZGUgZnJvbSB0cmF2ZWxpbmcgZm9yIHdvcmsuIFRoZSB0aHJlZSBmYWN0b3JzIGFsbCBoYXZlIHNtYWxsZXIgbWFnbml0dWRlIGNvZWZmaWNpZW50cyB0aGFuIGJlZm9yZSwgZXhwbGFpbmluZyBsZXNzIG9mIHRoZSBpbnRlcmFjdGlvbiB2YXJpYXRpb24gYWx0aG91Z2ggdGhleSBhcmUgc3RpbGwgc2lnbmlmaWNhbnQuIEhvd2V2ZXIsIHRoZWlyIHNpZ25zIHJlbWFpbiB0aGUgc2FtZSwgd2hpY2ggbWFrZXMgc2Vuc2UgLSB0aGVyZSBpcyBtb3JlIGludGVyYWN0aW9uIGlmIHRoZXJlIGFyZSBtb3JlIHJlc2lkZW50cyBhbmQgam9icywgYnV0IGxlc3MgYmV0d2VlbiBwbGFjZXMgZmFyIGZyb20gZWFjaCBvdGhlci4KCmBgYHtyfQpjb21tdXRlICU+JQogIG11dGF0ZShmaXR0ZWQgPSBmaXR0ZWQobW9kZWwpKSAlPiUKICBtdXRhdGUocmVzaWR1YWwgPSBpbnRlcmFjdGlvbiAtIGZpdHRlZCkgJT4lCiAgbXV0YXRlKHJlc2lkdWFsU2lnbiA9IHNpZ24ocmVzaWR1YWwpKSAlPiUKICBtdXRhdGUobGluZVdpZHRoID0gYWJzKHJlc2lkdWFsKS9tYXgocmVzaWR1YWwpKjUpICU+JSAgCiAgZ2dwbG90KCkgKyBnZW9tX3NmKGFlcyhjb2xvciA9IGZhY3RvcihyZXNpZHVhbFNpZ24pLHNpemU9bGluZVdpZHRoKSkgKwogIHNjYWxlX3NpemVfaWRlbnRpdHkoKQoKYGBgCgpUaGUgbGFyZ2VzdCByZXNpZHVhbHMgYXJlIGF0IGEgY291cGxlIG9mIGNpdGllcyBvbiB0aGUgV2VzdCBzaWRlIG9mIHRoZSBOZXRoZXJsYW5kcy4gVGhlcmUgYXJlIGFsc28gYSBsb3Qgb2YgbmVnYXRpdmUgcmVzaWR1YWxzIGFyb3VuZCBOb3J0aCBIb2xsYW5kIGFuZCB0aGUgcGxhY2VzIHdpdGggaGlnaCBpbnRlcmFjdGlvbi4KCmBgYHtyfQptdW5pcy53LmNlbnRyb2lkMyA8LSBzdF9jZW50cm9pZChtdW5pcy53KSAlPiUKICBzZWxlY3QoaWQsR01fTkFBTSkKI2ZpbmRpbmcgdG9wIDMgYmFzZWQgb24gZml0dGVkIHZhbHVlcwpjb21tdXRlICU+JQogIG11dGF0ZShmaXR0ZWQgPSBmaXR0ZWQobW9kZWwpKSAlPiUKICBncm91cF9ieShzaW5rKSAlPiUKICBzdW1tYXJpemUoc3VtID0gc3VtKGZpdHRlZCkpICU+JQogIGxlZnRfam9pbihhcy5kYXRhLmZyYW1lKG11bmlzLncuY2VudHJvaWQzKSwgYnk9Yygic2luayI9ImlkIikpICU+JQogIGFycmFuZ2UoZGVzYyhzdW0pKQoKYGBgCgpUaGlzIGlzIGEgdGFibGUgb2YgdGhlIHRvcCB0aHJlZSBkZXN0aW5hdGlvbnMgd2l0aCB0aGUgaGlnaGVzdCBmaXR0ZWQgaW50ZXJhY3Rpb24uIFdoZW4gb3JkZXJlZCBieSBmaXR0ZWQgaW50ZXJhY3Rpb24gaW5zdGVhZCBvZiBhY3R1YWwgY291bnRzLCB0aGUgdG9wIHRocmVlIGRlc3RpbmF0aW9ucyBhcmUgUm90dGVyZGFtLCBBbXN0ZXJkYW0sIGFuZCBVdHJlY2h0LiBNYXliZSB0aGUgZml0dGluZyBvdmVyLWVzdGltYXRlcyB0aGUgaW50ZXJhY3Rpb24gZm9yIFJvdHRlcmRhbSBiZWNhdXNlIGl0IGlzIHF1aXRlIGNlbnRyYWwgb3IgaGFzIGEgbGFyZ2UgcG9wdWxhdGlvbi9udW1iZXIgb2Ygam9icyB3aXRob3V0IHBlb3BsZSBhY3R1YWxseSB0cmF2ZWxpbmcgdGhlcmUuCgo=