Morgan State University

Department of Information Science & Systems

Fall 2024

INSS 615: Data Wrangling for Visualization

Name: Natalia Miranda

Due: Dec 1, 2024 (Sunday)

Questions

A. Scrape the College Ranked by Acceptance Rate dataset available at this link: https://www.oedb.org/rankings/acceptance-rate/#table-rankings and select the first 9 columns [Rank, School, Student to Faculty Ratio, Graduation Rate, Retention Rate, Acceptance Rate, Enrollment Rate, Institutional Aid Rate, and Default Rate] as the dataset for this assignment. [20 Points]

Hint: There are 6 pages of data, so you may want to use a for loop to automate the scraping process and combine the data from all 6 pages. This is just a suggestion—you are free to create the dataset without automating the web scrapping process.

Solution:

library(rvest) 
library(tidyverse)
library(readr)
library(dplyr)
library(scales)

# URL OF THE WEBSITE: url_base PAGE# url_end
url_base <- "https://www.oedb.org/rankings/acceptance-rate/page/" # Base URL for the target website
url_end <- "/#table-rankings" # Ending part of URL for the target website

# Empty list to store the tables from each page
tables_list <- list()

# Loop for the MULTIPAGE SCRAPING, through the page numbers (1 to 6)
for (page_num in 1:6) {
    # TARGET WEBSITE: build the full URL for each page
  page_url <- paste0(url_base, page_num, url_end)
  
    # Variable to store the PAGE HTML
  page <- rvest::read_html(page_url)
   
    #Using the HTML code, copy the *Xpath* or *CSS Selector* of the element (text or table) you want to get
  location <- "#content > div.js-data-list-r > table"
    #Extract the element
  page_table <- html_elements(page, css = location)
  
    # Convert the extracted element to a DATAFRAME
  target_table <- html_table(page_table)[[1]]

# Combine all the tables into a single data frame
tables_list[[page_num]] <- target_table [ , 1:9]
}

#Join all table to store it into a SINGLE DATA FRAME
original <- bind_rows(tables_list) #Table to save the original version
college_table <- bind_rows(tables_list) #Table to edit on the following steps

# View the combined dataset
View(college_table)

B. You are going to need the dataset created in Question A to answer the following questions. There are 16 questions each carrying 5 points:

  1. Replace the missing values “N/A” in the dataset with NA. Reviewing N/A Values:
#Checking N/A values within the Dataset
total_na_count <- sum(college_table == "N/A", na.rm = TRUE)
print(paste("Total 'N/A' values in the dataset:", total_na_count))
[1] "Total 'N/A' values in the dataset: 359"
# Column breakdown of N/A values
cols_with_na <- colSums(college_table == "N/A", na.rm = TRUE)
print("Breakdown of 'N/A' values by column:")
[1] "Breakdown of 'N/A' values by column:"
print(cols_with_na[cols_with_na > 0])  # Show only columns with at least one N/A
Graduation Rate  Retention Rate Acceptance Rate Enrollment Rate    Default Rate 
              6               4              29              29             291 

Solution:

#Replace Values
college_table[college_table == "N/A"] <- NA
  1. Convert percentage columns (e.g., Graduation Rate) to numeric format.

Verifying columns names:

names(college_table)
[1] "Rank"                     "School"                   "Student to Faculty Ratio"
[4] "Graduation Rate"          "Retention Rate"           "Acceptance Rate"         
[7] "Enrollment Rate"          "Institutional Aid Rate"   "Default Rate"            

Solution:

# List of percentage columns
#percentage_columns <- c("Graduation Rate", "Retention Rate","Acceptance Rate","Enrollment Rate","Institutional Aid Raid")
college_table$"Graduation Rate" <- as.numeric(gsub("%", "", college_table$"Graduation Rate")) / 100
college_table$"Retention Rate" <- as.numeric(gsub("%", "", college_table$"Retention Rate")) / 100
college_table$"Acceptance Rate" <- as.numeric(gsub("%", "", college_table$"Acceptance Rate")) / 100
college_table$"Enrollment Rate" <- as.numeric(gsub("%", "", college_table$"Enrollment Rate")) / 100
college_table$"Institutional Aid Rate" <- as.numeric(gsub("%", "", college_table$"Institutional Aid Rate")) / 100
college_table$"Default Rate" <- as.numeric(gsub("%", "", college_table$"Default Rate")) / 100
  1. Transform the “Student to Faculty Ratio” column into two separate numeric columns: Students and Faculty.

Solution:

#Separate Columns
college_table <- college_table %>%
  separate("Student to Faculty Ratio", into = c("Students", "Faculty"), sep = " to ")

#Convert columns to NUMERIC VALUE
college_table$Students <- parse_number(college_table$Students)
college_table$Faculty <- parse_number(college_table$Faculty)
  1. What is the count of missing values in the “Default Rate” column? Impute the missing values in the “Default Rate” column with the median value.

Solution:

# Count the missing values in the "Default Rate" column
missing_DefaultRate <- sum(is.na(college_table$"Default Rate"))
missing_DefaultRate
[1] 291
# Calculate the median of the "Default Rate" column, ignoring NAs
median_DefaultRate <- median(college_table$"Default Rate", na.rm = TRUE)
median_DefaultRate
[1] 0.06
# Impute the missing values with the median
college_table$"Default Rate" <- ifelse(is.na(college_table$"Default Rate"),
                                       median_DefaultRate,
                                       college_table$"Default Rate")
  1. Find the average graduation rate for universities ranked in the top 50.

Solution:

# Select Universities ranked in top 50 
U_top_50 <- college_table %>% filter(Rank <= 50)

# Calculate the average graduation rate
avg_grad_rate <- mean(U_top_50$"Graduation Rate", na.rm = TRUE)

avg_grad_rate
[1] 0.7918
  1. Filter universities with a retention rate above 90% and find the count of rows in the subset.

Solution:

# Select universities with a retention rate above 90% (decimal format)
U_90retention <- college_table %>% filter(`Retention Rate` > 0.90)

# Count the rows in the filtered subset
count_90retention <- nrow(U_90retention)

# Print the result
count_90retention
[1] 98
  1. Rank universities by enrollment rate in descending order and display the last 6 rows.

Solution:

# Rank universities by enrollment rate in descending order, ignoring NAs
U_EnrollmentTail <- college_table %>%
  filter(!is.na(`Enrollment Rate`)) %>%  # Exclude rows with NA in Enrollment Rate
  arrange(desc(`Enrollment Rate`))      # Sort in descending order

# Display the last 6 rows
tail_rows <- tail(U_EnrollmentTail, 6)

tail_rows
NA
  1. Create a histogram of graduation rates using ggplot2 library.

HISTOGRAMS in ggplot2 ggplot(data, aes(x = column_name)) + geom_histogram(binwidth = value, fill = “color”, color = “border_color”) + labs(title = “Plot Title”, x = “X-axis Label”, y = “Y-axis Label”) + theme_style()

Solution:


college_table_clean <- college_table %>%
  filter(!is.na(`Graduation Rate`))

# Create a histogram of graduation rates
ggplot(college_table_clean, aes(x = `Graduation Rate`)) +  # Set x-axis
  geom_histogram(fill = "blue", color = "black") +  # Bar settings width and colors
  labs(title = "Graduation Rates Distribution", x = "Graduation Rate", y = "Univerisity Count") +  # Title and axis labels
  theme_minimal() 
`stat_bin()` using `bins = 30`. Pick better value with `binwidth`.

  1. Plot a scatterplot between acceptance rate and enrollment rate using ggplot2 library.

SCATTERPLOT in ggplot2 ggplot(data = , aes(x = , y = )) + # Map variables to axes geom_point(color = , size = , alpha = ) + # Add points with customizations labs( title = “”, # Add title x = “”, # Add x-axis label y = “” # Add y-axis label ) + theme_() # Choose a theme (minimal, classic, etc)

Solution:

ggplot(college_table, aes(x = `Acceptance Rate`, y = `Enrollment Rate`)) +  # Map variables to x and y axes
  geom_point(color = "blue", size = 1, alpha = 0.5) +  # Customize points features
  labs(
    title = "Acceptance Rate vs. Enrollment Rate",  # Title for the plot
    x = "Acceptance Rate",  # Name for x-axis
    y = "Enrollment Rate"   # Name for y-axis
  ) +
  theme_minimal() 
Warning: Removed 29 rows containing missing values or values outside the scale range (`geom_point()`).

  1. Calculate the average default rate by aid rate category (e.g., grouped into ranges like 0-20%, 20-40%). Display the categories.

Solution:

# Define RANGES for "Institutional Aid Rate" CATEGORIES
college_table <- college_table %>%
  mutate(`Institutional Aid Category` = case_when(
    `Institutional Aid Rate` >= 0 & `Institutional Aid Rate` < 0.2 ~ "0-20%",
    `Institutional Aid Rate` >= 0.2 & `Institutional Aid Rate` < 0.4 ~ "20-40%",
    `Institutional Aid Rate` >= 0.4 & `Institutional Aid Rate` < 0.6 ~ "40-60%",
    `Institutional Aid Rate` >= 0.6 & `Institutional Aid Rate` < 0.8 ~ "60-80%",
    `Institutional Aid Rate` >= 0.8 & `Institutional Aid Rate` <= 1 ~ "80-100%",
  ))

# Calculate the average Default Rate for each RANGE
avg_AidRaid <- college_table %>%
  filter(!is.na(`Default Rate`)) %>%  # Exclude NAs
    #Group the Default Rate values by the categories previously established
  group_by(`Institutional Aid Category`) %>% 
    #Calculate averages for each group
  summarize(Average_Default_Rate = mean(`Default Rate`)) %>%
    #Sort the results by the categories to keep order (ascending)
  arrange(`Institutional Aid Category`) 

avg_AidRaid
NA
NA
  1. Normalize the acceptance rate to a scale of 0-1 and save in a new column “Acceptance Rate Normalized”. Display the first 6 values. x-min(x) NORMALIZATION = —————– max(x) - min(x)

From the library (scales) use the function rescale() rescale(x, to = c(0, 1), from = NULL, na.rm = FALSE)

x- target data to- range to rescale the data from- customize max and min na.rm- remove NA values before rescaling

Solution:

library(scales)

# Normalize the "Acceptance Rate" column using rescale()
college_table <- college_table %>%
  mutate(
    # Creating new column to save the normalized values with the 0-1 scale
    `Acceptance Rate Normalized` = rescale(`Acceptance Rate`, to = c(0, 1),na.rm=TRUE)  # Normalizes to 0-1 range
  )

# Display the first 6 normalized values
head(college_table$`Acceptance Rate Normalized`)
[1] 0.00000000 0.01063830 0.04255319 0.08510638 0.09574468 0.10638298
  1. What is the count of the duplicate entries in the “School” column? Remove duplicate university entries.

Solution:

# Count duplicates in the "School" column
duplicate_count <- sum(duplicated(college_table$School))

print(paste("There are", duplicate_count, "duplicated schools."))
[1] "There are 3 duplicated schools."
# Remove the duplicate entries based on the "School" column
college_table<- college_table %>%
  distinct(School, .keep_all = TRUE)
  1. Find the correlation between graduation rate and retention rate (exclude the NAs in both columns).

Use the function cor() to calculate the correlation between two columns, excluding NAs (complete.obs):

cor(result <- cor(data\(`Column1`, data\)Column2, use = “complete.obs”, method = “pearson”))

Solution:

# Calculate the correlation between "Graduation Rate" and "Retention Rate", excluding NAs
correlation_result <- cor(college_table$`Graduation Rate`, college_table$`Retention Rate`, use = "complete.obs")

print(paste("The correlation between Graduation Rate and Retention Rate is:", correlation_result))
[1] "The correlation between Graduation Rate and Retention Rate is: 0.615970939269887"
  1. Extract the values in School column into a new variable without “University” in the string. For example “Rowan University” becomes “Rowan”

Use gsub() to replace a pattern in a string: new_column <- gsub(“pattern_to_replace”, “replacement_string”, data$column_name)

Solution:

# Create a new variable "School_Name" by removing "University" from the "School" column
college_table$School_Name <- gsub(" University", "", college_table$School)

# Display the first few rows of the new column
head(college_table$School_Name)
[1] "Harvard"                    "Yale"                       "University of Pennsylvania"
[4] "Johns Hopkins"              "Cornell"                    "Tufts"                     
  1. Count how many universities have “Institute” in their name.

Use grpl() to get a logicat vector with the matches grepl(“pattern_to_find”, dataset$column_name)

To have the count add the sum()

Solution:


# Count how many universities have "Institute" in their name
count_institute <- sum(grepl("Institute", college_table$School))


print(paste("There are", count_institute, "universities with Insitutue in their name."))
[1] "There are 17 universities with Insitutue in their name."
  1. Export the cleaned and processed dataset to a CSV file.

write.csv(data, file, row.names)

Solution:


# Export the cleaned and processed dataset to a CSV file
write.csv(college_table, "INSS615_H5_OutputFile.csv", row.names = FALSE)

print("csv file created")
[1] "csv file created"
getwd()
[1] "C:/Users/natal/Downloads"
LS0tDQp0aXRsZTogIklOU1M2MTUgSG9tZXdvcmsgNSINCm91dHB1dDoNCiAgIyB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQoNCioqTW9yZ2FuIFN0YXRlIFVuaXZlcnNpdHkqKg0KDQoqKkRlcGFydG1lbnQgb2YgSW5mb3JtYXRpb24gU2NpZW5jZSAmIFN5c3RlbXMqKg0KDQoqKkZhbGwgMjAyNCoqDQoNCioqSU5TUyA2MTU6IERhdGEgV3JhbmdsaW5nIGZvciBWaXN1YWxpemF0aW9uKioNCg0KKipOYW1lOiBOYXRhbGlhIE1pcmFuZGEqKg0KDQoqRHVlOiBEZWMgMSwgMjAyNCAoU3VuZGF5KSoNCg0KDQoNClF1ZXN0aW9ucw0KDQoNCkEuIFNjcmFwZSB0aGUgQ29sbGVnZSBSYW5rZWQgYnkgQWNjZXB0YW5jZSBSYXRlIGRhdGFzZXQgYXZhaWxhYmxlIGF0IHRoaXMgbGluazogaHR0cHM6Ly93d3cub2VkYi5vcmcvcmFua2luZ3MvYWNjZXB0YW5jZS1yYXRlLyN0YWJsZS1yYW5raW5ncyBhbmQgc2VsZWN0IHRoZSBmaXJzdCA5IGNvbHVtbnMgW1JhbmssIFNjaG9vbCwgU3R1ZGVudCB0byBGYWN1bHR5IFJhdGlvLCBHcmFkdWF0aW9uIFJhdGUsIFJldGVudGlvbiBSYXRlLCBBY2NlcHRhbmNlIFJhdGUsIEVucm9sbG1lbnQgUmF0ZSwgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZSwgYW5kIERlZmF1bHQgUmF0ZV0gYXMgdGhlIGRhdGFzZXQgZm9yIHRoaXMgYXNzaWdubWVudC4gWzIwIFBvaW50c10NCg0KSGludDogVGhlcmUgYXJlIDYgcGFnZXMgb2YgZGF0YSwgc28geW91IG1heSB3YW50IHRvIHVzZSBhIGZvciBsb29wIHRvIGF1dG9tYXRlIHRoZSBzY3JhcGluZyBwcm9jZXNzIGFuZCBjb21iaW5lIHRoZSBkYXRhIGZyb20gYWxsIDYgcGFnZXMuIFRoaXMgaXMganVzdCBhIHN1Z2dlc3Rpb27igJR5b3UgYXJlIGZyZWUgdG8gY3JlYXRlIHRoZSBkYXRhc2V0IHdpdGhvdXQgYXV0b21hdGluZyB0aGUgd2ViIHNjcmFwcGluZyBwcm9jZXNzLg0KDQogDQogIFNvbHV0aW9uOg0KYGBge3J9DQpsaWJyYXJ5KHJ2ZXN0KSANCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHNjYWxlcykNCg0KIyBVUkwgT0YgVEhFIFdFQlNJVEU6IHVybF9iYXNlIFBBR0UjIHVybF9lbmQNCnVybF9iYXNlIDwtICJodHRwczovL3d3dy5vZWRiLm9yZy9yYW5raW5ncy9hY2NlcHRhbmNlLXJhdGUvcGFnZS8iICMgQmFzZSBVUkwgZm9yIHRoZSB0YXJnZXQgd2Vic2l0ZQ0KdXJsX2VuZCA8LSAiLyN0YWJsZS1yYW5raW5ncyIgIyBFbmRpbmcgcGFydCBvZiBVUkwgZm9yIHRoZSB0YXJnZXQgd2Vic2l0ZQ0KDQojIEVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIHRhYmxlcyBmcm9tIGVhY2ggcGFnZQ0KdGFibGVzX2xpc3QgPC0gbGlzdCgpDQoNCiMgTG9vcCBmb3IgdGhlIE1VTFRJUEFHRSBTQ1JBUElORywgdGhyb3VnaCB0aGUgcGFnZSBudW1iZXJzICgxIHRvIDYpDQpmb3IgKHBhZ2VfbnVtIGluIDE6Nikgew0KICAgICMgVEFSR0VUIFdFQlNJVEU6IGJ1aWxkIHRoZSBmdWxsIFVSTCBmb3IgZWFjaCBwYWdlDQogIHBhZ2VfdXJsIDwtIHBhc3RlMCh1cmxfYmFzZSwgcGFnZV9udW0sIHVybF9lbmQpDQogIA0KICAgICMgVmFyaWFibGUgdG8gc3RvcmUgdGhlIFBBR0UgSFRNTA0KICBwYWdlIDwtIHJ2ZXN0OjpyZWFkX2h0bWwocGFnZV91cmwpDQogICANCiAgICAjVXNpbmcgdGhlIEhUTUwgY29kZSwgY29weSB0aGUgKlhwYXRoKiBvciAqQ1NTIFNlbGVjdG9yKiBvZiB0aGUgZWxlbWVudCAodGV4dCBvciB0YWJsZSkgeW91IHdhbnQgdG8gZ2V0DQogIGxvY2F0aW9uIDwtICIjY29udGVudCA+IGRpdi5qcy1kYXRhLWxpc3QtciA+IHRhYmxlIg0KICAgICNFeHRyYWN0IHRoZSBlbGVtZW50DQogIHBhZ2VfdGFibGUgPC0gaHRtbF9lbGVtZW50cyhwYWdlLCBjc3MgPSBsb2NhdGlvbikNCiAgDQogICAgIyBDb252ZXJ0IHRoZSBleHRyYWN0ZWQgZWxlbWVudCB0byBhIERBVEFGUkFNRQ0KICB0YXJnZXRfdGFibGUgPC0gaHRtbF90YWJsZShwYWdlX3RhYmxlKVtbMV1dDQoNCiMgQ29tYmluZSBhbGwgdGhlIHRhYmxlcyBpbnRvIGEgc2luZ2xlIGRhdGEgZnJhbWUNCnRhYmxlc19saXN0W1twYWdlX251bV1dIDwtIHRhcmdldF90YWJsZSBbICwgMTo5XQ0KfQ0KDQojSm9pbiBhbGwgdGFibGUgdG8gc3RvcmUgaXQgaW50byBhIFNJTkdMRSBEQVRBIEZSQU1FDQpvcmlnaW5hbCA8LSBiaW5kX3Jvd3ModGFibGVzX2xpc3QpICNUYWJsZSB0byBzYXZlIHRoZSBvcmlnaW5hbCB2ZXJzaW9uDQpjb2xsZWdlX3RhYmxlIDwtIGJpbmRfcm93cyh0YWJsZXNfbGlzdCkgI1RhYmxlIHRvIGVkaXQgb24gdGhlIGZvbGxvd2luZyBzdGVwcw0KDQojIFZpZXcgdGhlIGNvbWJpbmVkIGRhdGFzZXQNClZpZXcoY29sbGVnZV90YWJsZSkNCg0KDQpgYGANCg0KQi4gWW91IGFyZSBnb2luZyB0byBuZWVkIHRoZSBkYXRhc2V0IGNyZWF0ZWQgaW4gUXVlc3Rpb24gQSB0byBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuIFRoZXJlIGFyZSAxNiBxdWVzdGlvbnMgZWFjaCBjYXJyeWluZyA1IHBvaW50czoNCg0KMS4gUmVwbGFjZSB0aGUgbWlzc2luZyB2YWx1ZXMgIk4vQSIgaW4gdGhlIGRhdGFzZXQgd2l0aCBOQS4NClJldmlld2luZyBOL0EgVmFsdWVzOg0KYGBge1J9DQojQ2hlY2tpbmcgTi9BIHZhbHVlcyB3aXRoaW4gdGhlIERhdGFzZXQNCnRvdGFsX25hX2NvdW50IDwtIHN1bShjb2xsZWdlX3RhYmxlID09ICJOL0EiLCBuYS5ybSA9IFRSVUUpDQpwcmludChwYXN0ZSgiVG90YWwgJ04vQScgdmFsdWVzIGluIHRoZSBkYXRhc2V0OiIsIHRvdGFsX25hX2NvdW50KSkNCg0KIyBDb2x1bW4gYnJlYWtkb3duIG9mIE4vQSB2YWx1ZXMNCmNvbHNfd2l0aF9uYSA8LSBjb2xTdW1zKGNvbGxlZ2VfdGFibGUgPT0gIk4vQSIsIG5hLnJtID0gVFJVRSkNCnByaW50KCJCcmVha2Rvd24gb2YgJ04vQScgdmFsdWVzIGJ5IGNvbHVtbjoiKQ0KcHJpbnQoY29sc193aXRoX25hW2NvbHNfd2l0aF9uYSA+IDBdKSAgIyBTaG93IG9ubHkgY29sdW1ucyB3aXRoIGF0IGxlYXN0IG9uZSBOL0ENCg0KYGBgDQogIFNvbHV0aW9uOg0KYGBge3J9DQojUmVwbGFjZSBWYWx1ZXMNCmNvbGxlZ2VfdGFibGVbY29sbGVnZV90YWJsZSA9PSAiTi9BIl0gPC0gTkENCg0KYGBgDQoNCjIuIENvbnZlcnQgcGVyY2VudGFnZSBjb2x1bW5zIChlLmcuLCBHcmFkdWF0aW9uIFJhdGUpIHRvIG51bWVyaWMgZm9ybWF0Lg0KDQpWZXJpZnlpbmcgY29sdW1ucyBuYW1lczoNCmBgYHtSfQ0KbmFtZXMoY29sbGVnZV90YWJsZSkNCmBgYA0KDQogIFNvbHV0aW9uOg0KYGBge3J9DQojIExpc3Qgb2YgcGVyY2VudGFnZSBjb2x1bW5zDQojcGVyY2VudGFnZV9jb2x1bW5zIDwtIGMoIkdyYWR1YXRpb24gUmF0ZSIsICJSZXRlbnRpb24gUmF0ZSIsIkFjY2VwdGFuY2UgUmF0ZSIsIkVucm9sbG1lbnQgUmF0ZSIsIkluc3RpdHV0aW9uYWwgQWlkIFJhaWQiKQ0KY29sbGVnZV90YWJsZSQiR3JhZHVhdGlvbiBSYXRlIiA8LSBhcy5udW1lcmljKGdzdWIoIiUiLCAiIiwgY29sbGVnZV90YWJsZSQiR3JhZHVhdGlvbiBSYXRlIikpIC8gMTAwDQpjb2xsZWdlX3RhYmxlJCJSZXRlbnRpb24gUmF0ZSIgPC0gYXMubnVtZXJpYyhnc3ViKCIlIiwgIiIsIGNvbGxlZ2VfdGFibGUkIlJldGVudGlvbiBSYXRlIikpIC8gMTAwDQpjb2xsZWdlX3RhYmxlJCJBY2NlcHRhbmNlIFJhdGUiIDwtIGFzLm51bWVyaWMoZ3N1YigiJSIsICIiLCBjb2xsZWdlX3RhYmxlJCJBY2NlcHRhbmNlIFJhdGUiKSkgLyAxMDANCmNvbGxlZ2VfdGFibGUkIkVucm9sbG1lbnQgUmF0ZSIgPC0gYXMubnVtZXJpYyhnc3ViKCIlIiwgIiIsIGNvbGxlZ2VfdGFibGUkIkVucm9sbG1lbnQgUmF0ZSIpKSAvIDEwMA0KY29sbGVnZV90YWJsZSQiSW5zdGl0dXRpb25hbCBBaWQgUmF0ZSIgPC0gYXMubnVtZXJpYyhnc3ViKCIlIiwgIiIsIGNvbGxlZ2VfdGFibGUkIkluc3RpdHV0aW9uYWwgQWlkIFJhdGUiKSkgLyAxMDANCmNvbGxlZ2VfdGFibGUkIkRlZmF1bHQgUmF0ZSIgPC0gYXMubnVtZXJpYyhnc3ViKCIlIiwgIiIsIGNvbGxlZ2VfdGFibGUkIkRlZmF1bHQgUmF0ZSIpKSAvIDEwMA0KDQpgYGANCg0KDQozLiBUcmFuc2Zvcm0gdGhlICJTdHVkZW50IHRvIEZhY3VsdHkgUmF0aW8iIGNvbHVtbiBpbnRvIHR3byBzZXBhcmF0ZSBudW1lcmljIGNvbHVtbnM6IFN0dWRlbnRzIGFuZCBGYWN1bHR5Lg0KDQoNCiAgU29sdXRpb246DQpgYGB7cn0NCiNTZXBhcmF0ZSBDb2x1bW5zDQpjb2xsZWdlX3RhYmxlIDwtIGNvbGxlZ2VfdGFibGUgJT4lDQogIHNlcGFyYXRlKCJTdHVkZW50IHRvIEZhY3VsdHkgUmF0aW8iLCBpbnRvID0gYygiU3R1ZGVudHMiLCAiRmFjdWx0eSIpLCBzZXAgPSAiIHRvICIpDQoNCiNDb252ZXJ0IGNvbHVtbnMgdG8gTlVNRVJJQyBWQUxVRQ0KY29sbGVnZV90YWJsZSRTdHVkZW50cyA8LSBwYXJzZV9udW1iZXIoY29sbGVnZV90YWJsZSRTdHVkZW50cykNCmNvbGxlZ2VfdGFibGUkRmFjdWx0eSA8LSBwYXJzZV9udW1iZXIoY29sbGVnZV90YWJsZSRGYWN1bHR5KQ0KDQpgYGANCg0KDQo0LiBXaGF0IGlzIHRoZSBjb3VudCBvZiBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgIkRlZmF1bHQgUmF0ZSIgY29sdW1uPyBJbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSAiRGVmYXVsdCBSYXRlIiBjb2x1bW4gd2l0aCB0aGUgbWVkaWFuIHZhbHVlLg0KDQoNCiAgU29sdXRpb246DQpgYGB7cn0NCiMgQ291bnQgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSAiRGVmYXVsdCBSYXRlIiBjb2x1bW4NCm1pc3NpbmdfRGVmYXVsdFJhdGUgPC0gc3VtKGlzLm5hKGNvbGxlZ2VfdGFibGUkIkRlZmF1bHQgUmF0ZSIpKQ0KbWlzc2luZ19EZWZhdWx0UmF0ZQ0KIyBDYWxjdWxhdGUgdGhlIG1lZGlhbiBvZiB0aGUgIkRlZmF1bHQgUmF0ZSIgY29sdW1uLCBpZ25vcmluZyBOQXMNCm1lZGlhbl9EZWZhdWx0UmF0ZSA8LSBtZWRpYW4oY29sbGVnZV90YWJsZSQiRGVmYXVsdCBSYXRlIiwgbmEucm0gPSBUUlVFKQ0KbWVkaWFuX0RlZmF1bHRSYXRlDQoNCiMgSW1wdXRlIHRoZSBtaXNzaW5nIHZhbHVlcyB3aXRoIHRoZSBtZWRpYW4NCmNvbGxlZ2VfdGFibGUkIkRlZmF1bHQgUmF0ZSIgPC0gaWZlbHNlKGlzLm5hKGNvbGxlZ2VfdGFibGUkIkRlZmF1bHQgUmF0ZSIpLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbWVkaWFuX0RlZmF1bHRSYXRlLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGVnZV90YWJsZSQiRGVmYXVsdCBSYXRlIikNCg0KDQpgYGANCg0KDQo1LiBGaW5kIHRoZSBhdmVyYWdlIGdyYWR1YXRpb24gcmF0ZSBmb3IgdW5pdmVyc2l0aWVzIHJhbmtlZCBpbiB0aGUgdG9wIDUwLg0KDQoNCiAgU29sdXRpb246DQpgYGB7cn0NCiMgU2VsZWN0IFVuaXZlcnNpdGllcyByYW5rZWQgaW4gdG9wIDUwIA0KVV90b3BfNTAgPC0gY29sbGVnZV90YWJsZSAlPiUgZmlsdGVyKFJhbmsgPD0gNTApDQoNCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIGdyYWR1YXRpb24gcmF0ZQ0KYXZnX2dyYWRfcmF0ZSA8LSBtZWFuKFVfdG9wXzUwJCJHcmFkdWF0aW9uIFJhdGUiLCBuYS5ybSA9IFRSVUUpDQoNCmF2Z19ncmFkX3JhdGUNCg0KYGBgDQoNCg0KNi4gRmlsdGVyIHVuaXZlcnNpdGllcyB3aXRoIGEgcmV0ZW50aW9uIHJhdGUgYWJvdmUgOTAlIGFuZCBmaW5kIHRoZSBjb3VudCBvZiByb3dzIGluIHRoZSBzdWJzZXQuDQoNCg0KICBTb2x1dGlvbjoNCmBgYHtyfQ0KIyBTZWxlY3QgdW5pdmVyc2l0aWVzIHdpdGggYSByZXRlbnRpb24gcmF0ZSBhYm92ZSA5MCUgKGRlY2ltYWwgZm9ybWF0KQ0KVV85MHJldGVudGlvbiA8LSBjb2xsZWdlX3RhYmxlICU+JSBmaWx0ZXIoYFJldGVudGlvbiBSYXRlYCA+IDAuOTApDQoNCiMgQ291bnQgdGhlIHJvd3MgaW4gdGhlIGZpbHRlcmVkIHN1YnNldA0KY291bnRfOTByZXRlbnRpb24gPC0gbnJvdyhVXzkwcmV0ZW50aW9uKQ0KDQojIFByaW50IHRoZSByZXN1bHQNCmNvdW50XzkwcmV0ZW50aW9uDQoNCmBgYA0KDQoNCjcuIFJhbmsgdW5pdmVyc2l0aWVzIGJ5IGVucm9sbG1lbnQgcmF0ZSBpbiBkZXNjZW5kaW5nIG9yZGVyIGFuZCBkaXNwbGF5IHRoZSBsYXN0IDYgcm93cy4NCg0KDQogIFNvbHV0aW9uOg0KYGBge3J9DQojIFJhbmsgdW5pdmVyc2l0aWVzIGJ5IGVucm9sbG1lbnQgcmF0ZSBpbiBkZXNjZW5kaW5nIG9yZGVyLCBpZ25vcmluZyBOQXMNClVfRW5yb2xsbWVudFRhaWwgPC0gY29sbGVnZV90YWJsZSAlPiUNCiAgZmlsdGVyKCFpcy5uYShgRW5yb2xsbWVudCBSYXRlYCkpICU+JSAgIyBFeGNsdWRlIHJvd3Mgd2l0aCBOQSBpbiBFbnJvbGxtZW50IFJhdGUNCiAgYXJyYW5nZShkZXNjKGBFbnJvbGxtZW50IFJhdGVgKSkgICAgICAjIFNvcnQgaW4gZGVzY2VuZGluZyBvcmRlcg0KDQojIERpc3BsYXkgdGhlIGxhc3QgNiByb3dzDQp0YWlsX3Jvd3MgPC0gdGFpbChVX0Vucm9sbG1lbnRUYWlsLCA2KQ0KDQp0YWlsX3Jvd3MNCg0KYGBgDQoNCjguIENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiBncmFkdWF0aW9uIHJhdGVzIHVzaW5nIGdncGxvdDIgbGlicmFyeS4NCg0KSElTVE9HUkFNUyBpbiBnZ3Bsb3QyDQogICAgZ2dwbG90KGRhdGEsIGFlcyh4ID0gY29sdW1uX25hbWUpKSArDQogICAgICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IHZhbHVlLCBmaWxsID0gImNvbG9yIiwgY29sb3IgPSAiYm9yZGVyX2NvbG9yIikgKw0KICAgICAgbGFicyh0aXRsZSA9ICJQbG90IFRpdGxlIiwgeCA9ICJYLWF4aXMgTGFiZWwiLCB5ID0gIlktYXhpcyBMYWJlbCIpICsNCiAgICAgIHRoZW1lX3N0eWxlKCkNCg0KICBTb2x1dGlvbjoNCmBgYHtyfQ0KDQpjb2xsZWdlX3RhYmxlX2NsZWFuIDwtIGNvbGxlZ2VfdGFibGUgJT4lDQogIGZpbHRlcighaXMubmEoYEdyYWR1YXRpb24gUmF0ZWApKQ0KDQojIENyZWF0ZSBhIGhpc3RvZ3JhbSBvZiBncmFkdWF0aW9uIHJhdGVzDQpnZ3Bsb3QoY29sbGVnZV90YWJsZV9jbGVhbiwgYWVzKHggPSBgR3JhZHVhdGlvbiBSYXRlYCkpICsgICMgU2V0IHgtYXhpcw0KICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gImJsdWUiLCBjb2xvciA9ICJibGFjayIpICsgICMgQmFyIHNldHRpbmdzIHdpZHRoIGFuZCBjb2xvcnMNCiAgbGFicyh0aXRsZSA9ICJHcmFkdWF0aW9uIFJhdGVzIERpc3RyaWJ1dGlvbiIsIHggPSAiR3JhZHVhdGlvbiBSYXRlIiwgeSA9ICJVbml2ZXJpc2l0eSBDb3VudCIpICsgICMgVGl0bGUgYW5kIGF4aXMgbGFiZWxzDQogIHRoZW1lX21pbmltYWwoKSANCg0KDQpgYGANCg0KDQo5LiBQbG90IGEgc2NhdHRlcnBsb3QgYmV0d2VlbiBhY2NlcHRhbmNlIHJhdGUgYW5kIGVucm9sbG1lbnQgcmF0ZSB1c2luZyBnZ3Bsb3QyIGxpYnJhcnkuDQoNClNDQVRURVJQTE9UIGluIGdncGxvdDINCmdncGxvdChkYXRhID0gPGRhdGFmcmFtZT4sIGFlcyh4ID0gPHhfY29sdW1uPiwgeSA9IDx5X2NvbHVtbj4pKSArICAjIE1hcCB2YXJpYWJsZXMgdG8gYXhlcw0KICBnZW9tX3BvaW50KGNvbG9yID0gPHBvaW50X2NvbG9yPiwgc2l6ZSA9IDxwb2ludF9zaXplPiwgYWxwaGEgPSA8dHJhbnNwYXJlbmN5X3ZhbHVlPikgKyAgIyBBZGQgcG9pbnRzIHdpdGggY3VzdG9taXphdGlvbnMNCiAgbGFicygNCiAgICB0aXRsZSA9ICI8UGxvdCBUaXRsZT4iLCAgIyBBZGQgdGl0bGUNCiAgICB4ID0gIjxYLWF4aXMgTGFiZWw+IiwgICAgIyBBZGQgeC1heGlzIGxhYmVsDQogICAgeSA9ICI8WS1heGlzIExhYmVsPiIgICAgICMgQWRkIHktYXhpcyBsYWJlbA0KICApICsNCiAgdGhlbWVfPHRoZW1lX25hbWU+KCkgICMgQ2hvb3NlIGEgdGhlbWUgKG1pbmltYWwsIGNsYXNzaWMsIGV0YykNCg0KICBTb2x1dGlvbjoNCmBgYHtyfQ0KZ2dwbG90KGNvbGxlZ2VfdGFibGUsIGFlcyh4ID0gYEFjY2VwdGFuY2UgUmF0ZWAsIHkgPSBgRW5yb2xsbWVudCBSYXRlYCkpICsgICMgTWFwIHZhcmlhYmxlcyB0byB4IGFuZCB5IGF4ZXMNCiAgZ2VvbV9wb2ludChjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDEsIGFscGhhID0gMC41KSArICAjIEN1c3RvbWl6ZSBwb2ludHMgZmVhdHVyZXMNCiAgbGFicygNCiAgICB0aXRsZSA9ICJBY2NlcHRhbmNlIFJhdGUgdnMuIEVucm9sbG1lbnQgUmF0ZSIsICAjIFRpdGxlIGZvciB0aGUgcGxvdA0KICAgIHggPSAiQWNjZXB0YW5jZSBSYXRlIiwgICMgTmFtZSBmb3IgeC1heGlzDQogICAgeSA9ICJFbnJvbGxtZW50IFJhdGUiICAgIyBOYW1lIGZvciB5LWF4aXMNCiAgKSArDQogIHRoZW1lX21pbmltYWwoKSANCg0KYGBgDQoNCg0KMTAuIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBkZWZhdWx0IHJhdGUgYnkgYWlkIHJhdGUgY2F0ZWdvcnkgKGUuZy4sIGdyb3VwZWQgaW50byByYW5nZXMgbGlrZSAwLTIwJSwgMjAtNDAlKS4gRGlzcGxheSB0aGUgY2F0ZWdvcmllcy4NCg0KDQogIFNvbHV0aW9uOg0KYGBge3J9DQojIERlZmluZSBSQU5HRVMgZm9yICJJbnN0aXR1dGlvbmFsIEFpZCBSYXRlIiBDQVRFR09SSUVTDQpjb2xsZWdlX3RhYmxlIDwtIGNvbGxlZ2VfdGFibGUgJT4lDQogIG11dGF0ZShgSW5zdGl0dXRpb25hbCBBaWQgQ2F0ZWdvcnlgID0gY2FzZV93aGVuKA0KICAgIGBJbnN0aXR1dGlvbmFsIEFpZCBSYXRlYCA+PSAwICYgYEluc3RpdHV0aW9uYWwgQWlkIFJhdGVgIDwgMC4yIH4gIjAtMjAlIiwNCiAgICBgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZWAgPj0gMC4yICYgYEluc3RpdHV0aW9uYWwgQWlkIFJhdGVgIDwgMC40IH4gIjIwLTQwJSIsDQogICAgYEluc3RpdHV0aW9uYWwgQWlkIFJhdGVgID49IDAuNCAmIGBJbnN0aXR1dGlvbmFsIEFpZCBSYXRlYCA8IDAuNiB+ICI0MC02MCUiLA0KICAgIGBJbnN0aXR1dGlvbmFsIEFpZCBSYXRlYCA+PSAwLjYgJiBgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZWAgPCAwLjggfiAiNjAtODAlIiwNCiAgICBgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZWAgPj0gMC44ICYgYEluc3RpdHV0aW9uYWwgQWlkIFJhdGVgIDw9IDEgfiAiODAtMTAwJSIsDQogICkpDQoNCiMgQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIERlZmF1bHQgUmF0ZSBmb3IgZWFjaCBSQU5HRQ0KYXZnX0FpZFJhaWQgPC0gY29sbGVnZV90YWJsZSAlPiUNCiAgZmlsdGVyKCFpcy5uYShgRGVmYXVsdCBSYXRlYCkpICU+JSAgIyBFeGNsdWRlIE5Bcw0KICAgICNHcm91cCB0aGUgRGVmYXVsdCBSYXRlIHZhbHVlcyBieSB0aGUgY2F0ZWdvcmllcyBwcmV2aW91c2x5IGVzdGFibGlzaGVkDQogIGdyb3VwX2J5KGBJbnN0aXR1dGlvbmFsIEFpZCBDYXRlZ29yeWApICU+JSANCiAgICAjQ2FsY3VsYXRlIGF2ZXJhZ2VzIGZvciBlYWNoIGdyb3VwDQogIHN1bW1hcml6ZShBdmVyYWdlX0RlZmF1bHRfUmF0ZSA9IG1lYW4oYERlZmF1bHQgUmF0ZWApKSAlPiUNCiAgICAjU29ydCB0aGUgcmVzdWx0cyBieSB0aGUgY2F0ZWdvcmllcyB0byBrZWVwIG9yZGVyIChhc2NlbmRpbmcpDQogIGFycmFuZ2UoYEluc3RpdHV0aW9uYWwgQWlkIENhdGVnb3J5YCkgDQoNCmF2Z19BaWRSYWlkDQoNCg0KYGBgDQoNCg0KMTEuIE5vcm1hbGl6ZSB0aGUgYWNjZXB0YW5jZSByYXRlIHRvIGEgc2NhbGUgb2YgMC0xIGFuZCBzYXZlIGluIGEgbmV3IGNvbHVtbiAiQWNjZXB0YW5jZSBSYXRlIE5vcm1hbGl6ZWQiLiBEaXNwbGF5IHRoZSBmaXJzdCA2IHZhbHVlcy4NCiAgICAgICAgICAgICAgICAgICAgIHgtbWluKHgpDQpOT1JNQUxJWkFUSU9OID0gLS0tLS0tLS0tLS0tLS0tLS0NCiAgICAgICAgICAgICAgICAgIG1heCh4KSAtIG1pbih4KQ0KDQpGcm9tIHRoZSBsaWJyYXJ5IChzY2FsZXMpIHVzZSB0aGUgZnVuY3Rpb24gcmVzY2FsZSgpDQpyZXNjYWxlKHgsIHRvID0gYygwLCAxKSwgZnJvbSA9IE5VTEwsIG5hLnJtID0gRkFMU0UpDQoNCngtIHRhcmdldCBkYXRhDQp0by0gcmFuZ2UgdG8gcmVzY2FsZSB0aGUgZGF0YQ0KZnJvbS0gY3VzdG9taXplIG1heCBhbmQgbWluDQpuYS5ybS0gcmVtb3ZlIE5BIHZhbHVlcyBiZWZvcmUgcmVzY2FsaW5nDQoNCiAgU29sdXRpb246DQogDQpgYGB7cn0NCmxpYnJhcnkoc2NhbGVzKQ0KDQojIE5vcm1hbGl6ZSB0aGUgIkFjY2VwdGFuY2UgUmF0ZSIgY29sdW1uIHVzaW5nIHJlc2NhbGUoKQ0KY29sbGVnZV90YWJsZSA8LSBjb2xsZWdlX3RhYmxlICU+JQ0KICBtdXRhdGUoDQogICAgIyBDcmVhdGluZyBuZXcgY29sdW1uIHRvIHNhdmUgdGhlIG5vcm1hbGl6ZWQgdmFsdWVzIHdpdGggdGhlIDAtMSBzY2FsZQ0KICAgIGBBY2NlcHRhbmNlIFJhdGUgTm9ybWFsaXplZGAgPSByZXNjYWxlKGBBY2NlcHRhbmNlIFJhdGVgLCB0byA9IGMoMCwgMSksbmEucm09VFJVRSkgICMgTm9ybWFsaXplcyB0byAwLTEgcmFuZ2UNCiAgKQ0KDQojIERpc3BsYXkgdGhlIGZpcnN0IDYgbm9ybWFsaXplZCB2YWx1ZXMNCmhlYWQoY29sbGVnZV90YWJsZSRgQWNjZXB0YW5jZSBSYXRlIE5vcm1hbGl6ZWRgKQ0KDQoNCmBgYA0KDQoxMi4gV2hhdCBpcyB0aGUgY291bnQgb2YgdGhlIGR1cGxpY2F0ZSBlbnRyaWVzIGluIHRoZSAiU2Nob29sIiBjb2x1bW4/IFJlbW92ZSBkdXBsaWNhdGUgdW5pdmVyc2l0eSBlbnRyaWVzLg0KDQoNCiBTb2x1dGlvbjoNCg0KYGBge3J9DQojIENvdW50IGR1cGxpY2F0ZXMgaW4gdGhlICJTY2hvb2wiIGNvbHVtbg0KZHVwbGljYXRlX2NvdW50IDwtIHN1bShkdXBsaWNhdGVkKGNvbGxlZ2VfdGFibGUkU2Nob29sKSkNCg0KcHJpbnQocGFzdGUoIlRoZXJlIGFyZSIsIGR1cGxpY2F0ZV9jb3VudCwgImR1cGxpY2F0ZWQgc2Nob29scy4iKSkNCg0KIyBSZW1vdmUgdGhlIGR1cGxpY2F0ZSBlbnRyaWVzIGJhc2VkIG9uIHRoZSAiU2Nob29sIiBjb2x1bW4NCmNvbGxlZ2VfdGFibGU8LSBjb2xsZWdlX3RhYmxlICU+JQ0KICBkaXN0aW5jdChTY2hvb2wsIC5rZWVwX2FsbCA9IFRSVUUpDQoNCmBgYA0KDQoxMy4gRmluZCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBncmFkdWF0aW9uIHJhdGUgYW5kIHJldGVudGlvbiByYXRlIChleGNsdWRlIHRoZSBOQXMgaW4gYm90aCBjb2x1bW5zKS4NCg0KVXNlIHRoZSBmdW5jdGlvbiBjb3IoKSB0byBjYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gdHdvIGNvbHVtbnMsIGV4Y2x1ZGluZyBOQXMgKGNvbXBsZXRlLm9icyk6IA0KDQpjb3IocmVzdWx0IDwtIGNvcihkYXRhJGBDb2x1bW4xYCwgZGF0YSRgQ29sdW1uMmAsIHVzZSA9ICJjb21wbGV0ZS5vYnMiLCBtZXRob2QgPSAicGVhcnNvbiIpKQ0KDQoNCiBTb2x1dGlvbjoNCg0KYGBge3J9DQojIENhbGN1bGF0ZSB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiAiR3JhZHVhdGlvbiBSYXRlIiBhbmQgIlJldGVudGlvbiBSYXRlIiwgZXhjbHVkaW5nIE5Bcw0KY29ycmVsYXRpb25fcmVzdWx0IDwtIGNvcihjb2xsZWdlX3RhYmxlJGBHcmFkdWF0aW9uIFJhdGVgLCBjb2xsZWdlX3RhYmxlJGBSZXRlbnRpb24gUmF0ZWAsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQ0KDQpwcmludChwYXN0ZSgiVGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gR3JhZHVhdGlvbiBSYXRlIGFuZCBSZXRlbnRpb24gUmF0ZSBpczoiLCBjb3JyZWxhdGlvbl9yZXN1bHQpKQ0KDQoNCmBgYA0KDQoxNC4gRXh0cmFjdCB0aGUgdmFsdWVzIGluIFNjaG9vbCBjb2x1bW4gaW50byBhIG5ldyB2YXJpYWJsZSB3aXRob3V0ICJVbml2ZXJzaXR5IiBpbiB0aGUgc3RyaW5nLiBGb3IgZXhhbXBsZSAiUm93YW4gVW5pdmVyc2l0eSIgYmVjb21lcyAiUm93YW4iDQoNClVzZSBnc3ViKCkgdG8gcmVwbGFjZSBhIHBhdHRlcm4gaW4gYSBzdHJpbmc6IA0KbmV3X2NvbHVtbiA8LSBnc3ViKCJwYXR0ZXJuX3RvX3JlcGxhY2UiLCAicmVwbGFjZW1lbnRfc3RyaW5nIiwgZGF0YSRjb2x1bW5fbmFtZSkNCg0KIFNvbHV0aW9uOg0KDQpgYGB7cn0NCiMgQ3JlYXRlIGEgbmV3IHZhcmlhYmxlICJTY2hvb2xfTmFtZSIgYnkgcmVtb3ZpbmcgIlVuaXZlcnNpdHkiIGZyb20gdGhlICJTY2hvb2wiIGNvbHVtbg0KY29sbGVnZV90YWJsZSRTY2hvb2xfTmFtZSA8LSBnc3ViKCIgVW5pdmVyc2l0eSIsICIiLCBjb2xsZWdlX3RhYmxlJFNjaG9vbCkNCg0KIyBEaXNwbGF5IHRoZSBmaXJzdCBmZXcgcm93cyBvZiB0aGUgbmV3IGNvbHVtbg0KaGVhZChjb2xsZWdlX3RhYmxlJFNjaG9vbF9OYW1lKQ0KDQoNCmBgYA0KDQoNCjE1LiBDb3VudCBob3cgbWFueSB1bml2ZXJzaXRpZXMgaGF2ZSAiSW5zdGl0dXRlIiBpbiB0aGVpciBuYW1lLg0KDQpVc2UgZ3JwbCgpIHRvIGdldCBhIGxvZ2ljYXQgdmVjdG9yIHdpdGggdGhlIG1hdGNoZXMNCmdyZXBsKCJwYXR0ZXJuX3RvX2ZpbmQiLCBkYXRhc2V0JGNvbHVtbl9uYW1lKQ0KDQpUbyBoYXZlIHRoZSBjb3VudCBhZGQgdGhlIHN1bSgpDQoNCiBTb2x1dGlvbjoNCg0KYGBge3J9DQoNCiMgQ291bnQgaG93IG1hbnkgdW5pdmVyc2l0aWVzIGhhdmUgIkluc3RpdHV0ZSIgaW4gdGhlaXIgbmFtZQ0KY291bnRfaW5zdGl0dXRlIDwtIHN1bShncmVwbCgiSW5zdGl0dXRlIiwgY29sbGVnZV90YWJsZSRTY2hvb2wpKQ0KDQoNCnByaW50KHBhc3RlKCJUaGVyZSBhcmUiLCBjb3VudF9pbnN0aXR1dGUsICJ1bml2ZXJzaXRpZXMgd2l0aCBJbnNpdHV0dWUgaW4gdGhlaXIgbmFtZS4iKSkNCg0KYGBgDQoNCjE2LiBFeHBvcnQgdGhlIGNsZWFuZWQgYW5kIHByb2Nlc3NlZCBkYXRhc2V0IHRvIGEgQ1NWIGZpbGUuDQoNCndyaXRlLmNzdihkYXRhLCBmaWxlLCByb3cubmFtZXMpDQoNCiBTb2x1dGlvbjoNCg0KYGBge3J9DQoNCiMgRXhwb3J0IHRoZSBjbGVhbmVkIGFuZCBwcm9jZXNzZWQgZGF0YXNldCB0byBhIENTViBmaWxlDQp3cml0ZS5jc3YoY29sbGVnZV90YWJsZSwgIklOU1M2MTVfSDVfT3V0cHV0RmlsZS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCg0KcHJpbnQoImNzdiBmaWxlIGNyZWF0ZWQiKQ0KDQpnZXR3ZCgpDQpgYGANCg0K