1 Introduction

The first important aspect of data analysis is to get the data in the correct format. This is sometimes referred to as getting the data into the tidy format. A tidy dataset is a prerequisite for the downstream data visualization and statistical analyses. Almost always, the first thing you would do when you start a data analysis is to tidy the data first.

In this lesson, you will learn how to use the dplyr + tidyr packages to achieve tidy datasets from “raw” datasets, as well as basic operations to data tables. We will cover:

  1. Basic concepts of tabular data and what is a tidy dataset?
  2. Basic operations to a data table: select(), filter(), mutate(), group_by() and summarise().
  3. Basic operations to multiple tables: binding, joining

As a resource, you can download the data wrangling cheat sheet

2 load packages

You might need to install some of these packages.

library(tidyverse)
── Attaching core tidyverse packages ───────────────────────── tidyverse 2.0.0 ──
✔ dplyr     1.1.3     ✔ readr     2.1.4
✔ forcats   1.0.0     ✔ stringr   1.5.0
✔ ggplot2   3.4.4     ✔ tibble    3.2.1
✔ lubridate 1.9.3     ✔ tidyr     1.3.0
✔ purrr     1.0.2     ── Conflicts ─────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag()    masks stats::lag()
ℹ Use the ]8;;http://conflicted.r-lib.org/conflicted package]8;; to force all conflicts to become errors
library(readxl)

The tidyverse a collection of R packages that share the same syntax and coding style. These packages work in harmony and provide simple solutions to data analysis problems. The readxl package allows us to read directly from excel files.

library(tidyverse)

These are what we need for now. We will install more as we move into more complex activities. Again the codes in text are highlighted in grey.

3 Tidy dataset makes your life easy!

First let’s start with an example.

USDA_2022 <- read_csv(file = "../Data/USDA_2022_matrix.csv")

head(USDA_2022)
USDA_2022 <- read_csv(file = "../Data/USDA_2022_matrix.csv")

head(USDA_2022)
library(dplyr)

Here are data queried from USDA National Agriculture Statitics Service

In this table, each row is a crop, and each column is the yield at a given year.

3.1 Data frames

This table is called a data frame in the jargon of R. A data frame is simply a data table. It has rows and columns. An important aspect of data frame is that rows of the same column must be the same class of variable. For example, in column “Crop”, all values in column “Crop” are characters. In column “2022”, all values in that column are numeric.
This is just one thing to keep in mind when working with data frames.

3.2 Tidy data

Not all data frames are tidy. The idea of tidy data means

  1. Each row has to be an observation
  2. Each column has be a variable

Getting your data into the tidy format will save you A LOT OF TIME in all your downstream analyses. A good practice is to tidy your data FIRST before you do any analyses.

Let’s look at this table again:

head(USDA_2022)

Is this table a tidy data frame?

No. Because there are up to 78 years of data for each crop, but the observations are spread out in 78 columns. Observations spreading out in columns is sometimes called the wide format. The wide format is easy for data collection, but unfortunately it’s not how R would like to read data. R likes tidy data, and tidy data frames need each observation to be in its individual row.

To convert a wide table into a tidy table, the command is pivot_longer() The syntax is pivot_longer(columns, names_to = "name", values_to = "value")

USDA_tidy <- USDA_2022 %>% #the %>% ("pipe") operator means taking the output of previous row and use it as input of next row 
  pivot_longer(names_to = "Year", values_to = "Yield", cols = !Crop) 

# cols = !Crop specifies all columns other than Crop. 

head(USDA_tidy)

What this code chunk did is that it made two new columns:

  1. The “name” column (sometimes called the label column) is “Year”: it houses which original columns the data came from (all the years).

  2. The “value” column is “Yield”: it is the variable that was actually measured.

  3. The columns to collect data from were all the years, i.e., not the “Crop” column.

Now this is a tidy data frame. Each row is an observation. Each column is a variable. We have 3 variables:

  • Crop,
  • Year (many years),
  • Yield (numeric).

However, there is just one problem. We should always check each variables are recorded as the correct type of data (e.g., character vs numeric). A quick way is calling str() (stands for “structure”)

str(USDA_tidy)

You will realize in the output it says “$ Year : chr [1:624]”. “chr” stands for character. We need it to be numeric. We will talk about how to change that in a second.

Let’s look at another example. This time it’s a hypothetical example that I made up.

#ignore this chunk. It simulates the data.
example_data <- data.frame(
  "sample1" = c(1, 2, 3),
  "sample2" = c(2, 2, 2),
  "sample3" = c(3, 2, 1)
) %>% 
  mutate(gene = c("gene1", "gene2", "gene3"))

head(example_data)

In this experiment, you are looking at the expression of three genes (gene1 to gene3) in three samples (sample1 to sample3). So you have 3 * 3 = 9 observations. This is not a tidy data frame because the 9 measurements of expression are spread out in 3 columns. We can tidy it using the pivot_longer command:

example_data_tidy <- example_data %>% 
  pivot_longer(names_to = "sample", values_to = "expression", cols = c(sample1, sample2, sample3)) 

example_data_tidy
  1. The “name” column now is “sample”: it houses which original columns the data came from (sample1 to sample3).
  2. The “value” column is “expression”: it is the variable that was actually measured.
  3. The columns to collect data from were sample1 to sample3. Alternatively, cols = !gene (all columns but gene) should also work.

Now this is a tidy data frame. Each row is an observation. Each column is a variable. We have 3 variables: sample (3 samples), genes (gene1 to gene3), and expression (numeric).

4 Basic operations to a data frame.

Now let’s talk about some of the basic operations for data frames, which includes:

  • How to subset a data frame,
  • How to create new columns in a data frame and summarise a data frame.

These are very useful and you will probably use these functions all the time.

4.1 Subsetting

There are actually two ways to subset a data frame: subsetting by columns and subsetting by rows. Let’s do subsetting by columns first.

Using our tidy USDA data now, say we only want the columns count and treatment and leave out the rest of the columns. The command is simple, just select() the columns you want.

head(USDA_tidy)

USDA_tidy_2 <- USDA_tidy %>% 
  select(Crop, Yield) #just selecting Crop and Yield 

head(USDA_tidy_2)

Now we just get two columns.

The more useful subsetting function is subsetting by rows based on the values of one or more columns. The command is filter(). In the USDA data, let’s say we only want years that have data because data were not collected for some years for some crops.

USDA_tidy_3 <- USDA_tidy %>% 
  filter(is.na(Yield) == FALSE) # filter for rows that the Yield value is not NA  

nrow(USDA_tidy)
nrow(USDA_tidy_3)

Now the data frame went from 624 rows to 578 rows. So there were actually a total of 578 of data. Note: in filter(), you should use == to mean “equal”.

Let’s say we only want corn.

USDA_tidy_corn <- USDA_tidy_3 %>% 
  filter(Crop == "corn") 

nrow(USDA_tidy)
nrow(USDA_tidy_corn)

Looks like there are 78 years of data for corn.

Let’s say you want to filter for corn and wheat:

USDA_tidy_corn_wheat <- USDA_tidy_3 %>% 
  filter(Crop == "corn" |
           Crop == "wheat") 

nrow(USDA_tidy)
nrow(USDA_tidy_corn_wheat) 

The vertical bar | means “or” in R. In this chunk you are filtering for either corn or wheat. You shouldn’t use & (“and”) here, because there is no crop that is both corn and wheat.

4.2 Making new columns

Oftentimes you will need to make new variables based on existing ones. The command is mutate(). As a biologist, I don’t think “mutate” sound too intuitive, but it is what it is.

We realized earlier the “Year” column is specified as a character variable. We need to convert that to a numeric variable.

USDA_tidy_3 <- USDA_tidy_3 %>% 
  mutate(Year = as.numeric(Year)) 

head(USDA_tidy_3)

Now you get a new column Year that is recorded as numeric.
As you can imagine, you can do any mathematical operations to a numerical column within mutate().

Let’s do another example. The yield for every crop other than cotton, canola, and rice are recorded in bushel/acre. Cotton, canola, and rice were recorded in lbs.
The number of pounds in a bushel vary by grain.
Let’s say there are 60 lbs in 1 bushel of wheat but only 56 lbs for corn. 48 lbs/bu for barley, 60 lbs/bu for soybean, and 56 lbs for sorghum. Let’s convert all the yield units to lbs/acre.

USDA_tidy_lbs <- USDA_tidy_3 %>% 
  mutate(Yields_lbs = case_when(
    Crop == "barley" ~ Yield * 48,
    Crop == "corn" ~ Yield * 56, 
    Crop == "soybean" ~ Yield * 60,
    Crop == "sorghum" ~ Yield * 56, 
    Crop == "wheat" ~ Yield * 60,
    T ~ Yield
  ))

head(USDA_tidy_lbs)

case_when() is an extremely useful command within mutate(). The syntax is case_when(condition ~ outcome). For example, when Crop is barley, it applies a value of 48 lbs/bu. The T ~ ... inside case_when() stands for “for the rest that are not the above”.

4.3 Summarise

Before you do real statistical inference, maybe you want to just take a look at the summary statistics. This can be done easily using the group_by() and summarise() commands.

Let’s simulate an experiment. In this experiment, let’s say there are two groups: one experimental treatment and a control. The experiment was conducted in two locations: A and B. And let’s say we measured some kind of response. Say I would like the average, standard deviation and number of observations for each group.

# You can ignore this chunk. 
trt <- rnorm(n = 10, mean = 10, sd = 1)
ctrl <- rnorm(n = 10, mean = 6, sd = 1)
location <- c(rep("A", 5), rep("B", 5))

example_experiment <- data.frame(
  ctrl,
  trt,
  location
) %>% 
  pivot_longer(names_to = "group", values_to = "response", cols = 1:2)

head(example_experiment)
example_experiment %>% 
  group_by(group) %>% 
  summarise(
    mean = mean(response),
    sd = sd(response),
    n = n()
  ) %>% 
  ungroup()
  1. group_by() selects which columns you want to summarise from. In this case you just want each group to be a group.
  2. summarise() allows you to call the statistical functions, such as mean, sd, and n.
  3. It’s a good practice to ungroup() after you are done. Most of the time it doesn’t matter, but it will create problems if you don’t and you try to switch/modify grouping later.

Now you just get 2 rows and 4 columns, each row is a group. Say I would like to average the counts for each group and for each location. Can I do that?

example_experiment %>% 
  group_by(group, location) %>% 
  summarise(
    mean = mean(response),
    sd = sd(response),
    n = n()
  ) %>% 
  ungroup()
  1. This time you need to select group and treatment in group_by(). Because you want the mean for each group and each location.
  2. In summarise() you call the mean function.
  3. It’s a good practice to ungroup() after you are done.

Now each group by treatment combination is a row.

5 Basic operations to multiple data frames.

In data analyses, the data might not be all in one file. It is common that the data of interest were recorded in multiple spreadsheets. This is extremely common when you are using data from multiple publications. Each publication may only have one aspect of the data you are interested in. After you download their tables, it’s on you to integrate them into one workable data frame in R.

5.1 Binding

When you need to add more rows (observations) into a data frame, you need to bind them as rows. Let’s use our hypothetical example again

example_data_tidy

Let’s say you are also interested in gene4 and gene5. And say the expression of gene4 and gene5 were studied in another experiment.

# ignore this chunk. It simulates the data 
example_data_tidy_2 <- data.frame(
  "sample1" = c(4, 5),
  "sample2" = c(2, 2),
  "sample3" = c(5, 4)
) %>% 
  mutate(gene = c("gene4", "gene5")) %>%
  gather("sample", "expression", c(sample1, sample2, sample3))

example_data_tidy_2

What you’ll do is simply bind them as rows using the rbind() command. “rbind” stands for “row bind”.

example_data_tidy_3 <- rbind(
  example_data_tidy,
  example_data_tidy_2
)

example_data_tidy_3

Now they are in one table. When you do rbind(), just make sure they have the same columns. But other than that, its pretty simple.

5.2 Joining

More often you’ll need to add different columns from another data frame. The jargon for that is joining. This sounds abstract, so let’s look at an example.

child_mortality <- read_csv("../Data/child_mortality_0_5_year_olds_dying_per_1000_born.csv", col_types = cols()) 
babies_per_woman <- read_csv("../Data/children_per_woman_total_fertility.csv", col_types = cols()) 

These are two datasets downloaded from the Gapminder foundation. The Gapminder foundation has datasets on life expectancy, economy, education, and population across countries and years. The goal is to remind us not only the “gaps” between developed and developing worlds, but also the amazing continuous improvements of quality of life through time.

  1. Child mortality (0 - 5 year old) dying per 1000 born.
  2. Births per woman.

These were recorded from year 1800 and projected all the way to 2100.

Let’s look at them.

head(child_mortality)
head(babies_per_woman)

In each table, you have a column for country. Then each year is a column, in which the numbers were recorded for the respective tables.

Say you want to know if births per woman is correlated with child mortality. You may hypothesize that in countries with high child mortality, women give more birth.

To test this hypothesis, you need to first get the two datasets in one data frame. There are a few commands for joining in R:

  1. left_join(X, Y) keeps all the rows in X.
  2. right_join(X, Y) keeps all the rows in Y.
  3. inner_join(X, Y) only keeps rows that are in common.
  4. full_join(X, Y) keeps all rows, filling in NA when values are missing from either one.

Usually, inner_join() will work well. When you visualize data you filter out NA anyways.

So now let’s inner_join them. But wait…
These tables are not in the tidy format! The year values are spread out across many columns, so you need to pivot them to “long” or tidy format first.

babies_per_woman_tidy <- babies_per_woman %>% 
  pivot_longer(names_to = "year", values_to = "birth", cols = c(2:302)) 

head(babies_per_woman_tidy)

child_mortality_tidy <- child_mortality %>% 
  pivot_longer(names_to = "year", values_to = "death_per_1000_born", cols = c(2:302)) 

head(child_mortality_tidy)

Now each table has a year column, a value column (either birth or death). The c(2:302) argument in pivot_longer() specifies gathering data from 2nd through the 302nd column. Now we can join them.

We will use X %>% inner_join(Y, by = common columns) syntax. In this case we have two common columns, country and year. This is asking R to match the countries and years.

birth_and_mortality <- babies_per_woman_tidy %>% 
  inner_join(child_mortality_tidy, by = c("country", "year"))

head(birth_and_mortality)

So now you have a table with 4 variables: countries, year, birth per woman and death per 1000 born. Now you have all the data together.

To quickly test our hypothesis that higher morality is correlated with more birth per woman, we can plot it. There are too much data to look at, but let’s just pull out the year 1945, when WWII ended. We will talk about how to use ggplot later, so you can ignore this chunk for now.

birth_and_mortality %>% 
  filter(year == 1945) %>% 
  ggplot(aes(x = birth, y = death_per_1000_born)) +
  geom_point(alpha = 0.8) +
  geom_smooth(method = "lm", se = F) +
  labs(x = "No. children per woman",
       y = "Child mortality/1000 born",
       title = "Year 1945") +
  theme_classic()

ggsave("../Results/02_scatter_plot.png", width = 2.5, height = 2.5)

You do see a upward trend, supporting our hypothesis. In countries with higher child mortality, women also tended to give more births.

6 Exercise

income_tidy 

You have learned data arrangement! Let’s do an exercise to practice what you have learned today. As the example, this time we will use income per person dataset from Gapminder foundation.

income <- read_csv("../Data/income_per_person_gdppercapita_ppp_inflation_adjusted.csv", col_types = cols()) 
head(income)

6.1 Tidy data

Is this a tidy data frame?

No. This is not a tidy data frame.

income <- read_csv("../Data/income_per_person_gdppercapita_ppp_inflation_adjusted.csv")
Rows: 193 Columns: 242── Column specification ─────────────────────────────────────────────────────────
Delimiter: ","
chr   (1): country
dbl (241): 1800, 1801, 1802, 1803, 1804, 1805, 1806, 1807, 1808, 1809, 1810, ...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(income)
income_tidy <- income %>%
  pivot_longer(cols = 2:242, names_to = "year", values_to = "income")

head(income_tidy)

Make it a tidy data frame using this code chunk.

income_tidy <- income %>% pivot_longer(cols = 2:242, names_to = "year", values_to = "income")

head(income_tidy)
str(income_tidy)
income_tidy <- income %>% pivot_longer(cols = 2:242, names_to = "year", values_to = "income")

head(income_tidy)

Hint: the years are spread out from columns 2 to 242.

 str(income)

6.2 Joining data

Combine the income data with birth per woman and child mortality data using this code chunk. Name the new data frame “birth_and_mortality_and_income”.

birth_and_mortality_and_income <- birth_and_mortality %>% 
  inner_join(income_tidy, by = c("year", "country"))

head(birth_and_mortality_and_income)

6.3 Filtering data

Filter out the data for Bangladesh and Sweden, in years 1945 (when WWII ended) and 2010. Name the new data frame BS_1945_2010. How has income, birth per woman and child mortality rate changed during this 55-year period?

Answer; Birth per woman and Child Mortality rates have dropped significantly.

BS_1945_2010 <- birth_and_mortality_and_income %>%
  filter(year == 1945 |
           year == 2021) %>%
  filter(country == "Bangladesh" |
           country == "Sweden")
head(BS_1945_2010)
NA

6.4 Mutate data

Let’s say for countries with income between 1000 to 10,000 dollars per year, they are called “fed”. For countries with income above 10,000 dollars per year, they are called “wealthy”. Below 1000, they are called “poor”.

Using this info to make a new column called “status”. Hint: you will have to use case_when() and the “&” logic somewhere in this chunk.

birth_and_mortality_and_income <- birth_and_mortality_and_income %>%
  mutate(status = case_when(income < 1000 ~ "poor",
                            income >= 1000 &
                              income < 10000 ~ "fed",
                            T ~ "wealthy"))
head(birth_and_mortality_and_income)
NA

6.5 Summarise the data

Let’s look at the average child mortality and its sd in year 2010. across countries across different status that we just defined. Name the new data frame “child_mortality_summmary_2010”.

birth_and_mortality_and_income %>%
  filter(year == 2010) %>%
  group_by(status) %>%
  summarise(
    mean = mean(death_per_1000_born),
    sd = sd(death_per_1000_born)
  )
NA
NA

How does child mortality compare across income group in year 2010?

Answer; Child mortality is highest in the “poor” group.

LS0tDQp0aXRsZTogIkRhdGEgQXJyYW5nZW1lbnQiDQphdXRob3I6ICJDaGVueGluIExpIg0KZGF0ZTogIjAxLzA2LzIwMjMiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6DQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDogeWVzDQogIGh0bWxfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQojIEludHJvZHVjdGlvbg0KDQpUaGUgZmlyc3QgaW1wb3J0YW50IGFzcGVjdCBvZiBkYXRhIGFuYWx5c2lzIGlzIHRvIGdldCB0aGUgZGF0YSBpbiB0aGUgY29ycmVjdCBmb3JtYXQuDQpUaGlzIGlzIHNvbWV0aW1lcyByZWZlcnJlZCB0byBhcyBnZXR0aW5nIHRoZSBkYXRhIGludG8gdGhlIHRpZHkgZm9ybWF0Lg0KQSB0aWR5IGRhdGFzZXQgaXMgYSBwcmVyZXF1aXNpdGUgZm9yIHRoZSBkb3duc3RyZWFtIGRhdGEgdmlzdWFsaXphdGlvbiBhbmQgc3RhdGlzdGljYWwgYW5hbHlzZXMuDQpBbG1vc3QgYWx3YXlzLCB0aGUgZmlyc3QgdGhpbmcgeW91IHdvdWxkIGRvIHdoZW4geW91IHN0YXJ0IGEgZGF0YSBhbmFseXNpcyBpcyB0byB0aWR5IHRoZSBkYXRhIGZpcnN0Lg0KDQpJbiB0aGlzIGxlc3NvbiwNCnlvdSB3aWxsIGxlYXJuIGhvdyB0byB1c2UgdGhlIGBkcGx5cmAgKyBgdGlkeXJgIHBhY2thZ2VzIHRvIGFjaGlldmUgdGlkeSBkYXRhc2V0cyBmcm9tICJyYXciIGRhdGFzZXRzLCBhcyB3ZWxsIGFzIGJhc2ljIG9wZXJhdGlvbnMgdG8gZGF0YSB0YWJsZXMuDQpXZSB3aWxsIGNvdmVyOg0KDQoxLiAgQmFzaWMgY29uY2VwdHMgb2YgdGFidWxhciBkYXRhIGFuZCB3aGF0IGlzIGEgdGlkeSBkYXRhc2V0Pw0KMi4gIEJhc2ljIG9wZXJhdGlvbnMgdG8gYSBkYXRhIHRhYmxlOiBgc2VsZWN0KClgLCBgZmlsdGVyKClgLCBgbXV0YXRlKClgLA0KICAgIGBncm91cF9ieSgpYCBhbmQgYHN1bW1hcmlzZSgpYC4NCjMuICBCYXNpYyBvcGVyYXRpb25zIHRvIG11bHRpcGxlIHRhYmxlczogYmluZGluZywgam9pbmluZw0KDQpBcyBhIHJlc291cmNlLCB5b3UgY2FuIGRvd25sb2FkIFt0aGUgZGF0YSB3cmFuZ2xpbmcgY2hlYXQgc2hlZXRdKGh0dHBzOi8vcnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDIvZGF0YS13cmFuZ2xpbmctY2hlYXRzaGVldC5wZGYpDQoNCiMgbG9hZCBwYWNrYWdlcw0KDQpZb3UgbWlnaHQgbmVlZCB0byBpbnN0YWxsIHNvbWUgb2YgdGhlc2UgcGFja2FnZXMuDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KHJlYWR4bCkNCmBgYA0KVGhlIGB0aWR5dmVyc2VgIGEgY29sbGVjdGlvbiBvZiBSIHBhY2thZ2VzIHRoYXQgc2hhcmUgdGhlIHNhbWUgc3ludGF4IGFuZCBjb2Rpbmcgc3R5bGUuIA0KVGhlc2UgcGFja2FnZXMgd29yayBpbiBoYXJtb255IGFuZCBwcm92aWRlIHNpbXBsZSBzb2x1dGlvbnMgdG8gZGF0YSBhbmFseXNpcyBwcm9ibGVtcy4gDQpUaGUgYHJlYWR4bGAgcGFja2FnZSBhbGxvd3MgdXMgdG8gcmVhZCBkaXJlY3RseSBmcm9tIGV4Y2VsIGZpbGVzLiAgDQpgYGB7cn0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KYGBgDQoNClRoZXNlIGFyZSB3aGF0IHdlIG5lZWQgZm9yIG5vdy4gV2Ugd2lsbCBpbnN0YWxsIG1vcmUgYXMgd2UgbW92ZSBpbnRvIG1vcmUgY29tcGxleCBhY3Rpdml0aWVzLg0KQWdhaW4gdGhlIGNvZGVzIGluIHRleHQgYXJlIGhpZ2hsaWdodGVkIGluIGBncmV5YC4gDQoNCiMgVGlkeSBkYXRhc2V0IG1ha2VzIHlvdXIgbGlmZSBlYXN5IQ0KDQpGaXJzdCBsZXQncyBzdGFydCB3aXRoIGFuIGV4YW1wbGUuDQoNCmBgYHtyfQ0KVVNEQV8yMDIyIDwtIHJlYWRfY3N2KGZpbGUgPSAiLi4vRGF0YS9VU0RBXzIwMjJfbWF0cml4LmNzdiIpDQoNCmhlYWQoVVNEQV8yMDIyKQ0KDQpgYGANCg0KYGBge3J9DQpVU0RBXzIwMjIgPC0gcmVhZF9jc3YoZmlsZSA9ICIuLi9EYXRhL1VTREFfMjAyMl9tYXRyaXguY3N2IikNCg0KaGVhZChVU0RBXzIwMjIpDQpgYGANCmBgYHtyfQ0KbGlicmFyeShkcGx5cikNCmBgYA0KDQoNCkhlcmUgYXJlIGRhdGEgcXVlcmllZCBmcm9tIFVTREEgW05hdGlvbmFsIEFncmljdWx0dXJlIFN0YXRpdGljcyBTZXJ2aWNlXShodHRwczovL3d3dy5uYXNzLnVzZGEuZ292L0RhdGFfYW5kX1N0YXRpc3RpY3MvKQ0KDQpJbiB0aGlzIHRhYmxlLCBlYWNoIHJvdyBpcyBhIGNyb3AsIGFuZCBlYWNoIGNvbHVtbiBpcyB0aGUgeWllbGQgYXQgYSBnaXZlbiB5ZWFyLiANCg0KIyMgRGF0YSBmcmFtZXMNCg0KVGhpcyB0YWJsZSBpcyBjYWxsZWQgYSBkYXRhIGZyYW1lIGluIHRoZSBqYXJnb24gb2YgUi4NCkEgZGF0YSBmcmFtZSBpcyBzaW1wbHkgYSBkYXRhIHRhYmxlLiBJdCBoYXMgcm93cyBhbmQgY29sdW1ucy4NCkFuIGltcG9ydGFudCBhc3BlY3Qgb2YgZGF0YSBmcmFtZSBpcyB0aGF0IHJvd3Mgb2YgdGhlIHNhbWUgY29sdW1uIG11c3QgYmUgdGhlIHNhbWUgY2xhc3Mgb2YgdmFyaWFibGUuDQpGb3IgZXhhbXBsZSwgaW4gY29sdW1uICJDcm9wIiwgYWxsIHZhbHVlcyBpbiBjb2x1bW4gIkNyb3AiIGFyZSBjaGFyYWN0ZXJzLg0KSW4gY29sdW1uICIyMDIyIiwgYWxsIHZhbHVlcyBpbiB0aGF0IGNvbHVtbiBhcmUgbnVtZXJpYy4gIA0KVGhpcyBpcyBqdXN0IG9uZSB0aGluZyB0byBrZWVwIGluIG1pbmQgd2hlbiB3b3JraW5nIHdpdGggZGF0YSBmcmFtZXMuDQoNCiMjIFRpZHkgZGF0YQ0KDQpOb3QgYWxsIGRhdGEgZnJhbWVzIGFyZSB0aWR5LiBUaGUgaWRlYSBvZiB0aWR5IGRhdGEgbWVhbnMNCg0KMS4gIEVhY2ggcm93IGhhcyB0byBiZSBhbiBvYnNlcnZhdGlvbiANCjIuICBFYWNoIGNvbHVtbiBoYXMgYmUgYSB2YXJpYWJsZQ0KDQpHZXR0aW5nIHlvdXIgZGF0YSBpbnRvIHRoZSB0aWR5IGZvcm1hdCB3aWxsIHNhdmUgeW91ICpBIExPVCBPRiBUSU1FKiBpbiBhbGwgeW91ciBkb3duc3RyZWFtIGFuYWx5c2VzLg0KQSBnb29kIHByYWN0aWNlIGlzIHRvIHRpZHkgeW91ciBkYXRhIEZJUlNUIGJlZm9yZSB5b3UgZG8gYW55IGFuYWx5c2VzLg0KDQpMZXQncyBsb29rIGF0IHRoaXMgdGFibGUgYWdhaW46DQoNCmBgYHtyfQ0KaGVhZChVU0RBXzIwMjIpDQpgYGANCg0KSXMgdGhpcyB0YWJsZSBhIHRpZHkgZGF0YSBmcmFtZT8NCg0KTm8uDQpCZWNhdXNlIHRoZXJlIGFyZSB1cCB0byA3OCB5ZWFycyBvZiBkYXRhIGZvciBlYWNoIGNyb3AsIGJ1dCB0aGUgb2JzZXJ2YXRpb25zIGFyZSBzcHJlYWQgb3V0IGluIDc4IGNvbHVtbnMuDQpPYnNlcnZhdGlvbnMgc3ByZWFkaW5nIG91dCBpbiBjb2x1bW5zIGlzIHNvbWV0aW1lcyBjYWxsZWQgdGhlIHdpZGUgZm9ybWF0Lg0KVGhlIHdpZGUgZm9ybWF0IGlzIGVhc3kgZm9yIGRhdGEgY29sbGVjdGlvbiwgYnV0IHVuZm9ydHVuYXRlbHkgaXQncyBub3QgaG93IFIgd291bGQgbGlrZSB0byByZWFkIGRhdGEuDQpSIGxpa2VzIHRpZHkgZGF0YSwgYW5kIHRpZHkgZGF0YSBmcmFtZXMgbmVlZCBlYWNoIG9ic2VydmF0aW9uIHRvIGJlIGluIGl0cyBpbmRpdmlkdWFsIHJvdy4NCg0KVG8gY29udmVydCBhIHdpZGUgdGFibGUgaW50byBhIHRpZHkgdGFibGUsIHRoZSBjb21tYW5kIGlzIGBwaXZvdF9sb25nZXIoKWAgVGhlIHN5bnRheCBpcyBgcGl2b3RfbG9uZ2VyKGNvbHVtbnMsIG5hbWVzX3RvID0gIm5hbWUiLCB2YWx1ZXNfdG8gPSAidmFsdWUiKWANCg0KYGBge3J9DQpVU0RBX3RpZHkgPC0gVVNEQV8yMDIyICU+JSAjdGhlICU+JSAoInBpcGUiKSBvcGVyYXRvciBtZWFucyB0YWtpbmcgdGhlIG91dHB1dCBvZiBwcmV2aW91cyByb3cgYW5kIHVzZSBpdCBhcyBpbnB1dCBvZiBuZXh0IHJvdyANCiAgcGl2b3RfbG9uZ2VyKG5hbWVzX3RvID0gIlllYXIiLCB2YWx1ZXNfdG8gPSAiWWllbGQiLCBjb2xzID0gIUNyb3ApIA0KDQojIGNvbHMgPSAhQ3JvcCBzcGVjaWZpZXMgYWxsIGNvbHVtbnMgb3RoZXIgdGhhbiBDcm9wLiANCg0KaGVhZChVU0RBX3RpZHkpDQpgYGANCg0KDQpXaGF0IHRoaXMgY29kZSBjaHVuayBkaWQgaXMgdGhhdCBpdCBtYWRlIHR3byBuZXcgY29sdW1uczoNCg0KMS4gIFRoZSAibmFtZSIgY29sdW1uIChzb21ldGltZXMgY2FsbGVkIHRoZSBsYWJlbCBjb2x1bW4pIGlzICJZZWFyIjoNCiAgICBpdCBob3VzZXMgd2hpY2ggb3JpZ2luYWwgY29sdW1ucyB0aGUgZGF0YSBjYW1lIGZyb20gKGFsbCB0aGUgeWVhcnMpLg0KDQoyLiAgVGhlICJ2YWx1ZSIgY29sdW1uIGlzICJZaWVsZCI6IGl0IGlzIHRoZSB2YXJpYWJsZSB0aGF0IHdhcyBhY3R1YWxseSBtZWFzdXJlZC4NCg0KMy4gIFRoZSBjb2x1bW5zIHRvIGNvbGxlY3QgZGF0YSBmcm9tIHdlcmUgYWxsIHRoZSB5ZWFycywgaS5lLiwgbm90IHRoZSAiQ3JvcCIgY29sdW1uLiANCg0KTm93IHRoaXMgaXMgYSB0aWR5IGRhdGEgZnJhbWUuIEVhY2ggcm93IGlzIGFuIG9ic2VydmF0aW9uLiBFYWNoIGNvbHVtbiBpcyBhIHZhcmlhYmxlLg0KV2UgaGF2ZSAzIHZhcmlhYmxlczoNCg0KKiAgIENyb3AsDQoqICAgWWVhciAobWFueSB5ZWFycyksDQoqICAgWWllbGQgKG51bWVyaWMpLg0KDQpIb3dldmVyLCB0aGVyZSBpcyBqdXN0IG9uZSBwcm9ibGVtLiBXZSBzaG91bGQgYWx3YXlzIGNoZWNrIGVhY2ggdmFyaWFibGVzIGFyZSByZWNvcmRlZCBhcyB0aGUgY29ycmVjdCB0eXBlIG9mIGRhdGEgKGUuZy4sIGNoYXJhY3RlciB2cyBudW1lcmljKS4NCkEgcXVpY2sgd2F5IGlzIGNhbGxpbmcgYHN0cigpYCAoc3RhbmRzIGZvciAic3RydWN0dXJlIikNCmBgYHtyfQ0Kc3RyKFVTREFfdGlkeSkNCmBgYA0KWW91IHdpbGwgcmVhbGl6ZSBpbiB0aGUgb3V0cHV0IGl0IHNheXMgIiQgWWVhciA6IGNociBbMTo2MjRdIi4gDQoiY2hyIiBzdGFuZHMgZm9yIGNoYXJhY3Rlci4gV2UgbmVlZCBpdCB0byBiZSBudW1lcmljLiANCldlIHdpbGwgdGFsayBhYm91dCBob3cgdG8gY2hhbmdlIHRoYXQgaW4gYSBzZWNvbmQuIA0KDQoNCkxldCdzIGxvb2sgYXQgYW5vdGhlciBleGFtcGxlLiBUaGlzIHRpbWUgaXQncyBhIGh5cG90aGV0aWNhbCBleGFtcGxlIHRoYXQgSSBtYWRlIHVwLg0KDQpgYGB7cn0NCiNpZ25vcmUgdGhpcyBjaHVuay4gSXQgc2ltdWxhdGVzIHRoZSBkYXRhLg0KZXhhbXBsZV9kYXRhIDwtIGRhdGEuZnJhbWUoDQogICJzYW1wbGUxIiA9IGMoMSwgMiwgMyksDQogICJzYW1wbGUyIiA9IGMoMiwgMiwgMiksDQogICJzYW1wbGUzIiA9IGMoMywgMiwgMSkNCikgJT4lIA0KICBtdXRhdGUoZ2VuZSA9IGMoImdlbmUxIiwgImdlbmUyIiwgImdlbmUzIikpDQoNCmhlYWQoZXhhbXBsZV9kYXRhKQ0KYGBgDQoNCkluIHRoaXMgZXhwZXJpbWVudCwgDQp5b3UgYXJlIGxvb2tpbmcgYXQgdGhlIGV4cHJlc3Npb24gb2YgdGhyZWUgZ2VuZXMgKGdlbmUxIHRvIGdlbmUzKSBpbiB0aHJlZSBzYW1wbGVzIChzYW1wbGUxIHRvIHNhbXBsZTMpLg0KU28geW91IGhhdmUgMyAqIDMgPSA5IG9ic2VydmF0aW9ucy4NClRoaXMgaXMgbm90IGEgdGlkeSBkYXRhIGZyYW1lIGJlY2F1c2UgdGhlIDkgbWVhc3VyZW1lbnRzIG9mIGV4cHJlc3Npb24gYXJlIHNwcmVhZCBvdXQgaW4gMyBjb2x1bW5zLg0KV2UgY2FuIHRpZHkgaXQgdXNpbmcgdGhlIGBwaXZvdF9sb25nZXJgIGNvbW1hbmQ6DQoNCmBgYHtyfQ0KZXhhbXBsZV9kYXRhX3RpZHkgPC0gZXhhbXBsZV9kYXRhICU+JSANCiAgcGl2b3RfbG9uZ2VyKG5hbWVzX3RvID0gInNhbXBsZSIsIHZhbHVlc190byA9ICJleHByZXNzaW9uIiwgY29scyA9IGMoc2FtcGxlMSwgc2FtcGxlMiwgc2FtcGxlMykpIA0KDQpleGFtcGxlX2RhdGFfdGlkeQ0KYGBgDQoNCjEuICBUaGUgIm5hbWUiIGNvbHVtbiBub3cgaXMgInNhbXBsZSI6IGl0IGhvdXNlcyB3aGljaCBvcmlnaW5hbCBjb2x1bW5zIHRoZSBkYXRhIGNhbWUgZnJvbSAoc2FtcGxlMSB0byBzYW1wbGUzKS4NCjIuICBUaGUgInZhbHVlIiBjb2x1bW4gaXMgImV4cHJlc3Npb24iOiBpdCBpcyB0aGUgdmFyaWFibGUgdGhhdCB3YXMgYWN0dWFsbHkgbWVhc3VyZWQuDQozLiAgVGhlIGNvbHVtbnMgdG8gY29sbGVjdCBkYXRhIGZyb20gd2VyZSBzYW1wbGUxIHRvIHNhbXBsZTMuIEFsdGVybmF0aXZlbHksIGBjb2xzID0gIWdlbmVgIChhbGwgY29sdW1ucyBidXQgZ2VuZSkgc2hvdWxkIGFsc28gd29yay4gDQoNCk5vdyB0aGlzIGlzIGEgdGlkeSBkYXRhIGZyYW1lLiBFYWNoIHJvdyBpcyBhbiBvYnNlcnZhdGlvbi4gRWFjaCBjb2x1bW4gaXMgYSB2YXJpYWJsZS4NCldlIGhhdmUgMyB2YXJpYWJsZXM6IHNhbXBsZSAoMyBzYW1wbGVzKSwgZ2VuZXMgKGdlbmUxIHRvIGdlbmUzKSwgYW5kIGV4cHJlc3Npb24gKG51bWVyaWMpLg0KDQojIEJhc2ljIG9wZXJhdGlvbnMgdG8gYSBkYXRhIGZyYW1lLg0KDQpOb3cgbGV0J3MgdGFsayBhYm91dCBzb21lIG9mIHRoZSBiYXNpYyBvcGVyYXRpb25zIGZvciBkYXRhIGZyYW1lcywgd2hpY2ggaW5jbHVkZXM6DQoNCiogICBIb3cgdG8gc3Vic2V0IGEgZGF0YSBmcmFtZSwNCiogICBIb3cgdG8gY3JlYXRlIG5ldyBjb2x1bW5zIGluIGEgZGF0YSBmcmFtZSBhbmQgc3VtbWFyaXNlIGEgZGF0YSBmcmFtZS4NCg0KVGhlc2UgYXJlIHZlcnkgdXNlZnVsIGFuZCB5b3Ugd2lsbCBwcm9iYWJseSB1c2UgdGhlc2UgZnVuY3Rpb25zIGFsbCB0aGUgdGltZS4gDQoNCiMjIFN1YnNldHRpbmcNCg0KVGhlcmUgYXJlIGFjdHVhbGx5IHR3byB3YXlzIHRvIHN1YnNldCBhIGRhdGEgZnJhbWU6IHN1YnNldHRpbmcgYnkgY29sdW1ucyBhbmQgc3Vic2V0dGluZyBieSByb3dzLg0KTGV0J3MgZG8gc3Vic2V0dGluZyBieSBjb2x1bW5zIGZpcnN0Lg0KDQpVc2luZyBvdXIgdGlkeSBVU0RBIGRhdGEgbm93LA0Kc2F5IHdlIG9ubHkgd2FudCB0aGUgY29sdW1ucyBjb3VudCBhbmQgdHJlYXRtZW50IGFuZCBsZWF2ZSBvdXQgdGhlIHJlc3Qgb2YgdGhlIGNvbHVtbnMuDQpUaGUgY29tbWFuZCBpcyBzaW1wbGUsIGp1c3QgYHNlbGVjdCgpYCB0aGUgY29sdW1ucyB5b3Ugd2FudC4NCg0KYGBge3J9DQpoZWFkKFVTREFfdGlkeSkNCg0KVVNEQV90aWR5XzIgPC0gVVNEQV90aWR5ICU+JSANCiAgc2VsZWN0KENyb3AsIFlpZWxkKSAjanVzdCBzZWxlY3RpbmcgQ3JvcCBhbmQgWWllbGQgDQoNCmhlYWQoVVNEQV90aWR5XzIpDQpgYGANCg0KTm93IHdlIGp1c3QgZ2V0IHR3byBjb2x1bW5zLg0KDQpUaGUgbW9yZSB1c2VmdWwgc3Vic2V0dGluZyBmdW5jdGlvbiBpcyBzdWJzZXR0aW5nIGJ5IHJvd3MgYmFzZWQgb24gdGhlIHZhbHVlcyBvZiBvbmUgb3IgbW9yZSBjb2x1bW5zLg0KVGhlIGNvbW1hbmQgaXMgYGZpbHRlcigpYC4gDQpJbiB0aGUgVVNEQSBkYXRhLCBsZXQncyBzYXkgd2Ugb25seSB3YW50IHllYXJzIHRoYXQgaGF2ZSBkYXRhIGJlY2F1c2UgZGF0YSB3ZXJlIG5vdCBjb2xsZWN0ZWQgZm9yIHNvbWUgeWVhcnMgZm9yIHNvbWUgY3JvcHMuICANCg0KYGBge3J9DQpVU0RBX3RpZHlfMyA8LSBVU0RBX3RpZHkgJT4lIA0KICBmaWx0ZXIoaXMubmEoWWllbGQpID09IEZBTFNFKSAjIGZpbHRlciBmb3Igcm93cyB0aGF0IHRoZSBZaWVsZCB2YWx1ZSBpcyBub3QgTkEgIA0KDQpucm93KFVTREFfdGlkeSkNCm5yb3coVVNEQV90aWR5XzMpDQpgYGANCg0KTm93IHRoZSBkYXRhIGZyYW1lIHdlbnQgZnJvbSA2MjQgcm93cyB0byA1Nzggcm93cy4NClNvIHRoZXJlIHdlcmUgYWN0dWFsbHkgYSB0b3RhbCBvZiA1Nzggb2YgZGF0YS4NCk5vdGU6IGluIGBmaWx0ZXIoKWAsIHlvdSBzaG91bGQgdXNlIGA9PWAgdG8gbWVhbiAiZXF1YWwiLg0KDQpMZXQncyBzYXkgd2Ugb25seSB3YW50IGNvcm4uIA0KDQpgYGB7cn0NClVTREFfdGlkeV9jb3JuIDwtIFVTREFfdGlkeV8zICU+JSANCiAgZmlsdGVyKENyb3AgPT0gImNvcm4iKSANCg0KbnJvdyhVU0RBX3RpZHkpDQpucm93KFVTREFfdGlkeV9jb3JuKQ0KYGBgDQoNCkxvb2tzIGxpa2UgdGhlcmUgYXJlIDc4IHllYXJzIG9mIGRhdGEgZm9yIGNvcm4uIA0KDQpMZXQncyBzYXkgeW91IHdhbnQgdG8gZmlsdGVyIGZvciBjb3JuIGFuZCB3aGVhdDogDQoNCmBgYHtyfQ0KVVNEQV90aWR5X2Nvcm5fd2hlYXQgPC0gVVNEQV90aWR5XzMgJT4lIA0KICBmaWx0ZXIoQ3JvcCA9PSAiY29ybiIgfA0KICAgICAgICAgICBDcm9wID09ICJ3aGVhdCIpIA0KDQpucm93KFVTREFfdGlkeSkNCm5yb3coVVNEQV90aWR5X2Nvcm5fd2hlYXQpIA0KYGBgDQoNClRoZSB2ZXJ0aWNhbCBiYXIgYHxgIG1lYW5zICJvciIgaW4gUi4gSW4gdGhpcyBjaHVuayB5b3UgYXJlIGZpbHRlcmluZyBmb3IgZWl0aGVyIGNvcm4gb3Igd2hlYXQuDQpZb3Ugc2hvdWxkbid0IHVzZSBgJmAgKCJhbmQiKSBoZXJlLCBiZWNhdXNlIHRoZXJlIGlzIG5vIGNyb3AgdGhhdCBpcyBib3RoIGNvcm4gYW5kIHdoZWF0Lg0KDQojIyBNYWtpbmcgbmV3IGNvbHVtbnMNCg0KT2Z0ZW50aW1lcyB5b3Ugd2lsbCBuZWVkIHRvIG1ha2UgbmV3IHZhcmlhYmxlcyBiYXNlZCBvbiBleGlzdGluZyBvbmVzLg0KVGhlIGNvbW1hbmQgaXMgYG11dGF0ZSgpYC4NCkFzIGEgYmlvbG9naXN0LCBJIGRvbid0IHRoaW5rICJtdXRhdGUiIHNvdW5kIHRvbyBpbnR1aXRpdmUsIGJ1dCBpdCBpcyB3aGF0IGl0IGlzLg0KDQpXZSByZWFsaXplZCBlYXJsaWVyIHRoZSAiWWVhciIgY29sdW1uIGlzIHNwZWNpZmllZCBhcyBhIGNoYXJhY3RlciB2YXJpYWJsZS4gDQpXZSBuZWVkIHRvIGNvbnZlcnQgdGhhdCB0byBhIG51bWVyaWMgdmFyaWFibGUuIA0KDQpgYGB7cn0NClVTREFfdGlkeV8zIDwtIFVTREFfdGlkeV8zICU+JSANCiAgbXV0YXRlKFllYXIgPSBhcy5udW1lcmljKFllYXIpKSANCg0KaGVhZChVU0RBX3RpZHlfMykNCmBgYA0KDQpOb3cgeW91IGdldCBhIG5ldyBjb2x1bW4gWWVhciB0aGF0IGlzIHJlY29yZGVkIGFzIG51bWVyaWMuICANCkFzIHlvdSBjYW4gaW1hZ2luZSwgeW91IGNhbiBkbyBhbnkgbWF0aGVtYXRpY2FsIG9wZXJhdGlvbnMgdG8gYQ0KbnVtZXJpY2FsIGNvbHVtbiB3aXRoaW4gYG11dGF0ZSgpYC4NCg0KTGV0J3MgZG8gYW5vdGhlciBleGFtcGxlLiANClRoZSB5aWVsZCBmb3IgZXZlcnkgY3JvcCBvdGhlciB0aGFuIGNvdHRvbiwgY2Fub2xhLCBhbmQgcmljZSBhcmUgcmVjb3JkZWQgaW4gYnVzaGVsL2FjcmUuIENvdHRvbiwgY2Fub2xhLCBhbmQgcmljZSB3ZXJlIHJlY29yZGVkIGluIGxicy4gIA0KVGhlIG51bWJlciBvZiBwb3VuZHMgaW4gYSBidXNoZWwgdmFyeSBieSBncmFpbi4gIA0KTGV0J3Mgc2F5IHRoZXJlIGFyZSA2MCBsYnMgaW4gMSBidXNoZWwgb2Ygd2hlYXQgYnV0IG9ubHkgNTYgbGJzIGZvciBjb3JuLg0KNDggbGJzL2J1IGZvciBiYXJsZXksIDYwIGxicy9idSBmb3Igc295YmVhbiwgYW5kIDU2IGxicyBmb3Igc29yZ2h1bS4gDQpMZXQncyBjb252ZXJ0IGFsbCB0aGUgeWllbGQgdW5pdHMgdG8gbGJzL2FjcmUuICANCg0KYGBge3J9DQpVU0RBX3RpZHlfbGJzIDwtIFVTREFfdGlkeV8zICU+JSANCiAgbXV0YXRlKFlpZWxkc19sYnMgPSBjYXNlX3doZW4oDQogICAgQ3JvcCA9PSAiYmFybGV5IiB+IFlpZWxkICogNDgsDQogICAgQ3JvcCA9PSAiY29ybiIgfiBZaWVsZCAqIDU2LCANCiAgICBDcm9wID09ICJzb3liZWFuIiB+IFlpZWxkICogNjAsDQogICAgQ3JvcCA9PSAic29yZ2h1bSIgfiBZaWVsZCAqIDU2LCANCiAgICBDcm9wID09ICJ3aGVhdCIgfiBZaWVsZCAqIDYwLA0KICAgIFQgfiBZaWVsZA0KICApKQ0KDQpoZWFkKFVTREFfdGlkeV9sYnMpDQpgYGANCg0KYGNhc2Vfd2hlbigpYCBpcyBhbiBleHRyZW1lbHkgdXNlZnVsIGNvbW1hbmQgd2l0aGluIGBtdXRhdGUoKWAuDQpUaGUgc3ludGF4IGlzIGBjYXNlX3doZW4oY29uZGl0aW9uIH4gb3V0Y29tZSlgLg0KRm9yIGV4YW1wbGUsIHdoZW4gQ3JvcCBpcyBiYXJsZXksIGl0IGFwcGxpZXMgYSB2YWx1ZSBvZiA0OCBsYnMvYnUuIA0KVGhlIGBUIH4gLi4uYCBpbnNpZGUgYGNhc2Vfd2hlbigpYCBzdGFuZHMgZm9yICJmb3IgdGhlIHJlc3QgdGhhdCBhcmUgbm90IHRoZSBhYm92ZSIuIA0KDQojIyBTdW1tYXJpc2UNCg0KQmVmb3JlIHlvdSBkbyByZWFsIHN0YXRpc3RpY2FsIGluZmVyZW5jZSwgbWF5YmUgeW91IHdhbnQgdG8ganVzdCB0YWtlIGEgbG9vayBhdCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzLiANClRoaXMgY2FuIGJlIGRvbmUgZWFzaWx5IHVzaW5nIHRoZSBgZ3JvdXBfYnkoKWAgYW5kIGBzdW1tYXJpc2UoKWAgY29tbWFuZHMuDQoNCkxldCdzIHNpbXVsYXRlIGFuIGV4cGVyaW1lbnQuIEluIHRoaXMgZXhwZXJpbWVudCwgbGV0J3Mgc2F5IHRoZXJlIGFyZSB0d28gZ3JvdXBzOiBvbmUgZXhwZXJpbWVudGFsIHRyZWF0bWVudCBhbmQgYSBjb250cm9sLiANClRoZSBleHBlcmltZW50IHdhcyBjb25kdWN0ZWQgaW4gdHdvIGxvY2F0aW9uczogQSBhbmQgQi4gDQpBbmQgbGV0J3Mgc2F5IHdlIG1lYXN1cmVkIHNvbWUga2luZCBvZiByZXNwb25zZS4gDQpTYXkgSSB3b3VsZCBsaWtlIHRoZSBhdmVyYWdlLCBzdGFuZGFyZCBkZXZpYXRpb24gYW5kIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgZm9yIGVhY2ggZ3JvdXAuIA0KDQpgYGB7cn0NCiMgWW91IGNhbiBpZ25vcmUgdGhpcyBjaHVuay4gDQp0cnQgPC0gcm5vcm0obiA9IDEwLCBtZWFuID0gMTAsIHNkID0gMSkNCmN0cmwgPC0gcm5vcm0obiA9IDEwLCBtZWFuID0gNiwgc2QgPSAxKQ0KbG9jYXRpb24gPC0gYyhyZXAoIkEiLCA1KSwgcmVwKCJCIiwgNSkpDQoNCmV4YW1wbGVfZXhwZXJpbWVudCA8LSBkYXRhLmZyYW1lKA0KICBjdHJsLA0KICB0cnQsDQogIGxvY2F0aW9uDQopICU+JSANCiAgcGl2b3RfbG9uZ2VyKG5hbWVzX3RvID0gImdyb3VwIiwgdmFsdWVzX3RvID0gInJlc3BvbnNlIiwgY29scyA9IDE6MikNCg0KaGVhZChleGFtcGxlX2V4cGVyaW1lbnQpDQpgYGANCg0KYGBge3J9DQpleGFtcGxlX2V4cGVyaW1lbnQgJT4lIA0KICBncm91cF9ieShncm91cCkgJT4lIA0KICBzdW1tYXJpc2UoDQogICAgbWVhbiA9IG1lYW4ocmVzcG9uc2UpLA0KICAgIHNkID0gc2QocmVzcG9uc2UpLA0KICAgIG4gPSBuKCkNCiAgKSAlPiUgDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCg0KMS4gIGBncm91cF9ieSgpYCBzZWxlY3RzIHdoaWNoIGNvbHVtbnMgeW91IHdhbnQgdG8gc3VtbWFyaXNlIGZyb20uDQogICAgSW4gdGhpcyBjYXNlIHlvdSBqdXN0IHdhbnQgZWFjaCBncm91cCB0byBiZSBhIGdyb3VwLg0KMi4gIGBzdW1tYXJpc2UoKWAgYWxsb3dzIHlvdSB0byBjYWxsIHRoZSBzdGF0aXN0aWNhbCBmdW5jdGlvbnMsIHN1Y2ggYXMgbWVhbiwgc2QsIGFuZCBuLg0KMy4gIEl0J3MgYSBnb29kIHByYWN0aWNlIHRvIGB1bmdyb3VwKClgIGFmdGVyIHlvdSBhcmUgZG9uZS4NCiAgICBNb3N0IG9mIHRoZSB0aW1lIGl0IGRvZXNuJ3QgbWF0dGVyLCBidXQgaXQgd2lsbCBjcmVhdGUgcHJvYmxlbXMgaWYgeW91IGRvbid0IGFuZCB5b3UgdHJ5IHRvIHN3aXRjaC9tb2RpZnkgZ3JvdXBpbmcgbGF0ZXIuDQoNCk5vdyB5b3UganVzdCBnZXQgMiByb3dzIGFuZCA0IGNvbHVtbnMsIGVhY2ggcm93IGlzIGEgZ3JvdXAuIA0KU2F5IEkgd291bGQgbGlrZSB0byBhdmVyYWdlIHRoZSBjb3VudHMgZm9yIGVhY2ggZ3JvdXAgYW5kIGZvciBlYWNoIGxvY2F0aW9uLiBDYW4gSSBkbyB0aGF0Pw0KDQpgYGB7cn0NCmV4YW1wbGVfZXhwZXJpbWVudCAlPiUgDQogIGdyb3VwX2J5KGdyb3VwLCBsb2NhdGlvbikgJT4lIA0KICBzdW1tYXJpc2UoDQogICAgbWVhbiA9IG1lYW4ocmVzcG9uc2UpLA0KICAgIHNkID0gc2QocmVzcG9uc2UpLA0KICAgIG4gPSBuKCkNCiAgKSAlPiUgDQogIHVuZ3JvdXAoKQ0KYGBgDQoNCjEuICBUaGlzIHRpbWUgeW91IG5lZWQgdG8gc2VsZWN0IGdyb3VwIGFuZCB0cmVhdG1lbnQgaW4gYGdyb3VwX2J5KClgLg0KICAgIEJlY2F1c2UgeW91IHdhbnQgdGhlIG1lYW4gZm9yIGVhY2ggZ3JvdXAgYW5kIGVhY2ggbG9jYXRpb24uDQoyLiAgSW4gYHN1bW1hcmlzZSgpYCB5b3UgY2FsbCB0aGUgbWVhbiBmdW5jdGlvbi4NCjMuICBJdCdzIGEgZ29vZCBwcmFjdGljZSB0byBgdW5ncm91cCgpYCBhZnRlciB5b3UgYXJlIGRvbmUuDQoNCk5vdyBlYWNoIGdyb3VwIGJ5IHRyZWF0bWVudCBjb21iaW5hdGlvbiBpcyBhIHJvdy4NCg0KIyBCYXNpYyBvcGVyYXRpb25zIHRvIG11bHRpcGxlIGRhdGEgZnJhbWVzLg0KDQpJbiBkYXRhIGFuYWx5c2VzLCB0aGUgZGF0YSBtaWdodCBub3QgYmUgYWxsIGluIG9uZSBmaWxlLg0KSXQgaXMgY29tbW9uIHRoYXQgdGhlIGRhdGEgb2YgaW50ZXJlc3Qgd2VyZSByZWNvcmRlZCBpbiBtdWx0aXBsZSBzcHJlYWRzaGVldHMuDQpUaGlzIGlzIGV4dHJlbWVseSBjb21tb24gd2hlbiB5b3UgYXJlIHVzaW5nIGRhdGEgZnJvbSBtdWx0aXBsZSBwdWJsaWNhdGlvbnMuDQpFYWNoIHB1YmxpY2F0aW9uIG1heSBvbmx5IGhhdmUgb25lIGFzcGVjdCBvZiB0aGUgZGF0YSB5b3UgYXJlIGludGVyZXN0ZWQgaW4uDQpBZnRlciB5b3UgZG93bmxvYWQgdGhlaXIgdGFibGVzLCBpdCdzIG9uIHlvdSB0byBpbnRlZ3JhdGUgdGhlbSBpbnRvIG9uZSB3b3JrYWJsZSBkYXRhIGZyYW1lIGluIFIuDQoNCiMjIEJpbmRpbmcNCg0KV2hlbiB5b3UgbmVlZCB0byBhZGQgbW9yZSByb3dzIChvYnNlcnZhdGlvbnMpIGludG8gYSBkYXRhIGZyYW1lLCB5b3UNCm5lZWQgdG8gYmluZCB0aGVtIGFzIHJvd3MuDQpMZXQncyB1c2Ugb3VyIGh5cG90aGV0aWNhbCBleGFtcGxlIGFnYWluDQoNCmBgYHtyfQ0KZXhhbXBsZV9kYXRhX3RpZHkNCmBgYA0KDQpMZXQncyBzYXkgeW91IGFyZSBhbHNvIGludGVyZXN0ZWQgaW4gZ2VuZTQgYW5kIGdlbmU1Lg0KQW5kIHNheSB0aGUgZXhwcmVzc2lvbiBvZiBnZW5lNCBhbmQgZ2VuZTUgd2VyZSBzdHVkaWVkIGluIGFub3RoZXIgZXhwZXJpbWVudC4NCg0KYGBge3J9DQojIGlnbm9yZSB0aGlzIGNodW5rLiBJdCBzaW11bGF0ZXMgdGhlIGRhdGEgDQpleGFtcGxlX2RhdGFfdGlkeV8yIDwtIGRhdGEuZnJhbWUoDQogICJzYW1wbGUxIiA9IGMoNCwgNSksDQogICJzYW1wbGUyIiA9IGMoMiwgMiksDQogICJzYW1wbGUzIiA9IGMoNSwgNCkNCikgJT4lIA0KICBtdXRhdGUoZ2VuZSA9IGMoImdlbmU0IiwgImdlbmU1IikpICU+JQ0KICBnYXRoZXIoInNhbXBsZSIsICJleHByZXNzaW9uIiwgYyhzYW1wbGUxLCBzYW1wbGUyLCBzYW1wbGUzKSkNCg0KZXhhbXBsZV9kYXRhX3RpZHlfMg0KYGBgDQoNCldoYXQgeW91J2xsIGRvIGlzIHNpbXBseSBiaW5kIHRoZW0gYXMgcm93cyB1c2luZyB0aGUgYHJiaW5kKClgIGNvbW1hbmQuDQoicmJpbmQiIHN0YW5kcyBmb3IgInJvdyBiaW5kIi4NCg0KYGBge3J9DQpleGFtcGxlX2RhdGFfdGlkeV8zIDwtIHJiaW5kKA0KICBleGFtcGxlX2RhdGFfdGlkeSwNCiAgZXhhbXBsZV9kYXRhX3RpZHlfMg0KKQ0KDQpleGFtcGxlX2RhdGFfdGlkeV8zDQpgYGANCg0KTm93IHRoZXkgYXJlIGluIG9uZSB0YWJsZS4gV2hlbiB5b3UgZG8gYHJiaW5kKClgLCBqdXN0IG1ha2Ugc3VyZSB0aGV5IGhhdmUgdGhlIHNhbWUgY29sdW1ucy4NCkJ1dCBvdGhlciB0aGFuIHRoYXQsIGl0cyBwcmV0dHkgc2ltcGxlLg0KDQojIyBKb2luaW5nDQoNCk1vcmUgb2Z0ZW4geW91J2xsIG5lZWQgdG8gYWRkIGRpZmZlcmVudCBjb2x1bW5zIGZyb20gYW5vdGhlciBkYXRhIGZyYW1lLg0KVGhlIGphcmdvbiBmb3IgdGhhdCBpcyBqb2luaW5nLiANClRoaXMgc291bmRzIGFic3RyYWN0LCBzbyBsZXQncyBsb29rIGF0IGFuIGV4YW1wbGUuDQoNCmBgYHtyfQ0KY2hpbGRfbW9ydGFsaXR5IDwtIHJlYWRfY3N2KCIuLi9EYXRhL2NoaWxkX21vcnRhbGl0eV8wXzVfeWVhcl9vbGRzX2R5aW5nX3Blcl8xMDAwX2Jvcm4uY3N2IiwgY29sX3R5cGVzID0gY29scygpKSANCmJhYmllc19wZXJfd29tYW4gPC0gcmVhZF9jc3YoIi4uL0RhdGEvY2hpbGRyZW5fcGVyX3dvbWFuX3RvdGFsX2ZlcnRpbGl0eS5jc3YiLCBjb2xfdHlwZXMgPSBjb2xzKCkpIA0KYGBgDQoNClRoZXNlIGFyZSB0d28gZGF0YXNldHMgZG93bmxvYWRlZCBmcm9tIHRoZSBbR2FwbWluZGVyIGZvdW5kYXRpb25dKGh0dHBzOi8vd3d3LmdhcG1pbmRlci5vcmcvZGF0YS8pLg0KVGhlIEdhcG1pbmRlciBmb3VuZGF0aW9uIGhhcyBkYXRhc2V0cyBvbiBsaWZlIGV4cGVjdGFuY3ksIGVjb25vbXksIGVkdWNhdGlvbiwgYW5kIHBvcHVsYXRpb24gYWNyb3NzIGNvdW50cmllcyBhbmQgeWVhcnMuDQpUaGUgZ29hbCBpcyB0byByZW1pbmQgdXMgbm90IG9ubHkgdGhlICJnYXBzIiBiZXR3ZWVuIGRldmVsb3BlZCBhbmQgZGV2ZWxvcGluZyB3b3JsZHMsIGJ1dCBhbHNvIHRoZSBhbWF6aW5nIGNvbnRpbnVvdXMgaW1wcm92ZW1lbnRzIG9mIHF1YWxpdHkgb2YgbGlmZSB0aHJvdWdoIHRpbWUuDQoNCjEuICBDaGlsZCBtb3J0YWxpdHkgKDAgLSA1IHllYXIgb2xkKSBkeWluZyBwZXIgMTAwMCBib3JuLg0KMi4gIEJpcnRocyBwZXIgd29tYW4uDQoNClRoZXNlIHdlcmUgcmVjb3JkZWQgZnJvbSB5ZWFyIDE4MDAgYW5kIHByb2plY3RlZCBhbGwgdGhlIHdheSB0byAyMTAwLg0KDQpMZXQncyBsb29rIGF0IHRoZW0uDQoNCmBgYHtyfQ0KaGVhZChjaGlsZF9tb3J0YWxpdHkpDQpoZWFkKGJhYmllc19wZXJfd29tYW4pDQpgYGANCg0KSW4gZWFjaCB0YWJsZSwgeW91IGhhdmUgYSBjb2x1bW4gZm9yIGNvdW50cnkuDQpUaGVuIGVhY2ggeWVhciBpcyBhIGNvbHVtbiwgaW4gd2hpY2ggdGhlIG51bWJlcnMgd2VyZSByZWNvcmRlZCBmb3IgdGhlIHJlc3BlY3RpdmUgdGFibGVzLg0KDQpTYXkgeW91IHdhbnQgdG8ga25vdyBpZiBiaXJ0aHMgcGVyIHdvbWFuIGlzIGNvcnJlbGF0ZWQgd2l0aCBjaGlsZCBtb3J0YWxpdHkuDQpZb3UgbWF5IGh5cG90aGVzaXplIHRoYXQgaW4gY291bnRyaWVzIHdpdGggaGlnaCBjaGlsZCBtb3J0YWxpdHksIHdvbWVuIGdpdmUgbW9yZSBiaXJ0aC4NCg0KVG8gdGVzdCB0aGlzIGh5cG90aGVzaXMsIHlvdSBuZWVkIHRvIGZpcnN0IGdldCB0aGUgdHdvIGRhdGFzZXRzIGluIG9uZSBkYXRhIGZyYW1lLg0KVGhlcmUgYXJlIGEgZmV3IGNvbW1hbmRzIGZvciBqb2luaW5nIGluIFI6DQoNCjEuICBgbGVmdF9qb2luKFgsIFkpYCBrZWVwcyBhbGwgdGhlIHJvd3MgaW4gWC4NCjIuICBgcmlnaHRfam9pbihYLCBZKWAga2VlcHMgYWxsIHRoZSByb3dzIGluIFkuDQozLiAgYGlubmVyX2pvaW4oWCwgWSlgIG9ubHkga2VlcHMgcm93cyB0aGF0IGFyZSBpbiBjb21tb24uDQo0LiAgYGZ1bGxfam9pbihYLCBZKWAga2VlcHMgYWxsIHJvd3MsIGZpbGxpbmcgaW4gYE5BYCB3aGVuIHZhbHVlcyBhcmUgbWlzc2luZyBmcm9tIGVpdGhlciBvbmUuDQoNClVzdWFsbHksIGBpbm5lcl9qb2luKClgIHdpbGwgd29yayB3ZWxsLiBXaGVuIHlvdSB2aXN1YWxpemUgZGF0YSB5b3UgZmlsdGVyIG91dCBgTkFgIGFueXdheXMuDQoNClNvIG5vdyBsZXQncyBpbm5lcl9qb2luIHRoZW0uIEJ1dCB3YWl0Li4uICAgDQpUaGVzZSB0YWJsZXMgYXJlIG5vdCBpbiB0aGUgdGlkeSBmb3JtYXQhDQpUaGUgeWVhciB2YWx1ZXMgYXJlIHNwcmVhZCBvdXQgYWNyb3NzIG1hbnkgY29sdW1ucywgc28geW91IG5lZWQgdG8gcGl2b3QgdGhlbSB0byAibG9uZyIgb3IgdGlkeSBmb3JtYXQgZmlyc3QuDQoNCmBgYHtyfQ0KYmFiaWVzX3Blcl93b21hbl90aWR5IDwtIGJhYmllc19wZXJfd29tYW4gJT4lIA0KICBwaXZvdF9sb25nZXIobmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJiaXJ0aCIsIGNvbHMgPSBjKDI6MzAyKSkgDQoNCmhlYWQoYmFiaWVzX3Blcl93b21hbl90aWR5KQ0KDQpjaGlsZF9tb3J0YWxpdHlfdGlkeSA8LSBjaGlsZF9tb3J0YWxpdHkgJT4lIA0KICBwaXZvdF9sb25nZXIobmFtZXNfdG8gPSAieWVhciIsIHZhbHVlc190byA9ICJkZWF0aF9wZXJfMTAwMF9ib3JuIiwgY29scyA9IGMoMjozMDIpKSANCg0KaGVhZChjaGlsZF9tb3J0YWxpdHlfdGlkeSkNCmBgYA0KDQpOb3cgZWFjaCB0YWJsZSBoYXMgYSB5ZWFyIGNvbHVtbiwgYSB2YWx1ZSBjb2x1bW4gKGVpdGhlciBiaXJ0aCBvciBkZWF0aCkuDQpUaGUgYGMoMjozMDIpYCBhcmd1bWVudCBpbiBgcGl2b3RfbG9uZ2VyKClgIHNwZWNpZmllcyBnYXRoZXJpbmcgZGF0YSBmcm9tIDJuZCB0aHJvdWdoIHRoZSAzMDJuZCBjb2x1bW4uDQpOb3cgd2UgY2FuIGpvaW4gdGhlbS4NCg0KV2Ugd2lsbCB1c2UgYFggJT4lIGlubmVyX2pvaW4oWSwgYnkgPSBjb21tb24gY29sdW1ucylgIHN5bnRheC4NCkluIHRoaXMgY2FzZSB3ZSBoYXZlIHR3byBjb21tb24gY29sdW1ucywgY291bnRyeSBhbmQgeWVhci4NClRoaXMgaXMgYXNraW5nIFIgdG8gbWF0Y2ggdGhlIGNvdW50cmllcyBhbmQgeWVhcnMuDQoNCmBgYHtyfQ0KYmlydGhfYW5kX21vcnRhbGl0eSA8LSBiYWJpZXNfcGVyX3dvbWFuX3RpZHkgJT4lIA0KICBpbm5lcl9qb2luKGNoaWxkX21vcnRhbGl0eV90aWR5LCBieSA9IGMoImNvdW50cnkiLCAieWVhciIpKQ0KDQpoZWFkKGJpcnRoX2FuZF9tb3J0YWxpdHkpDQpgYGANCg0KU28gbm93IHlvdSBoYXZlIGEgdGFibGUgd2l0aCA0IHZhcmlhYmxlczogY291bnRyaWVzLCB5ZWFyLCBiaXJ0aCBwZXIgd29tYW4gYW5kIGRlYXRoIHBlciAxMDAwIGJvcm4uDQpOb3cgeW91IGhhdmUgYWxsIHRoZSBkYXRhIHRvZ2V0aGVyLg0KDQpUbyBxdWlja2x5IHRlc3Qgb3VyIGh5cG90aGVzaXMgdGhhdCBoaWdoZXIgbW9yYWxpdHkgaXMgY29ycmVsYXRlZCB3aXRoIG1vcmUgYmlydGggcGVyIHdvbWFuLCB3ZSBjYW4gcGxvdCBpdC4gDQpUaGVyZSBhcmUgdG9vIG11Y2ggZGF0YSB0byBsb29rIGF0LCBidXQgbGV0J3MganVzdCBwdWxsIG91dCB0aGUgeWVhciAxOTQ1LCB3aGVuIFdXSUkgZW5kZWQuDQpXZSB3aWxsIHRhbGsgYWJvdXQgaG93IHRvIHVzZSBnZ3Bsb3QgbGF0ZXIsIHNvIHlvdSBjYW4gaWdub3JlIHRoaXMgY2h1bmsgZm9yIG5vdy4NCg0KYGBge3J9DQpiaXJ0aF9hbmRfbW9ydGFsaXR5ICU+JSANCiAgZmlsdGVyKHllYXIgPT0gMTk0NSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBiaXJ0aCwgeSA9IGRlYXRoX3Blcl8xMDAwX2Jvcm4pKSArDQogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjgpICsNCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgc2UgPSBGKSArDQogIGxhYnMoeCA9ICJOby4gY2hpbGRyZW4gcGVyIHdvbWFuIiwNCiAgICAgICB5ID0gIkNoaWxkIG1vcnRhbGl0eS8xMDAwIGJvcm4iLA0KICAgICAgIHRpdGxlID0gIlllYXIgMTk0NSIpICsNCiAgdGhlbWVfY2xhc3NpYygpDQoNCmdnc2F2ZSgiLi4vUmVzdWx0cy8wMl9zY2F0dGVyX3Bsb3QucG5nIiwgd2lkdGggPSAyLjUsIGhlaWdodCA9IDIuNSkNCmBgYA0KDQpZb3UgZG8gc2VlIGEgdXB3YXJkIHRyZW5kLCBzdXBwb3J0aW5nIG91ciBoeXBvdGhlc2lzLg0KSW4gY291bnRyaWVzIHdpdGggaGlnaGVyIGNoaWxkIG1vcnRhbGl0eSwgd29tZW4gYWxzbyB0ZW5kZWQgdG8gZ2l2ZSBtb3JlIGJpcnRocy4NCg0KIyBFeGVyY2lzZQ0KYGBge3J9DQppbmNvbWVfdGlkeSANCg0KYGBgDQoNCllvdSBoYXZlIGxlYXJuZWQgZGF0YSBhcnJhbmdlbWVudCEgTGV0J3MgZG8gYW4gZXhlcmNpc2UgdG8gcHJhY3RpY2Ugd2hhdA0KeW91IGhhdmUgbGVhcm5lZCB0b2RheS4gDQpBcyB0aGUgZXhhbXBsZSwgdGhpcyB0aW1lIHdlIHdpbGwgdXNlIGluY29tZSBwZXIgcGVyc29uIGRhdGFzZXQgZnJvbSBHYXBtaW5kZXIgZm91bmRhdGlvbi4NCg0KYGBge3J9DQppbmNvbWUgPC0gcmVhZF9jc3YoIi4uL0RhdGEvaW5jb21lX3Blcl9wZXJzb25fZ2RwcGVyY2FwaXRhX3BwcF9pbmZsYXRpb25fYWRqdXN0ZWQuY3N2IiwgY29sX3R5cGVzID0gY29scygpKSANCmhlYWQoaW5jb21lKQ0KYGBgDQoNCiMjIFRpZHkgZGF0YQ0KDQpJcyB0aGlzIGEgdGlkeSBkYXRhIGZyYW1lPw0KDQpOby4gVGhpcyBpcyBub3QgYSB0aWR5IGRhdGEgZnJhbWUuDQoNCmBgYHtyfQ0KaW5jb21lIDwtIHJlYWRfY3N2KCIuLi9EYXRhL2luY29tZV9wZXJfcGVyc29uX2dkcHBlcmNhcGl0YV9wcHBfaW5mbGF0aW9uX2FkanVzdGVkLmNzdiIpDQoNCmhlYWQoaW5jb21lKQ0KYGBgDQoNCmBgYHtyfQ0KaW5jb21lX3RpZHkgPC0gaW5jb21lICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IDI6MjQyLCBuYW1lc190byA9ICJ5ZWFyIiwgdmFsdWVzX3RvID0gImluY29tZSIpDQoNCmhlYWQoaW5jb21lX3RpZHkpDQpgYGANCg0KDQpNYWtlIGl0IGEgdGlkeSBkYXRhIGZyYW1lIHVzaW5nIHRoaXMgY29kZSBjaHVuay4NCg0KYGBge3J9DQppbmNvbWVfdGlkeSA8LSBpbmNvbWUgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gMjoyNDIsIG5hbWVzX3RvID0gInllYXIiLCB2YWx1ZXNfdG8gPSAiaW5jb21lIikNCg0KaGVhZChpbmNvbWVfdGlkeSkNCmBgYA0KDQpgYGB7cn0NCnN0cihpbmNvbWVfdGlkeSkNCmBgYA0KDQoNCg0KYGBge3J9DQppbmNvbWVfdGlkeSA8LSBpbmNvbWUgJT4lIHBpdm90X2xvbmdlcihjb2xzID0gMjoyNDIsIG5hbWVzX3RvID0gInllYXIiLCB2YWx1ZXNfdG8gPSAiaW5jb21lIikNCg0KaGVhZChpbmNvbWVfdGlkeSkNCmBgYA0KDQoNCg0KDQpIaW50OiB0aGUgeWVhcnMgYXJlIHNwcmVhZCBvdXQgZnJvbSBjb2x1bW5zIDIgdG8gMjQyLg0KDQpgYGB7cn0NCiBzdHIoaW5jb21lKQ0KYGBgDQoNCiMjIEpvaW5pbmcgZGF0YQ0KDQpDb21iaW5lIHRoZSBpbmNvbWUgZGF0YSB3aXRoIGJpcnRoIHBlciB3b21hbiBhbmQgY2hpbGQgbW9ydGFsaXR5IGRhdGEgdXNpbmcgdGhpcyBjb2RlIGNodW5rLg0KTmFtZSB0aGUgbmV3IGRhdGEgZnJhbWUgImJpcnRoX2FuZF9tb3J0YWxpdHlfYW5kX2luY29tZSIuDQoNCmBgYHtyfQ0KYmlydGhfYW5kX21vcnRhbGl0eV9hbmRfaW5jb21lIDwtIGJpcnRoX2FuZF9tb3J0YWxpdHkgJT4lIA0KICBpbm5lcl9qb2luKGluY29tZV90aWR5LCBieSA9IGMoInllYXIiLCAiY291bnRyeSIpKQ0KDQpoZWFkKGJpcnRoX2FuZF9tb3J0YWxpdHlfYW5kX2luY29tZSkNCmBgYA0KDQojIyBGaWx0ZXJpbmcgZGF0YQ0KDQpGaWx0ZXIgb3V0IHRoZSBkYXRhIGZvciBCYW5nbGFkZXNoIGFuZCBTd2VkZW4sIGluIHllYXJzIDE5NDUgKHdoZW4gV1dJSSBlbmRlZCkgYW5kIDIwMTAuDQpOYW1lIHRoZSBuZXcgZGF0YSBmcmFtZSBCU18xOTQ1XzIwMTAuDQpIb3cgaGFzIGluY29tZSwgYmlydGggcGVyIHdvbWFuIGFuZCBjaGlsZCBtb3J0YWxpdHkgcmF0ZSBjaGFuZ2VkIGR1cmluZyB0aGlzIDU1LXllYXIgcGVyaW9kPw0KDQpBbnN3ZXI7IEJpcnRoIHBlciB3b21hbiBhbmQgQ2hpbGQgTW9ydGFsaXR5IHJhdGVzIGhhdmUgZHJvcHBlZCBzaWduaWZpY2FudGx5Lg0KDQoNCmBgYHtyfQ0KQlNfMTk0NV8yMDEwIDwtIGJpcnRoX2FuZF9tb3J0YWxpdHlfYW5kX2luY29tZSAlPiUNCiAgZmlsdGVyKHllYXIgPT0gMTk0NSB8DQogICAgICAgICAgIHllYXIgPT0gMjAyMSkgJT4lDQogIGZpbHRlcihjb3VudHJ5ID09ICJCYW5nbGFkZXNoIiB8DQogICAgICAgICAgIGNvdW50cnkgPT0gIlN3ZWRlbiIpDQpoZWFkKEJTXzE5NDVfMjAxMCkNCg0KYGBgDQoNCiMjIE11dGF0ZSBkYXRhDQoNCkxldCdzIHNheSBmb3IgY291bnRyaWVzIHdpdGggaW5jb21lIGJldHdlZW4gMTAwMCB0byAxMCwwMDAgZG9sbGFycyBwZXIgeWVhciwgdGhleSBhcmUgY2FsbGVkICJmZWQiLg0KRm9yIGNvdW50cmllcyB3aXRoIGluY29tZSBhYm92ZSAxMCwwMDAgZG9sbGFycyBwZXIgeWVhciwgdGhleSBhcmUgY2FsbGVkICJ3ZWFsdGh5Ii4NCkJlbG93IDEwMDAsIHRoZXkgYXJlIGNhbGxlZCAicG9vciIuDQoNClVzaW5nIHRoaXMgaW5mbyB0byBtYWtlIGEgbmV3IGNvbHVtbiBjYWxsZWQgInN0YXR1cyIuDQpIaW50OiB5b3Ugd2lsbCBoYXZlIHRvIHVzZSBjYXNlX3doZW4oKSBhbmQgdGhlICImIiBsb2dpYyBzb21ld2hlcmUgaW4gdGhpcyBjaHVuay4NCg0KYGBge3J9DQpiaXJ0aF9hbmRfbW9ydGFsaXR5X2FuZF9pbmNvbWUgPC0gYmlydGhfYW5kX21vcnRhbGl0eV9hbmRfaW5jb21lICU+JQ0KICBtdXRhdGUoc3RhdHVzID0gY2FzZV93aGVuKGluY29tZSA8IDEwMDAgfiAicG9vciIsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jb21lID49IDEwMDAgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jb21lIDwgMTAwMDAgfiAiZmVkIiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBUIH4gIndlYWx0aHkiKSkNCmhlYWQoYmlydGhfYW5kX21vcnRhbGl0eV9hbmRfaW5jb21lKQ0KDQpgYGANCg0KIyMgU3VtbWFyaXNlIHRoZSBkYXRhDQoNCkxldCdzIGxvb2sgYXQgdGhlIGF2ZXJhZ2UgY2hpbGQgbW9ydGFsaXR5IGFuZCBpdHMgc2QgaW4geWVhciAyMDEwLiANCmFjcm9zcyBjb3VudHJpZXMgYWNyb3NzIGRpZmZlcmVudCBzdGF0dXMgdGhhdCB3ZSBqdXN0IGRlZmluZWQuIA0KTmFtZSB0aGUgbmV3IGRhdGEgZnJhbWUgImNoaWxkX21vcnRhbGl0eV9zdW1tbWFyeV8yMDEwIi4NCg0KYGBge3J9DQpiaXJ0aF9hbmRfbW9ydGFsaXR5X2FuZF9pbmNvbWUgJT4lDQogIGZpbHRlcih5ZWFyID09IDIwMTApICU+JQ0KICBncm91cF9ieShzdGF0dXMpICU+JQ0KICBzdW1tYXJpc2UoDQogICAgbWVhbiA9IG1lYW4oZGVhdGhfcGVyXzEwMDBfYm9ybiksDQogICAgc2QgPSBzZChkZWF0aF9wZXJfMTAwMF9ib3JuKQ0KICApDQoNCg0KYGBgDQoNCkhvdyBkb2VzIGNoaWxkIG1vcnRhbGl0eSBjb21wYXJlIGFjcm9zcyBpbmNvbWUgZ3JvdXAgaW4geWVhciAyMDEwPw0KDQpBbnN3ZXI7IENoaWxkIG1vcnRhbGl0eSBpcyBoaWdoZXN0IGluIHRoZSAicG9vciIgZ3JvdXAuDQoNCg==