This goes through a lot of the queries that you might write to work on your PPP data, but it will also serve as an example for the test.

If you are having trouble figuring out what these do, try them and see what they produce. Also try running pieces in their parts. You can select everything up to (but not including) a pipe %>% and run it by hitting CTL-Enter (both Mac and PC)

Loading libraries

Every time you start up R, you will have to run every chunk before subsequent ones will work. Don’t forget to do this. Here are some typical libraries that many of your programs will always use:

knitr::opts_chunk$set(echo=TRUE, 
                      message=FALSE, 
                      results = "hide")


library(tidyverse)  # for almost everything
library(janitor)    # for fixing column names, etc.
library(readxl)     # for importing Excel (.xlsx) files
library(lubridate)  # for working with dates

Overview

This document reviews the key commands that will be on the R exam. You will largely not have to write code yourself – instead, you’ll have to understand it. In cases where you write it, there will be plenty of examples preceeding so that you would be able to copy, paste and adjust to your own needs.

Although this document is organized by verb, the questions in the real test will not be. They will be organized by data set, and all of the verbs will be mixed together. However, there is nothing from R that will be on the test that is outside this document that you are expected to know. Your bigger challenge is the same as the first test: to understand and imagine the journalistic value of a dataset.

Each section has typical questions that might be in the exam itself.

Making sure you know where things are on your computer

The easiest way to make sure you know how to find things is to save everything into the same folder for a project – say, a week at school, or the PPP project. Then open that folder as a project.

For the long, complicated links that are csv files, you just have to copy and paste them into your code.

Files we’ll work with in this tutorial:

We MAY go over these, but they’re good practice for you. The two CSV files are used to standardize the categories in the spreadsheet, by joining to them .

Reading data

Three typical ways to get data in:

  • Load an R file
  • Import a csv (or one of its cousins)
  • Import an Excel file

R and csv files can be read from links. Excel files can’t.

When you import a file, you will likely have to check on a few things:

  • Were the data types of each column properly read? For example, is a date actually a date, or is a text field incorrectly seen as a number?

  • Are the names of the columns difficult to work with in R?

Import a csv

First we’ll import the Phoenix city salaries for 2020. You’ll have to describe what the following code does, in your own words:

Sometimes, as in this example, it’s useful to create a simple variable that holds just one thing to use in another command. In this case, it’s a long link copied from the Web. If you wanted to change it, all you’d have to do is change that line and it would propagate to everywhere you’ve used it.

phx_2020_salary_link <- "https://www.phoenixopendata.com/dataset/0c02bbfd-08d9-41e9-a00c-97848a9f7350/resource/4d8c9800-8054-4c11-8a13-b9b5bd0511dc/download/open-data-portal-staff-salaries-dec-2020.csv"

read_csv( phx_2020_salary_link)

Hmmm. That read it, but it came out with some weird messages and I don’t have a data frame.

Why?

phx_2020 <- read_csv(phx_2020_salary_link)

I still have the same weird messages, but they appear ok: Everything but the ANNUAL RATE has been read as text, and the amount has been read as a number. Instead of making this the default, let’s make it clear what we want it to do:

phx_2020 <- read_csv(phx_2020_salary_link, 
                     col_types = cols ( .default="c", 
                                        "ANNUAL RATE" = "n"))

No more message.

The names are still awful because there are spaces in them. Change them to lower case and remove the spaces using a shortcut for renaming variables in a standard format, from the janitor library:

phx_2020 <- read_csv(phx_2020_salary_link, 
                     col_types = cols ( .default="c", 
                                        "ANNUAL RATE" = "n")) %>%
  clean_names()

This is the same thing as doing:

  rename ( last_name = LAST_NAME, 
            first_name = FIRST_NAME
             etc.)

Read an Excel file

You don’t have the same options of controlling the input from Excel. You just have to pretty much live with what it guesses, then work with it later.

Notice that we don’t have a link, because it was saved into your default folder. However, the first line of the spreadsheet contains a title – here, you need to skip one line:

payouts <- read_excel("payouts.xlsx", skip=1) %>%
  clean_names()

Read an R file

R files can be called several things – you’ll most frequently see them as .Rda or .Rdata. These don’t get imported, they just get loaded. Load from the web by using the url() function. They create data frames in your environment without naming them, and there can be all kinds of objects in them. Think of them as a zip file that’s been unzipped right into R:

load ( url ("https://github.com/cronkitedata/rstudyguide/blob/master/data/az_ppp_zipcodes.Rda?raw=true"))
What is the noun for this dataset? Ie, what does each row represent?

Filter and arrange

Here are a few example questions you might get about a dataset like the PPP data:

  1. In your own words, what does this code do? Make sure your description is something that a normal person with no background in data would understand.

  2. Why might a reporter want to know this?

  3. try to write a short, declarative sentece that conveys the essence of the results. This is the equivalent of a straight lede that accurately describes the findings that would be understandable to someone who knew nothing about the data analysis .

You would do the same thing in the next three chunks.

az_ppp_zip %>%
  select ( borrower, city, state, zip, date_approved, status_date, initial_amt:nonpayroll_amt ) %>%
  arrange ( desc(nonpayroll_amt)) %>%
  head (15)

Number 2:

az_ppp_zip %>%
  filter ( str_detect ( naics_sector, "^72") & date_approved >= "2020-12-20") %>%
  select ( borrower, city, state, zip, date_approved,  initial_amt) %>%
  arrange ( desc ( date_approved) ) 

Number 3:

az_ppp_zip %>%
  filter ( business_age %in% c("STARTUP, LOAN FUNDS WILL OPEN BUSINESS", "NEW BUSINESS OR 2 YEARS OR LESS") &
             city == "PHOENIX")

(An alternative way to get the same thing using the wildcard regular expression of “^STARTUP|NEW” where ‘^’ means “it should start with this” and | means “either one of these things.”)

az_ppp_zip %>%
  filter ( str_detect ( business_age, "^STARTUP|NEW") &
             city == "PHOENIX")

What is another question that can be answered using select, filter and arrange verbs that a reporter might ask of the data? Write your question in plain English, not in computer-ese, and make it clear why a reporter might want to know this.

Mutate

NOTE: Looking at it today, there is a problem in the way the government is reporting the non-payroll amount in this second draw of money (since December). There are no non-payroll amounts, except there is sometimes a “1” in the utilities amount. I don’t know if that’s meant as a TRUE-FALSE indicator, or what, but it’s not reliable at this time. (I’ve checked, and it’s like that in the original data file as published by SBA, which hasn’t been updated since I downloaded it. )

Answer the same questions as above for this set of code chunks :

az_ppp_zip %>%
  mutate ( dollars_per_job = payroll_amt / jobs_reported, 
           new_bus_type = case_when ( str_detect ( business_type, "(SOLE PROP|SELF-EMPLOYED|INDEPENDENT)" ) ~ "SELF", 
                                    non_profit == "Y" ~ "NON-PROFIT", 
                                    TRUE ~ "FOR-PROFIT")
           ) %>%
  select (borrower, new_bus_type, initial_amt, dollars_per_job)

GROUP BY , SUMMARISE

Same questions for these code chunks:

(This one will take a minute to run. The group by here is slow.)

both_draws <- 
  az_ppp_zip %>%
  group_by ( borrower, zip) %>%
  summarise ( num_of_loans = n(), 
              amount = sum (initial_amt, na.rm=T), 
              .groups="drop"
            ) %>%
  filter ( num_of_loans > 1) %>%
  arrange ( zip)

(Don’t worry about that .groups=“drop” statement - it just gets rid of something that will be a pain later on. )

Additional question:

Now that you have this list, what might you do with it? Let’s discuss in the session.

zcta_totals <- 
  az_ppp_zip %>%
  group_by (census_zip ) %>%
  summarise ( number_of_loans = n() , 
              loan_amount = sum(initial_amt, na.rm=T), 
              payroll_amount = sum( payroll_amt, na.rm=T), 
              jobs_supported = sum(jobs_reported, na.rm=T)) 

Next one:

zcta_totals %>%
  mutate ( pct_of_loans = number_of_loans / sum(number_of_loans) * 100) %>%
  arrange ( desc (loan_amount))

Next one:

az_ppp_zip %>%
  mutate ( program = if_else ( date_approved < "2020-12-20", "First draw", "Second draw"))  %>%
  group_by ( census_zip, program) %>%
  summarise ( number_of_loans = n() ) %>%
  pivot_wider ( values_from = number_of_loans, values_fill=0, names_from = program) %>%
  clean_names() %>%
  filter ( second_draw / first_draw > .5 & first_draw > 20)

JOIN

Same questions

az_ppp_byziptype <- 
  az_ppp_zip %>%
  left_join ( az_by_zipcode, by=c("census_zip"="zcta")) %>%
  group_by ( lender, zcta_ethnic) %>%
  summarise (number_of_loans = n() ) %>%
  mutate (lender_total = sum(number_of_loans)) %>%
  pivot_wider ( names_from = zcta_ethnic, values_from = number_of_loans, values_fill=0) %>%
  mutate (white_as_pct = `01 - Majority white`/lender_total * 100) %>%
  filter (lender_total > 20)

What might you do with this table? (There’s one logical mistake in this: It excludes any zip codes in which there were no loans given. You’d have to join again to the zip code list and fill it with zeros to get it exact.)

Example 2:

options (scipen=999)

az_ppp_zip %>%
  group_by (census_zip) %>%
  summarise ( number_of_loans = n(), 
              number_of_borrowers  = n_distinct ( borrower, zip), 
              total_amount = sum( initial_amt )
  ) %>%
  right_join ( az_by_zipcode, by=c("census_zip"="zcta")) %>%
  mutate ( amt_per_cap = total_amount / tot_pop, 
           #YOU HAVEN'T SEEN THIS AND ARENT EXPECTED TO UNDERSTAND - IT IS TURNING NA INTO 0 FOR ALL NUMERIC
           across ( where (is.numeric), replace_na, 0)
           ) %>%
  select (zcta_ethnic,  tot_pop, census_zip, zipcode_city, number_of_loans: total_amount, amt_per_cap, county_name,  median_inc_2018) %>%
  filter (tot_pop > 100) %>%
  arrange ( amt_per_cap)

(Here’s where you see how missing the ones that go no loans matters.)

Another join example

We already loaded in the Phoenix salaries for 2020. Let’s load in the previous year’s doing exactly what we did before.

phx_2019_salary_link <- "https://www.phoenixopendata.com/dataset/0c02bbfd-08d9-41e9-a00c-97848a9f7350/resource/4280d70d-1dae-45dd-8190-2ba32109979d/download/2019_salies.csv"

phx_2019 <- read_csv ( phx_2019_salary_link, col_types = cols (.default="c", "ANNUAL RATE" = "n")) %>% clean_names()

Let’s try a full join - we have to rename columns so that they don’t get mixed up. This is really dangerous, and I always regret it, since if I make a mistake I have to go back to the import statement.

phx_2019 <- 
  phx_2019 %>%
  rename ( descr2019 = descr, annual_rate_2019 = annual_rate)  %>%
  select (last_name:annual_rate_2019)

phx_2020 <- 
phx_2020 %>%
  rename ( descr2020 = descr, annual_rate_2020 = annual_rate)

Now - what happens with a “full” join, in other words keeping everything?

phx_bothyears <- 
  phx_2020 %>% full_join ( phx_2019, by=c("last_name"="last_name", "first_name"="first_name"))

There’s a lot of duplicates in here, but that’s OK - what do you think this query does? And why might it be helpful for a reporter? What are the likely mistakes in it?

phx_bothyears %>%
  filter (is.na( descr2020) & str_detect(descr2019, "Police") )
LS0tCnRpdGxlOiAiUiBzdHVkeSBzZXNzaW9uIgphdXRob3I6ICJTYXJhaCBDb2hlbiIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogeWV0aQogICAgaGlnaGxpZ2h0OiBrYXRlCiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogNAogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCgoKVGhpcyBnb2VzIHRocm91Z2ggYSBsb3Qgb2YgdGhlIHF1ZXJpZXMgdGhhdCB5b3UgbWlnaHQgd3JpdGUgdG8gd29yayBvbiB5b3VyIFBQUCBkYXRhLCBidXQgaXQgd2lsbCBhbHNvIHNlcnZlIGFzIGFuIGV4YW1wbGUgZm9yIHRoZSB0ZXN0LiAKCklmIHlvdSBhcmUgaGF2aW5nIHRyb3VibGUgZmlndXJpbmcgb3V0IHdoYXQgdGhlc2UgZG8sIHRyeSB0aGVtIGFuZCBzZWUgd2hhdCB0aGV5IHByb2R1Y2UuIEFsc28gdHJ5IHJ1bm5pbmcgcGllY2VzIGluIHRoZWlyIHBhcnRzLiBZb3UgY2FuIHNlbGVjdCBldmVyeXRoaW5nIHVwIHRvIChidXQgbm90IGluY2x1ZGluZykgYSBwaXBlIGAlPiVgIGFuZCBydW4gaXQgYnkgaGl0dGluZyBDVEwtRW50ZXIgKGJvdGggTWFjIGFuZCBQQykKCiMjIExvYWRpbmcgbGlicmFyaWVzCgpFdmVyeSB0aW1lIHlvdSBzdGFydCB1cCBSLCB5b3Ugd2lsbCBoYXZlIHRvIHJ1biBldmVyeSBjaHVuayBiZWZvcmUgc3Vic2VxdWVudCBvbmVzIHdpbGwgd29yay4gRG9uJ3QgZm9yZ2V0IHRvIGRvIHRoaXMuIEhlcmUgYXJlIHNvbWUgdHlwaWNhbCBsaWJyYXJpZXMgdGhhdCBtYW55IG9mIHlvdXIgcHJvZ3JhbXMgd2lsbCBhbHdheXMgdXNlOgoKYGBge3Igc2V0dXAsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UgfQoKa25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG89VFJVRSwgCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlPUZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgIHJlc3VsdHMgPSAiaGlkZSIpCgoKbGlicmFyeSh0aWR5dmVyc2UpICAjIGZvciBhbG1vc3QgZXZlcnl0aGluZwpsaWJyYXJ5KGphbml0b3IpICAgICMgZm9yIGZpeGluZyBjb2x1bW4gbmFtZXMsIGV0Yy4KbGlicmFyeShyZWFkeGwpICAgICAjIGZvciBpbXBvcnRpbmcgRXhjZWwgKC54bHN4KSBmaWxlcwpsaWJyYXJ5KGx1YnJpZGF0ZSkgICMgZm9yIHdvcmtpbmcgd2l0aCBkYXRlcwoKCmBgYAoKCgoKIyMgT3ZlcnZpZXcKClRoaXMgZG9jdW1lbnQgcmV2aWV3cyB0aGUga2V5IGNvbW1hbmRzIHRoYXQgd2lsbCBiZSBvbiB0aGUgUiBleGFtLiBZb3Ugd2lsbCBsYXJnZWx5IG5vdCBoYXZlIHRvIHdyaXRlIGNvZGUgeW91cnNlbGYgLS0gaW5zdGVhZCwgeW91J2xsIGhhdmUgdG8gdW5kZXJzdGFuZCBpdC4gSW4gY2FzZXMgd2hlcmUgeW91IHdyaXRlIGl0LCB0aGVyZSB3aWxsIGJlIHBsZW50eSBvZiBleGFtcGxlcyBwcmVjZWVkaW5nIHNvIHRoYXQgeW91IHdvdWxkIGJlIGFibGUgdG8gY29weSwgcGFzdGUgYW5kIGFkanVzdCB0byB5b3VyIG93biBuZWVkcy4gCgpBbHRob3VnaCB0aGlzIGRvY3VtZW50IGlzIG9yZ2FuaXplZCBieSB2ZXJiLCB0aGUgcXVlc3Rpb25zIGluIHRoZSByZWFsIHRlc3Qgd2lsbCBub3QgYmUuIFRoZXkgd2lsbCBiZSBvcmdhbml6ZWQgYnkgZGF0YSBzZXQsIGFuZCBhbGwgb2YgdGhlIHZlcmJzIHdpbGwgYmUgbWl4ZWQgdG9nZXRoZXIuIEhvd2V2ZXIsIHRoZXJlIGlzIG5vdGhpbmcgZnJvbSBSIHRoYXQgd2lsbCBiZSBvbiB0aGUgdGVzdCB0aGF0IGlzIG91dHNpZGUgdGhpcyBkb2N1bWVudCB0aGF0IHlvdSBhcmUgZXhwZWN0ZWQgdG8ga25vdy4gWW91ciBiaWdnZXIgY2hhbGxlbmdlIGlzIHRoZSBzYW1lIGFzIHRoZSBmaXJzdCB0ZXN0OiB0byB1bmRlcnN0YW5kIGFuZCBpbWFnaW5lIHRoZSBqb3VybmFsaXN0aWMgdmFsdWUgb2YgYSBkYXRhc2V0LiAKCgpFYWNoIHNlY3Rpb24gaGFzIHR5cGljYWwgcXVlc3Rpb25zIHRoYXQgbWlnaHQgYmUgaW4gdGhlIGV4YW0gaXRzZWxmLiAKCiMjIE1ha2luZyBzdXJlIHlvdSBrbm93IHdoZXJlIHRoaW5ncyBhcmUgb24geW91ciBjb21wdXRlcgoKVGhlIGVhc2llc3Qgd2F5IHRvIG1ha2Ugc3VyZSB5b3Uga25vdyBob3cgdG8gZmluZCB0aGluZ3MgaXMgdG8gc2F2ZSBldmVyeXRoaW5nIGludG8gdGhlIHNhbWUgZm9sZGVyIGZvciBhIHByb2plY3QgLS0gc2F5LCBhIHdlZWsgYXQgc2Nob29sLCBvciB0aGUgUFBQIHByb2plY3QuIFRoZW4gb3BlbiB0aGF0IGZvbGRlciBhcyBhIHByb2plY3QuIAoKRm9yIHRoZSBsb25nLCBjb21wbGljYXRlZCBsaW5rcyB0aGF0IGFyZSBjc3YgZmlsZXMsIHlvdSBqdXN0IGhhdmUgdG8gY29weSBhbmQgcGFzdGUgdGhlbSBpbnRvIHlvdXIgY29kZS4gCgpGaWxlcyB3ZSdsbCB3b3JrIHdpdGggaW4gdGhpcyB0dXRvcmlhbDogCgoqIFBQUCBkYXRhIHRoYXQgaXMgaW4gYW4gUiBkYXRhIGZpbGUsIHdoaWNoIGlzIGRvY3VtZW50ZWQgaGVyZTogPGh0dHBzOi8vY3JvbmtpdGVkYXRhLmdpdGh1Yi5pby9yc3R1ZHlndWlkZS9leHRyYS9hel9wcHBfZG9jdW1lbnRhdGlvbi5odG1sPgoKCiogVGhlIFBob2VuaXggc2FsYXJpZXMgb2YgY2l0eSB3b3JrZXJzIGZvciAyMDIwIGFuZCAyMDE5OiAKPGh0dHBzOi8vd3d3LnBob2VuaXhvcGVuZGF0YS5jb20vZGF0YXNldC8wYzAyYmJmZC0wOGQ5LTQxZTktYTAwYy05Nzg0OGE5ZjczNTAvcmVzb3VyY2UvNGQ4Yzk4MDAtODA1NC00YzExLThhMTMtYjliNWJkMDUxMWRjL2Rvd25sb2FkL29wZW4tZGF0YS1wb3J0YWwtc3RhZmYtc2FsYXJpZXMtZGVjLTIwMjAuY3N2PiBhbmQgPGh0dHBzOi8vd3d3LnBob2VuaXhvcGVuZGF0YS5jb20vZGF0YXNldC8wYzAyYmJmZC0wOGQ5LTQxZTktYTAwYy05Nzg0OGE5ZjczNTAvcmVzb3VyY2UvNDI4MGQ3MGQtMWRhZS00NWRkLTgxOTAtMmJhMzIxMDk5NzlkL2Rvd25sb2FkLzIwMTlfc2FsaWVzLmNzdj4KCldlIE1BWSBnbyBvdmVyIHRoZXNlLCBidXQgdGhleSdyZSBnb29kIHByYWN0aWNlIGZvciB5b3UuIFRoZSB0d28gQ1NWIGZpbGVzIGFyZSB1c2VkIHRvIHN0YW5kYXJkaXplIHRoZSBjYXRlZ29yaWVzIGluIHRoZSBzcHJlYWRzaGVldCwgYnkgam9pbmluZyB0byB0aGVtIC4gCgoqIFRocmVlIGZpbGVzIGZyb20gdGhlIExBIFRpbWVzIG9uIHBheW91dHMgZnJvbSBsYXdzdWl0cy4gWW91IHJlYWQgdGhlIHN0b3J5IHRoaXMgZGF0YSBwcm9kdWNlZCBlYXJsaWVyIGluIHRoZSBzZW1lc3Rlci4gCgogICogRG93bmxvYWQgdGhlIEV4Y2VsIGZpbGUgY2FsbGVkIGBwYXlvdXRzLnhsc3hgIGZyb20gdGhpcyBsaW5rOiA8aHR0cHM6Ly9naXRodWIuY29tL2RhdGFkZXNrL2xhLXNldHRsZW1lbnRzLWFuYWx5c2lzL3Jhdy9tYXN0ZXIvaW5wdXQvcGF5b3V0cy54bHN4PiBhbmQgcHV0IGl0IGluIHlvdXIgZm9sZGVyLiAKICAqIFR3byAibG9va3VwIHRhYmxlcyIgdGhhdCB0aGUgTEFUIG1hZGUgdG8gc3RhbmRhcmRpemUgc29tZSBvZiB0aGUgaW5mb3JtYXRpb24gYXJlIGluIGNzdidzOiA8aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2RhdGFkZXNrL2xhLXNldHRsZW1lbnRzLWFuYWx5c2lzL21hc3Rlci9pbnB1dC9jYXNldHlwZXMuY3N2PiBhbmQgPGh0dHBzOi8vZ2l0aHViLmNvbS9kYXRhZGVzay9sYS1zZXR0bGVtZW50cy1hbmFseXNpcy9ibG9iL21hc3Rlci9pbnB1dC9kZXBhcnRtZW50cy5jc3Y+CiAgCgoKIyMgUmVhZGluZyBkYXRhIAoKVGhyZWUgdHlwaWNhbCB3YXlzIHRvIGdldCBkYXRhIGluOiAKCiogTG9hZCBhbiBSIGZpbGUKKiBJbXBvcnQgYSBjc3YgKG9yIG9uZSBvZiBpdHMgY291c2lucykKKiBJbXBvcnQgYW4gRXhjZWwgZmlsZQoKUiBhbmQgY3N2IGZpbGVzIGNhbiBiZSByZWFkIGZyb20gbGlua3MuIEV4Y2VsIGZpbGVzIGNhbid0LiAKCldoZW4geW91IGltcG9ydCBhIGZpbGUsIHlvdSB3aWxsIGxpa2VseSBoYXZlIHRvIGNoZWNrIG9uIGEgZmV3IHRoaW5nczoKCiogV2VyZSB0aGUgZGF0YSB0eXBlcyBvZiBlYWNoIGNvbHVtbiBwcm9wZXJseSByZWFkPyBGb3IgZXhhbXBsZSwgaXMgYSBkYXRlIGFjdHVhbGx5IGEgZGF0ZSwgb3IgaXMgYSB0ZXh0IGZpZWxkIGluY29ycmVjdGx5IHNlZW4gYXMgYSBudW1iZXI/IAoKKiBBcmUgdGhlIG5hbWVzIG9mIHRoZSBjb2x1bW5zIGRpZmZpY3VsdCB0byB3b3JrIHdpdGggaW4gUj8KCiMjIyBJbXBvcnQgYSBjc3YKCkZpcnN0IHdlJ2xsIGltcG9ydCB0aGUgUGhvZW5peCBjaXR5IHNhbGFyaWVzIGZvciAyMDIwLiBZb3UnbGwgaGF2ZSB0byBkZXNjcmliZSB3aGF0IHRoZSBmb2xsb3dpbmcgY29kZSBkb2VzLCBpbiB5b3VyIG93biB3b3JkczogCgoKU29tZXRpbWVzLCBhcyBpbiB0aGlzIGV4YW1wbGUsIGl0J3MgdXNlZnVsIHRvIGNyZWF0ZSBhIHNpbXBsZSB2YXJpYWJsZSB0aGF0IGhvbGRzIGp1c3Qgb25lIHRoaW5nIHRvIHVzZSBpbiBhbm90aGVyIGNvbW1hbmQuIEluIHRoaXMgY2FzZSwgaXQncyBhIGxvbmcgbGluayBjb3BpZWQgZnJvbSB0aGUgV2ViLiBJZiB5b3Ugd2FudGVkIHRvIGNoYW5nZSBpdCwgYWxsIHlvdSdkIGhhdmUgdG8gZG8gaXMgY2hhbmdlIHRoYXQgbGluZSBhbmQgaXQgd291bGQgcHJvcGFnYXRlIHRvIGV2ZXJ5d2hlcmUgeW91J3ZlIHVzZWQgaXQuIAoKYGBge3IgaW1wb3J0X2Nzdl9waHgyMDIwfQoKcGh4XzIwMjBfc2FsYXJ5X2xpbmsgPC0gImh0dHBzOi8vd3d3LnBob2VuaXhvcGVuZGF0YS5jb20vZGF0YXNldC8wYzAyYmJmZC0wOGQ5LTQxZTktYTAwYy05Nzg0OGE5ZjczNTAvcmVzb3VyY2UvNGQ4Yzk4MDAtODA1NC00YzExLThhMTMtYjliNWJkMDUxMWRjL2Rvd25sb2FkL29wZW4tZGF0YS1wb3J0YWwtc3RhZmYtc2FsYXJpZXMtZGVjLTIwMjAuY3N2IgoKcmVhZF9jc3YoIHBoeF8yMDIwX3NhbGFyeV9saW5rKQoKYGBgCkhtbW0uIFRoYXQgcmVhZCBpdCwgYnV0IGl0IGNhbWUgb3V0IHdpdGggc29tZSB3ZWlyZCBtZXNzYWdlcyBhbmQgSSBkb24ndCBoYXZlIGEgZGF0YSBmcmFtZS4gCgpXaHk/IAoKYGBge3J9CgpwaHhfMjAyMCA8LSByZWFkX2NzdihwaHhfMjAyMF9zYWxhcnlfbGluaykKCmBgYApJIHN0aWxsIGhhdmUgdGhlIHNhbWUgd2VpcmQgbWVzc2FnZXMsIGJ1dCB0aGV5IGFwcGVhciBvazogRXZlcnl0aGluZyBidXQgdGhlIGBBTk5VQUwgUkFURWAgaGFzIGJlZW4gcmVhZCBhcyB0ZXh0LCBhbmQgdGhlIGFtb3VudCBoYXMgYmVlbiByZWFkIGFzIGEgbnVtYmVyLiBJbnN0ZWFkIG9mIG1ha2luZyB0aGlzIHRoZSBkZWZhdWx0LCBsZXQncyBtYWtlIGl0IGNsZWFyIHdoYXQgd2Ugd2FudCBpdCB0byBkbzogCgpgYGB7cn0KCnBoeF8yMDIwIDwtIHJlYWRfY3N2KHBoeF8yMDIwX3NhbGFyeV9saW5rLCAKICAgICAgICAgICAgICAgICAgICAgY29sX3R5cGVzID0gY29scyAoIC5kZWZhdWx0PSJjIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQU5OVUFMIFJBVEUiID0gIm4iKSkKCgpgYGAKCk5vIG1vcmUgbWVzc2FnZS4gCgpUaGUgbmFtZXMgYXJlIHN0aWxsIGF3ZnVsIGJlY2F1c2UgdGhlcmUgYXJlIHNwYWNlcyBpbiB0aGVtLiBDaGFuZ2UgdGhlbSB0byBsb3dlciBjYXNlIGFuZCByZW1vdmUgdGhlIHNwYWNlcyB1c2luZyBhIHNob3J0Y3V0IGZvciByZW5hbWluZyB2YXJpYWJsZXMgaW4gYSBzdGFuZGFyZCBmb3JtYXQsIGZyb20gdGhlIGphbml0b3IgbGlicmFyeTogCgpgYGB7ciByZWFkX2Nzdl9waHh9CgpwaHhfMjAyMCA8LSByZWFkX2NzdihwaHhfMjAyMF9zYWxhcnlfbGluaywgCiAgICAgICAgICAgICAgICAgICAgIGNvbF90eXBlcyA9IGNvbHMgKCAuZGVmYXVsdD0iYyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkFOTlVBTCBSQVRFIiA9ICJuIikpICU+JQogIGNsZWFuX25hbWVzKCkKCgpgYGAKCgpUaGlzIGlzIHRoZSBzYW1lIHRoaW5nIGFzIGRvaW5nOiAKCiAgICAgIHJlbmFtZSAoIGxhc3RfbmFtZSA9IExBU1RfTkFNRSwgCiAgICAgICAgICAgICAgICBmaXJzdF9uYW1lID0gRklSU1RfTkFNRQogICAgICAgICAgICAgICAgIGV0Yy4pCgoKIyMjIyBSZWFkIGFuIEV4Y2VsIGZpbGUKCllvdSBkb24ndCBoYXZlIHRoZSBzYW1lIG9wdGlvbnMgb2YgY29udHJvbGxpbmcgdGhlIGlucHV0IGZyb20gRXhjZWwuIFlvdSBqdXN0IGhhdmUgdG8gcHJldHR5IG11Y2ggbGl2ZSB3aXRoIHdoYXQgaXQgZ3Vlc3NlcywgdGhlbiB3b3JrIHdpdGggaXQgbGF0ZXIuIAoKTm90aWNlIHRoYXQgd2UgZG9uJ3QgaGF2ZSBhIGxpbmssIGJlY2F1c2UgaXQgd2FzIHNhdmVkIGludG8geW91ciBkZWZhdWx0IGZvbGRlci4gSG93ZXZlciwgdGhlIGZpcnN0IGxpbmUgb2YgdGhlIHNwcmVhZHNoZWV0IGNvbnRhaW5zIGEgdGl0bGUgLS0gaGVyZSwgeW91IG5lZWQgdG8gc2tpcCBvbmUgbGluZToKCmBgYHtyIHJlYWRfZXhjZWxfcGF5b3V0c30KCnBheW91dHMgPC0gcmVhZF9leGNlbCgicGF5b3V0cy54bHN4Iiwgc2tpcD0xKSAlPiUKICBjbGVhbl9uYW1lcygpCgpgYGAKCiMjIyMgUmVhZCBhbiBSIGZpbGUKClIgZmlsZXMgY2FuIGJlIGNhbGxlZCBzZXZlcmFsIHRoaW5ncyAtLSB5b3UnbGwgbW9zdCBmcmVxdWVudGx5IHNlZSB0aGVtIGFzIC5SZGEgb3IgLlJkYXRhLiBUaGVzZSBkb24ndCBnZXQgaW1wb3J0ZWQsIHRoZXkganVzdCBnZXQgbG9hZGVkLiBMb2FkIGZyb20gdGhlIHdlYiBieSB1c2luZyB0aGUgYHVybCgpYCBmdW5jdGlvbi4gVGhleSBjcmVhdGUgZGF0YSBmcmFtZXMgaW4geW91ciBlbnZpcm9ubWVudCB3aXRob3V0IG5hbWluZyB0aGVtLCBhbmQgdGhlcmUgY2FuIGJlIGFsbCBraW5kcyBvZiBvYmplY3RzIGluIHRoZW0uIFRoaW5rIG9mIHRoZW0gYXMgYSB6aXAgZmlsZSB0aGF0J3MgYmVlbiB1bnppcHBlZCByaWdodCBpbnRvIFI6CgpgYGB7ciBsb2FkX3BwcH0KCmxvYWQgKCB1cmwgKCJodHRwczovL2dpdGh1Yi5jb20vY3JvbmtpdGVkYXRhL3JzdHVkeWd1aWRlL2Jsb2IvbWFzdGVyL2RhdGEvYXpfcHBwX3ppcGNvZGVzLlJkYT9yYXc9dHJ1ZSIpKQoKYGBgCgoKPGRpdiBzdHlsZT0iZm9udC13ZWlnaHQ6Ym9sZDtjb2xvcjpyZWQ7Ij5XaGF0IGlzIHRoZSBub3VuIGZvciB0aGlzIGRhdGFzZXQ/IEllLCB3aGF0IGRvZXMgZWFjaCByb3cgcmVwcmVzZW50PzwvZGl2PiAKCgojIyBGaWx0ZXIgYW5kIGFycmFuZ2UKCgpIZXJlIGFyZSBhIGZldyBleGFtcGxlIHF1ZXN0aW9ucyB5b3UgbWlnaHQgZ2V0IGFib3V0IGEgZGF0YXNldCBsaWtlIHRoZSBQUFAgZGF0YTogCgoKPGRpdiBzdHlsZT0iZm9udC13ZWlnaHQ6Ym9sZDtjb2xvcjpyZWQ7Ij4KCmEpIEluIHlvdXIgb3duIHdvcmRzLCB3aGF0IGRvZXMgdGhpcyBjb2RlIGRvPyBNYWtlIHN1cmUgeW91ciBkZXNjcmlwdGlvbiBpcyBzb21ldGhpbmcgdGhhdCBhIG5vcm1hbCBwZXJzb24gd2l0aCBubyBiYWNrZ3JvdW5kIGluIGRhdGEgd291bGQgdW5kZXJzdGFuZC4gCgpiKSBXaHkgIG1pZ2h0IGEgcmVwb3J0ZXIgd2FudCB0byBrbm93IHRoaXM/IAoKYykgdHJ5IHRvIHdyaXRlIGEgc2hvcnQsIGRlY2xhcmF0aXZlIHNlbnRlY2UgdGhhdCBjb252ZXlzIHRoZSBlc3NlbmNlIG9mIHRoZSByZXN1bHRzLiBUaGlzIGlzIHRoZSBlcXVpdmFsZW50IG9mIGEgc3RyYWlnaHQgbGVkZSB0aGF0IGFjY3VyYXRlbHkgZGVzY3JpYmVzIHRoZSBmaW5kaW5ncyB0aGF0IHdvdWxkIGJlIHVuZGVyc3RhbmRhYmxlIHRvIHNvbWVvbmUgd2hvIGtuZXcgbm90aGluZyBhYm91dCB0aGUgZGF0YSBhbmFseXNpcyAuIAoKWW91IHdvdWxkIGRvIHRoZSBzYW1lIHRoaW5nIGluIHRoZSBuZXh0IHRocmVlIGNodW5rcy4gCgo8L2Rpdj4KCgpgYGB7cn0KCmF6X3BwcF96aXAgJT4lCiAgc2VsZWN0ICggYm9ycm93ZXIsIGNpdHksIHN0YXRlLCB6aXAsIGRhdGVfYXBwcm92ZWQsIHN0YXR1c19kYXRlLCBpbml0aWFsX2FtdDpub25wYXlyb2xsX2FtdCApICU+JQogIGFycmFuZ2UgKCBkZXNjKG5vbnBheXJvbGxfYW10KSkgJT4lCiAgaGVhZCAoMTUpCgoKCmBgYAoKTnVtYmVyIDI6IAoKYGBge3J9Cgphel9wcHBfemlwICU+JQogIGZpbHRlciAoIHN0cl9kZXRlY3QgKCBuYWljc19zZWN0b3IsICJeNzIiKSAmIGRhdGVfYXBwcm92ZWQgPj0gIjIwMjAtMTItMjAiKSAlPiUKICBzZWxlY3QgKCBib3Jyb3dlciwgY2l0eSwgc3RhdGUsIHppcCwgZGF0ZV9hcHByb3ZlZCwgIGluaXRpYWxfYW10KSAlPiUKICBhcnJhbmdlICggZGVzYyAoIGRhdGVfYXBwcm92ZWQpICkgCgoKCmBgYAoKCgpOdW1iZXIgMzogCgpgYGB7cn0KCmF6X3BwcF96aXAgJT4lCiAgZmlsdGVyICggYnVzaW5lc3NfYWdlICVpbiUgYygiU1RBUlRVUCwgTE9BTiBGVU5EUyBXSUxMIE9QRU4gQlVTSU5FU1MiLCAiTkVXIEJVU0lORVNTIE9SIDIgWUVBUlMgT1IgTEVTUyIpICYKICAgICAgICAgICAgIGNpdHkgPT0gIlBIT0VOSVgiKQoKYGBgCgooQW4gYWx0ZXJuYXRpdmUgd2F5IHRvIGdldCB0aGUgc2FtZSB0aGluZyB1c2luZyB0aGUgd2lsZGNhcmQgcmVndWxhciBleHByZXNzaW9uIG9mICJeU1RBUlRVUHxORVciIHdoZXJlICdeJyBtZWFucyAiaXQgc2hvdWxkIHN0YXJ0IHdpdGggdGhpcyIgYW5kIGB8YCBtZWFucyAiZWl0aGVyIG9uZSBvZiB0aGVzZSB0aGluZ3MuIikKCmBgYHtyfQoKCmF6X3BwcF96aXAgJT4lCiAgZmlsdGVyICggc3RyX2RldGVjdCAoIGJ1c2luZXNzX2FnZSwgIl5TVEFSVFVQfE5FVyIpICYKICAgICAgICAgICAgIGNpdHkgPT0gIlBIT0VOSVgiKQoKCmBgYAoKPGRpdiBzdHlsZT0iZm9udC13ZWlnaHQ6Ym9sZDtjb2xvcjpyZWQ7Ij4KCldoYXQgaXMgYW5vdGhlciBxdWVzdGlvbiB0aGF0IGNhbiBiZSBhbnN3ZXJlZCB1c2luZyBzZWxlY3QsIGZpbHRlciBhbmQgYXJyYW5nZSB2ZXJicyB0aGF0IGEgcmVwb3J0ZXIgbWlnaHQgYXNrIG9mIHRoZSBkYXRhPyBXcml0ZSB5b3VyIHF1ZXN0aW9uIGluIHBsYWluIEVuZ2xpc2gsIG5vdCBpbiBjb21wdXRlci1lc2UsIGFuZCBtYWtlIGl0IGNsZWFyIHdoeSBhIHJlcG9ydGVyIG1pZ2h0IHdhbnQgdG8ga25vdyB0aGlzLiAKCjwvZGl2PgoKCiMjIE11dGF0ZQoKTk9URTogTG9va2luZyBhdCBpdCB0b2RheSwgdGhlcmUgaXMgYSBwcm9ibGVtIGluIHRoZSB3YXkgdGhlIGdvdmVybm1lbnQgaXMgcmVwb3J0aW5nIHRoZSBub24tcGF5cm9sbCBhbW91bnQgaW4gdGhpcyBzZWNvbmQgZHJhdyBvZiBtb25leSAoc2luY2UgRGVjZW1iZXIpLiBUaGVyZSBhcmUgbm8gbm9uLXBheXJvbGwgYW1vdW50cywgZXhjZXB0IHRoZXJlIGlzIHNvbWV0aW1lcyBhICIxIiBpbiB0aGUgdXRpbGl0aWVzIGFtb3VudC4gSSBkb24ndCBrbm93IGlmIHRoYXQncyBtZWFudCBhcyBhIFRSVUUtRkFMU0UgaW5kaWNhdG9yLCBvciB3aGF0LCBidXQgaXQncyBub3QgcmVsaWFibGUgYXQgdGhpcyB0aW1lLiAgKEkndmUgY2hlY2tlZCwgYW5kIGl0J3MgbGlrZSB0aGF0IGluIHRoZSBvcmlnaW5hbCBkYXRhIGZpbGUgYXMgcHVibGlzaGVkIGJ5IFNCQSwgd2hpY2ggaGFzbid0IGJlZW4gdXBkYXRlZCBzaW5jZSBJIGRvd25sb2FkZWQgaXQuICkgIAoKQW5zd2VyIHRoZSBzYW1lIHF1ZXN0aW9ucyBhcyBhYm92ZSBmb3IgdGhpcyBzZXQgb2YgY29kZSBjaHVua3MgOiAKCmBgYHtyfQoKYXpfcHBwX3ppcCAlPiUKICBtdXRhdGUgKCBkb2xsYXJzX3Blcl9qb2IgPSBwYXlyb2xsX2FtdCAvIGpvYnNfcmVwb3J0ZWQsIAogICAgICAgICAgIG5ld19idXNfdHlwZSA9IGNhc2Vfd2hlbiAoIHN0cl9kZXRlY3QgKCBidXNpbmVzc190eXBlLCAiKFNPTEUgUFJPUHxTRUxGLUVNUExPWUVEfElOREVQRU5ERU5UKSIgKSB+ICJTRUxGIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vbl9wcm9maXQgPT0gIlkiIH4gIk5PTi1QUk9GSVQiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICJGT1ItUFJPRklUIikKICAgICAgICAgICApICU+JQogIHNlbGVjdCAoYm9ycm93ZXIsIG5ld19idXNfdHlwZSwgaW5pdGlhbF9hbXQsIGRvbGxhcnNfcGVyX2pvYikKICAKCgpgYGAKCgojIyBHUk9VUCBCWSAsIFNVTU1BUklTRQoKU2FtZSBxdWVzdGlvbnMgZm9yIHRoZXNlIGNvZGUgY2h1bmtzOiAKCihUaGlzIG9uZSB3aWxsIHRha2UgYSBtaW51dGUgdG8gcnVuLiBUaGUgZ3JvdXAgYnkgaGVyZSBpcyBzbG93LikKCmBgYHtyIGdyb3VwaW5nMX0KCmJvdGhfZHJhd3MgPC0gCiAgYXpfcHBwX3ppcCAlPiUKICBncm91cF9ieSAoIGJvcnJvd2VyLCB6aXApICU+JQogIHN1bW1hcmlzZSAoIG51bV9vZl9sb2FucyA9IG4oKSwgCiAgICAgICAgICAgICAgYW1vdW50ID0gc3VtIChpbml0aWFsX2FtdCwgbmEucm09VCksIAogICAgICAgICAgICAgIC5ncm91cHM9ImRyb3AiCiAgICAgICAgICAgICkgJT4lCiAgZmlsdGVyICggbnVtX29mX2xvYW5zID4gMSkgJT4lCiAgYXJyYW5nZSAoIHppcCkKICAKICAKCgpgYGAKCihEb24ndCB3b3JyeSBhYm91dCB0aGF0IC5ncm91cHM9ImRyb3AiIHN0YXRlbWVudCAtIGl0IGp1c3QgZ2V0cyByaWQgb2Ygc29tZXRoaW5nIHRoYXQgd2lsbCBiZSBhIHBhaW4gbGF0ZXIgb24uICkKCkFkZGl0aW9uYWwgcXVlc3Rpb246IAoKTm93IHRoYXQgeW91IGhhdmUgdGhpcyBsaXN0LCB3aGF0IG1pZ2h0IHlvdSBkbyB3aXRoIGl0PyBMZXQncyBkaXNjdXNzIGluIHRoZSBzZXNzaW9uLiAKCmBgYHtyIGdyb3VwaW5nMn0KCnpjdGFfdG90YWxzIDwtIAogIGF6X3BwcF96aXAgJT4lCiAgZ3JvdXBfYnkgKGNlbnN1c196aXAgKSAlPiUKICBzdW1tYXJpc2UgKCBudW1iZXJfb2ZfbG9hbnMgPSBuKCkgLCAKICAgICAgICAgICAgICBsb2FuX2Ftb3VudCA9IHN1bShpbml0aWFsX2FtdCwgbmEucm09VCksIAogICAgICAgICAgICAgIHBheXJvbGxfYW1vdW50ID0gc3VtKCBwYXlyb2xsX2FtdCwgbmEucm09VCksIAogICAgICAgICAgICAgIGpvYnNfc3VwcG9ydGVkID0gc3VtKGpvYnNfcmVwb3J0ZWQsIG5hLnJtPVQpKSAKCgoKYGBgCgoKTmV4dCBvbmU6IAoKYGBge3J9Cgp6Y3RhX3RvdGFscyAlPiUKICBtdXRhdGUgKCBwY3Rfb2ZfbG9hbnMgPSBudW1iZXJfb2ZfbG9hbnMgLyBzdW0obnVtYmVyX29mX2xvYW5zKSAqIDEwMCkgJT4lCiAgYXJyYW5nZSAoIGRlc2MgKGxvYW5fYW1vdW50KSkKCgpgYGAKCk5leHQgb25lOiAKCmBgYHtyfQoKYXpfcHBwX3ppcCAlPiUKICBtdXRhdGUgKCBwcm9ncmFtID0gaWZfZWxzZSAoIGRhdGVfYXBwcm92ZWQgPCAiMjAyMC0xMi0yMCIsICJGaXJzdCBkcmF3IiwgIlNlY29uZCBkcmF3IikpICAlPiUKICBncm91cF9ieSAoIGNlbnN1c196aXAsIHByb2dyYW0pICU+JQogIHN1bW1hcmlzZSAoIG51bWJlcl9vZl9sb2FucyA9IG4oKSApICU+JQogIHBpdm90X3dpZGVyICggdmFsdWVzX2Zyb20gPSBudW1iZXJfb2ZfbG9hbnMsIHZhbHVlc19maWxsPTAsIG5hbWVzX2Zyb20gPSBwcm9ncmFtKSAlPiUKICBjbGVhbl9uYW1lcygpICU+JQogIGZpbHRlciAoIHNlY29uZF9kcmF3IC8gZmlyc3RfZHJhdyA+IC41ICYgZmlyc3RfZHJhdyA+IDIwKQogIApgYGAKCiMjIEpPSU4gCgpTYW1lIHF1ZXN0aW9ucyAKCmBgYHtyfQoKYXpfcHBwX2J5emlwdHlwZSA8LSAKICBhel9wcHBfemlwICU+JQogIGxlZnRfam9pbiAoIGF6X2J5X3ppcGNvZGUsIGJ5PWMoImNlbnN1c196aXAiPSJ6Y3RhIikpICU+JQogIGdyb3VwX2J5ICggbGVuZGVyLCB6Y3RhX2V0aG5pYykgJT4lCiAgc3VtbWFyaXNlIChudW1iZXJfb2ZfbG9hbnMgPSBuKCkgKSAlPiUKICBtdXRhdGUgKGxlbmRlcl90b3RhbCA9IHN1bShudW1iZXJfb2ZfbG9hbnMpKSAlPiUKICBwaXZvdF93aWRlciAoIG5hbWVzX2Zyb20gPSB6Y3RhX2V0aG5pYywgdmFsdWVzX2Zyb20gPSBudW1iZXJfb2ZfbG9hbnMsIHZhbHVlc19maWxsPTApICU+JQogIG11dGF0ZSAod2hpdGVfYXNfcGN0ID0gYDAxIC0gTWFqb3JpdHkgd2hpdGVgL2xlbmRlcl90b3RhbCAqIDEwMCkgJT4lCiAgZmlsdGVyIChsZW5kZXJfdG90YWwgPiAyMCkKICAKCmBgYAoKV2hhdCBtaWdodCB5b3UgZG8gd2l0aCB0aGlzIHRhYmxlPyAoVGhlcmUncyBvbmUgbG9naWNhbCBtaXN0YWtlIGluIHRoaXM6IEl0IGV4Y2x1ZGVzIGFueSB6aXAgY29kZXMgaW4gd2hpY2ggdGhlcmUgd2VyZSBubyBsb2FucyBnaXZlbi4gWW91J2QgaGF2ZSB0byBqb2luIGFnYWluIHRvIHRoZSB6aXAgY29kZSBsaXN0IGFuZCBmaWxsIGl0IHdpdGggemVyb3MgdG8gZ2V0IGl0IGV4YWN0LikKCkV4YW1wbGUgMjogCgpgYGB7cn0KCm9wdGlvbnMgKHNjaXBlbj05OTkpCgphel9wcHBfemlwICU+JQogIGdyb3VwX2J5IChjZW5zdXNfemlwKSAlPiUKICBzdW1tYXJpc2UgKCBudW1iZXJfb2ZfbG9hbnMgPSBuKCksIAogICAgICAgICAgICAgIG51bWJlcl9vZl9ib3Jyb3dlcnMgID0gbl9kaXN0aW5jdCAoIGJvcnJvd2VyLCB6aXApLCAKICAgICAgICAgICAgICB0b3RhbF9hbW91bnQgPSBzdW0oIGluaXRpYWxfYW10ICkKICApICU+JQogIHJpZ2h0X2pvaW4gKCBhel9ieV96aXBjb2RlLCBieT1jKCJjZW5zdXNfemlwIj0iemN0YSIpKSAlPiUKICBtdXRhdGUgKCBhbXRfcGVyX2NhcCA9IHRvdGFsX2Ftb3VudCAvIHRvdF9wb3AsIAogICAgICAgICAgICNZT1UgSEFWRU4nVCBTRUVOIFRISVMgQU5EIEFSRU5UIEVYUEVDVEVEIFRPIFVOREVSU1RBTkQgLSBJVCBJUyBUVVJOSU5HIE5BIElOVE8gMCBGT1IgQUxMIE5VTUVSSUMKICAgICAgICAgICBhY3Jvc3MgKCB3aGVyZSAoaXMubnVtZXJpYyksIHJlcGxhY2VfbmEsIDApCiAgICAgICAgICAgKSAlPiUKICBzZWxlY3QgKHpjdGFfZXRobmljLCAgdG90X3BvcCwgY2Vuc3VzX3ppcCwgemlwY29kZV9jaXR5LCBudW1iZXJfb2ZfbG9hbnM6IHRvdGFsX2Ftb3VudCwgYW10X3Blcl9jYXAsIGNvdW50eV9uYW1lLCAgbWVkaWFuX2luY18yMDE4KSAlPiUKICBmaWx0ZXIgKHRvdF9wb3AgPiAxMDApICU+JQogIGFycmFuZ2UgKCBhbXRfcGVyX2NhcCkKCgoKYGBgCgoKKEhlcmUncyB3aGVyZSB5b3Ugc2VlIGhvdyBtaXNzaW5nIHRoZSBvbmVzIHRoYXQgZ28gbm8gbG9hbnMgbWF0dGVycy4pCgojIyBBbm90aGVyIGpvaW4gZXhhbXBsZQoKV2UgYWxyZWFkeSBsb2FkZWQgaW4gdGhlIFBob2VuaXggc2FsYXJpZXMgZm9yIDIwMjAuIExldCdzIGxvYWQgaW4gdGhlIHByZXZpb3VzIHllYXIncyBkb2luZyBleGFjdGx5IHdoYXQgd2UgZGlkIGJlZm9yZS4gCgpgYGB7ciB9CnBoeF8yMDE5X3NhbGFyeV9saW5rIDwtICJodHRwczovL3d3dy5waG9lbml4b3BlbmRhdGEuY29tL2RhdGFzZXQvMGMwMmJiZmQtMDhkOS00MWU5LWEwMGMtOTc4NDhhOWY3MzUwL3Jlc291cmNlLzQyODBkNzBkLTFkYWUtNDVkZC04MTkwLTJiYTMyMTA5OTc5ZC9kb3dubG9hZC8yMDE5X3NhbGllcy5jc3YiCgpwaHhfMjAxOSA8LSByZWFkX2NzdiAoIHBoeF8yMDE5X3NhbGFyeV9saW5rLCBjb2xfdHlwZXMgPSBjb2xzICguZGVmYXVsdD0iYyIsICJBTk5VQUwgUkFURSIgPSAibiIpKSAlPiUgY2xlYW5fbmFtZXMoKQoKYGBgCgpMZXQncyB0cnkgYSBmdWxsIGpvaW4gLSB3ZSBoYXZlIHRvIHJlbmFtZSBjb2x1bW5zIHNvIHRoYXQgdGhleSBkb24ndCBnZXQgbWl4ZWQgdXAuIFRoaXMgaXMgcmVhbGx5IGRhbmdlcm91cywgYW5kIEkgYWx3YXlzIHJlZ3JldCBpdCwgc2luY2UgaWYgSSBtYWtlIGEgbWlzdGFrZSBJIGhhdmUgdG8gZ28gYmFjayB0byB0aGUgaW1wb3J0IHN0YXRlbWVudC4KCmBgYHtyfQpwaHhfMjAxOSA8LSAKICBwaHhfMjAxOSAlPiUKICByZW5hbWUgKCBkZXNjcjIwMTkgPSBkZXNjciwgYW5udWFsX3JhdGVfMjAxOSA9IGFubnVhbF9yYXRlKSAgJT4lCiAgc2VsZWN0IChsYXN0X25hbWU6YW5udWFsX3JhdGVfMjAxOSkKCnBoeF8yMDIwIDwtIApwaHhfMjAyMCAlPiUKICByZW5hbWUgKCBkZXNjcjIwMjAgPSBkZXNjciwgYW5udWFsX3JhdGVfMjAyMCA9IGFubnVhbF9yYXRlKQoKCgpgYGAKCk5vdyAtIHdoYXQgaGFwcGVucyB3aXRoIGEgImZ1bGwiIGpvaW4sIGluIG90aGVyIHdvcmRzIGtlZXBpbmcgZXZlcnl0aGluZz8gCgpgYGB7cn0KCnBoeF9ib3RoeWVhcnMgPC0gCiAgcGh4XzIwMjAgJT4lIGZ1bGxfam9pbiAoIHBoeF8yMDE5LCBieT1jKCJsYXN0X25hbWUiPSJsYXN0X25hbWUiLCAiZmlyc3RfbmFtZSI9ImZpcnN0X25hbWUiKSkKCgpgYGAKClRoZXJlJ3MgYSBsb3Qgb2YgZHVwbGljYXRlcyBpbiBoZXJlLCBidXQgdGhhdCdzIE9LIC0gd2hhdCBkbyB5b3UgdGhpbmsgdGhpcyBxdWVyeSBkb2VzPyBBbmQgd2h5IG1pZ2h0IGl0IGJlIGhlbHBmdWwgZm9yIGEgcmVwb3J0ZXI/IFdoYXQgYXJlIHRoZSBsaWtlbHkgbWlzdGFrZXMgaW4gaXQ/IAoKCmBgYHtyfQoKcGh4X2JvdGh5ZWFycyAlPiUKICBmaWx0ZXIgKGlzLm5hKCBkZXNjcjIwMjApICYgc3RyX2RldGVjdChkZXNjcjIwMTksICJQb2xpY2UiKSApCgpgYGAKCg==