Supplementary Protocol 2 – create a gene list by analyzing gene expression data from Affymetrix microarrays with Limma
This protocol demonstrates the extraction of gene lists for pathway enrichment analysis using RMA-normalized gene expression data from Affymetrix microarrays for downstream pathway enrichment analysis with g:Profiler, GSEA and other similar tools. g:Profiler requires a ranked list of differentially expressed genes that are filtered according to a significance cut-off. GSEA requires a two-column tab-separated RNK file with a ranked list of all genes in the genome. In the RNK file, the first column specifies the gene name and the second column specifies a numeric score representing the level of differential expression. For both methods, the first step involves calculating a statistic for each gene that represents the difference in its expression levels between the two groups. This step is performed using the limma R package.
Process Microarray data
Load required packages
- Load required Bioconductor packages into R
#check to see if the library has already been installed. If it hasn't then install it.
tryCatch(expr = { library("Biobase")},
error = function(e) {
source("https://bioconductor.org/biocLite.R")
biocLite("Biobase")},
finally = library("Biobase"))
tryCatch(expr = { library("limma")},
error = function(e) {
source("https://bioconductor.org/biocLite.R")
biocLite("limma")},
finally = library("limma"))
Set the working directory to the location of where the Supplemental Tables 1-4 are stored. The function getwd() shows the working directory and dir() shows its files.
Load Expression Data
- Load expression data into R. Minimally the expression set requires a gene name for each row and typically at least 6 expression values (3 values in each compared class). Our dataset consists of 216 patients with 107 Immunoreactive and 109 Mesenchymal samples. After loading, use the command head(expressionMatrix) to verify the loaded matrix.
expressionMatrix <- as.matrix(read.table(
file.path(working_dir, "Supplementary_Table10_TCGA_Microarray_rmanormalized.txt"),
header = TRUE, sep = "\t", quote="\"", stringsAsFactors = FALSE))
Calculate Differential expression
- Format data and class definitions for limma. The expression data needs to be converted to an object of type ExpressionSet. The ExpressionSet needs to include a data matrix where rows are genes, columns are samples and each cell contains an expression value. Classes need to be defined as factors.
minimalSet <- ExpressionSet(assayData=expressionMatrix)
classes <- factor(classDefinitions[,"SUBTYPE"])
- Create a model matrix with the defined classes.
modelDesign <- model.matrix(~ 0 + classes)
- Fit the model to the expression matrix.
fit <- lmFit(minimalSet, modelDesign)
- Create the contrast matrix - By specifying Mesenchymal first and Immunoreactive second, positive logFC and t-values refer to higher expression levels (up-regulation) in the Mesenchenchymal versus Immunoreactive samples
contrastnm <- c("classesMesenchymal-classesImmunoreactive")
contrast.matrix <- makeContrasts(
original ="classesMesenchymal-classesImmunoreactive",
mesenvsrest = "classesMesenchymal-(classesImmunoreactive +
classesProliferative +classesDifferentiated)/3",
immunovsrest = "classesImmunoreactive-(classesMesenchymal +
classesProliferative +classesDifferentiated)/3",
prolifvsrest = "classesProliferative-(classesMesenchymal +
classesImmunoreactive +classesDifferentiated)/3",
diffvsrest = "classesDifferentiated-(classesMesenchymal +
classesImmunoreactive +classesProliferative)/3",
levels=modelDesign)
- Model contrasts of gene expression. The following command models gene expression differences of each gene between the two groups of samples using linear regression and computes coefficients and standard errors.
fit1 <- contrasts.fit(fit, contrast.matrix)
- Compute differential expression statistics. Given a fitted linear regression model, the command generates a table containing the log fold change, average expression, t statistic, p-value, adjusted p-value and B statistic for each entity in the expression matrix using empirical Bayes statistics. The B-statistic represents the log-odds that the gene is differentially expressed but it is based on a prior assumption of how many genes are differentially expressed in the dataset. Because of its reliance on this prior assumption, the adjusted p-value is preferentially used as an indicator of significant differential expression.
- Generate a table with differentially expressed genes and adjust for multiple hypothesis testing using Benjamini-Hochberg False Discovery Rate. The table contains all genes ranked by p-value and shown with log fold change, average expression, t-statistic, p-value, adjusted p-value and B-statistic.
topfit <- topTable(fit2, coef="immunovsrest",number=nrow(expressionMatrix), adjust="BH")
Top of resulting table:
Examine results
- Significantly up-regulated genes in Mesenchymal samples have positive logFC and t-values.
length(which(topfit$adj.P.Val<0.05 & topfit$t >0))
topgenes_qvalue005_mesenchymal <-
rownames(topfit)[which(topfit$adj.P.Val<0.05 & topfit$t >0)]
head(topgenes_qvalue005_mesenchymal)
write.table(topgenes_qvalue005_mesenchymal,
"ImmunovsRest_immuno_significantgenes.txt",
col.names=FALSE, sep="\t", row.names=FALSE, quote=FALSE)
- Significantly up-regulated genes in Immunoreactive samples have negative logFC and t-values.
length(which(topfit$adj.P.Val<0.05 & topfit$t <0))
topgenes_qvalue005_immunoreactive<-
rownames(topfit)[which(topfit$adj.P.Val<0.05 & topfit$t <0)]
head(topgenes_qvalue005_immunoreactive)
write.table(topgenes_qvalue005_immunoreactive,
"ImmunovsRest_rest_significantgenes.txt",
col.names=FALSE, sep="\t", row.names=FALSE, quote=FALSE)
Create expression file
- Create an expression file for the enrichment map and save files to the home folder of the analysis. The expression file contains the gene IDs as the first column gene description as the second column and the expression values for each sample as the additional columns. Gene IDs should correspond to the first column of the rank file. The text files will be saved on your computer in the directory specified at the beginning of the script using setwd(). The .rnk, .cls and .txt are all tab delimited files that can be viewed in spreadsheet or in a text editor.
library(biomaRt)
mart = useMart(biomart="ensembl", dataset="hsapiens_gene_ensembl")
genes = getBM(attributes = c( 'hgnc_symbol', 'description'), filters='hgnc_symbol',
values=row.names(expressionMatrix), mart=mart);
genes$description = gsub("\\[Source.*", "", genes$description);
EM_expressionFile <- merge(genes,expressionMatrix, all.y=TRUE,by.x=1, by.y=0)
colnames(EM_expressionFile)[1] <- "Name"
colnames(EM_expressionFile)[2] <- "Description"
write.table(EM_expressionFile, "TCGA_OV_expression.txt",
col.name=TRUE, sep="\t", row.names=FALSE, quote=FALSE)
LS0tCnRpdGxlOiAiU3VwcGxlbWVudGFyeSBQcm90b2NvbCAyIOKAkyBjcmVhdGUgYSBnZW5lIGxpc3QgYnkgYW5hbHl6aW5nIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZyb20gQWZmeW1ldHJpeCBtaWNyb2FycmF5cyB3aXRoIExpbW1hIgphdXRob3I6ICJSdXRoIElzc2VybGluIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy5EYXRlKCkpYCIKb3V0cHV0OgogIGh0bWxfZG9jdW1lbnQ6CiAgICBoaWdobGlnaDogaGFkZG9jawogICAga2VlcF9tZDogeWVzCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcwogICAgdGhlbWU6IHBhcGVyCiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogbm8KICAgICAgc21vb3RoX3Njcm9sbDogbm8KICBwZGZfZG9jdW1lbnQ6CiAgICB0b2M6IHllcwogICAgdG9jX2RlcHRoOiAnMycKICBodG1sX25vdGVib29rOgogICAgaGlnaGxpZ2g6IGhhZGRvY2sKICAgIG51bWJlcl9zZWN0aW9uczogeWVzCiAgICB0aGVtZTogcGFwZXIKICAgIHRvYzogeWVzCiAgICB0b2NfZGVwdGg6IDMKICAgIHRvY19mbG9hdDoKICAgICAgY29sbGFwc2VkOiBubwogICAgICBzbW9vdGhfc2Nyb2xsOiBubwpiaWJsaW9ncmFwaHk6IHN1cF9wcm90b2NvbDFfcmVmZXJlbmNlcy5iaWIKY3NsOiBuYXR1cmUtcHJvdG9jb2xzLmNzbAotLS0KIyBTdXBwbGVtZW50YXJ5IFByb3RvY29sIDIg4oCTIGNyZWF0ZSBhIGdlbmUgbGlzdCBieSBhbmFseXppbmcgZ2VuZSBleHByZXNzaW9uIGRhdGEgZnJvbSBBZmZ5bWV0cml4IG1pY3JvYXJyYXlzIHdpdGggTGltbWEKVGhpcyBwcm90b2NvbCBkZW1vbnN0cmF0ZXMgdGhlIGV4dHJhY3Rpb24gb2YgZ2VuZSBsaXN0cyBmb3IgcGF0aHdheSBlbnJpY2htZW50IGFuYWx5c2lzIHVzaW5nIFJNQS1ub3JtYWxpemVkIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZyb20gQWZmeW1ldHJpeCBtaWNyb2FycmF5cyBmb3IgZG93bnN0cmVhbSBwYXRod2F5IGVucmljaG1lbnQgYW5hbHlzaXMgd2l0aCBnOlByb2ZpbGVyLCBHU0VBIGFuZCBvdGhlciBzaW1pbGFyIHRvb2xzLiBnOlByb2ZpbGVyIHJlcXVpcmVzIGEgcmFua2VkIGxpc3Qgb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIHRoYXQgYXJlIGZpbHRlcmVkIGFjY29yZGluZyB0byBhIHNpZ25pZmljYW5jZSBjdXQtb2ZmLiBHU0VBIHJlcXVpcmVzIGEgdHdvLWNvbHVtbiB0YWItc2VwYXJhdGVkIFJOSyBmaWxlIHdpdGggYSByYW5rZWQgbGlzdCBvZiBhbGwgZ2VuZXMgaW4gdGhlIGdlbm9tZS4gSW4gdGhlIFJOSyBmaWxlLCB0aGUgZmlyc3QgY29sdW1uIHNwZWNpZmllcyB0aGUgZ2VuZSBuYW1lIGFuZCB0aGUgc2Vjb25kIGNvbHVtbiBzcGVjaWZpZXMgYSBudW1lcmljIHNjb3JlIHJlcHJlc2VudGluZyB0aGUgbGV2ZWwgb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24uIEZvciBib3RoIG1ldGhvZHMsIHRoZSBmaXJzdCBzdGVwIGludm9sdmVzIGNhbGN1bGF0aW5nIGEgc3RhdGlzdGljIGZvciBlYWNoIGdlbmUgdGhhdCByZXByZXNlbnRzIHRoZSBkaWZmZXJlbmNlIGluIGl0cyBleHByZXNzaW9uIGxldmVscyBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzLiBUaGlzIHN0ZXAgaXMgcGVyZm9ybWVkIHVzaW5nIHRoZSBsaW1tYSBSIHBhY2thZ2UuIAoKYGBge3IgaW5jbHVkZT1GQUxTRX0KY2hlY2s9ZnVuY3Rpb24oeCkgdHJ5Q2F0Y2goaWYoY2xhc3MoeCkgPT0gJ2xvZ2ljYWwnKSAxIGVsc2UgMSwgZXJyb3I9ZnVuY3Rpb24oZSkgMCkgCmlmKGNoZWNrKGFkZF9zZXR1cCkgPT0gMCl7CiAgYWRkX3NldHVwID0gVFJVRQp9CmBgYAoKCmBgYHtyIGF1dG9kb2MsIGNoaWxkPSdzdXBwbGVtZW50YXJ5X3Byb3RvY29sczEyM19zZXR1cC5SbWQnLCBldmFsPUZBTFNFLCBlY2hvPUZBTFNFfQpgYGAgCgojIyBQcm9jZXNzIE1pY3JvYXJyYXkgZGF0YQoKIyMjIExvYWQgcmVxdWlyZWQgcGFja2FnZXMKMS4gTG9hZCByZXF1aXJlZCBCaW9jb25kdWN0b3IgcGFja2FnZXMgaW50byBSCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQojY2hlY2sgdG8gc2VlIGlmIHRoZSBsaWJyYXJ5IGhhcyBhbHJlYWR5IGJlZW4gaW5zdGFsbGVkLiAgSWYgaXQgaGFzbid0IHRoZW4gaW5zdGFsbCBpdC4gCnRyeUNhdGNoKGV4cHIgPSB7IGxpYnJhcnkoIkJpb2Jhc2UiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgCiAgICAgICAgICAgc291cmNlKCJodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCiAgICAgICAgICAgYmlvY0xpdGUoIkJpb2Jhc2UiKX0sIAogICAgICAgICBmaW5hbGx5ID0gbGlicmFyeSgiQmlvYmFzZSIpKQoKdHJ5Q2F0Y2goZXhwciA9IHsgbGlicmFyeSgibGltbWEiKX0sIAogICAgICAgICBlcnJvciA9IGZ1bmN0aW9uKGUpIHsgCiAgICAgICAgICAgc291cmNlKCJodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCiAgICAgICAgICAgYmlvY0xpdGUoImxpbW1hIil9LCAKICAgICAgICAgZmluYWxseSA9IGxpYnJhcnkoImxpbW1hIikpCgpgYGAKClNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgdG8gdGhlIGxvY2F0aW9uIG9mIHdoZXJlIHRoZSBTdXBwbGVtZW50YWwgVGFibGVzIDEtNCBhcmUgc3RvcmVkLiBUaGUgZnVuY3Rpb24gZ2V0d2QoKSBzaG93cyB0aGUgd29ya2luZyBkaXJlY3RvcnkgYW5kIGRpcigpIHNob3dzIGl0cyBmaWxlcy4KYGBge3J9CndvcmtpbmdfZGlyIDwtICIuL2RhdGEiCmBgYAoKIyMjIExvYWQgRXhwcmVzc2lvbiBEYXRhCjIuIExvYWQgZXhwcmVzc2lvbiBkYXRhIGludG8gUi4gTWluaW1hbGx5IHRoZSBleHByZXNzaW9uIHNldCByZXF1aXJlcyBhIGdlbmUgbmFtZSBmb3IgZWFjaCByb3cgYW5kIHR5cGljYWxseSBhdCBsZWFzdCA2IGV4cHJlc3Npb24gdmFsdWVzICgzIHZhbHVlcyBpbiBlYWNoIGNvbXBhcmVkIGNsYXNzKS4gT3VyIGRhdGFzZXQgY29uc2lzdHMgb2YgMjE2IHBhdGllbnRzIHdpdGggMTA3IEltbXVub3JlYWN0aXZlIGFuZCAxMDkgTWVzZW5jaHltYWwgc2FtcGxlcy4gQWZ0ZXIgbG9hZGluZywgdXNlIHRoZSBjb21tYW5kIGhlYWQoZXhwcmVzc2lvbk1hdHJpeCkgdG8gdmVyaWZ5IHRoZSBsb2FkZWQgbWF0cml4LgoKYGBge3J9CmV4cHJlc3Npb25NYXRyaXggPC0gYXMubWF0cml4KHJlYWQudGFibGUoCiAgZmlsZS5wYXRoKHdvcmtpbmdfZGlyLCAiU3VwcGxlbWVudGFyeV9UYWJsZTEwX1RDR0FfTWljcm9hcnJheV9ybWFub3JtYWxpemVkLnR4dCIpLCAKICBoZWFkZXIgPSBUUlVFLCBzZXAgPSAiXHQiLCBxdW90ZT0iXCIiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpKQoKYGBgCgojIyMgTG9hZCBzdWJ0eXBlIGluZm9ybWF0aW9uCjMuIExvYWQgc3VidHlwZSBjbGFzc2lmaWNhdGlvbiBvZiBzYW1wbGVzLiBUbyBjYWxjdWxhdGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24sIHdlIG5lZWQgdG8gZGVmaW5lIGF0IGxlYXN0IDIgY2xhc3NlcyBvZiBzYW1wbGVzLiBBIGNvbW1vbiBleHBlcmltZW50YWwgZGVzaWduIGludm9sdmVzIGNhc2VzIGFuZCBjb250cm9scyBidXQgYW55IHR3byBjbGFzc2VzIGNhbiBiZSB1c2VkLiBUaGUgY3VycmVudCBkYXRhc2V0IGlzIGRpdmlkZWQgaW50byBNZXNlbmNoeW1hbCBhbmQgSW1tdW5vcmVhY3RpdmUgY2xhc3NlcyAoU3VwcGxlbWVudGFyeSBUYWJsZSAzLCB0aGlyZCBjb2x1bW4pLiBBZnRlciBsb2FkaW5nIHRoZSBtYXRyaXgsIGNoZWNrIHRoYXQgdGhlIGNvbHVtbiBuYW1lcyBvZiB0aGUgZXhwcmVzc2lvbiBtYXRyaXggYW5kIGNsYXNzIGRlZmluaXRpb25zIGFyZSBlcXVhbC4gCmBgYHtyfQpjbGFzc0RlZmluaXRpb25zIDwtIHJlYWQudGFibGUoIAogIGZpbGUucGF0aCh3b3JraW5nX2RpciwiU3VwcGxlbWVudGFyeV9UYWJsZTExX01pY3JvYXJyYXlfY2xhc3NkZWZpbml0aW9ucy50eHQiKSwgCiAgaGVhZGVyID0gVFJVRSwgc2VwID0gIlx0IiwgcXVvdGU9IlwiIiwgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQppZGVudGljYWwoY29sbmFtZXMoZXhwcmVzc2lvbk1hdHJpeCksIGNsYXNzRGVmaW5pdGlvbnMkcGF0aWVudCkKCmBgYAoKIyMjIENhbGN1bGF0ZSBEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbgo0LiBGb3JtYXQgZGF0YSBhbmQgY2xhc3MgZGVmaW5pdGlvbnMgZm9yIGxpbW1hLiBUaGUgZXhwcmVzc2lvbiBkYXRhIG5lZWRzIHRvIGJlIGNvbnZlcnRlZCB0byBhbiBvYmplY3Qgb2YgdHlwZSBFeHByZXNzaW9uU2V0LiBUaGUgRXhwcmVzc2lvblNldCBuZWVkcyB0byBpbmNsdWRlIGEgZGF0YSBtYXRyaXggd2hlcmUgcm93cyBhcmUgZ2VuZXMsIGNvbHVtbnMgYXJlIHNhbXBsZXMgYW5kIGVhY2ggY2VsbCBjb250YWlucyBhbiBleHByZXNzaW9uIHZhbHVlLiBDbGFzc2VzIG5lZWQgdG8gYmUgZGVmaW5lZCBhcyBmYWN0b3JzLgpgYGB7cn0KbWluaW1hbFNldCA8LSBFeHByZXNzaW9uU2V0KGFzc2F5RGF0YT1leHByZXNzaW9uTWF0cml4KQpjbGFzc2VzIDwtIGZhY3RvcihjbGFzc0RlZmluaXRpb25zWywiU1VCVFlQRSJdKQoKYGBgCgo1LiBDcmVhdGUgYSBtb2RlbCBtYXRyaXggd2l0aCB0aGUgZGVmaW5lZCBjbGFzc2VzLgpgYGB7cn0KbW9kZWxEZXNpZ24gPC0gbW9kZWwubWF0cml4KH4gMCArIGNsYXNzZXMpCmBgYAoKNi4gRml0IHRoZSBtb2RlbCB0byB0aGUgZXhwcmVzc2lvbiBtYXRyaXguCmBgYHtyfQpmaXQgPC0gbG1GaXQobWluaW1hbFNldCwgbW9kZWxEZXNpZ24pCmBgYAoKNy4gQ3JlYXRlIHRoZSBjb250cmFzdCBtYXRyaXggLSBCeSBzcGVjaWZ5aW5nIE1lc2VuY2h5bWFsIGZpcnN0IGFuZCBJbW11bm9yZWFjdGl2ZSBzZWNvbmQsIHBvc2l0aXZlIGxvZ0ZDIGFuZCB0LXZhbHVlcyByZWZlciB0byBoaWdoZXIgZXhwcmVzc2lvbiBsZXZlbHMgKHVwLXJlZ3VsYXRpb24pIGluIHRoZSBNZXNlbmNoZW5jaHltYWwgdmVyc3VzIEltbXVub3JlYWN0aXZlIHNhbXBsZXMgCmBgYHtyfQpjb250cmFzdG5tIDwtIGMoImNsYXNzZXNNZXNlbmNoeW1hbC1jbGFzc2VzSW1tdW5vcmVhY3RpdmUiKSAKY29udHJhc3QubWF0cml4IDwtIG1ha2VDb250cmFzdHMoCiAgb3JpZ2luYWwgPSJjbGFzc2VzTWVzZW5jaHltYWwtY2xhc3Nlc0ltbXVub3JlYWN0aXZlIiwKICBtZXNlbnZzcmVzdCA9ICJjbGFzc2VzTWVzZW5jaHltYWwtKGNsYXNzZXNJbW11bm9yZWFjdGl2ZSArIAogICAgICAgICAgICAgICAgY2xhc3Nlc1Byb2xpZmVyYXRpdmUgK2NsYXNzZXNEaWZmZXJlbnRpYXRlZCkvMyIsCiAgaW1tdW5vdnNyZXN0ID0gImNsYXNzZXNJbW11bm9yZWFjdGl2ZS0oY2xhc3Nlc01lc2VuY2h5bWFsICsgCiAgICAgICAgICAgICAgICBjbGFzc2VzUHJvbGlmZXJhdGl2ZSArY2xhc3Nlc0RpZmZlcmVudGlhdGVkKS8zIiwKICBwcm9saWZ2c3Jlc3QgPSAiY2xhc3Nlc1Byb2xpZmVyYXRpdmUtKGNsYXNzZXNNZXNlbmNoeW1hbCArIAogICAgICAgICAgICAgICAgY2xhc3Nlc0ltbXVub3JlYWN0aXZlICtjbGFzc2VzRGlmZmVyZW50aWF0ZWQpLzMiLCAKICBkaWZmdnNyZXN0ID0gImNsYXNzZXNEaWZmZXJlbnRpYXRlZC0oY2xhc3Nlc01lc2VuY2h5bWFsICsgCiAgICAgICAgICAgICAgICBjbGFzc2VzSW1tdW5vcmVhY3RpdmUgK2NsYXNzZXNQcm9saWZlcmF0aXZlKS8zIiwKICBsZXZlbHM9bW9kZWxEZXNpZ24pCgpgYGAKCjguIE1vZGVsIGNvbnRyYXN0cyBvZiBnZW5lIGV4cHJlc3Npb24uIFRoZSBmb2xsb3dpbmcgY29tbWFuZCBtb2RlbHMgZ2VuZSBleHByZXNzaW9uIGRpZmZlcmVuY2VzIG9mIGVhY2ggZ2VuZSBiZXR3ZWVuIHRoZSB0d28gZ3JvdXBzIG9mIHNhbXBsZXMgdXNpbmcgbGluZWFyIHJlZ3Jlc3Npb24gYW5kIGNvbXB1dGVzIGNvZWZmaWNpZW50cyBhbmQgc3RhbmRhcmQgZXJyb3JzLgpgYGB7cn0KZml0MSA8LSBjb250cmFzdHMuZml0KGZpdCwgY29udHJhc3QubWF0cml4KQpgYGAKCjkuIENvbXB1dGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gc3RhdGlzdGljcy4gR2l2ZW4gYSBmaXR0ZWQgbGluZWFyIHJlZ3Jlc3Npb24gbW9kZWwsIHRoZSBjb21tYW5kIGdlbmVyYXRlcyBhIHRhYmxlIGNvbnRhaW5pbmcgdGhlIGxvZyBmb2xkIGNoYW5nZSwgYXZlcmFnZSBleHByZXNzaW9uLCB0IHN0YXRpc3RpYywgcC12YWx1ZSwgYWRqdXN0ZWQgcC12YWx1ZSBhbmQgQiBzdGF0aXN0aWMgZm9yIGVhY2ggZW50aXR5IGluIHRoZSBleHByZXNzaW9uIG1hdHJpeCB1c2luZyBlbXBpcmljYWwgQmF5ZXMgc3RhdGlzdGljcy4gVGhlIEItc3RhdGlzdGljIHJlcHJlc2VudHMgdGhlIGxvZy1vZGRzIHRoYXQgdGhlIGdlbmUgaXMgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGJ1dCBpdCBpcyBiYXNlZCBvbiBhIHByaW9yIGFzc3VtcHRpb24gb2YgaG93IG1hbnkgZ2VuZXMgYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbiB0aGUgZGF0YXNldC4gQmVjYXVzZSBvZiBpdHMgcmVsaWFuY2Ugb24gdGhpcyBwcmlvciBhc3N1bXB0aW9uLCB0aGUgYWRqdXN0ZWQgcC12YWx1ZSBpcyBwcmVmZXJlbnRpYWxseSB1c2VkIGFzIGFuIGluZGljYXRvciBvZiBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4KYGBge3J9CmZpdDIgPC0gZUJheWVzKGZpdDEpCmBgYAoKMTAuIEdlbmVyYXRlIGEgdGFibGUgd2l0aCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYW5kIGFkanVzdCBmb3IgbXVsdGlwbGUgaHlwb3RoZXNpcyB0ZXN0aW5nIHVzaW5nIEJlbmphbWluaS1Ib2NoYmVyZyBGYWxzZSBEaXNjb3ZlcnkgUmF0ZS4gVGhlIHRhYmxlIGNvbnRhaW5zIGFsbCBnZW5lcyByYW5rZWQgYnkgcC12YWx1ZSBhbmQgc2hvd24gd2l0aCBsb2cgZm9sZCBjaGFuZ2UsIGF2ZXJhZ2UgZXhwcmVzc2lvbiwgdC1zdGF0aXN0aWMsIHAtdmFsdWUsIGFkanVzdGVkIHAtdmFsdWUgYW5kIEItc3RhdGlzdGljLiAKYGBge3J9CnRvcGZpdCA8LSB0b3BUYWJsZShmaXQyLCBjb2VmPSJpbW11bm92c3Jlc3QiLG51bWJlcj1ucm93KGV4cHJlc3Npb25NYXRyaXgpLCBhZGp1c3Q9IkJIIikKYGBgCgpUb3Agb2YgcmVzdWx0aW5nIHRhYmxlOgpgYGB7cn0KaGVhZCh0b3BmaXQpCmBgYAoKCiMjIyBDcmVhdGUgZzpQcm9maWxlciBpbnB1dCBsaXN0CjExYS4gQ3JlYXRlIHRoZSBnZW5lIGxpc3QgZm9yIHVzZSBpbiBnOlByb2ZpbGVyIG9yIGFub3RoZXIgdGhyZXNob2xkZWQgZW5yaWNobWVudCB0b29sLiBUaGUgbGlzdCBtYXkgY29tcHJpc2UgYWxsIGdlbmVzIHRoYXQgaGF2ZSBhIHNpZ25pZmljYW50IEZEUi1jb3JyZWN0ZWQgcC12YWx1ZSwgYWxsIHVwLXJlZ3VsYXRlZCBnZW5lcyB3aXRoIGEgc2lnbmlmaWNhbnQgRkRSIHAtdmFsdWUsIGFsbCBkb3duLXJlZ3VsYXRlZCBnZW5lcyB3aXRoIGEgc2lnbmlmaWNhbnQgRkRSIHAtdmFsdWUsIG9yIHNvbWUgb3RoZXIgY29tYmluYXRpb24gb2YgdGhyZXNob2xkcy4KICogVG8gZ2V0IGFsbCBzaWduaWZpY2FudCBnZW5lczoKYGBge3J9Cmxlbmd0aCh3aGljaCh0b3BmaXQkYWRqLlAuVmFsPDAuMDUpKQp0b3BnZW5lc19xdmFsdWUwMDUgPC0gcm93bmFtZXModG9wZml0KVt3aGljaCh0b3BmaXQkYWRqLlAuVmFsPDAuMDUpXQpoZWFkKHRvcGdlbmVzX3F2YWx1ZTAwNSkKd3JpdGUudGFibGUodG9wZ2VuZXNfcXZhbHVlMDA1LCAKIkltbXVub3ZzUmVzdF9hbGxzaWduaWZpY2FudGdlbmVzLnR4dCIsIApjb2wubmFtZXM9RkFMU0UsIHNlcD0iXHQiLCByb3cubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFKQpgYGAKCiMjIyBFeGFtaW5lIHJlc3VsdHMKICogU2lnbmlmaWNhbnRseSB1cC1yZWd1bGF0ZWQgZ2VuZXMgaW4gTWVzZW5jaHltYWwgc2FtcGxlcyBoYXZlIHBvc2l0aXZlIGxvZ0ZDIGFuZCB0LXZhbHVlcy4KYGBge3J9Cmxlbmd0aCh3aGljaCh0b3BmaXQkYWRqLlAuVmFsPDAuMDUgJiB0b3BmaXQkdCA+MCkpCnRvcGdlbmVzX3F2YWx1ZTAwNV9tZXNlbmNoeW1hbCA8LQoJcm93bmFtZXModG9wZml0KVt3aGljaCh0b3BmaXQkYWRqLlAuVmFsPDAuMDUgJiB0b3BmaXQkdCA+MCldCmhlYWQodG9wZ2VuZXNfcXZhbHVlMDA1X21lc2VuY2h5bWFsKQp3cml0ZS50YWJsZSh0b3BnZW5lc19xdmFsdWUwMDVfbWVzZW5jaHltYWwsIAogICAgICAgICAgICAiSW1tdW5vdnNSZXN0X2ltbXVub19zaWduaWZpY2FudGdlbmVzLnR4dCIsIAogICAgICAgICAgICBjb2wubmFtZXM9RkFMU0UsIHNlcD0iXHQiLCByb3cubmFtZXM9RkFMU0UsIHF1b3RlPUZBTFNFKQpgYGAKCiAqIFNpZ25pZmljYW50bHkgdXAtcmVndWxhdGVkIGdlbmVzIGluIEltbXVub3JlYWN0aXZlIHNhbXBsZXMgaGF2ZSBuZWdhdGl2ZSBsb2dGQyBhbmQgdC12YWx1ZXMuCmBgYHtyfQpsZW5ndGgod2hpY2godG9wZml0JGFkai5QLlZhbDwwLjA1ICYgdG9wZml0JHQgPDApKQp0b3BnZW5lc19xdmFsdWUwMDVfaW1tdW5vcmVhY3RpdmU8LSAKICByb3duYW1lcyh0b3BmaXQpW3doaWNoKHRvcGZpdCRhZGouUC5WYWw8MC4wNSAmIHRvcGZpdCR0IDwwKV0KaGVhZCh0b3BnZW5lc19xdmFsdWUwMDVfaW1tdW5vcmVhY3RpdmUpCndyaXRlLnRhYmxlKHRvcGdlbmVzX3F2YWx1ZTAwNV9pbW11bm9yZWFjdGl2ZSwgCiAgIkltbXVub3ZzUmVzdF9yZXN0X3NpZ25pZmljYW50Z2VuZXMudHh0IiwgCiAgY29sLm5hbWVzPUZBTFNFLCBzZXA9Ilx0Iiwgcm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKYGBgCgojIyMgQ3JlYXRlIEdTRUEgaW5wdXQgbGlzdAoxMWIuIENyZWF0ZSBhIHJhbmsgZmlsZSBmb3IgR1NFQS4gVG8gcnVuIEdTRUEgaW4gcHJlLXJhbmtlZCBtb2RlLCB5b3UgbmVlZCBhIHR3byBjb2x1bW4gUk5LIGZpbGUgd2l0aCBnZW5lL3Byb3RlaW4vcHJvYmUgbmFtZSAoY29sdW1uIDEpIGFuZCB0aGUgYXNzb2NpYXRlZCBzY29yZSAoY29sdW1uIDIpLiBUaGUgZmlyc3QgY29sdW1uIHNob3VsZCBjb250YWluIHRoZSBzYW1lIHR5cGUgb2YgZ2VuZSBJRHMgdXNlZCBpbiB0aGUgcGF0aHdheSBnZW5lLXNldCAoR01UKSBmaWxlLiBHU0VBIGxvb2tzIGZvciBlbnJpY2htZW50IGluIHRoZSB0b3AgYW5kIGJvdHRvbSBwYXJ0cyBvZiB0aGUgbGlzdCwgcmFua2luZyB0aGUgZmlsZSB1c2luZyB0aGUgdC1zdGF0aXN0aWMuIFRoZSB0LXN0YXRpc3RpYyBpbmRpY2F0ZXMgdGhlIHN0cmVuZ3RoIG9mIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuZCBpcyB1c2VkIGluIHRoZSBwLXZhbHVlIGNhbGN1bGF0aW9uLiBPdGhlciBzY29yZXMgaW5kaWNhdGluZyB0aGUgc3RyZW5ndGggb2YgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gbWF5IGJlIHVzZWQgYXMgd2VsbC4gR1NFQSByYW5rcyB0aGUgbW9zdCB1cC1yZWd1bGF0ZWQgZ2VuZXMgYXQgdGhlIHRvcCBvZiB0aGUgbGlzdCBhbmQgdGhlIG1vc3QgZG93bi1yZWd1bGF0ZWQgYXQgdGhlIGJvdHRvbSBvZiB0aGUgbGlzdC4gR2VuZXMgYXQgdGhlIHRvcCBvZiB0aGUgbGlzdCBhcmUgbW9yZSBoaWdobHkgZXhwcmVzc2VkIGluIGNsYXNzIEEgY29tcGFyZWQgdG8gY2xhc3MgQiwgd2hpbGUgZ2VuZXMgYXQgdGhlIGJvdHRvbSBvZiB0aGUgbGlzdCBhcmUgaGlnaGVyIGluIGNsYXNzIEIuIEluIHRoaXMgd29ya2Zsb3csIGEgcG9zaXRpdmUgdC12YWx1ZSBtZWFucyBhIGhpZ2hlciBleHByZXNzaW9uIG9mIGEgZ2VuZSBpbiB0aGUgTWVzZW5jaHltYWwgc2FtcGxlcyBjb21wYXJlZCB0byB0aGUgSW1tdW5vcmVhY3RpdmUgc2FtcGxlcyAodmFyaWFibGUgY29uc3RyYXN0bm0pLiBUaGUgZm9sbG93aW5nIGNvbW1hbmRzIGNyZWF0ZSBhIGRhdGEgZnJhbWUgd2l0aCBnZW5lIElEcyBhbmQgdC1zdGF0aXN0aWNzLCByZW1vdmUgbGluZXMgd2l0aCBtaXNzaW5nIGdlbmUgSURzLCBhbmQgc3RvcmUgdGhlIHJlc3VsdCBhcyBhIFJOSyBmaWxlLiBBbiBhZGRpdGlvbmFsIHN0ZXAgaXMgdXN1YWxseSByZXF1aXJlZCBpbiBhbmFseXNpcyBvZiBBZmZ5bWV0cml4IG1pY3JvYXJyYXkgZGF0YSBhcyBnZW5lcyBhcmUgcmVwcmVzZW50ZWQgd2l0aCBtdWx0aXBsZSBwcm9iZXNldHMuIFRoZSBtb3N0IHNpZ25pZmljYW50IHByb2Jlc2V0IG9yIGF2ZXJhZ2UgcHJvYmVzZXQgc2NvcmUgbWF5IGJlIGNvbnNpZGVyZWQgZm9yIGV2ZXJ5IGdlbmUuCmBgYHtyfQpyYW5rcyA8LSBkYXRhLmZyYW1lKGdlbmVJRD1yb3duYW1lcyh0b3BmaXQpLHRfc3RhdD10b3BmaXRbLCJ0Il0sIAogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RikKcmFua3MgPC0gcmFua3Nbd2hpY2gocmFua3NbLCJnZW5lSUQiXSAhPSAiIiksXQp3cml0ZS50YWJsZShyYW5rcywiSW1tdW5vdnNSZXN0X2xpbW1hX3JhbmtzLnJuayIsCmNvbC5uYW1lPVRSVUUsc2VwPSJcdCIscm93Lm5hbWVzPUZBTFNFLCBxdW90ZT1GQUxTRSkKCmhlYWQocmFua3MpCmBgYAoKIyMjIENyZWF0ZSBleHByZXNzaW9uIGZpbGUKMTIuIENyZWF0ZSBhbiBleHByZXNzaW9uIGZpbGUgZm9yIHRoZSBlbnJpY2htZW50IG1hcCBhbmQgc2F2ZSBmaWxlcyB0byB0aGUgaG9tZSBmb2xkZXIgb2YgdGhlIGFuYWx5c2lzLiBUaGUgZXhwcmVzc2lvbiBmaWxlIGNvbnRhaW5zIHRoZSBnZW5lIElEcyBhcyB0aGUgZmlyc3QgY29sdW1uIGdlbmUgZGVzY3JpcHRpb24gYXMgdGhlIHNlY29uZCBjb2x1bW4gYW5kIHRoZSBleHByZXNzaW9uIHZhbHVlcyBmb3IgZWFjaCBzYW1wbGUgYXMgdGhlIGFkZGl0aW9uYWwgY29sdW1ucy4gR2VuZSBJRHMgc2hvdWxkIGNvcnJlc3BvbmQgdG8gdGhlIGZpcnN0IGNvbHVtbiBvZiB0aGUgcmFuayBmaWxlLiBUaGUgdGV4dCBmaWxlcyB3aWxsIGJlIHNhdmVkIG9uIHlvdXIgY29tcHV0ZXIgaW4gdGhlIGRpcmVjdG9yeSBzcGVjaWZpZWQgYXQgdGhlIGJlZ2lubmluZyBvZiB0aGUgc2NyaXB0IHVzaW5nIHNldHdkKCkuIFRoZSAucm5rLCAuY2xzIGFuZCAudHh0IGFyZSBhbGwgdGFiIGRlbGltaXRlZCBmaWxlcyB0aGF0IGNhbiBiZSB2aWV3ZWQgaW4gc3ByZWFkc2hlZXQgb3IgaW4gYSB0ZXh0IGVkaXRvci4KCmBgYHtyLCBldmFsPUZBTFNFLCBpbmNsdWRlPVRSVUV9CmxpYnJhcnkoYmlvbWFSdCkKbWFydCA9IHVzZU1hcnQoYmlvbWFydD0iZW5zZW1ibCIsIGRhdGFzZXQ9ImhzYXBpZW5zX2dlbmVfZW5zZW1ibCIpCgpnZW5lcyA9IGdldEJNKGF0dHJpYnV0ZXMgPSBjKCAnaGduY19zeW1ib2wnLCAnZGVzY3JpcHRpb24nKSwgZmlsdGVycz0naGduY19zeW1ib2wnLCAKICAgICAgICAgICAgICB2YWx1ZXM9cm93Lm5hbWVzKGV4cHJlc3Npb25NYXRyaXgpLCBtYXJ0PW1hcnQpOwpnZW5lcyRkZXNjcmlwdGlvbiA9IGdzdWIoIlxcW1NvdXJjZS4qIiwgIiIsIGdlbmVzJGRlc2NyaXB0aW9uKTsKCkVNX2V4cHJlc3Npb25GaWxlIDwtIG1lcmdlKGdlbmVzLGV4cHJlc3Npb25NYXRyaXgsICBhbGwueT1UUlVFLGJ5Lng9MSwgYnkueT0wKQpjb2xuYW1lcyhFTV9leHByZXNzaW9uRmlsZSlbMV0gPC0gIk5hbWUiCmNvbG5hbWVzKEVNX2V4cHJlc3Npb25GaWxlKVsyXSA8LSAiRGVzY3JpcHRpb24iCndyaXRlLnRhYmxlKEVNX2V4cHJlc3Npb25GaWxlLCAiVENHQV9PVl9leHByZXNzaW9uLnR4dCIsIAoJY29sLm5hbWU9VFJVRSwgc2VwPSJcdCIsIHJvdy5uYW1lcz1GQUxTRSwgcXVvdGU9RkFMU0UpCgoKYGBgCgpgYGB7ciwgZXZhbCA9IEZBTFNFLGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoa25pdHIpCmFzaXNfb3V0cHV0KCIjIyBSZWZlcmVuY2VzXFxuIikgIyBIZWFkZXIgdGhhdCBpcyBvbmx5IHNob3duIGlmIGFkZF9zZXR1cCA9PSBUUlVFCmBgYAoK