4. Programming in R

Motivation

From the previous exercise, you should see how we can easily adapt our markdown scripts:

  • e.g. ESR1 versus GATA3
  • But what if we want to analyse many genes?
  • It would be tedious to create a new markdown document for every gene
  • …and prone to error too

Introducing loops

  • Many programming languages have ways of doing the same thing many times, perhaps changing some variable each time. This is called looping
  • Loops are not used in R so often, because we can usually achieve the same thing using vector calculations
  • For example, to add two vectors together, we do not need to add each pair of elements one by one, we can just add the vectors
x <- 1:10
y <- 11:20
x+y
  • But there are some situations where R functions can not take vectors as input. For example, t.test() will only test one gene at a time
  • What if we wanted to test multiple genes?

For completeness, we can re-run the R code to import the data

geneAnnotation    <- read.delim("gene.description.txt",stringsAsFactors = FALSE)
patientMetadata <- read.delim("cancer.patients.txt",stringsAsFactors = FALSE)
normalizedValues    <- read.delim("gene.expression.txt")
  • We could run the following code to perform t-tests on the first two genes
t.test(as.numeric(normalizedValues[1,]) ~ factor(patientMetadata$er))
t.test(as.numeric(normalizedValues[2,]) ~ factor(patientMetadata$er))
  • But for many genes this will be boring to type, difficult to change, and prone to error
  • As we are doing the same thing multiple times, but with a different index each time, we can use a loop instead

Loops: Commands and flow control

  • R has two basic types of loop
    • a for loop: run some code on every value in a vector
    • a while loop: run some code while some condition is true (hardly ever used!)

for

for(i in 1:10) {
  print(i)
  }

while

i <- 1
while(i <= 10 ) {
  print(i)
  i <- i + 1
  }
  • Here’s how we might use a for loop to test the first 10 genes
for(i in 1:10) {
  
  t.test(as.numeric(normalizedValues[i,]) ~ factor(patientMetadata$er))
  
  }
  • This is exactly the same as:
i <- 1
t.test(as.numeric(normalizedValues[i,]) ~ factor(patientMetadata$er))
i <- 2
t.test(as.numeric(normalizedValues[i,]) ~ factor(patientMetadata$er))
i <- 3
###....etc....####

Storing results

However, this for loop is doing the calculations but not storing the results

  • The output of t.test() is an object with data placed in different slots
    • the names() of the object tells us what data we can retrieve, and what variable name to use
testResult <- t.test(as.numeric(normalizedValues[1,]) ~ factor(patientMetadata$er))
names(testResult)
testResult$statistic
  • When using a loop, we often create an empty “dummy” variable
  • This is used store the results at each stage of the loop
stats <- NULL
for(i in 1:10) {
  testResult <- t.test(as.numeric(normalizedValues[i,]) ~ factor(patientMetadata$er))
  stats[i] <- testResult$statistic
  }
stats

Practical application

Previously we have identified probes on chromosome 8

  • Lets say that we want to do a t-test for each gene on chromosome 8
chr8Genes <- geneAnnotation[geneAnnotation$Chromosome=="chr8",]
head(chr8Genes)
chr8GenesOrd <- chr8Genes[order(chr8Genes$Start),]
head(chr8GenesOrd)
  • The first step is to extract the expression values for chromosome 8 genes from our expression matrix, which has expression values for all genes
  • We can use the match function to tell us which rows in the matrix correspond to chromosome 8 genes
match(chr8GenesOrd$probe, rownames(normalizedValues))
chr8Expression <- normalizedValues[match(chr8GenesOrd$probe, rownames(normalizedValues)),]
dim(chr8Expression)

We are now ready to write the for loop

Exercise:

  • Create a for loop to perform to test if the expression level of each gene on chromosome 8 is significantly different between ER positive and negative samples
  • Store the p-value from each individual test
  • How many genes have a p-value < 0.05?
  • N.B. Our code will be more robust if we store the number of chromosome 8 genes as a variable
    • if the data change, the code should still run
chr8Genes <- geneAnnotation[geneAnnotation$Chromosome=="chr8",]
chr8GenesOrd <- chr8Genes[order(chr8Genes$Start),]
chr8Expression <- normalizedValues[match(chr8GenesOrd$probe, rownames(normalizedValues)),]
### Your Answer Here ###

Conditional branching: Commands and flow control

  • Use an if statement for any kind of condition testing
  • Different outcomes can be selected based on a condition within brackets
if (condition) {
  ... do this ...
  } else {
    ... do something else ...
    }
  • condition is any logical value, and can contain multiple conditions.
    • e.g. (a == 2 & b < 5), this is a compound conditional argument
  • The condition should return a single value of TRUE or FALSE

Other conditional tests

  • There are various tests that can check the type of data stored in a variable
    • these tend to be called is...().
      • try tab-complete on is.
is.numeric(10)
is.numeric("TEN")
is.character(10)
  • is.na() is useful for seeing if an NA value is found
    • cannot use == NA!
  • could be used to check if a gene symbol is found in the data before proceeding with statistical test
match("foo", geneAnnotation$HUGO.gene.symbol)
is.na(match("foo", geneAnnotation$HUGO.gene.symbol))
  • Using the for loop we wrote before, we could add some code to plot the expression of each gene
    • a boxplot would be ideal
  • However, we might only want plots for genes with a “significant” pvalue
  • Here’s how we can use an if statement to test for this
    • for each iteration of the the loop:
      1. test if the p-value from the test is below 0.05 or not
      2. if the p-value is less than 0.05 make a boxplot
      3. if not, do nothing
pdf("Chromosome8Genes.pdf")
pvals <- NULL
for (i in 1:18) {
  testResult <- t.test(as.numeric(chr8Expression[i,]) ~ factor(patientMetadata$er))
  pvals[i] <- testResult$p.value
  if(testResult$p.value < 0.05){
    boxplot(as.numeric(chr8Expression[i,]) ~ factor(patientMetadata$er),
            main=chr8Genes$HUGO.gene.symbol[i])
  }
} 
pvals
dev.off()

Code formatting avoids bugs!

Compare:

f<-26
while(f!=0){
print(letters[f])
f<-f-1}

to:

f <- 26
while(f != 0 ){
  print(letters[f])
  f <- f-1
  }
  • The code between brackets {} always is indented, this clearly separates what is executed once, and what is run multiple times
  • Trailing bracket } always alone on the line at the same indentation level as the initial bracket {
  • Use white spaces to divide the horizontal space between units of your code, e.g. around assignments, comparisons

Making a heatmap

  • A heatmap is often used to visualise how the expression level of a set of genes vary between conditions
  • Making the plot is actually quite straightforward
    • providing you have processed the data appropriately!
  • Let’s take a list of “most-variable genes”
    • see below for how we identified such genes
  • heatmap requires a matrix object rather than a data frame
genelist <- c("CLIC6","TFF3","PDZK1","SCUBE2","CYP2B6","HOXB13","NAT1","LY6D","SLC7A2")
probes   <- geneAnnotation$probe[match(genelist, geneAnnotation$HUGO.gene.symbol)]
probes
[1] "Contig46937_RC" "NM_003226"      "NM_002614"      "NM_020974"      "M29873"         "Contig28549"   
[7] "NM_000662"      "X82693"         "NM_003046"     
exprows  <- match(probes, rownames(normalizedValues))
heatmap(as.matrix(normalizedValues[exprows,]),labCol="")

Heatmap adjustments

  • We can provide a colour legend for the samples
  • Adjust colour of cells
  • Label the rows according to gene name
  • Don’t print the sample names as they are too cluttered
library(RColorBrewer)
sampcol <- rep("blue", ncol(normalizedValues))
sampcol[patientMetadata$er == 1 ] <- "yellow"
rbPal <- brewer.pal(10, "RdBu")
heatmap(as.matrix(normalizedValues[exprows,]), 
        ColSideColors = sampcol, 
        col=rbPal,
        labRow = genelist,labCol="")

  • see also
    • heatmap.2 from library(gplots); example(heatmap.2)
    • heatmap.plus from library(heatmap.plus); example(heatmap.plus)

(Supplementary) Choosing the genes for the heatmap

Often when using R you will come across a convenient shortcut function that can save you many hours of coding and frustration.

  • the genefilter package in Bioconductor contains many useful methods for filtering genomic datasets
  • you can install this package with the following commands
source("http://www.bioconductor.org/biocLite.R")
biocLite("genefilter")
  • the rowSds function will calculate the standard deviation for each row in a numeric matrix
  • the output will be vector, with each element being the standard deviation for a corresponding gene
library(genefilter)
geneVar <- rowSds(normalizedValues)
geneVar[1]
sd(normalizedValues[1,])
  • we can now order this matrix to get the subset with [] to get the indices of the most-variable genes (10 in this case).
  • the same indices can be used to subset the gene annotation data frame
    • we can do this because the annotation data frame and expression matrix are in the same order
topVar <- order(geneVar,decreasing = TRUE)[1:10]
topVar
geneAnnotation[topVar,]
LS0tCnRpdGxlOiAiSW50cm9kdWN0aW9uIHRvIFNvbHZpbmcgQmlvbG9naWNhbCBQcm9ibGVtcyBVc2luZyBSIC0gRGF5IDIiCmF1dGhvcjogTWFyayBEdW5uaW5nLCBTdXJhaiBNZW5vbiBhbmQgQWlvcmEgWmFiYWxhLiBPcmlnaW5hbCBtYXRlcmlhbCBieSBSb2JlcnQgU3Rvam5pxIcsCiAgTGF1cmVudCBHYXR0bywgUm9iIEZveSwgSm9obiBEYXZleSwgRMOhdmlkIE1vbG7DoXIgYW5kIElhbiBSb2JlcnRzCmRhdGU6ICdgciBmb3JtYXQoU3lzLnRpbWUoKSwgIkxhc3QgbW9kaWZpZWQ6ICVkICViICVZIilgJwpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogeWVzCiAgICB0b2NfZmxvYXQ6IHllcwotLS0KCiM0LiBQcm9ncmFtbWluZyBpbiBSCgojIyBNb3RpdmF0aW9uCgpGcm9tIHRoZSBwcmV2aW91cyBleGVyY2lzZSwgeW91IHNob3VsZCBzZWUgaG93IHdlIGNhbiBlYXNpbHkgYWRhcHQgb3VyIG1hcmtkb3duIHNjcmlwdHM6CgotIGUuZy4gRVNSMSB2ZXJzdXMgR0FUQTMKLSBCdXQgd2hhdCBpZiB3ZSB3YW50IHRvIGFuYWx5c2UgbWFueSBnZW5lcz8KLSBJdCB3b3VsZCBiZSB0ZWRpb3VzIHRvIGNyZWF0ZSBhIG5ldyBtYXJrZG93biBkb2N1bWVudCBmb3IgZXZlcnkgZ2VuZQotIC4uLmFuZCBwcm9uZSB0byBlcnJvciB0b28KCiMjSW50cm9kdWNpbmcgbG9vcHMKCi0gTWFueSBwcm9ncmFtbWluZyBsYW5ndWFnZXMgaGF2ZSB3YXlzIG9mIGRvaW5nIHRoZSBzYW1lIHRoaW5nIG1hbnkgdGltZXMsIHBlcmhhcHMgY2hhbmdpbmcgc29tZSB2YXJpYWJsZSBlYWNoIHRpbWUuIFRoaXMgaXMgY2FsbGVkICoqbG9vcGluZyoqCi0gTG9vcHMgYXJlIG5vdCB1c2VkIGluIFIgc28gb2Z0ZW4sIGJlY2F1c2Ugd2UgY2FuIHVzdWFsbHkgYWNoaWV2ZSB0aGUgc2FtZSB0aGluZyB1c2luZyB2ZWN0b3IgY2FsY3VsYXRpb25zCi0gRm9yIGV4YW1wbGUsIHRvIGFkZCB0d28gdmVjdG9ycyB0b2dldGhlciwgd2UgZG8gbm90IG5lZWQgdG8gYWRkIGVhY2ggcGFpciBvZiBlbGVtZW50cyBvbmUgYnkgb25lLCB3ZSBjYW4ganVzdCBhZGQgdGhlIHZlY3RvcnMKCmBgYHtyfQp4IDwtIDE6MTAKeSA8LSAxMToyMAp4K3kKYGBgCgotIEJ1dCB0aGVyZSBhcmUgc29tZSBzaXR1YXRpb25zIHdoZXJlIFIgZnVuY3Rpb25zIGNhbiBub3QgdGFrZSB2ZWN0b3JzIGFzIGlucHV0LiBGb3IgZXhhbXBsZSwgYHQudGVzdCgpYCB3aWxsIG9ubHkgdGVzdCBvbmUgZ2VuZSBhdCBhIHRpbWUKLSBXaGF0IGlmIHdlIHdhbnRlZCB0byB0ZXN0IG11bHRpcGxlIGdlbmVzPwoKRm9yIGNvbXBsZXRlbmVzcywgd2UgY2FuIHJlLXJ1biB0aGUgUiBjb2RlIHRvIGltcG9ydCB0aGUgZGF0YQpgYGB7cn0KZ2VuZUFubm90YXRpb24gICAgPC0gcmVhZC5kZWxpbSgiZ2VuZS5kZXNjcmlwdGlvbi50eHQiLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKcGF0aWVudE1ldGFkYXRhIDwtIHJlYWQuZGVsaW0oImNhbmNlci5wYXRpZW50cy50eHQiLHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkKbm9ybWFsaXplZFZhbHVlcyAgICA8LSByZWFkLmRlbGltKCJnZW5lLmV4cHJlc3Npb24udHh0IikKYGBgCgoKLSBXZSBjb3VsZCBydW4gdGhlIGZvbGxvd2luZyBjb2RlIHRvIHBlcmZvcm0gdC10ZXN0cyBvbiB0aGUgZmlyc3QgdHdvIGdlbmVzCgpgYGB7ciBldmFsPUZBTFNFfQp0LnRlc3QoYXMubnVtZXJpYyhub3JtYWxpemVkVmFsdWVzWzEsXSkgfiBmYWN0b3IocGF0aWVudE1ldGFkYXRhJGVyKSkKdC50ZXN0KGFzLm51bWVyaWMobm9ybWFsaXplZFZhbHVlc1syLF0pIH4gZmFjdG9yKHBhdGllbnRNZXRhZGF0YSRlcikpCgpgYGAKCi0gQnV0IGZvciBtYW55IGdlbmVzIHRoaXMgd2lsbCBiZSBib3JpbmcgdG8gdHlwZSwgZGlmZmljdWx0IHRvIGNoYW5nZSwgYW5kIHByb25lIHRvIGVycm9yCi0gQXMgd2UgYXJlIGRvaW5nIHRoZSBzYW1lIHRoaW5nIG11bHRpcGxlIHRpbWVzLCBidXQgd2l0aCBhIGRpZmZlcmVudCBpbmRleCBlYWNoIHRpbWUsIHdlIGNhbiB1c2UgYSAqKmxvb3AqKiBpbnN0ZWFkCgojI0xvb3BzOiBDb21tYW5kcyBhbmQgZmxvdyBjb250cm9sCi0gUiBoYXMgdHdvIGJhc2ljIHR5cGVzIG9mIGxvb3AKICAgICsgYSAqKmBmb3JgKiogbG9vcDogcnVuIHNvbWUgY29kZSBvbiBldmVyeSB2YWx1ZSBpbiBhIHZlY3RvcgogICAgKyBhICoqYHdoaWxlYCoqIGxvb3A6IHJ1biBzb21lIGNvZGUgd2hpbGUgc29tZSBjb25kaXRpb24gaXMgdHJ1ZSAoKmhhcmRseSBldmVyIHVzZWQhKikKICAgIApgZm9yYCAKYGBge3IgZXZhbD1GQUxTRX0KZm9yKGkgaW4gMToxMCkgewogIHByaW50KGkpCiAgfQoKYGBgCgpgd2hpbGVgCgpgYGB7ciBldmFsPUZBTFNFfQppIDwtIDEKd2hpbGUoaSA8PSAxMCApIHsKICBwcmludChpKQogIGkgPC0gaSArIDEKICB9CmBgYAoKCgotIEhlcmUncyBob3cgd2UgbWlnaHQgdXNlIGEgYGZvcmAgbG9vcCB0byB0ZXN0IHRoZSBmaXJzdCAxMCBnZW5lcwoKCmBgYHtyfQpmb3IoaSBpbiAxOjEwKSB7CiAgCiAgdC50ZXN0KGFzLm51bWVyaWMobm9ybWFsaXplZFZhbHVlc1tpLF0pIH4gZmFjdG9yKHBhdGllbnRNZXRhZGF0YSRlcikpCiAgCiAgfQpgYGAKCi0gVGhpcyBpcyAqZXhhY3RseSogdGhlIHNhbWUgYXM6CgpgYGB7ciBldmFsPUZBTFNFfQppIDwtIDEKdC50ZXN0KGFzLm51bWVyaWMobm9ybWFsaXplZFZhbHVlc1tpLF0pIH4gZmFjdG9yKHBhdGllbnRNZXRhZGF0YSRlcikpCmkgPC0gMgp0LnRlc3QoYXMubnVtZXJpYyhub3JtYWxpemVkVmFsdWVzW2ksXSkgfiBmYWN0b3IocGF0aWVudE1ldGFkYXRhJGVyKSkKaSA8LSAzCiMjIy4uLi5ldGMuLi4uIyMjIwpgYGAKCgoKIyMgU3RvcmluZyByZXN1bHRzCgpIb3dldmVyLCB0aGlzIGZvciBsb29wIGlzIGRvaW5nIHRoZSBjYWxjdWxhdGlvbnMgYnV0IG5vdCBzdG9yaW5nIHRoZSByZXN1bHRzCgotIFRoZSBvdXRwdXQgb2YgYHQudGVzdCgpYCBpcyBhbiBvYmplY3Qgd2l0aCBkYXRhIHBsYWNlZCBpbiBkaWZmZXJlbnQgc2xvdHMKICAgICsgdGhlIGBuYW1lcygpYCBvZiB0aGUgb2JqZWN0IHRlbGxzIHVzIHdoYXQgZGF0YSB3ZSBjYW4gcmV0cmlldmUsIGFuZCB3aGF0IHZhcmlhYmxlIG5hbWUgdG8gdXNlCgoKYGBge3J9CnRlc3RSZXN1bHQgPC0gdC50ZXN0KGFzLm51bWVyaWMobm9ybWFsaXplZFZhbHVlc1sxLF0pIH4gZmFjdG9yKHBhdGllbnRNZXRhZGF0YSRlcikpCm5hbWVzKHRlc3RSZXN1bHQpCnRlc3RSZXN1bHQkc3RhdGlzdGljCmBgYAoKCi0gV2hlbiB1c2luZyBhIGxvb3AsIHdlIG9mdGVuIGNyZWF0ZSBhbiBlbXB0eSAiZHVtbXkiIHZhcmlhYmxlCi0gVGhpcyBpcyB1c2VkIHN0b3JlIHRoZSByZXN1bHRzIGF0IGVhY2ggc3RhZ2Ugb2YgdGhlIGxvb3AKCmBgYHtyfQpzdGF0cyA8LSBOVUxMCmZvcihpIGluIDE6MTApIHsKICB0ZXN0UmVzdWx0IDwtIHQudGVzdChhcy5udW1lcmljKG5vcm1hbGl6ZWRWYWx1ZXNbaSxdKSB+IGZhY3RvcihwYXRpZW50TWV0YWRhdGEkZXIpKQogIHN0YXRzW2ldIDwtIHRlc3RSZXN1bHQkc3RhdGlzdGljCiAgfQpzdGF0cwpgYGAKCiMjIFByYWN0aWNhbCBhcHBsaWNhdGlvbgoKUHJldmlvdXNseSB3ZSBoYXZlIGlkZW50aWZpZWQgcHJvYmVzIG9uIGNocm9tb3NvbWUgOAoKLSBMZXRzIHNheSB0aGF0IHdlIHdhbnQgdG8gZG8gYSB0LXRlc3QgZm9yIGVhY2ggZ2VuZSBvbiBjaHJvbW9zb21lIDgKYGBge3J9CmNocjhHZW5lcyA8LSBnZW5lQW5ub3RhdGlvbltnZW5lQW5ub3RhdGlvbiRDaHJvbW9zb21lPT0iY2hyOCIsXQpoZWFkKGNocjhHZW5lcykKY2hyOEdlbmVzT3JkIDwtIGNocjhHZW5lc1tvcmRlcihjaHI4R2VuZXMkU3RhcnQpLF0KaGVhZChjaHI4R2VuZXNPcmQpCmBgYAoKLSBUaGUgZmlyc3Qgc3RlcCBpcyB0byBleHRyYWN0IHRoZSBleHByZXNzaW9uIHZhbHVlcyBmb3IgY2hyb21vc29tZSA4IGdlbmVzIGZyb20gb3VyIGV4cHJlc3Npb24gbWF0cml4LCB3aGljaCBoYXMgZXhwcmVzc2lvbiB2YWx1ZXMgZm9yIGFsbCBnZW5lcwotIFdlIGNhbiB1c2UgdGhlIGBtYXRjaGAgZnVuY3Rpb24gdG8gdGVsbCB1cyB3aGljaCByb3dzIGluIHRoZSBtYXRyaXggY29ycmVzcG9uZCB0byBjaHJvbW9zb21lIDggZ2VuZXMKCmBgYHtyfQptYXRjaChjaHI4R2VuZXNPcmQkcHJvYmUsIHJvd25hbWVzKG5vcm1hbGl6ZWRWYWx1ZXMpKQpjaHI4RXhwcmVzc2lvbiA8LSBub3JtYWxpemVkVmFsdWVzW21hdGNoKGNocjhHZW5lc09yZCRwcm9iZSwgcm93bmFtZXMobm9ybWFsaXplZFZhbHVlcykpLF0KZGltKGNocjhFeHByZXNzaW9uKQpgYGAKCldlIGFyZSBub3cgcmVhZHkgdG8gd3JpdGUgdGhlIGZvciBsb29wCgojIyBFeGVyY2lzZToKCi0gQ3JlYXRlIGEgZm9yIGxvb3AgdG8gcGVyZm9ybSB0byB0ZXN0IGlmIHRoZSBleHByZXNzaW9uIGxldmVsIG9mIGVhY2ggZ2VuZSBvbiBjaHJvbW9zb21lIDggaXMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnQgYmV0d2VlbiBFUiBwb3NpdGl2ZSBhbmQgbmVnYXRpdmUgc2FtcGxlcwotIFN0b3JlIHRoZSAqKipwLXZhbHVlKioqIGZyb20gZWFjaCBpbmRpdmlkdWFsIHRlc3QKLSBIb3cgbWFueSBnZW5lcyBoYXZlIGEgcC12YWx1ZSA8IDAuMDU/Ci0gTi5CLiBPdXIgY29kZSB3aWxsIGJlIG1vcmUgcm9idXN0IGlmIHdlIHN0b3JlIHRoZSBudW1iZXIgb2YgY2hyb21vc29tZSA4IGdlbmVzIGFzIGEgdmFyaWFibGUKICAgICsgaWYgdGhlIGRhdGEgY2hhbmdlLCB0aGUgY29kZSBzaG91bGQgc3RpbGwgcnVuCgpgYGB7cn0KY2hyOEdlbmVzIDwtIGdlbmVBbm5vdGF0aW9uW2dlbmVBbm5vdGF0aW9uJENocm9tb3NvbWU9PSJjaHI4IixdCmNocjhHZW5lc09yZCA8LSBjaHI4R2VuZXNbb3JkZXIoY2hyOEdlbmVzJFN0YXJ0KSxdCmNocjhFeHByZXNzaW9uIDwtIG5vcm1hbGl6ZWRWYWx1ZXNbbWF0Y2goY2hyOEdlbmVzT3JkJHByb2JlLCByb3duYW1lcyhub3JtYWxpemVkVmFsdWVzKSksXQojIyMgWW91ciBBbnN3ZXIgSGVyZSAjIyMKCmBgYAoKCgoKIyNDb25kaXRpb25hbCBicmFuY2hpbmc6IENvbW1hbmRzIGFuZCBmbG93IGNvbnRyb2wKCi0gVXNlIGFuIGBpZmAgc3RhdGVtZW50IGZvciBhbnkga2luZCBvZiBjb25kaXRpb24gdGVzdGluZwotIERpZmZlcmVudCBvdXRjb21lcyBjYW4gYmUgc2VsZWN0ZWQgYmFzZWQgb24gYSBjb25kaXRpb24gd2l0aGluIGJyYWNrZXRzCgpgYGAKaWYgKGNvbmRpdGlvbikgewogIC4uLiBkbyB0aGlzIC4uLgogIH0gZWxzZSB7CiAgICAuLi4gZG8gc29tZXRoaW5nIGVsc2UgLi4uCiAgICB9CmBgYAoKLSBgY29uZGl0aW9uYCBpcyBhbnkgbG9naWNhbCB2YWx1ZSwgYW5kIGNhbiBjb250YWluIG11bHRpcGxlIGNvbmRpdGlvbnMuIAogICAgKyBlLmcuIGAoYSA9PSAyICYgYiA8IDUpYCwgdGhpcyBpcyBhIGNvbXBvdW5kIGNvbmRpdGlvbmFsIGFyZ3VtZW50Ci0gVGhlIGNvbmRpdGlvbiBzaG91bGQgcmV0dXJuIGEgKnNpbmdsZSogdmFsdWUgb2YgYFRSVUVgIG9yIGBGQUxTRWAKICAgIAogICAgCiAgICAKIyMgT3RoZXIgY29uZGl0aW9uYWwgdGVzdHMKCi0gVGhlcmUgYXJlIHZhcmlvdXMgdGVzdHMgdGhhdCBjYW4gY2hlY2sgdGhlIHR5cGUgb2YgZGF0YSBzdG9yZWQgaW4gYSB2YXJpYWJsZQogICAgKyB0aGVzZSB0ZW5kIHRvIGJlIGNhbGxlZCAqKmBpcy4uLigpYCoqLiAKICAgICAgICArIHRyeSAqdGFiLWNvbXBsZXRlKiBvbiBgaXMuYAoKYGBge3J9CmlzLm51bWVyaWMoMTApCmlzLm51bWVyaWMoIlRFTiIpCmlzLmNoYXJhY3RlcigxMCkKYGBgCgotIGBpcy5uYSgpYCBpcyB1c2VmdWwgZm9yIHNlZWluZyBpZiBhbiBgTkFgIHZhbHVlIGlzIGZvdW5kCiAgICArIGNhbm5vdCB1c2UgYD09IE5BYCEKLSBjb3VsZCBiZSB1c2VkIHRvIGNoZWNrIGlmIGEgZ2VuZSBzeW1ib2wgaXMgZm91bmQgaW4gdGhlIGRhdGEgYmVmb3JlIHByb2NlZWRpbmcgd2l0aCBzdGF0aXN0aWNhbCB0ZXN0CgpgYGB7cn0KbWF0Y2goImZvbyIsIGdlbmVBbm5vdGF0aW9uJEhVR08uZ2VuZS5zeW1ib2wpCmlzLm5hKG1hdGNoKCJmb28iLCBnZW5lQW5ub3RhdGlvbiRIVUdPLmdlbmUuc3ltYm9sKSkKYGBgCgoKLSBVc2luZyB0aGUgKipgZm9yYCoqIGxvb3Agd2Ugd3JvdGUgYmVmb3JlLCB3ZSBjb3VsZCBhZGQgc29tZSBjb2RlIHRvIHBsb3QgdGhlIGV4cHJlc3Npb24gb2YgZWFjaCBnZW5lCiAgICArIGEgYm94cGxvdCB3b3VsZCBiZSBpZGVhbAotIEhvd2V2ZXIsIHdlIG1pZ2h0IG9ubHkgd2FudCBwbG90cyBmb3IgZ2VuZXMgd2l0aCBhICJzaWduaWZpY2FudCIgcHZhbHVlCi0gSGVyZSdzIGhvdyB3ZSBjYW4gdXNlIGFuIGBpZmAgc3RhdGVtZW50IHRvIHRlc3QgZm9yIHRoaXMKICAgICsgZm9yIGVhY2ggaXRlcmF0aW9uIG9mIHRoZSB0aGUgbG9vcDoKICAgICAgICAxLiB0ZXN0IGlmIHRoZSBwLXZhbHVlIGZyb20gdGhlIHRlc3QgaXMgYmVsb3cgMC4wNSBvciBub3QKICAgICAgICAyLiBpZiB0aGUgcC12YWx1ZSBpcyBsZXNzIHRoYW4gMC4wNSBtYWtlIGEgYm94cGxvdAogICAgICAgIDMuIGlmIG5vdCwgZG8gbm90aGluZwogICAgICAgIApgYGB7cn0KcGRmKCJDaHJvbW9zb21lOEdlbmVzLnBkZiIpCnB2YWxzIDwtIE5VTEwKZm9yIChpIGluIDE6MTgpIHsKICB0ZXN0UmVzdWx0IDwtIHQudGVzdChhcy5udW1lcmljKGNocjhFeHByZXNzaW9uW2ksXSkgfiBmYWN0b3IocGF0aWVudE1ldGFkYXRhJGVyKSkKICBwdmFsc1tpXSA8LSB0ZXN0UmVzdWx0JHAudmFsdWUKICBpZih0ZXN0UmVzdWx0JHAudmFsdWUgPCAwLjA1KXsKICAgIGJveHBsb3QoYXMubnVtZXJpYyhjaHI4RXhwcmVzc2lvbltpLF0pIH4gZmFjdG9yKHBhdGllbnRNZXRhZGF0YSRlciksCiAgICAgICAgICAgIG1haW49Y2hyOEdlbmVzJEhVR08uZ2VuZS5zeW1ib2xbaV0pCiAgfQp9IApwdmFscwpkZXYub2ZmKCkKCmBgYAoKCiMjQ29kZSBmb3JtYXR0aW5nIGF2b2lkcyBidWdzIQpDb21wYXJlOgpgYGB7ciBldmFsPUZBTFNFfQpmPC0yNgp3aGlsZShmIT0wKXsKcHJpbnQobGV0dGVyc1tmXSkKZjwtZi0xfQpgYGAKdG86CmBgYHtyIGV2YWw9RkFMU0V9CmYgPC0gMjYKd2hpbGUoZiAhPSAwICl7CiAgcHJpbnQobGV0dGVyc1tmXSkKICBmIDwtIGYtMQogIH0KYGBgCi0gVGhlIGNvZGUgYmV0d2VlbiBicmFja2V0cyBge31gICphbHdheXMqIGlzICppbmRlbnRlZCosIHRoaXMgY2xlYXJseSBzZXBhcmF0ZXMgd2hhdCBpcyBleGVjdXRlZCBvbmNlLCBhbmQgd2hhdCBpcyBydW4gbXVsdGlwbGUgdGltZXMKLSBUcmFpbGluZyBicmFja2V0IGB9YCBhbHdheXMgYWxvbmUgb24gdGhlIGxpbmUgYXQgdGhlIHNhbWUgaW5kZW50YXRpb24gbGV2ZWwgYXMgdGhlIGluaXRpYWwgYnJhY2tldCBge2AKLSBVc2Ugd2hpdGUgc3BhY2VzIHRvIGRpdmlkZSB0aGUgaG9yaXpvbnRhbCBzcGFjZSBiZXR3ZWVuIHVuaXRzIG9mIHlvdXIgY29kZSwgZS5nLiBhcm91bmQgYXNzaWdubWVudHMsIGNvbXBhcmlzb25zCgoKIyBNYWtpbmcgYSBoZWF0bWFwCgotIEEgaGVhdG1hcCBpcyBvZnRlbiB1c2VkIHRvIHZpc3VhbGlzZSBob3cgdGhlIGV4cHJlc3Npb24gbGV2ZWwgb2YgYSBzZXQgb2YgZ2VuZXMgdmFyeSBiZXR3ZWVuIGNvbmRpdGlvbnMKLSBNYWtpbmcgdGhlIHBsb3QgaXMgYWN0dWFsbHkgcXVpdGUgc3RyYWlnaHRmb3J3YXJkCiAgICArIHByb3ZpZGluZyB5b3UgaGF2ZSBwcm9jZXNzZWQgdGhlIGRhdGEgYXBwcm9wcmlhdGVseSEKLSBMZXQncyB0YWtlIGEgbGlzdCBvZiAibW9zdC12YXJpYWJsZSBnZW5lcyIKICAgICsgc2VlIGJlbG93IGZvciBob3cgd2UgaWRlbnRpZmllZCBzdWNoIGdlbmVzCi0gYGhlYXRtYXBgIHJlcXVpcmVzIGEgbWF0cml4IG9iamVjdCByYXRoZXIgdGhhbiBhIGRhdGEgZnJhbWUKCmBgYHtyfQpnZW5lbGlzdCA8LSBjKCJDTElDNiIsIlRGRjMiLCJQRFpLMSIsIlNDVUJFMiIsIkNZUDJCNiIsIkhPWEIxMyIsIk5BVDEiLCJMWTZEIiwiU0xDN0EyIikKcHJvYmVzICAgPC0gZ2VuZUFubm90YXRpb24kcHJvYmVbbWF0Y2goZ2VuZWxpc3QsIGdlbmVBbm5vdGF0aW9uJEhVR08uZ2VuZS5zeW1ib2wpXQpwcm9iZXMKZXhwcm93cyAgPC0gbWF0Y2gocHJvYmVzLCByb3duYW1lcyhub3JtYWxpemVkVmFsdWVzKSkKCmhlYXRtYXAoYXMubWF0cml4KG5vcm1hbGl6ZWRWYWx1ZXNbZXhwcm93cyxdKSkKCmBgYAoKCiMjIEhlYXRtYXAgYWRqdXN0bWVudHMKCi0gV2UgY2FuIHByb3ZpZGUgYSBjb2xvdXIgbGVnZW5kIGZvciB0aGUgc2FtcGxlcwotIEFkanVzdCBjb2xvdXIgb2YgY2VsbHMKLSBMYWJlbCB0aGUgcm93cyBhY2NvcmRpbmcgdG8gZ2VuZSBuYW1lCi0gRG9uJ3QgcHJpbnQgdGhlIHNhbXBsZSBuYW1lcyBhcyB0aGV5IGFyZSB0b28gY2x1dHRlcmVkCgpgYGB7cn0KbGlicmFyeShSQ29sb3JCcmV3ZXIpCgpzYW1wY29sIDwtIHJlcCgiYmx1ZSIsIG5jb2wobm9ybWFsaXplZFZhbHVlcykpCgpzYW1wY29sW3BhdGllbnRNZXRhZGF0YSRlciA9PSAxIF0gPC0gInllbGxvdyIKCnJiUGFsIDwtIGJyZXdlci5wYWwoMTAsICJSZEJ1IikKCmhlYXRtYXAoYXMubWF0cml4KG5vcm1hbGl6ZWRWYWx1ZXNbZXhwcm93cyxdKSwgCiAgICAgICAgQ29sU2lkZUNvbG9ycyA9IHNhbXBjb2wsIAogICAgICAgIGNvbD1yYlBhbCwKICAgICAgICBsYWJSb3cgPSBnZW5lbGlzdCxsYWJDb2w9IiIpCmBgYAoKLSBzZWUgYWxzbwogICAgKyBgaGVhdG1hcC4yYCBmcm9tIGBsaWJyYXJ5KGdwbG90cylgOyBgZXhhbXBsZShoZWF0bWFwLjIpYAogICAgKyBgaGVhdG1hcC5wbHVzYCBmcm9tIGBsaWJyYXJ5KGhlYXRtYXAucGx1cylgOyBgZXhhbXBsZShoZWF0bWFwLnBsdXMpYAogICAgCiMjIChTdXBwbGVtZW50YXJ5KSBDaG9vc2luZyB0aGUgZ2VuZXMgZm9yIHRoZSBoZWF0bWFwCiAgICAKT2Z0ZW4gd2hlbiB1c2luZyBSIHlvdSB3aWxsIGNvbWUgYWNyb3NzIGEgY29udmVuaWVudCBzaG9ydGN1dCBmdW5jdGlvbiB0aGF0IGNhbiBzYXZlIHlvdSBtYW55IGhvdXJzIG9mIGNvZGluZyBhbmQgZnJ1c3RyYXRpb24uCgotIHRoZSBgZ2VuZWZpbHRlcmAgcGFja2FnZSBpbiBCaW9jb25kdWN0b3IgY29udGFpbnMgbWFueSB1c2VmdWwgbWV0aG9kcyBmb3IgZmlsdGVyaW5nIGdlbm9taWMgZGF0YXNldHMKLSB5b3UgY2FuIGluc3RhbGwgdGhpcyBwYWNrYWdlIHdpdGggdGhlIGZvbGxvd2luZyBjb21tYW5kcwoKYGBge3IgZXZhbD1GQUxTRX0Kc291cmNlKCJodHRwOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCmJpb2NMaXRlKCJnZW5lZmlsdGVyIikKYGBgCgotIHRoZSBgcm93U2RzYCBmdW5jdGlvbiB3aWxsIGNhbGN1bGF0ZSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIGZvciBlYWNoIHJvdyBpbiBhIG51bWVyaWMgbWF0cml4Ci0gdGhlIG91dHB1dCB3aWxsIGJlIHZlY3Rvciwgd2l0aCBlYWNoIGVsZW1lbnQgYmVpbmcgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgYSBjb3JyZXNwb25kaW5nIGdlbmUKICAgIApgYGB7cn0KbGlicmFyeShnZW5lZmlsdGVyKQpnZW5lVmFyIDwtIHJvd1Nkcyhub3JtYWxpemVkVmFsdWVzKQpnZW5lVmFyWzFdCnNkKG5vcm1hbGl6ZWRWYWx1ZXNbMSxdKQpgYGAKCi0gd2UgY2FuIG5vdyBgb3JkZXJgIHRoaXMgbWF0cml4IHRvIGdldCB0aGUgc3Vic2V0IHdpdGggYFtdYCB0byBnZXQgdGhlIGluZGljZXMgb2YgdGhlIG1vc3QtdmFyaWFibGUgZ2VuZXMgKDEwIGluIHRoaXMgY2FzZSkuCi0gdGhlIHNhbWUgaW5kaWNlcyBjYW4gYmUgdXNlZCB0byBzdWJzZXQgdGhlIGdlbmUgYW5ub3RhdGlvbiBkYXRhIGZyYW1lCiAgICArIHdlIGNhbiBkbyB0aGlzIGJlY2F1c2UgdGhlIGFubm90YXRpb24gZGF0YSBmcmFtZSBhbmQgZXhwcmVzc2lvbiBtYXRyaXggYXJlIGluIHRoZSBzYW1lIG9yZGVyCgpgYGB7cn0KdG9wVmFyIDwtIG9yZGVyKGdlbmVWYXIsZGVjcmVhc2luZyA9IFRSVUUpWzE6MTBdCnRvcFZhcgpnZW5lQW5ub3RhdGlvblt0b3BWYXIsXQoKYGBgCgo=