Basics

Objectives

Learn how to effecitvely use visualization for

  • exploring and understanding data
  • communicating and explaining insights

Learn how to use data technologies for

  • acquiring data
  • cleaning data
  • organizing data

Learn how to do this in ways that are

  • reproducible
  • reusable
  • shareable

Topics

Data visualization

  • some history of visualization
  • learning the basic graph types
  • how to create basic graphs in R
  • human perception, and how it affects visualization
  • using understanding of perception to guide evaluation and design
  • dynamic and interactive visualizations

Data technologies

  • basic data types
  • reshaping and transforming data
  • aggregating and summarizing data
  • merging several data sets
  • regular expressions for cleaning data
  • harvesting data from the web

Reproducible research and collaboration

  • literate programming and data analysis
  • version control for collaboration

Prerequisites

An introductory statistics course.

A regression course.

Strongly recommended: Prior exposure to basic use of statistical programming software, such as R or SAS, as obtained from a regression course.

Assessment

Quizzes

  • Short quizzes quizzes will be posted on ICON after most lectures.

Homework

  • Homework assignments will be due approximately once a week.
  • You will typically submit your work by pushing it to your GitLab repository by 5:00 PM on the due date.
  • Your homework solutions should be written as reports, using proper sentences and paragraphs to present your results.

Project

  • You will do a project developing a visual analysis of a data set of your choosing.
  • You can work on your own or in a group of up to three students.
  • Your project should represent about 10 hours of work.
  • A one page proposal for your project is due on Monday, March 20.
  • A final report on your project is due on Friday, May 5.
  • Your project may be shared with the class through the class web page.

Your grade will be based on quizzes (10%), homework (70%) and the project (20%).

Tools

We will be using

  • R for computing and graphics
  • R Markdown for creating reproducible reports.
  • git and the UI GitLab service for revision control

You will need an editor or IDE; you can use

  • RStudio for editing and more
  • any other editor or IDE

To access these tools you can

For help installing your own a good place to start is https://happygitwithr.com/

First Steps: Do This Today!

Visit the UI GitLab site at https://research-git.uiowa.edu and log in with your HawkID.

Make sure you can access the UI IDAS Rstudio Notebook Server with your HawkID and password.

Make sure you are able to log into the CLAS Linux systems with your HawkID and password.

Look at the brief introduction to git or the beginning of https://happygitwithr.com to see what git is about and how to get started with it.

Make sure you have access to R and try someting like this:

with(faithful,
     plot(eruptions, waiting,
          xlab = "Eruption time (min)",
          ylab = "Waiting time to next eruption (min)"))

The result is a plot that looks like this:

Getting Set Up

Log into the UI GitLab site at https://research-git.uiowa.edu to get your GitLab account activated.

Decide where you want to work:

Setup needed for IDAS RStudio Server:

  • If you are registered then you should have an account now. If you add the course late you should have an account within a day.
  • Introduce yourself to Git.

Setup needed for CLAS Linux:

Setting up your own computer: (A good resource for help with this is https://happygitwithr.com):

  • Install the current version of R.

    • You might have older versions from other courses (e.g. from Anaconda).
    • You will need to add packages as we go along.
  • Install RStudio if you want to use it (highly recommended).

  • Install Git.

  • Introduce yourself to Git.

Even if you decide to use your own computer you should make sure you can use the RStudio server or CLAS systems as a backup.

Some Examples

Life Expectancy in the Americas in 2007

The data is from the GapMinder project.

library(dplyr)
library(ggplot2)
library(gapminder)
le_am_2007 <- filter(gapminder, year == 2007, continent == "Americas") %>%
    mutate(country = reorder(country, lifeExp))
knitr::kable(select(le_am_2007, country, lifeExp),
             col.names = c("Country", "Life Expectancy (years)"),
             digits = 1, format = "html") %>%
    kableExtra::kable_styling(bootstrap_options = "striped",
                              full_width = FALSE,
                              font_size = 14) %>%
    kableExtra::scroll_box(height = "300px", width = "75%")
Country Life Expectancy (years)
Argentina 75.3
Bolivia 65.6
Brazil 72.4
Canada 80.7
Chile 78.6
Colombia 72.9
Costa Rica 78.8
Cuba 78.3
Dominican Republic 72.2
Ecuador 75.0
El Salvador 71.9
Guatemala 70.3
Haiti 60.9
Honduras 70.2
Jamaica 72.6
Mexico 76.2
Nicaragua 72.9
Panama 75.5
Paraguay 71.8
Peru 71.4
Puerto Rico 78.7
Trinidad and Tobago 69.8
United States 78.2
Uruguay 76.4
Venezuela 73.7

A dot plot:

thm <- theme_minimal() + theme(text = element_text(size = 16))
ggplot(le_am_2007, aes(y = country, x = lifeExp)) +
    geom_point(fill = "lightblue") +
    labs(x = "Life Expectancy (years)", y = NULL) +
    thm + ggtitle("Dot Plot")

A bar chart:

ggplot(le_am_2007, aes(x = lifeExp, y = country)) +
    geom_col(fill = "lightblue") +
    labs(x = "Life Expectancy (years)", y = NULL) +
    thm + ggtitle("Bar Chart")

Another (bad!) bar chart:

baseline <- 60
ticks <- c(0, 10, 20, 30)
ggplot(le_am_2007, aes(x = lifeExp - baseline, y = country)) +
    geom_col(fill = "lightblue") +
    labs(x = "Life Expectancy (years)", y = NULL) +
    scale_x_continuous(breaks = ticks, labels = ticks + baseline) +
    thm + ggtitle("Another Bar Chart")

We will look at:

  • How to create these views using code that makes them easily reproducible.

  • How to assess their advantages and disadvantages as visual representations of the data

A data set with more variables for more countries and years is available in the gapminder R package.

Data preparation steps:

  • Filter the larger data set down to the countries and year we want.

  • Select the country name and life expectancy variables.

We will look at how to carry out these steps with reproducible code.

Yearly Snowfall in Iowa City

How did the winter of 2018/9 compare to other years?

The data are available from a NOAA web serice API as a CSV file.

## # A tibble: 6 × 34
##    year month element VALUE1 VALUE2 VALUE3 VALUE4 VALUE5 VALUE6 VALUE7 VALUE8
##   <int> <int> <chr>    <int>  <int>  <int>  <int>  <int>  <int>  <int>  <int>
## 1  1893     1 TMAX       -17    -28   -150    -44    -17   -106    -56    -67
## 2  1893     1 TMIN       -67   -161   -233   -156   -156   -206   -111   -211
## 3  1893     1 PRCP         0      0      0     64      0     38      0      0
## 4  1893     1 SNOW         0      0      0     64      0     38      0      0
## 5  1893     2 TMAX        11    -56   -106   -122     33     28   -161    -94
## 6  1893     2 TMIN      -233   -206   -217   -267   -122   -211   -256   -278
## # ℹ 23 more variables: VALUE9 <int>, VALUE10 <int>, VALUE11 <int>,
## #   VALUE12 <int>, VALUE13 <int>, VALUE14 <int>, VALUE15 <int>, VALUE16 <int>,
## #   VALUE17 <int>, VALUE18 <int>, VALUE19 <int>, VALUE20 <int>, VALUE21 <int>,
## #   VALUE22 <int>, VALUE23 <int>, VALUE24 <int>, VALUE25 <int>, VALUE26 <int>,
## #   VALUE27 <int>, VALUE28 <int>, VALUE29 <int>, VALUE30 <int>, VALUE31 <int>

Data preparation steps:

  • Read in the CSV file.

  • Reshape the data to have columns date, TMAX, TMIN, SNOW and PRCP.

  • Filter out bogus dates created by the original format.

  • Convert units to more standard (American) ones (e.g. milimeters to inches).

Code is available here.

Internet Adoption Across the World

An example from Wilke (2019) with World Bank data.

The data are available in several formats (CSV, XML, Excel).

## # A tibble: 6 × 65
##   Country.Name Country.Code Indicator.Name      Indicator.Code X1960 X1961 X1962
##   <chr>        <chr>        <chr>               <chr>          <int> <lgl> <lgl>
## 1 Aruba        ABW          Individuals using … IT.NET.USER.ZS    NA NA    NA   
## 2 Afghanistan  AFG          Individuals using … IT.NET.USER.ZS    NA NA    NA   
## 3 Angola       AGO          Individuals using … IT.NET.USER.ZS    NA NA    NA   
## 4 Albania      ALB          Individuals using … IT.NET.USER.ZS    NA NA    NA   
## 5 Andorra      AND          Individuals using … IT.NET.USER.ZS    NA NA    NA   
## 6 Arab World   ARB          Individuals using … IT.NET.USER.ZS    NA NA    NA   
## # ℹ 58 more variables: X1963 <lgl>, X1964 <lgl>, X1965 <int>, X1966 <lgl>,
## #   X1967 <lgl>, X1968 <lgl>, X1969 <lgl>, X1970 <int>, X1971 <lgl>,
## #   X1972 <lgl>, X1973 <lgl>, X1974 <lgl>, X1975 <int>, X1976 <int>,
## #   X1977 <int>, X1978 <int>, X1979 <int>, X1980 <int>, X1981 <int>,
## #   X1982 <int>, X1983 <int>, X1984 <int>, X1985 <int>, X1986 <int>,
## #   X1987 <int>, X1988 <int>, X1989 <int>, X1990 <dbl>, X1991 <dbl>,
## #   X1992 <dbl>, X1993 <dbl>, X1994 <dbl>, X1995 <dbl>, X1996 <dbl>, …

Data preparation:

  • Read in the data.

  • Filter down to the countries we want.

  • Reshape to have columns country, year, and users.

Code is available here.

Iowa Wind Turbines

Data is available from the U.S. Wind Turbine Database.

library(sf)
data(US_counties_geoms, package = "dviz.supp")
data(wind_turbines, package = "dviz.supp")

sf_wt <- st_as_sf(wind_turbines, coords = c("xlong", "ylat"), crs = 4326)
sf_wt_IA <- filter(sf_wt, t_state == "IA")
sf_wt_IA <- mutate(sf_wt_IA,
                   p_year = ifelse(p_year > 0, p_year, NA),
                   year = cut(p_year,
                              breaks = c(0, 2005, 2010, 2015, 2020),
                              labels = c("before 2005", "2005-2009",
                                         "2010-2014", "2015-2020"),
                              right = FALSE))

ggplot(filter(US_counties_geoms$lower48, STATEFP == 19)) +
    geom_sf() +
    geom_sf(data = sf_wt_IA, aes(fill = year, color = year), shape = 21) +
    scale_fill_viridis_d(direction = -1, na.value = "red") +
    ggthemes::theme_map() +
    ggtitle("A Map") +
    theme(legend.background = element_rect(fill = "transparent"),
          plot.title = element_text(size = 16))

There are two data sets:

  • Shape information for drawing the map.

  • Data on individual wind turbines.

Data preparation:

  • Read in the data.

  • Match up the projection used for the map and location data.

Iowa Population in 2010

data(US_census, package = "dviz.supp")
uscounties <- mutate(US_counties_geoms$lower48,
                     FIPS = as.numeric(paste0(STATEFP, COUNTYFP)))
uscounties <- left_join(uscounties,
                        select(US_census, FIPS, pop2010),
                        "FIPS")
## old-style crs object detected; please recreate object with a recent sf::st_crs()
ggplot(filter(uscounties, STATEFP == 19)) +
    geom_sf(aes(fill = pop2010)) +
    scale_fill_viridis_c(direction = -1, trans = "log10",
                         labels = scales::comma) +
    ggtitle("A Choropleth Map") +
    ggthemes::theme_map() +
    theme(legend.background = element_rect(fill = "transparent"),
          plot.title = element_text(size = 16))

Again there are two data sets:

  • Shape data for drawing the map.

  • County population data from the 2010 census.

Data preparation:

  • Merge or join the population data with the shape data.

Reproducibility

Reproducible Reports and Analyses

Preparing a report on a data analysis project usually involves

  • reading the data

  • wrangling the data into usable form

  • visualizing, summarizing, and modeling

  • writing a report that includes your results

To make your work reproducible for someone else, or for you when the data changes, it is best to use code for the entire workflow.

R Markdown is one technology that supports this.

Tools for Reproducibility

R Markdown files contain report text along with code to produce numerical and graphical results.

Tools are available to

  • convert an R Markdown file into a PDF or HTML report;

  • extract the code used to produce the computational and graphical results.

This page was generated from the R Markdown file intro.Rmd.

You will be creating R Markdown files like this for your homework and project.

Some R Markdown tutorials:

Code

Code for the Iowa City Snowfall Example

Read the data:

library(tidyverse)
library(lubridate)
if (! file.exists(here("ic_noaa.csv.gz")))
    download.file("http://www.stat.uiowa.edu/~luke/data/ic_noaa.csv.gz",
                  here("ic_noaa.csv.gz"))
ic_data_raw <- as_tibble(read.csv(here("ic_noaa.csv.gz"), head = TRUE))

Reshape from (very) wide to (too) long:

ic_data <- select(ic_data_raw, year, month, element, starts_with("VALUE"))
ic_data <- pivot_longer(ic_data,
                        names_to = "day",
                        values_to = "value", c(VALUE1 : VALUE31))

Extract the day as a number:

ic_data <- mutate(ic_data, day = as.integer(sub("VALUE", "", day)))

Reshape from too long to tidy with one row per day, keeping only the primary variables:

corevars <- c("TMAX", "TMIN", "PRCP", "SNOW", "SNWD")
ic_data <- filter(ic_data, element %in% corevars)
ic_data <- pivot_wider(ic_data, names_from = "element", values_from = "value")

Add a date variable for plotting and to help get rid of bogus days:

ic_data <- mutate(ic_data, date = lubridate::make_date(year, month, day))
ic_data <- filter(ic_data, ! is.na(date))

Make units more standard (American):

mm2in <- function(x) x / 25.4
C2F <- function(x) 32 + 1.8 * x
ic_data <- transmute(ic_data, year, month, day, date,
                     PRCP = mm2in(PRCP / 10),
                     SNOW = mm2in(SNOW),
                     SNWD = mm2in(SNWD),
                     TMIN = C2F(TMIN / 10),
                     TMAX = C2F(TMAX / 10))

Add a Month factor with abbreviated levels:

ic_data <- mutate(ic_data,
                  Month = lubridate::month(month, label = TRUE, abbr = TRUE))

Associate January through June with the winter starting in the previous year:

ic_data <- mutate(ic_data, wyear = ifelse(month <= 6, year - 1, year))

Compute the winter totals and the total for the 2018/9 winter:

ic_snow <- group_by(ic_data, wyear) %>%
    summarize(snow = sum(SNOW, na.rm = TRUE))
ic_snow_2018 <- filter(ic_snow, wyear == 2018)$snow

Create the histogram and show the 2018/9 total:

ggplot(ic_snow) +
    geom_histogram(aes(x = snow), bins = 15, fill = "grey", color = "black") +
    geom_vline(xintercept = ic_snow_2018, color = "red", size = 2) +
    scale_x_continuous(name = "Total Annual Snowfall (inches)",
                       sec.axis = dup_axis(name = NULL,
                                           breaks = ic_snow_2018,
                                           labels = "2018/9")) +
    scale_y_continuous(expand = expansion(0, 0)) +
    ggtitle("A Histogram") +
    theme_minimal() +
    theme(text = element_text(size = 16))

Code for the Internet Example

From code for the Visualizing amounts chapter in Claus Wilke’s Fundamentals of Data Visualization.

Reading in the data:

base_url <- "http://api.worldbank.org/v2/en/indicator/"
if (file.exists("internet.xls")) {
    ## if we have to go with Excel
    ## xls_url <- paste0(base_url, "IT.NET.USER.ZS?downloadformat=excel")
    ## download.file(xls_url, "internet.xls")
    internet_raw <- readxl::read_xls("internet.xls",
                                     skip = 2, .name_repair = "universal")
    names(internet_raw) <- sub("\\.\\.\\.", "X", names(internet_raw))
} else {
    csvfile <- here("internet.csv")
    if (! file.exists(csvfile)) {
        csv_url <- paste0(base_url, "IT.NET.USER.ZS?downloadformat=csv")
        download.file(csv_url, "internet.zip")
        unzip("internet.zip")
        file.rename("API_IT.NET.USER.ZS_DS2_en_csv_v2_2255007.csv",
                    csvfile)
    }

    internet_raw <- read.csv(csvfile, skip = 4)
}

Reshape to longer format:

internet <- select(internet_raw, country = Country.Name, matches("X.+"))
internet <- pivot_longer(internet, -country,
                         names_to = "year", values_to = "users")
internet <- mutate(internet, year = as.integer(sub("X", "", year)))

Select some countries to include in the plot:

country_list <- c("United States", "China", "India", "Japan", "Algeria",
                  "Brazil", "Germany", "France", "United Kingdom", "Italy",
                  "New Zealand", "Canada", "Mexico", "Chile", "Argentina",
                  "Norway", "South Africa", "Kenya", "Israel", "Iceland")

internet_short <- filter(internet, country %in% country_list)
## internet_short <- mutate(internet_short,
##                          users = ifelse(is.na(users), 0, users))

Get ordering by 2017 levels:

intr17 <- filter(internet_short, year == 2017)
levs <- arrange(intr17, users)$country

The basic plot:

p_inet <- ggplot(filter(internet_short, year > 1993),
                 aes(x = year,
                     y = factor(country, levs),
                     fill = users)) +
    geom_tile(color = "white", size = 0.25)

Adjust color palette and guide:

p_inet <- p_inet +
    scale_fill_viridis_c(
        option = "A", begin = 0.05, end = 0.98,
        limits = c(0, 100),
        name = "internet users / 100 people",
        guide = guide_colorbar(
            direction = "horizontal",
            label.position = "bottom",
            title.position = "top",
            ticks = FALSE,
            barwidth = grid::unit(3.5, "in"),
            barheight = grid::unit(0.2, "in"),
            order = 1))

Adjust x and y scales:

p_inet <- p_inet +
    scale_x_continuous(expand = c(0, 0), name = NULL) +
    scale_y_discrete(name = NULL, position = "right")

Add layer for NA values:

p_inet <- p_inet +
    ggnewscale::new_scale_fill() +
    geom_tile(data = filter(internet_short, year > 1993, is.na(users)),
              aes(fill = "NA"), color = "white") +
    scale_fill_manual(values = "grey50") +
    guides(fill = guide_legend(title = "",
                               label.position = "bottom",
                               title.position = "top",
                               keyheight = grid::unit(0.2, "in"),
                               keywidth = grid::unit(0.2, "in"),
                               order = 2))

Final plot with title and theme adjustments:

p_inet +
    ggtitle("A Heat Map",
            "Countries ordered by percent internet users in 2017.") +
    theme(text = element_text(size = 16)) +
    theme(axis.line = element_blank(),
          axis.ticks = element_blank(),
          axis.ticks.length = grid::unit(1, "pt"),
          legend.position = "top",
          legend.justification = "left",
          legend.title.align = 0.5,
          legend.title = element_text(size = 12 * 12 / 14)
          )
LS0tCnRpdGxlOiAiRGF0YSBWaXN1YWxpemF0aW9uIGFuZCBEYXRhIFRlY2hub2xvZ2llcyIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGUgPSBGQUxTRX0Kc291cmNlKGhlcmU6OmhlcmUoInNldHVwLlIiKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5oZWlnaHQgPSA1LCBmaWcud2lkdGggPSA2LCBmaWcuYWxpZ24gPSAiY2VudGVyIikKb3B0aW9ucyhodG1sdG9vbHMuZGlyLnZlcnNpb24gPSBGQUxTRSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShoZXJlKQp0aGVtZV9zZXQodGhlbWVfbWluaW1hbCgpICsKICAgICAgICAgIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkgKwogICAgICAgICAgdGhlbWUocGFuZWwuYm9yZGVyID0gZWxlbWVudF9yZWN0KGNvbG9yID0gImdyZXkzMCIsIGZpbGwgPSBOQSkpKQpzZXQuc2VlZCgxMjM0NSkKYGBgCjxsaW5rIHJlbD0ic3R5bGVzaGVldCIgaHJlZj0ic3RhdDQ1ODAuY3NzIiB0eXBlPSJ0ZXh0L2NzcyIgLz4KCgojIEJhc2ljcwoKCiMjIE9iamVjdGl2ZXMKCkxlYXJuIGhvdyB0byBlZmZlY2l0dmVseSB1c2UgdmlzdWFsaXphdGlvbiBmb3IKCiogZXhwbG9yaW5nIGFuZCB1bmRlcnN0YW5kaW5nIGRhdGEKKiBjb21tdW5pY2F0aW5nIGFuZCBleHBsYWluaW5nIGluc2lnaHRzCgpMZWFybiBob3cgdG8gdXNlIGRhdGEgdGVjaG5vbG9naWVzIGZvcgoKKiBhY3F1aXJpbmcgZGF0YQoqIGNsZWFuaW5nIGRhdGEKKiBvcmdhbml6aW5nIGRhdGEKCkxlYXJuIGhvdyB0byBkbyB0aGlzIGluIHdheXMgdGhhdCBhcmUKCiogcmVwcm9kdWNpYmxlCiogcmV1c2FibGUKKiBzaGFyZWFibGUKCgojIyBUb3BpY3MKCkRhdGEgdmlzdWFsaXphdGlvbgoKKiBzb21lIGhpc3Rvcnkgb2YgdmlzdWFsaXphdGlvbgoqIGxlYXJuaW5nIHRoZSBiYXNpYyBncmFwaCB0eXBlcwoqIGhvdyB0byBjcmVhdGUgYmFzaWMgZ3JhcGhzIGluIFIKKiBodW1hbiBwZXJjZXB0aW9uLCBhbmQgaG93IGl0IGFmZmVjdHMgdmlzdWFsaXphdGlvbgoqIHVzaW5nIHVuZGVyc3RhbmRpbmcgb2YgcGVyY2VwdGlvbiB0byBndWlkZSBldmFsdWF0aW9uIGFuZCBkZXNpZ24KKiBkeW5hbWljIGFuZCBpbnRlcmFjdGl2ZSB2aXN1YWxpemF0aW9ucwoKRGF0YSB0ZWNobm9sb2dpZXMKCiogYmFzaWMgZGF0YSB0eXBlcwoqIHJlc2hhcGluZyBhbmQgdHJhbnNmb3JtaW5nIGRhdGEKKiBhZ2dyZWdhdGluZyBhbmQgc3VtbWFyaXppbmcgZGF0YQoqIG1lcmdpbmcgc2V2ZXJhbCBkYXRhIHNldHMKKiByZWd1bGFyIGV4cHJlc3Npb25zIGZvciBjbGVhbmluZyBkYXRhCiogaGFydmVzdGluZyBkYXRhIGZyb20gdGhlIHdlYgoKUmVwcm9kdWNpYmxlIHJlc2VhcmNoIGFuZCBjb2xsYWJvcmF0aW9uCgoqIGxpdGVyYXRlIHByb2dyYW1taW5nIGFuZCBkYXRhIGFuYWx5c2lzCiogdmVyc2lvbiBjb250cm9sIGZvciBjb2xsYWJvcmF0aW9uCgo8IS0tICFbXShpbWcvUjREUy1kYXRhLXNjaWVuY2UucG5nKSAtLT4KCgojIyBSZWNvbW1lbmRlZCBUZXh0IEJvb2tzCgo+IEtpZXJhbiBIZWFseSAoMjAxOCkgW19EYXRhIFZpc3VhbGl6YXRpb246IEEgcHJhY3RpY2FsCj4gaW50cm9kdWN0aW9uX10oaHR0cHM6Ly9zb2N2aXouY28vKSwgUHJpbmNldG9uCgo+IFBhdWwgTXVycmVsbCAoMjAwOSkuIFtfSW50cm9kdWN0aW9uIHRvIERhdGEKPiBUZWNobm9sb2dpZXNfXShodHRwOi8vd3d3LnN0YXQuYXVja2xhbmQuYWMubnovfnBhdWwvSXREVC8pLCBDaGFwbWFuCj4gJiBIYWxsL0NSQy4KCj4gSGFkbGV5IFdpY2toYW0gYW5kIEdhcnJldHQgR3JvbGVtdW5kICgyMDE2KSwgW19SIGZvciBEYXRhCj4gU2NpZW5jZV9dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKSwgTydSZWlsbHkuCgo+IENsYXVzIE8uIFdpbGtlICgyMDE5KSBbX0Z1bmRhbWVudGFscyBvZiBEYXRhCj4gVmlzdWFsaXphdGlvbl9dKGh0dHBzOi8vY2xhdXN3aWxrZS5jb20vZGF0YXZpei8pLCBP4oCZUmVpbGx5LAo+IEluYy4gKFtCb29rIHNvdXJjZSBvbgo+IEdpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2NsYXVzd2lsa2UvZGF0YXZpeik7IFtzdXBwb3J0aW5nCj4gbWF0ZXJpYWxzIG9uIEdpdEh1Yl0oaHR0cHM6Ly9naXRodWIuY29tL2NsYXVzd2lsa2UvZHZpei5zdXBwKSkKCgojIyBQcmVyZXF1aXNpdGVzCgpBbiBpbnRyb2R1Y3Rvcnkgc3RhdGlzdGljcyBjb3Vyc2UuCgpBIHJlZ3Jlc3Npb24gY291cnNlLgoKU3Ryb25nbHkgcmVjb21tZW5kZWQ6IFByaW9yIGV4cG9zdXJlIHRvIGJhc2ljIHVzZSBvZiBzdGF0aXN0aWNhbApwcm9ncmFtbWluZyBzb2Z0d2FyZSwgc3VjaCBhcyBSIG9yIFNBUywgYXMgb2J0YWluZWQgZnJvbSBhIHJlZ3Jlc3Npb24KY291cnNlLgoKCiMjIEFzc2Vzc21lbnQKClF1aXp6ZXMKCiogU2hvcnQgcXVpenplcyBxdWl6emVzIHdpbGwgYmUgcG9zdGVkIG9uICoqSUNPTioqIGFmdGVyIG1vc3QKICBsZWN0dXJlcy4KCkhvbWV3b3JrCgoqIEhvbWV3b3JrIGFzc2lnbm1lbnRzIHdpbGwgYmUgZHVlIGFwcHJveGltYXRlbHkgb25jZSBhIHdlZWsuCiogWW91IHdpbGwgdHlwaWNhbGx5IHN1Ym1pdCB5b3VyIHdvcmsgYnkgcHVzaGluZyBpdCB0byB5b3VyCiAgR2l0TGFiIHJlcG9zaXRvcnkgYnkgNTowMCBQTSBvbiB0aGUgZHVlIGRhdGUuCiogWW91ciBob21ld29yayBzb2x1dGlvbnMgc2hvdWxkIGJlIHdyaXR0ZW4gYXMgcmVwb3J0cywgdXNpbmcgcHJvcGVyCiAgc2VudGVuY2VzIGFuZCBwYXJhZ3JhcGhzIHRvIHByZXNlbnQgeW91ciByZXN1bHRzLgoKUHJvamVjdAoKKiBZb3Ugd2lsbCBkbyBhIHByb2plY3QgZGV2ZWxvcGluZyBhIHZpc3VhbCBhbmFseXNpcyBvZiBhIGRhdGEgc2V0CiAgb2YgeW91ciBjaG9vc2luZy4KKiBZb3UgY2FuIHdvcmsgb24geW91ciBvd24gb3IgaW4gYSBncm91cCBvZiB1cCB0byB0aHJlZSBzdHVkZW50cy4KKiBZb3VyIHByb2plY3Qgc2hvdWxkIHJlcHJlc2VudCBhYm91dCAxMCBob3VycyBvZiB3b3JrLgoqIEEgb25lIHBhZ2UgcHJvcG9zYWwgZm9yIHlvdXIgcHJvamVjdCBpcyBkdWUgb24gTW9uZGF5LCBNYXJjaCAyMC4KKiBBIGZpbmFsIHJlcG9ydCBvbiB5b3VyIHByb2plY3QgaXMgZHVlIG9uIEZyaWRheSwgTWF5IDUuCiogWW91ciBwcm9qZWN0IG1heSBiZSBzaGFyZWQgd2l0aCB0aGUgY2xhc3MgdGhyb3VnaCB0aGUgY2xhc3Mgd2ViCiAgcGFnZS4KCllvdXIgZ3JhZGUgd2lsbCBiZSBiYXNlZCBvbiBxdWl6emVzICgxMCUpLCBob21ld29yayAoNzAlKSBhbmQgdGhlCnByb2plY3QgKDIwJSkuCgoKIyMgVG9vbHMKCldlIHdpbGwgYmUgdXNpbmcKCiogW1JdKGh0dHBzOi8vd3d3LnItcHJvamVjdC5vcmcpIGZvciBjb21wdXRpbmcgYW5kIGdyYXBoaWNzCiogW1IgTWFya2Rvd25dKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBmb3IgY3JlYXRpbmcKICByZXByb2R1Y2libGUgcmVwb3J0cy4KKiBbYGdpdGBdKGh0dHBzOi8vZ2l0LXNjbS5jb20vKSBhbmQgdGhlCiAgW1VJIEdpdExhYl0oaHR0cHM6Ly9yZXNlYXJjaC1naXQudWlvd2EuZWR1KSBzZXJ2aWNlIGZvciByZXZpc2lvbiBjb250cm9sCgpZb3Ugd2lsbCBuZWVkIGFuIGVkaXRvciBvciBJREU7IHlvdSBjYW4gdXNlCgoqIFtSU3R1ZGlvXShodHRwczovL3Bvc2l0LmNvL3Byb2R1Y3RzL29wZW4tc291cmNlL3JzdHVkaW8vKSBmb3IgZWRpdGluZyBhbmQgbW9yZQoqIGFueSBvdGhlciBlZGl0b3Igb3IgSURFCgpUbyBbYWNjZXNzIHRoZXNlIHRvb2xzXShhY2Nlc3MuaHRtbCkgeW91IGNhbgoKKiB1c2UgdGhlIFtVSSBJREFTIFJTdHVkaW8gTm90ZWJvb2sKICBTZXJ2ZXJdKGh0dHBzOi8vbm90ZWJvb2tzLmhwYy51aW93YS5lZHUvc3RhdC00NTgwLTAwMDEvaHViL2hvbWUpLAoqIHVzZSB0aGUgQ0xBUyBMaW51eCBzeXN0ZW1zIHZpYSB0aGUgW0Zhc3RYIHJlbW90ZQogIGRlc2t0b3BdKGh0dHBzOi8vZmFzdHguZGl2bXMudWlvd2EuZWR1KSwKKiBvciBpbnN0YWxsIHlvdXIgb3duIG9uIHlvdXIgY29tcHV0ZXIKCkZvciBoZWxwIGluc3RhbGxpbmcgeW91ciBvd24gYSBnb29kIHBsYWNlIHRvIHN0YXJ0IGlzCjxodHRwczovL2hhcHB5Z2l0d2l0aHIuY29tLz4KCgojIyBGaXJzdCBTdGVwczogRG8gVGhpcyBUb2RheSEKClZpc2l0IHRoZSBbVUkgR2l0TGFiIHNpdGVdKGh0dHBzOi8vcmVzZWFyY2gtZ2l0LnVpb3dhLmVkdSkgYXQKPGh0dHBzOi8vcmVzZWFyY2gtZ2l0LnVpb3dhLmVkdT4gYW5kIGxvZyBpbiB3aXRoIHlvdXIgSGF3a0lELgoKTWFrZSBzdXJlIHlvdSBjYW4gYWNjZXNzIHRoZSBVSSBJREFTIFJzdHVkaW8gTm90ZWJvb2sgU2VydmVyIHdpdGgKeW91ciBIYXdrSUQgYW5kIHBhc3N3b3JkLgoKKiBUaGUgc2VydmVyIGlzIGF2YWlsYWJsZSBhdAoKICA8aHR0cHM6Ly9ub3RlYm9va3MuaHBjLnVpb3dhLmVkdS9zdGF0LTQ1ODAtMDAwMS9odWIvaG9tZT4uCgoqIElmIHlvdSBjYW5ub3QgbG9nIGludG8gdGhlIFJTdHVkaW8gc2VydmVyLCBwbGVhc2UgbGV0IHlvdXIgVEEKICBvciBtZSBrbm93IGltbWVkaWF0ZWx5LgoKTWFrZSBzdXJlIHlvdSBhcmUgYWJsZSB0byBsb2cgaW50byB0aGUgQ0xBUyBMaW51eCBzeXN0ZW1zIHdpdGggeW91cgpIYXdrSUQgYW5kIHBhc3N3b3JkLgoKKiBUaGUgZWFzaWVzdCB3YXkgaXMgdG8gdXNlIHRoZQogIFtGYXN0WF0oaHR0cHM6Ly9mYXN0eC5kaXZtcy51aW93YS5lZHUpIGNsaWVudCBhdAogIDxodHRwczovL2Zhc3R4LmRpdm1zLnVpb3dhLmVkdT4uCiogSWYgeW91IGNhbm5vdCBsb2cgaW50byB0aGUgQ0xBUyB3b3Jrc3RhdGlvbnMsIHBsZWFzZSBsZXQgeW91ciBUQQogIG9yIG1lIGtub3cgaW1tZWRpYXRlbHkuCgpMb29rIGF0IHRoZSBbYnJpZWYgaW50cm9kdWN0aW9uIHRvIGdpdF0oZ2l0Lmh0bWwpIG9yIHRoZSBiZWdpbm5pbmcKb2YgPGh0dHBzOi8vaGFwcHlnaXR3aXRoci5jb20+IHRvIHNlZSB3aGF0IGBnaXRgIGlzIGFib3V0IGFuZCBob3cgdG8KZ2V0IHN0YXJ0ZWQgd2l0aCBpdC4KCjwhLS0KKiBXb3JraW5nIHdpdGggdGhlIFVJIEdpdExhYiBmcm9tIFJTdHVkaW8gb3IgdGhlIGNvbW1hbmQgbGluZSB3aWxsIHVzZQogIFtTU0hdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1NlY3VyZV9TaGVsbCkgY29tbXVuaWNhdGlvbi4gWW91CiAgd2lsbCBuZWVkIHRvIGRvIGEgZmV3IHRoaW5ncyB0byBzZXQgdGhpcyB1cDoKICAKICAgICogU2V0IHVwIGFuIFNTSCBrZXkgYnkgZm9sbG93aW5nIFt0aGVzZQogICAgICBpbnN0cnVjdGlvbnNdKGh0dHBzOi8vaGFwcHlnaXR3aXRoci5jb20vc3NoLWtleXMuaHRtbCkuIEFsdGVybmF0ZQogICAgICBpbnN0dWN0aW9ucyBhcmUgW2F2YWlsYWJsZQogICAgICBoZXJlXShodHRwczovL3dpa2kudWlvd2EuZWR1L2Rpc3BsYXkvZ2l0aHViZG9jcy9DcmVhdGluZytTU0grS2V5cykuCiAgICAgIChGb3IgTGludXggZm9sbG93IHRoZSBNYWMgaW5zdHJ1Y3Rpb25zLikKICAgICogVG8gdXNlIFNTSCB0byBjb21tdW5pY2F0ZSB3aXRoIFVJIEdpdExhYiBmcm9tIG9mZi1jYW1wdXMgeW91CiAgICAgIHdpbGwgbmVlZCB0byB1c2UgYSBbVlBOIGNvbm5lY3Rpb25dKGh0dHBzOi8vaXRzLnVpb3dhLmVkdS92cG4pLAotLT4KCk1ha2Ugc3VyZSB5b3UgaGF2ZSBhY2Nlc3MgdG8gUiBhbmQgdHJ5IHNvbWV0aW5nIGxpa2UgdGhpczoKCmBgYHtyIGdleXNlciwgZXZhbCA9IEZBTFNFfQp3aXRoKGZhaXRoZnVsLAogICAgIHBsb3QoZXJ1cHRpb25zLCB3YWl0aW5nLAogICAgICAgICAgeGxhYiA9ICJFcnVwdGlvbiB0aW1lIChtaW4pIiwKICAgICAgICAgIHlsYWIgPSAiV2FpdGluZyB0aW1lIHRvIG5leHQgZXJ1cHRpb24gKG1pbikiKSkKYGBgCgpUaGUgcmVzdWx0IGlzIGEgcGxvdCB0aGF0IGxvb2tzIGxpa2UgdGhpczoKCmBgYHtyLCByZWYubGFiZWw9ImdleXNlciIsIGVjaG89RkFMU0V9CmBgYAoKCiMjIEdldHRpbmcgU2V0IFVwCgpMb2cgaW50byB0aGUgW1VJIEdpdExhYiBzaXRlXShodHRwczovL3Jlc2VhcmNoLWdpdC51aW93YS5lZHUpIGF0CjxodHRwczovL3Jlc2VhcmNoLWdpdC51aW93YS5lZHU+IHRvIGdldCB5b3VyIEdpdExhYiBhY2NvdW50CmFjdGl2YXRlZC4KCkRlY2lkZSB3aGVyZSB5b3Ugd2FudCB0byB3b3JrOgoKKiBbVUkgSURBUyBSU3R1ZGlvIE5vdGVib29rCiAgU2VydmVyXShodHRwczovL25vdGVib29rcy5ocGMudWlvd2EuZWR1L3N0YXQtNDU4MC0wMDAxL2h1Yi9ob21lKQoqIFtGYXN0WF0oaHR0cHM6Ly9jbGFzLnVpb3dhLmVkdS9saW51eC9oZWxwL2Zhc3R4KSBmb3IgYWNjZXNzaW5nCiAgdGhlIENMQVMgTGludXggc3lzdGVtcyB2aWEgdGhlCiAgW3dlYiBpbnRlcmZhY2VdKGh0dHBzOi8vZmFzdHguZGl2bXMudWlvd2EuZWR1KSBvciB0aGUgCiAgW2Rlc2t0b3AgY2xpZW50XShodHRwczovL2NsYXMudWlvd2EuZWR1L2xpbnV4L2hlbHAvZmFzdHgvZGVza3RvcCkuCiogWW91ciBvd24gY29tcHV0ZXIuCgpTZXR1cCBuZWVkZWQgZm9yIElEQVMgUlN0dWRpbyBTZXJ2ZXI6CgoqIElmIHlvdSBhcmUgcmVnaXN0ZXJlZCB0aGVuIHlvdSBzaG91bGQgaGF2ZSBhbiBhY2NvdW50CiAgbm93LiBJZiB5b3UgYWRkIHRoZSBjb3Vyc2UgbGF0ZSB5b3Ugc2hvdWxkIGhhdmUgYW4gYWNjb3VudAogIHdpdGhpbiBhIGRheS4KKiBbSW50cm9kdWNlIHlvdXJzZWxmIHRvIEdpdF0oaHR0cHM6Ly9oYXBweWdpdHdpdGhyLmNvbS9oZWxsby1naXQuaHRtbCkuCgpTZXR1cCBuZWVkZWQgZm9yIENMQVMgTGludXg6CgoqIEluc3RhbGwgdGhlIFtkZXNrdG9wCiAgIGNsaWVudF0oaHR0cHM6Ly9jbGFzLnVpb3dhLmVkdS9saW51eC9oZWxwL2Zhc3R4L2Rlc2t0b3ApIGlmIHlvdQogICB3YW50IHRvIHVzZSBpdC4gT3RoZXJ3aXNlLCB1c2UgdGhlIFt3ZWIKICAgaW50ZXJmYWNlXShodHRwczovL2Zhc3R4LmRpdm1zLnVpb3dhLmVkdSkuCiogWW91ciBhY2NvdW50IHdpbGwgYmUgc2V0IHVwIGF1dG9tYXRpY2FsbHkgdGhlIGZpcnN0IHRpbWUgeW91IGxvZyBpbi4KKiBbSW50cm9kdWNlIHlvdXJzZWxmIHRvIEdpdF0oaHR0cHM6Ly9oYXBweWdpdHdpdGhyLmNvbS9oZWxsby1naXQuaHRtbCkuCgo8IS0tCiogU2V0IHVwIGFuIFNTSCBrZXkgaWYgeW91IGRvbid0IGhhdmUgb25lIGFscmVhZHkuCiogQWRkIHlvdSBwdWJsaWMgU1NIIGtleSB0byB5b3VyIFVJIEdpdEh1YiBhY2NvdW50LgotLT4KClNldHRpbmcgdXAgeW91ciBvd24gY29tcHV0ZXI6IChBIGdvb2QgcmVzb3VyY2UgZm9yIGhlbHAgd2l0aCB0aGlzIGlzCjxodHRwczovL2hhcHB5Z2l0d2l0aHIuY29tPik6CgoqIEluc3RhbGwgdGhlIGN1cnJlbnQgdmVyc2lvbiBvZiBSLgogICAgKiBZb3UgbWlnaHQgaGF2ZSBvbGRlciB2ZXJzaW9ucyBmcm9tIG90aGVyIGNvdXJzZXMgKGUuZy4gZnJvbQogICAgICBbQW5hY29uZGFdKGh0dHBzOi8vd3d3LmFuYWNvbmRhLmNvbS8pKS4KICAgICogWW91IHdpbGwgbmVlZCB0byBhZGQgcGFja2FnZXMgYXMgd2UgZ28gYWxvbmcuCgoqIEluc3RhbGwgUlN0dWRpbyBpZiB5b3Ugd2FudCB0byB1c2UgaXQgKGhpZ2hseSByZWNvbW1lbmRlZCkuCgoqIEluc3RhbGwgR2l0LgoKKiBbSW50cm9kdWNlIHlvdXJzZWxmIHRvIEdpdF0oaHR0cHM6Ly9oYXBweWdpdHdpdGhyLmNvbS9oZWxsby1naXQuaHRtbCkuCgo8IS0tCiAgICAqIFNldCB1cCBhbiBTU0gga2V5IGlmIHlvdSBkb24ndCBoYXZlIG9uZSBhbHJlYWR5LgogICAgKiBBZGQgeW91IHB1YmxpYyBTU0gga2V5IHRvIHlvdXIgVUkgR2l0SHViIGFjY291bnQuCi0tPgoKRXZlbiBpZiB5b3UgZGVjaWRlIHRvIHVzZSB5b3VyIG93biBjb21wdXRlciB5b3Ugc2hvdWxkIG1ha2Ugc3VyZSB5b3UgY2FuIHVzZQp0aGUgUlN0dWRpbyBzZXJ2ZXIgb3IgQ0xBUyBzeXN0ZW1zIGFzIGEgYmFja3VwLgoKPCEtLQoKIyMgQ2hlY2tpbmcgYW5kIFNldHRpbmcgVXAgYW4gU1NIIEtleQoKKiBVc2luZyBSU3R1ZGlvOiBDaGVjay9jcmVhdGUgeW91ciBrZXkgYnkgZ29pbmcgdG8gKipUb29scyoqID4gKipHbG9iYWwqKiA+CiAgICAgICoqT3B0aW9ucyoqID4gKipHSVQvU1ZOKiouCgoqIEZyb20gYSBzaGVsbCBjb21tYW5kIGxpbmU6IHVzZSBgc3NoLWtleWdlbmAuCgoqIE9wZW4gdGVybWluYWwgb3Igc2hlbGwgYW5kIGRvOgogICAgYGBge2Jhc2gsIGV2YWwgPSBGQUxTRX0KY2F0IDxmaWxlbmFtZT4ucHViCiAgICBgYGAKICBhbmQgY29weSB0byB0aGUgY2xpcGJvYXJkLgoKKiBPbiB0aGUgW1VJIEdpdExhYiB3ZWIgcGFnZV0oaHR0cHM6Ly9yZXNlYXJjaC1naXQudWlvd2EuZWR1KSBnbyB0bwogIGA8eW91ciBpY29uIGF0IHJpZ2h0PmAgPiAqKlNldHRpbmdzKiogPiAqKlNTSCBLZXlzKiogYW5kIHBhc3RlCiAgaW4gdGhlIHB1YmxpYyBrZXkuCgoqIENoZWNrIHRoYXQgeW91IGNhbiBjbG9uZSB5b3VyIHJlcG9zaXRvcnkKCiAgICAqIFJTdHVkaW86IGdvIHRvICoqRmlsZSoqID4gKipOZXcgUHJvamVjdCoqID4gKipWZXJzaW9uIENvbnRyb2wqKiA+ICoqR2l0KiouCiAgICAqIEZyb20gYSBzaGVsbCBjb21tYW5kIGxpbmUgdXNlIGBnaXQgY2xvbmVgCgoqIE9uIExpbnV4IGFuZCBNYWMgT1MgeW91IGNhbiBhdm9pZCB0aGUgbmVlZCB0byB0eXBlIHlvdXIgcGFzcyBwaHJhc2UKICBtYW55IHRpbWVzIGJ5IGNhbGxpbmcgYHNzaC1hZGRgIGluIGEgc2hlbGwuIFRoaXMgc2hvdWxkIHJlbWVtYmVyCiAgeW91ciBwYXNzIHBocmFzZSB1bnRpbCB5b3UgbG9nIG91dC4KCiogSW4gcHJpbmNpcGxlIHRoaXMgaXMgcG9zc2libGUgb24gV2luZG93cywgYnV0IHRoZSBzZXR1cCBpcyBtb3JlCmNvbXBsaWNhdGVkLiAgVGhpcwpbbGlua10oaHR0cHM6Ly9oZWxwLmdpdGh1Yi5jb20vYXJ0aWNsZXMvd29ya2luZy13aXRoLXNzaC1rZXktcGFzc3BocmFzZXMvI3BsYXRmb3JtLXdpbmRvd3MpCnByb3ZpZGVzIHNvbWUgaGludHMuCi0tPgoKCiMgU29tZSBFeGFtcGxlcwoKCiMjIExpZmUgRXhwZWN0YW5jeSBpbiB0aGUgQW1lcmljYXMgaW4gMjAwNwoKVGhlIGRhdGEgaXMgZnJvbSB0aGUgW0dhcE1pbmRlcl0oaHR0cHM6Ly93d3cuZ2FwbWluZGVyLm9yZykgcHJvamVjdC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2FwbWluZGVyKQpsZV9hbV8yMDA3IDwtIGZpbHRlcihnYXBtaW5kZXIsIHllYXIgPT0gMjAwNywgY29udGluZW50ID09ICJBbWVyaWNhcyIpICU+JQogICAgbXV0YXRlKGNvdW50cnkgPSByZW9yZGVyKGNvdW50cnksIGxpZmVFeHApKQprbml0cjo6a2FibGUoc2VsZWN0KGxlX2FtXzIwMDcsIGNvdW50cnksIGxpZmVFeHApLAogICAgICAgICAgICAgY29sLm5hbWVzID0gYygiQ291bnRyeSIsICJMaWZlIEV4cGVjdGFuY3kgKHllYXJzKSIpLAogICAgICAgICAgICAgZGlnaXRzID0gMSwgZm9ybWF0ID0gImh0bWwiKSAlPiUKICAgIGthYmxlRXh0cmE6OmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSAic3RyaXBlZCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZ1bGxfd2lkdGggPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9udF9zaXplID0gMTQpICU+JQogICAga2FibGVFeHRyYTo6c2Nyb2xsX2JveChoZWlnaHQgPSAiMzAwcHgiLCB3aWR0aCA9ICI3NSUiKQpgYGAKCkEgX2RvdCBwbG90XzoKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxMCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CnRobSA8LSB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCmdncGxvdChsZV9hbV8yMDA3LCBhZXMoeSA9IGNvdW50cnksIHggPSBsaWZlRXhwKSkgKwogICAgZ2VvbV9wb2ludChmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICAgIGxhYnMoeCA9ICJMaWZlIEV4cGVjdGFuY3kgKHllYXJzKSIsIHkgPSBOVUxMKSArCiAgICB0aG0gKyBnZ3RpdGxlKCJEb3QgUGxvdCIpCmBgYAoKQSBfYmFyIGNoYXJ0XzoKCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxMCwgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSJ9CmdncGxvdChsZV9hbV8yMDA3LCBhZXMoeCA9IGxpZmVFeHAsIHkgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9jb2woZmlsbCA9ICJsaWdodGJsdWUiKSArCiAgICBsYWJzKHggPSAiTGlmZSBFeHBlY3RhbmN5ICh5ZWFycykiLCB5ID0gTlVMTCkgKwogICAgdGhtICsgZ2d0aXRsZSgiQmFyIENoYXJ0IikKYGBgCgpBbm90aGVyIChiYWQhKSAgX2JhciBjaGFydF86CgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpiYXNlbGluZSA8LSA2MAp0aWNrcyA8LSBjKDAsIDEwLCAyMCwgMzApCmdncGxvdChsZV9hbV8yMDA3LCBhZXMoeCA9IGxpZmVFeHAgLSBiYXNlbGluZSwgeSA9IGNvdW50cnkpKSArCiAgICBnZW9tX2NvbChmaWxsID0gImxpZ2h0Ymx1ZSIpICsKICAgIGxhYnMoeCA9ICJMaWZlIEV4cGVjdGFuY3kgKHllYXJzKSIsIHkgPSBOVUxMKSArCiAgICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gdGlja3MsIGxhYmVscyA9IHRpY2tzICsgYmFzZWxpbmUpICsKICAgIHRobSArIGdndGl0bGUoIkFub3RoZXIgQmFyIENoYXJ0IikKYGBgCgpXZSB3aWxsIGxvb2sgYXQ6CgoqIEhvdyB0byBjcmVhdGUgdGhlc2Ugdmlld3MgdXNpbmcgY29kZSB0aGF0IG1ha2VzIHRoZW0gZWFzaWx5IHJlcHJvZHVjaWJsZS4KCiogSG93IHRvIGFzc2VzcyB0aGVpciBhZHZhbnRhZ2VzIGFuZCBkaXNhZHZhbnRhZ2VzIGFzIHZpc3VhbAogIHJlcHJlc2VudGF0aW9ucyBvZiB0aGUgZGF0YQoKQSBkYXRhIHNldCB3aXRoIG1vcmUgdmFyaWFibGVzIGZvciBtb3JlIGNvdW50cmllcyBhbmQgeWVhcnMgaXMKYXZhaWxhYmxlIGluIHRoZSBgZ2FwbWluZGVyYCBSIHBhY2thZ2UuCgpEYXRhIHByZXBhcmF0aW9uIHN0ZXBzOgoKKiBfRmlsdGVyXyB0aGUgbGFyZ2VyIGRhdGEgc2V0IGRvd24gdG8gdGhlIGNvdW50cmllcyBhbmQgeWVhciB3ZSB3YW50LgoKKiBfU2VsZWN0XyB0aGUgY291bnRyeSBuYW1lIGFuZCBsaWZlIGV4cGVjdGFuY3kgdmFyaWFibGVzLgoKV2Ugd2lsbCBsb29rIGF0IGhvdyB0byBjYXJyeSBvdXQgdGhlc2Ugc3RlcHMgd2l0aCByZXByb2R1Y2libGUgY29kZS4KCgojIyBZZWFybHkgU25vd2ZhbGwgaW4gSW93YSBDaXR5Cgo8ZGl2IGlkPSJpYy1zbm93ZmFsbC1leGFtcGxlIj48L2Rpdj4KCkhvdyBkaWQgdGhlIHdpbnRlciBvZiAyMDE4LzkgY29tcGFyZSB0byBvdGhlciB5ZWFycz8KCmBgYHtyIHNub3dmYWxsLCBlY2hvID0gRkFMU0UsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTB9CmBgYAoKVGhlIGRhdGEgYXJlIGF2YWlsYWJsZSBmcm9tIGEgW05PQUEgd2ViIHNlcmljZQpBUEldKGh0dHBzOi8vd3d3Lm5jZGMubm9hYS5nb3YvY2RvLXdlYi93ZWJzZXJ2aWNlcy92MikgYXMgYQpbX0NTVl9dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvbW1hLXNlcGFyYXRlZF92YWx1ZXMpIGZpbGUuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpoZWFkKHNlbGVjdChpY19kYXRhX3JhdywgeWVhciwgbW9udGgsIGVsZW1lbnQsIHN0YXJ0c193aXRoKCJWQUxVRSIpKSkKYGBgCgpEYXRhIHByZXBhcmF0aW9uIHN0ZXBzOgoKKiBSZWFkIGluIHRoZSBDU1YgZmlsZS4KCiogX1Jlc2hhcGVfIHRoZSBkYXRhIHRvIGhhdmUgY29sdW1ucyBgZGF0ZWAsIGBUTUFYYCwgYFRNSU5gLCBgU05PV2AgYW5kIGBQUkNQYC4KCiogX0ZpbHRlcl8gb3V0IGJvZ3VzIGRhdGVzIGNyZWF0ZWQgYnkgdGhlIG9yaWdpbmFsIGZvcm1hdC4KCiogX0NvbnZlcnRfIHVuaXRzIHRvIG1vcmUgc3RhbmRhcmQgKEFtZXJpY2FuKSBvbmVzIChlLmcuIG1pbGltZXRlcnMgdG8gaW5jaGVzKS4KCiogLi4uCgpDb2RlIGlzIGF2YWlsYWJsZSBbaGVyZV0oI2ljLXNub3dmYWxsLWV4YW1wbGUtY29kZSkuCgoKIyMgSW50ZXJuZXQgQWRvcHRpb24gQWNyb3NzIHRoZSBXb3JsZAoKPGRpdiBpZD0iaW50ZXJuZXQtZXhhbXBsZSI+PC9kaXY+CgpBbiBleGFtcGxlIGZyb20gW1dpbGtlICgyMDE5KV0oaHR0cHM6Ly9jbGF1c3dpbGtlLmNvbS9kYXRhdml6Lykgd2l0aApbV29ybGQgQmFuawpkYXRhXShodHRwczovL2RhdGEud29ybGRiYW5rLm9yZy9pbmRpY2F0b3IvSVQuTkVULlVTRVIuWlMpLgoKYGBge3IgaW50ZXJuZXQtaGVhdG1hcCwgZWNobyA9IEZBTFNFLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTB9CmBgYAoKVGhlIGRhdGEgYXJlIGF2YWlsYWJsZSBpbiBzZXZlcmFsIGZvcm1hdHMgKENTViwgWE1MLCBFeGNlbCkuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQpoZWFkKGFzX3RpYmJsZShpbnRlcm5ldF9yYXcpKQpgYGAKCkRhdGEgcHJlcGFyYXRpb246CgoqIFJlYWQgaW4gdGhlIGRhdGEuCgoqIF9GaWx0ZXJfIGRvd24gdG8gdGhlIGNvdW50cmllcyB3ZSB3YW50LgoKKiBfUmVzaGFwZV8gdG8gaGF2ZSBjb2x1bW5zIGBjb3VudHJ5YCwgYHllYXJgLCBhbmQgYHVzZXJzYC4gCgoqIC4uLgoKQ29kZSBpcyBhdmFpbGFibGUgW2hlcmVdKCNpbnRlcm5ldC1leGFtcGxlLWNvZGUpLgoKCiMjIElvd2EgV2luZCBUdXJiaW5lcwoKRGF0YSBpcyBhdmFpbGFibGUgZnJvbSB0aGUgW1UuUy4gV2luZCBUdXJiaW5lCkRhdGFiYXNlXShodHRwczovL2VlcnNjbWFwLnVzZ3MuZ292L3Vzd3RkYi8pLgoKYGBge3IsIGZpZy5oZWlnaHQgPSA2LCBmaWcud2lkdGggPSAxMCwgbWVzc2FnZSA9IEZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIn0KbGlicmFyeShzZikKZGF0YShVU19jb3VudGllc19nZW9tcywgcGFja2FnZSA9ICJkdml6LnN1cHAiKQpkYXRhKHdpbmRfdHVyYmluZXMsIHBhY2thZ2UgPSAiZHZpei5zdXBwIikKCnNmX3d0IDwtIHN0X2FzX3NmKHdpbmRfdHVyYmluZXMsIGNvb3JkcyA9IGMoInhsb25nIiwgInlsYXQiKSwgY3JzID0gNDMyNikKc2Zfd3RfSUEgPC0gZmlsdGVyKHNmX3d0LCB0X3N0YXRlID09ICJJQSIpCnNmX3d0X0lBIDwtIG11dGF0ZShzZl93dF9JQSwKICAgICAgICAgICAgICAgICAgIHBfeWVhciA9IGlmZWxzZShwX3llYXIgPiAwLCBwX3llYXIsIE5BKSwKICAgICAgICAgICAgICAgICAgIHllYXIgPSBjdXQocF95ZWFyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsIDIwMDUsIDIwMTAsIDIwMTUsIDIwMjApLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCJiZWZvcmUgMjAwNSIsICIyMDA1LTIwMDkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyMDEwLTIwMTQiLCAiMjAxNS0yMDIwIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJpZ2h0ID0gRkFMU0UpKQoKZ2dwbG90KGZpbHRlcihVU19jb3VudGllc19nZW9tcyRsb3dlcjQ4LCBTVEFURUZQID09IDE5KSkgKwogICAgZ2VvbV9zZigpICsKICAgIGdlb21fc2YoZGF0YSA9IHNmX3d0X0lBLCBhZXMoZmlsbCA9IHllYXIsIGNvbG9yID0geWVhciksIHNoYXBlID0gMjEpICsKICAgIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGRpcmVjdGlvbiA9IC0xLCBuYS52YWx1ZSA9ICJyZWQiKSArCiAgICBnZ3RoZW1lczo6dGhlbWVfbWFwKCkgKwogICAgZ2d0aXRsZSgiQSBNYXAiKSArCiAgICB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiksCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCmBgYApgYGB7ciwgZXZhbCA9IEZBTFNFLCBlY2hvID0gRkFMU0V9CiMjIFRoaXMgZmFpbHMgd2l0aCBjb2xvciA9IE5BCmdncGxvdChmaWx0ZXIoVVNfY291bnRpZXNfZ2VvbXMkbG93ZXI0OCwgU1RBVEVGUCA9PSAxOSkpICsKICAgIGdlb21fc2YoKSArCiAgICBnZW9tX3NmKGRhdGEgPSBzZl93dF9JQSwgYWVzKGZpbGwgPSB5ZWFyKSwgc2hhcGUgPSAyMSwgY29sb3IgPSBOQSkgKwogICAgc2NhbGVfZmlsbF92aXJpZGlzX2QoZGlyZWN0aW9uID0gLTEsIG5hLnZhbHVlID0gInJlZCIpICsKICAgIGdndGhlbWVzOjp0aGVtZV9tYXAoKSArCiAgICBnZ3RpdGxlKCJBIE1hcCIpICsKICAgIHRoZW1lKGxlZ2VuZC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAidHJhbnNwYXJlbnQiKSwKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE2KSkKYGBgCgpUaGVyZSBhcmUgdHdvIGRhdGEgc2V0czoKCiogX1NoYXBlXyBpbmZvcm1hdGlvbiBmb3IgZHJhd2luZyB0aGUgbWFwLgoKKiBEYXRhIG9uIGluZGl2aWR1YWwgd2luZCB0dXJiaW5lcy4KCkRhdGEgcHJlcGFyYXRpb246CgoqIFJlYWQgaW4gdGhlIGRhdGEuCgoqIE1hdGNoIHVwIHRoZSBfcHJvamVjdGlvbl8gdXNlZCBmb3IgdGhlIG1hcCBhbmQgbG9jYXRpb24gZGF0YS4KCiogLi4uCgoKIyMgSW93YSBQb3B1bGF0aW9uIGluIDIwMTAKCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CiMjIFRoaXMgaXMgd291bGQgYmUgbmVlZGVkIGlmIHdlIGRpZG4ndCBoYXZlIGEgbGlicmFyeShzZikgZWFybGllciBzbwojIyB0aGF0IHRoZSBmaWx0ZXIgb3BlcmF0aW9uIGRvZXMgdGhlIHJpZ2h0IHRoaW5nIEkgdGhpbmsKIyMgbG9hZE5hbWVzcGFjZSgic2YiKQpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0ID0gNiwgZmlnLndpZHRoID0gMTAsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUifQpkYXRhKFVTX2NlbnN1cywgcGFja2FnZSA9ICJkdml6LnN1cHAiKQp1c2NvdW50aWVzIDwtIG11dGF0ZShVU19jb3VudGllc19nZW9tcyRsb3dlcjQ4LAogICAgICAgICAgICAgICAgICAgICBGSVBTID0gYXMubnVtZXJpYyhwYXN0ZTAoU1RBVEVGUCwgQ09VTlRZRlApKSkKdXNjb3VudGllcyA8LSBsZWZ0X2pvaW4odXNjb3VudGllcywKICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0KFVTX2NlbnN1cywgRklQUywgcG9wMjAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgICJGSVBTIikKZ2dwbG90KGZpbHRlcih1c2NvdW50aWVzLCBTVEFURUZQID09IDE5KSkgKwogICAgZ2VvbV9zZihhZXMoZmlsbCA9IHBvcDIwMTApKSArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYyhkaXJlY3Rpb24gPSAtMSwgdHJhbnMgPSAibG9nMTAiLAogICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKwogICAgZ2d0aXRsZSgiQSBDaG9yb3BsZXRoIE1hcCIpICsKICAgIGdndGhlbWVzOjp0aGVtZV9tYXAoKSArCiAgICB0aGVtZShsZWdlbmQuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gInRyYW5zcGFyZW50IiksCiAgICAgICAgICBwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCmBgYAoKQWdhaW4gdGhlcmUgYXJlIHR3byBkYXRhIHNldHM6CgoqIFNoYXBlIGRhdGEgZm9yIGRyYXdpbmcgdGhlIG1hcC4KCiogQ291bnR5IHBvcHVsYXRpb24gZGF0YSBmcm9tIHRoZSAyMDEwIGNlbnN1cy4KCkRhdGEgcHJlcGFyYXRpb246CgoqIC4uLgoKKiBfTWVyZ2VfIG9yIF9qb2luXyB0aGUgcG9wdWxhdGlvbiBkYXRhIHdpdGggdGhlIHNoYXBlIGRhdGEuCgoqIC4uLgoKCiMgUmVwcm9kdWNpYmlsaXR5CgoKIyMgUmVwcm9kdWNpYmxlIFJlcG9ydHMgYW5kIEFuYWx5c2VzCgpQcmVwYXJpbmcgYSByZXBvcnQgb24gYSBkYXRhIGFuYWx5c2lzIHByb2plY3QgdXN1YWxseSBpbnZvbHZlcwoKKiByZWFkaW5nIHRoZSBkYXRhCgoqIF93cmFuZ2xpbmdfIHRoZSBkYXRhIGludG8gdXNhYmxlIGZvcm0KCiogdmlzdWFsaXppbmcsIHN1bW1hcml6aW5nLCBhbmQgbW9kZWxpbmcKCiogd3JpdGluZyBhIHJlcG9ydCB0aGF0IGluY2x1ZGVzIHlvdXIgcmVzdWx0cwoKVG8gbWFrZSB5b3VyIHdvcmsgcmVwcm9kdWNpYmxlIGZvciBzb21lb25lIGVsc2UsIG9yIGZvciB5b3Ugd2hlbiB0aGUKZGF0YSBjaGFuZ2VzLCBpdCBpcyBiZXN0IHRvIHVzZSBjb2RlIGZvciB0aGUgZW50aXJlIHdvcmtmbG93LgoKW19SIE1hcmtkb3duX10oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBpcyBvbmUgdGVjaG5vbG9neSB0aGF0CnN1cHBvcnRzIHRoaXMuCgoKIyMgVG9vbHMgZm9yIFJlcHJvZHVjaWJpbGl0eQoKUiBNYXJrZG93biBmaWxlcyBjb250YWluIHJlcG9ydCB0ZXh0IGFsb25nIHdpdGggY29kZSB0byBwcm9kdWNlCm51bWVyaWNhbCBhbmQgZ3JhcGhpY2FsIHJlc3VsdHMuCgpUb29scyBhcmUgYXZhaWxhYmxlIHRvCgoqIGNvbnZlcnQgYW4gUiBNYXJrZG93biBmaWxlIGludG8gYSBQREYgb3IgSFRNTCByZXBvcnQ7CgoqIGV4dHJhY3QgdGhlIGNvZGUgdXNlZCB0byBwcm9kdWNlIHRoZSBjb21wdXRhdGlvbmFsIGFuZCBncmFwaGljYWwgcmVzdWx0cy4KCmBgYHtyLCBpbmNsdWRlID0gRkFMU0V9CmRvY3dvcmRzIDwtIGlmICh1c2luZ194YXJpbmdhbikgIlRoZXNlIHNsaWRlcyB3ZXJlIiBlbHNlICJUaGlzIHBhZ2Ugd2FzIgpgYGAKYHIgZG9jd29yZHNgIGdlbmVyYXRlZCBmcm9tIHRoZSBSIE1hcmtkb3duIGZpbGUgW2ludHJvLlJtZF0oaW50cm8uUm1kKS4KCllvdSB3aWxsIGJlIGNyZWF0aW5nIFIgTWFya2Rvd24gZmlsZXMgbGlrZSB0aGlzIGZvciB5b3VyIGhvbWV3b3JrIGFuZApwcm9qZWN0LgoKU29tZSBSIE1hcmtkb3duIHR1dG9yaWFsczoKCiogW1IgTWFya2Rvd246IFRoZSBEZWZpbml0aXZlIEd1aWRlXShodHRwczovL2Jvb2tkb3duLm9yZy95aWh1aS9ybWFya2Rvd24vKQogIGJ5IFlpaHVpIFhpZSBpcyBhIGJvb2stbGVuZ3RoIHByZXNlbnRhdGlvbi4KCiogVGhlIFtSIE1hcmtkb3duIEhvbWUgUGFnZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20pIGhhcyBhIGxpbmsKICB0byBhIFt0dXRvcmlhbF0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vbGVzc29uLTEuaHRtbCkuCgoKIyBDb2RlCgoKIyMgQ29kZSBmb3IgdGhlIFtJb3dhIENpdHkgU25vd2ZhbGwgRXhhbXBsZV0oI2ljLXNub3dmYWxsLWV4YW1wbGUpCgo8ZGl2IGlkPSJpYy1zbm93ZmFsbC1leGFtcGxlLWNvZGUiPjwvZGl2PgoKUmVhZCB0aGUgZGF0YToKCmBgYHtyIHNub3dmYWxsLXJlYWQsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobHVicmlkYXRlKQppZiAoISBmaWxlLmV4aXN0cyhoZXJlKCJpY19ub2FhLmNzdi5neiIpKSkKICAgIGRvd25sb2FkLmZpbGUoImh0dHA6Ly93d3cuc3RhdC51aW93YS5lZHUvfmx1a2UvZGF0YS9pY19ub2FhLmNzdi5neiIsCiAgICAgICAgICAgICAgICAgIGhlcmUoImljX25vYWEuY3N2Lmd6IikpCmljX2RhdGFfcmF3IDwtIGFzX3RpYmJsZShyZWFkLmNzdihoZXJlKCJpY19ub2FhLmNzdi5neiIpLCBoZWFkID0gVFJVRSkpCmBgYAoKUmVzaGFwZSBmcm9tICh2ZXJ5KSB3aWRlIHRvICh0b28pIGxvbmc6CgpgYGB7ciBzbm93ZmFsbC13aWRlLXRvLWxvbmcsIGV2YWwgPSBGQUxTRX0KaWNfZGF0YSA8LSBzZWxlY3QoaWNfZGF0YV9yYXcsIHllYXIsIG1vbnRoLCBlbGVtZW50LCBzdGFydHNfd2l0aCgiVkFMVUUiKSkKaWNfZGF0YSA8LSBwaXZvdF9sb25nZXIoaWNfZGF0YSwKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiZGF5IiwKICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gInZhbHVlIiwgYyhWQUxVRTEgOiBWQUxVRTMxKSkKYGBgCgpFeHRyYWN0IHRoZSBkYXkgYXMgYSBudW1iZXI6CgpgYGB7ciBzbm93ZmFsbC1leHRyYWN0LWRheSwgZXZhbCA9IEZBTFNFfQppY19kYXRhIDwtIG11dGF0ZShpY19kYXRhLCBkYXkgPSBhcy5pbnRlZ2VyKHN1YigiVkFMVUUiLCAiIiwgZGF5KSkpCmBgYAoKUmVzaGFwZSBmcm9tIHRvbyBsb25nIHRvIHRpZHkgd2l0aCBvbmUgcm93IHBlciBkYXksIGtlZXBpbmcgb25seSB0aGUKcHJpbWFyeSB2YXJpYWJsZXM6CgpgYGB7ciBzbm93ZmFsbC1sb25nLXRvLXRpZHksIGV2YWwgPSBGQUxTRX0KY29yZXZhcnMgPC0gYygiVE1BWCIsICJUTUlOIiwgIlBSQ1AiLCAiU05PVyIsICJTTldEIikKaWNfZGF0YSA8LSBmaWx0ZXIoaWNfZGF0YSwgZWxlbWVudCAlaW4lIGNvcmV2YXJzKQppY19kYXRhIDwtIHBpdm90X3dpZGVyKGljX2RhdGEsIG5hbWVzX2Zyb20gPSAiZWxlbWVudCIsIHZhbHVlc19mcm9tID0gInZhbHVlIikKYGBgCgpBZGQgYSBkYXRlIHZhcmlhYmxlIGZvciBwbG90dGluZyBhbmQgdG8gaGVscCBnZXQgcmlkIG9mIGJvZ3VzIGRheXM6CgpgYGB7ciBzbm93ZmFsbC1hZGQtZGF0ZSwgZXZhbCA9IEZBTFNFfQppY19kYXRhIDwtIG11dGF0ZShpY19kYXRhLCBkYXRlID0gbHVicmlkYXRlOjptYWtlX2RhdGUoeWVhciwgbW9udGgsIGRheSkpCmljX2RhdGEgPC0gZmlsdGVyKGljX2RhdGEsICEgaXMubmEoZGF0ZSkpCmBgYAoKTWFrZSB1bml0cyBtb3JlIHN0YW5kYXJkIChBbWVyaWNhbik6CgpgYGB7ciBzbm93ZmFsbC1maXgtdW5pdHMsIGV2YWwgPSBGQUxTRX0KbW0yaW4gPC0gZnVuY3Rpb24oeCkgeCAvIDI1LjQKQzJGIDwtIGZ1bmN0aW9uKHgpIDMyICsgMS44ICogeAppY19kYXRhIDwtIHRyYW5zbXV0ZShpY19kYXRhLCB5ZWFyLCBtb250aCwgZGF5LCBkYXRlLAogICAgICAgICAgICAgICAgICAgICBQUkNQID0gbW0yaW4oUFJDUCAvIDEwKSwKICAgICAgICAgICAgICAgICAgICAgU05PVyA9IG1tMmluKFNOT1cpLAogICAgICAgICAgICAgICAgICAgICBTTldEID0gbW0yaW4oU05XRCksCiAgICAgICAgICAgICAgICAgICAgIFRNSU4gPSBDMkYoVE1JTiAvIDEwKSwKICAgICAgICAgICAgICAgICAgICAgVE1BWCA9IEMyRihUTUFYIC8gMTApKQpgYGAKCkFkZCBhIE1vbnRoIGZhY3RvciB3aXRoIGFiYnJldmlhdGVkIGxldmVsczoKCmBgYHtyIHNub3dmYWxsLWFkZC1tb250aCwgZXZhbCA9IEZBTFNFfQppY19kYXRhIDwtIG11dGF0ZShpY19kYXRhLAogICAgICAgICAgICAgICAgICBNb250aCA9IGx1YnJpZGF0ZTo6bW9udGgobW9udGgsIGxhYmVsID0gVFJVRSwgYWJiciA9IFRSVUUpKQpgYGAKCkFzc29jaWF0ZSBKYW51YXJ5IHRocm91Z2ggSnVuZSB3aXRoIHRoZSB3aW50ZXIgc3RhcnRpbmcgaW4gdGhlIHByZXZpb3VzIHllYXI6CgpgYGB7ciBzbm93ZmFsbC13eWVhciwgZXZhbCA9IEZBTFNFfQppY19kYXRhIDwtIG11dGF0ZShpY19kYXRhLCB3eWVhciA9IGlmZWxzZShtb250aCA8PSA2LCB5ZWFyIC0gMSwgeWVhcikpCmBgYAoKQ29tcHV0ZSB0aGUgd2ludGVyIHRvdGFscyBhbmQgdGhlIHRvdGFsIGZvciB0aGUgMjAxOC85IHdpbnRlcjoKCmBgYHtyIHNub3dmYWxsLXdpbnRlci10b3RhbHMsIGV2YWwgPSBGQUxTRX0KaWNfc25vdyA8LSBncm91cF9ieShpY19kYXRhLCB3eWVhcikgJT4lCiAgICBzdW1tYXJpemUoc25vdyA9IHN1bShTTk9XLCBuYS5ybSA9IFRSVUUpKQppY19zbm93XzIwMTggPC0gZmlsdGVyKGljX3Nub3csIHd5ZWFyID09IDIwMTgpJHNub3cKYGBgCgpDcmVhdGUgdGhlIGhpc3RvZ3JhbSBhbmQgc2hvdyB0aGUgMjAxOC85IHRvdGFsOgoKYGBge3Igc25vd2ZhbGwtaGlzdG9ncmFtLCBldmFsID0gRkFMU0V9CmdncGxvdChpY19zbm93KSArCiAgICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IHNub3cpLCBiaW5zID0gMTUsIGZpbGwgPSAiZ3JleSIsIGNvbG9yID0gImJsYWNrIikgKwogICAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gaWNfc25vd18yMDE4LCBjb2xvciA9ICJyZWQiLCBzaXplID0gMikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKG5hbWUgPSAiVG90YWwgQW5udWFsIFNub3dmYWxsIChpbmNoZXMpIiwKICAgICAgICAgICAgICAgICAgICAgICBzZWMuYXhpcyA9IGR1cF9heGlzKG5hbWUgPSBOVUxMLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gaWNfc25vd18yMDE4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gIjIwMTgvOSIpKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gZXhwYW5zaW9uKDAsIDApKSArCiAgICBnZ3RpdGxlKCJBIEhpc3RvZ3JhbSIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpCmBgYAoKPCEtLSAjIyBub2xpbnQgc3RhcnQgLS0+CmBgYHtyIHNub3dmYWxsLCBldmFsID0gRkFMU0UsIGVjaG8gPSBGQUxTRX0KbGlicmFyeShsdWJyaWRhdGUpCjw8c25vd2ZhbGwtcmVhZD4+Cgo8PHNub3dmYWxsLXdpZGUtdG8tbG9uZz4+Cgo8PHNub3dmYWxsLWV4dHJhY3QtZGF5Pj4KCjw8c25vd2ZhbGwtbG9uZy10by10aWR5Pj4KCjw8c25vd2ZhbGwtYWRkLWRhdGU+PgoKPDxzbm93ZmFsbC1maXgtdW5pdHM+PgoKPDxzbm93ZmFsbC1hZGQtbW9udGg+PgoKPDxzbm93ZmFsbC13eWVhcj4+Cgo8PHNub3dmYWxsLXdpbnRlci10b3RhbHM+PgoKPDxzbm93ZmFsbC1oaXN0b2dyYW0+PgpgYGAKPCEtLSAjIyBub2xpbnQgZW5kIC0tPgoKCiMjIENvZGUgZm9yIHRoZSBbSW50ZXJuZXQgRXhhbXBsZV0oI2ludGVybmV0LWV4YW1wbGUpCgo8ZGl2IGlkPSJpbnRlcm5ldC1leGFtcGxlLWNvZGUiPjwvZGl2PgoKRnJvbQpbY29kZV0oaHR0cHM6Ly9naXRodWIuY29tL2NsYXVzd2lsa2UvZGF0YXZpei9ibG9iL21hc3Rlci92aXN1YWxpemluZ19hbW91bnRzLlJtZCkKZm9yIHRoZSBbX1Zpc3VhbGl6aW5nIGFtb3VudHNfCmNoYXB0ZXJdKGh0dHBzOi8vY2xhdXN3aWxrZS5jb20vZGF0YXZpei92aXN1YWxpemluZy1hbW91bnRzLmh0bWwpIGluCkNsYXVzIFdpbGtlJ3MgW0Z1bmRhbWVudGFscyBvZiBEYXRhClZpc3VhbGl6YXRpb25dKGh0dHBzOi8vY2xhdXN3aWxrZS5jb20vZGF0YXZpei9pbmRleC5odG1sKS4KClJlYWRpbmcgaW4gdGhlIGRhdGE6CgpgYGB7ciBpbnRlcm5ldC1oZWF0bWFwLXJlYWRpbmctZGF0YSwgZXZhbCA9IEZBTFNFfQpiYXNlX3VybCA8LSAiaHR0cDovL2FwaS53b3JsZGJhbmsub3JnL3YyL2VuL2luZGljYXRvci8iCmlmIChmaWxlLmV4aXN0cygiaW50ZXJuZXQueGxzIikpIHsKICAgICMjIGlmIHdlIGhhdmUgdG8gZ28gd2l0aCBFeGNlbAogICAgIyMgeGxzX3VybCA8LSBwYXN0ZTAoYmFzZV91cmwsICJJVC5ORVQuVVNFUi5aUz9kb3dubG9hZGZvcm1hdD1leGNlbCIpCiAgICAjIyBkb3dubG9hZC5maWxlKHhsc191cmwsICJpbnRlcm5ldC54bHMiKQogICAgaW50ZXJuZXRfcmF3IDwtIHJlYWR4bDo6cmVhZF94bHMoImludGVybmV0LnhscyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBza2lwID0gMiwgLm5hbWVfcmVwYWlyID0gInVuaXZlcnNhbCIpCiAgICBuYW1lcyhpbnRlcm5ldF9yYXcpIDwtIHN1YigiXFwuXFwuXFwuIiwgIlgiLCBuYW1lcyhpbnRlcm5ldF9yYXcpKQp9IGVsc2UgewogICAgY3N2ZmlsZSA8LSBoZXJlKCJpbnRlcm5ldC5jc3YiKQogICAgaWYgKCEgZmlsZS5leGlzdHMoY3N2ZmlsZSkpIHsKICAgICAgICBjc3ZfdXJsIDwtIHBhc3RlMChiYXNlX3VybCwgIklULk5FVC5VU0VSLlpTP2Rvd25sb2FkZm9ybWF0PWNzdiIpCiAgICAgICAgZG93bmxvYWQuZmlsZShjc3ZfdXJsLCAiaW50ZXJuZXQuemlwIikKICAgICAgICB1bnppcCgiaW50ZXJuZXQuemlwIikKICAgICAgICBmaWxlLnJlbmFtZSgiQVBJX0lULk5FVC5VU0VSLlpTX0RTMl9lbl9jc3ZfdjJfMjI1NTAwNy5jc3YiLAogICAgICAgICAgICAgICAgICAgIGNzdmZpbGUpCiAgICB9CgogICAgaW50ZXJuZXRfcmF3IDwtIHJlYWQuY3N2KGNzdmZpbGUsIHNraXAgPSA0KQp9CmBgYAoKUmVzaGFwZSB0byBsb25nZXIgZm9ybWF0OgoKYGBge3IgaW50ZXJuZXQtaGVhdG1hcC1yZXNoYXBlLCBldmFsID0gRkFMU0V9CmludGVybmV0IDwtIHNlbGVjdChpbnRlcm5ldF9yYXcsIGNvdW50cnkgPSBDb3VudHJ5Lk5hbWUsIG1hdGNoZXMoIlguKyIpKQppbnRlcm5ldCA8LSBwaXZvdF9sb25nZXIoaW50ZXJuZXQsIC1jb3VudHJ5LAogICAgICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJ1c2VycyIpCmludGVybmV0IDwtIG11dGF0ZShpbnRlcm5ldCwgeWVhciA9IGFzLmludGVnZXIoc3ViKCJYIiwgIiIsIHllYXIpKSkKYGBgCgpTZWxlY3Qgc29tZSBjb3VudHJpZXMgdG8gaW5jbHVkZSBpbiB0aGUgcGxvdDoKCmBgYHtyIGludGVybmV0LWhlYXRtYXAtc2VsZWN0LWNvdW50cmllcywgZXZhbCA9IEZBTFNFfQpjb3VudHJ5X2xpc3QgPC0gYygiVW5pdGVkIFN0YXRlcyIsICJDaGluYSIsICJJbmRpYSIsICJKYXBhbiIsICJBbGdlcmlhIiwKICAgICAgICAgICAgICAgICAgIkJyYXppbCIsICJHZXJtYW55IiwgIkZyYW5jZSIsICJVbml0ZWQgS2luZ2RvbSIsICJJdGFseSIsCiAgICAgICAgICAgICAgICAgICJOZXcgWmVhbGFuZCIsICJDYW5hZGEiLCAiTWV4aWNvIiwgIkNoaWxlIiwgIkFyZ2VudGluYSIsCiAgICAgICAgICAgICAgICAgICJOb3J3YXkiLCAiU291dGggQWZyaWNhIiwgIktlbnlhIiwgIklzcmFlbCIsICJJY2VsYW5kIikKCmludGVybmV0X3Nob3J0IDwtIGZpbHRlcihpbnRlcm5ldCwgY291bnRyeSAlaW4lIGNvdW50cnlfbGlzdCkKIyMgaW50ZXJuZXRfc2hvcnQgPC0gbXV0YXRlKGludGVybmV0X3Nob3J0LAojIyAgICAgICAgICAgICAgICAgICAgICAgICAgdXNlcnMgPSBpZmVsc2UoaXMubmEodXNlcnMpLCAwLCB1c2VycykpCmBgYAoKR2V0IG9yZGVyaW5nIGJ5IDIwMTcgbGV2ZWxzOgoKYGBge3IgaW50ZXJuZXQtaGVhdG1hcC0yMDE3LWxldmVscywgZXZhbCA9IEZBTFNFfQppbnRyMTcgPC0gZmlsdGVyKGludGVybmV0X3Nob3J0LCB5ZWFyID09IDIwMTcpCmxldnMgPC0gYXJyYW5nZShpbnRyMTcsIHVzZXJzKSRjb3VudHJ5CmBgYAoKVGhlIGJhc2ljIHBsb3Q6CgpgYGB7ciwgaW50ZXJuZXQtaGVhdG1hcC1iYXNpYy1wbG90LCBldmFsID0gRkFMU0V9CnBfaW5ldCA8LSBnZ3Bsb3QoZmlsdGVyKGludGVybmV0X3Nob3J0LCB5ZWFyID4gMTk5MyksCiAgICAgICAgICAgICAgICAgYWVzKHggPSB5ZWFyLAogICAgICAgICAgICAgICAgICAgICB5ID0gZmFjdG9yKGNvdW50cnksIGxldnMpLAogICAgICAgICAgICAgICAgICAgICBmaWxsID0gdXNlcnMpKSArCiAgICBnZW9tX3RpbGUoY29sb3IgPSAid2hpdGUiLCBzaXplID0gMC4yNSkKYGBgCgpBZGp1c3QgY29sb3IgcGFsZXR0ZSBhbmQgZ3VpZGU6CgpgYGB7ciBpbnRlcm5ldC1oZWF0bWFwLWZpbGwtY29sb3IsIGV2YWwgPSBGQUxTRX0KcF9pbmV0IDwtIHBfaW5ldCArCiAgICBzY2FsZV9maWxsX3ZpcmlkaXNfYygKICAgICAgICBvcHRpb24gPSAiQSIsIGJlZ2luID0gMC4wNSwgZW5kID0gMC45OCwKICAgICAgICBsaW1pdHMgPSBjKDAsIDEwMCksCiAgICAgICAgbmFtZSA9ICJpbnRlcm5ldCB1c2VycyAvIDEwMCBwZW9wbGUiLAogICAgICAgIGd1aWRlID0gZ3VpZGVfY29sb3JiYXIoCiAgICAgICAgICAgIGRpcmVjdGlvbiA9ICJob3Jpem9udGFsIiwKICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgdGl0bGUucG9zaXRpb24gPSAidG9wIiwKICAgICAgICAgICAgdGlja3MgPSBGQUxTRSwKICAgICAgICAgICAgYmFyd2lkdGggPSBncmlkOjp1bml0KDMuNSwgImluIiksCiAgICAgICAgICAgIGJhcmhlaWdodCA9IGdyaWQ6OnVuaXQoMC4yLCAiaW4iKSwKICAgICAgICAgICAgb3JkZXIgPSAxKSkKYGBgCgpBZGp1c3QgYHhgIGFuZCBgeWAgc2NhbGVzOgoKYGBge3IgaW50ZXJuZXQtaGVhdG1hcC14LXktc2NhbGVzLCBldmFsID0gRkFMU0V9CnBfaW5ldCA8LSBwX2luZXQgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGV4cGFuZCA9IGMoMCwgMCksIG5hbWUgPSBOVUxMKSArCiAgICBzY2FsZV95X2Rpc2NyZXRlKG5hbWUgPSBOVUxMLCBwb3NpdGlvbiA9ICJyaWdodCIpCmBgYAoKQWRkIGxheWVyIGZvciBgTkFgIHZhbHVlczoKCmBgYHtyIGludGVybmV0LWhlYXRtYXAtTkEtbGF5ZXIsIGV2YWwgPSBGQUxTRX0KcF9pbmV0IDwtIHBfaW5ldCArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpICsKICAgIGdlb21fdGlsZShkYXRhID0gZmlsdGVyKGludGVybmV0X3Nob3J0LCB5ZWFyID4gMTk5MywgaXMubmEodXNlcnMpKSwKICAgICAgICAgICAgICBhZXMoZmlsbCA9ICJOQSIpLCBjb2xvciA9ICJ3aGl0ZSIpICsKICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9ICJncmV5NTAiKSArCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWwucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRpdGxlLnBvc2l0aW9uID0gInRvcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXloZWlnaHQgPSBncmlkOjp1bml0KDAuMiwgImluIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBrZXl3aWR0aCA9IGdyaWQ6OnVuaXQoMC4yLCAiaW4iKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG9yZGVyID0gMikpCmBgYAoKRmluYWwgcGxvdCB3aXRoIHRpdGxlIGFuZCB0aGVtZSBhZGp1c3RtZW50czoKCmBgYHtyIGludGVybmV0LWhlYXRtYXAtZmluYWxwbG90LCBldmFsID0gRkFMU0V9CnBfaW5ldCArCiAgICBnZ3RpdGxlKCJBIEhlYXQgTWFwIiwKICAgICAgICAgICAgIkNvdW50cmllcyBvcmRlcmVkIGJ5IHBlcmNlbnQgaW50ZXJuZXQgdXNlcnMgaW4gMjAxNy4iKSArCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNikpICsKICAgIHRoZW1lKGF4aXMubGluZSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGF4aXMudGlja3MgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBheGlzLnRpY2tzLmxlbmd0aCA9IGdyaWQ6OnVuaXQoMSwgInB0IiksCiAgICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAidG9wIiwKICAgICAgICAgIGxlZ2VuZC5qdXN0aWZpY2F0aW9uID0gImxlZnQiLAogICAgICAgICAgbGVnZW5kLnRpdGxlLmFsaWduID0gMC41LAogICAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiAqIDEyIC8gMTQpCiAgICAgICAgICApCmBgYAoKYGBge3IgaW50ZXJuZXQtaGVhdG1hcCwgZXZhbCA9IEZBTFNFLCBlY2hvID0gRkFMU0V9Cjw8aW50ZXJuZXQtaGVhdG1hcC1yZWFkaW5nLWRhdGE+PgoKPDxpbnRlcm5ldC1oZWF0bWFwLXJlc2hhcGU+PgoKPDxpbnRlcm5ldC1oZWF0bWFwLXNlbGVjdC1jb3VudHJpZXM+PgoKPDxpbnRlcm5ldC1oZWF0bWFwLTIwMTctbGV2ZWxzPj4KCjw8aW50ZXJuZXQtaGVhdG1hcC1iYXNpYy1wbG90Pj4KCjw8aW50ZXJuZXQtaGVhdG1hcC1maWxsLWNvbG9yPj4KCjw8aW50ZXJuZXQtaGVhdG1hcC14LXktc2NhbGVzPj4KCjw8aW50ZXJuZXQtaGVhdG1hcC1OQS1sYXllcj4+Cgo8PGludGVybmV0LWhlYXRtYXAtZmluYWxwbG90Pj4KYGBgCg==