https://github.com/rinckd/math-creativity/blob/0b33fdfb200e7a2524a154e97b9749bd215471c9/quiz_1.R https://github.com/SkillSmart/Coursera_SoftwareDevelopment-in-R/blob/2643b77a18b9fa8b05d1043e7e71be5c8debbd51/RProgrammingEnv/Week4/Quiz.Rmd https://github.com/yufree/democode/blob/0f307fc61e7b61d99a61ba37082378d008055589/tidyverse/hw.Rmd
Mastering R Class 1: Final Quiz
library(readr)
library(dplyr)
library(tidyr)
library(readxl)
Introduction
The goal of this assignment is to take datasets that are either messy or simply not tidy and to make them tidy datasets. The objective is to gain some familiarity with the functions for reading in data into R and calculating basic summary statistics on the data. In particular, we will make use of the following packages: dplyr, tidyr, readr, and readxl.
Before staring the quiz you will need to download the data for the quiz, which can be found in the file quiz_data.zip. The zip archive file contains two files:
daily_SPEC_2014.csv.bz2: a compressed CSV file containing daily measurements of particulate matter chemical constituents in the United States for the year 2014. Note that you should NOT have to decompress this file. The data are measured at a network of federal, state, and local monitors and assembled by the EPA. In this dataset, the “Arithmetic Mean” column provides the level of the indicated chemical constituent and the “Parameter.Name” column provides the name of the chemical constituent. The combination of a “State Code”, a “County Code”, and a “Site Num”, uniquely identifies a monitoring site (the location of which is provided by the “Latitude” and “Longitude” columns).
if(!file.exists("./data/pollution.csv")){
pollution <- read_csv("data/daily_SPEC_2014.csv.bz2")
write_csv(pollution, "./data/pollution.csv")
}
pollution <- read_csv("./data/pollution.csv")
aqs_sites.xlsx: An excel spreadsheet containing metadata about each of the monitoring sites in the United States where pollution measurements are made. In particular, the “Land Use” and “Location Setting” variables contain information about what kinds of areas the monitors are located in (i.e. “residential” vs. “forest”). Once the data have been downloaded to your working directory, you can begin the quiz assignment. For this assignment, you may want to review Sections 1.2 through 1.5 of Mastering Software Development in R.
1.Question
Use the readr package to read the daily_SPEC_2014.csv.bz2 data file in to R. This file contains daily levels of fine particulate matter (PM2.5) chemical constituents across the United States. The data are measured at a network of federal, state, and local monitors and assembled by the EPA.
In this dataset, the “Sample.Value” (see bolded words a couple of paragraphs before) column provides the level of the indicated chemical constituent and the “Parameter.Name” column provides the name of the chemical constituent. The combination of a “State.Code”, a “County.Code”, and a “Site.Num”, uniquely identifies a monitoring site (the location of which is provided by the “Latitude” and “Longitude” columns).
For all of the questions below, you can ignore the missing values in the dataset, so when taking averages, just remove the missing values before taking the average (i.e. you can use na.rm = TRUE in the mean() function)
What is average Sample.Value for “Bromine PM2.5 LC” in the state of Wisconsin in this dataset?
Q1 <- pollution %>% filter(`Parameter Name`== "Bromine PM2.5 LC", `State Name` == "Wisconsin")
Q1 <- pollution %>% summarise(avg = mean(`Arithmetic Mean`, na.rm=TRUE))
Q1
2.Question
Calculate the average of each chemical constituent across all states, monitoring sites and all time points.
Which constituent Parameter.Name has the highest average level?
Q2 <- pollution %>%
group_by(`Parameter Name`,`State Name`, `Site Num`, `Date Local`) %>%
summarise(avg = mean(`Arithmetic Mean`)) %>%
arrange(desc(avg))
Q2
Question 3
Which monitoring site has the highest average level of “Sulfate PM2.5 LC” across all time?
Indicate the state code, county code, and site number.
Q3_1 <- pollution %>% filter(`Parameter Name` == "Sulfate PM2.5 LC") %>%
group_by(`Site Num`,`County Code`,`State Code`) %>%
summarise(avg = mean(`Arithmetic Mean`)) %>%
arrange(desc(avg))
Q3_1
Find the mean concentration for all pollutants over time in one City
Q3_2 <- pollution %>% filter(`City Name` == 'Charlotte') %>% summarise(avg = mean(`Arithmetic Mean`))
Q3_2
Question 4
What is the absolute difference in the average levels of “EC PM2.5 LC TOR” between the states California and Arizona, across all time and all monitoring sites?
# Calculating the Pollutant Means for California, Arizona
Q4 <- pollution %>% filter(`State Name` %in% c("California", "Arizona"), `Parameter Name`=="EC PM2.5 LC TOR") %>%
group_by(`State Name`) %>%
summarise(avg_concentration = mean(`Arithmetic Mean`))
# Calculating the absolute difference
Q4 <- abs(Q4[1,2]-Q4[2,2])
Q4
Question 5
What is the median level of “OC PM2.5 LC TOR” in the western United States, across all time? Define western as any monitoring location that has a Longitude LESS THAN -100.
Q5 <- pollution %>% filter(Longitude < -100, `Parameter Name` == "OC PM2.5 LC TOR") %>%
summarise(med_level = median(`Arithmetic Mean`))
Q5
Question 6
Use the readxl package to read the file aqs_sites.xlsx into R (you may need to install the package first). This file contains metadata about each of the monitoring sites in the EPA’s monitoring system. In particular, the “Land Use” and “Location Setting” variables contain information about what kinds of areas the monitors are located in (i.e. “residential” vs. “forest”).
sites <- read_excel("./data/aqs_sites.xlsx")
How many monitoring sites are labelled as both RESIDENTIAL for “Land Use” and SUBURBAN for “Location Setting”?
Q6 <- sites %>% filter(`Land Use` == "RESIDENTIAL", `Location Setting` =="SUBURBAN") %>% summarise(n())
Q6
Question 7
What is the median level of “EC PM2.5 LC TOR” amongst monitoring sites that are labelled as both “RESIDENTIAL” and “SUBURBAN” in the eastern U.S., where eastern is defined as Longitude greater than or equal to -100?
In order to join the tables we have to set the codes the same
# Check if the identifiers are the same
names(pollution)[1:5] == names(sites)[1:5]
[1] TRUE TRUE FALSE FALSE FALSE
str(pollution[1:5])
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 2098160 obs. of 5 variables:
$ State Code : chr "01" "01" "01" "01" ...
$ County Code : chr "003" "003" "003" "003" ...
$ Site Num : chr "0010" "0010" "0010" "0010" ...
$ Parameter Code: int 68105 68105 68105 68105 68105 68105 68105 68105 68105 68105 ...
$ POC : int 1 1 1 1 1 1 1 1 1 1 ...
str(sites[1:5])
Classes ‘tbl_df’, ‘tbl’ and 'data.frame': 20239 obs. of 5 variables:
$ State Code : num 1 1 1 1 1 1 1 1 1 1 ...
$ County Code: num 1 1 1 3 3 3 3 5 5 7 ...
$ Site Number: num 1 2 3 1 2 3 10 1 2 1 ...
$ Latitude : num 32.4 32.4 32.3 0 30.6 ...
$ Longitude : num -86.5 -86.4 -86.8 0 -87.7 ...
Need to change a few variables in the pollution data
pollution$`State Code` <- as.numeric(pollution$`State Code`)
pollution$`County Code` <- as.numeric(pollution$`County Code`)
pollution$`Site Num` <- as.numeric(pollution$`Site Num`)
Now you can join the two DFs on (State, County, SiteId)
joined_tbl <- inner_join(sites, pollution)
Joining, by = c("State Code", "County Code", "Latitude", "Longitude", "Datum", "Local Site Name", "Address", "State Name", "County Name", "City Name", "CBSA Name")
summary(joined_tbl$`Arithmetic Mean`)
Min. 1st Qu. Median Mean 3rd Qu. Max.
-99.000 0.001 0.196 138.900 19.900 5453.000
Q7 <- joined_tbl %>%
group_by(`State Code`, `County Code`,`Parameter Name`) %>%
summarise(avg_concentration = mean(`Arithmetic Mean`)) %>%
arrange(desc(avg_concentration))
Calculating the median level
Q7 <- joined_tbl %>% group_by(`State Code`, `County Code`, `Site Number`, `Parameter Name`) %>%
filter(`Location Setting`== "SUBURBAN", `Land Use`=="RESIDENTIAL",
`Parameter Name`== "EC PM2.5 LC TOR", Longitude >= -100) %>%
summarise(avg_concentration = mean(`Arithmetic Mean`))
Q7
Question 8
Amongst monitoring sites that are labeled as COMMERCIAL for “Land Use”, which month of the year has the highest average levels of “Sulfate PM2.5 LC”?
library(lubridate)
package ‘lubridate’ was built under R version 3.2.5
Attaching package: ‘lubridate’
The following object is masked from ‘package:base’:
date
Q8 <- joined_tbl %>% filter(`Land Use`=="COMMERCIAL", `Parameter Name`=="Sulfate PM2.5 LC") %>%
group_by(month(`Date Local`)) %>%
summarise(avg_concentration = mean(`Arithmetic Mean`)) %>%
arrange(desc(avg_concentration))
Q8
Question 9:
Take a look at the data for the monitoring site identified by State Code 6, County Code 65, and Site Number 8001 (this monitor is in California). At this monitor, for how many days is the sum of “Sulfate PM2.5 LC” and “Total Nitrate PM2.5 LC” greater than 10?
For each of the chemical constituents, there will be some dates that have multiple Sample.Value’s at this monitoring site. When there are multiple values on a given date, take the average of the constituent values for that date.
Q9 <- joined_tbl %>%
filter(`State Code`==6, `County Code`==65, `Site Number`==8001, `Parameter Name` %in% c("Total Nitrate PM2.5 LC", "Sulfate PM2.5 LC")) %>%
group_by(`Site Number`, month = month(`Date Local`),`Parameter Name`, day = day(`Date Local`) ) %>%
summarise(avg_value = sum(mean(`Arithmetic Mean`))) %>%
filter(avg_value > 10) %>%
arrange(month,day)
Q9
Question 10:
Which monitoring site in the dataset has the highest correlation between “Sulfate PM2.5 LC” and “Total Nitrate PM2.5 LC” across all dates? Identify the monitoring site by it’s State, County, and Site Number code.
For each of the chemical constituents, there will be some dates that have multiple Sample.Value’s at a monitoring site. When there are multiple values on a given date, take the average of the constituent values for that date.
Correlations between to variables can be computed with the cor() function.
Q10ANS <- mapply(Q10Function, Q10, numSites)
Error in filter_impl(.data, dots) : incorrect length (97), expecting: 1
LS0tDQp0aXRsZTogIlIgTm90ZWJvb2siDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCi0tLQ0KDQpodHRwczovL2dpdGh1Yi5jb20vcmluY2tkL21hdGgtY3JlYXRpdml0eS9ibG9iLzBiMzNmZGZiMjAwZTdhMjUyNGExNTRlOTdiOTc0OWJkMjE1NDcxYzkvcXVpel8xLlINCmh0dHBzOi8vZ2l0aHViLmNvbS9Ta2lsbFNtYXJ0L0NvdXJzZXJhX1NvZnR3YXJlRGV2ZWxvcG1lbnQtaW4tUi9ibG9iLzI2NDNiNzdhMThiOWZhOGIwNWQxMDQzZTdlNzFiZTVjOGRlYmJkNTEvUlByb2dyYW1taW5nRW52L1dlZWs0L1F1aXouUm1kDQpodHRwczovL2dpdGh1Yi5jb20veXVmcmVlL2RlbW9jb2RlL2Jsb2IvMGYzMDdmYzYxZTdiNjFkOTlhNjFiYTM3MDgyMzc4ZDAwODA1NTU4OS90aWR5dmVyc2UvaHcuUm1kDQoNCg0KDQojIyBNYXN0ZXJpbmcgUiBDbGFzcyAxOiAgRmluYWwgUXVpeg0KYGBge3IgbG9hZFBhY2thZ2VzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KHRpZHlyKQ0KbGlicmFyeShyZWFkeGwpDQpgYGANCg0KIyNJbnRyb2R1Y3Rpb24NClRoZSBnb2FsIG9mIHRoaXMgYXNzaWdubWVudCBpcyB0byB0YWtlIGRhdGFzZXRzIHRoYXQgYXJlIGVpdGhlciBtZXNzeSBvciBzaW1wbHkgbm90IHRpZHkgYW5kIHRvIG1ha2UgdGhlbSB0aWR5IGRhdGFzZXRzLiBUaGUgb2JqZWN0aXZlIGlzIHRvIGdhaW4gc29tZSBmYW1pbGlhcml0eSB3aXRoIHRoZSBmdW5jdGlvbnMgZm9yIHJlYWRpbmcgaW4gZGF0YSBpbnRvIFIgYW5kIGNhbGN1bGF0aW5nIGJhc2ljIHN1bW1hcnkgc3RhdGlzdGljcyBvbiB0aGUgZGF0YS4gSW4gcGFydGljdWxhciwgd2Ugd2lsbCBtYWtlIHVzZSBvZiB0aGUgZm9sbG93aW5nIHBhY2thZ2VzOiBkcGx5ciwgdGlkeXIsIHJlYWRyLCBhbmQgcmVhZHhsLiANCg0KQmVmb3JlIHN0YXJpbmcgdGhlIHF1aXogeW91IHdpbGwgbmVlZCB0byBkb3dubG9hZCB0aGUgZGF0YSBmb3IgdGhlIHF1aXosIHdoaWNoIGNhbiBiZSBmb3VuZCBpbiB0aGUgZmlsZSAqKnF1aXpfZGF0YS56aXAqKi4gVGhlIHppcCBhcmNoaXZlIGZpbGUgY29udGFpbnMgdHdvIGZpbGVzOg0KDQoqKmRhaWx5X1NQRUNfMjAxNC5jc3YuYnoyKio6IGEgY29tcHJlc3NlZCBDU1YgZmlsZSBjb250YWluaW5nIGRhaWx5IG1lYXN1cmVtZW50cyBvZiBwYXJ0aWN1bGF0ZSBtYXR0ZXIgY2hlbWljYWwgY29uc3RpdHVlbnRzIGluIHRoZSBVbml0ZWQgU3RhdGVzIGZvciB0aGUgeWVhciAyMDE0LiBOb3RlIHRoYXQgeW91IHNob3VsZCBOT1QgaGF2ZSB0byBkZWNvbXByZXNzIHRoaXMgZmlsZS4gVGhlIGRhdGEgYXJlIG1lYXN1cmVkIGF0IGEgbmV0d29yayBvZiBmZWRlcmFsLCBzdGF0ZSwgYW5kIGxvY2FsIG1vbml0b3JzIGFuZCBhc3NlbWJsZWQgYnkgdGhlIEVQQS4gSW4gdGhpcyBkYXRhc2V0LCB0aGUgKioiQXJpdGhtZXRpYyBNZWFuIiBjb2x1bW4gcHJvdmlkZXMgdGhlIGxldmVsIG9mIHRoZSBpbmRpY2F0ZWQgY2hlbWljYWwgY29uc3RpdHVlbnQqKiBhbmQgdGhlICJQYXJhbWV0ZXIuTmFtZSIgY29sdW1uIHByb3ZpZGVzIHRoZSBuYW1lIG9mIHRoZSBjaGVtaWNhbCBjb25zdGl0dWVudC4gVGhlIGNvbWJpbmF0aW9uIG9mIGEgIlN0YXRlIENvZGUiLCBhICJDb3VudHkgQ29kZSIsIGFuZCBhICJTaXRlIE51bSIsIHVuaXF1ZWx5IGlkZW50aWZpZXMgYSBtb25pdG9yaW5nIHNpdGUgKHRoZSBsb2NhdGlvbiBvZiB3aGljaCBpcyBwcm92aWRlZCBieSB0aGUgIkxhdGl0dWRlIiBhbmQgIkxvbmdpdHVkZSIgY29sdW1ucykuDQpgYGB7ciBsb2FkRGF0YTF9DQppZighZmlsZS5leGlzdHMoIi4vZGF0YS9wb2xsdXRpb24uY3N2Iikpew0KcG9sbHV0aW9uIDwtIHJlYWRfY3N2KCJkYXRhL2RhaWx5X1NQRUNfMjAxNC5jc3YuYnoyIikNCndyaXRlX2Nzdihwb2xsdXRpb24sICIuL2RhdGEvcG9sbHV0aW9uLmNzdiIpDQp9DQpwb2xsdXRpb24gPC0gcmVhZF9jc3YoIi4vZGF0YS9wb2xsdXRpb24uY3N2IikNCmBgYA0KDQoqKmFxc19zaXRlcy54bHN4Kio6IEFuIGV4Y2VsIHNwcmVhZHNoZWV0IGNvbnRhaW5pbmcgbWV0YWRhdGEgYWJvdXQgZWFjaCBvZiB0aGUgbW9uaXRvcmluZyBzaXRlcyBpbiB0aGUgVW5pdGVkIFN0YXRlcyB3aGVyZSBwb2xsdXRpb24gbWVhc3VyZW1lbnRzIGFyZSBtYWRlLiBJbiBwYXJ0aWN1bGFyLCB0aGUgIkxhbmQgVXNlIiBhbmQgIkxvY2F0aW9uIFNldHRpbmciIHZhcmlhYmxlcyBjb250YWluIGluZm9ybWF0aW9uIGFib3V0IHdoYXQga2luZHMgb2YgYXJlYXMgdGhlIG1vbml0b3JzIGFyZSBsb2NhdGVkIGluIChpLmUuIOKAnHJlc2lkZW50aWFs4oCdIHZzLiDigJxmb3Jlc3TigJ0pLg0KT25jZSB0aGUgZGF0YSBoYXZlIGJlZW4gZG93bmxvYWRlZCB0byB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LCB5b3UgY2FuIGJlZ2luIHRoZSBxdWl6IGFzc2lnbm1lbnQuIEZvciB0aGlzIGFzc2lnbm1lbnQsIHlvdSBtYXkgd2FudCB0byByZXZpZXcgU2VjdGlvbnMgMS4yIHRocm91Z2ggMS41IG9mIE1hc3RlcmluZyBTb2Z0d2FyZSBEZXZlbG9wbWVudCBpbiBSLg0KDQojIyAxLlF1ZXN0aW9uDQpVc2UgdGhlIHJlYWRyIHBhY2thZ2UgdG8gcmVhZCB0aGUgZGFpbHlfU1BFQ18yMDE0LmNzdi5iejIgZGF0YSBmaWxlIGluIHRvIFIuIFRoaXMgZmlsZSBjb250YWlucyBkYWlseSBsZXZlbHMgb2YgZmluZSBwYXJ0aWN1bGF0ZSBtYXR0ZXIgKFBNMi41KSBjaGVtaWNhbCBjb25zdGl0dWVudHMgYWNyb3NzIHRoZSBVbml0ZWQgU3RhdGVzLiBUaGUgZGF0YSBhcmUgbWVhc3VyZWQgYXQgYSBuZXR3b3JrIG9mIGZlZGVyYWwsIHN0YXRlLCBhbmQgbG9jYWwgbW9uaXRvcnMgYW5kIGFzc2VtYmxlZCBieSB0aGUgRVBBLg0KDQpJbiB0aGlzIGRhdGFzZXQsIHRoZSAiU2FtcGxlLlZhbHVlIiAoc2VlIGJvbGRlZCB3b3JkcyBhIGNvdXBsZSBvZiBwYXJhZ3JhcGhzIGJlZm9yZSkgY29sdW1uIHByb3ZpZGVzIHRoZSBsZXZlbCBvZiB0aGUgaW5kaWNhdGVkIGNoZW1pY2FsIGNvbnN0aXR1ZW50IGFuZCB0aGUgIlBhcmFtZXRlci5OYW1lIiBjb2x1bW4gcHJvdmlkZXMgdGhlIG5hbWUgb2YgdGhlIGNoZW1pY2FsIGNvbnN0aXR1ZW50LiBUaGUgY29tYmluYXRpb24gb2YgYSAiU3RhdGUuQ29kZSIsIGEgIkNvdW50eS5Db2RlIiwgYW5kIGEgIlNpdGUuTnVtIiwgdW5pcXVlbHkgaWRlbnRpZmllcyBhIG1vbml0b3Jpbmcgc2l0ZSAodGhlIGxvY2F0aW9uIG9mIHdoaWNoIGlzIHByb3ZpZGVkIGJ5IHRoZSAiTGF0aXR1ZGUiIGFuZCAiTG9uZ2l0dWRlIiBjb2x1bW5zKS4NCg0KRm9yIGFsbCBvZiB0aGUgcXVlc3Rpb25zIGJlbG93LCB5b3UgY2FuIGlnbm9yZSB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gdGhlIGRhdGFzZXQsIHNvIHdoZW4gdGFraW5nIGF2ZXJhZ2VzLCBqdXN0IHJlbW92ZSB0aGUgbWlzc2luZyB2YWx1ZXMgYmVmb3JlIHRha2luZyB0aGUgYXZlcmFnZSAoaS5lLiB5b3UgY2FuIHVzZSBuYS5ybSA9IFRSVUUgaW4gdGhlIG1lYW4oKSBmdW5jdGlvbikNCg0KKipXaGF0IGlzIGF2ZXJhZ2UgU2FtcGxlLlZhbHVlIGZvciAiQnJvbWluZSBQTTIuNSBMQyIgaW4gdGhlIHN0YXRlIG9mIFdpc2NvbnNpbiBpbiB0aGlzIGRhdGFzZXQ/KioNCmBgYHtyfQ0KUTEgPC0gcG9sbHV0aW9uICU+JSBmaWx0ZXIoYFBhcmFtZXRlciBOYW1lYD09ICJCcm9taW5lIFBNMi41IExDIiwgYFN0YXRlIE5hbWVgID09ICJXaXNjb25zaW4iKQ0KUTEgPC0gcG9sbHV0aW9uICU+JSBzdW1tYXJpc2UoYXZnID0gbWVhbihgQXJpdGhtZXRpYyBNZWFuYCwgbmEucm09VFJVRSkpDQpRMQ0KYGBgDQoNCiMjIDIuUXVlc3Rpb24NCkNhbGN1bGF0ZSB0aGUgYXZlcmFnZSBvZiBlYWNoIGNoZW1pY2FsIGNvbnN0aXR1ZW50IGFjcm9zcyBhbGwgc3RhdGVzLCBtb25pdG9yaW5nIHNpdGVzIGFuZCBhbGwgdGltZSBwb2ludHMuDQoNCioqV2hpY2ggY29uc3RpdHVlbnQgUGFyYW1ldGVyLk5hbWUgaGFzIHRoZSBoaWdoZXN0IGF2ZXJhZ2UgbGV2ZWw/KioNCmBgYHtyfQ0KUTIgPC0gIHBvbGx1dGlvbiAlPiUgDQogIGdyb3VwX2J5KGBQYXJhbWV0ZXIgTmFtZWAsYFN0YXRlIE5hbWVgLCBgU2l0ZSBOdW1gLCBgRGF0ZSBMb2NhbGApICU+JQ0KICAgICAgICBzdW1tYXJpc2UoYXZnID0gbWVhbihgQXJpdGhtZXRpYyBNZWFuYCkpICU+JQ0KICAgICAgICBhcnJhbmdlKGRlc2MoYXZnKSkNClEyDQpgYGANCg0KIyMgUXVlc3Rpb24gMw0KV2hpY2ggbW9uaXRvcmluZyBzaXRlIGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIGxldmVsIG9mICJTdWxmYXRlIFBNMi41IExDIiBhY3Jvc3MgYWxsIHRpbWU/DQoNCkluZGljYXRlIHRoZSBzdGF0ZSBjb2RlLCBjb3VudHkgY29kZSwgYW5kIHNpdGUgbnVtYmVyLg0KYGBge3J9DQpRM18xIDwtICBwb2xsdXRpb24gJT4lIGZpbHRlcihgUGFyYW1ldGVyIE5hbWVgID09ICJTdWxmYXRlIFBNMi41IExDIikgJT4lDQogICAgICAgIGdyb3VwX2J5KGBTaXRlIE51bWAsYENvdW50eSBDb2RlYCxgU3RhdGUgQ29kZWApICU+JQ0KICAgICAgICBzdW1tYXJpc2UoYXZnID0gbWVhbihgQXJpdGhtZXRpYyBNZWFuYCkpICU+JQ0KICAgICAgICBhcnJhbmdlKGRlc2MoYXZnKSkNClEzXzENCmBgYA0KDQpGaW5kIHRoZSBtZWFuIGNvbmNlbnRyYXRpb24gZm9yIGFsbCBwb2xsdXRhbnRzIG92ZXIgdGltZSBpbiBvbmUgQ2l0eQ0KYGBge3J9DQpRM18yIDwtIHBvbGx1dGlvbiAlPiUgZmlsdGVyKGBDaXR5IE5hbWVgID09ICdDaGFybG90dGUnKSAlPiUgc3VtbWFyaXNlKGF2ZyA9IG1lYW4oYEFyaXRobWV0aWMgTWVhbmApKQ0KUTNfMg0KYGBgDQoNCiMjIFF1ZXN0aW9uIDQNCldoYXQgaXMgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UgaW4gdGhlIGF2ZXJhZ2UgbGV2ZWxzIG9mICJFQyBQTTIuNSBMQyBUT1IiIGJldHdlZW4gdGhlIHN0YXRlcyBDYWxpZm9ybmlhIGFuZCBBcml6b25hLCBhY3Jvc3MgYWxsIHRpbWUgYW5kIGFsbCBtb25pdG9yaW5nIHNpdGVzPw0KYGBge3J9DQojIENhbGN1bGF0aW5nIHRoZSBQb2xsdXRhbnQgTWVhbnMgZm9yIENhbGlmb3JuaWEsIEFyaXpvbmENClE0IDwtIHBvbGx1dGlvbiAlPiUgZmlsdGVyKGBTdGF0ZSBOYW1lYCAlaW4lIGMoIkNhbGlmb3JuaWEiLCAiQXJpem9uYSIpLCBgUGFyYW1ldGVyIE5hbWVgPT0iRUMgUE0yLjUgTEMgVE9SIikgJT4lIA0KICBncm91cF9ieShgU3RhdGUgTmFtZWApICU+JSANCiAgc3VtbWFyaXNlKGF2Z19jb25jZW50cmF0aW9uID0gbWVhbihgQXJpdGhtZXRpYyBNZWFuYCkpDQoNCiMgQ2FsY3VsYXRpbmcgdGhlIGFic29sdXRlIGRpZmZlcmVuY2UNClE0IDwtIGFicyhRNFsxLDJdLVE0WzIsMl0pDQpRNA0KYGBgDQoNCiMjIFF1ZXN0aW9uIDUNCldoYXQgaXMgdGhlIG1lZGlhbiBsZXZlbCBvZiAiT0MgUE0yLjUgTEMgVE9SIiBpbiB0aGUgd2VzdGVybiBVbml0ZWQgU3RhdGVzLCBhY3Jvc3MgYWxsIHRpbWU/IERlZmluZSB3ZXN0ZXJuIGFzIGFueSBtb25pdG9yaW5nIGxvY2F0aW9uIHRoYXQgaGFzIGEgTG9uZ2l0dWRlIExFU1MgVEhBTiAtMTAwLg0KYGBge3J9DQpRNSA8LSBwb2xsdXRpb24gJT4lIGZpbHRlcihMb25naXR1ZGUgPCAtMTAwLCBgUGFyYW1ldGVyIE5hbWVgID09ICJPQyBQTTIuNSBMQyBUT1IiKSAlPiUgDQogIHN1bW1hcmlzZShtZWRfbGV2ZWwgPSBtZWRpYW4oYEFyaXRobWV0aWMgTWVhbmApKQ0KUTUNCmBgYA0KDQojIyBRdWVzdGlvbiA2DQpVc2UgdGhlIHJlYWR4bCBwYWNrYWdlIHRvIHJlYWQgdGhlIGZpbGUgKiphcXNfc2l0ZXMueGxzeCoqIGludG8gUiAoeW91IG1heSBuZWVkIHRvIGluc3RhbGwgdGhlIHBhY2thZ2UgZmlyc3QpLiBUaGlzIGZpbGUgY29udGFpbnMgbWV0YWRhdGEgYWJvdXQgZWFjaCBvZiB0aGUgbW9uaXRvcmluZyBzaXRlcyBpbiB0aGUgRVBBJ3MgbW9uaXRvcmluZyBzeXN0ZW0uIEluIHBhcnRpY3VsYXIsIHRoZSAiTGFuZCBVc2UiIGFuZCAiTG9jYXRpb24gU2V0dGluZyIgdmFyaWFibGVzIGNvbnRhaW4gaW5mb3JtYXRpb24gYWJvdXQgd2hhdCBraW5kcyBvZiBhcmVhcyB0aGUgbW9uaXRvcnMgYXJlIGxvY2F0ZWQgaW4gKGkuZS4gInJlc2lkZW50aWFsIiB2cy4gImZvcmVzdCIpLg0KYGBge3IgbG9hZERhdGEyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc2l0ZXMgPC0gcmVhZF9leGNlbCgiLi9kYXRhL2Fxc19zaXRlcy54bHN4IikNCmBgYA0KDQoqKkhvdyBtYW55IG1vbml0b3Jpbmcgc2l0ZXMgYXJlIGxhYmVsbGVkIGFzIGJvdGggUkVTSURFTlRJQUwgZm9yICJMYW5kIFVzZSIgYW5kIFNVQlVSQkFOIGZvciAiTG9jYXRpb24gU2V0dGluZyI/KioNCg0KYGBge3J9DQpRNiA8LSBzaXRlcyAlPiUgZmlsdGVyKGBMYW5kIFVzZWAgPT0gIlJFU0lERU5USUFMIiwgYExvY2F0aW9uIFNldHRpbmdgID09IlNVQlVSQkFOIikgJT4lIHN1bW1hcmlzZShuKCkpDQpRNg0KYGBgDQoNCiMjIFF1ZXN0aW9uIDcNCldoYXQgaXMgdGhlIG1lZGlhbiBsZXZlbCBvZiAiRUMgUE0yLjUgTEMgVE9SIiBhbW9uZ3N0IG1vbml0b3Jpbmcgc2l0ZXMgdGhhdCBhcmUgbGFiZWxsZWQgYXMgYm90aCAiUkVTSURFTlRJQUwiIGFuZCAiU1VCVVJCQU4iIGluIHRoZSBlYXN0ZXJuIFUuUy4sIHdoZXJlIGVhc3Rlcm4gaXMgZGVmaW5lZCBhcyBMb25naXR1ZGUgZ3JlYXRlciB0aGFuIG9yIGVxdWFsIHRvIC0xMDA/DQoNCkluIG9yZGVyIHRvIGpvaW4gdGhlIHRhYmxlcyB3ZSBoYXZlIHRvIHNldCB0aGUgY29kZXMgdGhlIHNhbWUNCmBgYHtyfQ0KIyBDaGVjayBpZiB0aGUgaWRlbnRpZmllcnMgYXJlIHRoZSBzYW1lDQpuYW1lcyhwb2xsdXRpb24pWzE6NV0gPT0gbmFtZXMoc2l0ZXMpWzE6NV0NCnN0cihwb2xsdXRpb25bMTo1XSkNCnN0cihzaXRlc1sxOjVdKQ0KYGBgDQpOZWVkIHRvIGNoYW5nZSBhIGZldyB2YXJpYWJsZXMgaW4gdGhlIHBvbGx1dGlvbiBkYXRhDQpgYGB7cn0NCnBvbGx1dGlvbiRgU3RhdGUgQ29kZWAgPC0gYXMubnVtZXJpYyhwb2xsdXRpb24kYFN0YXRlIENvZGVgKQ0KcG9sbHV0aW9uJGBDb3VudHkgQ29kZWAgPC0gYXMubnVtZXJpYyhwb2xsdXRpb24kYENvdW50eSBDb2RlYCkNCnBvbGx1dGlvbiRgU2l0ZSBOdW1gIDwtIGFzLm51bWVyaWMocG9sbHV0aW9uJGBTaXRlIE51bWApI05lZWQgdG8gbWFrZSB0aGUgY29sdW1ucyB0aGUgc2FtZT8gIEkgdGhpbmsgc28uICBDaGVjayBpZiBhbnMgaXMgd3JvbmcNCmBgYA0KTm93IHlvdSBjYW4gam9pbiB0aGUgdHdvIERGcyBvbiAoU3RhdGUsIENvdW50eSwgU2l0ZUlkKQ0KYGBge3J9DQpqb2luZWRfdGJsIDwtIGlubmVyX2pvaW4oc2l0ZXMsIHBvbGx1dGlvbikNCnN1bW1hcnkoam9pbmVkX3RibCRgQXJpdGhtZXRpYyBNZWFuYCkNCg0KUTcgPC0gam9pbmVkX3RibCAlPiUgDQogIGdyb3VwX2J5KGBTdGF0ZSBDb2RlYCwgYENvdW50eSBDb2RlYCxgUGFyYW1ldGVyIE5hbWVgKSAlPiUgDQogIHN1bW1hcmlzZShhdmdfY29uY2VudHJhdGlvbiA9IG1lYW4oYEFyaXRobWV0aWMgTWVhbmApKSAlPiUgDQogIGFycmFuZ2UoZGVzYyhhdmdfY29uY2VudHJhdGlvbikpDQpgYGANCg0KQ2FsY3VsYXRpbmcgdGhlIG1lZGlhbiBsZXZlbA0KYGBge3J9DQpRNyA8LSBqb2luZWRfdGJsICU+JSBncm91cF9ieShgU3RhdGUgQ29kZWAsIGBDb3VudHkgQ29kZWAsIGBTaXRlIE51bWJlcmAsIGBQYXJhbWV0ZXIgTmFtZWApICU+JQ0KICAgICBmaWx0ZXIoYExvY2F0aW9uIFNldHRpbmdgPT0gIlNVQlVSQkFOIiwgYExhbmQgVXNlYD09IlJFU0lERU5USUFMIiwgDQogICAgICAgICAgICBgUGFyYW1ldGVyIE5hbWVgPT0gIkVDIFBNMi41IExDIFRPUiIsIExvbmdpdHVkZSA+PSAtMTAwKSAlPiUNCiAgICAgc3VtbWFyaXNlKGF2Z19jb25jZW50cmF0aW9uID0gbWVhbihgQXJpdGhtZXRpYyBNZWFuYCkpDQpRNw0KYGBgDQoNCiMjIFF1ZXN0aW9uIDgNCkFtb25nc3QgbW9uaXRvcmluZyBzaXRlcyB0aGF0IGFyZSBsYWJlbGVkIGFzIENPTU1FUkNJQUwgZm9yICJMYW5kIFVzZSIsIHdoaWNoIG1vbnRoIG9mIHRoZSB5ZWFyIGhhcyB0aGUgaGlnaGVzdCBhdmVyYWdlIGxldmVscyBvZiAiU3VsZmF0ZSBQTTIuNSBMQyI/DQoNCmBgYHtyfQ0KbGlicmFyeShsdWJyaWRhdGUpDQpROCA8LSBqb2luZWRfdGJsICU+JSBmaWx0ZXIoYExhbmQgVXNlYD09IkNPTU1FUkNJQUwiLCBgUGFyYW1ldGVyIE5hbWVgPT0iU3VsZmF0ZSBQTTIuNSBMQyIpICU+JQ0KICAgICBncm91cF9ieShtb250aChgRGF0ZSBMb2NhbGApKSAlPiUgDQogICAgIHN1bW1hcmlzZShhdmdfY29uY2VudHJhdGlvbiA9IG1lYW4oYEFyaXRobWV0aWMgTWVhbmApKSAlPiUNCiAgICAgYXJyYW5nZShkZXNjKGF2Z19jb25jZW50cmF0aW9uKSkNClE4DQpgYGANCg0KIyMgUXVlc3Rpb24gOToNClRha2UgYSBsb29rIGF0IHRoZSBkYXRhIGZvciB0aGUgbW9uaXRvcmluZyBzaXRlIGlkZW50aWZpZWQgYnkgU3RhdGUgQ29kZSA2LCBDb3VudHkgQ29kZSA2NSwgYW5kIFNpdGUgTnVtYmVyIDgwMDEgKHRoaXMgbW9uaXRvciBpcyBpbiBDYWxpZm9ybmlhKS4gQXQgdGhpcyBtb25pdG9yLCAqKmZvciBob3cgbWFueSBkYXlzIGlzIHRoZSBzdW0gb2YgIlN1bGZhdGUgUE0yLjUgTEMiIGFuZCAiVG90YWwgTml0cmF0ZSBQTTIuNSBMQyIgZ3JlYXRlciB0aGFuIDEwPyoqDQoNCkZvciBlYWNoIG9mIHRoZSBjaGVtaWNhbCBjb25zdGl0dWVudHMsIHRoZXJlIHdpbGwgYmUgc29tZSBkYXRlcyB0aGF0IGhhdmUgbXVsdGlwbGUgU2FtcGxlLlZhbHVlJ3MgYXQgdGhpcyBtb25pdG9yaW5nIHNpdGUuIFdoZW4gdGhlcmUgYXJlIG11bHRpcGxlIHZhbHVlcyBvbiBhIGdpdmVuIGRhdGUsIHRha2UgdGhlIGF2ZXJhZ2Ugb2YgdGhlIGNvbnN0aXR1ZW50IHZhbHVlcyBmb3IgdGhhdCBkYXRlLg0KDQpgYGB7cn0NClE5IDwtIGpvaW5lZF90YmwgJT4lIA0KICBmaWx0ZXIoYFN0YXRlIENvZGVgPT02LCBgQ291bnR5IENvZGVgPT02NSwgYFNpdGUgTnVtYmVyYD09ODAwMSwgYFBhcmFtZXRlciBOYW1lYCAlaW4lIGMoIlRvdGFsIE5pdHJhdGUgUE0yLjUgTEMiLCAiU3VsZmF0ZSBQTTIuNSBMQyIpKSAlPiUgDQogIGdyb3VwX2J5KGBTaXRlIE51bWJlcmAsIG1vbnRoID0gbW9udGgoYERhdGUgTG9jYWxgKSxgUGFyYW1ldGVyIE5hbWVgLCBkYXkgPSBkYXkoYERhdGUgTG9jYWxgKSApICU+JSANCiAgc3VtbWFyaXNlKGF2Z192YWx1ZSA9IHN1bShtZWFuKGBBcml0aG1ldGljIE1lYW5gKSkpICU+JSANCiAgZmlsdGVyKGF2Z192YWx1ZSA+IDEwKSAlPiUgDQogIGFycmFuZ2UobW9udGgsZGF5KSANClE5DQpgYGANCg0KUXVlc3Rpb24gMTA6DQoNCioqV2hpY2ggbW9uaXRvcmluZyBzaXRlIGluIHRoZSBkYXRhc2V0IGhhcyB0aGUgaGlnaGVzdCBjb3JyZWxhdGlvbiBiZXR3ZWVuICJTdWxmYXRlIFBNMi41IExDIiBhbmQgIlRvdGFsIE5pdHJhdGUgUE0yLjUgTEMiIGFjcm9zcyBhbGwgZGF0ZXM/KiogSWRlbnRpZnkgdGhlIG1vbml0b3Jpbmcgc2l0ZSBieSBpdCdzIFN0YXRlLCBDb3VudHksIGFuZCBTaXRlIE51bWJlciBjb2RlLg0KDQpGb3IgZWFjaCBvZiB0aGUgY2hlbWljYWwgY29uc3RpdHVlbnRzLCB0aGVyZSB3aWxsIGJlIHNvbWUgZGF0ZXMgdGhhdCBoYXZlIG11bHRpcGxlIFNhbXBsZS5WYWx1ZSdzIGF0IGEgbW9uaXRvcmluZyBzaXRlLiBXaGVuIHRoZXJlIGFyZSBtdWx0aXBsZSB2YWx1ZXMgb24gYSBnaXZlbiBkYXRlLCB0YWtlIHRoZSBhdmVyYWdlIG9mIHRoZSBjb25zdGl0dWVudCB2YWx1ZXMgZm9yIHRoYXQgZGF0ZS4NCg0KQ29ycmVsYXRpb25zIGJldHdlZW4gdG8gdmFyaWFibGVzIGNhbiBiZSBjb21wdXRlZCB3aXRoIHRoZSBjb3IoKSBmdW5jdGlvbi4NCg0KYGBge3J9DQpRMTAgPC0gam9pbmVkX3RibCAlPiUgZmlsdGVyKGBQYXJhbWV0ZXIgTmFtZWAgJWluJSBjKCJUb3RhbCBOaXRyYXRlIFBNMi41IExDIiwgIlN1bGZhdGUgUE0yLjUgTEMiKSkgJT4lDQogICAgIGdyb3VwX2J5KGBTaXRlIE51bWJlcmAsIG1vbnRoID0gbW9udGgoYERhdGUgTG9jYWxgKSwgZGF5ID0gZGF5KGBEYXRlIExvY2FsYCksIGBQYXJhbWV0ZXIgTmFtZWApICU+JQ0KICAgICBzdW1tYXJpc2UoYXZnX3ZhbHVlID0gbWVhbihgQXJpdGhtZXRpYyBNZWFuYCkpICU+JQ0KICAgICBzcHJlYWQoYFBhcmFtZXRlciBOYW1lYCwgYXZnX3ZhbHVlKQ0KDQpRMTBGdW5jdGlvbiA8LSBmdW5jdGlvbihERiwgU2l0ZU51bWJlcil7DQogICAgIGEgPC0gZmlsdGVyKFExMCwgYFNpdGUgTnVtYmVyYCA9PSBTaXRlTnVtYmVyKQ0KICAgICB4IDwtIGFbLDRdDQogICAgIHkgPC0gYVs1XQ0KICAgICB0bXBERiA8LSBjb3IoeCx5KQ0KICAgICByZXR1cm4odG1wREYpDQp9DQoNCm51bVNpdGVzIDwtIGFzLmRhdGEuZnJhbWUoUTEwKSAlPiUgc2VsZWN0KGBTaXRlIE51bWJlcmApICU+JSBkaXN0aW5jdCgpDQphbnNERiA8LSBkYXRhLmZyYW1lKCkNCmZvcihpIGluIDE6bnJvdyhudW1TaXRlcykpew0KICAgICB0bXBERiA8LSBRMTBGdW5jdGlvbihRMTAsIG51bVNpdGVzW2ksMV0pDQogICAgIGFuc0RGIDwtIHJiaW5kKGFuc0RGLCB0bXBERikNCn0NCg0Kam9pbmVkX3RibCAlPiUgZmlsdGVyKGBTdGF0ZSBDb2RlYD09NiwgYENvdW50eSBDb2RlYD09NjUsIGBTaXRlIE51bWJlcmA9PTgwMDEsIA0KICAgICBgUGFyYW1ldGVyIE5hbWVgICVpbiUgYygiVG90YWwgTml0cmF0ZSBQTTIuNSBMQyIsICJTdWxmYXRlIFBNMi41IExDIikpICU+JSANCiAgICAgZ3JvdXBfYnkoYFNpdGUgTnVtYmVyYCwgbW9udGggPSBtb250aChgRGF0ZSBMb2NhbGApLGRheSA9IGRheShgRGF0ZSBMb2NhbGApLCBgUGFyYW1ldGVyIE5hbWVgICkgJT4lDQogICAgIHN1bW1hcmlzZShhdmdfdmFsdWUgPSBtZWFuKGBBcml0aG1ldGljIE1lYW5gKSkgJT4lDQogICAgIHNwcmVhZChgUGFyYW1ldGVyIE5hbWVgLCBhdmdfdmFsdWUpDQogIA0Kc3VtbWFyaXNlKGNvcnJlbGF0aW9uID0gY29yKHg9YFRvdGFsIE5pdHJhdGUgUE0yLjUgTENgLCB5PWBTdWxmYXRlIFBNMi41IExDYCwgdXNlPSJwYWlyd2lzZS5jb21wbGV0ZS5vYnMiKSkNCmBgYA0KDQoNCg==