Reproducible Analysis

Researcher degrees of freedom are an issue in disciplines that rely heavily on fairly complicated statistical analysis. There are many different ways a dataset can be analysed, which can produce qualitatively different results (see Many analysts, one dataset). A way to combat this is to create reproducible workflows, so that given the files and analytical techniques used a result can be replicated and thus critiqued.

Hadley Wickam’s amazing book R for Data Science has the following image of the workflow:

Data Analysis Workflow

Data Analysis Workflow

Read the book for a much more developed workflow practice.


Data Import

The information contain here is a quick and dirty way to get set up data import, the first stage of the workflow, a stage that is confusing and daunting given the many different applications that exist to assist in streamlining and data sharing.

I will walk through three topics:

  1. R Projects

  2. Packrat

  3. Github

Projects

R Projects are amazing. Use them. They will save you time and allow you to organise all your work so that you, or anyone else, can pick up where you left off without any key files missing. There is great synergy between R projects, Packrat and GitHub, such that you should be able to import, analyse and share data and code that clearly illustrates what you are trying to do; great for collaboration and great for reporducibility.

Projects allow one to use relative file addresses, such as Temporal_data.csv, which allows for ease of reproducibilty. Projects automatically set the working directing to project folder. This allows us to use relative file addresses so we don’t have to specify what folder to look in on the computer for the dataset. If we have all the files of interest in the project folder, we can just call the name of the datafile and not have to specify where to look.

Why is this useful? Well consider if you sent your datafiles and scipts to someone else. They are going to have a different storage system to you, they’re unlikely to have folders laid out like /PhD/Data/Dimensions of Natural Capital/Transect Data/Nat_Cap_Dim_Transect_Data.csv". So instead we organise our files into one project folder, use relative addresses and send the whole project folder over to our friend. They will then be able to open up a script and get straight to work, no messing around with looking for files.

#relative address
temporal <- read.csv("Temporal_data.csv")

#absolute address, wont be transferable and so hinders ease of reproducibility
Transect <- read.csv("~/PhD/Data/Dimensions of Natural Capital/Transect Data/Nat_Cap_Dim_Transect_Data.csv")

Packrat

Packages make life so much easier, sharing functions and code allows analyses to proceed incredibly fast, once you know what to do. An issue for reproducibility though is that packages change over time, perhaps a function is changed to do something slightly different and years later when you go to reproduce some work, this could produce a different result. Packrat gets around this issue.

Packrat is a dependency management tool that allows each project to have it’s own package folder, or private package library. It notes what version of the package has been used, so that if an analysis is reproduced years later, it will install the version of the package that was used when the analysis took place.

Isolated: Installing a new or updated package for one project won’t break your other projects, and vice versa. That’s because packrat gives each project its own private package library.

Portable: Easily transport your projects from one computer to another, even across different platforms. Packrat makes it easy to install the packages your project depends on.

Reproducible: Packrat records the exact package versions you depend on, and ensures those exact versions are the ones that get installed wherever you go.

To set up Packrat: got to Packages tab and look for Packrat sub tab. You may need to update R studio for this to appear. Click on the packrat tab and check the box to use Packrat with this project.

Now all the packages for this project are stored in a folder for this project. Great.

GitHub

GitHub is a repository for code. It is useful as just that, a repository for all your code, which you can access from anywhere. It’s main advantage over other cloud storage services is that it is built to assist in collaboratively working on projects together. Very useful in the computer programming worlds, but also useful when working on data analysis.

Github works off repositories, basically the same as projects in R. All the filed related to one project are stored in a repositroy.

Introduction to Git

If you are new to Github it can be quite confusing. I was, and still am, a beginner, but I can now see why it can be useful and am eager to keep using it for all of my projects. For an introduction check out this tutorial. By following this tutorial you will be set up with a GitHub account and introduced to repositories.

Further tutorials

For our purposes it will mostly be a reposisitory for code and allow ease of collaboration on analysis, but it can be used for alot more besides.

Setting up Git with an R project:

It should be possible to set up git with an already existing project, yet when I do this I can’t connect to my online github account. I haven’t figured out why yet. So what I do is create a new project that uses Git from the start and then copy over any folders I want into the new Git initiated R project.

You will need a GitHub account to follow these instructions.

  1. Create a new project (top right tab in the R studio user interface)
  2. Choose Version Control option (the third option)
  3. Choose the local library on your computer where you would like to create the folder, I use the same address as the R project I created above and just make a new folder within that.
  4. On your git hub account, create a new repository (green button on the right hand side of the repository sreen).
  5. Name the repository (repo if Git jargon) whatever you would like, good to keep the names of the R project and Git repo the same.
  6. Copy and paste the URL of the newly created repo into the URL section of the New Project window in R Studio.
  7. Copy and paste all the files from the R project folder into the newly created Git enabled R project folder.
  8. To check that Git is now enabled for use with your R project, open up R project and look for the Git Tab in the top right window in R Studio, as shown:

Now that the project is set up and connected to your online github profile you can commit all the files in the R project and then push them to the master branch online (read this as uploading the project).

  1. In the top right window of R Studio go to the Git tab.
  2. As this is the first time we are commiting (getting files ready to upload) files, click on the commit sub tab
  3. Check the files that you would like to commit, write some commit comment, it is mandatory, and then click the commit button.
  • Commit
  1. Close the box that pops up and then click the push button. This will upload or push the committed files to the online repo.
  • Push
  1. Check the online repo, have the files been uploaded?

Sharing Projects

Your project can now be shared with other people, all they have to do is copy and paste the URL of the repository when they are creating a new project.

Create a new version control project

Create a new version control project

Choose GIT as the version control manager

Copy and paste the repository URL into the URL box

Copy and paste the repository URL into the URL box

Alternatively, you can add collaborators to the online repository. Go to the settings tab of your repository on GitHub, then manage access and add collaborators.

Doing this will mean that you don’t have to clone the repository and create a new project every time someone else conducts some new analysis, uploads a new file etc. This is the benefit of GIT, you can pull any new updates that someone else has pushed, you can see what has changed, then do some of your own work, commit the work and then push back to the online repository so your collaborators can access it.

When sharing files collaboratively, the process to follow is:

  1. Pull any new files from the online repo, this will update all the files you hold locally on your device.
  2. Commit the changes in the files you have worked on.
  3. Push the files to upload them to the online repo.

The Pull, Commit and Push workflow ensures that you stay up to date with how the project is developing while contributing your work.


Tidy Data

Tidy Data

Tidy data (go and check out the link for Hadley Wickham’s explanation) is way of formatting data that follows three rules:

  1. Each variable has its own column
  2. Each observation must have its own row
  3. Each value must have its own cell.

Visually it looks like this:

Tidy data format

Tidy data format

The benefit of using tidy datasets is that it provides a standardised way to format a dataset. This allows you to get familiar with a data structure and become fluent in handling data transformation and analysis, whatever dataset you are presented with. But it is not just an arbirary standard that benefits the user. The tidyverse family of packages is built around tidy datasets and so using tidy data will assist in using that very powerful package family.

The data set below is not tidy. I have multiple observations in one row, the size of a network in May, June, July and August. I am really missing a variable called month and a variable called size. This dataset needs to be tidied.

If you would like to run this code on your computer make sure to remove the comments, the #, in front of the lines of code for installing packages.

#install.packages("kableExtra")
#install.packages("dplyr")
library(dplyr)
library(kableExtra) #this is a package for making nice tables, to compare try just running head(temporal)
kable(head(temporal)) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
X May June July August type level
1 6 95 41 54 Urban High Urban
2 37 40 113 23 Urban High Urban
3 3 3 19 56 Urban High Urban
4 6 67 99 63 Urban Medium Urban
5 7 67 69 52 Urban Medium Urban
6 12 114 51 55 Urban Medium Urban

Fortunately, some very smart people have written packages that take all the pain out of doing this. The tidyr package contains functions such as pivot_longer which take an untidy dataset and transform it into a tidy dataset.

#install.packages("tidyr")
library("tidyr")

#taking the four columns May, June, July and Augst and creating a column called month for the names and a column called size for the values.
kable(temporal[1:2,] %>%
  pivot_longer(c("May", "June", "July", "August"), names_to = "month", values_to ="size", names_ptypes = list(month = factor()))) %>%
  kable_styling(bootstrap_options = c("striped", "hover"))
X type level month size
1 Urban High Urban May 6
1 Urban High Urban June 95
1 Urban High Urban July 41
1 Urban High Urban August 54
2 Urban High Urban May 37
2 Urban High Urban June 40
2 Urban High Urban July 113
2 Urban High Urban August 23

Ta da! Now we have tidy data.


Transform

Data Analysis Workflow

Data Analysis Workflow

Check out Hadley Wickham’s book for information on piping, vectors, functions and iteration. These are the nuts and bolts of manipulating and transforming data and I would recommend spending some time on each subject to get fluent in data manipulation. It will save you loads of time and even be enjoyable once you hit a certain escape velocity.

Tl;dr Storing objects in the environment is necessary but can be a bit cluttered, piping helps to reduce the clutter. Piping would bring joy to Marie Kondo.

One note on piping is that it changes how you think about using R. Whenever I was conducting multiple operations on a dataset I would store each step of the operation as an object, a = 7, a.1 = 6, a.2 = 8, in the environment. In the process of conducting an analysis I could end up with hundreds of objects in the environment most with arbritary names, such as a.2 being 8, that don’t make sense to anyone else, or even myself a week later. I then have to try and figure out what object stored what value and distinguish it from the myriad of other very similar objects I created, was I interested in a or a.1??. It is very easy to confuse a and a.1 in a piece of code and if I don’t catch it, this could effect the analysis.

Piping helps to get around this issue, as each piece of code can stand alone and be self referential to some degree. It bundles up multiple operations, piping the result of the previous operation to the following operation. That way we don’t need to store lots and lots of objects in the environment, we can use pipes, decluttering the environment, making code less confusing and increasing ease of reproducibilty.

A second point is that code in a script is more ‘real’ than any object stored in the environment. For a more consise explanation of the realness of objects and code check out this what is real section of the R for Data Science book.


Visualisation

Data Analysis Workflow

Data Analysis Workflow

I’m a convert to ggplot, after having used base R functions to create plots for years. To help assist in your conversion check out this webpage of plots built using ggplot, aren’t they beautiful?

An example of using ggplot

temporal %>%
  pivot_longer(c("May", "June", "July", "August"), names_to = "month", values_to ="size", names_ptypes = list(month = factor())) %>%
  ggplot( aes(x=month, y=size)) +
    geom_jitter(aes(col=level), size = 3, width = 0.1) +
       scale_colour_manual(values= urbanpalette) +
       coord_cartesian(ylim=c(0,150)) +
       labs(title = "", y="Size", x="", caption = "") +
        theme_bw()

In this document I am experimenting with ggplot trying to create beautiful looking PCA plots. Check out the many different ways of creatin PCA plots and compare the base plots to the more customised ggplot based plots. The code to recreate all the plots in the document can be downloaded from this github repo.


Communication

R is far from just a statistical analysis programme. It’s an integrated development environment (IDE) meaning that it can be used to create, develop and publish documents, widgets, webpages, books, theses, presentations, blogs and the list keeps growing. It’s like Powerpoint, Word, LateX, Stata, Wordpress and Wix all bundled into one. Best of all it’s open source, free and has a community that loves to share code and best practices, meaning that if you see some really cool figure, analysis or widget, you will most likely find a tutorial, or code, that will help you to reproduce it. Then you can modify it however you want and hopefully inspire someone to use your code.

R Markdown

R Markdown is a very verstaile document formatting language. It can be translated into HTML (the language of webpages), PDFs through LateX, Word Documents and numerious presentation style formats. People have created websites using R Markdown (link, link and link) and have written books in R Markdown, in fact there is a book about R Markdown written in R Markdown.

This guide has been written using R Markdown!

Markdown resources:

R Pubs

R Pubs is a free publishing service provided by RStudio the company so it is easy to share documents created in RStudio the application.

You are able to read this document because RPubs is hosting it on their servers.

ShinyR

ShinyR allows you to build interactive web applications directly from R. You can publish any apps you create for free at https://www.shinyapps.io/.

#install.packages("shiny")
library(shiny)

Define UI for application that draws a histogram

ui <- fluidPage(
   
   # Application title
   titlePanel("Old Faithful Geyser Data"),
   
   # Sidebar with a slider input for number of bins 
   sidebarLayout(
      sidebarPanel(
         sliderInput("bins",
                     "Number of bins:",
                     min = 1,
                     max = 50,
                     value = 30)
      ),
      
      # Show a plot of the generated distribution
      mainPanel(
         plotOutput("distPlot")
      )
   )
)

Define server logic required to draw a histogram

server <- function(input, output) {
   
   output$distPlot <- renderPlot({
      # generate bins based on input$bins from ui.R
      x    <- faithful[, 2] 
      bins <- seq(min(x), max(x), length.out = input$bins + 1)
      
      # draw the histogram with the specified number of bins
      hist(x, breaks = bins, col = 'darkgray', border = 'white')
   })
}

The render function is for the plots or text that you would like to change when in the app

Run the application

#shinyApp(ui = ui, server = server)

Making your own Website

Making websites with Git

Making websites with R

Writing a book or thesis with R

See this good blog on how to write a thesis in R

See this book chapter on how to write books

LS0tDQp0aXRsZTogIlJlcHJvZHVjaWJsZSBBbmFseXNpcyINCmF1dGhvcjogIkNpYW4gV2hpdGUiDQpkYXRlOiAnMjAyMC0wMy0xOCcNCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdA0KICBwZGZfZG9jdW1lbnQ6IGRlZmF1bHQNCi0tLQ0KDQoqKioNCg0KIyBSZXByb2R1Y2libGUgQW5hbHlzaXMNCg0KUmVzZWFyY2hlciBkZWdyZWVzIG9mIGZyZWVkb20gYXJlIGFuIGlzc3VlIGluIGRpc2NpcGxpbmVzIHRoYXQgcmVseSBoZWF2aWx5IG9uIGZhaXJseSBjb21wbGljYXRlZCBzdGF0aXN0aWNhbCBhbmFseXNpcy4gVGhlcmUgYXJlIG1hbnkgZGlmZmVyZW50IHdheXMgYSBkYXRhc2V0IGNhbiBiZSBhbmFseXNlZCwgd2hpY2ggY2FuIHByb2R1Y2UgcXVhbGl0YXRpdmVseSBkaWZmZXJlbnQgcmVzdWx0cyAoc2VlIFtNYW55IGFuYWx5c3RzLCBvbmUgZGF0YXNldF0oaHR0cHM6Ly9qb3VybmFscy5zYWdlcHViLmNvbS9kb2kvMTAuMTE3Ny8yNTE1MjQ1OTE3NzQ3NjQ2KSkuIEEgd2F5IHRvIGNvbWJhdCB0aGlzIGlzIHRvIGNyZWF0ZSByZXByb2R1Y2libGUgd29ya2Zsb3dzLCBzbyB0aGF0IGdpdmVuIHRoZSBmaWxlcyBhbmQgYW5hbHl0aWNhbCB0ZWNobmlxdWVzIHVzZWQgYSByZXN1bHQgY2FuIGJlIHJlcGxpY2F0ZWQgYW5kIHRodXMgY3JpdGlxdWVkLg0KDQpbSGFkbGV5IFdpY2thbSdzXShodHRwOi8vaGFkbGV5Lm56LykgYW1hemluZyBib29rIFtSIGZvciBEYXRhIFNjaWVuY2VdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovKSBoYXMgdGhlIGZvbGxvd2luZyBpbWFnZSBvZiB0aGUgd29ya2Zsb3c6DQoNCiFbRGF0YSBBbmFseXNpcyBXb3JrZmxvd10oV29ya2Zsb3cuUE5HKQ0KDQpSZWFkIHRoZSBib29rIGZvciBhIG11Y2ggbW9yZSBkZXZlbG9wZWQgd29ya2Zsb3cgcHJhY3RpY2UuDQoNCioqKg0KDQojIyBEYXRhIEltcG9ydA0KDQpUaGUgaW5mb3JtYXRpb24gY29udGFpbiBoZXJlIGlzIGEgcXVpY2sgYW5kIGRpcnR5IHdheSB0byBnZXQgc2V0IHVwIGRhdGEgaW1wb3J0LCB0aGUgZmlyc3Qgc3RhZ2Ugb2YgdGhlIHdvcmtmbG93LCBhIHN0YWdlIHRoYXQgaXMgY29uZnVzaW5nIGFuZCBkYXVudGluZyBnaXZlbiB0aGUgbWFueSBkaWZmZXJlbnQgYXBwbGljYXRpb25zIHRoYXQgZXhpc3QgdG8gYXNzaXN0IGluIHN0cmVhbWxpbmluZyBhbmQgZGF0YSBzaGFyaW5nLg0KDQpJIHdpbGwgd2FsayB0aHJvdWdoIHRocmVlIHRvcGljczoNCg0KMS4gUiBQcm9qZWN0cw0KDQoyLiBQYWNrcmF0DQoNCjMuIEdpdGh1YiAgDQoNCg0KIyMjIFByb2plY3RzDQoNClIgUHJvamVjdHMgYXJlIGFtYXppbmcuIFVzZSB0aGVtLiBUaGV5IHdpbGwgc2F2ZSB5b3UgdGltZSBhbmQgYWxsb3cgeW91IHRvIG9yZ2FuaXNlIGFsbCB5b3VyIHdvcmsgc28gdGhhdCB5b3UsIG9yIGFueW9uZSBlbHNlLCBjYW4gcGljayB1cCB3aGVyZSB5b3UgbGVmdCBvZmYgd2l0aG91dCBhbnkga2V5IGZpbGVzIG1pc3NpbmcuIFRoZXJlIGlzIGdyZWF0IHN5bmVyZ3kgYmV0d2VlbiBSIHByb2plY3RzLCBQYWNrcmF0IGFuZCBHaXRIdWIsIHN1Y2ggdGhhdCB5b3Ugc2hvdWxkIGJlIGFibGUgdG8gaW1wb3J0LCBhbmFseXNlIGFuZCBzaGFyZSBkYXRhIGFuZCBjb2RlIHRoYXQgY2xlYXJseSBpbGx1c3RyYXRlcyB3aGF0IHlvdSBhcmUgdHJ5aW5nIHRvIGRvOyBncmVhdCBmb3IgY29sbGFib3JhdGlvbiBhbmQgZ3JlYXQgZm9yIHJlcG9yZHVjaWJpbGl0eS4NCg0KW1Byb2plY3RzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3dvcmtmbG93LXByb2plY3RzLmh0bWwpIGFsbG93IG9uZSB0byB1c2UgcmVsYXRpdmUgZmlsZSBhZGRyZXNzZXMsIHN1Y2ggYXMgYFRlbXBvcmFsX2RhdGEuY3N2YCwgd2hpY2ggYWxsb3dzIGZvciBlYXNlIG9mIHJlcHJvZHVjaWJpbHR5LiBQcm9qZWN0cyBhdXRvbWF0aWNhbGx5IHNldCB0aGUgd29ya2luZyBkaXJlY3RpbmcgdG8gcHJvamVjdCBmb2xkZXIuIFRoaXMgYWxsb3dzIHVzIHRvIHVzZSByZWxhdGl2ZSBmaWxlIGFkZHJlc3NlcyBzbyB3ZSBkb24ndCBoYXZlIHRvIHNwZWNpZnkgd2hhdCBmb2xkZXIgdG8gbG9vayBpbiBvbiB0aGUgY29tcHV0ZXIgZm9yIHRoZSBkYXRhc2V0LiBJZiB3ZSBoYXZlIGFsbCB0aGUgZmlsZXMgb2YgaW50ZXJlc3QgaW4gdGhlIHByb2plY3QgZm9sZGVyLCB3ZSBjYW4ganVzdCBjYWxsIHRoZSBuYW1lIG9mIHRoZSBkYXRhZmlsZSBhbmQgbm90IGhhdmUgdG8gc3BlY2lmeSB3aGVyZSB0byBsb29rLiANCg0KV2h5IGlzIHRoaXMgdXNlZnVsPyBXZWxsIGNvbnNpZGVyIGlmIHlvdSBzZW50IHlvdXIgZGF0YWZpbGVzIGFuZCBzY2lwdHMgdG8gc29tZW9uZSBlbHNlLiBUaGV5IGFyZSBnb2luZyB0byBoYXZlIGEgZGlmZmVyZW50IHN0b3JhZ2Ugc3lzdGVtIHRvIHlvdSwgdGhleSdyZSB1bmxpa2VseSB0byBoYXZlIGZvbGRlcnMgbGFpZCBvdXQgbGlrZSBgL1BoRC9EYXRhL0RpbWVuc2lvbnMgb2YgTmF0dXJhbCBDYXBpdGFsL1RyYW5zZWN0IERhdGEvTmF0X0NhcF9EaW1fVHJhbnNlY3RfRGF0YS5jc3YiYC4gU28gaW5zdGVhZCB3ZSBvcmdhbmlzZSBvdXIgZmlsZXMgaW50byBvbmUgcHJvamVjdCBmb2xkZXIsIHVzZSByZWxhdGl2ZSBhZGRyZXNzZXMgYW5kIHNlbmQgdGhlIHdob2xlIHByb2plY3QgZm9sZGVyIG92ZXIgdG8gb3VyIGZyaWVuZC4gVGhleSB3aWxsIHRoZW4gYmUgYWJsZSB0byBvcGVuIHVwIGEgc2NyaXB0IGFuZCBnZXQgc3RyYWlnaHQgdG8gd29yaywgbm8gbWVzc2luZyBhcm91bmQgd2l0aCBsb29raW5nIGZvciBmaWxlcy4NCg0KYGBge3J9DQojcmVsYXRpdmUgYWRkcmVzcw0KdGVtcG9yYWwgPC0gcmVhZC5jc3YoIlRlbXBvcmFsX2RhdGEuY3N2IikNCg0KI2Fic29sdXRlIGFkZHJlc3MsIHdvbnQgYmUgdHJhbnNmZXJhYmxlIGFuZCBzbyBoaW5kZXJzIGVhc2Ugb2YgcmVwcm9kdWNpYmlsaXR5DQpUcmFuc2VjdCA8LSByZWFkLmNzdigifi9QaEQvRGF0YS9EaW1lbnNpb25zIG9mIE5hdHVyYWwgQ2FwaXRhbC9UcmFuc2VjdCBEYXRhL05hdF9DYXBfRGltX1RyYW5zZWN0X0RhdGEuY3N2IikNCmBgYA0KDQoNCg0KIyMjIFBhY2tyYXQNCg0KUGFja2FnZXMgbWFrZSBsaWZlIHNvIG11Y2ggZWFzaWVyLCBzaGFyaW5nIGZ1bmN0aW9ucyBhbmQgY29kZSBhbGxvd3MgYW5hbHlzZXMgdG8gcHJvY2VlZCBpbmNyZWRpYmx5IGZhc3QsIG9uY2UgeW91IGtub3cgd2hhdCB0byBkby4gQW4gaXNzdWUgZm9yIHJlcHJvZHVjaWJpbGl0eSB0aG91Z2ggaXMgdGhhdCBwYWNrYWdlcyBjaGFuZ2Ugb3ZlciB0aW1lLCBwZXJoYXBzIGEgZnVuY3Rpb24gaXMgY2hhbmdlZCB0byBkbyBzb21ldGhpbmcgc2xpZ2h0bHkgZGlmZmVyZW50IGFuZCB5ZWFycyBsYXRlciB3aGVuIHlvdSBnbyB0byByZXByb2R1Y2Ugc29tZSB3b3JrLCB0aGlzIGNvdWxkIHByb2R1Y2UgYSBkaWZmZXJlbnQgcmVzdWx0LiBQYWNrcmF0IGdldHMgYXJvdW5kIHRoaXMgaXNzdWUuDQoNCltQYWNrcmF0XShodHRwOi8vcnN0dWRpby5naXRodWIuaW8vcGFja3JhdC8pIGlzIGEgZGVwZW5kZW5jeSBtYW5hZ2VtZW50IHRvb2wgdGhhdCBhbGxvd3MgZWFjaCBwcm9qZWN0IHRvIGhhdmUgaXQncyBvd24gcGFja2FnZSBmb2xkZXIsIG9yICoqcHJpdmF0ZSBwYWNrYWdlIGxpYnJhcnkqKi4gSXQgbm90ZXMgd2hhdCB2ZXJzaW9uIG9mIHRoZSBwYWNrYWdlIGhhcyBiZWVuIHVzZWQsIHNvIHRoYXQgaWYgYW4gYW5hbHlzaXMgaXMgcmVwcm9kdWNlZCB5ZWFycyBsYXRlciwgaXQgd2lsbCBpbnN0YWxsIHRoZSB2ZXJzaW9uIG9mIHRoZSBwYWNrYWdlIHRoYXQgd2FzIHVzZWQgd2hlbiB0aGUgYW5hbHlzaXMgdG9vayBwbGFjZS4NCg0KDQo+IElzb2xhdGVkOiBJbnN0YWxsaW5nIGEgbmV3IG9yIHVwZGF0ZWQgcGFja2FnZSBmb3Igb25lIHByb2plY3Qgd29u4oCZdCBicmVhayB5b3VyIG90aGVyIHByb2plY3RzLCBhbmQgdmljZSB2ZXJzYS4gVGhhdOKAmXMgYmVjYXVzZSBwYWNrcmF0IGdpdmVzIGVhY2ggcHJvamVjdCBpdHMgb3duIHByaXZhdGUgcGFja2FnZSBsaWJyYXJ5Lg0KDQo+UG9ydGFibGU6IEVhc2lseSB0cmFuc3BvcnQgeW91ciBwcm9qZWN0cyBmcm9tIG9uZSBjb21wdXRlciB0byBhbm90aGVyLCBldmVuIGFjcm9zcyBkaWZmZXJlbnQgcGxhdGZvcm1zLiBQYWNrcmF0IG1ha2VzIGl0IGVhc3kgdG8gaW5zdGFsbCB0aGUgcGFja2FnZXMgeW91ciBwcm9qZWN0IGRlcGVuZHMgb24uDQoNCj5SZXByb2R1Y2libGU6IFBhY2tyYXQgcmVjb3JkcyB0aGUgZXhhY3QgcGFja2FnZSB2ZXJzaW9ucyB5b3UgZGVwZW5kIG9uLCBhbmQgZW5zdXJlcyB0aG9zZSBleGFjdCB2ZXJzaW9ucyBhcmUgdGhlIG9uZXMgdGhhdCBnZXQgaW5zdGFsbGVkIHdoZXJldmVyIHlvdSBnby4NCg0KVG8gc2V0IHVwIFBhY2tyYXQ6IGdvdCB0byBgUGFja2FnZXNgIHRhYiBhbmQgbG9vayBmb3IgYFBhY2tyYXRgIHN1YiB0YWIuIFlvdSBtYXkgbmVlZCB0byB1cGRhdGUgUiBzdHVkaW8gZm9yIHRoaXMgdG8gYXBwZWFyLiBDbGljayBvbiB0aGUgcGFja3JhdCB0YWIgYW5kIGNoZWNrIHRoZSBib3ggdG8gdXNlIFBhY2tyYXQgd2l0aCB0aGlzIHByb2plY3QuDQoNCk5vdyBhbGwgdGhlIHBhY2thZ2VzIGZvciB0aGlzIHByb2plY3QgYXJlIHN0b3JlZCBpbiBhIGZvbGRlciBmb3IgdGhpcyBwcm9qZWN0LiBHcmVhdC4NCg0KIyMjIEdpdEh1Yg0KDQpHaXRIdWIgaXMgYSByZXBvc2l0b3J5IGZvciBjb2RlLiBJdCBpcyB1c2VmdWwgYXMganVzdCB0aGF0LCBhIHJlcG9zaXRvcnkgZm9yIGFsbCB5b3VyIGNvZGUsIHdoaWNoIHlvdSBjYW4gYWNjZXNzIGZyb20gYW55d2hlcmUuIEl0J3MgbWFpbiBhZHZhbnRhZ2Ugb3ZlciBvdGhlciBjbG91ZCBzdG9yYWdlIHNlcnZpY2VzIGlzIHRoYXQgaXQgaXMgYnVpbHQgdG8gYXNzaXN0IGluIGNvbGxhYm9yYXRpdmVseSB3b3JraW5nIG9uIHByb2plY3RzIHRvZ2V0aGVyLiBWZXJ5IHVzZWZ1bCBpbiB0aGUgY29tcHV0ZXIgcHJvZ3JhbW1pbmcgd29ybGRzLCBidXQgYWxzbyB1c2VmdWwgd2hlbiB3b3JraW5nIG9uIGRhdGEgYW5hbHlzaXMuDQoNCkdpdGh1YiB3b3JrcyBvZmYgcmVwb3NpdG9yaWVzLCBiYXNpY2FsbHkgdGhlIHNhbWUgYXMgcHJvamVjdHMgaW4gUi4gQWxsIHRoZSBmaWxlZCByZWxhdGVkIHRvIG9uZSBwcm9qZWN0IGFyZSBzdG9yZWQgaW4gYSByZXBvc2l0cm95Lg0KDQojIyMjIEludHJvZHVjdGlvbiB0byBHaXQNCg0KSWYgeW91IGFyZSBuZXcgdG8gR2l0aHViIGl0IGNhbiBiZSBxdWl0ZSBjb25mdXNpbmcuIEkgd2FzLCBhbmQgc3RpbGwgYW0sIGEgYmVnaW5uZXIsIGJ1dCBJIGNhbiBub3cgc2VlIHdoeSBpdCBjYW4gYmUgdXNlZnVsIGFuZCBhbSBlYWdlciB0byBrZWVwIHVzaW5nIGl0IGZvciBhbGwgb2YgbXkgcHJvamVjdHMuIEZvciBhbiBpbnRyb2R1Y3Rpb24gY2hlY2sgb3V0IHRoaXMgIFt0dXRvcmlhbF0oaHR0cHM6Ly9ndWlkZXMuZ2l0aHViLmNvbS9hY3Rpdml0aWVzL2hlbGxvLXdvcmxkLykuIEJ5IGZvbGxvd2luZyB0aGlzIHR1dG9yaWFsIHlvdSB3aWxsIGJlIHNldCB1cCB3aXRoIGEgR2l0SHViIGFjY291bnQgYW5kIGludHJvZHVjZWQgdG8gcmVwb3NpdG9yaWVzLiANCg0KRnVydGhlciB0dXRvcmlhbHMNCg0KKiBbbGVhcm4gR2l0XShodHRwczovL3RyeS5naXRodWIuaW8vKSAgDQoNCiAgIE1vcmUgYWR2YW5jZWQ6ICANCiogW0dpdCBjaGVhdHNoZWV0XShodHRwczovL2dpdGh1Yi5jb20vdGlpbWdyZWVuL2dpdGh1Yi1jaGVhdC1zaGVldCkgIA0KKiBbR2l0IGFuZCB3ZWJzaXRlc10oaHR0cDovL2ptY2dsb25lLmNvbS9ndWlkZXMvZ2l0aHViLXBhZ2VzLykgIA0KKiBbSHVic3BvdF0oaHR0cHM6Ly9wcm9kdWN0Lmh1YnNwb3QuY29tL2Jsb2cvZ2l0LWFuZC1naXRodWItdHV0b3JpYWwtZm9yLWJlZ2lubmVycykgIA0KDQpGb3Igb3VyIHB1cnBvc2VzIGl0IHdpbGwgbW9zdGx5IGJlIGEgcmVwb3Npc2l0b3J5IGZvciBjb2RlIGFuZCBhbGxvdyBlYXNlIG9mIGNvbGxhYm9yYXRpb24gb24gYW5hbHlzaXMsIGJ1dCBpdCBjYW4gYmUgdXNlZCBmb3IgYWxvdCBtb3JlIGJlc2lkZXMuDQoNCiMjIyMgU2V0dGluZyB1cCBHaXQgd2l0aCBhbiBSIHByb2plY3Q6DQoNCkl0IHNob3VsZCBiZSBwb3NzaWJsZSB0byBzZXQgdXAgZ2l0IHdpdGggYW4gYWxyZWFkeSBleGlzdGluZyBwcm9qZWN0LCB5ZXQgd2hlbiBJIGRvIHRoaXMgSSBjYW4ndCBjb25uZWN0IHRvIG15IG9ubGluZSBnaXRodWIgYWNjb3VudC4gSSBoYXZlbid0IGZpZ3VyZWQgb3V0IHdoeSB5ZXQuIFNvIHdoYXQgSSBkbyBpcyBjcmVhdGUgYSBuZXcgcHJvamVjdCB0aGF0IHVzZXMgR2l0IGZyb20gdGhlIHN0YXJ0IGFuZCB0aGVuIGNvcHkgb3ZlciBhbnkgZm9sZGVycyBJIHdhbnQgaW50byB0aGUgbmV3IEdpdCBpbml0aWF0ZWQgUiBwcm9qZWN0Lg0KDQpZb3Ugd2lsbCBuZWVkIGEgR2l0SHViIGFjY291bnQgdG8gZm9sbG93IHRoZXNlIGluc3RydWN0aW9ucy4NCg0KMS4gQ3JlYXRlIGEgYG5ldyBwcm9qZWN0YCAodG9wIHJpZ2h0IHRhYiBpbiB0aGUgUiBzdHVkaW8gdXNlciBpbnRlcmZhY2UpICANCjIuIENob29zZSBgVmVyc2lvbiBDb250cm9sYCBvcHRpb24gKHRoZSB0aGlyZCBvcHRpb24pICANCjMuIENob29zZSB0aGUgbG9jYWwgbGlicmFyeSBvbiB5b3VyIGNvbXB1dGVyIHdoZXJlIHlvdSB3b3VsZCBsaWtlIHRvIGNyZWF0ZSB0aGUgZm9sZGVyLCBJIHVzZSB0aGUgc2FtZSBhZGRyZXNzIGFzIHRoZSBSIHByb2plY3QgSSBjcmVhdGVkIGFib3ZlIGFuZCBqdXN0IG1ha2UgYSBuZXcgZm9sZGVyIHdpdGhpbiB0aGF0LiAgDQo0LiBPbiB5b3VyIGdpdCBodWIgYWNjb3VudCwgY3JlYXRlIGEgbmV3IHJlcG9zaXRvcnkgKGdyZWVuIGJ1dHRvbiBvbiB0aGUgcmlnaHQgaGFuZCBzaWRlIG9mIHRoZSByZXBvc2l0b3J5IHNyZWVuKS4gIA0KNS4gTmFtZSB0aGUgcmVwb3NpdG9yeSAocmVwbyBpZiBHaXQgamFyZ29uKSB3aGF0ZXZlciB5b3Ugd291bGQgbGlrZSwgZ29vZCB0byBrZWVwIHRoZSBuYW1lcyBvZiB0aGUgUiBwcm9qZWN0IGFuZCBHaXQgcmVwbyB0aGUgc2FtZS4gIA0KNi4gQ29weSBhbmQgcGFzdGUgdGhlIFVSTCBvZiB0aGUgbmV3bHkgY3JlYXRlZCByZXBvIGludG8gdGhlIFVSTCBzZWN0aW9uIG9mIHRoZSBOZXcgUHJvamVjdCB3aW5kb3cgaW4gUiBTdHVkaW8uICANCjcuIENvcHkgYW5kIHBhc3RlIGFsbCB0aGUgZmlsZXMgZnJvbSB0aGUgUiBwcm9qZWN0IGZvbGRlciBpbnRvIHRoZSBuZXdseSBjcmVhdGVkIEdpdCBlbmFibGVkIFIgcHJvamVjdCBmb2xkZXIuICANCjguIFRvIGNoZWNrIHRoYXQgR2l0IGlzIG5vdyBlbmFibGVkIGZvciB1c2Ugd2l0aCB5b3VyIFIgcHJvamVjdCwgb3BlbiB1cCBSIHByb2plY3QgYW5kIGxvb2sgZm9yIHRoZSBHaXQgVGFiIGluIHRoZSB0b3AgcmlnaHQgd2luZG93IGluIFIgU3R1ZGlvLCBhcyBzaG93bjoNCg0KIVtdKEdpdF9lbmFibGVkLlBORykNCg0KTm93IHRoYXQgdGhlIHByb2plY3QgaXMgc2V0IHVwIGFuZCBjb25uZWN0ZWQgdG8geW91ciBvbmxpbmUgZ2l0aHViIHByb2ZpbGUgeW91IGNhbiBjb21taXQgYWxsIHRoZSBmaWxlcyBpbiB0aGUgUiBwcm9qZWN0IGFuZCB0aGVuIHB1c2ggdGhlbSB0byB0aGUgbWFzdGVyIGJyYW5jaCBvbmxpbmUgKHJlYWQgdGhpcyBhcyB1cGxvYWRpbmcgdGhlIHByb2plY3QpLg0KDQoxLiBJbiB0aGUgdG9wIHJpZ2h0IHdpbmRvdyBvZiBSIFN0dWRpbyBnbyB0byB0aGUgYEdpdGAgdGFiLiAgDQoyLiBBcyB0aGlzIGlzIHRoZSBmaXJzdCB0aW1lIHdlIGFyZSBjb21taXRpbmcgKGdldHRpbmcgZmlsZXMgcmVhZHkgdG8gdXBsb2FkKSBmaWxlcywgY2xpY2sgb24gdGhlIGBjb21taXRgIHN1YiB0YWIgIA0KMy4gQ2hlY2sgdGhlIGZpbGVzIHRoYXQgeW91IHdvdWxkIGxpa2UgdG8gYGNvbW1pdGAsIHdyaXRlIHNvbWUgY29tbWl0IGNvbW1lbnQsIGl0IGlzIG1hbmRhdG9yeSwgYW5kIHRoZW4gY2xpY2sgdGhlIGNvbW1pdCBidXR0b24uDQogICsgIVtDb21taXRdKENvbW1pdC5wbmcpDQo0LiBDbG9zZSB0aGUgYm94IHRoYXQgcG9wcyB1cCBhbmQgdGhlbiBjbGljayB0aGUgYHB1c2hgIGJ1dHRvbi4gVGhpcyB3aWxsIHVwbG9hZCBvciBgcHVzaGAgdGhlIGNvbW1pdHRlZCBmaWxlcyB0byB0aGUgb25saW5lIHJlcG8uDQogICsgIVtQdXNoXShQdXNoLlBORykNCjUuIENoZWNrIHRoZSBvbmxpbmUgcmVwbywgaGF2ZSB0aGUgZmlsZXMgYmVlbiB1cGxvYWRlZD8NCg0KIyMjIyBTaGFyaW5nIFByb2plY3RzDQoNCllvdXIgcHJvamVjdCBjYW4gbm93IGJlIHNoYXJlZCB3aXRoIG90aGVyIHBlb3BsZSwgYWxsIHRoZXkgaGF2ZSB0byBkbyBpcyBjb3B5IGFuZCBwYXN0ZSB0aGUgVVJMIG9mIHRoZSByZXBvc2l0b3J5IHdoZW4gdGhleSBhcmUgY3JlYXRpbmcgYSBuZXcgcHJvamVjdC4NCg0KIVtDcmVhdGUgYSBuZXcgdmVyc2lvbiBjb250cm9sIHByb2plY3RdKFZlcnNpb25fY29udHJvbC5QTkcpDQoNCkNob29zZSBHSVQgYXMgdGhlIHZlcnNpb24gY29udHJvbCBtYW5hZ2VyDQoNCiFbQ29weSBhbmQgcGFzdGUgdGhlIHJlcG9zaXRvcnkgVVJMIGludG8gdGhlIFVSTCBib3hdKE5ld19wcm9qZWN0LlBORykNCg0KDQoNCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gYWRkIGNvbGxhYm9yYXRvcnMgdG8gdGhlIG9ubGluZSByZXBvc2l0b3J5LiBHbyB0byB0aGUgc2V0dGluZ3MgdGFiIG9mIHlvdXIgcmVwb3NpdG9yeSBvbiBHaXRIdWIsIHRoZW4gbWFuYWdlIGFjY2VzcyBhbmQgYWRkIGNvbGxhYm9yYXRvcnMuIA0KDQohW10oQ29sbGFib3JhdG9ycy5QTkcpDQoNCkRvaW5nIHRoaXMgd2lsbCBtZWFuIHRoYXQgeW91IGRvbid0IGhhdmUgdG8gY2xvbmUgdGhlIHJlcG9zaXRvcnkgYW5kIGNyZWF0ZSBhIG5ldyBwcm9qZWN0IGV2ZXJ5IHRpbWUgc29tZW9uZSBlbHNlIGNvbmR1Y3RzIHNvbWUgbmV3IGFuYWx5c2lzLCB1cGxvYWRzIGEgbmV3IGZpbGUgZXRjLiBUaGlzIGlzIHRoZSBiZW5lZml0IG9mIEdJVCwgeW91IGNhbiBgcHVsbGAgYW55IG5ldyB1cGRhdGVzIHRoYXQgc29tZW9uZSBlbHNlIGhhcyBgcHVzaGVkYCwgeW91IGNhbiBzZWUgd2hhdCBoYXMgY2hhbmdlZCwgdGhlbiBkbyBzb21lIG9mIHlvdXIgb3duIHdvcmssIGBjb21taXRgIHRoZSB3b3JrIGFuZCB0aGVuIGBwdXNoYCBiYWNrIHRvIHRoZSBvbmxpbmUgcmVwb3NpdG9yeSBzbyB5b3VyIGNvbGxhYm9yYXRvcnMgY2FuIGFjY2VzcyBpdC4NCg0KV2hlbiBzaGFyaW5nIGZpbGVzIGNvbGxhYm9yYXRpdmVseSwgdGhlIHByb2Nlc3MgdG8gZm9sbG93IGlzOg0KDQoxLiBgUHVsbGAgYW55IG5ldyBmaWxlcyBmcm9tIHRoZSBvbmxpbmUgcmVwbywgdGhpcyB3aWxsIHVwZGF0ZSBhbGwgdGhlIGZpbGVzIHlvdSBob2xkIGxvY2FsbHkgb24geW91ciBkZXZpY2UuDQoyLiBgQ29tbWl0YCB0aGUgY2hhbmdlcyBpbiB0aGUgZmlsZXMgeW91IGhhdmUgd29ya2VkIG9uLg0KMy4gYFB1c2hgIHRoZSBmaWxlcyB0byB1cGxvYWQgdGhlbSB0byB0aGUgb25saW5lIHJlcG8uDQoNClRoZSBgUHVsbGAsIGBDb21taXRgIGFuZCBgUHVzaGAgd29ya2Zsb3cgZW5zdXJlcyB0aGF0IHlvdSBzdGF5IHVwIHRvIGRhdGUgd2l0aCBob3cgdGhlIHByb2plY3QgaXMgZGV2ZWxvcGluZyB3aGlsZSBjb250cmlidXRpbmcgeW91ciB3b3JrLg0KDQoqKioNCg0KIyMgVGlkeSBEYXRhDQoNCiFbXShXb3JrZmxvdy5QTkcpDQoNCg0KVGlkeSBEYXRhDQoNCltUaWR5IGRhdGFdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovdGlkeS1kYXRhLmh0bWwjdGlkeS1kYXRhLTEpIChnbyBhbmQgY2hlY2sgb3V0IHRoZSBsaW5rIGZvciBIYWRsZXkgV2lja2hhbSdzIGV4cGxhbmF0aW9uKSBpcyB3YXkgb2YgZm9ybWF0dGluZyBkYXRhIHRoYXQgZm9sbG93cyB0aHJlZSBydWxlczoNCg0KMS4gRWFjaCB2YXJpYWJsZSBoYXMgaXRzIG93biBjb2x1bW4NCjIuIEVhY2ggb2JzZXJ2YXRpb24gbXVzdCBoYXZlIGl0cyBvd24gcm93DQozLiBFYWNoIHZhbHVlIG11c3QgaGF2ZSBpdHMgb3duIGNlbGwuDQoNClZpc3VhbGx5IGl0IGxvb2tzIGxpa2UgdGhpczoNCg0KIVtUaWR5IGRhdGEgZm9ybWF0XShUaWR5X2RhdGEuUE5HKQ0KDQpUaGUgYmVuZWZpdCBvZiB1c2luZyB0aWR5IGRhdGFzZXRzIGlzIHRoYXQgaXQgcHJvdmlkZXMgYSBzdGFuZGFyZGlzZWQgd2F5IHRvIGZvcm1hdCBhIGRhdGFzZXQuIFRoaXMgYWxsb3dzIHlvdSB0byBnZXQgZmFtaWxpYXIgd2l0aCBhIGRhdGEgc3RydWN0dXJlIGFuZCBiZWNvbWUgZmx1ZW50IGluIGhhbmRsaW5nIGRhdGEgdHJhbnNmb3JtYXRpb24gYW5kIGFuYWx5c2lzLCB3aGF0ZXZlciBkYXRhc2V0IHlvdSBhcmUgcHJlc2VudGVkIHdpdGguIEJ1dCBpdCBpcyBub3QganVzdCBhbiBhcmJpcmFyeSBzdGFuZGFyZCB0aGF0IGJlbmVmaXRzIHRoZSB1c2VyLiBUaGUgYHRpZHl2ZXJzZWAgZmFtaWx5IG9mIHBhY2thZ2VzIGlzIGJ1aWx0IGFyb3VuZCB0aWR5IGRhdGFzZXRzIGFuZCBzbyB1c2luZyB0aWR5IGRhdGEgd2lsbCBhc3Npc3QgaW4gdXNpbmcgdGhhdCB2ZXJ5IHBvd2VyZnVsIHBhY2thZ2UgZmFtaWx5Lg0KDQpUaGUgZGF0YSBzZXQgYmVsb3cgaXMgbm90IHRpZHkuIEkgaGF2ZSBtdWx0aXBsZSBvYnNlcnZhdGlvbnMgaW4gb25lIHJvdywgdGhlIHNpemUgb2YgYSBuZXR3b3JrIGluIE1heSwgSnVuZSwgSnVseSBhbmQgQXVndXN0LiBJIGFtIHJlYWxseSBtaXNzaW5nIGEgdmFyaWFibGUgY2FsbGVkIGBtb250aGAgYW5kIGEgdmFyaWFibGUgY2FsbGVkIGBzaXplYC4gVGhpcyBkYXRhc2V0IG5lZWRzIHRvIGJlIHRpZGllZC4NCg0KSWYgeW91IHdvdWxkIGxpa2UgdG8gcnVuIHRoaXMgY29kZSBvbiB5b3VyIGNvbXB1dGVyIG1ha2Ugc3VyZSB0byByZW1vdmUgdGhlIGNvbW1lbnRzLCB0aGUgIywgaW4gZnJvbnQgb2YgdGhlIGxpbmVzIG9mIGNvZGUgZm9yIGluc3RhbGxpbmcgcGFja2FnZXMuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0V9DQojaW5zdGFsbC5wYWNrYWdlcygia2FibGVFeHRyYSIpDQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkoa2FibGVFeHRyYSkgI3RoaXMgaXMgYSBwYWNrYWdlIGZvciBtYWtpbmcgbmljZSB0YWJsZXMsIHRvIGNvbXBhcmUgdHJ5IGp1c3QgcnVubmluZyBoZWFkKHRlbXBvcmFsKQ0Ka2FibGUoaGVhZCh0ZW1wb3JhbCkpICU+JQ0KICBrYWJsZV9zdHlsaW5nKGJvb3RzdHJhcF9vcHRpb25zID0gYygic3RyaXBlZCIsICJob3ZlciIpKQ0KYGBgDQoNCkZvcnR1bmF0ZWx5LCBzb21lIHZlcnkgc21hcnQgcGVvcGxlIGhhdmUgd3JpdHRlbiBwYWNrYWdlcyB0aGF0IHRha2UgYWxsIHRoZSBwYWluIG91dCBvZiBkb2luZyB0aGlzLiBUaGUgYHRpZHlyYCBwYWNrYWdlIGNvbnRhaW5zIGZ1bmN0aW9ucyBzdWNoIGFzIGBwaXZvdF9sb25nZXJgIHdoaWNoIHRha2UgYW4gdW50aWR5IGRhdGFzZXQgYW5kIHRyYW5zZm9ybSBpdCBpbnRvIGEgdGlkeSBkYXRhc2V0Lg0KYGBge3J9DQojaW5zdGFsbC5wYWNrYWdlcygidGlkeXIiKQ0KbGlicmFyeSgidGlkeXIiKQ0KDQojdGFraW5nIHRoZSBmb3VyIGNvbHVtbnMgTWF5LCBKdW5lLCBKdWx5IGFuZCBBdWdzdCBhbmQgY3JlYXRpbmcgYSBjb2x1bW4gY2FsbGVkIG1vbnRoIGZvciB0aGUgbmFtZXMgYW5kIGEgY29sdW1uIGNhbGxlZCBzaXplIGZvciB0aGUgdmFsdWVzLg0Ka2FibGUodGVtcG9yYWxbMToyLF0gJT4lDQogIHBpdm90X2xvbmdlcihjKCJNYXkiLCAiSnVuZSIsICJKdWx5IiwgIkF1Z3VzdCIpLCBuYW1lc190byA9ICJtb250aCIsIHZhbHVlc190byA9InNpemUiLCBuYW1lc19wdHlwZXMgPSBsaXN0KG1vbnRoID0gZmFjdG9yKCkpKSkgJT4lDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIikpDQpgYGANCg0KVGEgZGEhIE5vdyB3ZSBoYXZlIHRpZHkgZGF0YS4gDQoNCioqKg0KDQojIyBUcmFuc2Zvcm0NCg0KIVtEYXRhIEFuYWx5c2lzIFdvcmtmbG93XShXb3JrZmxvdy5QTkcpDQoNCkNoZWNrIG91dCBIYWRsZXkgV2lja2hhbSdzIGJvb2sgZm9yIGluZm9ybWF0aW9uIG9uIFtwaXBpbmddKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovcGlwZXMuaHRtbCksIFt2ZWN0b3JzXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3ZlY3RvcnMuaHRtbCksIFtmdW5jdGlvbnNdKGh0dHBzOi8vcjRkcy5oYWQuY28ubnovZnVuY3Rpb25zLmh0bWwpIGFuZCBbaXRlcmF0aW9uLl0oaHR0cHM6Ly9yNGRzLmhhZC5jby5uei9pdGVyYXRpb24uaHRtbCkgVGhlc2UgYXJlIHRoZSBudXRzIGFuZCBib2x0cyBvZiBtYW5pcHVsYXRpbmcgYW5kIHRyYW5zZm9ybWluZyBkYXRhIGFuZCBJIHdvdWxkIHJlY29tbWVuZCBzcGVuZGluZyBzb21lIHRpbWUgb24gZWFjaCBzdWJqZWN0IHRvIGdldCBmbHVlbnQgaW4gZGF0YSBtYW5pcHVsYXRpb24uIEl0IHdpbGwgc2F2ZSB5b3UgbG9hZHMgb2YgdGltZSBhbmQgZXZlbiBiZSBlbmpveWFibGUgb25jZSB5b3UgaGl0IGEgY2VydGFpbiBlc2NhcGUgdmVsb2NpdHkuDQoNCg0KPiBUbDtkciBTdG9yaW5nIG9iamVjdHMgaW4gdGhlIGVudmlyb25tZW50IGlzIG5lY2Vzc2FyeSBidXQgY2FuIGJlIGEgYml0IGNsdXR0ZXJlZCwgcGlwaW5nIGhlbHBzIHRvIHJlZHVjZSB0aGUgY2x1dHRlci4gUGlwaW5nIHdvdWxkIGJyaW5nIGpveSB0byBNYXJpZSBLb25kby4NCg0KT25lIG5vdGUgb24gcGlwaW5nIGlzIHRoYXQgaXQgY2hhbmdlcyBob3cgeW91IHRoaW5rIGFib3V0IHVzaW5nIFIuIFdoZW5ldmVyIEkgd2FzIGNvbmR1Y3RpbmcgbXVsdGlwbGUgb3BlcmF0aW9ucyBvbiBhIGRhdGFzZXQgSSB3b3VsZCBzdG9yZSBlYWNoIHN0ZXAgb2YgdGhlIG9wZXJhdGlvbiBhcyBhbiBvYmplY3QsIGBhID0gN2AsICBgYS4xID0gNmAsIGBhLjIgPSA4YCwgaW4gdGhlIGVudmlyb25tZW50LiBJbiB0aGUgcHJvY2VzcyBvZiBjb25kdWN0aW5nIGFuIGFuYWx5c2lzIEkgY291bGQgZW5kIHVwIHdpdGggaHVuZHJlZHMgb2Ygb2JqZWN0cyBpbiB0aGUgZW52aXJvbm1lbnQgbW9zdCB3aXRoIGFyYnJpdGFyeSBuYW1lcywgc3VjaCBhcyBgYS4yYCBiZWluZyBgOGAsIHRoYXQgZG9uJ3QgbWFrZSBzZW5zZSB0byBhbnlvbmUgZWxzZSwgb3IgZXZlbiBteXNlbGYgYSB3ZWVrIGxhdGVyLiBJIHRoZW4gaGF2ZSB0byB0cnkgYW5kIGZpZ3VyZSBvdXQgd2hhdCBvYmplY3Qgc3RvcmVkIHdoYXQgdmFsdWUgYW5kIGRpc3Rpbmd1aXNoIGl0IGZyb20gdGhlIG15cmlhZCBvZiBvdGhlciB2ZXJ5IHNpbWlsYXIgb2JqZWN0cyBJIGNyZWF0ZWQsIHdhcyBJIGludGVyZXN0ZWQgaW4gYGFgIG9yIGBhLjFgPz8uIEl0IGlzIHZlcnkgZWFzeSB0byBjb25mdXNlIGBhYCBhbmQgYGEuMWAgaW4gYSBwaWVjZSBvZiBjb2RlIGFuZCBpZiBJIGRvbid0IGNhdGNoIGl0LCB0aGlzIGNvdWxkIGVmZmVjdCB0aGUgYW5hbHlzaXMuDQoNClBpcGluZyBoZWxwcyB0byBnZXQgYXJvdW5kIHRoaXMgaXNzdWUsIGFzIGVhY2ggcGllY2Ugb2YgY29kZSBjYW4gc3RhbmQgYWxvbmUgYW5kIGJlIHNlbGYgcmVmZXJlbnRpYWwgdG8gc29tZSBkZWdyZWUuIEl0IGJ1bmRsZXMgdXAgbXVsdGlwbGUgb3BlcmF0aW9ucywgcGlwaW5nIHRoZSByZXN1bHQgb2YgdGhlIHByZXZpb3VzIG9wZXJhdGlvbiB0byB0aGUgZm9sbG93aW5nIG9wZXJhdGlvbi4gVGhhdCB3YXkgd2UgZG9uJ3QgbmVlZCB0byBzdG9yZSBsb3RzIGFuZCBsb3RzIG9mIG9iamVjdHMgaW4gdGhlIGVudmlyb25tZW50LCB3ZSBjYW4gdXNlIHBpcGVzLCBkZWNsdXR0ZXJpbmcgdGhlIGVudmlyb25tZW50LCBtYWtpbmcgY29kZSBsZXNzIGNvbmZ1c2luZyBhbmQgaW5jcmVhc2luZyBlYXNlIG9mIHJlcHJvZHVjaWJpbHR5Lg0KDQpBIHNlY29uZCBwb2ludCBpcyB0aGF0IGNvZGUgaW4gYSBzY3JpcHQgaXMgbW9yZSAncmVhbCcgdGhhbiBhbnkgb2JqZWN0IHN0b3JlZCBpbiB0aGUgZW52aXJvbm1lbnQuIEZvciBhIG1vcmUgY29uc2lzZSBleHBsYW5hdGlvbiBvZiB0aGUgcmVhbG5lc3Mgb2Ygb2JqZWN0cyBhbmQgY29kZSBjaGVjayBvdXQgdGhpcyBbd2hhdCBpcyByZWFsXShodHRwczovL3I0ZHMuaGFkLmNvLm56L3dvcmtmbG93LXByb2plY3RzLmh0bWwjd2hhdC1pcy1yZWFsKSBzZWN0aW9uIG9mIHRoZSBSIGZvciBEYXRhIFNjaWVuY2UgYm9vay4NCg0KKioqDQoNCiMjIFZpc3VhbGlzYXRpb24NCg0KIVtEYXRhIEFuYWx5c2lzIFdvcmtmbG93XShXb3JrZmxvdy5QTkcpDQoNCkknbSBhIGNvbnZlcnQgdG8gZ2dwbG90LCBhZnRlciBoYXZpbmcgdXNlZCBiYXNlIFIgZnVuY3Rpb25zIHRvIGNyZWF0ZSBwbG90cyBmb3IgeWVhcnMuIFRvIGhlbHAgYXNzaXN0IGluIHlvdXIgY29udmVyc2lvbiBjaGVjayBvdXQgdGhpcyBbd2VicGFnZV0oaHR0cDovL3Itc3RhdGlzdGljcy5jby9Ub3A1MC1HZ3Bsb3QyLVZpc3VhbGl6YXRpb25zLU1hc3Rlckxpc3QtUi1Db2RlLmh0bWwpIG9mIHBsb3RzIGJ1aWx0IHVzaW5nIGdncGxvdCwgYXJlbid0IHRoZXkgYmVhdXRpZnVsPw0KDQoNCkFuIGV4YW1wbGUgb2YgdXNpbmcgZ2dwbG90DQpgYGB7ciBpbmNsdWRlPUZ9DQojc2V0dGluZyBteSBwYWxldHRlDQp1cmJhbnBhbGV0dGUgPC0gYygiIzQ1NzVCNCIsIiM3NEFERDEiLCIjQUJEOUU5IiwgImdyZXkiLA0KICAgICAgICAgICAgICAgICAiZ3JleSIsImdyZXkiLCJncmV5IikNCg0KI3NldHRpbmcgZmFjdG9yIGxldmVscyBzbyB0aGV5IHdpbGwgYmUgZGlzcGxheWVkIGFzIHN1Y2ggaW4gbGVnZW5kDQpmYWN0b3IodGVtcG9yYWwkbGV2ZWwsIGxldmVscyA9IGMoIkhpZ2ggVXJiYW4iLCJNZWRpdW0gVXJiYW4iLCJMb3cgVXJiYW4iLCJTZW1pIE5hdCIsIkxvdyBBZ3JpY3VsdHVyYWwiLCJNZWRpdW0gQWdyaWN1bHR1cmFsIiwiSGlnaCBBZ3JpY3VsdHVyYWwiKSkNCg0KDQojaW5zdGFsbGluZyBnZ3Bsb3QgYW5kIGxvYWRpbmcgbGlicmFyeQ0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KbGlicmFyeSgiZ2dwbG90MiIpDQpgYGANCg0KYGBge3J9DQp0ZW1wb3JhbCAlPiUNCiAgcGl2b3RfbG9uZ2VyKGMoIk1heSIsICJKdW5lIiwgIkp1bHkiLCAiQXVndXN0IiksIG5hbWVzX3RvID0gIm1vbnRoIiwgdmFsdWVzX3RvID0ic2l6ZSIsIG5hbWVzX3B0eXBlcyA9IGxpc3QobW9udGggPSBmYWN0b3IoKSkpICU+JQ0KICBnZ3Bsb3QoIGFlcyh4PW1vbnRoLCB5PXNpemUpKSArDQogICAgZ2VvbV9qaXR0ZXIoYWVzKGNvbD1sZXZlbCksIHNpemUgPSAzLCB3aWR0aCA9IDAuMSkgKw0KICAgICAgIHNjYWxlX2NvbG91cl9tYW51YWwodmFsdWVzPSB1cmJhbnBhbGV0dGUpICsNCiAgICAgICBjb29yZF9jYXJ0ZXNpYW4oeWxpbT1jKDAsMTUwKSkgKw0KICAgICAgIGxhYnModGl0bGUgPSAiIiwgeT0iU2l6ZSIsIHg9IiIsIGNhcHRpb24gPSAiIikgKw0KICAgICAgICB0aGVtZV9idygpDQpgYGANCg0KSW4gdGhpcyBbZG9jdW1lbnRdKGh0dHBzOi8vcnB1YnMuY29tL2Npd2hpdGUvNTg1OTQ4KSBJIGFtIGV4cGVyaW1lbnRpbmcgd2l0aCBnZ3Bsb3QgdHJ5aW5nIHRvIGNyZWF0ZSBiZWF1dGlmdWwgbG9va2luZyBQQ0EgcGxvdHMuIENoZWNrIG91dCB0aGUgbWFueSBkaWZmZXJlbnQgd2F5cyBvZiBjcmVhdGluIFBDQSBwbG90cyBhbmQgY29tcGFyZSB0aGUgYmFzZSBwbG90cyB0byB0aGUgbW9yZSBjdXN0b21pc2VkIGdncGxvdCBiYXNlZCBwbG90cy4gVGhlIGNvZGUgdG8gcmVjcmVhdGUgYWxsIHRoZSBwbG90cyBpbiB0aGUgZG9jdW1lbnQgY2FuIGJlIGRvd25sb2FkZWQgZnJvbSB0aGlzIFtnaXRodWIgcmVwb10oaHR0cHM6Ly9naXRodWIuY29tL2Npd2hpdGUvUENBL3RyZWUvbWFzdGVyKS4NCg0KDQoqKioNCg0KIyMgQ29tbXVuaWNhdGlvbg0KDQpSIGlzIGZhciBmcm9tIGp1c3QgYSBzdGF0aXN0aWNhbCBhbmFseXNpcyBwcm9ncmFtbWUuIEl0J3MgYW4gaW50ZWdyYXRlZCBkZXZlbG9wbWVudCBlbnZpcm9ubWVudCAoSURFKSBtZWFuaW5nIHRoYXQgaXQgY2FuIGJlIHVzZWQgdG8gY3JlYXRlLCBkZXZlbG9wIGFuZCBwdWJsaXNoIGRvY3VtZW50cywgd2lkZ2V0cywgd2VicGFnZXMsIGJvb2tzLCB0aGVzZXMsIHByZXNlbnRhdGlvbnMsIGJsb2dzIGFuZCB0aGUgbGlzdCBrZWVwcyBncm93aW5nLiBJdCdzIGxpa2UgUG93ZXJwb2ludCwgV29yZCwgTGF0ZVgsIFN0YXRhLCBXb3JkcHJlc3MgYW5kIFdpeCBhbGwgYnVuZGxlZCBpbnRvIG9uZS4gQmVzdCBvZiBhbGwgaXQncyBvcGVuIHNvdXJjZSwgZnJlZSBhbmQgaGFzIGEgY29tbXVuaXR5IHRoYXQgbG92ZXMgdG8gc2hhcmUgY29kZSBhbmQgYmVzdCBwcmFjdGljZXMsIG1lYW5pbmcgdGhhdCBpZiB5b3Ugc2VlIHNvbWUgcmVhbGx5IGNvb2wgZmlndXJlLCBhbmFseXNpcyBvciB3aWRnZXQsIHlvdSB3aWxsIG1vc3QgbGlrZWx5IGZpbmQgYSB0dXRvcmlhbCwgb3IgY29kZSwgdGhhdCB3aWxsIGhlbHAgeW91IHRvIHJlcHJvZHVjZSBpdC4gVGhlbiB5b3UgY2FuIG1vZGlmeSBpdCBob3dldmVyIHlvdSB3YW50IGFuZCBob3BlZnVsbHkgaW5zcGlyZSBzb21lb25lIHRvIHVzZSB5b3VyIGNvZGUuDQoNCiMjIyMgUiBNYXJrZG93bg0KDQpSIE1hcmtkb3duIGlzIGEgdmVyeSB2ZXJzdGFpbGUgZG9jdW1lbnQgZm9ybWF0dGluZyBsYW5ndWFnZS4gSXQgY2FuIGJlIHRyYW5zbGF0ZWQgaW50byBIVE1MICh0aGUgbGFuZ3VhZ2Ugb2Ygd2VicGFnZXMpLCBQREZzIHRocm91Z2ggTGF0ZVgsIFdvcmQgRG9jdW1lbnRzIGFuZCBudW1lcmlvdXMgcHJlc2VudGF0aW9uIHN0eWxlIGZvcm1hdHMuIFBlb3BsZSBoYXZlIGNyZWF0ZWQgd2Vic2l0ZXMgdXNpbmcgUiBNYXJrZG93biAoW2xpbmtdKGh0dHBzOi8vYW1iZXIucmJpbmQuaW8vKSwgW2xpbmtdKGh0dHBzOi8vcm9iamh5bmRtYW4uY29tKSBhbmQgW2xpbmtdKGh0dHA6Ly9saXZlZnJlZW9yZGljaG90b21pemUuY29tKSkgYW5kIGhhdmUgd3JpdHRlbiBib29rcyBpbiBSIE1hcmtkb3duLCBpbiBmYWN0IHRoZXJlIGlzIGEgYm9vayBhYm91dCBbUiBNYXJrZG93bl0oaHR0cHM6Ly9ib29rZG93bi5vcmcveWlodWkvcm1hcmtkb3duLykgd3JpdHRlbiBpbiBSIE1hcmtkb3duLg0KDQpUaGlzIGd1aWRlIGhhcyBiZWVuIHdyaXR0ZW4gdXNpbmcgUiBNYXJrZG93biEgIA0KDQpNYXJrZG93biByZXNvdXJjZXM6DQoNCiogW1F1aWNrIGd1aWRlXShodHRwczovL2dpdGh1Yi5jb20vYWRhbS1wL21hcmtkb3duLWhlcmUvd2lraS9NYXJrZG93bi1DaGVhdHNoZWV0KSBvbiBmb3JtYXRpbmcgb2YgZG9jdW1lbnRzICANCiogW0NoZWF0c2hlZXRdKGh0dHBzOi8vcnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTYvMDMvcm1hcmtkb3duLWNoZWF0c2hlZXQtMi4wLnBkZikNCg0KIyMjIyBSIFB1YnMNCg0KW1IgUHVic10oaHR0cHM6Ly9ycHVicy5jb20vKSBpcyBhIGZyZWUgcHVibGlzaGluZyBzZXJ2aWNlIHByb3ZpZGVkIGJ5IFJTdHVkaW8gdGhlIGNvbXBhbnkgc28gaXQgaXMgZWFzeSB0byBzaGFyZSBkb2N1bWVudHMgY3JlYXRlZCBpbiBSU3R1ZGlvIHRoZSBhcHBsaWNhdGlvbi4NCg0KWW91IGFyZSBhYmxlIHRvIHJlYWQgdGhpcyBkb2N1bWVudCBiZWNhdXNlIFJQdWJzIGlzIGhvc3RpbmcgaXQgb24gdGhlaXIgc2VydmVycy4NCg0KIyMjIyBTaGlueVINCg0KW1NoaW55Ul0oaHR0cHM6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pIGFsbG93cyB5b3UgdG8gYnVpbGQgaW50ZXJhY3RpdmUgd2ViIGFwcGxpY2F0aW9ucyBkaXJlY3RseSBmcm9tIFIuIFlvdSBjYW4gcHVibGlzaCBhbnkgYXBwcyB5b3UgY3JlYXRlIGZvciBmcmVlIGF0IGh0dHBzOi8vd3d3LnNoaW55YXBwcy5pby8uIA0KDQoNCmBgYHtyfQ0KI2luc3RhbGwucGFja2FnZXMoInNoaW55IikNCmxpYnJhcnkoc2hpbnkpDQpgYGANCg0KRGVmaW5lIFVJIGZvciBhcHBsaWNhdGlvbiB0aGF0IGRyYXdzIGEgaGlzdG9ncmFtDQoNCmBgYHtyfQ0KdWkgPC0gZmx1aWRQYWdlKA0KICAgDQogICAjIEFwcGxpY2F0aW9uIHRpdGxlDQogICB0aXRsZVBhbmVsKCJPbGQgRmFpdGhmdWwgR2V5c2VyIERhdGEiKSwNCiAgIA0KICAgIyBTaWRlYmFyIHdpdGggYSBzbGlkZXIgaW5wdXQgZm9yIG51bWJlciBvZiBiaW5zIA0KICAgc2lkZWJhckxheW91dCgNCiAgICAgIHNpZGViYXJQYW5lbCgNCiAgICAgICAgIHNsaWRlcklucHV0KCJiaW5zIiwNCiAgICAgICAgICAgICAgICAgICAgICJOdW1iZXIgb2YgYmluczoiLA0KICAgICAgICAgICAgICAgICAgICAgbWluID0gMSwNCiAgICAgICAgICAgICAgICAgICAgIG1heCA9IDUwLA0KICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSAzMCkNCiAgICAgICksDQogICAgICANCiAgICAgICMgU2hvdyBhIHBsb3Qgb2YgdGhlIGdlbmVyYXRlZCBkaXN0cmlidXRpb24NCiAgICAgIG1haW5QYW5lbCgNCiAgICAgICAgIHBsb3RPdXRwdXQoImRpc3RQbG90IikNCiAgICAgICkNCiAgICkNCikNCmBgYGANCg0KRGVmaW5lIHNlcnZlciBsb2dpYyByZXF1aXJlZCB0byBkcmF3IGEgaGlzdG9ncmFtDQpgYGB7cn0NCnNlcnZlciA8LSBmdW5jdGlvbihpbnB1dCwgb3V0cHV0KSB7DQogICANCiAgIG91dHB1dCRkaXN0UGxvdCA8LSByZW5kZXJQbG90KHsNCiAgICAgICMgZ2VuZXJhdGUgYmlucyBiYXNlZCBvbiBpbnB1dCRiaW5zIGZyb20gdWkuUg0KICAgICAgeCAgICA8LSBmYWl0aGZ1bFssIDJdIA0KICAgICAgYmlucyA8LSBzZXEobWluKHgpLCBtYXgoeCksIGxlbmd0aC5vdXQgPSBpbnB1dCRiaW5zICsgMSkNCiAgICAgIA0KICAgICAgIyBkcmF3IHRoZSBoaXN0b2dyYW0gd2l0aCB0aGUgc3BlY2lmaWVkIG51bWJlciBvZiBiaW5zDQogICAgICBoaXN0KHgsIGJyZWFrcyA9IGJpbnMsIGNvbCA9ICdkYXJrZ3JheScsIGJvcmRlciA9ICd3aGl0ZScpDQogICB9KQ0KfQ0KYGBgDQoNClRoZSByZW5kZXIgZnVuY3Rpb24gaXMgZm9yIHRoZSBwbG90cyBvciB0ZXh0IHRoYXQgeW91IHdvdWxkIGxpa2UgdG8gY2hhbmdlIHdoZW4gaW4gdGhlIGFwcA0KDQpSdW4gdGhlIGFwcGxpY2F0aW9uIA0KYGBge3J9DQojc2hpbnlBcHAodWkgPSB1aSwgc2VydmVyID0gc2VydmVyKQ0KYGBgDQoNCiMjIyMgTWFraW5nIHlvdXIgb3duIFdlYnNpdGUNCg0KTWFraW5nIHdlYnNpdGVzIHdpdGggR2l0ICANCg0KKiBNYWtlIHlvdXIgb3duIHdlYnNpdGUgd2l0aCBbR2l0IFBhZ2VzXShodHRwczovL3BhZ2VzLmdpdGh1Yi5jb20vKQ0KDQoqIFNlZSBteSBmaXJzdCBhdHRlbXB0OiBodHRwczovL2Npd2hpdGUuZ2l0aHViLmlvLw0KDQoqIFNlZSB0aGlzIGdvb2QgW3R1dG9yaWFsXShodHRwOi8vam1jZ2xvbmUuY29tL2d1aWRlcy9naXRodWItcGFnZXMvKQ0KDQoNCk1ha2luZyB3ZWJzaXRlcyB3aXRoIFINCg0KKiBTZWUgdGhpcyBbdHV0b3JpYWxdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi93ZWJzaXRlcy5odG1sKQ0KDQojIyMjIFdyaXRpbmcgYSBib29rIG9yIHRoZXNpcyB3aXRoIFINCg0KU2VlIHRoaXMgZ29vZCBbYmxvZ10oaHR0cHM6Ly9saXZlZnJlZW9yZGljaG90b21pemUuY29tLzIwMTgvMDkvMTQvb25lLXllYXItdG8tZGlzc2VydGF0ZS8pIG9uIGhvdyB0byB3cml0ZSBhIHRoZXNpcyBpbiBSDQoNClNlZSB0aGlzIFtib29rIGNoYXB0ZXJdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3lpaHVpL3JtYXJrZG93bi9ib29rcy5odG1sKSBvbiBob3cgdG8gd3JpdGUgYm9va3M=