Prologue: R Markdown

What is R Markdown?

Traditional .R scripts (analogous to a .do file in Stata) are a standard way to write pure R code. You could create a new R script yourself now by clicking the “New File” icon at the top-left of your RStudio session. Seriously, go ahead and try quickly.

What you see in front of you, however, is not a plain R script. It is an R Markdown document (file extension: .Rmd). This is simply a document type that allows you to combine both text — like the sentence you are reading now — and actual R code. It is similar to Stata’s dyndoc and is a very convenient way to integrate text and code in a single document. Think of it like LaTeX and R had a baby, but was very easy to use (was a very good baby?). In fact, the same Rmd file can be “knitted” to multiple formats: html, pdf, rich text, etc.

We don’t want to get too sidetracked by the special features of R Markdown… and should emphasise that it is not the only (or even “standard”) way to do analysis in R. But it is an extremely popular feature of R and is also a great way to teach a session like this. If you are interested in learning more, then the official website is a great place to start.1 Note further that this document is about as vanilla as it gets, but you can get extremely fancy.

Send commands to R in R Markdown

Behind the scenes, in the .Rmd document we are typing in “code-chunks” that get run by R. These code chunks are fenced in with the backticks.

For example, this is a code chunk that will evaluate the command sin(3)

```{r}
sin(3)
```

The output of this will appear as follows:

## [1] 0.14112

You can also create calculations inline for example `r sin(3)` will evaluate sin(3). With output rendering like this 0.14112.

Okay, with that bit of R Markdown prologue out of the way, let’s get down to running actual R code.

Load packages into memory

An awful lot of work in R gets done through third-party packages that must be installed separately to “base” R. This is similar to how third-party packages in Stata can be installed from ssc. We recommend using RStudio’s package installer to find and install (or update) any external packages. We’ll show you how do to this in the live session, or you can take a look here. But note that you can also install R packages directly from the R console, e.g.

install.packages(c('ggplot2', 'dplyr'))

A key difference between R and Stata, however, is that installed R packages must be loaded into memory if you want to use them in that session. Think of it like an app on your phone. You might have downloaded the app already, but you need to open it every time you use it. Later on we will cover how to manage packages efficiently. But right now, assuming that you have already installed them, let’s load and play around with some R packages.

Here we are going to load the ggplot2 package, which is an excellent graphics package. gg here stands for “grammar of graphics”. This is a part of the tidyverse suite of packages that you may have heard of. We will also load the dplyr package, which is a popular data wrangling package (and is also a part of the tidyverse).

# library(tidyverse) ## Shortcut to load both ggplot2 & dplyr (& several other packages)
library(ggplot2)
library(dplyr)
## 
## Attaching package: 'dplyr'
## The following objects are masked from 'package:stats':
## 
##     filter, lag
## The following objects are masked from 'package:base':
## 
##     intersect, setdiff, setequal, union

Aside: Comments in .R files (or code chunks) are denoted with the # character. A shortcut to comment out a line (or region) in RStudio is Ctrl + Shift + c. This shortcut generally just works correctly for whatever language or script that you open in RStudio.2

View data

Now that these packages are loaded into memory, we can begin to use them. In the code below, we’ll explore the diamonds dataset that comes bundled with ggplot2. Much like Stata’s auto dataset, many packages bundle pre-installed datasets that are useful for tutorials and debugging.

The diamonds dataset is already available to us, since we’ve loaded ggplot2 into memory. But let’s bring it visibly into our global Environment (top right-hand pane in RStudio). This would happen automatically if we read in an external file like a .csv or .dta. We’ll get to external file I/O later, though.

data("diamonds")

We can look at the first five observations using the head command

head(diamonds)
## # A tibble: 6 x 10
##   carat cut       color clarity depth table price     x     y     z
##   <dbl> <ord>     <ord> <ord>   <dbl> <dbl> <int> <dbl> <dbl> <dbl>
## 1  0.23 Ideal     E     SI2      61.5    55   326  3.95  3.98  2.43
## 2  0.21 Premium   E     SI1      59.8    61   326  3.89  3.84  2.31
## 3  0.23 Good      E     VS1      56.9    65   327  4.05  4.07  2.31
## 4  0.29 Premium   I     VS2      62.4    58   334  4.2   4.23  2.63
## 5  0.31 Good      J     SI2      63.3    58   335  4.34  4.35  2.75
## 6  0.24 Very Good J     VVS2     62.8    57   336  3.94  3.96  2.48

We can get a list of column names fairly easily too

names(diamonds)
##  [1] "carat"   "cut"     "color"   "clarity" "depth"   "table"   "price"  
##  [8] "x"       "y"       "z"

Tip: You can bring the dataset into view (similar to Stata’s browse feature) by typing View(diamonds), or just by clicking on it in your Environment pane.

Let’s compute the average price by color. The next code chunk is using dplyr commands (“verbs”) and invokes a “pipe” (the %>% syntax) to write cleaner code.

## summarise(group_by(diamonds, color), mean(price))
diamonds %>% group_by(color) %>% summarise(mean(price)) 
## # A tibble: 7 x 2
##   color `mean(price)`
##   <ord>         <dbl>
## 1 D             3170.
## 2 E             3077.
## 3 F             3725.
## 4 G             3999.
## 5 H             4487.
## 6 I             5092.
## 7 J             5324.

Here the pipe command %>% will take the output from the command on the left and “pipes” it as input to the command on the right. So above we

  1. take the dataset called diamonds
  2. send it as input to the command group_by, which groups by the unique values in the column color
  3. We then send this grouped data to the summarize command, which calculates the mean price in each group.

Two things to note. First off, you can just move onto a new line without an error. This is neat because it allows us to write cleaner code and you don’t need a delimiter or the /// you may be used to from stata. So we can rewrite the above like this.

diamonds %>% 
    group_by(color) %>% 
    summarize(mean(price)) 
## # A tibble: 7 x 2
##   color `mean(price)`
##   <ord>         <dbl>
## 1 D             3170.
## 2 E             3077.
## 3 F             3725.
## 4 G             3999.
## 5 H             4487.
## 6 I             5092.
## 7 J             5324.

Second, in more recent versions of R (4.1 and above), you don’t need to have dpylr installed to pipe. You can do the same thing using |>.

diamonds |>
    group_by(color) |>
    summarize(mean(price)) 
## # A tibble: 7 x 2
##   color `mean(price)`
##   <ord>         <dbl>
## 1 D             3170.
## 2 E             3077.
## 3 F             3725.
## 4 G             3999.
## 5 H             4487.
## 6 I             5092.
## 7 J             5324.

Note: In this example, %>% and |> are interchangeable, but the two are not interchangeable in all settings. See https://www.r-bloggers.com/2021/05/the-new-r-pipe/ for more details.

Make summary statistics table.

An annoying, but also powerful, feature of R is that there are 15 ways to accomplish any task. In general, limiting the number of packages you use is a good idea since you won’t build dependencies on code that could become outdated as base R and other packages are updated. That being said, the power of R is the ability to use fantastic user written packages. Here we will use the datasummary*() family of functions from the modelsummary package to make fantastic summary statistic plots. Otherwise we’d have to continue to do things like we did above, exploring the data and making summary tables on our own.

Aside: In Stata, many ssc modules consist of one primary function, which shares the same name (e.g. ivreg2, reghdfe). R packages tend to have many functions, so you shouldn’t expect these functions to share the exact same name as the package itself.

First, load the model summary package

library(modelsummary)

Now we can use the datasummary_skim function.

datasummary_skim(diamonds)
Unique (#) Missing (%) Mean SD Min Median Max
carat 273 0 0.8 0.5 0.2 0.7 5.0
depth 184 0 61.7 1.4 43.0 61.8 79.0
table 127 0 57.5 2.2 43.0 57.0 95.0
price 11602 0 3932.8 3989.4 326.0 2401.0 18823.0
x 554 0 5.7 1.1 0.0 5.7 10.7
y 552 0 5.7 1.1 0.0 5.7 58.9
z 375 0 3.5 0.7 0.0 3.5 31.8

But not every variable is numeric. datasummary has this covered with the type = "categorical" sub-option.

datasummary_skim(diamonds, type = "categorical")
N %
cut Fair 1610 3.0
Good 4906 9.1
Very Good 12082 22.4
Premium 13791 25.6
Ideal 21551 40.0
color D 6775 12.6
E 9797 18.2
F 9542 17.7
G 11292 20.9
H 8304 15.4
I 5422 10.1
J 2808 5.2
clarity I1 741 1.4
SI2 9194 17.0
SI1 13065 24.2
VS2 12258 22.7
VS1 8171 15.1
VVS2 5066 9.4
VVS1 3655 6.8
IF 1790 3.3

Learn more about the datasummary* family of functions here: https://vincentarelbundock.github.io/modelsummary/articles/datasummary.html

Graphing

Next we will use the ggplot function. You can learn more about any R function by typing in ? before the function name (e.g., ?ggplot).

  • On the first line, we call the function, then clarify the dataset we wish to use (referable to by its name, diamonds), the y-variable (price), and the x-variable (carat).
  • On the second line, we clarify that we want the geometry to be points
ggplot(data = diamonds, aes(y = price, x = carat)) +
    geom_point() 

Think changing graphic features in ggplot as adding layers. You can do this by repeating all of the same code. Or by saving the above as an object then adding to that object.

For example let’s add a third line where use a default theme called classic that I like.

ggplot(data = diamonds, aes(y = price, x = carat)) +
    geom_point() +
    theme_classic()

We can obtain the same result, by saving the above as an object and adding to it.

base_plot = ggplot(data = diamonds, aes(y = price, x = carat)) +
    geom_point()

You can also use the <- arrow instead of the = to the name base_plot

base_plot +
    theme_classic()

All we need to do is add this theme line theme_classic() to the above.

Deviations from simple scatter plot

It’s very simple to make complex plots in R.

We can add some transparency

ggplot(data = diamonds, aes(y = price, x = carat)) +
    geom_point(alpha = .33) +
    theme_classic()

Customized labels and increased size of text

ggplot(data = diamonds, aes(y = price, x = carat)) +
    geom_point(alpha = .1) +
    theme_classic() + 
    theme(text = element_text(size = 18)) +
    labs(title = "Larger diamonds cost more", 
          subtitle = "Price, $",
          y = "", 
          x = "Carat")

Recall that the data contain information on diamond color. We can easily create small multiples of the scatter plot for each color.

ggplot(data = diamonds, aes(y = price, x = carat)) +
    geom_point(alpha = .1) +
    facet_wrap(~color) +
    theme_classic() + 
    theme(text = element_text(size = 14)) +
    labs(title = "Larger diamonds cost more by diamond color", 
          subtitle = "Price, $",
          y = "", 
          x = "Carat")

Similarly, we can add color (or shapes) based on diamond clarity

ggplot(data = diamonds, aes(y = price, x = carat, color = clarity)) +
    geom_point(alpha = .33) +
    facet_wrap(~color) +
    theme_classic() + 
    theme(text = element_text(size = 14)) +
    labs(title = "Larger diamonds cost more by diamond color", 
          subtitle = "Price, $",
          y = "", 
          x = "Carat")

It’s also trivial to add a regression line to each. We will pick method lm, which is a simple linear regression.

ggplot(data = diamonds, aes(y = price, x = carat, color = clarity)) +
    geom_point(alpha = .33) +
    facet_wrap(~color) + 
    geom_smooth(method = "lm") +
    theme_classic() + 
    theme(text = element_text(size = 14)) +
    labs(title = "Larger diamonds cost more by diamond color", 
          subtitle = "Price, $",
          y = "", 
          x = "Carat")
## `geom_smooth()` using formula 'y ~ x'

We can even remove the points below by simply removing the second line

ggplot(data = diamonds, aes(y = price, x = carat, color = clarity)) +
    facet_wrap(~color) + 
    geom_smooth(method = "lm") +
    theme_classic() + 
    theme(text = element_text(size = 14)) +
    labs(title = "Larger diamonds cost more by diamond color", 
          subtitle = "Price, $",
          y = "", 
          x = "Carat")
## `geom_smooth()` using formula 'y ~ x'

We can use ggplot to create all sorts of cool graphics. See cool gallery with code here: http://r-statistics.co/Top50-Ggplot2-Visualizations-MasterList-R-Code.html

Run regressions

To do this we can use a few different methods. We will first demonstrate using base R’s lm() (linear models) function and then with the fixest package.

Base R

Regressions in R use a formula interface a la y ~ x1 + x2 + .... Let’s see that in action via a simple OLS model using the lm function

base_model = lm(price ~ carat, data = diamonds)

Two things to note:

  1. We saved the resulting model as an object. If you take a look at your global Environment pane (top right window in RStudio). You should see the base_model object there. This reflects the fact that R can hold a multitude of objects in memory at any one time. Speaking fo which…

  2. Note that we specified the dataset that we want to use in the above call, i.e. lm(..., data = diamonds). Specifying the dataset is very important in R; precisely because it can hold multiple objects and datasets in memory at any one time. This is a major difference to Stata where, frames aside, we only ever hold one dataset in memory at a time.

To view detailed information about our saved regression model, I can use the generic summary() function.

summary(base_model)
## 
## Call:
## lm(formula = price ~ carat, data = diamonds)
## 
## Residuals:
##      Min       1Q   Median       3Q      Max 
## -18585.3   -804.8    -18.9    537.4  12731.7 
## 
## Coefficients:
##             Estimate Std. Error t value Pr(>|t|)    
## (Intercept) -2256.36      13.06  -172.8   <2e-16 ***
## carat        7756.43      14.07   551.4   <2e-16 ***
## ---
## Signif. codes:  0 '***' 0.001 '**' 0.01 '*' 0.05 '.' 0.1 ' ' 1
## 
## Residual standard error: 1549 on 53938 degrees of freedom
## Multiple R-squared:  0.8493, Adjusted R-squared:  0.8493 
## F-statistic: 3.041e+05 on 1 and 53938 DF,  p-value: < 2.2e-16

To put this in a regression table form, we can use the modelsummary() function from the aforementioned package.

# library(modelsummary) ## Already loaded
modelsummary(base_model) 
Model 1
(Intercept) -2256.361
(13.055)
carat 7756.426
(14.067)
Num.Obs. 53940
R2 0.849
R2 Adj. 0.849
AIC 945466.5
BIC 945493.2
Log.Lik. -472730.266
F 304050.906

We don’t have time to go into much detail, but modelsummary() is extremely flexible and powerful. It also plays very well with other packages. For example, can tweak this output to look a bit better. We will use the nice kableExtra package to help.

library(kableExtra) ## For adding a footnote to our table
## 
## Attaching package: 'kableExtra'
## The following object is masked from 'package:dplyr':
## 
##     group_rows
modelsummary(
  base_model,  
  title = "Fantastic regression table", 
  stars = TRUE, 
  coef_map = c("carat" = "Carat"),
  gof_omit = "Adj|Pseudo|Log|AIC|BIC|F|R2"
  ) %>%
  add_footnote("An important note.")
## Warning: In version 0.8.0 of the `modelsummary` package, the default significance markers produced by the `stars=TRUE` argument were changed to be consistent with R's defaults.
## This warning is displayed once per session.
Fantastic regression table
Model 1
Carat 7756.426***
(14.067)
Num.Obs. 53940
a An important note.
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

Now we can run all sorts of other models and display them side-by-side. Let’s add some controls

model_add_controls = lm(price ~ carat + depth + table , data = diamonds)

We can add this model to a list with the base model. We name each item here.

models = list(
    "Base"  = base_model, 
    "Add Controls"  = model_add_controls
    )

We can use the same format as earlier.

modelsummary(
  models,  
  title = "Fantastic regression table", 
  stars = TRUE,
  coef_omit = c("(Intercept)"), 
  coef_rename = c("carat" = "Carat", "depth" = "Depth", "table" = "Table width"),
  gof_omit = "Adj|Pseudo|Log|AIC|BIC|F|R2"
  ) %>%
  add_footnote("An important note.")
Fantastic regression table
Base Add Controls
Carat 7756.426*** 7858.771***
(14.067) (14.151)
Depth -151.236***
(4.820)
Table width -104.473***
(3.141)
Num.Obs. 53940 53940
a An important note.
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

Aside 1: Adjusting standard errors

Several R packages provide support for adjusting standard errors (SEs) at estimation time, similar to Stata’s , robust syntax. We’ll see an example of that in the next section. However, R also offers a powerful alternative: Namely “on-the-fly” SE adjustment for models that have already been run. This alternative approach takes a little getting used to if you’re coming from Stata. But separating estimation from inference in this way comes with some big potential upsides.

Grant has a blog post that explains this on-the-fly adjustment process in more depth. However, to very quickly illustrate using the modelsummary() function again, consider what happens when we feed our single base_model object a set of “vcov” arguments. Answer: It automatically reports the adjusted SEs for all those case without having to re-run the model again!

modelsummary(
  base_model,  
  vcov = c('iid', 'robust', 'stata', 'NeweyWest'), ## New
  title = "One model, many standard errors", 
  stars = TRUE,
  coef_map = c("carat" = "Carat"),
  gof_omit = "Adj|Pseudo|Log|AIC|BIC|F|R2"
  )
One model, many standard errors
Model 1 Model 2 Model 3 Model 4
Carat 7756.426*** 7756.426*** 7756.426*** 7756.426***
(14.067) (25.408) (25.399) (150.790)
Num.Obs. 53940 53940 53940 53940
Std. Errors IID Robust Stata Newey-West
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

Note: R (and Python) use a different default sandwich estimator for calculating “robust” (HC) SEs than Stata. The differences in most cases are likely to be small and due to a different DoF adjustment choice. For more information, see here or here.

Aside 2: Coefficient plot

The final arrow in the modelsummary quiver is modelplot(). This accepts most of the same arguments as its modelsummary() companion and produces nice, ggplot2-friendly coefficient plots. Adapting our previous example:

modelplot(
    base_model,  
    vcov = c(iid = 'iid', 'robust', 'stata', 'NeweyWest'), 
    coef_map = c("carat" = "Carat")
    )

Using fixest

Next, we will use fixest to run a regression with fixed effects.

library(fixest)
## fixest 0.9.0, BREAKING changes! (Permanently remove this message with fixest_startup_msg(FALSE).) 
## - In i():
##     + the first two arguments have been swapped! Now it's i(factor_var, continuous_var) for interactions. 
##     + argument 'drop' has been removed (put everything in 'ref' now).
## - In feglm(): 
##     + the default family becomes 'gaussian' to be in line with glm(). Hence, for Poisson estimations, please use fepois() instead.

The fixed effects are those after the | symbol.

fe_model = feols(price ~ carat + depth + table | color + cut + clarity, data = diamonds, cluster = ~color)

We can create a similar list as earlier.

models = list(
    "Base"  = base_model, 
    "Add Controls"  = model_add_controls,
    "Fixed-Effects" = fe_model
)
modelsummary(models,  
  title = "Fantastic regression table", 
  stars = TRUE,
  gof_omit = "Adj|Pseudo|Log|AIC|BIC|F|R2", 
  coef_omit = c("(Intercept)"), 
  coef_rename = c("carat" = "Carat", 
                  "Num.Obs." = "N")) %>%
  add_footnote("An important note.",
  threeparttable = TRUE)
Fantastic regression table
Base Add Controls Fixed-Effects
Carat 7756.426*** 7858.771*** 8895.194***
(14.067) (14.151) (283.956)
depth -151.236*** -21.024*
(4.820) (6.770)
table -104.473*** -24.803**
(3.141) (4.298)
Num.Obs. 53940 53940 53940
Std. Errors Clustered (color)
a An important note.
+ p < 0.1, * p < 0.05, ** p < 0.01, *** p < 0.001

See more html display options here, https://cran.r-project.org/web/packages/kableExtra/vignettes/awesome_table_in_html.html

Now we can export this to latex

modelsummary(
  models,  
  title = "Fantastic regression table", 
  stars = TRUE,
  gof_omit = "Adj|Pseudo|Log|AIC|BIC|F|R2", 
  coef_omit = c("(Intercept)"), 
  coef_rename = c("carat" = "Carat", 
                  "Num.Obs." = "N"), 
  output = 'output/table.tex'
  )

Table using etable

We can also use the etable function that is a part of fixest package.

models_for_etable = list(
    "Fixed-Effects" = fe_model
)
etable(models_for_etable) %>%
    kbl(caption = "Fantastic regression table") %>%
    kable_styling(full_width = F)
Fantastic regression table
Fixed-Effects
Dependent Var.: price
carat 8,895.2*** (284.0)
depth -21.02* (6.770)
table -24.80** (4.298)
Fixed-Effects: ——————
color Yes
cut Yes
clarity Yes
_______________ __________________
S.E.: Clustered by: color
Observations 53,940
R2 0.91605
Within R2 0.91014

Aside: See this excellent blog post by Patrick Baylis on how to “fine-tune” your tables using etable for latex export. It’s quite simple to do, but will require a bit of time perfecting it to your exact liking. While it’s outside the scope of this presentation, it’s some hard earned knowledge he’s sharing that should make your life easier. https://www.patrickbaylis.com/blog/2021-05-30-making-tables/

Package management

A key feature of R is that you’ll need to have packages installed and load them as needed. This can be painful to keep track of across machines and especially across coauthors. In addition to simply having the package installed, making sure you have the right version of the package is important.

pacman

pacman is a fantastically named package manager, it can handle both packages on CRAN and packages stored only on places like github. The p_load function will include the library if you have it and install it and include it if you do not. For this code chunk I have it set to not evaluate since we previously included many of these libraries.

if (!require("pacman")) install.packages("pacman")
pacman::p_load(
  ggplot2, dplyr, modelsummary, kableExtra, fixest, devtools
)
pacman::p_load_gh("hemken/Statamarkdown")

This will install pacman if it’s not currently installed. This code also introduces us to some other coding in R. You can call a function from a package using the :: syntax where the package name is on the left and the function name on the right. This is helpful if there are two packages that have the same function name (e.g., this happens with the select function sometimes).

renv

renv helps make sure that differences in package versions won’t prevent you from being able to replicate your results. Essentially it stores the exact version of each package you are using in a sandboxed environment. It makes it easy for another person (or future you) to use the exact same version of each package. It saves these in a renv.lock file. Once you’ve installed renv and you’ve opened up a new R-project, just type renv::init() and you should be good to go. renv::snapshot() will update changes.

You can read more about renv here: https://rstudio.github.io/renv/articles/renv.html

Running stata from R

  • First you’ll need to make sure that you have stata on the computer you’re using.
  • Second, you’ll need to install Statamarkdown

Here you’d load the package devtools, this allows you to install packages from github. If you’re using pacman the p_load_gh function is a great way to load these packages. Then I’ll install the Statamarkdown package. Again, not needed if you’ve done this using pacman so I’ve set this code chunk to only show the code and not run it.

library("devtools")
install_github("hemken/Statamarkdown")

Load the package Statamarkdown, again not needed if using pacman.

library("Statamarkdown")
## Stata found at /Applications/Stata//StataMP.app/Contents/MacOS/StataMP
## The 'stata' engine is ready to use.

Then you just need to type this code chunk.

```{stata}
sysuse auto
summarize
```

which will result in the following output.

(1978 Automobile Data)

    Variable |        Obs        Mean    Std. Dev.  
>      Min        Max
-------------+--------------------------------------
> -------------------
        make |          0
       price |         74    6165.257    2949.496   
>     3291      15906
         mpg |         74     21.2973    5.785503   
>       12         41
       rep78 |         69    3.405797    .9899323   
>        1          5
    headroom |         74    2.993243    .8459948   
>      1.5          5
-------------+--------------------------------------
> -------------------
       trunk |         74    13.75676    4.277404   
>        5         23
      weight |         74    3019.459    777.1936   
>     1760       4840
      length |         74    187.9324    22.26634   
>      142        233
        turn |         74    39.64865    4.399354   
>       31         51
displacement |         74    197.2973    91.83722   
>       79        425
-------------+--------------------------------------
> -------------------
  gear_ratio |         74    3.014865    .4562871   
>     2.19       3.89
     foreign |         74    .2972973    .4601885   
>        0          1

/Users/hollinal/Documents/GitHub/ehec-intro-to-r/not
> es


(file output/scatter.png written in PNG format)

You can then put these image in the RMarkdown document. scatter-from-stata

Note: If you run this on your computer the scatter will likely not look the same. These are not default stata graph preferences. See blindschemes with the suboption plotplainblind if you like this formatting.

Aside: It’s possible to do this without the Statamarkdown package, but I struggeled to get it to work, possibly because I have Stata-MP. You can see this link for more details: https://bookdown.org/yihui/rmarkdown-cookbook/eng-stata.html For more information on Statamarkdown see here: https://www.ssc.wisc.edu/~hemken/Stataworkshops/Stata%20and%20R%20Markdown/StataEnginePath.html

Meta: Use R to call stata to call R

Say that you have an R-script called test.R. This test script just shows sin(3) and is saved in the same folder as this .Rmd file.

```{stata}
shell /usr/local/bin/R --vanilla <test.R
```
R version 4.1.0 (2021-05-18) -- "Camp Pontanezen"
Copyright (C) 2021 The R Foundation for Statistical 
> Computing
Platform: x86_64-apple-darwin17.0 (64-bit)

R is free software and comes with ABSOLUTELY NO WARR
> ANTY.
You are welcome to redistribute it under certain con
> ditions.
Type 'license()' or 'licence()' for distribution det
> ails.

  Natural language support but running in an English
>  locale

R is a collaborative project with many contributors.
Type 'contributors()' for more information and
'citation()' on how to cite R or R packages in publi
> cations.

Type 'demo()' for some demos, 'help()' for on-line h
> elp, or
'help.start()' for an HTML browser interface to help
> .
Type 'q()' to quit R.

> sin(3)
[1] 0.14112
> 

Misc. tips

Use R projects

If you take a look at the parent directory (repo) containing this document, you’ll see a file called ehec-intro-to-r.Rproj. The .Rproj denotes an “R project” file.

What is this and why is it useful? Think of .Rproj files as specifying the root of a bunch of related files. Among other things, this helps to keep your projects organised and ergonomic. For example, you can open a group of related files in RStudio simply by clicking the .Rproj file. It also means that you never have to worry about specifying absolute paths when reading or writing files. You need only specify relative paths, since R knows that the project file acts as a base (root) for everything else.

R Projects are just as easy to create as regular R scripts or Rmd files. You can use them to start new projects or associate with an existing directory. Read more about them here.

Clear memory

You can remove objects at any time using the rm() function. For example, rm(base_model) will remove our simple OLS model from earlier. To completely clear your memory and remove all objects, it’s best to simply restart your R session. In RStudio, go to Session > Restart R. (Or use the keyboard shortcut: Ctrl + Shift + F10.)

Reading and writing data

  • Whenever possible, I try to use the data.table functions fread and fwrite. These work really well for csv data.

For example, let’s save the diamonds dataset so we can import it in stata for a little example later on.

library(data.table)

Attaching package: 'data.table'
The following objects are masked from 'package:dplyr':

    between, first, last
fwrite(diamonds, file = "data/diamonds.csv")

data.table is really really good with big datasets. Check out this walkthrough: https://cran.r-project.org/web/packages/data.table/vignettes/datatable-intro.html

library(haven)
haven::write_dta(diamonds, path = "data/diamonds.dta", version = 14)

Note: Reading and writing data in other formats is really easy with the haven package. This can also handle, wait for it, SAS datasets really easily.

  • You can read and write excel data with readxl package, which is a part of the tidyverse

We can show that this worked by using the Statamarkdown package.

// Use the csv file saved by fwrite in R
import delimited data/diamonds.csv, clear

twoway ///
  || scatter price carat, ///
  title("Bigger diamonds cost more- using fwrite to csv", pos(11)) ///
  subtitle("Price", pos(11)) ///
  ytitle("") ///
  xtitle("Carat") ///
  yla(,nogrid notick) ///
  xla(,nogrid notick)
  
graph export "output/scatter-diamonds-using-csv.png", replace

// Use the dta file saved by fwrite in R
use data/diamonds.dta, clear

twoway ///
  || scatter price carat, ///
  title("Bigger diamonds cost more- using write_dta to dta", pos(11)) ///
  subtitle("Price", pos(11)) ///
  ytitle("") ///
  xtitle("Carat") ///
  yla(,nogrid notick) ///
  xla(,nogrid notick)

graph export "output/scatter-diamonds-using-dta.png", replace
(10 vars, 53,940 obs)


(file output/scatter-diamonds-using-csv.png written 
> in PNG format)



(file output/scatter-diamonds-using-dta.png written 
> in PNG format)

You can then put these image in the RMarkdown document.

scatter-from-stata-csv scatter-from-stata-dta

Collapsing data

The collapse package is amazing. It’s crazy fast too. collapse plus data.table make creating analyitic files from large microdata a breeze.

Reshaping data.

pivot_wider and pivot_longer are both a part of the tidyverse. Read more about them here. https://tidyr.tidyverse.org/articles/pivot.html


  1. If you are curious about Markdown formatting on its own — how to get bold text or italics, how to create tables or headers, etc — then see this handy cheatsheet.↩︎

  2. Case in point: .Rmd files are Markdown files, so using a leading # in the text will create a heading rather than a comment. However, if we hit Ctrl + Shift + c then RStudio will automatically generate the correct comment syntax, namely <!–– to start the comment and ––> to end it.↩︎

LS0tCnRpdGxlOiBFSEVDLCBJbnRyb2R1Y3Rpb24gdG8gUgphdXRob3I6CiAgICBuYW1lOiBBbGV4IEhvbGxpbmdzd29ydGgsIEdyYW50IE1jRGVybW90dCwgYW5kIEtlbGxpIE1hcnF1YXJkdAogICAgYWZmaWxpYXRpb246IEluZGlhbmEgVW5pdmVyc2l0eSB8IFVuaXZlcnNpdHkgb2YgT3JlZ29uIHwgQ2hpY2FnbyBGRUQKICAgIGRhdGU6ICJgciBmb3JtYXQoU3lzLnRpbWUoKSwgJyVkICVCICVZJylgIiAjIyBPciAiTGVjdHVyZSBuby4iCm91dHB1dDogCiAgICBodG1sX2RvY3VtZW50OgogICAgICAgIHRvYzogeWVzCiAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgdG9jX2Zsb2F0OiB5ZXMKICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLCBjYWNoZSA9IFRSVUUsIGRwaT0zMDApCmBgYAoKIyMgUHJvbG9ndWU6IFIgTWFya2Rvd24gCgojIyMgV2hhdCBpcyBSIE1hcmtkb3duPwoKVHJhZGl0aW9uYWwgYC5SYCBzY3JpcHRzIChhbmFsb2dvdXMgdG8gYSBgLmRvYCBmaWxlIGluIFN0YXRhKSBhcmUgYSBzdGFuZGFyZCB3YXkgdG8gd3JpdGUgcHVyZSBSIGNvZGUuIFlvdSBjb3VsZCBjcmVhdGUgYSBuZXcgUiBzY3JpcHQgeW91cnNlbGYgbm93IGJ5IGNsaWNraW5nIHRoZSAiTmV3IEZpbGUiIGljb24gYXQgdGhlIHRvcC1sZWZ0IG9mIHlvdXIgUlN0dWRpbyBzZXNzaW9uLiBTZXJpb3VzbHksIGdvIGFoZWFkIGFuZCB0cnkgcXVpY2tseS4KCldoYXQgeW91IHNlZSBpbiBmcm9udCBvZiB5b3UsIGhvd2V2ZXIsIGlzIG5vdCBhIHBsYWluIFIgc2NyaXB0LiBJdCBpcyBhbiBbKipSIE1hcmtkb3duKipdKGh0dHBzOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tLykgZG9jdW1lbnQgKGZpbGUgZXh0ZW5zaW9uOiBgLlJtZGApLiBUaGlzIGlzIHNpbXBseSBhIGRvY3VtZW50IHR5cGUgdGhhdCBhbGxvd3MgeW91IHRvIGNvbWJpbmUgYm90aCB0ZXh0IC0tLSBsaWtlIHRoZSBzZW50ZW5jZSB5b3UgYXJlIHJlYWRpbmcgbm93IC0tLSBhbmQgYWN0dWFsIFIgY29kZS4gSXQgaXMgc2ltaWxhciB0byBTdGF0YSdzIGBkeW5kb2NgIGFuZCBpcyBhIHZlcnkgY29udmVuaWVudCB3YXkgdG8gaW50ZWdyYXRlIHRleHQgYW5kIGNvZGUgaW4gYSBzaW5nbGUgZG9jdW1lbnQuIFRoaW5rIG9mIGl0IGxpa2UgTGFUZVggYW5kIFIgaGFkIGEgYmFieSwgYnV0IHdhcyB2ZXJ5IGVhc3kgdG8gdXNlICh3YXMgYSB2ZXJ5IGdvb2QgYmFieT8pLiBJbiBmYWN0LCB0aGUgc2FtZSBSbWQgZmlsZSBjYW4gYmUgImtuaXR0ZWQiIHRvIG11bHRpcGxlIGZvcm1hdHM6IGh0bWwsIHBkZiwgcmljaCB0ZXh0LCBldGMuCgpXZSBkb24ndCB3YW50IHRvIGdldCBfdG9vXyBzaWRldHJhY2tlZCBieSB0aGUgc3BlY2lhbCBmZWF0dXJlcyBvZiBSIE1hcmtkb3duLi4uIGFuZCBzaG91bGQgZW1waGFzaXNlIHRoYXQgaXQgaXMgbm90IHRoZSBvbmx5IChvciBldmVuICJzdGFuZGFyZCIpIHdheSB0byBkbyBhbmFseXNpcyBpbiBSLiBCdXQgaXQgaXMgYW4gZXh0cmVtZWx5IHBvcHVsYXIgZmVhdHVyZSBvZiBSIGFuZCBpcyBhbHNvIGEgZ3JlYXQgd2F5IHRvIHRlYWNoIGEgc2Vzc2lvbiBsaWtlIHRoaXMuIElmIHlvdSBhcmUgaW50ZXJlc3RlZCBpbiBsZWFybmluZyBtb3JlLCB0aGVuIHRoZSBbb2ZmaWNpYWwgd2Vic2l0ZV0oaHR0cHM6Ly9ybWFya2Rvd24ucnN0dWRpby5jb20vKSBpcyBhIGdyZWF0IHBsYWNlIHRvIHN0YXJ0Ll5bSWYgeW91IGFyZSBjdXJpb3VzIGFib3V0IE1hcmtkb3duIGZvcm1hdHRpbmcgb24gaXRzIG93biAtLS0gaG93IHRvIGdldCAqKmJvbGQqKiB0ZXh0IG9yIF9pdGFsaWNzXywgaG93IHRvIGNyZWF0ZSB0YWJsZXMgb3IgaGVhZGVycywgZXRjIC0tLSB0aGVuIHNlZSBbdGhpcyBoYW5keSBjaGVhdHNoZWV0XShodHRwczovL3d3dy5tYXJrZG93bmd1aWRlLm9yZy9iYXNpYy1zeW50YXgvKS5dIE5vdGUgZnVydGhlciB0aGF0IHRoaXMgZG9jdW1lbnQgaXMgYWJvdXQgYXMgdmFuaWxsYSBhcyBpdCBnZXRzLCBidXQgeW91IFtjYW4gZ2V0XShodHRwczovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbS9nYWxsZXJ5Lmh0bWwpIGV4dHJlbWVseSBmYW5jeS4KCiMjIyBTZW5kIGNvbW1hbmRzIHRvIFIgaW4gUiBNYXJrZG93biAKCkJlaGluZCB0aGUgc2NlbmVzLCBpbiB0aGUgYC5SbWRgIGRvY3VtZW50IHdlIGFyZSB0eXBpbmcgaW4gImNvZGUtY2h1bmtzIiB0aGF0IGdldCBydW4gYnkgUi4gVGhlc2UgY29kZSBjaHVua3MgYXJlIGZlbmNlZCBpbiB3aXRoIHRoZSBiYWNrdGlja3MuCgpGb3IgZXhhbXBsZSwgdGhpcyBpcyBhIGNvZGUgY2h1bmsgdGhhdCB3aWxsIGV2YWx1YXRlIHRoZSBjb21tYW5kICBgc2luKDMpYCAKCmBgYGAKYGBge3J9YHIgJydgCnNpbigzKQpgYGAKYGBgYAoKVGhlIG91dHB1dCBvZiB0aGlzIHdpbGwgYXBwZWFyIGFzIGZvbGxvd3M6CgpgYGB7ciBlY2hvID0gRkFMU0V9CnNpbigzKQpgYGAKCllvdSBjYW4gYWxzbyBjcmVhdGUgY2FsY3VsYXRpb25zIGlubGluZSBmb3IgZXhhbXBsZSBgYCBgciBrbml0cjo6aW5saW5lX2V4cHIoInNpbigzKSIpYCBgYCB3aWxsIGV2YWx1YXRlIGBzaW4oMylgLiBXaXRoIG91dHB1dCByZW5kZXJpbmcgbGlrZSB0aGlzIGByIHNpbigzKWAuCgpPa2F5LCB3aXRoIHRoYXQgYml0IG9mIFIgTWFya2Rvd24gcHJvbG9ndWUgb3V0IG9mIHRoZSB3YXksIGxldCdzIGdldCBkb3duIHRvIHJ1bm5pbmcgYWN0dWFsIFIgY29kZS4KCiMjIExvYWQgcGFja2FnZXMgaW50byBtZW1vcnkgCgpBbiBhd2Z1bCBsb3Qgb2Ygd29yayBpbiBSIGdldHMgZG9uZSB0aHJvdWdoIHRoaXJkLXBhcnR5IHBhY2thZ2VzIHRoYXQgbXVzdCBiZSBpbnN0YWxsZWQgc2VwYXJhdGVseSB0byAiYmFzZSIgUi4gVGhpcyBpcyBzaW1pbGFyIHRvIGhvdyB0aGlyZC1wYXJ0eSBwYWNrYWdlcyBpbiBTdGF0YSBjYW4gYmUgaW5zdGFsbGVkIGZyb20gc3NjLiBXZSByZWNvbW1lbmQgdXNpbmcgUlN0dWRpbydzIHBhY2thZ2UgaW5zdGFsbGVyIHRvIGZpbmQgYW5kIGluc3RhbGwgKG9yIHVwZGF0ZSkgYW55IGV4dGVybmFsIHBhY2thZ2VzLiBXZSdsbCBzaG93IHlvdSBob3cgZG8gdG8gdGhpcyBpbiB0aGUgbGl2ZSBzZXNzaW9uLCBvciB5b3UgY2FuIHRha2UgYSBsb29rIFtoZXJlXShodHRwczovL3Jhdy5naXRoYWNrLmNvbS91by1lYzYwNy9sZWN0dXJlcy9tYXN0ZXIvMDEtaW50cm8vMDEtSW50cm8uaHRtbCMzNCkuIEJ1dCBub3RlIHRoYXQgeW91IGNhbiBhbHNvIGluc3RhbGwgUiBwYWNrYWdlcyBkaXJlY3RseSBmcm9tIHRoZSBSIGNvbnNvbGUsIGUuZy4KCmBgYHtyIHBrZ19pbnN0YWxsLCBldmFsPUZBTFNFfQppbnN0YWxsLnBhY2thZ2VzKGMoJ2dncGxvdDInLCAnZHBseXInKSkKYGBgCgpBIGtleSBkaWZmZXJlbmNlIGJldHdlZW4gUiBhbmQgU3RhdGEsIGhvd2V2ZXIsIGlzIHRoYXQgaW5zdGFsbGVkIFIgcGFja2FnZXMgbXVzdCBiZSBsb2FkZWQgaW50byBtZW1vcnkgaWYgeW91IHdhbnQgdG8gdXNlIHRoZW0gaW4gdGhhdCBzZXNzaW9uLiBUaGluayBvZiBpdCBsaWtlIGFuIGFwcCBvbiB5b3VyIHBob25lLiBZb3UgbWlnaHQgaGF2ZSBkb3dubG9hZGVkIHRoZSBhcHAgYWxyZWFkeSwgYnV0IHlvdSBuZWVkIHRvIG9wZW4gaXQgZXZlcnkgdGltZSB5b3UgdXNlIGl0LiBMYXRlciBvbiB3ZSB3aWxsIGNvdmVyIGhvdyB0byBtYW5hZ2UgcGFja2FnZXMgZWZmaWNpZW50bHkuIEJ1dCByaWdodCBub3csIGFzc3VtaW5nIHRoYXQgeW91IGhhdmUgYWxyZWFkeSBpbnN0YWxsZWQgdGhlbSwgbGV0J3MgbG9hZCBhbmQgcGxheSBhcm91bmQgd2l0aCBzb21lIFIgcGFja2FnZXMuCgpIZXJlIHdlIGFyZSBnb2luZyB0byBsb2FkIHRoZSBbYGdncGxvdDJgXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy8pIHBhY2thZ2UsIHdoaWNoIGlzIGFuIGV4Y2VsbGVudCBncmFwaGljcyBwYWNrYWdlLiBgZ2dgIGhlcmUgc3RhbmRzIGZvciAiZ3JhbW1hciBvZiBncmFwaGljcyIuIFRoaXMgaXMgYSBwYXJ0IG9mIHRoZSBgdGlkeXZlcnNlYCBzdWl0ZSBvZiBwYWNrYWdlcyB0aGF0IHlvdSBtYXkgaGF2ZSBoZWFyZCBvZi4gV2Ugd2lsbCBhbHNvIGxvYWQgdGhlIFtgZHBseXJgXShodHRwczovL2RwbHlyLnRpZHl2ZXJzZS5vcmcvKSBwYWNrYWdlLCB3aGljaCBpcyBhIHBvcHVsYXIgZGF0YSB3cmFuZ2xpbmcgcGFja2FnZSAoYW5kIGlzIGFsc28gYSBwYXJ0IG9mIHRoZSBgdGlkeXZlcnNlYCkuIAoKYGBge3IgbG9hZC1wYWNrYWdlc30KIyBsaWJyYXJ5KHRpZHl2ZXJzZSkgIyMgU2hvcnRjdXQgdG8gbG9hZCBib3RoIGdncGxvdDIgJiBkcGx5ciAoJiBzZXZlcmFsIG90aGVyIHBhY2thZ2VzKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmBgYAoKPiAqKkFzaWRlOioqIENvbW1lbnRzIGluIGAuUmAgZmlsZXMgKG9yIGNvZGUgY2h1bmtzKSBhcmUgZGVub3RlZCB3aXRoIHRoZSBgI2AgY2hhcmFjdGVyLiBBIHNob3J0Y3V0IHRvIGNvbW1lbnQgb3V0IGEgbGluZSAob3IgcmVnaW9uKSBpbiBSU3R1ZGlvIGlzIGBDdHJsICsgU2hpZnQgKyBjYC4gVGhpcyBzaG9ydGN1dCBnZW5lcmFsbHkganVzdCB3b3JrcyBjb3JyZWN0bHkgZm9yIHdoYXRldmVyIGxhbmd1YWdlIG9yIHNjcmlwdCB0aGF0IHlvdSBvcGVuIGluIFJTdHVkaW8uXltDYXNlIGluIHBvaW50OiBgLlJtZGAgZmlsZXMgYXJlIE1hcmtkb3duIGZpbGVzLCBzbyB1c2luZyBhIGxlYWRpbmcgYCNgIGluIHRoZSB0ZXh0IHdpbGwgY3JlYXRlIGEgaGVhZGluZyByYXRoZXIgdGhhbiBhIGNvbW1lbnQuIEhvd2V2ZXIsIGlmIHdlIGhpdCBgQ3RybCArIFNoaWZ0ICsgY2AgdGhlbiBSU3R1ZGlvIHdpbGwgYXV0b21hdGljYWxseSBnZW5lcmF0ZSB0aGUgY29ycmVjdCBjb21tZW50IHN5bnRheCwgbmFtZWx5IGA8IeKAk+KAk2AgdG8gc3RhcnQgdGhlIGNvbW1lbnQgYW5kIGDigJPigJM+YCB0byBlbmQgaXQuXSAKCiMjIFZpZXcgZGF0YQoKTm93IHRoYXQgdGhlc2UgcGFja2FnZXMgYXJlIGxvYWRlZCBpbnRvIG1lbW9yeSwgd2UgY2FuIGJlZ2luIHRvIHVzZSB0aGVtLiBJbiB0aGUgY29kZSBiZWxvdywgd2UnbGwgZXhwbG9yZSB0aGUgYGRpYW1vbmRzYCBkYXRhc2V0IHRoYXQgY29tZXMgYnVuZGxlZCB3aXRoIGBnZ3Bsb3QyYC4gTXVjaCBsaWtlIFN0YXRhJ3MgYXV0byBkYXRhc2V0LCBtYW55IHBhY2thZ2VzIGJ1bmRsZSBwcmUtaW5zdGFsbGVkIGRhdGFzZXRzIHRoYXQgYXJlIHVzZWZ1bCBmb3IgdHV0b3JpYWxzIGFuZCBkZWJ1Z2dpbmcuIAoKVGhlIGBkaWFtb25kc2AgZGF0YXNldCBpcyBhbHJlYWR5IGF2YWlsYWJsZSB0byB1cywgc2luY2Ugd2UndmUgbG9hZGVkIGBnZ3Bsb3QyYCBpbnRvIG1lbW9yeS4gQnV0IGxldCdzIGJyaW5nIGl0IHZpc2libHkgaW50byBvdXIgZ2xvYmFsIEVudmlyb25tZW50ICh0b3AgcmlnaHQtaGFuZCBwYW5lIGluIFJTdHVkaW8pLiBUaGlzIHdvdWxkIGhhcHBlbiBhdXRvbWF0aWNhbGx5IGlmIHdlIHJlYWQgaW4gYW4gZXh0ZXJuYWwgZmlsZSBsaWtlIGEgLmNzdiBvciAuZHRhLiBXZSdsbCBnZXQgdG8gZXh0ZXJuYWwgZmlsZSBJL08gbGF0ZXIsIHRob3VnaC4gCgpgYGB7ciBkYXRhLWRpYW1vbmRzLCBldmFsID0gRkFMU0V9CmRhdGEoImRpYW1vbmRzIikKYGBgCgpXZSBjYW4gbG9vayBhdCB0aGUgZmlyc3QgZml2ZSBvYnNlcnZhdGlvbnMgdXNpbmcgdGhlIGBoZWFkYCBjb21tYW5kCgpgYGB7ciBoZWFkLW9mLWRhdGF9IApoZWFkKGRpYW1vbmRzKQpgYGAKCldlIGNhbiBnZXQgYSBsaXN0IG9mIGNvbHVtbiBuYW1lcyBmYWlybHkgZWFzaWx5IHRvbwoKYGBge3IgbGlzdC1vZi1jb2x1bW4tbmFtZXN9Cm5hbWVzKGRpYW1vbmRzKQpgYGAKCj4gVGlwOiBZb3UgY2FuIGJyaW5nIHRoZSBkYXRhc2V0IGludG8gdmlldyAoc2ltaWxhciB0byBTdGF0YSdzIGJyb3dzZSBmZWF0dXJlKSBieSB0eXBpbmcgYFZpZXcoZGlhbW9uZHMpYCwgb3IganVzdCBieSBjbGlja2luZyBvbiBpdCBpbiB5b3VyIEVudmlyb25tZW50IHBhbmUuIAoKTGV0J3MgY29tcHV0ZSB0aGUgYXZlcmFnZSBwcmljZSBieSBjb2xvci4gVGhlIG5leHQgY29kZSBjaHVuayBpcyB1c2luZyBgZHBseXJgIGNvbW1hbmRzICgidmVyYnMiKSBhbmQgaW52b2tlcyBhICJwaXBlIiAodGhlIGAlPiVgIHN5bnRheCkgdG8gd3JpdGUgY2xlYW5lciBjb2RlLgoKYGBge3IgbWVhbi1wcmljZS1ieS1jb2xvcn0KIyMgc3VtbWFyaXNlKGdyb3VwX2J5KGRpYW1vbmRzLCBjb2xvciksIG1lYW4ocHJpY2UpKQpkaWFtb25kcyAlPiUgZ3JvdXBfYnkoY29sb3IpICU+JSBzdW1tYXJpc2UobWVhbihwcmljZSkpIApgYGAKCkhlcmUgdGhlIHBpcGUgY29tbWFuZCBgJT4lYCB3aWxsIHRha2UgdGhlIG91dHB1dCBmcm9tIHRoZSBjb21tYW5kIG9uIHRoZSBsZWZ0IGFuZCAicGlwZXMiIGl0IGFzIGlucHV0IHRvIHRoZSBjb21tYW5kIG9uIHRoZSByaWdodC4gU28gYWJvdmUgd2UgCgoxLiB0YWtlIHRoZSBkYXRhc2V0IGNhbGxlZCBkaWFtb25kcyAKMS4gc2VuZCBpdCBhcyBpbnB1dCB0byB0aGUgY29tbWFuZCBgZ3JvdXBfYnlgLCB3aGljaCBncm91cHMgYnkgdGhlIHVuaXF1ZSB2YWx1ZXMgaW4gdGhlIGNvbHVtbiBgY29sb3JgCjEuIFdlIHRoZW4gc2VuZCB0aGlzIGdyb3VwZWQgZGF0YSB0byB0aGUgYHN1bW1hcml6ZWAgY29tbWFuZCwgd2hpY2ggY2FsY3VsYXRlcyB0aGUgbWVhbiBwcmljZSBpbiBlYWNoIGdyb3VwLiAKClR3byB0aGluZ3MgdG8gbm90ZS4gRmlyc3Qgb2ZmLCB5b3UgY2FuIGp1c3QgbW92ZSBvbnRvIGEgbmV3IGxpbmUgd2l0aG91dCBhbiBlcnJvci4gVGhpcyBpcyBuZWF0IGJlY2F1c2UgaXQgYWxsb3dzIHVzIHRvIHdyaXRlIGNsZWFuZXIgY29kZSBhbmQgeW91IGRvbid0IG5lZWQgYSBkZWxpbWl0ZXIgb3IgdGhlIGAvLy9gIHlvdSBtYXkgYmUgdXNlZCB0byBmcm9tIHN0YXRhLiBTbyB3ZSBjYW4gcmV3cml0ZSB0aGUgYWJvdmUgbGlrZSB0aGlzLiAKCmBgYHtyIG1lYW4tcHJpY2UtYnktY29sb3ItY2xlYW5lcn0KZGlhbW9uZHMgJT4lIAogICAgZ3JvdXBfYnkoY29sb3IpICU+JSAKICAgIHN1bW1hcml6ZShtZWFuKHByaWNlKSkgCmBgYAoKU2Vjb25kLCBpbiBtb3JlIHJlY2VudCB2ZXJzaW9ucyBvZiBSICg0LjEgYW5kIGFib3ZlKSwgeW91IGRvbid0IG5lZWQgdG8gaGF2ZSBgZHB5bHJgIGluc3RhbGxlZCB0byBwaXBlLiBZb3UgY2FuIGRvIHRoZSBzYW1lIHRoaW5nIHVzaW5nIGB8PmAuIAoKYGBge3IgbWVhbi1wcmljZS1ieS1jb2xvci1uZXctcGlwZX0KZGlhbW9uZHMgfD4KICAgIGdyb3VwX2J5KGNvbG9yKSB8PgogICAgc3VtbWFyaXplKG1lYW4ocHJpY2UpKSAKYGBgCgo+ICoqTm90ZToqKiBJbiB0aGlzIGV4YW1wbGUsIGAlPiVgIGFuZCBgfD5gIGFyZSBpbnRlcmNoYW5nZWFibGUsIGJ1dCB0aGUgdHdvIGFyZSBub3QgaW50ZXJjaGFuZ2VhYmxlIGluIGFsbCBzZXR0aW5ncy4gU2VlIGh0dHBzOi8vd3d3LnItYmxvZ2dlcnMuY29tLzIwMjEvMDUvdGhlLW5ldy1yLXBpcGUvIGZvciBtb3JlIGRldGFpbHMuIAoKIyMjIyBNYWtlIHN1bW1hcnkgc3RhdGlzdGljcyB0YWJsZS4gCgpBbiBhbm5veWluZywgYnV0IGFsc28gcG93ZXJmdWwsIGZlYXR1cmUgb2YgUiBpcyB0aGF0IHRoZXJlIGFyZSAxNSB3YXlzIHRvIGFjY29tcGxpc2ggYW55IHRhc2suIEluIGdlbmVyYWwsIGxpbWl0aW5nIHRoZSBudW1iZXIgb2YgcGFja2FnZXMgeW91IHVzZSBpcyBhIGdvb2QgaWRlYSBzaW5jZSB5b3Ugd29uJ3QgYnVpbGQgZGVwZW5kZW5jaWVzIG9uIGNvZGUgdGhhdCBjb3VsZCBiZWNvbWUgb3V0ZGF0ZWQgYXMgYmFzZSBSIGFuZCBvdGhlciBwYWNrYWdlcyBhcmUgdXBkYXRlZC4gVGhhdCBiZWluZyBzYWlkLCB0aGUgcG93ZXIgb2YgUiBpcyB0aGUgYWJpbGl0eSB0byB1c2UgZmFudGFzdGljIHVzZXIgd3JpdHRlbiBwYWNrYWdlcy4gSGVyZSB3ZSB3aWxsIHVzZSB0aGUgYGRhdGFzdW1tYXJ5KigpYCBmYW1pbHkgb2YgZnVuY3Rpb25zIGZyb20gdGhlIFsqKmBtb2RlbHN1bW1hcnlgKipdKGh0dHBzOi8vdmluY2VudGFyZWxidW5kb2NrLmdpdGh1Yi5pby9tb2RlbHN1bW1hcnkvaW5kZXguaHRtbCkgcGFja2FnZSB0byBtYWtlIGZhbnRhc3RpYyBzdW1tYXJ5IHN0YXRpc3RpYyBwbG90cy4gT3RoZXJ3aXNlIHdlJ2QgaGF2ZSB0byBjb250aW51ZSB0byBkbyB0aGluZ3MgbGlrZSB3ZSBkaWQgYWJvdmUsIGV4cGxvcmluZyB0aGUgZGF0YSBhbmQgbWFraW5nIHN1bW1hcnkgdGFibGVzIG9uIG91ciBvd24uCgo+ICoqQXNpZGU6KiogSW4gU3RhdGEsIG1hbnkgc3NjIG1vZHVsZXMgY29uc2lzdCBvZiBvbmUgcHJpbWFyeSBmdW5jdGlvbiwgd2hpY2ggc2hhcmVzIHRoZSBzYW1lIG5hbWUgKGUuZy4gYGl2cmVnMmAsIGByZWdoZGZlYCkuIFIgcGFja2FnZXMgdGVuZCB0byBoYXZlIG1hbnkgZnVuY3Rpb25zLCBzbyB5b3Ugc2hvdWxkbid0IGV4cGVjdCB0aGVzZSBmdW5jdGlvbnMgdG8gc2hhcmUgdGhlIGV4YWN0IHNhbWUgbmFtZSBhcyB0aGUgcGFja2FnZSBpdHNlbGYuCgpGaXJzdCwgbG9hZCB0aGUgbW9kZWwgc3VtbWFyeSBwYWNrYWdlIApgYGB7ciBsb2FkLW1vZGVsc3VtbWFyeX0KbGlicmFyeShtb2RlbHN1bW1hcnkpCmBgYAoKCk5vdyB3ZSBjYW4gdXNlIHRoZSBgZGF0YXN1bW1hcnlfc2tpbWAgZnVuY3Rpb24uIAoKYGBge3Igc3VtbWFyeS10YWJsZX0KZGF0YXN1bW1hcnlfc2tpbShkaWFtb25kcykKYGBgCgpCdXQgbm90IGV2ZXJ5IHZhcmlhYmxlIGlzIG51bWVyaWMuIGBkYXRhc3VtbWFyeWAgaGFzIHRoaXMgY292ZXJlZCB3aXRoIHRoZSBgdHlwZSA9ICJjYXRlZ29yaWNhbCJgIHN1Yi1vcHRpb24uIAoKYGBge3Igc3VtbWFyeS10YWJsZS1jYXRlZ29yaWNhbH0KZGF0YXN1bW1hcnlfc2tpbShkaWFtb25kcywgdHlwZSA9ICJjYXRlZ29yaWNhbCIpCmBgYAoKTGVhcm4gbW9yZSBhYm91dCB0aGUgYGRhdGFzdW1tYXJ5KmAgZmFtaWx5IG9mIGZ1bmN0aW9ucyBoZXJlOiBodHRwczovL3ZpbmNlbnRhcmVsYnVuZG9jay5naXRodWIuaW8vbW9kZWxzdW1tYXJ5L2FydGljbGVzL2RhdGFzdW1tYXJ5Lmh0bWwgCgoKIyMgIEdyYXBoaW5nCgpOZXh0IHdlIHdpbGwgdXNlIHRoZSBgZ2dwbG90YCBmdW5jdGlvbi4gWW91IGNhbiBsZWFybiBtb3JlIGFib3V0IGFueSBSIGZ1bmN0aW9uIGJ5IHR5cGluZyBpbiBgP2AgYmVmb3JlIHRoZSBmdW5jdGlvbiBuYW1lIChlLmcuLCBgP2dncGxvdGApLgoKLSBPbiB0aGUgZmlyc3QgbGluZSwgd2UgY2FsbCB0aGUgZnVuY3Rpb24sIHRoZW4gY2xhcmlmeSAgdGhlIGRhdGFzZXQgd2Ugd2lzaCB0byB1c2UgKHJlZmVyYWJsZSB0byBieSBpdHMgbmFtZSwgYGRpYW1vbmRzYCksIHRoZSB5LXZhcmlhYmxlIChgcHJpY2VgKSwgYW5kIHRoZSB4LXZhcmlhYmxlIChgY2FyYXRgKS4gCi0gT24gdGhlIHNlY29uZCBsaW5lLCB3ZSBjbGFyaWZ5IHRoYXQgd2Ugd2FudCB0aGUgZ2VvbWV0cnkgdG8gYmUgcG9pbnRzCgpgYGB7ciBnZ3Bsb3QtMX0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHkgPSBwcmljZSwgeCA9IGNhcmF0KSkgKwogICAgZ2VvbV9wb2ludCgpIApgYGAKClRoaW5rIGNoYW5naW5nIGdyYXBoaWMgZmVhdHVyZXMgaW4gZ2dwbG90IGFzIGFkZGluZyBsYXllcnMuIFlvdSBjYW4gZG8gdGhpcyBieSByZXBlYXRpbmcgYWxsIG9mIHRoZSBzYW1lIGNvZGUuIE9yIGJ5IHNhdmluZyB0aGUgYWJvdmUgYXMgYW4gb2JqZWN0IHRoZW4gYWRkaW5nIHRvIHRoYXQgb2JqZWN0LgoKRm9yIGV4YW1wbGUgbGV0J3MgYWRkIGEgdGhpcmQgbGluZSB3aGVyZSB1c2UgYSBkZWZhdWx0IHRoZW1lIGNhbGxlZCBjbGFzc2ljIHRoYXQgSSBsaWtlLiAKCmBgYHtyIGdncGxvdC0yfQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeSA9IHByaWNlLCB4ID0gY2FyYXQpKSArCiAgICBnZW9tX3BvaW50KCkgKwogICAgdGhlbWVfY2xhc3NpYygpCmBgYAoKV2UgY2FuIG9idGFpbiB0aGUgc2FtZSByZXN1bHQsIGJ5IHNhdmluZyB0aGUgYWJvdmUgYXMgYW4gb2JqZWN0IGFuZCBhZGRpbmcgdG8gaXQuIAoKYGBge3IgZ2dwbG90LTN9CmJhc2VfcGxvdCA9IGdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh5ID0gcHJpY2UsIHggPSBjYXJhdCkpICsKICAgIGdlb21fcG9pbnQoKQpgYGAKCllvdSBjYW4gYWxzbyB1c2UgdGhlIGA8LWAgYXJyb3cgaW5zdGVhZCBvZiB0aGUgYD1gIHRvIHRoZSBuYW1lIGBiYXNlX3Bsb3RgIAoKYGBge3IgZ2dwbG90LTR9CmJhc2VfcGxvdCArCiAgICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpBbGwgd2UgbmVlZCB0byBkbyBpcyBhZGQgdGhpcyB0aGVtZSBsaW5lIGB0aGVtZV9jbGFzc2ljKClgIHRvIHRoZSBhYm92ZS4gCgojIyMgRGV2aWF0aW9ucyBmcm9tIHNpbXBsZSBzY2F0dGVyIHBsb3QKCkl0J3MgdmVyeSBzaW1wbGUgdG8gbWFrZSBjb21wbGV4IHBsb3RzIGluIFIuIAoKV2UgY2FuIGFkZCBzb21lIHRyYW5zcGFyZW5jeSAKCmBgYHtyIGdncGxvdC01fQpnZ3Bsb3QoZGF0YSA9IGRpYW1vbmRzLCBhZXMoeSA9IHByaWNlLCB4ID0gY2FyYXQpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gLjMzKSArCiAgICB0aGVtZV9jbGFzc2ljKCkKYGBgCgpDdXN0b21pemVkIGxhYmVscyBhbmQgaW5jcmVhc2VkIHNpemUgb2YgdGV4dAoKYGBge3IgZ2dwbG90LTZ9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh5ID0gcHJpY2UsIHggPSBjYXJhdCkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAuMSkgKwogICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICAgIGxhYnModGl0bGUgPSAiTGFyZ2VyIGRpYW1vbmRzIGNvc3QgbW9yZSIsIAogICAgICAgICAgc3VidGl0bGUgPSAiUHJpY2UsICQiLAogICAgICAgICAgeSA9ICIiLCAKICAgICAgICAgIHggPSAiQ2FyYXQiKQpgYGAKClJlY2FsbCB0aGF0IHRoZSBkYXRhIGNvbnRhaW4gaW5mb3JtYXRpb24gb24gZGlhbW9uZCBjb2xvci4gV2UgY2FuIGVhc2lseSBjcmVhdGUgc21hbGwgbXVsdGlwbGVzIG9mIHRoZSBzY2F0dGVyIHBsb3QgZm9yIGVhY2ggY29sb3IuIAoKYGBge3IgZ2dwbG90LTd9CmdncGxvdChkYXRhID0gZGlhbW9uZHMsIGFlcyh5ID0gcHJpY2UsIHggPSBjYXJhdCkpICsKICAgIGdlb21fcG9pbnQoYWxwaGEgPSAuMSkgKwogICAgZmFjZXRfd3JhcCh+Y29sb3IpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArIAogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpKSArCiAgICBsYWJzKHRpdGxlID0gIkxhcmdlciBkaWFtb25kcyBjb3N0IG1vcmUgYnkgZGlhbW9uZCBjb2xvciIsIAogICAgICAgICAgc3VidGl0bGUgPSAiUHJpY2UsICQiLAogICAgICAgICAgeSA9ICIiLCAKICAgICAgICAgIHggPSAiQ2FyYXQiKQpgYGAKClNpbWlsYXJseSwgd2UgY2FuIGFkZCBjb2xvciAob3Igc2hhcGVzKSBiYXNlZCBvbiBkaWFtb25kIGNsYXJpdHkgCgpgYGB7ciBnZ3Bsb3QtOH0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHkgPSBwcmljZSwgeCA9IGNhcmF0LCBjb2xvciA9IGNsYXJpdHkpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gLjMzKSArCiAgICBmYWNldF93cmFwKH5jb2xvcikgKwogICAgdGhlbWVfY2xhc3NpYygpICsgCiAgICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkpICsKICAgIGxhYnModGl0bGUgPSAiTGFyZ2VyIGRpYW1vbmRzIGNvc3QgbW9yZSBieSBkaWFtb25kIGNvbG9yIiwgCiAgICAgICAgICBzdWJ0aXRsZSA9ICJQcmljZSwgJCIsCiAgICAgICAgICB5ID0gIiIsIAogICAgICAgICAgeCA9ICJDYXJhdCIpCmBgYAoKSXQncyBhbHNvIHRyaXZpYWwgdG8gYWRkIGEgcmVncmVzc2lvbiBsaW5lIHRvIGVhY2guIFdlIHdpbGwgcGljayBtZXRob2QgYGxtYCwgd2hpY2ggaXMgYSBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24uCgpgYGB7ciBnZ3Bsb3QtOX0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHkgPSBwcmljZSwgeCA9IGNhcmF0LCBjb2xvciA9IGNsYXJpdHkpKSArCiAgICBnZW9tX3BvaW50KGFscGhhID0gLjMzKSArCiAgICBmYWNldF93cmFwKH5jb2xvcikgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArIAogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpKSArCiAgICBsYWJzKHRpdGxlID0gIkxhcmdlciBkaWFtb25kcyBjb3N0IG1vcmUgYnkgZGlhbW9uZCBjb2xvciIsIAogICAgICAgICAgc3VidGl0bGUgPSAiUHJpY2UsICQiLAogICAgICAgICAgeSA9ICIiLCAKICAgICAgICAgIHggPSAiQ2FyYXQiKQpgYGAKCldlIGNhbiBldmVuIHJlbW92ZSB0aGUgcG9pbnRzIGJlbG93IGJ5IHNpbXBseSByZW1vdmluZyB0aGUgc2Vjb25kIGxpbmUKCmBgYHtyIGdncGxvdC0xMH0KZ2dwbG90KGRhdGEgPSBkaWFtb25kcywgYWVzKHkgPSBwcmljZSwgeCA9IGNhcmF0LCBjb2xvciA9IGNsYXJpdHkpKSArCiAgICBmYWNldF93cmFwKH5jb2xvcikgKyAKICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICAgIHRoZW1lX2NsYXNzaWMoKSArIAogICAgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpKSArCiAgICBsYWJzKHRpdGxlID0gIkxhcmdlciBkaWFtb25kcyBjb3N0IG1vcmUgYnkgZGlhbW9uZCBjb2xvciIsIAogICAgICAgICAgc3VidGl0bGUgPSAiUHJpY2UsICQiLAogICAgICAgICAgeSA9ICIiLCAKICAgICAgICAgIHggPSAiQ2FyYXQiKQpgYGAKCldlIGNhbiB1c2UgZ2dwbG90IHRvIGNyZWF0ZSBhbGwgc29ydHMgb2YgY29vbCBncmFwaGljcy4gU2VlIGNvb2wgZ2FsbGVyeSB3aXRoIGNvZGUgaGVyZTogaHR0cDovL3Itc3RhdGlzdGljcy5jby9Ub3A1MC1HZ3Bsb3QyLVZpc3VhbGl6YXRpb25zLU1hc3Rlckxpc3QtUi1Db2RlLmh0bWwKCiMjIFJ1biByZWdyZXNzaW9ucwoKVG8gZG8gdGhpcyB3ZSBjYW4gdXNlIGEgZmV3IGRpZmZlcmVudCBtZXRob2RzLiBXZSB3aWxsIGZpcnN0IGRlbW9uc3RyYXRlIHVzaW5nIGJhc2UgUidzIGBsbSgpYCAoKipsKippbmVhciAqKm0qKm9kZWxzKSBmdW5jdGlvbiBhbmQgdGhlbiB3aXRoIHRoZSBgZml4ZXN0YCBwYWNrYWdlLgo8IS0tIFdlIHdpbGwgdXNlIGBtb2RlbHN1bW1hcnlgIGFuZCBgZXRhYmxlYCAoZnJvbSBgZml4ZXN0YCBwYWNrYWdlKSB0byBkaXNwbGF5IHRoZSByZXN1bHRzIC0tPgoKIyMjICBCYXNlIFIKClJlZ3Jlc3Npb25zIGluIFIgdXNlIGEgZm9ybXVsYSBpbnRlcmZhY2UgX2EgbGFfIGB5IH4geDEgKyB4MiArIC4uLmAuIExldCdzIHNlZSB0aGF0IGluIGFjdGlvbiB2aWEgYSBzaW1wbGUgT0xTIG1vZGVsIHVzaW5nIHRoZSBgbG1gIGZ1bmN0aW9uCgpgYGB7ciBiYXNlLXItcmVncmVzc2lvbn0KYmFzZV9tb2RlbCA9IGxtKHByaWNlIH4gY2FyYXQsIGRhdGEgPSBkaWFtb25kcykKYGBgCgpUd28gdGhpbmdzIHRvIG5vdGU6CgoxLiBXZSBzYXZlZCB0aGUgcmVzdWx0aW5nIG1vZGVsIGFzIGFuIG9iamVjdC4gSWYgeW91IHRha2UgYSBsb29rIGF0IHlvdXIgZ2xvYmFsIEVudmlyb25tZW50IHBhbmUgKHRvcCByaWdodCB3aW5kb3cgaW4gUlN0dWRpbykuIFlvdSBzaG91bGQgc2VlIHRoZSBgYmFzZV9tb2RlbGAgb2JqZWN0IHRoZXJlLiBUaGlzIHJlZmxlY3RzIHRoZSBmYWN0IHRoYXQgUiBjYW4gaG9sZCBhIG11bHRpdHVkZSBvZiBvYmplY3RzIGluIG1lbW9yeSBhdCBhbnkgb25lIHRpbWUuIFNwZWFraW5nIGZvIHdoaWNoLi4uCgoyLiBOb3RlIHRoYXQgd2Ugc3BlY2lmaWVkIHRoZSBkYXRhc2V0IHRoYXQgd2Ugd2FudCB0byB1c2UgaW4gdGhlIGFib3ZlIGNhbGwsIGkuZS4gYGxtKC4uLiwgZGF0YSA9IGRpYW1vbmRzKWAuIFNwZWNpZnlpbmcgdGhlIGRhdGFzZXQgaXMgdmVyeSBpbXBvcnRhbnQgaW4gUjsgcHJlY2lzZWx5IGJlY2F1c2UgaXQgY2FuIGhvbGQgbXVsdGlwbGUgb2JqZWN0cyBhbmQgZGF0YXNldHMgaW4gbWVtb3J5IGF0IGFueSBvbmUgdGltZS4gVGhpcyBpcyBhIG1ham9yIGRpZmZlcmVuY2UgdG8gU3RhdGEgd2hlcmUsIGZyYW1lcyBhc2lkZSwgd2Ugb25seSBldmVyIGhvbGQgb25lIGRhdGFzZXQgaW4gbWVtb3J5IGF0IGEgdGltZS4KClRvIHZpZXcgZGV0YWlsZWQgaW5mb3JtYXRpb24gYWJvdXQgb3VyIHNhdmVkIHJlZ3Jlc3Npb24gbW9kZWwsIEkgY2FuIHVzZSB0aGUgZ2VuZXJpYyBgc3VtbWFyeSgpYCBmdW5jdGlvbi4KCmBgYHtyIGJhc2Utc3VtbWFyeX0Kc3VtbWFyeShiYXNlX21vZGVsKQpgYGAKClRvIHB1dCB0aGlzIGluIGEgcmVncmVzc2lvbiB0YWJsZSBmb3JtLCB3ZSBjYW4gdXNlIHRoZSBbYG1vZGVsc3VtbWFyeSgpYF0oaHR0cHM6Ly92aW5jZW50YXJlbGJ1bmRvY2suZ2l0aHViLmlvL21vZGVsc3VtbWFyeS9hcnRpY2xlcy9tb2RlbHN1bW1hcnkuaHRtbCkgZnVuY3Rpb24gZnJvbSB0aGUgYWZvcmVtZW50aW9uZWQgcGFja2FnZS4gCgpgYGB7ciBiYXNlLXItZGlzcGxheX0KIyBsaWJyYXJ5KG1vZGVsc3VtbWFyeSkgIyMgQWxyZWFkeSBsb2FkZWQKbW9kZWxzdW1tYXJ5KGJhc2VfbW9kZWwpIApgYGAKCldlIGRvbid0IGhhdmUgdGltZSB0byBnbyBpbnRvIG11Y2ggZGV0YWlsLCBidXQgYG1vZGVsc3VtbWFyeSgpYCBpcyBleHRyZW1lbHkgZmxleGlibGUgYW5kIHBvd2VyZnVsLiBJdCBhbHNvIHBsYXlzIHZlcnkgd2VsbCB3aXRoIG90aGVyIHBhY2thZ2VzLiBGb3IgZXhhbXBsZSwgY2FuIHR3ZWFrIHRoaXMgb3V0cHV0IHRvIGxvb2sgYSBiaXQgYmV0dGVyLiBXZSB3aWxsIHVzZSB0aGUgbmljZSBga2FibGVFeHRyYWAgcGFja2FnZSB0byBoZWxwLiAKCmBgYHtyIGxvYWQta2FibGVFeHRyYX0KbGlicmFyeShrYWJsZUV4dHJhKSAjIyBGb3IgYWRkaW5nIGEgZm9vdG5vdGUgdG8gb3VyIHRhYmxlCgptb2RlbHN1bW1hcnkoCiAgYmFzZV9tb2RlbCwgIAogIHRpdGxlID0gIkZhbnRhc3RpYyByZWdyZXNzaW9uIHRhYmxlIiwgCiAgc3RhcnMgPSBUUlVFLCAKICBjb2VmX21hcCA9IGMoImNhcmF0IiA9ICJDYXJhdCIpLAogIGdvZl9vbWl0ID0gIkFkanxQc2V1ZG98TG9nfEFJQ3xCSUN8RnxSMiIKICApICU+JQogIGFkZF9mb290bm90ZSgiQW4gaW1wb3J0YW50IG5vdGUuIikKYGBgCgpOb3cgd2UgY2FuIHJ1biBhbGwgc29ydHMgb2Ygb3RoZXIgbW9kZWxzIGFuZCBkaXNwbGF5IHRoZW0gc2lkZS1ieS1zaWRlLiBMZXQncyBhZGQgc29tZSBjb250cm9scyAKCmBgYHtyIGJhc2Utci1yZWctMn0KbW9kZWxfYWRkX2NvbnRyb2xzID0gbG0ocHJpY2UgfiBjYXJhdCArIGRlcHRoICsgdGFibGUgLCBkYXRhID0gZGlhbW9uZHMpCmBgYAoKV2UgY2FuIGFkZCB0aGlzIG1vZGVsIHRvIGEgbGlzdCB3aXRoIHRoZSBiYXNlIG1vZGVsLiBXZSBuYW1lIGVhY2ggaXRlbSBoZXJlLgoKYGBge3IgYmFzZS1yLW1vZGVsLXNhdmV9Cm1vZGVscyA9IGxpc3QoCiAgICAiQmFzZSIgID0gYmFzZV9tb2RlbCwgCiAgICAiQWRkIENvbnRyb2xzIiAgPSBtb2RlbF9hZGRfY29udHJvbHMKICAgICkKYGBgCgpXZSBjYW4gdXNlIHRoZSBzYW1lIGZvcm1hdCBhcyBlYXJsaWVyLiAKCmBgYHtyIGJhc2Utci1tb2RlbC1kaXNwbGF5LTJ9Cm1vZGVsc3VtbWFyeSgKICBtb2RlbHMsICAKICB0aXRsZSA9ICJGYW50YXN0aWMgcmVncmVzc2lvbiB0YWJsZSIsIAogIHN0YXJzID0gVFJVRSwKICBjb2VmX29taXQgPSBjKCIoSW50ZXJjZXB0KSIpLCAKICBjb2VmX3JlbmFtZSA9IGMoImNhcmF0IiA9ICJDYXJhdCIsICJkZXB0aCIgPSAiRGVwdGgiLCAidGFibGUiID0gIlRhYmxlIHdpZHRoIiksCiAgZ29mX29taXQgPSAiQWRqfFBzZXVkb3xMb2d8QUlDfEJJQ3xGfFIyIgogICkgJT4lCiAgYWRkX2Zvb3Rub3RlKCJBbiBpbXBvcnRhbnQgbm90ZS4iKQpgYGAKCiMjIyMgQXNpZGUgMTogQWRqdXN0aW5nIHN0YW5kYXJkIGVycm9ycwoKU2V2ZXJhbCBSIHBhY2thZ2VzIHByb3ZpZGUgc3VwcG9ydCBmb3IgYWRqdXN0aW5nIHN0YW5kYXJkIGVycm9ycyAoU0VzKSBhdCBlc3RpbWF0aW9uIHRpbWUsIHNpbWlsYXIgdG8gU3RhdGEncyBgLCByb2J1c3RgIHN5bnRheC4gV2UnbGwgc2VlIGFuIGV4YW1wbGUgb2YgdGhhdCBpbiB0aGUgbmV4dCBzZWN0aW9uLiBIb3dldmVyLCBSIGFsc28gb2ZmZXJzIGEgcG93ZXJmdWwgYWx0ZXJuYXRpdmU6IE5hbWVseSAib24tdGhlLWZseSIgU0UgYWRqdXN0bWVudCBmb3IgbW9kZWxzIHRoYXQgaGF2ZSBfYWxyZWFkeV8gYmVlbiBydW4uIFRoaXMgYWx0ZXJuYXRpdmUgYXBwcm9hY2ggdGFrZXMgYSBsaXR0bGUgZ2V0dGluZyB1c2VkIHRvIGlmIHlvdSdyZSBjb21pbmcgZnJvbSBTdGF0YS4gQnV0IHNlcGFyYXRpbmcgZXN0aW1hdGlvbiBmcm9tIGluZmVyZW5jZSBpbiB0aGlzIHdheSBjb21lcyB3aXRoIHNvbWUgYmlnIHBvdGVudGlhbCB1cHNpZGVzLiAKCkdyYW50IGhhcyBhIFtibG9nIHBvc3RdKGh0dHBzOi8vZ3JhbnRtY2Rlcm1vdHQuY29tL2JldHRlci13YXktYWRqdXN0LVNFcy8pIHRoYXQgZXhwbGFpbnMgdGhpcyBvbi10aGUtZmx5IGFkanVzdG1lbnQgcHJvY2VzcyBpbiBtb3JlIGRlcHRoLiBIb3dldmVyLCB0byB2ZXJ5IHF1aWNrbHkgaWxsdXN0cmF0ZSB1c2luZyB0aGUgYG1vZGVsc3VtbWFyeSgpYCBmdW5jdGlvbiBhZ2FpbiwgY29uc2lkZXIgd2hhdCBoYXBwZW5zIHdoZW4gd2UgZmVlZCBvdXIgc2luZ2xlIGBiYXNlX21vZGVsYCBvYmplY3QgYSBzZXQgb2YgIlt2Y292XShodHRwczovL3ZpbmNlbnRhcmVsYnVuZG9jay5naXRodWIuaW8vbW9kZWxzdW1tYXJ5L2FydGljbGVzL21vZGVsc3VtbWFyeS5odG1sI3Zjb3YtMSkiIGFyZ3VtZW50cy4gQW5zd2VyOiBJdCBhdXRvbWF0aWNhbGx5IHJlcG9ydHMgdGhlIGFkanVzdGVkIFNFcyBmb3IgYWxsIHRob3NlIGNhc2Ugd2l0aG91dCBoYXZpbmcgdG8gcmUtcnVuIHRoZSBtb2RlbCBhZ2FpbiEKCmBgYHtyIGJhc2VfbW9kZWxfdmNvdn0KbW9kZWxzdW1tYXJ5KAogIGJhc2VfbW9kZWwsICAKICB2Y292ID0gYygnaWlkJywgJ3JvYnVzdCcsICdzdGF0YScsICdOZXdleVdlc3QnKSwgIyMgTmV3CiAgdGl0bGUgPSAiT25lIG1vZGVsLCBtYW55IHN0YW5kYXJkIGVycm9ycyIsIAogIHN0YXJzID0gVFJVRSwKICBjb2VmX21hcCA9IGMoImNhcmF0IiA9ICJDYXJhdCIpLAogIGdvZl9vbWl0ID0gIkFkanxQc2V1ZG98TG9nfEFJQ3xCSUN8RnxSMiIKICApCmBgYAoKPiAqKk5vdGU6KiogUiAoYW5kIFB5dGhvbikgdXNlIGEgZGlmZmVyZW50IGRlZmF1bHQgc2FuZHdpY2ggZXN0aW1hdG9yIGZvciBjYWxjdWxhdGluZyAicm9idXN0IiAoSEMpIFNFcyB0aGFuIFN0YXRhLiBUaGUgZGlmZmVyZW5jZXMgaW4gbW9zdCBjYXNlcyBhcmUgbGlrZWx5IHRvIGJlIHNtYWxsIGFuZCBkdWUgdG8gYSBkaWZmZXJlbnQgRG9GIGFkanVzdG1lbnQgY2hvaWNlLiBGb3IgbW9yZSBpbmZvcm1hdGlvbiwgc2VlIFtoZXJlXShodHRwczovL2RlY2xhcmVkZXNpZ24ub3JnL3IvZXN0aW1hdHIvYXJ0aWNsZXMvc3RhdGEtd2xzLWhhdC5odG1sKSBvciAgW2hlcmVdKGh0dHBzOi8vbHJiZXJnZS5naXRodWIuaW8vZml4ZXN0L2FydGljbGVzL3N0YW5kYXJkX2Vycm9ycy5odG1sIGxpbmspLgoKIyMjIyBBc2lkZSAyOiBDb2VmZmljaWVudCBwbG90CgpUaGUgZmluYWwgYXJyb3cgaW4gdGhlIG1vZGVsc3VtbWFyeSBxdWl2ZXIgaXMgW2Btb2RlbHBsb3QoKWBdKGh0dHBzOi8vdmluY2VudGFyZWxidW5kb2NrLmdpdGh1Yi5pby9tb2RlbHN1bW1hcnkvYXJ0aWNsZXMvbW9kZWxwbG90Lmh0bWwpLiBUaGlzIGFjY2VwdHMgbW9zdCBvZiB0aGUgc2FtZSBhcmd1bWVudHMgYXMgaXRzIGBtb2RlbHN1bW1hcnkoKWAgY29tcGFuaW9uIGFuZCBwcm9kdWNlcyBuaWNlLCBnZ3Bsb3QyLWZyaWVuZGx5IGNvZWZmaWNpZW50IHBsb3RzLiBBZGFwdGluZyBvdXIgcHJldmlvdXMgZXhhbXBsZToKCmBgYHtyIGJhc2VfbW9kZWxfdmNvdl9wbG90fQptb2RlbHBsb3QoCiAgICBiYXNlX21vZGVsLCAgCiAgICB2Y292ID0gYyhpaWQgPSAnaWlkJywgJ3JvYnVzdCcsICdzdGF0YScsICdOZXdleVdlc3QnKSwgCiAgICBjb2VmX21hcCA9IGMoImNhcmF0IiA9ICJDYXJhdCIpCiAgICApCmBgYAoKIyMjIFVzaW5nIGBmaXhlc3RgCgpOZXh0LCB3ZSB3aWxsIHVzZSBbKipgZml4ZXN0YCoqXShodHRwczovL2xyYmVyZ2UuZ2l0aHViLmlvL2ZpeGVzdC9pbmRleC5odG1sKSB0byBydW4gYSByZWdyZXNzaW9uIHdpdGggZml4ZWQgZWZmZWN0cy4gCgpgYGB7ciBsb2FkLWZpeGVzdH0KbGlicmFyeShmaXhlc3QpCmBgYAoKVGhlIGZpeGVkIGVmZmVjdHMgYXJlIHRob3NlIGFmdGVyIHRoZSBgfGAgc3ltYm9sLiAKCmBgYHtyIGZpeGVzdC1tb2RlbH0KZmVfbW9kZWwgPSBmZW9scyhwcmljZSB+IGNhcmF0ICsgZGVwdGggKyB0YWJsZSB8IGNvbG9yICsgY3V0ICsgY2xhcml0eSwgZGF0YSA9IGRpYW1vbmRzLCBjbHVzdGVyID0gfmNvbG9yKQpgYGAKCldlIGNhbiBjcmVhdGUgYSBzaW1pbGFyIGxpc3QgYXMgZWFybGllci4gCgpgYGB7ciBmaXhlc3QtbGlzdH0KbW9kZWxzID0gbGlzdCgKICAgICJCYXNlIiAgPSBiYXNlX21vZGVsLCAKICAgICJBZGQgQ29udHJvbHMiICA9IG1vZGVsX2FkZF9jb250cm9scywKICAgICJGaXhlZC1FZmZlY3RzIiA9IGZlX21vZGVsCikKYGBgCgpgYGB7ciBmaXhlc3QtZGlzcGxheX0KbW9kZWxzdW1tYXJ5KG1vZGVscywgIAogIHRpdGxlID0gIkZhbnRhc3RpYyByZWdyZXNzaW9uIHRhYmxlIiwgCiAgc3RhcnMgPSBUUlVFLAogIGdvZl9vbWl0ID0gIkFkanxQc2V1ZG98TG9nfEFJQ3xCSUN8RnxSMiIsIAogIGNvZWZfb21pdCA9IGMoIihJbnRlcmNlcHQpIiksIAogIGNvZWZfcmVuYW1lID0gYygiY2FyYXQiID0gIkNhcmF0IiwgCiAgICAgICAgICAgICAgICAgICJOdW0uT2JzLiIgPSAiTiIpKSAlPiUKICBhZGRfZm9vdG5vdGUoIkFuIGltcG9ydGFudCBub3RlLiIsCiAgdGhyZWVwYXJ0dGFibGUgPSBUUlVFKQpgYGAKU2VlIG1vcmUgaHRtbCBkaXNwbGF5IG9wdGlvbnMgaGVyZSwgaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2thYmxlRXh0cmEvdmlnbmV0dGVzL2F3ZXNvbWVfdGFibGVfaW5faHRtbC5odG1sCgoKTm93IHdlIGNhbiBleHBvcnQgdGhpcyB0byBsYXRleAoKYGBge3IgZml4ZXN0LWV4cG9ydH0KbW9kZWxzdW1tYXJ5KAogIG1vZGVscywgIAogIHRpdGxlID0gIkZhbnRhc3RpYyByZWdyZXNzaW9uIHRhYmxlIiwgCiAgc3RhcnMgPSBUUlVFLAogIGdvZl9vbWl0ID0gIkFkanxQc2V1ZG98TG9nfEFJQ3xCSUN8RnxSMiIsIAogIGNvZWZfb21pdCA9IGMoIihJbnRlcmNlcHQpIiksIAogIGNvZWZfcmVuYW1lID0gYygiY2FyYXQiID0gIkNhcmF0IiwgCiAgICAgICAgICAgICAgICAgICJOdW0uT2JzLiIgPSAiTiIpLCAKICBvdXRwdXQgPSAnb3V0cHV0L3RhYmxlLnRleCcKICApCmBgYAoKIyMjIyBUYWJsZSB1c2luZyBgZXRhYmxlYAoKV2UgY2FuIGFsc28gdXNlIHRoZSBgZXRhYmxlYCBmdW5jdGlvbiB0aGF0IGlzIGEgcGFydCBvZiBgZml4ZXN0YCBwYWNrYWdlLiAKCmBgYHtyIGZpeGVzdC1ldGFibGUtbG9hZH0KbW9kZWxzX2Zvcl9ldGFibGUgPSBsaXN0KAogICAgIkZpeGVkLUVmZmVjdHMiID0gZmVfbW9kZWwKKQpgYGAKCmBgYHtyIGZpeGVzdC1ldGFibGUtZGlzcGxheX0KZXRhYmxlKG1vZGVsc19mb3JfZXRhYmxlKSAlPiUKICAgIGtibChjYXB0aW9uID0gIkZhbnRhc3RpYyByZWdyZXNzaW9uIHRhYmxlIikgJT4lCiAgICBrYWJsZV9zdHlsaW5nKGZ1bGxfd2lkdGggPSBGKQpgYGAKCj4gKipBc2lkZToqKiBTZWUgdGhpcyBleGNlbGxlbnQgYmxvZyBwb3N0IGJ5IFBhdHJpY2sgQmF5bGlzIG9uIGhvdyB0byAiZmluZS10dW5lIiB5b3VyIHRhYmxlcyB1c2luZyBgZXRhYmxlYCBmb3IgbGF0ZXggZXhwb3J0LiBJdCdzIHF1aXRlIHNpbXBsZSB0byBkbywgYnV0IHdpbGwgcmVxdWlyZSBhIGJpdCBvZiB0aW1lIHBlcmZlY3RpbmcgaXQgdG8geW91ciBleGFjdCBsaWtpbmcuIFdoaWxlIGl0J3Mgb3V0c2lkZSB0aGUgc2NvcGUgb2YgdGhpcyBwcmVzZW50YXRpb24sIGl0J3Mgc29tZSBoYXJkIGVhcm5lZCBrbm93bGVkZ2UgaGUncyBzaGFyaW5nIHRoYXQgc2hvdWxkIG1ha2UgeW91ciBsaWZlIGVhc2llci4gaHR0cHM6Ly93d3cucGF0cmlja2JheWxpcy5jb20vYmxvZy8yMDIxLTA1LTMwLW1ha2luZy10YWJsZXMvCgoKIyMgUGFja2FnZSBtYW5hZ2VtZW50CgpBIGtleSBmZWF0dXJlIG9mIFIgaXMgdGhhdCB5b3UnbGwgbmVlZCB0byBoYXZlIHBhY2thZ2VzIGluc3RhbGxlZCBhbmQgbG9hZCB0aGVtIGFzIG5lZWRlZC4gVGhpcyBjYW4gYmUgcGFpbmZ1bCB0byBrZWVwIHRyYWNrIG9mIGFjcm9zcyBtYWNoaW5lcyBhbmQgZXNwZWNpYWxseSBhY3Jvc3MgY29hdXRob3JzLiBJbiBhZGRpdGlvbiB0byBzaW1wbHkgaGF2aW5nIHRoZSBwYWNrYWdlIGluc3RhbGxlZCwgbWFraW5nIHN1cmUgeW91IGhhdmUgdGhlIHJpZ2h0ICp2ZXJzaW9uKiBvZiB0aGUgcGFja2FnZSBpcyBpbXBvcnRhbnQuIAoKIyMjIGBwYWNtYW5gCgpgcGFjbWFuYCBpcyBhIGZhbnRhc3RpY2FsbHkgbmFtZWQgcGFja2FnZSBtYW5hZ2VyLCBpdCBjYW4gaGFuZGxlIGJvdGggcGFja2FnZXMgb24gQ1JBTiBhbmQgcGFja2FnZXMgc3RvcmVkIG9ubHkgb24gcGxhY2VzIGxpa2UgZ2l0aHViLiBUaGUgYHBfbG9hZGAgZnVuY3Rpb24gd2lsbCBpbmNsdWRlIHRoZSBsaWJyYXJ5IGlmIHlvdSBoYXZlIGl0IGFuZCBpbnN0YWxsIGl0IGFuZCBpbmNsdWRlIGl0IGlmIHlvdSBkbyBub3QuIEZvciB0aGlzIGNvZGUgY2h1bmsgSSBoYXZlIGl0IHNldCB0byBub3QgZXZhbHVhdGUgc2luY2Ugd2UgcHJldmlvdXNseSBpbmNsdWRlZCBtYW55IG9mIHRoZXNlIGxpYnJhcmllcy4KCmBgYHtyLCBldmFsID0gRkFMU0V9CmlmICghcmVxdWlyZSgicGFjbWFuIikpIGluc3RhbGwucGFja2FnZXMoInBhY21hbiIpCnBhY21hbjo6cF9sb2FkKAogIGdncGxvdDIsIGRwbHlyLCBtb2RlbHN1bW1hcnksIGthYmxlRXh0cmEsIGZpeGVzdCwgZGV2dG9vbHMKKQpwYWNtYW46OnBfbG9hZF9naCgiaGVta2VuL1N0YXRhbWFya2Rvd24iKQpgYGAKClRoaXMgd2lsbCBpbnN0YWxsIGBwYWNtYW5gIGlmIGl0J3Mgbm90IGN1cnJlbnRseSBpbnN0YWxsZWQuIFRoaXMgY29kZSBhbHNvIGludHJvZHVjZXMgdXMgdG8gc29tZSBvdGhlciBjb2RpbmcgaW4gUi4gWW91IGNhbiBjYWxsIGEgZnVuY3Rpb24gZnJvbSBhIHBhY2thZ2UgdXNpbmcgdGhlIGA6OmAgc3ludGF4IHdoZXJlIHRoZSBwYWNrYWdlIG5hbWUgaXMgb24gdGhlIGxlZnQgYW5kIHRoZSBmdW5jdGlvbiBuYW1lIG9uIHRoZSByaWdodC4gVGhpcyBpcyBoZWxwZnVsIGlmIHRoZXJlIGFyZSB0d28gcGFja2FnZXMgdGhhdCBoYXZlIHRoZSBzYW1lIGZ1bmN0aW9uIG5hbWUgKGUuZy4sIHRoaXMgaGFwcGVucyB3aXRoIHRoZSBgc2VsZWN0YCBmdW5jdGlvbiBzb21ldGltZXMpLgoKIyMjIGByZW52YAoKYHJlbnZgIGhlbHBzIG1ha2Ugc3VyZSB0aGF0IGRpZmZlcmVuY2VzIGluIHBhY2thZ2UgdmVyc2lvbnMgd29uJ3QgcHJldmVudCB5b3UgZnJvbSBiZWluZyBhYmxlIHRvIHJlcGxpY2F0ZSB5b3VyIHJlc3VsdHMuIEVzc2VudGlhbGx5IGl0IHN0b3JlcyB0aGUgZXhhY3QgdmVyc2lvbiBvZiBlYWNoIHBhY2thZ2UgeW91IGFyZSB1c2luZyBpbiBhIHNhbmRib3hlZCBlbnZpcm9ubWVudC4gSXQgbWFrZXMgaXQgZWFzeSBmb3IgYW5vdGhlciBwZXJzb24gKG9yIGZ1dHVyZSB5b3UpIHRvIHVzZSB0aGUgZXhhY3Qgc2FtZSB2ZXJzaW9uIG9mIGVhY2ggcGFja2FnZS4gSXQgc2F2ZXMgdGhlc2UgaW4gYSBgcmVudi5sb2NrYCBmaWxlLiAgT25jZSB5b3UndmUgaW5zdGFsbGVkIGByZW52YCBhbmQgeW91J3ZlIG9wZW5lZCB1cCBhIG5ldyBSLXByb2plY3QsIGp1c3QgdHlwZSBgcmVudjo6aW5pdCgpYCBhbmQgeW91IHNob3VsZCBiZSBnb29kIHRvIGdvLiBgcmVudjo6c25hcHNob3QoKWAgd2lsbCB1cGRhdGUgY2hhbmdlcy4gCgpZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCBgcmVudmAgaGVyZTogaHR0cHM6Ly9yc3R1ZGlvLmdpdGh1Yi5pby9yZW52L2FydGljbGVzL3JlbnYuaHRtbAoKIyMgUnVubmluZyBzdGF0YSBmcm9tIFIKCi0gRmlyc3QgeW91J2xsIG5lZWQgdG8gbWFrZSBzdXJlIHRoYXQgeW91IGhhdmUgc3RhdGEgb24gdGhlIGNvbXB1dGVyIHlvdSdyZSB1c2luZy4gCi0gU2Vjb25kLCB5b3UnbGwgbmVlZCB0byBpbnN0YWxsIGBTdGF0YW1hcmtkb3duYAoKSGVyZSB5b3UnZCBsb2FkIHRoZSBwYWNrYWdlIGBkZXZ0b29sc2AsIHRoaXMgYWxsb3dzIHlvdSB0byBpbnN0YWxsIHBhY2thZ2VzIGZyb20gZ2l0aHViLiBJZiB5b3UncmUgdXNpbmcgYHBhY21hbmAgdGhlIGBwX2xvYWRfZ2hgIGZ1bmN0aW9uIGlzIGEgZ3JlYXQgd2F5IHRvIGxvYWQgdGhlc2UgcGFja2FnZXMuIFRoZW4gSSdsbCBpbnN0YWxsIHRoZSBgU3RhdGFtYXJrZG93bmAgcGFja2FnZS4gQWdhaW4sIG5vdCBuZWVkZWQgaWYgeW91J3ZlIGRvbmUgdGhpcyB1c2luZyBgcGFjbWFuYCBzbyBJJ3ZlIHNldCB0aGlzIGNvZGUgY2h1bmsgdG8gb25seSBzaG93IHRoZSBjb2RlIGFuZCBub3QgcnVuIGl0LgoKYGBge3IgbG9hZC1kZXZ0b29scywgZXZhbCA9IEZBTFNFfQpsaWJyYXJ5KCJkZXZ0b29scyIpCmluc3RhbGxfZ2l0aHViKCJoZW1rZW4vU3RhdGFtYXJrZG93biIpCmBgYAoKTG9hZCB0aGUgcGFja2FnZSBgU3RhdGFtYXJrZG93bmAsIGFnYWluIG5vdCBuZWVkZWQgaWYgdXNpbmcgYHBhY21hbmAuCgpgYGB7ciBzZXR1cC1zdGF0YSwgY2FjaGUgPSBGQUxTRX0KbGlicmFyeSgiU3RhdGFtYXJrZG93biIpCmBgYAoKVGhlbiB5b3UganVzdCBuZWVkIHRvIHR5cGUgdGhpcyBjb2RlIGNodW5rLgoKYGBgYApgYGB7c3RhdGF9YHIgJydgCnN5c3VzZSBhdXRvCnN1bW1hcml6ZQpgYGAKYGBgYAoKd2hpY2ggd2lsbCByZXN1bHQgaW4gdGhlIGZvbGxvd2luZyBvdXRwdXQuIAoKYGBge3N0YXRhLCBlY2hvID0gRkFMU0UsIGNhY2hlID0gRkFMU0V9CnN5c3VzZSBhdXRvCnN1bW1hcml6ZQpwd2QKCnNjYXR0ZXIgcHJpY2UgbXBnCmdyYXBoIGV4cG9ydCAib3V0cHV0L3NjYXR0ZXIucG5nIiwgcmVwbGFjZQpgYGAKCllvdSBjYW4gdGhlbiBwdXQgdGhlc2UgaW1hZ2UgaW4gdGhlIFJNYXJrZG93biBkb2N1bWVudC4gCiFbc2NhdHRlci1mcm9tLXN0YXRhXShvdXRwdXQvc2NhdHRlci5wbmcpCgo+ICoqTm90ZSoqOiBJZiB5b3UgcnVuIHRoaXMgb24geW91ciBjb21wdXRlciB0aGUgc2NhdHRlciB3aWxsIGxpa2VseSBub3QgbG9vayB0aGUgc2FtZS4gVGhlc2UgYXJlIG5vdCBkZWZhdWx0IHN0YXRhIGdyYXBoIHByZWZlcmVuY2VzLiBTZWUgW2JsaW5kc2NoZW1lc10oaHR0cHM6Ly9pZGVhcy5yZXBlYy5vcmcvYy9ib2MvYm9jb2RlL3M0NTgyNTEuaHRtbCkgd2l0aCB0aGUgc3Vib3B0aW9uIGBwbG90cGxhaW5ibGluZGAgaWYgeW91IGxpa2UgdGhpcyBmb3JtYXR0aW5nLiAKCj4gKipBc2lkZSoqOiBJdCdzIHBvc3NpYmxlIHRvIGRvIHRoaXMgd2l0aG91dCB0aGUgYFN0YXRhbWFya2Rvd25gIHBhY2thZ2UsIGJ1dCBJIHN0cnVnZ2VsZWQgdG8gZ2V0IGl0IHRvIHdvcmssIHBvc3NpYmx5IGJlY2F1c2UgSSBoYXZlIFN0YXRhLU1QLiBZb3UgY2FuIHNlZSB0aGlzIGxpbmsgZm9yIG1vcmUgZGV0YWlsczogIGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi1jb29rYm9vay9lbmctc3RhdGEuaHRtbCBGb3IgbW9yZSBpbmZvcm1hdGlvbiBvbiBgU3RhdGFtYXJrZG93bmAgc2VlIGhlcmU6IGh0dHBzOi8vd3d3LnNzYy53aXNjLmVkdS9+aGVta2VuL1N0YXRhd29ya3Nob3BzL1N0YXRhJTIwYW5kJTIwUiUyME1hcmtkb3duL1N0YXRhRW5naW5lUGF0aC5odG1sCgoKCiMjIyBNZXRhOiBVc2UgUiB0byBjYWxsIHN0YXRhIHRvIGNhbGwgUgoKU2F5IHRoYXQgeW91IGhhdmUgYW4gUi1zY3JpcHQgY2FsbGVkIGB0ZXN0LlJgLiBUaGlzIHRlc3Qgc2NyaXB0IGp1c3Qgc2hvd3MgYHNpbigzKWAgYW5kIGlzIHNhdmVkIGluIHRoZSBzYW1lIGZvbGRlciBhcyB0aGlzIGAuUm1kYCBmaWxlLiAKCmBgYGAKYGBge3N0YXRhfWByICcnYApzaGVsbCAvdXNyL2xvY2FsL2Jpbi9SIC0tdmFuaWxsYSA8dGVzdC5SCmBgYApgYGBgCgpgYGB7c3RhdGEsIGVjaG8gPSBGQUxTRSwgY2FjaGUgPSBGQUxTRX0Kc2hlbGwgL3Vzci9sb2NhbC9iaW4vUiAtLXZhbmlsbGEgPHRlc3QuUgpgYGAKCiMjIE1pc2MuIHRpcHMKCgojIyMgVXNlIFIgcHJvamVjdHMKCklmIHlvdSB0YWtlIGEgbG9vayBhdCB0aGUgcGFyZW50IGRpcmVjdG9yeSAocmVwbykgY29udGFpbmluZyB0aGlzIGRvY3VtZW50LCB5b3UnbGwgc2VlIGEgZmlsZSBjYWxsZWQgYGVoZWMtaW50cm8tdG8tci5ScHJvamAuIFRoZSBgLlJwcm9qYCBkZW5vdGVzIGFuICJSIHByb2plY3QiIGZpbGUuIAoKV2hhdCBpcyB0aGlzIGFuZCB3aHkgaXMgaXQgdXNlZnVsPyBUaGluayBvZiBgLlJwcm9qYCBmaWxlcyBhcyBzcGVjaWZ5aW5nIHRoZSByb290IG9mIGEgYnVuY2ggb2YgcmVsYXRlZCBmaWxlcy4gQW1vbmcgb3RoZXIgdGhpbmdzLCB0aGlzIGhlbHBzIHRvIGtlZXAgeW91ciBwcm9qZWN0cyBvcmdhbmlzZWQgYW5kIGVyZ29ub21pYy4gRm9yIGV4YW1wbGUsIHlvdSBjYW4gb3BlbiBhIGdyb3VwIG9mIHJlbGF0ZWQgZmlsZXMgaW4gUlN0dWRpbyBzaW1wbHkgYnkgY2xpY2tpbmcgdGhlIGAuUnByb2pgIGZpbGUuIEl0IGFsc28gbWVhbnMgdGhhdCB5b3UgbmV2ZXIgaGF2ZSB0byB3b3JyeSBhYm91dCBzcGVjaWZ5aW5nIGFic29sdXRlIHBhdGhzIHdoZW4gcmVhZGluZyBvciB3cml0aW5nIGZpbGVzLiBZb3UgbmVlZCBvbmx5IHNwZWNpZnkgX3JlbGF0aXZlXyBwYXRocywgc2luY2UgUiBrbm93cyB0aGF0IHRoZSBwcm9qZWN0IGZpbGUgYWN0cyBhcyBhIGJhc2UgKHJvb3QpIGZvciBldmVyeXRoaW5nIGVsc2UuCgpSIFByb2plY3RzIGFyZSBqdXN0IGFzIGVhc3kgdG8gY3JlYXRlIGFzIHJlZ3VsYXIgUiBzY3JpcHRzIG9yIFJtZCBmaWxlcy4gWW91IGNhbiB1c2UgdGhlbSB0byBzdGFydCBuZXcgcHJvamVjdHMgb3IgYXNzb2NpYXRlIHdpdGggYW4gZXhpc3RpbmcgZGlyZWN0b3J5LiBSZWFkIG1vcmUgYWJvdXQgdGhlbSBbaGVyZV0oaHR0cHM6Ly9zdXBwb3J0LnJzdHVkaW8uY29tL2hjL2VuLXVzL2FydGljbGVzLzIwMDUyNjIwNy1Vc2luZy1SU3R1ZGlvLVByb2plY3RzKS4KCiMjIyBDbGVhciBtZW1vcnkKCllvdSBjYW4gcmVtb3ZlIG9iamVjdHMgYXQgYW55IHRpbWUgdXNpbmcgdGhlIGBybSgpYCBmdW5jdGlvbi4gRm9yIGV4YW1wbGUsIGBybShiYXNlX21vZGVsKWAgd2lsbCByZW1vdmUgb3VyIHNpbXBsZSBPTFMgbW9kZWwgZnJvbSBlYXJsaWVyLiBUbyBjb21wbGV0ZWx5IGNsZWFyIHlvdXIgbWVtb3J5IGFuZCByZW1vdmUgYWxsIG9iamVjdHMsIGl0J3MgW2Jlc3RdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvYmxvZy8yMDE3LzEyL3dvcmtmbG93LXZzLXNjcmlwdC8pIHRvIHNpbXBseSByZXN0YXJ0IHlvdXIgUiBzZXNzaW9uLiBJbiBSU3R1ZGlvLCBnbyB0byBgU2Vzc2lvbiA+IFJlc3RhcnQgUmAuIChPciB1c2UgdGhlIGtleWJvYXJkIHNob3J0Y3V0OiBgQ3RybCArIFNoaWZ0ICsgRjEwYC4pCgojIyMgUmVhZGluZyBhbmQgd3JpdGluZyBkYXRhCgotIFdoZW5ldmVyIHBvc3NpYmxlLCBJIHRyeSB0byB1c2UgdGhlIGBkYXRhLnRhYmxlYCBmdW5jdGlvbnMgYGZyZWFkYCBhbmQgYGZ3cml0ZWAuIFRoZXNlIHdvcmsgcmVhbGx5IHdlbGwgZm9yIGNzdiBkYXRhLgoKRm9yIGV4YW1wbGUsIGxldCdzIHNhdmUgdGhlIGRpYW1vbmRzIGRhdGFzZXQgc28gd2UgY2FuIGltcG9ydCBpdCBpbiBzdGF0YSBmb3IgYSBsaXR0bGUgZXhhbXBsZSBsYXRlciBvbi4KCmBgYHtyIHNhdmUtZGlhbW9uZHN9CmxpYnJhcnkoZGF0YS50YWJsZSkKZndyaXRlKGRpYW1vbmRzLCBmaWxlID0gImRhdGEvZGlhbW9uZHMuY3N2IikKYGBgCgpgZGF0YS50YWJsZWAgaXMgKipyZWFsbHkqKiAqKnJlYWxseSoqIGdvb2Qgd2l0aCBiaWcgZGF0YXNldHMuICBDaGVjayBvdXQgdGhpcyB3YWxrdGhyb3VnaDogaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2RhdGEudGFibGUvdmlnbmV0dGVzL2RhdGF0YWJsZS1pbnRyby5odG1sCgpgYGB7ciBzYXZlLWRpYW1vbmRzLWR0YX0KbGlicmFyeShoYXZlbikKaGF2ZW46OndyaXRlX2R0YShkaWFtb25kcywgcGF0aCA9ICJkYXRhL2RpYW1vbmRzLmR0YSIsIHZlcnNpb24gPSAxNCkKYGBgCgo+ICoqTm90ZSoqOiBSZWFkaW5nIGFuZCB3cml0aW5nIGRhdGEgaW4gb3RoZXIgZm9ybWF0cyBpcyByZWFsbHkgZWFzeSB3aXRoIHRoZSBgaGF2ZW5gIHBhY2thZ2UuIFRoaXMgY2FuIGFsc28gaGFuZGxlLCB3YWl0IGZvciBpdCwgU0FTIGRhdGFzZXRzIHJlYWxseSBlYXNpbHkuIAoKLSBZb3UgY2FuIHJlYWQgYW5kIHdyaXRlIGV4Y2VsIGRhdGEgd2l0aCBgcmVhZHhsYCBwYWNrYWdlLCB3aGljaCBpcyBhIHBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgCgoKV2UgY2FuIHNob3cgdGhhdCB0aGlzIHdvcmtlZCBieSB1c2luZyB0aGUgYFN0YXRhbWFya2Rvd25gIHBhY2thZ2UuIAoKYGBge3N0YXRhLCBjYWNoZSA9IEZBTFNFfQovLyBVc2UgdGhlIGNzdiBmaWxlIHNhdmVkIGJ5IGZ3cml0ZSBpbiBSCmltcG9ydCBkZWxpbWl0ZWQgZGF0YS9kaWFtb25kcy5jc3YsIGNsZWFyCgp0d293YXkgLy8vCiAgfHwgc2NhdHRlciBwcmljZSBjYXJhdCwgLy8vCiAgdGl0bGUoIkJpZ2dlciBkaWFtb25kcyBjb3N0IG1vcmUtIHVzaW5nIGZ3cml0ZSB0byBjc3YiLCBwb3MoMTEpKSAvLy8KICBzdWJ0aXRsZSgiUHJpY2UiLCBwb3MoMTEpKSAvLy8KICB5dGl0bGUoIiIpIC8vLwogIHh0aXRsZSgiQ2FyYXQiKSAvLy8KICB5bGEoLG5vZ3JpZCBub3RpY2spIC8vLwogIHhsYSgsbm9ncmlkIG5vdGljaykKICAKZ3JhcGggZXhwb3J0ICJvdXRwdXQvc2NhdHRlci1kaWFtb25kcy11c2luZy1jc3YucG5nIiwgcmVwbGFjZQoKLy8gVXNlIHRoZSBkdGEgZmlsZSBzYXZlZCBieSBmd3JpdGUgaW4gUgp1c2UgZGF0YS9kaWFtb25kcy5kdGEsIGNsZWFyCgp0d293YXkgLy8vCiAgfHwgc2NhdHRlciBwcmljZSBjYXJhdCwgLy8vCiAgdGl0bGUoIkJpZ2dlciBkaWFtb25kcyBjb3N0IG1vcmUtIHVzaW5nIHdyaXRlX2R0YSB0byBkdGEiLCBwb3MoMTEpKSAvLy8KICBzdWJ0aXRsZSgiUHJpY2UiLCBwb3MoMTEpKSAvLy8KICB5dGl0bGUoIiIpIC8vLwogIHh0aXRsZSgiQ2FyYXQiKSAvLy8KICB5bGEoLG5vZ3JpZCBub3RpY2spIC8vLwogIHhsYSgsbm9ncmlkIG5vdGljaykKCmdyYXBoIGV4cG9ydCAib3V0cHV0L3NjYXR0ZXItZGlhbW9uZHMtdXNpbmctZHRhLnBuZyIsIHJlcGxhY2UKYGBgCgpZb3UgY2FuIHRoZW4gcHV0IHRoZXNlIGltYWdlIGluIHRoZSBSTWFya2Rvd24gZG9jdW1lbnQuIAoKIVtzY2F0dGVyLWZyb20tc3RhdGEtY3N2XShvdXRwdXQvc2NhdHRlci1kaWFtb25kcy11c2luZy1jc3YucG5nKQohW3NjYXR0ZXItZnJvbS1zdGF0YS1kdGFdKG91dHB1dC9zY2F0dGVyLWRpYW1vbmRzLXVzaW5nLWR0YS5wbmcpCgoKIyMjIENvbGxhcHNpbmcgZGF0YQoKVGhlIGBjb2xsYXBzZWAgcGFja2FnZSBpcyBhbWF6aW5nLiBJdCdzIGNyYXp5IGZhc3QgdG9vLiBgY29sbGFwc2VgIHBsdXMgYGRhdGEudGFibGVgIG1ha2UgY3JlYXRpbmcgYW5hbHlpdGljIGZpbGVzIGZyb20gbGFyZ2UgbWljcm9kYXRhIGEgYnJlZXplLiAKCiMjIyBSZXNoYXBpbmcgZGF0YS4gCgpgcGl2b3Rfd2lkZXJgIGFuZCBgcGl2b3RfbG9uZ2VyYCBhcmUgYm90aCBhIHBhcnQgb2YgdGhlIGB0aWR5dmVyc2VgLiBSZWFkIG1vcmUgYWJvdXQgdGhlbSBoZXJlLiBodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcvYXJ0aWNsZXMvcGl2b3QuaHRtbAoK