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:

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)

Variables Year Scale Source
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=