Morgan State University
Department of Information Science & Systems
Fall 2024
INSS 615: Data Wrangling for Visualization
Name: Cristina Sanchez
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)
#Initialize an empty list to store the data from all pages
all_data <- list()
#Loop through the pages (1 to 6)
for (i in 1:6) {
#Construct the URL for the page using the pager number
url <- paste0("https://www.oedb.org/rankings/acceptance-rate/page/"
,i, "/#table-rankings")
#Read the HTML content of the page
page <- read_html(url)
#Extract the table using the XPath
table <- page %>%
html_node(xpath = "/html/body/div[1]/div/div[1]/table") %>%
html_table(fill = TRUE)
#Select the first 9 columns
table_subset <- table %>%
select(1:9)
#Append the cleaned table data to the list
all_data[[i]] <- table_subset
}
#Combine all data into one data frame
final_data <- bind_rows(all_data)
#View the combined data
head(final_data)
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.
Solution:
#Replace "N/A" with NA for character columns
final_data_clean <- final_data %>%
mutate(across(where(is.character), ~ na_if(.x, "N/A")))
#View the cleaned data
head(final_data_clean)
- Convert percentage columns (e.g., Graduation Rate) to numeric
format.
Solution:
#Initialize vector with column names of percentage variables
per_col <- c("Graduation Rate", "Retention Rate", "Acceptance Rate",
"Enrollment Rate", "Institutional Aid Rate", "Default Rate")
#Convert percentage columns to numeric
final_data_clean <- final_data_clean %>%
mutate(across(per_col, ~ as.numeric(gsub("%", "", .x)) / 100))
#View the cleaned data
head(final_data_clean)
- Transform the “Student to Faculty Ratio” column into two separate
numeric columns: Students and Faculty.
Solution:
#Transform "Student to Faculty Ratio" into two separate columns: "Students" and "Faculty"
final_data_clean <- final_data_clean %>%
mutate(
`Students` = as.numeric(sub(" to .*", "", `Student to Faculty Ratio`)), #Extract number of Students
`Faculty` = as.numeric(sub(".*to ", "", `Student to Faculty Ratio`)) #Extract number of Faculty
) #%>% select(-`Student to Faculty Ratio`) #Remove the original column
# View the cleaned data
head(final_data_clean)
NA
- 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 number of missing values in the "Default Rate" column
na_cnt <- sum(is.na(final_data_clean$`Default Rate`))
print(paste("Number of missing values in 'Default Rate':", na_cnt))
[1] "Number of missing values in 'Default Rate': 291"
#Impute missing values in "Default Rate" with the median of the column
final_data_clean <- final_data_clean %>%
mutate(
`Default Rate` = ifelse(
is.na(`Default Rate`),
median(`Default Rate`, na.rm = TRUE), #Use the median, ignoring NA values
`Default Rate` #Keep the original value if not missing
)
)
#View the cleaned data
head(final_data_clean)
- Find the average graduation rate for universities ranked in the top
50.
Solution:
#Calculate the average graduation rate for universities ranked in the top 50
avg_grad_rate_top_50 <- final_data_clean %>%
filter(Rank <= 50) %>% # Filter for top 50 ranked universities
summarise(avg_grad_rate = mean(`Graduation Rate`, na.rm = TRUE)) #Calculate the average
#View the result
print(avg_grad_rate_top_50)
- Filter universities with a retention rate above 90% and find the
count of rows in the subset.
Solution:
uni_abv_90_retn <- final_data_clean %>%
filter(`Retention Rate` > .90) %>% #Filter for retention rate above 90%
summarise(count = n()) #Count the number of rows in the subset
#View the count of universities
print(uni_abv_90_retn)
- Rank universities by enrollment rate in descending order and display
the last 6 rows.
Solution:
last_6_uni <- final_data_clean %>%
arrange(desc(`Enrollment Rate`)) %>% #Sort by Enrollment Rate in descending order
filter(!is.na(`Enrollment Rate`)) %>%
tail(6) #Get the last 6 rows
#View the last 6 universities
print(last_6_uni)
- Create a histogram of graduation rates using ggplot2 library.
Solution:
library(ggplot2)
final_data_clean %>%
filter(!is.na(`Graduation Rate`), is.finite(`Graduation Rate`)) %>%
ggplot(aes(x = `Graduation Rate`)) +
geom_histogram(binwidth = 0.05, fill = "skyblue", color = "black", alpha = 0.7) +
labs(
title = "Histogram of Graduation Rates",
x = "Graduation Rate (%)",
y = "Count"
) +
theme_minimal()

- Plot a scatterplot between acceptance rate and enrollment rate using
ggplot2 library.
Solution:
ggplot(final_data_clean %>%
filter(!is.na(`Acceptance Rate`), !is.na(`Enrollment Rate`)), #Remove rows with NA in either column
aes(x = `Acceptance Rate`, y = `Enrollment Rate`)) +
geom_point(color = "blue", alpha = 0.7) +
labs(
title = "Scatterplot of Acceptance Rate vs Enrollment Rate",
x = "Acceptance Rate (%)",
y = "Enrollment Rate (%)"
) +
theme_minimal()

- Calculate the average default rate by aid rate category (e.g.,
grouped into ranges like 0-20%, 20-40%). Display the categories.
Solution:
library(dplyr)
avg_dflt_rate_by_aid_ctgry <- final_data_clean %>%
mutate(
# Create categories for Institutional Aid Rate
aid_rate_ctgry = case_when(
`Institutional Aid Rate` <= 0.2 ~ "0-20%",
`Institutional Aid Rate` <= 0.4 ~ "20-40%",
`Institutional Aid Rate` <= 0.6 ~ "40-60%",
`Institutional Aid Rate` <= 0.8 ~ "60-80%",
`Institutional Aid Rate` > 0.8 ~ "80-100%",
)
) %>%
# Group by aid rate category and calculate the average default rate
group_by(aid_rate_ctgry) %>%
summarise(
avg_dflt_rate = mean(`Default Rate`, na.rm = TRUE)
) %>%
#Arrange in the correct order of categories
mutate(aid_rate_ctgry = factor(aid_rate_ctgry,
levels = c("0-20%", "20-40%", "40-60%", "60-80%", "80-100%"))) %>%
arrange(aid_rate_ctgry)
# Display the result
print(avg_dflt_rate_by_aid_ctgry)
- Normalize the acceptance rate to a scale of 0-1 and save in a new
column “Acceptance Rate Normalized”. Display the first 6 values.
Solution:
library(scales)
final_data_clean2 <- final_data_clean %>%
mutate(
`Acceptance Rate Normalized` = rescale(`Acceptance Rate`)
)
#Display the first 6 rows of the updated data
head(final_data_clean)
- What is the count of the duplicate entries in the “School” column?
Remove duplicate university entries.
Solution:
#Count the duplicate entries in the "School" column
duplicate_count <- final_data_clean2 %>%
count(`School`) %>%
filter(n > 1)
#Display the count of duplicates
print(duplicate_count)
# Remove duplicate universities from the "School" column,
final_data_clean2 <- final_data_clean2 %>%
distinct(`School`, .keep_all = TRUE)
- Find the correlation between graduation rate and retention rate
(exclude the NAs in both columns).
Solution:
# Calculate the correlation between Graduation Rate and Retention Rate, excluding NA values
correlation <- cor(final_data_clean2$`Graduation Rate`, final_data_clean2$`Retention Rate`, use = "complete.obs")
# Display the correlation result
print(correlation)
[1] 0.6159709
- Extract the values in School column into a new variable without
“University” in the string. For example “Rowan University” becomes
“Rowan”
Solution:
#Create a new column without the word "University" in the "School" name
final_data_clean3 <- final_data_clean2 %>%
mutate(
`School Name` = gsub(" University$", "", `School`)
)
# Display the first few rows to check the result
head(final_data_clean3)
- Count how many universities have “Institute” in their name.
Solution:
#Count how many universities have "Institute" in their name
institute_count <- sum(grepl("Institute", final_data_clean3$`School`, ignore.case = TRUE))
#Display the count
print(institute_count)
[1] 17
- Export the cleaned and processed dataset to a CSV file.
Solution:
library(readr)
write_csv(final_data_clean3, "University_Ranking_Clean.csv", na = "")
LS0tCnRpdGxlOiAiSU5TUzYxNSBIb21ld29yayA1IgpvdXRwdXQ6CiAgIyB3b3JkX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKLS0tCgoKKipNb3JnYW4gU3RhdGUgVW5pdmVyc2l0eSoqCgoqKkRlcGFydG1lbnQgb2YgSW5mb3JtYXRpb24gU2NpZW5jZSAmIFN5c3RlbXMqKgoKKipGYWxsIDIwMjQqKgoKKipJTlNTIDYxNTogRGF0YSBXcmFuZ2xpbmcgZm9yIFZpc3VhbGl6YXRpb24qKgoKKipOYW1lOiBDcmlzdGluYSBTYW5jaGV6KioKCipEdWU6IERlYyAxLCAyMDI0IChTdW5kYXkpKgoKCgpRdWVzdGlvbnMKCgpBLiBTY3JhcGUgdGhlIENvbGxlZ2UgUmFua2VkIGJ5IEFjY2VwdGFuY2UgUmF0ZSBkYXRhc2V0IGF2YWlsYWJsZSBhdCB0aGlzIGxpbms6IGh0dHBzOi8vd3d3Lm9lZGIub3JnL3JhbmtpbmdzL2FjY2VwdGFuY2UtcmF0ZS8jdGFibGUtcmFua2luZ3MgYW5kIHNlbGVjdCB0aGUgZmlyc3QgOSBjb2x1bW5zIFtSYW5rLCBTY2hvb2wsIFN0dWRlbnQgdG8gRmFjdWx0eSBSYXRpbywgR3JhZHVhdGlvbiBSYXRlLCBSZXRlbnRpb24gUmF0ZSwgQWNjZXB0YW5jZSBSYXRlLCBFbnJvbGxtZW50IFJhdGUsIEluc3RpdHV0aW9uYWwgQWlkIFJhdGUsIGFuZCBEZWZhdWx0IFJhdGVdIGFzIHRoZSBkYXRhc2V0IGZvciB0aGlzIGFzc2lnbm1lbnQuIFsyMCBQb2ludHNdCgpIaW50OiBUaGVyZSBhcmUgNiBwYWdlcyBvZiBkYXRhLCBzbyB5b3UgbWF5IHdhbnQgdG8gdXNlIGEgZm9yIGxvb3AgdG8gYXV0b21hdGUgdGhlIHNjcmFwaW5nIHByb2Nlc3MgYW5kIGNvbWJpbmUgdGhlIGRhdGEgZnJvbSBhbGwgNiBwYWdlcy4gVGhpcyBpcyBqdXN0IGEgc3VnZ2VzdGlvbuKAlHlvdSBhcmUgZnJlZSB0byBjcmVhdGUgdGhlIGRhdGFzZXQgd2l0aG91dCBhdXRvbWF0aW5nIHRoZSB3ZWIgc2NyYXBwaW5nIHByb2Nlc3MuCgogCiAgU29sdXRpb246CmBgYHtyfQpsaWJyYXJ5KHJ2ZXN0KSAKbGlicmFyeSh0aWR5dmVyc2UpCgojSW5pdGlhbGl6ZSBhbiBlbXB0eSBsaXN0IHRvIHN0b3JlIHRoZSBkYXRhIGZyb20gYWxsIHBhZ2VzCmFsbF9kYXRhIDwtIGxpc3QoKQoKI0xvb3AgdGhyb3VnaCB0aGUgcGFnZXMgKDEgdG8gNikKZm9yIChpIGluIDE6NikgewogIAogICNDb25zdHJ1Y3QgdGhlIFVSTCBmb3IgdGhlIHBhZ2UgdXNpbmcgdGhlIHBhZ2VyIG51bWJlcgogIHVybCA8LSBwYXN0ZTAoImh0dHBzOi8vd3d3Lm9lZGIub3JnL3JhbmtpbmdzL2FjY2VwdGFuY2UtcmF0ZS9wYWdlLyIKICAgICAgICAgICAgICAgICxpLCAiLyN0YWJsZS1yYW5raW5ncyIpCiAgCiAgI1JlYWQgdGhlIEhUTUwgY29udGVudCBvZiB0aGUgcGFnZQogIHBhZ2UgPC0gcmVhZF9odG1sKHVybCkKICAKICAjRXh0cmFjdCB0aGUgdGFibGUgdXNpbmcgdGhlIFhQYXRoCiAgdGFibGUgPC0gcGFnZSAlPiUKICAgIGh0bWxfbm9kZSh4cGF0aCA9ICIvaHRtbC9ib2R5L2RpdlsxXS9kaXYvZGl2WzFdL3RhYmxlIikgJT4lCiAgICBodG1sX3RhYmxlKGZpbGwgPSBUUlVFKQogIAogICAgI1NlbGVjdCB0aGUgZmlyc3QgOSBjb2x1bW5zCiAgdGFibGVfc3Vic2V0IDwtIHRhYmxlICU+JSAKICAgIHNlbGVjdCgxOjkpCiAgCiAgI0FwcGVuZCB0aGUgY2xlYW5lZCB0YWJsZSBkYXRhIHRvIHRoZSBsaXN0CiAgYWxsX2RhdGFbW2ldXSA8LSB0YWJsZV9zdWJzZXQKfQoKI0NvbWJpbmUgYWxsIGRhdGEgaW50byBvbmUgZGF0YSBmcmFtZQpmaW5hbF9kYXRhIDwtIGJpbmRfcm93cyhhbGxfZGF0YSkKCiNWaWV3IHRoZSBjb21iaW5lZCBkYXRhCmhlYWQoZmluYWxfZGF0YSkKYGBgCgoKQi4gWW91IGFyZSBnb2luZyB0byBuZWVkIHRoZSBkYXRhc2V0IGNyZWF0ZWQgaW4gUXVlc3Rpb24gQSB0byBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuIFRoZXJlIGFyZSAxNiBxdWVzdGlvbnMgZWFjaCBjYXJyeWluZyA1IHBvaW50czoKCjEuIFJlcGxhY2UgdGhlIG1pc3NpbmcgdmFsdWVzICJOL0EiIGluIHRoZSBkYXRhc2V0IHdpdGggTkEuCgoKICBTb2x1dGlvbjoKYGBge3J9CiNSZXBsYWNlICJOL0EiIHdpdGggTkEgZm9yIGNoYXJhY3RlciBjb2x1bW5zCmZpbmFsX2RhdGFfY2xlYW4gPC0gZmluYWxfZGF0YSAlPiUKICBtdXRhdGUoYWNyb3NzKHdoZXJlKGlzLmNoYXJhY3RlciksIH4gbmFfaWYoLngsICJOL0EiKSkpCgojVmlldyB0aGUgY2xlYW5lZCBkYXRhCmhlYWQoZmluYWxfZGF0YV9jbGVhbikKYGBgCgoyLiBDb252ZXJ0IHBlcmNlbnRhZ2UgY29sdW1ucyAoZS5nLiwgR3JhZHVhdGlvbiBSYXRlKSB0byBudW1lcmljIGZvcm1hdC4KCiAgCiAgU29sdXRpb246CmBgYHtyfQojSW5pdGlhbGl6ZSB2ZWN0b3Igd2l0aCBjb2x1bW4gbmFtZXMgb2YgcGVyY2VudGFnZSB2YXJpYWJsZXMKcGVyX2NvbCA8LSBjKCJHcmFkdWF0aW9uIFJhdGUiLCAiUmV0ZW50aW9uIFJhdGUiLCAiQWNjZXB0YW5jZSBSYXRlIiwgCiAgICAgICAgICAgICAiRW5yb2xsbWVudCBSYXRlIiwgIkluc3RpdHV0aW9uYWwgQWlkIFJhdGUiLCAiRGVmYXVsdCBSYXRlIikKCiNDb252ZXJ0IHBlcmNlbnRhZ2UgY29sdW1ucyB0byBudW1lcmljCmZpbmFsX2RhdGFfY2xlYW4gPC0gZmluYWxfZGF0YV9jbGVhbiAlPiUgCiAgbXV0YXRlKGFjcm9zcyhwZXJfY29sLCB+IGFzLm51bWVyaWMoZ3N1YigiJSIsICIiLCAueCkpIC8gMTAwKSkKCiNWaWV3IHRoZSBjbGVhbmVkIGRhdGEKaGVhZChmaW5hbF9kYXRhX2NsZWFuKQpgYGAKCgozLiBUcmFuc2Zvcm0gdGhlICJTdHVkZW50IHRvIEZhY3VsdHkgUmF0aW8iIGNvbHVtbiBpbnRvIHR3byBzZXBhcmF0ZSBudW1lcmljIGNvbHVtbnM6IFN0dWRlbnRzIGFuZCBGYWN1bHR5LgoKCiAgU29sdXRpb246CmBgYHtyfQojVHJhbnNmb3JtICJTdHVkZW50IHRvIEZhY3VsdHkgUmF0aW8iIGludG8gdHdvIHNlcGFyYXRlIGNvbHVtbnM6ICJTdHVkZW50cyIgYW5kICJGYWN1bHR5IgpmaW5hbF9kYXRhX2NsZWFuIDwtIGZpbmFsX2RhdGFfY2xlYW4gJT4lCiAgbXV0YXRlKAogICAgYFN0dWRlbnRzYCA9IGFzLm51bWVyaWMoc3ViKCIgdG8gLioiLCAiIiwgYFN0dWRlbnQgdG8gRmFjdWx0eSBSYXRpb2ApKSwgICNFeHRyYWN0IG51bWJlciBvZiBTdHVkZW50cwogICAgYEZhY3VsdHlgID0gYXMubnVtZXJpYyhzdWIoIi4qdG8gIiwgIiIsIGBTdHVkZW50IHRvIEZhY3VsdHkgUmF0aW9gKSkgICAjRXh0cmFjdCBudW1iZXIgb2YgRmFjdWx0eQogICkKCiNWaWV3IHRoZSBjbGVhbmVkIGRhdGEKaGVhZChmaW5hbF9kYXRhX2NsZWFuKQpgYGAKCgoKCjQuIFdoYXQgaXMgdGhlIGNvdW50IG9mIG1pc3NpbmcgdmFsdWVzIGluIHRoZSAiRGVmYXVsdCBSYXRlIiBjb2x1bW4/IEltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gdGhlICJEZWZhdWx0IFJhdGUiIGNvbHVtbiB3aXRoIHRoZSBtZWRpYW4gdmFsdWUuCgoKICBTb2x1dGlvbjoKYGBge3J9CiNDb3VudCB0aGUgbnVtYmVyIG9mIG1pc3NpbmcgdmFsdWVzIGluIHRoZSAiRGVmYXVsdCBSYXRlIiBjb2x1bW4KbmFfY250IDwtIHN1bShpcy5uYShmaW5hbF9kYXRhX2NsZWFuJGBEZWZhdWx0IFJhdGVgKSkKCnByaW50KHBhc3RlKCJOdW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgaW4gJ0RlZmF1bHQgUmF0ZSc6IiwgbmFfY250KSkKCiNJbXB1dGUgbWlzc2luZyB2YWx1ZXMgaW4gIkRlZmF1bHQgUmF0ZSIgd2l0aCB0aGUgbWVkaWFuIG9mIHRoZSBjb2x1bW4KZmluYWxfZGF0YV9jbGVhbiA8LSBmaW5hbF9kYXRhX2NsZWFuICU+JQogIG11dGF0ZSgKICAgIGBEZWZhdWx0IFJhdGVgID0gaWZlbHNlKAogICAgICBpcy5uYShgRGVmYXVsdCBSYXRlYCksIAogICAgICBtZWRpYW4oYERlZmF1bHQgUmF0ZWAsIG5hLnJtID0gVFJVRSksICAjVXNlIHRoZSBtZWRpYW4sIGlnbm9yaW5nIE5BIHZhbHVlcwogICAgICBgRGVmYXVsdCBSYXRlYCAgI0tlZXAgdGhlIG9yaWdpbmFsIHZhbHVlIGlmIG5vdCBtaXNzaW5nCiAgICApCiAgKQoKI1ZpZXcgdGhlIGNsZWFuZWQgZGF0YQpoZWFkKGZpbmFsX2RhdGFfY2xlYW4pCmBgYAoKCjUuIEZpbmQgdGhlIGF2ZXJhZ2UgZ3JhZHVhdGlvbiByYXRlIGZvciB1bml2ZXJzaXRpZXMgcmFua2VkIGluIHRoZSB0b3AgNTAuCgoKICBTb2x1dGlvbjoKYGBge3J9CiNDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgZ3JhZHVhdGlvbiByYXRlIGZvciB1bml2ZXJzaXRpZXMgcmFua2VkIGluIHRoZSB0b3AgNTAKYXZnX2dyYWRfcmF0ZV90b3BfNTAgPC0gZmluYWxfZGF0YV9jbGVhbiAlPiUKICBmaWx0ZXIoUmFuayA8PSA1MCkgJT4lICAjRmlsdGVyIGZvciB0b3AgNTAgcmFua2VkIHVuaXZlcnNpdGllcwogIHN1bW1hcmlzZShhdmdfZ3JhZF9yYXRlID0gbWVhbihgR3JhZHVhdGlvbiBSYXRlYCwgbmEucm0gPSBUUlVFKSkgICNDYWxjdWxhdGUgdGhlIGF2ZXJhZ2UKCiNWaWV3IHRoZSByZXN1bHQKcHJpbnQoYXZnX2dyYWRfcmF0ZV90b3BfNTApCmBgYAoKCjYuIEZpbHRlciB1bml2ZXJzaXRpZXMgd2l0aCBhIHJldGVudGlvbiByYXRlIGFib3ZlIDkwJSBhbmQgZmluZCB0aGUgY291bnQgb2Ygcm93cyBpbiB0aGUgc3Vic2V0LgoKCiAgU29sdXRpb246CmBgYHtyfQp1bmlfYWJ2XzkwX3JldG4gPC0gZmluYWxfZGF0YV9jbGVhbiAlPiUKICBmaWx0ZXIoYFJldGVudGlvbiBSYXRlYCA+IC45MCkgJT4lICAjRmlsdGVyIGZvciByZXRlbnRpb24gcmF0ZSBhYm92ZSA5MCUKICBzdW1tYXJpc2UoY291bnQgPSBuKCkpICAjQ291bnQgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBzdWJzZXQKCiNWaWV3IHRoZSBjb3VudCBvZiB1bml2ZXJzaXRpZXMKcHJpbnQodW5pX2Fidl85MF9yZXRuKQpgYGAKCgo3LiBSYW5rIHVuaXZlcnNpdGllcyBieSBlbnJvbGxtZW50IHJhdGUgaW4gZGVzY2VuZGluZyBvcmRlciBhbmQgZGlzcGxheSB0aGUgbGFzdCA2IHJvd3MuCgoKICBTb2x1dGlvbjoKYGBge3J9Cmxhc3RfNl91bmkgPC0gZmluYWxfZGF0YV9jbGVhbiAlPiUKICBhcnJhbmdlKGRlc2MoYEVucm9sbG1lbnQgUmF0ZWApKSAlPiUgICNTb3J0IGJ5IEVucm9sbG1lbnQgUmF0ZSBpbiBkZXNjZW5kaW5nIG9yZGVyCiAgZmlsdGVyKCFpcy5uYShgRW5yb2xsbWVudCBSYXRlYCkpICU+JSAjUmVtb3ZlIHJvd3Mgd2l0aCBOQSBpbiBFbnJvbGxtZW50IFJhdGUKICB0YWlsKDYpICAjR2V0IHRoZSBsYXN0IDYgcm93cwoKI1ZpZXcgdGhlIGxhc3QgNiB1bml2ZXJzaXRpZXMKcHJpbnQobGFzdF82X3VuaSkKYGBgCgoKOC4gQ3JlYXRlIGEgaGlzdG9ncmFtIG9mIGdyYWR1YXRpb24gcmF0ZXMgdXNpbmcgZ2dwbG90MiBsaWJyYXJ5LgoKCiAgU29sdXRpb246CmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgpmaW5hbF9kYXRhX2NsZWFuICU+JQogIGZpbHRlcighaXMubmEoYEdyYWR1YXRpb24gUmF0ZWApLCBpcy5maW5pdGUoYEdyYWR1YXRpb24gUmF0ZWApKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBgR3JhZHVhdGlvbiBSYXRlYCkpICsKICBnZW9tX2hpc3RvZ3JhbShiaW53aWR0aCA9IDAuMDUsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gImJsYWNrIiwgYWxwaGEgPSAwLjcpICsKICBsYWJzKAogICAgdGl0bGUgPSAiSGlzdG9ncmFtIG9mIEdyYWR1YXRpb24gUmF0ZXMiLAogICAgeCA9ICJHcmFkdWF0aW9uIFJhdGUgKCUpIiwKICAgIHkgPSAiQ291bnQiCiAgKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKCjkuIFBsb3QgYSBzY2F0dGVycGxvdCBiZXR3ZWVuIGFjY2VwdGFuY2UgcmF0ZSBhbmQgZW5yb2xsbWVudCByYXRlIHVzaW5nIGdncGxvdDIgbGlicmFyeS4KCgogIFNvbHV0aW9uOgpgYGB7cn0KZ2dwbG90KGZpbmFsX2RhdGFfY2xlYW4gJT4lCiAgICAgICAgIGZpbHRlcighaXMubmEoYEFjY2VwdGFuY2UgUmF0ZWApLCAhaXMubmEoYEVucm9sbG1lbnQgUmF0ZWApKSwgICNSZW1vdmUgcm93cyB3aXRoIE5BIGluIGVpdGhlciBjb2x1bW4KICAgICAgIGFlcyh4ID0gYEFjY2VwdGFuY2UgUmF0ZWAsIHkgPSBgRW5yb2xsbWVudCBSYXRlYCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCBhbHBoYSA9IDAuNykgKyAgCiAgbGFicygKICAgIHRpdGxlID0gIlNjYXR0ZXJwbG90IG9mIEFjY2VwdGFuY2UgUmF0ZSB2cyBFbnJvbGxtZW50IFJhdGUiLAogICAgeCA9ICJBY2NlcHRhbmNlIFJhdGUgKCUpIiwKICAgIHkgPSAiRW5yb2xsbWVudCBSYXRlICglKSIKICApICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgoKMTAuIENhbGN1bGF0ZSB0aGUgYXZlcmFnZSBkZWZhdWx0IHJhdGUgYnkgYWlkIHJhdGUgY2F0ZWdvcnkgKGUuZy4sIGdyb3VwZWQgaW50byByYW5nZXMgbGlrZSAwLTIwJSwgMjAtNDAlKS4gRGlzcGxheSB0aGUgY2F0ZWdvcmllcy4KCgogIFNvbHV0aW9uOgpgYGB7cn0KYXZnX2RmbHRfcmF0ZV9ieV9haWRfY3RncnkgPC0gZmluYWxfZGF0YV9jbGVhbiAlPiUKICBtdXRhdGUoCiAgICAjQ3JlYXRlIGNhdGVnb3JpZXMgZm9yIEluc3RpdHV0aW9uYWwgQWlkIFJhdGUKICAgIGFpZF9yYXRlX2N0Z3J5ID0gY2FzZV93aGVuKAogICAgICBgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZWAgPD0gMC4yIH4gIjAtMjAlIiwKICAgICAgYEluc3RpdHV0aW9uYWwgQWlkIFJhdGVgIDw9IDAuNCB+ICIyMC00MCUiLAogICAgICBgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZWAgPD0gMC42IH4gIjQwLTYwJSIsCiAgICAgIGBJbnN0aXR1dGlvbmFsIEFpZCBSYXRlYCA8PSAwLjggfiAiNjAtODAlIiwKICAgICAgYEluc3RpdHV0aW9uYWwgQWlkIFJhdGVgID4gMC44IH4gIjgwLTEwMCUiLAogICAgKQogICkgJT4lCiAgI0dyb3VwIGJ5IGFpZCByYXRlIGNhdGVnb3J5IGFuZCBjYWxjdWxhdGUgdGhlIGF2ZXJhZ2UgZGVmYXVsdCByYXRlCiAgZ3JvdXBfYnkoYWlkX3JhdGVfY3RncnkpICU+JQogIHN1bW1hcmlzZSgKICAgIGF2Z19kZmx0X3JhdGUgPSBtZWFuKGBEZWZhdWx0IFJhdGVgLCBuYS5ybSA9IFRSVUUpCiAgKSAlPiUKICAjQXJyYW5nZSBpbiB0aGUgY29ycmVjdCBvcmRlciBvZiBjYXRlZ29yaWVzCiAgbXV0YXRlKGFpZF9yYXRlX2N0Z3J5ID0gZmFjdG9yKGFpZF9yYXRlX2N0Z3J5LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygiMC0yMCUiLCAiMjAtNDAlIiwgIjQwLTYwJSIsICI2MC04MCUiLCAiODAtMTAwJSIpKSkgJT4lCiAgYXJyYW5nZShhaWRfcmF0ZV9jdGdyeSkKCiNEaXNwbGF5IHRoZSByZXN1bHQKcHJpbnQoYXZnX2RmbHRfcmF0ZV9ieV9haWRfY3RncnkpCmBgYAoKCgoKMTEuIE5vcm1hbGl6ZSB0aGUgYWNjZXB0YW5jZSByYXRlIHRvIGEgc2NhbGUgb2YgMC0xIGFuZCBzYXZlIGluIGEgbmV3IGNvbHVtbiAiQWNjZXB0YW5jZSBSYXRlIE5vcm1hbGl6ZWQiLiBEaXNwbGF5IHRoZSBmaXJzdCA2IHZhbHVlcy4KCgogIFNvbHV0aW9uOgogCmBgYHtyfQpsaWJyYXJ5KHNjYWxlcykKCmZpbmFsX2RhdGFfY2xlYW4yIDwtIGZpbmFsX2RhdGFfY2xlYW4gJT4lCiAgbXV0YXRlKAogICAgYEFjY2VwdGFuY2UgUmF0ZSBOb3JtYWxpemVkYCA9IHJlc2NhbGUoYEFjY2VwdGFuY2UgUmF0ZWApCiAgKQoKI0Rpc3BsYXkgdGhlIGZpcnN0IDYgcm93cyBvZiB0aGUgdXBkYXRlZCBkYXRhCmhlYWQoZmluYWxfZGF0YV9jbGVhbikKYGBgCgoxMi4gV2hhdCBpcyB0aGUgY291bnQgb2YgdGhlIGR1cGxpY2F0ZSBlbnRyaWVzIGluIHRoZSAiU2Nob29sIiBjb2x1bW4/IFJlbW92ZSBkdXBsaWNhdGUgdW5pdmVyc2l0eSBlbnRyaWVzLgoKCiBTb2x1dGlvbjoKCmBgYHtyfQojQ291bnQgdGhlIGR1cGxpY2F0ZSBlbnRyaWVzIGluIHRoZSAiU2Nob29sIiBjb2x1bW4KZHVwbGljYXRlX2NvdW50IDwtIGZpbmFsX2RhdGFfY2xlYW4yICU+JQogIGNvdW50KGBTY2hvb2xgKSAlPiUKICBmaWx0ZXIobiA+IDEpCgojRGlzcGxheSB0aGUgY291bnQgb2YgZHVwbGljYXRlcwpwcmludChkdXBsaWNhdGVfY291bnQpCgojUmVtb3ZlIGR1cGxpY2F0ZSB1bml2ZXJzaXRpZXMgZnJvbSB0aGUgIlNjaG9vbCIgY29sdW1uLApmaW5hbF9kYXRhX2NsZWFuMiA8LSBmaW5hbF9kYXRhX2NsZWFuMiAlPiUKICBkaXN0aW5jdChgU2Nob29sYCwgLmtlZXBfYWxsID0gVFJVRSkKYGBgCgoKMTMuIEZpbmQgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gZ3JhZHVhdGlvbiByYXRlIGFuZCByZXRlbnRpb24gcmF0ZSAoZXhjbHVkZSB0aGUgTkFzIGluIGJvdGggY29sdW1ucykuCgoKIFNvbHV0aW9uOgoKYGBge3J9CiNDYWxjdWxhdGUgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gR3JhZHVhdGlvbiBSYXRlIGFuZCBSZXRlbnRpb24gUmF0ZSwgZXhjbHVkaW5nIE5BIHZhbHVlcwpjb3JyZWxhdGlvbiA8LSBjb3IoZmluYWxfZGF0YV9jbGVhbjIkYEdyYWR1YXRpb24gUmF0ZWAsIGZpbmFsX2RhdGFfY2xlYW4yJGBSZXRlbnRpb24gUmF0ZWAsIHVzZSA9ICJjb21wbGV0ZS5vYnMiKQoKI0Rpc3BsYXkgdGhlIGNvcnJlbGF0aW9uIHJlc3VsdApwcmludChjb3JyZWxhdGlvbikKYGBgCgoKCjE0LiBFeHRyYWN0IHRoZSB2YWx1ZXMgaW4gU2Nob29sIGNvbHVtbiBpbnRvIGEgbmV3IHZhcmlhYmxlIHdpdGhvdXQgIlVuaXZlcnNpdHkiIGluIHRoZSBzdHJpbmcuIEZvciBleGFtcGxlICJSb3dhbiBVbml2ZXJzaXR5IiBiZWNvbWVzICJSb3dhbiIKCgogU29sdXRpb246CgpgYGB7cn0KI0NyZWF0ZSBhIG5ldyBjb2x1bW4gd2l0aG91dCB0aGUgd29yZCAiVW5pdmVyc2l0eSIgaW4gdGhlICJTY2hvb2wiIG5hbWUKZmluYWxfZGF0YV9jbGVhbjMgPC0gZmluYWxfZGF0YV9jbGVhbjIgJT4lCiAgbXV0YXRlKAogICAgYFNjaG9vbCBOYW1lYCA9IGdzdWIoIiBVbml2ZXJzaXR5JCIsICIiLCBgU2Nob29sYCkKICApCgojRGlzcGxheSB0aGUgZmlyc3QgZmV3IHJvd3MgdG8gY2hlY2sgdGhlIHJlc3VsdApoZWFkKGZpbmFsX2RhdGFfY2xlYW4zKQpgYGAKCgoKCjE1LiBDb3VudCBob3cgbWFueSB1bml2ZXJzaXRpZXMgaGF2ZSAiSW5zdGl0dXRlIiBpbiB0aGVpciBuYW1lLgoKCiBTb2x1dGlvbjoKCmBgYHtyfQojQ291bnQgaG93IG1hbnkgdW5pdmVyc2l0aWVzIGhhdmUgIkluc3RpdHV0ZSIgaW4gdGhlaXIgbmFtZQppbnN0aXR1dGVfY291bnQgPC0gc3VtKGdyZXBsKCJJbnN0aXR1dGUiLCBmaW5hbF9kYXRhX2NsZWFuMyRgU2Nob29sYCwgaWdub3JlLmNhc2UgPSBUUlVFKSkKCiNEaXNwbGF5IHRoZSBjb3VudApwcmludChpbnN0aXR1dGVfY291bnQpCmBgYAoKMTYuIEV4cG9ydCB0aGUgY2xlYW5lZCBhbmQgcHJvY2Vzc2VkIGRhdGFzZXQgdG8gYSBDU1YgZmlsZS4KCgogU29sdXRpb246CgpgYGB7cn0KbGlicmFyeShyZWFkcikKCndyaXRlX2NzdihmaW5hbF9kYXRhX2NsZWFuMywgIlVuaXZlcnNpdHlfUmFua2luZ19DbGVhbi5jc3YiLCBuYSA9ICIiKQpgYGAKCg==