System preparation

knitr::opts_chunk$set(
    echo = TRUE,
    message = FALSE,
    warning = FALSE
)
# packages to load (if they are already installed)
library(ggplot2)
library(lattice)
library(ggversa)
library(car)
Loading required package: carData

Introduction to graphs

R is a great platform for building graphs. Literally, in a typical interactive session, you build a graph one statement at a time, adding features, until you have what you want.
The base graphic system chapter of the book, provides information on how modify and customize graphs. Two other systems, that are widely used, and provide extensive options are lattice and ggplot2. We will be mostly using the base graphic system and ggplot2.

The primary graph for a variable: the histogram

Histograms display the distribution of a continuous variable by dividing the range of scores into a specified number of bins on the x-axis and displaying the frequency of scores in each bin on the y-axis.

Basic histograms

The following chunk produces four histograms, using the R base graphic system.

#reading data on Melocactus intortus
melodata <- read.csv("melocactus.csv", header = TRUE)
#this is to produce a 2x2 graphs arrangement
par(mfrow=c(2,2))
# the most basic form
hist(melodata$alturatotal)  

# controlling some aspects of the histogram
hist(melodata$alturatotal,
     breaks=12,
     col="green",
     xlab="Altura de la planta, cm",
     main="Colored histogram with 12 bins")

# with a density curve and rug plot 
hist(melodata$alturatotal,
     freq=FALSE,
     breaks=12,
     col="green",
     xlab="Altura de la planta, cm",
     main="Histogram, rug plot, density curve")
rug(jitter(melodata$alturatotal))
lines(density(melodata$alturatotal), col="red", lwd=2)

# including a normal curve based on the data
x <- melodata$alturatotal
h<-hist(x,
        breaks=12,
        col="green",
        xlab="Altura de la planta, cm",
        main="Histogram with normal curve and box")
xfit<-seq(min(x), max(x), length=80)
yfit<-dnorm(xfit, mean=mean(x), sd=sd(x))
yfit <- yfit*diff(h$mids[1:2])*length(x)
lines(xfit, yfit, col="red", lwd=2)
box()
Introducing ggplot2: building a histogram

Now we are going to build a histogram using ggplot2. ggplot2 provides a system for creating graphs based on the grammar of graphics. The intention of the ggplot2 package is to provide a comprehensive, grammar-based system for generating graphs in a unified and coherent manner, allowing users to create new and innovative data visualizations. The power of this approach has led ggplot2 to become an important tool for visualizing data using R.

First, let see a basic ggplot2 histogram:

melodata <- read.csv("melocactus.csv", header = TRUE)

ggplot(melodata, aes(alturatotal))+
  geom_histogram(color="white", bins = 14)

Now a more detailed histogram, including several layers:

hist.melodata <- ggplot(melodata, aes(alturatotal)) + 
  geom_histogram(aes(y=..density..), bins = 14, colour="white", fill="green") +
  geom_rug(sides = "b", color = "black") +
  labs(x="Altura total de la planta,cm", y = "Density") +
  stat_function(fun = dnorm, 
                args = list(mean = mean(melodata$alturatotal, na.rm = TRUE), 
                            sd = sd(melodata$alturatotal, na.rm = TRUE)), 
                colour = "red", size = 1)
hist.melodata

The anatomy of a box-and-whiskers plot

A box-and-whiskers plot describes the distribution of a continuous variable by plotting its five-number summary: the minimum, lower quartile (25th percentile), median (50th percentile), upper quartile (75th percentile), and maximum. It can also display observations that may be outliers (values outside the range of ± 1.5*IQR, where IQR is the interquartile range defined as the upper quartile minus the lower quartile). By default, each whisker extends to the most extreme data point, which is no more than 1.5 times the interquartile range for the box. Values outside this range are depicted as dots.


Now we are going to analyze the Melocactus data using a box-plot.

ggplot(melodata, aes(x = "0", y = alturatotal)) +
  geom_boxplot(fill="cornflowerblue", color="black") +
  geom_point(position="jitter", size = 0.5, color="blue", alpha=.5) +
  labs(x = "Melocactus intortus", y = "Altura total de la planta, cm")

Related to box-plots are the violin plots; the violin plots provide more visual cues as to the distribution of scores over the range of heights for each voice part.

ggplot(melodata, aes(x="0", y=alturatotal)) +
  geom_violin(fill="lightblue") +
  geom_point(color = "blue", alpha = 0.3) +
  labs(x = "Melocactus intortus", y = "Altura total de la planta, cm")

Exercises

Build a graph, with the Melocactus data, that combines the box-plot and violin plot.

Bar graphs for categorical variables

Counts of cases for categorical variable are usually presented using bar graphs. Here we use data from a clinical trial of a treatment for arthritis, comparing the outcomes for treated individuals versus individuals receiving a placebo.

load("Arthr.Rdata")
head(Arthritis)
A <- ggplot(Arthritis, aes(x=Treatment, fill=Improved)) +
     geom_bar(position="stack")
B <- ggplot(Arthritis, aes(x=Treatment, fill=Improved)) +
     geom_bar(position="dodge")
C <- ggplot(Arthritis, aes(x=Treatment, fill=Improved)) +
     geom_bar(position="fill")
#using the package cowplot to group graphs built separatelly
plot_grid(A, B, C, ncol = 2, labels = "AUTO")

Exercise

Try to improve the graphs, with smaller fonts.


Statistical tests and graphs for the relationship between two or more variables

In this section we will consider the testing of statistical hypothesis for the relationship of two or more variables, and the graphs that will help in the interpretation of the results.

Regression analysis

Regression analysis is a broad term for a set of methodologies used to predict a response variable (also called a dependent, criterion, or outcome variable) from one or more predictor variables (also called independent or explanatory variables). In general, regression analysis can be used to identify the explanatory variables that are related to a response variable, to describe the form of the relationships involved, and to provide an equation for predicting the response variable from the explanatory variables.

Simple linear regression

When the regression model contains one dependent variable and one independent variable, the approach is called simple linear regression.

The data set women in the base installation provides the height and weight for a set of 15 women ages 30 to 39. Suppose you want to predict weight from height. Having an equation for predicting weight from height can help you to identify overweight or underweight individuals.

women
fit <- lm(weight ~ height, data=women)
summary(fit)

plot(women$height,women$weight,
     xlab="Height (in inches)",
     ylab="Weight (in pounds)")
abline(fit)
plot(women$height,residuals(fit),
     xlab="Height (in inches)",
     ylab="residuals")

The resulting predictive equation is:


From the Pr(>|t|) column, you see that the regression coefficient (3.45) is significantly different from zero (p < 0.001) and indicates that there is an expected increase of 3.45 pounds of weight for every 1 inch increase in height. The multiple R-squared (0.991) indicates that the model accounts for 99.1% of the variance in weights. The residual standard error (1.53 pounds) can be thought of as the average error in predicting weight from height using this model. The F statistic tests whether the predictor variables, taken together, predict the response variable above chance levels.

Polynomial regression

The residuals plot from the linear regression, indicates a non-random distribution of the prediction error of the linear model. You might be able to improve your prediction using a regression with a quadratic term (that is, X2), so we have now a polynomial regression, quadratic in this case.

fit2 <- lm(weight ~ height + I(height^2), data=women)
summary(fit2)

plot(women$height,women$weight,
     xlab="Height (in inches)",
     ylab="Weight (in lbs)")
lines(women$height,fitted(fit2))
plot(women$height,residuals(fit2),
     xlab="Height (in inches)",
     ylab="residuals")

The new polynomial equation is:


The amount of variance accounted for has increased to 99.9%. The significance of the squared term (t = 13.89, p < .001) suggests that inclusion of the quadratic term improves the model fit. The prediction error (residuals) is smaller, and their distribution looks more random.

Exploring linear regression with ggplot2

Now we are going to look the mtcars to explore the possible relationship between fuel efficiency (mpg) and the weight of the vehicle. We will use ggplot2 to build graphs that use a linear regression model to fit a regression line and show its uncertainty (confidence interval).

How do you expect is the relationship?

mtcars <- read.csv("mtcars.csv", header = TRUE)
mtcars
ggplot(data=mtcars, aes(x=wt, y=mpg)) +
  geom_point() +
  labs(x="Weight, x1000 lb", y="Miles Per Gallon")

A linear fit and its confidence interval show more information about the model we are using:

ggplot(data=mtcars, aes(x=wt, y=mpg)) +
  geom_point(pch=19, color="blue", size=2) +
  geom_smooth(method="lm", color="red", linetype=2) +
  labs(x="Weight, x1000 lb", y="Miles Per Gallon")

According to the graph, the linear model and the relationship requires a more thorough analysis to reach a conclusion.

Now we can explore in more details, what kind of car has a better fuel efficiency, and look if the mpg vs weight relationship is present on each group of car.

# first we have to convert character variables to factors
mtcars <- read.csv("mtcars.csv", header = TRUE)
mtcars$am <- factor(mtcars$am, levels=c(0,1),
                    labels=c("Automatic", "Manual"))
mtcars$vs <- factor(mtcars$vs, levels=c(0,1),
                    labels=c("V-Engine", "Straight Engine"))
mtcars$cyl <- factor(mtcars$cyl)
# multiple graphs for groups according motor characteristics
ggplot(data=mtcars, aes(x=wt, y=mpg,
                        shape=cyl, color=cyl)) +
  geom_point(size=3) +
  facet_grid(am~vs) +
  labs(x="Weight, x1000 lb", y="Miles Per Gallon")

To look if the efficiency-weight relationship holds for different groups, we can include the regression line and confidence interval.

ggplot(data=mtcars, aes(x=wt, y=mpg)) + 
  geom_point(aes(shape=cyl, color=cyl), size=2) +
  geom_smooth(aes(x=wt, y=mpg), method="lm", color="red", size=0.5, linetype=2) +
  facet_grid(am~vs) + 
  labs(x="Weight, x1000 lb", y="Miles Per Gallon")

Scatter plot matrix with regression lines, and extras

There is a special package for regression called car which provide highly detailed visualization of multiple scatter plots with regression lines, with few lines of codes.

Here we are going to produce a scatter plot matrix with the mtcars data, using the scatterplotMatrix function.

mtcars <- read.csv("mtcars.csv")
scatterplotMatrix(~ mpg + disp + drat + wt, data=mtcars, 
                  regLine = list(lty = 1, col = "gray"), 
                  smooth = FALSE, col = "blue")

Not as elegant as ggplot2, but easy and fast to implement.

Smoothing scatter plots

With ggplot2 you can use methods for adding smoothed lines (linear, nonlinear, and nonparametric) to scatter plots. You can use the geom_smooth() function to add a variety of smoothed lines and confidence regions.

We will use the Salaries dataset (associated to the car package) to illustrate the use of smoothing, and to examine the relationship between years since obtaining a PhD and the annual salary of professors.

Salaries
#to compare a linear model with the LOESS smoothing
ggplot(data=Salaries, aes(x=yrs.since.phd, y=salary)) +
  geom_smooth(method = "lm", col = "green") + geom_smooth(method = "loess") +
  geom_point(col = "blue") +
  labs(x="Years since obtaining PhD", y="Salary")

Here we are separating male and females professors, and using a polynomial (quadratic) model.

ggplot(data=Salaries, aes(x=yrs.since.phd, y=salary,linetype=sex, shape=sex, color=sex)) +
  geom_smooth(method=lm, formula=y~poly(x,2), se=FALSE, size=1) +
  geom_point(size=2) +
  labs(x="Years since obtaining PhD", y="Salary")

Bubble plots: the third dimension in two dimensions

A bubble plot is a scatterplot where a third dimension is added: the value of an additional variable is represented through the size of the dots. You need three numerical variables as input: one is represented by the X axis, one by the Y axis, and one by the size.

Using the mtcars dataset we will look again fot the relationship between efficiency (mpg), and weight, but also looking for the variable disp (engine displacement), in the same graph.

mtcars <- read.csv("mtcars.csv", header = TRUE)
mtcars
ggplot(data=mtcars, aes(x=wt, y=mpg)) +
  geom_point(aes(size=disp),colour = "lightblue") +
  geom_text(aes(label=model), size=2, angle=45) +
  scale_size_continuous(range=c(2,15), name = "disp., cu.in.") +
  labs(x="Weight, x1000 lb", y="Miles per Gallon")
LS0tCnRpdGxlOiAiRGF5X1R3byIKYXV0aG9yOiAiRC5fUy5fRmVybsOhbmRlei1kZWwtVmlzbyIKZGF0ZTogIjcvMTgvMjAxOCIKb3V0cHV0OgogIAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiA1CiAgaHRtbF9kb2N1bWVudDoKICAgIGRmX3ByaW50OiBwYWdlZAotLS0KX19TeXN0ZW0gcHJlcGFyYXRpb25fXwpgYGB7ciBzZXR1cH0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAoJZWNobyA9IFRSVUUsCgltZXNzYWdlID0gRkFMU0UsCgl3YXJuaW5nID0gRkFMU0UKKQojIHBhY2thZ2VzIHRvIGxvYWQgKGlmIHRoZXkgYXJlIGFscmVhZHkgaW5zdGFsbGVkKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobGF0dGljZSkKbGlicmFyeShnZ3ZlcnNhKQpsaWJyYXJ5KGNhcikKbGlicmFyeShjb3dwbG90KQpgYGAKCgoKIyMjSW50cm9kdWN0aW9uIHRvIGdyYXBocwpSIGlzIGEgZ3JlYXQgcGxhdGZvcm0gZm9yIF9idWlsZGluZ18gZ3JhcGhzLiBMaXRlcmFsbHksIGluIGEgdHlwaWNhbCBpbnRlcmFjdGl2ZSBzZXNzaW9uLCB5b3UgYnVpbGQgYSBncmFwaCBvbmUgc3RhdGVtZW50IGF0IGEgdGltZSwgYWRkaW5nIGZlYXR1cmVzLCB1bnRpbCB5b3UgaGF2ZSB3aGF0IHlvdSB3YW50LiAgClRoZSBbYmFzZSBncmFwaGljIHN5c3RlbV0oaHR0cHM6Ly9saXZlYm9vay5tYW5uaW5nLmNvbSMhL2Jvb2svci1pbi1hY3Rpb24tc2Vjb25kLWVkaXRpb24vY2hhcHRlci0zL3BvaW50LTI3ODMtMTAtMTQtMCkgY2hhcHRlciBvZiB0aGUgYm9vaywgcHJvdmlkZXMgaW5mb3JtYXRpb24gb24gaG93IG1vZGlmeSBhbmQgY3VzdG9taXplIGdyYXBocy4KVHdvIG90aGVyIHN5c3RlbXMsIHRoYXQgYXJlIHdpZGVseSB1c2VkLCBhbmQgcHJvdmlkZSBleHRlbnNpdmUgb3B0aW9ucyBhcmUgW19fbGF0dGljZV9fXSh3d3cuc3RhdG1ldGhvZHMubmV0L1JpQS9sYXR0aWNlLnBkZikgYW5kIFtfX2dncGxvdDJfX10oaHR0cHM6Ly9saXZlYm9vay5tYW5uaW5nLmNvbSMhL2Jvb2svci1pbi1hY3Rpb24tc2Vjb25kLWVkaXRpb24vY2hhcHRlci0xOS9wb2ludC0yNzg0LTEtNS0wKS4gIFdlIHdpbGwgYmUgbW9zdGx5IHVzaW5nIHRoZSBiYXNlIGdyYXBoaWMgc3lzdGVtIGFuZCBnZ3Bsb3QyLgoKIyMjI1RoZSBwcmltYXJ5IGdyYXBoIGZvciBhIHZhcmlhYmxlOiB0aGUgaGlzdG9ncmFtCkhpc3RvZ3JhbXMgZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIGEgY29udGludW91cyB2YXJpYWJsZSBieSBkaXZpZGluZyB0aGUgcmFuZ2Ugb2Ygc2NvcmVzIGludG8gYSBzcGVjaWZpZWQgbnVtYmVyIG9mIGJpbnMgb24gdGhlIHgtYXhpcyBhbmQgZGlzcGxheWluZyB0aGUgZnJlcXVlbmN5IG9mIHNjb3JlcyBpbiBlYWNoIGJpbiBvbiB0aGUgeS1heGlzLiAKCiMjIyMjQmFzaWMgaGlzdG9ncmFtcwpUaGUgZm9sbG93aW5nIGNodW5rIHByb2R1Y2VzIGZvdXIgaGlzdG9ncmFtcywgdXNpbmcgdGhlIFIgYmFzZSBncmFwaGljIHN5c3RlbS4gCmBgYHtyIGJhc2ljX2hpc3RvZ3JhbSwgZWNobz1UUlVFfQojcmVhZGluZyBkYXRhIG9uIE1lbG9jYWN0dXMgaW50b3J0dXMKbWVsb2RhdGEgPC0gcmVhZC5jc3YoIm1lbG9jYWN0dXMuY3N2IiwgaGVhZGVyID0gVFJVRSkKI3RoaXMgaXMgdG8gcHJvZHVjZSBhIDJ4MiBncmFwaHMgYXJyYW5nZW1lbnQKcGFyKG1mcm93PWMoMiwyKSkKIyB0aGUgbW9zdCBiYXNpYyBmb3JtCmhpc3QobWVsb2RhdGEkYWx0dXJhdG90YWwpCQoKIyBjb250cm9sbGluZyBzb21lIGFzcGVjdHMgb2YgdGhlIGhpc3RvZ3JhbQpoaXN0KG1lbG9kYXRhJGFsdHVyYXRvdGFsLAogICAgIGJyZWFrcz0xMiwKICAgICBjb2w9ImdyZWVuIiwKICAgICB4bGFiPSJBbHR1cmEgZGUgbGEgcGxhbnRhLCBjbSIsCiAgICAgbWFpbj0iQ29sb3JlZCBoaXN0b2dyYW0gd2l0aCAxMiBiaW5zIikKCiMgd2l0aCBhIGRlbnNpdHkgY3VydmUgYW5kIHJ1ZyBwbG90IApoaXN0KG1lbG9kYXRhJGFsdHVyYXRvdGFsLAogICAgIGZyZXE9RkFMU0UsCiAgICAgYnJlYWtzPTEyLAogICAgIGNvbD0iZ3JlZW4iLAogICAgIHhsYWI9IkFsdHVyYSBkZSBsYSBwbGFudGEsIGNtIiwKICAgICBtYWluPSJIaXN0b2dyYW0sIHJ1ZyBwbG90LCBkZW5zaXR5IGN1cnZlIikKcnVnKGppdHRlcihtZWxvZGF0YSRhbHR1cmF0b3RhbCkpCmxpbmVzKGRlbnNpdHkobWVsb2RhdGEkYWx0dXJhdG90YWwpLCBjb2w9InJlZCIsIGx3ZD0yKQoKIyBpbmNsdWRpbmcgYSBub3JtYWwgY3VydmUgYmFzZWQgb24gdGhlIGRhdGEKeCA8LSBtZWxvZGF0YSRhbHR1cmF0b3RhbApoPC1oaXN0KHgsCiAgICAgICAgYnJlYWtzPTEyLAogICAgICAgIGNvbD0iZ3JlZW4iLAogICAgICAgIHhsYWI9IkFsdHVyYSBkZSBsYSBwbGFudGEsIGNtIiwKICAgICAgICBtYWluPSJIaXN0b2dyYW0gd2l0aCBub3JtYWwgY3VydmUgYW5kIGJveCIpCnhmaXQ8LXNlcShtaW4oeCksIG1heCh4KSwgbGVuZ3RoPTgwKQp5Zml0PC1kbm9ybSh4Zml0LCBtZWFuPW1lYW4oeCksIHNkPXNkKHgpKQp5Zml0IDwtIHlmaXQqZGlmZihoJG1pZHNbMToyXSkqbGVuZ3RoKHgpCmxpbmVzKHhmaXQsIHlmaXQsIGNvbD0icmVkIiwgbHdkPTIpCmJveCgpCmBgYAoKIyMjIyNJbnRyb2R1Y2luZyBnZ3Bsb3QyOiBidWlsZGluZyBhIGhpc3RvZ3JhbQpOb3cgd2UgYXJlIGdvaW5nIHRvIGJ1aWxkIGEgaGlzdG9ncmFtIHVzaW5nIGdncGxvdDIuICBbZ2dwbG90Ml0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dncGxvdDIvZ2dwbG90Mi5wZGYpIHByb3ZpZGVzIGEgc3lzdGVtIGZvciBjcmVhdGluZyBncmFwaHMgYmFzZWQgb24gdGhlIGdyYW1tYXIgb2YgZ3JhcGhpY3MuIFRoZSBpbnRlbnRpb24gb2YgdGhlIGdncGxvdDIgcGFja2FnZSBpcyB0byBwcm92aWRlIGEgY29tcHJlaGVuc2l2ZSwgZ3JhbW1hci1iYXNlZCBzeXN0ZW0gZm9yIGdlbmVyYXRpbmcgZ3JhcGhzIGluIGEgdW5pZmllZCBhbmQgY29oZXJlbnQgbWFubmVyLCBhbGxvd2luZyB1c2VycyB0byBjcmVhdGUgbmV3IGFuZCBpbm5vdmF0aXZlIGRhdGEgdmlzdWFsaXphdGlvbnMuIFRoZSBwb3dlciBvZiB0aGlzIGFwcHJvYWNoIGhhcyBsZWQgZ2dwbG90MiB0byBiZWNvbWUgYW4gaW1wb3J0YW50IHRvb2wgZm9yIHZpc3VhbGl6aW5nIGRhdGEgdXNpbmcgUi4KCkZpcnN0LCBsZXQgc2VlIGEgYmFzaWMgZ2dwbG90MiBoaXN0b2dyYW06CmBgYHtyIGdncGxvdDJfaGlzdG9fYmFzaWMsIGVjaG89VFJVRSwgbWVzc2FnZT1UUlVFLCBwYWdlZC5wcmludD1UUlVFfQptZWxvZGF0YSA8LSByZWFkLmNzdigibWVsb2NhY3R1cy5jc3YiLCBoZWFkZXIgPSBUUlVFKQoKZ2dwbG90KG1lbG9kYXRhLCBhZXMoYWx0dXJhdG90YWwpKSsKICBnZW9tX2hpc3RvZ3JhbShjb2xvcj0id2hpdGUiLCBiaW5zID0gMTQpCmBgYAoKTm93IGEgbW9yZSBkZXRhaWxlZCBoaXN0b2dyYW0sIGluY2x1ZGluZyBzZXZlcmFsIF9sYXllcnNfOgpgYGB7ciBnZ3Bsb3QyX2hpc3RvX2Z1bGx9Cmhpc3QubWVsb2RhdGEgPC0gZ2dwbG90KG1lbG9kYXRhLCBhZXMoYWx0dXJhdG90YWwpKSArIAogIGdlb21faGlzdG9ncmFtKGFlcyh5PS4uZGVuc2l0eS4uKSwgYmlucyA9IDE0LCBjb2xvdXI9IndoaXRlIiwgZmlsbD0iZ3JlZW4iKSArCiAgZ2VvbV9ydWcoc2lkZXMgPSAiYiIsIGNvbG9yID0gImJsYWNrIikgKwogIGxhYnMoeD0iQWx0dXJhIHRvdGFsIGRlIGxhIHBsYW50YSxjbSIsIHkgPSAiRGVuc2l0eSIpICsKICBzdGF0X2Z1bmN0aW9uKGZ1biA9IGRub3JtLCAKICAgICAgICAgICAgICAgIGFyZ3MgPSBsaXN0KG1lYW4gPSBtZWFuKG1lbG9kYXRhJGFsdHVyYXRvdGFsLCBuYS5ybSA9IFRSVUUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNkID0gc2QobWVsb2RhdGEkYWx0dXJhdG90YWwsIG5hLnJtID0gVFJVRSkpLCAKICAgICAgICAgICAgICAgIGNvbG91ciA9ICJyZWQiLCBzaXplID0gMSkKaGlzdC5tZWxvZGF0YQpgYGAKCiMjIyNUaGUgYW5hdG9teSBvZiBhIGJveC1hbmQtd2hpc2tlcnMgcGxvdApBIFtib3gtYW5kLXdoaXNrZXJzIHBsb3RdKGh0dHBzOi8vbGl2ZWJvb2subWFubmluZy5jb20jIS9ib29rL3ItaW4tYWN0aW9uLXNlY29uZC1lZGl0aW9uL2NoYXB0ZXItNi9wb2ludC0yNzg1LTEwMS0xMDItMCkgZGVzY3JpYmVzIHRoZSBkaXN0cmlidXRpb24gb2YgYSBjb250aW51b3VzIHZhcmlhYmxlIGJ5IHBsb3R0aW5nIGl0cyBmaXZlLW51bWJlciBzdW1tYXJ5OiB0aGUgbWluaW11bSwgbG93ZXIgcXVhcnRpbGUgKDI1dGggcGVyY2VudGlsZSksIG1lZGlhbiAoNTB0aCBwZXJjZW50aWxlKSwgdXBwZXIgcXVhcnRpbGUgKDc1dGggcGVyY2VudGlsZSksIGFuZCBtYXhpbXVtLiBJdCBjYW4gYWxzbyBkaXNwbGF5IG9ic2VydmF0aW9ucyB0aGF0IG1heSBiZSBvdXRsaWVycyAodmFsdWVzIG91dHNpZGUgdGhlIHJhbmdlIG9mIMKxIDEuNSpJUVIsIHdoZXJlIElRUiBpcyB0aGUgaW50ZXJxdWFydGlsZSByYW5nZSBkZWZpbmVkIGFzIHRoZSB1cHBlciBxdWFydGlsZSBtaW51cyB0aGUgbG93ZXIgcXVhcnRpbGUpLiBCeSBkZWZhdWx0LCBlYWNoIHdoaXNrZXIgZXh0ZW5kcyB0byB0aGUgbW9zdCBleHRyZW1lIGRhdGEgcG9pbnQsIHdoaWNoIGlzIG5vIG1vcmUgdGhhbiAxLjUgdGltZXMgdGhlIGludGVycXVhcnRpbGUgcmFuZ2UgZm9yIHRoZSBib3guIFZhbHVlcyBvdXRzaWRlIHRoaXMgcmFuZ2UgYXJlIGRlcGljdGVkIGFzIGRvdHMuCgo+IVtdKGJveHBsb3QuanBnKXsjaWQgLmNsYXNzIHdpZHRoPTM2MCBoZWlnaHQ9MjQwcHh9XAoKTm93IHdlIGFyZSBnb2luZyB0byBhbmFseXplIHRoZSBfTWVsb2NhY3R1c18gZGF0YSB1c2luZyBhIGJveC1wbG90LgpgYGB7ciBnZ3Bsb3QyX2JveHBsb3R9CmdncGxvdChtZWxvZGF0YSwgYWVzKHggPSAiMCIsIHkgPSBhbHR1cmF0b3RhbCkpICsKICBnZW9tX2JveHBsb3QoZmlsbD0iY29ybmZsb3dlcmJsdWUiLCBjb2xvcj0iYmxhY2siKSArCiAgZ2VvbV9wb2ludChwb3NpdGlvbj0iaml0dGVyIiwgc2l6ZSA9IDAuNSwgY29sb3I9ImJsdWUiLCBhbHBoYT0uNSkgKwogIGxhYnMoeCA9ICJNZWxvY2FjdHVzIGludG9ydHVzIiwgeSA9ICJBbHR1cmEgdG90YWwgZGUgbGEgcGxhbnRhLCBjbSIpCmBgYAoKUmVsYXRlZCB0byBib3gtcGxvdHMgYXJlIHRoZSBbdmlvbGluIHBsb3RzXShodHRwczovL2xpdmVib29rLm1hbm5pbmcuY29tIyEvYm9vay9yLWluLWFjdGlvbi1zZWNvbmQtZWRpdGlvbi9jaGFwdGVyLTE5L3BvaW50LTI3ODYtNjItNjQtMCk7IHRoZSB2aW9saW4gcGxvdHMgcHJvdmlkZSBtb3JlIHZpc3VhbCBjdWVzIGFzIHRvIHRoZSBkaXN0cmlidXRpb24gb2Ygc2NvcmVzIG92ZXIgdGhlIHJhbmdlIG9mIGhlaWdodHMgZm9yIGVhY2ggdm9pY2UgcGFydC4KYGBge3IgZ2dwbG90Ml92aW9saW59CmdncGxvdChtZWxvZGF0YSwgYWVzKHg9IjAiLCB5PWFsdHVyYXRvdGFsKSkgKwogIGdlb21fdmlvbGluKGZpbGw9ImxpZ2h0Ymx1ZSIpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCBhbHBoYSA9IDAuMykgKwogIGxhYnMoeCA9ICJNZWxvY2FjdHVzIGludG9ydHVzIiwgeSA9ICJBbHR1cmEgdG90YWwgZGUgbGEgcGxhbnRhLCBjbSIpCmBgYAoKIyMjI19FeGVyY2lzZXNfCkJ1aWxkIGEgZ3JhcGgsIHdpdGggdGhlIE1lbG9jYWN0dXMgZGF0YSwgdGhhdCBjb21iaW5lcyB0aGUgYm94LXBsb3QgYW5kIHZpb2xpbiBwbG90LgoKIyMjI0JhciBncmFwaHMgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlcwpDb3VudHMgb2YgY2FzZXMgZm9yIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGFyZSB1c3VhbGx5IHByZXNlbnRlZCB1c2luZyBiYXIgZ3JhcGhzLiAgSGVyZSB3ZSB1c2UgZGF0YSBmcm9tIGEgY2xpbmljYWwgdHJpYWwgb2YgYSB0cmVhdG1lbnQgZm9yIGFydGhyaXRpcywgY29tcGFyaW5nIHRoZSBvdXRjb21lcyBmb3IgdHJlYXRlZCBpbmRpdmlkdWFscyBfdmVyc3VzXyBpbmRpdmlkdWFscyByZWNlaXZpbmcgYSBwbGFjZWJvLiAKYGBge3IgfQpsb2FkKCJBcnRoci5SZGF0YSIpCmhlYWQoQXJ0aHJpdGlzKQpBIDwtIGdncGxvdChBcnRocml0aXMsIGFlcyh4PVRyZWF0bWVudCwgZmlsbD1JbXByb3ZlZCkpICsKICAgICBnZW9tX2Jhcihwb3NpdGlvbj0ic3RhY2siKQpCIDwtIGdncGxvdChBcnRocml0aXMsIGFlcyh4PVRyZWF0bWVudCwgZmlsbD1JbXByb3ZlZCkpICsKICAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZG9kZ2UiKQpDIDwtIGdncGxvdChBcnRocml0aXMsIGFlcyh4PVRyZWF0bWVudCwgZmlsbD1JbXByb3ZlZCkpICsKICAgICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIpCiN1c2luZyB0aGUgcGFja2FnZSBjb3dwbG90IHRvIGdyb3VwIGdyYXBocyBidWlsdCBzZXBhcmF0ZWxseQpwbG90X2dyaWQoQSwgQiwgQywgbmNvbCA9IDIsIGxhYmVscyA9ICJBVVRPIikKYGBgCgojIyMjX0V4ZXJjaXNlXwpUcnkgdG8gaW1wcm92ZSB0aGUgZ3JhcGhzLCB3aXRoIHNtYWxsZXIgZm9udHMuCgoqKioKCiMjI1N0YXRpc3RpY2FsIHRlc3RzIGFuZCBncmFwaHMgZm9yIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0d28gb3IgbW9yZSB2YXJpYWJsZXMKSW4gdGhpcyBzZWN0aW9uIHdlIHdpbGwgY29uc2lkZXIgdGhlIHRlc3Rpbmcgb2Ygc3RhdGlzdGljYWwgaHlwb3RoZXNpcyBmb3IgdGhlIHJlbGF0aW9uc2hpcCBvZiB0d28gb3IgbW9yZSB2YXJpYWJsZXMsIGFuZCB0aGUgZ3JhcGhzIHRoYXQgd2lsbCBoZWxwIGluIHRoZSBpbnRlcnByZXRhdGlvbiBvZiB0aGUgcmVzdWx0cy4KCiMjIyNSZWdyZXNzaW9uIGFuYWx5c2lzCltSZWdyZXNzaW9uIGFuYWx5c2lzXShodHRwczovL2xpdmVib29rLm1hbm5pbmcuY29tIyEvYm9vay9yLWluLWFjdGlvbi1zZWNvbmQtZWRpdGlvbi9jaGFwdGVyLTgvcG9pbnQtMjc5My0xLTUtMCkgaXMgYSBicm9hZCB0ZXJtIGZvciBhIHNldCBvZiBtZXRob2RvbG9naWVzIHVzZWQgdG8gcHJlZGljdCBhIF9yZXNwb25zZSB2YXJpYWJsZV8gKGFsc28gY2FsbGVkIGEgX2RlcGVuZGVudF8sIF9jcml0ZXJpb25fLCBvciBfb3V0Y29tZSB2YXJpYWJsZV8pIGZyb20gb25lIG9yIG1vcmUgX3ByZWRpY3RvciB2YXJpYWJsZXNfIChhbHNvIGNhbGxlZCBfaW5kZXBlbmRlbnRfIG9yIF9leHBsYW5hdG9yeSB2YXJpYWJsZXNfKS4gSW4gZ2VuZXJhbCwgcmVncmVzc2lvbiBhbmFseXNpcyBjYW4gYmUgdXNlZCB0byBpZGVudGlmeSB0aGUgZXhwbGFuYXRvcnkgdmFyaWFibGVzIHRoYXQgYXJlIHJlbGF0ZWQgdG8gYSByZXNwb25zZSB2YXJpYWJsZSwgdG8gZGVzY3JpYmUgdGhlIGZvcm0gb2YgdGhlIHJlbGF0aW9uc2hpcHMgaW52b2x2ZWQsIGFuZCB0byBwcm92aWRlIGFuIGVxdWF0aW9uIGZvciBwcmVkaWN0aW5nIHRoZSByZXNwb25zZSB2YXJpYWJsZSBmcm9tIHRoZSBleHBsYW5hdG9yeSB2YXJpYWJsZXMuCgojIyMjI1NpbXBsZSBsaW5lYXIgcmVncmVzc2lvbgpXaGVuIHRoZSByZWdyZXNzaW9uIG1vZGVsIGNvbnRhaW5zIG9uZSBkZXBlbmRlbnQgdmFyaWFibGUgYW5kIG9uZSBpbmRlcGVuZGVudCB2YXJpYWJsZSwgdGhlIGFwcHJvYWNoIGlzIGNhbGxlZCBbc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uXShodHRwczovL2xpdmVib29rLm1hbm5pbmcuY29tIyEvYm9vay9yLWluLWFjdGlvbi1zZWNvbmQtZWRpdGlvbi9jaGFwdGVyLTgvcG9pbnQtMjc5NC02NC02NS0wKS4KClRoZSBkYXRhIHNldCBfX3dvbWVuX18gaW4gdGhlIGJhc2UgaW5zdGFsbGF0aW9uIHByb3ZpZGVzIHRoZSBoZWlnaHQgYW5kIHdlaWdodCBmb3IgYSBzZXQgb2YgMTUgd29tZW4gYWdlcyAzMCB0byAzOS4gU3VwcG9zZSB5b3Ugd2FudCB0byBwcmVkaWN0IHdlaWdodCBmcm9tIGhlaWdodC4gSGF2aW5nIGFuIGVxdWF0aW9uIGZvciBwcmVkaWN0aW5nIHdlaWdodCBmcm9tIGhlaWdodCBjYW4gaGVscCB5b3UgdG8gaWRlbnRpZnkgb3ZlcndlaWdodCBvciB1bmRlcndlaWdodCBpbmRpdmlkdWFscy4gCmBgYHtyIHJlZ3JfbGluZWFyfQp3b21lbgpmaXQgPC0gbG0od2VpZ2h0IH4gaGVpZ2h0LCBkYXRhPXdvbWVuKQpzdW1tYXJ5KGZpdCkKCnBsb3Qod29tZW4kaGVpZ2h0LHdvbWVuJHdlaWdodCwKICAgICB4bGFiPSJIZWlnaHQgKGluIGluY2hlcykiLAogICAgIHlsYWI9IldlaWdodCAoaW4gcG91bmRzKSIpCmFibGluZShmaXQpCnBsb3Qod29tZW4kaGVpZ2h0LHJlc2lkdWFscyhmaXQpLAogICAgIHhsYWI9IkhlaWdodCAoaW4gaW5jaGVzKSIsCiAgICAgeWxhYj0icmVzaWR1YWxzIikKYGBgClRoZSByZXN1bHRpbmcgcHJlZGljdGl2ZSBlcXVhdGlvbiBpczoKCj4hW10oZXFsaW5lYXIuanBnKXsjaWQgLmNsYXNzIHdpZHRoPTI0MCBoZWlnaHQ9MTgwcHh9XAoKRnJvbSB0aGUgUHIoPnx0fCkgY29sdW1uLCB5b3Ugc2VlIHRoYXQgdGhlIHJlZ3Jlc3Npb24gY29lZmZpY2llbnQgKDMuNDUpIGlzIHNpZ25pZmljYW50bHkgZGlmZmVyZW50IGZyb20gemVybyAocCA8IDAuMDAxKSBhbmQgaW5kaWNhdGVzIHRoYXQgdGhlcmUgaXMgYW4gZXhwZWN0ZWQgaW5jcmVhc2Ugb2YgMy40NSBwb3VuZHMgb2Ygd2VpZ2h0IGZvciBldmVyeSAxIGluY2ggaW5jcmVhc2UgaW4gaGVpZ2h0LiBUaGUgbXVsdGlwbGUgUi1zcXVhcmVkICgwLjk5MSkgaW5kaWNhdGVzIHRoYXQgdGhlIG1vZGVsIGFjY291bnRzIGZvciA5OS4xJSBvZiB0aGUgdmFyaWFuY2UgaW4gd2VpZ2h0cy4gVGhlIHJlc2lkdWFsIHN0YW5kYXJkIGVycm9yICgxLjUzIHBvdW5kcykgY2FuIGJlIHRob3VnaHQgb2YgYXMgdGhlIGF2ZXJhZ2UgZXJyb3IgaW4gcHJlZGljdGluZyB3ZWlnaHQgZnJvbSBoZWlnaHQgdXNpbmcgdGhpcyBtb2RlbC4gVGhlIEYgc3RhdGlzdGljIHRlc3RzIHdoZXRoZXIgdGhlIHByZWRpY3RvciB2YXJpYWJsZXMsIHRha2VuIHRvZ2V0aGVyLCBwcmVkaWN0IHRoZSByZXNwb25zZSB2YXJpYWJsZSBhYm92ZSBjaGFuY2UgbGV2ZWxzLgoKIyMjIyNQb2x5bm9taWFsIHJlZ3Jlc3Npb24KVGhlIHJlc2lkdWFscyBwbG90IGZyb20gdGhlIGxpbmVhciByZWdyZXNzaW9uLCBpbmRpY2F0ZXMgYSBub24tcmFuZG9tIGRpc3RyaWJ1dGlvbiBvZiB0aGUgcHJlZGljdGlvbiBlcnJvciBvZiB0aGUgbGluZWFyIG1vZGVsLiAgWW91IG1pZ2h0IGJlIGFibGUgdG8gaW1wcm92ZSB5b3VyIHByZWRpY3Rpb24gdXNpbmcgYSByZWdyZXNzaW9uIHdpdGggYSBxdWFkcmF0aWMgdGVybSAodGhhdCBpcywgWDIpLCBzbyB3ZSBoYXZlIG5vdyBhIFtwb2x5bm9taWFsIHJlZ3Jlc3Npb25dKGh0dHBzOi8vbGl2ZWJvb2subWFubmluZy5jb20jIS9ib29rL3ItaW4tYWN0aW9uLXNlY29uZC1lZGl0aW9uL2NoYXB0ZXItOC9wb2ludC0yNzk1LTczLTc3LTApLCBxdWFkcmF0aWMgaW4gdGhpcyBjYXNlLgpgYGB7ciByZWdyX3BvbHlufQpmaXQyIDwtIGxtKHdlaWdodCB+IGhlaWdodCArIEkoaGVpZ2h0XjIpLCBkYXRhPXdvbWVuKQpzdW1tYXJ5KGZpdDIpCgpwbG90KHdvbWVuJGhlaWdodCx3b21lbiR3ZWlnaHQsCiAgICAgeGxhYj0iSGVpZ2h0IChpbiBpbmNoZXMpIiwKICAgICB5bGFiPSJXZWlnaHQgKGluIGxicykiKQpsaW5lcyh3b21lbiRoZWlnaHQsZml0dGVkKGZpdDIpKQpwbG90KHdvbWVuJGhlaWdodCxyZXNpZHVhbHMoZml0MiksCiAgICAgeGxhYj0iSGVpZ2h0IChpbiBpbmNoZXMpIiwKICAgICB5bGFiPSJyZXNpZHVhbHMiKQpgYGAKClRoZSBuZXcgcG9seW5vbWlhbCBlcXVhdGlvbiBpczoKCj4hW10oZXFwb2x5LmpwZyl7I2lkIC5jbGFzcyB3aWR0aD0zMjAgaGVpZ2h0PTI0MHB4fVwKClRoZSBhbW91bnQgb2YgdmFyaWFuY2UgYWNjb3VudGVkIGZvciBoYXMgaW5jcmVhc2VkIHRvIDk5LjklLiBUaGUgc2lnbmlmaWNhbmNlIG9mIHRoZSBzcXVhcmVkIHRlcm0gKHQgPSAxMy44OSwgcCA8IC4wMDEpIHN1Z2dlc3RzIHRoYXQgaW5jbHVzaW9uIG9mIHRoZSBxdWFkcmF0aWMgdGVybSBpbXByb3ZlcyB0aGUgbW9kZWwgZml0LiAgVGhlIHByZWRpY3Rpb24gZXJyb3IgKHJlc2lkdWFscykgaXMgc21hbGxlciwgYW5kIHRoZWlyIGRpc3RyaWJ1dGlvbiBsb29rcyBtb3JlIHJhbmRvbS4KCiMjIyNFeHBsb3JpbmcgbGluZWFyIHJlZ3Jlc3Npb24gd2l0aCBnZ3Bsb3QyCk5vdyB3ZSBhcmUgZ29pbmcgdG8gbG9vayB0aGUgX19tdGNhcnNfXyB0byBleHBsb3JlIHRoZSBwb3NzaWJsZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBmdWVsIGVmZmljaWVuY3kgKG1wZykgYW5kIHRoZSB3ZWlnaHQgb2YgdGhlIHZlaGljbGUuICBXZSB3aWxsIHVzZSBbZ2dwbG90Ml0oaHR0cHM6Ly9saXZlYm9vay5tYW5uaW5nLmNvbSMhL2Jvb2svci1pbi1hY3Rpb24tc2Vjb25kLWVkaXRpb24vY2hhcHRlci0xOS9wb2ludC0yODAwLTIxLTIyLTApIHRvIGJ1aWxkIGdyYXBocyB0aGF0IHVzZSBhIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsIHRvIGZpdCBhIHJlZ3Jlc3Npb24gbGluZSBhbmQgc2hvdyBpdHMgdW5jZXJ0YWludHkgKGNvbmZpZGVuY2UgaW50ZXJ2YWwpLgoKSG93IGRvIHlvdSBleHBlY3QgaXMgdGhlIHJlbGF0aW9uc2hpcD8KYGBge3IgZ2dwbG90Ml9sbX0KbXRjYXJzIDwtIHJlYWQuY3N2KCJtdGNhcnMuY3N2IiwgaGVhZGVyID0gVFJVRSkKbXRjYXJzCmdncGxvdChkYXRhPW10Y2FycywgYWVzKHg9d3QsIHk9bXBnKSkgKwogIGdlb21fcG9pbnQoKSArCiAgbGFicyh4PSJXZWlnaHQsIHgxMDAwIGxiIiwgeT0iTWlsZXMgUGVyIEdhbGxvbiIpCmBgYAoKQSBsaW5lYXIgZml0IGFuZCBpdHMgY29uZmlkZW5jZSBpbnRlcnZhbCBzaG93IG1vcmUgaW5mb3JtYXRpb24gYWJvdXQgdGhlIG1vZGVsIHdlIGFyZSB1c2luZzoKYGBge3IgZ2dwbG90Ml9sbV9jaX0KZ2dwbG90KGRhdGE9bXRjYXJzLCBhZXMoeD13dCwgeT1tcGcpKSArCiAgZ2VvbV9wb2ludChwY2g9MTksIGNvbG9yPSJibHVlIiwgc2l6ZT0yKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPSJsbSIsIGNvbG9yPSJyZWQiLCBsaW5ldHlwZT0yKSArCiAgbGFicyh4PSJXZWlnaHQsIHgxMDAwIGxiIiwgeT0iTWlsZXMgUGVyIEdhbGxvbiIpCmBgYAoKQWNjb3JkaW5nIHRvIHRoZSBncmFwaCwgdGhlIGxpbmVhciBtb2RlbCBhbmQgdGhlIHJlbGF0aW9uc2hpcCByZXF1aXJlcyBhIG1vcmUgdGhvcm91Z2ggYW5hbHlzaXMgdG8gcmVhY2ggYSBjb25jbHVzaW9uLgoKTm93IHdlIGNhbiBleHBsb3JlIGluIG1vcmUgZGV0YWlscywgd2hhdCBraW5kIG9mIGNhciBoYXMgYSBiZXR0ZXIgZnVlbCBlZmZpY2llbmN5LCBhbmQgbG9vayBpZiB0aGUgbXBnIF92c18gd2VpZ2h0IHJlbGF0aW9uc2hpcCBpcyBwcmVzZW50IG9uIGVhY2ggZ3JvdXAgb2YgY2FyLgpgYGB7ciBnZ3Bsb3QyX2ZhY2V0MX0KIyBmaXJzdCB3ZSBoYXZlIHRvIGNvbnZlcnQgY2hhcmFjdGVyIHZhcmlhYmxlcyB0byBmYWN0b3JzCm10Y2FycyA8LSByZWFkLmNzdigibXRjYXJzLmNzdiIsIGhlYWRlciA9IFRSVUUpCm10Y2FycyRhbSA8LSBmYWN0b3IobXRjYXJzJGFtLCBsZXZlbHM9YygwLDEpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJBdXRvbWF0aWMiLCAiTWFudWFsIikpCm10Y2FycyR2cyA8LSBmYWN0b3IobXRjYXJzJHZzLCBsZXZlbHM9YygwLDEpLAogICAgICAgICAgICAgICAgICAgIGxhYmVscz1jKCJWLUVuZ2luZSIsICJTdHJhaWdodCBFbmdpbmUiKSkKbXRjYXJzJGN5bCA8LSBmYWN0b3IobXRjYXJzJGN5bCkKIyBtdWx0aXBsZSBncmFwaHMgZm9yIGdyb3VwcyBhY2NvcmRpbmcgbW90b3IgY2hhcmFjdGVyaXN0aWNzCmdncGxvdChkYXRhPW10Y2FycywgYWVzKHg9d3QsIHk9bXBnLAogICAgICAgICAgICAgICAgICAgICAgICBzaGFwZT1jeWwsIGNvbG9yPWN5bCkpICsKICBnZW9tX3BvaW50KHNpemU9MykgKwogIGZhY2V0X2dyaWQoYW1+dnMpICsKICBsYWJzKHg9IldlaWdodCwgeDEwMDAgbGIiLCB5PSJNaWxlcyBQZXIgR2FsbG9uIikKYGBgCgpUbyBsb29rIGlmIHRoZSBlZmZpY2llbmN5LXdlaWdodCByZWxhdGlvbnNoaXAgaG9sZHMgZm9yIGRpZmZlcmVudCBncm91cHMsIHdlIGNhbiBpbmNsdWRlIHRoZSByZWdyZXNzaW9uIGxpbmUgYW5kIGNvbmZpZGVuY2UgaW50ZXJ2YWwuCmBgYHtyIGdncGxvdDJfZmFjZXQyfQpnZ3Bsb3QoZGF0YT1tdGNhcnMsIGFlcyh4PXd0LCB5PW1wZykpICsgCiAgZ2VvbV9wb2ludChhZXMoc2hhcGU9Y3lsLCBjb2xvcj1jeWwpLCBzaXplPTIpICsKICBnZW9tX3Ntb290aChhZXMoeD13dCwgeT1tcGcpLCBtZXRob2Q9ImxtIiwgY29sb3I9InJlZCIsIHNpemU9MC41LCBsaW5ldHlwZT0yKSArCiAgZmFjZXRfZ3JpZChhbX52cykgKyAKICBsYWJzKHg9IldlaWdodCwgeDEwMDAgbGIiLCB5PSJNaWxlcyBQZXIgR2FsbG9uIikKYGBgCgojIyMjU2NhdHRlciBwbG90IG1hdHJpeCB3aXRoIHJlZ3Jlc3Npb24gbGluZXMsIGFuZCBleHRyYXMKVGhlcmUgaXMgYSBzcGVjaWFsIHBhY2thZ2UgZm9yIHJlZ3Jlc3Npb24gY2FsbGVkIF9fY2FyX18gd2hpY2ggcHJvdmlkZSBoaWdobHkgZGV0YWlsZWQgdmlzdWFsaXphdGlvbiBvZiBtdWx0aXBsZSBzY2F0dGVyIHBsb3RzIHdpdGggcmVncmVzc2lvbiBsaW5lcywgd2l0aCBmZXcgbGluZXMgb2YgY29kZXMuCgpIZXJlIHdlIGFyZSBnb2luZyB0byBwcm9kdWNlIGEgW3NjYXR0ZXIgcGxvdCBtYXRyaXhdKGh0dHBzOi8vbGl2ZWJvb2subWFubmluZy5jb20jIS9ib29rL3ItaW4tYWN0aW9uLXNlY29uZC1lZGl0aW9uL2NoYXB0ZXItMTEvcG9pbnQtMjc5OS0zMi0zMy0wKSB3aXRoIHRoZSBfX210Y2Fyc19fIGRhdGEsIHVzaW5nIHRoZSBfX3NjYXR0ZXJwbG90TWF0cml4X18gZnVuY3Rpb24uCmBgYHtyIHNjYXR0ZXJwbG90X21hdHJpeH0KbXRjYXJzIDwtIHJlYWQuY3N2KCJtdGNhcnMuY3N2IikKc2NhdHRlcnBsb3RNYXRyaXgofiBtcGcgKyBkaXNwICsgZHJhdCArIHd0LCBkYXRhPW10Y2FycywgCiAgICAgICAgICAgICAgICAgIHJlZ0xpbmUgPSBsaXN0KGx0eSA9IDEsIGNvbCA9ICJncmF5IiksIAogICAgICAgICAgICAgICAgICBzbW9vdGggPSBGQUxTRSwgY29sID0gImJsdWUiKQpgYGAKTm90IGFzIGVsZWdhbnQgYXMgZ2dwbG90MiwgYnV0IGVhc3kgYW5kIGZhc3QgdG8gaW1wbGVtZW50LgoKIyMjI1Ntb290aGluZyBzY2F0dGVyIHBsb3RzCldpdGggZ2dwbG90MiB5b3UgY2FuIHVzZSBtZXRob2RzIGZvciBhZGRpbmcgW3Ntb290aGVkIGxpbmVzXShodHRwczovL2xpdmVib29rLm1hbm5pbmcuY29tIyEvYm9vay9yLWluLWFjdGlvbi1zZWNvbmQtZWRpdGlvbi9jaGFwdGVyLTE5L3BvaW50LTI4MDEtMTA1LTEwNy0wKSAobGluZWFyLCBub25saW5lYXIsIGFuZCBub25wYXJhbWV0cmljKSB0byBzY2F0dGVyIHBsb3RzLiAgWW91IGNhbiB1c2UgdGhlIF9fZ2VvbV9zbW9vdGgoKV9fIGZ1bmN0aW9uIHRvIGFkZCBhIHZhcmlldHkgb2Ygc21vb3RoZWQgbGluZXMgYW5kIGNvbmZpZGVuY2UgcmVnaW9ucy4gCgpXZSB3aWxsIHVzZSB0aGUgX19TYWxhcmllc19fIGRhdGFzZXQgKGFzc29jaWF0ZWQgdG8gdGhlIF9fY2FyX18gcGFja2FnZSkgdG8gaWxsdXN0cmF0ZSB0aGUgdXNlIG9mIHNtb290aGluZywgYW5kIHRvIGV4YW1pbmUgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHllYXJzIHNpbmNlIG9idGFpbmluZyBhIFBoRCBhbmQgdGhlIGFubnVhbCBzYWxhcnkgb2YgcHJvZmVzc29ycy4KYGBge3Igc21vb3RoX2xvZXNzfQpTYWxhcmllcwojdG8gY29tcGFyZSBhIGxpbmVhciBtb2RlbCB3aXRoIHRoZSBMT0VTUyBzbW9vdGhpbmcKZ2dwbG90KGRhdGE9U2FsYXJpZXMsIGFlcyh4PXlycy5zaW5jZS5waGQsIHk9c2FsYXJ5KSkgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIGNvbCA9ICJncmVlbiIpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxvZXNzIikgKwogIGdlb21fcG9pbnQoY29sID0gImJsdWUiKSArCiAgbGFicyh4PSJZZWFycyBzaW5jZSBvYnRhaW5pbmcgUGhEIiwgeT0iU2FsYXJ5IikKYGBgCgpIZXJlIHdlIGFyZSBzZXBhcmF0aW5nIG1hbGUgYW5kIGZlbWFsZXMgcHJvZmVzc29ycywgYW5kIHVzaW5nIGEgcG9seW5vbWlhbCAocXVhZHJhdGljKSBtb2RlbC4KYGBge3Igc21vb3RoX2dyb3VwX3BvbHlufQpnZ3Bsb3QoZGF0YT1TYWxhcmllcywgYWVzKHg9eXJzLnNpbmNlLnBoZCwgeT1zYWxhcnksbGluZXR5cGU9c2V4LCBzaGFwZT1zZXgsIGNvbG9yPXNleCkpICsKICBnZW9tX3Ntb290aChtZXRob2Q9bG0sIGZvcm11bGE9eX5wb2x5KHgsMiksIHNlPUZBTFNFLCBzaXplPTEpICsKICBnZW9tX3BvaW50KHNpemU9MikgKwogIGxhYnMoeD0iWWVhcnMgc2luY2Ugb2J0YWluaW5nIFBoRCIsIHk9IlNhbGFyeSIpCmBgYAoKIyMjI0J1YmJsZSBwbG90czogdGhlIHRoaXJkIGRpbWVuc2lvbiBpbiB0d28gZGltZW5zaW9ucwpBIFtidWJibGUgcGxvdF0oaHR0cHM6Ly9saXZlYm9vay5tYW5uaW5nLmNvbSMhL2Jvb2svci1pbi1hY3Rpb24tc2Vjb25kLWVkaXRpb24vY2hhcHRlci0xMS9wb2ludC0yODAyLTk1LTk5LTApIGlzIGEgc2NhdHRlcnBsb3Qgd2hlcmUgYSB0aGlyZCBkaW1lbnNpb24gaXMgYWRkZWQ6IHRoZSB2YWx1ZSBvZiBhbiBhZGRpdGlvbmFsIHZhcmlhYmxlIGlzIHJlcHJlc2VudGVkIHRocm91Z2ggdGhlIHNpemUgb2YgdGhlIGRvdHMuIFlvdSBuZWVkIHRocmVlIG51bWVyaWNhbCB2YXJpYWJsZXMgYXMgaW5wdXQ6IG9uZSBpcyByZXByZXNlbnRlZCBieSB0aGUgWCBheGlzLCBvbmUgYnkgdGhlIFkgYXhpcywgYW5kIG9uZSBieSB0aGUgc2l6ZS4KClVzaW5nIHRoZSBfX210Y2Fyc19fIGRhdGFzZXQgd2Ugd2lsbCBsb29rIGFnYWluIGZvdCB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gZWZmaWNpZW5jeSAobXBnKSwgYW5kIHdlaWdodCwgYnV0IGFsc28gbG9va2luZyBmb3IgdGhlIHZhcmlhYmxlIF9fZGlzcF9fIChlbmdpbmUgZGlzcGxhY2VtZW50KSwgaW4gdGhlIHNhbWUgZ3JhcGguCmBgYHtyIGdncGxvdDJfYnViYmxlfQptdGNhcnMgPC0gcmVhZC5jc3YoIm10Y2Fycy5jc3YiLCBoZWFkZXIgPSBUUlVFKQptdGNhcnMKZ2dwbG90KGRhdGE9bXRjYXJzLCBhZXMoeD13dCwgeT1tcGcpKSArCiAgZ2VvbV9wb2ludChhZXMoc2l6ZT1kaXNwKSxjb2xvdXIgPSAibGlnaHRibHVlIikgKwogIGdlb21fdGV4dChhZXMobGFiZWw9bW9kZWwpLCBzaXplPTIsIGFuZ2xlPTQ1KSArCiAgc2NhbGVfc2l6ZV9jb250aW51b3VzKHJhbmdlPWMoMiwxNSksIG5hbWUgPSAiZGlzcC4sIGN1LmluLiIpICsKICBsYWJzKHg9IldlaWdodCwgeDEwMDAgbGIiLCB5PSJNaWxlcyBwZXIgR2FsbG9uIikKYGBgCgo=