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:

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

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

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

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)
  1. 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)
  1. 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
  1. 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)
  1. 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
  1. 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==