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 show you how to map or translate identifiers from
one database (e.g., Ensembl) to another (e.g, Entrez Gene). This is a
common requirement for data analysis. In the context of Cytoscape, for
example, identifier mapping is needed when you want to import data to
overlay on a network but you don’t have matching keys. There are three
distinct examples below, highlighting different lessons that may apply
to your use cases.
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:
Example: Species specific considerations
When planning to import data, you need to consider the key columns
you have in your network data and in your table data. It’s always
recommended that you use proper identifiers as your keys (e.g., from
databases like Ensembl and Uniprot-TrEMBL). Relying on conventional
symbols and names is not standard and error prone.
Let’s start with the sample network provided by Cytoscape.
Caution: Loading a session file will discard your current
session. Save first, if you have networks or data you want to keep. Use
saveSession(‘path_to_file’).
openSession() #Closes current session (without saving) and opens a sample session file
You should now see a network with just over 300 nodes. If you look at
the Node Table, you’ll see that there are proper identifiers in the
name columns, like “YDL194W”. These are the Ensembl-supported
IDs for Yeast.
Example: From proteins to genes
For this next example, you’ll need the STRING app to access the
STRING database from within Cytoscape: * Install the STRING app from https://apps.cytoscape.org/apps/stringapp
#available in Cytoscape 3.7.0 and above
installApp('STRINGapp')
Now we can import protein interaction networks with a ton of
annotations from the STRING database with a simple commandsGET function,
like this:
string.cmd = 'string disease query disease="breast cancer" cutoff=0.9 species="Homo sapiens" limit=150'
commandsGET(string.cmd)
# for more information on string commands:
# commandsHelp('string')
# commandsHelp('string disease query')
Check out the Node Table and you’ll see display names and
identifiers. In particular, the canonical name column appears
to hold Uniprot-TrEMBL IDs. Nice, we can use that!
Example: Mixed identifiers
From time to time, you’ll come across a case where the identifiers in
your network are of mixed types. This is a rare scenario, but here is
one approach to solving it.
First, you’ll need the WikiPathways app to access the WikiPathways
database. The pathways in WikiPathways are curated by a community of
interested researchers and citizen scientists. As such, there are times
where authors might use different sources of identifiers. They are valid
IDs, just not all from the same source. Future versions of the
WikiPathways app will provide pre-mapped columns to a single ID type.
But in the meantime (and relevant to other use cases), this
example highlights how to handle a source of mixed identifier
types.
#available in Cytoscape 3.7.0 and above
installApp('WikiPathways')
Now we can import an Apoptosis Pathway from WikiPathways. Either from
the web site (https://wikipathways.org), or from the Network Search
Tool in Cytoscape GUI or from the rWikiPathways package, we could
identify the pathway as WP254.
wp.cmd = 'wikipathways import-as-pathway id="WP254"'
commandsGET(wp.cmd)
# for more information on wikipathways commands:
# commandsHelp('wikipathways')
# commandsHelp('wikipathways import-as-pathway')
Take look in the XrefId column and you’ll see a mix of
identifier types. The next column over, XrefDatasource,
conveniently names each type’s source. Ignoring the metabolites for this
example, we just have a mix of Ensembl and Entrez Gene to deal with.
More advanced cases
This identifier mapping function is intended to handle the majority
of common ID mapping problems. It has limitation, however.
If you need an ID mapping solution for species or ID types not
covered by this tool, or if you want to connect to alternative sources
of mappings, then check out the BridgeDb app: http://apps.cytoscape.org/apps/bridgedb.
#available in Cytoscape 3.7.0 and above
installApp('BridgeDb')
And then browse the available function with
commandsHelp(‘bridgedb’)
LS0tCnRpdGxlOiAiSWRlbnRpZmllciBtYXBwaW5nIgphdXRob3I6ICJieSBBbGV4YW5kZXIgUGljbyIKcGFja2FnZTogUkN5MwpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiAibm9uZSIKIyAgcGRmX2RvY3VtZW50OgojICAgIHRvYzogdHJ1ZSAKdmlnbmV0dGU6ID4KICAlXFZpZ25ldHRlSW5kZXhFbnRyeXswNy4gSWRlbnRpZmllciBtYXBwaW5nIH4yMCBtaW59CiAgJVxWaWduZXR0ZUVuZ2luZXtrbml0cjo6cm1hcmtkb3dufQogICVcVmlnbmV0dGVFbmNvZGluZ3tVVEYtOH0KLS0tCmBgYHtyLCBlY2hvID0gRkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldCgKICBldmFsPUZBTFNFCikKYGBgCipUaGUgUiBtYXJrZG93biBpcyBhdmFpbGFibGUgZnJvbSB0aGUgcHVsbGRvd24gbWVudSBmb3IqIENvZGUgKmF0IHRoZSB1cHBlci1yaWdodCwgY2hvb3NlICJEb3dubG9hZCBSbWQiLCBvciBbZG93bmxvYWQgdGhlIFJtZCBmcm9tIEdpdEh1Yl0oaHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2N5dG9zY2FwZS9jeXRvc2NhcGUtYXV0b21hdGlvbi9tYXN0ZXIvZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9JZGVudGlmaWVyLW1hcHBpbmcuUm1kKS4qCgo8aHIgLz4KVGhpcyB2aWduZXR0ZSB3aWxsIHNob3cgeW91IGhvdyB0byBtYXAgb3IgdHJhbnNsYXRlIGlkZW50aWZpZXJzIGZyb20gb25lIGRhdGFiYXNlCihlLmcuLCBFbnNlbWJsKSB0byBhbm90aGVyICAoZS5nLCBFbnRyZXogR2VuZSkuIFRoaXMgaXMgYSBjb21tb24gcmVxdWlyZW1lbnQKZm9yIGRhdGEgYW5hbHlzaXMuIEluIHRoZSBjb250ZXh0IG9mIEN5dG9zY2FwZSwgZm9yIGV4YW1wbGUsIGlkZW50aWZpZXIgbWFwcGluZyBpcwpuZWVkZWQgd2hlbiB5b3Ugd2FudCB0byBpbXBvcnQgZGF0YSB0byBvdmVybGF5IG9uIGEgbmV0d29yayBidXQgeW91IGRvbid0IGhhdmUKbWF0Y2hpbmcga2V5cy4gVGhlcmUgYXJlIHRocmVlIGRpc3RpbmN0IGV4YW1wbGVzIGJlbG93LCBoaWdobGlnaHRpbmcgZGlmZmVyZW50Cmxlc3NvbnMgdGhhdCBtYXkgYXBwbHkgdG8geW91ciB1c2UgY2FzZXMuCgojIEluc3RhbGxhdGlvbgpgYGB7cn0KaWYoISJSQ3kzIiAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpKXsKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKICAgIEJpb2NNYW5hZ2VyOjppbnN0YWxsKCJSQ3kzIikKfQpsaWJyYXJ5KFJDeTMpCmBgYAoKIyBSZXF1aXJlZCBTb2Z0d2FyZQpUaGUgd2hvbGUgcG9pbnQgb2YgUkN5MyBpcyB0byBjb25uZWN0IHdpdGggQ3l0b3NjYXBlLiBZb3Ugd2lsbCBuZWVkIHRvIGluc3RhbGwgYW5kIGxhdW5jaCBDeXRvc2NhcGU6IAoKKiBEb3dubG9hZCB0aGUgbGF0ZXN0IEN5dG9zY2FwZSBmcm9tIGh0dHA6Ly93d3cuY3l0b3NjYXBlLm9yZy9kb3dubG9hZC5waHAKKiBDb21wbGV0ZSBpbnN0YWxsYXRpb24gd2l6YXJkCiogTGF1bmNoIEN5dG9zY2FwZSAKCmBgYHtyfQpjeXRvc2NhcGVQaW5nKCkKYGBgCgojIEV4YW1wbGU6IFNwZWNpZXMgc3BlY2lmaWMgY29uc2lkZXJhdGlvbnMKV2hlbiBwbGFubmluZyB0byBpbXBvcnQgZGF0YSwgeW91IG5lZWQgdG8gY29uc2lkZXIgdGhlIGtleSBjb2x1bW5zIHlvdSBoYXZlIGluCnlvdXIgbmV0d29yayBkYXRhIGFuZCBpbiB5b3VyIHRhYmxlIGRhdGEuIEl0J3MgYWx3YXlzIHJlY29tbWVuZGVkIHRoYXQgeW91IHVzZQpwcm9wZXIgaWRlbnRpZmllcnMgYXMgeW91ciBrZXlzIChlLmcuLCBmcm9tIGRhdGFiYXNlcyBsaWtlIEVuc2VtYmwgYW5kIFVuaXByb3QtVHJFTUJMKS4KUmVseWluZyBvbiBjb252ZW50aW9uYWwgc3ltYm9scyBhbmQgbmFtZXMgaXMgbm90IHN0YW5kYXJkIGFuZCBlcnJvciBwcm9uZS4gCgpMZXQncyBzdGFydCB3aXRoIHRoZSBzYW1wbGUgbmV0d29yayBwcm92aWRlZCBieSBDeXRvc2NhcGUuCgoqKkNhdXRpb246IExvYWRpbmcgYSBzZXNzaW9uIGZpbGUgd2lsbCBkaXNjYXJkIHlvdXIgY3VycmVudCBzZXNzaW9uLiBTYXZlIGZpcnN0LAppZiB5b3UgaGF2ZSBuZXR3b3JrcyBvciBkYXRhIHlvdSB3YW50IHRvIGtlZXAuIFVzZSBzYXZlU2Vzc2lvbigncGF0aF90b19maWxlJykuKioKYGBge3J9Cm9wZW5TZXNzaW9uKCkgICNDbG9zZXMgY3VycmVudCBzZXNzaW9uICh3aXRob3V0IHNhdmluZykgYW5kIG9wZW5zIGEgc2FtcGxlIHNlc3Npb24gZmlsZQpgYGAKCllvdSBzaG91bGQgbm93IHNlZSBhIG5ldHdvcmsgd2l0aCBqdXN0IG92ZXIgMzAwIG5vZGVzLiBJZiB5b3UgbG9vayBhdCB0aGUgTm9kZSAKVGFibGUsIHlvdSdsbCBzZWUgdGhhdCB0aGVyZSBhcmUgcHJvcGVyIGlkZW50aWZpZXJzIGluIHRoZSAqbmFtZSogY29sdW1ucywgbGlrZQoiWURMMTk0VyIuIFRoZXNlIGFyZSB0aGUgRW5zZW1ibC1zdXBwb3J0ZWQgSURzIGZvciBZZWFzdC4KCiMjIFBlcmZvcm0gaWRlbnRpZmllciBtYXBwaW5nCllvdSBuZWVkIHRvIGtub3cgYSBmZXcgdGhpbmdzIGFib3V0IHlvdXIgbmV0d29yayBpbiBvcmRlciB0byBydW4gdGhpcyBmdW5jdGlvbiwgCmUuZy4sIHRoZSBzcGVjaWVzIGFuZCBzdGFydGluZyAob3Igc291cmNlKSBpZGVudGlmaWVyIHR5cGUuIFRoaXMgaXNuJ3QgdXN1YWxseQphIHByb2JsZW0sIGJ1dCAqKnRoaXMgZXhhbXBsZSBoaWdobGlnaHRzIGEgdW5pcXVlIGNhc2Ugd2hlcmUgdGhlIEVuc2VtYmwgSUQgdHlwZQpmb3IgYSBwYXJ0aWN1bGFyIHNwZWNpZXMgKGkuZS4sIFllYXN0KSBoYXMgYSBwYXJ0aWN1bGFyIGZvcm1hdCAoZS5nLiwgWURMMTk0VykqKiwgCnJhdGhlciB0aGFuIHRoZSBtb3JlIHR5cGljYWwgRU5TWFhYRzAwMDAxMjMyIGZvcm1hdC4KClNvLCB3aXRoIHRoaXMga25vd2xlZGdlLCB5b3UgY2FuIHJ1biB0aGUgZm9sbG93aW5nIGZ1bmN0aW9uOgoKYGBge3J9Cm1hcHBlZC5jb2xzIDwtIG1hcFRhYmxlQ29sdW1uKCduYW1lJywnWWVhc3QnLCdFbnNlbWJsJywnRW50cmV6IEdlbmUnKQpgYGAKCldlIGFyZSBhc2tpbmcgQ3l0b3NjYXBlIHRvIGxvb2sgaW4gdGhlICpuYW1lKiBjb2x1bW4gZm9yICpZZWFzdCBFbnNlbWJsKiBJRHMgYW5kCnRoZW4gcHJvdmlkZSBhIG5ldyBjb2x1bW5zIG9mIGNvcnJlc3BvbmRpbmcgKkVudHJleiBHZW5lKiBJRHMuIEFuZCBpZiB5b3UgbG9vawpiYWNrIGF0IHRoZSBOb2RlIFRhYmxlLCB5b3UnbGwgc2VlIHRoYXQgbmV3IGNvbHVtbiAoYWxsIHRoZSB3YXkgdG8gdGhlIHJpZ2h0KS4KVGhhdCdzIGl0IQoKVGhlIHJldHVybiB2YWx1ZSBpcyBhIGRhdGEgZnJhbWUgb2YgYWxsIHRoZSBtYXBwaW5ncyBiZXR3ZWVuIEVuc2VtYmwgYW5kIEVudHJleiBHZW5lCnRoYXQgd2VyZSBmb3VuZCBmb3IgeW91ciBuZXR3b3JrIGluIGNhc2UgeW91IHdhbnQgdGhvc2UgZGV0YWlsczoKYGBge3J9Cm1hcHBlZC5jb2xzWzE6MyxdICNmaXJzdCB0aHJlZSBlbnRyaWVzCmBgYAoKKk5vdGU6IHRoZSByb3cgbmFtZXMgb2YgdGhlIHJldHVybiBkYXRhIGZyYW1lIGFyZSB0aGUgbm9kZSBTVUlEcyBmcm9tIEN5dG9zY2FwZS4KVGhlc2UgYXJlIGhhbmR5IGlmIHlvdSB3YW50IHRvIGxvYWQgdGhlIG1hcHBpbmdzIHlvdXJzZWxmIChzZWUgbGFzdCBleGFtcGxlKS4qCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvZGF0YS9pbWcvaWRlbnRpZmllci1tYXBwaW5nMS5wbmcpe3dpZHRoPTYwJX0KPC9jZW50ZXI+CgojIEV4YW1wbGU6IEZyb20gcHJvdGVpbnMgdG8gZ2VuZXMKRm9yIHRoaXMgbmV4dCBleGFtcGxlLCB5b3UnbGwgbmVlZCB0aGUgU1RSSU5HIGFwcCB0byBhY2Nlc3MgdGhlIFNUUklORyBkYXRhYmFzZQpmcm9tIHdpdGhpbiBDeXRvc2NhcGU6CiogSW5zdGFsbCB0aGUgU1RSSU5HIGFwcCBmcm9tIGh0dHBzOi8vYXBwcy5jeXRvc2NhcGUub3JnL2FwcHMvc3RyaW5nYXBwCgpgYGB7cn0KI2F2YWlsYWJsZSBpbiBDeXRvc2NhcGUgMy43LjAgYW5kIGFib3ZlCmluc3RhbGxBcHAoJ1NUUklOR2FwcCcpICAKYGBgCgpOb3cgd2UgY2FuIGltcG9ydCBwcm90ZWluIGludGVyYWN0aW9uIG5ldHdvcmtzIHdpdGggYSB0b24gb2YgYW5ub3RhdGlvbnMgZnJvbQp0aGUgU1RSSU5HIGRhdGFiYXNlIHdpdGggYSBzaW1wbGUgY29tbWFuZHNHRVQgZnVuY3Rpb24sIGxpa2UgdGhpczoKYGBge3J9CnN0cmluZy5jbWQgPSAnc3RyaW5nIGRpc2Vhc2UgcXVlcnkgZGlzZWFzZT0iYnJlYXN0IGNhbmNlciIgY3V0b2ZmPTAuOSBzcGVjaWVzPSJIb21vIHNhcGllbnMiIGxpbWl0PTE1MCcKY29tbWFuZHNHRVQoc3RyaW5nLmNtZCkKCiMgZm9yIG1vcmUgaW5mb3JtYXRpb24gb24gc3RyaW5nIGNvbW1hbmRzOgojIGNvbW1hbmRzSGVscCgnc3RyaW5nJykKIyBjb21tYW5kc0hlbHAoJ3N0cmluZyBkaXNlYXNlIHF1ZXJ5JykKYGBgCgpDaGVjayBvdXQgdGhlIE5vZGUgVGFibGUgYW5kIHlvdSdsbCBzZWUgZGlzcGxheSBuYW1lcyBhbmQgaWRlbnRpZmllcnMuIEluIHBhcnRpY3VsYXIsCnRoZSAqY2Fub25pY2FsIG5hbWUqIGNvbHVtbiBhcHBlYXJzIHRvIGhvbGQgVW5pcHJvdC1UckVNQkwgSURzLiBOaWNlLCB3ZSBjYW4gdXNlIHRoYXQhCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvZGF0YS9pbWcvaWRlbnRpZmllci1tYXBwaW5nMi5wbmcpe3dpZHRoPTYwJX0KPC9jZW50ZXI+CgojIyBQZXJmb3JtIGlkZW50aWZpZXIgbWFwcGluZwpTYXkgd2UgaGF2ZSBhIGRhdGFzZXQga2V5ZWQgYnkgRW5zZW1ibCBnZW5lIGlkZW50aWZpZXJzLiBXZWxsLCB0aGVuIHdlIHdvdWxkIHdhbnQKdG8gcGVyZm9ybSB0aGlzIG1hcHBpbmc6CmBgYHtyfQptYXBwZWQuY29scyA8LSBtYXBUYWJsZUNvbHVtbignc3RyaW5nZGI6OmNhbm9uaWNhbCBuYW1lJywnSHVtYW4nLCdVbmlwcm90LVRyRU1CTCcsJ0Vuc2VtYmwnKQpgYGAKClNjcm9sbCBhbGwgdGhlIHdheSB0byB0aGUgcmlnaHQgaW4gdGhlIE5vZGUgVGFibGUgYW5kIHlvdSdsbCBzZWUgYSBuZXcgY29sdW1uIHdpdGgKRW5zZW1ibCBJRHMuICoqVGhpcyBleGFtcGxlIGhpZ2hsaWdodHMgYSB1c2VmdWwgdHJhbnNsYXRpb24gZnJvbSBwcm90ZWluIHRvIGdlbmUKaWRlbnRpZmllcnMgKG9yIHZpY2UgdmVyc2EpLCBidXQgaXMgYWxzbyBhIGNhdXRpb24gdG8gYmUgYXdhcmUgb2YgdGhlIGFzc3VtcHRpb25zCmludm9sdmVkIHdoZW4gbWFraW5nIHRoaXMgdHJhbnNsYXRpb24uKiogRm9yIGV4YW1wbGUsIGEgdHlwaWNhbCBnZW5lIGVuY29kZXMgZm9yIG1hbnkgCnByb3RlaW5zLCBzbyB5b3UgbWF5IGhhdmUgbWFueS10by1vbmUgbWFwcGluZ3MgaW4geW91ciByZXN1bHRzLgoKIyBFeGFtcGxlOiBNaXhlZCBpZGVudGlmaWVycwpGcm9tIHRpbWUgdG8gdGltZSwgeW91J2xsIGNvbWUgYWNyb3NzIGEgY2FzZSB3aGVyZSB0aGUgaWRlbnRpZmllcnMgaW4geW91ciBuZXR3b3JrCmFyZSBvZiBtaXhlZCB0eXBlcy4gVGhpcyBpcyBhIHJhcmUgc2NlbmFyaW8sIGJ1dCBoZXJlIGlzIG9uZSBhcHByb2FjaCB0byBzb2x2aW5nCml0LgoKRmlyc3QsIHlvdSdsbCBuZWVkIHRoZSBXaWtpUGF0aHdheXMgYXBwIHRvIGFjY2VzcyB0aGUgV2lraVBhdGh3YXlzCmRhdGFiYXNlLiBUaGUgcGF0aHdheXMgaW4gV2lraVBhdGh3YXlzIGFyZSBjdXJhdGVkIGJ5IGEgY29tbXVuaXR5IG9mIGludGVyZXN0ZWQKcmVzZWFyY2hlcnMgYW5kIGNpdGl6ZW4gc2NpZW50aXN0cy4gQXMgc3VjaCwgdGhlcmUgYXJlIHRpbWVzIHdoZXJlIGF1dGhvcnMgbWlnaHQKdXNlIGRpZmZlcmVudCBzb3VyY2VzIG9mIGlkZW50aWZpZXJzLiBUaGV5IGFyZSB2YWxpZCBJRHMsIGp1c3Qgbm90IGFsbCBmcm9tIHRoZSBzYW1lCnNvdXJjZS4gRnV0dXJlIHZlcnNpb25zIG9mIHRoZSBXaWtpUGF0aHdheXMgYXBwIHdpbGwgcHJvdmlkZSBwcmUtbWFwcGVkIGNvbHVtbnMKdG8gYSBzaW5nbGUgSUQgdHlwZS4gQnV0IGluIHRoZSBtZWFudGltZSAoYW5kIHJlbGV2YW50IHRvIG90aGVyIHVzZSBjYXNlcyksICoqdGhpcwpleGFtcGxlIGhpZ2hsaWdodHMgaG93IHRvIGhhbmRsZSBhIHNvdXJjZSBvZiBtaXhlZCBpZGVudGlmaWVyIHR5cGVzLioqCgoqIEluc3RhbGwgdGhlIFdpa2lQYXRod2F5cyBhcHAgZnJvbSBodHRwczovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL3dpa2lwYXRod2F5cwoKYGBge3J9CiNhdmFpbGFibGUgaW4gQ3l0b3NjYXBlIDMuNy4wIGFuZCBhYm92ZQppbnN0YWxsQXBwKCdXaWtpUGF0aHdheXMnKSAgCmBgYAoKCk5vdyB3ZSBjYW4gaW1wb3J0IGFuIEFwb3B0b3NpcyBQYXRod2F5IGZyb20gV2lraVBhdGh3YXlzLiBFaXRoZXIgZnJvbSB0aGUgd2ViCnNpdGUgKGh0dHBzOi8vd2lraXBhdGh3YXlzLm9yZyksIG9yIGZyb20gdGhlIE5ldHdvcmsgU2VhcmNoIFRvb2wgaW4gQ3l0b3NjYXBlCkdVSSBvciBmcm9tIHRoZSByV2lraVBhdGh3YXlzIHBhY2thZ2UsIHdlIGNvdWxkIGlkZW50aWZ5IHRoZSBwYXRod2F5IGFzIFdQMjU0LgpgYGB7cn0Kd3AuY21kID0gJ3dpa2lwYXRod2F5cyBpbXBvcnQtYXMtcGF0aHdheSBpZD0iV1AyNTQiJwpjb21tYW5kc0dFVCh3cC5jbWQpCgojIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHdpa2lwYXRod2F5cyBjb21tYW5kczoKIyBjb21tYW5kc0hlbHAoJ3dpa2lwYXRod2F5cycpCiMgY29tbWFuZHNIZWxwKCd3aWtpcGF0aHdheXMgaW1wb3J0LWFzLXBhdGh3YXknKQoKYGBgCgpUYWtlIGxvb2sgaW4gdGhlICpYcmVmSWQqIGNvbHVtbiBhbmQgeW91J2xsIHNlZSBhIG1peCBvZiBpZGVudGlmaWVyIHR5cGVzLiBUaGUKbmV4dCBjb2x1bW4gb3ZlciwgKlhyZWZEYXRhc291cmNlKiwgY29udmVuaWVudGx5IG5hbWVzIGVhY2ggdHlwZSdzIHNvdXJjZS4gSWdub3JpbmcKdGhlIG1ldGFib2xpdGVzIGZvciB0aGlzIGV4YW1wbGUsIHdlIGp1c3QgaGF2ZSBhIG1peCBvZiBFbnNlbWJsIGFuZCBFbnRyZXogR2VuZSAKdG8gZGVhbCB3aXRoLgoKIyMgUGVyZm9ybSBpZGVudGlmaWVyIG1hcHBpbmcKU2F5IHdlIHdhbnQgYSBjb2x1bW4gd2l0aCBvbmx5IEVuc2VtYmwgSURzLiBUaGUgZWFzaWVzdCBhcHByb2FjaCBpcyB0byBzaW1wbHkgCm92ZXJ3cml0ZSBhbGwgdGhlIG5vbi1FbnNlbWJsIElEcywgaS5lLiwgaW4gdGhpcyBjYXNlLCBFbnRyZXogR2VuZSBJRHMuIExldCdzIApjb2xsZWN0IHRoZSBtYXBwaW5ncyBmaXJzdDoKCmBgYHtyfQptYXBwZWQuY29scyA8LSBtYXBUYWJsZUNvbHVtbignWHJlZklkJywnSHVtYW4nLCdFbnRyZXogR2VuZScsJ0Vuc2VtYmwnKQpgYGAKCk5leHQsIHdlIHdhbnQgdG8gcmVtb3ZlIHRoZSA8TkE+IHZhbHVlcyBmcm9tIHRoZSAqRW5zZW1ibCogY29sdW1uIGluIG91ciByZXN1bHRpbmcKbWFwcGVkLmNvbHMgZGF0YSBmcmFtZS4gV2UnbGwgYWxzbyByZW1vdmUgdGhlIG9yaWdpbmFsIHNvdXJjZSBjb2x1bW5zICh0byBhdm9pZApjb25mdXNpb24pIGFuZCByZW5hbWUgb3VyICpFbnNlbWJsKiBjb2x1bW4gdG8gKlhyZWZJZCogdG8gcHJlcGFyZSB0byBvdmVyd3JpdGUuIApUaGVuIHdlJ2xsIGxvYWQgdGhhdCBpbnRvIEN5dG9zYWNwZToKCmBgYHtyfQpvbmx5Lm1hcHBlZC5jb2xzIDwtIG1hcHBlZC5jb2xzW2NvbXBsZXRlLmNhc2VzKG1hcHBlZC5jb2xzKSwgJ0Vuc2VtYmwnLCBkcm9wPUZBTFNFXQpjb2xuYW1lcyhvbmx5Lm1hcHBlZC5jb2xzKSA8LSAnWHJlZklkJwpsb2FkVGFibGVEYXRhKG9ubHkubWFwcGVkLmNvbHMsdGFibGUua2V5LmNvbHVtbiA9ICdTVUlEJykKYGBgCgpEb25lISBTZWUgdGhlIHVwZGF0ZWQgKlhyZWZJZCogY29sdW1uIGluIEN5dG9zY2FwZSB3aXRoIGFsbCBFbnNlbWJsIElEcy4gCgoqTm90ZTogeW91J2xsIHdhbnQgdG8gZWl0aGVyIHVwZGF0ZSB0aGUgKlhyZWZEYXRhc291cmNlKiBjb2x1bW4gYXMgd2VsbCBvciBzaW1wbHkgbWFrZSAKYSBub3RlIHRvIGlnbm9yZSBpdCBhdCB0aGlzIHBvaW50LioKCiMgTW9yZSBhZHZhbmNlZCBjYXNlcwpUaGlzIGlkZW50aWZpZXIgbWFwcGluZyBmdW5jdGlvbiBpcyBpbnRlbmRlZCB0byBoYW5kbGUgdGhlIG1ham9yaXR5IG9mIGNvbW1vbgpJRCBtYXBwaW5nIHByb2JsZW1zLiBJdCBoYXMgbGltaXRhdGlvbiwgaG93ZXZlci4gCmBgYHtyfQo/bWFwVGFibGVDb2x1bW4KYGBgCgpJZiB5b3UgbmVlZCBhbiBJRCBtYXBwaW5nIHNvbHV0aW9uIGZvciBzcGVjaWVzIG9yIElEIHR5cGVzIG5vdCBjb3ZlcmVkIGJ5IHRoaXMgCnRvb2wsIG9yIGlmIHlvdSB3YW50IHRvIGNvbm5lY3QgdG8gYWx0ZXJuYXRpdmUgc291cmNlcyBvZiBtYXBwaW5ncywgdGhlbiBjaGVjawpvdXQgdGhlIEJyaWRnZURiIGFwcDogaHR0cDovL2FwcHMuY3l0b3NjYXBlLm9yZy9hcHBzL2JyaWRnZWRiLiAKCmBgYHtyfQojYXZhaWxhYmxlIGluIEN5dG9zY2FwZSAzLjcuMCBhbmQgYWJvdmUKaW5zdGFsbEFwcCgnQnJpZGdlRGInKSAgCmBgYAoKQW5kIHRoZW4gYnJvd3NlIHRoZSBhdmFpbGFibGUgZnVuY3Rpb24gd2l0aCAqY29tbWFuZHNIZWxwKCdicmlkZ2VkYicpKg==