Disclaimer
This is a project that I worked on during my practicum in Masters of Business Analytics program at University of Washington, Foster School of Business in collarboration with Microsoft Financing team. We’ve only used public data for this project and the suggestions are not binding.
Problem Statement
Microsoft Financing is a service that offers a wide range of financing options for its customers to accomodate to their needs. Microsoft Financing has expanded to 22 different countries by 2019 and four more countries in 2020 (Singapore, United Arabic Emirates, India and South Africa). Our goal is to find out which countries they should expand into next. In this part of the quantitative analysis, we narrowed down a list of countries that we should look deeper into in our qualitative analysis. We first collect publicly available country-level data from various websites and clean the data. Then, we build three different models to evaluate which countries are more favorable to expand into. Last we summarise our finding and move on to the qualitative analysis.
Load packages
library(tidyverse)
library(readxl)
library(data.table)
library(caret) # For Normalizing Data
Read Data
World_Bank <- read_csv("World Bank Data_Data.csv")
CPI_2019 <- read_xlsx("CPI_2012_2019.xlsx", skip = 2, sheet = "CPI2019")
Population <- read.csv("Population.csv", stringsAsFactors = FALSE)
Global_Economy <- read.csv("Theglobaleconomy_Data.csv", stringsAsFactors = FALSE)
Internet_Data <- read.csv("Internet_Data.csv" , stringsAsFactors = FALSE)
Data_Center <- read_xlsx("#of Data Center by Country.xlsx")
GCI <- read_xlsx("WEF_GCI_4.0_2019_Dataset.xlsx", sheet = "Data", skip = 3)
The data that we collected can be generallized into 5 categories:
- Economic
- Demographic
- Public policy and Institutions
- Competitiveness
- Industry and Market
The data that we gathered are all public data from various websites, including the World Bank, World Economic Forum and theglobaleconomy.com.
Data Sources : World_Bank and Population is collected from the World Bank. CPI_2019 is from Transparency International. Global_Economy and Internet_Data are from The Global Economy. Data_Center is from Data Center Map. GCI is from the World Economic Forum.
Expand_Countries_List <- c("United States", "Australia", "Austria", "Belgium", "Brazil", "Canada", "Denmark", "Finland", "France", "Germany", "Ireland", "Italy", "Japan", "Mexico", "Netherlands", "New Zealand", "Norway", "Portugal", "Spain", "Sweden", "Switzerland", "United Kingdom", "South Africa", "India", "United Arab Emirates", "Singapore")
Expand_Countries_List_re <- c("United States", "Australia", "Austria", "Belgium", "Brazil", "Canada", "Denmark", "Finland", "France", "Germany", "Ireland", "Italy", "Japan", "Mexico", "Netherlands", "New Zealand", "Norway", "Portugal", "Spain", "Sweden", "Switzerland", "United Kingdom")
Expand_Countries_List is a vector of countries that contains all the countries Microsoft Financing has expanded into, including the 4 expansions in 2020 (Singapore, UAE, India, South Africa) We also wanted to evaluate the current expansions in 2020, so we created two separted vectors to store these countries. Expand_Countries_List_re is a vector of countries that contains all the countries Microsoft Financing has expanded into, without the 4 expansions in 2020.
Cleaning Data
Clean WB
WB <- World_Bank
head(WB)
tail(WB)
# Drop last five rows
WB <- head(WB, -5)
We can see that each row is a country with a given variable (series). The last 5 rows are not useful so we will drop them.
# Change column names
names(WB) <- c("Country_Name", "Country_Code", "Series_Name", "Series_Code", "2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019")
NAs are recorded as “..” and therefore other numeric values are recoreded as characters. We loop through the 5th to the 14th columns to make the numeric values numeric and the characters that are not numeric into NAs.
WB[, 5:14] <- sapply(WB[, 5:14], as.numeric)
# Keep only the first 217 countries
Unique_Country_Name <- unique(WB$Country_Name)
Unique_Country_Name <- Unique_Country_Name[1:217] # The rest of the Country_Name are regional such as North America
WB <- WB[WB$Country_Name %in% Unique_Country_Name, ] # Keep only the country level data in WB
Use the country names in World Bank to join data from difference source together. We will match all the country names from data using the country names used in the World Bank.
Clean CPI_2019
head(CPI_2019)
# The list of countries that are not using the exact same name as the names used in WB
CPI_2019$Country[!CPI_2019$Country %in% unique(WB$Country_Name)]
[1] "Hong Kong" "United States of America" "Taiwan"
[4] "Bahamas" "Korea, South" "Saint Vincent and the Grenadines"
[7] "Saint Lucia" "Slovakia" "Gambia"
[10] "Egypt" "Kyrgyzstan" "Laos"
[13] "Russia" "Iran" "Congo"
[16] "Democratic Republic of the Congo" "Guinea Bissau" "Korea, North"
[19] "Venezuela" "Yemen" "Syria"
Need to change these names to the names used in WB Manually change the country names.
CPI_2019$Country[!CPI_2019$Country %in% unique(WB$Country_Name)] <- c("Hong Kong SAR, China", "United States", "Taiwan", "Bahamas, The", "Korea, Rep.", "St. Vincent and the Grenadines", "St. Lucia", "Slovak Republic", "Gambia, The", "Egypt, Arab Rep.", "Kyrgyz Republic", "Lao PDR", "Russian Federation", "Iran, Islamic Rep.","Congo, Rep.", "Congo, Dem. Rep.", "Guinea-Bissau", "Korea, Dem. People’s Rep.", "Venezuela, RB", "Yemen, Rep.", "Syrian Arab Republic")
Clean Global_Economy
head(Global_Economy)
# The country names that are not in WB.
Global_Economy$Country[!Global_Economy$Country %in% unique(WB$Country_Name)] %>% unique()
[1] "Bahamas" "Brunei" "Burma (Myanmar)"
[4] "Cape Verde" "Democratic Republic of the Congo" "Egypt"
[7] "Gambia" "Hong Kong" "Iran"
[10] "Ivory Coast" "Kyrgyzstan" "Laos"
[13] "Macao" "Macedonia" "Micronesia"
[16] "North Korea" "Palestine" "Republic of the Congo"
[19] "Russia" "Saint Lucia" "Saint Vincent and the Grenadines"
[22] "Slovakia" "South Korea" "Swaziland"
[25] "Syria" "Taiwan" "USA"
[28] "Venezuela" "Yemen"
Manually check the names used in WB. Some of them are not in WB such as Taiwan and Palestine, just keep them as they are.
GE_name <- Global_Economy$Country[!Global_Economy$Country %in% unique(WB$Country_Name)] %>% unique()
WB_name <- c("Bahamas, The", "Brunei Darussalam", "Myanmar", "Cabo Verde", "Congo, Dem. Rep.", "Egypt, Arab Rep.", "Gambia, The", "Hong Kong SAR, China", "Iran, Islamic Rep.", "Cote d'Ivoire", "Kyrgyz Republic", "Lao PDR", "Macao SAR, China", "North Macedonia", "Micronesia, Fed. Sts.", "Korea, Dem. People’s Rep.", "Palestine", "Congo, Rep.", "Russian Federation", "St. Lucia", "St. Vincent and the Grenadines", "Slovak Republic", "Korea, Rep.", "Eswatini", "Syrian Arab Republic", "Taiwan", "United States", "Venezuela, RB", "Yemen, Rep.")
length(GE_name) == length(WB_name) # Check if the two vectors have the same length
[1] TRUE
Because there are multiple rows for a country, so change the names using a loop
for (i in 1:nrow(Global_Economy)) {
if(!Global_Economy$Country[i] %in% GE_name) { # If the country name is the same as WB
next # Do not change name
} else { # Else (the country name is different)
Global_Economy$Country[i] <- WB_name[Global_Economy$Country[i] == GE_name] # Use the name in WB
}
}
Change column names
names(Global_Economy) <- c("Country", "Code", "Year", "Rule_of_law_index", "Government_effectiveness_index", "Control_of_corruption", "Regulatory_quality_index")
names(Global_Economy)
[1] "Country" "Code" "Year"
[4] "Rule_of_law_index" "Government_effectiveness_index" "Control_of_corruption"
[7] "Regulatory_quality_index"
Clean Data_Center
Need to change these names to the names used in WB
Data_Center$Country[!Data_Center$Country %in% WB$Country_Name]
[1] "USA" "The Netherlands" "Hong Kong" "Russia"
[5] "Iran" "South Korea" "Slovakia" "Egypt"
[9] "Taiwan" "Jersey" "Macedonia" "Venezuela"
[13] "Guernsey" "Isle Of Man" "Bahamas" "Bosnia And Herzegovina"
[17] "Dr Congo" "Macau" "Netherlands Antilles" "Palestine"
[21] "Reunion" "Trinidad And Tobago" "Us Virgin Islands"
Data_Center$Country[!Data_Center$Country %in% WB$Country_Name] <- c("United States", "Netherlands", "Hong Kong SAR, China", "Russian Federation", "Iran, Islamic Rep.", "Korea, Rep.", "Slovak Republic", "Egypt, Arab Rep.", "Taiwan", "Jersey", "North Macedonia", "Venezuela, RB", "Guernsey", "Isle of Man", "Bahamas, The", "Bosnia and Herzegovina", "Congo, Dem. Rep.", "Macao SAR, China", "Netherlands Antilles", "Palestine", "Reunion", "Trinidad and Tobago", "Virgin Islands (U.S.)")
# "Taiwan", "Jersey", "Guernsey", "Netherlands Antilles", "Palestine", "Reunion" are not in WB
Clean Internet_Data
for (i in 1:nrow(Internet_Data)) {
if(!Internet_Data$Country[i] %in% GE_name) { # If the country name is the same as WB
Internet_Data$Country[i] <- Internet_Data$Country[i] # Do not chang name
} else { # Else (the country name is different)
Internet_Data$Country[i] <- WB_name[Internet_Data$Country[i] == GE_name] # Use the name in WB
}
}
Variables
Variables that we use are: Population, GDP(2018, Current US$), GDP Growth (2018, %), Inflation Rate(2018, %), Number of Data Centers (2018, #), Number of Secure Internet Servers(2018, #), Percentage of Internet Users of Population (2017, %), Rule of law index(2018, -2.5 ~ 2.5), Government effectiveness index(2018, -2.5 ~ 2.5), Control of corruption(2018, -2.5 ~ 2.5), Regulatory quality index(2018, -2.5 ~ 2.5), Ease of doing business(2019, Ranking), Global Competitiveness Index Score (2019, 0-100), the 12 pillars in GCI: Institutions, Infrastructure, ICT adoption, Macroeconomic stability, Health, Skills, Product market, Labour market, Financial system, Market size, Business dynamism, Innovation capability (2019, 0-100)
| GDP |
2018 |
in US$ |
World Bank |
| GDP Growth |
2018 |
% |
World Bank |
| FX Rate |
2018 |
in US$ |
World Bank |
| Inflation |
2018 |
% |
World Bank |
| # of Data Centers |
2018 |
# |
datacentermap.com |
| # of Internet Servers |
2018 |
# |
World Bank |
| % Internet Users |
2017 |
% population |
theglobaleconomy.com |
| Population |
2018 |
# |
World Bank |
| Rule of law |
2018 |
-2.5 ~ 2.5 |
theglobaleconomy.com |
| Government effectiveness |
2018 |
-2.5 ~ 2.5 |
theglobaleconomy.com |
| Regulatory quality |
2018 |
-2.5 ~ 2.5 |
theglobaleconomy.com |
| Ease of doing business |
2018 |
Ranking |
World Bank |
| Competitiveness Index |
2019 |
0 ~ 100 |
World Economy Forum |
| Institutions |
2019 |
0 ~ 100 |
World Economy Forum |
| Infrastructure |
2019 |
0 ~ 100 |
World Economy Forum |
| ICT adoption |
2019 |
0 ~ 100 |
World Economy Forum |
| Macroeconomic stability |
2019 |
0 ~ 100 |
World Economy Forum |
| Health |
2019 |
0 ~ 100 |
World Economy Forum |
| Skills |
2019 |
0 ~ 100 |
World Economy Forum |
| Product market |
2019 |
0 ~ 100 |
World Economy Forum |
| Labour market |
2019 |
0 ~ 100 |
World Economy Forum |
| Financial system |
2019 |
0 ~ 100 |
World Economy Forum |
| Market size |
2019 |
0 ~ 100 |
World Economy Forum |
| Business dynamism |
2019 |
0 ~ 100 |
World Economy Forum |
| Innovation capability |
2019 |
0 ~ 100 |
World Economy Forum |
Population
From wbstats R package. Already cleaned. Use 2018 population.
Population_2018 <- Population %>%
select(Country_Name, Population_2018)
GDP
From World Bank. Use 2018 data because we still don’t have 2019 data yet.
GDP_2018 <- WB %>%
filter(Series_Name == "GDP (current US$)") %>%
mutate(GDP_2018 = `2018`) %>%
select(Country_Name, GDP_2018)
GDP Growth
From World Bank. Use 2018 data because we still don’t have 2019 data yet.
GDP_Growth_2018 <- WB %>%
filter(Series_Name == "GDP growth (annual %)") %>%
rename(GDP_Growth_2018 = `2018`) %>%
select(Country_Name, GDP_Growth_2018)
Inflation Rate (No Iran and Syria)
Inflation_Rate_2018<- WB %>%
filter(Series_Name == "Inflation, GDP deflator (annual %)") %>%
mutate(Inflation_Rate_2018 = `2018`) %>%
select(Country_Name, Inflation_Rate_2018)
Number of Data Centers (cleaned)
Number of Secure Internet Servers (cleaned)
Internet_servers_2018 <- WB %>%
filter(Series_Name == "Secure Internet servers") %>%
mutate(Internet_servers_2018 = `2018`) %>%
select(Country_Name, Internet_servers_2018)
Percentage of Internet Users 2017
There’s more NAs in 2018 Data
Internet_Users_2017 <- Internet_Data %>%
filter(Year == 2017) %>%
select(Country, Internet.users.percent.of.population)
Corruption
Corruption_2019 <- CPI_2019 %>%
select(Country, `CPI score 2019`)
Stability_2018: Rule_of_law_index, Government_effectiveness_index, Control_of_corruption, Regulatory_quality_index
Stability_2018 <- Global_Economy %>%
filter(Year == 2018) %>%
select(-c(Code, Year))
Stability_2018
Ease of doing business
Ease_of_doing_business_2019 <- WB %>%
filter(Series_Name == "Ease of doing business index (1=most business-friendly regulations)") %>%
mutate(Ease_of_doing_business_2019 = `2019`) %>%
select(Country_Name, Ease_of_doing_business_2019)
!!! Is Ranked Data, meaning that lower the number, the better the country !!!
Ease_of_doing_business_2019 <- Ease_of_doing_business_2019 %>%
arrange(Ease_of_doing_business_2019) %>%
mutate(Reverese_Ranking = 191 - Ease_of_doing_business_2019) %>%
select(Country_Name, Reverese_Ranking) %>%
mutate(Ease_of_doing_business_2019 = Reverese_Ranking) %>%
select(Country_Name, Ease_of_doing_business_2019)
Reverse the ranking so higher in rank has a higher score
GCI
GCI_score_2019 <- GCI %>%
filter(Edition == 2019, `Series name` == "Global Competitiveness Index 4.0", Attribute == "SCORE") %>%
select(-c(Index, Edition, `Series Global ID`,`Series name`, `Freeze date`, `Series units`, `Series order`, `Series code (if applicable)`, `Series type`, Attribute)) %>%
t() %>%
as.matrix(rownames.force = TRUE) %>%
as.data.frame()
GCI_score_2019 <- cbind(rownames(GCI_score_2019), GCI_score_2019)
names(GCI_score_2019) <- c("Country_Name", "GCI_2019")
rownames(GCI_score_2019) <- c()
GCI_score_2019$Country_Name <- as.character(GCI_score_2019$Country_Name)
GCI_score_2019$GCI_2019 <- as.numeric(as.character(GCI_score_2019$GCI_2019))
GCI_score_2019 <- head(GCI_score_2019, -12) # get rid of the last 12 rows (regional data)
GCI_score_2019$Country_Name[!GCI_score_2019$Country_Name %in% unique(WB$Country_Name)] <- c("Cote d'Ivoire", "Congo, Dem. Rep.", "Cabo Verde", "Egypt, Arab Rep.", "Hong Kong SAR, China", "North Macedonia", "Taiwan", "Venezuela, RB", "Vietnam", "Yemen, Rep.")
12 Pillars in GCI
Names of the 12 pillars
Pillars_12 <- GCI %>%
filter(Edition == 2019, `Series type` == "Pillar", Attribute == "SCORE") %>%
select(`Series name`) %>%
.$`Series name`
GCI_pillars <- GCI %>%
filter(Edition == 2019, `Series type` == "Pillar", Attribute == "SCORE") %>%
select(-c(Index, Edition, `Series Global ID`,`Series name`, `Freeze date`, `Series units`, `Series order`, `Series code (if applicable)`, `Series type`, Attribute)) %>%
t() %>%
as.data.frame()
GCI_pillars <- cbind(rownames(GCI_pillars), GCI_pillars)
names(GCI_pillars) <- c("Country_Name", Pillars_12)
rownames(GCI_pillars) <- c()
GCI_pillars$Country_Name <- as.character(GCI_pillars$Country_Name)
GCI_pillars[, 2:13]<- sapply(GCI_pillars[, 2:13], as.character)
GCI_pillars[, 2:13]<- sapply(GCI_pillars[, 2:13], as.numeric)
GCI_pillars <- head(GCI_pillars, -12) # get rid of the last 12 rows (regional data)
GCI_pillars$Country_Name[!GCI_pillars$Country_Name %in% unique(WB$Country_Name)] <- c("Cote d'Ivoire", "Congo, Dem. Rep.", "Cabo Verde", "Egypt, Arab Rep.", "Hong Kong SAR, China", "North Macedonia", "Taiwan", "Venezuela, RB", "Vietnam", "Yemen, Rep.")
Joining Dataframes:
GDP_2018, Population_2018, GDP_Growth_2018, Inflation_Rate_2018, FX_Rate_2018, Data_Center, Internet_servers_2018, Internet_Users_2017, Stability_2018, Ease_of_doing_business_2019, GCI_score_2019, GCI_pillars
Clustering_df <- GDP_2018 %>%
left_join(Population_2018, by = c("Country_Name")) %>%
left_join(GDP_Growth_2018, by = c("Country_Name")) %>%
left_join(Inflation_Rate_2018, by = c("Country_Name")) %>%
left_join(Data_Center, by = c("Country_Name" = "Country")) %>%
left_join(Internet_servers_2018, by = c("Country_Name")) %>%
left_join(Internet_Users_2017, by = c("Country_Name" = "Country")) %>%
left_join(Stability_2018, by = c("Country_Name" = "Country")) %>%
left_join(Ease_of_doing_business_2019, by = c("Country_Name")) %>%
left_join(GCI_score_2019, by = c("Country_Name")) %>%
left_join(GCI_pillars, by = c("Country_Name"))
Inpute missing data centers as 0s.
Clustering_df[is.na(Clustering_df$`Number of Data Center`), ]$`Number of Data Center` <- 0
Assume that countries that have missing values are countries Microsoft should not expand into.
Check the countries with missing values
# List of countries with missing values
Clustering_df[!complete.cases(Clustering_df), ]$Country_Name
[1] "Afghanistan" "American Samoa" "Andorra"
[4] "Antigua and Barbuda" "Aruba" "Bahamas, The"
[7] "Belarus" "Belize" "Bermuda"
[10] "Bhutan" "British Virgin Islands" "Cayman Islands"
[13] "Central African Republic" "Channel Islands" "Comoros"
[16] "Congo, Rep." "Cuba" "Curacao"
[19] "Djibouti" "Dominica" "Equatorial Guinea"
[22] "Eritrea" "Faroe Islands" "Fiji"
[25] "French Polynesia" "Gibraltar" "Greenland"
[28] "Grenada" "Guam" "Guinea-Bissau"
[31] "Guyana" "Iran, Islamic Rep." "Iraq"
[34] "Isle of Man" "Kiribati" "Korea, Dem. People’s Rep."
[37] "Kosovo" "Liberia" "Libya"
[40] "Liechtenstein" "Macao SAR, China" "Maldives"
[43] "Marshall Islands" "Micronesia, Fed. Sts." "Monaco"
[46] "Myanmar" "Nauru" "New Caledonia"
[49] "Niger" "Northern Mariana Islands" "Palau"
[52] "Papua New Guinea" "Puerto Rico" "Samoa"
[55] "San Marino" "Sao Tome and Principe" "Sierra Leone"
[58] "Sint Maarten (Dutch part)" "Solomon Islands" "Somalia"
[61] "South Sudan" "St. Kitts and Nevis" "St. Lucia"
[64] "St. Martin (French part)" "St. Vincent and the Grenadines" "Sudan"
[67] "Suriname" "Syrian Arab Republic" "Timor-Leste"
[70] "Togo" "Tonga" "Turkmenistan"
[73] "Turks and Caicos Islands" "Tuvalu" "Uzbekistan"
[76] "Vanuatu" "Venezuela, RB" "Virgin Islands (U.S.)"
[79] "West Bank and Gaza"
# Keep countries(rows) that have no missing values
Clustering_df <- Clustering_df[complete.cases(Clustering_df), ]
Clustering_df
Add Expand Column
Clustering_df$Expand <- ifelse(Clustering_df$Country_Name %in% Expand_Countries_List, TRUE, FALSE)
Clustering_df$Expand_re <- ifelse(Clustering_df$Country_Name %in% Expand_Countries_List_re, TRUE, FALSE)
Normalize Clustering_df
Clustering_df_preproc <- preProcess(Clustering_df)
Clustering_df_norm <- predict(Clustering_df_preproc, Clustering_df)
head(Clustering_df_norm )
Exploratory data analysis (EDA)
We first take a look at some of the variables in our final data.
p1 <- ggplot(Clustering_df, aes(x = GDP_2018)) +
geom_histogram() +
ggtitle("2018 GDP") +
xlab("") +
scale_x_continuous(breaks = c(0,5000000000000,10000000000000, 15000000000000, 20000000000000),
labels = c("0", "5 Trillion", "10 Trillion", "15 Trillion", "20 Trillion")) +
theme(plot.title = element_text(size = 10, face = "bold"))+
theme(axis.text.x = element_text(angle = 45, hjust=1, size = 8))
p2 <- ggplot(Clustering_df, aes(x = Population_2018)) +
geom_histogram() +
ggtitle("2018 Population")+
scale_x_continuous(breaks = c(0,500000000,1000000000),
labels = c("0", "5 Billion", "10 Billion")) +
xlab("")+
theme(plot.title = element_text(size = 10, face = "bold"))
p3 <- ggplot(Clustering_df, aes(x = GDP_Growth_2018)) +
geom_histogram() +
ggtitle("2018 GDP Growth") +
xlab("") +
theme(plot.title = element_text(size = 10, face = "bold"))
p4 <- ggplot(Clustering_df, aes(x = GCI_2019)) +
geom_histogram() +
ggtitle("Global Competitiveness Index")+
xlab("") +
theme(plot.title = element_text(size = 8, face = "bold"))
p5 <- ggplot(Clustering_df, aes(x = Inflation_Rate_2018)) +
geom_histogram()+
ggtitle("2018 Inflation Rate")+
xlab("") +
theme(plot.title = element_text(size = 10, face = "bold"))
p6 <- ggplot(Clustering_df, aes(x = `Number of Data Center`)) +
geom_histogram() +
ggtitle("Number of Data Center")+
xlab("") +
theme(plot.title = element_text(size = 10, face = "bold"))
grid.arrange(p1, p2, p3, p4, p5, p6, nrow = 2)
We can see that some of the variables are extremely skewed, such as population and GDP, while others seem to be normally distributed.
Method 1: Ranking Countries:
To find out which countries are more favorable to expand into, we rank all the countries that Microsoft Financing has not expanded into by different variables, and calculate how many times each country appears in the top 20% of a variable.
# "not in" function
`%notin%` <- Negate(`%in%`)
# Rank country takes a dataframe with two columns (first column is country and second column is the attribute)'
# Returns the top 20% of the country's names in that attribute
Rank_Country <- function(x, Reverse = FALSE) {
colnames(x) <- c("Country_Name", "Attribute")
if(Reverse == FALSE) {
output_vector <- x %>%
filter(Country_Name %notin% Expand_Countries_List) %>%
arrange(desc(Attribute)) %>%
head(nrow(Clustering_df) * 0.2) %>%
.$Country_Name
} else if (Reverse == TRUE) {
output_vector <- x %>%
filter(Country_Name %notin% Expand_Countries_List) %>%
arrange(Attribute) %>%
head(nrow(Clustering_df) * 0.2) %>%
.$Country_Name
}
return(output_vector)
}
# Function to re-run ranking to include the 4 countries (SG, India, South Africa and UAE)
Rank_Country_re <- function(x, Reverse = FALSE) {
colnames(x) <- c("Country_Name", "Attribute")
if(Reverse == FALSE) {
output_vector <- x %>%
filter(Country_Name %notin% Expand_Countries_List_re) %>%
arrange(desc(Attribute)) %>%
head(nrow(Clustering_df) * 0.2) %>%
.$Country_Name
} else if (Reverse == TRUE) {
output_vector <- x %>%
filter(Country_Name %notin% Expand_Countries_List_re) %>%
arrange(Attribute) %>%
head(nrow(Clustering_df) * 0.2) %>%
.$Country_Name
}
return(output_vector)
}
A generic function that takes in a dataframe of two columns, Country Name and a variable. Then it returns a vector of countries that ranks in the top 20% of that variable. Use a for loop and table() to see how each countries perform.
Country_vector <- c()
for (i in c(2:26)) {
Country_vector <- append(Country_vector, Rank_Country(Clustering_df[,c(1,i)]))
}
# Replace inflation because inflation should be ranked from the lowest to highest
Country_vector[(3*26+1):(3*26+27)] <- Rank_Country(Clustering_df[,c(1,5)], Reverse = TRUE)
Country_vector_re <- c()
for (i in c(2:26)) {
Country_vector_re <- append(Country_vector_re, Rank_Country_re(Clustering_df[,c(1,i)]))
}
# Replace inflation because inflation should be ranked from the lowest to highest
Country_vector_re[(3*26+1):(3*26+27)] <- Rank_Country_re(Clustering_df[,c(1,5)], Reverse = TRUE)
# Without 2020 expansions
head(sort(table(Country_vector),decreasing = TRUE), 18)
The first table shows the results of lists of countries without the 2020 expansions. These are all the countries that scored 15+ in our ranking system. The ranking system provides an intuitive and straightforward result that everyone could easily gauge. For example, with a score of 22, Hong Kong are in the top 20% in 22 out of the 25 different variables, putting it as one of the most favorable regions to expand into.
# 2020 expansions included
head(sort(table(Country_vector_re),decreasing = TRUE), 40)
The second table evaluates the performance of the current expansion (Singapore. United Arab Emirates, India and South Africa). It turns out that Singapore and United Arab Emirate scored really well, but India and South Africa scored pourly in our model.
Method 2: Clustering Model
We could also use clustering models to help find out which countries Microsoft should take a closer look at. Clustering models put different countries into different clusters, and the countries within the same cluster are said to be similar to each other. If we have a cluster that predominantly consists of countries that Microsoft Financing has expanded into, the other countries that Microsoft have not expanded into are the ones that they should consider.
Clustering Model : Hierarchical clustering
Hierachical clustering calculates the “distance” between different countries, and countries that are closer together will be put in the same cluster, depending on the number of clusters. In this model, we use the most common euclidean distance, which is the ordinary straight-line distance. For example, the euclidean distance between point A(1,2,3,4) and B(9, 8, 7, 6) would be: \[\sqrt{(9-1)^2 + (8-2)^2 + (7-3)^2 + (6-4)^2} = \sqrt{120} \]
Build Tree
Clustering_df_h <- column_to_rownames(Clustering_df, var = "Country_Name") # turn Country_Name into row names to display in the dendrogram.
distance_1 <- dist(Clustering_df_h, method = "euclidean")
ClusteringTree_1 <- hclust(distance_1, method = "ward.D")
Plot Full Tree
hcd = as.dendrogram(ClusteringTree_1)
plot(hcd)
</br>
It is really hard to read from the dendrogram because there are too many countries, so we would display just part of the tree.
The bottom part of the tree
plot(cut(hcd, h = 600000000000)$lower[[13]], main = "Lower tree part 1")
plot(cut(hcd, h = 600000000000)$lower[[12]], main = "Lower tree part 2")
From the dendrograms shown, we can see that the Hierachical clustering model groups countries together buy their distance to other countries. In the second plot, the model shows that the countries that is the most similar to Hong Kong is Singapore, and the second closest group are Israel and South Africa. In the third tree, if we want to have two clusters, then we would put Argentina nad Thailand in a cluster and Poland, Belgium and Sweden in the other cluster. If we want three clusters, then Poland would form a cluster of its own.
4 Clusters
k = 4
ClusterGroup_1 <- cutree(ClusteringTree_1, k = k) # Get the clustering result
Clustering_df_cluster <- Clustering_df
Clustering_df_cluster$Cluster <- factor(ClusterGroup_1) # Put it into anther dataframe
table(Clustering_df_cluster$Cluster, Clustering_df_cluster$Expand) # Show the overall clustering result in regards to expansion
for (i in 3:4) {
print(Clustering_df_cluster %>% filter(Cluster == i, Expand == FALSE) %>% .$Country_Name)
}
The table shows that we should look at the 3rd and 4th clusters because they have the highest percentage of current expansions. The TRUE represents the number of countries that Microsoft has already expanded into while FALSE represents the number of countries that Microsoft have not expanded into yet. The two FALSE countries in Cluster 3 are South Korea and Russia and the FALSE country in Cluster 4 is China.
5 Clusters
k = 5
ClusterGroup_1 <- cutree(ClusteringTree_1, k = k) # Get the clustering result
Clustering_df_cluster <- Clustering_df
Clustering_df_cluster$Cluster <- factor(ClusterGroup_1) # Put it into anther dataframe
table(Clustering_df_cluster$Cluster, Clustering_df_cluster$Expand)
print(Clustering_df_cluster %>% filter(Cluster == 3, Expand == FALSE) %>% .$Country_Name)
6 Clusters
k = 6
ClusterGroup_1 <- cutree(ClusteringTree_1, k = k) # Get the clustering result
Clustering_df_cluster <- Clustering_df
Clustering_df_cluster$Cluster <- factor(ClusterGroup_1) # Put it into anther dataframe
table(Clustering_df_cluster$Cluster, Clustering_df_cluster$Expand)
print(Clustering_df_cluster %>% filter(Cluster == 3, Expand == FALSE) %>% .$Country_Name)
7 Clusters
k = 7
ClusterGroup_1 <- cutree(ClusteringTree_1, k = k) # Get the clustering result
Clustering_df_cluster <- Clustering_df
Clustering_df_cluster$Cluster <- factor(ClusterGroup_1) # Put it into anther dataframe
table(Clustering_df_cluster$Cluster, Clustering_df_cluster$Expand)
print(Clustering_df_cluster %>% filter(Cluster == 3, Expand == FALSE) %>% .$Country_Name)
8 Clusters
k = 8
ClusterGroup_1 <- cutree(ClusteringTree_1, k = k) # Get the clustering result
Clustering_df_cluster <- Clustering_df
Clustering_df_cluster$Cluster <- factor(ClusterGroup_1) # Put it into anther dataframe
table(Clustering_df_cluster$Cluster, Clustering_df_cluster$Expand)
print(Clustering_df_cluster %>% filter(Cluster == 3, Expand == FALSE) %>% .$Country_Name)
The clustering result does not change much when the number of clusters increase. This is because when we increase the number of clusters by 1, it would be easy to “kick out” a country from a cluster to form a new cluster, therefore the majority of the clusters did not change.
=====================================================================================================================
Clustering Model : K-means clustering (Normalized)
K means clustering is different clustering algorithm. The algorithm would randomly genearate a number of centroids (based on our choice of k, the number of clusters) to start. Each data point is then assigned to the closest centroid. After that, the centroids shift to their respective average point within their cluster, then each point is assigned to the clostest centroid. The process is repeated until an equilibrium is reached or the maximum number of iterations are met.
set.seed(1)
k = 4
Clustering_kmeans <- kmeans(Clustering_df_norm[,2:26], centers = k, nstart = 20) # Use n_start to "stabalize" the clustering results.
Clustering_df_norm_kmeans <- Clustering_df_norm
Clustering_df_norm_kmeans$Cluster <- Clustering_kmeans$cluster
table(Clustering_df_norm_kmeans$Cluster, Clustering_df_norm_kmeans$Expand)
print(Clustering_df_norm_kmeans %>% filter(Cluster == 3, Expand == FALSE) %>%.$Country_Name)
From the results, we can see that we should focus on the third cluster: Chile, Czech Republic, Estonia, Hong Kong SAR, Iceland, Israel, South Korea, Lithuania, Luxembourg, Malaysia, Malta, Qatar, Slovenia.
If we change the number of clusters, we may get different results. (k =7, seed = 1)
set.seed(1)
k = 7
Clustering_kmeans <- kmeans(Clustering_df_norm[,2:26], centers = k, nstart = 20) # Use n_start to "stabalize" the clustering results.
Clustering_df_norm_kmeans <- Clustering_df_norm
Clustering_df_norm_kmeans$Cluster <- Clustering_kmeans$cluster
table(Clustering_df_norm_kmeans$Cluster, Clustering_df_norm_kmeans$Expand)
print(Clustering_df_norm_kmeans %>% filter(Cluster == 4, Expand == FALSE) %>%.$Country_Name)
Different seed might generate different results (k =7, seed = 3)
set.seed(3)
k = 7
Clustering_kmeans <- kmeans(Clustering_df_norm[,2:26], centers = k, nstart = 20) # Use n_start to "stabalize" the clustering results.
Clustering_df_norm_kmeans <- Clustering_df_norm
Clustering_df_norm_kmeans$Cluster <- Clustering_kmeans$cluster
table(Clustering_df_norm_kmeans$Cluster, Clustering_df_norm_kmeans$Expand)
print(Clustering_df_norm_kmeans %>% filter(Cluster == 2, Expand == FALSE) %>%.$Country_Name)
If we change the number of clusters or set a different seed, we will get different results from the K-means clustering model. The clustering results might be different while using the same number of clusters because the different starting points might generate differnt results, as we cans see from the outputs above.
To address this, we could run a simulation to see which countries are more preferable. We re run the clustering model 10,000 times and calculate the number of times each countries appear in a cluster which has more expanded countries than non-expanded countries. #### Simulation: k=4, 10,000 times
k = 4
c_list <- c()
for (j in 1:10000){
set.seed(j)
Clustering_kmeans <- kmeans(Clustering_df_norm[,2:26], centers = k, nstart = 20) # Use n_start to "stabalize" the clustering results.
Clustering_df_norm_kmeans <- Clustering_df_norm
Clustering_df_norm_kmeans$Cluster <- Clustering_kmeans$cluster
a <- table(Clustering_df_norm_kmeans$Cluster, Clustering_df_norm_kmeans$Expand)
for (i in 1:k){
if(a[i] < a[i+k]){
c_list <- append(c_list, Clustering_df_norm_kmeans %>% filter(Cluster == i, Expand == FALSE) %>%.$Country_Name)
}
}
}
sort(table(c_list), decreasing = TRUE)
Simulation: k=7, 10,000 times
k = 7
c_list <- c()
for (j in 1:10000){
set.seed(j)
Clustering_kmeans <- kmeans(Clustering_df_norm[,2:26], centers = k, nstart = 20) # Use n_start to "stabalize" the clustering results.
Clustering_df_norm_kmeans <- Clustering_df_norm
Clustering_df_norm_kmeans$Cluster <- Clustering_kmeans$cluster
a <- table(Clustering_df_norm_kmeans$Cluster, Clustering_df_norm_kmeans$Expand)
for (i in 1:k){
if(a[i] < a[i+k]){
c_list <- append(c_list, Clustering_df_norm_kmeans %>% filter(Cluster == i, Expand == FALSE) %>%.$Country_Name)
}
}
}
sort(table(c_list), decreasing = TRUE)
From the above simulation, we can see that the recommended countries wouls be Chile, Czech, Estonia, Hong Kong, Iceland, Israel, Korea, Luxembourg, Malaysia, Qatar, Slovenia, Lituania and Malta. The results are pretty similar with the results from our ranking model: Hong Kong, Korea, Malaysia, Israel, Czech, Chile, Estonia, Iceland, Luxembourg, Slovenia, Cyprus, Latvia, Lithuania, Malta, Poland, Qatar, China, Saudi Arabia.
Should_Expand_List <- c("Chile", "Czech Republic", "Estonia", "Hong Kong SAR, China", "Iceland", "Israel", "Korea, Rep.", "Luxembourg", "Malaysia", "Qatar", "Slovenia", "Lituania", "Malta", "Poland", "Cyprus", "Latvia", "Lithuania", "China", "Saudi Arabia")
Scree Plot
A scree plot is commonly used to determine the number of clusters to use in k means clustering.The recommended number of cluster is at the “elbow” where there’s a big change in slope.
wss <- c()
for (i in 1:15) {
Clustering_kmeans <- kmeans(Clustering_df_norm[,2:26], centers = i, nstart = 20)
# Save total within sum of squares to wss variable
wss[i] <- Clustering_kmeans$tot.withinss
}
plot(1:15, wss, type = "b",
xlab = "Number of Clusters",
ylab = "Within groups sum of squares")
</br>
Optimal cluster is 3 or 4.
The effect of Clustering
Status <- c()
for (i in 1:length(Clustering_df$Country_Name)){
if (Clustering_df$Country_Name[i] %in% Expand_Countries_List) {
Status = append(Status, "Already Expanded")
} else if (Clustering_df$Country_Name[i] %in%Should_Expand_List) {
Status = append(Status, "Should Expanded")
} else {
Status = append(Status, "Should Not Expanded")
}
}
for (i in 3:7) {
set.seed(1)
k = i
Clustering_kmeans <- kmeans(Clustering_df_norm[,2:26], centers = k, nstart = 20) # Use n_start to "stabalize" the clustering results.
df_plot <- Clustering_df
df_plot$Expand <- Status
df_plot$Cluster <- Clustering_kmeans$cluster
plot <- ggplot(data = df_plot, aes(x = Internet.users.percent.of.population, y = GCI_2019,
color = factor(Cluster),
shape = factor(Expand))) +
geom_point() +
# theme(legend.title = element_text("Clusters")) +
xlab("% of Internet Users") +
ylab("Global Competitiveness Index") +
ggtitle(paste0("Percentage of Internet Users against GCI with ", i, " clusters")) +
scale_colour_discrete("Clusters") +
scale_shape_discrete("Expand")
print(plot)
}
</br>
It is hard to visualize the effect of clustering using all vairables, but we can still see the effect in this two dimensional graph. Countries closer together are grouped in the same clusters, and it is not surprising that the countries on the upper right hand side (the countries with higher percentage of internet users and higher GCI score) would be more favorable to expand into. The clustering result with k=3 and k=4 are similar, expect that the United States has formed its own cluster.
Conclusion
Using three different models: Ranking, Hierarchical Clustering and K-means Clustering, we come up with a shrot list of countries that we could delve into without having to look into every country in the world. The countries from Ranking: Hong Kong, Korea, Malaysia, Israel, Czech, Chile, Estonia, Iceland, Luxembourg, Slovenia, Cyprus, Latvia, Lithuania, Malta, Poland, Qatar, China, Saudi Arabia From Hierarchical Clustering: Russia, Korea From K-means Clustering: Chile, Estonia, Hong Kong, Iceland ,Israel ,Korea, Luxembourg, Malaysia, Czech Republic, Qatar, Slovenia, Lithuania, Malta With a short list of countries to focus on, we continue our qualitative analysis in these countries.
LS0tDQp0aXRsZTogIkludGVybmF0aW9uYWwgRXhwYW5zaW9uIGZvciBNaWNyb3NvZnQgRmluYW5jaW5nIg0KYXV0aG9yOiAiSWFuIENodWFuZyINCmRhdGU6ICI0LzE1LzIwMjAiDQpvdXRwdXQ6DQogIGh0bWxfbm90ZWJvb2s6ZGVmYXVsdA0KLS0tDQoNCjxjZW50ZXI+IDxoMT5EaXNjbGFpbWVyPC9oMT4gPC9jZW50ZXI+DQpUaGlzIGlzIGEgcHJvamVjdCB0aGF0IEkgd29ya2VkIG9uIGR1cmluZyBteSBwcmFjdGljdW0gaW4gTWFzdGVycyBvZiBCdXNpbmVzcyBBbmFseXRpY3MgcHJvZ3JhbSBhdCBVbml2ZXJzaXR5IG9mIFdhc2hpbmd0b24sIEZvc3RlciBTY2hvb2wgb2YgQnVzaW5lc3MgaW4gY29sbGFyYm9yYXRpb24gd2l0aCBNaWNyb3NvZnQgRmluYW5jaW5nIHRlYW0uIFdlJ3ZlIG9ubHkgdXNlZCBwdWJsaWMgZGF0YSBmb3IgdGhpcyBwcm9qZWN0IGFuZCB0aGUgc3VnZ2VzdGlvbnMgYXJlIG5vdCBiaW5kaW5nLiANCg0KPGNlbnRlcj4gPGgxPlByb2JsZW0gU3RhdGVtZW50PC9oMT4gPC9jZW50ZXI+DQpNaWNyb3NvZnQgRmluYW5jaW5nIGlzIGEgc2VydmljZSB0aGF0IG9mZmVycyBhIHdpZGUgcmFuZ2Ugb2YgZmluYW5jaW5nIG9wdGlvbnMgZm9yIGl0cyBjdXN0b21lcnMgdG8gYWNjb21vZGF0ZSB0byB0aGVpciBuZWVkcy4gDQpNaWNyb3NvZnQgRmluYW5jaW5nIGhhcyBleHBhbmRlZCB0byAyMiBkaWZmZXJlbnQgY291bnRyaWVzIGJ5IDIwMTkgYW5kIGZvdXIgbW9yZSBjb3VudHJpZXMgaW4gMjAyMCAoU2luZ2Fwb3JlLCBVbml0ZWQgQXJhYmljIEVtaXJhdGVzLCBJbmRpYSBhbmQgU291dGggQWZyaWNhKS4gKipPdXIgZ29hbCBpcyB0byBmaW5kIG91dCB3aGljaCBjb3VudHJpZXMgdGhleSBzaG91bGQgZXhwYW5kIGludG8gbmV4dC4qKiBJbiB0aGlzIHBhcnQgb2YgdGhlIHF1YW50aXRhdGl2ZSBhbmFseXNpcywgd2UgbmFycm93ZWQgZG93biBhIGxpc3Qgb2YgY291bnRyaWVzIHRoYXQgd2Ugc2hvdWxkIGxvb2sgZGVlcGVyIGludG8gaW4gb3VyIHF1YWxpdGF0aXZlIGFuYWx5c2lzLiAgICA8L2JyPiANCldlIGZpcnN0IGNvbGxlY3QgcHVibGljbHkgYXZhaWxhYmxlIGNvdW50cnktbGV2ZWwgZGF0YSBmcm9tIHZhcmlvdXMgd2Vic2l0ZXMgYW5kIGNsZWFuIHRoZSBkYXRhLiBUaGVuLCB3ZSBidWlsZCB0aHJlZSBkaWZmZXJlbnQgbW9kZWxzIHRvIGV2YWx1YXRlIHdoaWNoIGNvdW50cmllcyBhcmUgbW9yZSBmYXZvcmFibGUgdG8gZXhwYW5kIGludG8uIExhc3Qgd2Ugc3VtbWFyaXNlIG91ciBmaW5kaW5nIGFuZCBtb3ZlIG9uIHRvIHRoZSBxdWFsaXRhdGl2ZSBhbmFseXNpcy4gDQoNCiA8L2JyPg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCg0KIyMjIyBMb2FkIHBhY2thZ2VzDQpgYGB7ciBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0NCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkeGwpDQpsaWJyYXJ5KGRhdGEudGFibGUpDQpsaWJyYXJ5KGNhcmV0KSAgICAgICAgICAgICAgIyBGb3IgTm9ybWFsaXppbmcgRGF0YQ0KbGlicmFyeShncmlkRXh0cmEpDQpgYGANCg0KIyMjIyBSZWFkIERhdGENCmBgYHtyIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQ0KV29ybGRfQmFuayA8LSByZWFkX2NzdigiV29ybGQgQmFuayBEYXRhX0RhdGEuY3N2IikgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KQ1BJXzIwMTkgPC0gcmVhZF94bHN4KCJDUElfMjAxMl8yMDE5Lnhsc3giLCBza2lwID0gMiwgc2hlZXQgPSAiQ1BJMjAxOSIpICAgICAgICAgICAgICAgIA0KUG9wdWxhdGlvbiA8LSByZWFkLmNzdigiUG9wdWxhdGlvbi5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICAgICAgICAgICAgICAgICAgIA0KR2xvYmFsX0Vjb25vbXkgPC0gcmVhZC5jc3YoIlRoZWdsb2JhbGVjb25vbXlfRGF0YS5jc3YiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpICAgICAgIA0KSW50ZXJuZXRfRGF0YSA8LSByZWFkLmNzdigiSW50ZXJuZXRfRGF0YS5jc3YiICwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKSAgICAgICAgICAgICAgDQpEYXRhX0NlbnRlciA8LSByZWFkX3hsc3goIiNvZiBEYXRhIENlbnRlciBieSBDb3VudHJ5Lnhsc3giKQ0KR0NJIDwtIHJlYWRfeGxzeCgiV0VGX0dDSV80LjBfMjAxOV9EYXRhc2V0Lnhsc3giLCBzaGVldCA9ICJEYXRhIiwgc2tpcCA9IDMpDQpgYGANClRoZSBkYXRhIHRoYXQgd2UgY29sbGVjdGVkIGNhbiBiZSBnZW5lcmFsbGl6ZWQgaW50byA1IGNhdGVnb3JpZXM6DQoNCi0gRWNvbm9taWMNCi0gRGVtb2dyYXBoaWMNCi0gUHVibGljIHBvbGljeSBhbmQgSW5zdGl0dXRpb25zDQotIENvbXBldGl0aXZlbmVzcw0KLSBJbmR1c3RyeSBhbmQgTWFya2V0DQoNClRoZSBkYXRhIHRoYXQgd2UgZ2F0aGVyZWQgYXJlIGFsbCBwdWJsaWMgZGF0YSBmcm9tIHZhcmlvdXMgd2Vic2l0ZXMsIGluY2x1ZGluZyB0aGUgV29ybGQgQmFuaywgV29ybGQgRWNvbm9taWMgRm9ydW0gYW5kIHRoZWdsb2JhbGVjb25vbXkuY29tLiAgICAgPC9icj4gIA0KDQpEYXRhIFNvdXJjZXMgOiAgICAgPC9icj4gDQpXb3JsZF9CYW5rIGFuZCBQb3B1bGF0aW9uIGlzIGNvbGxlY3RlZCBmcm9tIHRoZSBbV29ybGQgQmFua10oaHR0cHM6Ly93d3cud29ybGRiYW5rLm9yZy8pLiAgPC9icj4NCkNQSV8yMDE5IGlzIGZyb20gW1RyYW5zcGFyZW5jeSBJbnRlcm5hdGlvbmFsXShodHRwczovL3d3dy50cmFuc3BhcmVuY3kub3JnL2VuL2NwaSkuICA8L2JyPg0KR2xvYmFsX0Vjb25vbXkgYW5kIEludGVybmV0X0RhdGEgYXJlIGZyb20gW1RoZSBHbG9iYWwgRWNvbm9teV0oaHR0cHM6Ly93d3cudGhlZ2xvYmFsZWNvbm9teS5jb20vKS4gIDwvYnI+DQpEYXRhX0NlbnRlciBpcyBmcm9tIFtEYXRhIENlbnRlciBNYXBdKGh0dHBzOi8vd3d3LmRhdGFjZW50ZXJtYXAuY29tLykuICA8L2JyPg0KR0NJIGlzIGZyb20gdGhlIFtXb3JsZCBFY29ub21pYyBGb3J1bV0oaHR0cHM6Ly93d3cud2Vmb3J1bS5vcmcvKS4gIDwvYnI+DQogDQoNCmBgYHtyfQ0KRXhwYW5kX0NvdW50cmllc19MaXN0IDwtIGMoIlVuaXRlZCBTdGF0ZXMiLCAiQXVzdHJhbGlhIiwgIkF1c3RyaWEiLCAiQmVsZ2l1bSIsICJCcmF6aWwiLCAiQ2FuYWRhIiwgIkRlbm1hcmsiLCAiRmlubGFuZCIsICJGcmFuY2UiLCAiR2VybWFueSIsICJJcmVsYW5kIiwgIkl0YWx5IiwgIkphcGFuIiwgIk1leGljbyIsICJOZXRoZXJsYW5kcyIsICJOZXcgWmVhbGFuZCIsICJOb3J3YXkiLCAiUG9ydHVnYWwiLCAiU3BhaW4iLCAiU3dlZGVuIiwgIlN3aXR6ZXJsYW5kIiwgIlVuaXRlZCBLaW5nZG9tIiwgIlNvdXRoIEFmcmljYSIsICJJbmRpYSIsICJVbml0ZWQgQXJhYiBFbWlyYXRlcyIsICJTaW5nYXBvcmUiKQ0KRXhwYW5kX0NvdW50cmllc19MaXN0X3JlIDwtIGMoIlVuaXRlZCBTdGF0ZXMiLCAiQXVzdHJhbGlhIiwgIkF1c3RyaWEiLCAiQmVsZ2l1bSIsICJCcmF6aWwiLCAiQ2FuYWRhIiwgIkRlbm1hcmsiLCAiRmlubGFuZCIsICJGcmFuY2UiLCAiR2VybWFueSIsICJJcmVsYW5kIiwgIkl0YWx5IiwgIkphcGFuIiwgIk1leGljbyIsICJOZXRoZXJsYW5kcyIsICJOZXcgWmVhbGFuZCIsICJOb3J3YXkiLCAiUG9ydHVnYWwiLCAiU3BhaW4iLCAiU3dlZGVuIiwgIlN3aXR6ZXJsYW5kIiwgIlVuaXRlZCBLaW5nZG9tIikNCmBgYA0KRXhwYW5kX0NvdW50cmllc19MaXN0IGlzIGEgdmVjdG9yIG9mIGNvdW50cmllcyB0aGF0IGNvbnRhaW5zIGFsbCB0aGUgY291bnRyaWVzIE1pY3Jvc29mdCBGaW5hbmNpbmcgaGFzIGV4cGFuZGVkIGludG8sIGluY2x1ZGluZyB0aGUgNCBleHBhbnNpb25zIGluIDIwMjAgKFNpbmdhcG9yZSwgVUFFLCBJbmRpYSwgU291dGggQWZyaWNhKSAgIDwvYnI+DQpXZSBhbHNvIHdhbnRlZCB0byBldmFsdWF0ZSB0aGUgY3VycmVudCBleHBhbnNpb25zIGluIDIwMjAsIHNvIHdlIGNyZWF0ZWQgdHdvIHNlcGFydGVkIHZlY3RvcnMgdG8gc3RvcmUgdGhlc2UgY291bnRyaWVzLiAgICA8L2JyPg0KRXhwYW5kX0NvdW50cmllc19MaXN0X3JlIGlzIGEgdmVjdG9yIG9mIGNvdW50cmllcyB0aGF0IGNvbnRhaW5zIGFsbCB0aGUgY291bnRyaWVzIE1pY3Jvc29mdCBGaW5hbmNpbmcgaGFzIGV4cGFuZGVkIGludG8sIHdpdGhvdXQgdGhlIDQgZXhwYW5zaW9ucyBpbiAyMDIwLiAgICA8L2JyPg0KDQojIyBDbGVhbmluZyBEYXRhDQojIyMjIENsZWFuIFdCDQpgYGB7cn0NCldCIDwtIFdvcmxkX0JhbmsNCmhlYWQoV0IpDQp0YWlsKFdCKQ0KIyBEcm9wIGxhc3QgZml2ZSByb3dzDQpXQiA8LSBoZWFkKFdCLCAtNSkNCmBgYA0KV2UgY2FuIHNlZSB0aGF0IGVhY2ggcm93IGlzIGEgY291bnRyeSB3aXRoIGEgZ2l2ZW4gdmFyaWFibGUgKHNlcmllcykuICAgIDwvYnI+DQpUaGUgbGFzdCA1IHJvd3MgYXJlIG5vdCB1c2VmdWwgc28gd2Ugd2lsbCBkcm9wIHRoZW0uIA0KDQpgYGB7cn0NCiMgQ2hhbmdlIGNvbHVtbiBuYW1lcw0KbmFtZXMoV0IpIDwtIGMoIkNvdW50cnlfTmFtZSIsICJDb3VudHJ5X0NvZGUiLCAiU2VyaWVzX05hbWUiLCAiU2VyaWVzX0NvZGUiLCAiMjAxMCIsICIyMDExIiwgIjIwMTIiLCAiMjAxMyIsICIyMDE0IiwgIjIwMTUiLCAiMjAxNiIsICIyMDE3IiwgIjIwMTgiLCAiMjAxOSIpDQpgYGANCg0KTkFzIGFyZSByZWNvcmRlZCBhcyAiLi4iIGFuZCB0aGVyZWZvcmUgb3RoZXIgbnVtZXJpYyB2YWx1ZXMgYXJlIHJlY29yZWRlZCBhcyBjaGFyYWN0ZXJzLiAgICAgPC9icj4NCldlIGxvb3AgdGhyb3VnaCB0aGUgNXRoIHRvIHRoZSAxNHRoIGNvbHVtbnMgdG8gbWFrZSB0aGUgbnVtZXJpYyB2YWx1ZXMgbnVtZXJpYyBhbmQgdGhlIGNoYXJhY3RlcnMgdGhhdCBhcmUgbm90IG51bWVyaWMgaW50byBOQXMuICAgIDwvYnI+DQpgYGB7ciB3YXJuaW5nPUZBTFNFfQ0KV0JbLCA1OjE0XSA8LSBzYXBwbHkoV0JbLCA1OjE0XSwgYXMubnVtZXJpYykNCmBgYA0KDQpgYGB7cn0NCiMgS2VlcCBvbmx5IHRoZSBmaXJzdCAyMTcgY291bnRyaWVzDQpVbmlxdWVfQ291bnRyeV9OYW1lIDwtIHVuaXF1ZShXQiRDb3VudHJ5X05hbWUpDQpVbmlxdWVfQ291bnRyeV9OYW1lIDwtIFVuaXF1ZV9Db3VudHJ5X05hbWVbMToyMTddICAgICMgVGhlIHJlc3Qgb2YgdGhlIENvdW50cnlfTmFtZSBhcmUgcmVnaW9uYWwgc3VjaCBhcyBOb3J0aCBBbWVyaWNhDQpXQiA8LSBXQltXQiRDb3VudHJ5X05hbWUgJWluJSBVbmlxdWVfQ291bnRyeV9OYW1lLCBdICMgS2VlcCBvbmx5IHRoZSBjb3VudHJ5IGxldmVsIGRhdGEgaW4gV0INCmBgYA0KDQpVc2UgdGhlIGNvdW50cnkgbmFtZXMgaW4gV29ybGQgQmFuayB0byBqb2luIGRhdGEgZnJvbSBkaWZmZXJlbmNlIHNvdXJjZSB0b2dldGhlci4gICAgIDwvYnI+DQpXZSB3aWxsIG1hdGNoIGFsbCB0aGUgY291bnRyeSBuYW1lcyBmcm9tIGRhdGEgdXNpbmcgdGhlIGNvdW50cnkgbmFtZXMgdXNlZCBpbiB0aGUgV29ybGQgQmFuay4NCg0KDQojIyMjIENsZWFuIENQSV8yMDE5DQpgYGB7cn0NCmhlYWQoQ1BJXzIwMTkpDQojIFRoZSBsaXN0IG9mIGNvdW50cmllcyB0aGF0IGFyZSBub3QgdXNpbmcgdGhlIGV4YWN0IHNhbWUgbmFtZSBhcyB0aGUgbmFtZXMgdXNlZCBpbiBXQg0KQ1BJXzIwMTkkQ291bnRyeVshQ1BJXzIwMTkkQ291bnRyeSAlaW4lIHVuaXF1ZShXQiRDb3VudHJ5X05hbWUpXQ0KYGBgDQpOZWVkIHRvIGNoYW5nZSB0aGVzZSBuYW1lcyB0byB0aGUgbmFtZXMgdXNlZCBpbiBXQiAgICA8L2JyPg0KTWFudWFsbHkgY2hhbmdlIHRoZSBjb3VudHJ5IG5hbWVzLg0KYGBge3J9DQpDUElfMjAxOSRDb3VudHJ5WyFDUElfMjAxOSRDb3VudHJ5ICVpbiUgdW5pcXVlKFdCJENvdW50cnlfTmFtZSldIDwtIGMoIkhvbmcgS29uZyBTQVIsIENoaW5hIiwgIlVuaXRlZCBTdGF0ZXMiLCAiVGFpd2FuIiwgIkJhaGFtYXMsIFRoZSIsICJLb3JlYSwgUmVwLiIsICJTdC4gVmluY2VudCBhbmQgdGhlIEdyZW5hZGluZXMiLCAiU3QuIEx1Y2lhIiwgIlNsb3ZhayBSZXB1YmxpYyIsICJHYW1iaWEsIFRoZSIsICJFZ3lwdCwgQXJhYiBSZXAuIiwgIkt5cmd5eiBSZXB1YmxpYyIsICJMYW8gUERSIiwgIlJ1c3NpYW4gRmVkZXJhdGlvbiIsICJJcmFuLCBJc2xhbWljIFJlcC4iLCJDb25nbywgUmVwLiIsICJDb25nbywgRGVtLiBSZXAuIiwgIkd1aW5lYS1CaXNzYXUiLCAiS29yZWEsIERlbS4gUGVvcGxl4oCZcyBSZXAuIiwgIlZlbmV6dWVsYSwgUkIiLCAiWWVtZW4sIFJlcC4iLCAiU3lyaWFuIEFyYWIgUmVwdWJsaWMiKQ0KYGBgDQoNCg0KIyMjIyBDbGVhbiBHbG9iYWxfRWNvbm9teSANCmBgYHtyfQ0KaGVhZChHbG9iYWxfRWNvbm9teSkNCiMgVGhlIGNvdW50cnkgbmFtZXMgdGhhdCBhcmUgbm90IGluIFdCLiANCkdsb2JhbF9FY29ub215JENvdW50cnlbIUdsb2JhbF9FY29ub215JENvdW50cnkgJWluJSB1bmlxdWUoV0IkQ291bnRyeV9OYW1lKV0gJT4lIHVuaXF1ZSgpDQpgYGANCk1hbnVhbGx5IGNoZWNrIHRoZSBuYW1lcyB1c2VkIGluIFdCLiBTb21lIG9mIHRoZW0gYXJlIG5vdCBpbiBXQiBzdWNoIGFzIFRhaXdhbiBhbmQgUGFsZXN0aW5lLCBqdXN0IGtlZXAgdGhlbSBhcyB0aGV5IGFyZS4gDQpgYGB7cn0NCkdFX25hbWUgPC0gR2xvYmFsX0Vjb25vbXkkQ291bnRyeVshR2xvYmFsX0Vjb25vbXkkQ291bnRyeSAlaW4lIHVuaXF1ZShXQiRDb3VudHJ5X05hbWUpXSAlPiUgdW5pcXVlKCkNCldCX25hbWUgPC0gYygiQmFoYW1hcywgVGhlIiwgIkJydW5laSBEYXJ1c3NhbGFtIiwgIk15YW5tYXIiLCAiQ2FibyBWZXJkZSIsICJDb25nbywgRGVtLiBSZXAuIiwgIkVneXB0LCBBcmFiIFJlcC4iLCAiR2FtYmlhLCBUaGUiLCAiSG9uZyBLb25nIFNBUiwgQ2hpbmEiLCAiSXJhbiwgSXNsYW1pYyBSZXAuIiwgIkNvdGUgZCdJdm9pcmUiLCAiS3lyZ3l6IFJlcHVibGljIiwgIkxhbyBQRFIiLCAgIk1hY2FvIFNBUiwgQ2hpbmEiLCAiTm9ydGggTWFjZWRvbmlhIiwgIk1pY3JvbmVzaWEsIEZlZC4gU3RzLiIsICAiS29yZWEsIERlbS4gUGVvcGxl4oCZcyBSZXAuIiwgIlBhbGVzdGluZSIsICJDb25nbywgUmVwLiIsICJSdXNzaWFuIEZlZGVyYXRpb24iLCAiU3QuIEx1Y2lhIiwgIlN0LiBWaW5jZW50IGFuZCB0aGUgR3JlbmFkaW5lcyIsICJTbG92YWsgUmVwdWJsaWMiLCAiS29yZWEsIFJlcC4iLCAiRXN3YXRpbmkiLCAiU3lyaWFuIEFyYWIgUmVwdWJsaWMiLCAiVGFpd2FuIiwgIlVuaXRlZCBTdGF0ZXMiLCAiVmVuZXp1ZWxhLCBSQiIsICJZZW1lbiwgUmVwLiIpDQpsZW5ndGgoR0VfbmFtZSkgPT0gbGVuZ3RoKFdCX25hbWUpICAjIENoZWNrIGlmIHRoZSB0d28gdmVjdG9ycyBoYXZlIHRoZSBzYW1lIGxlbmd0aA0KYGBgDQpCZWNhdXNlIHRoZXJlIGFyZSBtdWx0aXBsZSByb3dzIGZvciBhIGNvdW50cnksIHNvIGNoYW5nZSB0aGUgbmFtZXMgdXNpbmcgYSBsb29wIA0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KEdsb2JhbF9FY29ub215KSkgew0KICBpZighR2xvYmFsX0Vjb25vbXkkQ291bnRyeVtpXSAlaW4lIEdFX25hbWUpIHsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIElmIHRoZSBjb3VudHJ5IG5hbWUgaXMgdGhlIHNhbWUgYXMgV0INCiAgICBuZXh0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBEbyBub3QgY2hhbmdlIG5hbWUNCiAgfSBlbHNlIHsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBFbHNlICh0aGUgY291bnRyeSBuYW1lIGlzIGRpZmZlcmVudCkNCiAgICBHbG9iYWxfRWNvbm9teSRDb3VudHJ5W2ldIDwtIFdCX25hbWVbR2xvYmFsX0Vjb25vbXkkQ291bnRyeVtpXSA9PSBHRV9uYW1lXSAgICAgIyBVc2UgdGhlIG5hbWUgaW4gV0INCiAgfQ0KfQ0KYGBgDQoNCkNoYW5nZSBjb2x1bW4gbmFtZXMNCmBgYHtyfQ0KbmFtZXMoR2xvYmFsX0Vjb25vbXkpIDwtIGMoIkNvdW50cnkiLCAiQ29kZSIsICJZZWFyIiwgIlJ1bGVfb2ZfbGF3X2luZGV4IiwgIkdvdmVybm1lbnRfZWZmZWN0aXZlbmVzc19pbmRleCIsICJDb250cm9sX29mX2NvcnJ1cHRpb24iLCAiUmVndWxhdG9yeV9xdWFsaXR5X2luZGV4IikNCm5hbWVzKEdsb2JhbF9FY29ub215KQ0KYGBgDQoNCiMjIyMgQ2xlYW4gRGF0YV9DZW50ZXINCk5lZWQgdG8gY2hhbmdlIHRoZXNlIG5hbWVzIHRvIHRoZSBuYW1lcyB1c2VkIGluIFdCDQpgYGB7cn0NCkRhdGFfQ2VudGVyJENvdW50cnlbIURhdGFfQ2VudGVyJENvdW50cnkgJWluJSBXQiRDb3VudHJ5X05hbWVdDQpgYGANCg0KYGBge3J9DQpEYXRhX0NlbnRlciRDb3VudHJ5WyFEYXRhX0NlbnRlciRDb3VudHJ5ICVpbiUgV0IkQ291bnRyeV9OYW1lXSA8LSBjKCJVbml0ZWQgU3RhdGVzIiwgIk5ldGhlcmxhbmRzIiwgIkhvbmcgS29uZyBTQVIsIENoaW5hIiwgIlJ1c3NpYW4gRmVkZXJhdGlvbiIsICJJcmFuLCBJc2xhbWljIFJlcC4iLCAiS29yZWEsIFJlcC4iLCAiU2xvdmFrIFJlcHVibGljIiwgIkVneXB0LCBBcmFiIFJlcC4iLCAiVGFpd2FuIiwgIkplcnNleSIsICJOb3J0aCBNYWNlZG9uaWEiLCAiVmVuZXp1ZWxhLCBSQiIsICJHdWVybnNleSIsICJJc2xlIG9mIE1hbiIsICJCYWhhbWFzLCBUaGUiLCAiQm9zbmlhIGFuZCBIZXJ6ZWdvdmluYSIsICJDb25nbywgRGVtLiBSZXAuIiwgIk1hY2FvIFNBUiwgQ2hpbmEiLCAiTmV0aGVybGFuZHMgQW50aWxsZXMiLCAiUGFsZXN0aW5lIiwgIlJldW5pb24iLCAiVHJpbmlkYWQgYW5kIFRvYmFnbyIsICJWaXJnaW4gSXNsYW5kcyAoVS5TLikiKQ0KIyAiVGFpd2FuIiwgIkplcnNleSIsICJHdWVybnNleSIsICJOZXRoZXJsYW5kcyBBbnRpbGxlcyIsICJQYWxlc3RpbmUiLCAiUmV1bmlvbiIgYXJlIG5vdCBpbiBXQg0KYGBgDQoNCiMjIyMgQ2xlYW4gSW50ZXJuZXRfRGF0YQ0KYGBge3J9DQpmb3IgKGkgaW4gMTpucm93KEludGVybmV0X0RhdGEpKSB7DQogIGlmKCFJbnRlcm5ldF9EYXRhJENvdW50cnlbaV0gJWluJSBHRV9uYW1lKSB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBJZiB0aGUgY291bnRyeSBuYW1lIGlzIHRoZSBzYW1lIGFzIFdCDQogICAgSW50ZXJuZXRfRGF0YSRDb3VudHJ5W2ldIDwtIEludGVybmV0X0RhdGEkQ291bnRyeVtpXSAgICAgICAgICAgICAgICAgICAgICAgICAjIERvIG5vdCBjaGFuZyBuYW1lDQogIH0gZWxzZSB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgRWxzZSAodGhlIGNvdW50cnkgbmFtZSBpcyBkaWZmZXJlbnQpDQogICAgSW50ZXJuZXRfRGF0YSRDb3VudHJ5W2ldIDwtIFdCX25hbWVbSW50ZXJuZXRfRGF0YSRDb3VudHJ5W2ldID09IEdFX25hbWVdICAgICAjIFVzZSB0aGUgbmFtZSBpbiBXQg0KICB9DQp9DQpgYGANCg0KIyMgVmFyaWFibGVzDQpWYXJpYWJsZXMgdGhhdCB3ZSB1c2UgYXJlOiBQb3B1bGF0aW9uLCBHRFAoMjAxOCwgQ3VycmVudCBVU1wkKSwgR0RQIEdyb3d0aCAoMjAxOCwgJSksIEluZmxhdGlvbiBSYXRlKDIwMTgsICUpLCBOdW1iZXIgb2YgRGF0YSBDZW50ZXJzICgyMDE4LCAjKSwgTnVtYmVyIG9mIFNlY3VyZSBJbnRlcm5ldCBTZXJ2ZXJzKDIwMTgsICMpLCBQZXJjZW50YWdlIG9mIEludGVybmV0IFVzZXJzIG9mIFBvcHVsYXRpb24gKDIwMTcsICUpLCBSdWxlIG9mIGxhdyBpbmRleCgyMDE4LCAtMi41IH4gMi41KSwgR292ZXJubWVudCBlZmZlY3RpdmVuZXNzIGluZGV4KDIwMTgsIC0yLjUgfiAyLjUpLCBDb250cm9sIG9mIGNvcnJ1cHRpb24oMjAxOCwgLTIuNSB+IDIuNSksIFJlZ3VsYXRvcnkgcXVhbGl0eSBpbmRleCgyMDE4LCAtMi41IH4gMi41KSwgRWFzZSBvZiBkb2luZyBidXNpbmVzcygyMDE5LCBSYW5raW5nKSwgR2xvYmFsIENvbXBldGl0aXZlbmVzcyBJbmRleCBTY29yZSAoMjAxOSwgMC0xMDApLCB0aGUgMTIgcGlsbGFycyBpbiBHQ0k6IEluc3RpdHV0aW9ucywgSW5mcmFzdHJ1Y3R1cmUsIElDVCBhZG9wdGlvbiwgTWFjcm9lY29ub21pYyBzdGFiaWxpdHksIEhlYWx0aCwgU2tpbGxzLCBQcm9kdWN0IG1hcmtldCwgTGFib3VyIG1hcmtldCwgRmluYW5jaWFsIHN5c3RlbSwgTWFya2V0IHNpemUsIEJ1c2luZXNzIGR5bmFtaXNtLCBJbm5vdmF0aW9uIGNhcGFiaWxpdHkgKDIwMTksIDAtMTAwKQ0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgICBWYXJpYWJsZXMgICAgICAgICAgICAgIHwgICAgIFllYXIgICAgICB8ICAgIFNjYWxlICAgICAgfCAgICAgICAgU291cmNlICAgICAgICAgICAgIHwgICAgDQorPT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PSs9PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT09PT09PT09Kw0KfCAgICBHRFAgICAgICAgICAgICAgICAgICAgIHwgICAyMDE4ICAgICAgICB8ICAgaW4gICBVUyQgICAgfCAgIFdvcmxkIEJhbmsgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICAgR0RQIEdyb3d0aCAgICAgICAgICAgICAgfCAgICAyMDE4ICAgICAgIHwgICAlICAgICAgICAgICB8ICAgV29ybGQgQmFuayAgICAgICAgICAgICAgfCANCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICAgRlggUmF0ZSAgICAgICAgICAgICAgICAgfCAgICAyMDE4ICAgICAgIHwgICAgaW4gVVMkICAgICB8ICAgV29ybGQgQmFuayAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICBJbmZsYXRpb24gICAgICAgICAgICAgICB8ICAgIDIwMTggICAgICAgfCAgICUgICAgICAgICAgIHwgICBXb3JsZCBCYW5rICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgICMgb2YgRGF0YSBDZW50ZXJzICAgICAgIHwgICAgMjAxOCAgICAgICB8ICAgIyAgICAgICAgICAgfCAgIGRhdGFjZW50ZXJtYXAuY29tICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICAgIyBvZiBJbnRlcm5ldCBTZXJ2ZXJzICAgfCAgICAyMDE4ICAgICAgIHwgICAjICAgICAgICAgICB8ICAgV29ybGQgQmFuayAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgICAlIEludGVybmV0IFVzZXJzICAgICAgICB8ICAgIDIwMTcgICAgICAgfCAgJSBwb3B1bGF0aW9uIHwgICB0aGVnbG9iYWxlY29ub215LmNvbSAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgICBQb3B1bGF0aW9uICAgICAgICAgICAgIHwgICAgMjAxOCAgICAgICB8ICAgIyAgICAgICAgICAgfCAgIFdvcmxkIEJhbmsgICAgICAgICAgICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8ICAgUnVsZSBvZiBsYXcgICAgICAgICAgICAgfCAgICAyMDE4ICAgICAgIHwgIC0yLjUgfiAyLjUgICB8ICAgdGhlZ2xvYmFsZWNvbm9teS5jb20gICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgR292ZXJubWVudCBlZmZlY3RpdmVuZXNzICB8ICAgIDIwMTggICAgICAgfCAgLTIuNSB+IDIuNSAgIHwgICB0aGVnbG9iYWxlY29ub215LmNvbSAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCAgUmVndWxhdG9yeSBxdWFsaXR5ICAgICAgIHwgICAgMjAxOCAgICAgICB8ICAtMi41IH4gMi41ICAgfCAgIHRoZWdsb2JhbGVjb25vbXkuY29tICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEVhc2Ugb2YgZG9pbmcgYnVzaW5lc3MgICAgfCAgICAyMDE4ICAgICAgIHwgIFJhbmtpbmcgICAgICB8ICAgV29ybGQgQmFuayAgICAgICAgICAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgQ29tcGV0aXRpdmVuZXNzIEluZGV4ICAgICB8ICAgIDIwMTkgICAgICAgfCAgMCB+IDEwMCAgICAgIHwgICBXb3JsZCBFY29ub215IEZvcnVtICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBJbnN0aXR1dGlvbnMgICAgICAgICAgICAgIHwgICAgMjAxOSAgICAgICB8ICAwIH4gMTAwICAgICAgfCAgIFdvcmxkIEVjb25vbXkgRm9ydW0gICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEluZnJhc3RydWN0dXJlICAgICAgICAgICAgfCAgICAyMDE5ICAgICAgIHwgICAwIH4gMTAwICAgICB8ICAgV29ybGQgRWNvbm9teSBGb3J1bSAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgSUNUIGFkb3B0aW9uICAgICAgICAgICAgICB8ICAgIDIwMTkgICAgICAgfCAgMCB+IDEwMCAgICAgIHwgICBXb3JsZCBFY29ub215IEZvcnVtICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBNYWNyb2Vjb25vbWljIHN0YWJpbGl0eSAgIHwgICAgMjAxOSAgICAgICB8ICAwIH4gMTAwICAgICAgfCAgIFdvcmxkIEVjb25vbXkgRm9ydW0gICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEhlYWx0aCAgICAgICAgICAgICAgICAgICAgfCAgICAyMDE5ICAgICAgIHwgIDAgfiAxMDAgICAgICB8ICAgV29ybGQgRWNvbm9teSBGb3J1bSAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgU2tpbGxzICAgICAgICAgICAgICAgICAgICB8ICAgIDIwMTkgICAgICAgfCAgMCB+IDEwMCAgICAgIHwgICBXb3JsZCBFY29ub215IEZvcnVtICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBQcm9kdWN0IG1hcmtldCAgICAgICAgICAgIHwgICAgMjAxOSAgICAgICB8ICAwIH4gMTAwICAgICAgfCAgIFdvcmxkIEVjb25vbXkgRm9ydW0gICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IExhYm91ciBtYXJrZXQgICAgICAgICAgICAgfCAgICAyMDE5ICAgICAgIHwgIDAgfiAxMDAgICAgICB8ICAgV29ybGQgRWNvbm9teSBGb3J1bSAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgRmluYW5jaWFsIHN5c3RlbSAgICAgICAgICB8ICAgIDIwMTkgICAgICAgfCAgMCB+IDEwMCAgICAgIHwgICBXb3JsZCBFY29ub215IEZvcnVtICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KfCBNYXJrZXQgc2l6ZSAgICAgICAgICAgICAgIHwgICAgMjAxOSAgICAgICB8ICAwIH4gMTAwICAgICAgfCAgIFdvcmxkIEVjb25vbXkgRm9ydW0gICAgIHwNCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IEJ1c2luZXNzIGR5bmFtaXNtICAgICAgICAgfCAgICAyMDE5ICAgICAgIHwgIDAgfiAxMDAgICAgICB8ICAgV29ybGQgRWNvbm9teSBGb3J1bSAgICAgfA0KKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsNCnwgSW5ub3ZhdGlvbiBjYXBhYmlsaXR5ICAgICB8ICAgIDIwMTkgICAgICAgfCAgMCB+IDEwMCAgICAgIHwgICBXb3JsZCBFY29ub215IEZvcnVtICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKw0KDQoNCiMjIyMgUG9wdWxhdGlvbg0KRnJvbSB3YnN0YXRzIFIgcGFja2FnZS4gQWxyZWFkeSBjbGVhbmVkLiANClVzZSAyMDE4IHBvcHVsYXRpb24uIA0KYGBge3J9DQpQb3B1bGF0aW9uXzIwMTggPC0gUG9wdWxhdGlvbiAlPiUgDQogIHNlbGVjdChDb3VudHJ5X05hbWUsIFBvcHVsYXRpb25fMjAxOCkNCmBgYA0KDQoNCiMjIyMgR0RQDQpGcm9tIFdvcmxkIEJhbmsuIFVzZSAyMDE4IGRhdGEgYmVjYXVzZSB3ZSBzdGlsbCBkb24ndCBoYXZlIDIwMTkgZGF0YSB5ZXQuIA0KYGBge3J9DQpHRFBfMjAxOCA8LSBXQiAlPiUNCiAgZmlsdGVyKFNlcmllc19OYW1lID09ICJHRFAgKGN1cnJlbnQgVVMkKSIpICU+JQ0KICBtdXRhdGUoR0RQXzIwMTggPSBgMjAxOGApICU+JQ0KICBzZWxlY3QoQ291bnRyeV9OYW1lLCBHRFBfMjAxOCkNCmBgYA0KDQojIyMjIEdEUCBHcm93dGgNCkZyb20gV29ybGQgQmFuay4gVXNlIDIwMTggZGF0YSBiZWNhdXNlIHdlIHN0aWxsIGRvbid0IGhhdmUgMjAxOSBkYXRhIHlldC4gDQpgYGB7cn0NCkdEUF9Hcm93dGhfMjAxOCA8LSBXQiAlPiUNCiAgZmlsdGVyKFNlcmllc19OYW1lID09ICJHRFAgZ3Jvd3RoIChhbm51YWwgJSkiKSAlPiUNCiAgcmVuYW1lKEdEUF9Hcm93dGhfMjAxOCA9IGAyMDE4YCkgJT4lDQogIHNlbGVjdChDb3VudHJ5X05hbWUsIEdEUF9Hcm93dGhfMjAxOCkNCmBgYA0KDQojIyMjIEluZmxhdGlvbiBSYXRlIChObyBJcmFuIGFuZCBTeXJpYSkgDQpgYGB7cn0NCkluZmxhdGlvbl9SYXRlXzIwMTg8LSBXQiAlPiUNCiAgZmlsdGVyKFNlcmllc19OYW1lID09ICJJbmZsYXRpb24sIEdEUCBkZWZsYXRvciAoYW5udWFsICUpIikgJT4lDQogIG11dGF0ZShJbmZsYXRpb25fUmF0ZV8yMDE4ID0gYDIwMThgKSAlPiUNCiAgc2VsZWN0KENvdW50cnlfTmFtZSwgSW5mbGF0aW9uX1JhdGVfMjAxOCkNCmBgYA0KDQoNCiMjIyMgTnVtYmVyIG9mIERhdGEgQ2VudGVycyAoY2xlYW5lZCkNCg0KDQojIyMjIE51bWJlciBvZiBTZWN1cmUgSW50ZXJuZXQgU2VydmVycyAoY2xlYW5lZCkNCmBgYHtyfQ0KSW50ZXJuZXRfc2VydmVyc18yMDE4IDwtIFdCICU+JQ0KICBmaWx0ZXIoU2VyaWVzX05hbWUgPT0gIlNlY3VyZSBJbnRlcm5ldCBzZXJ2ZXJzIikgJT4lDQogIG11dGF0ZShJbnRlcm5ldF9zZXJ2ZXJzXzIwMTggPSBgMjAxOGApICU+JQ0KICBzZWxlY3QoQ291bnRyeV9OYW1lLCBJbnRlcm5ldF9zZXJ2ZXJzXzIwMTgpDQpgYGANCg0KIyMjIyBQZXJjZW50YWdlIG9mIEludGVybmV0IFVzZXJzIDIwMTcNClRoZXJlJ3MgbW9yZSBOQXMgaW4gMjAxOCBEYXRhDQpgYGB7cn0NCkludGVybmV0X1VzZXJzXzIwMTcgPC0gSW50ZXJuZXRfRGF0YSAlPiUNCiAgZmlsdGVyKFllYXIgPT0gMjAxNykgJT4lDQogIHNlbGVjdChDb3VudHJ5LCBJbnRlcm5ldC51c2Vycy5wZXJjZW50Lm9mLnBvcHVsYXRpb24pDQpgYGANCg0KIyMjIyBDb3JydXB0aW9uDQpgYGB7cn0NCkNvcnJ1cHRpb25fMjAxOSA8LSBDUElfMjAxOSAlPiUNCiAgc2VsZWN0KENvdW50cnksIGBDUEkgc2NvcmUgMjAxOWApDQpgYGANCg0KDQojIyMjIFN0YWJpbGl0eV8yMDE4OiBSdWxlX29mX2xhd19pbmRleCwgR292ZXJubWVudF9lZmZlY3RpdmVuZXNzX2luZGV4LCBDb250cm9sX29mX2NvcnJ1cHRpb24sIFJlZ3VsYXRvcnlfcXVhbGl0eV9pbmRleA0KYGBge3J9DQpTdGFiaWxpdHlfMjAxOCA8LSBHbG9iYWxfRWNvbm9teSAlPiUNCiAgZmlsdGVyKFllYXIgPT0gMjAxOCkgJT4lDQogIHNlbGVjdCgtYyhDb2RlLCBZZWFyKSkNClN0YWJpbGl0eV8yMDE4DQpgYGANCg0KIyMjIyBFYXNlIG9mIGRvaW5nIGJ1c2luZXNzDQpgYGB7cn0NCkVhc2Vfb2ZfZG9pbmdfYnVzaW5lc3NfMjAxOSA8LSBXQiAlPiUNCiAgZmlsdGVyKFNlcmllc19OYW1lID09ICJFYXNlIG9mIGRvaW5nIGJ1c2luZXNzIGluZGV4ICgxPW1vc3QgYnVzaW5lc3MtZnJpZW5kbHkgcmVndWxhdGlvbnMpIikgJT4lDQogIG11dGF0ZShFYXNlX29mX2RvaW5nX2J1c2luZXNzXzIwMTkgPSBgMjAxOWApICU+JQ0KICBzZWxlY3QoQ291bnRyeV9OYW1lLCBFYXNlX29mX2RvaW5nX2J1c2luZXNzXzIwMTkpDQpgYGANCiEhISBJcyBSYW5rZWQgRGF0YSwgbWVhbmluZyB0aGF0IGxvd2VyIHRoZSBudW1iZXIsIHRoZSBiZXR0ZXIgdGhlIGNvdW50cnkgISEhIA0KDQpgYGB7cn0NCkVhc2Vfb2ZfZG9pbmdfYnVzaW5lc3NfMjAxOSA8LSBFYXNlX29mX2RvaW5nX2J1c2luZXNzXzIwMTkgJT4lDQogIGFycmFuZ2UoRWFzZV9vZl9kb2luZ19idXNpbmVzc18yMDE5KSAlPiUNCiAgbXV0YXRlKFJldmVyZXNlX1JhbmtpbmcgPSAxOTEgLSBFYXNlX29mX2RvaW5nX2J1c2luZXNzXzIwMTkpICU+JQ0KICBzZWxlY3QoQ291bnRyeV9OYW1lLCBSZXZlcmVzZV9SYW5raW5nKSAlPiUNCiAgbXV0YXRlKEVhc2Vfb2ZfZG9pbmdfYnVzaW5lc3NfMjAxOSA9IFJldmVyZXNlX1JhbmtpbmcpICU+JQ0KICBzZWxlY3QoQ291bnRyeV9OYW1lLCBFYXNlX29mX2RvaW5nX2J1c2luZXNzXzIwMTkpDQpgYGANClJldmVyc2UgdGhlIHJhbmtpbmcgc28gaGlnaGVyIGluIHJhbmsgaGFzIGEgaGlnaGVyIHNjb3JlDQoNCg0KIyMjIyBHQ0kNCmBgYHtyfQ0KR0NJX3Njb3JlXzIwMTkgPC0gR0NJICU+JQ0KICBmaWx0ZXIoRWRpdGlvbiA9PSAyMDE5LCBgU2VyaWVzIG5hbWVgID09ICJHbG9iYWwgQ29tcGV0aXRpdmVuZXNzIEluZGV4IDQuMCIsIEF0dHJpYnV0ZSA9PSAiU0NPUkUiKSAlPiUNCiAgc2VsZWN0KC1jKEluZGV4LCBFZGl0aW9uLCBgU2VyaWVzIEdsb2JhbCBJRGAsYFNlcmllcyBuYW1lYCwgYEZyZWV6ZSBkYXRlYCwgYFNlcmllcyB1bml0c2AsIGBTZXJpZXMgb3JkZXJgLCBgU2VyaWVzIGNvZGUgKGlmIGFwcGxpY2FibGUpYCwgYFNlcmllcyB0eXBlYCwgQXR0cmlidXRlKSkgJT4lDQogIHQoKSAlPiUNCiAgYXMubWF0cml4KHJvd25hbWVzLmZvcmNlID0gVFJVRSkgJT4lDQogIGFzLmRhdGEuZnJhbWUoKQ0KR0NJX3Njb3JlXzIwMTkgPC0gY2JpbmQocm93bmFtZXMoR0NJX3Njb3JlXzIwMTkpLCBHQ0lfc2NvcmVfMjAxOSkNCm5hbWVzKEdDSV9zY29yZV8yMDE5KSA8LSBjKCJDb3VudHJ5X05hbWUiLCAiR0NJXzIwMTkiKQ0Kcm93bmFtZXMoR0NJX3Njb3JlXzIwMTkpIDwtIGMoKQ0KR0NJX3Njb3JlXzIwMTkkQ291bnRyeV9OYW1lIDwtIGFzLmNoYXJhY3RlcihHQ0lfc2NvcmVfMjAxOSRDb3VudHJ5X05hbWUpDQpHQ0lfc2NvcmVfMjAxOSRHQ0lfMjAxOSA8LSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihHQ0lfc2NvcmVfMjAxOSRHQ0lfMjAxOSkpDQpHQ0lfc2NvcmVfMjAxOSA8LSBoZWFkKEdDSV9zY29yZV8yMDE5LCAtMTIpICMgZ2V0IHJpZCBvZiB0aGUgbGFzdCAxMiByb3dzIChyZWdpb25hbCBkYXRhKQ0KR0NJX3Njb3JlXzIwMTkkQ291bnRyeV9OYW1lWyFHQ0lfc2NvcmVfMjAxOSRDb3VudHJ5X05hbWUgJWluJSB1bmlxdWUoV0IkQ291bnRyeV9OYW1lKV0gPC0gYygiQ290ZSBkJ0l2b2lyZSIsICJDb25nbywgRGVtLiBSZXAuIiwgIkNhYm8gVmVyZGUiLCAiRWd5cHQsIEFyYWIgUmVwLiIsICJIb25nIEtvbmcgU0FSLCBDaGluYSIsICJOb3J0aCBNYWNlZG9uaWEiLCAiVGFpd2FuIiwgIlZlbmV6dWVsYSwgUkIiLCAiVmlldG5hbSIsICJZZW1lbiwgUmVwLiIpDQpgYGANCg0KMTIgUGlsbGFycyBpbiBHQ0kNCg0KTmFtZXMgb2YgdGhlIDEyIHBpbGxhcnMNCmBgYHtyfQ0KUGlsbGFyc18xMiA8LSBHQ0kgJT4lIA0KICBmaWx0ZXIoRWRpdGlvbiA9PSAyMDE5LCBgU2VyaWVzIHR5cGVgID09ICJQaWxsYXIiLCBBdHRyaWJ1dGUgPT0gIlNDT1JFIikgJT4lDQogIHNlbGVjdChgU2VyaWVzIG5hbWVgKSAlPiUNCiAgLiRgU2VyaWVzIG5hbWVgDQpgYGANCg0KYGBge3J9DQpHQ0lfcGlsbGFycyA8LSBHQ0kgJT4lIA0KICBmaWx0ZXIoRWRpdGlvbiA9PSAyMDE5LCBgU2VyaWVzIHR5cGVgID09ICJQaWxsYXIiLCBBdHRyaWJ1dGUgPT0gIlNDT1JFIikgICU+JQ0KICBzZWxlY3QoLWMoSW5kZXgsIEVkaXRpb24sIGBTZXJpZXMgR2xvYmFsIElEYCxgU2VyaWVzIG5hbWVgLCBgRnJlZXplIGRhdGVgLCBgU2VyaWVzIHVuaXRzYCwgYFNlcmllcyBvcmRlcmAsIGBTZXJpZXMgY29kZSAoaWYgYXBwbGljYWJsZSlgLCBgU2VyaWVzIHR5cGVgLCBBdHRyaWJ1dGUpKSAlPiUNCiAgdCgpICU+JQ0KICBhcy5kYXRhLmZyYW1lKCkNCkdDSV9waWxsYXJzIDwtIGNiaW5kKHJvd25hbWVzKEdDSV9waWxsYXJzKSwgR0NJX3BpbGxhcnMpDQpuYW1lcyhHQ0lfcGlsbGFycykgPC0gYygiQ291bnRyeV9OYW1lIiwgUGlsbGFyc18xMikNCnJvd25hbWVzKEdDSV9waWxsYXJzKSA8LSBjKCkNCkdDSV9waWxsYXJzJENvdW50cnlfTmFtZSA8LSBhcy5jaGFyYWN0ZXIoR0NJX3BpbGxhcnMkQ291bnRyeV9OYW1lKQ0KR0NJX3BpbGxhcnNbLCAyOjEzXTwtIHNhcHBseShHQ0lfcGlsbGFyc1ssIDI6MTNdLCBhcy5jaGFyYWN0ZXIpDQpHQ0lfcGlsbGFyc1ssIDI6MTNdPC0gc2FwcGx5KEdDSV9waWxsYXJzWywgMjoxM10sIGFzLm51bWVyaWMpDQpHQ0lfcGlsbGFycyA8LSBoZWFkKEdDSV9waWxsYXJzLCAtMTIpICMgZ2V0IHJpZCBvZiB0aGUgbGFzdCAxMiByb3dzIChyZWdpb25hbCBkYXRhKQ0KR0NJX3BpbGxhcnMkQ291bnRyeV9OYW1lWyFHQ0lfcGlsbGFycyRDb3VudHJ5X05hbWUgJWluJSB1bmlxdWUoV0IkQ291bnRyeV9OYW1lKV0gPC0gYygiQ290ZSBkJ0l2b2lyZSIsICJDb25nbywgRGVtLiBSZXAuIiwgIkNhYm8gVmVyZGUiLCAiRWd5cHQsIEFyYWIgUmVwLiIsICJIb25nIEtvbmcgU0FSLCBDaGluYSIsICJOb3J0aCBNYWNlZG9uaWEiLCAiVGFpd2FuIiwgIlZlbmV6dWVsYSwgUkIiLCAiVmlldG5hbSIsICJZZW1lbiwgUmVwLiIpDQpgYGANCg0KIyMgSm9pbmluZyBEYXRhZnJhbWVzOiANCkdEUF8yMDE4LCBQb3B1bGF0aW9uXzIwMTgsIEdEUF9Hcm93dGhfMjAxOCwgSW5mbGF0aW9uX1JhdGVfMjAxOCwgRlhfUmF0ZV8yMDE4LCBEYXRhX0NlbnRlciwgSW50ZXJuZXRfc2VydmVyc18yMDE4LCBJbnRlcm5ldF9Vc2Vyc18yMDE3LCBTdGFiaWxpdHlfMjAxOCwgRWFzZV9vZl9kb2luZ19idXNpbmVzc18yMDE5LCBHQ0lfc2NvcmVfMjAxOSwgR0NJX3BpbGxhcnMNCmBgYHtyfQ0KQ2x1c3RlcmluZ19kZiA8LSBHRFBfMjAxOCAlPiUgDQogIGxlZnRfam9pbihQb3B1bGF0aW9uXzIwMTgsIGJ5ID0gYygiQ291bnRyeV9OYW1lIikpICU+JQ0KICBsZWZ0X2pvaW4oR0RQX0dyb3d0aF8yMDE4LCBieSA9IGMoIkNvdW50cnlfTmFtZSIpKSAlPiUNCiAgbGVmdF9qb2luKEluZmxhdGlvbl9SYXRlXzIwMTgsIGJ5ID0gYygiQ291bnRyeV9OYW1lIikpICU+JQ0KICBsZWZ0X2pvaW4oRGF0YV9DZW50ZXIsIGJ5ID0gYygiQ291bnRyeV9OYW1lIiA9ICJDb3VudHJ5IikpICU+JQ0KICBsZWZ0X2pvaW4oSW50ZXJuZXRfc2VydmVyc18yMDE4LCBieSA9IGMoIkNvdW50cnlfTmFtZSIpKSAlPiUNCiAgbGVmdF9qb2luKEludGVybmV0X1VzZXJzXzIwMTcsIGJ5ID0gYygiQ291bnRyeV9OYW1lIiA9ICJDb3VudHJ5IikpICU+JQ0KICBsZWZ0X2pvaW4oU3RhYmlsaXR5XzIwMTgsIGJ5ID0gYygiQ291bnRyeV9OYW1lIiA9ICJDb3VudHJ5IikpICU+JQ0KICBsZWZ0X2pvaW4oRWFzZV9vZl9kb2luZ19idXNpbmVzc18yMDE5LCBieSA9IGMoIkNvdW50cnlfTmFtZSIpKSAlPiUNCiAgbGVmdF9qb2luKEdDSV9zY29yZV8yMDE5LCBieSA9IGMoIkNvdW50cnlfTmFtZSIpKSAlPiUNCiAgbGVmdF9qb2luKEdDSV9waWxsYXJzLCBieSA9IGMoIkNvdW50cnlfTmFtZSIpKQ0KYGBgDQoNCklucHV0ZSBtaXNzaW5nIGRhdGEgY2VudGVycyBhcyAwcy4gDQpgYGB7cn0NCkNsdXN0ZXJpbmdfZGZbaXMubmEoQ2x1c3RlcmluZ19kZiRgTnVtYmVyIG9mIERhdGEgQ2VudGVyYCksIF0kYE51bWJlciBvZiBEYXRhIENlbnRlcmAgPC0gMA0KYGBgDQoNCiMjIyMgQXNzdW1lIHRoYXQgY291bnRyaWVzIHRoYXQgaGF2ZSBtaXNzaW5nIHZhbHVlcyBhcmUgY291bnRyaWVzIE1pY3Jvc29mdCBzaG91bGQgbm90IGV4cGFuZCBpbnRvLiANCkNoZWNrIHRoZSBjb3VudHJpZXMgd2l0aCBtaXNzaW5nIHZhbHVlcw0KYGBge3J9DQojIExpc3Qgb2YgY291bnRyaWVzIHdpdGggbWlzc2luZyB2YWx1ZXMNCkNsdXN0ZXJpbmdfZGZbIWNvbXBsZXRlLmNhc2VzKENsdXN0ZXJpbmdfZGYpLCBdJENvdW50cnlfTmFtZQ0KYGBgDQoNCmBgYHtyfQ0KIyBLZWVwIGNvdW50cmllcyhyb3dzKSB0aGF0IGhhdmUgbm8gbWlzc2luZyB2YWx1ZXMNCkNsdXN0ZXJpbmdfZGYgPC0gQ2x1c3RlcmluZ19kZltjb21wbGV0ZS5jYXNlcyhDbHVzdGVyaW5nX2RmKSwgXQ0KQ2x1c3RlcmluZ19kZg0KYGBgDQpBZGQgRXhwYW5kIENvbHVtbg0KYGBge3J9DQpDbHVzdGVyaW5nX2RmJEV4cGFuZCA8LSBpZmVsc2UoQ2x1c3RlcmluZ19kZiRDb3VudHJ5X05hbWUgJWluJSBFeHBhbmRfQ291bnRyaWVzX0xpc3QsIFRSVUUsIEZBTFNFKQ0KQ2x1c3RlcmluZ19kZiRFeHBhbmRfcmUgPC0gaWZlbHNlKENsdXN0ZXJpbmdfZGYkQ291bnRyeV9OYW1lICVpbiUgRXhwYW5kX0NvdW50cmllc19MaXN0X3JlLCBUUlVFLCBGQUxTRSkNCmBgYA0KDQpOb3JtYWxpemUgQ2x1c3RlcmluZ19kZg0KYGBge3J9DQpDbHVzdGVyaW5nX2RmX3ByZXByb2MgPC0gcHJlUHJvY2VzcyhDbHVzdGVyaW5nX2RmKQ0KQ2x1c3RlcmluZ19kZl9ub3JtIDwtICBwcmVkaWN0KENsdXN0ZXJpbmdfZGZfcHJlcHJvYywgQ2x1c3RlcmluZ19kZikNCmhlYWQoQ2x1c3RlcmluZ19kZl9ub3JtICkgICAgIA0KYGBgDQojIyBFeHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzIChFREEpDQpXZSBmaXJzdCB0YWtlIGEgbG9vayBhdCBzb21lIG9mIHRoZSB2YXJpYWJsZXMgaW4gb3VyIGZpbmFsIGRhdGEuIA0KYGBge3J9DQpwMSA8LSBnZ3Bsb3QoQ2x1c3RlcmluZ19kZiwgYWVzKHggPSBHRFBfMjAxOCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdndGl0bGUoIjIwMTggR0RQIikgKw0KICB4bGFiKCIiKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBjKDAsNTAwMDAwMDAwMDAwMCwxMDAwMDAwMDAwMDAwMCwgMTUwMDAwMDAwMDAwMDAsIDIwMDAwMDAwMDAwMDAwKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiLCAiNSBUcmlsbGlvbiIsICIxMCBUcmlsbGlvbiIsICIxNSBUcmlsbGlvbiIsICIyMCBUcmlsbGlvbiIpKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSkrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0PTEsIHNpemUgPSA4KSkNCmBgYA0KDQpgYGB7cn0NCnAyIDwtIGdncGxvdChDbHVzdGVyaW5nX2RmLCBhZXMoeCA9IFBvcHVsYXRpb25fMjAxOCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdndGl0bGUoIjIwMTggUG9wdWxhdGlvbiIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gYygwLDUwMDAwMDAwMCwxMDAwMDAwMDAwKSwNCiAgICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IGMoIjAiLCAiNSBCaWxsaW9uIiwgIjEwIEJpbGxpb24iKSkgKw0KICB4bGFiKCIiKSsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpKQ0KYGBgDQoNCmBgYHtyfQ0KcDMgPC0gZ2dwbG90KENsdXN0ZXJpbmdfZGYsIGFlcyh4ID0gR0RQX0dyb3d0aF8yMDE4KSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2d0aXRsZSgiMjAxOCBHRFAgR3Jvd3RoIikgKw0KICB4bGFiKCIiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSkNCmBgYA0KDQpgYGB7cn0NCnA0IDwtIGdncGxvdChDbHVzdGVyaW5nX2RmLCBhZXMoeCA9IEdDSV8yMDE5KSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpICsNCiAgZ2d0aXRsZSgiR2xvYmFsIENvbXBldGl0aXZlbmVzcyBJbmRleCIpKw0KICB4bGFiKCIiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIGZhY2UgPSAiYm9sZCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpwNSA8LSBnZ3Bsb3QoQ2x1c3RlcmluZ19kZiwgYWVzKHggPSBJbmZsYXRpb25fUmF0ZV8yMDE4KSkgKw0KICBnZW9tX2hpc3RvZ3JhbSgpKw0KICBnZ3RpdGxlKCIyMDE4IEluZmxhdGlvbiBSYXRlIikrDQogIHhsYWIoIiIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTAsIGZhY2UgPSAiYm9sZCIpKQ0KYGBgDQoNCg0KYGBge3J9DQpwNiA8LSBnZ3Bsb3QoQ2x1c3RlcmluZ19kZiwgYWVzKHggPSBgTnVtYmVyIG9mIERhdGEgQ2VudGVyYCkpICsNCiAgZ2VvbV9oaXN0b2dyYW0oKSArDQogIGdndGl0bGUoIk51bWJlciBvZiBEYXRhIENlbnRlciIpKw0KICB4bGFiKCIiKSArDQogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwLCBmYWNlID0gImJvbGQiKSkNCmBgYA0KDQpgYGB7cn0NCmdyaWQuYXJyYW5nZShwMSwgcDIsIHAzLCBwNCwgcDUsIHA2LCBucm93ID0gMikNCmBgYA0KV2UgY2FuIHNlZSB0aGF0IHNvbWUgb2YgdGhlIHZhcmlhYmxlcyBhcmUgZXh0cmVtZWx5IHNrZXdlZCwgc3VjaCBhcyBwb3B1bGF0aW9uIGFuZCBHRFAsIHdoaWxlIG90aGVycyBzZWVtIHRvIGJlIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiANCg0KDQojIyBNZXRob2QgMTogUmFua2luZyBDb3VudHJpZXM6DQpUbyBmaW5kIG91dCB3aGljaCBjb3VudHJpZXMgYXJlIG1vcmUgZmF2b3JhYmxlIHRvIGV4cGFuZCBpbnRvLCB3ZSByYW5rIGFsbCB0aGUgY291bnRyaWVzIHRoYXQgTWljcm9zb2Z0IEZpbmFuY2luZyBoYXMgbm90IGV4cGFuZGVkIGludG8gYnkgZGlmZmVyZW50IHZhcmlhYmxlcywgYW5kIGNhbGN1bGF0ZSBob3cgbWFueSB0aW1lcyBlYWNoIGNvdW50cnkgYXBwZWFycyBpbiB0aGUgdG9wIDIwJSBvZiBhIHZhcmlhYmxlLiANCmBgYHtyIFJhbmsgQ291bnRyeX0NCiMgIm5vdCBpbiIgZnVuY3Rpb24NCmAlbm90aW4lYCA8LSBOZWdhdGUoYCVpbiVgKQ0KIyBSYW5rIGNvdW50cnkgdGFrZXMgYSBkYXRhZnJhbWUgd2l0aCB0d28gY29sdW1ucyAoZmlyc3QgY29sdW1uIGlzIGNvdW50cnkgYW5kIHNlY29uZCBjb2x1bW4gaXMgdGhlIGF0dHJpYnV0ZSknDQojIFJldHVybnMgdGhlIHRvcCAyMCUgb2YgdGhlIGNvdW50cnkncyBuYW1lcyBpbiB0aGF0IGF0dHJpYnV0ZQ0KUmFua19Db3VudHJ5IDwtIGZ1bmN0aW9uKHgsIFJldmVyc2UgPSBGQUxTRSkgew0KICBjb2xuYW1lcyh4KSA8LSBjKCJDb3VudHJ5X05hbWUiLCAiQXR0cmlidXRlIikNCiAgaWYoUmV2ZXJzZSA9PSBGQUxTRSkgew0KICAgICAgb3V0cHV0X3ZlY3RvciA8LSB4ICU+JQ0KICAgICAgZmlsdGVyKENvdW50cnlfTmFtZSAlbm90aW4lIEV4cGFuZF9Db3VudHJpZXNfTGlzdCkgJT4lDQogICAgICBhcnJhbmdlKGRlc2MoQXR0cmlidXRlKSkgJT4lDQogICAgICBoZWFkKG5yb3coQ2x1c3RlcmluZ19kZikgKiAwLjIpICU+JQ0KICAgICAgLiRDb3VudHJ5X05hbWUNCiAgfSBlbHNlIGlmIChSZXZlcnNlID09IFRSVUUpIHsNCiAgICAgIG91dHB1dF92ZWN0b3IgPC0geCAlPiUNCiAgICAgIGZpbHRlcihDb3VudHJ5X05hbWUgJW5vdGluJSBFeHBhbmRfQ291bnRyaWVzX0xpc3QpICU+JQ0KICAgICAgYXJyYW5nZShBdHRyaWJ1dGUpICU+JQ0KICAgICAgaGVhZChucm93KENsdXN0ZXJpbmdfZGYpICogMC4yKSAlPiUNCiAgICAgIC4kQ291bnRyeV9OYW1lDQogIH0NCiAgcmV0dXJuKG91dHB1dF92ZWN0b3IpDQp9DQojIEZ1bmN0aW9uIHRvIHJlLXJ1biByYW5raW5nIHRvIGluY2x1ZGUgdGhlIDQgY291bnRyaWVzIChTRywgSW5kaWEsIFNvdXRoIEFmcmljYSBhbmQgVUFFKQ0KUmFua19Db3VudHJ5X3JlIDwtIGZ1bmN0aW9uKHgsIFJldmVyc2UgPSBGQUxTRSkgew0KICBjb2xuYW1lcyh4KSA8LSBjKCJDb3VudHJ5X05hbWUiLCAiQXR0cmlidXRlIikNCiAgaWYoUmV2ZXJzZSA9PSBGQUxTRSkgew0KICAgICAgb3V0cHV0X3ZlY3RvciA8LSB4ICU+JQ0KICAgICAgZmlsdGVyKENvdW50cnlfTmFtZSAlbm90aW4lIEV4cGFuZF9Db3VudHJpZXNfTGlzdF9yZSkgJT4lDQogICAgICBhcnJhbmdlKGRlc2MoQXR0cmlidXRlKSkgJT4lDQogICAgICBoZWFkKG5yb3coQ2x1c3RlcmluZ19kZikgKiAwLjIpICU+JQ0KICAgICAgLiRDb3VudHJ5X05hbWUNCiAgfSBlbHNlIGlmIChSZXZlcnNlID09IFRSVUUpIHsNCiAgICAgIG91dHB1dF92ZWN0b3IgPC0geCAlPiUNCiAgICAgIGZpbHRlcihDb3VudHJ5X05hbWUgJW5vdGluJSBFeHBhbmRfQ291bnRyaWVzX0xpc3RfcmUpICU+JQ0KICAgICAgYXJyYW5nZShBdHRyaWJ1dGUpICU+JQ0KICAgICAgaGVhZChucm93KENsdXN0ZXJpbmdfZGYpICogMC4yKSAlPiUNCiAgICAgIC4kQ291bnRyeV9OYW1lDQogIH0NCiAgcmV0dXJuKG91dHB1dF92ZWN0b3IpDQp9DQpgYGANCkEgZ2VuZXJpYyBmdW5jdGlvbiB0aGF0IHRha2VzIGluIGEgZGF0YWZyYW1lIG9mIHR3byBjb2x1bW5zLCBDb3VudHJ5IE5hbWUgYW5kIGEgdmFyaWFibGUuIFRoZW4gaXQgcmV0dXJucyBhIHZlY3RvciBvZiBjb3VudHJpZXMgdGhhdCByYW5rcyBpbiB0aGUgdG9wIDIwJSBvZiB0aGF0IHZhcmlhYmxlLiAgICA8L2JyPg0KVXNlIGEgZm9yIGxvb3AgYW5kIHRhYmxlKCkgdG8gc2VlIGhvdyBlYWNoIGNvdW50cmllcyBwZXJmb3JtLiANCmBgYHtyfQ0KQ291bnRyeV92ZWN0b3IgPC0gYygpDQpmb3IgKGkgaW4gYygyOjI2KSkgew0KICBDb3VudHJ5X3ZlY3RvciA8LSBhcHBlbmQoQ291bnRyeV92ZWN0b3IsIFJhbmtfQ291bnRyeShDbHVzdGVyaW5nX2RmWyxjKDEsaSldKSkNCn0NCiMgUmVwbGFjZSBpbmZsYXRpb24gYmVjYXVzZSBpbmZsYXRpb24gc2hvdWxkIGJlIHJhbmtlZCBmcm9tIHRoZSBsb3dlc3QgdG8gaGlnaGVzdA0KQ291bnRyeV92ZWN0b3JbKDMqMjYrMSk6KDMqMjYrMjcpXSA8LSBSYW5rX0NvdW50cnkoQ2x1c3RlcmluZ19kZlssYygxLDUpXSwgUmV2ZXJzZSA9IFRSVUUpDQoNCg0KQ291bnRyeV92ZWN0b3JfcmUgPC0gYygpDQpmb3IgKGkgaW4gYygyOjI2KSkgew0KICBDb3VudHJ5X3ZlY3Rvcl9yZSA8LSBhcHBlbmQoQ291bnRyeV92ZWN0b3JfcmUsIFJhbmtfQ291bnRyeV9yZShDbHVzdGVyaW5nX2RmWyxjKDEsaSldKSkNCn0NCiMgUmVwbGFjZSBpbmZsYXRpb24gYmVjYXVzZSBpbmZsYXRpb24gc2hvdWxkIGJlIHJhbmtlZCBmcm9tIHRoZSBsb3dlc3QgdG8gaGlnaGVzdA0KQ291bnRyeV92ZWN0b3JfcmVbKDMqMjYrMSk6KDMqMjYrMjcpXSA8LSBSYW5rX0NvdW50cnlfcmUoQ2x1c3RlcmluZ19kZlssYygxLDUpXSwgUmV2ZXJzZSA9IFRSVUUpDQpgYGANCg0KDQpgYGB7ciBSYW5rIENvdW50cnkgT3V0cHV0fSANCiMgV2l0aG91dCAyMDIwIGV4cGFuc2lvbnMNCmhlYWQoc29ydCh0YWJsZShDb3VudHJ5X3ZlY3RvciksZGVjcmVhc2luZyA9IFRSVUUpLCAxOCkgDQpgYGANCg0KVGhlIGZpcnN0IHRhYmxlIHNob3dzIHRoZSByZXN1bHRzIG9mIGxpc3RzIG9mIGNvdW50cmllcyB3aXRob3V0IHRoZSAyMDIwIGV4cGFuc2lvbnMuIFRoZXNlIGFyZSBhbGwgdGhlIGNvdW50cmllcyB0aGF0IHNjb3JlZCAxNSsgaW4gb3VyIHJhbmtpbmcgc3lzdGVtLiBUaGUgcmFua2luZyBzeXN0ZW0gcHJvdmlkZXMgYW4gaW50dWl0aXZlIGFuZCBzdHJhaWdodGZvcndhcmQgcmVzdWx0IHRoYXQgZXZlcnlvbmUgY291bGQgZWFzaWx5IGdhdWdlLiBGb3IgZXhhbXBsZSwgd2l0aCBhIHNjb3JlIG9mIDIyLCBIb25nIEtvbmcgYXJlIGluIHRoZSB0b3AgMjAlIGluIDIyIG91dCBvZiB0aGUgMjUgZGlmZmVyZW50IHZhcmlhYmxlcywgcHV0dGluZyBpdCBhcyBvbmUgb2YgdGhlIG1vc3QgZmF2b3JhYmxlIHJlZ2lvbnMgdG8gZXhwYW5kIGludG8uIA0KDQpgYGB7cn0NCiMgMjAyMCBleHBhbnNpb25zIGluY2x1ZGVkDQpoZWFkKHNvcnQodGFibGUoQ291bnRyeV92ZWN0b3JfcmUpLGRlY3JlYXNpbmcgPSBUUlVFKSwgNDApDQpgYGANClRoZSBzZWNvbmQgdGFibGUgZXZhbHVhdGVzIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgY3VycmVudCBleHBhbnNpb24gKFNpbmdhcG9yZS4gVW5pdGVkIEFyYWIgRW1pcmF0ZXMsIEluZGlhIGFuZCBTb3V0aCBBZnJpY2EpLiBJdCB0dXJucyBvdXQgdGhhdCBTaW5nYXBvcmUgYW5kIFVuaXRlZCBBcmFiIEVtaXJhdGUgc2NvcmVkIHJlYWxseSB3ZWxsLCBidXQgSW5kaWEgYW5kIFNvdXRoIEFmcmljYSBzY29yZWQgcG91cmx5IGluIG91ciBtb2RlbC4gIA0KDQojIyBNZXRob2QgMjogQ2x1c3RlcmluZyBNb2RlbA0KV2UgY291bGQgYWxzbyB1c2UgY2x1c3RlcmluZyBtb2RlbHMgdG8gaGVscCBmaW5kIG91dCB3aGljaCBjb3VudHJpZXMgTWljcm9zb2Z0IHNob3VsZCB0YWtlIGEgY2xvc2VyIGxvb2sgYXQuIA0KQ2x1c3RlcmluZyBtb2RlbHMgcHV0IGRpZmZlcmVudCBjb3VudHJpZXMgaW50byBkaWZmZXJlbnQgY2x1c3RlcnMsIGFuZCB0aGUgY291bnRyaWVzIHdpdGhpbiB0aGUgc2FtZSBjbHVzdGVyIGFyZSBzYWlkIHRvIGJlIHNpbWlsYXIgdG8gZWFjaCBvdGhlci4gSWYgd2UgaGF2ZSBhIGNsdXN0ZXIgdGhhdCBwcmVkb21pbmFudGx5IGNvbnNpc3RzIG9mIGNvdW50cmllcyB0aGF0IE1pY3Jvc29mdCBGaW5hbmNpbmcgaGFzIGV4cGFuZGVkIGludG8sIHRoZSBvdGhlciBjb3VudHJpZXMgdGhhdCBNaWNyb3NvZnQgaGF2ZSBub3QgZXhwYW5kZWQgaW50byBhcmUgdGhlIG9uZXMgdGhhdCB0aGV5IHNob3VsZCBjb25zaWRlci4NCg0KIyMjIENsdXN0ZXJpbmcgTW9kZWwgOiBIaWVyYXJjaGljYWwgY2x1c3RlcmluZw0KSGllcmFjaGljYWwgY2x1c3RlcmluZyBjYWxjdWxhdGVzIHRoZSAiZGlzdGFuY2UiIGJldHdlZW4gZGlmZmVyZW50IGNvdW50cmllcywgYW5kIGNvdW50cmllcyB0aGF0IGFyZSBjbG9zZXIgdG9nZXRoZXIgd2lsbCBiZSBwdXQgaW4gdGhlIHNhbWUgY2x1c3RlciwgZGVwZW5kaW5nIG9uIHRoZSBudW1iZXIgb2YgY2x1c3RlcnMuIEluIHRoaXMgbW9kZWwsIHdlIHVzZSB0aGUgbW9zdCBjb21tb24gZXVjbGlkZWFuIGRpc3RhbmNlLCB3aGljaCBpcyB0aGUgb3JkaW5hcnkgc3RyYWlnaHQtbGluZSBkaXN0YW5jZS4gRm9yIGV4YW1wbGUsIHRoZSBldWNsaWRlYW4gZGlzdGFuY2UgYmV0d2VlbiBwb2ludCBBKDEsMiwzLDQpIGFuZCBCKDksIDgsIDcsIDYpIHdvdWxkIGJlOiAkJFxzcXJ0eyg5LTEpXjIgKyAoOC0yKV4yICsgKDctMyleMiArICg2LTQpXjJ9ID0gXHNxcnR7MTIwfSAkJA0KDQojIyMjIEJ1aWxkIFRyZWUNCmBgYHtyfQ0KQ2x1c3RlcmluZ19kZl9oIDwtIGNvbHVtbl90b19yb3duYW1lcyhDbHVzdGVyaW5nX2RmLCB2YXIgPSAiQ291bnRyeV9OYW1lIikgIyB0dXJuIENvdW50cnlfTmFtZSBpbnRvIHJvdyBuYW1lcyB0byBkaXNwbGF5IGluIHRoZSBkZW5kcm9ncmFtLg0KZGlzdGFuY2VfMSA8LSBkaXN0KENsdXN0ZXJpbmdfZGZfaCwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQpDbHVzdGVyaW5nVHJlZV8xIDwtIGhjbHVzdChkaXN0YW5jZV8xLCBtZXRob2QgPSAid2FyZC5EIikNCmBgYA0KDQojIyMjIFBsb3QgRnVsbCBUcmVlDQpgYGB7cn0NCmhjZCA9IGFzLmRlbmRyb2dyYW0oQ2x1c3RlcmluZ1RyZWVfMSkNCnBsb3QoaGNkKQ0KYGBgDQogICAgIDwvYnI+DQoNCkl0IGlzIHJlYWxseSBoYXJkIHRvIHJlYWQgZnJvbSB0aGUgZGVuZHJvZ3JhbSBiZWNhdXNlIHRoZXJlIGFyZSB0b28gbWFueSBjb3VudHJpZXMsIHNvIHdlIHdvdWxkIGRpc3BsYXkganVzdCBwYXJ0IG9mIHRoZSB0cmVlLiANCg0KIyMjIyBUaGUgYm90dG9tIHBhcnQgb2YgdGhlIHRyZWUNCmBgYHtyfQ0KcGxvdChjdXQoaGNkLCBoID0gNjAwMDAwMDAwMDAwKSRsb3dlcltbMTNdXSwgbWFpbiA9ICJMb3dlciB0cmVlIHBhcnQgMSIpDQpgYGANCg0KYGBge3J9DQpwbG90KGN1dChoY2QsIGggPSA2MDAwMDAwMDAwMDApJGxvd2VyW1sxMl1dLCBtYWluID0gIkxvd2VyIHRyZWUgcGFydCAyIikNCmBgYA0KDQpGcm9tIHRoZSBkZW5kcm9ncmFtcyBzaG93biwgd2UgY2FuIHNlZSB0aGF0IHRoZSBIaWVyYWNoaWNhbCBjbHVzdGVyaW5nIG1vZGVsIGdyb3VwcyBjb3VudHJpZXMgdG9nZXRoZXIgYnV5IHRoZWlyIGRpc3RhbmNlIHRvIG90aGVyIGNvdW50cmllcy4gSW4gdGhlIHNlY29uZCBwbG90LCB0aGUgbW9kZWwgc2hvd3MgdGhhdCB0aGUgY291bnRyaWVzIHRoYXQgaXMgdGhlIG1vc3Qgc2ltaWxhciB0byBIb25nIEtvbmcgaXMgU2luZ2Fwb3JlLCBhbmQgdGhlIHNlY29uZCBjbG9zZXN0IGdyb3VwIGFyZSBJc3JhZWwgYW5kIFNvdXRoIEFmcmljYS4gDQpJbiB0aGUgdGhpcmQgdHJlZSwgaWYgd2Ugd2FudCB0byBoYXZlIHR3byBjbHVzdGVycywgdGhlbiB3ZSB3b3VsZCBwdXQgQXJnZW50aW5hIG5hZCBUaGFpbGFuZCBpbiBhIGNsdXN0ZXIgYW5kIFBvbGFuZCwgQmVsZ2l1bSBhbmQgU3dlZGVuIGluIHRoZSBvdGhlciBjbHVzdGVyLiBJZiB3ZSB3YW50IHRocmVlIGNsdXN0ZXJzLCB0aGVuIFBvbGFuZCB3b3VsZCBmb3JtIGEgY2x1c3RlciBvZiBpdHMgb3duLiANCg0KIyMjIyA0IENsdXN0ZXJzIA0KYGBge3J9DQprID0gNA0KQ2x1c3Rlckdyb3VwXzEgPC0gY3V0cmVlKENsdXN0ZXJpbmdUcmVlXzEsIGsgPSBrKSAgICAgICAgICAgICAgICAgICAgICMgR2V0IHRoZSBjbHVzdGVyaW5nIHJlc3VsdA0KQ2x1c3RlcmluZ19kZl9jbHVzdGVyIDwtIENsdXN0ZXJpbmdfZGYNCkNsdXN0ZXJpbmdfZGZfY2x1c3RlciRDbHVzdGVyIDwtIGZhY3RvcihDbHVzdGVyR3JvdXBfMSkgICAgICAgICAgICAgICAjIFB1dCBpdCBpbnRvIGFudGhlciBkYXRhZnJhbWUNCnRhYmxlKENsdXN0ZXJpbmdfZGZfY2x1c3RlciRDbHVzdGVyLCBDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkRXhwYW5kKSAgICAjIFNob3cgdGhlIG92ZXJhbGwgY2x1c3RlcmluZyByZXN1bHQgaW4gcmVnYXJkcyB0byBleHBhbnNpb24NCmZvciAoaSBpbiAzOjQpIHsNCiAgcHJpbnQoQ2x1c3RlcmluZ19kZl9jbHVzdGVyICU+JSBmaWx0ZXIoQ2x1c3RlciA9PSBpLCBFeHBhbmQgPT0gRkFMU0UpICU+JSAuJENvdW50cnlfTmFtZSkNCn0NCmBgYA0KVGhlIHRhYmxlIHNob3dzIHRoYXQgd2Ugc2hvdWxkIGxvb2sgYXQgdGhlIDNyZCBhbmQgNHRoIGNsdXN0ZXJzIGJlY2F1c2UgdGhleSBoYXZlIHRoZSBoaWdoZXN0IHBlcmNlbnRhZ2Ugb2YgY3VycmVudCBleHBhbnNpb25zLiBUaGUgVFJVRSByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgY291bnRyaWVzIHRoYXQgTWljcm9zb2Z0IGhhcyBhbHJlYWR5IGV4cGFuZGVkIGludG8gd2hpbGUgRkFMU0UgcmVwcmVzZW50cyB0aGUgbnVtYmVyIG9mIGNvdW50cmllcyB0aGF0IE1pY3Jvc29mdCBoYXZlIG5vdCBleHBhbmRlZCBpbnRvIHlldC4gICA8L2JyPg0KVGhlIHR3byBGQUxTRSBjb3VudHJpZXMgaW4gQ2x1c3RlciAzIGFyZSBTb3V0aCBLb3JlYSBhbmQgUnVzc2lhIGFuZCB0aGUgRkFMU0UgY291bnRyeSBpbiBDbHVzdGVyIDQgaXMgQ2hpbmEuIA0KDQojIyMjIDUgQ2x1c3RlcnMgDQpgYGB7cn0NCmsgPSA1DQpDbHVzdGVyR3JvdXBfMSA8LSBjdXRyZWUoQ2x1c3RlcmluZ1RyZWVfMSwgayA9IGspICAgICAjIEdldCB0aGUgY2x1c3RlcmluZyByZXN1bHQNCkNsdXN0ZXJpbmdfZGZfY2x1c3RlciA8LSBDbHVzdGVyaW5nX2RmDQpDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkQ2x1c3RlciA8LSBmYWN0b3IoQ2x1c3Rlckdyb3VwXzEpICAgICAgICMgUHV0IGl0IGludG8gYW50aGVyIGRhdGFmcmFtZQ0KdGFibGUoQ2x1c3RlcmluZ19kZl9jbHVzdGVyJENsdXN0ZXIsIENsdXN0ZXJpbmdfZGZfY2x1c3RlciRFeHBhbmQpDQpwcmludChDbHVzdGVyaW5nX2RmX2NsdXN0ZXIgJT4lIGZpbHRlcihDbHVzdGVyID09IDMsIEV4cGFuZCA9PSBGQUxTRSkgJT4lIC4kQ291bnRyeV9OYW1lKQ0KYGBgDQoNCiMjIyMgNiBDbHVzdGVycyANCmBgYHtyfQ0KayA9IDYNCkNsdXN0ZXJHcm91cF8xIDwtIGN1dHJlZShDbHVzdGVyaW5nVHJlZV8xLCBrID0gaykgICAgICMgR2V0IHRoZSBjbHVzdGVyaW5nIHJlc3VsdA0KQ2x1c3RlcmluZ19kZl9jbHVzdGVyIDwtIENsdXN0ZXJpbmdfZGYNCkNsdXN0ZXJpbmdfZGZfY2x1c3RlciRDbHVzdGVyIDwtIGZhY3RvcihDbHVzdGVyR3JvdXBfMSkgICAgICAgIyBQdXQgaXQgaW50byBhbnRoZXIgZGF0YWZyYW1lDQp0YWJsZShDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkQ2x1c3RlciwgQ2x1c3RlcmluZ19kZl9jbHVzdGVyJEV4cGFuZCkNCnByaW50KENsdXN0ZXJpbmdfZGZfY2x1c3RlciAlPiUgZmlsdGVyKENsdXN0ZXIgPT0gMywgRXhwYW5kID09IEZBTFNFKSAlPiUgLiRDb3VudHJ5X05hbWUpDQpgYGANCg0KDQojIyMjIDcgQ2x1c3RlcnMgDQpgYGB7cn0NCmsgPSA3DQpDbHVzdGVyR3JvdXBfMSA8LSBjdXRyZWUoQ2x1c3RlcmluZ1RyZWVfMSwgayA9IGspICAgICAjIEdldCB0aGUgY2x1c3RlcmluZyByZXN1bHQNCkNsdXN0ZXJpbmdfZGZfY2x1c3RlciA8LSBDbHVzdGVyaW5nX2RmDQpDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkQ2x1c3RlciA8LSBmYWN0b3IoQ2x1c3Rlckdyb3VwXzEpICAgICAgICMgUHV0IGl0IGludG8gYW50aGVyIGRhdGFmcmFtZQ0KdGFibGUoQ2x1c3RlcmluZ19kZl9jbHVzdGVyJENsdXN0ZXIsIENsdXN0ZXJpbmdfZGZfY2x1c3RlciRFeHBhbmQpDQpwcmludChDbHVzdGVyaW5nX2RmX2NsdXN0ZXIgJT4lIGZpbHRlcihDbHVzdGVyID09IDMsIEV4cGFuZCA9PSBGQUxTRSkgJT4lIC4kQ291bnRyeV9OYW1lKQ0KYGBgDQoNCiMjIyMgOCBDbHVzdGVycyANCmBgYHtyfQ0KayA9IDgNCkNsdXN0ZXJHcm91cF8xIDwtIGN1dHJlZShDbHVzdGVyaW5nVHJlZV8xLCBrID0gaykgICAgICMgR2V0IHRoZSBjbHVzdGVyaW5nIHJlc3VsdA0KQ2x1c3RlcmluZ19kZl9jbHVzdGVyIDwtIENsdXN0ZXJpbmdfZGYNCkNsdXN0ZXJpbmdfZGZfY2x1c3RlciRDbHVzdGVyIDwtIGZhY3RvcihDbHVzdGVyR3JvdXBfMSkgICAgICAgIyBQdXQgaXQgaW50byBhbnRoZXIgZGF0YWZyYW1lDQp0YWJsZShDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkQ2x1c3RlciwgQ2x1c3RlcmluZ19kZl9jbHVzdGVyJEV4cGFuZCkNCnByaW50KENsdXN0ZXJpbmdfZGZfY2x1c3RlciAlPiUgZmlsdGVyKENsdXN0ZXIgPT0gMywgRXhwYW5kID09IEZBTFNFKSAlPiUgLiRDb3VudHJ5X05hbWUpDQpgYGANCg0KVGhlIGNsdXN0ZXJpbmcgcmVzdWx0IGRvZXMgbm90IGNoYW5nZSBtdWNoIHdoZW4gdGhlIG51bWJlciBvZiBjbHVzdGVycyBpbmNyZWFzZS4gVGhpcyBpcyBiZWNhdXNlIHdoZW4gd2UgaW5jcmVhc2UgdGhlIG51bWJlciBvZiBjbHVzdGVycyBieSAxLCBpdCB3b3VsZCBiZSBlYXN5IHRvICJraWNrIG91dCIgYSBjb3VudHJ5IGZyb20gYSBjbHVzdGVyIHRvIGZvcm0gYSBuZXcgY2x1c3RlciwgdGhlcmVmb3JlIHRoZSBtYWpvcml0eSBvZiB0aGUgY2x1c3RlcnMgZGlkIG5vdCBjaGFuZ2UuIA0KDQoNCg0KYGBge3IsIGluY2x1ZGU9RkFMU0V9DQojIyMjIEdlbmVyaWMgZnVuY3Rpb246IEhfY2x1c3RlcmluZygpDQojIFRha2VzIGluIDIgaW5wdXQ6IGsgYW5kIENvdW50cnkNCiMgayBpcyB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIHVzZWQgaW4gSGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcNCiMgQ291bnRyeSBpcyB0aGUgY291bnRyeSBuYW1lIHRoYXQgeW91IHdhbnQgdG8gZm9jdXMgb246IGNvdWxkIGJlIGEgY291bnRyeSBvciBhIGxpc3Qgb2YgY291bnRyaWVzDQoNCkdEUF90IDwtIGZ1bmN0aW9uKGlucHV0X3ZlY3Rvcil7DQogIG91dHB1dF92ZWN0b3IgPSB2ZWN0b3IoKQ0KICBmb3IgKGkgaW4gMTpsZW5ndGgoaW5wdXRfdmVjdG9yKSkgew0KICAgIGlmKHJvdW5kKGlucHV0X3ZlY3RvcltpXS8xMDAwMDAwMDAwLCAzKSA+PSAxMDAwKSB7DQogICAgICBvdXRwdXRfdmVjdG9yID0gYXBwZW5kKG91dHB1dF92ZWN0b3IsIHBhc3RlMCgiJCIsIHJvdW5kKGlucHV0X3ZlY3RvcltpXS8xMDAwMDAwMDAwMDAwLCAzKSwgIiBUcmlsbG9uIikpDQogICAgfSBlbHNlIHsNCiAgICAgIG91dHB1dF92ZWN0b3IgPSBhcHBlbmQob3V0cHV0X3ZlY3RvciwgcGFzdGUwKCIkIiwgcm91bmQoaW5wdXRfdmVjdG9yW2ldLzEwMDAwMDAwMDAsIDMpLCAiIEJpbGxvbiIpKQ0KICAgIH0NCiAgfQ0KICByZXR1cm4ob3V0cHV0X3ZlY3RvcikNCn0NCmBgYA0KDQoNCmBgYHtyIEhfY2x1c3RlcmluZywgaW5jbHVkZT1GQUxTRX0NCkhfY2x1c3RlcmluZyA8LSBmdW5jdGlvbihrLCBDb3VudHJ5ID0gTlVMTCkgew0KICBkaXN0YW5jZSA8LSBkaXN0KENsdXN0ZXJpbmdfZGZbLCAyOjI2XSwgbWV0aG9kID0gImV1Y2xpZGVhbiIpDQogIENsdXN0ZXJpbmdUcmVlIDwtIGhjbHVzdChkaXN0YW5jZSwgbWV0aG9kID0gIndhcmQuRCIpDQogIENsdXN0ZXJHcm91cCA8LSBjdXRyZWUoQ2x1c3RlcmluZ1RyZWUsIGsgPSBrKSAgICAgICAgICAgICAgICAgIyBHZXQgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0DQogIENsdXN0ZXJpbmdfZGZfY2x1c3RlciA8LSBDbHVzdGVyaW5nX2RmDQogIENsdXN0ZXJpbmdfZGZfY2x1c3RlciRDbHVzdGVyIDwtIGZhY3RvcihDbHVzdGVyR3JvdXApICAgICAgICAgIyBQdXQgaXQgaW50byBhbnRoZXIgZGF0YWZyYW1lDQogIENsdXN0ZXJpbmdfcmVzdWx0X2Z1bGwgPC0gYXMuZGF0YS5mcmFtZS5tYXRyaXgodGFibGUoQ2x1c3RlcmluZ19kZl9jbHVzdGVyJENsdXN0ZXIsIENsdXN0ZXJpbmdfZGZfY2x1c3RlciRFeHBhbmQpKQ0KICBDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkR0RQXzIwMTggPC0gR0RQX3QoQ2x1c3RlcmluZ19kZl9jbHVzdGVyJEdEUF8yMDE4KQ0KICBDbHVzdGVyaW5nX2RmX2NsdXN0ZXJbLCBjKDQsNSwxNDoyNildIDwtIHNhcHBseShDbHVzdGVyaW5nX2RmX2NsdXN0ZXJbLCBjKDQsNSwxNDoyNildLCByb3VuZCwgMikNCiAgQ2x1c3RlcmluZ19kZl9jbHVzdGVyJEdEUF9Hcm93dGhfMjAxOCA8LSBwYXN0ZTAoIENsdXN0ZXJpbmdfZGZfY2x1c3RlciRHRFBfR3Jvd3RoXzIwMTgsICIlIikNCiAgQ2x1c3RlcmluZ19kZl9jbHVzdGVyJEluZmxhdGlvbl9SYXRlXzIwMTggPC0gcGFzdGUwKCBDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkSW5mbGF0aW9uX1JhdGVfMjAxOCwgIiUiKQ0KICBDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkUG9wdWxhdGlvbl8yMDE4IDwtIGZvcm1hdChDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkUG9wdWxhdGlvbl8yMDE4LCBiaWcubWFyaz0iLCIsIHNjaWVudGlmaWM9RkFMU0UpDQogIGlmIChpcy5udWxsKENvdW50cnkpKSB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXaGVuIHRoZXJlIGlzIG5vIGNvdW50cnkgaW5wdXQNCiAgICAgIE91dHB1dCA8LSBsaXN0KCkNCiAgICAgIGZvcihpIGluIDE6ayl7DQogICAgICBPdXRwdXRbW2ldXSA8LSBDbHVzdGVyaW5nX2RmX2NsdXN0ZXIgJT4lIGZpbHRlcihDbHVzdGVyID09IGkpICU+JSBhcnJhbmdlKEV4cGFuZF9yZSkNCiAgICAgIH0NCiAgICAgIE91dHB1dFtbaysxXV0gPC0gQ2x1c3RlcmluZ19yZXN1bHRfZnVsbA0KICB9DQoNCiAgaWYgKCFpcy5udWxsKENvdW50cnkpKSB7ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXaGVuIHRoZXJlIGlzIGNvdW50cnkgaW5wdXQNCiAgICBpZiAobGVuZ3RoKENvdW50cnkpID4gMSkgeyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXaGVuIGNvdW50cnkgaW5wdXQgaXMgYSB2ZWN0b3IgKG1vcmUgdGhhbiAxKQ0KICAgICAgaW5fayA8LSBjKCkNCiAgICAgIE91dHB1dCA8LSBsaXN0KCkNCiAgICAgIGZvciAoaSBpbiAxOmxlbmd0aChDb3VudHJ5KSkgew0KICAgICAgICBpbl9rW2ldIDwtIENsdXN0ZXJpbmdfZGZfY2x1c3RlcltDbHVzdGVyaW5nX2RmX2NsdXN0ZXIkQ291bnRyeV9OYW1lID09IENvdW50cnlbaV0sIF0kQ2x1c3Rlcg0KICAgICAgfQ0KICAgICAgZm9yIChqIGluIDE6bGVuZ3RoKHVuaXF1ZShpbl9rKSkpIHsNCiAgICAgICAgT3V0cHV0W1tqXV0gPC0gQ2x1c3RlcmluZ19kZl9jbHVzdGVyICU+JSBmaWx0ZXIoQ2x1c3RlciA9PSB1bmlxdWUoaW5faylbal0pICU+JSBhcnJhbmdlKEV4cGFuZF9yZSkNCiAgICAgIH0gIA0KICAgICAgT3V0cHV0W1tqKzFdXSA8LSBDbHVzdGVyaW5nX3Jlc3VsdF9mdWxsDQogICAgfSBlbHNlIHsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBXaGVuIGNvdW50cnkgaW5wdXQgaXMgb25seSBvbmUgY291bnRyeQ0KICAgICAgaW5fayA8LSBDbHVzdGVyaW5nX2RmX2NsdXN0ZXJbQ2x1c3RlcmluZ19kZl9jbHVzdGVyJENvdW50cnlfTmFtZSA9PSBDb3VudHJ5LCBdJENsdXN0ZXINCiAgICAgIE91dHB1dCA8LSBDbHVzdGVyaW5nX2RmX2NsdXN0ZXIgJT4lIGZpbHRlcihDbHVzdGVyID09IGluX2spICU+JSBhcnJhbmdlKEV4cGFuZF9yZSkNCiAgICB9DQogICAgDQogIH0NCiAgcmV0dXJuKE91dHB1dCkNCiAgfQ0KYGBgDQoNCg0KYGBge3IgSF9jbHVzdGVyaW5nIGV4YW1wbGVfMSwgaW5jbHVkZT1GQUxTRX0NCiMgU2hvd3MgdGhlIHJlc3VsdCBvZiBrPTQgaW4gSGllcmFyY2hpY2FsIGNsc3V0ZXJpbmcNCkhfY2x1c3RlcmluZyg0KQ0KYGBgDQoNCg0KDQoNCmBgYHtyIEhfY2x1c3RlcmluZyBleGFtcGxlXzIsIGluY2x1ZGU9RkFMU0V9DQojIFNob3dzIHRoZSBjbHVzdGVyIHRoYXQgQ2hpbGUgaXMgaW4gdW5kZXIgaz0xMiBpbiBoaWVyYXJjaGljYWwgY2x1c3RlcmluZw0KSF9jbHVzdGVyaW5nKDEyLCAiQ2hpbGUiKQ0KYGBgDQoNCmBgYHtyIEhfY2x1c3RlcmluZyBleGFtcGxlXzMsIGluY2x1ZGU9RkFMU0V9DQojIFNob3dzIHRoZSBjbHVzdGVycyB0aGF0IFNpbmdhcG9yZSwgSW5kaWEsIFNvdXRoIEFmcmljYSBhbmQgVUFFIGFyZSBpbiB1bmRlciBrPTYgaW4gaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgDQpIX2NsdXN0ZXJpbmcoNiwgYygiU2luZ2Fwb3JlIiwgIkluZGlhIiwgIlNvdXRoIEFmcmljYSIsICJVbml0ZWQgQXJhYiBFbWlyYXRlcyIpKQ0KYGBgDQoNCg0KDQoNCg0KPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09DQoNCg0KDQoNCg0KDQojIyMgQ2x1c3RlcmluZyBNb2RlbCA6IEstbWVhbnMgY2x1c3RlcmluZyAoTm9ybWFsaXplZCkNCksgbWVhbnMgY2x1c3RlcmluZyBpcyBkaWZmZXJlbnQgY2x1c3RlcmluZyBhbGdvcml0aG0uIFRoZSBhbGdvcml0aG0gd291bGQgcmFuZG9tbHkgZ2VuZWFyYXRlIGEgbnVtYmVyIG9mIGNlbnRyb2lkcyAoYmFzZWQgb24gb3VyIGNob2ljZSBvZiBrLCB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzKSB0byBzdGFydC4gRWFjaCBkYXRhIHBvaW50IGlzIHRoZW4gYXNzaWduZWQgdG8gdGhlIGNsb3Nlc3QgY2VudHJvaWQuIEFmdGVyIHRoYXQsIHRoZSBjZW50cm9pZHMgc2hpZnQgdG8gdGhlaXIgcmVzcGVjdGl2ZSBhdmVyYWdlIHBvaW50IHdpdGhpbiB0aGVpciBjbHVzdGVyLCB0aGVuIGVhY2ggcG9pbnQgaXMgYXNzaWduZWQgdG8gdGhlIGNsb3N0ZXN0IGNlbnRyb2lkLiBUaGUgcHJvY2VzcyBpcyByZXBlYXRlZCB1bnRpbCBhbiBlcXVpbGlicml1bSBpcyByZWFjaGVkIG9yIHRoZSBtYXhpbXVtIG51bWJlciBvZiBpdGVyYXRpb25zIGFyZSBtZXQuIA0KDQojIyMjIA0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KayA9IDQNCkNsdXN0ZXJpbmdfa21lYW5zIDwtICBrbWVhbnMoQ2x1c3RlcmluZ19kZl9ub3JtWywyOjI2XSwgY2VudGVycyA9IGssIG5zdGFydCA9IDIwKSAgIyBVc2Ugbl9zdGFydCB0byAic3RhYmFsaXplIiB0aGUgY2x1c3RlcmluZyByZXN1bHRzLiANCkNsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgPC0gQ2x1c3RlcmluZ19kZl9ub3JtICAgICAgICAgICAgICAgICAgICAgICANCkNsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMkQ2x1c3RlciA8LSBDbHVzdGVyaW5nX2ttZWFucyRjbHVzdGVyICAgIA0KdGFibGUoQ2x1c3RlcmluZ19kZl9ub3JtX2ttZWFucyRDbHVzdGVyLCBDbHVzdGVyaW5nX2RmX25vcm1fa21lYW5zJEV4cGFuZCkNCnByaW50KENsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgJT4lIGZpbHRlcihDbHVzdGVyID09IDMsIEV4cGFuZCA9PSBGQUxTRSkgJT4lLiRDb3VudHJ5X05hbWUpDQpgYGANCkZyb20gdGhlIHJlc3VsdHMsIHdlIGNhbiBzZWUgdGhhdCB3ZSBzaG91bGQgZm9jdXMgb24gdGhlIHRoaXJkIGNsdXN0ZXI6IENoaWxlLCBDemVjaCBSZXB1YmxpYywgRXN0b25pYSwgSG9uZyBLb25nIFNBUiwgSWNlbGFuZCwgSXNyYWVsLCBTb3V0aCBLb3JlYSwgTGl0aHVhbmlhLCBMdXhlbWJvdXJnLCBNYWxheXNpYSwgTWFsdGEsIFFhdGFyLCBTbG92ZW5pYS4NCg0KIyMjIyBJZiB3ZSBjaGFuZ2UgdGhlIG51bWJlciBvZiBjbHVzdGVycywgd2UgbWF5IGdldCBkaWZmZXJlbnQgcmVzdWx0cy4gKGsgPTcsIHNlZWQgPSAxKQ0KYGBge3J9DQpzZXQuc2VlZCgxKQ0KayA9IDcNCkNsdXN0ZXJpbmdfa21lYW5zIDwtICBrbWVhbnMoQ2x1c3RlcmluZ19kZl9ub3JtWywyOjI2XSwgY2VudGVycyA9IGssIG5zdGFydCA9IDIwKSAgIyBVc2Ugbl9zdGFydCB0byAic3RhYmFsaXplIiB0aGUgY2x1c3RlcmluZyByZXN1bHRzLiANCkNsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgPC0gQ2x1c3RlcmluZ19kZl9ub3JtICAgICAgICAgICAgICAgICAgICAgICANCkNsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMkQ2x1c3RlciA8LSBDbHVzdGVyaW5nX2ttZWFucyRjbHVzdGVyICAgIA0KdGFibGUoQ2x1c3RlcmluZ19kZl9ub3JtX2ttZWFucyRDbHVzdGVyLCBDbHVzdGVyaW5nX2RmX25vcm1fa21lYW5zJEV4cGFuZCkNCnByaW50KENsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgJT4lIGZpbHRlcihDbHVzdGVyID09IDQsIEV4cGFuZCA9PSBGQUxTRSkgJT4lLiRDb3VudHJ5X05hbWUpDQpgYGANCiMjIyMgRGlmZmVyZW50IHNlZWQgbWlnaHQgZ2VuZXJhdGUgZGlmZmVyZW50IHJlc3VsdHMgKGsgPTcsIHNlZWQgPSAzKQ0KYGBge3J9DQpzZXQuc2VlZCgzKQ0KayA9IDcNCkNsdXN0ZXJpbmdfa21lYW5zIDwtICBrbWVhbnMoQ2x1c3RlcmluZ19kZl9ub3JtWywyOjI2XSwgY2VudGVycyA9IGssIG5zdGFydCA9IDIwKSAgIyBVc2Ugbl9zdGFydCB0byAic3RhYmFsaXplIiB0aGUgY2x1c3RlcmluZyByZXN1bHRzLiANCkNsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgPC0gQ2x1c3RlcmluZ19kZl9ub3JtICAgICAgICAgICAgICAgICAgICAgICANCkNsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMkQ2x1c3RlciA8LSBDbHVzdGVyaW5nX2ttZWFucyRjbHVzdGVyICAgIA0KdGFibGUoQ2x1c3RlcmluZ19kZl9ub3JtX2ttZWFucyRDbHVzdGVyLCBDbHVzdGVyaW5nX2RmX25vcm1fa21lYW5zJEV4cGFuZCkNCnByaW50KENsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgJT4lIGZpbHRlcihDbHVzdGVyID09IDIsIEV4cGFuZCA9PSBGQUxTRSkgJT4lLiRDb3VudHJ5X05hbWUpDQpgYGANCklmIHdlIGNoYW5nZSB0aGUgbnVtYmVyIG9mIGNsdXN0ZXJzIG9yIHNldCBhIGRpZmZlcmVudCBzZWVkLCB3ZSB3aWxsIGdldCBkaWZmZXJlbnQgcmVzdWx0cyBmcm9tIHRoZSBLLW1lYW5zIGNsdXN0ZXJpbmcgbW9kZWwuIFRoZSBjbHVzdGVyaW5nIHJlc3VsdHMgbWlnaHQgYmUgZGlmZmVyZW50IHdoaWxlIHVzaW5nIHRoZSBzYW1lIG51bWJlciBvZiBjbHVzdGVycyBiZWNhdXNlIHRoZSBkaWZmZXJlbnQgc3RhcnRpbmcgcG9pbnRzIG1pZ2h0IGdlbmVyYXRlIGRpZmZlcm50IHJlc3VsdHMsIGFzIHdlIGNhbnMgc2VlIGZyb20gdGhlIG91dHB1dHMgYWJvdmUuIA0KDQpUbyBhZGRyZXNzIHRoaXMsIHdlIGNvdWxkIHJ1biBhIHNpbXVsYXRpb24gdG8gc2VlIHdoaWNoIGNvdW50cmllcyBhcmUgbW9yZSBwcmVmZXJhYmxlLiBXZSByZSBydW4gdGhlIGNsdXN0ZXJpbmcgbW9kZWwgMTAsMDAwIHRpbWVzIGFuZCBjYWxjdWxhdGUgdGhlIG51bWJlciBvZiB0aW1lcyBlYWNoIGNvdW50cmllcyBhcHBlYXIgaW4gYSBjbHVzdGVyIHdoaWNoIGhhcyBtb3JlIGV4cGFuZGVkIGNvdW50cmllcyB0aGFuIG5vbi1leHBhbmRlZCBjb3VudHJpZXMuIA0KIyMjIyBTaW11bGF0aW9uOiBrPTQsIDEwLDAwMCB0aW1lcw0KYGBge3J9DQprID0gNA0KY19saXN0IDwtICBjKCkNCmZvciAoaiBpbiAxOjEwMDAwKXsNCiAgc2V0LnNlZWQoaikNCiAgQ2x1c3RlcmluZ19rbWVhbnMgPC0ga21lYW5zKENsdXN0ZXJpbmdfZGZfbm9ybVssMjoyNl0sIGNlbnRlcnMgPSBrLCBuc3RhcnQgPSAyMCkgICMgVXNlIG5fc3RhcnQgdG8gInN0YWJhbGl6ZSIgdGhlIGNsdXN0ZXJpbmcgcmVzdWx0cy4gDQogIENsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgPC0gQ2x1c3RlcmluZ19kZl9ub3JtICAgICAgICAgICAgICAgICAgICAgICANCiAgQ2x1c3RlcmluZ19kZl9ub3JtX2ttZWFucyRDbHVzdGVyIDwtIENsdXN0ZXJpbmdfa21lYW5zJGNsdXN0ZXIgDQogIGEgPC0gdGFibGUoQ2x1c3RlcmluZ19kZl9ub3JtX2ttZWFucyRDbHVzdGVyLCBDbHVzdGVyaW5nX2RmX25vcm1fa21lYW5zJEV4cGFuZCkNCiAgZm9yIChpIGluIDE6ayl7DQogIGlmKGFbaV0gPCBhW2kra10pew0KICAgICBjX2xpc3QgPC0gYXBwZW5kKGNfbGlzdCwgQ2x1c3RlcmluZ19kZl9ub3JtX2ttZWFucyAlPiUgZmlsdGVyKENsdXN0ZXIgPT0gaSwgRXhwYW5kID09IEZBTFNFKSAlPiUuJENvdW50cnlfTmFtZSkNCiAgfQ0KfQ0KfQ0Kc29ydCh0YWJsZShjX2xpc3QpLCBkZWNyZWFzaW5nID0gVFJVRSkNCmBgYA0KIyMjIyBTaW11bGF0aW9uOiBrPTcsIDEwLDAwMCB0aW1lcw0KYGBge3J9DQprID0gNyANCmNfbGlzdCA8LSAgYygpDQpmb3IgKGogaW4gMToxMDAwMCl7DQogIHNldC5zZWVkKGopDQogIENsdXN0ZXJpbmdfa21lYW5zIDwtIGttZWFucyhDbHVzdGVyaW5nX2RmX25vcm1bLDI6MjZdLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMjApICAjIFVzZSBuX3N0YXJ0IHRvICJzdGFiYWxpemUiIHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMuIA0KICBDbHVzdGVyaW5nX2RmX25vcm1fa21lYW5zIDwtIENsdXN0ZXJpbmdfZGZfbm9ybSAgICAgICAgICAgICAgICAgICAgICAgDQogIENsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMkQ2x1c3RlciA8LSBDbHVzdGVyaW5nX2ttZWFucyRjbHVzdGVyIA0KICBhIDwtIHRhYmxlKENsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMkQ2x1c3RlciwgQ2x1c3RlcmluZ19kZl9ub3JtX2ttZWFucyRFeHBhbmQpDQogIGZvciAoaSBpbiAxOmspew0KICBpZihhW2ldIDwgYVtpK2tdKXsNCiAgICAgY19saXN0IDwtIGFwcGVuZChjX2xpc3QsIENsdXN0ZXJpbmdfZGZfbm9ybV9rbWVhbnMgJT4lIGZpbHRlcihDbHVzdGVyID09IGksIEV4cGFuZCA9PSBGQUxTRSkgJT4lLiRDb3VudHJ5X05hbWUpDQogIH0NCn0NCn0NCnNvcnQodGFibGUoY19saXN0KSwgZGVjcmVhc2luZyA9IFRSVUUpDQpgYGANCg0KRnJvbSB0aGUgYWJvdmUgc2ltdWxhdGlvbiwgd2UgY2FuIHNlZSB0aGF0IHRoZSByZWNvbW1lbmRlZCBjb3VudHJpZXMgd291bHMgYmUgQ2hpbGUsIEN6ZWNoLCBFc3RvbmlhLCBIb25nIEtvbmcsIEljZWxhbmQsIElzcmFlbCwgS29yZWEsIEx1eGVtYm91cmcsIE1hbGF5c2lhLCBRYXRhciwgU2xvdmVuaWEsIExpdHVhbmlhIGFuZCBNYWx0YS4gVGhlIHJlc3VsdHMgYXJlIHByZXR0eSBzaW1pbGFyIHdpdGggdGhlIHJlc3VsdHMgZnJvbSBvdXIgcmFua2luZyBtb2RlbDogSG9uZyBLb25nLCBLb3JlYSwgTWFsYXlzaWEsIElzcmFlbCwgQ3plY2gsIENoaWxlLCBFc3RvbmlhLCBJY2VsYW5kLCBMdXhlbWJvdXJnLCBTbG92ZW5pYSwgQ3lwcnVzLCBMYXR2aWEsIExpdGh1YW5pYSwgTWFsdGEsIFBvbGFuZCwgUWF0YXIsIENoaW5hLCBTYXVkaSBBcmFiaWEuIA0KDQpgYGB7cn0NClNob3VsZF9FeHBhbmRfTGlzdCA8LSBjKCJDaGlsZSIsICJDemVjaCBSZXB1YmxpYyIsICJFc3RvbmlhIiwgIkhvbmcgS29uZyBTQVIsIENoaW5hIiwgIkljZWxhbmQiLCAiSXNyYWVsIiwgIktvcmVhLCBSZXAuIiwgIkx1eGVtYm91cmciLCAiTWFsYXlzaWEiLCAiUWF0YXIiLCAiU2xvdmVuaWEiLCAiTGl0dWFuaWEiLCAiTWFsdGEiLCAiUG9sYW5kIiwgIkN5cHJ1cyIsICJMYXR2aWEiLCAiTGl0aHVhbmlhIiwgIkNoaW5hIiwgIlNhdWRpIEFyYWJpYSIpDQpgYGANCg0KIyMjIyBTY3JlZSBQbG90DQpBIHNjcmVlIHBsb3QgaXMgY29tbW9ubHkgdXNlZCB0byBkZXRlcm1pbmUgdGhlIG51bWJlciBvZiBjbHVzdGVycyB0byB1c2UgaW4gayBtZWFucyBjbHVzdGVyaW5nLlRoZSByZWNvbW1lbmRlZCBudW1iZXIgb2YgY2x1c3RlciBpcyBhdCB0aGUgImVsYm93IiB3aGVyZSB0aGVyZSdzIGEgYmlnIGNoYW5nZSBpbiBzbG9wZS4gDQpgYGB7cn0NCndzcyA8LSBjKCkNCmZvciAoaSBpbiAxOjE1KSB7DQogIENsdXN0ZXJpbmdfa21lYW5zIDwtIGttZWFucyhDbHVzdGVyaW5nX2RmX25vcm1bLDI6MjZdLCBjZW50ZXJzID0gaSwgbnN0YXJ0ID0gMjApDQogICMgU2F2ZSB0b3RhbCB3aXRoaW4gc3VtIG9mIHNxdWFyZXMgdG8gd3NzIHZhcmlhYmxlDQogIHdzc1tpXSA8LSBDbHVzdGVyaW5nX2ttZWFucyR0b3Qud2l0aGluc3MNCn0NCnBsb3QoMToxNSwgd3NzLCB0eXBlID0gImIiLCANCiAgICAgeGxhYiA9ICJOdW1iZXIgb2YgQ2x1c3RlcnMiLCANCiAgICAgeWxhYiA9ICJXaXRoaW4gZ3JvdXBzIHN1bSBvZiBzcXVhcmVzIikNCmBgYA0KICAgICAgPC9icj4gDQpPcHRpbWFsIGNsdXN0ZXIgaXMgMyBvciA0LiANCg0KDQojIyMjIFRoZSBlZmZlY3Qgb2YgQ2x1c3RlcmluZw0KYGBge3J9DQpTdGF0dXMgPC0gYygpDQpmb3IgKGkgaW4gMTpsZW5ndGgoQ2x1c3RlcmluZ19kZiRDb3VudHJ5X05hbWUpKXsNCiAgaWYgKENsdXN0ZXJpbmdfZGYkQ291bnRyeV9OYW1lW2ldICVpbiUgRXhwYW5kX0NvdW50cmllc19MaXN0KSB7DQogICAgU3RhdHVzID0gYXBwZW5kKFN0YXR1cywgIkFscmVhZHkgRXhwYW5kZWQiKQ0KICB9IGVsc2UgaWYgKENsdXN0ZXJpbmdfZGYkQ291bnRyeV9OYW1lW2ldICVpbiVTaG91bGRfRXhwYW5kX0xpc3QpIHsNCiAgICBTdGF0dXMgPSBhcHBlbmQoU3RhdHVzLCAiU2hvdWxkIEV4cGFuZGVkIikNCiAgfSBlbHNlIHsNCiAgICBTdGF0dXMgPSBhcHBlbmQoU3RhdHVzLCAiU2hvdWxkIE5vdCBFeHBhbmRlZCIpDQogIH0NCn0NCmBgYA0KDQpgYGB7cn0NCmZvciAoaSBpbiAzOjcpIHsNCiAgc2V0LnNlZWQoMSkNCiAgayA9IGkNCiAgQ2x1c3RlcmluZ19rbWVhbnMgPC0gIGttZWFucyhDbHVzdGVyaW5nX2RmX25vcm1bLDI6MjZdLCBjZW50ZXJzID0gaywgbnN0YXJ0ID0gMjApICAjIFVzZSBuX3N0YXJ0IHRvICJzdGFiYWxpemUiIHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMuIA0KICBkZl9wbG90IDwtIENsdXN0ZXJpbmdfZGYNCiAgZGZfcGxvdCRFeHBhbmQgPC0gU3RhdHVzDQogIGRmX3Bsb3QkQ2x1c3RlciA8LSBDbHVzdGVyaW5nX2ttZWFucyRjbHVzdGVyDQogIHBsb3QgPC0gZ2dwbG90KGRhdGEgPSBkZl9wbG90LCBhZXMoeCA9IEludGVybmV0LnVzZXJzLnBlcmNlbnQub2YucG9wdWxhdGlvbiwgeSA9IEdDSV8yMDE5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9IGZhY3RvcihDbHVzdGVyKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hhcGUgPSBmYWN0b3IoRXhwYW5kKSkpICsNCiAgICBnZW9tX3BvaW50KCkgKw0KICAgICMgdGhlbWUobGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KCJDbHVzdGVycyIpKSArDQogICAgeGxhYigiJSBvZiBJbnRlcm5ldCBVc2VycyIpICsNCiAgICB5bGFiKCJHbG9iYWwgQ29tcGV0aXRpdmVuZXNzIEluZGV4IikgKyANCiAgICBnZ3RpdGxlKHBhc3RlMCgiUGVyY2VudGFnZSBvZiBJbnRlcm5ldCBVc2VycyBhZ2FpbnN0IEdDSSB3aXRoICIsIGksICIgY2x1c3RlcnMiKSkgKw0KICAgIHNjYWxlX2NvbG91cl9kaXNjcmV0ZSgiQ2x1c3RlcnMiKSArDQogICAgc2NhbGVfc2hhcGVfZGlzY3JldGUoIkV4cGFuZCIpDQogIHByaW50KHBsb3QpDQp9DQoNCmBgYA0KICAgICA8L2JyPg0KSXQgaXMgaGFyZCB0byB2aXN1YWxpemUgdGhlIGVmZmVjdCBvZiBjbHVzdGVyaW5nIHVzaW5nIGFsbCB2YWlyYWJsZXMsIGJ1dCB3ZSBjYW4gc3RpbGwgc2VlIHRoZSBlZmZlY3QgaW4gdGhpcyB0d28gZGltZW5zaW9uYWwgZ3JhcGguIENvdW50cmllcyBjbG9zZXIgdG9nZXRoZXIgYXJlIGdyb3VwZWQgaW4gdGhlIHNhbWUgY2x1c3RlcnMsIGFuZCBpdCBpcyBub3Qgc3VycHJpc2luZyB0aGF0IHRoZSBjb3VudHJpZXMgb24gdGhlIHVwcGVyIHJpZ2h0IGhhbmQgc2lkZSAodGhlIGNvdW50cmllcyB3aXRoIGhpZ2hlciBwZXJjZW50YWdlIG9mIGludGVybmV0IHVzZXJzIGFuZCBoaWdoZXIgR0NJIHNjb3JlKSB3b3VsZCBiZSBtb3JlIGZhdm9yYWJsZSB0byBleHBhbmQgaW50by4gVGhlIGNsdXN0ZXJpbmcgcmVzdWx0IHdpdGggaz0zIGFuZCBrPTQgYXJlIHNpbWlsYXIsIGV4cGVjdCB0aGF0IHRoZSBVbml0ZWQgU3RhdGVzIGhhcyBmb3JtZWQgaXRzIG93biBjbHVzdGVyLiANCg0KDQojIyBDb25jbHVzaW9uDQpVc2luZyB0aHJlZSBkaWZmZXJlbnQgbW9kZWxzOiBSYW5raW5nLCBIaWVyYXJjaGljYWwgQ2x1c3RlcmluZyBhbmQgSy1tZWFucyBDbHVzdGVyaW5nLCB3ZSBjb21lIHVwIHdpdGggYSBzaHJvdCBsaXN0IG9mIGNvdW50cmllcyB0aGF0IHdlIGNvdWxkIGRlbHZlIGludG8gd2l0aG91dCBoYXZpbmcgdG8gbG9vayBpbnRvIGV2ZXJ5IGNvdW50cnkgaW4gdGhlIHdvcmxkLiAgIDwvYnI+IA0KVGhlIGNvdW50cmllcyBmcm9tIFJhbmtpbmc6ICAgIDwvYnI+IA0KSG9uZyBLb25nLCBLb3JlYSwgTWFsYXlzaWEsIElzcmFlbCwgQ3plY2gsIENoaWxlLCBFc3RvbmlhLCBJY2VsYW5kLCBMdXhlbWJvdXJnLCBTbG92ZW5pYSwgQ3lwcnVzLCBMYXR2aWEsIExpdGh1YW5pYSwgTWFsdGEsIFBvbGFuZCwgUWF0YXIsIENoaW5hLCBTYXVkaSBBcmFiaWEgICAgPC9icj4gDQpGcm9tIEhpZXJhcmNoaWNhbCBDbHVzdGVyaW5nOiAgICA8L2JyPiANClJ1c3NpYSwgS29yZWEgICA8L2JyPiANCkZyb20gSy1tZWFucyBDbHVzdGVyaW5nOiAgICA8L2JyPiANCkNoaWxlLCBFc3RvbmlhLCBIb25nIEtvbmcsIEljZWxhbmQgLElzcmFlbCAsS29yZWEsIEx1eGVtYm91cmcsIE1hbGF5c2lhLCBDemVjaCBSZXB1YmxpYywgUWF0YXIsIFNsb3ZlbmlhLCBMaXRodWFuaWEsIE1hbHRhICAgIDwvYnI+IA0KV2l0aCBhIHNob3J0IGxpc3Qgb2YgY291bnRyaWVzIHRvIGZvY3VzIG9uLCB3ZSBjb250aW51ZSBvdXIgcXVhbGl0YXRpdmUgYW5hbHlzaXMgaW4gdGhlc2UgY291bnRyaWVzLiA=