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:
- 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
- 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
- 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)
- 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")
- 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
- 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
- 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
- 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`.

- 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()`).

- 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
- 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
- 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)
- 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"
- 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"
- 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."
- 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