Introduction

The purpose of this section is to review some of the key concepts in basic R usage, and statistical testing

  • Reading data into R
  • The data-frame representation of data in R
  • Selecting rows and columns from a data frame
  • Computing numerical summaries
  • Basic plotting
  • Getting help on functions in RStudio

About this tutorial

  • The traditional way to enter R commands is via the Terminal, or using the console in RStudio (bottom-left panel when RStudio opens for first time).
  • However, for this course we will use a relatively new feature called R-notebooks.
  • An R-notebook mixes plain text with R code
    • The R code can be run from inside the document and the results are displayed directly underneath
  • Each chunk of R code looks something like this.
  • Each line of R can be executed by clicking on the line and pressing CTRL and ENTER
  • Or you can press the green triangle on the right-hand side to run everything in the chunk
    • Try this now!
print("Hello World")
[1] "Hello World"
  • You can add R chunks by pressing CRTL + ALT + I
    • or using the Insert menu option
    • (can also include code from other languages such as Python or bash)

The document may also contain other formatting options that are used to render the HTML (or PDF, Word) output.

Here is some italic text, but we can also write in bold, or write things

  • in
  • a
  • list
    • which include sub-lists

Example Analysis

We will use a dataset from The University of Sheffield Mathematics and Statistics Help group ((MASH)(https://www.sheffield.ac.uk/mash/statistics2/anova)).

The data set Diet.csv contains information on 78 people who undertook one of three diets. There is background information such as age, gender (Female=0, Male=1) and height. The aim of the study was to see which diet was best for losing weight so the independent variable (group) is diet.

Reading and inspecting the data

Like other software (Word, Excel, Photoshop….), R has a default location where it will save files to and import data from. This is known as the working directory in R. You can query what R currently considers its working directory by executing the following R command:-

getwd()
[1] "/Volumes/Files/courses/cruk/LinearModelAndExtensions/git_linear-models-r"
/Volumes/Files/courses/cruk/LinearModelAndExtensions/git_linear-models-r

N.B.Here, a set of open and closed brackets () is used to run the getwd function with no arguments.
*Note if you are following this material on a Windows machine as opposed to a Linux or MacOS machine you will get a path like C:. If you want to use the complementing R command 'setwd()' to set the working directory you MUST escape the  i.e. setwd("C:\Users\Fred").
We can also list the files in a specific directory with:-

list.files("data/")
 [1] "amess.csv"                  "Assay.txt"                  "Bronchitis.csv"             "clinicalTrials.txt"        
 [5] "crab.csv"                   "diet.csv"                   "genotypes.txt"              "globalBreastCancerRisk.csv"
 [9] "lactoferrin.csv"            "myocardialinfarction.csv"   "OscillationIndex.txt"       "pollution.csv"             
[13] "protein-expression.csv"     "students.csv"               "treatments.txt"            
amess.csv

Assay.txt

Bronchitis.csv

clinicalTrials.txt

crab.csv

diet.csv

genotypes.txt

globalBreastCancerRisk.csv

lactoferrin.csv

myocardialinfarction.csv

OscillationIndex.txt

pollution.csv

protein-expression.csv

students.csv

treatments.txt

A useful sanity check is the file.exists function which will print TRUE is the file can be found in the working directory.

file.exists("data/diet.csv")
[1] TRUE
  • Assuming the file can be found, we can use the read.csv function to import the data. Other functions can be used to read tab-delimited files (read.delim) or a generic read.table function. A data frame object is created.
  • The file name diet.csv is the only argument to the function read.csv
    • arguments are listed inside the brackets
    • for functions requiring more than one argument (input), arguments are separated by commas
    • a function may have default values for some arguments; meaning they do not need to be specified
  • The characters <- are used to tell R to create a variable
    • without this, the data are not loaded into memory and you won't be able to work with them
  • If you get an error saying Error in file(file, “rt”) : cannot open the connection..., you might need to change your working directory or make sure the file name is typed correctly (R is case-sensitive)
  • Typing the name of an object will cause R to print the contents to the screen
diet <- read.csv("data/diet.csv")
diet

A note on importing your own data

If you are trying to read your own data, and encounter an error at this stage, you may need to consider if your data are in the correct form for analysis. Like most programming languages, R will struggle if your spreadsheet has been heavily formatted to include colours, formulas and special formatting.

These references will guide you through some of the pitfalls and common mistakes to avoid when formatting data

diet is an example of a data frame. The data frame object in R allows us to work with “tabular” data, like we might be used to dealing with in Excel, where our data can be thought of having rows and columns. The values in each column have to all be of the same type (i.e. all numbers or all text).

  • the summary function will provide a overview of the contents of each column in the table
    • the type of summary provided depends on the data type in each column
summary(diet)
       id           gender               age            height       diet.type         initial.weight   final.weight  
 Min.   : 1.00   Length:76          Min.   :16.00   Min.   :141.0   Length:76          Min.   :58.00   Min.   :53.00  
 1st Qu.:19.75   Class :character   1st Qu.:32.50   1st Qu.:163.8   Class :character   1st Qu.:66.00   1st Qu.:61.95  
 Median :40.50   Mode  :character   Median :39.00   Median :169.0   Mode  :character   Median :72.00   Median :68.95  
 Mean   :39.87                      Mean   :39.22   Mean   :170.8                      Mean   :72.29   Mean   :68.34  
 3rd Qu.:59.25                      3rd Qu.:47.25   3rd Qu.:175.2                      3rd Qu.:78.00   3rd Qu.:73.67  
 Max.   :78.00                      Max.   :60.00   Max.   :201.0                      Max.   :88.00   Max.   :84.50  
  • particular columns can be accessed using the $ operator
    • TIP RStudio will allow auto-complete using the Tab key
diet$gender
 [1] "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female"
[16] "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female"
[31] "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Female" "Male"   "Male"  
[46] "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"  
[61] "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"   "Male"  
[76] "Male"  
Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Female

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male

Male
diet$age
 [1] 22 46 55 33 50 50 37 28 28 45 60 48 41 37 44 37 41 43 20 51 31 54 50 48 16 37 30 29 51 35 21 22 36 20 35 45 58 37 31 35 56 48 41 39 31
[46] 40 50 43 25 52 42 39 40 51 38 54 33 45 37 44 40 37 39 31 36 47 29 37 31 26 40 35 49 28 40 51

We can create new columns based on existing ones

diet$weight.loss <- diet$final.weight - diet$initial.weight

Subsetting rows and columns is done using the [rows, columns] syntax; where rows and columns are vectors containing the rows and columns you want

  • you can choose to omit either vector to show all rows and columns. *However, you still need to remember the ,
diet[1:5,]
diet[,2:3]

Logical tests can be used to select rows. e.g. using ==, <, >

diet$diet.type == "A"
 [1]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[23] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE  TRUE
[45]  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE  TRUE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
[67] FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE FALSE
dietA <- diet[diet$diet.type == "A",]
dietA

Visualisation

All your favourite types of plot can be created in R

Plots can be constructed from vectors of numeric data, such as the data we get from a particular column in a data frame.

  • a histogram is commonly-used to examine the distribution of a particular variable
hist(diet$weight.loss)

  • a boxplot is often used to compare distributions visually
    • if given a data-frame, each column will be shown as a separate box
    • otherwise the formula syntax ~ is used to define x and y variables
boxplot(diet$weight.loss~diet$diet.type)

  • scatter plots can be constructed by given two vectors as arguments to plot
plot(diet$age,diet$initial.weight)

Lots of customisations are possible to enhance the appaerance of our plots. Not for the faint-hearted, the help pages ?plot and ?par give the full details. In short,

  • Axis labels, and titles can be specified as character strings.

  • R recognises many preset names as colours. To get a full list use colours(), or check this online reference.
    • can also use `Red, Green, Blue values; which you might get from a paint program
  • Plotting characters can be specified using a pre-defined number

boxplot(diet$weight.loss~diet$diet.type, 
        ylab="Weight Loss", 
        xlab="Diet Type",
        col=c("yellow","blue","red"),
        main="Weight Loss According to diet type")

You can get help on any of the functions that we will be using in this course by using the '?' or 'help()' commands. The help will appear in the help pane (usually bottom RH corner) .

?lm
help(lm)
Ci0tLQp0aXRsZTogIlJlY2FwIG9mIFN0YXRpc3RpY2FsIEFuYWx5c2lzIGluIFIiCmF1dGhvcjogQ2hhbmRyYSBDaGlsYW1ha3VyaSwgRG9taW5pcXVlLUxhdXJlbnQgQ291dHVyaWVyLCBSb2IgTmljaG9sbHMgCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCjwhLS0tIHJtYXJrZG93bjo6cmVuZGVyKCJ+L2NvdXJzZXMvY3J1ay9MaW5lYXJNb2RlbEFuZEV4dGVuc2lvbnMvMjAyMDAzMTAvUHJhY3RpY2Fscy9yLXJlY2FwLlJtZCIpIC0tLT4KPCEtLS0gcm1hcmtkb3duOjpyZW5kZXIoIi9Wb2x1bWVzL0ZpbGVzL2NvdXJzZXMvY3J1ay9MaW5lYXJNb2RlbEFuZEV4dGVuc2lvbnMvZ2l0X2xpbmVhci1tb2RlbHMtci9yLXJlY2FwLlJtZCIpIC0tLT4KCiMgSW50cm9kdWN0aW9uCgpUaGUgcHVycG9zZSBvZiB0aGlzIHNlY3Rpb24gaXMgdG8gcmV2aWV3IHNvbWUgb2YgdGhlIGtleSBjb25jZXB0cyBpbiBiYXNpYyBSIHVzYWdlLCBhbmQgc3RhdGlzdGljYWwgdGVzdGluZwoKLSBSZWFkaW5nIGRhdGEgaW50byBSCi0gVGhlIGRhdGEtZnJhbWUgcmVwcmVzZW50YXRpb24gb2YgZGF0YSBpbiBSCi0gU2VsZWN0aW5nIHJvd3MgYW5kIGNvbHVtbnMgZnJvbSBhIGRhdGEgZnJhbWUKLSBDb21wdXRpbmcgbnVtZXJpY2FsIHN1bW1hcmllcwotIEJhc2ljIHBsb3R0aW5nCi0gR2V0dGluZyBoZWxwIG9uIGZ1bmN0aW9ucyBpbiBSU3R1ZGlvCgoKIyMgQWJvdXQgdGhpcyB0dXRvcmlhbAoKLSBUaGUgdHJhZGl0aW9uYWwgd2F5IHRvIGVudGVyIFIgY29tbWFuZHMgaXMgdmlhIHRoZSBUZXJtaW5hbCwgb3IgdXNpbmcgdGhlIGNvbnNvbGUgaW4gUlN0dWRpbyAoYm90dG9tLWxlZnQgcGFuZWwgd2hlbiBSU3R1ZGlvIG9wZW5zIGZvciBmaXJzdCB0aW1lKS4KLSBIb3dldmVyLCBmb3IgdGhpcyBjb3Vyc2Ugd2Ugd2lsbCB1c2UgYSByZWxhdGl2ZWx5IG5ldyBmZWF0dXJlIGNhbGxlZCBSLW5vdGVib29rcy4KLSBBbiBSLW5vdGVib29rIG1peGVzIHBsYWluIHRleHQgd2l0aCBSIGNvZGUKICAgICsgVGhlIFIgY29kZSBjYW4gYmUgcnVuIGZyb20gaW5zaWRlIHRoZSBkb2N1bWVudCBhbmQgdGhlIHJlc3VsdHMgYXJlIGRpc3BsYXllZCBkaXJlY3RseSB1bmRlcm5lYXRoCi0gRWFjaCBjaHVuayBvZiBSIGNvZGUgbG9va3Mgc29tZXRoaW5nIGxpa2UgdGhpcy4KCmBgYHtyfQoKYGBgCgotIEVhY2ggbGluZSBvZiBSIGNhbiBiZSBleGVjdXRlZCBieSBjbGlja2luZyBvbiB0aGUgbGluZSBhbmQgcHJlc3NpbmcgQ1RSTCBhbmQgRU5URVIKLSBPciB5b3UgY2FuIHByZXNzIHRoZSBncmVlbiB0cmlhbmdsZSBvbiB0aGUgcmlnaHQtaGFuZCBzaWRlIHRvIHJ1biBldmVyeXRoaW5nIGluIHRoZSBjaHVuawogICAgKyBUcnkgdGhpcyBub3chCgpgYGB7cn0KcHJpbnQoIkhlbGxvIFdvcmxkIikKYGBgCgotIFlvdSBjYW4gYWRkIFIgY2h1bmtzIGJ5IHByZXNzaW5nIENSVEwgKyBBTFQgKyBJCiAgICArIG9yIHVzaW5nIHRoZSBJbnNlcnQgbWVudSBvcHRpb24KICAgICsgKGNhbiBhbHNvIGluY2x1ZGUgY29kZSBmcm9tIG90aGVyIGxhbmd1YWdlcyBzdWNoIGFzIFB5dGhvbiBvciBiYXNoKQoKVGhlIGRvY3VtZW50IG1heSBhbHNvIGNvbnRhaW4gb3RoZXIgZm9ybWF0dGluZyBvcHRpb25zIHRoYXQgYXJlIHVzZWQgdG8gcmVuZGVyIHRoZSBIVE1MIChvciBQREYsIFdvcmQpIG91dHB1dC4KCkhlcmUgaXMgc29tZSAqaXRhbGljKiB0ZXh0LCBidXQgd2UgY2FuIGFsc28gd3JpdGUgaW4gKipib2xkKiosIG9yIHdyaXRlIHRoaW5ncwoKLSBpbiAKLSBhIAotIGxpc3QKICAgICsgd2hpY2ggaW5jbHVkZSBzdWItbGlzdHMKCgoKIyBFeGFtcGxlIEFuYWx5c2lzCgpXZSB3aWxsIHVzZSBhIGRhdGFzZXQgZnJvbSBUaGUgVW5pdmVyc2l0eSBvZiBTaGVmZmllbGQgTWF0aGVtYXRpY3MgYW5kIFN0YXRpc3RpY3MgSGVscCBncm91cCAoKE1BU0gpKGh0dHBzOi8vd3d3LnNoZWZmaWVsZC5hYy51ay9tYXNoL3N0YXRpc3RpY3MyL2Fub3ZhKSkuCgo+IFRoZSBkYXRhIHNldCBEaWV0LmNzdiBjb250YWlucyBpbmZvcm1hdGlvbiBvbiA3OCBwZW9wbGUgd2hvIHVuZGVydG9vayBvbmUgb2YgdGhyZWUgZGlldHMuIFRoZXJlIGlzIGJhY2tncm91bmQgaW5mb3JtYXRpb24gc3VjaCBhcyBhZ2UsIGdlbmRlciAoRmVtYWxlPTAsIE1hbGU9MSkgYW5kIGhlaWdodC4gVGhlIGFpbSBvZiB0aGUgc3R1ZHkgd2FzIHRvIHNlZSB3aGljaCBkaWV0IHdhcyBiZXN0IGZvciBsb3Npbmcgd2VpZ2h0IHNvIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZSAoZ3JvdXApIGlzIGRpZXQuCgoKIyMgUmVhZGluZyBhbmQgaW5zcGVjdGluZyB0aGUgZGF0YQoKCkxpa2Ugb3RoZXIgc29mdHdhcmUgKFdvcmQsIEV4Y2VsLCBQaG90b3Nob3DigKYuKSwgUiBoYXMgYSBkZWZhdWx0IGxvY2F0aW9uIHdoZXJlIGl0IHdpbGwgc2F2ZSBmaWxlcyB0byBhbmQgaW1wb3J0IGRhdGEgZnJvbS4gVGhpcyBpcyBrbm93biBhcyB0aGUgd29ya2luZyBkaXJlY3RvcnkgaW4gUi4gWW91IGNhbiBxdWVyeSB3aGF0IFIgY3VycmVudGx5IGNvbnNpZGVycyBpdHMgd29ya2luZyBkaXJlY3RvcnkgYnkgZXhlY3V0aW5nIHRoZSBmb2xsb3dpbmcgUiBjb21tYW5kOi0KCmBgYHtyfQpnZXR3ZCgpCmBgYAoKKk4uQi4qSGVyZSwgYSBzZXQgb2Ygb3BlbiBhbmQgY2xvc2VkIGJyYWNrZXRzICgpIGlzIHVzZWQgdG8gcnVuIHRoZSBgZ2V0d2RgIGZ1bmN0aW9uIHdpdGggbm8gYXJndW1lbnRzLiAgIAoqTm90ZSBpZiB5b3UgYXJlIGZvbGxvd2luZyB0aGlzIG1hdGVyaWFsIG9uIGEgV2luZG93cyBtYWNoaW5lIGFzIG9wcG9zZWQgdG8gYSBMaW51eCBvciBNYWNPUyBtYWNoaW5lIAp5b3Ugd2lsbCBnZXQgYSBwYXRoIGxpa2UgQzpcVXNlcnNcRnJlZC4gSWYgeW91IHdhbnQgdG8gdXNlIHRoZSBjb21wbGVtZW50aW5nIFIgY29tbWFuZCAnc2V0d2QoKScgdG8gc2V0CnRoZSB3b3JraW5nIGRpcmVjdG9yeSB5b3UgTVVTVCBlc2NhcGUgdGhlIFwgaS5lLiBzZXR3ZCgiQzpcXFVzZXJzXFxGcmVkIikuICAKV2UgY2FuIGFsc28gbGlzdCB0aGUgZmlsZXMgaW4gYSBzcGVjaWZpYyBkaXJlY3Rvcnkgd2l0aDotCgpgYGB7cn0KbGlzdC5maWxlcygiZGF0YS8iKQpgYGAKCkEgdXNlZnVsIHNhbml0eSBjaGVjayBpcyB0aGUgZmlsZS5leGlzdHMgZnVuY3Rpb24gd2hpY2ggd2lsbCBwcmludCBUUlVFIGlzIHRoZSBmaWxlIGNhbiBiZSBmb3VuZCBpbiB0aGUgd29ya2luZyBkaXJlY3RvcnkuCgpgYGB7cn0KZmlsZS5leGlzdHMoImRhdGEvZGlldC5jc3YiKQpgYGAKCgoKLSBBc3N1bWluZyB0aGUgZmlsZSBjYW4gYmUgZm91bmQsIHdlIGNhbiB1c2UgdGhlIGByZWFkLmNzdmAgZnVuY3Rpb24gdG8gaW1wb3J0IHRoZSBkYXRhLiBPdGhlciBmdW5jdGlvbnMgY2FuIGJlIHVzZWQgdG8gcmVhZCB0YWItZGVsaW1pdGVkIGZpbGVzIChyZWFkLmRlbGltKSBvciBhIGdlbmVyaWMgcmVhZC50YWJsZSBmdW5jdGlvbi4gQSBkYXRhIGZyYW1lIG9iamVjdCBpcyBjcmVhdGVkLgotIFRoZSBmaWxlIG5hbWUgYGRpZXQuY3N2YCBpcyB0aGUgb25seSAqYXJndW1lbnQqIHRvIHRoZSBmdW5jdGlvbiBgcmVhZC5jc3ZgCiAgICArIGFyZ3VtZW50cyBhcmUgbGlzdGVkIGluc2lkZSB0aGUgYnJhY2tldHMKICAgICsgZm9yIGZ1bmN0aW9ucyByZXF1aXJpbmcgbW9yZSB0aGFuIG9uZSBhcmd1bWVudCAoaW5wdXQpLCBhcmd1bWVudHMgYXJlIHNlcGFyYXRlZCBieSBjb21tYXMKICAgICsgYSBmdW5jdGlvbiBtYXkgaGF2ZSBkZWZhdWx0IHZhbHVlcyBmb3Igc29tZSBhcmd1bWVudHM7IG1lYW5pbmcgdGhleSBkbyBub3QgbmVlZCB0byBiZSBzcGVjaWZpZWQKLSBUaGUgY2hhcmFjdGVycyBgPC1gIGFyZSB1c2VkIHRvIHRlbGwgUiB0byBjcmVhdGUgYSB2YXJpYWJsZQogICAgKyB3aXRob3V0IHRoaXMsIHRoZSBkYXRhIGFyZSBub3QgbG9hZGVkIGludG8gbWVtb3J5IGFuZCB5b3Ugd29uJ3QgYmUgYWJsZSB0byB3b3JrIHdpdGggdGhlbQotIElmIHlvdSBnZXQgYW4gZXJyb3Igc2F5aW5nIGBFcnJvciBpbiBmaWxlKGZpbGUsIOKAnHJ04oCdKSA6IGNhbm5vdCBvcGVuIHRoZSBjb25uZWN0aW9uLi4uYCwgeW91IG1pZ2h0IG5lZWQgdG8gY2hhbmdlIHlvdXIgd29ya2luZyBkaXJlY3Rvcnkgb3IgbWFrZSBzdXJlIHRoZSBmaWxlIG5hbWUgaXMgdHlwZWQgY29ycmVjdGx5IChSIGlzIGNhc2Utc2Vuc2l0aXZlKQotIFR5cGluZyB0aGUgbmFtZSBvZiBhbiBvYmplY3Qgd2lsbCBjYXVzZSBSIHRvIHByaW50IHRoZSBjb250ZW50cyB0byB0aGUgc2NyZWVuCgpgYGB7cn0KZGlldCA8LSByZWFkLmNzdigiZGF0YS9kaWV0LmNzdiIpCmRpZXQKYGBgCgojIyMgQSBub3RlIG9uIGltcG9ydGluZyB5b3VyIG93biBkYXRhCgpJZiB5b3UgYXJlIHRyeWluZyB0byByZWFkIHlvdXIgb3duIGRhdGEsIGFuZCBlbmNvdW50ZXIgYW4gZXJyb3IgYXQgdGhpcyBzdGFnZSwgeW91IG1heSBuZWVkIHRvIGNvbnNpZGVyIGlmIHlvdXIgZGF0YSBhcmUgaW4gdGhlIGNvcnJlY3QgZm9ybSBmb3IgYW5hbHlzaXMuIExpa2UgbW9zdCBwcm9ncmFtbWluZyBsYW5ndWFnZXMsIFIgd2lsbCBzdHJ1Z2dsZSBpZiB5b3VyIHNwcmVhZHNoZWV0IGhhcyBiZWVuIGhlYXZpbHkgZm9ybWF0dGVkIHRvIGluY2x1ZGUgY29sb3VycywgZm9ybXVsYXMgYW5kIHNwZWNpYWwgZm9ybWF0dGluZy4KClRoZXNlIHJlZmVyZW5jZXMgd2lsbCBndWlkZSB5b3UgdGhyb3VnaCBzb21lIG9mIHRoZSBwaXRmYWxscyBhbmQgY29tbW9uIG1pc3Rha2VzIHRvIGF2b2lkIHdoZW4gZm9ybWF0dGluZyBkYXRhCgotIFtGb3JtYXR0aW5nIGRhdGEgdGFibGVzIGluIFNwcmVhZHNoZWV0c10oaHR0cDovL3d3dy5kYXRhY2FycGVudHJ5Lm9yZy9zcHJlYWRzaGVldC1lY29sb2d5LWxlc3Nvbi8wMS1mb3JtYXQtZGF0YS5odG1sKQotIFtEYXRhIE9yZ2FuaXNhdGlvbiB0dXRvcmlhbCBieSBLYXJsIEJyb21hbl0oaHR0cDovL2ticm9tYW4ub3JnL2RhdGFvcmcvKQotIFtUaGUgUXVhcnR6IGd1aWRlIHRvIGJhZCBkYXRhXShodHRwczovL2dpdGh1Yi5jb20vUXVhcnR6L2JhZC1kYXRhLWd1aWRlL2Jsb2IvbWFzdGVyL1JFQURNRS5tZCkKCgoKCmBkaWV0YCBpcyBhbiBleGFtcGxlIG9mIGEgZGF0YSBmcmFtZS4gVGhlIGRhdGEgZnJhbWUgb2JqZWN0IGluIFIgYWxsb3dzIHVzIHRvIHdvcmsgd2l0aCDigJx0YWJ1bGFy4oCdIGRhdGEsIGxpa2Ugd2UgbWlnaHQgYmUgdXNlZCB0byBkZWFsaW5nIHdpdGggaW4gRXhjZWwsIHdoZXJlIG91ciBkYXRhIGNhbiBiZSB0aG91Z2h0IG9mIGhhdmluZyByb3dzIGFuZCBjb2x1bW5zLiBUaGUgdmFsdWVzIGluIGVhY2ggY29sdW1uIGhhdmUgdG8gYWxsIGJlIG9mIHRoZSBzYW1lIHR5cGUgKGkuZS4gYWxsIG51bWJlcnMgb3IgYWxsIHRleHQpLgoKLSB0aGUgYHN1bW1hcnlgIGZ1bmN0aW9uIHdpbGwgcHJvdmlkZSBhIG92ZXJ2aWV3IG9mIHRoZSBjb250ZW50cyBvZiBlYWNoIGNvbHVtbiBpbiB0aGUgdGFibGUKICAgICsgdGhlIHR5cGUgb2Ygc3VtbWFyeSBwcm92aWRlZCBkZXBlbmRzIG9uIHRoZSBkYXRhIHR5cGUgaW4gZWFjaCBjb2x1bW4KCmBgYHtyfQpzdW1tYXJ5KGRpZXQpCmBgYAoKLSBwYXJ0aWN1bGFyIGNvbHVtbnMgY2FuIGJlIGFjY2Vzc2VkIHVzaW5nIHRoZSBgJGAgb3BlcmF0b3IKICAgICsgKioqVElQKioqIFJTdHVkaW8gd2lsbCBhbGxvdyBhdXRvLWNvbXBsZXRlIHVzaW5nIHRoZSAqVGFiKiBrZXkKICAgIApgYGB7cn0KZGlldCRnZW5kZXIKZGlldCRhZ2UKCgpgYGAKCldlIGNhbiBjcmVhdGUgbmV3IGNvbHVtbnMgYmFzZWQgb24gZXhpc3Rpbmcgb25lcwoKYGBge3J9CmRpZXQkd2VpZ2h0Lmxvc3MgPC0gZGlldCRmaW5hbC53ZWlnaHQgLSBkaWV0JGluaXRpYWwud2VpZ2h0CgpgYGAKClN1YnNldHRpbmcgcm93cyBhbmQgY29sdW1ucyBpcyBkb25lIHVzaW5nIHRoZSBgW3Jvd3MsIGNvbHVtbnNdYCBzeW50YXg7IHdoZXJlIGByb3dzYCBhbmQgYGNvbHVtbnNgIGFyZSAqdmVjdG9ycyogY29udGFpbmluZyB0aGUgcm93cyBhbmQgY29sdW1ucyB5b3Ugd2FudAoKLSB5b3UgY2FuIGNob29zZSB0byBvbWl0IGVpdGhlciB2ZWN0b3IgdG8gc2hvdyBhbGwgcm93cyBhbmQgY29sdW1ucy4gKkhvd2V2ZXIsIHlvdSBzdGlsbCBuZWVkIHRvIHJlbWVtYmVyIHRoZSBgLGAKCmBgYHtyfQpkaWV0WzE6NSxdCmRpZXRbLDI6M10KYGBgCgpMb2dpY2FsIHRlc3RzIGNhbiBiZSB1c2VkIHRvIHNlbGVjdCByb3dzLiBlLmcuIHVzaW5nIGA9PWAsIGA8YCwgYD5gCgpgYGB7cn0KZGlldCRkaWV0LnR5cGUgPT0gIkEiCgpkaWV0QSA8LSBkaWV0W2RpZXQkZGlldC50eXBlID09ICJBIixdCmRpZXRBCmBgYAoKCgojIyBWaXN1YWxpc2F0aW9uIAoKQWxsIHlvdXIgZmF2b3VyaXRlIHR5cGVzIG9mIHBsb3QgY2FuIGJlIGNyZWF0ZWQgaW4gUgoKCi0gU2ltcGxlIHBsb3RzIGFyZSBzdXBwb3J0ZWQgaW4gdGhlICpiYXNlKiBkaXN0cmlidXRpb24gb2YgUiAod2hhdCB5b3UgZ2V0IGF1dG9tYXRpY2FsbHkgd2hlbiB5b3UgZG93bmxvYWQgUikuIAogICAgKyBgYm94cGxvdGAsIGBoaXN0YCwgYGJhcnBsb3RgLC4uLiBhbGwgb2Ygd2hpY2ggYXJlIGV4dGVuc2lvbnMgb2YgdGhlIGJhc2ljIGBwbG90YCBmdW5jdGlvbgotIE1hbnkgZGlmZmVyZW50IGN1c3RvbWlzYXRpb25zIGFyZSBwb3NzaWJsZQogICAgKyBjb2xvdXIsIG92ZXJsYXkgcG9pbnRzIC8gdGV4dCwgbGVnZW5kcywgbXVsdGktcGFuZWwgZmlndXJlcwotICoqKllvdSBuZWVkIHRvIHRoaW5rIGFib3V0IGhvdyBiZXN0IHRvIHZpc3VhbGlzZSB5b3VyIGRhdGEqKiogCiAgICArIGh0dHA6Ly93d3cuYmlvaW5mb3JtYXRpY3MuYmFicmFoYW0uYWMudWsvdHJhaW5pbmcuaHRtbCNmaWd1cmVkZXNpZ24KLSBSIGNhbm5vdCBwcmV2ZW50IHlvdSBmcm9tIGNyZWF0aW5nIGEgcGxvdHRpbmcgZGlzYXN0ZXI6IAogICAgKyBodHRwOi8vd3d3LmJ1c2luZXNzaW5zaWRlci5jb20vdGhlLTI3LXdvcnN0LWNoYXJ0cy1vZi1hbGwtdGltZS0yMDEzLTY/b3A9MSZJUj1UCgogICAgClBsb3RzIGNhbiBiZSBjb25zdHJ1Y3RlZCBmcm9tIHZlY3RvcnMgb2YgbnVtZXJpYyBkYXRhLCBzdWNoIGFzIHRoZSBkYXRhIHdlIGdldCBmcm9tIGEgcGFydGljdWxhciBjb2x1bW4gaW4gYSBkYXRhIGZyYW1lLgoKLSBhIGhpc3RvZ3JhbSBpcyBjb21tb25seS11c2VkIHRvIGV4YW1pbmUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIHBhcnRpY3VsYXIgdmFyaWFibGUKCmBgYHtyfQpoaXN0KGRpZXQkd2VpZ2h0Lmxvc3MpCmBgYAoKLSBhIGJveHBsb3QgaXMgb2Z0ZW4gdXNlZCB0byBjb21wYXJlIGRpc3RyaWJ1dGlvbnMgdmlzdWFsbHkKICAgICsgaWYgZ2l2ZW4gYSBkYXRhLWZyYW1lLCBlYWNoIGNvbHVtbiB3aWxsIGJlIHNob3duIGFzIGEgc2VwYXJhdGUgYm94CiAgICArIG90aGVyd2lzZSB0aGUgZm9ybXVsYSBzeW50YXggYH5gIGlzIHVzZWQgdG8gZGVmaW5lIHggYW5kIHkgdmFyaWFibGVzCgpgYGB7cn0KYm94cGxvdChkaWV0JHdlaWdodC5sb3NzfmRpZXQkZGlldC50eXBlKQoKYGBgCgotIHNjYXR0ZXIgcGxvdHMgY2FuIGJlIGNvbnN0cnVjdGVkIGJ5IGdpdmVuIHR3byB2ZWN0b3JzIGFzIGFyZ3VtZW50cyB0byBgcGxvdGAKCmBgYHtyfQpwbG90KGRpZXQkYWdlLGRpZXQkaW5pdGlhbC53ZWlnaHQpCmBgYAoKCipMb3RzKiBvZiBjdXN0b21pc2F0aW9ucyBhcmUgcG9zc2libGUgdG8gZW5oYW5jZSB0aGUgYXBwYWVyYW5jZSBvZiBvdXIgcGxvdHMuIE5vdCBmb3IgdGhlIGZhaW50LWhlYXJ0ZWQsIHRoZSBoZWxwIHBhZ2VzIGA/cGxvdGAgYW5kIGA/cGFyYCBnaXZlIHRoZSBmdWxsIGRldGFpbHMuIEluIHNob3J0LAoKLSBBeGlzIGxhYmVscywgYW5kIHRpdGxlcyBjYW4gYmUgc3BlY2lmaWVkIGFzIGNoYXJhY3RlciBzdHJpbmdzLiAKCi0gUiByZWNvZ25pc2VzIG1hbnkgcHJlc2V0IG5hbWVzIGFzIGNvbG91cnMuIFRvIGdldCBhIGZ1bGwgbGlzdCB1c2UgYGNvbG91cnMoKWAsIG9yIGNoZWNrIHRoaXMgW29ubGluZSByZWZlcmVuY2VdKGh0dHA6Ly93d3cuc3RhdC5jb2x1bWJpYS5lZHUvfnR6aGVuZy9maWxlcy9SY29sb3IucGRmKS4KICAgICsgY2FuIGFsc28gdXNlIGAqUiplZCwgKkcqcmVlbiwgKkIqbHVlIHZhbHVlczsgd2hpY2ggeW91IG1pZ2h0IGdldCBmcm9tIGEgcGFpbnQgcHJvZ3JhbQotIFBsb3R0aW5nIGNoYXJhY3RlcnMgY2FuIGJlIHNwZWNpZmllZCB1c2luZyBhIHByZS1kZWZpbmVkIG51bWJlcgoKYGBge3J9CmJveHBsb3QoZGlldCR3ZWlnaHQubG9zc35kaWV0JGRpZXQudHlwZSwgCiAgICAgICAgeWxhYj0iV2VpZ2h0IExvc3MiLCAKICAgICAgICB4bGFiPSJEaWV0IFR5cGUiLAogICAgICAgIGNvbD1jKCJ5ZWxsb3ciLCJibHVlIiwicmVkIiksCiAgICAgICAgbWFpbj0iV2VpZ2h0IExvc3MgQWNjb3JkaW5nIHRvIGRpZXQgdHlwZSIpCmBgYAoKWW91IGNhbiBnZXQgaGVscCBvbiBhbnkgb2YgdGhlIGZ1bmN0aW9ucyB0aGF0IHdlIHdpbGwgYmUgdXNpbmcgaW4gdGhpcyBjb3Vyc2UgYnkgdXNpbmcgdGhlICc/JyBvciAnaGVscCgpJyBjb21tYW5kcy4gVGhlIGhlbHAgd2lsbCBhcHBlYXIgaW4gdGhlIGhlbHAgcGFuZSAodXN1YWxseSBib3R0b20gUkggY29ybmVyKSAuCgpgYGB7cn0KP2xtCmBgYAoKYGBge3J9CmhlbHAobG0pCmBgYAo=