What is R?
R is an open-source statistical language available for free download and maintained by the R Consortium, Inc. and the worldwide community of users, maintainers, and developers of R software. This workshop is for demonstration purposes only and is not meant to provide an exhaustive education in the R Project for Statistical Computing. The examples used in this workshop were developed primarily for educational purposes and are being presented in this free workshop as a demonstration of some of the capabilities of this open source language and environment for statistical computing and graphics. Throughout this workshop you will see screen captures and images from the RStudio Program. It is an integrated development environment (IDE) for R maintained by RStudio, Inc. and is available in free open source and commercial editions. All of the code and data sourced for this workshop was developed or modified by the authors and has been made available for educational use through the links provided in this workbook. Additional materials in this workbook have been made freely available under Creative Commons licenses and distributed in open-source platforms.
In The Beginning
You must install R and RStudio on your PC, Mac, or Linux computer. RStudio is not a stand-alone program so please install R first:
R Download: https://mirrors.nics.utk.edu/cran/
RStudio Download: https://www.rstudio.com/products/rstudio/download/
RStudio operates as a quasi-graphical user interface (GUI) for R. RStudio typically opens in a three or four window design:
Script/Data Window |
History/Environment |
Code/Console |
Packages, Help, etc. |
Script/Data Window: This area lets you develop script to be run in the console window and also view data sets.
History/Environment Window: In this section of RStudio you can see objects you have imported or created and a history of the code you have run.
Code/Console Window: All of the code you run from scripts will appear in the console window (along with any errors highlighted in red). You can also input code directly to this area, initiate help windows on various packages, view options for certain commands, etc.
Files, Plots, Packages, Help, Viewer Window: In this area you can load and view options within various packages (a collection of formatted functions, data, and compiled code that improve the functionality of the base R functions), view and export plots/graphics that have been created, view help options for packages/functions, and view files in your current working directory (explained later).
The Basics
When beginning any project in R, it can be beneficial to start a new R Script. This creates a record of the code you create that can be saved for future reference and sharing. To start a script click CRTL+Shift+N on PC, Shift+⌘+N on Mac, or select R Script from the drop-down menu below File io the menu bar.
In the scripting window there are some limited debugging features that provide suggestions regarding errors in your code. You can see an example of this feature in the image below.
However, there are a lot of resources available through the help tab in the lower-right window or by including a ? before any package or function you’re trying to run such as ?setwd()
. There are also tons of resources available through google searches and YouTube tutorials that walk you step-by-step through various packages or analyses. Throughout this workshop we will use a number of different packages. These are collections of R functions, data, and compiled code to help perform specific tasks. While R provides some basic functions, packages expand the power of R allowing you to perform more advanced functions and analyses. For each new package you will need to use the function install.packages(...)
with the name of the package between the parenthesis to download and library(...)
to load the package into your working environment. You only need to download the package once, but you will need to load the package with each new project.
Getting Started
To start you want to either begin with a new project or establish your working directory. If you are planning on the analysis being part of a large project or that you plan to share with other researchers, it would be ideal to begin with a new project directory using version control. However, if you are looking to complete a quick analysis to working on a class exercise it might be sufficient enough to just create w working directory. This directory is where all of the datasets used for the project should be stored and where all of the output data, plots, etc. will be stored by default. Set this directory by using the following script:
setwd(“…”)
Between the parentheses and quotes you should input the folder path from your computer. This can be obtained by using windows explorer on a PC, Command+⌘ on a Mac, or find in Linux. One thing to note with R, path names use forward slash and not the backslash which is common to windows. Here is an example of a correct setwd(“...”)
script:
Once you have the script written in the scripting window, select the line and click Run to initiate the script. Notice when you set the working directory, that information also appears at the top of and in the Console/Code window:
With the working directory set, it is time to import your data for analysis.
Importing Data
To begin, you will need to import a dataset(s) in to your working environment. As with all things in R and RStudio, there are a number of different ways to complete various tasks. This includes using a read function in R, using the import feature in RStudio, or using scripts to create the data. For the purpose of this workshop we begin by using a multi-tab excel file.
So we will need to add a specific package called readxl
and to do that we will use the following script to obtain, install, and load the package:
If successful you should now have the package listed under Packages in the packages tab and it should be checked. To get information about the package you and type ?readxl into the console or click on the package name in the packages tab.We are going to use the read_excel()
function to load each tab individually as objects in our working environment. To learn more about the function ?read_excel
type it in the console.
basic_stats <- read_excel("ChemData.xlsx", sheet="central tendency")
ttest <- read_excel("ChemData.xlsx", sheet="t-test")
anova <- read_excel("ChemData.xlsx", sheet="anova")
correlation <- read_excel("ChemData.xlsx", sheet="correlations")
regression <- read_excel("ChemData.xlsx",sheet = "regression")
When complete you should have a list of five (5) datasets in your working environment. By clicking on the arrow in the blue circle on the left of the object name you can expand the dataset to see what sort of information it contains. By clicking the sheet icon on the far right you can open the data to view its contents.
Data Formatting and Analysis
If you examine the basic_stats dataset you will see columns labeled No which refers to the trial number and then a column for each student in a class. Because we want our dataset to contain only numerical values we can use R to quickly format the dataset and remove the first column of data using the row numbers instead to identify the trails.
To perform this action we will use the following command:
basic_stats refers to the dataset and [,1] refers to the first column. If we wanted to remove rows in a vector or matrix dataset we would have used [1,] instead. Using the head()
function we can now view the first few rows of our newly formatted data.
Summary Statistics
Now we can use the dataset to examine some basic statistics such as mean, median, standard deviation (sd), variance (var), min, max, median, range, and quantile or we can see a summary of the data.
Student_2 Student_3 Student_4 Student_5
Min. :0.9933 Min. :0.9343 Min. :0.8611 Min. :0.9741
1st Qu.:0.9954 1st Qu.:0.9559 1st Qu.:0.8739 1st Qu.:1.0226
Median :0.9969 Median :0.9581 Median :0.8792 Median :1.0247
Mean :1.0457 Mean :0.9572 Mean :0.8803 Mean :1.0428
3rd Qu.:0.9981 3rd Qu.:0.9607 3rd Qu.:0.8884 3rd Qu.:1.0370
Max. :1.9768 Max. :0.9653 Max. :0.8942 Max. :1.1705
Student_6 Student_7 Student_8 Student_9
Min. :0.9018 Min. :0.9992 Min. :1.007 Min. :0.5432
1st Qu.:0.9127 1st Qu.:1.0011 1st Qu.:1.008 1st Qu.:0.9961
Median :0.9200 Median :1.0018 Median :1.009 Median :1.0006
Mean :0.9184 Mean :1.0025 Mean :1.009 Mean :0.9774
3rd Qu.:0.9248 3rd Qu.:1.0026 3rd Qu.:1.010 3rd Qu.:1.0044
Max. :0.9310 Max. :1.0155 Max. :1.012 Max. :1.3930
Student_10 Student_11 Student_12 Student_13
Min. :0.9730 Min. :0.5261 Min. :1.005 Min. :0.9559
1st Qu.:0.9896 1st Qu.:0.9723 1st Qu.:1.006 1st Qu.:0.9830
Median :0.9913 Median :0.9826 Median :1.007 Median :0.9870
Mean :0.9953 Mean :0.9567 Mean :1.008 Mean :0.9847
3rd Qu.:1.0051 3rd Qu.:0.9894 3rd Qu.:1.009 3rd Qu.:0.9893
Max. :1.0316 Max. :0.9921 Max. :1.013 Max. :0.9914
Student_14 Student_15 Student_16 Student_17
Min. :1.159 Min. :0.9591 Min. :0.9486 Min. :0.9912
1st Qu.:1.179 1st Qu.:0.9962 1st Qu.:0.9580 1st Qu.:0.9939
Median :1.183 Median :0.9972 Median :0.9636 Median :0.9952
Mean :1.181 Mean :0.9971 Mean :0.9669 Mean :0.9947
3rd Qu.:1.184 3rd Qu.:0.9982 3rd Qu.:0.9727 3rd Qu.:0.9952
Max. :1.187 Max. :1.0333 Max. :0.9876 Max. :0.9972
Student_18 Student_19 Student_20
Min. :1.004 Min. :0.8837 Min. :1.001
1st Qu.:1.006 1st Qu.:0.9815 1st Qu.:1.002
Median :1.007 Median :0.9830 Median :1.003
Mean :1.007 Mean :0.9833 Mean :1.003
3rd Qu.:1.009 3rd Qu.:0.9842 3rd Qu.:1.003
Max. :1.011 Max. :1.0856 Max. :1.004
If we want to examine statistics not included in the summary above you can use the script sapply(basic_stats, sd)
and substitute the sd for any of the statistics listed above. This command applies the same function, standard deviation in this example, for values across a dataset, in this case basic_stats.
Student_2 Student_3 Student_4 Student_5 Student_6
0.2191556659 0.0063999859 0.0098162159 0.0480434591 0.0087473247
Student_7 Student_8 Student_9 Student_10 Student_11
0.0032817992 0.0014800854 0.1624048055 0.0126479638 0.1021407781
Student_12 Student_13 Student_14 Student_15 Student_16
0.0020438150 0.0079614194 0.0061589307 0.0121377380 0.0123836928
Student_17 Student_18 Student_19 Student_20
0.0014721942 0.0017635611 0.0327986545 0.0008405765
Paired t-test
For this next analysis we are going to use the ttest dataset to run a paired t-test so we can calculate the difference between paired observations. To do this we are going to use the $
operator to point to specific values in our dataset. This operator can be used in a number of different data formats to narrow down which information will be used in a calculation. To perform the t-test we will use the following script:
t.test(ttest$Student_A,ttest$Student_B, paired = TRUE)
Paired t-test
data: ttest$Student_A and ttest$Student_B
t = 2.4924, df = 19, p-value = 0.02209
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
0.002010165 0.023080060
sample estimates:
mean of the differences
0.01254511
We can vary the input to perform variations on the ttest such as One Sample t-test t.test(ttest)
or a Welch Two Sample t-test t.test(ttest$Student_A,ttest$Student_B, paired = FALSE)
. More information on this function can be found at ?t.test
We can also use a boxplot to graph out the values in our dataset. This can be used as a diagnostic step to help us compare two variables. To create a boxplot we use the following:
boxplot(ttest$Student_A,ttest$Student_B)

What hypothesis would you have made regarding the t-test after seeing the results of the box plot?
ANOVA
An Analysis of Variance (ANOVA) is a powerful tool for examining the differences among group means in a sample. To run this analysis we will use the anova dataset to examine all of the students trials. In order to examine the differences among the groups we will use a Tukey’s HSD (honestly significant difference) test. However, because the output tables can become quite large, we are going to use two additional packages to help organize the data. These are the tidyverse
and broom
packages.
library(broom)
library(tidyverse)
anova.results <- aov(Quantity~Group,data=anova)
summary(anova.results)
Df Sum Sq Mean Sq F value Pr(>F)
Group 18 1.265 0.07026 14.91 <2e-16 ***
Residuals 359 1.691 0.00471
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
From our analysis we can see there is a significant difference in the amount of liquid (quantity) being pipetted by each student (group). However, we still need to run the post-hoc test to determine exactly where those significant differences are within the data. To do that we are going to use a function from broom called tidy
to help format the table that results from the TuketHSD
function.
tidy(TukeyHSD(anova.results))
You can use the arrow in the header row to move the table to see columns to the right or the numbers at the bottom to see additional rows. The resulting table has 171 rows of data. Some of which have results that are significant and some are not. So we can instead use the filter function from the dplyr package to select only those comparisons where the adjust p-value is less than 0.05.
tidy(TukeyHSD(anova.results)) %>% filter(adj.p.value<0.05)
This helps reduce the table to the 46 comparisons that have significant p-values. Remember you can use the arrow to see columns to the right or the numbers to see additional rows. We can use the ggplot2
package to make a plot of the data and identify outliers in the dataset.
Warning messages:
1: In readChar(file, size, TRUE) : truncating string with embedded nuls
2: In readChar(file, size, TRUE) : truncating string with embedded nuls
ggplot(anova.results, aes(x = Group, y = Quantity)) +
geom_boxplot(colour = "black", outlier.colour="red", outlier.shape = 8, outlier.size = 2, fatten = 1) +
scale_x_discrete(limits = c('Student_2','Student_3','Student_4',
'Student_5','Student_6','Student_7',
'Student_8','Student_9','Student_10',
'Student_11','Student_12','Student_13',
'Student_14','Student_15','Student_16',
'Student_17','Student_18','Student_19',
'Student_20')) +
labs(x="Students", y="Volume (mL)", title="Box Plot of Means") +
theme(plot.title = element_text(hjust=0.5, face="bold")) +
theme(axis.text.x = element_text(angle = 90, vjust = 0.5))

In this plot can you identify outliers (red symbols) in the dataset?
Regression
You can also examine the relationship between the volume and mass of each trial in the data. To do this we will run and plot a linear regression with mass as dependent variable and volume as the independent predictor.
regression.results <- lm(Volume~Mass, data = regression)
summary(regression.results)
Call:
lm(formula = Volume ~ Mass, data = regression)
Residuals:
Min 1Q Median 3Q Max
-0.018805 -0.003122 0.001021 0.005849 0.009918
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) 0.007527 0.006058 1.242 0.249
Mass 0.972383 0.009598 101.307 1.01e-13 ***
---
Signif. codes: 0 ‘***’ 0.001 ‘**’ 0.01 ‘*’ 0.05 ‘.’ 0.1 ‘ ’ 1
Residual standard error: 0.008962 on 8 degrees of freedom
Multiple R-squared: 0.9992, Adjusted R-squared: 0.9991
F-statistic: 1.026e+04 on 1 and 8 DF, p-value: 1.007e-13
From these results we can see there is a significant relationship between mass and volume as you likely expected. To visualize this information we can create a plot of the data.
lm.coef<-round(coef(regression.results), 2)
plot(regression$Volume, regression$Mass, pch = 21, cex = 1.3, col = "black", bg="gray", main = "Volume vs Mass",xlab="Volume (mL)", ylab="Mass (g)")
abline(regression.results, col="red")
r2<-round(summary(regression.results)$r.squared,4)
mtext(bquote(y==.(lm.coef[2])*x+.(lm.coef[1])*","~~r^2==.(r2)*" "),line=-18,adj=1, padj=0)

You can see from this plot how closely mass follows volume as the r2 value for the model is approaching 1.0.
How To Get Coding
Hopefully this quick workshop gave you an idea of how quickly we can run statistical analyses using freely available software and packages in R using the RStudio IDE. As the job market and admission to graduate schools becomes more competitive, students need to find ways to help push their applications to the top of the pile. One way beyond your curriculum and grade point average is to at least a cursory knowledge of programming language(s). While there are a number of languages that would be beneficial, those that are adept at large scale data analysis are currently some of the most sought after. Two of the more popular languages for data science are Python and R. Each of them have their pros and cons so you should spend some time determing which would be most beneficial to you. There are a lot of website and YouTube videos dedicated to teaching these langauges, but there are also books that can help you get started. Check out…
R for Dummies or Python for Dummies
Each of these books will thoroughly cover the basics of each language and allow you to gain more confidence moving forward in your analyses. Just remember, like most other skills that you take time to develop, it pays to be consist as your skills will diminish the less engaged you are over time.
LS0tDQp0aXRsZTogIkJhc2ljIEFuYWx5c2lzIGluIFIgPGJyPjxzbWFsbD5BbiBJbnRyb2R1Y3Rpb24gZm9yIERhdGEgQW5hbHlzaXMgaW4gQ2hlbWlzdHJ5PC9zbWFsbD48L2JyPiINCmF1dGhvcjogIkNocmlzIEdlbnRyeSBhbmQgTGVzbGllIEhpYXR0Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOg0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIGhpZ2hsaWdodDogYnJlZXplZGFyaw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgcm93cy5wcmludDogMTANCiAgICB0aGVtZTogY29zbW8NCiAgICB0b2M6IHllcw0KICAgIHRvY19mbG9hdDoNCiAgICAgIGNvbGxhcHNlZDogbm8NCiAgICAgIHNtb290aF9zY3JvbGw6IHllcw0KICBodG1sX2RvY3VtZW50Og0KICAgIGRmX3ByaW50OiBwYWdlZA0KICAgIHRvYzogeWVzDQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KZWRpdG9yX29wdGlvbnM6DQogIGNodW5rX291dHB1dF90eXBlOiBpbmxpbmUNCi0tLQ0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4NCg0KaDEudGl0bGUgew0KICBmb250LXNpemU6IDQwcHg7DQogIGZvbnQtZmFtaWx5OiAiVGltZXMgTmV3IFJvbWFuIiwgVGltZXMsIHNlcmlmOw0KICBjb2xvcjogRGFya0JsdWU7DQogIHRleHQtYWxpZ246IGNlbnRlcjsNCn0NCmg0LmF1dGhvciB7IC8qIEhlYWRlciA0IC0gYW5kIHRoZSBhdXRob3IgYW5kIGRhdGEgaGVhZGVycyB1c2UgdGhpcyB0b28gICovDQogIGZvbnQtc2l6ZTogMjBweDsNCiAgZm9udC1mYW1pbHk6ICJUaW1lcyBOZXcgUm9tYW4iLCBUaW1lcywgc2VyaWY7DQogIGNvbG9yOiBEYXJrQmx1ZTsNCiAgdGV4dC1hbGlnbjogY2VudGVyOw0KfQ0KPC9zdHlsZT4NCg0KIyBXaGF0IGlzIFI/DQoNClIgaXMgYW4gb3Blbi1zb3VyY2Ugc3RhdGlzdGljYWwgbGFuZ3VhZ2UgYXZhaWxhYmxlIGZvciBbZnJlZSBkb3dubG9hZF0oaHR0cHM6Ly93d3cuci1wcm9qZWN0Lm9yZy8pIGFuZA0KbWFpbnRhaW5lZCBieSB0aGUgUiBDb25zb3J0aXVtLCBJbmMuIGFuZCB0aGUgd29ybGR3aWRlIGNvbW11bml0eSBvZg0KdXNlcnMsIG1haW50YWluZXJzLCBhbmQgZGV2ZWxvcGVycyBvZiBSIHNvZnR3YXJlLiBUaGlzIHdvcmtzaG9wIGlzIGZvciBkZW1vbnN0cmF0aW9uIHB1cnBvc2VzIG9ubHkgYW5kIGlzIG5vdCBtZWFudCB0byBwcm92aWRlIGFuIGV4aGF1c3RpdmUgZWR1Y2F0aW9uIGluIHRoZSBSIFByb2plY3QgZm9yIFN0YXRpc3RpY2FsIENvbXB1dGluZy4gVGhlIGV4YW1wbGVzIHVzZWQgaW4gdGhpcyB3b3Jrc2hvcCB3ZXJlIGRldmVsb3BlZCBwcmltYXJpbHkgZm9yIGVkdWNhdGlvbmFsIHB1cnBvc2VzIGFuZCBhcmUgYmVpbmcgcHJlc2VudGVkIGluIHRoaXMgZnJlZSB3b3Jrc2hvcCBhcyBhIGRlbW9uc3RyYXRpb24gb2Ygc29tZSBvZiB0aGUgY2FwYWJpbGl0aWVzIG9mIHRoaXMgb3BlbiBzb3VyY2UgbGFuZ3VhZ2UgYW5kIGVudmlyb25tZW50IGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgYW5kIGdyYXBoaWNzLiBUaHJvdWdob3V0IHRoaXMgd29ya3Nob3AgeW91IHdpbGwgc2VlIHNjcmVlbiBjYXB0dXJlcyBhbmQgaW1hZ2VzIGZyb20gdGhlIFJTdHVkaW8gUHJvZ3JhbS4gSXQgaXMgYW4gaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCAoSURFKSBmb3IgUiBtYWludGFpbmVkIGJ5IFJTdHVkaW8sIEluYy4gYW5kIGlzIGF2YWlsYWJsZSBpbiBbZnJlZSBvcGVuIHNvdXJjZV0oaHR0cHM6Ly9yc3R1ZGlvLmNvbS8pIGFuZCBjb21tZXJjaWFsIGVkaXRpb25zLiBBbGwgb2YgdGhlIGNvZGUgYW5kIGRhdGEgc291cmNlZCBmb3IgdGhpcyB3b3Jrc2hvcCB3YXMgZGV2ZWxvcGVkIG9yIG1vZGlmaWVkIGJ5IHRoZSBhdXRob3JzIGFuZCBoYXMgYmVlbiBtYWRlIGF2YWlsYWJsZSBmb3IgZWR1Y2F0aW9uYWwgdXNlIHRocm91Z2ggdGhlIGxpbmtzIHByb3ZpZGVkIGluIHRoaXMgd29ya2Jvb2suIEFkZGl0aW9uYWwgbWF0ZXJpYWxzIGluIHRoaXMgd29ya2Jvb2sgaGF2ZSBiZWVuIG1hZGUgZnJlZWx5IGF2YWlsYWJsZSB1bmRlciBDcmVhdGl2ZSBDb21tb25zIGxpY2Vuc2VzIGFuZCBkaXN0cmlidXRlZCBpbiBvcGVuLXNvdXJjZSBwbGF0Zm9ybXMuDQoNCiMgSW4gVGhlIEJlZ2lubmluZw0KDQpZb3UgbXVzdCBpbnN0YWxsIFIgYW5kIFJTdHVkaW8gb24geW91ciBQQywgTWFjLCBvciBMaW51eCBjb21wdXRlci4NClJTdHVkaW8gaXMgbm90IGEgc3RhbmQtYWxvbmUgcHJvZ3JhbSBzbyBwbGVhc2UgaW5zdGFsbCBSIGZpcnN0Og0KDQpSIERvd25sb2FkOiBodHRwczovL21pcnJvcnMubmljcy51dGsuZWR1L2NyYW4vIA0KDQpSU3R1ZGlvIERvd25sb2FkOiBodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9yc3R1ZGlvL2Rvd25sb2FkLw0KDQpSU3R1ZGlvIG9wZXJhdGVzIGFzIGEgcXVhc2ktZ3JhcGhpY2FsIHVzZXIgaW50ZXJmYWNlIChHVUkpIGZvciBSLiBSU3R1ZGlvIHR5cGljYWxseSBvcGVucyBpbiBhIHRocmVlIG9yIGZvdXIgd2luZG93IGRlc2lnbjoNCg0KPHAgYWxpZ249ImNlbnRlciI+DQohW1JTdHVkaW8gV2luZG93c10oLlxXb3JrYm9vayBHcmFwaGljc1wxIFJTdHVkaW8gNCBXaW5kb3cucG5nICJSU3R1ZGlvIFdpbmRvd3MiKQ0KPC9wPg0KDQpVcHBlciBMZWZ0IHwgVXBwZXIgUmlnaHQgfCBMb3dlciBMZWZ0IHwgTG93ZXIgUmlnaHQNCi0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tLSB8IC0tLS0tLS0tLS0gfCAtLS0tLS0tLS0tDQpTY3JpcHQvRGF0YSBXaW5kb3cgfCBIaXN0b3J5L0Vudmlyb25tZW50IHwgQ29kZS9Db25zb2xlIHwgUGFja2FnZXMsIEhlbHAsIGV0Yy4NCg0KU2NyaXB0L0RhdGEgV2luZG93OiBUaGlzIGFyZWEgbGV0cyB5b3UgZGV2ZWxvcCBzY3JpcHQgdG8gYmUgcnVuIGluIHRoZSBjb25zb2xlIHdpbmRvdyBhbmQgYWxzbyB2aWV3IGRhdGEgc2V0cy4NCg0KSGlzdG9yeS9FbnZpcm9ubWVudCBXaW5kb3c6IEluIHRoaXMgc2VjdGlvbiBvZiBSU3R1ZGlvIHlvdSBjYW4gc2VlIG9iamVjdHMgeW91IGhhdmUgaW1wb3J0ZWQgb3IgY3JlYXRlZCBhbmQgYSBoaXN0b3J5IG9mIHRoZSBjb2RlIHlvdSBoYXZlIHJ1bi4NCg0KQ29kZS9Db25zb2xlIFdpbmRvdzogQWxsIG9mIHRoZSBjb2RlIHlvdSBydW4gZnJvbSBzY3JpcHRzIHdpbGwgYXBwZWFyIGluIHRoZSBjb25zb2xlIHdpbmRvdyAoYWxvbmcgd2l0aCBhbnkgZXJyb3JzIGhpZ2hsaWdodGVkIGluIHJlZCkuIFlvdSBjYW4gYWxzbyBpbnB1dCBjb2RlIGRpcmVjdGx5IHRvIHRoaXMgYXJlYSwgaW5pdGlhdGUgaGVscCB3aW5kb3dzIG9uIHZhcmlvdXMgcGFja2FnZXMsIHZpZXcgb3B0aW9ucyBmb3IgY2VydGFpbiBjb21tYW5kcywgZXRjLg0KDQpGaWxlcywgUGxvdHMsIFBhY2thZ2VzLCBIZWxwLCBWaWV3ZXIgV2luZG93OiBJbiB0aGlzIGFyZWEgeW91IGNhbiBsb2FkIGFuZCB2aWV3IG9wdGlvbnMgd2l0aGluIHZhcmlvdXMgcGFja2FnZXMgKGEgY29sbGVjdGlvbiBvZiBmb3JtYXR0ZWQgZnVuY3Rpb25zLCBkYXRhLCBhbmQgY29tcGlsZWQgY29kZSB0aGF0IGltcHJvdmUgdGhlIGZ1bmN0aW9uYWxpdHkgb2YgdGhlIGJhc2UgUiBmdW5jdGlvbnMpLCB2aWV3IGFuZCBleHBvcnQgcGxvdHMvZ3JhcGhpY3MgdGhhdCBoYXZlIGJlZW4gY3JlYXRlZCwgdmlldyBoZWxwIG9wdGlvbnMgZm9yIHBhY2thZ2VzL2Z1bmN0aW9ucywgYW5kIHZpZXcgZmlsZXMgaW4geW91ciBjdXJyZW50IHdvcmtpbmcgZGlyZWN0b3J5IChleHBsYWluZWQgbGF0ZXIpLg0KDQojIyBUaGUgQmFzaWNzDQoNCldoZW4gYmVnaW5uaW5nIGFueSBwcm9qZWN0IGluIFIsIGl0IGNhbiBiZSBiZW5lZmljaWFsIHRvIHN0YXJ0IGEgbmV3IFIgU2NyaXB0LiBUaGlzIGNyZWF0ZXMgYSByZWNvcmQgb2YgdGhlIGNvZGUgeW91IGNyZWF0ZSB0aGF0IGNhbiBiZSBzYXZlZCBmb3IgZnV0dXJlIHJlZmVyZW5jZSBhbmQgc2hhcmluZy4gVG8gc3RhcnQgYSBzY3JpcHQgY2xpY2sgQ1JUTCtTaGlmdCtOIG9uIFBDLCBTaGlmdCvijJgrTiBvbiBNYWMsIG9yIHNlbGVjdCBSIFNjcmlwdCBmcm9tIHRoZSBkcm9wLWRvd24gbWVudSBiZWxvdyBGaWxlIGlvIHRoZSBtZW51IGJhci4NCg0KPHAgYWxpZ249ImNlbnRlciI+DQohW1NjcmlwdGluZyBXaW5kb3ddKC5cV29ya2Jvb2sgR3JhcGhpY3NcMiBOZXcgUiBTY3JpcHQucG5nICJTY3JpcHRpbmcgV2luZG93IikNCjwvcD4NCg0KSW4gdGhlIHNjcmlwdGluZyB3aW5kb3cgdGhlcmUgYXJlIHNvbWUgbGltaXRlZCBkZWJ1Z2dpbmcgZmVhdHVyZXMgdGhhdCBwcm92aWRlIHN1Z2dlc3Rpb25zIHJlZ2FyZGluZyBlcnJvcnMgaW4geW91ciBjb2RlLiBZb3UgY2FuIHNlZSBhbiBleGFtcGxlIG9mIHRoaXMgZmVhdHVyZSBpbiB0aGUgaW1hZ2UgYmVsb3cuDQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KIVtEZWJ1Z2dpbmddKC5cV29ya2Jvb2sgR3JhcGhpY3NcMyBSIFNjcmlwdCBEZWJ1Zy5wbmcgIkRlYnVnZ2luZyIpDQo8L3A+DQpIb3dldmVyLCB0aGVyZSBhcmUgYSBsb3Qgb2YgcmVzb3VyY2VzIGF2YWlsYWJsZSB0aHJvdWdoIHRoZSBoZWxwIHRhYiBpbiB0aGUgbG93ZXItcmlnaHQgd2luZG93IG9yIGJ5IGluY2x1ZGluZyBhID8gYmVmb3JlIGFueSBwYWNrYWdlIG9yIGZ1bmN0aW9uIHlvdSdyZSB0cnlpbmcgdG8gcnVuIHN1Y2ggYXMgYGBgP3NldHdkKClgYGAuIFRoZXJlIGFyZSBhbHNvIHRvbnMgb2YgcmVzb3VyY2VzIGF2YWlsYWJsZSB0aHJvdWdoIGdvb2dsZSBzZWFyY2hlcyBhbmQgWW91VHViZSB0dXRvcmlhbHMgdGhhdCB3YWxrIHlvdSBzdGVwLWJ5LXN0ZXAgdGhyb3VnaCB2YXJpb3VzIHBhY2thZ2VzIG9yIGFuYWx5c2VzLiBUaHJvdWdob3V0IHRoaXMgd29ya3Nob3Agd2Ugd2lsbCB1c2UgYSBudW1iZXIgb2YgZGlmZmVyZW50IHBhY2thZ2VzLiBUaGVzZSBhcmUgY29sbGVjdGlvbnMgb2YgUiBmdW5jdGlvbnMsIGRhdGEsIGFuZCBjb21waWxlZCBjb2RlIHRvIGhlbHAgcGVyZm9ybSBzcGVjaWZpYyB0YXNrcy4gV2hpbGUgUiBwcm92aWRlcyBzb21lIGJhc2ljIGZ1bmN0aW9ucywgcGFja2FnZXMgZXhwYW5kIHRoZSBwb3dlciBvZiBSIGFsbG93aW5nIHlvdSB0byBwZXJmb3JtIG1vcmUgYWR2YW5jZWQgZnVuY3Rpb25zIGFuZCBhbmFseXNlcy4gRm9yIGVhY2ggbmV3IHBhY2thZ2UgeW91IHdpbGwgbmVlZCB0byB1c2UgdGhlIGZ1bmN0aW9uIGBgYGluc3RhbGwucGFja2FnZXMoLi4uKWBgYCB3aXRoIHRoZSBuYW1lIG9mIHRoZSBwYWNrYWdlIGJldHdlZW4gdGhlIHBhcmVudGhlc2lzIHRvIGRvd25sb2FkIGFuZCBgYGBsaWJyYXJ5KC4uLilgYGAgdG8gbG9hZCB0aGUgcGFja2FnZSBpbnRvIHlvdXIgd29ya2luZyBlbnZpcm9ubWVudC4gWW91IG9ubHkgbmVlZCB0byBkb3dubG9hZCB0aGUgcGFja2FnZSBvbmNlLCBidXQgeW91IHdpbGwgbmVlZCB0byBsb2FkIHRoZSBwYWNrYWdlIHdpdGggZWFjaCBuZXcgcHJvamVjdC4NCg0KIyBHZXR0aW5nIFN0YXJ0ZWQNCg0KVG8gc3RhcnQgeW91IHdhbnQgdG8gZWl0aGVyIGJlZ2luIHdpdGggYSBuZXcgcHJvamVjdCBvciBlc3RhYmxpc2ggeW91ciB3b3JraW5nIGRpcmVjdG9yeS4gSWYgeW91IGFyZSBwbGFubmluZyBvbiB0aGUgYW5hbHlzaXMgYmVpbmcgcGFydCBvZiBhIGxhcmdlIHByb2plY3Qgb3IgdGhhdCB5b3UgcGxhbiB0byBzaGFyZSB3aXRoIG90aGVyIHJlc2VhcmNoZXJzLCBpdCB3b3VsZCBiZSBpZGVhbCB0byBiZWdpbiB3aXRoIGEgbmV3IHByb2plY3QgZGlyZWN0b3J5IHVzaW5nIHZlcnNpb24gY29udHJvbC4gSG93ZXZlciwgaWYgeW91IGFyZSBsb29raW5nIHRvIGNvbXBsZXRlIGEgcXVpY2sgYW5hbHlzaXMgdG8gd29ya2luZyBvbiBhIGNsYXNzIGV4ZXJjaXNlIGl0IG1pZ2h0IGJlIHN1ZmZpY2llbnQgZW5vdWdoIHRvIGp1c3QgY3JlYXRlIHcgd29ya2luZyBkaXJlY3RvcnkuIFRoaXMgZGlyZWN0b3J5IGlzIHdoZXJlIGFsbCBvZiB0aGUgZGF0YXNldHMgdXNlZCBmb3IgdGhlIHByb2plY3Qgc2hvdWxkIGJlIHN0b3JlZCBhbmQgd2hlcmUgYWxsIG9mIHRoZSBvdXRwdXQgZGF0YSwgcGxvdHMsIGV0Yy4gd2lsbCBiZSBzdG9yZWQgYnkgZGVmYXVsdC4gU2V0IHRoaXMgZGlyZWN0b3J5IGJ5IHVzaW5nIHRoZSBmb2xsb3dpbmcgc2NyaXB0Og0KDQo8cCBhbGlnbj0iY2VudGVyIj4NCioqc2V0d2QoIi4uLiIpKioNCjwvcD4NCg0KQmV0d2VlbiB0aGUgcGFyZW50aGVzZXMgYW5kIHF1b3RlcyB5b3Ugc2hvdWxkIGlucHV0IHRoZSBmb2xkZXIgcGF0aCBmcm9tIHlvdXIgY29tcHV0ZXIuIFRoaXMgY2FuIGJlIG9idGFpbmVkIGJ5IHVzaW5nIHdpbmRvd3MgZXhwbG9yZXIgb24gYSBQQywgQ29tbWFuZCvijJggb24gYSBNYWMsIG9yIGZpbmQgaW4gTGludXguIE9uZSB0aGluZyB0byBub3RlIHdpdGggUiwgcGF0aCBuYW1lcyB1c2UgZm9yd2FyZCBzbGFzaCBhbmQgbm90IHRoZSBiYWNrc2xhc2ggd2hpY2ggaXMgY29tbW9uIHRvIHdpbmRvd3MuIEhlcmUgaXMgYW4gZXhhbXBsZSBvZiBhIGNvcnJlY3QgYGBgc2V0d2Qo4oCcLi4u4oCdKWBgYCBzY3JpcHQ6DQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KIVtTZXQgd2RdKC5cV29ya2Jvb2sgR3JhcGhpY3NcNCBTZXR3ZC5wbmcgIldvcmtpbmcgRGlyZWN0b3J5IikNCjwvcD4NCg0KT25jZSB5b3UgaGF2ZSB0aGUgc2NyaXB0IHdyaXR0ZW4gaW4gdGhlIHNjcmlwdGluZyB3aW5kb3csIHNlbGVjdCB0aGUgbGluZSBhbmQgY2xpY2sgUnVuIHRvIGluaXRpYXRlIHRoZSBzY3JpcHQuIE5vdGljZSB3aGVuIHlvdSBzZXQgdGhlIHdvcmtpbmcgZGlyZWN0b3J5LCB0aGF0IGluZm9ybWF0aW9uIGFsc28gYXBwZWFycyBhdCB0aGUgdG9wIG9mIGFuZCBpbiB0aGUgQ29uc29sZS9Db2RlIHdpbmRvdzoNCg0KPHAgYWxpZ249ImNlbnRlciI+DQohW1NldCB3ZCBhbmQgY29uc29sZV0oLlxXb3JrYm9vayBHcmFwaGljc1w1IFNldHdkMi5wbmcgIldvcmtpbmcgRGlyZWN0b3J5ICsgQ29uc29sZSIpDQo8L3A+DQoNCldpdGggdGhlIHdvcmtpbmcgZGlyZWN0b3J5IHNldCwgaXQgaXMgdGltZSB0byBpbXBvcnQgeW91ciBkYXRhIGZvciBhbmFseXNpcy4NCg0KIyMgSW1wb3J0aW5nIERhdGENCg0KVG8gYmVnaW4sIHlvdSB3aWxsIG5lZWQgdG8gaW1wb3J0IGEgZGF0YXNldChzKSBpbiB0byB5b3VyIHdvcmtpbmcgZW52aXJvbm1lbnQuIEFzIHdpdGggYWxsIHRoaW5ncyBpbiBSIGFuZCBSU3R1ZGlvLCB0aGVyZSBhcmUgYSBudW1iZXIgb2YgZGlmZmVyZW50IHdheXMgdG8gY29tcGxldGUgdmFyaW91cyB0YXNrcy4gVGhpcyBpbmNsdWRlcyB1c2luZyBhIHJlYWQgZnVuY3Rpb24gaW4gUiwgdXNpbmcgdGhlIGltcG9ydCBmZWF0dXJlIGluIFJTdHVkaW8sIG9yIHVzaW5nIHNjcmlwdHMgdG8gY3JlYXRlIHRoZSBkYXRhLiBGb3IgdGhlIHB1cnBvc2Ugb2YgdGhpcyB3b3Jrc2hvcCB3ZSBiZWdpbiBieSB1c2luZyBhIG11bHRpLXRhYiBleGNlbCBmaWxlLiANCg0KPHAgYWxpZ249ImNlbnRlciI+DQohW0V4Y2VsIERhdGFzZXRdKC5cV29ya2Jvb2sgR3JhcGhpY3NcRXhhbXBsZSBFeGNlbC5wbmcgIkV4Y2VsIERhdGFzZXQiKQ0KPC9wPg0KDQpTbyB3ZSB3aWxsIG5lZWQgdG8gYWRkIGEgc3BlY2lmaWMgcGFja2FnZSBjYWxsZWQgYGBgcmVhZHhsYGBgIGFuZCB0byBkbyB0aGF0IHdlIHdpbGwgdXNlIHRoZSBmb2xsb3dpbmcgc2NyaXB0IHRvIG9idGFpbiwgaW5zdGFsbCwgYW5kIGxvYWQgdGhlIHBhY2thZ2U6DQoNCmBgYHtyIGxvYWRpbmcgbGlicmFyaWVzLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUV9DQpsaWJyYXJ5KHJlYWR4bCkNCmBgYA0KDQpJZiBzdWNjZXNzZnVsIHlvdSBzaG91bGQgbm93IGhhdmUgdGhlIHBhY2thZ2UgbGlzdGVkIHVuZGVyICpQYWNrYWdlcyogaW4gdGhlIHBhY2thZ2VzIHRhYiBhbmQgaXQgc2hvdWxkIGJlIGNoZWNrZWQuIFRvIGdldCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgcGFja2FnZSB5b3UgYW5kIHR5cGUgP3JlYWR4bCBpbnRvIHRoZSBjb25zb2xlIG9yIGNsaWNrIG9uIHRoZSBwYWNrYWdlIG5hbWUgaW4gdGhlIHBhY2thZ2VzIHRhYi5XZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSBgYGByZWFkX2V4Y2VsKClgYGAgZnVuY3Rpb24gdG8gbG9hZCBlYWNoIHRhYiBpbmRpdmlkdWFsbHkgYXMgb2JqZWN0cyBpbiBvdXIgd29ya2luZyBlbnZpcm9ubWVudC4gVG8gbGVhcm4gbW9yZSBhYm91dCB0aGUgZnVuY3Rpb24gYGBgP3JlYWRfZXhjZWxgYGAgdHlwZSBpdCBpbiB0aGUgY29uc29sZS4NCg0KYGBge3IgaW1wb3J0aW5nIGRhdGEsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRX0NCmJhc2ljX3N0YXRzIDwtIHJlYWRfZXhjZWwoIkNoZW1EYXRhLnhsc3giLCBzaGVldD0iY2VudHJhbCB0ZW5kZW5jeSIpDQp0dGVzdCA8LSByZWFkX2V4Y2VsKCJDaGVtRGF0YS54bHN4Iiwgc2hlZXQ9InQtdGVzdCIpDQphbm92YSA8LSByZWFkX2V4Y2VsKCJDaGVtRGF0YS54bHN4Iiwgc2hlZXQ9ImFub3ZhIikNCmNvcnJlbGF0aW9uIDwtIHJlYWRfZXhjZWwoIkNoZW1EYXRhLnhsc3giLCBzaGVldD0iY29ycmVsYXRpb25zIikNCnJlZ3Jlc3Npb24gPC0gcmVhZF9leGNlbCgiQ2hlbURhdGEueGxzeCIsc2hlZXQgPSAicmVncmVzc2lvbiIpDQpgYGANCg0KV2hlbiBjb21wbGV0ZSB5b3Ugc2hvdWxkIGhhdmUgYSBsaXN0IG9mIGZpdmUgKDUpIGRhdGFzZXRzIGluIHlvdXIgd29ya2luZyBlbnZpcm9ubWVudC4gQnkgY2xpY2tpbmcgb24gdGhlIGFycm93IGluIHRoZSBibHVlIGNpcmNsZSBvbiB0aGUgbGVmdCBvZiB0aGUgb2JqZWN0IG5hbWUgeW91IGNhbiBleHBhbmQgdGhlIGRhdGFzZXQgdG8gc2VlIHdoYXQgc29ydCBvZiBpbmZvcm1hdGlvbiBpdCBjb250YWlucy4gQnkgY2xpY2tpbmcgdGhlIHNoZWV0IGljb24gb24gdGhlIGZhciByaWdodCB5b3UgY2FuIG9wZW4gdGhlIGRhdGEgdG8gdmlldyBpdHMgY29udGVudHMuDQoNCjxwIGFsaWduPSJjZW50ZXIiPg0KIVtEYXRhIEVudmlyb25tZW50XSguXFdvcmtib29rIEdyYXBoaWNzXERhdGEgRW52aXJvbm1lbnQucG5nICJEYXRhIEVudmlyb25tZW50IikNCjwvcD4NCg0KIyMgRGF0YSBGb3JtYXR0aW5nIGFuZCBBbmFseXNpcw0KDQpJZiB5b3UgZXhhbWluZSB0aGUgKipiYXNpY19zdGF0cyoqIGRhdGFzZXQgeW91IHdpbGwgc2VlIGNvbHVtbnMgbGFiZWxlZCBObyB3aGljaCByZWZlcnMgdG8gdGhlIHRyaWFsIG51bWJlciBhbmQgdGhlbiBhIGNvbHVtbiBmb3IgZWFjaCBzdHVkZW50IGluIGEgY2xhc3MuIEJlY2F1c2Ugd2Ugd2FudCBvdXIgZGF0YXNldCB0byBjb250YWluIG9ubHkgbnVtZXJpY2FsIHZhbHVlcyB3ZSBjYW4gdXNlIFIgdG8gcXVpY2tseSBmb3JtYXQgdGhlIGRhdGFzZXQgYW5kIHJlbW92ZSB0aGUgZmlyc3QgY29sdW1uIG9mIGRhdGEgdXNpbmcgdGhlIHJvdyBudW1iZXJzIGluc3RlYWQgdG8gaWRlbnRpZnkgdGhlIHRyYWlscy4NCg0KPHAgYWxpZ249ImNlbnRlciI+DQohW0Zvcm1hdCBSb3dzXSguXFdvcmtib29rIEdyYXBoaWNzXEZvcm1hdCBSb3dzLnBuZyAiRm9ybWF0IFJvd3MiKQ0KPC9wPg0KDQpUbyBwZXJmb3JtIHRoaXMgYWN0aW9uIHdlIHdpbGwgdXNlIHRoZSBmb2xsb3dpbmcgY29tbWFuZDoNCg0KYGBge3IgZm9ybWF0dGluZyBkYXRhLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpiYXNpY19zdGF0c1ssMV0gPC0gTlVMTA0KYGBgDQoNCipiYXNpY19zdGF0cyogcmVmZXJzIHRvIHRoZSBkYXRhc2V0IGFuZCBbLDFdIHJlZmVycyB0byB0aGUgZmlyc3QgY29sdW1uLiBJZiB3ZSB3YW50ZWQgdG8gcmVtb3ZlIHJvd3MgaW4gYSB2ZWN0b3Igb3IgbWF0cml4IGRhdGFzZXQgd2Ugd291bGQgaGF2ZSB1c2VkIFsxLF0gaW5zdGVhZC4gVXNpbmcgdGhlIGBgYGhlYWQoKWBgYCBmdW5jdGlvbiB3ZSBjYW4gbm93IHZpZXcgdGhlIGZpcnN0IGZldyByb3dzIG9mIG91ciBuZXdseSBmb3JtYXR0ZWQgZGF0YS4NCg0KYGBge3J9DQpoZWFkKGJhc2ljX3N0YXRzKQ0KYGBgDQoNCiMjIyBTdW1tYXJ5IFN0YXRpc3RpY3MNCg0KTm93IHdlIGNhbiB1c2UgdGhlIGRhdGFzZXQgdG8gZXhhbWluZSBzb21lIGJhc2ljIHN0YXRpc3RpY3Mgc3VjaCBhcyBtZWFuLCBtZWRpYW4sIHN0YW5kYXJkIGRldmlhdGlvbiAoc2QpLCB2YXJpYW5jZSAodmFyKSwgbWluLCBtYXgsIG1lZGlhbiwgcmFuZ2UsIGFuZCBxdWFudGlsZSBvciB3ZSBjYW4gc2VlIGEgc3VtbWFyeSBvZiB0aGUgZGF0YS4gDQoNCmBgYHtyIGRhdGEgc3VtbWFyeSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0Kc3VtbWFyeShiYXNpY19zdGF0cykNCmBgYA0KDQpJZiB3ZSB3YW50IHRvIGV4YW1pbmUgc3RhdGlzdGljcyBub3QgaW5jbHVkZWQgaW4gdGhlIHN1bW1hcnkgYWJvdmUgeW91IGNhbiB1c2UgdGhlIHNjcmlwdCBgYGBzYXBwbHkoYmFzaWNfc3RhdHMsIHNkKWBgYCBhbmQgc3Vic3RpdHV0ZSB0aGUgKnNkKiBmb3IgYW55IG9mIHRoZSBzdGF0aXN0aWNzIGxpc3RlZCBhYm92ZS4gVGhpcyBjb21tYW5kIGFwcGxpZXMgdGhlIHNhbWUgZnVuY3Rpb24sIHN0YW5kYXJkIGRldmlhdGlvbiBpbiB0aGlzIGV4YW1wbGUsIGZvciB2YWx1ZXMgYWNyb3NzIGEgZGF0YXNldCwgaW4gdGhpcyBjYXNlIGJhc2ljX3N0YXRzLg0KDQpgYGB7ciBzdGFuZGFyZCBkZXZpYXRpb24sIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnNhcHBseShiYXNpY19zdGF0cywgc2QpDQpgYGANCg0KIyMjIFBhaXJlZCB0LXRlc3QNCg0KRm9yIHRoaXMgbmV4dCBhbmFseXNpcyB3ZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSAqKnR0ZXN0KiogZGF0YXNldCB0byBydW4gYSBwYWlyZWQgdC10ZXN0IHNvIHdlIGNhbiBjYWxjdWxhdGUgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiBwYWlyZWQgb2JzZXJ2YXRpb25zLiBUbyBkbyB0aGlzIHdlIGFyZSBnb2luZyB0byB1c2UgdGhlIGBgYCRgYGAgb3BlcmF0b3IgdG8gcG9pbnQgdG8gc3BlY2lmaWMgdmFsdWVzIGluIG91ciBkYXRhc2V0LiBUaGlzIG9wZXJhdG9yIGNhbiBiZSB1c2VkIGluIGEgbnVtYmVyIG9mIGRpZmZlcmVudCBkYXRhIGZvcm1hdHMgdG8gbmFycm93IGRvd24gd2hpY2ggaW5mb3JtYXRpb24gd2lsbCBiZSB1c2VkIGluIGEgY2FsY3VsYXRpb24uIFRvIHBlcmZvcm0gdGhlIHQtdGVzdCB3ZSB3aWxsIHVzZSB0aGUgZm9sbG93aW5nIHNjcmlwdDoNCg0KYGBge3IgdHRlc3QsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnQudGVzdCh0dGVzdCRTdHVkZW50X0EsdHRlc3QkU3R1ZGVudF9CLCBwYWlyZWQgPSBUUlVFKQ0KYGBgDQoNCldlIGNhbiB2YXJ5IHRoZSBpbnB1dCB0byBwZXJmb3JtIHZhcmlhdGlvbnMgb24gdGhlIHR0ZXN0IHN1Y2ggYXMgKipPbmUgU2FtcGxlIHQtdGVzdCoqIGBgYHQudGVzdCh0dGVzdClgYGAgb3IgYSAqKldlbGNoIFR3byBTYW1wbGUgdC10ZXN0KiogYGBgdC50ZXN0KHR0ZXN0JFN0dWRlbnRfQSx0dGVzdCRTdHVkZW50X0IsIHBhaXJlZCA9IEZBTFNFKWBgYC4gTW9yZSBpbmZvcm1hdGlvbiBvbiB0aGlzIGZ1bmN0aW9uIGNhbiBiZSBmb3VuZCBhdCBgYGA/dC50ZXN0YGBgIA0KDQpXZSBjYW4gYWxzbyB1c2UgYSBib3hwbG90IHRvIGdyYXBoIG91dCB0aGUgdmFsdWVzIGluIG91ciBkYXRhc2V0LiBUaGlzIGNhbiBiZSB1c2VkIGFzIGEgZGlhZ25vc3RpYyBzdGVwIHRvIGhlbHAgdXMgY29tcGFyZSB0d28gdmFyaWFibGVzLiBUbyBjcmVhdGUgYSBib3hwbG90IHdlIHVzZSB0aGUgZm9sbG93aW5nOg0KDQpgYGB7ciBib3hwbG90LCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpib3hwbG90KHR0ZXN0JFN0dWRlbnRfQSx0dGVzdCRTdHVkZW50X0IpDQpgYGANCg0KV2hhdCBoeXBvdGhlc2lzIHdvdWxkIHlvdSBoYXZlIG1hZGUgcmVnYXJkaW5nIHRoZSB0LXRlc3QgYWZ0ZXIgc2VlaW5nIHRoZSByZXN1bHRzIG9mIHRoZSBib3ggcGxvdD8NCg0KIyMjIEFOT1ZBDQoNCkFuIEFuYWx5c2lzIG9mIFZhcmlhbmNlIChBTk9WQSkgaXMgYSBwb3dlcmZ1bCB0b29sIGZvciBleGFtaW5pbmcgdGhlIGRpZmZlcmVuY2VzIGFtb25nIGdyb3VwIG1lYW5zIGluIGEgc2FtcGxlLiBUbyBydW4gdGhpcyBhbmFseXNpcyB3ZSB3aWxsIHVzZSB0aGUgKiphbm92YSoqIGRhdGFzZXQgdG8gZXhhbWluZSBhbGwgb2YgdGhlIHN0dWRlbnRzIHRyaWFscy4gSW4gb3JkZXIgdG8gZXhhbWluZSB0aGUgZGlmZmVyZW5jZXMgYW1vbmcgdGhlIGdyb3VwcyB3ZSB3aWxsIHVzZSBhIFR1a2V5J3MgSFNEIChob25lc3RseSBzaWduaWZpY2FudCBkaWZmZXJlbmNlKSB0ZXN0LiBIb3dldmVyLCBiZWNhdXNlIHRoZSBvdXRwdXQgdGFibGVzIGNhbiBiZWNvbWUgcXVpdGUgbGFyZ2UsIHdlIGFyZSBnb2luZyB0byB1c2UgdHdvIGFkZGl0aW9uYWwgcGFja2FnZXMgdG8gaGVscCBvcmdhbml6ZSB0aGUgZGF0YS4gVGhlc2UgYXJlIHRoZSBgYGB0aWR5dmVyc2VgYGAgYW5kIGBgYGJyb29tYGBgIHBhY2thZ2VzLg0KDQpgYGB7ciBhbm92YSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KbGlicmFyeShicm9vbSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KYW5vdmEucmVzdWx0cyA8LSBhb3YoUXVhbnRpdHl+R3JvdXAsZGF0YT1hbm92YSkNCnN1bW1hcnkoYW5vdmEucmVzdWx0cykNCmBgYA0KDQpGcm9tIG91ciBhbmFseXNpcyB3ZSBjYW4gc2VlIHRoZXJlIGlzIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiB0aGUgYW1vdW50IG9mIGxpcXVpZCAocXVhbnRpdHkpIGJlaW5nIHBpcGV0dGVkIGJ5IGVhY2ggc3R1ZGVudCAoZ3JvdXApLiBIb3dldmVyLCB3ZSBzdGlsbCBuZWVkIHRvIHJ1biB0aGUgcG9zdC1ob2MgdGVzdCB0byBkZXRlcm1pbmUgZXhhY3RseSB3aGVyZSB0aG9zZSBzaWduaWZpY2FudCBkaWZmZXJlbmNlcyBhcmUgd2l0aGluIHRoZSBkYXRhLiBUbyBkbyB0aGF0IHdlIGFyZSBnb2luZyB0byB1c2UgYSBmdW5jdGlvbiBmcm9tIGJyb29tIGNhbGxlZCBgYGB0aWR5YGBgIHRvIGhlbHAgZm9ybWF0IHRoZSB0YWJsZSB0aGF0IHJlc3VsdHMgZnJvbSB0aGUgYGBgVHVrZXRIU0RgYGAgZnVuY3Rpb24uDQoNCmBgYHtyIFR1a2V5cywgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KdGlkeShUdWtleUhTRChhbm92YS5yZXN1bHRzKSkNCmBgYA0KDQpZb3UgY2FuIHVzZSB0aGUgYXJyb3cgaW4gdGhlIGhlYWRlciByb3cgdG8gbW92ZSB0aGUgdGFibGUgdG8gc2VlIGNvbHVtbnMgdG8gdGhlIHJpZ2h0IG9yIHRoZSBudW1iZXJzIGF0IHRoZSBib3R0b20gdG8gc2VlIGFkZGl0aW9uYWwgcm93cy4gVGhlIHJlc3VsdGluZyB0YWJsZSBoYXMgMTcxIHJvd3Mgb2YgZGF0YS4gU29tZSBvZiB3aGljaCBoYXZlIHJlc3VsdHMgdGhhdCBhcmUgc2lnbmlmaWNhbnQgYW5kIHNvbWUgYXJlIG5vdC4gU28gd2UgY2FuIGluc3RlYWQgdXNlIHRoZSBmaWx0ZXIgZnVuY3Rpb24gZnJvbSB0aGUgKmRwbHlyKiBwYWNrYWdlIHRvIHNlbGVjdCBvbmx5IHRob3NlIGNvbXBhcmlzb25zIHdoZXJlIHRoZSBhZGp1c3QgcC12YWx1ZSBpcyBsZXNzIHRoYW4gMC4wNS4NCg0KYGBge3IgRmlsdGVyZWQgVHVrZXlzLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp0aWR5KFR1a2V5SFNEKGFub3ZhLnJlc3VsdHMpKSAlPiUgZmlsdGVyKGFkai5wLnZhbHVlPDAuMDUpDQpgYGANCg0KVGhpcyBoZWxwcyByZWR1Y2UgdGhlIHRhYmxlIHRvIHRoZSA0NiBjb21wYXJpc29ucyB0aGF0IGhhdmUgc2lnbmlmaWNhbnQgcC12YWx1ZXMuIFJlbWVtYmVyIHlvdSBjYW4gdXNlIHRoZSBhcnJvdyB0byBzZWUgY29sdW1ucyB0byB0aGUgcmlnaHQgb3IgdGhlIG51bWJlcnMgdG8gc2VlIGFkZGl0aW9uYWwgcm93cy4gV2UgY2FuIHVzZSB0aGUgYGBgZ2dwbG90MmBgYCBwYWNrYWdlIHRvIG1ha2UgYSBwbG90IG9mIHRoZSBkYXRhIGFuZCBpZGVudGlmeSBvdXRsaWVycyBpbiB0aGUgZGF0YXNldC4NCg0KYGBge3IgYW5vdmEgcGxvdCwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD05fQ0KbGlicmFyeShnZ3Bsb3QyKQ0KZ2dwbG90KGFub3ZhLnJlc3VsdHMsIGFlcyh4ID0gR3JvdXAsIHkgPSBRdWFudGl0eSkpICsgDQogIGdlb21fYm94cGxvdChjb2xvdXIgPSAiYmxhY2siLCBvdXRsaWVyLmNvbG91cj0icmVkIiwgb3V0bGllci5zaGFwZSA9IDgsICAgb3V0bGllci5zaXplID0gMiwgZmF0dGVuID0gMSkgKyANCiAgc2NhbGVfeF9kaXNjcmV0ZShsaW1pdHMgPSBjKCdTdHVkZW50XzInLCdTdHVkZW50XzMnLCdTdHVkZW50XzQnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1N0dWRlbnRfNScsJ1N0dWRlbnRfNicsJ1N0dWRlbnRfNycsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU3R1ZGVudF84JywnU3R1ZGVudF85JywnU3R1ZGVudF8xMCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU3R1ZGVudF8xMScsJ1N0dWRlbnRfMTInLCdTdHVkZW50XzEzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdTdHVkZW50XzE0JywnU3R1ZGVudF8xNScsJ1N0dWRlbnRfMTYnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ1N0dWRlbnRfMTcnLCdTdHVkZW50XzE4JywnU3R1ZGVudF8xOScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU3R1ZGVudF8yMCcpKSArDQogIGxhYnMoeD0iU3R1ZGVudHMiLCB5PSJWb2x1bWUgKG1MKSIsIHRpdGxlPSJCb3ggUGxvdCBvZiBNZWFucyIpICsNCiAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdD0wLjUsIGZhY2U9ImJvbGQiKSkgKyANCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgdmp1c3QgPSAwLjUpKQ0KYGBgDQoNCkluIHRoaXMgcGxvdCBjYW4geW91IGlkZW50aWZ5IG91dGxpZXJzIChyZWQgc3ltYm9scykgaW4gdGhlIGRhdGFzZXQ/DQoNCiMjIyBSZWdyZXNzaW9uDQoNCllvdSBjYW4gYWxzbyBleGFtaW5lIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGUgdm9sdW1lIGFuZCBtYXNzIG9mIGVhY2ggdHJpYWwgaW4gdGhlIGRhdGEuIFRvIGRvIHRoaXMgd2Ugd2lsbCBydW4gYW5kIHBsb3QgYSBsaW5lYXIgcmVncmVzc2lvbiAgd2l0aCBtYXNzIGFzIGRlcGVuZGVudCB2YXJpYWJsZSBhbmQgdm9sdW1lIGFzIHRoZSBpbmRlcGVuZGVudCBwcmVkaWN0b3IuDQoNCmBgYHtyIHJlZ3Jlc3Npb24sIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCnJlZ3Jlc3Npb24ucmVzdWx0cyA8LSBsbShWb2x1bWV+TWFzcywgZGF0YSA9IHJlZ3Jlc3Npb24pDQpzdW1tYXJ5KHJlZ3Jlc3Npb24ucmVzdWx0cykNCmBgYA0KDQpGcm9tIHRoZXNlIHJlc3VsdHMgd2UgY2FuIHNlZSB0aGVyZSBpcyBhIHNpZ25pZmljYW50IHJlbGF0aW9uc2hpcCBiZXR3ZWVuIG1hc3MgYW5kIHZvbHVtZSBhcyB5b3UgbGlrZWx5IGV4cGVjdGVkLiBUbyB2aXN1YWxpemUgdGhpcyBpbmZvcm1hdGlvbiB3ZSBjYW4gY3JlYXRlIGEgcGxvdCBvZiB0aGUgZGF0YS4NCg0KYGBge3IgcmVncmVzc2lvbiBwbG90LCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD05LCBmaWcuaGVpZ2h0PTZ9DQpsbS5jb2VmPC1yb3VuZChjb2VmKHJlZ3Jlc3Npb24ucmVzdWx0cyksIDIpDQpwbG90KHJlZ3Jlc3Npb24kVm9sdW1lLCByZWdyZXNzaW9uJE1hc3MsIHBjaCA9IDIxLCBjZXggPSAxLjMsIGNvbCA9ICJibGFjayIsIGJnPSJncmF5IiwgbWFpbiA9ICJWb2x1bWUgdnMgTWFzcyIseGxhYj0iVm9sdW1lIChtTCkiLCB5bGFiPSJNYXNzIChnKSIpDQphYmxpbmUocmVncmVzc2lvbi5yZXN1bHRzLCBjb2w9InJlZCIpDQpyMjwtcm91bmQoc3VtbWFyeShyZWdyZXNzaW9uLnJlc3VsdHMpJHIuc3F1YXJlZCw0KQ0KbXRleHQoYnF1b3RlKHk9PS4obG0uY29lZlsyXSkqeCsuKGxtLmNvZWZbMV0pKiIsIn5+cl4yPT0uKHIyKSoiICIpLGxpbmU9LTE4LGFkaj0xLCBwYWRqPTApDQpgYGANCg0KWW91IGNhbiBzZWUgZnJvbSB0aGlzIHBsb3QgaG93IGNsb3NlbHkgbWFzcyBmb2xsb3dzIHZvbHVtZSBhcyB0aGUgcl4yXiB2YWx1ZSBmb3IgdGhlIG1vZGVsIGlzIGFwcHJvYWNoaW5nIDEuMC4NCg0KIyBIb3cgVG8gR2V0IENvZGluZw0KDQpIb3BlZnVsbHkgdGhpcyBxdWljayB3b3Jrc2hvcCBnYXZlIHlvdSBhbiBpZGVhIG9mIGhvdyBxdWlja2x5IHdlIGNhbiBydW4gc3RhdGlzdGljYWwgYW5hbHlzZXMgdXNpbmcgZnJlZWx5IGF2YWlsYWJsZSBzb2Z0d2FyZSBhbmQgcGFja2FnZXMgaW4gUiB1c2luZyB0aGUgUlN0dWRpbyBJREUuIEFzIHRoZSBqb2IgbWFya2V0IGFuZCBhZG1pc3Npb24gdG8gZ3JhZHVhdGUgc2Nob29scyBiZWNvbWVzIG1vcmUgY29tcGV0aXRpdmUsIHN0dWRlbnRzIG5lZWQgdG8gZmluZCB3YXlzIHRvIGhlbHAgcHVzaCB0aGVpciBhcHBsaWNhdGlvbnMgdG8gdGhlIHRvcCBvZiB0aGUgcGlsZS4gT25lIHdheSBiZXlvbmQgeW91ciBjdXJyaWN1bHVtIGFuZCBncmFkZSBwb2ludCBhdmVyYWdlIGlzIHRvIGF0IGxlYXN0IGEgY3Vyc29yeSBrbm93bGVkZ2Ugb2YgcHJvZ3JhbW1pbmcgbGFuZ3VhZ2UocykuIFdoaWxlIHRoZXJlIGFyZSBhIG51bWJlciBvZiBsYW5ndWFnZXMgdGhhdCB3b3VsZCBiZSBiZW5lZmljaWFsLCB0aG9zZSB0aGF0IGFyZSBhZGVwdCBhdCBsYXJnZSBzY2FsZSBkYXRhIGFuYWx5c2lzIGFyZSBjdXJyZW50bHkgc29tZSBvZiB0aGUgbW9zdCBzb3VnaHQgYWZ0ZXIuIFR3byBvZiB0aGUgbW9yZSBwb3B1bGFyIGxhbmd1YWdlcyBmb3IgZGF0YSBzY2llbmNlIGFyZSBQeXRob24gYW5kIFIuIEVhY2ggb2YgdGhlbSBoYXZlIHRoZWlyIHByb3MgYW5kIGNvbnMgc28geW91IHNob3VsZCBzcGVuZCBzb21lIHRpbWUgZGV0ZXJtaW5nIHdoaWNoIHdvdWxkIGJlIG1vc3QgYmVuZWZpY2lhbCB0byB5b3UuIFRoZXJlIGFyZSBhIGxvdCBvZiB3ZWJzaXRlIGFuZCBZb3VUdWJlIHZpZGVvcyBkZWRpY2F0ZWQgdG8gdGVhY2hpbmcgdGhlc2UgbGFuZ2F1Z2VzLCBidXQgdGhlcmUgYXJlIGFsc28gYm9va3MgdGhhdCBjYW4gaGVscCB5b3UgZ2V0IHN0YXJ0ZWQuIENoZWNrIG91dC4uLg0KDQpbUiBmb3IgRHVtbWllc10oaHR0cDovL3NncHdlLml6dC51YW0ubXgvZmlsZXMvdXNlcnMvdWFtaS9nbWEvUl9mb3JfZHVtbWllcy5wZGYpDQpvcg0KW1B5dGhvbiBmb3IgRHVtbWllc10oaHR0cHM6Ly9naXRodWIuY29tL1NoYXJtYU5hdGFzaGEvQm9va3MvYmxvYi9tYXN0ZXIvQmVnaW5uaW5nJTIwUHJvZ3JhbW1pbmclMjB3aXRoJTIwUHl0aG9uJTIwZm9yJTIwRHVtbWllcy5wZGYpIA0KDQpFYWNoIG9mIHRoZXNlIGJvb2tzIHdpbGwgdGhvcm91Z2hseSBjb3ZlciB0aGUgYmFzaWNzIG9mIGVhY2ggbGFuZ3VhZ2UgYW5kIGFsbG93IHlvdSB0byBnYWluIG1vcmUgY29uZmlkZW5jZSBtb3ZpbmcgZm9yd2FyZCBpbiB5b3VyIGFuYWx5c2VzLiBKdXN0IHJlbWVtYmVyLCBsaWtlIG1vc3Qgb3RoZXIgc2tpbGxzIHRoYXQgeW91IHRha2UgdGltZSB0byBkZXZlbG9wLCBpdCBwYXlzIHRvIGJlIGNvbnNpc3QgYXMgeW91ciBza2lsbHMgd2lsbCBkaW1pbmlzaCB0aGUgbGVzcyBlbmdhZ2VkIHlvdSBhcmUgb3ZlciB0aW1lLiA=