The R markdown is available from the pulldown menu for Code at the upper-right, choose “Download Rmd”, or download the Rmd from GitHub.


Cytoscape is a well-known bioinformatics tool for displaying and exploring biological networks. R is a powerful programming language and environment for statistical and exploratory data analysis. RCy3 uses CyREST to communicate between R and Cytoscape, allowing any graphs (e.g., igraph, graphNEL or dataframes) to be viewed, explored and manipulated with the Cytoscape point-and-click visual interface. Thus, via RCy3, these two quite different, quite useful bioinformatics software environments are connected, mutually enhancing each other, providing new possibilities for exploring biological data.

Installation

if(!"RCy3" %in% installed.packages()){
    install.packages("BiocManager")
    BiocManager::install("RCy3")
}
library(RCy3)

Prerequisites

In addition to this package (RCy3), you will need:

Getting started

First, launch Cytoscape and keep it running whenever using RCy3. Confirm that you have everything installed and running:

    cytoscapePing ()
    cytoscapeVersionInfo ()

My first network

Let’s create a Cytoscape network from some basic R objects

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), # numeric
           stringsAsFactors=FALSE)

createNetworkFromDataFrames(nodes,edges, title="my first network", collection="DataFrame Example")

Switch styles

Check out the marquee style!

setVisualStyle('Marquee')

My own style

Create your own style with node attribute fill mappings and some defaults

style.name = "myStyle"
defaults <- list(NODE_SHAPE="diamond",
                 NODE_SIZE=30,
                 EDGE_TRANSPARENCY=120,
                 NODE_LABEL_POSITION="W,E,c,0.00,0.00")
nodeLabels <- mapVisualProperty('node label','id','p')
nodeFills <- mapVisualProperty('node fill color','group','d',c("A","B"), c("#FF9900","#66AAAA"))
arrowShapes <- mapVisualProperty('Edge Target Arrow Shape','interaction','d',c("activates","inhibits","interacts"),c("Arrow","T","None"))
edgeWidth <- mapVisualProperty('edge width','weight','p')

createVisualStyle(style.name, defaults, list(nodeLabels,nodeFills,arrowShapes,edgeWidth))
setVisualStyle(style.name)

Pro-tip: if you want to set NODE_WIDTH and NODE_HEIGHT independently, you also need to unlock the node dimensions with…

lockNodeDimensions(FALSE, style.name)

Bioconductor graph example

Alternatively, you might want to start from a Bioconductor graphNEL object. Here we create a 4-node graph in R, send it to Cytoscape for display and layout. For the sake of simplicity, no node attributes and no visual styles are included; those topics are covered in subsequent steps.

    g = new ('graphNEL', edgemode='directed')
    g = graph::addNode ('A', g)
    g = graph::addNode ('D', g)
    g = graph::addNode ('C', g, edges = list('D'))
    g = graph::addNode ('B', g, edges = list(c('A','D','C')))
    createNetworkFromGraph (g, title='simple network', collection='GraphNEL Example')

You should now have the structure of this 4-node graph with a basic, default style. Fortunately, Cytoscape has some built-in rendering rules in which (and unless instructed otherwise) nodes and edges are rendered and a default (user-preference) layout algorithm is applied.

Add node attributes

We often know quite a lot about the nodes and edges in our graphs. By conveying this information visually, the graph will be easier to explore. For instance, we may know that protein A phosphorylates protein B, that A is a kinase and B a transcription factor, and that their mRNA expression (compared to a control) is a log2 fold change of 1.8 and 3.2 respectively. One of the core features of Cytoscape is visual styles, which allow you to specify how data values (e.g., kinase',transcription factor’; expression ratios) should be conveyed in the visual properties of the graph (e.g., node shape, node color or size).

We continue with the simple 4-node graph, adding two kinds data values (moleculeType' andlog2fc’). The easiest way to do this is via data.frames. However, you can also include attributes together with the original graph models as Bioconductor graphs, igraphs or data.frames and then use the provided create functions to create and load in a single step (see createNetworkFromGraph, createNetworkFromIgraph and createNetworkFromDataFrames functions). Check out the other vignettes for more exampls.

    df <- data.frame (moleculeType=c('kinase','TF','cytokine','cytokine'),
                     log2fc=c(1.8,3.0,-1.2,-2.5),
                     row.names = c('A','B','C','D'), # row.names = node names
                     stringsAsFactors = FALSE)       # important when loading strings!
    loadTableData (df)

Note that adding the attributes does not in itself cause the appearance of the graph to change. Such a change requires that you specify and apply visual style mappings, which will be explained in the next section. You can, however, examine these attributes in Cytoscape, using Cytoscape’s the Data Panel to display data values associated with selected nodes immediately below the Cytoscape window.

Modifying the display: defaults and mappings

RCy3 provides an easy way to not only change the default styles, but more interestingly, RCy3 also provides easy access to mapping your data to visual styles, e.g., allowing the size, shape and color of nodes and edges to be determined by the data you have associated with those nodes and edges.

First, let’s change the the defaults.

    setNodeShapeDefault ('OCTAGON')
    setNodeColorDefault ('#AAFF88')
    setNodeSizeDefault  (60)
    setNodeFontSizeDefault (30)

Now we will add some visual mappings. Let’s map `moleculeType’ to node shapes. First, we can see which shapes are available in Cytoscape, then we can define the mapping with paired lists.

    getNodeShapes ()   # diamond, ellipse, trapezoid, triangle, etc.
    column <- 'moleculeType'
    values <- c ('kinase',  'TF','cytokine')
    shapes <- c ('DIAMOND', 'TRIANGLE', 'RECTANGLE')
    setNodeShapeMapping (column, values, shapes)

The node shape mapping is an example of a discrete mapping, where a style is defined for each, discrete value. This is useful for categorical data (like type) where there is only a limited set of possible values. This is in contast to the other two other types of mappings: continuous and passthrough. In the case of expression values, for example, we will want to use continuous mapping (e.g., to node color), defining a small set of control points, rather than an explicit color for each possible data value. Cytoscape will simply interpolate between the control points to provide a gradient of colors. Let’s try that one now

    column <- 'log2fc'
    control.points <- c (-3.0, 0.0, 3.0)
    colors <-  c ('#5588DD', '#FFFFFF', '#DD8855')
    setNodeColorMapping (column, control.points, colors)

Note that there are three colors and three control points. However, you can also specify two additional colors beyond the number of control points if you want to set extreme (or out-of-bounds) colors for values less than or greater than your control points.

    control.points <- c (-2.0, 0.0, 2.0)
    colors <-  c ('#2255CC', '#5588DD', '#FFFFFF', '#DD8855','#CC5522')
    setNodeColorMapping (column, control.points, colors)

Now, add a node size rule, using log2fc again as controlling node values.

    control.points = c (-3.0, 2.0, 3.0)
    sizes     = c (20, 80, 90)
    setNodeSizeMapping (column, control.points, sizes)

If you recall the third type of mapping, passthrough, we can see it already working in our current network example. The node labels! By default, the name column is mapped to the node label property using passthrough logic: the value is passed directly to the style property.

Selecting nodes

Let us now try selecting nodes in Cytoscape from R. Select the C node by name:

    selectNodes ('C','name')
    getSelectedNodes ()

Now we wish to extend the selected nodes to include the first neighbors of the already-selected node B. This is a common operation: for instance, after selecting one or more nodes based on experimental data or annotation, you may want to explore these in the context of interaction partners (in a protein-protein network) or in relation to upstream and downstream partners in a signaling or metabolic network. Type:

    selectFirstNeighbors ()

You will see that three nodes are now selected. Get their names back to R as a list:

    node.names <- getSelectedNodes ()

And, finally, deselection works as you’d expect by means of a general clearSelection function:

    clearSelection()
    ?clearSelection

Saving and export

Session files save everything. As with most project software, we recommend saving often!

    saveSession('vignette_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.

    full.path=paste(getwd(),'vignette_image',sep='/')
    exportImage(full.path, 'PNG', zoom=200) #.png scaled by 200%
    exportImage(full.path, 'PDF') #.pdf
    ?exportImage

Browse available functions, commands and arguments

RCy3 functions

help(package=RCy3)
Category Description Examples
Apps Inspecting and managing apps for Cytoscape. installApp() disableApp() getInstalledApps()
Collections Getting information about network collections. getCollectionList() getCollectionNetworks()
Commands Constructing any arbitrary CyREST API or Commands API method via standard GET, PUT, POST and DELETE protocols. cyrestPOST() commandsPOST() cyrestAPI() commandsAPI()
CyNDEx Communicating with NDEx from within Cytoscape. importNetworkFromNDEx() exportNetworkToNDEx()
Cytoscape System Checking Cytoscape System information, including versions and memory usage. cytoscapePing() cytoscapeVersionInfo()
Groups Working with groups in Cytoscape. createGroup() collapseGroup()
Layouts Performing layouts in addition to getting and setting layout properties. layoutNetwork() getLayoutNames()
Networks Functions for network management and retrieving information on networks, nodes and edges. createNetworkFrom…() create…FromNetwork() getNetworkSuid(), exportNetwork() getAllNodes() getAllEdges()
Network Selection Working with selection of nodes and edges in networks. selectNodes() invertNodeSelection() selectFirstNeighbors()
Network Views Performing view operations in addition to getting and setting view properties. getCurrentView() fitContent() exportImage() toggleGraphicsDetails()
Session Managing Cytoscape sessions, including save, open and close. openSession() saveSession() closeSession()
Style Bypasses Setting and clearing bypass values for visual properties. setNodeColorBypass() setEdgeLineStyleBypass() hideNodes()
Style Defaults Getting and setting default values for visual properties. setNodeShapeDefault() setEdgeLineWidthDefault()
Style Dependencies Getting and setting style dependencies. lockNodeDimensions()
Style Mappings Defining mappings between table column values and visual properties. setNodeSizeMapping() setEdgeColorMapping()
Styles Managing styles and retrieving general lists of properties relevant to multiple style modes. createVisualStyle() setVisualStyle() exportVisualStyles() getArrowShapes()
Style Values Retrieving current values for visual properties. getNodeWidth() getEdgeColor() getNetworkZoom()
Tables Managing table columns and table column functions, like map and rename, as well as loading and extracting table data in Cytoscape. getTableColumns() renameTableColumn() loadTableData() mapTableColumn()
Tools Functions related to actions found in the Tools Menu in Cytoscape. cybrowserDialog() diffusionBasic()
User Interface Functions affecting the Cytoscape user interface, such as panel management. hidePanel() floatPanel() dockPanel()

Open swagger docs for live instances of CyREST API and Commands API:

cyrestAPI()  # CyREST API
commandsAPI()  # Commands API

List available commands and arguments in R. Use “help” to list top level:

commandsHelp("help")  

List network commands. Note that “help” is optional:

commandsHelp("help network")  

List arguments for the network select command:

commandsHelp("help network select") 

That covers the basics of network manipulation. Check out the other vignettes for additional amd more complex examples. And when you are ready to work with some real data, check out the other basic and advanced R tutorials, https://github.com/cytoscape/cytoscape-automation/tree/master/for-scripters/R.

More examples

Don’t forget to check out the other vignettes in this package:

browseVignettes("RCy3")

In addition, the Cytoscape team is collecting scripts from the community in a public GitHub repository at https://github.com/cytoscape/cytoscape-automation/tree/master/for-scripters/R.

Development

The RCy3 project code and documentation is maintained at GitHub: https://github.com/cytoscape/RCy3. All bugs and feature requests are tracked as Issues, https://github.com/cytoscape/RCy3/issues.

Credits

  • Paul Shannon’s generous advice and mentorship was very important for transforming this package from using XMLRPC and CytoscapeRPC to using CyREST.
  • David Otasek, Keiichiro Ono and Barry Demchak kindly provided CyREST as well as help and support for new functionalities and changes.
  • Mark Grimes and Ruth Isserlin kindly provided helpful user feedback.
  • Julia Gustavsen generously developed various use cases/examples for using RCy3 with biological data during GSOC 2016, https://github.com/jooolia/gsoc_Rcy3_vignettes/blob/master/blog_post_drafts/final_work_submission.md.
  • Tanja Muetze provided many years of development, design, maintenance and documentation for the RCy3 project.
  • All contributors, new and old, are dynamically acknowledged in our Contributor Graph, https://github.com/cytoscape/RCy3/graphs/contributors

References

  1. Shannon P, Markiel A, Ozier O, Baliga NS, Wang JT, Ramage D, Amin N, Schwikowski B, Ideker T. 2003. Cytoscape: a software environment for integrated models of biomolecular interaction networks. Genome Res. Nov;13(11):2498-504
  2. Huber W, Carey VJ, Long L, Falcon S, Gentleman R. 2007. Graphs in molecular biology. BMC Bioinformatics. 2007 Sep 27;8.
  3. Ono K, Muetze T, Kolishovski G, Shannon P, Demchak, B. CyREST: Turbocharging Cytoscape Access for External Tools via a RESTful API [version 1; referees: 2 approved]. F1000Research 2015, 4:478.
LS0tCnRpdGxlOiAiT3ZlcnZpZXcgb2YgUkN5MyIKYXV0aG9yOiAiYnkgQWxleGFuZGVyIFBpY28sIFRhbmphIE11ZXR6ZSwgR2VvcmdpIEtvbGlzaG92c2tpLCBQYXVsIFNoYW5ub24iCnBhY2thZ2U6IFJDeTMKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogIm5vbmUiCiMgIHBkZl9kb2N1bWVudDoKIyAgICB0b2M6IHRydWUgICAgCnZpZ25ldHRlOiA+CiAgJVxWaWduZXR0ZUluZGV4RW50cnl7MDEuIE92ZXJ2aWV3IG9mIFJDeTMgfjI1IG1pbn0KICAlXFZpZ25ldHRlRW5naW5le2tuaXRyOjpybWFya2Rvd259CiAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KYGBge3IsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGV2YWw9RkFMU0UKKQpgYGAKKlRoZSBSIG1hcmtkb3duIGlzIGF2YWlsYWJsZSBmcm9tIHRoZSBwdWxsZG93biBtZW51IGZvciogQ29kZSAqYXQgdGhlIHVwcGVyLXJpZ2h0LCBjaG9vc2UgIkRvd25sb2FkIFJtZCIsIG9yIFtkb3dubG9hZCB0aGUgUm1kIGZyb20gR2l0SHViXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vY3l0b3NjYXBlL2N5dG9zY2FwZS1hdXRvbWF0aW9uL21hc3Rlci9mb3Itc2NyaXB0ZXJzL1Ivbm90ZWJvb2tzL092ZXJ2aWV3LW9mLVJDeTMuUm1kKS4qCgo8aHIgLz4KCipDeXRvc2NhcGUqIGlzIGEgd2VsbC1rbm93biBiaW9pbmZvcm1hdGljcyB0b29sIGZvciBkaXNwbGF5aW5nIGFuZCBleHBsb3JpbmcgYmlvbG9naWNhbCBuZXR3b3Jrcy4KKipSKiogaXMgYSBwb3dlcmZ1bCBwcm9ncmFtbWluZyBsYW5ndWFnZSBhbmQgZW52aXJvbm1lbnQgZm9yIHN0YXRpc3RpY2FsIGFuZCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLgoqUkN5MyogdXNlcyBDeVJFU1QgdG8gY29tbXVuaWNhdGUgYmV0d2VlbiAqKlIqKiBhbmQgQ3l0b3NjYXBlLCBhbGxvd2luZyBhbnkgZ3JhcGhzIChlLmcuLCBpZ3JhcGgsIGdyYXBoTkVMIG9yIGRhdGFmcmFtZXMpIHRvIGJlIHZpZXdlZCwgZXhwbG9yZWQgYW5kIG1hbmlwdWxhdGVkIHdpdGggdGhlIEN5dG9zY2FwZSBwb2ludC1hbmQtY2xpY2sgdmlzdWFsIGludGVyZmFjZS4gVGh1cywgdmlhIFJDeTMsIHRoZXNlIHR3byBxdWl0ZSBkaWZmZXJlbnQsIHF1aXRlIHVzZWZ1bCBiaW9pbmZvcm1hdGljcyBzb2Z0d2FyZSBlbnZpcm9ubWVudHMgYXJlIGNvbm5lY3RlZCwgbXV0dWFsbHkgZW5oYW5jaW5nIGVhY2ggb3RoZXIsIHByb3ZpZGluZyBuZXcgcG9zc2liaWxpdGllcyBmb3IgZXhwbG9yaW5nIGJpb2xvZ2ljYWwgZGF0YS4KCiMgSW5zdGFsbGF0aW9uCmBgYHtyfQppZighIlJDeTMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpewogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlJDeTMiKQp9CmxpYnJhcnkoUkN5MykKYGBgCgojIFByZXJlcXVpc2l0ZXMKSW4gYWRkaXRpb24gdG8gdGhpcyBwYWNrYWdlIChSQ3kzKSwgeW91IHdpbGwgbmVlZDoKCiAgKiAqKkN5dG9zY2FwZSAzLjYuMSoqIG9yIGdyZWF0ZXIsIHdoaWNoIGNhbiBiZSBkb3dubG9hZGVkIGZyb20gaHR0cDovL3d3dy5jeXRvc2NhcGUub3JnL2Rvd25sb2FkLnBocC4gU2ltcGx5IGZvbGxvdyB0aGUgaW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucyBvbiBzY3JlZW4uCgojIEdldHRpbmcgc3RhcnRlZAoKRmlyc3QsIGxhdW5jaCBDeXRvc2NhcGUgYW5kIGtlZXAgaXQgcnVubmluZyB3aGVuZXZlciB1c2luZyBSQ3kzLiBDb25maXJtIHRoYXQKeW91IGhhdmUgZXZlcnl0aGluZyBpbnN0YWxsZWQgYW5kIHJ1bm5pbmc6CgpgYGB7cn0KICAgIGN5dG9zY2FwZVBpbmcgKCkKICAgIGN5dG9zY2FwZVZlcnNpb25JbmZvICgpCmBgYAoKIyBNeSBmaXJzdCBuZXR3b3JrCkxldCdzIGNyZWF0ZSBhIEN5dG9zY2FwZSBuZXR3b3JrIGZyb20gc29tZSBiYXNpYyBSIG9iamVjdHMKYGBge3J9Cm5vZGVzIDwtIGRhdGEuZnJhbWUoaWQ9Yygibm9kZSAwIiwibm9kZSAxIiwibm9kZSAyIiwibm9kZSAzIiksCiAgICAgICAgICAgZ3JvdXA9YygiQSIsIkEiLCJCIiwiQiIpLCAjIGNhdGVnb3JpY2FsIHN0cmluZ3MKICAgICAgICAgICBzY29yZT1hcy5pbnRlZ2VyKGMoMjAsMTAsMTUsNSkpLCAjIGludGVnZXJzCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKZWRnZXMgPC0gZGF0YS5mcmFtZShzb3VyY2U9Yygibm9kZSAwIiwibm9kZSAwIiwibm9kZSAwIiwibm9kZSAyIiksCiAgICAgICAgICAgdGFyZ2V0PWMoIm5vZGUgMSIsIm5vZGUgMiIsIm5vZGUgMyIsIm5vZGUgMyIpLAogICAgICAgICAgIGludGVyYWN0aW9uPWMoImluaGliaXRzIiwiaW50ZXJhY3RzIiwiYWN0aXZhdGVzIiwiaW50ZXJhY3RzIiksICAjIG9wdGlvbmFsCiAgICAgICAgICAgd2VpZ2h0PWMoNS4xLDMuMCw1LjIsOS45KSwgIyBudW1lcmljCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKCmNyZWF0ZU5ldHdvcmtGcm9tRGF0YUZyYW1lcyhub2RlcyxlZGdlcywgdGl0bGU9Im15IGZpcnN0IG5ldHdvcmsiLCBjb2xsZWN0aW9uPSJEYXRhRnJhbWUgRXhhbXBsZSIpCmBgYAoKIyMgU3dpdGNoIHN0eWxlcwpDaGVjayBvdXQgdGhlIG1hcnF1ZWUgc3R5bGUhCmBgYHtyfQpzZXRWaXN1YWxTdHlsZSgnTWFycXVlZScpCmBgYAoKIyMgTXkgb3duIHN0eWxlCkNyZWF0ZSB5b3VyIG93biBzdHlsZSB3aXRoIG5vZGUgYXR0cmlidXRlIGZpbGwgbWFwcGluZ3MgYW5kIHNvbWUgZGVmYXVsdHMKYGBge3J9CnN0eWxlLm5hbWUgPSAibXlTdHlsZSIKZGVmYXVsdHMgPC0gbGlzdChOT0RFX1NIQVBFPSJkaWFtb25kIiwKICAgICAgICAgICAgICAgICBOT0RFX1NJWkU9MzAsCiAgICAgICAgICAgICAgICAgRURHRV9UUkFOU1BBUkVOQ1k9MTIwLAogICAgICAgICAgICAgICAgIE5PREVfTEFCRUxfUE9TSVRJT049IlcsRSxjLDAuMDAsMC4wMCIpCm5vZGVMYWJlbHMgPC0gbWFwVmlzdWFsUHJvcGVydHkoJ25vZGUgbGFiZWwnLCdpZCcsJ3AnKQpub2RlRmlsbHMgPC0gbWFwVmlzdWFsUHJvcGVydHkoJ25vZGUgZmlsbCBjb2xvcicsJ2dyb3VwJywnZCcsYygiQSIsIkIiKSwgYygiI0ZGOTkwMCIsIiM2NkFBQUEiKSkKYXJyb3dTaGFwZXMgPC0gbWFwVmlzdWFsUHJvcGVydHkoJ0VkZ2UgVGFyZ2V0IEFycm93IFNoYXBlJywnaW50ZXJhY3Rpb24nLCdkJyxjKCJhY3RpdmF0ZXMiLCJpbmhpYml0cyIsImludGVyYWN0cyIpLGMoIkFycm93IiwiVCIsIk5vbmUiKSkKZWRnZVdpZHRoIDwtIG1hcFZpc3VhbFByb3BlcnR5KCdlZGdlIHdpZHRoJywnd2VpZ2h0JywncCcpCgpjcmVhdGVWaXN1YWxTdHlsZShzdHlsZS5uYW1lLCBkZWZhdWx0cywgbGlzdChub2RlTGFiZWxzLG5vZGVGaWxscyxhcnJvd1NoYXBlcyxlZGdlV2lkdGgpKQpzZXRWaXN1YWxTdHlsZShzdHlsZS5uYW1lKQpgYGAKCjxjZW50ZXI+CiFbXShodHRwczovL2N5dG9zY2FwZS5naXRodWIuaW8vY3l0b3NjYXBlLWF1dG9tYXRpb24vZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9kYXRhL2ltZy9NeUZpcnN0TmV0d29yay5wbmcpe3dpZHRoPTYwJX0KPC9jZW50ZXI+CgoqUHJvLXRpcDogaWYgeW91IHdhbnQgdG8gc2V0IE5PREVfV0lEVEggYW5kIE5PREVfSEVJR0hUIGluZGVwZW5kZW50bHksIHlvdSBhbHNvIG5lZWQgdG8gdW5sb2NrIHRoZSBub2RlIGRpbWVuc2lvbnMgd2l0aC4uLioKYGBgCmxvY2tOb2RlRGltZW5zaW9ucyhGQUxTRSwgc3R5bGUubmFtZSkKYGBgCgojIEJpb2NvbmR1Y3RvciBncmFwaCBleGFtcGxlCgpBbHRlcm5hdGl2ZWx5LCB5b3UgbWlnaHQgd2FudCB0byBzdGFydCBmcm9tIGEgQmlvY29uZHVjdG9yIGdyYXBoTkVMIG9iamVjdC4gSGVyZSB3ZSBjcmVhdGUgYSA0LW5vZGUgZ3JhcGggaW4gUiwgc2VuZCBpdCB0byBDeXRvc2NhcGUgZm9yIGRpc3BsYXkgYW5kIGxheW91dC4gIEZvciB0aGUgc2FrZSBvZiBzaW1wbGljaXR5LCBubyBub2RlIGF0dHJpYnV0ZXMgYW5kIG5vIHZpc3VhbCBzdHlsZXMgYXJlIGluY2x1ZGVkOyB0aG9zZSB0b3BpY3MgYXJlIGNvdmVyZWQgaW4gc3Vic2VxdWVudCBzdGVwcy4KCmBgYHtyfQogICAgZyA9IG5ldyAoJ2dyYXBoTkVMJywgZWRnZW1vZGU9J2RpcmVjdGVkJykKICAgIGcgPSBncmFwaDo6YWRkTm9kZSAoJ0EnLCBnKQogICAgZyA9IGdyYXBoOjphZGROb2RlICgnRCcsIGcpCiAgICBnID0gZ3JhcGg6OmFkZE5vZGUgKCdDJywgZywgZWRnZXMgPSBsaXN0KCdEJykpCiAgICBnID0gZ3JhcGg6OmFkZE5vZGUgKCdCJywgZywgZWRnZXMgPSBsaXN0KGMoJ0EnLCdEJywnQycpKSkKICAgIGNyZWF0ZU5ldHdvcmtGcm9tR3JhcGggKGcsIHRpdGxlPSdzaW1wbGUgbmV0d29yaycsIGNvbGxlY3Rpb249J0dyYXBoTkVMIEV4YW1wbGUnKQpgYGAgCgpZb3Ugc2hvdWxkIG5vdyBoYXZlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhpcyA0LW5vZGUgZ3JhcGggd2l0aCBhIGJhc2ljLCBkZWZhdWx0IHN0eWxlLiBGb3J0dW5hdGVseSwgQ3l0b3NjYXBlIGhhcyBzb21lIGJ1aWx0LWluIHJlbmRlcmluZyBydWxlcyBpbiB3aGljaCAoYW5kIHVubGVzcyBpbnN0cnVjdGVkIG90aGVyd2lzZSkgbm9kZXMgYW5kIGVkZ2VzIGFyZSByZW5kZXJlZCBhbmQgYSBkZWZhdWx0ICh1c2VyLXByZWZlcmVuY2UpIGxheW91dCBhbGdvcml0aG0gaXMgYXBwbGllZC4KCiMjIEFkZCBub2RlIGF0dHJpYnV0ZXMKCldlIG9mdGVuIGtub3cgcXVpdGUgYSBsb3QgYWJvdXQgdGhlIG5vZGVzIGFuZCBlZGdlcyBpbiBvdXIgZ3JhcGhzLiBCeSBjb252ZXlpbmcgdGhpcyBpbmZvcm1hdGlvbiB2aXN1YWxseSwgdGhlIGdyYXBoIHdpbGwgYmUgZWFzaWVyIHRvIGV4cGxvcmUuIEZvciBpbnN0YW5jZSwgd2UgbWF5IGtub3cgdGhhdCBwcm90ZWluIEEgcGhvc3Bob3J5bGF0ZXMgcHJvdGVpbiBCLCB0aGF0IEEgaXMgYSBraW5hc2UgYW5kIEIgYSB0cmFuc2NyaXB0aW9uIGZhY3RvciwgYW5kIHRoYXQgdGhlaXIgbVJOQSBleHByZXNzaW9uIChjb21wYXJlZCB0byBhIGNvbnRyb2wpIGlzIGEgbG9nMiBmb2xkIGNoYW5nZSBvZiAxLjggYW5kIDMuMiByZXNwZWN0aXZlbHkuIE9uZSBvZiB0aGUgY29yZSBmZWF0dXJlcyBvZiBDeXRvc2NhcGUgaXMgdmlzdWFsIHN0eWxlcywgd2hpY2ggYWxsb3cgeW91IHRvIHNwZWNpZnkgaG93IGRhdGEgdmFsdWVzIChlLmcuLCBga2luYXNlJywgYHRyYW5zY3JpcHRpb24gZmFjdG9yJzsgZXhwcmVzc2lvbiByYXRpb3MpIHNob3VsZCBiZSBjb252ZXllZCBpbiB0aGUgdmlzdWFsIHByb3BlcnRpZXMgb2YgdGhlIGdyYXBoIChlLmcuLCBub2RlIHNoYXBlLCBub2RlIGNvbG9yIG9yIHNpemUpLgoKV2UgY29udGludWUgd2l0aCB0aGUgc2ltcGxlIDQtbm9kZSBncmFwaCwgYWRkaW5nIHR3byBraW5kcyBkYXRhIHZhbHVlcyAoYG1vbGVjdWxlVHlwZScgYW5kIGBsb2cyZmMnKS4gVGhlIGVhc2llc3Qgd2F5IHRvIGRvIHRoaXMgaXMgdmlhIGRhdGEuZnJhbWVzLiBIb3dldmVyLCB5b3UgY2FuIGFsc28gaW5jbHVkZSBhdHRyaWJ1dGVzIHRvZ2V0aGVyIHdpdGggdGhlIG9yaWdpbmFsIGdyYXBoIG1vZGVscyBhcyBCaW9jb25kdWN0b3IgZ3JhcGhzLCBpZ3JhcGhzIG9yIGRhdGEuZnJhbWVzIGFuZCB0aGVuIHVzZSB0aGUgcHJvdmlkZWQgKmNyZWF0ZSogZnVuY3Rpb25zIHRvIGNyZWF0ZSBhbmQgbG9hZCBpbiBhIHNpbmdsZSBzdGVwIChzZWUgKmNyZWF0ZU5ldHdvcmtGcm9tR3JhcGgqLCAgKmNyZWF0ZU5ldHdvcmtGcm9tSWdyYXBoKiBhbmQgKmNyZWF0ZU5ldHdvcmtGcm9tRGF0YUZyYW1lcyogZnVuY3Rpb25zKS4gQ2hlY2sgb3V0IHRoZSBvdGhlciB2aWduZXR0ZXMgZm9yIG1vcmUgZXhhbXBscy4KCmBgYHtyfQogICAgZGYgPC0gZGF0YS5mcmFtZSAobW9sZWN1bGVUeXBlPWMoJ2tpbmFzZScsJ1RGJywnY3l0b2tpbmUnLCdjeXRva2luZScpLAogICAgICAgICAgICAgICAgICAgICBsb2cyZmM9YygxLjgsMy4wLC0xLjIsLTIuNSksCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IGMoJ0EnLCdCJywnQycsJ0QnKSwgIyByb3cubmFtZXMgPSBub2RlIG5hbWVzCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgICAgICAgIyBpbXBvcnRhbnQgd2hlbiBsb2FkaW5nIHN0cmluZ3MhCiAgICBsb2FkVGFibGVEYXRhIChkZikKYGBgIAoKTm90ZSB0aGF0IGFkZGluZyB0aGUgYXR0cmlidXRlcyBkb2VzIG5vdCBpbiBpdHNlbGYgY2F1c2UgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIGdyYXBoIHRvIGNoYW5nZS4gU3VjaCBhIGNoYW5nZSByZXF1aXJlcyB0aGF0IHlvdSBzcGVjaWZ5IGFuZCBhcHBseSB2aXN1YWwgc3R5bGUgbWFwcGluZ3MsIHdoaWNoIHdpbGwgYmUgZXhwbGFpbmVkIGluIHRoZSAqbmV4dCogc2VjdGlvbi4gWW91IGNhbiwgaG93ZXZlciwgZXhhbWluZSB0aGVzZSBhdHRyaWJ1dGVzIGluIEN5dG9zY2FwZSwgdXNpbmcgQ3l0b3NjYXBlJ3MgdGhlICoqRGF0YSBQYW5lbCoqIHRvIGRpc3BsYXkgZGF0YSB2YWx1ZXMgYXNzb2NpYXRlZCB3aXRoIHNlbGVjdGVkIG5vZGVzIGltbWVkaWF0ZWx5IGJlbG93IHRoZSBDeXRvc2NhcGUgd2luZG93LgoKCiMjIE1vZGlmeWluZyB0aGUgZGlzcGxheTogIGRlZmF1bHRzIGFuZCBtYXBwaW5ncwoKUkN5MyBwcm92aWRlcyBhbiBlYXN5IHdheSB0byBub3Qgb25seSBjaGFuZ2UgdGhlIGRlZmF1bHQgc3R5bGVzLCBidXQgbW9yZSBpbnRlcmVzdGluZ2x5LCBSQ3kzIGFsc28gcHJvdmlkZXMgZWFzeSBhY2Nlc3MgdG8gKm1hcHBpbmcqIHlvdXIgZGF0YSB0byB2aXN1YWwgc3R5bGVzLCBlLmcuLCBhbGxvd2luZyB0aGUgc2l6ZSwgc2hhcGUgYW5kIGNvbG9yIG9mIG5vZGVzIGFuZCBlZGdlcyB0byBiZSBkZXRlcm1pbmVkIGJ5IHRoZSBkYXRhIHlvdSBoYXZlIGFzc29jaWF0ZWQgd2l0aCB0aG9zZSBub2RlcyBhbmQgZWRnZXMuIAoKRmlyc3QsIGxldCdzIGNoYW5nZSB0aGUgdGhlIGRlZmF1bHRzLiAKCmBgYHtyfQogICAgc2V0Tm9kZVNoYXBlRGVmYXVsdCAoJ09DVEFHT04nKQogICAgc2V0Tm9kZUNvbG9yRGVmYXVsdCAoJyNBQUZGODgnKQogICAgc2V0Tm9kZVNpemVEZWZhdWx0ICAoNjApCiAgICBzZXROb2RlRm9udFNpemVEZWZhdWx0ICgzMCkKYGBgIAoKTm93IHdlIHdpbGwgYWRkIHNvbWUgdmlzdWFsIG1hcHBpbmdzLiAgTGV0J3MgbWFwIGBtb2xlY3VsZVR5cGUnIHRvIG5vZGUgc2hhcGVzLiBGaXJzdCwgd2UgY2FuIHNlZSB3aGljaCBzaGFwZXMgYXJlIGF2YWlsYWJsZSBpbiBDeXRvc2NhcGUsIHRoZW4gd2UgY2FuIGRlZmluZSB0aGUgbWFwcGluZyB3aXRoIHBhaXJlZCBsaXN0cy4gCgpgYGB7cn0KICAgIGdldE5vZGVTaGFwZXMgKCkgICAjIGRpYW1vbmQsIGVsbGlwc2UsIHRyYXBlem9pZCwgdHJpYW5nbGUsIGV0Yy4KICAgIGNvbHVtbiA8LSAnbW9sZWN1bGVUeXBlJwogICAgdmFsdWVzIDwtIGMgKCdraW5hc2UnLCAgJ1RGJywnY3l0b2tpbmUnKQogICAgc2hhcGVzIDwtIGMgKCdESUFNT05EJywgJ1RSSUFOR0xFJywgJ1JFQ1RBTkdMRScpCiAgICBzZXROb2RlU2hhcGVNYXBwaW5nIChjb2x1bW4sIHZhbHVlcywgc2hhcGVzKQpgYGAgCgpUaGUgbm9kZSBzaGFwZSBtYXBwaW5nIGlzIGFuIGV4YW1wbGUgb2YgYSAqZGlzY3JldGUqIG1hcHBpbmcsIHdoZXJlIGEgc3R5bGUgaXMgZGVmaW5lZCBmb3IgZWFjaCwgZGlzY3JldGUgdmFsdWUuICBUaGlzIGlzIHVzZWZ1bCBmb3IgY2F0ZWdvcmljYWwgZGF0YSAobGlrZSB0eXBlKSB3aGVyZSB0aGVyZSBpcyBvbmx5IGEgbGltaXRlZCBzZXQgb2YgcG9zc2libGUgdmFsdWVzLiBUaGlzIGlzIGluIGNvbnRhc3QgdG8gdGhlIG90aGVyIHR3byBvdGhlciB0eXBlcyBvZiBtYXBwaW5nczogKmNvbnRpbnVvdXMqIGFuZCAqcGFzc3Rocm91Z2gqLiBJbiB0aGUgY2FzZSBvZiBleHByZXNzaW9uIHZhbHVlcywgZm9yIGV4YW1wbGUsIHdlIHdpbGwgd2FudCB0byB1c2UgKmNvbnRpbnVvdXMqIG1hcHBpbmcgKGUuZy4sIHRvIG5vZGUgY29sb3IpLCBkZWZpbmluZyBhIHNtYWxsIHNldCBvZiBjb250cm9sIHBvaW50cywgcmF0aGVyIHRoYW4gYW4gZXhwbGljaXQgY29sb3IgZm9yIGVhY2ggcG9zc2libGUgZGF0YSB2YWx1ZS4gQ3l0b3NjYXBlIHdpbGwgc2ltcGx5IGludGVycG9sYXRlIGJldHdlZW4gdGhlIGNvbnRyb2wgcG9pbnRzIHRvIHByb3ZpZGUgYSBncmFkaWVudCBvZiBjb2xvcnMuIExldCdzIHRyeSB0aGF0IG9uZSBub3cgCgpgYGB7cn0KICAgIGNvbHVtbiA8LSAnbG9nMmZjJwogICAgY29udHJvbC5wb2ludHMgPC0gYyAoLTMuMCwgMC4wLCAzLjApCiAgICBjb2xvcnMgPC0gIGMgKCcjNTU4OEREJywgJyNGRkZGRkYnLCAnI0REODg1NScpCiAgICBzZXROb2RlQ29sb3JNYXBwaW5nIChjb2x1bW4sIGNvbnRyb2wucG9pbnRzLCBjb2xvcnMpCmBgYCAKCk5vdGUgdGhhdCB0aGVyZSBhcmUgdGhyZWUgY29sb3JzIGFuZCB0aHJlZSBjb250cm9sIHBvaW50cy4gSG93ZXZlciwgeW91IGNhbiBhbHNvIHNwZWNpZnkgKnR3byBhZGRpdGlvbmFsKiBjb2xvcnMgYmV5b25kIHRoZSBudW1iZXIgb2YgY29udHJvbCBwb2ludHMgaWYgeW91IHdhbnQgdG8gc2V0IGV4dHJlbWUgKG9yIG91dC1vZi1ib3VuZHMpIGNvbG9ycyBmb3IgdmFsdWVzIGxlc3MgdGhhbiBvciBncmVhdGVyIHRoYW4geW91ciBjb250cm9sIHBvaW50cy4KCmBgYHtyfQogICAgY29udHJvbC5wb2ludHMgPC0gYyAoLTIuMCwgMC4wLCAyLjApCiAgICBjb2xvcnMgPC0gIGMgKCcjMjI1NUNDJywgJyM1NTg4REQnLCAnI0ZGRkZGRicsICcjREQ4ODU1JywnI0NDNTUyMicpCiAgICBzZXROb2RlQ29sb3JNYXBwaW5nIChjb2x1bW4sIGNvbnRyb2wucG9pbnRzLCBjb2xvcnMpCmBgYCAKCk5vdywgYWRkIGEgbm9kZSBzaXplIHJ1bGUsIHVzaW5nICpsb2cyZmMqIGFnYWluIGFzIGNvbnRyb2xsaW5nIG5vZGUgdmFsdWVzLgoKYGBge3J9CiAgICBjb250cm9sLnBvaW50cyA9IGMgKC0zLjAsIDIuMCwgMy4wKQogICAgc2l6ZXMgICAgID0gYyAoMjAsIDgwLCA5MCkKICAgIHNldE5vZGVTaXplTWFwcGluZyAoY29sdW1uLCBjb250cm9sLnBvaW50cywgc2l6ZXMpCgpgYGAgCgpJZiB5b3UgcmVjYWxsIHRoZSB0aGlyZCB0eXBlIG9mIG1hcHBpbmcsICpwYXNzdGhyb3VnaCosIHdlIGNhbiBzZWUgaXQgYWxyZWFkeSB3b3JraW5nIGluIG91ciBjdXJyZW50IG5ldHdvcmsgZXhhbXBsZS4gVGhlIG5vZGUgbGFiZWxzISBCeSBkZWZhdWx0LCB0aGUgKm5hbWUqIGNvbHVtbiBpcyBtYXBwZWQgdG8gdGhlIG5vZGUgbGFiZWwgcHJvcGVydHkgdXNpbmcgKnBhc3N0aHJvdWdoKiBsb2dpYzogdGhlIHZhbHVlIGlzIHBhc3NlZCBkaXJlY3RseSB0byB0aGUgc3R5bGUgcHJvcGVydHkuCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvZGF0YS9pbWcvTXlGaXJzdE5ldHdvcmtfTm9kZVNpemVNYXBwaW5nLnBuZyl7d2lkdGg9NjAlfQo8L2NlbnRlcj4KCgojIyBTZWxlY3Rpbmcgbm9kZXMKCkxldCB1cyBub3cgdHJ5IHNlbGVjdGluZyBub2RlcyBpbiBDeXRvc2NhcGUgZnJvbSBSLiBTZWxlY3QgdGhlIEMgbm9kZSBieSBuYW1lOgoKYGBge3J9CiAgICBzZWxlY3ROb2RlcyAoJ0MnLCduYW1lJykKYGBgCgpgYGB7cn0KICAgIGdldFNlbGVjdGVkTm9kZXMgKCkKYGBgIAoKTm93IHdlIHdpc2ggdG8gZXh0ZW5kIHRoZSBzZWxlY3RlZCBub2RlcyB0byBpbmNsdWRlIHRoZSBmaXJzdCBuZWlnaGJvcnMgb2YgdGhlIGFscmVhZHktc2VsZWN0ZWQgbm9kZSBCLiBUaGlzIGlzIGEgY29tbW9uIG9wZXJhdGlvbjogZm9yIGluc3RhbmNlLCBhZnRlciBzZWxlY3Rpbmcgb25lIG9yIG1vcmUgbm9kZXMgYmFzZWQgb24gZXhwZXJpbWVudGFsIGRhdGEgb3IgYW5ub3RhdGlvbiwgeW91IG1heSB3YW50IHRvIGV4cGxvcmUgdGhlc2UgaW4gdGhlIGNvbnRleHQgb2YgaW50ZXJhY3Rpb24gcGFydG5lcnMgKGluIGEgcHJvdGVpbi1wcm90ZWluIG5ldHdvcmspIG9yIGluIHJlbGF0aW9uIHRvIHVwc3RyZWFtIGFuZCBkb3duc3RyZWFtIHBhcnRuZXJzIGluIGEgc2lnbmFsaW5nIG9yIG1ldGFib2xpYyBuZXR3b3JrLiBUeXBlOgoKYGBge3J9CiAgICBzZWxlY3RGaXJzdE5laWdoYm9ycyAoKQpgYGAgCgpZb3Ugd2lsbCBzZWUgdGhhdCB0aHJlZSBub2RlcyBhcmUgbm93IHNlbGVjdGVkLiBHZXQgdGhlaXIgbmFtZXMgYmFjayB0byBSIGFzIGEgbGlzdDoKCmBgYHtyfQogICAgbm9kZS5uYW1lcyA8LSBnZXRTZWxlY3RlZE5vZGVzICgpCmBgYCAKCkFuZCwgZmluYWxseSwgZGVzZWxlY3Rpb24gd29ya3MgYXMgeW91J2QgZXhwZWN0IGJ5IG1lYW5zIG9mIGEgZ2VuZXJhbCAqY2xlYXJTZWxlY3Rpb24qIGZ1bmN0aW9uOgpgYGB7cn0KICAgIGNsZWFyU2VsZWN0aW9uKCkKICAgID9jbGVhclNlbGVjdGlvbgpgYGAKCiMjIFNhdmluZyBhbmQgZXhwb3J0ClNlc3Npb24gZmlsZXMgc2F2ZSAqZXZlcnl0aGluZyouIEFzIHdpdGggbW9zdCBwcm9qZWN0IHNvZnR3YXJlLCB3ZSByZWNvbW1lbmQgc2F2aW5nIG9mdGVuIQpgYGB7cn0KICAgIHNhdmVTZXNzaW9uKCd2aWduZXR0ZV9zZXNzaW9uJykgIy5jeXMKYGBgCgoqKk5vdGUqKjogSWYgeW91IGRvbid0IHNwZWNpZnkgYSBjb21wbGV0ZSBwYXRoLCB0aGUgZmlsZXMgd2lsbCBiZSBzYXZlZCByZWxhdGl2ZSB0byB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkgaW4gUi4KCiMjIyBTYXZpbmcgaGlnaCByZXNvbHV0aW9uIGltYWdlIGZpbGVzCllvdSBjYW4gZXhwb3J0IGV4dHJlbWVseSBoaWdoIHJlc29sdXRpb24gaW1hZ2VzLCBpbmNsdWRpbmcgdmVjdG9yIGdyYXBoaWMgZm9ybWF0cy4KYGBge3J9CiAgICBmdWxsLnBhdGg9cGFzdGUoZ2V0d2QoKSwndmlnbmV0dGVfaW1hZ2UnLHNlcD0nLycpCiAgICBleHBvcnRJbWFnZShmdWxsLnBhdGgsICdQTkcnLCB6b29tPTIwMCkgIy5wbmcgc2NhbGVkIGJ5IDIwMCUKICAgIGV4cG9ydEltYWdlKGZ1bGwucGF0aCwgJ1BERicpICMucGRmCiAgICA/ZXhwb3J0SW1hZ2UKYGBgCgojIEJyb3dzZSBhdmFpbGFibGUgZnVuY3Rpb25zLCBjb21tYW5kcyBhbmQgYXJndW1lbnRzClJDeTMgZnVuY3Rpb25zCmBgYHtyfQpoZWxwKHBhY2thZ2U9UkN5MykKYGBgCgoKfCBDYXRlZ29yeSB8IERlc2NyaXB0aW9uIHwgRXhhbXBsZXMgfAp8LS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tfAp8ICoqQXBwcyoqIHwgSW5zcGVjdGluZyBhbmQgbWFuYWdpbmcgYXBwcyBmb3IgQ3l0b3NjYXBlLiB8IF9pbnN0YWxsQXBwKCkgZGlzYWJsZUFwcCgpIGdldEluc3RhbGxlZEFwcHMoKV8gfAp8ICoqQ29sbGVjdGlvbnMqKiB8IEdldHRpbmcgaW5mb3JtYXRpb24gYWJvdXQgbmV0d29yayBjb2xsZWN0aW9ucy4gfCBfZ2V0Q29sbGVjdGlvbkxpc3QoKSBnZXRDb2xsZWN0aW9uTmV0d29ya3MoKV8gfAp8ICoqQ29tbWFuZHMqKiB8IENvbnN0cnVjdGluZyBhbnkgYXJiaXRyYXJ5IEN5UkVTVCBBUEkgb3IgQ29tbWFuZHMgQVBJIG1ldGhvZCB2aWEgc3RhbmRhcmQgR0VULCBQVVQsIFBPU1QgYW5kIERFTEVURSBwcm90b2NvbHMuIHwgX2N5cmVzdFBPU1QoKSBjb21tYW5kc1BPU1QoKSBjeXJlc3RBUEkoKSBjb21tYW5kc0FQSSgpXyB8CnwgKipDeU5ERXgqKiB8IENvbW11bmljYXRpbmcgd2l0aCBOREV4IGZyb20gd2l0aGluIEN5dG9zY2FwZS4gfCBfaW1wb3J0TmV0d29ya0Zyb21OREV4KCkgZXhwb3J0TmV0d29ya1RvTkRFeCgpXyB8CnwgKipDeXRvc2NhcGUgU3lzdGVtKiogfCBDaGVja2luZyBDeXRvc2NhcGUgU3lzdGVtIGluZm9ybWF0aW9uLCBpbmNsdWRpbmcgdmVyc2lvbnMgYW5kIG1lbW9yeSB1c2FnZS4gfCBfY3l0b3NjYXBlUGluZygpIGN5dG9zY2FwZVZlcnNpb25JbmZvKClfIHwKfCAqKkdyb3VwcyoqIHwgV29ya2luZyB3aXRoIGdyb3VwcyBpbiBDeXRvc2NhcGUuIHwgX2NyZWF0ZUdyb3VwKCkgY29sbGFwc2VHcm91cCgpXyB8CnwgKipMYXlvdXRzKiogfCBQZXJmb3JtaW5nIGxheW91dHMgaW4gYWRkaXRpb24gdG8gZ2V0dGluZyBhbmQgc2V0dGluZyBsYXlvdXQgcHJvcGVydGllcy4gfCBfbGF5b3V0TmV0d29yaygpIGdldExheW91dE5hbWVzKClfIHwKfCAqKk5ldHdvcmtzKiogfCBGdW5jdGlvbnMgZm9yIG5ldHdvcmsgbWFuYWdlbWVudCBhbmQgcmV0cmlldmluZyBpbmZvcm1hdGlvbiBvbiBuZXR3b3Jrcywgbm9kZXMgYW5kIGVkZ2VzLiB8IF9jcmVhdGVOZXR3b3JrRnJvbS4uLigpIGNyZWF0ZS4uLkZyb21OZXR3b3JrKCkgZ2V0TmV0d29ya1N1aWQoKSwgZXhwb3J0TmV0d29yaygpIGdldEFsbE5vZGVzKCkgZ2V0QWxsRWRnZXMoKV8gfAp8ICoqTmV0d29yayBTZWxlY3Rpb24qKiB8IFdvcmtpbmcgd2l0aCBzZWxlY3Rpb24gb2Ygbm9kZXMgYW5kIGVkZ2VzIGluIG5ldHdvcmtzLiB8IF9zZWxlY3ROb2RlcygpIGludmVydE5vZGVTZWxlY3Rpb24oKSBzZWxlY3RGaXJzdE5laWdoYm9ycygpXyB8CnwgKipOZXR3b3JrIFZpZXdzKiogfCBQZXJmb3JtaW5nIHZpZXcgb3BlcmF0aW9ucyBpbiBhZGRpdGlvbiB0byBnZXR0aW5nIGFuZCBzZXR0aW5nIHZpZXcgcHJvcGVydGllcy4gfCBfZ2V0Q3VycmVudFZpZXcoKSBmaXRDb250ZW50KCkgZXhwb3J0SW1hZ2UoKSB0b2dnbGVHcmFwaGljc0RldGFpbHMoKV8gfAp8ICoqU2Vzc2lvbioqIHwgTWFuYWdpbmcgQ3l0b3NjYXBlIHNlc3Npb25zLCBpbmNsdWRpbmcgc2F2ZSwgb3BlbiBhbmQgY2xvc2UuIHwgX29wZW5TZXNzaW9uKCkgc2F2ZVNlc3Npb24oKSBjbG9zZVNlc3Npb24oKV8gfAp8ICoqU3R5bGUgQnlwYXNzZXMqKiB8IFNldHRpbmcgYW5kIGNsZWFyaW5nIGJ5cGFzcyB2YWx1ZXMgZm9yIHZpc3VhbCBwcm9wZXJ0aWVzLiB8IF9zZXROb2RlQ29sb3JCeXBhc3MoKSBzZXRFZGdlTGluZVN0eWxlQnlwYXNzKCkgaGlkZU5vZGVzKClfIHwKfCAqKlN0eWxlIERlZmF1bHRzKiogfCBHZXR0aW5nIGFuZCBzZXR0aW5nIGRlZmF1bHQgdmFsdWVzIGZvciB2aXN1YWwgcHJvcGVydGllcy4gfCBfc2V0Tm9kZVNoYXBlRGVmYXVsdCgpIHNldEVkZ2VMaW5lV2lkdGhEZWZhdWx0KClfIHwKfCAqKlN0eWxlIERlcGVuZGVuY2llcyoqIHwgR2V0dGluZyBhbmQgc2V0dGluZyBzdHlsZSBkZXBlbmRlbmNpZXMuIHwgX2xvY2tOb2RlRGltZW5zaW9ucygpXyB8CnwgKipTdHlsZSBNYXBwaW5ncyoqIHwgRGVmaW5pbmcgbWFwcGluZ3MgYmV0d2VlbiB0YWJsZSBjb2x1bW4gdmFsdWVzIGFuZCB2aXN1YWwgcHJvcGVydGllcy4gfCBfc2V0Tm9kZVNpemVNYXBwaW5nKCkgc2V0RWRnZUNvbG9yTWFwcGluZygpXyB8CnwgKipTdHlsZXMqKiB8IE1hbmFnaW5nIHN0eWxlcyBhbmQgcmV0cmlldmluZyBnZW5lcmFsIGxpc3RzIG9mIHByb3BlcnRpZXMgcmVsZXZhbnQgdG8gbXVsdGlwbGUgc3R5bGUgbW9kZXMuIHwgX2NyZWF0ZVZpc3VhbFN0eWxlKCkgc2V0VmlzdWFsU3R5bGUoKSBleHBvcnRWaXN1YWxTdHlsZXMoKSBnZXRBcnJvd1NoYXBlcygpXyB8CnwgKipTdHlsZSBWYWx1ZXMqKiB8IFJldHJpZXZpbmcgY3VycmVudCB2YWx1ZXMgZm9yIHZpc3VhbCBwcm9wZXJ0aWVzLiB8IF9nZXROb2RlV2lkdGgoKSBnZXRFZGdlQ29sb3IoKSBnZXROZXR3b3JrWm9vbSgpXyB8CnwgKipUYWJsZXMqKiB8IE1hbmFnaW5nIHRhYmxlIGNvbHVtbnMgYW5kIHRhYmxlIGNvbHVtbiBmdW5jdGlvbnMsIGxpa2UgbWFwIGFuZCByZW5hbWUsIGFzIHdlbGwgYXMgbG9hZGluZyBhbmQgZXh0cmFjdGluZyB0YWJsZSBkYXRhIGluIEN5dG9zY2FwZS4gfCBfZ2V0VGFibGVDb2x1bW5zKCkgcmVuYW1lVGFibGVDb2x1bW4oKSBsb2FkVGFibGVEYXRhKCkgbWFwVGFibGVDb2x1bW4oKV8gfAp8ICoqVG9vbHMqKiB8IEZ1bmN0aW9ucyByZWxhdGVkIHRvIGFjdGlvbnMgZm91bmQgaW4gdGhlIFRvb2xzIE1lbnUgaW4gQ3l0b3NjYXBlLiB8IF9jeWJyb3dzZXJEaWFsb2coKSBkaWZmdXNpb25CYXNpYygpXyB8CnwgKipVc2VyIEludGVyZmFjZSoqIHwgRnVuY3Rpb25zIGFmZmVjdGluZyB0aGUgQ3l0b3NjYXBlIHVzZXIgaW50ZXJmYWNlLCBzdWNoIGFzIHBhbmVsIG1hbmFnZW1lbnQuIHwgX2hpZGVQYW5lbCgpIGZsb2F0UGFuZWwoKSBkb2NrUGFuZWwoKV8gfAoKCk9wZW4gc3dhZ2dlciBkb2NzIGZvciBsaXZlIGluc3RhbmNlcyBvZiBDeVJFU1QgQVBJIGFuZCBDb21tYW5kcyBBUEk6CmBgYHtyfQpjeXJlc3RBUEkoKSAgIyBDeVJFU1QgQVBJCmNvbW1hbmRzQVBJKCkgICMgQ29tbWFuZHMgQVBJCmBgYAoKTGlzdCBhdmFpbGFibGUgY29tbWFuZHMgYW5kIGFyZ3VtZW50cyBpbiBSLiBVc2UgImhlbHAiIHRvIGxpc3QgdG9wIGxldmVsOgpgYGB7cn0KY29tbWFuZHNIZWxwKCJoZWxwIikgIApgYGAKCkxpc3QgKipuZXR3b3JrKiogY29tbWFuZHMuIE5vdGUgdGhhdCAiaGVscCIgaXMgb3B0aW9uYWw6CmBgYHtyfQpjb21tYW5kc0hlbHAoImhlbHAgbmV0d29yayIpICAKYGBgCgpMaXN0IGFyZ3VtZW50cyBmb3IgdGhlICoqbmV0d29yayBzZWxlY3QqKiBjb21tYW5kOgpgYGB7cn0KY29tbWFuZHNIZWxwKCJoZWxwIG5ldHdvcmsgc2VsZWN0IikgCmBgYAoKVGhhdCBjb3ZlcnMgdGhlIGJhc2ljcyBvZiBuZXR3b3JrIG1hbmlwdWxhdGlvbi4gQ2hlY2sgb3V0IHRoZSBvdGhlciB2aWduZXR0ZXMgZm9yIGFkZGl0aW9uYWwgYW1kIG1vcmUgY29tcGxleCBleGFtcGxlcy4gQW5kIHdoZW4geW91IGFyZSByZWFkeSB0byB3b3JrIHdpdGggc29tZSByZWFsIGRhdGEsIGNoZWNrIG91dCB0aGUgb3RoZXIgYmFzaWMgYW5kIGFkdmFuY2VkIFIgdHV0b3JpYWxzLCBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL2N5dG9zY2FwZS1hdXRvbWF0aW9uL3RyZWUvbWFzdGVyL2Zvci1zY3JpcHRlcnMvUi4KCiMgTW9yZSBleGFtcGxlcwpEb24ndCBmb3JnZXQgdG8gY2hlY2sgb3V0IHRoZSBvdGhlciB2aWduZXR0ZXMgaW4gdGhpcyBwYWNrYWdlOgpgYGB7cn0KYnJvd3NlVmlnbmV0dGVzKCJSQ3kzIikKYGBgCgpJbiBhZGRpdGlvbiwgdGhlIEN5dG9zY2FwZSB0ZWFtIGlzIGNvbGxlY3Rpbmcgc2NyaXB0cyBmcm9tIHRoZSBjb21tdW5pdHkgaW4gYSBwdWJsaWMgR2l0SHViIHJlcG9zaXRvcnkgYXQgaHR0cHM6Ly9naXRodWIuY29tL2N5dG9zY2FwZS9jeXRvc2NhcGUtYXV0b21hdGlvbi90cmVlL21hc3Rlci9mb3Itc2NyaXB0ZXJzL1IuICAKCiMgRGV2ZWxvcG1lbnQKClRoZSBSQ3kzIHByb2plY3QgY29kZSBhbmQgZG9jdW1lbnRhdGlvbiBpcyBtYWludGFpbmVkIGF0IEdpdEh1YjogaHR0cHM6Ly9naXRodWIuY29tL2N5dG9zY2FwZS9SQ3kzLiBBbGwgYnVncyBhbmQgZmVhdHVyZSByZXF1ZXN0cyBhcmUgdHJhY2tlZCBhcyAqKklzc3VlcyoqLCBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL1JDeTMvaXNzdWVzLiAKCgojIENyZWRpdHMKCiogUGF1bCBTaGFubm9uJ3MgZ2VuZXJvdXMgYWR2aWNlIGFuZCBtZW50b3JzaGlwIHdhcyB2ZXJ5IGltcG9ydGFudCBmb3IgdHJhbnNmb3JtaW5nIHRoaXMgcGFja2FnZSBmcm9tIHVzaW5nIFhNTFJQQyBhbmQgQ3l0b3NjYXBlUlBDIHRvIHVzaW5nIEN5UkVTVC4KKiBEYXZpZCBPdGFzZWssIEtlaWljaGlybyBPbm8gYW5kIEJhcnJ5IERlbWNoYWsga2luZGx5IHByb3ZpZGVkIEN5UkVTVCBhcyB3ZWxsIGFzIGhlbHAgYW5kIHN1cHBvcnQgZm9yIG5ldyBmdW5jdGlvbmFsaXRpZXMgYW5kIGNoYW5nZXMuCiogTWFyayBHcmltZXMgYW5kIFJ1dGggSXNzZXJsaW4ga2luZGx5IHByb3ZpZGVkIGhlbHBmdWwgdXNlciBmZWVkYmFjay4KKiBKdWxpYSBHdXN0YXZzZW4gZ2VuZXJvdXNseSBkZXZlbG9wZWQgdmFyaW91cyB1c2UgY2FzZXMvZXhhbXBsZXMgZm9yIHVzaW5nIFJDeTMgd2l0aCBiaW9sb2dpY2FsIGRhdGEgZHVyaW5nIEdTT0MgMjAxNiwgaHR0cHM6Ly9naXRodWIuY29tL2pvb29saWEvZ3NvY19SY3kzX3ZpZ25ldHRlcy9ibG9iL21hc3Rlci9ibG9nX3Bvc3RfZHJhZnRzL2ZpbmFsX3dvcmtfc3VibWlzc2lvbi5tZC4KKiBUYW5qYSBNdWV0emUgcHJvdmlkZWQgbWFueSB5ZWFycyBvZiBkZXZlbG9wbWVudCwgZGVzaWduLCBtYWludGVuYW5jZSBhbmQgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIFJDeTMgcHJvamVjdC4KKiBBbGwgY29udHJpYnV0b3JzLCBuZXcgYW5kIG9sZCwgYXJlIGR5bmFtaWNhbGx5IGFja25vd2xlZGdlZCBpbiBvdXIgKipDb250cmlidXRvciBHcmFwaCoqLCBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL1JDeTMvZ3JhcGhzL2NvbnRyaWJ1dG9ycwoKIyBSZWZlcmVuY2VzCjEuIFNoYW5ub24gUCwgTWFya2llbCBBLCBPemllciBPLCBCYWxpZ2EgTlMsIFdhbmcgSlQsIFJhbWFnZSBELCBBbWluIE4sIFNjaHdpa293c2tpIEIsIElkZWtlciBULiAyMDAzLiBDeXRvc2NhcGU6IGEgc29mdHdhcmUgZW52aXJvbm1lbnQgZm9yIGludGVncmF0ZWQgbW9kZWxzIG9mIGJpb21vbGVjdWxhciBpbnRlcmFjdGlvbiBuZXR3b3Jrcy4gR2Vub21lIFJlcy4gTm92OzEzKDExKToyNDk4LTUwNAoyLiBIdWJlciBXLCBDYXJleSBWSiwgTG9uZyBMLCBGYWxjb24gUywgR2VudGxlbWFuIFIuIDIwMDcuIEdyYXBocyBpbiBtb2xlY3VsYXIgYmlvbG9neS4gQk1DIEJpb2luZm9ybWF0aWNzLiAyMDA3IFNlcCAyNzs4LgozLiBPbm8gSywgTXVldHplIFQsIEtvbGlzaG92c2tpIEcsIFNoYW5ub24gUCwgRGVtY2hhaywgQi4gQ3lSRVNUOiBUdXJib2NoYXJnaW5nIEN5dG9zY2FwZSBBY2Nlc3MgZm9yIEV4dGVybmFsIFRvb2xzIHZpYSBhIFJFU1RmdWwgQVBJIFt2ZXJzaW9uIDE7IHJlZmVyZWVzOiAyIGFwcHJvdmVkXS4gRjEwMDBSZXNlYXJjaCAyMDE1LCA0OjQ3OC4K