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 vignette will demonstrate network retrieval from the STRING
database, basic analysis, loading and visualization TCGA data in
Cytoscape from R using the RCy3 package. Relevant subnetworks will be
identified using different strategies, including network
connectivity.
At the end of this vignette, you will have will be a visualization of
TCGA data on a subnetwork built around highly mutated genes in the
relevant cancer type.
Installation
if(!"RCy3" %in% installed.packages()){
install.packages("BiocManager")
BiocManager::install("RCy3")
}
library(RCy3)
Required Software
The whole point of RCy3 is to connect with Cytoscape. You will need
to install and launch Cytoscape:
For this vignette, you’ll also need the STRING app to
access the STRING database from within Cytoscape:
#available in Cytoscape 3.7.0 and above
installApp('STRINGapp')
Getting Disease Networks
Use Cytoscape to query the STRING database for networks of genes
associated with breast cancer and ovarian cancer.
If the STRING app is not installed, no error is reported, but
your network will be empty
Query STRING database by disease to generate networks
Breast cancer
string.cmd = 'string disease query disease="breast cancer" cutoff=0.9 species="Homo sapiens" limit=150'
commandsRun(string.cmd)
Here we are using Cytoscape’s command line syntax, which can be used
for any core or app automation function, and then making a GET request.
Use commandsHelp to interrogate the functions and parameters
available in your active Cytoscape session, including the apps you’ve
installed!
Ovarian cancer
string.cmd = 'string disease query disease="ovarian cancer" cutoff=0.9 species="Homo sapiens" limit=150'
commandsRun(string.cmd)
Interacting with Cytoscape
Now that we’ve got a couple networks into Cytoscape, let’s see what
we can do with them from R…
Layout network
layoutNetwork(layout.name='circular')
List of layout algorithms available
Layout with parameters!
getLayoutPropertyNames(layout.name='force-directed')
layoutNetwork('force-directed defaultSpringCoefficient=0.0000008 defaultSpringLength=120')
Get table data from network
Now, let’s look at the tablular data associated with our STRING
networks…
getTableColumnNames('node')
One of the great things about the STRING database is all the node and
edge attriubtes they provide. Let’s pull some of it into R to play
with…
Retrieve disease scores
We can retrieve any set of columns from Cytoscape and store them as
an R data frame keyed by SUID. In this case, let’s retrieve the disease
score column from the node table. Those will be our two parameters:
disease.score.table <- getTableColumns('node','stringdb::disease score')
disease.score.table
Plot distribution and pick threshold
Now you can use R like you normally would explore the data.
par(mar=c(1,1,1,1))
plot(factor(row.names(disease.score.table)),disease.score.table[,1], ylab=colnames(disease.score.table)[1])
summary(disease.score.table)
Generate subnetworks
In order to reflect your exploration back onto the network, let’s
generate subnetworks…
…from top quartile of ‘disease score’
top.quart <- quantile(disease.score.table[,1], 0.75)
top.nodes <- row.names(disease.score.table)[which(disease.score.table[,1]>top.quart)]
createSubnetwork(top.nodes,subnetwork.name ='top disease quartile')
#returns a Cytoscape network SUID
…of connected nodes only
createSubnetwork(edges='all',subnetwork.name='top disease quartile connected') #handy way to exclude unconnected nodes!
…from first neighbors of top 3 genes, using the network connectivity
together with the data to direct discovery.
setCurrentNetwork(network="STRING network - ovarian cancer")
top.nodes <- row.names(disease.score.table)[tail(order(disease.score.table[,1]),3)]
selectNodes(nodes=top.nodes)
selectFirstNeighbors()
createSubnetwork('selected', subnetwork.name='top disease neighbors') # selected nodes, all connecting edges (default)
…from diffusion algorithm starting with top 3 genes, using the
network connectivity in a more subtle way than just first-degree
neighbors.
setCurrentNetwork(network="STRING network - ovarian cancer")
selectNodes(nodes=top.nodes)
diffusionBasic() # diffusion!
createSubnetwork('selected',subnetwork.name = 'top disease diffusion')
layoutNetwork('force-directed')
Pro-tip: don’t forget to
setCurrentNetwork() to the correct parent network
before getting table column data and making selections.
Visualizing data on networks
Load datasets
Downloaded TCGA data, preprocessed as R objects. Also available via
each TCGA publication, e.g.:
load(system.file("extdata","tutorial-ovc-expr-mean-dataset.robj", package="RCy3"))
load(system.file("extdata","tutorial-ovc-mut-dataset.robj", package="RCy3"))
load(system.file("extdata","tutorial-brc-expr-mean-dataset.robj", package="RCy3"))
load(system.file("extdata","tutorial-brc-mut-dataset.robj", package="RCy3"))
Breast Cancer Datset
These datasets are similar to the data frames you normally encounter
in R. For diversity, one using row.names to store corresponding gene
names and the other uses the first column. Both are easy to import into
Cytoscape.
str(brc.expr) # gene names in row.names of data.frame
str(brc.mut) # gene names in column named 'Hugo_Symbol'
Let’s return to the Breast Cancer network…
setCurrentNetwork(network="STRING network - breast cancer")
layoutNetwork('force-directed') #uses same settings as previously set
…and use the helper function from RCy3 called
loadTableData
?loadTableData
loadTableData(brc.expr,table.key.column = "display name") #default data.frame key is row.names
loadTableData(brc.mut,'Hugo_Symbol',table.key.column = "display name") #specify column name if not default
Visual styles
Let’s create a new style to visualize our imported data …starting
with the basics, we will specify a few defaults and obvious mappings in
a custom style all our own.
style.name = "dataStyle"
createVisualStyle(style.name)
setVisualStyle(style.name)
setNodeShapeDefault("ellipse", style.name) #remember to specify your style.name!
setNodeSizeDefault(60, style.name)
setNodeColorDefault("#AAAAAA", style.name)
setEdgeLineWidthDefault(2, style.name)
setEdgeOpacityDefault(50, style.name)
setNodeLabelMapping('display name', style.name)
Visualize expression data
Now let’s update the style with a mapping for mean expression using a
standard Color Brewer palette.
setNodeColorMapping('expr.mean', colors=paletteColorBrewerRdBu, style.name=style.name)
Visualize mutation data
OK, now let’s update with a mapping for mutation. Here are all the
same steps, but this time mapping mutation counts to both node
border width and color.
setNodeBorderColorMapping('mut_count', colors = paletteColorBrewerReds, style.name=style.name)
setNodeBorderWidthMapping('mut_count', widths = c(2,8), style.name=style.name) # min and max width values are arbitrarily provided here
This is a useful pair of visual properties to map to a single data
column. See why?
Subnetwork based on diffusion from heavily mutated nodes
Now, let’s pull in what we learned about subnetwork selection and
apply it here…
top.mut <- (brc.mut$Hugo_Symbol)[tail(order(brc.mut$mut_count),2)]
top.mut
selectNodes(nodes=top.mut,'display name')
diffusionBasic()
createSubnetwork('selected',subnetwork.name = 'top mutated diffusion')
layoutNetwork('force-directed defaultSpringCoefficient=0.000008 defaultSpringLength=60')
The top mutated genes are based on TCGA data and the diffusion
algorithm is operating based on the network connectivity from STRING
data, leading to a focused subnetwork view of critical Breast Cancer
genes with mean patient expression data mapped to fill color. Now
that’s data integration!
Pro-tip: You can generate a legend for this in Cytoscape
Style tab > Options > Create style… This is no yet available as a
command.
Ovarian Cancer Datset
But what about the other network and datasets? Do we have to repeat
all of those steps again? Actually, no!
First, let’s switch back over to the Ovarian Cancer network and load
our data.
setCurrentNetwork(network="STRING network - ovarian cancer")
clearSelection()
str(ovc.expr) # gene names in row.names of data.frame
str(ovc.mut) # gene names in column named 'Hugo_Symbol'
loadTableData(ovc.expr, table.key.column = 'display name')
loadTableData(ovc.mut,'Hugo_Symbol', table.key.column = 'display name')
Because we used the same column names in our original data
frames, now we can simply apply the same visual style created
above!
setVisualStyle(style.name=style.name)
Reusing the same style for both breast and ovarian cancers, we can
compare the relative expression and mutation counts across the two
datasets. For example, notice in the case of ovarian cancer:
decreased range of mean expression and
fewer mega-mutated genes.
Saving, sharing and publishing
Saving a Cytoscape session file
Session files save everything. As with most project
software, we recommend saving often!
saveSession('tutorial_session') #.cys
Note: If you don’t specify a complete path, the
files will be saved relative to your current working directory in R.
Saving high resolution image files
You can export extremely high resolution images, including vector
graphic formats.
exportImage(filename='tutorial_image2', type = 'PDF') #.pdf
?exportImage
LS0tCnRpdGxlOiAiQ2FuY2VyIG5ldHdvcmtzIGFuZCBkYXRhIgphdXRob3I6ICJieSBBbGV4YW5kZXIgUGljbyIKcGFja2FnZTogUkN5MwpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiAibm9uZSIKIyAgcGRmX2RvY3VtZW50OgojICAgIHRvYzogdHJ1ZSAgIAp2aWduZXR0ZTogPgogICAgJVxWaWduZXR0ZUluZGV4RW50cnl7MDYuIENhbmNlciBuZXR3b3JrcyBhbmQgZGF0YSB+NDAgbWlufQogICAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KYGBge3IsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGV2YWw9RkFMU0UKKQpgYGAKKlRoZSBSIG1hcmtkb3duIGlzIGF2YWlsYWJsZSBmcm9tIHRoZSBwdWxsZG93biBtZW51IGZvciogQ29kZSAqYXQgdGhlIHVwcGVyLXJpZ2h0LCBjaG9vc2UgIkRvd25sb2FkIFJtZCIsIG9yIFtkb3dubG9hZCB0aGUgUm1kIGZyb20gR2l0SHViXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vY3l0b3NjYXBlL2N5dG9zY2FwZS1hdXRvbWF0aW9uL21hc3Rlci9mb3Itc2NyaXB0ZXJzL1Ivbm90ZWJvb2tzL0NhbmNlci1uZXR3b3Jrcy1hbmQtZGF0YS5SbWQpLioKCjxociAvPgpUaGlzIHZpZ25ldHRlIHdpbGwgZGVtb25zdHJhdGUgbmV0d29yayByZXRyaWV2YWwgZnJvbSB0aGUgU1RSSU5HIGRhdGFiYXNlLCBiYXNpYyBhbmFseXNpcywgbG9hZGluZyBhbmQgdmlzdWFsaXphdGlvbiBUQ0dBIGRhdGEgaW4gQ3l0b3NjYXBlIGZyb20gUiB1c2luZyB0aGUgUkN5MyBwYWNrYWdlLiBSZWxldmFudCBzdWJuZXR3b3JrcyB3aWxsIGJlIGlkZW50aWZpZWQgdXNpbmcgZGlmZmVyZW50IHN0cmF0ZWdpZXMsIGluY2x1ZGluZyBuZXR3b3JrIGNvbm5lY3Rpdml0eS4KCkF0IHRoZSBlbmQgb2YgdGhpcyB2aWduZXR0ZSwgeW91IHdpbGwgaGF2ZSB3aWxsIGJlIGEgdmlzdWFsaXphdGlvbiBvZiBUQ0dBIGRhdGEgb24gYSBzdWJuZXR3b3JrIGJ1aWx0IGFyb3VuZCBoaWdobHkgbXV0YXRlZCBnZW5lcyBpbiB0aGUgcmVsZXZhbnQgY2FuY2VyIHR5cGUuCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvZGF0YS9pbWcvYnJjNC5wbmcpCjwvY2VudGVyPgoKPGhyIC8+CgojIEluc3RhbGxhdGlvbgpgYGB7cn0KaWYoISJSQ3kzIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKXsKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKfQpsaWJyYXJ5KFJDeTMpCmBgYAoKIyBSZXF1aXJlZCBTb2Z0d2FyZQpUaGUgd2hvbGUgcG9pbnQgb2YgUkN5MyBpcyB0byBjb25uZWN0IHdpdGggQ3l0b3NjYXBlLiBZb3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgYW5kIGxhdW5jaCBDeXRvc2NhcGU6IAogICAgCiogRG93bmxvYWQgdGhlIGxhdGVzdCBDeXRvc2NhcGUgZnJvbSBodHRwOi8vd3d3LmN5dG9zY2FwZS5vcmcvZG93bmxvYWQucGhwCiogQ29tcGxldGUgaW5zdGFsbGF0aW9uIHdpemFyZAoqIExhdW5jaCBDeXRvc2NhcGUgCgpgYGB7cn0KY3l0b3NjYXBlUGluZygpCmBgYAoKRm9yIHRoaXMgdmlnbmV0dGUsIHlvdSdsbCBhbHNvIG5lZWQgdGhlIFtTVFJJTkcgYXBwXShodHRwczovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL3N0cmluZ2FwcCkgdG8gYWNjZXNzIHRoZSBTVFJJTkcgZGF0YWJhc2UgZnJvbSB3aXRoaW4gQ3l0b3NjYXBlOgoKYGBge3J9CiNhdmFpbGFibGUgaW4gQ3l0b3NjYXBlIDMuNy4wIGFuZCBhYm92ZQppbnN0YWxsQXBwKCdTVFJJTkdhcHAnKSAgCmBgYAoKIyBHZXR0aW5nIERpc2Vhc2UgTmV0d29ya3MKClVzZSBDeXRvc2NhcGUgdG8gcXVlcnkgdGhlIFNUUklORyBkYXRhYmFzZSBmb3IgbmV0d29ya3Mgb2YgZ2VuZXMgYXNzb2NpYXRlZCB3aXRoIGJyZWFzdCBjYW5jZXIgYW5kIG92YXJpYW4gY2FuY2VyLgoKKipJZiB0aGUgU1RSSU5HIGFwcCBpcyBub3QgaW5zdGFsbGVkLCBubyBlcnJvciBpcyByZXBvcnRlZCwgYnV0IHlvdXIgbmV0d29yayAgd2lsbCBiZSBlbXB0eSoqCgojIyBRdWVyeSBTVFJJTkcgZGF0YWJhc2UgYnkgZGlzZWFzZSB0byBnZW5lcmF0ZSBuZXR3b3JrcwojIyMgQnJlYXN0IGNhbmNlcgpgYGB7cn0Kc3RyaW5nLmNtZCA9ICdzdHJpbmcgZGlzZWFzZSBxdWVyeSBkaXNlYXNlPSJicmVhc3QgY2FuY2VyIiBjdXRvZmY9MC45IHNwZWNpZXM9IkhvbW8gc2FwaWVucyIgbGltaXQ9MTUwJwpjb21tYW5kc1J1bihzdHJpbmcuY21kKQpgYGAKSGVyZSB3ZSBhcmUgdXNpbmcgQ3l0b3NjYXBlJ3MgY29tbWFuZCBsaW5lIHN5bnRheCwgd2hpY2ggY2FuIGJlIHVzZWQgZm9yIGFueSBjb3JlIG9yIGFwcAphdXRvbWF0aW9uIGZ1bmN0aW9uLCBhbmQgdGhlbiBtYWtpbmcgYSBHRVQgcmVxdWVzdC4gVXNlICpjb21tYW5kc0hlbHAqIHRvIGludGVycm9nYXRlIAp0aGUgZnVuY3Rpb25zIGFuZCBwYXJhbWV0ZXJzIGF2YWlsYWJsZSBpbiB5b3VyIGFjdGl2ZSBDeXRvc2NhcGUgc2Vzc2lvbiwgaW5jbHVkaW5nIHRoZSAKYXBwcyB5b3UndmUgaW5zdGFsbGVkIQoKIVtdKC4vZGF0YS9pbWcvYnJjLnBuZyl7aGVpZ2h0PTEwMCV9ICAKCiMjIyBPdmFyaWFuIGNhbmNlcgpgYGB7cn0Kc3RyaW5nLmNtZCA9ICdzdHJpbmcgZGlzZWFzZSBxdWVyeSBkaXNlYXNlPSJvdmFyaWFuIGNhbmNlciIgY3V0b2ZmPTAuOSBzcGVjaWVzPSJIb21vIHNhcGllbnMiIGxpbWl0PTE1MCcKY29tbWFuZHNSdW4oc3RyaW5nLmNtZCkKYGBgCgohW10oLi9kYXRhL2ltZy9vdmMucG5nKXtoZWlnaHQ9MTAwJX0KCiMgSW50ZXJhY3Rpbmcgd2l0aCBDeXRvc2NhcGUgCgpOb3cgdGhhdCB3ZSd2ZSBnb3QgYSBjb3VwbGUgbmV0d29ya3MgaW50byBDeXRvc2NhcGUsIGxldCdzIHNlZSB3aGF0IHdlIGNhbiBkbyB3aXRoIHRoZW0gZnJvbSBSLi4uCgojIyBHZXQgbGlzdCBvZiBuZXR3b3JrcyAKYGBge3J9CmdldE5ldHdvcmtMaXN0KCkKYGBgCgojIyBMYXlvdXQgbmV0d29yawpgYGB7cn0KbGF5b3V0TmV0d29yayhsYXlvdXQubmFtZT0nY2lyY3VsYXInKSAKYGBgCgohW10oLi9kYXRhL2ltZy9vdmMyLnBuZyl7aGVpZ2h0PTEwMCV9IAoKIyMjIExpc3Qgb2YgbGF5b3V0IGFsZ29yaXRobXMgYXZhaWxhYmxlCmBgYHtyfQpnZXRMYXlvdXROYW1lcygpCmBgYAoKIyMjIExheW91dCB3aXRoIHBhcmFtZXRlcnMhCmBgYHtyfQpnZXRMYXlvdXRQcm9wZXJ0eU5hbWVzKGxheW91dC5uYW1lPSdmb3JjZS1kaXJlY3RlZCcpCmxheW91dE5ldHdvcmsoJ2ZvcmNlLWRpcmVjdGVkIGRlZmF1bHRTcHJpbmdDb2VmZmljaWVudD0wLjAwMDAwMDggZGVmYXVsdFNwcmluZ0xlbmd0aD0xMjAnKQpgYGAKCiMjIEdldCB0YWJsZSBkYXRhIGZyb20gbmV0d29yawpOb3csIGxldCdzIGxvb2sgYXQgdGhlIHRhYmx1bGFyIGRhdGEgYXNzb2NpYXRlZCB3aXRoIG91ciBTVFJJTkcgbmV0d29ya3MuLi4KYGBge3J9CmdldFRhYmxlQ29sdW1uTmFtZXMoJ25vZGUnKQpgYGAKCk9uZSBvZiB0aGUgZ3JlYXQgdGhpbmdzIGFib3V0IHRoZSBTVFJJTkcgZGF0YWJhc2UgaXMgYWxsIHRoZSBub2RlIGFuZCBlZGdlIGF0dHJpdWJ0ZXMgdGhleSBwcm92aWRlLiBMZXQncyBwdWxsIHNvbWUgb2YgaXQgaW50byBSIHRvIHBsYXkgd2l0aC4uLgoKIyMjIFJldHJpZXZlIGRpc2Vhc2Ugc2NvcmVzIApXZSBjYW4gcmV0cmlldmUgYW55IHNldCBvZiBjb2x1bW5zIGZyb20gQ3l0b3NjYXBlIGFuZCBzdG9yZSB0aGVtIGFzIGFuIFIgZGF0YSBmcmFtZSBrZXllZCBieSBTVUlELiBJbiB0aGlzIGNhc2UsIGxldCdzIHJldHJpZXZlIHRoZSBkaXNlYXNlIHNjb3JlIGNvbHVtbiBmcm9tIHRoZSBub2RlIHRhYmxlLiBUaG9zZSB3aWxsIGJlIG91ciB0d28gcGFyYW1ldGVyczoKYGBge3J9CmRpc2Vhc2Uuc2NvcmUudGFibGUgPC0gZ2V0VGFibGVDb2x1bW5zKCdub2RlJywnc3RyaW5nZGI6OmRpc2Vhc2Ugc2NvcmUnKQpkaXNlYXNlLnNjb3JlLnRhYmxlCmBgYAoKIyMjIFBsb3QgZGlzdHJpYnV0aW9uIGFuZCBwaWNrIHRocmVzaG9sZApOb3cgeW91IGNhbiB1c2UgUiBsaWtlIHlvdSBub3JtYWxseSB3b3VsZCBleHBsb3JlIHRoZSBkYXRhLgpgYGB7cn0KcGFyKG1hcj1jKDEsMSwxLDEpKQpwbG90KGZhY3Rvcihyb3cubmFtZXMoZGlzZWFzZS5zY29yZS50YWJsZSkpLGRpc2Vhc2Uuc2NvcmUudGFibGVbLDFdLCB5bGFiPWNvbG5hbWVzKGRpc2Vhc2Uuc2NvcmUudGFibGUpWzFdKQpzdW1tYXJ5KGRpc2Vhc2Uuc2NvcmUudGFibGUpCmBgYAoKIyMgR2VuZXJhdGUgc3VibmV0d29ya3MKSW4gb3JkZXIgdG8gcmVmbGVjdCB5b3VyIGV4cGxvcmF0aW9uIGJhY2sgb250byB0aGUgbmV0d29yaywgbGV0J3MgZ2VuZXJhdGUgc3VibmV0d29ya3MuLi4KCi4uLmZyb20gdG9wIHF1YXJ0aWxlIG9mICdkaXNlYXNlIHNjb3JlJwpgYGB7cn0KdG9wLnF1YXJ0IDwtIHF1YW50aWxlKGRpc2Vhc2Uuc2NvcmUudGFibGVbLDFdLCAwLjc1KQp0b3Aubm9kZXMgPC0gcm93Lm5hbWVzKGRpc2Vhc2Uuc2NvcmUudGFibGUpW3doaWNoKGRpc2Vhc2Uuc2NvcmUudGFibGVbLDFdPnRvcC5xdWFydCldCmNyZWF0ZVN1Ym5ldHdvcmsodG9wLm5vZGVzLHN1Ym5ldHdvcmsubmFtZSA9J3RvcCBkaXNlYXNlIHF1YXJ0aWxlJykKI3JldHVybnMgYSBDeXRvc2NhcGUgbmV0d29yayBTVUlECmBgYAoKLi4ub2YgY29ubmVjdGVkIG5vZGVzIG9ubHkKYGBge3J9CmNyZWF0ZVN1Ym5ldHdvcmsoZWRnZXM9J2FsbCcsc3VibmV0d29yay5uYW1lPSd0b3AgZGlzZWFzZSBxdWFydGlsZSBjb25uZWN0ZWQnKSAgI2hhbmR5IHdheSB0byBleGNsdWRlIHVuY29ubmVjdGVkIG5vZGVzIQpgYGAKCi4uLmZyb20gZmlyc3QgbmVpZ2hib3JzIG9mIHRvcCAzIGdlbmVzLCB1c2luZyB0aGUgbmV0d29yayBjb25uZWN0aXZpdHkgdG9nZXRoZXIgd2l0aCB0aGUgZGF0YSB0byBkaXJlY3QgZGlzY292ZXJ5LgpgYGB7cn0Kc2V0Q3VycmVudE5ldHdvcmsobmV0d29yaz0iU1RSSU5HIG5ldHdvcmsgLSBvdmFyaWFuIGNhbmNlciIpCnRvcC5ub2RlcyA8LSByb3cubmFtZXMoZGlzZWFzZS5zY29yZS50YWJsZSlbdGFpbChvcmRlcihkaXNlYXNlLnNjb3JlLnRhYmxlWywxXSksMyldCnNlbGVjdE5vZGVzKG5vZGVzPXRvcC5ub2RlcykKc2VsZWN0Rmlyc3ROZWlnaGJvcnMoKQpjcmVhdGVTdWJuZXR3b3JrKCdzZWxlY3RlZCcsIHN1Ym5ldHdvcmsubmFtZT0ndG9wIGRpc2Vhc2UgbmVpZ2hib3JzJykgIyBzZWxlY3RlZCBub2RlcywgYWxsIGNvbm5lY3RpbmcgZWRnZXMgKGRlZmF1bHQpCmBgYAoKLi4uZnJvbSBkaWZmdXNpb24gYWxnb3JpdGhtIHN0YXJ0aW5nIHdpdGggdG9wIDMgZ2VuZXMsIHVzaW5nIHRoZSBuZXR3b3JrIGNvbm5lY3Rpdml0eSBpbiBhIG1vcmUgc3VidGxlIHdheSB0aGFuIGp1c3QgZmlyc3QtZGVncmVlIG5laWdoYm9ycy4KYGBge3J9CnNldEN1cnJlbnROZXR3b3JrKG5ldHdvcms9IlNUUklORyBuZXR3b3JrIC0gb3ZhcmlhbiBjYW5jZXIiKQpzZWxlY3ROb2Rlcyhub2Rlcz10b3Aubm9kZXMpCmRpZmZ1c2lvbkJhc2ljKCkgIyBkaWZmdXNpb24hCmNyZWF0ZVN1Ym5ldHdvcmsoJ3NlbGVjdGVkJyxzdWJuZXR3b3JrLm5hbWUgPSAndG9wIGRpc2Vhc2UgZGlmZnVzaW9uJykKbGF5b3V0TmV0d29yaygnZm9yY2UtZGlyZWN0ZWQnKQpgYGAKCiFbXSguL2RhdGEvaW1nL292YzQucG5nKXtoZWlnaHQ9MTAwJX0gCgoqUHJvLXRpcCo6IGRvbid0IGZvcmdldCB0byAqKnNldEN1cnJlbnROZXR3b3JrKCkqKiB0byB0aGUgY29ycmVjdCBwYXJlbnQgbmV0d29yayBiZWZvcmUgZ2V0dGluZyB0YWJsZSBjb2x1bW4gZGF0YSBhbmQgbWFraW5nIHNlbGVjdGlvbnMuCgojIFZpc3VhbGl6aW5nIGRhdGEgb24gbmV0d29ya3MKCiMjIExvYWQgZGF0YXNldHMKRG93bmxvYWRlZCBUQ0dBIGRhdGEsIHByZXByb2Nlc3NlZCBhcyBSIG9iamVjdHMuIEFsc28gYXZhaWxhYmxlIHZpYSBlYWNoIFRDR0EgcHVibGljYXRpb24sIGUuZy46CiAKKiBCcmVhc3Q6IGh0dHBzOi8vdGNnYS1kYXRhLm5jaS5uaWguZ292L2RvY3MvcHVibGljYXRpb25zL2JyY2FfMjAxMi8KKiBPdmFyaWFuOiBodHRwczovL3RjZ2EtZGF0YS5uY2kubmloLmdvdi9kb2NzL3B1YmxpY2F0aW9ucy9vdl8yMDExLwogIApgYGB7cn0KbG9hZChzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsInR1dG9yaWFsLW92Yy1leHByLW1lYW4tZGF0YXNldC5yb2JqIiwgcGFja2FnZT0iUkN5MyIpKQpsb2FkKHN5c3RlbS5maWxlKCJleHRkYXRhIiwidHV0b3JpYWwtb3ZjLW11dC1kYXRhc2V0LnJvYmoiLCBwYWNrYWdlPSJSQ3kzIikpCmxvYWQoc3lzdGVtLmZpbGUoImV4dGRhdGEiLCJ0dXRvcmlhbC1icmMtZXhwci1tZWFuLWRhdGFzZXQucm9iaiIsIHBhY2thZ2U9IlJDeTMiKSkKbG9hZChzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsInR1dG9yaWFsLWJyYy1tdXQtZGF0YXNldC5yb2JqIiwgcGFja2FnZT0iUkN5MyIpKQpgYGAKCgojIyBCcmVhc3QgQ2FuY2VyIERhdHNldApUaGVzZSBkYXRhc2V0cyBhcmUgc2ltaWxhciB0byB0aGUgZGF0YSBmcmFtZXMgeW91IG5vcm1hbGx5IGVuY291bnRlciBpbiBSLiBGb3IgZGl2ZXJzaXR5LCBvbmUgdXNpbmcgcm93Lm5hbWVzIHRvIHN0b3JlIGNvcnJlc3BvbmRpbmcgZ2VuZSBuYW1lcyBhbmQgdGhlIG90aGVyIHVzZXMgdGhlIGZpcnN0IGNvbHVtbi4gQm90aCBhcmUgZWFzeSB0byBpbXBvcnQgaW50byBDeXRvc2NhcGUuCmBgYHtyfQpzdHIoYnJjLmV4cHIpICAjIGdlbmUgbmFtZXMgaW4gcm93Lm5hbWVzIG9mIGRhdGEuZnJhbWUKc3RyKGJyYy5tdXQpICAjIGdlbmUgbmFtZXMgaW4gY29sdW1uIG5hbWVkICdIdWdvX1N5bWJvbCcKYGBgCgpMZXQncyByZXR1cm4gdG8gdGhlIEJyZWFzdCBDYW5jZXIgbmV0d29yay4uLgpgYGB7cn0Kc2V0Q3VycmVudE5ldHdvcmsobmV0d29yaz0iU1RSSU5HIG5ldHdvcmsgLSBicmVhc3QgY2FuY2VyIikKbGF5b3V0TmV0d29yaygnZm9yY2UtZGlyZWN0ZWQnKSAjdXNlcyBzYW1lIHNldHRpbmdzIGFzIHByZXZpb3VzbHkgc2V0CmBgYAoKLi4uYW5kIHVzZSB0aGUgaGVscGVyIGZ1bmN0aW9uIGZyb20gUkN5MyBjYWxsZWQgKmxvYWRUYWJsZURhdGEqCmBgYHtyfQo/bG9hZFRhYmxlRGF0YQpsb2FkVGFibGVEYXRhKGJyYy5leHByLHRhYmxlLmtleS5jb2x1bW4gPSAiZGlzcGxheSBuYW1lIikgICNkZWZhdWx0IGRhdGEuZnJhbWUga2V5IGlzIHJvdy5uYW1lcwpsb2FkVGFibGVEYXRhKGJyYy5tdXQsJ0h1Z29fU3ltYm9sJyx0YWJsZS5rZXkuY29sdW1uID0gImRpc3BsYXkgbmFtZSIpICAjc3BlY2lmeSBjb2x1bW4gbmFtZSBpZiBub3QgZGVmYXVsdApgYGAKCiMjIyBWaXN1YWwgc3R5bGVzCkxldCdzIGNyZWF0ZSBhIG5ldyBzdHlsZSB0byB2aXN1YWxpemUgb3VyIGltcG9ydGVkIGRhdGEKLi4uc3RhcnRpbmcgd2l0aCB0aGUgYmFzaWNzLCB3ZSB3aWxsIHNwZWNpZnkgYSBmZXcgZGVmYXVsdHMgYW5kIG9idmlvdXMgbWFwcGluZ3MgaW4gYSBjdXN0b20gc3R5bGUgYWxsIG91ciBvd24uCmBgYHtyfQpzdHlsZS5uYW1lID0gImRhdGFTdHlsZSIKY3JlYXRlVmlzdWFsU3R5bGUoc3R5bGUubmFtZSkKc2V0VmlzdWFsU3R5bGUoc3R5bGUubmFtZSkKCnNldE5vZGVTaGFwZURlZmF1bHQoImVsbGlwc2UiLCBzdHlsZS5uYW1lKSAjcmVtZW1iZXIgdG8gc3BlY2lmeSB5b3VyIHN0eWxlLm5hbWUhCnNldE5vZGVTaXplRGVmYXVsdCg2MCwgc3R5bGUubmFtZSkKc2V0Tm9kZUNvbG9yRGVmYXVsdCgiI0FBQUFBQSIsIHN0eWxlLm5hbWUpCnNldEVkZ2VMaW5lV2lkdGhEZWZhdWx0KDIsIHN0eWxlLm5hbWUpCnNldEVkZ2VPcGFjaXR5RGVmYXVsdCg1MCwgc3R5bGUubmFtZSkKc2V0Tm9kZUxhYmVsTWFwcGluZygnZGlzcGxheSBuYW1lJywgc3R5bGUubmFtZSkKYGBgCgojIyMjIFZpc3VhbGl6ZSBleHByZXNzaW9uIGRhdGEKTm93IGxldCdzIHVwZGF0ZSB0aGUgc3R5bGUgd2l0aCBhIG1hcHBpbmcgZm9yIG1lYW4gZXhwcmVzc2lvbiB1c2luZyBhIHN0YW5kYXJkIENvbG9yIEJyZXdlciBwYWxldHRlLgoKYGBge3J9CnNldE5vZGVDb2xvck1hcHBpbmcoJ2V4cHIubWVhbicsIGNvbG9ycz1wYWxldHRlQ29sb3JCcmV3ZXJSZEJ1LCBzdHlsZS5uYW1lPXN0eWxlLm5hbWUpCmBgYAo8Y2VudGVyPgohW10oLi9kYXRhL2ltZy9icmMyLnBuZyl7aGVpZ2h0PTEwMCV9IAo8L2NlbnRlcj4KCgojIyMgVmlzdWFsaXplIG11dGF0aW9uIGRhdGEKT0ssIG5vdyBsZXQncyB1cGRhdGUgd2l0aCBhIG1hcHBpbmcgZm9yIG11dGF0aW9uLiBIZXJlIGFyZSBhbGwgdGhlIHNhbWUgc3RlcHMsIGJ1dCB0aGlzIHRpbWUgbWFwcGluZyBtdXRhdGlvbiBjb3VudHMgdG8gKmJvdGgqIG5vZGUgYm9yZGVyIHdpZHRoIGFuZCBjb2xvci4gCmBgYHtyfQpzZXROb2RlQm9yZGVyQ29sb3JNYXBwaW5nKCdtdXRfY291bnQnLCBjb2xvcnMgPSBwYWxldHRlQ29sb3JCcmV3ZXJSZWRzLCBzdHlsZS5uYW1lPXN0eWxlLm5hbWUpCnNldE5vZGVCb3JkZXJXaWR0aE1hcHBpbmcoJ211dF9jb3VudCcsIHdpZHRocyA9IGMoMiw4KSwgc3R5bGUubmFtZT1zdHlsZS5uYW1lKSAjIG1pbiBhbmQgbWF4IHdpZHRoIHZhbHVlcyBhcmUgYXJiaXRyYXJpbHkgcHJvdmlkZWQgaGVyZQpgYGAKVGhpcyBpcyBhIHVzZWZ1bCBwYWlyIG9mIHZpc3VhbCBwcm9wZXJ0aWVzIHRvIG1hcCB0byBhIHNpbmdsZSBkYXRhIGNvbHVtbi4gU2VlIHdoeT8KCiFbXSguL2RhdGEvaW1nL2JyYzMucG5nKXtoZWlnaHQ9MTAwJX0gCgojIyMgU3VibmV0d29yayBiYXNlZCBvbiBkaWZmdXNpb24gZnJvbSBoZWF2aWx5IG11dGF0ZWQgbm9kZXMKTm93LCBsZXQncyBwdWxsIGluIHdoYXQgd2UgbGVhcm5lZCBhYm91dCBzdWJuZXR3b3JrIHNlbGVjdGlvbiBhbmQgYXBwbHkgaXQgaGVyZS4uLgpgYGB7cn0KdG9wLm11dCA8LSAoYnJjLm11dCRIdWdvX1N5bWJvbClbdGFpbChvcmRlcihicmMubXV0JG11dF9jb3VudCksMildCnRvcC5tdXQKc2VsZWN0Tm9kZXMobm9kZXM9dG9wLm11dCwnZGlzcGxheSBuYW1lJykKZGlmZnVzaW9uQmFzaWMoKSAKY3JlYXRlU3VibmV0d29yaygnc2VsZWN0ZWQnLHN1Ym5ldHdvcmsubmFtZSA9ICd0b3AgbXV0YXRlZCBkaWZmdXNpb24nKQpsYXlvdXROZXR3b3JrKCdmb3JjZS1kaXJlY3RlZCBkZWZhdWx0U3ByaW5nQ29lZmZpY2llbnQ9MC4wMDAwMDggZGVmYXVsdFNwcmluZ0xlbmd0aD02MCcpCmBgYAoKVGhlIHRvcCBtdXRhdGVkIGdlbmVzIGFyZSBiYXNlZCBvbiBUQ0dBIGRhdGEgYW5kIHRoZSBkaWZmdXNpb24gYWxnb3JpdGhtIGlzIG9wZXJhdGluZyBiYXNlZCBvbiB0aGUgbmV0d29yayBjb25uZWN0aXZpdHkgZnJvbSBTVFJJTkcgZGF0YSwgbGVhZGluZyB0byBhIGZvY3VzZWQgc3VibmV0d29yayB2aWV3IG9mIGNyaXRpY2FsIEJyZWFzdCBDYW5jZXIgZ2VuZXMgd2l0aCBtZWFuIHBhdGllbnQgZXhwcmVzc2lvbiBkYXRhIG1hcHBlZCB0byBmaWxsIGNvbG9yLiBOb3cgKnRoYXQncyogZGF0YSBpbnRlZ3JhdGlvbiEKCiFbXSguL2RhdGEvaW1nL2JyYzQucG5nKXtoZWlnaHQ9MTAwJX0gCgoqKlByby10aXA6IFlvdSBjYW4gZ2VuZXJhdGUgYSBsZWdlbmQgZm9yIHRoaXMgaW4gQ3l0b3NjYXBlIFN0eWxlIHRhYiA+IE9wdGlvbnMgPiBDcmVhdGUgc3R5bGUuLi4gIFRoaXMgaXMgbm8geWV0IGF2YWlsYWJsZSBhcyBhIGNvbW1hbmQuKioKCiMjIE92YXJpYW4gQ2FuY2VyIERhdHNldApCdXQgd2hhdCBhYm91dCB0aGUgb3RoZXIgbmV0d29yayBhbmQgZGF0YXNldHM/IERvIHdlIGhhdmUgdG8gcmVwZWF0ICphbGwqIG9mIHRob3NlIHN0ZXBzIGFnYWluPyAgQWN0dWFsbHksIG5vIQoKRmlyc3QsIGxldCdzIHN3aXRjaCBiYWNrIG92ZXIgdG8gdGhlIE92YXJpYW4gQ2FuY2VyIG5ldHdvcmsgYW5kIGxvYWQgb3VyIGRhdGEuCmBgYHtyfQpzZXRDdXJyZW50TmV0d29yayhuZXR3b3JrPSJTVFJJTkcgbmV0d29yayAtIG92YXJpYW4gY2FuY2VyIikKY2xlYXJTZWxlY3Rpb24oKQpzdHIob3ZjLmV4cHIpICAjIGdlbmUgbmFtZXMgaW4gcm93Lm5hbWVzIG9mIGRhdGEuZnJhbWUKc3RyKG92Yy5tdXQpICAjIGdlbmUgbmFtZXMgaW4gY29sdW1uIG5hbWVkICdIdWdvX1N5bWJvbCcKCmxvYWRUYWJsZURhdGEob3ZjLmV4cHIsIHRhYmxlLmtleS5jb2x1bW4gPSAnZGlzcGxheSBuYW1lJykKbG9hZFRhYmxlRGF0YShvdmMubXV0LCdIdWdvX1N5bWJvbCcsIHRhYmxlLmtleS5jb2x1bW4gPSAnZGlzcGxheSBuYW1lJykKYGBgCgoqKkJlY2F1c2Ugd2UgdXNlZCB0aGUgc2FtZSBjb2x1bW4gbmFtZXMgaW4gb3VyIG9yaWdpbmFsIGRhdGEgZnJhbWVzLCBub3cgd2UgY2FuIHNpbXBseSBhcHBseSB0aGUgKnNhbWUqIHZpc3VhbCBzdHlsZSBjcmVhdGVkIGFib3ZlISoqCmBgYHtyfQpzZXRWaXN1YWxTdHlsZShzdHlsZS5uYW1lPXN0eWxlLm5hbWUpCmBgYAoKIVtdKC4vZGF0YS9pbWcvb3ZjMy5wbmcpe2hlaWdodD0xMDAlfSAgCgpSZXVzaW5nIHRoZSBzYW1lIHN0eWxlIGZvciBib3RoIGJyZWFzdCBhbmQgb3ZhcmlhbiBjYW5jZXJzLCB3ZSBjYW4gY29tcGFyZSB0aGUgcmVsYXRpdmUgZXhwcmVzc2lvbiBhbmQgbXV0YXRpb24gY291bnRzIGFjcm9zcyB0aGUgdHdvIGRhdGFzZXRzLiAKRm9yIGV4YW1wbGUsIG5vdGljZSBpbiB0aGUgY2FzZSBvZiBvdmFyaWFuIGNhbmNlcjogKipkZWNyZWFzZWQqKiByYW5nZSBvZiBtZWFuIGV4cHJlc3Npb24gYW5kICoqZmV3ZXIqKiBtZWdhLW11dGF0ZWQgZ2VuZXMuCgojIFNhdmluZywgc2hhcmluZyBhbmQgcHVibGlzaGluZwoKIyMgU2F2aW5nIGEgQ3l0b3NjYXBlIHNlc3Npb24gZmlsZQpTZXNzaW9uIGZpbGVzIHNhdmUgKmV2ZXJ5dGhpbmcqLiBBcyB3aXRoIG1vc3QgcHJvamVjdCBzb2Z0d2FyZSwgd2UgcmVjb21tZW5kIHNhdmluZyBvZnRlbiEKYGBge3J9CnNhdmVTZXNzaW9uKCd0dXRvcmlhbF9zZXNzaW9uJykgIy5jeXMKYGBgCioqTm90ZToqKiBJZiB5b3UgZG9uJ3Qgc3BlY2lmeSBhIGNvbXBsZXRlIHBhdGgsIHRoZSBmaWxlcyB3aWxsIGJlIHNhdmVkIHJlbGF0aXZlIHRvIHlvdXIgY3VycmVudCB3b3JraW5nIGRpcmVjdG9yeSBpbiBSLiAKCiMjIFNhdmluZyBoaWdoIHJlc29sdXRpb24gaW1hZ2UgZmlsZXMKWW91IGNhbiBleHBvcnQgZXh0cmVtZWx5IGhpZ2ggcmVzb2x1dGlvbiBpbWFnZXMsIGluY2x1ZGluZyB2ZWN0b3IgZ3JhcGhpYyBmb3JtYXRzLgpgYGB7cn0KZXhwb3J0SW1hZ2UoZmlsZW5hbWU9J3R1dG9yaWFsX2ltYWdlMicsIHR5cGUgPSAnUERGJykgIy5wZGYKP2V4cG9ydEltYWdlCmBgYAo=