1. Location of Severe Fires

This dataset is only updated annually, and thus far only data from 2013 to 2018 is contained. The full dataset is also somewhat too large for an exercise (2.5M rows), so I suggest to limit yourself to a subset. I have added a file containing the subset of of only building fires (INCIDENT_TYPE_DESC == “111 - Building fire”) for 2013 to 2018 only which yields about 14,000 incidents.

Provide a leaflet map of the highest severity fires (i.e. subset to the highest category in HIGHEST_LEVEL_DESC) contained in the file buiding_fires.csv. Ignore locations that fall outside the five boroughs of New York City. Provide at least three pieces of information on the incident in a popup.

severe_building_fires <- read_csv("building_fires.csv") %>%
  mutate(alarm = case_when(
    HIGHEST_LEVEL_DESC == "5 - 5th alarm"    ~ "severe",
    HIGHEST_LEVEL_DESC == "55 - Fifth Alarm" ~ "severe",
    TRUE                                     ~ "not severe")) %>%
  filter(alarm == "severe") %>%
  mutate(durat_hours = (TOTAL_INCIDENT_DURATION/3600)) %>%
  mutate(durat_hours = sprintf("%0.1f", durat_hours)) %>%
  mutate(fire_spread = case_when(
    FIRE_SPREAD_DESC == "1 - Confined to object of origin"   ~ "Confined to object",
    FIRE_SPREAD_DESC == "2 - Confined to room of origin"     ~ "Confined to room",
    FIRE_SPREAD_DESC == "3 - Confined to floor of origin"    ~ "Confined to floor",
    FIRE_SPREAD_DESC == "4 - Confined to building of origin" ~ "Confined to building",
    FIRE_SPREAD_DESC == "5 - Beyond building of origin"      ~ "Spread beyond building",
    TRUE                                                     ~ "NA")) %>% 
  mutate(fire_spread = factor(fire_spread, levels = c("Confined to object", 
                                                      "Confined to room", 
                                                      "Confined to floor", 
                                                      "Confined to building", 
                                                      "Spread beyond building"))) %>%
  mutate(detector = case_when(
    DETECTOR_PRESENCE_DESC == "N - None present" ~ "No",
    DETECTOR_PRESENCE_DESC == "1 - Present"      ~ "Yes",
    TRUE                                         ~ "NA"))

|=======                                                                                                                                     |   5%
|===========                                                                                                                                 |   7%
|==============                                                                                                                              |  10%
|==================                                                                                                                          |  13%
|======================                                                                                                                      |  15%
|=========================                                                                                                                   |  18%
|===========================                                                                                                         |  20%    1 MB
|===============================                                                                                                     |  23%    1 MB
|==================================                                                                                                  |  26%    1 MB
|======================================                                                                                              |  28%    1 MB
|=========================================                                                                                           |  31%    1 MB
|=============================================                                                                                       |  34%    1 MB
|================================================                                                                                    |  36%    1 MB
|====================================================                                                                                |  39%    2 MB
|=======================================================                                                                             |  41%    2 MB
|===========================================================                                                                         |  44%    2 MB
|==============================================================                                                                      |  47%    2 MB
|==================================================================                                                                  |  49%    2 MB
|=====================================================================                                                               |  52%    2 MB
|========================================================================                                                            |  54%    2 MB
|============================================================================                                                        |  57%    3 MB
|===============================================================================                                                     |  60%    3 MB
|===================================================================================                                                 |  62%    3 MB
|======================================================================================                                              |  65%    3 MB
|==========================================================================================                                          |  67%    3 MB
|=============================================================================================                                       |  70%    3 MB
|=================================================================================================                                   |  73%    3 MB
|====================================================================================================                                |  75%    3 MB
|========================================================================================================                            |  78%    4 MB
|===========================================================================================================                         |  80%    4 MB
|===============================================================================================================                     |  83%    4 MB
|==================================================================================================================                  |  86%    4 MB
|======================================================================================================================              |  88%    4 MB
|=========================================================================================================================           |  91%    4 MB
|============================================================================================================================        |  93%    4 MB
|================================================================================================================================    |  96%    5 MB
|=================================================================================================================================== |  99%    5 MB
|=====================================================================================================================================| 100%    5 MB
content <- paste("Units responding:", severe_building_fires$UNITS_ONSCENE,"<br/>",
                 "Hours to contain:", (severe_building_fires$durat_hours),"<br/>",
                 "Fire spread:", severe_building_fires$fire_spread,"<br/>")
m1 <- leaflet(severe_building_fires) %>%
  addTiles(urlTemplate = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data") %>%
  addCircles(lng = ~lon, lat = ~lat, radius = 100, color = "orange", fill = "orange", popup = content) %>%
  addControl("Severe Building Fires in NYC (2013-2018)", position = "topright")
m1

2. Layers and Clusters

a) Color by Type of Property

Start with the previous map. Now, distinguish the markers of the fire locations by PROPERTY_USE_DESC, i.e. what kind of property was affected. If there are too many categories, collapse some categories. Choose an appropriate coloring scheme to map the locations by type of affected property. Add a legend informing the user about the color scheme. Also make sure that the information about the type of affected property is now contained in the popup information. Show this map.

severe_building_fires_2 <- severe_building_fires %>%
  mutate(bldg_use = case_when(
    PROPERTY_USE_DESC == "429 - Multifamily dwelling"                   ~ "Apartment or condo",
    PROPERTY_USE_DESC == "500 - Mercantile, business, other"            ~ "Business",
    PROPERTY_USE_DESC == "419 - 1 or 2 family dwelling"                 ~ "House",
    PROPERTY_USE_DESC == "519 - Food and beverage sales, grocery store" ~ "Grocery",
    PROPERTY_USE_DESC == "599 - Business office"                        ~ "Office",
    TRUE                                                                ~ "Other")) %>%
  mutate(bldg_use = factor(bldg_use, 
         levels = c("Apartment or condo", "Business", "House", "Grocery", "Office", "Other")))
content2 <- paste("Building use:", severe_building_fires_2$bldg_use, "<br/>",
                  "Units responding:", severe_building_fires_2$UNITS_ONSCENE,"<br/>",
                  "Hours to contain:", severe_building_fires_2$durat_hours,"<br/>",
                  "Fire spread:", severe_building_fires_2$fire_spread,"<br/>")
pal = colorFactor("Dark2", domain = severe_building_fires_2$bldg_use)
color_use = pal(severe_building_fires_2$bldg_use)
m2 <- leaflet(severe_building_fires_2) %>%
  addTiles('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data") %>%
  addCircles(lng = ~lon, lat = ~lat, radius = 100, color = color_use, fill = color_use, popup = content2) %>%
  addLegend(pal = pal, values = severe_building_fires_2$bldg_use, title = "Building use", 
            position = "topleft") %>%
  addControl("Severe Building Fires in NYC (2013-2018)", position = "topright")
m2

b) Cluster

Add marker clustering, so that zooming in will reveal the individual locations but the zoomed out map only shows the clusters. Show the map with clusters.

m3 <- m2 %>% addCircleMarkers(lng = ~lon, lat = ~lat, color = color_use, popup = content2, 
                              clusterOptions = markerClusterOptions())
m3

3. Fire Houses

The second data file contains the locations of the 218 firehouses in New York City. Start with the non-clustered map (2b) and now adjust the size of the circle markers by severity (TOTAL_INCIDENT_DURATION or UNITS_ONSCENE seem plausible options). More severe incidents should have larger circles on the map. On the map, also add the locations of the fire houses. Add two layers (“Incidents”, “Firehouses”) that allow the user to select which information to show.

firehouses <- read_csv("FDNY_Firehouse_Listing.csv") %>%
  filter(!is.na(Latitude))
m4 <- leaflet() %>%
  addTiles('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data") %>%
  addCircleMarkers(data = severe_building_fires_2, lng = ~lon, lat = ~lat, color = color_use, popup = content2, 
                   radius = (severe_building_fires_2$UNITS_ONSCENE/5), group = "Incidents") %>%
  addLegend(data = severe_building_fires_2, pal = pal, values = severe_building_fires_2$bldg_use, 
            title = "Building use", group = "Incidents", position = "topleft") %>%
  addCircles(data = firehouses, color = "gray", popup = ~paste("Name: ", FacilityName, "<br>Address: ", 
             FacilityAddress), group = "Firehouses") %>%
  addControl("Severe Building Fires in NYC 2013-2018", position = "topright") %>%
  addLayersControl(overlayGroups = c("Incidents","Firehouses"), 
                   options = layersControlOptions(collapsed = FALSE))
m4

4. Distance from Firehouse and Response Time

We now want to investigate whether the distance of the incident from the nearest firehouse varies across the city.

a) Calculate Distance

For all incident locations (independent of severity), identify the nearest firehouse and calculate the distance between the firehouse and the incident location. Provide a scatter plot showing the time until the first engine arrived (the variables INCIDENT_DATE_TIME and ARRIVAL_DATE_TIME) will be helpful.

building_fires <- read_csv("building_fires.csv")
houses.xy <- select(firehouses, Longitude, Latitude)
fires.xy <- select(building_fires, lon, lat)
dist_matrix <- distm(fires.xy, houses.xy, fun = distGeo)
dist_nearest <- apply(dist_matrix, 1, min)
building_fires_2 <- building_fires
building_fires_2['dist_nearest'] <- dist_nearest
building_fires_3 <- building_fires_2 %>%
  filter(IM_INCIDENT_KEY != 59533504) %>%  # Incident located outside NYC
  filter(IM_INCIDENT_KEY != 57703382) %>%  # Response time is negative
  mutate(dist_nearest_miles = (dist_nearest * 0.0006213712)) %>%
  mutate(dist_nearest_feet = (dist_nearest * 3.28084)) %>%
  mutate(call_time = mdy_hms(INCIDENT_DATE_TIME)) %>%
  mutate(arrival_time = mdy_hms(ARRIVAL_DATE_TIME)) %>%
  mutate(call_time_2 = as.POSIXct(call_time, tz = Sys.timezone())) %>%
  mutate(arrival_time_2 = as.POSIXct(arrival_time, tz = Sys.timezone())) %>%
  mutate(mins_to_resp = difftime(arrival_time_2, call_time_2, units = "mins"))%>%
  mutate(mins_to_resp_2 = as.double(mins_to_resp)) %>%
  mutate(mins_to_resp_3 = sprintf("%0.1f", mins_to_resp_2))
p1 <- ggplot(data = building_fires_3, aes(x = dist_nearest_miles, y = mins_to_resp_2)) +
  geom_point() +
  coord_cartesian(xlim = c(0, 3), ylim = c(0, 25)) +
  theme_fivethirtyeight() +
  geom_smooth(se = FALSE, lwd = 1.5, color = "brown4") +
  theme(axis.title = element_text(), legend.position = "none") +
  labs(caption = "Source: NYC Open Data 2013-2018") +
  xlab("Miles to nearest firehouse") +
  ylab("Minutes to respond") +
  ggtitle("Response time for building fires in NYC")
p1

Now also visualize the patterns separately for severe and non-severe incidents (use HIGHEST_LEVEL_DESC but feel free to reduce the number of categories). What do you find?

building_fires_4 <- building_fires_3 %>%
  mutate(alarm_level = case_when(
    HIGHEST_LEVEL_DESC == "0 - Initial alarm"      ~ "Initial",
    HIGHEST_LEVEL_DESC == "1 - More than initial alarm, less than Signal 7-5" ~ "1st alarm",
    HIGHEST_LEVEL_DESC == "11 - First Alarm"       ~ "1st alarm",
    HIGHEST_LEVEL_DESC == "7 - Signal 7-5"         ~ "All hands",
    HIGHEST_LEVEL_DESC == "75 - All Hands Working" ~ "All hands",
    HIGHEST_LEVEL_DESC == "2 - 2nd alarm"          ~ "2nd alarm",
    HIGHEST_LEVEL_DESC == "22 - Second Alarm"      ~ "2nd alarm",
    HIGHEST_LEVEL_DESC == "3 - 3rd alarm"          ~ "3rd alarm",
    HIGHEST_LEVEL_DESC == "33 - Third Alarm"       ~ "3rd alarm",
    HIGHEST_LEVEL_DESC == "4 - 4th alarm"          ~ "4th alarm",
    HIGHEST_LEVEL_DESC == "44 - Fourth Alarm"      ~ "4th alarm",
    HIGHEST_LEVEL_DESC == "5 - 5th alarm"          ~ "5th alarm",
    HIGHEST_LEVEL_DESC == "55 - Fifth Alarm"       ~ "5th alarm")) %>%
  mutate(alarm_level = factor(alarm_level, levels = c("Initial", "1st alarm", "All hands", 
                                                      "2nd alarm", "3rd alarm", "4th alarm", "5th alarm"))) %>%
  drop_na(alarm_level) %>%
  mutate(alarm_cat = case_when(
    alarm_level == "Initial"    ~ "Initial and 1st alarm",
    alarm_level == "1st alarm"  ~ "Initial and 1st alarm",
    alarm_level == "All hands"  ~ "All hands",
    alarm_level == "2nd alarm"  ~ "2nd and 3rd alarm",
    alarm_level == "3rd alarm"  ~ "2nd and 3rd alarm",
    alarm_level == "4th alarm"  ~ "4th and 5th alarm",
    alarm_level == "5th alarm"  ~ "4th and 5th alarm")) %>%
  mutate(alarm_cat = factor(alarm_cat, levels = c("Initial and 1st alarm","All hands", 
                                                  "2nd and 3rd alarm", "4th and 5th alarm"))) %>%
  mutate(alarm_sev = case_when(
    alarm_level == "Initial"   ~ "Low",
    alarm_level == "1st alarm" ~ "Low",
    alarm_level == "All hands" ~ "Medium",
    alarm_level == "2nd alarm" ~ "Medium",
    TRUE                       ~ "High")) %>%
  mutate(alarm_sev = factor(alarm_sev, levels = c("Low", "Medium", "High"))) %>%
  mutate(durat_hours = (TOTAL_INCIDENT_DURATION/3600)) %>%
  mutate(durat_hours = sprintf("%0.1f", durat_hours)) %>%
  mutate(fire_spread = case_when(
    FIRE_SPREAD_DESC == "1 - Confined to object of origin"   ~ "Confined to object",
    FIRE_SPREAD_DESC == "2 - Confined to room of origin"     ~ "Confined to room",
    FIRE_SPREAD_DESC == "3 - Confined to floor of origin"    ~ "Confined to floor",
    FIRE_SPREAD_DESC == "4 - Confined to building of origin" ~ "Confined to building",
    FIRE_SPREAD_DESC == "5 - Beyond building of origin"      ~ "Spread beyond building",
    TRUE                                                     ~ "NA")) %>% 
  mutate(fire_spread = factor(fire_spread, levels = c("Confined to object", 
                                                      "Confined to room", 
                                                      "Confined to floor", 
                                                      "Confined to building", 
                                                      "Spread beyond building"))) %>%
  mutate(bldg_use = case_when(
    PROPERTY_USE_DESC == "429 - Multifamily dwelling"        ~ "Apartment or condo",
    PROPERTY_USE_DESC == "419 - 1 or 2 family dwelling"      ~ "House",
    PROPERTY_USE_DESC == "500 - Mercantile, business, other" ~ "Business",
    PROPERTY_USE_DESC == "161 - Restaurant or cafeteria"     ~ "Restaurant",
    TRUE                                                     ~ "Other")) %>%
  mutate(bldg_use = factor(bldg_use, levels = c("Apartment or condo", "House", "Business", 
                                                "Restaurant", "Office", "Other"))) %>%
  mutate(bldg_cat = case_when(
    bldg_use == "Apartment or condo" ~ "Residential",
    bldg_use == "House"              ~ "Residential",
    bldg_use == "Business"           ~ "Commercial",
    bldg_use == "Office"             ~ "Commercial",
    bldg_use == "Restaurant"         ~ "Commercial",
    TRUE                             ~ "Other")) %>%
  mutate(bldg_cat = factor(bldg_cat, levels = c("Residential", "Commercial", "Other")))
p2 <- ggplot(data = building_fires_4, aes(x = dist_nearest_miles, y = mins_to_resp_2)) +
  geom_point() +
  coord_cartesian(xlim = c(0, 3), ylim = c(0, 25)) +
  theme_fivethirtyeight() +
  geom_smooth(se = FALSE, lwd = 1, color = "brown4") +
  theme(axis.title = element_text(), legend.position = "none") +
  labs(caption = "Source: NYC Open Data 2013-2018") +
  xlab("Miles to nearest firehouse") +
  ylab("Minutes to respond") +
  ggtitle("Response time for building fires in NYC \nby alarm level") +
  facet_wrap(~ alarm_cat)
p2

b) Map of Response Times

Provide a map visualization of response times. Investigate whether the type of property affected (PROPERTY_USE_DESC) or fire severity (HIGHEST_LEVEL_DESC) play a role here.

pal.1 <- colorBin(palette = "YlOrRd", domain = building_fires_4$mins_to_resp_2, bins = 5, 
                  na.color = NULL, pretty = FALSE, alpha = TRUE)
qpal <- colorQuantile("YlOrRd", building_fires_4$mins_to_resp_2, n = 5)
content3 <- paste("Building use:", building_fires_4$bldg_use, "<br/>",
                  "Highest alarm:", building_fires_4$alarm_level, "<br/>",
                  "Units responding:", building_fires_4$UNITS_ONSCENE, "<br/>",
                  "Minutes to respond:", building_fires_4$mins_to_resp_3, "<br/>",
                  "Hours to contain:", building_fires_4$durat_hours,"<br/>",
                  "Fire spread:", building_fires_4$fire_spread,"<br/>")
m5 <- leaflet(building_fires_4) %>%
  addTiles(urlTemplate = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data 2013-2018") %>%
  addCircles(data = subset(building_fires_4, building_fires_4$alarm_sev == "Low"), lng = ~lon, lat = ~lat, 
             color = ~qpal(mins_to_resp_2), popup = content3, group = "Low severity") %>%
  addCircles(data = subset(building_fires_4, building_fires_4$alarm_sev == "Medium"), lng = ~lon, lat = ~lat, 
             color = ~qpal(mins_to_resp_2), popup = content3, group = "Medium severity") %>%
  addCircles(data = subset(building_fires_4, building_fires_4$alarm_sev == "High"), lng = ~lon, lat = ~lat, 
             color = ~qpal(mins_to_resp_2), popup = content3, group = "High severity") %>%
  addLegend(pal = pal.1, values  = building_fires_4$mins_to_resp_2, position = "topleft", 
            title = "Minutes to respond", labFormat = labelFormat(digits = 0)) %>%
  addControl("Fire Response Time by Alarm Level", position = "topright") %>%
  addLayersControl(overlayGroups = c("Low severity", "Medium severity", "High severity"), 
                   options = layersControlOptions(collapsed = FALSE))
m5
m6 <- leaflet(building_fires_4) %>%
  addTiles(urlTemplate = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data 2013-2018") %>%
  addCircles(data = subset(building_fires_4, building_fires_4$bldg_cat == "Residential"), lng = ~lon, lat = ~lat, 
             color = ~qpal(mins_to_resp_2), popup = content3, group = "Residential") %>%
  addCircles(data = subset(building_fires_4, building_fires_4$bldg_cat == "Commercial"), lng = ~lon, lat = ~lat, 
             color = ~qpal(mins_to_resp_2), popup = content3, group = "Commercial") %>%
  addCircles(data = subset(building_fires_4, building_fires_4$bldg_cat == "Other"), lng = ~lon, lat = ~lat, 
             color = ~qpal(mins_to_resp_2), popup = content3, group = "Other") %>%
  addLegend(pal = pal.1, values  = building_fires_4$mins_to_resp_2, position = "topleft", 
            title = "Minutes to respond", labFormat = labelFormat(digits = 0)) %>%
  addControl("Fire Response Time by Building Type", position = "topright") %>%
  addLayersControl(overlayGroups = c("Residential", "Commercial", "Other"), 
                   options = layersControlOptions(collapsed = FALSE))
m6

Show a faceted choropleth map indicating how response times have developed over the years. What do you find?

rename_boros <- function(x){gsub("^[0-9] - ", "", x)}
boro_name <- unlist(lapply(building_fires_4$BOROUGH_DESC, rename_boros))
building_fires_5 <- building_fires_4
building_fires_5['boro_name'] <- boro_name
pal.2 <- colorNumeric("YlOrRd", NULL)
m7 <- leaflet(nyc_boros) %>%
  addTiles(urlTemplate = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data 2013-2018") %>%
  addPolygons(stroke = FALSE, smoothFactor = 0.3, fillOpacity = 1,
    fillColor = ~pal.2(avg_boro_resp_time)) %>%
  addLegend(pal = pal.2, values  = ~avg_boro_resp_time, position = "topleft", 
            title = "Minutes to respond", labFormat = labelFormat(digits = 2)) %>%
  addControl("Average Fire Response Time (2013-2018)", position = "topright") 
m7
m8 <- leaflet(nyc_boros) %>%
  addTiles(urlTemplate = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data 2013-2018") %>%
  addPolygons(stroke = FALSE, smoothFactor = 0.3, fillOpacity = 1,
    fillColor = ~pal.2(boro_resp_time_2013)) %>%
  addLegend(pal = pal.2, values = ~boro_resp_time_2013, position = "topleft", 
            title = "Minutes to respond", labFormat = labelFormat(digits = 2)) %>%
  addControl("Average Fire Response Time in 2013", position = "topright")
m8
m9 <- leaflet(nyc_boros) %>%
  addTiles(urlTemplate = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data 2013-2018") %>%
  addPolygons(stroke = FALSE, smoothFactor = 0.3, fillOpacity = 1,
    fillColor = ~pal.2(boro_resp_time_2018)) %>%
  addLegend(pal = pal.2, values = ~boro_resp_time_2018, position = "topleft", 
            title = "Minutes to respond", labFormat = labelFormat(digits = 2)) %>%
  addControl("Average Fire Response Time in 2018", position = "topright")
m9
m10 <- leaflet(nyc_boros) %>%
  addTiles(urlTemplate = 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png', 
           attribution = "Source: NYC Open Data 2013-2018") %>%
  addPolygons(stroke = FALSE, smoothFactor = 0.3, fillOpacity = 1,
    fillColor = ~pal.2(boro_resp_time_diff)) %>%
  addLegend(pal = pal.2, values = ~boro_resp_time_diff, position = "topleft", 
            title = "Minutes to respond", labFormat = labelFormat(digits = 2)) %>%
  addControl("Change in Fire Response Times 2013-2018", position = "topright") 
m10

Average response times increased from 2013 to 2018, with the lowest increase in Brooklyn and the highest increase in Queens.

LS0tCnRpdGxlOiAiSG9tZXdvcmsgMjogTllDIEZpcmVzIgphdXRob3I6ICJBbGlzb24gUnlsYW5kIgpkYXRlOiAiTWFyY2ggMzAsIDIwMjAiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCmBgYHtyIFNldHVwLCBpbmNsdWRlID0gRkFMU0UsIHJlc3VsdHMgPSAnaGlkZScsIHdhcm5pbmcgPSBGQUxTRX0KbGlicmFyeShrbml0cikKbGlicmFyeShwYWNtYW4pCgpwX2xvYWQodGlkeXZlcnNlLCBnZ21hcCwgbGVhZmxldCwgUkNvbG9yQnJld2VyLCBkYXRhLnRhYmxlLCBzcCwgcmdkYWwsIHJnZW9zLCBnZW9zcGhlcmUsIGx1YnJpZGF0ZSwgZ2dwbG90MiwgZ2d0aGVtZXMsIHRtYXAsIHNwYXRpYWxFY28sIGdlb2pzb25pbywgdG1hcHRvb2xzLCBsdWJyaWRhdGUpCmBgYAoKIyMgMS4gTG9jYXRpb24gb2YgU2V2ZXJlIEZpcmVzCgoqVGhpcyBkYXRhc2V0IGlzIG9ubHkgdXBkYXRlZCBhbm51YWxseSwgYW5kIHRodXMgZmFyIG9ubHkgZGF0YSBmcm9tIDIwMTMgdG8gMjAxOCBpcyBjb250YWluZWQuIFRoZSBmdWxsIGRhdGFzZXQgaXMgYWxzbyBzb21ld2hhdCB0b28gbGFyZ2UgZm9yIGFuIGV4ZXJjaXNlICgyLjVNIHJvd3MpLCBzbyBJIHN1Z2dlc3QgdG8gbGltaXQgeW91cnNlbGYgdG8gYSBzdWJzZXQuIEkgaGF2ZSBhZGRlZCBhIGZpbGUgY29udGFpbmluZyB0aGUgc3Vic2V0IG9mIG9mIG9ubHkgYnVpbGRpbmcgZmlyZXMgKElOQ0lERU5UX1RZUEVfREVTQyA9PSAiMTExIC0gQnVpbGRpbmcgZmlyZSIpIGZvciAyMDEzIHRvIDIwMTggb25seSB3aGljaCB5aWVsZHMgYWJvdXQgMTQsMDAwIGluY2lkZW50cy4qCgoqUHJvdmlkZSBhIGxlYWZsZXQgbWFwIG9mIHRoZSBoaWdoZXN0IHNldmVyaXR5IGZpcmVzIChpLmUuIHN1YnNldCB0byB0aGUgaGlnaGVzdCBjYXRlZ29yeSBpbiBISUdIRVNUX0xFVkVMX0RFU0MpIGNvbnRhaW5lZCBpbiB0aGUgZmlsZSBidWlkaW5nX2ZpcmVzLmNzdi4gSWdub3JlIGxvY2F0aW9ucyB0aGF0IGZhbGwgb3V0c2lkZSB0aGUgZml2ZSBib3JvdWdocyBvZiBOZXcgWW9yayBDaXR5LiBQcm92aWRlIGF0IGxlYXN0IHRocmVlIHBpZWNlcyBvZiBpbmZvcm1hdGlvbiBvbiB0aGUgaW5jaWRlbnQgaW4gYSBwb3B1cC4qCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpzZXZlcmVfYnVpbGRpbmdfZmlyZXMgPC0gcmVhZF9jc3YoImJ1aWxkaW5nX2ZpcmVzLmNzdiIpICU+JQogIG11dGF0ZShhbGFybSA9IGNhc2Vfd2hlbigKICAgIEhJR0hFU1RfTEVWRUxfREVTQyA9PSAiNSAtIDV0aCBhbGFybSIgICAgfiAic2V2ZXJlIiwKICAgIEhJR0hFU1RfTEVWRUxfREVTQyA9PSAiNTUgLSBGaWZ0aCBBbGFybSIgfiAic2V2ZXJlIiwKICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiAibm90IHNldmVyZSIpKSAlPiUKICBmaWx0ZXIoYWxhcm0gPT0gInNldmVyZSIpICU+JQogIG11dGF0ZShkdXJhdF9ob3VycyA9IChUT1RBTF9JTkNJREVOVF9EVVJBVElPTi8zNjAwKSkgJT4lCiAgbXV0YXRlKGR1cmF0X2hvdXJzID0gc3ByaW50ZigiJTAuMWYiLCBkdXJhdF9ob3VycykpICU+JQogIG11dGF0ZShmaXJlX3NwcmVhZCA9IGNhc2Vfd2hlbigKICAgIEZJUkVfU1BSRUFEX0RFU0MgPT0gIjEgLSBDb25maW5lZCB0byBvYmplY3Qgb2Ygb3JpZ2luIiAgIH4gIkNvbmZpbmVkIHRvIG9iamVjdCIsCiAgICBGSVJFX1NQUkVBRF9ERVNDID09ICIyIC0gQ29uZmluZWQgdG8gcm9vbSBvZiBvcmlnaW4iICAgICB+ICJDb25maW5lZCB0byByb29tIiwKICAgIEZJUkVfU1BSRUFEX0RFU0MgPT0gIjMgLSBDb25maW5lZCB0byBmbG9vciBvZiBvcmlnaW4iICAgIH4gIkNvbmZpbmVkIHRvIGZsb29yIiwKICAgIEZJUkVfU1BSRUFEX0RFU0MgPT0gIjQgLSBDb25maW5lZCB0byBidWlsZGluZyBvZiBvcmlnaW4iIH4gIkNvbmZpbmVkIHRvIGJ1aWxkaW5nIiwKICAgIEZJUkVfU1BSRUFEX0RFU0MgPT0gIjUgLSBCZXlvbmQgYnVpbGRpbmcgb2Ygb3JpZ2luIiAgICAgIH4gIlNwcmVhZCBiZXlvbmQgYnVpbGRpbmciLAogICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiAiTkEiKSkgJT4lIAogIG11dGF0ZShmaXJlX3NwcmVhZCA9IGZhY3RvcihmaXJlX3NwcmVhZCwgbGV2ZWxzID0gYygiQ29uZmluZWQgdG8gb2JqZWN0IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb25maW5lZCB0byByb29tIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDb25maW5lZCB0byBmbG9vciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29uZmluZWQgdG8gYnVpbGRpbmciLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNwcmVhZCBiZXlvbmQgYnVpbGRpbmciKSkpICU+JQogIG11dGF0ZShkZXRlY3RvciA9IGNhc2Vfd2hlbigKICAgIERFVEVDVE9SX1BSRVNFTkNFX0RFU0MgPT0gIk4gLSBOb25lIHByZXNlbnQiIH4gIk5vIiwKICAgIERFVEVDVE9SX1BSRVNFTkNFX0RFU0MgPT0gIjEgLSBQcmVzZW50IiAgICAgIH4gIlllcyIsCiAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+ICJOQSIpKQpgYGAKCmBgYHtyfQpjb250ZW50IDwtIHBhc3RlKCJVbml0cyByZXNwb25kaW5nOiIsIHNldmVyZV9idWlsZGluZ19maXJlcyRVTklUU19PTlNDRU5FLCI8YnIvPiIsCiAgICAgICAgICAgICAgICAgIkhvdXJzIHRvIGNvbnRhaW46IiwgKHNldmVyZV9idWlsZGluZ19maXJlcyRkdXJhdF9ob3VycyksIjxici8+IiwKICAgICAgICAgICAgICAgICAiRmlyZSBzcHJlYWQ6Iiwgc2V2ZXJlX2J1aWxkaW5nX2ZpcmVzJGZpcmVfc3ByZWFkLCI8YnIvPiIpCmBgYAoKYGBge3J9Cm0xIDwtIGxlYWZsZXQoc2V2ZXJlX2J1aWxkaW5nX2ZpcmVzKSAlPiUKICBhZGRUaWxlcyh1cmxUZW1wbGF0ZSA9ICdodHRwczovL3tzfS5iYXNlbWFwcy5jYXJ0b2Nkbi5jb20vbGlnaHRfYWxsL3t6fS97eH0ve3l9e3J9LnBuZycsIAogICAgICAgICAgIGF0dHJpYnV0aW9uID0gIlNvdXJjZTogTllDIE9wZW4gRGF0YSIpICU+JQogIGFkZENpcmNsZXMobG5nID0gfmxvbiwgbGF0ID0gfmxhdCwgcmFkaXVzID0gMTAwLCBjb2xvciA9ICJvcmFuZ2UiLCBmaWxsID0gIm9yYW5nZSIsIHBvcHVwID0gY29udGVudCkgJT4lCiAgYWRkQ29udHJvbCgiU2V2ZXJlIEJ1aWxkaW5nIEZpcmVzIGluIE5ZQyAoMjAxMy0yMDE4KSIsIHBvc2l0aW9uID0gInRvcHJpZ2h0IikKCm0xCmBgYAoKCiMjIDIuIExheWVycyBhbmQgQ2x1c3RlcnMKCiMjIyBhKSBDb2xvciBieSBUeXBlIG9mIFByb3BlcnR5CgoqU3RhcnQgd2l0aCB0aGUgcHJldmlvdXMgbWFwLiBOb3csIGRpc3Rpbmd1aXNoIHRoZSBtYXJrZXJzIG9mIHRoZSBmaXJlIGxvY2F0aW9ucyBieSBQUk9QRVJUWV9VU0VfREVTQywgaS5lLiB3aGF0IGtpbmQgb2YgcHJvcGVydHkgd2FzIGFmZmVjdGVkLiBJZiB0aGVyZSBhcmUgdG9vIG1hbnkgY2F0ZWdvcmllcywgY29sbGFwc2Ugc29tZSBjYXRlZ29yaWVzLiBDaG9vc2UgYW4gYXBwcm9wcmlhdGUgY29sb3Jpbmcgc2NoZW1lIHRvIG1hcCB0aGUgbG9jYXRpb25zIGJ5IHR5cGUgb2YgYWZmZWN0ZWQgcHJvcGVydHkuIEFkZCBhIGxlZ2VuZCBpbmZvcm1pbmcgdGhlIHVzZXIgYWJvdXQgdGhlIGNvbG9yIHNjaGVtZS4gQWxzbyBtYWtlIHN1cmUgdGhhdCB0aGUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHR5cGUgb2YgYWZmZWN0ZWQgcHJvcGVydHkgaXMgbm93IGNvbnRhaW5lZCBpbiB0aGUgcG9wdXAgaW5mb3JtYXRpb24uIFNob3cgdGhpcyBtYXAuKgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0Kc2V2ZXJlX2J1aWxkaW5nX2ZpcmVzICU+JQogIGdyb3VwX2J5KFBST1BFUlRZX1VTRV9ERVNDKSAlPiUgCiAgY291bnQoc29ydCA9IFRSVUUpCmBgYAoKYGBge3J9CnNldmVyZV9idWlsZGluZ19maXJlc18yIDwtIHNldmVyZV9idWlsZGluZ19maXJlcyAlPiUKICBtdXRhdGUoYmxkZ191c2UgPSBjYXNlX3doZW4oCiAgICBQUk9QRVJUWV9VU0VfREVTQyA9PSAiNDI5IC0gTXVsdGlmYW1pbHkgZHdlbGxpbmciICAgICAgICAgICAgICAgICAgIH4gIkFwYXJ0bWVudCBvciBjb25kbyIsCiAgICBQUk9QRVJUWV9VU0VfREVTQyA9PSAiNTAwIC0gTWVyY2FudGlsZSwgYnVzaW5lc3MsIG90aGVyIiAgICAgICAgICAgIH4gIkJ1c2luZXNzIiwKICAgIFBST1BFUlRZX1VTRV9ERVNDID09ICI0MTkgLSAxIG9yIDIgZmFtaWx5IGR3ZWxsaW5nIiAgICAgICAgICAgICAgICAgfiAiSG91c2UiLAogICAgUFJPUEVSVFlfVVNFX0RFU0MgPT0gIjUxOSAtIEZvb2QgYW5kIGJldmVyYWdlIHNhbGVzLCBncm9jZXJ5IHN0b3JlIiB+ICJHcm9jZXJ5IiwKICAgIFBST1BFUlRZX1VTRV9ERVNDID09ICI1OTkgLSBCdXNpbmVzcyBvZmZpY2UiICAgICAgICAgICAgICAgICAgICAgICAgfiAiT2ZmaWNlIiwKICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfiAiT3RoZXIiKSkgJT4lCiAgbXV0YXRlKGJsZGdfdXNlID0gZmFjdG9yKGJsZGdfdXNlLCAKICAgICAgICAgbGV2ZWxzID0gYygiQXBhcnRtZW50IG9yIGNvbmRvIiwgIkJ1c2luZXNzIiwgIkhvdXNlIiwgIkdyb2NlcnkiLCAiT2ZmaWNlIiwgIk90aGVyIikpKQpgYGAKCmBgYHtyfQpjb250ZW50MiA8LSBwYXN0ZSgiQnVpbGRpbmcgdXNlOiIsIHNldmVyZV9idWlsZGluZ19maXJlc18yJGJsZGdfdXNlLCAiPGJyLz4iLAogICAgICAgICAgICAgICAgICAiVW5pdHMgcmVzcG9uZGluZzoiLCBzZXZlcmVfYnVpbGRpbmdfZmlyZXNfMiRVTklUU19PTlNDRU5FLCI8YnIvPiIsCiAgICAgICAgICAgICAgICAgICJIb3VycyB0byBjb250YWluOiIsIHNldmVyZV9idWlsZGluZ19maXJlc18yJGR1cmF0X2hvdXJzLCI8YnIvPiIsCiAgICAgICAgICAgICAgICAgICJGaXJlIHNwcmVhZDoiLCBzZXZlcmVfYnVpbGRpbmdfZmlyZXNfMiRmaXJlX3NwcmVhZCwiPGJyLz4iKQpgYGAKCmBgYHtyfQpwYWwgPSBjb2xvckZhY3RvcigiRGFyazIiLCBkb21haW4gPSBzZXZlcmVfYnVpbGRpbmdfZmlyZXNfMiRibGRnX3VzZSkKY29sb3JfdXNlID0gcGFsKHNldmVyZV9idWlsZGluZ19maXJlc18yJGJsZGdfdXNlKQpgYGAKCmBgYHtyfQptMiA8LSBsZWFmbGV0KHNldmVyZV9idWlsZGluZ19maXJlc18yKSAlPiUKICBhZGRUaWxlcygnaHR0cHM6Ly97c30uYmFzZW1hcHMuY2FydG9jZG4uY29tL2xpZ2h0X2FsbC97en0ve3h9L3t5fXtyfS5wbmcnLCAKICAgICAgICAgICBhdHRyaWJ1dGlvbiA9ICJTb3VyY2U6IE5ZQyBPcGVuIERhdGEiKSAlPiUKICBhZGRDaXJjbGVzKGxuZyA9IH5sb24sIGxhdCA9IH5sYXQsIHJhZGl1cyA9IDEwMCwgY29sb3IgPSBjb2xvcl91c2UsIGZpbGwgPSBjb2xvcl91c2UsIHBvcHVwID0gY29udGVudDIpICU+JQogIGFkZExlZ2VuZChwYWwgPSBwYWwsIHZhbHVlcyA9IHNldmVyZV9idWlsZGluZ19maXJlc18yJGJsZGdfdXNlLCB0aXRsZSA9ICJCdWlsZGluZyB1c2UiLCAKICAgICAgICAgICAgcG9zaXRpb24gPSAidG9wbGVmdCIpICU+JQogIGFkZENvbnRyb2woIlNldmVyZSBCdWlsZGluZyBGaXJlcyBpbiBOWUMgKDIwMTMtMjAxOCkiLCBwb3NpdGlvbiA9ICJ0b3ByaWdodCIpCgptMgpgYGAKCiMjIyBiKSBDbHVzdGVyCgoqQWRkIG1hcmtlciBjbHVzdGVyaW5nLCBzbyB0aGF0IHpvb21pbmcgaW4gd2lsbCByZXZlYWwgdGhlIGluZGl2aWR1YWwgbG9jYXRpb25zIGJ1dCB0aGUgem9vbWVkIG91dCBtYXAgb25seSBzaG93cyB0aGUgY2x1c3RlcnMuIFNob3cgdGhlIG1hcCB3aXRoIGNsdXN0ZXJzLioKCmBgYHtyfQptMyA8LSBtMiAlPiUgYWRkQ2lyY2xlTWFya2VycyhsbmcgPSB+bG9uLCBsYXQgPSB+bGF0LCBjb2xvciA9IGNvbG9yX3VzZSwgcG9wdXAgPSBjb250ZW50MiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJPcHRpb25zID0gbWFya2VyQ2x1c3Rlck9wdGlvbnMoKSkKCm0zCmBgYAoKCiMjIDMuIEZpcmUgSG91c2VzCgoqVGhlIHNlY29uZCBkYXRhIGZpbGUgY29udGFpbnMgdGhlIGxvY2F0aW9ucyBvZiB0aGUgMjE4IGZpcmVob3VzZXMgaW4gTmV3IFlvcmsgQ2l0eS4gU3RhcnQgd2l0aCB0aGUgbm9uLWNsdXN0ZXJlZCBtYXAgKDJiKSBhbmQgbm93IGFkanVzdCB0aGUgc2l6ZSBvZiB0aGUgY2lyY2xlIG1hcmtlcnMgYnkgc2V2ZXJpdHkgKFRPVEFMX0lOQ0lERU5UX0RVUkFUSU9OIG9yIFVOSVRTX09OU0NFTkUgc2VlbSBwbGF1c2libGUgb3B0aW9ucykuIE1vcmUgc2V2ZXJlIGluY2lkZW50cyBzaG91bGQgaGF2ZSBsYXJnZXIgY2lyY2xlcyBvbiB0aGUgbWFwLiBPbiB0aGUgbWFwLCBhbHNvIGFkZCB0aGUgbG9jYXRpb25zIG9mIHRoZSBmaXJlIGhvdXNlcy4gQWRkIHR3byBsYXllcnMgKOKAnEluY2lkZW50c+KAnSwg4oCcRmlyZWhvdXNlc+KAnSkgdGhhdCBhbGxvdyB0aGUgdXNlciB0byBzZWxlY3Qgd2hpY2ggaW5mb3JtYXRpb24gdG8gc2hvdy4qCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFfQpmaXJlaG91c2VzIDwtIHJlYWRfY3N2KCJGRE5ZX0ZpcmVob3VzZV9MaXN0aW5nLmNzdiIpICU+JQogIGZpbHRlcighaXMubmEoTGF0aXR1ZGUpKQpgYGAKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9Cm00IDwtIGxlYWZsZXQoKSAlPiUKICBhZGRUaWxlcygnaHR0cHM6Ly97c30uYmFzZW1hcHMuY2FydG9jZG4uY29tL2xpZ2h0X2FsbC97en0ve3h9L3t5fXtyfS5wbmcnLCAKICAgICAgICAgICBhdHRyaWJ1dGlvbiA9ICJTb3VyY2U6IE5ZQyBPcGVuIERhdGEiKSAlPiUKICBhZGRDaXJjbGVNYXJrZXJzKGRhdGEgPSBzZXZlcmVfYnVpbGRpbmdfZmlyZXNfMiwgbG5nID0gfmxvbiwgbGF0ID0gfmxhdCwgY29sb3IgPSBjb2xvcl91c2UsIHBvcHVwID0gY29udGVudDIsIAogICAgICAgICAgICAgICAgICAgcmFkaXVzID0gKHNldmVyZV9idWlsZGluZ19maXJlc18yJFVOSVRTX09OU0NFTkUvNSksIGdyb3VwID0gIkluY2lkZW50cyIpICU+JQogIGFkZExlZ2VuZChkYXRhID0gc2V2ZXJlX2J1aWxkaW5nX2ZpcmVzXzIsIHBhbCA9IHBhbCwgdmFsdWVzID0gc2V2ZXJlX2J1aWxkaW5nX2ZpcmVzXzIkYmxkZ191c2UsIAogICAgICAgICAgICB0aXRsZSA9ICJCdWlsZGluZyB1c2UiLCBncm91cCA9ICJJbmNpZGVudHMiLCBwb3NpdGlvbiA9ICJ0b3BsZWZ0IikgJT4lCiAgYWRkQ2lyY2xlcyhkYXRhID0gZmlyZWhvdXNlcywgY29sb3IgPSAiZ3JheSIsIHBvcHVwID0gfnBhc3RlKCJOYW1lOiAiLCBGYWNpbGl0eU5hbWUsICI8YnI+QWRkcmVzczogIiwgCiAgICAgICAgICAgICBGYWNpbGl0eUFkZHJlc3MpLCBncm91cCA9ICJGaXJlaG91c2VzIikgJT4lCiAgYWRkQ29udHJvbCgiU2V2ZXJlIEJ1aWxkaW5nIEZpcmVzIGluIE5ZQyAyMDEzLTIwMTgiLCBwb3NpdGlvbiA9ICJ0b3ByaWdodCIpICU+JQogIGFkZExheWVyc0NvbnRyb2wob3ZlcmxheUdyb3VwcyA9IGMoIkluY2lkZW50cyIsIkZpcmVob3VzZXMiKSwgCiAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gbGF5ZXJzQ29udHJvbE9wdGlvbnMoY29sbGFwc2VkID0gRkFMU0UpKQoKbTQKYGBgCgojIyA0LiBEaXN0YW5jZSBmcm9tIEZpcmVob3VzZSBhbmQgUmVzcG9uc2UgVGltZQoKKldlIG5vdyB3YW50IHRvIGludmVzdGlnYXRlIHdoZXRoZXIgdGhlIGRpc3RhbmNlIG9mIHRoZSBpbmNpZGVudCBmcm9tIHRoZSBuZWFyZXN0IGZpcmVob3VzZSB2YXJpZXMgYWNyb3NzIHRoZSBjaXR5LioKCiMjIyBhKSBDYWxjdWxhdGUgRGlzdGFuY2UKCipGb3IgYWxsIGluY2lkZW50IGxvY2F0aW9ucyAoaW5kZXBlbmRlbnQgb2Ygc2V2ZXJpdHkpLCBpZGVudGlmeSB0aGUgbmVhcmVzdCBmaXJlaG91c2UgYW5kIGNhbGN1bGF0ZSB0aGUgZGlzdGFuY2UgYmV0d2VlbiB0aGUgZmlyZWhvdXNlIGFuZCB0aGUgaW5jaWRlbnQgbG9jYXRpb24uIFByb3ZpZGUgYSBzY2F0dGVyIHBsb3Qgc2hvd2luZyB0aGUgdGltZSB1bnRpbCB0aGUgZmlyc3QgZW5naW5lIGFycml2ZWQgKHRoZSB2YXJpYWJsZXMgSU5DSURFTlRfREFURV9USU1FIGFuZCBBUlJJVkFMX0RBVEVfVElNRSkgd2lsbCBiZSBoZWxwZnVsLioKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0V9CmJ1aWxkaW5nX2ZpcmVzIDwtIHJlYWRfY3N2KCJidWlsZGluZ19maXJlcy5jc3YiKQoKaG91c2VzLnh5IDwtIHNlbGVjdChmaXJlaG91c2VzLCBMb25naXR1ZGUsIExhdGl0dWRlKQoKZmlyZXMueHkgPC0gc2VsZWN0KGJ1aWxkaW5nX2ZpcmVzLCBsb24sIGxhdCkKYGBgCgpgYGB7cn0KZGlzdF9tYXRyaXggPC0gZGlzdG0oZmlyZXMueHksIGhvdXNlcy54eSwgZnVuID0gZGlzdEdlbykKCmRpc3RfbmVhcmVzdCA8LSBhcHBseShkaXN0X21hdHJpeCwgMSwgbWluKQpgYGAKCmBgYHtyfQpidWlsZGluZ19maXJlc18yIDwtIGJ1aWxkaW5nX2ZpcmVzCgpidWlsZGluZ19maXJlc18yWydkaXN0X25lYXJlc3QnXSA8LSBkaXN0X25lYXJlc3QKYGBgCgpgYGB7cn0KYnVpbGRpbmdfZmlyZXNfMyA8LSBidWlsZGluZ19maXJlc18yICU+JQogIGZpbHRlcihJTV9JTkNJREVOVF9LRVkgIT0gNTk1MzM1MDQpICU+JSAgIyBJbmNpZGVudCBsb2NhdGVkIG91dHNpZGUgTllDCiAgZmlsdGVyKElNX0lOQ0lERU5UX0tFWSAhPSA1NzcwMzM4MikgJT4lICAjIFJlc3BvbnNlIHRpbWUgaXMgbmVnYXRpdmUKICBtdXRhdGUoZGlzdF9uZWFyZXN0X21pbGVzID0gKGRpc3RfbmVhcmVzdCAqIDAuMDAwNjIxMzcxMikpICU+JQogIG11dGF0ZShkaXN0X25lYXJlc3RfZmVldCA9IChkaXN0X25lYXJlc3QgKiAzLjI4MDg0KSkgJT4lCiAgbXV0YXRlKGNhbGxfdGltZSA9IG1keV9obXMoSU5DSURFTlRfREFURV9USU1FKSkgJT4lCiAgbXV0YXRlKGFycml2YWxfdGltZSA9IG1keV9obXMoQVJSSVZBTF9EQVRFX1RJTUUpKSAlPiUKICBtdXRhdGUoY2FsbF90aW1lXzIgPSBhcy5QT1NJWGN0KGNhbGxfdGltZSwgdHogPSBTeXMudGltZXpvbmUoKSkpICU+JQogIG11dGF0ZShhcnJpdmFsX3RpbWVfMiA9IGFzLlBPU0lYY3QoYXJyaXZhbF90aW1lLCB0eiA9IFN5cy50aW1lem9uZSgpKSkgJT4lCiAgbXV0YXRlKG1pbnNfdG9fcmVzcCA9IGRpZmZ0aW1lKGFycml2YWxfdGltZV8yLCBjYWxsX3RpbWVfMiwgdW5pdHMgPSAibWlucyIpKSU+JQogIG11dGF0ZShtaW5zX3RvX3Jlc3BfMiA9IGFzLmRvdWJsZShtaW5zX3RvX3Jlc3ApKSAlPiUKICBtdXRhdGUobWluc190b19yZXNwXzMgPSBzcHJpbnRmKCIlMC4xZiIsIG1pbnNfdG9fcmVzcF8yKSkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnAxIDwtIGdncGxvdChkYXRhID0gYnVpbGRpbmdfZmlyZXNfMywgYWVzKHggPSBkaXN0X25lYXJlc3RfbWlsZXMsIHkgPSBtaW5zX3RvX3Jlc3BfMikpICsKICBnZW9tX3BvaW50KCkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAzKSwgeWxpbSA9IGMoMCwgMjUpKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIGx3ZCA9IDEuNSwgY29sb3IgPSAiYnJvd240IikgKwogIHRoZW1lKGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoKSwgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgbGFicyhjYXB0aW9uID0gIlNvdXJjZTogTllDIE9wZW4gRGF0YSAyMDEzLTIwMTgiKSArCiAgeGxhYigiTWlsZXMgdG8gbmVhcmVzdCBmaXJlaG91c2UiKSArCiAgeWxhYigiTWludXRlcyB0byByZXNwb25kIikgKwogIGdndGl0bGUoIlJlc3BvbnNlIHRpbWUgZm9yIGJ1aWxkaW5nIGZpcmVzIGluIE5ZQyIpCgpwMQpgYGAKCipOb3cgYWxzbyB2aXN1YWxpemUgdGhlIHBhdHRlcm5zIHNlcGFyYXRlbHkgZm9yIHNldmVyZSBhbmQgbm9uLXNldmVyZSBpbmNpZGVudHMgKHVzZSBISUdIRVNUX0xFVkVMX0RFU0MgYnV0IGZlZWwgZnJlZSB0byByZWR1Y2UgdGhlIG51bWJlciBvZiBjYXRlZ29yaWVzKS4gV2hhdCBkbyB5b3UgZmluZD8qCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpidWlsZGluZ19maXJlc18zICU+JQogIGdyb3VwX2J5KEhJR0hFU1RfTEVWRUxfREVTQykgJT4lIAogIHRhbGx5KHNvcnQgPSBUUlVFKQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmJ1aWxkaW5nX2ZpcmVzXzMgJT4lCiAgZ3JvdXBfYnkoUFJPUEVSVFlfVVNFX0RFU0MpICU+JSAKICB0YWxseShzb3J0ID0gVFJVRSkKYGBgCgpgYGB7cn0KYnVpbGRpbmdfZmlyZXNfNCA8LSBidWlsZGluZ19maXJlc18zICU+JQogIG11dGF0ZShhbGFybV9sZXZlbCA9IGNhc2Vfd2hlbigKICAgIEhJR0hFU1RfTEVWRUxfREVTQyA9PSAiMCAtIEluaXRpYWwgYWxhcm0iICAgICAgfiAiSW5pdGlhbCIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjEgLSBNb3JlIHRoYW4gaW5pdGlhbCBhbGFybSwgbGVzcyB0aGFuIFNpZ25hbCA3LTUiIH4gIjFzdCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjExIC0gRmlyc3QgQWxhcm0iICAgICAgIH4gIjFzdCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjcgLSBTaWduYWwgNy01IiAgICAgICAgIH4gIkFsbCBoYW5kcyIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjc1IC0gQWxsIEhhbmRzIFdvcmtpbmciIH4gIkFsbCBoYW5kcyIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjIgLSAybmQgYWxhcm0iICAgICAgICAgIH4gIjJuZCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjIyIC0gU2Vjb25kIEFsYXJtIiAgICAgIH4gIjJuZCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjMgLSAzcmQgYWxhcm0iICAgICAgICAgIH4gIjNyZCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjMzIC0gVGhpcmQgQWxhcm0iICAgICAgIH4gIjNyZCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjQgLSA0dGggYWxhcm0iICAgICAgICAgIH4gIjR0aCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjQ0IC0gRm91cnRoIEFsYXJtIiAgICAgIH4gIjR0aCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjUgLSA1dGggYWxhcm0iICAgICAgICAgIH4gIjV0aCBhbGFybSIsCiAgICBISUdIRVNUX0xFVkVMX0RFU0MgPT0gIjU1IC0gRmlmdGggQWxhcm0iICAgICAgIH4gIjV0aCBhbGFybSIpKSAlPiUKICBtdXRhdGUoYWxhcm1fbGV2ZWwgPSBmYWN0b3IoYWxhcm1fbGV2ZWwsIGxldmVscyA9IGMoIkluaXRpYWwiLCAiMXN0IGFsYXJtIiwgIkFsbCBoYW5kcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMm5kIGFsYXJtIiwgIjNyZCBhbGFybSIsICI0dGggYWxhcm0iLCAiNXRoIGFsYXJtIikpKSAlPiUKICBkcm9wX25hKGFsYXJtX2xldmVsKSAlPiUKICBtdXRhdGUoYWxhcm1fY2F0ID0gY2FzZV93aGVuKAogICAgYWxhcm1fbGV2ZWwgPT0gIkluaXRpYWwiICAgIH4gIkluaXRpYWwgYW5kIDFzdCBhbGFybSIsCiAgICBhbGFybV9sZXZlbCA9PSAiMXN0IGFsYXJtIiAgfiAiSW5pdGlhbCBhbmQgMXN0IGFsYXJtIiwKICAgIGFsYXJtX2xldmVsID09ICJBbGwgaGFuZHMiICB+ICJBbGwgaGFuZHMiLAogICAgYWxhcm1fbGV2ZWwgPT0gIjJuZCBhbGFybSIgIH4gIjJuZCBhbmQgM3JkIGFsYXJtIiwKICAgIGFsYXJtX2xldmVsID09ICIzcmQgYWxhcm0iICB+ICIybmQgYW5kIDNyZCBhbGFybSIsCiAgICBhbGFybV9sZXZlbCA9PSAiNHRoIGFsYXJtIiAgfiAiNHRoIGFuZCA1dGggYWxhcm0iLAogICAgYWxhcm1fbGV2ZWwgPT0gIjV0aCBhbGFybSIgIH4gIjR0aCBhbmQgNXRoIGFsYXJtIikpICU+JQogIG11dGF0ZShhbGFybV9jYXQgPSBmYWN0b3IoYWxhcm1fY2F0LCBsZXZlbHMgPSBjKCJJbml0aWFsIGFuZCAxc3QgYWxhcm0iLCJBbGwgaGFuZHMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMm5kIGFuZCAzcmQgYWxhcm0iLCAiNHRoIGFuZCA1dGggYWxhcm0iKSkpICU+JQogIG11dGF0ZShhbGFybV9zZXYgPSBjYXNlX3doZW4oCiAgICBhbGFybV9sZXZlbCA9PSAiSW5pdGlhbCIgICB+ICJMb3ciLAogICAgYWxhcm1fbGV2ZWwgPT0gIjFzdCBhbGFybSIgfiAiTG93IiwKICAgIGFsYXJtX2xldmVsID09ICJBbGwgaGFuZHMiIH4gIk1lZGl1bSIsCiAgICBhbGFybV9sZXZlbCA9PSAiMm5kIGFsYXJtIiB+ICJNZWRpdW0iLAogICAgVFJVRSAgICAgICAgICAgICAgICAgICAgICAgfiAiSGlnaCIpKSAlPiUKICBtdXRhdGUoYWxhcm1fc2V2ID0gZmFjdG9yKGFsYXJtX3NldiwgbGV2ZWxzID0gYygiTG93IiwgIk1lZGl1bSIsICJIaWdoIikpKSAlPiUKICBtdXRhdGUoZHVyYXRfaG91cnMgPSAoVE9UQUxfSU5DSURFTlRfRFVSQVRJT04vMzYwMCkpICU+JQogIG11dGF0ZShkdXJhdF9ob3VycyA9IHNwcmludGYoIiUwLjFmIiwgZHVyYXRfaG91cnMpKSAlPiUKICBtdXRhdGUoZmlyZV9zcHJlYWQgPSBjYXNlX3doZW4oCiAgICBGSVJFX1NQUkVBRF9ERVNDID09ICIxIC0gQ29uZmluZWQgdG8gb2JqZWN0IG9mIG9yaWdpbiIgICB+ICJDb25maW5lZCB0byBvYmplY3QiLAogICAgRklSRV9TUFJFQURfREVTQyA9PSAiMiAtIENvbmZpbmVkIHRvIHJvb20gb2Ygb3JpZ2luIiAgICAgfiAiQ29uZmluZWQgdG8gcm9vbSIsCiAgICBGSVJFX1NQUkVBRF9ERVNDID09ICIzIC0gQ29uZmluZWQgdG8gZmxvb3Igb2Ygb3JpZ2luIiAgICB+ICJDb25maW5lZCB0byBmbG9vciIsCiAgICBGSVJFX1NQUkVBRF9ERVNDID09ICI0IC0gQ29uZmluZWQgdG8gYnVpbGRpbmcgb2Ygb3JpZ2luIiB+ICJDb25maW5lZCB0byBidWlsZGluZyIsCiAgICBGSVJFX1NQUkVBRF9ERVNDID09ICI1IC0gQmV5b25kIGJ1aWxkaW5nIG9mIG9yaWdpbiIgICAgICB+ICJTcHJlYWQgYmV5b25kIGJ1aWxkaW5nIiwKICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gIk5BIikpICU+JSAKICBtdXRhdGUoZmlyZV9zcHJlYWQgPSBmYWN0b3IoZmlyZV9zcHJlYWQsIGxldmVscyA9IGMoIkNvbmZpbmVkIHRvIG9iamVjdCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29uZmluZWQgdG8gcm9vbSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ29uZmluZWQgdG8gZmxvb3IiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvbmZpbmVkIHRvIGJ1aWxkaW5nIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTcHJlYWQgYmV5b25kIGJ1aWxkaW5nIikpKSAlPiUKICBtdXRhdGUoYmxkZ191c2UgPSBjYXNlX3doZW4oCiAgICBQUk9QRVJUWV9VU0VfREVTQyA9PSAiNDI5IC0gTXVsdGlmYW1pbHkgZHdlbGxpbmciICAgICAgICB+ICJBcGFydG1lbnQgb3IgY29uZG8iLAogICAgUFJPUEVSVFlfVVNFX0RFU0MgPT0gIjQxOSAtIDEgb3IgMiBmYW1pbHkgZHdlbGxpbmciICAgICAgfiAiSG91c2UiLAogICAgUFJPUEVSVFlfVVNFX0RFU0MgPT0gIjUwMCAtIE1lcmNhbnRpbGUsIGJ1c2luZXNzLCBvdGhlciIgfiAiQnVzaW5lc3MiLAogICAgUFJPUEVSVFlfVVNFX0RFU0MgPT0gIjE2MSAtIFJlc3RhdXJhbnQgb3IgY2FmZXRlcmlhIiAgICAgfiAiUmVzdGF1cmFudCIsCiAgICBUUlVFICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB+ICJPdGhlciIpKSAlPiUKICBtdXRhdGUoYmxkZ191c2UgPSBmYWN0b3IoYmxkZ191c2UsIGxldmVscyA9IGMoIkFwYXJ0bWVudCBvciBjb25kbyIsICJIb3VzZSIsICJCdXNpbmVzcyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUmVzdGF1cmFudCIsICJPZmZpY2UiLCAiT3RoZXIiKSkpICU+JQogIG11dGF0ZShibGRnX2NhdCA9IGNhc2Vfd2hlbigKICAgIGJsZGdfdXNlID09ICJBcGFydG1lbnQgb3IgY29uZG8iIH4gIlJlc2lkZW50aWFsIiwKICAgIGJsZGdfdXNlID09ICJIb3VzZSIgICAgICAgICAgICAgIH4gIlJlc2lkZW50aWFsIiwKICAgIGJsZGdfdXNlID09ICJCdXNpbmVzcyIgICAgICAgICAgIH4gIkNvbW1lcmNpYWwiLAogICAgYmxkZ191c2UgPT0gIk9mZmljZSIgICAgICAgICAgICAgfiAiQ29tbWVyY2lhbCIsCiAgICBibGRnX3VzZSA9PSAiUmVzdGF1cmFudCIgICAgICAgICB+ICJDb21tZXJjaWFsIiwKICAgIFRSVUUgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH4gIk90aGVyIikpICU+JQogIG11dGF0ZShibGRnX2NhdCA9IGZhY3RvcihibGRnX2NhdCwgbGV2ZWxzID0gYygiUmVzaWRlbnRpYWwiLCAiQ29tbWVyY2lhbCIsICJPdGhlciIpKSkKYGBgCgpgYGB7ciwgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CnAyIDwtIGdncGxvdChkYXRhID0gYnVpbGRpbmdfZmlyZXNfNCwgYWVzKHggPSBkaXN0X25lYXJlc3RfbWlsZXMsIHkgPSBtaW5zX3RvX3Jlc3BfMikpICsKICBnZW9tX3BvaW50KCkgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltID0gYygwLCAzKSwgeWxpbSA9IGMoMCwgMjUpKSArCiAgdGhlbWVfZml2ZXRoaXJ0eWVpZ2h0KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UsIGx3ZCA9IDEsIGNvbG9yID0gImJyb3duNCIpICsKICB0aGVtZShheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KCksIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnMoY2FwdGlvbiA9ICJTb3VyY2U6IE5ZQyBPcGVuIERhdGEgMjAxMy0yMDE4IikgKwogIHhsYWIoIk1pbGVzIHRvIG5lYXJlc3QgZmlyZWhvdXNlIikgKwogIHlsYWIoIk1pbnV0ZXMgdG8gcmVzcG9uZCIpICsKICBnZ3RpdGxlKCJSZXNwb25zZSB0aW1lIGZvciBidWlsZGluZyBmaXJlcyBpbiBOWUMgXG5ieSBhbGFybSBsZXZlbCIpICsKICBmYWNldF93cmFwKH4gYWxhcm1fY2F0KQoKcDIKYGBgCgoKIyMjIGIpIE1hcCBvZiBSZXNwb25zZSBUaW1lcwoKKlByb3ZpZGUgYSBtYXAgdmlzdWFsaXphdGlvbiBvZiByZXNwb25zZSB0aW1lcy4gSW52ZXN0aWdhdGUgd2hldGhlciB0aGUgdHlwZSBvZiBwcm9wZXJ0eSBhZmZlY3RlZCAoUFJPUEVSVFlfVVNFX0RFU0MpIG9yIGZpcmUgc2V2ZXJpdHkgKEhJR0hFU1RfTEVWRUxfREVTQykgcGxheSBhIHJvbGUgaGVyZS4qCgpgYGB7cn0KcGFsLjEgPC0gY29sb3JCaW4ocGFsZXR0ZSA9ICJZbE9yUmQiLCBkb21haW4gPSBidWlsZGluZ19maXJlc180JG1pbnNfdG9fcmVzcF8yLCBiaW5zID0gNSwgCiAgICAgICAgICAgICAgICAgIG5hLmNvbG9yID0gTlVMTCwgcHJldHR5ID0gRkFMU0UsIGFscGhhID0gVFJVRSkKCnFwYWwgPC0gY29sb3JRdWFudGlsZSgiWWxPclJkIiwgYnVpbGRpbmdfZmlyZXNfNCRtaW5zX3RvX3Jlc3BfMiwgbiA9IDUpCgpjb250ZW50MyA8LSBwYXN0ZSgiQnVpbGRpbmcgdXNlOiIsIGJ1aWxkaW5nX2ZpcmVzXzQkYmxkZ191c2UsICI8YnIvPiIsCiAgICAgICAgICAgICAgICAgICJIaWdoZXN0IGFsYXJtOiIsIGJ1aWxkaW5nX2ZpcmVzXzQkYWxhcm1fbGV2ZWwsICI8YnIvPiIsCiAgICAgICAgICAgICAgICAgICJVbml0cyByZXNwb25kaW5nOiIsIGJ1aWxkaW5nX2ZpcmVzXzQkVU5JVFNfT05TQ0VORSwgIjxici8+IiwKICAgICAgICAgICAgICAgICAgIk1pbnV0ZXMgdG8gcmVzcG9uZDoiLCBidWlsZGluZ19maXJlc180JG1pbnNfdG9fcmVzcF8zLCAiPGJyLz4iLAogICAgICAgICAgICAgICAgICAiSG91cnMgdG8gY29udGFpbjoiLCBidWlsZGluZ19maXJlc180JGR1cmF0X2hvdXJzLCI8YnIvPiIsCiAgICAgICAgICAgICAgICAgICJGaXJlIHNwcmVhZDoiLCBidWlsZGluZ19maXJlc180JGZpcmVfc3ByZWFkLCI8YnIvPiIpCmBgYAoKYGBge3J9Cm01IDwtIGxlYWZsZXQoYnVpbGRpbmdfZmlyZXNfNCkgJT4lCiAgYWRkVGlsZXModXJsVGVtcGxhdGUgPSAnaHR0cHM6Ly97c30uYmFzZW1hcHMuY2FydG9jZG4uY29tL2xpZ2h0X2FsbC97en0ve3h9L3t5fXtyfS5wbmcnLCAKICAgICAgICAgICBhdHRyaWJ1dGlvbiA9ICJTb3VyY2U6IE5ZQyBPcGVuIERhdGEgMjAxMy0yMDE4IikgJT4lCiAgYWRkQ2lyY2xlcyhkYXRhID0gc3Vic2V0KGJ1aWxkaW5nX2ZpcmVzXzQsIGJ1aWxkaW5nX2ZpcmVzXzQkYWxhcm1fc2V2ID09ICJMb3ciKSwgbG5nID0gfmxvbiwgbGF0ID0gfmxhdCwgCiAgICAgICAgICAgICBjb2xvciA9IH5xcGFsKG1pbnNfdG9fcmVzcF8yKSwgcG9wdXAgPSBjb250ZW50MywgZ3JvdXAgPSAiTG93IHNldmVyaXR5IikgJT4lCiAgYWRkQ2lyY2xlcyhkYXRhID0gc3Vic2V0KGJ1aWxkaW5nX2ZpcmVzXzQsIGJ1aWxkaW5nX2ZpcmVzXzQkYWxhcm1fc2V2ID09ICJNZWRpdW0iKSwgbG5nID0gfmxvbiwgbGF0ID0gfmxhdCwgCiAgICAgICAgICAgICBjb2xvciA9IH5xcGFsKG1pbnNfdG9fcmVzcF8yKSwgcG9wdXAgPSBjb250ZW50MywgZ3JvdXAgPSAiTWVkaXVtIHNldmVyaXR5IikgJT4lCiAgYWRkQ2lyY2xlcyhkYXRhID0gc3Vic2V0KGJ1aWxkaW5nX2ZpcmVzXzQsIGJ1aWxkaW5nX2ZpcmVzXzQkYWxhcm1fc2V2ID09ICJIaWdoIiksIGxuZyA9IH5sb24sIGxhdCA9IH5sYXQsIAogICAgICAgICAgICAgY29sb3IgPSB+cXBhbChtaW5zX3RvX3Jlc3BfMiksIHBvcHVwID0gY29udGVudDMsIGdyb3VwID0gIkhpZ2ggc2V2ZXJpdHkiKSAlPiUKICBhZGRMZWdlbmQocGFsID0gcGFsLjEsIHZhbHVlcyAgPSBidWlsZGluZ19maXJlc180JG1pbnNfdG9fcmVzcF8yLCBwb3NpdGlvbiA9ICJ0b3BsZWZ0IiwgCiAgICAgICAgICAgIHRpdGxlID0gIk1pbnV0ZXMgdG8gcmVzcG9uZCIsIGxhYkZvcm1hdCA9IGxhYmVsRm9ybWF0KGRpZ2l0cyA9IDApKSAlPiUKICBhZGRDb250cm9sKCJGaXJlIFJlc3BvbnNlIFRpbWUgYnkgQWxhcm0gTGV2ZWwiLCBwb3NpdGlvbiA9ICJ0b3ByaWdodCIpICU+JQogIGFkZExheWVyc0NvbnRyb2wob3ZlcmxheUdyb3VwcyA9IGMoIkxvdyBzZXZlcml0eSIsICJNZWRpdW0gc2V2ZXJpdHkiLCAiSGlnaCBzZXZlcml0eSIpLCAKICAgICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkpCgptNQpgYGAKCmBgYHtyfQptNiA8LSBsZWFmbGV0KGJ1aWxkaW5nX2ZpcmVzXzQpICU+JQogIGFkZFRpbGVzKHVybFRlbXBsYXRlID0gJ2h0dHBzOi8ve3N9LmJhc2VtYXBzLmNhcnRvY2RuLmNvbS9saWdodF9hbGwve3p9L3t4fS97eX17cn0ucG5nJywgCiAgICAgICAgICAgYXR0cmlidXRpb24gPSAiU291cmNlOiBOWUMgT3BlbiBEYXRhIDIwMTMtMjAxOCIpICU+JQogIGFkZENpcmNsZXMoZGF0YSA9IHN1YnNldChidWlsZGluZ19maXJlc180LCBidWlsZGluZ19maXJlc180JGJsZGdfY2F0ID09ICJSZXNpZGVudGlhbCIpLCBsbmcgPSB+bG9uLCBsYXQgPSB+bGF0LCAKICAgICAgICAgICAgIGNvbG9yID0gfnFwYWwobWluc190b19yZXNwXzIpLCBwb3B1cCA9IGNvbnRlbnQzLCBncm91cCA9ICJSZXNpZGVudGlhbCIpICU+JQogIGFkZENpcmNsZXMoZGF0YSA9IHN1YnNldChidWlsZGluZ19maXJlc180LCBidWlsZGluZ19maXJlc180JGJsZGdfY2F0ID09ICJDb21tZXJjaWFsIiksIGxuZyA9IH5sb24sIGxhdCA9IH5sYXQsIAogICAgICAgICAgICAgY29sb3IgPSB+cXBhbChtaW5zX3RvX3Jlc3BfMiksIHBvcHVwID0gY29udGVudDMsIGdyb3VwID0gIkNvbW1lcmNpYWwiKSAlPiUKICBhZGRDaXJjbGVzKGRhdGEgPSBzdWJzZXQoYnVpbGRpbmdfZmlyZXNfNCwgYnVpbGRpbmdfZmlyZXNfNCRibGRnX2NhdCA9PSAiT3RoZXIiKSwgbG5nID0gfmxvbiwgbGF0ID0gfmxhdCwgCiAgICAgICAgICAgICBjb2xvciA9IH5xcGFsKG1pbnNfdG9fcmVzcF8yKSwgcG9wdXAgPSBjb250ZW50MywgZ3JvdXAgPSAiT3RoZXIiKSAlPiUKICBhZGRMZWdlbmQocGFsID0gcGFsLjEsIHZhbHVlcyAgPSBidWlsZGluZ19maXJlc180JG1pbnNfdG9fcmVzcF8yLCBwb3NpdGlvbiA9ICJ0b3BsZWZ0IiwgCiAgICAgICAgICAgIHRpdGxlID0gIk1pbnV0ZXMgdG8gcmVzcG9uZCIsIGxhYkZvcm1hdCA9IGxhYmVsRm9ybWF0KGRpZ2l0cyA9IDApKSAlPiUKICBhZGRDb250cm9sKCJGaXJlIFJlc3BvbnNlIFRpbWUgYnkgQnVpbGRpbmcgVHlwZSIsIHBvc2l0aW9uID0gInRvcHJpZ2h0IikgJT4lCiAgYWRkTGF5ZXJzQ29udHJvbChvdmVybGF5R3JvdXBzID0gYygiUmVzaWRlbnRpYWwiLCAiQ29tbWVyY2lhbCIsICJPdGhlciIpLCAKICAgICAgICAgICAgICAgICAgIG9wdGlvbnMgPSBsYXllcnNDb250cm9sT3B0aW9ucyhjb2xsYXBzZWQgPSBGQUxTRSkpCgptNgpgYGAKCgoqU2hvdyBhIGZhY2V0ZWQgY2hvcm9wbGV0aCBtYXAgaW5kaWNhdGluZyBob3cgcmVzcG9uc2UgdGltZXMgaGF2ZSBkZXZlbG9wZWQgb3ZlciB0aGUgeWVhcnMuIFdoYXQgZG8geW91IGZpbmQ/KgoKYGBge3J9CnJlbmFtZV9ib3JvcyA8LSBmdW5jdGlvbih4KXtnc3ViKCJeWzAtOV0gLSAiLCAiIiwgeCl9Cgpib3JvX25hbWUgPC0gdW5saXN0KGxhcHBseShidWlsZGluZ19maXJlc180JEJPUk9VR0hfREVTQywgcmVuYW1lX2Jvcm9zKSkKCmJ1aWxkaW5nX2ZpcmVzXzUgPC0gYnVpbGRpbmdfZmlyZXNfNAoKYnVpbGRpbmdfZmlyZXNfNVsnYm9yb19uYW1lJ10gPC0gYm9yb19uYW1lCmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KYXZnX2Jvcm9fcmVzcF90aW1lIDwtIGJ1aWxkaW5nX2ZpcmVzXzUgJT4lCiAgZ3JvdXBfYnkoYXMuZmFjdG9yKGJvcm9fbmFtZSkpICU+JQogIHN1bW1hcmlzZShhdmdfYm9yb19yZXNwX3RpbWUgPSBtZWFuKG1pbnNfdG9fcmVzcF8yLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBzZWxlY3QoYXZnX2Jvcm9fcmVzcF90aW1lKQoKZmFjdG9yKGF2Z19ib3JvX3Jlc3BfdGltZSwgbGV2ZWxzPSBjKCIzLjg0IiwgIjMuMTQiLCAiMy44NyIsICIzLjc2IiwgIjMuODAiKSkKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpueWNfYm9yb3MgPC0gcmVhZE9HUigiYm9yb3VnaF9ib3VuZGFyaWVzLmdlb2pzb24iKQoKbnljX2Jvcm9zQGRhdGFbJ2F2Z19ib3JvX3Jlc3BfdGltZSddIDwtIGF2Z19ib3JvX3Jlc3BfdGltZQpgYGAKCmBgYHtyfQpwYWwuMiA8LSBjb2xvck51bWVyaWMoIllsT3JSZCIsIE5VTEwpCgptNyA8LSBsZWFmbGV0KG55Y19ib3JvcykgJT4lCiAgYWRkVGlsZXModXJsVGVtcGxhdGUgPSAnaHR0cHM6Ly97c30uYmFzZW1hcHMuY2FydG9jZG4uY29tL2xpZ2h0X2FsbC97en0ve3h9L3t5fXtyfS5wbmcnLCAKICAgICAgICAgICBhdHRyaWJ1dGlvbiA9ICJTb3VyY2U6IE5ZQyBPcGVuIERhdGEgMjAxMy0yMDE4IikgJT4lCiAgYWRkUG9seWdvbnMoc3Ryb2tlID0gRkFMU0UsIHNtb290aEZhY3RvciA9IDAuMywgZmlsbE9wYWNpdHkgPSAxLAogICAgZmlsbENvbG9yID0gfnBhbC4yKGF2Z19ib3JvX3Jlc3BfdGltZSkpICU+JQogIGFkZExlZ2VuZChwYWwgPSBwYWwuMiwgdmFsdWVzICA9IH5hdmdfYm9yb19yZXNwX3RpbWUsIHBvc2l0aW9uID0gInRvcGxlZnQiLCAKICAgICAgICAgICAgdGl0bGUgPSAiTWludXRlcyB0byByZXNwb25kIiwgbGFiRm9ybWF0ID0gbGFiZWxGb3JtYXQoZGlnaXRzID0gMikpICU+JQogIGFkZENvbnRyb2woIkF2ZXJhZ2UgRmlyZSBSZXNwb25zZSBUaW1lICgyMDEzLTIwMTgpIiwgcG9zaXRpb24gPSAidG9wcmlnaHQiKSAKCm03CmBgYAoKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmJvcm9fcmVzcF90aW1lXzIwMTMgPC0gYnVpbGRpbmdfZmlyZXNfNSAlPiUKICBtdXRhdGUoeWVhciA9IHllYXIoY2FsbF90aW1lKSkgJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAxMykgJT4lCiAgZ3JvdXBfYnkoYXMuZmFjdG9yKGJvcm9fbmFtZSkpICU+JQogIHN1bW1hcmlzZShib3JvX3Jlc3BfdGltZV8yMDEzID0gbWVhbihtaW5zX3RvX3Jlc3BfMiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgc2VsZWN0KGJvcm9fcmVzcF90aW1lXzIwMTMpCgpmYWN0b3IoYm9yb19yZXNwX3RpbWVfMjAxMywgbGV2ZWxzPSBjKCIzLjc4IiwgIjMuMTEiLCAiMy44MiIsICIzLjcxIiwgIjMuODYiKSkKCm55Y19ib3Jvc0BkYXRhWydib3JvX3Jlc3BfdGltZV8yMDEzJ10gPC0gYm9yb19yZXNwX3RpbWVfMjAxMwpgYGAKCmBgYHtyfQptOCA8LSBsZWFmbGV0KG55Y19ib3JvcykgJT4lCiAgYWRkVGlsZXModXJsVGVtcGxhdGUgPSAnaHR0cHM6Ly97c30uYmFzZW1hcHMuY2FydG9jZG4uY29tL2xpZ2h0X2FsbC97en0ve3h9L3t5fXtyfS5wbmcnLCAKICAgICAgICAgICBhdHRyaWJ1dGlvbiA9ICJTb3VyY2U6IE5ZQyBPcGVuIERhdGEgMjAxMy0yMDE4IikgJT4lCiAgYWRkUG9seWdvbnMoc3Ryb2tlID0gRkFMU0UsIHNtb290aEZhY3RvciA9IDAuMywgZmlsbE9wYWNpdHkgPSAxLAogICAgZmlsbENvbG9yID0gfnBhbC4yKGJvcm9fcmVzcF90aW1lXzIwMTMpKSAlPiUKICBhZGRMZWdlbmQocGFsID0gcGFsLjIsIHZhbHVlcyA9IH5ib3JvX3Jlc3BfdGltZV8yMDEzLCBwb3NpdGlvbiA9ICJ0b3BsZWZ0IiwgCiAgICAgICAgICAgIHRpdGxlID0gIk1pbnV0ZXMgdG8gcmVzcG9uZCIsIGxhYkZvcm1hdCA9IGxhYmVsRm9ybWF0KGRpZ2l0cyA9IDIpKSAlPiUKICBhZGRDb250cm9sKCJBdmVyYWdlIEZpcmUgUmVzcG9uc2UgVGltZSBpbiAyMDEzIiwgcG9zaXRpb24gPSAidG9wcmlnaHQiKQoKbTgKYGBgCgoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KYm9yb19yZXNwX3RpbWVfMjAxOCA8LSBidWlsZGluZ19maXJlc181ICU+JQogIG11dGF0ZSh5ZWFyID0geWVhcihjYWxsX3RpbWUpKSAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDE4KSAlPiUKICBncm91cF9ieShhcy5mYWN0b3IoYm9yb19uYW1lKSkgJT4lCiAgc3VtbWFyaXNlKGJvcm9fcmVzcF90aW1lXzIwMTggPSBtZWFuKG1pbnNfdG9fcmVzcF8yLCBuYS5ybSA9IFRSVUUpKSAlPiUKICBzZWxlY3QoYm9yb19yZXNwX3RpbWVfMjAxOCkKCmZhY3Rvcihib3JvX3Jlc3BfdGltZV8yMDE4LCBsZXZlbHM9IGMoIjQuMDciLCAiMy40MSIsICIzLjkzIiwgIjQuMTUiLCAiNC4xMiIpKQoKbnljX2Jvcm9zQGRhdGFbJ2Jvcm9fcmVzcF90aW1lXzIwMTgnXSA8LSBib3JvX3Jlc3BfdGltZV8yMDE4CmBgYAoKYGBge3J9Cm05IDwtIGxlYWZsZXQobnljX2Jvcm9zKSAlPiUKICBhZGRUaWxlcyh1cmxUZW1wbGF0ZSA9ICdodHRwczovL3tzfS5iYXNlbWFwcy5jYXJ0b2Nkbi5jb20vbGlnaHRfYWxsL3t6fS97eH0ve3l9e3J9LnBuZycsIAogICAgICAgICAgIGF0dHJpYnV0aW9uID0gIlNvdXJjZTogTllDIE9wZW4gRGF0YSAyMDEzLTIwMTgiKSAlPiUKICBhZGRQb2x5Z29ucyhzdHJva2UgPSBGQUxTRSwgc21vb3RoRmFjdG9yID0gMC4zLCBmaWxsT3BhY2l0eSA9IDEsCiAgICBmaWxsQ29sb3IgPSB+cGFsLjIoYm9yb19yZXNwX3RpbWVfMjAxOCkpICU+JQogIGFkZExlZ2VuZChwYWwgPSBwYWwuMiwgdmFsdWVzID0gfmJvcm9fcmVzcF90aW1lXzIwMTgsIHBvc2l0aW9uID0gInRvcGxlZnQiLCAKICAgICAgICAgICAgdGl0bGUgPSAiTWludXRlcyB0byByZXNwb25kIiwgbGFiRm9ybWF0ID0gbGFiZWxGb3JtYXQoZGlnaXRzID0gMikpICU+JQogIGFkZENvbnRyb2woIkF2ZXJhZ2UgRmlyZSBSZXNwb25zZSBUaW1lIGluIDIwMTgiLCBwb3NpdGlvbiA9ICJ0b3ByaWdodCIpCgptOQpgYGAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmJvcm9fcmVzcF90aW1lXzIwMTMgPSBjKCIzLjc4IiwgIjMuMTEiLCAiMy44MiIsICIzLjcxIiwgIjMuODYiKQpib3JvX3Jlc3BfdGltZV8yMDE4ID0gYygiNC4wNyIsICIzLjQxIiwgIjMuOTMiLCAiNC4xNSIsICI0LjEyIikKCmJvcm9fcmVzcF90aW1lXzIwMTMgPSBhcy5udW1lcmljKGJvcm9fcmVzcF90aW1lXzIwMTMpCmJvcm9fcmVzcF90aW1lXzIwMTggPSBhcy5udW1lcmljKGJvcm9fcmVzcF90aW1lXzIwMTgpCgpib3JvX3Jlc3BfdGltZV9hbGwgPC0gZGF0YS5mcmFtZShib3JvX3Jlc3BfdGltZV8yMDEzLGJvcm9fcmVzcF90aW1lXzIwMTgpCgpib3JvX3Jlc3BfdGltZV9kaWZmIDwtIGJvcm9fcmVzcF90aW1lX2FsbCAlPiUKICBtdXRhdGUoYm9yb19yZXNwX3RpbWVfZGlmZiA9IGJvcm9fcmVzcF90aW1lXzIwMTgtYm9yb19yZXNwX3RpbWVfMjAxMykgJT4lCiAgc2VsZWN0KGJvcm9fcmVzcF90aW1lX2RpZmYpCgpmYWN0b3IoYm9yb19yZXNwX3RpbWVfZGlmZiwgbGV2ZWxzPSBjKCIwLjI5IiwgIjAuMzAiLCAiMC4xMSIsICIwLjQ0IiwgIjAuMjYiKSkKCm55Y19ib3Jvc0BkYXRhWydib3JvX3Jlc3BfdGltZV9kaWZmJ10gPC0gYm9yb19yZXNwX3RpbWVfZGlmZgpgYGAKCmBgYHtyfQptMTAgPC0gbGVhZmxldChueWNfYm9yb3MpICU+JQogIGFkZFRpbGVzKHVybFRlbXBsYXRlID0gJ2h0dHBzOi8ve3N9LmJhc2VtYXBzLmNhcnRvY2RuLmNvbS9saWdodF9hbGwve3p9L3t4fS97eX17cn0ucG5nJywgCiAgICAgICAgICAgYXR0cmlidXRpb24gPSAiU291cmNlOiBOWUMgT3BlbiBEYXRhIDIwMTMtMjAxOCIpICU+JQogIGFkZFBvbHlnb25zKHN0cm9rZSA9IEZBTFNFLCBzbW9vdGhGYWN0b3IgPSAwLjMsIGZpbGxPcGFjaXR5ID0gMSwKICAgIGZpbGxDb2xvciA9IH5wYWwuMihib3JvX3Jlc3BfdGltZV9kaWZmKSkgJT4lCiAgYWRkTGVnZW5kKHBhbCA9IHBhbC4yLCB2YWx1ZXMgPSB+Ym9yb19yZXNwX3RpbWVfZGlmZiwgcG9zaXRpb24gPSAidG9wbGVmdCIsIAogICAgICAgICAgICB0aXRsZSA9ICJNaW51dGVzIHRvIHJlc3BvbmQiLCBsYWJGb3JtYXQgPSBsYWJlbEZvcm1hdChkaWdpdHMgPSAyKSkgJT4lCiAgYWRkQ29udHJvbCgiQ2hhbmdlIGluIEZpcmUgUmVzcG9uc2UgVGltZXMgMjAxMy0yMDE4IiwgcG9zaXRpb24gPSAidG9wcmlnaHQiKSAKCm0xMApgYGAKCkF2ZXJhZ2UgcmVzcG9uc2UgdGltZXMgaW5jcmVhc2VkIGZyb20gMjAxMyB0byAyMDE4LCB3aXRoIHRoZSBsb3dlc3QgaW5jcmVhc2UgaW4gQnJvb2tseW4gYW5kIHRoZSBoaWdoZXN0IGluY3JlYXNlIGluIFF1ZWVucy4K