Data Structures and Data Attribute Types

Data comes in many different forms.

Some of the most common data structures are

Other forms include

We will work mostly with tables.

Many other forms can be reduced to tables.

Stevens (1945) classifies scales of measurement for attributes, or variables, as

These are sometimes grouped as

These can be viewed as semantic classifications

Computational considerations often classify variables as

Another consideration is that some scales may be cyclic:

These distinctions can be important in choosing visual representations.

Other typologies include one proposed by Mosteller and Tukey (1977):

  1. Names
  2. Grades (ordered labels like beginner, intermediate, advanced)
  3. Ranks (orders with 1 being the smallest or largest, 2 the next smallest or largest, and so on)
  4. Counted fractions (bound by 0 and 1)
  5. Counts (non-negative integers)
  6. Amounts (non-negative real numbers)
  7. Balances (any real number)

Data Types in R

R variables can be of different types.

The most common types are

Factors can be

factors are more efficient and powerful for representing nominal or ordinal data than character data but can take a bit more getting used to.

Membership predicates and coercion functions are

Predicate Coersion
is.numeric as.numeric
is.integer as.integer
is.character as.character
is.factor as.factor
is.ordered as.ordered

Conversion of factors with numeric-looking labels to numeric data should always go through as.character first.

Data Frames: Organizing Cases and Variables

Tabular data in R is usually stored as a data frame.

A data frame is a collection of variables, each with a value for every case or observacion.

The faithful data set is a data frame:

class(faithful)
## [1] "data.frame"
names(faithful)
## [1] "eruptions" "waiting"

Most tools we work with in R use data organized in data frames.

Our plot() and lm() expressions from the introductory section can also we written as

plot(waiting ~ eruptions, data = faithful,
     xlab = "Eruption time (min)",
     ylab = "Waiting time to next eruption (min)")
fit <- lm(waiting ~ eruptions, data = faithful)

plot() only uses the data argument when the plot is specified as a formula, like

waiting ~ eruptions

Examining the Data in a Data Frame

head() provides an idea of what the raw data looks like:

head(faithful)
##   eruptions waiting
## 1     3.600      79
## 2     1.800      54
## 3     3.333      74
## 4     2.283      62
## 5     4.533      85
## 6     2.883      55

str() is also useful for an overview of an object’s structure:

str(faithful)
## 'data.frame':    272 obs. of  2 variables:
##  $ eruptions: num  3.6 1.8 3.33 2.28 4.53 ...
##  $ waiting  : num  79 54 74 62 85 55 88 85 51 85 ...

Another useful function available from the dplyr or tibble packages is glimpse():

library(dplyr)
glimpse(faithful)
## Rows: 272
## Columns: 2
## $ eruptions <dbl> 3.600, 1.800, 3.333, 2.283, 4.533, 2.883, 4.700, 3.600, 1.95…
## $ waiting   <dbl> 79, 54, 74, 62, 85, 55, 88, 85, 51, 85, 54, 84, 78, 47, 83, …

summary() shows basic statistical properties of the variables:

summary(faithful)
##    eruptions        waiting    
##  Min.   :1.600   Min.   :43.0  
##  1st Qu.:2.163   1st Qu.:58.0  
##  Median :4.000   Median :76.0  
##  Mean   :3.488   Mean   :70.9  
##  3rd Qu.:4.454   3rd Qu.:82.0  
##  Max.   :5.100   Max.   :96.0

summary() with a character variable and a factor variable:

ffl <- mutate(faithful,
              type = ifelse(eruptions < 3, "short", "long"),
              ftype = factor(type))
summary(ffl)
##    eruptions        waiting         type             ftype    
##  Min.   :1.600   Min.   :43.0   Length:272         long :175  
##  1st Qu.:2.163   1st Qu.:58.0   Class :character   short: 97  
##  Median :4.000   Median :76.0   Mode  :character              
##  Mean   :3.488   Mean   :70.9                                 
##  3rd Qu.:4.454   3rd Qu.:82.0                                 
##  Max.   :5.100   Max.   :96.0

Variables in a Data Frame

A Data frame is a list, or vector, of variables:

length(faithful)
## [1] 2

The dollar sign $ can be used to examine individual variables:

class(faithful$eruptions)
## [1] "numeric"
class(faithful$waiting)
## [1] "numeric"

The variables can also be extracted by numerical or character index using the element extraction operation [[:

class(faithful[[1]])
## [1] "numeric"
class(faithful[["waiting"]])
## [1] "numeric"

Dimensions

The numbers of rows and columns can be obtained using nrow() and ncol():

ncol(faithful)
## [1] 2
nrow(faithful)
## [1] 272

dim() returns a vector of the dimensions:

dim(faithful)
## [1] 272   2

Simple Visualizations

plot has a method for data frames that tries to provide a reasonable default visualization for numeric data frames:

plot(faithful)

Sample Data Sets

The datasets package in the base R distribution contains a number of data sets you can explore.

Another package with a useful collection of data sets is dslabs.

Many other data sets are contained in contributed packages as examples.

There are also many contributed packages designed specifically for making particular data sets available.

Tidy Data

The useful concept of a tidy data frame was introduced fairly recently and is described is a chapter in R for Data Science.

The idea is that

Tidy data is computationally convenient, and many of the tools we will use are designed around tidy data frames.

A large range of these tools can be accessed by loading the tidyverse package:

library(tidyverse)

But for now I will load the needed packages individually.

The term tidy is a little unfortunate.

  • Data that is not tidy isn’t necessarily bad.
  • For human reading, and for some computations, data in a wider format can be better.
  • For other computations data in a longer, or narrower, format can be better.

Tibbles

Many tools in the tidyverse produce slightly enhanced data frames called tibbles:

library(tibble)
faithful_tbl <- as_tibble(faithful)
class(faithful_tbl)
## [1] "tbl_df"     "tbl"        "data.frame"

Tibbles print differently from standard data frames:

faithful_tbl
## # A tibble: 272 × 2
##    eruptions waiting
##        <dbl>   <dbl>
##  1      3.6       79
##  2      1.8       54
##  3      3.33      74
##  4      2.28      62
##  5      4.53      85
##  6      2.88      55
##  7      4.7       88
##  8      3.6       85
##  9      1.95      51
## 10      4.35      85
## # ℹ 262 more rows

For the most part data frames and tibbles can be used interchangeably.

Tidying Data

Many data sets are in tidy form already.

If they are not, they can be put into tidy form.

The tools for this are part of data technologies.

The tasks involved are part of what is sometimes called data wrangling.

An Example: Global Average Surface Temperatures

Among many data sets available at https://data.giss.nasa.gov/gistemp/ is data on monthly global average surface temperatures over a number of years.

These data from 2017 were used for the widely cited Bloomberg hottest year visualization.

The current data are available in a formatted text file at

https://data.giss.nasa.gov/gistemp/tabledata_v4/GLB.Ts+dSST.txt

or as a CSV (comma-separated values) file at

https://data.giss.nasa.gov/gistemp/tabledata_v4/GLB.Ts+dSST.csv

The first few lines of the CSV file:

    Land-Ocean: Global Means
    Year,Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec,J-D,D-N,DJF,MAM,JJA,SON
    1880,-.18,-.24,-.08,-.16,-.09,-.21,-.18,-.10,-.14,-.23,-.21,-.17,-.16,***,***,-.11,-.16,-.19
    1881,-.19,-.13,.04,.06,.07,-.18,.01,-.03,-.15,-.21,-.18,-.06,-.08,-.09,-.16,.05,-.07,-.18
    1882,.17,.14,.05,-.15,-.13,-.22,-.16,-.07,-.14,-.23,-.16,-.36,-.11,-.08,.08,-.08,-.15,-.18
    1883,-.29,-.36,-.12,-.18,-.17,-.07,-.07,-.13,-.22,-.11,-.24,-.11,-.17,-.19,-.33,-.16,-.09,-.19

The CSV file is a little easier (for a computer program) to read in, so we will work with that.

The numbers in the CSV file represent deviations in degrees Celcius from the average temperature for the base period 1951-1980.

The file available on January 19, 2023, is now available locally.

We can make sure it has been downloaded to our working directory with

if (! file.exists("GLB.Ts+dSST.csv"))
    download.file("https://stat.uiowa.edu/~luke/data/GLB.Ts+dSST.csv",
                  "GLB.Ts+dSST.csv")

Assuming this locally available file has been downloaded, we can read in the data and drop some columns we don’t need with

library(readr)
gast <- read_csv("GLB.Ts+dSST.csv", skip = 1)[1 : 13]
  • The function read_csv is from the readr package, which is part of the tidyverse.
  • An alternative is the base R function read.csv.

A look a the first few lines:

head(gast, 5)
## # A tibble: 5 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct   Nov   Dec
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1  1880 -0.18 -0.24 -0.08 -0.16 -0.09 -0.21 -0.18 -0.1  -0.14 -0.23 -0.21 -0.17
## 2  1881 -0.19 -0.13  0.04  0.06  0.07 -0.18  0.01 -0.03 -0.15 -0.21 -0.18 -0.06
## 3  1882  0.17  0.14  0.05 -0.15 -0.13 -0.22 -0.16 -0.07 -0.14 -0.23 -0.16 -0.36
## 4  1883 -0.29 -0.36 -0.12 -0.18 -0.17 -0.07 -0.07 -0.13 -0.22 -0.11 -0.24 -0.11
## 5  1884 -0.12 -0.08 -0.36 -0.4  -0.33 -0.34 -0.3  -0.27 -0.27 -0.24 -0.33 -0.3

And the last few lines:

tail(gast, 5)
## # A tibble: 5 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct   Nov   Dec
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1  2018  0.81  0.84  0.88  0.89  0.82  0.77  0.82  0.76  0.8   1.01  0.82  0.92
## 2  2019  0.93  0.94  1.17  1.01  0.85  0.91  0.94  0.94  0.92  1.01  0.99  1.09
## 3  2020  1.16  1.24  1.17  1.13  1.02  0.92  0.9   0.88  0.99  0.89  1.1   0.81
## 4  2021  0.81  0.64  0.89  0.76  0.78  0.84  0.91  0.81  0.92  1     0.93  0.87
## 5  2022  0.91  0.89  1.05  0.84  0.84  0.92  0.93  0.95  0.9   0.97  0.72  0.8

The print() method for tibbles abbreviates the output.

It is neater and provides some useful additional information on variable data types.

But it shows only the first few rows and may not explicitly show some columns.

If some columns are skipped, you can ask to see more by calling print() explicitly:

print(tail(gast), width = 100)
## # A tibble: 6 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct   Nov   Dec
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1  2017  1.02  1.13  1.16  0.94  0.91  0.72  0.81  0.87  0.76  0.9   0.88  0.93
## 2  2018  0.81  0.84  0.88  0.89  0.82  0.77  0.82  0.76  0.8   1.01  0.82  0.92
## 3  2019  0.93  0.94  1.17  1.01  0.85  0.91  0.94  0.94  0.92  1.01  0.99  1.09
## 4  2020  1.16  1.24  1.17  1.13  1.02  0.92  0.9   0.88  0.99  0.89  1.1   0.81
## 5  2021  0.81  0.64  0.89  0.76  0.78  0.84  0.91  0.81  0.92  1     0.93  0.87
## 6  2022  0.91  0.89  1.05  0.84  0.84  0.92  0.93  0.95  0.9   0.97  0.72  0.8

The format with one column per month is compact and useful for viewing and data entry.

But it is not in tidy format since

For obvious reasons this data format is often referred to as wide format.

The tidy, or long, format would have three variables: Year, Month, and Temp.

One way to put this data frame in tidy, or long, format uses pivot_longer from the tidyr package:

library(tidyr)
lgast <- pivot_longer(gast,
                      -Year,  ## specifies the columns to use -- all but Year
                      names_to = "Month",
                      values_to = "Temp")

The first few rows of the result:

head(lgast)
## # A tibble: 6 × 3
##    Year Month  Temp
##   <dbl> <chr> <dbl>
## 1  1880 Jan   -0.18
## 2  1880 Feb   -0.24
## 3  1880 Mar   -0.08
## 4  1880 Apr   -0.16
## 5  1880 May   -0.09
## 6  1880 Jun   -0.21

During plotting it is likely that the Month variable will be converted to a factor.

By default, this will order levels alphabetically, which is not what we want:

levels(as.factor(lgast$Month))
##  [1] "Apr" "Aug" "Dec" "Feb" "Jan" "Jul" "Jun" "Mar" "May" "Nov" "Oct" "Sep"

We can guard against this by converting Month to a factor with the right levels now:

lgast <- mutate(lgast, Month = factor(Month, levels = month.abb))
levels(lgast$Month)
##  [1] "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"

We can now use this tidy version of the data to create a static version of the Bloomberg hottest year visualization.

The basic framework is set up with

library(ggplot2)
p <- ggplot(lgast) +
    ggtitle("Global Average Surface Temperatures") +
    theme(plot.title = element_text(hjust = 0.5))

ggplot objects only produce output when they are printed.

To see the plot in p we need to print it, for example by using a line line with only p.

 p

Then add a layer with lines for each year (specified by the group argument to geom_line).

p + geom_line(aes(x = Month,
                  y = Temp,
                  group = Year))

We can use color to distingish the years.

p1 <- p +
    geom_line(aes(x = Month,
                  y = Temp,
                  color = Year,
                  group = Year),
              na.rm = TRUE)
p1

Saving the plot specification in the variable p1 will make it easier to experiment with color variations:

One way to highlight the past_year 2022:

lgast_last <- filter(lgast, Year == past_year)
p1 + geom_line(aes(x = Month,
                   y = Temp,
                   group = Year),
               linewidth = 1,
               color = "red",
               data = lgast_last,
               na.rm = TRUE)

A useful way to show more recent data in the context of the full data is to show the full data in grey and the more recent years in black:

lgast2k <- filter(lgast, Year >= 2000)
ggplot(lgast, aes(x = Month,
                  y = Temp,
                  group = Year)) +
    geom_line(color = "grey80") +
    theme_minimal() +
    geom_line(data = lgast2k)

If you want to update your plot later in the year then the current year’s entry may contain missing value indicators that you will have to deal with.

The New York Times on January 18, 2018, published another visualization of these data showing average yearly temperatures (via Google may work better).

To recreate this plot we first need to compute yearly average temperatures.

This is easy to do with the summarize and group_by functions from dpyr:

library(dplyr)
atemp <- lgast %>%
    group_by(Year) %>%
    summarize(AveTemp = mean(Temp, na.rm = TRUE))
head(atemp)
## # A tibble: 6 × 2
##    Year AveTemp
##   <dbl>   <dbl>
## 1  1880 -0.166 
## 2  1881 -0.0792
## 3  1882 -0.105 
## 4  1883 -0.172 
## 5  1884 -0.278 
## 6  1885 -0.328

Using na.rm = TRUE ensures that the mean is based on the available months if data for some months is missing.

A simple version of the plot is then produced by

ggplot(atemp) +
    geom_point(aes(x = Year, y = AveTemp))

Another variation on the Bloomberg plot showing just a few years 20 years apart:

lg_by_20 <-
    filter(lgast,
           Year %in% seq(2020, by = -20, len = 5)) %>%
    mutate(Year = factor(Year))
ggplot(lg_by_20, aes(x = Month,
                     y = Temp,
                     group = Year,
                     color = Year)) +
    geom_line()

Converting Year to a factor results in a discrete color scale and legend.

Handling Missing Values

The data for 2019 available in early 2020 is also available locally.

Assuming this locally available file has been downloaded, we can read in the data and drop some columns we don’t need with

gast2019 <- read_csv("GLB.Ts+dSST-2019.csv", skip = 1)[1 : 13]

The last three columns are read as character variables:

head(gast2019, 5)
## # A tibble: 5 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep Oct   Nov   Dec  
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1  1880 -0.17 -0.23 -0.08 -0.15 -0.08 -0.2  -0.17 -0.09 -0.13 -.22  -.20  -.16 
## 2  1881 -0.18 -0.13  0.04  0.06  0.08 -0.17  0.02 -0.02 -0.14 -.20  -.17  -.05 
## 3  1882  0.18  0.15  0.06 -0.15 -0.13 -0.21 -0.15 -0.06 -0.13 -.23  -.15  -.34 
## 4  1883 -0.28 -0.36 -0.11 -0.17 -0.16 -0.07 -0.05 -0.12 -0.2  -.10  -.22  -.10 
## 5  1884 -0.12 -0.07 -0.36 -0.39 -0.33 -0.34 -0.32 -0.27 -0.26 -.24  -.32  -.30

The reason is that data for October through December were not available:

tail(gast2019, 2)
## # A tibble: 2 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep Oct   Nov   Dec  
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr> <chr>
## 1  2018  0.82  0.85  0.9   0.89  0.83  0.78  0.83  0.76  0.81 1.02  .83   .92  
## 2  2019  0.94  0.96  1.18  1.02  0.86  0.93  0.95  0.94  0.93 ***   ***   ***

We want to convert these columns to numeric, with missing values represented as NA.

One option is to handle them individually:

gast2019$Oct <- as.numeric(gast2019$Oct)
## Warning: NAs introduced by coercion
tail(gast2019, 2)
## # A tibble: 2 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct Nov   Dec  
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <chr> <chr>
## 1  2018  0.82  0.85  0.9   0.89  0.83  0.78  0.83  0.76  0.81  1.02 .83   .92  
## 2  2019  0.94  0.96  1.18  1.02  0.86  0.93  0.95  0.94  0.93 NA    ***   ***

Another option is to convert all character columns to numeric with

gast2019 <- mutate(gast2019, across(where(is.character), as.numeric))
## Warning: There were 2 warnings in `mutate()`.
## The first warning was:
## ℹ In argument: `across(where(is.character), as.numeric)`.
## Caused by warning:
## ! NAs introduced by coercion
## ℹ Run `dplyr::last_dplyr_warnings()` to see the 1 remaining warning.
tail(gast2019, 2)
## # A tibble: 2 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct   Nov   Dec
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1  2018  0.82  0.85  0.9   0.89  0.83  0.78  0.83  0.76  0.81  1.02  0.83  0.92
## 2  2019  0.94  0.96  1.18  1.02  0.86  0.93  0.95  0.94  0.93 NA    NA    NA

The warnings are benign and can be suppressed with the warning = FALSE chunk option.

Since we know the missing value pattern *** we can also avoid the need to fix the data after the fact by specifying this at read time:

gast2019 <- read_csv("GLB.Ts+dSST-2019.csv", na = "***", skip = 1)[1 : 13]
tail(gast2019, 2)
## # A tibble: 2 × 13
##    Year   Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct   Nov   Dec
##   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
## 1  2018  0.82  0.85  0.9   0.89  0.83  0.78  0.83  0.76  0.81  1.02  0.83  0.92
## 2  2019  0.94  0.96  1.18  1.02  0.86  0.93  0.95  0.94  0.93 NA    NA    NA

A plot highlighting the year 2019 shows only the months with available data:

lgast2019 <- gast2019 %>%
    pivot_longer(-Year,
                 names_to = "Month",
                 values_to = "Temp") %>%
    mutate(Month = factor(Month, levels = month.abb))
ggplot(lgast2019, aes(x = Month,
                      y = Temp,
                      group = Year)) +
    geom_line(color = "grey80",
              na.rm = TRUE) +
    geom_line(data = filter(lgast2019, Year == 2019),
              na.rm = TRUE)

Adding na.rm = TRUE in the geom_line calls suppresses warnings; the plot would be the same without these.

Outline of the tools used:

  • Data processing:
    • Reading: read_csv, read.csv;
    • Reshaping: pivot_longer;
    • Cleaning: is.character, as.numeric, mutate, across, factor;
    • Summarizing: group_by, summarize.
  • Visualization geometries:
    • geom_line for a line plot;
    • geom_point for a scatter plot.

Reading

Stevens’ classification of scales of measurement is described in a Wikipedia article.

A good introduction to the concept of tidy data is provided in a chapter in R for Data Science.

Interactive Tutorial

An interactive learnr tutorial for these notes is available.

You can run the tutorial with

STAT4580::runTutorial("datafrm")

Exercises

  1. Which of the Stevens classifications (nominal, ordinal, interval, ratio) best characterizes these variables:

    1. Daily maximal temperatures in Iowa City.
    2. Population counts for Iowa counties.
    3. Education level of job applicants using the Bureau of Labor Statistics classification.
    4. Major of UI students.
  1. Which of these data sets are in tidy form?

    1. The builtin data set co2
    2. The builtin data set BOD
    3. The who data set in package tidyr (tidyr::who)
    4. The mpg data set in package ggplot2 (ggplot2::mpg)

The next exercises use the data in the variable gapminder in the package gapminder. You can make it available with

data(gapminder, package = "gapminder")
  1. Use the function str to examine the value of the gapminder variable. How many cases are there in the data set? How many of the variables are factors?

  2. Use the functions class and names to find the class and variable names in the gapminder data.

  3. Use summary to compute summary information for the variables.

  4. Fill in the values for --- needed to produce plots of life expectancy against year for the countries in continent Oceania.

library(dplyr)
library(ggplot2)
data(gapminder, package = "gapminder")
ggplot(filter(gapminder, continent == "Oceania"),
       aes(x = ---, y = ---, color = country)) +
    geom_line()
LS0tCnRpdGxlOiAiRGF0YSBhbmQgRGF0YSBGcmFtZXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgo8bGluayByZWw9InN0eWxlc2hlZXQiIGhyZWY9InN0YXQ0NTgwLmNzcyIgdHlwZT0idGV4dC9jc3MiIC8+CgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Kc291cmNlKGhlcmU6OmhlcmUoInNldHVwLlIiKSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KGNvbGxhcHNlID0gVFJVRSwgbWVzc2FnZSA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgZmlnLmhlaWdodCA9IDUsIGZpZy53aWR0aCA9IDYsIGZpZy5hbGlnbiA9ICJjZW50ZXIiKQpvcHRpb25zKHdpZHRoID0gODApCmBgYAoKCiMjIERhdGEgU3RydWN0dXJlcyBhbmQgRGF0YSBBdHRyaWJ1dGUgVHlwZXMKCkRhdGEgY29tZXMgaW4gbWFueSBkaWZmZXJlbnQgZm9ybXMuCgpTb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBkYXRhIHN0cnVjdHVyZXMgYXJlCgoqIHJlY3Rhbmd1bGFyIHRhYmxlcwoqIG5ldHdvcmtzIGFuZCB0cmVlcwoqIGdlb21ldHJpZXMsIHJlZ2lvbnMKCk90aGVyIGZvcm1zIGluY2x1ZGUKCiogY29sbGVjdGlvbnMgb2YgdGV4dAoqIHZpZGVvIGFuZCBhdWRpbyByZWNvcmRpbmdzCiogLi4uCgpXZSB3aWxsIHdvcmsgbW9zdGx5IHdpdGggdGFibGVzLgoKTWFueSBvdGhlciBmb3JtcyBjYW4gYmUgcmVkdWNlZCB0byB0YWJsZXMuCgpbU3RldmVuc10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvU3RhbmxleV9TbWl0aF9TdGV2ZW5zKSAoMTk0NSkKIGNsYXNzaWZpZXMgW3NjYWxlcyBvZgogbWVhc3VyZW1lbnRdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0xldmVsX29mX21lYXN1cmVtZW50KSBmb3IKIGF0dHJpYnV0ZXMsIG9yIHZhcmlhYmxlcywgYXMKPCEtLSBwZXJtYW5lbnQgbGluazogaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3cvaW5kZXgucGhwP3RpdGxlPUxldmVsX29mX21lYXN1cmVtZW50Jm9sZGlkPTEwNjA3NzI4NDcgLS0+IAoKKiBub21pbmFsIChlLmcuIGhhaXIgY29sb3IpCiogb3JkaW5hbCAoZS5nLiBkaXNsaWtlLCBuZXV0cmFsLCBsaWtlKQoqIGludGVydmFsIChlLmcuIHRlbXBlcmF0dXJlKSAtLS0gY29tcGFyZSBieSBkaWZmZXJlbmNlCiogcmF0aW8gKGUuZy4gY291bnRzKSAtLS0gY29tcGFyZSBieSByYXRpbwoKVGhlc2UgYXJlIHNvbWV0aW1lcyBncm91cGVkIGFzCgoqIHF1YWxpdGF0aXZlOiBub21pbmFsCiogcXVhbnRpdGF0aXZlOiBvcmRpbmFsLCBpbnRlcnZhbCwgcmF0aW8KClRoZXNlIGNhbiBiZSB2aWV3ZWQgYXMgX3NlbWFudGljIGNsYXNzaWZpY2F0aW9uc18KCl9Db21wdXRhdGlvbmFsIGNvbnNpZGVyYXRpb25zXyBvZnRlbiBjbGFzc2lmeSB2YXJpYWJsZXMgYXMKCiogY2F0ZWdvcmljYWwKKiBpbnRlZ2VyLCBkaXNjcmV0ZQoqIHJlYWwsIGNvbnRpbnVvdXMKCkFub3RoZXIgY29uc2lkZXJhdGlvbiBpcyB0aGF0IHNvbWUgc2NhbGVzIG1heSBiZSBjeWNsaWM6CgoqIGhvdXJzIG9mIHRoZSBkYXkKKiBhbmdsZXMsIGxvbmdpdHVkZQoKVGhlc2UgZGlzdGluY3Rpb25zIGNhbiBiZSBpbXBvcnRhbnQgaW4gY2hvb3NpbmcgdmlzdWFsIHJlcHJlc2VudGF0aW9ucy4KCk90aGVyIHR5cG9sb2dpZXMgaW5jbHVkZSBvbmUgcHJvcG9zZWQgYnkgW01vc3RlbGxlciBhbmQgVHVrZXkKKDE5NzcpXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9MZXZlbF9vZl9tZWFzdXJlbWVudCNNb3N0ZWxsZXJfYW5kX1R1a2V5J3NfdHlwb2xvZ3lfKDE5NzcpKToKCjEuIE5hbWVzCjIuIEdyYWRlcyAob3JkZXJlZCBsYWJlbHMgbGlrZSBiZWdpbm5lciwgaW50ZXJtZWRpYXRlLCBhZHZhbmNlZCkKMy4gUmFua3MgKG9yZGVycyB3aXRoIDEgYmVpbmcgdGhlIHNtYWxsZXN0IG9yIGxhcmdlc3QsIDIgdGhlIG5leHQKICAgc21hbGxlc3Qgb3IgbGFyZ2VzdCwgYW5kIHNvIG9uKQo0LiBDb3VudGVkIGZyYWN0aW9ucyAoYm91bmQgYnkgMCBhbmQgMSkKNS4gQ291bnRzIChub24tbmVnYXRpdmUgaW50ZWdlcnMpCjYuIEFtb3VudHMgKG5vbi1uZWdhdGl2ZSByZWFsIG51bWJlcnMpCjcuIEJhbGFuY2VzIChhbnkgcmVhbCBudW1iZXIpCgoKIyMgRGF0YSBUeXBlcyBpbiBSCgpSIHZhcmlhYmxlcyBjYW4gYmUgb2YgZGlmZmVyZW50IHR5cGVzLgoKVGhlIG1vc3QgY29tbW9uIHR5cGVzIGFyZQoKKiBgbnVtZXJpY2AgZm9yIHJlYWwgbnVtYmVycwoqIGBpbnRlZ2VyYAoqIGBjaGFyYWN0ZXJgIGZvciB0ZXh0IGRhdGEgb3Igbm9taW5hbCBkYXRhCiogYGZhY3RvcmAgZm9yIG5vbWluYWwgb3Igb3JkaW5hbCBkYXRhCgpGYWN0b3JzIGNhbiBiZQoKKiB1bm9yZGVyZWQsIGZvciBub21pbmFsIGRhdGEKKiBvcmRlcmVkLCBmb3Igb3JkaW5hbCBkYXRhCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KYGZhY3RvcnNgIGFyZSBtb3JlIGVmZmljaWVudCBhbmQgcG93ZXJmdWwgZm9yIHJlcHJlc2VudGluZyBub21pbmFsIG9yCm9yZGluYWwgZGF0YSB0aGFuIGBjaGFyYWN0ZXJgIGRhdGEgYnV0IGNhbiB0YWtlIGEgYml0IG1vcmUgZ2V0dGluZwp1c2VkIHRvLgo8L2Rpdj4KCk1lbWJlcnNoaXAgcHJlZGljYXRlcyBhbmQgY29lcmNpb24gZnVuY3Rpb25zIGFyZQoKfCBQcmVkaWNhdGUgICAgICAgfCBDb2Vyc2lvbnwKfDotLS0tLS0tLS0tLS0tLS0tfDotLS0tLS0tLS0tLS0tLS0tLXwKfCBgaXMubnVtZXJpY2AgICB8IGBhcy5udW1lcmljYCAgICB8CnwgYGlzLmludGVnZXJgICAgfCBgYXMuaW50ZWdlcmAgICAgfAp8IGBpcy5jaGFyYWN0ZXJgIHwgYGFzLmNoYXJhY3RlcmAgIHwKfCBgaXMuZmFjdG9yYCAgICB8IGBhcy5mYWN0b3JgICAgICB8CnwgYGlzLm9yZGVyZWRgICAgfCBgYXMub3JkZXJlZGAgICAgfAoKPGRpdiBjbGFzcyA9ICJhbGVydCI+CkNvbnZlcnNpb24gb2YgZmFjdG9ycyB3aXRoIG51bWVyaWMtbG9va2luZyBsYWJlbHMgdG8gbnVtZXJpYyBkYXRhCnNob3VsZCBhbHdheXMgZ28gdGhyb3VnaCBgYXMuY2hhcmFjdGVyYCBmaXJzdC4KPC9kaXY+CgoKIyMgRGF0YSBGcmFtZXM6IE9yZ2FuaXppbmcgQ2FzZXMgYW5kIFZhcmlhYmxlcwoKVGFidWxhciBkYXRhIGluIFIgaXMgdXN1YWxseSBzdG9yZWQgYXMgYSBfZGF0YSBmcmFtZV8uCgpBIGRhdGEgZnJhbWUgaXMgYSBjb2xsZWN0aW9uIG9mIF92YXJpYWJsZXNfLCBlYWNoIHdpdGggYSB2YWx1ZSBmb3IKZXZlcnkgX2Nhc2VfIG9yIF9vYnNlcnZhY2lvbl8uCgpUaGUgYGZhaXRoZnVsYCBkYXRhIHNldCBpcyBhIGRhdGEgZnJhbWU6CgpgYGB7cn0KY2xhc3MoZmFpdGhmdWwpCm5hbWVzKGZhaXRoZnVsKQpgYGAKCk1vc3QgdG9vbHMgd2Ugd29yayB3aXRoIGluIFIgdXNlIGRhdGEgb3JnYW5pemVkIGluIGRhdGEgZnJhbWVzLgoKT3VyIGBwbG90KClgIGFuZCBgbG0oKWAgZXhwcmVzc2lvbnMgZnJvbSB0aGUKW2ludHJvZHVjdG9yeSBzZWN0aW9uXShpbnRyby5odG1sKQpjYW4gYWxzbyB3ZSB3cml0dGVuIGFzCgpgYGB7ciwgZXZhbCA9IEZBTFNFfQpwbG90KHdhaXRpbmcgfiBlcnVwdGlvbnMsIGRhdGEgPSBmYWl0aGZ1bCwKICAgICB4bGFiID0gIkVydXB0aW9uIHRpbWUgKG1pbikiLAogICAgIHlsYWIgPSAiV2FpdGluZyB0aW1lIHRvIG5leHQgZXJ1cHRpb24gKG1pbikiKQpmaXQgPC0gbG0od2FpdGluZyB+IGVydXB0aW9ucywgZGF0YSA9IGZhaXRoZnVsKQpgYGAKCjxkaXYgY2xhc3MgPSAiYWxlcnQiPgpgcGxvdCgpYCBvbmx5IHVzZXMgdGhlIGBkYXRhYCBhcmd1bWVudCB3aGVuIHRoZSBwbG90IGlzIHNwZWNpZmllZCBhcyBhCl9mb3JtdWxhXywgbGlrZQoKYGBgcgp3YWl0aW5nIH4gZXJ1cHRpb25zCmBgYAo8L2Rpdj4KCgojIyBFeGFtaW5pbmcgdGhlIERhdGEgaW4gYSBEYXRhIEZyYW1lCgpgaGVhZCgpYCBwcm92aWRlcyBhbiBpZGVhIG9mIHdoYXQgdGhlIHJhdyBkYXRhIGxvb2tzIGxpa2U6CmBgYHtyfQpoZWFkKGZhaXRoZnVsKQpgYGAKCmBzdHIoKWAgaXMgYWxzbyB1c2VmdWwgZm9yIGFuIG92ZXJ2aWV3IG9mIGFuIG9iamVjdCdzIHN0cnVjdHVyZToKYGBge3J9CnN0cihmYWl0aGZ1bCkKYGBgCgpBbm90aGVyIHVzZWZ1bCBmdW5jdGlvbiBhdmFpbGFibGUgZnJvbSB0aGUgYGRwbHlyYCBvciBgdGliYmxlYApwYWNrYWdlcyBpcyBgZ2xpbXBzZSgpYDoKCmBgYHtyfQpsaWJyYXJ5KGRwbHlyKQpnbGltcHNlKGZhaXRoZnVsKQpgYGAKCmBzdW1tYXJ5KClgIHNob3dzIGJhc2ljIHN0YXRpc3RpY2FsIHByb3BlcnRpZXMgb2YgdGhlIHZhcmlhYmxlczoKCmBgYHtyfQpzdW1tYXJ5KGZhaXRoZnVsKQpgYGAKCmBzdW1tYXJ5KClgIHdpdGggYSBjaGFyYWN0ZXIgdmFyaWFibGUgYW5kIGEgZmFjdG9yIHZhcmlhYmxlOgoKYGBge3J9CmZmbCA8LSBtdXRhdGUoZmFpdGhmdWwsCiAgICAgICAgICAgICAgdHlwZSA9IGlmZWxzZShlcnVwdGlvbnMgPCAzLCAic2hvcnQiLCAibG9uZyIpLAogICAgICAgICAgICAgIGZ0eXBlID0gZmFjdG9yKHR5cGUpKQpzdW1tYXJ5KGZmbCkKYGBgCgoKIyMgVmFyaWFibGVzIGluIGEgRGF0YSBGcmFtZQoKQSBEYXRhIGZyYW1lIGlzIGEgbGlzdCwgb3IgdmVjdG9yLCBvZiB2YXJpYWJsZXM6CmBgYHtyfQpsZW5ndGgoZmFpdGhmdWwpCmBgYAoKVGhlIGRvbGxhciBzaWduIGAkYCBjYW4gYmUgdXNlZCB0byBleGFtaW5lIGluZGl2aWR1YWwgdmFyaWFibGVzOgpgYGB7cn0KY2xhc3MoZmFpdGhmdWwkZXJ1cHRpb25zKQpjbGFzcyhmYWl0aGZ1bCR3YWl0aW5nKQpgYGAKClRoZSB2YXJpYWJsZXMgY2FuIGFsc28gYmUgZXh0cmFjdGVkIGJ5IG51bWVyaWNhbCBvciBjaGFyYWN0ZXIgaW5kZXggdXNpbmcgdGhlCiAgZWxlbWVudCBleHRyYWN0aW9uIG9wZXJhdGlvbiBgW1tgOgpgYGB7cn0KY2xhc3MoZmFpdGhmdWxbWzFdXSkKY2xhc3MoZmFpdGhmdWxbWyJ3YWl0aW5nIl1dKQpgYGAKCgojIyBEaW1lbnNpb25zCgpUaGUgbnVtYmVycyBvZiByb3dzIGFuZCBjb2x1bW5zIGNhbiBiZSBvYnRhaW5lZCB1c2luZyBgbnJvdygpYCBhbmQgYG5jb2woKWA6CgpgYGB7cn0KbmNvbChmYWl0aGZ1bCkKbnJvdyhmYWl0aGZ1bCkKYGBgCgpgZGltKClgIHJldHVybnMgYSB2ZWN0b3Igb2YgdGhlIGRpbWVuc2lvbnM6CgpgYGB7cn0KZGltKGZhaXRoZnVsKQpgYGAKCgojIyBTaW1wbGUgVmlzdWFsaXphdGlvbnMKCmBwbG90YCBoYXMgYSBfbWV0aG9kXyBmb3IgZGF0YSBmcmFtZXMgdGhhdCB0cmllcyB0byBwcm92aWRlIGEKcmVhc29uYWJsZSBkZWZhdWx0IHZpc3VhbGl6YXRpb24gZm9yIG51bWVyaWMgZGF0YSBmcmFtZXM6CmBgYHtyfQpwbG90KGZhaXRoZnVsKQpgYGAKCgojIyBTYW1wbGUgRGF0YSBTZXRzCgpUaGUKW2BkYXRhc2V0c2BdKGh0dHA6Ly9zdGF0LmV0aHouY2gvUi1tYW51YWwvUi1kZXZlbC9saWJyYXJ5L2RhdGFzZXRzL2h0bWwvMDBJbmRleC5odG1sKQpwYWNrYWdlIGluIHRoZSBiYXNlIFIgZGlzdHJpYnV0aW9uIGNvbnRhaW5zIGEgbnVtYmVyIG9mIGRhdGEgc2V0cyB5b3UKY2FuIGV4cGxvcmUuCgpBbm90aGVyIHBhY2thZ2Ugd2l0aCBhIHVzZWZ1bCBjb2xsZWN0aW9uIG9mIGRhdGEgc2V0cyBpcyBbYGRzbGFic2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9ZHNsYWJzKS4KCk1hbnkgb3RoZXIgZGF0YSBzZXRzIGFyZSBjb250YWluZWQgaW4gY29udHJpYnV0ZWQgcGFja2FnZXMgYXMgZXhhbXBsZXMuCgpUaGVyZSBhcmUgYWxzbyBtYW55IGNvbnRyaWJ1dGVkIHBhY2thZ2VzIGRlc2lnbmVkIHNwZWNpZmljYWxseSBmb3IKbWFraW5nIHBhcnRpY3VsYXIgZGF0YSBzZXRzIGF2YWlsYWJsZS4KCgojIyBUaWR5IERhdGEKClRoZSB1c2VmdWwgY29uY2VwdCBvZiBhIF90aWR5XyBkYXRhIGZyYW1lIHdhcyBpbnRyb2R1Y2VkIGZhaXJseQpbcmVjZW50bHldKGh0dHBzOi8vd3d3LmpzdGF0c29mdC5vcmcvYXJ0aWNsZS92aWV3L3YwNTlpMTApIGFuZCBpcwpkZXNjcmliZWQgaXMgYSBbY2hhcHRlciBpbiBfUiBmb3IgRGF0YQpTY2llbmNlX10oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei90aWR5LWRhdGEuaHRtbCkuCgpUaGUgaWRlYSBpcyB0aGF0CgoqIGV2ZXJ5IG9ic2VydmF0aW9uIHNob3VsZCBjb3JyZXNwb25kIHRvIGEgc2luZ2xlIHJvdzsKKiBldmVyeSB2YXJpYWJsZSBzaG91bGQgY29ycmVzcG9uZCB0byBhIHNpbmdsZSBjb2x1bW4uCgpUaWR5IGRhdGEgaXMgY29tcHV0YXRpb25hbGx5IGNvbnZlbmllbnQsIGFuZCBtYW55IG9mIHRoZSB0b29scyB3ZSB3aWxsCnVzZSBhcmUgZGVzaWduZWQgYXJvdW5kIHRpZHkgZGF0YSBmcmFtZXMuCgpBIGxhcmdlIHJhbmdlIG9mIHRoZXNlIHRvb2xzIGNhbiBiZSBhY2Nlc3NlZCBieSBsb2FkaW5nIHRoZQpgdGlkeXZlcnNlYCBwYWNrYWdlOgoKYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKQnV0IGZvciBub3cgSSB3aWxsIGxvYWQgdGhlIG5lZWRlZCBwYWNrYWdlcyBpbmRpdmlkdWFsbHkuCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KVGhlIHRlcm0gX3RpZHlfIGlzIGEgbGl0dGxlIHVuZm9ydHVuYXRlLgoKKiBEYXRhIHRoYXQgaXMgbm90IF90aWR5XyBpc24ndCBuZWNlc3NhcmlseSBfYmFkXy4KKiBGb3IgaHVtYW4gcmVhZGluZywgYW5kICBmb3Igc29tZSBjb21wdXRhdGlvbnMsIGRhdGEgaW4gYSB3aWRlciBmb3JtYXQgY2FuCiAgYmUgYmV0dGVyLgoqIEZvciBvdGhlciBjb21wdXRhdGlvbnMgZGF0YSBpbiBhIGxvbmdlciwgb3IgbmFycm93ZXIsIGZvcm1hdCBjYW4gYmUgYmV0dGVyLgo8L2Rpdj4KCgojIyBUaWJibGVzCgpNYW55IHRvb2xzIGluIHRoZSBgdGlkeXZlcnNlYCBwcm9kdWNlIHNsaWdodGx5IGVuaGFuY2VkIGRhdGEgZnJhbWVzCmNhbGxlZCBfdGliYmxlc186CgpgYGB7cn0KbGlicmFyeSh0aWJibGUpCmZhaXRoZnVsX3RibCA8LSBhc190aWJibGUoZmFpdGhmdWwpCmNsYXNzKGZhaXRoZnVsX3RibCkKYGBgClRpYmJsZXMgcHJpbnQgZGlmZmVyZW50bHkgZnJvbSBzdGFuZGFyZCBkYXRhIGZyYW1lczoKCmBgYHtyLCBoaWdobGlnaHQub3V0cHV0PWMoMSwgMywgMTQpfQpmYWl0aGZ1bF90YmwKYGBgCgpGb3IgdGhlIG1vc3QgcGFydCBkYXRhIGZyYW1lcyBhbmQgdGliYmxlcyBjYW4gYmUgdXNlZCBpbnRlcmNoYW5nZWFibHkuCgoKIyMgVGlkeWluZyBEYXRhCgpNYW55IGRhdGEgc2V0cyBhcmUgaW4gdGlkeSBmb3JtIGFscmVhZHkuCgpJZiB0aGV5IGFyZSBub3QsIHRoZXkgY2FuIGJlIHB1dCBpbnRvIHRpZHkgZm9ybS4KClRoZSB0b29scyBmb3IgdGhpcyBhcmUgcGFydCBvZiBfZGF0YSB0ZWNobm9sb2dpZXNfLgoKVGhlIHRhc2tzIGludm9sdmVkIGFyZSBwYXJ0IG9mIHdoYXQgaXMgc29tZXRpbWVzIGNhbGxlZCBfZGF0YQp3cmFuZ2xpbmdfLgoKCiMjIEFuIEV4YW1wbGU6IEdsb2JhbCBBdmVyYWdlIFN1cmZhY2UgVGVtcGVyYXR1cmVzCgpBbW9uZyBtYW55IGRhdGEgc2V0cyBhdmFpbGFibGUgYXQKPGh0dHBzOi8vZGF0YS5naXNzLm5hc2EuZ292L2dpc3RlbXAvPiBpcyBkYXRhIG9uIG1vbnRobHkgZ2xvYmFsCmF2ZXJhZ2Ugc3VyZmFjZSB0ZW1wZXJhdHVyZXMgb3ZlciBhIG51bWJlciBvZiB5ZWFycy4KClRoZXNlIGRhdGEgZnJvbSAyMDE3IHdlcmUgdXNlZCBmb3IgdGhlIHdpZGVseSBjaXRlZApbQmxvb21iZXJnIGhvdHRlc3QgeWVhciB2aXN1YWxpemF0aW9uXShodHRwczovL3d3dy5ibG9vbWJlcmcuY29tL2dyYXBoaWNzL2hvdHRlc3QteWVhci1vbi1yZWNvcmQvKS4KClRoZSBjdXJyZW50IGRhdGEgYXJlIGF2YWlsYWJsZSBpbiBhIGZvcm1hdHRlZCB0ZXh0IGZpbGUgYXQKCjxodHRwczovL2RhdGEuZ2lzcy5uYXNhLmdvdi9naXN0ZW1wL3RhYmxlZGF0YV92NC9HTEIuVHMrZFNTVC50eHQ+CgpvciBhcyBhCltfQ1NWXyAoY29tbWEtc2VwYXJhdGVkIHZhbHVlcyldKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0NvbW1hLXNlcGFyYXRlZF92YWx1ZXMpIGZpbGUgYXQKCjxodHRwczovL2RhdGEuZ2lzcy5uYXNhLmdvdi9naXN0ZW1wL3RhYmxlZGF0YV92NC9HTEIuVHMrZFNTVC5jc3Y+CgpgYGB7ciBkb3dubG9hZC1HTEIsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGUgZmlyc3QgZmV3IGxpbmVzIG9mIHRoZSBDU1YgZmlsZToKCmBgYHtyfQojfCBlY2hvOiBmYWxzZQojfCBjb21tZW50OiAiICAgIgpyZWFkTGluZXMoIkdMQi5UcytkU1NULmNzdiIsIDYpIHw+IHdyaXRlTGluZXMoKQpgYGAKClRoZSBDU1YgZmlsZSBpcyBhIGxpdHRsZSBlYXNpZXIgKGZvciBhIGNvbXB1dGVyIHByb2dyYW0pIHRvIHJlYWQgaW4sCnNvIHdlIHdpbGwgd29yayB3aXRoIHRoYXQuCgpUaGUgbnVtYmVycyBpbiB0aGUgQ1NWIGZpbGUgcmVwcmVzZW50IGRldmlhdGlvbnMgaW4gZGVncmVlcyBDZWxjaXVzCmZyb20gdGhlIGF2ZXJhZ2UgdGVtcGVyYXR1cmUgZm9yIHRoZSBiYXNlIHBlcmlvZCAxOTUxLTE5ODAuCgpUaGUgZmlsZSBhdmFpbGFibGUgb24gSmFudWFyeSAxOSwgMjAyMywgaXMgbm93IGF2YWlsYWJsZQpbbG9jYWxseV0oaHR0cHM6Ly9zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL0dMQi5UcytkU1NULmNzdikuCgpXZSBjYW4gbWFrZSBzdXJlIGl0IGhhcyBiZWVuIGRvd25sb2FkZWQgdG8gb3VyIHdvcmtpbmcgZGlyZWN0b3J5IHdpdGgKCmBgYHtyIGRvd25sb2FkLUdMQn0KaWYgKCEgZmlsZS5leGlzdHMoIkdMQi5UcytkU1NULmNzdiIpKQogICAgZG93bmxvYWQuZmlsZSgiaHR0cHM6Ly9zdGF0LnVpb3dhLmVkdS9+bHVrZS9kYXRhL0dMQi5UcytkU1NULmNzdiIsCiAgICAgICAgICAgICAgICAgICJHTEIuVHMrZFNTVC5jc3YiKQpgYGAKCkFzc3VtaW5nIHRoaXMgbG9jYWxseSBhdmFpbGFibGUgZmlsZSBoYXMgYmVlbiBkb3dubG9hZGVkLCB3ZSBjYW4gcmVhZAppbiB0aGUgZGF0YSBhbmQgZHJvcCBzb21lIGNvbHVtbnMgd2UgZG9uJ3QgbmVlZCB3aXRoCgpgYGB7cn0KbGlicmFyeShyZWFkcikKZ2FzdCA8LSByZWFkX2NzdigiR0xCLlRzK2RTU1QuY3N2Iiwgc2tpcCA9IDEpWzEgOiAxM10KYGBgCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KKiBUaGUgZnVuY3Rpb24gYHJlYWRfY3N2YCBpcyBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2UsIHdoaWNoIGlzIHBhcnQgb2YgdGhlCiAgYHRpZHl2ZXJzZWAuCiogQW4gYWx0ZXJuYXRpdmUgaXMgdGhlIGJhc2UgUiBmdW5jdGlvbiBgcmVhZC5jc3ZgLgo8L2Rpdj4KCkEgbG9vayBhIHRoZSBmaXJzdCBmZXcgbGluZXM6CgpgYGB7cn0KaGVhZChnYXN0LCA1KQpgYGAKCkFuZCB0aGUgbGFzdCBmZXcgbGluZXM6CgpgYGB7cn0KdGFpbChnYXN0LCA1KQpgYGAKClRoZSBgcHJpbnQoKWAgbWV0aG9kIGZvciB0aWJibGVzIGFiYnJldmlhdGVzIHRoZSBvdXRwdXQuCgpJdCBpcyBuZWF0ZXIgYW5kIHByb3ZpZGVzIHNvbWUgdXNlZnVsIGFkZGl0aW9uYWwgaW5mb3JtYXRpb24gb24KdmFyaWFibGUgZGF0YSB0eXBlcy4KCkJ1dCBpdCBzaG93cyBvbmx5IHRoZSBmaXJzdCBmZXcgcm93cyBhbmQgbWF5IG5vdCBleHBsaWNpdGx5IHNob3cgc29tZQpjb2x1bW5zLgoKSWYgc29tZSBjb2x1bW5zIGFyZSBza2lwcGVkLCB5b3UgY2FuIGFzayB0byBzZWUgbW9yZSBieSBjYWxsaW5nCmBwcmludCgpYCBleHBsaWNpdGx5OgoKYGBge3J9CnByaW50KHRhaWwoZ2FzdCksIHdpZHRoID0gMTAwKQpgYGAKCjwhLS0KKiBUaGUgbGFzdCB2YWx1ZXMgaW4gdGhlIGBBdWdgIC0gYERlY2AgY29sdW1ucyBhcmUgbWlzc2luZyBhbmQgY29kZWQgYXMgYCoqKmAuCiogVGhpcyBjYXVzZXMgdGhlc2UgY29sdW1ucyB0byBiZSByZWFkIGFzIGNoYXJhY3RlciB2ZWN0b3JzIGluZGljYXRlZAogIGJ5IGA8Y2hyPmAuCiogVGhlIG90aGVycyBoYXZlIGJlZW4gcmVhZCBhcyBudW1lcmljLCBjb2RlZCBgPGRibD5gIChmb3IgCiAgX2RvdWJsZSBwcmVjaXNpb25fKS4KKiBXZSBjYW4gZml4IHRoZSB0aGVzZSBjb2x1bW5zIG5vdyBvciBkZWFsIHdpdGggdGhlbSBsYXRlci4KCk9uZSB3YXkgdG8gZml4IHRoZW0gbm93IGlzIHRvIHVzZSBgbXV0YXRlX2lmYDoKCmBgYHtyLCB3YXJuaW5nID0gRkFMU0V9Cmdhc3QgPC0gbXV0YXRlKGdhc3QsIGFjcm9zcyh3aGVyZShpcy5jaGFyYWN0ZXIpLCBhcy5udW1lcmljKSkKaGVhZChnYXN0KQpgYGAKCkVhY2ggb2JzZXJ2YXRpb24gY29uc2lzdHMgb2YgYSB5ZWFyLCBhIG1vbnRoLCBhbmQgYSB0ZW1wZXJhdHVyZS4KLS0+CgpUaGUgZm9ybWF0IHdpdGggb25lIGNvbHVtbiBwZXIgbW9udGggaXMgY29tcGFjdCBhbmQgdXNlZnVsIGZvciB2aWV3aW5nCmFuZCBkYXRhIGVudHJ5LgoKQnV0IGl0IGlzIG5vdCBpbiBfdGlkeSBmb3JtYXRfIHNpbmNlCgoqIHRoZSBtb250aGx5IHRlbXBlcmF0dXJlICB2YXJpYWJsZSBpcyBzcHJlYWQgb3ZlciAxMiBjb2x1bW5zOwoqIHRoZSBtb250aCB2YXJpYWJsZSBpcyBlbmNvZGVkIGluIHRoZSBjb2x1bW4gaGVhZGluZ3MuCgpGb3Igb2J2aW91cyByZWFzb25zIHRoaXMgZGF0YSBmb3JtYXQgaXMgb2Z0ZW4gcmVmZXJyZWQgdG8gYXMgX3dpZGUKZm9ybWF0Xy4KClRoZSB0aWR5LCBvciBfbG9uZ18sIGZvcm1hdCB3b3VsZCBoYXZlIHRocmVlIHZhcmlhYmxlczogYFllYXJgLApgTW9udGhgLCBhbmQgYFRlbXBgLgoKT25lIHdheSB0byBwdXQgdGhpcyBkYXRhIGZyYW1lIGluIHRpZHksIG9yIGxvbmcsIGZvcm1hdCB1c2VzIGBwaXZvdF9sb25nZXJgCmZyb20gdGhlIGB0aWR5cmAgcGFja2FnZToKCmBgYHtyfQpsaWJyYXJ5KHRpZHlyKQpsZ2FzdCA8LSBwaXZvdF9sb25nZXIoZ2FzdCwKICAgICAgICAgICAgICAgICAgICAgIC1ZZWFyLCAgIyMgc3BlY2lmaWVzIHRoZSBjb2x1bW5zIHRvIHVzZSAtLSBhbGwgYnV0IFllYXIKICAgICAgICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIk1vbnRoIiwKICAgICAgICAgICAgICAgICAgICAgIHZhbHVlc190byA9ICJUZW1wIikKYGBgCgpUaGUgZmlyc3QgZmV3IHJvd3Mgb2YgdGhlIHJlc3VsdDoKCmBgYHtyfQpoZWFkKGxnYXN0KQpgYGAKCkR1cmluZyBwbG90dGluZyBpdCBpcyBsaWtlbHkgdGhhdCB0aGUgYE1vbnRoYCB2YXJpYWJsZSB3aWxsIGJlCmNvbnZlcnRlZCB0byBhIF9mYWN0b3JfLgoKQnkgZGVmYXVsdCwgdGhpcyB3aWxsIG9yZGVyIGxldmVscyBhbHBoYWJldGljYWxseSwgd2hpY2ggaXMgbm90IHdoYXQgd2Ugd2FudDoKCmBgYHtyfQpsZXZlbHMoYXMuZmFjdG9yKGxnYXN0JE1vbnRoKSkKYGBgCgpXZSBjYW4gZ3VhcmQgYWdhaW5zdCB0aGlzIGJ5IGNvbnZlcnRpbmcgYE1vbnRoYCB0byBhIGZhY3RvciB3aXRoIHRoZQpyaWdodCBsZXZlbHMgbm93OgoKYGBge3J9CmxnYXN0IDwtIG11dGF0ZShsZ2FzdCwgTW9udGggPSBmYWN0b3IoTW9udGgsIGxldmVscyA9IG1vbnRoLmFiYikpCmxldmVscyhsZ2FzdCRNb250aCkKYGBgCgpXZSBjYW4gbm93IHVzZSB0aGlzIHRpZHkgdmVyc2lvbiBvZiB0aGUgZGF0YSB0byBjcmVhdGUgYSBzdGF0aWMKdmVyc2lvbiBvZiB0aGUgW0Jsb29tYmVyZyBob3R0ZXN0IHllYXIKdmlzdWFsaXphdGlvbl0oaHR0cHM6Ly93d3cuYmxvb21iZXJnLmNvbS9ncmFwaGljcy9ob3R0ZXN0LXllYXItb24tcmVjb3JkLykuCgpUaGUgYmFzaWMgZnJhbWV3b3JrIGlzIHNldCB1cCB3aXRoCgpgYGB7cn0KbGlicmFyeShnZ3Bsb3QyKQpwIDwtIGdncGxvdChsZ2FzdCkgKwogICAgZ2d0aXRsZSgiR2xvYmFsIEF2ZXJhZ2UgU3VyZmFjZSBUZW1wZXJhdHVyZXMiKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41KSkKYGBgCgo8ZGl2IGNsYXNzID0gImFsZXJ0Ij4KYGdncGxvdGAgb2JqZWN0cyBvbmx5IHByb2R1Y2Ugb3V0cHV0IHdoZW4gdGhleSBhcmUgcHJpbnRlZC4KClRvIHNlZSB0aGUgcGxvdCBpbiBgcGAgd2UgbmVlZCB0byBwcmludCBpdCwgZm9yIGV4YW1wbGUgYnkgdXNpbmcgYQpsaW5lIGxpbmUgd2l0aCBvbmx5IGBwYC4KPC9kaXY+CgpgYGB7ciBnYXN0LWJhc2UsIGV2YWwgPSBGQUxTRX0KIHAKYGBgCmBgYHtyIGdhc3QtYmFzZSwgZXZhbCA9IFRSVUUsIGVjaG8gPSBGQUxTRX0KYGBgCgpUaGVuIGFkZCBhIGxheWVyIHdpdGggbGluZXMgZm9yIGVhY2ggeWVhciAoc3BlY2lmaWVkIGJ5IHRoZSBgZ3JvdXBgCmFyZ3VtZW50IHRvIGBnZW9tX2xpbmVgKS4KCmBgYHtyIGdhc3QtbGluZXMsIGV2YWwgPSBGQUxTRX0KcCArIGdlb21fbGluZShhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyKSkKYGBgCmBgYHtyIGdhc3QtbGluZXMsIGV2YWwgPSBUUlVFLCBlY2hvID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KYGBgCgpXZSBjYW4gdXNlIGNvbG9yIHRvIGRpc3Rpbmdpc2ggdGhlIHllYXJzLgoKYGBge3IgZ2FzdC1jb2xvcjEsIGV2YWwgPSBGQUxTRX0KcDEgPC0gcCArCiAgICBnZW9tX2xpbmUoYWVzKHggPSBNb250aCwKICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgIGNvbG9yID0gWWVhciwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyKSwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCnAxCmBgYApTYXZpbmcgdGhlIHBsb3Qgc3BlY2lmaWNhdGlvbiBpbiB0aGUgdmFyaWFibGUgYHAxYCB3aWxsIG1ha2UgaXQgZWFzaWVyCnRvIGV4cGVyaW1lbnQgd2l0aCBjb2xvciB2YXJpYXRpb25zOgoKYGBge3IgZ2FzdC1jb2xvcjEsIGV2YWwgPSBUUlVFLCBlY2hvID0gRkFMU0V9CmBgYAoKYGBge3IsIGluY2x1ZGUgPSBGQUxTRX0KIyMgQ29tcHV0ZSB0aGUgcGFzdCB5ZWFyIGFuZCBtYWtlIHN1cmUgaXQgaXMgaW4gdGhlIGZpbGUKbGlicmFyeShsdWJyaWRhdGUpCnBhc3RfeWVhciA8LSB5ZWFyKHRvZGF5KCkpIC0gMQpwYXN0X3llYXIKc3RvcGlmbm90KHBhc3RfeWVhciAlaW4lIGxnYXN0JFllYXIpCmBgYAoKT25lIHdheSB0byBoaWdobGlnaHQgdGhlIGBwYXN0X3llYXJgIGByIHBhc3RfeWVhcmA6CgpgYGB7ciBnYXN0LWNvbG9yMiwgZXZhbCA9IEZBTFNFfQpsZ2FzdF9sYXN0IDwtIGZpbHRlcihsZ2FzdCwgWWVhciA9PSBwYXN0X3llYXIpCnAxICsgZ2VvbV9saW5lKGFlcyh4ID0gTW9udGgsCiAgICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgIGdyb3VwID0gWWVhciksCiAgICAgICAgICAgICAgIGxpbmV3aWR0aCA9IDEsCiAgICAgICAgICAgICAgIGNvbG9yID0gInJlZCIsCiAgICAgICAgICAgICAgIGRhdGEgPSBsZ2FzdF9sYXN0LAogICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCmBgYApgYGB7ciBnYXN0LWNvbG9yMiwgZWNobyA9IEZBTFNFfQpgYGAKCkEgdXNlZnVsIHdheSB0byBzaG93IG1vcmUgcmVjZW50IGRhdGEgaW4gdGhlIGNvbnRleHQgb2YgdGhlIGZ1bGwgZGF0YQppcyB0byBzaG93IHRoZSBmdWxsIGRhdGEgaW4gZ3JleSBhbmQgdGhlIG1vcmUgcmVjZW50IHllYXJzIGluIGJsYWNrOgoKYGBge3IgZ2FzdC1mdWxsLWdyZXksIGV2YWwgPSBGQUxTRX0KbGdhc3QyayA8LSBmaWx0ZXIobGdhc3QsIFllYXIgPj0gMjAwMCkKZ2dwbG90KGxnYXN0LCBhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyKSkgKwogICAgZ2VvbV9saW5lKGNvbG9yID0gImdyZXk4MCIpICsKICAgIHRoZW1lX21pbmltYWwoKSArCiAgICBnZW9tX2xpbmUoZGF0YSA9IGxnYXN0MmspCmBgYApgYGB7ciBnYXN0LWZ1bGwtZ3JleSwgZWNobyA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CmBgYAoKSWYgeW91IHdhbnQgdG8gdXBkYXRlIHlvdXIgcGxvdCBsYXRlciBpbiB0aGUgeWVhciB0aGVuIHRoZSBjdXJyZW50CnllYXIncyBlbnRyeSBtYXkgY29udGFpbiBtaXNzaW5nIHZhbHVlIGluZGljYXRvcnMgdGhhdCB5b3Ugd2lsbCBoYXZlCnRvIGRlYWwgd2l0aC4KPCEtLQpUaGUgdmVyc2lvbiBvZiB0aGUgZGF0YSBbb24gdGhlCndlYl0oaHR0cHM6Ly9kYXRhLmdpc3MubmFzYS5nb3YvZ2lzdGVtcC90YWJsZWRhdGFfdjMvR0xCLlRzK2RTU1QudHh0KQptYXkgaGF2ZSBiZWVuIHVwZGF0ZWQgdG8gaW5jbHVkZSB0aGUgbWlzc2luZyB2YWx1ZXMgZm9yIGByIHBhc3RfeWVhcmAuCklmIHlvdSB3YW50IHRvIHVwZGF0ZSB5b3VyIHBsb3QgbGF0ZXIgaW4gdGhlIHllYXIgeW91IHdpbGwgc2VlCnNpbWlsYXIgbWlzc2luZyB2YWx1ZSBtYXJrZXJzIGZvciB0aGUgcmVtYWluaW5nIG1vbnRocyBvZgpgciAocGFzdF95ZWFyICsgMSlgLgotLT4KClRoZSBOZXcgWW9yayBUaW1lcyBvbiBKYW51YXJ5IDE4LCAyMDE4LCBwdWJsaXNoZWQKW2Fub3RoZXIgdmlzdWFsaXphdGlvbl0oaHR0cHM6Ly93d3cubnl0aW1lcy5jb20vaW50ZXJhY3RpdmUvMjAxOC8wMS8xOC9jbGltYXRlL2hvdHRlc3QteWVhci0yMDE3Lmh0bWwpCm9mIHRoZXNlIGRhdGEgc2hvd2luZyBhdmVyYWdlIHllYXJseSB0ZW1wZXJhdHVyZXMgKFt2aWEgR29vZ2xlIG1heSB3b3JrIGJldHRlcl0oaHR0cHM6Ly93d3cuZ29vZ2xlLmNvbS91cmw/c2E9dCZyY3Q9aiZxPSZlc3JjPXMmc291cmNlPXdlYiZjZD0mY2FkPXJqYSZ1YWN0PTgmdmVkPTJhaFVLRXdqXzRQbWhuTTMxQWhYRmpZa0VIZlF4QVRFUUZub0VDQVlRQVEmdXJsPWh0dHBzJTNBJTJGJTJGd3d3Lm55dGltZXMuY29tJTJGaW50ZXJhY3RpdmUlMkYyMDE4JTJGMDElMkYxOCUyRmNsaW1hdGUlMkZob3R0ZXN0LXllYXItMjAxNy5odG1sJnVzZz1BT3ZWYXczcW91eEhRQUpDcXJ0X0YzZmJMRUdFKSkuCgpUbyByZWNyZWF0ZSB0aGlzIHBsb3Qgd2UgZmlyc3QgbmVlZCB0byBjb21wdXRlIHllYXJseSBhdmVyYWdlIHRlbXBlcmF0dXJlcy4KClRoaXMgaXMgZWFzeSB0byBkbyB3aXRoIHRoZSBgc3VtbWFyaXplYCBhbmQgYGdyb3VwX2J5YCBmdW5jdGlvbnMgZnJvbSBgZHB5cmA6CgpgYGB7cn0KbGlicmFyeShkcGx5cikKYXRlbXAgPC0gbGdhc3QgJT4lCiAgICBncm91cF9ieShZZWFyKSAlPiUKICAgIHN1bW1hcml6ZShBdmVUZW1wID0gbWVhbihUZW1wLCBuYS5ybSA9IFRSVUUpKQpoZWFkKGF0ZW1wKQpgYGAKClVzaW5nIGBuYS5ybSA9IFRSVUVgIGVuc3VyZXMgdGhhdCB0aGUgbWVhbiBpcyBiYXNlZCBvbiB0aGUgYXZhaWxhYmxlCm1vbnRocyBpZiBkYXRhIGZvciBzb21lIG1vbnRocyBpcyBtaXNzaW5nLgoKQSBzaW1wbGUgdmVyc2lvbiBvZiB0aGUgcGxvdCBpcyB0aGVuIHByb2R1Y2VkIGJ5CgpgYGB7ciBnYXN0LW55dCwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoYXRlbXApICsKICAgIGdlb21fcG9pbnQoYWVzKHggPSBZZWFyLCB5ID0gQXZlVGVtcCkpCmBgYApgYGB7ciBnYXN0LW55dCwgZWNobyA9IEZBTFNFfQpgYGAKCkFub3RoZXIgdmFyaWF0aW9uIG9uIHRoZSBCbG9vbWJlcmcgcGxvdCBzaG93aW5nIGp1c3QgYSBmZXcgeWVhcnMgMjAKeWVhcnMgYXBhcnQ6CgpgYGB7ciBnYXN0LXNraXAsIGV2YWwgPSBGQUxTRX0KbGdfYnlfMjAgPC0KICAgIGZpbHRlcihsZ2FzdCwKICAgICAgICAgICBZZWFyICVpbiUgc2VxKDIwMjAsIGJ5ID0gLTIwLCBsZW4gPSA1KSkgJT4lCiAgICBtdXRhdGUoWWVhciA9IGZhY3RvcihZZWFyKSkKZ2dwbG90KGxnX2J5XzIwLCBhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICAgICB5ID0gVGVtcCwKICAgICAgICAgICAgICAgICAgICAgZ3JvdXAgPSBZZWFyLAogICAgICAgICAgICAgICAgICAgICBjb2xvciA9IFllYXIpKSArCiAgICBnZW9tX2xpbmUoKQpgYGAKQ29udmVydGluZyBgWWVhcmAgdG8gYSBmYWN0b3IgcmVzdWx0cyBpbiBhIGRpc2NyZXRlIGNvbG9yIHNjYWxlIGFuZCBsZWdlbmQuCmBgYHtyIGdhc3Qtc2tpcCwgZWNobyA9IEZBTFNFfQpgYGAKCgojIyBIYW5kbGluZyBNaXNzaW5nIFZhbHVlcwoKVGhlIGRhdGEgZm9yIDIwMTkgYXZhaWxhYmxlIGluIGVhcmx5IDIwMjAgaXMgYWxzbyBhdmFpbGFibGUKW2xvY2FsbHldKGh0dHBzOi8vc3RhdC51aW93YS5lZHUvfmx1a2UvZGF0YS9HTEIuVHMrZFNTVC0yMDE5LmNzdikuCgpgYGB7ciwgZWNobyA9IEZBTFNFfQppZiAoISBmaWxlLmV4aXN0cygiR0xCLlRzK2RTU1QtMjAxOS5jc3YiKSkKICAgIGRvd25sb2FkLmZpbGUoImh0dHBzOi8vc3RhdC51aW93YS5lZHUvfmx1a2UvZGF0YS9HTEIuVHMrZFNTVC0yMDE5LmNzdiIsCiAgICAgICAgICAgICAgICAgICJHTEIuVHMrZFNTVC0yMDE5LmNzdiIpCmBgYAoKQXNzdW1pbmcgdGhpcyBsb2NhbGx5IGF2YWlsYWJsZSBmaWxlIGhhcyBiZWVuIGRvd25sb2FkZWQsIHdlIGNhbiByZWFkCmluIHRoZSBkYXRhIGFuZCBkcm9wIHNvbWUgY29sdW1ucyB3ZSBkb24ndCBuZWVkIHdpdGgKCmBgYHtyfQpnYXN0MjAxOSA8LSByZWFkX2NzdigiR0xCLlRzK2RTU1QtMjAxOS5jc3YiLCBza2lwID0gMSlbMSA6IDEzXQpgYGAKClRoZSBsYXN0IHRocmVlIGNvbHVtbnMgYXJlIHJlYWQgYXMgY2hhcmFjdGVyIHZhcmlhYmxlczoKCmBgYHtyfQpoZWFkKGdhc3QyMDE5LCA1KQpgYGAKClRoZSByZWFzb24gaXMgdGhhdCBkYXRhIGZvciBPY3RvYmVyIHRocm91Z2ggRGVjZW1iZXIgd2VyZSBub3QKYXZhaWxhYmxlOgoKYGBge3J9CnRhaWwoZ2FzdDIwMTksIDIpCmBgYAoKV2Ugd2FudCB0byBjb252ZXJ0IHRoZXNlIGNvbHVtbnMgdG8gbnVtZXJpYywgd2l0aCBtaXNzaW5nIHZhbHVlcwpyZXByZXNlbnRlZCBhcyBgTkFgLgoKT25lIG9wdGlvbiBpcyB0byBoYW5kbGUgdGhlbSBpbmRpdmlkdWFsbHk6CgpgYGB7cn0KZ2FzdDIwMTkkT2N0IDwtIGFzLm51bWVyaWMoZ2FzdDIwMTkkT2N0KQp0YWlsKGdhc3QyMDE5LCAyKQpgYGAKCkFub3RoZXIgb3B0aW9uIGlzIHRvIGNvbnZlcnQgYWxsIGNoYXJhY3RlciBjb2x1bW5zIHRvIG51bWVyaWMgd2l0aAoKYGBge3J9Cmdhc3QyMDE5IDwtIG11dGF0ZShnYXN0MjAxOSwgYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIGFzLm51bWVyaWMpKQp0YWlsKGdhc3QyMDE5LCAyKQpgYGAKClRoZSB3YXJuaW5ncyBhcmUgYmVuaWduIGFuZCBjYW4gYmUgc3VwcHJlc3NlZCB3aXRoIHRoZSBgd2FybmluZyA9CkZBTFNFYCBjaHVuayBvcHRpb24uCgpTaW5jZSB3ZSBrbm93IHRoZSBtaXNzaW5nIHZhbHVlIHBhdHRlcm4gYCoqKmAgd2UgY2FuIGFsc28gYXZvaWQgdGhlCm5lZWQgdG8gZml4IHRoZSBkYXRhIGFmdGVyIHRoZSBmYWN0IGJ5IHNwZWNpZnlpbmcgdGhpcyBhdCByZWFkIHRpbWU6CgpgYGB7cn0KZ2FzdDIwMTkgPC0gcmVhZF9jc3YoIkdMQi5UcytkU1NULTIwMTkuY3N2IiwgbmEgPSAiKioqIiwgc2tpcCA9IDEpWzEgOiAxM10KdGFpbChnYXN0MjAxOSwgMikKYGBgCgpBIHBsb3QgaGlnaGxpZ2h0aW5nIHRoZSB5ZWFyIDIwMTkgc2hvd3Mgb25seSB0aGUgbW9udGhzIHdpdGggYXZhaWxhYmxlCmRhdGE6CgpgYGB7ciBnYXN0LTIwMTksIGV2YWwgPSBGQUxTRX0KbGdhc3QyMDE5IDwtIGdhc3QyMDE5ICU+JQogICAgcGl2b3RfbG9uZ2VyKC1ZZWFyLAogICAgICAgICAgICAgICAgIG5hbWVzX3RvID0gIk1vbnRoIiwKICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAiVGVtcCIpICU+JQogICAgbXV0YXRlKE1vbnRoID0gZmFjdG9yKE1vbnRoLCBsZXZlbHMgPSBtb250aC5hYmIpKQpnZ3Bsb3QobGdhc3QyMDE5LCBhZXMoeCA9IE1vbnRoLAogICAgICAgICAgICAgICAgICAgICAgeSA9IFRlbXAsCiAgICAgICAgICAgICAgICAgICAgICBncm91cCA9IFllYXIpKSArCiAgICBnZW9tX2xpbmUoY29sb3IgPSAiZ3JleTgwIiwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpICsKICAgIGdlb21fbGluZShkYXRhID0gZmlsdGVyKGxnYXN0MjAxOSwgWWVhciA9PSAyMDE5KSwKICAgICAgICAgICAgICBuYS5ybSA9IFRSVUUpCmBgYAoKQWRkaW5nIGBuYS5ybSA9IFRSVUVgIGluIHRoZSBgZ2VvbV9saW5lYCBjYWxscyBzdXBwcmVzc2VzIHdhcm5pbmdzOwp0aGUgcGxvdCB3b3VsZCBiZSB0aGUgc2FtZSB3aXRob3V0IHRoZXNlLgoKYGBge3IgZ2FzdC0yMDE5LCBlY2hvID0gRkFMU0V9CmBgYAoKPGRpdiBjbGFzcyA9ICJhbGVydCI+Ck91dGxpbmUgb2YgdGhlIHRvb2xzIHVzZWQ6CgoqIERhdGEgcHJvY2Vzc2luZzoKICAgICogUmVhZGluZzogYHJlYWRfY3N2YCwgYHJlYWQuY3N2YDsKICAgICogUmVzaGFwaW5nOiBgcGl2b3RfbG9uZ2VyYDsKICAgICogQ2xlYW5pbmc6IGBpcy5jaGFyYWN0ZXJgLCBgYXMubnVtZXJpY2AsIGBtdXRhdGVgLCBgYWNyb3NzYCwgYGZhY3RvcmA7CiAgICAqIFN1bW1hcml6aW5nOiBgZ3JvdXBfYnlgLCBgc3VtbWFyaXplYC4KKiBWaXN1YWxpemF0aW9uIGdlb21ldHJpZXM6CiAgICAqIGBnZW9tX2xpbmVgIGZvciBhIGxpbmUgcGxvdDsKICAgICogYGdlb21fcG9pbnRgIGZvciBhIHNjYXR0ZXIgcGxvdC4KPC9kaXY+CgoKIyMgUmVhZGluZwoKU3RldmVucycgY2xhc3NpZmljYXRpb24gb2Ygc2NhbGVzIG9mIG1lYXN1cmVtZW50IGlzIGRlc2NyaWJlZCBpbiBhCltXaWtpcGVkaWEKYXJ0aWNsZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvTGV2ZWxfb2ZfbWVhc3VyZW1lbnQpLgoKQSBnb29kIGludHJvZHVjdGlvbiB0byB0aGUgY29uY2VwdCBvZiBfdGlkeSBkYXRhXyBpcyBwcm92aWRlZCBpbiBhCltjaGFwdGVyIGluIF9SIGZvciBEYXRhClNjaWVuY2VfXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3RpZHktZGF0YS5odG1sKS4KCgojIyBJbnRlcmFjdGl2ZSBUdXRvcmlhbAoKQW4gaW50ZXJhY3RpdmUgW2BsZWFybnJgXShodHRwczovL3JzdHVkaW8uZ2l0aHViLmlvL2xlYXJuci8pIHR1dG9yaWFsCmZvciB0aGVzZSBub3RlcyBpcyBbYXZhaWxhYmxlXShgciBXTE5LKCJ0dXRvcmlhbHMvZGF0YWZybS5SbWQiKWApLgoKWW91IGNhbiBydW4gdGhlIHR1dG9yaWFsIHdpdGgKCmBgYHtyLCBldmFsID0gRkFMU0V9ClNUQVQ0NTgwOjpydW5UdXRvcmlhbCgiZGF0YWZybSIpCmBgYAoKCiMjIEV4ZXJjaXNlcwoKMS4gV2hpY2ggb2YgdGhlIFN0ZXZlbnMgY2xhc3NpZmljYXRpb25zIChub21pbmFsLCBvcmRpbmFsLCBpbnRlcnZhbCwgcmF0aW8pCiAgIGJlc3QgY2hhcmFjdGVyaXplcyB0aGVzZSB2YXJpYWJsZXM6CgogICAgYS4gRGFpbHkgbWF4aW1hbCB0ZW1wZXJhdHVyZXMgaW4gSW93YSBDaXR5LgogICAgYi4gUG9wdWxhdGlvbiBjb3VudHMgZm9yIElvd2EgY291bnRpZXMuCiAgICBjLiBFZHVjYXRpb24gbGV2ZWwgb2Ygam9iIGFwcGxpY2FudHMgdXNpbmcgdGhlIFtCdXJlYXUgb2YgTGFib3IKICAgICAgIFN0YXRpc3RpY3MKICAgICAgIGNsYXNzaWZpY2F0aW9uXShodHRwczovL3d3dy5ibHMuZ292L2NhcmVlcm91dGxvb2svMjAxNC9hcnRpY2xlL2VkdWNhdGlvbi1sZXZlbC1hbmQtam9icy5odG0pLgogICAgZC4gTWFqb3Igb2YgVUkgc3R1ZGVudHMuCgo8IS0tCldoaWNoIGFuc3dlcnMgYXJlIGNvcnJlY3QgZm9yIGV4ZXJjaXNlIDE6CiogYTogaW50ZXJ2YWw7IGI6IHJhdGlvOyBjOiBvcmRpbmFsOyBkOiBub21pbmFsCiAgYTogbm9taW5hbDsgYjogaW50ZXJ2YWw7IGM6IG9yZGluYWw7IGQ6IHJhdGlvCiAgYTogbm9taW5hbDsgYjogaW50ZXJ2YWw7IGM6IG9yZGluYWw7IGQ6IHJhdGlvCiAgYTogaW50ZXJ2YWw7IGI6IHJhdGlvOyBjOiBub21pbmFsOyBkOiBvcmRpbmFsCmZtdCA8LSBmdW5jdGlvbih4KQogICAgcGFzdGUobGV0dGVyc1tzZXFfYWxvbmcoeCldLCB4LCBzZXAgPSAiOiAiLCBjb2xsYXBzZSA9ICI7ICIpCnN0ZXYgPC0gYygibm9taW5hbCIsICJvcmRpbmFsIiwgImludGVydmFsIiwgInJhdGlvIikKZm10KHNhbXBsZShzdGV2LCA0KSkKLS0+CgoyLiBXaGljaCBvZiB0aGVzZSBkYXRhIHNldHMgYXJlIGluIHRpZHkgZm9ybT8KCiAgICBhLiBUaGUgYnVpbHRpbiBkYXRhIHNldCBgY28yYAogICAgYi4gVGhlIGJ1aWx0aW4gZGF0YSBzZXQgYEJPRGAKICAgIGMuIFRoZSBgd2hvYCBkYXRhIHNldCBpbiBwYWNrYWdlIGB0aWR5cmAgKGB0aWR5cjo6d2hvYCkKICAgIGQuIFRoZSBgbXBnYCBkYXRhIHNldCBpbiBwYWNrYWdlIGBnZ3Bsb3QyYCAoYGdncGxvdDI6Om1wZ2ApCgo8IS0tCldoaWNoIGFuc3dlcnMgYXJlIGNvcnJlY3QgZm9yIGV4ZXJjaXNlIDI6CiogYTogbm90IHRpZHk7IGI6IHRpZHk7IGM6IG5vdCB0aWR5OyBkOiB0aWR5CiogYTogdGlkeTsgYjogdGlkeTsgYzogbm90IHRpZHk7IGQ6IHRpZHkKKiBhOiBub3QgdGlkeTsgYjogbm90IHRpZHk7IGM6IG5vdCB0aWR5OyBkOiB0aWR5CiogYTogbm90IHRpZHk7IGI6IHRpZHk7IGM6IHRpZHk7IGQ6IG5vdCB0aWR5Ci0tPgoKVGhlIG5leHQgZXhlcmNpc2VzIHVzZSB0aGUgZGF0YSBpbiB0aGUgdmFyaWFibGUgYGdhcG1pbmRlcmAgaW4gdGhlIHBhY2thZ2UKYGdhcG1pbmRlcmAuIFlvdSBjYW4gbWFrZSBpdCBhdmFpbGFibGUgd2l0aAoKYGBge3IsIGV2YWwgPSBGQUxTRX0KZGF0YShnYXBtaW5kZXIsIHBhY2thZ2UgPSAiZ2FwbWluZGVyIikKYGBgCjMuIFVzZSB0aGUgZnVuY3Rpb24gYHN0cmAgdG8gZXhhbWluZSB0aGUgdmFsdWUgb2YgdGhlIGdhcG1pbmRlcgogICB2YXJpYWJsZS4gIEhvdyBtYW55IGNhc2VzIGFyZSB0aGVyZSBpbiB0aGUgZGF0YSBzZXQ/IEhvdyBtYW55IG9mCiAgIHRoZSB2YXJpYWJsZXMgYXJlIGZhY3RvcnM/Cgo0LiBVc2UgdGhlIGZ1bmN0aW9ucyBgY2xhc3NgIGFuZCBgbmFtZXNgIHRvIGZpbmQgdGhlIGNsYXNzIGFuZAogICB2YXJpYWJsZSBuYW1lcyBpbiB0aGUgYGdhcG1pbmRlcmAgZGF0YS4KCjUuIFVzZSBgc3VtbWFyeWAgdG8gY29tcHV0ZSBzdW1tYXJ5IGluZm9ybWF0aW9uIGZvciB0aGUgdmFyaWFibGVzLgoKNi4gRmlsbCBpbiB0aGUgdmFsdWVzIGZvciBgLS0tYCBuZWVkZWQgdG8gcHJvZHVjZSBwbG90cyBvZiBsaWZlCiAgIGV4cGVjdGFuY3kgYWdhaW5zdCB5ZWFyIGZvciB0aGUgY291bnRyaWVzIGluIGNvbnRpbmVudCBPY2VhbmlhLgo8IS0tICMjIG5vbGludCBzdGFydCAtLT4KYGBge3IsIGV2YWwgPSBGQUxTRX0KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQpkYXRhKGdhcG1pbmRlciwgcGFja2FnZSA9ICJnYXBtaW5kZXIiKQpnZ3Bsb3QoZmlsdGVyKGdhcG1pbmRlciwgY29udGluZW50ID09ICJPY2VhbmlhIiksCiAgICAgICBhZXMoeCA9IC0tLSwgeSA9IC0tLSwgY29sb3IgPSBjb3VudHJ5KSkgKwogICAgZ2VvbV9saW5lKCkKYGBgCjwhLS0gIyMgbm9saW50IGVuZCAtLT4K