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

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

RStudio Windows

Upper Left Upper Right Lower Left Lower Right
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).

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

Scripting Window

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.

Debugging

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.

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

Set wd

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:

Set wd and console

With the working directory set, it is time to import your data for analysis.

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

Excel Dataset

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:

library(readxl)

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 Environment

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

Format Rows

To perform this action we will use the following command:

basic_stats[,1] <- NULL

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.

head(basic_stats)

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

summary(basic_stats)
   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.

sapply(basic_stats, sd)
   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 

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

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

library(ggplot2)
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?

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

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