1 Datascience for Macroeconomics using WDI

1.1 Introduction

1.1.1 Datascience

Datascience is an interdisciplinary academic field to extract or extrapolate knowledge and insights from noisy, structured, and unstructured data.

Why datascience? There are many reasons, but I believe it would promote evidence based, non-biased thinking.

1.1.1.1 Exploratory Data Analysis, EDA

EDA is an iterative cycle that helps you understand what your data says. When you do EDA, you:

  1. Generate questions about your data

  2. Search for answers by visualising, transforming, and/or modeling your data

  3. Use what you learn to refine your questions and/or generate new questions

EDA is an important part of any data analysis. You can use EDA to make discoveries about the world; or you can use EDA to ensure the quality of your data, asking questions about whether the data meets your standards or not. (Posit Primers: EDA)

1.1.2 Macroeconomics

The study of the entire economy of a region, a country, or the entire world

1.1.3 World Development Indicators

World Development Indicators (WDI) is the World Bank’s premier compilation of international statistics on global development. Drawing from officially recognized sources and including national, regional, and global estimates, the WDI provides access to approximately 1,600 indicators for 217 economies, with some time series extending back more than 50 years. The database helps users find information related to development, both current and historical. The topics covered in the WDI range from poverty, health, and demographics to GDP, trade, and the environment.

The World Development Indicators website provides access to data as well as information about data coverage, curation, and methodologies, and allows users to discover what type of indicators are available, how they are collected, and how they can be visualized to analyze development trends. (Wikipedia: Link)

1.2 Open Data and Public Data

Datascience and Artificial Intelligence

  • Computer Resources

  • Data

    • Big Data

    • Open and Public Data

Q. Explain ‘Open Data’ and ‘Public Data’, and their differences.

A. AI Poe Assistant. (Poe.com)

“Open data” and “public data” are related concepts that refer to different aspects of data accessibility and usage.

Open Data: Open data refers to the idea that certain data should be freely available to the public, without restrictions on access, usage, or redistribution. Open data is typically released in a machine-readable format, such as CSV, JSON, or XML, and is accompanied by an open license that allows anyone to use, reuse, and distribute the data for any purpose. The goal of open data is to promote transparency, collaboration, and innovation by enabling individuals, organizations, and communities to analyze and build upon the data.

Open data often includes government data, scientific research data, public records, and other datasets that are deemed to be of public interest. Governments, international organizations, and various institutions actively promote the release of open data to foster accountability, drive economic growth, and empower citizens to make informed decisions.

Public Data: Public data, on the other hand, refers to any data that is accessible or available to the public. It is a broader term that encompasses both open data and other types of data that may have certain restrictions on access or usage. Public data can include information that is freely and openly available, as well as data that is accessible through specific channels, such as public libraries, government agencies, or subscription-based services.

Public data can include a wide range of information, such as government reports, census data, legal documents, financial statements, and more. While public data may be accessible to the public, it does not necessarily guarantee the same level of openness, usability, or freedom as open data. Public data may have limitations on its usage, copyright restrictions, or conditions imposed by the data provider.

In summary, open data specifically refers to data that is freely accessible, usable, and redistributable without restrictions, often accompanied by an open license. Public data, on the other hand, is a broader term that includes both open data and other data that may have varying degrees of accessibility and usage restrictions.

1.2.0.1 World Bank on Open Data for Open Government

Visit Open Government Data Toolkit.

1.3 R on RStudio

1.3.1 Install R and RStudio

1.3.2 Posit Cloud

1.4 Datascience Workflow

The image above is from R4DS(2e) by Hadley Wickham, Mine Çetinkaya-Rundel and Garrett Grolemund
The image above is from R4DS(2e) by Hadley Wickham, Mine Çetinkaya-Rundel and Garrett Grolemund

1.5 First Datascience

We use two packages, tidyverse and WDI.

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

  • tidyverse: The tidyverse is a collection of open source packages for the R programming language introduced by Hadley Wickham and his team that “share an underlying design philosophy, grammar, and data structures” of tidy data. Characteristic features of tidyverse packages include extensive use of non-standard evaluation and encouraging piping. [CRAN Link]

  • WDI: Search and download data from over 40 databases hosted by the World Bank, including the World Development Indicators (‘WDI’), International Debt Statistics, Doing Business, Human Capital Index, and Sub-national Poverty indicators. [CRAN Link]

1.5.1 Setup

Step 1. Install packages if necessary.

install.packages("tidyverse")
install.packages("WDI")

Step 2. Load packages.

library(tidyverse)
library(WDI)

Step 3. Create a data directory for the first time.

dir.create("data")
Warning: 'data' already exists

Step 4. Set ‘System Language’ to be English, recommended.

Sys.setenv(LANG = "en")

1.5.2 Importing GDP Data

The following code chunk is to download GDP data with the following indicator code.

WDI indicator: NY.GDP.MKTP.PP.KD

df_gdp <- WDI(indicator = "NY.GDP.MKTP.PP.KD")

N.B. There are many GDP related data in WDI, for example, “NY.GDP.MKTP.CD”

To avoid the internet traffic, save the data and reuse it.

CSV: comma separated values, a text format of a data.

write_csv(df_gdp, "data/gdp.csv")

Run codes above only once to download and write the data into the data directory.

df_gdp <- read_csv("data/gdp.csv")
Rows: 16758 Columns: 5── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (3): country, iso2c, iso3c
dbl (2): year, NY.GDP.MKTP.PP.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.

1.5.3 Viewing Data

First, look at the data by head, str ucture, and summary.

head: display the first 6 rows by default

head(df_gdp)
head(df_gdp, 50)

2.561800e+12 is in scientific notation, i.e., \(2.561800 \times10^{12} = 2,562,800,000,000\).

str: display the structure of an object

str(df_gdp)
spc_tbl_ [16,758 × 5] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ country          : chr [1:16758] "Africa Eastern and Southern" "Africa Eastern and Southern" "Africa Eastern and Southern" "Africa Eastern and Southern" ...
 $ iso2c            : chr [1:16758] "ZH" "ZH" "ZH" "ZH" ...
 $ iso3c            : chr [1:16758] "AFE" "AFE" "AFE" "AFE" ...
 $ year             : num [1:16758] 2022 2021 2020 2019 2018 ...
 $ NY.GDP.MKTP.PP.KD: num [1:16758] 2.56e+12 2.47e+12 2.37e+12 2.43e+12 2.38e+12 ...
 - attr(*, "spec")=
  .. cols(
  ..   country = col_character(),
  ..   iso2c = col_character(),
  ..   iso3c = col_character(),
  ..   year = col_double(),
  ..   NY.GDP.MKTP.PP.KD = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 

summary: display the summary of an object

summary(df_gdp)
   country             iso2c              iso3c                year      NY.GDP.MKTP.PP.KD  
 Length:16758       Length:16758       Length:16758       Min.   :1960   Min.   :2.482e+07  
 Class :character   Class :character   Class :character   1st Qu.:1975   1st Qu.:1.824e+10  
 Mode  :character   Mode  :character   Mode  :character   Median :1991   Median :1.055e+11  
                                                          Mean   :1991   Mean   :3.329e+12  
                                                          3rd Qu.:2007   3rd Qu.:1.083e+12  
                                                          Max.   :2022   Max.   :1.390e+14  
                                                                         NA's   :9096       

In RNotebook, the following also displays the first 1000 rows of the data in the paged format.

df_gdp

1.5.4 Transformation

|> , or %>%, is called a pipe operator and df_gdp |> filter(country == COUNTRY) is same as

filter(df_gdp, country == COUNTRY) .

filter : Keep rows that match a condition

COUNTRY <- "Japan"
df_gdp |> filter(country == COUNTRY)

1.5.5 Visualization

1.5.5.1 GDP of Japan

ggplot() + geom_line(): A tidyverse function of draw a line graph

aes(year, NY.GDP.MKTP.PP.KD) : aesthetic mapping sending year to x-axis and NY.GDP.MKTP.PP.KD to y-axis

COUNTRY <- "Japan"
df_gdp |> filter(country == COUNTRY) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD)) + geom_line()

Let’s delete the rows with missing values using drop_na(NY.GDP.MKTP.PP.KD), a transformation.

COUNTRY <- "Japan"
df_gdp |> filter(country == COUNTRY) |> drop_na(NY.GDP.MKTP.PP.KD) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD)) + geom_line()

1.5.5.2 GDP of the World

COUNTRY <- "World"
df_gdp |> filter(country == COUNTRY) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD)) + geom_line()

1.5.5.3 Exercise.

  1. Delete the missing data of the GDP of the World by mimicking the one of Japan.
  2. Write down your observations and questions.
COUNTRY <- "World"
df_gdp |> filter(country == COUNTRY) |> drop_na(NY.GDP.MKTP.PP.KD) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD)) + geom_line()

Observations and Questions

  • e.g. The GDP of the world is continuously increasing since 1990.

  • There drops in the year around 2008 and 2020

1.5.5.4 GDP of the countries

By country names

COUNTRIES <- c("Japan", "China", "India", "United Kingdom", "United States", "Germany", "France")
df_gdp |> filter(country %in% COUNTRIES) |> drop_na(NY.GDP.MKTP.PP.KD) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD, color = country)) + geom_line()

ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_gdp |> filter(iso2c %in% ISO2C) |> drop_na(NY.GDP.MKTP.PP.KD) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD, col = iso2c)) + geom_line()

1.5.5.5 Exercise.

What happens if you replace color = iso2c at the bottom of the code above with colour = iso2c , color = country , col = country ?

ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_gdp |> filter(iso2c %in% ISO2C) |> drop_na(NY.GDP.MKTP.PP.KD) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD, color = country)) + geom_line()

1.5.5.6 List of Countries and iso2c

(df_codes <- df_gdp |> distinct(country, iso2c))

1.5.5.7 Exercise.

Set COUNTRIES and/or ISO2C to draw line graphs of GDP.

COUNTRIES <- c() # surround the country name with quotation marks, and use a comma as a separator
df_gdp |> filter(country %in% COUNTRIES) |> drop_na(NY.GDP.MKTP.PP.KD) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD, color = country)) + geom_line()
ISO2C <- c() # surround the iso2c code with quotation marks, and use a comma as a separator
df_gdp |> filter(iso2c %in% ISO2C) |> drop_na(NY.GDP.MKTP.PP.KD) |>
  ggplot(aes(year, NY.GDP.MKTP.PP.KD, color = iso2c)) + geom_line()

2 World Development Indicators

2.1 Find Indicator Codes

  1. World Bank Home Page

    • World Development Indicators: Link

    • World Bank Open Data: Link

    • WDI Statistical Tables: Link

  2. Excel Files

    • Data Catalogues: Link Download Excel File
  3. API Search

WDIsearch(string = "gdp", field = "name")
WDIsearch(string = "NY.GDP.MKTP.PP.KD", field = "indicator", short = FALSE)

2.1.0.1 Assignment

  1. Find at least one WDI indicator with its name and its code.

  2. Find at least one pair of WDI indicators with their names and their codes you want to study their relation.

2.2 GDP and GDP per Capita

  1. GDP, PPP (constant 2017 international $): NY.GDP.MKTP.PP.KD

  2. Population, total: SP.POP.TOTL

  3. Calculate GDP per Capita

    • GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD
  • GDP, PPP (constant 2017 international $) PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP is the sum of gross value added by all resident producers in the country 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 2017 international dollars. ID: NY.GDP.MKTP.PP.KD

  • Population, total Total population is based on the de facto definition of population, which counts all residents regardless of legal status or citizenship. The values shown are midyear estimates. ID: SP.POP.TOTL

df_gdppcap <- WDI(indicator = c(gdp = "NY.GDP.MKTP.PP.KD", pop = "SP.POP.TOTL", gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_gdppcap, "data/gdppcap.csv")
/bin/sh: rE: command not found
df_gdppcap <- read_csv("data/gdppcap.csv")
Rows: 16758 Columns: 15── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (6): year, gdp, pop, gdppcap, 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.
str(df_gdppcap)
spc_tbl_ [16,758 × 15] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ country    : chr [1:16758] "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
 $ iso2c      : chr [1:16758] "AF" "AF" "AF" "AF" ...
 $ iso3c      : chr [1:16758] "AFG" "AFG" "AFG" "AFG" ...
 $ year       : num [1:16758] 2014 2012 2009 2013 1971 ...
 $ status     : logi [1:16758] NA NA NA NA NA NA ...
 $ lastupdated: Date[1:16758], format: "2023-09-19" "2023-09-19" "2023-09-19" ...
 $ gdp        : num [1:16758] 7.02e+10 6.47e+10 4.99e+10 6.83e+10 NA ...
 $ pop        : num [1:16758] 32716210 30466479 27385307 31541209 11015857 ...
 $ gdppcap    : num [1:16758] 2144 2123 1824 2165 NA ...
 $ region     : chr [1:16758] "South Asia" "South Asia" "South Asia" "South Asia" ...
 $ capital    : chr [1:16758] "Kabul" "Kabul" "Kabul" "Kabul" ...
 $ longitude  : num [1:16758] 69.2 69.2 69.2 69.2 69.2 ...
 $ latitude   : num [1:16758] 34.5 34.5 34.5 34.5 34.5 ...
 $ income     : chr [1:16758] "Low income" "Low income" "Low income" "Low income" ...
 $ lending    : chr [1:16758] "IDA" "IDA" "IDA" "IDA" ...
 - attr(*, "spec")=
  .. cols(
  ..   country = col_character(),
  ..   iso2c = col_character(),
  ..   iso3c = col_character(),
  ..   year = col_double(),
  ..   status = col_logical(),
  ..   lastupdated = col_date(format = ""),
  ..   gdp = col_double(),
  ..   pop = col_double(),
  ..   gdppcap = col_double(),
  ..   region = col_character(),
  ..   capital = col_character(),
  ..   longitude = col_double(),
  ..   latitude = col_double(),
  ..   income = col_character(),
  ..   lending = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 
df_gdppcap |> select(region, income, lending) |> lapply(unique)
$region
[1] "South Asia"                 "Aggregates"                 "Europe & Central Asia"     
[4] "Middle East & North Africa" "East Asia & Pacific"        "Sub-Saharan Africa"        
[7] "Latin America & Caribbean"  "North America"              NA                          

$income
[1] "Low income"          "Aggregates"          "Upper middle income" "Lower middle income"
[5] "High income"         NA                    "Not classified"     

$lending
[1] "IDA"            "Aggregates"     "IBRD"           "Not classified" "Blend"          NA              
COUNTRY <- "World"
df_gdppcap |> filter(country == COUNTRY) |>
  ggplot(aes(year, gdppcap)) + geom_line()

COUNTRY <- "World"
df_gdppcap |> filter(country == COUNTRY) |>
  ggplot(aes(year, pop)) + geom_line()

2.2.0.1 Exercise.

Write your observations and questions.

  • GDP chart looks simiar to GDPPerCapit.

2.2.1 GDP Per Capita

df_gdppcap2 <- df_gdppcap |> drop_na(pop) |> 
  mutate(PCAP = gdp/pop, .after = gdppcap)
df_gdppcap2

2.2.1.1 Check against GDP per capita, PPP

df_gdppcap2 |> drop_na(gdppcap, PCAP) |> mutate(near = near(gdppcap, PCAP)) |> 
  summarize(numberofdata = n(), sum(near))
df_gdppcap2 |> filter(!near(gdppcap, PCAP))
df_gdppcap2 |> filter(!near(gdppcap, PCAP)) |> distinct(country) |> pull()
[1] "Cyprus"             "Morocco"            "Russian Federation" "Sudan"             
[5] "Tanzania"           "Ukraine"           

2.2.1.2 Exercise.

Write your observations and questions.

2.2.2 Visualization

Two useful questions.

  1. What type of variation occurs within my variables?

  2. What type of covariation occurs between my variables?

See Link.

2.2.2.1 Ranks.

arrange(desc(gdp)) is to reorder in descending order of gdp, arrange(gdp) in ascending order.

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> 
  drop_na(gdp) |> arrange(desc(gdp))

2.2.2.2 Exercises.

  1. Find the top 10 of the countries with the highest GDP per capita.
df_gdppcap |> filter(year == 2022, region != "Aggregates") |> 
  drop_na(gdppcap) |> arrange(desc(gdppcap))
  1. Find the top 10 of the countries with the lowest GDP per capita.
df_gdppcap |> filter(year == 2022, region != "Aggregates") |> 
  drop_na(gdppcap) |> arrange(gdppcap)
  1. Find the top 10 of the countries with the largest population.

  2. Find the top 10 of the countries with the smallest population.

2.2.2.3 Scatter Plot

What type of covariation occurs between my variables?

df_gdppcap2 |> filter(year == 2022, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp)) + geom_point()

df_gdppcap2 |> filter(year == 2022, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2022, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp)) + geom_point() + 
  geom_smooth(method = "lm", se = FALSE) +
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2022, region !="Aggregates") |> 
  drop_na(gdp, pop) |> lm(log10(gdp) ~ log10(pop), data = _) |> summary()

Call:
lm(formula = log10(gdp) ~ log10(pop), data = drop_na(filter(df_gdppcap2, 
    year == 2022, region != "Aggregates"), gdp, pop))

Residuals:
     Min       1Q   Median       3Q      Max 
-1.22646 -0.39512  0.03996  0.42553  0.95842 

Coefficients:
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  4.45320    0.27485   16.20   <2e-16 ***
log10(pop)   0.94704    0.03998   23.69   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.5117 on 181 degrees of freedom
Multiple R-squared:  0.7561,    Adjusted R-squared:  0.7548 
F-statistic: 561.2 on 1 and 181 DF,  p-value: < 2.2e-16
df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp, color = region)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> 
  drop_na(gdp, pop) |> 
  ggplot(aes(pop, gdp, color = region, shape = income)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> 
  drop_na(gdp, gdppcap, pop) |> 
  ggplot(aes(gdppcap, gdp, color = region, size = pop)) + geom_point() + 
  scale_x_log10() + scale_y_log10()

install.packages("plotly")
trying URL 'https://cran.rstudio.com/bin/macosx/big-sur-x86_64/contrib/4.3/plotly_4.10.3.tgz'
Content type 'application/x-gzip' length 3198308 bytes (3.1 MB)
==================================================
downloaded 3.1 MB

The downloaded binary packages are in
    /var/folders/sf/0qlks6k13n512p460v20b87m0000gn/T//Rtmp45fPtT/downloaded_packages
library(plotly)
test <- df_gdppcap2 |> filter(year == 2020, region !="Aggregates") |> drop_na(gdp, pop) |> 
  ggplot(aes(color = country, shape = region, pop, gdp)) + geom_point() + 
  scale_x_log10() + scale_y_log10() + theme(legend.position = "none")
test |> ggplotly()

3 Exploratory Data Analysis (EDA)

The image above is from R4DS(2e) by Hadley Wickham, Mine Çetinkaya-Rundel and Garrett Grolemund
The image above is from R4DS(2e) by Hadley Wickham, Mine Çetinkaya-Rundel and Garrett Grolemund

EDA is an iterative cycle that helps you understand what your data says. When you do EDA, you:

  1. Generate questions about your data

  2. Search for answers by visualising, transforming, and/or modeling your data

  3. Use what you learn to refine your questions and/or generate new questions

EDA is an important part of any data analysis. You can use EDA to make discoveries about the world; or you can use EDA to ensure the quality of your data, asking questions about whether the data meets your standards or not. (Posit Primers: EDA)

3.1 GDP and GDP per Capita

  1. GDP, PPP (constant 2017 international $): NY.GDP.MKTP.PP.KD

  2. Population, total: SP.POP.TOTL

  3. Calculate GDP per Capita

    • GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD
  • GDP, PPP (constant 2017 international $) PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP is the sum of gross value added by all resident producers in the country 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 2017 international dollars. ID: NY.GDP.MKTP.PP.KD

  • Population, total Total population is based on the de facto definition of population, which counts all residents regardless of legal status or citizenship. The values shown are midyear estimates. ID: SP.POP.TOTL

df_gdppcap <- WDI(indicator = c(gdp = "NY.GDP.MKTP.PP.KD", pop = "SP.POP.TOTL", gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_gdppcap, "data/gdppcap.csv")
df_gdppcap <- read_csv("data/gdppcap.csv")
Rows: 16758 Columns: 15── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (6): year, gdp, pop, gdppcap, 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.
str(df_gdppcap)
spc_tbl_ [16,758 × 15] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ country    : chr [1:16758] "Afghanistan" "Afghanistan" "Afghanistan" "Afghanistan" ...
 $ iso2c      : chr [1:16758] "AF" "AF" "AF" "AF" ...
 $ iso3c      : chr [1:16758] "AFG" "AFG" "AFG" "AFG" ...
 $ year       : num [1:16758] 2014 2012 2009 2013 1971 ...
 $ status     : logi [1:16758] NA NA NA NA NA NA ...
 $ lastupdated: Date[1:16758], format: "2023-09-19" "2023-09-19" "2023-09-19" ...
 $ gdp        : num [1:16758] 7.02e+10 6.47e+10 4.99e+10 6.83e+10 NA ...
 $ pop        : num [1:16758] 32716210 30466479 27385307 31541209 11015857 ...
 $ gdppcap    : num [1:16758] 2144 2123 1824 2165 NA ...
 $ region     : chr [1:16758] "South Asia" "South Asia" "South Asia" "South Asia" ...
 $ capital    : chr [1:16758] "Kabul" "Kabul" "Kabul" "Kabul" ...
 $ longitude  : num [1:16758] 69.2 69.2 69.2 69.2 69.2 ...
 $ latitude   : num [1:16758] 34.5 34.5 34.5 34.5 34.5 ...
 $ income     : chr [1:16758] "Low income" "Low income" "Low income" "Low income" ...
 $ lending    : chr [1:16758] "IDA" "IDA" "IDA" "IDA" ...
 - attr(*, "spec")=
  .. cols(
  ..   country = col_character(),
  ..   iso2c = col_character(),
  ..   iso3c = col_character(),
  ..   year = col_double(),
  ..   status = col_logical(),
  ..   lastupdated = col_date(format = ""),
  ..   gdp = col_double(),
  ..   pop = col_double(),
  ..   gdppcap = col_double(),
  ..   region = col_character(),
  ..   capital = col_character(),
  ..   longitude = col_double(),
  ..   latitude = col_double(),
  ..   income = col_character(),
  ..   lending = col_character()
  .. )
 - attr(*, "problems")=<externalptr> 
df_gdppcap |> select(region, income, lending) |> lapply(unique)
$region
[1] "South Asia"                 "Aggregates"                 "Europe & Central Asia"     
[4] "Middle East & North Africa" "East Asia & Pacific"        "Sub-Saharan Africa"        
[7] "Latin America & Caribbean"  "North America"              NA                          

$income
[1] "Low income"          "Aggregates"          "Upper middle income" "Lower middle income"
[5] "High income"         NA                    "Not classified"     

$lending
[1] "IDA"            "Aggregates"     "IBRD"           "Not classified" "Blend"          NA              
COUNTRY <- "World"
df_gdppcap |> filter(country == COUNTRY) |>
  ggplot(aes(year, gdppcap)) + geom_line()

COUNTRY <- "World"
df_gdppcap |> filter(country == COUNTRY) |>
  ggplot(aes(year, pop)) + geom_line()

3.1.0.1 Exercise.

Write your observations and questions.

3.1.1 Visualization

Two useful questions.

  1. What type of variation occurs within my variables?

  2. What type of covariation occurs between my variables?

See Link.

3.1.1.1 Ranks.

arrange(desc(gdp)) is to reorder in descending order of gdp, arrange(gdp) in ascending order.

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> 
  drop_na(gdp) |> arrange(desc(gdp))

3.1.1.2 Exercises.

  1. Find the top 10 of the countries with the highest GDP per capita.

  2. Find the top 10 of the countries with the lowest GDP per capita.

  3. Find the top 10 of the countries with the largest population.

  4. Find the top 10 of the countries with the smallest population.

3.1.1.3 Variation 1. Histogram

Histogram Video in Posit Primers: Link

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdp) |> 
  ggplot(aes(gdp)) + geom_histogram()

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap)) + geom_histogram()

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdp) |> 
  ggplot(aes(gdp)) + geom_histogram() + scale_x_log10()

3.1.1.4 Exercises.

  1. Change bins, i.e., geom_histogram(bins = 20), etc.
df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdp) |> 
  ggplot(aes(gdp)) + geom_histogram(bins = 20) + scale_x_log10()

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap)) + geom_histogram(binwidth = 10000)

  1. Create a similar histogram of gdppcap by using scale_x_log10() and adjust the number of bins.
df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap)) + geom_histogram(bins = 10) + scale_x_log10()

  1. Create a similar histogram for population.
df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(pop) |> 
  ggplot(aes(pop)) + geom_histogram(bins = 20) + scale_x_log10()

  1. Write your observations and comments.

3.1.1.5 Extra.

df_gdppcap |> filter(year == 2022,region != "Aggregates") |> drop_na(pop) |> 
  group_by(region) |> 
  ggplot(aes(pop, fill = region)) + geom_histogram(col = "black", linewidth = 0.2) + scale_x_log10()

df_gdppcap |> filter(year == 2022, region != "Aggregates") |> drop_na(pop) |> 
  group_by(region) |> 
  ggplot(aes(pop, fill = income)) + geom_histogram(col = "black", linewidth = 0.2) + scale_x_log10()

World by Income and Region: [Link]

3.1.1.6 Variation 2. Boxplot

Boxplot Video in Posit Primers: Link

df_gdppcap2 |> filter(year %in% c(1990,2000, 2010, 2020)) |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap, factor(year))) + geom_boxplot() + scale_x_log10()

df_gdppcap2 |> filter(year %in% c(1990,2000, 2010, 2020)) |> drop_na(gdppcap) |> 
  ggplot(aes(gdppcap, factor(year))) + geom_boxplot() + scale_x_log10() +
  labs(title = "Distribution of the GDP per Capita of Countries", subtitle = "Year 1990, 2000, 2010, 2020", 
       y = "Year", x = "GDP per capita in log10 scale")

df_gdppcap2 |> filter(year == 2020) |> drop_na(gdppcap) |> 
  filter(income != "Aggregates") |> 
  ggplot(aes(gdppcap, income, fill = income)) + geom_boxplot() + scale_x_log10() +
  theme(legend.position = "none")

df_gdppcap2 |> filter(year == 2020) |> drop_na(gdppcap) |> 
  filter(income != "Aggregates") |> 
  ggplot(aes(gdppcap, factor(income, levels = c("High income", "Upper middle income", "Lower middle income", "Low income")), fill = income)) + geom_boxplot() + scale_x_log10() +
  labs(y = "") +
  theme(legend.position = "none")

df_gdppcap2 |> filter(year == 2020) |> drop_na(gdp) |> 
  filter(income != "Aggregates") |> 
  ggplot(aes(gdp, region, fill = region)) + geom_boxplot() + scale_x_log10() +
  theme(legend.position = "none")

3.2 CO2 Emissions Per Capita vs GDP Per Capita

  1. CO2 emissions (metric tons per capita): EN.ATM.CO2E.PC

  2. GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD

  • CO2 emissions (metric tons per capita) 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. EN.ATM.CO2E.PC

  • GDP per capita, PPP (constant 2017 international $) GDP per capita based on purchasing power parity (PPP). PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP at purchaser’s prices is the sum of gross value added by all resident producers in the country 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 2017 international dollars. ID: NY.GDP.PCAP.PP.KD

3.2.1 Importing Data

df_co2gdp <- WDI(indicator = c(co2pcap = "EN.ATM.CO2E.PC", gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_co2gdp, "data/co2gdp.csv")
df_co2gdp <- read_csv("data/co2gdp.csv")
Rows: 16758 Columns: 14── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, co2pcap, gdppcap, 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.

3.2.2 Visualization by Line Graphs

COUNTRY <- "World"
df_co2gdp |> filter(country == COUNTRY) |>
  ggplot(aes(year, co2pcap)) + geom_line()

ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_co2gdp |> filter(iso2c %in% ISO2C) |> drop_na(co2pcap) |>
  ggplot(aes(year, co2pcap, linetype = iso2c)) + geom_line()

3.2.2.1 Exercises.

  1. Change iso2c codes to those you want to investigate. Use df_codes under Environment
  2. Change linetype to col.
ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_co2gdp |> filter(iso2c %in% ISO2C) |> drop_na(co2pcap) |>
  ggplot(aes(year, co2pcap, col = iso2c)) + geom_line()

3.2.3 Scatterplot for Covariation

df_co2gdp |> filter(year == 2020) |> drop_na(co2pcap) |>
  ggplot(aes(gdppcap, co2pcap)) + geom_point()

df_co2gdp |> filter(year == 2020) |> 
  drop_na(gdppcap, co2pcap) |>
  ggplot(aes(gdppcap, co2pcap)) + geom_point() +
  scale_x_log10() + scale_y_log10()

3.2.3.1 Scatterplot with a regression line

df_co2gdp |> filter(year == 2020) |> 
  drop_na(gdppcap, co2pcap) |>
  ggplot(aes(gdppcap, co2pcap)) + geom_point() +
  geom_smooth(method = "lm", formula = 'y~x', se = FALSE) +
  scale_x_log10() + scale_y_log10()

3.2.3.2 Summary of a linear model

df_co2gdp |> filter(year == 2020) |> drop_na(gdppcap, co2pcap) |>
  lm(log10(co2pcap)~log10(gdppcap), data = _) |> summary()

Call:
lm(formula = log10(co2pcap) ~ log10(gdppcap), data = drop_na(filter(df_co2gdp, 
    year == 2020), gdppcap, co2pcap))

Residuals:
     Min       1Q   Median       3Q      Max 
-0.60778 -0.15660 -0.00651  0.16129  0.59437 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -4.31545    0.13386  -32.24   <2e-16 ***
log10(gdppcap)  1.13831    0.03288   34.62   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2362 on 228 degrees of freedom
Multiple R-squared:  0.8402,    Adjusted R-squared:  0.8395 
F-statistic:  1199 on 1 and 228 DF,  p-value: < 2.2e-16

3.3 School Enrollment vs GDP Per Capita

  1. School enrollment, secondary (% gross): SE.SEC.ENRR

  2. GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD

  • School enrollment, secondary (% gross) Gross enrollment ratio is the ratio of total enrollment, regardless of age, to the population of the age group that officially corresponds to the level of education shown. Secondary education completes the provision of basic education that began at the primary level, and aims at laying the foundations for lifelong learning and human development, by offering more subject- or skill-oriented instruction using more specialized teachers. SE.SEC.ENRR

  • GDP per capita, PPP (constant 2017 international $) GDP per capita based on purchasing power parity (PPP). PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP at purchaser’s prices is the sum of gross value added by all resident producers in the country 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 2017 international dollars. ID: NY.GDP.PCAP.PP.KD

3.3.1 Importing Data

df_secgdp <- WDI(indicator = c(sec = "SE.SEC.ENRR", gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_secgdp, "data/secgdp.csv")
df_secgdp <- read_csv("data/secgdp.csv")
Rows: 16758 Columns: 14── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, sec, gdppcap, 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.

3.3.2 Visualization by Line Graphs

COUNTRY <- "World"
df_secgdp |> filter(country == COUNTRY) |>
  ggplot(aes(year, sec)) + geom_line()

COUNTRIES <- c("Low income", "Low & middle income", "Lower middle income", "Middle income", "Upper middle income", "High income")
df_secgdp |> filter(country %in% COUNTRIES) |> drop_na(sec) |>
  ggplot(aes(year, sec, linetype = factor(country, levels = COUNTRIES))) + geom_line() +
  labs(linetype = "Income Levels")

3.3.2.1 Exercise.

Change COUNTRIES to ISO2C of countries you want to investigate. Use df_codes under Environment

df_secgdp |> filter(year == 2020) |> drop_na(sec) |>
  ggplot(aes(gdppcap, sec)) + geom_point()

df_secgdp |> filter(year == 2020) |> drop_na(gdppcap, sec) |>
  ggplot(aes(gdppcap, sec)) + geom_point() +
  scale_x_log10()

df_secgdp |> filter(year == 2020) |> drop_na(gdppcap, sec) |>
  ggplot(aes(gdppcap, sec)) + geom_point() +
  geom_smooth(method = "lm", formula = 'y~x', se = FALSE) +
  scale_x_log10()

df_secgdp |> filter(year == 2020) |> drop_na(gdppcap, sec) |>
  lm(sec~log10(gdppcap), data = _) |> summary()

Call:
lm(formula = sec ~ log10(gdppcap), data = drop_na(filter(df_secgdp, 
    year == 2020), gdppcap, sec))

Residuals:
    Min      1Q  Median      3Q     Max 
-53.777 -10.846  -1.173   9.006  66.996 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -102.994     11.933  -8.631 6.38e-15 ***
log10(gdppcap)   46.088      2.841  16.222  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 15.64 on 157 degrees of freedom
Multiple R-squared:  0.6263,    Adjusted R-squared:  0.624 
F-statistic: 263.2 on 1 and 157 DF,  p-value: < 2.2e-16

3.4 References

  1. R for Data Science (2e): Link. The First Edition: Link.

  2. Posit Primers: Link.

  3. Cheat Sheet: Link.

  4. Shared Project: https://posit.cloud/content/5539763

    • intro2r: Revised version of the materials used in QPFD411 AY2022 (2 periods)

      • intro2r-1, intro2r-2 or intro2r2023
      • HP Link
    • intro2rj (ECO232) and first_ds (ECO101) are in Japanese

  5. R for Social Scientists: https://datacarpentry.org/r-socialsci/

  6. Data Analysis for Researchers AY2022: Link.

  7. みんなのデータサイエンス - Data Science for All (in Japanese)

    • Digital Book Link - use English translation of Google Chrome

4 EDA using WDI

4.1 Exploratory Data Analysis, EDA

EDA is an iterative cycle that helps you understand what your data says. When you do EDA, you:

  1. Generate questions about your data

  2. Search for answers by visualising, transforming, and/or modeling your data

  3. Use what you learn to refine your questions and/or generate new questions

EDA is an important part of any data analysis. You can use EDA to make discoveries about the world; or you can use EDA to ensure the quality of your data, asking questions about whether the data meets your standards or not. (Posit Primers: EDA)

4.2 Workflow

  1. Importing data by WDI
df_dataframe_name <- WDI(indicators = c(name1 = "Indicator Code 1", 
name2 = "Indicator Code 2"), extra = TRUE)

Write and read:

write_csv(df_dataframe_name, "data/dataframe_name.csv")
df_dataframe_name <- read_csv("data/dataframe_name.csv")
  1. Viewing data by

head(), str(), summary(), and try df_dataframe_name. See also Environment Tab of RStudio.

  1. Transforming data by restricting the values of a variable.
df_dataframe_name |> filter(var == "value") 
df_dataframe_name |> filter(var %in% c("value_1", ... , "value_n") 
df_dataframe_name |> filter(var != "value") 
df_dataframe_name |> drop_na(var)
  • Creating a new variable by mutation. (A little advanced. PCAP = gdp/pop)
df_dataframe_name |> mutate(var_new = var1 * var2)}
  1. Change orders by arrange()
df_dataframe_name |> arrange(var)
df_dataframe_name |> arrange(dsc(var))
  1. Visualizing using ggplot() + geom_*()

    What type of variation occurs within my variables?

    What type of covariation occurs between my variables?

  • line graph
transformed_data |> ggplot(aes(year, name1)) + geom_line()
transformed_data |> ggplot(aes(year, name2)) + geom_line()
  • scatterplot
transformed_data |> ggplot(aes(name1, name2)) + geom_point()
transformed_data |> ggplot(aes(name1, name2)) + geom_point() + scale_x_log10()
  • scatterplot with a regression line
transformed_data |> ggplot(aes(name1, name2)) + geom_point() +
  geom_smooth(method = "lm", se = FALSE)
transformed_data |> ggplot(aes(name1, name2)) + geom_point() + 
  geom_smooth(method = "lm", se = FALSE) + scale_x_log10()
  • histogram
transformed_data |> ggplot(aes(name1)) + geom_histogram()
  • boxplot

categorical_var: factor(year), income, region

transformed_data |> ggplot(aes(categorical_var, name1)) + geom_boxplot()
  1. Do not forget to add your observations and questions.

4.3 Setup

library(tidyverse)
library(WDI)

5 Examples

5.1 CO2 Emissions Per Capita vs GDP Per Capita

We study the relation between the CO2 emission per capita and the GDP per capita using the following two World Development Indicators.

  1. CO2 emissions (metric tons per capita): EN.ATM.CO2E.PC
  • Description: CO2 emissions (metric tons per capita) 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. EN.ATM.CO2E.PC
  1. GDP per capita, PPP (constant 2017 international $): NY.GDP.PCAP.PP.KD
  • Description: GDP per capita, PPP (constant 2017 international $) GDP per capita based on purchasing power parity (PPP). PPP GDP is gross domestic product converted to international dollars using purchasing power parity rates. An international dollar has the same purchasing power over GDP as the U.S. dollar has in the United States. GDP at purchaser’s prices is the sum of gross value added by all resident producers in the country 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 2017 international dollars. ID: NY.GDP.PCAP.PP.KD

5.1.1 Importing Data

df_co2gdp <- WDI(indicator = c(co2pcap = "EN.ATM.CO2E.PC", gdppcap = "NY.GDP.PCAP.PP.KD"),
                 extra = TRUE)
write_csv(df_co2gdp, "data/co2gdp.csv")
df_co2gdp <- read_csv("data/co2gdp.csv")
Rows: 16758 Columns: 14── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, co2pcap, gdppcap, 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.

5.1.2 Visualization by Line Graphs

5.1.2.1 CO2 per capita

COUNTRY <- "World"
df_co2gdp |> filter(country == COUNTRY) |> drop_na(co2pcap) |>
  ggplot(aes(year, co2pcap)) + geom_line() +
  labs(title = expression(paste(CO[2], " per capita of the World")),
       y = expression(paste(CO[2], " per capita in tons")))

ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_co2gdp |> filter(iso2c %in% ISO2C) |> drop_na(co2pcap) |>
  ggplot(aes(year, co2pcap, col = iso2c)) + geom_line() +
  labs(title = expression(paste(CO[2], " per capita of seven conutries with large GDP")),
       subtitle = "China, Germany, France, United Kingdom, India, Japan, United States", 
       y = expression(paste(CO[2], " per capita in tons")))

5.1.2.2 GDP per capita

COUNTRY <- "World"
df_co2gdp |> filter(country == COUNTRY) |> drop_na(gdppcap) |>
  ggplot(aes(year, gdppcap)) + geom_line() +
  labs(title = "GDP per capita of the World")

ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_co2gdp |> filter(iso2c %in% ISO2C) |> drop_na(gdppcap) |>
  ggplot(aes(year, gdppcap, col = iso2c)) + geom_line() +
  labs(title = "GDP per capita of seven countries with large GDP",
       subtitle = "China, Germany, France, United Kingdom, India, Japan, United States", 
       y = "GDP per capita PPP",
       caption = "constant 2017 international usd")

5.1.2.3 Ranking of CO2 per capita

df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(co2pcap) |> arrange(desc(co2pcap))
df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(co2pcap) |> arrange(co2pcap)

Observations and Questions:

  • Top 10 countries of CO2 emission per capita:

    • Qtar, Bahrain, Brunei Darussalam, Kuwait, United Arab Emirates, Oman, Australia, Saudi Arabia, Canada, and United States
  • Lowest 10 countries of CO2 emission per capita:

    • Congo, Dem. Rep., Somalia, Central African Republic, Burundi, Malawi, Niger, Chad, Madagascar, Rwanda, Sierra Leone

5.1.2.4 Ranking of GDP per capita

df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(gdppcap) |> arrange(desc(gdppcap))
df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(gdppcap) |> arrange(gdppcap)

5.1.3 Histograms and Boxplots for Variation

5.1.3.1 CO2 per capita

df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(co2pcap) |> filter(income != "Not classified") |>
  ggplot(aes(co2pcap, fill = factor(income, levels = INCOME))) + geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() +
  labs(title = "Histogram of CO2 per capita in 2020", fill = "")

df_co2gdp |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(co2pcap) |> filter(co2pcap > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(co2pcap, fill = factor(year))) + 
  geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() + facet_wrap(~year) +
  labs(title = "Histogram of CO2 per capita in 1990, 2000, 2010, 2020", fill = "")

df_co2gdp |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(co2pcap) |> filter(co2pcap > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(co2pcap, factor(year), fill = factor(year))) + 
  geom_boxplot() + scale_x_log10() + labs(y = "") + theme(legend.position = "none")

df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(co2pcap) |> filter(co2pcap > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(co2pcap, factor(income, levels = INCOME), fill = income)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "CO2 per capita by income level", y = "", fill = "") +
  theme(legend.position = "none")

df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(co2pcap) |> filter(co2pcap > 0) |> 
  ggplot(aes(co2pcap, region, fill = region)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "CO2 per capita by region", y = "", fill = "") +
  theme(legend.position = "none")

5.1.3.2 GDP per capita

df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(gdppcap) |> filter(income != "Not classified") |>
  ggplot(aes(gdppcap, fill = factor(income, levels = INCOME))) + geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() +
  labs(title = "Histogram of GDP per capita in 2020", fill = "")

df_co2gdp |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(gdppcap) |> filter(gdppcap > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(gdppcap, fill = factor(year))) + 
  geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() + facet_wrap(~year) +
  labs(title = "Histogram of GDP per capita in 1990, 2000, 2010, 2020", fill = "") +
  theme(legend.position = "none")

df_co2gdp |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(gdppcap) |> filter(gdppcap > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(gdppcap, factor(year), fill = factor(year))) + 
  geom_boxplot() + scale_x_log10() + labs(y = "") + theme(legend.position = "none")

INCOME <- c("Low income", "Low & middle income", "Lower middle income", "Middle income", "Upper middle income", "High income")
df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(gdppcap) |> filter(gdppcap > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(gdppcap, factor(income, levels = INCOME), fill = income)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "GDP per capita by income level", y = "", fill = "") +
  theme(legend.position = "none")

df_co2gdp |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(gdppcap) |> filter(gdppcap > 0) |> 
  ggplot(aes(gdppcap, region, fill = region)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "GDP per capita by region", y = "", fill = "") +
  theme(legend.position = "none")

5.1.4 Scatterplot for Covariation

5.1.4.1 Scatterplot with a regression line

df_co2gdp |> filter(year == 2020) |> 
  drop_na(gdppcap, co2pcap) |>
  ggplot(aes(gdppcap, co2pcap)) + geom_point(aes(col = region)) +
  geom_smooth(method = "lm", formula = 'y~x', se = FALSE) +
  scale_x_log10() + scale_y_log10() +
  labs(title = "GDP per capita vs CO2 per capita",
       x = "GDP per capita",
       y = expression(paste(CO[2], " per capita in tons")))

5.1.4.2 Summary of a linear model

df_co2gdp |> filter(year == 2020) |> drop_na(gdppcap, co2pcap) |>
  lm(log10(co2pcap)~log10(gdppcap), data = _) |> summary()

Call:
lm(formula = log10(co2pcap) ~ log10(gdppcap), data = drop_na(filter(df_co2gdp, 
    year == 2020), gdppcap, co2pcap))

Residuals:
     Min       1Q   Median       3Q      Max 
-0.60778 -0.15660 -0.00651  0.16129  0.59437 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -4.31545    0.13386  -32.24   <2e-16 ***
log10(gdppcap)  1.13831    0.03288   34.62   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.2362 on 228 degrees of freedom
Multiple R-squared:  0.8402,    Adjusted R-squared:  0.8395 
F-statistic:  1199 on 1 and 228 DF,  p-value: < 2.2e-16

5.2 School Enrollment vs GDP Per Capita

5.2.2 Importing Data

df_sec_ter_gdp <- WDI(indicator = c(sec = "SE.SEC.ENRR", ter = "SE.TER.ENRR", 
                                    gdppcap = "NY.GDP.PCAP.PP.KD"), extra = TRUE)
write_csv(df_secgdp, "data/sec_ter_gdp.csv")
df_secgdp <- read_csv("data/sec_ter_gdp.csv")
Rows: 16758 Columns: 14── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, sec, gdppcap, 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.

5.2.3 Visualization by Line Graphs

COUNTRY <- "World"
df_sec_ter_gdp |> filter(country == COUNTRY) |> drop_na(sec, ter) |>
  ggplot() + geom_line(aes(year, sec), col = "blue") + geom_line(aes(year, ter), col = "red") +
  labs(title = "School enrollment; Secondary and Tertiary", 
       subtitle = "secondary in blue and tertiary in red", y = "")

INCOME <- c("Low income", "Low & middle income", "Lower middle income", "Middle income", "Upper middle income", "High income")
df_sec_ter_gdp |> filter(country %in% INCOME) |> drop_na(sec, ter) |>
  ggplot(aes(linetype = factor(country, levels = INCOME))) + geom_line(aes(year, sec), col = "blue") + geom_line(aes(year, ter), col = "red") + ylim(c(0,110)) +
  labs(title = "School enrollment; Secondary and Tertiary", 
       subtitle = "secondary in blue and tertiary in red", linetype = "Income Levels", y = "")

5.2.4 Scatterplot for Covariation

df_sec_ter_gdp |> filter(year == 2020) |> drop_na(sec, ter, gdppcap) |>
  ggplot() + geom_point(aes(gdppcap, sec), col = "blue") + 
  geom_point(aes(gdppcap, ter), col = "red") +
  labs(title = "School enrollment; Secondary and Tertiary vs GDP per capita", 
       subtitle = "secondary in blue and tertiary in red", y = "")

df_sec_ter_gdp |> filter(year == 2020) |> drop_na(sec, ter, gdppcap) |>
  ggplot() + geom_point(aes(gdppcap, sec), col = "blue") + 
  geom_point(aes(gdppcap, ter), col = "red") + 
  scale_x_log10() +
  labs(title = "School enrollment; Secondary and Tertiary vs GDP per capita in log10 scale", 
       subtitle = "secondary in blue and tertiary in red", y = "")

df_sec_ter_gdp |> filter(year == 2020) |> drop_na(sec, ter, gdppcap) |>
  ggplot() + geom_point(aes(gdppcap, sec), col = "blue") + 
  geom_point(aes(gdppcap, ter), col = "red") +
  geom_smooth(aes(gdppcap, sec), col = "blue", method = "lm", formula = 'y~x', se = FALSE) +
  geom_smooth(aes(gdppcap, ter), col = "red", method = "lm", formula = 'y~x', se = FALSE) +
  scale_x_log10() +
  labs(title = "School enrollment; Secondary and Tertiary vs GDP per capita in log10 scale", 
       subtitle = "secondary in blue and tertiary in red with regression lines", y = "")

df_sec_ter_gdp |> filter(year == 2020) |> drop_na(gdppcap, sec) |>
  lm(sec~log10(gdppcap), data = _) |> summary()

Call:
lm(formula = sec ~ log10(gdppcap), data = drop_na(filter(df_sec_ter_gdp, 
    year == 2020), gdppcap, sec))

Residuals:
    Min      1Q  Median      3Q     Max 
-53.777 -10.846  -1.173   9.006  66.996 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -102.994     11.933  -8.631 6.38e-15 ***
log10(gdppcap)   46.088      2.841  16.222  < 2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 15.64 on 157 degrees of freedom
Multiple R-squared:  0.6263,    Adjusted R-squared:  0.624 
F-statistic: 263.2 on 1 and 157 DF,  p-value: < 2.2e-16
df_sec_ter_gdp |> filter(year == 2020) |> drop_na(gdppcap, ter) |>
  lm(ter~log10(gdppcap), data = _) |> summary()

Call:
lm(formula = ter ~ log10(gdppcap), data = drop_na(filter(df_sec_ter_gdp, 
    year == 2020), gdppcap, ter))

Residuals:
    Min      1Q  Median      3Q     Max 
-72.696  -8.388  -0.808   8.589  89.657 

Coefficients:
               Estimate Std. Error t value Pr(>|t|)    
(Intercept)    -159.817     13.877  -11.52   <2e-16 ***
log10(gdppcap)   49.861      3.303   15.09   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 19.18 on 157 degrees of freedom
Multiple R-squared:  0.592, Adjusted R-squared:  0.5894 
F-statistic: 227.8 on 1 and 157 DF,  p-value: < 2.2e-16
df_sec_ter_gdp |> filter(year == 2020, region != "Aggregates") |> drop_na(sec, region) |>
  ggplot(aes(sec, region, fill = region)) + geom_boxplot() + 
  labs(x = "School enrollment, secondary (% gross)", y = "") + theme(legend.position = "none")

df_sec_ter_gdp |> filter(year == 2020, income !="Aggregates") |> drop_na(sec, income) |>
  ggplot(aes(sec, factor(income, levels = INCOME), fill = income)) + geom_boxplot() + 
  labs(title = "Seconary education: School enrollment by income level", x = "School enrollment, secondary (% gross)", y = "") + theme(legend.position = "none")

df_sec_ter_gdp |> filter(year == 2020, region != "Aggregates") |> drop_na(ter, region) |>
  ggplot(aes(ter, region, fill = region)) + geom_boxplot() + 
  labs(x = "School enrollment, tertiary (% gross)", y = "") + theme(legend.position = "none")

df_sec_ter_gdp |> filter(year == 2020, income != "Aggregates") |> drop_na(ter, income) |>
  ggplot(aes(ter, factor(income, levels = INCOME), fill = income)) + geom_boxplot() + 
  labs(title = "Tertiary education: School enrollment by income level", x = "School enrollment, tertiary (% gross)", y = "") + theme(legend.position = "none")

Observations

  • Income level has more effect on school enrollment to tertiary education

6 Your Project

6.1 Title of your project

We study …..

  1. Name of the indicator 1: Indicator Code 1
  • Description:
  1. Name of the indicator 2: Indicator Code 2
  • Description:

6.1.1 Importing Data

Edit the following code chunk!
chosen_indicator_1 <- "SE.SEC.ENRR"
short_name_1 <- "sec"
chosen_indicator_2 <- "NY.GDP.PCAP.PP.KD"
short_name_2 <- "gdppcap"
df_yourdata <- WDI(indicator = c(short_name_1 = chosen_indicator_1, short_name_2 = chosen_indicator_2),
                 extra = TRUE)
write_csv(df_yourdata, "data/yourdata.csv")
df_yourdata <- read_csv("data/yourdata.csv")
Rows: 16758 Columns: 14── Column specification ──────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (7): country, iso2c, iso3c, region, capital, income, lending
dbl  (5): year, short_name_1, short_name_2, 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.

6.1.2 Visualization by Line Graphs

6.1.2.1 Name of the Indicator 1

Edit the title and the label of y-axis.
COUNTRY <- "World"
df_yourdata |> filter(country == COUNTRY) |> drop_na(short_name_1) |>
  ggplot(aes(year, short_name_1)) + geom_line() +
  labs(title = "",
       y = "")

Observations and Questions:

Edit ISO2C, title, subtitle, and the label of y-axis.
ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_yourdata |> filter(iso2c %in% ISO2C) |> drop_na(short_name_1) |>
  ggplot(aes(year, short_name_1, col = iso2c)) + geom_line() +
  labs(title = "",
       subtitle = "China, Germany, France, United Kingdom, India, Japan, United States", 
       y = "")

Observations and Questions:

6.1.2.2 Name of the Indicator 2

Edit COUNTRY and the title.
COUNTRY <- "World"
df_yourdata |> filter(country == COUNTRY) |> drop_na(short_name_2) |>
  ggplot(aes(year, short_name_2)) + geom_line() +
  labs(title = "")

Observations and Questions:

Edit ISO2C, title, subtitle, and the label of y-axis, and add caption if preferable.
ISO2C <- c("JP", "CN", "ID", "GB", "US", "DE", "FR")
df_yourdata |> filter(iso2c %in% ISO2C) |> drop_na(short_name_2) |>
  ggplot(aes(year, short_name_2, col = iso2c)) + geom_line() +
  labs(title = "",
       subtitle = "China, Germany, France, United Kingdom, India, Japan, United States", 
       y = "",
       caption = "")

Observations and Questions:

6.1.2.3 Ranking of the indicator 1

Edit year if necessary.
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(short_name_1) |> arrange(desc(short_name_1))
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(short_name_1) |> arrange(short_name_1)

Observations and Questions:

6.1.2.4 Ranking of the Indicator 2

df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(short_name_2) |> arrange(desc(short_name_2))

Observations and Questions:

df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |>
  drop_na(short_name_2) |> arrange(short_name_2)

Observations and Questions:

6.1.3 Histograms and Boxplots for Variation

6.1.3.1 Name of the Indicator 1

Edit the title and year.
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(short_name_1) |> filter(income != "Not classified") |>
  ggplot(aes(short_name_1, fill = factor(income, levels = INCOME))) + geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() +
  labs(title = "", fill = "")

Observations and Questions:

Edit the title and the years.
df_yourdata |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(short_name_1) |> filter(short_name_1 > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(short_name_1, fill = factor(year))) + 
  geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() + facet_wrap(~year) +
  labs(title = "", fill = "")

Observations and Questions:

df_yourdata |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(short_name_1) |> filter(short_name_1 > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(short_name_1, factor(year), fill = factor(year))) + 
  geom_boxplot() + scale_x_log10() + labs(y = "") + theme(legend.position = "none")

Observations and Questions:

Edit the title, and the year if necessary.
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(short_name_1) |> filter(short_name_1 > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(short_name_1, factor(income, levels = INCOME), fill = income)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "", y = "", fill = "") +
  theme(legend.position = "none")

Observations and Questions:

Edit the title and year if necessary.
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(short_name_1) |> filter(short_name_1 > 0) |> 
  ggplot(aes(short_name_1, region, fill = region)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "", y = "", fill = "") +
  theme(legend.position = "none")

Observations and Questions:

6.1.3.2 GDP per capita

Edit the title, and year if necessary.
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(short_name_2) |> filter(income != "Not classified") |>
  ggplot(aes(short_name_2, fill = factor(income, levels = INCOME))) + geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() +
  labs(title = "", fill = "")

Edit the title and the year if necessary.
df_yourdata |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(short_name_2) |> filter(short_name_2 > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(short_name_2, fill = factor(year))) + 
  geom_histogram(bins = 15, col = "black", linewidth = 0.1) + 
  scale_x_log10() + facet_wrap(~year) +
  labs(title = "", fill = "") +
  theme(legend.position = "none")

Observations and Questions:

df_yourdata |> filter(year %in% c(1990, 2000, 2010, 2020)) |> filter(region != "Aggregates") |> 
  drop_na(short_name_2) |> filter(short_name_2 > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(short_name_2, factor(year), fill = factor(year))) + 
  geom_boxplot() + scale_x_log10() + labs(y = "") + theme(legend.position = "none")

Observations and Questions:

Edit the title, and the year if necessary.
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(short_name_2) |> filter(short_name_2 > 0) |> filter(income != "Not classified") |> 
  ggplot(aes(short_name_2, factor(income, levels = INCOME), fill = income)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "", y = "", fill = "") +
  theme(legend.position = "none")

Observations and Questions:

Edit the title and the year if necessary.
df_yourdata |> filter(year == 2020) |> filter(region != "Aggregates") |> 
  drop_na(short_name_2) |> filter(short_name_2 > 0) |> 
  ggplot(aes(short_name_2, region, fill = region)) + 
  geom_boxplot() + scale_x_log10() + 
  labs(title = "", y = "", fill = "") +
  theme(legend.position = "none")

6.1.4 Scatterplot for Covariation

6.1.4.1 Scatterplot with a regression line

Edit the title, the labels of x- and y- axes.
df_yourdata |> filter(year == 2020) |> 
  drop_na(short_name_2, short_name_1) |>
  ggplot(aes(short_name_2, short_name_1)) + geom_point(aes(col = region)) +
  geom_smooth(method = "lm", formula = 'y~x', se = FALSE) +
  scale_x_log10() + scale_y_log10() +
  labs(title = "",
       x = "",
       y = "")

Observations and Questions:

6.1.4.2 Summary of a linear model

Edit year if necessary.
df_yourdata |> filter(year == 2020) |> drop_na(short_name_2, short_name_1) |>
  lm(log10(short_name_1)~log10(short_name_2), data = _) |> summary()

Call:
lm(formula = log10(short_name_1) ~ log10(short_name_2), data = drop_na(filter(df_yourdata, 
    year == 2020), short_name_2, short_name_1))

Residuals:
      Min        1Q    Median        3Q       Max 
-0.289057 -0.056975  0.002394  0.057760  0.260181 

Coefficients:
                    Estimate Std. Error t value Pr(>|t|)    
(Intercept)           0.8248     0.0647   12.75   <2e-16 ***
log10(short_name_2)   0.2648     0.0154   17.19   <2e-16 ***
---
Signif. codes:  0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1

Residual standard error: 0.08479 on 157 degrees of freedom
Multiple R-squared:  0.653, Adjusted R-squared:  0.6508 
F-statistic: 295.5 on 1 and 157 DF,  p-value: < 2.2e-16

Observations and Questions:

LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFdESSIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIGRmX3ByaW50OiBwYWdlZAogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwogIHdvcmRfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgcmVmZXJlbmNlX2RvY3g6IGludHJvMndkaV90bXAuZG9jeAogIHBkZl9kb2N1bWVudDoKICAgIHRvYzogeWVzCi0tLQoKIyBEYXRhc2NpZW5jZSBmb3IgTWFjcm9lY29ub21pY3MgdXNpbmcgV0RJCgojIyBJbnRyb2R1Y3Rpb24KCiMjIyBEYXRhc2NpZW5jZQoKRGF0YXNjaWVuY2XCoGlzIGFuIGludGVyZGlzY2lwbGluYXJ5wqBhY2FkZW1pYyBmaWVsZCB0byBleHRyYWN0IG9yIGV4dHJhcG9sYXRlwqBrbm93bGVkZ2XCoGFuZCBpbnNpZ2h0cyBmcm9tIG5vaXN5LCBzdHJ1Y3R1cmVkLCBhbmTCoHVuc3RydWN0dXJlZCBkYXRhLgoKV2h5IGRhdGFzY2llbmNlPyBUaGVyZSBhcmUgbWFueSByZWFzb25zLCBidXQgSSBiZWxpZXZlIGl0IHdvdWxkIHByb21vdGUgZXZpZGVuY2UgYmFzZWQsIG5vbi1iaWFzZWQgdGhpbmtpbmcuCgojIyMjIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMsIEVEQQoKRURBIGlzIGFuIGl0ZXJhdGl2ZSBjeWNsZSB0aGF0IGhlbHBzIHlvdSB1bmRlcnN0YW5kIHdoYXQgeW91ciBkYXRhIHNheXMuIFdoZW4geW91IGRvIEVEQSwgeW91OgoKMS4gIEdlbmVyYXRlIHF1ZXN0aW9ucyBhYm91dCB5b3VyIGRhdGEKCjIuICBTZWFyY2ggZm9yIGFuc3dlcnMgYnkgdmlzdWFsaXNpbmcsIHRyYW5zZm9ybWluZywgYW5kL29yIG1vZGVsaW5nIHlvdXIgZGF0YQoKMy4gIFVzZSB3aGF0IHlvdSBsZWFybiB0byByZWZpbmUgeW91ciBxdWVzdGlvbnMgYW5kL29yIGdlbmVyYXRlIG5ldyBxdWVzdGlvbnMKCkVEQSBpcyBhbiBpbXBvcnRhbnQgcGFydCBvZiBhbnkgZGF0YSBhbmFseXNpcy4gWW91IGNhbiB1c2UgRURBIHRvIG1ha2UgZGlzY292ZXJpZXMgYWJvdXQgdGhlIHdvcmxkOyBvciB5b3UgY2FuIHVzZSBFREEgdG8gZW5zdXJlIHRoZSBxdWFsaXR5IG9mIHlvdXIgZGF0YSwgYXNraW5nIHF1ZXN0aW9ucyBhYm91dCB3aGV0aGVyIHRoZSBkYXRhIG1lZXRzIHlvdXIgc3RhbmRhcmRzIG9yIG5vdC4gKFBvc2l0IFByaW1lcnM6IFtFREFdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vcHJpbWVycy8zLjEpKQoKIyMjIE1hY3JvZWNvbm9taWNzCgpUaGUgc3R1ZHkgb2YgdGhlIGVudGlyZSBlY29ub215IG9mIGEgcmVnaW9uLCBhIGNvdW50cnksIG9yIHRoZSBlbnRpcmUgd29ybGQKCiMjIyBXb3JsZCBEZXZlbG9wbWVudCBJbmRpY2F0b3JzCgpXb3JsZCBEZXZlbG9wbWVudCBJbmRpY2F0b3JzwqAoV0RJKSBpcyB0aGXCoFdvcmxkIEJhbmsncyBwcmVtaWVyIGNvbXBpbGF0aW9uIG9mIGludGVybmF0aW9uYWwgc3RhdGlzdGljcyBvbsKgZ2xvYmFsIGRldmVsb3BtZW50LiBEcmF3aW5nIGZyb20gb2ZmaWNpYWxseSByZWNvZ25pemVkIHNvdXJjZXMgYW5kIGluY2x1ZGluZyBuYXRpb25hbCwgcmVnaW9uYWwsIGFuZCBnbG9iYWwgZXN0aW1hdGVzLCB0aGUgV0RJIHByb3ZpZGVzIGFjY2VzcyB0byBhcHByb3hpbWF0ZWx5IDEsNjAwIGluZGljYXRvcnMgZm9yIDIxNyBlY29ub21pZXMsIHdpdGggc29tZSB0aW1lIHNlcmllcyBleHRlbmRpbmcgYmFjayBtb3JlIHRoYW4gNTAgeWVhcnMuIFRoZSBkYXRhYmFzZSBoZWxwcyB1c2VycyBmaW5kIGluZm9ybWF0aW9uIHJlbGF0ZWQgdG8gZGV2ZWxvcG1lbnQsIGJvdGggY3VycmVudCBhbmQgaGlzdG9yaWNhbC4gVGhlIHRvcGljcyBjb3ZlcmVkIGluIHRoZSBXREkgcmFuZ2UgZnJvbSBwb3ZlcnR5LCBoZWFsdGgsIGFuZCBkZW1vZ3JhcGhpY3MgdG8gR0RQLCB0cmFkZSwgYW5kIHRoZSBlbnZpcm9ubWVudC4KClRoZSBXb3JsZCBEZXZlbG9wbWVudCBJbmRpY2F0b3JzIHdlYnNpdGUgcHJvdmlkZXMgYWNjZXNzIHRvIGRhdGEgYXMgd2VsbCBhcyBpbmZvcm1hdGlvbiBhYm91dCBkYXRhIGNvdmVyYWdlLCBjdXJhdGlvbiwgYW5kIG1ldGhvZG9sb2dpZXMsIGFuZCBhbGxvd3MgdXNlcnMgdG8gZGlzY292ZXIgd2hhdCB0eXBlIG9mIGluZGljYXRvcnMgYXJlIGF2YWlsYWJsZSwgaG93IHRoZXkgYXJlIGNvbGxlY3RlZCwgYW5kIGhvdyB0aGV5IGNhbiBiZSB2aXN1YWxpemVkIHRvIGFuYWx5emUgZGV2ZWxvcG1lbnQgdHJlbmRzLiAoV2lraXBlZGlhOiBbTGlua10oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvV29ybGRfRGV2ZWxvcG1lbnRfSW5kaWNhdG9ycykpCgojIyBPcGVuIERhdGEgYW5kIFB1YmxpYyBEYXRhCgpEYXRhc2NpZW5jZSBhbmQgQXJ0aWZpY2lhbCBJbnRlbGxpZ2VuY2UKCi0gICBDb21wdXRlciBSZXNvdXJjZXMKCi0gICBEYXRhCgogICAgLSAgIEJpZyBEYXRhCgogICAgLSAgIE9wZW4gYW5kIFB1YmxpYyBEYXRhCgo+ICoqUS4gRXhwbGFpbiAnT3BlbiBEYXRhJyBhbmQgJ1B1YmxpYyBEYXRhJywgYW5kIHRoZWlyIGRpZmZlcmVuY2VzLioqCj4KPiAqKkEuIEFJIFBvZSBBc3Npc3RhbnQqKi4gKFtQb2UuY29tXShodHRwczovL3BvZS5jb20pKQo+Cj4gIk9wZW4gZGF0YSIgYW5kICJwdWJsaWMgZGF0YSIgYXJlIHJlbGF0ZWQgY29uY2VwdHMgdGhhdCByZWZlciB0byBkaWZmZXJlbnQgYXNwZWN0cyBvZiBkYXRhIGFjY2Vzc2liaWxpdHkgYW5kIHVzYWdlLgo+Cj4gT3BlbiBEYXRhOiBPcGVuIGRhdGEgcmVmZXJzIHRvIHRoZSBpZGVhIHRoYXQgY2VydGFpbiBkYXRhIHNob3VsZCBiZSBmcmVlbHkgYXZhaWxhYmxlIHRvIHRoZSBwdWJsaWMsIHdpdGhvdXQgcmVzdHJpY3Rpb25zIG9uIGFjY2VzcywgdXNhZ2UsIG9yIHJlZGlzdHJpYnV0aW9uLiBPcGVuIGRhdGEgaXMgdHlwaWNhbGx5IHJlbGVhc2VkIGluIGEgbWFjaGluZS1yZWFkYWJsZSBmb3JtYXQsIHN1Y2ggYXMgQ1NWLCBKU09OLCBvciBYTUwsIGFuZCBpcyBhY2NvbXBhbmllZCBieSBhbiBvcGVuIGxpY2Vuc2UgdGhhdCBhbGxvd3MgYW55b25lIHRvIHVzZSwgcmV1c2UsIGFuZCBkaXN0cmlidXRlIHRoZSBkYXRhIGZvciBhbnkgcHVycG9zZS4gVGhlIGdvYWwgb2Ygb3BlbiBkYXRhIGlzIHRvIHByb21vdGUgdHJhbnNwYXJlbmN5LCBjb2xsYWJvcmF0aW9uLCBhbmQgaW5ub3ZhdGlvbiBieSBlbmFibGluZyBpbmRpdmlkdWFscywgb3JnYW5pemF0aW9ucywgYW5kIGNvbW11bml0aWVzIHRvIGFuYWx5emUgYW5kIGJ1aWxkIHVwb24gdGhlIGRhdGEuCj4KPiBPcGVuIGRhdGEgb2Z0ZW4gaW5jbHVkZXMgZ292ZXJubWVudCBkYXRhLCBzY2llbnRpZmljIHJlc2VhcmNoIGRhdGEsIHB1YmxpYyByZWNvcmRzLCBhbmQgb3RoZXIgZGF0YXNldHMgdGhhdCBhcmUgZGVlbWVkIHRvIGJlIG9mIHB1YmxpYyBpbnRlcmVzdC4gR292ZXJubWVudHMsIGludGVybmF0aW9uYWwgb3JnYW5pemF0aW9ucywgYW5kIHZhcmlvdXMgaW5zdGl0dXRpb25zIGFjdGl2ZWx5IHByb21vdGUgdGhlIHJlbGVhc2Ugb2Ygb3BlbiBkYXRhIHRvIGZvc3RlciBhY2NvdW50YWJpbGl0eSwgZHJpdmUgZWNvbm9taWMgZ3Jvd3RoLCBhbmQgZW1wb3dlciBjaXRpemVucyB0byBtYWtlIGluZm9ybWVkIGRlY2lzaW9ucy4KPgo+IFB1YmxpYyBEYXRhOiBQdWJsaWMgZGF0YSwgb24gdGhlIG90aGVyIGhhbmQsIHJlZmVycyB0byBhbnkgZGF0YSB0aGF0IGlzIGFjY2Vzc2libGUgb3IgYXZhaWxhYmxlIHRvIHRoZSBwdWJsaWMuIEl0IGlzIGEgYnJvYWRlciB0ZXJtIHRoYXQgZW5jb21wYXNzZXMgYm90aCBvcGVuIGRhdGEgYW5kIG90aGVyIHR5cGVzIG9mIGRhdGEgdGhhdCBtYXkgaGF2ZSBjZXJ0YWluIHJlc3RyaWN0aW9ucyBvbiBhY2Nlc3Mgb3IgdXNhZ2UuIFB1YmxpYyBkYXRhIGNhbiBpbmNsdWRlIGluZm9ybWF0aW9uIHRoYXQgaXMgZnJlZWx5IGFuZCBvcGVubHkgYXZhaWxhYmxlLCBhcyB3ZWxsIGFzIGRhdGEgdGhhdCBpcyBhY2Nlc3NpYmxlIHRocm91Z2ggc3BlY2lmaWMgY2hhbm5lbHMsIHN1Y2ggYXMgcHVibGljIGxpYnJhcmllcywgZ292ZXJubWVudCBhZ2VuY2llcywgb3Igc3Vic2NyaXB0aW9uLWJhc2VkIHNlcnZpY2VzLgo+Cj4gUHVibGljIGRhdGEgY2FuIGluY2x1ZGUgYSB3aWRlIHJhbmdlIG9mIGluZm9ybWF0aW9uLCBzdWNoIGFzIGdvdmVybm1lbnQgcmVwb3J0cywgY2Vuc3VzIGRhdGEsIGxlZ2FsIGRvY3VtZW50cywgZmluYW5jaWFsIHN0YXRlbWVudHMsIGFuZCBtb3JlLiBXaGlsZSBwdWJsaWMgZGF0YSBtYXkgYmUgYWNjZXNzaWJsZSB0byB0aGUgcHVibGljLCBpdCBkb2VzIG5vdCBuZWNlc3NhcmlseSBndWFyYW50ZWUgdGhlIHNhbWUgbGV2ZWwgb2Ygb3Blbm5lc3MsIHVzYWJpbGl0eSwgb3IgZnJlZWRvbSBhcyBvcGVuIGRhdGEuIFB1YmxpYyBkYXRhIG1heSBoYXZlIGxpbWl0YXRpb25zIG9uIGl0cyB1c2FnZSwgY29weXJpZ2h0IHJlc3RyaWN0aW9ucywgb3IgY29uZGl0aW9ucyBpbXBvc2VkIGJ5IHRoZSBkYXRhIHByb3ZpZGVyLgo+Cj4gSW4gc3VtbWFyeSwgb3BlbiBkYXRhIHNwZWNpZmljYWxseSByZWZlcnMgdG8gZGF0YSB0aGF0IGlzIGZyZWVseSBhY2Nlc3NpYmxlLCB1c2FibGUsIGFuZCByZWRpc3RyaWJ1dGFibGUgd2l0aG91dCByZXN0cmljdGlvbnMsIG9mdGVuIGFjY29tcGFuaWVkIGJ5IGFuIG9wZW4gbGljZW5zZS4gUHVibGljIGRhdGEsIG9uIHRoZSBvdGhlciBoYW5kLCBpcyBhIGJyb2FkZXIgdGVybSB0aGF0IGluY2x1ZGVzIGJvdGggb3BlbiBkYXRhIGFuZCBvdGhlciBkYXRhIHRoYXQgbWF5IGhhdmUgdmFyeWluZyBkZWdyZWVzIG9mIGFjY2Vzc2liaWxpdHkgYW5kIHVzYWdlIHJlc3RyaWN0aW9ucy4KCiMjIyMgV29ybGQgQmFuayBvbiBPcGVuIERhdGEgZm9yIE9wZW4gR292ZXJubWVudAoKVmlzaXQgW09wZW4gR292ZXJubWVudCBEYXRhIFRvb2xraXRdKGh0dHBzOi8vb3BlbmRhdGF0b29sa2l0LndvcmxkYmFuay5vcmcvZW4vZGF0YS9vcGVuZGF0YXRvb2xraXQvaG9tZSkuCgojIyBSIG9uIFJTdHVkaW8KCiMjIyBJbnN0YWxsIFIgYW5kIFJTdHVkaW8KCi0gICBSOiA8aHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZz4KLSAgIFJTdHVkaW86IDxodHRwczovL3Bvc2l0LmNvPgoKIyMjIFBvc2l0IENsb3VkCgotICAgUG9zaXQuY2xvdWQ6IDxodHRwczovL3Bvc2l0LmNsb3VkLz4KCiAgICAtICAgW1Bvc2l0IFByaW1lcnNdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vcHJpbWVycyk6IGludGVyYWN0aXZlIHR1dG9yaWFscwoKICAgIC0gICBbQ2hlYXRzaGVldHNdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vY2hlYXQtc2hlZXRzKTogUlN0dWRpbyBJREUsIERhdGEgVmlzdWFsaXphdGlvbiwgRGF0YSBUcmFuc2Zvcm1hdGlvbiwgRGF0YSBJbXBvcnQsIFJNYXJrZG93biwgZXRjLgoKLSAgIFNoYXJlZCBQcm9qZWN0OiA8aHR0cHM6Ly9wb3NpdC5jbG91ZC9jb250ZW50LzU1Mzk3NjM+CgotICAgQ3JlYXRlIFlvdXIgQ29weSBhbmQgRG93bmxvYWQgaXQuCgojIyBEYXRhc2NpZW5jZSBXb3JrZmxvdwoKIVsqVGhlIGltYWdlIGFib3ZlIGlzIGZyb20gW1I0RFMoMmUpXShodHRwczovL3I0ZHMuaGFkbGV5Lm56KSBieSBIYWRsZXkgV2lja2hhbSwgTWluZSDDh2V0aW5rYXlhLVJ1bmRlbCBhbmQgR2FycmV0dCBHcm9sZW11bmQqXShkYXRhL2RhdGEtc2NpZW5jZS5wbmcpCgojIyBGaXJzdCBEYXRhc2NpZW5jZQoKV2UgdXNlIHR3byBwYWNrYWdlcywgYHRpZHl2ZXJzZWAgYW5kIGBXRElgLgoKUiBwYWNrYWdlcyBhcmUgZXh0ZW5zaW9ucyB0byB0aGUgUiBzdGF0aXN0aWNhbCBwcm9ncmFtbWluZyBsYW5ndWFnZS4gUiBwYWNrYWdlcyBjb250YWluIGNvZGUsIGRhdGEsIGFuZCBkb2N1bWVudGF0aW9uIGluIGEgc3RhbmRhcmRpemVkIGNvbGxlY3Rpb24gZm9ybWF0IHRoYXQgY2FuIGJlIGluc3RhbGxlZCBieSB1c2VycyBvZiBSLCB0eXBpY2FsbHkgdmlhIGEgY2VudHJhbGl6ZWQgc29mdHdhcmUgcmVwb3NpdG9yeSBzdWNoIGFzIENSQU4gKHRoZSBDb21wcmVoZW5zaXZlIFIgQXJjaGl2ZSBOZXR3b3JrKS4gW1tDUkFOIExpbmtdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnKV0KCi0gICAqKnRpZHl2ZXJzZSoqOiBUaGXCoHRpZHl2ZXJzZcKgaXMgYSBjb2xsZWN0aW9uIG9mwqBvcGVuIHNvdXJjZcKgcGFja2FnZXMgZm9yIHRoZSBSIHByb2dyYW1taW5nIGxhbmd1YWdlwqBpbnRyb2R1Y2VkIGJ5wqBIYWRsZXkgV2lja2hhbSBhbmQgaGlzIHRlYW0gdGhhdCAic2hhcmUgYW4gdW5kZXJseWluZyBkZXNpZ24gcGhpbG9zb3BoeSwgZ3JhbW1hciwgYW5kIGRhdGEgc3RydWN0dXJlcyIgb2bCoHRpZHkgZGF0YS4gQ2hhcmFjdGVyaXN0aWMgZmVhdHVyZXMgb2YgdGlkeXZlcnNlIHBhY2thZ2VzIGluY2x1ZGUgZXh0ZW5zaXZlIHVzZSBvZiBub24tc3RhbmRhcmQgZXZhbHVhdGlvbiBhbmQgZW5jb3VyYWdpbmcgcGlwaW5nLiBbW0NSQU4gTGlua10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL3RpZHl2ZXJzZS9pbmRleC5odG1sKV0KCi0gICAqKldESSoqOiBTZWFyY2ggYW5kIGRvd25sb2FkIGRhdGEgZnJvbSBvdmVyIDQwIGRhdGFiYXNlcyBob3N0ZWQgYnkgdGhlIFdvcmxkIEJhbmssIGluY2x1ZGluZyB0aGUgV29ybGQgRGV2ZWxvcG1lbnQgSW5kaWNhdG9ycyAoJ1dESScpLCBJbnRlcm5hdGlvbmFsIERlYnQgU3RhdGlzdGljcywgRG9pbmcgQnVzaW5lc3MsIEh1bWFuIENhcGl0YWwgSW5kZXgsIGFuZCBTdWItbmF0aW9uYWwgUG92ZXJ0eSBpbmRpY2F0b3JzLiBbW0NSQU4gTGlua10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL1dESS9pbmRleC5odG1sKV0KCiMjIyBTZXR1cAoKU3RlcCAxLiBJbnN0YWxsIHBhY2thZ2VzIGlmIG5lY2Vzc2FyeS4KCmBgYHtyIGV2YWwgPSBGQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIikKaW5zdGFsbC5wYWNrYWdlcygiV0RJIikKYGBgCgpTdGVwIDIuIExvYWQgcGFja2FnZXMuCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoV0RJKQpgYGAKClN0ZXAgMy4gQ3JlYXRlIGEgZGF0YSBkaXJlY3RvcnkgZm9yIHRoZSBmaXJzdCB0aW1lLgoKYGBge3IgY3JlYXRlLWRpcnN9CmRpci5jcmVhdGUoImRhdGEiKQpgYGAKClN0ZXAgNC4gU2V0ICdTeXN0ZW0gTGFuZ3VhZ2UnIHRvIGJlIEVuZ2xpc2gsIHJlY29tbWVuZGVkLgoKYGBge3IgZXZhbCA9IEZBTFNFfQpTeXMuc2V0ZW52KExBTkcgPSAiZW4iKQpgYGAKCiMjIyBJbXBvcnRpbmcgR0RQIERhdGEKClRoZSBmb2xsb3dpbmcgY29kZSBjaHVuayBpcyB0byBkb3dubG9hZCBHRFAgZGF0YSB3aXRoIHRoZSBmb2xsb3dpbmcgaW5kaWNhdG9yIGNvZGUuCgpXREkgaW5kaWNhdG9yOiBOWS5HRFAuTUtUUC5QUC5LRAoKYGBge3IgY2FjaGUgPSBUUlVFfQpkZl9nZHAgPC0gV0RJKGluZGljYXRvciA9ICJOWS5HRFAuTUtUUC5QUC5LRCIpCmBgYAoKTi5CLiBUaGVyZSBhcmUgbWFueSBHRFAgcmVsYXRlZCBkYXRhIGluIFdESSwgZm9yIGV4YW1wbGUsICJOWS5HRFAuTUtUUC5DRCIKClRvIGF2b2lkIHRoZSBpbnRlcm5ldCB0cmFmZmljLCBzYXZlIHRoZSBkYXRhIGFuZCByZXVzZSBpdC4KCkNTVjogY29tbWEgc2VwYXJhdGVkIHZhbHVlcywgYSB0ZXh0IGZvcm1hdCBvZiBhIGRhdGEuCgpgYGB7cn0Kd3JpdGVfY3N2KGRmX2dkcCwgImRhdGEvZ2RwLmNzdiIpCmBgYAoKUnVuIGNvZGVzIGFib3ZlIG9ubHkgb25jZSB0byBkb3dubG9hZCBhbmQgd3JpdGUgdGhlIGRhdGEgaW50byB0aGUgZGF0YSBkaXJlY3RvcnkuCgpgYGB7cn0KZGZfZ2RwIDwtIHJlYWRfY3N2KCJkYXRhL2dkcC5jc3YiKQpgYGAKCiMjIyBWaWV3aW5nIERhdGEKCkZpcnN0LCBsb29rIGF0IHRoZSBkYXRhIGJ5IGBoZWFkYCwgYHN0cmAgdWN0dXJlLCBhbmQgYHN1bW1hcnlgLgoKYGhlYWRgOiBkaXNwbGF5IHRoZSBmaXJzdCA2IHJvd3MgYnkgZGVmYXVsdAoKYGBge3J9CmhlYWQoZGZfZ2RwKQpgYGAKCmBgYHtyfQpoZWFkKGRmX2dkcCwgNTApCmBgYAoKMi41NjE4MDBlKzEyIGlzIGluIHNjaWVudGlmaWMgbm90YXRpb24sIGkuZS4sICQyLjU2MTgwMCBcdGltZXMxMF57MTJ9ID0gMiw1NjIsODAwLDAwMCwwMDAkLgoKYHN0cmA6IGRpc3BsYXkgdGhlIHN0cnVjdHVyZSBvZiBhbiBvYmplY3QKCmBgYHtyfQpzdHIoZGZfZ2RwKQpgYGAKCmBzdW1tYXJ5YDogZGlzcGxheSB0aGUgc3VtbWFyeSBvZiBhbiBvYmplY3QKCmBgYHtyfQpzdW1tYXJ5KGRmX2dkcCkKYGBgCgpJbiBSTm90ZWJvb2ssIHRoZSBmb2xsb3dpbmcgYWxzbyBkaXNwbGF5cyB0aGUgZmlyc3QgMTAwMCByb3dzIG9mIHRoZSBkYXRhIGluIHRoZSBwYWdlZCBmb3JtYXQuCgpgYGB7cn0KZGZfZ2RwCmBgYAoKIyMjIFRyYW5zZm9ybWF0aW9uCgpgfD5gICwgb3IgYCU+JWAsIGlzIGNhbGxlZCBhIHBpcGUgb3BlcmF0b3IgYW5kIGBkZl9nZHAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSlgIGlzIHNhbWUgYXMKCmBmaWx0ZXIoZGZfZ2RwLCBjb3VudHJ5ID09IENPVU5UUlkpYCAuCgpgZmlsdGVyYCA6IEtlZXAgcm93cyB0aGF0IG1hdGNoIGEgY29uZGl0aW9uCgpgYGB7cn0KQ09VTlRSWSA8LSAiSmFwYW4iCmRmX2dkcCB8PiBmaWx0ZXIoY291bnRyeSA9PSBDT1VOVFJZKQpgYGAKCiMjIyBWaXN1YWxpemF0aW9uCgojIyMjIEdEUCBvZiBKYXBhbgoKYGdncGxvdCgpICsgZ2VvbV9saW5lKClgOiBBIHRpZHl2ZXJzZSBmdW5jdGlvbiBvZiBkcmF3IGEgbGluZSBncmFwaAoKYGFlcyh5ZWFyLCBOWS5HRFAuTUtUUC5QUC5LRClgIDogYWVzdGhldGljIG1hcHBpbmcgc2VuZGluZyB5ZWFyIHRvIHgtYXhpcyBhbmQgYE5ZLkdEUC5NS1RQLlBQLktEYCB0byB5LWF4aXMKCmBgYHtyfQpDT1VOVFJZIDwtICJKYXBhbiIKZGZfZ2RwIHw+IGZpbHRlcihjb3VudHJ5ID09IENPVU5UUlkpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBOWS5HRFAuTUtUUC5QUC5LRCkpICsgZ2VvbV9saW5lKCkKYGBgCgpMZXQncyBkZWxldGUgdGhlIHJvd3Mgd2l0aCBtaXNzaW5nIHZhbHVlcyB1c2luZyBgZHJvcF9uYShOWS5HRFAuTUtUUC5QUC5LRClgLCBhIHRyYW5zZm9ybWF0aW9uLgoKYGBge3J9CkNPVU5UUlkgPC0gIkphcGFuIgpkZl9nZHAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSkgfD4gZHJvcF9uYShOWS5HRFAuTUtUUC5QUC5LRCkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIE5ZLkdEUC5NS1RQLlBQLktEKSkgKyBnZW9tX2xpbmUoKQpgYGAKCiMjIyMgR0RQIG9mIHRoZSBXb3JsZAoKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl9nZHAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIE5ZLkdEUC5NS1RQLlBQLktEKSkgKyBnZW9tX2xpbmUoKQpgYGAKCiMjIyMgRXhlcmNpc2UuCgoxLiAgRGVsZXRlIHRoZSBtaXNzaW5nIGRhdGEgb2YgdGhlIEdEUCBvZiB0aGUgV29ybGQgYnkgbWltaWNraW5nIHRoZSBvbmUgb2YgSmFwYW4uCjIuICBXcml0ZSBkb3duIHlvdXIgb2JzZXJ2YXRpb25zIGFuZCBxdWVzdGlvbnMuCgpgYGB7ciBldmFsID0gRkFMU0V9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl9nZHAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSkgfD4gZHJvcF9uYShOWS5HRFAuTUtUUC5QUC5LRCkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIE5ZLkdEUC5NS1RQLlBQLktEKSkgKyBnZW9tX2xpbmUoKQpgYGAKCioqT2JzZXJ2YXRpb25zIGFuZCBRdWVzdGlvbnMqKgoKLSAgIGUuZy4gVGhlIEdEUCBvZiB0aGUgd29ybGQgaXMgY29udGludW91c2x5IGluY3JlYXNpbmcgc2luY2UgMTk5MC4KCi0gICBUaGVyZSBkcm9wcyBpbiB0aGUgeWVhciBhcm91bmQgMjAwOCBhbmQgMjAyMAoKIyMjIyBHRFAgb2YgdGhlIGNvdW50cmllcwoKQnkgY291bnRyeSBuYW1lcwoKYGBge3J9CkNPVU5UUklFUyA8LSBjKCJKYXBhbiIsICJDaGluYSIsICJJbmRpYSIsICJVbml0ZWQgS2luZ2RvbSIsICJVbml0ZWQgU3RhdGVzIiwgIkdlcm1hbnkiLCAiRnJhbmNlIikKZGZfZ2RwIHw+IGZpbHRlcihjb3VudHJ5ICVpbiUgQ09VTlRSSUVTKSB8PiBkcm9wX25hKE5ZLkdEUC5NS1RQLlBQLktEKSB8PgogIGdncGxvdChhZXMoeWVhciwgTlkuR0RQLk1LVFAuUFAuS0QsIGNvbG9yID0gY291bnRyeSkpICsgZ2VvbV9saW5lKCkKYGBgCgpgYGB7cn0KSVNPMkMgPC0gYygiSlAiLCAiQ04iLCAiSUQiLCAiR0IiLCAiVVMiLCAiREUiLCAiRlIiKQpkZl9nZHAgfD4gZmlsdGVyKGlzbzJjICVpbiUgSVNPMkMpIHw+IGRyb3BfbmEoTlkuR0RQLk1LVFAuUFAuS0QpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBOWS5HRFAuTUtUUC5QUC5LRCwgY29sID0gaXNvMmMpKSArIGdlb21fbGluZSgpCmBgYAoKIyMjIyBFeGVyY2lzZS4KCldoYXQgaGFwcGVucyBpZiB5b3UgcmVwbGFjZSBgY29sb3IgPSBpc28yY2AgYXQgdGhlIGJvdHRvbSBvZiB0aGUgY29kZSBhYm92ZSB3aXRoIGBjb2xvdXIgPSBpc28yY2AgLCBgY29sb3IgPSBjb3VudHJ5YCAsIGBjb2wgPSBjb3VudHJ5YCA/CgpgYGB7ciBldmFsID0gRkFMU0V9CklTTzJDIDwtIGMoIkpQIiwgIkNOIiwgIklEIiwgIkdCIiwgIlVTIiwgIkRFIiwgIkZSIikKZGZfZ2RwIHw+IGZpbHRlcihpc28yYyAlaW4lIElTTzJDKSB8PiBkcm9wX25hKE5ZLkdEUC5NS1RQLlBQLktEKSB8PgogIGdncGxvdChhZXMoeWVhciwgTlkuR0RQLk1LVFAuUFAuS0QsIGNvbG9yID0gY291bnRyeSkpICsgZ2VvbV9saW5lKCkKYGBgCgojIyMjIExpc3Qgb2YgQ291bnRyaWVzIGFuZCBpc28yYwoKYGBge3J9CihkZl9jb2RlcyA8LSBkZl9nZHAgfD4gZGlzdGluY3QoY291bnRyeSwgaXNvMmMpKQpgYGAKCiMjIyMgKipFeGVyY2lzZS4qKgoKU2V0IENPVU5UUklFUyBhbmQvb3IgSVNPMkMgdG8gZHJhdyBsaW5lIGdyYXBocyBvZiBHRFAuCgpgYGB7ciBldmFsID0gRkFMU0V9CkNPVU5UUklFUyA8LSBjKCkgIyBzdXJyb3VuZCB0aGUgY291bnRyeSBuYW1lIHdpdGggcXVvdGF0aW9uIG1hcmtzLCBhbmQgdXNlIGEgY29tbWEgYXMgYSBzZXBhcmF0b3IKZGZfZ2RwIHw+IGZpbHRlcihjb3VudHJ5ICVpbiUgQ09VTlRSSUVTKSB8PiBkcm9wX25hKE5ZLkdEUC5NS1RQLlBQLktEKSB8PgogIGdncGxvdChhZXMoeWVhciwgTlkuR0RQLk1LVFAuUFAuS0QsIGNvbG9yID0gY291bnRyeSkpICsgZ2VvbV9saW5lKCkKYGBgCgpgYGB7ciBldmFsID0gRkFMU0V9CklTTzJDIDwtIGMoKSAjIHN1cnJvdW5kIHRoZSBpc28yYyBjb2RlIHdpdGggcXVvdGF0aW9uIG1hcmtzLCBhbmQgdXNlIGEgY29tbWEgYXMgYSBzZXBhcmF0b3IKZGZfZ2RwIHw+IGZpbHRlcihpc28yYyAlaW4lIElTTzJDKSB8PiBkcm9wX25hKE5ZLkdEUC5NS1RQLlBQLktEKSB8PgogIGdncGxvdChhZXMoeWVhciwgTlkuR0RQLk1LVFAuUFAuS0QsIGNvbG9yID0gaXNvMmMpKSArIGdlb21fbGluZSgpCmBgYAoKIyBXb3JsZCBEZXZlbG9wbWVudCBJbmRpY2F0b3JzCgojIyBGaW5kIEluZGljYXRvciBDb2RlcwoKMS4gIFdvcmxkIEJhbmsgSG9tZSBQYWdlCgogICAgLSAgIFdvcmxkIERldmVsb3BtZW50IEluZGljYXRvcnM6IFtMaW5rXShodHRwczovL2RhdGF0b3BpY3Mud29ybGRiYW5rLm9yZy93b3JsZC1kZXZlbG9wbWVudC1pbmRpY2F0b3JzLykKCiAgICAtICAgV29ybGQgQmFuayBPcGVuIERhdGE6IFtMaW5rXShodHRwczovL2RhdGEud29ybGRiYW5rLm9yZykKCiAgICAtICAgV0RJIFN0YXRpc3RpY2FsIFRhYmxlczogW0xpbmtdKGh0dHBzOi8vd2RpLndvcmxkYmFuay5vcmcvdGFibGVzKQoKMi4gIEV4Y2VsIEZpbGVzCgogICAgLSAgIERhdGEgQ2F0YWxvZ3VlczogW0xpbmtdKGh0dHBzOi8vZGF0YWNhdGFsb2cud29ybGRiYW5rLm9yZy9zZWFyY2gvZGF0YXNldC8wMDM3NzEyL1dvcmxkLURldmVsb3BtZW50LUluZGljYXRvcnMpIERvd25sb2FkIEV4Y2VsIEZpbGUKCjMuICBBUEkgU2VhcmNoCgpgYGB7cn0KV0RJc2VhcmNoKHN0cmluZyA9ICJnZHAiLCBmaWVsZCA9ICJuYW1lIikKYGBgCgpgYGB7cn0KV0RJc2VhcmNoKHN0cmluZyA9ICJOWS5HRFAuTUtUUC5QUC5LRCIsIGZpZWxkID0gImluZGljYXRvciIsIHNob3J0ID0gRkFMU0UpCmBgYAoKIyMjIyBBc3NpZ25tZW50CgoxLiAgRmluZCBhdCBsZWFzdCBvbmUgV0RJIGluZGljYXRvciB3aXRoIGl0cyBuYW1lIGFuZCBpdHMgY29kZS4KCjIuICBGaW5kIGF0IGxlYXN0IG9uZSBwYWlyIG9mIFdESSBpbmRpY2F0b3JzIHdpdGggdGhlaXIgbmFtZXMgYW5kIHRoZWlyIGNvZGVzIHlvdSB3YW50IHRvIHN0dWR5IHRoZWlyIHJlbGF0aW9uLgoKIyMgR0RQIGFuZCBHRFAgcGVyIENhcGl0YQoKMS4gIEdEUCwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpOiBOWS5HRFAuTUtUUC5QUC5LRAoKMi4gIFBvcHVsYXRpb24sIHRvdGFsOiBTUC5QT1AuVE9UTAoKMy4gIENhbGN1bGF0ZSBHRFAgcGVyIENhcGl0YQoKICAgIC0gICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpOiBOWS5HRFAuUENBUC5QUC5LRAoKLSAgIEdEUCwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpIFBQUCBHRFAgaXMgZ3Jvc3MgZG9tZXN0aWMgcHJvZHVjdCBjb252ZXJ0ZWQgdG8gaW50ZXJuYXRpb25hbCBkb2xsYXJzIHVzaW5nIHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IHJhdGVzLiBBbiBpbnRlcm5hdGlvbmFsIGRvbGxhciBoYXMgdGhlIHNhbWUgcHVyY2hhc2luZyBwb3dlciBvdmVyIEdEUCBhcyB0aGUgVS5TLiBkb2xsYXIgaGFzIGluIHRoZSBVbml0ZWQgU3RhdGVzLiBHRFAgaXMgdGhlIHN1bSBvZiBncm9zcyB2YWx1ZSBhZGRlZCBieSBhbGwgcmVzaWRlbnQgcHJvZHVjZXJzIGluIHRoZSBjb3VudHJ5IHBsdXMgYW55IHByb2R1Y3QgdGF4ZXMgYW5kIG1pbnVzIGFueSBzdWJzaWRpZXMgbm90IGluY2x1ZGVkIGluIHRoZSB2YWx1ZSBvZiB0aGUgcHJvZHVjdHMuIEl0IGlzIGNhbGN1bGF0ZWQgd2l0aG91dCBtYWtpbmcgZGVkdWN0aW9ucyBmb3IgZGVwcmVjaWF0aW9uIG9mIGZhYnJpY2F0ZWQgYXNzZXRzIG9yIGZvciBkZXBsZXRpb24gYW5kIGRlZ3JhZGF0aW9uIG9mIG5hdHVyYWwgcmVzb3VyY2VzLiBEYXRhIGFyZSBpbiBjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgZG9sbGFycy4gSUQ6IE5ZLkdEUC5NS1RQLlBQLktECgotICAgUG9wdWxhdGlvbiwgdG90YWwgVG90YWwgcG9wdWxhdGlvbiBpcyBiYXNlZCBvbiB0aGUgZGUgZmFjdG8gZGVmaW5pdGlvbiBvZiBwb3B1bGF0aW9uLCB3aGljaCBjb3VudHMgYWxsIHJlc2lkZW50cyByZWdhcmRsZXNzIG9mIGxlZ2FsIHN0YXR1cyBvciBjaXRpemVuc2hpcC4gVGhlIHZhbHVlcyBzaG93biBhcmUgbWlkeWVhciBlc3RpbWF0ZXMuIElEOiBTUC5QT1AuVE9UTAoKYGBge3IgY2FjaGUgPSBUUlVFfQpkZl9nZHBwY2FwIDwtIFdESShpbmRpY2F0b3IgPSBjKGdkcCA9ICJOWS5HRFAuTUtUUC5QUC5LRCIsIHBvcCA9ICJTUC5QT1AuVE9UTCIsIGdkcHBjYXAgPSAiTlkuR0RQLlBDQVAuUFAuS0QiKSwgZXh0cmEgPSBUUlVFKQpgYGAKCmBgYHtyRX0Kd3JpdGVfY3N2KGRmX2dkcHBjYXAsICJkYXRhL2dkcHBjYXAuY3N2IikKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcCA8LSByZWFkX2NzdigiZGF0YS9nZHBwY2FwLmNzdiIpCmBgYAoKYGBge3J9CnN0cihkZl9nZHBwY2FwKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwIHw+IHNlbGVjdChyZWdpb24sIGluY29tZSwgbGVuZGluZykgfD4gbGFwcGx5KHVuaXF1ZSkKYGBgCgpgYGB7cn0KQ09VTlRSWSA8LSAiV29ybGQiCmRmX2dkcHBjYXAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIGdkcHBjYXApKSArIGdlb21fbGluZSgpCmBgYAoKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl9nZHBwY2FwIHw+IGZpbHRlcihjb3VudHJ5ID09IENPVU5UUlkpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBwb3ApKSArIGdlb21fbGluZSgpCmBgYAoKIyMjIyBFeGVyY2lzZS4KCldyaXRlIHlvdXIgb2JzZXJ2YXRpb25zIGFuZCBxdWVzdGlvbnMuCgotICAgR0RQIGNoYXJ0IGxvb2tzIHNpbWlhciB0byBHRFBQZXJDYXBpdC4KCiMjIyBHRFAgUGVyIENhcGl0YQoKYGBge3J9CmRmX2dkcHBjYXAyIDwtIGRmX2dkcHBjYXAgfD4gZHJvcF9uYShwb3ApIHw+IAogIG11dGF0ZShQQ0FQID0gZ2RwL3BvcCwgLmFmdGVyID0gZ2RwcGNhcCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIKYGBgCgojIyMjIENoZWNrIGFnYWluc3QgR0RQIHBlciBjYXBpdGEsIFBQUAoKYGBge3J9CmRmX2dkcHBjYXAyIHw+IGRyb3BfbmEoZ2RwcGNhcCwgUENBUCkgfD4gbXV0YXRlKG5lYXIgPSBuZWFyKGdkcHBjYXAsIFBDQVApKSB8PiAKICBzdW1tYXJpemUobnVtYmVyb2ZkYXRhID0gbigpLCBzdW0obmVhcikpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAyIHw+IGZpbHRlcighbmVhcihnZHBwY2FwLCBQQ0FQKSkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKCFuZWFyKGdkcHBjYXAsIFBDQVApKSB8PiBkaXN0aW5jdChjb3VudHJ5KSB8PiBwdWxsKCkKYGBgCgojIyMjIEV4ZXJjaXNlLgoKV3JpdGUgeW91ciBvYnNlcnZhdGlvbnMgYW5kIHF1ZXN0aW9ucy4KCi0gICAKCiMjIyBWaXN1YWxpemF0aW9uCgpUd28gdXNlZnVsIHF1ZXN0aW9ucy4KCjEuICBXaGF0IHR5cGUgb2bCoCoqdmFyaWF0aW9uKirCoG9jY3Vyc8KgKip3aXRoaW4qKsKgbXkgdmFyaWFibGVzPwoKMi4gIFdoYXQgdHlwZSBvZsKgKipjb3ZhcmlhdGlvbioqwqBvY2N1cnPCoCoqYmV0d2VlbioqwqBteSB2YXJpYWJsZXM/CgpTZWUgW0xpbmtdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vcHJpbWVycy8zLjEpLgoKIyMjIyBSYW5rcy4KCmBhcnJhbmdlKGRlc2MoZ2RwKSlgIGlzIHRvIHJlb3JkZXIgaW4gZGVzY2VuZGluZyBvcmRlciBvZiBgZ2RwLGAgYGFycmFuZ2UoZ2RwKWAgaW4gYXNjZW5kaW5nIG9yZGVyLgoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShnZHApIHw+IGFycmFuZ2UoZGVzYyhnZHApKQpgYGAKCiMjIyMgRXhlcmNpc2VzLgoKMS4gIEZpbmQgdGhlIHRvcCAxMCBvZiB0aGUgY291bnRyaWVzIHdpdGggdGhlIGhpZ2hlc3QgR0RQIHBlciBjYXBpdGEuCgpgYGB7ciBldmFsID0gRkFMU0V9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShnZHBwY2FwKSB8PiBhcnJhbmdlKGRlc2MoZ2RwcGNhcCkpCmBgYAoKMS4gIEZpbmQgdGhlIHRvcCAxMCBvZiB0aGUgY291bnRyaWVzIHdpdGggdGhlIGxvd2VzdCBHRFAgcGVyIGNhcGl0YS4KCmBgYHtyIGV2YWwgPSBGQUxTRX0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGdkcHBjYXApIHw+IGFycmFuZ2UoZ2RwcGNhcCkKYGBgCgoxLiAgRmluZCB0aGUgdG9wIDEwIG9mIHRoZSBjb3VudHJpZXMgd2l0aCB0aGUgbGFyZ2VzdCBwb3B1bGF0aW9uLgoKMi4gIEZpbmQgdGhlIHRvcCAxMCBvZiB0aGUgY291bnRyaWVzIHdpdGggdGhlIHNtYWxsZXN0IHBvcHVsYXRpb24uCgojIyMjIFNjYXR0ZXIgUGxvdAoKV2hhdCB0eXBlIG9mwqAqKmNvdmFyaWF0aW9uKirCoG9jY3Vyc8KgKipiZXR3ZWVuKirCoG15IHZhcmlhYmxlcz8KCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMocG9wLCBnZHApKSArIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMocG9wLCBnZHApKSArIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMocG9wLCBnZHApKSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9IkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGdkcCwgcG9wKSB8PiBsbShsb2cxMChnZHApIH4gbG9nMTAocG9wKSwgZGF0YSA9IF8pIHw+IHN1bW1hcnkoKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMocG9wLCBnZHAsIGNvbG9yID0gcmVnaW9uKSkgKyBnZW9tX3BvaW50KCkgKyAKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCwgcmVnaW9uICE9IkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGdkcCwgcG9wKSB8PiAKICBnZ3Bsb3QoYWVzKHBvcCwgZ2RwLCBjb2xvciA9IHJlZ2lvbiwgc2hhcGUgPSBpbmNvbWUpKSArIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKQpgYGAKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwLCBnZHBwY2FwLCBwb3ApIHw+IAogIGdncGxvdChhZXMoZ2RwcGNhcCwgZ2RwLCBjb2xvciA9IHJlZ2lvbiwgc2l6ZSA9IHBvcCkpICsgZ2VvbV9wb2ludCgpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpCmBgYAoKYGBge3J9Cmluc3RhbGwucGFja2FnZXMoInBsb3RseSIpCmBgYAoKYGBge3IgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KHBsb3RseSkKdGVzdCA8LSBkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwLCByZWdpb24gIT0iQWdncmVnYXRlcyIpIHw+IGRyb3BfbmEoZ2RwLCBwb3ApIHw+IAogIGdncGxvdChhZXMoY29sb3IgPSBjb3VudHJ5LCBzaGFwZSA9IHJlZ2lvbiwgcG9wLCBnZHApKSArIGdlb21fcG9pbnQoKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIHNjYWxlX3lfbG9nMTAoKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKdGVzdCB8PiBnZ3Bsb3RseSgpCmBgYAoKIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIChFREEpCgohWypUaGUgaW1hZ2UgYWJvdmUgaXMgZnJvbSBbUjREUygyZSldKGh0dHBzOi8vcjRkcy5oYWRsZXkubnopIGJ5IEhhZGxleSBXaWNraGFtLCBNaW5lIMOHZXRpbmtheWEtUnVuZGVsIGFuZCBHYXJyZXR0IEdyb2xlbXVuZCpdKGRhdGEvZGF0YS1zY2llbmNlLnBuZykKCioqRURBKiogaXMgYW4gaXRlcmF0aXZlIGN5Y2xlIHRoYXQgaGVscHMgeW91IHVuZGVyc3RhbmQgd2hhdCB5b3VyIGRhdGEgc2F5cy4gV2hlbiB5b3UgZG8gRURBLCB5b3U6CgoxLiAgR2VuZXJhdGUgcXVlc3Rpb25zIGFib3V0IHlvdXIgZGF0YQoKMi4gIFNlYXJjaCBmb3IgYW5zd2VycyBieSB2aXN1YWxpc2luZywgdHJhbnNmb3JtaW5nLCBhbmQvb3IgbW9kZWxpbmcgeW91ciBkYXRhCgozLiAgVXNlIHdoYXQgeW91IGxlYXJuIHRvIHJlZmluZSB5b3VyIHF1ZXN0aW9ucyBhbmQvb3IgZ2VuZXJhdGUgbmV3IHF1ZXN0aW9ucwoKRURBIGlzIGFuIGltcG9ydGFudCBwYXJ0IG9mIGFueSBkYXRhIGFuYWx5c2lzLiBZb3UgY2FuIHVzZSBFREEgdG8gbWFrZSBkaXNjb3ZlcmllcyBhYm91dCB0aGUgd29ybGQ7IG9yIHlvdSBjYW4gdXNlIEVEQSB0byBlbnN1cmUgdGhlIHF1YWxpdHkgb2YgeW91ciBkYXRhLCBhc2tpbmcgcXVlc3Rpb25zIGFib3V0IHdoZXRoZXIgdGhlIGRhdGEgbWVldHMgeW91ciBzdGFuZGFyZHMgb3Igbm90LiAoUG9zaXQgUHJpbWVyczogW0VEQV0oaHR0cHM6Ly9wb3NpdC5jbG91ZC9sZWFybi9wcmltZXJzLzMuMSkpCgojIyBHRFAgYW5kIEdEUCBwZXIgQ2FwaXRhCgoxLiAgR0RQLCBQUFAgKGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBcJCk6IE5ZLkdEUC5NS1RQLlBQLktECgoyLiAgUG9wdWxhdGlvbiwgdG90YWw6IFNQLlBPUC5UT1RMCgozLiAgQ2FsY3VsYXRlIEdEUCBwZXIgQ2FwaXRhCgogICAgLSAgIEdEUCBwZXIgY2FwaXRhLCBQUFAgKGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBcJCk6IE5ZLkdEUC5QQ0FQLlBQLktECgotICAgR0RQLCBQUFAgKGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBcJCkgUFBQIEdEUCBpcyBncm9zcyBkb21lc3RpYyBwcm9kdWN0IGNvbnZlcnRlZCB0byBpbnRlcm5hdGlvbmFsIGRvbGxhcnMgdXNpbmcgcHVyY2hhc2luZyBwb3dlciBwYXJpdHkgcmF0ZXMuIEFuIGludGVybmF0aW9uYWwgZG9sbGFyIGhhcyB0aGUgc2FtZSBwdXJjaGFzaW5nIHBvd2VyIG92ZXIgR0RQIGFzIHRoZSBVLlMuIGRvbGxhciBoYXMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMuIEdEUCBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGNvdW50cnkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBkb2xsYXJzLiBJRDogTlkuR0RQLk1LVFAuUFAuS0QKCi0gICBQb3B1bGF0aW9uLCB0b3RhbCBUb3RhbCBwb3B1bGF0aW9uIGlzIGJhc2VkIG9uIHRoZSBkZSBmYWN0byBkZWZpbml0aW9uIG9mIHBvcHVsYXRpb24sIHdoaWNoIGNvdW50cyBhbGwgcmVzaWRlbnRzIHJlZ2FyZGxlc3Mgb2YgbGVnYWwgc3RhdHVzIG9yIGNpdGl6ZW5zaGlwLiBUaGUgdmFsdWVzIHNob3duIGFyZSBtaWR5ZWFyIGVzdGltYXRlcy4gSUQ6IFNQLlBPUC5UT1RMCgpgYGB7ciBjYWNoZSA9IFRSVUV9CmRmX2dkcHBjYXAgPC0gV0RJKGluZGljYXRvciA9IGMoZ2RwID0gIk5ZLkdEUC5NS1RQLlBQLktEIiwgcG9wID0gIlNQLlBPUC5UT1RMIiwgZ2RwcGNhcCA9ICJOWS5HRFAuUENBUC5QUC5LRCIpLCBleHRyYSA9IFRSVUUpCmBgYAoKYGBge3J9CndyaXRlX2NzdihkZl9nZHBwY2FwLCAiZGF0YS9nZHBwY2FwLmNzdiIpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAgPC0gcmVhZF9jc3YoImRhdGEvZ2RwcGNhcC5jc3YiKQpgYGAKCmBgYHtyfQpzdHIoZGZfZ2RwcGNhcCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBzZWxlY3QocmVnaW9uLCBpbmNvbWUsIGxlbmRpbmcpIHw+IGxhcHBseSh1bmlxdWUpCmBgYAoKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl9nZHBwY2FwIHw+IGZpbHRlcihjb3VudHJ5ID09IENPVU5UUlkpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBnZHBwY2FwKSkgKyBnZW9tX2xpbmUoKQpgYGAKCmBgYHtyfQpDT1VOVFJZIDwtICJXb3JsZCIKZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoY291bnRyeSA9PSBDT1VOVFJZKSB8PgogIGdncGxvdChhZXMoeWVhciwgcG9wKSkgKyBnZW9tX2xpbmUoKQpgYGAKCiMjIyMgRXhlcmNpc2UuCgpXcml0ZSB5b3VyIG9ic2VydmF0aW9ucyBhbmQgcXVlc3Rpb25zLgoKIyMjIFZpc3VhbGl6YXRpb24KClR3byB1c2VmdWwgcXVlc3Rpb25zLgoKMS4gIFdoYXQgdHlwZSBvZsKgKip2YXJpYXRpb24qKsKgb2NjdXJzwqAqKndpdGhpbioqwqBteSB2YXJpYWJsZXM/CgoyLiAgV2hhdCB0eXBlIG9mwqAqKmNvdmFyaWF0aW9uKirCoG9jY3Vyc8KgKipiZXR3ZWVuKirCoG15IHZhcmlhYmxlcz8KClNlZSBbTGlua10oaHR0cHM6Ly9wb3NpdC5jbG91ZC9sZWFybi9wcmltZXJzLzMuMSkuCgojIyMjIFJhbmtzLgoKYGFycmFuZ2UoZGVzYyhnZHApKWAgaXMgdG8gcmVvcmRlciBpbiBkZXNjZW5kaW5nIG9yZGVyIG9mIGBnZHAsYCBgYXJyYW5nZShnZHApYCBpbiBhc2NlbmRpbmcgb3JkZXIuCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGdkcCkgfD4gYXJyYW5nZShkZXNjKGdkcCkpCmBgYAoKIyMjIyBFeGVyY2lzZXMuCgoxLiAgRmluZCB0aGUgdG9wIDEwIG9mIHRoZSBjb3VudHJpZXMgd2l0aCB0aGUgaGlnaGVzdCBHRFAgcGVyIGNhcGl0YS4KCjIuICBGaW5kIHRoZSB0b3AgMTAgb2YgdGhlIGNvdW50cmllcyB3aXRoIHRoZSBsb3dlc3QgR0RQIHBlciBjYXBpdGEuCgozLiAgRmluZCB0aGUgdG9wIDEwIG9mIHRoZSBjb3VudHJpZXMgd2l0aCB0aGUgbGFyZ2VzdCBwb3B1bGF0aW9uLgoKNC4gIEZpbmQgdGhlIHRvcCAxMCBvZiB0aGUgY291bnRyaWVzIHdpdGggdGhlIHNtYWxsZXN0IHBvcHVsYXRpb24uCgojIyMjIFZhcmlhdGlvbiAxLiBIaXN0b2dyYW0KCkhpc3RvZ3JhbSBWaWRlbyBpbiBQb3NpdCBQcmltZXJzOiBbTGlua10oaHR0cHM6Ly92aW1lby5jb20vMjIxNjA3MzQxKQoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShnZHApIHw+IAogIGdncGxvdChhZXMoZ2RwKSkgKyBnZW9tX2hpc3RvZ3JhbSgpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShnZHBwY2FwKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXApKSArIGdlb21faGlzdG9ncmFtKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKGdkcCkgfD4gCiAgZ2dwbG90KGFlcyhnZHApKSArIGdlb21faGlzdG9ncmFtKCkgKyBzY2FsZV94X2xvZzEwKCkKYGBgCgojIyMjIEV4ZXJjaXNlcy4KCjEuICBDaGFuZ2UgYmlucywgaS5lLiwgYGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMClgLCBldGMuCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKGdkcCkgfD4gCiAgZ2dwbG90KGFlcyhnZHApKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyMCkgKyBzY2FsZV94X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKGdkcHBjYXApIHw+IAogIGdncGxvdChhZXMoZ2RwcGNhcCkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAxMDAwMCkKYGBgCgoyLiAgQ3JlYXRlIGEgc2ltaWxhciBoaXN0b2dyYW0gb2YgZ2RwcGNhcCBieSB1c2luZyBgc2NhbGVfeF9sb2cxMCgpYCBhbmQgYWRqdXN0IHRoZSBudW1iZXIgb2YgYmlucy4KCmBgYHtyfQpkZl9nZHBwY2FwIHw+IGZpbHRlcih5ZWFyID09IDIwMjIsIHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IGRyb3BfbmEoZ2RwcGNhcCkgfD4gCiAgZ2dwbG90KGFlcyhnZHBwY2FwKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTApICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKMy4gIENyZWF0ZSBhIHNpbWlsYXIgaGlzdG9ncmFtIGZvciBwb3B1bGF0aW9uLgoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShwb3ApIHw+IAogIGdncGxvdChhZXMocG9wKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjApICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKNC4gIFdyaXRlIHlvdXIgb2JzZXJ2YXRpb25zIGFuZCBjb21tZW50cy4KCiMjIyMgRXh0cmEuCgpgYGB7cn0KZGZfZ2RwcGNhcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIyLHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IGRyb3BfbmEocG9wKSB8PiAKICBncm91cF9ieShyZWdpb24pIHw+IAogIGdncGxvdChhZXMocG9wLCBmaWxsID0gcmVnaW9uKSkgKyBnZW9tX2hpc3RvZ3JhbShjb2wgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjIpICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMiwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYShwb3ApIHw+IAogIGdyb3VwX2J5KHJlZ2lvbikgfD4gCiAgZ2dwbG90KGFlcyhwb3AsIGZpbGwgPSBpbmNvbWUpKSArIGdlb21faGlzdG9ncmFtKGNvbCA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuMikgKyBzY2FsZV94X2xvZzEwKCkKYGBgCgpXb3JsZCBieSBJbmNvbWUgYW5kIFJlZ2lvbjogW1tMaW5rXShodHRwczovL2RhdGF0b3BpY3Mud29ybGRiYW5rLm9yZy93b3JsZC1kZXZlbG9wbWVudC1pbmRpY2F0b3JzL3RoZS13b3JsZC1ieS1pbmNvbWUtYW5kLXJlZ2lvbi5odG1sKV0KCiMjIyMgVmFyaWF0aW9uIDIuIEJveHBsb3QKCkJveHBsb3QgVmlkZW8gaW4gUG9zaXQgUHJpbWVyczogW0xpbmtdKGh0dHBzOi8vdmltZW8uY29tLzIyMjM1ODAzNCkKCmBgYHtyfQpkZl9nZHBwY2FwMiB8PiBmaWx0ZXIoeWVhciAlaW4lIGMoMTk5MCwyMDAwLCAyMDEwLCAyMDIwKSkgfD4gZHJvcF9uYShnZHBwY2FwKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIGZhY3Rvcih5ZWFyKSkpICsgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgJWluJSBjKDE5OTAsMjAwMCwgMjAxMCwgMjAyMCkpIHw+IGRyb3BfbmEoZ2RwcGNhcCkgfD4gCiAgZ2dwbG90KGFlcyhnZHBwY2FwLCBmYWN0b3IoeWVhcikpKSArIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKHRpdGxlID0gIkRpc3RyaWJ1dGlvbiBvZiB0aGUgR0RQIHBlciBDYXBpdGEgb2YgQ291bnRyaWVzIiwgc3VidGl0bGUgPSAiWWVhciAxOTkwLCAyMDAwLCAyMDEwLCAyMDIwIiwgCiAgICAgICB5ID0gIlllYXIiLCB4ID0gIkdEUCBwZXIgY2FwaXRhIGluIGxvZzEwIHNjYWxlIikKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHBwY2FwKSB8PiAKICBmaWx0ZXIoaW5jb21lICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZ2dwbG90KGFlcyhnZHBwY2FwLCBpbmNvbWUsIGZpbGwgPSBpbmNvbWUpKSArIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3J9CmRmX2dkcHBjYXAyIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoZ2RwcGNhcCkgfD4gCiAgZmlsdGVyKGluY29tZSAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGdncGxvdChhZXMoZ2RwcGNhcCwgZmFjdG9yKGluY29tZSwgbGV2ZWxzID0gYygiSGlnaCBpbmNvbWUiLCAiVXBwZXIgbWlkZGxlIGluY29tZSIsICJMb3dlciBtaWRkbGUgaW5jb21lIiwgIkxvdyBpbmNvbWUiKSksIGZpbGwgPSBpbmNvbWUpKSArIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKHkgPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpgYGB7cn0KZGZfZ2RwcGNhcDIgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHApIHw+IAogIGZpbHRlcihpbmNvbWUgIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcCwgcmVnaW9uLCBmaWxsID0gcmVnaW9uKSkgKyBnZW9tX2JveHBsb3QoKSArIHNjYWxlX3hfbG9nMTAoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIENPMiBFbWlzc2lvbnMgUGVyIENhcGl0YSB2cyBHRFAgUGVyIENhcGl0YQoKMS4gIENPMiBlbWlzc2lvbnMgKG1ldHJpYyB0b25zIHBlciBjYXBpdGEpOiBFTi5BVE0uQ08yRS5QQwoKMi4gIEdEUCBwZXIgY2FwaXRhLCBQUFAgKGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBcJCk6IE5ZLkdEUC5QQ0FQLlBQLktECgotICAgQ08yIGVtaXNzaW9ucyAobWV0cmljIHRvbnMgcGVyIGNhcGl0YSkgQ2FyYm9uIGRpb3hpZGUgZW1pc3Npb25zIGFyZSB0aG9zZSBzdGVtbWluZyBmcm9tIHRoZSBidXJuaW5nIG9mIGZvc3NpbCBmdWVscyBhbmQgdGhlIG1hbnVmYWN0dXJlIG9mIGNlbWVudC4gVGhleSBpbmNsdWRlIGNhcmJvbiBkaW94aWRlIHByb2R1Y2VkIGR1cmluZyBjb25zdW1wdGlvbiBvZiBzb2xpZCwgbGlxdWlkLCBhbmQgZ2FzIGZ1ZWxzIGFuZCBnYXMgZmxhcmluZy4gRU4uQVRNLkNPMkUuUEMKCi0gICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpIEdEUCBwZXIgY2FwaXRhIGJhc2VkIG9uIHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IChQUFApLiBQUFAgR0RQIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgY29udmVydGVkIHRvIGludGVybmF0aW9uYWwgZG9sbGFycyB1c2luZyBwdXJjaGFzaW5nIHBvd2VyIHBhcml0eSByYXRlcy4gQW4gaW50ZXJuYXRpb25hbCBkb2xsYXIgaGFzIHRoZSBzYW1lIHB1cmNoYXNpbmcgcG93ZXIgb3ZlciBHRFAgYXMgdGhlIFUuUy4gZG9sbGFyIGhhcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gR0RQIGF0IHB1cmNoYXNlcidzIHByaWNlcyBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGNvdW50cnkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBkb2xsYXJzLiBJRDogTlkuR0RQLlBDQVAuUFAuS0QKCiMjIyBJbXBvcnRpbmcgRGF0YQoKYGBge3IgY2FjaGUgPSBUUlVFRX0KZGZfY28yZ2RwIDwtIFdESShpbmRpY2F0b3IgPSBjKGNvMnBjYXAgPSAiRU4uQVRNLkNPMkUuUEMiLCBnZHBwY2FwID0gIk5ZLkdEUC5QQ0FQLlBQLktEIiksIGV4dHJhID0gVFJVRSkKYGBgCgpgYGB7cn0Kd3JpdGVfY3N2KGRmX2NvMmdkcCwgImRhdGEvY28yZ2RwLmNzdiIpCmBgYAoKYGBge3J9CmRmX2NvMmdkcCA8LSByZWFkX2NzdigiZGF0YS9jbzJnZHAuY3N2IikKYGBgCgojIyMgVmlzdWFsaXphdGlvbiBieSBMaW5lIEdyYXBocwoKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl9jbzJnZHAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIGNvMnBjYXApKSArIGdlb21fbGluZSgpCmBgYAoKYGBge3J9CklTTzJDIDwtIGMoIkpQIiwgIkNOIiwgIklEIiwgIkdCIiwgIlVTIiwgIkRFIiwgIkZSIikKZGZfY28yZ2RwIHw+IGZpbHRlcihpc28yYyAlaW4lIElTTzJDKSB8PiBkcm9wX25hKGNvMnBjYXApIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBjbzJwY2FwLCBsaW5ldHlwZSA9IGlzbzJjKSkgKyBnZW9tX2xpbmUoKQpgYGAKCiMjIyMgRXhlcmNpc2VzLgoKMS4gIENoYW5nZSBgaXNvMmNgIGNvZGVzIHRvIHRob3NlIHlvdSB3YW50IHRvIGludmVzdGlnYXRlLiBVc2UgYGRmX2NvZGVzYCB1bmRlciBFbnZpcm9ubWVudAoyLiAgQ2hhbmdlIGBsaW5ldHlwZWAgdG8gY29sLgoKYGBge3J9CklTTzJDIDwtIGMoIkpQIiwgIkNOIiwgIklEIiwgIkdCIiwgIlVTIiwgIkRFIiwgIkZSIikKZGZfY28yZ2RwIHw+IGZpbHRlcihpc28yYyAlaW4lIElTTzJDKSB8PiBkcm9wX25hKGNvMnBjYXApIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBjbzJwY2FwLCBjb2wgPSBpc28yYykpICsgZ2VvbV9saW5lKCkKYGBgCgojIyMgU2NhdHRlcnBsb3QgZm9yIENvdmFyaWF0aW9uCgpgYGB7cn0KZGZfY28yZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoY28ycGNhcCkgfD4KICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIGNvMnBjYXApKSArIGdlb21fcG9pbnQoKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gCiAgZHJvcF9uYShnZHBwY2FwLCBjbzJwY2FwKSB8PgogIGdncGxvdChhZXMoZ2RwcGNhcCwgY28ycGNhcCkpICsgZ2VvbV9wb2ludCgpICsKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgojIyMjIFNjYXR0ZXJwbG90IHdpdGggYSByZWdyZXNzaW9uIGxpbmUKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gCiAgZHJvcF9uYShnZHBwY2FwLCBjbzJwY2FwKSB8PgogIGdncGxvdChhZXMoZ2RwcGNhcCwgY28ycGNhcCkpICsgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0gJ3l+eCcsIHNlID0gRkFMU0UpICsKICBzY2FsZV94X2xvZzEwKCkgKyBzY2FsZV95X2xvZzEwKCkKYGBgCgojIyMjIFN1bW1hcnkgb2YgYSBsaW5lYXIgbW9kZWwKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHBwY2FwLCBjbzJwY2FwKSB8PgogIGxtKGxvZzEwKGNvMnBjYXApfmxvZzEwKGdkcHBjYXApLCBkYXRhID0gXykgfD4gc3VtbWFyeSgpCmBgYAoKIyMgU2Nob29sIEVucm9sbG1lbnQgdnMgR0RQIFBlciBDYXBpdGEKCjEuICBTY2hvb2wgZW5yb2xsbWVudCwgc2Vjb25kYXJ5ICglIGdyb3NzKTogU0UuU0VDLkVOUlIKCjIuICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpOiBOWS5HRFAuUENBUC5QUC5LRAoKLSAgIFNjaG9vbCBlbnJvbGxtZW50LCBzZWNvbmRhcnkgKCUgZ3Jvc3MpIEdyb3NzIGVucm9sbG1lbnQgcmF0aW8gaXMgdGhlIHJhdGlvIG9mIHRvdGFsIGVucm9sbG1lbnQsIHJlZ2FyZGxlc3Mgb2YgYWdlLCB0byB0aGUgcG9wdWxhdGlvbiBvZiB0aGUgYWdlIGdyb3VwIHRoYXQgb2ZmaWNpYWxseSBjb3JyZXNwb25kcyB0byB0aGUgbGV2ZWwgb2YgZWR1Y2F0aW9uIHNob3duLiBTZWNvbmRhcnkgZWR1Y2F0aW9uIGNvbXBsZXRlcyB0aGUgcHJvdmlzaW9uIG9mIGJhc2ljIGVkdWNhdGlvbiB0aGF0IGJlZ2FuIGF0IHRoZSBwcmltYXJ5IGxldmVsLCBhbmQgYWltcyBhdCBsYXlpbmcgdGhlIGZvdW5kYXRpb25zIGZvciBsaWZlbG9uZyBsZWFybmluZyBhbmQgaHVtYW4gZGV2ZWxvcG1lbnQsIGJ5IG9mZmVyaW5nIG1vcmUgc3ViamVjdC0gb3Igc2tpbGwtb3JpZW50ZWQgaW5zdHJ1Y3Rpb24gdXNpbmcgbW9yZSBzcGVjaWFsaXplZCB0ZWFjaGVycy4gU0UuU0VDLkVOUlIKCi0gICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpIEdEUCBwZXIgY2FwaXRhIGJhc2VkIG9uIHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IChQUFApLiBQUFAgR0RQIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgY29udmVydGVkIHRvIGludGVybmF0aW9uYWwgZG9sbGFycyB1c2luZyBwdXJjaGFzaW5nIHBvd2VyIHBhcml0eSByYXRlcy4gQW4gaW50ZXJuYXRpb25hbCBkb2xsYXIgaGFzIHRoZSBzYW1lIHB1cmNoYXNpbmcgcG93ZXIgb3ZlciBHRFAgYXMgdGhlIFUuUy4gZG9sbGFyIGhhcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gR0RQIGF0IHB1cmNoYXNlcidzIHByaWNlcyBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGNvdW50cnkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBkb2xsYXJzLiBJRDogTlkuR0RQLlBDQVAuUFAuS0QKCiMjIyBJbXBvcnRpbmcgRGF0YQoKYGBge3IgY2FjaGUgPSBUUlVFfQpkZl9zZWNnZHAgPC0gV0RJKGluZGljYXRvciA9IGMoc2VjID0gIlNFLlNFQy5FTlJSIiwgZ2RwcGNhcCA9ICJOWS5HRFAuUENBUC5QUC5LRCIpLCBleHRyYSA9IFRSVUUpCmBgYAoKYGBge3J9CndyaXRlX2NzdihkZl9zZWNnZHAsICJkYXRhL3NlY2dkcC5jc3YiKQpgYGAKCmBgYHtyfQpkZl9zZWNnZHAgPC0gcmVhZF9jc3YoImRhdGEvc2VjZ2RwLmNzdiIpCmBgYAoKIyMjIFZpc3VhbGl6YXRpb24gYnkgTGluZSBHcmFwaHMKCmBgYHtyfQpDT1VOVFJZIDwtICJXb3JsZCIKZGZfc2VjZ2RwIHw+IGZpbHRlcihjb3VudHJ5ID09IENPVU5UUlkpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBzZWMpKSArIGdlb21fbGluZSgpCmBgYAoKYGBge3J9CkNPVU5UUklFUyA8LSBjKCJMb3cgaW5jb21lIiwgIkxvdyAmIG1pZGRsZSBpbmNvbWUiLCAiTG93ZXIgbWlkZGxlIGluY29tZSIsICJNaWRkbGUgaW5jb21lIiwgIlVwcGVyIG1pZGRsZSBpbmNvbWUiLCAiSGlnaCBpbmNvbWUiKQpkZl9zZWNnZHAgfD4gZmlsdGVyKGNvdW50cnkgJWluJSBDT1VOVFJJRVMpIHw+IGRyb3BfbmEoc2VjKSB8PgogIGdncGxvdChhZXMoeWVhciwgc2VjLCBsaW5ldHlwZSA9IGZhY3Rvcihjb3VudHJ5LCBsZXZlbHMgPSBDT1VOVFJJRVMpKSkgKyBnZW9tX2xpbmUoKSArCiAgbGFicyhsaW5ldHlwZSA9ICJJbmNvbWUgTGV2ZWxzIikKYGBgCgojIyMjIEV4ZXJjaXNlLgoKQ2hhbmdlIGBDT1VOVFJJRVNgIHRvIGBJU08yQ2Agb2YgY291bnRyaWVzIHlvdSB3YW50IHRvIGludmVzdGlnYXRlLiBVc2UgYGRmX2NvZGVzYCB1bmRlciBFbnZpcm9ubWVudAoKYGBge3J9CmRmX3NlY2dkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBkcm9wX25hKHNlYykgfD4KICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIHNlYykpICsgZ2VvbV9wb2ludCgpCmBgYAoKYGBge3J9CmRmX3NlY2dkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBkcm9wX25hKGdkcHBjYXAsIHNlYykgfD4KICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIHNlYykpICsgZ2VvbV9wb2ludCgpICsKICBzY2FsZV94X2xvZzEwKCkKYGBgCgpgYGB7cn0KZGZfc2VjZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoZ2RwcGNhcCwgc2VjKSB8PgogIGdncGxvdChhZXMoZ2RwcGNhcCwgc2VjKSkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSAneX54Jywgc2UgPSBGQUxTRSkgKwogIHNjYWxlX3hfbG9nMTAoKQpgYGAKCmBgYHtyfQpkZl9zZWNnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHBwY2FwLCBzZWMpIHw+CiAgbG0oc2VjfmxvZzEwKGdkcHBjYXApLCBkYXRhID0gXykgfD4gc3VtbWFyeSgpCmBgYAoKIyMgUmVmZXJlbmNlcwoKMS4gIFIgZm9yIERhdGEgU2NpZW5jZSAoMmUpOiBbTGlua10oaHR0cHM6Ly9yNGRzLmhhZGxleS5ueikuIFRoZSBGaXJzdCBFZGl0aW9uOiBbTGlua10oaHR0cHM6Ly9yNGRzLmhhZC5jby5ueikuCgoyLiAgUG9zaXQgUHJpbWVyczogW0xpbmtdKGh0dHBzOi8vcG9zaXQuY2xvdWQvbGVhcm4vcHJpbWVycykuCgozLiAgQ2hlYXQgU2hlZXQ6IFtMaW5rXShodHRwczovL3Bvc2l0LmNsb3VkL2xlYXJuL2NoZWF0LXNoZWV0cykuCgo0LiAgU2hhcmVkIFByb2plY3Q6wqA8aHR0cHM6Ly9wb3NpdC5jbG91ZC9jb250ZW50LzU1Mzk3NjM+CgogICAgLSAgIGludHJvMnI6IFJldmlzZWQgdmVyc2lvbiBvZiB0aGUgbWF0ZXJpYWxzIHVzZWQgaW4gUVBGRDQxMSBBWTIwMjIgKDIgcGVyaW9kcykKCiAgICAgICAgLSAgIGludHJvMnItMSwgaW50cm8yci0yIG9yIGludHJvMnIyMDIzCiAgICAgICAgLSAgIFtIUCBMaW5rXShodHRwczovL2RzLXNsLmdpdGh1Yi5pby9pbnRybzJyL2ludHJvMnIubmIuaHRtbCkKCiAgICAtICAgaW50cm8ycmogKEVDTzIzMikgYW5kIGZpcnN0X2RzIChFQ08xMDEpIGFyZSBpbiBKYXBhbmVzZQoKICAgICAgICAtICAgW0hQIExpbmtdKGh0dHBzOi8vZHMtc2wuZ2l0aHViLmlvL2ludHJvMnIvZmlyc3RfZHMubmIuaHRtbCkgLSB1c2UgRW5nbGlzaCB0cmFuc2xhdGlvbiBvZiBHb29nbGUgQ2hyb21lCgogICAgICAgICAgICAtICAgTGVjdHVyZSBWaWRlb3MgaW4gSmFwYW5lc2UgWzxhIGhyZWY9Imh0dHBzOi8veW91dHUuYmUvSTdUSTNkU3VLTEkiPnZpZGVvMTwvYT5d44O7WzxhIGhyZWY9Imh0dHBzOi8veW91dHUuYmUvTFUzTktUTndQREEiPnZpZGVvMjwvYT5d44O7WzxhIGhyZWY9Imh0dHBzOi8veW91dHUuYmUvVUZtQWhvMWlMWGMiPnZpZGVvMzwvYT5dCgo1LiAgUiBmb3IgU29jaWFsIFNjaWVudGlzdHM6IDxodHRwczovL2RhdGFjYXJwZW50cnkub3JnL3Itc29jaWFsc2NpLz4KCiAgICAtICAgT2xkIFNoYXJlZCBQcm9qZWN0OiA8aHR0cHM6Ly9yc3R1ZGlvLmNsb3VkL2NvbnRlbnQvNDg1ODk0OD4KCiAgICAgICAgLSAgIHI0c3MuUm1kLCByNHNzLm5iLmh0bWwgLSBPbGQgdmVyc2lvbiBvZiBSIGZvciBTb2NpYWwgU2NpZW50aXN0cwoKNi4gIERhdGEgQW5hbHlzaXMgZm9yIFJlc2VhcmNoZXJzIEFZMjAyMjogW0xpbmsuXShodHRwczovL2ljdS1oc3V6dWtpLmdpdGh1Yi5pby9kYTRyMjAyMi9pbmRleC5odG1sKQoKNy4gIOOBv+OCk+OBquOBruODh+ODvOOCv+OCteOCpOOCqOODs+OCuSAtIERhdGEgU2NpZW5jZSBmb3IgQWxsIChpbiBKYXBhbmVzZSkKCiAgICAtICAgRGlnaXRhbCBCb29rIFtMaW5rXShodHRwczovL2ljdS1oc3V6dWtpLmdpdGh1Yi5pby9kczRhai8pIC0gdXNlIEVuZ2xpc2ggdHJhbnNsYXRpb24gb2YgR29vZ2xlIENocm9tZQoKIyBFREEgdXNpbmcgV0RJCgojIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzLCBFREEKCkVEQSBpcyBhbiBpdGVyYXRpdmUgY3ljbGUgdGhhdCBoZWxwcyB5b3UgdW5kZXJzdGFuZCB3aGF0IHlvdXIgZGF0YSBzYXlzLiBXaGVuIHlvdSBkbyBFREEsIHlvdToKCjEuICBHZW5lcmF0ZSBxdWVzdGlvbnMgYWJvdXQgeW91ciBkYXRhCgoyLiAgU2VhcmNoIGZvciBhbnN3ZXJzIGJ5IHZpc3VhbGlzaW5nLCB0cmFuc2Zvcm1pbmcsIGFuZC9vciBtb2RlbGluZyB5b3VyIGRhdGEKCjMuICBVc2Ugd2hhdCB5b3UgbGVhcm4gdG8gcmVmaW5lIHlvdXIgcXVlc3Rpb25zIGFuZC9vciBnZW5lcmF0ZSBuZXcgcXVlc3Rpb25zCgpFREEgaXMgYW4gaW1wb3J0YW50IHBhcnQgb2YgYW55IGRhdGEgYW5hbHlzaXMuIFlvdSBjYW4gdXNlIEVEQSB0byBtYWtlIGRpc2NvdmVyaWVzIGFib3V0IHRoZSB3b3JsZDsgb3IgeW91IGNhbiB1c2UgRURBIHRvIGVuc3VyZSB0aGUgcXVhbGl0eSBvZiB5b3VyIGRhdGEsIGFza2luZyBxdWVzdGlvbnMgYWJvdXQgd2hldGhlciB0aGUgZGF0YSBtZWV0cyB5b3VyIHN0YW5kYXJkcyBvciBub3QuIChQb3NpdCBQcmltZXJzOiBbRURBXShodHRwczovL3Bvc2l0LmNsb3VkL2xlYXJuL3ByaW1lcnMvMy4xKSkKCiMjIFdvcmtmbG93CgoxLiAgSW1wb3J0aW5nIGRhdGEgYnkgV0RJCgpgYGAgICAgICAgICAKZGZfZGF0YWZyYW1lX25hbWUgPC0gV0RJKGluZGljYXRvcnMgPSBjKG5hbWUxID0gIkluZGljYXRvciBDb2RlIDEiLCAKbmFtZTIgPSAiSW5kaWNhdG9yIENvZGUgMiIpLCBleHRyYSA9IFRSVUUpCmBgYAoKV3JpdGUgYW5kIHJlYWQ6CgpgYGAgICAgICAgICAKd3JpdGVfY3N2KGRmX2RhdGFmcmFtZV9uYW1lLCAiZGF0YS9kYXRhZnJhbWVfbmFtZS5jc3YiKQpkZl9kYXRhZnJhbWVfbmFtZSA8LSByZWFkX2NzdigiZGF0YS9kYXRhZnJhbWVfbmFtZS5jc3YiKQpgYGAKCjIuICBWaWV3aW5nIGRhdGEgYnkKCmBoZWFkKClgLCBgc3RyKClgLCBgc3VtbWFyeSgpYCwgYW5kIHRyeSBgZGZfZGF0YWZyYW1lX25hbWVgLiBTZWUgYWxzbyBFbnZpcm9ubWVudCBUYWIgb2YgUlN0dWRpby4KCjMuICBUcmFuc2Zvcm1pbmcgZGF0YSBieSByZXN0cmljdGluZyB0aGUgdmFsdWVzIG9mIGEgdmFyaWFibGUuCgpgYGAgICAgICAgICAKZGZfZGF0YWZyYW1lX25hbWUgfD4gZmlsdGVyKHZhciA9PSAidmFsdWUiKSAKZGZfZGF0YWZyYW1lX25hbWUgfD4gZmlsdGVyKHZhciAlaW4lIGMoInZhbHVlXzEiLCAuLi4gLCAidmFsdWVfbiIpIApkZl9kYXRhZnJhbWVfbmFtZSB8PiBmaWx0ZXIodmFyICE9ICJ2YWx1ZSIpIApkZl9kYXRhZnJhbWVfbmFtZSB8PiBkcm9wX25hKHZhcikKYGBgCgotICAgQ3JlYXRpbmcgYSBuZXcgdmFyaWFibGUgYnkgbXV0YXRpb24uIChBIGxpdHRsZSBhZHZhbmNlZC4gUENBUCA9IGdkcC9wb3ApCgpgYGAgICAgICAgICAKZGZfZGF0YWZyYW1lX25hbWUgfD4gbXV0YXRlKHZhcl9uZXcgPSB2YXIxICogdmFyMil9CmBgYAoKNC4gIENoYW5nZSBvcmRlcnMgYnkgYGFycmFuZ2UoKWAKCmBgYCAgICAgICAgIApkZl9kYXRhZnJhbWVfbmFtZSB8PiBhcnJhbmdlKHZhcikKZGZfZGF0YWZyYW1lX25hbWUgfD4gYXJyYW5nZShkc2ModmFyKSkKYGBgCgo1LiAgVmlzdWFsaXppbmcgdXNpbmcgZ2dwbG90KCkgKyBnZW9tXF9cKigpCgogICAgV2hhdCB0eXBlIG9mwqAqKnZhcmlhdGlvbioqwqBvY2N1cnPCoCoqd2l0aGluKirCoG15IHZhcmlhYmxlcz8KCiAgICBXaGF0IHR5cGUgb2bCoCoqY292YXJpYXRpb24qKsKgb2NjdXJzwqAqKmJldHdlZW4qKsKgbXkgdmFyaWFibGVzPwoKLSAgIGxpbmUgZ3JhcGgKCmBgYCAgICAgICAgIAp0cmFuc2Zvcm1lZF9kYXRhIHw+IGdncGxvdChhZXMoeWVhciwgbmFtZTEpKSArIGdlb21fbGluZSgpCnRyYW5zZm9ybWVkX2RhdGEgfD4gZ2dwbG90KGFlcyh5ZWFyLCBuYW1lMikpICsgZ2VvbV9saW5lKCkKYGBgCgotICAgc2NhdHRlcnBsb3QKCmBgYCAgICAgICAgIAp0cmFuc2Zvcm1lZF9kYXRhIHw+IGdncGxvdChhZXMobmFtZTEsIG5hbWUyKSkgKyBnZW9tX3BvaW50KCkKdHJhbnNmb3JtZWRfZGF0YSB8PiBnZ3Bsb3QoYWVzKG5hbWUxLCBuYW1lMikpICsgZ2VvbV9wb2ludCgpICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKLSAgIHNjYXR0ZXJwbG90IHdpdGggYSByZWdyZXNzaW9uIGxpbmUKCmBgYCAgICAgICAgIAp0cmFuc2Zvcm1lZF9kYXRhIHw+IGdncGxvdChhZXMobmFtZTEsIG5hbWUyKSkgKyBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpCnRyYW5zZm9ybWVkX2RhdGEgfD4gZ2dwbG90KGFlcyhuYW1lMSwgbmFtZTIpKSArIGdlb21fcG9pbnQoKSArIAogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UpICsgc2NhbGVfeF9sb2cxMCgpCmBgYAoKLSAgIGhpc3RvZ3JhbQoKYGBgICAgICAgICAgCnRyYW5zZm9ybWVkX2RhdGEgfD4gZ2dwbG90KGFlcyhuYW1lMSkpICsgZ2VvbV9oaXN0b2dyYW0oKQpgYGAKCi0gICBib3hwbG90CgpgY2F0ZWdvcmljYWxfdmFyYDogYGZhY3Rvcih5ZWFyKWAsIGBpbmNvbWVgLCBgcmVnaW9uYAoKYGBgICAgICAgICAgCnRyYW5zZm9ybWVkX2RhdGEgfD4gZ2dwbG90KGFlcyhjYXRlZ29yaWNhbF92YXIsIG5hbWUxKSkgKyBnZW9tX2JveHBsb3QoKQpgYGAKCjYuICBEbyBub3QgZm9yZ2V0IHRvIGFkZCB5b3VyIG9ic2VydmF0aW9ucyBhbmQgcXVlc3Rpb25zLgoKIyMgU2V0dXAKCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShXREkpCmBgYAoKIyBFeGFtcGxlcwoKIyMgQ09+Mn4gRW1pc3Npb25zIFBlciBDYXBpdGEgdnMgR0RQIFBlciBDYXBpdGEKCldlIHN0dWR5IHRoZSByZWxhdGlvbiBiZXR3ZWVuIHRoZSBDT34yfiBlbWlzc2lvbiBwZXIgY2FwaXRhIGFuZCB0aGUgR0RQIHBlciBjYXBpdGEgdXNpbmcgdGhlIGZvbGxvd2luZyB0d28gV29ybGQgRGV2ZWxvcG1lbnQgSW5kaWNhdG9ycy4KCjEuICBDTzIgZW1pc3Npb25zIChtZXRyaWMgdG9ucyBwZXIgY2FwaXRhKTogRU4uQVRNLkNPMkUuUEMKCi0gICBEZXNjcmlwdGlvbjogQ08yIGVtaXNzaW9ucyAobWV0cmljIHRvbnMgcGVyIGNhcGl0YSkgQ2FyYm9uIGRpb3hpZGUgZW1pc3Npb25zIGFyZSB0aG9zZSBzdGVtbWluZyBmcm9tIHRoZSBidXJuaW5nIG9mIGZvc3NpbCBmdWVscyBhbmQgdGhlIG1hbnVmYWN0dXJlIG9mIGNlbWVudC4gVGhleSBpbmNsdWRlIGNhcmJvbiBkaW94aWRlIHByb2R1Y2VkIGR1cmluZyBjb25zdW1wdGlvbiBvZiBzb2xpZCwgbGlxdWlkLCBhbmQgZ2FzIGZ1ZWxzIGFuZCBnYXMgZmxhcmluZy4gRU4uQVRNLkNPMkUuUEMKCjIuICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpOiBOWS5HRFAuUENBUC5QUC5LRAoKLSAgIERlc2NyaXB0aW9uOiBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpIEdEUCBwZXIgY2FwaXRhIGJhc2VkIG9uIHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IChQUFApLiBQUFAgR0RQIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgY29udmVydGVkIHRvIGludGVybmF0aW9uYWwgZG9sbGFycyB1c2luZyBwdXJjaGFzaW5nIHBvd2VyIHBhcml0eSByYXRlcy4gQW4gaW50ZXJuYXRpb25hbCBkb2xsYXIgaGFzIHRoZSBzYW1lIHB1cmNoYXNpbmcgcG93ZXIgb3ZlciBHRFAgYXMgdGhlIFUuUy4gZG9sbGFyIGhhcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gR0RQIGF0IHB1cmNoYXNlcidzIHByaWNlcyBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGNvdW50cnkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBkb2xsYXJzLiBJRDogTlkuR0RQLlBDQVAuUFAuS0QKCiMjIyBJbXBvcnRpbmcgRGF0YQoKYGBge3IgY2FjaGUgPSBUUlVFLCBldmFsID0gRkFMU0V9CmRmX2NvMmdkcCA8LSBXREkoaW5kaWNhdG9yID0gYyhjbzJwY2FwID0gIkVOLkFUTS5DTzJFLlBDIiwgZ2RwcGNhcCA9ICJOWS5HRFAuUENBUC5QUC5LRCIpLAogICAgICAgICAgICAgICAgIGV4dHJhID0gVFJVRSkKYGBgCgpgYGB7ciBldmFsID0gRkFMU0V9CndyaXRlX2NzdihkZl9jbzJnZHAsICJkYXRhL2NvMmdkcC5jc3YiKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgPC0gcmVhZF9jc3YoImRhdGEvY28yZ2RwLmNzdiIpCmBgYAoKIyMjIFZpc3VhbGl6YXRpb24gYnkgTGluZSBHcmFwaHMKCiMjIyMgQ09+Mn4gcGVyIGNhcGl0YQoKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl9jbzJnZHAgfD4gZmlsdGVyKGNvdW50cnkgPT0gQ09VTlRSWSkgfD4gZHJvcF9uYShjbzJwY2FwKSB8PgogIGdncGxvdChhZXMoeWVhciwgY28ycGNhcCkpICsgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSBleHByZXNzaW9uKHBhc3RlKENPWzJdLCAiIHBlciBjYXBpdGEgb2YgdGhlIFdvcmxkIikpLAogICAgICAgeSA9IGV4cHJlc3Npb24ocGFzdGUoQ09bMl0sICIgcGVyIGNhcGl0YSBpbiB0b25zIikpKQpgYGAKCmBgYHtyfQpJU08yQyA8LSBjKCJKUCIsICJDTiIsICJJRCIsICJHQiIsICJVUyIsICJERSIsICJGUiIpCmRmX2NvMmdkcCB8PiBmaWx0ZXIoaXNvMmMgJWluJSBJU08yQykgfD4gZHJvcF9uYShjbzJwY2FwKSB8PgogIGdncGxvdChhZXMoeWVhciwgY28ycGNhcCwgY29sID0gaXNvMmMpKSArIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gZXhwcmVzc2lvbihwYXN0ZShDT1syXSwgIiBwZXIgY2FwaXRhIG9mIHNldmVuIGNvbnV0cmllcyB3aXRoIGxhcmdlIEdEUCIpKSwKICAgICAgIHN1YnRpdGxlID0gIkNoaW5hLCBHZXJtYW55LCBGcmFuY2UsIFVuaXRlZCBLaW5nZG9tLCBJbmRpYSwgSmFwYW4sIFVuaXRlZCBTdGF0ZXMiLCAKICAgICAgIHkgPSBleHByZXNzaW9uKHBhc3RlKENPWzJdLCAiIHBlciBjYXBpdGEgaW4gdG9ucyIpKSkKYGBgCgojIyMjIEdEUCBwZXIgY2FwaXRhCgpgYGB7cn0KQ09VTlRSWSA8LSAiV29ybGQiCmRmX2NvMmdkcCB8PiBmaWx0ZXIoY291bnRyeSA9PSBDT1VOVFJZKSB8PiBkcm9wX25hKGdkcHBjYXApIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBnZHBwY2FwKSkgKyBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9ICJHRFAgcGVyIGNhcGl0YSBvZiB0aGUgV29ybGQiKQpgYGAKCmBgYHtyfQpJU08yQyA8LSBjKCJKUCIsICJDTiIsICJJRCIsICJHQiIsICJVUyIsICJERSIsICJGUiIpCmRmX2NvMmdkcCB8PiBmaWx0ZXIoaXNvMmMgJWluJSBJU08yQykgfD4gZHJvcF9uYShnZHBwY2FwKSB8PgogIGdncGxvdChhZXMoeWVhciwgZ2RwcGNhcCwgY29sID0gaXNvMmMpKSArIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIkdEUCBwZXIgY2FwaXRhIG9mIHNldmVuIGNvdW50cmllcyB3aXRoIGxhcmdlIEdEUCIsCiAgICAgICBzdWJ0aXRsZSA9ICJDaGluYSwgR2VybWFueSwgRnJhbmNlLCBVbml0ZWQgS2luZ2RvbSwgSW5kaWEsIEphcGFuLCBVbml0ZWQgU3RhdGVzIiwgCiAgICAgICB5ID0gIkdEUCBwZXIgY2FwaXRhIFBQUCIsCiAgICAgICBjYXB0aW9uID0gImNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCB1c2QiKQpgYGAKCiMjIyMgUmFua2luZyBvZiBDT34yfiBwZXIgY2FwaXRhCgpgYGB7cn0KZGZfY28yZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PgogIGRyb3BfbmEoY28ycGNhcCkgfD4gYXJyYW5nZShkZXNjKGNvMnBjYXApKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+CiAgZHJvcF9uYShjbzJwY2FwKSB8PiBhcnJhbmdlKGNvMnBjYXApCmBgYAoKKipPYnNlcnZhdGlvbnMgYW5kIFF1ZXN0aW9uczoqKgoKLSAgIFRvcCAxMCBjb3VudHJpZXMgb2YgQ09+Mn4gZW1pc3Npb24gcGVyIGNhcGl0YToKCiAgICAtICAgUXRhciwgQmFocmFpbiwgQnJ1bmVpIERhcnVzc2FsYW0sIEt1d2FpdCwgVW5pdGVkIEFyYWIgRW1pcmF0ZXMsIE9tYW4sIEF1c3RyYWxpYSwgU2F1ZGkgQXJhYmlhLCBDYW5hZGEsIGFuZCBVbml0ZWQgU3RhdGVzCgotICAgTG93ZXN0IDEwIGNvdW50cmllcyBvZiBDT34yfiBlbWlzc2lvbiBwZXIgY2FwaXRhOgoKICAgIC0gICBDb25nbywgRGVtLiBSZXAuLCBTb21hbGlhLCBDZW50cmFsIEFmcmljYW4gUmVwdWJsaWMsIEJ1cnVuZGksIE1hbGF3aSwgTmlnZXIsIENoYWQsIE1hZGFnYXNjYXIsIFJ3YW5kYSwgU2llcnJhIExlb25lCgojIyMjIFJhbmtpbmcgb2YgR0RQIHBlciBjYXBpdGEKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+CiAgZHJvcF9uYShnZHBwY2FwKSB8PiBhcnJhbmdlKGRlc2MoZ2RwcGNhcCkpCmBgYAoKYGBge3J9CmRmX2NvMmdkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4KICBkcm9wX25hKGdkcHBjYXApIHw+IGFycmFuZ2UoZ2RwcGNhcCkKYGBgCgojIyMgSGlzdG9ncmFtcyBhbmQgQm94cGxvdHMgZm9yIFZhcmlhdGlvbgoKIyMjIyBDT34yfiBwZXIgY2FwaXRhCgpgYGB7cn0KZGZfY28yZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGNvMnBjYXApIHw+IGZpbHRlcihpbmNvbWUgIT0gIk5vdCBjbGFzc2lmaWVkIikgfD4KICBnZ3Bsb3QoYWVzKGNvMnBjYXAsIGZpbGwgPSBmYWN0b3IoaW5jb21lLCBsZXZlbHMgPSBJTkNPTUUpKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTUsIGNvbCA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuMSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIENPMiBwZXIgY2FwaXRhIGluIDIwMjAiLCBmaWxsID0gIiIpCmBgYAoKYGBge3J9CmRmX2NvMmdkcCB8PiBmaWx0ZXIoeWVhciAlaW4lIGMoMTk5MCwgMjAwMCwgMjAxMCwgMjAyMCkpIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGNvMnBjYXApIHw+IGZpbHRlcihjbzJwY2FwID4gMCkgfD4gZmlsdGVyKGluY29tZSAhPSAiTm90IGNsYXNzaWZpZWQiKSB8PiAKICBnZ3Bsb3QoYWVzKGNvMnBjYXAsIGZpbGwgPSBmYWN0b3IoeWVhcikpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxNSwgY29sID0gImJsYWNrIiwgbGluZXdpZHRoID0gMC4xKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIGZhY2V0X3dyYXAofnllYXIpICsKICBsYWJzKHRpdGxlID0gIkhpc3RvZ3JhbSBvZiBDTzIgcGVyIGNhcGl0YSBpbiAxOTkwLCAyMDAwLCAyMDEwLCAyMDIwIiwgZmlsbCA9ICIiKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgJWluJSBjKDE5OTAsIDIwMDAsIDIwMTAsIDIwMjApKSB8PiBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShjbzJwY2FwKSB8PiBmaWx0ZXIoY28ycGNhcCA+IDApIHw+IGZpbHRlcihpbmNvbWUgIT0gIk5vdCBjbGFzc2lmaWVkIikgfD4gCiAgZ2dwbG90KGFlcyhjbzJwY2FwLCBmYWN0b3IoeWVhciksIGZpbGwgPSBmYWN0b3IoeWVhcikpKSArIAogIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsgbGFicyh5ID0gIiIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoY28ycGNhcCkgfD4gZmlsdGVyKGNvMnBjYXAgPiAwKSB8PiBmaWx0ZXIoaW5jb21lICE9ICJOb3QgY2xhc3NpZmllZCIpIHw+IAogIGdncGxvdChhZXMoY28ycGNhcCwgZmFjdG9yKGluY29tZSwgbGV2ZWxzID0gSU5DT01FKSwgZmlsbCA9IGluY29tZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkgKyAKICBsYWJzKHRpdGxlID0gIkNPMiBwZXIgY2FwaXRhIGJ5IGluY29tZSBsZXZlbCIsIHkgPSAiIiwgZmlsbCA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoY28ycGNhcCkgfD4gZmlsdGVyKGNvMnBjYXAgPiAwKSB8PiAKICBnZ3Bsb3QoYWVzKGNvMnBjYXAsIHJlZ2lvbiwgZmlsbCA9IHJlZ2lvbikpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkgKyAKICBsYWJzKHRpdGxlID0gIkNPMiBwZXIgY2FwaXRhIGJ5IHJlZ2lvbiIsIHkgPSAiIiwgZmlsbCA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIyMgR0RQIHBlciBjYXBpdGEKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwcGNhcCkgfD4gZmlsdGVyKGluY29tZSAhPSAiTm90IGNsYXNzaWZpZWQiKSB8PgogIGdncGxvdChhZXMoZ2RwcGNhcCwgZmlsbCA9IGZhY3RvcihpbmNvbWUsIGxldmVscyA9IElOQ09NRSkpKSArIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxNSwgY29sID0gImJsYWNrIiwgbGluZXdpZHRoID0gMC4xKSArIAogIHNjYWxlX3hfbG9nMTAoKSArCiAgbGFicyh0aXRsZSA9ICJIaXN0b2dyYW0gb2YgR0RQIHBlciBjYXBpdGEgaW4gMjAyMCIsIGZpbGwgPSAiIikKYGBgCgpgYGB7cn0KZGZfY28yZ2RwIHw+IGZpbHRlcih5ZWFyICVpbiUgYygxOTkwLCAyMDAwLCAyMDEwLCAyMDIwKSkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwcGNhcCkgfD4gZmlsdGVyKGdkcHBjYXAgPiAwKSB8PiBmaWx0ZXIoaW5jb21lICE9ICJOb3QgY2xhc3NpZmllZCIpIHw+IAogIGdncGxvdChhZXMoZ2RwcGNhcCwgZmlsbCA9IGZhY3Rvcih5ZWFyKSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDE1LCBjb2wgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgZmFjZXRfd3JhcCh+eWVhcikgKwogIGxhYnModGl0bGUgPSAiSGlzdG9ncmFtIG9mIEdEUCBwZXIgY2FwaXRhIGluIDE5OTAsIDIwMDAsIDIwMTAsIDIwMjAiLCBmaWxsID0gIiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3J9CmRmX2NvMmdkcCB8PiBmaWx0ZXIoeWVhciAlaW4lIGMoMTk5MCwgMjAwMCwgMjAxMCwgMjAyMCkpIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKGdkcHBjYXApIHw+IGZpbHRlcihnZHBwY2FwID4gMCkgfD4gZmlsdGVyKGluY29tZSAhPSAiTm90IGNsYXNzaWZpZWQiKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIGZhY3Rvcih5ZWFyKSwgZmlsbCA9IGZhY3Rvcih5ZWFyKSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkgKyBsYWJzKHkgPSAiIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKYGBge3J9CklOQ09NRSA8LSBjKCJMb3cgaW5jb21lIiwgIkxvdyAmIG1pZGRsZSBpbmNvbWUiLCAiTG93ZXIgbWlkZGxlIGluY29tZSIsICJNaWRkbGUgaW5jb21lIiwgIlVwcGVyIG1pZGRsZSBpbmNvbWUiLCAiSGlnaCBpbmNvbWUiKQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwcGNhcCkgfD4gZmlsdGVyKGdkcHBjYXAgPiAwKSB8PiBmaWx0ZXIoaW5jb21lICE9ICJOb3QgY2xhc3NpZmllZCIpIHw+IAogIGdncGxvdChhZXMoZ2RwcGNhcCwgZmFjdG9yKGluY29tZSwgbGV2ZWxzID0gSU5DT01FKSwgZmlsbCA9IGluY29tZSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkgKyAKICBsYWJzKHRpdGxlID0gIkdEUCBwZXIgY2FwaXRhIGJ5IGluY29tZSBsZXZlbCIsIHkgPSAiIiwgZmlsbCA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCmBgYHtyfQpkZl9jbzJnZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoZ2RwcGNhcCkgfD4gZmlsdGVyKGdkcHBjYXAgPiAwKSB8PiAKICBnZ3Bsb3QoYWVzKGdkcHBjYXAsIHJlZ2lvbiwgZmlsbCA9IHJlZ2lvbikpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkgKyAKICBsYWJzKHRpdGxlID0gIkdEUCBwZXIgY2FwaXRhIGJ5IHJlZ2lvbiIsIHkgPSAiIiwgZmlsbCA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCiMjIyBTY2F0dGVycGxvdCBmb3IgQ292YXJpYXRpb24KCiMjIyMgU2NhdHRlcnBsb3Qgd2l0aCBhIHJlZ3Jlc3Npb24gbGluZQoKYGBge3J9CmRmX2NvMmdkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiAKICBkcm9wX25hKGdkcHBjYXAsIGNvMnBjYXApIHw+CiAgZ2dwbG90KGFlcyhnZHBwY2FwLCBjbzJwY2FwKSkgKyBnZW9tX3BvaW50KGFlcyhjb2wgPSByZWdpb24pKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9ICd5fngnLCBzZSA9IEZBTFNFKSArCiAgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpICsKICBsYWJzKHRpdGxlID0gIkdEUCBwZXIgY2FwaXRhIHZzIENPMiBwZXIgY2FwaXRhIiwKICAgICAgIHggPSAiR0RQIHBlciBjYXBpdGEiLAogICAgICAgeSA9IGV4cHJlc3Npb24ocGFzdGUoQ09bMl0sICIgcGVyIGNhcGl0YSBpbiB0b25zIikpKQpgYGAKCiMjIyMgU3VtbWFyeSBvZiBhIGxpbmVhciBtb2RlbAoKYGBge3J9CmRmX2NvMmdkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBkcm9wX25hKGdkcHBjYXAsIGNvMnBjYXApIHw+CiAgbG0obG9nMTAoY28ycGNhcCl+bG9nMTAoZ2RwcGNhcCksIGRhdGEgPSBfKSB8PiBzdW1tYXJ5KCkKYGBgCgojIyBTY2hvb2wgRW5yb2xsbWVudCB2cyBHRFAgUGVyIENhcGl0YQoKIyMjIEluZGV4IFNlYXJjaAoKYGBge3J9CldESXNlYXJjaChzdHJpbmcgPSAic2Nob29sIGVucm9sbG1lbnQuKiglIGdyb3NzKSIsIGZpZWxkID0gIm5hbWUiLCBzaG9ydCA9IEZBTFNFKQpgYGAKCjEuICBTY2hvb2wgZW5yb2xsbWVudCwgc2Vjb25kYXJ5ICglIGdyb3NzKTogU0UuU0VDLkVOUlIKCi0gICBTY2hvb2wgZW5yb2xsbWVudCwgc2Vjb25kYXJ5ICglIGdyb3NzKSBHcm9zcyBlbnJvbGxtZW50IHJhdGlvIGlzIHRoZSByYXRpbyBvZiB0b3RhbCBlbnJvbGxtZW50LCByZWdhcmRsZXNzIG9mIGFnZSwgdG8gdGhlIHBvcHVsYXRpb24gb2YgdGhlIGFnZSBncm91cCB0aGF0IG9mZmljaWFsbHkgY29ycmVzcG9uZHMgdG8gdGhlIGxldmVsIG9mIGVkdWNhdGlvbiBzaG93bi4gU2Vjb25kYXJ5IGVkdWNhdGlvbiBjb21wbGV0ZXMgdGhlIHByb3Zpc2lvbiBvZiBiYXNpYyBlZHVjYXRpb24gdGhhdCBiZWdhbiBhdCB0aGUgcHJpbWFyeSBsZXZlbCwgYW5kIGFpbXMgYXQgbGF5aW5nIHRoZSBmb3VuZGF0aW9ucyBmb3IgbGlmZWxvbmcgbGVhcm5pbmcgYW5kIGh1bWFuIGRldmVsb3BtZW50LCBieSBvZmZlcmluZyBtb3JlIHN1YmplY3QtIG9yIHNraWxsLW9yaWVudGVkIGluc3RydWN0aW9uIHVzaW5nIG1vcmUgc3BlY2lhbGl6ZWQgdGVhY2hlcnMuIFNFLlNFQy5FTlJSCgoyLiAgU2Nob29sIGVucm9sbG1lbnQsIHRlcnRpYXJ5ICglIGdyb3NzKTogU0UuVEVSLkVOUlIKCi0gICBHcm9zcyBlbnJvbGxtZW50IHJhdGlvIGlzIHRoZSByYXRpbyBvZiB0b3RhbCBlbnJvbGxtZW50LCByZWdhcmRsZXNzIG9mIGFnZSwgdG8gdGhlIHBvcHVsYXRpb24gb2YgdGhlIGFnZSBncm91cCB0aGF0IG9mZmljaWFsbHkgY29ycmVzcG9uZHMgdG8gdGhlIGxldmVsIG9mIGVkdWNhdGlvbiBzaG93bi4gVGVydGlhcnkgZWR1Y2F0aW9uLCB3aGV0aGVyIG9yIG5vdCB0byBhbiBhZHZhbmNlZCByZXNlYXJjaCBxdWFsaWZpY2F0aW9uLCBub3JtYWxseSByZXF1aXJlcywgYXMgYSBtaW5pbXVtIGNvbmRpdGlvbiBvZiBhZG1pc3Npb24sIHRoZSBzdWNjZXNzZnVsIGNvbXBsZXRpb24gb2YgZWR1Y2F0aW9uIGF0IHRoZSBzZWNvbmRhcnkgbGV2ZWwuCgozLiAgR0RQIHBlciBjYXBpdGEsIFBQUCAoY29uc3RhbnQgMjAxNyBpbnRlcm5hdGlvbmFsIFwkKTogTlkuR0RQLlBDQVAuUFAuS0QKCi0gICBHRFAgcGVyIGNhcGl0YSwgUFBQIChjb25zdGFudCAyMDE3IGludGVybmF0aW9uYWwgXCQpIEdEUCBwZXIgY2FwaXRhIGJhc2VkIG9uIHB1cmNoYXNpbmcgcG93ZXIgcGFyaXR5IChQUFApLiBQUFAgR0RQIGlzIGdyb3NzIGRvbWVzdGljIHByb2R1Y3QgY29udmVydGVkIHRvIGludGVybmF0aW9uYWwgZG9sbGFycyB1c2luZyBwdXJjaGFzaW5nIHBvd2VyIHBhcml0eSByYXRlcy4gQW4gaW50ZXJuYXRpb25hbCBkb2xsYXIgaGFzIHRoZSBzYW1lIHB1cmNoYXNpbmcgcG93ZXIgb3ZlciBHRFAgYXMgdGhlIFUuUy4gZG9sbGFyIGhhcyBpbiB0aGUgVW5pdGVkIFN0YXRlcy4gR0RQIGF0IHB1cmNoYXNlcidzIHByaWNlcyBpcyB0aGUgc3VtIG9mIGdyb3NzIHZhbHVlIGFkZGVkIGJ5IGFsbCByZXNpZGVudCBwcm9kdWNlcnMgaW4gdGhlIGNvdW50cnkgcGx1cyBhbnkgcHJvZHVjdCB0YXhlcyBhbmQgbWludXMgYW55IHN1YnNpZGllcyBub3QgaW5jbHVkZWQgaW4gdGhlIHZhbHVlIG9mIHRoZSBwcm9kdWN0cy4gSXQgaXMgY2FsY3VsYXRlZCB3aXRob3V0IG1ha2luZyBkZWR1Y3Rpb25zIGZvciBkZXByZWNpYXRpb24gb2YgZmFicmljYXRlZCBhc3NldHMgb3IgZm9yIGRlcGxldGlvbiBhbmQgZGVncmFkYXRpb24gb2YgbmF0dXJhbCByZXNvdXJjZXMuIERhdGEgYXJlIGluIGNvbnN0YW50IDIwMTcgaW50ZXJuYXRpb25hbCBkb2xsYXJzLiBJRDogTlkuR0RQLlBDQVAuUFAuS0QKCiMjIyBJbXBvcnRpbmcgRGF0YQoKYGBge3IgY2FjaGUgPSBUUlVFfQpkZl9zZWNfdGVyX2dkcCA8LSBXREkoaW5kaWNhdG9yID0gYyhzZWMgPSAiU0UuU0VDLkVOUlIiLCB0ZXIgPSAiU0UuVEVSLkVOUlIiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ2RwcGNhcCA9ICJOWS5HRFAuUENBUC5QUC5LRCIpLCBleHRyYSA9IFRSVUUpCmBgYAoKYGBge3J9CndyaXRlX2NzdihkZl9zZWNnZHAsICJkYXRhL3NlY190ZXJfZ2RwLmNzdiIpCmBgYAoKYGBge3J9CmRmX3NlY2dkcCA8LSByZWFkX2NzdigiZGF0YS9zZWNfdGVyX2dkcC5jc3YiKQpgYGAKCiMjIyBWaXN1YWxpemF0aW9uIGJ5IExpbmUgR3JhcGhzCgpgYGB7cn0KQ09VTlRSWSA8LSAiV29ybGQiCmRmX3NlY190ZXJfZ2RwIHw+IGZpbHRlcihjb3VudHJ5ID09IENPVU5UUlkpIHw+IGRyb3BfbmEoc2VjLCB0ZXIpIHw+CiAgZ2dwbG90KCkgKyBnZW9tX2xpbmUoYWVzKHllYXIsIHNlYyksIGNvbCA9ICJibHVlIikgKyBnZW9tX2xpbmUoYWVzKHllYXIsIHRlciksIGNvbCA9ICJyZWQiKSArCiAgbGFicyh0aXRsZSA9ICJTY2hvb2wgZW5yb2xsbWVudDsgU2Vjb25kYXJ5IGFuZCBUZXJ0aWFyeSIsIAogICAgICAgc3VidGl0bGUgPSAic2Vjb25kYXJ5IGluIGJsdWUgYW5kIHRlcnRpYXJ5IGluIHJlZCIsIHkgPSAiIikKYGBgCgpgYGB7cn0KSU5DT01FIDwtIGMoIkxvdyBpbmNvbWUiLCAiTG93ICYgbWlkZGxlIGluY29tZSIsICJMb3dlciBtaWRkbGUgaW5jb21lIiwgIk1pZGRsZSBpbmNvbWUiLCAiVXBwZXIgbWlkZGxlIGluY29tZSIsICJIaWdoIGluY29tZSIpCmRmX3NlY190ZXJfZ2RwIHw+IGZpbHRlcihjb3VudHJ5ICVpbiUgSU5DT01FKSB8PiBkcm9wX25hKHNlYywgdGVyKSB8PgogIGdncGxvdChhZXMobGluZXR5cGUgPSBmYWN0b3IoY291bnRyeSwgbGV2ZWxzID0gSU5DT01FKSkpICsgZ2VvbV9saW5lKGFlcyh5ZWFyLCBzZWMpLCBjb2wgPSAiYmx1ZSIpICsgZ2VvbV9saW5lKGFlcyh5ZWFyLCB0ZXIpLCBjb2wgPSAicmVkIikgKyB5bGltKGMoMCwxMTApKSArCiAgbGFicyh0aXRsZSA9ICJTY2hvb2wgZW5yb2xsbWVudDsgU2Vjb25kYXJ5IGFuZCBUZXJ0aWFyeSIsIAogICAgICAgc3VidGl0bGUgPSAic2Vjb25kYXJ5IGluIGJsdWUgYW5kIHRlcnRpYXJ5IGluIHJlZCIsIGxpbmV0eXBlID0gIkluY29tZSBMZXZlbHMiLCB5ID0gIiIpCmBgYAoKIyMjIFNjYXR0ZXJwbG90IGZvciBDb3ZhcmlhdGlvbgoKYGBge3J9CmRmX3NlY190ZXJfZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoc2VjLCB0ZXIsIGdkcHBjYXApIHw+CiAgZ2dwbG90KCkgKyBnZW9tX3BvaW50KGFlcyhnZHBwY2FwLCBzZWMpLCBjb2wgPSAiYmx1ZSIpICsgCiAgZ2VvbV9wb2ludChhZXMoZ2RwcGNhcCwgdGVyKSwgY29sID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIlNjaG9vbCBlbnJvbGxtZW50OyBTZWNvbmRhcnkgYW5kIFRlcnRpYXJ5IHZzIEdEUCBwZXIgY2FwaXRhIiwgCiAgICAgICBzdWJ0aXRsZSA9ICJzZWNvbmRhcnkgaW4gYmx1ZSBhbmQgdGVydGlhcnkgaW4gcmVkIiwgeSA9ICIiKQpgYGAKCmBgYHtyfQpkZl9zZWNfdGVyX2dkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBkcm9wX25hKHNlYywgdGVyLCBnZHBwY2FwKSB8PgogIGdncGxvdCgpICsgZ2VvbV9wb2ludChhZXMoZ2RwcGNhcCwgc2VjKSwgY29sID0gImJsdWUiKSArIAogIGdlb21fcG9pbnQoYWVzKGdkcHBjYXAsIHRlciksIGNvbCA9ICJyZWQiKSArIAogIHNjYWxlX3hfbG9nMTAoKSArCiAgbGFicyh0aXRsZSA9ICJTY2hvb2wgZW5yb2xsbWVudDsgU2Vjb25kYXJ5IGFuZCBUZXJ0aWFyeSB2cyBHRFAgcGVyIGNhcGl0YSBpbiBsb2cxMCBzY2FsZSIsIAogICAgICAgc3VidGl0bGUgPSAic2Vjb25kYXJ5IGluIGJsdWUgYW5kIHRlcnRpYXJ5IGluIHJlZCIsIHkgPSAiIikKYGBgCgpgYGB7cn0KZGZfc2VjX3Rlcl9nZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShzZWMsIHRlciwgZ2RwcGNhcCkgfD4KICBnZ3Bsb3QoKSArIGdlb21fcG9pbnQoYWVzKGdkcHBjYXAsIHNlYyksIGNvbCA9ICJibHVlIikgKyAKICBnZW9tX3BvaW50KGFlcyhnZHBwY2FwLCB0ZXIpLCBjb2wgPSAicmVkIikgKwogIGdlb21fc21vb3RoKGFlcyhnZHBwY2FwLCBzZWMpLCBjb2wgPSAiYmx1ZSIsIG1ldGhvZCA9ICJsbSIsIGZvcm11bGEgPSAneX54Jywgc2UgPSBGQUxTRSkgKwogIGdlb21fc21vb3RoKGFlcyhnZHBwY2FwLCB0ZXIpLCBjb2wgPSAicmVkIiwgbWV0aG9kID0gImxtIiwgZm9ybXVsYSA9ICd5fngnLCBzZSA9IEZBTFNFKSArCiAgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKHRpdGxlID0gIlNjaG9vbCBlbnJvbGxtZW50OyBTZWNvbmRhcnkgYW5kIFRlcnRpYXJ5IHZzIEdEUCBwZXIgY2FwaXRhIGluIGxvZzEwIHNjYWxlIiwgCiAgICAgICBzdWJ0aXRsZSA9ICJzZWNvbmRhcnkgaW4gYmx1ZSBhbmQgdGVydGlhcnkgaW4gcmVkIHdpdGggcmVncmVzc2lvbiBsaW5lcyIsIHkgPSAiIikKYGBgCgpgYGB7cn0KZGZfc2VjX3Rlcl9nZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZHJvcF9uYShnZHBwY2FwLCBzZWMpIHw+CiAgbG0oc2VjfmxvZzEwKGdkcHBjYXApLCBkYXRhID0gXykgfD4gc3VtbWFyeSgpCmBgYAoKYGBge3J9CmRmX3NlY190ZXJfZ2RwIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGRyb3BfbmEoZ2RwcGNhcCwgdGVyKSB8PgogIGxtKHRlcn5sb2cxMChnZHBwY2FwKSwgZGF0YSA9IF8pIHw+IHN1bW1hcnkoKQpgYGAKCmBgYHtyfQpkZl9zZWNfdGVyX2dkcCB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwLCByZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKHNlYywgcmVnaW9uKSB8PgogIGdncGxvdChhZXMoc2VjLCByZWdpb24sIGZpbGwgPSByZWdpb24pKSArIGdlb21fYm94cGxvdCgpICsgCiAgbGFicyh4ID0gIlNjaG9vbCBlbnJvbGxtZW50LCBzZWNvbmRhcnkgKCUgZ3Jvc3MpIiwgeSA9ICIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpgYGB7cn0KZGZfc2VjX3Rlcl9nZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCwgaW5jb21lICE9IkFnZ3JlZ2F0ZXMiKSB8PiBkcm9wX25hKHNlYywgaW5jb21lKSB8PgogIGdncGxvdChhZXMoc2VjLCBmYWN0b3IoaW5jb21lLCBsZXZlbHMgPSBJTkNPTUUpLCBmaWxsID0gaW5jb21lKSkgKyBnZW9tX2JveHBsb3QoKSArIAogIGxhYnModGl0bGUgPSAiU2Vjb25hcnkgZWR1Y2F0aW9uOiBTY2hvb2wgZW5yb2xsbWVudCBieSBpbmNvbWUgbGV2ZWwiLCB4ID0gIlNjaG9vbCBlbnJvbGxtZW50LCBzZWNvbmRhcnkgKCUgZ3Jvc3MpIiwgeSA9ICIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpgYGB7cn0KZGZfc2VjX3Rlcl9nZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCwgcmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYSh0ZXIsIHJlZ2lvbikgfD4KICBnZ3Bsb3QoYWVzKHRlciwgcmVnaW9uLCBmaWxsID0gcmVnaW9uKSkgKyBnZW9tX2JveHBsb3QoKSArIAogIGxhYnMoeCA9ICJTY2hvb2wgZW5yb2xsbWVudCwgdGVydGlhcnkgKCUgZ3Jvc3MpIiwgeSA9ICIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpgYGB7cn0KZGZfc2VjX3Rlcl9nZHAgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCwgaW5jb21lICE9ICJBZ2dyZWdhdGVzIikgfD4gZHJvcF9uYSh0ZXIsIGluY29tZSkgfD4KICBnZ3Bsb3QoYWVzKHRlciwgZmFjdG9yKGluY29tZSwgbGV2ZWxzID0gSU5DT01FKSwgZmlsbCA9IGluY29tZSkpICsgZ2VvbV9ib3hwbG90KCkgKyAKICBsYWJzKHRpdGxlID0gIlRlcnRpYXJ5IGVkdWNhdGlvbjogU2Nob29sIGVucm9sbG1lbnQgYnkgaW5jb21lIGxldmVsIiwgeCA9ICJTY2hvb2wgZW5yb2xsbWVudCwgdGVydGlhcnkgKCUgZ3Jvc3MpIiwgeSA9ICIiKSArIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoqKk9ic2VydmF0aW9ucyoqCgotICAgSW5jb21lIGxldmVsIGhhcyBtb3JlIGVmZmVjdCBvbiBzY2hvb2wgZW5yb2xsbWVudCB0byB0ZXJ0aWFyeSBlZHVjYXRpb24KCiMgWW91ciBQcm9qZWN0CgojIyBUaXRsZSBvZiB5b3VyIHByb2plY3QKCldlIHN0dWR5IC4uLi4uCgoxLiAgTmFtZSBvZiB0aGUgaW5kaWNhdG9yIDE6IEluZGljYXRvciBDb2RlIDEKCi0gICBEZXNjcmlwdGlvbjoKCjIuICBOYW1lIG9mIHRoZSBpbmRpY2F0b3IgMjogSW5kaWNhdG9yIENvZGUgMgoKLSAgIERlc2NyaXB0aW9uOgoKIyMjIEltcG9ydGluZyBEYXRhCgpgYGB7PWh0bWx9CjxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7Ij5FZGl0IHRoZSBmb2xsb3dpbmcgY29kZSBjaHVuayE8L3NwYW4+CmBgYApgYGB7cn0KY2hvc2VuX2luZGljYXRvcl8xIDwtICJTRS5TRUMuRU5SUiIKc2hvcnRfbmFtZV8xIDwtICJzZWMiCmNob3Nlbl9pbmRpY2F0b3JfMiA8LSAiTlkuR0RQLlBDQVAuUFAuS0QiCnNob3J0X25hbWVfMiA8LSAiZ2RwcGNhcCIKYGBgCgpgYGB7ciBjYWNoZSA9IFRSVUV9CmRmX3lvdXJkYXRhIDwtIFdESShpbmRpY2F0b3IgPSBjKHNob3J0X25hbWVfMSA9IGNob3Nlbl9pbmRpY2F0b3JfMSwgc2hvcnRfbmFtZV8yID0gY2hvc2VuX2luZGljYXRvcl8yKSwKICAgICAgICAgICAgICAgICBleHRyYSA9IFRSVUUpCmBgYAoKYGBge3J9CndyaXRlX2NzdihkZl95b3VyZGF0YSwgImRhdGEveW91cmRhdGEuY3N2IikKYGBgCgpgYGB7cn0KZGZfeW91cmRhdGEgPC0gcmVhZF9jc3YoImRhdGEveW91cmRhdGEuY3N2IikKYGBgCgojIyMgVmlzdWFsaXphdGlvbiBieSBMaW5lIEdyYXBocwoKIyMjIyBOYW1lIG9mIHRoZSBJbmRpY2F0b3IgMQoKYGBgez1odG1sfQo8c3BhbiBzdHlsZSA9ICJjb2xvcjogcmVkOyI+RWRpdCB0aGUgdGl0bGUgYW5kIHRoZSBsYWJlbCBvZiB5LWF4aXMuPC9zcGFuPgpgYGAKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoY291bnRyeSA9PSBDT1VOVFJZKSB8PiBkcm9wX25hKHNob3J0X25hbWVfMSkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIHNob3J0X25hbWVfMSkpICsgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiIiwKICAgICAgIHkgPSAiIikKYGBgCgoqKk9ic2VydmF0aW9ucyBhbmQgUXVlc3Rpb25zOioqCgotICAgCgpgYGB7PWh0bWx9CjxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7Ij5FZGl0IElTTzJDLCB0aXRsZSwgc3VidGl0bGUsIGFuZCB0aGUgbGFiZWwgb2YgeS1heGlzLjwvc3Bhbj4KYGBgCmBgYHtyfQpJU08yQyA8LSBjKCJKUCIsICJDTiIsICJJRCIsICJHQiIsICJVUyIsICJERSIsICJGUiIpCmRmX3lvdXJkYXRhIHw+IGZpbHRlcihpc28yYyAlaW4lIElTTzJDKSB8PiBkcm9wX25hKHNob3J0X25hbWVfMSkgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIHNob3J0X25hbWVfMSwgY29sID0gaXNvMmMpKSArIGdlb21fbGluZSgpICsKICBsYWJzKHRpdGxlID0gIiIsCiAgICAgICBzdWJ0aXRsZSA9ICJDaGluYSwgR2VybWFueSwgRnJhbmNlLCBVbml0ZWQgS2luZ2RvbSwgSW5kaWEsIEphcGFuLCBVbml0ZWQgU3RhdGVzIiwgCiAgICAgICB5ID0gIiIpCmBgYAoKKipPYnNlcnZhdGlvbnMgYW5kIFF1ZXN0aW9uczoqKgoKLSAgIAoKIyMjIyBOYW1lIG9mIHRoZSBJbmRpY2F0b3IgMgoKYGBgez1odG1sfQo8c3BhbiBzdHlsZSA9ICJjb2xvcjogcmVkOyI+RWRpdCBDT1VOVFJZIGFuZCB0aGUgdGl0bGUuPC9zcGFuPgpgYGAKYGBge3J9CkNPVU5UUlkgPC0gIldvcmxkIgpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoY291bnRyeSA9PSBDT1VOVFJZKSB8PiBkcm9wX25hKHNob3J0X25hbWVfMikgfD4KICBnZ3Bsb3QoYWVzKHllYXIsIHNob3J0X25hbWVfMikpICsgZ2VvbV9saW5lKCkgKwogIGxhYnModGl0bGUgPSAiIikKYGBgCgoqKk9ic2VydmF0aW9ucyBhbmQgUXVlc3Rpb25zOioqCgotICAgCgpgYGB7PWh0bWx9CjxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7Ij5FZGl0IElTTzJDLCB0aXRsZSwgc3VidGl0bGUsIGFuZCB0aGUgbGFiZWwgb2YgeS1heGlzLCBhbmQgYWRkIGNhcHRpb24gaWYgcHJlZmVyYWJsZS48L3NwYW4+CmBgYApgYGB7cn0KSVNPMkMgPC0gYygiSlAiLCAiQ04iLCAiSUQiLCAiR0IiLCAiVVMiLCAiREUiLCAiRlIiKQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoaXNvMmMgJWluJSBJU08yQykgfD4gZHJvcF9uYShzaG9ydF9uYW1lXzIpIHw+CiAgZ2dwbG90KGFlcyh5ZWFyLCBzaG9ydF9uYW1lXzIsIGNvbCA9IGlzbzJjKSkgKyBnZW9tX2xpbmUoKSArCiAgbGFicyh0aXRsZSA9ICIiLAogICAgICAgc3VidGl0bGUgPSAiQ2hpbmEsIEdlcm1hbnksIEZyYW5jZSwgVW5pdGVkIEtpbmdkb20sIEluZGlhLCBKYXBhbiwgVW5pdGVkIFN0YXRlcyIsIAogICAgICAgeSA9ICIiLAogICAgICAgY2FwdGlvbiA9ICIiKQpgYGAKCioqT2JzZXJ2YXRpb25zIGFuZCBRdWVzdGlvbnM6KioKCi0gICAKCiMjIyMgUmFua2luZyBvZiB0aGUgaW5kaWNhdG9yIDEKCmBgYHs9aHRtbH0KPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsiPkVkaXQgeWVhciBpZiBuZWNlc3NhcnkuPC9zcGFuPgpgYGAKYGBge3J9CmRmX3lvdXJkYXRhIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PgogIGRyb3BfbmEoc2hvcnRfbmFtZV8xKSB8PiBhcnJhbmdlKGRlc2Moc2hvcnRfbmFtZV8xKSkKYGBgCgpgYGB7cn0KZGZfeW91cmRhdGEgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+CiAgZHJvcF9uYShzaG9ydF9uYW1lXzEpIHw+IGFycmFuZ2Uoc2hvcnRfbmFtZV8xKQpgYGAKCioqT2JzZXJ2YXRpb25zIGFuZCBRdWVzdGlvbnM6KioKCi0gICAKCiMjIyMgUmFua2luZyBvZiB0aGUgSW5kaWNhdG9yIDIKCmBgYHtyfQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4KICBkcm9wX25hKHNob3J0X25hbWVfMikgfD4gYXJyYW5nZShkZXNjKHNob3J0X25hbWVfMikpCmBgYAoKKipPYnNlcnZhdGlvbnMgYW5kIFF1ZXN0aW9uczoqKgoKLSAgIAoKYGBge3J9CmRmX3lvdXJkYXRhIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PgogIGRyb3BfbmEoc2hvcnRfbmFtZV8yKSB8PiBhcnJhbmdlKHNob3J0X25hbWVfMikKYGBgCgoqKk9ic2VydmF0aW9ucyBhbmQgUXVlc3Rpb25zOioqCgotICAgCgojIyMgSGlzdG9ncmFtcyBhbmQgQm94cGxvdHMgZm9yIFZhcmlhdGlvbgoKIyMjIyBOYW1lIG9mIHRoZSBJbmRpY2F0b3IgMQoKYGBgez1odG1sfQo8c3BhbiBzdHlsZSA9ICJjb2xvcjogcmVkOyI+RWRpdCB0aGUgdGl0bGUgYW5kIHllYXIuPC9zcGFuPgpgYGAKYGBge3J9CmRmX3lvdXJkYXRhIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKHNob3J0X25hbWVfMSkgfD4gZmlsdGVyKGluY29tZSAhPSAiTm90IGNsYXNzaWZpZWQiKSB8PgogIGdncGxvdChhZXMoc2hvcnRfbmFtZV8xLCBmaWxsID0gZmFjdG9yKGluY29tZSwgbGV2ZWxzID0gSU5DT01FKSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDE1LCBjb2wgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsKICBsYWJzKHRpdGxlID0gIiIsIGZpbGwgPSAiIikKYGBgCgoqKk9ic2VydmF0aW9ucyBhbmQgUXVlc3Rpb25zOioqCgotICAgCgpgYGB7PWh0bWx9CjxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7Ij5FZGl0IHRoZSB0aXRsZSBhbmQgdGhlIHllYXJzLjwvc3Bhbj4KYGBgCmBgYHtyfQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoeWVhciAlaW4lIGMoMTk5MCwgMjAwMCwgMjAxMCwgMjAyMCkpIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKHNob3J0X25hbWVfMSkgfD4gZmlsdGVyKHNob3J0X25hbWVfMSA+IDApIHw+IGZpbHRlcihpbmNvbWUgIT0gIk5vdCBjbGFzc2lmaWVkIikgfD4gCiAgZ2dwbG90KGFlcyhzaG9ydF9uYW1lXzEsIGZpbGwgPSBmYWN0b3IoeWVhcikpKSArIAogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAxNSwgY29sID0gImJsYWNrIiwgbGluZXdpZHRoID0gMC4xKSArIAogIHNjYWxlX3hfbG9nMTAoKSArIGZhY2V0X3dyYXAofnllYXIpICsKICBsYWJzKHRpdGxlID0gIiIsIGZpbGwgPSAiIikKYGBgCgoqKk9ic2VydmF0aW9ucyBhbmQgUXVlc3Rpb25zOioqCgotICAgCgpgYGB7cn0KZGZfeW91cmRhdGEgfD4gZmlsdGVyKHllYXIgJWluJSBjKDE5OTAsIDIwMDAsIDIwMTAsIDIwMjApKSB8PiBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShzaG9ydF9uYW1lXzEpIHw+IGZpbHRlcihzaG9ydF9uYW1lXzEgPiAwKSB8PiBmaWx0ZXIoaW5jb21lICE9ICJOb3QgY2xhc3NpZmllZCIpIHw+IAogIGdncGxvdChhZXMoc2hvcnRfbmFtZV8xLCBmYWN0b3IoeWVhciksIGZpbGwgPSBmYWN0b3IoeWVhcikpKSArIAogIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsgbGFicyh5ID0gIiIpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCioqT2JzZXJ2YXRpb25zIGFuZCBRdWVzdGlvbnM6KioKCi0gICAKCmBgYHs9aHRtbH0KPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsiPkVkaXQgdGhlIHRpdGxlLCBhbmQgdGhlIHllYXIgaWYgbmVjZXNzYXJ5Ljwvc3Bhbj4KYGBgCmBgYHtyfQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShzaG9ydF9uYW1lXzEpIHw+IGZpbHRlcihzaG9ydF9uYW1lXzEgPiAwKSB8PiBmaWx0ZXIoaW5jb21lICE9ICJOb3QgY2xhc3NpZmllZCIpIHw+IAogIGdncGxvdChhZXMoc2hvcnRfbmFtZV8xLCBmYWN0b3IoaW5jb21lLCBsZXZlbHMgPSBJTkNPTUUpLCBmaWxsID0gaW5jb21lKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIHNjYWxlX3hfbG9nMTAoKSArIAogIGxhYnModGl0bGUgPSAiIiwgeSA9ICIiLCBmaWxsID0gIiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKKipPYnNlcnZhdGlvbnMgYW5kIFF1ZXN0aW9uczoqKgoKLSAgIAoKYGBgez1odG1sfQo8c3BhbiBzdHlsZSA9ICJjb2xvcjogcmVkOyI+RWRpdCB0aGUgdGl0bGUgYW5kIHllYXIgaWYgbmVjZXNzYXJ5Ljwvc3Bhbj4KYGBgCmBgYHtyfQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShzaG9ydF9uYW1lXzEpIHw+IGZpbHRlcihzaG9ydF9uYW1lXzEgPiAwKSB8PiAKICBnZ3Bsb3QoYWVzKHNob3J0X25hbWVfMSwgcmVnaW9uLCBmaWxsID0gcmVnaW9uKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIHNjYWxlX3hfbG9nMTAoKSArIAogIGxhYnModGl0bGUgPSAiIiwgeSA9ICIiLCBmaWxsID0gIiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKKipPYnNlcnZhdGlvbnMgYW5kIFF1ZXN0aW9uczoqKgoKLSAgIAoKIyMjIyBHRFAgcGVyIGNhcGl0YQoKYGBgez1odG1sfQo8c3BhbiBzdHlsZSA9ICJjb2xvcjogcmVkOyI+RWRpdCB0aGUgdGl0bGUsIGFuZCB5ZWFyIGlmIG5lY2Vzc2FyeS48L3NwYW4+CmBgYApgYGB7cn0KZGZfeW91cmRhdGEgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoc2hvcnRfbmFtZV8yKSB8PiBmaWx0ZXIoaW5jb21lICE9ICJOb3QgY2xhc3NpZmllZCIpIHw+CiAgZ2dwbG90KGFlcyhzaG9ydF9uYW1lXzIsIGZpbGwgPSBmYWN0b3IoaW5jb21lLCBsZXZlbHMgPSBJTkNPTUUpKSkgKyBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMTUsIGNvbCA9ICJibGFjayIsIGxpbmV3aWR0aCA9IDAuMSkgKyAKICBzY2FsZV94X2xvZzEwKCkgKwogIGxhYnModGl0bGUgPSAiIiwgZmlsbCA9ICIiKQpgYGAKCmBgYHs9aHRtbH0KPHNwYW4gc3R5bGUgPSAiY29sb3I6IHJlZDsiPkVkaXQgdGhlIHRpdGxlIGFuZCB0aGUgeWVhciBpZiBuZWNlc3NhcnkuPC9zcGFuPgpgYGAKYGBge3J9CmRmX3lvdXJkYXRhIHw+IGZpbHRlcih5ZWFyICVpbiUgYygxOTkwLCAyMDAwLCAyMDEwLCAyMDIwKSkgfD4gZmlsdGVyKHJlZ2lvbiAhPSAiQWdncmVnYXRlcyIpIHw+IAogIGRyb3BfbmEoc2hvcnRfbmFtZV8yKSB8PiBmaWx0ZXIoc2hvcnRfbmFtZV8yID4gMCkgfD4gZmlsdGVyKGluY29tZSAhPSAiTm90IGNsYXNzaWZpZWQiKSB8PiAKICBnZ3Bsb3QoYWVzKHNob3J0X25hbWVfMiwgZmlsbCA9IGZhY3Rvcih5ZWFyKSkpICsgCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDE1LCBjb2wgPSAiYmxhY2siLCBsaW5ld2lkdGggPSAwLjEpICsgCiAgc2NhbGVfeF9sb2cxMCgpICsgZmFjZXRfd3JhcCh+eWVhcikgKwogIGxhYnModGl0bGUgPSAiIiwgZmlsbCA9ICIiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQpgYGAKCioqT2JzZXJ2YXRpb25zIGFuZCBRdWVzdGlvbnM6KioKCi0gICAKCmBgYHtyfQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoeWVhciAlaW4lIGMoMTk5MCwgMjAwMCwgMjAxMCwgMjAyMCkpIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKHNob3J0X25hbWVfMikgfD4gZmlsdGVyKHNob3J0X25hbWVfMiA+IDApIHw+IGZpbHRlcihpbmNvbWUgIT0gIk5vdCBjbGFzc2lmaWVkIikgfD4gCiAgZ2dwbG90KGFlcyhzaG9ydF9uYW1lXzIsIGZhY3Rvcih5ZWFyKSwgZmlsbCA9IGZhY3Rvcih5ZWFyKSkpICsgCiAgZ2VvbV9ib3hwbG90KCkgKyBzY2FsZV94X2xvZzEwKCkgKyBsYWJzKHkgPSAiIikgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKKipPYnNlcnZhdGlvbnMgYW5kIFF1ZXN0aW9uczoqKgoKLSAgIAoKYGBgez1odG1sfQo8c3BhbiBzdHlsZSA9ICJjb2xvcjogcmVkOyI+RWRpdCB0aGUgdGl0bGUsIGFuZCB0aGUgeWVhciBpZiBuZWNlc3NhcnkuPC9zcGFuPgpgYGAKYGBge3J9CmRmX3lvdXJkYXRhIHw+IGZpbHRlcih5ZWFyID09IDIwMjApIHw+IGZpbHRlcihyZWdpb24gIT0gIkFnZ3JlZ2F0ZXMiKSB8PiAKICBkcm9wX25hKHNob3J0X25hbWVfMikgfD4gZmlsdGVyKHNob3J0X25hbWVfMiA+IDApIHw+IGZpbHRlcihpbmNvbWUgIT0gIk5vdCBjbGFzc2lmaWVkIikgfD4gCiAgZ2dwbG90KGFlcyhzaG9ydF9uYW1lXzIsIGZhY3RvcihpbmNvbWUsIGxldmVscyA9IElOQ09NRSksIGZpbGwgPSBpbmNvbWUpKSArIAogIGdlb21fYm94cGxvdCgpICsgc2NhbGVfeF9sb2cxMCgpICsgCiAgbGFicyh0aXRsZSA9ICIiLCB5ID0gIiIsIGZpbGwgPSAiIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgoqKk9ic2VydmF0aW9ucyBhbmQgUXVlc3Rpb25zOioqCgotICAgCgpgYGB7PWh0bWx9CjxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7Ij5FZGl0IHRoZSB0aXRsZSBhbmQgdGhlIHllYXIgaWYgbmVjZXNzYXJ5Ljwvc3Bhbj4KYGBgCmBgYHtyfQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBmaWx0ZXIocmVnaW9uICE9ICJBZ2dyZWdhdGVzIikgfD4gCiAgZHJvcF9uYShzaG9ydF9uYW1lXzIpIHw+IGZpbHRlcihzaG9ydF9uYW1lXzIgPiAwKSB8PiAKICBnZ3Bsb3QoYWVzKHNob3J0X25hbWVfMiwgcmVnaW9uLCBmaWxsID0gcmVnaW9uKSkgKyAKICBnZW9tX2JveHBsb3QoKSArIHNjYWxlX3hfbG9nMTAoKSArIAogIGxhYnModGl0bGUgPSAiIiwgeSA9ICIiLCBmaWxsID0gIiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCmBgYAoKIyMjIFNjYXR0ZXJwbG90IGZvciBDb3ZhcmlhdGlvbgoKIyMjIyBTY2F0dGVycGxvdCB3aXRoIGEgcmVncmVzc2lvbiBsaW5lCgpgYGB7PWh0bWx9CjxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7Ij5FZGl0IHRoZSB0aXRsZSwgdGhlIGxhYmVscyBvZiB4LSBhbmQgeS0gYXhlcy48L3NwYW4+CmBgYApgYGB7cn0KZGZfeW91cmRhdGEgfD4gZmlsdGVyKHllYXIgPT0gMjAyMCkgfD4gCiAgZHJvcF9uYShzaG9ydF9uYW1lXzIsIHNob3J0X25hbWVfMSkgfD4KICBnZ3Bsb3QoYWVzKHNob3J0X25hbWVfMiwgc2hvcnRfbmFtZV8xKSkgKyBnZW9tX3BvaW50KGFlcyhjb2wgPSByZWdpb24pKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIiwgZm9ybXVsYSA9ICd5fngnLCBzZSA9IEZBTFNFKSArCiAgc2NhbGVfeF9sb2cxMCgpICsgc2NhbGVfeV9sb2cxMCgpICsKICBsYWJzKHRpdGxlID0gIiIsCiAgICAgICB4ID0gIiIsCiAgICAgICB5ID0gIiIpCmBgYAoKKipPYnNlcnZhdGlvbnMgYW5kIFF1ZXN0aW9uczoqKgoKLSAgIAoKIyMjIyBTdW1tYXJ5IG9mIGEgbGluZWFyIG1vZGVsCgpgYGB7PWh0bWx9CjxzcGFuIHN0eWxlID0gImNvbG9yOiByZWQ7Ij5FZGl0IHllYXIgaWYgbmVjZXNzYXJ5Ljwvc3Bhbj4KYGBgCmBgYHtyfQpkZl95b3VyZGF0YSB8PiBmaWx0ZXIoeWVhciA9PSAyMDIwKSB8PiBkcm9wX25hKHNob3J0X25hbWVfMiwgc2hvcnRfbmFtZV8xKSB8PgogIGxtKGxvZzEwKHNob3J0X25hbWVfMSl+bG9nMTAoc2hvcnRfbmFtZV8yKSwgZGF0YSA9IF8pIHw+IHN1bW1hcnkoKQpgYGAKCioqT2JzZXJ2YXRpb25zIGFuZCBRdWVzdGlvbnM6KioKCi0gICAK