Analysis of a public United States Museums dataset from Kaggle: https://www.kaggle.com/imls/museum-directory

The museum dataset is a list of museums and related organizations in the United States. The data file includes basic information about each organization (name, address, phone, website, and revenue) plus the museum type or discipline.

We are interested in extracting some insights about museums in the US, such as

library(tidyverse)
library(ggplot2)
library(gghighlight)

#Importing the csv file to a books Data Frame
museum_data <- read.csv("museums.csv")

#Viewing the Data Frame
head(museum_data )

Data Cleaning and Preparation

In this stage the data is checked for accuracy and completeness prior to beginning the analysis. Some of the issues addressed are as follows:

Remove Extraneous Data

#Identifying the column names
colnames(museum_data)
 [1] "Museum.ID"                                "Museum.Name"                              "Legal.Name"                              
 [4] "Alternate.Name"                           "Museum.Type"                              "Institution.Name"                        
 [7] "Street.Address..Administrative.Location." "City..Administrative.Location."           "State..Administrative.Location."         
[10] "Zip.Code..Administrative.Location."       "Street.Address..Physical.Location."       "City..Physical.Location."                
[13] "State..Physical.Location."                "Zip.Code..Physical.Location."             "Phone.Number"                            
[16] "Latitude"                                 "Longitude"                                "Locale.Code..NCES."                      
[19] "County.Code..FIPS."                       "State.Code..FIPS."                        "Region.Code..AAM."                       
[22] "Employer.ID.Number"                       "Tax.Period"                               "Income"                                  
[25] "Revenue"                                 

There are a lot of columns in the dataframe, however not all of them are useful. Therefore, we will only select a subset of the dataframe containing the columns we are interested in.

#Creating a character vector with all the columns names we are interested in
keep_col <- c("Museum.ID","Legal.Name","Museum.Type","City..Administrative.Location.","State..Administrative.Location.","Zip.Code..Administrative.Location.","Income","Revenue"   )

museum <-museum_data[keep_col]

head(museum)

Missing Values

#Identifying total number of missing values
sum(is.na(museum))
[1] 20893

There are a significant number of missing values. Let’s check which columns have missing values

#Identifying total number of missing values
summary(museum)
   Museum.ID          Legal.Name        Museum.Type        City..Administrative.Location. State..Administrative.Location.
 Min.   :8.400e+09   Length:33072       Length:33072       Length:33072                   Length:33072                   
 1st Qu.:8.402e+09   Class :character   Class :character   Class :character               Class :character               
 Median :8.404e+09   Mode  :character   Mode  :character   Mode  :character               Mode  :character               
 Mean   :8.404e+09                                                                                                       
 3rd Qu.:8.405e+09                                                                                                       
 Max.   :8.410e+09                                                                                                       
                                                                                                                         
 Zip.Code..Administrative.Location.     Income              Revenue          
 Length:33072                       Min.   :-9.230e+02   Min.   :  -2127393  
 Class :character                   1st Qu.: 0.000e+00   1st Qu.:         0  
 Mode  :character                   Median : 8.781e+03   Median :      3307  
                                    Mean   : 1.070e+08   Mean   :  20976047  
                                    3rd Qu.: 2.164e+05   3rd Qu.:    167696  
                                    Max.   : 8.318e+10   Max.   :5840349457  
                                    NA's   :10111        NA's   :10782       

As seen above the Income and Revenue Columns have missing values. These columns also seem to have negative values, which we have to correct. As we do not have a way to accurately replace the missing data we will be dropping the associated rows.

Dropping Rows with Missing Values

#Removing rows with missing data
#The complete.cases() function will examine a dataframe and return a result vector of the rows which contain missing values.
museum <- na.omit(museum)

#Checking for any remaining missing values
sum(is.na(museum))
[1] 0

There are no more missing values.

Correcting Formatting Issues

Income and Revenue Columns have negative values. The negative values could represent two scenarios:

  • Museums are operating at a loss, which accounts for the negative income and negative revenue.
  • Error in the data input process which resulted in a negative value being entered.

In the absence of specific context, we do not know how or if these values need to be corrected. Therefore, we will assume that these negative values represent inaccurate entries and we will be dropping these values.

#Removing all rows with income and revenue less than 0
museum <- museum %>% filter((Income >= 0) & (Revenue >= 0))

Duplicate Data

#Number of Museum/Institution Names in the dataframe
length(museum$Legal.Name)
[1] 22250
#Number of unique Museum/Institution Names in the dataframe
length(unique(museum$Legal.Name))
[1] 20276

As we see here, there are clearly some museum names that have been repeated. ### Removing Duplicates

#Keeping only rows with distinct Legal.Name values
museum <- museum %>% distinct(Legal.Name, .keep_all = TRUE)

Exploratory Data Analysis

In this stage, we will examine the data to identify any patterns, trends and relationships between the variables. It will help us analyze the data and extract insights that can be used to make decisions.

Data Visualization will give us a clear idea of what the data means by giving it visual context.

Statistics

To understand the museum data at a high level we can start be looking at income and revenue in more detail

#Calculating average income, total income, average revenue and total revenue
avg_inc = mean(museum$Income) 
max_inc = max(museum$Income) 
avg_rev = mean(museum$Revenue) 
max_rev = max(museum$Revenue) 

#Calculating number of museums with zero income and revenue
zero_inc_rev <- museum %>% filter((Income == 0) & (Revenue == 0))

cat('The average museum income:', avg_inc, ' and the highest museum income:', max_inc, '\nThe average museum revenue', avg_rev, ' and the highest revenue:', max_rev, '\nThe number of museums with no income and revenue are:', length(zero_inc_rev))
The average museum income: 18073371  and the highest museum income: 83181439574 
The average museum revenue 8011790  and the highest revenue: 5840349457 
The number of museums with no income and revenue are: 8
#Number of unique Museum Types, Cities and States
length(unique(museum$Museum.Type))
[1] 9
length(unique(museum$City..Administrative.Location.))
[1] 7236
length(unique(museum$State..Administrative.Location.))
[1] 51

We have museum data for museums of 9 Types, located in 7236 Cities across 51 States.

Grouping Museum Data by Type, City and State

We can create a few functions to help us aggregate the museum data and

#Creating a function to create a dataframe with aggregate data
museum_group <- function(col_name) {
  
  #Grouping by col_name
  group_name <- museum %>% group_by(.dots = col_name)
  
  #Creating a data frame to store the summarized values of museums by col_name
  #tally() gives us a count of how many museums belong to the category
  museum_group_name <- group_name %>% tally()
  
  #Renaming the columns
  colnames(museum_group_name)[which(names(museum_group_name) == "n")] <- "Museum_Count"
  
  #Summarizing by average income
  group_name_average_income <- group_name %>% summarise(Income = mean(Income))
  museum_group_name$Average_Income<- group_name_average_income$Income
  
  #Summarizing by total income
  group_name_total_income <- group_name %>% summarise(Income = sum(Income))
  museum_group_name$Total_Income<- group_name_total_income$Income
  
  #Summarizing by average revenue
  group_name_average_revenue <- group_name %>% summarise(Revenue = mean(Revenue))
  museum_group_name$Average_Revenue<- group_name_average_revenue$Revenue
  
  #Summarizing by total revenue
  group_name_total_revenue <- group_name %>% summarise(Revenue = sum(Revenue))
  museum_group_name$Total_Revenue<- group_name_total_revenue$Revenue
  
  #Returning a dataframe
  return (museum_group_name)
}

#Creating a function to output maximum values from the dataframe with aggregate data
museum_group_max <- function(df,col_name) {
    
  avg_inc_max <- df %>% filter(Average_Income == max(df$Average_Income))
  tot_inc_max <- df %>% filter(Total_Income == max(df$Total_Income))
  avg_rev_max <- df %>% filter(Average_Revenue == max(df$Average_Revenue))
  tot_rev_max <- df %>% filter(Total_Revenue == max(df$Total_Revenue))
  
  #Calculating the highest museum count and associated col_name value
  count_max <- df %>% filter(Museum_Count == max(df$Museum_Count))
  
  return (cat('Museum Types with highest average income:', avg_inc_max[[col_name]], ' highest total income:', tot_inc_max[[col_name]], '\nhighest average revenue', avg_rev_max[[col_name]], ' highest total revenue:',tot_rev_max[[col_name]], '\n\n', count_max[[col_name]], 'which has',count_max$Museum_Count, ' museums, has the most museums.'))
}

#Creating a function to output minimum values from the dataframe with aggregate data
museum_group_min <- function(df,col_name) {

  avg_inc_min <- df %>% filter(Average_Income == min(df$Average_Income))
  tot_inc_min <- df %>% filter(Total_Income == min(df$Total_Income))
  avg_rev_min <- df %>% filter(Average_Revenue == min(df$Average_Revenue))
  tot_rev_min <- df %>% filter(Total_Revenue == min(df$Total_Revenue))

  #Calculating the lowest museum count and associated col_name value
  count_min <- df %>% filter(Museum_Count == min(df$Museum_Count))
  
  return (cat('\n\nMuseums with lowest average income:', avg_inc_min[[col_name]], ' lowest total income:', tot_inc_min[[col_name]], '\nlowest average revenue',avg_rev_min[[col_name]], ' lowest total revenue:', tot_rev_min[[col_name]],'\n\n', count_min[[col_name]], 'which has',count_min$Museum_Count, ' museums, has the least museums.'))
}

Type

To understand the museum data better we can group the data by Type and take a look at the average and total Income and Revenue.

#Grouping by type
museum_type <- museum_group('Museum.Type')
Warning: The `.dots` argument of `group_by()` is deprecated as of dplyr 1.0.0.
This warning is displayed once every 8 hours.
Call `lifecycle::last_warnings()` to see where this warning was generated.
#Renaming the column
colnames(museum_type)[which(names(museum_type) == "Museum.Type")] <- "Type"

head(museum_type)
NA
museum_group_max(museum_type,'Type')
Museum Types with highest average income: SCIENCE & TECHNOLOGY MUSEUM OR PLANETARIUM  highest total income: ART MUSEUM 
highest average revenue ART MUSEUM  highest total revenue: ART MUSEUM 

 HISTORIC PRESERVATION which has 11340  museums, has the most museums.
museum_group_min(museum_type,'Type')


Museums with lowest average income: HISTORIC PRESERVATION  lowest total income: CHILDREN'S MUSEUM 
lowest average revenue HISTORIC PRESERVATION  lowest total revenue: CHILDREN'S MUSEUM 

 NATURAL HISTORY MUSEUM which has 205  museums, has the least museums.
  • Science & Technology Museums/Planetariums have the highest average income.
  • Art Museums have the highest total income, average revenue and total revenue.
  • Historic Preservations have the lowest average income and average revenue, and this museum type has the most museums as well.
  • Children’s Museums have the lowest total income and total revenue.
  • Natural History Museums have the least museums among all museum types.
#plotting bar graph for museum type and number of Museums
ggplot(data=museum_type,aes(x=Type,y=Museum_Count, fill=Type)) + geom_bar(stat="identity") + labs(title="Number of Museums by Type")+ theme(axis.title.x=element_blank(), axis.text.x=element_blank(), axis.ticks.x=element_blank())

Total Income by Museum Type

#Creating a Pie Chart 
type_income = museum_type$Total_Income
type_labels = museum_type$Type

# Plot the chart.
pie(type_income, labels=type_labels, main = "Distribution of Total Income by Museum Type",col = rainbow(length(type_income)))

As seen above Art Museums make up the biggest portion of all museum income in the US, while Children’s Museums make up the smallest portion of all museum income in the US. We can check to see if a similar pattern is observed with revenue.

Total Revenue by Museum Type

#Creating a Pie Chart 
type_revenue = museum_type$Total_Revenue
type_labels = museum_type$Type

# Plot the chart.
pie(type_revenue, labels=type_labels, main = "Distribution of Total Revenue by Museum Type",col = rainbow(length(type_revenue)))

Once again Art Museums make up the biggest portion of all museum revenue in the US, while Children’s Museums make up the smallest portion of all museum revenue in the US. For the museum data the revenue always lower than income so we could assume that the revenue is what the museum is left over with after deducting taxes and other expenses.

  • It it noteworthy that the Art Museum’s revenue makes up a bigger portion of all museum revenue.
  • The Art Museum’s is retaining a larger portion of its income as opposed to the other museum types.
  • If this were not the case then we would expect the distribution to be similar to that of income.

City

We can group the data by City as well and take a look at the average and total Income and Revenue.

#Grouping by city
museum_city <- museum_group('City..Administrative.Location.')

#Renaming the column
colnames(museum_city)[which(names(museum_city) == "City..Administrative.Location.")] <- "City"

head(museum_city)
museum_group_max(museum_city,'City')
Museum Types with highest average income: AMADO  highest total income: AMADO 
highest average revenue AMADO  highest total revenue: WASHINGTON 

 NEW YORK which has 172  museums, has the most museums.
  • Amado has the highest average income, average revenue and total income.
  • Washington has the highest total revenue.
  • New York City has the most museums.

Number of Museums vs. Total Income

#plotting scatter plot for number of museums vs. total income from museums in each city
ggplot(data=museum_city,aes(x=Museum_Count,y=Total_Income)) + geom_point() + labs(title="City Level Data: Number of Museums vs. Total Income") + gghighlight(Total_Income > 10000000000)

As seen above the vast majority of cities make under 10 Billion in income from museums.

Number of Museums vs. Total Revenue

#plotting scatter plot for number of museums vs. total income from museums in each state
ggplot(data=museum_city,aes(x=Museum_Count,y=Total_Revenue)) + geom_point() + labs(title="City Level Data: Number of Museums vs. Total Revenue") + gghighlight(Total_Revenue > 2000000000)

As seen above the vast majority of cities make under 2 Billion in revenue from museums.

State

We can group the data by State as well and take a look at the average and total Income and Revenue.

#Grouping by state
museum_state <- museum_group('State..Administrative.Location.')

#Renaming the column
colnames(museum_state)[which(names(museum_state) == "State..Administrative.Location.")] <- "State"

head(museum_state)
museum_group_max(museum_state,'State')
Museum Types with highest average income: AZ  highest total income: AZ 
highest average revenue DC  highest total revenue: CA 

 CA which has 1596  museums, has the most museums.
museum_group_min(museum_state,'State')


Museums with lowest average income: ND  lowest total income: ND 
lowest average revenue ND  lowest total revenue: ND 

 UT which has 63  museums, has the least museums.
  • Arizona (AZ) has the highest average and total income.
  • Washington DC (DC) has the highest average revenue.
  • California (CA) has the highest total revenue and and also the most museums.
  • North Dakota (ND) has the lowest average and total income and revenue.
  • Utah (UT) has the least museums.

Number of Museums vs. Total Income

#plotting scatter plot for number of museums vs. total income from museums in each state
ggplot(data=museum_state,aes(x=Museum_Count,y=Total_Income)) + geom_point() + labs(title="State Level Data: Number of Museums vs. Total Income") + gghighlight(Total_Income > 10000000000)

Similar to the city level data, the majority of states make under 10 Billion in income from museums.

Number of Museums vs. Total Revenue

#plotting scatter plot for number of museums vs. total income from museums in each state
ggplot(data=museum_state,aes(x=Museum_Count,y=Total_Revenue)) + geom_point() + labs(title="State Level Data: Number of Museums vs. Total Revenue") + gghighlight(Total_Revenue > 5000000000)

The majority of states make under 5 Billion in revenue from museums.

Summary of Data Analysis

We have museum data for museums of 9 Types, located in 7236 Cities across 51 States.

Museum Types

  • Science & Technology Museums/Planetariums have the highest average income.
  • Art Museums have the highest total income, average revenue and total revenue.
  • Historic Preservations have the lowest average income and average revenue, and this museum type has the most museums as well.
  • Children’s Museums have the lowest total income and total revenue.
  • Natural History Museums have the least museums among all museum types.

Art Museums

Art Museums make up the biggest portion of all museum income and revenue in the US, while Children’s Museums make up the smallest portion of all museum income and revenue For the museum data the revenue always lower than income so we could assume that the revenue is what the museum is left over with after deducting taxes and other expenses.

  • It is noteworthy that the Art Museums revenue makes up a bigger portion of all museum revenue.
  • The Art Museums are retaining a larger portion of its income as opposed to the other museum types.
  • If this were not the case then we would expect the distribution to be similar to that of income.

City

  • Amado has the highest average income, average revenue and total income.
  • Washington has the highest total revenue.
  • New York City has the most museums.

The vast majority of cities make under 10 Billion in income and under 2 Billion in revenue from museums.

State

  • Arizona (AZ) has the highest average and total income.
  • Washington DC (DC)has the highest average revenue.
  • California (CA) has the highest total revenue and also the most museums.
  • North Dakota (ND) has the lowest average and total income and revenue.
  • Utah (UT) has the least museums.

The majority of states make under 10 Billion in income and under 5 Billion in revenue from museums. If we take into context the city level data, it becomes clear that the majority of income and revenue generated from museums in each state comes from just a few cities in that state.

LS0tCnRpdGxlOiAiTXVzZXVtcyBEYXRhIEFuYWx5c2lzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tCkFuYWx5c2lzIG9mIGEgcHVibGljIFVuaXRlZCBTdGF0ZXMgTXVzZXVtcyBkYXRhc2V0IGZyb20gS2FnZ2xlOiBodHRwczovL3d3dy5rYWdnbGUuY29tL2ltbHMvbXVzZXVtLWRpcmVjdG9yeQoKVGhlIG11c2V1bSBkYXRhc2V0IGlzIGEgbGlzdCBvZiBtdXNldW1zIGFuZCByZWxhdGVkIG9yZ2FuaXphdGlvbnMgaW4gdGhlIFVuaXRlZCBTdGF0ZXMuIFRoZSBkYXRhIGZpbGUgaW5jbHVkZXMgYmFzaWMgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBvcmdhbml6YXRpb24gKG5hbWUsIGFkZHJlc3MsIHBob25lLCB3ZWJzaXRlLCBhbmQgcmV2ZW51ZSkgcGx1cyB0aGUgbXVzZXVtIHR5cGUgb3IgZGlzY2lwbGluZS4gCgpXZSBhcmUgaW50ZXJlc3RlZCBpbiBleHRyYWN0aW5nIHNvbWUgaW5zaWdodHMgYWJvdXQgbXVzZXVtcyBpbiB0aGUgVVMsIHN1Y2ggYXMgCgotIFdoaWNoIGNpdHkgb3Igc3RhdGUgaGFzIHRoZSBtb3N0IG11c2V1bXMgcGVyIGNhcGl0YT8gCi0gSG93IG1hbnkgem9vcyBvciBhcXVhcml1bXMgZXhpc3QgaW4gdGhlIFVuaXRlZCBTdGF0ZXM/IAotIFdoYXQgbXVzZXVtIG9yIHJlbGF0ZWQgb3JnYW5pemF0aW9uIGhhZCB0aGUgaGlnaGVzdCByZXZlbnVlIGxhc3QgeWVhcj8KLSBIb3cgZG9lcyB0aGUgY29tcG9zaXRpb24gb2YgbXVzZXVtIHR5cGVzIGRpZmZlciBhY3Jvc3MgdGhlIGNvdW50cnk/CgoKYGBge3J9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZ2doaWdobGlnaHQpCgojSW1wb3J0aW5nIHRoZSBjc3YgZmlsZSB0byBhIGJvb2tzIERhdGEgRnJhbWUKbXVzZXVtX2RhdGEgPC0gcmVhZC5jc3YoIm11c2V1bXMuY3N2IikKCiNWaWV3aW5nIHRoZSBEYXRhIEZyYW1lCmhlYWQobXVzZXVtX2RhdGEgKQpgYGAKIyBEYXRhIENsZWFuaW5nIGFuZCBQcmVwYXJhdGlvbgpJbiB0aGlzIHN0YWdlIHRoZSBkYXRhIGlzIGNoZWNrZWQgZm9yIGFjY3VyYWN5IGFuZCBjb21wbGV0ZW5lc3MgcHJpb3IgdG8gYmVnaW5uaW5nIHRoZSBhbmFseXNpcy4gU29tZSBvZiB0aGUgaXNzdWVzIGFkZHJlc3NlZCBhcmUgYXMgZm9sbG93czoKCi0gUmVtb3ZlIGV4dHJhbmVvdXMgZGF0YQotIENoZWNrIGZvciBpbiBtaXNzaW5nIHZhbHVlcwotIFJlcGxhY2UgbWlzc2luZyB2YWx1ZXMKLSBEZWxldGUgZGF0YSB0aGF0IGNhbm5vdCBiZSBjb3JyZWN0ZWQvcmVwbGFjZWQKLSBDb3JyZWN0IGFueSBkYXRhIGZvcm1hdHRpbmcgaXNzdWVzCi0gQ3JlYXRpbmcgbmV3IGZlYXR1cmVzCi0gSWRlbnRpZnkgZXJyb3JzIHJldmVhbGVkIHdoZW4gbmV3IHZhcmlhYmxlcyBhcmUgY3JlYXRlZAoKIyMgUmVtb3ZlIEV4dHJhbmVvdXMgRGF0YQoKYGBge3J9CiNJZGVudGlmeWluZyB0aGUgY29sdW1uIG5hbWVzCmNvbG5hbWVzKG11c2V1bV9kYXRhKQpgYGAKVGhlcmUgYXJlIGEgbG90IG9mIGNvbHVtbnMgaW4gdGhlIGRhdGFmcmFtZSwgaG93ZXZlciBub3QgYWxsIG9mIHRoZW0gYXJlIHVzZWZ1bC4gVGhlcmVmb3JlLCB3ZSB3aWxsIG9ubHkgc2VsZWN0IGEgc3Vic2V0IG9mIHRoZSBkYXRhZnJhbWUgY29udGFpbmluZyB0aGUgY29sdW1ucyB3ZSBhcmUgaW50ZXJlc3RlZCBpbi4KCmBgYHtyfQojQ3JlYXRpbmcgYSBjaGFyYWN0ZXIgdmVjdG9yIHdpdGggYWxsIHRoZSBjb2x1bW5zIG5hbWVzIHdlIGFyZSBpbnRlcmVzdGVkIGluCmtlZXBfY29sIDwtIGMoIk11c2V1bS5JRCIsIkxlZ2FsLk5hbWUiLCJNdXNldW0uVHlwZSIsIkNpdHkuLkFkbWluaXN0cmF0aXZlLkxvY2F0aW9uLiIsIlN0YXRlLi5BZG1pbmlzdHJhdGl2ZS5Mb2NhdGlvbi4iLCJaaXAuQ29kZS4uQWRtaW5pc3RyYXRpdmUuTG9jYXRpb24uIiwiSW5jb21lIiwiUmV2ZW51ZSIgICApCgptdXNldW0gPC1tdXNldW1fZGF0YVtrZWVwX2NvbF0KCmhlYWQobXVzZXVtKQpgYGAKCiMjIE1pc3NpbmcgVmFsdWVzCmBgYHtyfQojSWRlbnRpZnlpbmcgdG90YWwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzCnN1bShpcy5uYShtdXNldW0pKQpgYGAKVGhlcmUgYXJlIGEgc2lnbmlmaWNhbnQgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzLiBMZXQncyBjaGVjayB3aGljaCBjb2x1bW5zIGhhdmUgbWlzc2luZyB2YWx1ZXMgCmBgYHtyfQojSWRlbnRpZnlpbmcgdG90YWwgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzCnN1bW1hcnkobXVzZXVtKQpgYGAKCkFzIHNlZW4gYWJvdmUgdGhlIEluY29tZSBhbmQgUmV2ZW51ZSBDb2x1bW5zIGhhdmUgbWlzc2luZyB2YWx1ZXMuIFRoZXNlIGNvbHVtbnMgYWxzbyBzZWVtIHRvIGhhdmUgbmVnYXRpdmUgdmFsdWVzLCB3aGljaCB3ZSBoYXZlIHRvIGNvcnJlY3QuIEFzIHdlIGRvIG5vdCBoYXZlIGEgd2F5IHRvIGFjY3VyYXRlbHkgcmVwbGFjZSB0aGUgbWlzc2luZyBkYXRhIHdlIHdpbGwgYmUgZHJvcHBpbmcgdGhlIGFzc29jaWF0ZWQgcm93cy4gCgojIyMgRHJvcHBpbmcgUm93cyB3aXRoIE1pc3NpbmcgVmFsdWVzCmBgYHtyfQojUmVtb3Zpbmcgcm93cyB3aXRoIG1pc3NpbmcgZGF0YQojVGhlIGNvbXBsZXRlLmNhc2VzKCkgZnVuY3Rpb24gd2lsbCBleGFtaW5lIGEgZGF0YWZyYW1lIGFuZCByZXR1cm4gYSByZXN1bHQgdmVjdG9yIG9mIHRoZSByb3dzIHdoaWNoIGNvbnRhaW4gbWlzc2luZyB2YWx1ZXMuCm11c2V1bSA8LSBuYS5vbWl0KG11c2V1bSkKCiNDaGVja2luZyBmb3IgYW55IHJlbWFpbmluZyBtaXNzaW5nIHZhbHVlcwpzdW0oaXMubmEobXVzZXVtKSkKYGBgClRoZXJlIGFyZSBubyBtb3JlIG1pc3NpbmcgdmFsdWVzLgoKIyMgQ29ycmVjdGluZyBGb3JtYXR0aW5nIElzc3VlcwpJbmNvbWUgYW5kIFJldmVudWUgQ29sdW1ucyBoYXZlIG5lZ2F0aXZlIHZhbHVlcy4gVGhlIG5lZ2F0aXZlIHZhbHVlcyBjb3VsZCByZXByZXNlbnQgdHdvIHNjZW5hcmlvczoKCi0gTXVzZXVtcyBhcmUgb3BlcmF0aW5nIGF0IGEgbG9zcywgd2hpY2ggYWNjb3VudHMgZm9yIHRoZSBuZWdhdGl2ZSBpbmNvbWUgYW5kIG5lZ2F0aXZlIHJldmVudWUuCi0gRXJyb3IgaW4gdGhlIGRhdGEgaW5wdXQgcHJvY2VzcyB3aGljaCByZXN1bHRlZCBpbiBhIG5lZ2F0aXZlIHZhbHVlIGJlaW5nIGVudGVyZWQuIAoKSW4gdGhlIGFic2VuY2Ugb2Ygc3BlY2lmaWMgY29udGV4dCwgd2UgZG8gbm90IGtub3cgaG93IG9yIGlmIHRoZXNlIHZhbHVlcyBuZWVkIHRvIGJlIGNvcnJlY3RlZC4gVGhlcmVmb3JlLCB3ZSB3aWxsIGFzc3VtZSB0aGF0IHRoZXNlIG5lZ2F0aXZlIHZhbHVlcyByZXByZXNlbnQgaW5hY2N1cmF0ZSBlbnRyaWVzIGFuZCB3ZSB3aWxsIGJlIGRyb3BwaW5nIHRoZXNlIHZhbHVlcy4gCgpgYGB7cn0KI1JlbW92aW5nIGFsbCByb3dzIHdpdGggaW5jb21lIGFuZCByZXZlbnVlIGxlc3MgdGhhbiAwCm11c2V1bSA8LSBtdXNldW0gJT4lIGZpbHRlcigoSW5jb21lID49IDApICYgKFJldmVudWUgPj0gMCkpCmBgYAoKCiMjIER1cGxpY2F0ZSBEYXRhIApgYGB7cn0KI051bWJlciBvZiBNdXNldW0vSW5zdGl0dXRpb24gTmFtZXMgaW4gdGhlIGRhdGFmcmFtZQpsZW5ndGgobXVzZXVtJExlZ2FsLk5hbWUpCiNOdW1iZXIgb2YgdW5pcXVlIE11c2V1bS9JbnN0aXR1dGlvbiBOYW1lcyBpbiB0aGUgZGF0YWZyYW1lCmxlbmd0aCh1bmlxdWUobXVzZXVtJExlZ2FsLk5hbWUpKQoKYGBgCgpBcyB3ZSBzZWUgaGVyZSwgdGhlcmUgYXJlIGNsZWFybHkgc29tZSBtdXNldW0gbmFtZXMgdGhhdCBoYXZlIGJlZW4gcmVwZWF0ZWQuCiMjIyBSZW1vdmluZyBEdXBsaWNhdGVzIApgYGB7cn0KI0tlZXBpbmcgb25seSByb3dzIHdpdGggZGlzdGluY3QgTGVnYWwuTmFtZSB2YWx1ZXMKbXVzZXVtIDwtIG11c2V1bSAlPiUgZGlzdGluY3QoTGVnYWwuTmFtZSwgLmtlZXBfYWxsID0gVFJVRSkKYGBgCgojIEV4cGxvcmF0b3J5IERhdGEgQW5hbHlzaXMKSW4gdGhpcyBzdGFnZSwgd2Ugd2lsbCBleGFtaW5lIHRoZSBkYXRhIHRvIGlkZW50aWZ5IGFueSBwYXR0ZXJucywgdHJlbmRzIGFuZCByZWxhdGlvbnNoaXBzIGJldHdlZW4gdGhlIHZhcmlhYmxlcy4gSXQgd2lsbCBoZWxwIHVzIGFuYWx5emUgdGhlIGRhdGEgYW5kIGV4dHJhY3QgaW5zaWdodHMgdGhhdCBjYW4gYmUgdXNlZCB0byBtYWtlIGRlY2lzaW9ucy4KCkRhdGEgVmlzdWFsaXphdGlvbiB3aWxsIGdpdmUgdXMgYSBjbGVhciBpZGVhIG9mIHdoYXQgdGhlIGRhdGEgbWVhbnMgYnkgZ2l2aW5nIGl0IHZpc3VhbCBjb250ZXh0LgoKIyMgU3RhdGlzdGljcwpUbyB1bmRlcnN0YW5kIHRoZSBtdXNldW0gZGF0YSBhdCBhIGhpZ2ggbGV2ZWwgd2UgY2FuIHN0YXJ0IGJlIGxvb2tpbmcgYXQgaW5jb21lIGFuZCByZXZlbnVlIGluIG1vcmUgZGV0YWlsCmBgYHtyfQojQ2FsY3VsYXRpbmcgYXZlcmFnZSBpbmNvbWUsIHRvdGFsIGluY29tZSwgYXZlcmFnZSByZXZlbnVlIGFuZCB0b3RhbCByZXZlbnVlCmF2Z19pbmMgPSBtZWFuKG11c2V1bSRJbmNvbWUpIAptYXhfaW5jID0gbWF4KG11c2V1bSRJbmNvbWUpIAphdmdfcmV2ID0gbWVhbihtdXNldW0kUmV2ZW51ZSkgCm1heF9yZXYgPSBtYXgobXVzZXVtJFJldmVudWUpIAoKI0NhbGN1bGF0aW5nIG51bWJlciBvZiBtdXNldW1zIHdpdGggemVybyBpbmNvbWUgYW5kIHJldmVudWUKemVyb19pbmNfcmV2IDwtIG11c2V1bSAlPiUgZmlsdGVyKChJbmNvbWUgPT0gMCkgJiAoUmV2ZW51ZSA9PSAwKSkKCmNhdCgnVGhlIGF2ZXJhZ2UgbXVzZXVtIGluY29tZTonLCBhdmdfaW5jLCAnIGFuZCB0aGUgaGlnaGVzdCBtdXNldW0gaW5jb21lOicsIG1heF9pbmMsICdcblRoZSBhdmVyYWdlIG11c2V1bSByZXZlbnVlJywgYXZnX3JldiwgJyBhbmQgdGhlIGhpZ2hlc3QgcmV2ZW51ZTonLCBtYXhfcmV2LCAnXG5UaGUgbnVtYmVyIG9mIG11c2V1bXMgd2l0aCBubyBpbmNvbWUgYW5kIHJldmVudWUgYXJlOicsIGxlbmd0aCh6ZXJvX2luY19yZXYpKQpgYGAKYGBge3J9CiNOdW1iZXIgb2YgdW5pcXVlIE11c2V1bSBUeXBlcywgQ2l0aWVzIGFuZCBTdGF0ZXMKbGVuZ3RoKHVuaXF1ZShtdXNldW0kTXVzZXVtLlR5cGUpKQpsZW5ndGgodW5pcXVlKG11c2V1bSRDaXR5Li5BZG1pbmlzdHJhdGl2ZS5Mb2NhdGlvbi4pKQpsZW5ndGgodW5pcXVlKG11c2V1bSRTdGF0ZS4uQWRtaW5pc3RyYXRpdmUuTG9jYXRpb24uKSkKYGBgCldlIGhhdmUgbXVzZXVtIGRhdGEgZm9yIG11c2V1bXMgb2YgPGI+OSBUeXBlczwvYj4sIGxvY2F0ZWQgaW4gPGI+NzIzNiBDaXRpZXM8L2I+IGFjcm9zcyA8Yj41MSBTdGF0ZXM8L2I+LgoKIyMjIEdyb3VwaW5nIE11c2V1bSBEYXRhIGJ5IFR5cGUsIENpdHkgYW5kIFN0YXRlCldlIGNhbiBjcmVhdGUgYSBmZXcgZnVuY3Rpb25zIHRvIGhlbHAgdXMgYWdncmVnYXRlIHRoZSBtdXNldW0gZGF0YSBhbmQgCmBgYHtyfQojQ3JlYXRpbmcgYSBmdW5jdGlvbiB0byBjcmVhdGUgYSBkYXRhZnJhbWUgd2l0aCBhZ2dyZWdhdGUgZGF0YQptdXNldW1fZ3JvdXAgPC0gZnVuY3Rpb24oY29sX25hbWUpIHsKICAKICAjR3JvdXBpbmcgYnkgY29sX25hbWUKICBncm91cF9uYW1lIDwtIG11c2V1bSAlPiUgZ3JvdXBfYnkoLmRvdHMgPSBjb2xfbmFtZSkKICAKICAjQ3JlYXRpbmcgYSBkYXRhIGZyYW1lIHRvIHN0b3JlIHRoZSBzdW1tYXJpemVkIHZhbHVlcyBvZiBtdXNldW1zIGJ5IGNvbF9uYW1lCiAgI3RhbGx5KCkgZ2l2ZXMgdXMgYSBjb3VudCBvZiBob3cgbWFueSBtdXNldW1zIGJlbG9uZyB0byB0aGUgY2F0ZWdvcnkKICBtdXNldW1fZ3JvdXBfbmFtZSA8LSBncm91cF9uYW1lICU+JSB0YWxseSgpCiAgCiAgI1JlbmFtaW5nIHRoZSBjb2x1bW5zCiAgY29sbmFtZXMobXVzZXVtX2dyb3VwX25hbWUpW3doaWNoKG5hbWVzKG11c2V1bV9ncm91cF9uYW1lKSA9PSAibiIpXSA8LSAiTXVzZXVtX0NvdW50IgogIAogICNTdW1tYXJpemluZyBieSBhdmVyYWdlIGluY29tZQogIGdyb3VwX25hbWVfYXZlcmFnZV9pbmNvbWUgPC0gZ3JvdXBfbmFtZSAlPiUgc3VtbWFyaXNlKEluY29tZSA9IG1lYW4oSW5jb21lKSkKICBtdXNldW1fZ3JvdXBfbmFtZSRBdmVyYWdlX0luY29tZTwtIGdyb3VwX25hbWVfYXZlcmFnZV9pbmNvbWUkSW5jb21lCiAgCiAgI1N1bW1hcml6aW5nIGJ5IHRvdGFsIGluY29tZQogIGdyb3VwX25hbWVfdG90YWxfaW5jb21lIDwtIGdyb3VwX25hbWUgJT4lIHN1bW1hcmlzZShJbmNvbWUgPSBzdW0oSW5jb21lKSkKICBtdXNldW1fZ3JvdXBfbmFtZSRUb3RhbF9JbmNvbWU8LSBncm91cF9uYW1lX3RvdGFsX2luY29tZSRJbmNvbWUKICAKICAjU3VtbWFyaXppbmcgYnkgYXZlcmFnZSByZXZlbnVlCiAgZ3JvdXBfbmFtZV9hdmVyYWdlX3JldmVudWUgPC0gZ3JvdXBfbmFtZSAlPiUgc3VtbWFyaXNlKFJldmVudWUgPSBtZWFuKFJldmVudWUpKQogIG11c2V1bV9ncm91cF9uYW1lJEF2ZXJhZ2VfUmV2ZW51ZTwtIGdyb3VwX25hbWVfYXZlcmFnZV9yZXZlbnVlJFJldmVudWUKICAKICAjU3VtbWFyaXppbmcgYnkgdG90YWwgcmV2ZW51ZQogIGdyb3VwX25hbWVfdG90YWxfcmV2ZW51ZSA8LSBncm91cF9uYW1lICU+JSBzdW1tYXJpc2UoUmV2ZW51ZSA9IHN1bShSZXZlbnVlKSkKICBtdXNldW1fZ3JvdXBfbmFtZSRUb3RhbF9SZXZlbnVlPC0gZ3JvdXBfbmFtZV90b3RhbF9yZXZlbnVlJFJldmVudWUKICAKICAjUmV0dXJuaW5nIGEgZGF0YWZyYW1lCiAgcmV0dXJuIChtdXNldW1fZ3JvdXBfbmFtZSkKfQoKI0NyZWF0aW5nIGEgZnVuY3Rpb24gdG8gb3V0cHV0IG1heGltdW0gdmFsdWVzIGZyb20gdGhlIGRhdGFmcmFtZSB3aXRoIGFnZ3JlZ2F0ZSBkYXRhCm11c2V1bV9ncm91cF9tYXggPC0gZnVuY3Rpb24oZGYsY29sX25hbWUpIHsKICAgIAogIGF2Z19pbmNfbWF4IDwtIGRmICU+JSBmaWx0ZXIoQXZlcmFnZV9JbmNvbWUgPT0gbWF4KGRmJEF2ZXJhZ2VfSW5jb21lKSkKICB0b3RfaW5jX21heCA8LSBkZiAlPiUgZmlsdGVyKFRvdGFsX0luY29tZSA9PSBtYXgoZGYkVG90YWxfSW5jb21lKSkKICBhdmdfcmV2X21heCA8LSBkZiAlPiUgZmlsdGVyKEF2ZXJhZ2VfUmV2ZW51ZSA9PSBtYXgoZGYkQXZlcmFnZV9SZXZlbnVlKSkKICB0b3RfcmV2X21heCA8LSBkZiAlPiUgZmlsdGVyKFRvdGFsX1JldmVudWUgPT0gbWF4KGRmJFRvdGFsX1JldmVudWUpKQogIAogICNDYWxjdWxhdGluZyB0aGUgaGlnaGVzdCBtdXNldW0gY291bnQgYW5kIGFzc29jaWF0ZWQgY29sX25hbWUgdmFsdWUKICBjb3VudF9tYXggPC0gZGYgJT4lIGZpbHRlcihNdXNldW1fQ291bnQgPT0gbWF4KGRmJE11c2V1bV9Db3VudCkpCiAgCiAgcmV0dXJuIChjYXQoJ011c2V1bSBUeXBlcyB3aXRoIGhpZ2hlc3QgYXZlcmFnZSBpbmNvbWU6JywgYXZnX2luY19tYXhbW2NvbF9uYW1lXV0sICcgaGlnaGVzdCB0b3RhbCBpbmNvbWU6JywgdG90X2luY19tYXhbW2NvbF9uYW1lXV0sICdcbmhpZ2hlc3QgYXZlcmFnZSByZXZlbnVlJywgYXZnX3Jldl9tYXhbW2NvbF9uYW1lXV0sICcgaGlnaGVzdCB0b3RhbCByZXZlbnVlOicsdG90X3Jldl9tYXhbW2NvbF9uYW1lXV0sICdcblxuJywgY291bnRfbWF4W1tjb2xfbmFtZV1dLCAnd2hpY2ggaGFzJyxjb3VudF9tYXgkTXVzZXVtX0NvdW50LCAnIG11c2V1bXMsIGhhcyB0aGUgbW9zdCBtdXNldW1zLicpKQp9CgojQ3JlYXRpbmcgYSBmdW5jdGlvbiB0byBvdXRwdXQgbWluaW11bSB2YWx1ZXMgZnJvbSB0aGUgZGF0YWZyYW1lIHdpdGggYWdncmVnYXRlIGRhdGEKbXVzZXVtX2dyb3VwX21pbiA8LSBmdW5jdGlvbihkZixjb2xfbmFtZSkgewoKICBhdmdfaW5jX21pbiA8LSBkZiAlPiUgZmlsdGVyKEF2ZXJhZ2VfSW5jb21lID09IG1pbihkZiRBdmVyYWdlX0luY29tZSkpCiAgdG90X2luY19taW4gPC0gZGYgJT4lIGZpbHRlcihUb3RhbF9JbmNvbWUgPT0gbWluKGRmJFRvdGFsX0luY29tZSkpCiAgYXZnX3Jldl9taW4gPC0gZGYgJT4lIGZpbHRlcihBdmVyYWdlX1JldmVudWUgPT0gbWluKGRmJEF2ZXJhZ2VfUmV2ZW51ZSkpCiAgdG90X3Jldl9taW4gPC0gZGYgJT4lIGZpbHRlcihUb3RhbF9SZXZlbnVlID09IG1pbihkZiRUb3RhbF9SZXZlbnVlKSkKCiAgI0NhbGN1bGF0aW5nIHRoZSBsb3dlc3QgbXVzZXVtIGNvdW50IGFuZCBhc3NvY2lhdGVkIGNvbF9uYW1lIHZhbHVlCiAgY291bnRfbWluIDwtIGRmICU+JSBmaWx0ZXIoTXVzZXVtX0NvdW50ID09IG1pbihkZiRNdXNldW1fQ291bnQpKQogIAogIHJldHVybiAoY2F0KCdcblxuTXVzZXVtcyB3aXRoIGxvd2VzdCBhdmVyYWdlIGluY29tZTonLCBhdmdfaW5jX21pbltbY29sX25hbWVdXSwgJyBsb3dlc3QgdG90YWwgaW5jb21lOicsIHRvdF9pbmNfbWluW1tjb2xfbmFtZV1dLCAnXG5sb3dlc3QgYXZlcmFnZSByZXZlbnVlJyxhdmdfcmV2X21pbltbY29sX25hbWVdXSwgJyBsb3dlc3QgdG90YWwgcmV2ZW51ZTonLCB0b3RfcmV2X21pbltbY29sX25hbWVdXSwnXG5cbicsIGNvdW50X21pbltbY29sX25hbWVdXSwgJ3doaWNoIGhhcycsY291bnRfbWluJE11c2V1bV9Db3VudCwgJyBtdXNldW1zLCBoYXMgdGhlIGxlYXN0IG11c2V1bXMuJykpCn0KYGBgCgojIyBUeXBlClRvIHVuZGVyc3RhbmQgdGhlIG11c2V1bSBkYXRhIGJldHRlciB3ZSBjYW4gZ3JvdXAgdGhlIGRhdGEgYnkgVHlwZSBhbmQgdGFrZSBhIGxvb2sgYXQgdGhlIGF2ZXJhZ2UgYW5kIHRvdGFsIEluY29tZSBhbmQgUmV2ZW51ZS4KYGBge3J9CiNHcm91cGluZyBieSB0eXBlCm11c2V1bV90eXBlIDwtIG11c2V1bV9ncm91cCgnTXVzZXVtLlR5cGUnKQoKI1JlbmFtaW5nIHRoZSBjb2x1bW4KY29sbmFtZXMobXVzZXVtX3R5cGUpW3doaWNoKG5hbWVzKG11c2V1bV90eXBlKSA9PSAiTXVzZXVtLlR5cGUiKV0gPC0gIlR5cGUiCgpoZWFkKG11c2V1bV90eXBlKQoKYGBgCmBgYHtyfQptdXNldW1fZ3JvdXBfbWF4KG11c2V1bV90eXBlLCdUeXBlJykKYGBgCmBgYHtyfQptdXNldW1fZ3JvdXBfbWluKG11c2V1bV90eXBlLCdUeXBlJykKYGBgCi0gU2NpZW5jZSAmIFRlY2hub2xvZ3kgTXVzZXVtcy9QbGFuZXRhcml1bXMgaGF2ZSB0aGUgaGlnaGVzdCBhdmVyYWdlIGluY29tZS4KLSBBcnQgTXVzZXVtcyBoYXZlIHRoZSBoaWdoZXN0IHRvdGFsIGluY29tZSwgYXZlcmFnZSByZXZlbnVlIGFuZCB0b3RhbCByZXZlbnVlLiAKLSBIaXN0b3JpYyBQcmVzZXJ2YXRpb25zIGhhdmUgdGhlIGxvd2VzdCBhdmVyYWdlIGluY29tZSBhbmQgYXZlcmFnZSByZXZlbnVlLCBhbmQgdGhpcyBtdXNldW0gdHlwZSBoYXMgdGhlIG1vc3QgbXVzZXVtcyBhcyB3ZWxsLgotIENoaWxkcmVuJ3MgTXVzZXVtcyBoYXZlIHRoZSBsb3dlc3QgdG90YWwgaW5jb21lIGFuZCB0b3RhbCByZXZlbnVlLgotIE5hdHVyYWwgSGlzdG9yeSBNdXNldW1zIGhhdmUgdGhlIGxlYXN0IG11c2V1bXMgYW1vbmcgYWxsIG11c2V1bSB0eXBlcy4KCmBgYHtyfQojcGxvdHRpbmcgYmFyIGdyYXBoIGZvciBtdXNldW0gdHlwZSBhbmQgbnVtYmVyIG9mIE11c2V1bXMKZ2dwbG90KGRhdGE9bXVzZXVtX3R5cGUsYWVzKHg9VHlwZSx5PU11c2V1bV9Db3VudCwgZmlsbD1UeXBlKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsgbGFicyh0aXRsZT0iTnVtYmVyIG9mIE11c2V1bXMgYnkgVHlwZSIpKyB0aGVtZShheGlzLnRpdGxlLng9ZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD1lbGVtZW50X2JsYW5rKCkpCgpgYGAKCiMjIyBUb3RhbCBJbmNvbWUgYnkgTXVzZXVtIFR5cGUKYGBge3J9CiNDcmVhdGluZyBhIFBpZSBDaGFydCAKdHlwZV9pbmNvbWUgPSBtdXNldW1fdHlwZSRUb3RhbF9JbmNvbWUKdHlwZV9sYWJlbHMgPSBtdXNldW1fdHlwZSRUeXBlCgojIFBsb3QgdGhlIGNoYXJ0LgpwaWUodHlwZV9pbmNvbWUsIGxhYmVscz10eXBlX2xhYmVscywgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgVG90YWwgSW5jb21lIGJ5IE11c2V1bSBUeXBlIixjb2wgPSByYWluYm93KGxlbmd0aCh0eXBlX2luY29tZSkpKQpgYGAKQXMgc2VlbiBhYm92ZSBBcnQgTXVzZXVtcyBtYWtlIHVwIHRoZSBiaWdnZXN0IHBvcnRpb24gb2YgYWxsIG11c2V1bSBpbmNvbWUgaW4gdGhlIFVTLCB3aGlsZSBDaGlsZHJlbidzIE11c2V1bXMgbWFrZSB1cCB0aGUgc21hbGxlc3QgcG9ydGlvbiBvZiBhbGwgbXVzZXVtIGluY29tZSBpbiB0aGUgVVMuIFdlIGNhbiBjaGVjayB0byBzZWUgaWYgYSBzaW1pbGFyIHBhdHRlcm4gaXMgb2JzZXJ2ZWQgd2l0aCByZXZlbnVlLgoKIyMjIFRvdGFsIFJldmVudWUgYnkgTXVzZXVtIFR5cGUKYGBge3J9CiNDcmVhdGluZyBhIFBpZSBDaGFydCAKdHlwZV9yZXZlbnVlID0gbXVzZXVtX3R5cGUkVG90YWxfUmV2ZW51ZQp0eXBlX2xhYmVscyA9IG11c2V1bV90eXBlJFR5cGUKCiMgUGxvdCB0aGUgY2hhcnQuCnBpZSh0eXBlX3JldmVudWUsIGxhYmVscz10eXBlX2xhYmVscywgbWFpbiA9ICJEaXN0cmlidXRpb24gb2YgVG90YWwgUmV2ZW51ZSBieSBNdXNldW0gVHlwZSIsY29sID0gcmFpbmJvdyhsZW5ndGgodHlwZV9yZXZlbnVlKSkpCmBgYApPbmNlIGFnYWluIEFydCBNdXNldW1zIG1ha2UgdXAgdGhlIGJpZ2dlc3QgcG9ydGlvbiBvZiBhbGwgbXVzZXVtIHJldmVudWUgaW4gdGhlIFVTLCB3aGlsZSBDaGlsZHJlbidzIE11c2V1bXMgbWFrZSB1cCB0aGUgc21hbGxlc3QgcG9ydGlvbiBvZiBhbGwgbXVzZXVtIHJldmVudWUgaW4gdGhlIFVTLiBGb3IgdGhlIG11c2V1bSBkYXRhIHRoZSByZXZlbnVlIGFsd2F5cyBsb3dlciB0aGFuIGluY29tZSBzbyB3ZSBjb3VsZCBhc3N1bWUgdGhhdCB0aGUgcmV2ZW51ZSBpcyB3aGF0IHRoZSBtdXNldW0gaXMgbGVmdCBvdmVyIHdpdGggYWZ0ZXIgZGVkdWN0aW5nIHRheGVzIGFuZCBvdGhlciBleHBlbnNlcy4gCgotIEl0IGl0IG5vdGV3b3J0aHkgdGhhdCB0aGUgQXJ0IE11c2V1bSdzIHJldmVudWUgbWFrZXMgdXAgYSBiaWdnZXIgcG9ydGlvbiBvZiBhbGwgbXVzZXVtIHJldmVudWUuCi0gVGhlIEFydCBNdXNldW0ncyBpcyByZXRhaW5pbmcgYSBsYXJnZXIgcG9ydGlvbiBvZiBpdHMgaW5jb21lIGFzIG9wcG9zZWQgdG8gdGhlIG90aGVyIG11c2V1bSB0eXBlcy4KLSBJZiB0aGlzIHdlcmUgbm90IHRoZSBjYXNlIHRoZW4gd2Ugd291bGQgZXhwZWN0IHRoZSBkaXN0cmlidXRpb24gdG8gYmUgc2ltaWxhciB0byB0aGF0IG9mIGluY29tZS4KCiMjIENpdHkKV2UgY2FuIGdyb3VwIHRoZSBkYXRhIGJ5IENpdHkgYXMgd2VsbCBhbmQgdGFrZSBhIGxvb2sgYXQgdGhlIGF2ZXJhZ2UgYW5kIHRvdGFsIEluY29tZSBhbmQgUmV2ZW51ZS4KCmBgYHtyfQojR3JvdXBpbmcgYnkgY2l0eQptdXNldW1fY2l0eSA8LSBtdXNldW1fZ3JvdXAoJ0NpdHkuLkFkbWluaXN0cmF0aXZlLkxvY2F0aW9uLicpCgojUmVuYW1pbmcgdGhlIGNvbHVtbgpjb2xuYW1lcyhtdXNldW1fY2l0eSlbd2hpY2gobmFtZXMobXVzZXVtX2NpdHkpID09ICJDaXR5Li5BZG1pbmlzdHJhdGl2ZS5Mb2NhdGlvbi4iKV0gPC0gIkNpdHkiCgpoZWFkKG11c2V1bV9jaXR5KQpgYGAKYGBge3J9Cm11c2V1bV9ncm91cF9tYXgobXVzZXVtX2NpdHksJ0NpdHknKQpgYGAKLSBBbWFkbyBoYXMgdGhlIGhpZ2hlc3QgYXZlcmFnZSBpbmNvbWUsIGF2ZXJhZ2UgcmV2ZW51ZSBhbmQgdG90YWwgaW5jb21lLgotIFdhc2hpbmd0b24gaGFzIHRoZSBoaWdoZXN0IHRvdGFsIHJldmVudWUuIAotIE5ldyBZb3JrIENpdHkgaGFzIHRoZSBtb3N0IG11c2V1bXMuCgojIyMgTnVtYmVyIG9mIE11c2V1bXMgdnMuIFRvdGFsIEluY29tZQpgYGB7cn0KI3Bsb3R0aW5nIHNjYXR0ZXIgcGxvdCBmb3IgbnVtYmVyIG9mIG11c2V1bXMgdnMuIHRvdGFsIGluY29tZSBmcm9tIG11c2V1bXMgaW4gZWFjaCBjaXR5CmdncGxvdChkYXRhPW11c2V1bV9jaXR5LGFlcyh4PU11c2V1bV9Db3VudCx5PVRvdGFsX0luY29tZSkpICsgZ2VvbV9wb2ludCgpICsgbGFicyh0aXRsZT0iQ2l0eSBMZXZlbCBEYXRhOiBOdW1iZXIgb2YgTXVzZXVtcyB2cy4gVG90YWwgSW5jb21lIikgKyBnZ2hpZ2hsaWdodChUb3RhbF9JbmNvbWUgPiAxMDAwMDAwMDAwMCkKCmBgYApBcyBzZWVuIGFib3ZlIHRoZSB2YXN0IG1ham9yaXR5IG9mIGNpdGllcyBtYWtlIHVuZGVyIDEwIEJpbGxpb24gaW4gaW5jb21lIGZyb20gbXVzZXVtcy4gCgojIyMgTnVtYmVyIG9mIE11c2V1bXMgdnMuIFRvdGFsIFJldmVudWUKYGBge3J9CiNwbG90dGluZyBzY2F0dGVyIHBsb3QgZm9yIG51bWJlciBvZiBtdXNldW1zIHZzLiB0b3RhbCBpbmNvbWUgZnJvbSBtdXNldW1zIGluIGVhY2ggc3RhdGUKZ2dwbG90KGRhdGE9bXVzZXVtX2NpdHksYWVzKHg9TXVzZXVtX0NvdW50LHk9VG90YWxfUmV2ZW51ZSkpICsgZ2VvbV9wb2ludCgpICsgbGFicyh0aXRsZT0iQ2l0eSBMZXZlbCBEYXRhOiBOdW1iZXIgb2YgTXVzZXVtcyB2cy4gVG90YWwgUmV2ZW51ZSIpICsgZ2doaWdobGlnaHQoVG90YWxfUmV2ZW51ZSA+IDIwMDAwMDAwMDApCgpgYGAKQXMgc2VlbiBhYm92ZSB0aGUgdmFzdCBtYWpvcml0eSBvZiBjaXRpZXMgbWFrZSB1bmRlciAyIEJpbGxpb24gaW4gcmV2ZW51ZSBmcm9tIG11c2V1bXMuCgojIyBTdGF0ZQpXZSBjYW4gZ3JvdXAgdGhlIGRhdGEgYnkgU3RhdGUgYXMgd2VsbCBhbmQgdGFrZSBhIGxvb2sgYXQgdGhlIGF2ZXJhZ2UgYW5kIHRvdGFsIEluY29tZSBhbmQgUmV2ZW51ZS4KYGBge3J9CiNHcm91cGluZyBieSBzdGF0ZQptdXNldW1fc3RhdGUgPC0gbXVzZXVtX2dyb3VwKCdTdGF0ZS4uQWRtaW5pc3RyYXRpdmUuTG9jYXRpb24uJykKCiNSZW5hbWluZyB0aGUgY29sdW1uCmNvbG5hbWVzKG11c2V1bV9zdGF0ZSlbd2hpY2gobmFtZXMobXVzZXVtX3N0YXRlKSA9PSAiU3RhdGUuLkFkbWluaXN0cmF0aXZlLkxvY2F0aW9uLiIpXSA8LSAiU3RhdGUiCgpoZWFkKG11c2V1bV9zdGF0ZSkKYGBgCmBgYHtyfQptdXNldW1fZ3JvdXBfbWF4KG11c2V1bV9zdGF0ZSwnU3RhdGUnKQpgYGAKCmBgYHtyfQptdXNldW1fZ3JvdXBfbWluKG11c2V1bV9zdGF0ZSwnU3RhdGUnKQpgYGAKLSBBcml6b25hIChBWikgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgYW5kIHRvdGFsIGluY29tZS4KLSBXYXNoaW5ndG9uIERDIChEQykgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgcmV2ZW51ZS4KLSBDYWxpZm9ybmlhIChDQSkgaGFzIHRoZSBoaWdoZXN0IHRvdGFsIHJldmVudWUgYW5kIGFuZCBhbHNvIHRoZSBtb3N0IG11c2V1bXMuCi0gTm9ydGggRGFrb3RhIChORCkgaGFzIHRoZSBsb3dlc3QgYXZlcmFnZSBhbmQgdG90YWwgaW5jb21lIGFuZCByZXZlbnVlLgotIFV0YWggKFVUKSBoYXMgdGhlIGxlYXN0IG11c2V1bXMuCgojIyMgTnVtYmVyIG9mIE11c2V1bXMgdnMuIFRvdGFsIEluY29tZQpgYGB7cn0KI3Bsb3R0aW5nIHNjYXR0ZXIgcGxvdCBmb3IgbnVtYmVyIG9mIG11c2V1bXMgdnMuIHRvdGFsIGluY29tZSBmcm9tIG11c2V1bXMgaW4gZWFjaCBzdGF0ZQpnZ3Bsb3QoZGF0YT1tdXNldW1fc3RhdGUsYWVzKHg9TXVzZXVtX0NvdW50LHk9VG90YWxfSW5jb21lKSkgKyBnZW9tX3BvaW50KCkgKyBsYWJzKHRpdGxlPSJTdGF0ZSBMZXZlbCBEYXRhOiBOdW1iZXIgb2YgTXVzZXVtcyB2cy4gVG90YWwgSW5jb21lIikgKyBnZ2hpZ2hsaWdodChUb3RhbF9JbmNvbWUgPiAxMDAwMDAwMDAwMCkKCmBgYApTaW1pbGFyIHRvIHRoZSBjaXR5IGxldmVsIGRhdGEsIHRoZSBtYWpvcml0eSBvZiBzdGF0ZXMgbWFrZSB1bmRlciAxMCBCaWxsaW9uIGluIGluY29tZSBmcm9tIG11c2V1bXMuCgojIyMgTnVtYmVyIG9mIE11c2V1bXMgdnMuIFRvdGFsIFJldmVudWUKYGBge3J9CiNwbG90dGluZyBzY2F0dGVyIHBsb3QgZm9yIG51bWJlciBvZiBtdXNldW1zIHZzLiB0b3RhbCBpbmNvbWUgZnJvbSBtdXNldW1zIGluIGVhY2ggc3RhdGUKZ2dwbG90KGRhdGE9bXVzZXVtX3N0YXRlLGFlcyh4PU11c2V1bV9Db3VudCx5PVRvdGFsX1JldmVudWUpKSArIGdlb21fcG9pbnQoKSArIGxhYnModGl0bGU9IlN0YXRlIExldmVsIERhdGE6IE51bWJlciBvZiBNdXNldW1zIHZzLiBUb3RhbCBSZXZlbnVlIikgKyBnZ2hpZ2hsaWdodChUb3RhbF9SZXZlbnVlID4gNTAwMDAwMDAwMCkKCmBgYApUaGUgbWFqb3JpdHkgb2Ygc3RhdGVzIG1ha2UgdW5kZXIgNSBCaWxsaW9uIGluIHJldmVudWUgZnJvbSBtdXNldW1zLgoKIyBTdW1tYXJ5IG9mIERhdGEgQW5hbHlzaXMKCldlIGhhdmUgbXVzZXVtIGRhdGEgZm9yIG11c2V1bXMgb2YgPGI+OSBUeXBlczwvYj4sIGxvY2F0ZWQgaW4gPGI+NzIzNiBDaXRpZXM8L2I+IGFjcm9zcyA8Yj41MSBTdGF0ZXM8L2I+LgoKIyMgTXVzZXVtIFR5cGVzCgotIDxiPlNjaWVuY2UgJiBUZWNobm9sb2d5IE11c2V1bXMvUGxhbmV0YXJpdW1zPC9iPiBoYXZlIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgaW5jb21lLgotIDxiPkFydCBNdXNldW1zPC9iPiBoYXZlIHRoZSBoaWdoZXN0IHRvdGFsIGluY29tZSwgYXZlcmFnZSByZXZlbnVlIGFuZCB0b3RhbCByZXZlbnVlLiAKLSA8Yj5IaXN0b3JpYyBQcmVzZXJ2YXRpb25zPC9iPiBoYXZlIHRoZSBsb3dlc3QgYXZlcmFnZSBpbmNvbWUgYW5kIGF2ZXJhZ2UgcmV2ZW51ZSwgYW5kIHRoaXMgbXVzZXVtIHR5cGUgaGFzIHRoZSBtb3N0IG11c2V1bXMgYXMgd2VsbC4KLSA8Yj5DaGlsZHJlbidzIE11c2V1bXM8L2I+IGhhdmUgdGhlIGxvd2VzdCB0b3RhbCBpbmNvbWUgYW5kIHRvdGFsIHJldmVudWUuCi0gPGI+TmF0dXJhbCBIaXN0b3J5IE11c2V1bXM8L2I+IGhhdmUgdGhlIGxlYXN0IG11c2V1bXMgYW1vbmcgYWxsIG11c2V1bSB0eXBlcy4KCiMjIyBBcnQgTXVzZXVtcwpBcnQgTXVzZXVtcyBtYWtlIHVwIHRoZSBiaWdnZXN0IHBvcnRpb24gb2YgYWxsIG11c2V1bSBpbmNvbWUgYW5kIHJldmVudWUgaW4gdGhlIFVTLCB3aGlsZSBDaGlsZHJlbidzIE11c2V1bXMgbWFrZSB1cCB0aGUgc21hbGxlc3QgcG9ydGlvbiBvZiBhbGwgbXVzZXVtIGluY29tZSBhbmQgcmV2ZW51ZSAgRm9yIHRoZSBtdXNldW0gZGF0YSB0aGUgcmV2ZW51ZSBhbHdheXMgbG93ZXIgdGhhbiBpbmNvbWUgc28gd2UgY291bGQgYXNzdW1lIHRoYXQgdGhlIHJldmVudWUgaXMgd2hhdCB0aGUgbXVzZXVtIGlzIGxlZnQgb3ZlciB3aXRoIGFmdGVyIGRlZHVjdGluZyB0YXhlcyBhbmQgb3RoZXIgZXhwZW5zZXMuIAoKLSBJdCBpcyBub3Rld29ydGh5IHRoYXQgdGhlIEFydCBNdXNldW1zIHJldmVudWUgbWFrZXMgdXAgYSBiaWdnZXIgcG9ydGlvbiBvZiBhbGwgbXVzZXVtIHJldmVudWUuCi0gVGhlIDx1PkFydCBNdXNldW1zIGFyZSByZXRhaW5pbmcgYSBsYXJnZXIgcG9ydGlvbiBvZiBpdHMgaW5jb21lPC91PiBhcyBvcHBvc2VkIHRvIHRoZSBvdGhlciBtdXNldW0gdHlwZXMuCi0gSWYgdGhpcyB3ZXJlIG5vdCB0aGUgY2FzZSB0aGVuIHdlIHdvdWxkIGV4cGVjdCB0aGUgZGlzdHJpYnV0aW9uIHRvIGJlIHNpbWlsYXIgdG8gdGhhdCBvZiBpbmNvbWUuCgojIyBDaXR5CgotIDxiPkFtYWRvPC9iPiBoYXMgdGhlIGhpZ2hlc3QgYXZlcmFnZSBpbmNvbWUsIGF2ZXJhZ2UgcmV2ZW51ZSBhbmQgdG90YWwgaW5jb21lLgotIDxiPldhc2hpbmd0b248L2I+IGhhcyB0aGUgaGlnaGVzdCB0b3RhbCByZXZlbnVlLiAKLSA8Yj5OZXcgWW9yayBDaXR5PC9iPiBoYXMgdGhlIG1vc3QgbXVzZXVtcy4KClRoZSB2YXN0IG1ham9yaXR5IG9mIGNpdGllcyBtYWtlIDxiPnVuZGVyIDEwIEJpbGxpb24gaW4gaW5jb21lPC9iPiBhbmQgPGI+dW5kZXIgMiBCaWxsaW9uIGluIHJldmVudWU8L2I+IGZyb20gbXVzZXVtcy4gCgojIyBTdGF0ZQoKLSA8Yj5Bcml6b25hIChBWik8L2I+IGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIGFuZCB0b3RhbCBpbmNvbWUuCi0gPGI+V2FzaGluZ3RvbiBEQyAoREMpPC9iPmhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIHJldmVudWUuCi0gPGI+Q2FsaWZvcm5pYSAoQ0EpPC9iPiBoYXMgdGhlIGhpZ2hlc3QgdG90YWwgcmV2ZW51ZSBhbmQgYWxzbyB0aGUgbW9zdCBtdXNldW1zLgotIDxiPk5vcnRoIERha290YSAoTkQpPC9iPiBoYXMgdGhlIGxvd2VzdCBhdmVyYWdlIGFuZCB0b3RhbCBpbmNvbWUgYW5kIHJldmVudWUuCi0gPGI+VXRhaCAoVVQpPC9iPiBoYXMgdGhlIGxlYXN0IG11c2V1bXMuCgpUaGUgbWFqb3JpdHkgb2Ygc3RhdGVzIG1ha2UgdW5kZXIgMTAgQmlsbGlvbiBpbiBpbmNvbWUgYW5kIHVuZGVyIDUgQmlsbGlvbiBpbiByZXZlbnVlIGZyb20gbXVzZXVtcy4gSWYgd2UgdGFrZSBpbnRvIGNvbnRleHQgdGhlIGNpdHkgbGV2ZWwgZGF0YSwgaXQgYmVjb21lcyBjbGVhciB0aGF0IHRoZSBtYWpvcml0eSBvZiBpbmNvbWUgYW5kIHJldmVudWUgZ2VuZXJhdGVkIGZyb20gbXVzZXVtcyBpbiBlYWNoIHN0YXRlIGNvbWVzIGZyb20ganVzdCBhIGZldyBjaXRpZXMgaW4gdGhhdCBzdGF0ZS4gCg==