1 Introduction

In this note, we use a real data set containing crime cases since 2015 in Philadelphia neighborhoods to illustrate various visualizations of visual analysis. We will use two different shapefiles that to draw the boundaries of neighborhoods and census tracts to plot various information available in the crime data set.

2 Data Preparation

We will create both choropleth and reference maps to display different types of information.

2.1 Data Integration

The visual analysis is based on the following three data sets

##  covid_vaccines_by_census_tract.geojson
phillyNeighbor  <- st_read("https://pengdsci.github.io/STA553VIZ/w08/Neighborhoods_Philadelphia.geojson")
philly  <- st_read("https://pengdsci.github.io/STA553VIZ/w08/PhillyNeighborhood-blocks.geojson") # block level data
phillyCrime <- read.csv("https://pengdsci.github.io/STA553VIZ/w08/PhillyCrimeSince2015.csv")
###
phillyCrime$name = toupper(gsub("-", "_", phillyCrime$neighborhood)) 
# extracting year from variable 'date'
slash.loc = unlist(gregexpr('/', phillyCrime$date))
slash2.loc = slash.loc[2*(1:dim(phillyCrime)[1])]
phillyCrime$year = substr(phillyCrime$date, slash2.loc+1, slash2.loc+4)

Since the crime data set has neighborhood name which will be used as a key to join the shapefile of the neighborhood. There is no variable in the crime data that can be used as a key to merge with the census tract shapefile. We aggregate relevant information at neighborhood level to make a choropleth map and make a scatterplot map to display information of individual crime cases.

SubCrime = phillyCrime[,c("dc_key","fatal","name","year")]
aggregateCrime = aggregate(SubCrime$fatal, by=list(SubCrime$name,SubCrime$year), FUN=length)
## Longitude and latitude of zip code center
ZipLon = aggregate(phillyCrime$lng, by=list(phillyCrime$zip_code), FUN=mean)
ZipLat = aggregate(phillyCrime$lat, by=list(phillyCrime$zip_code), FUN=mean)
ZipLonLat = merge(ZipLon, ZipLat, by = "Group.1")
names(ZipLonLat) = c("zip", "lng", "lat")
##  Zip code crime
ZipCrimeTable = table(phillyCrime$fatal,  phillyCrime$zip_code)
ZipCrime = data.frame(zip=as.numeric(names(table(phillyCrime$zip_code))), 
                      fatal = table(phillyCrime$fatal, phillyCrime$zip_code)[1,],
                      nonfatal = table(phillyCrime$fatal, phillyCrime$zip_code)[2,],
                      total.crime = table(phillyCrime$zip_code) 
                      )
ZipCrime = ZipCrime[, c("zip", "fatal", "nonfatal", "total.crime.Freq")]
colnames(ZipCrime) = c("zip", "fatal", "nonfatal", "total.crime")
ZipCrime = merge(ZipLonLat, ZipCrime, by = "zip")

3 Visualizing Analysis Results

##
pal <- colorFactor(c("red", "gold"), domain = c("Fatal", "Nonfatal"))
##
pnt = st_as_sf(data.frame(x = -75.1652, y = 39.9526),
                coords = c("x", "y"),
                crs = 4326)

media = st_as_sf(data.frame(x = -75.3877, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
##
ageDistloc = st_as_sf(data.frame(x = -75.3677, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
##
ziploc = st_as_sf(data.frame(x = -75.3477, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
##
fig <- plot_ly(ZipCrime, x = ~lng, y = ~lat, color = ~zip, size = ~total.crime, #colors = colors,
               type = 'scatter', 
               mode = 'markers', 
               #sizes = c(min(log(ZipCrime$total.crime)), max(log(ZipCrime$total.crime))),
               marker = list(symbol = 'circle', 
                           sizemode = 'diameter',
                               line = list(width = 2, color = '#FFFFFF')),
        text = ~paste('Zip Code:', zip, 
                      '<br>Total Crime:', total.crime, 
                      '<br>Fatal Crime:', fatal,
                      '<br>Nonfatal Crime:', nonfatal)) %>% hide_colorbar()
##
img = "https://pengdsci.github.io/STA553VIZ/w08/PhillyCityHall.jpg"
trend = "https://pengdsci.github.io/STA553VIZ/w08/CrimeTrend.jpg"
ageDist = "https://pengdsci.github.io/STA553VIZ/w08/ageDist.jpg"
trendIcon = "https://pengdsci.github.io/STA553VIZ/w08/trend-icon.jpg"
######################################
## Data analysis
## 1. age distribution by age
fatal.cols <- c("#F76D5E", "#72D8FF")
# Basic density plot in ggplot2
fatalAge = ggplot(phillyCrime, aes(x = age, fill =fatal )) +
  geom_density(alpha = 0.7) + 
  scale_fill_manual(values = fatal.cols)
##################################################
## 2. Frequency distribution: fatal vs nonfatal
#fatal.Dist = pander(table(phillyCrime$fatal, phillyCrime$race))
##################################################
## popup interactive graphs with plotly
fl = tempfile(fileext = ".html")
saveWidget(fig, file = fl)
##
leaflet() %>%
  setView(lng=-75.2427, lat=40.0107, zoom = 11) %>%
  addProviderTiles(providers$CartoDB.DarkMatter) %>% 
  #addTiles(providers$CartoDB.PositronNoLabels) %>%
  addMiniMap() %>%
  addPolygons(data = phillyNeighbor,
              color = 'skyblue',
              weight = 1)  %>%
  addCircleMarkers(data = phillyCrime,
                   radius = ~ifelse(fatal == "Fatal", 5, 3),
                   color = ~pal(fatal),
                   stroke = FALSE, 
                   fillOpacity = 0.5,
                   popup = ~popupTable(phillyCrime)) %>%
  addCircleMarkers(data = pnt, 
                   color = "blue",
                   weight = 2,
                   label = "City Hall",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "pnt") %>%
  addPopupImages(img, 
                  width = 100,
                  height = 120,
                  tooltip = FALSE,
                  group = "pnt")  %>%
  addCircleMarkers(data = media, 
                   color = "red",
                   weight = 2,
                   label = "Trend",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "media") %>%
  addPopupImages(trend, 
                  width = 500,
                  height = 400,
                  tooltip = FALSE,
                  group = "media") %>%
 addCircleMarkers(data = ageDistloc, 
                   color = "skyblue",
                   weight = 2,
                   label = "Age Distribution",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "ageDistloc") %>%
  addPopupImages(ageDist, 
                  width = 500,
                  height = 400,
                  tooltip = FALSE,
                  group = "ageDistloc") %>%
  addCircleMarkers(data = ziploc, 
                   color = "white",
                   weight = 2,
                   label = "ZIP Location",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "ziploc") %>%
  #addPopupGraphs(list(fig),  
  #               width = 500, 
  #               height = 500, 
  #               tooltip = FALSE,
  #               group = "ziploc") %>%
  #  addMarkers(
  #         ~Long, ~Lat, group="3" ) %>%
  leafpop:::addPopupIframes(
      source = fl,
      group = "ziploc"
    )
LS0tDQp0aXRsZTogIkFkdmFuY2VkIE1hcHMgd2l0aCBSIg0KYXV0aG9yOiAiQ2hlbmcgUGVuZyINCmRhdGU6ICJXZXN0IENoZXN0ZXIgVW5pdmVyc2l0eSAiDQpvdXRwdXQ6DQogIGh0bWxfZG9jdW1lbnQ6IA0KICAgIHRvYzogeWVzDQogICAgdG9jX2RlcHRoOiA0DQogICAgdG9jX2Zsb2F0OiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIHRoZW1lOiByZWFkYWJsZQ0KLS0tDQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPg0KDQpkaXYjVE9DIGxpIHsNCiAgICBsaXN0LXN0eWxlOm5vbmU7DQogICAgYmFja2dyb3VuZC1pbWFnZTpub25lOw0KICAgIGJhY2tncm91bmQtcmVwZWF0Om5vbmU7DQogICAgYmFja2dyb3VuZC1wb3NpdGlvbjowOw0KfQ0KaDEudGl0bGUgew0KICBmb250LXNpemU6IDI0cHg7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5hdXRob3IgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoNC5kYXRlIHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMSB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAyMnB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgyIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDMgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMTVweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoNCB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBkYXJrcmVkOw0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQo8L3N0eWxlPg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCiMgY29kZSBjaHVuayBzcGVjaWZpZXMgd2hldGhlciB0aGUgUiBjb2RlLCB3YXJuaW5ncywgYW5kIG91dHB1dCANCiMgd2lsbCBiZSBpbmNsdWRlZCBpbiB0aGUgb3V0cHV0IGZpbGVzLg0KaWYgKCFyZXF1aXJlKCJ0aWR5dmVyc2UiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikNCiAgIGxpYnJhcnkodGlkeXZlcnNlKQ0KfQ0KaWYgKCFyZXF1aXJlKCJrbml0ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJrbml0ciIpDQogICBsaWJyYXJ5KGtuaXRyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJzZiIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJzZiIpDQogICBsaWJyYXJ5KHNmKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0ZXJyYSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0ZXJyYSIpDQogICBsaWJyYXJ5KHRlcnJhKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbG90bHkiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygicGxvdGx5IikNCiAgIGxpYnJhcnkocGxvdGx5KQ0KfQ0KaWYgKCFyZXF1aXJlKCJkcGx5ciIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpDQogICBsaWJyYXJ5KGRwbHlyKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwbmciKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBuZyIpICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBuZyIpDQp9DQppZiAoIXJlcXVpcmUoInNwRGF0YSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic3BEYXRhIikgICAgICAgICAgICAgDQogICAgbGlicmFyeSgic3BEYXRhIikNCn0NCmlmICghcmVxdWlyZSgiY29sb3VycGlja2VyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJjb2xvdXJwaWNrZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiY29sb3VycGlja2VyIikNCn0NCmlmICghcmVxdWlyZSgiZ2lmc2tpIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnaWZza2kiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2lmc2tpIikNCn0NCmlmICghcmVxdWlyZSgibWFnaWNrIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtYWdpY2siKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibWFnaWNrIikNCn0NCmlmICghcmVxdWlyZSgic3BEYXRhTGFyZ2UiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInNwRGF0YUxhcmdlIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInNwRGF0YUxhcmdlIikNCn0NCiMjIyBnZ3Bsb3QgYW5kIGV4dGVuc2lvbnMNCmlmICghcmVxdWlyZSgiZ2dwbG90MiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ3Bsb3QyIikNCn0NCmlmICghcmVxdWlyZSgiZ2dhbmltYXRlIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ2FuaW1hdGUiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dhbmltYXRlIikNCn0NCmlmICghcmVxdWlyZSgidG1hcCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygidG1hcCIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJ0bWFwIikNCn0NCmlmICghcmVxdWlyZSgic2YiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInNmIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInNmIikNCn0NCmlmICghcmVxdWlyZSgidGlncmlzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWdyaXMiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgidGlncmlzIikNCn0NCmlmICghcmVxdWlyZSgibWFwdmlldyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibWFwdmlldyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtYXB2aWV3IikNCn0NCmlmICghcmVxdWlyZSgicGFuZGVyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwYW5kZXIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicGFuZGVyIikNCn0NCmlmICghcmVxdWlyZSgibGF0dGljZSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibGF0dGljZSIpDQpsaWJyYXJ5KCJsYXR0aWNlIikNCn0NCmlmICghcmVxdWlyZSgic3AiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInNwIikNCmxpYnJhcnkoInNwIikNCn0NCmlmICghcmVxdWlyZSgibGVhZmxldCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibGVhZmxldCIpDQpsaWJyYXJ5KCJsZWFmbGV0IikNCn0NCmlmICghcmVxdWlyZSgibGVhZnBvcCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibGVhZnBvcCIpDQpsaWJyYXJ5KCJsZWFmcG9wIikNCn0NCmlmICghcmVxdWlyZSgibGVhZmVtIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJsZWFmZW0iKQ0KbGlicmFyeSgibGVhZmVtIikNCn0NCmlmICghcmVxdWlyZSgic3BEYXRhTGFyZ2UiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInNwRGF0YUxhcmdlIiwgcmVwb3MgPSAiaHR0cHM6Ly9nZW9jb21wci5yLXVuaXZlcnNlLmRldiIpDQpsaWJyYXJ5KCJzcERhdGFMYXJnZSIpDQp9DQppZiAoIXJlcXVpcmUoImh0bWx3aWRnZXRzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJodG1sd2lkZ2V0cyIpDQpsaWJyYXJ5KCJodG1sd2lkZ2V0cyIpDQp9DQojIyNodG1sd2lkZ2V0cw0KIyMjDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BKQ0KYGBgDQoNClwNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KSW4gdGhpcyBub3RlLCB3ZSB1c2UgYSByZWFsIGRhdGEgc2V0IGNvbnRhaW5pbmcgY3JpbWUgY2FzZXMgc2luY2UgMjAxNSBpbiBQaGlsYWRlbHBoaWEgbmVpZ2hib3Job29kcyB0byBpbGx1c3RyYXRlIHZhcmlvdXMgdmlzdWFsaXphdGlvbnMgb2YgdmlzdWFsIGFuYWx5c2lzLiBXZSB3aWxsIHVzZSB0d28gZGlmZmVyZW50IHNoYXBlZmlsZXMgdGhhdCB0byBkcmF3IHRoZSBib3VuZGFyaWVzIG9mIG5laWdoYm9yaG9vZHMgYW5kIGNlbnN1cyB0cmFjdHMgdG8gcGxvdCB2YXJpb3VzIGluZm9ybWF0aW9uIGF2YWlsYWJsZSBpbiB0aGUgY3JpbWUgZGF0YSBzZXQuIA0KDQoNCiMgRGF0YSBQcmVwYXJhdGlvbg0KDQpXZSB3aWxsIGNyZWF0ZSBib3RoIGNob3JvcGxldGggYW5kIHJlZmVyZW5jZSBtYXBzIHRvIGRpc3BsYXkgZGlmZmVyZW50IHR5cGVzIG9mIGluZm9ybWF0aW9uLg0KDQojIyBEYXRhIEludGVncmF0aW9uDQoNClRoZSB2aXN1YWwgYW5hbHlzaXMgaXMgYmFzZWQgb24gdGhlIGZvbGxvd2luZyB0aHJlZSBkYXRhIHNldHMNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0UsIHJlc3VsdHM9RkFMU0V9DQojIyAgY292aWRfdmFjY2luZXNfYnlfY2Vuc3VzX3RyYWN0Lmdlb2pzb24NCnBoaWxseU5laWdoYm9yICA8LSBzdF9yZWFkKCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L05laWdoYm9yaG9vZHNfUGhpbGFkZWxwaGlhLmdlb2pzb24iKQ0KcGhpbGx5ICA8LSBzdF9yZWFkKCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L1BoaWxseU5laWdoYm9yaG9vZC1ibG9ja3MuZ2VvanNvbiIpICMgYmxvY2sgbGV2ZWwgZGF0YQ0KcGhpbGx5Q3JpbWUgPC0gcmVhZC5jc3YoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvUGhpbGx5Q3JpbWVTaW5jZTIwMTUuY3N2IikNCiMjIw0KcGhpbGx5Q3JpbWUkbmFtZSA9IHRvdXBwZXIoZ3N1YigiLSIsICJfIiwgcGhpbGx5Q3JpbWUkbmVpZ2hib3Job29kKSkgDQojIGV4dHJhY3RpbmcgeWVhciBmcm9tIHZhcmlhYmxlICdkYXRlJw0Kc2xhc2gubG9jID0gdW5saXN0KGdyZWdleHByKCcvJywgcGhpbGx5Q3JpbWUkZGF0ZSkpDQpzbGFzaDIubG9jID0gc2xhc2gubG9jWzIqKDE6ZGltKHBoaWxseUNyaW1lKVsxXSldDQpwaGlsbHlDcmltZSR5ZWFyID0gc3Vic3RyKHBoaWxseUNyaW1lJGRhdGUsIHNsYXNoMi5sb2MrMSwgc2xhc2gyLmxvYys0KQ0KYGBgDQoNClNpbmNlIHRoZSBjcmltZSBkYXRhIHNldCBoYXMgbmVpZ2hib3Job29kIG5hbWUgd2hpY2ggd2lsbCBiZSB1c2VkIGFzIGEga2V5IHRvIGpvaW4gdGhlIHNoYXBlZmlsZSBvZiB0aGUgbmVpZ2hib3Job29kLiBUaGVyZSBpcyBubyB2YXJpYWJsZSBpbiB0aGUgY3JpbWUgZGF0YSB0aGF0IGNhbiBiZSB1c2VkIGFzIGEga2V5IHRvIG1lcmdlIHdpdGggdGhlIGNlbnN1cyB0cmFjdCBzaGFwZWZpbGUuIFdlIGFnZ3JlZ2F0ZSByZWxldmFudCBpbmZvcm1hdGlvbiBhdCBuZWlnaGJvcmhvb2QgbGV2ZWwgdG8gbWFrZSBhIGNob3JvcGxldGggbWFwIGFuZCBtYWtlIGEgc2NhdHRlcnBsb3QgbWFwIHRvIGRpc3BsYXkgaW5mb3JtYXRpb24gb2YgaW5kaXZpZHVhbCBjcmltZSBjYXNlcy4NCg0KYGBge3J9DQpTdWJDcmltZSA9IHBoaWxseUNyaW1lWyxjKCJkY19rZXkiLCJmYXRhbCIsIm5hbWUiLCJ5ZWFyIildDQphZ2dyZWdhdGVDcmltZSA9IGFnZ3JlZ2F0ZShTdWJDcmltZSRmYXRhbCwgYnk9bGlzdChTdWJDcmltZSRuYW1lLFN1YkNyaW1lJHllYXIpLCBGVU49bGVuZ3RoKQ0KYGBgDQoNCmBgYHtyfQ0KIyMgTG9uZ2l0dWRlIGFuZCBsYXRpdHVkZSBvZiB6aXAgY29kZSBjZW50ZXINClppcExvbiA9IGFnZ3JlZ2F0ZShwaGlsbHlDcmltZSRsbmcsIGJ5PWxpc3QocGhpbGx5Q3JpbWUkemlwX2NvZGUpLCBGVU49bWVhbikNClppcExhdCA9IGFnZ3JlZ2F0ZShwaGlsbHlDcmltZSRsYXQsIGJ5PWxpc3QocGhpbGx5Q3JpbWUkemlwX2NvZGUpLCBGVU49bWVhbikNClppcExvbkxhdCA9IG1lcmdlKFppcExvbiwgWmlwTGF0LCBieSA9ICJHcm91cC4xIikNCm5hbWVzKFppcExvbkxhdCkgPSBjKCJ6aXAiLCAibG5nIiwgImxhdCIpDQojIyAgWmlwIGNvZGUgY3JpbWUNClppcENyaW1lVGFibGUgPSB0YWJsZShwaGlsbHlDcmltZSRmYXRhbCwgIHBoaWxseUNyaW1lJHppcF9jb2RlKQ0KWmlwQ3JpbWUgPSBkYXRhLmZyYW1lKHppcD1hcy5udW1lcmljKG5hbWVzKHRhYmxlKHBoaWxseUNyaW1lJHppcF9jb2RlKSkpLCANCiAgICAgICAgICAgICAgICAgICAgICBmYXRhbCA9IHRhYmxlKHBoaWxseUNyaW1lJGZhdGFsLCBwaGlsbHlDcmltZSR6aXBfY29kZSlbMSxdLA0KICAgICAgICAgICAgICAgICAgICAgIG5vbmZhdGFsID0gdGFibGUocGhpbGx5Q3JpbWUkZmF0YWwsIHBoaWxseUNyaW1lJHppcF9jb2RlKVsyLF0sDQogICAgICAgICAgICAgICAgICAgICAgdG90YWwuY3JpbWUgPSB0YWJsZShwaGlsbHlDcmltZSR6aXBfY29kZSkgDQogICAgICAgICAgICAgICAgICAgICAgKQ0KWmlwQ3JpbWUgPSBaaXBDcmltZVssIGMoInppcCIsICJmYXRhbCIsICJub25mYXRhbCIsICJ0b3RhbC5jcmltZS5GcmVxIildDQpjb2xuYW1lcyhaaXBDcmltZSkgPSBjKCJ6aXAiLCAiZmF0YWwiLCAibm9uZmF0YWwiLCAidG90YWwuY3JpbWUiKQ0KWmlwQ3JpbWUgPSBtZXJnZShaaXBMb25MYXQsIFppcENyaW1lLCBieSA9ICJ6aXAiKQ0KYGBgDQoNCg0KIyBWaXN1YWxpemluZyBBbmFseXNpcyBSZXN1bHRzDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJywgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTAsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0NCiMjDQpwYWwgPC0gY29sb3JGYWN0b3IoYygicmVkIiwgImdvbGQiKSwgZG9tYWluID0gYygiRmF0YWwiLCAiTm9uZmF0YWwiKSkNCiMjDQpwbnQgPSBzdF9hc19zZihkYXRhLmZyYW1lKHggPSAtNzUuMTY1MiwgeSA9IDM5Ljk1MjYpLA0KICAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoIngiLCAieSIpLA0KICAgICAgICAgICAgICAgIGNycyA9IDQzMjYpDQoNCm1lZGlhID0gc3RfYXNfc2YoZGF0YS5mcmFtZSh4ID0gLTc1LjM4NzcsIHkgPSAzOS45MTY4KSwNCiAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJ4IiwgInkiKSwNCiAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQ0KIyMNCmFnZURpc3Rsb2MgPSBzdF9hc19zZihkYXRhLmZyYW1lKHggPSAtNzUuMzY3NywgeSA9IDM5LjkxNjgpLA0KICAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoIngiLCAieSIpLA0KICAgICAgICAgICAgICAgIGNycyA9IDQzMjYpDQojIw0KemlwbG9jID0gc3RfYXNfc2YoZGF0YS5mcmFtZSh4ID0gLTc1LjM0NzcsIHkgPSAzOS45MTY4KSwNCiAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJ4IiwgInkiKSwNCiAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQ0KIyMNCmZpZyA8LSBwbG90X2x5KFppcENyaW1lLCB4ID0gfmxuZywgeSA9IH5sYXQsIGNvbG9yID0gfnppcCwgc2l6ZSA9IH50b3RhbC5jcmltZSwgI2NvbG9ycyA9IGNvbG9ycywNCiAgICAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIA0KICAgICAgICAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywgDQogICAgICAgICAgICAgICAjc2l6ZXMgPSBjKG1pbihsb2coWmlwQ3JpbWUkdG90YWwuY3JpbWUpKSwgbWF4KGxvZyhaaXBDcmltZSR0b3RhbC5jcmltZSkpKSwNCiAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3Qoc3ltYm9sID0gJ2NpcmNsZScsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZW1vZGUgPSAnZGlhbWV0ZXInLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmUgPSBsaXN0KHdpZHRoID0gMiwgY29sb3IgPSAnI0ZGRkZGRicpKSwNCiAgICAgICAgdGV4dCA9IH5wYXN0ZSgnWmlwIENvZGU6JywgemlwLCANCiAgICAgICAgICAgICAgICAgICAgICAnPGJyPlRvdGFsIENyaW1lOicsIHRvdGFsLmNyaW1lLCANCiAgICAgICAgICAgICAgICAgICAgICAnPGJyPkZhdGFsIENyaW1lOicsIGZhdGFsLA0KICAgICAgICAgICAgICAgICAgICAgICc8YnI+Tm9uZmF0YWwgQ3JpbWU6Jywgbm9uZmF0YWwpKSAlPiUgaGlkZV9jb2xvcmJhcigpDQojIw0KaW1nID0gImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvUGhpbGx5Q2l0eUhhbGwuanBnIg0KdHJlbmQgPSAiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUzVklaL3cwOC9DcmltZVRyZW5kLmpwZyINCmFnZURpc3QgPSAiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUzVklaL3cwOC9hZ2VEaXN0LmpwZyINCnRyZW5kSWNvbiA9ICJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L3RyZW5kLWljb24uanBnIg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIERhdGEgYW5hbHlzaXMNCiMjIDEuIGFnZSBkaXN0cmlidXRpb24gYnkgYWdlDQpmYXRhbC5jb2xzIDwtIGMoIiNGNzZENUUiLCAiIzcyRDhGRiIpDQojIEJhc2ljIGRlbnNpdHkgcGxvdCBpbiBnZ3Bsb3QyDQpmYXRhbEFnZSA9IGdncGxvdChwaGlsbHlDcmltZSwgYWVzKHggPSBhZ2UsIGZpbGwgPWZhdGFsICkpICsNCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC43KSArIA0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBmYXRhbC5jb2xzKQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIDIuIEZyZXF1ZW5jeSBkaXN0cmlidXRpb246IGZhdGFsIHZzIG5vbmZhdGFsDQojZmF0YWwuRGlzdCA9IHBhbmRlcih0YWJsZShwaGlsbHlDcmltZSRmYXRhbCwgcGhpbGx5Q3JpbWUkcmFjZSkpDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgcG9wdXAgaW50ZXJhY3RpdmUgZ3JhcGhzIHdpdGggcGxvdGx5DQpmbCA9IHRlbXBmaWxlKGZpbGVleHQgPSAiLmh0bWwiKQ0Kc2F2ZVdpZGdldChmaWcsIGZpbGUgPSBmbCkNCiMjDQpsZWFmbGV0KCkgJT4lDQogIHNldFZpZXcobG5nPS03NS4yNDI3LCBsYXQ9NDAuMDEwNywgem9vbSA9IDExKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5EYXJrTWF0dGVyKSAlPiUgDQogICNhZGRUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbk5vTGFiZWxzKSAlPiUNCiAgYWRkTWluaU1hcCgpICU+JQ0KICBhZGRQb2x5Z29ucyhkYXRhID0gcGhpbGx5TmVpZ2hib3IsDQogICAgICAgICAgICAgIGNvbG9yID0gJ3NreWJsdWUnLA0KICAgICAgICAgICAgICB3ZWlnaHQgPSAxKSAgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHBoaWxseUNyaW1lLA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IH5pZmVsc2UoZmF0YWwgPT0gIkZhdGFsIiwgNSwgMyksDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSB+cGFsKGZhdGFsKSwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cG9wdXBUYWJsZShwaGlsbHlDcmltZSkpICU+JQ0KICBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBwbnQsIA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiQ2l0eSBIYWxsIiwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjk1LA0KICAgICAgICAgICAgICAgICAgIGdyb3VwID0gInBudCIpICU+JQ0KICBhZGRQb3B1cEltYWdlcyhpbWcsIA0KICAgICAgICAgICAgICAgICAgd2lkdGggPSAxMDAsDQogICAgICAgICAgICAgICAgICBoZWlnaHQgPSAxMjAsDQogICAgICAgICAgICAgICAgICB0b29sdGlwID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBncm91cCA9ICJwbnQiKSAgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IG1lZGlhLCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiVHJlbmQiLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAibWVkaWEiKSAlPiUNCiAgYWRkUG9wdXBJbWFnZXModHJlbmQsIA0KICAgICAgICAgICAgICAgICAgd2lkdGggPSA1MDAsDQogICAgICAgICAgICAgICAgICBoZWlnaHQgPSA0MDAsDQogICAgICAgICAgICAgICAgICB0b29sdGlwID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBncm91cCA9ICJtZWRpYSIpICU+JQ0KIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IGFnZURpc3Rsb2MsIA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gInNreWJsdWUiLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiQWdlIERpc3RyaWJ1dGlvbiIsDQogICAgICAgICAgICAgICAgICAgc3Ryb2tlID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC45NSwNCiAgICAgICAgICAgICAgICAgICBncm91cCA9ICJhZ2VEaXN0bG9jIikgJT4lDQogIGFkZFBvcHVwSW1hZ2VzKGFnZURpc3QsIA0KICAgICAgICAgICAgICAgICAgd2lkdGggPSA1MDAsDQogICAgICAgICAgICAgICAgICBoZWlnaHQgPSA0MDAsDQogICAgICAgICAgICAgICAgICB0b29sdGlwID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBncm91cCA9ICJhZ2VEaXN0bG9jIikgJT4lDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHppcGxvYywgDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSAid2hpdGUiLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiWklQIExvY2F0aW9uIiwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjk1LA0KICAgICAgICAgICAgICAgICAgIGdyb3VwID0gInppcGxvYyIpICU+JQ0KICAjYWRkUG9wdXBHcmFwaHMobGlzdChmaWcpLCAgDQogICMgICAgICAgICAgICAgICB3aWR0aCA9IDUwMCwgDQogICMgICAgICAgICAgICAgICBoZWlnaHQgPSA1MDAsIA0KICAjICAgICAgICAgICAgICAgdG9vbHRpcCA9IEZBTFNFLA0KICAjICAgICAgICAgICAgICAgZ3JvdXAgPSAiemlwbG9jIikgJT4lDQogICMgIGFkZE1hcmtlcnMoDQogICMgICAgICAgICB+TG9uZywgfkxhdCwgZ3JvdXA9IjMiICkgJT4lDQogIGxlYWZwb3A6OjphZGRQb3B1cElmcmFtZXMoDQogICAgICBzb3VyY2UgPSBmbCwNCiAgICAgIGdyb3VwID0gInppcGxvYyINCiAgICApDQoNCmBgYA0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg==