1 Introduction

The following chapter is a step by step guide for novice R users in the art of making boxplots and bar graphs, primarily using the ggplot2 package. R is capable of a lot more graphically, but this is a very good place to start.

1.0.1 What are boxplots good for?

Boxplots are an easy visual way to depict quartiles in your data. They are sometimes referred to as box-and-whisker-plots as they often have lines extending from the box data, which denote additional variablity outside of these quartiles. Boxplots allow us a simple way to compare groups and view dispersion and spread in data. They also help highlight outliers.

1.0.2 What are bar graphs good for?

Bar graphs (aka: bar charts or bar plots) present grouped data in a rectangular format. They allow us to see and compare groups or categories based on the scores (often means) of another (often continuous) variable.

1.0.3 The example in this chapter

In the current chapter I will be using a simple fake dataset that I made for the purposes of this chapter. When researching how to make these different plots and graphs, most of the information I found online used examples without real data. Personally, I find these types of examples difficult to follow and I wanted to create something different. This way, if you get confused about the data, you can actually look at it. This dataset contains data from 50 fake participants assessing their self-rated funniness. Essentially, these fake individuals were asked to rate on a scale from 1 to 100 - “How funny do you think you are?”- with 100 indicating the highest degree of funniness and 1 indicating the lowest degree of funniness. Additionally, their age, gender (Male or Female), and level of education (College Grad or Not College Grad) were recorded.

2 Getting Started - Review of important Basics

See Chapter 1 for more details on topics discussed in this section.

2.0.1 Set your working directory

The setwd() function assists you in setting your working directory. This information tells R where the files you are using are located. Make sure your working directory path is in quotations and in parenthesis (as shown below).

setwd(“/Users/Documents/Chapt9Tutorial”)

2.0.2 Install and load necessesary packages

Use the install.packages() function to install necessary packages. Make sure that the name of the package once again is in quotations and paranthesis (as shown below). To make boxplots and bar graphs, you will need the plyr and ggplot2 packages.

#install.packages("plyr") # This package is useful as a data manipulator 
#install.packages("ggplot2") #This package is useful for visualizing data

To load these packages from your library of installed packages use the library() function (as shown below). No quotations are needed in the parantheses here.

library(plyr)    
library(ggplot2)  

2.0.3 Read in your data file

To read in your data files to use in R, use the read.table() function. This function may require additional commands to indicate how the data is set up. For example, in this case the data is seperated by commas and the file contains a header row (T = True). The name of the data file used in this example is Humordata_50.csv, and I am calling it HumorData to make working with it a bit simpler.

HumorData <- read.table("Humordata_50.csv", sep=",", header=T)

3 Viewing and Manipulating the data

3.0.1 Looking at your data

Even though this chapter focuses on boxplots and bar graphs, I would never underestimate the utility of looking closely at your data before creating any plots. You can look at the structure of the data (or any object) using the str() function. It will briefly give you the variable names, # of observations and # of variables, type of variable (integer, factor, numeric, etc). For a data frame, it gives the number of cases and variables, the name and type of each variable, and the first several values of each.

str(HumorData)
## 'data.frame':    50 obs. of  5 variables:
##  $ ID       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Gender   : int  1 2 2 1 1 2 1 2 1 1 ...
##  $ College  : int  1 1 1 2 1 1 2 2 1 2 ...
##  $ Age      : int  87 52 72 53 30 34 62 35 76 86 ...
##  $ Funniness: int  79 79 58 22 98 55 26 43 34 32 ...

If you are only interested in viewing particular aspects of the data, you can use several other functions. The dimensions of your data can be found using the dim() function. The number of cases (rows) and variables (columns) specifically can be found with nrow() and ncol().

dim(HumorData) #number of participants number of variables
## [1] 50  5
nrow(HumorData) #number of participants
## [1] 50
ncol(HumorData) #number of variables
## [1] 5

If you want to look at a particular variable in your dataset, like in this case, the age of participants, type in the name of your data then a $ and the name of the variable of interest.

HumorData$Age
##  [1] 87 52 72 53 30 34 62 35 76 86 -1 63 87 68 42 89 65 69 80 46 38 86 66
## [24] 75 26 55 76 86 21 40 35 25 63 58 31 22 68 26 63 70 48 29 86 19 29 21
## [47] 28 25 26 62

3.0.2 Attending to missing data

To get rid of missing data points you can use the ifelse() function. In the example below, I am dealing with the missing data in my age variable. In this case, for those participants who did not provide an age, -1 was recorded to indicate missing. The code below is indicates that I am addressing the Age variable in my HumorData dataset (HumorData$Age), and that the value to be labeled as missing or NA is -1.

HumorData$Age<-ifelse(HumorData$Age==-1,NA,HumorData$Age)

3.0.3 Make variables into factors

It is important to make sure that R knows that any categorical variables you are going to use in your plots are factors and not some other type of data. If you are unsure if a variable is already a factor, double check the structure of your data (see above). The categorical variables in my data are Gender and College, yet they are currently not structured as factors. Instead, they are structured as integers. The as.factor() function converts a variable into a factor. Once again, use the $ symbol to tell R which variable in your datasset you want to change. See below.

HumorData$Gender<-as.factor(HumorData$Gender)
HumorData$College<-as.factor(HumorData$College)

Now that my categorical variables are factors, I need to properly label them. In my gender variable, 1=Male and 2=Female, and in my college variable 1=College Grad and 2=Not College Grad. I am using the factor() function. Within that function, I am telling R which variable to change in my HumorData, what the levels currently are, and how I want to label those levels.

HumorData$Gender<-factor(HumorData$Gender,
                          levels=c(1,2),
                          labels=c("Male", "Female"))

HumorData$College<-factor(HumorData$College,
                         levels=c(1,2),
                         labels=c("College Grad", "Not College Grad"))

To double check that this all worked properly, take another look at the structure of your data, and you should be able to see a change in the variable type and labeling for the Gender and College variables.

str(HumorData)
## 'data.frame':    50 obs. of  5 variables:
##  $ ID       : int  1 2 3 4 5 6 7 8 9 10 ...
##  $ Gender   : Factor w/ 2 levels "Male","Female": 1 2 2 1 1 2 1 2 1 1 ...
##  $ College  : Factor w/ 2 levels "College Grad",..: 1 1 1 2 1 1 2 2 1 2 ...
##  $ Age      : int  87 52 72 53 30 34 62 35 76 86 ...
##  $ Funniness: int  79 79 58 22 98 55 26 43 34 32 ...

3.0.4 Quick descriptive information

When preparing to make boxplots and bar graphs it can be useful to look at frequencies and/or descriptive summaries of the variables you intend to plot. For example, I may want to see the gender or college graduation breakdown for my sample, or a quick distribution of how funny people think they are. Combining the with() function with the summary() function, as shown below, can offer some quick descriptive information about both categorical and continuous variables.

with(HumorData, summary(Gender))
##   Male Female 
##     26     24
with(HumorData, summary(College))
##     College Grad Not College Grad 
##               29               21
with(HumorData, summary(Age))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max.    NA's 
##   19.00   30.00   55.00   53.04   70.00   89.00       1
with(HumorData, summary(Funniness))
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##   22.00   42.25   60.00   60.54   79.00   98.00

You can also futher break apart variables to see how they may vary as a function of another variable. For example, below I investigate how mean self-rated funniness differs based on the levels of Gender (Male vs. Female) and College (College Grad vs. Not College Grad). Here I use the with() function again in combination with the by() function. I have also chosen to name this information (GenderMeans and CollegeMeans) as objects in case I want to use them later.

GenderMeans = with(HumorData, by(Funniness, Gender, mean))
GenderMeans 
## Gender: Male
## [1] 64.53846
## -------------------------------------------------------- 
## Gender: Female
## [1] 56.20833
CollegeMeans = with(HumorData, by(Funniness, College, mean))
CollegeMeans 
## College: College Grad
## [1] 69.24138
## -------------------------------------------------------- 
## College: Not College Grad
## [1] 48.52381

In addition, you can use the tapply function to look at the different groups. See Chapter 3 for more details on apply functions.

tapply(HumorData$Funniness,
        list(HumorData$Gender,HumorData$College), mean)
##        College Grad Not College Grad
## Male       78.50000         42.20000
## Female     57.84615         54.27273

This infomation helps me to see how self-rated funniness does differ based on gender and college. Therefore, I have confirmed my interest in making plots to display this data.

4 Boxplots

4.0.1 Basic Boxplots

It is very simple to make a basic boxplot. Below I have made two basic box-plots looking at how self-rated funniness differs based on gender and college education. Use the ggplot() function and within that you need to describe the aesthetics or aes. I am indicating which variables I want on which axes below - I want gender (or college) as the variables on the x axis and self-described funniness as the variable on the y axis. I am also telling it what plot to make with geom_boxplot().

GenderPlot1 = ggplot(HumorData, aes(x = Gender, y = Funniness)) + geom_boxplot() 
GenderPlot1

CollegePlot1 = ggplot(HumorData, aes(x = College, y = Funniness)) + geom_boxplot()
CollegePlot1

Just a quick reminder now that you have seen this first boxplot that boxplots or box-and-whisker-plots are a visual depiction of quartiles, dispersion, and spread in your data, and they have lines extending from the box to show the range of additional variablity outside of these quartiles. Moreover, they offer a way to compare variable levels or groups. They can also help highlight outliers.

If you would like to flip these box-plots from a vertical orientation to a horizontal orientation, the code is almost exactly the same. The only addition is adding + coord_flip() to the end of the phrase.

GenderPlot1_FLIP = ggplot(HumorData, aes(x = Gender, y = Funniness)) + geom_boxplot() + coord_flip()
GenderPlot1_FLIP 

CollegePlot1_FLIP = ggplot(HumorData, aes(x = College, y = Funniness)) + geom_boxplot() + coord_flip()
CollegePlot1_FLIP

Moreover, you can make boxplots to get a visual of a single variable by making a fake grouping variable. Simply add xlab(“”) and scale_x_discrete(breaks = NULL) to the end of the phrase of code.

FunnyPlot = ggplot(HumorData, aes(x = factor(0), y = Funniness)) + geom_boxplot() + xlab("") +
  scale_x_discrete(breaks = NULL)
FunnyPlot

Once again the previous plot can be flipped by adding coord_flip() to the end of the phrase.

FunnyPlot_FLIP = ggplot(HumorData, aes(x = factor(0), y = Funniness)) + geom_boxplot() + xlab("") +
  scale_x_discrete(breaks = NULL) + coord_flip()
FunnyPlot_FLIP 

4.0.2 More Attractive Boxplots

The following box plots are slightly more advanced (and attractive) as they are further customized to include more detailed elements.

4.0.2.1 Labeled Boxplots

The following boxplot is showing the same information as the basic CollegePlot1 above. However,it is using a different funciton within the ggplot2 package. Here we use the qplot() function and fill it with my grouping variable (College), my continous variable(Funniness), the data (HumorData), and we specify the type of plot with geom =(“boxplot”). Then to add on, I have used the main command to add a title to the chart “Humor Chart” and the xlab and ylab commands to label the x and y axes.

CollegePlot2 = qplot(College, Funniness, data=HumorData, geom=("boxplot"), 
                   main="Humor Chart",xlab="Education Level", ylab="Self-Rated Funniness")
CollegePlot2 

Note: As you may have noticed, I used the qplot() function instead of the ggplot() function here (and in the plot below). They are similar functions that accomplish many of the same things. Overall, qplot() is quicker, easier to navigate, but less flexible than ggplot(), which is more customizable but also more advanced and complex.

4.0.2.2 Jitter Boxplots

We can expand upon the code from above to further customize our boxplots. The plot below is a jitter boxplot, meaning that the actual data points of self-rated funniness are overlayed on the plot. Following the same instructions as above (this time using Gender instead of College) but combining geom=c(“boxplot”, “jitter”) we can create a jitter boxplot.

JitterPlot = qplot(Gender, Funniness, data=HumorData, geom=c("boxplot", "jitter"), 
                   main="Humor Chart",xlab="Gender", ylab="Self-Rated Funniness")
JitterPlot 

4.0.2.3 Faceted Boxplots

Faceted plots are useful if you want to essentially look at two different boxplots at the same time but divided by the levels of one of your categorical variables. There are many times when you may want a boxplot that looks at the potential interaction of two categorical variables. Here I am looking at how self-percived funniness may differ as function of both gender and education. Using the ggplot () function. The following code is very similar to the simple boxplot code from our original GenderPlot1. I have simply added to the end facet_grid(~College) indicating that College is the variable that I want R to use to divide up the boxplots.

FacetPlot1 = ggplot(HumorData, aes(x=Gender, y=Funniness)) + geom_boxplot() + facet_grid(~College) 
FacetPlot1

4.0.2.4 Customizing Outliers

Boxplots can be useful in identifying outliers in your data. Outliers are any data points that fall outside of the whiskers on the plot, and they are depicted with dots. You may have noticed two dots in the boxplot above. They denote two male participants among the college graduates whose self-rated funniness is low for this group. To change the look of those dots, you can add info to the geom_boxplot() command. Here I have specified the size, shape, and color of my outliers, and consequently made the corresponding dots larger than the default, hollow diamond shaped, and purple.

FacetPlot2 = ggplot(HumorData, aes(x=Gender, y=Funniness, label=ID)) + geom_boxplot(outlier.size=3,outlier.shape=5,outlier.colour="purple") + facet_grid(~College) 

FacetPlot2

4.0.2.5 Adding Color to Your Plots

Expanding on the code from FacetPlot1, I want to tell R that I would like it to color in the boxplots instead of the same boring white. by adding fill=Gender to the aes command I am telling R to fill the color in with the default color. The scale_fill_brewer command tells R what color palette I want it to use if I would like it to be different from the default.

FacetPlot3 = ggplot(HumorData, aes(x=Gender, y=Funniness, fill=Gender)) + geom_boxplot() + facet_grid(~College) + scale_fill_brewer(palette = "Set1")
FacetPlot3

Color use in R could be its own chapter. As I will not be able to do this aspect of plots and charts justice, please see other resources on the internet for help, including this color chart - http://research.stowers-institute.org/efg/R/Color/Chart/ or the following R color cheat sheet which describes different color packages you can install - https://www.nceas.ucsb.edu/~frazier/RSpatialGuides/colorPaletteCheatsheet.pdf

4.0.2.6 Boxplots Using Multuple Categorical Variables Without Facets

I can also remove the facet (but maintain the presence of both categorical variables = Gender & College) by just removing the facet_grid command. Also, as you can see I have removed the scale_fill_brewer command and we still have colors (as now R is showing us the default colors).

CombinedPlot=ggplot(HumorData, aes(x=College, y=Funniness, fill=Gender)) + geom_boxplot() 
CombinedPlot

4.0.2.7 Notched Boxplots

Notched box-plots may be useful in some instances. Not only are they fun to look at, but if two boxes’ notches do not overlap this is ‘strong evidence’ their medians differ (Chambers et al., 1983, p. 62).

To make a plot notched, simply add notch=TRUE to the geom_boxplot command.

CombinedPlotNOTCH=ggplot(HumorData, aes(x=Gender, y=Funniness, fill=Gender)) + geom_boxplot(notch=TRUE) + facet_grid(~College)
CombinedPlotNOTCH

The notches, which start where the sides begin to slope inward, are similar in function to a confidence interval. When looking at the college graduates, it appears as if no part of the notches overlap for male and female participants. However, they do appear to overlap in the people who did not graduate from college. This suggests that there is a real difference in self-rated funniness between men and women who went to college, but this pattern was not demonstrated among those who did not go to college. Nevertheless, I would not soley rely on this visual. If this question is important to you, I would also conduct statistical analyses to be sure (for example you could use an ANOVA like in Chapters 20 & 21).

5 Bar Graphs

5.0.1 Preparing the data

Bar graphs are different from boxplots in that the data you use sometimes needs to be in vector or matrix format.

For example you can just make a quick vector using the c() function.

Vector1 <- c(13,29,23,35,16,20)

We can also use the means from the humor example to demonstrate how to do this with real data. One quick note here, you may notice that I added a backslash followed by n to the middle of the names in the HumorGroups vector below. This notation tells R to break up the name at that spot. It can be a trick to make graphs look nicer if you have long category names.

HumorMeans <- c(81,59,40,60)
HumorGroups <- c("College\nGrad Men","College\nGrad Women","Not College\nGrad Men","Not College\nGrad Women")

5.0.2 Making Simple Bar Charts

You can use the barplot() function to make simple bar graph with the original vector we made. I realize that the primary purpose of this chapter is to help people use the ggplot2 package to make boxplots and bar graphs, however, I think it is really useful to also know how to make really simple graphs in this way as well. The need to use different sources of data can change how easy it is to make graphs with different packages and functions.

Here is the super simple bar graph using Vector1.

SimpleBar = barplot(Vector1)

However, there is not much use for such a simple bar graph. With the addition of a few more commands, you can use the same function to make a more useful graph with the humor vectors we made earlier. In the parentheses first place the vector with the values you want in the graph, next establish the names to identify the groups these means belong to with the other vector (names.arg = HumorGroups). Then you can label and change the color of the graph. I have added color to the plots with the col=“green” command. In this context, typing in the name of most colors will work (e.g. “black”, “red”, “yellow”). Here there is the option to change the color of the border of the bars. I have also added in a label for the whole chart using the added command of main = “Humor Chart,” and labels for the x and y axes using the xlab and ylab commands.

SimpleBar2 = barplot(HumorMeans,names.arg = HumorGroups,xlab = "Gender and Education Groupings",ylab = "Self-Reported Funniness",col = "green",main = "Humor Chart",border = "black")

5.0.3 Creating Stacked Bar Graphs

As bar graphs get more complicated, ggplot() is a more useful function. You will not need to create vectors or matrices to deliniate the data points to use. Instead, you can tell the function to use the means you want using the command geom_bar(stat=“summary”, fun.y=“mean”). Once again, I have changed the color palatte here and added a title using ggtitle(“Humor Chart”).

StackedBar = ggplot(HumorData, aes(Gender, Funniness, fill = College)) + 
  geom_bar(stat="summary", fun.y="mean") + 
  scale_fill_brewer(palette = "Set2") + ggtitle("Humor Chart")
StackedBar

5.0.4 Creating Grouped Bar Graphs

Creating grouped bar graphs is pretty simple once you have a sense of how other bar graphs work. R creates stacked graphs as default, so you have to add in position = “dodge” to the geom_bar command.

GroupedBar = ggplot(HumorData, aes(Gender, Funniness, fill = College)) + 
  geom_bar(stat="summary", fun.y="mean", position = "dodge") + 
  scale_fill_brewer(palette = "Set2") + ggtitle("Humor Chart")
GroupedBar

5.0.5 Saving Your Work

After going through the trouble to create boxplots and bar graphs, you may want to save them for outside use. First, you need to decide what type of graphics file you want. For a PDF use the pdf() funcion, for PNG use png(), and for JPG use jpg(). Within the parentheses of this function, you indicate what you would like to name the file. For the next line of code, you need to identify the plot you want to save with the plot() function. Lastly, complete the saving process by adding dev.off(). Then you should see your saved file in your working directory! See below for an example using the last bar graph we made above:

png(filename="GroupedBarGraph.png")
plot(GroupedBar)
dev.off()

6 More Resources

This is only a brief introduction to using the ggplot2 packcage. For additional types of plots and plot customization tips see Chapter 8. Also, check out the following links for additional web resources for making boxplots and bar graphs:

LS0tDQp0aXRsZTogIkNoYXB0ZXIgMTE6IEJveHBsb3RzIGFuZCBCYXIgR3JhcGhzIg0KYXV0aG9yOiAiRW1pbHkgQnJheSINCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0aGVtZTogY2VydWxlYW4NCiAgICBoaWdobGlnaHQ6IHRleHRtYXRlDQogICAgZm9udHNpemU6IDhwdA0KICAgIHRvYzogdHJ1ZQ0KICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQ0KICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICB0b2NfZmxvYXQ6DQogICAgICBjb2xsYXBzZWQ6IGZhbHNlDQotLS0NCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpDQpgYGANCiMgKipJbnRyb2R1Y3Rpb24qKg0KVGhlIGZvbGxvd2luZyBjaGFwdGVyIGlzIGEgc3RlcCBieSBzdGVwIGd1aWRlIGZvciBub3ZpY2UgUiB1c2VycyBpbiB0aGUgYXJ0IG9mIG1ha2luZyBib3hwbG90cyBhbmQgYmFyIGdyYXBocywgcHJpbWFyaWx5IHVzaW5nIHRoZSAqKmdncGxvdDIgcGFja2FnZSoqLiBSIGlzIGNhcGFibGUgb2YgYSBsb3QgbW9yZSBncmFwaGljYWxseSwgYnV0IHRoaXMgaXMgYSB2ZXJ5IGdvb2QgcGxhY2UgdG8gc3RhcnQuIA0KDQojIyMgKipXaGF0IGFyZSBib3hwbG90cyBnb29kIGZvcj8qKg0KDQpCb3hwbG90cyBhcmUgYW4gZWFzeSB2aXN1YWwgd2F5IHRvIGRlcGljdCBxdWFydGlsZXMgaW4geW91ciBkYXRhLiBUaGV5IGFyZSBzb21ldGltZXMgcmVmZXJyZWQgdG8gYXMgYm94LWFuZC13aGlza2VyLXBsb3RzIGFzIHRoZXkgb2Z0ZW4gaGF2ZSBsaW5lcyBleHRlbmRpbmcgZnJvbSB0aGUgYm94IGRhdGEsIHdoaWNoIGRlbm90ZSBhZGRpdGlvbmFsIHZhcmlhYmxpdHkgb3V0c2lkZSBvZiB0aGVzZSBxdWFydGlsZXMuIEJveHBsb3RzIGFsbG93IHVzIGEgc2ltcGxlIHdheSB0byBjb21wYXJlIGdyb3VwcyBhbmQgdmlldyBkaXNwZXJzaW9uIGFuZCBzcHJlYWQgaW4gZGF0YS4gVGhleSBhbHNvIGhlbHAgaGlnaGxpZ2h0IG91dGxpZXJzLiANCg0KIyMjICoqV2hhdCBhcmUgYmFyIGdyYXBocyBnb29kIGZvcj8qKg0KDQpCYXIgZ3JhcGhzIChha2E6IGJhciBjaGFydHMgb3IgYmFyIHBsb3RzKSBwcmVzZW50IGdyb3VwZWQgZGF0YSBpbiBhIHJlY3Rhbmd1bGFyIGZvcm1hdC4gVGhleSBhbGxvdyB1cyB0byBzZWUgYW5kIGNvbXBhcmUgZ3JvdXBzIG9yIGNhdGVnb3JpZXMgYmFzZWQgb24gdGhlIHNjb3JlcyAob2Z0ZW4gbWVhbnMpIG9mIGFub3RoZXIgKG9mdGVuIGNvbnRpbnVvdXMpIHZhcmlhYmxlLg0KDQojIyMgKipUaGUgZXhhbXBsZSBpbiB0aGlzIGNoYXB0ZXIqKg0KDQpJbiB0aGUgY3VycmVudCBjaGFwdGVyIEkgd2lsbCBiZSB1c2luZyBhIHNpbXBsZSAqZmFrZSBkYXRhc2V0KiB0aGF0IEkgbWFkZSBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgY2hhcHRlci4gV2hlbiByZXNlYXJjaGluZyBob3cgdG8gbWFrZSB0aGVzZSBkaWZmZXJlbnQgcGxvdHMgYW5kIGdyYXBocywgbW9zdCBvZiB0aGUgaW5mb3JtYXRpb24gSSBmb3VuZCBvbmxpbmUgdXNlZCBleGFtcGxlcyB3aXRob3V0IHJlYWwgZGF0YS4gUGVyc29uYWxseSwgSSBmaW5kIHRoZXNlIHR5cGVzIG9mIGV4YW1wbGVzIGRpZmZpY3VsdCB0byBmb2xsb3cgYW5kIEkgd2FudGVkIHRvIGNyZWF0ZSBzb21ldGhpbmcgZGlmZmVyZW50LiBUaGlzIHdheSwgaWYgeW91IGdldCBjb25mdXNlZCBhYm91dCB0aGUgZGF0YSwgeW91IGNhbiBhY3R1YWxseSBsb29rIGF0IGl0LiBUaGlzIGRhdGFzZXQgY29udGFpbnMgZGF0YSBmcm9tIDUwIGZha2UgcGFydGljaXBhbnRzIGFzc2Vzc2luZyB0aGVpciBzZWxmLXJhdGVkIGZ1bm5pbmVzcy4gRXNzZW50aWFsbHksIHRoZXNlIGZha2UgaW5kaXZpZHVhbHMgd2VyZSBhc2tlZCB0byByYXRlIG9uIGEgc2NhbGUgZnJvbSAxIHRvIDEwMCAtICJIb3cgZnVubnkgZG8geW91IHRoaW5rIHlvdSBhcmU/Ii0gd2l0aCAxMDAgaW5kaWNhdGluZyB0aGUgaGlnaGVzdCBkZWdyZWUgb2YgZnVubmluZXNzIGFuZCAxIGluZGljYXRpbmcgdGhlIGxvd2VzdCBkZWdyZWUgb2YgZnVubmluZXNzLiBBZGRpdGlvbmFsbHksIHRoZWlyIGFnZSwgZ2VuZGVyIChNYWxlIG9yIEZlbWFsZSksIGFuZCBsZXZlbCBvZiBlZHVjYXRpb24gKENvbGxlZ2UgR3JhZCBvciBOb3QgQ29sbGVnZSBHcmFkKSB3ZXJlIHJlY29yZGVkLiANCg0KIyAqKkdldHRpbmcgU3RhcnRlZCAtIFJldmlldyBvZiBpbXBvcnRhbnQgQmFzaWNzKioNClNlZSBDaGFwdGVyIDEgZm9yIG1vcmUgZGV0YWlscyBvbiB0b3BpY3MgZGlzY3Vzc2VkIGluIHRoaXMgc2VjdGlvbi4NCg0KIyMjICoqU2V0IHlvdXIgd29ya2luZyBkaXJlY3RvcnkqKg0KDQpUaGUgc2V0d2QoKSBmdW5jdGlvbiBhc3Npc3RzIHlvdSBpbiBzZXR0aW5nIHlvdXIgd29ya2luZyBkaXJlY3RvcnkuIFRoaXMgaW5mb3JtYXRpb24gdGVsbHMgUiB3aGVyZSB0aGUgZmlsZXMgeW91IGFyZSB1c2luZyBhcmUgbG9jYXRlZC4gTWFrZSBzdXJlIHlvdXIgd29ya2luZyBkaXJlY3RvcnkgcGF0aCBpcyBpbiBxdW90YXRpb25zIGFuZCBpbiBwYXJlbnRoZXNpcyAoYXMgc2hvd24gYmVsb3cpLg0KDQoqc2V0d2QoIi9Vc2Vycy9Eb2N1bWVudHMvQ2hhcHQ5VHV0b3JpYWwiKSoNCg0KIyMjICoqSW5zdGFsbCBhbmQgbG9hZCBuZWNlc3Nlc2FyeSBwYWNrYWdlcyoqDQoNClVzZSB0aGUgaW5zdGFsbC5wYWNrYWdlcygpIGZ1bmN0aW9uIHRvIGluc3RhbGwgbmVjZXNzYXJ5IHBhY2thZ2VzLiBNYWtlIHN1cmUgdGhhdCB0aGUgbmFtZSBvZiB0aGUgcGFja2FnZSBvbmNlIGFnYWluIGlzIGluIHF1b3RhdGlvbnMgYW5kIHBhcmFudGhlc2lzIChhcyBzaG93biBiZWxvdykuIFRvIG1ha2UgYm94cGxvdHMgYW5kIGJhciBncmFwaHMsIHlvdSB3aWxsIG5lZWQgdGhlIHBseXIgYW5kIGdncGxvdDIgcGFja2FnZXMuDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KI2luc3RhbGwucGFja2FnZXMoInBseXIiKSAjIFRoaXMgcGFja2FnZSBpcyB1c2VmdWwgYXMgYSBkYXRhIG1hbmlwdWxhdG9yIA0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKSAjVGhpcyBwYWNrYWdlIGlzIHVzZWZ1bCBmb3IgdmlzdWFsaXppbmcgZGF0YQ0KYGBgDQoNClRvIGxvYWQgdGhlc2UgcGFja2FnZXMgZnJvbSB5b3VyIGxpYnJhcnkgb2YgaW5zdGFsbGVkIHBhY2thZ2VzIHVzZSB0aGUgbGlicmFyeSgpIGZ1bmN0aW9uIChhcyBzaG93biBiZWxvdykuIE5vIHF1b3RhdGlvbnMgYXJlIG5lZWRlZCBpbiB0aGUgcGFyYW50aGVzZXMgaGVyZS4gDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KbGlicmFyeShwbHlyKSAgICANCmxpYnJhcnkoZ2dwbG90MikgIA0KYGBgDQoNCiMjIyAqKlJlYWQgaW4geW91ciBkYXRhIGZpbGUqKiANCg0KVG8gcmVhZCBpbiB5b3VyIGRhdGEgZmlsZXMgdG8gdXNlIGluIFIsIHVzZSB0aGUgcmVhZC50YWJsZSgpIGZ1bmN0aW9uLiBUaGlzIGZ1bmN0aW9uIG1heSByZXF1aXJlIGFkZGl0aW9uYWwgY29tbWFuZHMgdG8gaW5kaWNhdGUgaG93IHRoZSBkYXRhIGlzIHNldCB1cC4gRm9yIGV4YW1wbGUsIGluIHRoaXMgY2FzZSB0aGUgZGF0YSBpcyBzZXBlcmF0ZWQgYnkgY29tbWFzIGFuZCB0aGUgZmlsZSBjb250YWlucyBhIGhlYWRlciByb3cgKFQgPSBUcnVlKS4gVGhlIG5hbWUgb2YgdGhlIGRhdGEgZmlsZSB1c2VkIGluIHRoaXMgZXhhbXBsZSBpcyBbKkh1bW9yZGF0YV81MC5jc3YqXSgvSHVtb3JkYXRhXzUwLmNzdiksIGFuZCBJIGFtIGNhbGxpbmcgaXQgSHVtb3JEYXRhIHRvIG1ha2Ugd29ya2luZyB3aXRoIGl0IGEgYml0IHNpbXBsZXIuDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KSHVtb3JEYXRhIDwtIHJlYWQudGFibGUoIkh1bW9yZGF0YV81MC5jc3YiLCBzZXA9IiwiLCBoZWFkZXI9VCkNCmBgYA0KDQojICoqVmlld2luZyBhbmQgTWFuaXB1bGF0aW5nIHRoZSBkYXRhKioNCg0KIyMjICoqTG9va2luZyBhdCB5b3VyIGRhdGEqKg0KDQpFdmVuIHRob3VnaCB0aGlzIGNoYXB0ZXIgZm9jdXNlcyBvbiBib3hwbG90cyBhbmQgYmFyIGdyYXBocywgSSB3b3VsZCBuZXZlciB1bmRlcmVzdGltYXRlIHRoZSB1dGlsaXR5IG9mIGxvb2tpbmcgY2xvc2VseSBhdCB5b3VyIGRhdGEgYmVmb3JlIGNyZWF0aW5nIGFueSBwbG90cy4gWW91IGNhbiBsb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGRhdGEgKG9yIGFueSBvYmplY3QpIHVzaW5nIHRoZSBzdHIoKSBmdW5jdGlvbi4gSXQgd2lsbCBicmllZmx5IGdpdmUgeW91IHRoZSB2YXJpYWJsZSBuYW1lcywgIyBvZiBvYnNlcnZhdGlvbnMgYW5kICMgb2YgdmFyaWFibGVzLCB0eXBlIG9mIHZhcmlhYmxlIChpbnRlZ2VyLCBmYWN0b3IsIG51bWVyaWMsIGV0YykuIEZvciBhIGRhdGEgZnJhbWUsIGl0IGdpdmVzIHRoZSBudW1iZXIgb2YgY2FzZXMgYW5kIHZhcmlhYmxlcywgdGhlIG5hbWUgYW5kIHR5cGUgb2YgZWFjaCB2YXJpYWJsZSwgYW5kIHRoZSBmaXJzdCBzZXZlcmFsIHZhbHVlcyBvZiBlYWNoLiANCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpzdHIoSHVtb3JEYXRhKQ0KYGBgDQoNCklmIHlvdSBhcmUgb25seSBpbnRlcmVzdGVkIGluIHZpZXdpbmcgcGFydGljdWxhciBhc3BlY3RzIG9mIHRoZSBkYXRhLCB5b3UgY2FuIHVzZSBzZXZlcmFsIG90aGVyIGZ1bmN0aW9ucy4gVGhlIGRpbWVuc2lvbnMgb2YgeW91ciBkYXRhIGNhbiBiZSBmb3VuZCB1c2luZyB0aGUgZGltKCkgZnVuY3Rpb24uIFRoZSBudW1iZXIgb2YgY2FzZXMgKHJvd3MpIGFuZCB2YXJpYWJsZXMgKGNvbHVtbnMpIHNwZWNpZmljYWxseSBjYW4gYmUgZm91bmQgd2l0aCBucm93KCkgYW5kIG5jb2woKS4NCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpkaW0oSHVtb3JEYXRhKSAjbnVtYmVyIG9mIHBhcnRpY2lwYW50cyBudW1iZXIgb2YgdmFyaWFibGVzDQpucm93KEh1bW9yRGF0YSkgI251bWJlciBvZiBwYXJ0aWNpcGFudHMNCm5jb2woSHVtb3JEYXRhKSAjbnVtYmVyIG9mIHZhcmlhYmxlcw0KYGBgDQoNCklmIHlvdSB3YW50IHRvIGxvb2sgYXQgYSBwYXJ0aWN1bGFyIHZhcmlhYmxlIGluIHlvdXIgZGF0YXNldCwgbGlrZSBpbiB0aGlzIGNhc2UsIHRoZSBhZ2Ugb2YgcGFydGljaXBhbnRzLCB0eXBlIGluIHRoZSBuYW1lIG9mIHlvdXIgZGF0YSB0aGVuIGEgJCBhbmQgdGhlIG5hbWUgb2YgdGhlIHZhcmlhYmxlIG9mIGludGVyZXN0LiANCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KSHVtb3JEYXRhJEFnZQ0KYGBgDQoNCiMjIyAqKkF0dGVuZGluZyB0byBtaXNzaW5nIGRhdGEqKg0KDQpUbyBnZXQgcmlkIG9mIG1pc3NpbmcgZGF0YSBwb2ludHMgeW91IGNhbiB1c2UgdGhlIGlmZWxzZSgpIGZ1bmN0aW9uLiBJbiB0aGUgZXhhbXBsZSBiZWxvdywgSSBhbSBkZWFsaW5nIHdpdGggdGhlIG1pc3NpbmcgZGF0YSBpbiBteSBhZ2UgdmFyaWFibGUuIEluIHRoaXMgY2FzZSwgZm9yIHRob3NlIHBhcnRpY2lwYW50cyB3aG8gZGlkIG5vdCBwcm92aWRlIGFuIGFnZSwgLTEgd2FzIHJlY29yZGVkIHRvIGluZGljYXRlIG1pc3NpbmcuIFRoZSBjb2RlIGJlbG93IGlzIGluZGljYXRlcyB0aGF0IEkgYW0gYWRkcmVzc2luZyB0aGUgQWdlIHZhcmlhYmxlIGluIG15IEh1bW9yRGF0YSBkYXRhc2V0IChIdW1vckRhdGEkQWdlKSwgYW5kIHRoYXQgdGhlIHZhbHVlIHRvIGJlIGxhYmVsZWQgYXMgbWlzc2luZyBvciBOQSBpcyAtMS4NCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpIdW1vckRhdGEkQWdlPC1pZmVsc2UoSHVtb3JEYXRhJEFnZT09LTEsTkEsSHVtb3JEYXRhJEFnZSkNCmBgYA0KDQojIyMgKipNYWtlIHZhcmlhYmxlcyBpbnRvIGZhY3RvcnMqKg0KDQpJdCBpcyBpbXBvcnRhbnQgdG8gbWFrZSBzdXJlIHRoYXQgUiBrbm93cyB0aGF0IGFueSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMgeW91IGFyZSBnb2luZyB0byB1c2UgaW4geW91ciBwbG90cyBhcmUgZmFjdG9ycyBhbmQgbm90IHNvbWUgb3RoZXIgdHlwZSBvZiBkYXRhLiBJZiB5b3UgYXJlIHVuc3VyZSBpZiBhIHZhcmlhYmxlIGlzIGFscmVhZHkgYSBmYWN0b3IsIGRvdWJsZSBjaGVjayB0aGUgc3RydWN0dXJlIG9mIHlvdXIgZGF0YSAoc2VlIGFib3ZlKS4gVGhlIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBpbiBteSBkYXRhIGFyZSBHZW5kZXIgYW5kIENvbGxlZ2UsIHlldCB0aGV5IGFyZSBjdXJyZW50bHkgbm90IHN0cnVjdHVyZWQgYXMgZmFjdG9ycy4gSW5zdGVhZCwgdGhleSBhcmUgc3RydWN0dXJlZCBhcyBpbnRlZ2Vycy4gVGhlIGFzLmZhY3RvcigpIGZ1bmN0aW9uIGNvbnZlcnRzIGEgdmFyaWFibGUgaW50byBhIGZhY3Rvci4gT25jZSBhZ2FpbiwgdXNlIHRoZSAkIHN5bWJvbCB0byB0ZWxsIFIgd2hpY2ggdmFyaWFibGUgaW4geW91ciBkYXRhc3NldCB5b3Ugd2FudCB0byBjaGFuZ2UuIFNlZSBiZWxvdy4gDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KSHVtb3JEYXRhJEdlbmRlcjwtYXMuZmFjdG9yKEh1bW9yRGF0YSRHZW5kZXIpDQpIdW1vckRhdGEkQ29sbGVnZTwtYXMuZmFjdG9yKEh1bW9yRGF0YSRDb2xsZWdlKQ0KYGBgDQoNCk5vdyB0aGF0IG15IGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhcmUgZmFjdG9ycywgSSBuZWVkIHRvIHByb3Blcmx5IGxhYmVsIHRoZW0uIEluIG15IGdlbmRlciB2YXJpYWJsZSwgMT1NYWxlIGFuZCAyPUZlbWFsZSwgYW5kIGluIG15IGNvbGxlZ2UgdmFyaWFibGUgMT1Db2xsZWdlIEdyYWQgYW5kIDI9Tm90IENvbGxlZ2UgR3JhZC4gSSBhbSB1c2luZyB0aGUgZmFjdG9yKCkgZnVuY3Rpb24uIFdpdGhpbiB0aGF0IGZ1bmN0aW9uLCBJIGFtIHRlbGxpbmcgUiB3aGljaCB2YXJpYWJsZSB0byBjaGFuZ2UgaW4gbXkgSHVtb3JEYXRhLCB3aGF0IHRoZSBsZXZlbHMgY3VycmVudGx5IGFyZSwgYW5kIGhvdyBJIHdhbnQgdG8gbGFiZWwgdGhvc2UgbGV2ZWxzLiANCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpIdW1vckRhdGEkR2VuZGVyPC1mYWN0b3IoSHVtb3JEYXRhJEdlbmRlciwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoMSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzPWMoIk1hbGUiLCAiRmVtYWxlIikpDQoNCkh1bW9yRGF0YSRDb2xsZWdlPC1mYWN0b3IoSHVtb3JEYXRhJENvbGxlZ2UsDQogICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoMSwyKSwNCiAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHM9YygiQ29sbGVnZSBHcmFkIiwgIk5vdCBDb2xsZWdlIEdyYWQiKSkNCmBgYA0KDQpUbyBkb3VibGUgY2hlY2sgdGhhdCB0aGlzIGFsbCB3b3JrZWQgcHJvcGVybHksIHRha2UgYW5vdGhlciBsb29rIGF0IHRoZSBzdHJ1Y3R1cmUgb2YgeW91ciBkYXRhLCBhbmQgeW91IHNob3VsZCBiZSBhYmxlIHRvIHNlZSBhIGNoYW5nZSBpbiB0aGUgdmFyaWFibGUgdHlwZSBhbmQgbGFiZWxpbmcgZm9yIHRoZSBHZW5kZXIgYW5kIENvbGxlZ2UgdmFyaWFibGVzLg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpzdHIoSHVtb3JEYXRhKQ0KYGBgDQoNCiMjIyAqKlF1aWNrIGRlc2NyaXB0aXZlIGluZm9ybWF0aW9uKiogDQoNCldoZW4gcHJlcGFyaW5nIHRvIG1ha2UgYm94cGxvdHMgYW5kIGJhciBncmFwaHMgaXQgY2FuIGJlIHVzZWZ1bCB0byBsb29rIGF0IGZyZXF1ZW5jaWVzIGFuZC9vciBkZXNjcmlwdGl2ZSBzdW1tYXJpZXMgb2YgdGhlIHZhcmlhYmxlcyB5b3UgaW50ZW5kIHRvIHBsb3QuIEZvciBleGFtcGxlLCBJIG1heSB3YW50IHRvIHNlZSB0aGUgZ2VuZGVyIG9yIGNvbGxlZ2UgZ3JhZHVhdGlvbiBicmVha2Rvd24gZm9yIG15IHNhbXBsZSwgb3IgYSBxdWljayBkaXN0cmlidXRpb24gb2YgaG93IGZ1bm55IHBlb3BsZSB0aGluayB0aGV5IGFyZS4gQ29tYmluaW5nIHRoZSB3aXRoKCkgZnVuY3Rpb24gd2l0aCB0aGUgc3VtbWFyeSgpIGZ1bmN0aW9uLCBhcyBzaG93biBiZWxvdywgY2FuIG9mZmVyIHNvbWUgcXVpY2sgZGVzY3JpcHRpdmUgaW5mb3JtYXRpb24gYWJvdXQgYm90aCBjYXRlZ29yaWNhbCBhbmQgY29udGludW91cyB2YXJpYWJsZXMuIA0KDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NCndpdGgoSHVtb3JEYXRhLCBzdW1tYXJ5KEdlbmRlcikpDQp3aXRoKEh1bW9yRGF0YSwgc3VtbWFyeShDb2xsZWdlKSkNCndpdGgoSHVtb3JEYXRhLCBzdW1tYXJ5KEFnZSkpDQp3aXRoKEh1bW9yRGF0YSwgc3VtbWFyeShGdW5uaW5lc3MpKQ0KYGBgDQoNCllvdSBjYW4gYWxzbyBmdXRoZXIgYnJlYWsgYXBhcnQgdmFyaWFibGVzIHRvIHNlZSBob3cgdGhleSBtYXkgdmFyeSBhcyBhIGZ1bmN0aW9uIG9mIGFub3RoZXIgdmFyaWFibGUuIEZvciBleGFtcGxlLCBiZWxvdyBJIGludmVzdGlnYXRlIGhvdyBtZWFuIHNlbGYtcmF0ZWQgZnVubmluZXNzIGRpZmZlcnMgYmFzZWQgb24gdGhlIGxldmVscyBvZiBHZW5kZXIgKE1hbGUgdnMuIEZlbWFsZSkgYW5kIENvbGxlZ2UgKENvbGxlZ2UgR3JhZCB2cy4gTm90IENvbGxlZ2UgR3JhZCkuIEhlcmUgSSB1c2UgdGhlIHdpdGgoKSBmdW5jdGlvbiBhZ2FpbiBpbiBjb21iaW5hdGlvbiB3aXRoIHRoZSBieSgpIGZ1bmN0aW9uLiBJIGhhdmUgYWxzbyBjaG9zZW4gdG8gbmFtZSB0aGlzIGluZm9ybWF0aW9uIChHZW5kZXJNZWFucyBhbmQgQ29sbGVnZU1lYW5zKSBhcyBvYmplY3RzIGluIGNhc2UgSSB3YW50IHRvIHVzZSB0aGVtIGxhdGVyLiANCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpHZW5kZXJNZWFucyA9IHdpdGgoSHVtb3JEYXRhLCBieShGdW5uaW5lc3MsIEdlbmRlciwgbWVhbikpDQpHZW5kZXJNZWFucyANCg0KQ29sbGVnZU1lYW5zID0gd2l0aChIdW1vckRhdGEsIGJ5KEZ1bm5pbmVzcywgQ29sbGVnZSwgbWVhbikpDQpDb2xsZWdlTWVhbnMgDQpgYGANCg0KSW4gYWRkaXRpb24sIHlvdSBjYW4gdXNlIHRoZSB0YXBwbHkgZnVuY3Rpb24gdG8gbG9vayBhdCB0aGUgZGlmZmVyZW50IGdyb3Vwcy4gU2VlIENoYXB0ZXIgMyBmb3IgbW9yZSBkZXRhaWxzIG9uIGFwcGx5IGZ1bmN0aW9ucy4gDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KdGFwcGx5KEh1bW9yRGF0YSRGdW5uaW5lc3MsDQogICAgICAgIGxpc3QoSHVtb3JEYXRhJEdlbmRlcixIdW1vckRhdGEkQ29sbGVnZSksIG1lYW4pDQpgYGANCg0KVGhpcyBpbmZvbWF0aW9uIGhlbHBzIG1lIHRvIHNlZSBob3cgc2VsZi1yYXRlZCBmdW5uaW5lc3MgZG9lcyBkaWZmZXIgYmFzZWQgb24gZ2VuZGVyIGFuZCBjb2xsZWdlLiBUaGVyZWZvcmUsIEkgaGF2ZSBjb25maXJtZWQgbXkgaW50ZXJlc3QgaW4gbWFraW5nIHBsb3RzIHRvIGRpc3BsYXkgdGhpcyBkYXRhLg0KDQojICoqQm94cGxvdHMqKg0KDQojIyMgKipCYXNpYyBCb3hwbG90cyoqDQoNCkl0IGlzIHZlcnkgc2ltcGxlIHRvIG1ha2UgYSBiYXNpYyBib3hwbG90LiBCZWxvdyBJIGhhdmUgbWFkZSB0d28gYmFzaWMgYm94LXBsb3RzIGxvb2tpbmcgYXQgaG93IHNlbGYtcmF0ZWQgZnVubmluZXNzIGRpZmZlcnMgYmFzZWQgb24gZ2VuZGVyIGFuZCBjb2xsZWdlIGVkdWNhdGlvbi4gVXNlIHRoZSBnZ3Bsb3QoKSBmdW5jdGlvbiBhbmQgd2l0aGluIHRoYXQgeW91IG5lZWQgdG8gZGVzY3JpYmUgdGhlIGFlc3RoZXRpY3Mgb3IgYWVzLiBJIGFtIGluZGljYXRpbmcgd2hpY2ggdmFyaWFibGVzIEkgd2FudCBvbiB3aGljaCBheGVzIGJlbG93IC0gSSB3YW50IGdlbmRlciAob3IgY29sbGVnZSkgYXMgdGhlIHZhcmlhYmxlcyBvbiB0aGUgeCBheGlzIGFuZCBzZWxmLWRlc2NyaWJlZCBmdW5uaW5lc3MgYXMgdGhlIHZhcmlhYmxlIG9uIHRoZSB5IGF4aXMuIEkgYW0gYWxzbyB0ZWxsaW5nIGl0IHdoYXQgcGxvdCB0byBtYWtlIHdpdGggZ2VvbV9ib3hwbG90KCkuIA0KDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NCkdlbmRlclBsb3QxID0gZ2dwbG90KEh1bW9yRGF0YSwgYWVzKHggPSBHZW5kZXIsIHkgPSBGdW5uaW5lc3MpKSArIGdlb21fYm94cGxvdCgpIA0KR2VuZGVyUGxvdDENCg0KQ29sbGVnZVBsb3QxID0gZ2dwbG90KEh1bW9yRGF0YSwgYWVzKHggPSBDb2xsZWdlLCB5ID0gRnVubmluZXNzKSkgKyBnZW9tX2JveHBsb3QoKQ0KQ29sbGVnZVBsb3QxDQpgYGANCg0KSnVzdCBhIHF1aWNrIHJlbWluZGVyIG5vdyB0aGF0IHlvdSBoYXZlIHNlZW4gdGhpcyBmaXJzdCBib3hwbG90IHRoYXQgYm94cGxvdHMgb3IgYm94LWFuZC13aGlza2VyLXBsb3RzIGFyZSBhIHZpc3VhbCBkZXBpY3Rpb24gb2YgcXVhcnRpbGVzLCBkaXNwZXJzaW9uLCBhbmQgc3ByZWFkIGluIHlvdXIgZGF0YSwgYW5kIHRoZXkgaGF2ZSBsaW5lcyBleHRlbmRpbmcgZnJvbSB0aGUgYm94IHRvIHNob3cgdGhlIHJhbmdlIG9mIGFkZGl0aW9uYWwgdmFyaWFibGl0eSBvdXRzaWRlIG9mIHRoZXNlIHF1YXJ0aWxlcy4gTW9yZW92ZXIsIHRoZXkgb2ZmZXIgYSB3YXkgdG8gY29tcGFyZSB2YXJpYWJsZSBsZXZlbHMgb3IgZ3JvdXBzLiBUaGV5IGNhbiBhbHNvIGhlbHAgaGlnaGxpZ2h0IG91dGxpZXJzLiANCg0KSWYgeW91IHdvdWxkIGxpa2UgdG8gZmxpcCB0aGVzZSBib3gtcGxvdHMgZnJvbSBhIHZlcnRpY2FsIG9yaWVudGF0aW9uIHRvIGEgaG9yaXpvbnRhbCBvcmllbnRhdGlvbiwgdGhlIGNvZGUgaXMgYWxtb3N0IGV4YWN0bHkgdGhlIHNhbWUuIFRoZSBvbmx5IGFkZGl0aW9uIGlzIGFkZGluZyArIGNvb3JkX2ZsaXAoKSB0byB0aGUgZW5kIG9mIHRoZSBwaHJhc2UuIA0KDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NCkdlbmRlclBsb3QxX0ZMSVAgPSBnZ3Bsb3QoSHVtb3JEYXRhLCBhZXMoeCA9IEdlbmRlciwgeSA9IEZ1bm5pbmVzcykpICsgZ2VvbV9ib3hwbG90KCkgKyBjb29yZF9mbGlwKCkNCkdlbmRlclBsb3QxX0ZMSVAgDQoNCkNvbGxlZ2VQbG90MV9GTElQID0gZ2dwbG90KEh1bW9yRGF0YSwgYWVzKHggPSBDb2xsZWdlLCB5ID0gRnVubmluZXNzKSkgKyBnZW9tX2JveHBsb3QoKSArIGNvb3JkX2ZsaXAoKQ0KQ29sbGVnZVBsb3QxX0ZMSVANCmBgYA0KDQpNb3Jlb3ZlciwgeW91IGNhbiBtYWtlIGJveHBsb3RzIHRvIGdldCBhIHZpc3VhbCBvZiBhIHNpbmdsZSB2YXJpYWJsZSBieSBtYWtpbmcgYSBmYWtlIGdyb3VwaW5nIHZhcmlhYmxlLiBTaW1wbHkgYWRkIHhsYWIoIiIpIGFuZCBzY2FsZV94X2Rpc2NyZXRlKGJyZWFrcyA9IE5VTEwpIHRvIHRoZSBlbmQgb2YgdGhlIHBocmFzZSBvZiBjb2RlLg0KDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NCkZ1bm55UGxvdCA9IGdncGxvdChIdW1vckRhdGEsIGFlcyh4ID0gZmFjdG9yKDApLCB5ID0gRnVubmluZXNzKSkgKyBnZW9tX2JveHBsb3QoKSArIHhsYWIoIiIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBOVUxMKQ0KRnVubnlQbG90DQpgYGANCg0KT25jZSBhZ2FpbiB0aGUgcHJldmlvdXMgcGxvdCBjYW4gYmUgZmxpcHBlZCBieSBhZGRpbmcgY29vcmRfZmxpcCgpIHRvIHRoZSBlbmQgb2YgdGhlIHBocmFzZS4NCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpGdW5ueVBsb3RfRkxJUCA9IGdncGxvdChIdW1vckRhdGEsIGFlcyh4ID0gZmFjdG9yKDApLCB5ID0gRnVubmluZXNzKSkgKyBnZW9tX2JveHBsb3QoKSArIHhsYWIoIiIpICsNCiAgc2NhbGVfeF9kaXNjcmV0ZShicmVha3MgPSBOVUxMKSArIGNvb3JkX2ZsaXAoKQ0KRnVubnlQbG90X0ZMSVAgDQpgYGANCg0KIyMjKipNb3JlIEF0dHJhY3RpdmUgQm94cGxvdHMqKg0KDQpUaGUgZm9sbG93aW5nIGJveCBwbG90cyBhcmUgc2xpZ2h0bHkgbW9yZSBhZHZhbmNlZCAoYW5kIGF0dHJhY3RpdmUpIGFzIHRoZXkgYXJlIGZ1cnRoZXIgY3VzdG9taXplZCB0byBpbmNsdWRlIG1vcmUgZGV0YWlsZWQgZWxlbWVudHMuDQoNCiMjIyMgKipMYWJlbGVkIEJveHBsb3RzKioNCg0KVGhlIGZvbGxvd2luZyBib3hwbG90IGlzIHNob3dpbmcgdGhlIHNhbWUgaW5mb3JtYXRpb24gYXMgdGhlIGJhc2ljIENvbGxlZ2VQbG90MSBhYm92ZS4gSG93ZXZlcixpdCBpcyB1c2luZyBhIGRpZmZlcmVudCBmdW5jaXRvbiB3aXRoaW4gdGhlIGdncGxvdDIgcGFja2FnZS4gSGVyZSB3ZSB1c2UgdGhlIHFwbG90KCkgZnVuY3Rpb24gYW5kIGZpbGwgaXQgd2l0aCBteSBncm91cGluZyB2YXJpYWJsZSAoQ29sbGVnZSksIG15IGNvbnRpbm91cyB2YXJpYWJsZShGdW5uaW5lc3MpLCB0aGUgZGF0YSAoSHVtb3JEYXRhKSwgYW5kIHdlIHNwZWNpZnkgdGhlIHR5cGUgb2YgcGxvdCB3aXRoIGdlb20gPSgiYm94cGxvdCIpLiBUaGVuIHRvIGFkZCBvbiwgSSBoYXZlIHVzZWQgdGhlIG1haW4gY29tbWFuZCB0byBhZGQgYSB0aXRsZSB0byB0aGUgY2hhcnQgIkh1bW9yIENoYXJ0IiBhbmQgdGhlIHhsYWIgYW5kIHlsYWIgY29tbWFuZHMgdG8gbGFiZWwgdGhlIHggYW5kIHkgYXhlcy4NCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpDb2xsZWdlUGxvdDIgPSBxcGxvdChDb2xsZWdlLCBGdW5uaW5lc3MsIGRhdGE9SHVtb3JEYXRhLCBnZW9tPSgiYm94cGxvdCIpLCANCiAgICAgICAgICAgICAgICAgICBtYWluPSJIdW1vciBDaGFydCIseGxhYj0iRWR1Y2F0aW9uIExldmVsIiwgeWxhYj0iU2VsZi1SYXRlZCBGdW5uaW5lc3MiKQ0KQ29sbGVnZVBsb3QyIA0KYGBgDQoNCipOb3RlOiogQXMgeW91IG1heSBoYXZlIG5vdGljZWQsIEkgdXNlZCB0aGUgcXBsb3QoKSBmdW5jdGlvbiBpbnN0ZWFkIG9mIHRoZSBnZ3Bsb3QoKSBmdW5jdGlvbiBoZXJlIChhbmQgaW4gdGhlIHBsb3QgYmVsb3cpLiBUaGV5IGFyZSBzaW1pbGFyIGZ1bmN0aW9ucyB0aGF0IGFjY29tcGxpc2ggbWFueSBvZiB0aGUgc2FtZSB0aGluZ3MuIE92ZXJhbGwsIHFwbG90KCkgaXMgcXVpY2tlciwgZWFzaWVyIHRvIG5hdmlnYXRlLCBidXQgbGVzcyBmbGV4aWJsZSB0aGFuIGdncGxvdCgpLCB3aGljaCBpcyBtb3JlIGN1c3RvbWl6YWJsZSBidXQgYWxzbyBtb3JlIGFkdmFuY2VkIGFuZCBjb21wbGV4Lg0KDQojIyMjKipKaXR0ZXIgQm94cGxvdHMqKg0KDQpXZSBjYW4gZXhwYW5kIHVwb24gdGhlIGNvZGUgZnJvbSBhYm92ZSB0byBmdXJ0aGVyIGN1c3RvbWl6ZSBvdXIgYm94cGxvdHMuIFRoZSBwbG90IGJlbG93IGlzIGEgaml0dGVyIGJveHBsb3QsIG1lYW5pbmcgdGhhdCB0aGUgYWN0dWFsIGRhdGEgcG9pbnRzIG9mIHNlbGYtcmF0ZWQgZnVubmluZXNzIGFyZSBvdmVybGF5ZWQgb24gdGhlIHBsb3QuIEZvbGxvd2luZyB0aGUgc2FtZSBpbnN0cnVjdGlvbnMgYXMgYWJvdmUgKHRoaXMgdGltZSB1c2luZyBHZW5kZXIgaW5zdGVhZCBvZiBDb2xsZWdlKSBidXQgY29tYmluaW5nIGdlb209YygiYm94cGxvdCIsICJqaXR0ZXIiKSB3ZSBjYW4gY3JlYXRlIGEgaml0dGVyIGJveHBsb3QuDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KSml0dGVyUGxvdCA9IHFwbG90KEdlbmRlciwgRnVubmluZXNzLCBkYXRhPUh1bW9yRGF0YSwgZ2VvbT1jKCJib3hwbG90IiwgImppdHRlciIpLCANCiAgICAgICAgICAgICAgICAgICBtYWluPSJIdW1vciBDaGFydCIseGxhYj0iR2VuZGVyIiwgeWxhYj0iU2VsZi1SYXRlZCBGdW5uaW5lc3MiKQ0KSml0dGVyUGxvdCANCmBgYA0KDQojIyMjKipGYWNldGVkIEJveHBsb3RzKioNCg0KRmFjZXRlZCBwbG90cyBhcmUgdXNlZnVsIGlmIHlvdSB3YW50IHRvIGVzc2VudGlhbGx5IGxvb2sgYXQgdHdvIGRpZmZlcmVudCBib3hwbG90cyBhdCB0aGUgc2FtZSB0aW1lIGJ1dCBkaXZpZGVkIGJ5IHRoZSBsZXZlbHMgb2Ygb25lIG9mIHlvdXIgY2F0ZWdvcmljYWwgdmFyaWFibGVzLiBUaGVyZSBhcmUgbWFueSB0aW1lcyB3aGVuIHlvdSBtYXkgd2FudCBhIGJveHBsb3QgdGhhdCBsb29rcyBhdCB0aGUgcG90ZW50aWFsIGludGVyYWN0aW9uIG9mIHR3byBjYXRlZ29yaWNhbCB2YXJpYWJsZXMuIEhlcmUgSSBhbSBsb29raW5nIGF0IGhvdyBzZWxmLXBlcmNpdmVkIGZ1bm5pbmVzcyBtYXkgZGlmZmVyIGFzIGZ1bmN0aW9uIG9mIGJvdGggZ2VuZGVyIGFuZCBlZHVjYXRpb24uIFVzaW5nIHRoZSBnZ3Bsb3QgKCkgZnVuY3Rpb24uIFRoZSBmb2xsb3dpbmcgY29kZSBpcyB2ZXJ5IHNpbWlsYXIgdG8gdGhlIHNpbXBsZSBib3hwbG90IGNvZGUgZnJvbSBvdXIgb3JpZ2luYWwgR2VuZGVyUGxvdDEuIEkgaGF2ZSBzaW1wbHkgYWRkZWQgdG8gdGhlIGVuZCBmYWNldF9ncmlkKH5Db2xsZWdlKSBpbmRpY2F0aW5nIHRoYXQgQ29sbGVnZSBpcyB0aGUgdmFyaWFibGUgdGhhdCBJIHdhbnQgUiB0byB1c2UgdG8gZGl2aWRlIHVwIHRoZSBib3hwbG90cy4NCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpGYWNldFBsb3QxID0gZ2dwbG90KEh1bW9yRGF0YSwgYWVzKHg9R2VuZGVyLCB5PUZ1bm5pbmVzcykpICsgZ2VvbV9ib3hwbG90KCkgKyBmYWNldF9ncmlkKH5Db2xsZWdlKSANCkZhY2V0UGxvdDENCmBgYA0KDQojIyMjKipDdXN0b21pemluZyBPdXRsaWVycyoqDQoNCkJveHBsb3RzIGNhbiBiZSB1c2VmdWwgaW4gaWRlbnRpZnlpbmcgb3V0bGllcnMgaW4geW91ciBkYXRhLiBPdXRsaWVycyBhcmUgYW55IGRhdGEgcG9pbnRzIHRoYXQgZmFsbCBvdXRzaWRlIG9mIHRoZSB3aGlza2VycyBvbiB0aGUgcGxvdCwgYW5kIHRoZXkgYXJlIGRlcGljdGVkIHdpdGggZG90cy4gWW91IG1heSBoYXZlIG5vdGljZWQgdHdvIGRvdHMgaW4gdGhlIGJveHBsb3QgYWJvdmUuIFRoZXkgZGVub3RlIHR3byBtYWxlIHBhcnRpY2lwYW50cyBhbW9uZyB0aGUgY29sbGVnZSBncmFkdWF0ZXMgd2hvc2Ugc2VsZi1yYXRlZCBmdW5uaW5lc3MgaXMgbG93IGZvciB0aGlzIGdyb3VwLiBUbyBjaGFuZ2UgdGhlIGxvb2sgb2YgdGhvc2UgZG90cywgeW91IGNhbiBhZGQgaW5mbyB0byB0aGUgZ2VvbV9ib3hwbG90KCkgY29tbWFuZC4gSGVyZSBJIGhhdmUgc3BlY2lmaWVkIHRoZSBzaXplLCBzaGFwZSwgYW5kIGNvbG9yIG9mIG15IG91dGxpZXJzLCBhbmQgY29uc2VxdWVudGx5IG1hZGUgdGhlIGNvcnJlc3BvbmRpbmcgZG90cyBsYXJnZXIgdGhhbiB0aGUgZGVmYXVsdCwgaG9sbG93IGRpYW1vbmQgc2hhcGVkLCBhbmQgcHVycGxlLiANCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpGYWNldFBsb3QyID0gZ2dwbG90KEh1bW9yRGF0YSwgYWVzKHg9R2VuZGVyLCB5PUZ1bm5pbmVzcywgbGFiZWw9SUQpKSArIGdlb21fYm94cGxvdChvdXRsaWVyLnNpemU9MyxvdXRsaWVyLnNoYXBlPTUsb3V0bGllci5jb2xvdXI9InB1cnBsZSIpICsgZmFjZXRfZ3JpZCh+Q29sbGVnZSkgDQoNCkZhY2V0UGxvdDINCmBgYA0KDQojIyMjKipBZGRpbmcgQ29sb3IgdG8gWW91ciBQbG90cyoqDQpFeHBhbmRpbmcgb24gdGhlIGNvZGUgZnJvbSBGYWNldFBsb3QxLCBJIHdhbnQgdG8gdGVsbCBSIHRoYXQgSSB3b3VsZCBsaWtlIGl0IHRvIGNvbG9yIGluIHRoZSBib3hwbG90cyBpbnN0ZWFkIG9mIHRoZSBzYW1lIGJvcmluZyB3aGl0ZS4gYnkgYWRkaW5nIGZpbGw9R2VuZGVyIHRvIHRoZSBhZXMgY29tbWFuZCBJIGFtIHRlbGxpbmcgUiB0byBmaWxsIHRoZSBjb2xvciBpbiB3aXRoIHRoZSBkZWZhdWx0IGNvbG9yLiBUaGUgc2NhbGVfZmlsbF9icmV3ZXIgY29tbWFuZCB0ZWxscyBSIHdoYXQgY29sb3IgcGFsZXR0ZSBJIHdhbnQgaXQgdG8gdXNlIGlmIEkgd291bGQgbGlrZSBpdCB0byBiZSBkaWZmZXJlbnQgZnJvbSB0aGUgZGVmYXVsdC4gDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KRmFjZXRQbG90MyA9IGdncGxvdChIdW1vckRhdGEsIGFlcyh4PUdlbmRlciwgeT1GdW5uaW5lc3MsIGZpbGw9R2VuZGVyKSkgKyBnZW9tX2JveHBsb3QoKSArIGZhY2V0X2dyaWQofkNvbGxlZ2UpICsgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikNCkZhY2V0UGxvdDMNCmBgYA0KDQpDb2xvciB1c2UgaW4gUiBjb3VsZCBiZSBpdHMgb3duIGNoYXB0ZXIuIEFzIEkgd2lsbCBub3QgYmUgYWJsZSB0byBkbyB0aGlzIGFzcGVjdCBvZiBwbG90cyBhbmQgY2hhcnRzIGp1c3RpY2UsIHBsZWFzZSBzZWUgb3RoZXIgcmVzb3VyY2VzIG9uIHRoZSBpbnRlcm5ldCBmb3IgaGVscCwgaW5jbHVkaW5nIHRoaXMgY29sb3IgY2hhcnQgLSBodHRwOi8vcmVzZWFyY2guc3Rvd2Vycy1pbnN0aXR1dGUub3JnL2VmZy9SL0NvbG9yL0NoYXJ0LyBvciB0aGUgZm9sbG93aW5nIFIgY29sb3IgY2hlYXQgc2hlZXQgd2hpY2ggZGVzY3JpYmVzIGRpZmZlcmVudCBjb2xvciBwYWNrYWdlcyB5b3UgY2FuIGluc3RhbGwgLSAgaHR0cHM6Ly93d3cubmNlYXMudWNzYi5lZHUvfmZyYXppZXIvUlNwYXRpYWxHdWlkZXMvY29sb3JQYWxldHRlQ2hlYXRzaGVldC5wZGYNCiANCiMjIyMqKkJveHBsb3RzIFVzaW5nIE11bHR1cGxlIENhdGVnb3JpY2FsIFZhcmlhYmxlcyBXaXRob3V0IEZhY2V0cyoqDQoNCkkgY2FuIGFsc28gcmVtb3ZlIHRoZSBmYWNldCAoYnV0IG1haW50YWluIHRoZSBwcmVzZW5jZSBvZiBib3RoIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyA9IEdlbmRlciAmIENvbGxlZ2UpIGJ5IGp1c3QgcmVtb3ZpbmcgdGhlIGZhY2V0X2dyaWQgY29tbWFuZC4gQWxzbywgYXMgeW91IGNhbiBzZWUgSSBoYXZlIHJlbW92ZWQgdGhlIHNjYWxlX2ZpbGxfYnJld2VyIGNvbW1hbmQgYW5kIHdlIHN0aWxsIGhhdmUgY29sb3JzIChhcyBub3cgUiBpcyBzaG93aW5nIHVzIHRoZSBkZWZhdWx0IGNvbG9ycykuDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KQ29tYmluZWRQbG90PWdncGxvdChIdW1vckRhdGEsIGFlcyh4PUNvbGxlZ2UsIHk9RnVubmluZXNzLCBmaWxsPUdlbmRlcikpICsgZ2VvbV9ib3hwbG90KCkgDQpDb21iaW5lZFBsb3QNCmBgYA0KDQojIyMjKipOb3RjaGVkIEJveHBsb3RzKioNCg0KTm90Y2hlZCBib3gtcGxvdHMgbWF5IGJlIHVzZWZ1bCBpbiBzb21lIGluc3RhbmNlcy4gTm90IG9ubHkgYXJlIHRoZXkgZnVuIHRvIGxvb2sgYXQsIGJ1dCBpZiB0d28gYm94ZXMnIG5vdGNoZXMgZG8gbm90IG92ZXJsYXAgdGhpcyBpcyDigJhzdHJvbmcgZXZpZGVuY2XigJkgdGhlaXIgbWVkaWFucyBkaWZmZXIgKENoYW1iZXJzIGV0IGFsLiwgMTk4MywgcC4gNjIpLiANCg0KVG8gbWFrZSBhIHBsb3Qgbm90Y2hlZCwgc2ltcGx5IGFkZCBub3RjaD1UUlVFIHRvIHRoZSBnZW9tX2JveHBsb3QgY29tbWFuZC4gDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KQ29tYmluZWRQbG90Tk9UQ0g9Z2dwbG90KEh1bW9yRGF0YSwgYWVzKHg9R2VuZGVyLCB5PUZ1bm5pbmVzcywgZmlsbD1HZW5kZXIpKSArIGdlb21fYm94cGxvdChub3RjaD1UUlVFKSArIGZhY2V0X2dyaWQofkNvbGxlZ2UpDQpDb21iaW5lZFBsb3ROT1RDSA0KYGBgDQoNClRoZSBub3RjaGVzLCB3aGljaCBzdGFydCB3aGVyZSB0aGUgc2lkZXMgYmVnaW4gdG8gc2xvcGUgaW53YXJkLCBhcmUgc2ltaWxhciBpbiBmdW5jdGlvbiB0byBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwuIFdoZW4gbG9va2luZyBhdCB0aGUgY29sbGVnZSBncmFkdWF0ZXMsIGl0IGFwcGVhcnMgYXMgaWYgbm8gcGFydCBvZiB0aGUgbm90Y2hlcyBvdmVybGFwIGZvciBtYWxlIGFuZCBmZW1hbGUgcGFydGljaXBhbnRzLiBIb3dldmVyLCB0aGV5IGRvIGFwcGVhciB0byBvdmVybGFwIGluIHRoZSBwZW9wbGUgd2hvIGRpZCBub3QgZ3JhZHVhdGUgZnJvbSBjb2xsZWdlLiBUaGlzIHN1Z2dlc3RzIHRoYXQgdGhlcmUgaXMgYSByZWFsIGRpZmZlcmVuY2UgaW4gc2VsZi1yYXRlZCBmdW5uaW5lc3MgYmV0d2VlbiBtZW4gYW5kIHdvbWVuIHdobyB3ZW50IHRvIGNvbGxlZ2UsIGJ1dCB0aGlzIHBhdHRlcm4gd2FzIG5vdCBkZW1vbnN0cmF0ZWQgYW1vbmcgdGhvc2Ugd2hvIGRpZCBub3QgZ28gdG8gY29sbGVnZS4gTmV2ZXJ0aGVsZXNzLCBJIHdvdWxkIG5vdCBzb2xleSByZWx5IG9uIHRoaXMgdmlzdWFsLiBJZiB0aGlzIHF1ZXN0aW9uIGlzIGltcG9ydGFudCB0byB5b3UsIEkgd291bGQgYWxzbyBjb25kdWN0IHN0YXRpc3RpY2FsIGFuYWx5c2VzIHRvIGJlIHN1cmUgKGZvciBleGFtcGxlIHlvdSBjb3VsZCB1c2UgYW4gQU5PVkEgbGlrZSBpbiBDaGFwdGVycyAyMCAmIDIxKS4NCg0KDQojICoqQmFyIEdyYXBocyoqDQoNCiMjIyoqUHJlcGFyaW5nIHRoZSBkYXRhKioNCg0KQmFyIGdyYXBocyBhcmUgZGlmZmVyZW50IGZyb20gYm94cGxvdHMgaW4gdGhhdCB0aGUgZGF0YSB5b3UgdXNlIHNvbWV0aW1lcyBuZWVkcyB0byBiZSBpbiB2ZWN0b3Igb3IgbWF0cml4IGZvcm1hdC4NCg0KRm9yIGV4YW1wbGUgeW91IGNhbiBqdXN0IG1ha2UgYSBxdWljayB2ZWN0b3IgdXNpbmcgdGhlIGMoKSBmdW5jdGlvbi4NCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpWZWN0b3IxIDwtIGMoMTMsMjksMjMsMzUsMTYsMjApDQpgYGANCg0KV2UgY2FuIGFsc28gdXNlIHRoZSBtZWFucyBmcm9tIHRoZSBodW1vciBleGFtcGxlIHRvIGRlbW9uc3RyYXRlIGhvdyB0byBkbyB0aGlzIHdpdGggcmVhbCBkYXRhLiBPbmUgcXVpY2sgbm90ZSBoZXJlLCB5b3UgbWF5IG5vdGljZSB0aGF0IEkgYWRkZWQgYSBiYWNrc2xhc2ggZm9sbG93ZWQgYnkgbiB0byB0aGUgbWlkZGxlIG9mIHRoZSBuYW1lcyBpbiB0aGUgSHVtb3JHcm91cHMgdmVjdG9yIGJlbG93LiBUaGlzIG5vdGF0aW9uIHRlbGxzIFIgdG8gYnJlYWsgdXAgdGhlIG5hbWUgYXQgdGhhdCBzcG90LiBJdCBjYW4gYmUgYSB0cmljayB0byBtYWtlIGdyYXBocyBsb29rIG5pY2VyIGlmIHlvdSBoYXZlIGxvbmcgY2F0ZWdvcnkgbmFtZXMuDQoNCmBgYCB7ciwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFfQ0KSHVtb3JNZWFucyA8LSBjKDgxLDU5LDQwLDYwKQ0KSHVtb3JHcm91cHMgPC0gYygiQ29sbGVnZVxuR3JhZCBNZW4iLCJDb2xsZWdlXG5HcmFkIFdvbWVuIiwiTm90IENvbGxlZ2VcbkdyYWQgTWVuIiwiTm90IENvbGxlZ2VcbkdyYWQgV29tZW4iKQ0KYGBgDQoNCiMjIyoqTWFraW5nIFNpbXBsZSBCYXIgQ2hhcnRzKioNCg0KWW91IGNhbiB1c2UgdGhlIGJhcnBsb3QoKSBmdW5jdGlvbiB0byBtYWtlIHNpbXBsZSBiYXIgZ3JhcGggd2l0aCB0aGUgb3JpZ2luYWwgdmVjdG9yIHdlIG1hZGUuIEkgcmVhbGl6ZSB0aGF0IHRoZSBwcmltYXJ5IHB1cnBvc2Ugb2YgdGhpcyBjaGFwdGVyIGlzIHRvIGhlbHAgcGVvcGxlIHVzZSB0aGUgKmdncGxvdDIgcGFja2FnZSogdG8gbWFrZSBib3hwbG90cyBhbmQgYmFyIGdyYXBocywgaG93ZXZlciwgSSB0aGluayBpdCBpcyByZWFsbHkgdXNlZnVsIHRvIGFsc28ga25vdyBob3cgdG8gbWFrZSByZWFsbHkgc2ltcGxlIGdyYXBocyBpbiB0aGlzIHdheSBhcyB3ZWxsLiBUaGUgbmVlZCB0byB1c2UgZGlmZmVyZW50IHNvdXJjZXMgb2YgZGF0YSBjYW4gY2hhbmdlIGhvdyBlYXN5IGl0IGlzIHRvIG1ha2UgZ3JhcGhzIHdpdGggZGlmZmVyZW50IHBhY2thZ2VzIGFuZCBmdW5jdGlvbnMuICANCg0KSGVyZSBpcyB0aGUgc3VwZXIgc2ltcGxlIGJhciBncmFwaCB1c2luZyBWZWN0b3IxLg0KDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NClNpbXBsZUJhciA9IGJhcnBsb3QoVmVjdG9yMSkNCmBgYA0KDQpIb3dldmVyLCB0aGVyZSBpcyBub3QgbXVjaCB1c2UgZm9yIHN1Y2ggYSBzaW1wbGUgYmFyIGdyYXBoLiBXaXRoIHRoZSBhZGRpdGlvbiBvZiBhIGZldyBtb3JlIGNvbW1hbmRzLCB5b3UgY2FuIHVzZSB0aGUgc2FtZSBmdW5jdGlvbiB0byBtYWtlIGEgbW9yZSB1c2VmdWwgZ3JhcGggd2l0aCB0aGUgaHVtb3IgdmVjdG9ycyB3ZSBtYWRlIGVhcmxpZXIuIEluIHRoZSBwYXJlbnRoZXNlcyBmaXJzdCBwbGFjZSB0aGUgdmVjdG9yIHdpdGggdGhlIHZhbHVlcyB5b3Ugd2FudCBpbiB0aGUgZ3JhcGgsIG5leHQgZXN0YWJsaXNoIHRoZSBuYW1lcyB0byBpZGVudGlmeSB0aGUgZ3JvdXBzIHRoZXNlIG1lYW5zIGJlbG9uZyB0byB3aXRoIHRoZSBvdGhlciB2ZWN0b3IgKG5hbWVzLmFyZyA9IEh1bW9yR3JvdXBzKS4gVGhlbiB5b3UgY2FuIGxhYmVsIGFuZCBjaGFuZ2UgdGhlIGNvbG9yIG9mIHRoZSBncmFwaC4gSSBoYXZlIGFkZGVkIGNvbG9yIHRvIHRoZSBwbG90cyB3aXRoIHRoZSBjb2w9ImdyZWVuIiBjb21tYW5kLiBJbiB0aGlzIGNvbnRleHQsIHR5cGluZyBpbiB0aGUgbmFtZSBvZiBtb3N0IGNvbG9ycyB3aWxsIHdvcmsgKGUuZy4gImJsYWNrIiwgInJlZCIsICJ5ZWxsb3ciKS4gIEhlcmUgdGhlcmUgaXMgdGhlIG9wdGlvbiB0byBjaGFuZ2UgdGhlIGNvbG9yIG9mIHRoZSBib3JkZXIgb2YgdGhlIGJhcnMuIEkgaGF2ZSBhbHNvIGFkZGVkIGluIGEgbGFiZWwgZm9yIHRoZSB3aG9sZSBjaGFydCB1c2luZyB0aGUgYWRkZWQgY29tbWFuZCBvZiBtYWluID0gIkh1bW9yIENoYXJ0LCIgYW5kIGxhYmVscyBmb3IgdGhlIHggYW5kIHkgYXhlcyB1c2luZyB0aGUgeGxhYiBhbmQgeWxhYiBjb21tYW5kcy4NCg0KYGBgIHtyLCBtZXNzYWdlPUZBTFNFLCBlY2hvPVRSVUV9DQpTaW1wbGVCYXIyID0gYmFycGxvdChIdW1vck1lYW5zLG5hbWVzLmFyZyA9IEh1bW9yR3JvdXBzLHhsYWIgPSAiR2VuZGVyIGFuZCBFZHVjYXRpb24gR3JvdXBpbmdzIix5bGFiID0gIlNlbGYtUmVwb3J0ZWQgRnVubmluZXNzIixjb2wgPSAiZ3JlZW4iLG1haW4gPSAiSHVtb3IgQ2hhcnQiLGJvcmRlciA9ICJibGFjayIpDQpgYGANCg0KIyMjICoqQ3JlYXRpbmcgU3RhY2tlZCBCYXIgR3JhcGhzKioNCg0KQXMgYmFyIGdyYXBocyBnZXQgbW9yZSBjb21wbGljYXRlZCwgZ2dwbG90KCkgaXMgYSBtb3JlIHVzZWZ1bCBmdW5jdGlvbi4gWW91IHdpbGwgbm90IG5lZWQgdG8gY3JlYXRlIHZlY3RvcnMgb3IgbWF0cmljZXMgdG8gZGVsaW5pYXRlIHRoZSBkYXRhIHBvaW50cyB0byB1c2UuIEluc3RlYWQsIHlvdSBjYW4gdGVsbCB0aGUgZnVuY3Rpb24gdG8gdXNlIHRoZSBtZWFucyB5b3Ugd2FudCB1c2luZyB0aGUgY29tbWFuZCBnZW9tX2JhcihzdGF0PSJzdW1tYXJ5IiwgZnVuLnk9Im1lYW4iKS4gT25jZSBhZ2FpbiwgSSBoYXZlIGNoYW5nZWQgdGhlIGNvbG9yIHBhbGF0dGUgaGVyZSBhbmQgYWRkZWQgYSB0aXRsZSB1c2luZyBnZ3RpdGxlKCJIdW1vciBDaGFydCIpLg0KDQpgYGAge3IsIG1lc3NhZ2U9RkFMU0UsIGVjaG89VFJVRX0NClN0YWNrZWRCYXIgPSBnZ3Bsb3QoSHVtb3JEYXRhLCBhZXMoR2VuZGVyLCBGdW5uaW5lc3MsIGZpbGwgPSBDb2xsZWdlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdD0ic3VtbWFyeSIsIGZ1bi55PSJtZWFuIikgKyANCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKyBnZ3RpdGxlKCJIdW1vciBDaGFydCIpDQpTdGFja2VkQmFyDQpgYGANCg0KIyMjICoqQ3JlYXRpbmcgR3JvdXBlZCBCYXIgR3JhcGhzKioNCg0KQ3JlYXRpbmcgZ3JvdXBlZCBiYXIgZ3JhcGhzIGlzIHByZXR0eSBzaW1wbGUgb25jZSB5b3UgaGF2ZSBhIHNlbnNlIG9mIGhvdyBvdGhlciBiYXIgZ3JhcGhzIHdvcmsuIFIgY3JlYXRlcyBzdGFja2VkIGdyYXBocyBhcyBkZWZhdWx0LCBzbyB5b3UgaGF2ZSB0byBhZGQgaW4gcG9zaXRpb24gPSAiZG9kZ2UiIHRvIHRoZSBnZW9tX2JhciBjb21tYW5kLiANCg0KYGBgIHtyLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCkdyb3VwZWRCYXIgPSBnZ3Bsb3QoSHVtb3JEYXRhLCBhZXMoR2VuZGVyLCBGdW5uaW5lc3MsIGZpbGwgPSBDb2xsZWdlKSkgKyANCiAgZ2VvbV9iYXIoc3RhdD0ic3VtbWFyeSIsIGZ1bi55PSJtZWFuIiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArIA0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlNldDIiKSArIGdndGl0bGUoIkh1bW9yIENoYXJ0IikNCkdyb3VwZWRCYXINCmBgYA0KDQojIyMqKlNhdmluZyBZb3VyIFdvcmsqKg0KDQpBZnRlciBnb2luZyB0aHJvdWdoIHRoZSB0cm91YmxlIHRvIGNyZWF0ZSBib3hwbG90cyBhbmQgYmFyIGdyYXBocywgeW91IG1heSB3YW50IHRvIHNhdmUgdGhlbSBmb3Igb3V0c2lkZSB1c2UuIEZpcnN0LCB5b3UgbmVlZCB0byBkZWNpZGUgd2hhdCB0eXBlIG9mIGdyYXBoaWNzIGZpbGUgeW91IHdhbnQuIEZvciBhIFBERiB1c2UgdGhlIHBkZigpIGZ1bmNpb24sIGZvciBQTkcgdXNlIHBuZygpLCBhbmQgZm9yIEpQRyB1c2UganBnKCkuIFdpdGhpbiB0aGUgcGFyZW50aGVzZXMgb2YgdGhpcyBmdW5jdGlvbiwgeW91IGluZGljYXRlIHdoYXQgeW91IHdvdWxkIGxpa2UgdG8gbmFtZSB0aGUgZmlsZS4gRm9yIHRoZSBuZXh0IGxpbmUgb2YgY29kZSwgeW91IG5lZWQgdG8gaWRlbnRpZnkgdGhlIHBsb3QgeW91IHdhbnQgdG8gc2F2ZSB3aXRoIHRoZSBwbG90KCkgZnVuY3Rpb24uIExhc3RseSwgY29tcGxldGUgdGhlIHNhdmluZyBwcm9jZXNzIGJ5IGFkZGluZyBkZXYub2ZmKCkuIFRoZW4geW91IHNob3VsZCBzZWUgeW91ciBzYXZlZCBmaWxlIGluIHlvdXIgd29ya2luZyBkaXJlY3RvcnkhIFNlZSBiZWxvdyBmb3IgYW4gZXhhbXBsZSB1c2luZyB0aGUgbGFzdCBiYXIgZ3JhcGggd2UgbWFkZSBhYm92ZToNCg0KYGBgIHtyLCByZXN1bHRzPSdoaWRlJywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnBuZyhmaWxlbmFtZT0iR3JvdXBlZEJhckdyYXBoLnBuZyIpDQpwbG90KEdyb3VwZWRCYXIpDQpkZXYub2ZmKCkNCmBgYA0KDQojICoqTW9yZSBSZXNvdXJjZXMqKg0KDQpUaGlzIGlzIG9ubHkgYSBicmllZiBpbnRyb2R1Y3Rpb24gdG8gdXNpbmcgdGhlIGdncGxvdDIgcGFja2NhZ2UuIEZvciBhZGRpdGlvbmFsIHR5cGVzIG9mIHBsb3RzIGFuZCBwbG90IGN1c3RvbWl6YXRpb24gdGlwcyBzZWUgQ2hhcHRlciA4LiBBbHNvLCBjaGVjayBvdXQgdGhlIGZvbGxvd2luZyBsaW5rcyBmb3IgYWRkaXRpb25hbCB3ZWIgcmVzb3VyY2VzIGZvciBtYWtpbmcgYm94cGxvdHMgYW5kIGJhciBncmFwaHM6DQoNCi0gaHR0cDovL3d3dy5zdGhkYS5jb20vZW5nbGlzaC93aWtpL2dncGxvdDItYm94LXBsb3QtcXVpY2stc3RhcnQtZ3VpZGUtci1zb2Z0d2FyZS1hbmQtZGF0YS12aXN1YWxpemF0aW9uDQotIGh0dHA6Ly9kb2NzLmdncGxvdDIub3JnLzAuOS4zLjEvZ2VvbV9ib3hwbG90Lmh0bWwNCi0gaHR0cDovL3d3dy50aGVhbmFseXNpc2ZhY3Rvci5jb20vci10dXRvcmlhbC1wYXJ0LTEzLw0KLSBodHRwOi8vd3d3LnN0YXRtZXRob2RzLm5ldC9ncmFwaHMvYmFyLmh0bWwNCi0gaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vdXNpbmctci1iYXJwbG90LXdpdGgtZ2dwbG90Mi8NCg0KDQo8c2NyaXB0Pg0KICAoZnVuY3Rpb24oaSxzLG8sZyxyLGEsbSl7aVsnR29vZ2xlQW5hbHl0aWNzT2JqZWN0J109cjtpW3JdPWlbcl18fGZ1bmN0aW9uKCl7DQogIChpW3JdLnE9aVtyXS5xfHxbXSkucHVzaChhcmd1bWVudHMpfSxpW3JdLmw9MSpuZXcgRGF0ZSgpO2E9cy5jcmVhdGVFbGVtZW50KG8pLA0KICBtPXMuZ2V0RWxlbWVudHNCeVRhZ05hbWUobylbMF07YS5hc3luYz0xO2Euc3JjPWc7bS5wYXJlbnROb2RlLmluc2VydEJlZm9yZShhLG0pDQogIH0pKHdpbmRvdyxkb2N1bWVudCwnc2NyaXB0JywnaHR0cHM6Ly93d3cuZ29vZ2xlLWFuYWx5dGljcy5jb20vYW5hbHl0aWNzLmpzJywnZ2EnKTsNCg0KICBnYSgnY3JlYXRlJywgJ1VBLTk4ODc4NzkzLTEnLCAnYXV0bycpOw0KICBnYSgnc2VuZCcsICdwYWdldmlldycpOw0KDQo8L3NjcmlwdD4NCg0K