This is an R Markdown
Notebook. When you execute code within the notebook, the results appear
beneath the code.
These notebooks are typically this is designed to create a pleasing
viewing environment of data analysis that allows you to include figures,
text, links, etc. so that your work is better understood and can be
reproduced and used with confidence.
The source code for this R notebook (Rmd suffixed files), when stored
as web pages (html files), can be downloaded by clicking the button at
the top of the page.
If viewing the source code in R Studio, try executing each R “chunk”
by clicking the Run button within the chunk or by placing your
cursor inside it and pressing Cmd+Shift+Enter.
Warning. Typos are Legion!
1. Introduction
When you’re in MATH
381 (Intro to Probability and Stats) you’ll get a taste of R. R is
an open-source statistical package build off of an earlier generation of
commercial.
The goal here is to demonstrate cracking open an excel spreadsheet in
R and calculate some basic stats, create various plots to view the
statistics, and finally, do some linear and multivariate regression
Another goal here is to show off some of R’s features. R is a very
powerful tool. When translating “powerful” from computereese to any
frustrated human dialect, that means “steep learning curve.” It’s also a
community-supported environment. When translating “powerful” from
computereese to any over-scheduled human dialect, that means “there are
LOTS of people donating packages and libraries to R.” Some have evolved
to be a standard in the community. Others are highly specialized for a
given discipline (but have one or two items that people outside their
user communities find handy.)
But don’t let that intimidate you. Once you learn one language you
can slowly pick up more. Also with this demo we aren’t going to get to
to be an R guru in a day.
If you want a good stepping off point to learn R I’d recommend some
of the resources at Data
Camp which have some free starter tutorials for R.
2. Loading the Libraries
To work with R we will first have to load some libraries. This is
like in C where you have the #include statement to do things like raise
things to powers and stuff like that.
Some of these libraries or “packages” come with R. Others will have
to be installed. Here are the ones we are using for this exercise.
Also in this exercise, we’re going to use the tidyverse set of packages.
Tidyverse is a set of co-developed tools for data science in R. This is
the new big thing in R and is widely used so we are just going to jump
in here. SD Mines has a course beyond Engineering Stats, MATH
443/543 (Data Analysis) that leverages this set of packages.
- Install Us First
- tidyverse : Set of
commonly-used Data Science packages for R that it can install and load
all at once. In the long-run you probably also want to install the
tidyverse package suite anyway. For this exercise this will include…
- ggplot2 : Create Elegant
Data Visualizations Using the Grammar of Graphics
- tibble : Simple Data
Frames
- tidyr : Tools for
shepherding data in data frames.
- readr : Read Rectangular
Text Data
- purr : Functional
Programming Tools
- dplyr : A grammar of data
manipulation
- stringr : Simple,
Consistent Wrappers for Common String Operations
- forcats : Tools for
Working with Categorical Variables (Factors)
- readxl
: also part of the tidyverse
package suite for reading traditional excel spreadsheets.
- moderndive
: Tidyverse-Friendly Introductory Linear Regression
- This should come with R’s core install, if not install ’em.
- MASS
: Has a lot of resources for regression.
- This doesn’t come with R’s core install so install that one…
- moments
: This has a load of good stuff for data analysis and plotting, more
than you will need here, but get it anyway.
- This is a nice contributed library that lets us make pretty
statistics tables. It was written for ecological applications but it’s
still pretty handy for looking at concrete
- pastecs:
Package for Analysis of Space-Time Ecological Series
- Another nice contributed library that makes matrices of correlation
coefficients look pretty (and graphically informative).
- corrplot
Visualization of a Correlation Matrix
# Tidyverse Handling Libraries
library(package = "tidyverse") # main tidyverse suite
library(package = "readxl") # Read Excel Files
library(package = "moderndive") # regression support
# Statistics Libraries
library(package = "moments") # Moments, cumulants, skewness, kurtosis and related tests
library(package = "MASS", warn.conflicts=FALSE) # Support Functions and Datasets for Venables & Ripley's MASS text
# Extra Graphics Libraries
library(package = "corrplot") # Visualization of a Correlation Matrix
# Data Processing Libraries
library(package = "pastecs") # Package for Analysis of Space-Time Ecological Series
3. Cracking a Spreadsheet
The spreadsheet example below is a more complicated than what you
hopefully have.
The original data set is from a set of papers on Concrete by I-Cheng
Yeh
Yeh,
I-Cheng, “Modeling slump of concrete with fly ash and superplasticizer,”
Computers and Concrete, 5(6), 559-572, 2008.
doi: 10.12989/cac.2008.5.6.559.
Yeh,
I-Cheng, “Simulation of concrete slump using neural networks,”
Construction Materials, 162(1), 11-18, 2009.
doi: 10.1680/coma.2009.162.1.11
Yeh,
I-Cheng, “Prediction of workability of concrete using design of
experiments for mixtures,” Computers and Concrete,
5(1), 1-20, 2008. doi:
10.12989/cac.2008.5.1.001
Yeh,
I-Cheng, “Modeling slump flow of concrete using second-order regressions
and artificial neural networks,” Cement and Concrete
Composites, 29(6), 474-480, 2007. doi:
10.1016/j.cemconcomp.2007.02.001
Yeh,
I-Cheng, “Exploring concrete slump model using artificial neural
networks,” ASCE J. of Computing in Civil Engineering,
20(3), 217-221, 2006. doi:
10.1061/(ASCE)0887-3801(2006)20:3(217)
and is kept at the UC-Irvine
Machine Learning Repository.
It can be found here at http://kyrill.ias.sdsmt.edu/cee_284/Base_Concrete_Slump_Test_for_R.xlsx
The relevant page and screenshot is below. For drama-free R import
you are probably best off keeping a page on your spreadsheet file that
is very simple, with numbers going down, and a single line for Row-1
with the headers of each column. If you want to get fancy on other pages
that you’d turn in as tables in reports, you can do that on another
spreadsheet page.
Concrete Spreadsheet Screenshot
To crack open the spreadsheet we will want to use the readxl::read_excel
function.
You can read the spreadsheet from a local drive or from a
website.
# you will need the full path to the file you are using (either online or locally on your disk)
# The if else block should query your machine to determine which operating system.
# if you are not bi-platform, you likely don't need this.
if(.Platform$OS.type == "windows") {
# Windows
spreadsheet_name = "%HOMEPATH%/Downloads/Base_Concrete_Slump_Test_for_R.xlsx"
} else {
# Unix (Linux, MacOS, Solaris)
spreadsheet_name = "~/Downloads/Base_Concrete_Slump_Test_for_R.xlsx"
}
# I am keeping a copy of these spreadsheet at the URL below. It can be downloaded automatically
# and then loaded. We can also discretely delete it when done.
spreadsheet_url = "http://kyrill.ias.sdsmt.edu/wjc/eduresources/Base_Concrete_Slump_Test_for_R.xlsx"
download.file(url = spreadsheet_url, # URL location
destfile = spreadsheet_name) # local downloaded location
trying URL 'http://kyrill.ias.sdsmt.edu/wjc/eduresources/Base_Concrete_Slump_Test_for_R.xlsx'
Content type 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' length 22230 bytes (21 KB)
==================================================
downloaded 21 KB
remove(spreadsheet_url) # clean up variables
# this command will read the file
concrete = read_excel(path = spreadsheet_name, # remove spreadsheet location
sheet = "Data", # page of spreadsheet
col_names = TRUE) # first row are the column headers
# clean up your hard drive! Don't be like me!
concrete
With the data read in we can now look at the table of the data. This
looks much nicer when working in R Notebooks instead of Plain Ordinary
R.
# Print data frame
colnames(concrete)[1] = "Test_Number"
print(concrete)
NA
4. Some Basic Statistics and Traditional Single Variable Plots
Lets start with some basic statistics and plotting of them.
4.1. The “classic” stats
Let’s get the mom-and-apple-pie stats for Concrete That second
argument allows you to deal with missing data.
# statistics for cement
print(str_c(" Mean Cement : ",
mean(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] " Mean Cement : 229.894174757282"
print(str_c(" Stdev Cement : ",
sd(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] " Stdev Cement : 78.8772300268858"
print(str_c("Skewness Cement : ",
skewness(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] "Skewness Cement : 0.143018080025135"
print(str_c("Kurtosis Cement : ",
kurtosis(x = concrete$Cement, # variable to crunch
na.rm = TRUE) # ignore msissing data
))
[1] "Kurtosis Cement : 1.33448397363582"
OK this is a little clunky. It would be nice if someone somewhere
made a support library for R that will make nice tables of
statistics.
In this case Vive La France! A team from French Research Institute
for Exploitation of the Sea thought the same question and as is often
the case for the R community not only drafted a set of tools to do this,
and made it public.
Here we ware using their stat.desc
function.
This will hopefully give people wanting to make basic tables maximum
satisfaction with minimal effort.
# Plot a statistics table -- all the classics nice and handy and pretty.
options(digits=2) # this simply set the decimal count in the table to be created below
# this particular function creates the table in scientific notation
concrete_statistics = stat.desc(x = concrete, # data frame
basic = TRUE, # includes counts and extremes
desc = TRUE, # include classic stats (mean etc)
norm = TRUE, # include normal dist stats (skewness etc)
p = 0.95) # use 95% confidence limits
print(concrete_statistics)
NA
4.2. Reorganizing Your Data to Handle Multiple Variables at
Once
To leverage some of R’s more nifty features we will need to
reorganize our data from a “spreadsheet style” format to what some
people have called a “long form” table so that the column headers of our
concrete traits become a single column with the values in the columns
placed all into a single column similar to the graphics below.
Example of the “pivot_long” Function
Example of the “pivot_wide” Function
In R this is now done with an analog to a function in Python’s
Pandas’s package (and excel), the “pivot”.
In R this is managed in the tidr package
using tidyr::pivot_longer()
to gathers a group of columns into a single longer column, and tidyr::pivot_wider()
which spreads a column into a shorter-but-wider table.
The documentation doesn’t help me a lot here. I need cartoons for
that!
# Gathering our components into a single column.
# the pivot_longer command will group everything in the column name group
concrete_tidy = concrete %>% # pipe your data frame from "concrete"
pivot_longer(cols = "Cement":"Compressive_Strength_28dy", # the list for the columns to "gather"
names_to = "Parameter", # column name for your former columns
values_to = "Value") # column name for your data
# this will let us sort future plots in the same order as our plots.
concrete_tidy$Parameter = factor(x = concrete_tidy$Parameter,
levels = unique(concrete_tidy$Parameter))
# we can also split things between our dependant variables and independant variables.
concrete_independent = subset(x = concrete_tidy,
subset = (Parameter != "Slump") &
(Parameter != "Flow") &
(Parameter != "Compressive_Strength_28dy")
)
concrete_dependent = subset(x = concrete_tidy,
subset = (Parameter == "Slump") |
(Parameter == "Flow") |
(Parameter == "Compressive_Strength_28dy")
)
print(concrete_tidy)
print(concrete_independent)
print(concrete_dependent)
NA
NA
5. Plotting Graphics using Tidyverse Resources
R has a few ways to do the basic histograms, Boxplots and other
distribution plots.
There are a number of spiffy ways to plot these statistical plots in
R. We’re just using one here…
5.1. SLOOOOWWWWLLLLLYYY Making a Simple Plot (Histogram
Edition)
Now I’m going to do this one tiny step at a time until we get to a
viable product. (This is how I work through cryptic procedures so I can
see what each little additional mystery thingie does.)
Graphing is invoked by the ggplot2 command.. which has a
heluvalot under its hood! For me all that detail was what had me a
little shy to adopt this way of printing data.
Tidyverse uses what is sometimes called the “grammar
of graphics” method… to make a long story longer, the GoG presents
separate commands to do separate things rather bundle stuff in a single
graphing function. Sometimes it makes a lot of sense… other times it may
be confusion. (Hence me demonstrating making a graph this one tiny step
at a time!
First thing we are going to do is open a plotting space with the
command ggplot2::ggplot()
# invoke the ggplot plotting environmnent.
ggplot()
Wow. We have a… big square of… grey. All it’s doing is setting up our
plot environment… so let’s do some more…
If we want to do a histogram we are going to have to tell it what we
want to print and where to get the stuff
When we add things to a plot command in Tidyverse we “add” to the
steps incrementally.
This involves a “mapping” function called “ggplot2::aes”
(short for aesthetics)
here, we are working with the data frame “concrete” and are working
on the variable Cement which we are tossing onto the x axis because
that’s where the bins of cement go!
ggplot(data = concrete) + # EDIT: invoke graphics environment using a given dataframe
aes(x = Cement) # NEW: select variable to print... You can get really fancy here later
OK now we have something that looks like we may have the making of
the graph. If you don’t like grey outlines and white grids, no worries,
we can change that shortly.
OK.. we are now ready to make a histogram…
Here we will use one of the gglot2’s “geom_*” (draw stuff) resources.
The default should work for us here.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
aes(x = Cement) + # select variable to print... You can get really fancy here later
geom_histogram() # NEW: insert histogram
(you may have gotten a warning about using the bin=X, you can adjust
it.)
Now quickly before moving on… I am not keen on the grey background
with white lines.
There are a number of out-of-the-box “themes”
for ggplot2.
I’m partial to theme_bw() and theme_light() but try the ones that you
prefer or stick with the default, theme_gray().
These plots shown here are mine. You should fidget about so they are
yours and so you can adapt to this new way of working with
data.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # NEW: changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
geom_histogram() # insert histogram (including controlling number of bins)
My OCD hates axes where the labels don’t envelop all of the data…
We can fix that with ggplot2::xlim()
or ggplot2::ylim()
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
xlim( 100, 400 ) + # NEW: adding x-axis limits
geom_histogram() # insert histogram
How about changing the color of the fill in the bars…
You
really don’t want to know about all the colors you can use.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
xlim( 100, 400 ) + # NEW: adding x-axis limits
geom_histogram(fill="gray") # EDIT: insert histogram (with a single chosen color)
Want to customize the labels and titles so we can have units?
You can add custom labels and titles! (https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf)
For the superscripting in the x-axis label, I am using the expression()
tool in R.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Cement) + # select variable to print... You can get really fancy here later
xlim( 100, 400 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + # NEW : Custom Title
xlab(expression('Cement Amount (kg m'^-3*")")) + # NEW : Custom Axis Label
geom_histogram(fill="gray") # insert histogram (with a single chosen color)
And I could keep tweaking this graph all day, but good enough is good
enough so this is a good place to stop…
We also can plot a few other fields with some trial and error..
# Histogram of Water
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Water) + # select variable to print... You can get really fancy here later
xlim( 150, 250 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + #Custom Title
xlab(expression('Water Amount (kg m'^-3*")")) + # NEW : Custom Axis Label note use of superscripts from above
geom_histogram(fill="blue") # insert histogram (with a single chosen color)
# Histogram of Strength
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Compressive_Strength_28dy) + # select variable to print... You can get really fancy here later
xlim( 10, 60 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + #Custom Title
xlab("28-dy Compressive Strength (MPa)") + # NEW : Custom Axis Label
geom_histogram(fill="red") # insert histogram (with a single chosen color)
(And from our Intro to Stats Lecture…)
# Histogram of Strength
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Slump) + # select variable to print... You can get really fancy here later
xlim( 0, 30 ) + # adding x-axis limits
ggtitle("Yeh Superplasticizer Tests") + #Custom Title
xlab("Slump (cm)") + # NEW : Custom Axis Label
geom_histogram(fill="darkgreen") # insert histogram (with a single chosen color)
5.2 Distribution Plot [not so good an] Example
There are some other plots that we can use to describe our data.
Here to play with them we will take a quick step back and address
that “tidy”’ed (should that say “tidied”?) dataframe “concrete_tidy”
We can now use all the parameters in the “tidy” (long) data frame to
print by specific traits.
ggplot(data = concrete_tidy) + # invoke graphics environment using a given dataframe
theme_bw() + # changing the plotting theme
aes(x = Value, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab("Value") + # Custom Axis Label
geom_density() # insert crete a relative density plot
In the past, I’ve gotten good results with this but in this case, I
think it’s too messy in part due to the disparity in the dynamic range
of our parameters.
5.3. Box-Whisker Plot Example
How about leveraging a box whisker? (I’m using only the independent
variables this time.)
ggplot(data = concrete_independent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Components") + # Custom Title
ylab(expression('Amount (kg m'^-3*")")) + # EDIT : Changing Custom Axis Label
geom_boxplot() # insert crete a relative density plot
What about our dependent variables? We can start by changing the data
frame…
ggplot(data = concrete_dependent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Results") + # Custom Title
ylab("Values") +
geom_boxplot() # insert crete a relative density plot
Want units? That’s a little tougher here since the units differ by
parameter. We can force the values to into new names though.
ggplot(data = concrete_dependent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Results") + # Custom Title
ylab("Values") +
# NEW: It says scale color but "color" is how we are distinguishing
# out boxplots (as seen in the mapping/aes command)
# we can then use the same plot order above to rewrite the labels
# (likewise we could change the plot order and of coruse the colors.)
scale_color_discrete(labels = c("Slump (cm)",
"Flow (cm)",
"28dy-Compresional Stress (mPa)")) +
geom_boxplot() # insert crete a relative density plot
NA
NA
5.4. Violin Plot Example
How about leveraging a “violin” plot? A violin plot’s width swells in
areas with more observations and contracts with sparser data so it is
like looking at a probability distribution.
ggplot(data = concrete_independent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Components") + # Custom Title
ylab(expression('Amount (kg m'^-3*")")) + # Changing Custom Axis Label
geom_violin(scale="width") # EDIT: change to a violin plot
# the width argument
# gives every plot the same width
and…
ggplot(data = concrete_dependent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
theme(axis.text.x = element_blank()) + # adding an extra trait to the x-axis
# to not print labels on the x-axis
# (the labels overlap and doesn't look
# pretty...)
aes(y = Value, # map y-axis value
x = Parameter, # map x-axis value
color = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Results") + # Custom Title
ylab("Values") +
# NEW: It says scale color but "color" is how we are distinguishing
# out boxplots (as seen in the mapping/aes command)
# we can then use the same plot order above to rewrite the labels
# (likewise we could change the plot order and of coruse the colors.)
scale_color_discrete(labels = c("Slump (cm)",
"Flow (cm)",
"28dy-Compresional Stress (mPa)")) +
geom_violin(scale="width") # EDIT: change to a violin plot
# the width argument
# gives every plot the same width
This is basically the above “density” plot but “looking down” as with
a box plot. Also here we are trimming the plot so that when we leave the
range of any of the data points, the “violins” are truncated.
5.5. Stacked Column or Bar Plot Example
We also can do bar plots or stacked column plots. The one produced
here shows the combined components by test unit.
ggplot(data = concrete_independent) + # EDIT Changing dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Test_Number,
y = Value,
fill = Parameter) + # map colors for different quality
ggtitle(label = "Yeh Superplasticizer Tests",
subtitle = "Concrete Test Components") + # Custom Title
ylab(expression('Amount (kg m'^-3*")")) + # Changing Custom Axis Label
geom_col(position = "stack", # new, create a stacekd column graph
width = 1.0 ) # with no space between columns
6. Correlation of Variables
6.1. Correlating and then Fitting Cement to Compressive
Strength
Let’s start by doing a “simple”” plot . In this case since I already
know the answer because the spreadsheet also has a table of how well our
independent variables correlate against the dependent variables (e.g.,
Slump, Flow, or in our case Strength). The Cement correlates the best
against Compressive Strength (OK, truth be told, it correlates the least
badly).
We can actually do this with a correlate function, boot::cor()…
To grab a value in the table “concrete” we call the data frame
(concrete) and the variable name (Cement or Water vs
Compressive_Strength_28dy), separating the frame and variable names by a
$ sign.
print("Cement vs Compressive Strength Correlation, r")
[1] "Cement vs Compressive Strength Correlation, r"
cor(x = concrete$Cement, # the x-value
y = concrete$Compressive_Strength_28dy, # the y-value
method = "pearson" # method of correlation
)
[1] 0.45
or if you like to do everything at once…
# calculate all correlation values against each other
correlation_matrix = cor(x = concrete, # using our dataframe to correlate evything
method = "pearson" )
tbl_df(correlation_matrix)
Warning: `tbl_df()` was deprecated in dplyr 1.0.0.
Please use `tibble::as_tibble()` instead.
Lots of numbers… not all that insightful on their own…
You also can graph the look-n-feel of what all of the different
correlations are… (it works best with a much smaller number of
variables). This function is called corrplot::corrplot()
and makes a nice presentation of the data.
# draw a correlation graphic...
corrplot(corr = correlation_matrix,
type = "upper")
We can now see for example that cement, slag, and fly ash amounts
have a nominal but not thrilling correlation to compression strength
while water has a good correlation with the resulting slump values. One
thing that this does not show is how well these parameters play
with other parameters. As we’ll see when all of our independent values
are working together we’ll discover that cement and water, followed by
fly ash and coarse aggregates will, together, contribute the most of our
independent parameters in calculating the compressive strength.
6.2. Scatter Plot Example
But for now, let’s plot plot the Cement amount against Compressive
Strength
# Making a simple X-Y scatter plot.
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Cement, # x-value
y = Compressive_Strength_28dy) + # y-value
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Cement Amount (kg m'^3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point(colour="grey") # EDIT: plot points the color keyword part was
# writen by an anglophile!
Here’s a cute trick: Could we color those dots by a variable?
Sure!
# Making a simple X-Y scatterplot now coloured by another parameter
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Cement, # x-value
y = Compressive_Strength_28dy, # y-value
color = Superplasticizer) + # ADD: we can color by a variable!
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Cement Amount (kg m'^3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point() + # plot points
scale_color_distiller(palette = "Spectral") # NEW: pick a custom "colour" palate.
Love overkill without any distinct numerical score and look at how
everything in your data set correlates with every other variables…?
Try graphics::pairs()
(I like the corrplot function better!)
# way too many tiny plots!
pairs(x = concrete, # do everything in the dataframe
pch = ".") # plot dots (the default is circles)
(Obviously the more variables in your dataframe the messier it
gets!)
6.3. Creating our linear model and “calibrating” it
We weren’t all that thrilled with the correlation between these
components and strength but let’s go ahead and demonstrate a
regression.
But let’s move on and create a regression model from this.
Here we will use the stats::lm()
(linear model) from the basic toolboxes that come with R.
For the regression formula
\(\widehat{y}(x) = {\alpha_0}+{\alpha_1}\
x\)
or
\(\widehat{Strength}(concrete) =
{\alpha_0}+{\alpha_1}\ concrete\)
the “prototype” (formula) for the function is written as …
“Y ~ X” (with the y-intercept implicit in the formula… you don’t put
it in but it’ll be there when you’re done.)
The above syntax is works like this….
Dependent Variable [~ is a function of ] Independent Variable [and
any other parameter you need gets added with a plus]
If this were a \(\widehat{y}(x)={\alpha_0}+{\alpha_0}\
x^3\), then the prototype for the function would be y ~ x^3
This will hopefully make more sense as we continue!
(lm and similar linear regression functions don’t play well with
units.)
linear_model.S_v_c = lm(formula = Compressive_Strength_28dy ~ Cement, # your formula y ~ x
data = concrete) # the data frame
Let’s see what we have… This summary command will provide the details
of the lm() function’s important results
For us we want to see the Y-Intercept [the (Intercept) under
“Estimate”] and the slope that goes with our independent value
(“Concrete” under “Estimate”)
The Standard Error of the Estimate is there (Residual Standard Error)
as is the Coefficient of Determination (Multiple R-squared)
We’ll talk about a few of the other features when we do the larger
multivariate regression
summary(object = linear_model.S_v_c)
Call:
lm(formula = Compressive_Strength_28dy ~ Cement, data = concrete)
Residuals:
Min 1Q Median 3Q Max
-15.134 -5.313 0.832 5.155 17.968
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 25.85676 2.15022 12 < 2e-16 ***
Cement 0.04429 0.00885 5 2.4e-06 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7 on 101 degrees of freedom
Multiple R-squared: 0.199, Adjusted R-squared: 0.191
F-statistic: 25 on 1 and 101 DF, p-value: 2.38e-06
In the above output, the asterisk identify the most significant
independent variables. Here it’s trivial even though this is a terrible
relationship between cement and strength. Later we will use all of our
available independent variables and the use of these asterisks will
become more important.
Want to plot it?
Good news?
Like Excel, you have some automated features to give you quick
satisfaction and happiness. More still, it will give you confidence
limits.
For this we use an extension to the graphics package called ggplot2::geom_smooth()
# Making a simple X-Y scatterplot and adding a regression to it
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Cement, # x-value
y = Compressive_Strength_28dy) + # y-value
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Cement Amount (kg m'^-3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point(colour="darkgrey") + # plot points
geom_smooth(method = "lm", # use a simple linar model
formula = y ~ x, # lm-style formula
se = TRUE, # splay Confidence Intervals
level = 0.95, # Confidene Level to Map Out
colour = "black", # regression line color
size = 0.5) # line thickness
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
Please use `linewidth` instead.
The line here looks like a positive correlation between the cement
amount and the resulting strength.
Let’s try water:
# getting the linear model
linear_model.S_v_w = lm(formula = Compressive_Strength_28dy ~ Water, # your formula y ~ x
data = concrete ) # the data frame
summary(linear_model.S_v_w)
Call:
lm(formula = Compressive_Strength_28dy ~ Water, data = concrete)
Residuals:
Min 1Q Median 3Q Max
-19.359 -5.451 -0.986 4.690 18.825
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 55.4824 7.3978 7.50 2.5e-11 ***
Water -0.0986 0.0373 -2.64 0.0096 **
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 7.6 on 101 degrees of freedom
Multiple R-squared: 0.0646, Adjusted R-squared: 0.0554
F-statistic: 6.98 on 1 and 101 DF, p-value: 0.00956
# Making a simple X-Y scatterplot and adding a regression to it
ggplot(data = concrete) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Water, # x-value
y = Compressive_Strength_28dy) + # y-value
ggtitle("Yeh Superplasticizer Tests") + # Custom Title
xlab(expression('Water Amount (kg m'^-3*")")) + # x-label
ylab("28-dy Compressive Strength (MPa)") + # y-label
geom_point(colour="darkblue") + # plot points
geom_smooth(method = "lm", # use a simple linar model
formula = y ~ x, # lm-style formula
se = TRUE, # splay Confidence Intervals
level = 0.95, # Confidene Level to Map Out
colour = "blue", # regression line color
fill = "cyan", # NEW: fill for confidence limits
size = 0.5) # line thickness
Looking up back the tables none of the variables
7. Multivariate Linear Regression
And now we’re going to do something about that!
We’re now going to use not just one independent variable… but all 7
of them!
The good news is that it follows the same form as the simple linear
regression. This time we string along all of our independent variables
with in our formula prototype.
Our formula now has multiple independent values but still follows the
same style of solution…
\(\widehat{y}(\mathbf{x}) =
{\alpha_0}+{\alpha_1} x_1 + {\alpha_2} x_2 + {\alpha_2} x_3 + ...
+{\alpha_n} x_n\)
linear_model.S_v_all <- lm(data = concrete, # your data frame
formula = Compressive_Strength_28dy ~ Cement + # your formula
Slag +
Fly_Ash +
Water +
Superplasticizer +
Fine_Aggregates +
Coarse_Aggregates)
And here are these results…
summary(object = linear_model.S_v_all)
Call:
lm(formula = Compressive_Strength_28dy ~ Cement + Slag + Fly_Ash +
Water + Superplasticizer + Fine_Aggregates + Coarse_Aggregates,
data = concrete)
Residuals:
Min 1Q Median 3Q Max
-5.841 -1.706 -0.283 1.299 7.942
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 139.7815 71.1013 1.97 0.0522 .
Cement 0.0614 0.0228 2.69 0.0084 **
Slag -0.0297 0.0318 -0.94 0.3520
Fly_Ash 0.0505 0.0232 2.18 0.0316 *
Water -0.2327 0.0717 -3.25 0.0016 **
Superplasticizer 0.1031 0.1346 0.77 0.4453
Fine_Aggregates -0.0391 0.0288 -1.36 0.1783
Coarse_Aggregates -0.0556 0.0274 -2.03 0.0455 *
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 2.6 on 95 degrees of freedom
Multiple R-squared: 0.897, Adjusted R-squared: 0.889
F-statistic: 118 on 7 and 95 DF, p-value: <2e-16
Our regression coefficients are still here under the “Estimate”
column as are our Standard Error of our Estimate and our Coeff of
Determination.
Also we can now take a good look at those asterisks at the end of
line with the parameter coefficients. These can explain which
independent variables do the heaviest lifting in our regression. The
more asterisks, the more important the dependent variable is to the
larger multivariate regression. Here, we can see that the Cement and
Water are doing most of the “work” in fitting our suite of independent
variables to our dependent variable of Compressive Strength.
Finally there is the P parameter for which the smaller it is, the
better we can say that the relationship that we’ve made with our
regression represents our dependent variable.
Now… on to looking at our results.
Here is where viewing the results of the regression is tricky.
We have 7 independent variables but we’d like to see the impact of
the fit if all 7 variables on our strength
When I do this I like to plot the true y value against my regression
y(x1,x2,x3,..)
So to do this I will take the fitted values of y and plot them
against the original values of y
Getting the fitted values is easy.
I’m using the get_regression_points function which adds the modeled
“y-hat” value to the dataframe of all of the other values stats::get_regression_points()
function.
The fitted version is the dependent variable w/ a “_hat”” at the
end
fitted.S_v_all = get_regression_points(model = linear_model.S_v_all)
print(fitted.S_v_all)
NA
And finally we can plot our actual vs modeled values. (I’m adding a
trend line)
# Making a simple X-Y scatterplot and adding a regression to it
ggplot(data = fitted.S_v_all) + # invoke graphics environment using a given dataframe
theme_bw( ) + # changing the plotting theme
aes(x = Compressive_Strength_28dy, # x-value
y = Compressive_Strength_28dy_hat) + # y-value
ggtitle("Yeh Superplasticizer Tests",
subtitle = "28-dy Compressive Strength (MPa)") + # EDITED: Custom Title now with a subtitle
ylab("Modelled") + # y-label
xlab("Observed") + # x-label
geom_point(colour="darkred") + # plot points
geom_smooth(method = "lm", # use a simple linar model
formula = y ~ x, # lm-style formula
se = TRUE, # display Confidence Intervals
level = 0.95, # Confidene Level to Map Out
colour = "red", # regression line color
fill = "magenta", # fill for confidence limits
size = 0.5) + # line thickness
geom_abline(slope = 1, # NEW: add a very simple line
intercept = 0, # (for a 1:1 reference)
color = "grey",
linetype = "dashed") +
coord_fixed(ratio = 1) # NEW: make the aspect ratio
# (I like my plots square)
And here we have a nice plot showing our true vs predicted
values.
8. Regression Quality Metrics
And to close things off, we can do some general error metrics that
may be useful..
First, the Mean Squared Error (MSE) or Bias… (if we are too high or
too low)
\(BIAS = MSE = \frac{1}{N} \sum_{i=1}^{n}
[\widehat{y}(\overrightarrow{x_i})-y_i] =
\overline{[\widehat{y}(\overrightarrow{x_i})-y_i]}\)
# Calculate Bias (MSE)
bias = mean(fitted.S_v_all$Compressive_Strength_28dy_hat -
fitted.S_v_all$Compressive_Strength_28dy)
print(str_c(" Mean Squared Error (MSE) or Bias: ", bias))
[1] " Mean Squared Error (MSE) or Bias: 2.91262135922143e-05"
For a linear or multivariate regression the average of our residuals
(the difference between each observation and prediction) should
be zero.
The root mean squared error (RMSE) is shown here. It shouldn’t be
zero since the residuals are squared before summing them up. We
technically should use the standard error of the estimate, but RMSE
remains a common error metric. We can always do both. The standard error
of the estimate takes into account the degrees of freedom which which
now includes all of the independent variables (p). We can get the
standard error of the estimate from our
\(RMSE = \sqrt{ \frac{1}{N} \sum_{i=1}^{n}
[\widehat{y}(\overrightarrow{x_i})-y_i]^2 } =
\sqrt{\overline{[\widehat{y}(\overrightarrow{x_i})-y_i]^2}
}\)
\(s_{e}\) or \(s_{y/x} = \sqrt{ \frac{1}{N-p-1} \sum_{i=1}^{n}
[\widehat{y}(\overrightarrow{x_i})-y_i]^2 }\)
# Calculate RMSE
rmse = sqrt(mean( (fitted.S_v_all$Compressive_Strength_28dy_hat -
fitted.S_v_all$Compressive_Strength_28dy)^2) )
print(str_c(" Root Mean Squared Error (RMSE): ",
rmse))
[1] " Root Mean Squared Error (RMSE): 2.50527978593714"
print(str_c("Standard Error of the Estimate (se): ",
summary(linear_model.S_v_all)$sigma)) # you have to dig for this one!
[1] "Standard Error of the Estimate (se): 2.6086576339523"
And finally our correlation coefficient (which is basically our
coefficient of determination before the “R” is “squared”)
# Get The Unadjusted Correlation Coefficient
r = cor(x = fitted.S_v_all$Compressive_Strength_28dy, # the x-value
y = fitted.S_v_all$Compressive_Strength_28dy_hat, # the y-value
method = "pearson" # method of correlation
)
print(str_c(" correlation coefficient (r): ", r))
[1] " correlation coefficient (r): 0.94701611900088"
print(str_c(" coefficient of determination (r²): ", r^2,
" ",
summary(linear_model.S_v_all)$r.squared))
[1] " coefficient of determination (r²): 0.89683952964749 0.896837609814009"
print(str_c("adjusted coefficient of determination (Adjusted r²): ",
summary(linear_model.S_v_all)$adj.r.squared))
[1] "adjusted coefficient of determination (Adjusted r²): 0.889236170537146"
9. Closing
And with that, we’re done… Once again, this exercise demonstrates a
lot of tricks just to show how you can use R for various statistics. You
may not use all of them in your encounters with R for linear or
multivariate regression or even at all, but you may be able to
cannibalize some of the tricks here for other applications.
LS0tCnRpdGxlOiAiVmlzdWFsaXppbmcgU3RhdGlzdGljcyBhbmQgUmVncmVzc2lvbnMgZnJvbSBhIFNwcmVhZHNoZWV0IHVzaW5nIFIiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgbGF0ZXhfZW5naW5lOiB4ZWxhdGV4Ci0tLQoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2suIFdoZW4geW91IGV4ZWN1dGUgY29kZSB3aXRoaW4gdGhlIG5vdGVib29rLCB0aGUgcmVzdWx0cyBhcHBlYXIgYmVuZWF0aCB0aGUgY29kZS4KClRoZXNlIG5vdGVib29rcyBhcmUgdHlwaWNhbGx5IHRoaXMgaXMgZGVzaWduZWQgdG8gY3JlYXRlIGEgcGxlYXNpbmcgdmlld2luZyBlbnZpcm9ubWVudCBvZiBkYXRhIGFuYWx5c2lzIHRoYXQgYWxsb3dzIHlvdSB0byBpbmNsdWRlIGZpZ3VyZXMsIHRleHQsIGxpbmtzLCBldGMuIHNvIHRoYXQgeW91ciB3b3JrIGlzIGJldHRlciB1bmRlcnN0b29kIGFuZCBjYW4gYmUgcmVwcm9kdWNlZCBhbmQgdXNlZCB3aXRoIGNvbmZpZGVuY2UuCgpUaGUgc291cmNlIGNvZGUgZm9yIHRoaXMgUiBub3RlYm9vayAoUm1kIHN1ZmZpeGVkIGZpbGVzKSwgd2hlbiBzdG9yZWQgYXMgd2ViIHBhZ2VzIChodG1sIGZpbGVzKSwgY2FuIGJlIGRvd25sb2FkZWQgYnkgY2xpY2tpbmcgdGhlIGJ1dHRvbiBhdCB0aGUgdG9wIG9mIHRoZSBwYWdlLgoKSWYgdmlld2luZyB0aGUgc291cmNlIGNvZGUgaW4gUiBTdHVkaW8sIHRyeSBleGVjdXRpbmcgZWFjaCBSICJjaHVuayIgYnkgY2xpY2tpbmcgdGhlICpSdW4qIGJ1dHRvbiB3aXRoaW4gdGhlIGNodW5rIG9yIGJ5IHBsYWNpbmcgeW91ciBjdXJzb3IgaW5zaWRlIGl0IGFuZCBwcmVzc2luZyAqQ21kK1NoaWZ0K0VudGVyKi4KCioqV2FybmluZy4gVHlwb3MgYXJlICpMZWdpb24qISoqCgojIDEuIEludHJvZHVjdGlvbgoKV2hlbiB5b3UncmUgaW4gW01BVEggMzgxIChJbnRybyB0byBQcm9iYWJpbGl0eSBhbmQgU3RhdHMpXShodHRwOi8vZWNhdGFsb2cuc2RzbXQuZWR1L3ByZXZpZXdfY291cnNlX25vcG9wLnBocD9jYXRvaWQ9MTcmY29pZD0yNjU3MSkgeW91J2xsIGdldCBhIHRhc3RlIG9mIFIuIFIgaXMgYW4gb3Blbi1zb3VyY2Ugc3RhdGlzdGljYWwgcGFja2FnZSBidWlsZCBvZmYgb2YgYW4gZWFybGllciBnZW5lcmF0aW9uIG9mIGNvbW1lcmNpYWwuCgpUaGUgZ29hbCBoZXJlIGlzIHRvIGRlbW9uc3RyYXRlIGNyYWNraW5nIG9wZW4gYW4gZXhjZWwgc3ByZWFkc2hlZXQgaW4gUiBhbmQgY2FsY3VsYXRlIHNvbWUgYmFzaWMgc3RhdHMsIGNyZWF0ZSB2YXJpb3VzIHBsb3RzIHRvIHZpZXcgdGhlIHN0YXRpc3RpY3MsIGFuZCBmaW5hbGx5LCBkbyBzb21lIGxpbmVhciBhbmQgbXVsdGl2YXJpYXRlIHJlZ3Jlc3Npb24KCkFub3RoZXIgZ29hbCBoZXJlIGlzIHRvIHNob3cgb2ZmIHNvbWUgb2YgUidzIGZlYXR1cmVzLiBSIGlzIGEgdmVyeSBwb3dlcmZ1bCB0b29sLiBXaGVuIHRyYW5zbGF0aW5nICJwb3dlcmZ1bCIgZnJvbSBjb21wdXRlcmVlc2UgdG8gYW55IGZydXN0cmF0ZWQgaHVtYW4gZGlhbGVjdCwgdGhhdCBtZWFucyAic3RlZXAgbGVhcm5pbmcgY3VydmUuIiBJdCdzIGFsc28gYSBjb21tdW5pdHktc3VwcG9ydGVkIGVudmlyb25tZW50LiBXaGVuIHRyYW5zbGF0aW5nICJwb3dlcmZ1bCIgZnJvbSBjb21wdXRlcmVlc2UgdG8gYW55IG92ZXItc2NoZWR1bGVkIGh1bWFuIGRpYWxlY3QsIHRoYXQgbWVhbnMgInRoZXJlIGFyZSBMT1RTIG9mIHBlb3BsZSBkb25hdGluZyBwYWNrYWdlcyBhbmQgbGlicmFyaWVzIHRvIFIuIiBTb21lIGhhdmUgZXZvbHZlZCB0byBiZSBhIHN0YW5kYXJkIGluIHRoZSBjb21tdW5pdHkuIE90aGVycyBhcmUgaGlnaGx5IHNwZWNpYWxpemVkIGZvciBhIGdpdmVuIGRpc2NpcGxpbmUgKGJ1dCBoYXZlIG9uZSBvciB0d28gaXRlbXMgdGhhdCBwZW9wbGUgb3V0c2lkZSB0aGVpciB1c2VyIGNvbW11bml0aWVzIGZpbmQgaGFuZHkuKQoKQnV0IGRvbid0IGxldCB0aGF0IGludGltaWRhdGUgeW91LiBPbmNlIHlvdSBsZWFybiBvbmUgbGFuZ3VhZ2UgeW91IGNhbiBzbG93bHkgcGljayB1cCBtb3JlLiBBbHNvIHdpdGggdGhpcyBkZW1vIHdlIGFyZW4ndCBnb2luZyB0byBnZXQgdG8gdG8gYmUgYW4gUiBndXJ1IGluIGEgZGF5LgoKSWYgeW91IHdhbnQgYSBnb29kIHN0ZXBwaW5nIG9mZiBwb2ludCB0byBsZWFybiBSIEknZCByZWNvbW1lbmQgc29tZSBvZiB0aGUgcmVzb3VyY2VzIGF0IFtEYXRhIENhbXBdKGh0dHBzOi8vd3d3LmRhdGFjYW1wLmNvbS9jb3Vyc2VzL2ZyZWUtaW50cm9kdWN0aW9uLXRvLXIpIHdoaWNoIGhhdmUgc29tZSBmcmVlIHN0YXJ0ZXIgdHV0b3JpYWxzIGZvciBSLgoKIyAyLiBMb2FkaW5nIHRoZSBMaWJyYXJpZXMKClRvIHdvcmsgd2l0aCBSIHdlIHdpbGwgZmlyc3QgaGF2ZSB0byBsb2FkIHNvbWUgbGlicmFyaWVzLiBUaGlzIGlzIGxpa2UgaW4gQyB3aGVyZSB5b3UgaGF2ZSB0aGUgI2luY2x1ZGUgc3RhdGVtZW50IHRvIGRvIHRoaW5ncyBsaWtlIHJhaXNlIHRoaW5ncyB0byBwb3dlcnMgYW5kIHN0dWZmIGxpa2UgdGhhdC4KClNvbWUgb2YgdGhlc2UgbGlicmFyaWVzIG9yICJwYWNrYWdlcyIgY29tZSB3aXRoIFIuIE90aGVycyB3aWxsIGhhdmUgdG8gYmUgaW5zdGFsbGVkLiBIZXJlIGFyZSB0aGUgb25lcyB3ZSBhcmUgdXNpbmcgZm9yIHRoaXMgZXhlcmNpc2UuCgpBbHNvIGluIHRoaXMgZXhlcmNpc2UsIHdlJ3JlIGdvaW5nIHRvIHVzZSB0aGUgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZykgc2V0IG9mIHBhY2thZ2VzLiBUaWR5dmVyc2UgaXMgYSBzZXQgb2YgY28tZGV2ZWxvcGVkIHRvb2xzIGZvciBkYXRhIHNjaWVuY2UgaW4gUi4gVGhpcyBpcyB0aGUgbmV3IGJpZyB0aGluZyBpbiBSIGFuZCBpcyB3aWRlbHkgdXNlZCBzbyB3ZSBhcmUganVzdCBnb2luZyB0byBqdW1wIGluIGhlcmUuIFNEIE1pbmVzIGhhcyBhIGNvdXJzZSBiZXlvbmQgRW5naW5lZXJpbmcgU3RhdHMsIFtNQVRIIDQ0My81NDMgKERhdGEgQW5hbHlzaXMpXShodHRwOi8vZWNhdGFsb2cuc2RzbXQuZWR1L3ByZXZpZXdfY291cnNlX25vcG9wLnBocD9jYXRvaWQ9MTcmY29pZD0yNjk3MykgdGhhdCBsZXZlcmFnZXMgdGhpcyBzZXQgb2YgcGFja2FnZXMuCgotICAgSW5zdGFsbCBVcyBGaXJzdAogICAgLSAgIFt0aWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcpIDogU2V0IG9mIGNvbW1vbmx5LXVzZWQgRGF0YSBTY2llbmNlIHBhY2thZ2VzIGZvciBSIHRoYXQgaXQgY2FuIGluc3RhbGwgYW5kIGxvYWQgYWxsIGF0IG9uY2UuIEluIHRoZSBsb25nLXJ1biB5b3UgcHJvYmFibHkgYWxzbyB3YW50IHRvIGluc3RhbGwgdGhlIHRpZHl2ZXJzZSBwYWNrYWdlIHN1aXRlIGFueXdheS4gRm9yIHRoaXMgZXhlcmNpc2UgdGhpcyB3aWxsIGluY2x1ZGUuLi4KICAgICAgICAtICAgW2dncGxvdDJdKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnKSA6IENyZWF0ZSBFbGVnYW50IERhdGEgVmlzdWFsaXphdGlvbnMgVXNpbmcgdGhlIEdyYW1tYXIgb2YgR3JhcGhpY3MKICAgICAgICAtICAgW3RpYmJsZV0oaHR0cHM6Ly90aWJibGUudGlkeXZlcnNlLm9yZykgOiBTaW1wbGUgRGF0YSBGcmFtZXMKICAgICAgICAtICAgW3RpZHlyXShodHRwczovL3RpZHlyLnRpZHl2ZXJzZS5vcmcpIDogVG9vbHMgZm9yIHNoZXBoZXJkaW5nIGRhdGEgaW4gZGF0YSBmcmFtZXMuCiAgICAgICAgLSAgIFtyZWFkcl0oaHR0cHM6Ly9yZWFkci50aWR5dmVyc2Uub3JnKSA6IFJlYWQgUmVjdGFuZ3VsYXIgVGV4dCBEYXRhCiAgICAgICAgLSAgIFtwdXJyXShodHRwczovL3B1cnJyLnRpZHl2ZXJzZS5vcmcpIDogRnVuY3Rpb25hbCBQcm9ncmFtbWluZyBUb29scwogICAgICAgIC0gICBbZHBseXJdKGh0dHBzOi8vZHBseXIudGlkeXZlcnNlLm9yZykgOiBBIGdyYW1tYXIgb2YgZGF0YSBtYW5pcHVsYXRpb24KICAgICAgICAtICAgW3N0cmluZ3JdKGh0dHBzOi8vc3RyaW5nci50aWR5dmVyc2Uub3JnKSA6IFNpbXBsZSwgQ29uc2lzdGVudCBXcmFwcGVycyBmb3IgQ29tbW9uIFN0cmluZyBPcGVyYXRpb25zCiAgICAgICAgLSAgIFtmb3JjYXRzXShodHRwczovL2ZvcmNhdHMudGlkeXZlcnNlLm9yZykgOiBUb29scyBmb3IgV29ya2luZyB3aXRoIENhdGVnb3JpY2FsIFZhcmlhYmxlcyAoRmFjdG9ycykKICAgIC0gICBbcmVhZHhsXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcmVhZHhsL3ZlcnNpb25zLzEuMS4wKSA6IGFsc28gcGFydCBvZiB0aGUgW3RpZHl2ZXJzZV0oaHR0cHM6Ly93d3cudGlkeXZlcnNlLm9yZykgcGFja2FnZSBzdWl0ZSBmb3IgcmVhZGluZyB0cmFkaXRpb25hbCBleGNlbCBzcHJlYWRzaGVldHMuXAogICAgLSAgIFttb2Rlcm5kaXZlXShUaWR5dmVyc2UtRnJpZW5kbHklMjBJbnRyb2R1Y3RvcnklMjBMaW5lYXIlMjBSZWdyZXNzaW9uKSA6IFRpZHl2ZXJzZS1GcmllbmRseSBJbnRyb2R1Y3RvcnkgTGluZWFyIFJlZ3Jlc3Npb24KLSAgIFRoaXMgc2hvdWxkIGNvbWUgd2l0aCBSJ3MgY29yZSBpbnN0YWxsLCBpZiBub3QgaW5zdGFsbCAnZW0uCiAgICAtICAgW01BU1NdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9NQVNTL3ZlcnNpb25zLzcuMy01MCkgOiBIYXMgYSBsb3Qgb2YgcmVzb3VyY2VzIGZvciByZWdyZXNzaW9uLgotICAgVGhpcyBkb2Vzbid0IGNvbWUgd2l0aCBSJ3MgY29yZSBpbnN0YWxsIHNvIGluc3RhbGwgdGhhdCBvbmUuLi4KICAgIC0gICBbbW9tZW50c10oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL21vbWVudHMvdmVyc2lvbnMvMC4xNCkgOiBUaGlzIGhhcyBhIGxvYWQgb2YgZ29vZCBzdHVmZiBmb3IgZGF0YSBhbmFseXNpcyBhbmQgcGxvdHRpbmcsIG1vcmUgdGhhbiB5b3Ugd2lsbCBuZWVkIGhlcmUsIGJ1dCBnZXQgaXQgYW55d2F5LgotICAgVGhpcyBpcyBhIG5pY2UgY29udHJpYnV0ZWQgbGlicmFyeSB0aGF0IGxldHMgdXMgbWFrZSBwcmV0dHkgc3RhdGlzdGljcyB0YWJsZXMuIEl0IHdhcyB3cml0dGVuIGZvciBlY29sb2dpY2FsIGFwcGxpY2F0aW9ucyBidXQgaXQncyBzdGlsbCBwcmV0dHkgaGFuZHkgZm9yIGxvb2tpbmcgYXQgY29uY3JldGUKICAgIC0gICBbcGFzdGVjc10oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3Bhc3RlY3MvdmVyc2lvbnMvMS4zLjIxKTogUGFja2FnZSBmb3IgQW5hbHlzaXMgb2YgU3BhY2UtVGltZSBFY29sb2dpY2FsIFNlcmllcwotICAgQW5vdGhlciBuaWNlIGNvbnRyaWJ1dGVkIGxpYnJhcnkgdGhhdCBtYWtlcyBtYXRyaWNlcyBvZiBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgbG9vayBwcmV0dHkgKGFuZCBncmFwaGljYWxseSBpbmZvcm1hdGl2ZSkuCiAgICAtICAgW2NvcnJwbG90XShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvY29ycnBsb3QvdmVyc2lvbnMvMC44NCkgVmlzdWFsaXphdGlvbiBvZiBhIENvcnJlbGF0aW9uIE1hdHJpeAoKYGBge3J9CgogICMgVGlkeXZlcnNlIEhhbmRsaW5nIExpYnJhcmllcwoKICBsaWJyYXJ5KHBhY2thZ2UgPSAidGlkeXZlcnNlIikgICAjIG1haW4gdGlkeXZlcnNlIHN1aXRlCiAgbGlicmFyeShwYWNrYWdlID0gInJlYWR4bCIpICAgICAjIFJlYWQgRXhjZWwgRmlsZXMKICBsaWJyYXJ5KHBhY2thZ2UgPSAibW9kZXJuZGl2ZSIpICMgcmVncmVzc2lvbiBzdXBwb3J0CgogICMgU3RhdGlzdGljcyBMaWJyYXJpZXMKCiAgbGlicmFyeShwYWNrYWdlID0gIm1vbWVudHMiKSAgICMgTW9tZW50cywgY3VtdWxhbnRzLCBza2V3bmVzcywga3VydG9zaXMgYW5kIHJlbGF0ZWQgdGVzdHMKICBsaWJyYXJ5KHBhY2thZ2UgPSAiTUFTUyIsIHdhcm4uY29uZmxpY3RzPUZBTFNFKSAgICAgICMgU3VwcG9ydCBGdW5jdGlvbnMgYW5kIERhdGFzZXRzIGZvciBWZW5hYmxlcyAmIFJpcGxleSdzIE1BU1MgdGV4dAoKICAjIEV4dHJhIEdyYXBoaWNzIExpYnJhcmllcwoKICBsaWJyYXJ5KHBhY2thZ2UgPSAiY29ycnBsb3QiKSAgIyBWaXN1YWxpemF0aW9uIG9mIGEgQ29ycmVsYXRpb24gTWF0cml4CgoKICAjIERhdGEgUHJvY2Vzc2luZyBMaWJyYXJpZXMKCiAgbGlicmFyeShwYWNrYWdlID0gInBhc3RlY3MiKSAgICMgUGFja2FnZSBmb3IgQW5hbHlzaXMgb2YgU3BhY2UtVGltZSBFY29sb2dpY2FsIFNlcmllcwoKCgpgYGAKCiMgMy4gQ3JhY2tpbmcgYSBTcHJlYWRzaGVldAoKVGhlIHNwcmVhZHNoZWV0IGV4YW1wbGUgYmVsb3cgaXMgYSBtb3JlIGNvbXBsaWNhdGVkIHRoYW4gd2hhdCB5b3UgaG9wZWZ1bGx5IGhhdmUuCgpUaGUgb3JpZ2luYWwgZGF0YSBzZXQgaXMgZnJvbSBhIHNldCBvZiBwYXBlcnMgb24gQ29uY3JldGUgYnkgSS1DaGVuZyBZZWgKCi0gICBbWWVoLCBJLUNoZW5nLCAiTW9kZWxpbmcgc2x1bXAgb2YgY29uY3JldGUgd2l0aCBmbHkgYXNoIGFuZCBzdXBlcnBsYXN0aWNpemVyLCIgKkNvbXB1dGVycyBhbmQgQ29uY3JldGUqLCAqKjUqKig2KSwgNTU5LTU3MiwgMjAwOC4gZG9pOiAxMC4xMjk4OS9jYWMuMjAwOC41LjYuNTU5Ll0oaHR0cDovL3d3dy50ZWNobm8tcHJlc3Mub3JnL2NvbnRlbnQvP3BhZ2U9YXJ0aWNsZSZqb3VybmFsPWNhYyZ2b2x1bWU9NSZudW09NiZvcmRlcm51bT00KQoKLSAgIFtZZWgsIEktQ2hlbmcsICJTaW11bGF0aW9uIG9mIGNvbmNyZXRlIHNsdW1wIHVzaW5nIG5ldXJhbCBuZXR3b3JrcywiICpDb25zdHJ1Y3Rpb24gTWF0ZXJpYWxzKiwgKioxNjIqKigxKSwgMTEtMTgsIDIwMDkuIGRvaTogMTAuMTY4MC9jb21hLjIwMDkuMTYyLjEuMTFdKGh0dHBzOi8vd3d3LmljZXZpcnR1YWxsaWJyYXJ5LmNvbS9kb2kvMTAuMTY4MC9jb21hLjIwMDkuMTYyLjEuMTEpCgotICAgW1llaCwgSS1DaGVuZywgIlByZWRpY3Rpb24gb2Ygd29ya2FiaWxpdHkgb2YgY29uY3JldGUgdXNpbmcgZGVzaWduIG9mIGV4cGVyaW1lbnRzIGZvciBtaXh0dXJlcywiICpDb21wdXRlcnMgYW5kIENvbmNyZXRlKiwgKio1KiooMSksIDEtMjAsIDIwMDguIGRvaTogMTAuMTI5ODkvY2FjLjIwMDguNS4xLjAwMV0oaHR0cDovL3d3dy50ZWNobm8tcHJlc3Mub3JnL2NvbnRlbnQvP3BhZ2U9YXJ0aWNsZSZqb3VybmFsPWNhYyZ2b2x1bWU9NSZudW09MSZvcmRlcm51bT0xKQoKLSAgIFtZZWgsIEktQ2hlbmcsICJNb2RlbGluZyBzbHVtcCBmbG93IG9mIGNvbmNyZXRlIHVzaW5nIHNlY29uZC1vcmRlciByZWdyZXNzaW9ucyBhbmQgYXJ0aWZpY2lhbCBuZXVyYWwgbmV0d29ya3MsIiAqQ2VtZW50IGFuZCBDb25jcmV0ZSBDb21wb3NpdGVzKiwgKioyOSoqKDYpLCA0NzQtNDgwLCAyMDA3LiBkb2k6IDEwLjEwMTYvai5jZW1jb25jb21wLjIwMDcuMDIuMDAxXShodHRwczovL3d3dy5zY2llbmNlZGlyZWN0LmNvbS9zY2llbmNlL2FydGljbGUvcGlpL1MwOTU4OTQ2NTA3MDAwMjYxP3ZpYSUzRGlodWIpCgotICAgW1llaCwgSS1DaGVuZywgIkV4cGxvcmluZyBjb25jcmV0ZSBzbHVtcCBtb2RlbCB1c2luZyBhcnRpZmljaWFsIG5ldXJhbCBuZXR3b3JrcywiICpBU0NFIEouIG9mIENvbXB1dGluZyBpbiBDaXZpbCBFbmdpbmVlcmluZyosICoqMjAqKigzKSwgMjE3LTIyMSwgMjAwNi4gZG9pOiAxMC4xMDYxLyhBU0NFKTA4ODctMzgwMSgyMDA2KTIwOjMoMjE3KV0oaHR0cHM6Ly9hc2NlbGlicmFyeS5vcmcvZG9pLzEwLjEwNjEvJTI4QVNDRSUyOTA4ODctMzgwMSUyODIwMDYlMjkyMCUzQTMlMjgyMTclMjkpCgphbmQgaXMga2VwdCBhdCB0aGUgW1VDLUlydmluZSBNYWNoaW5lIExlYXJuaW5nIFJlcG9zaXRvcnldKGh0dHBzOi8vYXJjaGl2ZS5pY3MudWNpLmVkdS9tbC9kYXRhc2V0cy9Db25jcmV0ZStTbHVtcCtUZXN0KS4KCkl0IGNhbiBiZSBmb3VuZCBoZXJlIGF0IFtodHRwOi8va3lyaWxsLmlhcy5zZHNtdC5lZHUvY2VlXzI4NC9CYXNlX0NvbmNyZXRlX1NsdW1wX1Rlc3RfZm9yX1IueGxzeF0oaHR0cDovL2t5cmlsbC5pYXMuc2RzbXQuZWR1L3dqYy9lZHVyZXNvdXJjZXMvQmFzZV9Db25jcmV0ZV9TbHVtcF9UZXN0X2Zvcl9SLnhsc3gpCgpUaGUgcmVsZXZhbnQgcGFnZSBhbmQgc2NyZWVuc2hvdCBpcyBiZWxvdy4gRm9yIGRyYW1hLWZyZWUgUiBpbXBvcnQgeW91IGFyZSBwcm9iYWJseSBiZXN0IG9mZiBrZWVwaW5nIGEgcGFnZSBvbiB5b3VyIHNwcmVhZHNoZWV0IGZpbGUgdGhhdCBpcyB2ZXJ5IHNpbXBsZSwgd2l0aCBudW1iZXJzIGdvaW5nIGRvd24sIGFuZCBhIHNpbmdsZSBsaW5lIGZvciBSb3ctMSB3aXRoIHRoZSBoZWFkZXJzIG9mIGVhY2ggY29sdW1uLiBJZiB5b3Ugd2FudCB0byBnZXQgZmFuY3kgb24gb3RoZXIgcGFnZXMgdGhhdCB5b3UnZCB0dXJuIGluIGFzIHRhYmxlcyBpbiByZXBvcnRzLCB5b3UgY2FuIGRvIHRoYXQgb24gYW5vdGhlciBzcHJlYWRzaGVldCBwYWdlLgoKIVtDb25jcmV0ZSBTcHJlYWRzaGVldCBTY3JlZW5zaG90XSguL0Jhc2VfQ29uY3JldGVfU2x1bXBfVGVzdF9mb3JfUi5wbmcpCgpUbyBjcmFjayBvcGVuIHRoZSBzcHJlYWRzaGVldCB3ZSB3aWxsIHdhbnQgdG8gdXNlIHRoZSBbcmVhZHhsOjpyZWFkX2V4Y2VsXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvcmVhZHhsL3ZlcnNpb25zLzEuMS4wL3RvcGljcy9yZWFkX2V4Y2VsKSBmdW5jdGlvbi4KCllvdSBjYW4gcmVhZCB0aGUgc3ByZWFkc2hlZXQgZnJvbSBhIGxvY2FsIGRyaXZlIG9yIGZyb20gYSB3ZWJzaXRlLgoKYGBge3J9CgogICMgeW91IHdpbGwgbmVlZCB0aGUgZnVsbCBwYXRoIHRvIHRoZSBmaWxlIHlvdSBhcmUgdXNpbmcgKGVpdGhlciBvbmxpbmUgb3IgbG9jYWxseSBvbiB5b3VyIGRpc2spCgogICMgVGhlIGlmIGVsc2UgYmxvY2sgc2hvdWxkIHF1ZXJ5IHlvdXIgbWFjaGluZSB0byBkZXRlcm1pbmUgd2hpY2ggb3BlcmF0aW5nIHN5c3RlbS4KICAjICBpZiB5b3UgYXJlIG5vdCBiaS1wbGF0Zm9ybSwgeW91IGxpa2VseSBkb24ndCBuZWVkIHRoaXMuCgogIGlmKC5QbGF0Zm9ybSRPUy50eXBlID09ICJ3aW5kb3dzIikgewogICAgIyBXaW5kb3dzCiAgICBzcHJlYWRzaGVldF9uYW1lICAgICA9ICIlSE9NRVBBVEglL0Rvd25sb2Fkcy9CYXNlX0NvbmNyZXRlX1NsdW1wX1Rlc3RfZm9yX1IueGxzeCIKICB9IGVsc2UgewogICAgIyBVbml4IChMaW51eCwgTWFjT1MsIFNvbGFyaXMpCiAgICBzcHJlYWRzaGVldF9uYW1lICAgICA9ICJ+L0Rvd25sb2Fkcy9CYXNlX0NvbmNyZXRlX1NsdW1wX1Rlc3RfZm9yX1IueGxzeCIKICB9CgoKICAjIEkgYW0ga2VlcGluZyBhIGNvcHkgb2YgdGhlc2Ugc3ByZWFkc2hlZXQgYXQgdGhlIFVSTCBiZWxvdy4gIEl0IGNhbiBiZSBkb3dubG9hZGVkIGF1dG9tYXRpY2FsbHkKICAjICAgYW5kIHRoZW4gbG9hZGVkLiAgV2UgY2FuIGFsc28gZGlzY3JldGVseSBkZWxldGUgaXQgd2hlbiBkb25lLgoKICAgICAgc3ByZWFkc2hlZXRfdXJsID0gImh0dHA6Ly9reXJpbGwuaWFzLnNkc210LmVkdS93amMvZWR1cmVzb3VyY2VzL0Jhc2VfQ29uY3JldGVfU2x1bXBfVGVzdF9mb3JfUi54bHN4IgogICAKICAgICAgZG93bmxvYWQuZmlsZSh1cmwgICAgICA9ICAgc3ByZWFkc2hlZXRfdXJsLCAjIFVSTCBsb2NhdGlvbgogICAgICAgICAgICAgICAgICAgIGRlc3RmaWxlID0gc3ByZWFkc2hlZXRfbmFtZSkgIyBsb2NhbCBkb3dubG9hZGVkIGxvY2F0aW9uCiAgICAgIAogICAgICByZW1vdmUoc3ByZWFkc2hlZXRfdXJsKSAjIGNsZWFuIHVwIHZhcmlhYmxlcwogIAogICMgdGhpcyBjb21tYW5kIHdpbGwgcmVhZCB0aGUgZmlsZQoKICBjb25jcmV0ZSA9IHJlYWRfZXhjZWwocGF0aCAgICAgID0gc3ByZWFkc2hlZXRfbmFtZSwgICMgcmVtb3ZlIHNwcmVhZHNoZWV0IGxvY2F0aW9uCiAgICAgICAgICAgICAgICAgICAgICAgIHNoZWV0ICAgICA9ICJEYXRhIiwgICAgICAgICAgICAjIHBhZ2Ugb2Ygc3ByZWFkc2hlZXQKICAgICAgICAgICAgICAgICAgICAgICAgY29sX25hbWVzID0gVFJVRSkgICAgICAgICAgICAgICMgZmlyc3Qgcm93IGFyZSB0aGUgY29sdW1uIGhlYWRlcnMKICAKICAKICAjIGNsZWFuIHVwIHlvdXIgaGFyZCBkcml2ZSEgIERvbid0IGJlIGxpa2UgbWUhCgogIAoKY29uY3JldGUKYGBgCgpXaXRoIHRoZSBkYXRhIHJlYWQgaW4gd2UgY2FuIG5vdyBsb29rIGF0IHRoZSB0YWJsZSBvZiB0aGUgZGF0YS4gVGhpcyBsb29rcyBtdWNoIG5pY2VyIHdoZW4gd29ya2luZyBpbiBSIE5vdGVib29rcyBpbnN0ZWFkIG9mIFBsYWluIE9yZGluYXJ5IFIuCgpgYGB7cn0KCiAgIyBQcmludCBkYXRhIGZyYW1lCiAgY29sbmFtZXMoY29uY3JldGUpWzFdID0gIlRlc3RfTnVtYmVyIgogIHByaW50KGNvbmNyZXRlKQoKYGBgCgojIyMgRXh0cmE6IFVuaXRzOiBBVk9JRCEKCi4uLiBVaC4uLiBKdXN0IGRvbid0LiBVbmxpa2UgTWF0aGNhZCB3aGljaCBpcyBkZXNpZ25lZCB0byB3b3JrIHdpdGggdW5pdHMgb3IgcHl0aG9uIHdoaWNoIGNsYWltcyB0byBiZSBhYmxlIHRvIHdvcmsgd2l0aCB1bml0cywgUiBpcyBhd2Z1bCB3aXRoIGl0LiBJJ3ZlIHVzZWQgdW5pdHMgb25jZSBvciB0d2ljZS4gQnV0IEkndmUgZm91bmQgaXQgdG8gYmUgbW9yZSB3b3JrIHRoYW4gaXQncyB3b3J0aC4KCiMgNC4gU29tZSBCYXNpYyBTdGF0aXN0aWNzIGFuZCBUcmFkaXRpb25hbCBTaW5nbGUgVmFyaWFibGUgUGxvdHMKCkxldHMgc3RhcnQgd2l0aCBzb21lIGJhc2ljIHN0YXRpc3RpY3MgYW5kIHBsb3R0aW5nIG9mIHRoZW0uCgojIyA0LjEuIFRoZSAiY2xhc3NpYyIgc3RhdHMKCkxldCdzIGdldCB0aGUgbW9tLWFuZC1hcHBsZS1waWUgc3RhdHMgZm9yIENvbmNyZXRlIFRoYXQgc2Vjb25kIGFyZ3VtZW50IGFsbG93cyB5b3UgdG8gZGVhbCB3aXRoIG1pc3NpbmcgZGF0YS4KCmBgYHtyfQoKICAjIHN0YXRpc3RpY3MgZm9yIGNlbWVudAoKCiAgcHJpbnQoc3RyX2MoIiAgICBNZWFuIENlbWVudCA6ICIsCiAgICAgICAgICAgICAgbWVhbih4ICAgICA9IGNvbmNyZXRlJENlbWVudCwgIyB2YXJpYWJsZSB0byBjcnVuY2gKICAgICAgICAgICAgICAgICAgIG5hLnJtID0gICAgICAgICAgICBUUlVFKSAjIGlnbm9yZSBtc2lzc2luZyBkYXRhCiAgICAgICAgICAgICAgKSkKCiAgcHJpbnQoc3RyX2MoIiAgIFN0ZGV2IENlbWVudCA6ICIsCiAgICAgICAgICAgICAgc2QoeCAgICAgPSBjb25jcmV0ZSRDZW1lbnQsICMgdmFyaWFibGUgdG8gY3J1bmNoCiAgICAgICAgICAgICAgICAgbmEucm0gPSAgICAgICAgICAgIFRSVUUpICMgaWdub3JlIG1zaXNzaW5nIGRhdGEKICAgICAgICAgICAgICApKQogIAogIHByaW50KHN0cl9jKCJTa2V3bmVzcyBDZW1lbnQgOiAiLAogICAgICAgICAgICAgIHNrZXduZXNzKHggICAgID0gY29uY3JldGUkQ2VtZW50LCAjIHZhcmlhYmxlIHRvIGNydW5jaAogICAgICAgICAgICAgICAgICAgICAgIG5hLnJtID0gICAgICAgICAgICBUUlVFKSAjIGlnbm9yZSBtc2lzc2luZyBkYXRhCiAgICAgICAgICAgICAgKSkKICAKICBwcmludChzdHJfYygiS3VydG9zaXMgQ2VtZW50IDogIiwKICAgICAgICAgICAgICBrdXJ0b3Npcyh4ICAgICA9IGNvbmNyZXRlJENlbWVudCwgIyB2YXJpYWJsZSB0byBjcnVuY2gKICAgICAgICAgICAgICAgICAgICAgICBuYS5ybSA9ICAgICAgICAgICAgVFJVRSkgIyBpZ25vcmUgbXNpc3NpbmcgZGF0YQogICAgICAgICAgICAgICkpCiAgICAgCmBgYAoKT0sgdGhpcyBpcyBhIGxpdHRsZSBjbHVua3kuIEl0IHdvdWxkIGJlIG5pY2UgaWYgc29tZW9uZSBzb21ld2hlcmUgbWFkZSBhIHN1cHBvcnQgbGlicmFyeSBmb3IgUiB0aGF0IHdpbGwgbWFrZSBuaWNlIHRhYmxlcyBvZiBzdGF0aXN0aWNzLgoKSW4gdGhpcyBjYXNlIFZpdmUgTGEgRnJhbmNlISBBIHRlYW0gZnJvbSBGcmVuY2ggUmVzZWFyY2ggSW5zdGl0dXRlIGZvciBFeHBsb2l0YXRpb24gb2YgdGhlIFNlYSB0aG91Z2h0IHRoZSBzYW1lIHF1ZXN0aW9uIGFuZCBhcyBpcyBvZnRlbiB0aGUgY2FzZSBmb3IgdGhlIFIgY29tbXVuaXR5IG5vdCBvbmx5IGRyYWZ0ZWQgYSBzZXQgb2YgdG9vbHMgdG8gZG8gdGhpcywgKmFuZCogbWFkZSBpdCBwdWJsaWMuCgpIZXJlIHdlIHdhcmUgdXNpbmcgdGhlaXIgW3N0YXQuZGVzY10oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3Bhc3RlY3MvdmVyc2lvbnMvMS4zLjIxL3RvcGljcy9zdGF0LmRlc2MpIGZ1bmN0aW9uLgoKVGhpcyB3aWxsIGhvcGVmdWxseSBnaXZlIHBlb3BsZSB3YW50aW5nIHRvIG1ha2UgYmFzaWMgdGFibGVzIG1heGltdW0gc2F0aXNmYWN0aW9uIHdpdGggbWluaW1hbCBlZmZvcnQuCgpgYGB7cn0KCiAgIyBQbG90IGEgc3RhdGlzdGljcyB0YWJsZSAtLSBhbGwgdGhlIGNsYXNzaWNzIG5pY2UgYW5kIGhhbmR5IGFuZCBwcmV0dHkuCgogIG9wdGlvbnMoZGlnaXRzPTIpICMgdGhpcyBzaW1wbHkgc2V0IHRoZSBkZWNpbWFsIGNvdW50IGluIHRoZSB0YWJsZSB0byBiZSBjcmVhdGVkIGJlbG93ICAKICAgICAgICAgICAgICAgICAgICAjIHRoaXMgcGFydGljdWxhciBmdW5jdGlvbiBjcmVhdGVzIHRoZSB0YWJsZSBpbiBzY2llbnRpZmljIG5vdGF0aW9uCiAgCiAgY29uY3JldGVfc3RhdGlzdGljcyA9IHN0YXQuZGVzYyh4ICAgID0gY29uY3JldGUsICAjIGRhdGEgZnJhbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJhc2ljID0gICAgVFJVRSwgICMgaW5jbHVkZXMgY291bnRzIGFuZCBleHRyZW1lcyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2MgPSAgICAgVFJVRSwgICMgaW5jbHVkZSBjbGFzc2ljIHN0YXRzIChtZWFuIGV0YykKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5vcm0gPSAgICAgVFJVRSwgICMgaW5jbHVkZSBub3JtYWwgZGlzdCBzdGF0cyAoc2tld25lc3MgZXRjKQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcCAgICA9ICAgICAwLjk1KSAgIyB1c2UgOTUlIGNvbmZpZGVuY2UgbGltaXRzCgoKICBwcmludChjb25jcmV0ZV9zdGF0aXN0aWNzKQoKYGBgCgojIyA0LjIuIFJlb3JnYW5pemluZyBZb3VyIERhdGEgdG8gSGFuZGxlIE11bHRpcGxlIFZhcmlhYmxlcyBhdCBPbmNlCgpUbyBsZXZlcmFnZSBzb21lIG9mIFIncyBtb3JlIG5pZnR5IGZlYXR1cmVzIHdlIHdpbGwgbmVlZCB0byByZW9yZ2FuaXplIG91ciBkYXRhIGZyb20gYSAic3ByZWFkc2hlZXQgc3R5bGUiIGZvcm1hdCB0byB3aGF0IHNvbWUgcGVvcGxlIGhhdmUgY2FsbGVkIGEgImxvbmcgZm9ybSIgdGFibGUgc28gdGhhdCB0aGUgY29sdW1uIGhlYWRlcnMgb2Ygb3VyIGNvbmNyZXRlIHRyYWl0cyBiZWNvbWUgYSBzaW5nbGUgY29sdW1uIHdpdGggdGhlIHZhbHVlcyBpbiB0aGUgY29sdW1ucyBwbGFjZWQgYWxsIGludG8gYSBzaW5nbGUgY29sdW1uIHNpbWlsYXIgdG8gdGhlIGdyYXBoaWNzIGJlbG93LgoKIVtFeGFtcGxlIG9mIHRoZSAicGl2b3RfbG9uZyIgRnVuY3Rpb25dKGh0dHBzOi8vZXBpcmhhbmRib29rLmNvbS9lbi9pbWFnZXMvcGl2b3RpbmcvcGl2b3RfbG9uZ2VyX25ldy5wbmcpCgohW0V4YW1wbGUgb2YgdGhlICJwaXZvdF93aWRlIiBGdW5jdGlvbl0oaHR0cHM6Ly9lcGlyaGFuZGJvb2suY29tL2VuL2ltYWdlcy9waXZvdGluZy9waXZvdF93aWRlcl9uZXcucG5nKQoKSW4gUiB0aGlzIGlzIG5vdyBkb25lIHdpdGggYW4gYW5hbG9nIHRvIGEgZnVuY3Rpb24gaW4gW1B5dGhvbidzIFBhbmRhcydzIHBhY2thZ2UgKGFuZCBleGNlbCksIHRoZSAicGl2b3QiXShodHRwczovL3BhbmRhcy5weWRhdGEub3JnL2RvY3MvdXNlcl9ndWlkZS9yZXNoYXBpbmcuaHRtbCkuCgpJbiBSIHRoaXMgaXMgbWFuYWdlZCBpbiB0aGUgW3RpZHJdKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy9hcnRpY2xlcy9waXZvdC5odG1sKSBwYWNrYWdlIHVzaW5nIFt0aWR5cjo6cGl2b3RfbG9uZ2VyKCldKGh0dHBzOi8vdGlkeXIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvcGl2b3RfbG9uZ2VyLmh0bWwpIHRvIGdhdGhlcnMgYSBncm91cCBvZiBjb2x1bW5zIGludG8gYSBzaW5nbGUgbG9uZ2VyIGNvbHVtbiwgYW5kIFt0aWR5cjo6cGl2b3Rfd2lkZXIoKV0oaHR0cHM6Ly90aWR5ci50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9waXZvdF93aWRlci5odG1sKSB3aGljaCBzcHJlYWRzIGEgY29sdW1uIGludG8gYSBzaG9ydGVyLWJ1dC13aWRlciB0YWJsZS4KClRoZSBkb2N1bWVudGF0aW9uIGRvZXNuJ3QgaGVscCBtZSBhIGxvdCBoZXJlLiBJIG5lZWQgY2FydG9vbnMgZm9yIHRoYXQhCgpgYGB7cn0KCiAgIyBHYXRoZXJpbmcgb3VyIGNvbXBvbmVudHMgaW50byBhIHNpbmdsZSBjb2x1bW4uCgogICMgdGhlIHBpdm90X2xvbmdlciBjb21tYW5kIHdpbGwgZ3JvdXAgZXZlcnl0aGluZyBpbiB0aGUgY29sdW1uIG5hbWUgZ3JvdXAgCiAgCiAgY29uY3JldGVfdGlkeSA9IGNvbmNyZXRlICU+JSAgICAjIHBpcGUgeW91ciBkYXRhIGZyYW1lIGZyb20gImNvbmNyZXRlIgogICAgcGl2b3RfbG9uZ2VyKGNvbHMgICAgICA9ICJDZW1lbnQiOiJDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5IiwgIyB0aGUgbGlzdCBmb3IgdGhlIGNvbHVtbnMgdG8gImdhdGhlciIKICAgICAgICAgICAgICAgICBuYW1lc190byAgPSAiUGFyYW1ldGVyIiwgICAgICAgICAgICAgICAgICAgICAgICAgICMgY29sdW1uIG5hbWUgZm9yIHlvdXIgZm9ybWVyIGNvbHVtbnMKICAgICAgICAgICAgICAgICB2YWx1ZXNfdG8gPSAgICAgIlZhbHVlIikgICAgICAgICAgICAgICAgICAgICAgICAgICMgY29sdW1uIG5hbWUgZm9yIHlvdXIgZGF0YQogIAoKICAjIHRoaXMgd2lsbCBsZXQgdXMgc29ydCBmdXR1cmUgcGxvdHMgaW4gdGhlIHNhbWUgb3JkZXIgYXMgb3VyIHBsb3RzLiAgCiAgCiAgY29uY3JldGVfdGlkeSRQYXJhbWV0ZXIgPSBmYWN0b3IoeCAgICAgID0gY29uY3JldGVfdGlkeSRQYXJhbWV0ZXIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gdW5pcXVlKGNvbmNyZXRlX3RpZHkkUGFyYW1ldGVyKSkKICAKICAjIHdlIGNhbiBhbHNvIHNwbGl0IHRoaW5ncyBiZXR3ZWVuIG91ciBkZXBlbmRhbnQgdmFyaWFibGVzIGFuZCBpbmRlcGVuZGFudCB2YXJpYWJsZXMuCiAgCiAgCiAgY29uY3JldGVfaW5kZXBlbmRlbnQgPSBzdWJzZXQoeCAgICAgID0gY29uY3JldGVfdGlkeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJzZXQgPSAoUGFyYW1ldGVyICE9ICJTbHVtcCIpICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoUGFyYW1ldGVyICE9ICJGbG93IikgICYKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoUGFyYW1ldGVyICE9ICJDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5IikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICApIAogICAgCiAgICAKICBjb25jcmV0ZV9kZXBlbmRlbnQgPSBzdWJzZXQoeCAgICAgID0gY29uY3JldGVfdGlkeSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gKFBhcmFtZXRlciA9PSAiU2x1bXAiKSB8CiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIChQYXJhbWV0ZXIgPT0gIkZsb3ciKSAgfAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAoUGFyYW1ldGVyID09ICJDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5IikKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgKQoKIAogICAgICAgICAgICAgICAgICAgICAgIAoKICBwcmludChjb25jcmV0ZV90aWR5KQogIHByaW50KGNvbmNyZXRlX2luZGVwZW5kZW50KQogIHByaW50KGNvbmNyZXRlX2RlcGVuZGVudCkKICAKICAKYGBgCgojIDUuIFBsb3R0aW5nIEdyYXBoaWNzIHVzaW5nIFRpZHl2ZXJzZSBSZXNvdXJjZXMKClIgaGFzIGEgZmV3IHdheXMgdG8gZG8gdGhlIGJhc2ljIGhpc3RvZ3JhbXMsIEJveHBsb3RzIGFuZCBvdGhlciBkaXN0cmlidXRpb24gcGxvdHMuCgpUaGVyZSBhcmUgYSBudW1iZXIgb2Ygc3BpZmZ5IHdheXMgdG8gcGxvdCB0aGVzZSBzdGF0aXN0aWNhbCBwbG90cyBpbiBSLiBXZSdyZSBqdXN0IHVzaW5nIG9uZSBoZXJlLi4uCgojIyA1LjEuIFNMT09PT1dXV1dMTExMTFlZWSBNYWtpbmcgYSBTaW1wbGUgUGxvdCAoSGlzdG9ncmFtIEVkaXRpb24pCgpOb3cgSSdtIGdvaW5nIHRvIGRvIHRoaXMgb25lIHRpbnkgc3RlcCBhdCBhIHRpbWUgdW50aWwgd2UgZ2V0IHRvIGEgdmlhYmxlIHByb2R1Y3QuIChUaGlzIGlzIGhvdyBJIHdvcmsgdGhyb3VnaCBjcnlwdGljIHByb2NlZHVyZXMgc28gSSBjYW4gc2VlIHdoYXQgZWFjaCBsaXR0bGUgYWRkaXRpb25hbCBteXN0ZXJ5IHRoaW5naWUgZG9lcy4pCgpHcmFwaGluZyBpcyBpbnZva2VkIGJ5IHRoZSBbZ2dwbG90Ml0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcpIGNvbW1hbmQuLiB3aGljaCBoYXMgYSBoZWx1dmFsb3QgdW5kZXIgaXRzIGhvb2QhIEZvciBtZSBhbGwgdGhhdCBkZXRhaWwgd2FzIHdoYXQgaGFkIG1lIGEgbGl0dGxlIHNoeSB0byBhZG9wdCB0aGlzIHdheSBvZiBwcmludGluZyBkYXRhLgoKVGlkeXZlcnNlIHVzZXMgd2hhdCBpcyBzb21ldGltZXMgY2FsbGVkIHRoZSBbImdyYW1tYXIgb2YgZ3JhcGhpY3MiXShodHRwczovL3JhbW5hdGh2LmdpdGh1Yi5pby9weWNvbjIwMTQtci92aXN1YWxpemUvZ2dwbG90Mi5odG1sKSBtZXRob2QuLi4gdG8gbWFrZSBhIGxvbmcgc3RvcnkgbG9uZ2VyLCB0aGUgR29HIHByZXNlbnRzIHNlcGFyYXRlIGNvbW1hbmRzIHRvIGRvIHNlcGFyYXRlIHRoaW5ncyByYXRoZXIgYnVuZGxlIHN0dWZmIGluIGEgc2luZ2xlIGdyYXBoaW5nIGZ1bmN0aW9uLiBTb21ldGltZXMgaXQgbWFrZXMgYSBsb3Qgb2Ygc2Vuc2UuLi4gb3RoZXIgdGltZXMgaXQgbWF5IGJlIGNvbmZ1c2lvbi4gKEhlbmNlIG1lIGRlbW9uc3RyYXRpbmcgbWFraW5nIGEgZ3JhcGggdGhpcyBvbmUgdGlueSBzdGVwIGF0IGEgdGltZSEKCkZpcnN0IHRoaW5nIHdlIGFyZSBnb2luZyB0byBkbyBpcyBvcGVuIGEgcGxvdHRpbmcgc3BhY2Ugd2l0aCB0aGUgY29tbWFuZCBbZ2dwbG90Mjo6Z2dwbG90KCldKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZ3Bsb3QuaHRtbCkKCmBgYHtyfQoKIyBpbnZva2UgdGhlIGdncGxvdCBwbG90dGluZyBlbnZpcm9ubW5lbnQuCgpnZ3Bsb3QoKSAKCmBgYAoKV293LiBXZSBoYXZlIGEuLi4gYmlnIHNxdWFyZSBvZi4uLiBncmV5LiBBbGwgaXQncyBkb2luZyBpcyBzZXR0aW5nIHVwIG91ciBwbG90IGVudmlyb25tZW50Li4uIHNvIGxldCdzIGRvIHNvbWUgbW9yZS4uLgoKSWYgd2Ugd2FudCB0byBkbyBhIGhpc3RvZ3JhbSB3ZSBhcmUgZ29pbmcgdG8gaGF2ZSB0byB0ZWxsIGl0IHdoYXQgd2Ugd2FudCB0byBwcmludCBhbmQgd2hlcmUgdG8gZ2V0IHRoZSBzdHVmZgoKV2hlbiB3ZSBhZGQgdGhpbmdzIHRvIGEgcGxvdCBjb21tYW5kIGluIFRpZHl2ZXJzZSB3ZSAiYWRkIiB0byB0aGUgc3RlcHMgaW5jcmVtZW50YWxseS4KClRoaXMgaW52b2x2ZXMgYSAibWFwcGluZyIgZnVuY3Rpb24gY2FsbGVkICJbZ2dwbG90Mjo6YWVzXShodHRwczovL2dncGxvdDIudGlkeXZlcnNlLm9yZy9yZWZlcmVuY2UvYWVzLmh0bWwpIiAoc2hvcnQgZm9yIGFlc3RoZXRpY3MpCgpoZXJlLCB3ZSBhcmUgd29ya2luZyB3aXRoIHRoZSBkYXRhIGZyYW1lICJjb25jcmV0ZSIgYW5kIGFyZSB3b3JraW5nIG9uIHRoZSB2YXJpYWJsZSBDZW1lbnQgd2hpY2ggd2UgYXJlIHRvc3Npbmcgb250byB0aGUgeCBheGlzIGJlY2F1c2UgdGhhdCdzIHdoZXJlIHRoZSBiaW5zIG9mIGNlbWVudCBnbyEKCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZSkgKyAgICMgRURJVDogIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIGFlcyh4ICAgID0gQ2VtZW50KSAgICAgICAgIyBORVc6IHNlbGVjdCB2YXJpYWJsZSB0byBwcmludC4uLiBZb3UgY2FuIGdldCByZWFsbHkgZmFuY3kgaGVyZSBsYXRlcgoKYGBgCgpPSyBub3cgd2UgaGF2ZSBzb21ldGhpbmcgdGhhdCBsb29rcyBsaWtlIHdlIG1heSBoYXZlIHRoZSBtYWtpbmcgb2YgdGhlIGdyYXBoLiBJZiB5b3UgZG9uJ3QgbGlrZSBncmV5IG91dGxpbmVzIGFuZCB3aGl0ZSBncmlkcywgbm8gd29ycmllcywgd2UgY2FuIGNoYW5nZSB0aGF0IHNob3J0bHkuCgpPSy4uIHdlIGFyZSBub3cgcmVhZHkgdG8gbWFrZSBhIGhpc3RvZ3JhbS4uLgoKSGVyZSB3ZSB3aWxsIHVzZSBvbmUgb2YgdGhlIGdnbG90MidzICJnZW9tXF9cKiIgKGRyYXcgc3R1ZmYpIHJlc291cmNlcy4gVGhlIGRlZmF1bHQgc2hvdWxkIHdvcmsgZm9yIHVzIGhlcmUuCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAjIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIGFlcyh4ID0gQ2VtZW50KSAgICsgICAgICAgIyBzZWxlY3QgdmFyaWFibGUgdG8gcHJpbnQuLi4gWW91IGNhbiBnZXQgcmVhbGx5IGZhbmN5IGhlcmUgbGF0ZXIKCiAgZ2VvbV9oaXN0b2dyYW0oKSAgICAgICAgICAjIE5FVzogaW5zZXJ0IGhpc3RvZ3JhbQoKYGBgCgooeW91IG1heSBoYXZlIGdvdHRlbiBhIHdhcm5pbmcgYWJvdXQgdXNpbmcgdGhlIGJpbj1YLCB5b3UgY2FuIGFkanVzdCBpdC4pCgpOb3cgcXVpY2tseSBiZWZvcmUgbW92aW5nIG9uLi4uIEkgYW0gbm90IGtlZW4gb24gdGhlIGdyZXkgYmFja2dyb3VuZCB3aXRoIHdoaXRlIGxpbmVzLgoKVGhlcmUgYXJlIGEgbnVtYmVyIG9mIG91dC1vZi10aGUtYm94IFsidGhlbWVzIl0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2dndGhlbWUuaHRtbCkgZm9yIGdncGxvdDIuCgpJJ20gcGFydGlhbCB0byB0aGVtZV9idygpIGFuZCB0aGVtZV9saWdodCgpIGJ1dCB0cnkgdGhlIG9uZXMgdGhhdCB5b3UgcHJlZmVyIG9yIHN0aWNrIHdpdGggdGhlIGRlZmF1bHQsIHRoZW1lX2dyYXkoKS4KClRoZXNlIHBsb3RzIHNob3duIGhlcmUgYXJlIG1pbmUuIFlvdSBzaG91bGQgZmlkZ2V0IGFib3V0IHNvIHRoZXkgYXJlICp5b3VycyogYW5kIHNvIHlvdSBjYW4gYWRhcHQgdG8gdGhpcyBuZXcgd2F5IG9mIHdvcmtpbmcgd2l0aCBkYXRhLgoKYGBge3J9CgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlKSArICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoKSArICAgICAgICAgICAgIyBORVc6IGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIGFlcyh4ID0gQ2VtZW50KSArICAgICAgICMgc2VsZWN0IHZhcmlhYmxlIHRvIHByaW50Li4uIFlvdSBjYW4gZ2V0IHJlYWxseSBmYW5jeSBoZXJlIGxhdGVyCgogIGdlb21faGlzdG9ncmFtKCkgICAgICAgICMgaW5zZXJ0IGhpc3RvZ3JhbSAoaW5jbHVkaW5nIGNvbnRyb2xsaW5nIG51bWJlciBvZiBiaW5zKQoKYGBgCgpNeSBPQ0QgaGF0ZXMgYXhlcyB3aGVyZSB0aGUgbGFiZWxzIGRvbid0IGVudmVsb3AgYWxsIG9mIHRoZSBkYXRhLi4uCgpXZSBjYW4gZml4IHRoYXQgd2l0aCBbZ2dwbG90Mjo6eGxpbSgpIG9yIGdncGxvdDI6OnlsaW0oKV0oaHR0cHM6Ly9nZ3Bsb3QyLnRpZHl2ZXJzZS5vcmcvcmVmZXJlbmNlL2xpbXMuaHRtbCkKCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZSkgKyAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idygpICsgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCA9IENlbWVudCkgKyAgICAgICAgICAgIyBzZWxlY3QgdmFyaWFibGUgdG8gcHJpbnQuLi4gWW91IGNhbiBnZXQgcmVhbGx5IGZhbmN5IGhlcmUgbGF0ZXIKICAKICB4bGltKCAxMDAsIDQwMCApICsgICAgICAgICAgIyBORVc6IGFkZGluZyB4LWF4aXMgbGltaXRzCgogIGdlb21faGlzdG9ncmFtKCkgICAgICAgICAgICAjIGluc2VydCBoaXN0b2dyYW0KCmBgYAoKSG93IGFib3V0IGNoYW5naW5nIHRoZSBjb2xvciBvZiB0aGUgZmlsbCBpbiB0aGUgYmFycy4uLgoKW1lvdSByZWFsbHkgZG9uJ3Qgd2FudCB0byBrbm93IGFib3V0IGFsbCB0aGUgY29sb3JzIHlvdSBjYW4gdXNlLl0oaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvfmZyYXppZXIvUlNwYXRpYWxHdWlkZXMvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYpCgpgYGB7cn0KCgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlKSArICAgICAjIGludm9rZSBncmFwaGljcyBlbnZpcm9ubWVudCB1c2luZyBhIGdpdmVuIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCkgKyAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIGFlcyh4ID0gQ2VtZW50KSArICAgICAgICAgICAjIHNlbGVjdCB2YXJpYWJsZSB0byBwcmludC4uLiBZb3UgY2FuIGdldCByZWFsbHkgZmFuY3kgaGVyZSBsYXRlcgogIAogIHhsaW0oIDEwMCwgNDAwICkgKyAgICAgICAgICAjIE5FVzogYWRkaW5nIHgtYXhpcyBsaW1pdHMKCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0iZ3JheSIpICMgRURJVDogaW5zZXJ0IGhpc3RvZ3JhbSAod2l0aCBhIHNpbmdsZSBjaG9zZW4gY29sb3IpCgpgYGAKCldhbnQgdG8gY3VzdG9taXplIHRoZSBsYWJlbHMgYW5kIHRpdGxlcyBzbyB3ZSBjYW4gaGF2ZSB1bml0cz8KCllvdSBjYW4gYWRkIGN1c3RvbSBsYWJlbHMgYW5kIHRpdGxlcyEgKDxodHRwczovL3d3dy5uY2Vhcy51Y3NiLmVkdS9+ZnJhemllci9SU3BhdGlhbEd1aWRlcy9jb2xvclBhbGV0dGVDaGVhdHNoZWV0LnBkZj4pCgpGb3IgdGhlIHN1cGVyc2NyaXB0aW5nIGluIHRoZSB4LWF4aXMgbGFiZWwsIEkgYW0gdXNpbmcgdGhlIFtleHByZXNzaW9uKCldKGh0dHA6Ly92aXMuc3Vwc3RhdC5jb20vMjAxMy8wNC9tYXRoZW1hdGljYWwtYW5ub3RhdGlvbi1pbi1yLykgdG9vbCBpbiBSLgoKYGBge3J9CgoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZSkgKyAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idygpICsgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCA9IENlbWVudCkgKyAgICAgICAgICAgIyBzZWxlY3QgdmFyaWFibGUgdG8gcHJpbnQuLi4gWW91IGNhbiBnZXQgcmVhbGx5IGZhbmN5IGhlcmUgbGF0ZXIKICAKICB4bGltKCAxMDAsIDQwMCApICsgICAgICAgICAgIyBhZGRpbmcgeC1heGlzIGxpbWl0cwoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgICAgICAgICAgIyBORVcgOiBDdXN0b20gVGl0bGUKICAKICB4bGFiKGV4cHJlc3Npb24oJ0NlbWVudCBBbW91bnQgKGtnIG0nXi0zKiIpIikpICsgIyBORVcgOiBDdXN0b20gQXhpcyBMYWJlbAoKICBnZW9tX2hpc3RvZ3JhbShmaWxsPSJncmF5IikgIyBpbnNlcnQgaGlzdG9ncmFtICh3aXRoIGEgc2luZ2xlIGNob3NlbiBjb2xvcikKCmBgYAoKQW5kIEkgY291bGQga2VlcCB0d2Vha2luZyB0aGlzIGdyYXBoIGFsbCBkYXksIGJ1dCBnb29kIGVub3VnaCBpcyBnb29kIGVub3VnaCBzbyB0aGlzIGlzIGEgZ29vZCBwbGFjZSB0byBzdG9wLi4uCgpXZSBhbHNvIGNhbiBwbG90IGEgZmV3IG90aGVyIGZpZWxkcyB3aXRoIHNvbWUgdHJpYWwgYW5kIGVycm9yLi4KCmBgYHtyfQoKIyBIaXN0b2dyYW0gb2YgV2F0ZXIKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoKSArICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggPSBXYXRlcikgKyAgICAgICAgICAgIyBzZWxlY3QgdmFyaWFibGUgdG8gcHJpbnQuLi4gWW91IGNhbiBnZXQgcmVhbGx5IGZhbmN5IGhlcmUgbGF0ZXIKICAKICB4bGltKCAxNTAsIDI1MCApICsgICAgICAgICAgIyBhZGRpbmcgeC1heGlzIGxpbWl0cwoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgI0N1c3RvbSBUaXRsZQogIAogIHhsYWIoZXhwcmVzc2lvbignV2F0ZXIgQW1vdW50IChrZyBtJ14tMyoiKSIpKSArICMgTkVXIDogQ3VzdG9tIEF4aXMgTGFiZWwgbm90ZSB1c2Ugb2Ygc3VwZXJzY3JpcHRzIGZyb20gYWJvdmUKCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0iYmx1ZSIpICMgaW5zZXJ0IGhpc3RvZ3JhbSAod2l0aCBhIHNpbmdsZSBjaG9zZW4gY29sb3IpCgpgYGAKCmBgYHtyfQoKIyBIaXN0b2dyYW0gb2YgU3RyZW5ndGgKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoKSArICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggPSBDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5KSArICMgc2VsZWN0IHZhcmlhYmxlIHRvIHByaW50Li4uIFlvdSBjYW4gZ2V0IHJlYWxseSBmYW5jeSBoZXJlIGxhdGVyCiAgCiAgeGxpbSggMTAsIDYwICkgKyAgICAgICAgICAjIGFkZGluZyB4LWF4aXMgbGltaXRzCgogIGdndGl0bGUoIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIikgKyAjQ3VzdG9tIFRpdGxlCiAgCiAgeGxhYigiMjgtZHkgQ29tcHJlc3NpdmUgU3RyZW5ndGggKE1QYSkiKSArICMgTkVXIDogQ3VzdG9tIEF4aXMgTGFiZWwKCiAgZ2VvbV9oaXN0b2dyYW0oZmlsbD0icmVkIikgIyBpbnNlcnQgaGlzdG9ncmFtICh3aXRoIGEgc2luZ2xlIGNob3NlbiBjb2xvcikKCmBgYAoKKEFuZCBmcm9tIG91ciBJbnRybyB0byBTdGF0cyBMZWN0dXJlLi4uKQoKYGBge3J9CgojIEhpc3RvZ3JhbSBvZiBTdHJlbmd0aAoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZSkgKyAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idygpICsgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCA9IFNsdW1wKSArICMgc2VsZWN0IHZhcmlhYmxlIHRvIHByaW50Li4uIFlvdSBjYW4gZ2V0IHJlYWxseSBmYW5jeSBoZXJlIGxhdGVyCiAgCiAgeGxpbSggMCwgMzAgKSArICAgICAgICAgICMgYWRkaW5nIHgtYXhpcyBsaW1pdHMKCiAgZ2d0aXRsZSgiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiKSArICNDdXN0b20gVGl0bGUKICAKICB4bGFiKCJTbHVtcCAoY20pIikgKyAjIE5FVyA6IEN1c3RvbSBBeGlzIExhYmVsCgogIGdlb21faGlzdG9ncmFtKGZpbGw9ImRhcmtncmVlbiIpICMgaW5zZXJ0IGhpc3RvZ3JhbSAod2l0aCBhIHNpbmdsZSBjaG9zZW4gY29sb3IpCgpgYGAKCiMjIDUuMiBEaXN0cmlidXRpb24gUGxvdCBbbm90IHNvIGdvb2QgYW5dIEV4YW1wbGUKClRoZXJlIGFyZSBzb21lIG90aGVyIHBsb3RzIHRoYXQgd2UgY2FuIHVzZSB0byBkZXNjcmliZSBvdXIgZGF0YS4KCkhlcmUgdG8gcGxheSB3aXRoIHRoZW0gd2Ugd2lsbCB0YWtlIGEgcXVpY2sgc3RlcCBiYWNrIGFuZCBhZGRyZXNzIHRoYXQgInRpZHkiJ2VkIChzaG91bGQgdGhhdCBzYXkgInRpZGllZCI/KSBkYXRhZnJhbWUgImNvbmNyZXRlX3RpZHkiCgpXZSBjYW4gbm93IHVzZSBhbGwgdGhlIHBhcmFtZXRlcnMgaW4gdGhlICJ0aWR5IiAobG9uZykgZGF0YSBmcmFtZSB0byBwcmludCBieSBzcGVjaWZpYyB0cmFpdHMuCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGVfdGlkeSkgKyAgICAgICAgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggICAgICA9IFZhbHVlLCAgICAgICAgICAgICAgICAgICAgICMgbWFwIHgtYXhpcyB2YWx1ZQogICAgICBjb2xvciAgPSBQYXJhbWV0ZXIpICsgICAgICAgICAgICAgICAjIG1hcCBjb2xvcnMgZm9yIGRpZmZlcmVudCBxdWFsaXR5CiAgCiAgZ2d0aXRsZSgiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiKSArICMgQ3VzdG9tIFRpdGxlCiAgCiAgeGxhYigiVmFsdWUiKSArICAgICAgICAgICAgICAgICAgICAgICAgICMgIEN1c3RvbSBBeGlzIExhYmVsCgogIGdlb21fZGVuc2l0eSgpICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluc2VydCBjcmV0ZSBhIHJlbGF0aXZlIGRlbnNpdHkgcGxvdCAKCmBgYAoKSW4gdGhlIHBhc3QsIEkndmUgZ290dGVuIGdvb2QgcmVzdWx0cyB3aXRoIHRoaXMgYnV0IGluIHRoaXMgY2FzZSwgSSB0aGluayBpdCdzIHRvbyBtZXNzeSBpbiBwYXJ0IGR1ZSB0byB0aGUgZGlzcGFyaXR5IGluIHRoZSBkeW5hbWljIHJhbmdlIG9mIG91ciBwYXJhbWV0ZXJzLgoKIyMgNS4zLiBCb3gtV2hpc2tlciBQbG90IEV4YW1wbGUKCkhvdyBhYm91dCBsZXZlcmFnaW5nIGEgYm94IHdoaXNrZXI/IChJJ20gdXNpbmcgb25seSB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHRoaXMgdGltZS4pCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGVfaW5kZXBlbmRlbnQpICsgICAgICAjIEVESVQgQ2hhbmdpbmcgZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoICkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArICAgIyBhZGRpbmcgYW4gZXh0cmEgdHJhaXQgdG8gdGhlIHgtYXhpcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0byBub3QgcHJpbnQgbGFiZWxzIG9uIHRoZSB4LWF4aXMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICh0aGUgbGFiZWxzIG92ZXJsYXAgYW5kIGRvZXNuJ3QgbG9vawogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwcmV0dHkuLi4pCiAgCiAgYWVzKHkgICAgICA9IFZhbHVlLCAgICAgICAgICAgICAgICAgICAgICMgbWFwIHktYXhpcyB2YWx1ZQogICAgICB4ICAgICAgPSBQYXJhbWV0ZXIsICAgICAgICAgICAgICAgICAjIG1hcCB4LWF4aXMgdmFsdWUKICAgICAgY29sb3IgID0gUGFyYW1ldGVyKSArICAgICAgICAgICAgICAgIyBtYXAgY29sb3JzIGZvciBkaWZmZXJlbnQgcXVhbGl0eQogIAogIGdndGl0bGUobGFiZWwgICAgPSAiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiLAogICAgICAgICAgc3VidGl0bGUgPSAiQ29uY3JldGUgVGVzdCBDb21wb25lbnRzIikgKyAjIEN1c3RvbSBUaXRsZQogIAogIHlsYWIoZXhwcmVzc2lvbignQW1vdW50IChrZyBtJ14tMyoiKSIpKSArICMgRURJVCA6IENoYW5naW5nIEN1c3RvbSBBeGlzIExhYmVsCgogIGdlb21fYm94cGxvdCgpICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluc2VydCBjcmV0ZSBhIHJlbGF0aXZlIGRlbnNpdHkgcGxvdCAKCmBgYAoKV2hhdCBhYm91dCBvdXIgZGVwZW5kZW50IHZhcmlhYmxlcz8gV2UgY2FuIHN0YXJ0IGJ5IGNoYW5naW5nIHRoZSBkYXRhIGZyYW1lLi4uCgpgYGB7cn0KCmdncGxvdChkYXRhID0gY29uY3JldGVfZGVwZW5kZW50KSArICAgICAgIyBFRElUIENoYW5naW5nIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfYmxhbmsoKSkgKyAgICMgYWRkaW5nIGFuIGV4dHJhIHRyYWl0IHRvIHRoZSB4LWF4aXMKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdG8gbm90IHByaW50IGxhYmVscyBvbiB0aGUgeC1heGlzIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAodGhlIGxhYmVscyBvdmVybGFwIGFuZCBkb2Vzbid0IGxvb2sKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgcHJldHR5Li4uKQogIAogIGFlcyh5ICAgICAgPSBWYWx1ZSwgICAgICAgICAgICAgICAgICAgICAjIG1hcCB5LWF4aXMgdmFsdWUKICAgICAgeCAgICAgID0gUGFyYW1ldGVyLCAgICAgICAgICAgICAgICAgIyBtYXAgeC1heGlzIHZhbHVlCiAgICAgIGNvbG9yICA9IFBhcmFtZXRlcikgKyAgICAgICAgICAgICAgICMgbWFwIGNvbG9ycyBmb3IgZGlmZmVyZW50IHF1YWxpdHkKICAKICBnZ3RpdGxlKGxhYmVsICAgID0gIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIiwKICAgICAgICAgIHN1YnRpdGxlID0gIkNvbmNyZXRlIFRlc3QgUmVzdWx0cyIpICsgIyBDdXN0b20gVGl0bGUKICAKICB5bGFiKCJWYWx1ZXMiKSArCgogIGdlb21fYm94cGxvdCgpICAgICAgICAgICAgICAgICAgICAgICAgICAjIGluc2VydCBjcmV0ZSBhIHJlbGF0aXZlIGRlbnNpdHkgcGxvdCAKCmBgYAoKV2FudCB1bml0cz8gVGhhdCdzIGEgbGl0dGxlIHRvdWdoZXIgaGVyZSBzaW5jZSB0aGUgdW5pdHMgZGlmZmVyIGJ5IHBhcmFtZXRlci4gV2UgY2FuIGZvcmNlIHRoZSB2YWx1ZXMgdG8gaW50byBuZXcgbmFtZXMgdGhvdWdoLgoKYGBge3J9CgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlX2RlcGVuZGVudCkgKyAgICAgICMgRURJVCBDaGFuZ2luZyBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsgICAjIGFkZGluZyBhbiBleHRyYSB0cmFpdCB0byB0aGUgeC1heGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIG5vdCBwcmludCBsYWJlbHMgb24gdGhlIHgtYXhpcyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgKHRoZSBsYWJlbHMgb3ZlcmxhcCBhbmQgZG9lc24ndCBsb29rCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHByZXR0eS4uLikKICAKICBhZXMoeSAgICAgID0gVmFsdWUsICAgICAgICAgICAgICAgICAgICAgIyBtYXAgeS1heGlzIHZhbHVlCiAgICAgIHggICAgICA9IFBhcmFtZXRlciwgICAgICAgICAgICAgICAgICMgbWFwIHgtYXhpcyB2YWx1ZQogICAgICBjb2xvciAgPSBQYXJhbWV0ZXIpICsgICAgICAgICAgICAgICAjIG1hcCBjb2xvcnMgZm9yIGRpZmZlcmVudCBxdWFsaXR5CiAgCiAgZ2d0aXRsZShsYWJlbCAgICA9ICJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJDb25jcmV0ZSBUZXN0IFJlc3VsdHMiKSArICMgQ3VzdG9tIFRpdGxlCiAgCiAgeWxhYigiVmFsdWVzIikgKwoKICAjIE5FVzogSXQgc2F5cyBzY2FsZSBjb2xvciBidXQgImNvbG9yIiBpcyBob3cgd2UgYXJlIGRpc3Rpbmd1aXNoaW5nCiAgIyAgICAgIG91dCBib3hwbG90cyAoYXMgc2VlbiBpbiB0aGUgbWFwcGluZy9hZXMgY29tbWFuZCkKICAjICAgICAgd2UgY2FuIHRoZW4gdXNlIHRoZSBzYW1lIHBsb3Qgb3JkZXIgYWJvdmUgdG8gcmV3cml0ZSB0aGUgbGFiZWxzCiAgIyAgICAgIChsaWtld2lzZSB3ZSBjb3VsZCBjaGFuZ2UgdGhlIHBsb3Qgb3JkZXIgYW5kIG9mIGNvcnVzZSB0aGUgY29sb3JzLikKICBzY2FsZV9jb2xvcl9kaXNjcmV0ZShsYWJlbHMgPSBjKCJTbHVtcCAoY20pIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJGbG93IChjbSkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyOGR5LUNvbXByZXNpb25hbCBTdHJlc3MgKG1QYSkiKSkgKyAKICAKICBnZW9tX2JveHBsb3QoKSAjIGluc2VydCBjcmV0ZSBhIHJlbGF0aXZlIGRlbnNpdHkgcGxvdCAKICAKCmBgYAoKIyMgNS40LiBWaW9saW4gUGxvdCBFeGFtcGxlCgpIb3cgYWJvdXQgbGV2ZXJhZ2luZyBhICJ2aW9saW4iIHBsb3Q/IEEgdmlvbGluIHBsb3QncyB3aWR0aCBzd2VsbHMgaW4gYXJlYXMgd2l0aCBtb3JlIG9ic2VydmF0aW9ucyBhbmQgY29udHJhY3RzIHdpdGggc3BhcnNlciBkYXRhIHNvIGl0IGlzIGxpa2UgbG9va2luZyBhdCBhIHByb2JhYmlsaXR5IGRpc3RyaWJ1dGlvbi4KCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZV9pbmRlcGVuZGVudCkgKyAgICAgICMgRURJVCBDaGFuZ2luZyBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCkpICsgICAjIGFkZGluZyBhbiBleHRyYSB0cmFpdCB0byB0aGUgeC1heGlzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHRvIG5vdCBwcmludCBsYWJlbHMgb24gdGhlIHgtYXhpcyAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgKHRoZSBsYWJlbHMgb3ZlcmxhcCBhbmQgZG9lc24ndCBsb29rCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIHByZXR0eS4uLikKICAKICBhZXMoeSAgICAgID0gVmFsdWUsICAgICAgICAgICAgICAgICAgICAgIyBtYXAgeS1heGlzIHZhbHVlCiAgICAgIHggICAgICA9IFBhcmFtZXRlciwgICAgICAgICAgICAgICAgICMgbWFwIHgtYXhpcyB2YWx1ZQogICAgICBjb2xvciAgPSBQYXJhbWV0ZXIpICsgICAgICAgICAgICAgICAjIG1hcCBjb2xvcnMgZm9yIGRpZmZlcmVudCBxdWFsaXR5CiAgCiAgZ2d0aXRsZShsYWJlbCAgICA9ICJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIsCiAgICAgICAgICBzdWJ0aXRsZSA9ICJDb25jcmV0ZSBUZXN0IENvbXBvbmVudHMiKSArICMgQ3VzdG9tIFRpdGxlCiAgCiAgeWxhYihleHByZXNzaW9uKCdBbW91bnQgKGtnIG0nXi0zKiIpIikpICsgIyAgQ2hhbmdpbmcgQ3VzdG9tIEF4aXMgTGFiZWwKCiAgZ2VvbV92aW9saW4oc2NhbGU9IndpZHRoIikgIyBFRElUOiBjaGFuZ2UgdG8gYSB2aW9saW4gcGxvdCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICAgdGhlIHdpZHRoIGFyZ3VtZW50IAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgZ2l2ZXMgZXZlcnkgcGxvdCB0aGUgc2FtZSB3aWR0aAogIAoKYGBgCgphbmQuLi4KCmBgYHtyfQoKZ2dwbG90KGRhdGEgPSBjb25jcmV0ZV9kZXBlbmRlbnQpICsgICAgICAjIEVESVQgQ2hhbmdpbmcgZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoICkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGNoYW5naW5nIHRoZSBwbG90dGluZyB0aGVtZQogIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF9ibGFuaygpKSArICAgIyBhZGRpbmcgYW4gZXh0cmEgdHJhaXQgdG8gdGhlIHgtYXhpcwogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0byBub3QgcHJpbnQgbGFiZWxzIG9uIHRoZSB4LWF4aXMgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjICh0aGUgbGFiZWxzIG92ZXJsYXAgYW5kIGRvZXNuJ3QgbG9vawogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBwcmV0dHkuLi4pCiAgCiAgYWVzKHkgICAgICA9IFZhbHVlLCAgICAgICAgICAgICAgICAgICAgICMgbWFwIHktYXhpcyB2YWx1ZQogICAgICB4ICAgICAgPSBQYXJhbWV0ZXIsICAgICAgICAgICAgICAgICAjIG1hcCB4LWF4aXMgdmFsdWUKICAgICAgY29sb3IgID0gUGFyYW1ldGVyKSArICAgICAgICAgICAgICAgIyBtYXAgY29sb3JzIGZvciBkaWZmZXJlbnQgcXVhbGl0eQogIAogIGdndGl0bGUobGFiZWwgICAgPSAiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiLAogICAgICAgICAgc3VidGl0bGUgPSAiQ29uY3JldGUgVGVzdCBSZXN1bHRzIikgKyAjIEN1c3RvbSBUaXRsZQogIAogIHlsYWIoIlZhbHVlcyIpICsKCiAgIyBORVc6IEl0IHNheXMgc2NhbGUgY29sb3IgYnV0ICJjb2xvciIgaXMgaG93IHdlIGFyZSBkaXN0aW5ndWlzaGluZwogICMgICAgICBvdXQgYm94cGxvdHMgKGFzIHNlZW4gaW4gdGhlIG1hcHBpbmcvYWVzIGNvbW1hbmQpCiAgIyAgICAgIHdlIGNhbiB0aGVuIHVzZSB0aGUgc2FtZSBwbG90IG9yZGVyIGFib3ZlIHRvIHJld3JpdGUgdGhlIGxhYmVscwogICMgICAgICAobGlrZXdpc2Ugd2UgY291bGQgY2hhbmdlIHRoZSBwbG90IG9yZGVyIGFuZCBvZiBjb3J1c2UgdGhlIGNvbG9ycy4pCiAgc2NhbGVfY29sb3JfZGlzY3JldGUobGFiZWxzID0gYygiU2x1bXAgKGNtKSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRmxvdyAoY20pIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMjhkeS1Db21wcmVzaW9uYWwgU3RyZXNzIChtUGEpIikpICsgCiAgCgogIGdlb21fdmlvbGluKHNjYWxlPSJ3aWR0aCIpICMgRURJVDogY2hhbmdlIHRvIGEgdmlvbGluIHBsb3QgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgIHRoZSB3aWR0aCBhcmd1bWVudCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAjIGdpdmVzIGV2ZXJ5IHBsb3QgdGhlIHNhbWUgd2lkdGggIAoKYGBgCgpUaGlzIGlzIGJhc2ljYWxseSB0aGUgYWJvdmUgImRlbnNpdHkiIHBsb3QgYnV0ICJsb29raW5nIGRvd24iIGFzIHdpdGggYSBib3ggcGxvdC4gQWxzbyBoZXJlIHdlIGFyZSB0cmltbWluZyB0aGUgcGxvdCBzbyB0aGF0IHdoZW4gd2UgbGVhdmUgdGhlIHJhbmdlIG9mIGFueSBvZiB0aGUgZGF0YSBwb2ludHMsIHRoZSAidmlvbGlucyIgYXJlIHRydW5jYXRlZC4KCiMjIDUuNS4gU3RhY2tlZCBDb2x1bW4gb3IgQmFyIFBsb3QgRXhhbXBsZQoKV2UgYWxzbyBjYW4gZG8gYmFyIHBsb3RzIG9yIHN0YWNrZWQgY29sdW1uIHBsb3RzLiBUaGUgb25lIHByb2R1Y2VkIGhlcmUgc2hvd3MgdGhlIGNvbWJpbmVkIGNvbXBvbmVudHMgYnkgdGVzdCB1bml0LgoKYGBge3J9CgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlX2luZGVwZW5kZW50KSArICAgICAgIyBFRElUIENoYW5naW5nIGRhdGFmcmFtZQogIAogIHRoZW1lX2J3KCApICsgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKCiAgCiAgYWVzKHggICAgID0gVGVzdF9OdW1iZXIsCiAgICAgIHkgICAgID0gVmFsdWUsCiAgICAgIGZpbGwgID0gUGFyYW1ldGVyKSArICAgICAgICAgICAgICAgIyBtYXAgY29sb3JzIGZvciBkaWZmZXJlbnQgcXVhbGl0eQogIAogIGdndGl0bGUobGFiZWwgICAgPSAiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiLAogICAgICAgICAgc3VidGl0bGUgPSAiQ29uY3JldGUgVGVzdCBDb21wb25lbnRzIikgKyAjIEN1c3RvbSBUaXRsZQogIAogIHlsYWIoZXhwcmVzc2lvbignQW1vdW50IChrZyBtJ14tMyoiKSIpKSArICMgIENoYW5naW5nIEN1c3RvbSBBeGlzIExhYmVsCgogIGdlb21fY29sKHBvc2l0aW9uID0gInN0YWNrIiwgICMgbmV3LCBjcmVhdGUgYSBzdGFjZWtkIGNvbHVtbiBncmFwaCAKICAgICAgICAgICB3aWR0aCAgICA9IDEuMCAgICApICAjIHdpdGggbm8gc3BhY2UgYmV0d2VlbiBjb2x1bW5zCgpgYGAKCiMgNi4gQ29ycmVsYXRpb24gb2YgVmFyaWFibGVzCgojIyA2LjEuIENvcnJlbGF0aW5nIGFuZCB0aGVuIEZpdHRpbmcgQ2VtZW50IHRvIENvbXByZXNzaXZlIFN0cmVuZ3RoCgpMZXQncyBzdGFydCBieSBkb2luZyBhICJzaW1wbGUiIiBwbG90IC4gSW4gdGhpcyBjYXNlIHNpbmNlIEkgYWxyZWFkeSBrbm93IHRoZSBhbnN3ZXIgYmVjYXVzZSB0aGUgc3ByZWFkc2hlZXQgYWxzbyBoYXMgYSB0YWJsZSBvZiBob3cgd2VsbCBvdXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGNvcnJlbGF0ZSBhZ2FpbnN0IHRoZSBkZXBlbmRlbnQgdmFyaWFibGVzIChlLmcuLCBTbHVtcCwgRmxvdywgb3IgaW4gb3VyIGNhc2UgU3RyZW5ndGgpLiBUaGUgQ2VtZW50IGNvcnJlbGF0ZXMgdGhlIGJlc3QgYWdhaW5zdCBDb21wcmVzc2l2ZSBTdHJlbmd0aCAoT0ssIHRydXRoIGJlIHRvbGQsIGl0IGNvcnJlbGF0ZXMgdGhlIGxlYXN0IGJhZGx5KS4KCldlIGNhbiBhY3R1YWxseSBkbyB0aGlzIHdpdGggYSBjb3JyZWxhdGUgZnVuY3Rpb24sIFtib290Ojpjb3IoKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9jb3IpLi4uCgpUbyBncmFiIGEgdmFsdWUgaW4gdGhlIHRhYmxlICJjb25jcmV0ZSIgd2UgY2FsbCB0aGUgZGF0YSBmcmFtZSAoY29uY3JldGUpIGFuZCB0aGUgdmFyaWFibGUgbmFtZSAoQ2VtZW50IG9yIFdhdGVyIHZzIENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpLCBzZXBhcmF0aW5nIHRoZSBmcmFtZSBhbmQgdmFyaWFibGUgbmFtZXMgYnkgYSBcJCBzaWduLgoKYGBge3J9CgpwcmludCgiQ2VtZW50IHZzIENvbXByZXNzaXZlIFN0cmVuZ3RoIENvcnJlbGF0aW9uLCByIikKCmNvcih4ID0gY29uY3JldGUkQ2VtZW50LCAgICAgICAgICAgICAgICAgICAgIyB0aGUgeC12YWx1ZSAKICAgIHkgPSBjb25jcmV0ZSRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5LCAjIHRoZSB5LXZhbHVlCiAgICBtZXRob2QgPSAicGVhcnNvbiIgICAgICAgICAgICAgICAgICAgICAgIyBtZXRob2Qgb2YgY29ycmVsYXRpb24KICAgICkKCmBgYAoKb3IgaWYgeW91IGxpa2UgdG8gZG8gZXZlcnl0aGluZyBhdCBvbmNlLi4uCgpgYGB7cn0KCiMgY2FsY3VsYXRlIGFsbCBjb3JyZWxhdGlvbiB2YWx1ZXMgYWdhaW5zdCBlYWNoIG90aGVyCgpjb3JyZWxhdGlvbl9tYXRyaXggPSBjb3IoeCAgICAgID0gY29uY3JldGUsICMgdXNpbmcgb3VyIGRhdGFmcmFtZSB0byBjb3JyZWxhdGUgZXZ5dGhpbmcKICAgICAgICAgICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJwZWFyc29uIiApCgp0YmxfZGYoY29ycmVsYXRpb25fbWF0cml4KQoKYGBgCgpMb3RzIG9mIG51bWJlcnMuLi4gbm90IGFsbCB0aGF0IGluc2lnaHRmdWwgb24gdGhlaXIgb3duLi4uCgpZb3UgYWxzbyBjYW4gZ3JhcGggdGhlIGxvb2stbi1mZWVsIG9mIHdoYXQgYWxsIG9mIHRoZSBkaWZmZXJlbnQgY29ycmVsYXRpb25zIGFyZS4uLiAoaXQgd29ya3MgYmVzdCB3aXRoIGEgbXVjaCBzbWFsbGVyIG51bWJlciBvZiB2YXJpYWJsZXMpLiAgVGhpcyBmdW5jdGlvbiBpcyBjYWxsZWQgW2NvcnJwbG90Ojpjb3JycGxvdCgpXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvY29ycnBsb3QvdmlnbmV0dGVzL2NvcnJwbG90LWludHJvLmh0bWwpIGFuZCBtYWtlcyBhIG5pY2UgcHJlc2VudGF0aW9uIG9mIHRoZSBkYXRhLgoKYGBge3J9CgogICMgZHJhdyBhIGNvcnJlbGF0aW9uIGdyYXBoaWMuLi4KCiAgY29ycnBsb3QoY29yciAgID0gY29ycmVsYXRpb25fbWF0cml4LAogICAgICAgICAgIHR5cGUgICA9ICJ1cHBlciIpCgpgYGAKCldlIGNhbiBub3cgc2VlIGZvciBleGFtcGxlIHRoYXQgY2VtZW50LCBzbGFnLCBhbmQgZmx5IGFzaCBhbW91bnRzIGhhdmUgYSBub21pbmFsIGJ1dCBub3QgdGhyaWxsaW5nIGNvcnJlbGF0aW9uIHRvIGNvbXByZXNzaW9uIHN0cmVuZ3RoIHdoaWxlIHdhdGVyIGhhcyBhIGdvb2QgY29ycmVsYXRpb24gd2l0aCB0aGUgcmVzdWx0aW5nIHNsdW1wIHZhbHVlcy4gT25lIHRoaW5nIHRoYXQgdGhpcyBkb2VzICpub3QqIHNob3cgaXMgaG93IHdlbGwgdGhlc2UgcGFyYW1ldGVycyBwbGF5IHdpdGggb3RoZXIgcGFyYW1ldGVycy4gQXMgd2UnbGwgc2VlIHdoZW4gYWxsIG9mIG91ciBpbmRlcGVuZGVudCB2YWx1ZXMgYXJlIHdvcmtpbmcgdG9nZXRoZXIgd2UnbGwgZGlzY292ZXIgdGhhdCBjZW1lbnQgYW5kIHdhdGVyLCBmb2xsb3dlZCBieSBmbHkgYXNoIGFuZCBjb2Fyc2UgYWdncmVnYXRlcyB3aWxsLCB0b2dldGhlciwgY29udHJpYnV0ZSB0aGUgbW9zdCBvZiBvdXIgaW5kZXBlbmRlbnQgcGFyYW1ldGVycyBpbiBjYWxjdWxhdGluZyB0aGUgY29tcHJlc3NpdmUgc3RyZW5ndGguCgojIyA2LjIuIFNjYXR0ZXIgUGxvdCBFeGFtcGxlCgpCdXQgZm9yIG5vdywgbGV0J3MgcGxvdCBwbG90IHRoZSBDZW1lbnQgYW1vdW50IGFnYWluc3QgQ29tcHJlc3NpdmUgU3RyZW5ndGgKCmBgYHtyfQoKIyBNYWtpbmcgYSBzaW1wbGUgWC1ZIHNjYXR0ZXIgcGxvdC4KCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICAgICAgICAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCAgICAgID0gQ2VtZW50LCAgICAgICAgICAgICAgICAgICAgICAgIyB4LXZhbHVlCiAgICAgIHkgICAgICA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpICsgICMgeS12YWx1ZQoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgICAgIyBDdXN0b20gVGl0bGUKICAKICB4bGFiKGV4cHJlc3Npb24oJ0NlbWVudCBBbW91bnQgKGtnIG0nXjMqIikiKSkgKyAgICMgeC1sYWJlbAogIHlsYWIoIjI4LWR5IENvbXByZXNzaXZlIFN0cmVuZ3RoIChNUGEpIikgICAgICArICAgIyB5LWxhYmVsCgogIGdlb21fcG9pbnQoY29sb3VyPSJncmV5IikgICAjIEVESVQ6IHBsb3QgcG9pbnRzIHRoZSBjb2xvciBrZXl3b3JkIHBhcnQgd2FzCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgICAgICAgd3JpdGVuIGJ5IGFuIGFuZ2xvcGhpbGUhCgpgYGAKCkhlcmUncyBhIGN1dGUgdHJpY2s6IENvdWxkIHdlIGNvbG9yIHRob3NlIGRvdHMgYnkgYSB2YXJpYWJsZT8KClN1cmUhCgpgYGB7cn0KCiMgTWFraW5nIGEgc2ltcGxlIFgtWSBzY2F0dGVycGxvdCBub3cgY29sb3VyZWQgYnkgYW5vdGhlciBwYXJhbWV0ZXIKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICAgICAgICAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCAgICAgID0gQ2VtZW50LCAgICAgICAgICAgICAgICAgICAgICAgIyB4LXZhbHVlCiAgICAgIHkgICAgICA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHksICAgICMgeS12YWx1ZQogICAgICBjb2xvciAgPSBTdXBlcnBsYXN0aWNpemVyKSAgICAgICAgICArICAjIEFERDogd2UgY2FuIGNvbG9yIGJ5IGEgdmFyaWFibGUhCgogIGdndGl0bGUoIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIikgKyAgICAjIEN1c3RvbSBUaXRsZQogIAogIHhsYWIoZXhwcmVzc2lvbignQ2VtZW50IEFtb3VudCAoa2cgbSdeMyoiKSIpKSArICAgIyB4LWxhYmVsCiAgeWxhYigiMjgtZHkgQ29tcHJlc3NpdmUgU3RyZW5ndGggKE1QYSkiKSAgICAgICsgICAjIHktbGFiZWwKCiAgZ2VvbV9wb2ludCgpICsgICMgcGxvdCBwb2ludHMgCiAgc2NhbGVfY29sb3JfZGlzdGlsbGVyKHBhbGV0dGUgPSAiU3BlY3RyYWwiKSAjIE5FVzogcGljayBhIGN1c3RvbSAiY29sb3VyIiBwYWxhdGUuCgpgYGAKCkxvdmUgb3ZlcmtpbGwgd2l0aG91dCBhbnkgZGlzdGluY3QgbnVtZXJpY2FsIHNjb3JlIGFuZCBsb29rIGF0IGhvdyBldmVyeXRoaW5nIGluIHlvdXIgZGF0YSBzZXQgY29ycmVsYXRlcyB3aXRoIGV2ZXJ5IG90aGVyIHZhcmlhYmxlcy4uLj8KClRyeSBbZ3JhcGhpY3M6OnBhaXJzKCldKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ncmFwaGljcy92ZXJzaW9ucy8zLjUuMS90b3BpY3MvcGFpcnMpCgooSSBsaWtlIHRoZSBjb3JycGxvdCBmdW5jdGlvbiBiZXR0ZXIhKQoKYGBge3J9CgojIHdheSB0b28gbWFueSB0aW55IHBsb3RzIQoKcGFpcnMoeCAgID0gY29uY3JldGUsICMgZG8gZXZlcnl0aGluZyBpbiB0aGUgZGF0YWZyYW1lCiAgICAgIHBjaCA9ICIuIikgICAgICAjIHBsb3QgZG90cyAodGhlIGRlZmF1bHQgaXMgY2lyY2xlcykKCmBgYAoKKE9idmlvdXNseSB0aGUgbW9yZSB2YXJpYWJsZXMgaW4geW91ciBkYXRhZnJhbWUgdGhlIG1lc3NpZXIgaXQgZ2V0cyEpCgojIyA2LjMuIENyZWF0aW5nIG91ciBsaW5lYXIgbW9kZWwgYW5kICJjYWxpYnJhdGluZyIgaXQKCldlIHdlcmVuJ3QgYWxsIHRoYXQgdGhyaWxsZWQgd2l0aCB0aGUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGVzZSBjb21wb25lbnRzIGFuZCBzdHJlbmd0aCBidXQgbGV0J3MgZ28gYWhlYWQgYW5kIGRlbW9uc3RyYXRlIGEgcmVncmVzc2lvbi4KCkJ1dCBsZXQncyBtb3ZlIG9uIGFuZCBjcmVhdGUgYSByZWdyZXNzaW9uIG1vZGVsIGZyb20gdGhpcy4KCkhlcmUgd2Ugd2lsbCB1c2UgdGhlIFtzdGF0czo6bG0oKV0oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL3N0YXRzL3ZlcnNpb25zLzMuNC4zL3RvcGljcy9sbSkgKGxpbmVhciBtb2RlbCkgZnJvbSB0aGUgYmFzaWMgdG9vbGJveGVzIHRoYXQgY29tZSB3aXRoIFIuCgpGb3IgdGhlIHJlZ3Jlc3Npb24gZm9ybXVsYQoKJFx3aWRlaGF0e3l9KHgpID0ge1xhbHBoYV8wfSt7XGFscGhhXzF9XCB4JAoKb3IKCiRcd2lkZWhhdHtTdHJlbmd0aH0oY29uY3JldGUpID0ge1xhbHBoYV8wfSt7XGFscGhhXzF9XCBjb25jcmV0ZSQKCnRoZSAicHJvdG90eXBlIiAoZm9ybXVsYSkgZm9yIHRoZSBmdW5jdGlvbiBpcyB3cml0dGVuIGFzIC4uLgoKIlkgXH4gWCIgKHdpdGggdGhlIHktaW50ZXJjZXB0IGltcGxpY2l0IGluIHRoZSBmb3JtdWxhLi4uIHlvdSBkb24ndCBwdXQgaXQgaW4gYnV0IGl0J2xsIGJlIHRoZXJlIHdoZW4geW91J3JlIGRvbmUuKQoKVGhlIGFib3ZlIHN5bnRheCBpcyB3b3JrcyBsaWtlIHRoaXMuLi4uCgpEZXBlbmRlbnQgVmFyaWFibGUgW1x+IGlzIGEgZnVuY3Rpb24gb2YgXSBJbmRlcGVuZGVudCBWYXJpYWJsZSBbYW5kIGFueSBvdGhlciBwYXJhbWV0ZXIgeW91IG5lZWQgZ2V0cyBhZGRlZCB3aXRoIGEgcGx1c10KCklmIHRoaXMgd2VyZSBhICRcd2lkZWhhdHt5fSh4KT17XGFscGhhXzB9K3tcYWxwaGFfMH1cIHheMyQsIHRoZW4gdGhlIHByb3RvdHlwZSBmb3IgdGhlIGZ1bmN0aW9uIHdvdWxkIGJlIHkgXH4geFxeMwoKVGhpcyB3aWxsIGhvcGVmdWxseSBtYWtlIG1vcmUgc2Vuc2UgYXMgd2UgY29udGludWUhCgoqKGxtIGFuZCBzaW1pbGFyIGxpbmVhciByZWdyZXNzaW9uIGZ1bmN0aW9ucyBkb24ndCBwbGF5IHdlbGwgd2l0aCB1bml0cy4pKgoKYGBge3J9CgpsaW5lYXJfbW9kZWwuU192X2MgPSAgbG0oZm9ybXVsYSA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkgfiBDZW1lbnQsICMgeW91ciBmb3JtdWxhIHkgfiB4CiAgICAgICAgICAgICAgICAgICAgICAgICBkYXRhICAgID0gY29uY3JldGUpICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB0aGUgZGF0YSBmcmFtZQpgYGAKCkxldCdzIHNlZSB3aGF0IHdlIGhhdmUuLi4gVGhpcyBzdW1tYXJ5IGNvbW1hbmQgd2lsbCBwcm92aWRlIHRoZSBkZXRhaWxzIG9mIHRoZSBsbSgpIGZ1bmN0aW9uJ3MgaW1wb3J0YW50IHJlc3VsdHMKCkZvciB1cyB3ZSB3YW50IHRvIHNlZSB0aGUgWS1JbnRlcmNlcHQgW3RoZSAoSW50ZXJjZXB0KSB1bmRlciAiRXN0aW1hdGUiXSBhbmQgdGhlIHNsb3BlIHRoYXQgZ29lcyB3aXRoIG91ciBpbmRlcGVuZGVudCB2YWx1ZSAoIkNvbmNyZXRlIiB1bmRlciAiRXN0aW1hdGUiKQoKVGhlIFN0YW5kYXJkIEVycm9yIG9mIHRoZSBFc3RpbWF0ZSBpcyB0aGVyZSAoUmVzaWR1YWwgU3RhbmRhcmQgRXJyb3IpIGFzIGlzIHRoZSBDb2VmZmljaWVudCBvZiBEZXRlcm1pbmF0aW9uIChNdWx0aXBsZSBSLXNxdWFyZWQpCgpXZSdsbCB0YWxrIGFib3V0IGEgZmV3IG9mIHRoZSBvdGhlciBmZWF0dXJlcyB3aGVuIHdlIGRvIHRoZSBsYXJnZXIgbXVsdGl2YXJpYXRlIHJlZ3Jlc3Npb24KCmBgYHtyfQoKIHN1bW1hcnkob2JqZWN0ID0gbGluZWFyX21vZGVsLlNfdl9jKQoKYGBgCgpJbiB0aGUgYWJvdmUgb3V0cHV0LCB0aGUgYXN0ZXJpc2sgaWRlbnRpZnkgdGhlIG1vc3Qgc2lnbmlmaWNhbnQgaW5kZXBlbmRlbnQgdmFyaWFibGVzLiBIZXJlIGl0J3MgdHJpdmlhbCBldmVuIHRob3VnaCB0aGlzIGlzIGEgdGVycmlibGUgcmVsYXRpb25zaGlwIGJldHdlZW4gY2VtZW50IGFuZCBzdHJlbmd0aC4gTGF0ZXIgd2Ugd2lsbCB1c2UgYWxsIG9mIG91ciBhdmFpbGFibGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGFuZCB0aGUgdXNlIG9mIHRoZXNlIGFzdGVyaXNrcyB3aWxsIGJlY29tZSBtb3JlIGltcG9ydGFudC4KCldhbnQgdG8gcGxvdCBpdD8KCkdvb2QgbmV3cz8KCkxpa2UgRXhjZWwsIHlvdSBoYXZlIHNvbWUgYXV0b21hdGVkIGZlYXR1cmVzIHRvIGdpdmUgeW91IHF1aWNrIHNhdGlzZmFjdGlvbiBhbmQgaGFwcGluZXNzLiBNb3JlIHN0aWxsLCBpdCB3aWxsIGdpdmUgeW91IGNvbmZpZGVuY2UgbGltaXRzLgoKRm9yIHRoaXMgd2UgdXNlIGFuIGV4dGVuc2lvbiB0byB0aGUgZ3JhcGhpY3MgcGFja2FnZSBjYWxsZWQgW2dncGxvdDI6Omdlb21fc21vb3RoKCldKGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnL3JlZmVyZW5jZS9nZW9tX3Ntb290aC5odG1sKQoKYGBge3J9CgojIE1ha2luZyBhIHNpbXBsZSBYLVkgc2NhdHRlcnBsb3QgYW5kIGFkZGluZyBhIHJlZ3Jlc3Npb24gdG8gaXQKCmdncGxvdChkYXRhID0gY29uY3JldGUpICsgICAgICAgICAgICAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCAgICAgID0gQ2VtZW50LCAgICAgICAgICAgICAgICAgICAgICAgIyB4LXZhbHVlCiAgICAgIHkgICAgICA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpICsgICMgeS12YWx1ZQoKICBnZ3RpdGxlKCJZZWggU3VwZXJwbGFzdGljaXplciBUZXN0cyIpICsgICAgIyBDdXN0b20gVGl0bGUKICAKICB4bGFiKGV4cHJlc3Npb24oJ0NlbWVudCBBbW91bnQgKGtnIG0nXi0zKiIpIikpICsgICAjIHgtbGFiZWwKICB5bGFiKCIyOC1keSBDb21wcmVzc2l2ZSBTdHJlbmd0aCAoTVBhKSIpICAgICAgKyAgICMgeS1sYWJlbAoKICBnZW9tX3BvaW50KGNvbG91cj0iZGFya2dyZXkiKSArICAjIHBsb3QgcG9pbnRzCiAgZ2VvbV9zbW9vdGgobWV0aG9kICA9ICJsbSIsICAgICMgdXNlIGEgc2ltcGxlIGxpbmFyIG1vZGVsCiAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LCAgICMgbG0tc3R5bGUgZm9ybXVsYQogICAgICAgICAgICAgIHNlICAgICAgPSBUUlVFLCAgICAjIHNwbGF5IENvbmZpZGVuY2UgSW50ZXJ2YWxzCiAgICAgICAgICAgICAgbGV2ZWwgICA9IDAuOTUsICAgICMgQ29uZmlkZW5lIExldmVsIHRvIE1hcCBPdXQKICAgICAgICAgICAgICBjb2xvdXIgID0gImJsYWNrIiwgIyByZWdyZXNzaW9uIGxpbmUgY29sb3IKICAgICAgICAgICAgICBzaXplICAgID0gMC41KSAgICAgIyBsaW5lIHRoaWNrbmVzcwoKYGBgCgpUaGUgbGluZSBoZXJlIGxvb2tzIGxpa2UgYSBwb3NpdGl2ZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSBjZW1lbnQgYW1vdW50IGFuZCB0aGUgcmVzdWx0aW5nIHN0cmVuZ3RoLgoKTGV0J3MgdHJ5IHdhdGVyOgoKYGBge3J9CgojIGdldHRpbmcgdGhlIGxpbmVhciBtb2RlbAoKCmxpbmVhcl9tb2RlbC5TX3ZfdyA9ICBsbShmb3JtdWxhID0gQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeSB+IFdhdGVyLCAjIHlvdXIgZm9ybXVsYSB5IH4geAogICAgICAgICAgICAgICAgICAgICAgICAgZGF0YSAgICA9IGNvbmNyZXRlICAgKSAgICAgICAgICAgICAgICAgICAgICAgICAgICMgdGhlIGRhdGEgZnJhbWUKCnN1bW1hcnkobGluZWFyX21vZGVsLlNfdl93KQpgYGAKCmBgYHtyfQoKIyBNYWtpbmcgYSBzaW1wbGUgWC1ZIHNjYXR0ZXJwbG90IGFuZCBhZGRpbmcgYSByZWdyZXNzaW9uIHRvIGl0CgpnZ3Bsb3QoZGF0YSA9IGNvbmNyZXRlKSArICAgICAgICAgICAgICAgICMgaW52b2tlIGdyYXBoaWNzIGVudmlyb25tZW50IHVzaW5nIGEgZ2l2ZW4gZGF0YWZyYW1lCiAgCiAgdGhlbWVfYncoICkgKyAgICAgICAgICAgICAgICAgICAgICAgICAgICMgY2hhbmdpbmcgdGhlIHBsb3R0aW5nIHRoZW1lCiAgCiAgYWVzKHggICAgICA9IFdhdGVyLCAgICAgICAgICAgICAgICAgICAgICAjIHgtdmFsdWUKICAgICAgeSAgICAgID0gQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeSkgKyAgIyB5LXZhbHVlCgogIGdndGl0bGUoIlllaCBTdXBlcnBsYXN0aWNpemVyIFRlc3RzIikgKyAgICAjIEN1c3RvbSBUaXRsZQogIAogIHhsYWIoZXhwcmVzc2lvbignV2F0ZXIgQW1vdW50IChrZyBtJ14tMyoiKSIpKSArICAjIHgtbGFiZWwKICB5bGFiKCIyOC1keSBDb21wcmVzc2l2ZSBTdHJlbmd0aCAoTVBhKSIpICAgICAgKyAgICMgeS1sYWJlbAoKICBnZW9tX3BvaW50KGNvbG91cj0iZGFya2JsdWUiKSArICAjIHBsb3QgcG9pbnRzCiAgCiAgZ2VvbV9zbW9vdGgobWV0aG9kICA9ICJsbSIsICAgICMgdXNlIGEgc2ltcGxlIGxpbmFyIG1vZGVsCiAgICAgICAgICAgICAgZm9ybXVsYSA9IHkgfiB4LCAgICMgbG0tc3R5bGUgZm9ybXVsYQogICAgICAgICAgICAgIHNlICAgICAgPSBUUlVFLCAgICAjIHNwbGF5IENvbmZpZGVuY2UgSW50ZXJ2YWxzCiAgICAgICAgICAgICAgbGV2ZWwgICA9IDAuOTUsICAgICMgQ29uZmlkZW5lIExldmVsIHRvIE1hcCBPdXQKICAgICAgICAgICAgICBjb2xvdXIgID0gImJsdWUiLCAgIyByZWdyZXNzaW9uIGxpbmUgY29sb3IKICAgICAgICAgICAgICBmaWxsICAgID0gImN5YW4iLCAgIyBORVc6IGZpbGwgZm9yIGNvbmZpZGVuY2UgbGltaXRzCiAgICAgICAgICAgICAgc2l6ZSAgICA9IDAuNSkgICAgICMgbGluZSB0aGlja25lc3MKCmBgYAoKTG9va2luZyB1cCBiYWNrIHRoZSB0YWJsZXMgbm9uZSBvZiB0aGUgdmFyaWFibGVzCgojIDcuIE11bHRpdmFyaWF0ZSBMaW5lYXIgUmVncmVzc2lvbgoKQW5kIG5vdyB3ZSdyZSBnb2luZyB0byBkbyBzb21ldGhpbmcgYWJvdXQgdGhhdCEKCldlJ3JlIG5vdyBnb2luZyB0byB1c2Ugbm90IGp1c3Qgb25lIGluZGVwZW5kZW50IHZhcmlhYmxlLi4uIGJ1dCBhbGwgNyBvZiB0aGVtIQoKVGhlIGdvb2QgbmV3cyBpcyB0aGF0IGl0IGZvbGxvd3MgdGhlIHNhbWUgZm9ybSBhcyB0aGUgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uLiBUaGlzIHRpbWUgd2Ugc3RyaW5nIGFsb25nIGFsbCBvZiBvdXIgaW5kZXBlbmRlbnQgdmFyaWFibGVzIHdpdGggaW4gb3VyIGZvcm11bGEgcHJvdG90eXBlLgoKT3VyIGZvcm11bGEgbm93IGhhcyBtdWx0aXBsZSBpbmRlcGVuZGVudCB2YWx1ZXMgYnV0IHN0aWxsIGZvbGxvd3MgdGhlIHNhbWUgc3R5bGUgb2Ygc29sdXRpb24uLi4KCiRcd2lkZWhhdHt5fShcbWF0aGJme3h9KSA9IHtcYWxwaGFfMH0re1xhbHBoYV8xfSB4XzEgKyB7XGFscGhhXzJ9IHhfMiArIHtcYWxwaGFfMn0geF8zICsgLi4uICt7XGFscGhhX259IHhfbiQKCmBgYHtyfQoKbGluZWFyX21vZGVsLlNfdl9hbGwgPC0gbG0oZGF0YSAgICA9IGNvbmNyZXRlLCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyB5b3VyIGRhdGEgZnJhbWUKICAgICAgICAgICAgICAgICAgICAgICAgICAgZm9ybXVsYSA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkgfiBDZW1lbnQgKyAgIyB5b3VyIGZvcm11bGEKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBTbGFnICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBGbHlfQXNoICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBXYXRlciArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgU3VwZXJwbGFzdGljaXplciArCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgRmluZV9BZ2dyZWdhdGVzICsKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBDb2Fyc2VfQWdncmVnYXRlcykgIAoKCmBgYAoKQW5kIGhlcmUgYXJlIHRoZXNlIHJlc3VsdHMuLi4KCmBgYHtyfQoKc3VtbWFyeShvYmplY3QgPSBsaW5lYXJfbW9kZWwuU192X2FsbCkKCmBgYAoKT3VyIHJlZ3Jlc3Npb24gY29lZmZpY2llbnRzIGFyZSBzdGlsbCBoZXJlIHVuZGVyIHRoZSAiRXN0aW1hdGUiIGNvbHVtbiBhcyBhcmUgb3VyIFN0YW5kYXJkIEVycm9yIG9mIG91ciBFc3RpbWF0ZSBhbmQgb3VyIENvZWZmIG9mIERldGVybWluYXRpb24uCgpBbHNvIHdlIGNhbiBub3cgdGFrZSBhIGdvb2QgbG9vayBhdCB0aG9zZSBhc3Rlcmlza3MgYXQgdGhlIGVuZCBvZiBsaW5lIHdpdGggdGhlIHBhcmFtZXRlciBjb2VmZmljaWVudHMuIFRoZXNlIGNhbiBleHBsYWluIHdoaWNoIGluZGVwZW5kZW50IHZhcmlhYmxlcyBkbyB0aGUgaGVhdmllc3QgbGlmdGluZyBpbiBvdXIgcmVncmVzc2lvbi4gVGhlIG1vcmUgYXN0ZXJpc2tzLCB0aGUgbW9yZSBpbXBvcnRhbnQgdGhlIGRlcGVuZGVudCB2YXJpYWJsZSBpcyB0byB0aGUgbGFyZ2VyIG11bHRpdmFyaWF0ZSByZWdyZXNzaW9uLiBIZXJlLCB3ZSBjYW4gc2VlIHRoYXQgdGhlIENlbWVudCBhbmQgV2F0ZXIgYXJlIGRvaW5nIG1vc3Qgb2YgdGhlICJ3b3JrIiBpbiBmaXR0aW5nIG91ciBzdWl0ZSBvZiBpbmRlcGVuZGVudCB2YXJpYWJsZXMgdG8gb3VyIGRlcGVuZGVudCB2YXJpYWJsZSBvZiBDb21wcmVzc2l2ZSBTdHJlbmd0aC4KCkZpbmFsbHkgdGhlcmUgaXMgdGhlIFAgcGFyYW1ldGVyIGZvciB3aGljaCB0aGUgc21hbGxlciBpdCBpcywgdGhlIGJldHRlciB3ZSBjYW4gc2F5IHRoYXQgdGhlIHJlbGF0aW9uc2hpcCB0aGF0IHdlJ3ZlIG1hZGUgd2l0aCBvdXIgcmVncmVzc2lvbiByZXByZXNlbnRzIG91ciBkZXBlbmRlbnQgdmFyaWFibGUuCgpOb3cuLi4gb24gdG8gbG9va2luZyBhdCBvdXIgcmVzdWx0cy4KCkhlcmUgaXMgd2hlcmUgdmlld2luZyB0aGUgcmVzdWx0cyBvZiB0aGUgcmVncmVzc2lvbiBpcyB0cmlja3kuCgpXZSBoYXZlIDcgaW5kZXBlbmRlbnQgdmFyaWFibGVzIGJ1dCB3ZSdkIGxpa2UgdG8gc2VlIHRoZSBpbXBhY3Qgb2YgdGhlIGZpdCBpZiBhbGwgNyB2YXJpYWJsZXMgb24gb3VyIHN0cmVuZ3RoCgpXaGVuIEkgZG8gdGhpcyBJIGxpa2UgdG8gcGxvdCB0aGUgdHJ1ZSB5IHZhbHVlIGFnYWluc3QgbXkgcmVncmVzc2lvbiB5KHgxLHgyLHgzLC4uKQoKU28gdG8gZG8gdGhpcyBJIHdpbGwgdGFrZSB0aGUgZml0dGVkIHZhbHVlcyBvZiB5IGFuZCBwbG90IHRoZW0gYWdhaW5zdCB0aGUgb3JpZ2luYWwgdmFsdWVzIG9mIHkKCkdldHRpbmcgdGhlIGZpdHRlZCB2YWx1ZXMgaXMgZWFzeS4KCkknbSB1c2luZyB0aGUgZ2V0X3JlZ3Jlc3Npb25fcG9pbnRzIGZ1bmN0aW9uIHdoaWNoIGFkZHMgdGhlIG1vZGVsZWQgInktaGF0IiB2YWx1ZSB0byB0aGUgZGF0YWZyYW1lIG9mIGFsbCBvZiB0aGUgb3RoZXIgdmFsdWVzIFtzdGF0czo6Z2V0X3JlZ3Jlc3Npb25fcG9pbnRzKCldKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9zdGF0cy92ZXJzaW9ucy8zLjUuMS90b3BpY3MvZml0dGVkKSBmdW5jdGlvbi4KClRoZSBmaXR0ZWQgdmVyc2lvbiBpcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlIHcvIGEgIlxfaGF0IiIgYXQgdGhlIGVuZAoKYGBge3J9CgpmaXR0ZWQuU192X2FsbCA9IGdldF9yZWdyZXNzaW9uX3BvaW50cyhtb2RlbCA9IGxpbmVhcl9tb2RlbC5TX3ZfYWxsKQoKcHJpbnQoZml0dGVkLlNfdl9hbGwpCgpgYGAKCkFuZCBmaW5hbGx5IHdlIGNhbiBwbG90IG91ciBhY3R1YWwgdnMgbW9kZWxlZCB2YWx1ZXMuIChJJ20gYWRkaW5nIGEgdHJlbmQgbGluZSkKCmBgYHtyfQoKCiMgTWFraW5nIGEgc2ltcGxlIFgtWSBzY2F0dGVycGxvdCBhbmQgYWRkaW5nIGEgcmVncmVzc2lvbiB0byBpdAoKZ2dwbG90KGRhdGEgPSBmaXR0ZWQuU192X2FsbCkgKyAgICAgICAgICAgIyBpbnZva2UgZ3JhcGhpY3MgZW52aXJvbm1lbnQgdXNpbmcgYSBnaXZlbiBkYXRhZnJhbWUKICAKICB0aGVtZV9idyggKSArICAgICAgICAgICAgICAgICAgICAgICAgICAgIyBjaGFuZ2luZyB0aGUgcGxvdHRpbmcgdGhlbWUKICAKICBhZXMoeCAgICAgID0gQ29tcHJlc3NpdmVfU3RyZW5ndGhfMjhkeSwgICAgIyB4LXZhbHVlCiAgICAgIHkgICAgICA9IENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHlfaGF0KSArICAjIHktdmFsdWUKCiAgZ2d0aXRsZSgiWWVoIFN1cGVycGxhc3RpY2l6ZXIgVGVzdHMiLAogICAgICAgICAgc3VidGl0bGUgPSAiMjgtZHkgQ29tcHJlc3NpdmUgU3RyZW5ndGggKE1QYSkiKSArICAgICMgRURJVEVEOiBDdXN0b20gVGl0bGUgbm93IHdpdGggYSBzdWJ0aXRsZQogIAogIHlsYWIoIk1vZGVsbGVkIikgICAgICsgIyB5LWxhYmVsCiAgeGxhYigiT2JzZXJ2ZWQiKSAgICAgKyAjIHgtbGFiZWwKCiAgZ2VvbV9wb2ludChjb2xvdXI9ImRhcmtyZWQiKSArICAjIHBsb3QgcG9pbnRzCiAgCiAgZ2VvbV9zbW9vdGgobWV0aG9kICA9ICJsbSIsICAgICAgIyB1c2UgYSBzaW1wbGUgbGluYXIgbW9kZWwKICAgICAgICAgICAgICBmb3JtdWxhID0geSB+IHgsICAgICAjIGxtLXN0eWxlIGZvcm11bGEKICAgICAgICAgICAgICBzZSAgICAgID0gVFJVRSwgICAgICAjIGRpc3BsYXkgQ29uZmlkZW5jZSBJbnRlcnZhbHMKICAgICAgICAgICAgICBsZXZlbCAgID0gMC45NSwgICAgICAjIENvbmZpZGVuZSBMZXZlbCB0byBNYXAgT3V0CiAgICAgICAgICAgICAgY29sb3VyICA9ICJyZWQiLCAgICAgIyByZWdyZXNzaW9uIGxpbmUgY29sb3IKICAgICAgICAgICAgICBmaWxsICAgID0gIm1hZ2VudGEiLCAjIGZpbGwgZm9yIGNvbmZpZGVuY2UgbGltaXRzCiAgICAgICAgICAgICAgc2l6ZSAgICA9IDAuNSkgICsgICAgIyBsaW5lIHRoaWNrbmVzcwogIAogIGdlb21fYWJsaW5lKHNsb3BlICAgICA9IDEsICAgICAgICMgTkVXOiBhZGQgYSB2ZXJ5IHNpbXBsZSBsaW5lCiAgICAgICAgICAgICAgaW50ZXJjZXB0ID0gMCwgICAgICAgIyAgKGZvciBhIDE6MSByZWZlcmVuY2UpCiAgICAgICAgICAgICAgY29sb3IgICAgID0gImdyZXkiLAogICAgICAgICAgICAgIGxpbmV0eXBlICA9ICJkYXNoZWQiKSArCgogIGNvb3JkX2ZpeGVkKHJhdGlvID0gMSkgICAgICAgICAgICMgTkVXOiBtYWtlIHRoZSBhc3BlY3QgcmF0aW8gCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIyAgIChJIGxpa2UgbXkgcGxvdHMgc3F1YXJlKQpgYGAKCkFuZCBoZXJlIHdlIGhhdmUgYSBuaWNlIHBsb3Qgc2hvd2luZyBvdXIgdHJ1ZSB2cyBwcmVkaWN0ZWQgdmFsdWVzLgoKIyA4LiBSZWdyZXNzaW9uIFF1YWxpdHkgTWV0cmljcwoKQW5kIHRvIGNsb3NlIHRoaW5ncyBvZmYsIHdlIGNhbiBkbyBzb21lIGdlbmVyYWwgZXJyb3IgbWV0cmljcyB0aGF0IG1heSBiZSB1c2VmdWwuLgoKRmlyc3QsIHRoZSBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkgb3IgQmlhcy4uLiAoaWYgd2UgYXJlIHRvbyBoaWdoIG9yIHRvbyBsb3cpCgokQklBUyA9IE1TRSA9IFxmcmFjezF9e059IFxzdW1fe2k9MX1ee259IFtcd2lkZWhhdHt5fShcb3ZlcnJpZ2h0YXJyb3d7eF9pfSkteV9pXSA9IFxvdmVybGluZXtbXHdpZGVoYXR7eX0oXG92ZXJyaWdodGFycm93e3hfaX0pLXlfaV19JAoKYGBge3J9CiAgIyBDYWxjdWxhdGUgQmlhcyAoTVNFKQoKICBiaWFzID0gbWVhbihmaXR0ZWQuU192X2FsbCRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5X2hhdCAtIAogICAgICAgICAgICAgICAgIGZpdHRlZC5TX3ZfYWxsJENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHkpCiAgCiAgcHJpbnQoc3RyX2MoIiBNZWFuIFNxdWFyZWQgRXJyb3IgKE1TRSkgb3IgQmlhczogIiwgYmlhcykpCmBgYAoKRm9yIGEgbGluZWFyIG9yIG11bHRpdmFyaWF0ZSByZWdyZXNzaW9uIHRoZSBhdmVyYWdlIG9mIG91ciByZXNpZHVhbHMgKHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gZWFjaCBvYnNlcnZhdGlvbiBhbmQgcHJlZGljdGlvbikgKnNob3VsZCogYmUgemVyby4KClRoZSByb290IG1lYW4gc3F1YXJlZCBlcnJvciAoUk1TRSkgaXMgc2hvd24gaGVyZS4gSXQgc2hvdWxkbid0IGJlIHplcm8gc2luY2UgdGhlIHJlc2lkdWFscyBhcmUgc3F1YXJlZCBiZWZvcmUgc3VtbWluZyB0aGVtIHVwLiBXZSB0ZWNobmljYWxseSBzaG91bGQgdXNlIHRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgZXN0aW1hdGUsIGJ1dCBSTVNFIHJlbWFpbnMgYSBjb21tb24gZXJyb3IgbWV0cmljLiBXZSBjYW4gYWx3YXlzIGRvIGJvdGguIFRoZSBzdGFuZGFyZCBlcnJvciBvZiB0aGUgZXN0aW1hdGUgdGFrZXMgaW50byBhY2NvdW50IHRoZSBkZWdyZWVzIG9mIGZyZWVkb20gd2hpY2ggd2hpY2ggbm93IGluY2x1ZGVzIGFsbCBvZiB0aGUgaW5kZXBlbmRlbnQgdmFyaWFibGVzIChwKS4gV2UgY2FuIGdldCB0aGUgc3RhbmRhcmQgZXJyb3Igb2YgdGhlIGVzdGltYXRlIGZyb20gb3VyCgokUk1TRSA9IFxzcXJ0eyBcZnJhY3sxfXtOfSBcc3VtX3tpPTF9XntufSBbXHdpZGVoYXR7eX0oXG92ZXJyaWdodGFycm93e3hfaX0pLXlfaV1eMiB9ID0gXHNxcnR7XG92ZXJsaW5le1tcd2lkZWhhdHt5fShcb3ZlcnJpZ2h0YXJyb3d7eF9pfSkteV9pXV4yfSB9JAoKJHNfe2V9JCBvciAkc197eS94fSA9IFxzcXJ0eyBcZnJhY3sxfXtOLXAtMX0gXHN1bV97aT0xfV57bn0gW1x3aWRlaGF0e3l9KFxvdmVycmlnaHRhcnJvd3t4X2l9KS15X2ldXjIgfSQKCmBgYHtyfQogICMgQ2FsY3VsYXRlIFJNU0UKCiAgcm1zZSA9IHNxcnQobWVhbiggKGZpdHRlZC5TX3ZfYWxsJENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHlfaGF0IC0KICAgICAgICAgICAgICAgICAgICAgICBmaXR0ZWQuU192X2FsbCRDb21wcmVzc2l2ZV9TdHJlbmd0aF8yOGR5KV4yKSAgKQogIAogIHByaW50KHN0cl9jKCIgICAgIFJvb3QgTWVhbiBTcXVhcmVkIEVycm9yIChSTVNFKTogIiwgIAogICAgICAgICAgICAgIHJtc2UpKQogIHByaW50KHN0cl9jKCJTdGFuZGFyZCBFcnJvciBvZiB0aGUgRXN0aW1hdGUgKHNlKTogIiwgCiAgICAgICAgICAgICAgc3VtbWFyeShsaW5lYXJfbW9kZWwuU192X2FsbCkkc2lnbWEpKSAgIyB5b3UgaGF2ZSB0byBkaWcgZm9yIHRoaXMgb25lIQpgYGAKCkFuZCBmaW5hbGx5IG91ciBjb3JyZWxhdGlvbiBjb2VmZmljaWVudCAod2hpY2ggaXMgYmFzaWNhbGx5IG91ciBjb2VmZmljaWVudCBvZiBkZXRlcm1pbmF0aW9uIGJlZm9yZSB0aGUgIlIiIGlzICJzcXVhcmVkIikKCmBgYHtyfQogICMgR2V0IFRoZSBVbmFkanVzdGVkIENvcnJlbGF0aW9uIENvZWZmaWNpZW50CgogIHIgPSBjb3IoeCA9IGZpdHRlZC5TX3ZfYWxsJENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHksICAgICAjIHRoZSB4LXZhbHVlIAogICAgICAgICAgeSA9IGZpdHRlZC5TX3ZfYWxsJENvbXByZXNzaXZlX1N0cmVuZ3RoXzI4ZHlfaGF0LCAjIHRoZSB5LXZhbHVlCiAgICAgICAgICBtZXRob2QgPSAicGVhcnNvbiIgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgbWV0aG9kIG9mIGNvcnJlbGF0aW9uCiAgICAgICAgICApCiAgCiAgcHJpbnQoc3RyX2MoIiAgICAgICAgICAgICAgICAgICAgICAgIGNvcnJlbGF0aW9uIGNvZWZmaWNpZW50IChyKTogIiwgcikpCiAgcHJpbnQoc3RyX2MoIiAgICAgICAgICAgICAgICAgIGNvZWZmaWNpZW50IG9mIGRldGVybWluYXRpb24gKHLCsik6ICIsIHJeMiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIiAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VtbWFyeShsaW5lYXJfbW9kZWwuU192X2FsbCkkci5zcXVhcmVkKSkKICBwcmludChzdHJfYygiYWRqdXN0ZWQgY29lZmZpY2llbnQgb2YgZGV0ZXJtaW5hdGlvbiAoQWRqdXN0ZWQgcsKyKTogIiwgCiAgICAgICAgICAgICAgIHN1bW1hcnkobGluZWFyX21vZGVsLlNfdl9hbGwpJGFkai5yLnNxdWFyZWQpKQoKCmBgYAoKIyA5LiBDbG9zaW5nCgpBbmQgd2l0aCB0aGF0LCB3ZSdyZSBkb25lLi4uIE9uY2UgYWdhaW4sIHRoaXMgZXhlcmNpc2UgZGVtb25zdHJhdGVzIGEgbG90IG9mIHRyaWNrcyBqdXN0IHRvIHNob3cgaG93IHlvdSBjYW4gdXNlIFIgZm9yIHZhcmlvdXMgc3RhdGlzdGljcy4gWW91IG1heSBub3QgdXNlIGFsbCBvZiB0aGVtIGluIHlvdXIgZW5jb3VudGVycyB3aXRoIFIgZm9yIGxpbmVhciBvciBtdWx0aXZhcmlhdGUgcmVncmVzc2lvbiBvciBldmVuIGF0IGFsbCwgYnV0IHlvdSBtYXkgYmUgYWJsZSB0byBjYW5uaWJhbGl6ZSBzb21lIG9mIHRoZSB0cmlja3MgaGVyZSBmb3Igb3RoZXIgYXBwbGljYXRpb25zLgo=