install.packages(c(tidyverse, sf, tmap, spatstat, maptools, ggplot2, sqldf)) install.packages(“spatstat.geom”, dependencies = TRUE) install.packages(“leaflet.providers”) install.packages(“markdown”, dependencies = TRUE) install.packages(“polyclip”, dependencies = TRUE) install.packages(“ggmap”, dependencies = TRUE)

library(tidyverse) 
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
-- Attaching packages ----------------------------------------------------------------------- tidyverse 1.3.1 --
v ggplot2 3.3.5     v purrr   0.3.4
v tibble  3.1.4     v dplyr   1.0.7
v tidyr   1.1.3     v stringr 1.4.0
v readr   2.0.1     v forcats 0.5.1
-- Conflicts -------------------------------------------------------------------------- tidyverse_conflicts() --
x dplyr::filter() masks stats::filter()
x dplyr::lag()    masks stats::lag()
library(sf)
Linking to GEOS 3.9.0, GDAL 3.2.1, PROJ 7.2.1
library(tmap)
Registered S3 methods overwritten by 'htmltools':
  method               from         
  print.html           tools:rstudio
  print.shiny.tag      tools:rstudio
  print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(spatstat)
Loading required package: spatstat.data
Loading required package: spatstat.geom
Registered S3 method overwritten by 'spatstat.geom':
  method     from
  print.boxx cli 
spatstat.geom 2.2-2
Loading required package: spatstat.core
Loading required package: nlme

Attaching package: ‘nlme’

The following object is masked from ‘package:dplyr’:

    collapse

Loading required package: rpart
spatstat.core 2.3-0
Loading required package: spatstat.linnet
spatstat.linnet 2.3-0

spatstat 2.2-0       (nickname: ‘That's not important right now’) 
For an introduction to spatstat, type ‘beginner’ 
library(maptools)
Checking rgeos availability: FALSE
Please note that 'maptools' will be retired by the end of 2023,
plan transition at your earliest convenience;
some functionality will be moved to 'sp'.
    Note: when rgeos is not available, polygon geometry     computations in maptools depend on gpclib,
    which has a restricted licence. It is disabled by default;
    to enable gpclib, type gpclibPermit()
library(sqldf)
Loading required package: gsubfn
Loading required package: proto
Loading required package: RSQLite
library(ggplot2)
library(spatstat.geom)
library(leaflet.providers)
library(markdown)
library(polyclip)
polyclip 1.10-0 built from Clipper C++ version 6.4.0
library(ggmap)
Google's Terms of Service: https://cloud.google.com/maps-platform/terms/.
Please cite ggmap if you use it! See citation("ggmap") for details.
# Aggregate by hour
tripsByHour <- rdfClean  %>%
  group_by(`hour`, `gender`) %>%
  summarise(trips=n()) %>%
  mutate(hour = as.numeric(hour))
`summarise()` has grouped output by 'hour'. You can override using the `.groups` argument.

#other way to do it (may not return hour as int...)
#tripsByHour <- sqldf("select hour, gender, count(hour) as trips from rdfClean group by hour, gender")

# See trips by hour (very basic plot---you can also use another plotting call from ggplot2 etc)
plot(tripsByHour$hour, tripsByHour$trips, xlab = "Hour of the Day", ylab = "Number of Trips")

# Turns it into a gis file
morningStationsGeo <- st_as_sf(morningStations, coords = c("lon", "lat"), dim = "XY", crs = 4326) #st_as_sf is Data to shape file

# Save if you feel like it! # st_write(popularStations, "popularStations.shp", delete_layer = TRUE)

# Look at the map 
tmap_mode('view')
tmap mode set to interactive viewing
tm_shape(morningStationsGeo %>% 
           filter(!is.na(trips)) %>% 
           mutate(trips = as.numeric(trips))) + tm_dots(col = "trips", style="quantile", id = "name")
######## PART 4 KERNEL DENSITY  ######### 

## Read SF for a file of boroughs
boroughs <- st_read("borough.shp")
boroughs_prj <- st_transform(boroughs, crs = 2263)

# Density map (This method doesn't allow us to see the background map)
df.size.proj <- st_transform(morningStationsGeo, crs = 2263)
p.sp <- as(df.size.proj, "Spatial")
p.ppp <- as(p.sp, "ppp")
kernelDensityMorning <- (density(p.ppp, sigma = 1000))

##Plot the two together
plot(kernelDensityMorning)
plot(boroughs_prj$geometry, add=TRUE)

##CHALLENGES FOR YOUR DENSITY MAP
##Extra: Clip the raster and clip the vector as well. (only plot the intersection of the two?) 
##Extra: Make the lines white (hint: it is now a polygon!)
##Extra: Add the top 3 (or 5) points and label them.
## Read SF for a file of boroughs
boroughs <- st_read("borough.shp")
Reading layer `borough' from data source 
  `D:\GaTech\Academic\Fall 2021\Urban Analytics\Assignment__Citibikes_export\ForClass\borough.shp' 
  using driver `ESRI Shapefile'
Simple feature collection with 5 features and 4 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -74.25559 ymin: 40.49612 xmax: -73.70001 ymax: 40.91553
Geodetic CRS:  WGS84(DD)
boroughs_prj <- st_transform(boroughs, crs = 2263)

# Density map (This method doesn't allow us to see the background map)
df.size.proj <- st_transform(morningStationsGeo, crs = 2263)
p.sp <- as(df.size.proj, "Spatial")
p.ppp <- as(p.sp, "ppp")
kernelDensityMorning <- (density(p.ppp, sigma = 1000))

##Plot the two together
plot(kernelDensityMorning)
plot(boroughs_prj$geometry, add=TRUE)


# Look at the map 
tmap_mode('view')
tmap mode set to interactive viewing
tm_shape(DifferenceGeo %>% 
           filter(!is.na(Difference)) %>% 
           mutate(trips = as.numeric(Difference))) + tm_dots(col = "Difference", style="quantile", id = "name",
           midpoint = NA)
# Top 3 stations for Morning and Evening trips

fulldata <- fulldata[order(fulldata$MorningTrips,decreasing = TRUE),]
morningstationstop3 <- fulldata[1:3,]
View(morningstationstop3)


fulldata <- fulldata[order(fulldata$EveningTrips,decreasing = TRUE),]
eveningstationstop3 <- fulldata[1:3,]
View(eveningstationstop3)
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQppbnN0YWxsLnBhY2thZ2VzKGModGlkeXZlcnNlLCBzZiwgdG1hcCwgc3BhdHN0YXQsIG1hcHRvb2xzLCBnZ3Bsb3QyLCBzcWxkZikpIGluc3RhbGwucGFja2FnZXMoInNwYXRzdGF0Lmdlb20iLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSBpbnN0YWxsLnBhY2thZ2VzKCJsZWFmbGV0LnByb3ZpZGVycyIpIGluc3RhbGwucGFja2FnZXMoIm1hcmtkb3duIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkgaW5zdGFsbC5wYWNrYWdlcygicG9seWNsaXAiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKSBpbnN0YWxsLnBhY2thZ2VzKCJnZ21hcCIsIGRlcGVuZGVuY2llcyA9IFRSVUUpDQoNCmBgYHtyIGVjaG89VFJVRX0NCiMjIyMjIyMjIFBBUlQgMSBTRVQgVVAgIyMjIyMjIyMjIA0KDQpsaWJyYXJ5KHRpZHl2ZXJzZSkgDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSh0bWFwKQ0KbGlicmFyeShzcGF0c3RhdCkNCmxpYnJhcnkobWFwdG9vbHMpDQpsaWJyYXJ5KHNxbGRmKQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeShzcGF0c3RhdC5nZW9tKQ0KbGlicmFyeShsZWFmbGV0LnByb3ZpZGVycykNCmxpYnJhcnkobWFya2Rvd24pDQpsaWJyYXJ5KHBvbHljbGlwKQ0KbGlicmFyeShnZ21hcCkNCg0KIyBFbnZpcm9ubWVudCAoY2hhbmdlIHRoaXMgcGF0aCB0byB5b3VyIGZvbGRlciB3aGVyZSB5b3Ugc3RvcmVkIHRoZSBDSVRJIEJpa2UgZGF0YSkNCiMjc2V0d2QoIkM6XFxVc2Vyc1xcY2FuZHJpczNcXERyb3Bib3hcXERyb3Bib3hcXFVyYmFuQW5hbHl0aWNzX2NsYXNzXFxjb2RlIikNCnNldHdkKCJEOi9HYVRlY2gvQWNhZGVtaWMvRmFsbCAyMDIxL1VyYmFuIEFuYWx5dGljcy9Bc3NpZ25tZW50X19DaXRpYmlrZXNfZXhwb3J0L0ZvckNsYXNzIikNCiMjIFdlIGRvd25sb2FkZWQgdGhlIGRhdGEgZnJvbSBoZXJlOiBodHRwczovL3MzLmFtYXpvbmF3cy5jb20vdHJpcGRhdGEvaW5kZXguaHRtbA0KIyMgU29tZSBjb2RlIHRha2VuIGZyb206IGh0dHA6Ly9sYWIucmFkeS51Y3NkLmVkdS9zYXd0b290aC9idXNpbmVzc19hbmFseXRpY3NfaW5fci9tYXBzLmh0bWwNCiMjIFRoYW5rcyB0byBCb24gV29vIEtvbyBmb3IgaGlzIGhlbHAuDQoNCiMgUmVhZCBkYXRhDQpyZGYgPC0gcmVhZC5jc3YoIjIwMTMwNi1jaXRpYmlrZS10cmlwZGF0YS5jc3YiKQ0KI3JkZiA8LSBzYW1wbGVfbihyZGYxLCgzMzcxOSoyKSkNCiN3cml0ZS5jc3YocmRmLCAiY2l0aWJpa2Vfc21hbGxzYW1wbGUuY3N2IikNClZpZXcocmRmKSAjdG8gdmlldyBkYXRhIHRhYmxlDQojIFRha2UgYSBsb29rIGJ5IGJpcnRoIHllYXIhDQpyZGYgJT4lIA0KICBtdXRhdGUoYmlydGhfeWVhciA9IGFzLm51bWVyaWMoJ2JpcnRoLnllYXInKSkgJT4lICNUSElTIENIQU5HRVMgQSBTVFJJTkcgSU5UTyBBIE5VTUJFUg0KICBkcGx5cjo6c2VsZWN0KC1jKCdiaXJ0aC55ZWFyJykpICAlPiUgICAgI1RISVMgREVMRVRFUyBUSEUgT0xEIENPTFVNTiANCiAgYXJyYW5nZShiaXJ0aF95ZWFyKSAjVEhJUyBTT1JUUyBPTiBCSVJUSCBZRUFSDQpgYGANCg0KYGBge3IgZWNobz1UUlVFfQ0KIyMjIyMjIyMgUEFSVCAyIEVYUExPUklORyBUSEUgREFUQSAjIyMjIyMjIyMgDQoNCiMgU3BsaXQgdGltZSwgYmlydGggeWVhciBhbmQgZmlsdGVyIGJ5IGJpcnRoIHllYXINCnJkZkNsZWFuIDwtIHJkZiAlPiUgDQogIG11dGF0ZShkYXRlID0gYXMuRGF0ZShzdGFydHRpbWUpLA0KICAgICAgICAgdGltZSA9IGZvcm1hdChhcy5QT1NJWGN0KHN0YXJ0dGltZSksIGZvcm1hdCA9ICIlSDolTTolUyIpKSAlPiUgIyMgU1BFQ0lBTCBGT1JNQVRUSU5HIFRPIEdFVCBILE0sUy4NCiAgbXV0YXRlKGhvdXIgPSBzdWJzdHJpbmcoLiR0aW1lLCBmaXJzdCA9IDEsIGxhc3QgPSAyKSkgJT4lICMjIFRSVU5DQVRFUyBUSEUgU1RSSU5HDQogIG11dGF0ZShnZW5kZXIgPSBhcy5jaGFyYWN0ZXIoZ2VuZGVyKSkgJT4lICAjIyAgQ0hBTkdFUyBOVU1CRVJTIGludG8gU1RSSU5HUw0KICBkcGx5cjo6c2VsZWN0KGRhdGUsIHRpbWUsIGhvdXIsIGV2ZXJ5dGhpbmcoKSkgJT4lICAjIyBSRU9SREVSUyBUSEUgQ09MVU1OUyANCiAgZmlsdGVyKCJiaXJ0aF95ZWFyIiA+IDE5MzApICMjIEtFRVBTIEJJUlRIIFlFQVJTIEFGVEVSIDE5MzANCg0KVmlldyhyZGZDbGVhbikNCg0KIyBBZ2dyZWdhdGUgYnkgaG91cg0KdHJpcHNCeUhvdXIgPC0gcmRmQ2xlYW4gICU+JQ0KICBncm91cF9ieShgaG91cmAsIGBnZW5kZXJgKSAlPiUNCiAgc3VtbWFyaXNlKHRyaXBzPW4oKSkgJT4lDQogIG11dGF0ZShob3VyID0gYXMubnVtZXJpYyhob3VyKSkNCg0KI290aGVyIHdheSB0byBkbyBpdCAobWF5IG5vdCByZXR1cm4gaG91ciBhcyBpbnQuLi4pDQojdHJpcHNCeUhvdXIgPC0gc3FsZGYoInNlbGVjdCBob3VyLCBnZW5kZXIsIGNvdW50KGhvdXIpIGFzIHRyaXBzIGZyb20gcmRmQ2xlYW4gZ3JvdXAgYnkgaG91ciwgZ2VuZGVyIikNCg0KIyBTZWUgdHJpcHMgYnkgaG91ciAodmVyeSBiYXNpYyBwbG90LS0teW91IGNhbiBhbHNvIHVzZSBhbm90aGVyIHBsb3R0aW5nIGNhbGwgZnJvbSBnZ3Bsb3QyIGV0YykNCnBsb3QodHJpcHNCeUhvdXIkaG91ciwgdHJpcHNCeUhvdXIkdHJpcHMsIHhsYWIgPSAiSG91ciBvZiB0aGUgRGF5IiwgeWxhYiA9ICJOdW1iZXIgb2YgVHJpcHMiKQ0KYGBgDQoNCmBgYHtyIGVjaG89VFJVRX0NCiMjIFNBTUUgUExPVA0KZ2dwbG90KHRyaXBzQnlIb3VyLCBhZXMoeD1ob3VyLCB5PXRyaXBzLCBjb2xvdXI9Z2VuZGVyKSkgKyBnZW9tX2xpbmUoKQ0KDQpgYGANCg0KYGBge3IgZWNobz1UUlVFfQ0KIyMjIyMjIyMgUEFSVCAzIFNFR01FTlRBVElPTiBBTkQgTUFQUElORyAjIyMjIyMjIyMgDQoNCiMgU2FtcGxlIGZvciBqdXN0IHRoZSBtb3JuaW5nIA0KbW9ybmluZyA8LSByZGZDbGVhbiAlPiUgZmlsdGVyKGhvdXIgJWluJSBjKCcwNycsICcwOCcsICcwOScpKQ0KDQojIEZpbmQgbW9zdCBpbXBvcnRhbnQgc3RhdGlvbnMNCm1vcm5pbmdTdGF0aW9ucyA8LSBtb3JuaW5nICAlPiUNCiAgZ3JvdXBfYnkoYHN0YXJ0LnN0YXRpb24uaWRgKSAlPiUNCiAgc3VtbWFyaXNlKGxhdD1hcy5udW1lcmljKGBzdGFydC5zdGF0aW9uLmxhdGl0dWRlYFsxXSksDQogICAgICAgICAgICBsb249YXMubnVtZXJpYyhgc3RhcnQuc3RhdGlvbi5sb25naXR1ZGVgWzFdKSwNCiAgICAgICAgICAgIG5hbWU9YHN0YXJ0LnN0YXRpb24ubmFtZWBbMV0sDQogICAgICAgICAgICB0cmlwcz1uKCkpDQoNCiMjIFRha2UgYSBsb29rDQptb3JuaW5nU3RhdGlvbnMgJT4lDQogIGFycmFuZ2UoZGVzYyh0cmlwcykpDQpgYGANCg0KYGBge3IgZWNobz1UUlVFfQ0KIyBUdXJucyBpdCBpbnRvIGEgZ2lzIGZpbGUNCm1vcm5pbmdTdGF0aW9uc0dlbyA8LSBzdF9hc19zZihtb3JuaW5nU3RhdGlvbnMsIGNvb3JkcyA9IGMoImxvbiIsICJsYXQiKSwgZGltID0gIlhZIiwgY3JzID0gNDMyNikgI3N0X2FzX3NmIGlzIERhdGEgdG8gc2hhcGUgZmlsZQ0KDQojIFNhdmUgaWYgeW91IGZlZWwgbGlrZSBpdCEgIyBzdF93cml0ZShwb3B1bGFyU3RhdGlvbnMsICJwb3B1bGFyU3RhdGlvbnMuc2hwIiwgZGVsZXRlX2xheWVyID0gVFJVRSkNCg0KIyBMb29rIGF0IHRoZSBtYXAgDQp0bWFwX21vZGUoJ3ZpZXcnKQ0KdG1fc2hhcGUobW9ybmluZ1N0YXRpb25zR2VvICU+JSANCiAgICAgICAgICAgZmlsdGVyKCFpcy5uYSh0cmlwcykpICU+JSANCiAgICAgICAgICAgbXV0YXRlKHRyaXBzID0gYXMubnVtZXJpYyh0cmlwcykpKSArIHRtX2RvdHMoY29sID0gInRyaXBzIiwgc3R5bGU9InF1YW50aWxlIiwgaWQgPSAibmFtZSIpDQpgYGANCg0KYGBge3IgZWNobz1UUlVFfQ0KIyMjIyMjIyMgUEFSVCA0IEtFUk5FTCBERU5TSVRZICAjIyMjIyMjIyMgDQoNCiMjIFJlYWQgU0YgZm9yIGEgZmlsZSBvZiBib3JvdWdocw0KYm9yb3VnaHMgPC0gc3RfcmVhZCgiYm9yb3VnaC5zaHAiKQ0KYm9yb3VnaHNfcHJqIDwtIHN0X3RyYW5zZm9ybShib3JvdWdocywgY3JzID0gMjI2MykNCg0KIyBEZW5zaXR5IG1hcCAoVGhpcyBtZXRob2QgZG9lc24ndCBhbGxvdyB1cyB0byBzZWUgdGhlIGJhY2tncm91bmQgbWFwKQ0KZGYuc2l6ZS5wcm9qIDwtIHN0X3RyYW5zZm9ybShtb3JuaW5nU3RhdGlvbnNHZW8sIGNycyA9IDIyNjMpDQpwLnNwIDwtIGFzKGRmLnNpemUucHJvaiwgIlNwYXRpYWwiKQ0KcC5wcHAgPC0gYXMocC5zcCwgInBwcCIpDQprZXJuZWxEZW5zaXR5TW9ybmluZyA8LSAoZGVuc2l0eShwLnBwcCwgc2lnbWEgPSAxMDAwKSkNCg0KIyNQbG90IHRoZSB0d28gdG9nZXRoZXINCnBsb3Qoa2VybmVsRGVuc2l0eU1vcm5pbmcpDQpwbG90KGJvcm91Z2hzX3ByaiRnZW9tZXRyeSwgYWRkPVRSVUUpDQoNCiMjQ0hBTExFTkdFUyBGT1IgWU9VUiBERU5TSVRZIE1BUA0KIyNFeHRyYTogQ2xpcCB0aGUgcmFzdGVyIGFuZCBjbGlwIHRoZSB2ZWN0b3IgYXMgd2VsbC4gKG9ubHkgcGxvdCB0aGUgaW50ZXJzZWN0aW9uIG9mIHRoZSB0d28/KSANCiMjRXh0cmE6IE1ha2UgdGhlIGxpbmVzIHdoaXRlIChoaW50OiBpdCBpcyBub3cgYSBwb2x5Z29uISkNCiMjRXh0cmE6IEFkZCB0aGUgdG9wIDMgKG9yIDUpIHBvaW50cyBhbmQgbGFiZWwgdGhlbS4NCmBgYA0KDQpgYGB7ciBlY2hvPVRSVUV9DQojIyMjIyMjIyBQQVJUIDUgR0dQTE9UIEVYQU1QTEUgV0lUSCBDT05UT1VSUyAjIyMjIyMjIyMgDQoNCiMgSGVhdG1hcCBpbiBnZ3Bsb3QNCmxpYnJhcnkoZ2dwbG90MikNCmxpYnJhcnkoZ2dtYXApDQoNCmRmLnNpemUuZGVncmVlIDwtIHN0X3RyYW5zZm9ybShtb3JuaW5nU3RhdGlvbnNHZW8sIGNycyA9IDQzMjYpICU+JSANCiAgbXV0YXRlKGxhdCA9IHN0X2Nvb3JkaW5hdGVzKC4pWywyXSwNCiAgICAgICAgIGxvbiA9IHN0X2Nvb3JkaW5hdGVzKC4pWywxXSkNCg0KIyNHZXQgYm9yZGVycw0KaGVpZ2h0IDwtIG1heChzdF9jb29yZGluYXRlcyhkZi5zaXplLmRlZ3JlZSlbLDJdKSAtIG1pbihzdF9jb29yZGluYXRlcyhkZi5zaXplLmRlZ3JlZSlbLDJdKQ0Kd2lkdGggPC0gbWF4KHN0X2Nvb3JkaW5hdGVzKGRmLnNpemUuZGVncmVlKVssMV0pIC0gbWluKHN0X2Nvb3JkaW5hdGVzKGRmLnNpemUuZGVncmVlKVssMV0pDQogIA0Kc2FjX2JvcmRlcnMgPC0gYyhib3R0b20gPSBtaW4oc3RfY29vcmRpbmF0ZXMoZGYuc2l6ZS5kZWdyZWUpWywyXSkgLSAwLjEqaGVpZ2h0LA0KICAgICAgICAgICAgICAgIHRvcCA9IG1heChzdF9jb29yZGluYXRlcyhkZi5zaXplLmRlZ3JlZSlbLDJdKSArIDAuMSpoZWlnaHQsDQogICAgICAgICAgICAgICAgbGVmdCA9IG1pbihzdF9jb29yZGluYXRlcyhkZi5zaXplLmRlZ3JlZSlbLDFdKSAtIDAuMSp3aWR0aCwNCiAgICAgICAgICAgICAgICByaWdodCA9IG1heChzdF9jb29yZGluYXRlcyhkZi5zaXplLmRlZ3JlZSlbLDFdKSArIDAuMSp3aWR0aCkNCg0KbWFwIDwtIGdldF9zdGFtZW5tYXAoc2FjX2JvcmRlcnMsIHpvb20gPSAxMiwgbWFwdHlwZSA9ICJ0b25lci1saXRlIikNCg0KZ2dtYXAobWFwKSArDQogIGdlb21fZGVuc2l0eTJkKGRhdGEgPSBkZi5zaXplLmRlZ3JlZSwgYWVzKHggPSBsb24sIHkgPSBsYXQpLCBzaXplID0gMC4zKSArDQogIHN0YXRfZGVuc2l0eTJkKGRhdGEgPSBkZi5zaXplLmRlZ3JlZSwgYWVzKHggPSBsb24sIHkgPSBsYXQsIGZpbGwgPSAuLmxldmVsLi4sIGFscGhhID0gLi5sZXZlbC4uKSwgDQogICAgICAgICAgICAgICAgIGdlb20gPSAicG9seWdvbiIsIGJpbnMgPSAxMCkgKyANCiAgc2NhbGVfZmlsbF9ncmFkaWVudChsb3cgPSAiZ3JlZW4iLCBoaWdoID0gInJlZCIpICsNCiAgc2NhbGVfYWxwaGEocmFuZ2UgPSBjKDAsIDAuNCksIGd1aWRlID0gRkFMU0UpICNQbG90ICsNCiBjb29yZF9jYXJ0ZXNpYW4oeGxpbSA9IGMoLTc0LjAyLCAtOTUuNyksIA0KICAgICAgICAgICAgICAgICAgeWxpbSA9IGMoMjkuNSwgMzAuMSkpDQpgYGANCg0KYGBge3IgZWNobz1UUlVFLCBwYWdlZC5wcmludD1UUlVFfQ0KI1EyIEFnZSBvZiBVc2Vycw0KVXNlckFnZSA8LSByZGZDbGVhbg0KVmlldyhVc2VyQWdlKQ0KVXNlckFnZSRBZ2UgPC0gMjAxMyAtIGFzLm51bWVyaWMoVXNlckFnZSRiaXJ0aC55ZWFyKQ0KbWVhbihhcy5udW1lcmljKFVzZXJBZ2UkQWdlKSxuYS5ybT1UUlVFKQ0KDQoNCiNRMTEgTW9ybmluZyBhbmQgRXZlbmluZyByaWRlcw0KbW9ybmluZ19yaWRlcyA8LSByZGZDbGVhbiAlPiUgZmlsdGVyKGhvdXIgJWluJSBjKCcwNycsICcwOCcsICcwOScpKQ0KbW9ybmluZ3N0YXRpb25zIDwtIG1vcm5pbmdfcmlkZXMgICU+JQ0KICBncm91cF9ieShgc3RhcnQuc3RhdGlvbi5pZGApICU+JQ0KICBzdW1tYXJpc2UobGF0PWFzLm51bWVyaWMoYHN0YXJ0LnN0YXRpb24ubGF0aXR1ZGVgWzFdKSwNCiAgICAgICAgICAgIGxvbj1hcy5udW1lcmljKGBzdGFydC5zdGF0aW9uLmxvbmdpdHVkZWBbMV0pLA0KICAgICAgICAgICAgbmFtZT1gc3RhcnQuc3RhdGlvbi5uYW1lYFsxXSwNCiAgICAgICAgICAgIHRyaXBzPW4oKSkNCm5hbWVzKG1vcm5pbmdzdGF0aW9ucylbbmFtZXMobW9ybmluZ3N0YXRpb25zKSA9PSAidHJpcHMiXSA8LSAiTW9ybmluZ1RyaXBzIg0KDQoNCmV2ZW5pbmdfcmlkZXMgPC0gcmRmQ2xlYW4gJT4lIGZpbHRlcihob3VyICVpbiUgYygnMTcnLCAnMTgnLCAnMTknKSkNCmV2ZW5pbmdzdGF0aW9ucyA8LSBldmVuaW5nX3JpZGVzICAlPiUNCiAgZ3JvdXBfYnkoYHN0YXJ0LnN0YXRpb24uaWRgKSAlPiUNCiAgc3VtbWFyaXNlKGxhdD1hcy5udW1lcmljKGBzdGFydC5zdGF0aW9uLmxhdGl0dWRlYFsxXSksDQogICAgICAgICAgICBsb249YXMubnVtZXJpYyhgc3RhcnQuc3RhdGlvbi5sb25naXR1ZGVgWzFdKSwNCiAgICAgICAgICAgIG5hbWU9YHN0YXJ0LnN0YXRpb24ubmFtZWBbMV0sDQogICAgICAgICAgICB0cmlwcz1uKCkpDQpuYW1lcyhldmVuaW5nc3RhdGlvbnMpW25hbWVzKGV2ZW5pbmdzdGF0aW9ucykgPT0gInRyaXBzIl0gPC0gIkV2ZW5pbmdUcmlwcyINCg0KICANCmZ1bGxkYXRhIDwtIG1lcmdlKG1vcm5pbmdzdGF0aW9ucywgZXZlbmluZ3N0YXRpb25zKQ0KVmlldyhmdWxsZGF0YSkNCmZ1bGxkYXRhJERpZmZlcmVuY2UgPC0gKGZ1bGxkYXRhJE1vcm5pbmdUcmlwcy1mdWxsZGF0YSRFdmVuaW5nVHJpcHMpDQpWaWV3KGZ1bGxkYXRhKQ0KDQoNCiMgVHVybnMgaXQgaW50byBhIGdpcyBmaWxlDQpEaWZmZXJlbmNlR2VvIDwtIHN0X2FzX3NmKGZ1bGxkYXRhLCBjb29yZHMgPSBjKCJsb24iLCAibGF0IiksIGRpbSA9ICJYWSIsIGNycyA9IDQzMjYpICNzdF9hc19zZiBpcyBEYXRhIHRvIHNoYXBlIGZpbGUNCg0KIyBMb29rIGF0IHRoZSBtYXAgDQp0bWFwX21vZGUoJ3ZpZXcnKQ0KdG1fc2hhcGUoRGlmZmVyZW5jZUdlbyAlPiUgDQogICAgICAgICAgIGZpbHRlcighaXMubmEoRGlmZmVyZW5jZSkpICU+JSANCiAgICAgICAgICAgbXV0YXRlKHRyaXBzID0gYXMubnVtZXJpYyhEaWZmZXJlbmNlKSkpICsgdG1fZG90cyhjb2wgPSAiRGlmZmVyZW5jZSIsIHN0eWxlPSJxdWFudGlsZSIsIGlkID0gIm5hbWUiLA0KICAgICAgICAgICBtaWRwb2ludCA9IE5BKQ0KDQoNCmBgYA0KDQpgYGB7ciBlY2hvPVRSVUUsIHBhZ2VkLnByaW50PVRSVUV9DQojIFRvcCAzIHN0YXRpb25zIGZvciBNb3JuaW5nIGFuZCBFdmVuaW5nIHRyaXBzDQoNCmZ1bGxkYXRhIDwtIGZ1bGxkYXRhW29yZGVyKGZ1bGxkYXRhJE1vcm5pbmdUcmlwcyxkZWNyZWFzaW5nID0gVFJVRSksXQ0KbW9ybmluZ3N0YXRpb25zdG9wMyA8LSBmdWxsZGF0YVsxOjMsXQ0KVmlldyhtb3JuaW5nc3RhdGlvbnN0b3AzKQ0KDQoNCmZ1bGxkYXRhIDwtIGZ1bGxkYXRhW29yZGVyKGZ1bGxkYXRhJEV2ZW5pbmdUcmlwcyxkZWNyZWFzaW5nID0gVFJVRSksXQ0KZXZlbmluZ3N0YXRpb25zdG9wMyA8LSBmdWxsZGF0YVsxOjMsXQ0KVmlldyhldmVuaW5nc3RhdGlvbnN0b3AzKQ0KDQpgYGANCg==