Before we begin, you will need to install these packages

Now we load a few R packages

Motivation

Today we are going to talk about getting data, examples of common data formats, and useful tools to access data.

First let’s have a bit of a philosophical discussion about data.

“Raw” vs “Clean” data

As data analysts, this is what we wished data looked like whenever we start a project

However, the reality, is data is rarely in that form in comes in all types of “raw” formats that need to be transformed into a “clean” format.

For example, in field of genomics, raw data looks like something like this:

Or if you are interested in analyzing data from Twitter:

Or data from Electronic Healthcare Records (EHRs):

We all have our scary spreadsheet tales. Here is Jenny Bryan from RStudio and UBC actually asking for some of those spreasheet tales on twitter.

For example, this is an actual spreadsheet from Enron in 2001:

What do we mean by “raw” data?

From https://simplystatistics.org/2016/07/20/relativity-raw-data/ raw data is defined as data…

…if you have done no processing, manipulation, coding, or analysis of the data. In other words, the file you received from the person before you is untouched. But it may not be the rawest version of the data. The person who gave you the raw data may have done some computations. They have a different “raw data set”.

Where do data live?

Data lives anywhere and everywhere. Data might be stored simply in a .csv or .txt file. Data might be stored in an Excel or Google Spreadsheet. Data might be stored in large databases that require users to write special functions to interact with to extract the data they are interested in.

For example, you may have heard of the terms mySQL or MongoDB.

From Wikipedia, MySQL is defined as an open-source relational database management system (RDBMS). Its name is a combination of “My”, the name of co-founder Michael Widenius’s daughter,[7] and “SQL”, the abbreviation for Structured Query Language..

From Wikipeda, MongoDB is defined as “a free and open-source cross-platform document-oriented database program. Classified as a NoSQL database program, MongoDB uses JSON-like documents with schemata.”

So after reading that, we get the sense that there are multiple ways large databases can be structured, data can be formatted and interacted with. In addition, we see that database programs (e.g. MySQL and MongoDB) can also interact with each other.

We will learn more about SQL and JSON in a bit.

Best practices on sharing data

A great article in PeerJ was written titled How to share data for collaboration, in which the authors describe a set of guidelines for sharing data:

We highlight the need to provide raw data to the statistician, the importance of consistent formatting, and the necessity of including all essential experimental information and pre-processing steps carried out to the statistician. With these guidelines we hope to avoid errors and delays in data analysis. the importance of consistent formatting, and the necessity of including all essential experimental information and pre-processing steps carried out to the statistician.

It’s a great paper that describes the information you should pass to a statistician to facilitate the most efficient and timely analysis. Specifically:

  1. The raw data (or the rawest form of the data to which you have access)
    • Should not have modified, removed or summarized any data; Ran no software on data
    • e.g. strange binary file your measurement machine spits out
    • e.g. complicated JSON file you scrapped from Twitter Application Programming Interfaces (API)
    • e.g. hand-entered numbers you collected looking through a microscope
  2. A clean data set
    • This may or may not be transforming data into a tidy dataset, but possibly yes
  3. A code book describing each variable and its values in the clean or tidy data set.
    • More detailed information about the measurements in the data set (e.g. units, experimental design, summary choices made)
    • Doesn’t quite fit into the column names in the spreadsheet
    • Often reported in a .md, .txt or Word file.

  1. An explicit and exact recipe you used to go from 1 -> 2,3

Before we go get some data

First let’s talk about a few important things before we download any data.

Relative versus absolute paths

When you are starting a data analysis, you have already learned about the use of .Rproj files. When you open up a .Rproj file, RStudio changes the path (location on your computer) to the .Rproj location.

After opening up a .Rproj file, you can test this by

When you open up someone else’s R code or analysis, you might also see the setwd() function being used which explicitly tells R to change the absolute path or absolute location of which directory to move into.

For example, say I want to clone a GitHub repo from Roger, which has 100 R script files, and in every one of those files at the top is:

The problem is, if I want to use his code, I will need to go and hand-edit every single one of those paths (C:\Users\Roger\path\only\that\Roger\has) to the path that I want to use on my computer or wherever I saved the folder on my computer (e.g.  /Users/Stephanie/Documents/path/only/I/have).

  1. This is an unsustainable practice.
  2. I can go in and manually edit the path, but this assumes I know how to set a working directory. Not everyone does.

So instead of absolute paths:

A better idea is to use relative paths:

Within R, an even better idea is to use the here R package will recognize the top-level directory of a Git repo and supports building all paths relative to that. For more on project-oriented workflow suggestions, read this post from Jenny Bryan.

The here package

In her post, she writes

“I suggest organizing each data analysis into a project: a folder on your computer that holds all the files relevant to that particular piece of work.”

Instead of using setwd() at the top your .R or .Rmd file, she suggests:

  • Organize each logical project into a folder on your computer.
  • Make sure the top-level folder advertises itself as such. This can be as simple as having an empty file named .here. Or, if you use RStudio and/or Git, those both leave characteristic files behind that will get the job done.
  • Use the here() function from the here package to build the path when you read or write a file. Create paths relative to the top-level directory.
  • Whenever you work on this project, launch the R process from the project’s top-level directory. If you launch R from the shell, cd to the correct folder first.

Let’s test this out. We can use getwd() to see our current working directory path and the files available using list.file()

## [1] "/Users/shicks/Documents/github/teaching/jhu-advdatasci/2019/lectures"
## [1] "01-introduction-lecture.html"      
## [2] "01-introduction-lecture.Rmd"       
## [3] "03-elements-prinicples-success.pdf"
## [4] "04-gettingdata-api.html"           
## [5] "04-gettingdata-api.Rmd"            
## [6] "TypesOfQuestions.pdf"

OK so our current location is in the lectures sub-folder of the 2019 course repository. Let’s try using the here package.

## here() starts at /Users/shicks/Documents/github/teaching/jhu-advdatasci/2019
##  [1] "_navbar.yml"           "_site.yml"            
##  [3] "2019-advdatasci.Rproj" "additional.css"       
##  [5] "book"                  "data"                 
##  [7] "discussion.html"       "discussion.Rmd"       
##  [9] "homework.html"         "homework.Rmd"         
## [11] "homeworks"             "imgs"                 
## [13] "index.html"            "index.Rmd"            
## [15] "lectures"              "Makefile"             
## [17] "projects.html"         "projects.Rmd"         
## [19] "R_functions.sh"        "README.html"          
## [21] "README.md"             "resources.html"       
## [23] "resources.Rmd"         "site_libs"            
## [25] "slide_functions.R"     "styles.css"           
## [27] "syllabus.html"         "syllabus.Rmd"
## [1] "cameras.csv"    "Chinook.sqlite"

Now we see that using the here::here() function is a relative path (relative to the .Rproj file in our 2019 repository. We also see there is a cameras.csv file in the data folder. Let’s read it into R with the readr package.

## Parsed with column specification:
## cols(
##   address = col_character(),
##   direction = col_character(),
##   street = col_character(),
##   crossStreet = col_character(),
##   intersection = col_character(),
##   `Location 1` = col_character()
## )
## # A tibble: 80 x 6
##    address      direction street   crossStreet intersection   `Location 1` 
##    <chr>        <chr>     <chr>    <chr>       <chr>          <chr>        
##  1 GARRISON BL… E/B       "Garris… Wabash Ave  "Garrison \n … (39.341209, …
##  2 HILLEN ST &… W/B       "Hillen… Forrest St  "Hillen \n & … (39.29686, -…
##  3 EDMONDSON A… E/B       "Edmons… Woodbridge… "Edmonson\n  … (39.293453, …
##  4 YORK RD & G… S/B       "York R… Gitting Ave "York Rd \n &… (39.370493, …
##  5 RUSSELL ST … S/B       "Russel… Hamburg St  "Russell\n  &… (39.279819, …
##  6 S MARTIN LU… S/B       "MLK Jr… Pratt St    "MLK Jr. Blvd… (39.286027, …
##  7 ORLEANS ST … E/B       Orleans  Linwood Ave Orleans   & L… (39.295866, …
##  8 E NORTHERN … W/B       "Northe… Springlake… "Northern Pkw… (39.364311, …
##  9 W COLD SPRI… E/B       "Cold S… Roland Ave  "Cold Spring\… (39.343906, …
## 10 E NORTHERN … W/B       "Northe… York Road   "Northern Pkw… (39.365146, …
## # … with 70 more rows

We can also ask for the full paths for specific files

## [1] "/Users/shicks/Documents/github/teaching/jhu-advdatasci/2019/data/cameras.csv"

Finding and creating files locally

If you want to download a file, one way to use the file.exists(), dir.create() and list.files() functions.

  • file.exists(here("my", "relative", "path")) = logical test if the file exists
  • dir.create(here("my", "relative", "path")) = create a folder
  • list.files(here("my", "relative", "path")) = list contents of folder

Getting data

Downloading files

Let’s say we wanted to find out where are all the Fixed Speed Cameras in Baltimore?

To do this, we can use the Open Baltimore API which has information on the locations of fixed speed cameras in Baltimore.

In case you aren’t familiar with fixed speed cameras, the website states:

Motorists who drive aggressively and exceed the posted speed limit by at least 12 miles per hour will receive $40 citations in the mail. These citations are not reported to insurance companies and no license points are assigned. Notification signs will be placed at all speed enforcement locations so that motorists will be aware that they are approaching a speed check zone. The goal of the program is to make the streets of Baltimore safer for everyone by changing aggressive driving behavior. In addition to the eight portable speed enforcement units, the city has retrofitted 50 red light camera locations with the automated speed enforcement technology.

When we go to the website, we see that the data can be provided to us as a .csv file. To download in this data, we can do the following:

Alternatively, if we want to only download the file once each time we knit our reproducible report or homework or project, we can us wrap the code above into a !file.exists() function.

## [1] "cameras.csv"    "Chinook.sqlite"

Reading in CSV files

From there, we can read in the cameras.csv like we have already learned how to do using the readr::read_csv() function:

## Parsed with column specification:
## cols(
##   address = col_character(),
##   direction = col_character(),
##   street = col_character(),
##   crossStreet = col_character(),
##   intersection = col_character(),
##   `Location 1` = col_character()
## )
## # A tibble: 80 x 6
##    address      direction street   crossStreet intersection   `Location 1` 
##    <chr>        <chr>     <chr>    <chr>       <chr>          <chr>        
##  1 GARRISON BL… E/B       "Garris… Wabash Ave  "Garrison \n … (39.341209, …
##  2 HILLEN ST &… W/B       "Hillen… Forrest St  "Hillen \n & … (39.29686, -…
##  3 EDMONDSON A… E/B       "Edmons… Woodbridge… "Edmonson\n  … (39.293453, …
##  4 YORK RD & G… S/B       "York R… Gitting Ave "York Rd \n &… (39.370493, …
##  5 RUSSELL ST … S/B       "Russel… Hamburg St  "Russell\n  &… (39.279819, …
##  6 S MARTIN LU… S/B       "MLK Jr… Pratt St    "MLK Jr. Blvd… (39.286027, …
##  7 ORLEANS ST … E/B       Orleans  Linwood Ave Orleans   & L… (39.295866, …
##  8 E NORTHERN … W/B       "Northe… Springlake… "Northern Pkw… (39.364311, …
##  9 W COLD SPRI… E/B       "Cold S… Roland Ave  "Cold Spring\… (39.343906, …
## 10 E NORTHERN … W/B       "Northe… York Road   "Northern Pkw… (39.365146, …
## # … with 70 more rows

Reading in a JSON file using jsonlite

What is JSON?

JSON (or JavaScript Object Notation) is a file format that stores information in human-readable, organized, logical, easy-to-access manner.

For example, here is what a JSON file looks like:

Some features about JSON object:

  • JSON objects are surrounded by curly braces {}
  • JSON objects are written in key/value pairs
  • Keys must be strings, and values must be a valid JSON data type (string, number, object, array, boolean)
  • Keys and values are separated by a colon
  • Each key/value pair is separated by a comma

Using GitHub API

Let’s say we want to use the GitHub API to find out how many of my GitHub repositories have open issues?

We will use the jsonlite R package and the fromJSON() function to convert from a JSON object to a data frame.

We will read in a JSON file located at https://api.github.com/users/stephaniehicks/repos

The function fromJSON() has now converted the JSON file into a data frame with the names:

##  [1] "id"                "node_id"           "name"             
##  [4] "full_name"         "private"           "owner"            
##  [7] "html_url"          "description"       "fork"             
## [10] "url"               "forks_url"         "keys_url"         
## [13] "collaborators_url" "teams_url"         "hooks_url"        
## [16] "issue_events_url"  "events_url"        "assignees_url"    
## [19] "branches_url"      "tags_url"          "blobs_url"        
## [22] "git_tags_url"      "git_refs_url"      "trees_url"        
## [25] "statuses_url"      "languages_url"     "stargazers_url"   
## [28] "contributors_url"  "subscribers_url"   "subscription_url" 
## [31] "commits_url"       "git_commits_url"   "comments_url"     
## [34] "issue_comment_url" "contents_url"      "compare_url"      
## [37] "merges_url"        "archive_url"       "downloads_url"    
## [40] "issues_url"        "pulls_url"         "milestones_url"   
## [43] "notifications_url" "labels_url"        "releases_url"     
## [46] "deployments_url"   "created_at"        "updated_at"       
## [49] "pushed_at"         "git_url"           "ssh_url"          
## [52] "clone_url"         "svn_url"           "homepage"         
## [55] "size"              "stargazers_count"  "watchers_count"   
## [58] "language"          "has_issues"        "has_projects"     
## [61] "has_downloads"     "has_wiki"          "has_pages"        
## [64] "forks_count"       "mirror_url"        "archived"         
## [67] "disabled"          "open_issues_count" "license"          
## [70] "forks"             "open_issues"       "watchers"         
## [73] "default_branch"

How many are private repos? How many have forks?

## 
## FALSE 
##    30
## 
##  0  1  2  3  4  5  8 
## 20  4  2  1  1  1  1

What’s the most popular language?

## 
##       HTML JavaScript          R       Ruby      Shell        TeX 
##          7          1         11          2          1          2

To find out how many repos that I have with open issues, we can just create a table:

## 
##  0  1 
## 28  2

Whew! Not as many as I thought.

How many do you have?

Finally, I will leave you with a few other examples of using GitHub API:

Reading in XML or HTML files using rvest

Do we want to purchase a book on Amazon?

Next we are going to learn about what to do if your data is on a website (XML or HTML) formatted to be read by humans instead of R.

We will use the (really powerful) rvest R package to do what is often called “scraping data from the web”.

Before we do that, we need to set up a few things:

We’re going to be scraping this page: it just contains the (first page of) reviews of the ggplot2 book by Hadley Wickham.

We use the rvest package to download this page.

Now h is an xml_document that contains the contents of the page:

## {xml_document}
## <html lang="en-us" class="a-no-js" data-19ax5a9jf="dingo">
## [1] <head>\n<meta http-equiv="Content-Type" content="text/html; charset= ...
## [2] <body>\n<span id="cr-state-object" data-state='{"asin":"0387981403", ...

How can you actually pull the interesting information out? That’s where CSS selectors come in.

CSS Selectors

CSS selectors are a way to specify a subset of nodes (that is, units of content) on a web page (e.g., just getting the titles of reviews). CSS selectors are very powerful and not too challenging to master- here’s a great tutorial But honestly you can get a lot done even with very little understanding, by using a tool called SelectorGadget.

Install the SelectorGadget on your web browser. (If you use Chrome you can use the Chrome extension, otherwise drag the provided link into your bookmarks bar). Here’s a guide for how to use it with rvest to “point-and-click” your way to a working selector.

For example, if you just wanted the titles, you’ll end up with a selector that looks something like .a-text-bold span. You can pipe your HTML object along with that selector into the html_nodes function, to select just those nodes:

## {xml_nodeset (10)}
##  [1] <span class="">Must-have reference for R graphics</span>
##  [2] <span class="">Still a great package and highly worth learning - bu ...
##  [3] <span class="">Excellent</span>
##  [4] <span class="">Nice resource, but already out of date</span>
##  [5] <span class="">The best guide to the best graphics (I think) out th ...
##  [6] <span class="">Graphing in R</span>
##  [7] <span class="">Excellent content, poor adaptation to kindle</span>
##  [8] <span class="">Excellent R resource for the Kindle</span>
##  [9] <span class="">Great book, outdated</span>
## [10] <span class="">Indispensable resource for ggplot2 users</span>

But you need the text from each of these, not the full tags. Pipe to the html_text function to pull these out:

##  [1] "Must-have reference for R graphics"                                                          
##  [2] "Still a great package and highly worth learning - but the text is getting quite out of date."
##  [3] "Excellent"                                                                                   
##  [4] "Nice resource, but already out of date"                                                      
##  [5] "The best guide to the best graphics (I think) out there."                                    
##  [6] "Graphing in R"                                                                               
##  [7] "Excellent content, poor adaptation to kindle"                                                
##  [8] "Excellent R resource for the Kindle"                                                         
##  [9] "Great book, outdated"                                                                        
## [10] "Indispensable resource for ggplot2 users"

Now we’ve extracted something useful! Similarly, let’s grab the format (hardcover or paperback). Some experimentation with SelectorGadget shows it’s:

##  [1] "Format: Paperback"      "Format: Paperback"     
##  [3] "Format: Kindle Edition" "Format: Paperback"     
##  [5] "Format: Paperback"      "Format: Kindle Edition"
##  [7] "Format: Kindle Edition" "Format: Kindle Edition"
##  [9] "Format: Paperback"      "Format: Paperback"

Now, we may be annoyed that it always starts with Format:. Let’s introduce the stringr package.

##  [1] "Paperback"      "Paperback"      "Kindle Edition" "Paperback"     
##  [5] "Paperback"      "Kindle Edition" "Kindle Edition" "Kindle Edition"
##  [9] "Paperback"      "Paperback"

We could do similar exercise for extracting the number of stars and whether or not someone found a review useful. This would help us decide if we were interested in purchasing the book!

Reading in from SQLite database

Another important type of data you might interact with are databases (such as SQL or SQLite). There are several ways to query databases in R.

First, we will download a .sqlite database. This is a portable version of a SQL database. For our purposes, we will use the chinook sqlite database here. The database represents a “digital media store, including tables for artists, albums, media tracks, invoices and customers”.

From the Readme.md file:

Sample Data

Media related data was created using real data from an iTunes Library. It is possible for you to use your own iTunes Library to generate the SQL scripts, see instructions below. Customer and employee information was manually created using fictitious names, addresses that can be located on Google maps, and other well formatted data (phone, fax, email, etc.). Sales information is auto generated using random data for a four year period.

## [1] "cameras.csv"    "Chinook.sqlite"

The main workhorse packages that we will use are the DBI and dplyr packages. Let’s look at the DBI::dbConnect() help file

So we need a driver and one example is RSQLite::SQLite(). Let’s look at the help file

Ok so with RSQLite::SQLite() and DBI::dbConnect() we can connect to a SQLite database. Let’s try that with our Chinook.sqlite file that we downloaded. Chinook.sqlite

## <SQLiteConnection>
##   Path: /Users/shicks/Documents/github/teaching/jhu-advdatasci/2019/data/Chinook.sqlite
##   Extensions: TRUE

So we have opened up a connection with the SQLite database. Next, we can see what tables are available in the database using the dbListTables() function:

##  [1] "Album"         "Artist"        "Customer"      "Employee"     
##  [5] "Genre"         "Invoice"       "InvoiceLine"   "MediaType"    
##  [9] "Playlist"      "PlaylistTrack" "Track"

From RStudio’s website, there are several ways to interact with SQL Databases. One of the simplest ways that we will use here is to leverage the dplyr framework.

"The dplyr package now has a generalized SQL backend for talking to databases, and the new dbplyr package translates R code into database-specific variants. As of this writing, SQL variants are supported for the following databases: Oracle, Microsoft SQL Server, PostgreSQL, Amazon Redshift, Apache Hive, and Apache Impala. More will follow over time.

So if we want to query a SQL databse with dplyr, the benefit of usingdbplyr` is:

"You can write your code in dplyr syntax, and dplyr will translate your code into SQL. There are several benefits to writing queries in dplyr syntax: you can keep the same consistent language both for R objects and database tables, no knowledge of SQL or the specific SQL variant is required, and you can take advantage of the fact that dplyr uses lazy evaluation.

Let’s take a closer look at the conn database that we just connected to:

## 
## Attaching package: 'dbplyr'
## The following objects are masked from 'package:dplyr':
## 
##     ident, sql
## src:  sqlite 3.29.0 [/Users/shicks/Documents/github/teaching/jhu-advdatasci/2019/data/Chinook.sqlite]
## tbls: Album, Artist, Customer, Employee, Genre, Invoice, InvoiceLine,
##   MediaType, Playlist, PlaylistTrack, Track

You can think of the multiple tables similar to having multiple worksheets in a spreadsheet.

Let’s try interacting with one.

Querying with dplyr syntax

First, let’s look at the first ten rows in the Album table.

## # Source:   lazy query [?? x 3]
## # Database: sqlite 3.29.0
## #   [/Users/shicks/Documents/github/teaching/jhu-advdatasci/2019/data/Chinook.sqlite]
##    AlbumId Title                                 ArtistId
##      <int> <chr>                                    <int>
##  1       1 For Those About To Rock We Salute You        1
##  2       2 Balls to the Wall                            2
##  3       3 Restless and Wild                            2
##  4       4 Let There Be Rock                            1
##  5       5 Big Ones                                     3
##  6       6 Jagged Little Pill                           4
##  7       7 Facelift                                     5
##  8       8 Warner 25 Anos                               6
##  9       9 Plays Metallica By Four Cellos               7
## 10      10 Audioslave                                   8

The output looks just like a data.frame that we are familiar with. But it’s important to know that it’s not really a dataframe. For example, what about if we use the dim() function?

## [1] NA  3

Interesting! We see that the number of rows returned is NA. This is because these functions are different than operating on datasets in memory (e.g. loading data into memory using read_csv()). Instead, dplyr communicates differently with a SQLite database.

Let’s consider our example. If we were to use straight SQL, the following SQL query returns the first 10 rows from the Album table:

In the background, dplyr does the following:

  • translates your R code into SQL
  • submits it to the database
  • translates the database’s response into an R data frame

To better understand the dplyr code, we can use the show_query() function:

## <SQL>
## SELECT *
## FROM `Album`
## LIMIT 10

This is nice because instead of having to write the SQL query ourself, we can just use the dplyr and R syntax that we are used to.

However, the downside is that dplyr never gets to see the full Album table. It only sends our query to the database, waits for a response and returns the query. However, in this way we can interact with large datasets!

Many of the usual dplyr functions are available too:

  • select()
  • filter()
  • summarize()

and many join functions.

Ok let’s try some of the functions out. First, let’s count how many albums each artist has made.

## # Source:   lazy query [?? x 2]
## # Database: sqlite 3.29.0
## #   [/Users/shicks/Documents/github/teaching/jhu-advdatasci/2019/data/Chinook.sqlite]
##    ArtistId     n
##       <int> <int>
##  1        1     2
##  2        2     2
##  3        3     1
##  4        4     1
##  5        5     1
##  6        6     2
##  7        7     1
##  8        8     3
##  9        9     1
## 10       10     1

Next, let’s plot it.

Let’s also extract the first letter from each album and plot the frequency of each letter.

Other cool APIs

Huffington Post Opinion Polling data

The Huffington Post has an API which provides US opinion poll data on various political races and other non-political opinion polls.

There is an R package called pollstR which provides an easy user interface.

For example, the API has data on the Trump Job Approval

Here we use the pollster_charts_polls() function:

## Parsed with column specification:
## cols(
##   Approve = col_double(),
##   Disapprove = col_double(),
##   Undecided = col_double(),
##   poll_slug = col_character(),
##   survey_house = col_character(),
##   start_date = col_date(format = ""),
##   end_date = col_date(format = ""),
##   question_text = col_character(),
##   sample_subpopulation = col_character(),
##   observations = col_double(),
##   margin_of_error = col_double(),
##   mode = col_character(),
##   partisanship = col_character(),
##   partisan_affiliation = col_character()
## )

We can see what’s in the object:

## [1] "content"  "url"      "response"

The url links to the data itself

## [1] "https://elections.huffingtonpost.com/pollster/api/v2/charts/trump-job-approval/pollster-chart-poll-questions.tsv"

The content contains the polling data:

## # A tibble: 970 x 14
##    Approve Disapprove Undecided poll_slug survey_house start_date
##      <dbl>      <dbl>     <dbl> <chr>     <chr>        <date>    
##  1      49         50        NA rasmusse… Rasmussen    2018-12-03
##  2      43         48         8 yougov-e… YouGov/Econ… 2018-12-02
##  3      41         52         6 ipsos-re… Ipsos/Reute… 2018-11-28
##  4      48         50        NA rasmusse… Rasmussen    2018-11-28
##  5      40         56        NA gallup-2… Gallup       2018-11-26
##  6      40         52         9 yougov-e… YouGov/Econ… 2018-11-25
##  7      48         50        NA rasmusse… Rasmussen    2018-11-25
##  8      43         45        12 grinnell… Grinnell/Se… 2018-11-24
##  9      44         51         6 ipsos-re… Ipsos/Reute… 2018-11-21
## 10      38         60        NA gallup-2… Gallup       2018-11-18
## # … with 960 more rows, and 8 more variables: end_date <date>,
## #   question_text <chr>, sample_subpopulation <chr>, observations <dbl>,
## #   margin_of_error <dbl>, mode <chr>, partisanship <chr>,
## #   partisan_affiliation <chr>

This might be useful if you were ever interested in using polling data.

Summary

  • Best practices for sharing data
  • Best practices for downloading and reading in data
    • Relative versus absolute paths
    • Finding and creating files locally
  • Best practices for getting data
    • jsonlite for JSON (e.g. GitHub API)
    • rvest to grab all the exact elements you want (e.g. book reviews)
      • Check out selector gadget
    • DBI, RSQLite, dbplyr for interacting with SQLite databses
    • Other APIs
      • Huffington Post API

Other good R packages to know about

LS0tCnRpdGxlOiBHZXR0aW5nIERhdGEgYW5kIFVzaW5nIEFQSXMKZGF0ZTogU2VwdCAxNiwgMjAxOQpvdXRwdXQ6IAogICAgaHRtbF9kb2N1bWVudDoKICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICAgICAgdGhlbWU6IGNvc21vIAogICAgICAgIHRvYzogdHJ1ZQogICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgIGhpZ2hsaWdodDogdGFuZ28KICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCmZpZ193aWR0aDogNQpmaWdfaGVpZ2h0OiA1Ci0tLQoKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIAogICAgICAgICAgICAgICAgICAgICAgZmlnLmFsaWduID0gImNlbnRlciIsIG91dC53aWR0aCA9ICc5MCUnKQpgYGAKCkJlZm9yZSB3ZSBiZWdpbiwgeW91IHdpbGwgbmVlZCB0byBpbnN0YWxsCnRoZXNlIHBhY2thZ2VzCgpgYGB7cixldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKCJqc29ubGl0ZSIpCmluc3RhbGwucGFja2FnZXMoInJ2ZXN0IikKaW5zdGFsbC5wYWNrYWdlcygiREJJIikKaW5zdGFsbC5wYWNrYWdlcygiUlNRTGl0ZSIpCmluc3RhbGwucGFja2FnZXMoImRicGx5ciIpCmluc3RhbGwucGFja2FnZXMoInBvbGxzdFIiKQpgYGAKCk5vdyB3ZSBsb2FkIGEgZmV3IFIgcGFja2FnZXMKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGpzb25saXRlKQpsaWJyYXJ5KHJ2ZXN0KQpsaWJyYXJ5KHBvbGxzdFIpCmBgYAoKIyBNb3RpdmF0aW9uCgpUb2RheSB3ZSBhcmUgZ29pbmcgdG8gdGFsayBhYm91dCBnZXR0aW5nIGRhdGEsIApleGFtcGxlcyBvZiBjb21tb24gZGF0YSBmb3JtYXRzLCBhbmQgdXNlZnVsIAp0b29scyB0byBhY2Nlc3MgZGF0YS4gCgpGaXJzdCBsZXQncyBoYXZlIGEgYml0IG9mIGEgcGhpbG9zb3BoaWNhbCAKZGlzY3Vzc2lvbiBhYm91dCBkYXRhLiAKCiMjICJSYXciIHZzICJDbGVhbiIgZGF0YQoKQXMgZGF0YSBhbmFseXN0cywgdGhpcyBpcyB3aGF0IHdlIHdpc2hlZCBkYXRhIApsb29rZWQgbGlrZSB3aGVuZXZlciB3ZSBzdGFydCBhIHByb2plY3QKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9naXRodWIuY29tL2p0bGVlay9hZHZkYXRhc2NpL3Jhdy9tYXN0ZXIvaW1ncy90aWR5LWRhdGEtZXhhbXBsZS5wbmciKQpgYGAKCkhvd2V2ZXIsIHRoZSByZWFsaXR5LCBpcyBkYXRhIGlzIHJhcmVseSBpbiB0aGF0IApmb3JtIGluIGNvbWVzIGluIGFsbCB0eXBlcyBvZiBfInJhdyJfIGZvcm1hdHMgdGhhdCAKbmVlZCB0byBiZSB0cmFuc2Zvcm1lZCBpbnRvIGEgXyJjbGVhbiJfIGZvcm1hdC4gCgpGb3IgZXhhbXBsZSwgaW4gZmllbGQgb2YgZ2Vub21pY3MsIHJhdyBkYXRhIApsb29rcyBsaWtlIHNvbWV0aGluZyBsaWtlIHRoaXM6IAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL2dpdGh1Yi5jb20vanRsZWVrL2FkdmRhdGFzY2kvcmF3L21hc3Rlci9pbWdzL2Zhc3RxLnBuZyIpCmBgYAoKT3IgaWYgeW91IGFyZSBpbnRlcmVzdGVkIGluIGFuYWx5emluZyBkYXRhIGZyb20gClR3aXR0ZXI6IAoKYGBge3IsIGVjaG89RkFMU0V9CmtuaXRyOjppbmNsdWRlX2dyYXBoaWNzKCJodHRwczovL2dpdGh1Yi5jb20vanRsZWVrL2FkdmRhdGFzY2kvcmF3L21hc3Rlci9pbWdzL3R3aXR0ZXItYXBpLnBuZyIpCmBgYAoKT3IgZGF0YSBmcm9tIEVsZWN0cm9uaWMgSGVhbHRoY2FyZSBSZWNvcmRzIChFSFJzKTogCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vZ2l0aHViLmNvbS9qdGxlZWsvYWR2ZGF0YXNjaS9yYXcvbWFzdGVyL2ltZ3MvZWhyLnBuZyIpCmBgYAoKV2UgYWxsIGhhdmUgb3VyIHNjYXJ5IHNwcmVhZHNoZWV0IHRhbGVzLiBIZXJlIGlzIApKZW5ueSBCcnlhbiBmcm9tIFJTdHVkaW8gYW5kIFVCQyBhY3R1YWxseSBhc2tpbmcgCmZvciBzb21lIG9mIHRob3NlIHNwcmVhc2hlZXQgdGFsZXMgb24gdHdpdHRlci4gCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vZ2l0aHViLmNvbS9qdGxlZWsvYWR2ZGF0YXNjaS9yYXcvbWFzdGVyL2ltZ3Mvc3ByZWFkc2hlZXQtdGFsZXMucG5nIikKYGBgCgpGb3IgZXhhbXBsZSwgdGhpcyBpcyBhbiBhY3R1YWwgCltzcHJlYWRzaGVldCBmcm9tIEVucm9uIGluIDIwMDFdKGh0dHBzOi8vZ2l0aHViLmNvbS9qZW5ueWJjLzIwMTYtMDZfc3ByZWFkc2hlZXRzL2Jsb2IvbWFzdGVyLzIwMTYtMDZfdXNlUi1zdGFuZm9yZC5wZGYpOiAKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9naXRodWIuY29tL2p0bGVlay9hZHZkYXRhc2NpL3Jhdy9tYXN0ZXIvaW1ncy9lbnJvbi1zcHJlYWRzaGVldC5wbmciKQpgYGAKCiMjIyBXaGF0IGRvIHdlIG1lYW4gYnkgInJhdyIgZGF0YT8gCgpGcm9tIFtodHRwczovL3NpbXBseXN0YXRpc3RpY3Mub3JnLzIwMTYvMDcvMjAvcmVsYXRpdml0eS1yYXctZGF0YS9dKGh0dHBzOi8vc2ltcGx5c3RhdGlzdGljcy5vcmcvMjAxNi8wNy8yMC9yZWxhdGl2aXR5LXJhdy1kYXRhLykKcmF3IGRhdGEgaXMgZGVmaW5lZCBhcyBkYXRhLi4uIAoKPiAuLi5pZiB5b3UgaGF2ZSBkb25lIG5vIHByb2Nlc3NpbmcsIG1hbmlwdWxhdGlvbiwgY29kaW5nLCBvciBhbmFseXNpcyBvZiB0aGUgZGF0YS4gSW4gb3RoZXIgd29yZHMsIHRoZSBmaWxlIHlvdSByZWNlaXZlZCBmcm9tIHRoZSBwZXJzb24gYmVmb3JlIHlvdSBpcyB1bnRvdWNoZWQuIEJ1dCBpdCBtYXkgbm90IGJlIHRoZSByYXdlc3QgdmVyc2lvbiBvZiB0aGUgZGF0YS4gVGhlIHBlcnNvbiB3aG8gZ2F2ZSB5b3UgdGhlIHJhdyBkYXRhIG1heSBoYXZlIGRvbmUgc29tZSBjb21wdXRhdGlvbnMuIFRoZXkgaGF2ZSBhIGRpZmZlcmVudCAicmF3IGRhdGEgc2V0Ii4KCiMjIFdoZXJlIGRvIGRhdGEgbGl2ZT8gCgpEYXRhIGxpdmVzIGFueXdoZXJlIGFuZCBldmVyeXdoZXJlLiBEYXRhIAptaWdodCBiZSBzdG9yZWQgc2ltcGx5IGluIGEgYC5jc3ZgIG9yIGAudHh0YApmaWxlLiBEYXRhIG1pZ2h0IGJlIHN0b3JlZCBpbiBhbiBFeGNlbCBvciAKR29vZ2xlIFNwcmVhZHNoZWV0LiBEYXRhIG1pZ2h0IGJlIHN0b3JlZCBpbiAKbGFyZ2UgZGF0YWJhc2VzIHRoYXQgcmVxdWlyZSB1c2VycyB0byB3cml0ZSAKc3BlY2lhbCBmdW5jdGlvbnMgdG8gaW50ZXJhY3Qgd2l0aCB0byBleHRyYWN0IAp0aGUgZGF0YSB0aGV5IGFyZSBpbnRlcmVzdGVkIGluLiAKCkZvciBleGFtcGxlLCB5b3UgbWF5IGhhdmUgaGVhcmQgb2YgdGhlIHRlcm1zIApgbXlTUUxgIG9yIGBNb25nb0RCYC4gCgpGcm9tIFtXaWtpcGVkaWEsIE15U1FMXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9NeVNRTCkgCmlzIGRlZmluZWQgYXMgX2FuIG9wZW4tc291cmNlIHJlbGF0aW9uYWwgZGF0YWJhc2UgbWFuYWdlbWVudCBzeXN0ZW0gKFJEQk1TKS4gSXRzIG5hbWUgaXMgYSBjb21iaW5hdGlvbiBvZiAiTXkiLCB0aGUgbmFtZSBvZiBjby1mb3VuZGVyIE1pY2hhZWwgV2lkZW5pdXMncyBkYXVnaHRlcixbN10gYW5kICJTUUwiLCB0aGUgYWJicmV2aWF0aW9uIGZvciBTdHJ1Y3R1cmVkIFF1ZXJ5IExhbmd1YWdlLl8uIAoKRnJvbSBbV2lraXBlZGEsIE1vbmdvREJdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL01vbmdvREIpCmlzIGRlZmluZWQgYXMgXyJhIGZyZWUgYW5kIG9wZW4tc291cmNlIGNyb3NzLXBsYXRmb3JtIGRvY3VtZW50LW9yaWVudGVkIGRhdGFiYXNlIHByb2dyYW0uIENsYXNzaWZpZWQgYXMgYSBOb1NRTCBkYXRhYmFzZSBwcm9ncmFtLCBNb25nb0RCIHVzZXMgSlNPTi1saWtlIGRvY3VtZW50cyB3aXRoIHNjaGVtYXRhLiJfCgpTbyBhZnRlciByZWFkaW5nIHRoYXQsIHdlIGdldCB0aGUgc2Vuc2UgdGhhdCB0aGVyZQphcmUgbXVsdGlwbGUgd2F5cyBsYXJnZSBkYXRhYmFzZXMgY2FuIGJlIHN0cnVjdHVyZWQsIApkYXRhIGNhbiBiZSBmb3JtYXR0ZWQgYW5kIGludGVyYWN0ZWQgd2l0aC4gCkluIGFkZGl0aW9uLCB3ZSBzZWUgdGhhdCBkYXRhYmFzZSBwcm9ncmFtcyAKKGUuZy4gTXlTUUwgYW5kIE1vbmdvREIpIGNhbiBhbHNvIGludGVyYWN0IAp3aXRoIGVhY2ggb3RoZXIuCgpgYGB7ciwgZWNobz1GQUxTRX0Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoImh0dHBzOi8vZ2l0aHViLmNvbS9qdGxlZWsvYWR2ZGF0YXNjaS9yYXcvbWFzdGVyL2ltZ3MvZGF0YWJhc2VzLnBuZyIpCmBgYAoKV2Ugd2lsbCBsZWFybiBtb3JlIGFib3V0IGBTUUxgIGFuZCBgSlNPTmAgaW4gYSBiaXQuIAoKIyBCZXN0IHByYWN0aWNlcyBvbiBzaGFyaW5nIGRhdGEKCkEgZ3JlYXQgYXJ0aWNsZSBpbiBQZWVySiB3YXMgd3JpdHRlbiAKdGl0bGVkIFtfSG93IHRvIHNoYXJlIGRhdGEgZm9yIGNvbGxhYm9yYXRpb25fXShodHRwczovL3BlZXJqLmNvbS9wcmVwcmludHMvMzEzOXY1LnBkZiksIAppbiB3aGljaCB0aGUgYXV0aG9ycyBkZXNjcmliZSBhIHNldCBvZiBndWlkZWxpbmVzCmZvciBzaGFyaW5nIGRhdGE6Cgo+IFdlIGhpZ2hsaWdodCB0aGUgbmVlZCB0byBwcm92aWRlIHJhdyBkYXRhIHRvIHRoZSBzdGF0aXN0aWNpYW4sIHRoZSBpbXBvcnRhbmNlIG9mIGNvbnNpc3RlbnQgZm9ybWF0dGluZywgYW5kIHRoZSBuZWNlc3NpdHkgb2YgaW5jbHVkaW5nIGFsbCBlc3NlbnRpYWwgZXhwZXJpbWVudGFsIGluZm9ybWF0aW9uIGFuZCBwcmUtcHJvY2Vzc2luZyBzdGVwcyBjYXJyaWVkIG91dCB0byB0aGUgc3RhdGlzdGljaWFuLiBXaXRoIHRoZXNlIGd1aWRlbGluZXMgd2UgaG9wZSB0byBhdm9pZCBlcnJvcnMgYW5kIGRlbGF5cyBpbiBkYXRhIGFuYWx5c2lzLiB0aGUgaW1wb3J0YW5jZSBvZiBjb25zaXN0ZW50IGZvcm1hdHRpbmcsIGFuZCB0aGUgbmVjZXNzaXR5IG9mIGluY2x1ZGluZyBhbGwgZXNzZW50aWFsIGV4cGVyaW1lbnRhbCBpbmZvcm1hdGlvbiBhbmQgcHJlLXByb2Nlc3Npbmcgc3RlcHMgY2FycmllZCBvdXQgdG8gdGhlIHN0YXRpc3RpY2lhbi4KCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9naXRodWIuY29tL2p0bGVlay9hZHZkYXRhc2NpL3Jhdy9tYXN0ZXIvaW1ncy9lbGxpcy1kYXRhc2hhcmUucG5nIikKYGBgCgpJdCdzIGEgZ3JlYXQgcGFwZXIgdGhhdCBkZXNjcmliZXMgdGhlIGluZm9ybWF0aW9uIAp5b3Ugc2hvdWxkIHBhc3MgdG8gYSBzdGF0aXN0aWNpYW4gdG8gZmFjaWxpdGF0ZSAKdGhlIG1vc3QgZWZmaWNpZW50IGFuZCB0aW1lbHkgYW5hbHlzaXMuIFNwZWNpZmljYWxseToKCjEuIFRoZSByYXcgZGF0YSAob3IgdGhlIHJhd2VzdCBmb3JtIG9mIHRoZSBkYXRhIHRvIHdoaWNoIHlvdSBoYXZlIGFjY2VzcykKICAgICogU2hvdWxkIG5vdCBoYXZlIG1vZGlmaWVkLCByZW1vdmVkIG9yIHN1bW1hcml6ZWQgYW55IGRhdGE7IFJhbiBubyBzb2Z0d2FyZSBvbiBkYXRhCiAgICAqIGUuZy4gc3RyYW5nZSBiaW5hcnkgZmlsZSB5b3VyIG1lYXN1cmVtZW50IG1hY2hpbmUgc3BpdHMgb3V0CiAgICAqIGUuZy4gY29tcGxpY2F0ZWQgSlNPTiBmaWxlIHlvdSBzY3JhcHBlZCBmcm9tIFR3aXR0ZXIgQXBwbGljYXRpb24gUHJvZ3JhbW1pbmcgSW50ZXJmYWNlcyAoQVBJKQogICAgKiBlLmcuIGhhbmQtZW50ZXJlZCBudW1iZXJzIHlvdSBjb2xsZWN0ZWQgbG9va2luZyB0aHJvdWdoIGEgbWljcm9zY29wZQoKMi4gQSBjbGVhbiBkYXRhIHNldAogICAgKiBUaGlzIG1heSBvciBtYXkgbm90IGJlIHRyYW5zZm9ybWluZyBkYXRhIGludG8gYSBgdGlkeWAgZGF0YXNldCwgYnV0IHBvc3NpYmx5IHllcwoKMy4gQSBjb2RlIGJvb2sgZGVzY3JpYmluZyBlYWNoIHZhcmlhYmxlIGFuZCBpdHMgdmFsdWVzIGluIHRoZSBjbGVhbiBvciB0aWR5IGRhdGEgc2V0LgogICAgKiBNb3JlIGRldGFpbGVkIGluZm9ybWF0aW9uIGFib3V0IHRoZSBtZWFzdXJlbWVudHMgaW4gdGhlIGRhdGEgc2V0IChlLmcuIHVuaXRzLCBleHBlcmltZW50YWwgZGVzaWduLCBzdW1tYXJ5IGNob2ljZXMgbWFkZSkKICAgICogRG9lc24ndCBxdWl0ZSBmaXQgaW50byB0aGUgY29sdW1uIG5hbWVzIGluIHRoZSBzcHJlYWRzaGVldAogICAgKiBPZnRlbiByZXBvcnRlZCBpbiBhIGAubWRgLCBgLnR4dGAgb3IgV29yZCBmaWxlLiAKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9naXRodWIuY29tL2p0bGVlay9hZHZkYXRhc2NpL3Jhdy9tYXN0ZXIvaW1ncy9jb2RlLWJvb2sucG5nIikKYGBgCgo0LiBBbiBleHBsaWNpdCBhbmQgZXhhY3QgcmVjaXBlIHlvdSB1c2VkIHRvIGdvIGZyb20gMSAtPiAyLDMKCmBgYHtyLCBlY2hvPUZBTFNFfQprbml0cjo6aW5jbHVkZV9ncmFwaGljcygiaHR0cHM6Ly9naXRodWIuY29tL2p0bGVlay9hZHZkYXRhc2NpL3Jhdy9tYXN0ZXIvaW1ncy9yZWNpcGUtYmVzdC5wbmciKQpgYGAKCiMgQmVmb3JlIHdlIGdvIGdldCBzb21lIGRhdGEKCkZpcnN0IGxldCdzIHRhbGsgYWJvdXQgYSBmZXcgaW1wb3J0YW50IHRoaW5ncyAKYmVmb3JlIHdlIGRvd25sb2FkIGFueSBkYXRhLiAKCiMjIFJlbGF0aXZlIHZlcnN1cyBhYnNvbHV0ZSBwYXRocwoKV2hlbiB5b3UgYXJlIHN0YXJ0aW5nIGEgZGF0YSBhbmFseXNpcywgeW91IGhhdmUKYWxyZWFkeSBsZWFybmVkIGFib3V0IHRoZSB1c2Ugb2YgYC5ScHJvamAgZmlsZXMuIApXaGVuIHlvdSBvcGVuIHVwIGEgYC5ScHJvamAgZmlsZSwgUlN0dWRpbyBjaGFuZ2VzIAp0aGUgcGF0aCAobG9jYXRpb24gb24geW91ciBjb21wdXRlcikgdG8gdGhlIGAuUnByb2pgIApsb2NhdGlvbi4gCgpBZnRlciBvcGVuaW5nIHVwIGEgYC5ScHJvamAgZmlsZSwgeW91IGNhbiB0ZXN0IHRoaXMKYnkKCmBgYHtyLCBldmFsPUZBTFNFfQpnZXR3ZCgpCmBgYAoKV2hlbiB5b3Ugb3BlbiB1cCBzb21lb25lIGVsc2UncyBSIGNvZGUgb3IgYW5hbHlzaXMsIAp5b3UgbWlnaHQgYWxzbyBzZWUgdGhlIGBzZXR3ZCgpYCBmdW5jdGlvbiBiZWluZyB1c2VkCndoaWNoIGV4cGxpY2l0bHkgdGVsbHMgUiB0byBjaGFuZ2UgdGhlIGFic29sdXRlIHBhdGggCm9yIGFic29sdXRlIGxvY2F0aW9uIG9mIHdoaWNoIGRpcmVjdG9yeSB0byBtb3ZlIGludG8uIAoKRm9yIGV4YW1wbGUsIHNheSBJIHdhbnQgdG8gY2xvbmUgYSBHaXRIdWIgcmVwbyBmcm9tIApSb2dlciwgd2hpY2ggaGFzIDEwMCBSIHNjcmlwdCBmaWxlcywgYW5kIGluIGV2ZXJ5IApvbmUgb2YgdGhvc2UgZmlsZXMgYXQgdGhlIHRvcCBpczogCgpgYGB7ciwgZXZhbD1GQUxTRX0Kc2V0d2QoIkM6XFVzZXJzXFJvZ2VyXHBhdGhcb25seVx0aGF0XFJvZ2VyXGhhcyIpCmBgYAoKVGhlIHByb2JsZW0gaXMsIGlmIEkgd2FudCB0byB1c2UgaGlzIGNvZGUsIEkgd2lsbCAKbmVlZCB0byBnbyBhbmQgaGFuZC1lZGl0IGV2ZXJ5IHNpbmdsZSBvbmUgb2YgdGhvc2UgCnBhdGhzIChgQzpcVXNlcnNcUm9nZXJccGF0aFxvbmx5XHRoYXRcUm9nZXJcaGFzYCkKdG8gdGhlIHBhdGggdGhhdCBJIHdhbnQgdG8gdXNlIG9uIG15IGNvbXB1dGVyIApvciB3aGVyZXZlciBJIHNhdmVkIHRoZSBmb2xkZXIgb24gbXkgY29tcHV0ZXIgKGUuZy4gCmAvVXNlcnMvU3RlcGhhbmllL0RvY3VtZW50cy9wYXRoL29ubHkvSS9oYXZlYCkuIAoKMS4gVGhpcyBpcyBhbiB1bnN1c3RhaW5hYmxlIHByYWN0aWNlLiAKMi4gSSBjYW4gZ28gaW4gYW5kIG1hbnVhbGx5IGVkaXQgdGhlIHBhdGgsIGJ1dCB0aGlzIAphc3N1bWVzIEkga25vdyBob3cgdG8gc2V0IGEgd29ya2luZyBkaXJlY3RvcnkuIE5vdCAKZXZlcnlvbmUgZG9lcy4gCgpTbyBpbnN0ZWFkIG9mIGFic29sdXRlIHBhdGhzOiAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXR3ZCgiL1VzZXJzL2p0bGVlay9kYXRhIikKc2V0d2QoIn4vRGVza3RvcC9maWxlcy9kYXRhIikKc2V0d2QoIkM6XFxVc2Vyc1xcQW5kcmV3XFxEb3dubG9hZHMiKQpgYGAKCkEgYmV0dGVyIGlkZWEgaXMgdG8gdXNlIHJlbGF0aXZlIHBhdGhzOiAKCmBgYHtyLCBldmFsPUZBTFNFfQpzZXR3ZCgiLi4vZGF0YSIpCnNldHdkKCIuLi9maWxlcyIpCnNldHdkKCIuLlx0bXAiKQpgYGAKCldpdGhpbiBSLCBhbiBldmVuIGJldHRlciBpZGVhIGlzIHRvIHVzZSB0aGUgCltoZXJlXShodHRwczovL2dpdGh1Yi5jb20vci1saWIvaGVyZSkKUiBwYWNrYWdlIHdpbGwgcmVjb2duaXplIHRoZSB0b3AtbGV2ZWwgZGlyZWN0b3J5IApvZiBhIEdpdCByZXBvIGFuZCBzdXBwb3J0cyBidWlsZGluZyBhbGwgcGF0aHMgCnJlbGF0aXZlIHRvIHRoYXQuIEZvciBtb3JlIG9uIHByb2plY3Qtb3JpZW50ZWQgCndvcmtmbG93IHN1Z2dlc3Rpb25zLCByZWFkIApbdGhpcyBwb3N0XShodHRwczovL3d3dy50aWR5dmVyc2Uub3JnL2FydGljbGVzLzIwMTcvMTIvd29ya2Zsb3ctdnMtc2NyaXB0LykKZnJvbSBKZW5ueSBCcnlhbi4KCiMjIyBUaGUgYGhlcmVgIHBhY2thZ2UKCkluIGhlciBwb3N0LCBzaGUgd3JpdGVzIAoKPiAiSSBzdWdnZXN0IG9yZ2FuaXppbmcgZWFjaCBkYXRhIGFuYWx5c2lzIGludG8gYSBwcm9qZWN0OiBhIGZvbGRlciBvbiB5b3VyIGNvbXB1dGVyIHRoYXQgaG9sZHMgYWxsIHRoZSBmaWxlcyByZWxldmFudCB0byB0aGF0IHBhcnRpY3VsYXIgcGllY2Ugb2Ygd29yay4iCgpJbnN0ZWFkIG9mIHVzaW5nIGBzZXR3ZCgpYCBhdCB0aGUgdG9wIHlvdXIgYC5SYCBvciBgLlJtZGAgZmlsZSwgc2hlIHN1Z2dlc3RzOiAKCiogT3JnYW5pemUgZWFjaCBsb2dpY2FsIHByb2plY3QgaW50byBhIGZvbGRlciBvbiB5b3VyIGNvbXB1dGVyLgoqIE1ha2Ugc3VyZSB0aGUgdG9wLWxldmVsIGZvbGRlciBhZHZlcnRpc2VzIGl0c2VsZiBhcyBzdWNoLiBUaGlzIGNhbiBiZSBhcyBzaW1wbGUgYXMgaGF2aW5nIGFuIGVtcHR5IGZpbGUgbmFtZWQgYC5oZXJlYC4gT3IsIGlmIHlvdSB1c2UgUlN0dWRpbyBhbmQvb3IgR2l0LCB0aG9zZSBib3RoIGxlYXZlIGNoYXJhY3RlcmlzdGljIGZpbGVzIGJlaGluZCB0aGF0IHdpbGwgZ2V0IHRoZSBqb2IgZG9uZS4KKiBVc2UgdGhlIGBoZXJlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGBoZXJlYCBwYWNrYWdlIHRvIGJ1aWxkIHRoZSBwYXRoIHdoZW4geW91IHJlYWQgb3Igd3JpdGUgYSBmaWxlLiBDcmVhdGUgcGF0aHMgcmVsYXRpdmUgdG8gdGhlIHRvcC1sZXZlbCBkaXJlY3RvcnkuCiogV2hlbmV2ZXIgeW91IHdvcmsgb24gdGhpcyBwcm9qZWN0LCBsYXVuY2ggdGhlIFIgcHJvY2VzcyBmcm9tIHRoZSBwcm9qZWN04oCZcyB0b3AtbGV2ZWwgZGlyZWN0b3J5LiBJZiB5b3UgbGF1bmNoIFIgZnJvbSB0aGUgc2hlbGwsIGBjZGAgdG8gdGhlIGNvcnJlY3QgZm9sZGVyIGZpcnN0LgoKTGV0J3MgdGVzdCB0aGlzIG91dC4gV2UgY2FuIHVzZSBgZ2V0d2QoKWAgdG8gc2VlIG91ciBjdXJyZW50IAp3b3JraW5nIGRpcmVjdG9yeSBwYXRoIGFuZCB0aGUgZmlsZXMgYXZhaWxhYmxlIHVzaW5nIGBsaXN0LmZpbGUoKWAgIAoKYGBge3J9CmdldHdkKCkKbGlzdC5maWxlcygpCmBgYAoKT0sgc28gb3VyIGN1cnJlbnQgbG9jYXRpb24gaXMgaW4gdGhlIGBsZWN0dXJlc2Agc3ViLWZvbGRlciAKb2YgdGhlIGAyMDE5YCBjb3Vyc2UgcmVwb3NpdG9yeS4gTGV0J3MgdHJ5IHVzaW5nIHRoZSAKYGhlcmVgIHBhY2thZ2UuIAoKYGBge3J9CmxpYnJhcnkoaGVyZSkKCmxpc3QuZmlsZXMoaGVyZTo6aGVyZSgpKQpsaXN0LmZpbGVzKGhlcmUoImRhdGEiKSkKYGBgCgpOb3cgd2Ugc2VlIHRoYXQgdXNpbmcgdGhlIGBoZXJlOjpoZXJlKClgIGZ1bmN0aW9uIGlzIGEgCl9yZWxhdGl2ZV8gcGF0aCAocmVsYXRpdmUgdG8gdGhlIGAuUnByb2pgIGZpbGUgaW4gb3VyIGAyMDE5YCAKcmVwb3NpdG9yeS4gV2UgYWxzbyBzZWUgdGhlcmUgaXMgYSBgY2FtZXJhcy5jc3ZgIGZpbGUgaW4gCnRoZSBgZGF0YWAgZm9sZGVyLiBMZXQncyByZWFkIGl0IGludG8gUiB3aXRoIHRoZSBgcmVhZHJgIHBhY2thZ2UuIApgYGB7cn0KZGYgPC0gcmVhZHI6OnJlYWRfY3N2KGhlcmUoImRhdGEiLCAiY2FtZXJhcy5jc3YiKSkKZGYKYGBgCgpXZSBjYW4gYWxzbyBhc2sgZm9yIHRoZSBmdWxsIHBhdGhzIGZvciBzcGVjaWZpYyBmaWxlcwpgYGB7cn0KaGVyZSgiZGF0YSIsICJjYW1lcmFzLmNzdiIpCmBgYAoKIyMgRmluZGluZyBhbmQgY3JlYXRpbmcgZmlsZXMgbG9jYWxseQoKSWYgeW91IHdhbnQgdG8gZG93bmxvYWQgYSBmaWxlLCBvbmUgd2F5IHRvIHVzZSB0aGUgCmBmaWxlLmV4aXN0cygpYCwgYGRpci5jcmVhdGUoKWAgYW5kIGBsaXN0LmZpbGVzKClgCmZ1bmN0aW9ucy4gCgoqIGBmaWxlLmV4aXN0cyhoZXJlKCJteSIsICJyZWxhdGl2ZSIsICJwYXRoIikpYCA9IGxvZ2ljYWwgdGVzdCBpZiB0aGUgZmlsZSBleGlzdHMKKiBgZGlyLmNyZWF0ZShoZXJlKCJteSIsICJyZWxhdGl2ZSIsICJwYXRoIikpYCA9IGNyZWF0ZSBhIGZvbGRlcgoqIGBsaXN0LmZpbGVzKGhlcmUoIm15IiwgInJlbGF0aXZlIiwgInBhdGgiKSlgID0gbGlzdCBjb250ZW50cyBvZiBmb2xkZXIKCmBgYHtyLCBldmFsPUZBTFNFfQppZighZmlsZS5leGlzdHMoaGVyZSgibXkiLCAicmVsYXRpdmUiLCAicGF0aCIpKSl7CiAgZGlyLmNyZWF0ZShoZXJlKCJteSIsICJyZWxhdGl2ZSIsICJwYXRoIikpCn0KbGlzdC5maWxlcyhoZXJlKCJteSIsICJyZWxhdGl2ZSIsICJwYXRoIikpCmBgYAoKIyBHZXR0aW5nIGRhdGEKCiMjIERvd25sb2FkaW5nIGZpbGVzCgpMZXQncyBzYXkgd2Ugd2FudGVkIHRvIGZpbmQgb3V0IHdoZXJlIGFyZQphbGwgdGhlIEZpeGVkIFNwZWVkIENhbWVyYXMgaW4gQmFsdGltb3JlPyAKClRvIGRvIHRoaXMsIHdlIGNhbiB1c2UgdGhlIApbT3BlbiBCYWx0aW1vcmVdKGh0dHBzOi8vZGF0YS5iYWx0aW1vcmVjaXR5LmdvdikgCkFQSSB3aGljaCBoYXMgaW5mb3JtYXRpb24gb24gClt0aGUgbG9jYXRpb25zXShodHRwczovL2RhdGEuYmFsdGltb3JlY2l0eS5nb3YvVHJhbnNwb3J0YXRpb24vQmFsdGltb3JlLUZpeGVkLVNwZWVkLUNhbWVyYXMvZHo1NC0yYXJ1KSBvZiBmaXhlZCBzcGVlZCBjYW1lcmFzCmluIEJhbHRpbW9yZS4gCgpJbiBjYXNlIHlvdSBhcmVuJ3QgZmFtaWxpYXIgd2l0aCAKZml4ZWQgc3BlZWQgY2FtZXJhcywgdGhlIHdlYnNpdGUgc3RhdGVzOiAKCj4gTW90b3Jpc3RzIHdobyBkcml2ZSBhZ2dyZXNzaXZlbHkgYW5kIGV4Y2VlZCB0aGUgcG9zdGVkIHNwZWVkIGxpbWl0IGJ5IGF0IGxlYXN0IDEyIG1pbGVzIHBlciBob3VyIHdpbGwgcmVjZWl2ZSAkNDAgY2l0YXRpb25zIGluIHRoZSBtYWlsLiBUaGVzZSBjaXRhdGlvbnMgYXJlIG5vdCByZXBvcnRlZCB0byBpbnN1cmFuY2UgY29tcGFuaWVzIGFuZCBubyBsaWNlbnNlIHBvaW50cyBhcmUgYXNzaWduZWQuIE5vdGlmaWNhdGlvbiBzaWducyB3aWxsIGJlIHBsYWNlZCBhdCBhbGwgc3BlZWQgZW5mb3JjZW1lbnQgbG9jYXRpb25zIHNvIHRoYXQgbW90b3Jpc3RzIHdpbGwgYmUgYXdhcmUgdGhhdCB0aGV5IGFyZSBhcHByb2FjaGluZyBhIHNwZWVkIGNoZWNrIHpvbmUuIFRoZSBnb2FsIG9mIHRoZSBwcm9ncmFtIGlzIHRvIG1ha2UgdGhlIHN0cmVldHMgb2YgQmFsdGltb3JlIHNhZmVyIGZvciBldmVyeW9uZSBieSBjaGFuZ2luZyBhZ2dyZXNzaXZlIGRyaXZpbmcgYmVoYXZpb3IuIEluIGFkZGl0aW9uIHRvIHRoZSBlaWdodCBwb3J0YWJsZSBzcGVlZCBlbmZvcmNlbWVudCB1bml0cywgdGhlIGNpdHkgaGFzIHJldHJvZml0dGVkIDUwIHJlZCBsaWdodCBjYW1lcmEgbG9jYXRpb25zIHdpdGggdGhlIGF1dG9tYXRlZCBzcGVlZCBlbmZvcmNlbWVudCB0ZWNobm9sb2d5LgoKV2hlbiB3ZSBnbyB0byB0aGUgd2Vic2l0ZSwgd2Ugc2VlIHRoYXQKdGhlIGRhdGEgY2FuIGJlIHByb3ZpZGVkIHRvIHVzIGFzIGEgCmAuY3N2YCBmaWxlLiBUbyBkb3dubG9hZCBpbiB0aGlzIGRhdGEsCndlIGNhbiBkbyB0aGUgZm9sbG93aW5nOiAKCmBgYHtyLCBldmFsPUZBTFNFfQpmaWxlX3VybCA8LSBwYXN0ZTAoImh0dHBzOi8vZGF0YS5iYWx0aW1vcmVjaXR5Lmdvdi9hcGkvIiwKICAgICAgICAgICAgICAgICAgICJ2aWV3cy9kejU0LTJhcnUvcm93cy5jc3Y/YWNjZXNzVHlwZT1ET1dOTE9BRCIpCmRvd25sb2FkLmZpbGUoZmlsZV91cmwsCiAgICAgICAgICAgICAgZGVzdGZpbGU9aGVyZSgiZGF0YSIsICJjYW1lcmFzLmNzdiIpKQpsaXN0LmZpbGVzKGhlcmUoImRhdGEiKSkKYGBgCgpBbHRlcm5hdGl2ZWx5LCBpZiB3ZSB3YW50IHRvIG9ubHkgZG93bmxvYWQKdGhlIGZpbGUgb25jZSBlYWNoIHRpbWUgd2Uga25pdCBvdXIgcmVwcm9kdWNpYmxlCnJlcG9ydCBvciBob21ld29yayBvciBwcm9qZWN0LCB3ZSBjYW4gdXMgd3JhcAp0aGUgY29kZSBhYm92ZSBpbnRvIGEgYCFmaWxlLmV4aXN0cygpYCBmdW5jdGlvbi4gCgpgYGB7cn0KaWYoIWZpbGUuZXhpc3RzKGhlcmUoImRhdGEiLCAiY2FtZXJhcy5jc3YiKSkpewogIGZpbGVfdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly9kYXRhLmJhbHRpbW9yZWNpdHkuZ292L2FwaS8iLAogICAgICAgICAgICAgICAgICAgInZpZXdzL2R6NTQtMmFydS9yb3dzLmNzdj9hY2Nlc3NUeXBlPURPV05MT0FEIikKICBkb3dubG9hZC5maWxlKGZpbGVfdXJsLAogICAgICAgICAgICAgICAgZGVzdGZpbGU9aGVyZSgiZGF0YSIsICJjYW1lcmFzLmNzdiIpKQp9Cmxpc3QuZmlsZXMoaGVyZSgiZGF0YSIpKQpgYGAKCiMjIFJlYWRpbmcgaW4gQ1NWIGZpbGVzCgpGcm9tIHRoZXJlLCB3ZSBjYW4gcmVhZCBpbiB0aGUgYGNhbWVyYXMuY3N2YApsaWtlIHdlIGhhdmUgYWxyZWFkeSBsZWFybmVkIGhvdyB0byBkbyB1c2luZyB0aGUgCmByZWFkcjo6cmVhZF9jc3YoKWAgZnVuY3Rpb246IAoKYGBge3J9CmNhbWVyYXMgPC0gcmVhZHI6OnJlYWRfY3N2KGhlcmUoImRhdGEiLCAiY2FtZXJhcy5jc3YiKSkKY2FtZXJhcwpgYGAKCiMjIFJlYWRpbmcgaW4gYSBKU09OIGZpbGUgdXNpbmcgYGpzb25saXRlYAoKIyMjIFdoYXQgaXMgSlNPTj8gCgpKU09OIChvciBKYXZhU2NyaXB0IE9iamVjdCBOb3RhdGlvbikgaXMgYSBmaWxlCmZvcm1hdCB0aGF0IHN0b3JlcyBpbmZvcm1hdGlvbiBpbiBodW1hbi1yZWFkYWJsZSwgCm9yZ2FuaXplZCwgbG9naWNhbCwgZWFzeS10by1hY2Nlc3MgbWFubmVyLgoKRm9yIGV4YW1wbGUsIGhlcmUgaXMgd2hhdCBhIEpTT04gZmlsZSBsb29rcyAKbGlrZTogCgpgYGB7amF2YXNjcmlwdCwgZXZhbD1GQUxTRX0KdmFyIHN0ZXBoYW5pZSA9IHsKCSJhZ2UiIDogIjMzIiwKCSJob21ldG93biIgOiAiQmFsdGltb3JlLCBNRCIsCgkiZ2VuZGVyIiA6ICJmZW1hbGUiLCAKICAiY2FycyIgOiB7CiAgICAiY2FyMSIgOiAiSHl1bmRhaSBFbGFudHJhIiwKICAgICJjYXIyIiA6ICJUb3lvdGEgUmF2NCIsCiAgICAiY2FyMyIgOiAiSG9uZGEgQ1ItViIKICB9Cn0KYGBgCgpTb21lIGZlYXR1cmVzIGFib3V0IGBKU09OYCBvYmplY3Q6IAoKKiBKU09OIG9iamVjdHMgYXJlIHN1cnJvdW5kZWQgYnkgY3VybHkgYnJhY2VzIGB7fWAKKiBKU09OIG9iamVjdHMgYXJlIHdyaXR0ZW4gaW4ga2V5L3ZhbHVlIHBhaXJzCiogS2V5cyBtdXN0IGJlIHN0cmluZ3MsIGFuZCB2YWx1ZXMgbXVzdCBiZSBhIHZhbGlkIEpTT04gZGF0YSB0eXBlIChzdHJpbmcsIG51bWJlciwgb2JqZWN0LCBhcnJheSwgYm9vbGVhbikKKiBLZXlzIGFuZCB2YWx1ZXMgYXJlIHNlcGFyYXRlZCBieSBhIGNvbG9uCiogRWFjaCBrZXkvdmFsdWUgcGFpciBpcyBzZXBhcmF0ZWQgYnkgYSBjb21tYQoKIyMjIFVzaW5nIEdpdEh1YiBBUEkKCkxldCdzIHNheSB3ZSB3YW50IHRvIHVzZSB0aGUgCltHaXRIdWIgQVBJXShodHRwczovL2RldmVsb3Blci5naXRodWIuY29tL3YzLz8pCnRvIGZpbmQgb3V0IGhvdyBtYW55IG9mIG15IEdpdEh1YiByZXBvc2l0b3JpZXMKaGF2ZSBvcGVuIGlzc3Vlcz8gCgpXZSB3aWxsIHVzZSB0aGUgCltqc29ubGl0ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2pzb25saXRlL2luZGV4Lmh0bWwpClIgcGFja2FnZSBhbmQgdGhlIGBmcm9tSlNPTigpYCBmdW5jdGlvbgp0byBjb252ZXJ0IGZyb20gYSBKU09OIG9iamVjdCB0byBhIGRhdGEgZnJhbWUuIAoKV2Ugd2lsbCByZWFkIGluIGEgSlNPTiBmaWxlIGxvY2F0ZWQgYXQgCltodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3N0ZXBoYW5pZWhpY2tzL3JlcG9zXShodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL3N0ZXBoYW5pZWhpY2tzL3JlcG9zKQoKYGBge3J9CmdpdGh1Yl91cmwgPSAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9zdGVwaGFuaWVoaWNrcy9yZXBvcyIKCmxpYnJhcnkoanNvbmxpdGUpCmpzb25EYXRhIDwtIGZyb21KU09OKGdpdGh1Yl91cmwpCmBgYAoKVGhlIGZ1bmN0aW9uIGBmcm9tSlNPTigpYCBoYXMgbm93IGNvbnZlcnRlZCAKdGhlIEpTT04gZmlsZSBpbnRvIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBuYW1lczogCgpgYGB7cn0KbmFtZXMoanNvbkRhdGEpCmBgYAoKSG93IG1hbnkgYXJlIHByaXZhdGUgcmVwb3M/IEhvdyBtYW55IGhhdmUgZm9ya3M/IAoKYGBge3J9CnRhYmxlKGpzb25EYXRhJHByaXZhdGUpCnRhYmxlKGpzb25EYXRhJGZvcmtzKQpgYGAKCldoYXQncyB0aGUgbW9zdCBwb3B1bGFyIGxhbmd1YWdlPyAKCmBgYHtyfQp0YWJsZShqc29uRGF0YSRsYW5ndWFnZSkKYGBgCgpUbyBmaW5kIG91dCBob3cgbWFueSByZXBvcyB0aGF0IEkgaGF2ZQp3aXRoIG9wZW4gaXNzdWVzLCB3ZSBjYW4ganVzdCBjcmVhdGUgCmEgdGFibGU6IAoKYGBge3J9CiMgaG93IG1hbnkgcmVwb3MgaGF2ZSBvcGVuIGlzc3Vlcz8gCnRhYmxlKGpzb25EYXRhJG9wZW5faXNzdWVzX2NvdW50KQpgYGAKCldoZXchIE5vdCBhcyBtYW55IGFzIEkgdGhvdWdodC4KCkhvdyBtYW55IGRvIHlvdSBoYXZlPyAKCkZpbmFsbHksIEkgd2lsbCBsZWF2ZSB5b3Ugd2l0aCBhIGZldyAKb3RoZXIgZXhhbXBsZXMgb2YgdXNpbmcgR2l0SHViIEFQSTogCgoqIFtIb3cgbG9uZyBkb2VzIGl0IHRha2UgdG8gY2xvc2UgYSBHaXRIdWIgSXNzdWUgaW4gdGhlIGBkcGx5cmAgcGFja2FnZT9dKGh0dHBzOi8vYmxvZy5leHBsb3JhdG9yeS5pby9hbmFseXppbmctaXNzdWUtZGF0YS13aXRoLWdpdGh1Yi1yZXN0LWFwaS02Mzk0NTAxN2RlZGMpCiogW0hvdyB0byByZXRyaWV2ZSBhbGwgY29tbWl0cyBmb3IgYSBicmFuY2hdKGh0dHBzOi8vc3RhY2tvdmVyZmxvdy5jb20vcXVlc3Rpb25zLzkxNzk4MjgvZ2l0aHViLWFwaS1yZXRyaWV2ZS1hbGwtY29tbWl0cy1mb3ItYWxsLWJyYW5jaGVzLWZvci1hLXJlcG8pCiogW0dldHRpbmcgbXkgR2l0SHViIEFjdGl2aXR5XShodHRwczovL21hc2FsbW9uLmV1LzIwMTcvMTIvMjEvd2hlcmVoYXZleW91YmVlbi8pCgohW10oaHR0cHM6Ly9tYXNhbG1vbi5ldS9maWd1cmUvc291cmNlLzIwMTctMTItMjEtd2hlcmVoYXZleW91YmVlbi91bm5hbWVkLWNodW5rLTUtMS5wbmcpCgoKIyMgUmVhZGluZyBpbiBYTUwgb3IgSFRNTCBmaWxlcyB1c2luZyBgcnZlc3RgCgpEbyB3ZSB3YW50IHRvIHB1cmNoYXNlIGEgYm9vayBvbiBBbWF6b24/IAoKTmV4dCB3ZSBhcmUgZ29pbmcgdG8gbGVhcm4gYWJvdXQgd2hhdCB0byBkbyBpZgp5b3VyIGRhdGEgaXMgb24gYSB3ZWJzaXRlIChYTUwgb3IgSFRNTCkgZm9ybWF0dGVkIAp0byBiZSByZWFkIGJ5IGh1bWFucyBpbnN0ZWFkIG9mIFIuCgpXZSB3aWxsIHVzZSB0aGUgKHJlYWxseSBwb3dlcmZ1bCkKW3J2ZXN0XShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcnZlc3QvcnZlc3QucGRmKQpSIHBhY2thZ2UgdG8gZG8gd2hhdCBpcyBvZnRlbiBjYWxsZWQgCiJzY3JhcGluZyBkYXRhIGZyb20gdGhlIHdlYiIuIAoKQmVmb3JlIHdlIGRvIHRoYXQsIHdlIG5lZWQgdG8gc2V0IHVwIGEgCmZldyB0aGluZ3M6CgoqIFtTZWxlY3RvckdhZGdldCB0b29sXShodHRwOi8vc2VsZWN0b3JnYWRnZXQuY29tLykKKiBbcnZlc3QgYW5kIFNlbGVjdG9yR2FkZ2V0IGd1aWRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvcnZlc3QvdmlnbmV0dGVzL3NlbGVjdG9yZ2FkZ2V0Lmh0bWwpCiogW0F3ZXNvbWUgdHV0b3JpYWwgZm9yIENTUyBTZWxlY3RvcnNdKGh0dHA6Ly9mbHVrZW91dC5naXRodWIuaW8vIykKKiBbSW50cm9kdWN0aW9uIHRvIHN0cmluZ3JdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9zdHJpbmdyL3ZpZ25ldHRlcy9zdHJpbmdyLmh0bWwpCiogW1JlZ3VsYXIgRXhwcmVzc2lvbnMvc3RyaW5nciB0dXRvcmlhbF0oaHR0cHM6Ly9zdGF0NTQ1LXViYy5naXRodWIuaW8vYmxvY2swMjJfcmVndWxhci1leHByZXNzaW9uLmh0bWwpCiogW1JlZ3VsYXIgRXhwcmVzc2lvbiBvbmxpbmUgdGVzdGVyXShodHRwczovL3JlZ2V4MTAxLmNvbS8jcHl0aG9uKS0gZXhwbGFpbnMgYSByZWd1bGFyIGV4cHJlc3Npb24gYXMgaXQgaXMgYnVpbHQsIGFuZCBjb25maXJtcyBsaXZlIHdoZXRoZXIgYW5kIGhvdyBpdCBtYXRjaGVzIHBhcnRpY3VsYXIgdGV4dC4KCldlJ3JlIGdvaW5nIHRvIGJlIHNjcmFwaW5nIFt0aGlzIHBhZ2VdKGh0dHA6Ly93d3cuYW1hem9uLmNvbS9nZ3Bsb3QyLUVsZWdhbnQtR3JhcGhpY3MtRGF0YS1BbmFseXNpcy9wcm9kdWN0LXJldmlld3MvMDM4Nzk4MTQwMy9yZWY9Y21fY3JfZHBfcXRfc2VlX2FsbF90b3A/aWU9VVRGOCZzaG93Vmlld3BvaW50cz0xJnNvcnRCeT1oZWxwZnVsKTogaXQganVzdCBjb250YWlucyB0aGUgKGZpcnN0IHBhZ2Ugb2YpIHJldmlld3Mgb2YgdGhlIApnZ3Bsb3QyIGJvb2sgYnkgSGFkbGV5IFdpY2toYW0uIAoKYGBge3J9CnVybCA8LSAiaHR0cDovL3d3dy5hbWF6b24uY29tL2dncGxvdDItRWxlZ2FudC1HcmFwaGljcy1EYXRhLUFuYWx5c2lzL3Byb2R1Y3QtcmV2aWV3cy8wMzg3OTgxNDAzL3JlZj1jbV9jcl9kcF9xdF9zZWVfYWxsX3RvcD9pZT1VVEY4JnNob3dWaWV3cG9pbnRzPTEmc29ydEJ5PWhlbHBmdWwiCmBgYAoKV2UgdXNlIHRoZSBgcnZlc3RgIHBhY2thZ2UgdG8gZG93bmxvYWQgdGhpcyBwYWdlLgoKYGBge3J9CmxpYnJhcnkocnZlc3QpCmggPC0gcmVhZF9odG1sKHVybCkKYGBgCgpOb3cgYGhgIGlzIGFuIGB4bWxfZG9jdW1lbnRgIHRoYXQgY29udGFpbnMgdGhlIGNvbnRlbnRzIG9mIHRoZSBwYWdlOgoKYGBge3J9CmgKYGBgCgpIb3cgY2FuIHlvdSBhY3R1YWxseSBwdWxsIHRoZSBpbnRlcmVzdGluZyAKaW5mb3JtYXRpb24gb3V0PyBUaGF0J3Mgd2hlcmUgQ1NTIHNlbGVjdG9ycyBjb21lIGluLgoKIyMjIENTUyBTZWxlY3RvcnMKCkNTUyBzZWxlY3RvcnMgYXJlIGEgd2F5IHRvIHNwZWNpZnkgYSBzdWJzZXQgb2YgCm5vZGVzICh0aGF0IGlzLCB1bml0cyBvZiBjb250ZW50KSBvbiBhIHdlYiBwYWdlCihlLmcuLCBqdXN0IGdldHRpbmcgdGhlIHRpdGxlcyBvZiByZXZpZXdzKS4gCkNTUyBzZWxlY3RvcnMgYXJlIHZlcnkgcG93ZXJmdWwgYW5kIG5vdCB0b28gCmNoYWxsZW5naW5nIHRvIG1hc3Rlci0gaGVyZSdzIApbYSBncmVhdCB0dXRvcmlhbF0oaHR0cDovL2ZsdWtlb3V0LmdpdGh1Yi5pby8jKSAKQnV0IGhvbmVzdGx5IHlvdSBjYW4gZ2V0IGEgbG90IGRvbmUgZXZlbiB3aXRoIAp2ZXJ5IGxpdHRsZSB1bmRlcnN0YW5kaW5nLCBieSB1c2luZyBhIHRvb2wgCmNhbGxlZCBTZWxlY3RvckdhZGdldC4KCkluc3RhbGwgdGhlIFtTZWxlY3RvckdhZGdldF0oaHR0cDovL3NlbGVjdG9yZ2FkZ2V0LmNvbS8pIApvbiB5b3VyIHdlYiBicm93c2VyLiAoSWYgeW91IHVzZSBDaHJvbWUgeW91IGNhbgp1c2UgdGhlIENocm9tZSBleHRlbnNpb24sIG90aGVyd2lzZSBkcmFnIHRoZSAKcHJvdmlkZWQgbGluayBpbnRvIHlvdXIgYm9va21hcmtzIGJhcikuIApbSGVyZSdzIGEgZ3VpZGUgZm9yIGhvdyB0byB1c2UgaXQgd2l0aCBydmVzdCB0byAicG9pbnQtYW5kLWNsaWNrIiB5b3VyIHdheSB0byBhIHdvcmtpbmcgc2VsZWN0b3JdKGh0dHA6Ly9zZWxlY3RvcmdhZGdldC5jb20vKS4KCkZvciBleGFtcGxlLCBpZiB5b3UganVzdCB3YW50ZWQgdGhlIHRpdGxlcywgCnlvdSdsbCBlbmQgdXAgd2l0aCBhIHNlbGVjdG9yIHRoYXQgbG9va3MgCnNvbWV0aGluZyBsaWtlIGAuYS10ZXh0LWJvbGQgc3BhbmAuIFlvdSBjYW4gcGlwZQp5b3VyIEhUTUwgb2JqZWN0IGFsb25nIHdpdGggdGhhdCBzZWxlY3RvciAKaW50byB0aGUgYGh0bWxfbm9kZXNgIGZ1bmN0aW9uLCB0byBzZWxlY3QgCmp1c3QgdGhvc2Ugbm9kZXM6CgpgYGB7cn0KaCAlPiUKICBodG1sX25vZGVzKCIuYS10ZXh0LWJvbGQgc3BhbiIpCmBgYAoKQnV0IHlvdSBuZWVkIHRoZSB0ZXh0IGZyb20gZWFjaCBvZiB0aGVzZSwgbm90IHRoZSBmdWxsIHRhZ3MuIFBpcGUgdG8gdGhlIGBodG1sX3RleHRgIGZ1bmN0aW9uIHRvIHB1bGwgdGhlc2Ugb3V0OgoKYGBge3J9CnJldmlld190aXRsZXMgPC0gaCAlPiUKICBodG1sX25vZGVzKCIuYS10ZXh0LWJvbGQgc3BhbiIpICU+JQogIGh0bWxfdGV4dCgpCgpyZXZpZXdfdGl0bGVzCmBgYAoKTm93IHdlJ3ZlIGV4dHJhY3RlZCBzb21ldGhpbmcgdXNlZnVsISBTaW1pbGFybHksIApsZXQncyBncmFiIHRoZSBmb3JtYXQgKGhhcmRjb3ZlciBvciBwYXBlcmJhY2spLgpTb21lIGV4cGVyaW1lbnRhdGlvbiB3aXRoIFNlbGVjdG9yR2FkZ2V0IApzaG93cyBpdCdzOgoKYGBge3J9CmggJT4lCiAgaHRtbF9ub2RlcygiLmEtc2l6ZS1taW5pLmEtY29sb3Itc2Vjb25kYXJ5IikgJT4lCiAgaHRtbF90ZXh0KCkKYGBgCgpOb3csIHdlIG1heSBiZSBhbm5veWVkIHRoYXQgaXQgYWx3YXlzCnN0YXJ0cyB3aXRoIGBGb3JtYXQ6IGAuIExldCdzIGludHJvZHVjZSAKdGhlIGBzdHJpbmdyYCBwYWNrYWdlLgoKYGBge3J9CmZvcm1hdHMgPC0gaCAlPiUKICBodG1sX25vZGVzKCIuYS1zaXplLW1pbmkuYS1jb2xvci1zZWNvbmRhcnkiKSAlPiUKICBodG1sX3RleHQoKSAlPiUKICBzdHJpbmdyOjpzdHJfcmVwbGFjZSgiRm9ybWF0OiAiLCAiIikKCmZvcm1hdHMKYGBgCgpXZSBjb3VsZCBkbyBzaW1pbGFyIGV4ZXJjaXNlIGZvciBleHRyYWN0aW5nCnRoZSBudW1iZXIgb2Ygc3RhcnMgYW5kIHdoZXRoZXIgb3Igbm90IHNvbWVvbmUKZm91bmQgYSByZXZpZXcgdXNlZnVsLiBUaGlzIHdvdWxkIGhlbHAgdXMgZGVjaWRlCmlmIHdlIHdlcmUgaW50ZXJlc3RlZCBpbiBwdXJjaGFzaW5nIHRoZSBib29rISAKCiMjIFJlYWRpbmcgaW4gZnJvbSBgU1FMaXRlYCBkYXRhYmFzZQoKQW5vdGhlciBpbXBvcnRhbnQgdHlwZSBvZiBkYXRhIHlvdSBtaWdodCBpbnRlcmFjdCB3aXRoIAphcmUgZGF0YWJhc2VzIChzdWNoIGFzIGBTUUxgIG9yIGBTUUxpdGVgKS4gVGhlcmUgYXJlIHNldmVyYWwgd2F5cyB0byAKW3F1ZXJ5IGRhdGFiYXNlcyBpbiBSXShodHRwczovL2RiLnJzdHVkaW8uY29tL2dldHRpbmctc3RhcnRlZC9kYXRhYmFzZS1xdWVyaWVzLykuIAoKRmlyc3QsIHdlIHdpbGwgZG93bmxvYWQgYSBgLnNxbGl0ZWAgZGF0YWJhc2UuIFRoaXMgaXMgYQpwb3J0YWJsZSB2ZXJzaW9uIG9mIGEgYFNRTGAgZGF0YWJhc2UuIEZvciBvdXIgCnB1cnBvc2VzLCB3ZSB3aWxsIHVzZSB0aGUgCltjaGlub29rIHNxbGl0ZSBkYXRhYmFzZSBoZXJlXShodHRwczovL2dpdGh1Yi5jb20vbGVyb2NoYS9jaGlub29rLWRhdGFiYXNlL2Jsb2IvbWFzdGVyL0NoaW5vb2tEYXRhYmFzZS9EYXRhU291cmNlcy9DaGlub29rX1NxbGl0ZS5zcWxpdGUpLiBUaGUgZGF0YWJhc2UgcmVwcmVzZW50cyBhIAoiZGlnaXRhbCBtZWRpYSBzdG9yZSwgaW5jbHVkaW5nIHRhYmxlcyBmb3IgYXJ0aXN0cywgCmFsYnVtcywgbWVkaWEgdHJhY2tzLCBpbnZvaWNlcyBhbmQgY3VzdG9tZXJzIi4KCkZyb20gdGhlIFtSZWFkbWUubWRdKGh0dHBzOi8vZ2l0aHViLmNvbS9sZXJvY2hhL2NoaW5vb2stZGF0YWJhc2UpIGZpbGU6IAoKPiBTYW1wbGUgRGF0YQo+IAo+IE1lZGlhIHJlbGF0ZWQgZGF0YSB3YXMgY3JlYXRlZCB1c2luZyByZWFsIGRhdGEgZnJvbSBhbiBpVHVuZXMgTGlicmFyeS4gSXQgaXMgcG9zc2libGUgZm9yIHlvdSB0byB1c2UgeW91ciBvd24gaVR1bmVzIExpYnJhcnkgdG8gZ2VuZXJhdGUgdGhlIFNRTCBzY3JpcHRzLCBzZWUgaW5zdHJ1Y3Rpb25zIGJlbG93LiBDdXN0b21lciBhbmQgZW1wbG95ZWUgaW5mb3JtYXRpb24gd2FzIG1hbnVhbGx5IGNyZWF0ZWQgdXNpbmcgZmljdGl0aW91cyBuYW1lcywgYWRkcmVzc2VzIHRoYXQgY2FuIGJlIGxvY2F0ZWQgb24gR29vZ2xlIG1hcHMsIGFuZCBvdGhlciB3ZWxsIGZvcm1hdHRlZCBkYXRhIChwaG9uZSwgZmF4LCBlbWFpbCwgZXRjLikuIFNhbGVzIGluZm9ybWF0aW9uIGlzIGF1dG8gZ2VuZXJhdGVkIHVzaW5nIHJhbmRvbSBkYXRhIGZvciBhIGZvdXIgeWVhciBwZXJpb2QuCgpgYGB7cn0KaWYoIWZpbGUuZXhpc3RzKGhlcmUoImRhdGEiLCAiQ2hpbm9vay5zcWxpdGUiKSkpewogIGZpbGVfdXJsIDwtIHBhc3RlMCgiaHR0cHM6Ly9naXRodWIuY29tL2xlcm9jaGEvY2hpbm9vay1kYXRhYmFzZS9yYXcvbWFzdGVyL0NoaW5vb2tEYXRhYmFzZS9EYXRhU291cmNlcy9DaGlub29rX1NxbGl0ZS5zcWxpdGUiKQogIGRvd25sb2FkLmZpbGUoZmlsZV91cmwsCiAgICAgICAgICAgICAgICBkZXN0ZmlsZT1oZXJlKCJkYXRhIiwgIkNoaW5vb2suc3FsaXRlIikpCn0KbGlzdC5maWxlcyhoZXJlKCJkYXRhIikpCmBgYAoKVGhlIG1haW4gd29ya2hvcnNlIHBhY2thZ2VzIHRoYXQgd2Ugd2lsbCB1c2UgYXJlIAp0aGUgYERCSWAgYW5kIGBkcGx5cmAgcGFja2FnZXMuIExldCdzIGxvb2sgYXQgdGhlIApgREJJOjpkYkNvbm5lY3QoKWAgaGVscCBmaWxlCgpgYGB7ciwgZXZhbD1GQUxTRX0KP0RCSTo6ZGJDb25uZWN0CmBgYAoKU28gd2UgbmVlZCBhIGRyaXZlciBhbmQgb25lIGV4YW1wbGUgaXMgYFJTUUxpdGU6OlNRTGl0ZSgpYC4gCkxldCdzIGxvb2sgYXQgdGhlIGhlbHAgZmlsZQoKYGBge3IsIGV2YWw9RkFMU0V9Cj9SU1FMaXRlOjpTUUxpdGUKYGBgCgpPayBzbyB3aXRoIGBSU1FMaXRlOjpTUUxpdGUoKWAgYW5kIGBEQkk6OmRiQ29ubmVjdCgpYCAKd2UgY2FuIGNvbm5lY3QgdG8gYSBgU1FMaXRlYCBkYXRhYmFzZS4gTGV0J3MgdHJ5IHRoYXQgCndpdGggb3VyIGBDaGlub29rLnNxbGl0ZWAgZmlsZSB0aGF0IHdlIGRvd25sb2FkZWQuIENoaW5vb2suc3FsaXRlCgpgYGB7cn0KbGlicmFyeShEQkkpCmNvbm4gPC0gREJJOjpkYkNvbm5lY3QoUlNRTGl0ZTo6U1FMaXRlKCksIAogICAgICAgICAgICAgICAgICAgICAgIGhlcmUoImRhdGEiLCAiQ2hpbm9vay5zcWxpdGUiKSkKY29ubgpgYGAKClNvIHdlIGhhdmUgb3BlbmVkIHVwIGEgY29ubmVjdGlvbiB3aXRoIHRoZSBTUUxpdGUgZGF0YWJhc2UuIApOZXh0LCB3ZSBjYW4gc2VlIHdoYXQgdGFibGVzIGFyZSBhdmFpbGFibGUgaW4gdGhlIGRhdGFiYXNlIAp1c2luZyB0aGUgYGRiTGlzdFRhYmxlcygpYCBmdW5jdGlvbjogCgpgYGB7cn0KZGJMaXN0VGFibGVzKGNvbm4pCmBgYAoKRnJvbSBSU3R1ZGlvJ3Mgd2Vic2l0ZSwgdGhlcmUgYXJlIHNldmVyYWwgd2F5cyB0byBpbnRlcmFjdCB3aXRoIApTUUwgRGF0YWJhc2VzLiBPbmUgb2YgdGhlIHNpbXBsZXN0IHdheXMgdGhhdCB3ZSB3aWxsIHVzZSBoZXJlIGlzIAp0byBsZXZlcmFnZSB0aGUgYGRwbHlyYCBmcmFtZXdvcmsuIAoKPiAiVGhlIGBkcGx5cmAgcGFja2FnZSBub3cgaGFzIGEgZ2VuZXJhbGl6ZWQgU1FMIGJhY2tlbmQgZm9yIHRhbGtpbmcgdG8gZGF0YWJhc2VzLCBhbmQgdGhlIG5ldyBgZGJwbHlyYCBwYWNrYWdlIHRyYW5zbGF0ZXMgUiBjb2RlIGludG8gZGF0YWJhc2Utc3BlY2lmaWMgdmFyaWFudHMuIEFzIG9mIHRoaXMgd3JpdGluZywgU1FMIHZhcmlhbnRzIGFyZSBzdXBwb3J0ZWQgZm9yIHRoZSBmb2xsb3dpbmcgZGF0YWJhc2VzOiBPcmFjbGUsIE1pY3Jvc29mdCBTUUwgU2VydmVyLCBQb3N0Z3JlU1FMLCBBbWF6b24gUmVkc2hpZnQsIEFwYWNoZSBIaXZlLCBhbmQgQXBhY2hlIEltcGFsYS4gTW9yZSB3aWxsIGZvbGxvdyBvdmVyIHRpbWUuCgpTbyBpZiB3ZSB3YW50IHRvIHF1ZXJ5IGEgU1FMIGRhdGFic2Ugd2l0aCBgZHBseXIsIHRoZSAKYmVuZWZpdCBvZiB1c2luZyBgZGJwbHlyYCBpczogCgo+ICJZb3UgY2FuIHdyaXRlIHlvdXIgY29kZSBpbiBgZHBseXJgIHN5bnRheCwgYW5kIGBkcGx5cmAgd2lsbCB0cmFuc2xhdGUgeW91ciBjb2RlIGludG8gU1FMLiBUaGVyZSBhcmUgc2V2ZXJhbCBiZW5lZml0cyB0byB3cml0aW5nIHF1ZXJpZXMgaW4gYGRwbHlyYCBzeW50YXg6IHlvdSBjYW4ga2VlcCB0aGUgc2FtZSBjb25zaXN0ZW50IGxhbmd1YWdlIGJvdGggZm9yIFIgb2JqZWN0cyBhbmQgZGF0YWJhc2UgdGFibGVzLCBubyBrbm93bGVkZ2Ugb2YgU1FMIG9yIHRoZSBzcGVjaWZpYyBTUUwgdmFyaWFudCBpcyByZXF1aXJlZCwgYW5kIHlvdSBjYW4gdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIGZhY3QgdGhhdCBgZHBseXJgIHVzZXMgbGF6eSBldmFsdWF0aW9uLgoKTGV0J3MgdGFrZSBhIGNsb3NlciBsb29rIGF0IHRoZSBgY29ubmAgZGF0YWJhc2UKdGhhdCB3ZSBqdXN0IGNvbm5lY3RlZCB0bzoKCmBgYHtyfQpsaWJyYXJ5KGRicGx5cikKc3JjX2RiaShjb25uKQpgYGAKCllvdSBjYW4gdGhpbmsgb2YgdGhlIG11bHRpcGxlIHRhYmxlcyBzaW1pbGFyIHRvIGhhdmluZyAKbXVsdGlwbGUgd29ya3NoZWV0cyBpbiBhIHNwcmVhZHNoZWV0LiAKCkxldCdzIHRyeSBpbnRlcmFjdGluZyB3aXRoIG9uZS4gCgojIyMgUXVlcnlpbmcgd2l0aCBgZHBseXJgIHN5bnRheAoKRmlyc3QsIGxldCdzIGxvb2sgYXQgdGhlIGZpcnN0IHRlbiByb3dzIGluIHRoZSAKYEFsYnVtYCB0YWJsZS4gCmBgYHtyfQp0YmwoY29ubiwgIkFsYnVtIikgJT4lCiAgaGVhZChuPTEwKQpgYGAKClRoZSBvdXRwdXQgbG9va3MganVzdCBsaWtlIGEgYGRhdGEuZnJhbWVgIHRoYXQgd2UgYXJlIGZhbWlsaWFyIAp3aXRoLiBCdXQgaXQncyBpbXBvcnRhbnQgdG8ga25vdyB0aGF0IGl0J3Mgbm90IHJlYWxseSAKYSBkYXRhZnJhbWUuIEZvciBleGFtcGxlLCB3aGF0IGFib3V0IGlmIHdlIHVzZSAKdGhlIGBkaW0oKWAgZnVuY3Rpb24/IApgYGB7cn0KdGJsKGNvbm4sICJBbGJ1bSIpICU+JQogIGRpbSgpCmBgYAoKSW50ZXJlc3RpbmchIFdlIHNlZSB0aGF0IHRoZSBudW1iZXIgb2Ygcm93cyByZXR1cm5lZCBpcyBgTkFgLiAKVGhpcyBpcyBiZWNhdXNlIHRoZXNlIGZ1bmN0aW9ucyBhcmUgZGlmZmVyZW50IHRoYW4gb3BlcmF0aW5nIApvbiBkYXRhc2V0cyBpbiBtZW1vcnkgKGUuZy4gbG9hZGluZyBkYXRhIGludG8gbWVtb3J5IHVzaW5nIApgcmVhZF9jc3YoKWApLiBJbnN0ZWFkLCBgZHBseXJgIGNvbW11bmljYXRlcyBkaWZmZXJlbnRseSAKd2l0aCBhIFNRTGl0ZSBkYXRhYmFzZS4gCgpMZXQncyBjb25zaWRlciBvdXIgZXhhbXBsZS4gSWYgd2Ugd2VyZSB0byB1c2Ugc3RyYWlnaHQgU1FMLCAKdGhlIGZvbGxvd2luZyBTUUwgcXVlcnkgcmV0dXJucyB0aGUgZmlyc3QgMTAgcm93cyAKZnJvbSB0aGUgYEFsYnVtYCB0YWJsZToKCmBgYHtyLCBldmFsPUZBTFNFfQpTRUxFQ1QgKgpGUk9NIGBBbGJ1bWAKTElNSVQgMTAKYGBgCgpJbiB0aGUgYmFja2dyb3VuZCwgYGRwbHlyYCBkb2VzIHRoZSBmb2xsb3dpbmc6IAoKKiB0cmFuc2xhdGVzIHlvdXIgUiBjb2RlIGludG8gU1FMCiogc3VibWl0cyBpdCB0byB0aGUgZGF0YWJhc2UKKiB0cmFuc2xhdGVzIHRoZSBkYXRhYmFzZSdzIHJlc3BvbnNlIGludG8gYW4gUiBkYXRhIGZyYW1lCgpUbyBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgYGRwbHlyYCBjb2RlLCB3ZSBjYW4gdXNlIHRoZSAKYHNob3dfcXVlcnkoKWAgZnVuY3Rpb246IApgYGB7cn0KQWxidW0gPC0gdGJsKGNvbm4sICJBbGJ1bSIpCnNob3dfcXVlcnkoaGVhZChBbGJ1bSwgbiA9IDEwKSkKYGBgCgpUaGlzIGlzIG5pY2UgYmVjYXVzZSBpbnN0ZWFkIG9mIGhhdmluZyB0byB3cml0ZSB0aGUgClNRTCBxdWVyeSBvdXJzZWxmLCB3ZSBjYW4ganVzdCB1c2UgdGhlIGBkcGx5cmAgYW5kIFIgCnN5bnRheCB0aGF0IHdlIGFyZSB1c2VkIHRvLiAKCkhvd2V2ZXIsIHRoZSBkb3duc2lkZSBpcyB0aGF0IGBkcGx5cmAgbmV2ZXIgZ2V0cyB0byBzZWUgdGhlIApmdWxsIGBBbGJ1bWAgdGFibGUuIEl0IG9ubHkgc2VuZHMgb3VyIHF1ZXJ5IHRvIHRoZSBkYXRhYmFzZSwgCndhaXRzIGZvciBhIHJlc3BvbnNlIGFuZCByZXR1cm5zIHRoZSBxdWVyeS4gSG93ZXZlciwgaW4gdGhpcyAKd2F5IHdlIGNhbiBpbnRlcmFjdCB3aXRoIGxhcmdlIGRhdGFzZXRzISAKCk1hbnkgb2YgdGhlIHVzdWFsIGBkcGx5cmAgZnVuY3Rpb25zIGFyZSBhdmFpbGFibGUgdG9vOiAKCiogYHNlbGVjdCgpYAoqIGBmaWx0ZXIoKWAKKiBgc3VtbWFyaXplKClgIAoKYW5kIG1hbnkgam9pbiBmdW5jdGlvbnMuIAoKT2sgbGV0J3MgdHJ5IHNvbWUgb2YgdGhlIGZ1bmN0aW9ucyBvdXQuIApGaXJzdCwgbGV0J3MgY291bnQgaG93IG1hbnkgYWxidW1zIGVhY2ggCmFydGlzdCBoYXMgbWFkZS4gCgpgYGB7cn0KdGJsKGNvbm4sICJBbGJ1bSIpICU+JQogIGdyb3VwX2J5KEFydGlzdElkKSAlPiUgCiAgc3VtbWFyaXplKG4gPSBjb3VudChBcnRpc3RJZCkpICU+JSAKICBoZWFkKG49MTApCmBgYAoKTmV4dCwgbGV0J3MgcGxvdCBpdC4gCmBgYHtyfQp0YmwoY29ubiwgIkFsYnVtIikgJT4lCiAgZ3JvdXBfYnkoQXJ0aXN0SWQpICU+JSAKICBzdW1tYXJpemUobiA9IGNvdW50KEFydGlzdElkKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhuKSkgJT4lIAogIGdncGxvdChhZXMoeCA9IEFydGlzdElkLCB5ID0gbikpICsgCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpCmBgYAoKTGV0J3MgYWxzbyBleHRyYWN0IHRoZSBmaXJzdCBsZXR0ZXIgZnJvbSBlYWNoIAphbGJ1bSBhbmQgcGxvdCB0aGUgZnJlcXVlbmN5IG9mIGVhY2ggbGV0dGVyLiAKCmBgYHtyfQp0YmwoY29ubiwgIkFsYnVtIikgJT4lCiAgbXV0YXRlKGZpcnN0X2xldHRlciA9IHN0cl9zdWIoVGl0bGUsIGVuZCA9IDEpKSAlPiUgCiAgZ2dwbG90KGFlcyhmaXJzdF9sZXR0ZXIpKSArIAogIGdlb21fYmFyKCkKYGBgCgoKIyMgT3RoZXIgY29vbCBBUElzCgojIyMgSHVmZmluZ3RvbiBQb3N0IE9waW5pb24gUG9sbGluZyBkYXRhIAoKVGhlIEh1ZmZpbmd0b24gUG9zdCBoYXMgYW4gQVBJIHdoaWNoIHByb3ZpZGVzClVTIG9waW5pb24gcG9sbCBkYXRhIG9uIHZhcmlvdXMgcG9saXRpY2FsIHJhY2VzIAphbmQgb3RoZXIgbm9uLXBvbGl0aWNhbCBvcGluaW9uIHBvbGxzLiAKClRoZXJlIGlzIGFuIFIgcGFja2FnZSBjYWxsZWQgCltgcG9sbHN0UmBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9wb2xsc3RSL2luZGV4Lmh0bWwpCndoaWNoIHByb3ZpZGVzIGFuIGVhc3kgdXNlciBpbnRlcmZhY2UuIAoKRm9yIGV4YW1wbGUsIHRoZSBBUEkgaGFzIGRhdGEgb24gdGhlIApbVHJ1bXAgSm9iIEFwcHJvdmFsXShodHRwOi8vZWxlY3Rpb25zLmh1ZmZpbmd0b25wb3N0LmNvbS9wb2xsc3Rlci90cnVtcC1qb2ItYXBwcm92YWwpCgpIZXJlIHdlIHVzZSB0aGUgYHBvbGxzdGVyX2NoYXJ0c19wb2xscygpYApmdW5jdGlvbjogCgpgYGB7cn0KbGlicmFyeShwb2xsc3RSKQp0cnVtcF9hcHByb3ZhbCA8LSBwb2xsc3Rlcl9jaGFydHNfcG9sbHMoInRydW1wLWpvYi1hcHByb3ZhbCIpCmBgYAoKV2UgY2FuIHNlZSB3aGF0J3MgaW4gdGhlIG9iamVjdDogCmBgYHtyfQpuYW1lcyh0cnVtcF9hcHByb3ZhbCkKYGBgCgpUaGUgYHVybGAgbGlua3MgdG8gdGhlIGRhdGEgaXRzZWxmCmBgYHtyfQp0cnVtcF9hcHByb3ZhbCR1cmwKYGBgCgpUaGUgYGNvbnRlbnRgIGNvbnRhaW5zIHRoZSBwb2xsaW5nIGRhdGE6IApgYGB7cn0KdHJ1bXBfYXBwcm92YWwkY29udGVudApgYGAKClRoaXMgbWlnaHQgYmUgdXNlZnVsIGlmIHlvdSB3ZXJlIGV2ZXIgaW50ZXJlc3RlZCBpbiAKdXNpbmcgcG9sbGluZyBkYXRhLiAKCiMgU3VtbWFyeQoKKiBCZXN0IHByYWN0aWNlcyBmb3Igc2hhcmluZyBkYXRhCiogQmVzdCBwcmFjdGljZXMgZm9yIGRvd25sb2FkaW5nIGFuZCByZWFkaW5nIGluIGRhdGEKICAqIFJlbGF0aXZlIHZlcnN1cyBhYnNvbHV0ZSBwYXRocwogICogRmluZGluZyBhbmQgY3JlYXRpbmcgZmlsZXMgbG9jYWxseQoqIEJlc3QgcHJhY3RpY2VzIGZvciBnZXR0aW5nIGRhdGEgCiAgKiBganNvbmxpdGVgIGZvciBKU09OIChlLmcuIEdpdEh1YiBBUEkpCiAgKiBgcnZlc3RgIHRvIGdyYWIgYWxsIHRoZSBleGFjdCBlbGVtZW50cyB5b3Ugd2FudCAoZS5nLiBib29rIHJldmlld3MpCiAgICAgICogQ2hlY2sgb3V0IHNlbGVjdG9yIGdhZGdldCAKICAqIGBEQklgLCBgUlNRTGl0ZWAsIGBkYnBseXJgIGZvciBpbnRlcmFjdGluZyB3aXRoIGBTUUxpdGVgIGRhdGFic2VzCiAgKiBPdGhlciBBUElzCiAgICAgICogSHVmZmluZ3RvbiBQb3N0IEFQSQogICAgCiMjIE90aGVyIGdvb2QgUiBwYWNrYWdlcyB0byBrbm93IGFib3V0IAoKKiBbYGh0dHJgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvaHR0ci9pbmRleC5odG1sKSBmb3IgdG9vbHMgdG8gd29yayB3aXRoIFVSTHMgYW5kIEhUVFAKKiBbYGdvb2dsZXNoZWV0c2BdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nb29nbGVzaGVldHMvdmlnbmV0dGVzL2Jhc2ljLXVzYWdlLmh0bWwpIHRvIGludGVyYWN0IHdpdGggR29vZ2xlIFNoZWV0cyBpbiBSCiogW2Bnb29nbGVkcml2ZWBdKGh0dHBzOi8vZ29vZ2xlZHJpdmUudGlkeXZlcnNlLm9yZ10oaHR0cDovL2dvb2dsZWRyaXZlLnRpZHl2ZXJzZS5vcmcvKSB0byBpbnRlcmFjdCB3aXRoIHlvdXIgR29vZ2xlIERyaXZlCgoKCgoKCgoK