The R markdown is available from the pulldown menu for Code
at the upper-right, choose “Download Rmd”, or download
the Rmd from GitHub.
This protocol describes a network analysis workflow in Cytoscape for
a set of differentially expressed genes. Points covered:
- Retrieving relevant networks from public databases
- Network functional enrichment analysis
- Integration and visualization of experimental data
- Exporting network visualizations
Installation
if (!requireNamespace("BiocManager", quietly = TRUE)){
install.packages("BiocManager")
}
if(!"RCy3" %in% installed.packages()){
BiocManager::install("RCy3")
}
library(RCy3)
Getting started
First, launch Cytoscape and keep it running whenever using RCy3.
Confirm that you have everything installed and running:
cytoscapePing()
cytoscapeVersionInfo()
Prerequisites
If you haven’t already, install the STRINGapp
Background
Ovarian serous cystadenocarcinoma is a type of epithelial ovarian
cancer which accounts for ~90% of all ovarian cancers. The data used in
this protocol are from The
Cancer Genome Atlas, in which multiple subtypes of serous
cystadenocarcinoma were identified and characterized by mRNA
expression.
We will focus on the differential gene expression between two
subtypes, Mesenchymal and
Immunoreactive.
For convenience, the data has already been analyzed and pre-filtered,
using log fold change value and adjusted p-value.
Network Retrieval
Many public databases and multiple Cytoscape apps allow you to
retrieve a network or pathway relevant to your data. For this workflow,
we will use the STRING app. Some other options include:
Retrieve Networks from STRING
To identify a relevant network, we will query the STRING database in
two different ways:
- Query STRING protein with the list of
differentially expressed genes.
- Query STRING disease for a keyword; ovarian
cancer.
The two examples are split into two separate workflows below.
Example 1: STRING Protein Query Up-regulated Genes
Load the file containing the differential gene expression data and
filter for up-regulated genes, TCGA-Ovarian-MesenvsImmuno_data.csv:
de.genes <- read.table("https://raw.githubusercontent.com/cytoscape/cytoscape-tutorials/gh-pages/protocols/data/TCGA-Ovarian-MesenvsImmuno_data.csv", header = TRUE, sep = ",", quote="\"", stringsAsFactors = FALSE)
de.genes.up <- de.genes[which(de.genes$logFC >= 1.8 & de.genes$FDR.adjusted.Pvalue <= 0.05),]
We will use the identifiers in the first column of this datafile to
run a STRING protein query, with confidence (score)
cutoff of 0.4 and no additional proteins (limit = 0):
string.cmd = paste('string protein query query="', paste(de.genes.up$Gene, collapse = '\n'), '" cutoff=0.4 limit=0 species="Homo sapiens"', sep = "")
commandsRun(string.cmd)
The resulting network will load automatically and contains
up-regulated genes recognized by STRING, and interactions between them
with an evidence score of 0.4 or greater.
The networks consists of one large connected component, several
smaller networks, and some unconnected nodes. We will you a handy
utility app for Cytoscape to select the largest connected component and
create a subnetwork with it.
installApp("Largest Subnetwork")
commandsRun("network select subnetwork createSubnetwork=true")
Data Integration
Next we will create a visualization from the log fold changes and
p-values from our TCGA dataset. Our data from TCGA has NCBI Gene
identifiers, and our STRING network retained these identifiers in the
“query term” column.
loadTableData(de.genes, data.key.column="Gene", table.key.column="query term")
You will notice two new columns (logFC and FDR.adjusted.Pvalue) in
the Node Table.
tail(getTableColumnNames('node'))
Visualization
Next, we will create a visualization of the imported data on the
network. Let’s set all the defaults first:
copyVisualStyle(from.style = "default", to.style = "de genes up")
setVisualStyle("de genes up")
setNodeShapeDefault("ELLIPSE", "de genes up")
lockNodeDimensions("TRUE", "de genes up")
setNodeSizeDefault("50", "de genes up")
setNodeColorDefault("#D3D3D3", "de genes up")
setNodeBorderWidthDefault("2", "de genes up")
setNodeBorderColorDefault("#616060", "de genes up")
setNodeLabelMapping("display name", "de genes up")
setNodeFontSizeDefault("14", "de genes up")
setEdgeOpacityDefault(100, "de genes up")
Then we can use the logFC values to produce a
gradient of node fill colors:
setNodeColorMapping('logFC', colors=paletteColorBrewerYlOrRd, style.name="de genes up")
Note: we are using paletteColorBrewerYlOrRd
to
generate a set of standardized colors balanced for a sequential gradient
(low to high), which are automatically matched to the logFC
column values.
Applying a force-directed layout, the network will now look something
like this:
layoutNetwork(paste('force-directed',
'defaultSpringCoefficient=0.000009',
'defaultSpringLength=50',
'defaultNodeMass=4',
sep=' '))
Enrichment Analysis Options
Next, we are going to perform enrichment analysis uing the STRING
app.
STRING Enrichment
The STRING app has built-in enrichment analysis functionality, which
includes enrichment for GO Process, GO Component, GO Function, InterPro,
KEGG Pathways, and PFAM.
First, we will run the enrichment on the whole network, against the
genome:
commandsRun('string retrieve enrichment allNetSpecies="Homo sapiens"')
commandsRun('string show enrichment')
When the enrichment analysis is complete, a new tab titled
STRING Enrichment will open in the Table
Panel.
The STRING app includes several options for filtering and displaying
the enrichment results. The features are all available at the top of the
STRING Enrichment tab.
We are going to filter the table to only show GO Process:
commandsRun('string filter enrichment categories="GO Biological Process", overlapCutoff = "0.5", removeOverlapping = "true"')
Next, we will add a split donut chart to the nodes representing the
top terms:
commandsRun('string show charts')
STRING Protein Query: Down-regulated genes
We are going to repeat the network search, data integration,
visualization and enrichment analysis for the set of down-regulated
genes by subsetting our original dataset for downregulated logFC
genes:
de.genes.down <- de.genes[which(de.genes$logFC <= -1.1 & de.genes$FDR.adjusted.Pvalue <= 0.05),]
string.cmd = paste('string protein query query="', paste(de.genes.down$Gene, collapse = '\n'), '" cutoff=0.4 limit=0 species="Homo sapiens"', sep = "")
commandsRun(string.cmd)
Subnetwork
Let’s select only the connected nodes to work with for the rest of
this tutorial, by creating a subnetwork based on all edges:
commandsRun("network select subnetwork createSubnetwork=true")
Data integration
Again, we can import the data, mapping to the “query term”
column:
loadTableData(de.genes,data.key.column="Gene",table.key.column="query term")
Visualization
Next, we can create a visualization. Note that we can reuse most of
the previous style (e.g., all the default settings) and just provide a
new node color mapping with downregulated color scheme, like
paletteColorBrewerBlues
:
copyVisualStyle(from.style = "de genes up", to.style = "de genes down")
setVisualStyle(style.name="de genes down")
setNodeColorMapping('logFC', colors=paletteColorBrewerBlues, style.name="de genes down")
Apply a force-directed layout.
layoutNetwork(paste('force-directed',
'defaultSpringCoefficient=0.00001',
'defaultSpringLength=50',
'defaultNodeMass=4',
sep=' '))
Focusing on the connected part of the network, it should look something
like this:
STRING Enrichment
Now we can perform STRING Enrichment analysis on the resulting
network:
commandsRun('string retrieve enrichment allNetSpecies="Homo sapiens"')
commandsRun('string show enrichment')
Filter the analysis results for non-redundant GO Process terms
only.
commandsRun('string filter enrichment categories="GO Biological Process", overlapCutoff = "0.5", removeOverlapping = "true"')
commandsRun('string show charts')
STRING Disease Query
So far, we queried the STRING database with a set of genes we knew
were differentially expressed. Next, we will query the STRING
disease database to retrieve a network genes associated with
ovarian cancer, which will be completely independent of our dataset.
commandsRun('string disease query disease="ovarian cancer" cutoff="0.95"')
This will bring in the top 100 (default) ovarian cancer associated
genes connected with a confidence score greater than 0.95. Again, lets
extract out the connected nodes:
commandsRun("network select subnetwork createSubnetwork=true")
Data integration
Next we will load the same differential gene expression data from our
TCGA dataset to create a visualization. This time we will need to do
some identifier mapping to match the data to the network.
mapped.cols <- mapTableColumn("display name",'Human','HGNC','Entrez Gene')
Here we set Human as species, HGNC
as Map from, and Entrez Gene as
To.
We can now import the data frame with the data into the node table in
Cytoscape:
loadTableData(de.genes, data.key.column = "Gene", table = "node", table.key.column = "Entrez Gene")
Visualization
Again, we can create a visualization reusing our prior style and
setting a new color gradient for the nodes:
copyVisualStyle(from.style = "de genes up", to.style = "ovarian")
setVisualStyle(style.name="ovarian")
setNodeColorMapping('logFC', colors=paletteColorBrewerRdBu, style.name="ovarian")
Apply a force-directed layout.
layoutNetwork(paste('force-directed',
'defaultSpringCoefficient=0.000006',
'defaultSpringLength=50',
'defaultNodeMass=2',
sep=' '))
The TCGA found several genes that were commonly mutated in ovarian
cancer, so called “cancer drivers”. We can add information about these
genes to the network visualization, by changing the visual style of
these nodes. Three of the most important drivers are
TP53, BRCA1 and
BRCA2. We will add a thicker, colored border for these
genes in the network.
Select all three driver genes by:
selectNodes(c("TP53", "BRCA1", "BRCA2"), by.col = "display name")
Add a style bypass for node Border Width (5) and
node Border Paint (bright pink):
setNodeBorderWidthBypass(getSelectedNodes(), 5)
setNodeBorderColorBypass(getSelectedNodes(), '#FF007F')
Exporting Networks
Cytoscape provides a number of ways to export results and
visualizations:
exportImage('differentially-expressed-genes', 'PDF')
Note: PNG, SVG, JPEG and PS are also supported
exportNetworkToNDEx("user", "password", TRUE) #requires a free NDEx account
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzIE5ldHdvcmsgQW5hbHlzaXMiCmF1dGhvcjogIktvem8gTmlzaGlkYSwgS3Jpc3RpbmEgSGFuc3BlcnMgYW5kIEFsZXggUGljbyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiAibm9uZSIKLS0tCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBldmFsPUZBTFNFCikKYGBgCgoqVGhlIFIgbWFya2Rvd24gaXMgYXZhaWxhYmxlIGZyb20gdGhlIHB1bGxkb3duIG1lbnUgZm9yKiBDb2RlICphdCB0aGUgdXBwZXItcmlnaHQsIGNob29zZSAiRG93bmxvYWQgUm1kIiwgb3IgW2Rvd25sb2FkIHRoZSBSbWQgZnJvbSBHaXRIdWJdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jeXRvc2NhcGUvY3l0b3NjYXBlLWF1dG9tYXRpb24vbWFzdGVyL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvRGlmZmVyZW50aWFsbHktZXhwcmVzc2VkLWdlbmVzLlJtZCkuKgoKPGhyIC8+CgpUaGlzIHByb3RvY29sIGRlc2NyaWJlcyBhIG5ldHdvcmsgYW5hbHlzaXMgd29ya2Zsb3cgaW4gQ3l0b3NjYXBlIGZvciBhIHNldCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFBvaW50cyBjb3ZlcmVkOgoKLSBSZXRyaWV2aW5nIHJlbGV2YW50IG5ldHdvcmtzIGZyb20gcHVibGljIGRhdGFiYXNlcwotIE5ldHdvcmsgZnVuY3Rpb25hbCBlbnJpY2htZW50IGFuYWx5c2lzCi0gSW50ZWdyYXRpb24gYW5kIHZpc3VhbGl6YXRpb24gb2YgZXhwZXJpbWVudGFsIGRhdGEKLSBFeHBvcnRpbmcgbmV0d29yayB2aXN1YWxpemF0aW9ucwoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvc3RyaW5nLW92YXJpYW4tZmluYWwucG5nKQo8L2NlbnRlcj4KCjxociAvPgoKIyBJbnN0YWxsYXRpb24KYGBge3IsIGV2YWwgPSBGQUxTRX0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQp9CgppZighIlJDeTMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpewogIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKfQpsaWJyYXJ5KFJDeTMpCmBgYAoKIyBHZXR0aW5nIHN0YXJ0ZWQKRmlyc3QsIGxhdW5jaCBDeXRvc2NhcGUgYW5kIGtlZXAgaXQgcnVubmluZyB3aGVuZXZlciB1c2luZyBSQ3kzLiBDb25maXJtIHRoYXQgeW91IGhhdmUgZXZlcnl0aGluZyBpbnN0YWxsZWQgYW5kIHJ1bm5pbmc6CmBgYHtyfQpjeXRvc2NhcGVQaW5nKCkKY3l0b3NjYXBlVmVyc2lvbkluZm8oKQpgYGAKCiMgUHJlcmVxdWlzaXRlcwoKSWYgeW91IGhhdmVuJ3QgYWxyZWFkeSwgaW5zdGFsbCB0aGUgW1NUUklOR2FwcF0oaHR0cDovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL3N0cmluZ2FwcCkKCmBgYHtyfQppbnN0YWxsQXBwKCdzdHJpbmdBcHAnKQpgYGAKCiMgQmFja2dyb3VuZAoKT3ZhcmlhbiBzZXJvdXMgY3lzdGFkZW5vY2FyY2lub21hIGlzIGEgdHlwZSBvZiBlcGl0aGVsaWFsIG92YXJpYW4gY2FuY2VyIHdoaWNoIGFjY291bnRzIGZvciB+OTAlIG9mIGFsbCBvdmFyaWFuIGNhbmNlcnMuClRoZSBkYXRhIHVzZWQgaW4gdGhpcyBwcm90b2NvbCBhcmUgZnJvbSBbVGhlIENhbmNlciBHZW5vbWUgQXRsYXNdKGh0dHBzOi8vY2FuY2VyZ2Vub21lLm5paC5nb3YvKSwgaW4gd2hpY2ggbXVsdGlwbGUgc3VidHlwZXMgb2Ygc2Vyb3VzIGN5c3RhZGVub2NhcmNpbm9tYSB3ZXJlIGlkZW50aWZpZWQgYW5kIGNoYXJhY3Rlcml6ZWQgYnkgbVJOQSBleHByZXNzaW9uLgoKV2Ugd2lsbCBmb2N1cyBvbiB0aGUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIHR3byBzdWJ0eXBlcywgKipNZXNlbmNoeW1hbCoqIGFuZCAqKkltbXVub3JlYWN0aXZlKiouCgpGb3IgY29udmVuaWVuY2UsIHRoZSBkYXRhIGhhcyBhbHJlYWR5IGJlZW4gYW5hbHl6ZWQgYW5kIHByZS1maWx0ZXJlZCwgdXNpbmcgbG9nIGZvbGQgY2hhbmdlIHZhbHVlIGFuZCBhZGp1c3RlZCBwLXZhbHVlLgoKIyBOZXR3b3JrIFJldHJpZXZhbAoKTWFueSBwdWJsaWMgZGF0YWJhc2VzIGFuZCBtdWx0aXBsZSBDeXRvc2NhcGUgYXBwcyBhbGxvdyB5b3UgdG8gcmV0cmlldmUgYSBuZXR3b3JrIG9yIHBhdGh3YXkgcmVsZXZhbnQgdG8geW91ciBkYXRhLgpGb3IgdGhpcyB3b3JrZmxvdywgd2Ugd2lsbCB1c2UgdGhlIFNUUklORyBhcHAuIFNvbWUgb3RoZXIgb3B0aW9ucyBpbmNsdWRlOgoKLSBbV2lraVBhdGh3YXlzXShodHRwczovL25ybmIub3JnL2dzb2QyMDE5X2tvem9fbmlzaGlkYS9odG1sX2RvY3VtZW50cy9SbWQvd2lraXBhdGh3YXlzLWFwcC5odG1sKQotIFtOREV4XShodHRwOi8vd3d3Lm5kZXhiaW8ub3JnLykKLSBbR2VuZU1BTklBXShodHRwczovL2dlbmVtYW5pYS5vcmcvKQoKIyBSZXRyaWV2ZSBOZXR3b3JrcyBmcm9tIFNUUklORwoKVG8gaWRlbnRpZnkgYSByZWxldmFudCBuZXR3b3JrLCB3ZSB3aWxsIHF1ZXJ5IHRoZSBTVFJJTkcgZGF0YWJhc2UgaW4gdHdvIGRpZmZlcmVudCB3YXlzOgoKLSBRdWVyeSAqKlNUUklORyBwcm90ZWluKiogd2l0aCB0aGUgbGlzdCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuCi0gUXVlcnkgKipTVFJJTkcgZGlzZWFzZSoqIGZvciBhIGtleXdvcmQ7ICoqb3ZhcmlhbiBjYW5jZXIqKi4KClRoZSB0d28gZXhhbXBsZXMgYXJlIHNwbGl0IGludG8gdHdvIHNlcGFyYXRlIHdvcmtmbG93cyBiZWxvdy4KCiMgRXhhbXBsZSAxOiBTVFJJTkcgUHJvdGVpbiBRdWVyeSBVcC1yZWd1bGF0ZWQgR2VuZXMKCkxvYWQgdGhlIGZpbGUgY29udGFpbmluZyB0aGUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGFuZCBmaWx0ZXIgZm9yIHVwLXJlZ3VsYXRlZCBnZW5lcywgVENHQS1PdmFyaWFuLU1lc2VudnNJbW11bm9fZGF0YS5jc3Y6CgpgYGB7cn0KZGUuZ2VuZXMgPC0gcmVhZC50YWJsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2N5dG9zY2FwZS9jeXRvc2NhcGUtdHV0b3JpYWxzL2doLXBhZ2VzL3Byb3RvY29scy9kYXRhL1RDR0EtT3Zhcmlhbi1NZXNlbnZzSW1tdW5vX2RhdGEuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiLCBxdW90ZT0iXCIiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmRlLmdlbmVzLnVwIDwtIGRlLmdlbmVzW3doaWNoKGRlLmdlbmVzJGxvZ0ZDID49IDEuOCAmIGRlLmdlbmVzJEZEUi5hZGp1c3RlZC5QdmFsdWUgPD0gMC4wNSksXQpgYGAKCldlIHdpbGwgdXNlIHRoZSBpZGVudGlmaWVycyBpbiB0aGUgZmlyc3QgY29sdW1uIG9mIHRoaXMgZGF0YWZpbGUgdG8gcnVuIGEgKipTVFJJTkcgcHJvdGVpbiBxdWVyeSoqLCB3aXRoIGNvbmZpZGVuY2UgKHNjb3JlKSBjdXRvZmYgb2YgMC40IGFuZCBubyBhZGRpdGlvbmFsIHByb3RlaW5zIChsaW1pdCA9IDApOgoKYGBge3J9CnN0cmluZy5jbWQgPSBwYXN0ZSgnc3RyaW5nIHByb3RlaW4gcXVlcnkgcXVlcnk9IicsIHBhc3RlKGRlLmdlbmVzLnVwJEdlbmUsIGNvbGxhcHNlID0gJ1xuJyksICciIGN1dG9mZj0wLjQgbGltaXQ9MCBzcGVjaWVzPSJIb21vIHNhcGllbnMiJywgc2VwID0gIiIpCmNvbW1hbmRzUnVuKHN0cmluZy5jbWQpCmBgYAoKVGhlIHJlc3VsdGluZyBuZXR3b3JrIHdpbGwgbG9hZCBhdXRvbWF0aWNhbGx5IGFuZCBjb250YWlucyB1cC1yZWd1bGF0ZWQgZ2VuZXMgcmVjb2duaXplZCBieSBTVFJJTkcsIGFuZCBpbnRlcmFjdGlvbnMgYmV0d2VlbiB0aGVtIHdpdGggYW4gZXZpZGVuY2Ugc2NvcmUgb2YgMC40IG9yIGdyZWF0ZXIuCgo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9zdHJpbmctZGUtdXAyLnBuZykKPC9jZW50ZXI+CgpUaGUgbmV0d29ya3MgY29uc2lzdHMgb2Ygb25lIGxhcmdlIGNvbm5lY3RlZCBjb21wb25lbnQsIHNldmVyYWwgc21hbGxlciBuZXR3b3JrcywgYW5kIHNvbWUgdW5jb25uZWN0ZWQgbm9kZXMuIFdlIHdpbGwgeW91IGEgaGFuZHkgdXRpbGl0eSBhcHAgZm9yIEN5dG9zY2FwZSB0byBzZWxlY3QgdGhlIGxhcmdlc3QgY29ubmVjdGVkIGNvbXBvbmVudCBhbmQgY3JlYXRlIGEgc3VibmV0d29yayB3aXRoIGl0LgoKYGBge3J9Cmluc3RhbGxBcHAoIkxhcmdlc3QgU3VibmV0d29yayIpCmNvbW1hbmRzUnVuKCJuZXR3b3JrIHNlbGVjdCBzdWJuZXR3b3JrIGNyZWF0ZVN1Ym5ldHdvcms9dHJ1ZSIpCmBgYAoKIyBEYXRhIEludGVncmF0aW9uCgpOZXh0IHdlIHdpbGwgY3JlYXRlIGEgdmlzdWFsaXphdGlvbiBmcm9tIHRoZSBsb2cgZm9sZCBjaGFuZ2VzIGFuZCBwLXZhbHVlcyBmcm9tIG91ciBUQ0dBIGRhdGFzZXQuIE91ciBkYXRhIGZyb20gVENHQSBoYXMgTkNCSSBHZW5lIGlkZW50aWZpZXJzLCBhbmQgb3VyIFNUUklORyBuZXR3b3JrIHJldGFpbmVkIHRoZXNlIGlkZW50aWZpZXJzIGluIHRoZSAicXVlcnkgdGVybSIgY29sdW1uLgoKYGBge3J9CmxvYWRUYWJsZURhdGEoZGUuZ2VuZXMsIGRhdGEua2V5LmNvbHVtbj0iR2VuZSIsIHRhYmxlLmtleS5jb2x1bW49InF1ZXJ5IHRlcm0iKQpgYGAKCllvdSB3aWxsIG5vdGljZSB0d28gbmV3IGNvbHVtbnMgKGxvZ0ZDIGFuZCBGRFIuYWRqdXN0ZWQuUHZhbHVlKSBpbiB0aGUgTm9kZSBUYWJsZS4gCgpgYGB7cn0KdGFpbChnZXRUYWJsZUNvbHVtbk5hbWVzKCdub2RlJykpCmBgYAoKIyBWaXN1YWxpemF0aW9uCk5leHQsIHdlIHdpbGwgY3JlYXRlIGEgdmlzdWFsaXphdGlvbiBvZiB0aGUgaW1wb3J0ZWQgZGF0YSBvbiB0aGUgbmV0d29yay4gTGV0J3Mgc2V0IGFsbCB0aGUgZGVmYXVsdHMgZmlyc3Q6CgpgYGB7cn0KY29weVZpc3VhbFN0eWxlKGZyb20uc3R5bGUgPSAiZGVmYXVsdCIsIHRvLnN0eWxlID0gImRlIGdlbmVzIHVwIikKc2V0VmlzdWFsU3R5bGUoImRlIGdlbmVzIHVwIikKc2V0Tm9kZVNoYXBlRGVmYXVsdCgiRUxMSVBTRSIsICJkZSBnZW5lcyB1cCIpCmxvY2tOb2RlRGltZW5zaW9ucygiVFJVRSIsICJkZSBnZW5lcyB1cCIpCnNldE5vZGVTaXplRGVmYXVsdCgiNTAiLCAiZGUgZ2VuZXMgdXAiKQpzZXROb2RlQ29sb3JEZWZhdWx0KCIjRDNEM0QzIiwgImRlIGdlbmVzIHVwIikKc2V0Tm9kZUJvcmRlcldpZHRoRGVmYXVsdCgiMiIsICJkZSBnZW5lcyB1cCIpCnNldE5vZGVCb3JkZXJDb2xvckRlZmF1bHQoIiM2MTYwNjAiLCAiZGUgZ2VuZXMgdXAiKQpzZXROb2RlTGFiZWxNYXBwaW5nKCJkaXNwbGF5IG5hbWUiLCAiZGUgZ2VuZXMgdXAiKQpzZXROb2RlRm9udFNpemVEZWZhdWx0KCIxNCIsICJkZSBnZW5lcyB1cCIpCnNldEVkZ2VPcGFjaXR5RGVmYXVsdCgxMDAsICJkZSBnZW5lcyB1cCIpCmBgYAoKVGhlbiB3ZSBjYW4gdXNlIHRoZSAqKmxvZ0ZDKiogdmFsdWVzIHRvIHByb2R1Y2UgYSBncmFkaWVudCBvZiBub2RlIGZpbGwgY29sb3JzOgoKYGBge3J9CnNldE5vZGVDb2xvck1hcHBpbmcoJ2xvZ0ZDJywgY29sb3JzPXBhbGV0dGVDb2xvckJyZXdlcllsT3JSZCwgc3R5bGUubmFtZT0iZGUgZ2VuZXMgdXAiKQpgYGAKCipOb3RlOiB3ZSBhcmUgdXNpbmcgYHBhbGV0dGVDb2xvckJyZXdlcllsT3JSZGAgdG8gZ2VuZXJhdGUgYSBzZXQgb2Ygc3RhbmRhcmRpemVkIGNvbG9ycyBiYWxhbmNlZCBmb3IgYSBzZXF1ZW50aWFsIGdyYWRpZW50IChsb3cgdG8gaGlnaCksIHdoaWNoIGFyZSBhdXRvbWF0aWNhbGx5IG1hdGNoZWQgdG8gdGhlIGBsb2dGQ2AgY29sdW1uIHZhbHVlcy4qCgpBcHBseWluZyBhIGZvcmNlLWRpcmVjdGVkIGxheW91dCwgdGhlIG5ldHdvcmsgd2lsbCBub3cgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOgoKYGBge3J9CmxheW91dE5ldHdvcmsocGFzdGUoJ2ZvcmNlLWRpcmVjdGVkJywgCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdDb2VmZmljaWVudD0wLjAwMDAwOScsCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdMZW5ndGg9NTAnLAogICAgICAgICAgICAgICdkZWZhdWx0Tm9kZU1hc3M9NCcsCiAgICAgICAgICAgICAgc2VwPScgJykpCmBgYAoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvc3RyaW5nLWRlLXVwLWZvcmNlZGlyZWN0ZWQucG5nKQo8L2NlbnRlcj4KCiMgRW5yaWNobWVudCBBbmFseXNpcyBPcHRpb25zCgpOZXh0LCB3ZSBhcmUgZ29pbmcgdG8gcGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIHVpbmcgdGhlIFNUUklORyBhcHAuCgojIyBTVFJJTkcgRW5yaWNobWVudAoKVGhlIFNUUklORyBhcHAgaGFzIGJ1aWx0LWluIGVucmljaG1lbnQgYW5hbHlzaXMgZnVuY3Rpb25hbGl0eSwgd2hpY2ggaW5jbHVkZXMgZW5yaWNobWVudCBmb3IgR08gUHJvY2VzcywgR08gQ29tcG9uZW50LCBHTyBGdW5jdGlvbiwgSW50ZXJQcm8sIEtFR0cgUGF0aHdheXMsIGFuZCBQRkFNLgoKRmlyc3QsIHdlIHdpbGwgcnVuIHRoZSBlbnJpY2htZW50IG9uIHRoZSB3aG9sZSBuZXR3b3JrLCBhZ2FpbnN0IHRoZSBnZW5vbWU6IAoKYGBge3J9CmNvbW1hbmRzUnVuKCdzdHJpbmcgcmV0cmlldmUgZW5yaWNobWVudCBhbGxOZXRTcGVjaWVzPSJIb21vIHNhcGllbnMiJykKY29tbWFuZHNSdW4oJ3N0cmluZyBzaG93IGVucmljaG1lbnQnKQpgYGAKCldoZW4gdGhlIGVucmljaG1lbnQgYW5hbHlzaXMgaXMgY29tcGxldGUsIGEgbmV3IHRhYiB0aXRsZWQgKipTVFJJTkcgRW5yaWNobWVudCoqIHdpbGwgb3BlbiBpbiB0aGUgKipUYWJsZSBQYW5lbCoqLgoKPGNlbnRlcj4KIVtdKGh0dHBzOi8vY3l0b3NjYXBlLm9yZy9jeXRvc2NhcGUtYXV0b21hdGlvbi9mb3Itc2NyaXB0ZXJzL1Ivbm90ZWJvb2tzL2RhdGEvaW1nL3N0cmluZy1lbnJpY2htZW50LnBuZykKPC9jZW50ZXI+CgpUaGUgU1RSSU5HIGFwcCBpbmNsdWRlcyBzZXZlcmFsIG9wdGlvbnMgZm9yIGZpbHRlcmluZyBhbmQgZGlzcGxheWluZyB0aGUgZW5yaWNobWVudCByZXN1bHRzLgpUaGUgZmVhdHVyZXMgYXJlIGFsbCBhdmFpbGFibGUgYXQgdGhlIHRvcCBvZiB0aGUgKipTVFJJTkcgRW5yaWNobWVudCoqIHRhYi4KCldlIGFyZSBnb2luZyB0byBmaWx0ZXIgdGhlIHRhYmxlIHRvIG9ubHkgc2hvdyBHTyBQcm9jZXNzOgoKYGBge3J9CmNvbW1hbmRzUnVuKCdzdHJpbmcgZmlsdGVyIGVucmljaG1lbnQgY2F0ZWdvcmllcz0iR08gQmlvbG9naWNhbCBQcm9jZXNzIiwgb3ZlcmxhcEN1dG9mZiA9ICIwLjUiLCByZW1vdmVPdmVybGFwcGluZyA9ICJ0cnVlIicpCmBgYAoKTmV4dCwgd2Ugd2lsbCBhZGQgYSBzcGxpdCBkb251dCBjaGFydCB0byB0aGUgbm9kZXMgcmVwcmVzZW50aW5nIHRoZSB0b3AgdGVybXM6CgpgYGB7cn0KY29tbWFuZHNSdW4oJ3N0cmluZyBzaG93IGNoYXJ0cycpCmBgYAoKIyMgU1RSSU5HIFByb3RlaW4gUXVlcnk6IERvd24tcmVndWxhdGVkIGdlbmVzCgpXZSBhcmUgZ29pbmcgdG8gcmVwZWF0IHRoZSBuZXR3b3JrIHNlYXJjaCwgZGF0YSBpbnRlZ3JhdGlvbiwgdmlzdWFsaXphdGlvbiBhbmQgZW5yaWNobWVudCBhbmFseXNpcyBmb3IgdGhlIHNldCBvZiBkb3duLXJlZ3VsYXRlZCBnZW5lcyBieSBzdWJzZXR0aW5nIG91ciBvcmlnaW5hbCBkYXRhc2V0IGZvciBkb3ducmVndWxhdGVkIGxvZ0ZDIGdlbmVzOgoKYGBge3J9CmRlLmdlbmVzLmRvd24gPC0gZGUuZ2VuZXNbd2hpY2goZGUuZ2VuZXMkbG9nRkMgPD0gLTEuMSAmIGRlLmdlbmVzJEZEUi5hZGp1c3RlZC5QdmFsdWUgPD0gMC4wNSksXQpzdHJpbmcuY21kID0gcGFzdGUoJ3N0cmluZyBwcm90ZWluIHF1ZXJ5IHF1ZXJ5PSInLCBwYXN0ZShkZS5nZW5lcy5kb3duJEdlbmUsIGNvbGxhcHNlID0gJ1xuJyksICciIGN1dG9mZj0wLjQgbGltaXQ9MCBzcGVjaWVzPSJIb21vIHNhcGllbnMiJywgc2VwID0gIiIpCmNvbW1hbmRzUnVuKHN0cmluZy5jbWQpCmBgYAoKIyMgU3VibmV0d29yawoKTGV0J3Mgc2VsZWN0IG9ubHkgdGhlIGNvbm5lY3RlZCBub2RlcyB0byB3b3JrIHdpdGggZm9yIHRoZSByZXN0IG9mIHRoaXMgdHV0b3JpYWwsIGJ5IGNyZWF0aW5nIGEgc3VibmV0d29yayBiYXNlZCBvbiBhbGwgZWRnZXM6CgpgYGB7cn0KY29tbWFuZHNSdW4oIm5ldHdvcmsgc2VsZWN0IHN1Ym5ldHdvcmsgY3JlYXRlU3VibmV0d29yaz10cnVlIikKYGBgCgojIyBEYXRhIGludGVncmF0aW9uCgpBZ2Fpbiwgd2UgY2FuIGltcG9ydCB0aGUgZGF0YSwgbWFwcGluZyB0byB0aGUgInF1ZXJ5IHRlcm0iIGNvbHVtbjoKCmBgYHtyfQpsb2FkVGFibGVEYXRhKGRlLmdlbmVzLGRhdGEua2V5LmNvbHVtbj0iR2VuZSIsdGFibGUua2V5LmNvbHVtbj0icXVlcnkgdGVybSIpCmBgYAoKIyMgVmlzdWFsaXphdGlvbgpOZXh0LCB3ZSBjYW4gY3JlYXRlIGEgdmlzdWFsaXphdGlvbi4gTm90ZSB0aGF0IHdlIGNhbiByZXVzZSBtb3N0IG9mIHRoZSBwcmV2aW91cyBzdHlsZSAoZS5nLiwgYWxsIHRoZSBkZWZhdWx0IHNldHRpbmdzKSBhbmQganVzdCBwcm92aWRlIGEgbmV3IG5vZGUgY29sb3IgbWFwcGluZyB3aXRoIGRvd25yZWd1bGF0ZWQgY29sb3Igc2NoZW1lLCBsaWtlIGBwYWxldHRlQ29sb3JCcmV3ZXJCbHVlc2A6CgpgYGB7cn0KY29weVZpc3VhbFN0eWxlKGZyb20uc3R5bGUgPSAiZGUgZ2VuZXMgdXAiLCB0by5zdHlsZSA9ICJkZSBnZW5lcyBkb3duIikKc2V0VmlzdWFsU3R5bGUoc3R5bGUubmFtZT0iZGUgZ2VuZXMgZG93biIpCgpzZXROb2RlQ29sb3JNYXBwaW5nKCdsb2dGQycsIGNvbG9ycz1wYWxldHRlQ29sb3JCcmV3ZXJCbHVlcywgc3R5bGUubmFtZT0iZGUgZ2VuZXMgZG93biIpCmBgYAoKQXBwbHkgYSBmb3JjZS1kaXJlY3RlZCBsYXlvdXQuCgpgYGB7cn0KbGF5b3V0TmV0d29yayhwYXN0ZSgnZm9yY2UtZGlyZWN0ZWQnLCAKICAgICAgICAgICAgICAnZGVmYXVsdFNwcmluZ0NvZWZmaWNpZW50PTAuMDAwMDEnLAogICAgICAgICAgICAgICdkZWZhdWx0U3ByaW5nTGVuZ3RoPTUwJywKICAgICAgICAgICAgICAnZGVmYXVsdE5vZGVNYXNzPTQnLAogICAgICAgICAgICAgIHNlcD0nICcpKQpgYGAKCkZvY3VzaW5nIG9uIHRoZSBjb25uZWN0ZWQgcGFydCBvZiB0aGUgbmV0d29yaywgaXQgc2hvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvc3RyaW5nLWRlLWRvd24tZm9yY2VkaXJlY3RlZC5wbmcpCgojIyBTVFJJTkcgRW5yaWNobWVudAoKTm93IHdlIGNhbiBwZXJmb3JtIFNUUklORyBFbnJpY2htZW50IGFuYWx5c2lzIG9uIHRoZSByZXN1bHRpbmcgbmV0d29yazoKCmBgYHtyfQpjb21tYW5kc1J1bignc3RyaW5nIHJldHJpZXZlIGVucmljaG1lbnQgYWxsTmV0U3BlY2llcz0iSG9tbyBzYXBpZW5zIicpCmNvbW1hbmRzUnVuKCdzdHJpbmcgc2hvdyBlbnJpY2htZW50JykKYGBgCgpGaWx0ZXIgdGhlIGFuYWx5c2lzIHJlc3VsdHMgZm9yIG5vbi1yZWR1bmRhbnQgR08gUHJvY2VzcyB0ZXJtcyBvbmx5LiAKCmBgYHtyfQpjb21tYW5kc1J1bignc3RyaW5nIGZpbHRlciBlbnJpY2htZW50IGNhdGVnb3JpZXM9IkdPIEJpb2xvZ2ljYWwgUHJvY2VzcyIsIG92ZXJsYXBDdXRvZmYgPSAiMC41IiwgcmVtb3ZlT3ZlcmxhcHBpbmcgPSAidHJ1ZSInKQpgYGAKCmBgYHtyfQpjb21tYW5kc1J1bignc3RyaW5nIHNob3cgY2hhcnRzJykKYGBgCgojIyBTVFJJTkcgRGlzZWFzZSBRdWVyeQoKU28gZmFyLCB3ZSBxdWVyaWVkIHRoZSBTVFJJTkcgZGF0YWJhc2Ugd2l0aCBhIHNldCBvZiBnZW5lcyB3ZSBrbmV3IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBOZXh0LCB3ZSB3aWxsIHF1ZXJ5IHRoZSAqKlNUUklORyBkaXNlYXNlKiogZGF0YWJhc2UgdG8gcmV0cmlldmUgYSBuZXR3b3JrIGdlbmVzIGFzc29jaWF0ZWQgd2l0aCBvdmFyaWFuIGNhbmNlciwgd2hpY2ggd2lsbCBiZSBjb21wbGV0ZWx5IGluZGVwZW5kZW50IG9mIG91ciBkYXRhc2V0LgoKYGBge3J9CmNvbW1hbmRzUnVuKCdzdHJpbmcgZGlzZWFzZSBxdWVyeSBkaXNlYXNlPSJvdmFyaWFuIGNhbmNlciIgY3V0b2ZmPSIwLjk1IicpCmBgYAoKVGhpcyB3aWxsIGJyaW5nIGluIHRoZSB0b3AgMTAwIChkZWZhdWx0KSBvdmFyaWFuIGNhbmNlciBhc3NvY2lhdGVkIGdlbmVzIGNvbm5lY3RlZCB3aXRoIGEgY29uZmlkZW5jZSBzY29yZSBncmVhdGVyIHRoYW4gMC45NS4gQWdhaW4sIGxldHMgZXh0cmFjdCBvdXQgdGhlIGNvbm5lY3RlZCBub2RlczoKCmBgYHtyfQpjb21tYW5kc1J1bigibmV0d29yayBzZWxlY3Qgc3VibmV0d29yayBjcmVhdGVTdWJuZXR3b3JrPXRydWUiKQpgYGAKCiMgRGF0YSBpbnRlZ3JhdGlvbgoKTmV4dCB3ZSB3aWxsIGxvYWQgdGhlIHNhbWUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZyb20gb3VyIFRDR0EgZGF0YXNldCB0byBjcmVhdGUgYSB2aXN1YWxpemF0aW9uLiBUaGlzIHRpbWUgd2Ugd2lsbCBuZWVkIHRvIGRvIHNvbWUgaWRlbnRpZmllciBtYXBwaW5nIHRvIG1hdGNoIHRoZSBkYXRhIHRvIHRoZSBuZXR3b3JrLgoKYGBge3J9Cm1hcHBlZC5jb2xzIDwtIG1hcFRhYmxlQ29sdW1uKCJkaXNwbGF5IG5hbWUiLCdIdW1hbicsJ0hHTkMnLCdFbnRyZXogR2VuZScpCmBgYAoKSGVyZSB3ZSBzZXQgKipIdW1hbioqIGFzIHNwZWNpZXMsICoqSEdOQyoqIGFzICoqTWFwIGZyb20qKiwgYW5kICoqRW50cmV6IEdlbmUqKiBhcyAqKlRvKiouCgpXZSBjYW4gbm93IGltcG9ydCB0aGUgZGF0YSBmcmFtZSB3aXRoIHRoZSBkYXRhIGludG8gdGhlIG5vZGUgdGFibGUgaW4gQ3l0b3NjYXBlOgoKYGBge3J9CmxvYWRUYWJsZURhdGEoZGUuZ2VuZXMsIGRhdGEua2V5LmNvbHVtbiA9ICJHZW5lIiwgdGFibGUgPSAibm9kZSIsIHRhYmxlLmtleS5jb2x1bW4gPSAiRW50cmV6IEdlbmUiKQpgYGAKCiMgVmlzdWFsaXphdGlvbgoKQWdhaW4sIHdlIGNhbiBjcmVhdGUgYSB2aXN1YWxpemF0aW9uIHJldXNpbmcgb3VyIHByaW9yIHN0eWxlIGFuZCBzZXR0aW5nIGEgbmV3IGNvbG9yIGdyYWRpZW50IGZvciB0aGUgbm9kZXM6CgpgYGB7cn0KY29weVZpc3VhbFN0eWxlKGZyb20uc3R5bGUgPSAiZGUgZ2VuZXMgdXAiLCB0by5zdHlsZSA9ICJvdmFyaWFuIikKc2V0VmlzdWFsU3R5bGUoc3R5bGUubmFtZT0ib3ZhcmlhbiIpCgpzZXROb2RlQ29sb3JNYXBwaW5nKCdsb2dGQycsIGNvbG9ycz1wYWxldHRlQ29sb3JCcmV3ZXJSZEJ1LCBzdHlsZS5uYW1lPSJvdmFyaWFuIikKYGBgCkFwcGx5IGEgZm9yY2UtZGlyZWN0ZWQgbGF5b3V0LgoKYGBge3J9CmxheW91dE5ldHdvcmsocGFzdGUoJ2ZvcmNlLWRpcmVjdGVkJywgCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdDb2VmZmljaWVudD0wLjAwMDAwNicsCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdMZW5ndGg9NTAnLAogICAgICAgICAgICAgICdkZWZhdWx0Tm9kZU1hc3M9MicsCiAgICAgICAgICAgICAgc2VwPScgJykpCmBgYCAKClRoZSBUQ0dBIGZvdW5kIHNldmVyYWwgZ2VuZXMgdGhhdCB3ZXJlIGNvbW1vbmx5IG11dGF0ZWQgaW4gb3ZhcmlhbiBjYW5jZXIsIHNvIGNhbGxlZCAiY2FuY2VyIGRyaXZlcnMiLgpXZSBjYW4gYWRkIGluZm9ybWF0aW9uIGFib3V0IHRoZXNlIGdlbmVzIHRvIHRoZSBuZXR3b3JrIHZpc3VhbGl6YXRpb24sIGJ5IGNoYW5naW5nIHRoZSB2aXN1YWwgc3R5bGUgb2YgdGhlc2Ugbm9kZXMuClRocmVlIG9mIHRoZSBtb3N0IGltcG9ydGFudCBkcml2ZXJzIGFyZSAqKlRQNTMqKiwgKipCUkNBMSoqIGFuZCAqKkJSQ0EyKiouCldlIHdpbGwgYWRkIGEgdGhpY2tlciwgY29sb3JlZCBib3JkZXIgZm9yIHRoZXNlIGdlbmVzIGluIHRoZSBuZXR3b3JrLgoKU2VsZWN0IGFsbCB0aHJlZSBkcml2ZXIgZ2VuZXMgYnk6CgpgYGB7cn0Kc2VsZWN0Tm9kZXMoYygiVFA1MyIsICJCUkNBMSIsICJCUkNBMiIpLCBieS5jb2wgPSAiZGlzcGxheSBuYW1lIikKYGBgCgpBZGQgYSBzdHlsZSBieXBhc3MgZm9yIG5vZGUgKipCb3JkZXIgV2lkdGgqKiAoNSkgYW5kIG5vZGUgKipCb3JkZXIgUGFpbnQqKiAoYnJpZ2h0IHBpbmspOgoKYGBge3J9CnNldE5vZGVCb3JkZXJXaWR0aEJ5cGFzcyhnZXRTZWxlY3RlZE5vZGVzKCksIDUpCnNldE5vZGVCb3JkZXJDb2xvckJ5cGFzcyhnZXRTZWxlY3RlZE5vZGVzKCksICcjRkYwMDdGJykKYGBgCgo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9zdHJpbmctb3Zhcmlhbi1maW5hbC5wbmcpIAo8L2NlbnRlcj4KCiMgRXhwb3J0aW5nIE5ldHdvcmtzCkN5dG9zY2FwZSBwcm92aWRlcyBhIG51bWJlciBvZiB3YXlzIHRvIGV4cG9ydCByZXN1bHRzIGFuZCB2aXN1YWxpemF0aW9uczoKCi0gQXMgYW4gaW1hZ2U6CgpgYGB7cn0KZXhwb3J0SW1hZ2UoJ2RpZmZlcmVudGlhbGx5LWV4cHJlc3NlZC1nZW5lcycsICdQREYnKQpgYGAKCipOb3RlOiBQTkcsIFNWRywgSlBFRyBhbmQgUFMgYXJlIGFsc28gc3VwcG9ydGVkKgoKLSBUbyBhIHB1YmxpYyByZXBvc2l0b3J5OgoKYGBgCmV4cG9ydE5ldHdvcmtUb05ERXgoInVzZXIiLCAicGFzc3dvcmQiLCBUUlVFKSAjcmVxdWlyZXMgYSBmcmVlIE5ERXggYWNjb3VudApgYGA=