Operation
In order to work with RCy3 you must have Cytoscape v3.7 or later
installed and running. Cytoscape can be installed from cytoscape.org. The RCy3 package can be
installed from Bioconductor:
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install("RCy3")
library(RCy3)
Launch Cytoscape and keep it running whenever using RCy3. Confirm
that you have everything installed and that RCy3 is communicating with
Cytoscape via CyREST:
cytoscapePing ()
#[1] "You are connected to Cytoscape!"
As with any R package, one can access the documentation and browse
over a dozen vignettes included in the RCy3 package:
help(package=RCy3)
browseVignettes("RCy3")
Use Cases
The following sections demonstrate a variety of common and advanced
network biology use cases as runnable R code snippets. The first set
focuses on fundamental Cytoscape operations that are common to most use
cases:
- Loading networks (from R objects, Cytoscape files and public
databases)
- Visualizing network data
- Filtering by node degree or data
- Saving and exporting networks
Additionally, there are examples that demonstrate analytical
workflows, relying not only on Cytoscape, but also on Cytoscape apps and
other R packages:
- Building maps of enrichment analysis results using EnrichmentMap and
AutoAnnotate
- Visualizing integrated network analysis using BioNet
- Performing advanced graph analytics using RBGL
Loading Networks
Networks come in all shapes and sizes in multiple formats from
multiple sources. Here are just a few of the myriad ways to load
networks into Cytoscape using RCy3.
From R Objects. . .
# From graph objects (graphNEL)
g <- makeSimpleGraph()
createNetworkFromGraph(g)
## And round-trip back from Cytoscape to graph
g2 <- createGraphFromNetwork()
# From igraph objects
library(igraph)
ig <- make_graph("Zachary")
createNetworkFromIgraph(ig)
## And round-trip back from Cytoscape to igraph
ig2 <- createIgraphFromNetwork()
## Note that the Cytoscape model infers directionality
# From dataframes
nodes <- data.frame(id=c("node 0","node 1","node 2","node 3"),
group=c("A","A","B","B"), #categorical strings
score=as.integer(c(20,10,15,5)), #integers
stringsAsFactors=FALSE)
edges <- data.frame(source=c("node 0","node 0","node 0","node 2"),
target=c("node 1","node 2","node 3","node 3"),
interaction=c("inhibits","interacts",
"activates","interacts"), #optional
weight=c(5.1,3.0,5.2,9.9), #numerics
stringsAsFactors=FALSE)
createNetworkFromDataFrames(nodes, edges)
From Cytoscape-supported File Formats. . .
# From Cytoscape session files
## Will erase and replace all data from current session!
openSession() # default file = galFiltered.cys
# From local network files
importNetworkFromFile() # default file = galFiltered.sif
## Supported file formats: SIF, GML, xGMML, graphML, CX, plus
# From NDEx, the network database
importNetworkFromNDEx("5be85817-1e5f-11e8-b939-0ac135e8bacf")
## Account information or accessKey are required arguments only
## when accessing private content
From Public Databases via Cytoscape Apps. . .
# From STRING, starting with a list of genes/proteins
installApp("stringApp")
gene.list <- c("T53","AKT1","CDKN1A")
gene.str <- paste(gene.list, collapse = ",")
string.cmd <- paste("string protein query cutoff=0.99 limit=40 query",
gene.str, sep = "=")
commandsRun(string.cmd)
# From WikiPathways, starting with a keyword
library(rWikiPathways) # install from Bioconductor
installApp("WikiPathways")
keyword <- "glioblastoma"
gbm.pathways <- findPathwaysByText(keyword)
gbm.wpid <- gbm.pathways[[1]]$id # let’s just take the first one
wikipathways.cmd <- paste("wikipathways import-as-pathway id",
gbm.wpid, sep = "=")
commandsRun(wikipathways.cmd)
Visualizing Data on Networks
Cytoscape excels at generating publication-quality network
visualization with data overlays. This vignette demonstrates just one of
the hundreds of visual style mapping options using RCy3.
# Load sample network
closeSession(FALSE) # clears all session data wihtout saving
importNetworkFromFile() # default file = galFiltered.sif
# Load sample data
csv <- system.file("extdata","galExpData.csv", package="RCy3")
data <- read.csv(csv, stringsAsFactors = FALSE)
loadTableData(data,data.key.column="name")
# Prepare data-mapping points
gal80Rexp.min <- min(data$gal80Rexp, na.rm = TRUE)
gal80Rexp.max <- max(data$gal80Rexp, na.rm = TRUE)
## For a balanced color gradient, pick the largest absolute value
gal80Rexp.max.abs <- max(abs(gal80Rexp.min), abs(gal80Rexp.max))
# Set node color gradient from blue to white to red
setNodeColorMapping('gal80Rexp', c(-gal80Rexp.max.abs, 0, gal80Rexp.max.abs),
c('#5577FF','#FFFFFF','#FF7755'), default.color = '#999999')
Filtering Networks by Degree and by Data
Network topology and associated node or edge data can be used to make
selections in Cytoscape that enable filtering and subnetworking. The
filters are added to the Select tab in the Control Panel of Cytoscape’s
GUI and saved in session files.
# Load demo Cytoscape session file
openSession() # default file = galFiltered.cys
net.suid <- getNetworkSuid() # get SUID for future reference
# Filter for neighbors of high degree nodes
createDegreeFilter(filter.name = "degree filter",
criterion = c(0,9),
predicate = "IS_NOT_BETWEEN")
selectFirstNeighbors() # expand selection to first neighbors
createSubnetwork(subnetwork.name = "first neighbors of high degree nodes")
# Filter for high edge betweenness
createColumnFilter(filter.name = "edge betweenness",
type = "edges",
column = "EdgeBetweenness",
4000,
"GREATER_THAN",
network = net.suid)
createSubnetwork(subnetwork.name = "high edge betweenness")
Saving and Exporting Networks
There are local and cloud-hosted options for saving and sharing
network models and images. The Cytoscape session file (CYS) will include
all networks, collections, tables and styles. It should retain every
aspect of your session, including the size of the application window.
Network and image exports include only the currently active network.
Export to NDEx requires account information you can obtain from ndexbio.org.
# Saving sessions
saveSession("MySession") #.cys
## Leave filename blank to update previously saved session file
# Exporting images and networks
exportNetwork() #.sif
## Optionally specify filename, default is network name
## Optionally specify type: SIF(default), CX, cyjs, graphML, NNF, SIF, xGMML
exportImage(type='png') #.png
## Optionally specify filename, default is network name
## Optionally specify type: PNG (default), JPEG, PDF, PostScript, SVG
# Exporting to NDEx, a.k.a. “Dropbox” for networks
exportNetworkToNDEx(username, password, TRUE)
## Account information (username and password) is required to upload
## Use updateNetworkInNDEx if the network has previously been uploaded
Building Maps of Enrichment Analysis Results
This workflow illustrates how to plot an annotated map of enrichment
results using the EnrichmentMap
Pipeline Collection of apps in Cytoscape. An enrichment map is a
network visualization of related genesets in which nodes are gene sets
(or pathways) and edge weight indicates the overlap in member genes.
Following the construction of the enrichment map, AutoAnnotate clusters
redundant gene sets and uses WordCloud to label the resulting cluster.
The code uses the Commands interface to invoke EnrichmentMap and
AutoAnnotate apps. After installing apps, run commandsAPI() to open the
live Swagger documentation to browse and execute command-line
syntax.
installApp("EnrichmentMap Pipeline Collection") # installs 4 apps
# Download sample gmt file of human pathways
gmt.file <- "rcy3_enrichmentmap.gmt"
download.file(file.path("http://download.baderlab.org/EM_Genesets",
"September_01_2019/Human/symbol/Pathways",
"Human_WikiPathways_September_01_2019_symbol.gmt"),
gmt.file)
# Run EnrichmentMap build command
em_command <- paste('enrichmentmap build analysisType="generic"',
"gmtFile=", paste(getwd(), gmt.file, sep="/"),
"pvalue=", 1,
"qvalue=", 1,
"similaritycutoff=",0.25,
"coefficients=","JACCARD")
print(em_command)
commandsGET(em_command)
# Run the AutoAnnotate command
aa_command <- paste("autoannotate annotate-clusterBoosted",
"clusterAlgorithm=MCL",
"labelColumn=EnrichmentMap::GS_DESCR",
"maxWords=3")
print(aa_command)
commandsGET(aa_command)
# Annotate a subnetwork
createSubnetwork(c(1:4),"__mclCluster")
commandsGET(aa_command)
Visualizing Integrated Network Analysis Using BioNet
The BioNet
package implements analytical methods to perform integrated network
analysis, e.g., of gene expression data and clinical survival data in
the context of protein-protein interaction networks. Partnered with
RCy3, the analytical results from BioNet can be visualized in Cytoscape
with vastly more options for customization. Starting with the “Quick
Start” tutorial from BioNet, we pass the results directly to
Cytoscape for visualization:
library(BioNet) # install from Bioconductor
library(DLBCL) # install from Bioconductor
data(dataLym)
data(interactome)
## The following steps are from BioNet's Quick Start tutorial:
pvals <- cbind(t = dataLym$t.pval, s = dataLym$s.pval)
rownames(pvals) <- dataLym$label
pval <- aggrPvals(pvals, order = 2, plot = FALSE)
subnet <- subNetwork(dataLym$label, interactome)
subnet <- rmSelfLoops(subnet)
fb <- fitBumModel(pval, plot = FALSE)
scores <- scoreNodes(subnet, fb, fdr = 0.001)
module <- runFastHeinz(subnet, scores)
logFC <- dataLym$diff
names(logFC) <- dataLym$label
plotModule(module, scores = scores, diff.expr = logFC)
# Using RCy3 we can generate a custom visualization of the same output
## Create network from graphNEL object and load data calculated above
createNetworkFromGraph(module, "module", "BioNet")
loadTableData(as.data.frame(scores))
loadTableData(as.data.frame(logFC))
## Set styles
setNodeLabelMapping("geneSymbol")
setNodeFontSizeDefault(18)
setNodeBorderWidthDefault(3.0)
logFC.max.abs <- max(abs(min(logFC)), abs(max(logFC)))
setNodeColorMapping('logFC', c(-logFC.max.abs, 0, logFC.max.abs),
c('#5577FF','#FFFFFF','#FF7755'), default.color = '#999999')
createColumnFilter("Positive scores", "scores",c(0,max(scores)),"BETWEEN")
setNodeShapeBypass(getSelectedNodes(), "ELLIPSE")
clearSelection()
##Performing Advanced Graph Analytics Using RBGL As an interface to
the BOOST library, the RBGL
Bioconductor package offers an impressive array of analytical functions
for graphs. Here we will start with a network in Cytoscape, load it into
R as a graph object, perform shortest path calculation using RBGL and
then visualize the results back in Cytoscape.
library(RBGL) # install from Bioconductor
# Convert a sample Cytoscape network to a graph object
openSession()
g <- createGraphFromNetwork()
# Identify start and finish nodes (styling is optional)
start <- "YNL216W"
finish <- "YER040W"
setNodeBorderWidthBypass(c(start, finish), 20)
setNodeBorderColorBypass(start, "#00CC33")
setNodeBorderColorBypass(finish, "#CC00CC")
# Use RBGL to perform shortest path calculation
shortest <- sp.between(g, start, finish)
shortest$`YNL216W:YER040W`$length
#[1] 6
shortest.path <- shortest$`YNL216W:YER040W`$path_detail
# Visualize results in Cytoscape
selectNodes(shortest.path, "name")
setNodeBorderWidthBypass(shortest.path, 20)
createSubnetwork()
LS0tCnRpdGxlOiAiUkN5MyBVc2UgQ2FzZXMiCmF1dGhvcjogIkFsZXggUGljbywgSnVsaWEgR3VzdGF2c2VuLCBTaHJhZGRoYSBQYWksIFJ1dGggSXNzZXJsaW4sIEJhcnJ5IERlbWNoYWsiCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogIm5vbmUiCiMgIHBkZl9kb2N1bWVudDoKIyAgICB0b2M6IHRydWUgIAotLS0KYGBge3IsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGV2YWw9RkFMU0UKKQpgYGAKCiMgT3BlcmF0aW9uCkluIG9yZGVyIHRvIHdvcmsgd2l0aCBSQ3kzIHlvdSBtdXN0IGhhdmUgQ3l0b3NjYXBlIHYzLjcgb3IgbGF0ZXIgaW5zdGFsbGVkIGFuZCBydW5uaW5nLiAgQ3l0b3NjYXBlIGNhbiBiZSBpbnN0YWxsZWQgZnJvbSBbY3l0b3NjYXBlLm9yZ10oaHR0cHM6Ly9jeXRvc2NhcGUub3JnKS4gVGhlIFJDeTMgcGFja2FnZSBjYW4gYmUgaW5zdGFsbGVkIGZyb20gW0Jpb2NvbmR1Y3Rvcl0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL1JDeTMuaHRtbH17QmlvY29uZHVjdG9yKToKYGBge3IsIGV2YWw9RkFMU0V9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQmlvY01hbmFnZXIiLCBxdWlldGx5ID0gVFJVRSkpCiAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQpCaW9jTWFuYWdlcjo6aW5zdGFsbCgiUkN5MyIpCmxpYnJhcnkoUkN5MykKYGBgCgpMYXVuY2ggQ3l0b3NjYXBlIGFuZCBrZWVwIGl0IHJ1bm5pbmcgd2hlbmV2ZXIgdXNpbmcgUkN5My4gQ29uZmlybSB0aGF0IHlvdSBoYXZlIGV2ZXJ5dGhpbmcgaW5zdGFsbGVkIGFuZCB0aGF0IFJDeTMgaXMgY29tbXVuaWNhdGluZyB3aXRoIEN5dG9zY2FwZSB2aWEgQ3lSRVNUOgpgYGB7cn0KY3l0b3NjYXBlUGluZyAoKQojWzFdICJZb3UgYXJlIGNvbm5lY3RlZCB0byBDeXRvc2NhcGUhIgpgYGAKCkFzIHdpdGggYW55IFIgcGFja2FnZSwgb25lIGNhbiBhY2Nlc3MgdGhlIGRvY3VtZW50YXRpb24gYW5kIGJyb3dzZSBvdmVyIGEgZG96ZW4gdmlnbmV0dGVzIGluY2x1ZGVkIGluIHRoZSBSQ3kzIHBhY2thZ2U6CmBgYHtyfQpoZWxwKHBhY2thZ2U9UkN5MykKYnJvd3NlVmlnbmV0dGVzKCJSQ3kzIikKYGBgCgojIFVzZSBDYXNlcwpUaGUgZm9sbG93aW5nIHNlY3Rpb25zIGRlbW9uc3RyYXRlIGEgdmFyaWV0eSBvZiBjb21tb24gYW5kIGFkdmFuY2VkIG5ldHdvcmsgYmlvbG9neSB1c2UgY2FzZXMgYXMgcnVubmFibGUgUiBjb2RlIHNuaXBwZXRzLiBUaGUgZmlyc3Qgc2V0IGZvY3VzZXMgb24gZnVuZGFtZW50YWwgQ3l0b3NjYXBlIG9wZXJhdGlvbnMgdGhhdCBhcmUgY29tbW9uIHRvIG1vc3QgdXNlIGNhc2VzOgoKKiBMb2FkaW5nIG5ldHdvcmtzIChmcm9tIFIgb2JqZWN0cywgQ3l0b3NjYXBlIGZpbGVzIGFuZCBwdWJsaWMgZGF0YWJhc2VzKQoqIFZpc3VhbGl6aW5nIG5ldHdvcmsgZGF0YQoqIEZpbHRlcmluZyBieSBub2RlIGRlZ3JlZSBvciBkYXRhCiogU2F2aW5nIGFuZCBleHBvcnRpbmcgbmV0d29ya3MKCkFkZGl0aW9uYWxseSwgdGhlcmUgYXJlIGV4YW1wbGVzIHRoYXQgZGVtb25zdHJhdGUgYW5hbHl0aWNhbCB3b3JrZmxvd3MsIHJlbHlpbmcgbm90IG9ubHkgb24gQ3l0b3NjYXBlLCBidXQgYWxzbyBvbiBDeXRvc2NhcGUgYXBwcyBhbmQgb3RoZXIgUiBwYWNrYWdlczoKCiogQnVpbGRpbmcgbWFwcyBvZiBlbnJpY2htZW50IGFuYWx5c2lzIHJlc3VsdHMgdXNpbmcgRW5yaWNobWVudE1hcCBhbmQgQXV0b0Fubm90YXRlCiogVmlzdWFsaXppbmcgaW50ZWdyYXRlZCBuZXR3b3JrIGFuYWx5c2lzIHVzaW5nIEJpb05ldAoqIFBlcmZvcm1pbmcgYWR2YW5jZWQgZ3JhcGggYW5hbHl0aWNzIHVzaW5nIFJCR0wKCiMjIExvYWRpbmcgTmV0d29ya3MKTmV0d29ya3MgY29tZSBpbiBhbGwgc2hhcGVzIGFuZCBzaXplcyBpbiBtdWx0aXBsZSBmb3JtYXRzIGZyb20gbXVsdGlwbGUgc291cmNlcy4gSGVyZSBhcmUganVzdCBhIGZldyBvZiB0aGUgbXlyaWFkIHdheXMgdG8gbG9hZCBuZXR3b3JrcyBpbnRvIEN5dG9zY2FwZSB1c2luZyBSQ3kzLgoKRnJvbSBSIE9iamVjdHMuIC4gLgpgYGB7cn0KIyBGcm9tIGdyYXBoIG9iamVjdHMgKGdyYXBoTkVMKSAgCmcgPC0gbWFrZVNpbXBsZUdyYXBoKCkKY3JlYXRlTmV0d29ya0Zyb21HcmFwaChnKQojIyBBbmQgcm91bmQtdHJpcCBiYWNrIGZyb20gQ3l0b3NjYXBlIHRvIGdyYXBoIApnMiA8LSBjcmVhdGVHcmFwaEZyb21OZXR3b3JrKCkKCiMgRnJvbSBpZ3JhcGggb2JqZWN0cwpsaWJyYXJ5KGlncmFwaCkKaWcgPC0gbWFrZV9ncmFwaCgiWmFjaGFyeSIpCmNyZWF0ZU5ldHdvcmtGcm9tSWdyYXBoKGlnKQojIyBBbmQgcm91bmQtdHJpcCBiYWNrIGZyb20gQ3l0b3NjYXBlIHRvIGlncmFwaAppZzIgPC0gY3JlYXRlSWdyYXBoRnJvbU5ldHdvcmsoKQojIyBOb3RlIHRoYXQgdGhlIEN5dG9zY2FwZSBtb2RlbCBpbmZlcnMgZGlyZWN0aW9uYWxpdHkKCiMgRnJvbSBkYXRhZnJhbWVzCm5vZGVzIDwtIGRhdGEuZnJhbWUoaWQ9Yygibm9kZSAwIiwibm9kZSAxIiwibm9kZSAyIiwibm9kZSAzIiksCiAgICAgICAgICAgICAgICAgICAgZ3JvdXA9YygiQSIsIkEiLCJCIiwiQiIpLCAjY2F0ZWdvcmljYWwgc3RyaW5ncwogICAgICAgICAgICAgICAgICAgIHNjb3JlPWFzLmludGVnZXIoYygyMCwxMCwxNSw1KSksICNpbnRlZ2VycwogICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnM9RkFMU0UpCmVkZ2VzIDwtIGRhdGEuZnJhbWUoc291cmNlPWMoIm5vZGUgMCIsIm5vZGUgMCIsIm5vZGUgMCIsIm5vZGUgMiIpLAogICAgICAgICAgICAgICAgICAgIHRhcmdldD1jKCJub2RlIDEiLCJub2RlIDIiLCJub2RlIDMiLCJub2RlIDMiKSwKICAgICAgICAgICAgICAgICAgICBpbnRlcmFjdGlvbj1jKCJpbmhpYml0cyIsImludGVyYWN0cyIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgImFjdGl2YXRlcyIsImludGVyYWN0cyIpLCAgI29wdGlvbmFsCiAgICAgICAgICAgICAgICAgICAgd2VpZ2h0PWMoNS4xLDMuMCw1LjIsOS45KSwgI251bWVyaWNzCiAgICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKY3JlYXRlTmV0d29ya0Zyb21EYXRhRnJhbWVzKG5vZGVzLCBlZGdlcykKYGBgCgpGcm9tIEN5dG9zY2FwZS1zdXBwb3J0ZWQgRmlsZSBGb3JtYXRzLiAuIC4KYGBge3J9CiMgRnJvbSBDeXRvc2NhcGUgc2Vzc2lvbiBmaWxlcwojIyBXaWxsIGVyYXNlIGFuZCByZXBsYWNlIGFsbCBkYXRhIGZyb20gY3VycmVudCBzZXNzaW9uIQpvcGVuU2Vzc2lvbigpICMgZGVmYXVsdCBmaWxlID0gZ2FsRmlsdGVyZWQuY3lzCgojIEZyb20gbG9jYWwgbmV0d29yayBmaWxlcwppbXBvcnROZXR3b3JrRnJvbUZpbGUoKSAjIGRlZmF1bHQgZmlsZSA9IGdhbEZpbHRlcmVkLnNpZgojIyBTdXBwb3J0ZWQgZmlsZSBmb3JtYXRzOiBTSUYsIEdNTCwgeEdNTUwsIGdyYXBoTUwsIENYLCBwbHVzCgojIEZyb20gTkRFeCwgdGhlIG5ldHdvcmsgZGF0YWJhc2UKaW1wb3J0TmV0d29ya0Zyb21OREV4KCI1YmU4NTgxNy0xZTVmLTExZTgtYjkzOS0wYWMxMzVlOGJhY2YiKQojIyBBY2NvdW50IGluZm9ybWF0aW9uIG9yIGFjY2Vzc0tleSBhcmUgcmVxdWlyZWQgYXJndW1lbnRzIG9ubHkKIyMgd2hlbiBhY2Nlc3NpbmcgcHJpdmF0ZSBjb250ZW50CmBgYAoKRnJvbSBQdWJsaWMgRGF0YWJhc2VzIHZpYSBDeXRvc2NhcGUgQXBwcy4gLiAuCmBgYHtyfQojIEZyb20gU1RSSU5HLCBzdGFydGluZyB3aXRoIGEgbGlzdCBvZiBnZW5lcy9wcm90ZWlucwppbnN0YWxsQXBwKCJzdHJpbmdBcHAiKQpnZW5lLmxpc3QgPC0gYygiVDUzIiwiQUtUMSIsIkNES04xQSIpCmdlbmUuc3RyIDwtIHBhc3RlKGdlbmUubGlzdCwgY29sbGFwc2UgPSAiLCIpCnN0cmluZy5jbWQgPC0gcGFzdGUoInN0cmluZyBwcm90ZWluIHF1ZXJ5IGN1dG9mZj0wLjk5IGxpbWl0PTQwIHF1ZXJ5IiwKICAgICAgICAgICAgICAgICAgICBnZW5lLnN0ciwgc2VwID0gIj0iKQpjb21tYW5kc1J1bihzdHJpbmcuY21kKQoKIyBGcm9tIFdpa2lQYXRod2F5cywgc3RhcnRpbmcgd2l0aCBhIGtleXdvcmQKbGlicmFyeShyV2lraVBhdGh3YXlzKSAjIGluc3RhbGwgZnJvbSBCaW9jb25kdWN0b3IKaW5zdGFsbEFwcCgiV2lraVBhdGh3YXlzIikKa2V5d29yZCA8LSAiZ2xpb2JsYXN0b21hIgpnYm0ucGF0aHdheXMgPC0gZmluZFBhdGh3YXlzQnlUZXh0KGtleXdvcmQpCmdibS53cGlkIDwtIGdibS5wYXRod2F5c1tbMV1dJGlkICMgbGV04oCZcyBqdXN0IHRha2UgdGhlIGZpcnN0IG9uZQp3aWtpcGF0aHdheXMuY21kIDwtIHBhc3RlKCJ3aWtpcGF0aHdheXMgaW1wb3J0LWFzLXBhdGh3YXkgaWQiLAogICAgICAgICAgICAgICAgICAgICAgICAgIGdibS53cGlkLCBzZXAgPSAiPSIpCmNvbW1hbmRzUnVuKHdpa2lwYXRod2F5cy5jbWQpCmBgYAoKIyMgVmlzdWFsaXppbmcgRGF0YSBvbiBOZXR3b3JrcwpDeXRvc2NhcGUgZXhjZWxzIGF0IGdlbmVyYXRpbmcgcHVibGljYXRpb24tcXVhbGl0eSBuZXR3b3JrIHZpc3VhbGl6YXRpb24gd2l0aCBkYXRhIG92ZXJsYXlzLiBUaGlzIHZpZ25ldHRlIGRlbW9uc3RyYXRlcyBqdXN0IG9uZSBvZiB0aGUgaHVuZHJlZHMgb2YgdmlzdWFsIHN0eWxlIG1hcHBpbmcgb3B0aW9ucyB1c2luZyBSQ3kzLgpgYGB7cn0KIyBMb2FkIHNhbXBsZSBuZXR3b3JrCmNsb3NlU2Vzc2lvbihGQUxTRSkgIyBjbGVhcnMgYWxsIHNlc3Npb24gZGF0YSB3aWh0b3V0IHNhdmluZwppbXBvcnROZXR3b3JrRnJvbUZpbGUoKSAjIGRlZmF1bHQgZmlsZSA9IGdhbEZpbHRlcmVkLnNpZgoKIyBMb2FkIHNhbXBsZSBkYXRhCmNzdiA8LSBzeXN0ZW0uZmlsZSgiZXh0ZGF0YSIsImdhbEV4cERhdGEuY3N2IiwgcGFja2FnZT0iUkN5MyIpCmRhdGEgPC0gcmVhZC5jc3YoY3N2LCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCmxvYWRUYWJsZURhdGEoZGF0YSxkYXRhLmtleS5jb2x1bW49Im5hbWUiKQoKIyBQcmVwYXJlIGRhdGEtbWFwcGluZyBwb2ludHMKZ2FsODBSZXhwLm1pbiA8LSBtaW4oZGF0YSRnYWw4MFJleHAsIG5hLnJtID0gVFJVRSkKZ2FsODBSZXhwLm1heCA8LSBtYXgoZGF0YSRnYWw4MFJleHAsIG5hLnJtID0gVFJVRSkKIyMgRm9yIGEgYmFsYW5jZWQgY29sb3IgZ3JhZGllbnQsIHBpY2sgdGhlIGxhcmdlc3QgYWJzb2x1dGUgdmFsdWUKZ2FsODBSZXhwLm1heC5hYnMgPC0gbWF4KGFicyhnYWw4MFJleHAubWluKSwgYWJzKGdhbDgwUmV4cC5tYXgpKSAKCiMgU2V0IG5vZGUgY29sb3IgZ3JhZGllbnQgZnJvbSBibHVlIHRvIHdoaXRlIHRvIHJlZApzZXROb2RlQ29sb3JNYXBwaW5nKCdnYWw4MFJleHAnLCBjKC1nYWw4MFJleHAubWF4LmFicywgMCwgZ2FsODBSZXhwLm1heC5hYnMpLCAKICAgICAgICAgICAgICAgICAgICBjKCcjNTU3N0ZGJywnI0ZGRkZGRicsJyNGRjc3NTUnKSwgZGVmYXVsdC5jb2xvciA9ICcjOTk5OTk5JykKCmBgYAoKIyMgRmlsdGVyaW5nIE5ldHdvcmtzIGJ5IERlZ3JlZSBhbmQgYnkgRGF0YQpOZXR3b3JrIHRvcG9sb2d5IGFuZCBhc3NvY2lhdGVkIG5vZGUgb3IgZWRnZSBkYXRhIGNhbiBiZSB1c2VkIHRvIG1ha2Ugc2VsZWN0aW9ucyBpbiBDeXRvc2NhcGUgdGhhdCBlbmFibGUgZmlsdGVyaW5nIGFuZCBzdWJuZXR3b3JraW5nLiBUaGUgZmlsdGVycyBhcmUgYWRkZWQgdG8gdGhlIFNlbGVjdCB0YWIgaW4gdGhlIENvbnRyb2wgUGFuZWwgb2YgQ3l0b3NjYXBl4oCZcyBHVUkgYW5kIHNhdmVkIGluIHNlc3Npb24gZmlsZXMuCmBgYHtyfQojIExvYWQgZGVtbyBDeXRvc2NhcGUgc2Vzc2lvbiBmaWxlCm9wZW5TZXNzaW9uKCkgIyBkZWZhdWx0IGZpbGUgPSBnYWxGaWx0ZXJlZC5jeXMKbmV0LnN1aWQgPC0gZ2V0TmV0d29ya1N1aWQoKSAjIGdldCBTVUlEIGZvciBmdXR1cmUgcmVmZXJlbmNlCgojIEZpbHRlciBmb3IgbmVpZ2hib3JzIG9mIGhpZ2ggZGVncmVlIG5vZGVzCmNyZWF0ZURlZ3JlZUZpbHRlcihmaWx0ZXIubmFtZSA9ICJkZWdyZWUgZmlsdGVyIiwKICAgICAgICAgICAgICAgICAgIGNyaXRlcmlvbiA9IGMoMCw5KSwKICAgICAgICAgICAgICAgICAgIHByZWRpY2F0ZSA9ICJJU19OT1RfQkVUV0VFTiIpCnNlbGVjdEZpcnN0TmVpZ2hib3JzKCkgIyBleHBhbmQgc2VsZWN0aW9uIHRvIGZpcnN0IG5laWdoYm9ycwpjcmVhdGVTdWJuZXR3b3JrKHN1Ym5ldHdvcmsubmFtZSA9ICJmaXJzdCBuZWlnaGJvcnMgb2YgaGlnaCBkZWdyZWUgbm9kZXMiKQoKIyBGaWx0ZXIgZm9yIGhpZ2ggZWRnZSBiZXR3ZWVubmVzcwpjcmVhdGVDb2x1bW5GaWx0ZXIoZmlsdGVyLm5hbWUgPSAiZWRnZSBiZXR3ZWVubmVzcyIsCiAgICAgICAgICAgICAgICAgICB0eXBlID0gImVkZ2VzIiwKICAgICAgICAgICAgICAgICAgIGNvbHVtbiA9ICJFZGdlQmV0d2Vlbm5lc3MiLAogICAgICAgICAgICAgICAgICAgNDAwMCwKICAgICAgICAgICAgICAgICAgICJHUkVBVEVSX1RIQU4iLAogICAgICAgICAgICAgICAgICAgbmV0d29yayA9IG5ldC5zdWlkKQpjcmVhdGVTdWJuZXR3b3JrKHN1Ym5ldHdvcmsubmFtZSA9ICJoaWdoIGVkZ2UgYmV0d2Vlbm5lc3MiKQpgYGAKCiMjIFNhdmluZyBhbmQgRXhwb3J0aW5nIE5ldHdvcmtzClRoZXJlIGFyZSBsb2NhbCBhbmQgY2xvdWQtaG9zdGVkIG9wdGlvbnMgZm9yIHNhdmluZyBhbmQgc2hhcmluZyBuZXR3b3JrIG1vZGVscyBhbmQgaW1hZ2VzLiBUaGUgQ3l0b3NjYXBlIHNlc3Npb24gZmlsZSAoQ1lTKSB3aWxsIGluY2x1ZGUgYWxsIG5ldHdvcmtzLCBjb2xsZWN0aW9ucywgdGFibGVzIGFuZCBzdHlsZXMuIEl0IHNob3VsZCByZXRhaW4gZXZlcnkgYXNwZWN0IG9mIHlvdXIgc2Vzc2lvbiwgaW5jbHVkaW5nIHRoZSBzaXplIG9mIHRoZSBhcHBsaWNhdGlvbiB3aW5kb3cuIE5ldHdvcmsgYW5kIGltYWdlIGV4cG9ydHMgaW5jbHVkZSBvbmx5IHRoZSBjdXJyZW50bHkgYWN0aXZlIG5ldHdvcmsuIEV4cG9ydCB0byBOREV4IHJlcXVpcmVzIGFjY291bnQgaW5mb3JtYXRpb24geW91IGNhbiBvYnRhaW4gZnJvbSBbbmRleGJpby5vcmddKGh0dHBzOi8vbmRleGJpby5vcmcpLgpgYGB7cn0KIyBTYXZpbmcgc2Vzc2lvbnMKc2F2ZVNlc3Npb24oIk15U2Vzc2lvbiIpICMuY3lzCiMjIExlYXZlIGZpbGVuYW1lIGJsYW5rIHRvIHVwZGF0ZSBwcmV2aW91c2x5IHNhdmVkIHNlc3Npb24gZmlsZQoKIyBFeHBvcnRpbmcgaW1hZ2VzIGFuZCBuZXR3b3JrcwpleHBvcnROZXR3b3JrKCkgIy5zaWYKIyMgT3B0aW9uYWxseSBzcGVjaWZ5IGZpbGVuYW1lLCBkZWZhdWx0IGlzIG5ldHdvcmsgbmFtZQojIyBPcHRpb25hbGx5IHNwZWNpZnkgdHlwZTogU0lGKGRlZmF1bHQpLCBDWCwgY3lqcywgZ3JhcGhNTCwgTk5GLCBTSUYsIHhHTU1MCmV4cG9ydEltYWdlKHR5cGU9J3BuZycpICMucG5nCiMjIE9wdGlvbmFsbHkgc3BlY2lmeSBmaWxlbmFtZSwgZGVmYXVsdCBpcyBuZXR3b3JrIG5hbWUKIyMgT3B0aW9uYWxseSBzcGVjaWZ5IHR5cGU6IFBORyAoZGVmYXVsdCksIEpQRUcsIFBERiwgUG9zdFNjcmlwdCwgU1ZHIAoKIyBFeHBvcnRpbmcgdG8gTkRFeCwgYS5rLmEuIOKAnERyb3Bib3jigJ0gZm9yIG5ldHdvcmtzCmV4cG9ydE5ldHdvcmtUb05ERXgodXNlcm5hbWUsIHBhc3N3b3JkLCBUUlVFKQojIyBBY2NvdW50IGluZm9ybWF0aW9uICh1c2VybmFtZSBhbmQgcGFzc3dvcmQpIGlzIHJlcXVpcmVkIHRvIHVwbG9hZAojIyBVc2UgdXBkYXRlTmV0d29ya0luTkRFeCBpZiB0aGUgbmV0d29yayBoYXMgcHJldmlvdXNseSBiZWVuIHVwbG9hZGVkCmBgYAoKIyMgQnVpbGRpbmcgTWFwcyBvZiBFbnJpY2htZW50IEFuYWx5c2lzIFJlc3VsdHMKVGhpcyB3b3JrZmxvdyBpbGx1c3RyYXRlcyBob3cgdG8gcGxvdCBhbiBhbm5vdGF0ZWQgbWFwIG9mIGVucmljaG1lbnQgcmVzdWx0cyB1c2luZyB0aGUgIFtFbnJpY2htZW50TWFwIFBpcGVsaW5lIENvbGxlY3Rpb24gb2YgYXBwc10oaHR0cDovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL2VucmljaG1lbnRtYXBwaXBlbGluZWNvbGxlY3Rpb24pIGluIEN5dG9zY2FwZS4gIEFuIGVucmljaG1lbnQgbWFwIGlzIGEgbmV0d29yayB2aXN1YWxpemF0aW9uIG9mIHJlbGF0ZWQgZ2VuZXNldHMgaW4gd2hpY2ggbm9kZXMgYXJlIGdlbmUgc2V0cyAob3IgcGF0aHdheXMpIGFuZCBlZGdlIHdlaWdodCBpbmRpY2F0ZXMgdGhlIG92ZXJsYXAgaW4gbWVtYmVyIGdlbmVzLiBGb2xsb3dpbmcgdGhlIGNvbnN0cnVjdGlvbiBvZiB0aGUgZW5yaWNobWVudCBtYXAsIEF1dG9Bbm5vdGF0ZSBjbHVzdGVycyByZWR1bmRhbnQgZ2VuZSBzZXRzIGFuZCB1c2VzIFdvcmRDbG91ZCB0byBsYWJlbCB0aGUgcmVzdWx0aW5nIGNsdXN0ZXIuIFRoZSBjb2RlIHVzZXMgdGhlIENvbW1hbmRzIGludGVyZmFjZSB0byBpbnZva2UgRW5yaWNobWVudE1hcCBhbmQgQXV0b0Fubm90YXRlIGFwcHMuIEFmdGVyIGluc3RhbGxpbmcgYXBwcywgcnVuIGNvbW1hbmRzQVBJKCkgdG8gb3BlbiB0aGUgbGl2ZSBTd2FnZ2VyIGRvY3VtZW50YXRpb24gdG8gYnJvd3NlIGFuZCBleGVjdXRlIGNvbW1hbmQtbGluZSBzeW50YXguCmBgYHtyfQppbnN0YWxsQXBwKCJFbnJpY2htZW50TWFwIFBpcGVsaW5lIENvbGxlY3Rpb24iKSAjIGluc3RhbGxzIDQgYXBwcwojIERvd25sb2FkIHNhbXBsZSBnbXQgZmlsZSBvZiBodW1hbiBwYXRod2F5cwpnbXQuZmlsZSA8LSAicmN5M19lbnJpY2htZW50bWFwLmdtdCIKZG93bmxvYWQuZmlsZShmaWxlLnBhdGgoImh0dHA6Ly9kb3dubG9hZC5iYWRlcmxhYi5vcmcvRU1fR2VuZXNldHMiLAogICAgICAgICAgICAgICAgICAgICAgICAiU2VwdGVtYmVyXzAxXzIwMTkvSHVtYW4vc3ltYm9sL1BhdGh3YXlzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIkh1bWFuX1dpa2lQYXRod2F5c19TZXB0ZW1iZXJfMDFfMjAxOV9zeW1ib2wuZ210IiksCiAgICAgICAgICAgICAgZ210LmZpbGUpCiMgUnVuIEVucmljaG1lbnRNYXAgYnVpbGQgY29tbWFuZAplbV9jb21tYW5kIDwtIHBhc3RlKCdlbnJpY2htZW50bWFwIGJ1aWxkIGFuYWx5c2lzVHlwZT0iZ2VuZXJpYyInLAogICAgICAgICAgICAgICAgICAgICJnbXRGaWxlPSIsIHBhc3RlKGdldHdkKCksIGdtdC5maWxlLCBzZXA9Ii8iKSwKICAgICAgICAgICAgICAgICAgICAicHZhbHVlPSIsIDEsCiAgICAgICAgICAgICAgICAgICAgInF2YWx1ZT0iLCAxLAogICAgICAgICAgICAgICAgICAgICJzaW1pbGFyaXR5Y3V0b2ZmPSIsMC4yNSwKICAgICAgICAgICAgICAgICAgICAiY29lZmZpY2llbnRzPSIsIkpBQ0NBUkQiKQpwcmludChlbV9jb21tYW5kKQpjb21tYW5kc0dFVChlbV9jb21tYW5kKQojIFJ1biB0aGUgQXV0b0Fubm90YXRlIGNvbW1hbmQKYWFfY29tbWFuZCA8LSBwYXN0ZSgiYXV0b2Fubm90YXRlIGFubm90YXRlLWNsdXN0ZXJCb29zdGVkIiwKICAgICAgICAgICAgICAgICAgICAiY2x1c3RlckFsZ29yaXRobT1NQ0wiLAogICAgICAgICAgICAgICAgICAgICJsYWJlbENvbHVtbj1FbnJpY2htZW50TWFwOjpHU19ERVNDUiIsCiAgICAgICAgICAgICAgICAgICAgIm1heFdvcmRzPTMiKQpwcmludChhYV9jb21tYW5kKQpjb21tYW5kc0dFVChhYV9jb21tYW5kKQojIEFubm90YXRlIGEgc3VibmV0d29yawpjcmVhdGVTdWJuZXR3b3JrKGMoMTo0KSwiX19tY2xDbHVzdGVyIikKY29tbWFuZHNHRVQoYWFfY29tbWFuZCkKYGBgCgojIyBWaXN1YWxpemluZyBJbnRlZ3JhdGVkIE5ldHdvcmsgQW5hbHlzaXMgVXNpbmcgQmlvTmV0IApUaGUgW0Jpb05ldF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0Jpb05ldC5odG1sKSBwYWNrYWdlIGltcGxlbWVudHMgYW5hbHl0aWNhbCBtZXRob2RzIHRvIHBlcmZvcm0gaW50ZWdyYXRlZCBuZXR3b3JrIGFuYWx5c2lzLCBlLmcuLCBvZiBnZW5lIGV4cHJlc3Npb24gZGF0YSBhbmQgY2xpbmljYWwgc3Vydml2YWwgZGF0YSBpbiB0aGUgY29udGV4dCBvZiBwcm90ZWluLXByb3RlaW4gaW50ZXJhY3Rpb24gbmV0d29ya3MuIFBhcnRuZXJlZCB3aXRoIFJDeTMsIHRoZSBhbmFseXRpY2FsIHJlc3VsdHMgZnJvbSBCaW9OZXQgY2FuIGJlIHZpc3VhbGl6ZWQgaW4gQ3l0b3NjYXBlIHdpdGggdmFzdGx5IG1vcmUgb3B0aW9ucyBmb3IgY3VzdG9taXphdGlvbi4gU3RhcnRpbmcgd2l0aCB0aGUgWyJRdWljayBTdGFydCIgdHV0b3JpYWxdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvdmlnbmV0dGVzL0Jpb05ldC9pbnN0L2RvYy9UdXRvcmlhbC5wZGYpIGZyb20gQmlvTmV0LCB3ZSBwYXNzIHRoZSByZXN1bHRzIGRpcmVjdGx5IHRvIEN5dG9zY2FwZSBmb3IgdmlzdWFsaXphdGlvbjoKCmBgYHtyfQpsaWJyYXJ5KEJpb05ldCkgIyBpbnN0YWxsIGZyb20gQmlvY29uZHVjdG9yCmxpYnJhcnkoRExCQ0wpICMgaW5zdGFsbCBmcm9tIEJpb2NvbmR1Y3RvcgpkYXRhKGRhdGFMeW0pCmRhdGEoaW50ZXJhY3RvbWUpCiMjIFRoZSBmb2xsb3dpbmcgc3RlcHMgYXJlIGZyb20gQmlvTmV0J3MgUXVpY2sgU3RhcnQgdHV0b3JpYWw6CnB2YWxzIDwtIGNiaW5kKHQgPSBkYXRhTHltJHQucHZhbCwgcyA9IGRhdGFMeW0kcy5wdmFsKQpyb3duYW1lcyhwdmFscykgPC0gZGF0YUx5bSRsYWJlbApwdmFsIDwtIGFnZ3JQdmFscyhwdmFscywgb3JkZXIgPSAyLCBwbG90ID0gRkFMU0UpCnN1Ym5ldCA8LSBzdWJOZXR3b3JrKGRhdGFMeW0kbGFiZWwsIGludGVyYWN0b21lKQpzdWJuZXQgPC0gcm1TZWxmTG9vcHMoc3VibmV0KQpmYiA8LSBmaXRCdW1Nb2RlbChwdmFsLCBwbG90ID0gRkFMU0UpCnNjb3JlcyA8LSBzY29yZU5vZGVzKHN1Ym5ldCwgZmIsIGZkciA9IDAuMDAxKQptb2R1bGUgPC0gcnVuRmFzdEhlaW56KHN1Ym5ldCwgc2NvcmVzKQpsb2dGQyA8LSBkYXRhTHltJGRpZmYKbmFtZXMobG9nRkMpIDwtIGRhdGFMeW0kbGFiZWwKcGxvdE1vZHVsZShtb2R1bGUsIHNjb3JlcyA9IHNjb3JlcywgZGlmZi5leHByID0gbG9nRkMpCgojIFVzaW5nIFJDeTMgd2UgY2FuIGdlbmVyYXRlIGEgY3VzdG9tIHZpc3VhbGl6YXRpb24gb2YgdGhlIHNhbWUgb3V0cHV0CiMjIENyZWF0ZSBuZXR3b3JrIGZyb20gZ3JhcGhORUwgb2JqZWN0IGFuZCBsb2FkIGRhdGEgY2FsY3VsYXRlZCBhYm92ZQpjcmVhdGVOZXR3b3JrRnJvbUdyYXBoKG1vZHVsZSwgIm1vZHVsZSIsICJCaW9OZXQiKQpsb2FkVGFibGVEYXRhKGFzLmRhdGEuZnJhbWUoc2NvcmVzKSkKbG9hZFRhYmxlRGF0YShhcy5kYXRhLmZyYW1lKGxvZ0ZDKSkKIyMgU2V0IHN0eWxlcwpzZXROb2RlTGFiZWxNYXBwaW5nKCJnZW5lU3ltYm9sIikKc2V0Tm9kZUZvbnRTaXplRGVmYXVsdCgxOCkKc2V0Tm9kZUJvcmRlcldpZHRoRGVmYXVsdCgzLjApCmxvZ0ZDLm1heC5hYnMgPC0gbWF4KGFicyhtaW4obG9nRkMpKSwgYWJzKG1heChsb2dGQykpKSAKc2V0Tm9kZUNvbG9yTWFwcGluZygnbG9nRkMnLCBjKC1sb2dGQy5tYXguYWJzLCAwLCBsb2dGQy5tYXguYWJzKSwgCiAgICAgICAgICAgICAgICAgICAgYygnIzU1NzdGRicsJyNGRkZGRkYnLCcjRkY3NzU1JyksIGRlZmF1bHQuY29sb3IgPSAnIzk5OTk5OScpCmNyZWF0ZUNvbHVtbkZpbHRlcigiUG9zaXRpdmUgc2NvcmVzIiwgInNjb3JlcyIsYygwLG1heChzY29yZXMpKSwiQkVUV0VFTiIpCnNldE5vZGVTaGFwZUJ5cGFzcyhnZXRTZWxlY3RlZE5vZGVzKCksICJFTExJUFNFIikKY2xlYXJTZWxlY3Rpb24oKQpgYGAKCiMjUGVyZm9ybWluZyBBZHZhbmNlZCBHcmFwaCBBbmFseXRpY3MgVXNpbmcgUkJHTApBcyBhbiBpbnRlcmZhY2UgdG8gdGhlIEJPT1NUIGxpYnJhcnksIHRoZSBbUkJHTF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL1JCR0wuaHRtbCkgQmlvY29uZHVjdG9yIHBhY2thZ2Ugb2ZmZXJzIGFuIGltcHJlc3NpdmUgYXJyYXkgb2YgYW5hbHl0aWNhbCBmdW5jdGlvbnMgZm9yIGdyYXBocy4gSGVyZSB3ZSB3aWxsIHN0YXJ0IHdpdGggYSBuZXR3b3JrIGluIEN5dG9zY2FwZSwgbG9hZCBpdCBpbnRvIFIgYXMgYSBncmFwaCBvYmplY3QsIHBlcmZvcm0gc2hvcnRlc3QgcGF0aCBjYWxjdWxhdGlvbiB1c2luZyBSQkdMIGFuZCB0aGVuIHZpc3VhbGl6ZSB0aGUgcmVzdWx0cyBiYWNrIGluIEN5dG9zY2FwZS4KYGBge3J9CmxpYnJhcnkoUkJHTCkgIyBpbnN0YWxsIGZyb20gQmlvY29uZHVjdG9yCiMgQ29udmVydCBhIHNhbXBsZSBDeXRvc2NhcGUgbmV0d29yayB0byBhIGdyYXBoIG9iamVjdApvcGVuU2Vzc2lvbigpCmcgPC0gY3JlYXRlR3JhcGhGcm9tTmV0d29yaygpCiMgSWRlbnRpZnkgc3RhcnQgYW5kIGZpbmlzaCBub2RlcyAoc3R5bGluZyBpcyBvcHRpb25hbCkKc3RhcnQgPC0gIllOTDIxNlciCmZpbmlzaCA8LSAiWUVSMDQwVyIKc2V0Tm9kZUJvcmRlcldpZHRoQnlwYXNzKGMoc3RhcnQsIGZpbmlzaCksIDIwKQpzZXROb2RlQm9yZGVyQ29sb3JCeXBhc3Moc3RhcnQsICIjMDBDQzMzIikKc2V0Tm9kZUJvcmRlckNvbG9yQnlwYXNzKGZpbmlzaCwgIiNDQzAwQ0MiKQojIFVzZSBSQkdMIHRvIHBlcmZvcm0gc2hvcnRlc3QgcGF0aCBjYWxjdWxhdGlvbgpzaG9ydGVzdCA8LSBzcC5iZXR3ZWVuKGcsIHN0YXJ0LCBmaW5pc2gpCnNob3J0ZXN0JGBZTkwyMTZXOllFUjA0MFdgJGxlbmd0aAojWzFdIDYKc2hvcnRlc3QucGF0aCA8LSBzaG9ydGVzdCRgWU5MMjE2VzpZRVIwNDBXYCRwYXRoX2RldGFpbAojIFZpc3VhbGl6ZSByZXN1bHRzIGluIEN5dG9zY2FwZQpzZWxlY3ROb2RlcyhzaG9ydGVzdC5wYXRoLCAibmFtZSIpCnNldE5vZGVCb3JkZXJXaWR0aEJ5cGFzcyhzaG9ydGVzdC5wYXRoLCAyMCkKY3JlYXRlU3VibmV0d29yaygpCmBgYAoK