Preparation Instructions

THE DATASETS

For this Challenge Problem assignment, you are going to be using dataset, titled OrdwayBirds, on Minnesota bird species. This data was collected as part of a historical record of birds captured and released at a nature preserve in Inver Grove Heights, Minnesota (i.e., the Katharine Ordway Natural History Study Area, which is owned and managed by Macalester College in St. Paul, Minnesota). Originally written by hand in a field notebook, the entries have been transcribed into electronic format under the supervision of Jerald Dosch, Dept. of Biology, Macalester College.1

Due to mistakes in data entry, the SpeciesName variable in the OrdwayBirds dataset needs some fixing. SpeciesName is intended to identify the species of each of the birds, but the spelling often varies among birds of the same biological species. This leads to misclassification of birds.

Fortunately, this error is easy to fix. A different dataset, titled OrdwaySpeciesNames, was created to take into account all of the original variations in the spelling of the species names (SpeciesName) and translate them into a unified spelling (SpeciesNameCleaned). That is, this other dataset provides a cross-reference between the original spelling and a common or more appropriate one.

The information from the two datasets, OrdwayBirds and OrdwaySpeciesNames, can be merged using a join_function() to correct the original spellings and then carry out further explorations of the Minnesota birds dataset.

EXERCISES

Practice Problems

Now let’s practice joining tables and describing the result of the specific join_function().

  1. Perform a left_join() between the OrdwayBirds dataset and the OrdwaySpeciesNames dataset, saving it to a new object (so all the rows aren’t knitted to your HTML), and examine the dimensions of the new merged dataset.
OrdwayBirds_merged <- OrdwayBirds %>%
  left_join(OrdwaySpeciesNames, by = "SpeciesName")
## Warning in left_join(., OrdwaySpeciesNames, by = "SpeciesName"): Detected an unexpected many-to-many relationship between `x` and `y`.
## ℹ Row 4 of `x` matches multiple rows in `y`.
## ℹ Row 211 of `y` matches multiple rows in `x`.
## ℹ If a many-to-many relationship is expected, set `relationship =
##   "many-to-many"` to silence this warning.
dim(OrdwayBirds)
## [1] 15829    26
dim(OrdwaySpeciesNames)
## [1] 265   2
dim(OrdwayBirds_merged)
## [1] 17120    27
  1. Describe what modifications were made when the two datasets were joined together into one using this join function (e.g., row changes? column changes?). Also, did the left_join() do what you thought it would do? Explain.
  • Before the datasets were merged, the OrdwayBirds dataset had 15,829 observations and 26 variables for the names of bird species. On the other hand, in the OrdwaySpeciesName dataset, there were only 265 observations and two variables. After merging the datasets using the join function, there were 17,120 observations and 27 variables. Because the datasets were joined, the ‘SpeciesNameCleaned’ variable was included in the merged dataset, thereby increasing the number of variables. Additionally, because the OrdwayBirds dataset contained some ‘SpeciesName’ observations that matched multiple rows in the OrdwaySpeciesName dataset, the merged dataset also had more observations. Therefore, the left_join() function performed as expected.

[Hint: Are the number of rows in the OrdwayBirds dataset the same as your new merged dataset?]

  1. Modify the OrdwaySpeciesNames dataset so that only the unique/distinct rows are in the dataset. Save this new dataset in an object called sn.
sn <- OrdwaySpeciesNames %>%
  distinct()
  1. Perform the left_join() again, but this time between the OrdwayBirds dataset and the sn dataset, saving it to a new object, and examine the dimensions of the new merged dataset. Is this new merged dataset what you would expect from doing a left_join()? Explain.
  • After evaluating the new merged dataset, this is what is expected when using a left_join function. The OrdwayBirds dataset contained 15,829 observations and 26 variables. After merging the datasets using the left_join function, every row from the OrdwayBirds dataset was kept, and each bird species was matched to only one unique row in the ‘sn’ dataset. Therefore, the merged dataset had the same number of observations as the OrdwayBirds dataset. Furthermore, because the ‘SpeciesNameCleaned’ variable from the ‘sn’ dataset was included in the new merged dataset, the dataset now had 27 variables.
sn_OrdwayBirds <- OrdwayBirds %>%
  left_join(sn, by = "SpeciesName")


dim(sn)
## [1] 251   2
view(sn)
dim(OrdwayBirds)
## [1] 15829    26
dim(sn_OrdwayBirds)
## [1] 15829    27
  1. Perform an inner_join() between the OrdwayBirds dataset and the sn dataset, saving it to a new object. Examine the dimensions of the new merged dataset. Describe what modifications were made when the two datasets were joined together into one (e.g., row changes? column changes?).
  • When using the inner_join function, the ‘SpeciesName’ observations that match both in the OrdwayBirds and ‘sn’ datasets are kept. Therefore, after merging the two datasets, the number of observations (rows) decreased from 15,829 to 15,724. The inner_join function also adds any additional variable (column) from the ‘sn’ dataset. Therefore, the merged dataset, which has 27 variables, includes the 26 variables from the OrdwayBirds dataset and an additional ‘SpeciesNameCleaned’ variable from the ‘sn’ dataset.
OrdwayBirds_inner <- OrdwayBirds %>%
  inner_join(sn, by = "SpeciesName")

dim(OrdwayBirds)
## [1] 15829    26
dim(OrdwayBirds_inner)
## [1] 15724    27
  1. Perform a full_join() between the OrdwayBirds dataset and the sn dataset, saving it to a new object. Examine the dimensions of the new merged dataset. Describe what modifications were made when the two datasets were joined together into one (e.g., row changes? column changes?).
  • When using the full_join function, all ‘SpeciesName’ observations from both the OrdwayBirds and ‘sn’ datasets are kept. After merging the datasets, the number of observations (rows) increased slightly from 15,829 to 15,830 because the ‘sn’ dataset included an additional bird species that was not present in the OrdwayBirds dataset. The full_join function also adds any additional variable (column) from the ‘sn’ dataset. Therefore, the merged dataset, which has 27 variables, includes the 26 variables from the OrdwayBirds dataset and an additional ‘SpeciesNameCleaned’ variable from the ‘sn’ dataset.
OrdwayBirds_full <- OrdwayBirds %>% 
  full_join(sn, by = "SpeciesName")


dim(OrdwayBirds)
## [1] 15829    26
dim(OrdwayBirds_full)
## [1] 15830    27
  1. Perform a semi_join() between the OrdwayBirds dataset and the sn dataset, saving it to a new object. Examine the dimensions of the new merged dataset. Describe what modifications were made when the two datasets were joined together into one (e.g., row changes? column changes?).
  • When using the semi_join function, only the ‘SpeciesName’ observations in the OrdwayBirds dataset that also match in the ‘sn’ dataset are kept. After merging the two datasets, the number of observations (rows) decreased from 15,829 to 15,724 because some of the ‘SpeciesName’ observations in the OrdwayBirds dataset did not match with the ‘sn’ dataset. The semi_join function only keeps the variables (columns) from the OrdwayBirds dataset; therefore, the merged dataset has only 26 variables.
OrdwayBirds_semi <- OrdwayBirds %>%
  semi_join(sn, by = "SpeciesName")


dim(OrdwayBirds)
## [1] 15829    26
dim(OrdwayBirds_semi)
## [1] 15724    26
  1. Perform an anti_join() between the OrdwayBirds dataset and the sn dataset, saving it to a new object. Examine the dimensions of the new merged dataset. Describe what modifications were made when the two datasets were joined together into one (e.g., row changes? column changes?).
  • When using the anti_join function, only the ‘SpeciesName’ observations in the Ordway dataset that do not match any ‘SpeciesName’ observations in the ‘sn’ dataset are kept. Therefore, after merging the two datasets, the number of observations (rows) decreased from 15,829 to 105. The anti_join function only keeps the variables (columns) from the OrdwayBirds dataset; therefore, the merged dataset has only 26 variables.
OrdwayBirds_anti <- OrdwayBirds %>% 
  anti_join(sn, by = "SpeciesName")


dim(OrdwayBirds)
## [1] 15829    26
dim(OrdwayBirds_anti)
## [1] 105  26

Putting It All Together

Now let’s put the joins to use (in addition to other data verbs and data visualization) to answer a question.

What is month-to-month presence of the most common bird species in the Ordway nature preserve area?

Think of this assignment as creating a resource for birders on the ideal time of year to visit Ordway to see a particular species.

In addition to the errors in SpeciesName, there are also problems with the Month and Day variables. They are supposed to be numerical, but mistakes prevent them from being correctly identified as such. The following code will take care of this issue with Month and Day: [Note: Take out eval=FALSE in the options of the code chunk so that the code executes in your assignment.]

birds <- OrdwayBirds %>%
  mutate(Month = as.numeric(as.character(Month)), 
        Day = as.numeric(as.character(Day)))

The next set of questions are going to walk you through the process of how to explore the data to answer the question.

  1. Including misspellings, how many different species are there in the (newly created) birds dataset?
birds %>% 
  distinct(SpeciesName) %>%
  count()
  1. How many distinct species are there in the SpeciesNameCleaned variable in sn dataset?
sn %>% 
  distinct(SpeciesNameCleaned) %>%
  count()
  1. Perform the appropriate join function to merge the birds dataset with the sn dataset, such that only the matching rows from sn and birds are included and all columns from birds and sn are included. Also use the na.omit() function to remove missing data from your dataset (Note: This will remove all rows with ANY NAs). Save this merged dataset to a new object called birds_sn.
birds_sn <- birds %>%
  inner_join(sn, by = "SpeciesName") %>%
  na.omit()
  1. How many bird captures are reported for each of the (correct) species? Be sure to arrange the data in descending order from the species with the most birds to the least.
birds_sn %>%
  count(SpeciesNameCleaned) %>%
  arrange(desc(n))
  1. Let’s keep only the the most common bird species captured. Create a new dataset of counts similar to the answer from the previous question in an object called top_species that contains only the top 6 most common bird species.
top_species <- birds_sn %>%
  count(SpeciesNameCleaned, sort = TRUE) %>%
  head(6)

top_species
  1. Now let’s create the final dataset to help answer the question. Create a new dataset in an object called top_species_month that contains the top species and a month-by-month count of each of the most common species. (Hint: use a specific type of join to limit the birds_sn entries to only the birds included in the top_species dataset, then count the number of sightings by species and month).
top_species_month <- birds_sn %>%
  semi_join(top_species, by = "SpeciesNameCleaned") %>%
  group_by(SpeciesNameCleaned, Month) %>%
  count() %>%
  arrange(SpeciesNameCleaned, Month)
  1. Create a data visualization that helps to answer the question. Be sure to take into account meaningful design elements as you create your plot.
top_species_month %>%
  ggplot(aes(x = factor(Month), y = n, group = SpeciesNameCleaned, color = SpeciesNameCleaned)) +
  geom_line() + 
  facet_wrap(~SpeciesNameCleaned) + 
  theme_bw() + 
  theme(legend.position = "none",
        strip.text.x = element_text(size = 6),
        axis.text = element_text(size = 6),
        axis.title = element_text(size = 12),
        plot.title = element_text(hjust = 0.5)) + 
  labs(x = "Month", y = "# of Captures", title = "Most Common Bird Species: Month by Month Count ") 

  1. Use the data visualization to answer these questions for the birders:

    1. Which most common species are present year-round?
    • Tree Swallow
    1. Which species are migratory, that is, primarily present in one or two seasons?
    • The Field Sparrow species was only primarily present in one season while the American Goldfinch, Black-capped Chickadee, Robin, and State-Colored Junco species are primarily present in two seasons.
    1. Which is the peak month for each major species?
    • American Goldfinch: May and October
    • Back-capped Chickadee: February and November
    • Field Sparrow: May
    • Robin: April - May
    • State-colored Junco: April and October
    • Tree Swallow: March, June, and November

  1. Source: Data Computing textbook Project: Bird Species.↩︎

LS0tCnRpdGxlOiAiV2VlayAxMCBDaGFsbGVuZ2UgUHJvYmxlbSIKYXV0aG9yOiAiQmlsc3VtYSBBZGVtYSIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgojIyMgUHJlcGFyYXRpb24gSW5zdHJ1Y3Rpb25zCgotICAgQmUgc3VyZSB0byBjaGFuZ2UgdGhlIGF1dGhvciBpbiB0aGUgWUFNTCB0byB5b3VyIG5hbWUuIFJlbWVtYmVyIHRvIGtlZXAgaXQgaW5zaWRlIHRoZSBxdW90ZXMuCgotICAgUXVlc3Rpb25zIHRoYXQgcmVxdWlyZSB0aGUgdXNlIG9mIFIgd2lsbCBoYXZlIGFuIFIgY29kZSBjaHVuayBiZWxvdyBpdC4KCi0gICBEb3dubG9hZCB0aGUgZGF0YXNldHMgYWJvdXQgYmlyZHMgKHRpdGxlZCAqT3Jkd2F5QmlyZHMuY3N2KiBhbmQgKk9yZHdheVNwZWNpZXNOYW1lcy5jc3YqKSBmcm9tIHRoZSBDYW52YXMgcGFnZSBmb3IgdGhpcyBhc3NpZ25tZW50IGFuZCBzYXZlIHRoZXNlIGZpbGVzIHRvIHRoZSBmb2xkZXIgd2hlcmUgdGhlIFJNRCBmaWxlIGlzIGxvY2F0ZWQuCgotIFRoZXJlIGlzIGxlc3MgaGFuZC1ob2xkaW5nIGluIHRoaXMgYXNzaWdubWVudC4KICAgIC0gVGhlIHBhY2thZ2VzIHRoYXQgbmVlZCB0byBiZSBsb2FkZWQgd2lsbCBubyBsb25nZXIgYmUgcHJvdmlkZWQgaW4gdGhlIHNldHVwIGNvZGUgY2h1bmsuIFlvdSBhcmUgcmVzcG9uc2libGUgZm9yIGxvYWRpbmcgdGhlIGFwcHJvcHJpYXRlIHBhY2thZ2VzIGZvciB0aGUgYXNzaWdubWVudC4KICAgIC0gUXVlc3Rpb25zIG9uIGV4YW1pbmluZyB0aGUgZGF0YSB3aWxsIG5vIGxvbmdlciBiZSBwcm92aWRlZC4gWW91IGFyZSByZXNwb25zaWJsZSBmb3IgZ2V0dGluZyBhIHNlbnNlIG9mIHRoZSBkYXRhIHByaW9yIHRvIGNvbXBsZXRpbmcgdGhlIGFuYWx5c2VzLiAKCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCgpPcmR3YXlCaXJkcyA8LSByZWFkLmNzdigiL1VzZXJzL2JpbHN1bWFhZGVtYS9Eb2N1bWVudHMvUiBDb3Vyc2UgQ2xhc3MgMjAyNS9PcmR3YXlCaXJkcy5jc3YiLCBoZWFkZXIgPSBUUlVFKQpPcmR3YXlTcGVjaWVzTmFtZXMgPC0gcmVhZC5jc3YoIi9Vc2Vycy9iaWxzdW1hYWRlbWEvRG9jdW1lbnRzL1IgQ291cnNlIENsYXNzIDIwMjUvT3Jkd2F5U3BlY2llc05hbWVzLmNzdiIsIGhlYWRlciA9IFRSVUUpCgpgYGAKCiMjIyMgVEhFIERBVEFTRVRTCgpGb3IgdGhpcyBDaGFsbGVuZ2UgUHJvYmxlbSBhc3NpZ25tZW50LCB5b3UgYXJlIGdvaW5nIHRvIGJlIHVzaW5nIGRhdGFzZXQsIHRpdGxlZCAqT3Jkd2F5QmlyZHMqLCBvbiBNaW5uZXNvdGEgYmlyZCBzcGVjaWVzLiBUaGlzIGRhdGEgd2FzIGNvbGxlY3RlZCBhcyBwYXJ0IG9mIGEgaGlzdG9yaWNhbCByZWNvcmQgb2YgYmlyZHMgY2FwdHVyZWQgYW5kIHJlbGVhc2VkIGF0IGEgbmF0dXJlIHByZXNlcnZlIGluIEludmVyIEdyb3ZlIEhlaWdodHMsIE1pbm5lc290YSAoaS5lLiwgdGhlIEthdGhhcmluZSBPcmR3YXkgTmF0dXJhbCBIaXN0b3J5IFN0dWR5IEFyZWEsIHdoaWNoIGlzIG93bmVkIGFuZCBtYW5hZ2VkIGJ5IE1hY2FsZXN0ZXIgQ29sbGVnZSBpbiBTdC4gUGF1bCwgTWlubmVzb3RhKS4gT3JpZ2luYWxseSB3cml0dGVuIGJ5IGhhbmQgaW4gYSBmaWVsZCBub3RlYm9vaywgdGhlIGVudHJpZXMgaGF2ZSBiZWVuIHRyYW5zY3JpYmVkIGludG8gZWxlY3Ryb25pYyBmb3JtYXQgdW5kZXIgdGhlIHN1cGVydmlzaW9uIG9mIEplcmFsZCBEb3NjaCwgRGVwdC4gb2YgQmlvbG9neSwgTWFjYWxlc3RlciBDb2xsZWdlLlteMV0KClteMV06IFNvdXJjZTogW0RhdGEgQ29tcHV0aW5nIHRleHRib29rIFByb2plY3Q6IEJpcmQgU3BlY2llc10oaHR0cHM6Ly9kdGthcGxhbi5naXRodWIuaW8vRGF0YUNvbXB1dGluZ0Vib29rL3Byb2plY3QtYmlyZC1zcGVjaWVzLmh0bWwjcHJvamVjdC1iaXJkLXNwZWNpZXMpLgoKRHVlIHRvIG1pc3Rha2VzIGluIGRhdGEgZW50cnksIHRoZSAqU3BlY2llc05hbWUqIHZhcmlhYmxlIGluIHRoZSAqT3Jkd2F5QmlyZHMqIGRhdGFzZXQgbmVlZHMgc29tZSBmaXhpbmcuICpTcGVjaWVzTmFtZSogaXMgaW50ZW5kZWQgdG8gaWRlbnRpZnkgdGhlIHNwZWNpZXMgb2YgZWFjaCBvZiB0aGUgYmlyZHMsIGJ1dCB0aGUgc3BlbGxpbmcgb2Z0ZW4gdmFyaWVzIGFtb25nIGJpcmRzIG9mIHRoZSBzYW1lIGJpb2xvZ2ljYWwgc3BlY2llcy4gVGhpcyBsZWFkcyB0byBtaXNjbGFzc2lmaWNhdGlvbiBvZiBiaXJkcy4gCgpGb3J0dW5hdGVseSwgdGhpcyBlcnJvciBpcyBlYXN5IHRvIGZpeC4gQSBkaWZmZXJlbnQgZGF0YXNldCwgdGl0bGVkICpPcmR3YXlTcGVjaWVzTmFtZXMqLCB3YXMgY3JlYXRlZCB0byB0YWtlIGludG8gYWNjb3VudCBhbGwgb2YgdGhlIG9yaWdpbmFsIHZhcmlhdGlvbnMgaW4gdGhlIHNwZWxsaW5nIG9mIHRoZSBzcGVjaWVzIG5hbWVzICgqU3BlY2llc05hbWUqKSBhbmQgdHJhbnNsYXRlIHRoZW0gaW50byBhIHVuaWZpZWQgc3BlbGxpbmcgKCpTcGVjaWVzTmFtZUNsZWFuZWQqKS4gVGhhdCBpcywgdGhpcyBvdGhlciBkYXRhc2V0IHByb3ZpZGVzIGEgY3Jvc3MtcmVmZXJlbmNlIGJldHdlZW4gdGhlIG9yaWdpbmFsIHNwZWxsaW5nIGFuZCBhIGNvbW1vbiBvciBtb3JlIGFwcHJvcHJpYXRlIG9uZS4gCgpUaGUgaW5mb3JtYXRpb24gZnJvbSB0aGUgdHdvIGRhdGFzZXRzLCAqT3Jkd2F5QmlyZHMqIGFuZCAqT3Jkd2F5U3BlY2llc05hbWVzKiwgY2FuIGJlIG1lcmdlZCB1c2luZyBhIGBqb2luX2Z1bmN0aW9uKClgIHRvIGNvcnJlY3QgdGhlIG9yaWdpbmFsIHNwZWxsaW5ncyBhbmQgdGhlbiBjYXJyeSBvdXQgZnVydGhlciBleHBsb3JhdGlvbnMgb2YgdGhlIE1pbm5lc290YSBiaXJkcyBkYXRhc2V0LgoKIyMjIyBFWEVSQ0lTRVMKCioqUHJhY3RpY2UgUHJvYmxlbXMqKgoKTm93IGxldCdzIHByYWN0aWNlIGpvaW5pbmcgdGFibGVzIGFuZCBkZXNjcmliaW5nIHRoZSByZXN1bHQgb2YgdGhlIHNwZWNpZmljIGBqb2luX2Z1bmN0aW9uKClgLgoKKEApIFBlcmZvcm0gYSBgbGVmdF9qb2luKClgIGJldHdlZW4gdGhlICpPcmR3YXlCaXJkcyogZGF0YXNldCBhbmQgdGhlICpPcmR3YXlTcGVjaWVzTmFtZXMqIGRhdGFzZXQsIHNhdmluZyBpdCB0byBhIG5ldyBvYmplY3QgKHNvIGFsbCB0aGUgcm93cyBhcmVuJ3Qga25pdHRlZCB0byB5b3VyIEhUTUwpLCBhbmQgZXhhbWluZSB0aGUgZGltZW5zaW9ucyBvZiB0aGUgbmV3IG1lcmdlZCBkYXRhc2V0LiAKCmBgYHtyfQpPcmR3YXlCaXJkc19tZXJnZWQgPC0gT3Jkd2F5QmlyZHMgJT4lCiAgbGVmdF9qb2luKE9yZHdheVNwZWNpZXNOYW1lcywgYnkgPSAiU3BlY2llc05hbWUiKQoKZGltKE9yZHdheUJpcmRzKQpkaW0oT3Jkd2F5U3BlY2llc05hbWVzKQpkaW0oT3Jkd2F5QmlyZHNfbWVyZ2VkKQpgYGAKCihAKSBEZXNjcmliZSB3aGF0IG1vZGlmaWNhdGlvbnMgd2VyZSBtYWRlIHdoZW4gdGhlIHR3byBkYXRhc2V0cyB3ZXJlIGpvaW5lZCB0b2dldGhlciBpbnRvIG9uZSB1c2luZyB0aGlzIGpvaW4gZnVuY3Rpb24gKGUuZy4sIHJvdyBjaGFuZ2VzPyBjb2x1bW4gY2hhbmdlcz8pLiBBbHNvLCBkaWQgdGhlIGBsZWZ0X2pvaW4oKWAgZG8gd2hhdCB5b3UgdGhvdWdodCBpdCB3b3VsZCBkbz8gRXhwbGFpbi4KCi0gQmVmb3JlIHRoZSBkYXRhc2V0cyB3ZXJlIG1lcmdlZCwgdGhlIE9yZHdheUJpcmRzIGRhdGFzZXQgaGFkIDE1LDgyOSBvYnNlcnZhdGlvbnMgYW5kIDI2IHZhcmlhYmxlcyBmb3IgdGhlIG5hbWVzIG9mIGJpcmQgc3BlY2llcy4gT24gdGhlIG90aGVyIGhhbmQsIGluIHRoZSBPcmR3YXlTcGVjaWVzTmFtZSBkYXRhc2V0LCB0aGVyZSB3ZXJlIG9ubHkgMjY1IG9ic2VydmF0aW9ucyBhbmQgdHdvIHZhcmlhYmxlcy4gQWZ0ZXIgbWVyZ2luZyB0aGUgZGF0YXNldHMgdXNpbmcgdGhlIGpvaW4gZnVuY3Rpb24sIHRoZXJlIHdlcmUgMTcsMTIwIG9ic2VydmF0aW9ucyBhbmQgMjcgdmFyaWFibGVzLiBCZWNhdXNlIHRoZSBkYXRhc2V0cyB3ZXJlIGpvaW5lZCwgdGhlICdTcGVjaWVzTmFtZUNsZWFuZWQnIHZhcmlhYmxlIHdhcyBpbmNsdWRlZCBpbiB0aGUgbWVyZ2VkIGRhdGFzZXQsIHRoZXJlYnkgaW5jcmVhc2luZyB0aGUgbnVtYmVyIG9mIHZhcmlhYmxlcy4gQWRkaXRpb25hbGx5LCBiZWNhdXNlIHRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0IGNvbnRhaW5lZCBzb21lICdTcGVjaWVzTmFtZScgb2JzZXJ2YXRpb25zIHRoYXQgbWF0Y2hlZCBtdWx0aXBsZSByb3dzIGluIHRoZSBPcmR3YXlTcGVjaWVzTmFtZSBkYXRhc2V0LCB0aGUgbWVyZ2VkIGRhdGFzZXQgYWxzbyBoYWQgbW9yZSBvYnNlcnZhdGlvbnMuIFRoZXJlZm9yZSwgdGhlIGxlZnRfam9pbigpIGZ1bmN0aW9uIHBlcmZvcm1lZCBhcyBleHBlY3RlZC4gCgoKKltIaW50OiBBcmUgdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0IHRoZSBzYW1lIGFzIHlvdXIgbmV3IG1lcmdlZCBkYXRhc2V0P10qCiAKCihAKSBNb2RpZnkgdGhlICpPcmR3YXlTcGVjaWVzTmFtZXMqIGRhdGFzZXQgc28gdGhhdCBvbmx5IHRoZSB1bmlxdWUvZGlzdGluY3Qgcm93cyBhcmUgaW4gdGhlIGRhdGFzZXQuIFNhdmUgdGhpcyBuZXcgZGF0YXNldCBpbiBhbiBvYmplY3QgY2FsbGVkIGBzbmAuIAoKYGBge3J9CnNuIDwtIE9yZHdheVNwZWNpZXNOYW1lcyAlPiUKICBkaXN0aW5jdCgpCmBgYAoKKEApIFBlcmZvcm0gdGhlIGBsZWZ0X2pvaW4oKWAgYWdhaW4sIGJ1dCB0aGlzIHRpbWUgYmV0d2VlbiB0aGUgKk9yZHdheUJpcmRzKiBkYXRhc2V0IGFuZCB0aGUgYHNuYCBkYXRhc2V0LCBzYXZpbmcgaXQgdG8gYSBuZXcgb2JqZWN0LCBhbmQgZXhhbWluZSB0aGUgZGltZW5zaW9ucyBvZiB0aGUgbmV3IG1lcmdlZCBkYXRhc2V0LiBJcyB0aGlzIG5ldyBtZXJnZWQgZGF0YXNldCB3aGF0IHlvdSB3b3VsZCBleHBlY3QgZnJvbSBkb2luZyBhIGBsZWZ0X2pvaW4oKWA/IEV4cGxhaW4uCgotIEFmdGVyIGV2YWx1YXRpbmcgdGhlIG5ldyBtZXJnZWQgZGF0YXNldCwgdGhpcyBpcyB3aGF0IGlzIGV4cGVjdGVkIHdoZW4gdXNpbmcgYSBsZWZ0X2pvaW4gZnVuY3Rpb24uIFRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0IGNvbnRhaW5lZCAxNSw4Mjkgb2JzZXJ2YXRpb25zIGFuZCAyNiB2YXJpYWJsZXMuIEFmdGVyIG1lcmdpbmcgdGhlIGRhdGFzZXRzIHVzaW5nIHRoZSBsZWZ0X2pvaW4gZnVuY3Rpb24sIGV2ZXJ5IHJvdyBmcm9tIHRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0IHdhcyBrZXB0LCBhbmQgZWFjaCBiaXJkIHNwZWNpZXMgd2FzIG1hdGNoZWQgdG8gb25seSBvbmUgdW5pcXVlIHJvdyBpbiB0aGUgJ3NuJyBkYXRhc2V0LiBUaGVyZWZvcmUsIHRoZSBtZXJnZWQgZGF0YXNldCBoYWQgdGhlIHNhbWUgbnVtYmVyIG9mIG9ic2VydmF0aW9ucyBhcyB0aGUgT3Jkd2F5QmlyZHMgZGF0YXNldC4gRnVydGhlcm1vcmUsIGJlY2F1c2UgdGhlICdTcGVjaWVzTmFtZUNsZWFuZWQnIHZhcmlhYmxlIGZyb20gdGhlICdzbicgZGF0YXNldCB3YXMgaW5jbHVkZWQgaW4gdGhlIG5ldyBtZXJnZWQgZGF0YXNldCwgdGhlIGRhdGFzZXQgbm93IGhhZCAyNyB2YXJpYWJsZXMuIAoKYGBge3J9CnNuX09yZHdheUJpcmRzIDwtIE9yZHdheUJpcmRzICU+JQogIGxlZnRfam9pbihzbiwgYnkgPSAiU3BlY2llc05hbWUiKQoKCmRpbShzbikKdmlldyhzbikKZGltKE9yZHdheUJpcmRzKQpkaW0oc25fT3Jkd2F5QmlyZHMpCmBgYAoKKEApIFBlcmZvcm0gYW4gYGlubmVyX2pvaW4oKWAgYmV0d2VlbiB0aGUgKk9yZHdheUJpcmRzKiBkYXRhc2V0IGFuZCB0aGUgYHNuYCBkYXRhc2V0LCBzYXZpbmcgaXQgdG8gYSBuZXcgb2JqZWN0LiBFeGFtaW5lIHRoZSBkaW1lbnNpb25zIG9mIHRoZSBuZXcgbWVyZ2VkIGRhdGFzZXQuIERlc2NyaWJlIHdoYXQgbW9kaWZpY2F0aW9ucyB3ZXJlIG1hZGUgd2hlbiB0aGUgdHdvIGRhdGFzZXRzIHdlcmUgam9pbmVkIHRvZ2V0aGVyIGludG8gb25lIChlLmcuLCByb3cgY2hhbmdlcz8gY29sdW1uIGNoYW5nZXM/KS4KCi0gIFdoZW4gdXNpbmcgdGhlIGlubmVyX2pvaW4gZnVuY3Rpb24sIHRoZSAnU3BlY2llc05hbWUnIG9ic2VydmF0aW9ucyB0aGF0IG1hdGNoIGJvdGggaW4gdGhlIE9yZHdheUJpcmRzIGFuZCAnc24nIGRhdGFzZXRzIGFyZSBrZXB0LiBUaGVyZWZvcmUsIGFmdGVyIG1lcmdpbmcgdGhlIHR3byBkYXRhc2V0cywgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgKHJvd3MpIGRlY3JlYXNlZCBmcm9tIDE1LDgyOSB0byAxNSw3MjQuIFRoZSBpbm5lcl9qb2luIGZ1bmN0aW9uIGFsc28gYWRkcyBhbnkgYWRkaXRpb25hbCB2YXJpYWJsZSAoY29sdW1uKSBmcm9tIHRoZSAnc24nIGRhdGFzZXQuIFRoZXJlZm9yZSwgdGhlIG1lcmdlZCBkYXRhc2V0LCB3aGljaCBoYXMgMjcgdmFyaWFibGVzLCBpbmNsdWRlcyB0aGUgMjYgdmFyaWFibGVzIGZyb20gdGhlIE9yZHdheUJpcmRzIGRhdGFzZXQgYW5kIGFuIGFkZGl0aW9uYWwgJ1NwZWNpZXNOYW1lQ2xlYW5lZCcgdmFyaWFibGUgZnJvbSB0aGUgJ3NuJyBkYXRhc2V0LgoKCmBgYHtyfQpPcmR3YXlCaXJkc19pbm5lciA8LSBPcmR3YXlCaXJkcyAlPiUKICBpbm5lcl9qb2luKHNuLCBieSA9ICJTcGVjaWVzTmFtZSIpCgpkaW0oT3Jkd2F5QmlyZHMpCmRpbShPcmR3YXlCaXJkc19pbm5lcikKYGBgCgooQCkgUGVyZm9ybSBhIGBmdWxsX2pvaW4oKWAgYmV0d2VlbiB0aGUgKk9yZHdheUJpcmRzKiBkYXRhc2V0IGFuZCB0aGUgYHNuYCBkYXRhc2V0LCBzYXZpbmcgaXQgdG8gYSBuZXcgb2JqZWN0LiBFeGFtaW5lIHRoZSBkaW1lbnNpb25zIG9mIHRoZSBuZXcgbWVyZ2VkIGRhdGFzZXQuIERlc2NyaWJlIHdoYXQgbW9kaWZpY2F0aW9ucyB3ZXJlIG1hZGUgd2hlbiB0aGUgdHdvIGRhdGFzZXRzIHdlcmUgam9pbmVkIHRvZ2V0aGVyIGludG8gb25lIChlLmcuLCByb3cgY2hhbmdlcz8gY29sdW1uIGNoYW5nZXM/KS4KCi0gV2hlbiB1c2luZyB0aGUgZnVsbF9qb2luIGZ1bmN0aW9uLCBhbGwgJ1NwZWNpZXNOYW1lJyBvYnNlcnZhdGlvbnMgZnJvbSBib3RoIHRoZSBPcmR3YXlCaXJkcyBhbmQgJ3NuJyBkYXRhc2V0cyBhcmUga2VwdC4gQWZ0ZXIgbWVyZ2luZyB0aGUgZGF0YXNldHMsIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIChyb3dzKSBpbmNyZWFzZWQgc2xpZ2h0bHkgZnJvbSAxNSw4MjkgdG8gMTUsODMwIGJlY2F1c2UgdGhlICdzbicgZGF0YXNldCBpbmNsdWRlZCBhbiBhZGRpdGlvbmFsIGJpcmQgc3BlY2llcyB0aGF0IHdhcyBub3QgcHJlc2VudCBpbiB0aGUgT3Jkd2F5QmlyZHMgZGF0YXNldC4gVGhlIGZ1bGxfam9pbiBmdW5jdGlvbiBhbHNvIGFkZHMgYW55IGFkZGl0aW9uYWwgdmFyaWFibGUgKGNvbHVtbikgZnJvbSB0aGUgJ3NuJyBkYXRhc2V0LiBUaGVyZWZvcmUsIHRoZSBtZXJnZWQgZGF0YXNldCwgd2hpY2ggaGFzIDI3IHZhcmlhYmxlcywgaW5jbHVkZXMgdGhlIDI2IHZhcmlhYmxlcyBmcm9tIHRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0IGFuZCBhbiBhZGRpdGlvbmFsICdTcGVjaWVzTmFtZUNsZWFuZWQnIHZhcmlhYmxlIGZyb20gdGhlICdzbicgZGF0YXNldC4KCmBgYHtyfQpPcmR3YXlCaXJkc19mdWxsIDwtIE9yZHdheUJpcmRzICU+JSAKICBmdWxsX2pvaW4oc24sIGJ5ID0gIlNwZWNpZXNOYW1lIikKCgpkaW0oT3Jkd2F5QmlyZHMpCmRpbShPcmR3YXlCaXJkc19mdWxsKQpgYGAKCihAKSBQZXJmb3JtIGEgYHNlbWlfam9pbigpYCBiZXR3ZWVuIHRoZSAqT3Jkd2F5QmlyZHMqIGRhdGFzZXQgYW5kIHRoZSBgc25gIGRhdGFzZXQsIHNhdmluZyBpdCB0byBhIG5ldyBvYmplY3QuIEV4YW1pbmUgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIG5ldyBtZXJnZWQgZGF0YXNldC4gRGVzY3JpYmUgd2hhdCBtb2RpZmljYXRpb25zIHdlcmUgbWFkZSB3aGVuIHRoZSB0d28gZGF0YXNldHMgd2VyZSBqb2luZWQgdG9nZXRoZXIgaW50byBvbmUgKGUuZy4sIHJvdyBjaGFuZ2VzPyBjb2x1bW4gY2hhbmdlcz8pLgoKLSBXaGVuIHVzaW5nIHRoZSBzZW1pX2pvaW4gZnVuY3Rpb24sIG9ubHkgdGhlICdTcGVjaWVzTmFtZScgb2JzZXJ2YXRpb25zIGluIHRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0IHRoYXQgYWxzbyBtYXRjaCBpbiB0aGUgJ3NuJyBkYXRhc2V0IGFyZSBrZXB0LiBBZnRlciBtZXJnaW5nIHRoZSB0d28gZGF0YXNldHMsIHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIChyb3dzKSBkZWNyZWFzZWQgZnJvbSAxNSw4MjkgdG8gMTUsNzI0IGJlY2F1c2Ugc29tZSBvZiB0aGUgJ1NwZWNpZXNOYW1lJyBvYnNlcnZhdGlvbnMgaW4gdGhlIE9yZHdheUJpcmRzIGRhdGFzZXQgZGlkIG5vdCBtYXRjaCB3aXRoIHRoZSAnc24nIGRhdGFzZXQuIFRoZSBzZW1pX2pvaW4gZnVuY3Rpb24gb25seSBrZWVwcyB0aGUgdmFyaWFibGVzIChjb2x1bW5zKSBmcm9tIHRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0OyB0aGVyZWZvcmUsIHRoZSBtZXJnZWQgZGF0YXNldCBoYXMgb25seSAyNiB2YXJpYWJsZXMuIAoKYGBge3J9Ck9yZHdheUJpcmRzX3NlbWkgPC0gT3Jkd2F5QmlyZHMgJT4lCiAgc2VtaV9qb2luKHNuLCBieSA9ICJTcGVjaWVzTmFtZSIpCgoKZGltKE9yZHdheUJpcmRzKQpkaW0oT3Jkd2F5QmlyZHNfc2VtaSkKYGBgCgooQCkgUGVyZm9ybSBhbiBgYW50aV9qb2luKClgIGJldHdlZW4gdGhlICpPcmR3YXlCaXJkcyogZGF0YXNldCBhbmQgdGhlIGBzbmAgZGF0YXNldCwgc2F2aW5nIGl0IHRvIGEgbmV3IG9iamVjdC4gRXhhbWluZSB0aGUgZGltZW5zaW9ucyBvZiB0aGUgbmV3IG1lcmdlZCBkYXRhc2V0LiBEZXNjcmliZSB3aGF0IG1vZGlmaWNhdGlvbnMgd2VyZSBtYWRlIHdoZW4gdGhlIHR3byBkYXRhc2V0cyB3ZXJlIGpvaW5lZCB0b2dldGhlciBpbnRvIG9uZSAoZS5nLiwgcm93IGNoYW5nZXM/IGNvbHVtbiBjaGFuZ2VzPykuCgotIFdoZW4gdXNpbmcgdGhlIGFudGlfam9pbiBmdW5jdGlvbiwgb25seSB0aGUgJ1NwZWNpZXNOYW1lJyBvYnNlcnZhdGlvbnMgaW4gdGhlIE9yZHdheSBkYXRhc2V0IHRoYXQgZG8gbm90IG1hdGNoIGFueSAnU3BlY2llc05hbWUnIG9ic2VydmF0aW9ucyBpbiB0aGUgJ3NuJyBkYXRhc2V0IGFyZSBrZXB0LiBUaGVyZWZvcmUsIGFmdGVyIG1lcmdpbmcgdGhlIHR3byBkYXRhc2V0cywgdGhlIG51bWJlciBvZiBvYnNlcnZhdGlvbnMgKHJvd3MpIGRlY3JlYXNlZCBmcm9tIDE1LDgyOSB0byAxMDUuIFRoZSBhbnRpX2pvaW4gZnVuY3Rpb24gb25seSBrZWVwcyB0aGUgdmFyaWFibGVzIChjb2x1bW5zKSBmcm9tIHRoZSBPcmR3YXlCaXJkcyBkYXRhc2V0OyB0aGVyZWZvcmUsIHRoZSBtZXJnZWQgZGF0YXNldCBoYXMgb25seSAyNiB2YXJpYWJsZXMuIAoKYGBge3J9Ck9yZHdheUJpcmRzX2FudGkgPC0gT3Jkd2F5QmlyZHMgJT4lIAogIGFudGlfam9pbihzbiwgYnkgPSAiU3BlY2llc05hbWUiKQoKCmRpbShPcmR3YXlCaXJkcykKZGltKE9yZHdheUJpcmRzX2FudGkpCmBgYAoKKipQdXR0aW5nIEl0IEFsbCBUb2dldGhlcioqCgpOb3cgbGV0J3MgcHV0IHRoZSBqb2lucyB0byB1c2UgKGluIGFkZGl0aW9uIHRvIG90aGVyIGRhdGEgdmVyYnMgYW5kIGRhdGEgdmlzdWFsaXphdGlvbikgdG8gYW5zd2VyIGEgcXVlc3Rpb24uCgo8cCBhbGlnbj0iY2VudGVyIj4KKipXaGF0IGlzIG1vbnRoLXRvLW1vbnRoIHByZXNlbmNlIG9mIHRoZSBtb3N0IGNvbW1vbiBiaXJkIHNwZWNpZXMgaW4gdGhlIE9yZHdheSBuYXR1cmUgcHJlc2VydmUgYXJlYT8qKgo8L3A+CgpUaGluayBvZiB0aGlzIGFzc2lnbm1lbnQgYXMgY3JlYXRpbmcgYSByZXNvdXJjZSBmb3IgYmlyZGVycyBvbiB0aGUgaWRlYWwgdGltZSBvZiB5ZWFyIHRvIHZpc2l0IE9yZHdheSB0byBzZWUgYSBwYXJ0aWN1bGFyIHNwZWNpZXMuCgpJbiBhZGRpdGlvbiB0byB0aGUgZXJyb3JzIGluICpTcGVjaWVzTmFtZSosIHRoZXJlIGFyZSBhbHNvIHByb2JsZW1zIHdpdGggdGhlICpNb250aCogYW5kICpEYXkqIHZhcmlhYmxlcy4gVGhleSBhcmUgc3VwcG9zZWQgdG8gYmUgbnVtZXJpY2FsLCBidXQgbWlzdGFrZXMgcHJldmVudCB0aGVtIGZyb20gYmVpbmcgY29ycmVjdGx5IGlkZW50aWZpZWQgYXMgc3VjaC4gVGhlIGZvbGxvd2luZyBjb2RlIHdpbGwgdGFrZSBjYXJlIG9mIHRoaXMgaXNzdWUgd2l0aCAqTW9udGgqIGFuZCAqRGF5KjogCltOb3RlOiBUYWtlIG91dCBldmFsPUZBTFNFIGluIHRoZSBvcHRpb25zIG9mIHRoZSBjb2RlIGNodW5rIHNvIHRoYXQgdGhlIGNvZGUgZXhlY3V0ZXMgaW4geW91ciBhc3NpZ25tZW50Ll0KCmBgYHtyfQpiaXJkcyA8LSBPcmR3YXlCaXJkcyAlPiUKICBtdXRhdGUoTW9udGggPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihNb250aCkpLCAKICAgICAgICBEYXkgPSBhcy5udW1lcmljKGFzLmNoYXJhY3RlcihEYXkpKSkKYGBgCgpUaGUgbmV4dCBzZXQgb2YgcXVlc3Rpb25zIGFyZSBnb2luZyB0byB3YWxrIHlvdSB0aHJvdWdoIHRoZSBwcm9jZXNzIG9mIGhvdyB0byBleHBsb3JlIHRoZSBkYXRhIHRvIGFuc3dlciB0aGUgcXVlc3Rpb24uIAoKKEApIEluY2x1ZGluZyBtaXNzcGVsbGluZ3MsIGhvdyBtYW55IGRpZmZlcmVudCBzcGVjaWVzIGFyZSB0aGVyZSBpbiB0aGUgKG5ld2x5IGNyZWF0ZWQpIGBiaXJkc2AgZGF0YXNldD8KCmBgYHtyfQpiaXJkcyAlPiUgCiAgZGlzdGluY3QoU3BlY2llc05hbWUpICU+JQogIGNvdW50KCkKYGBgCgooQCkgSG93IG1hbnkgKipkaXN0aW5jdCoqIHNwZWNpZXMgYXJlIHRoZXJlIGluIHRoZSAqU3BlY2llc05hbWVDbGVhbmVkKiB2YXJpYWJsZSBpbiBgc25gIGRhdGFzZXQ/CgpgYGB7cn0Kc24gJT4lIAogIGRpc3RpbmN0KFNwZWNpZXNOYW1lQ2xlYW5lZCkgJT4lCiAgY291bnQoKQpgYGAKCihAKSBQZXJmb3JtIHRoZSBhcHByb3ByaWF0ZSBqb2luIGZ1bmN0aW9uIHRvIG1lcmdlIHRoZSBgYmlyZHNgIGRhdGFzZXQgd2l0aCB0aGUgYHNuYCBkYXRhc2V0LCBzdWNoIHRoYXQgb25seSB0aGUgbWF0Y2hpbmcgcm93cyBmcm9tIGBzbmAgYW5kIGBiaXJkc2AgYXJlIGluY2x1ZGVkIGFuZCBhbGwgY29sdW1ucyBmcm9tIGBiaXJkc2AgYW5kIGBzbmAgYXJlIGluY2x1ZGVkLiBBbHNvIHVzZSB0aGUgYG5hLm9taXQoKWAgZnVuY3Rpb24gdG8gcmVtb3ZlIG1pc3NpbmcgZGF0YSBmcm9tIHlvdXIgZGF0YXNldCAoTm90ZTogVGhpcyB3aWxsIHJlbW92ZSBhbGwgcm93cyB3aXRoIEFOWSBOQXMpLiBTYXZlIHRoaXMgbWVyZ2VkIGRhdGFzZXQgdG8gYSBuZXcgb2JqZWN0IGNhbGxlZCBgYmlyZHNfc25gLgoKYGBge3J9CmJpcmRzX3NuIDwtIGJpcmRzICU+JQogIGlubmVyX2pvaW4oc24sIGJ5ID0gIlNwZWNpZXNOYW1lIikgJT4lCiAgbmEub21pdCgpCmBgYAoKKEApIEhvdyBtYW55IGJpcmQgY2FwdHVyZXMgYXJlIHJlcG9ydGVkIGZvciBlYWNoIG9mIHRoZSAoY29ycmVjdCkgc3BlY2llcz8gQmUgc3VyZSB0byBhcnJhbmdlIHRoZSBkYXRhIGluIGRlc2NlbmRpbmcgb3JkZXIgZnJvbSB0aGUgc3BlY2llcyB3aXRoIHRoZSBtb3N0IGJpcmRzIHRvIHRoZSBsZWFzdC4KCmBgYHtyfQpiaXJkc19zbiAlPiUKICBjb3VudChTcGVjaWVzTmFtZUNsZWFuZWQpICU+JQogIGFycmFuZ2UoZGVzYyhuKSkKYGBgCgooQCkgTGV0J3Mga2VlcCBvbmx5IHRoZSB0aGUgbW9zdCBjb21tb24gYmlyZCBzcGVjaWVzIGNhcHR1cmVkLiBDcmVhdGUgYSBuZXcgZGF0YXNldCBvZiBjb3VudHMgc2ltaWxhciB0byB0aGUgYW5zd2VyIGZyb20gdGhlIHByZXZpb3VzIHF1ZXN0aW9uIGluIGFuIG9iamVjdCBjYWxsZWQgYHRvcF9zcGVjaWVzYCB0aGF0IGNvbnRhaW5zIG9ubHkgdGhlIHRvcCA2IG1vc3QgY29tbW9uIGJpcmQgc3BlY2llcy4gCgpgYGB7cn0KdG9wX3NwZWNpZXMgPC0gYmlyZHNfc24gJT4lCiAgY291bnQoU3BlY2llc05hbWVDbGVhbmVkLCBzb3J0ID0gVFJVRSkgJT4lCiAgaGVhZCg2KQoKdG9wX3NwZWNpZXMKYGBgCgooQCkgTm93IGxldCdzIGNyZWF0ZSB0aGUgZmluYWwgZGF0YXNldCB0byBoZWxwIGFuc3dlciB0aGUgcXVlc3Rpb24uIENyZWF0ZSBhIG5ldyBkYXRhc2V0IGluIGFuIG9iamVjdCBjYWxsZWQgYHRvcF9zcGVjaWVzX21vbnRoYCB0aGF0IGNvbnRhaW5zIHRoZSB0b3Agc3BlY2llcyBhbmQgYSBtb250aC1ieS1tb250aCBjb3VudCBvZiBlYWNoIG9mIHRoZSBtb3N0IGNvbW1vbiBzcGVjaWVzLiAoSGludDogdXNlIGEgc3BlY2lmaWMgdHlwZSBvZiBqb2luIHRvIGxpbWl0IHRoZSBiaXJkc19zbiBlbnRyaWVzIHRvIG9ubHkgdGhlIGJpcmRzIGluY2x1ZGVkIGluIHRoZSB0b3Bfc3BlY2llcyBkYXRhc2V0LCB0aGVuIGNvdW50IHRoZSBudW1iZXIgb2Ygc2lnaHRpbmdzIGJ5IHNwZWNpZXMgYW5kIG1vbnRoKS4gCgpgYGB7cn0KdG9wX3NwZWNpZXNfbW9udGggPC0gYmlyZHNfc24gJT4lCiAgc2VtaV9qb2luKHRvcF9zcGVjaWVzLCBieSA9ICJTcGVjaWVzTmFtZUNsZWFuZWQiKSAlPiUKICBncm91cF9ieShTcGVjaWVzTmFtZUNsZWFuZWQsIE1vbnRoKSAlPiUKICBjb3VudCgpICU+JQogIGFycmFuZ2UoU3BlY2llc05hbWVDbGVhbmVkLCBNb250aCkKCmBgYAoKKEApIENyZWF0ZSBhIGRhdGEgdmlzdWFsaXphdGlvbiB0aGF0IGhlbHBzIHRvIGFuc3dlciB0aGUgcXVlc3Rpb24uIEJlIHN1cmUgdG8gdGFrZSBpbnRvIGFjY291bnQgbWVhbmluZ2Z1bCBkZXNpZ24gZWxlbWVudHMgYXMgeW91IGNyZWF0ZSB5b3VyIHBsb3QuCgpgYGB7cn0KdG9wX3NwZWNpZXNfbW9udGggJT4lCiAgZ2dwbG90KGFlcyh4ID0gZmFjdG9yKE1vbnRoKSwgeSA9IG4sIGdyb3VwID0gU3BlY2llc05hbWVDbGVhbmVkLCBjb2xvciA9IFNwZWNpZXNOYW1lQ2xlYW5lZCkpICsKICBnZW9tX2xpbmUoKSArIAogIGZhY2V0X3dyYXAoflNwZWNpZXNOYW1lQ2xlYW5lZCkgKyAKICB0aGVtZV9idygpICsgCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHN0cmlwLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNiksCiAgICAgICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSA2KSwKICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgCiAgbGFicyh4ID0gIk1vbnRoIiwgeSA9ICIjIG9mIENhcHR1cmVzIiwgdGl0bGUgPSAiTW9zdCBDb21tb24gQmlyZCBTcGVjaWVzOiBNb250aCBieSBNb250aCBDb3VudCAiKSAKYGBgCgooQCkgVXNlIHRoZSBkYXRhIHZpc3VhbGl6YXRpb24gdG8gYW5zd2VyIHRoZXNlIHF1ZXN0aW9ucyBmb3IgdGhlIGJpcmRlcnM6CgogICAgYS4gV2hpY2ggbW9zdCBjb21tb24gc3BlY2llcyBhcmUgcHJlc2VudCB5ZWFyLXJvdW5kPwogICAgLSBUcmVlIFN3YWxsb3cgCiAgICAKICAgIGIuIFdoaWNoIHNwZWNpZXMgYXJlIG1pZ3JhdG9yeSwgdGhhdCBpcywgcHJpbWFyaWx5IHByZXNlbnQgaW4gb25lIG9yIHR3byBzZWFzb25zPwogICAgLSBUaGUgRmllbGQgU3BhcnJvdyBzcGVjaWVzIHdhcyBvbmx5IHByaW1hcmlseSBwcmVzZW50IGluIG9uZSBzZWFzb24gd2hpbGUgdGhlIEFtZXJpY2FuIEdvbGRmaW5jaCwgQmxhY2stY2FwcGVkIENoaWNrYWRlZSwgUm9iaW4sIGFuZCBTdGF0ZS1Db2xvcmVkIEp1bmNvIHNwZWNpZXMgYXJlIHByaW1hcmlseSBwcmVzZW50IGluIHR3byBzZWFzb25zLiAKICAgIAogICAgYy4gV2hpY2ggaXMgdGhlIHBlYWsgbW9udGggZm9yIGVhY2ggbWFqb3Igc3BlY2llcz8KICAgIC0gQW1lcmljYW4gR29sZGZpbmNoOiBNYXkgYW5kIE9jdG9iZXIgCiAgICAtIEJhY2stY2FwcGVkIENoaWNrYWRlZTogRmVicnVhcnkgYW5kIE5vdmVtYmVyCiAgICAtIEZpZWxkIFNwYXJyb3c6IE1heSAKICAgIC0gUm9iaW46IEFwcmlsIC0gTWF5IAogICAgLSBTdGF0ZS1jb2xvcmVkIEp1bmNvOiBBcHJpbCBhbmQgT2N0b2JlciAKICAgIC0gVHJlZSBTd2FsbG93OiBNYXJjaCwgSnVuZSwgYW5kIE5vdmVtYmVyIAoKICAgIAogICAg