1 Introduction

This RMarkdown document is part of a Workshop on Data Visualization with R. The material is based on A Layered Grammar of Graphics by Hadley Wickham. The intent is to build Skill in coding in R, and also appreciate R as a way to metaphorically visualize information of various kinds, using predominantly geometric figures and structures.

All RMarkdown files combine code, text, web-images, and figures developed using code. Everything is text; code chunks are enclosed in fences (```)

2 Goals

  • Understand different kinds of data variables
  • Appreciate how they can be identified based on the Interrogative Pronouns they answer to
  • Understand how each kind of variable lends itself to a specific geometric aspect in the data visualization.
  • Understand how ask Questions of Data to develop Visualizations

3 Pedagogical Note

The method followed will be based on PRIMM:

  • PREDICT Inspect the code and guess at what the code might do, write predictions
  • RUN the code provided and check what happens
  • INFER what the parameters of the code do and write comments to explain. What bells and whistles can you see?
  • MODIFY the parameters code provided to understand the options available. Write comments to show what you have aimed for and achieved.
  • MAKE : take an idea/concept of your own, and graph it.

In the following:

When it is YOUR TURN: wherever you see YOUR TURN, please respond with explanations, more questions and if you are already confident, code chunks to create new calculations and graphs.

4 Set Up

The setup code chunk below brings into our coding session R packages that provide specific computational abilities and also datasets which we can use.

To reiterate: Packages and datasets are not the same thing !! Packages are (small) collections of programs. Datasets are just….information.

4.1 Packages needed

knitr::opts_chunk$set(echo = TRUE,warning = TRUE)
library(tidyverse)
library(palmerpenguins)
library(janitor)

5 What Does Data Look Like?

In this RMarkdown document, we try to connect story-making questions with two ideas:

  1. a Variable in a dataset
  2. A computed Quantity / Descriptive Statistic or a Visual, based on one or more Variables

So: a question identifies a variable and a question also leads to a Computation or a Data Visualization. The idea is to get the intuition behind data, and iteratively ask the questions and form hypotheses and perform Exploratory Data Analysis (EDA) using graphs and charts in R.

At some point we may find that the data is not adequate to prove/disprove a particular hypothesis and need to get into further research / experimental design. It is possible to design the research experiments also in R, but we may cover that much later.

5.1 Interrogative Pronouns for Data Variables

So how do we ask questions? These are usually with interrogative pronouns in English: What? Who? Where? Which? What Kind? How? and so on.

5.2 The penguins dataset

names(penguins) # Column, i.e. Variable names
## [1] "species"           "island"            "bill_length_mm"   
## [4] "bill_depth_mm"     "flipper_length_mm" "body_mass_g"      
## [7] "sex"               "year"
head(penguins) # first six rows
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
Adelie Torgersen 39.1 18.7 181 3750 male 2007
Adelie Torgersen 39.5 17.4 186 3800 female 2007
Adelie Torgersen 40.3 18.0 195 3250 female 2007
Adelie Torgersen NA NA NA NA NA 2007
Adelie Torgersen 36.7 19.3 193 3450 female 2007
Adelie Torgersen 39.3 20.6 190 3650 male 2007
tail(penguins) # Last six rows
species island bill_length_mm bill_depth_mm flipper_length_mm body_mass_g sex year
Chinstrap Dream 45.7 17.0 195 3650 female 2009
Chinstrap Dream 55.8 19.8 207 4000 male 2009
Chinstrap Dream 43.5 18.1 202 3400 female 2009
Chinstrap Dream 49.6 18.2 193 3775 male 2009
Chinstrap Dream 50.8 19.0 210 4100 male 2009
Chinstrap Dream 50.2 18.7 198 3775 female 2009
dim(penguins) # Size of dataset
## [1] 344   8
# Check for missing data
any(is.na(penguins) == TRUE)
## [1] TRUE
  1. What are the variable names()?
  2. What would be the Question you might have asked to obtain each of the variables?
  3. What further questions/meta questions would you ask to “process” that variable? ( Hint: Add another word after any of the Interrogative Pronouns, e.g. How…MANY?)
  4. Where might the answers take your story?

5.3 YOUR TURN-1

State a few questions after discussion with your friend and state possible variables, or what you could DO with the variables, as an answer.
E.g. Q. How many penguins? A. We need to count…rows?

In the Table below, we have a rough mapping of interrogative pronouns to the kinds of variables in the data:

Pronoun Answer Variable/Scale Example What Operations?
How Many / Much / Heavy? Few? Seldom? Often? When? Quantities, with Scale and a Zero Value.Differences and Ratios /Products are meaningful. Quantitative/Ratio Length,Height,Temperature in Kelvin,Activity,Dose Amount,Reaction Rate,Flow Rate,Concentration,Pulse,Survival Rate Correlation
How Many / Much / Heavy? Few? Seldom? Often? When? Quantities with Scale. Differences are meaningful, but not products or ratios Quantitative/Interval pH,SAT score(200-800),Credit score(300-850),SAT score(200-800),Year of Starting College Mean,Standard Deviation
How, What Kind, What Sort A Manner / Method, Type or Attribute from a list, with list items in some ” order” ( e.g. good, better, improved, best..) Qualitative/Ordinal Socioeconomic status (Low income, Middle income, High income),Education level (HighSchool, BS, MS, PhD),Satisfaction rating(Very much Dislike, Dislike, Neutral, Like, Very Much Like) Median,Percentile
What, Who, Where, Whom, Which Name, Place, Animal, Thing Qualitative/Nominal Name Count no. of cases,Mode

As you go from Qualitative to Quantitative data types in the table, I hope you can detect a movement from fuzzy groups/categories to more and more crystallized numbers. Each variable/scale can be subjected to the operations of the previous group.

In the words of S.S.Stevens.:

the basic operations needed to create each type of scale is cumulative: to an operation listed opposite a particular scale must be added all those operations preceding it.

See his classic paper here PDF. Do think about this as you work with data.

Types of Data Variables

Do take a look at these references:

  1. https://stats.idre.ucla.edu/other/mult-pkg/whatstat/what-is-the-difference-between-categorical-ordinal-and-interval-variables/
  2. https://www.freecodecamp.org/news/types-of-data-in-statistics-nominal-ordinal-interval-and-ratio-data-types-explained-with-examples/

5.4 The mpg dataset

names(mpg) # Column, i.e. Variable names
##  [1] "manufacturer" "model"        "displ"        "year"         "cyl"         
##  [6] "trans"        "drv"          "cty"          "hwy"          "fl"          
## [11] "class"
head(mpg) # first six rows
manufacturer model displ year cyl trans drv cty hwy fl class
audi a4 1.8 1999 4 auto(l5) f 18 29 p compact
audi a4 1.8 1999 4 manual(m5) f 21 29 p compact
audi a4 2.0 2008 4 manual(m6) f 20 31 p compact
audi a4 2.0 2008 4 auto(av) f 21 30 p compact
audi a4 2.8 1999 6 auto(l5) f 16 26 p compact
audi a4 2.8 1999 6 manual(m5) f 18 26 p compact
tail(mpg) # Last six rows
manufacturer model displ year cyl trans drv cty hwy fl class
volkswagen passat 1.8 1999 4 auto(l5) f 18 29 p midsize
volkswagen passat 2.0 2008 4 auto(s6) f 19 28 p midsize
volkswagen passat 2.0 2008 4 manual(m6) f 21 29 p midsize
volkswagen passat 2.8 1999 6 auto(l5) f 16 26 p midsize
volkswagen passat 2.8 1999 6 manual(m5) f 18 26 p midsize
volkswagen passat 3.6 2008 6 auto(s6) f 17 26 p midsize
dim(mpg) # Size of dataset
## [1] 234  11
# Check for missing data
any(is.na(mpg) == TRUE)
## [1] FALSE

5.5 YOUR TURN-2

Look carefully at the variables here. How would you interpret say the cyl variable? Is it a number and therefore Quantitative, or could it be something else?

5.6 Reading External Data

A first task is often the reading in of external data. Data is best stored and shared in a CSV file format.Download this CSV file into your project folder:

To read this CSV data into our R Session, we use a command called read_csv from the readr package:

mpg_new <- readr::read_csv("mpg_uppercase.csv")
## Rows: 234 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (6): MANUFACTURER, MODEL, TRANS, DRV, FL, CLASS
## dbl (5): DISPL, YEAR, CYL, CTY, HWY
## 
## ℹ 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.

As can be seen , read_csv tells us by default what the column names are and the types they are: chr and dbl in this case.

In the event that the column names are not very good or evocative, we can set name_repair = make_clean_names inside read_csv; this additional function is available via the janitor package.

read_csv("mpg_uppercase.csv",
         show_col_types = TRUE,
         name_repair = make_clean_names) %>% # needs `janitor`
  glimpse()
## Rows: 234 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (6): manufacturer, model, trans, drv, fl, class
## dbl (5): displ, year, cyl, cty, hwy
## 
## ℹ 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.
## Rows: 234
## Columns: 11
## $ manufacturer <chr> "audi", "audi", "audi", "audi", "audi", "audi", "audi", "…
## $ model        <chr> "a4", "a4", "a4", "a4", "a4", "a4", "a4", "a4 quattro", "…
## $ displ        <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 1.8, 1.8, 2.0, 2.0, 2.…
## $ year         <dbl> 1999, 1999, 2008, 2008, 1999, 1999, 2008, 1999, 1999, 200…
## $ cyl          <dbl> 4, 4, 4, 4, 6, 6, 6, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, …
## $ trans        <chr> "auto(l5)", "manual(m5)", "manual(m6)", "auto(av)", "auto…
## $ drv          <chr> "f", "f", "f", "f", "f", "f", "f", "4", "4", "4", "4", "4…
## $ cty          <dbl> 18, 21, 20, 21, 16, 18, 18, 18, 16, 20, 19, 15, 17, 17, 1…
## $ hwy          <dbl> 29, 29, 31, 30, 26, 26, 27, 26, 25, 28, 27, 25, 25, 25, 2…
## $ fl           <chr> "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p…
## $ class        <chr> "compact", "compact", "compact", "compact", "compact", "c…

Note that the default naming is based in snake_case. There are other ways and if we desire to use them, we need to make make_clean_names as what is called a lambda function with extra parameters:

read_csv("mpg_uppercase.csv",
         show_col_types = TRUE,
         name_repair = 
           
      # Here is the lambda function
      # To be used only when you want to go beyond the defaults
      # using additional parameters. e.g `case`
           ~ make_clean_names(., 
                              case = "big_camel")) %>% 
  
  # needs `janitor`
  # `.` denotes the VECTOR of column names
  
  glimpse()
## Rows: 234 Columns: 11
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (6): Manufacturer, Model, Trans, Drv, Fl, Class
## dbl (5): Displ, Year, Cyl, Cty, Hwy
## 
## ℹ 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.
## Rows: 234
## Columns: 11
## $ Manufacturer <chr> "audi", "audi", "audi", "audi", "audi", "audi", "audi", "…
## $ Model        <chr> "a4", "a4", "a4", "a4", "a4", "a4", "a4", "a4 quattro", "…
## $ Displ        <dbl> 1.8, 1.8, 2.0, 2.0, 2.8, 2.8, 3.1, 1.8, 1.8, 2.0, 2.0, 2.…
## $ Year         <dbl> 1999, 1999, 2008, 2008, 1999, 1999, 2008, 1999, 1999, 200…
## $ Cyl          <dbl> 4, 4, 4, 4, 6, 6, 6, 4, 4, 4, 4, 6, 6, 6, 6, 6, 6, 8, 8, …
## $ Trans        <chr> "auto(l5)", "manual(m5)", "manual(m6)", "auto(av)", "auto…
## $ Drv          <chr> "f", "f", "f", "f", "f", "f", "f", "4", "4", "4", "4", "4…
## $ Cty          <dbl> 18, 21, 20, 21, 16, 18, 18, 18, 16, 20, 19, 15, 17, 17, 1…
## $ Hwy          <dbl> 29, 29, 31, 30, 26, 26, 27, 26, 25, 28, 27, 25, 25, 25, 2…
## $ Fl           <chr> "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p", "p…
## $ Class        <chr> "compact", "compact", "compact", "compact", "compact", "c…

Note the usage of ~ to make a function: this is needed only if we want to pass (additional) arguments to make_clean_names. (We will find this also when we encounter the purrr package where we run functions iteratively over each individual entry in a vector/column or list-column.)

6 Interrogations and Graphs

Now that we know how to access “R internal” datasets and to read in external datasets, we can also respond to ( more complex ) Questions, with not just a variable but one of two things:

  • A calculation, shown in a table
  • a data visualization. This visualization can even involve more than one variable, as we will see.

What sort of calculations, and visuals charts can we create with different kinds of variables, taken singly or together? Let us write some simple English descriptions of measures and visuals and see what commands they use in R.

Here we will use the Grammar of a package called ggplot, which we will encounter in Lab:04. Let us go with our intuition with the code in the following sections.

Note: since we saw a couple of missing entries in the penguins dataset, let us remove them for now.

penguins <- penguins %>% drop_na()

6.1 Single Qualitative/Categorical/ Nominal Variable

  1. Questions: Which? What Kind? How? How many of each Kind?
  • Island ( Which island ? )
  • Species ( Which Species? )
  1. Calculations: No of levels / Counts for each level
  • count / tally of no. of penguins on each island or in each species
  • sort and order by island or species
  1. Charts: Bar Chart / Pie Chart / Tree Map
  • geom_bar / geom_bar + coord_polar() / Find out!!
penguins %>% count(species)
species n
Adelie 146
Chinstrap 68
Gentoo 119
ggplot(penguins) + geom_bar(aes(x = island))

ggplot(penguins) + geom_bar(aes(x = sex))

6.2 YOUR TURN-3

Use the mpg_new dataset to create a few Single Categorical Graphs.

6.3 Single Quantitative Variable

  1. Questions: How many? How few? How often? How much?

  2. Calculations: max / min / mean / mode / (units)

  • max(), min(), range(), mean(), mode(), summary()
  1. Charts: Bar Chart / Histogram / Density
    • geom_histogram() / geom_density()
max(penguins$bill_length_mm)
## [1] 59.6
range(penguins$bill_length_mm, na.rm =TRUE) 
## [1] 32.1 59.6
summary(penguins$flipper_length_mm)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##     172     190     197     201     213     231
ggplot(penguins) + geom_density(aes(bill_length_mm))

ggplot(penguins) + geom_histogram(aes(x = bill_length_mm))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

6.4 YOUR TURN-4

Are all the above Quantitative variables ratio variables? Justify. Use the mpg_new dataset to create a few Single Quant Graphs.

6.5 Two Variables: Quantitative vs Quantitative

We can easily extend our intuition about one quantitative variable, to a pair of them. What Questions can we ask?

  1. Questions: How many of this vs How many of that? Does this depend upon that? How are they related? (Remember \(y = mx + c\) and friends?)

  2. Calculations: Correlation / Covariance / T-test / Chi-Square Test for Two Means etc. We won’t go into this here !

  3. Charts: Scatter Plot / Line Plot / Regression i.e. best fit lines

cor(penguins$bill_length_mm, penguins$bill_depth_mm)
## [1] -0.2286256
ggplot(penguins) +
  geom_point(aes(x = flipper_length_mm,
                 y = body_mass_g))

ggplot(penguins) +
  geom_point(aes(x = flipper_length_mm, 
                 y = bill_length_mm))

6.6 YOUR TURN-5

Use the mpg_new dataset to create a few Quant vs Quant Graphs.

6.7 Two Variables: Categorical vs Categorical

What sort of question could we ask that involves two categorical variables?

  1. Questions: How Many of this Kind( ~x) are How Many of that Kind( ~y ) ?

  2. Calculations: Counts and Tallies sliced by Category

    • counts , tally
  3. Charts: Stacked Bar Charts / Grouped Bar Charts / Segmented Bar Chart / Mosaic Chart

    • geom_bar()
    • Use the second Categorical variables to modify fill, color.
    • Also try to vary the parameter position of the bars.
ggplot(penguins) + geom_bar(aes(x = island, 
                                fill = species),
                            position = "stack")

Storyline: तीन पेनगीन। और तुम भी तीन(Oh never mind!)

6.8 YOUR TURN-6

Use the mpg_new dataset to create a few Quant vs Categorical Graphs.

6.9 Two Variables: Quantitative vs Qualitative

Finally, what if we want to look at Quant variables and Qual variables together? What questions could we ask?

  1. Questions: How much of this is Which Kind of that? How many vs Which? How many vs How?

  2. Calculations: Counts, Means, Ranges etc., grouped by Categorical variable.

ggplot(penguins) + 
    geom_density(aes(x = body_mass_g, 
                 color = island, 
                 fill = island), 
                 alpha = 0.3)

  1. Charts: Bar Chart using group / density plots by group / violin plots by group / box plots by group
  • geom_bar / geom_density / geom_violin / geom_boxplot using Categorical Variable for grouping
ggplot(penguins) + 
    geom_density(aes(x = body_mass_g, 
                 color = island, 
                 fill = island), 
                 alpha = 0.3)

ggplot(penguins) + 
  geom_histogram(aes(x = flipper_length_mm,
                 fill = sex))
## `stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

6.10 YOUR TURN-7

Use the mpg_new dataset to create a few Single Categorical Graphs.

7 Time to Play

  1. Create a fresh RMarkdown/Quarto document and similarly analyse two datasets of the following data sets

8 References

  1. Data Visualization with R, Robert Kabacoff
    • Good crisp descriptions of many kinds of graphs, no nonsense book. Available free on the web.
  2. Wickham and Grolemund, R for Data Science
    • R Bible. Available free on the web.
  3. The best stats you’ve ever seen | Hans Rosling

Ask me for help any time!

LS0tDQp0aXRsZTogIkxhYi0wMjogUHJvbm91bnMgYW5kIERhdGEiDQphdXRob3I6ICJBcnZpbmQgVmVua2F0YWRyaSINCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiBUUlVFDQogICAgdG9jX2Zsb2F0OiBUUlVFDQogICAgdG9jX2RlcHRoOiAyDQogICAgbnVtYmVyX3NlY3Rpb25zOiBUUlVFDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgY29kZV9kb3dubG9hZDogVFJVRQ0KICAgIGRmX3ByaW50OiBrYWJsZQ0KYWJzdHJhY3Q6IFBhcnQgb2YgbXkgYFIgZm9yIEFydGlzdHMgYW5kIERlc2lnbmVyc2Agd29ya3Nob3AgY291cnNlLg0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQojIEludHJvZHVjdGlvbg0KDQpUaGlzIFJNYXJrZG93biBkb2N1bWVudCBpcyBwYXJ0IG9mIGEgV29ya3Nob3Agb24gRGF0YSBWaXN1YWxpemF0aW9uIHdpdGggUi4gVGhlIG1hdGVyaWFsDQppcyBiYXNlZCBvbiAqQSBMYXllcmVkIEdyYW1tYXIgb2YgR3JhcGhpY3MqIGJ5IEhhZGxleSBXaWNraGFtLiBUaGUgaW50ZW50IGlzIHRvIGJ1aWxkIFNraWxsIGluIGNvZGluZyBpbiBSLCBhbmQgYWxzbyBhcHByZWNpYXRlIFIgYXMgYSB3YXkgdG8gbWV0YXBob3JpY2FsbHkgdmlzdWFsaXplIGluZm9ybWF0aW9uIG9mIHZhcmlvdXMga2luZHMsIHVzaW5nIHByZWRvbWluYW50bHkgZ2VvbWV0cmljIGZpZ3VyZXMgYW5kIHN0cnVjdHVyZXMuDQoNCkFsbCBSTWFya2Rvd24gZmlsZXMgY29tYmluZSBjb2RlLCB0ZXh0LCB3ZWItaW1hZ2VzLCBhbmQgZmlndXJlcw0KZGV2ZWxvcGVkIHVzaW5nIGNvZGUuIEV2ZXJ5dGhpbmcgaXMgdGV4dDsgY29kZSBjaHVua3MgYXJlIGVuY2xvc2VkIGluDQoqKmZlbmNlcyoqIChcYFxgXGApDQoNCiMgR29hbHMNCg0KLSAgIFVuZGVyc3RhbmQgZGlmZmVyZW50IGtpbmRzIG9mIGRhdGEgdmFyaWFibGVzDQotICAgQXBwcmVjaWF0ZSBob3cgdGhleSBjYW4gYmUgaWRlbnRpZmllZCBiYXNlZCBvbiB0aGUgKkludGVycm9nYXRpdmUNCiAgICBQcm9ub3VucyogdGhleSBhbnN3ZXIgdG8NCi0gICBVbmRlcnN0YW5kIGhvdyBlYWNoIGtpbmQgb2YgdmFyaWFibGUgbGVuZHMgaXRzZWxmIHRvIGEgc3BlY2lmaWMNCiAgICBnZW9tZXRyaWMgYXNwZWN0IGluIHRoZSBkYXRhIHZpc3VhbGl6YXRpb24uDQotICAgVW5kZXJzdGFuZCBob3cgYXNrIFF1ZXN0aW9ucyBvZiBEYXRhIHRvIGRldmVsb3AgVmlzdWFsaXphdGlvbnMNCg0KIyBQZWRhZ29naWNhbCBOb3RlDQoNClRoZSBtZXRob2QgZm9sbG93ZWQgd2lsbCBiZSBiYXNlZCBvbg0KW1BSSU1NXShodHRwczovL2Jsb2dzLmtjbC5hYy51ay9jc2VyLzIwMTcvMDkvMDEvcHJpbW0tYS1zdHJ1Y3R1cmVkLWFwcHJvYWNoLXRvLXRlYWNoaW5nLXByb2dyYW1taW5nLyk6DQoNCi0gICAqKlBSRURJQ1QqKiBJbnNwZWN0IHRoZSBjb2RlIGFuZCBndWVzcyBhdCB3aGF0IHRoZSBjb2RlIG1pZ2h0IGRvLA0KICAgICoqd3JpdGUgcHJlZGljdGlvbnMqKg0KLSAgICoqUlVOKiogdGhlIGNvZGUgcHJvdmlkZWQgYW5kIGNoZWNrIHdoYXQgaGFwcGVucw0KLSAgICoqSU5GRVIqKiB3aGF0IHRoZSBgcGFyYW1ldGVyc2Agb2YgdGhlIGNvZGUgZG8gYW5kICoqd3JpdGUgY29tbWVudHMNCiAgICB0byBleHBsYWluKiouIFdoYXQgYmVsbHMgYW5kIHdoaXN0bGVzIGNhbiB5b3Ugc2VlPw0KLSAgICoqTU9ESUZZKiogdGhlIGBwYXJhbWV0ZXJzYCBjb2RlIHByb3ZpZGVkIHRvIHVuZGVyc3RhbmQgdGhlDQogICAgYG9wdGlvbnNgIGF2YWlsYWJsZS4gKipXcml0ZSBjb21tZW50cyoqIHRvIHNob3cgd2hhdCB5b3UgaGF2ZSBhaW1lZA0KICAgIGZvciBhbmQgYWNoaWV2ZWQuDQotICAgKipNQUtFKiogOiB0YWtlIGFuIGlkZWEvY29uY2VwdCBvZiB5b3VyIG93biwgYW5kIGdyYXBoIGl0Lg0KDQpJbiB0aGUgZm9sbG93aW5nOg0KDQo+IFdoZW4gaXQgaXMgWU9VUiBUVVJOOiB3aGVyZXZlciB5b3Ugc2VlIFlPVVIgVFVSTiwgcGxlYXNlIHJlc3BvbmQgd2l0aA0KPiBleHBsYW5hdGlvbnMsIG1vcmUgcXVlc3Rpb25zIGFuZCBpZiB5b3UgYXJlIGFscmVhZHkgY29uZmlkZW50LCBjb2RlDQo+IGNodW5rcyB0byBjcmVhdGUgbmV3IGNhbGN1bGF0aW9ucyBhbmQgZ3JhcGhzLg0KDQojIFNldCBVcA0KDQpUaGUgYHNldHVwYCBjb2RlICoqY2h1bmsqKiBiZWxvdyBicmluZ3MgaW50byBvdXIgY29kaW5nIHNlc3Npb24gKipSDQpwYWNrYWdlcyoqIHRoYXQgcHJvdmlkZSBzcGVjaWZpYyBjb21wdXRhdGlvbmFsIGFiaWxpdGllcyBhbmQgYWxzbw0KKipkYXRhc2V0cyoqIHdoaWNoIHdlIGNhbiB1c2UuDQoNClRvIHJlaXRlcmF0ZTogUGFja2FnZXMgYW5kIGRhdGFzZXRzIGFyZSAqKm5vdCoqIHRoZSBzYW1lIHRoaW5nICEhDQpQYWNrYWdlcyBhcmUgKHNtYWxsKSBjb2xsZWN0aW9ucyBvZiBwcm9ncmFtcy4gRGF0YXNldHMgYXJlDQpqdXN0Li4uLmluZm9ybWF0aW9uLg0KDQojIyBQYWNrYWdlcyBuZWVkZWQNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9VFJVRSxtZXNzYWdlPUZBTFNFfQ0KDQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsd2FybmluZyA9IFRSVUUpDQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpsaWJyYXJ5KGphbml0b3IpDQoNCmBgYA0KDQojIFdoYXQgRG9lcyBEYXRhIExvb2sgTGlrZT8NCg0KSW4gdGhpcyBSTWFya2Rvd24gZG9jdW1lbnQsIHdlIHRyeSB0byBjb25uZWN0IHN0b3J5LW1ha2luZyAqKnF1ZXN0aW9ucyoqDQp3aXRoIHR3byBpZGVhczoNCg0KYSkgIGEgKlZhcmlhYmxlKiBpbiBhIGRhdGFzZXQNCmIpICBBIGNvbXB1dGVkICpRdWFudGl0eSAvIERlc2NyaXB0aXZlIFN0YXRpc3RpYyogb3IgYSAqVmlzdWFsKiwgYmFzZWQNCiAgICBvbiBvbmUgb3IgbW9yZSBWYXJpYWJsZXMNCg0KU286IGEgcXVlc3Rpb24gaWRlbnRpZmllcyBhIHZhcmlhYmxlIGFuZCBhIHF1ZXN0aW9uIGFsc28gbGVhZHMgdG8gYQ0KKkNvbXB1dGF0aW9uKiBvciBhICpEYXRhIFZpc3VhbGl6YXRpb24qLiBUaGUgaWRlYSBpcyB0byBnZXQgdGhlDQppbnR1aXRpb24gYmVoaW5kIGRhdGEsIGFuZCBpdGVyYXRpdmVseSBhc2sgdGhlIHF1ZXN0aW9ucyBhbmQgZm9ybQ0KaHlwb3RoZXNlcyBhbmQgcGVyZm9ybSAqRXhwbG9yYXRvcnkgRGF0YSBBbmFseXNpcyogKEVEQSkgdXNpbmcgZ3JhcGhzDQphbmQgY2hhcnRzIGluIFIuDQoNCkF0IHNvbWUgcG9pbnQgd2UgbWF5IGZpbmQgdGhhdCB0aGUgZGF0YSBpcyBub3QgYWRlcXVhdGUgdG8NCnByb3ZlL2Rpc3Byb3ZlIGEgcGFydGljdWxhciBoeXBvdGhlc2lzIGFuZCBuZWVkIHRvIGdldCBpbnRvIGZ1cnRoZXINCnJlc2VhcmNoIC8gZXhwZXJpbWVudGFsIGRlc2lnbi4gSXQgaXMgcG9zc2libGUgdG8gZGVzaWduIHRoZSByZXNlYXJjaA0KZXhwZXJpbWVudHMgYWxzbyBpbiBSLCBidXQgd2UgbWF5IGNvdmVyIHRoYXQgbXVjaCBsYXRlci4NCg0KDQojIyBJbnRlcnJvZ2F0aXZlIFByb25vdW5zIGZvciBEYXRhIFZhcmlhYmxlcw0KDQpTbyBob3cgZG8gd2UgYXNrIHF1ZXN0aW9ucz8gVGhlc2UgYXJlIHVzdWFsbHkgd2l0aCAqaW50ZXJyb2dhdGl2ZQ0KcHJvbm91bnMqIGluIEVuZ2xpc2g6IFdoYXQ/IFdobz8gV2hlcmU/IFdoaWNoPyBXaGF0IEtpbmQ/IEhvdz8gYW5kIHNvDQpvbi4NCg0KIyMgVGhlIGBwZW5ndWluc2AgZGF0YXNldA0KDQpgYGB7ciBMb29rX2F0X1Blbmd1aW5zfQ0KDQpuYW1lcyhwZW5ndWlucykgIyBDb2x1bW4sIGkuZS4gVmFyaWFibGUgbmFtZXMNCmhlYWQocGVuZ3VpbnMpICMgZmlyc3Qgc2l4IHJvd3MNCnRhaWwocGVuZ3VpbnMpICMgTGFzdCBzaXggcm93cw0KZGltKHBlbmd1aW5zKSAjIFNpemUgb2YgZGF0YXNldA0KDQojIENoZWNrIGZvciBtaXNzaW5nIGRhdGENCmFueShpcy5uYShwZW5ndWlucykgPT0gVFJVRSkNCg0KYGBgDQoNCjEuICBXaGF0IGFyZSB0aGUgdmFyaWFibGUgYG5hbWVzKClgPw0KMi4gIFdoYXQgd291bGQgYmUgdGhlIFF1ZXN0aW9uIHlvdSBtaWdodCBoYXZlIGFza2VkIHRvIG9idGFpbiBlYWNoIG9mDQogICAgdGhlIHZhcmlhYmxlcz8NCjMuICBXaGF0IGZ1cnRoZXIgcXVlc3Rpb25zL21ldGEgcXVlc3Rpb25zIHdvdWxkIHlvdSBhc2sgdG8gInByb2Nlc3MiDQogICAgdGhhdCB2YXJpYWJsZT8gKCBIaW50OiBBZGQgYW5vdGhlciB3b3JkIGFmdGVyIGFueSBvZiB0aGUNCiAgICBJbnRlcnJvZ2F0aXZlIFByb25vdW5zLCBlLmcuIEhvdy4uLk1BTlk/KQ0KNC4gIFdoZXJlIG1pZ2h0IHRoZSBhbnN3ZXJzIHRha2UgeW91ciBzdG9yeT8NCg0KIyMgWU9VUiBUVVJOLTENCg0KU3RhdGUgYSBmZXcgcXVlc3Rpb25zIGFmdGVyIGRpc2N1c3Npb24gd2l0aCB5b3VyIGZyaWVuZCBhbmQgc3RhdGUNCnBvc3NpYmxlIHZhcmlhYmxlcywgb3Igd2hhdCB5b3UgY291bGQgRE8gd2l0aCB0aGUgdmFyaWFibGVzLCBhcyBhbg0KYW5zd2VyLlwNCkUuZy4gUS4gSG93IG1hbnkgcGVuZ3VpbnM/IEEuIFdlIG5lZWQgdG8gY291bnQuLi5yb3dzPw0KDQoNCg0KSW4gdGhlIFRhYmxlIGJlbG93LCB3ZSBoYXZlIGEgcm91Z2ggbWFwcGluZyBvZiBpbnRlcnJvZ2F0aXZlIHByb25vdW5zIHRvDQp0aGUga2luZHMgb2YgdmFyaWFibGVzIGluIHRoZSBkYXRhOg0KDQpgYGB7ciBlY2hvPUZBTFNFLG1lc3NhZ2U9RkFMU0Usd2FybmluZz1GQUxTRX0NCmxpYnJhcnkoa2FibGVFeHRyYSkNCnByb25vdW5zIDwtIHJlYWRfY3N2KCJwcm9ub3Vucy5jc3YiKSAlPiUgc2VsZWN0KC1ObykNCnByb25vdW5zICU+JQ0KICBrYmwoKSAlPiUgDQogIGthYmxlX2NsYXNzaWMoZnVsbF93aWR0aCA9IFRSVUUpICU+JSANCiAgICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIsImNvbmRlbnNlZCIpKQ0KDQoNCmBgYA0KDQpBcyB5b3UgZ28gZnJvbSBRdWFsaXRhdGl2ZSB0byBRdWFudGl0YXRpdmUgZGF0YSB0eXBlcyBpbiB0aGUgdGFibGUsIEkNCmhvcGUgeW91IGNhbiBkZXRlY3QgYSBtb3ZlbWVudCBmcm9tIGZ1enp5IGdyb3Vwcy9jYXRlZ29yaWVzIHRvIG1vcmUgYW5kDQptb3JlIGNyeXN0YWxsaXplZCBudW1iZXJzLiBFYWNoIHZhcmlhYmxlL3NjYWxlIGNhbiBiZSBzdWJqZWN0ZWQgdG8gdGhlDQpvcGVyYXRpb25zIG9mIHRoZSBwcmV2aW91cyBncm91cC4gDQoNCkluIHRoZSB3b3JkcyBvZiBbUy5TLlN0ZXZlbnMuXShodHRwczovL3N0YXRzLmlkcmUudWNsYS5lZHUvb3RoZXIvbXVsdC1wa2cvd2hhdHN0YXQvd2hhdC1pcy10aGUtZGlmZmVyZW5jZS1iZXR3ZWVuLWNhdGVnb3JpY2FsLW9yZGluYWwtYW5kLWludGVydmFsLXZhcmlhYmxlcy8pOg0KDQo+IHRoZSBiYXNpYyBvcGVyYXRpb25zIG5lZWRlZCB0byBjcmVhdGUgZWFjaCB0eXBlIG9mIHNjYWxlIGlzDQo+IGN1bXVsYXRpdmU6IHRvIGFuIG9wZXJhdGlvbiBsaXN0ZWQgb3Bwb3NpdGUgYSBwYXJ0aWN1bGFyIHNjYWxlIG11c3QgYmUNCj4gYWRkZWQgYWxsIHRob3NlIG9wZXJhdGlvbnMgcHJlY2VkaW5nIGl0Lg0KDQpTZWUgaGlzIGNsYXNzaWMgcGFwZXIgaGVyZSBbUERGXShodHRwczovL3BzeWNob2xvZ3kub2tzdGF0ZS5lZHUvZmFjdWx0eS9qZ3JpY2UvcHN5YzMyMTQvU3RldmVuc19Gb3VyU2NhbGVzXzE5NDYucGRmKS4gRG8gdGhpbmsgYWJvdXQgdGhpcyBhcyB5b3Ugd29yayB3aXRoIGRhdGEuDQoNCiFbVHlwZXMgb2YgRGF0YSBWYXJpYWJsZXNdKC4uL2ltYWdlcy9SYXRpb0ludGVydmFsT3JkaW5hbE5vbWluYWwuUE5HKQ0KDQpEbyB0YWtlIGEgbG9vayBhdCB0aGVzZSByZWZlcmVuY2VzOg0KDQoxLiAgPGh0dHBzOi8vc3RhdHMuaWRyZS51Y2xhLmVkdS9vdGhlci9tdWx0LXBrZy93aGF0c3RhdC93aGF0LWlzLXRoZS1kaWZmZXJlbmNlLWJldHdlZW4tY2F0ZWdvcmljYWwtb3JkaW5hbC1hbmQtaW50ZXJ2YWwtdmFyaWFibGVzLz4NCjIuICA8aHR0cHM6Ly93d3cuZnJlZWNvZGVjYW1wLm9yZy9uZXdzL3R5cGVzLW9mLWRhdGEtaW4tc3RhdGlzdGljcy1ub21pbmFsLW9yZGluYWwtaW50ZXJ2YWwtYW5kLXJhdGlvLWRhdGEtdHlwZXMtZXhwbGFpbmVkLXdpdGgtZXhhbXBsZXMvPg0KDQojIyBUaGUgYG1wZ2AgZGF0YXNldA0KDQpgYGB7ciBMb29rX2F0X21wZ30NCm5hbWVzKG1wZykgIyBDb2x1bW4sIGkuZS4gVmFyaWFibGUgbmFtZXMNCmhlYWQobXBnKSAjIGZpcnN0IHNpeCByb3dzDQp0YWlsKG1wZykgIyBMYXN0IHNpeCByb3dzDQpkaW0obXBnKSAjIFNpemUgb2YgZGF0YXNldA0KDQojIENoZWNrIGZvciBtaXNzaW5nIGRhdGENCmFueShpcy5uYShtcGcpID09IFRSVUUpDQoNCmBgYA0KDQojIyBZT1VSIFRVUk4tMg0KDQpMb29rIGNhcmVmdWxseSBhdCB0aGUgdmFyaWFibGVzIGhlcmUuIEhvdyB3b3VsZCB5b3UgaW50ZXJwcmV0IHNheSB0aGUNCmBjeWxgIHZhcmlhYmxlPyBJcyBpdCBhIG51bWJlciBhbmQgdGhlcmVmb3JlIFF1YW50aXRhdGl2ZSwgb3IgY291bGQgaXQNCmJlIHNvbWV0aGluZyBlbHNlPw0KDQojIyBSZWFkaW5nIEV4dGVybmFsIERhdGENCg0KQSBmaXJzdCB0YXNrIGlzIG9mdGVuIHRoZSByZWFkaW5nIGluIG9mIGV4dGVybmFsIGRhdGEuIERhdGEgaXMgYmVzdCBzdG9yZWQgYW5kIHNoYXJlZCBpbiBhIGBDU1YgZmlsZSBmb3JtYXRgLkRvd25sb2FkIHRoaXMgQ1NWIGZpbGUgaW50byB5b3VyIHByb2plY3QgZm9sZGVyOg0KPGJyPg0KDQpgYGB7ciBlY2hvPUZBTFNFLG1lc3NhZ2U9RkFMU0V9DQptcGdfbmV3IDwtIHJlYWRfY3N2KCJtcGdfdXBwZXJjYXNlLmNzdiIpDQpsaWJyYXJ5KGRvd25sb2FkdGhpcykNCm1wZ19uZXcgJT4lIGRvd25sb2FkX3RoaXMoDQogICAgb3V0cHV0X25hbWUgPSAibXBnX25ldyIsDQogICAgb3V0cHV0X2V4dGVuc2lvbiA9ICIuY3N2IiwNCiAgICBidXR0b25fbGFiZWwgPSAiRG93bmxvYWQgQ1NWIiwNCiAgICBidXR0b25fdHlwZSA9ICJpbmZvIiwNCiAgICBoYXNfaWNvbiA9IFRSVUUsDQogICAgaWNvbiA9ICJmYSBmYS1zYXZlIg0KICApDQpgYGANCg0KVG8gcmVhZCB0aGlzIENTViBkYXRhIGludG8gb3VyIFIgU2Vzc2lvbiwgd2UgdXNlIGEgY29tbWFuZCBjYWxsZWQgYHJlYWRfY3N2YCBmcm9tIHRoZSBgcmVhZHJgIHBhY2thZ2U6DQoNCg0KYGBge3J9DQojfCBsYWJlbDogcmVhZGluZy1maWxlcw0KbXBnX25ldyA8LSByZWFkcjo6cmVhZF9jc3YoIm1wZ191cHBlcmNhc2UuY3N2IikNCg0KYGBgDQoNCkFzIGNhbiBiZSBzZWVuICwgYHJlYWRfY3N2YCB0ZWxscyB1cyBieSBkZWZhdWx0IHdoYXQgdGhlIGNvbHVtbiBuYW1lcyBhcmUgYW5kIHRoZSB0eXBlcyB0aGV5IGFyZTogYGNocmAgYW5kIGBkYmxgIGluIHRoaXMgY2FzZS4gDQoNCkluIHRoZSBldmVudCB0aGF0IHRoZSBjb2x1bW4gbmFtZXMgYXJlIG5vdCB2ZXJ5IGdvb2Qgb3IgZXZvY2F0aXZlLCB3ZSBjYW4gc2V0IGBuYW1lX3JlcGFpcmAgPSBgbWFrZV9jbGVhbl9uYW1lc2AgaW5zaWRlIGByZWFkX2NzdmA7IHRoaXMgYWRkaXRpb25hbCBmdW5jdGlvbiBpcyBhdmFpbGFibGUgdmlhIHRoZSBgamFuaXRvcmAgcGFja2FnZS4NCg0KYGBge3J9DQpyZWFkX2NzdigibXBnX3VwcGVyY2FzZS5jc3YiLA0KICAgICAgICAgc2hvd19jb2xfdHlwZXMgPSBUUlVFLA0KICAgICAgICAgbmFtZV9yZXBhaXIgPSBtYWtlX2NsZWFuX25hbWVzKSAlPiUgIyBuZWVkcyBgamFuaXRvcmANCiAgZ2xpbXBzZSgpDQoNCmBgYA0KDQpOb3RlIHRoYXQgdGhlIGRlZmF1bHQgbmFtaW5nIGlzIGJhc2VkIGluIGBzbmFrZV9jYXNlYC4gVGhlcmUgYXJlIG90aGVyIHdheXMgYW5kIGlmIHdlIGRlc2lyZSB0byB1c2UgdGhlbSwgd2UgbmVlZCB0byBtYWtlIGBtYWtlX2NsZWFuX25hbWVzYCBhcyB3aGF0IGlzIGNhbGxlZCBhICoqbGFtYmRhIGZ1bmN0aW9uKiogd2l0aCBleHRyYSBwYXJhbWV0ZXJzOg0KDQpgYGB7cn0NCg0KcmVhZF9jc3YoIm1wZ191cHBlcmNhc2UuY3N2IiwNCiAgICAgICAgIHNob3dfY29sX3R5cGVzID0gVFJVRSwNCiAgICAgICAgIG5hbWVfcmVwYWlyID0gDQogICAgICAgICAgIA0KICAgICAgIyBIZXJlIGlzIHRoZSBsYW1iZGEgZnVuY3Rpb24NCiAgICAgICMgVG8gYmUgdXNlZCBvbmx5IHdoZW4geW91IHdhbnQgdG8gZ28gYmV5b25kIHRoZSBkZWZhdWx0cw0KICAgICAgIyB1c2luZyBhZGRpdGlvbmFsIHBhcmFtZXRlcnMuIGUuZyBgY2FzZWANCiAgICAgICAgICAgfiBtYWtlX2NsZWFuX25hbWVzKC4sIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2FzZSA9ICJiaWdfY2FtZWwiKSkgJT4lIA0KICANCiAgIyBuZWVkcyBgamFuaXRvcmANCiAgIyBgLmAgZGVub3RlcyB0aGUgVkVDVE9SIG9mIGNvbHVtbiBuYW1lcw0KICANCiAgZ2xpbXBzZSgpDQoNCmBgYA0KDQpOb3RlIHRoZSB1c2FnZSBvZiBgfmAgdG8gbWFrZSBhIGZ1bmN0aW9uOiB0aGlzIGlzIG5lZWRlZCBvbmx5IGlmIHdlIHdhbnQNCnRvIHBhc3MgKGFkZGl0aW9uYWwpIGFyZ3VtZW50cyB0byBgbWFrZV9jbGVhbl9uYW1lc2AuIChXZSB3aWxsIGZpbmQgdGhpcw0KYWxzbyB3aGVuIHdlIGVuY291bnRlciB0aGUgYHB1cnJyYCBwYWNrYWdlIHdoZXJlIHdlIHJ1biBmdW5jdGlvbnMNCml0ZXJhdGl2ZWx5IG92ZXIgZWFjaCBpbmRpdmlkdWFsIGVudHJ5IGluIGEgdmVjdG9yL2NvbHVtbiBvcg0KbGlzdC1jb2x1bW4uKQ0KDQoNCiMgSW50ZXJyb2dhdGlvbnMgYW5kIEdyYXBocw0KDQpOb3cgdGhhdCB3ZSBrbm93IGhvdyB0byBhY2Nlc3MgIlIgaW50ZXJuYWwiIGRhdGFzZXRzIGFuZCB0byByZWFkIGluIGV4dGVybmFsIGRhdGFzZXRzLCB3ZSBjYW4gYWxzbyByZXNwb25kIHRvICggbW9yZSBjb21wbGV4ICkgUXVlc3Rpb25zLCB3aXRoIG5vdCBqdXN0IGENCip2YXJpYWJsZSogYnV0IG9uZSBvZiB0d28gdGhpbmdzOg0KDQotICAgQSBjYWxjdWxhdGlvbiwgc2hvd24gaW4gYSAqdGFibGUqDQotICAgYSAqZGF0YSB2aXN1YWxpemF0aW9uKi4gVGhpcyB2aXN1YWxpemF0aW9uIGNhbiBldmVuIGludm9sdmUgbW9yZQ0KICAgIHRoYW4gb25lIHZhcmlhYmxlLCBhcyB3ZSB3aWxsIHNlZS4NCg0KV2hhdCBzb3J0IG9mIGNhbGN1bGF0aW9ucywgYW5kIHZpc3VhbHMgY2hhcnRzIGNhbiB3ZSBjcmVhdGUgd2l0aA0KZGlmZmVyZW50IGtpbmRzIG9mIHZhcmlhYmxlcywgdGFrZW4gc2luZ2x5IG9yIHRvZ2V0aGVyPyBMZXQgdXMgd3JpdGUNCnNvbWUgc2ltcGxlIEVuZ2xpc2ggZGVzY3JpcHRpb25zIG9mIG1lYXN1cmVzIGFuZCB2aXN1YWxzIGFuZCBzZWUgd2hhdA0KY29tbWFuZHMgdGhleSB1c2UgaW4gUi4NCg0KSGVyZSB3ZSB3aWxsIHVzZSB0aGUgR3JhbW1hciBvZiBhIHBhY2thZ2UgY2FsbGVkIGBnZ3Bsb3RgLCB3aGljaCB3ZSB3aWxsDQplbmNvdW50ZXIgaW4gTGFiOjA0LiBMZXQgdXMgZ28gd2l0aCBvdXIgaW50dWl0aW9uIHdpdGggdGhlIGNvZGUgaW4gdGhlDQpmb2xsb3dpbmcgc2VjdGlvbnMuDQoNCk5vdGU6IHNpbmNlIHdlIHNhdyBhIGNvdXBsZSBvZiBtaXNzaW5nIGVudHJpZXMgaW4gdGhlIGBwZW5ndWluc2ANCmRhdGFzZXQsIGxldCB1cyByZW1vdmUgdGhlbSBmb3Igbm93Lg0KDQpgYGB7cn0NCg0KcGVuZ3VpbnMgPC0gcGVuZ3VpbnMgJT4lIGRyb3BfbmEoKQ0KDQpgYGANCg0KIyMgU2luZ2xlIFF1YWxpdGF0aXZlL0NhdGVnb3JpY2FsLyBOb21pbmFsIFZhcmlhYmxlDQoNCjEuICBRdWVzdGlvbnM6IFdoaWNoPyBXaGF0IEtpbmQ/IEhvdz8gSG93IG1hbnkgb2YgZWFjaCBLaW5kPw0KDQotICAgSXNsYW5kICggV2hpY2ggaXNsYW5kID8gKQ0KLSAgIFNwZWNpZXMgKCBXaGljaCBTcGVjaWVzPyApDQoNCjIpICBDYWxjdWxhdGlvbnM6IE5vIG9mIGBsZXZlbHNgIC8gQ291bnRzIGZvciBlYWNoIGBsZXZlbGANCg0KLSAgIGBjb3VudCAvIHRhbGx5YCBvZiBuby4gb2YgcGVuZ3VpbnMgb24gZWFjaCBpc2xhbmQgb3IgaW4gZWFjaCBzcGVjaWVzDQotICAgYHNvcnRgIGFuZCBgb3JkZXJgIGJ5IGlzbGFuZCBvciBzcGVjaWVzDQoNCjMpICBDaGFydHM6IEJhciBDaGFydCAvIFBpZSBDaGFydCAvIFRyZWUgTWFwDQoNCi0gICBgZ2VvbV9iYXJgIC8gYGdlb21fYmFyICsgY29vcmRfcG9sYXIoKWAgLyBGaW5kIG91dCEhDQoNCmBgYHtyIFNpbmdsZV9DYXRfVmFyX0NhbGN1bGF0aW9uc30NCg0KcGVuZ3VpbnMgJT4lIGNvdW50KHNwZWNpZXMpDQoNCmBgYA0KDQpgYGB7ciBTaW5nbGVfQ2F0X1Zhcl9HcmFwaHN9DQoNCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2JhcihhZXMoeCA9IGlzbGFuZCkpDQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9iYXIoYWVzKHggPSBzZXgpKQ0KDQpgYGANCg0KIyMgWU9VUiBUVVJOLTMNClVzZSB0aGUgYG1wZ19uZXdgIGRhdGFzZXQgdG8gY3JlYXRlIGEgZmV3IFNpbmdsZSBDYXRlZ29yaWNhbCBHcmFwaHMuDQoNCg0KDQojIyBTaW5nbGUgUXVhbnRpdGF0aXZlIFZhcmlhYmxlDQoNCjEpICBRdWVzdGlvbnM6IEhvdyBtYW55PyBIb3cgZmV3PyBIb3cgb2Z0ZW4/IEhvdyBtdWNoPw0KDQoyKSAgQ2FsY3VsYXRpb25zOiBtYXggLyBtaW4gLyBtZWFuIC8gbW9kZSAvICh1bml0cykNCg0KLSAgIGBtYXgoKWAsIGBtaW4oKWAsIGByYW5nZSgpYCwgYG1lYW4oKWAsIGBtb2RlKCksIHN1bW1hcnkoKWANCg0KMykgIENoYXJ0czogQmFyIENoYXJ0IC8gSGlzdG9ncmFtIC8gRGVuc2l0eQ0KICAgIC0gICBgZ2VvbV9oaXN0b2dyYW0oKWAgLyBgZ2VvbV9kZW5zaXR5KClgDQoNCmBgYHtyIFNpbmdsZV9RdWFudF9jYWxjdWxhdGlvbnN9DQoNCm1heChwZW5ndWlucyRiaWxsX2xlbmd0aF9tbSkNCg0KcmFuZ2UocGVuZ3VpbnMkYmlsbF9sZW5ndGhfbW0sIG5hLnJtID1UUlVFKSANCg0Kc3VtbWFyeShwZW5ndWlucyRmbGlwcGVyX2xlbmd0aF9tbSkNCg0KYGBgDQoNCmBgYHtyIFNpbmdsZV9RdWFudF9ncmFwaHN9DQoNCmdncGxvdChwZW5ndWlucykgKyBnZW9tX2RlbnNpdHkoYWVzKGJpbGxfbGVuZ3RoX21tKSkNCg0KZ2dwbG90KHBlbmd1aW5zKSArIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gYmlsbF9sZW5ndGhfbW0pKQ0KDQpgYGANCg0KIyMgWU9VUiBUVVJOLTQNCg0KQXJlIGFsbCB0aGUgYWJvdmUgUXVhbnRpdGF0aXZlIHZhcmlhYmxlcyAqcmF0aW8qIHZhcmlhYmxlcz8gSnVzdGlmeS4NClVzZSB0aGUgYG1wZ19uZXdgIGRhdGFzZXQgdG8gY3JlYXRlIGEgZmV3IFNpbmdsZSBRdWFudCBHcmFwaHMuDQoNCg0KIyMgVHdvIFZhcmlhYmxlczogUXVhbnRpdGF0aXZlIHZzIFF1YW50aXRhdGl2ZQ0KDQpXZSBjYW4gZWFzaWx5IGV4dGVuZCBvdXIgaW50dWl0aW9uIGFib3V0IG9uZSBxdWFudGl0YXRpdmUgdmFyaWFibGUsIHRvIGENCnBhaXIgb2YgdGhlbS4gV2hhdCBRdWVzdGlvbnMgY2FuIHdlIGFzaz8NCg0KMSkgIFF1ZXN0aW9uczogSG93IG1hbnkgb2YgdGhpcyB2cyBIb3cgbWFueSBvZiB0aGF0PyBEb2VzIHRoaXMgZGVwZW5kDQogICAgdXBvbiB0aGF0PyBIb3cgYXJlIHRoZXkgcmVsYXRlZD8gKFJlbWVtYmVyICR5ID0gbXggKyBjJCBhbmQNCiAgICBmcmllbmRzPykNCg0KMikgIENhbGN1bGF0aW9uczogQ29ycmVsYXRpb24gLyBDb3ZhcmlhbmNlIC8gVC10ZXN0IC8gQ2hpLVNxdWFyZSBUZXN0DQogICAgZm9yIFR3byBNZWFucyBldGMuIFdlIHdvbid0IGdvIGludG8gdGhpcyBoZXJlICENCg0KMykgIENoYXJ0czogU2NhdHRlciBQbG90IC8gTGluZSBQbG90IC8gUmVncmVzc2lvbiBpLmUuIGJlc3QgZml0IGxpbmVzDQoNCmBgYHtyIFF1YW50X3ZzX1F1YW50X0NhbGN1bGF0aW9uc30NCg0KY29yKHBlbmd1aW5zJGJpbGxfbGVuZ3RoX21tLCBwZW5ndWlucyRiaWxsX2RlcHRoX21tKQ0KDQoNCmBgYA0KDQpgYGB7ciBRdWFudF92c19RdWFudC1HcmFwaHN9DQoNCmdncGxvdChwZW5ndWlucykgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZmxpcHBlcl9sZW5ndGhfbW0sDQogICAgICAgICAgICAgICAgIHkgPSBib2R5X21hc3NfZykpDQoNCmdncGxvdChwZW5ndWlucykgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZmxpcHBlcl9sZW5ndGhfbW0sIA0KICAgICAgICAgICAgICAgICB5ID0gYmlsbF9sZW5ndGhfbW0pKQ0KDQpgYGANCg0KIyMgWU9VUiBUVVJOLTUNClVzZSB0aGUgYG1wZ19uZXdgIGRhdGFzZXQgdG8gY3JlYXRlIGEgZmV3IFF1YW50IHZzIFF1YW50IEdyYXBocy4NCg0KDQojIyBUd28gVmFyaWFibGVzOiBDYXRlZ29yaWNhbCB2cyBDYXRlZ29yaWNhbA0KDQpXaGF0IHNvcnQgb2YgcXVlc3Rpb24gY291bGQgd2UgYXNrIHRoYXQgaW52b2x2ZXMgKnR3byogY2F0ZWdvcmljYWwNCnZhcmlhYmxlcz8NCg0KMSkgIFF1ZXN0aW9uczogSG93IE1hbnkgb2YgdGhpcyBLaW5kKCBcfngpIGFyZSBIb3cgTWFueSBvZiB0aGF0IEtpbmQoDQogICAgXH55ICkgPw0KDQoyKSAgQ2FsY3VsYXRpb25zOiBDb3VudHMgYW5kIFRhbGxpZXMgc2xpY2VkIGJ5IENhdGVnb3J5DQoNCiAgICAtICAgYGNvdW50c2AgLCBgdGFsbHlgDQoNCjMpICBDaGFydHM6IFN0YWNrZWQgQmFyIENoYXJ0cyAvIEdyb3VwZWQgQmFyIENoYXJ0cyAvIFNlZ21lbnRlZCBCYXINCiAgICBDaGFydCAvIE1vc2FpYyBDaGFydA0KDQogICAgLSAgIGBnZW9tX2JhcigpYA0KICAgIC0gICBVc2UgdGhlIHNlY29uZCBDYXRlZ29yaWNhbCB2YXJpYWJsZXMgdG8gbW9kaWZ5IGBmaWxsYCwgYGNvbG9yYC4NCiAgICAtICAgQWxzbyB0cnkgdG8gdmFyeSB0aGUgcGFyYW1ldGVyIGBwb3NpdGlvbmAgb2YgdGhlIGJhcnMuDQoNCmBgYHtyIFR3b19DYXRfVmFyX0NhbGN1bGF0aW9uc30NCg0KDQpgYGANCg0KYGBge3IgVHdvX0NhdF9WYXJfR3JhcGhzfQ0KDQpnZ3Bsb3QocGVuZ3VpbnMpICsgZ2VvbV9iYXIoYWVzKHggPSBpc2xhbmQsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gc3BlY2llcyksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgcG9zaXRpb24gPSAic3RhY2siKQ0KYGBgDQoNClN0b3J5bGluZTog4KSk4KWA4KSoIOCkquClh+CkqOCkl+ClgOCkqOClpCDgpJTgpLAg4KSk4KWB4KSuIOCkreClgCDgpKTgpYDgpKgoT2ggbmV2ZXIgbWluZCEpDQoNCiMjIFlPVVIgVFVSTi02DQpVc2UgdGhlIGBtcGdfbmV3YCBkYXRhc2V0IHRvIGNyZWF0ZSBhIGZldyBRdWFudCB2cyBDYXRlZ29yaWNhbCBHcmFwaHMuDQoNCg0KIyMgVHdvIFZhcmlhYmxlczogUXVhbnRpdGF0aXZlIHZzIFF1YWxpdGF0aXZlDQoNCkZpbmFsbHksIHdoYXQgaWYgd2Ugd2FudCB0byBsb29rIGF0IFF1YW50IHZhcmlhYmxlcyBhbmQgUXVhbCB2YXJpYWJsZXMNCnRvZ2V0aGVyPyBXaGF0IHF1ZXN0aW9ucyBjb3VsZCB3ZSBhc2s/DQoNCjEpICBRdWVzdGlvbnM6IEhvdyBtdWNoIG9mIHRoaXMgaXMgV2hpY2ggS2luZCBvZiB0aGF0PyBIb3cgbWFueSB2cw0KICAgIFdoaWNoPyBIb3cgbWFueSB2cyBIb3c/DQoNCjIpICBDYWxjdWxhdGlvbnM6IENvdW50cywgTWVhbnMsIFJhbmdlcyBldGMuLCAqKmdyb3VwZWQgYnkqKiBDYXRlZ29yaWNhbA0KICAgIHZhcmlhYmxlLg0KDQpgYGB7ciBRdWFudF92c19RdWFsX0NhbGN1bGF0aW9uc30NCg0KZ2dwbG90KHBlbmd1aW5zKSArIA0KICAgIGdlb21fZGVuc2l0eShhZXMoeCA9IGJvZHlfbWFzc19nLCANCiAgICAgICAgICAgICAgICAgY29sb3IgPSBpc2xhbmQsIA0KICAgICAgICAgICAgICAgICBmaWxsID0gaXNsYW5kKSwgDQogICAgICAgICAgICAgICAgIGFscGhhID0gMC4zKQ0KYGBgDQoNCjMpICBDaGFydHM6IEJhciBDaGFydCB1c2luZyBncm91cCAvIGRlbnNpdHkgcGxvdHMgYnkgZ3JvdXAgLyB2aW9saW4NCiAgICBwbG90cyBieSBncm91cCAvIGJveCBwbG90cyBieSBncm91cA0KDQotICAgYGdlb21fYmFyYCAvIGBnZW9tX2RlbnNpdHlgIC8gYGdlb21fdmlvbGluYCAvIGBnZW9tX2JveHBsb3RgIHVzaW5nDQogICAgQ2F0ZWdvcmljYWwgVmFyaWFibGUgZm9yIGdyb3VwaW5nDQoNCmBgYHtyIFF1YW50X3ZzX1F1YWxfR3JhcGhzfQ0KDQpnZ3Bsb3QocGVuZ3VpbnMpICsgDQogICAgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gYm9keV9tYXNzX2csIA0KICAgICAgICAgICAgICAgICBjb2xvciA9IGlzbGFuZCwgDQogICAgICAgICAgICAgICAgIGZpbGwgPSBpc2xhbmQpLCANCiAgICAgICAgICAgICAgICAgYWxwaGEgPSAwLjMpDQoNCmdncGxvdChwZW5ndWlucykgKyANCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBmbGlwcGVyX2xlbmd0aF9tbSwNCiAgICAgICAgICAgICAgICAgZmlsbCA9IHNleCkpDQoNCmBgYA0KDQojIyBZT1VSIFRVUk4tNw0KVXNlIHRoZSBgbXBnX25ld2AgZGF0YXNldCB0byBjcmVhdGUgYSBmZXcgU2luZ2xlIENhdGVnb3JpY2FsIEdyYXBocy4NCg0KDQojIFRpbWUgdG8gUGxheQ0KDQoxLiAgQ3JlYXRlIGEgZnJlc2ggUk1hcmtkb3duL1F1YXJ0byBkb2N1bWVudCBhbmQgc2ltaWxhcmx5IGFuYWx5c2UgdHdvIGRhdGFzZXRzIG9mIHRoZSBmb2xsb3dpbmcgZGF0YSBzZXRzDQoNCi0gICBBbnkgZGF0YSBzZXQgaW4geW91ciBSIGluc3RhbGxhdGlvbi4gVHlwZSBgZGF0YSgpYCBpbiB5b3VyIGNvbnNvbGUgdG8NCiAgICBzZWUgd2hhdCBpcyBhdmFpbGFibGUuDQoNCi0gICBgZGlhbW9uZHNgIC4gVGhpcyBkYXRhc2V0IGlzIHBhcnQgb2YgdGhlIHRpZHl2ZXJzZSBwYWNrYWdlIHNvIGp1c3QNCiAgICB0eXBlIGBkaWFtb25kc2AgaW4geW91ciBjb2RlIGFuZCB0aGVyZSBpdCBpcy4NCg0KLSAgIGBnYXBtaW5kZXJgICEhIFllcyEhWW91IHdpbGwgbmVlZCB0byBpbnN0YWxsIHRoZSBgZ2FwbWluZGVyYCBwYWNrYWdlDQogICAgdG8gYWNjZXNzIHRoaXMgZGF0YXNldA0KDQotICAgYG1vc2FpY0RhdGFgIHBhY2thZ2UgZGF0YXNldHMuIEluc3RhbGwgYG1vc2FpY0RhdGFgIGZpcnN0ICEhDQoNCi0gICBgZGF0YS53b3JsZGA6IEZpbmQgRGF0YXNldHMgb2YgeW91ciBjaG9pY2U6DQogICAgPGh0dHBzOi8vZG9jcy5kYXRhLndvcmxkL2VuLzY0NDk5LTY0NTE2LVF1aWNrc3RhcnRzLWFuZC10dXRvcmlhbHMuaHRtbD4NCg0KLSAgIGBrYWdnbGVgOiA8aHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cz4NCg0KIyBSZWZlcmVuY2VzDQoNCjEuICBEYXRhIFZpc3VhbGl6YXRpb24gd2l0aCBSLCBbUm9iZXJ0DQogICAgS2FiYWNvZmZdKGh0dHBzOi8vcmthYmFjb2ZmLmdpdGh1Yi5pby9kYXRhdmlzLykNCiAgICAtIEdvb2QgY3Jpc3AgZGVzY3JpcHRpb25zIG9mIG1hbnkga2luZHMgb2YgZ3JhcGhzLCBubyBub25zZW5zZSBib29rLiBBdmFpbGFibGUgZnJlZSBvbiB0aGUgd2ViLg0KDQoyLiAgV2lja2hhbSBhbmQgR3JvbGVtdW5kLCBbUiBmb3IgRGF0YSBTY2llbmNlXShodHRwczovL3I0ZHMuaGFkLmNvLm56LykNCiAgICAtIFIgQmlibGUuIEF2YWlsYWJsZSBmcmVlIG9uIHRoZSB3ZWIuDQoNCjMuICBbVGhlIGJlc3Qgc3RhdHMgeW91J3ZlIGV2ZXIgc2VlbiBcfCBIYW5zDQogICAgUm9zbGluZ10oaHR0cHM6Ly93d3cueW91dHViZS5jb20vd2F0Y2g/dj1oVmltVnpndEQ2dykNCiAgICAtIEluZGlhIERhdGEgUG9ydGFsIDxodHRwczovL2RhdGEuZ292LmluLz4NCg0KQXNrIG1lIGZvciBoZWxwIGFueSB0aW1lIQ0KDQoNCg==