About and Credits

These materials were developed by Sam Csik & Juliette Verstaen using UFO Sighting Data from the National UFO Reporting Center (NUFORC). Data can be downloaded from kaggle. Source code can be found on GitHub.

In this example report, we demonstrate how to use a variety of different HTML widgets that allow for interactive web visualizations, as well as how to leverage R Markdown and GitHub Pages for building web pages to display reports (or whatever else you might dream of creating with RMarkdown).

Expand hidden Code chunks to see how we’ve created the following tables, maps, and plots. Also, be on the lookout for TIPs and RESOURCEs throughout for pointers and links to resources!

And finally, before we get started, the R Markdown Cookbook by Yihui Xie, Christophe Dervieux, & Emily Riederer and R Markdown: The Definitive Guide by Yihui Xie, J. J. Allaire, & Garrett Grolemund contain tons of incredible information and answers to nearly all of the R Markdown-related questions we have encountered. We recommend coming back to these to learn more about all-things R Markdown.


Setting up your R Markdown file

###########################
# load packages
###########################

# for cleaning and wrangling
library(tidyverse)
library(janitor)
library(lubridate) 

# for map making and general spatial work
library(leaflet)
library(mapview)
library(sf)

# for working with time series data
library(dygraphs)
library(xts)

# for making tables
library(DT)
library(gt)

# for palette making
library(RColorBrewer)
library(colorspace)
library(scales)

# for reproducibility
library(here)

##############################
# source custom alien color palette
##############################

source(here::here("code", "palettes.R"))

###########################
# load data
###########################

UFO_data_all <- read_csv(here::here("data", "UFO_complete.csv"))

###########################
# bunch of parsing failures from columns that are shifted; we're going to just filter those out bc they don't have appropriate lat/long data
###########################

# see parsing failures here
UFO_parsing_failures <- UFO_data_all %>% 
  filter(is.na(city))

# remove those from the dataset
UFO_data_cleaned <- UFO_data_all %>% 
 filter(!is.na(city))


Alien Encounters

Timeseries (dygraph)

##############################
# get colors from our custom alien palette
##############################

colors <- alien_palette("galaxy_quest", 8, "discrete")

##############################
# wrangle data
##############################

UFO_dygraph <- UFO_data_cleaned %>%
  clean_names() %>%
  select(-date_posted) %>%
  separate(datetime, c("month", "second", "third"), sep = "/") %>%
  separate(third, c("year", "time"), sep = " ") %>%
  group_by(year) %>%
  count() %>%
  rename(number_instances = n) %>%
  filter(!is.na(year)) %>%
  mutate(date_seen = paste("01/01/", year, sep = "")) %>%
  mutate(date_seen = as.character(date_seen),
         ## needs to be in date time format/class in order to be converted into extendable time series below
         date_seen = as.Date(date_seen, format = "%m/%d/%Y"))

##############################
# create time series object
##############################

ufo_timeseries <- xts(x = UFO_dygraph$number_instances,
                              order.by = UFO_dygraph$date_seen)

##############################
# finally create the plot
##############################

dy_plot <- dygraph(ufo_timeseries, main = "Aliens from Earth") %>%
  dyRangeSelector() %>%
  dyOptions(labelsUTC = TRUE, fillGraph=TRUE, fillAlpha=0.1, drawGrid = FALSE, colors= colors[1]) %>%
  dyCrosshair(direction = "vertical") %>%
  dyHighlight(highlightCircleSize = 5, highlightSeriesBackgroundAlpha = 0.2, hideOnMouseOut = FALSE) %>%
  dyRoller(rollPeriod = 1) %>%
  dyAxis("y", label = "Number of sightings, abductions, etc")

##############################
# display plot in knitted doc
##############################

dy_plot

RESOURCE: For more information and tips on (1) creating dygraphs, check out dygraphs for R (2) xts objects check this out


Mapping duration (mapview)

##############################
# clean data
##############################

UFO_time <- UFO_data_cleaned %>%
  clean_names() %>%
  select(-date_posted) %>%
  mutate(longitude = as.numeric(longitude),
         latitude = as.numeric(latitude)) %>%
  mutate(duration_length = case_when(duration_seconds <= 60 ~ "short",
                                     duration_seconds >= 360 ~ "long",
                                     T ~ "medium")) %>%
  filter(!is.na(longitude),
         !is.na(latitude)) %>%
  group_by(duration_length, longitude, latitude) %>%
  count() %>%
  rename(number_instances = n)

##############################
# need to convert to a spatial feature object
##############################

UFO_spatial <-  st_as_sf(UFO_time, coords = c("longitude", "latitude"),
           crs = 4326)

##############################
# create the map!
##############################

UFO_map_mv <- mapview(UFO_spatial, zcol = "duration_length", cex = "number_instances", alpha = 0.2)

##############################
# display map in knitted doc
##############################

UFO_map_mv

RESOURCE: Explore Jamie Montgomery’s mapview and mapedit tutorial from SB Eco-Data Science for more mapview fun.


Spaceship shapes (gt)

##############################
# get colors from our custom alien palette
##############################

colors_table <- alien_palette("galaxy_quest", 8, "discrete")

##############################
# wrangle data (get counts by UFO shape)
##############################

UFO_shape_count <- UFO_data_cleaned %>%
  clean_names() %>%
  select(-date_posted) %>%
  mutate(longitude = as.numeric(longitude),
         latitude = as.numeric(latitude)) %>%
  group_by(shape) %>%
  count() %>%
  rename(number_instances = n) %>%
  filter(!is.na(shape)) ## There are 28 unique shapes

##############################
# wrangle data (get total time each shape was viewed)
##############################

UFO_shape_times <- UFO_data_cleaned %>%
  clean_names() %>%
  select(-date_posted) %>%
  mutate(longitude = as.numeric(longitude),
         latitude = as.numeric(latitude)) %>%
  group_by(shape) %>%
  dplyr::summarise(total_seconds = sum(duration_seconds, na.rm = TRUE)) %>%
  filter(!is.na(shape))

##############################
# combine data frames
##############################

UFO_shape <- left_join(UFO_shape_count, UFO_shape_times) %>%
  mutate(shape_rate_seconds = total_seconds/number_instances) %>%
  mutate(Hours = hour(seconds_to_period(shape_rate_seconds)),
         Minutes = minute(seconds_to_period(shape_rate_seconds)),
         Seconds = round(second(seconds_to_period(shape_rate_seconds)), digits = 0)) %>%
  unite(shape_rate, c("Hours", "Minutes", "Seconds"), sep = ":") %>%
  ungroup()

##############################
# make the `gt` table
##############################

Table1_UFO_shapes <- UFO_shape %>%
  dplyr::arrange(desc(shape_rate_seconds)) %>%
  select(-shape_rate_seconds) %>%
  gt() %>%
  tab_header(title = "Alien Spaceship Shapes & Time in the Air") %>%
  cols_label(shape = "Shape",
             number_instances = "Number Sightings",
             total_seconds = "Total Seconds Observed",
             shape_rate = "Time per each Sighting")  %>%
  cols_align("center") %>%
  tab_options(
    table.background.color = colors[1]) %>%
  tab_style(
    style = list(
      cell_fill(color = colors[6]),
      cell_text(weight = "bold", color = colors[1])
    ),
    locations = cells_title(groups = "title")
  )

##############################
# display table in knitted doc
##############################

Table1_UFO_shapes
Alien Spaceship Shapes & Time in the Air
Shape Number Sightings Total Seconds Observed Time per each Sighting
cone 367 26182820.0 19:49:3
sphere 5755 117463088.6 5:40:11
crescent 2 37810.0 5:15:5
other 6247 116627072.8 5:11:9
light 17872 219047085.2 3:24:16
diamond 1308 7997864.5 1:41:55
unknown 6319 31088140.3 1:21:60
flash 1472 7093452.4 1:20:19
circle 8453 36407947.3 1:11:47
fireball 6562 25138040.2 1:3:51
changed 1 3600.0 1:0:0
oval 4119 14619172.7 0:59:9
cylinder 1382 4400397.5 0:53:4
delta 8 16155.0 0:33:39
disk 6005 12119911.8 0:33:38
changing 2140 4137860.3 0:32:14
cigar 2241 4045685.0 0:30:5
egg 845 1439911.1 0:28:24
triangle 8489 13408958.2 0:26:20
formation 2657 3143588.3 0:19:43
teardrop 817 710229.5 0:14:29
cross 265 174465.0 0:10:58
rectangle 1418 910543.8 0:10:42
chevron 1007 492047.0 0:8:9
round 2 905.0 0:7:32
hexagon 1 240.0 0:4:0
pyramid 1 120.0 0:2:0
flare 1 30.0 0:0:30
dome 1 2.0 0:0:2

RESOURCE: Explore Juliette Verstaen’s gt R-Ladies workshop for more table fun.


Alien Abductions

All accounts of abductions (DT)

##############################
# clean up UFO_data to display in an interactive table
##############################

Table2_abduction_data <- UFO_data_cleaned %>% 
  clean_names() %>% # clean up those col headers using the janitor package
  filter(str_detect(comments, "(?i)abduction")) %>%  # filter for any comments that mention 'abduction'
  select(datetime, date_posted, country, state, city, duration_seconds, shape, comments) 

##############################
# generate an interactive table (using the `DT` package) 
##############################

Table2 <- datatable(Table2_abduction_data, # your data frame
                    class = 'cell-border stripe', 
                    colnames = c("Date/Time (of encounter)", "Date Reported", # rename column names
                                 "Country", "State", "City", "Duration of Encounter (sec)", 
                                 "Shape of UFO", "Comments"),
                    caption = htmltools::tags$caption( # add a caption
                      style = 'caption-side: top; text-align: left;', # set location/alignment of caption text
                      htmltools::em('Table 1: Global accounts of alien abductions from 1910-2013.')), # write caption
                    filter = 'top', 
                    options = list(pageLength = 5, autoWidth = TRUE)) # set number of viewable rows

##############################
# display table in knitted doc
##############################

Table2

RESOURCE: Explore DT: An R interface to the DataTables library for more ways to customize your DT tables.


Map of alien abductions (leaflet)

##############################
# wrangle data (get latitude, longitude, and all comments mentioning the word "abduction")
##############################

leaflet_abduction_data <- UFO_data_cleaned %>% 
  filter(str_detect(comments, "(?i)abduction")) %>% # filter for any comments mentioning 'abduction'
  mutate(latitude = as.numeric(latitude), # leaflet requires lat/long to be numeric values
         longitude = as.numeric(longitude))

###########################
# make a custom icon 
###########################

UFO_icon <- makeIcon(iconUrl = "media/ufo.png", # file path to saved .png (or URL)
                     iconWidth = 35, iconHeight = 45) # adjust size

###########################
# make interactive map (using the `leaflet` package)
###########################

abduction_map <- leaflet(leaflet_abduction_data) %>% 
  addProviderTiles(providers$CartoDB.Positron) %>% # add basemap and choose style
  addMarkers(lng = ~longitude, lat = ~latitude, icon = UFO_icon, popup = ~ comments) %>% # add UFO_icon as data points and show comments in the popup
  addMiniMap(tiles = providers$CartoDB.Positron, # add a mini map to show zoom coverage
             toggleDisplay = TRUE) # allows user to collapse mini map

###########################
# display map in knitted doc
###########################

abduction_map

RESOURCE: Explore Leaflet for R for more ways to customize your Leaflet maps.


Additional Resources

  • The R Markdown Reference Guide is a great cheat sheet for Markdown syntax and knitr chunk options
  • R Markdown Cheat Sheet (also find more cheatsheets for all things R/RStudio by clicking Help > Cheatsheets)
  • For a list of awesome interactive htmlwidgets for R see here. Find even more options in the htmlwidgets gallery.
  • A Guide to GitHub Pages. This is a cool choose-your-own-adventure-style resource if you’re comfortable in the command line.
  • For creating your own palette, I use the ColorPick Eyedropper extension (for Chrome, for Firefox, but any color picking tool will work)
LS0tCnRpdGxlOiAiQSAqdmVyeSogaW1wb3J0YW50IHJlcG9ydCBvbiBnbG9iYWwgVUZPIHNpZ2h0aW5ncyBhbmQgYWJkdWN0aW9ucyIKc3VidGl0bGU6ICIqKEJ1dCBtYWlubHkgYSBsZXNzb24gb24gdXNpbmcgUiBNYXJrZG93biBhbmQgR2l0SHViIFBhZ2VzIHRvIHNoYXJlIHlvdXIgd29yaykqIgphdXRob3I6ICJTYW0gQ3NpayAmIEp1bGlldHRlIFZlcnN0YWVuIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGNvZGVfZm9sZGluZzogaGlkZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRoZW1lOiBzcGFjZWxhYgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQplZGl0b3Jfb3B0aW9uczogCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGNvbnNvbGUKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCjxicj4gCgojIyBBYm91dCBhbmQgQ3JlZGl0cwoKVGhlc2UgbWF0ZXJpYWxzIHdlcmUgZGV2ZWxvcGVkIGJ5IFNhbSBDc2lrICYgSnVsaWV0dGUgVmVyc3RhZW4gdXNpbmcgVUZPIFNpZ2h0aW5nIERhdGEgZnJvbSB0aGUgTmF0aW9uYWwgVUZPIFJlcG9ydGluZyBDZW50ZXIgKE5VRk9SQykuIERhdGEgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSBba2FnZ2xlXShodHRwczovL3d3dy5rYWdnbGUuY29tL05VRk9SQy91Zm8tc2lnaHRpbmdzKS4gU291cmNlIGNvZGUgY2FuIGJlIGZvdW5kIG9uIFtHaXRIdWJdKGh0dHBzOi8vZ2l0aHViLmNvbS9zYW1hbnRoYWNzaWsvUkxhZGllc1NCLWdpdGh1Yi1wYWdlcykuCgpJbiB0aGlzIGV4YW1wbGUgcmVwb3J0LCB3ZSBkZW1vbnN0cmF0ZSBob3cgdG8gdXNlIGEgdmFyaWV0eSBvZiBkaWZmZXJlbnQgYEhUTUwgd2lkZ2V0c2AgdGhhdCBhbGxvdyBmb3IgaW50ZXJhY3RpdmUgd2ViIHZpc3VhbGl6YXRpb25zLCBhcyB3ZWxsIGFzIGhvdyB0byBsZXZlcmFnZSBbUiBNYXJrZG93bl0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbCkgYW5kIFtHaXRIdWIgUGFnZXNdKGh0dHBzOi8vcGFnZXMuZ2l0aHViLmNvbS8pIGZvciBidWlsZGluZyB3ZWIgcGFnZXMgdG8gZGlzcGxheSByZXBvcnRzIChvciB3aGF0ZXZlciBlbHNlIHlvdSBtaWdodCBkcmVhbSBvZiBjcmVhdGluZyB3aXRoIFJNYXJrZG93bikuCgpFeHBhbmQgaGlkZGVuIGBDb2RlYCBjaHVua3MgdG8gc2VlIGhvdyB3ZSd2ZSBjcmVhdGVkIHRoZSBmb2xsb3dpbmcgdGFibGVzLCBtYXBzLCBhbmQgcGxvdHMuIEFsc28sIGJlIG9uIHRoZSBsb29rb3V0IGZvciBbKipUSVAqKl17c3R5bGU9ImNvbG9yOiBncmVlbjsifXMgYW5kIFsqKlJFU09VUkNFKipde3N0eWxlPSJjb2xvcjogYmx1ZTsifXMgdGhyb3VnaG91dCBmb3IgcG9pbnRlcnMgYW5kIGxpbmtzIHRvIHJlc291cmNlcyEKCkFuZCBmaW5hbGx5LCBiZWZvcmUgd2UgZ2V0IHN0YXJ0ZWQsIHRoZSBbUiBNYXJrZG93biBDb29rYm9va10oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLWNvb2tib29rLykgYnkgKllpaHVpIFhpZSwgQ2hyaXN0b3BoZSBEZXJ2aWV1eCwgJiBFbWlseSBSaWVkZXJlciogYW5kIFtSIE1hcmtkb3duOiBUaGUgRGVmaW5pdGl2ZSBHdWlkZV0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgYnkgKllpaHVpIFhpZSwgSi4gSi4gQWxsYWlyZSwgJiBHYXJyZXR0IEdyb2xlbXVuZCogY29udGFpbiAqKnRvbnMqKiBvZiBpbmNyZWRpYmxlIGluZm9ybWF0aW9uIGFuZCBhbnN3ZXJzIHRvIG5lYXJseSBhbGwgb2YgdGhlIFIgTWFya2Rvd24tcmVsYXRlZCBxdWVzdGlvbnMgd2UgaGF2ZSBlbmNvdW50ZXJlZC4gV2UgcmVjb21tZW5kIGNvbWluZyBiYWNrIHRvIHRoZXNlIHRvIGxlYXJuIG1vcmUgYWJvdXQgYWxsLXRoaW5ncyBSIE1hcmtkb3duLgoKPGJyPiAKCiMjIFNldHRpbmcgdXAgeW91ciBSIE1hcmtkb3duIGZpbGUKCi0gICBbY3VzdG9taXppbmcgdGhlIFlBTUxdKGxpbmtlZF9wYWdlcy95YW1sLmh0bWwpCi0gICBbYSBub3RlIG9uIGNvZGUgY2h1bmtzXShsaW5rZWRfcGFnZXMvY29kZV9jaHVua3MuaHRtbCkKLSAgIFttYXJrZG93biBzeW50YXhdKGxpbmtlZF9wYWdlcy9tYXJrZG93bl9zeW50YXguaHRtbCkKLSAgIFtjcmVhdGluZyB0YWJzXShsaW5rZWRfcGFnZXMvY3JlYXRpbmdfdGFicy5odG1sKQotICAgW3B1Ymxpc2hpbmcgeW91ciAuUm1kIGFzIGEgR2l0SHViIFBhZ2VdKGxpbmtlZF9wYWdlcy9naXRodWJfcGFnZXMuaHRtbCkKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgcmVzdWx0cyA9ICdoaWRlJ30KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgbG9hZCBwYWNrYWdlcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgZm9yIGNsZWFuaW5nIGFuZCB3cmFuZ2xpbmcKbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoamFuaXRvcikKbGlicmFyeShsdWJyaWRhdGUpIAoKIyBmb3IgbWFwIG1ha2luZyBhbmQgZ2VuZXJhbCBzcGF0aWFsIHdvcmsKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KG1hcHZpZXcpCmxpYnJhcnkoc2YpCgojIGZvciB3b3JraW5nIHdpdGggdGltZSBzZXJpZXMgZGF0YQpsaWJyYXJ5KGR5Z3JhcGhzKQpsaWJyYXJ5KHh0cykKCiMgZm9yIG1ha2luZyB0YWJsZXMKbGlicmFyeShEVCkKbGlicmFyeShndCkKCiMgZm9yIHBhbGV0dGUgbWFraW5nCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KGNvbG9yc3BhY2UpCmxpYnJhcnkoc2NhbGVzKQoKIyBmb3IgcmVwcm9kdWNpYmlsaXR5CmxpYnJhcnkoaGVyZSkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHNvdXJjZSBjdXN0b20gYWxpZW4gY29sb3IgcGFsZXR0ZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCnNvdXJjZShoZXJlOjpoZXJlKCJjb2RlIiwgInBhbGV0dGVzLlIiKSkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGxvYWQgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKClVGT19kYXRhX2FsbCA8LSByZWFkX2NzdihoZXJlOjpoZXJlKCJkYXRhIiwgIlVGT19jb21wbGV0ZS5jc3YiKSkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGJ1bmNoIG9mIHBhcnNpbmcgZmFpbHVyZXMgZnJvbSBjb2x1bW5zIHRoYXQgYXJlIHNoaWZ0ZWQ7IHdlJ3JlIGdvaW5nIHRvIGp1c3QgZmlsdGVyIHRob3NlIG91dCBiYyB0aGV5IGRvbid0IGhhdmUgYXBwcm9wcmlhdGUgbGF0L2xvbmcgZGF0YQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCiMgc2VlIHBhcnNpbmcgZmFpbHVyZXMgaGVyZQpVRk9fcGFyc2luZ19mYWlsdXJlcyA8LSBVRk9fZGF0YV9hbGwgJT4lIAogIGZpbHRlcihpcy5uYShjaXR5KSkKCiMgcmVtb3ZlIHRob3NlIGZyb20gdGhlIGRhdGFzZXQKVUZPX2RhdGFfY2xlYW5lZCA8LSBVRk9fZGF0YV9hbGwgJT4lIAogZmlsdGVyKCFpcy5uYShjaXR5KSkKYGBgCgo8YnI+CgojIyBBbGllbiBFbmNvdW50ZXJzIHsudGFic2V0fQoKIyMjIFRpbWVzZXJpZXMgKGBkeWdyYXBoYCkKYGBge3IsICB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgZ2V0IGNvbG9ycyBmcm9tIG91ciBjdXN0b20gYWxpZW4gcGFsZXR0ZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmNvbG9ycyA8LSBhbGllbl9wYWxldHRlKCJnYWxheHlfcXVlc3QiLCA4LCAiZGlzY3JldGUiKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgd3JhbmdsZSBkYXRhCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKVUZPX2R5Z3JhcGggPC0gVUZPX2RhdGFfY2xlYW5lZCAlPiUKICBjbGVhbl9uYW1lcygpICU+JQogIHNlbGVjdCgtZGF0ZV9wb3N0ZWQpICU+JQogIHNlcGFyYXRlKGRhdGV0aW1lLCBjKCJtb250aCIsICJzZWNvbmQiLCAidGhpcmQiKSwgc2VwID0gIi8iKSAlPiUKICBzZXBhcmF0ZSh0aGlyZCwgYygieWVhciIsICJ0aW1lIiksIHNlcCA9ICIgIikgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgY291bnQoKSAlPiUKICByZW5hbWUobnVtYmVyX2luc3RhbmNlcyA9IG4pICU+JQogIGZpbHRlcighaXMubmEoeWVhcikpICU+JQogIG11dGF0ZShkYXRlX3NlZW4gPSBwYXN0ZSgiMDEvMDEvIiwgeWVhciwgc2VwID0gIiIpKSAlPiUKICBtdXRhdGUoZGF0ZV9zZWVuID0gYXMuY2hhcmFjdGVyKGRhdGVfc2VlbiksCiAgICAgICAgICMjIG5lZWRzIHRvIGJlIGluIGRhdGUgdGltZSBmb3JtYXQvY2xhc3MgaW4gb3JkZXIgdG8gYmUgY29udmVydGVkIGludG8gZXh0ZW5kYWJsZSB0aW1lIHNlcmllcyBiZWxvdwogICAgICAgICBkYXRlX3NlZW4gPSBhcy5EYXRlKGRhdGVfc2VlbiwgZm9ybWF0ID0gIiVtLyVkLyVZIikpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBjcmVhdGUgdGltZSBzZXJpZXMgb2JqZWN0CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKdWZvX3RpbWVzZXJpZXMgPC0geHRzKHggPSBVRk9fZHlncmFwaCRudW1iZXJfaW5zdGFuY2VzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBvcmRlci5ieSA9IFVGT19keWdyYXBoJGRhdGVfc2VlbikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGZpbmFsbHkgY3JlYXRlIHRoZSBwbG90CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKZHlfcGxvdCA8LSBkeWdyYXBoKHVmb190aW1lc2VyaWVzLCBtYWluID0gIkFsaWVucyBmcm9tIEVhcnRoIikgJT4lCiAgZHlSYW5nZVNlbGVjdG9yKCkgJT4lCiAgZHlPcHRpb25zKGxhYmVsc1VUQyA9IFRSVUUsIGZpbGxHcmFwaD1UUlVFLCBmaWxsQWxwaGE9MC4xLCBkcmF3R3JpZCA9IEZBTFNFLCBjb2xvcnM9IGNvbG9yc1sxXSkgJT4lCiAgZHlDcm9zc2hhaXIoZGlyZWN0aW9uID0gInZlcnRpY2FsIikgJT4lCiAgZHlIaWdobGlnaHQoaGlnaGxpZ2h0Q2lyY2xlU2l6ZSA9IDUsIGhpZ2hsaWdodFNlcmllc0JhY2tncm91bmRBbHBoYSA9IDAuMiwgaGlkZU9uTW91c2VPdXQgPSBGQUxTRSkgJT4lCiAgZHlSb2xsZXIocm9sbFBlcmlvZCA9IDEpICU+JQogIGR5QXhpcygieSIsIGxhYmVsID0gIk51bWJlciBvZiBzaWdodGluZ3MsIGFiZHVjdGlvbnMsIGV0YyIpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBkaXNwbGF5IHBsb3QgaW4ga25pdHRlZCBkb2MKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpkeV9wbG90CgpgYGAKClsqKlJFU09VUkNFOioqXXtzdHlsZT0iY29sb3I6IGJsdWU7In0gRm9yIG1vcmUgaW5mb3JtYXRpb24gYW5kIHRpcHMgb24gKDEpIGNyZWF0aW5nIGR5Z3JhcGhzLCBjaGVjayBvdXQgW2R5Z3JhcGhzIGZvciBSXShodHRwczovL3d3dy5odG1sd2lkZ2V0cy5vcmcvc2hvd2Nhc2VfbGVhZmxldC5odG1sKSAoMikgeHRzIG9iamVjdHMgY2hlY2sgdGhpcyBbb3V0XShodHRwczovL3d3dy5kYXRhY2FtcC5jb20vY29tbXVuaXR5L2Jsb2cvci14dHMtY2hlYXQtc2hlZXQpCgo8YnI+IAoKIyMjIE1hcHBpbmcgZHVyYXRpb24gKGBtYXB2aWV3YCkKCmBgYHtyLCAgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGNsZWFuIGRhdGEKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpVRk9fdGltZSA8LSBVRk9fZGF0YV9jbGVhbmVkICU+JQogIGNsZWFuX25hbWVzKCkgJT4lCiAgc2VsZWN0KC1kYXRlX3Bvc3RlZCkgJT4lCiAgbXV0YXRlKGxvbmdpdHVkZSA9IGFzLm51bWVyaWMobG9uZ2l0dWRlKSwKICAgICAgICAgbGF0aXR1ZGUgPSBhcy5udW1lcmljKGxhdGl0dWRlKSkgJT4lCiAgbXV0YXRlKGR1cmF0aW9uX2xlbmd0aCA9IGNhc2Vfd2hlbihkdXJhdGlvbl9zZWNvbmRzIDw9IDYwIH4gInNob3J0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGR1cmF0aW9uX3NlY29uZHMgPj0gMzYwIH4gImxvbmciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVCB+ICJtZWRpdW0iKSkgJT4lCiAgZmlsdGVyKCFpcy5uYShsb25naXR1ZGUpLAogICAgICAgICAhaXMubmEobGF0aXR1ZGUpKSAlPiUKICBncm91cF9ieShkdXJhdGlvbl9sZW5ndGgsIGxvbmdpdHVkZSwgbGF0aXR1ZGUpICU+JQogIGNvdW50KCkgJT4lCiAgcmVuYW1lKG51bWJlcl9pbnN0YW5jZXMgPSBuKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgbmVlZCB0byBjb252ZXJ0IHRvIGEgc3BhdGlhbCBmZWF0dXJlIG9iamVjdAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKClVGT19zcGF0aWFsIDwtICBzdF9hc19zZihVRk9fdGltZSwgY29vcmRzID0gYygibG9uZ2l0dWRlIiwgImxhdGl0dWRlIiksCiAgICAgICAgICAgY3JzID0gNDMyNikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGNyZWF0ZSB0aGUgbWFwIQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKClVGT19tYXBfbXYgPC0gbWFwdmlldyhVRk9fc3BhdGlhbCwgemNvbCA9ICJkdXJhdGlvbl9sZW5ndGgiLCBjZXggPSAibnVtYmVyX2luc3RhbmNlcyIsIGFscGhhID0gMC4yKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgZGlzcGxheSBtYXAgaW4ga25pdHRlZCBkb2MKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpVRk9fbWFwX212CmBgYAoKWyoqUkVTT1VSQ0U6Kipde3N0eWxlPSJjb2xvcjogYmx1ZTsifSBFeHBsb3JlIEphbWllIE1vbnRnb21lcnkncyBbbWFwdmlldyBhbmQgbWFwZWRpdCB0dXRvcmlhbCBmcm9tIFNCIEVjby1EYXRhIFNjaWVuY2VdKGh0dHBzOi8vZ2l0aHViLmNvbS9qYW1pZWNtb250Z29tZXJ5L21hcHZpZXctbWFwZWRpdCkgZm9yIG1vcmUgbWFwdmlldyBmdW4uCgo8YnI+CgojIyMgU3BhY2VzaGlwIHNoYXBlcyAoYGd0YCkKCmBgYHtyLCAgd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGdldCBjb2xvcnMgZnJvbSBvdXIgY3VzdG9tIGFsaWVuIHBhbGV0dGUKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpjb2xvcnNfdGFibGUgPC0gYWxpZW5fcGFsZXR0ZSgiZ2FsYXh5X3F1ZXN0IiwgOCwgImRpc2NyZXRlIikKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHdyYW5nbGUgZGF0YSAoZ2V0IGNvdW50cyBieSBVRk8gc2hhcGUpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKVUZPX3NoYXBlX2NvdW50IDwtIFVGT19kYXRhX2NsZWFuZWQgJT4lCiAgY2xlYW5fbmFtZXMoKSAlPiUKICBzZWxlY3QoLWRhdGVfcG9zdGVkKSAlPiUKICBtdXRhdGUobG9uZ2l0dWRlID0gYXMubnVtZXJpYyhsb25naXR1ZGUpLAogICAgICAgICBsYXRpdHVkZSA9IGFzLm51bWVyaWMobGF0aXR1ZGUpKSAlPiUKICBncm91cF9ieShzaGFwZSkgJT4lCiAgY291bnQoKSAlPiUKICByZW5hbWUobnVtYmVyX2luc3RhbmNlcyA9IG4pICU+JQogIGZpbHRlcighaXMubmEoc2hhcGUpKSAjIyBUaGVyZSBhcmUgMjggdW5pcXVlIHNoYXBlcwoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgd3JhbmdsZSBkYXRhIChnZXQgdG90YWwgdGltZSBlYWNoIHNoYXBlIHdhcyB2aWV3ZWQpCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKVUZPX3NoYXBlX3RpbWVzIDwtIFVGT19kYXRhX2NsZWFuZWQgJT4lCiAgY2xlYW5fbmFtZXMoKSAlPiUKICBzZWxlY3QoLWRhdGVfcG9zdGVkKSAlPiUKICBtdXRhdGUobG9uZ2l0dWRlID0gYXMubnVtZXJpYyhsb25naXR1ZGUpLAogICAgICAgICBsYXRpdHVkZSA9IGFzLm51bWVyaWMobGF0aXR1ZGUpKSAlPiUKICBncm91cF9ieShzaGFwZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSh0b3RhbF9zZWNvbmRzID0gc3VtKGR1cmF0aW9uX3NlY29uZHMsIG5hLnJtID0gVFJVRSkpICU+JQogIGZpbHRlcighaXMubmEoc2hhcGUpKQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgY29tYmluZSBkYXRhIGZyYW1lcwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKClVGT19zaGFwZSA8LSBsZWZ0X2pvaW4oVUZPX3NoYXBlX2NvdW50LCBVRk9fc2hhcGVfdGltZXMpICU+JQogIG11dGF0ZShzaGFwZV9yYXRlX3NlY29uZHMgPSB0b3RhbF9zZWNvbmRzL251bWJlcl9pbnN0YW5jZXMpICU+JQogIG11dGF0ZShIb3VycyA9IGhvdXIoc2Vjb25kc190b19wZXJpb2Qoc2hhcGVfcmF0ZV9zZWNvbmRzKSksCiAgICAgICAgIE1pbnV0ZXMgPSBtaW51dGUoc2Vjb25kc190b19wZXJpb2Qoc2hhcGVfcmF0ZV9zZWNvbmRzKSksCiAgICAgICAgIFNlY29uZHMgPSByb3VuZChzZWNvbmQoc2Vjb25kc190b19wZXJpb2Qoc2hhcGVfcmF0ZV9zZWNvbmRzKSksIGRpZ2l0cyA9IDApKSAlPiUKICB1bml0ZShzaGFwZV9yYXRlLCBjKCJIb3VycyIsICJNaW51dGVzIiwgIlNlY29uZHMiKSwgc2VwID0gIjoiKSAlPiUKICB1bmdyb3VwKCkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIG1ha2UgdGhlIGBndGAgdGFibGUKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpUYWJsZTFfVUZPX3NoYXBlcyA8LSBVRk9fc2hhcGUgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhzaGFwZV9yYXRlX3NlY29uZHMpKSAlPiUKICBzZWxlY3QoLXNoYXBlX3JhdGVfc2Vjb25kcykgJT4lCiAgZ3QoKSAlPiUKICB0YWJfaGVhZGVyKHRpdGxlID0gIkFsaWVuIFNwYWNlc2hpcCBTaGFwZXMgJiBUaW1lIGluIHRoZSBBaXIiKSAlPiUKICBjb2xzX2xhYmVsKHNoYXBlID0gIlNoYXBlIiwKICAgICAgICAgICAgIG51bWJlcl9pbnN0YW5jZXMgPSAiTnVtYmVyIFNpZ2h0aW5ncyIsCiAgICAgICAgICAgICB0b3RhbF9zZWNvbmRzID0gIlRvdGFsIFNlY29uZHMgT2JzZXJ2ZWQiLAogICAgICAgICAgICAgc2hhcGVfcmF0ZSA9ICJUaW1lIHBlciBlYWNoIFNpZ2h0aW5nIikgICU+JQogIGNvbHNfYWxpZ24oImNlbnRlciIpICU+JQogIHRhYl9vcHRpb25zKAogICAgdGFibGUuYmFja2dyb3VuZC5jb2xvciA9IGNvbG9yc1sxXSkgJT4lCiAgdGFiX3N0eWxlKAogICAgc3R5bGUgPSBsaXN0KAogICAgICBjZWxsX2ZpbGwoY29sb3IgPSBjb2xvcnNbNl0pLAogICAgICBjZWxsX3RleHQod2VpZ2h0ID0gImJvbGQiLCBjb2xvciA9IGNvbG9yc1sxXSkKICAgICksCiAgICBsb2NhdGlvbnMgPSBjZWxsc190aXRsZShncm91cHMgPSAidGl0bGUiKQogICkKCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIGRpc3BsYXkgdGFibGUgaW4ga25pdHRlZCBkb2MKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpUYWJsZTFfVUZPX3NoYXBlcwpgYGAKClsqKlJFU09VUkNFOioqXXtzdHlsZT0iY29sb3I6IGJsdWU7In0gRXhwbG9yZSBKdWxpZXR0ZSBWZXJzdGFlbidzIFtgZ3RgIFItTGFkaWVzIHdvcmtzaG9wXShodHRwczovL2dpdGh1Yi5jb20vai12ZXJzdGFlbi9ndF93b3Jrc2hvcF9TQl9SX0xhZGllcykgZm9yIG1vcmUgdGFibGUgZnVuLgoKPGJyPgoKIyMgQWxpZW4gQWJkdWN0aW9ucyB7LnRhYnNldH0KCiMjIyBBbGwgYWNjb3VudHMgb2YgYWJkdWN0aW9ucyAoYERUYCkKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRX0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgY2xlYW4gdXAgVUZPX2RhdGEgdG8gZGlzcGxheSBpbiBhbiBpbnRlcmFjdGl2ZSB0YWJsZQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKClRhYmxlMl9hYmR1Y3Rpb25fZGF0YSA8LSBVRk9fZGF0YV9jbGVhbmVkICU+JSAKICBjbGVhbl9uYW1lcygpICU+JSAjIGNsZWFuIHVwIHRob3NlIGNvbCBoZWFkZXJzIHVzaW5nIHRoZSBqYW5pdG9yIHBhY2thZ2UKICBmaWx0ZXIoc3RyX2RldGVjdChjb21tZW50cywgIig/aSlhYmR1Y3Rpb24iKSkgJT4lICAjIGZpbHRlciBmb3IgYW55IGNvbW1lbnRzIHRoYXQgbWVudGlvbiAnYWJkdWN0aW9uJwogIHNlbGVjdChkYXRldGltZSwgZGF0ZV9wb3N0ZWQsIGNvdW50cnksIHN0YXRlLCBjaXR5LCBkdXJhdGlvbl9zZWNvbmRzLCBzaGFwZSwgY29tbWVudHMpIAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgZ2VuZXJhdGUgYW4gaW50ZXJhY3RpdmUgdGFibGUgKHVzaW5nIHRoZSBgRFRgIHBhY2thZ2UpIAojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKClRhYmxlMiA8LSBkYXRhdGFibGUoVGFibGUyX2FiZHVjdGlvbl9kYXRhLCAjIHlvdXIgZGF0YSBmcmFtZQogICAgICAgICAgICAgICAgICAgIGNsYXNzID0gJ2NlbGwtYm9yZGVyIHN0cmlwZScsIAogICAgICAgICAgICAgICAgICAgIGNvbG5hbWVzID0gYygiRGF0ZS9UaW1lIChvZiBlbmNvdW50ZXIpIiwgIkRhdGUgUmVwb3J0ZWQiLCAjIHJlbmFtZSBjb2x1bW4gbmFtZXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNvdW50cnkiLCAiU3RhdGUiLCAiQ2l0eSIsICJEdXJhdGlvbiBvZiBFbmNvdW50ZXIgKHNlYykiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNoYXBlIG9mIFVGTyIsICJDb21tZW50cyIpLAogICAgICAgICAgICAgICAgICAgIGNhcHRpb24gPSBodG1sdG9vbHM6OnRhZ3MkY2FwdGlvbiggIyBhZGQgYSBjYXB0aW9uCiAgICAgICAgICAgICAgICAgICAgICBzdHlsZSA9ICdjYXB0aW9uLXNpZGU6IHRvcDsgdGV4dC1hbGlnbjogbGVmdDsnLCAjIHNldCBsb2NhdGlvbi9hbGlnbm1lbnQgb2YgY2FwdGlvbiB0ZXh0CiAgICAgICAgICAgICAgICAgICAgICBodG1sdG9vbHM6OmVtKCdUYWJsZSAxOiBHbG9iYWwgYWNjb3VudHMgb2YgYWxpZW4gYWJkdWN0aW9ucyBmcm9tIDE5MTAtMjAxMy4nKSksICMgd3JpdGUgY2FwdGlvbgogICAgICAgICAgICAgICAgICAgIGZpbHRlciA9ICd0b3AnLCAKICAgICAgICAgICAgICAgICAgICBvcHRpb25zID0gbGlzdChwYWdlTGVuZ3RoID0gNSwgYXV0b1dpZHRoID0gVFJVRSkpICMgc2V0IG51bWJlciBvZiB2aWV3YWJsZSByb3dzCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBkaXNwbGF5IHRhYmxlIGluIGtuaXR0ZWQgZG9jCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKVGFibGUyCmBgYAoKWyoqUkVTT1VSQ0U6Kipde3N0eWxlPSJjb2xvcjogYmx1ZTsifSBFeHBsb3JlIFtEVDogQW4gUiBpbnRlcmZhY2UgdG8gdGhlIERhdGFUYWJsZXMgbGlicmFyeV0oaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9EVC8pIGZvciBtb3JlIHdheXMgdG8gY3VzdG9taXplIHlvdXIgRFQgdGFibGVzLgoKPGJyPgoKIyMjIE1hcCBvZiBhbGllbiBhYmR1Y3Rpb25zIChgbGVhZmxldGApCgpgYGB7ciwgZmlnLmFsdCA9ICJBbiBpbnRlcmFjdGl2ZSBtYXAsIHdoaWNoIHNob3dzIHRoZSBsb2NhdGlvbnMgb2YgYWxsIFVGTyBzaWdodGluZyB3aGVyZSB0aGUgb2JzZXJ2ZXIgbWVudGlvbnMgdGhlIHdvcmQgJ2FiZHVjdGlvbicuIFRoZSB2YXN0ICBtYWpvcml0eSBvZiB0aGUgYWNjb3VudHMgYXJlIGluIHRoZSBVbml0ZWQgU3RhdGVzLiJ9CiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIHdyYW5nbGUgZGF0YSAoZ2V0IGxhdGl0dWRlLCBsb25naXR1ZGUsIGFuZCBhbGwgY29tbWVudHMgbWVudGlvbmluZyB0aGUgd29yZCAiYWJkdWN0aW9uIikKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgpsZWFmbGV0X2FiZHVjdGlvbl9kYXRhIDwtIFVGT19kYXRhX2NsZWFuZWQgJT4lIAogIGZpbHRlcihzdHJfZGV0ZWN0KGNvbW1lbnRzLCAiKD9pKWFiZHVjdGlvbiIpKSAlPiUgIyBmaWx0ZXIgZm9yIGFueSBjb21tZW50cyBtZW50aW9uaW5nICdhYmR1Y3Rpb24nCiAgbXV0YXRlKGxhdGl0dWRlID0gYXMubnVtZXJpYyhsYXRpdHVkZSksICMgbGVhZmxldCByZXF1aXJlcyBsYXQvbG9uZyB0byBiZSBudW1lcmljIHZhbHVlcwogICAgICAgICBsb25naXR1ZGUgPSBhcy5udW1lcmljKGxvbmdpdHVkZSkpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBtYWtlIGEgY3VzdG9tIGljb24gCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwoKVUZPX2ljb24gPC0gbWFrZUljb24oaWNvblVybCA9ICJtZWRpYS91Zm8ucG5nIiwgIyBmaWxlIHBhdGggdG8gc2F2ZWQgLnBuZyAob3IgVVJMKQogICAgICAgICAgICAgICAgICAgICBpY29uV2lkdGggPSAzNSwgaWNvbkhlaWdodCA9IDQ1KSAjIGFkanVzdCBzaXplCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBtYWtlIGludGVyYWN0aXZlIG1hcCAodXNpbmcgdGhlIGBsZWFmbGV0YCBwYWNrYWdlKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmFiZHVjdGlvbl9tYXAgPC0gbGVhZmxldChsZWFmbGV0X2FiZHVjdGlvbl9kYXRhKSAlPiUgCiAgYWRkUHJvdmlkZXJUaWxlcyhwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbikgJT4lICMgYWRkIGJhc2VtYXAgYW5kIGNob29zZSBzdHlsZQogIGFkZE1hcmtlcnMobG5nID0gfmxvbmdpdHVkZSwgbGF0ID0gfmxhdGl0dWRlLCBpY29uID0gVUZPX2ljb24sIHBvcHVwID0gfiBjb21tZW50cykgJT4lICMgYWRkIFVGT19pY29uIGFzIGRhdGEgcG9pbnRzIGFuZCBzaG93IGNvbW1lbnRzIGluIHRoZSBwb3B1cAogIGFkZE1pbmlNYXAodGlsZXMgPSBwcm92aWRlcnMkQ2FydG9EQi5Qb3NpdHJvbiwgIyBhZGQgYSBtaW5pIG1hcCB0byBzaG93IHpvb20gY292ZXJhZ2UKICAgICAgICAgICAgIHRvZ2dsZURpc3BsYXkgPSBUUlVFKSAjIGFsbG93cyB1c2VyIHRvIGNvbGxhcHNlIG1pbmkgbWFwCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBkaXNwbGF5IG1hcCBpbiBrbml0dGVkIGRvYwojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmFiZHVjdGlvbl9tYXAKYGBgCgpbKipSRVNPVVJDRToqKl17c3R5bGU9ImNvbG9yOiBibHVlOyJ9IEV4cGxvcmUgW0xlYWZsZXQgZm9yIFJdKGh0dHBzOi8vcnN0dWRpby5naXRodWIuaW8vbGVhZmxldC8pIGZvciBtb3JlIHdheXMgdG8gY3VzdG9taXplIHlvdXIgTGVhZmxldCBtYXBzLiAgCgo8YnI+CgoKIyMgQWRkaXRpb25hbCBSZXNvdXJjZXMKCi0gICBUaGUgW1IgTWFya2Rvd24gUmVmZXJlbmNlIEd1aWRlXShodHRwczovL3JzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE1LzAzL3JtYXJrZG93bi1yZWZlcmVuY2UucGRmKSBpcyBhIGdyZWF0IGNoZWF0IHNoZWV0IGZvciBNYXJrZG93biBzeW50YXggYW5kIGtuaXRyIGNodW5rIG9wdGlvbnMKLSAgIFtSIE1hcmtkb3duIENoZWF0IFNoZWV0XShodHRwczovL3JzdHVkaW8uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE2LzAzL3JtYXJrZG93bi1jaGVhdHNoZWV0LTIuMC5wZGYpIChhbHNvIGZpbmQgbW9yZSBjaGVhdHNoZWV0cyBmb3IgYWxsIHRoaW5ncyBSL1JTdHVkaW8gYnkgY2xpY2tpbmcgSGVscCA+IENoZWF0c2hlZXRzKQotICAgRm9yIGEgbGlzdCBvZiBhd2Vzb21lIGludGVyYWN0aXZlICoqYGh0bWx3aWRnZXRzYCoqIGZvciBSIHNlZSBbaGVyZV0oaHR0cHM6Ly93d3cuaHRtbHdpZGdldHMub3JnL3Nob3djYXNlX2xlYWZsZXQuaHRtbCkuIEZpbmQgZXZlbiBtb3JlIG9wdGlvbnMgaW4gdGhlIFtodG1sd2lkZ2V0cyBnYWxsZXJ5XShodHRwOi8vZ2FsbGVyeS5odG1sd2lkZ2V0cy5vcmcvKS4KLSAgIFtBIEd1aWRlIHRvIEdpdEh1YiBQYWdlc10oaHR0cHM6Ly93d3cudGhpbmtmdWwuY29tL2xlYXJuL2EtZ3VpZGUtdG8tdXNpbmctZ2l0aHViLXBhZ2VzLykuIFRoaXMgaXMgYSBjb29sIGNob29zZS15b3VyLW93bi1hZHZlbnR1cmUtc3R5bGUgcmVzb3VyY2UgaWYgeW91J3JlIGNvbWZvcnRhYmxlIGluIHRoZSBjb21tYW5kIGxpbmUuCi0gRm9yIGNyZWF0aW5nIHlvdXIgb3duIHBhbGV0dGUsIEkgdXNlIHRoZSBDb2xvclBpY2sgRXllZHJvcHBlciBleHRlbnNpb24gKGZvciBbQ2hyb21lXShodHRwczovL2Nocm9tZS5nb29nbGUuY29tL3dlYnN0b3JlL2RldGFpbC9jb2xvcnBpY2stZXllZHJvcHBlci9vaGNwbmlnYWxla2doY21nY2RjZW5rcGVsZmZwZG9sZz9obD1lbiksIGZvciBbRmlyZWZveF0oaHR0cHM6Ly9hZGRvbnMubW96aWxsYS5vcmcvZW4tVVMvZmlyZWZveC9hZGRvbi9jb2xvcnBpY2stZXllZHJvcHBlci8pLCBidXQgYW55IGNvbG9yIHBpY2tpbmcgdG9vbCB3aWxsIHdvcmspCgo=