Motivation

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


Working directories

One of the keys to creating a reproducible workflow is maintaining the proper folder/directory structures across computers, platforms, etc.

Absolute paths

In the early days of R programming, many people worked with absolute paths. When you open up someone’s R code or analysis, you would often see the setwd() function being used. This explicitly tells R to change the absolute path to the folder/directory where work is being done. Although this may work fine for an individual who works by themselves on a single computer, it will quickly lead to headaches and long trails of work when projects are shared with others.

For example, if Mark had a series of scripts scattered throughout a repo, and each of them began with something like

setwd("~/Users/Mark/Documents/folder/that/only/Mark/has")

you would need to go into each script and edit every single one of those absolute paths, such that they worked on your own computer.

Relative paths

An alternative to using absolute paths is to use relative paths. For example, the following command would set the working directory to the /data/ folder in someone’s root directory.

setwd("~/data/")

RStudio .Rproj files

We have already seen how to set up projects in RStudio and make use of the relative file paths associated with a project’s .Rproj file. When you start a project by opening a .Rproj file, RStudio automatically changes the path to the folder/directory where the .Rproj file is located.

The {here} package

Within R, the {here} package will automatically identify the top-level directory of a Git repo, and then direct all other paths relative to that. For more on project-oriented workflows, check out this blog post by Jenny Bryan.

The here::here() function creates a path relative to the folder/directory where the .Rproj file exists.

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

  • Organize each logical project into a folder on your computer (i.e., use a research compendium).

  • Make sure the top-level folder advertises itself as such. This can be as simple as having an empty file named .here. 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, use cd to move to the correct folder before beginning.

You can use getwd() to see your current working directory and you can inspect the files available using list.file() or dir().

## what is the working directory?
getwd()
## [1] "/Users/scheuerl/Documents/GitHub/FISH549/website/lectures/week_04"
## what files exist in the working directory?
list.files()
## [1] "data"                             "images"                          
## [3] "lec_10_getting_data_archived.Rmd" "lec_10_getting_data_cache"       
## [5] "lec_10_getting_data.html"         "lec_10_getting_data.Rmd"         
## [7] "lec_11_getting_help.html"         "lec_11_getting_help.Rmd"         
## [9] "readr_cheatsheet.pdf"
dir()
## [1] "data"                             "images"                          
## [3] "lec_10_getting_data_archived.Rmd" "lec_10_getting_data_cache"       
## [5] "lec_10_getting_data.html"         "lec_10_getting_data.Rmd"         
## [7] "lec_11_getting_help.html"         "lec_11_getting_help.Rmd"         
## [9] "readr_cheatsheet.pdf"

Our current location is in the week_04 sub-folder buried relatively deeply within the course website repository.

Using here() returns the following information:

## load here package
library(here)
## here() starts at /Users/scheuerl/Documents/GitHub/FISH549/website
## show files in base directory
list.files(here())
##  [1] "_list_of_hw.Rmd"         "_list_of_lectures.Rmd"  
##  [3] "_site.yml"               "_syllabus.Rmd"          
##  [5] "_timeline.Rmd"           "callout_code.html"      
##  [7] "docs"                    "FISH549_hex_sticker.png"
##  [9] "homework"                "homework.Rmd"           
## [11] "index.Rmd"               "lectures"               
## [13] "lectures.Rmd"            "LICENSE"                
## [15] "pkg_reviews.Rmd"         "README.md"              
## [17] "references"              "references.Rmd"         
## [19] "syllabus.Rmd"            "webfonts"               
## [21] "website.Rproj"
## show files in references directory
list.files(here("references"))
##  [1] "BES_guide_to_reproducible_code.pdf"      
##  [2] "Broman_Woo_2018_data_in_spreadsheets.pdf"
##  [3] "Bryan_2017_version_control.pdf"          
##  [4] "Crameri_2020_color_misuse.pdf"           
##  [5] "Ellis_Leek_2017_how_to_share_data.pdf"   
##  [6] "Fegraus_2005_intro_EML.pdf"              
##  [7] "fontawesome.css"                         
##  [8] "git_install"                             
##  [9] "git_install_windows.html"                
## [10] "git_install_windows.Rmd"                 
## [11] "github_pat_creation.html"                
## [12] "github_pat_creation.Rmd"                 
## [13] "github_signup.html"                      
## [14] "github_signup.Rmd"                       
## [15] "images"                                  
## [16] "Jones_2006_new_bioinformatics.pdf"       
## [17] "lecture_inst.css"                        
## [18] "Leek_Peng_2015_what_is_the_Q.pdf"        
## [19] "Marwick_etal_2018_research_compendia.pdf"
## [20] "Michener_2005_meta_info_concepts.pdf"    
## [21] "Parker_2017_analysis_development.pdf"    
## [22] "readr_cheatsheet.pdf"                    
## [23] "Ross_etal_2017_tidy_tools.pdf"           
## [24] "rstudio_cheatsheet.pdf"                  
## [25] "set_global_gitignore.html"               
## [26] "set_global_gitignore.Rmd"                
## [27] "Silberzahn_2018_crowdsourcing_paper.pdf" 
## [28] "solid.css"                               
## [29] "Tufte_visual_display_of_info.pdf"

We can also use here() to check the path for a specific file.

here("data", "palmer_penguins.csv")
## [1] "/Users/scheuerl/Documents/GitHub/FISH549/website/data/palmer_penguins.csv"

Checking for local files

You may run into situations where you’d like to

  1. check if a file already exists, and

  2. if not, perhaps create a new folder/directory before downloading or saving the file.

The function file.exists() will test for the presence of a file and return TRUE or FALSE.

For example,

file.exists(here("my", "relative", "path", "filename.csv"))

will look for the filename.csv within the folder/directory /my/relative/path.

Why or when would someone use this? Fitting statistical models in JAGS or Stan can take a lot of time (hours), and I don’t want to have to refit the same models when doing an analysis. Therefore, I often use file.exists() within an if statement to first check if the folder/file exists, and if not, create the folder and save the file.

## set analysis directory
analysis_dir <- here("analysis")

## check if the `/analysis/cache` folder exists
## if not, create it
if(!file.exists(here("analysis_dir", "cache"))) {
  dir.create(here("analysis_dir", "cache"))
}

## check for saved file named `fit_ipm_JAGS.rds`
## if it doesn't exists, fit the model and save it
if(!file.exists(here("analysis_dir", "cache", "fit_ipm_JAGS.rds"))) {
  ## fit the model
  fit_ipm_JAGS <- rjags(...)
  ## save the model fit
  saveRDS(fit_ipm_JAGS, here("analysis_dir", "cache", "fit_ipm_JAGS.rds"))
}

Getting data

Downloading files

## load some helpful packages
library(readr)
library(tidyr)
library(dplyr)

## URL for PDO data
url <- "https://www.ncei.noaa.gov/pub/data/cmb/ersst/v5/v6/index/ersst.v6.pdo.dat"

## get raw data
pdo_raw <- read_lines(url)

## inspect the first few lines
head(pdo_raw)
## [1] " ERSST PDO Index:"                                                           
## [2] "Year  Jan   Feb   Mar   Apr   May   Jun   Jul   Aug   Sep   Oct   Nov   Dec" 
## [3] "1850  1.06 -0.95 -1.77 -2.77 -1.28 -1.46 -0.22  1.10  0.13 -2.41 -1.17  2.74"
## [4] "1851 -0.66  0.73  0.44 -0.45 -0.60 -0.39 -0.17 -0.30  0.37  2.08  1.49  0.86"
## [5] "1852 -0.37 -0.38 -1.30  0.50  1.18  1.28  2.23  2.61  0.30  2.51  0.19 -2.21"
## [6] "1853  0.17 -1.71  2.69  0.87 -0.86 -0.71 -1.70  0.22 -0.12  0.90  0.32 -0.97"

The data are currently stored as a vector with a bunch of character elements. Row 1 looks to be some sort of title and row 2 appears to be the column headers. We need to clean it up a bit before we can use it.

Let’s begin by replacing double-spaces with a single space and dropping the first 2 elements.

## replace double-spaces with a single space
pdo_clean <- gsub("  ", " ", pdo_raw)

## drop the title and col headers (elements 1-2)
pdo_clean <- pdo_clean[-c(1:2)]

Now convert the vector to a data frame and tidy it.

## create a data frame and split the text into columns
pdo_df <- tibble(text = pdo_clean) |>
  separate_wider_delim(
    cols = text, 
    delim = " ",
    names = c("year", month.abb)
  ) |>
  ## tidy the tibble by pivoting the month columns into a single column
  pivot_longer(
    cols = month.abb,
    names_to = "month",
    values_to = "pdo"
  )

## inspect tibble
head(pdo_df)
## # A tibble: 6 × 3
##   year  month pdo  
##   <chr> <chr> <chr>
## 1 1850  Jan   1.06 
## 2 1850  Feb   -0.95
## 3 1850  Mar   -1.77
## 4 1850  Apr   -2.77
## 5 1850  May   -1.28
## 6 1850  Jun   -1.46
tail(pdo_df)
## # A tibble: 6 × 3
##   year  month pdo  
##   <chr> <chr> <chr>
## 1 2026  Jul   -1.35
## 2 2026  Aug   -1.12
## 3 2026  Sep   -0.74
## 4 2026  Oct   99.99
## 5 2026  Nov   99.99
## 6 2026  Dec   99.99

I still see two things here that are (potentially) problematic:

  1. The PDO values are character strings, so we need to convert them to numeric

  2. This file uses a non-standard missing value code (99.99), so we need to convert those to NA

Let’s fix these last two problems.

pdo_df <- pdo_df |>
  # 1. Convert PDO values to numeric
  mutate(pdo = as.numeric(pdo)) |>
  # 2. Convert missing value code (99.99) to NA
  mutate(pdo = ifelse(pdo == 99.99, NA, pdo))

tail(pdo_df)
## # A tibble: 6 × 3
##   year  month   pdo
##   <chr> <chr> <dbl>
## 1 2026  Jul   -1.35
## 2 2026  Aug   -1.12
## 3 2026  Sep   -0.74
## 4 2026  Oct   NA   
## 5 2026  Nov   NA   
## 6 2026  Dec   NA

Now we have a tidy data frame of PDO values that we can use for analysis.


Reading .csv files

Base functions

The read.table() function, and its specific variants read.csv() and read.delim(), is one of the most commonly used functions for reading data into R. The help file for read.table() provides a lot of useful information (see ?read.table).

The read.table() function has several important arguments:

  • file: the name of a file, or a connection
  • header: a logical value indicating if the file has a header line
  • sep: a string indicating how the columns are separated
  • colClasses: a character vector indicating the class of each column in the dataset
  • nrows: the number of rows in the dataset (by default read.table() reads an entire file)
  • comment.char: a character string indicating the comment character, which defaults to #. If there are no commented lines in your file, it’s worth setting this to be the empty string "".
  • skip: the number of lines to skip from the beginning
  • stringsAsFactors: should character variables be coded as factors? Note that as of R version 4.0, this defaults to FALSE

For small to moderately sized files, you can usually call read.table() without specifying any other arguments.

For example, the following line

raw_data <- read.table("flat_file.txt")

would

  • skip any lines that begin with a #
  • figure out how many rows there are (and how much memory needs to be allocated)
  • figure what type of variable is in each column of the table

On the other hand, explicitly specifying all of these arguments directly (i.e., “defensive programming”) makes R run faster and more efficiently.

The read.csv() function is identical to read.table() except that some of the defaults are set differently (e.g., sep argument).

There is a palmer_penguins.csv file located deep in our working directory structure.

## read penguin data
data_raw <- read.csv(here("lectures", "week_04", "data", "palmer_penguins.csv"))

## inspect its content
head(data_raw)
##   studyName Sample.Number                             Species Region    Island
## 1   PAL0708             1 Adelie Penguin (Pygoscelis adeliae) Anvers Torgersen
## 2   PAL0708             2 Adelie Penguin (Pygoscelis adeliae) Anvers Torgersen
## 3   PAL0708             3 Adelie Penguin (Pygoscelis adeliae) Anvers Torgersen
## 4   PAL0708             4 Adelie Penguin (Pygoscelis adeliae) Anvers Torgersen
## 5   PAL0708             5 Adelie Penguin (Pygoscelis adeliae) Anvers Torgersen
## 6   PAL0708             6 Adelie Penguin (Pygoscelis adeliae) Anvers Torgersen
##                Stage Individual.ID Clutch.Completion Date.Egg
## 1 Adult, 1 Egg Stage          N1A1               Yes 11/11/07
## 2 Adult, 1 Egg Stage          N1A2               Yes 11/11/07
## 3 Adult, 1 Egg Stage          N2A1               Yes 11/16/07
## 4 Adult, 1 Egg Stage          N2A2               Yes 11/16/07
## 5 Adult, 1 Egg Stage          N3A1               Yes 11/16/07
## 6 Adult, 1 Egg Stage          N3A2               Yes 11/16/07
##   Culmen.Length..mm. Culmen.Depth..mm. Flipper.Length..mm. Body.Mass..g.    Sex
## 1               39.1              18.7                 181          3750   MALE
## 2               39.5              17.4                 186          3800 FEMALE
## 3               40.3              18.0                 195          3250 FEMALE
## 4                 NA                NA                  NA            NA   <NA>
## 5               36.7              19.3                 193          3450 FEMALE
## 6               39.3              20.6                 190          3650   MALE
##   Delta.15.N..o.oo. Delta.13.C..o.oo.                       Comments
## 1                NA                NA Not enough blood for isotopes.
## 2           8.94956         -24.69454                           <NA>
## 3           8.36821         -25.33302                           <NA>
## 4                NA                NA             Adult not sampled.
## 5           8.76651         -25.32426                           <NA>
## 6           8.66496         -25.29805                           <NA>

Tidyverse

The newer Tidyverse collection of packages includes the {readr} package for reading in data files and assigning them the tibble object class. Click here to see the cheat sheet.

Read the Palmer penguins data into R with the readr package and then inspect its contents.

## read penguin data
data_raw <- read_csv(here("lectures", "week_04", "data", "palmer_penguins.csv"))
## Rows: 344 Columns: 17
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (10): studyName, Species, Region, Island, Stage, Individual ID, Clutch C...
## dbl  (7): Sample Number, Culmen Length (mm), Culmen Depth (mm), Flipper Leng...
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
## inspect its content
head(data_raw)
## # A tibble: 6 × 17
##   studyName `Sample Number` Species          Region Island Stage `Individual ID`
##   <chr>               <dbl> <chr>            <chr>  <chr>  <chr> <chr>          
## 1 PAL0708                 1 Adelie Penguin … Anvers Torge… Adul… N1A1           
## 2 PAL0708                 2 Adelie Penguin … Anvers Torge… Adul… N1A2           
## 3 PAL0708                 3 Adelie Penguin … Anvers Torge… Adul… N2A1           
## 4 PAL0708                 4 Adelie Penguin … Anvers Torge… Adul… N2A2           
## 5 PAL0708                 5 Adelie Penguin … Anvers Torge… Adul… N3A1           
## 6 PAL0708                 6 Adelie Penguin … Anvers Torge… Adul… N3A2           
## # ℹ 10 more variables: `Clutch Completion` <chr>, `Date Egg` <chr>,
## #   `Culmen Length (mm)` <dbl>, `Culmen Depth (mm)` <dbl>,
## #   `Flipper Length (mm)` <dbl>, `Body Mass (g)` <dbl>, Sex <chr>,
## #   `Delta 15 N (o/oo)` <dbl>, `Delta 13 C (o/oo)` <dbl>, Comments <chr>


Reading Excel files

Excel is very popular for entering and storing data, although we’ve already seen some of the pitfalls that you might encounter. There is a great package called {readxl} that allows you to read Excel files directly into R. Here’s an example that uses an Excel file called world_bank_climate_change.xls from the World Bank’s website. It looks like this:


You can download the Excel file and save it in your working directory. Mark’s is stored in a folder called data.

We can get a list of the individual worksheets within the workbook with excel_sheets().

## load readxl
library(readxl)

## inspect the sheet names
excel_sheets("data/world_bank_climate_change.xls")
## [1] "Data"    "Country" "Series"

We can read in a specific worksheet with read_excel().

## read in the "Data" worksheet
cc_data <- read_excel("data/world_bank_climate_change.xls", sheet = "Data")

## inspect it
head(cc_data)
## # A tibble: 6 × 28
##   `Country code` `Country name`       `Series code` `Series name` SCALE Decimals
##   <chr>          <chr>                <chr>         <chr>         <dbl>    <dbl>
## 1 ABW            Aruba                AG.LND.EL5M.… Land area be…     0        1
## 2 ADO            Andorra              AG.LND.EL5M.… Land area be…     0        1
## 3 AFG            Afghanistan          AG.LND.EL5M.… Land area be…     0        1
## 4 AGO            Angola               AG.LND.EL5M.… Land area be…     0        1
## 5 ALB            Albania              AG.LND.EL5M.… Land area be…     0        1
## 6 ARE            United Arab Emirates AG.LND.EL5M.… Land area be…     0        1
## # ℹ 22 more variables: `1990` <chr>, `1991` <chr>, `1992` <chr>, `1993` <chr>,
## #   `1994` <chr>, `1995` <chr>, `1996` <chr>, `1997` <chr>, `1998` <chr>,
## #   `1999` <chr>, `2000` <chr>, `2001` <chr>, `2002` <chr>, `2003` <chr>,
## #   `2004` <chr>, `2005` <chr>, `2006` <chr>, `2007` <chr>, `2008` <chr>,
## #   `2009` <chr>, `2010` <chr>, `2011` <chr>

If you are only interested in a certain subset of the data, the range argument can be used to select which cells to read.

For example, to read in the columns related to cereal yield for the years 1990 through 1995 for country names that start with “B” in the Data tab, we would only want to read in the rectangle of cells from G481 to L498.

The authors of this file decided to use .. to represent missing values, rather than something more conventional like NA or N/A, so hence the argument na = "..".

cc_data <- read_excel("data/world_bank_climate_change.xls",
                      sheet     = "Data",
                      range     = "G481:L498",
                      col_names = as.character(c(1990:1995)),
                      na        = "..")
head(cc_data)
## # A tibble: 6 × 6
##   `1990` `1991` `1992` `1993` `1994` `1995`
##    <dbl>  <dbl>  <dbl>  <dbl>  <dbl>  <dbl>
## 1  1348.  1369.  1391.  1387.  1289.  1332.
## 2    NA     NA     NA     NA     NA     NA 
## 3   848.   878.   913.   919.   957.  1086.
## 4   600.   876.   872.   936.   788.   851 
## 5  2490.  2586.  2626.  2648.  2476.  2593.
## 6  3954.  3990   2954.  2512.  2799.  3069.

Workflow example

If you’d like to see an example of how one might use the {readxl} package in a workflow, check out Mark’s project on estimating trends in the density of bull trout across the Pacific Northwest here.


Reading a Google Sheet

The {googlesheets4} package allows you to inspect and read Google Sheets.

The gs_ls() function returns a data frame of the sheets listed in your Google Sheets home screen located at https://docs.google.com/spreadsheets/. This will include sheets that you own and sheets owned by others that you have permission to access.

To begin, you need to use gs4_deauth() to authorize googlesheets4 to access the contents of your Google Drive where the Sheets are stored (here I’m allowing only access to public sheets).

## load the packge
library(googlesheets4)

## authorize for reading public-only sheets
gs4_deauth()

We can read in a sheet by one of two methods:

  1. specify the entire URL

  2. specify only the sheet ID

Option 1: Specify the entire URL

## specify URL
gs_url <- "https://docs.google.com/spreadsheets/d/1ct3MMMzEX82BeqJcUw3uYD5b4CJrKUmRYCK-wVA3yig/edit#gid=0"

## read from URL
read_sheet(gs_url)
## ✔ Reading from "FISH549_example".
## ✔ Range 'Sheet1'.
## # A tibble: 34 × 5
##     year flow_wtr flow_sum  npgo h_releases
##    <dbl>    <dbl>    <dbl> <dbl>      <dbl>
##  1  1978     234.     29.4 -0.75     340416
##  2  1979     391.     28.3 -0.22     274090
##  3  1980     490.     41.9 -0.69     216537
##  4  1981     253.     40.5 -0.09     300942
##  5  1982     248.     39.6  0.62     241931
##  6  1983     306.     40.8 -0.39     256238
##  7  1984     199.     45.9 -0.73     319220
##  8  1985     205.     42.8  0.43     125280
##  9  1986     207      45.0  1.32     229013
## 10  1987     190.     36.5  0.61     217948
## # ℹ 24 more rows
## assign it to an object
gs_raw <- read_sheet(gs_url)
## ✔ Reading from "FISH549_example".
## ✔ Range 'Sheet1'.

Option 2: Specify the sheet ID

## specify ID
gs_id <- as_sheets_id(gs_url)

## read from ID
read_sheet(gs_id)
## ✔ Reading from "FISH549_example".
## ✔ Range 'Sheet1'.
## # A tibble: 34 × 5
##     year flow_wtr flow_sum  npgo h_releases
##    <dbl>    <dbl>    <dbl> <dbl>      <dbl>
##  1  1978     234.     29.4 -0.75     340416
##  2  1979     391.     28.3 -0.22     274090
##  3  1980     490.     41.9 -0.69     216537
##  4  1981     253.     40.5 -0.09     300942
##  5  1982     248.     39.6  0.62     241931
##  6  1983     306.     40.8 -0.39     256238
##  7  1984     199.     45.9 -0.73     319220
##  8  1985     205.     42.8  0.43     125280
##  9  1986     207      45.0  1.32     229013
## 10  1987     190.     36.5  0.61     217948
## # ℹ 24 more rows
## assign it to an object
gs_raw <- read_sheet(gs_id)
## ✔ Reading from "FISH549_example".
## ✔ Range 'Sheet1'.

We can browse the sheet’s metadata with gs4_browse().

## browse sheet contents
gs4_get(gs_id)
## 
## ── <googlesheets4_spreadsheet> ─────────────────────────────────────────────────
## Spreadsheet name: "FISH549_example"                           
##               ID: 1ct3MMMzEX82BeqJcUw3uYD5b4CJrKUmRYCK-wVA3yig
##           Locale: en_US                                       
##        Time zone: America/Los_Angeles                         
##      # of sheets: 1                                           
## 
## ── <sheets> ────────────────────────────────────────────────────────────────────
## (Sheet name): (Nominal extent in rows x columns)
##     'Sheet1': 1001 x 26

We can also read in just a subset of cells by specifying the range argument.

read_sheet(gs_id, range = "A1:B10")
## ✔ Reading from "FISH549_example".
## ✔ Range 'A1:B10'.
## # A tibble: 9 × 2
##    year flow_wtr
##   <dbl>    <dbl>
## 1  1978     234.
## 2  1979     391.
## 3  1980     490.
## 4  1981     253.
## 5  1982     248.
## 6  1983     306.
## 7  1984     199.
## 8  1985     205.
## 9  1986     207


Reading a JSON file

What is JSON?

JSON (or JavaScript Object Notation) is a file format that stores information in an organized, logical, easy-to-access manner (i.e., “human readable”). For example, here is what a JSON file looks like:

var mark = {
  "city" : "Seattle",
  "state" : "WA", 
  "hobbies" : {
    "hobby1" : "cycling",
    "hobby2" : "skiing"
  }
  "bikes" : {
    "bike1" : "Ridley Helium",
    "bike2" : "Niner RLT",
    "bike3" : "Santa Cruz Tallboy"
  }
  "skis" : {
    "skis1" : "K2 Hardside",
    "skis2" : "Black Crows Corvus Freebird"
  }
}

Some features about JSON object:

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

Using the GitHub API

Many organizations provide an application programming interface (API) for accessing their data. GitHub has an API that allows you to explore information about users. Let’s use this API to explore some of Mark’s GitHub activities.

To do so, we’ll use the fromJSON() function from the {jsonlite} package to convert the data from a JSON object to a data frame.

Begin by reading in the JSON file located at https://api.github.com/users/mdscheuerell/repos.

## load the pckage
library(jsonlite)

## set the URL
github_url = "https://api.github.com/users/mdscheuerell/repos"

## get the JSON data
data_json <- fromJSON(github_url)

The function fromJSON() will convert the JSON file into a data frame.

You can inspect the field names in the data frame with names().

## all of the JSON names
names(data_json)
##  [1] "id"                           "node_id"                     
##  [3] "name"                         "full_name"                   
##  [5] "private"                      "owner"                       
##  [7] "html_url"                     "description"                 
##  [9] "fork"                         "url"                         
## [11] "forks_url"                    "keys_url"                    
## [13] "collaborators_url"            "teams_url"                   
## [15] "hooks_url"                    "issue_events_url"            
## [17] "events_url"                   "assignees_url"               
## [19] "branches_url"                 "tags_url"                    
## [21] "blobs_url"                    "git_tags_url"                
## [23] "git_refs_url"                 "trees_url"                   
## [25] "statuses_url"                 "languages_url"               
## [27] "stargazers_url"               "contributors_url"            
## [29] "subscribers_url"              "subscription_url"            
## [31] "commits_url"                  "git_commits_url"             
## [33] "comments_url"                 "issue_comment_url"           
## [35] "contents_url"                 "compare_url"                 
## [37] "merges_url"                   "archive_url"                 
## [39] "downloads_url"                "issues_url"                  
## [41] "pulls_url"                    "milestones_url"              
## [43] "notifications_url"            "labels_url"                  
## [45] "releases_url"                 "deployments_url"             
## [47] "created_at"                   "updated_at"                  
## [49] "pushed_at"                    "git_url"                     
## [51] "ssh_url"                      "clone_url"                   
## [53] "svn_url"                      "homepage"                    
## [55] "size"                         "stargazers_count"            
## [57] "watchers_count"               "language"                    
## [59] "has_issues"                   "has_projects"                
## [61] "has_downloads"                "has_wiki"                    
## [63] "has_pages"                    "has_discussions"             
## [65] "forks_count"                  "mirror_url"                  
## [67] "archived"                     "disabled"                    
## [69] "open_issues_count"            "license"                     
## [71] "allow_forking"                "is_template"                 
## [73] "web_commit_signoff_required"  "has_pull_requests"           
## [75] "pull_request_creation_policy" "topics"                      
## [77] "visibility"                   "forks"                       
## [79] "open_issues"                  "watchers"                    
## [81] "default_branch"

Now we can query these data to answer some questions about Mark’s GitHub profile and activities. (Note that the following results are only based upon the first 30 repos listed in Mark’s account.)

How many of Mark’s repos have been forked?

## table of the number of forks
table(data_json$forks)
## 
##  0  1  2 
## 23  6  1

Only a total of 7 of the firt 30 repos have been forked.

What are the languages used in these repos?

## table number of the different languages
table(data_json$language)
## 
## HTML  Lua    R  TeX 
##    7    1    8    3

How many repos have open issues that Mark should address?

## how many repos have open issues? 
table(data_json$open_issues_count)
## 
##  0  1  2  3  5 
## 26  1  1  1  1

Not too bad, but clearly he’s behind on some things. One of his repos has 5 open issues!


Other good R packages

Here are some other R packages that are helpful for working with remote data sources:

LS0tCnRpdGxlOiAiUmVhZGluZyBkYXRhIGludG8gUiIKYXV0aG9yOiAiTWFyayBTY2hldWVyZWxsIgpkYXRlOiAiMjcgSmFudWFyeSAyMDI3IgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDoKICAgIHRoZW1lOiAKICAgICAgYm9vdHN3YXRjaDogam91cm5hbAogICAgICBwcmltYXJ5OiAiIzMyMDA2ZSIKICAgIGhpZ2hsaWdodDogdGV4dG1hdGUKICAgIGNzczogLi4vbGVjdHVyZV9pbnN0LmNzcwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIHRvY19kZXB0aDogMwotLS0KCjxicj4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgIGZpZy5hbGlnbiA9ICJjZW50ZXIiLCBvdXQud2lkdGggPSAnOTAlJykKYGBgCgojIE1vdGl2YXRpb24KClRvZGF5IHdlIGFyZSBnb2luZyB0byB0YWxrIGFib3V0IHJlYWRpbmcgZGF0YSwgZXhhbXBsZXMgb2YgY29tbW9uIGRhdGEgZm9ybWF0cywgYW5kIHVzZWZ1bCB0b29scyB0byBhY2Nlc3MgZGF0YS4gCgoqKioKCiMgV29ya2luZyBkaXJlY3RvcmllcwoKT25lIG9mIHRoZSBrZXlzIHRvIGNyZWF0aW5nIGEgcmVwcm9kdWNpYmxlIHdvcmtmbG93IGlzIG1haW50YWluaW5nIHRoZSBwcm9wZXIgZm9sZGVyL2RpcmVjdG9yeSBzdHJ1Y3R1cmVzIGFjcm9zcyBjb21wdXRlcnMsIHBsYXRmb3JtcywgZXRjLiAKCiMjIEFic29sdXRlIHBhdGhzCgpJbiB0aGUgZWFybHkgZGF5cyBvZiAqKlIqKiBwcm9ncmFtbWluZywgbWFueSBwZW9wbGUgd29ya2VkIHdpdGggKmFic29sdXRlKiBwYXRocy4gV2hlbiB5b3Ugb3BlbiB1cCBzb21lb25lJ3MgKipSKiogY29kZSBvciBhbmFseXNpcywgeW91IHdvdWxkIG9mdGVuIHNlZSB0aGUgYHNldHdkKClgIGZ1bmN0aW9uIGJlaW5nIHVzZWQuIFRoaXMgZXhwbGljaXRseSB0ZWxscyAqKlIqKiB0byBjaGFuZ2UgdGhlIGFic29sdXRlIHBhdGggdG8gdGhlIGZvbGRlci9kaXJlY3Rvcnkgd2hlcmUgd29yayBpcyBiZWluZyBkb25lLiBBbHRob3VnaCB0aGlzIG1heSB3b3JrIGZpbmUgZm9yIGFuIGluZGl2aWR1YWwgd2hvIHdvcmtzIGJ5IHRoZW1zZWx2ZXMgb24gYSBzaW5nbGUgY29tcHV0ZXIsIGl0IHdpbGwgcXVpY2tseSBsZWFkIHRvIGhlYWRhY2hlcyBhbmQgbG9uZyB0cmFpbHMgb2Ygd29yayB3aGVuIHByb2plY3RzIGFyZSBzaGFyZWQgd2l0aCBvdGhlcnMuCgpGb3IgZXhhbXBsZSwgaWYgTWFyayBoYWQgYSBzZXJpZXMgb2Ygc2NyaXB0cyBzY2F0dGVyZWQgdGhyb3VnaG91dCBhIHJlcG8sIGFuZCBlYWNoIG9mIHRoZW0gYmVnYW4gd2l0aCBzb21ldGhpbmcgbGlrZSAKCmBgYHtyIHNldHdkX2V4LCBldmFsPUZBTFNFfQpzZXR3ZCgifi9Vc2Vycy9NYXJrL0RvY3VtZW50cy9mb2xkZXIvdGhhdC9vbmx5L01hcmsvaGFzIikKYGBgCgp5b3Ugd291bGQgbmVlZCB0byBnbyBpbnRvIGVhY2ggc2NyaXB0IGFuZCBlZGl0IGV2ZXJ5IHNpbmdsZSBvbmUgb2YgdGhvc2UgKmFic29sdXRlKiBwYXRocywgc3VjaCB0aGF0IHRoZXkgd29ya2VkIG9uIHlvdXIgb3duIGNvbXB1dGVyLgoKIyMgUmVsYXRpdmUgcGF0aHMKCkFuIGFsdGVybmF0aXZlIHRvIHVzaW5nIGFic29sdXRlIHBhdGhzIGlzIHRvIHVzZSByZWxhdGl2ZSBwYXRocy4gRm9yIGV4YW1wbGUsIHRoZSBmb2xsb3dpbmcgY29tbWFuZCB3b3VsZCBzZXQgdGhlIHdvcmtpbmcgZGlyZWN0b3J5IHRvIHRoZSBgL2RhdGEvYCBmb2xkZXIgaW4gc29tZW9uZSdzIHJvb3QgZGlyZWN0b3J5LgoKYGBge3Igc2V0d2RfcmVsYXRpdmUsIGV2YWw9RkFMU0V9CnNldHdkKCJ+L2RhdGEvIikKYGBgCgojIyMgUlN0dWRpbyBgLlJwcm9qYCBmaWxlcwoKV2UgaGF2ZSBhbHJlYWR5IHNlZW4gaG93IHRvIHNldCB1cCBwcm9qZWN0cyBpbiAqKlJTdHVkaW8qKiBhbmQgbWFrZSB1c2Ugb2YgdGhlIHJlbGF0aXZlIGZpbGUgcGF0aHMgYXNzb2NpYXRlZCB3aXRoIGEgcHJvamVjdCdzIGAuUnByb2pgIGZpbGUuIFdoZW4geW91IHN0YXJ0IGEgcHJvamVjdCBieSBvcGVuaW5nIGEgYC5ScHJvamAgZmlsZSwgKipSU3R1ZGlvKiogYXV0b21hdGljYWxseSBjaGFuZ2VzIHRoZSBwYXRoIHRvIHRoZSBmb2xkZXIvZGlyZWN0b3J5IHdoZXJlIHRoZSBgLlJwcm9qYCBmaWxlIGlzIGxvY2F0ZWQuIAoKIyMjIFRoZSBge2hlcmV9YCBwYWNrYWdlCgpXaXRoaW4gKipSKiosIHRoZSBbYHtoZXJlfWBdKGh0dHBzOi8vZ2l0aHViLmNvbS9yLWxpYi9oZXJlKSBwYWNrYWdlIHdpbGwgYXV0b21hdGljYWxseSBpZGVudGlmeSB0aGUgdG9wLWxldmVsIGRpcmVjdG9yeSBvZiBhICoqR2l0KiogcmVwbywgYW5kIHRoZW4gZGlyZWN0IGFsbCBvdGhlciBwYXRocyByZWxhdGl2ZSB0byB0aGF0LiBGb3IgbW9yZSBvbiBwcm9qZWN0LW9yaWVudGVkIHdvcmtmbG93cywgY2hlY2sgb3V0IFt0aGlzIGJsb2cgcG9zdF0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy8yMDE3LzEyL3dvcmtmbG93LXZzLXNjcmlwdC8pIGJ5IEplbm55IEJyeWFuLgoKOjo6IHRpcAoKVGhlIGBoZXJlOjpoZXJlKClgIGZ1bmN0aW9uIGNyZWF0ZXMgYSBwYXRoICpyZWxhdGl2ZSogdG8gdGhlIGZvbGRlci9kaXJlY3Rvcnkgd2hlcmUgdGhlIGAuUnByb2pgIGZpbGUgZXhpc3RzLgoKOjo6CgpJbnN0ZWFkIG9mIHVzaW5nIGBzZXR3ZCgpYCBhdCB0aGUgdG9wIHlvdXIgYC5SYCBvciBgLlJtZGAgZmlsZSwgSmVubnkgc3VnZ2VzdHM6IAoKKiBPcmdhbml6ZSBlYWNoIGxvZ2ljYWwgcHJvamVjdCBpbnRvIGEgZm9sZGVyIG9uIHlvdXIgY29tcHV0ZXIgKGkuZS4sIHVzZSBhIHJlc2VhcmNoIGNvbXBlbmRpdW0pLgoKKiBNYWtlIHN1cmUgdGhlIHRvcC1sZXZlbCBmb2xkZXIgYWR2ZXJ0aXNlcyBpdHNlbGYgYXMgc3VjaC4gVGhpcyBjYW4gYmUgYXMgc2ltcGxlIGFzIGhhdmluZyBhbiBlbXB0eSBmaWxlIG5hbWVkIGAuaGVyZWAuIElmIHlvdSB1c2UgKipSU3R1ZGlvKiogYW5kL29yICoqR2l0KiosIHRob3NlIGJvdGggbGVhdmUgY2hhcmFjdGVyaXN0aWMgZmlsZXMgYmVoaW5kIHRoYXQgd2lsbCBnZXQgdGhlIGpvYiBkb25lLgoKKiBVc2UgdGhlIGBoZXJlKClgIGZ1bmN0aW9uIGZyb20gdGhlIGB7aGVyZX1gIHBhY2thZ2UgdG8gYnVpbGQgdGhlIHBhdGggd2hlbiB5b3UgcmVhZCBvciB3cml0ZSBhIGZpbGUuIENyZWF0ZSBwYXRocyByZWxhdGl2ZSB0byB0aGUgdG9wLWxldmVsIGRpcmVjdG9yeS4KCiogV2hlbmV2ZXIgeW91IHdvcmsgb24gdGhpcyBwcm9qZWN0LCBsYXVuY2ggdGhlICoqUioqIHByb2Nlc3MgZnJvbSB0aGUgcHJvamVjdCdzIHRvcC1sZXZlbCBkaXJlY3RvcnkuIElmIHlvdSBsYXVuY2ggKipSKiogZnJvbSB0aGUgc2hlbGwsIHVzZSBgY2RgIHRvIG1vdmUgdG8gdGhlIGNvcnJlY3QgZm9sZGVyIGJlZm9yZSBiZWdpbm5pbmcuCgo6OjogdGlwCgpZb3UgY2FuIHVzZSBgZ2V0d2QoKWAgdG8gc2VlIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSBhbmQgeW91IGNhbiBpbnNwZWN0IHRoZSBmaWxlcyBhdmFpbGFibGUgdXNpbmcgYGxpc3QuZmlsZSgpYCBvciBgZGlyKClgLgoKOjo6CgpgYGB7ciBjaGtfZGlyX2ZpbGVzfQojIyB3aGF0IGlzIHRoZSB3b3JraW5nIGRpcmVjdG9yeT8KZ2V0d2QoKQoKIyMgd2hhdCBmaWxlcyBleGlzdCBpbiB0aGUgd29ya2luZyBkaXJlY3Rvcnk/Cmxpc3QuZmlsZXMoKQpkaXIoKQpgYGAKCk91ciBjdXJyZW50IGxvY2F0aW9uIGlzIGluIHRoZSBgd2Vla18wNGAgc3ViLWZvbGRlciBidXJpZWQgcmVsYXRpdmVseSBkZWVwbHkgd2l0aGluIHRoZSBjb3Vyc2UgYHdlYnNpdGVgIHJlcG9zaXRvcnkuIAoKVXNpbmcgYGhlcmUoKWAgcmV0dXJucyB0aGUgZm9sbG93aW5nIGluZm9ybWF0aW9uOgoKYGBge3IgaGVyZV9leH0KIyMgbG9hZCBoZXJlIHBhY2thZ2UKbGlicmFyeShoZXJlKQoKIyMgc2hvdyBmaWxlcyBpbiBiYXNlIGRpcmVjdG9yeQpsaXN0LmZpbGVzKGhlcmUoKSkKCiMjIHNob3cgZmlsZXMgaW4gcmVmZXJlbmNlcyBkaXJlY3RvcnkKbGlzdC5maWxlcyhoZXJlKCJyZWZlcmVuY2VzIikpCmBgYAoKOjo6IHRpcAoKV2UgY2FuIGFsc28gdXNlIGBoZXJlKClgIHRvIGNoZWNrIHRoZSBwYXRoIGZvciBhIHNwZWNpZmljIGZpbGUuCgo6OjoKCmBgYHtyfQpoZXJlKCJkYXRhIiwgInBhbG1lcl9wZW5ndWlucy5jc3YiKQpgYGAKCiMjIENoZWNraW5nIGZvciBsb2NhbCBmaWxlcwoKWW91IG1heSBydW4gaW50byBzaXR1YXRpb25zIHdoZXJlIHlvdSdkIGxpa2UgdG8gCgoxLiBjaGVjayBpZiBhIGZpbGUgYWxyZWFkeSBleGlzdHMsIGFuZAoKMi4gaWYgbm90LCBwZXJoYXBzIGNyZWF0ZSBhIG5ldyBmb2xkZXIvZGlyZWN0b3J5IGJlZm9yZSBkb3dubG9hZGluZyBvciBzYXZpbmcgdGhlIGZpbGUuCgo6OjogdGlwCgpUaGUgZnVuY3Rpb24gYGZpbGUuZXhpc3RzKClgIHdpbGwgdGVzdCBmb3IgdGhlIHByZXNlbmNlIG9mIGEgZmlsZSBhbmQgcmV0dXJuIGBUUlVFYCBvciBgRkFMU0VgLgoKOjo6CgpGb3IgZXhhbXBsZSwKCmBmaWxlLmV4aXN0cyhoZXJlKCJteSIsICJyZWxhdGl2ZSIsICJwYXRoIiwgImZpbGVuYW1lLmNzdiIpKWAKCndpbGwgbG9vayBmb3IgdGhlIGBmaWxlbmFtZS5jc3ZgIHdpdGhpbiB0aGUgZm9sZGVyL2RpcmVjdG9yeSBgL215L3JlbGF0aXZlL3BhdGhgLgoKV2h5IG9yIHdoZW4gd291bGQgc29tZW9uZSB1c2UgdGhpcz8gRml0dGluZyBzdGF0aXN0aWNhbCBtb2RlbHMgaW4gKipKQUdTKiogb3IgKipTdGFuKiogY2FuIHRha2UgYSBsb3Qgb2YgdGltZSAoaG91cnMpLCBhbmQgSSBkb24ndCB3YW50IHRvIGhhdmUgdG8gcmVmaXQgdGhlIHNhbWUgbW9kZWxzIHdoZW4gZG9pbmcgYW4gYW5hbHlzaXMuIFRoZXJlZm9yZSwgSSBvZnRlbiB1c2UgYGZpbGUuZXhpc3RzKClgIHdpdGhpbiBhbiBgaWZgIHN0YXRlbWVudCB0byBmaXJzdCBjaGVjayBpZiB0aGUgZm9sZGVyL2ZpbGUgZXhpc3RzLCBhbmQgaWYgbm90LCAgY3JlYXRlIHRoZSBmb2xkZXIgYW5kIHNhdmUgdGhlIGZpbGUuCgpgYGB7ciBoZXJlX2V4X3NhdmUsIGV2YWwgPSBGQUxTRX0KIyMgc2V0IGFuYWx5c2lzIGRpcmVjdG9yeQphbmFseXNpc19kaXIgPC0gaGVyZSgiYW5hbHlzaXMiKQoKIyMgY2hlY2sgaWYgdGhlIGAvYW5hbHlzaXMvY2FjaGVgIGZvbGRlciBleGlzdHMKIyMgaWYgbm90LCBjcmVhdGUgaXQKaWYoIWZpbGUuZXhpc3RzKGhlcmUoImFuYWx5c2lzX2RpciIsICJjYWNoZSIpKSkgewogIGRpci5jcmVhdGUoaGVyZSgiYW5hbHlzaXNfZGlyIiwgImNhY2hlIikpCn0KCiMjIGNoZWNrIGZvciBzYXZlZCBmaWxlIG5hbWVkIGBmaXRfaXBtX0pBR1MucmRzYAojIyBpZiBpdCBkb2Vzbid0IGV4aXN0cywgZml0IHRoZSBtb2RlbCBhbmQgc2F2ZSBpdAppZighZmlsZS5leGlzdHMoaGVyZSgiYW5hbHlzaXNfZGlyIiwgImNhY2hlIiwgImZpdF9pcG1fSkFHUy5yZHMiKSkpIHsKICAjIyBmaXQgdGhlIG1vZGVsCiAgZml0X2lwbV9KQUdTIDwtIHJqYWdzKC4uLikKICAjIyBzYXZlIHRoZSBtb2RlbCBmaXQKICBzYXZlUkRTKGZpdF9pcG1fSkFHUywgaGVyZSgiYW5hbHlzaXNfZGlyIiwgImNhY2hlIiwgImZpdF9pcG1fSkFHUy5yZHMiKSkKfQpgYGAKCioqKgoKIyBHZXR0aW5nIGRhdGEKCiMjIERvd25sb2FkaW5nIGZpbGVzCgpgYGB7ciByZWFkX1BET19kYXRhLCBtZXNzYWdlID0gRkFMU0V9CiMjIGxvYWQgc29tZSBoZWxwZnVsIHBhY2thZ2VzCmxpYnJhcnkocmVhZHIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoZHBseXIpCgojIyBVUkwgZm9yIFBETyBkYXRhCnVybCA8LSAiaHR0cHM6Ly93d3cubmNlaS5ub2FhLmdvdi9wdWIvZGF0YS9jbWIvZXJzc3QvdjUvdjYvaW5kZXgvZXJzc3QudjYucGRvLmRhdCIKCiMjIGdldCByYXcgZGF0YQpwZG9fcmF3IDwtIHJlYWRfbGluZXModXJsKQoKIyMgaW5zcGVjdCB0aGUgZmlyc3QgZmV3IGxpbmVzCmhlYWQocGRvX3JhdykKYGBgCgo6Ojogbm90ZQoKVGhlIGRhdGEgYXJlIGN1cnJlbnRseSBzdG9yZWQgYXMgYSB2ZWN0b3Igd2l0aCBhIGJ1bmNoIG9mIGNoYXJhY3RlciBlbGVtZW50cy4gUm93IDEgbG9va3MgdG8gYmUgc29tZSBzb3J0IG9mIHRpdGxlIGFuZCByb3cgMiBhcHBlYXJzIHRvIGJlIHRoZSBjb2x1bW4gaGVhZGVycy4gV2UgbmVlZCB0byBjbGVhbiBpdCB1cCBhIGJpdCBiZWZvcmUgd2UgY2FuIHVzZSBpdC4KCjo6OgoKOjo6IHRhc2sKCkxldCdzIGJlZ2luIGJ5IHJlcGxhY2luZyBkb3VibGUtc3BhY2VzIHdpdGggYSBzaW5nbGUgc3BhY2UgYW5kIGRyb3BwaW5nIHRoZSBmaXJzdCAyIGVsZW1lbnRzLgoKOjo6CgpgYGB7ciBjbGVhbl9QRE9fdmVjdG9yfQojIyByZXBsYWNlIGRvdWJsZS1zcGFjZXMgd2l0aCBhIHNpbmdsZSBzcGFjZQpwZG9fY2xlYW4gPC0gZ3N1YigiICAiLCAiICIsIHBkb19yYXcpCgojIyBkcm9wIHRoZSB0aXRsZSBhbmQgY29sIGhlYWRlcnMgKGVsZW1lbnRzIDEtMikKcGRvX2NsZWFuIDwtIHBkb19jbGVhblstYygxOjIpXQpgYGAKCjo6OiB0YXNrCgpOb3cgY29udmVydCB0aGUgdmVjdG9yIHRvIGEgZGF0YSBmcmFtZSBhbmQgdGlkeSBpdC4KCjo6OgoKYGBge3IgdGlkeV9QRE9fZGF0YSwgaG9sZCA9IFRSVUV9CiMjIGNyZWF0ZSBhIGRhdGEgZnJhbWUgYW5kIHNwbGl0IHRoZSB0ZXh0IGludG8gY29sdW1ucwpwZG9fZGYgPC0gdGliYmxlKHRleHQgPSBwZG9fY2xlYW4pIHw+CiAgc2VwYXJhdGVfd2lkZXJfZGVsaW0oCiAgICBjb2xzID0gdGV4dCwgCiAgICBkZWxpbSA9ICIgIiwKICAgIG5hbWVzID0gYygieWVhciIsIG1vbnRoLmFiYikKICApIHw+CiAgIyMgdGlkeSB0aGUgdGliYmxlIGJ5IHBpdm90aW5nIHRoZSBtb250aCBjb2x1bW5zIGludG8gYSBzaW5nbGUgY29sdW1uCiAgcGl2b3RfbG9uZ2VyKAogICAgY29scyA9IG1vbnRoLmFiYiwKICAgIG5hbWVzX3RvID0gIm1vbnRoIiwKICAgIHZhbHVlc190byA9ICJwZG8iCiAgKQoKIyMgaW5zcGVjdCB0aWJibGUKaGVhZChwZG9fZGYpCnRhaWwocGRvX2RmKQpgYGAKCjo6OiBub3RlCgpJIHN0aWxsIHNlZSB0d28gdGhpbmdzIGhlcmUgdGhhdCBhcmUgKHBvdGVudGlhbGx5KSBwcm9ibGVtYXRpYzoKCjEuIFRoZSBQRE8gdmFsdWVzIGFyZSBjaGFyYWN0ZXIgc3RyaW5ncywgc28gd2UgbmVlZCB0byBjb252ZXJ0IHRoZW0gdG8gbnVtZXJpYwoKMi4gVGhpcyBmaWxlIHVzZXMgYSBub24tc3RhbmRhcmQgbWlzc2luZyB2YWx1ZSBjb2RlICg5OS45OSksIHNvIHdlIG5lZWQgdG8gY29udmVydCB0aG9zZSB0byBOQQoKOjo6Cgo6OjogdGFzawoKTGV0J3MgZml4IHRoZXNlIGxhc3QgdHdvIHByb2JsZW1zLgoKOjo6CgpgYGB7ciBjbGVhbl9wZG9fZGZ9CnBkb19kZiA8LSBwZG9fZGYgfD4KICAjIDEuIENvbnZlcnQgUERPIHZhbHVlcyB0byBudW1lcmljCiAgbXV0YXRlKHBkbyA9IGFzLm51bWVyaWMocGRvKSkgfD4KICAjIDIuIENvbnZlcnQgbWlzc2luZyB2YWx1ZSBjb2RlICg5OS45OSkgdG8gTkEKICBtdXRhdGUocGRvID0gaWZlbHNlKHBkbyA9PSA5OS45OSwgTkEsIHBkbykpCgp0YWlsKHBkb19kZikKYGBgCgo6Ojogc3VjY2VzcwoKTm93IHdlIGhhdmUgYSB0aWR5IGRhdGEgZnJhbWUgb2YgUERPIHZhbHVlcyB0aGF0IHdlIGNhbiB1c2UgZm9yIGFuYWx5c2lzLiAKCjo6OgoKPGJyPgoKIyMgUmVhZGluZyBgLmNzdmAgZmlsZXMKCiMjIyBCYXNlIGZ1bmN0aW9ucwoKVGhlIGByZWFkLnRhYmxlKClgIGZ1bmN0aW9uLCBhbmQgaXRzIHNwZWNpZmljIHZhcmlhbnRzIGByZWFkLmNzdigpYCBhbmQgYHJlYWQuZGVsaW0oKWAsIGlzIG9uZSBvZiB0aGUgbW9zdCBjb21tb25seSB1c2VkIGZ1bmN0aW9ucyBmb3IgcmVhZGluZyBkYXRhIGludG8gKipSKiouIFRoZSBoZWxwIGZpbGUgZm9yIGByZWFkLnRhYmxlKClgIHByb3ZpZGVzIGEgbG90IG9mIHVzZWZ1bCBpbmZvcm1hdGlvbiAoc2VlIGA/cmVhZC50YWJsZWApLgoKVGhlIHJlYWQudGFibGUoKSBmdW5jdGlvbiBoYXMgc2V2ZXJhbCBpbXBvcnRhbnQgYXJndW1lbnRzOgoKKiBgZmlsZWA6IHRoZSBuYW1lIG9mIGEgZmlsZSwgb3IgYSBjb25uZWN0aW9uICAKKiBgaGVhZGVyYDogYSBsb2dpY2FsIHZhbHVlIGluZGljYXRpbmcgaWYgdGhlIGZpbGUgaGFzIGEgaGVhZGVyIGxpbmUgIAoqIGBzZXBgOiBhIHN0cmluZyBpbmRpY2F0aW5nIGhvdyB0aGUgY29sdW1ucyBhcmUgc2VwYXJhdGVkICAKKiBgY29sQ2xhc3Nlc2A6IGEgY2hhcmFjdGVyIHZlY3RvciBpbmRpY2F0aW5nIHRoZSBjbGFzcyBvZiBlYWNoIGNvbHVtbiBpbiB0aGUgZGF0YXNldCAgCiogYG5yb3dzYDogdGhlIG51bWJlciBvZiByb3dzIGluIHRoZSBkYXRhc2V0IChieSBkZWZhdWx0IGByZWFkLnRhYmxlKClgIHJlYWRzIGFuIGVudGlyZSBmaWxlKQoqIGBjb21tZW50LmNoYXJgOiBhIGNoYXJhY3RlciBzdHJpbmcgaW5kaWNhdGluZyB0aGUgY29tbWVudCBjaGFyYWN0ZXIsIHdoaWNoIGRlZmF1bHRzIHRvIGAjYC4gSWYgdGhlcmUgYXJlIG5vIGNvbW1lbnRlZCBsaW5lcyBpbiB5b3VyIGZpbGUsIGl04oCZcyB3b3J0aCBzZXR0aW5nIHRoaXMgdG8gYmUgdGhlIGVtcHR5IHN0cmluZyBgIiJgLiAgCiogYHNraXBgOiB0aGUgbnVtYmVyIG9mIGxpbmVzIHRvIHNraXAgZnJvbSB0aGUgYmVnaW5uaW5nICAKKiBgc3RyaW5nc0FzRmFjdG9yc2A6IHNob3VsZCBjaGFyYWN0ZXIgdmFyaWFibGVzIGJlIGNvZGVkIGFzIGZhY3RvcnM/IE5vdGUgdGhhdCBhcyBvZiAqKlIqKiB2ZXJzaW9uIDQuMCwgdGhpcyBkZWZhdWx0cyB0byBgRkFMU0VgCgo6OjogdGlwCgpGb3Igc21hbGwgdG8gbW9kZXJhdGVseSBzaXplZCBmaWxlcywgeW91IGNhbiB1c3VhbGx5IGNhbGwgYHJlYWQudGFibGUoKWAgd2l0aG91dCBzcGVjaWZ5aW5nIGFueSBvdGhlciBhcmd1bWVudHMuIAoKOjo6CgpGb3IgZXhhbXBsZSwgdGhlIGZvbGxvd2luZyBsaW5lCgpgYGB7ciBleF9yZWFkdGFibGUsIGV2YWwgPSBGQUxTRX0KcmF3X2RhdGEgPC0gcmVhZC50YWJsZSgiZmxhdF9maWxlLnR4dCIpCmBgYAoKd291bGQKCiogc2tpcCBhbnkgbGluZXMgdGhhdCBiZWdpbiB3aXRoIGEgYCNgICAKKiBmaWd1cmUgb3V0IGhvdyBtYW55IHJvd3MgdGhlcmUgYXJlIChhbmQgaG93IG11Y2ggbWVtb3J5IG5lZWRzIHRvIGJlIGFsbG9jYXRlZCkgIAoqIGZpZ3VyZSB3aGF0IHR5cGUgb2YgdmFyaWFibGUgaXMgaW4gZWFjaCBjb2x1bW4gb2YgdGhlIHRhYmxlICAKCk9uIHRoZSBvdGhlciBoYW5kLCBleHBsaWNpdGx5IHNwZWNpZnlpbmcgYWxsIG9mIHRoZXNlIGFyZ3VtZW50cyBkaXJlY3RseSAoaS5lLiwgImRlZmVuc2l2ZSBwcm9ncmFtbWluZyIpIG1ha2VzICoqUioqIHJ1biBmYXN0ZXIgYW5kIG1vcmUgZWZmaWNpZW50bHkuCgo6OjogdGlwCgpUaGUgYHJlYWQuY3N2KClgIGZ1bmN0aW9uIGlzIGlkZW50aWNhbCB0byBgcmVhZC50YWJsZSgpYCBleGNlcHQgdGhhdCBzb21lIG9mIHRoZSBkZWZhdWx0cyBhcmUgc2V0IGRpZmZlcmVudGx5IChlLmcuLCBgc2VwYCBhcmd1bWVudCkuCgo6OjoKClRoZXJlIGlzIGEgYHBhbG1lcl9wZW5ndWlucy5jc3ZgIGZpbGUgbG9jYXRlZCBkZWVwIGluIG91ciB3b3JraW5nIGRpcmVjdG9yeSBzdHJ1Y3R1cmUuIAoKYGBge3IgaGVyZV9leF9yZWFkX2Jhc2V9CiMjIHJlYWQgcGVuZ3VpbiBkYXRhCmRhdGFfcmF3IDwtIHJlYWQuY3N2KGhlcmUoImxlY3R1cmVzIiwgIndlZWtfMDQiLCAiZGF0YSIsICJwYWxtZXJfcGVuZ3VpbnMuY3N2IikpCgojIyBpbnNwZWN0IGl0cyBjb250ZW50CmhlYWQoZGF0YV9yYXcpCmBgYAoKCiMjIyBUaWR5dmVyc2UKClRoZSBuZXdlciAqKlRpZHl2ZXJzZSoqIGNvbGxlY3Rpb24gb2YgcGFja2FnZXMgaW5jbHVkZXMgdGhlIFtge3JlYWRyfWBdKGh0dHBzOi8vcmVhZHIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UgZm9yIHJlYWRpbmcgaW4gZGF0YSBmaWxlcyBhbmQgYXNzaWduaW5nIHRoZW0gdGhlIGB0aWJibGVgIG9iamVjdCBjbGFzcy4gQ2xpY2sgW2hlcmVdKHJlYWRyX2NoZWF0c2hlZXQucGRmKSB0byBzZWUgdGhlIGNoZWF0IHNoZWV0LgoKOjo6IHRhc2sKClJlYWQgdGhlIFBhbG1lciBwZW5ndWlucyBkYXRhIGludG8gKipSKiogd2l0aCB0aGUgYHJlYWRyYCBwYWNrYWdlIGFuZCB0aGVuIGluc3BlY3QgaXRzIGNvbnRlbnRzLgoKOjo6CgpgYGB7ciBoZXJlX2V4X3JlYWRyfQojIyByZWFkIHBlbmd1aW4gZGF0YQpkYXRhX3JhdyA8LSByZWFkX2NzdihoZXJlKCJsZWN0dXJlcyIsICJ3ZWVrXzA0IiwgImRhdGEiLCAicGFsbWVyX3Blbmd1aW5zLmNzdiIpKQoKIyMgaW5zcGVjdCBpdHMgY29udGVudApoZWFkKGRhdGFfcmF3KQpgYGAKCjxicj4KCiMjIFJlYWRpbmcgRXhjZWwgZmlsZXMKCkV4Y2VsIGlzIHZlcnkgcG9wdWxhciBmb3IgZW50ZXJpbmcgYW5kIHN0b3JpbmcgZGF0YSwgYWx0aG91Z2ggd2UndmUgYWxyZWFkeSBzZWVuIHNvbWUgb2YgdGhlIHBpdGZhbGxzIHRoYXQgeW91IG1pZ2h0IGVuY291bnRlci4gVGhlcmUgaXMgYSBncmVhdCBwYWNrYWdlIGNhbGxlZCBbYHtyZWFkeGx9YF0oaHR0cHM6Ly9yZWFkeGwudGlkeXZlcnNlLm9yZy8pIHRoYXQgYWxsb3dzIHlvdSB0byByZWFkIEV4Y2VsIGZpbGVzIGRpcmVjdGx5IGludG8gKipSKiouIApIZXJlJ3MgYW4gZXhhbXBsZSB0aGF0IHVzZXMgYW4gRXhjZWwgZmlsZSBjYWxsZWQgYHdvcmxkX2JhbmtfY2xpbWF0ZV9jaGFuZ2UueGxzYCBmcm9tIHRoZSBXb3JsZCBCYW5rJ3MgW3dlYnNpdGVdKGh0dHBzOi8vZGF0YWNhdGFsb2cud29ybGRiYW5rLm9yZy9zZWFyY2gvZGF0YXNldC8wMDQwMjA1L2NsaW1hdGUtY2hhbmdlLWRhdGEpLiBJdCBsb29rcyBsaWtlIHRoaXM6CgohW10oaW1hZ2VzL3diX2V4Y2VsX3ZpZXcucG5nKQoKPGJyPgoKOjo6IG5vdGUKCllvdSBjYW4gW2Rvd25sb2FkIHRoZSBFeGNlbCBmaWxlXShodHRwczovL2Zpc2g1NDkuZ2l0aHViLmlvL3dlYnNpdGUvbGVjdHVyZXMvd2Vla18wNC9kYXRhL3dvcmxkX2JhbmtfY2xpbWF0ZV9jaGFuZ2UueGxzKSBhbmQgc2F2ZSBpdCBpbiB5b3VyIHdvcmtpbmcgZGlyZWN0b3J5LiBNYXJrJ3MgaXMgc3RvcmVkIGluIGEgZm9sZGVyIGNhbGxlZCBgZGF0YWAuCgo6OjoKCjo6OiB0aXAKCldlIGNhbiBnZXQgYSBsaXN0IG9mIHRoZSBpbmRpdmlkdWFsIHdvcmtzaGVldHMgd2l0aGluIHRoZSB3b3JrYm9vayB3aXRoIGBleGNlbF9zaGVldHMoKWAuCgo6OjoKCmBgYHtyIGV4YW1pbmVfd2JfZmlsZSwgd2FybmluZyA9IEZBTFNFfQojIyBsb2FkIHJlYWR4bApsaWJyYXJ5KHJlYWR4bCkKCiMjIGluc3BlY3QgdGhlIHNoZWV0IG5hbWVzCmV4Y2VsX3NoZWV0cygiZGF0YS93b3JsZF9iYW5rX2NsaW1hdGVfY2hhbmdlLnhscyIpCmBgYAoKOjo6IHRpcAoKV2UgY2FuIHJlYWQgaW4gYSBzcGVjaWZpYyB3b3Jrc2hlZXQgd2l0aCBgcmVhZF9leGNlbCgpYC4KCjo6OgoKYGBge3IgcmVhZF93Yl9zaGVldCwgd2FybmluZyA9IEZBTFNFfQojIyByZWFkIGluIHRoZSAiRGF0YSIgd29ya3NoZWV0CmNjX2RhdGEgPC0gcmVhZF9leGNlbCgiZGF0YS93b3JsZF9iYW5rX2NsaW1hdGVfY2hhbmdlLnhscyIsIHNoZWV0ID0gIkRhdGEiKQoKIyMgaW5zcGVjdCBpdApoZWFkKGNjX2RhdGEpCmBgYAoKOjo6IHRpcAoKSWYgeW91IGFyZSBvbmx5IGludGVyZXN0ZWQgaW4gYSBjZXJ0YWluIHN1YnNldCBvZiB0aGUgZGF0YSwgdGhlIGByYW5nZWAgYXJndW1lbnQgY2FuIGJlIHVzZWQgdG8gc2VsZWN0IHdoaWNoIGNlbGxzIHRvIHJlYWQuCgo6OjoKCkZvciBleGFtcGxlLCB0byByZWFkIGluIHRoZSBjb2x1bW5zIHJlbGF0ZWQgdG8gY2VyZWFsIHlpZWxkIGZvciB0aGUgeWVhcnMgMTk5MCB0aHJvdWdoIDE5OTUgZm9yIGNvdW50cnkgbmFtZXMgdGhhdCBzdGFydCB3aXRoICJCIiBpbiB0aGUgRGF0YSB0YWIsIHdlIHdvdWxkIG9ubHkgd2FudCB0byByZWFkIGluIHRoZSByZWN0YW5nbGUgb2YgY2VsbHMgZnJvbSBHNDgxIHRvIEw0OTguCgo6Ojogbm90ZQoKVGhlIGF1dGhvcnMgb2YgdGhpcyBmaWxlIGRlY2lkZWQgdG8gdXNlIGAuLmAgdG8gcmVwcmVzZW50IG1pc3NpbmcgdmFsdWVzLCByYXRoZXIgdGhhbiBzb21ldGhpbmcgbW9yZSBjb252ZW50aW9uYWwgbGlrZSBgTkFgIG9yIGBOL0FgLCBzbyBoZW5jZSB0aGUgYXJndW1lbnQgYG5hID0gIi4uImAuCgo6OjoKCmBgYHtyfQpjY19kYXRhIDwtIHJlYWRfZXhjZWwoImRhdGEvd29ybGRfYmFua19jbGltYXRlX2NoYW5nZS54bHMiLAogICAgICAgICAgICAgICAgICAgICAgc2hlZXQgICAgID0gIkRhdGEiLAogICAgICAgICAgICAgICAgICAgICAgcmFuZ2UgICAgID0gIkc0ODE6TDQ5OCIsCiAgICAgICAgICAgICAgICAgICAgICBjb2xfbmFtZXMgPSBhcy5jaGFyYWN0ZXIoYygxOTkwOjE5OTUpKSwKICAgICAgICAgICAgICAgICAgICAgIG5hICAgICAgICA9ICIuLiIpCmhlYWQoY2NfZGF0YSkKYGBgCgojIyMgV29ya2Zsb3cgZXhhbXBsZQoKOjo6IHRpcAoKSWYgeW91J2QgbGlrZSB0byBzZWUgYW4gZXhhbXBsZSBvZiBob3cgb25lIG1pZ2h0IHVzZSB0aGUgYHtyZWFkeGx9YCBwYWNrYWdlIGluIGEgd29ya2Zsb3csIGNoZWNrIG91dCBNYXJrJ3MgcHJvamVjdCBvbiBlc3RpbWF0aW5nIHRyZW5kcyBpbiB0aGUgZGVuc2l0eSBvZiBidWxsIHRyb3V0IGFjcm9zcyB0aGUgUGFjaWZpYyBOb3J0aHdlc3QgW2hlcmVdKGh0dHBzOi8vZ2l0aHViLmNvbS9tZHNjaGV1ZXJlbGwvYnVsbHRyb3V0L2Jsb2IvbWFpbi9SLzAxX2RhdGEtd3JhbmdsaW5nLlIpLgoKOjo6Cgo8YnI+CgojIyBSZWFkaW5nIGEgR29vZ2xlIFNoZWV0CgpUaGUgYHtnb29nbGVzaGVldHM0fWAgcGFja2FnZSBhbGxvd3MgeW91IHRvIGluc3BlY3QgYW5kIHJlYWQgR29vZ2xlIFNoZWV0cy4gCgo6OjogdGlwCgpUaGUgYGdzX2xzKClgIGZ1bmN0aW9uIHJldHVybnMgYSBkYXRhIGZyYW1lIG9mIHRoZSBzaGVldHMgbGlzdGVkIGluIHlvdXIgR29vZ2xlIFNoZWV0cyBob21lIHNjcmVlbiBsb2NhdGVkIGF0IGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy8uIFRoaXMgd2lsbCBpbmNsdWRlIHNoZWV0cyB0aGF0IHlvdSBvd24gYW5kIHNoZWV0cyBvd25lZCBieSBvdGhlcnMgdGhhdCB5b3UgaGF2ZSBwZXJtaXNzaW9uIHRvIGFjY2Vzcy4KCjo6OgoKOjo6IHRhc2sKClRvIGJlZ2luLCB5b3UgbmVlZCB0byB1c2UgYGdzNF9kZWF1dGgoKWAgdG8gYXV0aG9yaXplIGBnb29nbGVzaGVldHM0YCB0byBhY2Nlc3MgdGhlIGNvbnRlbnRzIG9mIHlvdXIgR29vZ2xlIERyaXZlIHdoZXJlIHRoZSBTaGVldHMgYXJlIHN0b3JlZCAoaGVyZSBJJ20gYWxsb3dpbmcgb25seSBhY2Nlc3MgdG8gcHVibGljIHNoZWV0cykuCgo6OjoKCmBgYHtyIGdvb2dsZXNoZWV0X3NldHVwfQojIyBsb2FkIHRoZSBwYWNrZ2UKbGlicmFyeShnb29nbGVzaGVldHM0KQoKIyMgYXV0aG9yaXplIGZvciByZWFkaW5nIHB1YmxpYy1vbmx5IHNoZWV0cwpnczRfZGVhdXRoKCkKYGBgCgo6OjogdGlwCgpXZSBjYW4gcmVhZCBpbiBhIHNoZWV0IGJ5IG9uZSBvZiB0d28gbWV0aG9kczoKCjEpIHNwZWNpZnkgdGhlIGVudGlyZSBVUkwKCjIpIHNwZWNpZnkgb25seSB0aGUgc2hlZXQgSUQKCjo6OgoKIyMjIE9wdGlvbiAxOiBTcGVjaWZ5IHRoZSBlbnRpcmUgVVJMCgpgYGB7ciBnb29nbGVzaGVldF9yZWFkXzF9CiMjIHNwZWNpZnkgVVJMCmdzX3VybCA8LSAiaHR0cHM6Ly9kb2NzLmdvb2dsZS5jb20vc3ByZWFkc2hlZXRzL2QvMWN0M01NTXpFWDgyQmVxSmNVdzN1WUQ1YjRDSnJLVW1SWUNLLXdWQTN5aWcvZWRpdCNnaWQ9MCIKCiMjIHJlYWQgZnJvbSBVUkwKcmVhZF9zaGVldChnc191cmwpCgojIyBhc3NpZ24gaXQgdG8gYW4gb2JqZWN0CmdzX3JhdyA8LSByZWFkX3NoZWV0KGdzX3VybCkKYGBgCgojIyMgT3B0aW9uIDI6IFNwZWNpZnkgdGhlIHNoZWV0IElECgpgYGB7ciBnb29nbGVzaGVldF9yZWFkXzJ9CiMjIHNwZWNpZnkgSUQKZ3NfaWQgPC0gYXNfc2hlZXRzX2lkKGdzX3VybCkKCiMjIHJlYWQgZnJvbSBJRApyZWFkX3NoZWV0KGdzX2lkKQoKIyMgYXNzaWduIGl0IHRvIGFuIG9iamVjdApnc19yYXcgPC0gcmVhZF9zaGVldChnc19pZCkKYGBgCgo6OjogdGlwCgpXZSBjYW4gYnJvd3NlIHRoZSBzaGVldCdzIG1ldGFkYXRhIHdpdGggYGdzNF9icm93c2UoKWAuCgo6OjoKCmBgYHtyIGdzX2Jyb3dzZX0KIyMgYnJvd3NlIHNoZWV0IGNvbnRlbnRzCmdzNF9nZXQoZ3NfaWQpCmBgYAoKOjo6IHRpcAoKV2UgY2FuIGFsc28gcmVhZCBpbiBqdXN0IGEgc3Vic2V0IG9mIGNlbGxzIGJ5IHNwZWNpZnlpbmcgdGhlIGByYW5nZWAgYXJndW1lbnQuCgo6OjoKCmBgYHtyfQpyZWFkX3NoZWV0KGdzX2lkLCByYW5nZSA9ICJBMTpCMTAiKQpgYGAKCjxicj4KCiMjIFJlYWRpbmcgYSBKU09OIGZpbGUKCiMjIyBXaGF0IGlzIEpTT04/IAoKSlNPTiAob3IgSmF2YVNjcmlwdCBPYmplY3QgTm90YXRpb24pIGlzIGEgZmlsZSBmb3JtYXQgdGhhdCBzdG9yZXMgaW5mb3JtYXRpb24gaW4gYW4gb3JnYW5pemVkLCBsb2dpY2FsLCBlYXN5LXRvLWFjY2VzcyBtYW5uZXIgKGkuZS4sICJodW1hbiByZWFkYWJsZSIpLiBGb3IgZXhhbXBsZSwgaGVyZSBpcyB3aGF0IGEgSlNPTiBmaWxlIGxvb2tzIApsaWtlOiAKCmBgYHtqYXZhc2NyaXB0LCBldmFsPUZBTFNFfQp2YXIgbWFyayA9IHsKICAiY2l0eSIgOiAiU2VhdHRsZSIsCiAgInN0YXRlIiA6ICJXQSIsIAogICJob2JiaWVzIiA6IHsKICAgICJob2JieTEiIDogImN5Y2xpbmciLAogICAgImhvYmJ5MiIgOiAic2tpaW5nIgogIH0KICAiYmlrZXMiIDogewogICAgImJpa2UxIiA6ICJSaWRsZXkgSGVsaXVtIiwKICAgICJiaWtlMiIgOiAiTmluZXIgUkxUIiwKICAgICJiaWtlMyIgOiAiU2FudGEgQ3J1eiBUYWxsYm95IgogIH0KICAic2tpcyIgOiB7CiAgICAic2tpczEiIDogIksyIEhhcmRzaWRlIiwKICAgICJza2lzMiIgOiAiQmxhY2sgQ3Jvd3MgQ29ydnVzIEZyZWViaXJkIgogIH0KfQpgYGAKClNvbWUgZmVhdHVyZXMgYWJvdXQgYEpTT05gIG9iamVjdDogCgoqIEpTT04gb2JqZWN0cyBhcmUgc3Vycm91bmRlZCBieSBjdXJseSBicmFjZXMgYHt9YCAgCiogSlNPTiBvYmplY3RzIGFyZSB3cml0dGVuIGluIGBrZXkgOiB2YWx1ZWAgcGFpcnMgKHNlcGFyYXRlZCBieSBhIGNvbG9uKSAgCiogS2V5cyBtdXN0IGJlIHN0cmluZ3MgIAoqIFZhbHVlcyBtdXN0IGJlIGEgdmFsaWQgSlNPTiBkYXRhIHR5cGUgKHN0cmluZywgbnVtYmVyLCBvYmplY3QsIGFycmF5LCBib29sZWFuKSAgIAoqIEVhY2ggYGtleSA6IHZhbHVlYCBwYWlyIGlzIHNlcGFyYXRlZCBieSBhIGNvbW1hICAKCiMjIyBVc2luZyB0aGUgR2l0SHViIEFQSQoKTWFueSBvcmdhbml6YXRpb25zIHByb3ZpZGUgYW4gYXBwbGljYXRpb24gcHJvZ3JhbW1pbmcgaW50ZXJmYWNlIChBUEkpIGZvciBhY2Nlc3NpbmcgdGhlaXIgZGF0YS4gKipHaXRIdWIqKiBoYXMgYW4gW0FQSV0oaHR0cHM6Ly9kZXZlbG9wZXIuZ2l0aHViLmNvbS92My8/KSB0aGF0IGFsbG93cyB5b3UgdG8gZXhwbG9yZSBpbmZvcm1hdGlvbiBhYm91dCB1c2Vycy4gTGV0J3MgdXNlIHRoaXMgQVBJIHRvIGV4cGxvcmUgc29tZSBvZiBNYXJrJ3MgR2l0SHViIGFjdGl2aXRpZXMuCgpUbyBkbyBzbywgd2UnbGwgdXNlIHRoZSBgZnJvbUpTT04oKWAgZnVuY3Rpb24gZnJvbSB0aGUKW2B7anNvbmxpdGV9YF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2pzb25saXRlL2luZGV4Lmh0bWwpIHBhY2thZ2UgdG8gY29udmVydCB0aGUgZGF0YSBmcm9tIGEgSlNPTiBvYmplY3QgdG8gYSBkYXRhIGZyYW1lLiAKCjo6OiB0YXNrCgpCZWdpbiBieSByZWFkaW5nIGluIHRoZSBKU09OIGZpbGUgbG9jYXRlZCBhdCAKW2h0dHBzOi8vYXBpLmdpdGh1Yi5jb20vdXNlcnMvbWRzY2hldWVyZWxsL3JlcG9zXShodHRwczovL2FwaS5naXRodWIuY29tL3VzZXJzL21kc2NoZXVlcmVsbC9yZXBvcykuCgo6OjoKCmBgYHtyIGpzb25fZ2l0aHViX2V4fQojIyBsb2FkIHRoZSBwY2thZ2UKbGlicmFyeShqc29ubGl0ZSkKCiMjIHNldCB0aGUgVVJMCmdpdGh1Yl91cmwgPSAiaHR0cHM6Ly9hcGkuZ2l0aHViLmNvbS91c2Vycy9tZHNjaGV1ZXJlbGwvcmVwb3MiCgojIyBnZXQgdGhlIEpTT04gZGF0YQpkYXRhX2pzb24gPC0gZnJvbUpTT04oZ2l0aHViX3VybCkKYGBgCgo6Ojogbm90ZQoKVGhlIGZ1bmN0aW9uIGBmcm9tSlNPTigpYCB3aWxsIGNvbnZlcnQgdGhlIEpTT04gZmlsZSBpbnRvIGEgZGF0YSBmcmFtZS4gCgo6OjoKCjo6OiB0aXAKCllvdSBjYW4gaW5zcGVjdCB0aGUgZmllbGQgbmFtZXMgaW4gdGhlIGRhdGEgZnJhbWUgd2l0aCBgbmFtZXMoKWAuCgo6OjoKCmBgYHtyIGpzb25fbmFtZXN9CiMjIGFsbCBvZiB0aGUgSlNPTiBuYW1lcwpuYW1lcyhkYXRhX2pzb24pCmBgYAoKTm93IHdlIGNhbiBxdWVyeSB0aGVzZSBkYXRhIHRvIGFuc3dlciBzb21lIHF1ZXN0aW9ucyBhYm91dCBNYXJrJ3MgKipHaXRIdWIqKiBwcm9maWxlIGFuZCBhY3Rpdml0aWVzLiAoTm90ZSB0aGF0IHRoZSBmb2xsb3dpbmcgcmVzdWx0cyBhcmUgb25seSBiYXNlZCB1cG9uIHRoZSBmaXJzdCAzMCByZXBvcyBsaXN0ZWQgaW4gTWFyaydzIGFjY291bnQuKQoKOjo6IHRhc2sKCkhvdyBtYW55IG9mIE1hcmsncyByZXBvcyBoYXZlIGJlZW4gZm9ya2VkPyAKCjo6OgoKYGBge3IgdGJsX2doX2NvdW50X2ZvcmtzfQojIyB0YWJsZSBvZiB0aGUgbnVtYmVyIG9mIGZvcmtzCnRhYmxlKGRhdGFfanNvbiRmb3JrcykKYGBgCgpPbmx5IGEgdG90YWwgb2YgYHIgc3VtKHRhYmxlKGRhdGFfanNvbiRmb3JrcylbLTFdKWAgb2YgdGhlIGZpcnQgMzAgcmVwb3MgaGF2ZSBiZWVuIGZvcmtlZC4KCjo6OiB0YXNrCgpXaGF0IGFyZSB0aGUgbGFuZ3VhZ2VzIHVzZWQgaW4gdGhlc2UgcmVwb3M/IAoKOjo6CgpgYGB7ciB0YmxfZ2hfbGFuZ3VhZ2VzfQojIyB0YWJsZSBudW1iZXIgb2YgdGhlIGRpZmZlcmVudCBsYW5ndWFnZXMKdGFibGUoZGF0YV9qc29uJGxhbmd1YWdlKQpgYGAKCjo6OiB0YXNrCgpIb3cgbWFueSByZXBvcyBoYXZlIG9wZW4gaXNzdWVzIHRoYXQgTWFyayBzaG91bGQgYWRkcmVzcz8KCjo6OgoKYGBge3IgZ2hfb3Blbl9pc3N1ZXN9CiMjIGhvdyBtYW55IHJlcG9zIGhhdmUgb3BlbiBpc3N1ZXM/IAp0YWJsZShkYXRhX2pzb24kb3Blbl9pc3N1ZXNfY291bnQpCmBgYAoKTm90IHRvbyBiYWQsIGJ1dCBjbGVhcmx5IGhlJ3MgYmVoaW5kIG9uIHNvbWUgdGhpbmdzLiBPbmUgb2YgaGlzIHJlcG9zIGhhcyBgciBtYXgoYXMuaW50ZWdlcihuYW1lcyh0YWJsZShkYXRhX2pzb24kb3Blbl9pc3N1ZXNfY291bnQpKSkpYCBvcGVuIGlzc3VlcyEKCioqKgoKIyBPdGhlciBnb29kIFIgcGFja2FnZXMKCkhlcmUgYXJlIHNvbWUgb3RoZXIgUiBwYWNrYWdlcyB0aGF0IGFyZSBoZWxwZnVsIGZvciB3b3JraW5nIHdpdGggcmVtb3RlIGRhdGEgc291cmNlczogCgoqIFtge2h0dHJ9YF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2h0dHIvaW5kZXguaHRtbCkgZm9yIHRvb2xzIHRvIHdvcmsgd2l0aCBVUkxzIGFuZCBIVFRQCgoqIFtge2dvb2dsZWRyaXZlfWBdKGh0dHA6Ly9nb29nbGVkcml2ZS50aWR5dmVyc2Uub3JnLykgdG8gaW50ZXJhY3Qgd2l0aCB5b3VyIEdvb2dsZSBEcml2ZQo=