Today, November 2, 2022 RStudio became Posit! (https://posit.co/)

1 Introduction

1.1 Before We Start

  1. We will introduce the basics of the Data Analysis focusing on handling and visualizing data.
  • Handle Data: Importing, Viewing, Tidying, and Transforming
  • Visualize Data
  • Analyse Data using Models

The image above is from R4DS by Hadley Wickham and Garrett Grolemund

  1. Two Keys and One Comment:
  • Data Science is an empirical science rather than theoretical, and we must ‘Learn by Doing’.
  • In every scientific research, we need to keep in mind to replicate the process and results, and we must ‘Keep Records’ to communicate.
  • The Instructor is not an experienced data scientist or a researcher using data analysis, but a beginner and a learner of data science. Let’s learn together!
  1. What do we learn?
  • R, a statistical computing language, using RStudio
  • The tidyverse package attaches ggplot2, purrr, tibble, dplyr, tidyr, stringr, readr, forcats as well.
  • The rmarkdown package to analyze, share and reproduce.

1.2 R, RStudio and Packages

1.2.1 Setup

We need both R and RStudio. If you have not installed R and RStudio, please follow the instruction in ModernDive: Chapter 1 Getting Started with Data in R .

1.2.1.1 R

R is ‘GNU S’, a freely available language and environment for statistical computing and graphics which provides a wide variety of statistical and graphical techniques: linear and nonlinear modelling, statistical tests, time series analysis, classification, clustering, etc.

The R Project for Statistical Computing: https://www.r-project.org

Update R

Currently, R version 4.2.1 (2022-06-23) is running on this system, recommended to update once a year. If you are Windows user, you can use the following code.

if( !("installr" %in% installed.packages()) ){install.packages("installr")}
installr::updateR(TRUE)

System Language

It is more convenient to set the system language of R to be English. You can copy and paste error message for search solutions.

Sys.setenv(LANG = "en")

1.2.1.2 RStudio

RStudio Site: https://www.rstudio.com

Inspired by innovators in science, education, government, and industry, RStudio develops free and open tools for R, and enterprise-ready professional products for teams who use both R and Python, to scale and share their work. (About RStudio: Inspiration)

Update RStudio

Top Menu > Help > Check Updates

1.2.1.3 Packages

R packages are extensions to the R statistical programming language. R packages contain code, data, and documentation in a standardised collection format that can be installed by users of R, typically via a centralised software repository such as CRAN (the Comprehensive R Archive Network).

Install packages

We need two packages, rmarkdown and tidyverse. Install these by the following code. You can also copy install.packages(c("rmarkdown", "tidyverse")) and paste in Console. You can also use Tools > Install Packages in the top menu.

The standard link to an R package is in the form: https://cran.r-project.org/package=<Package Name>.

install.packages(c("rmarkdown", "tidyverse"))

The following code does the same. c("rmarkdown", "tidyverse") is a vector notation in R to concatenate, or combine, two values “rmarkdown”, and “tidyverse”.

install.packages("rmarkdown")
install.packages("tidyverse")

When you use a package you need to attach it by the following code.

library(tidyverse)

Tidyverse is a collection of packages designed with consistency. If you are interested in the undelying philosophy, please take a look at Tidyverse design guide. See the messages of attaching packages and conflicts.

1.2.2 Brief Introduction to R on RStudio

1.2.2.1 Four Panes and Tabs

  1. Top Left: Source Editor
  2. Top Right: Environment, History, etc.
  3. Bottom Left: Console, Terminal, Render, Background Jobs
  4. Bottom Right: Files, Plots, Packages, Help, Viewer, Presentation

1.2.2.2 Three Ways to Run Codes

  1. Console - Bottom Left Pane
  2. R Script - pull down menu under File
  3. R Notebook, R Markdown - pull down menu under File

1.2.2.3 RStudio Cloud

Create an account of RStudio Cloud: https://www.rstudio.com/products/cloud/

GET STARTED FREE

1.2.2.4 R Markdown

We mainly use one of the formats of R Markdwon called R Notebook.

1.2.3 References

2 Introduction to Data Analysis

2.1 First Example

2.1.1 Importing data

Let us assign the iris data in the pre-installed package datasets to df_iris. You can give any name starting from an alphabet, though there are some rules.

df_iris <- datasets::iris
class(df_iris)
[1] "data.frame"

The class of data iris is data.frame, the basic data class of R. You can assign the same data as a tibble, the data class of tidyverse as follows.

tbl_iris <- as_tibble(datasets::iris)
class(tbl_iris)
[1] "tbl_df"     "tbl"        "data.frame"
  • df_iris <- iris can replace df_iris <- datasets::iris because the package datasets is installed and attached as default. Since you may have other data called iris included in a differenct package or you may have changed iris before, it is safer to specify the name of the package with the name of the data.
  • Within R Notebook or in Console, you may get different output, and tf_iris and tbl_iris behave differently. It is because of the default settings of R Markdown.

2.1.2 Look at the data

2.1.2.1 Several ways to view the data.

The View command open up a window to show the contents of the data and you can use the filter as well.

View(df_iris)

The following simple command also shows the data.

df_iris

The output within R Notebook is a tibble style. Try the same command in Console.

slice(df_iris, 1:10)
11+2
[1] 13
1:10
 [1]  1  2  3  4  5  6  7  8  9 10
seq(1,10, by = 2)
[1] 1 3 5 7 9

2.1.2.2 Data Structure

Let us look at the structure of the data. You can try str(df_iris) on Console or by adding a code chunk in R Notebook introducing later.

glimpse(df_iris)
Rows: 150
Columns: 5
$ Sepal.Length <dbl> 5.1, 4.9, 4.7, 4.6, 5.0, 5.4, 4.6, 5.0, 4.4, 4.9, 5.4, 4.8, 4…
$ Sepal.Width  <dbl> 3.5, 3.0, 3.2, 3.1, 3.6, 3.9, 3.4, 3.4, 2.9, 3.1, 3.7, 3.4, 3…
$ Petal.Length <dbl> 1.4, 1.4, 1.3, 1.5, 1.4, 1.7, 1.4, 1.5, 1.4, 1.5, 1.5, 1.6, 1…
$ Petal.Width  <dbl> 0.2, 0.2, 0.2, 0.2, 0.2, 0.4, 0.3, 0.2, 0.2, 0.1, 0.2, 0.2, 0…
$ Species      <fct> setosa, setosa, setosa, setosa, setosa, setosa, setosa, setos…

There are six types of data in R; Double, Integer, Character, Logical, Raw, Complex.

The names after $ are column names. If you call df_iris$Species, you have the Species column. Species is in the 5th collumn, typeof(df_iris[[5]]) does the same as the next. df_iris[2,4] =0.2 is the fourth entry of Sepal.Width.

typeof(df_iris$Species)
[1] "integer"
class(df_iris$Species)
[1] "factor"

For factors = fct see the R Document or an explanation in Factor in R: Categorical Variable & Continuous Variables.

typeof(df_iris$Sepal.Length)
[1] "double"
class(df_iris$Sepal.Length)
[1] "numeric"

Q1. What are the differences ofdf_iris, slice(df_iris, 1:10) and glimpse(df_iris) above?

Q2. What are the differences ofdf_iris, slice(df_iris, 1:10) and glimpse(df_iris) in the console?

2.1.2.3 Summary of the Data

The following is very convenient to get the summary information of a data.

summary(df_iris)
  Sepal.Length    Sepal.Width     Petal.Length    Petal.Width          Species  
 Min.   :4.300   Min.   :2.000   Min.   :1.000   Min.   :0.100   setosa    :50  
 1st Qu.:5.100   1st Qu.:2.800   1st Qu.:1.600   1st Qu.:0.300   versicolor:50  
 Median :5.800   Median :3.000   Median :4.350   Median :1.300   virginica :50  
 Mean   :5.843   Mean   :3.057   Mean   :3.758   Mean   :1.199                  
 3rd Qu.:6.400   3rd Qu.:3.300   3rd Qu.:5.100   3rd Qu.:1.800                  
 Max.   :7.900   Max.   :4.400   Max.   :6.900   Max.   :2.500                  

Minimum, 1st Quadrant (25%), Median, Mean, 3rd Quadrant (75%), Maximum, and the count of each factor.

2.1.3 Visualizing Data

2.1.3.1 Scatter Plot

We use ggplot to draw graphs. The scatter plot is a projection of data with two variables \(x\) and \(y\).

ggplot(data = <data>, aes(x = <column name for x>, y = <column name for y>)) +
  geom_point()
ggplot(data = df_iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point()

2.1.3.2 Scatter Plot with Labels

Add title and labels adding labs(). See Modify axis, legend, and plot labels.

ggplot(data = <data>, aes(x = <column name for x>, y = <column name for y>)) +
  geom_point() +
  labs(title = "Title", x = "Label for x", y = "Label for y")
ggplot(data = df_iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() + 
  labs(title = "Scatter Plot of Sepal Data of Iris", x = "Sepal Length", y = "Sepal Width")

2.1.3.3 Scatter Plot with Colors

Add different colors automatically to each spieces. See Colour related aesthetics: colour, fill, and alpha. Can you see each group?

ggplot(data = df_iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point()

2.1.3.4 Boxplot

The boxplot compactly displays the distribution of a continuous variable. See A box and whiskers plot (in the style of Tukey).

ggplot(data = df_iris, aes(x = Species, y = Sepal.Length)) +
  geom_boxplot()

2.1.3.5 Histogram

Visualize the distribution of a single continuous variable by dividing the x axis into bins and counting the number of observations in each bin. Histograms (geom_histogram()) display the counts with bars. See Histograms and frequency polygons.

ggplot(data = df_iris, aes(x = Sepal.Length)) +
  geom_histogram()
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

Change the number of bins by bins = <number>.

ggplot(data = df_iris, aes(x = Sepal.Length)) +
  geom_histogram(bins = 10)

2.1.4 Various Plots of Density

Three denity plots of the same data.

ggplot(data = df_iris, aes(x = Species, y = Sepal.Length)) +
  geom_violin()

ggplot(data = df_iris, aes(x = Species, y = Sepal.Length)) +
  geom_jitter(width = 0.2)

ggplot(data = df_iris, aes(x = Sepal.Length, fill = Species)) +
  geom_density(alpha = 0.5)

2.1.5 Data Modeling

We will not get into the mathematical models and hypothesis testings. The following is a simple linear regression model and the graph of the fitted line.

ggplot(data = df_iris, aes(x = Sepal.Length, y = Sepal.Width)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)
`geom_smooth()` using formula 'y ~ x'

lm(Sepal.Width ~ Sepal.Length, data = df_iris)

Call:
lm(formula = Sepal.Width ~ Sepal.Length, data = df_iris)

Coefficients:
 (Intercept)  Sepal.Length  
     3.41895      -0.06188  

The formula of the line in the scattered plot is the following. \[y = (-0.06188)x + 3.41895\] The slope is \(-0.06188\) and the \(y\)-intercept is 3.41895.

It is a little strange and counter intuitive, because it claims that as the sepal width gets smaller as the sepal length gets larger. What is hidden behind?

lm(Sepal.Width ~ Sepal.Length, data = df_iris) %>% summary()

Call:
lm(formula = Sepal.Width ~ Sepal.Length, data = df_iris)

Residuals:
    Min      1Q  Median      3Q     Max 
-1.1095 -0.2454 -0.0167  0.2763  1.3338 

Coefficients:
             Estimate Std. Error t value Pr(>|t|)    
(Intercept)   3.41895    0.25356   13.48   <2e-16 ***
Sepal.Length -0.06188    0.04297   -1.44    0.152    
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.4343 on 148 degrees of freedom
Multiple R-squared:  0.01382,   Adjusted R-squared:  0.007159 
F-statistic: 2.074 on 1 and 148 DF,  p-value: 0.1519
ggplot(data = df_iris, aes(x = Sepal.Length, y = Sepal.Width, color = Species)) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE)
`geom_smooth()` using formula 'y ~ x'

ggplot(df_iris, aes(x = Sepal.Length, y = Sepal.Width)) + 
  geom_point(aes(color = Species)) + 
  geom_smooth(aes(color = Species), formula = y ~ x, method = "lm", se = FALSE) +
  geom_smooth(formula =  y ~ x, method = "lm", se = FALSE, color = "black", linetype = "twodash", size = 1.5)

The column Species is a confounding variable. See Wikipedia: Confounding, Confounding Variables | Definition, Examples & Controls.

3 Data Analysis Using RStudio

3.1 Setup

Open RStudio first.

  1. Create a project.
  2. Create a data folder. Not absolutely necessary but recommended.
  3. Create a new R Notebook, R Markdown, or an R Script to keep a record.
  4. Edit YAML of R Notebook or R Markdown if necessary.
  5. Create a code chunk to attach packages, and install packages if necessary.
  6. Find data to analyze.

3.1.1 Create a Project

  1. Choose New Project from File menu.
    You need to save files if you are working with an other project.
  2. Choose New Directory.
  3. Project Type: New Project
  4. Directory Name: Choose or create a directory of a workplace.

3.1.2 Create a Data Folder

Run the following code by clicking the triangle or Run Current Chunk on Top bar. Alternately, you can use New Folder button in the Files tab in the right bottom pane.

dir.create("data")

3.1.3 Create a New R Notebook

Choose R Notebook from the pull down File menu in the top bar.

3.1.4 Edit YAML

Default* is as follows

---
title: "R Notebook"
output: html_notebook
---

Template

---
title: "Title of R Notebook"
author: "ID and Your Name"
date: "2022-11-02" 
output: 
  html_notebook:
#    number_sections: yes
#    toc: true
#    toc_float: true
---
  • Don’t change the format. Indention matters.
  • The statement after # is ignored.
  • Date is automatically inserted when you compile the file.
  • You can replace “2022-11-02” by “2022-11-02” or in any date format surrounded by double quotation marks.
  • Section numbers: - default is number_sections: no.
  • Table of contents, toc: true - default is toc: false.
  • Floating table of contents in HTML output, toc_float: true - default is toc_float: false

3.1.5 Create a Code Chunk to Attach Packages

See the Setup Section Above

Insert Chunk in Code pull down menu in the top bar, or use the C button on top. You can use shortcut keys listed under Tools in the top bar.

library(tidyverse)

3.1.6 Find Data

There are many sites we can get data. Here we learn how to get two types of data, i.e, a data with comma separated values in *.csv, and a Microsoft Excel Data in *.xslx or *.xsl in three ways. We have used a data in a package of R, e.g. datasets::iris, and there are many other data packages of R. However, we focus on how we get so called open public data. The folowing is the definition of Open Data by World Bank.

World Bank:Open Data Defined

The term ``Open Data’’ has a very precise meaning. Data or content is open if anyone is free to use, re-use or redistribute it, subject at most to measures that preserve provenance and openness.
1. The data must be , which means they must be placed in the public domain or under liberal terms of use with minimal restrictions.
2. The data must be , which means they must be published in electronic formats that are machine readable and non-proprietary, so that anyone can access and use the data using common, freely available software tools. Data must also be publicly available and accessible on a public server, without password or firewall restrictions. To make Open Data easier to find, most organizations create and manage Open Data catalogs.

See a list of open public data below.

3.2 Import Data

  1. Use the file link
  2. Read the data in data folder
  3. Use the API, application program interface

3.2.2 Read from the data Folder

object <- read_csv("data/<file name>)

3.2.3 Use the API

Install WDI package for the first time by install.packages("WDI"). See the Setup Section Above.

install.packages("WDI")
library(WDI)

3.2.3.1 Pakcage Site: WDI: World Development Indicators and Other World Bank Data

3.2.3.2 Usage

Try ?WDI in Console or WDI in the Help tab on the right buttom pane.

WDI(country = "all",
    indicator = "NY.GDP.PCAP.KD",
    start = 1960,
    end = 2025,
    extra = FALSE,
    cache = NULL)

Arguments - country: Vector of countries (ISO-2 character codes, e.g. “BR”, “US”, “CA”, or “all”) - indicator: If you supply a named vector, the indicators will be automatically renamed: c('women_private_sector' = 'BI.PWK.PRVS.FE.ZS')

3.2.4 Function WDIsearch

WDIsearch(string = "NY.GDP.PCAP.KD", 
          field = "indicator", cache = NULL)
WDIsearch(string = "NY.GDP.PCAP.KD", 
  field = "indicator", short = FALSE, cache = NULL) 

Default: short = TRUE

WDIsearch(string = "gdp per capita", 
  field = "name", cache = NULL)
WDIsearch(string = "6.0.GDP_current", field = "indicator", short = FALSE)

Another way to find the indicator

Go to the World Bank Open Data site and select Browse by Indicators and find the data. Then the indicator is included in the URL.

CO2 emissions (metric tons per capita)

Indicator: EN.ATM.CO2E.PC

Check the indicator by WDIsearch.

WDIsearch(string = "EN.ATM.CO2E.PC", field = "indicator", short = FALSE)

Further Examples

WDIsearch(string = "population, total", field = "name", short = FALSE)
WDIsearch(string = "SP.POP.TOTL", field = "indicator", short = FALSE)

Exercise. Try the following on Console or in a new code chunk, and choose one interesting data with its indicator, name and description.

  • ?WDIsearch
  • View(WDIsearch(string = "gdp per capita", field = "name", cache = NULL))
  • View(WDIsearch(string = "gdp per capita", field = "name", short = FALSE, cache = NULL))
  • View(WDIsearch(string = "gdp", field = "name", short = FALSE, cache = NULL))
  • View(WDIsearch(string = "gender", field = "name", short = FALSE, cache = NULL))

3.2.5 Download Data by WDI

3.2.5.1 GDP per capita (constant 2015 US$)

Description: GDP per capita is gross domestic product divided by midyear population. GDP is the sum of gross value added by all resident producers in the economy plus any product taxes and minus any subsidies not included in the value of the products. It is calculated without making deductions for depreciation of fabricated assets or for depletion and degradation of natural resources. Data are in constant 2015 U.S. dollars.

gdpcap <- WDI(country = "all", indicator = "NY.GDP.PCAP.KD")
gdpcap

Avoiding downloading the data repeatedly as a CSV file, let us sage it in the data folder created above.

write_csv(gdpcap, "data/gdpcap.csv")

3.2.5.2 GDP per capita and CO2 emission per capita

We can download two data as one data! The GDP per capita data and the CO2 emission per capita data with indicator EN.ATM.CO2E.PC.

Description: Carbon dioxide emissions are those stemming from the burning of fossil fuels and the manufacture of cement. They include carbon dioxide produced during consumption of solid, liquid, and gas fuels and gas flaring.

gdpcap_co2 <- WDI(country = "all", indicator = c("NY.GDP.PCAP.KD", "EN.ATM.CO2E.PC"), extra = TRUE)
gdpcap_co2

Let us save this data as well. It is very large, i.e., 14 columns and 16,492 rows, 1.9MB.

write_csv(gdpcap_co2, "data/gdpcap_co2.csv")

3.3 Wrangle Data

3.3.1 dplyr Overview

dplyr is a grammar of data manipulation, providing a consistent set of verbs that help you solve the most common data manipulation challenges:

  • select() picks variables based on their names.
  • filter() picks cases based on their values.
  • arrange() changes the ordering of the rows.
  • mutate() adds new variables that are functions of existing variables
  • group_by() takes an existing tbl and converts it into a grouped tbl.
  • summarise() reduces multiple values down to a single summary.

You can learn more about them in vignette(“dplyr”). As well as these single-table verbs, dplyr also provides a variety of two-table verbs, which you can learn about in vignette(“two-table”).

If you are new to dplyr, the best place to start is the data transformation chapter in R for data science.

3.3.2 select: Subset columns using their names and types

Helper Function Use Example
- Columns except select(babynames, -prop)
: Columns between (inclusive) select(babynames, year:n)
contains() Columns that contains a string select(babynames, contains(“n”))
ends_with() Columns that ends with a string select(babynames, ends_with(“n”))
matches() Columns that matches a regex select(babynames, matches(“n”))
num_range() Columns with a numerical suffix in the range Not applicable with babynames
one_of() Columns whose name appear in the given set select(babynames, one_of(c(“sex”, “gender”)))
starts_with() Columns that starts with a string select(babynames, starts_with(“n”))

3.3.3 filter: Subset rows using column values

Logical operator tests Example
> Is x greater than y? x > y
>= Is x greater than or equal to y? x >= y
< Is x less than y? x < y
<= Is x less than or equal to y? x <= y
== Is x equal to y? x == y
!= Is x not equal to y? x != y
is.na() Is x an NA? is.na(x)
!is.na() Is x not an NA? !is.na(x)

3.3.4 arrange and Pipe %>%

  • arrange() orders the rows of a data frame by the values of selected columns.

Unlike other dplyr verbs, arrange() largely ignores grouping; you need to explicitly mention grouping variables (or use .by_group = TRUE) in order to group by them, and functions of variables are evaluated once per data frame, not once per group. * [pipes`](https://r4ds.had.co.nz/pipes.html) in R for Data Science.

Examples

arrange(<data>, <varible>)
arrange(<data>, desc(<variable>))

3.3.5 mutate

  • Create, modify, and delete columns

  • Useful mutate functions

    • +, -, log(), etc., for their usual mathematical meanings

    • lead(), lag()

    • dense_rank(), min_rank(), percent_rank(), row_number(), cume_dist(), ntile()

    • cumsum(), cummean(), cummin(), cummax(), cumany(), cumall()

    • na_if(), coalesce()

    • if_else(), recode(), case_when()

3.4 Visualize Data

3.4.1 ggplot2

See https://ggplot2.tidyverse.org/reference/

ggplot(<data>) + geom_point(aes(x = <>, y = <>))
<data> %>% ggplot() + geom_point(aes(x = <>, y = <>))

3.4.1.1 Geoms

  • geom_point(): Points
  • geom_boxplot(): A box plot
  • geom_histogram(): Histograms
  • geom_smooth(): Smoothed conditional means
  • and much more

3.5 Examples

3.5.1 GDP Per Capita

gdpcap <- read_csv("data/gdpcap.csv")
Rows: 16492 Columns: 5
── Column specification ────────────────────────────────────────────────────────────
Delimiter: ","
chr (3): country, iso2c, iso3c
dbl (2): year, NY.GDP.PCAP.KD

ℹ 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.
gdpcap
summary(gdpcap)
   country             iso2c              iso3c                year     
 Length:16492       Length:16492       Length:16492       Min.   :1960  
 Class :character   Class :character   Class :character   1st Qu.:1975  
 Mode  :character   Mode  :character   Mode  :character   Median :1990  
                                                          Mean   :1990  
                                                          3rd Qu.:2006  
                                                          Max.   :2021  
                                                                        
 NY.GDP.PCAP.KD    
 Min.   :   144.2  
 1st Qu.:  1307.1  
 Median :  3463.2  
 Mean   : 10674.3  
 3rd Qu.: 11755.0  
 Max.   :181709.3  
 NA's   :4042      
gdpcap %>% distinct(country, iso2c)
gdpcap %>% filter(country == "World") %>%
  ggplot(aes(x = year, y = NY.GDP.PCAP.KD)) + 
  geom_line()

gdpcap %>% filter(iso2c %in% c("BR", "RU", "IN", "CN")) %>% 
  ggplot(aes(x = year, y = NY.GDP.PCAP.KD, color = country)) + 
  geom_line() +
  labs(title = "GDP per capta of BRICs", y = "GDP per capita (constant 2015 US$)")

3.5.1.1 Exercises

  1. Draw the line graph for ASEAN countries: Brunei, Cambodia, Indonesia, Laos, Malaysia, Myanmar, the Philippines, Singapore, Thailand and Vietnam.
  2. Choose all Low income countries and do the same.
    Hint: use wb_countries above.
(low_countries <- wb_countries %>% filter(income == "Low income"))
(gdpcap_low <- semi_join(gdpcap, low_countries))
Joining, by = c("country", "iso3c")
gdpcap_low %>% 
  ggplot(aes(x = year, y = NY.GDP.PCAP.KD, color = country)) + 
  geom_line() +
  labs(title = "GDP per capta of low income countries", y = "GDP per capita (constant 2015 US$)")
Warning: Removed 439 row(s) containing missing values (geom_path).

3.5.2 CO2 Emission Per Capita

Let us use the saved data.

gdpcap_co2 <- read_csv("data/gdpcap_co2.csv")
Rows: 16492 Columns: 14
── Column specification ────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, NY.GDP.PCAP.KD, EN.ATM.CO2E.PC, longitude, latitude
lgl  (1): status
date (1): lastupdated

ℹ 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.
gdpcap_co2
co2 <- gdpcap_co2 %>% 
  select(country, iso2c, iso3c, year, co2 = EN.ATM.CO2E.PC, region)
co2 %>% filter(country %in% c("France", "Germany", "Italy", "Japan", "United Kingdom", "United States", "Canada", "Russian Federation")) %>% distinct(country, iso2c, iso3c, region)

Successfully chosen data of G8 countries.

co2 %>% filter(country %in% c("France", "Germany", "Italy", "Japan", "United Kingdom", "United States", "Canada", "Russian Federation")) %>% 
  filter(!is.na(co2)) %>%
  ggplot(aes(x = year, y = co2, color = country)) + geom_line() +
  labs(title = "CO2 Emission Per Capita of G8 Countries")

3.5.3 GDP Per Capita vs CO2 Emission Per Capita

Let us use the saved data.

gdpcap_co2 <- read_csv("data/gdpcap_co2.csv")
Rows: 16492 Columns: 14
── Column specification ────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, NY.GDP.PCAP.KD, EN.ATM.CO2E.PC, longitude, latitude
lgl  (1): status
date (1): lastupdated

ℹ 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.
gdpcap_co2
summary(gdpcap_co2)
   country             iso2c              iso3c                year     
 Length:16492       Length:16492       Length:16492       Min.   :1960  
 Class :character   Class :character   Class :character   1st Qu.:1975  
 Mode  :character   Mode  :character   Mode  :character   Median :1990  
                                                          Mean   :1990  
                                                          3rd Qu.:2006  
                                                          Max.   :2021  
                                                                        
  status         lastupdated         NY.GDP.PCAP.KD     EN.ATM.CO2E.PC  
 Mode:logical   Min.   :2022-09-16   Min.   :   144.2   Min.   : 0.000  
 NA's:16492     1st Qu.:2022-09-16   1st Qu.:  1307.1   1st Qu.: 0.674  
                Median :2022-09-16   Median :  3463.2   Median : 2.460  
                Mean   :2022-09-16   Mean   : 10674.3   Mean   : 4.218  
                3rd Qu.:2022-09-16   3rd Qu.: 11755.0   3rd Qu.: 6.209  
                Max.   :2022-09-16   Max.   :181709.3   Max.   :50.954  
                                     NA's   :4042       NA's   :9348    
    region            capital            longitude          latitude      
 Length:16492       Length:16492       Min.   :-175.22   Min.   :-41.286  
 Class :character   Class :character   1st Qu.: -15.18   1st Qu.:  4.174  
 Mode  :character   Mode  :character   Median :  19.54   Median : 17.277  
                                       Mean   :  19.16   Mean   : 18.740  
                                       3rd Qu.:  50.53   3rd Qu.: 39.715  
                                       Max.   : 179.09   Max.   : 64.184  
                                       NA's   :3472      NA's   :3472     
    income            lending         
 Length:16492       Length:16492      
 Class :character   Class :character  
 Mode  :character   Mode  :character  
                                      
                                      
                                      
                                      

Since the most recent year seems to be 2021, let us choose the year.

gdpcap_co2 %>% filter(year == 2021) %>%
  ggplot(aes(x = NY.GDP.PCAP.KD, y = EN.ATM.CO2E.PC)) + 
  geom_point()
Warning: Removed 266 rows containing missing values (geom_point).

What is wrong? There seems to be many missing values. First use filter to choose countries, i.e., non-aggregates and see how many data are in each year.

gdpcap_co2 %>% filter(region != "Aggregates", year == "2021") 

Alternately, we can find the data points in each year except NA, i.e., not available.

gdpcap_co2 %>% filter(region != "Aggregates", !is.na(NY.GDP.PCAP.KD), !is.na(EN.ATM.CO2E.PC)) %>% 
  group_by(year) %>% summarize(n = n())
gdpcap_co2 %>% filter(region != "Aggregates", !is.na(NY.GDP.PCAP.KD), !is.na(EN.ATM.CO2E.PC), year == 2019) %>%
  ggplot(aes(x = NY.GDP.PCAP.KD, y = EN.ATM.CO2E.PC)) + 
  geom_point()

Most of the points are near origin. Let’s try the log-log scale.

gdpcap_co2 %>% filter(region != "Aggregates", !is.na(NY.GDP.PCAP.KD), !is.na(EN.ATM.CO2E.PC), year == 2019) %>%
  ggplot(aes(x = log10(NY.GDP.PCAP.KD), y = log10(EN.ATM.CO2E.PC))) + 
  geom_point() 

gdpco2_2019 <- gdpcap_co2 %>% select(region, year, gdp = NY.GDP.PCAP.KD, co2 = EN.ATM.CO2E.PC) %>%
  filter(region != "Aggregates", !is.na(gdp), !is.na(co2), year == 2019)
gdpco2_2019 %>% ggplot(aes(x = log10(gdp), y = log10(co2))) + 
  geom_point() + 
  labs(title = bquote(~CO[2]~ "Log10 Scale Plot of "~CO[2]~" Emission Per Capita Against GDP Per Capitain 2019"),
       subtitle = bquote(~CO[2]~": metric tons per capita, GDP: constant 2015 US$"),
       x = bquote(~CO[2]~ "Emission Per Capita (Log10))"), y = "GDP Per Capita (Log10)") + 
  geom_smooth(formula = y ~ x, method = "lm", se = FALSE) + 
  geom_smooth(formula = y ~ x, method = "loess", col = "red")

summary(gdpco2_2019)
    region               year           gdp                co2          
 Length:184         Min.   :2019   Min.   :   278.2   Min.   : 0.03699  
 Class :character   1st Qu.:2019   1st Qu.:  1979.2   1st Qu.: 0.80237  
 Mode  :character   Median :2019   Median :  5676.2   Median : 2.78168  
                    Mean   :2019   Mean   : 13487.5   Mean   : 4.15755  
                    3rd Qu.:2019   3rd Qu.: 16122.0   3rd Qu.: 5.59986  
                    Max.   :2019   Max.   :108570.0   Max.   :32.47447  
lm(log10(co2) ~ log10(gdp), data = gdpco2_2019) %>% summary()

Call:
lm(formula = log10(co2) ~ log10(gdp), data = gdpco2_2019)

Residuals:
     Min       1Q   Median       3Q      Max 
-0.78798 -0.18503 -0.03235  0.21130  0.67157 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept) -3.08818    0.13802  -22.38   <2e-16 ***
log10(gdp)   0.90200    0.03624   24.89   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2929 on 182 degrees of freedom
Multiple R-squared:  0.7729,    Adjusted R-squared:  0.7717 
F-statistic: 619.5 on 1 and 182 DF,  p-value: < 2.2e-16
  • Call: The R feature to show what function and parameters were used to create the model.
  • Residuals: Difference between what the model predicted and the actual value of y.
  • Coefficients: The coefficients of a linear model (line) that minimize the sum of the square of the errors.
    \[y = 0.90200 x -3.08818\]
  • Std. Error: Residual Standard Error divided by the square root of the sum of the square of that particular x variable.
  • t value: Estimate divided by Std. Error
  • Pr(>|t|): t value in a T distribution table (with the given degrees of freedom).

There are \(n = 184\) data. So gdp = \(x_1, x_2, \ldots, x_{184}\) and co2 = \(y_1, y_2, \ldots, y_{184}\). In log10 scale, log10(gdp) = \(\log_{10}(x_1), \log_{10}(x_2), \ldots, \log_{10}(x_{184})\) and log10(co2) = \(\log_{10}(y_1), \log_{10}(y_2), \ldots, \log_{10}(y_{184})\), and call them \(x_1', x_2', \ldots, x_{184}'\) and \(y_1', y_2', \ldots, y_{184}'\). The scatter plot is the points \((x_1', y_1'), (x_2', y_2'), \ldots, (x_{184}', y_{184}')\). Let \(\mathrm{mean}(x')\) and \(\mathrm{mean}(y')\) be mean of \(x_1', x_2', \ldots, x_{184}'\) and \(y_1', y_2', \ldots, y_{184}'\).

\[\mathrm{SSxx} = \sum_{i=1}^{184} (x_i' - \mathrm{mean}(x'))^2, \quad \mathrm{SSxy} = \sum_{i=1}^{184} (x_i' - \mathrm{mean}(x'))(y_i' - \mathrm{mean}(y')).\] Then, by theory, we know the slope and the y-inetrcept as follows. \[\mathrm{Slope} = \frac{\mathrm{SSxy}}{\mathrm{SSxx}}, \quad \mathrm{Intercept} = \mathrm{mean}(y') - \mathrm{Slope}\cdot \mathrm{mean}(x').\]

gdpco2log_ssxx <- sum((log10(gdpco2_2019$gdp)- mean(log10(gdpco2_2019$gdp)))^2)
gdpco2log_ssxy <- sum((log10(gdpco2_2019$gdp)- mean(log10(gdpco2_2019$gdp)))*(log10(gdpco2_2019$co2)- mean(log10(gdpco2_2019$co2))))
gdpco2log_slope <- gdpco2log_ssxy/gdpco2log_ssxx
gdpco2log_intercept <- mean(log10(gdpco2_2019$co2)) - gdpco2log_slope*mean(log10(gdpco2_2019$gdp))
c(Intercept = gdpco2log_intercept, Slope = gdpco2log_slope)
 Intercept      Slope 
-3.0881849  0.9019973 

Now consider, the squared difference at \(\log_{10}(x_i)\) between the actual value \(\log_{10}(y_i)\) and the linear estimation \(m\cdot \log_{10}(x_i) + b\). Since it is a square, it is positive. Now take the sum and want to determine \(m\) and \(b\) to minimize the sum, which is called the errors. \[\mathrm{SSE} = \sum_{i=1}^{184} (m\cdot \log_{10}(x_i) + b - \log_{10}(y_i))^2 = \sum_{i=1}^{184} (m\cdot x_i' + b - y_i')^2.\] Let \(k\) be the number of variables for estimation. In our case we estimate using only one variable, we set \(k=1\). The following value is called the Residual Standard Error. \(n-(k+1) = 182\) is called the degree of freedom. \[\mbox{Residual Standard Error} = \sqrt{\frac{\mathrm{SSE}}{n-(k+1)}} = \sqrt{\frac{\mathrm{SSE}}{182}}\]

gdpco2log_model <- lm(log10(co2) ~ log10(gdp), data = gdpco2_2019)
gdpco2log_sse <- sum((log10(gdpco2_2019$co2) - gdpco2log_model$fitted.values)^2)
(gdpco2log_rse <- sqrt(gdpco2log_sse/182))
[1] 0.2928812

The estimated value of \(b\) appears in the row (Intercept) and \(m\) appears in the row log10(gdp). Roughly speaking it is the best fit line against the data points minimizing the sum of the squared distances.

To normalize the scale, we divide the value \(\mathrm{SSyy}\) and consider the following. \[\mathrm{SSyy} = \sum_{i=1}^{184} (\log_{10}(y_i) - \mathrm{mean})^2 = \sum_{i=1}^{184} (y_i'-\mathrm{mean})^2, \quad \mbox{Multiple R Squared} = 1-\frac{\mathrm{SSE}}{\mathrm{SSyy}}\]

gdpco2log_ssyy <- sum((log10(gdpco2_2019$co2)- mean(log10(gdpco2_2019$co2)))^2)
1-gdpco2log_sse/gdpco2log_ssyy
[1] 0.7729313

Conclusion: Roughly speaking, the linear model fits much better than the estimation by means and the model explains about 77% of the scattered points.

4 References

4.1 Data

4.1.1 A List of Open Data Catalogue

4.1.1.1 International Institutions

4.1.1.3 Other Open Public Data

4.2 Books

You can read many excellent books online.

4.3 Websites

4.4 Interactive Exercises

  • RStudio Primers: The following four sets of interactive exercises written using learnr package help you to review and consolidate your understanding of basis of R.
    • The Basics
    • Work with Data
    • Visualize Data
    • Tidy Your Data
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFIiCmRhdGU6ICIyMDIyLTExLTAyIiAjImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICBkZl9wcmludDogcGFnZWQKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KPiBUb2RheSwgTm92ZW1iZXIgMiwgMjAyMiBSU3R1ZGlvIGJlY2FtZSBQb3NpdCEgKGh0dHBzOi8vcG9zaXQuY28vKQoKIyBJbnRyb2R1Y3Rpb24KCiMjIEJlZm9yZSBXZSBTdGFydAoKCjEuIFdlIHdpbGwgaW50cm9kdWNlIHRoZSBiYXNpY3Mgb2YgdGhlIERhdGEgQW5hbHlzaXMgZm9jdXNpbmcgb24gaGFuZGxpbmcgYW5kIHZpc3VhbGl6aW5nIGRhdGEuCiAgLSBIYW5kbGUgRGF0YTogSW1wb3J0aW5nLCBWaWV3aW5nLCBUaWR5aW5nLCBhbmQgVHJhbnNmb3JtaW5nCiAgLSBWaXN1YWxpemUgRGF0YQogIC0gQW5hbHlzZSBEYXRhIHVzaW5nIE1vZGVscwoKIVtfVGhlIGltYWdlIGFib3ZlIGlzIGZyb20gW1I0RFNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnopIGJ5IEhhZGxleSBXaWNraGFtIGFuZCBHYXJyZXR0IEdyb2xlbXVuZF9dKC4vZmlnL2RhdGEtc2NpZW5jZS5wbmcpCiAgCjIuIFR3byBLZXlzIGFuZCBPbmUgQ29tbWVudDoKICAtIERhdGEgU2NpZW5jZSBpcyBhbiAqKmVtcGlyaWNhbCBzY2llbmNlKiogcmF0aGVyIHRoYW4gdGhlb3JldGljYWwsIGFuZCB3ZSBtdXN0ICoqJ0xlYXJuIGJ5IERvaW5nJyoqLgogIC0gSW4gZXZlcnkgc2NpZW50aWZpYyByZXNlYXJjaCwgd2UgbmVlZCB0byBrZWVwIGluIG1pbmQgdG8gKipyZXBsaWNhdGUqKiB0aGUgcHJvY2VzcyBhbmQgcmVzdWx0cywgYW5kIHdlIG11c3QgKionS2VlcCBSZWNvcmRzJyoqIHRvIGNvbW11bmljYXRlLgogIC0gVGhlIEluc3RydWN0b3IgaXMgbm90IGFuIGV4cGVyaWVuY2VkIGRhdGEgc2NpZW50aXN0IG9yIGEgcmVzZWFyY2hlciB1c2luZyBkYXRhIGFuYWx5c2lzLCBidXQgYSBiZWdpbm5lciBhbmQgYSBsZWFybmVyIG9mIGRhdGEgc2NpZW5jZS4gTGV0J3MgbGVhcm4gdG9nZXRoZXIhCiAgCjMuIFdoYXQgZG8gd2UgbGVhcm4/CiAgLSBSLCBhIHN0YXRpc3RpY2FsIGNvbXB1dGluZyBsYW5ndWFnZSwgdXNpbmcgUlN0dWRpbwogIC0gVGhlIGB0aWR5dmVyc2VgIHBhY2thZ2UgYXR0YWNoZXMgYGdncGxvdDJgLCBgcHVycnJgLCBgdGliYmxlYCwgYGRwbHlyYCwgYHRpZHlyYCwgYHN0cmluZ3JgLCBgcmVhZHJgLCBgZm9yY2F0c2AgYXMgd2VsbC4KICAtIFRoZSBgcm1hcmtkb3duYCBwYWNrYWdlIHRvIGFuYWx5emUsIHNoYXJlIGFuZCByZXByb2R1Y2UuCgoKIyMgUiwgUlN0dWRpbyBhbmQgUGFja2FnZXMKCiMjIyBTZXR1cCB7I2ludHJvOnNldHVwfQoKV2UgbmVlZCBib3RoIFIgYW5kIFJTdHVkaW8uIApJZiB5b3UgaGF2ZSBub3QgaW5zdGFsbGVkIFIgYW5kIFJTdHVkaW8sIHBsZWFzZSBmb2xsb3cgdGhlIGluc3RydWN0aW9uIGluIE1vZGVybkRpdmU6IFtDaGFwdGVyIDEgR2V0dGluZyBTdGFydGVkIHdpdGggRGF0YSBpbiBSXShodHRwczovL21vZGVybmRpdmUubmV0bGlmeS5hcHAvMS1nZXR0aW5nLXN0YXJ0ZWQuaHRtbCNnZXR0aW5nLXN0YXJ0ZWQpIC4KCiMjIyMgUgoKPiBSIGlzIOKAmEdOVSBT4oCZLCBhIGZyZWVseSBhdmFpbGFibGUgbGFuZ3VhZ2UgYW5kIGVudmlyb25tZW50IGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgYW5kIGdyYXBoaWNzIHdoaWNoIHByb3ZpZGVzIGEgd2lkZSB2YXJpZXR5IG9mIHN0YXRpc3RpY2FsIGFuZCBncmFwaGljYWwgdGVjaG5pcXVlczogbGluZWFyIGFuZCBub25saW5lYXIgbW9kZWxsaW5nLCBzdGF0aXN0aWNhbCB0ZXN0cywgdGltZSBzZXJpZXMgYW5hbHlzaXMsIGNsYXNzaWZpY2F0aW9uLCBjbHVzdGVyaW5nLCBldGMuIAoKKipUaGUgUiBQcm9qZWN0IGZvciBTdGF0aXN0aWNhbCBDb21wdXRpbmc6KiogaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZwoKKiBUaGUgUiBNYW51YWxzOiBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9tYW51YWxzLmh0bWwKCioqVXBkYXRlIFIqKgoKQ3VycmVudGx5LCBgciBSLnZlcnNpb24uc3RyaW5nYCBpcyBydW5uaW5nIG9uIHRoaXMgc3lzdGVtLCByZWNvbW1lbmRlZCB0byB1cGRhdGUgb25jZSBhIHllYXIuIElmIHlvdSBhcmUgV2luZG93cyB1c2VyLCB5b3UgY2FuIHVzZSB0aGUgZm9sbG93aW5nIGNvZGUuCmBgYHtyIGV2YWwgPSBGQUxTRX0KaWYoICEoImluc3RhbGxyIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKSApe2luc3RhbGwucGFja2FnZXMoImluc3RhbGxyIil9Cmluc3RhbGxyOjp1cGRhdGVSKFRSVUUpCmBgYAoKKipTeXN0ZW0gTGFuZ3VhZ2UqKgoKSXQgaXMgbW9yZSBjb252ZW5pZW50IHRvIHNldCB0aGUgc3lzdGVtIGxhbmd1YWdlIG9mIFIgdG8gYmUgRW5nbGlzaC4gWW91IGNhbiBjb3B5IGFuZCBwYXN0ZSBlcnJvciBtZXNzYWdlIGZvciBzZWFyY2ggc29sdXRpb25zLgpgYGB7ciBldmFsID0gRkFMU0V9ClN5cy5zZXRlbnYoTEFORyA9ICJlbiIpCmBgYAoKIyMjIyBSU3R1ZGlvCgoqKlJTdHVkaW8gU2l0ZToqKiBodHRwczovL3d3dy5yc3R1ZGlvLmNvbQoKPiBJbnNwaXJlZCBieSBpbm5vdmF0b3JzIGluIHNjaWVuY2UsIGVkdWNhdGlvbiwgZ292ZXJubWVudCwgYW5kIGluZHVzdHJ5LCBSU3R1ZGlvIGRldmVsb3BzIGZyZWUgYW5kIG9wZW4gdG9vbHMgZm9yIFIsIGFuZCBlbnRlcnByaXNlLXJlYWR5IHByb2Zlc3Npb25hbCBwcm9kdWN0cyBmb3IgdGVhbXMgd2hvIHVzZSBib3RoIFIgYW5kIFB5dGhvbiwgdG8gc2NhbGUgYW5kIHNoYXJlIHRoZWlyIHdvcmsuIChbQWJvdXQgUlN0dWRpbzogSW5zcGlyYXRpb25dKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL2Fib3V0LykpCgoKKipVcGRhdGUgUlN0dWRpbyoqCgpUb3AgTWVudSA+IEhlbHAgPiBDaGVjayBVcGRhdGVzCgojIyMjIFBhY2thZ2VzIAoKPiBSIHBhY2thZ2VzIGFyZSBleHRlbnNpb25zIHRvIHRoZSBSIHN0YXRpc3RpY2FsIHByb2dyYW1taW5nIGxhbmd1YWdlLiBSIHBhY2thZ2VzIGNvbnRhaW4gY29kZSwgZGF0YSwgYW5kIGRvY3VtZW50YXRpb24gaW4gYSBzdGFuZGFyZGlzZWQgY29sbGVjdGlvbiBmb3JtYXQgdGhhdCBjYW4gYmUgaW5zdGFsbGVkIGJ5IHVzZXJzIG9mIFIsIHR5cGljYWxseSB2aWEgYSBjZW50cmFsaXNlZCBzb2Z0d2FyZSByZXBvc2l0b3J5IHN1Y2ggYXMgQ1JBTiAodGhlIENvbXByZWhlbnNpdmUgUiBBcmNoaXZlIE5ldHdvcmspLgoKKipJbnN0YWxsIHBhY2thZ2VzKioKCldlIG5lZWQgdHdvIHBhY2thZ2VzLCBgcm1hcmtkb3duYCBhbmQgYHRpZHl2ZXJzZWAuIEluc3RhbGwgdGhlc2UgYnkgdGhlIGZvbGxvd2luZyBjb2RlLiBZb3UgY2FuIGFsc28gY29weSBgaW5zdGFsbC5wYWNrYWdlcyhjKCJybWFya2Rvd24iLCAidGlkeXZlcnNlIikpYCBhbmQgcGFzdGUgaW4gQ29uc29sZS4gWW91IGNhbiBhbHNvIHVzZSBUb29scyA+IEluc3RhbGwgUGFja2FnZXMgaW4gdGhlIHRvcCBtZW51LgoKKiBgdGlkeXZlcnNlYDogaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZywgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT10aWR5dmVyc2UKKiBgcm1hcmtkb3duYDogaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20sIGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9cm1hcmtkb3duCgpUaGUgc3RhbmRhcmQgbGluayB0byBhbiBSIHBhY2thZ2UgaXMgaW4gdGhlIGZvcm06IGBodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPTxQYWNrYWdlIE5hbWU+YC4gCgpgYGB7ciBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoYygicm1hcmtkb3duIiwgInRpZHl2ZXJzZSIpKQpgYGAKClRoZSBmb2xsb3dpbmcgY29kZSBkb2VzIHRoZSBzYW1lLiBgYygicm1hcmtkb3duIiwgInRpZHl2ZXJzZSIpYCBpcyBhIHZlY3RvciBub3RhdGlvbiBpbiBSIHRvIGNvbmNhdGVuYXRlLCBvciBjb21iaW5lLCB0d28gdmFsdWVzICJybWFya2Rvd24iLCAgYW5kICJ0aWR5dmVyc2UiLgpgYGB7ciBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoInJtYXJrZG93biIpCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCmBgYAoKV2hlbiB5b3UgdXNlIGEgcGFja2FnZSB5b3UgbmVlZCB0byBhdHRhY2ggaXQgYnkgdGhlIGZvbGxvd2luZyBjb2RlLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKClRpZHl2ZXJzZSBpcyBhIGNvbGxlY3Rpb24gb2YgcGFja2FnZXMgZGVzaWduZWQgd2l0aCBjb25zaXN0ZW5jeS4gSWYgeW91IGFyZSBpbnRlcmVzdGVkIGluIHRoZSB1bmRlbHlpbmcgcGhpbG9zb3BoeSwgcGxlYXNlIHRha2UgYSBsb29rIGF0IFtUaWR5dmVyc2UgZGVzaWduIGd1aWRlXShodHRwczovL2Rlc2lnbi50aWR5dmVyc2Uub3JnKS4gU2VlIHRoZSBtZXNzYWdlcyBvZiBhdHRhY2hpbmcgcGFja2FnZXMgYW5kIGNvbmZsaWN0cy4KCiMjIyBCcmllZiBJbnRyb2R1Y3Rpb24gdG8gUiBvbiBSU3R1ZGlvCgojIyMjIEZvdXIgUGFuZXMgYW5kIFRhYnMKCjEuIFRvcCBMZWZ0OiBTb3VyY2UgRWRpdG9yCjIuIFRvcCBSaWdodDogRW52aXJvbm1lbnQsIEhpc3RvcnksIGV0Yy4KMy4gQm90dG9tIExlZnQ6IENvbnNvbGUsIFRlcm1pbmFsLCBSZW5kZXIsIEJhY2tncm91bmQgSm9icwo0LiBCb3R0b20gUmlnaHQ6IEZpbGVzLCBQbG90cywgUGFja2FnZXMsIEhlbHAsIFZpZXdlciwgUHJlc2VudGF0aW9uCgojIyMjIFRocmVlIFdheXMgdG8gUnVuIENvZGVzCgoxLiBDb25zb2xlIC0gQm90dG9tIExlZnQgUGFuZQoyLiBSIFNjcmlwdCAtIHB1bGwgZG93biBtZW51IHVuZGVyIEZpbGUKMy4gUiBOb3RlYm9vaywgUiBNYXJrZG93biAtIHB1bGwgZG93biBtZW51IHVuZGVyIEZpbGUKCiMjIyMgUlN0dWRpbyBDbG91ZAoKQ3JlYXRlIGFuIGFjY291bnQgb2YgUlN0dWRpbyBDbG91ZDogaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvY2xvdWQvCgpHRVQgU1RBUlRFRCBGUkVFCgoqIFNoYXJlIFJTdHVkaW8gQ2xvdWQKICAtIFRoZSBmb2xsb3dpbmcgaXMgdGhlIFVSTCBvZiB0aGUgd29ya3NwYWNlIG9mIHRoaXMgcHJlc2VudGFpb246IGh0dHBzOi8vcnN0dWRpby5jbG91ZC9jb250ZW50LzQ4NTg5NDgKICAtIEZpcnN0IGxvZ2luIHRvIHlvdXIgUlN0dWRpbyBDbG91ZCBhY2NvdW50IGFuZCB0aGVuIG9wZW4gdGhlIGxpbmsgYWJvdmUuCiAgLSBJZiB5b3Ugd2FudCB0byB3b3JrIG9uIGl0IHlvdSBjYW4gIlNhdmUgYSBQZXJtYW5lbnQgQ29weSIgZnJvbSB0aGUgdG9wIGJhci4gCiogW1JTdHVkaW8gUHJpbWVyc10oaHR0cHM6Ly9yc3R1ZGlvLmNsb3VkL2xlYXJuL3ByaW1lcnMpCiogW0NoZWF0c2hlZXRzXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9yZXNvdXJjZXMvY2hlYXRzaGVldHMvKQogIAojIyMjIFIgTWFya2Rvd24KCldlIG1haW5seSB1c2Ugb25lIG9mIHRoZSBmb3JtYXRzIG9mIFIgTWFya2R3b24gY2FsbGVkIFIgTm90ZWJvb2suCgoqIEFuIGludHJvZHVjdGlvbiBpcyBpbiBbUlN0dWRpbyBQcmltZXJzOiBSZXBvcnQgUmVwcm9kdWNpYmx5XShodHRwczovL3JzdHVkaW8uY2xvdWQvbGVhcm4vcHJpbWVycykKICAtIFtXaGF0IGlzIFIgTWFya2Rvd25dKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tL2xlc3Nvbi0xLmh0bWwpCgojIyMgUmVmZXJlbmNlcwoKKiBNb2Rlcm5EaXZlOiBbQ2hhcHRlciAxIEdldHRpbmcgU3RhcnRlZCB3aXRoIERhdGEgaW4gUl0oaHR0cHM6Ly9tb2Rlcm5kaXZlLm5ldGxpZnkuYXBwLzEtZ2V0dGluZy1zdGFydGVkLmh0bWwjZ2V0dGluZy1zdGFydGVkKSAKKiBSLUxhZGllcyBTeWRuZXk6IFtCYXNpY0Jhc2ljc10oaHR0cHM6Ly9ybGFkaWVzc3lkbmV5Lm9yZy9jb3Vyc2VzL3J5b3V3aXRobWUvMDEtYmFzaWNiYXNpY3MtMS8pCgoKIyBJbnRyb2R1Y3Rpb24gdG8gRGF0YSBBbmFseXNpcwoKIyMgRmlyc3QgRXhhbXBsZQoKIyMjIEltcG9ydGluZyBkYXRhCgpMZXQgdXMgYXNzaWduIHRoZSBgaXJpc2AgZGF0YSBpbiB0aGUgcHJlLWluc3RhbGxlZCBwYWNrYWdlIGBkYXRhc2V0c2AgdG8gYGRmX2lyaXNgLiBZb3UgY2FuIGdpdmUgYW55IG5hbWUgc3RhcnRpbmcgZnJvbSBhbiBhbHBoYWJldCwgdGhvdWdoIHRoZXJlIGFyZSBzb21lIHJ1bGVzLiAKYGBge3J9CmRmX2lyaXMgPC0gZGF0YXNldHM6OmlyaXMKY2xhc3MoZGZfaXJpcykKYGBgCgpUaGUgY2xhc3Mgb2YgZGF0YSBgaXJpc2AgaXMgYGRhdGEuZnJhbWVgLCB0aGUgYmFzaWMgZGF0YSBjbGFzcyBvZiBSLiBZb3UgY2FuIGFzc2lnbiB0aGUgc2FtZSBkYXRhIGFzIGEgYHRpYmJsZWAsIHRoZSBkYXRhIGNsYXNzIG9mIGB0aWR5dmVyc2VgIGFzIGZvbGxvd3MuCmBgYHtyfQp0YmxfaXJpcyA8LSBhc190aWJibGUoZGF0YXNldHM6OmlyaXMpCmNsYXNzKHRibF9pcmlzKQpgYGAKCiogYGRmX2lyaXMgPC0gaXJpc2AgY2FuIHJlcGxhY2UgIGBkZl9pcmlzIDwtIGRhdGFzZXRzOjppcmlzYCBiZWNhdXNlIHRoZSBwYWNrYWdlIGBkYXRhc2V0c2AgaXMgaW5zdGFsbGVkIGFuZCBhdHRhY2hlZCBhcyBkZWZhdWx0LiBTaW5jZSB5b3UgbWF5IGhhdmUgb3RoZXIgZGF0YSBjYWxsZWQgYGlyaXNgIGluY2x1ZGVkIGluIGEgZGlmZmVyZW5jdCBwYWNrYWdlIG9yIHlvdSBtYXkgaGF2ZSBjaGFuZ2VkIGBpcmlzYCBiZWZvcmUsIGl0IGlzIHNhZmVyIHRvIHNwZWNpZnkgdGhlIG5hbWUgb2YgdGhlIHBhY2thZ2Ugd2l0aCB0aGUgbmFtZSBvZiB0aGUgZGF0YS4KKiBXaXRoaW4gUiBOb3RlYm9vayBvciBpbiBDb25zb2xlLCB5b3UgbWF5IGdldCBkaWZmZXJlbnQgb3V0cHV0LCBhbmQgYHRmX2lyaXNgIGFuZCBgdGJsX2lyaXNgIGJlaGF2ZSBkaWZmZXJlbnRseS4gSXQgaXMgYmVjYXVzZSBvZiB0aGUgZGVmYXVsdCBzZXR0aW5ncyBvZiBSIE1hcmtkb3duLiAKCiMjIyBMb29rIGF0IHRoZSBkYXRhCgojIyMjIFNldmVyYWwgd2F5cyB0byB2aWV3IHRoZSBkYXRhLgoKVGhlIGBWaWV3YCBjb21tYW5kIG9wZW4gdXAgYSB3aW5kb3cgdG8gc2hvdyB0aGUgY29udGVudHMgb2YgdGhlIGRhdGEgYW5kIHlvdSBjYW4gdXNlIHRoZSBmaWx0ZXIgYXMgd2VsbC4KYGBge3Igdmlld2lyaXMsIGV2YWwgPSBGQUxTRX0KVmlldyhkZl9pcmlzKQpgYGAKClRoZSBmb2xsb3dpbmcgc2ltcGxlIGNvbW1hbmQgYWxzbyBzaG93cyB0aGUgZGF0YS4gCmBgYHtyIGRmaXJpc30KZGZfaXJpcwpgYGAKClRoZSBvdXRwdXQgd2l0aGluIFIgTm90ZWJvb2sgaXMgYSB0aWJibGUgc3R5bGUuIFRyeSB0aGUgc2FtZSBjb21tYW5kIGluIENvbnNvbGUuCmBgYHtyIHNsaWNlMTBpcmlzfQpzbGljZShkZl9pcmlzLCAxOjEwKQpgYGAKYGBge3IgZWxldmVucGx1c3R3b30KMTErMgpgYGAKCmBgYHtyIG9uZTJ0ZW59CjE6MTAKYGBgCgpgYGB7ciBzZXF1ZW5jZX0Kc2VxKDEsMTAsIGJ5ID0gMikKYGBgCgoKIyMjIyBEYXRhIFN0cnVjdHVyZQoKTGV0IHVzIGxvb2sgYXQgdGhlIHN0cnVjdHVyZSBvZiB0aGUgZGF0YS4gWW91IGNhbiB0cnkgYHN0cihkZl9pcmlzKWAgb24gQ29uc29sZSBvciBieSBhZGRpbmcgYSBjb2RlIGNodW5rIGluIFIgTm90ZWJvb2sgaW50cm9kdWNpbmcgbGF0ZXIuCgpgYGB7ciBnbGltcHNlOmVpcmlzfQpnbGltcHNlKGRmX2lyaXMpCmBgYApUaGVyZSBhcmUgc2l4IHR5cGVzIG9mIGRhdGEgaW4gUjsgRG91YmxlLCBJbnRlZ2VyLCBDaGFyYWN0ZXIsIExvZ2ljYWwsIFJhdywgQ29tcGxleC4KClRoZSBuYW1lcyBhZnRlciAkIGFyZSBjb2x1bW4gbmFtZXMuIElmIHlvdSBjYWxsIGBkZl9pcmlzJFNwZWNpZXNgLCB5b3UgaGF2ZSB0aGUgU3BlY2llcyBjb2x1bW4uIFNwZWNpZXMgaXMgaW4gdGhlIDV0aCBjb2xsdW1uLCBgdHlwZW9mKGRmX2lyaXNbWzVdXSlgIGRvZXMgdGhlIHNhbWUgYXMgdGhlIG5leHQuIGBkZl9pcmlzWzIsNF0gPSBgYHIgZGZfaXJpc1syLDRdYCBpcyB0aGUgZm91cnRoIGVudHJ5IG9mIFNlcGFsLldpZHRoLgpgYGB7cn0KdHlwZW9mKGRmX2lyaXMkU3BlY2llcykKYGBgCgpgYGB7cn0KY2xhc3MoZGZfaXJpcyRTcGVjaWVzKQpgYGAKCkZvciBgZmFjdG9ycyA9IGZjdGAgc2VlIFt0aGUgUiBEb2N1bWVudF0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2UvdmVyc2lvbnMvMy42LjIvdG9waWNzL2ZhY3Rvcikgb3IgYW4gZXhwbGFuYXRpb24gaW4gW0ZhY3RvciBpbiBSOiBDYXRlZ29yaWNhbCBWYXJpYWJsZSAmIENvbnRpbnVvdXMgVmFyaWFibGVzXShodHRwczovL3d3dy5ndXJ1OTkuY29tL3ItZmFjdG9yLWNhdGVnb3JpY2FsLWNvbnRpbnVvdXMuaHRtbCkuCgpgYGB7cn0KdHlwZW9mKGRmX2lyaXMkU2VwYWwuTGVuZ3RoKQpjbGFzcyhkZl9pcmlzJFNlcGFsLkxlbmd0aCkKYGBgCgoKKipRMS4qKiBXaGF0IGFyZSB0aGUgZGlmZmVyZW5jZXMgb2ZgZGZfaXJpc2AsIGBzbGljZShkZl9pcmlzLCAxOjEwKWAgYW5kIGBnbGltcHNlKGRmX2lyaXMpYCBhYm92ZT8KCioqUTIuKiogV2hhdCBhcmUgdGhlIGRpZmZlcmVuY2VzIG9mYGRmX2lyaXNgLCBgc2xpY2UoZGZfaXJpcywgMToxMClgIGFuZCBgZ2xpbXBzZShkZl9pcmlzKWAgaW4gdGhlIGNvbnNvbGU/CgojIyMjIFN1bW1hcnkgb2YgdGhlIERhdGEKClRoZSBmb2xsb3dpbmcgaXMgdmVyeSBjb252ZW5pZW50IHRvIGdldCB0aGUgc3VtbWFyeSBpbmZvcm1hdGlvbiBvZiBhIGRhdGEuCmBgYHtyfQpzdW1tYXJ5KGRmX2lyaXMpCmBgYApNaW5pbXVtLCAxc3QgUXVhZHJhbnQgKDI1JSksICBNZWRpYW4sIE1lYW4sIDNyZCBRdWFkcmFudCAoNzUlKSwgTWF4aW11bSwgYW5kIHRoZSBjb3VudCBvZiBlYWNoIGZhY3Rvci4KCiMjIyBWaXN1YWxpemluZyBEYXRhCgojIyMjIFNjYXR0ZXIgUGxvdAoKV2UgdXNlIGBnZ3Bsb3RgIHRvIGRyYXcgZ3JhcGhzLiBUaGUgc2NhdHRlciBwbG90IGlzIGEgcHJvamVjdGlvbiBvZiBkYXRhIHdpdGggdHdvIHZhcmlhYmxlcyAkeCQgYW5kICR5JC4gCmBgYApnZ3Bsb3QoZGF0YSA9IDxkYXRhPiwgYWVzKHggPSA8Y29sdW1uIG5hbWUgZm9yIHg+LCB5ID0gPGNvbHVtbiBuYW1lIGZvciB5PikpICsKICBnZW9tX3BvaW50KCkKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkZl9pcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoKSkgKwogIGdlb21fcG9pbnQoKQpgYGAKIyMjIyBTY2F0dGVyIFBsb3Qgd2l0aCBMYWJlbHMKCkFkZCB0aXRsZSBhbmQgbGFiZWxzIGFkZGluZyBgbGFicygpYC4gU2VlIFsKTW9kaWZ5IGF4aXMsIGxlZ2VuZCwgYW5kIHBsb3QgbGFiZWxzXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvbGFicy5odG1sKS4KYGBgCmdncGxvdChkYXRhID0gPGRhdGE+LCBhZXMoeCA9IDxjb2x1bW4gbmFtZSBmb3IgeD4sIHkgPSA8Y29sdW1uIG5hbWUgZm9yIHk+KSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh0aXRsZSA9ICJUaXRsZSIsIHggPSAiTGFiZWwgZm9yIHgiLCB5ID0gIkxhYmVsIGZvciB5IikKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRmX2lyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgpKSArCiAgZ2VvbV9wb2ludCgpICsgCiAgbGFicyh0aXRsZSA9ICJTY2F0dGVyIFBsb3Qgb2YgU2VwYWwgRGF0YSBvZiBJcmlzIiwgeCA9ICJTZXBhbCBMZW5ndGgiLCB5ID0gIlNlcGFsIFdpZHRoIikKYGBgCgojIyMjIFNjYXR0ZXIgUGxvdCB3aXRoIENvbG9ycwoKQWRkIGRpZmZlcmVudCBjb2xvcnMgYXV0b21hdGljYWxseSB0byBlYWNoIHNwaWVjZXMuIFNlZSBbQ29sb3VyIHJlbGF0ZWQgYWVzdGhldGljczogY29sb3VyLCBmaWxsLCBhbmQgYWxwaGFdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hZXNfY29sb3VyX2ZpbGxfYWxwaGEuaHRtbCkuIENhbiB5b3Ugc2VlIGVhY2ggZ3JvdXA/CmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRmX2lyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpICsKICBnZW9tX3BvaW50KCkKYGBgCiMjIyMgQm94cGxvdAoKVGhlIGJveHBsb3QgY29tcGFjdGx5IGRpc3BsYXlzIHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlLiBTZWUgW0EgYm94IGFuZCB3aGlza2VycyBwbG90IChpbiB0aGUgc3R5bGUgb2YgVHVrZXkpXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9ib3hwbG90Lmh0bWwpLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkZl9pcmlzLCBhZXMoeCA9IFNwZWNpZXMsIHkgPSBTZXBhbC5MZW5ndGgpKSArCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgojIyMjIEhpc3RvZ3JhbQoKVmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgYSBzaW5nbGUgY29udGludW91cyB2YXJpYWJsZSBieSBkaXZpZGluZyB0aGUgeCBheGlzIGludG8gYmlucyBhbmQgY291bnRpbmcgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgaW4gZWFjaCBiaW4uIEhpc3RvZ3JhbXMgKGdlb21faGlzdG9ncmFtKCkpIGRpc3BsYXkgdGhlIGNvdW50cyB3aXRoIGJhcnMuIFNlZSBbSGlzdG9ncmFtcyBhbmQgZnJlcXVlbmN5IHBvbHlnb25zXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZ2VvbV9oaXN0b2dyYW0uaHRtbCkuCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRmX2lyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoKSkgKwogIGdlb21faGlzdG9ncmFtKCkKYGBgCkNoYW5nZSB0aGUgbnVtYmVyIG9mIGJpbnMgYnkgYGJpbnMgPWAgYDxudW1iZXI+YC4KYGBge3J9CmdncGxvdChkYXRhID0gZGZfaXJpcywgYWVzKHggPSBTZXBhbC5MZW5ndGgpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDEwKQpgYGAKCiMjIyBWYXJpb3VzIFBsb3RzIG9mIERlbnNpdHkKClRocmVlIGRlbml0eSBwbG90cyBvZiB0aGUgc2FtZSBkYXRhLgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkZl9pcmlzLCBhZXMoeCA9IFNwZWNpZXMsIHkgPSBTZXBhbC5MZW5ndGgpKSArCiAgZ2VvbV92aW9saW4oKQpgYGAKCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRmX2lyaXMsIGFlcyh4ID0gU3BlY2llcywgeSA9IFNlcGFsLkxlbmd0aCkpICsKICBnZW9tX2ppdHRlcih3aWR0aCA9IDAuMikKYGBgCgpgYGB7cn0KZ2dwbG90KGRhdGEgPSBkZl9pcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgZmlsbCA9IFNwZWNpZXMpKSArCiAgZ2VvbV9kZW5zaXR5KGFscGhhID0gMC41KQpgYGAKCiMjIyBEYXRhIE1vZGVsaW5nIAoKV2Ugd2lsbCBub3QgZ2V0IGludG8gdGhlIG1hdGhlbWF0aWNhbCBtb2RlbHMgYW5kIGh5cG90aGVzaXMgdGVzdGluZ3MuIFRoZSBmb2xsb3dpbmcgaXMgYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwgYW5kIHRoZSBncmFwaCBvZiB0aGUgZml0dGVkIGxpbmUuCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRmX2lyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKQpgYGAKYGBge3J9CmxtKFNlcGFsLldpZHRoIH4gU2VwYWwuTGVuZ3RoLCBkYXRhID0gZGZfaXJpcykKYGBgCgpUaGUgZm9ybXVsYSBvZiB0aGUgbGluZSBpbiB0aGUgc2NhdHRlcmVkIHBsb3QgaXMgdGhlIGZvbGxvd2luZy4KJCR5ID0gKC0wLjA2MTg4KXggKyAzLjQxODk1JCQKVGhlIHNsb3BlIGlzICQtMC4wNjE4OCQgYW5kIHRoZSAkeSQtaW50ZXJjZXB0IGlzIDMuNDE4OTUuIAoKSXQgaXMgYSBsaXR0bGUgc3RyYW5nZSBhbmQgY291bnRlciBpbnR1aXRpdmUsIGJlY2F1c2UgaXQgY2xhaW1zIHRoYXQgYXMgdGhlIHNlcGFsIHdpZHRoIGdldHMgc21hbGxlciBhcyB0aGUgc2VwYWwgbGVuZ3RoIGdldHMgbGFyZ2VyLiBXaGF0IGlzIGhpZGRlbiBiZWhpbmQ/CmBgYHtyfQpsbShTZXBhbC5XaWR0aCB+IFNlcGFsLkxlbmd0aCwgZGF0YSA9IGRmX2lyaXMpICU+JSBzdW1tYXJ5KCkKYGBgCmBgYHtyfQpnZ3Bsb3QoZGF0YSA9IGRmX2lyaXMsIGFlcyh4ID0gU2VwYWwuTGVuZ3RoLCB5ID0gU2VwYWwuV2lkdGgsIGNvbG9yID0gU3BlY2llcykpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCmBgYApgYGB7ciBpcmlzOnNlcGFsOmNvbmZvdW5kZXJ9CmdncGxvdChkZl9pcmlzLCBhZXMoeCA9IFNlcGFsLkxlbmd0aCwgeSA9IFNlcGFsLldpZHRoKSkgKyAKICBnZW9tX3BvaW50KGFlcyhjb2xvciA9IFNwZWNpZXMpKSArIAogIGdlb21fc21vb3RoKGFlcyhjb2xvciA9IFNwZWNpZXMpLCBmb3JtdWxhID0geSB+IHgsIG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICBnZW9tX3Ntb290aChmb3JtdWxhID0gIHkgfiB4LCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFLCBjb2xvciA9ICJibGFjayIsIGxpbmV0eXBlID0gInR3b2Rhc2giLCBzaXplID0gMS41KQpgYGAKVGhlIGNvbHVtbiBTcGVjaWVzIGlzIGEgY29uZm91bmRpbmcgdmFyaWFibGUuIFNlZSBbV2lraXBlZGlhOiBDb25mb3VuZGluZ10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQ29uZm91bmRpbmcpLCBbQ29uZm91bmRpbmcgVmFyaWFibGVzIHwgRGVmaW5pdGlvbiwgRXhhbXBsZXMgJiBDb250cm9sc10oaHR0cHM6Ly93d3cuc2NyaWJici5jb20vbWV0aG9kb2xvZ3kvY29uZm91bmRpbmctdmFyaWFibGVzLykuCgojIERhdGEgQW5hbHlzaXMgVXNpbmcgUlN0dWRpbwoKIyMgU2V0dXAKCk9wZW4gUlN0dWRpbyBmaXJzdC4KCjEuIENyZWF0ZSBhIHByb2plY3QuCjIuIENyZWF0ZSBhIGRhdGEgZm9sZGVyLiBOb3QgYWJzb2x1dGVseSBuZWNlc3NhcnkgYnV0IHJlY29tbWVuZGVkLgozLiBDcmVhdGUgYSBuZXcgUiBOb3RlYm9vaywgUiBNYXJrZG93biwgb3IgYW4gUiBTY3JpcHQgdG8ga2VlcCBhIHJlY29yZC4KNC4gRWRpdCBZQU1MIG9mIFIgTm90ZWJvb2sgb3IgUiBNYXJrZG93biBpZiBuZWNlc3NhcnkuCjUuIENyZWF0ZSBhIGNvZGUgY2h1bmsgdG8gYXR0YWNoIHBhY2thZ2VzLCBhbmQgaW5zdGFsbCBwYWNrYWdlcyBpZiBuZWNlc3NhcnkuCjYuIEZpbmQgZGF0YSB0byBhbmFseXplLgoKIyMjIENyZWF0ZSBhIFByb2plY3QKCjEuIENob29zZSBfTmV3IFByb2plY3RfIGZyb20gRmlsZSBtZW51LiAgCiAgWW91IG5lZWQgdG8gc2F2ZSBmaWxlcyBpZiB5b3UgYXJlIHdvcmtpbmcgd2l0aCBhbiBvdGhlciBwcm9qZWN0LgoyLiBDaG9vc2UgX05ldyBEaXJlY3RvcnlfLgozLiBQcm9qZWN0IFR5cGU6IF9OZXcgUHJvamVjdF8KNC4gRGlyZWN0b3J5IE5hbWU6IENob29zZSBvciBjcmVhdGUgYSBkaXJlY3Rvcnkgb2YgYSB3b3JrcGxhY2UuCgojIyMgQ3JlYXRlIGEgRGF0YSBGb2xkZXIKClJ1biB0aGUgZm9sbG93aW5nIGNvZGUgYnkgY2xpY2tpbmcgdGhlIHRyaWFuZ2xlIG9yIFJ1biBDdXJyZW50IENodW5rIG9uIFRvcCBiYXIuCkFsdGVybmF0ZWx5LCB5b3UgY2FuIHVzZSBOZXcgRm9sZGVyIGJ1dHRvbiBpbiB0aGUgRmlsZXMgdGFiIGluIHRoZSByaWdodCBib3R0b20gcGFuZS4KYGBge3IgY3JlYXRlLWRpcnMsIGV2YWwgPSBGQUxTRX0KZGlyLmNyZWF0ZSgiZGF0YSIpCmBgYAoKIyMjIENyZWF0ZSBhIE5ldyBSIE5vdGVib29rCgpDaG9vc2UgUiBOb3RlYm9vayBmcm9tIHRoZSBwdWxsIGRvd24gRmlsZSBtZW51IGluIHRoZSB0b3AgYmFyLgoKIyMjIEVkaXQgWUFNTAoKKipEZWZhdWx0KiBpcyBhcyBmb2xsb3dzKioKCmBgYAotLS0KdGl0bGU6ICJSIE5vdGVib29rIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCmBgYAoKKipUZW1wbGF0ZSoqCgpgYGAKLS0tCnRpdGxlOiAiVGl0bGUgb2YgUiBOb3RlYm9vayIKYXV0aG9yOiAiSUQgYW5kIFlvdXIgTmFtZSIKZGF0ZTogImByIFN5cy5EYXRlKClgIiAKb3V0cHV0OiAKICBodG1sX25vdGVib29rOgojICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiMgICAgdG9jOiB0cnVlCiMgICAgdG9jX2Zsb2F0OiB0cnVlCi0tLQpgYGAKCiogRG9uJ3QgY2hhbmdlIHRoZSBmb3JtYXQuIEluZGVudGlvbiBtYXR0ZXJzLgoqIFRoZSBzdGF0ZW1lbnQgYWZ0ZXIgXCMgaXMgaWdub3JlZC4KKiBEYXRlIGlzIGF1dG9tYXRpY2FsbHkgaW5zZXJ0ZWQgd2hlbiB5b3UgY29tcGlsZSB0aGUgZmlsZS4KKiBZb3UgY2FuIHJlcGxhY2UgImByIFN5cy5EYXRlKClgIiBieSAiMjAyMi0xMS0wMiIgb3IgaW4gYW55IGRhdGUgZm9ybWF0IHN1cnJvdW5kZWQgYnkgZG91YmxlIHF1b3RhdGlvbiBtYXJrcy4KKiBTZWN0aW9uIG51bWJlcnM6IC0gZGVmYXVsdCBpcyBgbnVtYmVyX3NlY3Rpb25zOiBub2AuCiogVGFibGUgb2YgY29udGVudHMsIGB0b2M6IHRydWVgIC0gZGVmYXVsdCBpcyBgdG9jOiBmYWxzZWAuCiogRmxvYXRpbmcgdGFibGUgb2YgY29udGVudHMgaW4gSFRNTCBvdXRwdXQsIGB0b2NfZmxvYXQ6IHRydWVgIC0gZGVmYXVsdCBpcyBgdG9jX2Zsb2F0OiBmYWxzZWAKCiMjIyBDcmVhdGUgYSBDb2RlIENodW5rIHRvIEF0dGFjaCBQYWNrYWdlcwoKU2VlIFt0aGUgU2V0dXAgU2VjdGlvbiBBYm92ZV0oI2ludHJvOnNldHVwKQoKSW5zZXJ0IENodW5rIGluIENvZGUgcHVsbCBkb3duIG1lbnUgaW4gdGhlIHRvcCBiYXIsIG9yIHVzZSB0aGUgPGtiZD5DPC9rYmQ+IGJ1dHRvbiBvbiB0b3AuIFlvdSBjYW4gdXNlIHNob3J0Y3V0IGtleXMgbGlzdGVkIHVuZGVyIFRvb2xzIGluIHRoZSB0b3AgYmFyLgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpgYGAKCiMjIyBGaW5kIERhdGEKClRoZXJlIGFyZSBtYW55IHNpdGVzIHdlIGNhbiBnZXQgZGF0YS4gSGVyZSB3ZSBsZWFybiBob3cgdG8gZ2V0IHR3byB0eXBlcyBvZiBkYXRhLCBpLmUsIGEgZGF0YSB3aXRoIGNvbW1hIHNlcGFyYXRlZCB2YWx1ZXMgaW4gYCouY3N2YCwgYW5kIGEgTWljcm9zb2Z0IEV4Y2VsIERhdGEgaW4gYCoueHNseGAgb3IgYCoueHNsYCBpbiB0aHJlZSB3YXlzLiBXZSBoYXZlIHVzZWQgYSBkYXRhIGluIGEgcGFja2FnZSBvZiBSLCBlLmcuIGBkYXRhc2V0czo6aXJpc2AsIGFuZCB0aGVyZSBhcmUgbWFueSBvdGhlciBkYXRhIHBhY2thZ2VzIG9mIFIuIEhvd2V2ZXIsIHdlIGZvY3VzIG9uIGhvdyB3ZSBnZXQgc28gY2FsbGVkIG9wZW4gcHVibGljIGRhdGEuIFRoZSBmb2xvd2luZyBpcyB0aGUgZGVmaW5pdGlvbiBvZiBPcGVuIERhdGEgYnkgV29ybGQgQmFuay4KCiMjIyBbV29ybGQgQmFuazpPcGVuIERhdGEgRGVmaW5lZF0oaHR0cDovL29wZW5kYXRhdG9vbGtpdC53b3JsZGJhbmsub3JnKSB7LX0KCj5UaGUgdGVybSBgYE9wZW4gRGF0YScnIGhhcyBhIHZlcnkgcHJlY2lzZSBtZWFuaW5nLiBEYXRhIG9yIGNvbnRlbnQgaXMgb3BlbiBpZiBhbnlvbmUgaXMgZnJlZSB0byB1c2UsIHJlLXVzZSBvciByZWRpc3RyaWJ1dGUgaXQsIHN1YmplY3QgYXQgbW9zdCB0byBtZWFzdXJlcyB0aGF0IHByZXNlcnZlIHByb3ZlbmFuY2UgYW5kIG9wZW5uZXNzLiAgCj4xLiBUaGUgZGF0YSBtdXN0IGJlIFx1bmRlcmxpbmV7bGVnYWxseSBvcGVufSwgd2hpY2ggbWVhbnMgdGhleSBtdXN0IGJlIHBsYWNlZCBpbiB0aGUgcHVibGljIGRvbWFpbiBvciB1bmRlciBsaWJlcmFsIHRlcm1zIG9mIHVzZSB3aXRoIG1pbmltYWwgcmVzdHJpY3Rpb25zLiAgCj4yLiBUaGUgZGF0YSBtdXN0IGJlIFx1bmRlcmxpbmV7dGVjaG5pY2FsbHkgb3Blbn0sIHdoaWNoIG1lYW5zIHRoZXkgbXVzdCBiZSBwdWJsaXNoZWQgaW4gZWxlY3Ryb25pYyBmb3JtYXRzIHRoYXQgYXJlIG1hY2hpbmUgcmVhZGFibGUgYW5kIG5vbi1wcm9wcmlldGFyeSwgc28gdGhhdCBhbnlvbmUgY2FuIGFjY2VzcyBhbmQgdXNlIHRoZSBkYXRhIHVzaW5nIGNvbW1vbiwgZnJlZWx5IGF2YWlsYWJsZSBzb2Z0d2FyZSB0b29scy4gRGF0YSBtdXN0IGFsc28gYmUgcHVibGljbHkgYXZhaWxhYmxlIGFuZCBhY2Nlc3NpYmxlIG9uIGEgcHVibGljIHNlcnZlciwgd2l0aG91dCBwYXNzd29yZCBvciBmaXJld2FsbCByZXN0cmljdGlvbnMuIFRvIG1ha2UgT3BlbiBEYXRhIGVhc2llciB0byBmaW5kLCBtb3N0IG9yZ2FuaXphdGlvbnMgY3JlYXRlIGFuZCBtYW5hZ2UgT3BlbiBEYXRhIGNhdGFsb2dzLgoKU2VlIGEgbGlzdCBvZiBvcGVuIHB1YmxpYyBkYXRhIFtiZWxvd10oI2NoYXA6ZGF0YSkuCgojIyBJbXBvcnQgRGF0YQoKMS4gVXNlIHRoZSBmaWxlIGxpbmsKMi4gUmVhZCB0aGUgZGF0YSBpbiBgZGF0YWAgZm9sZGVyCjMuIFVzZSB0aGUgQVBJLCBhcHBsaWNhdGlvbiBwcm9ncmFtIGludGVyZmFjZQoKIyMjIFVzZSB0aGUgRmlsZSBMaW5rCgpgYGAKdXJsX2NsYXNzIDwtICI8VVJMIG9mIHRoZSBkYXRhPiIKZG93bmxvYWQuZmlsZSh1cmwgPSB1cmxfY2xhc3MsIGRlc3RmaWxlID0gImRhdGEvPGZpbGUgbmFtZT4iKQpgYGAKCiMjIyMgRXhhbXBsZTogW1VOIERhdGFdKGh0dHBzOi8vZGF0YS51bi5vcmcpCgpDb3B5IHRoZSBsaW5rIGJ5IFJpZ2h0IENsaWNrIG9yIDxrYmQ+Q3RybDwva2JkPitDbGljayBhbmQgcGFzdGUgaXQgaW4gdGhlIFVSTC4KCmBgYHtyIGNhc2ggPSBUUlVFfQp1cmwgPC0gImh0dHBzOi8vZGF0YS51bi5vcmcvX0RvY3MvU1lCL0NTVi9TWUI2NV8xXzIwMjIwOV9Qb3B1bGF0aW9uLCUyMFN1cmZhY2UlMjBBcmVhJTIwYW5kJTIwRGVuc2l0eS5jc3YiICMgbG9uZyBmaWxlIG5hbWUKKHBvcCA8LSByZWFkX2Nzdih1cmwpKQpgYGAKCmAocG9wIDwtIHJlYWRfY3N2KHVybCkpYCBpcyBhIHNob3J0IGhhbmQgb2YgdGhlIGZvbGxvd2luZzoKYGBgCnBvcCA8LSByZWFkX2Nzdih1cmwpCnBvcApgYGAKClRoZSBmaXJzdCByb3cgaXMgbm90IHRoZSBjb2x1bW4gbmFtZXMsIHNvIHNraXAgdGhlIGZpcnN0IHJvdy4KCmBgYHtyIGNhc2ggPSBUUlVFfQp1cmwgPC0gImh0dHBzOi8vZGF0YS51bi5vcmcvX0RvY3MvU1lCL0NTVi9TWUI2NV8xXzIwMjIwOV9Qb3B1bGF0aW9uLCUyMFN1cmZhY2UlMjBBcmVhJTIwYW5kJTIwRGVuc2l0eS5jc3YiCihwb3AgPC0gcmVhZF9jc3YodXJsLCBza2lwID0gMSkpCmBgYAoKSXQgaXMgYmV0dGVyIHRvIHNhdmUgaXQgaW4gdGhlIGRhdGEgZm9sZGVyLgoKYGBge3IgY2FzaCA9IFRSVUV9CnVybCA8LSAiaHR0cHM6Ly9kYXRhLnVuLm9yZy9fRG9jcy9TWUIvQ1NWL1NZQjY1XzFfMjAyMjA5X1BvcHVsYXRpb24sJTIwU3VyZmFjZSUyMEFyZWElMjBhbmQlMjBEZW5zaXR5LmNzdiIKZG93bmxvYWQuZmlsZSh1cmwgPSB1cmwsIGRlc3RmaWxlID0gImRhdGEvcG9wLmNzdiIpCnBvcCA8LSByZWFkX2NzdigiZGF0YS9wb3AuY3N2Iiwgc2tpcCA9IDEpCnBvcApgYGAKCiMjIyMgRXhhbXBsZTogSW1wb3J0aW5nIEV4Y2VsIEZpbGVzIHsjY2xhc3MueGxzeH0KCiogQ0xBU1MueGxzeDogLSBfY29weSB0aGUgZm9sbG93aW5nIGxpbmtfCiAgLSBbVGhlIGN1cnJlbnQgY2xhc3NpZmljYXRpb24gYnkgaW5jb21lIGluIFhMUyBmb3JtYXRdKGh0dHBzOi8vZGF0YWJhbmtmaWxlcy53b3JsZGJhbmsub3JnL2RhdGEvZG93bmxvYWQvc2l0ZS1jb250ZW50L0NMQVNTLnhsc3gpIAoqIHJlYWR4bDogaHR0cHM6Ly9yZWFkeGwudGlkeXZlcnNlLm9yZwoqIEhlbHA6IGByZWFkX2V4Y2VsYCwgYHJlYWRfeGxzYCwgYHJlYWRfeGxzeGAKCmBgYHtyIGNhc2ggPSBUUlVFfQp1cmxfY2xhc3MgPC0gImh0dHBzOi8vZGF0YWJhbmtmaWxlcy53b3JsZGJhbmsub3JnL2RhdGEvZG93bmxvYWQvc2l0ZS1jb250ZW50L0NMQVNTLnhsc3giCmRvd25sb2FkLmZpbGUodXJsID0gdXJsX2NsYXNzLCBkZXN0ZmlsZSA9ICJkYXRhL0NMQVNTLnhsc3giKQpgYGAKCkxldCB1cyBsb29rIGF0IHRoZSBmaXJzdCBzaGVldC4KCjEuIFRoZSBjb2x1bW4gbmFtZXMgYXJlIGluIHRoZSA1dGggcm93LgoyLiBUaGUgY291bnRyeSBkYXRhIHN0YXJ0cyBmcm9tIHRoZSA3dGggcm93LgozLiBaaW1iYWJ1ZSBpcyBhdCB0aGUgbGFzdCByb3cuCgpgYGB7ciBjYXNoID0gVFJVRX0KbGlicmFyeShyZWFkeGwpCndiX2NvdW50cmllc190bXAgPC0gcmVhZF9leGNlbCgiZGF0YS9DTEFTUy54bHN4Iiwgc2hlZXQgPSAxLCBza2lwID0gMCwgbl9tYXggPTIxOSkgCndiX2NvdW50cmllcyA8LSB3Yl9jb3VudHJpZXNfdG1wICU+JSAKICBzZWxlY3QoY291bnRyeSA9IEVjb25vbXksIGlzbzNjID0gQ29kZSwgcmVnaW9uID0gUmVnaW9uLCBpbmNvbWUgPSBgSW5jb21lIGdyb3VwYCwgbGVuZGluZyA9ICJMZW5kaW5nIGNhdGVnb3J5Iiwgb3RoZXIgPSAiT3RoZXIgKEVNVSBvciBISVBDKSIpCndiX2NvdW50cmllcwpgYGAKCiogYHJlYWR4bGA6IGh0dHBzOi8vcmVhZHhsLnRpZHl2ZXJzZS5vcmcgIAogIGByZWFkeGxgIGlzIGEgcGFydCBvZiB0aGUgYHRpZHl2ZXJzZWAgZmFtaWx5IGJ1dCBub3QgYXV0b21hdGljYWxseSBhdHRhY2hlZC4gU28gYXR0YWNoIGl0IGJ5IGBsaWJyYXJ5KHJlYWR4bClgLgoqIEhlbHA6IGByZWFkX2V4Y2VsYCwgYHJlYWRfeGxzYCwgYHJlYWRfeGxzeGAKCjEuIFJlZ2lvbnMgc3RhcnQgZnJvbSB0aGUgMjIxdGggcm93LgoyLiBSZWdpb25zIGVuZCBhdCB0aGUgMjY2dGggcm93LgoKYGBge3IgY2FzaCA9IFRSVUV9CndiX3JlZ2lvbnNfdG1wIDwtIHJlYWRfZXhjZWwoImRhdGEvQ0xBU1MueGxzeCIsIHNoZWV0ID0gMSwgc2tpcCA9IDAsIG5fbWF4ID0yNjYpICU+JSAKICBzbGljZSgtKDE6MjIwKSkKd2JfcmVnaW9ucyA8LSB3Yl9yZWdpb25zX3RtcCAlPiUgCiAgc2VsZWN0KHJlZ2lvbiA9IEVjb25vbXksIGlzbzNjID0gQ29kZSkgJT4lIGRyb3BfbmEoKQp3Yl9yZWdpb25zCmBgYAoKCiMjIyBSZWFkIGZyb20gdGhlIGBkYXRhYCBGb2xkZXIKCmBgYApvYmplY3QgPC0gcmVhZF9jc3YoImRhdGEvPGZpbGUgbmFtZT4pCmBgYAoKCiMjIyBVc2UgdGhlIEFQSQoKSW5zdGFsbCBgV0RJYCBwYWNrYWdlIGZvciB0aGUgZmlyc3QgdGltZSBieSBgaW5zdGFsbC5wYWNrYWdlcygiV0RJIilgLiBTZWUgW3RoZSBTZXR1cCBTZWN0aW9uIEFib3ZlXSgjaW50cm86c2V0dXApLgpgYGB7ciBldmFsID0gRkFMU0V9Cmluc3RhbGwucGFja2FnZXMoIldESSIpCmBgYApgYGB7cn0KbGlicmFyeShXREkpCmBgYAoKIyMjIyBQYWtjYWdlIFNpdGU6IFtXREldKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9V0RJKTogV29ybGQgRGV2ZWxvcG1lbnQgSW5kaWNhdG9ycyBhbmQgT3RoZXIgV29ybGQgQmFuayBEYXRhCgojIyMjIFVzYWdlCgpUcnkgYD9XRElgIGluIENvbnNvbGUgb3IgYFdESWAgaW4gdGhlIEhlbHAgdGFiIG9uIHRoZSByaWdodCBidXR0b20gcGFuZS4KYGBgCldESShjb3VudHJ5ID0gImFsbCIsCiAgICBpbmRpY2F0b3IgPSAiTlkuR0RQLlBDQVAuS0QiLAogICAgc3RhcnQgPSAxOTYwLAogICAgZW5kID0gMjAyNSwKICAgIGV4dHJhID0gRkFMU0UsCiAgICBjYWNoZSA9IE5VTEwpCmBgYAoKKipBcmd1bWVudHMqKgogIC0gY291bnRyeTogVmVjdG9yIG9mIGNvdW50cmllcyAoSVNPLTIgY2hhcmFjdGVyIGNvZGVzLCBlLmcuICJCUiIsICJVUyIsICJDQSIsIG9yICJhbGwiKSAKICAtIGluZGljYXRvcjogSWYgeW91IHN1cHBseSBhIG5hbWVkIHZlY3RvciwgdGhlIGluZGljYXRvcnMgd2lsbCBiZSBhdXRvbWF0aWNhbGx5IHJlbmFtZWQ6IGBjKCd3b21lbl9wcml2YXRlX3NlY3RvcicgPSAnQkkuUFdLLlBSVlMuRkUuWlMnKWAKICAKIyMjIEZ1bmN0aW9uIFdESXNlYXJjaAoKYGBge3IgY2FzaCA9IFRSVUV9CldESXNlYXJjaChzdHJpbmcgPSAiTlkuR0RQLlBDQVAuS0QiLCAKICAgICAgICAgIGZpZWxkID0gImluZGljYXRvciIsIGNhY2hlID0gTlVMTCkKYGBgCgpgYGB7ciBjYXNoID0gVFJVRX0KV0RJc2VhcmNoKHN0cmluZyA9ICJOWS5HRFAuUENBUC5LRCIsIAogIGZpZWxkID0gImluZGljYXRvciIsIHNob3J0ID0gRkFMU0UsIGNhY2hlID0gTlVMTCkgCmBgYAoKRGVmYXVsdDogc2hvcnQgPSBUUlVFCmBgYHtyIGNhc2ggPSBUUlVFfQpXRElzZWFyY2goc3RyaW5nID0gImdkcCBwZXIgY2FwaXRhIiwgCiAgZmllbGQgPSAibmFtZSIsIGNhY2hlID0gTlVMTCkKYGBgCmBgYHtyfQpXRElzZWFyY2goc3RyaW5nID0gIjYuMC5HRFBfY3VycmVudCIsIGZpZWxkID0gImluZGljYXRvciIsIHNob3J0ID0gRkFMU0UpCmBgYAoKKipBbm90aGVyIHdheSB0byBmaW5kIHRoZSBpbmRpY2F0b3IqKgoKR28gdG8gdGhlIFdvcmxkIEJhbmsgT3BlbiBEYXRhIHNpdGUgYW5kIHNlbGVjdCBCcm93c2UgYnkgSW5kaWNhdG9ycyBhbmQgZmluZCB0aGUgZGF0YS4gVGhlbiB0aGUgaW5kaWNhdG9yIGlzIGluY2x1ZGVkIGluIHRoZSBVUkwuIAoKKiBbV29ybGQgQmFuayBPcGVuIERhdGFdKGh0dHBzOi8vZGF0YS53b3JsZGJhbmsub3JnKQogIC0gW0Jyb3dzZSBieSBJbmRpY2F0b3JzXShodHRwczovL2RhdGEud29ybGRiYW5rLm9yZy9pbmRpY2F0b3IpOiBDaG9vc2UgYSBUYWIgb2YgRmVhdHVyZWQgSW5kaWNhdG9ycyBhbmQgQWxsIEluZGljYXRvcnMKICAtIFtXT1JMRCBERVZFTE9QTUVOVCBJTkRJQ0FUT1JTXShodHRwczovL2RhdGF0b3BpY3Mud29ybGRiYW5rLm9yZy93b3JsZC1kZXZlbG9wbWVudC1pbmRpY2F0b3JzLyk6IENob29zZSBmcm9tIERhdGEgVGhlbWVzCgoKW0NPMiBlbWlzc2lvbnMgKG1ldHJpYyB0b25zIHBlciBjYXBpdGEpXShodHRwczovL2RhdGEud29ybGRiYW5rLm9yZy9pbmRpY2F0b3IvRU4uQVRNLkNPMkUuUEM/dmlldz1jaGFydCkKCkluZGljYXRvcjogRU4uQVRNLkNPMkUuUEMKCkNoZWNrIHRoZSBpbmRpY2F0b3IgYnkgYFdESXNlYXJjaGAuCmBgYHtyfQpXRElzZWFyY2goc3RyaW5nID0gIkVOLkFUTS5DTzJFLlBDIiwgZmllbGQgPSAiaW5kaWNhdG9yIiwgc2hvcnQgPSBGQUxTRSkKYGBgCioqRnVydGhlciBFeGFtcGxlcyoqCgpgYGB7cn0KV0RJc2VhcmNoKHN0cmluZyA9ICJwb3B1bGF0aW9uLCB0b3RhbCIsIGZpZWxkID0gIm5hbWUiLCBzaG9ydCA9IEZBTFNFKQpgYGAKCmBgYHtyfQpXRElzZWFyY2goc3RyaW5nID0gIlNQLlBPUC5UT1RMIiwgZmllbGQgPSAiaW5kaWNhdG9yIiwgc2hvcnQgPSBGQUxTRSkKYGBgCgoqKkV4ZXJjaXNlLioqIFRyeSB0aGUgZm9sbG93aW5nIG9uIENvbnNvbGUgb3IgaW4gYSBuZXcgY29kZSBjaHVuaywgYW5kIGNob29zZSBvbmUgaW50ZXJlc3RpbmcgZGF0YSB3aXRoIGl0cyBpbmRpY2F0b3IsIG5hbWUgYW5kIGRlc2NyaXB0aW9uLgoKKiBgP1dESXNlYXJjaGAKKiBgVmlldyhXRElzZWFyY2goc3RyaW5nID0gImdkcCBwZXIgY2FwaXRhIiwgZmllbGQgPSAibmFtZSIsIGNhY2hlID0gTlVMTCkpYAoqIGBWaWV3KFdESXNlYXJjaChzdHJpbmcgPSAiZ2RwIHBlciBjYXBpdGEiLCBmaWVsZCA9ICJuYW1lIiwgc2hvcnQgPSBGQUxTRSwgY2FjaGUgPSBOVUxMKSlgCiogYFZpZXcoV0RJc2VhcmNoKHN0cmluZyA9ICJnZHAiLCBmaWVsZCA9ICJuYW1lIiwgc2hvcnQgPSBGQUxTRSwgY2FjaGUgPSBOVUxMKSlgCiogYFZpZXcoV0RJc2VhcmNoKHN0cmluZyA9ICJnZW5kZXIiLCBmaWVsZCA9ICJuYW1lIiwgc2hvcnQgPSBGQUxTRSwgY2FjaGUgPSBOVUxMKSlgCgojIyMgRG93bmxvYWQgRGF0YSBieSBgV0RJYAoKIyMjIyBHRFAgcGVyIGNhcGl0YSAoY29uc3RhbnQgMjAxNSBVUyQpCgoqKkRlc2NyaXB0aW9uKio6IEdEUCBwZXIgY2FwaXRhIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgZGl2aWRlZCBieSBtaWR5ZWFyIHBvcHVsYXRpb24uIEdEUCBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGVjb25vbXkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTUgVS5TLiBkb2xsYXJzLgpgYGB7ciBjYXNoID0gVFJVRX0KZ2RwY2FwIDwtIFdESShjb3VudHJ5ID0gImFsbCIsIGluZGljYXRvciA9ICJOWS5HRFAuUENBUC5LRCIpCmdkcGNhcApgYGAKQXZvaWRpbmcgZG93bmxvYWRpbmcgdGhlIGRhdGEgcmVwZWF0ZWRseSBhcyBhIENTViBmaWxlLCBsZXQgdXMgc2FnZSBpdCBpbiB0aGUgYGRhdGFgIGZvbGRlciBjcmVhdGVkIGFib3ZlLgpgYGB7ciBjYXNoID0gVFJVRX0Kd3JpdGVfY3N2KGdkcGNhcCwgImRhdGEvZ2RwY2FwLmNzdiIpCmBgYAoKIyMjIyBHRFAgcGVyIGNhcGl0YSBhbmQgQ08yIGVtaXNzaW9uIHBlciBjYXBpdGEKCldlIGNhbiBkb3dubG9hZCB0d28gZGF0YSBhcyBvbmUgZGF0YSEgVGhlIEdEUCBwZXIgY2FwaXRhIGRhdGEgYW5kIHRoZSBDTzIgZW1pc3Npb24gcGVyIGNhcGl0YSBkYXRhIHdpdGggaW5kaWNhdG9yIGBFTi5BVE0uQ08yRS5QQ2AuIAoKKipEZXNjcmlwdGlvbioqOiBDYXJib24gZGlveGlkZSBlbWlzc2lvbnMgYXJlIHRob3NlIHN0ZW1taW5nIGZyb20gdGhlIGJ1cm5pbmcgb2YgZm9zc2lsIGZ1ZWxzIGFuZCB0aGUgbWFudWZhY3R1cmUgb2YgY2VtZW50LiBUaGV5IGluY2x1ZGUgY2FyYm9uIGRpb3hpZGUgcHJvZHVjZWQgZHVyaW5nIGNvbnN1bXB0aW9uIG9mIHNvbGlkLCBsaXF1aWQsIGFuZCBnYXMgZnVlbHMgYW5kIGdhcyBmbGFyaW5nLgpgYGB7ciBjYXNoID0gVFJVRX0KZ2RwY2FwX2NvMiA8LSBXREkoY291bnRyeSA9ICJhbGwiLCBpbmRpY2F0b3IgPSBjKCJOWS5HRFAuUENBUC5LRCIsICJFTi5BVE0uQ08yRS5QQyIpLCBleHRyYSA9IFRSVUUpCmdkcGNhcF9jbzIKYGBgCgpMZXQgdXMgc2F2ZSB0aGlzIGRhdGEgYXMgd2VsbC4gSXQgaXMgdmVyeSBsYXJnZSwgaS5lLiwgMTQgY29sdW1ucyBhbmQgMTYsNDkyIHJvd3MsIDEuOU1CLgpgYGB7ciBjYXNoID0gVFJVRX0Kd3JpdGVfY3N2KGdkcGNhcF9jbzIsICJkYXRhL2dkcGNhcF9jbzIuY3N2IikKYGBgCgogIAojIyBXcmFuZ2xlIERhdGEgCgojIyMgYGRwbHlyYCBbT3ZlcnZpZXddKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZykKCmRwbHlyIGlzIGEgZ3JhbW1hciBvZiBkYXRhIG1hbmlwdWxhdGlvbiwgcHJvdmlkaW5nIGEgY29uc2lzdGVudCBzZXQgb2YgdmVyYnMgdGhhdCBoZWxwIHlvdSBzb2x2ZSB0aGUgbW9zdCBjb21tb24gZGF0YSBtYW5pcHVsYXRpb24gY2hhbGxlbmdlczoKCiogYHNlbGVjdCgpYCBwaWNrcyB2YXJpYWJsZXMgYmFzZWQgb24gdGhlaXIgbmFtZXMuCiogYGZpbHRlcigpYCBwaWNrcyBjYXNlcyBiYXNlZCBvbiB0aGVpciB2YWx1ZXMuCiogYGFycmFuZ2UoKWAgY2hhbmdlcyB0aGUgb3JkZXJpbmcgb2YgdGhlIHJvd3MuCiogYG11dGF0ZSgpYCBhZGRzIG5ldyB2YXJpYWJsZXMgdGhhdCBhcmUgZnVuY3Rpb25zIG9mIGV4aXN0aW5nIHZhcmlhYmxlcwoqIGBncm91cF9ieSgpYCB0YWtlcyBhbiBleGlzdGluZyB0YmwgYW5kIGNvbnZlcnRzIGl0IGludG8gYSBncm91cGVkIHRibC4KKiBgc3VtbWFyaXNlKClgIHJlZHVjZXMgbXVsdGlwbGUgdmFsdWVzIGRvd24gdG8gYSBzaW5nbGUgc3VtbWFyeS4KCllvdSBjYW4gbGVhcm4gbW9yZSBhYm91dCB0aGVtIGluIHZpZ25ldHRlKCJkcGx5ciIpLiBBcyB3ZWxsIGFzIHRoZXNlIHNpbmdsZS10YWJsZSB2ZXJicywgZHBseXIgYWxzbyBwcm92aWRlcyBhIHZhcmlldHkgb2YgdHdvLXRhYmxlIHZlcmJzLCB3aGljaCB5b3UgY2FuIGxlYXJuIGFib3V0IGluIHZpZ25ldHRlKCJ0d28tdGFibGUiKS4KCklmIHlvdSBhcmUgbmV3IHRvIGRwbHlyLCB0aGUgYmVzdCBwbGFjZSB0byBzdGFydCBpcyBbdGhlIGRhdGEgdHJhbnNmb3JtYXRpb24gY2hhcHRlciBpbiBSIGZvciBkYXRhIHNjaWVuY2VdKGh0dHA6Ly9yNGRzLmhhZC5jby5uei90cmFuc2Zvcm0uaHRtbCkuCgojIyMgW2BzZWxlY3RgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL3NlbGVjdC5odG1sKTogU3Vic2V0IGNvbHVtbnMgdXNpbmcgdGhlaXIgbmFtZXMgYW5kIHR5cGVzCgpIZWxwZXIgRnVuY3Rpb24JfCBVc2UJfCBFeGFtcGxlCi0tLXwtLS0tLS0tfC0tLS0tLS0tCi0JfCBDb2x1bW5zIGV4Y2VwdAl8IHNlbGVjdChiYWJ5bmFtZXMsIC1wcm9wKQo6CXwgQ29sdW1ucyBiZXR3ZWVuIChpbmNsdXNpdmUpCXwgc2VsZWN0KGJhYnluYW1lcywgeWVhcjpuKQpjb250YWlucygpIHwJQ29sdW1ucyB0aGF0IGNvbnRhaW5zIGEgc3RyaW5nIHwJc2VsZWN0KGJhYnluYW1lcywgY29udGFpbnMoIm4iKSkKZW5kc193aXRoKCkJfCBDb2x1bW5zIHRoYXQgZW5kcyB3aXRoIGEgc3RyaW5nCXwgc2VsZWN0KGJhYnluYW1lcywgZW5kc193aXRoKCJuIikpCm1hdGNoZXMoKQl8IENvbHVtbnMgdGhhdCBtYXRjaGVzIGEgcmVnZXggfAlzZWxlY3QoYmFieW5hbWVzLCBtYXRjaGVzKCJuIikpCm51bV9yYW5nZSgpCXwgQ29sdW1ucyB3aXRoIGEgbnVtZXJpY2FsIHN1ZmZpeCBpbiB0aGUgcmFuZ2UgfCBOb3QgYXBwbGljYWJsZSB3aXRoIGJhYnluYW1lcwpvbmVfb2YoKSB8CUNvbHVtbnMgd2hvc2UgbmFtZSBhcHBlYXIgaW4gdGhlIGdpdmVuIHNldCB8CXNlbGVjdChiYWJ5bmFtZXMsIG9uZV9vZihjKCJzZXgiLCAiZ2VuZGVyIikpKQpzdGFydHNfd2l0aCgpCXwgQ29sdW1ucyB0aGF0IHN0YXJ0cyB3aXRoIGEgc3RyaW5nCXwgc2VsZWN0KGJhYnluYW1lcywgc3RhcnRzX3dpdGgoIm4iKSkKCiMjIyBbYGZpbHRlcmBdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvZmlsdGVyLmh0bWwpOiBTdWJzZXQgcm93cyB1c2luZyBjb2x1bW4gdmFsdWVzCgpMb2dpY2FsIG9wZXJhdG9yCXwgdGVzdHMJfCBFeGFtcGxlCi0tfC0tLS0tfC0tLQo+CXwgSXMgeCBncmVhdGVyIHRoYW4geT8gfAl4ID4geQo+PQl8IElzIHggZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIHk/IHwJeCA+PSB5CjwJfCBJcyB4IGxlc3MgdGhhbiB5Pwl8IHggPCB5Cjw9CXwgSXMgeCBsZXNzIHRoYW4gb3IgZXF1YWwgdG8geT8gfCAJeCA8PSB5Cj09CXwgSXMgeCBlcXVhbCB0byB5PyB8CXggPT0geQohPQl8IElzIHggbm90IGVxdWFsIHRvIHk/IHwJeCAhPSB5CmlzLm5hKCkJfCBJcyB4IGFuIE5BPwl8IGlzLm5hKHgpCiFpcy5uYSgpIHwJSXMgeCBub3QgYW4gTkE/IHwJIWlzLm5hKHgpCgojIyMgW2BhcnJhbmdlYF0oaHR0cHM6Ly9kcGx5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9hcnJhbmdlLmh0bWwpIGFuZCBgUGlwZSAlPiVgCgoqIGBhcnJhbmdlKClgIG9yZGVycyB0aGUgcm93cyBvZiBhIGRhdGEgZnJhbWUgYnkgdGhlIHZhbHVlcyBvZiBzZWxlY3RlZCBjb2x1bW5zLgoKVW5saWtlIG90aGVyIGBkcGx5cmAgdmVyYnMsIGBhcnJhbmdlKClgIGxhcmdlbHkgaWdub3JlcyBncm91cGluZzsgeW91IG5lZWQgdG8gZXhwbGljaXRseSBtZW50aW9uIGdyb3VwaW5nIHZhcmlhYmxlcyAoYG9yIHVzZSAuYnlfZ3JvdXAgPSBUUlVFKSBpbiBvcmRlciB0byBncm91cCBieSB0aGVtLCBhbmQgZnVuY3Rpb25zIG9mIHZhcmlhYmxlcyBhcmUgZXZhbHVhdGVkIG9uY2UgcGVyIGRhdGEgZnJhbWUsIG5vdCBvbmNlIHBlciBncm91cC4KKiBbYHBpcGVzYF0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9waXBlcy5odG1sKSBpbiBSIGZvciBEYXRhIFNjaWVuY2UuCgoqKkV4YW1wbGVzKioKCmBgYAphcnJhbmdlKDxkYXRhPiwgPHZhcmlibGU+KQphcnJhbmdlKDxkYXRhPiwgZGVzYyg8dmFyaWFibGU+KSkKYGBgCgojIyMgW2BtdXRhdGVgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL211dGF0ZS5odG1sKSAKCiogQ3JlYXRlLCBtb2RpZnksIGFuZCBkZWxldGUgY29sdW1ucwoKKiBVc2VmdWwgbXV0YXRlIGZ1bmN0aW9ucwoKICAtICssIC0sIGxvZygpLCBldGMuLCBmb3IgdGhlaXIgdXN1YWwgbWF0aGVtYXRpY2FsIG1lYW5pbmdzCgogIC0gbGVhZCgpLCBsYWcoKQoKICAtIGRlbnNlX3JhbmsoKSwgbWluX3JhbmsoKSwgcGVyY2VudF9yYW5rKCksIHJvd19udW1iZXIoKSwgY3VtZV9kaXN0KCksIG50aWxlKCkKCiAgLSBjdW1zdW0oKSwgY3VtbWVhbigpLCBjdW1taW4oKSwgY3VtbWF4KCksIGN1bWFueSgpLCBjdW1hbGwoKQoKICAtIG5hX2lmKCksIGNvYWxlc2NlKCkKCiAgLSBpZl9lbHNlKCksIHJlY29kZSgpLCBjYXNlX3doZW4oKQoKCgojIyBWaXN1YWxpemUgRGF0YQoKIyMjIGdncGxvdDIKClNlZSBodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvCgpgYGAKZ2dwbG90KDxkYXRhPikgKyBnZW9tX3BvaW50KGFlcyh4ID0gPD4sIHkgPSA8PikpCmBgYAoKYGBgCjxkYXRhPiAlPiUgZ2dwbG90KCkgKyBnZW9tX3BvaW50KGFlcyh4ID0gPD4sIHkgPSA8PikpCmBgYAoKIyMjIyBHZW9tcwoKKiBgZ2VvbV9wb2ludCgpYDogUG9pbnRzCiogYGdlb21fYm94cGxvdCgpYDogQSBib3ggcGxvdCAKKiBgZ2VvbV9oaXN0b2dyYW0oKWA6IEhpc3RvZ3JhbXMgCiogYGdlb21fc21vb3RoKClgOiBTbW9vdGhlZCBjb25kaXRpb25hbCBtZWFucwoqIGFuZCBtdWNoIG1vcmUKCgojIyMjIFJlZmVyZW5jZXMKCiogW1IgZm9yIERhdGEgU2NpZW5jZV0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei8pCiAgLSBbMy4gRGF0YSBWaXN1YWxpc2F0aW9uXShodHRwczovL3I0ZHMuaGFkLmNvLm56L2RhdGEtdmlzdWFsaXNhdGlvbi5odG1sI2RhdGEtdmlzdWFsaXNhdGlvbikKICAtIFsyOC4gR3JhcGhpY3MgZm9yIGNvbW11bmljYXRpb25dKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZ3JhcGhpY3MtZm9yLWNvbW11bmljYXRpb24uaHRtbCkKKiBbZ2dwbG90MiBwYWdlIGluIHRpZHl2ZXJzZV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcpCiogW2dncGxvdDIgZXh0ZW5zaW9ucyAtIGdhbGxlcnldKGh0dHBzOi8vZXh0cy5nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvZ2FsbGVyeS8pCiogW2dncGxvdDIgQ2hlYXRzaGVldF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vY2hlYXRzaGVldHMvYmxvYi9tYXN0ZXIvZGF0YS12aXN1YWxpemF0aW9uLTIuMS5wZGYpCgojIyBFeGFtcGxlcwoKIyMjIEdEUCBQZXIgQ2FwaXRhCgpgYGB7ciBjYXNoID0gVFJVRX0KZ2RwY2FwIDwtIHJlYWRfY3N2KCJkYXRhL2dkcGNhcC5jc3YiKQpnZHBjYXAKYGBgCgoKYGBge3J9CnN1bW1hcnkoZ2RwY2FwKQpgYGAKCmBgYHtyfQpnZHBjYXAgJT4lIGRpc3RpbmN0KGNvdW50cnksIGlzbzJjKQpgYGAKCmBgYHtyfQpnZHBjYXAgJT4lIGZpbHRlcihjb3VudHJ5ID09ICJXb3JsZCIpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBOWS5HRFAuUENBUC5LRCkpICsgCiAgZ2VvbV9saW5lKCkKYGBgCmBgYHtyIHdhcm5pbmcgPSBGQUxTRX0KZ2RwY2FwICU+JSBmaWx0ZXIoaXNvMmMgJWluJSBjKCJCUiIsICJSVSIsICJJTiIsICJDTiIpKSAlPiUgCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IE5ZLkdEUC5QQ0FQLktELCBjb2xvciA9IGNvdW50cnkpKSArIAogIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkdEUCBwZXIgY2FwdGEgb2YgQlJJQ3MiLCB5ID0gIkdEUCBwZXIgY2FwaXRhIChjb25zdGFudCAyMDE1IFVTJCkiKQpgYGAKCiMjIyMgRXhlcmNpc2VzCgoxLiBEcmF3IHRoZSBsaW5lIGdyYXBoIGZvciBBU0VBTiBjb3VudHJpZXM6IEJydW5laSwgQ2FtYm9kaWEsIEluZG9uZXNpYSwgTGFvcywgTWFsYXlzaWEsIE15YW5tYXIsIHRoZSBQaGlsaXBwaW5lcywgU2luZ2Fwb3JlLCBUaGFpbGFuZCBhbmQgVmlldG5hbS4KMi4gQ2hvb3NlIGFsbCBMb3cgaW5jb21lIGNvdW50cmllcyBhbmQgZG8gdGhlIHNhbWUuICAgCkhpbnQ6IHVzZSBgd2JfY291bnRyaWVzYCBbYWJvdmVdKCNjbGFzcy54bHN4KS4KYGBge3J9Cihsb3dfY291bnRyaWVzIDwtIHdiX2NvdW50cmllcyAlPiUgZmlsdGVyKGluY29tZSA9PSAiTG93IGluY29tZSIpKQpgYGAKYGBge3J9CihnZHBjYXBfbG93IDwtIHNlbWlfam9pbihnZHBjYXAsIGxvd19jb3VudHJpZXMpKQpgYGAKCmBgYHtyfQpnZHBjYXBfbG93ICU+JSAKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gTlkuR0RQLlBDQVAuS0QsIGNvbG9yID0gY291bnRyeSkpICsgCiAgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiR0RQIHBlciBjYXB0YSBvZiBsb3cgaW5jb21lIGNvdW50cmllcyIsIHkgPSAiR0RQIHBlciBjYXBpdGEgKGNvbnN0YW50IDIwMTUgVVMkKSIpCmBgYAoKIyMjIENPMiBFbWlzc2lvbiBQZXIgQ2FwaXRhCgpMZXQgdXMgdXNlIHRoZSBzYXZlZCBkYXRhLgpgYGB7ciBjYXNoID0gVFJVRX0KZ2RwY2FwX2NvMiA8LSByZWFkX2NzdigiZGF0YS9nZHBjYXBfY28yLmNzdiIpCmdkcGNhcF9jbzIKYGBgCgpgYGB7cn0KY28yIDwtIGdkcGNhcF9jbzIgJT4lIAogIHNlbGVjdChjb3VudHJ5LCBpc28yYywgaXNvM2MsIHllYXIsIGNvMiA9IEVOLkFUTS5DTzJFLlBDLCByZWdpb24pCmBgYAoKYGBge3J9CmNvMiAlPiUgZmlsdGVyKGNvdW50cnkgJWluJSBjKCJGcmFuY2UiLCAiR2VybWFueSIsICJJdGFseSIsICJKYXBhbiIsICJVbml0ZWQgS2luZ2RvbSIsICJVbml0ZWQgU3RhdGVzIiwgIkNhbmFkYSIsICJSdXNzaWFuIEZlZGVyYXRpb24iKSkgJT4lIGRpc3RpbmN0KGNvdW50cnksIGlzbzJjLCBpc28zYywgcmVnaW9uKQpgYGAKU3VjY2Vzc2Z1bGx5IGNob3NlbiBkYXRhIG9mIEc4IGNvdW50cmllcy4KYGBge3J9CmNvMiAlPiUgZmlsdGVyKGNvdW50cnkgJWluJSBjKCJGcmFuY2UiLCAiR2VybWFueSIsICJJdGFseSIsICJKYXBhbiIsICJVbml0ZWQgS2luZ2RvbSIsICJVbml0ZWQgU3RhdGVzIiwgIkNhbmFkYSIsICJSdXNzaWFuIEZlZGVyYXRpb24iKSkgJT4lIAogIGZpbHRlcighaXMubmEoY28yKSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IGNvMiwgY29sb3IgPSBjb3VudHJ5KSkgKyBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9ICJDTzIgRW1pc3Npb24gUGVyIENhcGl0YSBvZiBHOCBDb3VudHJpZXMiKQpgYGAKCiMjIyBHRFAgUGVyIENhcGl0YSB2cyBDTzIgRW1pc3Npb24gUGVyIENhcGl0YQoKTGV0IHVzIHVzZSB0aGUgc2F2ZWQgZGF0YS4KYGBge3IgY2FzaCA9IFRSVUV9CmdkcGNhcF9jbzIgPC0gcmVhZF9jc3YoImRhdGEvZ2RwY2FwX2NvMi5jc3YiKQpnZHBjYXBfY28yCmBgYAoKYGBge3J9CnN1bW1hcnkoZ2RwY2FwX2NvMikKYGBgCgpTaW5jZSB0aGUgbW9zdCByZWNlbnQgeWVhciBzZWVtcyB0byBiZSAyMDIxLCBsZXQgdXMgY2hvb3NlIHRoZSB5ZWFyLgpgYGB7cn0KZ2RwY2FwX2NvMiAlPiUgZmlsdGVyKHllYXIgPT0gMjAyMSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gTlkuR0RQLlBDQVAuS0QsIHkgPSBFTi5BVE0uQ08yRS5QQykpICsgCiAgZ2VvbV9wb2ludCgpCmBgYApXaGF0IGlzIHdyb25nPyBUaGVyZSBzZWVtcyB0byBiZSBtYW55IG1pc3NpbmcgdmFsdWVzLiBGaXJzdCB1c2UgYGZpbHRlcmAgdG8gY2hvb3NlIGNvdW50cmllcywgaS5lLiwgbm9uLWFnZ3JlZ2F0ZXMgYW5kIHNlZSBob3cgbWFueSBkYXRhIGFyZSBpbiBlYWNoIHllYXIuCmBgYHtyfQpnZHBjYXBfY28yICU+JSBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIiwgeWVhciA9PSAiMjAyMSIpIApgYGAKQWx0ZXJuYXRlbHksIHdlIGNhbiBmaW5kIHRoZSBkYXRhIHBvaW50cyBpbiBlYWNoIHllYXIgZXhjZXB0IE5BLCBpLmUuLCBub3QgYXZhaWxhYmxlLgpgYGB7cn0KZ2RwY2FwX2NvMiAlPiUgZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIsICFpcy5uYShOWS5HRFAuUENBUC5LRCksICFpcy5uYShFTi5BVE0uQ08yRS5QQykpICU+JSAKICBncm91cF9ieSh5ZWFyKSAlPiUgc3VtbWFyaXplKG4gPSBuKCkpCmBgYAoKCmBgYHtyfQpnZHBjYXBfY28yICU+JSBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIiwgIWlzLm5hKE5ZLkdEUC5QQ0FQLktEKSwgIWlzLm5hKEVOLkFUTS5DTzJFLlBDKSwgeWVhciA9PSAyMDE5KSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBOWS5HRFAuUENBUC5LRCwgeSA9IEVOLkFUTS5DTzJFLlBDKSkgKyAKICBnZW9tX3BvaW50KCkKYGBgCgpNb3N0IG9mIHRoZSBwb2ludHMgYXJlIG5lYXIgb3JpZ2luLiBMZXQncyB0cnkgdGhlIGxvZy1sb2cgc2NhbGUuCgpgYGB7cn0KZ2RwY2FwX2NvMiAlPiUgZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIsICFpcy5uYShOWS5HRFAuUENBUC5LRCksICFpcy5uYShFTi5BVE0uQ08yRS5QQyksIHllYXIgPT0gMjAxOSkgJT4lCiAgZ2dwbG90KGFlcyh4ID0gbG9nMTAoTlkuR0RQLlBDQVAuS0QpLCB5ID0gbG9nMTAoRU4uQVRNLkNPMkUuUEMpKSkgKyAKICBnZW9tX3BvaW50KCkgCmBgYApgYGB7cn0KZ2RwY28yXzIwMTkgPC0gZ2RwY2FwX2NvMiAlPiUgc2VsZWN0KHJlZ2lvbiwgeWVhciwgZ2RwID0gTlkuR0RQLlBDQVAuS0QsIGNvMiA9IEVOLkFUTS5DTzJFLlBDKSAlPiUKICBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIiwgIWlzLm5hKGdkcCksICFpcy5uYShjbzIpLCB5ZWFyID09IDIwMTkpCmdkcGNvMl8yMDE5ICU+JSBnZ3Bsb3QoYWVzKHggPSBsb2cxMChnZHApLCB5ID0gbG9nMTAoY28yKSkpICsgCiAgZ2VvbV9wb2ludCgpICsgCiAgbGFicyh0aXRsZSA9IGJxdW90ZSh+Q09bMl1+ICJMb2cxMCBTY2FsZSBQbG90IG9mICJ+Q09bMl1+IiBFbWlzc2lvbiBQZXIgQ2FwaXRhIEFnYWluc3QgR0RQIFBlciBDYXBpdGFpbiAyMDE5IiksCiAgICAgICBzdWJ0aXRsZSA9IGJxdW90ZSh+Q09bMl1+IjogbWV0cmljIHRvbnMgcGVyIGNhcGl0YSwgR0RQOiBjb25zdGFudCAyMDE1IFVTJCIpLAogICAgICAgeCA9IGJxdW90ZSh+Q09bMl1+ICJFbWlzc2lvbiBQZXIgQ2FwaXRhIChMb2cxMCkpIiksIHkgPSAiR0RQIFBlciBDYXBpdGEgKExvZzEwKSIpICsgCiAgZ2VvbV9zbW9vdGgoZm9ybXVsYSA9IHkgfiB4LCBtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSArIAogIGdlb21fc21vb3RoKGZvcm11bGEgPSB5IH4geCwgbWV0aG9kID0gImxvZXNzIiwgY29sID0gInJlZCIpCmBgYAoKYGBge3J9CnN1bW1hcnkoZ2RwY28yXzIwMTkpCmBgYAoKYGBge3J9CmxtKGxvZzEwKGNvMikgfiBsb2cxMChnZHApLCBkYXRhID0gZ2RwY28yXzIwMTkpICU+JSBzdW1tYXJ5KCkKYGBgCgoqICoqQ2FsbCoqOiBUaGUgUiBmZWF0dXJlIHRvIHNob3cgd2hhdCBmdW5jdGlvbiBhbmQgcGFyYW1ldGVycyB3ZXJlIHVzZWQgdG8gY3JlYXRlIHRoZSBtb2RlbC4KKiAqKlJlc2lkdWFscyoqOiBEaWZmZXJlbmNlIGJldHdlZW4gd2hhdCB0aGUgbW9kZWwgcHJlZGljdGVkIGFuZCB0aGUgYWN0dWFsIHZhbHVlIG9mIHkuICAKKiAqKkNvZWZmaWNpZW50cyoqOiBUaGUgY29lZmZpY2llbnRzIG9mIGEgbGluZWFyIG1vZGVsIChsaW5lKSB0aGF0IG1pbmltaXplIHRoZSBzdW0gb2YgdGhlIHNxdWFyZSBvZiB0aGUgZXJyb3JzLiAgCiQkeSA9IDAuOTAyMDAgeCAtMy4wODgxOCQkCiogKipTdGQuIEVycm9yKio6IFJlc2lkdWFsIFN0YW5kYXJkIEVycm9yIGRpdmlkZWQgYnkgdGhlIHNxdWFyZSByb290IG9mIHRoZSBzdW0gb2YgdGhlIHNxdWFyZSBvZiB0aGF0IHBhcnRpY3VsYXIgeCB2YXJpYWJsZS4KKiAqKnQgdmFsdWUqKjogRXN0aW1hdGUgZGl2aWRlZCBieSBTdGQuIEVycm9yCiogKipQcig+fHR8KSoqOiB0IHZhbHVlIGluIGEgVCBkaXN0cmlidXRpb24gdGFibGUgKHdpdGggdGhlIGdpdmVuIGRlZ3JlZXMgb2YgZnJlZWRvbSkuCgpUaGVyZSBhcmUgJG4gPSAxODQkIGRhdGEuIFNvIGdkcCA9ICR4XzEsIHhfMiwgXGxkb3RzLCB4X3sxODR9JCBhbmQgY28yID0gJHlfMSwgeV8yLCBcbGRvdHMsIHlfezE4NH0kLiBJbiBsb2cxMCBzY2FsZSwgbG9nMTAoZ2RwKSA9ICRcbG9nX3sxMH0oeF8xKSwgXGxvZ197MTB9KHhfMiksIFxsZG90cywgXGxvZ197MTB9KHhfezE4NH0pJCBhbmQgbG9nMTAoY28yKSA9ICRcbG9nX3sxMH0oeV8xKSwgXGxvZ197MTB9KHlfMiksIFxsZG90cywgXGxvZ197MTB9KHlfezE4NH0pJCwgYW5kIGNhbGwgdGhlbSAkeF8xJywgeF8yJywgXGxkb3RzLCB4X3sxODR9JyQgYW5kICR5XzEnLCB5XzInLCBcbGRvdHMsIHlfezE4NH0nJC4gVGhlIHNjYXR0ZXIgcGxvdCBpcyB0aGUgcG9pbnRzICQoeF8xJywgeV8xJyksICh4XzInLCB5XzInKSwgXGxkb3RzLCAoeF97MTg0fScsIHlfezE4NH0nKSQuIExldCAkXG1hdGhybXttZWFufSh4JykkIGFuZCAkXG1hdGhybXttZWFufSh5JykkIGJlIG1lYW4gb2YgJHhfMScsIHhfMicsIFxsZG90cywgeF97MTg0fSckIGFuZCAkeV8xJywgeV8yJywgXGxkb3RzLCB5X3sxODR9JyQuCgokJFxtYXRocm17U1N4eH0gPSBcc3VtX3tpPTF9XnsxODR9ICh4X2knIC0gXG1hdGhybXttZWFufSh4JykpXjIsIFxxdWFkICBcbWF0aHJte1NTeHl9ID0gXHN1bV97aT0xfV57MTg0fSAoeF9pJyAtIFxtYXRocm17bWVhbn0oeCcpKSh5X2knIC0gXG1hdGhybXttZWFufSh5JykpLiQkClRoZW4sIGJ5IHRoZW9yeSwgd2Uga25vdyB0aGUgc2xvcGUgYW5kIHRoZSB5LWluZXRyY2VwdCBhcyBmb2xsb3dzLgokJFxtYXRocm17U2xvcGV9ID0gXGZyYWN7XG1hdGhybXtTU3h5fX17XG1hdGhybXtTU3h4fX0sIFxxdWFkICBcbWF0aHJte0ludGVyY2VwdH0gPSBcbWF0aHJte21lYW59KHknKSAtIFxtYXRocm17U2xvcGV9XGNkb3QgXG1hdGhybXttZWFufSh4JykuJCQKYGBge3J9CmdkcGNvMmxvZ19zc3h4IDwtIHN1bSgobG9nMTAoZ2RwY28yXzIwMTkkZ2RwKS0gbWVhbihsb2cxMChnZHBjbzJfMjAxOSRnZHApKSleMikKZ2RwY28ybG9nX3NzeHkgPC0gc3VtKChsb2cxMChnZHBjbzJfMjAxOSRnZHApLSBtZWFuKGxvZzEwKGdkcGNvMl8yMDE5JGdkcCkpKSoobG9nMTAoZ2RwY28yXzIwMTkkY28yKS0gbWVhbihsb2cxMChnZHBjbzJfMjAxOSRjbzIpKSkpCmdkcGNvMmxvZ19zbG9wZSA8LSBnZHBjbzJsb2dfc3N4eS9nZHBjbzJsb2dfc3N4eApnZHBjbzJsb2dfaW50ZXJjZXB0IDwtIG1lYW4obG9nMTAoZ2RwY28yXzIwMTkkY28yKSkgLSBnZHBjbzJsb2dfc2xvcGUqbWVhbihsb2cxMChnZHBjbzJfMjAxOSRnZHApKQpjKEludGVyY2VwdCA9IGdkcGNvMmxvZ19pbnRlcmNlcHQsIFNsb3BlID0gZ2RwY28ybG9nX3Nsb3BlKQpgYGAKCk5vdyBjb25zaWRlciwgdGhlIHNxdWFyZWQgZGlmZmVyZW5jZSBhdCAkXGxvZ197MTB9KHhfaSkkIGJldHdlZW4gdGhlIGFjdHVhbCB2YWx1ZSAkXGxvZ197MTB9KHlfaSkkIGFuZCB0aGUgbGluZWFyIGVzdGltYXRpb24gJG1cY2RvdCBcbG9nX3sxMH0oeF9pKSArIGIkLiBTaW5jZSBpdCBpcyBhIHNxdWFyZSwgaXQgaXMgcG9zaXRpdmUuIE5vdyB0YWtlIHRoZSBzdW0gYW5kIHdhbnQgdG8gZGV0ZXJtaW5lICRtJCBhbmQgJGIkIHRvIG1pbmltaXplIHRoZSBzdW0sIHdoaWNoIGlzIGNhbGxlZCB0aGUgZXJyb3JzLiAKJCRcbWF0aHJte1NTRX0gPSBcc3VtX3tpPTF9XnsxODR9IChtXGNkb3QgXGxvZ197MTB9KHhfaSkgKyBiIC0gXGxvZ197MTB9KHlfaSkpXjIgPSBcc3VtX3tpPTF9XnsxODR9IChtXGNkb3QgeF9pJyArIGIgLSB5X2knKV4yLiQkCkxldCAkayQgYmUgdGhlIG51bWJlciBvZiB2YXJpYWJsZXMgZm9yIGVzdGltYXRpb24uIEluIG91ciBjYXNlIHdlIGVzdGltYXRlIHVzaW5nIG9ubHkgb25lIHZhcmlhYmxlLCB3ZSBzZXQgJGs9MSQuIFRoZSBmb2xsb3dpbmcgdmFsdWUgaXMgY2FsbGVkIHRoZSBSZXNpZHVhbCBTdGFuZGFyZCBFcnJvci4gJG4tKGsrMSkgPSAxODIkIGlzIGNhbGxlZCB0aGUgZGVncmVlIG9mIGZyZWVkb20uCiQkXG1ib3h7UmVzaWR1YWwgU3RhbmRhcmQgRXJyb3J9ID0gXHNxcnR7XGZyYWN7XG1hdGhybXtTU0V9fXtuLShrKzEpfX0gPSBcc3FydHtcZnJhY3tcbWF0aHJte1NTRX19ezE4Mn19JCQKYGBge3IgZ2RwY28ybG9nOlJTRX0KZ2RwY28ybG9nX21vZGVsIDwtIGxtKGxvZzEwKGNvMikgfiBsb2cxMChnZHApLCBkYXRhID0gZ2RwY28yXzIwMTkpCmdkcGNvMmxvZ19zc2UgPC0gc3VtKChsb2cxMChnZHBjbzJfMjAxOSRjbzIpIC0gZ2RwY28ybG9nX21vZGVsJGZpdHRlZC52YWx1ZXMpXjIpCihnZHBjbzJsb2dfcnNlIDwtIHNxcnQoZ2RwY28ybG9nX3NzZS8xODIpKQpgYGAKClRoZSBlc3RpbWF0ZWQgdmFsdWUgb2YgJGIkIGFwcGVhcnMgaW4gdGhlIHJvdyAoSW50ZXJjZXB0KSBhbmQgJG0kIGFwcGVhcnMgaW4gdGhlIHJvdyBsb2cxMChnZHApLiBSb3VnaGx5IHNwZWFraW5nIGl0IGlzIHRoZSBiZXN0IGZpdCBsaW5lIGFnYWluc3QgdGhlIGRhdGEgcG9pbnRzIG1pbmltaXppbmcgdGhlIHN1bSBvZiB0aGUgc3F1YXJlZCBkaXN0YW5jZXMuCgpUbyBub3JtYWxpemUgdGhlIHNjYWxlLCB3ZSBkaXZpZGUgdGhlIHZhbHVlICRcbWF0aHJte1NTeXl9JCBhbmQgY29uc2lkZXIgdGhlIGZvbGxvd2luZy4KJCRcbWF0aHJte1NTeXl9ID0gXHN1bV97aT0xfV57MTg0fSAoXGxvZ197MTB9KHlfaSkgLSBcbWF0aHJte21lYW59KV4yID0gXHN1bV97aT0xfV57MTg0fSAoeV9pJy1cbWF0aHJte21lYW59KV4yLCBccXVhZCBcbWJveHtNdWx0aXBsZSBSIFNxdWFyZWR9ID0gMS1cZnJhY3tcbWF0aHJte1NTRX19e1xtYXRocm17U1N5eX19JCQKCgpgYGB7ciBnZHBjbzJsb2c6TVJTfQpnZHBjbzJsb2dfc3N5eSA8LSBzdW0oKGxvZzEwKGdkcGNvMl8yMDE5JGNvMiktIG1lYW4obG9nMTAoZ2RwY28yXzIwMTkkY28yKSkpXjIpCjEtZ2RwY28ybG9nX3NzZS9nZHBjbzJsb2dfc3N5eQpgYGAKCioqQ29uY2x1c2lvbioqOiBSb3VnaGx5IHNwZWFraW5nLCB0aGUgbGluZWFyIG1vZGVsIGZpdHMgbXVjaCBiZXR0ZXIgdGhhbiB0aGUgZXN0aW1hdGlvbiBieSBtZWFucyBhbmQgdGhlIG1vZGVsIGV4cGxhaW5zIGFib3V0IDc3JSBvZiB0aGUgc2NhdHRlcmVkIHBvaW50cy4KCiMgUmVmZXJlbmNlcwoKIyMgRGF0YSB7I2NoYXA6ZGF0YX0KCiMjIyBBIExpc3Qgb2YgT3BlbiBEYXRhIENhdGFsb2d1ZQoKIyMjIyBJbnRlcm5hdGlvbmFsIEluc3RpdHV0aW9ucwoKKiBXb3JsZCBCYW5rOiBfTmV3IFdheXMgb2YgTG9va2luZyBhdCBQb3ZlcnR5XwogIC0gIE9wZW4gRGF0YTogaHR0cHM6Ly9kYXRhLndvcmxkYmFuay5vcmcKICAtICBXb3JsZCBEZXZlbG9wbWVudCBJbmRpY2F0b3JzOiBodHRwOi8vZGF0YXRvcGljcy53b3JsZGJhbmsub3JnL3dvcmxkLWRldmVsb3BtZW50LWluZGljYXRvcnMvCiogVU4gRGF0YTogaHR0cDovL2RhdGEudW4ub3JnCiogV0hPIERhdGE6IGh0dHBzOi8vd3d3Lndoby5pbnQvZ2hvL2VuLwoqIE9FQ0Q6IGh0dHBzOi8vZGF0YS5vZWNkLm9yZwoqIEV1cm9wZWFuIFVuaW9uOiBodHRwOi8vZGF0YS5ldXJvcGEuZXUvZXVvZHAvZW4vaG9tZQoqIEFmcmljYW4gVW5pb246IGh0dHBzOi8vYXUuaW50L2VuL2VhL3N0YXRpc3RpY3MKCiMjIyMgR292ZXJtZW50cwoKKiBVbml0ZWQgU3RhdGVzOiBodHRwczovL3d3dy5kYXRhLmdvdgoqIFVuaXRlZCBLaW5nZG9tOiBodHRwczovL2RhdGEuZ292LnVrCiogQ2hpbmE6IGh0dHA6Ly93d3cuc3RhdHMuZ292LmNuL2VuZ2xpc2gvCiogSmFwYW46IGh0dHBzOi8vd3d3LmRhdGEuZ28uanAvbGlzdC1vZi1kYXRhYmFzZS8/bGFuZz1lbgoKIyMjIyBPdGhlciBPcGVuIFB1YmxpYyBEYXRhCgoqIEdvb2dsZSBQdWJsaWMgRGF0YSBFeHBsb3JlOiBodHRwczovL3d3dy5nb29nbGUuY29tL3B1YmxpY2RhdGEvZGlyZWN0b3J5P2hsPWVuX1VTCiAgLSBHb29nbGUgRGF0YXNldCBTZWFyY2g6IGh0dHBzOi8vdG9vbGJveC5nb29nbGUuY29tL2RhdGFzZXRzZWFyY2gKICAtIEdvb2dsZSBUcmVuZHM6IGh0dHBzOi8vdHJlbmRzLmdvb2dsZS5jb20vdHJlbmRzLz9nZW89VVMKKiBPcGVuIEtub3dsZWRnZSBGb3VuZGF0aW9uOiBodHRwczovL29rZm4ub3JnCiAgLSBHbG9iYWwgT3BlbiBEYXRhIEluZGV4OiBodHRwczovL2luZGV4Lm9rZm4ub3JnCiAgLSBBIGdsb2JhbCwgbm9uLXByb2ZpdCBuZXR3b3JrIHRoYXQgcHJvbW90ZXMgYW5kIHNoYXJlcyBpbmZvcm1hdGlvbiBhdCBubyBjaGFyZ2UsIGluY2x1ZGluZyBib3RoIGNvbnRlbnQgYW5kIGRhdGEuIEl0IHdhcyBmb3VuZGVkIGJ5IFJ1ZnVzIFBvbGxvY2sgb24gMjAgTWF5IDIwMDQgYW5kIGxhdW5jaGVkIG9uIDI0IE1heSAyMDA0IGluIENhbWJyaWRnZSwgVUsuIEl0IGlzIGluY29ycG9yYXRlZCBpbiBFbmdsYW5kIGFuZCBXYWxlcyBhcyBhIGNvbXBhbnkgbGltaXRlZCBieSBndWFyYW50ZWUuIFxoZmlsbCAoV2lraXBlZGlhKQoqIE91ciBXb3JsZCBpbiBEYXRhOiBodHRwczovL291cndvcmxkaW5kYXRhLm9yZwogIC0gQSBzY2llbnRpZmljIG9ubGluZSBwdWJsaWNhdGlvbiB0aGF0IGZvY3VzZXMgb24gbGFyZ2UgZ2xvYmFsIHByb2JsZW1zIHN1Y2ggYXMgcG92ZXJ0eSwgZGlzZWFzZSwgaHVuZ2VyLCBjbGltYXRlIGNoYW5nZSwgd2FyLCBleGlzdGVudGlhbCByaXNrcywgYW5kIGluZXF1YWxpdHkuClRoZSBwdWJsaWNhdGlvbidzIGZvdW5kZXIgaXMgdGhlIHNvY2lhbCBoaXN0b3JpYW4gYW5kIGRldmVsb3BtZW50IGVjb25vbWlzdCBNYXggUm9zZXIuIFRoZSByZXNlYXJjaCB0ZWFtIGlzIGJhc2VkIGF0IHRoZSBVbml2ZXJzaXR5IG9mIE94Zm9yZC4gXGhmaWxsIChXaWtpcGVkaWEpCgoKIyMjIyBNaXNjZWxsYW5lb3VzIERhdGEgc2V0cwoKMS4gW0RGSUQgcG9ydGFsXShodHRwczovL2RldnRyYWNrZXIuZGZpZC5nb3YudWsvKQoyLiBbRVUgQWlkIHBvcnRhbF0oaHR0cHM6Ly9ldWFpZGV4cGxvcmVyLmVjLmV1cm9wYS5ldS8pCjMuIFtFdXJvc3RhdF0oaHR0cHM6Ly9lYy5ldXJvcGEuZXUvZXVyb3N0YXQpCjQuIFtGQU9TVEFUXShodHRwOi8vd3d3LmZhby5vcmcvZmFvc3RhdC9lbi8jaG9tZSkKNS4gW09wZW5haWQgTmV0aGVybGFuZHNdKGh0dHA6Ly9vcGVuYWlkLm5sLykKNi4gW09wZW5haWQgU3dlZGVuXShodHRwOi8vb3BlbmFpZC5zZS9haWQvKQo3LiBbT3hmYW0gZGF0YSBzZXRzXShodHRwczovL3BvbGljeS1wcmFjdGljZS5veGZhbS5vcmcudWsvb3VyLWFwcHJvYWNoL2FjY291bnRhYmlsaXR5LWFuZC10cmFuc3BhcmVuY3kvb3VyLWRhdGEpCjguIFtTZWEgQXJvdW5kIFVzIChVbml2ZXJzaXR5IG9mIEJyaXRpc2ggQ29sdW1iaWEpXShodHRwOi8vd3d3LnNlYWFyb3VuZHVzLm9yZy9kYXRhLyMvZWV6KQo5LiBbVU4gSGFiaXRhdCBwb3J0YWxdKGh0dHA6Ly9vcGVuLnVuaGFiaXRhdC5vcmcvKQoxMC4gW1VORVNDTyBwb3J0YWxdKGh0dHA6Ly9vcGVuZGF0YS51bmVzY28ub3JnLykKMTIuIFtVU0FJRCBwb3J0YWxdKGh0dHA6Ly9vcGVuLnVuZHAub3JnLykKMTMuIFtXb3JsZCBQb3ZlcnR5IENsb2NrIChXb3JsZCBEYXRhIExhYildKGh0dHBzOi8vYWlkc2NhcGUudXNhaWQuZ292LykKCgojIyBCb29rcwoKWW91IGNhbiByZWFkIG1hbnkgZXhjZWxsZW50IGJvb2tzIG9ubGluZS4KCiogW1JTdHVkaW8gPiBSZXNvdXJjZXMgPiBCb29rc10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2Jvb2tzLykKKiBbQm9va2Rvd25dKGh0dHBzOi8vYm9va2Rvd24ub3JnKSBhbmQgaXRzIFthcmNoaXZlXShodHRwczovL2Jvb2tkb3duLm9yZy9ob21lL2FyY2hpdmUvKSBwYWdlCgojIyBXZWJzaXRlcwoKKiBbUlN0dWRpb10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20pCiAgLSBFZHVjYXRpb246IGh0dHBzOi8vZWR1Y2F0aW9uLnJzdHVkaW8uY29tCiogUiBCYXNpY3M6CiAgLSBbUiBmb3IgQmVnaW5uZXJzXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9kb2MvY29udHJpYi9QYXJhZGlzLXJkZWJ1dHNfZW4ucGRmKSBpbiBQREYKICAtIFtRdWljay1SIGJ5IERhdGFDYW1wXShodHRwczovL3d3dy5zdGF0bWV0aG9kcy5uZXQvci10dXRvcmlhbC9pbmRleC5odG1sKQoqIERhdGEgU2NpZW5jZSBmb3IgU29jaWFsIFNjaWVudGlzdHM6IGh0dHBzOi8vZGF0YXNjaWVuY2UudG50bGFiLm9yZwogIC0gUiBmb3IgU29jaWFsIFNjaWVudGlzdHM6IGh0dHBzOi8vZGF0YWNhcnBlbnRyeS5vcmcvci1zb2NpYWxzY2kvCiAgCiogUk1hcmtkb3duCiAgLSBbUlN0dWRpbyBTaXRlXShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9pbmRleC5odG1sKQoKIyMgSW50ZXJhY3RpdmUgRXhlcmNpc2VzCgoqIFtSU3R1ZGlvIFByaW1lcnNdKGh0dHBzOi8vcnN0dWRpby5jbG91ZC9sZWFybi9wcmltZXJzKTogVGhlIGZvbGxvd2luZyBmb3VyIHNldHMgb2YgaW50ZXJhY3RpdmUgZXhlcmNpc2VzIHdyaXR0ZW4gdXNpbmcgYGxlYXJucmAgcGFja2FnZSBoZWxwIHlvdSB0byByZXZpZXcgYW5kIGNvbnNvbGlkYXRlIHlvdXIgdW5kZXJzdGFuZGluZyBvZiBiYXNpcyBvZiBSLgogIC0gVGhlIEJhc2ljcwogIC0gV29yayB3aXRoIERhdGEKICAtIFZpc3VhbGl6ZSBEYXRhCiAgLSBUaWR5IFlvdXIgRGF0YQoK