1 Introduction

In this note, we use a real data set containing crime cases since 2015 in Philadelphia neighborhoods as an example to illustrate various visual inspections of the given data including basic EDA and inference statistics if the data set has relevant information for inference. You can find other related data sets with information from OpenDataPhilly.

2 Map Shapefiles

We have introduced two basic types of maps: choropleth and reference maps. When analyzing data involving geo-information of specific geographical regions at certain levels such as blocks, neighborhoods, districts, zip codes, counties, states, etc, we need to use a related boundary shapefile to draw the related regions. We use filled colors to denote the values of a key variable of interest. In general, making a choropleth map requires a process of data integration in which other relevant data sources with be merged with the shapefile.

include_graphics("shapeFileContent.png")

Plotting a reference map is relatively straightforward, the only required geo-information is longitude and latitude. The point sizes and colors can be mapped to the values of variables other than longitude and latitude.

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 a 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 the neighborhood level to make a choropleth map and a scatter-plot map to display information on 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 Visual Design Process

The process of visualization involves several steps. In order to create an effective visualization, we need to know the data, the audience needs, the principles of visualization, the relevant information to be visualized, the appropriate tools for creating visualization, and

  • The initial step is to know the data
    • population or sample?
    • random sample or non-random sample?
    • information available in the data?
    • cross-sectional or longitudinal data?
    • Spatial and temporal data?
    • Controlled Experimental data?
    • complete or incomplete data?
    • missing data and missing mechanism?
    • appropriateness for descriptive statistics or inferential statistics
    • specific information that is important to the audience/clients?
    • What information do statisticians believe is important to audience/clients?
    • unstructured data?
    • data set with large size?
    • process visualization?
  • Potential Inferential Procedures - Broadly Regression and Prediction
    • density estimation?
    • contingency table analysis?
    • association analysis?
    • predictive analysis?
  • Considerations of Visual Design - general
    • Understanding user/client needs?
    • Implementing principles of visualization?
    • Choosing colors and topography?
    • Using animation and motion?
    • Creating interactive visualizations?
    • Testing and iterating?
  • Using Appropriate Visualizing Tools and Visual Representation
    • Point-and-click tools?
    • Programmatic tools?
    • static graphics?
    • Interactive graphics?
    • Real-time visualization?

4 Philadelphia Crime Data (2015-2024)

The crime data set is neither a population nor a random sample from a population. It is only a set of public records of crime in the neighborhoods of Philadelphia for the past 10 years.

  • The data set has some special features
    • The crimes are recorded chronically
    • Some geographical information such as zip code, block, census trait, etc.
    • crime types
    • Victims’ demographic information
  • The audience might be interested in
    • Trend of crime counts over the year
    • age distributions by crime types
    • Safety levels of neighborhoods in Philly
    • whether the crime occurrences can be predicted
  • Data integration procedure
    • Information aggregation - counting crimes by regions such as blocks, neighborhoods, zip codes, etc.
    • Merge shapefiles with crime data if possible.
    • Crime trend by year
  • Visual Design
    • line plot for trend
    • bubble plot on the map
    • Joint distribution with contingency table: crime and race
    • density comparison
    • animated plot with crime occurrence by year

The following code reflects the above thoughts.

## Define a color palette
pal <- colorFactor(c("red", "gold"), domain = c("Fatal", "Nonfatal"))

## Define objects with geo-coordinate system to plot specific information
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)
aniloc = st_as_sf(data.frame(x = -75.3277, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
ageDistloc01 = st_as_sf(data.frame(x = -75.3077, y = 39.9168),
                coords = c("x", "y"),
                crs = 4326)
#### Caution: plot_ly DID NOT show in the popup of leaflet. 
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()
## Images to be plot on the map
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"
crimeByYear= "https://pengdsci.github.io/STA553VIZ/w08/PhillyCrimeByYear.gif"
## HTML style for the map title
tag.map.title <- tags$style(HTML("
               .leaflet-control.map-title {
                   transform: translate(50%,50%);
                   position: fixed !important;
                   left: 5%;
                   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://pengdsci.github.io/STA553VIZ/w08/leafTitle.png" width="200" height="45">')
 ) 

######################################
######################################
## 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)
##
tble = as.matrix(table(phillyCrime$race, phillyCrime$fatal))
dat.tbl = data.frame(race=rownames(tble), fatal = as.vector(tble[, 1]), non.fatal = as.vector(tble[, 2]))
popups = paste(str_pad("Race", 20, "right"),  str_pad("Fatal", 20, "right"), str_pad("Nonfatal", 20, "right"), 
"<br> Asian"  ,strrep(' ', 10),     25,strrep(' ', 6), strrep(' ', 20),  "86",
"<br> Black (Non-Hispanic)", strrep(' ', 4),  "2628",strrep(' ', 5),   "9924",
"<br> Hispanic (Black or White)", strrep(' ', 2),   "393",strrep(' ', 5),  "1482",
"<br> Other/Unknown", strrep(' ', 9), "2",strrep(' ', 5),     "128",
"<br> White (Non-Hispanic)", strrep(' ', 5),  "169", strrep(' ', 5), "683") 
#poptble <- tble %>% htmlTable
##################################################
## Crime distribution by zip code - bubble plot
pal0 <- colorNumeric(palette = viridis(256, option = "B"), domain = range(ZipCrime$total.crime))
zipfig <- leaflet() %>%
  setView(lng=-75.1527, lat=39.9707, zoom = 11) %>%
  addProviderTiles(providers$Esri.WorldGrayCanvas) %>% 
  ## plot the neighborhood boundary of Philly
  addPolygons(data = phillyNeighbor,
              color = 'skyblue',
              weight = 1)  %>%
  ##
  addCircleMarkers(data = ZipCrime,
                   radius = ~((total.crime)^(1/3)),
                   color = ~pal0(total.crime),
                   stroke = FALSE, 
                   fillOpacity = 0.5,
                   popup = ~paste('Zip Code:', zip, 
                      '<br>Total Crime:', total.crime, 
                      '<br>Fatal Crime:', fatal,
                      '<br>Nonfatal Crime:', nonfatal)) 
##################################################
## popup interactive graphs with plotly
fl = tempfile(fileext = ".html")
saveWidget(zipfig, file = fl)
###
leaflet() %>%
  setView(lng=-75.15092, lat=40.00995, zoom = 11) %>%
  #addProviderTiles(providers$CartoDB.DarkMatter) %>% 
  ##
  addProviderTiles(providers$CartoDB.DarkMatter, group="Dark") %>%
  addProviderTiles(providers$CartoDB.DarkMatterNoLabels, group="DarkLabel") %>%  
  addProviderTiles(providers$Esri.NatGeoWorldMap, group="Esri") %>%
  #addTiles(providers$CartoDB.PositronNoLabels) %>%
  addControl(rr, position = "topleft", className="map-title") %>%
  ## mini reference map
  addMiniMap() %>%
  ## neighborhood boundary
  addPolygons(data = phillyNeighbor,
              color = 'skyblue',
              weight = 1)  %>%
  ## plot information on the map
  addCircleMarkers(data = phillyCrime,
                   radius = ~ifelse(fatal == "Fatal", 5, 3),
                   color = ~pal(fatal),
                   stroke = FALSE, 
                   fillOpacity = 0.5,
                   popup = ~popupTable(phillyCrime),
                   clusterOptions = markerClusterOptions(maxClusterRadius = 40)) %>%
  # Adding the image of city hall
  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")  %>%
  # Trend of crimes over the years
  addCircleMarkers(data = media, 
                   color = "red",
                   weight = 2,
                   label = "Trend",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "media") %>%
  addPopupImages(trend, 
                  width = 500,
                  height = 350,
                  tooltip = FALSE,
                  group = "media") %>%
 # age distribution within the two types of crimes
 addCircleMarkers(data = ageDistloc, 
                   color = "skyblue",
                   weight = 2,
                   label = "Age Distribution",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "ageDistloc") %>%
  addPopupImages(ageDist, 
                  width = 500,
                  height = 320,
                  tooltip = FALSE,
                  group = "ageDistloc") %>%
  # Crime counts by zip code: bubble plot
  addCircleMarkers(data = ziploc, 
                   color = "white",
                   weight = 2,
                   label = "ZIP Location",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "ziploc") %>%
  leafpop:::addPopupIframes(
                  source = fl,
                   width = 500,
                  height = 400,
                   group = "ziploc" ) %>%
  # Animated (cumulative) crime counts by years
  addCircleMarkers(data = aniloc, 
                   color = "gold",
                   weight = 2,
                   label = "Crimes Accumulation by year",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   group = "aniloc") %>%
  addPopupImages(crimeByYear, 
                  width = 500,
                  height = 400,
                  tooltip = FALSE,
                  group = "aniloc") %>% 
  # Crime counts by zip code: bubble plot
  addCircleMarkers(data = ageDistloc01, 
                   color = "purple",
                   weight = 2,
                   label = "Contingency Table: Race vs Crime Type",
                   stroke = FALSE, 
                   fillOpacity = 0.95,
                   popup = ~popups) %>%
  addLayersControl(baseGroups = c('Dark', 'DarkLabel', 'Esri'),
                   overlayGroups = c("Crime Data"),
                   options = layersControlOptions(collapsed = TRUE)) %>%
  ##
  browsable()

The animated plot (gif) is based on the following code

which_state <- "pennsylvania"
county_info <- map_data("county", region=which_state)
philly_info <- county_info[county_info$subregion == "philadelphia",]
phillyCrime$YEAR = as.integer(as.numeric(phillyCrime$year))
pal <- colorFactor(c("red", "gold"), domain = c("Fatal", "Nonfatal"))
##
base_map <- ggplot(data = philly_info, mapping = aes(x = long, y = lat, group = group)) +
 geom_polygon(color = "red", fill = "white") +
  coord_quickmap() +
  theme_void() 

map_with_data <- base_map +
  geom_point(data = phillyCrime, aes(x = lng, y = lat, group=YEAR))

map_with_data <- base_map +
  geom_point(data = phillyCrime, aes(x = lng, y = lat, color = fatal, fill = fatal, group=year), size = 2, alpha = 0.5)  +
  scale_shape_manual(values = c(21, 24)) +
  scale_color_manual(values = c("#FC4E07", "#E7B800") )

map_with_animation <- map_with_data +
  transition_time(YEAR) +
  ggtitle('Year: {frame_time}') +
   shadow_mark()

anim_save("PhillyCrimeByYear.gif", map_with_animation)
#animate(map_with_animation)

5 Practice Exercise

This assignment uses a data set that contains the records of shootings that occurred in Philadelphia in the past few years. The data set is available at OpenDataPhilly. The URL of the specific data set is at: https://opendataphilly.org/datasets/shooting-victims/. The Philly shooting data set is given in several formats. We use the geojson file. I also upload this geojson file to my GitHub repository so that you can read the file directly using the R function st_read (see the following code). There are two shapefiles of the Philly neighborhood and blocks were also used in the case study. If you want to use these two shapefiles in your visual design, please feel free to use one or both of them.

phillyNeighborShooting  <- na.omit(st_read("https://pengdsci.github.io/STA553VIZ/w08/PhillyShootings.geojson"))

The objective is to use this data set to create an interactive map that contains information about both individualized observation and aggregated formation.

LS0tDQp0aXRsZTogIlZpc3VhbGl6aW5nIEdlb3NwYXRpYWwgSW5mb3JtYXRpb24gLSBBIENhc2UgU3R1ZHkiDQphdXRob3I6ICJDaGVuZyBQZW5nIg0KZGF0ZTogIldlc3QgQ2hlc3RlciBVbml2ZXJzaXR5ICINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDogDQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZGVwdGg6IDQNCiAgICB0b2NfZmxvYXQ6IHllcw0KICAgIGNvZGVfZm9sZGluZzogaGlkZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHllcw0KICAgIHNtb290aF9zY3JvbGw6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IHJlYWRhYmxlDQotLS0NCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmRpdiNUT0MgbGkgew0KICAgIGxpc3Qtc3R5bGU6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLWltYWdlOm5vbmU7DQogICAgYmFja2dyb3VuZC1yZXBlYXQ6bm9uZTsNCiAgICBiYWNrZ3JvdW5kLXBvc2l0aW9uOjA7DQp9DQpoMS50aXRsZSB7DQogIGZvbnQtc2l6ZTogMjRweDsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxOHB4Ow0KICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgY29sb3I6IERhcmtSZWQ7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmRhdGUgeyAvKiBIZWFkZXIgNCAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICBmb250LXNpemU6IDE4cHg7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmgxIHsgLyogSGVhZGVyIDMgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDIycHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KaDIgeyAvKiBIZWFkZXIgMyAtIGFuZCB0aGUgYXV0aG9yIGFuZCBkYXRhIGhlYWRlcnMgdXNlIHRoaXMgdG9vICAqLw0KICAgIGZvbnQtc2l6ZTogMThweDsNCiAgICBmb250LWZhbWlseTogIlRpbWVzIE5ldyBSb21hbiIsIFRpbWVzLCBzZXJpZjsNCiAgICBjb2xvcjogbmF2eTsNCiAgICB0ZXh0LWFsaWduOiBsZWZ0Ow0KfQ0KDQpoMyB7IC8qIEhlYWRlciAzIC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogICAgZm9udC1zaXplOiAxNXB4Ow0KICAgIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICAgIGNvbG9yOiBuYXZ5Ow0KICAgIHRleHQtYWxpZ246IGxlZnQ7DQp9DQoNCmg0IHsgLyogSGVhZGVyIDQgLSBhbmQgdGhlIGF1dGhvciBhbmQgZGF0YSBoZWFkZXJzIHVzZSB0aGlzIHRvbyAgKi8NCiAgICBmb250LXNpemU6IDE4cHg7DQogICAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogICAgY29sb3I6IGRhcmtyZWQ7DQogICAgdGV4dC1hbGlnbjogbGVmdDsNCn0NCjwvc3R5bGU+DQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0KIyBjb2RlIGNodW5rIHNwZWNpZmllcyB3aGV0aGVyIHRoZSBSIGNvZGUsIHdhcm5pbmdzLCBhbmQgb3V0cHV0IA0KIyB3aWxsIGJlIGluY2x1ZGVkIGluIHRoZSBvdXRwdXQgZmlsZXMuDQppZiAoIXJlcXVpcmUoInRpZHl2ZXJzZSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQ0KICAgbGlicmFyeSh0aWR5dmVyc2UpDQp9DQppZiAoIXJlcXVpcmUoImtuaXRyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImtuaXRyIikNCiAgIGxpYnJhcnkoa25pdHIpDQp9DQppZiAoIXJlcXVpcmUoInNmIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInNmIikNCiAgIGxpYnJhcnkoc2YpDQp9DQppZiAoIXJlcXVpcmUoInRlcnJhIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoInRlcnJhIikNCiAgIGxpYnJhcnkodGVycmEpDQp9DQppZiAoIXJlcXVpcmUoInBsb3RseSIpKSB7DQogICBpbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KICAgbGlicmFyeShwbG90bHkpDQp9DQppZiAoIXJlcXVpcmUoImRwbHlyIikpIHsNCiAgIGluc3RhbGwucGFja2FnZXMoImRwbHlyIikNCiAgIGxpYnJhcnkoZHBseXIpDQp9DQppZiAoIXJlcXVpcmUoInBuZyIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygicG5nIikgICAgICAgICAgICAgDQogICAgbGlicmFyeSgicG5nIikNCn0NCmlmICghcmVxdWlyZSgic3BEYXRhIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJzcERhdGEiKSAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJzcERhdGEiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJjb2xvdXJwaWNrZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImNvbG91cnBpY2tlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJjb2xvdXJwaWNrZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnaWZza2kiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdpZnNraSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnaWZza2kiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtYWdpY2siKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJtYWdpY2siKQ0KfQ0KaWYgKCFyZXF1aXJlKCJzcERhdGFMYXJnZSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic3BEYXRhTGFyZ2UiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgic3BEYXRhTGFyZ2UiKQ0KfQ0KIyMjIGdncGxvdCBhbmQgZXh0ZW5zaW9ucw0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoImdncGxvdDIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJnZ2FuaW1hdGUiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImdnYW5pbWF0ZSIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJnZ2FuaW1hdGUiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0bWFwIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJ0bWFwIikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoInRtYXAiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJzZiIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic2YiKSAgICAgICAgICAgICAgDQogICAgbGlicmFyeSgic2YiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJ0aWdyaXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInRpZ3JpcyIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJ0aWdyaXMiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJtYXB2aWV3IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJtYXB2aWV3IikgICAgICAgICAgICAgIA0KICAgIGxpYnJhcnkoIm1hcHZpZXciKQ0KfQ0KaWYgKCFyZXF1aXJlKCJwYW5kZXIiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoInBhbmRlciIpICAgICAgICAgICAgICANCiAgICBsaWJyYXJ5KCJwYW5kZXIiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJsYXR0aWNlIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJsYXR0aWNlIikNCmxpYnJhcnkoImxhdHRpY2UiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJzcCIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic3AiKQ0KbGlicmFyeSgic3AiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJsZWFmbGV0IikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJsZWFmbGV0IikNCmxpYnJhcnkoImxlYWZsZXQiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJsZWFmcG9wIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJsZWFmcG9wIikNCmxpYnJhcnkoImxlYWZwb3AiKQ0KfQ0KaWYgKCFyZXF1aXJlKCJsZWFmZW0iKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxlYWZlbSIpDQpsaWJyYXJ5KCJsZWFmZW0iKQ0KfQ0KaWYgKCFyZXF1aXJlKCJzcERhdGFMYXJnZSIpKSB7DQogICAgaW5zdGFsbC5wYWNrYWdlcygic3BEYXRhTGFyZ2UiLCByZXBvcyA9ICJodHRwczovL2dlb2NvbXByLnItdW5pdmVyc2UuZGV2IikNCmxpYnJhcnkoInNwRGF0YUxhcmdlIikNCn0NCmlmICghcmVxdWlyZSgiaHRtbHdpZGdldHMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImh0bWx3aWRnZXRzIikNCmxpYnJhcnkoImh0bWx3aWRnZXRzIikNCn0NCmlmICghcmVxdWlyZSgibGVhZmxldC5leHRyYXMiKSkgew0KICAgIGluc3RhbGwucGFja2FnZXMoImxlYWZsZXQuZXh0cmFzIikNCmxpYnJhcnkoImxlYWZsZXQuZXh0cmFzIikNCn0NCmlmICghcmVxdWlyZSgiaHRtbHRvb2xzIikpIHsNCiAgICBpbnN0YWxsLnBhY2thZ2VzKCJodG1sdG9vbHMiKQ0KbGlicmFyeSgiaHRtbHRvb2xzIikNCn0NCmlmKCFyZXF1aXJlKCJwbmciKSl7DQogIGluc3RhbGwucGFja2FnZXMoInBuZyIpDQogIGxpYnJhcnkocG5nKQ0KfQ0KaWYoIXJlcXVpcmUoInZpcmlkaXMiKSl7DQogIGluc3RhbGwucGFja2FnZXMoInZpcmlkaXMiKQ0KICBsaWJyYXJ5KHZpcmlkaXMpDQp9DQppZighcmVxdWlyZSgiZ2dtYXAiKSl7DQogIGluc3RhbGwucGFja2FnZXMoImdnbWFwIikNCiAgbGlicmFyeShnZ21hcCkNCn0NCmlmKCFyZXF1aXJlKCJ3ZWJzaG90Iikpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJ3ZWJzaG90IikNCiAgbGlicmFyeSh3ZWJzaG90KQ0KfQ0KaWYoIXJlcXVpcmUoImh0bWx3aWRnZXRzIikpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJodG1sd2lkZ2V0cyIpDQogIGxpYnJhcnkoaHRtbHdpZGdldHMpDQp9DQppZighcmVxdWlyZSgiYW5pbWF0aW9uIikpew0KICBpbnN0YWxsLnBhY2thZ2VzKCJhbmltYXRpb24iKQ0KICBsaWJyYXJ5KGFuaW1hdGlvbikNCn0NCmlmKCFyZXF1aXJlKCJnaWZza2kiKSl7DQogIGluc3RhbGwucGFja2FnZXMoImdpZnNraSIpDQogIGxpYnJhcnkoZ2lmc2tpKQ0KfQ0KaWYoIXJlcXVpcmUoImh0bWxUYWJsZSIpKXsNCiAgaW5zdGFsbC5wYWNrYWdlcygiaHRtbFRhYmxlIikNCiAgbGlicmFyeShodG1sVGFibGUpDQp9DQppZighcmVxdWlyZSgibWFncml0dHIiKSl7DQogIGluc3RhbGwucGFja2FnZXMoIm1hZ3JpdHRyIikNCiAgbGlicmFyeShtYWdyaXR0cikNCn0NCiMjIyBsaWJyYXJ5KG1hZ3JpdHRyKQ0KIyMjDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsICAgICAgIA0KICAgICAgICAgICAgICAgICAgICAgIHdhcm5pbmcgPSBGQUxTRSwgICANCiAgICAgICAgICAgICAgICAgICAgICByZXN1bHRzID0gVFJVRSwgICANCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgY29tbWVudCA9IE5BKQ0KYGBgDQoNClwNCg0KIyBJbnRyb2R1Y3Rpb24NCg0KSW4gdGhpcyBub3RlLCB3ZSB1c2UgYSByZWFsIGRhdGEgc2V0IGNvbnRhaW5pbmcgY3JpbWUgY2FzZXMgc2luY2UgMjAxNSBpbiBQaGlsYWRlbHBoaWEgbmVpZ2hib3Job29kcyBhcyBhbiBleGFtcGxlIHRvIGlsbHVzdHJhdGUgdmFyaW91cyB2aXN1YWwgaW5zcGVjdGlvbnMgb2YgdGhlIGdpdmVuIGRhdGEgaW5jbHVkaW5nIGJhc2ljIEVEQSBhbmQgaW5mZXJlbmNlIHN0YXRpc3RpY3MgaWYgdGhlIGRhdGEgc2V0IGhhcyByZWxldmFudCBpbmZvcm1hdGlvbiBmb3IgaW5mZXJlbmNlLiBZb3UgY2FuIGZpbmQgb3RoZXIgcmVsYXRlZCBkYXRhIHNldHMgd2l0aCBpbmZvcm1hdGlvbiBmcm9tIFtPcGVuRGF0YVBoaWxseV0oaHR0cHM6Ly9vcGVuZGF0YXBoaWxseS5vcmcvZGF0YXNldHMvKS4NCg0KDQojIE1hcCBTaGFwZWZpbGVzDQoNCldlIGhhdmUgaW50cm9kdWNlZCB0d28gYmFzaWMgdHlwZXMgb2YgbWFwczogY2hvcm9wbGV0aCBhbmQgcmVmZXJlbmNlIG1hcHMuIFdoZW4gYW5hbHl6aW5nIGRhdGEgaW52b2x2aW5nIGdlby1pbmZvcm1hdGlvbiBvZiBzcGVjaWZpYyBnZW9ncmFwaGljYWwgcmVnaW9ucyBhdCBjZXJ0YWluIGxldmVscyBzdWNoIGFzIGJsb2NrcywgbmVpZ2hib3Job29kcywgZGlzdHJpY3RzLCB6aXAgY29kZXMsIGNvdW50aWVzLCBzdGF0ZXMsIGV0Yywgd2UgbmVlZCB0byB1c2UgYSByZWxhdGVkIGJvdW5kYXJ5IHNoYXBlZmlsZSB0byBkcmF3IHRoZSByZWxhdGVkIHJlZ2lvbnMuIFdlIHVzZSBmaWxsZWQgY29sb3JzIHRvIGRlbm90ZSB0aGUgdmFsdWVzIG9mIGEga2V5IHZhcmlhYmxlIG9mIGludGVyZXN0LiBJbiBnZW5lcmFsLCBtYWtpbmcgYSBjaG9yb3BsZXRoIG1hcCByZXF1aXJlcyBhIHByb2Nlc3Mgb2YgZGF0YSBpbnRlZ3JhdGlvbiBpbiB3aGljaCBvdGhlciByZWxldmFudCBkYXRhIHNvdXJjZXMgd2l0aCBiZSBtZXJnZWQgd2l0aCB0aGUgc2hhcGVmaWxlLiANCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBvdXQud2lkdGg9IjkwJSJ9DQppbmNsdWRlX2dyYXBoaWNzKCJzaGFwZUZpbGVDb250ZW50LnBuZyIpDQpgYGANCg0KDQpQbG90dGluZyBhIHJlZmVyZW5jZSBtYXAgaXMgcmVsYXRpdmVseSBzdHJhaWdodGZvcndhcmQsIHRoZSBvbmx5IHJlcXVpcmVkIGdlby1pbmZvcm1hdGlvbiBpcyBsb25naXR1ZGUgYW5kIGxhdGl0dWRlLiBUaGUgcG9pbnQgc2l6ZXMgYW5kIGNvbG9ycyBjYW4gYmUgbWFwcGVkIHRvIHRoZSB2YWx1ZXMgb2YgdmFyaWFibGVzIG90aGVyIHRoYW4gbG9uZ2l0dWRlIGFuZCBsYXRpdHVkZS4gDQoNCg0KDQojIyBEYXRhIEludGVncmF0aW9uDQoNClRoZSB2aXN1YWwgYW5hbHlzaXMgaXMgYmFzZWQgb24gdGhlIGZvbGxvd2luZyB0aHJlZSBkYXRhIHNldHMNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgcGFnZWQucHJpbnQ9RkFMU0UsIHJlc3VsdHM9RkFMU0V9DQojIyAgY292aWRfdmFjY2luZXNfYnlfY2Vuc3VzX3RyYWN0Lmdlb2pzb24NCnBoaWxseU5laWdoYm9yICA8LSBzdF9yZWFkKCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L05laWdoYm9yaG9vZHNfUGhpbGFkZWxwaGlhLmdlb2pzb24iKQ0KcGhpbGx5ICA8LSBzdF9yZWFkKCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L1BoaWxseU5laWdoYm9yaG9vZC1ibG9ja3MuZ2VvanNvbiIpICMgYmxvY2sgbGV2ZWwgZGF0YQ0KcGhpbGx5Q3JpbWUgPC0gcmVhZC5jc3YoImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvUGhpbGx5Q3JpbWVTaW5jZTIwMTUuY3N2IikNCiMjIw0KcGhpbGx5Q3JpbWUkbmFtZSA9IHRvdXBwZXIoZ3N1YigiLSIsICJfIiwgcGhpbGx5Q3JpbWUkbmVpZ2hib3Job29kKSkgDQojIGV4dHJhY3RpbmcgeWVhciBmcm9tIHZhcmlhYmxlICdkYXRlJw0Kc2xhc2gubG9jID0gdW5saXN0KGdyZWdleHByKCcvJywgcGhpbGx5Q3JpbWUkZGF0ZSkpDQpzbGFzaDIubG9jID0gc2xhc2gubG9jWzIqKDE6ZGltKHBoaWxseUNyaW1lKVsxXSldDQpwaGlsbHlDcmltZSR5ZWFyID0gc3Vic3RyKHBoaWxseUNyaW1lJGRhdGUsIHNsYXNoMi5sb2MrMSwgc2xhc2gyLmxvYys0KQ0KYGBgDQoNClNpbmNlIHRoZSBjcmltZSBkYXRhIHNldCBoYXMgYSBuZWlnaGJvcmhvb2QgbmFtZSB3aGljaCB3aWxsIGJlIHVzZWQgYXMgYSBrZXkgdG8gam9pbiB0aGUgc2hhcGVmaWxlIG9mIHRoZSBuZWlnaGJvcmhvb2QuIFRoZXJlIGlzIG5vIHZhcmlhYmxlIGluIHRoZSBjcmltZSBkYXRhIHRoYXQgY2FuIGJlIHVzZWQgYXMgYSBrZXkgdG8gbWVyZ2Ugd2l0aCB0aGUgY2Vuc3VzIHRyYWN0IHNoYXBlZmlsZS4gV2UgYWdncmVnYXRlIHJlbGV2YW50IGluZm9ybWF0aW9uIGF0IHRoZSBuZWlnaGJvcmhvb2QgbGV2ZWwgdG8gbWFrZSBhIGNob3JvcGxldGggbWFwIGFuZCBhIHNjYXR0ZXItcGxvdCBtYXAgdG8gZGlzcGxheSBpbmZvcm1hdGlvbiBvbiBpbmRpdmlkdWFsIGNyaW1lIGNhc2VzLg0KDQpgYGB7cn0NClN1YkNyaW1lID0gcGhpbGx5Q3JpbWVbLGMoImRjX2tleSIsImZhdGFsIiwibmFtZSIsInllYXIiKV0NCmFnZ3JlZ2F0ZUNyaW1lID0gYWdncmVnYXRlKFN1YkNyaW1lJGZhdGFsLCBieT1saXN0KFN1YkNyaW1lJG5hbWUsU3ViQ3JpbWUkeWVhciksIEZVTj1sZW5ndGgpDQojIyBMb25naXR1ZGUgYW5kIGxhdGl0dWRlIG9mIHppcCBjb2RlIGNlbnRlcg0KWmlwTG9uID0gYWdncmVnYXRlKHBoaWxseUNyaW1lJGxuZywgYnk9bGlzdChwaGlsbHlDcmltZSR6aXBfY29kZSksIEZVTj1tZWFuKQ0KWmlwTGF0ID0gYWdncmVnYXRlKHBoaWxseUNyaW1lJGxhdCwgYnk9bGlzdChwaGlsbHlDcmltZSR6aXBfY29kZSksIEZVTj1tZWFuKQ0KWmlwTG9uTGF0ID0gbWVyZ2UoWmlwTG9uLCBaaXBMYXQsIGJ5ID0gIkdyb3VwLjEiKQ0KbmFtZXMoWmlwTG9uTGF0KSA9IGMoInppcCIsICJsbmciLCAibGF0IikNCiMjICBaaXAgY29kZSBjcmltZQ0KWmlwQ3JpbWVUYWJsZSA9IHRhYmxlKHBoaWxseUNyaW1lJGZhdGFsLCAgcGhpbGx5Q3JpbWUkemlwX2NvZGUpDQpaaXBDcmltZSA9IGRhdGEuZnJhbWUoemlwPWFzLm51bWVyaWMobmFtZXModGFibGUocGhpbGx5Q3JpbWUkemlwX2NvZGUpKSksIA0KICAgICAgICAgICAgICAgICAgICAgIGZhdGFsID0gdGFibGUocGhpbGx5Q3JpbWUkZmF0YWwsIHBoaWxseUNyaW1lJHppcF9jb2RlKVsxLF0sDQogICAgICAgICAgICAgICAgICAgICAgbm9uZmF0YWwgPSB0YWJsZShwaGlsbHlDcmltZSRmYXRhbCwgcGhpbGx5Q3JpbWUkemlwX2NvZGUpWzIsXSwNCiAgICAgICAgICAgICAgICAgICAgICB0b3RhbC5jcmltZSA9IHRhYmxlKHBoaWxseUNyaW1lJHppcF9jb2RlKSANCiAgICAgICAgICAgICAgICAgICAgICApDQpaaXBDcmltZSA9IFppcENyaW1lWywgYygiemlwIiwgImZhdGFsIiwgIm5vbmZhdGFsIiwgInRvdGFsLmNyaW1lLkZyZXEiKV0NCmNvbG5hbWVzKFppcENyaW1lKSA9IGMoInppcCIsICJmYXRhbCIsICJub25mYXRhbCIsICJ0b3RhbC5jcmltZSIpDQpaaXBDcmltZSA9IG1lcmdlKFppcExvbkxhdCwgWmlwQ3JpbWUsIGJ5ID0gInppcCIpDQpgYGANCg0KDQojIFZpc3VhbCBEZXNpZ24gUHJvY2Vzcw0KDQpUaGUgcHJvY2VzcyBvZiB2aXN1YWxpemF0aW9uIGludm9sdmVzIHNldmVyYWwgc3RlcHMuIEluIG9yZGVyIHRvIGNyZWF0ZSBhbiBlZmZlY3RpdmUgdmlzdWFsaXphdGlvbiwgd2UgbmVlZCB0byBrbm93IHRoZSBkYXRhLCB0aGUgYXVkaWVuY2UgbmVlZHMsIHRoZSBwcmluY2lwbGVzIG9mIHZpc3VhbGl6YXRpb24sIHRoZSByZWxldmFudCBpbmZvcm1hdGlvbiB0byBiZSB2aXN1YWxpemVkLCB0aGUgYXBwcm9wcmlhdGUgdG9vbHMgZm9yIGNyZWF0aW5nIHZpc3VhbGl6YXRpb24sIGFuZCAgIA0KDQoqIFRoZSBpbml0aWFsIHN0ZXAgaXMgdG8ga25vdyB0aGUgZGF0YSANCiAgKyBwb3B1bGF0aW9uIG9yIHNhbXBsZT8gDQogICsgcmFuZG9tIHNhbXBsZSBvciBub24tcmFuZG9tIHNhbXBsZT8gDQogICsgaW5mb3JtYXRpb24gYXZhaWxhYmxlIGluIHRoZSBkYXRhPyANCiAgKyBjcm9zcy1zZWN0aW9uYWwgb3IgbG9uZ2l0dWRpbmFsIGRhdGE/DQogICsgU3BhdGlhbCBhbmQgdGVtcG9yYWwgZGF0YT8gDQogICsgQ29udHJvbGxlZCBFeHBlcmltZW50YWwgZGF0YT8NCiAgKyBjb21wbGV0ZSBvciBpbmNvbXBsZXRlIGRhdGE/DQogICsgbWlzc2luZyBkYXRhIGFuZCBtaXNzaW5nIG1lY2hhbmlzbT8NCiAgKyBhcHByb3ByaWF0ZW5lc3MgZm9yIGRlc2NyaXB0aXZlIHN0YXRpc3RpY3Mgb3IgaW5mZXJlbnRpYWwgc3RhdGlzdGljcw0KICArIHNwZWNpZmljIGluZm9ybWF0aW9uIHRoYXQgaXMgaW1wb3J0YW50IHRvIHRoZSBhdWRpZW5jZS9jbGllbnRzPw0KICArIFdoYXQgaW5mb3JtYXRpb24gZG8gc3RhdGlzdGljaWFucyBiZWxpZXZlIGlzIGltcG9ydGFudCB0byBhdWRpZW5jZS9jbGllbnRzPw0KICArIHVuc3RydWN0dXJlZCBkYXRhPw0KICArIGRhdGEgc2V0IHdpdGggbGFyZ2Ugc2l6ZT8NCiAgKyBwcm9jZXNzIHZpc3VhbGl6YXRpb24/DQogIA0KKiBQb3RlbnRpYWwgSW5mZXJlbnRpYWwgUHJvY2VkdXJlcyAtIEJyb2FkbHkgUmVncmVzc2lvbiBhbmQgUHJlZGljdGlvbg0KICArIGRlbnNpdHkgZXN0aW1hdGlvbj8NCiAgKyBjb250aW5nZW5jeSB0YWJsZSBhbmFseXNpcz8NCiAgKyBhc3NvY2lhdGlvbiBhbmFseXNpcz8NCiAgKyBwcmVkaWN0aXZlIGFuYWx5c2lzPw0KICANCiogQ29uc2lkZXJhdGlvbnMgb2YgVmlzdWFsIERlc2lnbiAtIGdlbmVyYWwgDQogICsgVW5kZXJzdGFuZGluZyB1c2VyL2NsaWVudCBuZWVkcz8NCiAgKyBJbXBsZW1lbnRpbmcgcHJpbmNpcGxlcyBvZiB2aXN1YWxpemF0aW9uPw0KICArIENob29zaW5nIGNvbG9ycyBhbmQgdG9wb2dyYXBoeT8NCiAgKyBVc2luZyBhbmltYXRpb24gYW5kIG1vdGlvbj8NCiAgKyBDcmVhdGluZyBpbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucz8NCiAgKyBUZXN0aW5nIGFuZCBpdGVyYXRpbmc/DQogIA0KKiBVc2luZyBBcHByb3ByaWF0ZSBWaXN1YWxpemluZyBUb29scyBhbmQgVmlzdWFsIFJlcHJlc2VudGF0aW9uDQogICsgUG9pbnQtYW5kLWNsaWNrIHRvb2xzPw0KICArIFByb2dyYW1tYXRpYyB0b29scz8NCiAgKyBzdGF0aWMgZ3JhcGhpY3M/DQogICsgSW50ZXJhY3RpdmUgZ3JhcGhpY3M/DQogICsgUmVhbC10aW1lIHZpc3VhbGl6YXRpb24/DQogIA0KDQoNCiMgUGhpbGFkZWxwaGlhIENyaW1lIERhdGEgKDIwMTUtMjAyNCkNCg0KVGhlIGNyaW1lIGRhdGEgc2V0IGlzIG5laXRoZXIgYSBwb3B1bGF0aW9uIG5vciBhIHJhbmRvbSBzYW1wbGUgZnJvbSBhIHBvcHVsYXRpb24uIEl0IGlzIG9ubHkgYSBzZXQgb2YgcHVibGljIHJlY29yZHMgb2YgY3JpbWUgaW4gdGhlIG5laWdoYm9yaG9vZHMgb2YgUGhpbGFkZWxwaGlhIGZvciB0aGUgcGFzdCAxMCB5ZWFycy4gDQoNCiogVGhlIGRhdGEgc2V0IGhhcyBzb21lIHNwZWNpYWwgZmVhdHVyZXMNCiAgKyBUaGUgY3JpbWVzIGFyZSByZWNvcmRlZCBjaHJvbmljYWxseQ0KICArIFNvbWUgZ2VvZ3JhcGhpY2FsIGluZm9ybWF0aW9uIHN1Y2ggYXMgemlwIGNvZGUsIGJsb2NrLCBjZW5zdXMgdHJhaXQsIGV0Yy4NCiAgKyBjcmltZSB0eXBlcw0KICArIFZpY3RpbXMnIGRlbW9ncmFwaGljIGluZm9ybWF0aW9uDQogIA0KKiBUaGUgYXVkaWVuY2UgbWlnaHQgYmUgaW50ZXJlc3RlZCBpbg0KICArIFRyZW5kIG9mIGNyaW1lIGNvdW50cyBvdmVyIHRoZSB5ZWFyDQogICsgYWdlIGRpc3RyaWJ1dGlvbnMgYnkgY3JpbWUgdHlwZXMNCiAgKyBTYWZldHkgbGV2ZWxzIG9mIG5laWdoYm9yaG9vZHMgaW4gUGhpbGx5DQogICsgd2hldGhlciB0aGUgY3JpbWUgb2NjdXJyZW5jZXMgY2FuIGJlIHByZWRpY3RlZA0KICANCiogRGF0YSBpbnRlZ3JhdGlvbiBwcm9jZWR1cmUNCiAgKyBJbmZvcm1hdGlvbiBhZ2dyZWdhdGlvbiAtIGNvdW50aW5nIGNyaW1lcyBieSByZWdpb25zIHN1Y2ggYXMgYmxvY2tzLCBuZWlnaGJvcmhvb2RzLCB6aXAgY29kZXMsIGV0Yy4NCiAgKyBNZXJnZSBzaGFwZWZpbGVzIHdpdGggY3JpbWUgZGF0YSBpZiBwb3NzaWJsZS4NCiAgKyBDcmltZSB0cmVuZCBieSB5ZWFyIA0KICANCiogVmlzdWFsIERlc2lnbg0KICArIGxpbmUgcGxvdCBmb3IgdHJlbmQNCiAgKyBidWJibGUgcGxvdCBvbiB0aGUgbWFwDQogICsgSm9pbnQgZGlzdHJpYnV0aW9uIHdpdGggY29udGluZ2VuY3kgdGFibGU6IGNyaW1lIGFuZCByYWNlDQogICsgZGVuc2l0eSBjb21wYXJpc29uDQogICsgYW5pbWF0ZWQgcGxvdCB3aXRoIGNyaW1lIG9jY3VycmVuY2UgYnkgeWVhcg0KDQoNClRoZSBmb2xsb3dpbmcgY29kZSByZWZsZWN0cyB0aGUgYWJvdmUgdGhvdWdodHMuDQoNCg0KYGBge3IgZmlnLmFsaWduPSdjZW50ZXInLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMCwgd2FybmluZz1GQUxTRSwgbWVzc2FnZSA9IEZBTFNFfQ0KIyMgRGVmaW5lIGEgY29sb3IgcGFsZXR0ZQ0KcGFsIDwtIGNvbG9yRmFjdG9yKGMoInJlZCIsICJnb2xkIiksIGRvbWFpbiA9IGMoIkZhdGFsIiwgIk5vbmZhdGFsIikpDQoNCiMjIERlZmluZSBvYmplY3RzIHdpdGggZ2VvLWNvb3JkaW5hdGUgc3lzdGVtIHRvIHBsb3Qgc3BlY2lmaWMgaW5mb3JtYXRpb24NCnBudCA9IHN0X2FzX3NmKGRhdGEuZnJhbWUoeCA9IC03NS4xNjUyLCB5ID0gMzkuOTUyNiksDQogICAgICAgICAgICAgICAgY29vcmRzID0gYygieCIsICJ5IiksDQogICAgICAgICAgICAgICAgY3JzID0gNDMyNikNCm1lZGlhID0gc3RfYXNfc2YoZGF0YS5mcmFtZSh4ID0gLTc1LjM4NzcsIHkgPSAzOS45MTY4KSwNCiAgICAgICAgICAgICAgICBjb29yZHMgPSBjKCJ4IiwgInkiKSwNCiAgICAgICAgICAgICAgICBjcnMgPSA0MzI2KQ0KYWdlRGlzdGxvYyA9IHN0X2FzX3NmKGRhdGEuZnJhbWUoeCA9IC03NS4zNjc3LCB5ID0gMzkuOTE2OCksDQogICAgICAgICAgICAgICAgY29vcmRzID0gYygieCIsICJ5IiksDQogICAgICAgICAgICAgICAgY3JzID0gNDMyNikNCnppcGxvYyA9IHN0X2FzX3NmKGRhdGEuZnJhbWUoeCA9IC03NS4zNDc3LCB5ID0gMzkuOTE2OCksDQogICAgICAgICAgICAgICAgY29vcmRzID0gYygieCIsICJ5IiksDQogICAgICAgICAgICAgICAgY3JzID0gNDMyNikNCmFuaWxvYyA9IHN0X2FzX3NmKGRhdGEuZnJhbWUoeCA9IC03NS4zMjc3LCB5ID0gMzkuOTE2OCksDQogICAgICAgICAgICAgICAgY29vcmRzID0gYygieCIsICJ5IiksDQogICAgICAgICAgICAgICAgY3JzID0gNDMyNikNCmFnZURpc3Rsb2MwMSA9IHN0X2FzX3NmKGRhdGEuZnJhbWUoeCA9IC03NS4zMDc3LCB5ID0gMzkuOTE2OCksDQogICAgICAgICAgICAgICAgY29vcmRzID0gYygieCIsICJ5IiksDQogICAgICAgICAgICAgICAgY3JzID0gNDMyNikNCiMjIyMgQ2F1dGlvbjogcGxvdF9seSBESUQgTk9UIHNob3cgaW4gdGhlIHBvcHVwIG9mIGxlYWZsZXQuIA0KZmlnIDwtIHBsb3RfbHkoWmlwQ3JpbWUsIHggPSB+bG5nLCB5ID0gfmxhdCwgY29sb3IgPSB+emlwLCBzaXplID0gfnRvdGFsLmNyaW1lLCAjY29sb3JzID0gY29sb3JzLA0KICAgICAgICAgICAgICAgdHlwZSA9ICdzY2F0dGVyJywgDQogICAgICAgICAgICAgICBtb2RlID0gJ21hcmtlcnMnLCANCiAgICAgICAgICAgICAgICNzaXplcyA9IGMobWluKGxvZyhaaXBDcmltZSR0b3RhbC5jcmltZSkpLCBtYXgobG9nKFppcENyaW1lJHRvdGFsLmNyaW1lKSkpLA0KICAgICAgICAgICAgICAgbWFya2VyID0gbGlzdChzeW1ib2wgPSAnY2lyY2xlJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICBzaXplbW9kZSA9ICdkaWFtZXRlcicsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGluZSA9IGxpc3Qod2lkdGggPSAyLCBjb2xvciA9ICcjRkZGRkZGJykpLA0KICAgICAgICAgICAgICAgdGV4dCA9IH5wYXN0ZSgnWmlwIENvZGU6JywgemlwLCANCiAgICAgICAgICAgICAgICAgICAgICAnPGJyPlRvdGFsIENyaW1lOicsIHRvdGFsLmNyaW1lLCANCiAgICAgICAgICAgICAgICAgICAgICAnPGJyPkZhdGFsIENyaW1lOicsIGZhdGFsLA0KICAgICAgICAgICAgICAgICAgICAgICc8YnI+Tm9uZmF0YWwgQ3JpbWU6Jywgbm9uZmF0YWwpKSAlPiUgaGlkZV9jb2xvcmJhcigpDQojIyBJbWFnZXMgdG8gYmUgcGxvdCBvbiB0aGUgbWFwDQppbWcgPSAiaHR0cHM6Ly9wZW5nZHNjaS5naXRodWIuaW8vU1RBNTUzVklaL3cwOC9QaGlsbHlDaXR5SGFsbC5qcGciDQp0cmVuZCA9ICJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L0NyaW1lVHJlbmQuanBnIg0KYWdlRGlzdCA9ICJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L2FnZURpc3QuanBnIg0KdHJlbmRJY29uID0gImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvdHJlbmQtaWNvbi5qcGciDQpjcmltZUJ5WWVhcj0gImh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvUGhpbGx5Q3JpbWVCeVllYXIuZ2lmIg0KIyMgSFRNTCBzdHlsZSBmb3IgdGhlIG1hcCB0aXRsZQ0KdGFnLm1hcC50aXRsZSA8LSB0YWdzJHN0eWxlKEhUTUwoIg0KICAgICAgICAgICAgICAgLmxlYWZsZXQtY29udHJvbC5tYXAtdGl0bGUgew0KICAgICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlKDUwJSw1MCUpOw0KICAgICAgICAgICAgICAgICAgIHBvc2l0aW9uOiBmaXhlZCAhaW1wb3J0YW50Ow0KICAgICAgICAgICAgICAgICAgIGxlZnQ6IDUlOw0KICAgICAgICAgICAgICAgICAgIHRleHQtYWxpZ246IGNlbnRlcjsNCiAgICAgICAgICAgICAgICAgICBwYWRkaW5nLWxlZnQ6IDEwcHg7DQogICAgICAgICAgICAgICAgICAgcGFkZGluZy1yaWdodDogMTBweDsNCiAgICAgICAgICAgICAgICAgICBiYWNrZ3JvdW5kOiB0cmFuc3BhcmVudDsNCiAgICAgICAgICAgICAgICAgICBmb250LXdlaWdodDogYm9sZDsNCiAgICAgICAgICAgICAgICAgICBmb250LXNpemU6IDE4cHg7fQ0KICAgICAgICAgICAgICAgICAiKSkNCg0KcnIgPC0gdGFncyRkaXYoDQogICBIVE1MKCc8aW1nIGJvcmRlcj0iMCIgYWx0PSJJbWFnZVRpdGxlIiBzcmM9Imh0dHBzOi8vcGVuZ2RzY2kuZ2l0aHViLmlvL1NUQTU1M1ZJWi93MDgvbGVhZlRpdGxlLnBuZyIgd2lkdGg9IjIwMCIgaGVpZ2h0PSI0NSI+JykNCiApIA0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMjIERhdGEgYW5hbHlzaXMNCiMjIDEuIGFnZSBkaXN0cmlidXRpb24gYnkgYWdlDQpmYXRhbC5jb2xzIDwtIGMoIiNGNzZENUUiLCAiIzcyRDhGRiIpDQojIEJhc2ljIGRlbnNpdHkgcGxvdCBpbiBnZ3Bsb3QyDQpmYXRhbEFnZSA9IGdncGxvdChwaGlsbHlDcmltZSwgYWVzKHggPSBhZ2UsIGZpbGwgPWZhdGFsICkpICsNCiAgICAgICAgICAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC43KSAgKw0KICAgICAgICAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBmYXRhbC5jb2xzKQ0KIyMNCnRibGUgPSBhcy5tYXRyaXgodGFibGUocGhpbGx5Q3JpbWUkcmFjZSwgcGhpbGx5Q3JpbWUkZmF0YWwpKQ0KZGF0LnRibCA9IGRhdGEuZnJhbWUocmFjZT1yb3duYW1lcyh0YmxlKSwgZmF0YWwgPSBhcy52ZWN0b3IodGJsZVssIDFdKSwgbm9uLmZhdGFsID0gYXMudmVjdG9yKHRibGVbLCAyXSkpDQpwb3B1cHMgPSBwYXN0ZShzdHJfcGFkKCJSYWNlIiwgMjAsICJyaWdodCIpLCAgc3RyX3BhZCgiRmF0YWwiLCAyMCwgInJpZ2h0IiksIHN0cl9wYWQoIk5vbmZhdGFsIiwgMjAsICJyaWdodCIpLCANCiI8YnI+IEFzaWFuIiAgLHN0cnJlcCgnICcsIDEwKSwgICAgIDI1LHN0cnJlcCgnICcsIDYpLCBzdHJyZXAoJyAnLCAyMCksICAiODYiLA0KIjxicj4gQmxhY2sgKE5vbi1IaXNwYW5pYykiLCBzdHJyZXAoJyAnLCA0KSwgICIyNjI4IixzdHJyZXAoJyAnLCA1KSwgICAiOTkyNCIsDQoiPGJyPiBIaXNwYW5pYyAoQmxhY2sgb3IgV2hpdGUpIiwgc3RycmVwKCcgJywgMiksICAgIjM5MyIsc3RycmVwKCcgJywgNSksICAiMTQ4MiIsDQoiPGJyPiBPdGhlci9Vbmtub3duIiwgc3RycmVwKCcgJywgOSksICIyIixzdHJyZXAoJyAnLCA1KSwgICAgICIxMjgiLA0KIjxicj4gV2hpdGUgKE5vbi1IaXNwYW5pYykiLCBzdHJyZXAoJyAnLCA1KSwgICIxNjkiLCBzdHJyZXAoJyAnLCA1KSwgIjY4MyIpIA0KI3BvcHRibGUgPC0gdGJsZSAlPiUgaHRtbFRhYmxlDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgQ3JpbWUgZGlzdHJpYnV0aW9uIGJ5IHppcCBjb2RlIC0gYnViYmxlIHBsb3QNCnBhbDAgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSB2aXJpZGlzKDI1Niwgb3B0aW9uID0gIkIiKSwgZG9tYWluID0gcmFuZ2UoWmlwQ3JpbWUkdG90YWwuY3JpbWUpKQ0KemlwZmlnIDwtIGxlYWZsZXQoKSAlPiUNCiAgc2V0Vmlldyhsbmc9LTc1LjE1MjcsIGxhdD0zOS45NzA3LCB6b29tID0gMTEpICU+JQ0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRFc3JpLldvcmxkR3JheUNhbnZhcykgJT4lIA0KICAjIyBwbG90IHRoZSBuZWlnaGJvcmhvb2QgYm91bmRhcnkgb2YgUGhpbGx5DQogIGFkZFBvbHlnb25zKGRhdGEgPSBwaGlsbHlOZWlnaGJvciwNCiAgICAgICAgICAgICAgY29sb3IgPSAnc2t5Ymx1ZScsDQogICAgICAgICAgICAgIHdlaWdodCA9IDEpICAlPiUNCiAgIyMNCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gWmlwQ3JpbWUsDQogICAgICAgICAgICAgICAgICAgcmFkaXVzID0gfigodG90YWwuY3JpbWUpXigxLzMpKSwNCiAgICAgICAgICAgICAgICAgICBjb2xvciA9IH5wYWwwKHRvdGFsLmNyaW1lKSwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cGFzdGUoJ1ppcCBDb2RlOicsIHppcCwgDQogICAgICAgICAgICAgICAgICAgICAgJzxicj5Ub3RhbCBDcmltZTonLCB0b3RhbC5jcmltZSwgDQogICAgICAgICAgICAgICAgICAgICAgJzxicj5GYXRhbCBDcmltZTonLCBmYXRhbCwNCiAgICAgICAgICAgICAgICAgICAgICAnPGJyPk5vbmZhdGFsIENyaW1lOicsIG5vbmZhdGFsKSkgDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMgcG9wdXAgaW50ZXJhY3RpdmUgZ3JhcGhzIHdpdGggcGxvdGx5DQpmbCA9IHRlbXBmaWxlKGZpbGVleHQgPSAiLmh0bWwiKQ0Kc2F2ZVdpZGdldCh6aXBmaWcsIGZpbGUgPSBmbCkNCiMjIw0KbGVhZmxldCgpICU+JQ0KICBzZXRWaWV3KGxuZz0tNzUuMTUwOTIsIGxhdD00MC4wMDk5NSwgem9vbSA9IDExKSAlPiUNCiAgI2FkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuRGFya01hdHRlcikgJT4lIA0KICAjIw0KICBhZGRQcm92aWRlclRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLkRhcmtNYXR0ZXIsIGdyb3VwPSJEYXJrIikgJT4lDQogIGFkZFByb3ZpZGVyVGlsZXMocHJvdmlkZXJzJENhcnRvREIuRGFya01hdHRlck5vTGFiZWxzLCBncm91cD0iRGFya0xhYmVsIikgJT4lICANCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkRXNyaS5OYXRHZW9Xb3JsZE1hcCwgZ3JvdXA9IkVzcmkiKSAlPiUNCiAgI2FkZFRpbGVzKHByb3ZpZGVycyRDYXJ0b0RCLlBvc2l0cm9uTm9MYWJlbHMpICU+JQ0KICBhZGRDb250cm9sKHJyLCBwb3NpdGlvbiA9ICJ0b3BsZWZ0IiwgY2xhc3NOYW1lPSJtYXAtdGl0bGUiKSAlPiUNCiAgIyMgbWluaSByZWZlcmVuY2UgbWFwDQogIGFkZE1pbmlNYXAoKSAlPiUNCiAgIyMgbmVpZ2hib3Job29kIGJvdW5kYXJ5DQogIGFkZFBvbHlnb25zKGRhdGEgPSBwaGlsbHlOZWlnaGJvciwNCiAgICAgICAgICAgICAgY29sb3IgPSAnc2t5Ymx1ZScsDQogICAgICAgICAgICAgIHdlaWdodCA9IDEpICAlPiUNCiAgIyMgcGxvdCBpbmZvcm1hdGlvbiBvbiB0aGUgbWFwDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IHBoaWxseUNyaW1lLA0KICAgICAgICAgICAgICAgICAgIHJhZGl1cyA9IH5pZmVsc2UoZmF0YWwgPT0gIkZhdGFsIiwgNSwgMyksDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSB+cGFsKGZhdGFsKSwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjUsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cG9wdXBUYWJsZShwaGlsbHlDcmltZSksDQogICAgICAgICAgICAgICAgICAgY2x1c3Rlck9wdGlvbnMgPSBtYXJrZXJDbHVzdGVyT3B0aW9ucyhtYXhDbHVzdGVyUmFkaXVzID0gNDApKSAlPiUNCiAgIyBBZGRpbmcgdGhlIGltYWdlIG9mIGNpdHkgaGFsbA0KICBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBwbnQsIA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImJsdWUiLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiQ2l0eSBIYWxsIiwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjk1LA0KICAgICAgICAgICAgICAgICAgIGdyb3VwID0gInBudCIpICU+JQ0KICBhZGRQb3B1cEltYWdlcyhpbWcsIA0KICAgICAgICAgICAgICAgICAgd2lkdGggPSAxMDAsDQogICAgICAgICAgICAgICAgICBoZWlnaHQgPSAxMjAsDQogICAgICAgICAgICAgICAgICB0b29sdGlwID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBncm91cCA9ICJwbnQiKSAgJT4lDQogICMgVHJlbmQgb2YgY3JpbWVzIG92ZXIgdGhlIHllYXJzDQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IG1lZGlhLCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJyZWQiLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiVHJlbmQiLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAibWVkaWEiKSAlPiUNCiAgYWRkUG9wdXBJbWFnZXModHJlbmQsIA0KICAgICAgICAgICAgICAgICAgd2lkdGggPSA1MDAsDQogICAgICAgICAgICAgICAgICBoZWlnaHQgPSAzNTAsDQogICAgICAgICAgICAgICAgICB0b29sdGlwID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICBncm91cCA9ICJtZWRpYSIpICU+JQ0KICMgYWdlIGRpc3RyaWJ1dGlvbiB3aXRoaW4gdGhlIHR3byB0eXBlcyBvZiBjcmltZXMNCiBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBhZ2VEaXN0bG9jLCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJza3libHVlIiwNCiAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLA0KICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIkFnZSBEaXN0cmlidXRpb24iLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiYWdlRGlzdGxvYyIpICU+JQ0KICBhZGRQb3B1cEltYWdlcyhhZ2VEaXN0LCANCiAgICAgICAgICAgICAgICAgIHdpZHRoID0gNTAwLA0KICAgICAgICAgICAgICAgICAgaGVpZ2h0ID0gMzIwLA0KICAgICAgICAgICAgICAgICAgdG9vbHRpcCA9IEZBTFNFLA0KICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiYWdlRGlzdGxvYyIpICU+JQ0KICAjIENyaW1lIGNvdW50cyBieSB6aXAgY29kZTogYnViYmxlIHBsb3QNCiAgYWRkQ2lyY2xlTWFya2VycyhkYXRhID0gemlwbG9jLCANCiAgICAgICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsDQogICAgICAgICAgICAgICAgICAgd2VpZ2h0ID0gMiwNCiAgICAgICAgICAgICAgICAgICBsYWJlbCA9ICJaSVAgTG9jYXRpb24iLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgZ3JvdXAgPSAiemlwbG9jIikgJT4lDQogIGxlYWZwb3A6OjphZGRQb3B1cElmcmFtZXMoDQogICAgICAgICAgICAgICAgICBzb3VyY2UgPSBmbCwNCiAgICAgICAgICAgICAgICAgICB3aWR0aCA9IDUwMCwNCiAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDQwMCwNCiAgICAgICAgICAgICAgICAgICBncm91cCA9ICJ6aXBsb2MiICkgJT4lDQogICMgQW5pbWF0ZWQgKGN1bXVsYXRpdmUpIGNyaW1lIGNvdW50cyBieSB5ZWFycw0KICBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBhbmlsb2MsIA0KICAgICAgICAgICAgICAgICAgIGNvbG9yID0gImdvbGQiLA0KICAgICAgICAgICAgICAgICAgIHdlaWdodCA9IDIsDQogICAgICAgICAgICAgICAgICAgbGFiZWwgPSAiQ3JpbWVzIEFjY3VtdWxhdGlvbiBieSB5ZWFyIiwNCiAgICAgICAgICAgICAgICAgICBzdHJva2UgPSBGQUxTRSwgDQogICAgICAgICAgICAgICAgICAgZmlsbE9wYWNpdHkgPSAwLjk1LA0KICAgICAgICAgICAgICAgICAgIGdyb3VwID0gImFuaWxvYyIpICU+JQ0KICBhZGRQb3B1cEltYWdlcyhjcmltZUJ5WWVhciwgDQogICAgICAgICAgICAgICAgICB3aWR0aCA9IDUwMCwNCiAgICAgICAgICAgICAgICAgIGhlaWdodCA9IDQwMCwNCiAgICAgICAgICAgICAgICAgIHRvb2x0aXAgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgIGdyb3VwID0gImFuaWxvYyIpICU+JSANCiAgIyBDcmltZSBjb3VudHMgYnkgemlwIGNvZGU6IGJ1YmJsZSBwbG90DQogIGFkZENpcmNsZU1hcmtlcnMoZGF0YSA9IGFnZURpc3Rsb2MwMSwgDQogICAgICAgICAgICAgICAgICAgY29sb3IgPSAicHVycGxlIiwNCiAgICAgICAgICAgICAgICAgICB3ZWlnaHQgPSAyLA0KICAgICAgICAgICAgICAgICAgIGxhYmVsID0gIkNvbnRpbmdlbmN5IFRhYmxlOiBSYWNlIHZzIENyaW1lIFR5cGUiLA0KICAgICAgICAgICAgICAgICAgIHN0cm9rZSA9IEZBTFNFLCANCiAgICAgICAgICAgICAgICAgICBmaWxsT3BhY2l0eSA9IDAuOTUsDQogICAgICAgICAgICAgICAgICAgcG9wdXAgPSB+cG9wdXBzKSAlPiUNCiAgYWRkTGF5ZXJzQ29udHJvbChiYXNlR3JvdXBzID0gYygnRGFyaycsICdEYXJrTGFiZWwnLCAnRXNyaScpLA0KICAgICAgICAgICAgICAgICAgIG92ZXJsYXlHcm91cHMgPSBjKCJDcmltZSBEYXRhIiksDQogICAgICAgICAgICAgICAgICAgb3B0aW9ucyA9IGxheWVyc0NvbnRyb2xPcHRpb25zKGNvbGxhcHNlZCA9IFRSVUUpKSAlPiUNCiAgIyMNCiAgYnJvd3NhYmxlKCkNCmBgYA0KDQpUaGUgYW5pbWF0ZWQgcGxvdCAoZ2lmKSBpcyBiYXNlZCBvbiB0aGUgZm9sbG93aW5nIGNvZGUNCg0KYGBge3J9DQp3aGljaF9zdGF0ZSA8LSAicGVubnN5bHZhbmlhIg0KY291bnR5X2luZm8gPC0gbWFwX2RhdGEoImNvdW50eSIsIHJlZ2lvbj13aGljaF9zdGF0ZSkNCnBoaWxseV9pbmZvIDwtIGNvdW50eV9pbmZvW2NvdW50eV9pbmZvJHN1YnJlZ2lvbiA9PSAicGhpbGFkZWxwaGlhIixdDQpwaGlsbHlDcmltZSRZRUFSID0gYXMuaW50ZWdlcihhcy5udW1lcmljKHBoaWxseUNyaW1lJHllYXIpKQ0KcGFsIDwtIGNvbG9yRmFjdG9yKGMoInJlZCIsICJnb2xkIiksIGRvbWFpbiA9IGMoIkZhdGFsIiwgIk5vbmZhdGFsIikpDQojIw0KYmFzZV9tYXAgPC0gZ2dwbG90KGRhdGEgPSBwaGlsbHlfaW5mbywgbWFwcGluZyA9IGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCkpICsNCiBnZW9tX3BvbHlnb24oY29sb3IgPSAicmVkIiwgZmlsbCA9ICJ3aGl0ZSIpICsNCiAgY29vcmRfcXVpY2ttYXAoKSArDQogIHRoZW1lX3ZvaWQoKSANCg0KbWFwX3dpdGhfZGF0YSA8LSBiYXNlX21hcCArDQogIGdlb21fcG9pbnQoZGF0YSA9IHBoaWxseUNyaW1lLCBhZXMoeCA9IGxuZywgeSA9IGxhdCwgZ3JvdXA9WUVBUikpDQoNCm1hcF93aXRoX2RhdGEgPC0gYmFzZV9tYXAgKw0KICBnZW9tX3BvaW50KGRhdGEgPSBwaGlsbHlDcmltZSwgYWVzKHggPSBsbmcsIHkgPSBsYXQsIGNvbG9yID0gZmF0YWwsIGZpbGwgPSBmYXRhbCwgZ3JvdXA9eWVhciksIHNpemUgPSAyLCBhbHBoYSA9IDAuNSkgICsNCiAgc2NhbGVfc2hhcGVfbWFudWFsKHZhbHVlcyA9IGMoMjEsIDI0KSkgKw0KICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYygiI0ZDNEUwNyIsICIjRTdCODAwIikgKQ0KDQptYXBfd2l0aF9hbmltYXRpb24gPC0gbWFwX3dpdGhfZGF0YSArDQogIHRyYW5zaXRpb25fdGltZShZRUFSKSArDQogIGdndGl0bGUoJ1llYXI6IHtmcmFtZV90aW1lfScpICsNCiAgIHNoYWRvd19tYXJrKCkNCg0KYW5pbV9zYXZlKCJQaGlsbHlDcmltZUJ5WWVhci5naWYiLCBtYXBfd2l0aF9hbmltYXRpb24pDQojYW5pbWF0ZShtYXBfd2l0aF9hbmltYXRpb24pDQpgYGANCg0KDQoNCg0KDQojIFByYWN0aWNlIEV4ZXJjaXNlDQoNClRoaXMgYXNzaWdubWVudCB1c2VzIGEgZGF0YSBzZXQgdGhhdCBjb250YWlucyB0aGUgcmVjb3JkcyBvZiBzaG9vdGluZ3MgdGhhdCBvY2N1cnJlZCBpbiBQaGlsYWRlbHBoaWEgaW4gdGhlIHBhc3QgZmV3IHllYXJzLiBUaGUgZGF0YSBzZXQgaXMgYXZhaWxhYmxlIGF0IFtPcGVuRGF0YVBoaWxseV0oaHR0cHM6Ly9vcGVuZGF0YXBoaWxseS5vcmcvZGF0YXNldHMvKS4gVGhlIFVSTCBvZiB0aGUgc3BlY2lmaWMgZGF0YSBzZXQgaXMgYXQ6IFtodHRwczovL29wZW5kYXRhcGhpbGx5Lm9yZy9kYXRhc2V0cy9zaG9vdGluZy12aWN0aW1zL10oaHR0cHM6Ly9vcGVuZGF0YXBoaWxseS5vcmcvZGF0YXNldHMvc2hvb3RpbmctdmljdGltcy8pLiAgVGhlIFBoaWxseSBzaG9vdGluZyBkYXRhIHNldCBpcyBnaXZlbiBpbiBzZXZlcmFsIGZvcm1hdHMuIFdlIHVzZSB0aGUgZ2VvanNvbiBmaWxlLiBJIGFsc28gdXBsb2FkIHRoaXMgZ2VvanNvbiBmaWxlIHRvIG15IEdpdEh1YiByZXBvc2l0b3J5IHNvIHRoYXQgeW91IGNhbiByZWFkIHRoZSBmaWxlIGRpcmVjdGx5IHVzaW5nIHRoZSBSIGZ1bmN0aW9uIGBzdF9yZWFkYCAoc2VlIHRoZSBmb2xsb3dpbmcgY29kZSkuIFRoZXJlIGFyZSB0d28gc2hhcGVmaWxlcyBvZiB0aGUgUGhpbGx5IG5laWdoYm9yaG9vZCBhbmQgYmxvY2tzIHdlcmUgYWxzbyB1c2VkIGluIHRoZSBjYXNlIHN0dWR5LiBJZiB5b3Ugd2FudCB0byB1c2UgdGhlc2UgdHdvIHNoYXBlZmlsZXMgaW4geW91ciB2aXN1YWwgZGVzaWduLCBwbGVhc2UgZmVlbCBmcmVlIHRvIHVzZSBvbmUgb3IgYm90aCBvZiB0aGVtLg0KDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIHBhZ2VkLnByaW50PUZBTFNFLCByZXN1bHRzPUZBTFNFfQ0KcGhpbGx5TmVpZ2hib3JTaG9vdGluZyAgPC0gbmEub21pdChzdF9yZWFkKCJodHRwczovL3Blbmdkc2NpLmdpdGh1Yi5pby9TVEE1NTNWSVovdzA4L1BoaWxseVNob290aW5ncy5nZW9qc29uIikpDQpgYGANCg0KVGhlIG9iamVjdGl2ZSBpcyB0byB1c2UgdGhpcyBkYXRhIHNldCB0byBjcmVhdGUgYW4gaW50ZXJhY3RpdmUgbWFwIHRoYXQgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgYm90aCBpbmRpdmlkdWFsaXplZCBvYnNlcnZhdGlvbiBhbmQgYWdncmVnYXRlZCBmb3JtYXRpb24uDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQo=