Loading [MathJax]/jax/output/HTML-CSS/jax.js
  • 1 Introduction
    • 1.1 Before We Start
    • 1.2 R, RStudio and Packages
      • 1.2.1 Setup
      • 1.2.2 Brief Introduction to R on RStudio
      • 1.2.3 References
  • 2 Introduction to Data Analysis
    • 2.1 First Example
      • 2.1.1 Importing data
      • 2.1.2 Look at the data
      • 2.1.3 Visualizing Data
      • 2.1.4 Various Plots of Density
      • 2.1.5 Data Modeling
  • 3 Data Analysis Using RStudio
    • 3.1 Setup
      • 3.1.1 Create a Project
      • 3.1.2 Create a Data Folder
      • 3.1.3 Create a New R Notebook
      • 3.1.4 Edit YAML
      • 3.1.5 Create a Code Chunk to Attach Packages
      • 3.1.6 Find Data
      • World Bank:Open Data Defined
    • 3.2 Import Data
      • 3.2.1 Use the File Link
      • 3.2.2 Read from the data Folder
      • 3.2.3 Use the API
      • 3.2.4 Function WDIsearch
      • 3.2.5 Download Data by WDI
    • 3.3 Wrangle Data
    • 3.4 Visualize Data
      • 3.4.1 ggplot2
    • 3.5 Examples
      • 3.5.1 GDP Per Capita
      • 3.5.2 CO2 Emission Per Capita
      • 3.5.3 GDP Per Capita vs CO2 Emission Per Capita
  • 4 References
    • 4.1 Data
      • 4.1.1 A List of Open Data Catalogue
    • 4.2 Books
    • 4.3 Websites
    • 4.4 Interactive Exercises

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
ABCDEFGHIJ0123456789
Sepal.Length
<dbl>
Sepal.Width
<dbl>
Petal.Length
<dbl>
Petal.Width
<dbl>
Species
<fctr>
5.13.51.40.2setosa
4.93.01.40.2setosa
4.73.21.30.2setosa
4.63.11.50.2setosa
5.03.61.40.2setosa
5.43.91.70.4setosa
4.63.41.40.3setosa
5.03.41.50.2setosa
4.42.91.40.2setosa
4.93.11.50.1setosa

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

slice(df_iris, 1:10)
ABCDEFGHIJ0123456789
Sepal.Length
<dbl>
Sepal.Width
<dbl>
Petal.Length
<dbl>
Petal.Width
<dbl>
Species
<fctr>
5.13.51.40.2setosa
4.93.01.40.2setosa
4.73.21.30.2setosa
4.63.11.50.2setosa
5.03.61.40.2setosa
5.43.91.70.4setosa
4.63.41.40.3setosa
5.03.41.50.2setosa
4.42.91.40.2setosa
4.93.11.50.1setosa
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)
ABCDEFGHIJ0123456789
 
 
indicator
<chr>
name
<chr>
11431NY.GDP.PCAP.KDGDP per capita (constant 2015 US$)
11432NY.GDP.PCAP.KD.ZGGDP per capita growth (annual %)
WDIsearch(string = "NY.GDP.PCAP.KD", 
  field = "indicator", short = FALSE, cache = NULL) 
ABCDEFGHIJ0123456789
 
 
indicator
<chr>
name
<chr>
11431NY.GDP.PCAP.KDGDP per capita (constant 2015 US$)
11432NY.GDP.PCAP.KD.ZGGDP per capita growth (annual %)

Default: short = TRUE

WDIsearch(string = "gdp per capita", 
  field = "name", cache = NULL)
ABCDEFGHIJ0123456789
 
 
indicator
<chr>
7176.0.GDPpc_constant
2271CC.GHG.MEMG.GC
6236FB.DPT.INSU.PC.ZS
11209NV.AGR.PCAP.KD.ZG
11429NY.GDP.PCAP.CD
11430NY.GDP.PCAP.CN
11431NY.GDP.PCAP.KD
11432NY.GDP.PCAP.KD.ZG
11433NY.GDP.PCAP.KN
11434NY.GDP.PCAP.PP.CD
WDIsearch(string = "6.0.GDP_current", field = "indicator", short = FALSE)
ABCDEFGHIJ0123456789
 
 
indicator
<chr>
name
<chr>
7146.0.GDP_currentGDP (current $)

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)
ABCDEFGHIJ0123456789
 
 
indicator
<chr>
name
<chr>
6032EN.ATM.CO2E.PCCO2 emissions (metric tons per capita)

Further Examples

WDIsearch(string = "population, total", field = "name", short = FALSE)
ABCDEFGHIJ0123456789
 
 
indicator
<chr>
name
<chr>
9659JI.POP.URBN.ZSUrban population, total (% of total population)
17674SP.POP.TOTLPopulation, total
WDIsearch(string = "SP.POP.TOTL", field = "indicator", short = FALSE)
ABCDEFGHIJ0123456789
 
 
indicator
<chr>
name
<chr>
17674SP.POP.TOTLPopulation, total
17675SP.POP.TOTL.FE.INPopulation, female
17676SP.POP.TOTL.FE.ZSPopulation, female (% of total population)
17677SP.POP.TOTL.ICPSP.POP.TOTL.ICP:Population
17678SP.POP.TOTL.ICP.ZSSP.POP.TOTL.ICP.ZS:Population shares (World=100)
17679SP.POP.TOTL.MA.INPopulation, male
17680SP.POP.TOTL.MA.ZSPopulation, male (% of total population)
17681SP.POP.TOTL.ZSPopulation (% of total)

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
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
year
<int>
NY.GDP.PCAP.KD
<dbl>
Africa Eastern and SouthernZHAFE20211477.2493
Africa Eastern and SouthernZHAFE20201452.7303
Africa Eastern and SouthernZHAFE20191534.8901
Africa Eastern and SouthernZHAFE20181544.0780
Africa Eastern and SouthernZHAFE20171546.7956
Africa Eastern and SouthernZHAFE20161548.8131
Africa Eastern and SouthernZHAFE20151556.3165
Africa Eastern and SouthernZHAFE20141552.9870
Africa Eastern and SouthernZHAFE20131534.5577
Africa Eastern and SouthernZHAFE20121513.3697

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
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
year
<int>
status
<chr>
lastupdated
<chr>
NY.GDP.PCAP.KD
<dbl>
AfghanistanAFAFG19932022-09-16NA
AfghanistanAFAFG19972022-09-16NA
AfghanistanAFAFG19942022-09-16NA
AfghanistanAFAFG19952022-09-16NA
AfghanistanAFAFG20012022-09-16NA
AfghanistanAFAFG19982022-09-16NA
AfghanistanAFAFG19992022-09-16NA
AfghanistanAFAFG20072022-09-16392.7105
AfghanistanAFAFG20082022-09-16398.9711
AfghanistanAFAFG19802022-09-16NA

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
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
year
<dbl>
NY.GDP.PCAP.KD
<dbl>
Africa Eastern and SouthernZHAFE20211477.2493
Africa Eastern and SouthernZHAFE20201452.7303
Africa Eastern and SouthernZHAFE20191534.8901
Africa Eastern and SouthernZHAFE20181544.0780
Africa Eastern and SouthernZHAFE20171546.7956
Africa Eastern and SouthernZHAFE20161548.8131
Africa Eastern and SouthernZHAFE20151556.3165
Africa Eastern and SouthernZHAFE20141552.9870
Africa Eastern and SouthernZHAFE20131534.5577
Africa Eastern and SouthernZHAFE20121513.3697
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)
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
Africa Eastern and SouthernZH
Africa Western and CentralZI
Arab World1A
Caribbean small statesS3
Central Europe and the BalticsB8
Early-demographic dividendV2
East Asia & PacificZ4
East Asia & Pacific (excluding high income)4E
East Asia & Pacific (IDA & IBRD countries)T4
Euro areaXC
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"))
ABCDEFGHIJ0123456789
country
<chr>
iso3c
<chr>
region
<chr>
income
<chr>
lending
<chr>
AfghanistanAFGSouth AsiaLow incomeIDA
BurundiBDISub-Saharan AfricaLow incomeIDA
Burkina FasoBFASub-Saharan AfricaLow incomeIDA
Central African RepublicCAFSub-Saharan AfricaLow incomeIDA
Congo, Dem. Rep.CODSub-Saharan AfricaLow incomeIDA
EritreaERISub-Saharan AfricaLow incomeIDA
EthiopiaETHSub-Saharan AfricaLow incomeIDA
GuineaGINSub-Saharan AfricaLow incomeIDA
Gambia, TheGMBSub-Saharan AfricaLow incomeIDA
Guinea-BissauGNBSub-Saharan AfricaLow incomeIDA
(gdpcap_low <- semi_join(gdpcap, low_countries))
Joining, by = c("country", "iso3c")
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
year
<dbl>
NY.GDP.PCAP.KD
<dbl>
AfghanistanAFAFG2021NA
AfghanistanAFAFG2020529.7412
AfghanistanAFAFG2019555.1390
AfghanistanAFAFG2018546.7430
AfghanistanAFAFG2017553.3551
AfghanistanAFAFG2016552.9969
AfghanistanAFAFG2015556.0072
AfghanistanAFAFG2014565.1793
AfghanistanAFAFG2013568.9645
AfghanistanAFAFG2012557.9497
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
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
year
<dbl>
status
<lgl>
lastupdated
<date>
NY.GDP.PCAP.KD
<dbl>
AfghanistanAFAFG1993NA2022-09-16NA
AfghanistanAFAFG1997NA2022-09-16NA
AfghanistanAFAFG1994NA2022-09-16NA
AfghanistanAFAFG1995NA2022-09-16NA
AfghanistanAFAFG2001NA2022-09-16NA
AfghanistanAFAFG1998NA2022-09-16NA
AfghanistanAFAFG1999NA2022-09-16NA
AfghanistanAFAFG2007NA2022-09-16392.7105
AfghanistanAFAFG2008NA2022-09-16398.9711
AfghanistanAFAFG1980NA2022-09-16NA
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)
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
region
<chr>
CanadaCACANNorth America
FranceFRFRAEurope & Central Asia
GermanyDEDEUEurope & Central Asia
ItalyITITAEurope & Central Asia
JapanJPJPNEast Asia & Pacific
Russian FederationRURUSEurope & Central Asia
United KingdomGBGBREurope & Central Asia
United StatesUSUSANorth America

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
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
year
<dbl>
status
<lgl>
lastupdated
<date>
NY.GDP.PCAP.KD
<dbl>
AfghanistanAFAFG1993NA2022-09-16NA
AfghanistanAFAFG1997NA2022-09-16NA
AfghanistanAFAFG1994NA2022-09-16NA
AfghanistanAFAFG1995NA2022-09-16NA
AfghanistanAFAFG2001NA2022-09-16NA
AfghanistanAFAFG1998NA2022-09-16NA
AfghanistanAFAFG1999NA2022-09-16NA
AfghanistanAFAFG2007NA2022-09-16392.7105
AfghanistanAFAFG2008NA2022-09-16398.9711
AfghanistanAFAFG1980NA2022-09-16NA
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") 
ABCDEFGHIJ0123456789
country
<chr>
iso2c
<chr>
iso3c
<chr>
year
<dbl>
status
<lgl>
lastupdated
<date>
NY.GDP.PCAP.KD
<dbl>
AfghanistanAFAFG2021NA2022-09-16NA
AlbaniaALALB2021NA2022-09-164831.8687
AlgeriaDZDZA2021NA2022-09-163913.6529
American SamoaASASM2021NA2022-09-16NA
AndorraADAND2021NA2022-09-1637640.1263
AngolaAOAGO2021NA2022-09-162331.4954
Antigua and BarbudaAGATG2021NA2022-09-1613872.5822
ArgentinaARARG2021NA2022-09-1612390.8087
ArmeniaAMARM2021NA2022-09-164243.2379
ArubaAWABW2021NA2022-09-16NA

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())
ABCDEFGHIJ0123456789
year
<dbl>
n
<int>
1990155
1991157
1992161
1993162
1994163
1995174
1996174
1997176
1998175
1999176
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.90200x3.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 = x1,x2,,x184 and co2 = y1,y2,,y184. In log10 scale, log10(gdp) = log10(x1),log10(x2),,log10(x184) and log10(co2) = log10(y1),log10(y2),,log10(y184), and call them x1,x2,,x184 and y1,y2,,y184. The scatter plot is the points (x1,y1),(x2,y2),,(x184,y184). Let mean(x) and mean(y) be mean of x1,x2,,x184 and y1,y2,,y184.

SSxx=184i=1(ximean(x))2,SSxy=184i=1(ximean(x))(yimean(y)). Then, by theory, we know the slope and the y-inetrcept as follows. Slope=SSxySSxx,Intercept=mean(y)Slopemean(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 log10(xi) between the actual value log10(yi) and the linear estimation mlog10(xi)+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. SSE=184i=1(mlog10(xi)+blog10(yi))2=184i=1(mxi+byi)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. Residual Standard Error=SSEn(k+1)=SSE182

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 SSyy and consider the following. SSyy=184i=1(log10(yi)mean)2=184i=1(yimean)2,Multiple R Squared=1SSESSyy

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