Morgan State University
Department of Information Science & Systems
Fall 2024
INSS 615: Data Wrangling for Visualization
Name: Mohammed Naveed Afroz Mulla
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(dplyr)
# multipage scraping using a for loop
base_url <- "https://www.oedb.org/rankings/acceptance-rate/#table-rankings?page="
all_pages <- list()
for (page in 1:6) {
url <- paste0(base_url, page)
page_content <- read_html(url)
table <- page_content %>%
html_table(fill = TRUE) %>%
.[[1]]
all_pages[[page]] <- table
}
college_data <- bind_rows(all_pages)
New names:New names:New names:New names:New names:New names:
selected_columns <- c("Rank", "School", "Student to Faculty Ratio",
"Graduation Rate", "Retention Rate", "Acceptance Rate",
"Enrollment Rate", "Institutional Aid Rate", "Default Rate")
college_data <- college_data %>%
select(all_of(selected_columns))
head(college_data)
NA
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:
college_data[college_data == "N/A"] <- NA
head(college_data)
- Convert percentage columns (e.g., Graduation Rate) to numeric
format.
Solution:
percentage_columns <- c("Graduation Rate", "Retention Rate",
"Acceptance Rate", "Enrollment Rate",
"Institutional Aid Rate", "Default Rate")
college_data[percentage_columns] <- lapply(college_data[percentage_columns], function(column) {
as.numeric(gsub("%", "", column))
})
str(college_data)
tibble [600 × 9] (S3: tbl_df/tbl/data.frame)
$ Rank : int [1:600] 1 2 3 4 5 6 7 8 9 10 ...
$ School : chr [1:600] "Harvard University" "Yale University" "University of Pennsylvania" "Johns Hopkins University" ...
$ Student to Faculty Ratio: chr [1:600] "7 to 1" "6 to 1" "6 to 1" "10 to 1" ...
$ Graduation Rate : num [1:600] 98 97 95 94 93 93 92 91 94 93 ...
$ Retention Rate : num [1:600] 98 99 98 97 97 97 96 96 96 96 ...
$ Acceptance Rate : num [1:600] 6 7 10 14 15 16 17 17 17 17 ...
$ Enrollment Rate : num [1:600] 4 5 7 5 8 7 7 6 8 6 ...
$ Institutional Aid Rate : num [1:600] 44 52 54 51 55 43 61 61 42 49 ...
$ Default Rate : num [1:600] NA NA NA NA NA NA NA NA NA NA ...
head(college_data)
- Transform the “Student to Faculty Ratio” column into two separate
numeric columns: Students and Faculty.
Solution:
library(dplyr)
library(stringr)
college_data <- college_data %>%
mutate(
Students = as.numeric(str_extract(`Student to Faculty Ratio`, "^[0-9]+")),
Faculty = as.numeric(str_extract(`Student to Faculty Ratio`, "[0-9]+$"))
)
college_data <- college_data %>%
select(-`Student to Faculty Ratio`)
head(college_data)
- 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:
missing_count <- sum(is.na(college_data$`Default Rate`))
cat("Number of missing values in 'Default Rate':", missing_count, "\n")
Number of missing values in 'Default Rate': 414
default_rate_median <- median(college_data$`Default Rate`, na.rm = TRUE)
college_data$`Default Rate`[is.na(college_data$`Default Rate`)] <- default_rate_median
cat("Number of missing values in 'Default Rate' after imputation:", sum(is.na(college_data$`Default Rate`)), "\n")
Number of missing values in 'Default Rate' after imputation: 0
- Find the average graduation rate for universities ranked in the top
50.
Solution:
str(college_data$`Graduation Rate`)
num [1:600] 98 97 95 94 93 93 92 91 94 93 ...
college_data$`Graduation Rate` <- as.numeric(gsub("%", "", college_data$`Graduation Rate`))
top_50_universities <- college_data %>%
filter(Rank <= 50)
average_graduation_rate <- mean(top_50_universities$`Graduation Rate`, na.rm = TRUE)
cat("Average Graduation Rate for Top 50 Universities:", average_graduation_rate, "%\n")
Average Graduation Rate for Top 50 Universities: 79.18 %
- Filter universities with a retention rate above 90% and find the
count of rows in the subset.
Solution:
high_retention_universities <- college_data %>%
filter(`Retention Rate` > 90)
high_retention_count <- nrow(high_retention_universities)
cat("Number of universities with a retention rate above 90%:", high_retention_count, "\n")
Number of universities with a retention rate above 90%: 300
- Rank universities by enrollment rate in descending order and display
the last 6 rows.
Solution:
ranked_universities <- college_data %>%
arrange(desc(`Enrollment Rate`))
tail(ranked_universities, 6)
NA
- Create a histogram of graduation rates using ggplot2 library.
Solution:
if (!require("ggplot2")) install.packages("ggplot2")
library(ggplot2)
ggplot(data = college_data, aes(x = `Graduation Rate`)) +
geom_histogram(binwidth = 5, color = "black", fill = "lightblue") +
labs(
title = "Histogram of Graduation Rates",
x = "Graduation Rate (%)",
y = "Frequency"
) +
theme_minimal()

- Plot a scatterplot between acceptance rate and enrollment rate using
ggplot2 library.
Solution:
if (!require("ggplot2")) install.packages("ggplot2")
library(ggplot2)
ggplot(data = college_data, aes(x = `Acceptance Rate`, y = `Enrollment Rate`)) +
geom_point(color = "blue", size = 2) +
labs(
title = "Scatterplot of Acceptance Rate vs Enrollment Rate",
x = "Acceptance Rate (%)",
y = "Enrollment Rate (%)"
) +
theme_minimal()

NA
NA
- Calculate the average default rate by aid rate category (e.g.,
grouped into ranges like 0-20%, 20-40%). Display the categories.
Solution:
college_data <- college_data %>%
mutate(
AidRateCategory = cut(
`Institutional Aid Rate`,
breaks = c(0, 20, 40, 60, 80, 100),
labels = c("0-20%", "20-40%", "40-60%", "60-80%", "80-100%"),
include.lowest = TRUE
)
)
average_default_rate <- college_data %>%
group_by(AidRateCategory) %>%
summarise(AverageDefaultRate = mean(`Default Rate`, na.rm = TRUE))
print(average_default_rate)
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.
Solution:
library(scales)
college_data <- college_data %>%
mutate(`Acceptance Rate Normalized` =
(`Acceptance Rate` - min(`Acceptance Rate`, na.rm = TRUE)) /
(max(`Acceptance Rate`, na.rm = TRUE) - min(`Acceptance Rate`, na.rm = TRUE)))
head(college_data[, c("Acceptance Rate", "Acceptance Rate Normalized")])
NA
NA
NA
- What is the count of the duplicate entries in the “School” column?
Remove duplicate university entries.
Solution:
duplicate_count <- sum(duplicated(college_data$School))
cat("Number of duplicate entries in the 'School' column:", duplicate_count, "\n")
Number of duplicate entries in the 'School' column: 500
college_data <- college_data %>%
distinct(School, .keep_all = TRUE)
cat("Number of rows after removing duplicates:", nrow(college_data), "\n")
Number of rows after removing duplicates: 100
- Find the correlation between graduation rate and retention rate
(exclude the NAs in both columns).
Solution:
correlation <- cor(
college_data$`Graduation Rate`,
college_data$`Retention Rate`,
use = "complete.obs" # Exclude NAs in both columns
)
cat("Correlation between Graduation Rate and Retention Rate:", correlation, "\n")
Correlation between Graduation Rate and Retention Rate: 0.8052769
- Extract the values in School column into a new variable without
“University” in the string. For example “Rowan University” becomes
“Rowan”
Solution:
college_data <- college_data %>%
mutate(School_No_University = gsub(" University", "", School))
head(college_data[, c("School", "School_No_University")])
NA
- Count how many universities have “Institute” in their name.
Solution:
# Count universities with "Institute" in their name
institute_count <- sum(grepl("Institute", college_data$School))
# Display the count
cat("Number of universities with 'Institute' in their name:", institute_count, "\n")
Number of universities with 'Institute' in their name: 5
- Export the cleaned and processed dataset to a CSV file.
Solution:
# Export the cleaned dataset to a CSV file
write.csv(college_data, "cleaned_college_data.csv", row.names = FALSE)
# Confirm the file has been saved
cat("The cleaned dataset has been exported to 'cleaned_college_data.csv'.\n")
The cleaned dataset has been exported to 'cleaned_college_data.csv'.
LS0tDQp0aXRsZTogIklOU1M2MTUgSG9tZXdvcmsgNSINCm91dHB1dDoNCiAgIyB3b3JkX2RvY3VtZW50OiBkZWZhdWx0DQogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQNCiAgaHRtbF9kb2N1bWVudDoNCiAgICBkZl9wcmludDogcGFnZWQNCi0tLQ0KDQoNCioqTW9yZ2FuIFN0YXRlIFVuaXZlcnNpdHkqKg0KDQoqKkRlcGFydG1lbnQgb2YgSW5mb3JtYXRpb24gU2NpZW5jZSAmIFN5c3RlbXMqKg0KDQoqKkZhbGwgMjAyNCoqDQoNCioqSU5TUyA2MTU6IERhdGEgV3JhbmdsaW5nIGZvciBWaXN1YWxpemF0aW9uKioNCg0KKipOYW1lOiBNb2hhbW1lZCBOYXZlZWQgQWZyb3ogTXVsbGEqKg0KDQoqRHVlOiBEZWMgMSwgMjAyNCAoU3VuZGF5KSoNCg0KDQoNClF1ZXN0aW9ucw0KDQoNCkEuIFNjcmFwZSB0aGUgQ29sbGVnZSBSYW5rZWQgYnkgQWNjZXB0YW5jZSBSYXRlIGRhdGFzZXQgYXZhaWxhYmxlIGF0IHRoaXMgbGluazogaHR0cHM6Ly93d3cub2VkYi5vcmcvcmFua2luZ3MvYWNjZXB0YW5jZS1yYXRlLyN0YWJsZS1yYW5raW5ncyBhbmQgc2VsZWN0IHRoZSBmaXJzdCA5IGNvbHVtbnMgW1JhbmssIFNjaG9vbCwgU3R1ZGVudCB0byBGYWN1bHR5IFJhdGlvLCBHcmFkdWF0aW9uIFJhdGUsIFJldGVudGlvbiBSYXRlLCBBY2NlcHRhbmNlIFJhdGUsIEVucm9sbG1lbnQgUmF0ZSwgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZSwgYW5kIERlZmF1bHQgUmF0ZV0gYXMgdGhlIGRhdGFzZXQgZm9yIHRoaXMgYXNzaWdubWVudC4gWzIwIFBvaW50c10NCg0KSGludDogVGhlcmUgYXJlIDYgcGFnZXMgb2YgZGF0YSwgc28geW91IG1heSB3YW50IHRvIHVzZSBhIGZvciBsb29wIHRvIGF1dG9tYXRlIHRoZSBzY3JhcGluZyBwcm9jZXNzIGFuZCBjb21iaW5lIHRoZSBkYXRhIGZyb20gYWxsIDYgcGFnZXMuIFRoaXMgaXMganVzdCBhIHN1Z2dlc3Rpb27igJR5b3UgYXJlIGZyZWUgdG8gY3JlYXRlIHRoZSBkYXRhc2V0IHdpdGhvdXQgYXV0b21hdGluZyB0aGUgd2ViIHNjcmFwcGluZyBwcm9jZXNzLg0KDQogDQogIFNvbHV0aW9uOg0KYGBge3J9DQpsaWJyYXJ5KHJ2ZXN0KSANCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShkcGx5cikNCg0KDQojIG11bHRpcGFnZSBzY3JhcGluZyB1c2luZyBhIGZvciBsb29wDQoNCmJhc2VfdXJsIDwtICJodHRwczovL3d3dy5vZWRiLm9yZy9yYW5raW5ncy9hY2NlcHRhbmNlLXJhdGUvI3RhYmxlLXJhbmtpbmdzP3BhZ2U9Ig0KDQphbGxfcGFnZXMgPC0gbGlzdCgpDQoNCmZvciAocGFnZSBpbiAxOjYpIHsNCiANCiAgdXJsIDwtIHBhc3RlMChiYXNlX3VybCwgcGFnZSkNCiAgDQogIHBhZ2VfY29udGVudCA8LSByZWFkX2h0bWwodXJsKQ0KICANCiAgdGFibGUgPC0gcGFnZV9jb250ZW50ICU+JQ0KICAgIGh0bWxfdGFibGUoZmlsbCA9IFRSVUUpICU+JQ0KICAgIC5bWzFdXQ0KICANCiAgYWxsX3BhZ2VzW1twYWdlXV0gPC0gdGFibGUNCn0NCg0KY29sbGVnZV9kYXRhIDwtIGJpbmRfcm93cyhhbGxfcGFnZXMpDQoNCnNlbGVjdGVkX2NvbHVtbnMgPC0gYygiUmFuayIsICJTY2hvb2wiLCAiU3R1ZGVudCB0byBGYWN1bHR5IFJhdGlvIiwgDQogICAgICAgICAgICAgICAgICAgICAgIkdyYWR1YXRpb24gUmF0ZSIsICJSZXRlbnRpb24gUmF0ZSIsICJBY2NlcHRhbmNlIFJhdGUiLA0KICAgICAgICAgICAgICAgICAgICAgICJFbnJvbGxtZW50IFJhdGUiLCAiSW5zdGl0dXRpb25hbCBBaWQgUmF0ZSIsICJEZWZhdWx0IFJhdGUiKQ0KDQpjb2xsZWdlX2RhdGEgPC0gY29sbGVnZV9kYXRhICU+JQ0KICBzZWxlY3QoYWxsX29mKHNlbGVjdGVkX2NvbHVtbnMpKQ0KaGVhZChjb2xsZWdlX2RhdGEpDQoNCmBgYA0KDQpCLiBZb3UgYXJlIGdvaW5nIHRvIG5lZWQgdGhlIGRhdGFzZXQgY3JlYXRlZCBpbiBRdWVzdGlvbiBBIHRvIGFuc3dlciB0aGUgZm9sbG93aW5nIHF1ZXN0aW9ucy4gVGhlcmUgYXJlIDE2IHF1ZXN0aW9ucyBlYWNoIGNhcnJ5aW5nIDUgcG9pbnRzOg0KDQoxLiBSZXBsYWNlIHRoZSBtaXNzaW5nIHZhbHVlcyAiTi9BIiBpbiB0aGUgZGF0YXNldCB3aXRoIE5BLg0KDQoNCiAgU29sdXRpb246DQpgYGB7cn0NCmNvbGxlZ2VfZGF0YVtjb2xsZWdlX2RhdGEgPT0gIk4vQSJdIDwtIE5BDQoNCmhlYWQoY29sbGVnZV9kYXRhKQ0KYGBgDQoNCjIuIENvbnZlcnQgcGVyY2VudGFnZSBjb2x1bW5zIChlLmcuLCBHcmFkdWF0aW9uIFJhdGUpIHRvIG51bWVyaWMgZm9ybWF0Lg0KDQogIA0KICBTb2x1dGlvbjoNCmBgYHtyfQ0KDQpwZXJjZW50YWdlX2NvbHVtbnMgPC0gYygiR3JhZHVhdGlvbiBSYXRlIiwgIlJldGVudGlvbiBSYXRlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAiQWNjZXB0YW5jZSBSYXRlIiwgIkVucm9sbG1lbnQgUmF0ZSIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgIkluc3RpdHV0aW9uYWwgQWlkIFJhdGUiLCAiRGVmYXVsdCBSYXRlIikNCg0KY29sbGVnZV9kYXRhW3BlcmNlbnRhZ2VfY29sdW1uc10gPC0gbGFwcGx5KGNvbGxlZ2VfZGF0YVtwZXJjZW50YWdlX2NvbHVtbnNdLCBmdW5jdGlvbihjb2x1bW4pIHsNCg0KICBhcy5udW1lcmljKGdzdWIoIiUiLCAiIiwgY29sdW1uKSkNCn0pDQoNCnN0cihjb2xsZWdlX2RhdGEpDQoNCmhlYWQoY29sbGVnZV9kYXRhKQ0KYGBgDQoNCg0KMy4gVHJhbnNmb3JtIHRoZSAiU3R1ZGVudCB0byBGYWN1bHR5IFJhdGlvIiBjb2x1bW4gaW50byB0d28gc2VwYXJhdGUgbnVtZXJpYyBjb2x1bW5zOiBTdHVkZW50cyBhbmQgRmFjdWx0eS4NCg0KDQogIFNvbHV0aW9uOg0KYGBge3J9DQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShzdHJpbmdyKQ0KDQpjb2xsZWdlX2RhdGEgPC0gY29sbGVnZV9kYXRhICU+JQ0KICBtdXRhdGUoDQogICAgU3R1ZGVudHMgPSBhcy5udW1lcmljKHN0cl9leHRyYWN0KGBTdHVkZW50IHRvIEZhY3VsdHkgUmF0aW9gLCAiXlswLTldKyIpKSwNCiAgICBGYWN1bHR5ID0gYXMubnVtZXJpYyhzdHJfZXh0cmFjdChgU3R1ZGVudCB0byBGYWN1bHR5IFJhdGlvYCwgIlswLTldKyQiKSkNCiAgKQ0KDQpjb2xsZWdlX2RhdGEgPC0gY29sbGVnZV9kYXRhICU+JQ0KICBzZWxlY3QoLWBTdHVkZW50IHRvIEZhY3VsdHkgUmF0aW9gKQ0KDQpoZWFkKGNvbGxlZ2VfZGF0YSkNCmBgYA0KDQoNCg0KDQo0LiBXaGF0IGlzIHRoZSBjb3VudCBvZiBtaXNzaW5nIHZhbHVlcyBpbiB0aGUgIkRlZmF1bHQgUmF0ZSIgY29sdW1uPyBJbXB1dGUgdGhlIG1pc3NpbmcgdmFsdWVzIGluIHRoZSAiRGVmYXVsdCBSYXRlIiBjb2x1bW4gd2l0aCB0aGUgbWVkaWFuIHZhbHVlLg0KDQoNCiAgU29sdXRpb246DQpgYGB7cn0NCm1pc3NpbmdfY291bnQgPC0gc3VtKGlzLm5hKGNvbGxlZ2VfZGF0YSRgRGVmYXVsdCBSYXRlYCkpDQpjYXQoIk51bWJlciBvZiBtaXNzaW5nIHZhbHVlcyBpbiAnRGVmYXVsdCBSYXRlJzoiLCBtaXNzaW5nX2NvdW50LCAiXG4iKQ0KDQpkZWZhdWx0X3JhdGVfbWVkaWFuIDwtIG1lZGlhbihjb2xsZWdlX2RhdGEkYERlZmF1bHQgUmF0ZWAsIG5hLnJtID0gVFJVRSkNCg0KY29sbGVnZV9kYXRhJGBEZWZhdWx0IFJhdGVgW2lzLm5hKGNvbGxlZ2VfZGF0YSRgRGVmYXVsdCBSYXRlYCldIDwtIGRlZmF1bHRfcmF0ZV9tZWRpYW4NCg0KY2F0KCJOdW1iZXIgb2YgbWlzc2luZyB2YWx1ZXMgaW4gJ0RlZmF1bHQgUmF0ZScgYWZ0ZXIgaW1wdXRhdGlvbjoiLCBzdW0oaXMubmEoY29sbGVnZV9kYXRhJGBEZWZhdWx0IFJhdGVgKSksICJcbiIpDQoNCmBgYA0KDQoNCjUuIEZpbmQgdGhlIGF2ZXJhZ2UgZ3JhZHVhdGlvbiByYXRlIGZvciB1bml2ZXJzaXRpZXMgcmFua2VkIGluIHRoZSB0b3AgNTAuDQoNCg0KICBTb2x1dGlvbjoNCmBgYHtyfQ0Kc3RyKGNvbGxlZ2VfZGF0YSRgR3JhZHVhdGlvbiBSYXRlYCkNCg0KY29sbGVnZV9kYXRhJGBHcmFkdWF0aW9uIFJhdGVgIDwtIGFzLm51bWVyaWMoZ3N1YigiJSIsICIiLCBjb2xsZWdlX2RhdGEkYEdyYWR1YXRpb24gUmF0ZWApKQ0KDQp0b3BfNTBfdW5pdmVyc2l0aWVzIDwtIGNvbGxlZ2VfZGF0YSAlPiUNCiAgZmlsdGVyKFJhbmsgPD0gNTApDQoNCmF2ZXJhZ2VfZ3JhZHVhdGlvbl9yYXRlIDwtIG1lYW4odG9wXzUwX3VuaXZlcnNpdGllcyRgR3JhZHVhdGlvbiBSYXRlYCwgbmEucm0gPSBUUlVFKQ0KDQpjYXQoIkF2ZXJhZ2UgR3JhZHVhdGlvbiBSYXRlIGZvciBUb3AgNTAgVW5pdmVyc2l0aWVzOiIsIGF2ZXJhZ2VfZ3JhZHVhdGlvbl9yYXRlLCAiJVxuIikNCg0KDQpgYGANCg0KDQo2LiBGaWx0ZXIgdW5pdmVyc2l0aWVzIHdpdGggYSByZXRlbnRpb24gcmF0ZSBhYm92ZSA5MCUgYW5kIGZpbmQgdGhlIGNvdW50IG9mIHJvd3MgaW4gdGhlIHN1YnNldC4NCg0KDQogIFNvbHV0aW9uOg0KYGBge3J9DQpoaWdoX3JldGVudGlvbl91bml2ZXJzaXRpZXMgPC0gY29sbGVnZV9kYXRhICU+JQ0KICBmaWx0ZXIoYFJldGVudGlvbiBSYXRlYCA+IDkwKQ0KDQpoaWdoX3JldGVudGlvbl9jb3VudCA8LSBucm93KGhpZ2hfcmV0ZW50aW9uX3VuaXZlcnNpdGllcykNCg0KDQpjYXQoIk51bWJlciBvZiB1bml2ZXJzaXRpZXMgd2l0aCBhIHJldGVudGlvbiByYXRlIGFib3ZlIDkwJToiLCBoaWdoX3JldGVudGlvbl9jb3VudCwgIlxuIikNCg0KDQpgYGANCg0KDQo3LiBSYW5rIHVuaXZlcnNpdGllcyBieSBlbnJvbGxtZW50IHJhdGUgaW4gZGVzY2VuZGluZyBvcmRlciBhbmQgZGlzcGxheSB0aGUgbGFzdCA2IHJvd3MuDQoNCg0KICBTb2x1dGlvbjoNCmBgYHtyfQ0KDQpyYW5rZWRfdW5pdmVyc2l0aWVzIDwtIGNvbGxlZ2VfZGF0YSAlPiUNCiAgYXJyYW5nZShkZXNjKGBFbnJvbGxtZW50IFJhdGVgKSkNCg0KdGFpbChyYW5rZWRfdW5pdmVyc2l0aWVzLCA2KQ0KDQpgYGANCg0KDQo4LiBDcmVhdGUgYSBoaXN0b2dyYW0gb2YgZ3JhZHVhdGlvbiByYXRlcyB1c2luZyBnZ3Bsb3QyIGxpYnJhcnkuDQoNCg0KICBTb2x1dGlvbjoNCmBgYHtyfQ0KDQppZiAoIXJlcXVpcmUoImdncGxvdDIiKSkgaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpDQoNCmxpYnJhcnkoZ2dwbG90MikNCg0KZ2dwbG90KGRhdGEgPSBjb2xsZWdlX2RhdGEsIGFlcyh4ID0gYEdyYWR1YXRpb24gUmF0ZWApKSArDQogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gNSwgY29sb3IgPSAiYmxhY2siLCBmaWxsID0gImxpZ2h0Ymx1ZSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJIaXN0b2dyYW0gb2YgR3JhZHVhdGlvbiBSYXRlcyIsDQogICAgeCA9ICJHcmFkdWF0aW9uIFJhdGUgKCUpIiwNCiAgICB5ID0gIkZyZXF1ZW5jeSINCiAgKSArDQogIHRoZW1lX21pbmltYWwoKQ0KDQpgYGANCg0KDQo5LiBQbG90IGEgc2NhdHRlcnBsb3QgYmV0d2VlbiBhY2NlcHRhbmNlIHJhdGUgYW5kIGVucm9sbG1lbnQgcmF0ZSB1c2luZyBnZ3Bsb3QyIGxpYnJhcnkuDQoNCg0KICBTb2x1dGlvbjoNCmBgYHtyfQ0KaWYgKCFyZXF1aXJlKCJnZ3Bsb3QyIikpIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KDQpsaWJyYXJ5KGdncGxvdDIpDQoNCmdncGxvdChkYXRhID0gY29sbGVnZV9kYXRhLCBhZXMoeCA9IGBBY2NlcHRhbmNlIFJhdGVgLCB5ID0gYEVucm9sbG1lbnQgUmF0ZWApKSArDQogIGdlb21fcG9pbnQoY29sb3IgPSAiYmx1ZSIsIHNpemUgPSAyKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiU2NhdHRlcnBsb3Qgb2YgQWNjZXB0YW5jZSBSYXRlIHZzIEVucm9sbG1lbnQgUmF0ZSIsDQogICAgeCA9ICJBY2NlcHRhbmNlIFJhdGUgKCUpIiwNCiAgICB5ID0gIkVucm9sbG1lbnQgUmF0ZSAoJSkiDQogICkgKw0KICB0aGVtZV9taW5pbWFsKCkNCg0KDQpgYGANCg0KDQoxMC4gQ2FsY3VsYXRlIHRoZSBhdmVyYWdlIGRlZmF1bHQgcmF0ZSBieSBhaWQgcmF0ZSBjYXRlZ29yeSAoZS5nLiwgZ3JvdXBlZCBpbnRvIHJhbmdlcyBsaWtlIDAtMjAlLCAyMC00MCUpLiBEaXNwbGF5IHRoZSBjYXRlZ29yaWVzLg0KDQoNCiAgU29sdXRpb246DQpgYGB7cn0NCmNvbGxlZ2VfZGF0YSA8LSBjb2xsZWdlX2RhdGEgJT4lDQogIG11dGF0ZSgNCiAgICBBaWRSYXRlQ2F0ZWdvcnkgPSBjdXQoDQogICAgICBgSW5zdGl0dXRpb25hbCBBaWQgUmF0ZWAsDQogICAgICBicmVha3MgPSBjKDAsIDIwLCA0MCwgNjAsIDgwLCAxMDApLA0KICAgICAgbGFiZWxzID0gYygiMC0yMCUiLCAiMjAtNDAlIiwgIjQwLTYwJSIsICI2MC04MCUiLCAiODAtMTAwJSIpLA0KICAgICAgaW5jbHVkZS5sb3dlc3QgPSBUUlVFDQogICAgKQ0KICApDQoNCmF2ZXJhZ2VfZGVmYXVsdF9yYXRlIDwtIGNvbGxlZ2VfZGF0YSAlPiUNCiAgZ3JvdXBfYnkoQWlkUmF0ZUNhdGVnb3J5KSAlPiUNCiAgc3VtbWFyaXNlKEF2ZXJhZ2VEZWZhdWx0UmF0ZSA9IG1lYW4oYERlZmF1bHQgUmF0ZWAsIG5hLnJtID0gVFJVRSkpDQoNCnByaW50KGF2ZXJhZ2VfZGVmYXVsdF9yYXRlKQ0KDQoNCmBgYA0KDQoNCg0KDQoxMS4gTm9ybWFsaXplIHRoZSBhY2NlcHRhbmNlIHJhdGUgdG8gYSBzY2FsZSBvZiAwLTEgYW5kIHNhdmUgaW4gYSBuZXcgY29sdW1uICJBY2NlcHRhbmNlIFJhdGUgTm9ybWFsaXplZCIuIERpc3BsYXkgdGhlIGZpcnN0IDYgdmFsdWVzLg0KDQoNCiAgU29sdXRpb246DQogDQpgYGB7cn0NCmxpYnJhcnkoc2NhbGVzKQ0KY29sbGVnZV9kYXRhIDwtIGNvbGxlZ2VfZGF0YSAlPiUNCiAgbXV0YXRlKGBBY2NlcHRhbmNlIFJhdGUgTm9ybWFsaXplZGAgPSANCiAgICAgICAgICAgKGBBY2NlcHRhbmNlIFJhdGVgIC0gbWluKGBBY2NlcHRhbmNlIFJhdGVgLCBuYS5ybSA9IFRSVUUpKSAvIA0KICAgICAgICAgICAobWF4KGBBY2NlcHRhbmNlIFJhdGVgLCBuYS5ybSA9IFRSVUUpIC0gbWluKGBBY2NlcHRhbmNlIFJhdGVgLCBuYS5ybSA9IFRSVUUpKSkNCg0KaGVhZChjb2xsZWdlX2RhdGFbLCBjKCJBY2NlcHRhbmNlIFJhdGUiLCAiQWNjZXB0YW5jZSBSYXRlIE5vcm1hbGl6ZWQiKV0pDQoNCg0KDQpgYGANCg0KMTIuIFdoYXQgaXMgdGhlIGNvdW50IG9mIHRoZSBkdXBsaWNhdGUgZW50cmllcyBpbiB0aGUgIlNjaG9vbCIgY29sdW1uPyBSZW1vdmUgZHVwbGljYXRlIHVuaXZlcnNpdHkgZW50cmllcy4NCg0KDQogU29sdXRpb246DQoNCmBgYHtyfQ0KZHVwbGljYXRlX2NvdW50IDwtIHN1bShkdXBsaWNhdGVkKGNvbGxlZ2VfZGF0YSRTY2hvb2wpKQ0KY2F0KCJOdW1iZXIgb2YgZHVwbGljYXRlIGVudHJpZXMgaW4gdGhlICdTY2hvb2wnIGNvbHVtbjoiLCBkdXBsaWNhdGVfY291bnQsICJcbiIpDQoNCmNvbGxlZ2VfZGF0YSA8LSBjb2xsZWdlX2RhdGEgJT4lDQogIGRpc3RpbmN0KFNjaG9vbCwgLmtlZXBfYWxsID0gVFJVRSkNCg0KY2F0KCJOdW1iZXIgb2Ygcm93cyBhZnRlciByZW1vdmluZyBkdXBsaWNhdGVzOiIsIG5yb3coY29sbGVnZV9kYXRhKSwgIlxuIikNCg0KDQpgYGANCg0KDQoxMy4gRmluZCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiBncmFkdWF0aW9uIHJhdGUgYW5kIHJldGVudGlvbiByYXRlIChleGNsdWRlIHRoZSBOQXMgaW4gYm90aCBjb2x1bW5zKS4NCg0KDQogU29sdXRpb246DQoNCmBgYHtyfQ0KY29ycmVsYXRpb24gPC0gY29yKA0KICBjb2xsZWdlX2RhdGEkYEdyYWR1YXRpb24gUmF0ZWAsDQogIGNvbGxlZ2VfZGF0YSRgUmV0ZW50aW9uIFJhdGVgLA0KICB1c2UgPSAiY29tcGxldGUub2JzIiAjIEV4Y2x1ZGUgTkFzIGluIGJvdGggY29sdW1ucw0KKQ0KDQpjYXQoIkNvcnJlbGF0aW9uIGJldHdlZW4gR3JhZHVhdGlvbiBSYXRlIGFuZCBSZXRlbnRpb24gUmF0ZToiLCBjb3JyZWxhdGlvbiwgIlxuIikNCg0KDQpgYGANCg0KDQoNCjE0LiBFeHRyYWN0IHRoZSB2YWx1ZXMgaW4gU2Nob29sIGNvbHVtbiBpbnRvIGEgbmV3IHZhcmlhYmxlIHdpdGhvdXQgIlVuaXZlcnNpdHkiIGluIHRoZSBzdHJpbmcuIEZvciBleGFtcGxlICJSb3dhbiBVbml2ZXJzaXR5IiBiZWNvbWVzICJSb3dhbiINCg0KDQogU29sdXRpb246DQoNCmBgYHtyfQ0KY29sbGVnZV9kYXRhIDwtIGNvbGxlZ2VfZGF0YSAlPiUNCiAgbXV0YXRlKFNjaG9vbF9Ob19Vbml2ZXJzaXR5ID0gZ3N1YigiIFVuaXZlcnNpdHkiLCAiIiwgU2Nob29sKSkNCg0KaGVhZChjb2xsZWdlX2RhdGFbLCBjKCJTY2hvb2wiLCAiU2Nob29sX05vX1VuaXZlcnNpdHkiKV0pDQoNCmBgYA0KDQoNCg0KDQoxNS4gQ291bnQgaG93IG1hbnkgdW5pdmVyc2l0aWVzIGhhdmUgIkluc3RpdHV0ZSIgaW4gdGhlaXIgbmFtZS4NCg0KDQogU29sdXRpb246DQoNCmBgYHtyfQ0KaW5zdGl0dXRlX2NvdW50IDwtIHN1bShncmVwbCgiSW5zdGl0dXRlIiwgY29sbGVnZV9kYXRhJFNjaG9vbCkpDQoNCmNhdCgiTnVtYmVyIG9mIHVuaXZlcnNpdGllcyB3aXRoICdJbnN0aXR1dGUnIGluIHRoZWlyIG5hbWU6IiwgaW5zdGl0dXRlX2NvdW50LCAiXG4iKQ0KDQoNCg0KYGBgDQoNCjE2LiBFeHBvcnQgdGhlIGNsZWFuZWQgYW5kIHByb2Nlc3NlZCBkYXRhc2V0IHRvIGEgQ1NWIGZpbGUuDQoNCg0KIFNvbHV0aW9uOg0KDQpgYGB7cn0NCndyaXRlLmNzdihjb2xsZWdlX2RhdGEsICJjbGVhbmVkX2NvbGxlZ2VfZGF0YS5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkNCg0KY2F0KCJUaGUgY2xlYW5lZCBkYXRhc2V0IGhhcyBiZWVuIGV4cG9ydGVkIHRvICdjbGVhbmVkX2NvbGxlZ2VfZGF0YS5jc3YnLlxuIikNCg0KDQoNCmBgYA0KDQo=