Chap 2. Basic R and github with Rstudio

1 Basic start up

1.1 Starting point

The detailed git setup instructions must have been followed.

  • a github repository learning_R has been made in your account
  • the github repository has been cloned locally on your PC

Now we will learn how to use git in Rstudio to backup your code to the cloud server: github. Git version control system can be used for much more. But learning how to backup your data on the cloud is enough for a starter.

1.2 Create/Open a project in Rstudio

External resource: see also Project Management with RStudio

  1. Open Rstudio
  2. Browse to the directory where you cloned the learning_Rgit repository:

Note: after to re-open the project you will need to do:

1.3 Git used to backup notes and code via Rstudio

DiagrammeR::mermaid(
  "graph TB
      A[R project directory] --git add--> B[Staging area: preparation]
      B --git commit --> C[Local Repository : version control locally on PC] 
      C --push --> D[copy code to remote Repository : github]
      D --pull --> A
  ")

Fig. A simple workflow to backup your code and notes to github cloud server

In the git tab in one of the Rstudio panel:

The status (git status) allows you to check if files have been modified, if they are under-version control (tracked) or not.

  • yellow ? the file is not tracked = not currently version controlled and not ignored
  • red D deleted, the file has been deleted (or renamed outside of git)
  • rosa Rrenamed, the file has been renamed
  • blue M the file has been modified

Staging the files means preparing the files for saving them in the version in the version control system

  • You can stage one or several files.

- It is usually best to only stage several files when there is a logical block of work that has been done.

- you select (checked box) the file(s) to stage and then you can commit.

You need to write a commit message that explains what you have done since your previous commit.

Example: “Summary statistics for AMR detection in E.coli added.”

This create a “saving” point in your version control system,

  • Then you need to push: this sends the changes to the remote repository (github / cloud)

It is important to do this process often, because you create saving points, where you can go back, should you for some reason loose your code on your PC. Each time you commit, the commit message and the status of your files are registered in the version control system. A log in git system (you can look at it on github) can help you find back the files at the saving point you want.

If you work on different computer, or with different people, you might need to pull changes. Pull takes the changes from the cloud repository (github) and put them in the local repository. Note that we likely wont have time to do this during this course, but it is good to know that its possible to do so if needed.

You can learn how to do that using the git for novice lesson

1.4 Exercise to learn how to use git in R studio: modify .gitignore and push the changes to github

Note:

  • we wont version control raw and intermediary data, because we do not want them to finish in the public directory (among other things).
  • Raw data need to be backed-up on their own.
  • The raw data should NEVER be modified manually.
  • It should be possible to recover the processed data (eg. outlier removed, quality ensured) using code for pre-processing the raw data. Therefore processed data should be recoverable by solely re-running the pre-processing code.
  • Both the pre-processing code and the code that is further used for your analyses should be version controlled.

Modify or create .gitignore file in your project directory.

A .gitignore file is a file that tells git what files the version control system should ignore (eg. any files that contains raw data).

**/.*xlsx
**/.*csv
data/
results/
**.Rprofile
**.Rhistory
**.Rproj
**.sqlite

You will now be able to version control your code code and notes. If you do not want a file to be tracked, you need to add its path (relative to the path in the git repository) to the .gitigore file.

You can look at the ignoring things lesson to go further on how to ignore files.

2 Using a notebook in Rstudio: anatomy, text, code and rendering

A Notebook (here Rmarkdown) is a way to write code and text (notes, publications, reports) in the same document.

The text can be easily formatted using the “Markdown syntax”. See Those lesson to learn about Markdown syntax. For now, it will be sufficient to use this cheatcheat, have it open in a web-browser and use it !

A rendering (knit button) of the document allows to create different types of documents (html, pdf, word, slides).

Create a Rmarkdown document :

  • directory, in a sub-directory called notes (as you will use it to take notes during this course) choose eg, date_learningR.Rmd as file name. ISO date is eg. 2024-10-09. ! Do not use spaces in the file name, use underscore _ instead.

NB: code directory can be done at saving time, using right click on the mouse then new directory and write: notes

Fig: Anatomy of a R markdown document

2.1 Exercise Using a R markdown document

¨ Here is what we will now do:

PS: - Note the importance of formatting in YALM header - We wrote the tutorial for the course using Markdown - you can download the Rmd document (top right of the html file under code), and look at the code of this file in Rstudio to see how it was done.

We will do a series of small exercises to get you familiar with Rstudio and how code is written in R. It is important than you stop us if you do not understand what the code is doing

my_message <- "Hello World"
print(my_message)
my_message
typeof(my_message)
str(my_message)

See also Help in R studio

length(my_message)
nchar(my_message)

nb_char <- nchar(my_message)
nb_char
nb_char + 10
nb_char * nchar(my_message)

Bellow we show you a small introduction to: - assignment of objects in R - types of objects in R - character vectors - lists and sub-setting of list using indexes - R numbering stats at 1.

strsplit(my_message, split = "")

split_test <- strsplit(my_message, split = "") 
split_test
typeof(split_test)

split_test[1]
split_test[[1]]
split_test[[1]][1]
split_test[[1]][1:3]
  • transforming a string into a list of characters (types conversion)
  • reassignment of objects: replacement in memory
unlist(split_test)
split_test <-unlist(split_test)
split_test

split_test[1]
typeof(split_test)
length(split_test)
  • manipulations with functions that allows to transform objects
  • several operations are possible one after another
3*3+1
(3*3)+1

(3*(3+1))
  • brackets priority in maths is respected (mathematics).

Important to note the difference between a list and a vector.

2.2 Tricks

Some tricks to make your life easier.

Do not worry, if this goes fast. We will come back to that during the course. During the course you will have to pay attention to the key-words in bold, and figure out what they mean. Those words are usefull when you need to find on the web how you should do things further.

What you have to retain from here is that you need to have critical sense and check your results, to be sure the code is doing what we want it to do

2.3 Do not forget

3 Installing packages

Packages are a way to extend the functionality of R.

# A comment
# Another way to get help on a function
?install.packages
help("install.packages")
# F1 on : install.packages()
  • we can install here. It is a simple package that will allow reference to files in the project by their path in an easy way. It allow is great to have compatibility between linux and windows based systems.
# installing here package
install.packages("here", dependencies = TRUE)

Library is a function that allows you to load (into memory), and make the functions contained a package available to you, in the current R session.

The function here() gives the path of the project

# loading the package
library(here)
here()

NB: You can also install packages using Rstudio (BUT its better to keep a trace of what you have installed), so please do that using code.

You can call functions from packages that are not loaded to memory if they are installed on your system. This can be convenient if you only want to use one function, this avoids cluttering the memory of your computer.

Calling functions while specifying the package can be useful also, if you have loaded some packages that have functions with the same name. It is generally a good practice to do so (but takes time to do so …. )

here::here()

3.1 Packages and different ways to do the same thing

There are many packages in R, and there are usually many ways to do the same thing, some functions share also some similarities

library(dplyr)

# both functions allow to have a look at the structure of your data
glimpse(my_message) 
dplyr::glimpse(my_message) 

str(my_message)
library(stringr)

# both functions allow to split a string
str_split_1(my_message, pattern = "")
#stringr::str_split(my_message, pattern = "")

unlist(strsplit(my_message, split = ""))
#base::unlist(base::strsplit(my_message, split = ""))

3.2 Functions are objects

In packages, functions functions that do complex things, are often build using several simpler functions.

Here we show that there are several ways to obtain the same results.

my_strsplit1 <- function(char_var){
  temp <- strsplit(char_var, split = "")
  return(unlist(temp))
}

my_strsplit1(my_message)

Different way to write : (explain)

# return is factlatif if an oject is returned at creation
my_strsplit2 <- function(char_var){
  unlist(
    strsplit(char_var, split = "")
    )
}
my_strsplit2(my_message)

Using Pipes …

my_strsplit3 <- function(char_var){
  # R pipe  
  strsplit(char_var, split = "") |>
    unlist()
  # importance ()
}

my_strsplit3(my_message)

magrittr which is part of the tidyverse multiverse (a collection of packages that work well together) offers another type of pipe, which is the one that you will see most commonly used.

my_strsplit4 <- function(char_var){
  # R pipe  
  strsplit(char_var, split = "") %>%
    unlist
  # with %>% () can be omitted (though not advised)
}

my_strsplit4(my_message)

What you need to retain from here is:

  • there are different ways to do the same tings. R syntax is flexible and not always homogeneous. Do not learn a syntax

  • understand a syntax, check in the help when in doubt

  • there are several ways to obtain the result you want. While some are prettier or more efficient from other, the most important is that it does what you want it to do.

3.3 Do not forget

3.4 Exercise: Printing your code in a word document

unhide by clicking on the Code button for hints:

# Installing the package
install.packages("webshot2", dependencies = TRUE)

# Modifying the YALM header
output: 
  word_document

# Load the library at the beginning of your script, in a cell
library(webshot2)

4 Summary

  • This was a lot for today. You got a first overview of what can be done and why it can be worth to use this system, ultimately, this will help you being more efficient and do reproducible research.

  • Now we will actually use some of those things and repeat them a lot to do data analysis,.

5 When you are ready to go further

With Git (lessons) :

Using notebooks:

Quarto a new and very similar system to Rmarkdown (its a good alternative to use it), however, it might still be a bit young and sometimes workaround are necessary

Back to Index

LS0tDQp0aXRsZTogImByIHBhcmFtcyR0aXRsZWAiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiDQphdXRob3I6IEV2ZSBaZXlsIEZpc2tlYmVjayBhbmQgTWFkZWxhaW5lIE5vcnN0csO2bQ0KcGFyYW1zOg0KICB0aXRsZTogIkNoYXAgMi4gQmFzaWMgUiBhbmQgZ2l0aHViIHdpdGggUnN0dWRpbyIgDQogIHByb2plY3RfcGF0aDogImByIGhlcmU6OmhlcmUoKWAiDQogIA0Ka25pdDogKGZ1bmN0aW9uKGlucHV0RmlsZSwgZW5jb2RpbmcpIHsNCiAgcm1hcmtkb3duOjpyZW5kZXIoaW5wdXRGaWxlLCBlbmNvZGluZyA9IGVuY29kaW5nLCBvdXRwdXRfZGlyID0gIi4uLy4uL2RvY3MiKSB9KQ0KICAgIA0Kb3V0cHV0OiANCiAgcm1kZm9ybWF0czo6cmVhZHRoZWRvd246DQogICAgICBjc3M6IC4uL3N0eWxlLmNzcw0KICAgICAgc2VsZl9jb250YWluZWQ6IHRydWUNCiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUNCiAgICAgIHRvY19kZXB0aDogNA0KICAgICAgZGZfcHJpbnQ6IHBhZ2VkDQogICAgICBjb2RlX2ZvbGRpbmc6IGhpZGUNCiAgICAgIGF1dGhvcjogcGFyYW1zJGF1dGhvcg0KICAgICAgaGlnaGxpZ2h0OiBlc3ByZXNzbw0KICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlDQogIA0KZWRpdG9yX29wdGlvbnM6IA0KICBtYXJrZG93bjogDQogICAgd3JhcDogNzINCi0tLQ0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0NCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkNCmBgYA0KDQpgYGB7ciBsaWJyYXJpZXMsIGluY2x1ZGU9RkFMU0V9DQpsaWJyYXJ5KGhlcmUpDQpsaWJyYXJ5KERpYWdyYW1tZVIpDQpgYGANCg0KIyBCYXNpYyBzdGFydCB1cA0KDQojIyBTdGFydGluZyBwb2ludA0KDQpUaGUgZGV0YWlsZWQgZ2l0IHNldHVwIGluc3RydWN0aW9ucyBtdXN0IGhhdmUgYmVlbiBmb2xsb3dlZC4NCg0KLSAgIGEgZ2l0aHViIHJlcG9zaXRvcnkgYGxlYXJuaW5nX1JgIGhhcyBiZWVuIG1hZGUgaW4geW91ciBhY2NvdW50DQotICAgdGhlIGdpdGh1YiByZXBvc2l0b3J5IGhhcyBiZWVuIGNsb25lZCBsb2NhbGx5IG9uIHlvdXIgUEMNCg0KTm93IHdlIHdpbGwgbGVhcm4gaG93IHRvIHVzZSAqKmdpdCBpbiBSc3R1ZGlvIHRvIGJhY2t1cCB5b3VyIGNvZGUgdG8gdGhlDQpjbG91ZCBzZXJ2ZXI6IGdpdGh1YioqLiBHaXQgdmVyc2lvbiBjb250cm9sIHN5c3RlbSBjYW4gYmUgdXNlZCBmb3IgbXVjaA0KbW9yZS4gQnV0IGxlYXJuaW5nIGhvdyB0byBiYWNrdXAgeW91ciBkYXRhIG9uIHRoZSBjbG91ZCBpcyBlbm91Z2ggZm9yIGENCnN0YXJ0ZXIuDQoNCiMjIENyZWF0ZS9PcGVuIGEgcHJvamVjdCBpbiBSc3R1ZGlvDQoNCkV4dGVybmFsIHJlc291cmNlOiBzZWUgYWxzbyBbUHJvamVjdCBNYW5hZ2VtZW50IHdpdGgNClJTdHVkaW9dKGh0dHBzOi8vc3djYXJwZW50cnkuZ2l0aHViLmlvL3Itbm92aWNlLWdhcG1pbmRlci8wMi1wcm9qZWN0LWludHJvLmh0bWwpDQoNCjEuICBPcGVuIFJzdHVkaW8NCjIuICBCcm93c2UgdG8gdGhlIGRpcmVjdG9yeSB3aGVyZSB5b3UgY2xvbmVkIHRoZSBgbGVhcm5pbmdfUmBnaXQNCiAgICByZXBvc2l0b3J5Og0KDQotICAgWyBdIOKAgiBjcmVhdGUgOiBgRmlsZSAtPiBOZXcgUHJvamVjdCAtPiBFeGlzdGluZyBEaXJlY3RvcnlgDQoNCk5vdGU6IGFmdGVyIHRvIHJlLW9wZW4gdGhlIHByb2plY3QgeW91IHdpbGwgbmVlZCB0byBkbzoNCg0KLSAgIFsgXSDigIIgb3BlbiA6DQogICAgYEZpbGUgLT4gT3BlbiBQcm9qZWN0IC0+IGNob3NlIHRoZSBmaWxlIHdpdGggZXh0ZW5zaW9uIC5ScHJvamANCg0KIyMgR2l0IHVzZWQgdG8gYmFja3VwIG5vdGVzIGFuZCBjb2RlIHZpYSBSc3R1ZGlvDQoNCmBgYHtyfQ0KRGlhZ3JhbW1lUjo6bWVybWFpZCgNCiAgImdyYXBoIFRCDQogICAgICBBW1IgcHJvamVjdCBkaXJlY3RvcnldIC0tZ2l0IGFkZC0tPiBCW1N0YWdpbmcgYXJlYTogcHJlcGFyYXRpb25dDQogICAgICBCIC0tZ2l0IGNvbW1pdCAtLT4gQ1tMb2NhbCBSZXBvc2l0b3J5IDogdmVyc2lvbiBjb250cm9sIGxvY2FsbHkgb24gUENdIA0KICAgICAgQyAtLXB1c2ggLS0+IERbY29weSBjb2RlIHRvIHJlbW90ZSBSZXBvc2l0b3J5IDogZ2l0aHViXQ0KICAgICAgRCAtLXB1bGwgLS0+IEENCiAgIikNCmBgYA0KDQo8dT5GaWcuIEEgc2ltcGxlIHdvcmtmbG93IHRvIGJhY2t1cCB5b3VyIGNvZGUgYW5kIG5vdGVzIHRvIGdpdGh1YiBjbG91ZA0Kc2VydmVyPC91Pg0KDQo8IS0tIG1ha2UgZmlsZXMgPyBvciBjaGFuZ2Ugb3JkZXIgLS0+DQoNCkluIHRoZSBnaXQgdGFiIGluIG9uZSBvZiB0aGUgUnN0dWRpbyBwYW5lbDoNCg0KVGhlIHN0YXR1cyAoYGdpdCBzdGF0dXNgKSBhbGxvd3MgeW91IHRvIGNoZWNrIGlmIGZpbGVzIGhhdmUgYmVlbg0KbW9kaWZpZWQsIGlmIHRoZXkgYXJlIHVuZGVyLXZlcnNpb24gY29udHJvbCAodHJhY2tlZCkgb3Igbm90Lg0KDQotICAgeWVsbG93IGA/YCB0aGUgZmlsZSBpcyBub3QgdHJhY2tlZCA9IG5vdCBjdXJyZW50bHkgdmVyc2lvbg0KICAgIGNvbnRyb2xsZWQgYW5kIG5vdCBpZ25vcmVkDQotICAgcmVkIGBEYCBkZWxldGVkLCB0aGUgZmlsZSBoYXMgYmVlbiBkZWxldGVkIChvciByZW5hbWVkIG91dHNpZGUgb2YNCiAgICBnaXQpDQotICAgcm9zYSBgUmByZW5hbWVkLCB0aGUgZmlsZSBoYXMgYmVlbiByZW5hbWVkDQotICAgYmx1ZSBgTWAgdGhlIGZpbGUgaGFzIGJlZW4gbW9kaWZpZWQNCg0KU3RhZ2luZyB0aGUgZmlsZXMgbWVhbnMgcHJlcGFyaW5nIHRoZSBmaWxlcyBmb3Igc2F2aW5nIHRoZW0gaW4gdGhlDQp2ZXJzaW9uIGluIHRoZSB2ZXJzaW9uIGNvbnRyb2wgc3lzdGVtDQoNCi0gICBZb3UgY2FuIHN0YWdlIG9uZSBvciBzZXZlcmFsIGZpbGVzLg0KDQpcLSBJdCBpcyB1c3VhbGx5IGJlc3QgdG8gb25seSBzdGFnZSBzZXZlcmFsIGZpbGVzIHdoZW4gdGhlcmUgaXMgYQ0KbG9naWNhbCBibG9jayBvZiB3b3JrIHRoYXQgaGFzIGJlZW4gZG9uZS4NCg0KXC0geW91IHNlbGVjdCAoY2hlY2tlZCBib3gpIHRoZSBmaWxlKHMpIHRvIHN0YWdlIGFuZCB0aGVuIHlvdSBjYW4NCmNvbW1pdC4NCg0KWW91IG5lZWQgdG8gd3JpdGUgYSBjb21taXQgbWVzc2FnZSB0aGF0IGV4cGxhaW5zIHdoYXQgeW91IGhhdmUgZG9uZQ0Kc2luY2UgeW91ciBwcmV2aW91cyBjb21taXQuDQoNCj4gRXhhbXBsZTogIlN1bW1hcnkgc3RhdGlzdGljcyBmb3IgQU1SIGRldGVjdGlvbiBpbiBFLmNvbGkgYWRkZWQuIg0KDQpUaGlzIGNyZWF0ZSBhICJzYXZpbmciIHBvaW50IGluIHlvdXIgdmVyc2lvbiBjb250cm9sIHN5c3RlbSwNCg0KLSAgIFRoZW4geW91IG5lZWQgdG8gcHVzaDogdGhpcyBzZW5kcyB0aGUgY2hhbmdlcyB0byB0aGUgcmVtb3RlDQogICAgcmVwb3NpdG9yeSAoZ2l0aHViIC8gY2xvdWQpDQoNCkl0IGlzIGltcG9ydGFudCB0byBkbyB0aGlzIHByb2Nlc3Mgb2Z0ZW4sIGJlY2F1c2UgeW91IGNyZWF0ZSBzYXZpbmcNCnBvaW50cywgd2hlcmUgeW91IGNhbiBnbyBiYWNrLCBzaG91bGQgeW91IGZvciBzb21lIHJlYXNvbiBsb29zZSB5b3VyDQpjb2RlIG9uIHlvdXIgUEMuIEVhY2ggdGltZSB5b3UgY29tbWl0LCB0aGUgY29tbWl0IG1lc3NhZ2UgYW5kIHRoZSBzdGF0dXMNCm9mIHlvdXIgZmlsZXMgYXJlIHJlZ2lzdGVyZWQgaW4gdGhlIHZlcnNpb24gY29udHJvbCBzeXN0ZW0uIEEgbG9nIGluIGdpdA0Kc3lzdGVtICh5b3UgY2FuIGxvb2sgYXQgaXQgb24gZ2l0aHViKSBjYW4gaGVscCB5b3UgZmluZCBiYWNrIHRoZSBmaWxlcw0KYXQgdGhlIHNhdmluZyBwb2ludCB5b3Ugd2FudC4NCg0KSWYgeW91IHdvcmsgb24gZGlmZmVyZW50IGNvbXB1dGVyLCBvciB3aXRoIGRpZmZlcmVudCBwZW9wbGUsIHlvdSBtaWdodA0KbmVlZCB0byBwdWxsIGNoYW5nZXMuIFB1bGwgdGFrZXMgdGhlIGNoYW5nZXMgZnJvbSB0aGUgY2xvdWQgcmVwb3NpdG9yeQ0KKGdpdGh1YikgYW5kIHB1dCB0aGVtIGluIHRoZSBsb2NhbCByZXBvc2l0b3J5LiBOb3RlIHRoYXQgd2UgbGlrZWx5IHdvbnQNCmhhdmUgdGltZSB0byBkbyB0aGlzIGR1cmluZyB0aGlzIGNvdXJzZSwgYnV0IGl0IGlzIGdvb2QgdG8ga25vdyB0aGF0IGl0cw0KcG9zc2libGUgdG8gZG8gc28gaWYgbmVlZGVkLg0KPCEtLSB3ZSB3b250IGV4cGxhaW4gdGhpcyBzcGVjaWZpY2FsbHkgZm9yIG5vdyAtLT4NCg0KPiBZb3UgY2FuIGxlYXJuIGhvdyB0byBkbyB0aGF0IHVzaW5nIHRoZSBbZ2l0IGZvciBub3ZpY2UNCj4gbGVzc29uXShodHRwczovL3N3Y2FycGVudHJ5LmdpdGh1Yi5pby9naXQtbm92aWNlLykNCg0KIyMgRXhlcmNpc2UgdG8gbGVhcm4gaG93IHRvIHVzZSBnaXQgaW4gUiBzdHVkaW86IG1vZGlmeSBgLmdpdGlnbm9yZWAgYW5kIHB1c2ggdGhlIGNoYW5nZXMgdG8gZ2l0aHViDQoNCj4gTm90ZToNCj4NCj4gLSAgIHdlIHdvbnQgdmVyc2lvbiBjb250cm9sIHJhdyBhbmQgaW50ZXJtZWRpYXJ5IGRhdGEsIGJlY2F1c2Ugd2UgZG8NCj4gICAgIG5vdCB3YW50IHRoZW0gdG8gZmluaXNoIGluIHRoZSBwdWJsaWMgZGlyZWN0b3J5IChhbW9uZyBvdGhlcg0KPiAgICAgdGhpbmdzKS4NCj4gLSAgIFJhdyBkYXRhIG5lZWQgdG8gYmUgYmFja2VkLXVwIG9uIHRoZWlyIG93bi4NCj4gLSAgIFRoZSByYXcgZGF0YSBzaG91bGQgTkVWRVIgYmUgbW9kaWZpZWQgbWFudWFsbHkuDQo+IC0gICBJdCBzaG91bGQgYmUgcG9zc2libGUgdG8gcmVjb3ZlciB0aGUgcHJvY2Vzc2VkIGRhdGEgKGVnLiBvdXRsaWVyDQo+ICAgICByZW1vdmVkLCBxdWFsaXR5IGVuc3VyZWQpIHVzaW5nIGNvZGUgZm9yIHByZS1wcm9jZXNzaW5nIHRoZSByYXcNCj4gICAgIGRhdGEuIFRoZXJlZm9yZSBwcm9jZXNzZWQgZGF0YSBzaG91bGQgYmUgcmVjb3ZlcmFibGUgYnkgc29sZWx5DQo+ICAgICByZS1ydW5uaW5nIHRoZSBwcmUtcHJvY2Vzc2luZyBjb2RlLg0KPiAtICAgQm90aCB0aGUgcHJlLXByb2Nlc3NpbmcgY29kZSBhbmQgdGhlIGNvZGUgdGhhdCBpcyBmdXJ0aGVyIHVzZWQgZm9yDQo+ICAgICB5b3VyIGFuYWx5c2VzIHNob3VsZCBiZSB2ZXJzaW9uIGNvbnRyb2xsZWQuDQoNCioqTW9kaWZ5IG9yIGNyZWF0ZSBgLmdpdGlnbm9yZWAgZmlsZSBpbiB5b3VyIHByb2plY3QgZGlyZWN0b3J5LioqDQoNCkEgYC5naXRpZ25vcmVgIGZpbGUgaXMgYSBmaWxlIHRoYXQgdGVsbHMgZ2l0IHdoYXQgZmlsZXMgdGhlIHZlcnNpb24NCmNvbnRyb2wgc3lzdGVtIHNob3VsZCBpZ25vcmUgKGVnLiBhbnkgZmlsZXMgdGhhdCBjb250YWlucyByYXcgZGF0YSkuDQoNCi0gICBbIF0g4oCCVmVyaWZ5IHRoYXQgdGhvc2UgbGluZXMgYXJlIHByZXNlbnQgaW4gdGhlIFxgLmdpdGlnbm9yZSBmaWxlLA0KICAgIGlmIG5vdCwgYWRkIHRoZW0NCg0KYGBgIHRleHQNCioqLy4qeGxzeA0KKiovLipjc3YNCmRhdGEvDQpyZXN1bHRzLw0KKiouUnByb2ZpbGUNCioqLlJoaXN0b3J5DQoqKi5ScHJvag0KKiouc3FsaXRlDQpgYGANCg0KLSAgIFsgXSDigIJhZGQgLmdpdGlnbm9yZSB0byB0aGUgc3RhZ2luZyBhcmVhDQotICAgWyBdIOKAgmNvbW1pdCB0aGUgY2hhbmdlcyB3aXRoIGEgY29tbWl0IG1lc3NhZ2UNCi0gICBbIF0g4oCCcHVzaCB0aGUgY2hhbmdlcyB0byBnaXRodWINCi0gICBbIF0g4oCCdmVyaWZ5IGluIHlvdXIgZ2l0aHViIGFjY291bnQgdGhhdCB0aGUgY2hhbmdlcyBhcmUgaGF2ZSBiZWVuDQogICAgcmVjb3JkZWQNCg0KWW91IHdpbGwgbm93IGJlIGFibGUgdG8gdmVyc2lvbiBjb250cm9sIHlvdXIgY29kZSBjb2RlIGFuZCBub3Rlcy4gSWYgeW91DQpkbyBub3Qgd2FudCBhIGZpbGUgdG8gYmUgdHJhY2tlZCwgeW91IG5lZWQgdG8gYWRkIGl0cyBwYXRoIChyZWxhdGl2ZSB0bw0KdGhlIHBhdGggaW4gdGhlIGdpdCByZXBvc2l0b3J5KSB0byB0aGUgYC5naXRpZ29yZWAgZmlsZS4NCg0KWW91IGNhbiBsb29rIGF0IFt0aGUgaWdub3JpbmcgdGhpbmdzDQpsZXNzb25dKGh0dHBzOi8vc3djYXJwZW50cnkuZ2l0aHViLmlvL2dpdC1ub3ZpY2UvMDYtaWdub3JlLmh0bWwpIHRvIGdvDQpmdXJ0aGVyIG9uIGhvdyB0byBpZ25vcmUgZmlsZXMuDQoNCiMgVXNpbmcgYSBub3RlYm9vayBpbiBSc3R1ZGlvOiBhbmF0b215LCB0ZXh0LCBjb2RlIGFuZCByZW5kZXJpbmcNCg0KQSBOb3RlYm9vayAoaGVyZSBSbWFya2Rvd24pIGlzIGEgd2F5IHRvIHdyaXRlIGNvZGUgYW5kIHRleHQgKG5vdGVzLA0KcHVibGljYXRpb25zLCByZXBvcnRzKSBpbiB0aGUgc2FtZSBkb2N1bWVudC4NCg0KVGhlIHRleHQgY2FuIGJlIGVhc2lseSBmb3JtYXR0ZWQgdXNpbmcgdGhlICJNYXJrZG93biBzeW50YXgiLiBTZWUgVGhvc2UNCmxlc3NvbiB0byBsZWFybiBhYm91dCBbTWFya2Rvd24NCnN5bnRheF0oaHR0cHM6Ly91Y3NiY2FycGVudHJ5LmdpdGh1Yi5pby9SLW1hcmtkb3duLzAzLWhlYWRpbmdzLWxpc3RzL2luZGV4Lmh0bWwpLg0KRm9yIG5vdywgaXQgd2lsbCBiZSBzdWZmaWNpZW50IHRvIHVzZSB0aGlzDQpbY2hlYXRjaGVhdF0oaHR0cHM6Ly93d3cubWFya2Rvd25ndWlkZS5vcmcvY2hlYXQtc2hlZXQvKSwgaGF2ZSBpdCBvcGVuDQppbiBhIHdlYi1icm93c2VyIGFuZCB1c2UgaXQgIQ0KDQpBIHJlbmRlcmluZyAoa25pdCBidXR0b24pIG9mIHRoZSBkb2N1bWVudCBhbGxvd3MgdG8gY3JlYXRlIGRpZmZlcmVudA0KdHlwZXMgb2YgZG9jdW1lbnRzIChodG1sLCBwZGYsIHdvcmQsIHNsaWRlcykuDQoNCjwhLS0gVGhpcyBpcyB3aGF0IHdlIGhhdmUgZG9uZSB0byBjcmVhdGUgdGhpcyBjb3Vyc2UuIC0tPg0KDQpDcmVhdGUgYSBSbWFya2Rvd24gZG9jdW1lbnQgOg0KDQotICAgWyBdIOKAgk9wZW4gYSBuZXcgUm1hcmtkb3duIGRvY3VtZW50OiBgRmlsZSAtPiBOZXcgRmlsZSAtPiBSbWFya2Rvd25gDQoNCi0gICBbIF0g4oCCZmlsbCB0aWxlIGluZm9ybWF0aW9uLCBrZWVwIGFzIGh0bWwgKGBvdXRwdXQ6IHdvcmRfZG9jdW1lbnRgDQogICAgY2FuIGJlIHVzZWQgYXMgYWx0ZXJuYXRpdmUgdG8gY3JlYXRlIHdvcmQgZG9jdW1lbnRzKS4NCg0KLSAgIFsgXSDigIJzYXZlIHRoaXMgZmlsZSAob3IgeW91ciBwcm9qZWN0KSB5b3VyIGN1cnJlbnQgUiBwcm9qZWN0DQoNCiAgICBkaXJlY3RvcnksIGluIGEgc3ViLWRpcmVjdG9yeSBjYWxsZWQgbm90ZXMgKGFzIHlvdSB3aWxsIHVzZSBpdCB0bw0KICAgIHRha2Ugbm90ZXMgZHVyaW5nIHRoaXMgY291cnNlKSBjaG9vc2UgZWcsIGBkYXRlX2xlYXJuaW5nUi5SbWRgIGFzDQogICAgZmlsZSBuYW1lLiBJU08gZGF0ZSBpcyBlZy4gMjAyNC0xMC0wOS4gKiohIERvIG5vdCB1c2Ugc3BhY2VzIGluIHRoZQ0KICAgIGZpbGUgbmFtZSwgdXNlIHVuZGVyc2NvcmUgYF9gIGluc3RlYWQuKioNCg0KLSAgIFsgXSDigIJSZWFkIHdoYXQgdGhlIHRleHQgdGhhdCBhcHBlYXJzIGluIHRoZSBkb2N1bWVudC4NCg0KPiBOQjogY29kZSBkaXJlY3RvcnkgY2FuIGJlIGRvbmUgYXQgc2F2aW5nIHRpbWUsIHVzaW5nIHJpZ2h0IGNsaWNrIG9uDQo+IHRoZSBtb3VzZSB0aGVuIG5ldyBkaXJlY3RvcnkgYW5kIHdyaXRlOiBub3Rlcw0KDQo8IS0tIGV4cGxhaW4gdGhlIHRleHQgLS0+DQoNCiFbRmlnOiBBbmF0b215IG9mIGEgUiBtYXJrZG93bg0KZG9jdW1lbnRdKGh0dHBzOi8vbmthemEuZ2l0aHViLmlvL2ludHJvMlJib29rL2ltYWdlcy9ybV9jb21wb25lbnRzLnBuZykNCjwvYnI+DQoNCiMjIEV4ZXJjaXNlIFVzaW5nIGEgUiBtYXJrZG93biBkb2N1bWVudA0KDQrCqCBIZXJlIGlzIHdoYXQgd2Ugd2lsbCBub3cgZG86DQoNCi0gICBbIF0g4oCCIFRha2Ugbm90ZXMgaW4geW91ciBkb2N1bWVudCBvZiB3aGF0IHRoZSBkaWZmZXJlbnQgcGFydHMgb2YgYQ0KICAgIFJtYXJrZG93biBmaWxlIGFyZSBkb2luZyAoYXNrIHVzIHRvIHJlcGVhdCBhcyBtYW55IHRpbWVzIGFzDQogICAgbmVjZXNzYXJ5IGFuZCBoZWxwIGlmIHlvdSBkbyBub3Qga25vdyB3aGVyZSB0byBzdGFydCB3aXRoKQ0KLSAgIFsgXSDigIJFeHBlcmllbmNlIHdpdGggU291cmNlIHZzIFZpc3VhbCBtb2RlIG9uIHRvcCBvZiB5b3VyIG1hcmtkb3duDQogICAgZG9jdW1lbnQNCi0gICBbIF0g4oCCRXhwZXJpZW5jZSB3aXRoIHNvbWUgbWFya2Rvd24gZm9ybWF0dGluZw0KLSAgIFsgXSDigIJ3cml0ZSB0aGUgY29kZSBiZWxsb3cgaW4gYSBjb2RlIGNodW5rIChjZWxsKSBhbmQgYW5kIHJ1biBpdA0KICAgIChncmVlbiBhcnJvdykgPCEtLSBFeHBsYWluIGNvZGUgLS0+DQotICAgWyBdIOKAgktuaXQgdGhlIGRvY3VtZW50IHRvIHNlZSBob3cgaXQgY2FuIGNyZWF0ZSBhIGRvY3VtZW50IGZyb20geW91cg0KICAgIG5vdGVzDQoNClBTOiAtIE5vdGUgdGhlIGltcG9ydGFuY2Ugb2YgZm9ybWF0dGluZyBpbiBZQUxNIGhlYWRlciAtIFdlIHdyb3RlIHRoZQ0KdHV0b3JpYWwgZm9yIHRoZSBjb3Vyc2UgdXNpbmcgTWFya2Rvd24gLSB5b3UgY2FuIGRvd25sb2FkIHRoZSBSbWQNCmRvY3VtZW50ICh0b3AgcmlnaHQgb2YgdGhlIGh0bWwgZmlsZSB1bmRlciBjb2RlKSwgYW5kIGxvb2sgYXQgdGhlIGNvZGUNCm9mIHRoaXMgZmlsZSBpbiBSc3R1ZGlvIHRvIHNlZSBob3cgaXQgd2FzIGRvbmUuDQoNCldlIHdpbGwgZG8gYSBzZXJpZXMgb2Ygc21hbGwgZXhlcmNpc2VzIHRvIGdldCB5b3UgZmFtaWxpYXIgd2l0aCBSc3R1ZGlvDQphbmQgaG93IGNvZGUgaXMgd3JpdHRlbiBpbiBSLiAqKkl0IGlzIGltcG9ydGFudCB0aGFuIHlvdSBzdG9wIHVzIGlmIHlvdQ0KZG8gbm90IHVuZGVyc3RhbmQgd2hhdCB0aGUgY29kZSBpcyBkb2luZyoqDQo8IS0tIEV4cGxhaW4gYWxsIHRoZSBzdGVwcyBvZiB0aGUgY29kZSAtLT4NCg0KYGBge3IgZmlyc3Qgb2JqZWN0LCBlY2hvPVRSVUUsIGV2YWw9RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQ0KbXlfbWVzc2FnZSA8LSAiSGVsbG8gV29ybGQiDQpwcmludChteV9tZXNzYWdlKQ0KYGBgDQoNCjwhLS0gU29tZSBvdGhlciB3YXkgd2l0aCBjb2RlIC0tPg0KDQpgYGB7ciAgZWNobz1UUlVFLCBldmFsPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0NCm15X21lc3NhZ2UNCnR5cGVvZihteV9tZXNzYWdlKQ0Kc3RyKG15X21lc3NhZ2UpDQpgYGANCg0KLSAgIFsgXSDigIJSdW4gb25lIGxpbmUgb2YgY29kZSBhdCBpbiBhIGNlbGwgKGBjdHJsK0VudGVyYCBjdXJzb3IgYXQgdGhlDQogICAgZW5kIG9mIHRoZSBsaW5lLCBsb29rIGF0IHRoZSByZXN1bHRzIGluIFJzdHVkaW8gY29uc29sZSkNCi0gICBbIF0g4oCCQ2hlY2sgd2hhdCBkb2VzIG15X21lc3NhZ2Ugb2JqZWN0IGNvbnRhaW5zDQotICAgWyBdIOKAgkNoZWNrIHRoZSB0eXBlIG9mIG15X21lc3NhZ2Ugd2hhdCBpcyBpdHMgYWJicmV2aWF0aW9uID8NCi0gICBbIF0g4oCCQ2hlY2sgdGhlIHN0cnVjdHVyZSBvZiBteV9tZXNzYWdlIG9iamVjdFwNCi0gICBbIF0g4oCCQWNjZXNzIHRoZSBoZWxwIChjdXJzb3IgKyBGMSBvbiB0aGUgZnVuY3Rpb24pIDogcmVhZCBpbiB0aGUNCiAgICBoZWxwIHRhYiAoZmluZCB0aGUgY29ycmVjdCBwYW5lbCkgd2luZG93DQotICAgWyBdIOKAgnJ1biB0aGUgd2hvbGUgY2VsbC4gV2hhdCBpcyB0aGUgZGlmZmVyZW5jZSBvZiBydW5uaW5nIGxpbmUgYnkNCiAgICBsaW5lIHRoYW4gcnVubmluZyB0aGUgd2hvbGUgY2VsbCA/DQoNClNlZSBhbHNvIFtIZWxwIGluIFINCnN0dWRpb10oaHR0cHM6Ly9zd2NhcnBlbnRyeS5naXRodWIuaW8vci1ub3ZpY2UtZ2FwbWluZGVyLzAzLXNlZWtpbmctaGVscC5odG1sKQ0KDQpgYGB7ciAgZWNobz1UUlVFLCBldmFsPUZBTFNFLCAgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9DQpsZW5ndGgobXlfbWVzc2FnZSkNCm5jaGFyKG15X21lc3NhZ2UpDQoNCm5iX2NoYXIgPC0gbmNoYXIobXlfbWVzc2FnZSkNCm5iX2NoYXINCm5iX2NoYXIgKyAxMA0KbmJfY2hhciAqIG5jaGFyKG15X21lc3NhZ2UpDQpgYGANCg0KLSAgIFsgXSDigIJ3ZSBjYW4gcmV1c2Ugb2JqZWN0cyBmcm9tIGNlbGwgdG8gY2VsbCBpbiB0aGUgc2FtZSBub3RlYm9vaw0KLSAgIFsgXSDigIJ2ZWN0b3Igb2YgbGVuZ3RoIG9uZSBWUyBudW1iZXIgb2YgY2hhcmFjdGVycyBvZiBhIHN0cmluZyAuLi4uDQogICAgKGJlIHN1cmUgdG8gYWx3YXlzIGNoZWNrIGlmIHlvdXIgcmVzdWx0cyBtYWtlIHNlbnNlIGFjY29yZGluZyB0bw0KICAgIHdoYXQgeW91IGV4cGVjdCAhKQ0KDQpCZWxsb3cgd2Ugc2hvdyB5b3UgYSBzbWFsbCBpbnRyb2R1Y3Rpb24gdG86IC0gKiphc3NpZ25tZW50Kiogb2YNCioqb2JqZWN0cyoqIGluIFIgLSAqKnR5cGVzKiogb2YgKipvYmplY3RzKiogaW4gUiAtICoqY2hhcmFjdGVyKioNCioqdmVjdG9ycyoqIC0gKipsaXN0cyoqIGFuZCAqKnN1Yi1zZXR0aW5nKiogb2YgbGlzdCB1c2luZyAqKmluZGV4ZXMqKiAtDQpSIG51bWJlcmluZyBzdGF0cyBhdCAxLg0KDQpgYGB7ciBlY2hvPVRSVUUsIGV2YWw9RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQ0Kc3Ryc3BsaXQobXlfbWVzc2FnZSwgc3BsaXQgPSAiIikNCg0Kc3BsaXRfdGVzdCA8LSBzdHJzcGxpdChteV9tZXNzYWdlLCBzcGxpdCA9ICIiKSANCnNwbGl0X3Rlc3QNCnR5cGVvZihzcGxpdF90ZXN0KQ0KDQpzcGxpdF90ZXN0WzFdDQpzcGxpdF90ZXN0W1sxXV0NCnNwbGl0X3Rlc3RbWzFdXVsxXQ0Kc3BsaXRfdGVzdFtbMV1dWzE6M10NCmBgYA0KDQotICAgdHJhbnNmb3JtaW5nIGEgc3RyaW5nIGludG8gYSBsaXN0IG9mIGNoYXJhY3RlcnMgKCoqdHlwZXMNCiAgICBjb252ZXJzaW9uKiopDQotICAgKipyZWFzc2lnbm1lbnQqKiBvZiBvYmplY3RzOiByZXBsYWNlbWVudCBpbiBtZW1vcnkNCg0KYGBge3IgZWNobz1UUlVFLCBldmFsPUZBTFNFLCAgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9DQp1bmxpc3Qoc3BsaXRfdGVzdCkNCnNwbGl0X3Rlc3QgPC11bmxpc3Qoc3BsaXRfdGVzdCkNCnNwbGl0X3Rlc3QNCg0Kc3BsaXRfdGVzdFsxXQ0KdHlwZW9mKHNwbGl0X3Rlc3QpDQpsZW5ndGgoc3BsaXRfdGVzdCkNCmBgYA0KDQotICAgbWFuaXB1bGF0aW9ucyB3aXRoICoqZnVuY3Rpb25zKiogdGhhdCBhbGxvd3MgdG8gdHJhbnNmb3JtIG9iamVjdHMNCi0gICBzZXZlcmFsICoqb3BlcmF0aW9ucyoqIGFyZSBwb3NzaWJsZSBvbmUgYWZ0ZXIgYW5vdGhlcg0KDQpgYGB7ciBlY2hvPVRSVUUsIGV2YWw9RkFMU0UsICBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0NCjMqMysxDQooMyozKSsxDQoNCigzKigzKzEpKQ0KYGBgDQoNCi0gICBicmFja2V0cyBwcmlvcml0eSBpbiBtYXRocyBpcyByZXNwZWN0ZWQgKG1hdGhlbWF0aWNzKS4NCg0KPiBJbXBvcnRhbnQgdG8gbm90ZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGEgbGlzdCBhbmQgYSB2ZWN0b3IuDQoNCiMjIFRyaWNrcw0KDQpTb21lIHRyaWNrcyB0byBtYWtlIHlvdXIgbGlmZSBlYXNpZXIuIA0KDQotIFsgXSDigIJ1c2luZyBhcnJvdyhzKSB0byByZWNhbGwgdGhlIGxhc3QgcmVzdWx0KHMpIGluIGNvbnNvbGUgDQotIFsgXSDigIIgdXNpbmcgdGFiIHRvIGF1dG8gY29tcGxldGUNCnRoZSBuYW1lIG9mIHRoZSBvYmplY3QgDQotIFsgXSDigIIgYGN0cmwrTGAgKG9yIHRoZSBicm9vbSkgdG8gY2xlYXIgdGhlDQpjb25zb2xlDQoNCj4gRG8gbm90IHdvcnJ5LCBpZiB0aGlzIGdvZXMgZmFzdC4gV2Ugd2lsbCBjb21lIGJhY2sgdG8gdGhhdCBkdXJpbmcgdGhlDQo+IGNvdXJzZS4gRHVyaW5nIHRoZSBjb3Vyc2UgeW91IHdpbGwgaGF2ZSB0byBwYXkgYXR0ZW50aW9uIHRvIHRoZQ0KPiBrZXktd29yZHMgaW4gYm9sZCwgYW5kIGZpZ3VyZSBvdXQgd2hhdCB0aGV5IG1lYW4uIFRob3NlIHdvcmRzIGFyZQ0KPiB1c2VmdWxsIHdoZW4geW91IG5lZWQgdG8gZmluZCBvbiB0aGUgd2ViIGhvdyB5b3Ugc2hvdWxkIGRvIHRoaW5ncw0KPiBmdXJ0aGVyLg0KDQo8bWFyaz48Yj5XaGF0IHlvdSBoYXZlIHRvIHJldGFpbiBmcm9tIGhlcmUgaXMgdGhhdCB5b3UgbmVlZCB0byBoYXZlDQpjcml0aWNhbCBzZW5zZSBhbmQgY2hlY2sgeW91ciByZXN1bHRzLCB0byBiZSBzdXJlIHRoZSBjb2RlIGlzIGRvaW5nIHdoYXQNCndlIHdhbnQgaXQgdG8gZG88L2I+PC9tYXJrPg0KDQojIyBEbyBub3QgZm9yZ2V0DQoNCi0gICBbIF0g4oCCcGxlYXNlIGNoZWNrIHRoYXQgeW91IHNhdmVkIHlvdXIgbm90ZXMgYW5kIGRvIGFsbCB0aGUgcmVxdWlyZWQNCiAgICBzdGVwcyB0byBwdXNoIHRob3NlIG9uIGdpdGh1Yi4NCg0KIyBJbnN0YWxsaW5nIHBhY2thZ2VzDQoNClBhY2thZ2VzIGFyZSBhIHdheSB0byBleHRlbmQgdGhlIGZ1bmN0aW9uYWxpdHkgb2YgUi4NCg0KYGBge3IgaW5zdGFsbCBwYWNrYWdlcywgZWNobz1UUlVFLCBldmFsPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0NCiMgQSBjb21tZW50DQojIEFub3RoZXIgd2F5IHRvIGdldCBoZWxwIG9uIGEgZnVuY3Rpb24NCj9pbnN0YWxsLnBhY2thZ2VzDQpoZWxwKCJpbnN0YWxsLnBhY2thZ2VzIikNCiMgRjEgb24gOiBpbnN0YWxsLnBhY2thZ2VzKCkNCmBgYA0KDQotICAgd2UgY2FuIGluc3RhbGwgaGVyZS4gSXQgaXMgYSBzaW1wbGUgcGFja2FnZSB0aGF0IHdpbGwgYWxsb3cNCiAgICByZWZlcmVuY2UgdG8gZmlsZXMgaW4gdGhlIHByb2plY3QgYnkgdGhlaXIgcGF0aCBpbiBhbiBlYXN5IHdheS4gSXQNCiAgICBhbGxvdyBpcyBncmVhdCB0byBoYXZlIGNvbXBhdGliaWxpdHkgYmV0d2VlbiBsaW51eCBhbmQgd2luZG93cyBiYXNlZA0KICAgIHN5c3RlbXMuDQoNCmBgYHtyIGluc3RhbGwgaGVyZSwgZWNobz1UUlVFLCBldmFsPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0NCiMgaW5zdGFsbGluZyBoZXJlIHBhY2thZ2UNCmluc3RhbGwucGFja2FnZXMoImhlcmUiLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQ0KYGBgDQoNCkxpYnJhcnkgaXMgYSBmdW5jdGlvbiB0aGF0IGFsbG93cyB5b3UgdG8gbG9hZCAoaW50byBtZW1vcnkpLCBhbmQgbWFrZQ0KdGhlIGZ1bmN0aW9ucyBjb250YWluZWQgYSBwYWNrYWdlIGF2YWlsYWJsZSB0byB5b3UsIGluIHRoZSBjdXJyZW50IFINCnNlc3Npb24uIDwhLS0gRXhwbGFpbiBSc2Vzc2lvbiAtLT4NCg0KPHU+VGhlIGZ1bmN0aW9uIGhlcmUoKSBnaXZlcyB0aGUgcGF0aCBvZiB0aGUgcHJvamVjdDwvdT4NCg0KYGBge3IgbG9hZCBoZXJlLCBlY2hvPVRSVUUsIGV2YWw9RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQ0KIyBsb2FkaW5nIHRoZSBwYWNrYWdlDQpsaWJyYXJ5KGhlcmUpDQpoZXJlKCkNCmBgYA0KDQpOQjogWW91IGNhbiBhbHNvIGluc3RhbGwgcGFja2FnZXMgdXNpbmcgUnN0dWRpbyAoQlVUIGl0cyBiZXR0ZXIgdG8ga2VlcA0KYSB0cmFjZSBvZiB3aGF0IHlvdSBoYXZlIGluc3RhbGxlZCksIHNvIHBsZWFzZSBkbyB0aGF0IHVzaW5nIGNvZGUuDQoNCllvdSBjYW4gY2FsbCBmdW5jdGlvbnMgZnJvbSBwYWNrYWdlcyB0aGF0IGFyZSBub3QgbG9hZGVkIHRvIG1lbW9yeSBpZg0KdGhleSBhcmUgaW5zdGFsbGVkIG9uIHlvdXIgc3lzdGVtLiBUaGlzIGNhbiBiZSBjb252ZW5pZW50IGlmIHlvdSBvbmx5DQp3YW50IHRvIHVzZSBvbmUgZnVuY3Rpb24sIHRoaXMgYXZvaWRzIGNsdXR0ZXJpbmcgdGhlIG1lbW9yeSBvZiB5b3VyDQpjb21wdXRlci4NCg0KQ2FsbGluZyBmdW5jdGlvbnMgd2hpbGUgc3BlY2lmeWluZyB0aGUgcGFja2FnZSBjYW4gYmUgdXNlZnVsIGFsc28sIGlmDQp5b3UgaGF2ZSBsb2FkZWQgc29tZSBwYWNrYWdlcyB0aGF0IGhhdmUgZnVuY3Rpb25zIHdpdGggdGhlIHNhbWUgbmFtZS4gSXQNCmlzIGdlbmVyYWxseSBhIGdvb2QgcHJhY3RpY2UgdG8gZG8gc28gKGJ1dCB0YWtlcyB0aW1lIHRvIGRvIHNvIC4uLi4gKQ0KPCEtLSByZWNvbW1lbmRzIHRvIGRvIHNvIHdoZW4gd2UgYXJlIGhhcHB5IG9mIHdoYXQgb3VyIGNvZGUgZG9lcyAgLS0+DQoNCmBgYHtyIGVjaG89VFJVRSwgZXZhbD1GQUxTRSwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9DQpoZXJlOjpoZXJlKCkNCmBgYA0KDQojIyBQYWNrYWdlcyBhbmQgZGlmZmVyZW50IHdheXMgdG8gZG8gdGhlIHNhbWUgdGhpbmcNCg0KVGhlcmUgYXJlIG1hbnkgcGFja2FnZXMgaW4gUiwgYW5kIHRoZXJlIGFyZSB1c3VhbGx5IG1hbnkgd2F5cyB0byBkbyB0aGUNCnNhbWUgdGhpbmcsIHNvbWUgZnVuY3Rpb25zIHNoYXJlIGFsc28gc29tZSBzaW1pbGFyaXRpZXMNCg0KYGBge3IgZWNobz1UUlVFLCBldmFsPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0NCmxpYnJhcnkoZHBseXIpDQoNCiMgYm90aCBmdW5jdGlvbnMgYWxsb3cgdG8gaGF2ZSBhIGxvb2sgYXQgdGhlIHN0cnVjdHVyZSBvZiB5b3VyIGRhdGENCmdsaW1wc2UobXlfbWVzc2FnZSkgDQpkcGx5cjo6Z2xpbXBzZShteV9tZXNzYWdlKSANCg0Kc3RyKG15X21lc3NhZ2UpDQpgYGANCg0KYGBge3IgZWNobz1UUlVFLCBldmFsPUZBTFNFLCBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93In0NCmxpYnJhcnkoc3RyaW5ncikNCg0KIyBib3RoIGZ1bmN0aW9ucyBhbGxvdyB0byBzcGxpdCBhIHN0cmluZw0Kc3RyX3NwbGl0XzEobXlfbWVzc2FnZSwgcGF0dGVybiA9ICIiKQ0KI3N0cmluZ3I6OnN0cl9zcGxpdChteV9tZXNzYWdlLCBwYXR0ZXJuID0gIiIpDQoNCnVubGlzdChzdHJzcGxpdChteV9tZXNzYWdlLCBzcGxpdCA9ICIiKSkNCiNiYXNlOjp1bmxpc3QoYmFzZTo6c3Ryc3BsaXQobXlfbWVzc2FnZSwgc3BsaXQgPSAiIikpDQpgYGANCg0KLSAgIFsgXSDigIJjYW4geW91IGZpbmQgd2hpY2ggcGFja2FnZSBnbGltcHNlLCB1bmxpc3QsIHN0ciBmdW5jdGlvbnMgYXJlDQogICAgZnJvbSA/DQotICAgWyBdIOKAgmhvdyBjYW4geW91IGZpbmQgdGhpcyBpbmZvcm1hdGlvbiA/DQotICAgWyBdIOKAgnJlYWRpbmcgdGhlIGhlbHANCi0gICBbIF0g4oCCaW1wb3J0cyAoZ2xpbXBzZSB2cyBwa2c6OmdsaW1wc2UpDQoNCiMjIEZ1bmN0aW9ucyBhcmUgb2JqZWN0cw0KDQpJbiBwYWNrYWdlcywgZnVuY3Rpb25zIGZ1bmN0aW9ucyB0aGF0IGRvIGNvbXBsZXggdGhpbmdzLCBhcmUgb2Z0ZW4gYnVpbGQNCnVzaW5nIHNldmVyYWwgc2ltcGxlciBmdW5jdGlvbnMuDQoNCkhlcmUgd2Ugc2hvdyB0aGF0IHRoZXJlIGFyZSBzZXZlcmFsIHdheXMgdG8gb2J0YWluIHRoZSBzYW1lIHJlc3VsdHMuDQoNCmBgYHtyIGVjaG89VFJVRSwgZXZhbD1GQUxTRSwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9DQpteV9zdHJzcGxpdDEgPC0gZnVuY3Rpb24oY2hhcl92YXIpew0KICB0ZW1wIDwtIHN0cnNwbGl0KGNoYXJfdmFyLCBzcGxpdCA9ICIiKQ0KICByZXR1cm4odW5saXN0KHRlbXApKQ0KfQ0KDQpteV9zdHJzcGxpdDEobXlfbWVzc2FnZSkNCmBgYA0KDQpEaWZmZXJlbnQgd2F5IHRvIHdyaXRlIDogKGV4cGxhaW4pDQoNCmBgYHtyIGVjaG89VFJVRSwgZXZhbD1GQUxTRSwgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyJ9DQojIHJldHVybiBpcyBmYWN0bGF0aWYgaWYgYW4gb2plY3QgaXMgcmV0dXJuZWQgYXQgY3JlYXRpb24NCm15X3N0cnNwbGl0MiA8LSBmdW5jdGlvbihjaGFyX3Zhcil7DQogIHVubGlzdCgNCiAgICBzdHJzcGxpdChjaGFyX3Zhciwgc3BsaXQgPSAiIikNCiAgICApDQp9DQpteV9zdHJzcGxpdDIobXlfbWVzc2FnZSkNCg0KYGBgDQoNClVzaW5nIFBpcGVzIC4uLg0KDQpgYGB7ciBlY2hvPVRSVUUsIGV2YWw9RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQ0KbXlfc3Ryc3BsaXQzIDwtIGZ1bmN0aW9uKGNoYXJfdmFyKXsNCiAgIyBSIHBpcGUgIA0KICBzdHJzcGxpdChjaGFyX3Zhciwgc3BsaXQgPSAiIikgfD4NCiAgICB1bmxpc3QoKQ0KICAjIGltcG9ydGFuY2UgKCkNCn0NCg0KbXlfc3Ryc3BsaXQzKG15X21lc3NhZ2UpDQpgYGANCg0KW21hZ3JpdHRyXShodHRwczovL21hZ3JpdHRyLnRpZHl2ZXJzZS5vcmcvKSB3aGljaCBpcyBwYXJ0IG9mIHRoZQ0KdGlkeXZlcnNlIG11bHRpdmVyc2UgKGEgY29sbGVjdGlvbiBvZiBwYWNrYWdlcyB0aGF0IHdvcmsgd2VsbCB0b2dldGhlcikNCm9mZmVycyBhbm90aGVyIHR5cGUgb2YgcGlwZSwgd2hpY2ggaXMgdGhlIG9uZSB0aGF0IHlvdSB3aWxsIHNlZSBtb3N0DQpjb21tb25seSB1c2VkLg0KDQpgYGB7ciBlY2hvPVRSVUUsIGV2YWw9RkFMU0UsIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3cifQ0KbXlfc3Ryc3BsaXQ0IDwtIGZ1bmN0aW9uKGNoYXJfdmFyKXsNCiAgIyBSIHBpcGUgIA0KICBzdHJzcGxpdChjaGFyX3Zhciwgc3BsaXQgPSAiIikgJT4lDQogICAgdW5saXN0DQogICMgd2l0aCAlPiUgKCkgY2FuIGJlIG9taXR0ZWQgKHRob3VnaCBub3QgYWR2aXNlZCkNCn0NCg0KbXlfc3Ryc3BsaXQ0KG15X21lc3NhZ2UpDQpgYGANCg0KPHU+V2hhdCB5b3UgbmVlZCB0byByZXRhaW4gZnJvbSBoZXJlIGlzOjwvdT4NCg0KLSAgIHRoZXJlIGFyZSBkaWZmZXJlbnQgd2F5cyB0byBkbyB0aGUgc2FtZSB0aW5ncy4gUiBzeW50YXggaXMgZmxleGlibGUNCiAgICBhbmQgbm90IGFsd2F5cyBob21vZ2VuZW91cy4gRG8gbm90IGxlYXJuIGEgc3ludGF4DQoNCi0gICB1bmRlcnN0YW5kIGEgc3ludGF4LCBjaGVjayBpbiB0aGUgaGVscCB3aGVuIGluIGRvdWJ0DQoNCi0gICB0aGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRvIG9idGFpbiB0aGUgcmVzdWx0IHlvdSB3YW50LiBXaGlsZSBzb21lIGFyZQ0KICAgIHByZXR0aWVyIG9yIG1vcmUgZWZmaWNpZW50IGZyb20gb3RoZXIsIHRoZSBtb3N0IGltcG9ydGFudCBpcyB0aGF0IGl0DQogICAgZG9lcyB3aGF0IHlvdSB3YW50IGl0IHRvIGRvLg0KICAgIA0KIyMgRG8gbm90IGZvcmdldA0KDQotICAgWyBdIOKAgnNhdmUsIHB1c2ggeW91ciBub3RlcyBvbiBnaXRodWIsIGFuZCBjbG9zZSB5b3VyIG5vdGVzIGFuZA0KICAgIHByb2plY3QuDQoNCi0gICBbIF0g4oCCQXNrIHF1ZXN0aW9ucyBpZiB5b3UgZG8gbm90IGtub3cgd2hhdCB0byBhbnN3ZXIgdG8gcXVlc3Rpb25zDQogICAgUnN0dWRpbyBtaWdodCBhc2sNCg0KDQojIyBFeGVyY2lzZTogUHJpbnRpbmcgeW91ciBjb2RlIGluIGEgd29yZCBkb2N1bWVudA0KDQotICAgWyBdIOKAgiBJbnN0YWxsIGB3ZWJzaG90MmAgcGFja2FnZQ0KLSAgIFsgXSDigIIgd3JpdGUgdGhlIGNvZGUgdG8gbG9hZCB0aGUgYHdlYnNob3QyYCBwYWNrYWdlIGludG8gUiBtZW1vcnkNCi0gICBbIF0g4oCCIGNoYW5nZSB0aGUgb3V0cHV0IGZvcm1hdCBvZiB0aGUgUm1hcmtkb3duIGRvY3VtZW50IHlvdSBhcmUNCiAgICB1c2luZyBhbmQgd2hpY2ggY29udGFpbnMgeW91ciBub3Rlcz8gKGluIHRoZSBZQU1MIGhlYWRlcikgdG8NCiAgICBgd29yZF9kb2N1bWVudGAgYW5kIHByZXNzIGBLbml0YCBidXR0b24uIFdhcyBhIHdvcmQgZG9jdW1lbnQNCiAgICBwcm9kdWNlZCA/IChub3RlIHRoYXQgaW5kZW50YXRpb25zIGhhdmUgbWVhbmluZykNCg0KdW5oaWRlIGJ5IGNsaWNraW5nIG9uIHRoZSBgQ29kZWAgYnV0dG9uIGZvciBoaW50czoNCg0KYGBge3Igc29sdXRpb24gZXhlcmNpc2U6IFByaW50aW5nIHlvdXIgY29kZSBpbiBhIHdvcmQgZG9jdW1lbnQgLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9DQojIEluc3RhbGxpbmcgdGhlIHBhY2thZ2UNCmluc3RhbGwucGFja2FnZXMoIndlYnNob3QyIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkNCg0KIyBNb2RpZnlpbmcgdGhlIFlBTE0gaGVhZGVyDQpvdXRwdXQ6IA0KICB3b3JkX2RvY3VtZW50DQoNCiMgTG9hZCB0aGUgbGlicmFyeSBhdCB0aGUgYmVnaW5uaW5nIG9mIHlvdXIgc2NyaXB0LCBpbiBhIGNlbGwNCmxpYnJhcnkod2Vic2hvdDIpDQpgYGANCg0KIyBTdW1tYXJ5DQoNCi0gICBUaGlzIHdhcyBhIGxvdCBmb3IgdG9kYXkuIFlvdSBnb3QgYSBmaXJzdCBvdmVydmlldyBvZiB3aGF0IGNhbiBiZQ0KICAgIGRvbmUgYW5kIHdoeSBpdCBjYW4gYmUgd29ydGggdG8gdXNlIHRoaXMgc3lzdGVtLCB1bHRpbWF0ZWx5LCB0aGlzDQogICAgd2lsbCBoZWxwIHlvdSBiZWluZyBtb3JlIGVmZmljaWVudCBhbmQgZG8gcmVwcm9kdWNpYmxlIHJlc2VhcmNoLg0KDQotICAgTm93IHdlIHdpbGwgYWN0dWFsbHkgdXNlIHNvbWUgb2YgdGhvc2UgdGhpbmdzIGFuZCByZXBlYXQgdGhlbSBhIGxvdA0KICAgIHRvIGRvIGRhdGEgYW5hbHlzaXMsLg0KDQojIFdoZW4geW91IGFyZSByZWFkeSB0byBnbyBmdXJ0aGVyDQoNCldpdGggR2l0IChsZXNzb25zKSA6DQoNCi0gICBbR2l0IG5vdmljZSBsZXNzb25dKGh0dHBzOi8vc3djYXJwZW50cnkuZ2l0aHViLmlvL2dpdC1ub3ZpY2UvKSwNCiAgICBsZWFybiB5b3UgaG93IHRvIHVzZSB0aGUgY29tbWFuZCBsaW5lIGluIGEgdGVybWluYWwgd2l0aCBnaXQsIGhvdyB0bw0KICAgIHVzZSB2ZXJzaW9uIGNvbnRyb2wgdG8gcmVjb3ZlciBsb3N0IGRhdGEgb3IgcmVjb3ZlciBmb3IgY2hhbmdlcyB0aGF0DQogICAgbWFkZSB5b3VyIGNvZGUgbm90IHdvcmtpbmcsIGFuZCBpbnRyb2R1Y2VzIHlvdSBvbiBob3cgdG8gdXNlIG9mIHRoZQ0KICAgIGdpdGh1YiByZW1vdGUgcmVwb3NpdG9yeSB1c2luZyB0aGUgZ3JhcGhpY2FsIGludGVyZmFjZS4NCi0gICBbSW50cm9kdWN0aW9uIHRvIE9wZW4gRGF0YSBTY2llbmNlIHdpdGgNCiAgICBSXShodHRwczovL2NhcnBlbnRyaWVzLWluY3ViYXRvci5naXRodWIuaW8vb3Blbi1zY2llbmNlLXdpdGgtci8pDQogICAgcGFydGljdWxhcmx5IHRoZSBsZXNzb24gOA0KICAgIDxodHRwczovL2NhcnBlbnRyaWVzLWluY3ViYXRvci5naXRodWIuaW8vb3Blbi1zY2llbmNlLXdpdGgtci8wOC1naXQvaW5kZXguaHRtbD4NCi0gICBbR2l0IGFuZCBSc3R1ZGlvDQogICAgKHNob3J0KV0oaHR0cHM6Ly9kYXRhY2FycGVudHJ5Lm9yZy9yci12ZXJzaW9uLWNvbnRyb2wvMDMtZ2l0LWluLXJzdHVkaW8vaW5kZXguaHRtbCkNCg0KVXNpbmcgbm90ZWJvb2tzOg0KDQotICAgW0F1dGhvcmluZyBzY2llbnRpZmljIHB1YmxpY2F0aW9ucyB3aXRoIFINCiAgICBNYXJrZG93bl0oaHR0cHM6Ly91Y3NiY2FycGVudHJ5LmdpdGh1Yi5pby9SLW1hcmtkb3duLykNCg0KPiBRdWFydG8gYSBuZXcgYW5kIHZlcnkgc2ltaWxhciBzeXN0ZW0gdG8gUm1hcmtkb3duIChpdHMgYSBnb29kDQo+IGFsdGVybmF0aXZlIHRvIHVzZSBpdCksIGhvd2V2ZXIsIGl0IG1pZ2h0IHN0aWxsIGJlIGEgYml0IHlvdW5nIGFuZA0KPiBzb21ldGltZXMgd29ya2Fyb3VuZCBhcmUgbmVjZXNzYXJ5DQoNCi0gICBbSW50cm9kdWN0aW9uIHRvIHJlcHJvZHVjaWJsZSBwdWJsaWNhdGlvbnMgd2l0aA0KICAgIFJzdHVkaW9dKGh0dHBzOi8vdWNzYmNhcnBlbnRyeS5naXRodWIuaW8vUmVwcm9kdWNpYmxlLVB1YmxpY2F0aW9ucy13aXRoLVJTdHVkaW8tUXVhcnRvLykNCiAgICANCiAgICANCkJhY2sgdG8gW0luZGV4XShpbmRleC5odG1sKQ0K