1 Map 1: Randomly Selected Gas Stations

1.1 Overview

Our first visualization will be an interactive map, containing various locations of gas stations from the contential United States. Each gas station will be a point on the map, and each point will have a hover text box containing state, county, address, and zip code.

1.2 Randomly Selecting Data

Since our source data contains nearly 73,000 observations, we will randomly select 500 observations.

gas<-read.csv("https://pengdsci.github.io/datasets/POC/POC.csv")

rand.df <- gas[sample(nrow(gas), size=500), ]

1.3 Mapping the Data

We will use the plotly package to map the data onto an interactive map.

g <- list(      scope = 'p',
                projection = list(type = 'albers usa'),
                showland = TRUE,
                landcolor = toRGB("gray95"),
                subunitcolor = toRGB("gray85"),
                countrycolor = toRGB("gray85"),
                countrywidth = 0.5,
                subunitwidth = 0.5
)


fig <- plot_geo(rand.df, lat = ~ycoord, lon = ~xcoord) %>% 
  add_markers( text = ~paste(STATE, county, ADDRESS, ZIPnew,
                             
                             sep = "<br>"),
               #color = , 
               symbol = "circle", 
               #size = , 
               hoverinfo = "text")   %>% 
  layout( title = 'Randomly Selected US Gas Stations', 
          geo = g )

fig

The above is a demonstration of a simple - yet effective - way to visualize data on a map. Our next will be a little more complex.

2 Map 2: Crime in Philadelphia, 2023

2.1 Overview

For this visualization, we will begin with a dataset of crimes committed in the city of Philadelphia between 2015 and early March of 2024.

2.2 Preparing the Data

We will need to subset the 2023 data before we can impose it over a map. We will use the stringr library for this.

crime<-read.csv("https://pengdsci.github.io/STA553VIZ/w08/PhillyCrimeSince2015.csv")

df<-crime

df$year <- str_extract(df$date, "\\d{4}")
df$year <- as.numeric(df$year)

crime23<-subset(df, year==2023)

write.csv(crime23, "C:\\Users\\Alex\\Documents\\R\\Grad\\553\\datasets\\wk7.csv")

A copy of the 2023 data can be found at https://raw.githubusercontent.com/AlexDragonetti/STA553/main/hw7/wk7.csv

#remove observations with missing values - at least one has a missing value for coordinates
crime23.nona<-na.omit(crime23)

2.3 Mapping the Data

Finally, we will map the incident data using the leaflet package.

color2 <- rep("red", length(crime23.nona))
color2[which(crime23.nona$fatal=="Nonfatal")] <- "blue"
color2[which(crime23.nona$fatal=="Fatal")] <- "red"



label.msg <- paste("Street:", crime23.nona$street_name,    
                   "<br>Block Number:",crime23.nona$block_number,
                   "<br>Neighborhood:", crime23.nona$neighborhood,
                   "<br>Incident Type:", crime23.nona$fatal)


leaflet(crime23.nona) %>%
  addTiles() %>% 
  setView(lng=mean(crime23.nona$lng), lat=mean(crime23.nona$lat), zoom = 11) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>%
  addCircleMarkers(
            ~lng, 
            ~lat,
            color = color2,
            stroke = FALSE, 
            fillOpacity = 0.5,
            popup= ~label.msg)  %>%
  addLegend(position = "bottomright", 
            colors = c("red", "blue"),
            labels= c("Fatal", "Nonfatal"),
            title= "Type of Incident",
            opacity = 0.4)

Our resulting graph is fully interactive - clicking a dot will show details of the incident.

Please note that the above graph has spots that appear purple. This is due to the opaque, overlapping red and blue dots, indicating both fatal and nonfatal incidents at the same address. For example, 1000 E Bristol Street in Juniata saw an incident with two victims, one being a fatality. For clarification on any confusing point, please refer to the dataset linked at the end of Preparing the Data.

3 Map 3: Philadelphia Shootings, 2015-2024

3.1 Overview

Our next visualization will specifically focus on the demographics of shooting victims. The data is from OpenDataPhilly and can be accessed here:https://opendataphilly.org/datasets/shooting-victims (see below code for raw, direct link). We will again utilize leaflet.

philly.data<-read.csv("https://phl.carto.com/api/v2/sql?q=SELECT+*,+ST_Y(the_geom)+AS+lat,+ST_X(the_geom)+AS+lng+FROM+shootings&filename=shootings&format=csv&skipfields=cartodb_id")
phillyNeighborShooting  <- na.omit(st_read("https://pengdsci.github.io/STA553VIZ/w08/PhillyShootings.geojson"))
Reading layer `PhillyShootings' from data source 
  `https://pengdsci.github.io/STA553VIZ/w08/PhillyShootings.geojson' 
  using driver `GeoJSON'
replacing null geometries with empty geometries
Simple feature collection with 15555 features and 21 fields (with 29 geometries empty)
Geometry type: POINT
Dimension:     XY
Bounding box:  xmin: -75.27362 ymin: 39.87799 xmax: -74.95936 ymax: 40.13117
Geodetic CRS:  WGS 84
phillyNeighbor  <- st_read("https://pengdsci.github.io/STA553VIZ/w08/Neighborhoods_Philadelphia.geojson")
Reading layer `Neighborhoods_Philadelphia' from data source 
  `https://pengdsci.github.io/STA553VIZ/w08/Neighborhoods_Philadelphia.geojson' 
  using driver `GeoJSON'
Simple feature collection with 158 features and 8 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -75.28027 ymin: 39.867 xmax: -74.95576 ymax: 40.13799
Geodetic CRS:  WGS 84
philly  <- st_read("https://pengdsci.github.io/STA553VIZ/w08/PhillyNeighborhood-blocks.geojson")
Reading layer `PhillyNeighborhood-blocks' from data source 
  `https://pengdsci.github.io/STA553VIZ/w08/PhillyNeighborhood-blocks.geojson' 
  using driver `GeoJSON'
Simple feature collection with 17555 features and 7 fields
Geometry type: MULTIPOLYGON
Dimension:     XY
Bounding box:  xmin: -75.28027 ymin: 39.867 xmax: -74.95576 ymax: 40.13799
Geodetic CRS:  WGS 84

3.2 Preparing the Data

While the data is usable, we intend to display much more information with each point, so we will remove unnecessary or redundant data, while fixing a formatting quirk with the ‘date_’ variable (which will also be renamed to ‘date’) using the stringr package. Without this, the ‘date_’ variable ends each entry with ‘00:00:00+00’, which is clunky and likely unintended.

philly.data2 <- philly.data %>%
  select(-c(1, 2, 3, 5, 6, 17, 18))

names(philly.data2)[which(names(philly.data2) == "date_")] <- "date"

philly.data2$date <- str_replace(philly.data2$date, " 00:00:00\\+00", "")

# Convert 'date' variable to Date format for possible future use
philly.data2$date <- as.Date(philly.data2$date, format = "%Y-%m-%d")

3.3 Preparing Visualization for Aggregate Data

With out map, we would like to feature individual incidents and allow someone to click to see victim demographic information, but we would also like to have ‘big picture’ aggregate plots available as well. We will prepare three simple plots here using ggplot for illustrative purposes.

#Distribution of Age across Race

ar.plot<-ggplot(philly.data2, aes(x = age, fill = race)) +
  geom_density(alpha = 0.5) +
  labs(title = "Distribution of Age by Race",
       x = "Age",
       y = "Density") +
  scale_fill_discrete(name = "Race") +
  theme_minimal()

#Probably unnecessary step, but made an extra dataset while testing something out
philly.data3<-subset(philly.data2)
philly.data3$fatal <- factor(philly.data3$fatal, levels = c(0, 1), labels = c("Nonfatal", "Fatal"))

# Plot the point graph with colored points
year.plot <- ggplot(philly.data3, aes(x = year, fill = fatal)) +
  geom_bar(position = position_dodge(width = 0.5), stat = "count") +
  scale_fill_manual(values = c("Nonfatal" = "blue", "Fatal" = "red")) +
  labs(title = "Number of Fatal and Nonfatal Incidents by Year",
       x = "Year",
       y = "Count") +
  theme_minimal() +
  scale_x_continuous(breaks = seq(min(philly.data3$year), max(philly.data3$year), by = 1))

#Frequency of indoor vs outdoor incidents

outside.plot <- ggplot(philly.data2, aes(x = year, fill = factor(outside, labels = c("Indoor", "Outdoor")))) +
  geom_bar(stat = "count") +
  labs(title = "Number of Indoor and Outdoor Incidents by Year",
       x = "Year",
       y = "Count",
       fill = "Location") +
  scale_fill_manual(values = c("Indoor" = "darkorchid", "Outdoor" = "darkgreen")) +
  scale_x_continuous(breaks = seq(min(philly.data2$year), max(philly.data2$year), by = 1)) +
  theme_minimal()

We have exported and uploaded these images separately and will now redefine them:

ar="https://raw.githubusercontent.com/AlexDragonetti/STA553/main/hw8/arplot.png"

out="https://raw.githubusercontent.com/AlexDragonetti/STA553/main/hw8/outsideplot2.png"

yr="https://raw.githubusercontent.com/AlexDragonetti/STA553/main/hw8/yearplot2.png"

3.4 Mapping the Data

Our final visualization will contain a map of shooting victims, fully interactive with demographic information, as well as additional, big-picture data available.

#defining things
pal <- colorFactor(c("blue", "red"), domain = c(0, 1))


ageraceplot = st_as_sf(data.frame(x = -75.4077, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
yearplot = st_as_sf(data.frame(x = -75.3877, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
outdoorplot = st_as_sf(data.frame(x = -75.3677, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
fig <- plot_ly(philly.data2, x = ~lng, y = ~lat, 
               type = 'scatter', 
               mode = 'markers', 
                              marker = list(symbol = 'circle', 
                           sizemode = 'diameter',
                               line = list(width = 2, color = '#FFFFFF')))
              
tag.map.title <- tags$style(HTML("
               .leaflet-control.map-title {
                   transform: translate(50%,50%);
                   position: fixed !important;
                   left: 50%;
                   text-align: center;
                   padding-left: 10px;
                   padding-right: 10px;
                   background: transparent;
                   font-weight: bold;
                   font-size: 18px;}
                 "))

rr <- tags$div(
   HTML('<img border="0" alt="ImageTitle" src="https://raw.githubusercontent.com/AlexDragonetti/STA553/main/hw8/map%20title.png" width="200" height="45">')
 ) 

###
leaflet() %>%
  setView(lng=-75.15092, lat=40.00995, zoom = 11) %>%
  addProviderTiles(providers$CartoDB.DarkMatter, group="Dark") %>%
  addProviderTiles(providers$CartoDB.DarkMatterNoLabels, group="DarkLabel") %>%  
  addProviderTiles(providers$Esri.NatGeoWorldMap, group="Esri") %>%
  addControl(rr, position = "topleft", className="map-title") %>%
  ## mini reference map
  addMiniMap() %>%
  ## neighborhood boundary
  addPolygons(data = phillyNeighbor,
              color = 'skyblue',
              weight = 1)  %>%
  
    
  addCircleMarkers(data = ageraceplot, 
                   color = "white",
                   weight = 2,
                   label = "Distribution of Age by Race",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "ageraceplot") %>%
  addPopupImages(ar, 
                  width = 500,
                  height = 320,
                  tooltip = FALSE,
                  group = "ageraceplot") %>%
    
 addCircleMarkers(data = yearplot, 
                   color = "skyblue",
                   weight = 2,
                   label = "Incident Rate by Year",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "yearplot") %>%
  addPopupImages(yr, 
                  width = 500,
                  height = 320,
                  tooltip = FALSE,
                  group = "yearplot") %>%

  
  addCircleMarkers(data = outdoorplot, 
                   color = "darkgreen",
                   weight = 2,
                   label = "Rate of Indoor vs Outdoor Incidents",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "outdoorplot") %>%
  addPopupImages(out, 
                   width = 500,
                  height = 320,
                   group = "outdoorplot" ) %>%


 
  
  ## plot information on the map
  addCircleMarkers(data = philly.data2,
                   color = ~pal(as.factor(fatal)),
                   stroke = FALSE, 
                   fillOpacity = 0.5,
                   popup = ~popupTable(philly.data2),
                   clusterOptions = markerClusterOptions(maxClusterRadius = 40)) %>%

  
          addLayersControl(baseGroups = c('Dark', 'DarkLabel', 'Esri'),
                   overlayGroups = c("Crime Data"),
                   options = layersControlOptions(collapsed = TRUE)) %>%
  ##
  browsable()

The above map is fully interactive, maintains our red/blue fatal/nonfatal color coding from the previous example, but allows us to provide much more information for each incident, as well as include aggregate data visualizations (by clicking on the dots blocking out Media on the map).

LS0tDQp0aXRsZTogIkludGVyYWN0aXZlIE1hcHMiDQphdXRob3I6ICJBbGV4IERyYWdvbmV0dGkiDQpkYXRlOiAiMy0yNS0yMDI0Ig0Kb3V0cHV0Og0KICBodG1sX2RvY3VtZW50OiANCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2NfY29sbGFwc2VkOiB5ZXMNCiAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICBjb2RlX2Rvd25sb2FkOiB5ZXMNCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlDQogICAgdGhlbWU6IGx1bWVuDQplZGl0b3Jfb3B0aW9uczoNCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQpgYGB7PWh0bWx9DQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCi8qIENhc2NhZGluZyBTdHlsZSBTaGVldHMgKENTUykgaXMgYSBzdHlsZXNoZWV0IGxhbmd1YWdlIHVzZWQgdG8gZGVzY3JpYmUgdGhlIHByZXNlbnRhdGlvbiBvZiBhIGRvY3VtZW50IHdyaXR0ZW4gaW4gSFRNTCBvciBYTUwuIGl0IGlzIGEgc2ltcGxlIG1lY2hhbmlzbSBmb3IgYWRkaW5nIHN0eWxlIChlLmcuLCBmb250cywgY29sb3JzLCBzcGFjaW5nKSB0byBXZWIgZG9jdW1lbnRzLiAqLw0KDQpoMS50aXRsZSB7ICAvKiBUaXRsZSAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgdGhlIHJlcG9ydCB0aXRsZSAqLw0KICBmb250LXNpemU6IDI0cHg7DQogIGNvbG9yOiBEYXJrUmVkOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogIGZvbnQtZmFtaWx5OiAiR2lsbCBTYW5zIiwgc2Fucy1zZXJpZjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgYXV0aG9ycyAgKi8NCiAgZm9udC1zaXplOiAyMHB4Ow0KICBmb250LWZhbWlseTogc3lzdGVtLXVpOw0KICBjb2xvcjogRGFya1JlZDsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDQuZGF0ZSB7IC8qIEhlYWRlciA0IC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgdGhlIGRhdGUgICovDQogIGZvbnQtc2l6ZTogMThweDsNCiAgZm9udC1mYW1pbHk6IHN5c3RlbS11aTsNCiAgY29sb3I6IERhcmtCbHVlOw0KICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMSB7IC8qIEhlYWRlciAxIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMSBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMjJweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQp9DQpoMiB7IC8qIEhlYWRlciAyIC0gZm9udCBzcGVjaWZpY2F0aW9ucyBmb3IgbGV2ZWwgMiBzZWN0aW9uIHRpdGxlICovDQogICAgZm9udC1zaXplOiAyMHB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmgzIHsgLyogSGVhZGVyIDMgLSBmb250IHNwZWNpZmljYXRpb25zIG9mIGxldmVsIDMgc2VjdGlvbiB0aXRsZSAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IG5hdnk7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCg0KaDQgeyAvKiBIZWFkZXIgNCAtIGZvbnQgc3BlY2lmaWNhdGlvbnMgb2YgbGV2ZWwgNCBzZWN0aW9uIHRpdGxlICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogZGFya3JlZDsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpib2R5IHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQouaGlnaGxpZ2h0bWUgeyBiYWNrZ3JvdW5kLWNvbG9yOnllbGxvdzsgfQ0KDQpwIHsgYmFja2dyb3VuZC1jb2xvcjp3aGl0ZTsgfQ0KDQo8L3N0eWxlPg0KYGBgDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBEZXRlY3QsIGluc3RhbGwsIGFuZCBsb2FkIHBhY2thZ2VzIGlmIG5lZWRlZC4NCmlmICghcmVxdWlyZSgidGlkeXZlcnNlIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQogICBsaWJyYXJ5KHRpZHl2ZXJzZSkNCn0NCmlmICghcmVxdWlyZSgia25pdHIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQ0KICAgbGlicmFyeShrbml0cikNCn0NCmlmICghcmVxdWlyZSgic2YiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygic2YiKQ0KICAgbGlicmFyeShzZikNCn0NCmlmICghcmVxdWlyZSgidGVycmEiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygidGVycmEiKQ0KICAgbGlicmFyeSh0ZXJyYSkNCn0NCmlmICghcmVxdWlyZSgicGxvdGx5IikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInBsb3RseSIpDQogICBsaWJyYXJ5KHBsb3RseSkNCn0NCmlmICghcmVxdWlyZSgiZHBseXIiKSkgew0KICAgaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KICAgbGlicmFyeShkcGx5cikNCn0NCmlmICghcmVxdWlyZSgicG5nIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJwbmciKSAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwbmciKQ0KfQ0KaWYgKCFyZXF1aXJlKCJzcERhdGEiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInNwRGF0YSIpICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInNwRGF0YSIpDQp9DQppZiAoIXJlcXVpcmUoImNvbG91cnBpY2tlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiY29sb3VycGlja2VyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImNvbG91cnBpY2tlciIpDQp9DQppZiAoIXJlcXVpcmUoImdpZnNraSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2lmc2tpIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdpZnNraSIpDQp9DQppZiAoIXJlcXVpcmUoIm1hZ2ljayIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm1hZ2ljayIpDQp9DQppZiAoIXJlcXVpcmUoInNwRGF0YUxhcmdlIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzcERhdGFMYXJnZSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJzcERhdGFMYXJnZSIpDQp9DQojIyMgZ2dwbG90IGFuZCBleHRlbnNpb25zDQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgiZ2dwbG90MiIpDQp9DQppZiAoIXJlcXVpcmUoImdnYW5pbWF0ZSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiZ2dhbmltYXRlIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdnYW5pbWF0ZSIpDQp9DQppZiAoIXJlcXVpcmUoInRtYXAiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInRtYXAiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgidG1hcCIpDQp9DQppZiAoIXJlcXVpcmUoInNmIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzZiIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJzZiIpDQp9DQppZiAoIXJlcXVpcmUoInRpZ3JpcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygidGlncmlzIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInRpZ3JpcyIpDQp9DQppZiAoIXJlcXVpcmUoIm1hcHZpZXciKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1hcHZpZXciKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgibWFwdmlldyIpDQp9DQppZiAoIXJlcXVpcmUoInBhbmRlciIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicGFuZGVyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInBhbmRlciIpDQp9DQppZiAoIXJlcXVpcmUoImxhdHRpY2UiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxhdHRpY2UiKQ0KbGlicmFyeSgibGF0dGljZSIpDQp9DQppZiAoIXJlcXVpcmUoInNwIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzcCIpDQpsaWJyYXJ5KCJzcCIpDQp9DQppZiAoIXJlcXVpcmUoImxlYWZsZXQiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxlYWZsZXQiKQ0KbGlicmFyeSgibGVhZmxldCIpDQp9DQppZiAoIXJlcXVpcmUoImxlYWZwb3AiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxlYWZwb3AiKQ0KbGlicmFyeSgibGVhZnBvcCIpDQp9DQppZiAoIXJlcXVpcmUoImxlYWZlbSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibGVhZmVtIikNCmxpYnJhcnkoImxlYWZlbSIpDQp9DQppZiAoIXJlcXVpcmUoInNwRGF0YUxhcmdlIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzcERhdGFMYXJnZSIsIHJlcG9zID0gImh0dHBzOi8vZ2VvY29tcHIuci11bml2ZXJzZS5kZXYiKQ0KbGlicmFyeSgic3BEYXRhTGFyZ2UiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJodG1sd2lkZ2V0cyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygiaHRtbHdpZGdldHMiKQ0KbGlicmFyeSgiaHRtbHdpZGdldHMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJsZWFmbGV0LmV4dHJhcyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygibGVhZmxldC5leHRyYXMiKQ0KbGlicmFyeSgibGVhZmxldC5leHRyYXMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJodG1sdG9vbHMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImh0bWx0b29scyIpDQpsaWJyYXJ5KCJodG1sdG9vbHMiKQ0KfQ0KaWYoIXJlcXVpcmUoInBuZyIpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygicG5nIikNCiAgbGlicmFyeShwbmcpDQp9DQppZighcmVxdWlyZSgidmlyaWRpcyIpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygidmlyaWRpcyIpDQogIGxpYnJhcnkodmlyaWRpcykNCn0NCmlmKCFyZXF1aXJlKCJnZ21hcCIpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2dtYXAiKQ0KICBsaWJyYXJ5KGdnbWFwKQ0KfQ0KaWYoIXJlcXVpcmUoIndlYnNob3QiKSl7DQogIGluc3RhbGwucGFja2FnZXMoIndlYnNob3QiKQ0KICBsaWJyYXJ5KHdlYnNob3QpDQp9DQppZighcmVxdWlyZSgiaHRtbHdpZGdldHMiKSl7DQogIGluc3RhbGwucGFja2FnZXMoImh0bWx3aWRnZXRzIikNCiAgbGlicmFyeShodG1sd2lkZ2V0cykNCn0NCmlmKCFyZXF1aXJlKCJhbmltYXRpb24iKSl7DQogIGluc3RhbGwucGFja2FnZXMoImFuaW1hdGlvbiIpDQogIGxpYnJhcnkoYW5pbWF0aW9uKQ0KfQ0KaWYoIXJlcXVpcmUoImdpZnNraSIpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiZ2lmc2tpIikNCiAgbGlicmFyeShnaWZza2kpDQp9DQppZighcmVxdWlyZSgiaHRtbFRhYmxlIikpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJodG1sVGFibGUiKQ0KICBsaWJyYXJ5KGh0bWxUYWJsZSkNCn0NCmlmKCFyZXF1aXJlKCJtYWdyaXR0ciIpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygibWFncml0dHIiKQ0KICBsaWJyYXJ5KG1hZ3JpdHRyKQ0KfQ0KIyBTcGVjaWZpY2F0aW9ucyBvZiBvdXRwdXRzIG9mIGNvZGUgaW4gY29kZSBjaHVua3MNCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSwgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgd2FybmluZyA9IEZBTFNFLCAgIA0KICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdCA9IFRSVUUsICAgDQogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgICAgIGNvbW1lbnQgPSBOQSkNCmBgYA0KDQoNCg0KIyBNYXAgMTogUmFuZG9tbHkgU2VsZWN0ZWQgR2FzIFN0YXRpb25zDQoNCg0KIyMgT3ZlcnZpZXcNCg0KDQpPdXIgZmlyc3QgdmlzdWFsaXphdGlvbiB3aWxsIGJlIGFuIGludGVyYWN0aXZlIG1hcCwgY29udGFpbmluZyB2YXJpb3VzIGxvY2F0aW9ucyBvZiBnYXMgc3RhdGlvbnMgZnJvbSB0aGUgY29udGVudGlhbCBVbml0ZWQgU3RhdGVzLiBFYWNoIGdhcyBzdGF0aW9uIHdpbGwgYmUgYSBwb2ludCBvbiB0aGUgbWFwLCBhbmQgZWFjaCBwb2ludCB3aWxsIGhhdmUgYSBob3ZlciB0ZXh0IGJveCBjb250YWluaW5nIHN0YXRlLCBjb3VudHksIGFkZHJlc3MsIGFuZCB6aXAgY29kZS4NCg0KDQojIyBSYW5kb21seSBTZWxlY3RpbmcgRGF0YQ0KDQoNClNpbmNlIG91ciBzb3VyY2UgZGF0YSBjb250YWlucyBuZWFybHkgNzMsMDAwIG9ic2VydmF0aW9ucywgd2Ugd2lsbCByYW5kb21seSBzZWxlY3QgNTAwIG9ic2VydmF0aW9ucy4NCg0KYGBge3J9DQpnYXM8LXJlYWQuY3N2KCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9kYXRhc2V0cy9QT0MvUE9DLmNzdiIpDQoNCnJhbmQuZGYgPC0gZ2FzW3NhbXBsZShucm93KGdhcyksIHNpemU9NTAwKSwgXQ0KYGBgDQoNCg0KIyMgTWFwcGluZyB0aGUgRGF0YQ0KDQoNCldlIHdpbGwgdXNlIHRoZSBgcGxvdGx5YCBwYWNrYWdlIHRvIG1hcCB0aGUgZGF0YSBvbnRvIGFuIGludGVyYWN0aXZlIG1hcC4NCg0KDQpgYGB7cn0NCmcgPC0gbGlzdCggICAgICBzY29wZSA9ICdwJywNCiAgICAgICAgICAgICAgICBwcm9qZWN0aW9uID0gbGlzdCh0eXBlID0gJ2FsYmVycyB1c2EnKSwNCiAgICAgICAgICAgICAgICBzaG93bGFuZCA9IFRSVUUsDQogICAgICAgICAgICAgICAgbGFuZGNvbG9yID0gdG9SR0IoImdyYXk5NSIpLA0KICAgICAgICAgICAgICAgIHN1YnVuaXRjb2xvciA9IHRvUkdCKCJncmF5ODUiKSwNCiAgICAgICAgICAgICAgICBjb3VudHJ5Y29sb3IgPSB0b1JHQigiZ3JheTg1IiksDQogICAgICAgICAgICAgICAgY291bnRyeXdpZHRoID0gMC41LA0KICAgICAgICAgICAgICAgIHN1YnVuaXR3aWR0aCA9IDAuNQ0KKQ0KDQoNCmZpZyA8LSBwbG90X2dlbyhyYW5kLmRmLCBsYXQgPSB+eWNvb3JkLCBsb24gPSB+eGNvb3JkKSAlPiUgDQogIGFkZF9tYXJrZXJzKCB0ZXh0ID0gfnBhc3RlKFNUQVRFLCBjb3VudHksIEFERFJFU1MsIFpJUG5ldywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNlcCA9ICI8YnI+IiksDQogICAgICAgICAgICAgICAjY29sb3IgPSAsIA0KICAgICAgICAgICAgICAgc3ltYm9sID0gImNpcmNsZSIsIA0KICAgICAgICAgICAgICAgI3NpemUgPSAsIA0KICAgICAgICAgICAgICAgaG92ZXJpbmZvID0gInRleHQiKSAgICU+JSANCiAgbGF5b3V0KCB0aXRsZSA9ICdSYW5kb21seSBTZWxlY3RlZCBVUyBHYXMgU3RhdGlvbnMnLCANCiAgICAgICAgICBnZW8gPSBnICkNCg0KZmlnDQpgYGANCg0KDQpUaGUgYWJvdmUgaXMgYSBkZW1vbnN0cmF0aW9uIG9mIGEgc2ltcGxlIC0geWV0IGVmZmVjdGl2ZSAtIHdheSB0byB2aXN1YWxpemUgZGF0YSBvbiBhIG1hcC4gT3VyIG5leHQgd2lsbCBiZSBhIGxpdHRsZSBtb3JlIGNvbXBsZXguDQoNCg0KDQojIE1hcCAyOiBDcmltZSBpbiBQaGlsYWRlbHBoaWEsIDIwMjMNCg0KDQojIyBPdmVydmlldw0KDQoNCkZvciB0aGlzIHZpc3VhbGl6YXRpb24sIHdlIHdpbGwgYmVnaW4gd2l0aCBhIGRhdGFzZXQgb2YgY3JpbWVzIGNvbW1pdHRlZCBpbiB0aGUgY2l0eSBvZiBQaGlsYWRlbHBoaWEgYmV0d2VlbiAyMDE1IGFuZCBlYXJseSBNYXJjaCBvZiAyMDI0Lg0KDQoNCiMjIFByZXBhcmluZyB0aGUgRGF0YQ0KDQpXZSB3aWxsIG5lZWQgdG8gc3Vic2V0IHRoZSAyMDIzIGRhdGEgYmVmb3JlIHdlIGNhbiBpbXBvc2UgaXQgb3ZlciBhIG1hcC4gV2Ugd2lsbCB1c2UgdGhlIGBzdHJpbmdyYCBsaWJyYXJ5IGZvciB0aGlzLg0KDQpgYGB7cn0NCmNyaW1lPC1yZWFkLmNzdigiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUzVklaL3cwOC9QaGlsbHlDcmltZVNpbmNlMjAxNS5jc3YiKQ0KDQpkZjwtY3JpbWUNCg0KZGYkeWVhciA8LSBzdHJfZXh0cmFjdChkZiRkYXRlLCAiXFxkezR9IikNCmRmJHllYXIgPC0gYXMubnVtZXJpYyhkZiR5ZWFyKQ0KDQpjcmltZTIzPC1zdWJzZXQoZGYsIHllYXI9PTIwMjMpDQoNCndyaXRlLmNzdihjcmltZTIzLCAiQzpcXFVzZXJzXFxBbGV4XFxEb2N1bWVudHNcXFJcXEdyYWRcXDU1M1xcZGF0YXNldHNcXHdrNy5jc3YiKQ0KYGBgDQoNCkEgY29weSBvZiB0aGUgMjAyMyBkYXRhIGNhbiBiZSBmb3VuZCBhdCBodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQWxleERyYWdvbmV0dGkvU1RBNTUzL21haW4vaHc3L3drNy5jc3YNCg0KYGBge3J9DQojcmVtb3ZlIG9ic2VydmF0aW9ucyB3aXRoIG1pc3NpbmcgdmFsdWVzIC0gYXQgbGVhc3Qgb25lIGhhcyBhIG1pc3NpbmcgdmFsdWUgZm9yIGNvb3JkaW5hdGVzDQpjcmltZTIzLm5vbmE8LW5hLm9taXQoY3JpbWUyMykNCg0KYGBgDQoNCg0KDQojIyBNYXBwaW5nIHRoZSBEYXRhDQoNCg0KRmluYWxseSwgd2Ugd2lsbCBtYXAgdGhlIGluY2lkZW50IGRhdGEgdXNpbmcgdGhlIGBsZWFmbGV0YCBwYWNrYWdlLg0KDQpgYGB7cn0NCmNvbG9yMiA8LSByZXAoInJlZCIsIGxlbmd0aChjcmltZTIzLm5vbmEpKQ0KY29sb3IyW3doaWNoKGNyaW1lMjMubm9uYSRmYXRhbD09Ik5vbmZhdGFsIildIDwtICJibHVlIg0KY29sb3IyW3doaWNoKGNyaW1lMjMubm9uYSRmYXRhbD09IkZhdGFsIildIDwtICJyZWQiDQoNCg0KDQpsYWJlbC5tc2cgPC0gcGFzdGUoIlN0cmVldDoiLCBjcmltZTIzLm5vbmEkc3RyZWV0X25hbWUsICAgIA0KICAgICAgICAgICAgICAgICAgICI8YnI+QmxvY2sgTnVtYmVyOiIsY3JpbWUyMy5ub25hJGJsb2NrX251bWJlciwNCiAgICAgICAgICAgICAgICAgICAiPGJyPk5laWdoYm9yaG9vZDoiLCBjcmltZTIzLm5vbmEkbmVpZ2hib3Job29kLA0KICAgICAgICAgICAgICAgICAgICI8YnI+SW5jaWRlbnQgVHlwZToiLCBjcmltZTIzLm5vbmEkZmF0YWwpDQoNCg0KbGVhZmxldChjcmltZTIzLm5vbmEpICU+JQ0KICBhZGRUaWxlcygpICU+JSANCiAgc2V0Vmlldyhsbmc9bWVhbihjcmltZTIzLm5vbmEkbG5nKSwgbGF0PW1lYW4oY3JpbWUyMy5ub25hJGxhdCksIHpvb20gPSAxMSkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJEVzcmkuV29ybGRHcmF5Q2FudmFzKSAlPiUNCiAgYWRkQ2lyY2xlTWFya2VycygNCiAgICAgICAgICAgIH5sbmcsIA0KICAgICAgICAgICAgfmxhdCwNCiAgICAgICAgICAgIGNvbG9yID0gY29sb3IyLA0KICAgICAgICAgICAgc3Ryb2tlID0gRkFMU0UsIA0KICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICBwb3B1cD0gfmxhYmVsLm1zZykgICU+JQ0KICBhZGRMZWdlbmQocG9zaXRpb24gPSAiYm90dG9tcmlnaHQiLCANCiAgICAgICAgICAgIGNvbG9ycyA9IGMoInJlZCIsICJibHVlIiksDQogICAgICAgICAgICBsYWJlbHM9IGMoIkZhdGFsIiwgIk5vbmZhdGFsIiksDQogICAgICAgICAgICB0aXRsZT0gIlR5cGUgb2YgSW5jaWRlbnQiLA0KICAgICAgICAgICAgb3BhY2l0eSA9IDAuNCkNCmBgYA0KDQpPdXIgcmVzdWx0aW5nIGdyYXBoIGlzIGZ1bGx5IGludGVyYWN0aXZlIC0gY2xpY2tpbmcgYSBkb3Qgd2lsbCBzaG93IGRldGFpbHMgb2YgdGhlIGluY2lkZW50LiANCg0KUGxlYXNlIG5vdGUgdGhhdCB0aGUgYWJvdmUgZ3JhcGggaGFzIHNwb3RzIHRoYXQgYXBwZWFyIHB1cnBsZS4gVGhpcyBpcyBkdWUgdG8gdGhlIG9wYXF1ZSwgb3ZlcmxhcHBpbmcgcmVkIGFuZCBibHVlIGRvdHMsIGluZGljYXRpbmcgYm90aCBmYXRhbCBhbmQgbm9uZmF0YWwgaW5jaWRlbnRzIGF0IHRoZSBzYW1lIGFkZHJlc3MuIEZvciBleGFtcGxlLCAxMDAwIEUgQnJpc3RvbCBTdHJlZXQgaW4gSnVuaWF0YSBzYXcgYW4gaW5jaWRlbnQgd2l0aCB0d28gdmljdGltcywgb25lIGJlaW5nIGEgZmF0YWxpdHkuIEZvciBjbGFyaWZpY2F0aW9uIG9uIGFueSBjb25mdXNpbmcgcG9pbnQsIHBsZWFzZSByZWZlciB0byB0aGUgZGF0YXNldCBsaW5rZWQgYXQgdGhlIGVuZCBvZiBgUHJlcGFyaW5nIHRoZSBEYXRhYC4NCg0KDQoNCiMgTWFwIDM6IFBoaWxhZGVscGhpYSBTaG9vdGluZ3MsIDIwMTUtMjAyNA0KDQoNCiMjIE92ZXJ2aWV3DQoNCk91ciBuZXh0IHZpc3VhbGl6YXRpb24gd2lsbCBzcGVjaWZpY2FsbHkgZm9jdXMgb24gdGhlIGRlbW9ncmFwaGljcyBvZiBzaG9vdGluZyB2aWN0aW1zLiBUaGUgZGF0YSBpcyBmcm9tIE9wZW5EYXRhUGhpbGx5IGFuZCBjYW4gYmUgYWNjZXNzZWQgaGVyZTpodHRwczovL29wZW5kYXRhcGhpbGx5Lm9yZy9kYXRhc2V0cy9zaG9vdGluZy12aWN0aW1zIChzZWUgYmVsb3cgY29kZSBmb3IgcmF3LCBkaXJlY3QgbGluaykuIFdlIHdpbGwgYWdhaW4gdXRpbGl6ZSBgbGVhZmxldGAuDQoNCg0KYGBge3J9DQoNCnBoaWxseS5kYXRhPC1yZWFkLmNzdigiaHR0cHM6Ly9waGwuY2FydG8uY29tL2FwaS92Mi9zcWw/cT1TRUxFQ1QrKiwrU1RfWSh0aGVfZ2VvbSkrQVMrbGF0LCtTVF9YKHRoZV9nZW9tKStBUytsbmcrRlJPTStzaG9vdGluZ3MmZmlsZW5hbWU9c2hvb3RpbmdzJmZvcm1hdD1jc3Ymc2tpcGZpZWxkcz1jYXJ0b2RiX2lkIikNCnBoaWxseU5laWdoYm9yU2hvb3RpbmcgIDwtIG5hLm9taXQoc3RfcmVhZCgiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUzVklaL3cwOC9QaGlsbHlTaG9vdGluZ3MuZ2VvanNvbiIpKQ0KcGhpbGx5TmVpZ2hib3IgIDwtIHN0X3JlYWQoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvTmVpZ2hib3Job29kc19QaGlsYWRlbHBoaWEuZ2VvanNvbiIpDQpwaGlsbHkgIDwtIHN0X3JlYWQoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvUGhpbGx5TmVpZ2hib3Job29kLWJsb2Nrcy5nZW9qc29uIikNCmBgYA0KDQoNCiMjIFByZXBhcmluZyB0aGUgRGF0YQ0KDQpXaGlsZSB0aGUgZGF0YSBpcyB1c2FibGUsIHdlIGludGVuZCB0byBkaXNwbGF5IG11Y2ggbW9yZSBpbmZvcm1hdGlvbiB3aXRoIGVhY2ggcG9pbnQsIHNvIHdlIHdpbGwgcmVtb3ZlIHVubmVjZXNzYXJ5IG9yIHJlZHVuZGFudCBkYXRhLCB3aGlsZSBmaXhpbmcgYSBmb3JtYXR0aW5nIHF1aXJrIHdpdGggdGhlICdkYXRlXycgdmFyaWFibGUgKHdoaWNoIHdpbGwgYWxzbyBiZSByZW5hbWVkIHRvICdkYXRlJykgdXNpbmcgdGhlIGBzdHJpbmdyYCBwYWNrYWdlLiBXaXRob3V0IHRoaXMsIHRoZSAnZGF0ZV8nIHZhcmlhYmxlIGVuZHMgZWFjaCBlbnRyeSB3aXRoICcwMDowMDowMCswMCcsIHdoaWNoIGlzIGNsdW5reSBhbmQgbGlrZWx5IHVuaW50ZW5kZWQuDQoNCmBgYHtyfQ0KcGhpbGx5LmRhdGEyIDwtIHBoaWxseS5kYXRhICU+JQ0KICBzZWxlY3QoLWMoMSwgMiwgMywgNSwgNiwgMTcsIDE4KSkNCg0KbmFtZXMocGhpbGx5LmRhdGEyKVt3aGljaChuYW1lcyhwaGlsbHkuZGF0YTIpID09ICJkYXRlXyIpXSA8LSAiZGF0ZSINCg0KcGhpbGx5LmRhdGEyJGRhdGUgPC0gc3RyX3JlcGxhY2UocGhpbGx5LmRhdGEyJGRhdGUsICIgMDA6MDA6MDBcXCswMCIsICIiKQ0KDQojIENvbnZlcnQgJ2RhdGUnIHZhcmlhYmxlIHRvIERhdGUgZm9ybWF0IGZvciBwb3NzaWJsZSBmdXR1cmUgdXNlDQpwaGlsbHkuZGF0YTIkZGF0ZSA8LSBhcy5EYXRlKHBoaWxseS5kYXRhMiRkYXRlLCBmb3JtYXQgPSAiJVktJW0tJWQiKQ0KYGBgDQoNCg0KDQojIyBQcmVwYXJpbmcgVmlzdWFsaXphdGlvbiBmb3IgQWdncmVnYXRlIERhdGENCg0KDQpXaXRoIG91dCBtYXAsIHdlIHdvdWxkIGxpa2UgdG8gZmVhdHVyZSBpbmRpdmlkdWFsIGluY2lkZW50cyBhbmQgYWxsb3cgc29tZW9uZSB0byBjbGljayB0byBzZWUgdmljdGltIGRlbW9ncmFwaGljIGluZm9ybWF0aW9uLCBidXQgd2Ugd291bGQgYWxzbyBsaWtlIHRvIGhhdmUgJ2JpZyBwaWN0dXJlJyBhZ2dyZWdhdGUgcGxvdHMgYXZhaWxhYmxlIGFzIHdlbGwuIFdlIHdpbGwgcHJlcGFyZSB0aHJlZSBzaW1wbGUgcGxvdHMgaGVyZSB1c2luZyBgZ2dwbG90YCBmb3IgaWxsdXN0cmF0aXZlIHB1cnBvc2VzLg0KDQoNCmBgYHtyfQ0KDQojRGlzdHJpYnV0aW9uIG9mIEFnZSBhY3Jvc3MgUmFjZQ0KDQphci5wbG90PC1nZ3Bsb3QocGhpbGx5LmRhdGEyLCBhZXMoeCA9IGFnZSwgZmlsbCA9IHJhY2UpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYSA9IDAuNSkgKw0KICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiBBZ2UgYnkgUmFjZSIsDQogICAgICAgeCA9ICJBZ2UiLA0KICAgICAgIHkgPSAiRGVuc2l0eSIpICsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlJhY2UiKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQojUHJvYmFibHkgdW5uZWNlc3Nhcnkgc3RlcCwgYnV0IG1hZGUgYW4gZXh0cmEgZGF0YXNldCB3aGlsZSB0ZXN0aW5nIHNvbWV0aGluZyBvdXQNCnBoaWxseS5kYXRhMzwtc3Vic2V0KHBoaWxseS5kYXRhMikNCnBoaWxseS5kYXRhMyRmYXRhbCA8LSBmYWN0b3IocGhpbGx5LmRhdGEzJGZhdGFsLCBsZXZlbHMgPSBjKDAsIDEpLCBsYWJlbHMgPSBjKCJOb25mYXRhbCIsICJGYXRhbCIpKQ0KDQojIFBsb3QgdGhlIHBvaW50IGdyYXBoIHdpdGggY29sb3JlZCBwb2ludHMNCnllYXIucGxvdCA8LSBnZ3Bsb3QocGhpbGx5LmRhdGEzLCBhZXMoeCA9IHllYXIsIGZpbGwgPSBmYXRhbCkpICsNCiAgZ2VvbV9iYXIocG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuNSksIHN0YXQgPSAiY291bnQiKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk5vbmZhdGFsIiA9ICJibHVlIiwgIkZhdGFsIiA9ICJyZWQiKSkgKw0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBGYXRhbCBhbmQgTm9uZmF0YWwgSW5jaWRlbnRzIGJ5IFllYXIiLA0KICAgICAgIHggPSAiWWVhciIsDQogICAgICAgeSA9ICJDb3VudCIpICsNCiAgdGhlbWVfbWluaW1hbCgpICsNCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcShtaW4ocGhpbGx5LmRhdGEzJHllYXIpLCBtYXgocGhpbGx5LmRhdGEzJHllYXIpLCBieSA9IDEpKQ0KDQojRnJlcXVlbmN5IG9mIGluZG9vciB2cyBvdXRkb29yIGluY2lkZW50cw0KDQpvdXRzaWRlLnBsb3QgPC0gZ2dwbG90KHBoaWxseS5kYXRhMiwgYWVzKHggPSB5ZWFyLCBmaWxsID0gZmFjdG9yKG91dHNpZGUsIGxhYmVscyA9IGMoIkluZG9vciIsICJPdXRkb29yIikpKSkgKw0KICBnZW9tX2JhcihzdGF0ID0gImNvdW50IikgKw0KICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBJbmRvb3IgYW5kIE91dGRvb3IgSW5jaWRlbnRzIGJ5IFllYXIiLA0KICAgICAgIHggPSAiWWVhciIsDQogICAgICAgeSA9ICJDb3VudCIsDQogICAgICAgZmlsbCA9ICJMb2NhdGlvbiIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiSW5kb29yIiA9ICJkYXJrb3JjaGlkIiwgIk91dGRvb3IiID0gImRhcmtncmVlbiIpKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzZXEobWluKHBoaWxseS5kYXRhMiR5ZWFyKSwgbWF4KHBoaWxseS5kYXRhMiR5ZWFyKSwgYnkgPSAxKSkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KYGBgDQoNCldlIGhhdmUgZXhwb3J0ZWQgYW5kIHVwbG9hZGVkIHRoZXNlIGltYWdlcyBzZXBhcmF0ZWx5IGFuZCB3aWxsIG5vdyByZWRlZmluZSB0aGVtOg0KDQpgYGB7cn0NCg0KYXI9Imh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9BbGV4RHJhZ29uZXR0aS9TVEE1NTMvbWFpbi9odzgvYXJwbG90LnBuZyINCg0Kb3V0PSJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQWxleERyYWdvbmV0dGkvU1RBNTUzL21haW4vaHc4L291dHNpZGVwbG90Mi5wbmciDQoNCnlyPSJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQWxleERyYWdvbmV0dGkvU1RBNTUzL21haW4vaHc4L3llYXJwbG90Mi5wbmciDQoNCmBgYA0KDQoNCiMjIE1hcHBpbmcgdGhlIERhdGENCg0KDQpPdXIgZmluYWwgdmlzdWFsaXphdGlvbiB3aWxsIGNvbnRhaW4gYSBtYXAgb2Ygc2hvb3RpbmcgdmljdGltcywgZnVsbHkgaW50ZXJhY3RpdmUgd2l0aCBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiwgYXMgd2VsbCBhcyBhZGRpdGlvbmFsLCBiaWctcGljdHVyZSBkYXRhIGF2YWlsYWJsZS4NCg0KDQpgYGB7cn0NCiNkZWZpbmluZyB0aGluZ3MNCnBhbCA8LSBjb2xvckZhY3RvcihjKCJibHVlIiwgInJlZCIpLCBkb21haW4gPSBjKDAsIDEpKQ0KDQoNCmFnZXJhY2VwbG90ID0gc3RfYXNfc2YoZGF0YS5mcmFtZSh4ID0gLTc1LjQwNzcsIHkgPSAzOS45MTY4KSwNCiAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJ4IiwgInkiKSwNCiAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQ0KeWVhcnBsb3QgPSBzdF9hc19zZihkYXRhLmZyYW1lKHggPSAtNzUuMzg3NywgeSA9IDM5LjkxNjgpLA0KICAgICAgICAgICAgICAgIGNvb3JkcyA9IGMoIngiLCAieSIpLA0KICAgICAgICAgICAgICAgIGNycyA9IDQzMjYpDQpvdXRkb29ycGxvdCA9IHN0X2FzX3NmKGRhdGEuZnJhbWUoeCA9IC03NS4zNjc3LCB5ID0gMzkuOTE2OCksDQogICAgICAgICAgICAgICAgY29vcmRzID0gYygieCIsICJ5IiksDQogICAgICAgICAgICAgICAgY3JzID0gNDMyNikNCmBgYA0KDQpgYGB7cn0NCmZpZyA8LSBwbG90X2x5KHBoaWxseS5kYXRhMiwgeCA9IH5sbmcsIHkgPSB+bGF0LCANCiAgICAgICAgICAgICAgIHR5cGUgPSAnc2NhdHRlcicsIA0KICAgICAgICAgICAgICAgbW9kZSA9ICdtYXJrZXJzJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KHN5bWJvbCA9ICdjaXJjbGUnLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemVtb2RlID0gJ2RpYW1ldGVyJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaW5lID0gbGlzdCh3aWR0aCA9IDIsIGNvbG9yID0gJyNGRkZGRkYnKSkpDQogICAgICAgICAgICAgIA0KdGFnLm1hcC50aXRsZSA8LSB0YWdzJHN0eWxlKEhUTUwoIg0KICAgICAgICAgICAgICAgLmxlYWZsZXQtY29udHJvbC5tYXAtdGl0bGUgew0KICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlKDUwJSw1MCUpOw0KICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBmaXhlZCAhaW1wb3J0YW50Ow0KICAgICAgICAgICAgICAgICAgIGxlZnQ6IDUwJTsNCiAgICAgICAgICAgICAgICAgICB0ZXh0LWFsaWduOiBjZW50ZXI7DQogICAgICAgICAgICAgICAgICAgcGFkZGluZy1sZWZ0OiAxMHB4Ow0KICAgICAgICAgICAgICAgICAgIHBhZGRpbmctcmlnaHQ6IDEwcHg7DQogICAgICAgICAgICAgICAgICAgYmFja2dyb3VuZDogdHJhbnNwYXJlbnQ7DQogICAgICAgICAgICAgICAgICAgZm9udC13ZWlnaHQ6IGJvbGQ7DQogICAgICAgICAgICAgICAgICAgZm9udC1zaXplOiAxOHB4O30NCiAgICAgICAgICAgICAgICAgIikpDQoNCnJyIDwtIHRhZ3MkZGl2KA0KICAgSFRNTCgnPGltZyBib3JkZXI9IjAiIGFsdD0iSW1hZ2VUaXRsZSIgc3JjPSJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vQWxleERyYWdvbmV0dGkvU1RBNTUzL21haW4vaHc4L21hcCUyMHRpdGxlLnBuZyIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI0NSI+JykNCiApIA0KDQojIyMNCmxlYWZsZXQoKSAlPiUNCiAgc2V0Vmlldyhsbmc9LTc1LjE1MDkyLCBsYXQ9NDAuMDA5OTUsIHpvb20gPSAxMSkgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuRGFya01hdHRlciwgZ3JvdXA9IkRhcmsiKSAlPiUNCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5EYXJrTWF0dGVyTm9MYWJlbHMsIGdyb3VwPSJEYXJrTGFiZWwiKSAlPiUgIA0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRFc3JpLk5hdEdlb1dvcmxkTWFwLCBncm91cD0iRXNyaSIpICU+JQ0KICBhZGRDb250cm9sKHJyLCBwb3NpdGlvbiA9ICJ0b3BsZWZ0IiwgY2xhc3NOYW1lPSJtYXAtdGl0bGUiKSAlPiUNCiAgIyMgbWluaSByZWZlcmVuY2UgbWFwDQogIGFkZE1pbmlNYXAoKSAlPiUNCiAgIyMgbmVpZ2hib3Job29kIGJvdW5kYXJ5DQogIGFkZFBvbHlnb25zKGRhdGEgPSBwaGlsbHlOZWlnaGJvciwNCiAgICAgICAgICAgICAgY29sb3IgPSAnc2t5Ymx1ZScsDQogICAgICAgICAgICAgIHdlaWdodCA9IDEpICAlPiUNCiAgDQogICAgDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IGFnZXJhY2VwbG90LCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwNCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJEaXN0cmlidXRpb24gb2YgQWdlIGJ5IFJhY2UiLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiYWdlcmFjZXBsb3QiKSAlPiUNCiAgYWRkUG9wdXBJbWFnZXMoYXIsIA0KICAgICAgICAgICAgICAgICAgd2lkdGggPSA1MDAsDQogICAgICAgICAgICAgICAgICBoZWlnaHQgPSAzMjAsDQogICAgICAgICAgICAgICAgICB0b29sdGlwID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBncm91cCA9ICJhZ2VyYWNlcGxvdCIpICU+JQ0KICAgIA0KIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHllYXJwbG90LCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJza3libHVlIiwNCiAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLA0KICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIkluY2lkZW50IFJhdGUgYnkgWWVhciIsDQogICAgICAgICAgICAgICAgICAgc3Ryb2tlID0gRkFMU0UsIA0KICAgICAgICAgICAgICAgICAgIGZpbGxPcGFjaXR5ID0gMC45NSwNCiAgICAgICAgICAgICAgICAgICBncm91cCA9ICJ5ZWFycGxvdCIpICU+JQ0KICBhZGRQb3B1cEltYWdlcyh5ciwgDQogICAgICAgICAgICAgICAgICB3aWR0aCA9IDUwMCwNCiAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDMyMCwNCiAgICAgICAgICAgICAgICAgIHRvb2x0aXAgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gInllYXJwbG90IikgJT4lDQoNCiAgDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IG91dGRvb3JwbG90LCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJkYXJrZ3JlZW4iLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiUmF0ZSBvZiBJbmRvb3IgdnMgT3V0ZG9vciBJbmNpZGVudHMiLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAib3V0ZG9vcnBsb3QiKSAlPiUNCiAgYWRkUG9wdXBJbWFnZXMob3V0LCANCiAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDUwMCwNCiAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDMyMCwNCiAgICAgICAgICAgICAgICAgICBncm91cCA9ICJvdXRkb29ycGxvdCIgKSAlPiUNCg0KDQogDQogIA0KICAjIyBwbG90IGluZm9ybWF0aW9uIG9uIHRoZSBtYXANCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gcGhpbGx5LmRhdGEyLA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfnBhbChhcy5mYWN0b3IoZmF0YWwpKSwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cG9wdXBUYWJsZShwaGlsbHkuZGF0YTIpLA0KICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMobWF4Q2x1c3RlclJhZGl1cyA9IDQwKSkgJT4lDQoNCiAgDQogICAgICAgICAgYWRkTGF5ZXJzQ29udHJvbChiYXNlR3JvdXBzID0gYygnRGFyaycsICdEYXJrTGFiZWwnLCAnRXNyaScpLA0KICAgICAgICAgICAgICAgICAgIG92ZXJsYXlHcm91cHMgPSBjKCJDcmltZSBEYXRhIiksDQogICAgICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKGNvbGxhcHNlZCA9IFRSVUUpKSAlPiUNCiAgIyMNCiAgYnJvd3NhYmxlKCkNCg0KYGBgDQoNCg0KDQpUaGUgYWJvdmUgbWFwIGlzIGZ1bGx5IGludGVyYWN0aXZlLCBtYWludGFpbnMgb3VyIHJlZC9ibHVlIGZhdGFsL25vbmZhdGFsIGNvbG9yIGNvZGluZyBmcm9tIHRoZSBwcmV2aW91cyBleGFtcGxlLCBidXQgYWxsb3dzIHVzIHRvIHByb3ZpZGUgbXVjaCBtb3JlIGluZm9ybWF0aW9uIGZvciBlYWNoIGluY2lkZW50LCBhcyB3ZWxsIGFzIGluY2x1ZGUgYWdncmVnYXRlIGRhdGEgdmlzdWFsaXphdGlvbnMgKGJ5IGNsaWNraW5nIG9uIHRoZSBkb3RzIGJsb2NraW5nIG91dCBNZWRpYSBvbiB0aGUgbWFwKS4=