hillali@ohsu.edu
@apreshill
@apreshill
OHSU Center for Spoken Language Understanding

Overview

This workshop was presented at OHSU on June 24, 2016. The same dataset and plots were made for a parallel breakout session using Python.

Dataset and inspiration

For this workshop, we’ll recreate the graphics presented in this article:

Graphics and statistics for cardiology: comparing categorical and continuous variables

Sometimes we’ll offer a challenge for you to try on your own (either during or after this workshop), which will look like this:

If this was easy:
Try this!

Other times, we’ll encourage you to pair up with a partner to solve a problem, which will look like this:

Pair up
Work together on this!

Pre-work

Your pre-work included:

  • Install R
  • Install RStudio
  • Install the packages listed below, and load them in your current work session
  • Importing the dataset into your current work session

Import data

Use the readr package to import the nhanes_large.csv with the read_csv function.

  • The first argument in the read_csv() parentheses is the url to the dataset
  • The second argument, na = ".", specifies that missing data in this dataset is denoted by a period
  • Finally, assign the data to an R object using <- and call that object something simple, like heart.

Basics

HLO

It is good practice to say HLO to your new dataset by doing a High-Level Overview using some built-in R functions.

Use the head() function in your console and check with your partner to see if you got the same output. How many rows of data do you see?

If this was easy:
Type ?head in your console and figure out how to reveal the first 10 rows. Search online to find out the R function that returns the last n rows of a dataframe.

Let’s look at the variables in our heart dataframe:

 [1] "BPXSAR"    "BPXDAR"    "BPXDI1"    "BPXDI2"    "race_ethc"
 [6] "gender"    "DR1TFOLA"  "RIAGENDR"  "BMXBMI"    "RIDAGEYR" 

You may be wondering what DR1TFOLA and BPXSAR are. The data dictionary online defines the variable names as follows:

  • BPXSAR: systolic blood pressure (mmHg)
  • BPXDAR: diastolic blood pressure (mmHg)
  • BPXDI1, BPXDI2: two diastolic blood pressure readings
  • race_ethc: race/ethnicity, coded as Hispanic, White non-Hispanic, Black non-Hispanic and Other
  • gender: sex, coded as Male/Female
  • DR1TFOLA: folate intake (μg/day)
  • RIAGENDR: sex, coded as 1/2
  • BMXBMI: body mass index (kg/m2)
  • RIDAGEY: age (years)

All functions have arguments

We just used 2 different functions in R: head() and names(). All functions take arguments, which typically go inside parentheses. Order matters here, unless you explicitly label the arguments; for example:

For both head() and names(), the first argument was our dataframe, which we named heart. ggplot2 is a package that essentially contains a bunch of functions, each taking different arguments, which we’ll explore now…

Graphs for display of single samples

Let’s focus first on univariate plots by examining folate intake (a continuous variable). The first function we need is ggplot(): this function initializes a new ggplot object.

We’ll start with 1 argument: your dataframe- we called ours heart.

The code above should produce an empty plot (a gray rectangle).

ggplot makes a plot by layering. So let’s take this empty plot and add a layer to it. This layer is a geometric object, or geom for short. Let’s start with the geom_histogram().

Each geom also takes arguments, and you can always find out what arguments go along with a specific geom using the help function in the console:

Histograms

We’ll take that empty plot and add a geom_histogram() layer using the + sign. This geom requires at least one argument, and it is special: it is an aesthestic mapping (aes()), meaning that what goes in the parentheses here must be a variable that is in our dataframe. Here, we map folate intake onto the x-axis.

Small tweak #1: change colour outline of bars

One of the arguments that geom_histogram() takes is colour- we’ll change it to white. Note that we are outside of the aes()- this is because the color “white” is not a variable.

Small tweak #2: change colour fill of bars

You might be surprised to see that colour does not in fact relate to the colour inside the bars (see above). To change that, we use another argument for geom_histogram()- fill. How colour versus fill works depends on which geom you are using. For our histogram, we’ll change the the colour inside to orchid.

If this was easy:
Make a histogram with royalblue bars and a hotpink outline.

Small tweak #3: change number of bins

You may have noticed that all of our histograms have printed this warning in your console:

#stat_bin() using bins = 30. Pick better value with binwidth.

This warning tells us that bins = 30 is the default that ggplot chose for us, based on our data. Let’s play around with this!

Pair up

Make a histogram with 10 bins, and one with 50 bins. Color/fill any way you like!

Small tweak #3: update x-axis title

This is done by adding a labs layer, short for labels. We want to relabel the x-axis. Let’s also save our ggplot object now, and call it myhist.

I’m pretty happy with this one, so I’m going to save this plot so I can stop typing the same thing over and over again, and I can add layers using this one as my base plot.

Graphs comparing two variables: continuous vs. categorical

OK, let’s add another variable to the mix. We’ll keep looking at folate intake as the continuous variable, with gender as the categorical variable. There are a few ways to do this in ggplot2.

  • Facets
  • Colors
  • Side-by-side plots (along the x-axis)

Facets

About facets (from facet_wrap documentation):

“Most displays are roughly rectangular, so if you have a categorical variable with many levels, it doesn’t make sense to try and display them all in one row (or one column). To solve this dilemma, facet_wrap wraps a 1d sequence of panels into 2d, making best use of screen real estate.”

Let’s add + facet_wrap(~variable) to split the histograms based on gender

Just as a reminder, the below code would create the exact same plot- just with more repetitive typing.

If this was easy:
Make the same facetted plot using the kernel density geom (geom_density) instead of geom_histogram.

Colours

So far, when we played with colours, we set fill/colour without mapping them onto another variable (i.e., (colour = "white", fill = "orchid")). Now, we want fill/colour to vary based on the value of a specific variable. Here we want colour to depend on gender, so we map the colour aesthetic (aes(colour = variable)) onto the variable gender.

Notice moving the colour into aesthetics parentheses.

The below code will produce the exact same plot. Try it! I promise it is identical. Play around with changing global aesthetics (like below) versus geom-specific aesthetics (like above). It won’t matter when you only have one layer, but once you start adding geoms and stats, this can be a powerful way to change your visualization.

Side-by-side plots

Now let’s make some side-by-side univariate plots, specifically dotcharts or stripcharts, to visualize systolic blood pressure by gender. We’ll start with a new base plot, and save it to an object called side that we can then add geoms to.

Pair up

If you type side into your console, what will the plot look like? Try adding a geom_point()- is this plot useful?

Let’s take this plot and tweak it a bit.

Small tweak #2: make points more transparent

Alpha works on a scale from 0 (transparent) to 1 (opaque).

Small tweak #3: try jittering the points

To do this, instead of geom_point(), we will switch to using geom_jitter(), which automatically adds both vertical and horizontal space (noise) to your datapoints.

Small tweak #4: control the jitter

Sometimes you may only want to change the width of jitter but not the height.

Now let’s try a different geom in our side-by-side plot, which is available through the beeswarm package you already loaded.

Small tweak #1: add alpha again

Small tweak #2: add statistics

We’ll include the mean plus 95% CI

Small tweak #3: add another geom layer

Try adding geom_boxplot on top of your side-by-side beeswarm plot. Also try re-ordering the geoms to see what changes.

New geom time- this time use geom_violin.

Small tweak #1: add statistics

Let’s include the sample mean and median

Small tweak #2: add another geom layer

Add another layer to your violin plot: try geom_boxplot

Graphs comparing two variables: continuous vs. continuous

Now we’ll create some bivariate plots to look at the association between age and systolic blood pressure, both of which are continuous variables.

Graphs illustrating more than two variables

We’ll finish up by creating some multivariable plots that help us visualize how the association between body mass index & systolic BP varies by gender and age (so 4 variables!).

Just copy this code to create a categorical age variable:

Recreate theirs first

Try with facet grid, update labels

Play with colors!



Since R Markdown use the bootstrap framework under the hood. It is possible to benefit its powerful grid system. Basically, you can consider that your row is divided in 12 subunits of same width. You can then choose to use only a few of this subunits.



Here, I use 3 subunits of size 4 (4x3=12). The last column is used for a plot. You can read more about the grid system here. I got this result showing the following code in my R Markdown document.

LS0tCnRpdGxlOiAiT0hTVSBIYW5kcy1vbiBEYXRhIEphbWJvcmVlOiBDb2RlIFlvdXIgR3JhcGgiCnN1YnRpdGxlOiAiQSBXb3Jrc2hvcCBvbiBWaXN1YWxpemluZyBZb3VyIERhdGEgd2l0aCBgZ2dwbG90YCIKYXV0aG9yOiAiQWxpc29uIFByZXNtYW5lcyBIaWxsICYgSnVsaWFubmUgTXllcnMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY3NzOiBuaGFuZXNfc3R5bGUuY3NzCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICB0aGVtZTogZmxhdGx5CiAgICBzbWFydDogZmFsc2UKICAgIHRvYzogVFJVRQogICAgdG9jX2Zsb2F0OgogICAgICBjb2xsYXBzZWQ6IFRSVUUKICAgICAgc21vb3RoX3Njcm9sbDogVFJVRQogICAgdG9jX2RlcHRoOiAyCiAgICBjb2RlX2Rvd25sb2FkOiBUUlVFCi0tLQoKPHNjcmlwdCBzcmM9Imh0dHBzOi8vdXNlLmZvbnRhd2Vzb21lLmNvbS81MjM1MDg1YjE1LmpzIj48L3NjcmlwdD4KCjxhIGhyZWY9Im1haWx0bzpoaWxsYWxpQG9oc3UuZWR1Ij48aSBjbGFzcz0iZmEgZmEtcGFwZXItcGxhbmUgZmEtZnciPjwvaT5oaWxsYWxpQG9oc3UuZWR1PC9hPjxicj4KPGEgaHJlZj0iaHR0cDovL3R3aXR0ZXIuY29tL2FwcmVzaGlsbCI+PGkgY2xhc3M9ImZhIGZhLXR3aXR0ZXIgZmEtZnciPjwvaT5AYXByZXNoaWxsPC9hPjxicj4KPGEgaHJlZj0iaHR0cDovL2dpdGh1Yi5jb20vYXByZXNoaWxsIj48aSBjbGFzcz0iZmEgZmEtZ2l0aHViIGZhLWZ3Ij48L2k+QGFwcmVzaGlsbDwvYT48YnI+CjxhIGhyZWY9Imh0dHA6Ly9jc2x1Lm9oc3UuZWR1Ij48aSBjbGFzcz0iZmEgZmEtbWFwLW1hcmtlciBmYS1mdyI+PC9pPk9IU1UgQ2VudGVyIGZvciBTcG9rZW4gTGFuZ3VhZ2UgVW5kZXJzdGFuZGluZzwvYT4KCmBgYHtyIHNldHVwX25oYW5lcywgaW5jbHVkZSA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZXJyb3IgPSBUUlVFLCBjb21tZW50ID0gTkEsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFLCB0aWR5ID0gRkFMU0UsIGZpZy5wYXRoPSJuaGFuZXMtZmlncy8iLCBlY2hvID0gVFJVRSkKYGBgCgpgYGB7ciBsb2FkX3BhY2thZ2VzX25oYW5lcywgaW5jbHVkZSA9IEZBTFNFfQpzdXBwcmVzc1dhcm5pbmdzKHN1cHByZXNzTWVzc2FnZXMobGlicmFyeShyZWFkcikpKQpzdXBwcmVzc1dhcm5pbmdzKHN1cHByZXNzTWVzc2FnZXMobGlicmFyeShkcGx5cikpKQpzdXBwcmVzc1dhcm5pbmdzKHN1cHByZXNzTWVzc2FnZXMobGlicmFyeShNQVNTKSkpCnN1cHByZXNzV2FybmluZ3Moc3VwcHJlc3NNZXNzYWdlcyhsaWJyYXJ5KGdncGxvdDIpKSkKc3VwcHJlc3NXYXJuaW5ncyhzdXBwcmVzc01lc3NhZ2VzKGxpYnJhcnkoZ2diZWVzd2FybSkpKQpzdXBwcmVzc1dhcm5pbmdzKHN1cHByZXNzTWVzc2FnZXMobGlicmFyeShnZ2FsdCkpKQpgYGAKIAojIE92ZXJ2aWV3CgoKVGhpcyB3b3Jrc2hvcCB3YXMgcHJlc2VudGVkIGF0IE9IU1Ugb24gSnVuZSAyNCwgMjAxNi4gVGhlIHNhbWUgZGF0YXNldCBhbmQgcGxvdHMgd2VyZSBtYWRlIGZvciBhIFtwYXJhbGxlbCBicmVha291dCBzZXNzaW9uIHVzaW5nIFB5dGhvbl0oaHR0cHM6Ly9naXRodWIuY29tL2FiYWx0ZXIvZGF0YS12aXotamFtYm9yZWUpLgoKCgoKIyMgRGF0YXNldCBhbmQgaW5zcGlyYXRpb24KCkZvciB0aGlzIHdvcmtzaG9wLCB3ZSdsbCByZWNyZWF0ZSB0aGUgZ3JhcGhpY3MgcHJlc2VudGVkIGluIHRoaXMgYXJ0aWNsZToKCltHcmFwaGljcyBhbmQgc3RhdGlzdGljcyBmb3IgY2FyZGlvbG9neTogY29tcGFyaW5nIGNhdGVnb3JpY2FsIGFuZCBjb250aW51b3VzIHZhcmlhYmxlc10oaHR0cDovL2hlYXJ0LmJtai5jb20vY29udGVudC9lYXJseS8yMDE2LzAxLzI3L2hlYXJ0am5sLTIwMTUtMzA4MTA0LmZ1bGwpIAoKKiBfQXV0aG9yczpfIEtlbm5ldGggUmljZSwgVGhvbWFzIEx1bWxleSAgCiogW0Z1bGwgdGV4dF0oaHR0cDovL2ZhY3VsdHkud2FzaGluZ3Rvbi5lZHUva2VucmljZS9oZWFydGdyYXBocy9lZmZlY3RpdmVncmFwaHMucGRmKQoqIFtEYXRhc2V0cyB1c2VkXShodHRwOi8vZmFjdWx0eS53YXNoaW5ndG9uLmVkdS9rZW5yaWNlL2hlYXJ0Z3JhcGhzLykKICAgIC0gVGhlIGxpbmtzIHRvIHRoZSBtZWRpdW0gYW5kIGxhcmdlIGRhdGFzZXRzIG1heSBiZSB3cm9uZy0gcmVtb3ZlIHRoZSBzZWNvbmQgInMiIGluIHRoZSB1cmxzCiAgICAtIERhdGFzZXRzIGFyZSBhbHNvIGluIG91ciBnaXRodWIgcmVwbyBmb3IgdGhpcyB3b3Jrc2hvcAoqIERhdGEgZGVyaXZlZCBmcm9tIFtOSEFORVMgKE5hdGlvbmFsIEhlYWx0aCBhbmQgTnV0cml0aW9uIEV4YW1pbmF0aW9uIFN1cnZleSldKGh0dHA6Ly93d3cuY2RjLmdvdi9uY2hzL25oYW5lcy8pCiogW0FkZGl0aW9uYWwgbWF0ZXJpYWxzXShodHRwOi8vZmFjdWx0eS53YXNoaW5ndG9uLmVkdS9rZW5yaWNlL2hlYXJ0Z3JhcGhzLykKICAgCgpTb21ldGltZXMgd2UnbGwgb2ZmZXIgYSBjaGFsbGVuZ2UgZm9yIHlvdSB0byB0cnkgb24geW91ciBvd24gKGVpdGhlciBkdXJpbmcgb3IgYWZ0ZXIgdGhpcyB3b3Jrc2hvcCksIHdoaWNoIHdpbGwgbG9vayBsaWtlIHRoaXM6Cgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1wcmltYXJ5Ij4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj48aSBjbGFzcz0iZmEgZmEtaGFuZC1vLXJpZ2h0IiBhcmlhLWhpZGRlbj0idHJ1ZSI+PC9pPiBJZiB0aGlzIHdhcyBlYXN5OjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpUcnkgdGhpcyEKICA8L2Rpdj4KPC9kaXY+CgpPdGhlciB0aW1lcywgd2UnbGwgZW5jb3VyYWdlIHlvdSB0byBwYWlyIHVwIHdpdGggYSBwYXJ0bmVyIHRvIHNvbHZlIGEgcHJvYmxlbSwgd2hpY2ggd2lsbCBsb29rIGxpa2UgdGhpczoKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPjxpIGNsYXNzPSJmYSBmYS1oYW5kc2hha2UtbyIgYXJpYS1oaWRkZW49InRydWUiPjwvaT4gUGFpciB1cDwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpXb3JrIHRvZ2V0aGVyIG9uIHRoaXMhCiAgPC9kaXY+CjwvZGl2PgoKCgojIFByZS13b3JrCgpZb3VyIHByZS13b3JrIGluY2x1ZGVkOgoKKiBJbnN0YWxsIFIKKiBJbnN0YWxsIFJTdHVkaW8KKiBJbnN0YWxsIHRoZSBwYWNrYWdlcyBsaXN0ZWQgYmVsb3csIGFuZCBsb2FkIHRoZW0gaW4geW91ciBjdXJyZW50IHdvcmsgc2Vzc2lvbgoqIEltcG9ydGluZyB0aGUgZGF0YXNldCBpbnRvIHlvdXIgY3VycmVudCB3b3JrIHNlc3Npb24KCgoKIyMgSW5zdGFsbCBwYWNrYWdlcyAKCkRvIHRoaXMgb25jZSBwZXIgbWFjaGluZS4KCmBgYHtyIGluc3RhbGxfcGFja2FnZXNfZGVtbywgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygicmVhZHIiKQppbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikKaW5zdGFsbC5wYWNrYWdlcygiZ2diZWVzd2FybSIpIAppbnN0YWxsLnBhY2thZ2VzKCJkcGx5ciIpCmluc3RhbGwucGFja2FnZXMoIk1BU1MiKQppbnN0YWxsLnBhY2thZ2VzKCJoZXhiaW4iKQppbnN0YWxsLnBhY2thZ2VzKCJnZ2FsdCIpCmBgYAoKIyMgTG9hZCBwYWNrYWdlcyAKCkRvIHRoaXMgb25jZSBwZXIgUiBzZXNzaW9uLgoKYGBge3IgbG9hZF9wYWNrYWdlc19kZW1vLCBldmFsID0gRkFMU0V9CmxpYnJhcnkocmVhZHIpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShnZ2JlZXN3YXJtKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KE1BU1MpCmxpYnJhcnkoaGV4YmluKQpsaWJyYXJ5KGdnYWx0KQpgYGAKCiMjIEltcG9ydCBkYXRhIAoKVXNlIHRoZSBgcmVhZHJgIHBhY2thZ2UgdG8gaW1wb3J0IHRoZSBuaGFuZXNfbGFyZ2UuY3N2IHdpdGggdGhlIGByZWFkX2NzdmAgZnVuY3Rpb24uCgoqIFRoZSBmaXJzdCBhcmd1bWVudCBpbiB0aGUgYHJlYWRfY3N2KClgIHBhcmVudGhlc2VzIGlzIHRoZSB1cmwgdG8gdGhlIGRhdGFzZXQKKiBUaGUgc2Vjb25kIGFyZ3VtZW50LCBgbmEgPSAiLiJgLCBzcGVjaWZpZXMgdGhhdCBtaXNzaW5nIGRhdGEgaW4gdGhpcyBkYXRhc2V0IGlzIGRlbm90ZWQgYnkgYSBwZXJpb2QKKiBGaW5hbGx5LCBhc3NpZ24gdGhlIGRhdGEgdG8gYW4gUiBvYmplY3QgdXNpbmcgYDwtYCBhbmQgY2FsbCB0aGF0IG9iamVjdCBzb21ldGhpbmcgc2ltcGxlLCBsaWtlIGBoZWFydGAuCgoKYGBge3IgbmhhbmVzfQpoZWFydCA8LSByZWFkX2NzdigiaHR0cDovL2ZhY3VsdHkud2FzaGluZ3Rvbi5lZHUva2VucmljZS9oZWFydGdyYXBocy9uaGFuZXNsYXJnZS5jc3YiLCBuYSA9ICIuIikgCmBgYAoKCgoKCiMgQmFzaWNzCgojIyBITE8KCkl0IGlzIGdvb2QgcHJhY3RpY2UgdG8gc2F5IEhMTyB0byB5b3VyIG5ldyBkYXRhc2V0IGJ5IGRvaW5nIGEgSGlnaC1MZXZlbCBPdmVydmlldyB1c2luZyBzb21lIGJ1aWx0LWluIFIgZnVuY3Rpb25zLiAKClVzZSB0aGUgYGhlYWQoKWAgZnVuY3Rpb24gaW4geW91ciBjb25zb2xlIGFuZCBjaGVjayB3aXRoIHlvdXIgcGFydG5lciB0byBzZWUgaWYgeW91IGdvdCB0aGUgc2FtZSBvdXRwdXQuIEhvdyBtYW55IHJvd3Mgb2YgZGF0YSBkbyB5b3Ugc2VlPyAKCmBgYHtyIGhlYWQsIGV2YWw9RkFMU0V9CmhlYWQoaGVhcnQpCmBgYAoKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXByaW1hcnkiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPjxpIGNsYXNzPSJmYSBmYS1oYW5kLW8tcmlnaHQiIGFyaWEtaGlkZGVuPSJ0cnVlIj48L2k+IElmIHRoaXMgd2FzIGVhc3k6PC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+ClR5cGUgYD9oZWFkYCBpbiB5b3VyIGNvbnNvbGUgYW5kIGZpZ3VyZSBvdXQgaG93IHRvIHJldmVhbCB0aGUgZmlyc3QgMTAgcm93cy4gU2VhcmNoIG9ubGluZSB0byBmaW5kIG91dCB0aGUgUiBmdW5jdGlvbiB0aGF0IHJldHVybnMgdGhlICoqbGFzdCoqICpuKiByb3dzIG9mIGEgZGF0YWZyYW1lLgogIDwvZGl2Pgo8L2Rpdj4KCkxldCdzIGxvb2sgYXQgdGhlIHZhcmlhYmxlcyBpbiBvdXIgYGhlYXJ0YCBkYXRhZnJhbWU6CgpgYGB7ciB2YXJpYWJsZXN9Cm5hbWVzKGhlYXJ0KQpgYGAKCllvdSBtYXkgYmUgd29uZGVyaW5nIHdoYXQgYERSMVRGT0xBYCBhbmQgYEJQWFNBUmAgYXJlLiBUaGUgW2RhdGEgZGljdGlvbmFyeSBvbmxpbmVdKGh0dHA6Ly9mYWN1bHR5Lndhc2hpbmd0b24uZWR1L2tlbnJpY2UvaGVhcnRncmFwaHMvKSBkZWZpbmVzIHRoZSB2YXJpYWJsZSBuYW1lcyBhcyBmb2xsb3dzOgoKKiBgQlBYU0FSYDogc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUgKG1tSGcpCiogYEJQWERBUmA6IGRpYXN0b2xpYyBibG9vZCBwcmVzc3VyZSAobW1IZykKKiBgQlBYREkxYCwgYEJQWERJMmA6IHR3byBkaWFzdG9saWMgYmxvb2QgcHJlc3N1cmUgcmVhZGluZ3MKKiBgcmFjZV9ldGhjYDogcmFjZS9ldGhuaWNpdHksIGNvZGVkIGFzIEhpc3BhbmljLCBXaGl0ZSBub24tSGlzcGFuaWMsIEJsYWNrIG5vbi1IaXNwYW5pYyBhbmQgT3RoZXIKKiBgZ2VuZGVyYDogc2V4LCBjb2RlZCBhcyBNYWxlL0ZlbWFsZQoqIGBEUjFURk9MQWA6IGZvbGF0ZSBpbnRha2UgKM68Zy9kYXkpCiogYFJJQUdFTkRSYDogc2V4LCBjb2RlZCBhcyAxLzIKKiBgQk1YQk1JYDogYm9keSBtYXNzIGluZGV4IChrZy9tMikKKiBgUklEQUdFWWA6IGFnZSAoeWVhcnMpCgojIyBBbGwgZnVuY3Rpb25zIGhhdmUgYXJndW1lbnRzCgpXZSBqdXN0IHVzZWQgMiBkaWZmZXJlbnQgKmZ1bmN0aW9ucyogaW4gUjogYGhlYWQoKWAgYW5kIGBuYW1lcygpYC4gQWxsIGZ1bmN0aW9ucyB0YWtlICphcmd1bWVudHMqLCB3aGljaCB0eXBpY2FsbHkgZ28gaW5zaWRlIHBhcmVudGhlc2VzLiBPcmRlciBtYXR0ZXJzIGhlcmUsIHVubGVzcyB5b3UgZXhwbGljaXRseSBsYWJlbCB0aGUgYXJndW1lbnRzOyBmb3IgZXhhbXBsZToKCmBgYHtyIG5oYW5lczIsIGV2YWwgPSBGQUxTRX0KaGVhZCg2LCBoZWFydCkgIyB0aGlzIHdpbGwgbm90IHdvcmsKaGVhZChuID0gNiwgeCA9IGhlYXJ0KSAjIHRoaXMgd29ya3MsIGJ1dCB5b3UgdHlwZSBtb3JlCmhlYWQoaGVhcnQsIDYpICMgdGhpcyB3b3JrcyBhbmQgaXMgc3VjY2luY3QKYGBgCgpGb3IgYm90aCBgaGVhZCgpYCBhbmQgYG5hbWVzKClgLCB0aGUgZmlyc3QgYXJndW1lbnQgd2FzIG91ciBkYXRhZnJhbWUsIHdoaWNoIHdlIG5hbWVkIGBoZWFydGAuIGBnZ3Bsb3QyYCBpcyBhIHBhY2thZ2UgdGhhdCBlc3NlbnRpYWxseSBjb250YWlucyBhIGJ1bmNoIG9mIGZ1bmN0aW9ucywgZWFjaCB0YWtpbmcgZGlmZmVyZW50IGFyZ3VtZW50cywgd2hpY2ggd2UnbGwgZXhwbG9yZSBub3cuLi4KCgoKCgojIEdyYXBocyBmb3IgZGlzcGxheSBvZiBzaW5nbGUgc2FtcGxlcwoKTGV0J3MgZm9jdXMgZmlyc3Qgb24gdW5pdmFyaWF0ZSBwbG90cyBieSBleGFtaW5pbmcgZm9sYXRlIGludGFrZSAoYSBjb250aW51b3VzIHZhcmlhYmxlKS4gVGhlIGZpcnN0IGZ1bmN0aW9uIHdlIG5lZWQgaXMgYGdncGxvdCgpYDogdGhpcyBmdW5jdGlvbiBpbml0aWFsaXplcyBhIG5ldyBnZ3Bsb3Qgb2JqZWN0LgoKV2UnbGwgc3RhcnQgd2l0aCAxIGFyZ3VtZW50OiB5b3VyIGRhdGFmcmFtZS0gd2UgY2FsbGVkIG91cnMgYGhlYXJ0YC4gCgoKYGBge3IgZW1wdHlfcGxvdH0KZ2dwbG90KGhlYXJ0KSAKYGBgCgpUaGUgY29kZSBhYm92ZSBzaG91bGQgcHJvZHVjZSBhbiBlbXB0eSBwbG90IChhIGdyYXkgcmVjdGFuZ2xlKS4gCgpgZ2dwbG90YCBtYWtlcyBhIHBsb3QgYnkgbGF5ZXJpbmcuIFNvIGxldCdzIHRha2UgdGhpcyBlbXB0eSBwbG90IGFuZCBhZGQgYSBsYXllciB0byBpdC4gVGhpcyBsYXllciBpcyBhIGdlb21ldHJpYyBvYmplY3QsIG9yIGBnZW9tYCBmb3Igc2hvcnQuIExldCdzIHN0YXJ0IHdpdGggdGhlIGBnZW9tX2hpc3RvZ3JhbSgpYC4gCgpFYWNoIGBnZW9tYCBhbHNvIHRha2VzIGFyZ3VtZW50cywgYW5kIHlvdSBjYW4gYWx3YXlzIGZpbmQgb3V0IHdoYXQgYXJndW1lbnRzIGdvIGFsb25nIHdpdGggYSBzcGVjaWZpYyBnZW9tIHVzaW5nIHRoZSBoZWxwIGZ1bmN0aW9uIGluIHRoZSBjb25zb2xlOgoKYGBge3IgaGVscCwgZXZhbCA9IEZBTFNFfQo/Z2VvbV9oaXN0b2dyYW0KYGBgCgojIyBIaXN0b2dyYW1zCgpXZSdsbCB0YWtlIHRoYXQgZW1wdHkgcGxvdCBhbmQgYWRkIGEgYGdlb21faGlzdG9ncmFtKClgIGxheWVyIHVzaW5nIHRoZSBgK2Agc2lnbi4gVGhpcyBnZW9tIHJlcXVpcmVzIGF0IGxlYXN0IG9uZSBhcmd1bWVudCwgYW5kIGl0IGlzIHNwZWNpYWw6IGl0IGlzIGFuIGFlc3RoZXN0aWMgbWFwcGluZyAoYGFlcygpYCksIG1lYW5pbmcgdGhhdCB3aGF0IGdvZXMgaW4gdGhlIHBhcmVudGhlc2VzIGhlcmUgKm11c3QqIGJlIGEgdmFyaWFibGUgdGhhdCBpcyBpbiBvdXIgZGF0YWZyYW1lLiBIZXJlLCB3ZSBtYXAgZm9sYXRlIGludGFrZSBvbnRvIHRoZSB4LWF4aXMuCgpgYGB7ciBoaXN0MX0KZ2dwbG90KGhlYXJ0KSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBEUjFURk9MQSkpCmBgYAoKCipTbWFsbCB0d2VhayAjMToqIGNoYW5nZSBjb2xvdXIgb3V0bGluZSBvZiBiYXJzCgpPbmUgb2YgdGhlIGFyZ3VtZW50cyB0aGF0IGBnZW9tX2hpc3RvZ3JhbSgpYCB0YWtlcyBpcyBgY29sb3VyYC0gd2UnbGwgY2hhbmdlIGl0IHRvIHdoaXRlLiBOb3RlIHRoYXQgd2UgYXJlICpvdXRzaWRlKiBvZiB0aGUgYGFlcygpYC0gdGhpcyBpcyBiZWNhdXNlIHRoZSBjb2xvciAid2hpdGUiIGlzIG5vdCBhIHZhcmlhYmxlLgoKYGBge3IgaGlzdDJ9CmdncGxvdChoZWFydCkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gRFIxVEZPTEEpLCBjb2xvdXIgPSAid2hpdGUiKSAKYGBgCgoqU21hbGwgdHdlYWsgIzI6KiBjaGFuZ2UgY29sb3VyIGZpbGwgb2YgYmFycwoKWW91IG1pZ2h0IGJlIHN1cnByaXNlZCB0byBzZWUgdGhhdCBgY29sb3VyYCBkb2VzIG5vdCBpbiBmYWN0IHJlbGF0ZSB0byB0aGUgY29sb3VyICppbnNpZGUqIHRoZSBiYXJzIChzZWUgYWJvdmUpLiBUbyBjaGFuZ2UgdGhhdCwgd2UgdXNlIGFub3RoZXIgYXJndW1lbnQgZm9yIGBnZW9tX2hpc3RvZ3JhbSgpYC0gYGZpbGxgLiBIb3cgYGNvbG91cmAgdmVyc3VzIGBmaWxsYCB3b3JrcyBkZXBlbmRzIG9uIHdoaWNoIGBnZW9tYCB5b3UgYXJlIHVzaW5nLiBGb3Igb3VyIGhpc3RvZ3JhbSwgd2UnbGwgY2hhbmdlIHRoZSB0aGUgY29sb3VyIGluc2lkZSB0byBvcmNoaWQuCgpgYGB7ciBoaXN0M30KZ2dwbG90KGhlYXJ0KSArCiAgZ2VvbV9oaXN0b2dyYW0oYWVzKHggPSBEUjFURk9MQSksIGZpbGwgPSAib3JjaGlkIikKYGBgCgoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtcHJpbWFyeSI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+PGkgY2xhc3M9ImZhIGZhLWhhbmQtby1yaWdodCIgYXJpYS1oaWRkZW49InRydWUiPjwvaT4gSWYgdGhpcyB3YXMgZWFzeTo8L2Rpdj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1ib2R5Ij4KTWFrZSBhIGhpc3RvZ3JhbSB3aXRoIGByb3lhbGJsdWVgIGJhcnMgYW5kIGEgYGhvdHBpbmtgIG91dGxpbmUuCmBgYHtyIGhpc3Q0LCBlY2hvID0gRkFMU0UsIGZpZy53aWR0aCA9IDQsIGZpZy5oZWlnaHQgPSAzfQpnZ3Bsb3QoaGVhcnQpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IERSMVRGT0xBKSwgY29sb3VyID0gImhvdHBpbmsiLCBmaWxsID0gInJveWFsYmx1ZSIpCmBgYAogIDwvZGl2Pgo8L2Rpdj4KCipTbWFsbCB0d2VhayAjMzoqIGNoYW5nZSBudW1iZXIgb2YgYmlucwoKWW91IG1heSBoYXZlIG5vdGljZWQgdGhhdCBhbGwgb2Ygb3VyIGhpc3RvZ3JhbXMgaGF2ZSBwcmludGVkIHRoaXMgd2FybmluZyBpbiB5b3VyIGNvbnNvbGU6CgpgI3N0YXRfYmluKCkgdXNpbmcgYmlucyA9IDMwLiBQaWNrIGJldHRlciB2YWx1ZSB3aXRoIGJpbndpZHRoLmAKClRoaXMgd2FybmluZyB0ZWxscyB1cyB0aGF0IGBiaW5zID0gMzBgIGlzIHRoZSBkZWZhdWx0IHRoYXQgYGdncGxvdGAgY2hvc2UgZm9yIHVzLCBiYXNlZCBvbiBvdXIgZGF0YS4gTGV0J3MgcGxheSBhcm91bmQgd2l0aCB0aGlzIQoKPGRpdiBjbGFzcz0icGFuZWwgcGFuZWwtc3VjY2VzcyI+CiAgPGRpdiBjbGFzcz0icGFuZWwtaGVhZGluZyI+PGkgY2xhc3M9ImZhIGZhLWhhbmRzaGFrZS1vIiBhcmlhLWhpZGRlbj0idHJ1ZSI+PC9pPiBQYWlyIHVwPC9kaXY+CiAgPGRpdiBjbGFzcz0icGFuZWwtYm9keSI+Ck1ha2UgYSBoaXN0b2dyYW0gd2l0aCAxMCBiaW5zLCBhbmQgb25lIHdpdGggNTAgYmlucy4gQ29sb3IvZmlsbCBhbnkgd2F5IHlvdSBsaWtlIQpgYGB7ciBoaXN0NiwgaW5jbHVkZSA9IEZBTFNFfQpnZ3Bsb3QoaGVhcnQpICsKICBnZW9tX2hpc3RvZ3JhbShhZXMoeCA9IERSMVRGT0xBKSwgCiAgICAgICAgICAgICAgICAgY29sb3VyID0gIndoaXRlIiwgCiAgICAgICAgICAgICAgICAgZmlsbCA9ICJvcmNoaWQiLCAKICAgICAgICAgICAgICAgICBiaW5zID0gNTApIApgYGAKICA8L2Rpdj4KPC9kaXY+CgoKCipTbWFsbCB0d2VhayAjMzoqIHVwZGF0ZSB4LWF4aXMgdGl0bGUgCgpUaGlzIGlzIGRvbmUgYnkgYWRkaW5nIGEgYGxhYnNgIGxheWVyLCBzaG9ydCBmb3IgbGFiZWxzLiBXZSB3YW50IHRvIHJlbGFiZWwgdGhlIHgtYXhpcy4gTGV0J3MgYWxzbyBzYXZlIG91ciBgZ2dwbG90YCBvYmplY3Qgbm93LCBhbmQgY2FsbCBpdCBgbXloaXN0YC4KCmBgYHtyIGhpc3Q1fQpteWhpc3QgPC0gZ2dwbG90KGhlYXJ0LCBhZXMoeCA9IERSMVRGT0xBKSkgKwogIGdlb21faGlzdG9ncmFtKGNvbG91ciA9ICJ3aGl0ZSIsIGZpbGwgPSAib3JjaGlkIikgKwogIGxhYnMoeCA9ICJmb2xhdGUgaW50YWtlIikKYGBgCgoKCgoKCkknbSBwcmV0dHkgaGFwcHkgd2l0aCB0aGlzIG9uZSwgc28gSSdtIGdvaW5nIHRvIHNhdmUgdGhpcyBwbG90IHNvIEkgY2FuIHN0b3AgdHlwaW5nIHRoZSBzYW1lIHRoaW5nIG92ZXIgYW5kIG92ZXIgYWdhaW4sIGFuZCBJIGNhbiBhZGQgbGF5ZXJzIHVzaW5nIHRoaXMgb25lIGFzIG15IGJhc2UgcGxvdC4KCmBgYHtyIGhpc3Rfc2F2ZSwgZmlnLnNob3c9ImhpZGUifQpoaXN0IDwtIGdncGxvdChoZWFydCwgYWVzKHggPSBEUjFURk9MQSkpICsKICBnZW9tX2hpc3RvZ3JhbShjb2xvdXIgPSAid2hpdGUiLCBmaWxsID0gIm9yY2hpZCIsIGJpbnMgPSA1MCkgKwogIGxhYnMoeCA9ICJmb2xhdGUgaW50YWtlICjOvGcvZGF5KSIpCmhpc3QgIyB2aWV3IHlvdXIgcGxvdApgYGAKCiMgR3JhcGhzIGNvbXBhcmluZyB0d28gdmFyaWFibGVzOiBjb250aW51b3VzIHZzLiBjYXRlZ29yaWNhbAoKT0ssIGxldCdzIGFkZCBhbm90aGVyIHZhcmlhYmxlIHRvIHRoZSBtaXguIFdlJ2xsIGtlZXAgbG9va2luZyBhdCBmb2xhdGUgaW50YWtlIGFzIHRoZSBjb250aW51b3VzIHZhcmlhYmxlLCB3aXRoIGBnZW5kZXJgIGFzIHRoZSBjYXRlZ29yaWNhbCB2YXJpYWJsZS4gVGhlcmUgYXJlIGEgZmV3IHdheXMgdG8gZG8gdGhpcyBpbiBgZ2dwbG90MmAuIAoKKiBGYWNldHMKKiBDb2xvcnMKKiBTaWRlLWJ5LXNpZGUgcGxvdHMgKGFsb25nIHRoZSB4LWF4aXMpCgoKIyMgRmFjZXRzCgpBYm91dCBmYWNldHMgKGZyb20gYGZhY2V0X3dyYXBgIGRvY3VtZW50YXRpb24pOgoKPiAiTW9zdCBkaXNwbGF5cyBhcmUgcm91Z2hseSByZWN0YW5ndWxhciwgc28gaWYgeW91IGhhdmUgYSBjYXRlZ29yaWNhbCB2YXJpYWJsZSB3aXRoIG1hbnkgbGV2ZWxzLCBpdCBkb2Vzbid0IG1ha2Ugc2Vuc2UgdG8gdHJ5IGFuZCBkaXNwbGF5IHRoZW0gYWxsIGluIG9uZSByb3cgKG9yIG9uZSBjb2x1bW4pLiBUbyBzb2x2ZSB0aGlzIGRpbGVtbWEsIGZhY2V0X3dyYXAgd3JhcHMgYSAxZCBzZXF1ZW5jZSBvZiBwYW5lbHMgaW50byAyZCwgbWFraW5nIGJlc3QgdXNlIG9mIHNjcmVlbiByZWFsIGVzdGF0ZS4iCgpMZXQncyBhZGQgYCsgZmFjZXRfd3JhcCh+dmFyaWFibGUpYCB0byBzcGxpdCB0aGUgaGlzdG9ncmFtcyBiYXNlZCBvbiBgZ2VuZGVyYAoKYGBge3IgaGlzdF9mYWNldH0KaGlzdCArIGZhY2V0X3dyYXAofmdlbmRlcikKYGBgCgpKdXN0IGFzIGEgcmVtaW5kZXIsIHRoZSBiZWxvdyBjb2RlIHdvdWxkIGNyZWF0ZSB0aGUgZXhhY3Qgc2FtZSBwbG90LSBqdXN0IHdpdGggbW9yZSByZXBldGl0aXZlIHR5cGluZy4KCmBgYHtyIGhpc3RfZmFjZXRfY29tcGFyZSwgZXZhbCA9IEZBTFNFfQpnZ3Bsb3QoaGVhcnQsIGFlcyh4ID0gRFIxVEZPTEEpKSArCiAgZ2VvbV9oaXN0b2dyYW0oY29sb3VyID0gIndoaXRlIiwgZmlsbCA9ICJvcmNoaWQiLCBiaW5zID0gNTApICsKICBsYWJzKHggPSAiZm9sYXRlIGludGFrZSAozrxnL2RheSkiKSArCiAgZmFjZXRfd3JhcCh+Z2VuZGVyKQpgYGAKCgo8ZGl2IGNsYXNzPSJwYW5lbCBwYW5lbC1zdWNjZXNzIj4KICA8ZGl2IGNsYXNzPSJwYW5lbC1oZWFkaW5nIj5JZiB0aGlzIHdhcyBlYXN5OjwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpNYWtlIHRoZSBzYW1lIGZhY2V0dGVkIHBsb3QgdXNpbmcgdGhlIGtlcm5lbCBkZW5zaXR5IGdlb20gKGBnZW9tX2RlbnNpdHlgKSBpbnN0ZWFkIG9mIGBnZW9tX2hpc3RvZ3JhbWAuCmBgYHtyIGRlbnNfZmFjZXQsIGVjaG8gPSBGQUxTRSwgZmlnLndpZHRoID0gNSwgZmlnLmhlaWdodCA9IDR9CmdncGxvdChoZWFydCwgYWVzKHggPSBEUjFURk9MQSkpICsKICBnZW9tX2RlbnNpdHkoY29sb3VyID0gIndoaXRlIiwgZmlsbCA9ICJvcmNoaWQiKSArCiAgbGFicyh4ID0gImZvbGF0ZSBpbnRha2UgKM68Zy9kYXkpIikgKwogIGZhY2V0X3dyYXAofmdlbmRlcikKYGBgCiAgPC9kaXY+CjwvZGl2PgoKCiMjIENvbG91cnMKClNvIGZhciwgd2hlbiB3ZSBwbGF5ZWQgd2l0aCBjb2xvdXJzLCB3ZSBzZXQgZmlsbC9jb2xvdXIgd2l0aG91dCBtYXBwaW5nIHRoZW0gb250byBhbm90aGVyIHZhcmlhYmxlIChpLmUuLCBgKGNvbG91ciA9ICJ3aGl0ZSIsIGZpbGwgPSAib3JjaGlkIilgKS4gTm93LCB3ZSB3YW50IGZpbGwvY29sb3VyIHRvICoqdmFyeSoqIGJhc2VkIG9uIHRoZSB2YWx1ZSBvZiBhIHNwZWNpZmljIHZhcmlhYmxlLiBIZXJlIHdlIHdhbnQgY29sb3VyIHRvIGRlcGVuZCBvbiBgZ2VuZGVyYCwgc28gd2UgbWFwIHRoZSBjb2xvdXIgYWVzdGhldGljIChgYWVzKGNvbG91ciA9IHZhcmlhYmxlKWApIG9udG8gdGhlIHZhcmlhYmxlIGBnZW5kZXJgLgoKTm90aWNlIG1vdmluZyB0aGUgY29sb3VyIGludG8gYWVzdGhldGljcyBwYXJlbnRoZXNlcy4KCmBgYHtyIGRlbnMxfQpnZ3Bsb3QoaGVhcnQsIGFlcyh4ID0gRFIxVEZPTEEpKSArCiAgZ2VvbV9kZW5zaXR5KGFlcyhjb2xvdXIgPSBnZW5kZXIpKSArCiAgbGFicyh4ID0gImZvbGF0ZSBpbnRha2UgKM68Zy9kYXkpIikgCmBgYAoKVGhlIGJlbG93IGNvZGUgd2lsbCBwcm9kdWNlIHRoZSAqZXhhY3QqIHNhbWUgcGxvdC4gVHJ5IGl0ISBJIHByb21pc2UgaXQgaXMgaWRlbnRpY2FsLiBQbGF5IGFyb3VuZCB3aXRoIGNoYW5naW5nIGdsb2JhbCBhZXN0aGV0aWNzIChsaWtlIGJlbG93KSB2ZXJzdXMgZ2VvbS1zcGVjaWZpYyBhZXN0aGV0aWNzIChsaWtlIGFib3ZlKS4gSXQgd29uJ3QgbWF0dGVyIHdoZW4geW91IG9ubHkgaGF2ZSBvbmUgbGF5ZXIsIGJ1dCBvbmNlIHlvdSBzdGFydCBhZGRpbmcgYGdlb21zYCBhbmQgYHN0YXRzYCwgdGhpcyBjYW4gYmUgYSBwb3dlcmZ1bCB3YXkgdG8gY2hhbmdlIHlvdXIgdmlzdWFsaXphdGlvbi4KCmBgYHtyIGRlbnMyLCBldmFsID0gRkFMU0V9CmdncGxvdChoZWFydCwgYWVzKHggPSBEUjFURk9MQSwgY29sb3VyID0gZ2VuZGVyKSkgKwogIGdlb21fZGVuc2l0eSgpICsKICBsYWJzKHggPSAiZm9sYXRlIGludGFrZSAozrxnL2RheSkiKSAKYGBgCgojIyBTaWRlLWJ5LXNpZGUgcGxvdHMKCk5vdyBsZXQncyBtYWtlIHNvbWUgc2lkZS1ieS1zaWRlIHVuaXZhcmlhdGUgcGxvdHMsIHNwZWNpZmljYWxseSBkb3RjaGFydHMgb3Igc3RyaXBjaGFydHMsIHRvIHZpc3VhbGl6ZSBzeXN0b2xpYyBibG9vZCBwcmVzc3VyZSBieSBnZW5kZXIuIFdlJ2xsIHN0YXJ0IHdpdGggYSBuZXcgYmFzZSBwbG90LCBhbmQgc2F2ZSBpdCB0byBhbiBvYmplY3QgY2FsbGVkIGBzaWRlYCB0aGF0IHdlIGNhbiB0aGVuIGFkZCBnZW9tcyB0by4KCmBgYHtyIHNpZGV9CnNpZGUgPC0gZ2dwbG90KGhlYXJ0LCBhZXMoeCA9IGdlbmRlciwgeSA9IEJQWFNBUikpICsKICBsYWJzKHggPSAiIiwgeSA9ICJTeXN0b2xpYyBCUCAobW1IZykiKQpgYGAKCjxkaXYgY2xhc3M9InBhbmVsIHBhbmVsLXN1Y2Nlc3MiPgogIDxkaXYgY2xhc3M9InBhbmVsLWhlYWRpbmciPjxpIGNsYXNzPSJmYSBmYS1oYW5kc2hha2UtbyIgYXJpYS1oaWRkZW49InRydWUiPjwvaT4gUGFpciB1cDwvZGl2PgogIDxkaXYgY2xhc3M9InBhbmVsLWJvZHkiPgpJZiB5b3UgdHlwZSBgc2lkZWAgaW50byB5b3VyIGNvbnNvbGUsIHdoYXQgd2lsbCB0aGUgcGxvdCBsb29rIGxpa2U/IFRyeSBhZGRpbmcgYSBgZ2VvbV9wb2ludCgpYC0gaXMgdGhpcyBwbG90IHVzZWZ1bD8KYGBge3IgcG9pbnQxLCBpbmNsdWRlID0gRkFMU0V9CnNpZGUgIyBibGFuayBwbG90CnNpZGUgKyBnZW9tX3BvaW50KCkgCmBgYAogIDwvZGl2Pgo8L2Rpdj4KCgpMZXQncyB0YWtlIHRoaXMgcGxvdCBhbmQgdHdlYWsgaXQgYSBiaXQuIAoKKlNtYWxsIHR3ZWFrICMyOiogbWFrZSBwb2ludHMgbW9yZSB0cmFuc3BhcmVudAoKQWxwaGEgd29ya3Mgb24gYSBzY2FsZSBmcm9tIDAgKHRyYW5zcGFyZW50KSB0byAxIChvcGFxdWUpLgogCmBgYHtyIHBvaW50Mn0Kc2lkZSArIGdlb21fcG9pbnQoYWxwaGEgPSAuMykgCmBgYAoKKlNtYWxsIHR3ZWFrICMzKjogdHJ5IGppdHRlcmluZyB0aGUgcG9pbnRzCgpUbyBkbyB0aGlzLCBpbnN0ZWFkIG9mIGBnZW9tX3BvaW50KClgLCB3ZSB3aWxsIHN3aXRjaCB0byB1c2luZyBgZ2VvbV9qaXR0ZXIoKWAsIHdoaWNoIGF1dG9tYXRpY2FsbHkgYWRkcyBib3RoIHZlcnRpY2FsIGFuZCBob3Jpem9udGFsIHNwYWNlIChub2lzZSkgdG8geW91ciBkYXRhcG9pbnRzLgoKYGBge3Igaml0dGVyMX0Kc2lkZSArIGdlb21faml0dGVyKGFscGhhID0gLjMpIApgYGAKCipTbWFsbCB0d2VhayAjNCo6IGNvbnRyb2wgdGhlIGppdHRlcgoKU29tZXRpbWVzIHlvdSBtYXkgb25seSB3YW50IHRvIGNoYW5nZSB0aGUgd2lkdGggb2Ygaml0dGVyIGJ1dCBub3QgdGhlIGhlaWdodC4gCgpgYGB7ciBqaXR0ZXIyfQpzaWRlICsgZ2VvbV9qaXR0ZXIoYWxwaGEgPSAuMywgd2lkdGggPSAuMiwgaGVpZ2h0ID0gMCkgCmBgYAoKCk5vdyBsZXQncyB0cnkgYSBkaWZmZXJlbnQgZ2VvbSBpbiBvdXIgc2lkZS1ieS1zaWRlIHBsb3QsIHdoaWNoIGlzIGF2YWlsYWJsZSB0aHJvdWdoIHRoZSBgYmVlc3dhcm1gIHBhY2thZ2UgeW91IGFscmVhZHkgbG9hZGVkLgoKCmBgYHtyIGJlZTF9CnNpZGUgKyBnZW9tX2JlZXN3YXJtKCkgCmBgYAoKKlNtYWxsIHR3ZWFrICMxOiogYWRkIGFscGhhIGFnYWluCgpgYGB7ciBiZWUyfQpzaWRlICsgZ2VvbV9iZWVzd2FybShhbHBoYSA9IC4yKSAKYGBgCgoqU21hbGwgdHdlYWsgIzI6KiBhZGQgc3RhdGlzdGljcwoKV2UnbGwgaW5jbHVkZSB0aGUgbWVhbiBwbHVzIDk1JSBDSQoKYGBge3IgYmVlX3N0YXRzfQpnZ3Bsb3QoaGVhcnQsIGFlcyh4ID0gZ2VuZGVyLCB5ID0gQlBYU0FSKSkgKwogIGdlb21fYmVlc3dhcm0oYWxwaGEgPSAuMikgKwogIHN0YXRfc3VtbWFyeShmdW4ueSA9ICJtZWFuIiwgZ2VvbSA9ICJwb2ludCIsIGNvbG91ciA9ICJvcmFuZ2UiKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhID0gbWVhbl9jbF9ib290LCBnZW9tID0gImxpbmVyYW5nZSIsIGNvbG91ciA9ICJvcmFuZ2UiKSAgKwogIGxhYnMoeCA9ICIiLCB5ID0gIlN5c3RvbGljIEJQIChtbUhnKSIpCmBgYAoKKlNtYWxsIHR3ZWFrICMzOiogYWRkIGFub3RoZXIgZ2VvbSBsYXllcgoKVHJ5IGFkZGluZyBgZ2VvbV9ib3hwbG90YCBvbiB0b3Agb2YgeW91ciBzaWRlLWJ5LXNpZGUgYmVlc3dhcm0gcGxvdC4gQWxzbyB0cnkgcmUtb3JkZXJpbmcgdGhlIGdlb21zIHRvIHNlZSB3aGF0IGNoYW5nZXMuCgpgYGB7ciBiZWVfYm94fQpzaWRlICsKICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArCiAgZ2VvbV9iZWVzd2FybShhbHBoYSA9IC4yKSAKYGBgCgoKTmV3IGdlb20gdGltZS0gdGhpcyB0aW1lIHVzZSBgZ2VvbV92aW9saW5gLgoKYGBge3IgdmlvMX0Kc2lkZSArIGdlb21fdmlvbGluKGFscGhhID0gLjIpIApgYGAKCipTbWFsbCB0d2VhayAjMToqIGFkZCBzdGF0aXN0aWNzCgpMZXQncyBpbmNsdWRlIHRoZSBzYW1wbGUgbWVhbiBhbmQgbWVkaWFuCgpgYGB7ciB2aW9fc3RhdH0KZ2dwbG90KGhlYXJ0LCBhZXMoeCA9IGdlbmRlciwgeSA9IEJQWFNBUikpICsKICBnZW9tX3Zpb2xpbihhbHBoYSA9IC4yKSArCiAgc3RhdF9zdW1tYXJ5KGZ1bi55ID0gIm1lYW4iLCBnZW9tID0gInBvaW50IiwgY29sb3VyID0gIm9yYW5nZSIpICsKICBzdGF0X3N1bW1hcnkoZnVuLnkgPSAibWVkaWFuIiwgZ2VvbSA9ICJwb2ludCIsIGNvbG91ciA9ICJibHVlIikgKwogIGxhYnMoeCA9ICIiLCB5ID0gIlN5c3RvbGljIEJQIChtbUhnKSIpCmBgYAoKKlNtYWxsIHR3ZWFrICMyOiogYWRkIGFub3RoZXIgZ2VvbSBsYXllcgoKQWRkIGFub3RoZXIgbGF5ZXIgdG8geW91ciB2aW9saW4gcGxvdDogdHJ5IGBnZW9tX2JveHBsb3RgCgpgYGB7ciB2aW9fYm94fQpzaWRlICsKICBnZW9tX3Zpb2xpbihhbHBoYSA9IC4yKSArCiAgZ2VvbV9ib3hwbG90KHdpZHRoID0gLjA1KSAKYGBgCgojIEdyYXBocyBjb21wYXJpbmcgdHdvIHZhcmlhYmxlczogY29udGludW91cyB2cy4gY29udGludW91cwoKTm93IHdlJ2xsIGNyZWF0ZSBzb21lIGJpdmFyaWF0ZSBwbG90cyB0byBsb29rIGF0IHRoZSBhc3NvY2lhdGlvbiBiZXR3ZWVuIGFnZSBhbmQgc3lzdG9saWMgYmxvb2QgcHJlc3N1cmUsIGJvdGggb2Ygd2hpY2ggYXJlIGNvbnRpbnVvdXMgdmFyaWFibGVzLgoKIyMgU2NhdHRlcnBsb3RzCgpgYGB7ciBzY2F0dGVyMX0KZ2dwbG90KGhlYXJ0LCBhZXMoeCA9IFJJREFHRVlSLCB5ID0gQlBYU0FSKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4ID0gIkFnZSAoeWVhcnMpIiwgeSA9ICJTeXN0b2xpYyBCUCAobW1IZykiKQpgYGAKCklmIHlvdSBoYXZlIGJpZyBuLCB0cnkgaGV4YmluIHBsb3QKYGBge3IgaGV4MX0KZ2dwbG90KGhlYXJ0LCBhZXMoeCA9IFJJREFHRVlSLCB5ID0gQlBYU0FSKSkgKwogIGdlb21faGV4KCkgKwogIGxhYnMoeCA9ICJBZ2UgKHllYXJzKSIsIHkgPSAiU3lzdG9saWMgQlAgKG1tSGcpIikKYGBgCgpNYWtlIGNvbG9ycyBtYWtlIG1vcmUgc2Vuc2UKYGBge3IgaGV4Mn0KbGlicmFyeSh2aXJpZGlzKQpnZ3Bsb3QoaGVhcnQsIGFlcyh4ID0gUklEQUdFWVIsIHkgPSBCUFhTQVIpKSArCiAgZ2VvbV9oZXgoKSArCiAgbGFicyh4ID0gIkFnZSAoeWVhcnMpIiwgeSA9ICJTeXN0b2xpYyBCUCAobW1IZykiKSArCiAgc2NhbGVfZmlsbF9ncmFkaWVudG4oY29sb3VycyA9IHZpcmlkaXMoMjU2LCBiZWdpbiA9IDEsIGVuZCA9IDApKQpgYGAKCgpgYGB7ciBjb250b3VyfQpsaWJyYXJ5KGdnYWx0KQpnZ3Bsb3QoaGVhcnQsIGFlcyh4ID0gUklEQUdFWVIsIHkgPSBCUFhTQVIpKSArCiAgc3RhdF9ia2RlMmQoYWVzKGZpbGwgPSAuLmxldmVsLi4sIGFscGhhID0gLi5sZXZlbC4uKSwgZ2VvbSA9ICJwb2x5Z29uIiwgYmFuZHdpZHRoID0gYygyLDIpKSAgKwogIGxhYnMoeCA9ICJBZ2UgKHllYXJzKSIsIHkgPSAiU3lzdG9saWMgQlAgKG1tSGcpIikgKwogIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKGNvbG91cnMgPSB2aXJpZGlzKDI1NiwgYmVnaW4gPSAxLCBlbmQgPSAwKSkKYGBgCgpBZGQgbGluZWFyIHJlZ3Jlc3Npb24gbGluZSB3aXRoIFNFCmBgYHtyIHNjYXR0ZXJfbG19CmdncGxvdChoZWFydCwgYWVzKHggPSBSSURBR0VZUiwgeSA9IEJQWFNBUikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpICsKICBsYWJzKHggPSAiQWdlICh5ZWFycykiLCB5ID0gIlN5c3RvbGljIEJQIChtbUhnKSIpCmBgYAoKRGVmYXVsdCBpcyBsb2VzcyBsaW5lCmBgYHtyIHNjYXR0ZXJfbG9lc3N9CmdncGxvdChoZWFydCwgYWVzKHggPSBSSURBR0VZUiwgeSA9IEJQWFNBUikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKCkgKwogIGxhYnMoeCA9ICJBZ2UgKHllYXJzKSIsIHkgPSAiU3lzdG9saWMgQlAgKG1tSGcpIikKYGBgCgpBZGQgc3BsaW5lcwpgYGB7ciBzY2F0dGVyX3NwbGluZXN9CmxpYnJhcnkoc3BsaW5lcykKbGlicmFyeShNQVNTKQpnZ3Bsb3QoaGVhcnQsIGFlcyh4ID0gUklEQUdFWVIsIHkgPSBCUFhTQVIpKSArCiAgZ2VvbV9wb2ludCgpICsKICBzdGF0X3Ntb290aChtZXRob2QgPSAibG0iLCBmb3JtdWxhID0geSB+IG5zKHgsIDMpKSArCiAgbGFicyh4ID0gIkFnZSAoeWVhcnMpIiwgeSA9ICJTeXN0b2xpYyBCUCAobW1IZykiKQpgYGAKCiMgR3JhcGhzIGlsbHVzdHJhdGluZyBtb3JlIHRoYW4gdHdvIHZhcmlhYmxlcwoKV2UnbGwgZmluaXNoIHVwIGJ5IGNyZWF0aW5nIHNvbWUgbXVsdGl2YXJpYWJsZSBwbG90cyB0aGF0IGhlbHAgdXMgdmlzdWFsaXplIGhvdyB0aGUgYXNzb2NpYXRpb24gYmV0d2VlbiBib2R5IG1hc3MgaW5kZXggJiBzeXN0b2xpYyBCUCB2YXJpZXMgYnkgZ2VuZGVyIGFuZCBhZ2UgKHNvIDQgdmFyaWFibGVzISkuCgpKdXN0IGNvcHkgdGhpcyBjb2RlIHRvIGNyZWF0ZSBhIGNhdGVnb3JpY2FsIGFnZSB2YXJpYWJsZToKYGBge3IgY3JlYXRlX2FnZV9jYXRlZ29yeX0KbGlicmFyeShkcGx5cikKaGVhcnQyIDwtIGhlYXJ0ICU+JSAKICBtdXRhdGUoYWdlX2NhdCA9IGN1dChSSURBR0VZUiwgYygwLCAzMCwgNTUsIDEwMCkpKQpgYGAKClJlY3JlYXRlIHRoZWlycyBmaXJzdAoKYGBge3IgbXVsdGlfZmFjZXR9CmdncGxvdChoZWFydDIsIGFlcyh4ID0gQk1YQk1JLCB5ID0gQlBYU0FSKSkgKwogIGdlb21fcG9pbnQoKSArCiAgc3RhdF9zbW9vdGgoYWVzKGNvbG91ciA9IGdlbmRlciksIG1ldGhvZCA9ICJsbSIpICsKICBmYWNldF93cmFwKH5hZ2VfY2F0KSArCiAgbGFicyh4ID0gIkJvZHkgTWFzcyBJbmRleCJ+KGtnL21eMiksIHkgPSAiU3lzdG9saWMgQlAgKG1tSGcpIikKYGBgCgpUcnkgd2l0aCBmYWNldCBncmlkLCB1cGRhdGUgbGFiZWxzCgpgYGB7ciBtdWx0aV9ncmlkMX0KZ3JpZCA8LSBnZ3Bsb3QoaGVhcnQyLCBhZXMoeCA9IEJNWEJNSSwgeSA9IEJQWFNBUikpICsKICBzdGF0X3Ntb290aChhZXMoY29sb3VyID0gZ2VuZGVyKSwgbWV0aG9kID0gImxtIikgKwogIGZhY2V0X2dyaWQoZ2VuZGVyfmFnZV9jYXQpICsKICBsYWJzKHggPSAiQm9keSBNYXNzIEluZGV4In4oa2cvbV4yKSwgeSA9ICJTeXN0b2xpYyBCUCAobW1IZykiKQpncmlkICsgZ2VvbV9wb2ludCgpCmBgYAoKUGxheSB3aXRoIGNvbG9ycyEKYGBge3IgbXVsdGlfZ3JpZDJ9CmdyaWQgKwogIGdlb21fcG9pbnQoYWVzKGNvbG91ciA9IGdlbmRlciksIGFscGhhID0gLjUpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCIjQjQ3Q0M3IiwgIiNENjVGNUYiKSwgZ3VpZGUgPSBGQUxTRSkKYGBgCgoKYGBge3IgbXVsdGlfZ3JpZDN9Cm15X2NvbG9ycyA8LSBjKCIjQzRBRDY2IiwgIiM3N0JFREIiKQpncmlkICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gbXlfY29sb3JzLCBndWlkZSA9IEZBTFNFKQpgYGAKCjxkaXYgY2xhc3MgPSAicm93Ij4KICAKPGRpdiBjbGFzcyA9ICJjb2wtbWQtNCI+Cjxicj48YnI+U2luY2UgUiBNYXJrZG93biB1c2UgdGhlIFtib290c3RyYXAgZnJhbWV3b3JrXShodHRwczovL2dldGJvb3RzdHJhcC5jb20vZG9jcy80LjAvbGF5b3V0L2dyaWQvKSB1bmRlciB0aGUgaG9vZC4gSXQgaXMgcG9zc2libGUgdG8gYmVuZWZpdCBpdHMgcG93ZXJmdWwgZ3JpZCBzeXN0ZW0uIEJhc2ljYWxseSwgeW91IGNhbiBjb25zaWRlciB0aGF0IHlvdXIgcm93IGlzIGRpdmlkZWQgaW4gMTIgc3VidW5pdHMgb2Ygc2FtZSB3aWR0aC4gWW91IGNhbiB0aGVuIGNob29zZSB0byB1c2Ugb25seSBhIGZldyBvZiB0aGlzIHN1YnVuaXRzLgo8L2Rpdj4KICAKPGRpdiBjbGFzcyA9ICJjb2wtbWQtNCI+Cjxicj48YnI+SGVyZSwgSSB1c2UgMyBzdWJ1bml0cyBvZiBzaXplIDQgKDR4Mz0xMikuIFRoZSBsYXN0IGNvbHVtbiBpcyB1c2VkIGZvciBhIHBsb3QuIFlvdSBjYW4gcmVhZCBtb3JlIGFib3V0IHRoZSBncmlkIHN5c3RlbSBbaGVyZV0oYm9vdHN0cmFwIGdyaWQgc3lzdGVtKS4gSSBnb3QgdGhpcyByZXN1bHQgc2hvd2luZyB0aGUgZm9sbG93aW5nIGNvZGUgaW4gbXkgUiBNYXJrZG93biBkb2N1bWVudC4KPC9kaXY+CiAgCjxkaXYgY2xhc3MgPSAiY29sLW1kLTQiPgpgYGB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1GQUxTRX0KZ2dwbG90KCBtdGNhcnMsIGFlcyh4PW1wZykpICsgZ2VvbV9oaXN0b2dyYW0oZmlsbD0ic2t5Ymx1ZSIsIGFscGhhPTAuNSkgKyB0aGVtZV9taW5pbWFsKCkKYGBgCjwvZGl2Pgo8L2Rpdj4K