Disclaimer: The purpose of the Open Case Studies project is to demonstrate the use of various data science methods, tools, and software in the context of messy, real-world data. A given case study does not cover all aspects of the research process, is not claiming to be the most appropriate way to analyze a given data set, and should not be used in the context of making policy decisions without external consultation from scientific experts.

This work is licensed under the Creative Commons Attribution-NonCommercial 3.0 (CC BY-NC 3.0) United States License.

To cite this case study please use:

Wright, Carrie and Ontiveros, Michael and Meng, Qier and Jager, Leah and Taub, Margaret and Hicks, Stephanie. (2020). https://github.com/opencasestudies/ocs-bp-vaping-case-study. Vaping Behaviors in American Youth (Version v1.0.0).

To access the GitHub Repository with the data for this case study see here: https://github.com/opencasestudies/ocs-bp-vaping-case-study

You may also access and download the data using our OCSdata package. To learn more about this package including examples, see this link. Here is how you would install this package:

install.packages("OCSdata")

This case study is part of a series of public health case studies for the Bloomberg American Health Initiative.


The total reading time for this case study is calculated via koRpus and shown below:

Reading Time Method
72 minutes koRpus

Readability Score:

A readability index estimates the reading difficulty level of a particular text. Flesch-Kincaid, FORCAST, and SMOG are three common readability indices that were calculated for this case study via koRpus. These indices provide an estimation of the minimum reading level required to comprehend this case study by grade and age.

Text language: en 
index grade age
Flesch-Kincaid 10 15
FORCAST 10 15
SMOG 12 17

Please help us by filling out our survey.

Motivation


In the United States, there have been significant and historical declines in cigarette smoking. In the 1970s, 75% of high school seniors were smoking, that number is below 10% now. This progress is largely due to the tobacco control movement and their focus on initiatives like ending advertising to children (like Joe Camel), passing indoor smoking laws, health communication, etc.

According to a recent report, overall tobacco/nicotine use increased in youths (middle school and high school students) in the United States in 2017 and 2018, despite previous years of declining use.

This major increase is attributed to an increase in the use of electronic cigarette (e-cigarette) products.

Forms of tobacco/nicotine include these categories:

  1. Cigarette and other combustible tobacco (pipes, cigars, cigarillo, etc.)
  2. E-cigarettes and vaporized tobacco/nicotine (hookah, Juul, etc.)
  3. Other non-combustible, non-vapor tobacco/nicotine products (snus, chewing tobacco, etc.)

Youths are more likely to use e-cigarettes vs. combustible cigarettes these days, which is concerning because e-cigarettes are really efficient nicotine delivery devices that are reinforcing and easy to initiate. By contrast, it takes quite a while to become accustomed to cigarettes (eg. because of coughing) and become dependent. It is also harder for parents to detect e-cigarette use and intervene (eg. the smell is not as strong). This means that youths may be becoming physically dependent on nicotine more quickly than in past years, and that cessation services designed for youths will be needed.

Whereas in previous decades the focus was on advertising, the current era requires attention to the marketing broadly. Juul caught on through Instagram influencers. New policies that regulate these innovative marketing strategies will be critical.

E-cigarettes are referred to by many different names, including but not limited to:

  1. Electronic nicotine delivery systems (ENDS)
  2. Vapes
  3. e-hookahs
  4. vape pens
  5. tanks
  6. mods

The devices vary greatly:

[source]

See this CDC guide and the American Lung Association website for more information.

The report found that:

During 2017–2018, current use of any tobacco[/nicotine]\(^*\) product increased 38.3% (from 19.6% to 27.1%) among high school students and 28.6% (from 5.6% to 7.2%) among middle school students; e-cigarette use increased 77.8% (from 11.7% to 20.8%) among high school students and 48.5% (from 3.3% to 4.9%) among middle school students.

\(^*\) Note: we added “[/nicotine]” to this quote from the report because e-cigarettes deliver nicotine, but are not actually tobacco.

In 2018, the Federal Drug Administration (FDA) in the United States stated that e-cigarette usage use among youth reached:

“nothing short of an epidemic proportion of growth

Additionally, you may learn more about e-cigarette or vaping use-associated lung injury (EVALI) from CDC’s Morbidity and Mortality Weekly Report (MMWR).

In this case study, we will be investigating the same data used in the report that generated the above findings. This data comes from the The National Youth Tobacco Survey (NYTS).

Gentzke, Andrea S., Melisa Creamer, Karen A. Cullen, Bridget K. Ambrose, Gordon Willis, Ahmed Jamal, and Brian A. King. “Vital Signs: Tobacco Product Use Among Middle and High School Students - United States, 2011-2018.” MMWR. Morbidity and Mortality Weekly Report 68 (6): 157–64 (2019).

Main Questions


Our main question:

  1. How has tobacco and e-cigarette/vaping use by American youths changed since 2015?
  2. How does e-cigarette use compare between males and females?
  3. What vaping brands and flavors appear to be used the most frequently?
    We will base this on the following survey questions:

“During the past 30 days, what brand of e-cigarettes did you usually use?”
“What flavors of tobacco products have you used in the past 30 days?”

  1. Is there a relationship between e-cigarette/vaping use and other tobacco use?

Learning Objectives


In this case study, we will cover how to import data from multiple files efficiently, how to import data from excel files, and how to make a variety of visualizations to compare multiple groups across time. We will also demonstrate how to work with codebooks. We will cover the concept of survey weighting and introduce the srvyr package. We will discuss the difference between pooled cross-sectional data and panel data. We will especially focus on using packages and functions from the Tidyverse for wrangling data, such as tidyr and dplyr and for visualization, such as as ggplot2. The Tidyverse is a library of packages created by RStudio. While some students may be familiar with previous R programming packages, these packages make data science in R especially efficient.

The skills, methods, and concepts that students will be familiar with by the end of this case study are:

Data Science Learning Objectives:

  1. Import data from Excel files
  2. Merge data from multiple similar but not identical data structures
  3. Create effective longitudinal data visualizations
  4. Write functions in R
  5. Apply functions across data subsets using purrr and dplyr functionality.

Statistical Learning Objectives:

  1. Understanding of different types of longitudinal data
  2. Usage of codebooks
  3. Conceptual understanding of survey weighting
  4. Implementing logistic regression with survey weighting

We will begin by loading the packages that we will need:

library(here)
library(readxl)
library(magrittr)
library(stringr)
library(purrr)
library(dplyr)
library(readr)
library(tidyr)
library(ggplot2)
library(scales)
library(viridis)
library(forcats)
library(naniar)
library(srvyr)
library(cowplot)
library(broom)
library(survey)
library(OCSdata)

Packages used in this case study:

Package Use in this case study
here to easily load and save data
readxl to import the data in the excel files
magrittr to use the compound assignment pipe operator %<>%
stringr to manipulate the character strings within the data
purrr to import the data in all the different excel and csv files efficiently
dplyr to arrange/filter/select/compare specific subsets of the data
readr to import the CSV file data
tidyr to rearrange data in wide and long formats
ggplot2 to make visualizations with multiple layers
scales to allow us to look at the colors within the viridis package
viridis to make plots with a color palette that is compatible with color blindness
forcats to allow for reordering of factors in plots
naniar to make a visualization of missing data
syrvr to use survey weights
cowplot to allow plots to be combined
broom to create nicely formatted model output
survey to fit survey-weighted logistic regression
OCSdata to access and download OCS data files

The first time we use a function, we will use the :: to indicate which package we are using. Unless we have overlapping function names, this is not necessary, but we will include it here to be informative about where the functions we will use come from.

Context


According to the cited Morbidity and Mortality Weekly Report this was what was already known about this topic and the implications of this study:

source

Importantly, the vapors used in e-cigarettes contain harmful chemicals:

source

E-cigarette usage has also been associated with lung injury:

source

See here for additional information about the potential health effects of e-cigarettes in teens and young adults.

Limitations


There are some important considerations regarding this data analysis to keep in mind:

  1. The National Youth Tobacco Survey (NYTS) does not follow the same individual student respondents over time. A longitudinal study that does follow the same individuals over time collects data called panel data. The data in this study is called pooled cross-sectional data, and is obtained from random collection of observations across time.

According to Wikipedia:

Panel data differs from pooled cross-sectional data across time, because it deals with the observations on the same subjects in different times whereas the latter observes different subjects in different time periods

  1. The data include percentages of student respondents reporting use of each particular tobacco product, but the survey questions did not ask the relative amount of use of one product compared to another. For example, the survey included questions like:“What flavors of tobacco products have you used in the past 30 days?” but did not ask how often one flavor was used by the same individual over another.

While gender and sex are not actually binary, the data used in this analysis only contain information for groups of individuals who answered the survey questions as male or female.

What are the data?


The data in this case study comes from the National Youth Tobacco Survey (NYTS) which is an annual survey that asks students in high school and middle school (grades 6-12) about tobacco usage in the United States of America.

The data for this survey is freely available online at this website with data from 1999, 2000, 2002, 2004, 2006, 2009, and 2011-2019. We will be using data from 2015-2019 due to the fact that these years are the most recent that asked questions regarding e-cigarette usage.

Each year includes documentation, such as a codebook and an excel file containing the data:

Therefore, since we are using data from 2015-2019, the data we are interested in are located in 5 different excel files, one for each year, each with its own codebook (this is the one for 2019).

The codebook contains information describing the data within the excel file.

As you can see the excel file contains very short variable names and numeric values, and it is not clear what they mean without the codebook:

The codebook explains what the variables (the columns) are:

And the codebook explains what the values for each variable are:

We will explain more later about what the values on the right indicate.

The reason that there are codebooks for each year is because the questions asked each year varied slightly.

The data in this survey are pooled cross-sectional data. In this study design, different subsets of student respondents are surveyed each year and it is not clear which, if any, individuals participate more than once from one year to the next.

Data Import


Reading in the excel files


Since these excel files are so large (each has roughly 20,000 rows), it takes a bit of time for the data to load. To make the process faster, we previously imported these files, selected only the columns relevant to our questions of interest, and saved these data subsets as comma-separated (.csv) files.

You may download these files using the OCSdata package:

# install.packages("OCSdata")

library(OCSdata)
simpler_import_data("ocs-bp-vaping-case-study", outpath = getwd())

You may also obtain these files from the GitHub Repository. If you have trouble accessing the GitHub Repository, these CSV files can be downloaded from here.


Click here for details on how the data were originally imported

If you have trouble accessing the GitHub Repository, you can follow the links to download the excel files for 2015, 2016, 2017, 2018, and 2019.

First we created a vector of file names of all the different excel files. Using the here() function of the here package, we looked in all the directories of the project. The list.files() function looked for all files with .xlsx within these sub-directories.

excel_files <- list.files(here::here(), recursive = TRUE,
                  pattern = "*.xlsx")
excel_files
[1] "data/raw/2015-nyts-dataset-and-codebook-microsoft-excel/nyts2015.xlsx"
[2] "data/raw/2016-nyts-dataset-and-codebook-microsoft-excel/nyts2016.xlsx"
[3] "data/raw/2017-nyts-dataset-and-codebook-microsoft-excel/nyts2017.xlsx"
[4] "data/raw/2018-nyts-dataset-and-codebook-microsoft-excel/nyts2018.xlsx"
[5] "data/raw/2019-nyts-dataset-and-codebook-microsoft-excel/nyts2019.xlsx"

All the files were read using read_excel() of the readxl package. Using the map() function of the purrr package this was done efficiently for all of the excel files in the vector using one command. The . is used to indicate that we want to apply the read_excel() function to each element of the data that we just piped into the map() function.

Here we also used the %>% pipe which can be used to pass the input from one function to another for later sequential steps. You can use pipes if you load the dplyr package or the magrittr package.

This created a single list of tibbles (one for each file).

tbl_files <- excel_files %>%
       map(~ readxl::read_excel(.))

The elements of this list are in the same order as the elements of the excel_files vector, so we can extract the data for a particular file (year) by selecting a certain element of the list. However, it is safer to be able to select the data from this list for a specific year using a name based on the original vector of file names. We extracted a name from each Excel file name using the str_extract() function of the stringr package. Here we are keeping occurrences of the character string “nyts201” followed by a “5”, “6”, “7”, “8”, or “9”.

tbl_names <- excel_files %>%
  str_extract("nyts201[5-9]")

tbl_names
[1] "nyts2015" "nyts2016" "nyts2017" "nyts2018" "nyts2019"

These names became the names of the tibbles in the list of tibbles.

names(tbl_files) <- tbl_names

Specific columns were selected using the select() function of dplyr from each of the tibbles using the variable name, as identified in the codebook as being of interest for our analysis. In some cases functions like starts_with() of the dplyr package were used to select several variables at once. Most of the survey questions about tobacco use start with an "E" or a "C" according to the codebooks.

tbl_files[["nyts2015"]] <- tbl_files[["nyts2015"]] %>%
    dplyr::select(psu, # Primary Sampling Unit
                  finwgt, # Analysis Weight
                  stratum, # Sampling stratum
                  Qn1, # Age
                  Qn2, # Sex
                  Qn3, # Grade
                  starts_with("E",
                              ignore.case = FALSE),
                  starts_with("C",
                              ignore.case = FALSE),
                  )


tbl_files[["nyts2016"]] <- tbl_files[["nyts2016"]] %>%
    dplyr::select(psu,
                  finwgt,
                  stratum,
                  Q1, # Age
                  Q2, # Sex
                  Q3, # Grade
                  starts_with("E",
                              ignore.case = FALSE),
                  starts_with("C",
                              ignore.case = FALSE),
                  Q50A, # Menthol # What flavors of tobacco products have you used in the past 30 days? (Select one or more)
                  Q50B, # Clove or spice
                  Q50C, # Fruit
                  Q50D, # Chocolate
                  Q50E, # Alcoholic Drink
                  Q50F, # Candy/Desserts/Other Sweets
                  Q50G  # Some Other Flavor Not Listed Here
                  )

tbl_files[["nyts2017"]] <- tbl_files[["nyts2017"]] %>%
    dplyr::select(psu,
                  finwgt,
                  stratum,
                  Q1, # Age
                  Q2, # Sex
                  Q3, # Grade
                  starts_with("E",
                              ignore.case = FALSE),
                  starts_with("C",
                              ignore.case = FALSE),
                  Q50A, # Menthol # What flavors of tobacco products have you used in the past 30 days? (Select one or more)
                  Q50B, # Clove or spice
                  Q50C, # Fruit
                  Q50D, # Chocolate
                  Q50E, # Alcoholic Drink
                  Q50F, # Candy/Desserts/Other Sweets
                  Q50G  # Some Other Flavor Not Listed Here
                  )

tbl_files[["nyts2018"]] <- tbl_files[["nyts2018"]] %>%
    dplyr::select(psu,
                  finwgt,
                  stratum,
                  Q1, # Age
                  Q2, # Sex
                  Q3, # Grade
                  starts_with("E",
                              ignore.case = FALSE),
                  starts_with("C",
                              ignore.case = FALSE),
                  Q50A, # Menthol #What flavors of tobacco products have you used in the past 30 days? (Select one or more)
                  Q50B, # Clove or spice
                  Q50C, # Fruit
                  Q50D, # Chocolate
                  Q50E, # Alcoholic Drink
                  Q50F, # Candy/Desserts/Other Sweets
                  Q50G  # Some Other Flavor Not Listed Here
                  )

tbl_files[["nyts2019"]] <- tbl_files[["nyts2019"]] %>%
    dplyr::select(psu,
                  finwgt,
                  stratum,
                  Q1, # Age
                  Q2, # Sex
                  Q3, # Grade
                  starts_with("E",
                              ignore.case = FALSE),
                  starts_with("C",
                              ignore.case = FALSE),
                  Q40, # Brand, e-cigarettes
                  Q62A, # Menthol # What flavors of tobacco products have you used in the past 30 days? (Select one or more)
                  Q62B, # Clove or spice
                  Q62C, # Fruit
                  Q62D, # Chocolate
                  Q62E, # Alcoholic Drink
                  Q62F, # Candy/Desserts/Other Sweets
                  Q62G, # Some Other Flavor Not Listed Here
                  )

A directory called reduced was created for the new .csv files using the base dir.create() function. A csv file was created for each of the tibbles in the list using the write_csv() function of the readr package. This was done all at once using the map() function.

dir.create("data/reduced")

names(tbl_files) %>% map(~ write_csv(tbl_files[[.]], 
                                     path = paste0("data/reduced/", ., ".csv")))

Now we will show how to read in the data from the five csv files that were created from the five different Excel files.

Reading in the CSV files


Using the here() function of the here package, we looked in all the directories of the project. The here package automatically starts looking for files based on where you have a .Rproj file which is created when you start a new RStudio project.


Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects.


The list.files() function looked for all files with .csv within the data/reduced sub-directories.

All the files were read using read_csv() of the readr package. Using the map() function of the purrr package this was done efficiently for all of the CSV files in the vector using one command. The . is used to indicate that we want to apply the read_csv() function to each element of the data that we just piped into the map() function. For more information about the map() function, see here.

Here we also used the %>% pipe which can be used to pass the input from one function to another for later sequential steps.

nyts_data <- list.files(here::here(), recursive = TRUE,
                        pattern = "*.csv") %>%
  stringr::str_subset(., "wrangled", negate = TRUE) %>%
  map(~ read_csv(.))


nyts_data_names <- list.files(recursive = TRUE,
                              pattern = "*.csv") %>%
  stringr::str_subset(., "wrangled", negate = TRUE) %>%
  str_extract("nyts201[5-9]")

names(nyts_data) <- nyts_data_names

We can save our imported data as an rda file (stands for R data file) using the save() function.

save(nyts_data, file = here::here("data", "imported", "imported_data.rda"))

Data Exploration and Wrangling


If you have been following along but stopped, we could load our imported data from the “data” directory like so:

load(here::here("data", "imported", "imported_data.rda"))

If you skipped the data import section click here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the imported data using the following code:

imported_data("ocs-bp-vaping-case-study", outpath = getwd())
load(here::here("OCSdata", "data", "imported", "imported_data.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found in our GitHub repository or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “imported” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "imported", "imported_data.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



Our goal in this section is to adjust or “wrangle” the data from each year into a common format so that we can combine the datasets across years for our analysis, and so that we have values in our variables that are correct and easy to interpret. We will need to understand what is the same and what is different across the data from different years, rename and recode the variables (e.g., by replacing the numbers 1 and 2 with the values “Male” and “Female” for the Sex variable), and combine the data. We will walk through these steps below.

First, let’s take a look at our data. We can get a good sense of it using the glimpse() function of the dplyr package.

dplyr::glimpse(nyts_data[["nyts2015"]])
Rows: 17,711
Columns: 29
$ psu        <chr> "015438", "015438", "015438", "015438", "015438", "015438",…
$ finwgt     <dbl> 216.7268, 324.9620, 324.9620, 397.1552, 264.8745, 264.8745,…
$ stratum    <chr> "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR…
$ Qn1        <dbl> 10, 9, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, …
$ Qn2        <dbl> 2, 1, 1, 1, 2, 2, 1, 2, 1, 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 2,…
$ Qn3        <dbl> 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 5, 5,…
$ ECIGT      <dbl> 2, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 2, 1, 2, 2, 1, 2, 1, 2, 1,…
$ ECIGAR     <dbl> 1, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 2,…
$ ESLT       <dbl> 2, 2, 2, 2, 2, 2, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ EELCIGT    <dbl> 2, 1, 2, 1, 2, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 2, 2, 2, 1,…
$ EROLLCIGTS <dbl> 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2,…
$ EFLAVCIGTS <dbl> 2, 2, 2, 1, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ EBIDIS     <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ EFLAVCIGAR <dbl> 2, 1, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 1, 2, 1, 1, 2,…
$ EHOOKAH    <dbl> 2, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ EPIPE      <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ ESNUS      <dbl> 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ EDISSOLV   <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CCIGT      <dbl> 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CCIGAR     <dbl> 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CSLT       <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CELCIGT    <dbl> 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CROLLCIGTS <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CFLAVCIGTS <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CBIDIS     <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CHOOKAH    <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CPIPE      <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CSNUS      <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…
$ CDISSOLV   <dbl> 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,…

Updating the set of variables and their names


The easiest way of making it so that the data from the different years can be combined is by making sure that the same type of data in different datasets share the same names. In addition, giving the columns informative names will help make our code more readable. Currently, it isn’t very clear what most of the variables indicate since the variable names are uninformative on their own, without the codebook.

We want to rename variables like Qn1 to something more meaningful like Age.

To do this we will use the rename() function of the dplyr package. The new name is always listed first before the =. This function will replace the old variable names with the new ones, i.e., after running the code below, there will no longer be a Qn1 variable in the dataset, but there will be an Age variable instead. We will start working with the 2015 data, and then move on to the other years down below.

nyts_data[["nyts2015"]] <- nyts_data[["nyts2015"]] %>%
  dplyr::rename(Age = Qn1,
                Sex = Qn2,
                Grade = Qn3)

Ultimately we will be combining the data from each year using the bind_rows() function of the dplyr package, which will fill in NA values for any columns that do not exist for one of the years.

The data for 2016-2018 have many common attributes, so we will want to write code that can be applied to all three datasets. To do this, we will use a function in R, which is basically a piece of code that can be applied to similar but different objects in R (e.g., the data tibbles from each of these three years). Recall that you are using functions from packages all the time, like the rename() function of the dplyr package. Now you are going to write your own function! For more information on functions, see here.

These next 3 years have the same structure for many of the questions we are interested in. For example, they all have flavor questions, but not a brand question. Moreover, their variable names are consistent across the years; for each year, we want to replace the vague question name Q50A with the value menthol in all three datasets, and the same is true for the other flavor variables.

Since we want to perform the same modifications on the data from all three years, rather than repeating the same somewhat messy piece of code three times, we can do this more efficiently if we create a function to do all of these steps at once. Then we can use the map_at() function of the purrr package, which is an extension of the map() function that we used in the [Data Import] (https://www.opencasestudies.org/ocs-bp-vaping-case-study/#Data_Import){target=“_blank”} section to apply the function we just created (for renaming variables etc.) specifically to the data from 2016-2018 within the nyts_data. By using vars() inside of the map_at() function we can specify what tibbles within our nyts_data list we want to include or exclude.

Functions


So how do you create a function? You first need to specify that you are creating a function by using the function() base function. Yes, that’s right it is a function for creating functions called function!

First we specify our input within the parentheses of function(). Thus if our function will apply something to an input called x then we would use function(x). Theoretically our input can be named whatever we want, but we we need to refer to it consistently within our function to indicate what we want done with the input data. We can actually have more than one input as well, we would indicate two inputs like this: function(x, b). Here we would be using both x and b to do something in our function.

The next part of a function is defined within curly brackets {}. This is where we write what we want done to our inputs.


Click here to see a simple example

Our function will be called simple_function, which will add 2 to our input. It will take the input vector_data (note that we usually want to use an informative input argument like vector_data).

simple_function <- function(vector_data) {
  vector_data + 2
}

We can use an input that is a vector called x.

x = c(1, 2, 3, 4)
x
[1] 1 2 3 4

Now we specify using the argument vector_data = to indicate that x is our input that we want to perform the function on.

simple_function(vector_data = x)
[1] 3 4 5 6

This function also works for other vector input. For example, vector y is now our input that we want to perform the function on.

y = c(5, 10, 15, 20)

simple_function(vector_data = y)
[1]  7 12 17 22

In our case we will be applying our function to the variable names for the dataset for each year. The output of our function is the result of renaming these variables for each year.

update_survey <- function(dataset) { dataset %>%
           rename(Age = Q1,
                  Sex = Q2,
                Grade = Q3,
              menthol = Q50A,
          clove_spice = Q50B,
                fruit = Q50C,
            chocolate = Q50D,
      alcoholic_drink = Q50E,
 candy_dessert_sweets = Q50F,
                other = Q50G)
}

# options to apply the function to the data:
# nyts_data <- nyts_data %>% map_at(vars(nyts2016, nyts2017, nyts2018), Update_survey)
nyts_data <- nyts_data %>% map_at(vars(-nyts2015, -nyts2019), update_survey)

The final year, 2019, has a slightly different data structure compared to these earlier datasets. It actually has a brand_ecig variable already and different question numbers correspond to our flavor questions of interest. So we will rename the variables in this dataset individually. We could also write this as a function, but since we are only applying this one time, there is no need to. Functions are really helpful for repeating the same task repeatedly using different data inputs.

nyts_data[["nyts2019"]] <- nyts_data[["nyts2019"]] %>%
    rename(brand_ecig = Q40,
                  Age = Q1,
                  Sex = Q2,
                Grade = Q3,
              menthol = Q62A,
          clove_spice = Q62B,
                fruit = Q62C,
            chocolate = Q62D,
      alcoholic_drink = Q62E,
 candy_dessert_sweets = Q62F,
                other = Q62G)

Now let’s take a look at the variable names for each of the years using the map function from purrr.

map(nyts_data, names)
$nyts2015
 [1] "psu"        "finwgt"     "stratum"    "Age"        "Sex"       
 [6] "Grade"      "ECIGT"      "ECIGAR"     "ESLT"       "EELCIGT"   
[11] "EROLLCIGTS" "EFLAVCIGTS" "EBIDIS"     "EFLAVCIGAR" "EHOOKAH"   
[16] "EPIPE"      "ESNUS"      "EDISSOLV"   "CCIGT"      "CCIGAR"    
[21] "CSLT"       "CELCIGT"    "CROLLCIGTS" "CFLAVCIGTS" "CBIDIS"    
[26] "CHOOKAH"    "CPIPE"      "CSNUS"      "CDISSOLV"  

$nyts2016
 [1] "psu"                  "finwgt"               "stratum"             
 [4] "Age"                  "Sex"                  "Grade"               
 [7] "ECIGT"                "ECIGAR"               "ESLT"                
[10] "EELCIGT"              "EHOOKAH"              "EROLLCIGTS"          
[13] "EFLAVCIGAR"           "EPIPE"                "ESNUS"               
[16] "EDISSOLV"             "EBIDIS"               "CCIGT"               
[19] "CCIGAR"               "CSLT"                 "CELCIGT"             
[22] "CHOOKAH"              "CROLLCIGTS"           "CPIPE"               
[25] "CSNUS"                "CDISSOLV"             "CBIDIS"              
[28] "menthol"              "clove_spice"          "fruit"               
[31] "chocolate"            "alcoholic_drink"      "candy_dessert_sweets"
[34] "other"               

$nyts2017
 [1] "psu"                  "finwgt"               "stratum"             
 [4] "Age"                  "Sex"                  "Grade"               
 [7] "ECIGT"                "ECIGAR"               "ESLT"                
[10] "EELCIGT"              "EHOOKAH"              "EROLLCIGTS"          
[13] "EPIPE"                "ESNUS"                "EDISSOLV"            
[16] "EBIDIS"               "CCIGT"                "CCIGAR"              
[19] "CSLT"                 "CELCIGT"              "CHOOKAH"             
[22] "CROLLCIGTS"           "CPIPE"                "CSNUS"               
[25] "CDISSOLV"             "CBIDIS"               "menthol"             
[28] "clove_spice"          "fruit"                "chocolate"           
[31] "alcoholic_drink"      "candy_dessert_sweets" "other"               

$nyts2018
 [1] "psu"                  "finwgt"               "stratum"             
 [4] "Age"                  "Sex"                  "Grade"               
 [7] "ECIGT"                "ECIGAR"               "ESLT"                
[10] "EELCIGT"              "EHOOKAH"              "EROLLCIGTS"          
[13] "EPIPE"                "ESNUS"                "EDISSOLV"            
[16] "EBIDIS"               "CCIGT"                "CCIGAR"              
[19] "CSLT"                 "CELCIGT"              "CHOOKAH"             
[22] "CROLLCIGTS"           "CPIPE"                "CSNUS"               
[25] "CDISSOLV"             "CBIDIS"               "menthol"             
[28] "clove_spice"          "fruit"                "chocolate"           
[31] "alcoholic_drink"      "candy_dessert_sweets" "other"               

$nyts2019
 [1] "psu"                  "finwgt"               "stratum"             
 [4] "Age"                  "Sex"                  "Grade"               
 [7] "ECIGT"                "ECIGAR"               "ESLT"                
[10] "EELCIGT"              "EHOOKAH"              "EROLLCIGTS"          
[13] "EPIPE"                "ESNUS"                "EDISSOLV"            
[16] "EBIDIS"               "EHTP"                 "CCIGT"               
[19] "CCIGAR"               "CSLT"                 "CELCIGT"             
[22] "CHOOKAH"              "CROLLCIGTS"           "CPIPE"               
[25] "CSNUS"                "CDISSOLV"             "CBIDIS"              
[28] "CHTP"                 "brand_ecig"           "menthol"             
[31] "clove_spice"          "fruit"                "chocolate"           
[34] "alcoholic_drink"      "candy_dessert_sweets" "other"               

It’s looking better! The data that overlap across years have the same variable names.

Updating Values


Now that we have made some progress on the selection and names of the variables themselves, we will work on the values contained in the different variables.

We can start with updating the values for Age and Grade, so that they are more understandable.

Recall from the codebook for this year’s dataset that Age isn’t listed in the way one might expect, i.e., it is not just a number of years, but a numerically valued categorical variable.

The same is true for Grade:

This is why it is so important to always check the codebook!!

We also want to replace the value of 19 for Age to be ">18" and the value of 13 for Grade to be replaced with "Ungraded/Other" Also according to the codebooks, numeric values of 1 indicate a survey answer of FALSE, while a value of 2 indicates TRUE. Sex also needs to be recoded. If we take a look at the codebooks carefully (make sure you look at the questions that we pulled, not the recoded values), we will see that males are indicated by 1 and females are indicated by 2. Finally some values are indicated with "*" or"**" when they are missing. We want to replace these with NA.

Let’s create a function to make all these updates. We will use the mutate function of the dplyr package to modify these variables. This function can also be used to create new variables. We will also use the recode() function of the dplyr package to replace specific values of certain variables.

update_values <- function(dataset) {
  dataset %>%
    dplyr::mutate(Age = as.numeric(Age) + 8,
                  Grade = as.numeric(Grade) + 5) %>%
    mutate(Age = as.factor(Age),
           Grade = as.factor(Grade),
           Sex = as.factor(Sex)) %>%
    mutate(Sex = recode(Sex,
                        `1` = "male",
                        `2` = "female")) %>%
    dplyr::mutate_all( ~ replace(., . %in% c("*", "**"), NA)) %>%
    mutate(Age = recode(Age,
                        `19` = ">18"),
           Grade = recode(Grade,
                          `13` = "Ungraded/Other")) %>%
    dplyr::mutate_at(vars(
      starts_with("E", ignore.case = FALSE),
      starts_with("C", ignore.case = FALSE)
    ),
    list( ~ recode(
      .,
      `1` = TRUE,
      `2` = FALSE,
      .default = NA,
      .missing = NA
    )))
}

nyts_data <- nyts_data %>% map(., update_values)

Now if we wanted to check that everything is expected we could do something like this to check the Sex variable using the count() function of the dplyr package. It is advisable to check your data frequently to make sure that it is as expected!

According to the codebook, we should have:

  1. 8,958 males in 2015
  2. 10,438 males in 2016
  3. 8,881 males in 2017
  4. 10,069 males in 2018
  5. 9,803 males in 2019
count_sex <- function(dataset) {dataset %>% count(Sex)}
nyts_data %>% map(., count_sex)
$nyts2015
# A tibble: 3 × 2
  Sex        n
  <fct>  <int>
1 male    8958
2 female  8622
3 <NA>     131

$nyts2016
# A tibble: 3 × 2
  Sex        n
  <fct>  <int>
1 male   10438
2 female 10082
3 <NA>     155

$nyts2017
# A tibble: 3 × 2
  Sex        n
  <fct>  <int>
1 male    8881
2 female  8815
3 <NA>     176

$nyts2018
# A tibble: 3 × 2
  Sex        n
  <fct>  <int>
1 male   10069
2 female  9920
3 <NA>     200

$nyts2019
# A tibble: 3 × 2
  Sex        n
  <fct>  <int>
1 .N       116
2 male    9803
3 female  9099

Looks good!

The years (2016-2019) that have flavors also need the flavor data to be logical (meaning TRUE or FALSE):

In this case we also are setting missing values to FALSE because then the TRUE values will represent those who reported using a specific flavor out of all users, rather than those that used a specific flavor compared to those who used a different flavor.

update_flavors <- function(dataset) {dataset %>%
   mutate_at(vars(menthol:other),
              list(~ recode(.,
                           `1` = TRUE,
                      .default = FALSE,
                      .missing = FALSE))) }

nyts_data  <- nyts_data  %>% map_at(vars(-nyts2015), update_flavors)

Now there are just a few changes needed that are specific to 2019. Specifically, some of the 2019 questions use the values “.N”, “.S”, and “.Z” to indicate different types of missing data (see for example Q2 of the 2019 codebook); we just want them to be replaced with NA values.

nyts_data[["nyts2019"]] <- nyts_data[["nyts2019"]] %>%
  mutate_all(~ replace(., . %in% c(".N", ".S", ".Z"), NA)) %>%
  mutate(psu = as.character(psu)) %>%
  mutate(brand_ecig = recode(brand_ecig,
                                             `1` = "Other", # levels 1,8 combined to `Other`
                                             `2` = "Blu",
                                             `3` = "JUUL",
                                             `4` = "Logic",
                                             `5` = "MarkTen",
                                             `6` = "NJOY",
                                             `7` = "Vuse",
                                             `8` = "Other"))

Great! Now our values don’t need to be handled any differently for any of the years, thus we can combine the data across years.

Even though we have different numbers of variables for each year, we can coerce the data to be combined into one tibble by using the bind_rows() function of dplyr. Importantly, this function does not require that the columns be the same. This will create NA values for any variable that is not present in given data frame but is present in one of the other data frames that is being combined. Note that the bind_cols() function does expect that the rows match. The .id argument will create a new variable with values to link each row to its original data frame. For more information see here.

nyts_data <- nyts_data %>%
  map_df(dplyr::bind_rows, .id = "year")

glimpse(nyts_data)
Rows: 95,465
Columns: 40
$ year                 <chr> "nyts2015", "nyts2015", "nyts2015", "nyts2015", "…
$ psu                  <chr> "015438", "015438", "015438", "015438", "015438",…
$ finwgt               <dbl> 216.7268, 324.9620, 324.9620, 397.1552, 264.8745,…
$ stratum              <chr> "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", …
$ Age                  <fct> 18, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 1…
$ Sex                  <fct> female, male, male, male, female, female, male, f…
$ Grade                <fct> 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1…
$ ECIGT                <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ ECIGAR               <lgl> TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FAL…
$ ESLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, T…
$ EELCIGT              <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ EROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FA…
$ EBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EFLAVCIGAR           <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FA…
$ EHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ ESNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CCIGT                <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CCIGAR               <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CSLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CELCIGT              <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, F…
$ CROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CSNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ menthol              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ clove_spice          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ fruit                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ chocolate            <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ alcoholic_drink      <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ candy_dessert_sweets <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ other                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ EHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ CHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ brand_ecig           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…

We will want to do some of our analysis split by year, so we would like to be sure we have one variable that has the correct value for year. It looks like we just need to remove "nyts" from the year variable that we created from the names of the tibbles in our list and we should be all set. We will use another function from the stringr package to do this. The str_remove() function takes a string followed by a pattern and removes the pattern from the string.

nyts_data <- nyts_data %>%
  mutate(year = as.numeric(str_remove(year, "nyts")))

Here is our clean and wrangled data:

glimpse(nyts_data)
Rows: 95,465
Columns: 40
$ year                 <dbl> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2…
$ psu                  <chr> "015438", "015438", "015438", "015438", "015438",…
$ finwgt               <dbl> 216.7268, 324.9620, 324.9620, 397.1552, 264.8745,…
$ stratum              <chr> "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", …
$ Age                  <fct> 18, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 1…
$ Sex                  <fct> female, male, male, male, female, female, male, f…
$ Grade                <fct> 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1…
$ ECIGT                <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ ECIGAR               <lgl> TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FAL…
$ ESLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, T…
$ EELCIGT              <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ EROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FA…
$ EBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EFLAVCIGAR           <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FA…
$ EHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ ESNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CCIGT                <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CCIGAR               <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CSLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CELCIGT              <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, F…
$ CROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CSNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ menthol              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ clove_spice          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ fruit                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ chocolate            <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ alcoholic_drink      <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ candy_dessert_sweets <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ other                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ EHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ CHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ brand_ecig           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…

Note that there are several variables where there are similar names, but with a C compared to an E in the variable name. Those starting with C are related to questions about current usage (last 30 days), while those with an E are related to usage across the student respondent’s whole life (“ever” usage). We will discuss these groups further below.

Now we will save our wrangled data. We will save it as an rda file for ourselves and as csv files, as this is often a good option for collaborators. We will save this file in a directory called “wrangled” within our “data” directory of our project.

save(nyts_data, file = here::here("data", "wrangled", "wrangled_data.rda"))

write_csv(nyts_data, path = here::here("data", "wrangled", "nyts_data.csv"))

Variable Table


Click here to see a table about the final variables in our data set.
Variable Details
year the year that the survey results from a particular student respondent were acquired
psu the primary sampling unit for the survey weighting
finwgt the final analysis weight for the survey weighting
stratum the stratum used for variance estimation for the survey weighting
Age the age of the student when they took the survey
Sex the sex of the student when they took the survey
Grade the grade of the the student when the took the survey
ECIGT student reported having ever tried cigarette smoking, even one or two puffs
ECIGAR student reported having ever tried cigar, cigarillo, or little cigar smoking, even one or two puffs
ESLT student reported having ever tried chewing tobacco, snuff, or dip
EELCIGT student reported having ever tried e-cigarettes
EROLLCIGTS student reported having ever tried roll-your-own cigarettes
EFLAVCIGTS (2015 only) based on answer to “Which of the following tobacco products that you used in the past 30 days were flavored?”
EBIDIS student reported having ever tried bidis (small brown cigarettes wrapped in a leaf)
EFLAVCIGAR student reported having ever tried a flavored cigar (2015-2016)
EHOOKAH student reported having ever smoked tobacco from a hookah or a waterpipe
EPIPE student reported having ever smoked tobacco from a pipe (not hookah)
ESNUS student reported having ever used snus, such as Camel or Malboro Snus
EDISSOLV student reported having ever tried dissolvable tobacco products such as Ariva, Stonewall, Camel orbs, Camel sticks, Marlboro sticks, or Camel strips
CCIGT student reported they smoked cigarettes on >= 1 of the past 30 days
CCIGAR student reported they smoked cigars on >= 1 of the past 30 days
CSLT student reported they used chewing tobacco, snuff, or dip on >= 1 of the past 30 days
CELCIGT student reported they used electronic cigarettes or e-cigarettes one or more days in the past 30
CROLLCIGTS student reported they smoked roll-your-own cigarettes during the past 30 days
CFLAVCIGTS (2015 only) based on answer to “Which of the following tobacco products that you used in the past 30 days were flavored?”
CBIDIS student reported they smoked bidis during the past 30 days
CHOOKAH student reported they smoked tobacco in a hookah on >= 1 of the past 30 days
CPIPE student reported they smoked tobacco in a pipe (not hookah) during the past 30 days
CSNUS student reported they used snus during the past 30 days
CDISSOLV student reported they used dissolvable tobacco products such as Ariva, Stonewall, Camel orbs, Camel sticks, Marlboro sticks, or Camel strips during the past 30 days
brand_ecig student answer to “During the past 30 days, what brand of e-cigarettes did you usually use?”
menthol student selected Menthol or mint as the answer to “What flavors of tobacco products have you used in the past 30 days? (select one or more)”
clove_spice student selected clove or spice as the answer to “What flavors of tobacco products have you used in the past 30 days? (select one or more)”
fruit student selected fruit as the answer to “What flavors of tobacco products have you used in the past 30 days? (select one or more)”
chocolate student selected chocolate as the answer to “What flavors of tobacco products have you used in the past 30 days? (select one or more)”
alcoholic_drink student selected alcoholic drink (such as wine, cognac, margarita, or other cocktails) as the answer to “What flavors of tobacco products have you used in the past 30 days? (choose one or more)”
candy_dessert_sweets student selected candy, desserts or other sweets as the answer to “What flavors of tobacco products have you used in the past 30 days? (choose one or more)”
other student selected some other flavor not listed as the answer to “What flavors of tobacco products have you used in the past 30 days? (choose one or more)”
EHTP student reported having ever tried heated (also known as “heat-not-burn”) tobacco products
CHTP student reported they used heated tobacco products during the past 30 days

Data Visualization


If you have been following along but stopped, we could load the wrangled data from the “data” directory like so:

load(here::here("data", "wrangled", "wrangled_data.rda"))

If you skipped the data exploration and wrangling section click here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the wrangled data using the following code:

wrangled_rda("ocs-bp-vaping-case-study", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found in our GitHub repository or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_data.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



Recall that our main questions were:

  1. How has tobacco and e-cigarette/vaping use by American youths changed since 2015?

  2. How does e-cigarette use compare between males and females?

  3. What vaping brands and flavors appear to be used the most frequently?
    We will base this on the following survey questions:
    > “During the past 30 days, what brand of e-cigarettes did you usually use?”
    > “What flavors of tobacco products have you used in the past 30 days?”

  4. Is there a relationship between e-cigarette/vaping use and other tobacco use?

We are now going to create data visualizations to explore each of these questions.

For many of these questions we will be interested in both current and ever users, so we will want to create a variable for labeling individuals who are current users of any tobacco product (or not, i.e., who do not currently use a tobacco product) and a variable for labeling individuals who are “ever users” of any tobacco product (or not, i.e., who have never used a tobacco product).

We define these two groups as follows:

  1. current = students who used a product for >=1 day in the past 30 days
  2. ever = students who report having used or tried a product at any point in time

All current users are therefore ever users but not all ever users are current users. Thus, current users are a subset of ever users.

To add these grouping variables to our data we will do a bit more wrangling using the mutate() function again of the dplyr package. As discussed above, our dataset contains a set of questions that relate to whether the student has ever used the particular tobacco product (questions that start with the letter “E”), and questions that relate to whether the student currently uses the particular tobacco product (questions that start with the letter “C”).

Here are some examples for these data entries:

  • EPIPE: Students who reported they have smoked tobacco from a pipe (not hookah).
  • CPIPE: Students who reported they smoked tobacco in a pipe (not hookah) during the past 30 days.
  • EROLLCIGTS: Students who reported they have tried smoking roll-your-own cigarettes.
  • CROLLCIGTS: Students who reported they smoked roll-your-own cigarettes during the past 30 days.

Based on many questions like this:

In the past 30 days, which of the following products have you used on at least one day? (Select one or more)
A. Roll-your-own cigarettes
B. Pipes filled with tobacco (not hookah or waterpipe)
C. Snus, such as Camel, Marlboro, or General Snus
D. Dissolvable tobacco products such as Ariva, Stonewall, Camel orbs, Camel sticks, Marlboro sticks, or Camel strips
E. Bidis (small brown cigarettes wrapped in a leaf)
F. I have not used any of the products listed above in the past 30 days

Which of the following tobacco products have you ever tried, even just one time? (Select one or more)
A. Roll-your-own cigarettes
B. Pipes filled with tobacco (not hookah or waterpipe)
C. Snus, such as Camel, Marlboro, or General Snus
D. Dissolvable tobacco products such as Ariva, Stonewall, Camel orbs, Camel sticks, Marlboro sticks, or Camel strips
E. Bidis (small brown cigarettes wrapped in a leaf)
F. I have never tried any of the products listed above

We will sum across the variables that relate to ever or current tobacco usage questions to determine if the student answered yes to any of the ever or current questions. To do this we will use the base rowSums function.

We will then use the case_when() function of the dplyr package to convert the sum values to TRUE or FALSE based on the threshold of zero. If the sum is greater than zero, then we know the student answered yes to at least one question.

nyts_data %<>%
  mutate(tobacco_sum_ever = rowSums(select(., starts_with("E", 
                                    ignore.case = FALSE)), na.rm = TRUE),
      tobacco_sum_current = rowSums(select(., starts_with("C", 
                                    ignore.case = FALSE)), na.rm = TRUE)) %>%
      mutate(tobacco_ever = case_when(tobacco_sum_ever > 0 ~ TRUE,
                                      tobacco_sum_ever == 0 ~ FALSE),
          tobacco_current = case_when(tobacco_sum_current > 0 ~ TRUE,
                                      tobacco_sum_current == 0 ~ FALSE))

glimpse(nyts_data)
Rows: 95,465
Columns: 44
$ year                 <dbl> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2…
$ psu                  <chr> "015438", "015438", "015438", "015438", "015438",…
$ finwgt               <dbl> 216.7268, 324.9620, 324.9620, 397.1552, 264.8745,…
$ stratum              <chr> "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", …
$ Age                  <fct> 18, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 1…
$ Sex                  <fct> female, male, male, male, female, female, male, f…
$ Grade                <fct> 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1…
$ ECIGT                <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ ECIGAR               <lgl> TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FAL…
$ ESLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, T…
$ EELCIGT              <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ EROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FA…
$ EBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EFLAVCIGAR           <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FA…
$ EHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ ESNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CCIGT                <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CCIGAR               <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CSLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CELCIGT              <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, F…
$ CROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CSNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ menthol              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ clove_spice          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ fruit                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ chocolate            <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ alcoholic_drink      <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ candy_dessert_sweets <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ other                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ EHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ CHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ brand_ecig           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ tobacco_sum_ever     <dbl> 1, 4, 0, 3, 0, 2, 8, 4, 0, 0, 0, 1, 1, 0, 0, 4, 0…
$ tobacco_sum_current  <dbl> 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ tobacco_ever         <lgl> TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE,…
$ tobacco_current      <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FA…

We are also interested in e-cigarette/vaping product usage compared to other tobacco products, so we will create some variables related to the sum of all e-cigarette usage question variables and the sum of all tobacco usage question variables excluding those that are about e-cigarettes. There is only one variable about e-cigarette usage ever (EELCIGT) and one about current usage (CELCIGT).

nyts_data <- nyts_data %>% 
  mutate(ecig_sum_ever = rowSums(select(., EELCIGT), na.rm = TRUE),
      ecig_sum_current = rowSums(select(., CELCIGT), na.rm = TRUE),
     non_ecig_sum_ever = rowSums(select(., starts_with("E", 
                                           ignore.case = FALSE), 
                                           -EELCIGT), na.rm = TRUE),
  non_ecig_sum_current = rowSums(select(., starts_with("C",
                                           ignore.case = FALSE), 
                                           -CELCIGT), na.rm = TRUE)) %>%
      mutate(ecig_ever = case_when(ecig_sum_ever > 0 ~ TRUE,
                                   ecig_sum_ever == 0 ~ FALSE),
          ecig_current = case_when(ecig_sum_current > 0 ~ TRUE,
                                   ecig_sum_current == 0 ~ FALSE),
         non_ecig_ever = case_when(non_ecig_sum_ever > 0 ~ TRUE,
                                   non_ecig_sum_ever == 0 ~ FALSE),
      non_ecig_current = case_when(non_ecig_sum_current > 0 ~ TRUE,
                                   non_ecig_sum_current == 0 ~ FALSE))

glimpse(nyts_data)
Rows: 95,465
Columns: 52
$ year                 <dbl> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2…
$ psu                  <chr> "015438", "015438", "015438", "015438", "015438",…
$ finwgt               <dbl> 216.7268, 324.9620, 324.9620, 397.1552, 264.8745,…
$ stratum              <chr> "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", …
$ Age                  <fct> 18, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 1…
$ Sex                  <fct> female, male, male, male, female, female, male, f…
$ Grade                <fct> 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 1…
$ ECIGT                <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ ECIGAR               <lgl> TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FAL…
$ ESLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, T…
$ EELCIGT              <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ EROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, FA…
$ EBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EFLAVCIGAR           <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FA…
$ EHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ EPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ ESNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CCIGT                <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CCIGAR               <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…
$ CSLT                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CELCIGT              <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, F…
$ CROLLCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CFLAVCIGTS           <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CBIDIS               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CHOOKAH              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CPIPE                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CSNUS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CDISSOLV             <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ menthol              <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ clove_spice          <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ fruit                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ chocolate            <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ alcoholic_drink      <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ candy_dessert_sweets <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ other                <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ EHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ CHTP                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ brand_ecig           <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, N…
$ tobacco_sum_ever     <dbl> 1, 4, 0, 3, 0, 2, 8, 4, 0, 0, 0, 1, 1, 0, 0, 4, 0…
$ tobacco_sum_current  <dbl> 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ tobacco_ever         <lgl> TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE,…
$ tobacco_current      <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, FA…
$ ecig_sum_ever        <dbl> 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0…
$ ecig_sum_current     <dbl> 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ non_ecig_sum_ever    <dbl> 1, 3, 0, 2, 0, 1, 7, 3, 0, 0, 0, 0, 1, 0, 0, 3, 0…
$ non_ecig_sum_current <dbl> 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0…
$ ecig_ever            <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ ecig_current         <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, F…
$ non_ecig_ever        <lgl> TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE,…
$ non_ecig_current     <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, F…

Finally, we are also interested in grouping students that only use e-cigarettes and those that only use other forms of tobacco.

Recall that current users are a subset of ever users, thus students would typically answer yes to having tried vaping products if they had used them one or more days in the past 30 days.

First we will make a small toy dataset called test to show what we will do with the larger dataset:

test <- tibble(ecig_ever = c("TRUE", "TRUE", "TRUE", "TRUE", "FALSE",
                             "FALSE", "TRUE", "FALSE", "FALSE"),
               non_ecig_ever = c("TRUE", "FALSE", "FALSE", "FALSE", "FALSE",
                                 "FALSE", "TRUE", "TRUE", "TRUE"),
               ecig_current = c("TRUE", "FALSE", "FALSE", "TRUE", "TRUE", 
                                "FALSE", "FALSE", "FALSE", "FALSE"),
               non_ecig_current = c("TRUE", "FALSE", "TRUE", "FALSE", "TRUE",
                                    "FALSE", "FALSE", "FALSE", "TRUE"))

test
# A tibble: 9 × 4
  ecig_ever non_ecig_ever ecig_current non_ecig_current
  <chr>     <chr>         <chr>        <chr>           
1 TRUE      TRUE          TRUE         TRUE            
2 TRUE      FALSE         FALSE        FALSE           
3 TRUE      FALSE         FALSE        TRUE            
4 TRUE      FALSE         TRUE         FALSE           
5 FALSE     FALSE         TRUE         TRUE            
6 FALSE     FALSE         FALSE        FALSE           
7 TRUE      TRUE          FALSE        FALSE           
8 FALSE     TRUE          FALSE        FALSE           
9 FALSE     TRUE          FALSE        TRUE            

Now, let’s look at identifying students who have tried e-cigarettes, but are not current users, and who have never tried other tobacco products (and are therefore not current users). We will again use the case_when() and the mutate function to create new variables with specific values when certain conditions are met. In this case, we will specify that several conditions must be met by using the & symbol. For a value of TRUE for the new ecig_only_ever variable, all of the conditions combined with & must be met. If any of the conditions are not met then the ecig_only_ever value will be FALSE based on the last line TRUE ~ FALSE.

test <- test %>% mutate(ecig_only_ever = case_when(ecig_ever == TRUE &
                                               non_ecig_ever == FALSE &
                                                ecig_current == FALSE &
                                            non_ecig_current == FALSE ~ TRUE,
                                                         TRUE ~ FALSE))
test
# A tibble: 9 × 5
  ecig_ever non_ecig_ever ecig_current non_ecig_current ecig_only_ever
  <chr>     <chr>         <chr>        <chr>            <lgl>         
1 TRUE      TRUE          TRUE         TRUE             FALSE         
2 TRUE      FALSE         FALSE        FALSE            TRUE          
3 TRUE      FALSE         FALSE        TRUE             FALSE         
4 TRUE      FALSE         TRUE         FALSE            FALSE         
5 FALSE     FALSE         TRUE         TRUE             FALSE         
6 FALSE     FALSE         FALSE        FALSE            FALSE         
7 TRUE      TRUE          FALSE        FALSE            FALSE         
8 FALSE     TRUE          FALSE        FALSE            FALSE         
9 FALSE     TRUE          FALSE        TRUE             FALSE         

We can see from the second row, that the ecig_only_ever is TRUE when we would expect it to be. We can also see from the fourth row, that even though the student reported yes to ever trying e-cigarettes, because they also reported yes to currently using e-cigarettes the value for only ever trying e-cigarettes is FALSE. Additionally we can see from the seventh row that similarly even though the student reported yes to ever trying e-cigarettes, they also reported yes to ever trying other products, and the value for only ever trying e-cigarettes is FALSE. Importantly, we can see from the 6th row, that if all responses are negative than the value is FALSE.

Now we will expand this to the other possible categories. In this case we note that since current users are a subset of ever users, it doesn’t matter if a user reports yes to ever trying e-cigarettes, they can still be a current user.

test <- test %>%
         mutate(ecig_only_ever = case_when(ecig_ever == TRUE &
                                       non_ecig_ever == FALSE &
                                        ecig_current == FALSE &
                                    non_ecig_current == FALSE ~ TRUE,
                                                        TRUE ~ FALSE),
          ecig_only_current = case_when(ecig_current == TRUE &
                                       non_ecig_ever == FALSE &
                                    non_ecig_current == FALSE ~ TRUE,
                                                        TRUE ~ FALSE),
        non_ecig_only_ever = case_when(non_ecig_ever == TRUE &
                                           ecig_ever == FALSE &
                                        ecig_current == FALSE &
                                    non_ecig_current == FALSE ~ TRUE,
                                                        TRUE ~ FALSE),
  non_ecig_only_current = case_when(non_ecig_current == TRUE &
                                           ecig_ever == FALSE &
                                        ecig_current == FALSE ~ TRUE,
                                                        TRUE ~ FALSE),
                    no_use = case_when(non_ecig_ever == FALSE &
                                           ecig_ever == FALSE &
                                        ecig_current == FALSE &
                                    non_ecig_current == FALSE ~ TRUE,
                                                        TRUE ~ FALSE))
glimpse(test)
Rows: 9
Columns: 9
$ ecig_ever             <chr> "TRUE", "TRUE", "TRUE", "TRUE", "FALSE", "FALSE"…
$ non_ecig_ever         <chr> "TRUE", "FALSE", "FALSE", "FALSE", "FALSE", "FAL…
$ ecig_current          <chr> "TRUE", "FALSE", "FALSE", "TRUE", "TRUE", "FALSE…
$ non_ecig_current      <chr> "TRUE", "FALSE", "TRUE", "FALSE", "TRUE", "FALSE…
$ ecig_only_ever        <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ ecig_only_current     <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, …
$ non_ecig_only_ever    <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ non_ecig_only_current <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ no_use                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, …

Take a minute to check that the values are what we would expect.

OK, now we are going to make a Group variable based on the new variables we just made to classify students into one of four mutually exclusive and exhaustive categories. In this case we will have a particular value based on one condition or another. This or conditional is specified by the | symbol. Only one of the conditions needs to exist for that particular value, whereas when we used the & symbol, all of the conditions had to be met.

If a student has ever tried or currently uses e-cigarettes, but has never tried other tobacco products, the value will be Only e-cigarettes. If a student has ever tried or is a current user of other tobacco products, but has never tried e-cigarettes, the value will be Only other products. If the value of the no_use variable is simply TRUE, then the Group variable value will be Neither. Finally, if a student has tried or currently uses both e-cigarettes and other tobacco products the Group variable value will be Combination of products. Thus in this case the values for the usage of the variables based on only using e-cigarettes or only other products will all be FALSE.

test <- test %>%
  mutate(Group = case_when(ecig_only_ever == TRUE |
                        ecig_only_current == TRUE ~ "Only e-cigarettes",
                       non_ecig_only_ever == TRUE |
                    non_ecig_only_current == TRUE ~ "Only other products",
                                   no_use == TRUE ~ "Neither",
                           ecig_only_ever == FALSE &
                        ecig_only_current == FALSE &
                       non_ecig_only_ever == FALSE &
                    non_ecig_only_current == FALSE &
                                   no_use == FALSE ~ "Combination of products"))


test %>% count(Group)
# A tibble: 4 × 2
  Group                       n
  <chr>                   <int>
1 Combination of products     4
2 Neither                     1
3 Only e-cigarettes           2
4 Only other products         2
glimpse(test)
Rows: 9
Columns: 10
$ ecig_ever             <chr> "TRUE", "TRUE", "TRUE", "TRUE", "FALSE", "FALSE"…
$ non_ecig_ever         <chr> "TRUE", "FALSE", "FALSE", "FALSE", "FALSE", "FAL…
$ ecig_current          <chr> "TRUE", "FALSE", "FALSE", "TRUE", "TRUE", "FALSE…
$ non_ecig_current      <chr> "TRUE", "FALSE", "TRUE", "FALSE", "TRUE", "FALSE…
$ ecig_only_ever        <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ ecig_only_current     <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, …
$ non_ecig_only_ever    <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ non_ecig_only_current <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ no_use                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, FALSE, …
$ Group                 <chr> "Combination of products", "Only e-cigarettes", …

OK, now that we have seen how this works with our toy dataset, we will apply our code to our nyts_data.

nyts_data %<>%
             mutate(ecig_only_ever = case_when(ecig_ever == TRUE &
                                           non_ecig_ever == FALSE &
                                            ecig_current == FALSE &
                                        non_ecig_current == FALSE ~ TRUE,
                                                             TRUE ~ FALSE),
              ecig_only_current = case_when(ecig_current == TRUE &
                                           non_ecig_ever == FALSE &
                                        non_ecig_current == FALSE ~ TRUE,
                                                            TRUE ~ FALSE),
            non_ecig_only_ever = case_when(non_ecig_ever == TRUE &
                                               ecig_ever == FALSE &
                                            ecig_current == FALSE &
                                        non_ecig_current == FALSE ~ TRUE,
                                                            TRUE ~ FALSE),
      non_ecig_only_current = case_when(non_ecig_current == TRUE &
                                               ecig_ever == FALSE &
                                            ecig_current == FALSE ~ TRUE,
                                                            TRUE ~ FALSE),
                        no_use = case_when(non_ecig_ever == FALSE &
                                               ecig_ever == FALSE &
                                            ecig_current == FALSE &
                                        non_ecig_current == FALSE ~ TRUE,
                                                            TRUE ~ FALSE)) %>%
                 mutate(Group = case_when(ecig_only_ever == TRUE |
                                       ecig_only_current == TRUE ~ "Only e-cigarettes",
                                      non_ecig_only_ever == TRUE |
                                   non_ecig_only_current == TRUE ~ "Only other products",
                                                  no_use == TRUE ~ "Neither",
                                          ecig_only_ever == FALSE &
                                       ecig_only_current == FALSE &
                                      non_ecig_only_ever == FALSE &
                                   non_ecig_only_current == FALSE &
                                                  no_use == FALSE ~ "Combination of products"))

Lastly, it can be very helpful to have the total number of students surveyed each year. We can easily add a variable for this by using the add_count() function of the dplyr package. This will create a variable called n which will show the total number of survey responses for that year.

nyts_data %<>% dplyr::add_count(year)

glimpse(nyts_data)
Rows: 95,465
Columns: 59
$ year                  <dbl> 2015, 2015, 2015, 2015, 2015, 2015, 2015, 2015, …
$ psu                   <chr> "015438", "015438", "015438", "015438", "015438"…
$ finwgt                <dbl> 216.7268, 324.9620, 324.9620, 397.1552, 264.8745…
$ stratum               <chr> "BR3", "BR3", "BR3", "BR3", "BR3", "BR3", "BR3",…
$ Age                   <fct> 18, 17, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, …
$ Sex                   <fct> female, male, male, male, female, female, male, …
$ Grade                 <fct> 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, 12, …
$ ECIGT                 <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRU…
$ ECIGAR                <lgl> TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, FA…
$ ESLT                  <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, …
$ EELCIGT               <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRU…
$ EROLLCIGTS            <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, …
$ EFLAVCIGTS            <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, TRUE, F…
$ EBIDIS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ EFLAVCIGAR            <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, TRUE, F…
$ EHOOKAH               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ EPIPE                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ ESNUS                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, TRUE, …
$ EDISSOLV              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CCIGT                 <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CCIGAR                <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ CSLT                  <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CELCIGT               <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, …
$ CROLLCIGTS            <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CFLAVCIGTS            <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CBIDIS                <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CHOOKAH               <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CPIPE                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CSNUS                 <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ CDISSOLV              <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ menthol               <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ clove_spice           <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ fruit                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ chocolate             <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ alcoholic_drink       <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ candy_dessert_sweets  <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ other                 <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ EHTP                  <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ CHTP                  <lgl> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ brand_ecig            <chr> NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, NA, …
$ tobacco_sum_ever      <dbl> 1, 4, 0, 3, 0, 2, 8, 4, 0, 0, 0, 1, 1, 0, 0, 4, …
$ tobacco_sum_current   <dbl> 0, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ tobacco_ever          <lgl> TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ tobacco_current       <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, FALSE, F…
$ ecig_sum_ever         <dbl> 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, …
$ ecig_sum_current      <dbl> 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ non_ecig_sum_ever     <dbl> 1, 3, 0, 2, 0, 1, 7, 3, 0, 0, 0, 0, 1, 0, 0, 3, …
$ non_ecig_sum_current  <dbl> 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
$ ecig_ever             <lgl> FALSE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRU…
$ ecig_current          <lgl> FALSE, FALSE, FALSE, TRUE, FALSE, FALSE, FALSE, …
$ non_ecig_ever         <lgl> TRUE, TRUE, FALSE, TRUE, FALSE, TRUE, TRUE, TRUE…
$ non_ecig_current      <lgl> FALSE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ ecig_only_ever        <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ ecig_only_current     <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ non_ecig_only_ever    <lgl> TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, …
$ non_ecig_only_current <lgl> FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE,…
$ no_use                <lgl> FALSE, FALSE, TRUE, FALSE, TRUE, FALSE, FALSE, F…
$ Group                 <chr> "Only other products", "Combination of products"…
$ n                     <int> 17711, 17711, 17711, 17711, 17711, 17711, 17711,…

Now let’s save our wrangled data for future use. We will save it as an r compatible file (a .rda file), as well as csv files, as these are useful to give to collaborators. We will use the write_csv() function of the readr package to do this.

save(nyts_data, file = here::here("data", "wrangled", "wrangled_data_with_var_for_plots.rda"))
readr::write_csv(nyts_data, 
                 path = here::here("data", "wrangled", "nyts_data_for_plots.csv"))

Question 1



Click here if you skipped the previous sections and want to start here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the wrangled data for plots using the following code:

wrangled_rda("ocs-bp-vaping-case-study", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data_with_var_for_plots.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found in our GitHub repository or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_data_with_var_for_plots.rda"))

Recall that we are interested in investigating how vaping product use has compared with other tobacco use over time. To answer this, we first want to get a sense of how tobacco use has changed in general since 2015.

To create a visualization of how tobacco usage has changed over time, we will first convert the usage data to a percent value for each year, telling us what percent of student respondents fall into a particular usage category. To do this we will use the group_by() and summarize() functions of the dplyr package. This will create new variables which we will name Ever and Current based on the percentages of TRUE values for tobacco_ever and tobacco_current for each year. In this case the mean() function is used to calculate the percentages based on an automatic conversion that R does where for TRUE/FALSE variables, TRUE is given a value of one and FALSE is given a value of zero. The mean of a 0-1 binary variable is just the percent of the time the value is 1. NA values do not contribute to the total count when we include the argument na.rm = TRUE to our function call.


Click here to see a toy example:
# the test data has 3 TRUE values and 7 FALSE values
test <- tibble("var1" = c("TRUE", "TRUE", "TRUE", rep("FALSE", 7)))
test %<>% mutate(var1 = as.logical(var1))
test
# A tibble: 10 × 1
   var1 
   <lgl>
 1 TRUE 
 2 TRUE 
 3 TRUE 
 4 FALSE
 5 FALSE
 6 FALSE
 7 FALSE
 8 FALSE
 9 FALSE
10 FALSE
test %>% summarize(Percentage = mean(var1) * 100)
# A tibble: 1 × 1
  Percentage
       <dbl>
1         30
# the test data has 3 TRUE values, 3 FALSE values, and 4 NA value
test <- tibble("var1" = c("TRUE", "TRUE", "TRUE", rep("FALSE", 3), rep("NA", 4)))
test %<>% mutate(var1 = as.logical(var1))
test
# A tibble: 10 × 1
   var1 
   <lgl>
 1 TRUE 
 2 TRUE 
 3 TRUE 
 4 FALSE
 5 FALSE
 6 FALSE
 7 NA   
 8 NA   
 9 NA   
10 NA   
test %>% summarize(Percentage = mean(var1, na.rm = TRUE) * 100)
# A tibble: 1 × 1
  Percentage
       <dbl>
1         50

And now back to our data:

nyts_data %>%
  dplyr::group_by(year) %>%
  dplyr::summarize(Ever = (mean(tobacco_ever, na.rm = TRUE) * 100),
                   Current = (mean(tobacco_current, na.rm = TRUE) * 100))
# A tibble: 5 × 3
   year  Ever Current
  <dbl> <dbl>   <dbl>
1  2015  36.8    18.0
2  2016  33.4    14.0
3  2017  31.8    14.4
4  2018  34.7    18.7
5  2019  39.7    22.4

We will use the pivot_longer function from the tidyr package to take all columns except year (in this case the Ever and Current columns), to create a column called User that will contain the current column name information and a column called Percentage of students which will contain the mean percentage values that we just calculated. This converts our data into a format called “long” format.

nyts_data %>%
  dplyr::group_by(year) %>%
  dplyr::summarize("Ever \n (any lifetime use)" = (mean(tobacco_ever, na.rm = TRUE) * 100),
                   "Current \n (any past-30-day use)" = (mean(tobacco_current, na.rm = TRUE) * 100)) %>%
  tidyr::pivot_longer(cols = -year,
                      names_to = "User",
                      values_to = "Percentage of students")
# A tibble: 10 × 3
    year User                               `Percentage of students`
   <dbl> <chr>                                                 <dbl>
 1  2015 "Ever \n (any lifetime use)"                           36.8
 2  2015 "Current \n (any past-30-day use)"                     18.0
 3  2016 "Ever \n (any lifetime use)"                           33.4
 4  2016 "Current \n (any past-30-day use)"                     14.0
 5  2017 "Ever \n (any lifetime use)"                           31.8
 6  2017 "Current \n (any past-30-day use)"                     14.4
 7  2018 "Ever \n (any lifetime use)"                           34.7
 8  2018 "Current \n (any past-30-day use)"                     18.7
 9  2019 "Ever \n (any lifetime use)"                           39.7
10  2019 "Current \n (any past-30-day use)"                     22.4

You may have noticed that our data is longer than it used to be! Hence the name of the function pivot_longer(). Data is often easier to plot when it is in this format.

Now we will use this data to create a plot using the ggplot2 package.

The first thing we do to create a plot is specify what data we are using for our x axis and y axis with theaes() argument of the ggplot() function. Then we add layers to our plot that specify what type of plot we would like to create. We can use the geom_line() function to create lines for each type of user. We specify that we want to use different line types for each user using aes(). We will also add points to our lines using the geom_point() function. We can add additional layers to specify colors and details about labels and legends etc.

plot1 <- nyts_data %>%
    dplyr::group_by(year) %>%
    dplyr::summarize("Ever \n (any lifetime use)" = (mean(tobacco_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)" = (mean(tobacco_current, na.rm = TRUE) * 100)) %>%
  pivot_longer(cols = -year, names_to = "User", values_to = "Percentage of students") %>%
  ggplot(aes(x = year, y = `Percentage of students`)) +
  geom_line(aes(linetype = User)) +
  geom_point(show.legend = FALSE, size = 2) +
  # this allows us to choose what type of line we want for each line
  scale_linetype_manual(values = c(1, 2), 
                        breaks = c("Ever \n (any lifetime use)", 
                                   "Current \n (any past-30-day use)")) +
  # this allows us to specify how the y-axis should appear
  scale_y_continuous(breaks = seq(0, 70, by = 10),
                     labels = seq(0, 70, by = 10),
                     limits = c(0, 70)) +
  # this adjusts the background style of the plot
    theme_linedraw() +
  # this moves the legend to the bottom of the plot and removes the x axis title
    theme(legend.position = "bottom",
          axis.title.x = element_blank()) +
    labs(title = "How has tobacco use varied over the years?",
         y = "% of students")

plot1 + theme(text = element_text(size = 15))

Nice! Now we can see how overall tobacco usage has changed since 2017. It appears that usage first decreased from 2015 to 2017 and then increased a bit since 2017, surpassing the levels in 2015.

What about e-cigarette use? How has the usage of e-cigarettes changed over time?

plot1a <- nyts_data %>%
    dplyr::group_by(year) %>%
    dplyr::summarize("Ever \n (any lifetime use)" = (mean(ecig_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)" = (mean(ecig_current, na.rm = TRUE) * 100)) %>%
  pivot_longer(cols = -year, names_to = "User", values_to = "Percentage of students") %>%
  ggplot(aes(x = year, y = `Percentage of students`)) +
  geom_line(aes(linetype = User)) +
  geom_point(show.legend = FALSE, size = 2) +
  # this allows us to choose what type of line we want for each line
  scale_linetype_manual(values = c(1, 2), 
                        breaks = c("Ever \n (any lifetime use)", 
                                   "Current \n (any past-30-day use)")) +
  # this allows us to specify how the y-axis should appear
  scale_y_continuous(breaks = seq(0, 60, by = 10),
                     labels = seq(0, 60, by = 10),
                     limits = c(0, 60)) +
  # this adjusts the background style of the plot
    theme_linedraw() +
  # this moves the legend to the bottom of the plot and removes the x axis title
    theme(legend.position = "bottom",
          axis.title.x = element_blank()) +
    labs(title = "How has e-cigarette use varied over the years?",
         y = "% of students")

plot1a + theme(text = element_text(size = 15))

It looks like the shape of the plot is very similar to tobacco usage overall. We see a downward trend until 2017 when the rate of both current and ever users increased. Recall that this is in agreement with the articles that we referenced earlier. We can see that the slope looks steeper for e-cigarette usage as compared to all tobacco products (including e-cigarettes).

Now let’s plot this data together on the same plot.

We will have four groups (e-cigarette ever users, e-cigarette current users, tobacco ever users, and tobacco current users) to plot, therefore, it would be useful to add color to our plot. Keep in mind that e-cigarette users are a subset of any tobacco product users.

One important thing to keep in mind when creating plots is that individuals with color blindness may have a difficult time distinguishing groups when certain color choices are used.

One great option is to use the viridis package, which offers color palettes with colors that are still distinguishable by individuals with most forms of color blindness.

We can choose which colors we want to use by using the show_col() function of the scales package.

Here are some color options:

scales::show_col(viridis_pal()(6))

v_colors =  viridis(6)[c(1, 4)]

We will select the first and fourth colors for our plot. To add these specific colors we will use the scale_color_manual() function of the ggplot2 package.

We will calculate the mean ever and current usage percentages for students who used e-cigarettes or any tobacco products (including e-cigarettes) for each year again using the group_by() and summarize() functions. We will again use the pivot_longer function to convert our data to long format. We will also use the separate() function of the tidyr package to create two variables from one of the variables. This is done by separating by, in this case, an underscore.

nyts_data %>%
    dplyr::group_by(year) %>%
    dplyr::summarize("Ever \n (any lifetime use)_Any Tobacco Product \n (including e-cigarettes)" = 
                       (mean(tobacco_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)_Any Tobacco Product \n (including e-cigarettes)" =   
                       (mean(tobacco_current, na.rm = TRUE) * 100),
                     "Ever \n (any lifetime use)_E-cigarettes" = 
                       (mean(ecig_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)_E-cigarettes" = 
                       (mean(ecig_current, na.rm = TRUE) * 100)) %>%
  pivot_longer(cols = -year, 
           names_to = "User", 
          values_to = "Percentage of students") %>%
  separate(User, into = c("User", "Product"), sep = "_") %>%
  head()
# A tibble: 6 × 4
   year User                               Product                       Perce…¹
  <dbl> <chr>                              <chr>                           <dbl>
1  2015 "Ever \n (any lifetime use)"       "Any Tobacco Product \n (inc…    36.8
2  2015 "Current \n (any past-30-day use)" "Any Tobacco Product \n (inc…    18.0
3  2015 "Ever \n (any lifetime use)"       "E-cigarettes"                   26.4
4  2015 "Current \n (any past-30-day use)" "E-cigarettes"                   11.0
5  2016 "Ever \n (any lifetime use)"       "Any Tobacco Product \n (inc…    33.4
6  2016 "Current \n (any past-30-day use)" "Any Tobacco Product \n (inc…    14.0
# … with abbreviated variable name ¹​`Percentage of students`
plot1t <- nyts_data %>%
    group_by(year) %>%
    summarize("Ever \n (any lifetime use)_Any Tobacco Product \n (including e-cigarettes)" = 
                (mean(tobacco_ever, na.rm = TRUE) * 100),
              "Current \n (any past-30-day use)_Any Tobacco Product \n (including e-cigarettes)" = 
                (mean(tobacco_current, na.rm = TRUE) * 100),
              "Ever \n (any lifetime use)_E-cigarettes" = 
                (mean(ecig_ever, na.rm = TRUE) * 100),
              "Current \n (any past-30-day use)_E-cigarettes" = 
                (mean(ecig_current, na.rm = TRUE) * 100)) %>%
  pivot_longer(cols = -year, 
           names_to = "User", 
          values_to = "Percentage of students") %>%
  separate(User, 
           into = c("User", "Product"), 
            sep = "_") %>%
    ggplot(aes(x = year, 
               y = `Percentage of students`,
           color = Product)) +
    geom_line(aes(linetype = User)) +
  geom_point(show.legend = FALSE, size = 2) +
  # this allows us to choose what type of line we want for each line
  scale_linetype_manual(values = c(1, 2), 
                        breaks = c("Ever \n (any lifetime use)", 
                                   "Current \n (any past-30-day use)")) +
  # we want purple associated with e-cigarettes to be consistent with later plots
  scale_color_manual(values = rev(v_colors)) +
  # this allows us to specify how the y-axis should appear
  scale_y_continuous(breaks = seq(0, 60, by = 10),
                     labels = seq(0, 60, by = 10),
                     limits = c(0, 60)) +
  # this adjusts the background style of the plot
    theme_linedraw() +
  # this moves the legend to the bottom of the plot and removes the x axis title
    theme(legend.position = "bottom",
          axis.title.x = element_blank()) +
    labs(title = "How has tobacco use varied over the years?",
         y = "% of students")

plot1t + theme(text = element_text(size = 15))

We see an increase in all categories starting in 2017, but the rate of increase is higher for students using only e-cigarettes (current or ever users), as shown by the higher slope of the e-cigarette lines.

In the above plots, the “Any tobacco product” groups include individuals in the “E-cigarette only” groups. Now let’s plot students in two mutually exclusive groups on the same plot: those who reported either using only e-cigarettes or only other tobacco products (besides e-cigarettes), but not both.

We will calculate the mean ever and current usage percentages for students in these two mutually exclusive groups, again using the group_by() function and the summarize() function. We will again use the pivot_longer function to convert our data to long format. We will also again use the separate() function of the tidyr package to create two variables from one variable. This is done by separating by, in this case, a space.

nyts_data %>%
    dplyr::group_by(year) %>%
    dplyr::summarize("Ever \n (any lifetime use)_E-cigarette" = 
                       (mean(ecig_only_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)_E-cigarette" = 
                       (mean(ecig_only_current, na.rm = TRUE) * 100),
                     "Ever \n (any lifetime use)_Non-e-cigarette" = 
                       (mean(non_ecig_only_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)_Non-e-cigarette" = 
                       (mean(non_ecig_only_current, na.rm = TRUE) * 100)) %>%
  pivot_longer(cols = -year, 
           names_to = "User", 
          values_to = "Percentage of students") %>%
  tidyr::separate(User, into = c("User", "Product"), sep = "_") %>%
  head()
# A tibble: 6 × 4
   year User                               Product         Percentage of stude…¹
  <dbl> <chr>                              <chr>                           <dbl>
1  2015 "Ever \n (any lifetime use)"       E-cigarette                      4.36
2  2015 "Current \n (any past-30-day use)" E-cigarette                      1.54
3  2015 "Ever \n (any lifetime use)"       Non-e-cigarette                  7.06
4  2015 "Current \n (any past-30-day use)" Non-e-cigarette                  3.35
5  2016 "Ever \n (any lifetime use)"       E-cigarette                      4.54
6  2016 "Current \n (any past-30-day use)" E-cigarette                      1.23
# … with abbreviated variable name ¹​`Percentage of students`
plot1c <- nyts_data %>%
    dplyr::group_by(year) %>%
    dplyr::summarize("Ever \n (any lifetime use)_E-cigarette" = 
                       (mean(ecig_only_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)_E-cigarette" = 
                       (mean(ecig_only_current, na.rm = TRUE) * 100),
                     "Ever \n (any lifetime use)_Non-e-cigarette" = 
                       (mean(non_ecig_only_ever, na.rm = TRUE) * 100),
                     "Current \n (any past-30-day use)_Non-e-cigarette" = 
                       (mean(non_ecig_only_current, na.rm = TRUE) * 100)) %>%
  pivot_longer(cols = -year, 
           names_to = "User", 
          values_to = "Percentage of students") %>%
  separate(User, into = c("User", "Product"), sep = "_") %>%
    ggplot(aes(x = year, y = `Percentage of students`, color = Product)) +
    geom_line(aes(linetype = User)) +
  geom_point(show.legend = FALSE, size = 2) +
  # this allows us to choose what type of line we want for each line
  scale_linetype_manual(values = c(1, 2), 
                        breaks = c("Ever \n (any lifetime use)", 
                                   "Current \n (any past-30-day use)")) +
  # this allows us to specify how the y-axis should appear
  scale_y_continuous(breaks = seq(0, 30, by = 10),
                     labels = seq(0, 30, by = 10),
                     limits = c(0, 30)) +
  scale_color_manual(values = v_colors) +
  # this adjusts the background style of the plot
    theme_linedraw() +
  # this moves the legend to the bottom of the plot and removes the x axis title
    theme(legend.position = "bottom",
          axis.title.x = element_blank()) +
    labs(title = "How has use of only e-cigarettes and
only tobacco products besides e-cigarettes varied over time?",
         y = "% of students")

plot1c + theme(text = element_text(size = 15))

Very interesting! We can see from this plot that the percentage of students who had currently used (or ever tried) only e-cigarettes greatly increased starting in 2017, while in contrast the percentage of students who had ever tried only non-e-cigarette tobacco products actually diminished over time. In fact, we can see that in 2019 the percentage of students who were current e-cigarette users surpassed the percentage that had tried a non-e-cigarette product even just once.

Recall that we made a variable called Group that identified students who used either just e-cigarette products, just other tobacco products (besides e-cigarettes), or students who used both e-cigarettes and some other type of tobacco product.

nyts_data %>%
  count(Group)
# A tibble: 4 × 2
  Group                       n
  <chr>                   <int>
1 Combination of products 16517
2 Neither                 61738
3 Only e-cigarettes        7866
4 Only other products      9344

We will now make a plot over time of each of these groups. Since we will have 4 total groups, we will use 4 of the viridis colors. Notice, that in this case we are grouping by three variables by simply separating the variables that we want to group by with a comma in our group_by() function like this: group_by(Group, year, n).

nyts_data %>%
  group_by(Group, year, n) %>%
  summarize(group_count = n()) %>%
  mutate("Percentage of students" = group_count / n * 100) %>%
  head()
# A tibble: 6 × 5
# Groups:   Group, year [6]
  Group                    year     n group_count `Percentage of students`
  <chr>                   <dbl> <int>       <int>                    <dbl>
1 Combination of products  2015 17711        3634                     20.5
2 Combination of products  2016 20675        3297                     15.9
3 Combination of products  2017 17872        2623                     14.7
4 Combination of products  2018 20189        3321                     16.4
5 Combination of products  2019 19018        3642                     19.2
6 Neither                  2015 17711       11188                     63.2
v_colors =  viridis(5)[1:4]

nyts_data %>%
  group_by(Group, year, n) %>%
  summarize(group_count = n()) %>%
  mutate("Percentage of students" = group_count / n * 100) %>%
  ggplot(aes(x = year, y = `Percentage of students`, color = Group)) +
  geom_point(size = 2) +
  geom_line() +
  scale_color_manual(breaks = c("Neither", "Combination of products",
                                "Only e-cigarettes", "Only other products"),
                     values = v_colors) +
  theme_linedraw() +
  labs(x = "Year") +
  theme(text = element_text(size = 15))

We can see that the majority of students did not report using any tobacco products. Of the students that did report using tobacco products, the majority of the students used both e-cigarettes and some other tobacco product. Again, a much larger percentage reported using only e-cigarettes rather than only other tobacco products in 2019.

We will further explore the relationship between e-cigarette usage and other tobacco products a bit later in the case study.

Question 2


Now we want to look how e-cigarette smoking rates compare between males and females across time.

We will calculate the percent ever and current e-cigarette users for each year and sex category again using the group_by() function and the summarize() function. We will again use the pivot_longer function to convert our data to long format.

As discussed above, we acknowledge that while gender and sex are not actually binary, the data used in this analysis only contain information for groups of individuals who answered the survey questions as male or female. For individuals that have NA values, it is unclear if the question was not answered or if the individual identifies as non-binary. Because of this uncertainty, we will filter these individuals out.

# use different colors here for males and females to differentiate from the previous plots
v_colors =  viridis(6)[c(3, 5)]

nyts_data %>%
     filter(!is.na(Sex)) %>%
     group_by(year, Sex) %>%
     summarize("Ever \n (any lifetime use)" = (mean(EELCIGT, na.rm = TRUE) * 100),
               "Current \n (any past-30-day use)" = (mean(CELCIGT, na.rm = TRUE) * 100)) %>%
     pivot_longer(cols = "Ever \n (any lifetime use)":"Current \n (any past-30-day use)",
                  names_to = "User",
                  values_to = "Percentage of students") %>%
     head()
# A tibble: 6 × 4
# Groups:   year [2]
   year Sex    User                               `Percentage of students`
  <dbl> <fct>  <chr>                                                 <dbl>
1  2015 male   "Ever \n (any lifetime use)"                          29.3 
2  2015 male   "Current \n (any past-30-day use)"                    13.3 
3  2015 female "Ever \n (any lifetime use)"                          24.3 
4  2015 female "Current \n (any past-30-day use)"                     9.05
5  2016 male   "Ever \n (any lifetime use)"                          24.1 
6  2016 male   "Current \n (any past-30-day use)"                     8.72
plot2 <- nyts_data %>%
     filter(!is.na(Sex)) %>%
     group_by(year, Sex) %>%
     summarize("Ever \n (any lifetime use)" = (mean(EELCIGT, na.rm = TRUE) * 100),
               "Current \n (any past-30-day use)" = (mean(CELCIGT, na.rm = TRUE) * 100)) %>%
     pivot_longer(cols = "Ever \n (any lifetime use)":"Current \n (any past-30-day use)",
                  names_to = "User",
                  values_to = "Percentage of students") %>%
    ggplot(aes(x = year, y = `Percentage of students`, color = Sex)) +
    geom_line(aes(linetype = User)) +
    geom_point(show.legend = FALSE, size = 2) +
    scale_linetype_manual(values = c(2, 1)) +
    scale_color_manual(values = v_colors) +
    theme_linedraw() +
    theme(legend.position = "bottom",
          axis.title.x = element_blank()) +
    labs(title = "How does e-cigarette usage compare between males and females?",
         subtitle = "Current and ever users by sex",
         y = "% of students")

plot2 + theme(text = element_text(size = 15))

It looks like the rates are fairly similar between the sexes, however the rate for males appears to be consistently higher across time.

Question 3


We are also interested in what vaping brands and flavors appear to be used the most frequently. Only the 2019 dataset has this information. Therefore, we will filter for just this year using the filter() function of the dplyr package. We will use the summarize() function slightly differently this time, to calculate the total number of students using each brand using the n() function and the sum() function to calculate the percent for each brand based on the counts. We will also reorder the factor levels for the brand names so that they are in descending order of percent use, using the fct_reorder() function from dplyr. This will make them appear in decreasing order of percent use on the plot.

We will make a bar plot this time by using geom_bar. Importantly we assign the stat argument to identity, so that we are using the percentages that we calculated not the counts which is what is used by default. When color in specified outside of the aes() argument, this determines the border color of the bars, which in this case will be black.

nyts_data %>%
  filter(year == 2019) %>%
  group_by(brand_ecig) %>%
  filter(!is.na(brand_ecig)) %>%
  summarize(n = n()) %>%
  mutate(total = sum(n),
         Percent = n * 100 / total) %>%
  mutate(brand_ecig = fct_reorder(brand_ecig, desc(Percent)))
# A tibble: 7 × 4
  brand_ecig     n total Percent
  <fct>      <int> <int>   <dbl>
1 Blu          111  3604   3.08 
2 JUUL        2028  3604  56.3  
3 Logic         36  3604   0.999
4 MarkTen       32  3604   0.888
5 NJOY          44  3604   1.22 
6 Other       1253  3604  34.8  
7 Vuse         100  3604   2.77 
plot3 <- nyts_data %>%
  filter(year == 2019) %>%
  group_by(brand_ecig) %>%
  filter(!is.na(brand_ecig)) %>%
  summarize(n = n()) %>%
  mutate(total = sum(n),
         Percent = n * 100 / total) %>%
  mutate(brand_ecig = fct_reorder(brand_ecig, desc(Percent))) %>%
  ggplot(aes(x = brand_ecig, y = Percent, fill = brand_ecig)) +
  geom_bar(stat = "identity", color = "black") +
  theme_linedraw() +
  theme(
    legend.position = "none",
    axis.title.x = element_blank()
  ) +
  labs(title = "What vaping brands appear to be used the most frequently?",
       subtitle = "Brand of e-cigarette most frequently used in the last 30 days (2019)",
       y = "% of e-cigarette users responding")

plot3 + theme(text = element_text(size = 15))

Juul appears to be the most widely used brand. This is in agreement with a recent article, whose most recent data was from 2017:

Huang J, Duan Z, Kwok J, et al. Tob Control 2019;28:146–151.

Huang J, Duan Z, Kwok J, et al. Tob Control 2019;28:146–151.

We are also interested in how the usage of different flavors has changed over time.

To evaluate this we will calculate the percentage of students using each flavor each year - this includes usage of any type of flavored tobacco product. We will exclude 2015 data, as no specific flavor questions were asked at that time.

Recall that NA values are not included in calculating the total count for our percentages. However all of these flavor questions had complete reporting and did not have NA values. Therefore, these values reflect the percentage of students reporting using a particular favor out of all students surveyed (including those that did not use any tobacco products). Also students were allowed to select more than one flavor. You can see whether these variables had complete reporting by checking the NA values using the base summary function. Alternatively you can create a visual representation using the vis_miss() function of the naniar package.

# Scroll through the output!
nyts_data %>%
  filter(year != 2015) %>%
  summary()
      year          psu                finwgt          stratum         
 Min.   :2016   Length:77754       Min.   :  11.15   Length:77754      
 1st Qu.:2016   Class :character   1st Qu.: 681.08   Class :character  
 Median :2018   Mode  :character   Median :1113.83   Mode  :character  
 Mean   :2017                      Mean   :1394.51                     
 3rd Qu.:2018                      3rd Qu.:1748.80                     
 Max.   :2019                      Max.   :6505.08                     
                                                                       
      Age            Sex            Grade         ECIGT           ECIGAR       
 13     :11781   male  :39191   7      :11978   Mode :logical   Mode :logical  
 14     :11301   female:37916   6      :11606   FALSE:63138     FALSE:65345    
 12     :11045   *     :    0   8      :11528   TRUE :13463     TRUE :11066    
 15     :10963   .N    :    0   9      :11049   NA's :1153      NA's :1343     
 16     :10765   NA's  :  647   10     :10609                                  
 (Other):21551                  (Other):20597                                  
 NA's   :  348                  NA's   :  387                                  
    ESLT          EELCIGT        EROLLCIGTS      EFLAVCIGTS       EBIDIS       
 Mode :logical   Mode :logical   Mode :logical   Mode:logical   Mode :logical  
 FALSE:71048     FALSE:56690     FALSE:71966     NA's:77754     FALSE:74618    
 TRUE :5202      TRUE :19660     TRUE :3574                     TRUE :911      
 NA's :1504      NA's :1404      NA's :2214                     NA's :2225     
                                                                               
                                                                               
                                                                               
 EFLAVCIGAR       EHOOKAH          EPIPE           ESNUS        
 Mode :logical   Mode :logical   Mode :logical   Mode :logical  
 FALSE:18012     FALSE:69763     FALSE:73760     FALSE:72777    
 TRUE :1889      TRUE :6318      TRUE :1768      TRUE :2751     
 NA's :57853     NA's :1673      NA's :2226      NA's :2226     
                                                                
                                                                
                                                                
  EDISSOLV         CCIGT           CCIGAR           CSLT        
 Mode :logical   Mode :logical   Mode :logical   Mode :logical  
 FALSE:74560     FALSE:72481     FALSE:72261     FALSE:73719    
 TRUE :970       TRUE :3797      TRUE :4017      TRUE :2190     
 NA's :2224      NA's :1476      NA's :1476      NA's :1845     
                                                                
                                                                
                                                                
  CELCIGT        CROLLCIGTS      CFLAVCIGTS       CBIDIS         CHOOKAH       
 Mode :logical   Mode :logical   Mode:logical   Mode :logical   Mode :logical  
 FALSE:67314     FALSE:73864     NA's:77754     FALSE:75102     FALSE:73573    
 TRUE :9205      TRUE :1608                     TRUE :381       TRUE :2282     
 NA's :1235      NA's :2282                     NA's :2271      NA's :1899     
                                                                               
                                                                               
                                                                               
   CPIPE           CSNUS          CDISSOLV        menthol       
 Mode :logical   Mode :logical   Mode :logical   Mode :logical  
 FALSE:74796     FALSE:74402     FALSE:75048     FALSE:73305    
 TRUE :664       TRUE :1066      TRUE :423       TRUE :4449     
 NA's :2294      NA's :2286      NA's :2283                     
                                                                
                                                                
                                                                
 clove_spice       fruit         chocolate       alcoholic_drink
 Mode :logical   Mode :logical   Mode :logical   Mode :logical  
 FALSE:77360     FALSE:71945     FALSE:76875     FALSE:76510    
 TRUE :394       TRUE :5809      TRUE :879       TRUE :1244     
                                                                
                                                                
                                                                
                                                                
 candy_dessert_sweets   other            EHTP            CHTP        
 Mode :logical        Mode :logical   Mode :logical   Mode :logical  
 FALSE:74188          FALSE:75675     FALSE:16633     FALSE:18582    
 TRUE :3566           TRUE :2079      TRUE :398       TRUE :291      
                                      NA's :60723     NA's :58881    
                                                                     
                                                                     
                                                                     
  brand_ecig        tobacco_sum_ever  tobacco_sum_current tobacco_ever   
 Length:77754       Min.   : 0.0000   Min.   : 0.0000     Mode :logical  
 Class :character   1st Qu.: 0.0000   1st Qu.: 0.0000     FALSE:50602    
 Mode  :character   Median : 0.0000   Median : 0.0000     TRUE :27152    
                    Mean   : 0.8742   Mean   : 0.3334                    
                    3rd Qu.: 1.0000   3rd Qu.: 0.0000                    
                    Max.   :11.0000   Max.   :11.0000                    
                                                                         
 tobacco_current ecig_sum_ever    ecig_sum_current non_ecig_sum_ever
 Mode :logical   Min.   :0.0000   Min.   :0.0000   Min.   : 0.0000  
 FALSE:64229     1st Qu.:0.0000   1st Qu.:0.0000   1st Qu.: 0.0000  
 TRUE :13525     Median :0.0000   Median :0.0000   Median : 0.0000  
                 Mean   :0.2528   Mean   :0.1184   Mean   : 0.6213  
                 3rd Qu.:1.0000   3rd Qu.:0.0000   3rd Qu.: 1.0000  
                 Max.   :1.0000   Max.   :1.0000   Max.   :10.0000  
                                                                    
 non_ecig_sum_current ecig_ever       ecig_current    non_ecig_ever  
 Min.   : 0.000       Mode :logical   Mode :logical   Mode :logical  
 1st Qu.: 0.000       FALSE:58094     FALSE:68549     FALSE:57436    
 Median : 0.000       TRUE :19660     TRUE :9205      TRUE :20318    
 Mean   : 0.215                                                      
 3rd Qu.: 0.000                                                      
 Max.   :10.000                                                      
                                                                     
 non_ecig_current ecig_only_ever  ecig_only_current non_ecig_only_ever
 Mode :logical    Mode :logical   Mode :logical     Mode :logical     
 FALSE:69182      FALSE:73369     FALSE:75318       FALSE:72687       
 TRUE :8572       TRUE :4385      TRUE :2436        TRUE :5067        
                                                                      
                                                                      
                                                                      
                                                                      
 non_ecig_only_current   no_use           Group                 n        
 Mode :logical         Mode :logical   Length:77754       Min.   :17872  
 FALSE:75321           FALSE:27204     Class :character   1st Qu.:19018  
 TRUE :2433            TRUE :50550     Mode  :character   Median :20189  
                                                          Mean   :19499  
                                                          3rd Qu.:20675  
                                                          Max.   :20675  
                                                                         

nyts_data %>%
  filter(year != 2015) %>%
  select(menthol:alcoholic_drink) %>%
  vis_miss()

The plot above confirms that these variables have no NA values (because all fields indicate 100% of data is present).

plot4 <- nyts_data %>%
  filter(year != 2015) %>%
  group_by(year) %>%
      summarize(Menthol = (mean(menthol) * 100),
       `Clove or Spice` = (mean(clove_spice) * 100),
                  Fruit = (mean(fruit) * 100),
              Chocolate = (mean(chocolate) * 100),
      `Alcoholic Drink` = (mean(alcoholic_drink) * 100),
`Candy/Desserts/Sweets` = (mean(candy_dessert_sweets) * 100),
                  Other = (mean(other) * 100)) %>%
      pivot_longer(cols = -year, 
               names_to = "Flavor",
              values_to = "Percentage of students") %>%
  rename(Year = year) %>%

 ggplot(aes(y = `Percentage of students`,
            x = Year,
            fill = reorder(Flavor, `Percentage of students`))) +
  geom_bar(stat = "identity",
           position = "dodge",
           color = "black") +
  scale_fill_viridis(discrete = TRUE) +
  theme_linedraw() +
  guides(fill = guide_legend("Flavor")) +
  labs(title = "What flavors appear to be used the most frequently?",
       subtitle = "Flavors of tobacco products used in the past 30 days")

plot4 + theme(text = element_text(size = 15))

From this plot, we can see that fruit flavors are the most widely used products, followed by menthol or mint flavored products. We can also see that there was a general increase in the usage of flavored products over time.

We will now look specifically at the usage of flavored e-cigarette products vs other flavored tobacco products.

Recall that we made a variable called Group that identified students who used either just e-cigarette/vaping products, just other tobacco products (besides e-cigarettes), or students who used both e-cigarettes and some other type of tobacco product. We will compare the usage of these flavors for these different groups. We also perform some data summaries to decide how to order the panels (flavors) for display.

v_colors =  viridis(5)[1:4]

plot5 <- nyts_data %>%
  filter(year != 2015) %>%
  group_by(year, Group) %>%
        summarize(Menthol = (mean(menthol) * 100),
         `Clove or Spice` = (mean(clove_spice) * 100),
                    Fruit = (mean(fruit) * 100),
                Chocolate = (mean(chocolate) * 100),
        `Alcoholic Drink` = (mean(alcoholic_drink) * 100),
`Candy/Desserts/\nSweets` = (mean(candy_dessert_sweets) * 100),
                    Other = (mean(other) * 100),
              Respondents = n()) %>%
  # converting columns between and including Menthol and Other to one column called Flavor
  pivot_longer(cols = Menthol:Other, 
           names_to = "Flavor", 
          values_to = "Percentage of students") %>%
  group_by(Flavor) %>%
  # calculate the count of students in the year/group combination who used that flavor
  mutate(affirmative = (Respondents * `Percentage of students`) / 100) %>%
  # calculate the fraction of total respondents who used that flavor
  mutate(flavor_mean = sum(affirmative) / sum(Respondents)) %>%
  ungroup() %>%
  # reorder the levels of Flavor to be in increasing order of percent of students
  mutate(flavor_mean_rank = dense_rank(flavor_mean),
         Flavor = fct_reorder(Flavor, flavor_mean_rank)) %>%
  ggplot(aes(x = year, 
             y = `Percentage of students`, 
         color = Group)) +
  facet_grid(~Flavor) +
  geom_line() +
  geom_point(show.legend = FALSE, size = 2) +
  scale_color_manual(values = v_colors) +
  theme_linedraw() +
  theme(legend.position = "bottom",
        axis.title.x = element_blank(),
        axis.text.x = element_text(angle = 90),
        strip.text.x = element_text(size = 10, face = "bold")) +
  labs(title = "Among different product users, what flavors are most frequently used?")

plot5 + theme(text = element_text(size = 15))

We can see from this plot that there has been an increase in the number of students reporting using flavored tobacco products. Users who use both e-cigarettes and other tobacco products appear to report using flavored products the most, followed by users who only use e-cigarettes.

Question 4


Is there a relationship between e-cigarette use and tobacco use? Now we will investigate the usage of e-cigarettes compared to other tobacco products in greater depth.

First let’s take a look at how e-cigarette usage and cigarette usage compare. We will select the data that specifically has to do with these products.

v_colors =  viridis(6)[c(1, 4)]

nyts_data %>%
    group_by(year) %>%
    summarize("Cigarettes, Ever \n (any lifetime use)" = (mean(ECIGT, na.rm = TRUE) * 100),
            "E-cigarettes, Ever \n (any lifetime use)" = (mean(EELCIGT, na.rm = TRUE) * 100),
           "Cigarettes, Current \n (any past-30-day use)" = (mean(CCIGT, na.rm = TRUE) * 100),
         "E-cigarettes, Current \n (any past-30-day use)" = (mean(CELCIGT, na.rm = TRUE) * 100)) %>%
    pivot_longer(cols = - year, 
             names_to = "Category", 
            values_to = "Percentage of students") %>%
    separate(Category, into = c("Product", "User"), sep = ", ") %>%
    head()
# A tibble: 6 × 4
   year Product      User                               `Percentage of students`
  <dbl> <chr>        <chr>                                                 <dbl>
1  2015 Cigarettes   "Ever \n (any lifetime use)"                          21.3 
2  2015 E-cigarettes "Ever \n (any lifetime use)"                          26.9 
3  2015 Cigarettes   "Current \n (any past-30-day use)"                     6.23
4  2015 E-cigarettes "Current \n (any past-30-day use)"                    11.2 
5  2016 Cigarettes   "Ever \n (any lifetime use)"                          19.1 
6  2016 E-cigarettes "Ever \n (any lifetime use)"                          22.1 
plot6 <- nyts_data %>%
  group_by(year) %>%
  summarize(
    "Cigarettes, Ever \n (any lifetime use)" = (mean(ECIGT, na.rm = TRUE) * 100),
    "E-cigarettes, Ever \n (any lifetime use)" = (mean(EELCIGT, na.rm = TRUE) * 100),
    "Cigarettes, Current \n (any past-30-day use)" = (mean(CCIGT, na.rm = TRUE) * 100),
    "E-cigarettes, Current \n (any past-30-day use)" = (mean(CELCIGT, na.rm = TRUE) * 100)
  ) %>%
  pivot_longer(cols = -year,
               names_to = "Category",
               values_to = "Percentage of students") %>%
  separate(Category, into = c("Product", "User"), sep = ", ") %>%
  ggplot(aes(
    x = year,
    y = `Percentage of students`,
    color = Product,
    linetype = User
  )) +
  geom_line() +
  geom_point(show.legend = FALSE, size = 2) +
  scale_linetype_manual(values = c(2, 1)) +
  scale_color_manual(values = v_colors) +
  theme_linedraw() +
  theme(legend.position = "bottom",
        axis.title.x = element_blank()) +
  labs(title = "How does e-cigarette use compare to cigarette use?",
       subtitle = "Current and ever users of e-cigarettes and cigarettes",
       y = "% of students")

plot6 + theme(text = element_text(size = 15))

Interesting! we can see that in 2019 the percentage of students that reported currently using e-cigarettes had surpassed those that ever tried (even just once) a cigarette. Overall cigarette usage appears to be declining over time. This is not the case for e-cigarettes.

Now we will look at students who reported that they had ever tried e-cigarettes or non-cigarette products. In this case we will not separate out users who specifically only used one or the other. Therefore, the students included in this plot who reported as having ever tried e-cigarettes might also be current users of non-e-cigarette products or may have at least tried non-e-cigarette products.

v_colors =  viridis(6)[c(1, 4)]

plot7 <- nyts_data %>%
  group_by(year) %>%
  summarize(
    `e-cigarette_ever` = (mean(ecig_ever, na.rm = TRUE) * 100),
    `non-e-cigarette_ever` = (mean(non_ecig_ever, na.rm = TRUE) * 100)
  ) %>%
  pivot_longer(cols = -year,
               names_to = "Category",
               values_to = "Percentage of students") %>%
  separate(Category, into = c("Product", "User"), sep = "_") %>%
  ggplot(aes(x = year,
             y = `Percentage of students`,
             color = Product)) +
  geom_line() +
  geom_point(show.legend = FALSE, size = 2) +
  scale_color_manual(values = v_colors) +
  scale_y_continuous(breaks = seq(0, 60, by = 10), limits = c(0, 60)) +
  theme_linedraw() +
  theme(legend.position = "bottom",
        axis.title.x = element_blank()) +
  labs(title = "How does the rate of ever trying e-cigarettes
compare to ever trying other products over time?",
y = "% of students")

plot7 + theme(text = element_text(size = 15))

Now we will do the same, but for students who reported currently using e-cigarettes or non-e-cigarette products.

v_colors =  viridis(6)[c(1, 4)]

plot8 <- nyts_data %>%
  group_by(year) %>%
  summarize(
    `e-cigarette_current` = (mean(ecig_current, na.rm = TRUE) * 100),
    `non-e-cigarette_current` = (mean(non_ecig_current, na.rm = TRUE) * 100)
  ) %>%
  pivot_longer(cols = -year,
               names_to = "Category",
               values_to = "Percentage of students") %>%
  separate(Category, into = c("Product", "User"), sep = "_") %>%
  ggplot(aes(x = year, y = `Percentage of students`, color = Product)) +
  geom_line(linetype = "dashed") +
  geom_point(show.legend = FALSE, size = 2) +
  scale_color_manual(values = v_colors) +
  scale_linetype_manual(values = c(1)) +
  scale_y_continuous(breaks = seq(0, 60, by = 10), limits = c(0, 60)) +
  theme_linedraw() +
  theme(legend.position = "bottom",
        axis.title.x = element_blank()) +
  labs(title = "How does the rate of currently using e-cigarettes
compare to currently using other products over time?",
       y = "% of students")

plot8 + theme(text = element_text(size = 15))

Putting plots together


Now we will put these plots together using the plot_grid() function of the cowplot package. We will also modify the labels using the ggdraw() function, which is also part of the cowplot package. To learn more about the cowplot package, refer to this case study.

plotA_uw <- plot1 +
  theme(axis.title.x = element_blank(),
        legend.position = "none") +
  labs(title = "Tobacco product users more prevalent after 2017",
       subtitle = NULL,
       y = "% of students")

plotB_uw <- plot7 +
  theme(axis.title.x = element_blank(),
        legend.position = "none") +
    labs(title = "% Ever trying e-cigarettes increases &
% Ever trying other products decreases",
         subtitle = NULL,
         y = "% of students")

plotC_uw <- plot8 +
  theme(axis.title.x = element_blank(),
        legend.position = "none") +
    labs(title = "% Currently using e-cigarettes increases &
% Currently using other products decreases",
         subtitle = NULL,
         y = "% of students")

title_uw <- ggdraw() +
  draw_label(
    "Is there a relationship between e-cigarette use and tobacco use?",
    fontface = 'bold',
    size = 14,
    x = 0,
    hjust = 0
  ) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

plotsA_uw <- plot_grid(plotA_uw,
                     rel_widths = c(1, 1))
plotsBC_uw <- plot_grid(plotB_uw,
                        plotC_uw,
                        rel_widths = c(1, 1))

# this will take the legend from plot1c to use as the legend for the plot we are creating
legend_uw <- get_legend(plot1c +
                       theme(legend.position = "bottom",
                             legend.direction = "horizontal"))

figure_uw <- plot_grid(title_uw,
                       plotsA_uw,
                       plotsBC_uw,
                       legend_uw,
                       ncol = 1,
                       rel_heights = c(0.1,
                                       1,
                                       1,
                                       0.1),
                       scale = 1.0)

figure_uw

Survey Weighting



Click here if you skipped the previous sections and want to start here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the wrangled data for plots using the following code:

wrangled_rda("ocs-bp-vaping-case-study", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data_with_var_for_plots.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found in our GitHub repository or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_data_with_var_for_plots.rda"))

It turns out that our analysis thus far has been brushing an important statistical concept under the rug, related to how our data were collected. Our data come from responses to a survey, which may have a particular sampling scheme to capture data about the population we are interested in. For example, the survey may be designed to capture a set of individuals who reflect the characteristics of the population that we are interested in drawing conclusions about. However, only a fraction of the individuals who were contacted about taking the survey may have completed it, and this fraction of individuals may no longer be representative of the population. Or the survey may be designed to over-sample a particular group of interest so that individuals from that group show up more often as survey respondents than are present in the population overall. In order to account for the fact that the survey respondents may not reflect the composition of the population we want to generalize to, we can employ a technique called survey weighting.

Survey weighting is a common technique used in survey data analysis because often the individuals that take a survey are not necessarily representative of the population that we are trying to gather information about. For example, we may have more females that respond to the survey than males because perhaps female students were more willing to participate. In this case, the proportion of data values in our data will be smaller for the males than the proportion of actual male students and larger for the females than the true proportion of actual female students. To get a better estimate of overall e-cigarette smoking rates, the data from the males can be weighted based on the true proportion of male students to amplify the contribution of the responses from the males that did participate. Conversely, the female data can be weighted to diminish the contribution if their responses to the overall picture. We will see if using survey weighting changes the general trends that we see in our data.

Calculating survey weights involves making a weight based on the ratio of the proportion of survey respondents from a particular group and the actual proportion of that group in the population. For example, let’s say that females account for 50% of the population and males account for 50 % of the population. Let’s also say that 75% of the respondents to the survey were female and only 25% were males.

Then we could calculate survey weights using this formula:

\[ \frac{\text{actual proportion of group in the population}}{\text{ proportion of group in the respondents}}\]

Thus the weight for the females would be calculated as:

\[ \frac{.5}{.75} = .67\]

The weight for the males would be calculated as:

\[ \frac{.5}{.25} = 2\]

Therefore each male response value would be multiplied by a factor of 2 and would have twice the contribution, while the female response values would have only about 70% of the contribution that they would have had without weighting.

Note that survey weights are in reality corrected for other aspects - for example the response rate to individual questions.

We do not need to calculate survey weights for our data as they were already supplied in the dataset, as described in the codebooks.

srvyr package and survey design


We will now use the srvyr package to evaluate our data using survey weights that were provided in the data for each year, as described in the respective codebooks. This package contains functions that allow the user to easily perform calculations from the data that take the survey design into account, without having to work out the math by hand.

Within the data you will see that we have three variables related to the survey sampling scheme: psu, finwgt, and stratum. Details about these variables are available, for example, in the 2019 Methodology Report.

In brief they represent:

  1. psu: Surveys like the one used to create the data we are using often sample people based on strata. This is done to ensure that the responses are representative of the population of interest. Thus, often people first think about ensuring that surveys are conducted in a variety of geographical areas. This is often called the primary sampling unit or PSU. In this survey, the county where the student’s school was located was used as the PSU.

  2. stratum: A categorical variable that indicates subsets of the data that include respondents from different PSUs. In our case, strata are determined by the predominant minority in the PSU (Non-Hispanic Black or Hispanic), whether the PSU is urban or non-urban, and what percent of the students in the PSU fall into the predominant minority group. PSUs are allocated across the 16 possible strata according to the sampling scheme. These strata values allow estimates based on the survey responses to be calculated using different strata allowing for improved precision of the response estimates.

  3. finwgt: The survey weight which was calculated based on a variety of factors.

This link and this link have more information about the study design of the data that we are using.

For detailed information on such survey designs in general see here and here.

We will use the as_survey_design() function of the srvyrpackage to create a survey object with a specified survey design. This is a special R object that includes information about how the survey was conducted that can be taken into account in the analysis.

There are several arguments to pay attention to:

  1. The strata argument is used to specify the variable(s) that defined strata in the data. In this case, we will use the stratum variable.
  2. The ids argument is used to define cluster ids within the data. In this case we will use the psu variable.
  3. The weight argument is the used to define which variable(s) are the survey weights.
  4. The nest = TRUE argument, forces cluster ids (in this case the PSU) to be nested within the strata.

We can then use the survey_mean() function to calculate percentages of students who report using tobacco for each year while accounting for the survey design and weights. We will specify that we want confidence interval estimates by using the vartype = "ci" argument. The confidence intervals in our case give a range of possible values for the true population mean based on the data observed in the survey. We will multiply these values by 100 to get percentages. (Note: We could also have calculated confidence intervals for the unweighted results above by computing them by hand; we leave this as a potential exercise.)

Since the survey weights are specific to a single year of the survey results, we need to create survey design objects for each year separately. We will use group_by and group_modify, which is also from the dplyr package, to do this. We first write the function that we want to call on each group.

This function takes an input called currYear, which will be one set of survey responses for a specific year, and then creates a survey design based on the stratum and finwgt values specific to that year. It then calculates the percent of student respondents who have ever tried any tobacco products or who are a current user of any tobacco products accounting for the survey design and weights using survey_mean() as was just described. The function then wrangles the data to convert the means to percentages and reformat the data in long form for plotting.

One technical note: since some years have strata with a single PSU, we need to tell the survey weighting package how to handle estimating within strata variances. The line options(survey.lonely.psu = "adjust") tells R to center the stratum with the single PSU on the sample grand mean, a conservative approach to solving the problem. See further information here and here.

Weighted Sample


First, we show the basic output of the survey_mean function by year. Since we include the argument vartype = "ci", we get a mean and upper and lower confidence interval bounds for the mean.

surveyMeanA <- function(currYear) {
  options(survey.lonely.psu = "adjust")
  currYear %>%
  as_survey_design(strata = stratum,
                      ids = psu,
                  weight  = finwgt,
                     nest = TRUE) %>%
   summarize(tobacco_ever = survey_mean(tobacco_ever,
                                        vartype = "ci",
                                        na.rm = TRUE),
          tobacco_current = survey_mean(tobacco_current,
                                        vartype = "ci",
                                        na.rm = TRUE)) }


nyts_data %>%
  group_by(year) %>%
  dplyr::group_modify(~ surveyMeanA(.x)) %>%
  head()
# A tibble: 5 × 7
# Groups:   year [5]
   year tobacco_ever tobacco_ever_low tobacco_ever_upp tobacco…¹ tobac…² tobac…³
  <dbl>        <dbl>            <dbl>            <dbl>     <dbl>   <dbl>   <dbl>
1  2015        0.372            0.344            0.400     0.180   0.162   0.199
2  2016        0.338            0.319            0.358     0.148   0.135   0.162
3  2017        0.307            0.284            0.330     0.139   0.124   0.153
4  2018        0.339            0.318            0.360     0.185   0.171   0.199
5  2019        0.408            0.384            0.432     0.233   0.217   0.249
# … with abbreviated variable names ¹​tobacco_current, ²​tobacco_current_low,
#   ³​tobacco_current_upp

Now let’s make the function wrangle the output in a more usable form too:

surveyMeanA <- function(currYear) {
  options(survey.lonely.psu = "adjust")
  currYear %>%
  as_survey_design(strata = stratum,
                      ids = psu,
                  weight  = finwgt,
                     nest = TRUE) %>%
   summarize(tobacco_ever = survey_mean(tobacco_ever,
                                        vartype = "ci",
                                        na.rm = TRUE),
          tobacco_current = survey_mean(tobacco_current,
                                        vartype = "ci",
                                        na.rm = TRUE))  %>%
  mutate_all("*", 100) %>%
  pivot_longer(everything(),
               names_to = "Type",
               values_to = "Percentage of students") %>%
  mutate(Estimate = case_when(str_detect(Type, "_low") ~ "Lower",
                              str_detect(Type, "_upp") ~ "Upper",
                          TRUE ~ "Mean"),
         User = case_when(str_detect(Type, "ever") ~ "Ever",
                          str_detect(Type, "current") ~ "Current",
                          TRUE ~ "Mean"))}

nyts_data %>%
  group_by(year) %>%
  group_modify(~ surveyMeanA(.x))
# A tibble: 30 × 5
# Groups:   year [5]
    year Type                `Percentage of students` Estimate User   
   <dbl> <chr>                                  <dbl> <chr>    <chr>  
 1  2015 tobacco_ever                            37.2 Mean     Ever   
 2  2015 tobacco_ever_low                        34.4 Lower    Ever   
 3  2015 tobacco_ever_upp                        40.0 Upper    Ever   
 4  2015 tobacco_current                         18.0 Mean     Current
 5  2015 tobacco_current_low                     16.2 Lower    Current
 6  2015 tobacco_current_upp                     19.9 Upper    Current
 7  2016 tobacco_ever                            33.8 Mean     Ever   
 8  2016 tobacco_ever_low                        31.9 Lower    Ever   
 9  2016 tobacco_ever_upp                        35.8 Upper    Ever   
10  2016 tobacco_current                         14.8 Mean     Current
# … with 20 more rows

We will now make a plot using this data. The confidence intervals are included using the geom_linerange() function of the ggplot2 package.

plotA_w <- nyts_data %>%
  group_by(year) %>%
  group_modify(~ surveyMeanA(.x)) %>%
  dplyr::select(-Type) %>%
  pivot_wider(names_from = Estimate,
             values_from = `Percentage of students`) %>%
  ggplot(aes(x = year, y = Mean)) +
  geom_line(aes(linetype = User)) +
  geom_linerange(aes(ymin = Lower,
                     ymax = Upper), 
                     size = 1, 
              show.legend = FALSE) +
  scale_linetype_manual(values = c(2, 1)) +
  scale_y_continuous(breaks = seq(0, 70, by = 10),
                     labels = seq(0, 70, by = 10),
                     limits = c(0, 70)) +
    theme_linedraw() +
    theme(legend.position = "none",
          axis.title.x = element_blank()) +
    labs(title = "Tobacco product users more prevalent after 2017",
         y = "% of students")
plotA_w + theme(text = element_text(size = 15))

Now we can see that we have confidence interval ranges plotted for each value.

We will make a similar plot for students who reported ever trying or who currently use e-cigarettes as opposed to tobacco in general.

v_colors =  viridis(6)[c(1, 4)]

surveyMeanB <- function(currYear) {
  options(survey.lonely.psu = "adjust")
  currYear %>%
  as_survey_design(strata = stratum,
                      ids = psu,
                  weight  = finwgt,
                     nest = TRUE) %>%
  summarize(ecig_ever_year = survey_mean(ecig_ever, 
                                         vartype = "ci", 
                                         na.rm = TRUE),
        non_ecig_ever_year = survey_mean(non_ecig_ever, 
                                         vartype = "ci", 
                                         na.rm = TRUE)) %>%
  mutate_all("*", 100) %>%
  pivot_longer(everything(),
           names_to = "Category",
          values_to = "Percentage of students") %>%
  mutate(Estimate = case_when(str_detect(Category, "_low") ~ "Lower",
                              str_detect(Category, "_upp") ~ "Upper",
                                                      TRUE ~ "Mean"),
             User = case_when(str_detect(Category, "ever") ~ "Ever",
                           str_detect(Category, "current") ~ "Current"),
      Product = case_when(str_detect(Category, "non_ecig") ~ "Other products",
                                                      TRUE ~ "E-cigarettes")) %>%
  dplyr::select(-Category) %>%
  pivot_wider(names_from = Estimate,
              values_from = `Percentage of students`)}

nyts_data %>%
  group_by(year) %>%
  group_modify( ~ surveyMeanB(.x)) %>%
  head()
# A tibble: 6 × 6
# Groups:   year [3]
   year User  Product         Mean Lower Upper
  <dbl> <chr> <chr>          <dbl> <dbl> <dbl>
1  2015 Ever  E-cigarettes    26.6  24.3  29.0
2  2015 Ever  Other products  31.3  28.7  33.8
3  2016 Ever  E-cigarettes    22.6  21.0  24.3
4  2016 Ever  Other products  28.2  26.2  30.2
5  2017 Ever  E-cigarettes    21.1  19.1  23.2
6  2017 Ever  Other products  24.3  22.2  26.4
plotB_w <- nyts_data %>%
  group_by(year) %>%
  group_modify( ~ surveyMeanB(.x)) %>%
  ggplot(aes(x = year, y = Mean, color = Product)) +
  geom_line() +
  geom_linerange(aes(ymin = Lower, ymax = Upper),
                 size = 1,
                 show.legend = FALSE) +
  scale_linetype_manual(values = c(2, 1)) +
  scale_color_manual(values = v_colors) +
  scale_y_continuous(
    breaks = seq(0, 60, by = 10),
    labels = seq(0, 60, by = 10),
    limits = c(0, 60)
  ) +
  theme_linedraw() +
  theme(legend.position = "none",
        axis.title.x = element_blank()) +
  labs(title = "% Ever trying e-cigarettes increases &
% Ever trying other products decreases",
       y = "% of students")

plotB_w + theme(text = element_text(size = 15))

Now we will do the same but for current users:

surveyMeanC <- function(currYear) {
  options(survey.lonely.psu = "adjust")
  currYear %>%
  as_survey_design(strata = stratum,
                      ids = psu,
                  weight  = finwgt,
                     nest = TRUE) %>%
  summarize(ecig_current_year = survey_mean(ecig_current, 
                                            vartype = "ci", 
                                            na.rm = TRUE),
        non_ecig_current_year = survey_mean(non_ecig_current, 
                                            vartype = "ci", 
                                            na.rm = TRUE)) %>%
  mutate_all("*", 100) %>%
  pivot_longer(everything(),
           names_to = "Category",
          values_to = "Percentage of students") %>%
  mutate(Estimate = case_when(str_detect(Category, "_low") ~ "Lower",
                              str_detect(Category, "_upp") ~ "Upper",
                                                      TRUE ~ "Mean"),
             User = case_when(str_detect(Category, "ever") ~ "Ever",
                           str_detect(Category, "current") ~ "Current"),
      Product = case_when(str_detect(Category, "non_ecig") ~ "Other products",
                                                      TRUE ~ "E-cigarettes")) %>%
  dplyr::select(-Category) %>%
  pivot_wider(names_from = Estimate,
              values_from = `Percentage of students`)}


plotC_w <- nyts_data %>%
  group_by(year) %>%
  group_modify( ~ surveyMeanC(.x)) %>%
  ggplot(aes(x = year, y = Mean, color = Product)) +
  geom_line(aes(linetype = "dashed")) +
  geom_linerange(aes(ymin = Lower, ymax = Upper),
                 size = 1,
                 show.legend = FALSE) +
  scale_linetype_manual(values = c(2, 1)) +
  scale_y_continuous(breaks = seq(0, 60, by = 10), limits = c(0, 60)) +
  scale_color_manual(values = v_colors) +
  theme_linedraw() +
  theme(legend.position = "none",
        axis.title.x = element_blank()) +
  labs(title = "% Currently using e-cigarettes increases &
% Currently using other products decreases",
        y = "% of students")
plotC_w + theme(text = element_text(size = 15))

Now we will put these plots together again using the cowplot package:

title_w <- ggdraw() +
  draw_label(
    expression("What is the relationship between e-cigarette use and tobacco use?"),
    fontface = 'bold',
    size = 14,
    x = 0,
    hjust = 0
  ) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

plotsA_w <- plot_grid(plotA_w,
                     rel_widths = c(1),
                     align = "v",
                     axis = "bt")
plotsBC_w <- plot_grid(plotB_w,
                     plotC_w,
                     rel_widths = c(1, 1),
                     align = "v",
                     axis = "bt")

legend_w <- get_legend(plot1c +
                       theme(legend.position = "bottom",
                             legend.direction = "horizontal"))

figure_w <- plot_grid(title_w,
                      plotsA_w,
                      plotsBC_w,
                      legend_w,
                      ncol = 1,
                      rel_heights = c(0.1,
                                      1,
                                      1,
                                      0.1),
                      scale = 1.0)

figure_w

We can see that these figures look quite similar to the ones generated without using the survey weights.

Artificial Cohort


Although the survey design does not allow specific individuals to be followed over time, we will use certain subsets of the data from each year to construct an artificial cohort where we follow students of the same age group as they get older. This will allow us to look at how tobacco usage changed for students who were in 8th grade in 2015 as they aged.

All of the data so far has included all 6th-12th graders every year. Now we will look at just the data for students expected to graduate in 2019. These are the students who were in 8th grade in 2015, most of whom were 9th graders in 2016, 10th graders in 2017 and so on. We will filter the data to just the students expected to be in the graduating class of 2019.

surveyMeanCohort <- function(currYear) {
  options(survey.lonely.psu = "adjust")
  currYear %>%
  as_survey_design(strata = stratum,
                      ids = psu,
                  weight  = finwgt,
                     nest = TRUE) %>%
  summarize(ecig_ever_year = 
              survey_mean(ecig_ever, vartype = "ci", na.rm = TRUE),
            ecig_current_year = 
              survey_mean(ecig_current, vartype = "ci", na.rm = TRUE),
            non_ecig_ever_year = 
              survey_mean(non_ecig_ever, vartype = "ci", na.rm = TRUE),
            non_ecig_current_year = 
              survey_mean(non_ecig_current, vartype = "ci", na.rm = TRUE),
            tobacco_ever_year = 
              survey_mean(tobacco_ever, vartype = "ci", na.rm = TRUE),
            tobacco_current_year = 
              survey_mean(tobacco_current, vartype = "ci", na.rm = TRUE)) %>%
  mutate_all("*", 100) %>%
  pivot_longer(everything(),
               names_to = "Category",
               values_to = "Percentage of students") %>%
  mutate(Estimate = case_when(str_detect(Category, "_low") ~ "Lower",
                              str_detect(Category, "_upp") ~ "Upper",
                                                      TRUE ~ "Mean"),
             User = case_when(str_detect(Category, "ever") ~ "Ever",
                           str_detect(Category, "current") ~ "Current"),
      Product = case_when(str_detect(Category, "non_ecig") ~ "Other products",
                           str_detect(Category, "tobacco") ~ "Any tobacco product",
                                                      TRUE ~ "E-cigarettes")) %>%
  dplyr::select(-Category) %>%
  pivot_wider(names_from = Estimate,
              values_from = `Percentage of students`)}


Cohort_data <- nyts_data %>%
  filter((Grade == "8" & year == 2015) |
         (Grade == "9" & year == 2016) |
         (Grade == "10" & year == 2017) |
         (Grade == "11" & year == 2018) |
         (Grade == "12" & year == 2019)
         ) %>%
  group_by(year) %>%
  group_modify(~ surveyMeanCohort(.x))

head(Cohort_data)
# A tibble: 6 × 6
# Groups:   year [1]
   year User    Product              Mean Lower Upper
  <dbl> <chr>   <chr>               <dbl> <dbl> <dbl>
1  2015 Ever    E-cigarettes        20.1  17.0  23.1 
2  2015 Current E-cigarettes         8.12  6.74  9.50
3  2015 Ever    Other products      20.7  17.7  23.7 
4  2015 Current Other products       7.06  5.63  8.49
5  2015 Ever    Any tobacco product 26.9  23.3  30.4 
6  2015 Current Any tobacco product 10.9   9.13 12.6 

We will now make similar plots to those above for this subset of the data:

plotA_w_8 <- Cohort_data %>%
  filter(Product == "Any tobacco product") %>%
  ggplot(aes(x = year, y = Mean)) +
  geom_line(aes(linetype = User)) +
  geom_linerange(aes(ymin = Lower, ymax = Upper), size = 1) +
  scale_linetype_manual(values = c(2, 1)) +
  scale_y_continuous(breaks = seq(0, 70, by = 10),
                     labels = seq(0, 70, by = 10),
                     limits = c(0, 70)) +
  scale_color_manual(values = v_colors) +
  theme_linedraw() +
  theme(legend.position = "none",
        axis.title.x = element_blank()) +
  labs(title = "Tobacco product use became increasingly prevalent",
       y = "% of students")

plotB_w_8 <- Cohort_data %>%
  filter(Product != "Any tobacco product", User == "Ever") %>%
  ggplot(aes(x = year, y = Mean, color = Product)) +
  geom_line(linetype = 1) +
  geom_linerange(aes(ymin = Lower, ymax = Upper), size = 1) +
  scale_y_continuous(breaks = seq(10, 60, by = 10), limits = c(10, 60)) +
  scale_color_manual(values = v_colors) +
  theme_linedraw() +
  theme(legend.position = "none",
        axis.title.x = element_blank()) +
  labs(title = "% ever trying tobacco products increases",
       y = "% of students")

plotC_w_8 <- Cohort_data %>%
  filter(Product != "Any tobacco product", User == "Current") %>%
  ggplot(aes(x = year, y = Mean, color = Product)) +
  geom_line(aes(linetype = User)) +
  geom_linerange(aes(ymin = Lower, ymax = Upper), size = 1) +
  scale_linetype_manual(values = c(2, 1)) +
  scale_y_continuous(breaks = seq(0, 60, by = 10), limits = c(0, 60)) +
  scale_color_manual(values = v_colors) +
  theme_linedraw() +
  theme(legend.position = "none",
        axis.title.x = element_blank()) +
  labs(title = "E-cigarette use surpasses use of other products",
       y = "% of students")

title_w_8 <- ggdraw() +
  draw_label(
  expression("For students in the 2019 graduating class, how are vaping and tobacco use related?"),
    fontface = 'bold',
    size = 14,
    x = 0,
    hjust = 0
  ) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

plotsA_w_8 <- plot_grid(plotA_w_8,
                        rel_widths = c(1),
                        align = "v",
                        axis = "bt")

plotsBC_w_8 <- plot_grid(plotB_w_8,
                         plotC_w_8,
                         rel_widths = c(1, 1),
                         axis = "bt")

legend_w_8 <- get_legend(plot1c +
                       theme(legend.position = "bottom",
                             legend.direction = "horizontal"))

figure_w_8 <- plot_grid(title_w_8,
                        plotsA_w_8,
                        plotsBC_w_8,
                        legend_w_8,
                        ncol = 1,
                        rel_heights = c(0.1,
                                      1,
                                      1,
                                      0.1),
                        scale = 1.0
)

figure_w_8

Data Analysis


If you have been following along but stopped, we could load the wrangled data from the “data” directory like so:

load(here::here("data", "wrangled", "wrangled_data_with_var_for_plots.rda"))

If you skipped the previous sections click here.

First you need to install and load the OCSdata package:

install.packages("OCSdata")
library(OCSdata)

Then, you may load the wrangled data for plots using the following code:

wrangled_rda("ocs-bp-vaping-case-study", outpath = getwd())
load(here::here("OCSdata", "data", "wrangled", "wrangled_data_with_var_for_plots.rda"))

If the package does not work for you, alternatively, an RDA file (stands for R data) of the data can be found in our GitHub repository or slightly more directly here. Download this file and then place it in your current working directory within a subdirectory called “wrangled” within a subdirectory called “data” to copy and paste our code. We used an RStudio project and the here package to navigate to the file more easily.

load(here::here("data", "wrangled", "wrangled_data_with_var_for_plots.rda"))

Click here to see more about creating new projects in RStudio.

You can create a project by going to the File menu of RStudio like so:

You can also do so by clicking the project button:

See here to learn more about using RStudio projects and here to learn more about the here package.



As an extension, we will include some material here on logistic regression and survey-weighted logistic regression that would be appropriate for answering Question 2 (“How does e-cigarette use compare between males and females?”) for a single year using statistical inference, rather than just data visualizations.

We can look at the final figure in the section on Question 2 and see that among both current and ever users of e-cigarettes, a higher percentage of males than females use or have used e-cigarettes.’

But what if we wanted to quantify this effect and assess whether this difference can be considered statistically significant? This is where the tool of logistic regression can come in handy.

Logistic regression motivation


Here, we will approach the topic of logistic regression assuming some prior knowledge of simple and multiple linear regression. These have been covered in another case study.

As a brief reminder, a linear regression model allows us to estimate the relationship between an outcome variable, call it \(Y\), and a set of one or more input variables, \(X_1, X_2, ..., X_n\). We can write a simple linear regression model as:

\[ E(Y) = \beta_0 + \beta_1 X_1\]

where the \(E(Y)\) means the expected value of \(Y\), i.e., our model gives us an estimate of the mean value of \(Y\) given a particular input \(X_1\). Here, \(\beta_1\) quantifies the expected difference in \(Y\) comparing two individuals who are one unit apart in \(X_1\).

Similarly, we can include more than one predictor so that our equation might look like:

\[ E(Y) = \beta_0 + \beta_1 X_1 + \beta_2 X_2\]

Here, \(\beta_1\) quantifies the expected difference in \(Y\) comparing two individuals who are one unit apart in \(X_1\), holding \(X_2\) at a fixed value. This material is covered in more detail elsewhere and in another case study.

In the case of our question of interest for this case study, however, our outcome variable is of a particular type: it is a Yes-No or binary outcome, since each student respondent either is or is not a current user of e-cigarettes. This means in our setting \(Y\) only takes on two values: TRUE or FALSE, which we can also think of as 1 and 0. For this kind of outcome variable, we need a special kind of regression, called logistic regression. And instead of using a linear model to estimate \(Y\) itself for a given set of input variables, we will use a linear model to estimate the log odds that Y=1 for a given set of input variables.

If we define \(p=P(Y=1)=E(Y)\), the standard simple logistic regression equation can be written as:

\[logit(p)= log_e (\frac{p}{1-p})= \beta_0 + \beta_1 X\]

In our case, we would define \(p\) as the probability that a student respondent is a current e-cigarette user, since \(Y\) is the binary variable that is 1 when a student respondent is a current e-cigarette user and 0 if not. The value \(\frac{p}{1-p}\) is called the odds that \(Y\) is equal to 1.

This logit function is short for log odds.

While it may feel strange working with the log odds that our outcome variable is equal to 1, there are some intuitive reasons why it makes sense to do this from the point of view of fitting a line to our data and interpreting the results. The log odds can take any value on the real number line, allowing us to estimate our model parameters with no constraints. If we instead tried to use say \(p\) as the outcome variable, we would somehow need to constrain \(\beta_0 + \beta_1 X\) to be between 0 and 1, since this is the only possible range of values for a probability. A second, more technical reason is that working on the log odds scale gives us a nice formulation of our likelihood, i.e., a function of our unknown parameters that incorporates our observed data. We use this likelihood function to estimate our unknown parameters (here, \(\beta_0\) and \(\beta_1\)) and this formulation gives us a nice way to calculate the maximum likelihood estimates of these parameters.

The intuitive explanation of logistic regression then is that we are fitting a line to the log odds of \(Y\), as it varies with different values of \(X\). We will work through an example below to illustrate and hopefully clarify this.

Logistic regression “by hand” and by model


For simplicity, we will consider just the set of current users of e-cigarettes in 2015. How much more likely is a male student to be a current e-cigarette user than a female student?

We can get a first look at the answer by calculating the percent of females and percent of males who are current e-cigarette users or not:

nyts_data %>% 
  filter(year == 2015, !is.na(Sex)) %>%
  group_by(Sex, ecig_current) %>%
  summarize(n = n()) %>%
  mutate(pct = n / sum(n))
# A tibble: 4 × 4
# Groups:   Sex [2]
  Sex    ecig_current     n    pct
  <fct>  <lgl>        <int>  <dbl>
1 male   FALSE         7787 0.869 
2 male   TRUE          1171 0.131 
3 female FALSE         7850 0.910 
4 female TRUE           772 0.0895

We can see that the percentage is lower for females than for males. Another way of organizing this data would be to make a 2x2 table, a data summarization frequently used in public health settings.

Male Female Total
Current e-cigarette user 1171 772 1943
Not current e-cigarette user 7787 7850 15637
Total 8958 8622 17580

As discussed above, one important ingredient in understanding the output of logistic regression is understanding the concept of an odds and an odds ratio. We can ask, among males who responded to the survey in 2015, what are the odds of being a current e-cigarette user? How about for females? How do these odds compare? The odds ratio is a tool frequently used in public health to compare the odds between two groups.

In this case:

  • Odds of current e-cigarette use for males: 1171 / 7787 = 0.150
  • Odds of current e-cigarette use for females: 772 / 7850 = 0.098
  • Odds ratio of e-cigarette use for females as compared to males: \[OR = \frac{odds \ for \ females}{odds \ for \ males} = \frac{772 / 7850}{1171 / 7787} = 0.65\]
  • Log odds ratio: \(log_e(OR) = log(1.53) = -0.42\)

We would interpret these values by saying that the odds of being a current e-cigarette user for women are around 0.65 times the odds of being a current e-cigarette user for men, or 35% lower for women. This matches what we can see in our data visualizations for Question 2.

We could also answer this question using logistic regression:

\[log(odds \ of \ current \ e-cigarette \ use) = \beta_0 + \beta_1 \cdot Sex\]

Here is how we would fit our logistic regression model, using the glm function from base R. We also use the tidy function from the broom package to create a tibble of the model output.

dat2015 <- nyts_data %>% 
  filter(year == 2015, !is.na(Sex))

currEcigSex <- glm(ecig_current ~ Sex, data = dat2015, family = binomial(link = "logit"))
currEcigSexTidy <- broom::tidy(currEcigSex)
currEcigSexTidy
# A tibble: 2 × 5
  term        estimate std.error statistic  p.value
  <chr>          <dbl>     <dbl>     <dbl>    <dbl>
1 (Intercept)   -1.89     0.0313    -60.4  0       
2 Sexfemale     -0.425    0.0490     -8.66 4.73e-18

Looking at this output, our estimated logistic regression equation is:

\[log(odds \ of \ current \ e-cigarette \ use) = \beta_0 + \beta_1 \cdot Sex = -1.89 - 0.425 \cdot (Sex == female)\]

Our \(\beta_1\) parameter tells us that the log odds of being a current e-cigarette user are 0.425 lower for females compared to males, i.e., the difference in log odds of being a current e-cigarette user for females compared to males is -0.425. And we can notice that this value matches the log odds ratio that we calculated by hand from the 2x2 table above. This is because a difference in log odds is the same as a log odds ratio – remember your rules of logs!

We can interpret this output as follows:

  • \(-0.425 = \beta_1 = \log(OR)\)
  • The log odds of being a current e-cigarette user is 0.425 lower for females compared to males
  • \(0.65 = e^{-0.425} = e^{\beta_1} = OR\)
  • The odds of being a current e-cigarette user for females is 0.65 times the odds for males.
  • The odds of being a current e-cigarette user for females is 35% lower than the odds for males.

We can also look at the other columns of the model output to assess whether our data indicate that \(\beta_1\) is statistically significantly different from 0. The p-value for the Sex variable in our model is 4.73e-18. Since this value is < 0.05, we would reject the null hypothesis that \(\beta_1 = 0\) based on our model output. See this case study for more information about alpha and p-values.

Simple logistic regression can be extended to include additional variables in the model, for example to adjust for potential confounding variables such as Age or Grade. For example, suppose we want to estimate the effect of Sex on current e-cigarette use, holding Age constant. We could fit the model:

\[log(odds \ of \ current \ e-cigarette \ use) = \beta_0 + \beta_1 \cdot Sex + \beta_2 \cdot Age\]

currEcigSexAge <- glm(ecig_current ~ Sex + Age, 
                      data = dat2015, 
                      family = binomial(link = "logit"))
tidy(currEcigSexAge)
# A tibble: 12 × 5
   term        estimate std.error statistic  p.value
   <chr>          <dbl>     <dbl>     <dbl>    <dbl>
 1 (Intercept)  -0.0872    0.451    -0.193  8.47e- 1
 2 Sexfemale    -0.385     0.0500   -7.70   1.39e-14
 3 Age10       -12.4     162.       -0.0765 9.39e- 1
 4 Age11        -3.30      0.495    -6.67   2.52e-11
 5 Age12        -3.05      0.464    -6.56   5.49e-11
 6 Age13        -2.57      0.459    -5.59   2.22e- 8
 7 Age14        -1.96      0.456    -4.30   1.74e- 5
 8 Age15        -1.67      0.455    -3.67   2.43e- 4
 9 Age16        -1.43      0.455    -3.14   1.70e- 3
10 Age17        -1.29      0.455    -2.84   4.45e- 3
11 Age18        -1.07      0.456    -2.35   1.89e- 2
12 Age>18       -0.924     0.495    -1.87   6.20e- 2

Now our \(\beta_1\) parameter tells us that the log odds of being an current e-cigarette user are 0.385 lower for females compared to males, within an age group, or holding Age constant.

Survey weighted logistic regression


As discussed elsewhere in this case study, our data come from a survey, where individuals were not necessarily sampled in direct proportion to their population representation, so it is necessary to incorporate survey weights into our analysis to perform inference about the population of interest. Luckily, there are implementations of survey-weighted logistic regression in R that can do this for us.

We first create our survey design object using the as_survey_design function from the srvyr package, and then use the svyglm function from the survey package to fit our logistic regression model.

dat2015_survey_design <- dat2015 %>%
                          as_survey_design(strata = stratum,
                                            ids = psu,
                                            weight  = finwgt,
                                            nest = TRUE)


currEcigSex_svy <- survey::svyglm(ecig_current ~ Sex,
                                  family = quasibinomial(link = 'logit'),
                                  design = dat2015_survey_design)
tidy(currEcigSex_svy)
# A tibble: 2 × 5
  term        estimate std.error statistic  p.value
  <chr>          <dbl>     <dbl>     <dbl>    <dbl>
1 (Intercept)   -1.90     0.0788    -24.1  3.95e-34
2 Sexfemale     -0.383    0.0700     -5.48 7.52e- 7

Note that in this case, we use the quasi-binomial family rather than the binomial family, which allows the data to not necessarily look like a sample from a binomial distribution. This is because by incorporating our survey weights, it is as if each individual does not have a 0 or 1 as their outcome variable, so we get a warning if we do not use this value for family.

From this model output, we can see that the estimate incorporating survey weights is a little different. The interpretation is as follows:

  • \(-0.383 = \beta_1 = \log(OR)\)
  • The log odds of being a current e-cigarette user is 0.383 lower for females than for males, taking survey weights into account.
  • \(0.68 = e^{-0.383} = e^{\beta_1} = OR\)
  • The odds of being a current e-cigarette user for females is 0.68 times the odds for males, taking survey weights into account.
  • The odds of being a current e-cigarette user for females is 32% lower than the odds for males, taking survey weights into account.

As above, we can also fit a more complicated model with additional covariates.

currEcigSexAge_svy <- survey::svyglm(ecig_current ~ Sex + Age,
                      family = quasibinomial(link = 'logit'), 
                      design = dat2015_survey_design)
tidy(currEcigSexAge_svy)
# A tibble: 12 × 5
   term        estimate std.error statistic  p.value
   <chr>          <dbl>     <dbl>     <dbl>    <dbl>
 1 (Intercept)    0.387    0.462      0.838 4.06e- 1
 2 Sexfemale     -0.380    0.0775    -4.90  8.84e- 6
 3 Age10        -11.9      0.766    -15.5   8.83e-22
 4 Age11         -3.72     0.555     -6.71  1.13e- 8
 5 Age12         -3.66     0.515     -7.11  2.49e- 9
 6 Age13         -3.20     0.480     -6.66  1.38e- 8
 7 Age14         -2.46     0.455     -5.41  1.41e- 6
 8 Age15         -2.10     0.480     -4.37  5.47e- 5
 9 Age16         -1.89     0.490     -3.86  3.03e- 4
10 Age17         -1.75     0.490     -3.58  7.37e- 4
11 Age18         -1.57     0.469     -3.35  1.48e- 3
12 Age>18        -1.49     0.534     -2.78  7.39e- 3

In this case, we can see that our estimated difference in log odds for females compared to males, -0.380, is not much different whether we are holding Age constant or not.

Summary


Summary Plot


Finally we will put our plots together to create a plot that describes the relationship between e-cigarette usage and overall tobacco use, combining both our first set of unweighted results, and those calculated using the srvyr package. We will also make the background of the plot white.

title_final <- ggdraw() +
  draw_label(
    expression("What is the relationship between e-cigarette use and tobacco use?"),
    fontface = 'bold',
    size = 16,
    x = 0.5) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

subtitle_uw_final <- ggdraw() +
  draw_label(
    expression(~ 6^th ~ "-" ~ 12^th ~ "graders, unweighted"),
    size = 12,
    x = 0.5) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

subtitle_w_final <- ggdraw() +
  draw_label(
    expression(~ 6^th ~ "-" ~ 12^th ~ "graders, weighted"),
    fontface = 'bold',
    size = 12,
    x = 0.5) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

subtitle_w_8_final <- ggdraw() +
  draw_label(
    expression("Approximate graduating \n class of 2019, weighted"),
    fontface = 'bold',
    size = 12,
    x = 0.5) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

subtitle_final <- plot_grid(subtitle_uw_final,
                            subtitle_w_final,
                            subtitle_w_8_final,
                            ncol = 3)

plotsA_title_final <- ggdraw() +
  draw_label(
    expression("Prevalence of any tobacco product use by user type"),
    size = 14,
    x = 0.5) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

plotsA_final <- plot_grid(plotA_uw + theme(plot.title = element_blank()),
                          plotA_w + theme(plot.title = element_blank()),
                          plotA_w_8 + theme(plot.title = element_blank()),
                          ncol = 3,
                          align = "v",
                          axis = "bt")

plotsB_title_final <- ggdraw() +
  draw_label(
    expression("Prevalence of ever trying by product type"),
    size = 14,
    x = 0.5) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

plotsB_final <- plot_grid(plotB_uw + theme(plot.title = element_blank()),
                          plotB_w + theme(plot.title = element_blank()),
                          plotB_w_8 + theme(plot.title = element_blank()),
                          ncol = 3,
                          align = "v",
                          axis = "bt")

plotsC_title_final <- ggdraw() +
  draw_label(
    expression("Prevalence of current use by product type"),
    size = 14,
    x = 0.5) +
  theme(
    plot.margin = margin(0, 0, 0, 0)
  )

plotsC_final <- plot_grid(plotC_uw + theme(plot.title = element_blank()),
                          plotC_w + theme(plot.title = element_blank()),
                          plotC_w_8 + theme(plot.title = element_blank()),
                          ncol = 3,
                          align = "v",
                          axis = "bt")

legend_final <- get_legend(plot1c +
                             theme(legend.position = "bottom",
                                   legend.direction = "horizontal"))

final_plot <- plot_grid(title_final,
          plotsA_title_final,
          subtitle_final,
          plotsA_final,
          plotsB_title_final,
          subtitle_final,
          plotsB_final,
          plotsC_title_final,
          subtitle_final,
          plotsC_final,
          legend_final,
          ncol = 1,
          rel_heights = c(0.2,
                          0.2,
                          0.15,
                          1,
                          0.2,
                          0.1,
                          1,
                          0.2,
                          0.1,
                          1,
                          0.2)) +
  theme(plot.background = element_rect(fill = "white"))

final_plot

Synopsis


In this case study, we used data from the National Youth Tobacco Survey (NYTS), an annual survey that asks students in high school and middle school (grades 6-12) about tobacco usage in the United States of America. We used data from 2015-2019 due to the fact that these years are the most recent that asked questions regarding e-cigarette usage.

We used this data to answer these questions:

  1. How has tobacco and e-cigarette/vaping use by American youths changed since 2015?

  2. How does e-cigarette use compare between males and females?

  3. What vaping brands and flavors appear to be used the most frequently?
    We will base this on the following survey questions:
    > “During the past 30 days, what brand of e-cigarettes did you usually use?”
    > “What flavors of tobacco products have you used in the past 30 days?”

  4. Is there a relationship between e-cigarette/vaping use and other tobacco use?

We showed how to work with the data in the format provided (Excel), how to to use the codebooks to decide what variables to use to answer our questions and how to clean and recode the data from the survey for our visualizations and analysis. We made visualizations of our summary statistics over time, to illustrate the trends present in the data for different products and groups of student respondents.

In answer to our questions, we found that tobacco use has gone up slightly overall between 2015 and 2019, with little difference in rates of change comparing males to females. This slight increase is the result of a large increase in e-cigarette/vaping use, coupled with a decrease in use of other tobacco products. The most used brand of e-cigarette/vaping products is Juul, and fruit, menthol and candy/desserts/sweets are the most commonly used flavors.

We then introduced the statistical concept of survey weighting, illustrating how to calculate usage percentages using survey-weighted means, and compare the results in the weighted and unweighted cases. We also introduced the topic of logistic regression and we performed a survey-weighted logistic regression analysis to compare the vaping rates of male and female students.

Suggested Homework


  • Calculate confidence intervals for the unweighted estimates and add the appropriate error bars to the main figures.
  • Apply survey weights to one of the figures produced in this case study in which weighted estimates were not produced. Include error bars in the updated figure.
    • Does the figure change after the application of survey weights?
    • If so, describe how.
  • Reproduce final_plot above for a different cohort of your choice.
  • Focusing on a single year of data, explore demographic factors that contribute to tobacco use of some kind. Compare results of unweighted and weighted analysis (for example, using the svyglm function to calculate survey-weighted logistic regression estimates).

Additional Information


Session info


sessionInfo()
R version 4.2.2 (2022-10-31)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Big Sur ... 10.16

Matrix products: default
BLAS:   /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRblas.0.dylib
LAPACK: /Library/Frameworks/R.framework/Versions/4.2/Resources/lib/libRlapack.dylib

locale:
[1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8

attached base packages:
[1] grid      stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
 [1] survey_4.1-1              survival_3.4-0           
 [3] Matrix_1.5-1              broom_1.0.2              
 [5] cowplot_1.1.1             srvyr_1.1.2              
 [7] naniar_0.6.1              forcats_0.5.2            
 [9] viridis_0.6.2             viridisLite_0.4.1        
[11] scales_1.2.1              ggplot2_3.4.0            
[13] tidyr_1.2.1               readr_2.1.3              
[15] dplyr_1.0.10              purrr_1.0.0              
[17] stringr_1.5.0             readxl_1.4.1             
[19] koRpus.lang.en_0.1-4      koRpus_0.13-8            
[21] sylly_0.1-6               extrafont_0.17.0.9000    
[23] OCSdata_1.1.0             read.so_0.1.1            
[25] wordcountaddin_0.3.0.9000 magrittr_2.0.3           
[27] knitr_1.41                here_1.0.1               

loaded via a namespace (and not attached):
 [1] httr_1.4.4        sass_0.4.4        bit64_4.0.5       vroom_1.6.0      
 [5] jsonlite_1.8.4    splines_4.2.2     bslib_0.4.2       highr_0.10       
 [9] cellranger_1.1.0  yaml_2.3.6        remotes_2.4.2     Rttf2pt1_1.3.11  
[13] pillar_1.8.1      backports_1.4.1   lattice_0.20-45   glue_1.6.2       
[17] visdat_0.5.3      extrafontdb_1.0   digest_0.6.31     colorspace_2.0-3 
[21] htmltools_0.5.4   pkgconfig_2.0.3   tzdb_0.3.0        tibble_3.1.8     
[25] farver_2.1.1      generics_0.1.3    usethis_2.1.6     ellipsis_0.3.2   
[29] cachem_1.0.6      withr_2.5.0       cli_3.5.0         crayon_1.5.2     
[33] evaluate_0.19     fs_1.5.2          fansi_1.0.3       tools_4.2.2      
[37] gh_1.3.1          data.table_1.14.6 hms_1.1.2         mitools_2.4      
[41] lifecycle_1.0.3   munsell_0.5.0     compiler_4.2.2    jquerylib_0.1.4  
[45] rlang_1.0.6       rstudioapi_0.14   labeling_0.4.2    rmarkdown_2.19   
[49] gtable_0.3.1      DBI_1.1.3         curl_4.3.3        R6_2.5.1         
[53] gridExtra_2.3     sylly.en_0.1-3    bit_4.0.5         fastmap_1.1.0    
[57] utf8_1.2.2        rprojroot_2.0.3   stringi_1.7.8     parallel_4.2.2   
[61] vctrs_0.5.1       tidyselect_1.2.0  xfun_0.36        

Estimate of RMarkdown Compilation Time:

About 93 - 103 seconds

This compilation time was measured on a PC machine operating on Windows 10. This range should only be used as an estimate as compilation time will vary with different machines and operating systems.

Acknowledgments


We would like to acknowledge Renee Johnson for assisting in framing the major direction of the case study and for reviewing the case study for subject matter content.

We would like to acknowledge Michael Breshock for his contributions to this case study and developing the OCSdata package.

We would also like to acknowledge the Bloomberg American Health Initiative for funding this work.

LS0tCnRpdGxlOiAiT3BlbiBDYXNlIFN0dWRpZXM6IFZhcGluZyBCZWhhdmlvcnMgaW4gQW1lcmljYW4gWW91dGgiCgpjc3M6IHN0eWxlLmNzcwpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIGluY2x1ZGVzOgogICAgICBpbl9oZWFkZXI6IEdBX1NjcmlwdC5SaHRtbAogICAgc2VsZl9jb250YWluZWQ6IHllcwogICAgY29kZV9kb3dubG9hZDogeWVzCiAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICBudW1iZXJfc2VjdGlvbnM6IG5vCiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCiAgd29yZF9kb2N1bWVudDoKICAgIHRvYzogeWVzCgotLS0KPHN0eWxlPgojVE9DIHsKIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL2ltZy9pY29uLWJhaGkucG5nIik7CiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOwogIHBhZGRpbmctdG9wOiAyNDBweCAhaW1wb3J0YW50OwogIGJhY2tncm91bmQtcmVwZWF0OiBuby1yZXBlYXQ7Cn0KPC9zdHlsZT4KCjwhLS0gT3BlbiBhbGwgbGlua3MgaW4gbmV3IHRhYi0tPiAgCjxiYXNlIHRhcmdldD0iX2JsYW5rIi8+IAogCjxkaXYgaWQ9Imdvb2dsZV90cmFuc2xhdGVfZWxlbWVudCI+PC9kaXY+Cgo8c2NyaXB0IHR5cGU9InRleHQvamF2YXNjcmlwdCIgc3JjPScvL3RyYW5zbGF0ZS5nb29nbGUuY29tL3RyYW5zbGF0ZV9hL2VsZW1lbnQuanM/Y2I9Z29vZ2xlVHJhbnNsYXRlRWxlbWVudEluaXQnPjwvc2NyaXB0PgoKPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgpmdW5jdGlvbiBnb29nbGVUcmFuc2xhdGVFbGVtZW50SW5pdCgpIHsKICBuZXcgZ29vZ2xlLnRyYW5zbGF0ZS5UcmFuc2xhdGVFbGVtZW50KHtwYWdlTGFuZ3VhZ2U6ICdlbid9LCAnZ29vZ2xlX3RyYW5zbGF0ZV9lbGVtZW50Jyk7Cn0KPC9zY3JpcHQ+CgoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRfdGltZV9zdGFydCA8LSBTeXMudGltZSgpCmBgYAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChmaWcud2lkdGggPSAxMCwgZmlnLmhlaWdodCA9IDgsIGRwaSA9IDMwMCkKYGBgCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGluY2x1ZGUgPSBUUlVFLCBjb21tZW50ID0gTkEsIGVjaG8gPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGNhY2hlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBmaWcuYWxpZ24gPSAiY2VudGVyIiwgb3V0LndpZHRoID0gJzkwJScpCmxpYnJhcnkoaGVyZSkKbGlicmFyeShrbml0cikKbGlicmFyeShtYWdyaXR0cikKcmVtb3Rlczo6aW5zdGFsbF9naXRodWIoImJlbm1hcndpY2svd29yZGNvdW50YWRkaW4iLCB0eXBlID0gInNvdXJjZSIsIGRlcGVuZGVuY2llcyA9IFRSVUUpCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJhbGlzdGFpcmU0Ny9yZWFkLnNvIikKbGlicmFyeSh3b3JkY291bnRhZGRpbikKbGlicmFyeShyZWFkLnNvKQpyZW1vdGVzOjppbnN0YWxsX2dpdGh1Yigib3BlbmNhc2VzdHVkaWVzL09DU2RhdGEiKQpsaWJyYXJ5KE9DU2RhdGEpCnJlbW90ZXM6Omluc3RhbGxfZ2l0aHViKCJodHRwczovL2dpdGh1Yi5jb20vd2NoL2V4dHJhZm9udCIpCmxpYnJhcnkoZXh0cmFmb250KQpleHRyYWZvbnQ6OmZvbnRfaW1wb3J0KCkKCnJtYXJrZG93bjo6OnBlcmZfdGltZXJfcmVzZXRfYWxsKCkKcm1hcmtkb3duOjo6cGVyZl90aW1lcl9zdGFydCgicmVuZGVyIikKYGBgCgoKIyMjIyB7Lm91dGxpbmUgfQoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjgwMCBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJmaW5hbF9wbG90LnBuZyIpKQpgYGAKCiMjIyMKCgoKCiMjIyMgey5kaXNjbGFpbWVyX2Jsb2NrfQoKCioqRGlzY2xhaW1lcioqOiBUaGUgcHVycG9zZSBvZiB0aGUgW09wZW4gQ2FzZSBTdHVkaWVzXShodHRwczovL29wZW5jYXNlc3R1ZGllcy5naXRodWIuaW8pe3RhcmdldD0iX2JsYW5rIn0gcHJvamVjdCBpcyAqKnRvIGRlbW9uc3RyYXRlIHRoZSB1c2Ugb2YgdmFyaW91cyBkYXRhIHNjaWVuY2UgbWV0aG9kcywgdG9vbHMsIGFuZCBzb2Z0d2FyZSBpbiB0aGUgY29udGV4dCBvZiBtZXNzeSwgcmVhbC13b3JsZCBkYXRhKiouIEEgZ2l2ZW4gY2FzZSBzdHVkeSBkb2VzIG5vdCBjb3ZlciBhbGwgYXNwZWN0cyBvZiB0aGUgcmVzZWFyY2ggcHJvY2VzcywgaXMgbm90IGNsYWltaW5nIHRvIGJlIHRoZSBtb3N0IGFwcHJvcHJpYXRlIHdheSB0byBhbmFseXplIGEgZ2l2ZW4gZGF0YSBzZXQsIGFuZCBzaG91bGQgbm90IGJlIHVzZWQgaW4gdGhlIGNvbnRleHQgb2YgbWFraW5nIHBvbGljeSBkZWNpc2lvbnMgd2l0aG91dCBleHRlcm5hbCBjb25zdWx0YXRpb24gZnJvbSBzY2llbnRpZmljIGV4cGVydHMuIAoKIyMjIwoKCiMjIyMgey5saWNlbnNlX2Jsb2NrfQoKVGhpcyB3b3JrIGlzIGxpY2Vuc2VkIHVuZGVyIHRoZSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbkNvbW1lcmNpYWwgMy4wIFsoQ0MgQlktTkMgMy4wKV0oaHR0cHM6Ly9jcmVhdGl2ZWNvbW1vbnMub3JnL2xpY2Vuc2VzL2J5LW5jLzMuMC91cy8pe3RhcmdldD0iX2JsYW5rIn0gIFVuaXRlZCBTdGF0ZXMgTGljZW5zZS4KCiMjIyMgCgojIyMjIHsucmVmZXJlbmNlX2Jsb2NrfQoKVG8gY2l0ZSB0aGlzIGNhc2Ugc3R1ZHkgcGxlYXNlIHVzZToKCldyaWdodCwgQ2FycmllIGFuZCBPbnRpdmVyb3MsIE1pY2hhZWwgYW5kIE1lbmcsIFFpZXIgYW5kIEphZ2VyLCBMZWFoIGFuZCBUYXViLCBNYXJnYXJldCBhbmQgSGlja3MsIFN0ZXBoYW5pZS4gKDIwMjApLiBodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC12YXBpbmctY2FzZS1zdHVkeS4gVmFwaW5nIEJlaGF2aW9ycyBpbiBBbWVyaWNhbiBZb3V0aCAoVmVyc2lvbiB2MS4wLjApLgoKIyMjIwoKVG8gYWNjZXNzIHRoZSBHaXRIdWIgUmVwb3NpdG9yeSB3aXRoIHRoZSBkYXRhIGZvciB0aGlzIGNhc2Ugc3R1ZHkgc2VlIGhlcmU6IGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5CgpZb3UgbWF5IGFsc28gYWNjZXNzIGFuZCBkb3dubG9hZCB0aGUgZGF0YSB1c2luZyBvdXIgYE9DU2RhdGFgIHBhY2thZ2UuIFRvIGxlYXJuIG1vcmUgYWJvdXQgdGhpcyBwYWNrYWdlIGluY2x1ZGluZyBleGFtcGxlcywgc2VlIHRoaXMgW2xpbmtdKGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvT0NTZGF0YSkuIEhlcmUgaXMgaG93IHlvdSB3b3VsZCBpbnN0YWxsIHRoaXMgcGFja2FnZToKCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikKYGBgCgpUaGlzIGNhc2Ugc3R1ZHkgaXMgcGFydCBvZiBhIHNlcmllcyBvZiBwdWJsaWMgaGVhbHRoIGNhc2Ugc3R1ZGllcyBmb3IgdGhlIFtCbG9vbWJlcmcgQW1lcmljYW4gSGVhbHRoICBJbml0aWF0aXZlXShodHRwczovL2FtZXJpY2FuaGVhbHRoLmpodS5lZHUvb3Blbi1jYXNlLXN0dWRpZXMpLgoKKioqCgpUaGUgdG90YWwgcmVhZGluZyB0aW1lIGZvciB0aGlzIGNhc2Ugc3R1ZHkgaXMgY2FsY3VsYXRlZCB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKSBhbmQgc2hvd24gYmVsb3c6IAoKYGBge3IsIGVjaG89RkFMU0V9CnJlYWR0YWJsZSA9IHRleHRfc3RhdHMoImluZGV4LlJtZCIpICMgcHJvZHVjaW5nIHJlYWRpbmcgdGltZSBtYXJrZG93biB0YWJsZQpyZWFkdGltZSA9IHJlYWQuc286OnJlYWQubWQocmVhZHRhYmxlKSAlPiUgZHBseXI6OnNlbGVjdChNZXRob2QsIGtvUnB1cykgJT4lICMgcmVhZGluZyB0YWJsZSBpbnRvIGRhdGFmcmFtZSwgc2VsZWN0aW5nIHJlbGV2YW50IGZhY3RvcnMKICBkcGx5cjo6ZmlsdGVyKE1ldGhvZCA9PSAiUmVhZGluZyB0aW1lIikgJT4lICMgZHJvcHBpbmcgdW5uZWNlc3Nhcnkgcm93cwogIGRwbHlyOjptdXRhdGUoa29ScHVzID0gcGFzdGUocm91bmQoYXMubnVtZXJpYyhzdHJpbmdyOjpzdHJfc3BsaXQoa29ScHVzLCAiICIpW1sxXV1bMV0pKSwgIm1pbnV0ZXMiKSkgJT4lICMgcm91bmRpbmcgcmVhZGluZyB0aW1lIGVzdGltYXRlCiAgZHBseXI6Om11dGF0ZShNZXRob2QgPSAia29ScHVzIikgJT4lIGRwbHlyOjpyZWxvY2F0ZShrb1JwdXMsIC5iZWZvcmUgPSBNZXRob2QpICU+JSBkcGx5cjo6cmVuYW1lKGBSZWFkaW5nIFRpbWVgID0ga29ScHVzKSAjIHJlb3JnYW5pemluZyB0YWJsZQprbml0cjo6a2FibGUocmVhZHRpbWUsIGZvcm1hdD0ibWFya2Rvd24iKQpgYGAKCioqKgoKKipSZWFkYWJpbGl0eSBTY29yZTogKioKCkEgcmVhZGFiaWxpdHkgaW5kZXggZXN0aW1hdGVzIHRoZSByZWFkaW5nIGRpZmZpY3VsdHkgbGV2ZWwgb2YgYSBwYXJ0aWN1bGFyIHRleHQuIEZsZXNjaC1LaW5jYWlkLCBGT1JDQVNULCBhbmQgU01PRyBhcmUgdGhyZWUgY29tbW9uIHJlYWRhYmlsaXR5IGluZGljZXMgdGhhdCB3ZXJlIGNhbGN1bGF0ZWQgZm9yIHRoaXMgY2FzZSBzdHVkeSB2aWEgW2tvUnB1c10oaHR0cHM6Ly9naXRodWIuY29tL3VuRG9jVU1lYW50SXQva29ScHVzKS4gVGhlc2UgaW5kaWNlcyBwcm92aWRlIGFuIGVzdGltYXRpb24gb2YgdGhlIG1pbmltdW0gcmVhZGluZyBsZXZlbCByZXF1aXJlZCB0byBjb21wcmVoZW5kIHRoaXMgY2FzZSBzdHVkeSBieSBncmFkZSBhbmQgYWdlLiAKCmBgYHtyLCBlY2hvPUZBTFNFfQpydCA9IHdvcmRjb3VudGFkZGluOjpyZWFkYWJpbGl0eSgiaW5kZXguUm1kIiwgcXVpZXQ9VFJVRSkgIyBwcm9kdWNpbmcgcmVhZGFiaWxpdHkgbWFya2Rvd24gdGFibGUKZGYgPSByZWFkLnNvOjpyZWFkLm1kKHJ0KSAlPiUgZHBseXI6OnNlbGVjdChpbmRleCwgZ3JhZGUsIGFnZSkgJT4lICAjIHJlYWRpbmcgdGFibGUgaW50byBkYXRhZnJhbWUsIHNlbGVjdGluZyByZWxldmFudCBmYWN0b3JzCiAgdGlkeXI6OmRyb3BfbmEoKSAlPiUgZHBseXI6Om11dGF0ZShncmFkZSA9IHJvdW5kKGFzLm51bWVyaWMoZ3JhZGUpKSwgIyBkcm9wcGluZyByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMsIHJvdW5kaW5nIGFnZSBhbmQgZ3JhZGUgY29sdW1ucwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYWdlID0gcm91bmQoYXMubnVtZXJpYyhhZ2UpKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQprbml0cjo6a2FibGUoZGYsIGZvcm1hdD0ibWFya2Rvd24iKQpgYGAKCioqKgoKUGxlYXNlIGhlbHAgdXMgYnkgZmlsbGluZyBvdXQgb3VyIHN1cnZleS4KCgo8ZGl2IHN0eWxlPSJkaXNwbGF5OiBmbGV4OyBqdXN0aWZ5LWNvbnRlbnQ6IGNlbnRlcjsiPjxpZnJhbWUgc3JjPSJodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9mb3Jtcy9kL2UvMUZBSXBRTFNmcE40Rk4zS0VMcUJORWdmMkF0cGk3V3k3TnF5MmJlU2tGUUlOTDdZNXNBTVY1X3cvdmlld2Zvcm0/ZW1iZWRkZWQ9dHJ1ZSIgd2lkdGg9IjEyMDAiIGhlaWdodD0iNzAwIiBmcmFtZWJvcmRlcj0iMCIgbWFyZ2luaGVpZ2h0PSIwIiBtYXJnaW53aWR0aD0iMCI+TG9hZGluZ+KApjwvaWZyYW1lPjwvZGl2PgoKCiMjICoqTW90aXZhdGlvbioqCioqKiAKCkluIHRoZSBVbml0ZWQgU3RhdGVzLCB0aGVyZSBoYXZlIGJlZW4gc2lnbmlmaWNhbnQgYW5kIGhpc3RvcmljYWwgZGVjbGluZXMgaW4gY2lnYXJldHRlIHNtb2tpbmcuIEluIHRoZSAxOTcwcywgNzUlIG9mIGhpZ2ggc2Nob29sIHNlbmlvcnMgd2VyZSBzbW9raW5nLCB0aGF0IG51bWJlciBpcyBiZWxvdyAxMCUgbm93LiBUaGlzIHByb2dyZXNzIGlzIGxhcmdlbHkgZHVlIHRvIHRoZSB0b2JhY2NvIGNvbnRyb2wgbW92ZW1lbnQgYW5kIHRoZWlyIGZvY3VzIG9uIGluaXRpYXRpdmVzIGxpa2UgZW5kaW5nIGFkdmVydGlzaW5nIHRvIGNoaWxkcmVuIChsaWtlIEpvZSBDYW1lbCksIHBhc3NpbmcgaW5kb29yIHNtb2tpbmcgbGF3cywgaGVhbHRoIGNvbW11bmljYXRpb24sIGV0Yy4gCgpBY2NvcmRpbmcgdG8gYSByZWNlbnQgW3JlcG9ydF0oaHR0cHM6Ly93d3cuY2RjLmdvdi9tbXdyL3ZvbHVtZXMvNjgvd3IvbW02ODA2ZTEuaHRtP3NfY2lkPW1tNjgwNmUxX3cpe3RhcmdldD0iX2JsYW5rIn0sIG92ZXJhbGwgdG9iYWNjby9uaWNvdGluZSB1c2UgKippbmNyZWFzZWQqKiBpbiB5b3V0aHMgKG1pZGRsZSBzY2hvb2wgYW5kIGhpZ2ggc2Nob29sIHN0dWRlbnRzKSBpbiB0aGUgVW5pdGVkIFN0YXRlcyBpbiAyMDE3IGFuZCAyMDE4LCBkZXNwaXRlIHByZXZpb3VzIHllYXJzIG9mIGRlY2xpbmluZyB1c2UuCgpUaGlzIG1ham9yIGluY3JlYXNlIGlzIGF0dHJpYnV0ZWQgdG8gYW4gaW5jcmVhc2UgaW4gdGhlIHVzZSBvZiBlbGVjdHJvbmljIGNpZ2FyZXR0ZSAoZS1jaWdhcmV0dGUpIHByb2R1Y3RzLgoKRm9ybXMgb2YgdG9iYWNjby9uaWNvdGluZSBpbmNsdWRlIHRoZXNlIGNhdGVnb3JpZXM6CgoxKSBDaWdhcmV0dGUgYW5kIG90aGVyIGNvbWJ1c3RpYmxlIHRvYmFjY28gKHBpcGVzLCBjaWdhcnMsIGNpZ2FyaWxsbywgZXRjLikKMikgRS1jaWdhcmV0dGVzIGFuZCB2YXBvcml6ZWQgdG9iYWNjby9uaWNvdGluZSAoaG9va2FoLCBKdXVsLCBldGMuKQozKSBPdGhlciBub24tY29tYnVzdGlibGUsIG5vbi12YXBvciB0b2JhY2NvL25pY290aW5lIHByb2R1Y3RzIChzbnVzLCBjaGV3aW5nIHRvYmFjY28sIGV0Yy4pCgpZb3V0aHMgYXJlIG1vcmUgbGlrZWx5IHRvIHVzZSBlLWNpZ2FyZXR0ZXMgdnMuIGNvbWJ1c3RpYmxlIGNpZ2FyZXR0ZXMgdGhlc2UgZGF5cywgd2hpY2ggaXMgY29uY2VybmluZyBiZWNhdXNlIGUtY2lnYXJldHRlcyBhcmUgcmVhbGx5IGVmZmljaWVudCBuaWNvdGluZSBkZWxpdmVyeSBkZXZpY2VzIHRoYXQgYXJlIHJlaW5mb3JjaW5nIGFuZCBlYXN5IHRvIGluaXRpYXRlLiBCeSBjb250cmFzdCwgaXQgdGFrZXMgcXVpdGUgYSB3aGlsZSB0byBiZWNvbWUgYWNjdXN0b21lZCB0byBjaWdhcmV0dGVzIChlZy4gYmVjYXVzZSBvZiBjb3VnaGluZykgYW5kIGJlY29tZSBkZXBlbmRlbnQuIEl0IGlzIGFsc28gaGFyZGVyIGZvciBwYXJlbnRzIHRvIGRldGVjdCBlLWNpZ2FyZXR0ZSB1c2UgYW5kIGludGVydmVuZSAoZWcuIHRoZSBzbWVsbCBpcyBub3QgYXMgc3Ryb25nKS4gVGhpcyBtZWFucyB0aGF0IHlvdXRocyBtYXkgYmUgYmVjb21pbmcgcGh5c2ljYWxseSBkZXBlbmRlbnQgb24gbmljb3RpbmUgbW9yZSBxdWlja2x5IHRoYW4gaW4gcGFzdCB5ZWFycywgYW5kIHRoYXQgY2Vzc2F0aW9uIHNlcnZpY2VzIGRlc2lnbmVkIGZvciB5b3V0aHMgd2lsbCBiZSBuZWVkZWQuCgpXaGVyZWFzIGluIHByZXZpb3VzIGRlY2FkZXMgdGhlIGZvY3VzIHdhcyBvbiBhZHZlcnRpc2luZywgdGhlIGN1cnJlbnQgZXJhIHJlcXVpcmVzIGF0dGVudGlvbiB0byB0aGUgbWFya2V0aW5nIGJyb2FkbHkuIEp1dWwgY2F1Z2h0IG9uIHRocm91Z2ggSW5zdGFncmFtIGluZmx1ZW5jZXJzLiBOZXcgcG9saWNpZXMgdGhhdCByZWd1bGF0ZSB0aGVzZSBpbm5vdmF0aXZlIG1hcmtldGluZyBzdHJhdGVnaWVzIHdpbGwgYmUgY3JpdGljYWwuIAoKRS1jaWdhcmV0dGVzIGFyZSByZWZlcnJlZCB0byBieSBtYW55IGRpZmZlcmVudCBuYW1lcywgaW5jbHVkaW5nIGJ1dCBub3QgbGltaXRlZCB0bzoKCjEpIEVsZWN0cm9uaWMgbmljb3RpbmUgZGVsaXZlcnkgc3lzdGVtcyAoRU5EUykKMikgVmFwZXMKMykgZS1ob29rYWhzCjQpIHZhcGUgcGVucwo1KSB0YW5rcwo2KSBtb2RzCgpUaGUgZGV2aWNlcyB2YXJ5IGdyZWF0bHk6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIifQoKaW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly93d3cubHVuZy5vcmcvZ2V0bWVkaWEvOGFjOGFiOGMtZTdmYy00OTdiLTgzODQtNDQxNjE1ZjUwZmYwL2VjaWdzX0suanBnLmpwZyIpCmBgYAoKIyMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vd3d3Lmx1bmcub3JnL3F1aXQtc21va2luZy9lLWNpZ2FyZXR0ZXMtdmFwaW5nL2x1bmctaGVhbHRoKV0KClNlZSB0aGlzIFtDREMgZ3VpZGVdKGh0dHBzOi8vd3d3LmNkYy5nb3YvdG9iYWNjby9iYXNpY19pbmZvcm1hdGlvbi9lLWNpZ2FyZXR0ZXMvcGRmcy9lY2lnYXJldHRlLW9yLXZhcGluZy1wcm9kdWN0cy12aXN1YWwtZGljdGlvbmFyeS01MDgucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZCB0aGUgW0FtZXJpY2FuIEx1bmcgQXNzb2NpYXRpb24gd2Vic2l0ZV0oaHR0cHM6Ly93d3cubHVuZy5vcmcvcXVpdC1zbW9raW5nL2UtY2lnYXJldHRlcy12YXBpbmcvbHVuZy1oZWFsdGgpe3RhcmdldD0iX2JsYW5rIn0gZm9yIG1vcmUgaW5mb3JtYXRpb24uIAoKVGhlIHJlcG9ydCBmb3VuZCB0aGF0OgoKPiBEdXJpbmcgMjAxN+KAkzIwMTgsIGN1cnJlbnQgdXNlIG9mIGFueSB0b2JhY2NvWy9uaWNvdGluZV0kXiokIHByb2R1Y3QgKippbmNyZWFzZWQgMzguMyUqKiAoZnJvbSAxOS42JSB0byAyNy4xJSkgYW1vbmcgaGlnaCBzY2hvb2wgc3R1ZGVudHMgYW5kICoqMjguNiUqKiAoZnJvbSA1LjYlIHRvIDcuMiUpIGFtb25nIG1pZGRsZSBzY2hvb2wgc3R1ZGVudHM7IGUtY2lnYXJldHRlIHVzZSAqKmluY3JlYXNlZCA3Ny44JSoqIChmcm9tIDExLjclIHRvIDIwLjglKSBhbW9uZyBoaWdoIHNjaG9vbCBzdHVkZW50cyBhbmQgKio0OC41JSoqIChmcm9tIDMuMyUgdG8gNC45JSkgYW1vbmcgbWlkZGxlIHNjaG9vbCBzdHVkZW50cy4KCiReKiQgKk5vdGU6IHdlIGFkZGVkICJbL25pY290aW5lXSIgdG8gdGhpcyBxdW90ZSBmcm9tIHRoZSByZXBvcnQgYmVjYXVzZSBlLWNpZ2FyZXR0ZXMgZGVsaXZlciBuaWNvdGluZSwgYnV0IGFyZSBub3QgYWN0dWFsbHkgdG9iYWNjby4qCgpJbiAyMDE4LCB0aGUgW0ZlZGVyYWwgRHJ1ZyBBZG1pbmlzdHJhdGlvbiAoRkRBKSBpbiB0aGUgVW5pdGVkIFN0YXRlc10oaHR0cHM6Ly9hY3Nqb3VybmFscy5vbmxpbmVsaWJyYXJ5LndpbGV5LmNvbS9kb2kvZnVsbC8xMC4xMDAyL2NuY3IuMzE4Njgpe3RhcmdldD0iX2JsYW5rIn0gc3RhdGVkIHRoYXQgZS1jaWdhcmV0dGUgdXNhZ2UgdXNlIGFtb25nIHlvdXRoIHJlYWNoZWQ6ICAKCj4g4oCcbm90aGluZyBzaG9ydCBvZiBhbiAqKmVwaWRlbWljIHByb3BvcnRpb24gb2YgZ3Jvd3RoKirigJ0KCkFkZGl0aW9uYWxseSwgeW91IG1heSBsZWFybiBtb3JlIGFib3V0IGUtY2lnYXJldHRlIG9yIHZhcGluZyB1c2UtYXNzb2NpYXRlZCBsdW5nIGluanVyeSAoRVZBTEkpIGZyb20gW0NEQydzIE1vcmJpZGl0eSBhbmQgTW9ydGFsaXR5IFdlZWtseSBSZXBvcnQgKE1NV1IpXShodHRwczovL3d3dy5jZGMuZ292L21td3IvZWNpZ2FyZXR0ZV9sdW5nX2luanVyeS5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9LgoKSW4gdGhpcyBjYXNlIHN0dWR5LCB3ZSB3aWxsIGJlIGludmVzdGlnYXRpbmcgdGhlIHNhbWUgZGF0YSB1c2VkIGluIHRoZSByZXBvcnQgdGhhdCBnZW5lcmF0ZWQgdGhlIGFib3ZlIGZpbmRpbmdzLiBUaGlzIGRhdGEgY29tZXMgZnJvbSB0aGUgW1RoZSBOYXRpb25hbCBZb3V0aCBUb2JhY2NvIFN1cnZleSAoTllUUyldKGh0dHBzOi8vd3d3LmNkYy5nb3YvdG9iYWNjby9kYXRhX3N0YXRpc3RpY3Mvc3VydmV5cy9ueXRzL2luZGV4Lmh0bSl7dGFyZ2V0PSJfYmxhbmsifS4KCiMjIyMgey5yZWZlcmVuY2VfYmxvY2t9CgpHZW50emtlLCBBbmRyZWEgUy4sIE1lbGlzYSBDcmVhbWVyLCBLYXJlbiBBLiBDdWxsZW4sIEJyaWRnZXQgSy4gQW1icm9zZSwgR29yZG9uIFdpbGxpcywgQWhtZWQgSmFtYWwsIGFuZCBCcmlhbiBBLiBLaW5nLiAg4oCcVml0YWwgU2lnbnM6IFRvYmFjY28gUHJvZHVjdCBVc2UgQW1vbmcgTWlkZGxlIGFuZCBIaWdoIFNjaG9vbCBTdHVkZW50cyAtIFVuaXRlZCBTdGF0ZXMsIDIwMTEtMjAxOC7igJ0gKipNTVdSLiBNb3JiaWRpdHkgYW5kIE1vcnRhbGl0eSBXZWVrbHkgUmVwb3J0KiogNjggKDYpOiAxNTfigJM2NCAoMjAxOSkuCgojIyMjCgoKIyMgKipNYWluIFF1ZXN0aW9ucyoqCioqKiAKCiMjIyMgey5tYWluX3F1ZXN0aW9uX2Jsb2NrfQo8Yj48dT4gT3VyIG1haW4gcXVlc3Rpb246IDwvdT48L2I+CgoxKSBIb3cgaGFzIHRvYmFjY28gYW5kIGUtY2lnYXJldHRlL3ZhcGluZyB1c2UgYnkgQW1lcmljYW4geW91dGhzIGNoYW5nZWQgc2luY2UgMjAxNT8KMikgSG93IGRvZXMgZS1jaWdhcmV0dGUgdXNlIGNvbXBhcmUgYmV0d2VlbiBtYWxlcyBhbmQgZmVtYWxlcz8KMykgV2hhdCB2YXBpbmcgYnJhbmRzIGFuZCBmbGF2b3JzIGFwcGVhciB0byBiZSB1c2VkIHRoZSBtb3N0IGZyZXF1ZW50bHk/ICAKV2Ugd2lsbCBiYXNlIHRoaXMgb24gdGhlIGZvbGxvd2luZyBzdXJ2ZXkgcXVlc3Rpb25zOiAgIAoKPiAiRHVyaW5nIHRoZSBwYXN0IDMwIGRheXMsIHdoYXQgYnJhbmQgb2YgZS1jaWdhcmV0dGVzIGRpZCB5b3UgdXN1YWxseSB1c2U/IiAgIAo+ICJXaGF0IGZsYXZvcnMgb2YgdG9iYWNjbyBwcm9kdWN0cyBoYXZlIHlvdSB1c2VkIGluIHRoZSBwYXN0CjMwIGRheXM/IiAKCjQpIElzIHRoZXJlIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gZS1jaWdhcmV0dGUvdmFwaW5nIHVzZSBhbmQgb3RoZXIgdG9iYWNjbyB1c2U/CgojIyMjCgojIyAqKkxlYXJuaW5nIE9iamVjdGl2ZXMqKiAKKioqIAoKSW4gdGhpcyBjYXNlIHN0dWR5LCB3ZSB3aWxsIGNvdmVyIGhvdyB0byBpbXBvcnQgZGF0YSBmcm9tIG11bHRpcGxlIGZpbGVzIGVmZmljaWVudGx5LCBob3cgdG8gaW1wb3J0IGRhdGEgZnJvbSBleGNlbCBmaWxlcywgYW5kIGhvdyB0byBtYWtlIGEgdmFyaWV0eSBvZiB2aXN1YWxpemF0aW9ucyB0byBjb21wYXJlIG11bHRpcGxlIGdyb3VwcyBhY3Jvc3MgdGltZS4gV2Ugd2lsbCBhbHNvIGRlbW9uc3RyYXRlIGhvdyB0byB3b3JrIHdpdGggY29kZWJvb2tzLiBXZSB3aWxsIGNvdmVyIHRoZSBjb25jZXB0IG9mIHN1cnZleSB3ZWlnaHRpbmcgYW5kIGludHJvZHVjZSB0aGUgYHNydnlyYCBwYWNrYWdlLiBXZSB3aWxsIGRpc2N1c3MgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBwb29sZWQgY3Jvc3Mtc2VjdGlvbmFsIGRhdGEgYW5kIHBhbmVsIGRhdGEuIFdlIHdpbGwgZXNwZWNpYWxseSBmb2N1cyBvbiB1c2luZyBwYWNrYWdlcyBhbmQgZnVuY3Rpb25zIGZyb20gdGhlIFtgVGlkeXZlcnNlYF0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gZm9yIHdyYW5nbGluZyBkYXRhLCBzdWNoIGFzIGB0aWR5cmAgYW5kIGBkcGx5cmAgYW5kIGZvciB2aXN1YWxpemF0aW9uLCBzdWNoIGFzIGFzIGBnZ3Bsb3QyYC4gVGhlIFRpZHl2ZXJzZSBpcyBhIGxpYnJhcnkgb2YgcGFja2FnZXMgY3JlYXRlZCBieSBSU3R1ZGlvLiBXaGlsZSBzb21lIHN0dWRlbnRzIG1heSBiZSBmYW1pbGlhciB3aXRoIHByZXZpb3VzIFIgcHJvZ3JhbW1pbmcgcGFja2FnZXMsIHRoZXNlIHBhY2thZ2VzIG1ha2UgZGF0YSBzY2llbmNlIGluIFIgZXNwZWNpYWxseSBlZmZpY2llbnQuCgpgYGB7ciwgb3V0LndpZHRoID0gIjIwJSIsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIn0KCmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vdGlkeXZlcnNlLnRpZHl2ZXJzZS5vcmcvbG9nby5wbmciKQpgYGAKCgpUaGUgc2tpbGxzLCBtZXRob2RzLCBhbmQgY29uY2VwdHMgdGhhdCBzdHVkZW50cyB3aWxsIGJlIGZhbWlsaWFyIHdpdGggYnkgdGhlIGVuZCBvZiB0aGlzIGNhc2Ugc3R1ZHkgYXJlOgoKPHU+KipEYXRhIFNjaWVuY2UgTGVhcm5pbmcgT2JqZWN0aXZlczoqKjwvdT4KCjEuIEltcG9ydCBkYXRhIGZyb20gRXhjZWwgZmlsZXMKMi4gTWVyZ2UgZGF0YSBmcm9tIG11bHRpcGxlIHNpbWlsYXIgYnV0IG5vdCBpZGVudGljYWwgZGF0YSBzdHJ1Y3R1cmVzCjMuIENyZWF0ZSBlZmZlY3RpdmUgbG9uZ2l0dWRpbmFsIGRhdGEgdmlzdWFsaXphdGlvbnMKNC4gV3JpdGUgZnVuY3Rpb25zIGluIFIKNS4gQXBwbHkgZnVuY3Rpb25zIGFjcm9zcyBkYXRhIHN1YnNldHMgdXNpbmcgYHB1cnJyYCBhbmQgYGRwbHlyYCBmdW5jdGlvbmFsaXR5LgoKPHU+KipTdGF0aXN0aWNhbCBMZWFybmluZyBPYmplY3RpdmVzOioqPC91PiAKCjEuIFVuZGVyc3RhbmRpbmcgb2YgZGlmZmVyZW50IHR5cGVzIG9mIGxvbmdpdHVkaW5hbCBkYXRhIAoyLiBVc2FnZSBvZiBjb2RlYm9va3MKMy4gQ29uY2VwdHVhbCB1bmRlcnN0YW5kaW5nIG9mIHN1cnZleSB3ZWlnaHRpbmcKNC4gSW1wbGVtZW50aW5nIGxvZ2lzdGljIHJlZ3Jlc3Npb24gd2l0aCBzdXJ2ZXkgd2VpZ2h0aW5nCgoKKioqIAoKCldlIHdpbGwgYmVnaW4gYnkgbG9hZGluZyB0aGUgcGFja2FnZXMgdGhhdCB3ZSB3aWxsIG5lZWQ6CgpgYGB7cn0KbGlicmFyeShoZXJlKQpsaWJyYXJ5KHJlYWR4bCkKbGlicmFyeShtYWdyaXR0cikKbGlicmFyeShzdHJpbmdyKQpsaWJyYXJ5KHB1cnJyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHJlYWRyKQpsaWJyYXJ5KHRpZHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZm9yY2F0cykKbGlicmFyeShuYW5pYXIpCmxpYnJhcnkoc3J2eXIpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShicm9vbSkKbGlicmFyeShzdXJ2ZXkpCmxpYnJhcnkoT0NTZGF0YSkKYGBgCgo8dT4qKlBhY2thZ2VzIHVzZWQgaW4gdGhpcyBjYXNlIHN0dWR5OioqPC91PgoKIFBhY2thZ2UgICB8IFVzZSBpbiB0aGlzIGNhc2Ugc3R1ZHkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKLS0tLS0tLS0tLSB8LS0tLS0tLS0tLS0tLQpbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgIHwgdG8gZWFzaWx5IGxvYWQgYW5kIHNhdmUgZGF0YSAgCltyZWFkeGxdKGh0dHBzOi8vcmVhZHhsLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBpbXBvcnQgdGhlIGRhdGEgaW4gdGhlIGV4Y2VsIGZpbGVzIApbbWFncml0dHJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tYWdyaXR0ci92aWduZXR0ZXMvbWFncml0dHIuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSB8IHRvIHVzZSB0aGUgY29tcG91bmQgYXNzaWdubWVudCBwaXBlIG9wZXJhdG9yIGAlPD4lYApbc3RyaW5ncl0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvc3RyaW5nci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gbWFuaXB1bGF0ZSB0aGUgY2hhcmFjdGVyIHN0cmluZ3Mgd2l0aGluIHRoZSBkYXRhICAKW3B1cnJyXShodHRwczovL3B1cnJyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgfCB0byBpbXBvcnQgdGhlIGRhdGEgaW4gYWxsIHRoZSBkaWZmZXJlbnQgZXhjZWwgYW5kIGNzdiBmaWxlcyBlZmZpY2llbnRseQpbZHBseXJdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGFycmFuZ2UvZmlsdGVyL3NlbGVjdC9jb21wYXJlIHNwZWNpZmljIHN1YnNldHMgb2YgdGhlIGRhdGEgIApbcmVhZHJdKGh0dHBzOi8vcmVhZHIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGltcG9ydCB0aGUgQ1NWIGZpbGUgZGF0YQpbdGlkeXJdKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIHJlYXJyYW5nZSBkYXRhIGluIHdpZGUgYW5kIGxvbmcgZm9ybWF0cyAKW2dncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICB8IHRvIG1ha2UgdmlzdWFsaXphdGlvbnMgd2l0aCBtdWx0aXBsZSBsYXllcnMKW3NjYWxlc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NjYWxlcy9zY2FsZXMucGRmKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gYWxsb3cgdXMgdG8gbG9vayBhdCB0aGUgY29sb3JzIHdpdGhpbiB0aGUgdmlyaWRpcyBwYWNrYWdlClt2aXJpZGlzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdmlyaWRpcy92aWduZXR0ZXMvaW50cm8tdG8tdmlyaWRpcy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gbWFrZSBwbG90cyB3aXRoIGEgY29sb3IgcGFsZXR0ZSB0aGF0IGlzIGNvbXBhdGlibGUgd2l0aCBjb2xvciBibGluZG5lc3MKW2ZvcmNhdHNdKGh0dHBzOi8vZm9yY2F0cy50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICB8IHRvIGFsbG93IGZvciByZW9yZGVyaW5nIG9mIGZhY3RvcnMgaW4gcGxvdHMKW25hbmlhcl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL25hbmlhci92aWduZXR0ZXMvZ2V0dGluZy1zdGFydGVkLXctbmFuaWFyLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gIHwgdG8gbWFrZSBhIHZpc3VhbGl6YXRpb24gb2YgbWlzc2luZyBkYXRhCltzeXJ2cl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NydnlyL3NydnlyLnBkZil7dGFyZ2V0PSJfYmxhbmsifSB8IHRvIHVzZSBzdXJ2ZXkgd2VpZ2h0cwpbY293cGxvdF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Nvd3Bsb3QvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9IHwgdG8gYWxsb3cgcGxvdHMgdG8gYmUgY29tYmluZWQgClticm9vbV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Jyb29tL3ZpZ25ldHRlcy9icm9vbS5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9IHwgdG8gY3JlYXRlIG5pY2VseSBmb3JtYXR0ZWQgbW9kZWwgb3V0cHV0CltzdXJ2ZXldKGh0dHA6Ly9yLXN1cnZleS5yLWZvcmdlLnItcHJvamVjdC5vcmcvc3VydmV5L2luZGV4Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gfCB0byBmaXQgc3VydmV5LXdlaWdodGVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24KW09DU2RhdGFdKGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvT0NTZGF0YSl7dGFyZ2V0PSJfYmxhbmsifSB8IHRvIGFjY2VzcyBhbmQgZG93bmxvYWQgT0NTIGRhdGEgZmlsZXMKCgpUaGUgZmlyc3QgdGltZSB3ZSB1c2UgYSBmdW5jdGlvbiwgd2Ugd2lsbCB1c2UgdGhlIGA6OmAgdG8gaW5kaWNhdGUgd2hpY2ggcGFja2FnZSB3ZSBhcmUgdXNpbmcuIFVubGVzcyB3ZSBoYXZlIG92ZXJsYXBwaW5nIGZ1bmN0aW9uIG5hbWVzLCB0aGlzIGlzIG5vdCBuZWNlc3NhcnksIGJ1dCB3ZSB3aWxsIGluY2x1ZGUgaXQgaGVyZSB0byBiZSBpbmZvcm1hdGl2ZSBhYm91dCB3aGVyZSB0aGUgZnVuY3Rpb25zIHdlIHdpbGwgdXNlIGNvbWUgZnJvbS4KCgojIyAqKkNvbnRleHQqKgoqKiogCgpBY2NvcmRpbmcgdG8gdGhlIGNpdGVkIFtNb3JiaWRpdHkgYW5kIE1vcnRhbGl0eSBXZWVrbHkgUmVwb3J0XShodHRwczovL3d3dy5jZGMuZ292L21td3Ivdm9sdW1lcy82OC93ci9tbTY4MDZlMS5odG0/c19jaWQ9bW02ODA2ZTFfdykgdGhpcyB3YXMgd2hhdCB3YXMgYWxyZWFkeSBrbm93biBhYm91dCB0aGlzIHRvcGljIGFuZCB0aGUgaW1wbGljYXRpb25zIG9mIHRoaXMgc3R1ZHk6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIiLCBvdXQud2lkdGggPSAiODAwIHB4In0KCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJjb250ZXh0LnBuZyIpKQpgYGAKCiMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vd3d3LmNkYy5nb3YvbW13ci92b2x1bWVzLzY4L3dyL21tNjgwNmUxLmh0bT9zX2NpZD1tbTY4MDZlMV93KV17dGFyZ2V0PSJfYmxhbmsifSAKCgpJbXBvcnRhbnRseSwgdGhlIHZhcG9ycyB1c2VkIGluIGUtY2lnYXJldHRlcyBjb250YWluIGhhcm1mdWwgY2hlbWljYWxzOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIn0KCmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgImUtY2lnYXJldHRlLWFlcm9zb2wtY2FuLWNvbnRhaW4taGFybWZ1bC1pbmdyZWRpZW50cy5qcGciKSkKYGBgCgojIyMjIFtbc291cmNlXShodHRwczovL3d3dy5jZGMuZ292L3RvYmFjY28vYmFzaWNfaW5mb3JtYXRpb24vZS1jaWdhcmV0dGVzL2ltYWdlcy9lLWNpZ2FyZXR0ZS1hZXJvc29sLWNhbi1jb250YWluLWhhcm1mdWwtaW5ncmVkaWVudHMtZGVza3RvcC03MDAuanBnKV17dGFyZ2V0PSJfYmxhbmsifSAKCkUtY2lnYXJldHRlIHVzYWdlIGhhcyBhbHNvIGJlZW4gYXNzb2NpYXRlZCB3aXRoIFtsdW5nIGluanVyeV0oKGh0dHBzOi8vd3d3LmZyb250aWVyc2luLm9yZy9hcnRpY2xlcy8xMC4zMzg5L2ZwaGFyLjIwMTkuMDE2MTkvZnVsbCkpe3RhcmdldD0iX2JsYW5rIn06CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIiLCBvdXQud2lkdGggPSAiODAwIHB4In0KCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJsdW5nLnBuZyIpKQpgYGAKCiMjIyMgW1tzb3VyY2VdKGh0dHBzOi8vd3d3LmZyb250aWVyc2luLm9yZy9hcnRpY2xlcy8xMC4zMzg5L2ZwaGFyLjIwMTkuMDE2MTkvZnVsbClde3RhcmdldD0iX2JsYW5rIn0gCgpTZWUgW2hlcmVdKGh0dHBzOi8vd3d3LmNkYy5nb3YvdG9iYWNjby9iYXNpY19pbmZvcm1hdGlvbi9lLWNpZ2FyZXR0ZXMvUXVpY2stRmFjdHMtb24tdGhlLVJpc2tzLW9mLUUtY2lnYXJldHRlcy1mb3ItS2lkcy1UZWVucy1hbmQtWW91bmctQWR1bHRzLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gZm9yIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gYWJvdXQgdGhlIHBvdGVudGlhbCBoZWFsdGggZWZmZWN0cyBvZiBlLWNpZ2FyZXR0ZXMgaW4gdGVlbnMgYW5kIHlvdW5nIGFkdWx0cy4KCiMjICoqTGltaXRhdGlvbnMqKgoqKiogClRoZXJlIGFyZSBzb21lIGltcG9ydGFudCBjb25zaWRlcmF0aW9ucyByZWdhcmRpbmcgdGhpcyBkYXRhIGFuYWx5c2lzIHRvIGtlZXAgaW4gbWluZDogCgoxKSBUaGUgW05hdGlvbmFsIFlvdXRoIFRvYmFjY28gU3VydmV5IChOWVRTKV0oaHR0cHM6Ly93d3cuY2RjLmdvdi90b2JhY2NvL2RhdGFfc3RhdGlzdGljcy9zdXJ2ZXlzL255dHMvaW5kZXguaHRtKXt0YXJnZXQ9Il9ibGFuayJ9IGRvZXMgbm90IGZvbGxvdyB0aGUgc2FtZSBpbmRpdmlkdWFsIHN0dWRlbnQgcmVzcG9uZGVudHMgb3ZlciB0aW1lLiAgQSBbbG9uZ2l0dWRpbmFsIHN0dWR5XShodHRwczovL3d3dy5ibWouY29tL2Fib3V0LWJtai9yZXNvdXJjZXMtcmVhZGVycy9wdWJsaWNhdGlvbnMvZXBpZGVtaW9sb2d5LXVuaW5pdGlhdGVkLzctbG9uZ2l0dWRpbmFsLXN0dWRpZXMpe3RhcmdldD0iX2JsYW5rIn0gdGhhdCBkb2VzIGZvbGxvdyB0aGUgc2FtZSBpbmRpdmlkdWFscyBvdmVyIHRpbWUgY29sbGVjdHMgZGF0YSBjYWxsZWQgW3BhbmVsIGRhdGFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1BhbmVsX2RhdGEpe3RhcmdldD0iX2JsYW5rIn0uIFRoZSBkYXRhIGluIHRoaXMgc3R1ZHkgaXMgY2FsbGVkIHBvb2xlZCBbY3Jvc3Mtc2VjdGlvbmFsIGRhdGFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nyb3NzLXNlY3Rpb25hbF9kYXRhKXt0YXJnZXQ9Il9ibGFuayJ9LCBhbmQgaXMgb2J0YWluZWQgZnJvbSByYW5kb20gY29sbGVjdGlvbiBvZiBvYnNlcnZhdGlvbnMgYWNyb3NzIHRpbWUuCgpBY2NvcmRpbmcgdG8gV2lraXBlZGlhOgoKPiBQYW5lbCBkYXRhIGRpZmZlcnMgZnJvbSBwb29sZWQgY3Jvc3Mtc2VjdGlvbmFsIGRhdGEgYWNyb3NzIHRpbWUsIGJlY2F1c2UgaXQgZGVhbHMgd2l0aCB0aGUgb2JzZXJ2YXRpb25zIG9uIHRoZSBzYW1lIHN1YmplY3RzIGluIGRpZmZlcmVudCB0aW1lcyB3aGVyZWFzIHRoZSBsYXR0ZXIgb2JzZXJ2ZXMgZGlmZmVyZW50IHN1YmplY3RzIGluIGRpZmZlcmVudCB0aW1lIHBlcmlvZHMKCjIpIFRoZSBkYXRhIGluY2x1ZGUgcGVyY2VudGFnZXMgb2Ygc3R1ZGVudCByZXNwb25kZW50cyByZXBvcnRpbmcgdXNlIG9mIGVhY2ggcGFydGljdWxhciB0b2JhY2NvIHByb2R1Y3QsIGJ1dCB0aGUgc3VydmV5IHF1ZXN0aW9ucyBkaWQgbm90IGFzayB0aGUgcmVsYXRpdmUgYW1vdW50IG9mIHVzZSBvZiBvbmUgcHJvZHVjdCBjb21wYXJlZCB0byBhbm90aGVyLiBGb3IgZXhhbXBsZSwgdGhlIHN1cnZleSBpbmNsdWRlZCBxdWVzdGlvbnMgbGlrZToiV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzPyIgYnV0IGRpZCBub3QgYXNrIGhvdyBvZnRlbiBvbmUgZmxhdm9yIHdhcyB1c2VkIGJ5IHRoZSBzYW1lIGluZGl2aWR1YWwgb3ZlciBhbm90aGVyLgoKV2hpbGUgW2dlbmRlcl0oaHR0cHM6Ly93d3cuZ2VuZGVyc3BlY3RydW0ub3JnL3F1aWNrLWxpbmtzL3VuZGVyc3RhbmRpbmctZ2VuZGVyLyl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgW3NleF0oaHR0cHM6Ly93d3cud2hvLmludC9nZW5vbWljcy9nZW5kZXIvZW4vaW5kZXgxLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gYXJlIG5vdCBhY3R1YWxseSBiaW5hcnksIHRoZSBkYXRhIHVzZWQgaW4gdGhpcyBhbmFseXNpcyBvbmx5IGNvbnRhaW4gaW5mb3JtYXRpb24gZm9yIGdyb3VwcyBvZiBpbmRpdmlkdWFscyB3aG8gYW5zd2VyZWQgdGhlIHN1cnZleSBxdWVzdGlvbnMgYXMgbWFsZSBvciBmZW1hbGUuIAoKIyMgKipXaGF0IGFyZSB0aGUgZGF0YT8qKgoqKiogCiAKVGhlIGRhdGEgaW4gdGhpcyBjYXNlIHN0dWR5IGNvbWVzIGZyb20gdGhlIFtOYXRpb25hbCBZb3V0aCBUb2JhY2NvIFN1cnZleSAoTllUUyldKGh0dHBzOi8vd3d3LmNkYy5nb3YvdG9iYWNjby9kYXRhX3N0YXRpc3RpY3Mvc3VydmV5cy9ueXRzL2luZGV4Lmh0bSl7dGFyZ2V0PSJfYmxhbmsifSB3aGljaCBpcyBhbiBhbm51YWwgc3VydmV5IHRoYXQgYXNrcyBzdHVkZW50cyBpbiBoaWdoIHNjaG9vbCBhbmQgbWlkZGxlIHNjaG9vbCAoZ3JhZGVzIDYtMTIpIGFib3V0IHRvYmFjY28gdXNhZ2UgaW4gdGhlIFVuaXRlZCBTdGF0ZXMgb2YgQW1lcmljYS4KClRoZSBkYXRhIGZvciB0aGlzIHN1cnZleSBpcyBmcmVlbHkgYXZhaWxhYmxlIG9ubGluZSBhdCB0aGlzIFt3ZWJzaXRlXShodHRwczovL3d3dy5jZGMuZ292L3RvYmFjY28vZGF0YV9zdGF0aXN0aWNzL3N1cnZleXMvbnl0cy9kYXRhL2luZGV4Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gd2l0aCBkYXRhIGZyb20gMTk5OSwgMjAwMCwgMjAwMiwgMjAwNCwgMjAwNiwgMjAwOSwgIGFuZCAyMDExLTIwMTkuIFdlIHdpbGwgYmUgdXNpbmcgZGF0YSBmcm9tICoqMjAxNS0yMDE5KiogZHVlIHRvIHRoZSBmYWN0IHRoYXQgdGhlc2UgeWVhcnMgYXJlIHRoZSBtb3N0IHJlY2VudCB0aGF0IGFza2VkIHF1ZXN0aW9ucyByZWdhcmRpbmcgZS1jaWdhcmV0dGUgdXNhZ2UuCgpFYWNoIHllYXIgaW5jbHVkZXMgZG9jdW1lbnRhdGlvbiwgc3VjaCBhcyBhIFtjb2RlYm9va10oaHR0cHM6Ly93d3cubGliLm5jc3UuZWR1L2RhdGEvaWNwc3JmYXEjd2hhdGFyZSl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgYW4gZXhjZWwgZmlsZSBjb250YWluaW5nIHRoZSBkYXRhOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIiwgb3V0LndpZHRoID0gIjYwMCBweCJ9Cgprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiZGF0YS5wbmciKSkKYGBgClRoZXJlZm9yZSwgc2luY2Ugd2UgYXJlIHVzaW5nIGRhdGEgZnJvbSAqKjIwMTUtMjAxOSoqLCB0aGUgZGF0YSB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBhcmUgbG9jYXRlZCBpbiA1IGRpZmZlcmVudCBleGNlbCBmaWxlcywgb25lIGZvciBlYWNoIHllYXIsIGVhY2ggd2l0aCBpdHMgb3duIFtjb2RlYm9va10oLi9kb2NzLzIwMTktbnl0cy1kYXRhc2V0LWFuZC1jb2RlYm9vay1taWNyb3NvZnQtZXhjZWwvMjAxOS1ueXRzLWNvZGVib29rLXAucGRmKXt0YXJnZXQ9Il9ibGFuayJ9ICh0aGlzIGlzIHRoZSBvbmUgZm9yIDIwMTkpLgoKVGhlIFtjb2RlYm9va10oaHR0cHM6Ly93d3cuaWNwc3IudW1pY2guZWR1L2ljcHNyd2ViL2NvbnRlbnQvc2hhcmVkL0lDUFNSL2ZhcXMvd2hhdC1pcy1hLWNvZGVib29rLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gY29udGFpbnMgaW5mb3JtYXRpb24gZGVzY3JpYmluZyB0aGUgZGF0YSB3aXRoaW4gdGhlIGV4Y2VsIGZpbGUuIAoKQXMgeW91IGNhbiBzZWUgdGhlIGV4Y2VsIGZpbGUgY29udGFpbnMgdmVyeSBzaG9ydCB2YXJpYWJsZSBuYW1lcyBhbmQgbnVtZXJpYyB2YWx1ZXMsIGFuZCBpdCBpcyBub3QgY2xlYXIgd2hhdCB0aGV5IG1lYW4gd2l0aG91dCB0aGUgY29kZWJvb2s6CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIiLCBvdXQud2lkdGggPSAiNjAwIHB4In0KCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJleGNlbC5wbmciKSkKYGBgCgpUaGUgY29kZWJvb2sgZXhwbGFpbnMgd2hhdCB0aGUgdmFyaWFibGVzICh0aGUgY29sdW1ucykgYXJlOgpgYGB7ciwgZWNobyA9IEZBTFNFLCBmaWcuYWxpZ24gPSJjZW50ZXIiLCBvdXQud2lkdGggPSAiNjAwIHB4In0KCmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJ2YXJpYWJsZXMucG5nIikpCmBgYAoKQW5kIHRoZSBjb2RlYm9vayBleHBsYWlucyB3aGF0IHRoZSB2YWx1ZXMgZm9yIGVhY2ggdmFyaWFibGUgYXJlOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIiwgb3V0LndpZHRoID0gIjYwMCBweCJ9Cgprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAicW4xLnBuZyIpKQpgYGAKCldlIHdpbGwgZXhwbGFpbiBtb3JlIGxhdGVyIGFib3V0IHdoYXQgdGhlIHZhbHVlcyBvbiB0aGUgcmlnaHQgaW5kaWNhdGUuCgpUaGUgcmVhc29uIHRoYXQgdGhlcmUgYXJlIGNvZGVib29rcyBmb3IgZWFjaCB5ZWFyIGlzIGJlY2F1c2UgdGhlIHF1ZXN0aW9ucyBhc2tlZCBlYWNoIHllYXIgdmFyaWVkIHNsaWdodGx5LgoKClRoZSBkYXRhIGluIHRoaXMgc3VydmV5IGFyZSBwb29sZWQgY3Jvc3Mtc2VjdGlvbmFsIGRhdGEuIEluIHRoaXMgc3R1ZHkgZGVzaWduLCBkaWZmZXJlbnQgc3Vic2V0cyBvZiBzdHVkZW50IHJlc3BvbmRlbnRzIGFyZSBzdXJ2ZXllZCBlYWNoIHllYXIgYW5kIGl0IGlzIG5vdCBjbGVhciB3aGljaCwgaWYgYW55LCBpbmRpdmlkdWFscyBwYXJ0aWNpcGF0ZSBtb3JlIHRoYW4gb25jZSBmcm9tIG9uZSB5ZWFyIHRvIHRoZSBuZXh0LgoKIyMgKipEYXRhIEltcG9ydCoqCioqKiAKCiMjIyAqKlJlYWRpbmcgaW4gdGhlIGV4Y2VsIGZpbGVzKioKKioqCgpTaW5jZSB0aGVzZSBleGNlbCBmaWxlcyBhcmUgc28gbGFyZ2UgKGVhY2ggaGFzIHJvdWdobHkgMjAsMDAwIHJvd3MpLCBpdCB0YWtlcyBhIGJpdCBvZiB0aW1lIGZvciB0aGUgZGF0YSB0byBsb2FkLiBUbyBtYWtlIHRoZSBwcm9jZXNzIGZhc3Rlciwgd2UgcHJldmlvdXNseSBpbXBvcnRlZCB0aGVzZSBmaWxlcywgc2VsZWN0ZWQgb25seSB0aGUgY29sdW1ucyByZWxldmFudCB0byBvdXIgcXVlc3Rpb25zIG9mIGludGVyZXN0LCBhbmQgc2F2ZWQgdGhlc2UgZGF0YSBzdWJzZXRzIGFzIGNvbW1hLXNlcGFyYXRlZCAoLmNzdikgZmlsZXMuIAoKWW91IG1heSBkb3dubG9hZCB0aGVzZSBmaWxlcyB1c2luZyB0aGUgYE9DU2RhdGFgIHBhY2thZ2U6CgpgYGB7ciwgZXZhbD1GQUxTRX0KIyBpbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikKCmxpYnJhcnkoT0NTZGF0YSkKc2ltcGxlcl9pbXBvcnRfZGF0YSgib2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5Iiwgb3V0cGF0aCA9IGdldHdkKCkpCmBgYAoKWW91IG1heSBhbHNvIG9idGFpbiB0aGVzZSBmaWxlcyBmcm9tIHRoZSBHaXRIdWIgUmVwb3NpdG9yeS4gSWYgeW91IGhhdmUgdHJvdWJsZSBhY2Nlc3NpbmcgdGhlIEdpdEh1YiBSZXBvc2l0b3J5LCB0aGVzZSBDU1YgZmlsZXMgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkvdHJlZS9tYXN0ZXIvZGF0YS9kYXRhX3JlZHVjZWQpe3RhcmdldD0iX2JsYW5rIn0uCgoqKioKCjxkZXRhaWxzPjxzdW1tYXJ5PiBDbGljayBoZXJlIGZvciBkZXRhaWxzIG9uIGhvdyB0aGUgZGF0YSB3ZXJlIG9yaWdpbmFsbHkgaW1wb3J0ZWQgPC9zdW1tYXJ5PgoKSWYgeW91IGhhdmUgdHJvdWJsZSBhY2Nlc3NpbmcgdGhlIEdpdEh1YiBSZXBvc2l0b3J5LCB5b3UgY2FuIGZvbGxvdyB0aGUgbGlua3MgdG8gZG93bmxvYWQgdGhlIGV4Y2VsIGZpbGVzIGZvciBbMjAxNV0oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkvYmxvYi9tYXN0ZXIvZG9jcy8yMDE1LW55dHMtZGF0YXNldC1hbmQtY29kZWJvb2stbWljcm9zb2Z0LWV4Y2VsL255dHMyMDE1Lnhsc3gpe3RhcmdldD0iX2JsYW5rIn0sIFsyMDE2XShodHRwczovL2dpdGh1Yi5jb20vb3BlbmNhc2VzdHVkaWVzL29jcy1icC12YXBpbmctY2FzZS1zdHVkeS9ibG9iL21hc3Rlci9kb2NzLzIwMTYtbnl0cy1kYXRhc2V0LWFuZC1jb2RlYm9vay1taWNyb3NvZnQtZXhjZWwvbnl0czIwMTYueGxzeCl7dGFyZ2V0PSJfYmxhbmsifSwgWzIwMTddKGh0dHBzOi8vZ2l0aHViLmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L2Jsb2IvbWFzdGVyL2RvY3MvMjAxNy1ueXRzLWRhdGFzZXQtYW5kLWNvZGVib29rLW1pY3Jvc29mdC1leGNlbC9ueXRzMjAxNy54bHN4KXt0YXJnZXQ9Il9ibGFuayJ9LCBbMjAxOF0oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkvYmxvYi9tYXN0ZXIvZG9jcy8yMDE4LW55dHMtZGF0YXNldC1hbmQtY29kZWJvb2stbWljcm9zb2Z0LWV4Y2VsL255dHMyMDE4Lnhsc3gpe3RhcmdldD0iX2JsYW5rIn0sIGFuZCBbMjAxOV0oaHR0cHM6Ly9naXRodWIuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkvYmxvYi9tYXN0ZXIvZG9jcy8yMDE5LW55dHMtZGF0YXNldC1hbmQtY29kZWJvb2stbWljcm9zb2Z0LWV4Y2VsL255dHMyMDE5Lnhsc3gpe3RhcmdldD0iX2JsYW5rIn0uCgpGaXJzdCB3ZSBjcmVhdGVkIGEgdmVjdG9yIG9mIGZpbGUgbmFtZXMgb2YgYWxsIHRoZSBkaWZmZXJlbnQgZXhjZWwgZmlsZXMuIFVzaW5nIHRoZSBgaGVyZSgpYCBmdW5jdGlvbiBvZiB0aGUgYGhlcmVgIHBhY2thZ2UsIHdlIGxvb2tlZCBpbiBhbGwgdGhlIGRpcmVjdG9yaWVzIG9mIHRoZSBwcm9qZWN0LgpUaGUgYGxpc3QuZmlsZXMoKWAgZnVuY3Rpb24gbG9va2VkIGZvciBhbGwgZmlsZXMgd2l0aCAueGxzeCB3aXRoaW4gdGhlc2Ugc3ViLWRpcmVjdG9yaWVzLgoKYGBge3J9CmV4Y2VsX2ZpbGVzIDwtIGxpc3QuZmlsZXMoaGVyZTo6aGVyZSgpLCByZWN1cnNpdmUgPSBUUlVFLAogICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIioueGxzeCIpCmV4Y2VsX2ZpbGVzCmBgYAoKQWxsIHRoZSBmaWxlcyB3ZXJlIHJlYWQgdXNpbmcgYHJlYWRfZXhjZWwoKWAgb2YgdGhlIGByZWFkeGxgIHBhY2thZ2UuIFVzaW5nIHRoZSBgbWFwKClgIGZ1bmN0aW9uIG9mIHRoZSBgcHVycnJgIHBhY2thZ2UgdGhpcyB3YXMgZG9uZSBlZmZpY2llbnRseSBmb3IgYWxsIG9mIHRoZSBleGNlbCBmaWxlcyBpbiB0aGUgdmVjdG9yIHVzaW5nIG9uZSBjb21tYW5kLiBUaGUgYC5gIGlzIHVzZWQgdG8gaW5kaWNhdGUgdGhhdCB3ZSB3YW50IHRvIGFwcGx5IHRoZSBgcmVhZF9leGNlbCgpYCBmdW5jdGlvbiB0byBlYWNoIGVsZW1lbnQgb2YgdGhlIGRhdGEgdGhhdCB3ZSBqdXN0IHBpcGVkIGludG8gdGhlIGBtYXAoKWAgZnVuY3Rpb24uCgpIZXJlIHdlIGFsc28gdXNlZCB0aGUgYCU+JWAgcGlwZSB3aGljaCBjYW4gYmUgdXNlZCB0byBwYXNzIHRoZSBpbnB1dCBmcm9tIG9uZSBmdW5jdGlvbiB0byBhbm90aGVyIGZvciBsYXRlciBzZXF1ZW50aWFsIHN0ZXBzLiBZb3UgY2FuIHVzZSBwaXBlcyBpZiB5b3UgbG9hZCB0aGUgYGRwbHlyYCBwYWNrYWdlIG9yIHRoZSBgbWFncml0dHJgIHBhY2thZ2UuCgpUaGlzIGNyZWF0ZWQgYSBzaW5nbGUgbGlzdCBvZiB0aWJibGVzIChvbmUgZm9yIGVhY2ggZmlsZSkuIApgYGB7ciwgZXZhbCA9IEZBTFNFfQp0YmxfZmlsZXMgPC0gZXhjZWxfZmlsZXMgJT4lCiAgICAgICBtYXAofiByZWFkeGw6OnJlYWRfZXhjZWwoLikpCmBgYAoKVGhlIGVsZW1lbnRzIG9mIHRoaXMgbGlzdCBhcmUgaW4gdGhlIHNhbWUgb3JkZXIgYXMgdGhlIGVsZW1lbnRzIG9mIHRoZSBgZXhjZWxfZmlsZXNgIHZlY3Rvciwgc28gd2UgY2FuIGV4dHJhY3QgdGhlIGRhdGEgZm9yIGEgcGFydGljdWxhciBmaWxlICh5ZWFyKSBieSBzZWxlY3RpbmcgYSBjZXJ0YWluIGVsZW1lbnQgb2YgdGhlIGxpc3QuIEhvd2V2ZXIsIGl0IGlzIHNhZmVyIHRvIGJlIGFibGUgdG8gc2VsZWN0IHRoZSBkYXRhIGZyb20gdGhpcyBsaXN0IGZvciBhIHNwZWNpZmljIHllYXIgdXNpbmcgYSBuYW1lIGJhc2VkIG9uIHRoZSBvcmlnaW5hbCB2ZWN0b3Igb2YgZmlsZSBuYW1lcy4gV2UgZXh0cmFjdGVkIGEgbmFtZSBmcm9tIGVhY2ggRXhjZWwgZmlsZSBuYW1lIHVzaW5nIHRoZSBgc3RyX2V4dHJhY3QoKWAgZnVuY3Rpb24gb2YgdGhlIGBzdHJpbmdyYCBwYWNrYWdlLiBIZXJlIHdlIGFyZSBrZWVwaW5nIG9jY3VycmVuY2VzIG9mIHRoZSBjaGFyYWN0ZXIgc3RyaW5nICJueXRzMjAxIiBmb2xsb3dlZCBieSBhICI1IiwgIjYiLCAiNyIsICI4Iiwgb3IgIjkiLgoKYGBge3J9CnRibF9uYW1lcyA8LSBleGNlbF9maWxlcyAlPiUKICBzdHJfZXh0cmFjdCgibnl0czIwMVs1LTldIikKCnRibF9uYW1lcwpgYGAKClRoZXNlIG5hbWVzIGJlY2FtZSB0aGUgbmFtZXMgb2YgdGhlIHRpYmJsZXMgaW4gdGhlIGxpc3Qgb2YgdGliYmxlcy4KYGBge3IsIGV2YWwgPSBGQUxTRX0KbmFtZXModGJsX2ZpbGVzKSA8LSB0YmxfbmFtZXMKYGBgCgoKU3BlY2lmaWMgY29sdW1ucyB3ZXJlIHNlbGVjdGVkIHVzaW5nIHRoZSBgc2VsZWN0KClgIGZ1bmN0aW9uIG9mIGBkcGx5cmAgZnJvbSBlYWNoIG9mIHRoZSB0aWJibGVzIHVzaW5nIHRoZSB2YXJpYWJsZSBuYW1lLCBhcyBpZGVudGlmaWVkIGluIHRoZSBjb2RlYm9vayBhcyBiZWluZyBvZiBpbnRlcmVzdCBmb3Igb3VyIGFuYWx5c2lzLgpJbiBzb21lIGNhc2VzIGZ1bmN0aW9ucyBsaWtlIGBzdGFydHNfd2l0aCgpYCBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlIHdlcmUgdXNlZCB0byBzZWxlY3Qgc2V2ZXJhbCB2YXJpYWJsZXMgYXQgb25jZS4gTW9zdCBvZiB0aGUgc3VydmV5IHF1ZXN0aW9ucyBhYm91dCB0b2JhY2NvIHVzZSBzdGFydCB3aXRoIGFuIGAiRSJgIG9yIGEgYCJDImAgYWNjb3JkaW5nIHRvIHRoZSBjb2RlYm9va3MuIAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KCnRibF9maWxlc1tbIm55dHMyMDE1Il1dIDwtIHRibF9maWxlc1tbIm55dHMyMDE1Il1dICU+JQogICAgZHBseXI6OnNlbGVjdChwc3UsICMgUHJpbWFyeSBTYW1wbGluZyBVbml0CiAgICAgICAgICAgICAgICAgIGZpbndndCwgIyBBbmFseXNpcyBXZWlnaHQKICAgICAgICAgICAgICAgICAgc3RyYXR1bSwgIyBTYW1wbGluZyBzdHJhdHVtCiAgICAgICAgICAgICAgICAgIFFuMSwgIyBBZ2UKICAgICAgICAgICAgICAgICAgUW4yLCAjIFNleAogICAgICAgICAgICAgICAgICBRbjMsICMgR3JhZGUKICAgICAgICAgICAgICAgICAgc3RhcnRzX3dpdGgoIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgc3RhcnRzX3dpdGgoIkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgKQoKCnRibF9maWxlc1tbIm55dHMyMDE2Il1dIDwtIHRibF9maWxlc1tbIm55dHMyMDE2Il1dICU+JQogICAgZHBseXI6OnNlbGVjdChwc3UsCiAgICAgICAgICAgICAgICAgIGZpbndndCwKICAgICAgICAgICAgICAgICAgc3RyYXR1bSwKICAgICAgICAgICAgICAgICAgUTEsICMgQWdlCiAgICAgICAgICAgICAgICAgIFEyLCAjIFNleAogICAgICAgICAgICAgICAgICBRMywgIyBHcmFkZQogICAgICAgICAgICAgICAgICBzdGFydHNfd2l0aCgiRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gRkFMU0UpLAogICAgICAgICAgICAgICAgICBzdGFydHNfd2l0aCgiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlnbm9yZS5jYXNlID0gRkFMU0UpLAogICAgICAgICAgICAgICAgICBRNTBBLCAjIE1lbnRob2wgIyBXaGF0IGZsYXZvcnMgb2YgdG9iYWNjbyBwcm9kdWN0cyBoYXZlIHlvdSB1c2VkIGluIHRoZSBwYXN0IDMwIGRheXM/IChTZWxlY3Qgb25lIG9yIG1vcmUpCiAgICAgICAgICAgICAgICAgIFE1MEIsICMgQ2xvdmUgb3Igc3BpY2UKICAgICAgICAgICAgICAgICAgUTUwQywgIyBGcnVpdAogICAgICAgICAgICAgICAgICBRNTBELCAjIENob2NvbGF0ZQogICAgICAgICAgICAgICAgICBRNTBFLCAjIEFsY29ob2xpYyBEcmluawogICAgICAgICAgICAgICAgICBRNTBGLCAjIENhbmR5L0Rlc3NlcnRzL090aGVyIFN3ZWV0cwogICAgICAgICAgICAgICAgICBRNTBHICAjIFNvbWUgT3RoZXIgRmxhdm9yIE5vdCBMaXN0ZWQgSGVyZQogICAgICAgICAgICAgICAgICApCgp0YmxfZmlsZXNbWyJueXRzMjAxNyJdXSA8LSB0YmxfZmlsZXNbWyJueXRzMjAxNyJdXSAlPiUKICAgIGRwbHlyOjpzZWxlY3QocHN1LAogICAgICAgICAgICAgICAgICBmaW53Z3QsCiAgICAgICAgICAgICAgICAgIHN0cmF0dW0sCiAgICAgICAgICAgICAgICAgIFExLCAjIEFnZQogICAgICAgICAgICAgICAgICBRMiwgIyBTZXgKICAgICAgICAgICAgICAgICAgUTMsICMgR3JhZGUKICAgICAgICAgICAgICAgICAgc3RhcnRzX3dpdGgoIkUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgc3RhcnRzX3dpdGgoIkMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgUTUwQSwgIyBNZW50aG9sICMgV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzPyAoU2VsZWN0IG9uZSBvciBtb3JlKQogICAgICAgICAgICAgICAgICBRNTBCLCAjIENsb3ZlIG9yIHNwaWNlCiAgICAgICAgICAgICAgICAgIFE1MEMsICMgRnJ1aXQKICAgICAgICAgICAgICAgICAgUTUwRCwgIyBDaG9jb2xhdGUKICAgICAgICAgICAgICAgICAgUTUwRSwgIyBBbGNvaG9saWMgRHJpbmsKICAgICAgICAgICAgICAgICAgUTUwRiwgIyBDYW5keS9EZXNzZXJ0cy9PdGhlciBTd2VldHMKICAgICAgICAgICAgICAgICAgUTUwRyAgIyBTb21lIE90aGVyIEZsYXZvciBOb3QgTGlzdGVkIEhlcmUKICAgICAgICAgICAgICAgICAgKQoKdGJsX2ZpbGVzW1sibnl0czIwMTgiXV0gPC0gdGJsX2ZpbGVzW1sibnl0czIwMTgiXV0gJT4lCiAgICBkcGx5cjo6c2VsZWN0KHBzdSwKICAgICAgICAgICAgICAgICAgZmlud2d0LAogICAgICAgICAgICAgICAgICBzdHJhdHVtLAogICAgICAgICAgICAgICAgICBRMSwgIyBBZ2UKICAgICAgICAgICAgICAgICAgUTIsICMgU2V4CiAgICAgICAgICAgICAgICAgIFEzLCAjIEdyYWRlCiAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBGQUxTRSksCiAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBGQUxTRSksCiAgICAgICAgICAgICAgICAgIFE1MEEsICMgTWVudGhvbCAjV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzPyAoU2VsZWN0IG9uZSBvciBtb3JlKQogICAgICAgICAgICAgICAgICBRNTBCLCAjIENsb3ZlIG9yIHNwaWNlCiAgICAgICAgICAgICAgICAgIFE1MEMsICMgRnJ1aXQKICAgICAgICAgICAgICAgICAgUTUwRCwgIyBDaG9jb2xhdGUKICAgICAgICAgICAgICAgICAgUTUwRSwgIyBBbGNvaG9saWMgRHJpbmsKICAgICAgICAgICAgICAgICAgUTUwRiwgIyBDYW5keS9EZXNzZXJ0cy9PdGhlciBTd2VldHMKICAgICAgICAgICAgICAgICAgUTUwRyAgIyBTb21lIE90aGVyIEZsYXZvciBOb3QgTGlzdGVkIEhlcmUKICAgICAgICAgICAgICAgICAgKQoKdGJsX2ZpbGVzW1sibnl0czIwMTkiXV0gPC0gdGJsX2ZpbGVzW1sibnl0czIwMTkiXV0gJT4lCiAgICBkcGx5cjo6c2VsZWN0KHBzdSwKICAgICAgICAgICAgICAgICAgZmlud2d0LAogICAgICAgICAgICAgICAgICBzdHJhdHVtLAogICAgICAgICAgICAgICAgICBRMSwgIyBBZ2UKICAgICAgICAgICAgICAgICAgUTIsICMgU2V4CiAgICAgICAgICAgICAgICAgIFEzLCAjIEdyYWRlCiAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBGQUxTRSksCiAgICAgICAgICAgICAgICAgIHN0YXJ0c193aXRoKCJDIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBGQUxTRSksCiAgICAgICAgICAgICAgICAgIFE0MCwgIyBCcmFuZCwgZS1jaWdhcmV0dGVzCiAgICAgICAgICAgICAgICAgIFE2MkEsICMgTWVudGhvbCAjIFdoYXQgZmxhdm9ycyBvZiB0b2JhY2NvIHByb2R1Y3RzIGhhdmUgeW91IHVzZWQgaW4gdGhlIHBhc3QgMzAgZGF5cz8gKFNlbGVjdCBvbmUgb3IgbW9yZSkKICAgICAgICAgICAgICAgICAgUTYyQiwgIyBDbG92ZSBvciBzcGljZQogICAgICAgICAgICAgICAgICBRNjJDLCAjIEZydWl0CiAgICAgICAgICAgICAgICAgIFE2MkQsICMgQ2hvY29sYXRlCiAgICAgICAgICAgICAgICAgIFE2MkUsICMgQWxjb2hvbGljIERyaW5rCiAgICAgICAgICAgICAgICAgIFE2MkYsICMgQ2FuZHkvRGVzc2VydHMvT3RoZXIgU3dlZXRzCiAgICAgICAgICAgICAgICAgIFE2MkcsICMgU29tZSBPdGhlciBGbGF2b3IgTm90IExpc3RlZCBIZXJlCiAgICAgICAgICAgICAgICAgICkKYGBgCgpBIGRpcmVjdG9yeSBjYWxsZWQgYHJlZHVjZWRgIHdhcyBjcmVhdGVkIGZvciB0aGUgbmV3IC5jc3YgZmlsZXMgdXNpbmcgdGhlIGJhc2UgYGRpci5jcmVhdGUoKWAgZnVuY3Rpb24uIApBIGNzdiBmaWxlIHdhcyBjcmVhdGVkIGZvciBlYWNoIG9mIHRoZSB0aWJibGVzIGluIHRoZSBsaXN0IHVzaW5nIHRoZSBgd3JpdGVfY3N2KClgIGZ1bmN0aW9uIG9mIHRoZSBgcmVhZHJgIHBhY2thZ2UuClRoaXMgd2FzIGRvbmUgYWxsIGF0IG9uY2UgdXNpbmcgdGhlIGBtYXAoKWAgZnVuY3Rpb24uCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpkaXIuY3JlYXRlKCJkYXRhL3JlZHVjZWQiKQoKbmFtZXModGJsX2ZpbGVzKSAlPiUgbWFwKH4gd3JpdGVfY3N2KHRibF9maWxlc1tbLl1dLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdGggPSBwYXN0ZTAoImRhdGEvcmVkdWNlZC8iLCAuLCAiLmNzdiIpKSkKYGBgCgo8L2RldGFpbHM+CgoqKioKCk5vdyB3ZSB3aWxsIHNob3cgaG93IHRvIHJlYWQgaW4gdGhlIGRhdGEgZnJvbSB0aGUgZml2ZSBjc3YgZmlsZXMgdGhhdCB3ZXJlIGNyZWF0ZWQgZnJvbSB0aGUgZml2ZSBkaWZmZXJlbnQgRXhjZWwgZmlsZXMuCgojIyMgKipSZWFkaW5nIGluIHRoZSBDU1YgZmlsZXMqKgoqKioKClVzaW5nIHRoZSBgaGVyZSgpYCBmdW5jdGlvbiBvZiB0aGUgYGhlcmVgIHBhY2thZ2UsIHdlIGxvb2tlZCBpbiBhbGwgdGhlIGRpcmVjdG9yaWVzIG9mIHRoZSBwcm9qZWN0LiBUaGUgYGhlcmVgIHBhY2thZ2UgYXV0b21hdGljYWxseSBzdGFydHMgbG9va2luZyBmb3IgZmlsZXMgYmFzZWQgb24gd2hlcmUgeW91IGhhdmUgYSBgLlJwcm9qYCBmaWxlIHdoaWNoIGlzIGNyZWF0ZWQgd2hlbiB5b3Ugc3RhcnQgYSBuZXcgUlN0dWRpbyBwcm9qZWN0LgoKKioqCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgbW9yZSBhYm91dCBjcmVhdGluZyBuZXcgcHJvamVjdHMgaW4gUlN0dWRpby4gPC9zdW1tYXJ5PgoKWW91IGNhbiBjcmVhdGUgYSBwcm9qZWN0IGJ5IGdvaW5nIHRvIHRoZSBGaWxlIG1lbnUgb2YgUlN0dWRpbyBsaWtlIHNvOgoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIk5ld19wcm9qZWN0LnBuZyIpKQpgYGAKCllvdSBjYW4gYWxzbyBkbyBzbyBieSBjbGlja2luZyB0aGUgcHJvamVjdCBidXR0b246CgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJwcm9qZWN0X2J1dHRvbi5wbmciKSkKYGBgCgpTZWUgW2hlcmVdKGh0dHBzOi8vc3VwcG9ydC5yc3R1ZGlvLmNvbS9oYy9lbi11cy9hcnRpY2xlcy8yMDA1MjYyMDctVXNpbmctUHJvamVjdHMpe3RhcmdldD0iX2JsYW5rIn0gdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzLiAKCjwvZGV0YWlscz4KCioqKgoKVGhlIGBsaXN0LmZpbGVzKClgIGZ1bmN0aW9uIGxvb2tlZCBmb3IgYWxsIGZpbGVzIHdpdGggLmNzdiB3aXRoaW4gdGhlIGBkYXRhL3JlZHVjZWRgIHN1Yi1kaXJlY3Rvcmllcy4KCkFsbCB0aGUgZmlsZXMgd2VyZSByZWFkIHVzaW5nIGByZWFkX2NzdigpYCBvZiB0aGUgYHJlYWRyYCBwYWNrYWdlLiBVc2luZyB0aGUgYG1hcCgpYCBmdW5jdGlvbiBvZiB0aGUgYHB1cnJyYCBwYWNrYWdlIHRoaXMgd2FzIGRvbmUgZWZmaWNpZW50bHkgZm9yIGFsbCBvZiB0aGUgQ1NWIGZpbGVzIGluIHRoZSB2ZWN0b3IgdXNpbmcgb25lIGNvbW1hbmQuIFRoZSBgLmAgaXMgdXNlZCB0byBpbmRpY2F0ZSB0aGF0IHdlIHdhbnQgdG8gYXBwbHkgdGhlIGByZWFkX2NzdigpYCBmdW5jdGlvbiB0byBlYWNoIGVsZW1lbnQgb2YgdGhlIGRhdGEgdGhhdCB3ZSBqdXN0IHBpcGVkIGludG8gdGhlIGBtYXAoKWAgZnVuY3Rpb24uIEZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBgbWFwKClgIGZ1bmN0aW9uLCBzZWUgW2hlcmVdKGh0dHBzOi8vcHVycnIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbWFwLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0uCgpIZXJlIHdlIGFsc28gdXNlZCB0aGUgYCU+JWAgcGlwZSB3aGljaCBjYW4gYmUgdXNlZCB0byBwYXNzIHRoZSBpbnB1dCBmcm9tIG9uZSBmdW5jdGlvbiB0byBhbm90aGVyIGZvciBsYXRlciBzZXF1ZW50aWFsIHN0ZXBzLiAKCmBgYHtyfQpueXRzX2RhdGEgPC0gbGlzdC5maWxlcyhoZXJlOjpoZXJlKCksIHJlY3Vyc2l2ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiKi5jc3YiKSAlPiUKICBzdHJpbmdyOjpzdHJfc3Vic2V0KC4sICJ3cmFuZ2xlZCIsIG5lZ2F0ZSA9IFRSVUUpICU+JQogIG1hcCh+IHJlYWRfY3N2KC4pKQoKCm55dHNfZGF0YV9uYW1lcyA8LSBsaXN0LmZpbGVzKHJlY3Vyc2l2ZSA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm4gPSAiKi5jc3YiKSAlPiUKICBzdHJpbmdyOjpzdHJfc3Vic2V0KC4sICJ3cmFuZ2xlZCIsIG5lZ2F0ZSA9IFRSVUUpICU+JQogIHN0cl9leHRyYWN0KCJueXRzMjAxWzUtOV0iKQoKbmFtZXMobnl0c19kYXRhKSA8LSBueXRzX2RhdGFfbmFtZXMKYGBgCgpXZSBjYW4gc2F2ZSBvdXIgaW1wb3J0ZWQgZGF0YSBhcyBhbiByZGEgZmlsZSAoc3RhbmRzIGZvciBSIGRhdGEgZmlsZSkgdXNpbmcgdGhlIGBzYXZlKClgIGZ1bmN0aW9uLiAKCmBgYHtyfQpzYXZlKG55dHNfZGF0YSwgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAiaW1wb3J0ZWQiLCAiaW1wb3J0ZWRfZGF0YS5yZGEiKSkKYGBgCgoKIyMgKipEYXRhIEV4cGxvcmF0aW9uIGFuZCBXcmFuZ2xpbmcqKgoqKiogCgpJZiB5b3UgaGF2ZSBiZWVuIGZvbGxvd2luZyBhbG9uZyBidXQgc3RvcHBlZCwgd2UgY291bGQgbG9hZCBvdXIgaW1wb3J0ZWQgZGF0YSBmcm9tIHRoZSAiZGF0YSIgZGlyZWN0b3J5IGxpa2Ugc286CgpgYGB7cn0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgImltcG9ydGVkIiwgImltcG9ydGVkX2RhdGEucmRhIikpCmBgYAoKKioqCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIGRhdGEgaW1wb3J0IHNlY3Rpb24gY2xpY2sgaGVyZS4gPC9zdW1tYXJ5PgoKRmlyc3QgeW91IG5lZWQgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYE9DU2RhdGFgIHBhY2thZ2U6CgpgYGB7ciwgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygiT0NTZGF0YSIpCmxpYnJhcnkoT0NTZGF0YSkKYGBgCgpUaGVuLCB5b3UgbWF5IGxvYWQgdGhlIGltcG9ydGVkIGRhdGEgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOgoKYGBge3IsIGV2YWw9RkFMU0V9CmltcG9ydGVkX2RhdGEoIm9jcy1icC12YXBpbmctY2FzZS1zdHVkeSIsIG91dHBhdGggPSBnZXR3ZCgpKQpsb2FkKGhlcmU6OmhlcmUoIk9DU2RhdGEiLCAiZGF0YSIsICJpbXBvcnRlZCIsICJpbXBvcnRlZF9kYXRhLnJkYSIpKQpgYGAKCklmIHRoZSBwYWNrYWdlIGRvZXMgbm90IHdvcmsgZm9yIHlvdSwgYWx0ZXJuYXRpdmVseSwgYW4gUkRBIGZpbGUgKHN0YW5kcyBmb3IgUiBkYXRhKSBvZiB0aGUgZGF0YSBjYW4gYmUgZm91bmQgaW4gb3VyIFtHaXRIdWIgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tLy9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L3RyZWUvbWFzdGVyL2RhdGEvaW1wb3J0ZWQpIG9yIHNsaWdodGx5IG1vcmUgZGlyZWN0bHkgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L21hc3Rlci9kYXRhL2ltcG9ydGVkL2ltcG9ydGVkX2RhdGEucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJpbXBvcnRlZCIgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgdG8gY29weSBhbmQgcGFzdGUgb3VyIGNvZGUuIFdlIHVzZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGFuZCB0aGUgW2BoZXJlYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIG5hdmlnYXRlIHRvIHRoZSBmaWxlIG1vcmUgZWFzaWx5LiAKCmBgYHtyfQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAiaW1wb3J0ZWQiLCAiaW1wb3J0ZWRfZGF0YS5yZGEiKSkKYGBgCgoqKioKPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+CgpZb3UgY2FuIGNyZWF0ZSBhIHByb2plY3QgYnkgZ29pbmcgdG8gdGhlIEZpbGUgbWVudSBvZiBSU3R1ZGlvIGxpa2Ugc286CgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpCmBgYAoKWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQpgYGAKClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4KCjwvZGV0YWlscz4KKioqCjwvZGV0YWlscz4KCioqKgoKCk91ciBnb2FsIGluIHRoaXMgc2VjdGlvbiBpcyB0byBhZGp1c3Qgb3IgIndyYW5nbGUiIHRoZSBkYXRhIGZyb20gZWFjaCB5ZWFyIGludG8gYSBjb21tb24gZm9ybWF0IHNvIHRoYXQgd2UgY2FuIGNvbWJpbmUgdGhlIGRhdGFzZXRzIGFjcm9zcyB5ZWFycyBmb3Igb3VyIGFuYWx5c2lzLCBhbmQgc28gdGhhdCB3ZSBoYXZlIHZhbHVlcyBpbiBvdXIgdmFyaWFibGVzIHRoYXQgYXJlIGNvcnJlY3QgYW5kIGVhc3kgdG8gaW50ZXJwcmV0LiBXZSB3aWxsIG5lZWQgdG8gdW5kZXJzdGFuZCB3aGF0IGlzIHRoZSBzYW1lIGFuZCB3aGF0IGlzIGRpZmZlcmVudCBhY3Jvc3MgdGhlIGRhdGEgZnJvbSBkaWZmZXJlbnQgeWVhcnMsIHJlbmFtZSBhbmQgcmVjb2RlIHRoZSB2YXJpYWJsZXMgKGUuZy4sIGJ5IHJlcGxhY2luZyB0aGUgbnVtYmVycyAxIGFuZCAyIHdpdGggdGhlIHZhbHVlcyAiTWFsZSIgYW5kICJGZW1hbGUiIGZvciB0aGUgYFNleGAgdmFyaWFibGUpLCBhbmQgY29tYmluZSB0aGUgZGF0YS4gV2Ugd2lsbCB3YWxrIHRocm91Z2ggdGhlc2Ugc3RlcHMgYmVsb3cuIAoKRmlyc3QsIGxldCdzIHRha2UgYSBsb29rIGF0IG91ciBkYXRhLiBXZSBjYW4gZ2V0IGEgZ29vZCBzZW5zZSBvZiBpdCB1c2luZyB0aGUgYGdsaW1wc2UoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZS4KCiMjIyMgey5zY3JvbGxhYmxlIH0KCmBgYHtyfQpkcGx5cjo6Z2xpbXBzZShueXRzX2RhdGFbWyJueXRzMjAxNSJdXSkKYGBgCiMjIyMKCiMjIyAqKlVwZGF0aW5nIHRoZSBzZXQgb2YgdmFyaWFibGVzIGFuZCB0aGVpciBuYW1lcyoqCioqKiAKClRoZSBlYXNpZXN0IHdheSBvZiBtYWtpbmcgaXQgc28gdGhhdCB0aGUgZGF0YSBmcm9tIHRoZSBkaWZmZXJlbnQgeWVhcnMgY2FuIGJlIGNvbWJpbmVkIGlzIGJ5IG1ha2luZyBzdXJlIHRoYXQgdGhlIHNhbWUgdHlwZSBvZiBkYXRhIGluIGRpZmZlcmVudCBkYXRhc2V0cyBzaGFyZSB0aGUgc2FtZSBuYW1lcy4gSW4gYWRkaXRpb24sIGdpdmluZyB0aGUgY29sdW1ucyBpbmZvcm1hdGl2ZSBuYW1lcyB3aWxsIGhlbHAgbWFrZSBvdXIgY29kZSBtb3JlIHJlYWRhYmxlLiBDdXJyZW50bHksIGl0IGlzbid0IHZlcnkgY2xlYXIgd2hhdCBtb3N0IG9mIHRoZSB2YXJpYWJsZXMgaW5kaWNhdGUgc2luY2UgdGhlIHZhcmlhYmxlIG5hbWVzIGFyZSB1bmluZm9ybWF0aXZlIG9uIHRoZWlyIG93biwgd2l0aG91dCB0aGUgW2NvZGVib29rXSguL2RvY3MvMjAxOS1ueXRzLWRhdGFzZXQtYW5kLWNvZGVib29rLW1pY3Jvc29mdC1leGNlbC8yMDE5LW55dHMtY29kZWJvb2stcC5wZGYpe3RhcmdldD0iX2JsYW5rIn0uCgpXZSB3YW50IHRvIHJlbmFtZSB2YXJpYWJsZXMgbGlrZSBgUW4xYCB0byBzb21ldGhpbmcgbW9yZSBtZWFuaW5nZnVsIGxpa2UgYEFnZWAuCgpUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSBgcmVuYW1lKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UuIFRoZSBuZXcgbmFtZSBpcyBhbHdheXMgbGlzdGVkIGZpcnN0IGJlZm9yZSB0aGUgYD1gLiBUaGlzIGZ1bmN0aW9uIHdpbGwgcmVwbGFjZSB0aGUgb2xkIHZhcmlhYmxlIG5hbWVzIHdpdGggdGhlIG5ldyBvbmVzLCBpLmUuLCBhZnRlciBydW5uaW5nIHRoZSBjb2RlIGJlbG93LCB0aGVyZSB3aWxsIG5vIGxvbmdlciBiZSBhIGBRbjFgIHZhcmlhYmxlIGluIHRoZSBkYXRhc2V0LCBidXQgdGhlcmUgd2lsbCBiZSBhbiBgQWdlYCB2YXJpYWJsZSBpbnN0ZWFkLiBXZSB3aWxsIHN0YXJ0IHdvcmtpbmcgd2l0aCB0aGUgMjAxNSBkYXRhLCBhbmQgdGhlbiBtb3ZlIG9uIHRvIHRoZSBvdGhlciB5ZWFycyBkb3duIGJlbG93LgoKYGBge3J9Cm55dHNfZGF0YVtbIm55dHMyMDE1Il1dIDwtIG55dHNfZGF0YVtbIm55dHMyMDE1Il1dICU+JQogIGRwbHlyOjpyZW5hbWUoQWdlID0gUW4xLAogICAgICAgICAgICAgICAgU2V4ID0gUW4yLAogICAgICAgICAgICAgICAgR3JhZGUgPSBRbjMpCmBgYAoKCgpVbHRpbWF0ZWx5IHdlIHdpbGwgYmUgY29tYmluaW5nIHRoZSBkYXRhIGZyb20gZWFjaCB5ZWFyIHVzaW5nIHRoZSBgYmluZF9yb3dzKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UsIHdoaWNoIHdpbGwgZmlsbCBpbiBgTkFgIHZhbHVlcyBmb3IgYW55IGNvbHVtbnMgdGhhdCBkbyBub3QgZXhpc3QgZm9yIG9uZSBvZiB0aGUgeWVhcnMuCgpUaGUgZGF0YSBmb3IgMjAxNi0yMDE4IGhhdmUgbWFueSBjb21tb24gYXR0cmlidXRlcywgc28gd2Ugd2lsbCB3YW50IHRvIHdyaXRlIGNvZGUgdGhhdCBjYW4gYmUgYXBwbGllZCB0byBhbGwgdGhyZWUgZGF0YXNldHMuIFRvIGRvIHRoaXMsIHdlIHdpbGwgdXNlIGEgKipmdW5jdGlvbioqIGluIFIsIHdoaWNoIGlzIGJhc2ljYWxseSBhIHBpZWNlIG9mIGNvZGUgdGhhdCBjYW4gYmUgYXBwbGllZCB0byBzaW1pbGFyIGJ1dCBkaWZmZXJlbnQgb2JqZWN0cyBpbiBSIChlLmcuLCB0aGUgZGF0YSB0aWJibGVzIGZyb20gZWFjaCBvZiB0aGVzZSB0aHJlZSB5ZWFycykuIFJlY2FsbCB0aGF0IHlvdSBhcmUgdXNpbmcgZnVuY3Rpb25zIGZyb20gcGFja2FnZXMgYWxsIHRoZSB0aW1lLCBsaWtlIHRoZSBgcmVuYW1lKClgIGZ1bmN0aW9uIG9mIHRoZSBgZHBseXJgIHBhY2thZ2UuIE5vdyB5b3UgYXJlIGdvaW5nIHRvIHdyaXRlIHlvdXIgb3duIGZ1bmN0aW9uISBGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBmdW5jdGlvbnMsIHNlZSBbaGVyZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9mdW5jdGlvbnMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4KClRoZXNlIG5leHQgMyB5ZWFycyBoYXZlIHRoZSBzYW1lIHN0cnVjdHVyZSBmb3IgbWFueSBvZiB0aGUgcXVlc3Rpb25zIHdlIGFyZSBpbnRlcmVzdGVkIGluLiBGb3IgZXhhbXBsZSwgdGhleSBhbGwgaGF2ZSBmbGF2b3IgcXVlc3Rpb25zLCBidXQgbm90IGEgYnJhbmQgcXVlc3Rpb24uIE1vcmVvdmVyLCB0aGVpciB2YXJpYWJsZSBuYW1lcyBhcmUgY29uc2lzdGVudCBhY3Jvc3MgdGhlIHllYXJzOyBmb3IgZWFjaCB5ZWFyLCB3ZSB3YW50IHRvIHJlcGxhY2UgdGhlIHZhZ3VlIHF1ZXN0aW9uIG5hbWUgYFE1MEFgIHdpdGggdGhlIHZhbHVlIGBtZW50aG9sYCBpbiBhbGwgdGhyZWUgZGF0YXNldHMsIGFuZCB0aGUgc2FtZSBpcyB0cnVlIGZvciB0aGUgb3RoZXIgZmxhdm9yIHZhcmlhYmxlcy4gIAoKU2luY2Ugd2Ugd2FudCB0byBwZXJmb3JtIHRoZSBzYW1lIG1vZGlmaWNhdGlvbnMgb24gdGhlIGRhdGEgZnJvbSBhbGwgdGhyZWUgeWVhcnMsIHJhdGhlciB0aGFuIHJlcGVhdGluZyB0aGUgc2FtZSBzb21ld2hhdCBtZXNzeSBwaWVjZSBvZiBjb2RlIHRocmVlIHRpbWVzLCB3ZSBjYW4gZG8gdGhpcyBtb3JlIGVmZmljaWVudGx5IGlmIHdlIGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIGRvIGFsbCBvZiB0aGVzZSBzdGVwcyBhdCBvbmNlLiBUaGVuIHdlIGNhbiB1c2UgdGhlIGBtYXBfYXQoKWAgZnVuY3Rpb24gb2YgdGhlIGBwdXJycmAgcGFja2FnZSwgd2hpY2ggaXMgYW4gZXh0ZW5zaW9uIG9mIHRoZSBgbWFwKClgIGZ1bmN0aW9uIHRoYXQgd2UgdXNlZCBpbiB0aGUgW0RhdGEgSW1wb3J0XSAoaHR0cHM6Ly93d3cub3BlbmNhc2VzdHVkaWVzLm9yZy9vY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkvI0RhdGFfSW1wb3J0KXt0YXJnZXQ9Il9ibGFuayJ9IHNlY3Rpb24gdG8gYXBwbHkgdGhlIGZ1bmN0aW9uIHdlIGp1c3QgY3JlYXRlZCAoZm9yIHJlbmFtaW5nIHZhcmlhYmxlcyBldGMuKSBzcGVjaWZpY2FsbHkgdG8gdGhlIGRhdGEgZnJvbSAyMDE2LTIwMTggd2l0aGluIHRoZSBgbnl0c19kYXRhYC4gQnkgdXNpbmcgYHZhcnMoKWAgaW5zaWRlIG9mIHRoZSBgbWFwX2F0KClgIGZ1bmN0aW9uIHdlIGNhbiBzcGVjaWZ5IHdoYXQgdGliYmxlcyB3aXRoaW4gb3VyIGBueXRzX2RhdGFgIGxpc3Qgd2Ugd2FudCB0byBpbmNsdWRlIG9yIGV4Y2x1ZGUuIAoKCiMjIyMgKipGdW5jdGlvbnMqKgoqKiogCgpTbyBob3cgZG8geW91IGNyZWF0ZSBhIGZ1bmN0aW9uPwpZb3UgZmlyc3QgbmVlZCB0byBzcGVjaWZ5IHRoYXQgeW91IGFyZSBjcmVhdGluZyBhIGZ1bmN0aW9uIGJ5IHVzaW5nIHRoZSBgZnVuY3Rpb24oKWAgYmFzZSBmdW5jdGlvbi4KWWVzLCB0aGF0J3MgcmlnaHQgaXQgaXMgYSBmdW5jdGlvbiBmb3IgY3JlYXRpbmcgZnVuY3Rpb25zIGNhbGxlZCBmdW5jdGlvbiEKCkZpcnN0IHdlIHNwZWNpZnkgb3VyIGlucHV0IHdpdGhpbiB0aGUgcGFyZW50aGVzZXMgb2YgYGZ1bmN0aW9uKClgLiBUaHVzIGlmIG91ciBmdW5jdGlvbiB3aWxsIGFwcGx5IHNvbWV0aGluZyB0byBhbiBpbnB1dCBjYWxsZWQgYHhgIHRoZW4gd2Ugd291bGQgdXNlIGBmdW5jdGlvbih4KWAuIFRoZW9yZXRpY2FsbHkgb3VyIGlucHV0IGNhbiBiZSBuYW1lZCB3aGF0ZXZlciB3ZSB3YW50LCBidXQgd2Ugd2UgbmVlZCB0byByZWZlciB0byBpdCBjb25zaXN0ZW50bHkgd2l0aGluIG91ciBmdW5jdGlvbiB0byBpbmRpY2F0ZSB3aGF0IHdlIHdhbnQgZG9uZSB3aXRoIHRoZSBpbnB1dCBkYXRhLiBXZSBjYW4gYWN0dWFsbHkgaGF2ZSBtb3JlIHRoYW4gb25lIGlucHV0IGFzIHdlbGwsIHdlIHdvdWxkIGluZGljYXRlIHR3byBpbnB1dHMgbGlrZSB0aGlzOiBgZnVuY3Rpb24oeCwgYilgLiBIZXJlIHdlIHdvdWxkIGJlIHVzaW5nIGJvdGggYHhgIGFuZCBgYmAgdG8gZG8gc29tZXRoaW5nIGluIG91ciBmdW5jdGlvbi4KClRoZSBuZXh0IHBhcnQgb2YgYSBmdW5jdGlvbiBpcyBkZWZpbmVkIHdpdGhpbiBjdXJseSBicmFja2V0cyBge31gLiBUaGlzIGlzIHdoZXJlIHdlIHdyaXRlIHdoYXQgd2Ugd2FudCBkb25lIHRvIG91ciBpbnB1dHMuCgoqKioKCjxkZXRhaWxzPiA8c3VtbWFyeT4gQ2xpY2sgaGVyZSB0byBzZWUgYSBzaW1wbGUgZXhhbXBsZSA8L3N1bW1hcnk+IAoKT3VyIGZ1bmN0aW9uIHdpbGwgYmUgY2FsbGVkIGBzaW1wbGVfZnVuY3Rpb25gLCB3aGljaCB3aWxsIGFkZCAyIHRvIG91ciBpbnB1dC4gSXQgd2lsbCB0YWtlIHRoZSBpbnB1dCBgdmVjdG9yX2RhdGFgIChub3RlIHRoYXQgd2UgdXN1YWxseSB3YW50IHRvIHVzZSBhbiBpbmZvcm1hdGl2ZSBpbnB1dCBhcmd1bWVudCBsaWtlIGB2ZWN0b3JfZGF0YWApLiAKCmBgYHtyfQpzaW1wbGVfZnVuY3Rpb24gPC0gZnVuY3Rpb24odmVjdG9yX2RhdGEpIHsKICB2ZWN0b3JfZGF0YSArIDIKfQpgYGAKCldlIGNhbiB1c2UgYW4gaW5wdXQgdGhhdCBpcyBhIHZlY3RvciBjYWxsZWQgYHhgLgoKYGBge3J9CnggPSBjKDEsIDIsIDMsIDQpCngKYGBgCgpOb3cgd2Ugc3BlY2lmeSB1c2luZyB0aGUgYXJndW1lbnQgYCB2ZWN0b3JfZGF0YSA9YCB0byBpbmRpY2F0ZSB0aGF0IGB4YCBpcyBvdXIgaW5wdXQgdGhhdCB3ZSB3YW50IHRvIHBlcmZvcm0gdGhlIGZ1bmN0aW9uIG9uLgoKYGBge3J9CnNpbXBsZV9mdW5jdGlvbih2ZWN0b3JfZGF0YSA9IHgpCmBgYAoKVGhpcyBmdW5jdGlvbiBhbHNvIHdvcmtzIGZvciBvdGhlciB2ZWN0b3IgaW5wdXQuIEZvciBleGFtcGxlLCB2ZWN0b3IgYHlgIGlzIG5vdyBvdXIgaW5wdXQgdGhhdCB3ZSB3YW50IHRvIHBlcmZvcm0gdGhlIGZ1bmN0aW9uIG9uLgoKYGBge3J9CnkgPSBjKDUsIDEwLCAxNSwgMjApCgpzaW1wbGVfZnVuY3Rpb24odmVjdG9yX2RhdGEgPSB5KQpgYGAKCjwvZGV0YWlscz4KCioqKgoKSW4gb3VyIGNhc2Ugd2Ugd2lsbCBiZSBhcHBseWluZyBvdXIgZnVuY3Rpb24gdG8gdGhlIHZhcmlhYmxlIG5hbWVzIGZvciB0aGUgZGF0YXNldCBmb3IgZWFjaCB5ZWFyLiBUaGUgb3V0cHV0IG9mIG91ciBmdW5jdGlvbiBpcyB0aGUgcmVzdWx0IG9mIHJlbmFtaW5nIHRoZXNlIHZhcmlhYmxlcyBmb3IgZWFjaCB5ZWFyLiAKCmBgYHtyfQoKdXBkYXRlX3N1cnZleSA8LSBmdW5jdGlvbihkYXRhc2V0KSB7IGRhdGFzZXQgJT4lCiAgICAgICAgICAgcmVuYW1lKEFnZSA9IFExLAogICAgICAgICAgICAgICAgICBTZXggPSBRMiwKICAgICAgICAgICAgICAgIEdyYWRlID0gUTMsCiAgICAgICAgICAgICAgbWVudGhvbCA9IFE1MEEsCiAgICAgICAgICBjbG92ZV9zcGljZSA9IFE1MEIsCiAgICAgICAgICAgICAgICBmcnVpdCA9IFE1MEMsCiAgICAgICAgICAgIGNob2NvbGF0ZSA9IFE1MEQsCiAgICAgIGFsY29ob2xpY19kcmluayA9IFE1MEUsCiBjYW5keV9kZXNzZXJ0X3N3ZWV0cyA9IFE1MEYsCiAgICAgICAgICAgICAgICBvdGhlciA9IFE1MEcpCn0KCiMgb3B0aW9ucyB0byBhcHBseSB0aGUgZnVuY3Rpb24gdG8gdGhlIGRhdGE6CiMgbnl0c19kYXRhIDwtIG55dHNfZGF0YSAlPiUgbWFwX2F0KHZhcnMobnl0czIwMTYsIG55dHMyMDE3LCBueXRzMjAxOCksIFVwZGF0ZV9zdXJ2ZXkpCm55dHNfZGF0YSA8LSBueXRzX2RhdGEgJT4lIG1hcF9hdCh2YXJzKC1ueXRzMjAxNSwgLW55dHMyMDE5KSwgdXBkYXRlX3N1cnZleSkKYGBgCgpUaGUgZmluYWwgeWVhciwgMjAxOSwgaGFzIGEgc2xpZ2h0bHkgZGlmZmVyZW50IGRhdGEgc3RydWN0dXJlIGNvbXBhcmVkIHRvIHRoZXNlIGVhcmxpZXIgZGF0YXNldHMuIEl0IGFjdHVhbGx5IGhhcyBhIGBicmFuZF9lY2lnYCB2YXJpYWJsZSBhbHJlYWR5IGFuZCBkaWZmZXJlbnQgcXVlc3Rpb24gbnVtYmVycyBjb3JyZXNwb25kIHRvIG91ciBmbGF2b3IgcXVlc3Rpb25zIG9mIGludGVyZXN0LiBTbyB3ZSB3aWxsIHJlbmFtZSB0aGUgdmFyaWFibGVzIGluIHRoaXMgZGF0YXNldCBpbmRpdmlkdWFsbHkuIFdlIGNvdWxkIGFsc28gd3JpdGUgdGhpcyBhcyBhIGZ1bmN0aW9uLCBidXQgc2luY2Ugd2UgYXJlIG9ubHkgYXBwbHlpbmcgdGhpcyBvbmUgdGltZSwgdGhlcmUgaXMgbm8gbmVlZCB0by4gRnVuY3Rpb25zIGFyZSByZWFsbHkgaGVscGZ1bCBmb3IgcmVwZWF0aW5nIHRoZSBzYW1lIHRhc2sgcmVwZWF0ZWRseSB1c2luZyBkaWZmZXJlbnQgZGF0YSBpbnB1dHMuCgpgYGB7cn0Kbnl0c19kYXRhW1sibnl0czIwMTkiXV0gPC0gbnl0c19kYXRhW1sibnl0czIwMTkiXV0gJT4lCiAgICByZW5hbWUoYnJhbmRfZWNpZyA9IFE0MCwKICAgICAgICAgICAgICAgICAgQWdlID0gUTEsCiAgICAgICAgICAgICAgICAgIFNleCA9IFEyLAogICAgICAgICAgICAgICAgR3JhZGUgPSBRMywKICAgICAgICAgICAgICBtZW50aG9sID0gUTYyQSwKICAgICAgICAgIGNsb3ZlX3NwaWNlID0gUTYyQiwKICAgICAgICAgICAgICAgIGZydWl0ID0gUTYyQywKICAgICAgICAgICAgY2hvY29sYXRlID0gUTYyRCwKICAgICAgYWxjb2hvbGljX2RyaW5rID0gUTYyRSwKIGNhbmR5X2Rlc3NlcnRfc3dlZXRzID0gUTYyRiwKICAgICAgICAgICAgICAgIG90aGVyID0gUTYyRykKYGBgCgoKTm93IGxldCdzIHRha2UgYSBsb29rIGF0IHRoZSB2YXJpYWJsZSBuYW1lcyBmb3IgZWFjaCBvZiB0aGUgeWVhcnMgdXNpbmcgdGhlIGBtYXBgIGZ1bmN0aW9uIGZyb20gYHB1cnJyYC4KCiMjIyMgey5zY3JvbGxhYmxlIH0KCmBgYHtyfQptYXAobnl0c19kYXRhLCBuYW1lcykKYGBgCiMjIyMKCkl0J3MgbG9va2luZyBiZXR0ZXIhIFRoZSBkYXRhIHRoYXQgb3ZlcmxhcCBhY3Jvc3MgeWVhcnMgaGF2ZSB0aGUgc2FtZSB2YXJpYWJsZSBuYW1lcy4KCiMjIyAqKlVwZGF0aW5nIFZhbHVlcyoqCioqKgpOb3cgdGhhdCB3ZSBoYXZlIG1hZGUgc29tZSBwcm9ncmVzcyBvbiB0aGUgc2VsZWN0aW9uIGFuZCBuYW1lcyBvZiB0aGUgdmFyaWFibGVzIHRoZW1zZWx2ZXMsIHdlIHdpbGwgd29yayBvbiB0aGUgdmFsdWVzIGNvbnRhaW5lZCBpbiB0aGUgZGlmZmVyZW50IHZhcmlhYmxlcy4KCldlIGNhbiBzdGFydCB3aXRoIHVwZGF0aW5nIHRoZSB2YWx1ZXMgZm9yIGBBZ2VgIGFuZCBgR3JhZGVgLCBzbyB0aGF0IHRoZXkgYXJlIG1vcmUgdW5kZXJzdGFuZGFibGUuIAoKUmVjYWxsIGZyb20gdGhlIGNvZGVib29rIGZvciB0aGlzIHllYXIncyBkYXRhc2V0IHRoYXQgYEFnZWAgaXNuJ3QgbGlzdGVkIGluIHRoZSB3YXkgb25lIG1pZ2h0IGV4cGVjdCwgaS5lLiwgaXQgaXMgbm90IGp1c3QgYSBudW1iZXIgb2YgeWVhcnMsIGJ1dCBhIG51bWVyaWNhbGx5IHZhbHVlZCBjYXRlZ29yaWNhbCB2YXJpYWJsZS4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIGZpZy5hbGlnbiA9ImNlbnRlciIsIG91dC53aWR0aCA9ICI2MDAgcHgifQoKa25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInFuMS5wbmciKSkKYGBgCgpUaGUgc2FtZSBpcyB0cnVlIGZvciBgR3JhZGVgOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgZmlnLmFsaWduID0iY2VudGVyIiwgb3V0LndpZHRoID0gIjYwMCBweCJ9Cgprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiZ3JhZGUucG5nIikpCmBgYAoKKipUaGlzIGlzIHdoeSBpdCBpcyBzbyBpbXBvcnRhbnQgdG8gYWx3YXlzIGNoZWNrIHRoZSBjb2RlYm9vayEhKioKCldlIGFsc28gIHdhbnQgdG8gcmVwbGFjZSB0aGUgdmFsdWUgb2YgYDE5YCBmb3IgYEFnZWAgdG8gYmUgYCI+MTgiYCBhbmQgdGhlIHZhbHVlIG9mIGAxM2AgZm9yIGBHcmFkZWAgdG8gYmUgcmVwbGFjZWQgd2l0aCBgIlVuZ3JhZGVkL090aGVyImAgQWxzbyBhY2NvcmRpbmcgdG8gdGhlIGNvZGVib29rcywgbnVtZXJpYyB2YWx1ZXMgb2YgYDFgIGluZGljYXRlIGEgc3VydmV5IGFuc3dlciBvZiBgRkFMU0VgLCB3aGlsZSBhIHZhbHVlIG9mIGAyYCBpbmRpY2F0ZXMgYFRSVUVgLiBgU2V4YCBhbHNvIG5lZWRzIHRvIGJlIHJlY29kZWQuIElmIHdlIHRha2UgYSBsb29rIGF0IHRoZSBjb2RlYm9va3MgY2FyZWZ1bGx5IChtYWtlIHN1cmUgeW91IGxvb2sgYXQgdGhlIHF1ZXN0aW9ucyB0aGF0IHdlIHB1bGxlZCwgbm90IHRoZSByZWNvZGVkIHZhbHVlcyksIHdlIHdpbGwgc2VlIHRoYXQgbWFsZXMgYXJlIGluZGljYXRlZCBieSBgMWAgYW5kIGZlbWFsZXMgYXJlIGluZGljYXRlZCBieSBgMmAuIEZpbmFsbHkgc29tZSB2YWx1ZXMgYXJlIGluZGljYXRlZCB3aXRoIGAiKiJgIG9yYCIqKiJgIHdoZW4gdGhleSBhcmUgbWlzc2luZy4gV2Ugd2FudCB0byByZXBsYWNlIHRoZXNlIHdpdGggYE5BYC4KCkxldCdzIGNyZWF0ZSBhIGZ1bmN0aW9uIHRvIG1ha2UgYWxsIHRoZXNlIHVwZGF0ZXMuIFdlIHdpbGwgdXNlIHRoZSBgbXV0YXRlYCBmdW5jdGlvbiBvZiB0aGUgYGRwbHlyYCBwYWNrYWdlIHRvIG1vZGlmeSB0aGVzZSB2YXJpYWJsZXMuIFRoaXMgZnVuY3Rpb24gY2FuIGFsc28gYmUgdXNlZCB0byBjcmVhdGUgbmV3IHZhcmlhYmxlcy4gV2Ugd2lsbCBhbHNvIHVzZSB0aGUgIGByZWNvZGUoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZSB0byByZXBsYWNlIHNwZWNpZmljIHZhbHVlcyBvZiBjZXJ0YWluIHZhcmlhYmxlcy4KCmBgYHtyfQp1cGRhdGVfdmFsdWVzIDwtIGZ1bmN0aW9uKGRhdGFzZXQpIHsKICBkYXRhc2V0ICU+JQogICAgZHBseXI6Om11dGF0ZShBZ2UgPSBhcy5udW1lcmljKEFnZSkgKyA4LAogICAgICAgICAgICAgICAgICBHcmFkZSA9IGFzLm51bWVyaWMoR3JhZGUpICsgNSkgJT4lCiAgICBtdXRhdGUoQWdlID0gYXMuZmFjdG9yKEFnZSksCiAgICAgICAgICAgR3JhZGUgPSBhcy5mYWN0b3IoR3JhZGUpLAogICAgICAgICAgIFNleCA9IGFzLmZhY3RvcihTZXgpKSAlPiUKICAgIG11dGF0ZShTZXggPSByZWNvZGUoU2V4LAogICAgICAgICAgICAgICAgICAgICAgICBgMWAgPSAibWFsZSIsCiAgICAgICAgICAgICAgICAgICAgICAgIGAyYCA9ICJmZW1hbGUiKSkgJT4lCiAgICBkcGx5cjo6bXV0YXRlX2FsbCggfiByZXBsYWNlKC4sIC4gJWluJSBjKCIqIiwgIioqIiksIE5BKSkgJT4lCiAgICBtdXRhdGUoQWdlID0gcmVjb2RlKEFnZSwKICAgICAgICAgICAgICAgICAgICAgICAgYDE5YCA9ICI+MTgiKSwKICAgICAgICAgICBHcmFkZSA9IHJlY29kZShHcmFkZSwKICAgICAgICAgICAgICAgICAgICAgICAgICBgMTNgID0gIlVuZ3JhZGVkL090aGVyIikpICU+JQogICAgZHBseXI6Om11dGF0ZV9hdCh2YXJzKAogICAgICBzdGFydHNfd2l0aCgiRSIsIGlnbm9yZS5jYXNlID0gRkFMU0UpLAogICAgICBzdGFydHNfd2l0aCgiQyIsIGlnbm9yZS5jYXNlID0gRkFMU0UpCiAgICApLAogICAgbGlzdCggfiByZWNvZGUoCiAgICAgIC4sCiAgICAgIGAxYCA9IFRSVUUsCiAgICAgIGAyYCA9IEZBTFNFLAogICAgICAuZGVmYXVsdCA9IE5BLAogICAgICAubWlzc2luZyA9IE5BCiAgICApKSkKfQoKbnl0c19kYXRhIDwtIG55dHNfZGF0YSAlPiUgbWFwKC4sIHVwZGF0ZV92YWx1ZXMpCmBgYAoKCk5vdyBpZiB3ZSB3YW50ZWQgdG8gY2hlY2sgdGhhdCBldmVyeXRoaW5nIGlzIGV4cGVjdGVkIHdlIGNvdWxkIGRvIHNvbWV0aGluZyBsaWtlIHRoaXMgdG8gY2hlY2sgdGhlIGBTZXhgIHZhcmlhYmxlIHVzaW5nIHRoZSBgY291bnQoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZS4gSXQgaXMgYWR2aXNhYmxlIHRvIGNoZWNrIHlvdXIgZGF0YSBmcmVxdWVudGx5IHRvIG1ha2Ugc3VyZSB0aGF0IGl0IGlzIGFzIGV4cGVjdGVkIQoKQWNjb3JkaW5nIHRvIHRoZSBjb2RlYm9vaywgd2Ugc2hvdWxkIGhhdmU6ICAKCjEpIDgsOTU4IG1hbGVzIGluIDIwMTUgIAoyKSAxMCw0MzggbWFsZXMgaW4gMjAxNiAgCjMpIDgsODgxIG1hbGVzIGluIDIwMTcgIAo0KSAxMCwwNjkgbWFsZXMgaW4gMjAxOCAgCjUpIDksODAzIG1hbGVzIGluIDIwMTkgICAKCmBgYHtyfQpjb3VudF9zZXggPC0gZnVuY3Rpb24oZGF0YXNldCkge2RhdGFzZXQgJT4lIGNvdW50KFNleCl9Cm55dHNfZGF0YSAlPiUgbWFwKC4sIGNvdW50X3NleCkKYGBgCgoKTG9va3MgZ29vZCEKClRoZSB5ZWFycyAoMjAxNi0yMDE5KSB0aGF0IGhhdmUgZmxhdm9ycyBhbHNvIG5lZWQgdGhlIGZsYXZvciBkYXRhIHRvIGJlIGxvZ2ljYWwgKG1lYW5pbmcgVFJVRSBvciBGQUxTRSk6CgpJbiB0aGlzIGNhc2Ugd2UgYWxzbyBhcmUgc2V0dGluZyBtaXNzaW5nIHZhbHVlcyB0byBgRkFMU0VgIGJlY2F1c2UgdGhlbiB0aGUgYFRSVUVgIHZhbHVlcyB3aWxsIHJlcHJlc2VudCB0aG9zZSB3aG8gcmVwb3J0ZWQgdXNpbmcgYSBzcGVjaWZpYyBmbGF2b3Igb3V0IG9mIGFsbCB1c2VycywgcmF0aGVyIHRoYW4gdGhvc2UgdGhhdCB1c2VkIGEgc3BlY2lmaWMgZmxhdm9yIGNvbXBhcmVkIHRvIHRob3NlIHdobyB1c2VkIGEgZGlmZmVyZW50IGZsYXZvci4KCgpgYGB7cn0KdXBkYXRlX2ZsYXZvcnMgPC0gZnVuY3Rpb24oZGF0YXNldCkge2RhdGFzZXQgJT4lCiAgIG11dGF0ZV9hdCh2YXJzKG1lbnRob2w6b3RoZXIpLAogICAgICAgICAgICAgIGxpc3QofiByZWNvZGUoLiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgYDFgID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgIC5kZWZhdWx0ID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAubWlzc2luZyA9IEZBTFNFKSkpIH0KCm55dHNfZGF0YSAgPC0gbnl0c19kYXRhICAlPiUgbWFwX2F0KHZhcnMoLW55dHMyMDE1KSwgdXBkYXRlX2ZsYXZvcnMpCmBgYAoKCk5vdyB0aGVyZSBhcmUganVzdCBhIGZldyBjaGFuZ2VzIG5lZWRlZCB0aGF0IGFyZSBzcGVjaWZpYyB0byAyMDE5LiBTcGVjaWZpY2FsbHksIHNvbWUgb2YgdGhlIDIwMTkgcXVlc3Rpb25zIHVzZSB0aGUgdmFsdWVzICIuTiIsICIuUyIsIGFuZCAiLloiIHRvIGluZGljYXRlIGRpZmZlcmVudCB0eXBlcyBvZiBtaXNzaW5nIGRhdGEgKHNlZSBmb3IgZXhhbXBsZSBRMiBvZiB0aGUgMjAxOSBbY29kZWJvb2tdKC4vZG9jcy8yMDE5LW55dHMtZGF0YXNldC1hbmQtY29kZWJvb2stbWljcm9zb2Z0LWV4Y2VsLzIwMTktbnl0cy1jb2RlYm9vay1wLnBkZil7dGFyZ2V0PSJfYmxhbmsifSk7IHdlIGp1c3Qgd2FudCB0aGVtIHRvIGJlIHJlcGxhY2VkIHdpdGggYE5BYCB2YWx1ZXMuCgoKYGBge3J9Cm55dHNfZGF0YVtbIm55dHMyMDE5Il1dIDwtIG55dHNfZGF0YVtbIm55dHMyMDE5Il1dICU+JQogIG11dGF0ZV9hbGwofiByZXBsYWNlKC4sIC4gJWluJSBjKCIuTiIsICIuUyIsICIuWiIpLCBOQSkpICU+JQogIG11dGF0ZShwc3UgPSBhcy5jaGFyYWN0ZXIocHN1KSkgJT4lCiAgbXV0YXRlKGJyYW5kX2VjaWcgPSByZWNvZGUoYnJhbmRfZWNpZywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYDFgID0gIk90aGVyIiwgIyBsZXZlbHMgMSw4IGNvbWJpbmVkIHRvIGBPdGhlcmAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYDJgID0gIkJsdSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGAzYCA9ICJKVVVMIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYDRgID0gIkxvZ2ljIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYDVgID0gIk1hcmtUZW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBgNmAgPSAiTkpPWSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGA3YCA9ICJWdXNlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYDhgID0gIk90aGVyIikpCmBgYAoKR3JlYXQhIE5vdyBvdXIgdmFsdWVzIGRvbid0IG5lZWQgdG8gYmUgaGFuZGxlZCBhbnkgZGlmZmVyZW50bHkgZm9yIGFueSBvZiB0aGUgeWVhcnMsIHRodXMgd2UgY2FuIGNvbWJpbmUgdGhlIGRhdGEgYWNyb3NzIHllYXJzLgoKRXZlbiB0aG91Z2ggd2UgaGF2ZSBkaWZmZXJlbnQgbnVtYmVycyBvZiB2YXJpYWJsZXMgZm9yIGVhY2ggeWVhciwgd2UgY2FuIGNvZXJjZSB0aGUgZGF0YSB0byBiZSBjb21iaW5lZCBpbnRvIG9uZSB0aWJibGUgYnkgdXNpbmcgdGhlIGBiaW5kX3Jvd3MoKWAgZnVuY3Rpb24gb2YgYGRwbHlyYC4gSW1wb3J0YW50bHksIHRoaXMgZnVuY3Rpb24gZG9lcyBub3QgcmVxdWlyZSB0aGF0IHRoZSBjb2x1bW5zIGJlIHRoZSBzYW1lLiAgVGhpcyB3aWxsIGNyZWF0ZSBOQSB2YWx1ZXMgZm9yIGFueSB2YXJpYWJsZSB0aGF0IGlzIG5vdCBwcmVzZW50IGluIGdpdmVuIGRhdGEgZnJhbWUgYnV0IGlzICBwcmVzZW50IGluIG9uZSBvZiB0aGUgb3RoZXIgZGF0YSBmcmFtZXMgdGhhdCBpcyBiZWluZyBjb21iaW5lZC4gTm90ZSB0aGF0IHRoZSBgYmluZF9jb2xzKClgIGZ1bmN0aW9uIGRvZXMgZXhwZWN0IHRoYXQgdGhlIHJvd3MgbWF0Y2guIFRoZSBgLmlkYCBhcmd1bWVudCB3aWxsIGNyZWF0ZSBhIG5ldyB2YXJpYWJsZSB3aXRoIHZhbHVlcyB0byBsaW5rIGVhY2ggcm93IHRvIGl0cyBvcmlnaW5hbCBkYXRhIGZyYW1lLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiBzZWUgW2hlcmVdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvYmluZC5odG1sKS4KCgpgYGB7cn0Kbnl0c19kYXRhIDwtIG55dHNfZGF0YSAlPiUKICBtYXBfZGYoZHBseXI6OmJpbmRfcm93cywgLmlkID0gInllYXIiKQoKZ2xpbXBzZShueXRzX2RhdGEpCmBgYAoKV2Ugd2lsbCB3YW50IHRvIGRvIHNvbWUgb2Ygb3VyIGFuYWx5c2lzIHNwbGl0IGJ5IHllYXIsIHNvIHdlIHdvdWxkIGxpa2UgdG8gYmUgc3VyZSB3ZSBoYXZlIG9uZSB2YXJpYWJsZSB0aGF0IGhhcyB0aGUgY29ycmVjdCB2YWx1ZSBmb3IgeWVhci4gSXQgbG9va3MgbGlrZSB3ZSBqdXN0IG5lZWQgdG8gcmVtb3ZlIGAibnl0cyJgIGZyb20gdGhlIHllYXIgdmFyaWFibGUgdGhhdCB3ZSBjcmVhdGVkIGZyb20gdGhlIG5hbWVzIG9mIHRoZSB0aWJibGVzIGluIG91ciBsaXN0IGFuZCB3ZSBzaG91bGQgYmUgYWxsIHNldC4gV2Ugd2lsbCB1c2UgYW5vdGhlciBmdW5jdGlvbiBmcm9tIHRoZSBgc3RyaW5ncmAgcGFja2FnZSB0byBkbyB0aGlzLiBUaGUgYHN0cl9yZW1vdmUoKWAgZnVuY3Rpb24gdGFrZXMgYSBzdHJpbmcgZm9sbG93ZWQgYnkgYSBwYXR0ZXJuIGFuZCByZW1vdmVzIHRoZSBwYXR0ZXJuIGZyb20gdGhlIHN0cmluZy4KCmBgYHtyfQpueXRzX2RhdGEgPC0gbnl0c19kYXRhICU+JQogIG11dGF0ZSh5ZWFyID0gYXMubnVtZXJpYyhzdHJfcmVtb3ZlKHllYXIsICJueXRzIikpKQpgYGAKCkhlcmUgaXMgb3VyIGNsZWFuIGFuZCB3cmFuZ2xlZCBkYXRhOgoKIyMjIyB7LnNjcm9sbGFibGV9CmBgYHtyfQpnbGltcHNlKG55dHNfZGF0YSkKCmBgYAoKIyMjIwoKCk5vdGUgdGhhdCB0aGVyZSBhcmUgc2V2ZXJhbCB2YXJpYWJsZXMgd2hlcmUgdGhlcmUgYXJlIHNpbWlsYXIgbmFtZXMsIGJ1dCB3aXRoIGEgYENgIGNvbXBhcmVkIHRvIGFuIGBFYCBpbiB0aGUgdmFyaWFibGUgbmFtZS4gVGhvc2Ugc3RhcnRpbmcgd2l0aCBgQ2AgYXJlIHJlbGF0ZWQgdG8gcXVlc3Rpb25zIGFib3V0IGN1cnJlbnQgdXNhZ2UgKGxhc3QgMzAgZGF5cyksIHdoaWxlIHRob3NlIHdpdGggYW4gYEVgIGFyZSByZWxhdGVkIHRvIHVzYWdlIGFjcm9zcyB0aGUgc3R1ZGVudCByZXNwb25kZW50J3Mgd2hvbGUgbGlmZSAoImV2ZXIiIHVzYWdlKS4gV2Ugd2lsbCBkaXNjdXNzIHRoZXNlIGdyb3VwcyBmdXJ0aGVyIGJlbG93LgoKTm93IHdlIHdpbGwgc2F2ZSBvdXIgd3JhbmdsZWQgZGF0YS4gV2Ugd2lsbCBzYXZlIGl0IGFzIGFuIHJkYSBmaWxlIGZvciBvdXJzZWx2ZXMgYW5kIGFzIGNzdiBmaWxlcywgYXMgdGhpcyBpcyBvZnRlbiBhIGdvb2Qgb3B0aW9uIGZvciBjb2xsYWJvcmF0b3JzLiBXZSB3aWxsIHNhdmUgdGhpcyBmaWxlIGluIGEgZGlyZWN0b3J5IGNhbGxlZCAid3JhbmdsZWQiIHdpdGhpbiBvdXIgImRhdGEiIGRpcmVjdG9yeSBvZiBvdXIgcHJvamVjdC4KCmBgYHtyfQpzYXZlKG55dHNfZGF0YSwgZmlsZSA9IGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkKCndyaXRlX2NzdihueXRzX2RhdGEsIHBhdGggPSBoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIm55dHNfZGF0YS5jc3YiKSkKYGBgCgoKIyMjICoqVmFyaWFibGUgVGFibGUqKgoqKioKPGRldGFpbHM+PHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIGEgdGFibGUgYWJvdXQgdGhlIGZpbmFsIHZhcmlhYmxlcyBpbiBvdXIgZGF0YSBzZXQuIDwvc3VtbWFyeT4KCgpWYXJpYWJsZSAgIHwgRGV0YWlscyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAotLS0tLS0tLS0tIHwtLS0tLS0tLS0tLS0tCioqeWVhcioqICB8IHRoZSB5ZWFyIHRoYXQgdGhlIHN1cnZleSByZXN1bHRzIGZyb20gYSBwYXJ0aWN1bGFyIHN0dWRlbnQgcmVzcG9uZGVudCB3ZXJlIGFjcXVpcmVkICAKKipwc3UqKiB8IHRoZSBwcmltYXJ5IHNhbXBsaW5nIHVuaXQgZm9yIHRoZSBzdXJ2ZXkgd2VpZ2h0aW5nICAKKipmaW53Z3QqKiB8IHRoZSBmaW5hbCBhbmFseXNpcyB3ZWlnaHQgZm9yIHRoZSBzdXJ2ZXkgd2VpZ2h0aW5nICAKKipzdHJhdHVtKiogfCB0aGUgc3RyYXR1bSB1c2VkIGZvciB2YXJpYW5jZSBlc3RpbWF0aW9uIGZvciB0aGUgc3VydmV5IHdlaWdodGluZyAgCioqQWdlKiogfCB0aGUgYWdlIG9mIHRoZSBzdHVkZW50IHdoZW4gdGhleSB0b29rIHRoZSBzdXJ2ZXkgIAoqKlNleCoqIHwgdGhlIHNleCBvZiB0aGUgc3R1ZGVudCB3aGVuIHRoZXkgdG9vayB0aGUgc3VydmV5ICAKKipHcmFkZSoqIHwgdGhlIGdyYWRlIG9mIHRoZSB0aGUgc3R1ZGVudCB3aGVuIHRoZSB0b29rIHRoZSBzdXJ2ZXkgIAoqKkVDSUdUKiogfCBzdHVkZW50IHJlcG9ydGVkIGhhdmluZyBldmVyIHRyaWVkIGNpZ2FyZXR0ZSBzbW9raW5nLCBldmVuIG9uZSBvciB0d28gcHVmZnMgIAoqKkVDSUdBUioqIHwgc3R1ZGVudCByZXBvcnRlZCBoYXZpbmcgZXZlciB0cmllZCBjaWdhciwgY2lnYXJpbGxvLCBvciBsaXR0bGUgY2lnYXIgc21va2luZywgZXZlbiBvbmUgb3IgdHdvIHB1ZmZzICAKKipFU0xUKiogfCBzdHVkZW50IHJlcG9ydGVkIGhhdmluZyBldmVyIHRyaWVkIGNoZXdpbmcgdG9iYWNjbywgc251ZmYsIG9yIGRpcCAgCioqRUVMQ0lHVCoqIHwgc3R1ZGVudCByZXBvcnRlZCBoYXZpbmcgZXZlciB0cmllZCBlLWNpZ2FyZXR0ZXMgIAoqKkVST0xMQ0lHVFMqKiB8IHN0dWRlbnQgcmVwb3J0ZWQgaGF2aW5nIGV2ZXIgdHJpZWQgcm9sbC15b3VyLW93biBjaWdhcmV0dGVzICAKKipFRkxBVkNJR1RTKiogfCAgKDIwMTUgb25seSkgYmFzZWQgb24gYW5zd2VyIHRvICJXaGljaCBvZiB0aGUgZm9sbG93aW5nIHRvYmFjY28gcHJvZHVjdHMgdGhhdCB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzIHdlcmUgZmxhdm9yZWQ/IiAgCioqRUJJRElTKiogfCBzdHVkZW50IHJlcG9ydGVkIGhhdmluZyBldmVyIHRyaWVkIGJpZGlzIChzbWFsbCBicm93biBjaWdhcmV0dGVzIHdyYXBwZWQgaW4gYSBsZWFmKSAgCioqRUZMQVZDSUdBUioqIHwgc3R1ZGVudCByZXBvcnRlZCBoYXZpbmcgZXZlciB0cmllZCBhIGZsYXZvcmVkIGNpZ2FyICgyMDE1LTIwMTYpCioqRUhPT0tBSCoqIHwgc3R1ZGVudCByZXBvcnRlZCBoYXZpbmcgZXZlciBzbW9rZWQgdG9iYWNjbyBmcm9tIGEgaG9va2FoIG9yIGEgd2F0ZXJwaXBlICAKKipFUElQRSoqIHwgc3R1ZGVudCByZXBvcnRlZCBoYXZpbmcgZXZlciBzbW9rZWQgdG9iYWNjbyBmcm9tIGEgcGlwZSAobm90IGhvb2thaCkgIAoqKkVTTlVTKiogfCBzdHVkZW50IHJlcG9ydGVkIGhhdmluZyBldmVyIHVzZWQgc251cywgc3VjaCBhcyBDYW1lbCBvciBNYWxib3JvIFNudXMgIAoqKkVESVNTT0xWKiogfCBzdHVkZW50IHJlcG9ydGVkIGhhdmluZyBldmVyIHRyaWVkIGRpc3NvbHZhYmxlIHRvYmFjY28gcHJvZHVjdHMgc3VjaCBhcyBBcml2YSwgU3RvbmV3YWxsLCBDYW1lbCBvcmJzLCBDYW1lbCBzdGlja3MsIE1hcmxib3JvIHN0aWNrcywgb3IgQ2FtZWwgc3RyaXBzICAKKipDQ0lHVCoqIHwgc3R1ZGVudCByZXBvcnRlZCB0aGV5IHNtb2tlZCBjaWdhcmV0dGVzIG9uID49IDEgb2YgdGhlIHBhc3QgMzAgZGF5cyAgCioqQ0NJR0FSKiogfCBzdHVkZW50IHJlcG9ydGVkIHRoZXkgc21va2VkIGNpZ2FycyBvbiA+PSAxIG9mIHRoZSBwYXN0IDMwIGRheXMgIAoqKkNTTFQqKiB8IHN0dWRlbnQgcmVwb3J0ZWQgdGhleSB1c2VkIGNoZXdpbmcgdG9iYWNjbywgc251ZmYsIG9yIGRpcCBvbiA+PSAxIG9mIHRoZSBwYXN0IDMwIGRheXMgIAoqKkNFTENJR1QqKiB8IHN0dWRlbnQgcmVwb3J0ZWQgdGhleSB1c2VkIGVsZWN0cm9uaWMgY2lnYXJldHRlcyBvciBlLWNpZ2FyZXR0ZXMgb25lIG9yIG1vcmUgZGF5cyBpbiB0aGUgcGFzdCAzMAoqKkNST0xMQ0lHVFMqKiB8IHN0dWRlbnQgcmVwb3J0ZWQgdGhleSBzbW9rZWQgcm9sbC15b3VyLW93biBjaWdhcmV0dGVzIGR1cmluZyB0aGUgcGFzdCAzMCBkYXlzICAKKipDRkxBVkNJR1RTKip8ICgyMDE1IG9ubHkpIGJhc2VkIG9uIGFuc3dlciB0byAiV2hpY2ggb2YgdGhlIGZvbGxvd2luZyB0b2JhY2NvIHByb2R1Y3RzIHRoYXQgeW91IHVzZWQgaW4gdGhlIHBhc3QgMzAgZGF5cyB3ZXJlIGZsYXZvcmVkPyIgCioqQ0JJRElTKiogfCBzdHVkZW50IHJlcG9ydGVkIHRoZXkgc21va2VkIGJpZGlzIGR1cmluZyB0aGUgcGFzdCAzMCBkYXlzICAKKipDSE9PS0FIKiogfCBzdHVkZW50IHJlcG9ydGVkIHRoZXkgc21va2VkIHRvYmFjY28gaW4gYSBob29rYWggb24gPj0gMSBvZiB0aGUgcGFzdCAzMCBkYXlzICAKKipDUElQRSoqIHwgc3R1ZGVudCByZXBvcnRlZCB0aGV5IHNtb2tlZCB0b2JhY2NvIGluIGEgcGlwZSAobm90IGhvb2thaCkgZHVyaW5nIHRoZSBwYXN0IDMwIGRheXMgIAoqKkNTTlVTKiogfCBzdHVkZW50IHJlcG9ydGVkIHRoZXkgdXNlZCBzbnVzIGR1cmluZyB0aGUgcGFzdCAzMCBkYXlzICAgIAoqKkNESVNTT0xWKiogfCBzdHVkZW50IHJlcG9ydGVkIHRoZXkgdXNlZCBkaXNzb2x2YWJsZSB0b2JhY2NvIHByb2R1Y3RzIHN1Y2ggYXMgQXJpdmEsIFN0b25ld2FsbCwgQ2FtZWwgb3JicywgQ2FtZWwgc3RpY2tzLCBNYXJsYm9ybyBzdGlja3MsIG9yIENhbWVsIHN0cmlwcyBkdXJpbmcgdGhlIHBhc3QgMzAgZGF5cyAgCioqYnJhbmRfZWNpZyoqIHwgc3R1ZGVudCBhbnN3ZXIgdG8gIkR1cmluZyB0aGUgcGFzdCAzMCBkYXlzLCB3aGF0IGJyYW5kIG9mIGUtY2lnYXJldHRlcyBkaWQgeW91IHVzdWFsbHkgdXNlPyIgIAoqKm1lbnRob2wqKiB8IHN0dWRlbnQgc2VsZWN0ZWQgTWVudGhvbCBvciBtaW50IGFzIHRoZSBhbnN3ZXIgdG8gIldoYXQgZmxhdm9ycyBvZiB0b2JhY2NvIHByb2R1Y3RzIGhhdmUgeW91IHVzZWQgaW4gdGhlIHBhc3QgMzAgZGF5cz8gKHNlbGVjdCBvbmUgb3IgbW9yZSkiICAKKipjbG92ZV9zcGljZSoqIHwgc3R1ZGVudCBzZWxlY3RlZCBjbG92ZSBvciBzcGljZSBhcyB0aGUgYW5zd2VyIHRvICJXaGF0IGZsYXZvcnMgb2YgdG9iYWNjbyBwcm9kdWN0cyBoYXZlIHlvdSB1c2VkIGluIHRoZSBwYXN0IDMwIGRheXM/IChzZWxlY3Qgb25lIG9yIG1vcmUpIiAgCioqZnJ1aXQqKiB8IHN0dWRlbnQgc2VsZWN0ZWQgZnJ1aXQgYXMgdGhlIGFuc3dlciB0byAiV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzPyAoc2VsZWN0IG9uZSBvciBtb3JlKSIgIAoqKmNob2NvbGF0ZSoqIHwgc3R1ZGVudCBzZWxlY3RlZCBjaG9jb2xhdGUgYXMgdGhlIGFuc3dlciB0byAiV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzPyAoc2VsZWN0IG9uZSBvciBtb3JlKSIgIAoqKmFsY29ob2xpY19kcmluayoqIHwgc3R1ZGVudCBzZWxlY3RlZCBhbGNvaG9saWMgZHJpbmsgKHN1Y2ggYXMgd2luZSwgY29nbmFjLCBtYXJnYXJpdGEsIG9yIG90aGVyIGNvY2t0YWlscykgYXMgdGhlIGFuc3dlciB0byAiV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzPyAoY2hvb3NlIG9uZSBvciBtb3JlKSIgIAoqKmNhbmR5X2Rlc3NlcnRfc3dlZXRzKiogfCAgc3R1ZGVudCBzZWxlY3RlZCBjYW5keSwgZGVzc2VydHMgb3Igb3RoZXIgc3dlZXRzIGFzIHRoZSBhbnN3ZXIgdG8gIldoYXQgZmxhdm9ycyBvZiB0b2JhY2NvIHByb2R1Y3RzIGhhdmUgeW91IHVzZWQgaW4gdGhlIHBhc3QgMzAgZGF5cz8gKGNob29zZSBvbmUgb3IgbW9yZSkiIAoqKm90aGVyKiogfCBzdHVkZW50IHNlbGVjdGVkIHNvbWUgb3RoZXIgZmxhdm9yIG5vdCBsaXN0ZWQgYXMgdGhlIGFuc3dlciB0byAiV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdCAzMCBkYXlzPyAoY2hvb3NlIG9uZSBvciBtb3JlKSIgCioqRUhUUCoqIHwgc3R1ZGVudCByZXBvcnRlZCBoYXZpbmcgZXZlciB0cmllZCBoZWF0ZWQgKGFsc28ga25vd24gYXMgImhlYXQtbm90LWJ1cm4iKSB0b2JhY2NvIHByb2R1Y3RzICAKKipDSFRQKiogfCBzdHVkZW50IHJlcG9ydGVkIHRoZXkgdXNlZCBoZWF0ZWQgdG9iYWNjbyBwcm9kdWN0cyBkdXJpbmcgdGhlIHBhc3QgMzAgZGF5cyAgCjwvZGV0YWlscz4KCgoKIyMgKipEYXRhIFZpc3VhbGl6YXRpb24qKgoqKiogCgpJZiB5b3UgaGF2ZSBiZWVuIGZvbGxvd2luZyBhbG9uZyBidXQgc3RvcHBlZCwgd2UgY291bGQgbG9hZCB0aGUgd3JhbmdsZWQgZGF0YSBmcm9tIHRoZSAiZGF0YSIgZGlyZWN0b3J5IGxpa2Ugc286CgpgYGB7cn0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGEucmRhIikpCmBgYAoKKioqCjxkZXRhaWxzPiA8c3VtbWFyeT4gSWYgeW91IHNraXBwZWQgdGhlIGRhdGEgZXhwbG9yYXRpb24gYW5kIHdyYW5nbGluZyBzZWN0aW9uIGNsaWNrIGhlcmUuIDwvc3VtbWFyeT4KCkZpcnN0IHlvdSBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlIGBPQ1NkYXRhYCBwYWNrYWdlOgoKYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoIk9DU2RhdGEiKQpsaWJyYXJ5KE9DU2RhdGEpCmBgYAoKVGhlbiwgeW91IG1heSBsb2FkIHRoZSB3cmFuZ2xlZCBkYXRhIHVzaW5nIHRoZSBmb2xsb3dpbmcgY29kZToKCmBgYHtyLCBldmFsPUZBTFNFfQp3cmFuZ2xlZF9yZGEoIm9jcy1icC12YXBpbmctY2FzZS1zdHVkeSIsIG91dHBhdGggPSBnZXR3ZCgpKQpsb2FkKGhlcmU6OmhlcmUoIk9DU2RhdGEiLCAiZGF0YSIsICJ3cmFuZ2xlZCIsICJ3cmFuZ2xlZF9kYXRhLnJkYSIpKQpgYGAKCklmIHRoZSBwYWNrYWdlIGRvZXMgbm90IHdvcmsgZm9yIHlvdSwgYWx0ZXJuYXRpdmVseSwgYW4gUkRBIGZpbGUgKHN0YW5kcyBmb3IgUiBkYXRhKSBvZiB0aGUgZGF0YSBjYW4gYmUgZm91bmQgaW4gb3VyIFtHaXRIdWIgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tLy9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L3RyZWUvbWFzdGVyL2RhdGEvd3JhbmdsZWQpIG9yIHNsaWdodGx5IG1vcmUgZGlyZWN0bHkgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L21hc3Rlci9kYXRhL3dyYW5nbGVkL3dyYW5nbGVkX2RhdGEucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJ3cmFuZ2xlZCIgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgdG8gY29weSBhbmQgcGFzdGUgb3VyIGNvZGUuIFdlIHVzZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGFuZCB0aGUgW2BoZXJlYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIG5hdmlnYXRlIHRvIHRoZSBmaWxlIG1vcmUgZWFzaWx5LiAKCmBgYHtyfQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YS5yZGEiKSkKYGBgCgoqKioKPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIHRvIHNlZSBtb3JlIGFib3V0IGNyZWF0aW5nIG5ldyBwcm9qZWN0cyBpbiBSU3R1ZGlvLiA8L3N1bW1hcnk+CgpZb3UgY2FuIGNyZWF0ZSBhIHByb2plY3QgYnkgZ29pbmcgdG8gdGhlIEZpbGUgbWVudSBvZiBSU3R1ZGlvIGxpa2Ugc286CgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAiTmV3X3Byb2plY3QucG5nIikpCmBgYAoKWW91IGNhbiBhbHNvIGRvIHNvIGJ5IGNsaWNraW5nIHRoZSBwcm9qZWN0IGJ1dHRvbjoKCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aD0iNjAlIn0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgInByb2plY3RfYnV0dG9uLnBuZyIpKQpgYGAKClNlZSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1Qcm9qZWN0cykgdG8gbGVhcm4gbW9yZSBhYm91dCB1c2luZyBSU3R1ZGlvIHByb2plY3RzIGFuZCBbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBsZWFybiBtb3JlIGFib3V0IHRoZSBgaGVyZWAgcGFja2FnZS4KCjwvZGV0YWlscz4KKioqCjwvZGV0YWlscz4KCioqKgoKClJlY2FsbCB0aGF0IG91ciBtYWluIHF1ZXN0aW9ucyB3ZXJlOgoKMSkgSG93IGhhcyB0b2JhY2NvIGFuZCBlLWNpZ2FyZXR0ZS92YXBpbmcgdXNlIGJ5IEFtZXJpY2FuIHlvdXRocyBjaGFuZ2VkIHNpbmNlIDIwMTU/CjIpIEhvdyBkb2VzIGUtY2lnYXJldHRlIHVzZSBjb21wYXJlIGJldHdlZW4gbWFsZXMgYW5kIGZlbWFsZXM/CjMpIFdoYXQgdmFwaW5nIGJyYW5kcyBhbmQgZmxhdm9ycyBhcHBlYXIgdG8gYmUgdXNlZCB0aGUgbW9zdCBmcmVxdWVudGx5PyAgCldlIHdpbGwgYmFzZSB0aGlzIG9uIHRoZSBmb2xsb3dpbmcgc3VydmV5IHF1ZXN0aW9uczogICAKPiAiRHVyaW5nIHRoZSBwYXN0IDMwIGRheXMsIHdoYXQgYnJhbmQgb2YgZS1jaWdhcmV0dGVzIGRpZCB5b3UgdXN1YWxseSB1c2U/IiAgIAo+ICJXaGF0IGZsYXZvcnMgb2YgdG9iYWNjbyBwcm9kdWN0cyBoYXZlIHlvdSB1c2VkIGluIHRoZSBwYXN0CjMwIGRheXM/IiAKCjQpIElzIHRoZXJlIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gZS1jaWdhcmV0dGUvdmFwaW5nIHVzZSBhbmQgb3RoZXIgdG9iYWNjbyB1c2U/CgoKV2UgYXJlIG5vdyBnb2luZyB0byBjcmVhdGUgZGF0YSB2aXN1YWxpemF0aW9ucyB0byBleHBsb3JlIGVhY2ggb2YgdGhlc2UgcXVlc3Rpb25zLgoKRm9yIG1hbnkgb2YgdGhlc2UgcXVlc3Rpb25zIHdlIHdpbGwgYmUgaW50ZXJlc3RlZCBpbiBib3RoICoqY3VycmVudCoqIGFuZCAqKmV2ZXIqKiB1c2Vycywgc28gd2Ugd2lsbCB3YW50IHRvIGNyZWF0ZSBhIHZhcmlhYmxlIGZvciBsYWJlbGluZyBpbmRpdmlkdWFscyB3aG8gYXJlIGN1cnJlbnQgdXNlcnMgb2YgYW55IHRvYmFjY28gcHJvZHVjdCAob3Igbm90LCBpLmUuLCB3aG8gZG8gbm90IGN1cnJlbnRseSB1c2UgYSB0b2JhY2NvIHByb2R1Y3QpIGFuZCBhIHZhcmlhYmxlIGZvciBsYWJlbGluZyBpbmRpdmlkdWFscyB3aG8gYXJlICJldmVyIHVzZXJzIiBvZiBhbnkgdG9iYWNjbyBwcm9kdWN0IChvciBub3QsIGkuZS4sIHdobyBoYXZlIG5ldmVyIHVzZWQgYSB0b2JhY2NvIHByb2R1Y3QpLgoKV2UgZGVmaW5lIHRoZXNlIHR3byBncm91cHMgYXMgZm9sbG93czoKCjEpICoqY3VycmVudCoqID0gc3R1ZGVudHMgd2hvIHVzZWQgYSBwcm9kdWN0IGZvciA+PTEgZGF5IGluIHRoZSBwYXN0IDMwIGRheXMgIAoyKSAqKmV2ZXIqKiA9ICBzdHVkZW50cyB3aG8gcmVwb3J0IGhhdmluZyB1c2VkIG9yIHRyaWVkIGEgcHJvZHVjdCBhdCBhbnkgcG9pbnQgaW4gdGltZQoKQWxsICoqY3VycmVudCoqIHVzZXJzIGFyZSB0aGVyZWZvcmUgKipldmVyKiogdXNlcnMgYnV0IG5vdCBhbGwgKipldmVyKiogdXNlcnMgYXJlICoqY3VycmVudCoqIHVzZXJzLiBUaHVzLCAqKmN1cnJlbnQqKiB1c2VycyBhcmUgYSBzdWJzZXQgb2YgKipldmVyKiogdXNlcnMuCgpUbyBhZGQgdGhlc2UgZ3JvdXBpbmcgdmFyaWFibGVzIHRvIG91ciBkYXRhIHdlIHdpbGwgZG8gYSBiaXQgbW9yZSB3cmFuZ2xpbmcgdXNpbmcgdGhlIGBtdXRhdGUoKWAgZnVuY3Rpb24gYWdhaW4gb2YgdGhlIGBkcGx5cmAgcGFja2FnZS4gQXMgZGlzY3Vzc2VkIGFib3ZlLCBvdXIgZGF0YXNldCBjb250YWlucyBhIHNldCBvZiBxdWVzdGlvbnMgdGhhdCByZWxhdGUgdG8gd2hldGhlciB0aGUgc3R1ZGVudCBoYXMgZXZlciB1c2VkIHRoZSBwYXJ0aWN1bGFyIHRvYmFjY28gcHJvZHVjdCAocXVlc3Rpb25zIHRoYXQgc3RhcnQgd2l0aCB0aGUgbGV0dGVyICJFIiksIGFuZCBxdWVzdGlvbnMgdGhhdCByZWxhdGUgdG8gd2hldGhlciB0aGUgc3R1ZGVudCBjdXJyZW50bHkgdXNlcyB0aGUgcGFydGljdWxhciB0b2JhY2NvIHByb2R1Y3QgKHF1ZXN0aW9ucyB0aGF0IHN0YXJ0IHdpdGggdGhlIGxldHRlciAiQyIpLiAKCkhlcmUgYXJlIHNvbWUgZXhhbXBsZXMgZm9yIHRoZXNlIGRhdGEgZW50cmllczogIAoKIC0gRVBJUEU6IFN0dWRlbnRzIHdobyByZXBvcnRlZCB0aGV5IGhhdmUgc21va2VkIHRvYmFjY28gZnJvbSBhIHBpcGUgKG5vdCBob29rYWgpLiAgCiAtIENQSVBFOiBTdHVkZW50cyB3aG8gcmVwb3J0ZWQgdGhleSBzbW9rZWQgdG9iYWNjbyBpbiBhIHBpcGUgKG5vdCBob29rYWgpIGR1cmluZyB0aGUgcGFzdCAzMCBkYXlzLiAKIC0gRVJPTExDSUdUUzogU3R1ZGVudHMgd2hvIHJlcG9ydGVkIHRoZXkgaGF2ZSB0cmllZCBzbW9raW5nIHJvbGwteW91ci1vd24gY2lnYXJldHRlcy4gCiAtIENST0xMQ0lHVFM6IFN0dWRlbnRzIHdobyByZXBvcnRlZCB0aGV5IHNtb2tlZCByb2xsLXlvdXItb3duIGNpZ2FyZXR0ZXMgZHVyaW5nIHRoZSBwYXN0IDMwIGRheXMuIAogCkJhc2VkIG9uIG1hbnkgcXVlc3Rpb25zIGxpa2UgdGhpczogIAogCiBJbiB0aGUgcGFzdCAzMCBkYXlzLCB3aGljaCBvZiB0aGUgZm9sbG93aW5nIHByb2R1Y3RzIGhhdmUgeW91IHVzZWQgb24gYXQgbGVhc3Qgb25lIGRheT8gKFNlbGVjdCBvbmUgb3IgbW9yZSkgICAKQS4gUm9sbC15b3VyLW93biBjaWdhcmV0dGVzICAKQi4gUGlwZXMgZmlsbGVkIHdpdGggdG9iYWNjbyAobm90IGhvb2thaCBvciB3YXRlcnBpcGUpICAgCkMuIFNudXMsIHN1Y2ggYXMgQ2FtZWwsIE1hcmxib3JvLCBvciBHZW5lcmFsIFNudXMgIApELiBEaXNzb2x2YWJsZSB0b2JhY2NvIHByb2R1Y3RzIHN1Y2ggYXMgQXJpdmEsIFN0b25ld2FsbCwgQ2FtZWwgb3JicywgQ2FtZWwgc3RpY2tzLCBNYXJsYm9ybyBzdGlja3MsCm9yIENhbWVsIHN0cmlwcyAgCkUuIEJpZGlzIChzbWFsbCBicm93biBjaWdhcmV0dGVzIHdyYXBwZWQgaW4gYSBsZWFmKSAgCkYuIEkgaGF2ZSBub3QgdXNlZCBhbnkgb2YgdGhlIHByb2R1Y3RzIGxpc3RlZCBhYm92ZSBpbiB0aGUgcGFzdCAzMCBkYXlzICAKCiBXaGljaCBvZiB0aGUgZm9sbG93aW5nIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgZXZlciB0cmllZCwgZXZlbiBqdXN0IG9uZSB0aW1lPyAoU2VsZWN0IG9uZSBvciBtb3JlKSAgIApBLiBSb2xsLXlvdXItb3duIGNpZ2FyZXR0ZXMgIApCLiBQaXBlcyBmaWxsZWQgd2l0aCB0b2JhY2NvIChub3QgaG9va2FoIG9yIHdhdGVycGlwZSkgICAKQy4gU251cywgc3VjaCBhcyBDYW1lbCwgTWFybGJvcm8sIG9yIEdlbmVyYWwgU251cyAgCkQuIERpc3NvbHZhYmxlIHRvYmFjY28gcHJvZHVjdHMgc3VjaCBhcyBBcml2YSwgU3RvbmV3YWxsLCBDYW1lbCBvcmJzLCBDYW1lbCBzdGlja3MsIE1hcmxib3JvIHN0aWNrcywgb3IgQ2FtZWwgc3RyaXBzICAgCkUuIEJpZGlzIChzbWFsbCBicm93biBjaWdhcmV0dGVzIHdyYXBwZWQgaW4gYSBsZWFmKSAgCkYuIEkgaGF2ZSBuZXZlciB0cmllZCBhbnkgb2YgdGhlIHByb2R1Y3RzIGxpc3RlZCBhYm92ZSAgIAogCiAKV2Ugd2lsbCBzdW0gYWNyb3NzIHRoZSB2YXJpYWJsZXMgdGhhdCByZWxhdGUgdG8gZXZlciBvciBjdXJyZW50IHRvYmFjY28gdXNhZ2UgcXVlc3Rpb25zIHRvIGRldGVybWluZSBpZiB0aGUgc3R1ZGVudCBhbnN3ZXJlZCB5ZXMgdG8gYW55IG9mIHRoZSBldmVyIG9yIGN1cnJlbnQgcXVlc3Rpb25zLiBUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSBiYXNlIGByb3dTdW1zYCBmdW5jdGlvbi4KCldlIHdpbGwgdGhlbiB1c2UgdGhlIGBjYXNlX3doZW4oKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZSB0byBjb252ZXJ0IHRoZSBzdW0gdmFsdWVzIHRvIGBUUlVFYCBvciBgRkFMU0VgIGJhc2VkIG9uIHRoZSB0aHJlc2hvbGQgb2YgemVyby4gSWYgdGhlIHN1bSBpcyBncmVhdGVyIHRoYW4gemVybywgdGhlbiB3ZSBrbm93IHRoZSBzdHVkZW50IGFuc3dlcmVkIHllcyB0byBhdCBsZWFzdCBvbmUgcXVlc3Rpb24uIAoKCmBgYHtyfQpueXRzX2RhdGEgJTw+JQogIG11dGF0ZSh0b2JhY2NvX3N1bV9ldmVyID0gcm93U3VtcyhzZWxlY3QoLiwgc3RhcnRzX3dpdGgoIkUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBGQUxTRSkpLCBuYS5ybSA9IFRSVUUpLAogICAgICB0b2JhY2NvX3N1bV9jdXJyZW50ID0gcm93U3VtcyhzZWxlY3QoLiwgc3RhcnRzX3dpdGgoIkMiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWdub3JlLmNhc2UgPSBGQUxTRSkpLCBuYS5ybSA9IFRSVUUpKSAlPiUKICAgICAgbXV0YXRlKHRvYmFjY29fZXZlciA9IGNhc2Vfd2hlbih0b2JhY2NvX3N1bV9ldmVyID4gMCB+IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9iYWNjb19zdW1fZXZlciA9PSAwIH4gRkFMU0UpLAogICAgICAgICAgdG9iYWNjb19jdXJyZW50ID0gY2FzZV93aGVuKHRvYmFjY29fc3VtX2N1cnJlbnQgPiAwIH4gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b2JhY2NvX3N1bV9jdXJyZW50ID09IDAgfiBGQUxTRSkpCmBgYAogCiAKIyMjIyB7LnNjcm9sbGFibGV9CmBgYHtyfQpnbGltcHNlKG55dHNfZGF0YSkKYGBgCiMjIyMKCldlIGFyZSBhbHNvIGludGVyZXN0ZWQgaW4gZS1jaWdhcmV0dGUvdmFwaW5nIHByb2R1Y3QgdXNhZ2UgY29tcGFyZWQgdG8gb3RoZXIgdG9iYWNjbyBwcm9kdWN0cywgc28gd2Ugd2lsbCBjcmVhdGUgc29tZSB2YXJpYWJsZXMgcmVsYXRlZCB0byB0aGUgc3VtIG9mIGFsbCBlLWNpZ2FyZXR0ZSB1c2FnZSBxdWVzdGlvbiB2YXJpYWJsZXMgYW5kIHRoZSBzdW0gb2YgYWxsIHRvYmFjY28gdXNhZ2UgcXVlc3Rpb24gdmFyaWFibGVzIGV4Y2x1ZGluZyB0aG9zZSB0aGF0IGFyZSBhYm91dCBlLWNpZ2FyZXR0ZXMuIFRoZXJlIGlzIG9ubHkgb25lIHZhcmlhYmxlIGFib3V0IGUtY2lnYXJldHRlIHVzYWdlIGV2ZXIgKEVFTENJR1QpIGFuZCBvbmUgYWJvdXQgY3VycmVudCB1c2FnZSAoQ0VMQ0lHVCkuCgoKYGBge3J9Cm55dHNfZGF0YSA8LSBueXRzX2RhdGEgJT4lIAogIG11dGF0ZShlY2lnX3N1bV9ldmVyID0gcm93U3VtcyhzZWxlY3QoLiwgRUVMQ0lHVCksIG5hLnJtID0gVFJVRSksCiAgICAgIGVjaWdfc3VtX2N1cnJlbnQgPSByb3dTdW1zKHNlbGVjdCguLCBDRUxDSUdUKSwgbmEucm0gPSBUUlVFKSwKICAgICBub25fZWNpZ19zdW1fZXZlciA9IHJvd1N1bXMoc2VsZWN0KC4sIHN0YXJ0c193aXRoKCJFIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IEZBTFNFKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtRUVMQ0lHVCksIG5hLnJtID0gVFJVRSksCiAgbm9uX2VjaWdfc3VtX2N1cnJlbnQgPSByb3dTdW1zKHNlbGVjdCguLCBzdGFydHNfd2l0aCgiQyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZ25vcmUuY2FzZSA9IEZBTFNFKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAtQ0VMQ0lHVCksIG5hLnJtID0gVFJVRSkpICU+JQogICAgICBtdXRhdGUoZWNpZ19ldmVyID0gY2FzZV93aGVuKGVjaWdfc3VtX2V2ZXIgPiAwIH4gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX3N1bV9ldmVyID09IDAgfiBGQUxTRSksCiAgICAgICAgICBlY2lnX2N1cnJlbnQgPSBjYXNlX3doZW4oZWNpZ19zdW1fY3VycmVudCA+IDAgfiBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfc3VtX2N1cnJlbnQgPT0gMCB+IEZBTFNFKSwKICAgICAgICAgbm9uX2VjaWdfZXZlciA9IGNhc2Vfd2hlbihub25fZWNpZ19zdW1fZXZlciA+IDAgfiBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX3N1bV9ldmVyID09IDAgfiBGQUxTRSksCiAgICAgIG5vbl9lY2lnX2N1cnJlbnQgPSBjYXNlX3doZW4obm9uX2VjaWdfc3VtX2N1cnJlbnQgPiAwIH4gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19zdW1fY3VycmVudCA9PSAwIH4gRkFMU0UpKQpgYGAgICAgICAgICAgICAgICAgICAgICAgIAoKIyMjIyB7LnNjcm9sbGFibGV9CmBgYHtyfQpnbGltcHNlKG55dHNfZGF0YSkKYGBgCgojIyMjCgpGaW5hbGx5LCB3ZSBhcmUgYWxzbyBpbnRlcmVzdGVkIGluIGdyb3VwaW5nIHN0dWRlbnRzIHRoYXQgb25seSB1c2UgZS1jaWdhcmV0dGVzIGFuZCB0aG9zZSB0aGF0IG9ubHkgdXNlIG90aGVyIGZvcm1zIG9mIHRvYmFjY28uCgpSZWNhbGwgdGhhdCBjdXJyZW50IHVzZXJzIGFyZSBhIHN1YnNldCBvZiBldmVyIHVzZXJzLCB0aHVzIHN0dWRlbnRzIHdvdWxkIHR5cGljYWxseSBhbnN3ZXIgeWVzIHRvIGhhdmluZyB0cmllZCB2YXBpbmcgcHJvZHVjdHMgaWYgdGhleSBoYWQgdXNlZCB0aGVtIG9uZSBvciBtb3JlIGRheXMgaW4gdGhlIHBhc3QgMzAgZGF5cy4KCkZpcnN0IHdlIHdpbGwgbWFrZSBhIHNtYWxsIHRveSBkYXRhc2V0IGNhbGxlZCBgdGVzdGAgdG8gc2hvdyB3aGF0IHdlIHdpbGwgZG8gd2l0aCB0aGUgbGFyZ2VyIGRhdGFzZXQ6CmBgYHtyfQp0ZXN0IDwtIHRpYmJsZShlY2lnX2V2ZXIgPSBjKCJUUlVFIiwgIlRSVUUiLCAiVFJVRSIsICJUUlVFIiwgIkZBTFNFIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkFMU0UiLCAiVFJVRSIsICJGQUxTRSIsICJGQUxTRSIpLAogICAgICAgICAgICAgICBub25fZWNpZ19ldmVyID0gYygiVFJVRSIsICJGQUxTRSIsICJGQUxTRSIsICJGQUxTRSIsICJGQUxTRSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGQUxTRSIsICJUUlVFIiwgIlRSVUUiLCAiVFJVRSIpLAogICAgICAgICAgICAgICBlY2lnX2N1cnJlbnQgPSBjKCJUUlVFIiwgIkZBTFNFIiwgIkZBTFNFIiwgIlRSVUUiLCAiVFJVRSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGQUxTRSIsICJGQUxTRSIsICJGQUxTRSIsICJGQUxTRSIpLAogICAgICAgICAgICAgICBub25fZWNpZ19jdXJyZW50ID0gYygiVFJVRSIsICJGQUxTRSIsICJUUlVFIiwgIkZBTFNFIiwgIlRSVUUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRkFMU0UiLCAiRkFMU0UiLCAiRkFMU0UiLCAiVFJVRSIpKQoKdGVzdApgYGAKCk5vdywgbGV0J3MgbG9vayBhdCBpZGVudGlmeWluZyBzdHVkZW50cyB3aG8gaGF2ZSB0cmllZCBlLWNpZ2FyZXR0ZXMsIGJ1dCBhcmUgbm90IGN1cnJlbnQgdXNlcnMsIGFuZCB3aG8gaGF2ZSBuZXZlciB0cmllZCBvdGhlciB0b2JhY2NvIHByb2R1Y3RzIChhbmQgYXJlIHRoZXJlZm9yZSBub3QgY3VycmVudCB1c2VycykuIFdlIHdpbGwgYWdhaW4gdXNlIHRoZSBgY2FzZV93aGVuKClgIGFuZCB0aGUgYG11dGF0ZWAgZnVuY3Rpb24gdG8gY3JlYXRlIG5ldyB2YXJpYWJsZXMgd2l0aCBzcGVjaWZpYyB2YWx1ZXMgd2hlbiBjZXJ0YWluIGNvbmRpdGlvbnMgYXJlIG1ldC4gSW4gdGhpcyBjYXNlLCB3ZSB3aWxsIHNwZWNpZnkgdGhhdCBzZXZlcmFsIGNvbmRpdGlvbnMgbXVzdCBiZSBtZXQgYnkgdXNpbmcgdGhlIGAmYCBzeW1ib2wuIEZvciBhIHZhbHVlIG9mIGBUUlVFYCBmb3IgdGhlIG5ldyBgZWNpZ19vbmx5X2V2ZXJgIHZhcmlhYmxlLCBhbGwgb2YgdGhlIGNvbmRpdGlvbnMgY29tYmluZWQgd2l0aCBgJmAgbXVzdCBiZSBtZXQuICBJZiBhbnkgb2YgdGhlIGNvbmRpdGlvbnMgYXJlIG5vdCBtZXQgdGhlbiB0aGUgYGVjaWdfb25seV9ldmVyYCB2YWx1ZSB3aWxsIGJlIGBGQUxTRWAgYmFzZWQgb24gdGhlIGxhc3QgbGluZSBgVFJVRSB+IEZBTFNFYC4KCmBgYHtyfQoKdGVzdCA8LSB0ZXN0ICU+JSBtdXRhdGUoZWNpZ19vbmx5X2V2ZXIgPSBjYXNlX3doZW4oZWNpZ19ldmVyID09IFRSVUUgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX2V2ZXIgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX2N1cnJlbnQgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX2N1cnJlbnQgPT0gRkFMU0UgfiBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gRkFMU0UpKQp0ZXN0CmBgYAoKV2UgY2FuIHNlZSBmcm9tIHRoZSBzZWNvbmQgcm93LCB0aGF0IHRoZSBgZWNpZ19vbmx5X2V2ZXJgIGlzIGBUUlVFYCB3aGVuIHdlIHdvdWxkIGV4cGVjdCBpdCB0byBiZS4KV2UgY2FuIGFsc28gc2VlIGZyb20gdGhlIGZvdXJ0aCByb3csIHRoYXQgZXZlbiB0aG91Z2ggdGhlIHN0dWRlbnQgcmVwb3J0ZWQgeWVzIHRvIGV2ZXIgdHJ5aW5nIGUtY2lnYXJldHRlcywgYmVjYXVzZSB0aGV5IGFsc28gcmVwb3J0ZWQgeWVzIHRvIGN1cnJlbnRseSB1c2luZyBlLWNpZ2FyZXR0ZXMgdGhlIHZhbHVlIGZvciBvbmx5IGV2ZXIgdHJ5aW5nIGUtY2lnYXJldHRlcyBpcyBgRkFMU0VgLiBBZGRpdGlvbmFsbHkgd2UgY2FuIHNlZSBmcm9tIHRoZSBzZXZlbnRoIHJvdyB0aGF0IHNpbWlsYXJseSBldmVuIHRob3VnaCB0aGUgc3R1ZGVudCByZXBvcnRlZCB5ZXMgdG8gZXZlciB0cnlpbmcgZS1jaWdhcmV0dGVzLCB0aGV5IGFsc28gcmVwb3J0ZWQgeWVzIHRvIGV2ZXIgdHJ5aW5nIG90aGVyIHByb2R1Y3RzLCBhbmQgdGhlIHZhbHVlIGZvciBvbmx5IGV2ZXIgdHJ5aW5nIGUtY2lnYXJldHRlcyBpcyBgRkFMU0VgLiBJbXBvcnRhbnRseSwgd2UgY2FuIHNlZSBmcm9tIHRoZSA2dGggcm93LCB0aGF0IGlmIGFsbCByZXNwb25zZXMgYXJlIG5lZ2F0aXZlIHRoYW4gdGhlIHZhbHVlIGlzIGBGQUxTRWAuCgpOb3cgd2Ugd2lsbCBleHBhbmQgdGhpcyB0byB0aGUgb3RoZXIgcG9zc2libGUgY2F0ZWdvcmllcy4gSW4gdGhpcyBjYXNlIHdlIG5vdGUgdGhhdCBzaW5jZSBjdXJyZW50IHVzZXJzIGFyZSBhIHN1YnNldCBvZiBldmVyIHVzZXJzLCBpdCBkb2Vzbid0IG1hdHRlciBpZiBhIHVzZXIgcmVwb3J0cyB5ZXMgdG8gZXZlciB0cnlpbmcgIGUtY2lnYXJldHRlcywgdGhleSBjYW4gc3RpbGwgYmUgYSBjdXJyZW50IHVzZXIuCgoKYGBge3J9CnRlc3QgPC0gdGVzdCAlPiUKICAgICAgICAgbXV0YXRlKGVjaWdfb25seV9ldmVyID0gY2FzZV93aGVuKGVjaWdfZXZlciA9PSBUUlVFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfZXZlciA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX2N1cnJlbnQgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19jdXJyZW50ID09IEZBTFNFIH4gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gRkFMU0UpLAogICAgICAgICAgZWNpZ19vbmx5X2N1cnJlbnQgPSBjYXNlX3doZW4oZWNpZ19jdXJyZW50ID09IFRSVUUgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19ldmVyID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfY3VycmVudCA9PSBGQUxTRSB+IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IEZBTFNFKSwKICAgICAgICBub25fZWNpZ19vbmx5X2V2ZXIgPSBjYXNlX3doZW4obm9uX2VjaWdfZXZlciA9PSBUUlVFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfZXZlciA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX2N1cnJlbnQgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19jdXJyZW50ID09IEZBTFNFIH4gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gRkFMU0UpLAogIG5vbl9lY2lnX29ubHlfY3VycmVudCA9IGNhc2Vfd2hlbihub25fZWNpZ19jdXJyZW50ID09IFRSVUUgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWNpZ19ldmVyID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfY3VycmVudCA9PSBGQUxTRSB+IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IEZBTFNFKSwKICAgICAgICAgICAgICAgICAgICBub191c2UgPSBjYXNlX3doZW4obm9uX2VjaWdfZXZlciA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX2V2ZXIgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWNpZ19jdXJyZW50ID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfY3VycmVudCA9PSBGQUxTRSB+IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IEZBTFNFKSkKZ2xpbXBzZSh0ZXN0KQpgYGAKCgoKVGFrZSBhIG1pbnV0ZSB0byBjaGVjayB0aGF0IHRoZSB2YWx1ZXMgYXJlIHdoYXQgd2Ugd291bGQgZXhwZWN0LgoKT0ssIG5vdyB3ZSBhcmUgZ29pbmcgdG8gbWFrZSBhIGBHcm91cGAgdmFyaWFibGUgYmFzZWQgb24gdGhlIG5ldyB2YXJpYWJsZXMgd2UganVzdCBtYWRlIHRvIGNsYXNzaWZ5IHN0dWRlbnRzIGludG8gb25lIG9mIGZvdXIgbXV0dWFsbHkgZXhjbHVzaXZlIGFuZCBleGhhdXN0aXZlIGNhdGVnb3JpZXMuIEluIHRoaXMgY2FzZSB3ZSB3aWxsIGhhdmUgYSBwYXJ0aWN1bGFyIHZhbHVlIGJhc2VkIG9uIG9uZSBjb25kaXRpb24gKipvcioqIGFub3RoZXIuIFRoaXMgKipvcioqIGNvbmRpdGlvbmFsIGlzIHNwZWNpZmllZCBieSB0aGUgYHxgIHN5bWJvbC4gT25seSBvbmUgb2YgdGhlIGNvbmRpdGlvbnMgbmVlZHMgdG8gZXhpc3QgZm9yIHRoYXQgcGFydGljdWxhciB2YWx1ZSwgd2hlcmVhcyB3aGVuIHdlIHVzZWQgdGhlIGAmYCBzeW1ib2wsIGFsbCBvZiB0aGUgY29uZGl0aW9ucyBoYWQgdG8gYmUgbWV0LiAKCklmIGEgc3R1ZGVudCBoYXMgZXZlciB0cmllZCBvciBjdXJyZW50bHkgdXNlcyBlLWNpZ2FyZXR0ZXMsIGJ1dCBoYXMgbmV2ZXIgdHJpZWQgb3RoZXIgdG9iYWNjbyBwcm9kdWN0cywgdGhlIHZhbHVlIHdpbGwgYmUgYE9ubHkgZS1jaWdhcmV0dGVzYC4gSWYgYSBzdHVkZW50IGhhcyBldmVyIHRyaWVkIG9yIGlzIGEgY3VycmVudCB1c2VyIG9mIG90aGVyIHRvYmFjY28gcHJvZHVjdHMsIGJ1dCBoYXMgbmV2ZXIgdHJpZWQgZS1jaWdhcmV0dGVzLCB0aGUgdmFsdWUgd2lsbCBiZSBgT25seSBvdGhlciBwcm9kdWN0c2AuIElmIHRoZSB2YWx1ZSBvZiB0aGUgYG5vX3VzZWAgdmFyaWFibGUgaXMgc2ltcGx5IGBUUlVFYCwgdGhlbiB0aGUgYEdyb3VwYCB2YXJpYWJsZSB2YWx1ZSB3aWxsIGJlIGBOZWl0aGVyYC4gRmluYWxseSwgaWYgYSBzdHVkZW50IGhhcyB0cmllZCBvciBjdXJyZW50bHkgdXNlcyBib3RoIGUtY2lnYXJldHRlcyBhbmQgb3RoZXIgdG9iYWNjbyBwcm9kdWN0cyB0aGUgYEdyb3VwYCB2YXJpYWJsZSB2YWx1ZSB3aWxsIGJlIGBDb21iaW5hdGlvbiBvZiBwcm9kdWN0c2AuIFRodXMgaW4gdGhpcyBjYXNlIHRoZSB2YWx1ZXMgZm9yIHRoZSB1c2FnZSBvZiB0aGUgdmFyaWFibGVzIGJhc2VkIG9uICoqb25seSoqIHVzaW5nIGUtY2lnYXJldHRlcyBvciAqKm9ubHkqKiBvdGhlciBwcm9kdWN0cyB3aWxsIGFsbCBiZSBgRkFMU0VgLiAKCmBgYHtSfQoKdGVzdCA8LSB0ZXN0ICU+JQogIG11dGF0ZShHcm91cCA9IGNhc2Vfd2hlbihlY2lnX29ubHlfZXZlciA9PSBUUlVFIHwKICAgICAgICAgICAgICAgICAgICAgICAgZWNpZ19vbmx5X2N1cnJlbnQgPT0gVFJVRSB+ICJPbmx5IGUtY2lnYXJldHRlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfb25seV9ldmVyID09IFRSVUUgfAogICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX29ubHlfY3VycmVudCA9PSBUUlVFIH4gIk9ubHkgb3RoZXIgcHJvZHVjdHMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vX3VzZSA9PSBUUlVFIH4gIk5laXRoZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX29ubHlfZXZlciA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfb25seV9jdXJyZW50ID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19vbmx5X2V2ZXIgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX29ubHlfY3VycmVudCA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9fdXNlID09IEZBTFNFIH4gIkNvbWJpbmF0aW9uIG9mIHByb2R1Y3RzIikpCgoKdGVzdCAlPiUgY291bnQoR3JvdXApCgpnbGltcHNlKHRlc3QpCmBgYAoKT0ssIG5vdyB0aGF0IHdlIGhhdmUgc2VlbiBob3cgdGhpcyB3b3JrcyB3aXRoIG91ciB0b3kgZGF0YXNldCwgd2Ugd2lsbCBhcHBseSBvdXIgY29kZSB0byBvdXIgYG55dHNfZGF0YWAuCgpgYGB7cn0Kbnl0c19kYXRhICU8PiUKICAgICAgICAgICAgIG11dGF0ZShlY2lnX29ubHlfZXZlciA9IGNhc2Vfd2hlbihlY2lnX2V2ZXIgPT0gVFJVRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19ldmVyID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX2N1cnJlbnQgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfY3VycmVudCA9PSBGQUxTRSB+IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gRkFMU0UpLAogICAgICAgICAgICAgIGVjaWdfb25seV9jdXJyZW50ID0gY2FzZV93aGVuKGVjaWdfY3VycmVudCA9PSBUUlVFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX2V2ZXIgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfY3VycmVudCA9PSBGQUxTRSB+IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBGQUxTRSksCiAgICAgICAgICAgIG5vbl9lY2lnX29ubHlfZXZlciA9IGNhc2Vfd2hlbihub25fZWNpZ19ldmVyID09IFRSVUUgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfZXZlciA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWNpZ19jdXJyZW50ID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX2N1cnJlbnQgPT0gRkFMU0UgfiBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gRkFMU0UpLAogICAgICBub25fZWNpZ19vbmx5X2N1cnJlbnQgPSBjYXNlX3doZW4obm9uX2VjaWdfY3VycmVudCA9PSBUUlVFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX2V2ZXIgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfY3VycmVudCA9PSBGQUxTRSB+IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiBGQUxTRSksCiAgICAgICAgICAgICAgICAgICAgICAgIG5vX3VzZSA9IGNhc2Vfd2hlbihub25fZWNpZ19ldmVyID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX2V2ZXIgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfY3VycmVudCA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19jdXJyZW50ID09IEZBTFNFIH4gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+IEZBTFNFKSkgJT4lCiAgICAgICAgICAgICAgICAgbXV0YXRlKEdyb3VwID0gY2FzZV93aGVuKGVjaWdfb25seV9ldmVyID09IFRSVUUgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBlY2lnX29ubHlfY3VycmVudCA9PSBUUlVFIH4gIk9ubHkgZS1jaWdhcmV0dGVzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub25fZWNpZ19vbmx5X2V2ZXIgPT0gVFJVRSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfb25seV9jdXJyZW50ID09IFRSVUUgfiAiT25seSBvdGhlciBwcm9kdWN0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9fdXNlID09IFRSVUUgfiAiTmVpdGhlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVjaWdfb25seV9ldmVyID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZWNpZ19vbmx5X2N1cnJlbnQgPT0gRkFMU0UgJgogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vbl9lY2lnX29ubHlfZXZlciA9PSBGQUxTRSAmCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbm9uX2VjaWdfb25seV9jdXJyZW50ID09IEZBTFNFICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBub191c2UgPT0gRkFMU0UgfiAiQ29tYmluYXRpb24gb2YgcHJvZHVjdHMiKSkKYGBgCgoKTGFzdGx5LCBpdCBjYW4gYmUgdmVyeSBoZWxwZnVsIHRvIGhhdmUgdGhlIHRvdGFsIG51bWJlciBvZiBzdHVkZW50cyBzdXJ2ZXllZCBlYWNoIHllYXIuIFdlIGNhbiBlYXNpbHkgYWRkIGEgdmFyaWFibGUgZm9yIHRoaXMgYnkgdXNpbmcgdGhlIGBhZGRfY291bnQoKWAgZnVuY3Rpb24gb2YgdGhlIGBkcGx5cmAgcGFja2FnZS4gVGhpcyB3aWxsIGNyZWF0ZSBhIHZhcmlhYmxlIGNhbGxlZCBgbmAgd2hpY2ggd2lsbCBzaG93IHRoZSB0b3RhbCBudW1iZXIgb2Ygc3VydmV5IHJlc3BvbnNlcyBmb3IgdGhhdCB5ZWFyLgoKYGBge3J9Cm55dHNfZGF0YSAlPD4lIGRwbHlyOjphZGRfY291bnQoeWVhcikKYGBgCgojIyMjIHsuc2Nyb2xsYWJsZX0KYGBge3J9CmdsaW1wc2Uobnl0c19kYXRhKQpgYGAKCk5vdyBsZXQncyBzYXZlIG91ciB3cmFuZ2xlZCBkYXRhIGZvciBmdXR1cmUgdXNlLiBXZSB3aWxsIHNhdmUgaXQgYXMgYW4gciBjb21wYXRpYmxlIGZpbGUgKGEgLnJkYSBmaWxlKSwgYXMgd2VsbCBhcyBjc3YgZmlsZXMsIGFzIHRoZXNlIGFyZSB1c2VmdWwgdG8gZ2l2ZSB0byBjb2xsYWJvcmF0b3JzLiBXZSB3aWxsIHVzZSB0aGUgYHdyaXRlX2NzdigpYCBmdW5jdGlvbiBvZiB0aGUgYHJlYWRyYCBwYWNrYWdlIHRvIGRvIHRoaXMuCgpgYGB7cn0Kc2F2ZShueXRzX2RhdGEsIGZpbGUgPSBoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSIpKQpyZWFkcjo6d3JpdGVfY3N2KG55dHNfZGF0YSwgCiAgICAgICAgICAgICAgICAgcGF0aCA9IGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAibnl0c19kYXRhX2Zvcl9wbG90cy5jc3YiKSkKYGBgCgojIyMgKipRdWVzdGlvbiAxKioKKioqCgoqKioKPGRldGFpbHM+IDxzdW1tYXJ5PiBDbGljayBoZXJlIGlmIHlvdSBza2lwcGVkIHRoZSBwcmV2aW91cyBzZWN0aW9ucyBhbmQgd2FudCB0byBzdGFydCBoZXJlLiA8L3N1bW1hcnk+CgpGaXJzdCB5b3UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgT0NTZGF0YWAgcGFja2FnZToKCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikKbGlicmFyeShPQ1NkYXRhKQpgYGAKClRoZW4sIHlvdSBtYXkgbG9hZCB0aGUgd3JhbmdsZWQgZGF0YSBmb3IgcGxvdHMgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOgoKYGBge3IsIGV2YWw9RkFMU0V9CndyYW5nbGVkX3JkYSgib2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5Iiwgb3V0cGF0aCA9IGdldHdkKCkpCmxvYWQoaGVyZTo6aGVyZSgiT0NTZGF0YSIsICJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSIpKQpgYGAKCklmIHRoZSBwYWNrYWdlIGRvZXMgbm90IHdvcmsgZm9yIHlvdSwgYWx0ZXJuYXRpdmVseSwgYW4gUkRBIGZpbGUgKHN0YW5kcyBmb3IgUiBkYXRhKSBvZiB0aGUgZGF0YSBjYW4gYmUgZm91bmQgaW4gb3VyIFtHaXRIdWIgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tLy9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L3RyZWUvbWFzdGVyL2RhdGEvd3JhbmdsZWQpIG9yIHNsaWdodGx5IG1vcmUgZGlyZWN0bHkgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L21hc3Rlci9kYXRhL3dyYW5nbGVkL3dyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSkuIERvd25sb2FkIHRoaXMgZmlsZSBhbmQgdGhlbiBwbGFjZSBpdCBpbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3Rvcnkgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAid3JhbmdsZWQiIHdpdGhpbiBhIHN1YmRpcmVjdG9yeSBjYWxsZWQgImRhdGEiIHRvIGNvcHkgYW5kIHBhc3RlIG91ciBjb2RlLiBXZSB1c2VkIGFuIFJTdHVkaW8gcHJvamVjdCBhbmQgdGhlIFtgaGVyZWAgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBuYXZpZ2F0ZSB0byB0aGUgZmlsZSBtb3JlIGVhc2lseS4gCgpgYGB7cn0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSIpKQpgYGAKCjwvZGV0YWlscz4KCioqKgoKUmVjYWxsIHRoYXQgd2UgYXJlIGludGVyZXN0ZWQgaW4gaW52ZXN0aWdhdGluZyBob3cgdmFwaW5nIHByb2R1Y3QgdXNlIGhhcyBjb21wYXJlZCB3aXRoIG90aGVyIHRvYmFjY28gdXNlIG92ZXIgdGltZS4gVG8gYW5zd2VyIHRoaXMsIHdlIGZpcnN0IHdhbnQgdG8gZ2V0IGEgc2Vuc2Ugb2YgaG93IHRvYmFjY28gdXNlIGhhcyBjaGFuZ2VkIGluIGdlbmVyYWwgc2luY2UgMjAxNS4gCgpUbyBjcmVhdGUgYSB2aXN1YWxpemF0aW9uIG9mIGhvdyB0b2JhY2NvIHVzYWdlIGhhcyBjaGFuZ2VkIG92ZXIgdGltZSwgd2Ugd2lsbCBmaXJzdCBjb252ZXJ0IHRoZSB1c2FnZSBkYXRhIHRvIGEgcGVyY2VudCB2YWx1ZSBmb3IgZWFjaCB5ZWFyLCB0ZWxsaW5nIHVzIHdoYXQgcGVyY2VudCBvZiBzdHVkZW50IHJlc3BvbmRlbnRzIGZhbGwgaW50byBhIHBhcnRpY3VsYXIgdXNhZ2UgY2F0ZWdvcnkuIFRvIGRvIHRoaXMgd2Ugd2lsbCB1c2UgdGhlIGBncm91cF9ieSgpYCBhbmQgYHN1bW1hcml6ZSgpYCBmdW5jdGlvbnMgb2YgdGhlIGBkcGx5cmAgcGFja2FnZS4gVGhpcyB3aWxsIGNyZWF0ZSBuZXcgdmFyaWFibGVzIHdoaWNoIHdlIHdpbGwgbmFtZSBgRXZlcmAgYW5kIGBDdXJyZW50YCBiYXNlZCBvbiB0aGUgIHBlcmNlbnRhZ2VzIG9mIGBUUlVFYCB2YWx1ZXMgZm9yIGB0b2JhY2NvX2V2ZXJgIGFuZCBgdG9iYWNjb19jdXJyZW50YCBmb3IgZWFjaCB5ZWFyLiBJbiB0aGlzIGNhc2UgdGhlIGBtZWFuKClgIGZ1bmN0aW9uIGlzIHVzZWQgdG8gY2FsY3VsYXRlIHRoZSBwZXJjZW50YWdlcyBiYXNlZCBvbiBhbiBhdXRvbWF0aWMgY29udmVyc2lvbiB0aGF0IFIgZG9lcyB3aGVyZSBmb3IgVFJVRS9GQUxTRSB2YXJpYWJsZXMsIGBUUlVFYCBpcyBnaXZlbiBhIHZhbHVlIG9mIG9uZSBhbmQgYEZBTFNFYCBpcyBnaXZlbiBhIHZhbHVlIG9mIHplcm8uIFRoZSBtZWFuIG9mIGEgMC0xIGJpbmFyeSB2YXJpYWJsZSBpcyBqdXN0IHRoZSBwZXJjZW50IG9mIHRoZSB0aW1lIHRoZSB2YWx1ZSBpcyAxLiBOQSB2YWx1ZXMgZG8gbm90IGNvbnRyaWJ1dGUgdG8gdGhlIHRvdGFsIGNvdW50IHdoZW4gd2UgaW5jbHVkZSB0aGUgYXJndW1lbnQgYG5hLnJtID0gVFJVRWAgdG8gb3VyIGZ1bmN0aW9uIGNhbGwuIAoKKioqCgo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIGEgdG95IGV4YW1wbGU6PC9zdW1tYXJ5PgpgYGB7cn0KIyB0aGUgdGVzdCBkYXRhIGhhcyAzIFRSVUUgdmFsdWVzIGFuZCA3IEZBTFNFIHZhbHVlcwp0ZXN0IDwtIHRpYmJsZSgidmFyMSIgPSBjKCJUUlVFIiwgIlRSVUUiLCAiVFJVRSIsIHJlcCgiRkFMU0UiLCA3KSkpCnRlc3QgJTw+JSBtdXRhdGUodmFyMSA9IGFzLmxvZ2ljYWwodmFyMSkpCnRlc3QKCnRlc3QgJT4lIHN1bW1hcml6ZShQZXJjZW50YWdlID0gbWVhbih2YXIxKSAqIDEwMCkKCgojIHRoZSB0ZXN0IGRhdGEgaGFzIDMgVFJVRSB2YWx1ZXMsIDMgRkFMU0UgdmFsdWVzLCBhbmQgNCBOQSB2YWx1ZQp0ZXN0IDwtIHRpYmJsZSgidmFyMSIgPSBjKCJUUlVFIiwgIlRSVUUiLCAiVFJVRSIsIHJlcCgiRkFMU0UiLCAzKSwgcmVwKCJOQSIsIDQpKSkKdGVzdCAlPD4lIG11dGF0ZSh2YXIxID0gYXMubG9naWNhbCh2YXIxKSkKdGVzdAoKdGVzdCAlPiUgc3VtbWFyaXplKFBlcmNlbnRhZ2UgPSBtZWFuKHZhcjEsIG5hLnJtID0gVFJVRSkgKiAxMDApCmBgYAo8L2RldGFpbHM+CgoqKioKCkFuZCBub3cgYmFjayB0byBvdXIgZGF0YToKCmBgYHtyfQoKbnl0c19kYXRhICU+JQogIGRwbHlyOjpncm91cF9ieSh5ZWFyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKEV2ZXIgPSAobWVhbih0b2JhY2NvX2V2ZXIsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgICAgICAgICAgICAgICAgQ3VycmVudCA9IChtZWFuKHRvYmFjY29fY3VycmVudCwgbmEucm0gPSBUUlVFKSAqIDEwMCkpCmBgYAoKV2Ugd2lsbCB1c2UgdGhlIGBwaXZvdF9sb25nZXJgIGZ1bmN0aW9uIGZyb20gdGhlIGB0aWR5cmAgcGFja2FnZSB0byB0YWtlIGFsbCBjb2x1bW5zIGV4Y2VwdCB5ZWFyIChpbiB0aGlzIGNhc2UgdGhlIGBFdmVyYCBhbmQgYEN1cnJlbnRgIGNvbHVtbnMpLCB0byBjcmVhdGUgYSBjb2x1bW4gY2FsbGVkIGBVc2VyYCB0aGF0IHdpbGwgY29udGFpbiB0aGUgY3VycmVudCBjb2x1bW4gbmFtZSBpbmZvcm1hdGlvbiBhbmQgYSBjb2x1bW4gY2FsbGVkIGBQZXJjZW50YWdlIG9mIHN0dWRlbnRzYCB3aGljaCB3aWxsIGNvbnRhaW4gdGhlIG1lYW4gcGVyY2VudGFnZSB2YWx1ZXMgdGhhdCB3ZSBqdXN0IGNhbGN1bGF0ZWQuIFRoaXMgY29udmVydHMgb3VyIGRhdGEgaW50byBhIGZvcm1hdCBjYWxsZWQgImxvbmciIGZvcm1hdC4KCmBgYHtyfQoKbnl0c19kYXRhICU+JQogIGRwbHlyOjpncm91cF9ieSh5ZWFyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXplKCJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKSIgPSAobWVhbih0b2JhY2NvX2V2ZXIsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgICAgICAgICAgICAgICAgIkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpIiA9IChtZWFuKHRvYmFjY29fY3VycmVudCwgbmEucm0gPSBUUlVFKSAqIDEwMCkpICU+JQogIHRpZHlyOjpwaXZvdF9sb25nZXIoY29scyA9IC15ZWFyLAogICAgICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVXNlciIsCiAgICAgICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpCmBgYApZb3UgbWF5IGhhdmUgbm90aWNlZCB0aGF0IG91ciBkYXRhIGlzIGxvbmdlciB0aGFuIGl0IHVzZWQgdG8gYmUhIEhlbmNlIHRoZSBuYW1lIG9mIHRoZSBmdW5jdGlvbiBgcGl2b3RfbG9uZ2VyKClgLiBEYXRhIGlzIG9mdGVuIGVhc2llciB0byBwbG90IHdoZW4gaXQgaXMgaW4gdGhpcyBmb3JtYXQuCiAgCk5vdyB3ZSB3aWxsIHVzZSB0aGlzIGRhdGEgdG8gY3JlYXRlIGEgcGxvdCB1c2luZyB0aGUgYGdncGxvdDJgIHBhY2thZ2UuIAoKVGhlIGZpcnN0IHRoaW5nIHdlIGRvIHRvIGNyZWF0ZSBhIHBsb3QgaXMgc3BlY2lmeSB3aGF0IGRhdGEgd2UgYXJlIHVzaW5nIGZvciBvdXIgeCBheGlzIGFuZCB5IGF4aXMgd2l0aCB0aGVgYWVzKClgIGFyZ3VtZW50IG9mIHRoZSBgZ2dwbG90KClgIGZ1bmN0aW9uLiBUaGVuIHdlIGFkZCBsYXllcnMgdG8gb3VyIHBsb3QgdGhhdCBzcGVjaWZ5IHdoYXQgdHlwZSBvZiBwbG90IHdlIHdvdWxkIGxpa2UgdG8gY3JlYXRlLiBXZSBjYW4gdXNlIHRoZSBgZ2VvbV9saW5lKClgIGZ1bmN0aW9uIHRvIGNyZWF0ZSBsaW5lcyBmb3IgZWFjaCB0eXBlIG9mIHVzZXIuIFdlIHNwZWNpZnkgdGhhdCB3ZSB3YW50IHRvIHVzZSBkaWZmZXJlbnQgbGluZSB0eXBlcyBmb3IgZWFjaCB1c2VyIHVzaW5nIGBhZXMoKWAuIFdlIHdpbGwgYWxzbyBhZGQgcG9pbnRzIHRvIG91ciBsaW5lcyB1c2luZyB0aGUgYGdlb21fcG9pbnQoKWAgZnVuY3Rpb24uIFdlIGNhbiBhZGQgYWRkaXRpb25hbCBsYXllcnMgdG8gc3BlY2lmeSBjb2xvcnMgYW5kIGRldGFpbHMgYWJvdXQgbGFiZWxzIGFuZCBsZWdlbmRzIGV0Yy4KICAKYGBge3J9ICAKCnBsb3QxIDwtIG55dHNfZGF0YSAlPiUKICAgIGRwbHlyOjpncm91cF9ieSh5ZWFyKSAlPiUKICAgIGRwbHlyOjpzdW1tYXJpemUoIkV2ZXIgXG4gKGFueSBsaWZldGltZSB1c2UpIiA9IChtZWFuKHRvYmFjY29fZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKSIgPSAobWVhbih0b2JhY2NvX2N1cnJlbnQsIG5hLnJtID0gVFJVRSkgKiAxMDApKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC15ZWFyLCBuYW1lc190byA9ICJVc2VyIiwgdmFsdWVzX3RvID0gIlBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgKSkgKwogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBVc2VyKSkgKwogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSwgc2l6ZSA9IDIpICsKICAjIHRoaXMgYWxsb3dzIHVzIHRvIGNob29zZSB3aGF0IHR5cGUgb2YgbGluZSB3ZSB3YW50IGZvciBlYWNoIGxpbmUKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygxLCAyKSwgCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IGMoIkV2ZXIgXG4gKGFueSBsaWZldGltZSB1c2UpIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpIikpICsKICAjIHRoaXMgYWxsb3dzIHVzIHRvIHNwZWNpZnkgaG93IHRoZSB5LWF4aXMgc2hvdWxkIGFwcGVhcgogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgNzAsIGJ5ID0gMTApLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoMCwgNzAsIGJ5ID0gMTApLAogICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDcwKSkgKwogICMgdGhpcyBhZGp1c3RzIHRoZSBiYWNrZ3JvdW5kIHN0eWxlIG9mIHRoZSBwbG90CiAgICB0aGVtZV9saW5lZHJhdygpICsKICAjIHRoaXMgbW92ZXMgdGhlIGxlZ2VuZCB0byB0aGUgYm90dG9tIG9mIHRoZSBwbG90IGFuZCByZW1vdmVzIHRoZSB4IGF4aXMgdGl0bGUKICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgICBsYWJzKHRpdGxlID0gIkhvdyBoYXMgdG9iYWNjbyB1c2UgdmFyaWVkIG92ZXIgdGhlIHllYXJzPyIsCiAgICAgICAgIHkgPSAiJSBvZiBzdHVkZW50cyIpCgpwbG90MSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkKYGBgCgpOaWNlISBOb3cgd2UgY2FuIHNlZSBob3cgb3ZlcmFsbCB0b2JhY2NvIHVzYWdlIGhhcyBjaGFuZ2VkIHNpbmNlIDIwMTcuIEl0IGFwcGVhcnMgdGhhdCB1c2FnZSBmaXJzdCBkZWNyZWFzZWQgZnJvbSAyMDE1IHRvIDIwMTcgYW5kIHRoZW4gaW5jcmVhc2VkIGEgYml0IHNpbmNlIDIwMTcsIHN1cnBhc3NpbmcgdGhlIGxldmVscyBpbiAyMDE1LgoKV2hhdCBhYm91dCBlLWNpZ2FyZXR0ZSB1c2U/IEhvdyBoYXMgdGhlIHVzYWdlIG9mIGUtY2lnYXJldHRlcyBjaGFuZ2VkIG92ZXIgdGltZT8KCmBgYHtyfQpwbG90MWEgPC0gbnl0c19kYXRhICU+JQogICAgZHBseXI6Omdyb3VwX2J5KHllYXIpICU+JQogICAgZHBseXI6OnN1bW1hcml6ZSgiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSkiID0gKG1lYW4oZWNpZ19ldmVyLCBuYS5ybSA9IFRSVUUpICogMTAwKSwKICAgICAgICAgICAgICAgICAgICAgIkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpIiA9IChtZWFuKGVjaWdfY3VycmVudCwgbmEucm0gPSBUUlVFKSAqIDEwMCkpICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLXllYXIsIG5hbWVzX3RvID0gIlVzZXIiLCB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBgUGVyY2VudGFnZSBvZiBzdHVkZW50c2ApKSArCiAgZ2VvbV9saW5lKGFlcyhsaW5ldHlwZSA9IFVzZXIpKSArCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFLCBzaXplID0gMikgKwogICMgdGhpcyBhbGxvd3MgdXMgdG8gY2hvb3NlIHdoYXQgdHlwZSBvZiBsaW5lIHdlIHdhbnQgZm9yIGVhY2ggbGluZQogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKDEsIDIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3VycmVudCBcbiAoYW55IHBhc3QtMzAtZGF5IHVzZSkiKSkgKwogICMgdGhpcyBhbGxvd3MgdXMgdG8gc3BlY2lmeSBob3cgdGhlIHktYXhpcyBzaG91bGQgYXBwZWFyCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA2MCwgYnkgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcSgwLCA2MCwgYnkgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgNjApKSArCiAgIyB0aGlzIGFkanVzdHMgdGhlIGJhY2tncm91bmQgc3R5bGUgb2YgdGhlIHBsb3QKICAgIHRoZW1lX2xpbmVkcmF3KCkgKwogICMgdGhpcyBtb3ZlcyB0aGUgbGVnZW5kIHRvIHRoZSBib3R0b20gb2YgdGhlIHBsb3QgYW5kIHJlbW92ZXMgdGhlIHggYXhpcyB0aXRsZQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGxhYnModGl0bGUgPSAiSG93IGhhcyBlLWNpZ2FyZXR0ZSB1c2UgdmFyaWVkIG92ZXIgdGhlIHllYXJzPyIsCiAgICAgICAgIHkgPSAiJSBvZiBzdHVkZW50cyIpCgpwbG90MWEgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYApJdCBsb29rcyBsaWtlIHRoZSBzaGFwZSBvZiB0aGUgcGxvdCBpcyB2ZXJ5IHNpbWlsYXIgdG8gdG9iYWNjbyB1c2FnZSBvdmVyYWxsLiBXZSBzZWUgYSBkb3dud2FyZCB0cmVuZCB1bnRpbCAyMDE3IHdoZW4gdGhlIHJhdGUgb2YgYm90aCBjdXJyZW50IGFuZCBldmVyIHVzZXJzIGluY3JlYXNlZC4gUmVjYWxsIHRoYXQgdGhpcyBpcyBpbiBhZ3JlZW1lbnQgd2l0aCB0aGUgYXJ0aWNsZXMgdGhhdCB3ZSByZWZlcmVuY2VkIGVhcmxpZXIuIFdlIGNhbiBzZWUgdGhhdCB0aGUgc2xvcGUgbG9va3Mgc3RlZXBlciBmb3IgZS1jaWdhcmV0dGUgdXNhZ2UgYXMgY29tcGFyZWQgdG8gYWxsIHRvYmFjY28gcHJvZHVjdHMgKGluY2x1ZGluZyBlLWNpZ2FyZXR0ZXMpLgoKCk5vdyBsZXQncyBwbG90IHRoaXMgZGF0YSB0b2dldGhlciBvbiB0aGUgc2FtZSBwbG90LgoKV2Ugd2lsbCBoYXZlIGZvdXIgZ3JvdXBzIChlLWNpZ2FyZXR0ZSBldmVyIHVzZXJzLCBlLWNpZ2FyZXR0ZSBjdXJyZW50IHVzZXJzLCB0b2JhY2NvIGV2ZXIgdXNlcnMsIGFuZCB0b2JhY2NvIGN1cnJlbnQgdXNlcnMpIHRvIHBsb3QsIHRoZXJlZm9yZSwgaXQgd291bGQgYmUgdXNlZnVsIHRvIGFkZCBjb2xvciB0byBvdXIgcGxvdC4gS2VlcCBpbiBtaW5kIHRoYXQgZS1jaWdhcmV0dGUgdXNlcnMgYXJlIGEgc3Vic2V0IG9mIGFueSB0b2JhY2NvIHByb2R1Y3QgdXNlcnMuCgpPbmUgaW1wb3J0YW50IHRoaW5nIHRvIGtlZXAgaW4gbWluZCB3aGVuIGNyZWF0aW5nIHBsb3RzIGlzIHRoYXQgaW5kaXZpZHVhbHMgd2l0aCBjb2xvciBibGluZG5lc3MgbWF5IGhhdmUgYSBkaWZmaWN1bHQgdGltZSBkaXN0aW5ndWlzaGluZyBncm91cHMgd2hlbiBjZXJ0YWluIGNvbG9yIGNob2ljZXMgYXJlIHVzZWQuIAoKT25lIGdyZWF0IG9wdGlvbiBpcyB0byB1c2UgdGhlIGB2aXJpZGlzYCBwYWNrYWdlLCB3aGljaCBvZmZlcnMgY29sb3IgcGFsZXR0ZXMgd2l0aCBjb2xvcnMgdGhhdCBhcmUgc3RpbGwgZGlzdGluZ3Vpc2hhYmxlIGJ5IGluZGl2aWR1YWxzIHdpdGggbW9zdCBmb3JtcyBvZiBjb2xvciBibGluZG5lc3MuIAoKV2UgY2FuIGNob29zZSB3aGljaCBjb2xvcnMgd2Ugd2FudCB0byB1c2UgYnkgdXNpbmcgdGhlIGBzaG93X2NvbCgpYCBmdW5jdGlvbiBvZiB0aGUgYHNjYWxlc2AgcGFja2FnZS4KCkhlcmUgYXJlIHNvbWUgY29sb3Igb3B0aW9uczoKYGBge3J9CgpzY2FsZXM6OnNob3dfY29sKHZpcmlkaXNfcGFsKCkoNikpCnZfY29sb3JzID0gIHZpcmlkaXMoNilbYygxLCA0KV0KYGBgCgpXZSB3aWxsIHNlbGVjdCB0aGUgZmlyc3QgYW5kIGZvdXJ0aCBjb2xvcnMgZm9yIG91ciBwbG90LiBUbyBhZGQgdGhlc2Ugc3BlY2lmaWMgY29sb3JzIHdlIHdpbGwgdXNlIHRoZSBgc2NhbGVfY29sb3JfbWFudWFsKClgIGZ1bmN0aW9uIG9mIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4KCldlIHdpbGwgY2FsY3VsYXRlIHRoZSBtZWFuIGV2ZXIgYW5kIGN1cnJlbnQgdXNhZ2UgcGVyY2VudGFnZXMgZm9yIHN0dWRlbnRzIHdobyB1c2VkIGUtY2lnYXJldHRlcyBvciBhbnkgdG9iYWNjbyBwcm9kdWN0cyAoaW5jbHVkaW5nIGUtY2lnYXJldHRlcykgZm9yIGVhY2ggeWVhciBhZ2FpbiB1c2luZyB0aGUgYGdyb3VwX2J5KClgIGFuZCBgc3VtbWFyaXplKClgIGZ1bmN0aW9ucy4gV2Ugd2lsbCBhZ2FpbiB1c2UgdGhlIGBwaXZvdF9sb25nZXJgIGZ1bmN0aW9uIHRvIGNvbnZlcnQgb3VyIGRhdGEgdG8gbG9uZyBmb3JtYXQuIFdlIHdpbGwgYWxzbyB1c2UgdGhlIGBzZXBhcmF0ZSgpYCBmdW5jdGlvbiBvZiB0aGUgYHRpZHlyYCBwYWNrYWdlIHRvIGNyZWF0ZSB0d28gdmFyaWFibGVzIGZyb20gb25lIG9mIHRoZSB2YXJpYWJsZXMuIFRoaXMgaXMgZG9uZSBieSBzZXBhcmF0aW5nIGJ5LCBpbiB0aGlzIGNhc2UsIGFuIHVuZGVyc2NvcmUuIAoKCmBgYHtyfQpueXRzX2RhdGEgJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKCJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKV9BbnkgVG9iYWNjbyBQcm9kdWN0IFxuIChpbmNsdWRpbmcgZS1jaWdhcmV0dGVzKSIgPSAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbih0b2JhY2NvX2V2ZXIsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgICAgICAgICAgICAgICAgICAiQ3VycmVudCBcbiAoYW55IHBhc3QtMzAtZGF5IHVzZSlfQW55IFRvYmFjY28gUHJvZHVjdCBcbiAoaW5jbHVkaW5nIGUtY2lnYXJldHRlcykiID0gICAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbih0b2JhY2NvX2N1cnJlbnQsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgICAgICAgICAgICAgICAgICAiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSlfRS1jaWdhcmV0dGVzIiA9IAogICAgICAgICAgICAgICAgICAgICAgIChtZWFuKGVjaWdfZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKV9FLWNpZ2FyZXR0ZXMiID0gCiAgICAgICAgICAgICAgICAgICAgICAgKG1lYW4oZWNpZ19jdXJyZW50LCBuYS5ybSA9IFRSVUUpICogMTAwKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAteWVhciwgCiAgICAgICAgICAgbmFtZXNfdG8gPSAiVXNlciIsIAogICAgICAgICAgdmFsdWVzX3RvID0gIlBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMiKSAlPiUKICBzZXBhcmF0ZShVc2VyLCBpbnRvID0gYygiVXNlciIsICJQcm9kdWN0IiksIHNlcCA9ICJfIikgJT4lCiAgaGVhZCgpCgoKcGxvdDF0IDwtIG55dHNfZGF0YSAlPiUKICAgIGdyb3VwX2J5KHllYXIpICU+JQogICAgc3VtbWFyaXplKCJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKV9BbnkgVG9iYWNjbyBQcm9kdWN0IFxuIChpbmNsdWRpbmcgZS1jaWdhcmV0dGVzKSIgPSAKICAgICAgICAgICAgICAgIChtZWFuKHRvYmFjY29fZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgIkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpX0FueSBUb2JhY2NvIFByb2R1Y3QgXG4gKGluY2x1ZGluZyBlLWNpZ2FyZXR0ZXMpIiA9IAogICAgICAgICAgICAgICAgKG1lYW4odG9iYWNjb19jdXJyZW50LCBuYS5ybSA9IFRSVUUpICogMTAwKSwKICAgICAgICAgICAgICAiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSlfRS1jaWdhcmV0dGVzIiA9IAogICAgICAgICAgICAgICAgKG1lYW4oZWNpZ19ldmVyLCBuYS5ybSA9IFRSVUUpICogMTAwKSwKICAgICAgICAgICAgICAiQ3VycmVudCBcbiAoYW55IHBhc3QtMzAtZGF5IHVzZSlfRS1jaWdhcmV0dGVzIiA9IAogICAgICAgICAgICAgICAgKG1lYW4oZWNpZ19jdXJyZW50LCBuYS5ybSA9IFRSVUUpICogMTAwKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAteWVhciwgCiAgICAgICAgICAgbmFtZXNfdG8gPSAiVXNlciIsIAogICAgICAgICAgdmFsdWVzX3RvID0gIlBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMiKSAlPiUKICBzZXBhcmF0ZShVc2VyLCAKICAgICAgICAgICBpbnRvID0gYygiVXNlciIsICJQcm9kdWN0IiksIAogICAgICAgICAgICBzZXAgPSAiXyIpICU+JQogICAgZ2dwbG90KGFlcyh4ID0geWVhciwgCiAgICAgICAgICAgICAgIHkgPSBgUGVyY2VudGFnZSBvZiBzdHVkZW50c2AsCiAgICAgICAgICAgY29sb3IgPSBQcm9kdWN0KSkgKwogICAgZ2VvbV9saW5lKGFlcyhsaW5ldHlwZSA9IFVzZXIpKSArCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFLCBzaXplID0gMikgKwogICMgdGhpcyBhbGxvd3MgdXMgdG8gY2hvb3NlIHdoYXQgdHlwZSBvZiBsaW5lIHdlIHdhbnQgZm9yIGVhY2ggbGluZQogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKDEsIDIpLCAKICAgICAgICAgICAgICAgICAgICAgICAgYnJlYWtzID0gYygiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ3VycmVudCBcbiAoYW55IHBhc3QtMzAtZGF5IHVzZSkiKSkgKwogICMgd2Ugd2FudCBwdXJwbGUgYXNzb2NpYXRlZCB3aXRoIGUtY2lnYXJldHRlcyB0byBiZSBjb25zaXN0ZW50IHdpdGggbGF0ZXIgcGxvdHMKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gcmV2KHZfY29sb3JzKSkgKwogICMgdGhpcyBhbGxvd3MgdXMgdG8gc3BlY2lmeSBob3cgdGhlIHktYXhpcyBzaG91bGQgYXBwZWFyCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA2MCwgYnkgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcSgwLCA2MCwgYnkgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgNjApKSArCiAgIyB0aGlzIGFkanVzdHMgdGhlIGJhY2tncm91bmQgc3R5bGUgb2YgdGhlIHBsb3QKICAgIHRoZW1lX2xpbmVkcmF3KCkgKwogICMgdGhpcyBtb3ZlcyB0aGUgbGVnZW5kIHRvIHRoZSBib3R0b20gb2YgdGhlIHBsb3QgYW5kIHJlbW92ZXMgdGhlIHggYXhpcyB0aXRsZQogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGxhYnModGl0bGUgPSAiSG93IGhhcyB0b2JhY2NvIHVzZSB2YXJpZWQgb3ZlciB0aGUgeWVhcnM/IiwKICAgICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3QxdCArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkKYGBgCgpXZSBzZWUgYW4gaW5jcmVhc2UgaW4gYWxsIGNhdGVnb3JpZXMgc3RhcnRpbmcgaW4gMjAxNywgYnV0IHRoZSByYXRlIG9mIGluY3JlYXNlIGlzIGhpZ2hlciBmb3Igc3R1ZGVudHMgdXNpbmcgb25seSBlLWNpZ2FyZXR0ZXMgKGN1cnJlbnQgb3IgZXZlciB1c2VycyksIGFzIHNob3duIGJ5IHRoZSBoaWdoZXIgc2xvcGUgb2YgdGhlIGUtY2lnYXJldHRlIGxpbmVzLgoKSW4gdGhlIGFib3ZlIHBsb3RzLCB0aGUgIkFueSB0b2JhY2NvIHByb2R1Y3QiIGdyb3VwcyBpbmNsdWRlIGluZGl2aWR1YWxzIGluIHRoZSAiRS1jaWdhcmV0dGUgb25seSIgZ3JvdXBzLiBOb3cgbGV0J3MgcGxvdCBzdHVkZW50cyBpbiB0d28gbXV0dWFsbHkgZXhjbHVzaXZlIGdyb3VwcyBvbiB0aGUgc2FtZSBwbG90OiB0aG9zZSB3aG8gcmVwb3J0ZWQgZWl0aGVyIHVzaW5nIG9ubHkgZS1jaWdhcmV0dGVzIG9yIG9ubHkgb3RoZXIgdG9iYWNjbyBwcm9kdWN0cyAoYmVzaWRlcyBlLWNpZ2FyZXR0ZXMpLCBidXQgbm90IGJvdGguIAoKV2Ugd2lsbCBjYWxjdWxhdGUgdGhlIG1lYW4gZXZlciBhbmQgY3VycmVudCB1c2FnZSBwZXJjZW50YWdlcyBmb3Igc3R1ZGVudHMgaW4gdGhlc2UgdHdvIG11dHVhbGx5IGV4Y2x1c2l2ZSBncm91cHMsIGFnYWluIHVzaW5nIHRoZSBgZ3JvdXBfYnkoKWAgZnVuY3Rpb24gYW5kIHRoZSBgc3VtbWFyaXplKClgIGZ1bmN0aW9uLiBXZSB3aWxsIGFnYWluIHVzZSB0aGUgYHBpdm90X2xvbmdlcmAgZnVuY3Rpb24gdG8gY29udmVydCBvdXIgZGF0YSB0byBsb25nIGZvcm1hdC4gV2Ugd2lsbCBhbHNvIGFnYWluIHVzZSB0aGUgYHNlcGFyYXRlKClgIGZ1bmN0aW9uIG9mIHRoZSBgdGlkeXJgIHBhY2thZ2UgdG8gY3JlYXRlIHR3byB2YXJpYWJsZXMgZnJvbSBvbmUgdmFyaWFibGUuIFRoaXMgaXMgZG9uZSBieSBzZXBhcmF0aW5nIGJ5LCBpbiB0aGlzIGNhc2UsIGEgc3BhY2UuIAoKYGBge3J9CgpueXRzX2RhdGEgJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKCJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKV9FLWNpZ2FyZXR0ZSIgPSAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbihlY2lnX29ubHlfZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKV9FLWNpZ2FyZXR0ZSIgPSAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbihlY2lnX29ubHlfY3VycmVudCwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKV9Ob24tZS1jaWdhcmV0dGUiID0gCiAgICAgICAgICAgICAgICAgICAgICAgKG1lYW4obm9uX2VjaWdfb25seV9ldmVyLCBuYS5ybSA9IFRSVUUpICogMTAwKSwKICAgICAgICAgICAgICAgICAgICAgIkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpX05vbi1lLWNpZ2FyZXR0ZSIgPSAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbihub25fZWNpZ19vbmx5X2N1cnJlbnQsIG5hLnJtID0gVFJVRSkgKiAxMDApKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC15ZWFyLCAKICAgICAgICAgICBuYW1lc190byA9ICJVc2VyIiwgCiAgICAgICAgICB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpICU+JQogIHRpZHlyOjpzZXBhcmF0ZShVc2VyLCBpbnRvID0gYygiVXNlciIsICJQcm9kdWN0IiksIHNlcCA9ICJfIikgJT4lCiAgaGVhZCgpCgpwbG90MWMgPC0gbnl0c19kYXRhICU+JQogICAgZHBseXI6Omdyb3VwX2J5KHllYXIpICU+JQogICAgZHBseXI6OnN1bW1hcml6ZSgiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSlfRS1jaWdhcmV0dGUiID0gCiAgICAgICAgICAgICAgICAgICAgICAgKG1lYW4oZWNpZ19vbmx5X2V2ZXIsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgICAgICAgICAgICAgICAgICAiQ3VycmVudCBcbiAoYW55IHBhc3QtMzAtZGF5IHVzZSlfRS1jaWdhcmV0dGUiID0gCiAgICAgICAgICAgICAgICAgICAgICAgKG1lYW4oZWNpZ19vbmx5X2N1cnJlbnQsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgICAgICAgICAgICAgICAgICAiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSlfTm9uLWUtY2lnYXJldHRlIiA9IAogICAgICAgICAgICAgICAgICAgICAgIChtZWFuKG5vbl9lY2lnX29ubHlfZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKV9Ob24tZS1jaWdhcmV0dGUiID0gCiAgICAgICAgICAgICAgICAgICAgICAgKG1lYW4obm9uX2VjaWdfb25seV9jdXJyZW50LCBuYS5ybSA9IFRSVUUpICogMTAwKSkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAteWVhciwgCiAgICAgICAgICAgbmFtZXNfdG8gPSAiVXNlciIsIAogICAgICAgICAgdmFsdWVzX3RvID0gIlBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMiKSAlPiUKICBzZXBhcmF0ZShVc2VyLCBpbnRvID0gYygiVXNlciIsICJQcm9kdWN0IiksIHNlcCA9ICJfIikgJT4lCiAgICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgLCBjb2xvciA9IFByb2R1Y3QpKSArCiAgICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlID0gVXNlcikpICsKICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UsIHNpemUgPSAyKSArCiAgIyB0aGlzIGFsbG93cyB1cyB0byBjaG9vc2Ugd2hhdCB0eXBlIG9mIGxpbmUgd2Ugd2FudCBmb3IgZWFjaCBsaW5lCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMSwgMiksIAogICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKCJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKSIpKSArCiAgIyB0aGlzIGFsbG93cyB1cyB0byBzcGVjaWZ5IGhvdyB0aGUgeS1heGlzIHNob3VsZCBhcHBlYXIKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDMwLCBieSA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2VxKDAsIDMwLCBieSA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCAzMCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdl9jb2xvcnMpICsKICAjIHRoaXMgYWRqdXN0cyB0aGUgYmFja2dyb3VuZCBzdHlsZSBvZiB0aGUgcGxvdAogICAgdGhlbWVfbGluZWRyYXcoKSArCiAgIyB0aGlzIG1vdmVzIHRoZSBsZWdlbmQgdG8gdGhlIGJvdHRvbSBvZiB0aGUgcGxvdCBhbmQgcmVtb3ZlcyB0aGUgeCBheGlzIHRpdGxlCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogICAgbGFicyh0aXRsZSA9ICJIb3cgaGFzIHVzZSBvZiBvbmx5IGUtY2lnYXJldHRlcyBhbmQKb25seSB0b2JhY2NvIHByb2R1Y3RzIGJlc2lkZXMgZS1jaWdhcmV0dGVzIHZhcmllZCBvdmVyIHRpbWU/IiwKICAgICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3QxYyArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkKYGBgCgpWZXJ5IGludGVyZXN0aW5nISBXZSBjYW4gc2VlIGZyb20gdGhpcyBwbG90IHRoYXQgdGhlIHBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMgd2hvIGhhZCBjdXJyZW50bHkgdXNlZCAob3IgZXZlciB0cmllZCkgb25seSBlLWNpZ2FyZXR0ZXMgZ3JlYXRseSBpbmNyZWFzZWQgc3RhcnRpbmcgaW4gMjAxNywgd2hpbGUgaW4gY29udHJhc3QgdGhlIHBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMgd2hvIGhhZCBldmVyIHRyaWVkIG9ubHkgbm9uLWUtY2lnYXJldHRlIHRvYmFjY28gcHJvZHVjdHMgYWN0dWFsbHkgZGltaW5pc2hlZCBvdmVyIHRpbWUuIEluIGZhY3QsIHdlIGNhbiBzZWUgdGhhdCBpbiAyMDE5IHRoZSBwZXJjZW50YWdlIG9mIHN0dWRlbnRzIHdobyB3ZXJlIGN1cnJlbnQgZS1jaWdhcmV0dGUgdXNlcnMgc3VycGFzc2VkIHRoZSBwZXJjZW50YWdlIHRoYXQgaGFkIHRyaWVkIGEgbm9uLWUtY2lnYXJldHRlIHByb2R1Y3QgZXZlbiBqdXN0IG9uY2UuIAoKClJlY2FsbCB0aGF0IHdlIG1hZGUgYSB2YXJpYWJsZSBjYWxsZWQgYEdyb3VwYCB0aGF0IGlkZW50aWZpZWQgc3R1ZGVudHMgd2hvIHVzZWQgZWl0aGVyIGp1c3QgZS1jaWdhcmV0dGUgcHJvZHVjdHMsIGp1c3Qgb3RoZXIgdG9iYWNjbyBwcm9kdWN0cyAoYmVzaWRlcyBlLWNpZ2FyZXR0ZXMpLCBvciBzdHVkZW50cyB3aG8gdXNlZCBib3RoIGUtY2lnYXJldHRlcyBhbmQgc29tZSBvdGhlciB0eXBlIG9mIHRvYmFjY28gcHJvZHVjdC4KCmBgYHtyfQpueXRzX2RhdGEgJT4lCiAgY291bnQoR3JvdXApCmBgYAoKV2Ugd2lsbCBub3cgbWFrZSBhIHBsb3Qgb3ZlciB0aW1lIG9mIGVhY2ggb2YgdGhlc2UgZ3JvdXBzLiBTaW5jZSB3ZSB3aWxsIGhhdmUgNCB0b3RhbCBncm91cHMsIHdlIHdpbGwgdXNlIDQgb2YgdGhlIHZpcmlkaXMgY29sb3JzLgpOb3RpY2UsIHRoYXQgaW4gdGhpcyBjYXNlIHdlIGFyZSBncm91cGluZyBieSB0aHJlZSB2YXJpYWJsZXMgYnkgc2ltcGx5IHNlcGFyYXRpbmcgdGhlIHZhcmlhYmxlcyB0aGF0IHdlIHdhbnQgdG8gZ3JvdXAgYnkgd2l0aCBhIGNvbW1hIGluIG91ciBgZ3JvdXBfYnkoKWAgZnVuY3Rpb24gbGlrZSB0aGlzOiBgZ3JvdXBfYnkoR3JvdXAsIHllYXIsIG4pYC4gCgpgYGB7cn0KCm55dHNfZGF0YSAlPiUKICBncm91cF9ieShHcm91cCwgeWVhciwgbikgJT4lCiAgc3VtbWFyaXplKGdyb3VwX2NvdW50ID0gbigpKSAlPiUKICBtdXRhdGUoIlBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMiID0gZ3JvdXBfY291bnQgLyBuICogMTAwKSAlPiUKICBoZWFkKCkKCnZfY29sb3JzID0gIHZpcmlkaXMoNSlbMTo0XQoKbnl0c19kYXRhICU+JQogIGdyb3VwX2J5KEdyb3VwLCB5ZWFyLCBuKSAlPiUKICBzdW1tYXJpemUoZ3JvdXBfY291bnQgPSBuKCkpICU+JQogIG11dGF0ZSgiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIgPSBncm91cF9jb3VudCAvIG4gKiAxMDApICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBgUGVyY2VudGFnZSBvZiBzdHVkZW50c2AsIGNvbG9yID0gR3JvdXApKSArCiAgZ2VvbV9wb2ludChzaXplID0gMikgKwogIGdlb21fbGluZSgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwoYnJlYWtzID0gYygiTmVpdGhlciIsICJDb21iaW5hdGlvbiBvZiBwcm9kdWN0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk9ubHkgZS1jaWdhcmV0dGVzIiwgIk9ubHkgb3RoZXIgcHJvZHVjdHMiKSwKICAgICAgICAgICAgICAgICAgICAgdmFsdWVzID0gdl9jb2xvcnMpICsKICB0aGVtZV9saW5lZHJhdygpICsKICBsYWJzKHggPSAiWWVhciIpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZSBtYWpvcml0eSBvZiBzdHVkZW50cyBkaWQgbm90IHJlcG9ydCB1c2luZyBhbnkgdG9iYWNjbyBwcm9kdWN0cy4gT2YgdGhlIHN0dWRlbnRzIHRoYXQgZGlkIHJlcG9ydCB1c2luZyB0b2JhY2NvIHByb2R1Y3RzLCB0aGUgbWFqb3JpdHkgb2YgdGhlIHN0dWRlbnRzIHVzZWQgYm90aCBlLWNpZ2FyZXR0ZXMgYW5kIHNvbWUgb3RoZXIgdG9iYWNjbyBwcm9kdWN0LiBBZ2FpbiwgYSBtdWNoIGxhcmdlciBwZXJjZW50YWdlIHJlcG9ydGVkIHVzaW5nIG9ubHkgZS1jaWdhcmV0dGVzIHJhdGhlciB0aGFuIG9ubHkgb3RoZXIgdG9iYWNjbyBwcm9kdWN0cyBpbiAyMDE5LgoKV2Ugd2lsbCBmdXJ0aGVyIGV4cGxvcmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGUtY2lnYXJldHRlIHVzYWdlIGFuZCBvdGhlciB0b2JhY2NvIHByb2R1Y3RzIGEgYml0IGxhdGVyIGluIHRoZSBjYXNlIHN0dWR5LgoKCiMjIyAqKlF1ZXN0aW9uIDIqKgoqKioKCk5vdyB3ZSB3YW50IHRvIGxvb2sgaG93IGUtY2lnYXJldHRlIHNtb2tpbmcgcmF0ZXMgY29tcGFyZSBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzIGFjcm9zcyB0aW1lLiAKCgpXZSB3aWxsIGNhbGN1bGF0ZSB0aGUgcGVyY2VudCBldmVyIGFuZCBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXJzIGZvciBlYWNoIHllYXIgYW5kIHNleCBjYXRlZ29yeSBhZ2FpbiB1c2luZyB0aGUgYGdyb3VwX2J5KClgIGZ1bmN0aW9uIGFuZCB0aGUgYHN1bW1hcml6ZSgpYCBmdW5jdGlvbi4gIFdlIHdpbGwgYWdhaW4gdXNlIHRoZSBgcGl2b3RfbG9uZ2VyYCBmdW5jdGlvbiB0byBjb252ZXJ0IG91ciBkYXRhIHRvIGxvbmcgZm9ybWF0LiAKCkFzIGRpc2N1c3NlZCBhYm92ZSwgd2UgYWNrbm93bGVkZ2UgdGhhdCB3aGlsZSBbZ2VuZGVyXShodHRwczovL3d3dy5nZW5kZXJzcGVjdHJ1bS5vcmcvcXVpY2stbGlua3MvdW5kZXJzdGFuZGluZy1nZW5kZXIvKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZCBbc2V4XShodHRwczovL3d3dy53aG8uaW50L2dlbm9taWNzL2dlbmRlci9lbi9pbmRleDEuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBhcmUgbm90IGFjdHVhbGx5IGJpbmFyeSwgdGhlIGRhdGEgdXNlZCBpbiB0aGlzIGFuYWx5c2lzIG9ubHkgY29udGFpbiBpbmZvcm1hdGlvbiBmb3IgZ3JvdXBzIG9mIGluZGl2aWR1YWxzIHdobyBhbnN3ZXJlZCB0aGUgc3VydmV5IHF1ZXN0aW9ucyBhcyBtYWxlIG9yIGZlbWFsZS4gRm9yIGluZGl2aWR1YWxzIHRoYXQgaGF2ZSBOQSB2YWx1ZXMsIGl0IGlzIHVuY2xlYXIgaWYgdGhlIHF1ZXN0aW9uIHdhcyBub3QgYW5zd2VyZWQgb3IgaWYgdGhlIGluZGl2aWR1YWwgaWRlbnRpZmllcyBhcyBub24tYmluYXJ5LiBCZWNhdXNlIG9mIHRoaXMgdW5jZXJ0YWludHksIHdlIHdpbGwgZmlsdGVyIHRoZXNlIGluZGl2aWR1YWxzIG91dC4gCgpgYGB7cn0KIyB1c2UgZGlmZmVyZW50IGNvbG9ycyBoZXJlIGZvciBtYWxlcyBhbmQgZmVtYWxlcyB0byBkaWZmZXJlbnRpYXRlIGZyb20gdGhlIHByZXZpb3VzIHBsb3RzCnZfY29sb3JzID0gIHZpcmlkaXMoNilbYygzLCA1KV0KCm55dHNfZGF0YSAlPiUKICAgICBmaWx0ZXIoIWlzLm5hKFNleCkpICU+JQogICAgIGdyb3VwX2J5KHllYXIsIFNleCkgJT4lCiAgICAgc3VtbWFyaXplKCJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKSIgPSAobWVhbihFRUxDSUdULCBuYS5ybSA9IFRSVUUpICogMTAwKSwKICAgICAgICAgICAgICAgIkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpIiA9IChtZWFuKENFTENJR1QsIG5hLnJtID0gVFJVRSkgKiAxMDApKSAlPiUKICAgICBwaXZvdF9sb25nZXIoY29scyA9ICJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKSI6IkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpIiwKICAgICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVXNlciIsCiAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJQZXJjZW50YWdlIG9mIHN0dWRlbnRzIikgJT4lCiAgICAgaGVhZCgpCgpwbG90MiA8LSBueXRzX2RhdGEgJT4lCiAgICAgZmlsdGVyKCFpcy5uYShTZXgpKSAlPiUKICAgICBncm91cF9ieSh5ZWFyLCBTZXgpICU+JQogICAgIHN1bW1hcml6ZSgiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSkiID0gKG1lYW4oRUVMQ0lHVCwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKSIgPSAobWVhbihDRUxDSUdULCBuYS5ybSA9IFRSVUUpICogMTAwKSkgJT4lCiAgICAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAiRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSkiOiJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKSIsCiAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIlVzZXIiLAogICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpICU+JQogICAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGBQZXJjZW50YWdlIG9mIHN0dWRlbnRzYCwgY29sb3IgPSBTZXgpKSArCiAgICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlID0gVXNlcikpICsKICAgIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSwgc2l6ZSA9IDIpICsKICAgIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKDIsIDEpKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdl9jb2xvcnMpICsKICAgIHRoZW1lX2xpbmVkcmF3KCkgKwogICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGxhYnModGl0bGUgPSAiSG93IGRvZXMgZS1jaWdhcmV0dGUgdXNhZ2UgY29tcGFyZSBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzPyIsCiAgICAgICAgIHN1YnRpdGxlID0gIkN1cnJlbnQgYW5kIGV2ZXIgdXNlcnMgYnkgc2V4IiwKICAgICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3QyICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCkl0IGxvb2tzIGxpa2UgdGhlIHJhdGVzIGFyZSBmYWlybHkgc2ltaWxhciBiZXR3ZWVuIHRoZSBzZXhlcywgaG93ZXZlciB0aGUgcmF0ZSBmb3IgbWFsZXMgYXBwZWFycyB0byBiZSBjb25zaXN0ZW50bHkgaGlnaGVyIGFjcm9zcyB0aW1lLgoKYGBge3IsIGVjaG89RkFMU0UsIGluY2x1ZGU9RkFMU0V9Cmdnc2F2ZShoZXJlOjpoZXJlKCJpbWciLCAicGxvdDIucG5nIikpCmBgYAoKIyMjICoqUXVlc3Rpb24gMyoqCioqKgoKV2UgYXJlIGFsc28gaW50ZXJlc3RlZCBpbiB3aGF0IHZhcGluZyBicmFuZHMgYW5kIGZsYXZvcnMgYXBwZWFyIHRvIGJlIHVzZWQgdGhlIG1vc3QgZnJlcXVlbnRseS4gT25seSB0aGUgMjAxOSBkYXRhc2V0IGhhcyB0aGlzIGluZm9ybWF0aW9uLiBUaGVyZWZvcmUsIHdlIHdpbGwgZmlsdGVyIGZvciBqdXN0IHRoaXMgeWVhciB1c2luZyB0aGUgYGZpbHRlcigpYCBmdW5jdGlvbiBvZiAgdGhlIGBkcGx5cmAgcGFja2FnZS4gV2Ugd2lsbCB1c2UgdGhlIGBzdW1tYXJpemUoKWAgZnVuY3Rpb24gc2xpZ2h0bHkgZGlmZmVyZW50bHkgdGhpcyB0aW1lLCB0byBjYWxjdWxhdGUgdGhlIHRvdGFsIG51bWJlciBvZiBzdHVkZW50cyB1c2luZyBlYWNoIGJyYW5kIHVzaW5nIHRoZSBgbigpYCBmdW5jdGlvbiBhbmQgdGhlIGBzdW0oKWAgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBwZXJjZW50IGZvciBlYWNoIGJyYW5kIGJhc2VkIG9uIHRoZSBjb3VudHMuIFdlIHdpbGwgYWxzbyByZW9yZGVyIHRoZSBmYWN0b3IgbGV2ZWxzIGZvciB0aGUgYnJhbmQgbmFtZXMgc28gdGhhdCB0aGV5IGFyZSBpbiBkZXNjZW5kaW5nIG9yZGVyIG9mIHBlcmNlbnQgdXNlLCB1c2luZyB0aGUgYGZjdF9yZW9yZGVyKClgIGZ1bmN0aW9uIGZyb20gYGRwbHlyYC4gVGhpcyB3aWxsIG1ha2UgdGhlbSBhcHBlYXIgaW4gZGVjcmVhc2luZyBvcmRlciBvZiBwZXJjZW50IHVzZSBvbiB0aGUgcGxvdC4KCldlIHdpbGwgbWFrZSBhIGJhciBwbG90IHRoaXMgdGltZSBieSB1c2luZyBgZ2VvbV9iYXJgLiBJbXBvcnRhbnRseSB3ZSBhc3NpZ24gdGhlIGBzdGF0YCBhcmd1bWVudCB0byBgaWRlbnRpdHlgLCBzbyB0aGF0IHdlIGFyZSB1c2luZyB0aGUgcGVyY2VudGFnZXMgdGhhdCB3ZSBjYWxjdWxhdGVkIG5vdCB0aGUgY291bnRzIHdoaWNoIGlzIHdoYXQgaXMgdXNlZCBieSBkZWZhdWx0LiBXaGVuIGNvbG9yIGluIHNwZWNpZmllZCBvdXRzaWRlIG9mIHRoZSBgYWVzKClgIGFyZ3VtZW50LCB0aGlzIGRldGVybWluZXMgdGhlIGJvcmRlciBjb2xvciBvZiB0aGUgYmFycywgd2hpY2ggaW4gdGhpcyBjYXNlIHdpbGwgYmUgYmxhY2suCgpgYGB7cn0KCm55dHNfZGF0YSAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDE5KSAlPiUKICBncm91cF9ieShicmFuZF9lY2lnKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGJyYW5kX2VjaWcpKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkgJT4lCiAgbXV0YXRlKHRvdGFsID0gc3VtKG4pLAogICAgICAgICBQZXJjZW50ID0gbiAqIDEwMCAvIHRvdGFsKSAlPiUKICBtdXRhdGUoYnJhbmRfZWNpZyA9IGZjdF9yZW9yZGVyKGJyYW5kX2VjaWcsIGRlc2MoUGVyY2VudCkpKQoKCnBsb3QzIDwtIG55dHNfZGF0YSAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDE5KSAlPiUKICBncm91cF9ieShicmFuZF9lY2lnKSAlPiUKICBmaWx0ZXIoIWlzLm5hKGJyYW5kX2VjaWcpKSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkgJT4lCiAgbXV0YXRlKHRvdGFsID0gc3VtKG4pLAogICAgICAgICBQZXJjZW50ID0gbiAqIDEwMCAvIHRvdGFsKSAlPiUKICBtdXRhdGUoYnJhbmRfZWNpZyA9IGZjdF9yZW9yZGVyKGJyYW5kX2VjaWcsIGRlc2MoUGVyY2VudCkpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBicmFuZF9lY2lnLCB5ID0gUGVyY2VudCwgZmlsbCA9IGJyYW5kX2VjaWcpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIGNvbG9yID0gImJsYWNrIikgKwogIHRoZW1lX2xpbmVkcmF3KCkgKwogIHRoZW1lKAogICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpCiAgKSArCiAgbGFicyh0aXRsZSA9ICJXaGF0IHZhcGluZyBicmFuZHMgYXBwZWFyIHRvIGJlIHVzZWQgdGhlIG1vc3QgZnJlcXVlbnRseT8iLAogICAgICAgc3VidGl0bGUgPSAiQnJhbmQgb2YgZS1jaWdhcmV0dGUgbW9zdCBmcmVxdWVudGx5IHVzZWQgaW4gdGhlIGxhc3QgMzAgZGF5cyAoMjAxOSkiLAogICAgICAgeSA9ICIlIG9mIGUtY2lnYXJldHRlIHVzZXJzIHJlc3BvbmRpbmciKQoKcGxvdDMgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYAoKSnV1bCBhcHBlYXJzIHRvIGJlIHRoZSBtb3N0IHdpZGVseSB1c2VkIGJyYW5kLiBUaGlzIGlzIGluIGFncmVlbWVudCB3aXRoIGEgcmVjZW50IFthcnRpY2xlXShodHRwczovL3RvYmFjY29jb250cm9sLmJtai5jb20vY29udGVudC90b2JhY2NvY29udHJvbC8yOC8yLzE0Ni5mdWxsLnBkZiksIHdob3NlIG1vc3QgcmVjZW50IGRhdGEgd2FzIGZyb20gMjAxNzoKCmBgYHtyLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSJIdWFuZyBKLCBEdWFuIFosIEt3b2sgSiwgZXQgYWwuIFRvYiBDb250cm9sIDIwMTk7Mjg6MTQ24oCTMTUxLiIsIG91dC53aWR0aCA9ICcxMDAlJ30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoaGVyZTo6aGVyZSgiaW1nIiwgIkh1YW5nSl9EdWFuWl9Ld29rSl9ldF9hbF9Ub2JhY2NvQ29udHJvbF9GaWd1cmUxLnBuZyIpKQpgYGAKCldlIGFyZSBhbHNvIGludGVyZXN0ZWQgaW4gaG93IHRoZSB1c2FnZSBvZiBkaWZmZXJlbnQgZmxhdm9ycyBoYXMgY2hhbmdlZCBvdmVyIHRpbWUuIAoKClRvIGV2YWx1YXRlIHRoaXMgd2Ugd2lsbCBjYWxjdWxhdGUgdGhlIHBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMgdXNpbmcgZWFjaCBmbGF2b3IgZWFjaCB5ZWFyIC0gdGhpcyBpbmNsdWRlcyB1c2FnZSBvZiBhbnkgdHlwZSBvZiBmbGF2b3JlZCB0b2JhY2NvIHByb2R1Y3QuIFdlIHdpbGwgZXhjbHVkZSAyMDE1IGRhdGEsIGFzIG5vIHNwZWNpZmljIGZsYXZvciBxdWVzdGlvbnMgd2VyZSBhc2tlZCBhdCB0aGF0IHRpbWUuCgpSZWNhbGwgdGhhdCBgTkFgIHZhbHVlcyBhcmUgbm90IGluY2x1ZGVkIGluIGNhbGN1bGF0aW5nIHRoZSB0b3RhbCBjb3VudCBmb3Igb3VyIHBlcmNlbnRhZ2VzLiBIb3dldmVyIGFsbCBvZiB0aGVzZSBmbGF2b3IgcXVlc3Rpb25zIGhhZCBjb21wbGV0ZSByZXBvcnRpbmcgYW5kIGRpZCBub3QgaGF2ZSBgTkFgIHZhbHVlcy4gVGhlcmVmb3JlLCB0aGVzZSB2YWx1ZXMgcmVmbGVjdCB0aGUgcGVyY2VudGFnZSBvZiBzdHVkZW50cyByZXBvcnRpbmcgdXNpbmcgYSBwYXJ0aWN1bGFyIGZhdm9yIG91dCBvZiBhbGwgc3R1ZGVudHMgc3VydmV5ZWQgKGluY2x1ZGluZyB0aG9zZSB0aGF0IGRpZCBub3QgdXNlIGFueSB0b2JhY2NvIHByb2R1Y3RzKS4gQWxzbyBzdHVkZW50cyB3ZXJlIGFsbG93ZWQgdG8gc2VsZWN0IG1vcmUgdGhhbiBvbmUgZmxhdm9yLiBZb3UgY2FuIHNlZSB3aGV0aGVyIHRoZXNlIHZhcmlhYmxlcyBoYWQgY29tcGxldGUgcmVwb3J0aW5nIGJ5IGNoZWNraW5nIHRoZSBgTkFgIHZhbHVlcyB1c2luZyB0aGUgYmFzZSBgc3VtbWFyeWAgZnVuY3Rpb24uIEFsdGVybmF0aXZlbHkgeW91IGNhbiBjcmVhdGUgYSB2aXN1YWwgcmVwcmVzZW50YXRpb24gdXNpbmcgdGhlIGB2aXNfbWlzcygpYCBmdW5jdGlvbiBvZiB0aGUgYG5hbmlhcmAgcGFja2FnZS4KCiMjIyMgey5zY3JvbGxhYmxlfQpgYGB7cn0KIyBTY3JvbGwgdGhyb3VnaCB0aGUgb3V0cHV0IQpueXRzX2RhdGEgJT4lCiAgZmlsdGVyKHllYXIgIT0gMjAxNSkgJT4lCiAgc3VtbWFyeSgpCmBgYAojIyMjCgoKYGBge3J9Cm55dHNfZGF0YSAlPiUKICBmaWx0ZXIoeWVhciAhPSAyMDE1KSAlPiUKICBzZWxlY3QobWVudGhvbDphbGNvaG9saWNfZHJpbmspICU+JQogIHZpc19taXNzKCkKYGBgCgoKVGhlIHBsb3QgYWJvdmUgY29uZmlybXMgdGhhdCB0aGVzZSB2YXJpYWJsZXMgaGF2ZSBubyBgTkFgIHZhbHVlcyAoYmVjYXVzZSBhbGwgZmllbGRzIGluZGljYXRlIDEwMCUgb2YgZGF0YSBpcyBwcmVzZW50KS4KCmBgYHtyfQpwbG90NCA8LSBueXRzX2RhdGEgJT4lCiAgZmlsdGVyKHllYXIgIT0gMjAxNSkgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgICAgIHN1bW1hcml6ZShNZW50aG9sID0gKG1lYW4obWVudGhvbCkgKiAxMDApLAogICAgICAgYENsb3ZlIG9yIFNwaWNlYCA9IChtZWFuKGNsb3ZlX3NwaWNlKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgIEZydWl0ID0gKG1lYW4oZnJ1aXQpICogMTAwKSwKICAgICAgICAgICAgICBDaG9jb2xhdGUgPSAobWVhbihjaG9jb2xhdGUpICogMTAwKSwKICAgICAgYEFsY29ob2xpYyBEcmlua2AgPSAobWVhbihhbGNvaG9saWNfZHJpbmspICogMTAwKSwKYENhbmR5L0Rlc3NlcnRzL1N3ZWV0c2AgPSAobWVhbihjYW5keV9kZXNzZXJ0X3N3ZWV0cykgKiAxMDApLAogICAgICAgICAgICAgICAgICBPdGhlciA9IChtZWFuKG90aGVyKSAqIDEwMCkpICU+JQogICAgICBwaXZvdF9sb25nZXIoY29scyA9IC15ZWFyLCAKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiRmxhdm9yIiwKICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpICU+JQogIHJlbmFtZShZZWFyID0geWVhcikgJT4lCgogZ2dwbG90KGFlcyh5ID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgLAogICAgICAgICAgICB4ID0gWWVhciwKICAgICAgICAgICAgZmlsbCA9IHJlb3JkZXIoRmxhdm9yLCBgUGVyY2VudGFnZSBvZiBzdHVkZW50c2ApKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLAogICAgICAgICAgIHBvc2l0aW9uID0gImRvZGdlIiwKICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXMoZGlzY3JldGUgPSBUUlVFKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQoIkZsYXZvciIpKSArCiAgbGFicyh0aXRsZSA9ICJXaGF0IGZsYXZvcnMgYXBwZWFyIHRvIGJlIHVzZWQgdGhlIG1vc3QgZnJlcXVlbnRseT8iLAogICAgICAgc3VidGl0bGUgPSAiRmxhdm9ycyBvZiB0b2JhY2NvIHByb2R1Y3RzIHVzZWQgaW4gdGhlIHBhc3QgMzAgZGF5cyIpCgpwbG90NCArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkKYGBgCgpGcm9tIHRoaXMgcGxvdCwgd2UgY2FuIHNlZSB0aGF0IGZydWl0IGZsYXZvcnMgYXJlIHRoZSBtb3N0IHdpZGVseSB1c2VkIHByb2R1Y3RzLCBmb2xsb3dlZCBieSBtZW50aG9sIG9yIG1pbnQgZmxhdm9yZWQgcHJvZHVjdHMuIFdlIGNhbiBhbHNvIHNlZSB0aGF0IHRoZXJlIHdhcyBhIGdlbmVyYWwgaW5jcmVhc2UgaW4gdGhlIHVzYWdlIG9mIGZsYXZvcmVkIHByb2R1Y3RzIG92ZXIgdGltZS4KCldlIHdpbGwgbm93IGxvb2sgc3BlY2lmaWNhbGx5IGF0IHRoZSB1c2FnZSBvZiBmbGF2b3JlZCBlLWNpZ2FyZXR0ZSBwcm9kdWN0cyB2cyBvdGhlciBmbGF2b3JlZCB0b2JhY2NvIHByb2R1Y3RzLiAKClJlY2FsbCB0aGF0IHdlIG1hZGUgYSB2YXJpYWJsZSBjYWxsZWQgYEdyb3VwYCB0aGF0IGlkZW50aWZpZWQgc3R1ZGVudHMgd2hvIHVzZWQgZWl0aGVyIGp1c3QgZS1jaWdhcmV0dGUvdmFwaW5nIHByb2R1Y3RzLCBqdXN0IG90aGVyIHRvYmFjY28gcHJvZHVjdHMgKGJlc2lkZXMgZS1jaWdhcmV0dGVzKSwgb3Igc3R1ZGVudHMgd2hvIHVzZWQgYm90aCBlLWNpZ2FyZXR0ZXMgYW5kIHNvbWUgb3RoZXIgdHlwZSBvZiB0b2JhY2NvIHByb2R1Y3QuIFdlIHdpbGwgY29tcGFyZSB0aGUgdXNhZ2Ugb2YgdGhlc2UgZmxhdm9ycyBmb3IgdGhlc2UgZGlmZmVyZW50IGdyb3Vwcy4gV2UgYWxzbyBwZXJmb3JtIHNvbWUgZGF0YSBzdW1tYXJpZXMgdG8gZGVjaWRlIGhvdyB0byBvcmRlciB0aGUgcGFuZWxzIChmbGF2b3JzKSBmb3IgZGlzcGxheS4KCgoKYGBge3J9Cgp2X2NvbG9ycyA9ICB2aXJpZGlzKDUpWzE6NF0KCnBsb3Q1IDwtIG55dHNfZGF0YSAlPiUKICBmaWx0ZXIoeWVhciAhPSAyMDE1KSAlPiUKICBncm91cF9ieSh5ZWFyLCBHcm91cCkgJT4lCiAgICAgICAgc3VtbWFyaXplKE1lbnRob2wgPSAobWVhbihtZW50aG9sKSAqIDEwMCksCiAgICAgICAgIGBDbG92ZSBvciBTcGljZWAgPSAobWVhbihjbG92ZV9zcGljZSkgKiAxMDApLAogICAgICAgICAgICAgICAgICAgIEZydWl0ID0gKG1lYW4oZnJ1aXQpICogMTAwKSwKICAgICAgICAgICAgICAgIENob2NvbGF0ZSA9IChtZWFuKGNob2NvbGF0ZSkgKiAxMDApLAogICAgICAgIGBBbGNvaG9saWMgRHJpbmtgID0gKG1lYW4oYWxjb2hvbGljX2RyaW5rKSAqIDEwMCksCmBDYW5keS9EZXNzZXJ0cy9cblN3ZWV0c2AgPSAobWVhbihjYW5keV9kZXNzZXJ0X3N3ZWV0cykgKiAxMDApLAogICAgICAgICAgICAgICAgICAgIE90aGVyID0gKG1lYW4ob3RoZXIpICogMTAwKSwKICAgICAgICAgICAgICBSZXNwb25kZW50cyA9IG4oKSkgJT4lCiAgIyBjb252ZXJ0aW5nIGNvbHVtbnMgYmV0d2VlbiBhbmQgaW5jbHVkaW5nIE1lbnRob2wgYW5kIE90aGVyIHRvIG9uZSBjb2x1bW4gY2FsbGVkIEZsYXZvcgogIHBpdm90X2xvbmdlcihjb2xzID0gTWVudGhvbDpPdGhlciwgCiAgICAgICAgICAgbmFtZXNfdG8gPSAiRmxhdm9yIiwgCiAgICAgICAgICB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpICU+JQogIGdyb3VwX2J5KEZsYXZvcikgJT4lCiAgIyBjYWxjdWxhdGUgdGhlIGNvdW50IG9mIHN0dWRlbnRzIGluIHRoZSB5ZWFyL2dyb3VwIGNvbWJpbmF0aW9uIHdobyB1c2VkIHRoYXQgZmxhdm9yCiAgbXV0YXRlKGFmZmlybWF0aXZlID0gKFJlc3BvbmRlbnRzICogYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgKSAvIDEwMCkgJT4lCiAgIyBjYWxjdWxhdGUgdGhlIGZyYWN0aW9uIG9mIHRvdGFsIHJlc3BvbmRlbnRzIHdobyB1c2VkIHRoYXQgZmxhdm9yCiAgbXV0YXRlKGZsYXZvcl9tZWFuID0gc3VtKGFmZmlybWF0aXZlKSAvIHN1bShSZXNwb25kZW50cykpICU+JQogIHVuZ3JvdXAoKSAlPiUKICAjIHJlb3JkZXIgdGhlIGxldmVscyBvZiBGbGF2b3IgdG8gYmUgaW4gaW5jcmVhc2luZyBvcmRlciBvZiBwZXJjZW50IG9mIHN0dWRlbnRzCiAgbXV0YXRlKGZsYXZvcl9tZWFuX3JhbmsgPSBkZW5zZV9yYW5rKGZsYXZvcl9tZWFuKSwKICAgICAgICAgRmxhdm9yID0gZmN0X3Jlb3JkZXIoRmxhdm9yLCBmbGF2b3JfbWVhbl9yYW5rKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgCiAgICAgICAgICAgICB5ID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgLCAKICAgICAgICAgY29sb3IgPSBHcm91cCkpICsKICBmYWNldF9ncmlkKH5GbGF2b3IpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFLCBzaXplID0gMikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSB2X2NvbG9ycykgKwogIHRoZW1lX2xpbmVkcmF3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKSwKICAgICAgICBzdHJpcC50ZXh0LnggPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSkgKwogIGxhYnModGl0bGUgPSAiQW1vbmcgZGlmZmVyZW50IHByb2R1Y3QgdXNlcnMsIHdoYXQgZmxhdm9ycyBhcmUgbW9zdCBmcmVxdWVudGx5IHVzZWQ/IikKCnBsb3Q1ICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCgpXZSBjYW4gc2VlIGZyb20gdGhpcyBwbG90IHRoYXQgdGhlcmUgaGFzIGJlZW4gYW4gaW5jcmVhc2UgaW4gdGhlIG51bWJlciBvZiBzdHVkZW50cyByZXBvcnRpbmcgdXNpbmcgZmxhdm9yZWQgdG9iYWNjbyBwcm9kdWN0cy4gVXNlcnMgd2hvIHVzZSBib3RoIGUtY2lnYXJldHRlcyBhbmQgb3RoZXIgdG9iYWNjbyBwcm9kdWN0cyBhcHBlYXIgdG8gcmVwb3J0IHVzaW5nIGZsYXZvcmVkIHByb2R1Y3RzIHRoZSBtb3N0LCBmb2xsb3dlZCBieSB1c2VycyB3aG8gb25seSB1c2UgZS1jaWdhcmV0dGVzLgoKIyMjICoqUXVlc3Rpb24gNCoqCioqKgoKSXMgdGhlcmUgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiBlLWNpZ2FyZXR0ZSB1c2UgYW5kIHRvYmFjY28gdXNlPyBOb3cgd2Ugd2lsbCBpbnZlc3RpZ2F0ZSB0aGUgdXNhZ2Ugb2YgZS1jaWdhcmV0dGVzIGNvbXBhcmVkIHRvIG90aGVyIHRvYmFjY28gcHJvZHVjdHMgaW4gZ3JlYXRlciBkZXB0aC4gCgpGaXJzdCBsZXQncyB0YWtlIGEgbG9vayBhdCBob3cgZS1jaWdhcmV0dGUgdXNhZ2UgYW5kIGNpZ2FyZXR0ZSB1c2FnZSBjb21wYXJlLiBXZSB3aWxsIHNlbGVjdCB0aGUgZGF0YSB0aGF0IHNwZWNpZmljYWxseSBoYXMgdG8gZG8gd2l0aCB0aGVzZSBwcm9kdWN0cy4KCmBgYHtyfQoKCnZfY29sb3JzID0gIHZpcmlkaXMoNilbYygxLCA0KV0KCm55dHNfZGF0YSAlPiUKICAgIGdyb3VwX2J5KHllYXIpICU+JQogICAgc3VtbWFyaXplKCJDaWdhcmV0dGVzLCBFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKSIgPSAobWVhbihFQ0lHVCwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICJFLWNpZ2FyZXR0ZXMsIEV2ZXIgXG4gKGFueSBsaWZldGltZSB1c2UpIiA9IChtZWFuKEVFTENJR1QsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgICAgICAgICJDaWdhcmV0dGVzLCBDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKSIgPSAobWVhbihDQ0lHVCwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICJFLWNpZ2FyZXR0ZXMsIEN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpIiA9IChtZWFuKENFTENJR1QsIG5hLnJtID0gVFJVRSkgKiAxMDApKSAlPiUKICAgIHBpdm90X2xvbmdlcihjb2xzID0gLSB5ZWFyLCAKICAgICAgICAgICAgIG5hbWVzX3RvID0gIkNhdGVnb3J5IiwgCiAgICAgICAgICAgIHZhbHVlc190byA9ICJQZXJjZW50YWdlIG9mIHN0dWRlbnRzIikgJT4lCiAgICBzZXBhcmF0ZShDYXRlZ29yeSwgaW50byA9IGMoIlByb2R1Y3QiLCAiVXNlciIpLCBzZXAgPSAiLCAiKSAlPiUKICAgIGhlYWQoKQoKCnBsb3Q2IDwtIG55dHNfZGF0YSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBzdW1tYXJpemUoCiAgICAiQ2lnYXJldHRlcywgRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSkiID0gKG1lYW4oRUNJR1QsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgIkUtY2lnYXJldHRlcywgRXZlciBcbiAoYW55IGxpZmV0aW1lIHVzZSkiID0gKG1lYW4oRUVMQ0lHVCwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAiQ2lnYXJldHRlcywgQ3VycmVudCBcbiAoYW55IHBhc3QtMzAtZGF5IHVzZSkiID0gKG1lYW4oQ0NJR1QsIG5hLnJtID0gVFJVRSkgKiAxMDApLAogICAgIkUtY2lnYXJldHRlcywgQ3VycmVudCBcbiAoYW55IHBhc3QtMzAtZGF5IHVzZSkiID0gKG1lYW4oQ0VMQ0lHVCwgbmEucm0gPSBUUlVFKSAqIDEwMCkKICApICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLXllYXIsCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkNhdGVnb3J5IiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMiKSAlPiUKICBzZXBhcmF0ZShDYXRlZ29yeSwgaW50byA9IGMoIlByb2R1Y3QiLCAiVXNlciIpLCBzZXAgPSAiLCAiKSAlPiUKICBnZ3Bsb3QoYWVzKAogICAgeCA9IHllYXIsCiAgICB5ID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgLAogICAgY29sb3IgPSBQcm9kdWN0LAogICAgbGluZXR5cGUgPSBVc2VyCiAgKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UsIHNpemUgPSAyKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMiwgMSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdl9jb2xvcnMpICsKICB0aGVtZV9saW5lZHJhdygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIkhvdyBkb2VzIGUtY2lnYXJldHRlIHVzZSBjb21wYXJlIHRvIGNpZ2FyZXR0ZSB1c2U/IiwKICAgICAgIHN1YnRpdGxlID0gIkN1cnJlbnQgYW5kIGV2ZXIgdXNlcnMgb2YgZS1jaWdhcmV0dGVzIGFuZCBjaWdhcmV0dGVzIiwKICAgICAgIHkgPSAiJSBvZiBzdHVkZW50cyIpCgpwbG90NiArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1KSkKYGBgCgpJbnRlcmVzdGluZyEgd2UgY2FuIHNlZSB0aGF0IGluIDIwMTkgdGhlIHBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMgdGhhdCByZXBvcnRlZCBjdXJyZW50bHkgdXNpbmcgZS1jaWdhcmV0dGVzIGhhZCBzdXJwYXNzZWQgdGhvc2UgdGhhdCBldmVyIHRyaWVkIChldmVuIGp1c3Qgb25jZSkgYSBjaWdhcmV0dGUuIE92ZXJhbGwgY2lnYXJldHRlIHVzYWdlIGFwcGVhcnMgdG8gYmUgZGVjbGluaW5nIG92ZXIgdGltZS4gVGhpcyBpcyBub3QgdGhlIGNhc2UgZm9yIGUtY2lnYXJldHRlcy4KCgpOb3cgd2Ugd2lsbCBsb29rIGF0IHN0dWRlbnRzIHdobyByZXBvcnRlZCB0aGF0IHRoZXkgaGFkIGV2ZXIgdHJpZWQgZS1jaWdhcmV0dGVzIG9yIG5vbi1jaWdhcmV0dGUgcHJvZHVjdHMuIEluIHRoaXMgY2FzZSB3ZSB3aWxsIG5vdCBzZXBhcmF0ZSBvdXQgdXNlcnMgd2hvIHNwZWNpZmljYWxseSBvbmx5IHVzZWQgb25lIG9yIHRoZSBvdGhlci4gVGhlcmVmb3JlLCB0aGUgc3R1ZGVudHMgaW5jbHVkZWQgaW4gdGhpcyBwbG90IHdobyByZXBvcnRlZCBhcyBoYXZpbmcgZXZlciB0cmllZCBlLWNpZ2FyZXR0ZXMgbWlnaHQgYWxzbyBiZSAgY3VycmVudCB1c2VycyBvZiBub24tZS1jaWdhcmV0dGUgcHJvZHVjdHMgb3IgbWF5IGhhdmUgYXQgbGVhc3QgdHJpZWQgbm9uLWUtY2lnYXJldHRlIHByb2R1Y3RzLgoKCmBgYHtyfQoKdl9jb2xvcnMgPSAgdmlyaWRpcyg2KVtjKDEsIDQpXQoKcGxvdDcgPC0gbnl0c19kYXRhICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIHN1bW1hcml6ZSgKICAgIGBlLWNpZ2FyZXR0ZV9ldmVyYCA9IChtZWFuKGVjaWdfZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICBgbm9uLWUtY2lnYXJldHRlX2V2ZXJgID0gKG1lYW4obm9uX2VjaWdfZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCkKICApICU+JQogIHBpdm90X2xvbmdlcihjb2xzID0gLXllYXIsCiAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIkNhdGVnb3J5IiwKICAgICAgICAgICAgICAgdmFsdWVzX3RvID0gIlBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHMiKSAlPiUKICBzZXBhcmF0ZShDYXRlZ29yeSwgaW50byA9IGMoIlByb2R1Y3QiLCAiVXNlciIpLCBzZXAgPSAiXyIpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsCiAgICAgICAgICAgICB5ID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgLAogICAgICAgICAgICAgY29sb3IgPSBQcm9kdWN0KSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KHNob3cubGVnZW5kID0gRkFMU0UsIHNpemUgPSAyKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHZfY29sb3JzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA2MCwgYnkgPSAxMCksIGxpbWl0cyA9IGMoMCwgNjApKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgZG9lcyB0aGUgcmF0ZSBvZiBldmVyIHRyeWluZyBlLWNpZ2FyZXR0ZXMKY29tcGFyZSB0byBldmVyIHRyeWluZyBvdGhlciBwcm9kdWN0cyBvdmVyIHRpbWU/IiwKeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3Q3ICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCgpOb3cgd2Ugd2lsbCBkbyB0aGUgc2FtZSwgYnV0IGZvciBzdHVkZW50cyB3aG8gcmVwb3J0ZWQgY3VycmVudGx5IHVzaW5nIGUtY2lnYXJldHRlcyBvciBub24tZS1jaWdhcmV0dGUgcHJvZHVjdHMuIAoKCmBgYHtyfQp2X2NvbG9ycyA9ICB2aXJpZGlzKDYpW2MoMSwgNCldCgpwbG90OCA8LSBueXRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgc3VtbWFyaXplKAogICAgYGUtY2lnYXJldHRlX2N1cnJlbnRgID0gKG1lYW4oZWNpZ19jdXJyZW50LCBuYS5ybSA9IFRSVUUpICogMTAwKSwKICAgIGBub24tZS1jaWdhcmV0dGVfY3VycmVudGAgPSAobWVhbihub25fZWNpZ19jdXJyZW50LCBuYS5ybSA9IFRSVUUpICogMTAwKQogICkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbHMgPSAteWVhciwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpICU+JQogIHNlcGFyYXRlKENhdGVnb3J5LCBpbnRvID0gYygiUHJvZHVjdCIsICJVc2VyIiksIHNlcCA9ICJfIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGBQZXJjZW50YWdlIG9mIHN0dWRlbnRzYCwgY29sb3IgPSBQcm9kdWN0KSkgKwogIGdlb21fbGluZShsaW5ldHlwZSA9ICJkYXNoZWQiKSArCiAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFLCBzaXplID0gMikgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSB2X2NvbG9ycykgKwogIHNjYWxlX2xpbmV0eXBlX21hbnVhbCh2YWx1ZXMgPSBjKDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA2MCwgYnkgPSAxMCksIGxpbWl0cyA9IGMoMCwgNjApKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICJIb3cgZG9lcyB0aGUgcmF0ZSBvZiBjdXJyZW50bHkgdXNpbmcgZS1jaWdhcmV0dGVzCmNvbXBhcmUgdG8gY3VycmVudGx5IHVzaW5nIG90aGVyIHByb2R1Y3RzIG92ZXIgdGltZT8iLAogICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3Q4ICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCiMjIyAqKlB1dHRpbmcgcGxvdHMgdG9nZXRoZXIqKgoqKioKCk5vdyB3ZSB3aWxsIHB1dCB0aGVzZSBwbG90cyB0b2dldGhlciB1c2luZyB0aGUgYHBsb3RfZ3JpZCgpYCBmdW5jdGlvbiBvZiB0aGUgYGNvd3Bsb3RgIHBhY2thZ2UuICBXZSB3aWxsIGFsc28gbW9kaWZ5IHRoZSBsYWJlbHMgdXNpbmcgdGhlIGBnZ2RyYXcoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIGFsc28gcGFydCBvZiB0aGUgYGNvd3Bsb3RgIHBhY2thZ2UuIFRvIGxlYXJuIG1vcmUgYWJvdXQgdGhlIGBjb3dwbG90YCBwYWNrYWdlLCByZWZlciB0byB0aGlzIFtjYXNlIHN0dWR5XShodHRwczovL3d3dy5vcGVuY2FzZXN0dWRpZXMub3JnL29jcy1icC1SVEMtYW5hbHlzaXMvI0RhdGFfVmlzdWFsaXphdGlvbil7dGFyZ2V0PSJfYmxhbmsifS4KCmBgYHtyLCBmaWcuaGVpZ2h0PTEwfQpwbG90QV91dyA8LSBwbG90MSArCiAgdGhlbWUoYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikgKwogIGxhYnModGl0bGUgPSAiVG9iYWNjbyBwcm9kdWN0IHVzZXJzIG1vcmUgcHJldmFsZW50IGFmdGVyIDIwMTciLAogICAgICAgc3VidGl0bGUgPSBOVUxMLAogICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3RCX3V3IDwtIHBsb3Q3ICsKICB0aGVtZShheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKSArCiAgICBsYWJzKHRpdGxlID0gIiUgRXZlciB0cnlpbmcgZS1jaWdhcmV0dGVzIGluY3JlYXNlcyAmCiUgRXZlciB0cnlpbmcgb3RoZXIgcHJvZHVjdHMgZGVjcmVhc2VzIiwKICAgICAgICAgc3VidGl0bGUgPSBOVUxMLAogICAgICAgICB5ID0gIiUgb2Ygc3R1ZGVudHMiKQoKcGxvdENfdXcgPC0gcGxvdDggKwogIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgIGxhYnModGl0bGUgPSAiJSBDdXJyZW50bHkgdXNpbmcgZS1jaWdhcmV0dGVzIGluY3JlYXNlcyAmCiUgQ3VycmVudGx5IHVzaW5nIG90aGVyIHByb2R1Y3RzIGRlY3JlYXNlcyIsCiAgICAgICAgIHN1YnRpdGxlID0gTlVMTCwKICAgICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnRpdGxlX3V3IDwtIGdnZHJhdygpICsKICBkcmF3X2xhYmVsKAogICAgIklzIHRoZXJlIGEgcmVsYXRpb25zaGlwIGJldHdlZW4gZS1jaWdhcmV0dGUgdXNlIGFuZCB0b2JhY2NvIHVzZT8iLAogICAgZm9udGZhY2UgPSAnYm9sZCcsCiAgICBzaXplID0gMTQsCiAgICB4ID0gMCwKICAgIGhqdXN0ID0gMAogICkgKwogIHRoZW1lKAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMCwgMCwgMCwgMCkKICApCgpwbG90c0FfdXcgPC0gcGxvdF9ncmlkKHBsb3RBX3V3LAogICAgICAgICAgICAgICAgICAgICByZWxfd2lkdGhzID0gYygxLCAxKSkKcGxvdHNCQ191dyA8LSBwbG90X2dyaWQocGxvdEJfdXcsCiAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RDX3V3LAogICAgICAgICAgICAgICAgICAgICAgICByZWxfd2lkdGhzID0gYygxLCAxKSkKCiMgdGhpcyB3aWxsIHRha2UgdGhlIGxlZ2VuZCBmcm9tIHBsb3QxYyB0byB1c2UgYXMgdGhlIGxlZ2VuZCBmb3IgdGhlIHBsb3Qgd2UgYXJlIGNyZWF0aW5nCmxlZ2VuZF91dyA8LSBnZXRfbGVnZW5kKHBsb3QxYyArCiAgICAgICAgICAgICAgICAgICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGVnZW5kLmRpcmVjdGlvbiA9ICJob3Jpem9udGFsIikpCgpmaWd1cmVfdXcgPC0gcGxvdF9ncmlkKHRpdGxlX3V3LAogICAgICAgICAgICAgICAgICAgICAgIHBsb3RzQV91dywKICAgICAgICAgICAgICAgICAgICAgICBwbG90c0JDX3V3LAogICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF91dywKICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMSwKICAgICAgICAgICAgICAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMC4xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjEpLAogICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMS4wKQoKZmlndXJlX3V3CmBgYAoKCiMjIyAqKlN1cnZleSBXZWlnaHRpbmcqKgoqKioKCioqKgo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgaWYgeW91IHNraXBwZWQgdGhlIHByZXZpb3VzIHNlY3Rpb25zIGFuZCB3YW50IHRvIHN0YXJ0IGhlcmUuIDwvc3VtbWFyeT4KCkZpcnN0IHlvdSBuZWVkIHRvIGluc3RhbGwgYW5kIGxvYWQgdGhlIGBPQ1NkYXRhYCBwYWNrYWdlOgoKYGBge3IsIGV2YWw9RkFMU0V9Cmluc3RhbGwucGFja2FnZXMoIk9DU2RhdGEiKQpsaWJyYXJ5KE9DU2RhdGEpCmBgYAoKVGhlbiwgeW91IG1heSBsb2FkIHRoZSB3cmFuZ2xlZCBkYXRhIGZvciBwbG90cyB1c2luZyB0aGUgZm9sbG93aW5nIGNvZGU6CgpgYGB7ciwgZXZhbD1GQUxTRX0Kd3JhbmdsZWRfcmRhKCJvY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkiLCBvdXRwYXRoID0gZ2V0d2QoKSkKbG9hZChoZXJlOjpoZXJlKCJPQ1NkYXRhIiwgImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YV93aXRoX3Zhcl9mb3JfcGxvdHMucmRhIikpCmBgYAoKSWYgdGhlIHBhY2thZ2UgZG9lcyBub3Qgd29yayBmb3IgeW91LCBhbHRlcm5hdGl2ZWx5LCBhbiBSREEgZmlsZSAoc3RhbmRzIGZvciBSIGRhdGEpIG9mIHRoZSBkYXRhIGNhbiBiZSBmb3VuZCBpbiBvdXIgW0dpdEh1YiByZXBvc2l0b3J5XShodHRwczovL2dpdGh1Yi5jb20vL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkvdHJlZS9tYXN0ZXIvZGF0YS93cmFuZ2xlZCkgb3Igc2xpZ2h0bHkgbW9yZSBkaXJlY3RseSBbaGVyZV0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL29wZW5jYXNlc3R1ZGllcy9vY3MtYnAtdmFwaW5nLWNhc2Utc3R1ZHkvbWFzdGVyL2RhdGEvd3JhbmdsZWQvd3JhbmdsZWRfZGF0YV93aXRoX3Zhcl9mb3JfcGxvdHMucmRhKS4gRG93bmxvYWQgdGhpcyBmaWxlIGFuZCB0aGVuIHBsYWNlIGl0IGluIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSB3aXRoaW4gYSBzdWJkaXJlY3RvcnkgY2FsbGVkICJ3cmFuZ2xlZCIgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAiZGF0YSIgdG8gY29weSBhbmQgcGFzdGUgb3VyIGNvZGUuIFdlIHVzZWQgYW4gUlN0dWRpbyBwcm9qZWN0IGFuZCB0aGUgW2BoZXJlYCBwYWNrYWdlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIG5hdmlnYXRlIHRvIHRoZSBmaWxlIG1vcmUgZWFzaWx5LiAKCmBgYHtyfQpsb2FkKGhlcmU6OmhlcmUoImRhdGEiLCAid3JhbmdsZWQiLCAid3JhbmdsZWRfZGF0YV93aXRoX3Zhcl9mb3JfcGxvdHMucmRhIikpCmBgYAoKPC9kZXRhaWxzPgoKKioqCiAgCkl0IHR1cm5zIG91dCB0aGF0IG91ciBhbmFseXNpcyB0aHVzIGZhciBoYXMgYmVlbiBicnVzaGluZyBhbiBpbXBvcnRhbnQgc3RhdGlzdGljYWwgY29uY2VwdCB1bmRlciB0aGUgcnVnLCByZWxhdGVkIHRvIGhvdyBvdXIgZGF0YSB3ZXJlIGNvbGxlY3RlZC4gT3VyIGRhdGEgY29tZSBmcm9tIHJlc3BvbnNlcyB0byBhIHN1cnZleSwgd2hpY2ggbWF5IGhhdmUgYSBwYXJ0aWN1bGFyIHNhbXBsaW5nIHNjaGVtZSB0byBjYXB0dXJlIGRhdGEgYWJvdXQgdGhlIHBvcHVsYXRpb24gd2UgYXJlIGludGVyZXN0ZWQgaW4uIEZvciBleGFtcGxlLCB0aGUgc3VydmV5IG1heSBiZSBkZXNpZ25lZCB0byBjYXB0dXJlIGEgc2V0IG9mIGluZGl2aWR1YWxzIHdobyByZWZsZWN0IHRoZSBjaGFyYWN0ZXJpc3RpY3Mgb2YgdGhlIHBvcHVsYXRpb24gdGhhdCB3ZSBhcmUgaW50ZXJlc3RlZCBpbiBkcmF3aW5nIGNvbmNsdXNpb25zIGFib3V0LiBIb3dldmVyLCBvbmx5IGEgZnJhY3Rpb24gb2YgdGhlIGluZGl2aWR1YWxzIHdobyB3ZXJlIGNvbnRhY3RlZCBhYm91dCB0YWtpbmcgdGhlIHN1cnZleSBtYXkgaGF2ZSBjb21wbGV0ZWQgaXQsIGFuZCB0aGlzIGZyYWN0aW9uIG9mIGluZGl2aWR1YWxzIG1heSBubyBsb25nZXIgYmUgcmVwcmVzZW50YXRpdmUgb2YgdGhlIHBvcHVsYXRpb24uIE9yIHRoZSBzdXJ2ZXkgbWF5IGJlIGRlc2lnbmVkIHRvIG92ZXItc2FtcGxlIGEgcGFydGljdWxhciBncm91cCBvZiBpbnRlcmVzdCBzbyB0aGF0IGluZGl2aWR1YWxzIGZyb20gdGhhdCBncm91cCBzaG93IHVwIG1vcmUgb2Z0ZW4gYXMgc3VydmV5IHJlc3BvbmRlbnRzIHRoYW4gYXJlIHByZXNlbnQgaW4gdGhlIHBvcHVsYXRpb24gb3ZlcmFsbC4gSW4gb3JkZXIgdG8gYWNjb3VudCBmb3IgdGhlIGZhY3QgdGhhdCB0aGUgc3VydmV5IHJlc3BvbmRlbnRzIG1heSBub3QgcmVmbGVjdCB0aGUgY29tcG9zaXRpb24gb2YgdGhlIHBvcHVsYXRpb24gd2Ugd2FudCB0byBnZW5lcmFsaXplIHRvLCB3ZSBjYW4gZW1wbG95IGEgdGVjaG5pcXVlIGNhbGxlZCBbc3VydmV5IHdlaWdodGluZ10oaHR0cDovL3d3dy5hcHBsaWVkLXN1cnZleS1tZXRob2RzLmNvbS93ZWlnaHQuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4KClN1cnZleSB3ZWlnaHRpbmcgaXMgYSBjb21tb24gdGVjaG5pcXVlIHVzZWQgaW4gc3VydmV5IGRhdGEgYW5hbHlzaXMgYmVjYXVzZSBvZnRlbiB0aGUgaW5kaXZpZHVhbHMgdGhhdCB0YWtlIGEgc3VydmV5IGFyZSBub3QgbmVjZXNzYXJpbHkgcmVwcmVzZW50YXRpdmUgb2YgdGhlIHBvcHVsYXRpb24gdGhhdCB3ZSBhcmUgdHJ5aW5nIHRvIGdhdGhlciBpbmZvcm1hdGlvbiBhYm91dC4gRm9yIGV4YW1wbGUsIHdlIG1heSBoYXZlIG1vcmUgZmVtYWxlcyB0aGF0IHJlc3BvbmQgdG8gdGhlIHN1cnZleSB0aGFuIG1hbGVzIGJlY2F1c2UgcGVyaGFwcyBmZW1hbGUgc3R1ZGVudHMgd2VyZSBtb3JlIHdpbGxpbmcgdG8gcGFydGljaXBhdGUuIEluIHRoaXMgY2FzZSwgdGhlIHByb3BvcnRpb24gb2YgZGF0YSB2YWx1ZXMgaW4gb3VyIGRhdGEgd2lsbCBiZSBzbWFsbGVyIGZvciB0aGUgbWFsZXMgdGhhbiB0aGUgcHJvcG9ydGlvbiBvZiBhY3R1YWwgbWFsZSBzdHVkZW50cyBhbmQgbGFyZ2VyIGZvciB0aGUgZmVtYWxlcyB0aGFuIHRoZSB0cnVlIHByb3BvcnRpb24gb2YgYWN0dWFsIGZlbWFsZSBzdHVkZW50cy4gVG8gZ2V0IGEgYmV0dGVyIGVzdGltYXRlIG9mIG92ZXJhbGwgZS1jaWdhcmV0dGUgc21va2luZyByYXRlcywgdGhlIGRhdGEgZnJvbSB0aGUgbWFsZXMgY2FuIGJlIHdlaWdodGVkIGJhc2VkIG9uIHRoZSB0cnVlIHByb3BvcnRpb24gb2YgbWFsZSBzdHVkZW50cyB0byBhbXBsaWZ5IHRoZSBjb250cmlidXRpb24gb2YgdGhlIHJlc3BvbnNlcyBmcm9tIHRoZSBtYWxlcyB0aGF0IGRpZCBwYXJ0aWNpcGF0ZS4gQ29udmVyc2VseSwgdGhlIGZlbWFsZSBkYXRhIGNhbiBiZSB3ZWlnaHRlZCB0byBkaW1pbmlzaCB0aGUgY29udHJpYnV0aW9uIGlmIHRoZWlyIHJlc3BvbnNlcyB0byB0aGUgb3ZlcmFsbCBwaWN0dXJlLiBXZSB3aWxsIHNlZSBpZiB1c2luZyBzdXJ2ZXkgd2VpZ2h0aW5nIGNoYW5nZXMgdGhlIGdlbmVyYWwgdHJlbmRzIHRoYXQgd2Ugc2VlIGluIG91ciBkYXRhLiAKCkNhbGN1bGF0aW5nIHN1cnZleSB3ZWlnaHRzIGludm9sdmVzIG1ha2luZyBhIHdlaWdodCBiYXNlZCBvbiB0aGUgcmF0aW8gb2YgdGhlIHByb3BvcnRpb24gb2Ygc3VydmV5IHJlc3BvbmRlbnRzIGZyb20gYSBwYXJ0aWN1bGFyIGdyb3VwIGFuZCB0aGUgYWN0dWFsIHByb3BvcnRpb24gb2YgdGhhdCBncm91cCBpbiB0aGUgcG9wdWxhdGlvbi4gRm9yIGV4YW1wbGUsIGxldCdzIHNheSB0aGF0IGZlbWFsZXMgYWNjb3VudCBmb3IgNTAlIG9mIHRoZSBwb3B1bGF0aW9uIGFuZCBtYWxlcyBhY2NvdW50IGZvciA1MCAlIG9mIHRoZSBwb3B1bGF0aW9uLiBMZXQncyBhbHNvIHNheSB0aGF0IDc1JSBvZiB0aGUgcmVzcG9uZGVudHMgdG8gdGhlIHN1cnZleSB3ZXJlIGZlbWFsZSBhbmQgb25seSAyNSUgd2VyZSBtYWxlcy4gCgpUaGVuIHdlIGNvdWxkIGNhbGN1bGF0ZSBzdXJ2ZXkgd2VpZ2h0cyB1c2luZyB0aGlzIGZvcm11bGE6CgokJCBcZnJhY3tcdGV4dHthY3R1YWwgcHJvcG9ydGlvbiBvZiBncm91cCBpbiB0aGUgcG9wdWxhdGlvbn19e1x0ZXh0eyBwcm9wb3J0aW9uIG9mIGdyb3VwIGluIHRoZSByZXNwb25kZW50c319JCQgIAoKVGh1cyB0aGUgd2VpZ2h0IGZvciB0aGUgZmVtYWxlcyB3b3VsZCBiZSBjYWxjdWxhdGVkIGFzOgoKJCQgXGZyYWN7LjV9ey43NX0gPSAuNjckJCAgCgpUaGUgd2VpZ2h0IGZvciB0aGUgbWFsZXMgd291bGQgYmUgY2FsY3VsYXRlZCBhczoKCiQkIFxmcmFjey41fXsuMjV9ID0gMiQkCgpUaGVyZWZvcmUgZWFjaCBtYWxlIHJlc3BvbnNlIHZhbHVlIHdvdWxkIGJlIG11bHRpcGxpZWQgYnkgYSBmYWN0b3Igb2YgMiBhbmQgd291bGQgaGF2ZSB0d2ljZSB0aGUgY29udHJpYnV0aW9uLCB3aGlsZSB0aGUgZmVtYWxlIHJlc3BvbnNlIHZhbHVlcyB3b3VsZCBoYXZlIG9ubHkgYWJvdXQgNzAlIG9mIHRoZSBjb250cmlidXRpb24gdGhhdCB0aGV5IHdvdWxkIGhhdmUgaGFkIHdpdGhvdXQgd2VpZ2h0aW5nLgoKTm90ZSB0aGF0IHN1cnZleSB3ZWlnaHRzIGFyZSBpbiByZWFsaXR5IGNvcnJlY3RlZCBmb3Igb3RoZXIgYXNwZWN0cyAtIGZvciBleGFtcGxlIHRoZSByZXNwb25zZSByYXRlIHRvIGluZGl2aWR1YWwgcXVlc3Rpb25zLgoKV2UgZG8gbm90IG5lZWQgdG8gY2FsY3VsYXRlIHN1cnZleSB3ZWlnaHRzIGZvciBvdXIgZGF0YSBhcyB0aGV5IHdlcmUgYWxyZWFkeSBzdXBwbGllZCBpbiB0aGUgZGF0YXNldCwgYXMgZGVzY3JpYmVkIGluIHRoZSBjb2RlYm9va3MuIAoKIyMjIyBgc3J2eXJgIHBhY2thZ2UgYW5kIHN1cnZleSBkZXNpZ24gCgoqKioKCldlIHdpbGwgbm93IHVzZSB0aGUgYHNydnlyYCBwYWNrYWdlIHRvIGV2YWx1YXRlIG91ciBkYXRhIHVzaW5nIHN1cnZleSB3ZWlnaHRzIHRoYXQgd2VyZSBwcm92aWRlZCBpbiB0aGUgZGF0YSBmb3IgZWFjaCB5ZWFyLCBhcyBkZXNjcmliZWQgaW4gdGhlIHJlc3BlY3RpdmUgY29kZWJvb2tzLiBUaGlzIHBhY2thZ2UgY29udGFpbnMgZnVuY3Rpb25zIHRoYXQgYWxsb3cgdGhlIHVzZXIgdG8gZWFzaWx5IHBlcmZvcm0gY2FsY3VsYXRpb25zIGZyb20gdGhlIGRhdGEgdGhhdCB0YWtlIHRoZSBzdXJ2ZXkgZGVzaWduIGludG8gYWNjb3VudCwgd2l0aG91dCBoYXZpbmcgdG8gd29yayBvdXQgdGhlIG1hdGggYnkgaGFuZC4KCldpdGhpbiB0aGUgZGF0YSB5b3Ugd2lsbCBzZWUgdGhhdCB3ZSBoYXZlIHRocmVlIHZhcmlhYmxlcyByZWxhdGVkIHRvIHRoZSBzdXJ2ZXkgc2FtcGxpbmcgc2NoZW1lOiAgYHBzdWAsIGBmaW53Z3RgLCBhbmQgYHN0cmF0dW1gLiBEZXRhaWxzIGFib3V0IHRoZXNlIHZhcmlhYmxlcyBhcmUgYXZhaWxhYmxlLCBmb3IgZXhhbXBsZSwgaW4gdGhlIFsyMDE5IE1ldGhvZG9sb2d5IFJlcG9ydF0oLi9kb2NzLzIwMTktbnl0cy1kYXRhc2V0LWFuZC1jb2RlYm9vay1taWNyb3NvZnQtZXhjZWwvMjAxOS1ueXRzLW1ldGhvZG9sb2d5LXJlcG9ydC1wLnBkZil7dGFyZ2V0PSJfYmxhbmsifS4KCkluIGJyaWVmIHRoZXkgcmVwcmVzZW50OiAKCjEpIGBwc3VgOiBTdXJ2ZXlzIGxpa2UgdGhlIG9uZSB1c2VkIHRvIGNyZWF0ZSB0aGUgZGF0YSB3ZSBhcmUgdXNpbmcgb2Z0ZW4gc2FtcGxlIHBlb3BsZSBiYXNlZCBvbiBzdHJhdGEuIFRoaXMgaXMgZG9uZSB0byBlbnN1cmUgdGhhdCB0aGUgcmVzcG9uc2VzIGFyZSByZXByZXNlbnRhdGl2ZSBvZiB0aGUgcG9wdWxhdGlvbiBvZiBpbnRlcmVzdC4gVGh1cywgb2Z0ZW4gcGVvcGxlIGZpcnN0IHRoaW5rIGFib3V0IGVuc3VyaW5nIHRoYXQgc3VydmV5cyBhcmUgY29uZHVjdGVkIGluIGEgdmFyaWV0eSBvZiBnZW9ncmFwaGljYWwgYXJlYXMuIFRoaXMgaXMgb2Z0ZW4gY2FsbGVkIHRoZSAqKnByaW1hcnkgc2FtcGxpbmcgdW5pdCoqIG9yICoqUFNVKiouIEluIFt0aGlzIHN1cnZleV0oaHR0cHM6Ly93ZWIuc3BoLmhhcnZhcmQuZWR1L21jaC1kYXRhLWNvbm5lY3QvcmVzdWx0cy9uYXRpb25hbC15b3V0aC10b2JhY2NvLXN1cnZleS1ueXRzLyl7dGFyZ2V0PSJfYmxhbmsifSwgdGhlIGNvdW50eSB3aGVyZSB0aGUgc3R1ZGVudCdzIHNjaG9vbCB3YXMgbG9jYXRlZCB3YXMgdXNlZCBhcyB0aGUgUFNVLiAKCjIpIGBzdHJhdHVtYDogQSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB0aGF0IGluZGljYXRlcyBzdWJzZXRzIG9mIHRoZSBkYXRhIHRoYXQgaW5jbHVkZSByZXNwb25kZW50cyBmcm9tIGRpZmZlcmVudCAqUFNVcyouIEluIG91ciBjYXNlLCBzdHJhdGEgYXJlIGRldGVybWluZWQgYnkgdGhlIHByZWRvbWluYW50IG1pbm9yaXR5IGluIHRoZSBQU1UgKE5vbi1IaXNwYW5pYyBCbGFjayBvciBIaXNwYW5pYyksIHdoZXRoZXIgdGhlIFBTVSBpcyB1cmJhbiBvciBub24tdXJiYW4sIGFuZCB3aGF0IHBlcmNlbnQgb2YgdGhlIHN0dWRlbnRzIGluIHRoZSBQU1UgZmFsbCBpbnRvIHRoZSBwcmVkb21pbmFudCBtaW5vcml0eSBncm91cC4gUFNVcyBhcmUgYWxsb2NhdGVkIGFjcm9zcyB0aGUgMTYgcG9zc2libGUgc3RyYXRhIGFjY29yZGluZyB0byB0aGUgc2FtcGxpbmcgc2NoZW1lLiBUaGVzZSBzdHJhdGEgdmFsdWVzIGFsbG93IGVzdGltYXRlcyBiYXNlZCBvbiB0aGUgc3VydmV5IHJlc3BvbnNlcyB0byBiZSBjYWxjdWxhdGVkIHVzaW5nIGRpZmZlcmVudCBzdHJhdGEgYWxsb3dpbmcgZm9yIGltcHJvdmVkIHByZWNpc2lvbiBvZiB0aGUgcmVzcG9uc2UgZXN0aW1hdGVzLgoKMykgYGZpbndndGA6IFRoZSBzdXJ2ZXkgd2VpZ2h0IHdoaWNoIHdhcyBjYWxjdWxhdGVkIGJhc2VkIG9uIGEgdmFyaWV0eSBvZiBmYWN0b3JzLgoKVGhpcyBbbGlua10oaHR0cHM6Ly93ZWIuc3BoLmhhcnZhcmQuZWR1L21jaC1kYXRhLWNvbm5lY3QvcmVzdWx0cy9uYXRpb25hbC15b3V0aC10b2JhY2NvLXN1cnZleS1ueXRzLyl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgdGhpcyBbbGlua10oaHR0cHM6Ly9vc2YuaW8vbjdyMzIpe3RhcmdldD0iX2JsYW5rIn0gaGF2ZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBzdHVkeSBkZXNpZ24gb2YgdGhlIGRhdGEgdGhhdCB3ZSBhcmUgdXNpbmcuCgpGb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb24gc3VjaCBzdXJ2ZXkgZGVzaWducyBpbiBnZW5lcmFsIHNlZSBbaGVyZV0oaHR0cDovL3d3dy5hc2Fzcm1zLm9yZy9Qcm9jZWVkaW5ncy95MjAwOC9GaWxlcy8zMDE4MzUucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZCBbaGVyZV0oaHR0cDovL29jdy5qaHNwaC5lZHUvY291cnNlcy9TdGF0TWV0aG9kc0ZvclNhbXBsZVN1cnZleXMvUERGcy9MZWN0dXJlNS5wZGYpe3RhcmdldD0iX2JsYW5rIn0uCgpXZSB3aWxsIHVzZSB0aGUgYGFzX3N1cnZleV9kZXNpZ24oKWAgZnVuY3Rpb24gb2YgdGhlIGBzcnZ5cmBwYWNrYWdlIHRvIGNyZWF0ZSBhIHN1cnZleSBvYmplY3Qgd2l0aCBhIHNwZWNpZmllZCBzdXJ2ZXkgZGVzaWduLiBUaGlzIGlzIGEgc3BlY2lhbCBSIG9iamVjdCB0aGF0IGluY2x1ZGVzIGluZm9ybWF0aW9uIGFib3V0IGhvdyB0aGUgc3VydmV5IHdhcyBjb25kdWN0ZWQgdGhhdCBjYW4gYmUgdGFrZW4gaW50byBhY2NvdW50IGluIHRoZSBhbmFseXNpcy4KClRoZXJlIGFyZSBzZXZlcmFsIGFyZ3VtZW50cyB0byBwYXkgYXR0ZW50aW9uIHRvOgoKMSkgVGhlIGBzdHJhdGFgIGFyZ3VtZW50IGlzIHVzZWQgdG8gc3BlY2lmeSB0aGUgdmFyaWFibGUocykgdGhhdCBkZWZpbmVkIHN0cmF0YSBpbiB0aGUgZGF0YS4gSW4gdGhpcyBjYXNlLCB3ZSB3aWxsIHVzZSB0aGUgYHN0cmF0dW1gIHZhcmlhYmxlLgoyKSBUaGUgYGlkc2AgYXJndW1lbnQgaXMgdXNlZCB0byBkZWZpbmUgY2x1c3RlciBpZHMgd2l0aGluIHRoZSBkYXRhLiBJbiB0aGlzIGNhc2Ugd2Ugd2lsbCB1c2UgdGhlIGBwc3VgIHZhcmlhYmxlLgozKSBUaGUgYHdlaWdodGAgYXJndW1lbnQgaXMgdGhlICB1c2VkIHRvIGRlZmluZSB3aGljaCB2YXJpYWJsZShzKSBhcmUgdGhlIHN1cnZleSB3ZWlnaHRzLgo0KSBUaGUgYG5lc3QgPSBUUlVFYCBhcmd1bWVudCwgZm9yY2VzIGNsdXN0ZXIgaWRzIChpbiB0aGlzIGNhc2UgdGhlIFBTVSkgdG8gYmUgbmVzdGVkIHdpdGhpbiB0aGUgc3RyYXRhLgoKV2UgY2FuIHRoZW4gdXNlIHRoZSBgc3VydmV5X21lYW4oKWAgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHBlcmNlbnRhZ2VzIG9mIHN0dWRlbnRzIHdobyByZXBvcnQgdXNpbmcgdG9iYWNjbyBmb3IgZWFjaCB5ZWFyIHdoaWxlIGFjY291bnRpbmcgZm9yIHRoZSBzdXJ2ZXkgZGVzaWduIGFuZCB3ZWlnaHRzLiBXZSB3aWxsIHNwZWNpZnkgdGhhdCB3ZSB3YW50IFtjb25maWRlbmNlIGludGVydmFsXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Db25maWRlbmNlX2ludGVydmFsKXt0YXJnZXQ9Il9ibGFuayJ9IGVzdGltYXRlcyBieSB1c2luZyB0aGUgYHZhcnR5cGUgPSAiY2kiYCBhcmd1bWVudC4gVGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGluIG91ciBjYXNlIGdpdmUgYSByYW5nZSBvZiBwb3NzaWJsZSB2YWx1ZXMgZm9yIHRoZSB0cnVlIHBvcHVsYXRpb24gbWVhbiBiYXNlZCBvbiB0aGUgZGF0YSBvYnNlcnZlZCBpbiB0aGUgc3VydmV5LiBXZSB3aWxsIG11bHRpcGx5IHRoZXNlIHZhbHVlcyBieSAxMDAgdG8gZ2V0IHBlcmNlbnRhZ2VzLiAoTm90ZTogV2UgY291bGQgYWxzbyBoYXZlIGNhbGN1bGF0ZWQgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSB1bndlaWdodGVkIHJlc3VsdHMgYWJvdmUgYnkgY29tcHV0aW5nIHRoZW0gYnkgaGFuZDsgd2UgbGVhdmUgdGhpcyBhcyBhIHBvdGVudGlhbCBleGVyY2lzZS4pCgpTaW5jZSB0aGUgc3VydmV5IHdlaWdodHMgYXJlIHNwZWNpZmljIHRvIGEgc2luZ2xlIHllYXIgb2YgdGhlIHN1cnZleSByZXN1bHRzLCB3ZSBuZWVkIHRvIGNyZWF0ZSBzdXJ2ZXkgZGVzaWduIG9iamVjdHMgZm9yIGVhY2ggeWVhciBzZXBhcmF0ZWx5LiBXZSB3aWxsIHVzZSBgZ3JvdXBfYnlgIGFuZCBgZ3JvdXBfbW9kaWZ5YCwgd2hpY2ggaXMgYWxzbyBmcm9tIHRoZSBgZHBseXJgIHBhY2thZ2UsIHRvIGRvIHRoaXMuIFdlIGZpcnN0IHdyaXRlIHRoZSBmdW5jdGlvbiB0aGF0IHdlIHdhbnQgdG8gY2FsbCBvbiBlYWNoIGdyb3VwLgoKVGhpcyBmdW5jdGlvbiB0YWtlcyBhbiBpbnB1dCBjYWxsZWQgYGN1cnJZZWFyYCwgd2hpY2ggd2lsbCBiZSBvbmUgc2V0IG9mIHN1cnZleSByZXNwb25zZXMgZm9yIGEgc3BlY2lmaWMgeWVhciwgYW5kIHRoZW4gY3JlYXRlcyBhIHN1cnZleSBkZXNpZ24gYmFzZWQgb24gdGhlIGBzdHJhdHVtYCBhbmQgYGZpbndndGAgdmFsdWVzIHNwZWNpZmljIHRvIHRoYXQgeWVhci4gSXQgdGhlbiBjYWxjdWxhdGVzIHRoZSBwZXJjZW50IG9mIHN0dWRlbnQgcmVzcG9uZGVudHMgd2hvIGhhdmUgZXZlciB0cmllZCBhbnkgdG9iYWNjbyBwcm9kdWN0cyBvciB3aG8gYXJlIGEgY3VycmVudCB1c2VyIG9mIGFueSB0b2JhY2NvIHByb2R1Y3RzIGFjY291bnRpbmcgZm9yIHRoZSBzdXJ2ZXkgZGVzaWduIGFuZCB3ZWlnaHRzIHVzaW5nIGBzdXJ2ZXlfbWVhbigpYCBhcyB3YXMganVzdCBkZXNjcmliZWQuIFRoZSBmdW5jdGlvbiB0aGVuIHdyYW5nbGVzIHRoZSBkYXRhIHRvIGNvbnZlcnQgdGhlIG1lYW5zIHRvIHBlcmNlbnRhZ2VzIGFuZCByZWZvcm1hdCB0aGUgZGF0YSBpbiBsb25nIGZvcm0gZm9yIHBsb3R0aW5nLgoKT25lIHRlY2huaWNhbCBub3RlOiBzaW5jZSBzb21lIHllYXJzIGhhdmUgc3RyYXRhIHdpdGggYSBzaW5nbGUgUFNVLCB3ZSBuZWVkIHRvIHRlbGwgdGhlIHN1cnZleSB3ZWlnaHRpbmcgcGFja2FnZSBob3cgdG8gaGFuZGxlIGVzdGltYXRpbmcgd2l0aGluIHN0cmF0YSB2YXJpYW5jZXMuIFRoZSBsaW5lIGBvcHRpb25zKHN1cnZleS5sb25lbHkucHN1ID0gImFkanVzdCIpYCB0ZWxscyBSIHRvIGNlbnRlciB0aGUgc3RyYXR1bSB3aXRoIHRoZSBzaW5nbGUgUFNVIG9uIHRoZSBzYW1wbGUgZ3JhbmQgbWVhbiwgYSBjb25zZXJ2YXRpdmUgYXBwcm9hY2ggdG8gc29sdmluZyB0aGUgcHJvYmxlbS4gU2VlIGZ1cnRoZXIgaW5mb3JtYXRpb24gW2hlcmVdKGh0dHBzOi8vci1zdXJ2ZXkuci1mb3JnZS5yLXByb2plY3Qub3JnL3N1cnZleS9leG1hbXBsZS1sb25lbHkuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgW2hlcmVdKGh0dHA6Ly9yLXN1cnZleS5yLWZvcmdlLnItcHJvamVjdC5vcmcvc3VydmV5L2h0bWwvc3VydmV5b3B0aW9ucy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9LgoKIyMjICoqV2VpZ2h0ZWQgU2FtcGxlKioKKioqCgpGaXJzdCwgd2Ugc2hvdyB0aGUgYmFzaWMgb3V0cHV0IG9mIHRoZSBgc3VydmV5X21lYW5gIGZ1bmN0aW9uIGJ5IHllYXIuIFNpbmNlIHdlIGluY2x1ZGUgdGhlIGFyZ3VtZW50IGB2YXJ0eXBlID0gImNpImAsIHdlIGdldCBhIG1lYW4gYW5kIHVwcGVyIGFuZCBsb3dlciBjb25maWRlbmNlIGludGVydmFsIGJvdW5kcyBmb3IgdGhlIG1lYW4uCgpgYGB7cn0Kc3VydmV5TWVhbkEgPC0gZnVuY3Rpb24oY3VyclllYXIpIHsKICBvcHRpb25zKHN1cnZleS5sb25lbHkucHN1ID0gImFkanVzdCIpCiAgY3VyclllYXIgJT4lCiAgYXNfc3VydmV5X2Rlc2lnbihzdHJhdGEgPSBzdHJhdHVtLAogICAgICAgICAgICAgICAgICAgICAgaWRzID0gcHN1LAogICAgICAgICAgICAgICAgICB3ZWlnaHQgID0gZmlud2d0LAogICAgICAgICAgICAgICAgICAgICBuZXN0ID0gVFJVRSkgJT4lCiAgIHN1bW1hcml6ZSh0b2JhY2NvX2V2ZXIgPSBzdXJ2ZXlfbWVhbih0b2JhY2NvX2V2ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJ0eXBlID0gImNpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSksCiAgICAgICAgICB0b2JhY2NvX2N1cnJlbnQgPSBzdXJ2ZXlfbWVhbih0b2JhY2NvX2N1cnJlbnQsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJ0eXBlID0gImNpIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gVFJVRSkpIH0KCgpueXRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgZHBseXI6Omdyb3VwX21vZGlmeSh+IHN1cnZleU1lYW5BKC54KSkgJT4lCiAgaGVhZCgpCmBgYAoKTm93IGxldCdzIG1ha2UgdGhlIGZ1bmN0aW9uIHdyYW5nbGUgdGhlIG91dHB1dCBpbiBhIG1vcmUgdXNhYmxlIGZvcm0gdG9vOgpgYGB7cn0KCnN1cnZleU1lYW5BIDwtIGZ1bmN0aW9uKGN1cnJZZWFyKSB7CiAgb3B0aW9ucyhzdXJ2ZXkubG9uZWx5LnBzdSA9ICJhZGp1c3QiKQogIGN1cnJZZWFyICU+JQogIGFzX3N1cnZleV9kZXNpZ24oc3RyYXRhID0gc3RyYXR1bSwKICAgICAgICAgICAgICAgICAgICAgIGlkcyA9IHBzdSwKICAgICAgICAgICAgICAgICAgd2VpZ2h0ICA9IGZpbndndCwKICAgICAgICAgICAgICAgICAgICAgbmVzdCA9IFRSVUUpICU+JQogICBzdW1tYXJpemUodG9iYWNjb19ldmVyID0gc3VydmV5X21lYW4odG9iYWNjb19ldmVyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFydHlwZSA9ICJjaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgdG9iYWNjb19jdXJyZW50ID0gc3VydmV5X21lYW4odG9iYWNjb19jdXJyZW50LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFydHlwZSA9ICJjaSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSAgJT4lCiAgbXV0YXRlX2FsbCgiKiIsIDEwMCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVHlwZSIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJQZXJjZW50YWdlIG9mIHN0dWRlbnRzIikgJT4lCiAgbXV0YXRlKEVzdGltYXRlID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoVHlwZSwgIl9sb3ciKSB+ICJMb3dlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoVHlwZSwgIl91cHAiKSB+ICJVcHBlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJNZWFuIiksCiAgICAgICAgIFVzZXIgPSBjYXNlX3doZW4oc3RyX2RldGVjdChUeXBlLCAiZXZlciIpIH4gIkV2ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoVHlwZSwgImN1cnJlbnQiKSB+ICJDdXJyZW50IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gIk1lYW4iKSl9CgpueXRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgZ3JvdXBfbW9kaWZ5KH4gc3VydmV5TWVhbkEoLngpKQpgYGAKCldlIHdpbGwgbm93IG1ha2UgYSBwbG90IHVzaW5nIHRoaXMgZGF0YS4gVGhlIGNvbmZpZGVuY2UgaW50ZXJ2YWxzIGFyZSBpbmNsdWRlZCB1c2luZyB0aGUgYGdlb21fbGluZXJhbmdlKClgIGZ1bmN0aW9uIG9mIHRoZSBgZ2dwbG90MmAgcGFja2FnZS4KCmBgYHtyfQpwbG90QV93IDwtIG55dHNfZGF0YSAlPiUKICBncm91cF9ieSh5ZWFyKSAlPiUKICBncm91cF9tb2RpZnkofiBzdXJ2ZXlNZWFuQSgueCkpICU+JQogIGRwbHlyOjpzZWxlY3QoLVR5cGUpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFc3RpbWF0ZSwKICAgICAgICAgICAgIHZhbHVlc19mcm9tID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gTWVhbikpICsKICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlID0gVXNlcikpICsKICBnZW9tX2xpbmVyYW5nZShhZXMoeW1pbiA9IExvd2VyLAogICAgICAgICAgICAgICAgICAgICB5bWF4ID0gVXBwZXIpLCAKICAgICAgICAgICAgICAgICAgICAgc2l6ZSA9IDEsIAogICAgICAgICAgICAgIHNob3cubGVnZW5kID0gRkFMU0UpICsKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygyLCAxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgNzAsIGJ5ID0gMTApLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzZXEoMCwgNzAsIGJ5ID0gMTApLAogICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDcwKSkgKwogICAgdGhlbWVfbGluZWRyYXcoKSArCiAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICAgIGxhYnModGl0bGUgPSAiVG9iYWNjbyBwcm9kdWN0IHVzZXJzIG1vcmUgcHJldmFsZW50IGFmdGVyIDIwMTciLAogICAgICAgICB5ID0gIiUgb2Ygc3R1ZGVudHMiKQpwbG90QV93ICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCk5vdyB3ZSBjYW4gc2VlIHRoYXQgd2UgaGF2ZSBjb25maWRlbmNlIGludGVydmFsIHJhbmdlcyBwbG90dGVkIGZvciBlYWNoIHZhbHVlLiAKCldlIHdpbGwgbWFrZSBhIHNpbWlsYXIgcGxvdCBmb3Igc3R1ZGVudHMgd2hvIHJlcG9ydGVkIGV2ZXIgdHJ5aW5nIG9yIHdobyBjdXJyZW50bHkgdXNlIGUtY2lnYXJldHRlcyBhcyBvcHBvc2VkIHRvIHRvYmFjY28gaW4gZ2VuZXJhbC4KCmBgYHtyfQp2X2NvbG9ycyA9ICB2aXJpZGlzKDYpW2MoMSwgNCldCgpzdXJ2ZXlNZWFuQiA8LSBmdW5jdGlvbihjdXJyWWVhcikgewogIG9wdGlvbnMoc3VydmV5LmxvbmVseS5wc3UgPSAiYWRqdXN0IikKICBjdXJyWWVhciAlPiUKICBhc19zdXJ2ZXlfZGVzaWduKHN0cmF0YSA9IHN0cmF0dW0sCiAgICAgICAgICAgICAgICAgICAgICBpZHMgPSBwc3UsCiAgICAgICAgICAgICAgICAgIHdlaWdodCAgPSBmaW53Z3QsCiAgICAgICAgICAgICAgICAgICAgIG5lc3QgPSBUUlVFKSAlPiUKICBzdW1tYXJpemUoZWNpZ19ldmVyX3llYXIgPSBzdXJ2ZXlfbWVhbihlY2lnX2V2ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnR5cGUgPSAiY2kiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpLAogICAgICAgIG5vbl9lY2lnX2V2ZXJfeWVhciA9IHN1cnZleV9tZWFuKG5vbl9lY2lnX2V2ZXIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnR5cGUgPSAiY2kiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGVfYWxsKCIqIiwgMTAwKSAlPiUKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLAogICAgICAgICAgIG5hbWVzX3RvID0gIkNhdGVnb3J5IiwKICAgICAgICAgIHZhbHVlc190byA9ICJQZXJjZW50YWdlIG9mIHN0dWRlbnRzIikgJT4lCiAgbXV0YXRlKEVzdGltYXRlID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJfbG93IikgfiAiTG93ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfZGV0ZWN0KENhdGVnb3J5LCAiX3VwcCIpIH4gIlVwcGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJNZWFuIiksCiAgICAgICAgICAgICBVc2VyID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJldmVyIikgfiAiRXZlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJjdXJyZW50IikgfiAiQ3VycmVudCIpLAogICAgICBQcm9kdWN0ID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJub25fZWNpZyIpIH4gIk90aGVyIHByb2R1Y3RzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJFLWNpZ2FyZXR0ZXMiKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtQ2F0ZWdvcnkpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFc3RpbWF0ZSwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGBQZXJjZW50YWdlIG9mIHN0dWRlbnRzYCl9CgpueXRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgZ3JvdXBfbW9kaWZ5KCB+IHN1cnZleU1lYW5CKC54KSkgJT4lCiAgaGVhZCgpCgoKcGxvdEJfdyA8LSBueXRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgZ3JvdXBfbW9kaWZ5KCB+IHN1cnZleU1lYW5CKC54KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IE1lYW4sIGNvbG9yID0gUHJvZHVjdCkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9saW5lcmFuZ2UoYWVzKHltaW4gPSBMb3dlciwgeW1heCA9IFVwcGVyKSwKICAgICAgICAgICAgICAgICBzaXplID0gMSwKICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMiwgMSkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdl9jb2xvcnMpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoCiAgICBicmVha3MgPSBzZXEoMCwgNjAsIGJ5ID0gMTApLAogICAgbGFiZWxzID0gc2VxKDAsIDYwLCBieSA9IDEwKSwKICAgIGxpbWl0cyA9IGMoMCwgNjApCiAgKSArCiAgdGhlbWVfbGluZWRyYXcoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSkgKwogIGxhYnModGl0bGUgPSAiJSBFdmVyIHRyeWluZyBlLWNpZ2FyZXR0ZXMgaW5jcmVhc2VzICYKJSBFdmVyIHRyeWluZyBvdGhlciBwcm9kdWN0cyBkZWNyZWFzZXMiLAogICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3RCX3cgKyB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSkpCmBgYAoKCk5vdyB3ZSB3aWxsIGRvIHRoZSBzYW1lIGJ1dCBmb3IgY3VycmVudCB1c2VyczoKCmBgYHtyfQpzdXJ2ZXlNZWFuQyA8LSBmdW5jdGlvbihjdXJyWWVhcikgewogIG9wdGlvbnMoc3VydmV5LmxvbmVseS5wc3UgPSAiYWRqdXN0IikKICBjdXJyWWVhciAlPiUKICBhc19zdXJ2ZXlfZGVzaWduKHN0cmF0YSA9IHN0cmF0dW0sCiAgICAgICAgICAgICAgICAgICAgICBpZHMgPSBwc3UsCiAgICAgICAgICAgICAgICAgIHdlaWdodCAgPSBmaW53Z3QsCiAgICAgICAgICAgICAgICAgICAgIG5lc3QgPSBUUlVFKSAlPiUKICBzdW1tYXJpemUoZWNpZ19jdXJyZW50X3llYXIgPSBzdXJ2ZXlfbWVhbihlY2lnX2N1cnJlbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnR5cGUgPSAiY2kiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpLAogICAgICAgIG5vbl9lY2lnX2N1cnJlbnRfeWVhciA9IHN1cnZleV9tZWFuKG5vbl9lY2lnX2N1cnJlbnQsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhcnR5cGUgPSAiY2kiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpKSAlPiUKICBtdXRhdGVfYWxsKCIqIiwgMTAwKSAlPiUKICBwaXZvdF9sb25nZXIoZXZlcnl0aGluZygpLAogICAgICAgICAgIG5hbWVzX3RvID0gIkNhdGVnb3J5IiwKICAgICAgICAgIHZhbHVlc190byA9ICJQZXJjZW50YWdlIG9mIHN0dWRlbnRzIikgJT4lCiAgbXV0YXRlKEVzdGltYXRlID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJfbG93IikgfiAiTG93ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfZGV0ZWN0KENhdGVnb3J5LCAiX3VwcCIpIH4gIlVwcGVyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJNZWFuIiksCiAgICAgICAgICAgICBVc2VyID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJldmVyIikgfiAiRXZlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJjdXJyZW50IikgfiAiQ3VycmVudCIpLAogICAgICBQcm9kdWN0ID0gY2FzZV93aGVuKHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJub25fZWNpZyIpIH4gIk90aGVyIHByb2R1Y3RzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJFLWNpZ2FyZXR0ZXMiKSkgJT4lCiAgZHBseXI6OnNlbGVjdCgtQ2F0ZWdvcnkpICU+JQogIHBpdm90X3dpZGVyKG5hbWVzX2Zyb20gPSBFc3RpbWF0ZSwKICAgICAgICAgICAgICB2YWx1ZXNfZnJvbSA9IGBQZXJjZW50YWdlIG9mIHN0dWRlbnRzYCl9CgoKcGxvdENfdyA8LSBueXRzX2RhdGEgJT4lCiAgZ3JvdXBfYnkoeWVhcikgJT4lCiAgZ3JvdXBfbW9kaWZ5KCB+IHN1cnZleU1lYW5DKC54KSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IE1lYW4sIGNvbG9yID0gUHJvZHVjdCkpICsKICBnZW9tX2xpbmUoYWVzKGxpbmV0eXBlID0gImRhc2hlZCIpKSArCiAgZ2VvbV9saW5lcmFuZ2UoYWVzKHltaW4gPSBMb3dlciwgeW1heCA9IFVwcGVyKSwKICAgICAgICAgICAgICAgICBzaXplID0gMSwKICAgICAgICAgICAgICAgICBzaG93LmxlZ2VuZCA9IEZBTFNFKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMiwgMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDYwLCBieSA9IDEwKSwgbGltaXRzID0gYygwLCA2MCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdl9jb2xvcnMpICsKICB0aGVtZV9saW5lZHJhdygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICIlIEN1cnJlbnRseSB1c2luZyBlLWNpZ2FyZXR0ZXMgaW5jcmVhc2VzICYKJSBDdXJyZW50bHkgdXNpbmcgb3RoZXIgcHJvZHVjdHMgZGVjcmVhc2VzIiwKICAgICAgICB5ID0gIiUgb2Ygc3R1ZGVudHMiKQpwbG90Q193ICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTUpKQpgYGAKCmBgYHtyLCBlY2hvID0gRkFMU0V9CiNjb2RlIHRvIGNyZWF0ZSBwcm9wZXIgbGVnZW5kIGZvciBpbnN0cnVjdG9ycyBzdGFydGluZyBhdCBzdXJ2ZXkgd2VpZ2h0aW5nIHNlY3Rpb24KCnBsb3QxYyA8LSBueXRzX2RhdGEgJT4lCiAgICBkcGx5cjo6Z3JvdXBfYnkoeWVhcikgJT4lCiAgICBkcGx5cjo6c3VtbWFyaXplKCJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKV9FLWNpZ2FyZXR0ZSIgPSAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbihlY2lnX29ubHlfZXZlciwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICJDdXJyZW50IFxuIChhbnkgcGFzdC0zMC1kYXkgdXNlKV9FLWNpZ2FyZXR0ZSIgPSAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbihlY2lnX29ubHlfY3VycmVudCwgbmEucm0gPSBUUlVFKSAqIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICJFdmVyIFxuIChhbnkgbGlmZXRpbWUgdXNlKV9Ob24tZS1jaWdhcmV0dGUiID0gCiAgICAgICAgICAgICAgICAgICAgICAgKG1lYW4obm9uX2VjaWdfb25seV9ldmVyLCBuYS5ybSA9IFRSVUUpICogMTAwKSwKICAgICAgICAgICAgICAgICAgICAgIkN1cnJlbnQgXG4gKGFueSBwYXN0LTMwLWRheSB1c2UpX05vbi1lLWNpZ2FyZXR0ZSIgPSAKICAgICAgICAgICAgICAgICAgICAgICAobWVhbihub25fZWNpZ19vbmx5X2N1cnJlbnQsIG5hLnJtID0gVFJVRSkgKiAxMDApKSAlPiUKICBwaXZvdF9sb25nZXIoY29scyA9IC0geWVhciwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiVXNlciIsCiAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJQZXJjZW50YWdlIG9mIHN0dWRlbnRzIikgJT4lCiAgc2VwYXJhdGUoVXNlciwgaW50byA9IGMoIlVzZXIiLCAiUHJvZHVjdCIpLCBzZXAgPSAiXyIpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsCiAgICAgICAgICAgICB5ID0gYFBlcmNlbnRhZ2Ugb2Ygc3R1ZGVudHNgLAogICAgICAgICAgICAgY29sb3IgPSBQcm9kdWN0KSkgKwogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBVc2VyKSkgKwogIGdlb21fcG9pbnQoc2hvdy5sZWdlbmQgPSBGQUxTRSwgc2l6ZSA9IDIpICsKICAjIHRoaXMgYWxsb3dzIHVzIHRvIGNob29zZSB3aGF0IHR5cGUgb2YgbGluZSB3ZSB3YW50IGZvciBlYWNoIGxpbmUKICBzY2FsZV9saW5ldHlwZV9tYW51YWwodmFsdWVzID0gYygyLCAxKSkgKwogICMgdGhpcyBhbGxvd3MgdXMgdG8gc3BlY2lmeSBob3cgdGhlIHktYXhpcyBzaG91bGQgYXBwZWFyCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAzMCwgYnkgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IHNlcSgwLCAzMCwgYnkgPSAxMCksCiAgICAgICAgICAgICAgICAgICAgICAgIGxpbWl0cyA9IGMoMCwgMzApKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHZfY29sb3JzKSArCiAgIyB0aGlzIGFkanVzdHMgdGhlIGJhY2tncm91bmQgc3R5bGUgb2YgdGhlIHBsb3QKICB0aGVtZV9saW5lZHJhdygpICsKICAjIHRoaXMgbW92ZXMgdGhlIGxlZ2VuZCB0byB0aGUgYm90dG9tIG9mIHRoZSBwbG90IGFuZCByZW1vdmVzIHRoZSB4IGF4aXMgdGl0bGUKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIkhvdyBoYXMgdXNlIG9mIG9ubHkgZS1jaWdhcmV0dGVzIGFuZApvbmx5IHRvYmFjY28gcHJvZHVjdHMgYmVzaWRlcyBlLWNpZ2FyZXR0ZXMgdmFyaWVkIG92ZXIgdGltZT8iLAogICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCmBgYAoKCgoKTm93IHdlIHdpbGwgcHV0IHRoZXNlIHBsb3RzIHRvZ2V0aGVyIGFnYWluIHVzaW5nIHRoZSBgY293cGxvdGAgcGFja2FnZToKCmBgYHtyfQp0aXRsZV93IDwtIGdnZHJhdygpICsKICBkcmF3X2xhYmVsKAogICAgZXhwcmVzc2lvbigiV2hhdCBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZS1jaWdhcmV0dGUgdXNlIGFuZCB0b2JhY2NvIHVzZT8iKSwKICAgIGZvbnRmYWNlID0gJ2JvbGQnLAogICAgc2l6ZSA9IDE0LAogICAgeCA9IDAsCiAgICBoanVzdCA9IDAKICApICsKICB0aGVtZSgKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDApCiAgKQoKcGxvdHNBX3cgPC0gcGxvdF9ncmlkKHBsb3RBX3csCiAgICAgICAgICAgICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDEpLAogICAgICAgICAgICAgICAgICAgICBhbGlnbiA9ICJ2IiwKICAgICAgICAgICAgICAgICAgICAgYXhpcyA9ICJidCIpCnBsb3RzQkNfdyA8LSBwbG90X2dyaWQocGxvdEJfdywKICAgICAgICAgICAgICAgICAgICAgcGxvdENfdywKICAgICAgICAgICAgICAgICAgICAgcmVsX3dpZHRocyA9IGMoMSwgMSksCiAgICAgICAgICAgICAgICAgICAgIGFsaWduID0gInYiLAogICAgICAgICAgICAgICAgICAgICBheGlzID0gImJ0IikKCmxlZ2VuZF93IDwtIGdldF9sZWdlbmQocGxvdDFjICsKICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSkKCmZpZ3VyZV93IDwtIHBsb3RfZ3JpZCh0aXRsZV93LAogICAgICAgICAgICAgICAgICAgICAgcGxvdHNBX3csCiAgICAgICAgICAgICAgICAgICAgICBwbG90c0JDX3csCiAgICAgICAgICAgICAgICAgICAgICBsZWdlbmRfdywKICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgICAgICAgICAgICAgcmVsX2hlaWdodHMgPSBjKDAuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMC4xKSwKICAgICAgICAgICAgICAgICAgICAgIHNjYWxlID0gMS4wKQoKZmlndXJlX3cKYGBgCgpXZSBjYW4gc2VlIHRoYXQgdGhlc2UgZmlndXJlcyBsb29rIHF1aXRlIHNpbWlsYXIgdG8gdGhlIG9uZXMgZ2VuZXJhdGVkIHdpdGhvdXQgdXNpbmcgdGhlIHN1cnZleSB3ZWlnaHRzLgoKIyMjICoqQXJ0aWZpY2lhbCBDb2hvcnQqKgoqKioKCkFsdGhvdWdoIHRoZSBzdXJ2ZXkgZGVzaWduIGRvZXMgbm90IGFsbG93IHNwZWNpZmljIGluZGl2aWR1YWxzIHRvIGJlIGZvbGxvd2VkIG92ZXIgdGltZSwgd2Ugd2lsbCB1c2UgY2VydGFpbiBzdWJzZXRzIG9mIHRoZSBkYXRhIGZyb20gZWFjaCB5ZWFyIHRvIGNvbnN0cnVjdCBhbiBhcnRpZmljaWFsIGNvaG9ydCB3aGVyZSB3ZSBmb2xsb3cgc3R1ZGVudHMgb2YgdGhlIHNhbWUgYWdlIGdyb3VwIGFzIHRoZXkgZ2V0IG9sZGVyLiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gbG9vayBhdCBob3cgdG9iYWNjbyB1c2FnZSBjaGFuZ2VkIGZvciBzdHVkZW50cyB3aG8gd2VyZSBpbiA4dGggZ3JhZGUgaW4gMjAxNSBhcyB0aGV5IGFnZWQuIAoKQWxsIG9mIHRoZSBkYXRhIHNvIGZhciBoYXMgaW5jbHVkZWQgYWxsIDZ0aC0xMnRoIGdyYWRlcnMgZXZlcnkgeWVhci4gTm93IHdlIHdpbGwgbG9vayBhdCBqdXN0IHRoZSBkYXRhIGZvciBzdHVkZW50cyBleHBlY3RlZCB0byBncmFkdWF0ZSBpbiAyMDE5LiBUaGVzZSBhcmUgdGhlIHN0dWRlbnRzIHdobyB3ZXJlIGluIDh0aCBncmFkZSBpbiAyMDE1LCBtb3N0IG9mIHdob20gd2VyZSA5dGggZ3JhZGVycyBpbiAyMDE2LCAxMHRoIGdyYWRlcnMgaW4gMjAxNyBhbmQgc28gb24uIFdlIHdpbGwgZmlsdGVyIHRoZSBkYXRhIHRvIGp1c3QgdGhlIHN0dWRlbnRzIGV4cGVjdGVkIHRvIGJlIGluIHRoZSBncmFkdWF0aW5nIGNsYXNzIG9mIDIwMTkuCgoKYGBge3J9CgpzdXJ2ZXlNZWFuQ29ob3J0IDwtIGZ1bmN0aW9uKGN1cnJZZWFyKSB7CiAgb3B0aW9ucyhzdXJ2ZXkubG9uZWx5LnBzdSA9ICJhZGp1c3QiKQogIGN1cnJZZWFyICU+JQogIGFzX3N1cnZleV9kZXNpZ24oc3RyYXRhID0gc3RyYXR1bSwKICAgICAgICAgICAgICAgICAgICAgIGlkcyA9IHBzdSwKICAgICAgICAgICAgICAgICAgd2VpZ2h0ICA9IGZpbndndCwKICAgICAgICAgICAgICAgICAgICAgbmVzdCA9IFRSVUUpICU+JQogIHN1bW1hcml6ZShlY2lnX2V2ZXJfeWVhciA9IAogICAgICAgICAgICAgIHN1cnZleV9tZWFuKGVjaWdfZXZlciwgdmFydHlwZSA9ICJjaSIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIGVjaWdfY3VycmVudF95ZWFyID0gCiAgICAgICAgICAgICAgc3VydmV5X21lYW4oZWNpZ19jdXJyZW50LCB2YXJ0eXBlID0gImNpIiwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbm9uX2VjaWdfZXZlcl95ZWFyID0gCiAgICAgICAgICAgICAgc3VydmV5X21lYW4obm9uX2VjaWdfZXZlciwgdmFydHlwZSA9ICJjaSIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIG5vbl9lY2lnX2N1cnJlbnRfeWVhciA9IAogICAgICAgICAgICAgIHN1cnZleV9tZWFuKG5vbl9lY2lnX2N1cnJlbnQsIHZhcnR5cGUgPSAiY2kiLCBuYS5ybSA9IFRSVUUpLAogICAgICAgICAgICB0b2JhY2NvX2V2ZXJfeWVhciA9IAogICAgICAgICAgICAgIHN1cnZleV9tZWFuKHRvYmFjY29fZXZlciwgdmFydHlwZSA9ICJjaSIsIG5hLnJtID0gVFJVRSksCiAgICAgICAgICAgIHRvYmFjY29fY3VycmVudF95ZWFyID0gCiAgICAgICAgICAgICAgc3VydmV5X21lYW4odG9iYWNjb19jdXJyZW50LCB2YXJ0eXBlID0gImNpIiwgbmEucm0gPSBUUlVFKSkgJT4lCiAgbXV0YXRlX2FsbCgiKiIsIDEwMCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGV2ZXJ5dGhpbmcoKSwKICAgICAgICAgICAgICAgbmFtZXNfdG8gPSAiQ2F0ZWdvcnkiLAogICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiUGVyY2VudGFnZSBvZiBzdHVkZW50cyIpICU+JQogIG11dGF0ZShFc3RpbWF0ZSA9IGNhc2Vfd2hlbihzdHJfZGV0ZWN0KENhdGVnb3J5LCAiX2xvdyIpIH4gIkxvd2VyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3RyX2RldGVjdChDYXRlZ29yeSwgIl91cHAiKSB+ICJVcHBlciIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiTWVhbiIpLAogICAgICAgICAgICAgVXNlciA9IGNhc2Vfd2hlbihzdHJfZGV0ZWN0KENhdGVnb3J5LCAiZXZlciIpIH4gIkV2ZXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJfZGV0ZWN0KENhdGVnb3J5LCAiY3VycmVudCIpIH4gIkN1cnJlbnQiKSwKICAgICAgUHJvZHVjdCA9IGNhc2Vfd2hlbihzdHJfZGV0ZWN0KENhdGVnb3J5LCAibm9uX2VjaWciKSB+ICJPdGhlciBwcm9kdWN0cyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHN0cl9kZXRlY3QoQ2F0ZWdvcnksICJ0b2JhY2NvIikgfiAiQW55IHRvYmFjY28gcHJvZHVjdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRSVUUgfiAiRS1jaWdhcmV0dGVzIikpICU+JQogIGRwbHlyOjpzZWxlY3QoLUNhdGVnb3J5KSAlPiUKICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gRXN0aW1hdGUsCiAgICAgICAgICAgICAgdmFsdWVzX2Zyb20gPSBgUGVyY2VudGFnZSBvZiBzdHVkZW50c2ApfQoKCkNvaG9ydF9kYXRhIDwtIG55dHNfZGF0YSAlPiUKICBmaWx0ZXIoKEdyYWRlID09ICI4IiAmIHllYXIgPT0gMjAxNSkgfAogICAgICAgICAoR3JhZGUgPT0gIjkiICYgeWVhciA9PSAyMDE2KSB8CiAgICAgICAgIChHcmFkZSA9PSAiMTAiICYgeWVhciA9PSAyMDE3KSB8CiAgICAgICAgIChHcmFkZSA9PSAiMTEiICYgeWVhciA9PSAyMDE4KSB8CiAgICAgICAgIChHcmFkZSA9PSAiMTIiICYgeWVhciA9PSAyMDE5KQogICAgICAgICApICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIGdyb3VwX21vZGlmeSh+IHN1cnZleU1lYW5Db2hvcnQoLngpKQoKaGVhZChDb2hvcnRfZGF0YSkKYGBgCgpXZSB3aWxsIG5vdyBtYWtlIHNpbWlsYXIgcGxvdHMgdG8gdGhvc2UgYWJvdmUgZm9yIHRoaXMgc3Vic2V0IG9mIHRoZSBkYXRhOgoKYGBge3J9CnBsb3RBX3dfOCA8LSBDb2hvcnRfZGF0YSAlPiUKICBmaWx0ZXIoUHJvZHVjdCA9PSAiQW55IHRvYmFjY28gcHJvZHVjdCIpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBNZWFuKSkgKwogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBVc2VyKSkgKwogIGdlb21fbGluZXJhbmdlKGFlcyh5bWluID0gTG93ZXIsIHltYXggPSBVcHBlciksIHNpemUgPSAxKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMiwgMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDcwLCBieSA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gc2VxKDAsIDcwLCBieSA9IDEwKSwKICAgICAgICAgICAgICAgICAgICAgbGltaXRzID0gYygwLCA3MCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdl9jb2xvcnMpICsKICB0aGVtZV9saW5lZHJhdygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICJUb2JhY2NvIHByb2R1Y3QgdXNlIGJlY2FtZSBpbmNyZWFzaW5nbHkgcHJldmFsZW50IiwKICAgICAgIHkgPSAiJSBvZiBzdHVkZW50cyIpCgpwbG90Ql93XzggPC0gQ29ob3J0X2RhdGEgJT4lCiAgZmlsdGVyKFByb2R1Y3QgIT0gIkFueSB0b2JhY2NvIHByb2R1Y3QiLCBVc2VyID09ICJFdmVyIikgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IE1lYW4sIGNvbG9yID0gUHJvZHVjdCkpICsKICBnZW9tX2xpbmUobGluZXR5cGUgPSAxKSArCiAgZ2VvbV9saW5lcmFuZ2UoYWVzKHltaW4gPSBMb3dlciwgeW1heCA9IFVwcGVyKSwgc2l6ZSA9IDEpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEwLCA2MCwgYnkgPSAxMCksIGxpbWl0cyA9IGMoMTAsIDYwKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSB2X2NvbG9ycykgKwogIHRoZW1lX2xpbmVkcmF3KCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X2JsYW5rKCkpICsKICBsYWJzKHRpdGxlID0gIiUgZXZlciB0cnlpbmcgdG9iYWNjbyBwcm9kdWN0cyBpbmNyZWFzZXMiLAogICAgICAgeSA9ICIlIG9mIHN0dWRlbnRzIikKCnBsb3RDX3dfOCA8LSBDb2hvcnRfZGF0YSAlPiUKICBmaWx0ZXIoUHJvZHVjdCAhPSAiQW55IHRvYmFjY28gcHJvZHVjdCIsIFVzZXIgPT0gIkN1cnJlbnQiKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gTWVhbiwgY29sb3IgPSBQcm9kdWN0KSkgKwogIGdlb21fbGluZShhZXMobGluZXR5cGUgPSBVc2VyKSkgKwogIGdlb21fbGluZXJhbmdlKGFlcyh5bWluID0gTG93ZXIsIHltYXggPSBVcHBlciksIHNpemUgPSAxKSArCiAgc2NhbGVfbGluZXR5cGVfbWFudWFsKHZhbHVlcyA9IGMoMiwgMSkpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDAsIDYwLCBieSA9IDEwKSwgbGltaXRzID0gYygwLCA2MCkpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gdl9jb2xvcnMpICsKICB0aGVtZV9saW5lZHJhdygpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKSArCiAgbGFicyh0aXRsZSA9ICJFLWNpZ2FyZXR0ZSB1c2Ugc3VycGFzc2VzIHVzZSBvZiBvdGhlciBwcm9kdWN0cyIsCiAgICAgICB5ID0gIiUgb2Ygc3R1ZGVudHMiKQoKdGl0bGVfd184IDwtIGdnZHJhdygpICsKICBkcmF3X2xhYmVsKAogIGV4cHJlc3Npb24oIkZvciBzdHVkZW50cyBpbiB0aGUgMjAxOSBncmFkdWF0aW5nIGNsYXNzLCBob3cgYXJlIHZhcGluZyBhbmQgdG9iYWNjbyB1c2UgcmVsYXRlZD8iKSwKICAgIGZvbnRmYWNlID0gJ2JvbGQnLAogICAgc2l6ZSA9IDE0LAogICAgeCA9IDAsCiAgICBoanVzdCA9IDAKICApICsKICB0aGVtZSgKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDApCiAgKQoKcGxvdHNBX3dfOCA8LSBwbG90X2dyaWQocGxvdEFfd184LAogICAgICAgICAgICAgICAgICAgICAgICByZWxfd2lkdGhzID0gYygxKSwKICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAidiIsCiAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMgPSAiYnQiKQoKcGxvdHNCQ193XzggPC0gcGxvdF9ncmlkKHBsb3RCX3dfOCwKICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RDX3dfOCwKICAgICAgICAgICAgICAgICAgICAgICAgIHJlbF93aWR0aHMgPSBjKDEsIDEpLAogICAgICAgICAgICAgICAgICAgICAgICAgYXhpcyA9ICJidCIpCgpsZWdlbmRfd184IDwtIGdldF9sZWdlbmQocGxvdDFjICsKICAgICAgICAgICAgICAgICAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsZWdlbmQuZGlyZWN0aW9uID0gImhvcml6b250YWwiKSkKCmZpZ3VyZV93XzggPC0gcGxvdF9ncmlkKHRpdGxlX3dfOCwKICAgICAgICAgICAgICAgICAgICAgICAgcGxvdHNBX3dfOCwKICAgICAgICAgICAgICAgICAgICAgICAgcGxvdHNCQ193XzgsCiAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZF93XzgsCiAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICByZWxfaGVpZ2h0cyA9IGMoMC4xLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAwLjEpLAogICAgICAgICAgICAgICAgICAgICAgICBzY2FsZSA9IDEuMAopCgpmaWd1cmVfd184CmBgYAoKCgojIyAqKkRhdGEgQW5hbHlzaXMqKgoqKiogCgpJZiB5b3UgaGF2ZSBiZWVuIGZvbGxvd2luZyBhbG9uZyBidXQgc3RvcHBlZCwgd2UgY291bGQgbG9hZCB0aGUgd3JhbmdsZWQgZGF0YSBmcm9tIHRoZSAiZGF0YSIgZGlyZWN0b3J5IGxpa2Ugc286CgpgYGB7cn0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSIpKQpgYGAKCioqKgo8ZGV0YWlscz4gPHN1bW1hcnk+IElmIHlvdSBza2lwcGVkIHRoZSBwcmV2aW91cyBzZWN0aW9ucyBjbGljayBoZXJlLiA8L3N1bW1hcnk+CgpGaXJzdCB5b3UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgT0NTZGF0YWAgcGFja2FnZToKCmBgYHtyLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJPQ1NkYXRhIikKbGlicmFyeShPQ1NkYXRhKQpgYGAKClRoZW4sIHlvdSBtYXkgbG9hZCB0aGUgd3JhbmdsZWQgZGF0YSBmb3IgcGxvdHMgdXNpbmcgdGhlIGZvbGxvd2luZyBjb2RlOgoKYGBge3IsIGV2YWw9RkFMU0V9CndyYW5nbGVkX3JkYSgib2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5Iiwgb3V0cGF0aCA9IGdldHdkKCkpCmxvYWQoaGVyZTo6aGVyZSgiT0NTZGF0YSIsICJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSIpKQpgYGAKCklmIHRoZSBwYWNrYWdlIGRvZXMgbm90IHdvcmsgZm9yIHlvdSwgYWx0ZXJuYXRpdmVseSwgYW4gUkRBIGZpbGUgKHN0YW5kcyBmb3IgUiBkYXRhKSBvZiB0aGUgZGF0YSBjYW4gYmUgZm91bmQgaW4gb3VyIFtHaXRIdWIgcmVwb3NpdG9yeV0oaHR0cHM6Ly9naXRodWIuY29tLy9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L3RyZWUvbWFzdGVyL2RhdGEvd3JhbmdsZWQpIG9yIHNsaWdodGx5IG1vcmUgZGlyZWN0bHkgW2hlcmVdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9vcGVuY2FzZXN0dWRpZXMvb2NzLWJwLXZhcGluZy1jYXNlLXN0dWR5L21hc3Rlci9kYXRhL3dyYW5nbGVkL3dyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSkuIERvd25sb2FkIHRoaXMgZmlsZSBhbmQgdGhlbiBwbGFjZSBpdCBpbiB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3Rvcnkgd2l0aGluIGEgc3ViZGlyZWN0b3J5IGNhbGxlZCAid3JhbmdsZWQiIHdpdGhpbiBhIHN1YmRpcmVjdG9yeSBjYWxsZWQgImRhdGEiIHRvIGNvcHkgYW5kIHBhc3RlIG91ciBjb2RlLiBXZSB1c2VkIGFuIFJTdHVkaW8gcHJvamVjdCBhbmQgdGhlIFtgaGVyZWAgcGFja2FnZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKSB0byBuYXZpZ2F0ZSB0byB0aGUgZmlsZSBtb3JlIGVhc2lseS4gCgpgYGB7cn0KbG9hZChoZXJlOjpoZXJlKCJkYXRhIiwgIndyYW5nbGVkIiwgIndyYW5nbGVkX2RhdGFfd2l0aF92YXJfZm9yX3Bsb3RzLnJkYSIpKQpgYGAKCioqKgo8ZGV0YWlscz4gPHN1bW1hcnk+IENsaWNrIGhlcmUgdG8gc2VlIG1vcmUgYWJvdXQgY3JlYXRpbmcgbmV3IHByb2plY3RzIGluIFJTdHVkaW8uIDwvc3VtbWFyeT4KCllvdSBjYW4gY3JlYXRlIGEgcHJvamVjdCBieSBnb2luZyB0byB0aGUgRmlsZSBtZW51IG9mIFJTdHVkaW8gbGlrZSBzbzoKCgpgYGB7ciwgZWNobyA9IEZBTFNFLCBvdXQud2lkdGg9IjYwJSJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJOZXdfcHJvamVjdC5wbmciKSkKYGBgCgpZb3UgY2FuIGFsc28gZG8gc28gYnkgY2xpY2tpbmcgdGhlIHByb2plY3QgYnV0dG9uOgoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoPSI2MCUifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAicHJvamVjdF9idXR0b24ucG5nIikpCmBgYAoKU2VlIFtoZXJlXShodHRwczovL3N1cHBvcnQucnN0dWRpby5jb20vaGMvZW4tdXMvYXJ0aWNsZXMvMjAwNTI2MjA3LVVzaW5nLVByb2plY3RzKSB0byBsZWFybiBtb3JlIGFib3V0IHVzaW5nIFJTdHVkaW8gcHJvamVjdHMgYW5kIFtoZXJlXShodHRwczovL2dpdGh1Yi5jb20vamVubnliYy9oZXJlX2hlcmUpIHRvIGxlYXJuIG1vcmUgYWJvdXQgdGhlIGBoZXJlYCBwYWNrYWdlLgoKPC9kZXRhaWxzPgoqKioKPC9kZXRhaWxzPgoKKioqCgpBcyBhbiBleHRlbnNpb24sIHdlIHdpbGwgaW5jbHVkZSBzb21lIG1hdGVyaWFsIGhlcmUgb24gW2xvZ2lzdGljIHJlZ3Jlc3Npb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xvZ2lzdGljX3JlZ3Jlc3Npb24pe3RhcmdldD0iX2JsYW5rIn0gYW5kIHN1cnZleS13ZWlnaHRlZCBsb2dpc3RpYyByZWdyZXNzaW9uIHRoYXQgd291bGQgYmUgYXBwcm9wcmlhdGUgZm9yIGFuc3dlcmluZyBRdWVzdGlvbiAyICgiSG93IGRvZXMgZS1jaWdhcmV0dGUgdXNlIGNvbXBhcmUgYmV0d2VlbiBtYWxlcyBhbmQgZmVtYWxlcz8iKSBmb3IgYSBzaW5nbGUgeWVhciB1c2luZyBzdGF0aXN0aWNhbCBpbmZlcmVuY2UsIHJhdGhlciB0aGFuIGp1c3QgZGF0YSB2aXN1YWxpemF0aW9ucy4KCldlIGNhbiBsb29rIGF0IHRoZSBmaW5hbCBmaWd1cmUgaW4gdGhlIHNlY3Rpb24gb24gUXVlc3Rpb24gMiBhbmQgc2VlIHRoYXQgYW1vbmcgYm90aCBjdXJyZW50IGFuZCBldmVyIHVzZXJzIG9mIGUtY2lnYXJldHRlcywgYSBoaWdoZXIgcGVyY2VudGFnZSBvZiBtYWxlcyB0aGFuIGZlbWFsZXMgdXNlIG9yIGhhdmUgdXNlZCBlLWNpZ2FyZXR0ZXMuJwoKYGBge3IsIGVjaG8gPSBGQUxTRSwgb3V0LndpZHRoID0gIjgwMCBweCJ9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKGhlcmU6OmhlcmUoImltZyIsICJwbG90Mi5wbmciKSkKYGBgCgpCdXQgd2hhdCBpZiB3ZSB3YW50ZWQgdG8gcXVhbnRpZnkgdGhpcyBlZmZlY3QgYW5kIGFzc2VzcyB3aGV0aGVyIHRoaXMgZGlmZmVyZW5jZSBjYW4gYmUgY29uc2lkZXJlZCBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50PyBUaGlzIGlzIHdoZXJlIHRoZSB0b29sIG9mIGxvZ2lzdGljIHJlZ3Jlc3Npb24gY2FuIGNvbWUgaW4gaGFuZHkuCgojIyMgKipMb2dpc3RpYyByZWdyZXNzaW9uIG1vdGl2YXRpb24qKgoqKioKCkhlcmUsIHdlIHdpbGwgYXBwcm9hY2ggdGhlIHRvcGljIG9mIFtsb2dpc3RpYyByZWdyZXNzaW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Mb2dpc3RpY19yZWdyZXNzaW9uKXt0YXJnZXQ9Il9ibGFuayJ9IGFzc3VtaW5nIHNvbWUgcHJpb3Iga25vd2xlZGdlIG9mIHNpbXBsZSBhbmQgbXVsdGlwbGUgbGluZWFyIHJlZ3Jlc3Npb24uIFRoZXNlIGhhdmUgYmVlbiBjb3ZlcmVkIGluIFthbm90aGVyIGNhc2Ugc3R1ZHldKGh0dHBzOi8vb3BlbmNhc2VzdHVkaWVzLmdpdGh1Yi5pby9vY3MtYnAtZGlldC8pLgoKQXMgYSBicmllZiByZW1pbmRlciwgYSBbbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWxdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpbmVhcl9yZWdyZXNzaW9uKXt0YXJnZXQ9Il9ibGFuayJ9IGFsbG93cyB1cyB0byBlc3RpbWF0ZSB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gYW4gb3V0Y29tZSB2YXJpYWJsZSwgY2FsbCBpdCAkWSQsIGFuZCBhIHNldCBvZiBvbmUgb3IgbW9yZSBpbnB1dCB2YXJpYWJsZXMsICRYXzEsIFhfMiwgLi4uLCBYX24kLiBXZSBjYW4gd3JpdGUgYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYXM6CgokJCBFKFkpID0gXGJldGFfMCArIFxiZXRhXzEgWF8xJCQKCndoZXJlIHRoZSAkRShZKSQgbWVhbnMgdGhlIGV4cGVjdGVkIHZhbHVlIG9mICRZJCwgaS5lLiwgb3VyIG1vZGVsIGdpdmVzIHVzIGFuIGVzdGltYXRlIG9mIHRoZSBtZWFuIHZhbHVlIG9mICRZJCBnaXZlbiBhIHBhcnRpY3VsYXIgaW5wdXQgJFhfMSQuIEhlcmUsICRcYmV0YV8xJCBxdWFudGlmaWVzIHRoZSBleHBlY3RlZCBkaWZmZXJlbmNlIGluICRZJCBjb21wYXJpbmcgdHdvIGluZGl2aWR1YWxzIHdobyBhcmUgb25lIHVuaXQgYXBhcnQgaW4gJFhfMSQuCgpTaW1pbGFybHksIHdlIGNhbiBpbmNsdWRlIG1vcmUgdGhhbiBvbmUgcHJlZGljdG9yIHNvIHRoYXQgb3VyIGVxdWF0aW9uIG1pZ2h0IGxvb2sgbGlrZToKCiQkIEUoWSkgPSBcYmV0YV8wICsgXGJldGFfMSBYXzEgKyBcYmV0YV8yIFhfMiQkCgpIZXJlLCAkXGJldGFfMSQgcXVhbnRpZmllcyB0aGUgZXhwZWN0ZWQgZGlmZmVyZW5jZSBpbiAkWSQgY29tcGFyaW5nIHR3byBpbmRpdmlkdWFscyB3aG8gYXJlIG9uZSB1bml0IGFwYXJ0IGluICRYXzEkLCBob2xkaW5nICRYXzIkIGF0IGEgZml4ZWQgdmFsdWUuIFRoaXMgbWF0ZXJpYWwgaXMgY292ZXJlZCBpbiBtb3JlIGRldGFpbCBbZWxzZXdoZXJlXShodHRwczovL3JhZmFsYWIuZ2l0aHViLmlvL2RzYm9vay9saW5lYXItbW9kZWxzLmh0bWwjbGluZWFyLXJlZ3Jlc3Npb24taW4tdGhlLXRpZHl2ZXJzZSl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgaW4gW2Fub3RoZXIgY2FzZSBzdHVkeV0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1kaWV0Lyl7dGFyZ2V0PSJfYmxhbmsifS4KCkluIHRoZSBjYXNlIG9mIG91ciBxdWVzdGlvbiBvZiBpbnRlcmVzdCBmb3IgdGhpcyBjYXNlIHN0dWR5LCBob3dldmVyLCBvdXIgb3V0Y29tZSB2YXJpYWJsZSBpcyBvZiBhIHBhcnRpY3VsYXIgdHlwZTogaXQgaXMgYSBZZXMtTm8gb3IgKmJpbmFyeSogb3V0Y29tZSwgc2luY2UgZWFjaCBzdHVkZW50IHJlc3BvbmRlbnQgZWl0aGVyIGlzIG9yIGlzIG5vdCBhIGN1cnJlbnQgdXNlciBvZiBlLWNpZ2FyZXR0ZXMuIFRoaXMgbWVhbnMgaW4gb3VyIHNldHRpbmcgJFkkIG9ubHkgdGFrZXMgb24gdHdvIHZhbHVlczogVFJVRSBvciBGQUxTRSwgd2hpY2ggd2UgY2FuIGFsc28gdGhpbmsgb2YgYXMgMSBhbmQgMC4gRm9yIHRoaXMga2luZCBvZiBvdXRjb21lIHZhcmlhYmxlLCB3ZSBuZWVkIGEgc3BlY2lhbCBraW5kIG9mIHJlZ3Jlc3Npb24sIGNhbGxlZCAqbG9naXN0aWMgcmVncmVzc2lvbiouIEFuZCBpbnN0ZWFkIG9mIHVzaW5nIGEgbGluZWFyIG1vZGVsIHRvIGVzdGltYXRlICRZJCBpdHNlbGYgZm9yIGEgZ2l2ZW4gc2V0IG9mIGlucHV0IHZhcmlhYmxlcywgd2Ugd2lsbCB1c2UgYSBsaW5lYXIgbW9kZWwgdG8gZXN0aW1hdGUgdGhlICpsb2cgb2RkcyB0aGF0IFk9MSogZm9yIGEgZ2l2ZW4gc2V0IG9mIGlucHV0IHZhcmlhYmxlcy4KCklmIHdlIGRlZmluZSAkcD1QKFk9MSk9RShZKSQsIHRoZSBzdGFuZGFyZCBzaW1wbGUgbG9naXN0aWMgcmVncmVzc2lvbiBlcXVhdGlvbiBjYW4gYmUgd3JpdHRlbiBhczoKCiQkbG9naXQocCk9IGxvZ19lIChcZnJhY3twfXsxLXB9KT0gXGJldGFfMCArIFxiZXRhXzEgWCQkICAKCkluIG91ciBjYXNlLCB3ZSB3b3VsZCBkZWZpbmUgJHAkIGFzIHRoZSBwcm9iYWJpbGl0eSB0aGF0IGEgc3R1ZGVudCByZXNwb25kZW50IGlzIGEgY3VycmVudCBlLWNpZ2FyZXR0ZSB1c2VyLCBzaW5jZSAkWSQgaXMgdGhlIGJpbmFyeSB2YXJpYWJsZSB0aGF0IGlzIDEgd2hlbiBhIHN0dWRlbnQgcmVzcG9uZGVudCBpcyBhIGN1cnJlbnQgZS1jaWdhcmV0dGUgdXNlciBhbmQgMCBpZiBub3QuIFRoZSB2YWx1ZSAkXGZyYWN7cH17MS1wfSQgaXMgY2FsbGVkIHRoZSAqb2RkcyogdGhhdCAkWSQgaXMgZXF1YWwgdG8gMS4KClRoaXMgKmxvZ2l0KiBmdW5jdGlvbiBpcyBzaG9ydCBmb3IgKmxvZyBvZGRzKi4gCgpXaGlsZSBpdCBtYXkgZmVlbCBzdHJhbmdlIHdvcmtpbmcgd2l0aCB0aGUgbG9nIG9kZHMgdGhhdCBvdXIgb3V0Y29tZSB2YXJpYWJsZSBpcyBlcXVhbCB0byAxLCB0aGVyZSBhcmUgc29tZSBpbnR1aXRpdmUgcmVhc29ucyB3aHkgaXQgbWFrZXMgc2Vuc2UgdG8gZG8gdGhpcyBmcm9tIHRoZSBwb2ludCBvZiB2aWV3IG9mIGZpdHRpbmcgYSBsaW5lIHRvIG91ciBkYXRhIGFuZCBpbnRlcnByZXRpbmcgdGhlIHJlc3VsdHMuIFRoZSBbbG9nIG9kZHNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xvZ2l0KXt0YXJnZXQ9Il9ibGFuayJ9IGNhbiB0YWtlIGFueSB2YWx1ZSBvbiB0aGUgcmVhbCBudW1iZXIgbGluZSwgYWxsb3dpbmcgdXMgdG8gZXN0aW1hdGUgb3VyIG1vZGVsIHBhcmFtZXRlcnMgd2l0aCBubyBjb25zdHJhaW50cy4gSWYgd2UgaW5zdGVhZCB0cmllZCB0byB1c2Ugc2F5ICRwJCBhcyB0aGUgb3V0Y29tZSB2YXJpYWJsZSwgd2Ugd291bGQgc29tZWhvdyBuZWVkIHRvIGNvbnN0cmFpbiAkXGJldGFfMCArIFxiZXRhXzEgWCQgdG8gYmUgYmV0d2VlbiAwIGFuZCAxLCBzaW5jZSB0aGlzIGlzIHRoZSBvbmx5IHBvc3NpYmxlIHJhbmdlIG9mIHZhbHVlcyBmb3IgYSBbcHJvYmFiaWxpdHldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1Byb2JhYmlsaXR5KXt0YXJnZXQ9Il9ibGFuayJ9LiBBIHNlY29uZCwgbW9yZSB0ZWNobmljYWwgcmVhc29uIGlzIHRoYXQgd29ya2luZyBvbiB0aGUgbG9nIG9kZHMgc2NhbGUgZ2l2ZXMgdXMgYSBuaWNlIGZvcm11bGF0aW9uIG9mIG91ciAqbGlrZWxpaG9vZCosIGkuZS4sIGEgZnVuY3Rpb24gb2Ygb3VyIHVua25vd24gcGFyYW1ldGVycyB0aGF0IGluY29ycG9yYXRlcyBvdXIgb2JzZXJ2ZWQgZGF0YS4gV2UgdXNlIHRoaXMgW2xpa2VsaWhvb2QgZnVuY3Rpb25dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xpa2VsaWhvb2RfZnVuY3Rpb24pe3RhcmdldD0iX2JsYW5rIn0gdG8gZXN0aW1hdGUgb3VyIHVua25vd24gcGFyYW1ldGVycyAoaGVyZSwgJFxiZXRhXzAkIGFuZCAkXGJldGFfMSQpIGFuZCB0aGlzIGZvcm11bGF0aW9uIGdpdmVzIHVzIGEgbmljZSB3YXkgdG8gY2FsY3VsYXRlIHRoZSBbbWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRlc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWF4aW11bV9saWtlbGlob29kX2VzdGltYXRpb24pe3RhcmdldD0iX2JsYW5rIn0gb2YgdGhlc2UgcGFyYW1ldGVycy4KClRoZSBpbnR1aXRpdmUgZXhwbGFuYXRpb24gb2YgbG9naXN0aWMgcmVncmVzc2lvbiB0aGVuIGlzIHRoYXQgd2UgYXJlIGZpdHRpbmcgYSBsaW5lIHRvIHRoZSBsb2cgb2RkcyBvZiAkWSQsIGFzIGl0IHZhcmllcyB3aXRoIGRpZmZlcmVudCB2YWx1ZXMgb2YgJFgkLiBXZSB3aWxsIHdvcmsgdGhyb3VnaCBhbiBleGFtcGxlIGJlbG93IHRvIGlsbHVzdHJhdGUgYW5kIGhvcGVmdWxseSBjbGFyaWZ5IHRoaXMuCgoKIyMjICoqTG9naXN0aWMgcmVncmVzc2lvbiAiYnkgaGFuZCIgYW5kIGJ5IG1vZGVsKioKKioqCgpGb3Igc2ltcGxpY2l0eSwgd2Ugd2lsbCBjb25zaWRlciBqdXN0IHRoZSBzZXQgb2YgY3VycmVudCB1c2VycyBvZiBlLWNpZ2FyZXR0ZXMgaW4gMjAxNS4gSG93IG11Y2ggbW9yZSBsaWtlbHkgaXMgYSBtYWxlIHN0dWRlbnQgdG8gYmUgYSBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgdGhhbiBhIGZlbWFsZSBzdHVkZW50PwoKV2UgY2FuIGdldCBhIGZpcnN0IGxvb2sgYXQgdGhlIGFuc3dlciBieSBjYWxjdWxhdGluZyB0aGUgcGVyY2VudCBvZiBmZW1hbGVzIGFuZCBwZXJjZW50IG9mIG1hbGVzIHdobyBhcmUgY3VycmVudCBlLWNpZ2FyZXR0ZSB1c2VycyBvciBub3Q6IAoKYGBge3J9Cm55dHNfZGF0YSAlPiUgCiAgZmlsdGVyKHllYXIgPT0gMjAxNSwgIWlzLm5hKFNleCkpICU+JQogIGdyb3VwX2J5KFNleCwgZWNpZ19jdXJyZW50KSAlPiUKICBzdW1tYXJpemUobiA9IG4oKSkgJT4lCiAgbXV0YXRlKHBjdCA9IG4gLyBzdW0obikpCmBgYAoKV2UgY2FuIHNlZSB0aGF0IHRoZSBwZXJjZW50YWdlIGlzIGxvd2VyIGZvciBmZW1hbGVzIHRoYW4gZm9yIG1hbGVzLiBBbm90aGVyIHdheSBvZiBvcmdhbml6aW5nIHRoaXMgZGF0YSB3b3VsZCBiZSB0byBtYWtlIGEgWzJ4MiB0YWJsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29udGluZ2VuY3lfdGFibGUpe3RhcmdldD0iX2JsYW5rIn0sIGEgZGF0YSBzdW1tYXJpemF0aW9uIGZyZXF1ZW50bHkgdXNlZCBpbiBwdWJsaWMgaGVhbHRoIHNldHRpbmdzLgoKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTWFsZSAgICAgfCBGZW1hbGV8IFRvdGFsfAp8Oi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLTp8LS0tLS0tOnwtLS0tLTp8CnxDdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgICAgICAgICB8ICAgICAgMTE3MXwgICAgNzcyfCAgMTk0M3wKfE5vdCBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgICAgIHwgICAgICA3Nzg3fCAgIDc4NTB8IDE1NjM3fAp8VG90YWwgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgIDg5NTh8ICAgODYyMnwgMTc1ODB8CgoKQXMgZGlzY3Vzc2VkIGFib3ZlLCBvbmUgaW1wb3J0YW50IGluZ3JlZGllbnQgaW4gdW5kZXJzdGFuZGluZyB0aGUgb3V0cHV0IG9mIGxvZ2lzdGljIHJlZ3Jlc3Npb24gaXMgdW5kZXJzdGFuZGluZyB0aGUgY29uY2VwdCBvZiBhbiBvZGRzIGFuZCBhbiBvZGRzIHJhdGlvLiBXZSBjYW4gYXNrLCBhbW9uZyBtYWxlcyB3aG8gcmVzcG9uZGVkIHRvIHRoZSBzdXJ2ZXkgaW4gMjAxNSwgd2hhdCBhcmUgdGhlIG9kZHMgb2YgYmVpbmcgYSBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXI/IEhvdyBhYm91dCBmb3IgZmVtYWxlcz8gSG93IGRvIHRoZXNlIG9kZHMgY29tcGFyZT8gVGhlIFtvZGRzIHJhdGlvXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PZGRzX3JhdGlvKXt0YXJnZXQ9Il9ibGFuayJ9IGlzIGEgdG9vbCBmcmVxdWVudGx5IHVzZWQgaW4gcHVibGljIGhlYWx0aCB0byBjb21wYXJlIHRoZSBvZGRzIGJldHdlZW4gdHdvIGdyb3Vwcy4gCgpJbiB0aGlzIGNhc2U6CgoqIE9kZHMgb2YgY3VycmVudCBlLWNpZ2FyZXR0ZSB1c2UgZm9yIG1hbGVzOiAxMTcxIC8gNzc4NyA9IDAuMTUwCiogT2RkcyBvZiBjdXJyZW50IGUtY2lnYXJldHRlIHVzZSBmb3IgZmVtYWxlczogNzcyIC8gNzg1MCA9IDAuMDk4CiogT2RkcyByYXRpbyBvZiBlLWNpZ2FyZXR0ZSB1c2UgZm9yIGZlbWFsZXMgYXMgY29tcGFyZWQgdG8gbWFsZXM6ICQkT1IgPSBcZnJhY3tvZGRzIFwgZm9yIFwgZmVtYWxlc317b2RkcyBcIGZvciBcIG1hbGVzfSA9IFxmcmFjezc3MiAvIDc4NTB9ezExNzEgLyA3Nzg3fSA9IDAuNjUkJAoqIExvZyBvZGRzIHJhdGlvOiAkbG9nX2UoT1IpID0gbG9nKDEuNTMpID0gLTAuNDIkCgpXZSB3b3VsZCBpbnRlcnByZXQgdGhlc2UgdmFsdWVzIGJ5IHNheWluZyB0aGF0IHRoZSBvZGRzIG9mIGJlaW5nIGEgY3VycmVudCBlLWNpZ2FyZXR0ZSB1c2VyIGZvciB3b21lbiBhcmUgYXJvdW5kIDAuNjUgdGltZXMgdGhlIG9kZHMgb2YgYmVpbmcgYSBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgZm9yIG1lbiwgb3IgMzUlIGxvd2VyIGZvciB3b21lbi4gVGhpcyBtYXRjaGVzIHdoYXQgd2UgY2FuIHNlZSBpbiBvdXIgZGF0YSB2aXN1YWxpemF0aW9ucyBmb3IgUXVlc3Rpb24gMi4KCmBgYHtyLCBlY2hvID0gRkFMU0UsIG91dC53aWR0aCA9ICI4MDAgcHgifQprbml0cjo6aW5jbHVkZV9ncmFwaGljcyhoZXJlOjpoZXJlKCJpbWciLCAicGxvdDIucG5nIikpCmBgYAoKV2UgY291bGQgYWxzbyBhbnN3ZXIgdGhpcyBxdWVzdGlvbiB1c2luZyBsb2dpc3RpYyByZWdyZXNzaW9uOgoKJCRsb2cob2RkcyBcIG9mIFwgY3VycmVudCBcIGUtY2lnYXJldHRlIFwgdXNlKSA9IFxiZXRhXzAgKyBcYmV0YV8xIFxjZG90IFNleCQkCgpIZXJlIGlzIGhvdyB3ZSB3b3VsZCBmaXQgb3VyIGxvZ2lzdGljIHJlZ3Jlc3Npb24gbW9kZWwsIHVzaW5nIHRoZSBgZ2xtYCBmdW5jdGlvbiBmcm9tIGJhc2UgUi4gV2UgYWxzbyB1c2UgdGhlIGB0aWR5YCBmdW5jdGlvbiBmcm9tIHRoZSBgYnJvb21gIHBhY2thZ2UgdG8gY3JlYXRlIGEgdGliYmxlIG9mIHRoZSBtb2RlbCBvdXRwdXQuCgpgYGB7cn0KZGF0MjAxNSA8LSBueXRzX2RhdGEgJT4lIAogIGZpbHRlcih5ZWFyID09IDIwMTUsICFpcy5uYShTZXgpKQoKY3VyckVjaWdTZXggPC0gZ2xtKGVjaWdfY3VycmVudCB+IFNleCwgZGF0YSA9IGRhdDIwMTUsIGZhbWlseSA9IGJpbm9taWFsKGxpbmsgPSAibG9naXQiKSkKY3VyckVjaWdTZXhUaWR5IDwtIGJyb29tOjp0aWR5KGN1cnJFY2lnU2V4KQpjdXJyRWNpZ1NleFRpZHkKYGBgCgpMb29raW5nIGF0IHRoaXMgb3V0cHV0LCBvdXIgZXN0aW1hdGVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24gZXF1YXRpb24gaXM6CgokJGxvZyhvZGRzIFwgb2YgXCBjdXJyZW50IFwgZS1jaWdhcmV0dGUgXCB1c2UpID0gXGJldGFfMCArIFxiZXRhXzEgXGNkb3QgU2V4ID0gLTEuODkgLSAwLjQyNSBcY2RvdCAoU2V4ID09IGZlbWFsZSkkJAoKT3VyICRcYmV0YV8xJCBwYXJhbWV0ZXIgdGVsbHMgdXMgdGhhdCB0aGUgbG9nIG9kZHMgb2YgYmVpbmcgYSBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgYXJlIDAuNDI1IGxvd2VyIGZvciBmZW1hbGVzIGNvbXBhcmVkIHRvIG1hbGVzLCBpLmUuLCB0aGUgZGlmZmVyZW5jZSBpbiBsb2cgb2RkcyBvZiBiZWluZyBhIGN1cnJlbnQgZS1jaWdhcmV0dGUgdXNlciBmb3IgZmVtYWxlcyBjb21wYXJlZCB0byBtYWxlcyBpcyAtMC40MjUuIEFuZCB3ZSBjYW4gbm90aWNlIHRoYXQgdGhpcyB2YWx1ZSBtYXRjaGVzIHRoZSBsb2cgb2RkcyByYXRpbyB0aGF0IHdlIGNhbGN1bGF0ZWQgYnkgaGFuZCBmcm9tIHRoZSAyeDIgdGFibGUgYWJvdmUuIFRoaXMgaXMgYmVjYXVzZSBhIGRpZmZlcmVuY2UgaW4gbG9nIG9kZHMgaXMgdGhlIHNhbWUgYXMgYSBsb2cgb2RkcyByYXRpbyAtLSByZW1lbWJlciB5b3VyIFtydWxlcyBvZiBsb2dzXShodHRwczovL3d3dy5yYXBpZHRhYmxlcy5jb20vbWF0aC9hbGdlYnJhL0xvZ2FyaXRobS5odG1sI2xvZy1ydWxlcyl7dGFyZ2V0PSJfYmxhbmsifSEKCldlIGNhbiBpbnRlcnByZXQgdGhpcyBvdXRwdXQgYXMgZm9sbG93czoKCiogJC0wLjQyNSA9IFxiZXRhXzEgPSBcbG9nKE9SKSQKKiBUaGUgbG9nIG9kZHMgb2YgYmVpbmcgYSBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgaXMgMC40MjUgbG93ZXIgZm9yIGZlbWFsZXMgY29tcGFyZWQgdG8gbWFsZXMKKiAkMC42NSA9IGVeey0wLjQyNX0gPSBlXntcYmV0YV8xfSA9IE9SJAoqIFRoZSBvZGRzIG9mIGJlaW5nIGEgY3VycmVudCBlLWNpZ2FyZXR0ZSB1c2VyIGZvciBmZW1hbGVzIGlzIDAuNjUgdGltZXMgdGhlIG9kZHMgZm9yIG1hbGVzLgoqIFRoZSBvZGRzIG9mIGJlaW5nIGEgY3VycmVudCBlLWNpZ2FyZXR0ZSB1c2VyIGZvciBmZW1hbGVzIGlzIDM1JSBsb3dlciB0aGFuIHRoZSBvZGRzIGZvciBtYWxlcy4KCldlIGNhbiBhbHNvIGxvb2sgYXQgdGhlIG90aGVyIGNvbHVtbnMgb2YgdGhlIG1vZGVsIG91dHB1dCB0byBhc3Nlc3Mgd2hldGhlciBvdXIgZGF0YSBpbmRpY2F0ZSB0aGF0ICRcYmV0YV8xJCBpcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gMC4gVGhlIHAtdmFsdWUgZm9yIHRoZSBgU2V4YCB2YXJpYWJsZSBpbiBvdXIgbW9kZWwgaXMgYHIgZm9ybWF0KGN1cnJFY2lnU2V4VGlkeSAlPiUgZmlsdGVyKHRlcm0gPT0gIlNleGZlbWFsZSIpICU+JSBwdWxsKHAudmFsdWUpLCBkaWdpdHMgPSAzKWAuIFNpbmNlIHRoaXMgdmFsdWUgaXMgPCAwLjA1LCB3ZSB3b3VsZCByZWplY3QgdGhlIG51bGwgaHlwb3RoZXNpcyB0aGF0ICRcYmV0YV8xID0gMCQgYmFzZWQgb24gb3VyIG1vZGVsIG91dHB1dC4gU2VlIHRoaXMgW2Nhc2Ugc3R1ZHldKGh0dHBzOi8vd3d3Lm9wZW5jYXNlc3R1ZGllcy5vcmcvb2NzLWJwLXJ1cmFsLWFuZC11cmJhbi1vYmVzaXR5LyNBbHBoYSl7dGFyZ2V0PSJfYmxhbmsifSBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBhbHBoYSBhbmQgcC12YWx1ZXMuCgpTaW1wbGUgbG9naXN0aWMgcmVncmVzc2lvbiBjYW4gYmUgZXh0ZW5kZWQgdG8gaW5jbHVkZSBhZGRpdGlvbmFsIHZhcmlhYmxlcyBpbiB0aGUgbW9kZWwsIGZvciBleGFtcGxlIHRvIGFkanVzdCBmb3IgcG90ZW50aWFsIGNvbmZvdW5kaW5nIHZhcmlhYmxlcyBzdWNoIGFzIGBBZ2VgIG9yIGBHcmFkZWAuIEZvciBleGFtcGxlLCBzdXBwb3NlIHdlIHdhbnQgdG8gZXN0aW1hdGUgdGhlIGVmZmVjdCBvZiBgU2V4YCBvbiBjdXJyZW50IGUtY2lnYXJldHRlIHVzZSwgaG9sZGluZyBgQWdlYCBjb25zdGFudC4gV2UgY291bGQgZml0IHRoZSBtb2RlbDoKCiQkbG9nKG9kZHMgXCBvZiBcIGN1cnJlbnQgXCBlLWNpZ2FyZXR0ZSBcIHVzZSkgPSBcYmV0YV8wICsgXGJldGFfMSBcY2RvdCBTZXggKyBcYmV0YV8yIFxjZG90IEFnZSQkCgoKYGBge3J9CmN1cnJFY2lnU2V4QWdlIDwtIGdsbShlY2lnX2N1cnJlbnQgfiBTZXggKyBBZ2UsIAogICAgICAgICAgICAgICAgICAgICAgZGF0YSA9IGRhdDIwMTUsIAogICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gYmlub21pYWwobGluayA9ICJsb2dpdCIpKQp0aWR5KGN1cnJFY2lnU2V4QWdlKQpgYGAKCk5vdyBvdXIgJFxiZXRhXzEkIHBhcmFtZXRlciB0ZWxscyB1cyB0aGF0IHRoZSBsb2cgb2RkcyBvZiBiZWluZyBhbiBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgYXJlIDAuMzg1IGxvd2VyIGZvciBmZW1hbGVzIGNvbXBhcmVkIHRvIG1hbGVzLCB3aXRoaW4gYW4gYWdlIGdyb3VwLCBvciBob2xkaW5nIGBBZ2VgIGNvbnN0YW50LgoKIyMjICoqU3VydmV5IHdlaWdodGVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24qKgoqKioKCkFzIGRpc2N1c3NlZCBlbHNld2hlcmUgaW4gdGhpcyBjYXNlIHN0dWR5LCBvdXIgZGF0YSBjb21lIGZyb20gYSBzdXJ2ZXksIHdoZXJlIGluZGl2aWR1YWxzIHdlcmUgbm90IG5lY2Vzc2FyaWx5IHNhbXBsZWQgaW4gZGlyZWN0IHByb3BvcnRpb24gdG8gdGhlaXIgcG9wdWxhdGlvbiByZXByZXNlbnRhdGlvbiwgc28gaXQgaXMgbmVjZXNzYXJ5IHRvIGluY29ycG9yYXRlIHN1cnZleSB3ZWlnaHRzIGludG8gb3VyIGFuYWx5c2lzIHRvIHBlcmZvcm0gaW5mZXJlbmNlIGFib3V0IHRoZSBwb3B1bGF0aW9uIG9mIGludGVyZXN0LiBMdWNraWx5LCB0aGVyZSBhcmUgaW1wbGVtZW50YXRpb25zIG9mIHN1cnZleS13ZWlnaHRlZCBsb2dpc3RpYyByZWdyZXNzaW9uIGluIFIgdGhhdCBjYW4gZG8gdGhpcyBmb3IgdXMuCgpXZSBmaXJzdCBjcmVhdGUgb3VyIHN1cnZleSBkZXNpZ24gb2JqZWN0IHVzaW5nIHRoZSBgYXNfc3VydmV5X2Rlc2lnbmAgZnVuY3Rpb24gZnJvbSB0aGUgYHNydnlyYCBwYWNrYWdlLCBhbmQgdGhlbiB1c2UgdGhlIGBzdnlnbG1gIGZ1bmN0aW9uIGZyb20gdGhlIGBzdXJ2ZXlgIHBhY2thZ2UgdG8gZml0IG91ciBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsLgoKYGBge3J9CmRhdDIwMTVfc3VydmV5X2Rlc2lnbiA8LSBkYXQyMDE1ICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgIGFzX3N1cnZleV9kZXNpZ24oc3RyYXRhID0gc3RyYXR1bSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZHMgPSBwc3UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2VpZ2h0ICA9IGZpbndndCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuZXN0ID0gVFJVRSkKCgpjdXJyRWNpZ1NleF9zdnkgPC0gc3VydmV5OjpzdnlnbG0oZWNpZ19jdXJyZW50IH4gU2V4LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gcXVhc2liaW5vbWlhbChsaW5rID0gJ2xvZ2l0JyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSBkYXQyMDE1X3N1cnZleV9kZXNpZ24pCnRpZHkoY3VyckVjaWdTZXhfc3Z5KQpgYGAKCk5vdGUgdGhhdCBpbiB0aGlzIGNhc2UsIHdlIHVzZSB0aGUgW3F1YXNpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9RdWFzaS1saWtlbGlob29kKXt0YXJnZXQ9Il9ibGFuayJ9LWJpbm9taWFsIGZhbWlseSByYXRoZXIgdGhhbiB0aGUgYmlub21pYWwgZmFtaWx5LCB3aGljaCBhbGxvd3MgdGhlIGRhdGEgdG8gbm90IG5lY2Vzc2FyaWx5IGxvb2sgbGlrZSBhIHNhbXBsZSBmcm9tIGEgW2Jpbm9taWFsIGRpc3RyaWJ1dGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQmlub21pYWxfZGlzdHJpYnV0aW9uKXt0YXJnZXQ9Il9ibGFuayJ9LiBUaGlzIGlzIGJlY2F1c2UgYnkgaW5jb3Jwb3JhdGluZyBvdXIgc3VydmV5IHdlaWdodHMsIGl0IGlzIGFzIGlmIGVhY2ggaW5kaXZpZHVhbCBkb2VzIG5vdCBoYXZlIGEgMCBvciAxIGFzIHRoZWlyIG91dGNvbWUgdmFyaWFibGUsIHNvIHdlIGdldCBhIHdhcm5pbmcgaWYgd2UgZG8gbm90IHVzZSB0aGlzIHZhbHVlIGZvciBmYW1pbHkuCgpGcm9tIHRoaXMgbW9kZWwgb3V0cHV0LCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGVzdGltYXRlIGluY29ycG9yYXRpbmcgc3VydmV5IHdlaWdodHMgaXMgYSBsaXR0bGUgZGlmZmVyZW50LiBUaGUgaW50ZXJwcmV0YXRpb24gaXMgYXMgZm9sbG93czoKCiogJC0wLjM4MyA9IFxiZXRhXzEgPSBcbG9nKE9SKSQKKiBUaGUgbG9nIG9kZHMgb2YgYmVpbmcgYSBjdXJyZW50IGUtY2lnYXJldHRlIHVzZXIgaXMgMC4zODMgbG93ZXIgZm9yIGZlbWFsZXMgdGhhbiBmb3IgbWFsZXMsIHRha2luZyBzdXJ2ZXkgd2VpZ2h0cyBpbnRvIGFjY291bnQuCiogJDAuNjggPSBlXnstMC4zODN9ID0gZV57XGJldGFfMX0gPSBPUiQKKiBUaGUgb2RkcyBvZiBiZWluZyBhIGN1cnJlbnQgZS1jaWdhcmV0dGUgdXNlciBmb3IgZmVtYWxlcyBpcyAwLjY4IHRpbWVzIHRoZSBvZGRzIGZvciBtYWxlcywgdGFraW5nIHN1cnZleSB3ZWlnaHRzIGludG8gYWNjb3VudC4KKiBUaGUgb2RkcyBvZiBiZWluZyBhIGN1cnJlbnQgZS1jaWdhcmV0dGUgdXNlciBmb3IgZmVtYWxlcyBpcyAzMiUgbG93ZXIgdGhhbiB0aGUgb2RkcyBmb3IgbWFsZXMsIHRha2luZyBzdXJ2ZXkgd2VpZ2h0cyBpbnRvIGFjY291bnQuCgoKQXMgYWJvdmUsIHdlIGNhbiBhbHNvIGZpdCBhIG1vcmUgY29tcGxpY2F0ZWQgbW9kZWwgd2l0aCBhZGRpdGlvbmFsIGNvdmFyaWF0ZXMuCgpgYGB7cn0KY3VyckVjaWdTZXhBZ2Vfc3Z5IDwtIHN1cnZleTo6c3Z5Z2xtKGVjaWdfY3VycmVudCB+IFNleCArIEFnZSwKICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9IHF1YXNpYmlub21pYWwobGluayA9ICdsb2dpdCcpLCAKICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IGRhdDIwMTVfc3VydmV5X2Rlc2lnbikKdGlkeShjdXJyRWNpZ1NleEFnZV9zdnkpCmBgYAoKSW4gdGhpcyBjYXNlLCB3ZSBjYW4gc2VlIHRoYXQgb3VyIGVzdGltYXRlZCBkaWZmZXJlbmNlIGluIGxvZyBvZGRzIGZvciBmZW1hbGVzIGNvbXBhcmVkIHRvIG1hbGVzLCAtMC4zODAsIGlzIG5vdCBtdWNoIGRpZmZlcmVudCB3aGV0aGVyIHdlIGFyZSBob2xkaW5nIGBBZ2VgIGNvbnN0YW50IG9yIG5vdC4KCgojIyAqKlN1bW1hcnkqKgoqKiogCgoKIyMjICoqU3VtbWFyeSBQbG90KioKKioqCgpGaW5hbGx5IHdlIHdpbGwgcHV0IG91ciBwbG90cyB0b2dldGhlciB0byBjcmVhdGUgYSBwbG90IHRoYXQgZGVzY3JpYmVzIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBlLWNpZ2FyZXR0ZSB1c2FnZSBhbmQgb3ZlcmFsbCB0b2JhY2NvIHVzZSwgY29tYmluaW5nIGJvdGggb3VyIGZpcnN0IHNldCBvZiB1bndlaWdodGVkIHJlc3VsdHMsIGFuZCB0aG9zZSBjYWxjdWxhdGVkIHVzaW5nIHRoZSBgc3J2eXJgIHBhY2thZ2UuIFdlIHdpbGwgYWxzbyBtYWtlIHRoZSBiYWNrZ3JvdW5kIG9mIHRoZSBwbG90IHdoaXRlLgoKYGBge3J9CnRpdGxlX2ZpbmFsIDwtIGdnZHJhdygpICsKICBkcmF3X2xhYmVsKAogICAgZXhwcmVzc2lvbigiV2hhdCBpcyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZS1jaWdhcmV0dGUgdXNlIGFuZCB0b2JhY2NvIHVzZT8iKSwKICAgIGZvbnRmYWNlID0gJ2JvbGQnLAogICAgc2l6ZSA9IDE2LAogICAgeCA9IDAuNSkgKwogIHRoZW1lKAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMCwgMCwgMCwgMCkKICApCgpzdWJ0aXRsZV91d19maW5hbCA8LSBnZ2RyYXcoKSArCiAgZHJhd19sYWJlbCgKICAgIGV4cHJlc3Npb24ofiA2XnRoIH4gIi0iIH4gMTJedGggfiAiZ3JhZGVycywgdW53ZWlnaHRlZCIpLAogICAgc2l6ZSA9IDEyLAogICAgeCA9IDAuNSkgKwogIHRoZW1lKAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMCwgMCwgMCwgMCkKICApCgpzdWJ0aXRsZV93X2ZpbmFsIDwtIGdnZHJhdygpICsKICBkcmF3X2xhYmVsKAogICAgZXhwcmVzc2lvbih+IDZedGggfiAiLSIgfiAxMl50aCB+ICJncmFkZXJzLCB3ZWlnaHRlZCIpLAogICAgZm9udGZhY2UgPSAnYm9sZCcsCiAgICBzaXplID0gMTIsCiAgICB4ID0gMC41KSArCiAgdGhlbWUoCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCAwLCAwLCAwKQogICkKCnN1YnRpdGxlX3dfOF9maW5hbCA8LSBnZ2RyYXcoKSArCiAgZHJhd19sYWJlbCgKICAgIGV4cHJlc3Npb24oIkFwcHJveGltYXRlIGdyYWR1YXRpbmcgXG4gY2xhc3Mgb2YgMjAxOSwgd2VpZ2h0ZWQiKSwKICAgIGZvbnRmYWNlID0gJ2JvbGQnLAogICAgc2l6ZSA9IDEyLAogICAgeCA9IDAuNSkgKwogIHRoZW1lKAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMCwgMCwgMCwgMCkKICApCgpzdWJ0aXRsZV9maW5hbCA8LSBwbG90X2dyaWQoc3VidGl0bGVfdXdfZmluYWwsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZV93X2ZpbmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGVfd184X2ZpbmFsLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMpCgpwbG90c0FfdGl0bGVfZmluYWwgPC0gZ2dkcmF3KCkgKwogIGRyYXdfbGFiZWwoCiAgICBleHByZXNzaW9uKCJQcmV2YWxlbmNlIG9mIGFueSB0b2JhY2NvIHByb2R1Y3QgdXNlIGJ5IHVzZXIgdHlwZSIpLAogICAgc2l6ZSA9IDE0LAogICAgeCA9IDAuNSkgKwogIHRoZW1lKAogICAgcGxvdC5tYXJnaW4gPSBtYXJnaW4oMCwgMCwgMCwgMCkKICApCgpwbG90c0FfZmluYWwgPC0gcGxvdF9ncmlkKHBsb3RBX3V3ICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdEFfdyArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RBX3dfOCArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIG5jb2wgPSAzLAogICAgICAgICAgICAgICAgICAgICAgICAgIGFsaWduID0gInYiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGF4aXMgPSAiYnQiKQoKcGxvdHNCX3RpdGxlX2ZpbmFsIDwtIGdnZHJhdygpICsKICBkcmF3X2xhYmVsKAogICAgZXhwcmVzc2lvbigiUHJldmFsZW5jZSBvZiBldmVyIHRyeWluZyBieSBwcm9kdWN0IHR5cGUiKSwKICAgIHNpemUgPSAxNCwKICAgIHggPSAwLjUpICsKICB0aGVtZSgKICAgIHBsb3QubWFyZ2luID0gbWFyZ2luKDAsIDAsIDAsIDApCiAgKQoKcGxvdHNCX2ZpbmFsIDwtIHBsb3RfZ3JpZChwbG90Ql91dyArIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgIHBsb3RCX3cgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90Ql93XzggKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBuY29sID0gMywKICAgICAgICAgICAgICAgICAgICAgICAgICBhbGlnbiA9ICJ2IiwKICAgICAgICAgICAgICAgICAgICAgICAgICBheGlzID0gImJ0IikKCnBsb3RzQ190aXRsZV9maW5hbCA8LSBnZ2RyYXcoKSArCiAgZHJhd19sYWJlbCgKICAgIGV4cHJlc3Npb24oIlByZXZhbGVuY2Ugb2YgY3VycmVudCB1c2UgYnkgcHJvZHVjdCB0eXBlIiksCiAgICBzaXplID0gMTQsCiAgICB4ID0gMC41KSArCiAgdGhlbWUoCiAgICBwbG90Lm1hcmdpbiA9IG1hcmdpbigwLCAwLCAwLCAwKQogICkKCnBsb3RzQ19maW5hbCA8LSBwbG90X2dyaWQocGxvdENfdXcgKyB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF9ibGFuaygpKSwKICAgICAgICAgICAgICAgICAgICAgICAgICBwbG90Q193ICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgcGxvdENfd184ICsgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfYmxhbmsoKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYWxpZ24gPSAidiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgYXhpcyA9ICJidCIpCgpsZWdlbmRfZmluYWwgPC0gZ2V0X2xlZ2VuZChwbG90MWMgKwogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxlZ2VuZC5kaXJlY3Rpb24gPSAiaG9yaXpvbnRhbCIpKQoKZmluYWxfcGxvdCA8LSBwbG90X2dyaWQodGl0bGVfZmluYWwsCiAgICAgICAgICBwbG90c0FfdGl0bGVfZmluYWwsCiAgICAgICAgICBzdWJ0aXRsZV9maW5hbCwKICAgICAgICAgIHBsb3RzQV9maW5hbCwKICAgICAgICAgIHBsb3RzQl90aXRsZV9maW5hbCwKICAgICAgICAgIHN1YnRpdGxlX2ZpbmFsLAogICAgICAgICAgcGxvdHNCX2ZpbmFsLAogICAgICAgICAgcGxvdHNDX3RpdGxlX2ZpbmFsLAogICAgICAgICAgc3VidGl0bGVfZmluYWwsCiAgICAgICAgICBwbG90c0NfZmluYWwsCiAgICAgICAgICBsZWdlbmRfZmluYWwsCiAgICAgICAgICBuY29sID0gMSwKICAgICAgICAgIHJlbF9oZWlnaHRzID0gYygwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgMC4yLAogICAgICAgICAgICAgICAgICAgICAgICAgIDAuMTUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAwLjIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgMC4xLAogICAgICAgICAgICAgICAgICAgICAgICAgIDEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgMC4yLAogICAgICAgICAgICAgICAgICAgICAgICAgIDAuMSwKICAgICAgICAgICAgICAgICAgICAgICAgICAxLAogICAgICAgICAgICAgICAgICAgICAgICAgIDAuMikpICsKICB0aGVtZShwbG90LmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICJ3aGl0ZSIpKQoKZmluYWxfcGxvdApgYGAKCmBgYHtyLCBlY2hvPUZBTFNFLCBpbmNsdWRlPUZBTFNFfQpnZ3NhdmUoaGVyZTo6aGVyZSgiaW1nIiwgImZpbmFsX3Bsb3QucG5nIikpCmBgYAoKIyMjICoqU3lub3BzaXMqKgoqKioKCkluIHRoaXMgY2FzZSBzdHVkeSwgd2UgdXNlZCBkYXRhIGZyb20gdGhlIFtOYXRpb25hbCBZb3V0aCBUb2JhY2NvIFN1cnZleSAoTllUUyldKGh0dHBzOi8vd3d3LmNkYy5nb3YvdG9iYWNjby9kYXRhX3N0YXRpc3RpY3Mvc3VydmV5cy9ueXRzL2luZGV4Lmh0bSl7dGFyZ2V0PSJfYmxhbmsifSwgYW4gYW5udWFsIHN1cnZleSB0aGF0IGFza3Mgc3R1ZGVudHMgaW4gaGlnaCBzY2hvb2wgYW5kIG1pZGRsZSBzY2hvb2wgKGdyYWRlcyA2LTEyKSBhYm91dCB0b2JhY2NvIHVzYWdlIGluIHRoZSBVbml0ZWQgU3RhdGVzIG9mIEFtZXJpY2EuIFdlIHVzZWQgZGF0YSBmcm9tICoqMjAxNS0yMDE5KiogZHVlIHRvIHRoZSBmYWN0IHRoYXQgdGhlc2UgeWVhcnMgYXJlIHRoZSBtb3N0IHJlY2VudCB0aGF0IGFza2VkIHF1ZXN0aW9ucyByZWdhcmRpbmcgZS1jaWdhcmV0dGUgdXNhZ2UuIAoKV2UgdXNlZCB0aGlzIGRhdGEgdG8gYW5zd2VyIHRoZXNlIHF1ZXN0aW9uczoKCjEpIEhvdyBoYXMgdG9iYWNjbyBhbmQgZS1jaWdhcmV0dGUvdmFwaW5nIHVzZSBieSBBbWVyaWNhbiB5b3V0aHMgY2hhbmdlZCBzaW5jZSAyMDE1PwoyKSBIb3cgZG9lcyBlLWNpZ2FyZXR0ZSB1c2UgY29tcGFyZSBiZXR3ZWVuIG1hbGVzIGFuZCBmZW1hbGVzPwozKSBXaGF0IHZhcGluZyBicmFuZHMgYW5kIGZsYXZvcnMgYXBwZWFyIHRvIGJlIHVzZWQgdGhlIG1vc3QgZnJlcXVlbnRseT8gIApXZSB3aWxsIGJhc2UgdGhpcyBvbiB0aGUgZm9sbG93aW5nIHN1cnZleSBxdWVzdGlvbnM6ICAgCj4gIkR1cmluZyB0aGUgcGFzdCAzMCBkYXlzLCB3aGF0IGJyYW5kIG9mIGUtY2lnYXJldHRlcyBkaWQgeW91IHVzdWFsbHkgdXNlPyIgICAKPiAiV2hhdCBmbGF2b3JzIG9mIHRvYmFjY28gcHJvZHVjdHMgaGF2ZSB5b3UgdXNlZCBpbiB0aGUgcGFzdAozMCBkYXlzPyIgCgo0KSBJcyB0aGVyZSBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGUtY2lnYXJldHRlL3ZhcGluZyB1c2UgYW5kIG90aGVyIHRvYmFjY28gdXNlPwoKV2Ugc2hvd2VkIGhvdyB0byB3b3JrIHdpdGggdGhlIGRhdGEgaW4gdGhlIGZvcm1hdCBwcm92aWRlZCAoRXhjZWwpLCBob3cgdG8gdG8gdXNlIHRoZSBjb2RlYm9va3MgdG8gZGVjaWRlIHdoYXQgdmFyaWFibGVzIHRvIHVzZSB0byBhbnN3ZXIgb3VyIHF1ZXN0aW9ucyBhbmQgaG93IHRvIGNsZWFuIGFuZCByZWNvZGUgdGhlIGRhdGEgZnJvbSB0aGUgc3VydmV5IGZvciBvdXIgdmlzdWFsaXphdGlvbnMgYW5kIGFuYWx5c2lzLiBXZSBtYWRlIHZpc3VhbGl6YXRpb25zIG9mIG91ciBzdW1tYXJ5IHN0YXRpc3RpY3Mgb3ZlciB0aW1lLCB0byBpbGx1c3RyYXRlIHRoZSB0cmVuZHMgcHJlc2VudCBpbiB0aGUgZGF0YSBmb3IgZGlmZmVyZW50IHByb2R1Y3RzIGFuZCBncm91cHMgb2Ygc3R1ZGVudCByZXNwb25kZW50cy4KCkluIGFuc3dlciB0byBvdXIgcXVlc3Rpb25zLCB3ZSBmb3VuZCB0aGF0IHRvYmFjY28gdXNlIGhhcyBnb25lIHVwIHNsaWdodGx5IG92ZXJhbGwgYmV0d2VlbiAyMDE1IGFuZCAyMDE5LCB3aXRoIGxpdHRsZSBkaWZmZXJlbmNlIGluIHJhdGVzIG9mIGNoYW5nZSBjb21wYXJpbmcgbWFsZXMgdG8gZmVtYWxlcy4gVGhpcyBzbGlnaHQgaW5jcmVhc2UgaXMgdGhlIHJlc3VsdCBvZiBhIGxhcmdlIGluY3JlYXNlIGluIGUtY2lnYXJldHRlL3ZhcGluZyB1c2UsIGNvdXBsZWQgd2l0aCBhIGRlY3JlYXNlIGluIHVzZSBvZiBvdGhlciB0b2JhY2NvIHByb2R1Y3RzLiBUaGUgbW9zdCB1c2VkIGJyYW5kIG9mIGUtY2lnYXJldHRlL3ZhcGluZyBwcm9kdWN0cyBpcyBKdXVsLCBhbmQgZnJ1aXQsIG1lbnRob2wgYW5kIGNhbmR5L2Rlc3NlcnRzL3N3ZWV0cyBhcmUgdGhlIG1vc3QgY29tbW9ubHkgdXNlZCBmbGF2b3JzLgoKV2UgdGhlbiBpbnRyb2R1Y2VkIHRoZSBzdGF0aXN0aWNhbCBjb25jZXB0IG9mIHN1cnZleSB3ZWlnaHRpbmcsIGlsbHVzdHJhdGluZyBob3cgdG8gY2FsY3VsYXRlIHVzYWdlIHBlcmNlbnRhZ2VzIHVzaW5nIHN1cnZleS13ZWlnaHRlZCBtZWFucywgYW5kIGNvbXBhcmUgdGhlIHJlc3VsdHMgaW4gdGhlIHdlaWdodGVkIGFuZCB1bndlaWdodGVkIGNhc2VzLiBXZSBhbHNvIGludHJvZHVjZWQgdGhlIHRvcGljIG9mIGxvZ2lzdGljIHJlZ3Jlc3Npb24gYW5kIHdlIHBlcmZvcm1lZCBhIHN1cnZleS13ZWlnaHRlZCBsb2dpc3RpYyByZWdyZXNzaW9uIGFuYWx5c2lzIHRvIGNvbXBhcmUgdGhlIHZhcGluZyByYXRlcyBvZiBtYWxlIGFuZCBmZW1hbGUgc3R1ZGVudHMuIAoKIyMgKipTdWdnZXN0ZWQgSG9tZXdvcmsqKgoqKiogCgo8c3R5bGU+CmRpdi5ibHVlIHsgYmFja2dyb3VuZC1jb2xvcjojZTZmMGZmOyBib3JkZXItcmFkaXVzOiA1cHg7IHBhZGRpbmc6IDIwcHg7fQo8L3N0eWxlPgo8ZGl2IGNsYXNzID0gImJsdWUiPgoKKyBDYWxjdWxhdGUgY29uZmlkZW5jZSBpbnRlcnZhbHMgZm9yIHRoZSB1bndlaWdodGVkIGVzdGltYXRlcyBhbmQgYWRkIHRoZSBhcHByb3ByaWF0ZSBlcnJvciBiYXJzIHRvIHRoZSBtYWluIGZpZ3VyZXMuCisgQXBwbHkgc3VydmV5IHdlaWdodHMgdG8gb25lIG9mIHRoZSBmaWd1cmVzIHByb2R1Y2VkIGluIHRoaXMgY2FzZSBzdHVkeSBpbiB3aGljaCB3ZWlnaHRlZCBlc3RpbWF0ZXMgd2VyZSBub3QgcHJvZHVjZWQuIEluY2x1ZGUgZXJyb3IgYmFycyBpbiB0aGUgdXBkYXRlZCBmaWd1cmUuCiAgICArIERvZXMgdGhlIGZpZ3VyZSBjaGFuZ2UgYWZ0ZXIgdGhlIGFwcGxpY2F0aW9uIG9mIHN1cnZleSB3ZWlnaHRzPwogICAgKyBJZiBzbywgZGVzY3JpYmUgaG93LiAKKyBSZXByb2R1Y2UgYGZpbmFsX3Bsb3RgIGFib3ZlIGZvciBhIGRpZmZlcmVudCBjb2hvcnQgb2YgeW91ciBjaG9pY2UuCisgRm9jdXNpbmcgb24gYSBzaW5nbGUgeWVhciBvZiBkYXRhLCBleHBsb3JlIGRlbW9ncmFwaGljIGZhY3RvcnMgdGhhdCBjb250cmlidXRlIHRvIHRvYmFjY28gdXNlIG9mIHNvbWUga2luZC4gQ29tcGFyZSByZXN1bHRzIG9mIHVud2VpZ2h0ZWQgYW5kIHdlaWdodGVkIGFuYWx5c2lzIChmb3IgZXhhbXBsZSwgdXNpbmcgdGhlIGBzdnlnbG1gIGZ1bmN0aW9uIHRvIGNhbGN1bGF0ZSBzdXJ2ZXktd2VpZ2h0ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBlc3RpbWF0ZXMpLgoKPC9kaXY+CgoKIyMgKipBZGRpdGlvbmFsIEluZm9ybWF0aW9uKioKKioqIAoKIyMjICoqSGVscGZ1bCBMaW5rcyoqCioqKgoKW1RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gIApbV3JpdGluZyBmdW5jdGlvbnNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZnVuY3Rpb25zLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gICAKW0NvZGVib29rc10oaHR0cHM6Ly93d3cubGliLm5jc3UuZWR1L2RhdGEvaWNwc3JmYXEjd2hhdGFyZSl7dGFyZ2V0PSJfYmxhbmsifSAgCltMb25naXR1ZGluYWwgc3R1ZGllc10oaHR0cHM6Ly93d3cuYm1qLmNvbS9hYm91dC1ibWovcmVzb3VyY2VzLXJlYWRlcnMvcHVibGljYXRpb25zL2VwaWRlbWlvbG9neS11bmluaXRpYXRlZC83LWxvbmdpdHVkaW5hbC1zdHVkaWVzKXt0YXJnZXQ9Il9ibGFuayJ9ICAgCltQYW5lbCBkYXRhXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9QYW5lbF9kYXRhKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIApbQ3Jvc3Mtc2VjdGlvbmFsIGRhdGFdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Nyb3NzLXNlY3Rpb25hbF9kYXRhKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIApbU3VydmV5IHdlaWdodGluZ10oaHR0cDovL3d3dy5hcHBsaWVkLXN1cnZleS1tZXRob2RzLmNvbS93ZWlnaHQuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSAgCltDb25maWRlbmNlIGludGVydmFsc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29uZmlkZW5jZV9pbnRlcnZhbCl7dGFyZ2V0PSJfYmxhbmsifSAgIApbSW50cm9kdWN0aW9uIHRvIExvZ2FyaXRobXNdKGh0dHBzOi8vd3d3Lm1hdGhzaXNmdW4uY29tL2FsZ2VicmEvbG9nYXJpdGhtcy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgCltMb2dhcml0aG1dKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xvZ2FyaXRobSl7dGFyZ2V0PSJfYmxhbmsifSAKW1J1bGVzIG9mIGxvZ3NdKGh0dHBzOi8vd3d3LnJhcGlkdGFibGVzLmNvbS9tYXRoL2FsZ2VicmEvTG9nYXJpdGhtLmh0bWwjbG9nLXJ1bGVzKXt0YXJnZXQ9Il9ibGFuayJ9IFtPZGRzIHJhdGlvXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9PZGRzX3JhdGlvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIApbTG9nIG9kZHNdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xvZ2l0KXt0YXJnZXQ9Il9ibGFuayJ9ICAgClsyeDIgdGFibGVdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvbnRpbmdlbmN5X3RhYmxlKXt0YXJnZXQ9Il9ibGFuayJ9ICAKW1Byb2JhYmlsaXR5XShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9Qcm9iYWJpbGl0eSl7dGFyZ2V0PSJfYmxhbmsifSAgIApbTGlrZWxpaG9vZCBmdW5jdGlvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGlrZWxpaG9vZF9mdW5jdGlvbil7dGFyZ2V0PSJfYmxhbmsifSAgIApbTWF4aW11bSBsaWtlbGlob29kIGVzdGltYXRlc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTWF4aW11bV9saWtlbGlob29kX2VzdGltYXRpb24pe3RhcmdldD0iX2JsYW5rIn0gICAKW0xpbmVhciByZWdyZXNzaW9uIG1vZGVsXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MaW5lYXJfcmVncmVzc2lvbil7dGFyZ2V0PSJfYmxhbmsifSAgIApbTG9naXN0aWMgcmVncmVzc2lvbl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTG9naXN0aWNfcmVncmVzc2lvbil7dGFyZ2V0PSJfYmxhbmsifSAgIApbUXVhc2ktbGlrZWxpaG9vZF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUXVhc2ktbGlrZWxpaG9vZCl7dGFyZ2V0PSJfYmxhbmsifSAgIApbQmlub21pYWwgZGlzdHJpYnV0aW9uXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9CaW5vbWlhbF9kaXN0cmlidXRpb24pe3RhcmdldD0iX2JsYW5rIn0gICAKCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIGxpbmVhciByZWdyZXNzaW9uIHNlZSB0aGlzIFtib29rXShodHRwczovL3JhZmFsYWIuZ2l0aHViLmlvL2RzYm9vay9saW5lYXItbW9kZWxzLmh0bWwjbGluZWFyLXJlZ3Jlc3Npb24taW4tdGhlLXRpZHl2ZXJzZSl7dGFyZ2V0PSJfYmxhbmsifSBhbmQgdGhpcyBbY2FzZSBzdHVkeV0oaHR0cHM6Ly9vcGVuY2FzZXN0dWRpZXMuZ2l0aHViLmlvL29jcy1icC1kaWV0Lyl7dGFyZ2V0PSJfYmxhbmsifS4KCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHN1cnZleSBkZXNpZ25zIHNlZSBbaGVyZV0oaHR0cDovL3d3dy5hc2Fzcm1zLm9yZy9Qcm9jZWVkaW5ncy95MjAwOC9GaWxlcy8zMDE4MzUucGRmKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZCBbaGVyZV0oaHR0cDovL29jdy5qaHNwaC5lZHUvY291cnNlcy9TdGF0TWV0aG9kc0ZvclNhbXBsZVN1cnZleXMvUERGcy9MZWN0dXJlNS5wZGYpe3RhcmdldD0iX2JsYW5rIn0uICAKCkZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHN1cnZleSBhbmFseXNpcyBpbiBSIFtoZXJlXShodHRwczovL3Itc3VydmV5LnItZm9yZ2Uuci1wcm9qZWN0Lm9yZy9zdXJ2ZXkvZXhtYW1wbGUtbG9uZWx5Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gYW5kIFtoZXJlXShodHRwOi8vci1zdXJ2ZXkuci1mb3JnZS5yLXByb2plY3Qub3JnL3N1cnZleS9odG1sL3N1cnZleW9wdGlvbnMuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifS4gICAKCklmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBhbiBpbmZvLWdyYXBoaWMgc3VtbWFyeSBvZiB0aGUgMjAxOSBmaW5kaW5ncywgYW5kIGxpbmtzIHRvIG1hbnkgbW9yZSByZXNvdXJjZXMgYWJvdXQgdGhpcyB0b3BpYyBhbmQgZGF0YXNldCwgc2VlIHRoZSBGREEncyB3ZWJzaXRlIFtoZXJlXShodHRwczovL3d3dy5mZGEuZ292L3RvYmFjY28tcHJvZHVjdHMveW91dGgtYW5kLXRvYmFjY28veW91dGgtdG9iYWNjby11c2UtcmVzdWx0cy1uYXRpb25hbC15b3V0aC10b2JhY2NvLXN1cnZleSl7dGFyZ2V0PSJfYmxhbmsifS4KCjx1PioqUGFja2FnZXMgdXNlZCBpbiB0aGlzIGNhc2Ugc3R1ZHk6Kio8L3U+CgpQYWNrYWdlICAgfCBVc2UgaW4gdGhpcyBjYXNlIHN0dWR5ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAKLS0tLS0tLS0tLSB8LS0tLS0tLS0tLS0tLQpbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2plbm55YmMvaGVyZV9oZXJlKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgIHwgdG8gZWFzaWx5IGxvYWQgYW5kIHNhdmUgZGF0YSAgCltyZWFkeGxdKGh0dHBzOi8vcmVhZHhsLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgICAgfCB0byBpbXBvcnQgdGhlIGRhdGEgaW4gdGhlIGV4Y2VsIGZpbGVzIApbbWFncml0dHJdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9tYWdyaXR0ci92aWduZXR0ZXMvbWFncml0dHIuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifSB8IHRvIHVzZSB0aGUgY29tcG91bmQgYXNzaWdubWVudCBwaXBlIG9wZXJhdG9yIGAlPD4lYApbc3RyaW5ncl0oaHR0cHM6Ly9zdHJpbmdyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvc3RyaW5nci5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gbWFuaXB1bGF0ZSB0aGUgY2hhcmFjdGVyIHN0cmluZ3Mgd2l0aGluIHRoZSBkYXRhICAKW3B1cnJyXShodHRwczovL3B1cnJyLnRpZHl2ZXJzZS5vcmcvKXt0YXJnZXQ9Il9ibGFuayJ9ICAgfCB0byBpbXBvcnQgdGhlIGRhdGEgaW4gYWxsIHRoZSBkaWZmZXJlbnQgZXhjZWwgYW5kIGNzdiBmaWxlcyBlZmZpY2llbnRseQpbZHBseXJdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGFycmFuZ2UvZmlsdGVyL3NlbGVjdC9jb21wYXJlIHNwZWNpZmljIHN1YnNldHMgb2YgdGhlIGRhdGEgIApbcmVhZHJdKGh0dHBzOi8vcmVhZHIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIGltcG9ydCB0aGUgQ1NWIGZpbGUgZGF0YQpbdGlkeXJdKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy8pe3RhcmdldD0iX2JsYW5rIn0gICAgICB8IHRvIHJlYXJyYW5nZSBkYXRhIGluIHdpZGUgYW5kIGxvbmcgZm9ybWF0cyAKW2dncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICB8IHRvIG1ha2UgdmlzdWFsaXphdGlvbnMgd2l0aCBtdWx0aXBsZSBsYXllcnMKW3NjYWxlc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NjYWxlcy9zY2FsZXMucGRmKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gYWxsb3cgdXMgdG8gbG9vayBhdCB0aGUgY29sb3JzIHdpdGhpbiB0aGUgdmlyaWRpcyBwYWNrYWdlClt2aXJpZGlzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvdmlyaWRpcy92aWduZXR0ZXMvaW50cm8tdG8tdmlyaWRpcy5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9ICAgIHwgdG8gbWFrZSBwbG90cyB3aXRoIGEgY29sb3IgcGFsZXR0ZSB0aGF0IGlzIGNvbXBhdGlibGUgd2l0aCBjb2xvciBibGluZG5lc3MKW2ZvcmNhdHNdKGh0dHBzOi8vZm9yY2F0cy50aWR5dmVyc2Uub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSAgICB8IHRvIGFsbG93IGZvciByZW9yZGVyaW5nIG9mIGZhY3RvcnMgaW4gcGxvdHMKW25hbmlhcl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL25hbmlhci92aWduZXR0ZXMvZ2V0dGluZy1zdGFydGVkLXctbmFuaWFyLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gIHwgdG8gbWFrZSBhIHZpc3VhbGl6YXRpb24gb2YgbWlzc2luZyBkYXRhCltzeXJ2cl0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3NydnlyL3NydnlyLnBkZil7dGFyZ2V0PSJfYmxhbmsifSB8IHRvIHVzZSBzdXJ2ZXkgd2VpZ2h0cwpbY293cGxvdF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Nvd3Bsb3QvdmlnbmV0dGVzL2ludHJvZHVjdGlvbi5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9IHwgdG8gYWxsb3cgcGxvdHMgdG8gYmUgY29tYmluZWQgClticm9vbV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2Jyb29tL3ZpZ25ldHRlcy9icm9vbS5odG1sKXt0YXJnZXQ9Il9ibGFuayJ9IHwgdG8gY3JlYXRlIG5pY2VseSBmb3JtYXR0ZWQgbW9kZWwgb3V0cHV0CltzdXJ2ZXldKGh0dHA6Ly9yLXN1cnZleS5yLWZvcmdlLnItcHJvamVjdC5vcmcvc3VydmV5L2luZGV4Lmh0bWwpe3RhcmdldD0iX2JsYW5rIn0gfCB0byBmaXQgc3VydmV5LXdlaWdodGVkIGxvZ2lzdGljIHJlZ3Jlc3Npb24KCiMjIyAqKlNlc3Npb24gaW5mbyoqCioqKgoKYGBge3J9CnNlc3Npb25JbmZvKCkKYGBgCgoqKkVzdGltYXRlIG9mIFJNYXJrZG93biBDb21waWxhdGlvbiBUaW1lOiAqKgoKYGBge3IsIGVjaG89RkFMU0V9CnJtYXJrZG93bjo6OnBlcmZfdGltZXJfc3RvcCgicmVuZGVyIikKcHRzID0gcm1hcmtkb3duOjo6cGVyZl90aW1lcl9zdW1tYXJ5KCkKY2F0KCJBYm91dCIsIHJvdW5kKHB0cyR0aW1lWzFdLzEwMDAgKyA1KSwgIi0iLCByb3VuZChwdHMkdGltZVsxXS8xMDAwICsgMTUpLCJzZWNvbmRzIikKYGBgCgpUaGlzIGNvbXBpbGF0aW9uIHRpbWUgd2FzIG1lYXN1cmVkIG9uIGEgUEMgbWFjaGluZSBvcGVyYXRpbmcgb24gV2luZG93cyAxMC4gVGhpcyByYW5nZSBzaG91bGQgb25seSBiZSB1c2VkIGFzIGFuIGVzdGltYXRlIGFzIGNvbXBpbGF0aW9uIHRpbWUgd2lsbCB2YXJ5IHdpdGggZGlmZmVyZW50IG1hY2hpbmVzIGFuZCBvcGVyYXRpbmcgc3lzdGVtcy4gCgojIyMgKipBY2tub3dsZWRnbWVudHMqKgoqKioKCldlIHdvdWxkIGxpa2UgdG8gYWNrbm93bGVkZ2UgW1JlbmVlIEpvaG5zb25dKGh0dHBzOi8vd3d3Lmpoc3BoLmVkdS9mYWN1bHR5L2RpcmVjdG9yeS9wcm9maWxlLzI4NDgvcmVuZWUtbS1qb2huc29uKSBmb3IgYXNzaXN0aW5nIGluIGZyYW1pbmcgdGhlIG1ham9yIGRpcmVjdGlvbiBvZiB0aGUgY2FzZSBzdHVkeSBhbmQgZm9yIHJldmlld2luZyB0aGUgY2FzZSBzdHVkeSBmb3Igc3ViamVjdCBtYXR0ZXIgY29udGVudC4KCldlIHdvdWxkIGxpa2UgdG8gYWNrbm93bGVkZ2UgW01pY2hhZWwgQnJlc2hvY2tdKGh0dHBzOi8vbWJyZXNob2NrLmdpdGh1Yi5pby8pIGZvciBoaXMgY29udHJpYnV0aW9ucyB0byB0aGlzIGNhc2Ugc3R1ZHkgYW5kIGRldmVsb3BpbmcgdGhlIGBPQ1NkYXRhYCBwYWNrYWdlLiAKCldlIHdvdWxkIGFsc28gbGlrZSB0byBhY2tub3dsZWRnZSB0aGUgW0Jsb29tYmVyZyBBbWVyaWNhbiBIZWFsdGggSW5pdGlhdGl2ZV0oaHR0cHM6Ly9hbWVyaWNhbmhlYWx0aC5qaHUuZWR1LykgZm9yIGZ1bmRpbmcgdGhpcyB3b3JrLiAKCjxzY3JpcHQgdHlwZT0ndGV4dC9qYXZhc2NyaXB0JyBpZD0nY2x1c3RybWFwcycgc3JjPScvL2Nkbi5jbHVzdHJtYXBzLmNvbS9tYXBfdjIuanM/Y2w9MDgwODA4Jnc9YSZ0PXR0JmQ9dHlnRFlfVGJHN1hFMkhWVUM2RW94Nlo3NGkyRTVNVUJxQ0E4M1BnSjlTWSZjbz1mZmZmZmYmY21vPTNhY2MzYSZjbW49ZmY1MzUzJmN0PTgwODA4MCc+PC9zY3JpcHQ+Cgo=