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

installApp('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:

  • As an image:
exportImage('differentially-expressed-genes', 'PDF')

Note: PNG, SVG, JPEG and PS are also supported

  • To a public repository:
exportNetworkToNDEx("user", "password", TRUE) #requires a free NDEx account
LS0tCnRpdGxlOiAiRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIEdlbmVzIE5ldHdvcmsgQW5hbHlzaXMiCmF1dGhvcjogIktvem8gTmlzaGlkYSwgS3Jpc3RpbmEgSGFuc3BlcnMgYW5kIEFsZXggUGljbyIKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazoKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiAibm9uZSIKLS0tCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBldmFsPUZBTFNFCikKYGBgCgoqVGhlIFIgbWFya2Rvd24gaXMgYXZhaWxhYmxlIGZyb20gdGhlIHB1bGxkb3duIG1lbnUgZm9yKiBDb2RlICphdCB0aGUgdXBwZXItcmlnaHQsIGNob29zZSAiRG93bmxvYWQgUm1kIiwgb3IgW2Rvd25sb2FkIHRoZSBSbWQgZnJvbSBHaXRIdWJdKGh0dHBzOi8vcmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbS9jeXRvc2NhcGUvY3l0b3NjYXBlLWF1dG9tYXRpb24vbWFzdGVyL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvRGlmZmVyZW50aWFsbHktZXhwcmVzc2VkLWdlbmVzLlJtZCkuKgoKPGhyIC8+CgpUaGlzIHByb3RvY29sIGRlc2NyaWJlcyBhIG5ldHdvcmsgYW5hbHlzaXMgd29ya2Zsb3cgaW4gQ3l0b3NjYXBlIGZvciBhIHNldCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuIFBvaW50cyBjb3ZlcmVkOgoKLSBSZXRyaWV2aW5nIHJlbGV2YW50IG5ldHdvcmtzIGZyb20gcHVibGljIGRhdGFiYXNlcwotIE5ldHdvcmsgZnVuY3Rpb25hbCBlbnJpY2htZW50IGFuYWx5c2lzCi0gSW50ZWdyYXRpb24gYW5kIHZpc3VhbGl6YXRpb24gb2YgZXhwZXJpbWVudGFsIGRhdGEKLSBFeHBvcnRpbmcgbmV0d29yayB2aXN1YWxpemF0aW9ucwoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvc3RyaW5nLW92YXJpYW4tZmluYWwucG5nKQo8L2NlbnRlcj4KCjxociAvPgoKIyBJbnN0YWxsYXRpb24KYGBge3IsIGV2YWwgPSBGQUxTRX0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSl7CiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQp9CgppZighIlJDeTMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpewogIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKfQpsaWJyYXJ5KFJDeTMpCmBgYAoKIyBHZXR0aW5nIHN0YXJ0ZWQKRmlyc3QsIGxhdW5jaCBDeXRvc2NhcGUgYW5kIGtlZXAgaXQgcnVubmluZyB3aGVuZXZlciB1c2luZyBSQ3kzLiBDb25maXJtIHRoYXQgeW91IGhhdmUgZXZlcnl0aGluZyBpbnN0YWxsZWQgYW5kIHJ1bm5pbmc6CmBgYHtyfQpjeXRvc2NhcGVQaW5nKCkKY3l0b3NjYXBlVmVyc2lvbkluZm8oKQpgYGAKCiMgUHJlcmVxdWlzaXRlcwoKSWYgeW91IGhhdmVuJ3QgYWxyZWFkeSwgaW5zdGFsbCB0aGUgW1NUUklOR2FwcF0oaHR0cDovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL3N0cmluZ2FwcCkKCmBgYHtyfQppbnN0YWxsQXBwKCdzdHJpbmdBcHAnKQpgYGAKCiMgQmFja2dyb3VuZAoKT3ZhcmlhbiBzZXJvdXMgY3lzdGFkZW5vY2FyY2lub21hIGlzIGEgdHlwZSBvZiBlcGl0aGVsaWFsIG92YXJpYW4gY2FuY2VyIHdoaWNoIGFjY291bnRzIGZvciB+OTAlIG9mIGFsbCBvdmFyaWFuIGNhbmNlcnMuClRoZSBkYXRhIHVzZWQgaW4gdGhpcyBwcm90b2NvbCBhcmUgZnJvbSBbVGhlIENhbmNlciBHZW5vbWUgQXRsYXNdKGh0dHBzOi8vY2FuY2VyZ2Vub21lLm5paC5nb3YvKSwgaW4gd2hpY2ggbXVsdGlwbGUgc3VidHlwZXMgb2Ygc2Vyb3VzIGN5c3RhZGVub2NhcmNpbm9tYSB3ZXJlIGlkZW50aWZpZWQgYW5kIGNoYXJhY3Rlcml6ZWQgYnkgbVJOQSBleHByZXNzaW9uLgoKV2Ugd2lsbCBmb2N1cyBvbiB0aGUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBiZXR3ZWVuIHR3byBzdWJ0eXBlcywgKipNZXNlbmNoeW1hbCoqIGFuZCAqKkltbXVub3JlYWN0aXZlKiouCgpGb3IgY29udmVuaWVuY2UsIHRoZSBkYXRhIGhhcyBhbHJlYWR5IGJlZW4gYW5hbHl6ZWQgYW5kIHByZS1maWx0ZXJlZCwgdXNpbmcgbG9nIGZvbGQgY2hhbmdlIHZhbHVlIGFuZCBhZGp1c3RlZCBwLXZhbHVlLgoKIyBOZXR3b3JrIFJldHJpZXZhbAoKTWFueSBwdWJsaWMgZGF0YWJhc2VzIGFuZCBtdWx0aXBsZSBDeXRvc2NhcGUgYXBwcyBhbGxvdyB5b3UgdG8gcmV0cmlldmUgYSBuZXR3b3JrIG9yIHBhdGh3YXkgcmVsZXZhbnQgdG8geW91ciBkYXRhLgpGb3IgdGhpcyB3b3JrZmxvdywgd2Ugd2lsbCB1c2UgdGhlIFNUUklORyBhcHAuIFNvbWUgb3RoZXIgb3B0aW9ucyBpbmNsdWRlOgoKLSBbV2lraVBhdGh3YXlzXShodHRwczovL25ybmIub3JnL2dzb2QyMDE5X2tvem9fbmlzaGlkYS9odG1sX2RvY3VtZW50cy9SbWQvd2lraXBhdGh3YXlzLWFwcC5odG1sKQotIFtOREV4XShodHRwOi8vd3d3Lm5kZXhiaW8ub3JnLykKLSBbR2VuZU1BTklBXShodHRwczovL2dlbmVtYW5pYS5vcmcvKQoKIyBSZXRyaWV2ZSBOZXR3b3JrcyBmcm9tIFNUUklORwoKVG8gaWRlbnRpZnkgYSByZWxldmFudCBuZXR3b3JrLCB3ZSB3aWxsIHF1ZXJ5IHRoZSBTVFJJTkcgZGF0YWJhc2UgaW4gdHdvIGRpZmZlcmVudCB3YXlzOgoKLSBRdWVyeSAqKlNUUklORyBwcm90ZWluKiogd2l0aCB0aGUgbGlzdCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuCi0gUXVlcnkgKipTVFJJTkcgZGlzZWFzZSoqIGZvciBhIGtleXdvcmQ7ICoqb3ZhcmlhbiBjYW5jZXIqKi4KClRoZSB0d28gZXhhbXBsZXMgYXJlIHNwbGl0IGludG8gdHdvIHNlcGFyYXRlIHdvcmtmbG93cyBiZWxvdy4KCiMgRXhhbXBsZSAxOiBTVFJJTkcgUHJvdGVpbiBRdWVyeSBVcC1yZWd1bGF0ZWQgR2VuZXMKCkxvYWQgdGhlIGZpbGUgY29udGFpbmluZyB0aGUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGFuZCBmaWx0ZXIgZm9yIHVwLXJlZ3VsYXRlZCBnZW5lcywgVENHQS1PdmFyaWFuLU1lc2VudnNJbW11bm9fZGF0YS5jc3Y6CgpgYGB7cn0KZGUuZ2VuZXMgPC0gcmVhZC50YWJsZSgiaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2N5dG9zY2FwZS9jeXRvc2NhcGUtdHV0b3JpYWxzL2doLXBhZ2VzL3Byb3RvY29scy9kYXRhL1RDR0EtT3Zhcmlhbi1NZXNlbnZzSW1tdW5vX2RhdGEuY3N2IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gIiwiLCBxdW90ZT0iXCIiLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmRlLmdlbmVzLnVwIDwtIGRlLmdlbmVzW3doaWNoKGRlLmdlbmVzJGxvZ0ZDID49IDEuOCAmIGRlLmdlbmVzJEZEUi5hZGp1c3RlZC5QdmFsdWUgPD0gMC4wNSksXQpgYGAKCldlIHdpbGwgdXNlIHRoZSBpZGVudGlmaWVycyBpbiB0aGUgZmlyc3QgY29sdW1uIG9mIHRoaXMgZGF0YWZpbGUgdG8gcnVuIGEgKipTVFJJTkcgcHJvdGVpbiBxdWVyeSoqLCB3aXRoIGNvbmZpZGVuY2UgKHNjb3JlKSBjdXRvZmYgb2YgMC40IGFuZCBubyBhZGRpdGlvbmFsIHByb3RlaW5zIChsaW1pdCA9IDApOgoKYGBge3J9CnN0cmluZy5jbWQgPSBwYXN0ZSgnc3RyaW5nIHByb3RlaW4gcXVlcnkgcXVlcnk9IicsIHBhc3RlKGRlLmdlbmVzLnVwJEdlbmUsIGNvbGxhcHNlID0gJ1xuJyksICciIGN1dG9mZj0wLjQgbGltaXQ9MCBzcGVjaWVzPSJIb21vIHNhcGllbnMiJywgc2VwID0gIiIpCmNvbW1hbmRzUnVuKHN0cmluZy5jbWQpCmBgYAoKVGhlIHJlc3VsdGluZyBuZXR3b3JrIHdpbGwgbG9hZCBhdXRvbWF0aWNhbGx5IGFuZCBjb250YWlucyB1cC1yZWd1bGF0ZWQgZ2VuZXMgcmVjb2duaXplZCBieSBTVFJJTkcsIGFuZCBpbnRlcmFjdGlvbnMgYmV0d2VlbiB0aGVtIHdpdGggYW4gZXZpZGVuY2Ugc2NvcmUgb2YgMC40IG9yIGdyZWF0ZXIuCgo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9zdHJpbmctZGUtdXAyLnBuZykKPC9jZW50ZXI+CgpUaGUgbmV0d29ya3MgY29uc2lzdHMgb2Ygb25lIGxhcmdlIGNvbm5lY3RlZCBjb21wb25lbnQsIHNldmVyYWwgc21hbGxlciBuZXR3b3JrcywgYW5kIHNvbWUgdW5jb25uZWN0ZWQgbm9kZXMuIFdlIHdpbGwgeW91IGEgaGFuZHkgdXRpbGl0eSBhcHAgZm9yIEN5dG9zY2FwZSB0byBzZWxlY3QgdGhlIGxhcmdlc3QgY29ubmVjdGVkIGNvbXBvbmVudCBhbmQgY3JlYXRlIGEgc3VibmV0d29yayB3aXRoIGl0LgoKYGBge3J9Cmluc3RhbGxBcHAoIkxhcmdlc3QgU3VibmV0d29yayIpCmNvbW1hbmRzUnVuKCJuZXR3b3JrIHNlbGVjdCBzdWJuZXR3b3JrIGNyZWF0ZVN1Ym5ldHdvcms9dHJ1ZSIpCmBgYAoKIyBEYXRhIEludGVncmF0aW9uCgpOZXh0IHdlIHdpbGwgY3JlYXRlIGEgdmlzdWFsaXphdGlvbiBmcm9tIHRoZSBsb2cgZm9sZCBjaGFuZ2VzIGFuZCBwLXZhbHVlcyBmcm9tIG91ciBUQ0dBIGRhdGFzZXQuIE91ciBkYXRhIGZyb20gVENHQSBoYXMgTkNCSSBHZW5lIGlkZW50aWZpZXJzLCBhbmQgb3VyIFNUUklORyBuZXR3b3JrIHJldGFpbmVkIHRoZXNlIGlkZW50aWZpZXJzIGluIHRoZSAicXVlcnkgdGVybSIgY29sdW1uLgoKYGBge3J9CmxvYWRUYWJsZURhdGEoZGUuZ2VuZXMsIGRhdGEua2V5LmNvbHVtbj0iR2VuZSIsIHRhYmxlLmtleS5jb2x1bW49InF1ZXJ5IHRlcm0iKQpgYGAKCllvdSB3aWxsIG5vdGljZSB0d28gbmV3IGNvbHVtbnMgKGxvZ0ZDIGFuZCBGRFIuYWRqdXN0ZWQuUHZhbHVlKSBpbiB0aGUgTm9kZSBUYWJsZS4gCgpgYGB7cn0KdGFpbChnZXRUYWJsZUNvbHVtbk5hbWVzKCdub2RlJykpCmBgYAoKIyBWaXN1YWxpemF0aW9uCk5leHQsIHdlIHdpbGwgY3JlYXRlIGEgdmlzdWFsaXphdGlvbiBvZiB0aGUgaW1wb3J0ZWQgZGF0YSBvbiB0aGUgbmV0d29yay4gTGV0J3Mgc2V0IGFsbCB0aGUgZGVmYXVsdHMgZmlyc3Q6CgpgYGB7cn0KY29weVZpc3VhbFN0eWxlKGZyb20uc3R5bGUgPSAiZGVmYXVsdCIsIHRvLnN0eWxlID0gImRlIGdlbmVzIHVwIikKc2V0VmlzdWFsU3R5bGUoImRlIGdlbmVzIHVwIikKc2V0Tm9kZVNoYXBlRGVmYXVsdCgiRUxMSVBTRSIsICJkZSBnZW5lcyB1cCIpCmxvY2tOb2RlRGltZW5zaW9ucygiVFJVRSIsICJkZSBnZW5lcyB1cCIpCnNldE5vZGVTaXplRGVmYXVsdCgiNTAiLCAiZGUgZ2VuZXMgdXAiKQpzZXROb2RlQ29sb3JEZWZhdWx0KCIjRDNEM0QzIiwgImRlIGdlbmVzIHVwIikKc2V0Tm9kZUJvcmRlcldpZHRoRGVmYXVsdCgiMiIsICJkZSBnZW5lcyB1cCIpCnNldE5vZGVCb3JkZXJDb2xvckRlZmF1bHQoIiM2MTYwNjAiLCAiZGUgZ2VuZXMgdXAiKQpzZXROb2RlTGFiZWxNYXBwaW5nKCJkaXNwbGF5IG5hbWUiLCAiZGUgZ2VuZXMgdXAiKQpzZXROb2RlRm9udFNpemVEZWZhdWx0KCIxNCIsICJkZSBnZW5lcyB1cCIpCnNldEVkZ2VPcGFjaXR5RGVmYXVsdCgxMDAsICJkZSBnZW5lcyB1cCIpCmBgYAoKVGhlbiB3ZSBjYW4gdXNlIHRoZSAqKmxvZ0ZDKiogdmFsdWVzIHRvIHByb2R1Y2UgYSBncmFkaWVudCBvZiBub2RlIGZpbGwgY29sb3JzOgoKYGBge3J9CnNldE5vZGVDb2xvck1hcHBpbmcoJ2xvZ0ZDJywgY29sb3JzPXBhbGV0dGVDb2xvckJyZXdlcllsT3JSZCwgc3R5bGUubmFtZT0iZGUgZ2VuZXMgdXAiKQpgYGAKCipOb3RlOiB3ZSBhcmUgdXNpbmcgYHBhbGV0dGVDb2xvckJyZXdlcllsT3JSZGAgdG8gZ2VuZXJhdGUgYSBzZXQgb2Ygc3RhbmRhcmRpemVkIGNvbG9ycyBiYWxhbmNlZCBmb3IgYSBzZXF1ZW50aWFsIGdyYWRpZW50IChsb3cgdG8gaGlnaCksIHdoaWNoIGFyZSBhdXRvbWF0aWNhbGx5IG1hdGNoZWQgdG8gdGhlIGBsb2dGQ2AgY29sdW1uIHZhbHVlcy4qCgpBcHBseWluZyBhIGZvcmNlLWRpcmVjdGVkIGxheW91dCwgdGhlIG5ldHdvcmsgd2lsbCBub3cgbG9vayBzb21ldGhpbmcgbGlrZSB0aGlzOgoKYGBge3J9CmxheW91dE5ldHdvcmsocGFzdGUoJ2ZvcmNlLWRpcmVjdGVkJywgCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdDb2VmZmljaWVudD0wLjAwMDAwOScsCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdMZW5ndGg9NTAnLAogICAgICAgICAgICAgICdkZWZhdWx0Tm9kZU1hc3M9NCcsCiAgICAgICAgICAgICAgc2VwPScgJykpCmBgYAoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvc3RyaW5nLWRlLXVwLWZvcmNlZGlyZWN0ZWQucG5nKQo8L2NlbnRlcj4KCiMgRW5yaWNobWVudCBBbmFseXNpcyBPcHRpb25zCgpOZXh0LCB3ZSBhcmUgZ29pbmcgdG8gcGVyZm9ybSBlbnJpY2htZW50IGFuYWx5c2lzIHVpbmcgdGhlIFNUUklORyBhcHAuCgojIyBTVFJJTkcgRW5yaWNobWVudAoKVGhlIFNUUklORyBhcHAgaGFzIGJ1aWx0LWluIGVucmljaG1lbnQgYW5hbHlzaXMgZnVuY3Rpb25hbGl0eSwgd2hpY2ggaW5jbHVkZXMgZW5yaWNobWVudCBmb3IgR08gUHJvY2VzcywgR08gQ29tcG9uZW50LCBHTyBGdW5jdGlvbiwgSW50ZXJQcm8sIEtFR0cgUGF0aHdheXMsIGFuZCBQRkFNLgoKRmlyc3QsIHdlIHdpbGwgcnVuIHRoZSBlbnJpY2htZW50IG9uIHRoZSB3aG9sZSBuZXR3b3JrLCBhZ2FpbnN0IHRoZSBnZW5vbWU6IAoKYGBge3J9CmNvbW1hbmRzUnVuKCdzdHJpbmcgcmV0cmlldmUgZW5yaWNobWVudCBhbGxOZXRTcGVjaWVzPSJIb21vIHNhcGllbnMiJykKY29tbWFuZHNSdW4oJ3N0cmluZyBzaG93IGVucmljaG1lbnQnKQpgYGAKCldoZW4gdGhlIGVucmljaG1lbnQgYW5hbHlzaXMgaXMgY29tcGxldGUsIGEgbmV3IHRhYiB0aXRsZWQgKipTVFJJTkcgRW5yaWNobWVudCoqIHdpbGwgb3BlbiBpbiB0aGUgKipUYWJsZSBQYW5lbCoqLgoKPGNlbnRlcj4KIVtdKGh0dHBzOi8vY3l0b3NjYXBlLm9yZy9jeXRvc2NhcGUtYXV0b21hdGlvbi9mb3Itc2NyaXB0ZXJzL1Ivbm90ZWJvb2tzL2RhdGEvaW1nL3N0cmluZy1lbnJpY2htZW50LnBuZykKPC9jZW50ZXI+CgpUaGUgU1RSSU5HIGFwcCBpbmNsdWRlcyBzZXZlcmFsIG9wdGlvbnMgZm9yIGZpbHRlcmluZyBhbmQgZGlzcGxheWluZyB0aGUgZW5yaWNobWVudCByZXN1bHRzLgpUaGUgZmVhdHVyZXMgYXJlIGFsbCBhdmFpbGFibGUgYXQgdGhlIHRvcCBvZiB0aGUgKipTVFJJTkcgRW5yaWNobWVudCoqIHRhYi4KCldlIGFyZSBnb2luZyB0byBmaWx0ZXIgdGhlIHRhYmxlIHRvIG9ubHkgc2hvdyBHTyBQcm9jZXNzOgoKYGBge3J9CmNvbW1hbmRzUnVuKCdzdHJpbmcgZmlsdGVyIGVucmljaG1lbnQgY2F0ZWdvcmllcz0iR08gQmlvbG9naWNhbCBQcm9jZXNzIiwgb3ZlcmxhcEN1dG9mZiA9ICIwLjUiLCByZW1vdmVPdmVybGFwcGluZyA9ICJ0cnVlIicpCmBgYAoKTmV4dCwgd2Ugd2lsbCBhZGQgYSBzcGxpdCBkb251dCBjaGFydCB0byB0aGUgbm9kZXMgcmVwcmVzZW50aW5nIHRoZSB0b3AgdGVybXM6CgpgYGB7cn0KY29tbWFuZHNSdW4oJ3N0cmluZyBzaG93IGNoYXJ0cycpCmBgYAoKIyMgU1RSSU5HIFByb3RlaW4gUXVlcnk6IERvd24tcmVndWxhdGVkIGdlbmVzCgpXZSBhcmUgZ29pbmcgdG8gcmVwZWF0IHRoZSBuZXR3b3JrIHNlYXJjaCwgZGF0YSBpbnRlZ3JhdGlvbiwgdmlzdWFsaXphdGlvbiBhbmQgZW5yaWNobWVudCBhbmFseXNpcyBmb3IgdGhlIHNldCBvZiBkb3duLXJlZ3VsYXRlZCBnZW5lcyBieSBzdWJzZXR0aW5nIG91ciBvcmlnaW5hbCBkYXRhc2V0IGZvciBkb3ducmVndWxhdGVkIGxvZ0ZDIGdlbmVzOgoKYGBge3J9CmRlLmdlbmVzLmRvd24gPC0gZGUuZ2VuZXNbd2hpY2goZGUuZ2VuZXMkbG9nRkMgPD0gLTEuMSAmIGRlLmdlbmVzJEZEUi5hZGp1c3RlZC5QdmFsdWUgPD0gMC4wNSksXQpzdHJpbmcuY21kID0gcGFzdGUoJ3N0cmluZyBwcm90ZWluIHF1ZXJ5IHF1ZXJ5PSInLCBwYXN0ZShkZS5nZW5lcy5kb3duJEdlbmUsIGNvbGxhcHNlID0gJ1xuJyksICciIGN1dG9mZj0wLjQgbGltaXQ9MCBzcGVjaWVzPSJIb21vIHNhcGllbnMiJywgc2VwID0gIiIpCmNvbW1hbmRzUnVuKHN0cmluZy5jbWQpCmBgYAoKIyMgU3VibmV0d29yawoKTGV0J3Mgc2VsZWN0IG9ubHkgdGhlIGNvbm5lY3RlZCBub2RlcyB0byB3b3JrIHdpdGggZm9yIHRoZSByZXN0IG9mIHRoaXMgdHV0b3JpYWwsIGJ5IGNyZWF0aW5nIGEgc3VibmV0d29yayBiYXNlZCBvbiBhbGwgZWRnZXM6CgpgYGB7cn0KY29tbWFuZHNSdW4oIm5ldHdvcmsgc2VsZWN0IHN1Ym5ldHdvcmsgY3JlYXRlU3VibmV0d29yaz10cnVlIikKYGBgCgojIyBEYXRhIGludGVncmF0aW9uCgpBZ2Fpbiwgd2UgY2FuIGltcG9ydCB0aGUgZGF0YSwgbWFwcGluZyB0byB0aGUgInF1ZXJ5IHRlcm0iIGNvbHVtbjoKCmBgYHtyfQpsb2FkVGFibGVEYXRhKGRlLmdlbmVzLGRhdGEua2V5LmNvbHVtbj0iR2VuZSIsdGFibGUua2V5LmNvbHVtbj0icXVlcnkgdGVybSIpCmBgYAoKIyMgVmlzdWFsaXphdGlvbgpOZXh0LCB3ZSBjYW4gY3JlYXRlIGEgdmlzdWFsaXphdGlvbi4gTm90ZSB0aGF0IHdlIGNhbiByZXVzZSBtb3N0IG9mIHRoZSBwcmV2aW91cyBzdHlsZSAoZS5nLiwgYWxsIHRoZSBkZWZhdWx0IHNldHRpbmdzKSBhbmQganVzdCBwcm92aWRlIGEgbmV3IG5vZGUgY29sb3IgbWFwcGluZyB3aXRoIGRvd25yZWd1bGF0ZWQgY29sb3Igc2NoZW1lLCBsaWtlIGBwYWxldHRlQ29sb3JCcmV3ZXJCbHVlc2A6CgpgYGB7cn0KY29weVZpc3VhbFN0eWxlKGZyb20uc3R5bGUgPSAiZGUgZ2VuZXMgdXAiLCB0by5zdHlsZSA9ICJkZSBnZW5lcyBkb3duIikKc2V0VmlzdWFsU3R5bGUoc3R5bGUubmFtZT0iZGUgZ2VuZXMgZG93biIpCgpzZXROb2RlQ29sb3JNYXBwaW5nKCdsb2dGQycsIGNvbG9ycz1wYWxldHRlQ29sb3JCcmV3ZXJCbHVlcywgc3R5bGUubmFtZT0iZGUgZ2VuZXMgZG93biIpCmBgYAoKQXBwbHkgYSBmb3JjZS1kaXJlY3RlZCBsYXlvdXQuCgpgYGB7cn0KbGF5b3V0TmV0d29yayhwYXN0ZSgnZm9yY2UtZGlyZWN0ZWQnLCAKICAgICAgICAgICAgICAnZGVmYXVsdFNwcmluZ0NvZWZmaWNpZW50PTAuMDAwMDEnLAogICAgICAgICAgICAgICdkZWZhdWx0U3ByaW5nTGVuZ3RoPTUwJywKICAgICAgICAgICAgICAnZGVmYXVsdE5vZGVNYXNzPTQnLAogICAgICAgICAgICAgIHNlcD0nICcpKQpgYGAKCkZvY3VzaW5nIG9uIHRoZSBjb25uZWN0ZWQgcGFydCBvZiB0aGUgbmV0d29yaywgaXQgc2hvdWxkIGxvb2sgc29tZXRoaW5nIGxpa2UgdGhpczoKPGNlbnRlcj4KIVtdKC4vZGF0YS9pbWcvc3RyaW5nLWRlLWRvd24tZm9yY2VkaXJlY3RlZC5wbmcpCgojIyBTVFJJTkcgRW5yaWNobWVudAoKTm93IHdlIGNhbiBwZXJmb3JtIFNUUklORyBFbnJpY2htZW50IGFuYWx5c2lzIG9uIHRoZSByZXN1bHRpbmcgbmV0d29yazoKCmBgYHtyfQpjb21tYW5kc1J1bignc3RyaW5nIHJldHJpZXZlIGVucmljaG1lbnQgYWxsTmV0U3BlY2llcz0iSG9tbyBzYXBpZW5zIicpCmNvbW1hbmRzUnVuKCdzdHJpbmcgc2hvdyBlbnJpY2htZW50JykKYGBgCgpGaWx0ZXIgdGhlIGFuYWx5c2lzIHJlc3VsdHMgZm9yIG5vbi1yZWR1bmRhbnQgR08gUHJvY2VzcyB0ZXJtcyBvbmx5LiAKCmBgYHtyfQpjb21tYW5kc1J1bignc3RyaW5nIGZpbHRlciBlbnJpY2htZW50IGNhdGVnb3JpZXM9IkdPIEJpb2xvZ2ljYWwgUHJvY2VzcyIsIG92ZXJsYXBDdXRvZmYgPSAiMC41IiwgcmVtb3ZlT3ZlcmxhcHBpbmcgPSAidHJ1ZSInKQpgYGAKCmBgYHtyfQpjb21tYW5kc1J1bignc3RyaW5nIHNob3cgY2hhcnRzJykKYGBgCgojIyBTVFJJTkcgRGlzZWFzZSBRdWVyeQoKU28gZmFyLCB3ZSBxdWVyaWVkIHRoZSBTVFJJTkcgZGF0YWJhc2Ugd2l0aCBhIHNldCBvZiBnZW5lcyB3ZSBrbmV3IHdlcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBOZXh0LCB3ZSB3aWxsIHF1ZXJ5IHRoZSAqKlNUUklORyBkaXNlYXNlKiogZGF0YWJhc2UgdG8gcmV0cmlldmUgYSBuZXR3b3JrIGdlbmVzIGFzc29jaWF0ZWQgd2l0aCBvdmFyaWFuIGNhbmNlciwgd2hpY2ggd2lsbCBiZSBjb21wbGV0ZWx5IGluZGVwZW5kZW50IG9mIG91ciBkYXRhc2V0LgoKYGBge3J9CmNvbW1hbmRzUnVuKCdzdHJpbmcgZGlzZWFzZSBxdWVyeSBkaXNlYXNlPSJvdmFyaWFuIGNhbmNlciIgY3V0b2ZmPSIwLjk1IicpCmBgYAoKVGhpcyB3aWxsIGJyaW5nIGluIHRoZSB0b3AgMTAwIChkZWZhdWx0KSBvdmFyaWFuIGNhbmNlciBhc3NvY2lhdGVkIGdlbmVzIGNvbm5lY3RlZCB3aXRoIGEgY29uZmlkZW5jZSBzY29yZSBncmVhdGVyIHRoYW4gMC45NS4gQWdhaW4sIGxldHMgZXh0cmFjdCBvdXQgdGhlIGNvbm5lY3RlZCBub2RlczoKCmBgYHtyfQpjb21tYW5kc1J1bigibmV0d29yayBzZWxlY3Qgc3VibmV0d29yayBjcmVhdGVTdWJuZXR3b3JrPXRydWUiKQpgYGAKCiMgRGF0YSBpbnRlZ3JhdGlvbgoKTmV4dCB3ZSB3aWxsIGxvYWQgdGhlIHNhbWUgZGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBkYXRhIGZyb20gb3VyIFRDR0EgZGF0YXNldCB0byBjcmVhdGUgYSB2aXN1YWxpemF0aW9uLiBUaGlzIHRpbWUgd2Ugd2lsbCBuZWVkIHRvIGRvIHNvbWUgaWRlbnRpZmllciBtYXBwaW5nIHRvIG1hdGNoIHRoZSBkYXRhIHRvIHRoZSBuZXR3b3JrLgoKYGBge3J9Cm1hcHBlZC5jb2xzIDwtIG1hcFRhYmxlQ29sdW1uKCJkaXNwbGF5IG5hbWUiLCdIdW1hbicsJ0hHTkMnLCdFbnRyZXogR2VuZScpCmBgYAoKSGVyZSB3ZSBzZXQgKipIdW1hbioqIGFzIHNwZWNpZXMsICoqSEdOQyoqIGFzICoqTWFwIGZyb20qKiwgYW5kICoqRW50cmV6IEdlbmUqKiBhcyAqKlRvKiouCgpXZSBjYW4gbm93IGltcG9ydCB0aGUgZGF0YSBmcmFtZSB3aXRoIHRoZSBkYXRhIGludG8gdGhlIG5vZGUgdGFibGUgaW4gQ3l0b3NjYXBlOgoKYGBge3J9CmxvYWRUYWJsZURhdGEoZGUuZ2VuZXMsIGRhdGEua2V5LmNvbHVtbiA9ICJHZW5lIiwgdGFibGUgPSAibm9kZSIsIHRhYmxlLmtleS5jb2x1bW4gPSAiRW50cmV6IEdlbmUiKQpgYGAKCiMgVmlzdWFsaXphdGlvbgoKQWdhaW4sIHdlIGNhbiBjcmVhdGUgYSB2aXN1YWxpemF0aW9uIHJldXNpbmcgb3VyIHByaW9yIHN0eWxlIGFuZCBzZXR0aW5nIGEgbmV3IGNvbG9yIGdyYWRpZW50IGZvciB0aGUgbm9kZXM6CgpgYGB7cn0KY29weVZpc3VhbFN0eWxlKGZyb20uc3R5bGUgPSAiZGUgZ2VuZXMgdXAiLCB0by5zdHlsZSA9ICJvdmFyaWFuIikKc2V0VmlzdWFsU3R5bGUoc3R5bGUubmFtZT0ib3ZhcmlhbiIpCgpzZXROb2RlQ29sb3JNYXBwaW5nKCdsb2dGQycsIGNvbG9ycz1wYWxldHRlQ29sb3JCcmV3ZXJSZEJ1LCBzdHlsZS5uYW1lPSJvdmFyaWFuIikKYGBgCkFwcGx5IGEgZm9yY2UtZGlyZWN0ZWQgbGF5b3V0LgoKYGBge3J9CmxheW91dE5ldHdvcmsocGFzdGUoJ2ZvcmNlLWRpcmVjdGVkJywgCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdDb2VmZmljaWVudD0wLjAwMDAwNicsCiAgICAgICAgICAgICAgJ2RlZmF1bHRTcHJpbmdMZW5ndGg9NTAnLAogICAgICAgICAgICAgICdkZWZhdWx0Tm9kZU1hc3M9MicsCiAgICAgICAgICAgICAgc2VwPScgJykpCmBgYCAKClRoZSBUQ0dBIGZvdW5kIHNldmVyYWwgZ2VuZXMgdGhhdCB3ZXJlIGNvbW1vbmx5IG11dGF0ZWQgaW4gb3ZhcmlhbiBjYW5jZXIsIHNvIGNhbGxlZCAiY2FuY2VyIGRyaXZlcnMiLgpXZSBjYW4gYWRkIGluZm9ybWF0aW9uIGFib3V0IHRoZXNlIGdlbmVzIHRvIHRoZSBuZXR3b3JrIHZpc3VhbGl6YXRpb24sIGJ5IGNoYW5naW5nIHRoZSB2aXN1YWwgc3R5bGUgb2YgdGhlc2Ugbm9kZXMuClRocmVlIG9mIHRoZSBtb3N0IGltcG9ydGFudCBkcml2ZXJzIGFyZSAqKlRQNTMqKiwgKipCUkNBMSoqIGFuZCAqKkJSQ0EyKiouCldlIHdpbGwgYWRkIGEgdGhpY2tlciwgY29sb3JlZCBib3JkZXIgZm9yIHRoZXNlIGdlbmVzIGluIHRoZSBuZXR3b3JrLgoKU2VsZWN0IGFsbCB0aHJlZSBkcml2ZXIgZ2VuZXMgYnk6CgpgYGB7cn0Kc2VsZWN0Tm9kZXMoYygiVFA1MyIsICJCUkNBMSIsICJCUkNBMiIpLCBieS5jb2wgPSAiZGlzcGxheSBuYW1lIikKYGBgCgpBZGQgYSBzdHlsZSBieXBhc3MgZm9yIG5vZGUgKipCb3JkZXIgV2lkdGgqKiAoNSkgYW5kIG5vZGUgKipCb3JkZXIgUGFpbnQqKiAoYnJpZ2h0IHBpbmspOgoKYGBge3J9CnNldE5vZGVCb3JkZXJXaWR0aEJ5cGFzcyhnZXRTZWxlY3RlZE5vZGVzKCksIDUpCnNldE5vZGVCb3JkZXJDb2xvckJ5cGFzcyhnZXRTZWxlY3RlZE5vZGVzKCksICcjRkYwMDdGJykKYGBgCgo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9zdHJpbmctb3Zhcmlhbi1maW5hbC5wbmcpIAo8L2NlbnRlcj4KCiMgRXhwb3J0aW5nIE5ldHdvcmtzCkN5dG9zY2FwZSBwcm92aWRlcyBhIG51bWJlciBvZiB3YXlzIHRvIGV4cG9ydCByZXN1bHRzIGFuZCB2aXN1YWxpemF0aW9uczoKCi0gQXMgYW4gaW1hZ2U6CgpgYGB7cn0KZXhwb3J0SW1hZ2UoJ2RpZmZlcmVudGlhbGx5LWV4cHJlc3NlZC1nZW5lcycsICdQREYnKQpgYGAKCipOb3RlOiBQTkcsIFNWRywgSlBFRyBhbmQgUFMgYXJlIGFsc28gc3VwcG9ydGVkKgoKLSBUbyBhIHB1YmxpYyByZXBvc2l0b3J5OgoKYGBgCmV4cG9ydE5ldHdvcmtUb05ERXgoInVzZXIiLCAicGFzc3dvcmQiLCBUUlVFKSAjcmVxdWlyZXMgYSBmcmVlIE5ERXggYWNjb3VudApgYGA=