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.


  # 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

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
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

Extra: Units: AVOID!

… Uh… Just don’t. Unlike Mathcad which is designed to work with units or python which claims to be able to work with units, R is awful with it. I’ve used units once or twice. But I’ve found it to be more work than it’s worth.

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_long” Function
Example of the “pivot_wide” 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=