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' and
log2fc’). 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:
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:
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
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:
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.
LS0tCnRpdGxlOiAiT3ZlcnZpZXcgb2YgUkN5MyIKYXV0aG9yOiAiYnkgQWxleGFuZGVyIFBpY28sIFRhbmphIE11ZXR6ZSwgR2VvcmdpIEtvbGlzaG92c2tpLCBQYXVsIFNoYW5ub24iCnBhY2thZ2U6IFJDeTMKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogIm5vbmUiCiMgIHBkZl9kb2N1bWVudDoKIyAgICB0b2M6IHRydWUgICAgCnZpZ25ldHRlOiA+CiAgJVxWaWduZXR0ZUluZGV4RW50cnl7MDEuIE92ZXJ2aWV3IG9mIFJDeTMgfjI1IG1pbn0KICAlXFZpZ25ldHRlRW5naW5le2tuaXRyOjpybWFya2Rvd259CiAgJVxWaWduZXR0ZUVuY29kaW5ne1VURi04fQotLS0KYGBge3IsIGVjaG8gPSBGQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGV2YWw9RkFMU0UKKQpgYGAKKlRoZSBSIG1hcmtkb3duIGlzIGF2YWlsYWJsZSBmcm9tIHRoZSBwdWxsZG93biBtZW51IGZvciogQ29kZSAqYXQgdGhlIHVwcGVyLXJpZ2h0LCBjaG9vc2UgIkRvd25sb2FkIFJtZCIsIG9yIFtkb3dubG9hZCB0aGUgUm1kIGZyb20gR2l0SHViXShodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vY3l0b3NjYXBlL2N5dG9zY2FwZS1hdXRvbWF0aW9uL21hc3Rlci9mb3Itc2NyaXB0ZXJzL1Ivbm90ZWJvb2tzL092ZXJ2aWV3LW9mLVJDeTMuUm1kKS4qCgo8aHIgLz4KCipDeXRvc2NhcGUqIGlzIGEgd2VsbC1rbm93biBiaW9pbmZvcm1hdGljcyB0b29sIGZvciBkaXNwbGF5aW5nIGFuZCBleHBsb3JpbmcgYmlvbG9naWNhbCBuZXR3b3Jrcy4KKipSKiogaXMgYSBwb3dlcmZ1bCBwcm9ncmFtbWluZyBsYW5ndWFnZSBhbmQgZW52aXJvbm1lbnQgZm9yIHN0YXRpc3RpY2FsIGFuZCBleHBsb3JhdG9yeSBkYXRhIGFuYWx5c2lzLgoqUkN5MyogdXNlcyBDeVJFU1QgdG8gY29tbXVuaWNhdGUgYmV0d2VlbiAqKlIqKiBhbmQgQ3l0b3NjYXBlLCBhbGxvd2luZyBhbnkgZ3JhcGhzIChlLmcuLCBpZ3JhcGgsIGdyYXBoTkVMIG9yIGRhdGFmcmFtZXMpIHRvIGJlIHZpZXdlZCwgZXhwbG9yZWQgYW5kIG1hbmlwdWxhdGVkIHdpdGggdGhlIEN5dG9zY2FwZSBwb2ludC1hbmQtY2xpY2sgdmlzdWFsIGludGVyZmFjZS4gVGh1cywgdmlhIFJDeTMsIHRoZXNlIHR3byBxdWl0ZSBkaWZmZXJlbnQsIHF1aXRlIHVzZWZ1bCBiaW9pbmZvcm1hdGljcyBzb2Z0d2FyZSBlbnZpcm9ubWVudHMgYXJlIGNvbm5lY3RlZCwgbXV0dWFsbHkgZW5oYW5jaW5nIGVhY2ggb3RoZXIsIHByb3ZpZGluZyBuZXcgcG9zc2liaWxpdGllcyBmb3IgZXhwbG9yaW5nIGJpb2xvZ2ljYWwgZGF0YS4KCiMgSW5zdGFsbGF0aW9uCmBgYHtyfQppZighIlJDeTMiICVpbiUgaW5zdGFsbGVkLnBhY2thZ2VzKCkpewogICAgaW5zdGFsbC5wYWNrYWdlcygiQmlvY01hbmFnZXIiKQogICAgQmlvY01hbmFnZXI6Omluc3RhbGwoIlJDeTMiKQp9CmxpYnJhcnkoUkN5MykKYGBgCgojIFByZXJlcXVpc2l0ZXMKSW4gYWRkaXRpb24gdG8gdGhpcyBwYWNrYWdlIChSQ3kzKSwgeW91IHdpbGwgbmVlZDoKCiAgKiAqKkN5dG9zY2FwZSAzLjYuMSoqIG9yIGdyZWF0ZXIsIHdoaWNoIGNhbiBiZSBkb3dubG9hZGVkIGZyb20gaHR0cDovL3d3dy5jeXRvc2NhcGUub3JnL2Rvd25sb2FkLnBocC4gU2ltcGx5IGZvbGxvdyB0aGUgaW5zdGFsbGF0aW9uIGluc3RydWN0aW9ucyBvbiBzY3JlZW4uCgojIEdldHRpbmcgc3RhcnRlZAoKRmlyc3QsIGxhdW5jaCBDeXRvc2NhcGUgYW5kIGtlZXAgaXQgcnVubmluZyB3aGVuZXZlciB1c2luZyBSQ3kzLiBDb25maXJtIHRoYXQKeW91IGhhdmUgZXZlcnl0aGluZyBpbnN0YWxsZWQgYW5kIHJ1bm5pbmc6CgpgYGB7cn0KICAgIGN5dG9zY2FwZVBpbmcgKCkKICAgIGN5dG9zY2FwZVZlcnNpb25JbmZvICgpCmBgYAoKIyBNeSBmaXJzdCBuZXR3b3JrCkxldCdzIGNyZWF0ZSBhIEN5dG9zY2FwZSBuZXR3b3JrIGZyb20gc29tZSBiYXNpYyBSIG9iamVjdHMKYGBge3J9Cm5vZGVzIDwtIGRhdGEuZnJhbWUoaWQ9Yygibm9kZSAwIiwibm9kZSAxIiwibm9kZSAyIiwibm9kZSAzIiksCiAgICAgICAgICAgZ3JvdXA9YygiQSIsIkEiLCJCIiwiQiIpLCAjIGNhdGVnb3JpY2FsIHN0cmluZ3MKICAgICAgICAgICBzY29yZT1hcy5pbnRlZ2VyKGMoMjAsMTAsMTUsNSkpLCAjIGludGVnZXJzCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKZWRnZXMgPC0gZGF0YS5mcmFtZShzb3VyY2U9Yygibm9kZSAwIiwibm9kZSAwIiwibm9kZSAwIiwibm9kZSAyIiksCiAgICAgICAgICAgdGFyZ2V0PWMoIm5vZGUgMSIsIm5vZGUgMiIsIm5vZGUgMyIsIm5vZGUgMyIpLAogICAgICAgICAgIGludGVyYWN0aW9uPWMoImluaGliaXRzIiwiaW50ZXJhY3RzIiwiYWN0aXZhdGVzIiwiaW50ZXJhY3RzIiksICAjIG9wdGlvbmFsCiAgICAgICAgICAgd2VpZ2h0PWMoNS4xLDMuMCw1LjIsOS45KSwgIyBudW1lcmljCiAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycz1GQUxTRSkKCmNyZWF0ZU5ldHdvcmtGcm9tRGF0YUZyYW1lcyhub2RlcyxlZGdlcywgdGl0bGU9Im15IGZpcnN0IG5ldHdvcmsiLCBjb2xsZWN0aW9uPSJEYXRhRnJhbWUgRXhhbXBsZSIpCmBgYAoKIyMgU3dpdGNoIHN0eWxlcwpDaGVjayBvdXQgdGhlIG1hcnF1ZWUgc3R5bGUhCmBgYHtyfQpzZXRWaXN1YWxTdHlsZSgnTWFycXVlZScpCmBgYAoKIyMgTXkgb3duIHN0eWxlCkNyZWF0ZSB5b3VyIG93biBzdHlsZSB3aXRoIG5vZGUgYXR0cmlidXRlIGZpbGwgbWFwcGluZ3MgYW5kIHNvbWUgZGVmYXVsdHMKYGBge3J9CnN0eWxlLm5hbWUgPSAibXlTdHlsZSIKZGVmYXVsdHMgPC0gbGlzdChOT0RFX1NIQVBFPSJkaWFtb25kIiwKICAgICAgICAgICAgICAgICBOT0RFX1NJWkU9MzAsCiAgICAgICAgICAgICAgICAgRURHRV9UUkFOU1BBUkVOQ1k9MTIwLAogICAgICAgICAgICAgICAgIE5PREVfTEFCRUxfUE9TSVRJT049IlcsRSxjLDAuMDAsMC4wMCIpCm5vZGVMYWJlbHMgPC0gbWFwVmlzdWFsUHJvcGVydHkoJ25vZGUgbGFiZWwnLCdpZCcsJ3AnKQpub2RlRmlsbHMgPC0gbWFwVmlzdWFsUHJvcGVydHkoJ25vZGUgZmlsbCBjb2xvcicsJ2dyb3VwJywnZCcsYygiQSIsIkIiKSwgYygiI0ZGOTkwMCIsIiM2NkFBQUEiKSkKYXJyb3dTaGFwZXMgPC0gbWFwVmlzdWFsUHJvcGVydHkoJ0VkZ2UgVGFyZ2V0IEFycm93IFNoYXBlJywnaW50ZXJhY3Rpb24nLCdkJyxjKCJhY3RpdmF0ZXMiLCJpbmhpYml0cyIsImludGVyYWN0cyIpLGMoIkFycm93IiwiVCIsIk5vbmUiKSkKZWRnZVdpZHRoIDwtIG1hcFZpc3VhbFByb3BlcnR5KCdlZGdlIHdpZHRoJywnd2VpZ2h0JywncCcpCgpjcmVhdGVWaXN1YWxTdHlsZShzdHlsZS5uYW1lLCBkZWZhdWx0cywgbGlzdChub2RlTGFiZWxzLG5vZGVGaWxscyxhcnJvd1NoYXBlcyxlZGdlV2lkdGgpKQpzZXRWaXN1YWxTdHlsZShzdHlsZS5uYW1lKQpgYGAKCjxjZW50ZXI+CiFbXShodHRwczovL2N5dG9zY2FwZS5naXRodWIuaW8vY3l0b3NjYXBlLWF1dG9tYXRpb24vZm9yLXNjcmlwdGVycy9SL25vdGVib29rcy9kYXRhL2ltZy9NeUZpcnN0TmV0d29yay5wbmcpe3dpZHRoPTYwJX0KPC9jZW50ZXI+CgoqUHJvLXRpcDogaWYgeW91IHdhbnQgdG8gc2V0IE5PREVfV0lEVEggYW5kIE5PREVfSEVJR0hUIGluZGVwZW5kZW50bHksIHlvdSBhbHNvIG5lZWQgdG8gdW5sb2NrIHRoZSBub2RlIGRpbWVuc2lvbnMgd2l0aC4uLioKYGBgCmxvY2tOb2RlRGltZW5zaW9ucyhGQUxTRSwgc3R5bGUubmFtZSkKYGBgCgojIEJpb2NvbmR1Y3RvciBncmFwaCBleGFtcGxlCgpBbHRlcm5hdGl2ZWx5LCB5b3UgbWlnaHQgd2FudCB0byBzdGFydCBmcm9tIGEgQmlvY29uZHVjdG9yIGdyYXBoTkVMIG9iamVjdC4gSGVyZSB3ZSBjcmVhdGUgYSA0LW5vZGUgZ3JhcGggaW4gUiwgc2VuZCBpdCB0byBDeXRvc2NhcGUgZm9yIGRpc3BsYXkgYW5kIGxheW91dC4gIEZvciB0aGUgc2FrZSBvZiBzaW1wbGljaXR5LCBubyBub2RlIGF0dHJpYnV0ZXMgYW5kIG5vIHZpc3VhbCBzdHlsZXMgYXJlIGluY2x1ZGVkOyB0aG9zZSB0b3BpY3MgYXJlIGNvdmVyZWQgaW4gc3Vic2VxdWVudCBzdGVwcy4KCmBgYHtyfQogICAgZyA9IG5ldyAoJ2dyYXBoTkVMJywgZWRnZW1vZGU9J2RpcmVjdGVkJykKICAgIGcgPSBncmFwaDo6YWRkTm9kZSAoJ0EnLCBnKQogICAgZyA9IGdyYXBoOjphZGROb2RlICgnRCcsIGcpCiAgICBnID0gZ3JhcGg6OmFkZE5vZGUgKCdDJywgZywgZWRnZXMgPSBsaXN0KCdEJykpCiAgICBnID0gZ3JhcGg6OmFkZE5vZGUgKCdCJywgZywgZWRnZXMgPSBsaXN0KGMoJ0EnLCdEJywnQycpKSkKICAgIGNyZWF0ZU5ldHdvcmtGcm9tR3JhcGggKGcsIHRpdGxlPSdzaW1wbGUgbmV0d29yaycsIGNvbGxlY3Rpb249J0dyYXBoTkVMIEV4YW1wbGUnKQpgYGAgCgpZb3Ugc2hvdWxkIG5vdyBoYXZlIHRoZSBzdHJ1Y3R1cmUgb2YgdGhpcyA0LW5vZGUgZ3JhcGggd2l0aCBhIGJhc2ljLCBkZWZhdWx0IHN0eWxlLiBGb3J0dW5hdGVseSwgQ3l0b3NjYXBlIGhhcyBzb21lIGJ1aWx0LWluIHJlbmRlcmluZyBydWxlcyBpbiB3aGljaCAoYW5kIHVubGVzcyBpbnN0cnVjdGVkIG90aGVyd2lzZSkgbm9kZXMgYW5kIGVkZ2VzIGFyZSByZW5kZXJlZCBhbmQgYSBkZWZhdWx0ICh1c2VyLXByZWZlcmVuY2UpIGxheW91dCBhbGdvcml0aG0gaXMgYXBwbGllZC4KCiMjIEFkZCBub2RlIGF0dHJpYnV0ZXMKCldlIG9mdGVuIGtub3cgcXVpdGUgYSBsb3QgYWJvdXQgdGhlIG5vZGVzIGFuZCBlZGdlcyBpbiBvdXIgZ3JhcGhzLiBCeSBjb252ZXlpbmcgdGhpcyBpbmZvcm1hdGlvbiB2aXN1YWxseSwgdGhlIGdyYXBoIHdpbGwgYmUgZWFzaWVyIHRvIGV4cGxvcmUuIEZvciBpbnN0YW5jZSwgd2UgbWF5IGtub3cgdGhhdCBwcm90ZWluIEEgcGhvc3Bob3J5bGF0ZXMgcHJvdGVpbiBCLCB0aGF0IEEgaXMgYSBraW5hc2UgYW5kIEIgYSB0cmFuc2NyaXB0aW9uIGZhY3RvciwgYW5kIHRoYXQgdGhlaXIgbVJOQSBleHByZXNzaW9uIChjb21wYXJlZCB0byBhIGNvbnRyb2wpIGlzIGEgbG9nMiBmb2xkIGNoYW5nZSBvZiAxLjggYW5kIDMuMiByZXNwZWN0aXZlbHkuIE9uZSBvZiB0aGUgY29yZSBmZWF0dXJlcyBvZiBDeXRvc2NhcGUgaXMgdmlzdWFsIHN0eWxlcywgd2hpY2ggYWxsb3cgeW91IHRvIHNwZWNpZnkgaG93IGRhdGEgdmFsdWVzIChlLmcuLCBga2luYXNlJywgYHRyYW5zY3JpcHRpb24gZmFjdG9yJzsgZXhwcmVzc2lvbiByYXRpb3MpIHNob3VsZCBiZSBjb252ZXllZCBpbiB0aGUgdmlzdWFsIHByb3BlcnRpZXMgb2YgdGhlIGdyYXBoIChlLmcuLCBub2RlIHNoYXBlLCBub2RlIGNvbG9yIG9yIHNpemUpLgoKV2UgY29udGludWUgd2l0aCB0aGUgc2ltcGxlIDQtbm9kZSBncmFwaCwgYWRkaW5nIHR3byBraW5kcyBkYXRhIHZhbHVlcyAoYG1vbGVjdWxlVHlwZScgYW5kIGBsb2cyZmMnKS4gVGhlIGVhc2llc3Qgd2F5IHRvIGRvIHRoaXMgaXMgdmlhIGRhdGEuZnJhbWVzLiBIb3dldmVyLCB5b3UgY2FuIGFsc28gaW5jbHVkZSBhdHRyaWJ1dGVzIHRvZ2V0aGVyIHdpdGggdGhlIG9yaWdpbmFsIGdyYXBoIG1vZGVscyBhcyBCaW9jb25kdWN0b3IgZ3JhcGhzLCBpZ3JhcGhzIG9yIGRhdGEuZnJhbWVzIGFuZCB0aGVuIHVzZSB0aGUgcHJvdmlkZWQgKmNyZWF0ZSogZnVuY3Rpb25zIHRvIGNyZWF0ZSBhbmQgbG9hZCBpbiBhIHNpbmdsZSBzdGVwIChzZWUgKmNyZWF0ZU5ldHdvcmtGcm9tR3JhcGgqLCAgKmNyZWF0ZU5ldHdvcmtGcm9tSWdyYXBoKiBhbmQgKmNyZWF0ZU5ldHdvcmtGcm9tRGF0YUZyYW1lcyogZnVuY3Rpb25zKS4gQ2hlY2sgb3V0IHRoZSBvdGhlciB2aWduZXR0ZXMgZm9yIG1vcmUgZXhhbXBscy4KCmBgYHtyfQogICAgZGYgPC0gZGF0YS5mcmFtZSAobW9sZWN1bGVUeXBlPWMoJ2tpbmFzZScsJ1RGJywnY3l0b2tpbmUnLCdjeXRva2luZScpLAogICAgICAgICAgICAgICAgICAgICBsb2cyZmM9YygxLjgsMy4wLC0xLjIsLTIuNSksCiAgICAgICAgICAgICAgICAgICAgIHJvdy5uYW1lcyA9IGMoJ0EnLCdCJywnQycsJ0QnKSwgIyByb3cubmFtZXMgPSBub2RlIG5hbWVzCiAgICAgICAgICAgICAgICAgICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSkgICAgICAgIyBpbXBvcnRhbnQgd2hlbiBsb2FkaW5nIHN0cmluZ3MhCiAgICBsb2FkVGFibGVEYXRhIChkZikKYGBgIAoKTm90ZSB0aGF0IGFkZGluZyB0aGUgYXR0cmlidXRlcyBkb2VzIG5vdCBpbiBpdHNlbGYgY2F1c2UgdGhlIGFwcGVhcmFuY2Ugb2YgdGhlIGdyYXBoIHRvIGNoYW5nZS4gU3VjaCBhIGNoYW5nZSByZXF1aXJlcyB0aGF0IHlvdSBzcGVjaWZ5IGFuZCBhcHBseSB2aXN1YWwgc3R5bGUgbWFwcGluZ3MsIHdoaWNoIHdpbGwgYmUgZXhwbGFpbmVkIGluIHRoZSAqbmV4dCogc2VjdGlvbi4gWW91IGNhbiwgaG93ZXZlciwgZXhhbWluZSB0aGVzZSBhdHRyaWJ1dGVzIGluIEN5dG9zY2FwZSwgdXNpbmcgQ3l0b3NjYXBlJ3MgdGhlICoqRGF0YSBQYW5lbCoqIHRvIGRpc3BsYXkgZGF0YSB2YWx1ZXMgYXNzb2NpYXRlZCB3aXRoIHNlbGVjdGVkIG5vZGVzIGltbWVkaWF0ZWx5IGJlbG93IHRoZSBDeXRvc2NhcGUgd2luZG93LgoKCiMjIE1vZGlmeWluZyB0aGUgZGlzcGxheTogIGRlZmF1bHRzIGFuZCBtYXBwaW5ncwoKUkN5MyBwcm92aWRlcyBhbiBlYXN5IHdheSB0byBub3Qgb25seSBjaGFuZ2UgdGhlIGRlZmF1bHQgc3R5bGVzLCBidXQgbW9yZSBpbnRlcmVzdGluZ2x5LCBSQ3kzIGFsc28gcHJvdmlkZXMgZWFzeSBhY2Nlc3MgdG8gKm1hcHBpbmcqIHlvdXIgZGF0YSB0byB2aXN1YWwgc3R5bGVzLCBlLmcuLCBhbGxvd2luZyB0aGUgc2l6ZSwgc2hhcGUgYW5kIGNvbG9yIG9mIG5vZGVzIGFuZCBlZGdlcyB0byBiZSBkZXRlcm1pbmVkIGJ5IHRoZSBkYXRhIHlvdSBoYXZlIGFzc29jaWF0ZWQgd2l0aCB0aG9zZSBub2RlcyBhbmQgZWRnZXMuIAoKRmlyc3QsIGxldCdzIGNoYW5nZSB0aGUgdGhlIGRlZmF1bHRzLiAKCmBgYHtyfQogICAgc2V0Tm9kZVNoYXBlRGVmYXVsdCAoJ09DVEFHT04nKQogICAgc2V0Tm9kZUNvbG9yRGVmYXVsdCAoJyNBQUZGODgnKQogICAgc2V0Tm9kZVNpemVEZWZhdWx0ICAoNjApCiAgICBzZXROb2RlRm9udFNpemVEZWZhdWx0ICgzMCkKYGBgIAoKTm93IHdlIHdpbGwgYWRkIHNvbWUgdmlzdWFsIG1hcHBpbmdzLiAgTGV0J3MgbWFwIGBtb2xlY3VsZVR5cGUnIHRvIG5vZGUgc2hhcGVzLiBGaXJzdCwgd2UgY2FuIHNlZSB3aGljaCBzaGFwZXMgYXJlIGF2YWlsYWJsZSBpbiBDeXRvc2NhcGUsIHRoZW4gd2UgY2FuIGRlZmluZSB0aGUgbWFwcGluZyB3aXRoIHBhaXJlZCBsaXN0cy4gCgpgYGB7cn0KICAgIGdldE5vZGVTaGFwZXMgKCkgICAjIGRpYW1vbmQsIGVsbGlwc2UsIHRyYXBlem9pZCwgdHJpYW5nbGUsIGV0Yy4KICAgIGNvbHVtbiA8LSAnbW9sZWN1bGVUeXBlJwogICAgdmFsdWVzIDwtIGMgKCdraW5hc2UnLCAgJ1RGJywnY3l0b2tpbmUnKQogICAgc2hhcGVzIDwtIGMgKCdESUFNT05EJywgJ1RSSUFOR0xFJywgJ1JFQ1RBTkdMRScpCiAgICBzZXROb2RlU2hhcGVNYXBwaW5nIChjb2x1bW4sIHZhbHVlcywgc2hhcGVzKQpgYGAgCgpUaGUgbm9kZSBzaGFwZSBtYXBwaW5nIGlzIGFuIGV4YW1wbGUgb2YgYSAqZGlzY3JldGUqIG1hcHBpbmcsIHdoZXJlIGEgc3R5bGUgaXMgZGVmaW5lZCBmb3IgZWFjaCwgZGlzY3JldGUgdmFsdWUuICBUaGlzIGlzIHVzZWZ1bCBmb3IgY2F0ZWdvcmljYWwgZGF0YSAobGlrZSB0eXBlKSB3aGVyZSB0aGVyZSBpcyBvbmx5IGEgbGltaXRlZCBzZXQgb2YgcG9zc2libGUgdmFsdWVzLiBUaGlzIGlzIGluIGNvbnRhc3QgdG8gdGhlIG90aGVyIHR3byBvdGhlciB0eXBlcyBvZiBtYXBwaW5nczogKmNvbnRpbnVvdXMqIGFuZCAqcGFzc3Rocm91Z2gqLiBJbiB0aGUgY2FzZSBvZiBleHByZXNzaW9uIHZhbHVlcywgZm9yIGV4YW1wbGUsIHdlIHdpbGwgd2FudCB0byB1c2UgKmNvbnRpbnVvdXMqIG1hcHBpbmcgKGUuZy4sIHRvIG5vZGUgY29sb3IpLCBkZWZpbmluZyBhIHNtYWxsIHNldCBvZiBjb250cm9sIHBvaW50cywgcmF0aGVyIHRoYW4gYW4gZXhwbGljaXQgY29sb3IgZm9yIGVhY2ggcG9zc2libGUgZGF0YSB2YWx1ZS4gQ3l0b3NjYXBlIHdpbGwgc2ltcGx5IGludGVycG9sYXRlIGJldHdlZW4gdGhlIGNvbnRyb2wgcG9pbnRzIHRvIHByb3ZpZGUgYSBncmFkaWVudCBvZiBjb2xvcnMuIExldCdzIHRyeSB0aGF0IG9uZSBub3cgCgpgYGB7cn0KICAgIGNvbHVtbiA8LSAnbG9nMmZjJwogICAgY29udHJvbC5wb2ludHMgPC0gYyAoLTMuMCwgMC4wLCAzLjApCiAgICBjb2xvcnMgPC0gIGMgKCcjNTU4OEREJywgJyNGRkZGRkYnLCAnI0REODg1NScpCiAgICBzZXROb2RlQ29sb3JNYXBwaW5nIChjb2x1bW4sIGNvbnRyb2wucG9pbnRzLCBjb2xvcnMpCmBgYCAKCk5vdGUgdGhhdCB0aGVyZSBhcmUgdGhyZWUgY29sb3JzIGFuZCB0aHJlZSBjb250cm9sIHBvaW50cy4gSG93ZXZlciwgeW91IGNhbiBhbHNvIHNwZWNpZnkgKnR3byBhZGRpdGlvbmFsKiBjb2xvcnMgYmV5b25kIHRoZSBudW1iZXIgb2YgY29udHJvbCBwb2ludHMgaWYgeW91IHdhbnQgdG8gc2V0IGV4dHJlbWUgKG9yIG91dC1vZi1ib3VuZHMpIGNvbG9ycyBmb3IgdmFsdWVzIGxlc3MgdGhhbiBvciBncmVhdGVyIHRoYW4geW91ciBjb250cm9sIHBvaW50cy4KCmBgYHtyfQogICAgY29udHJvbC5wb2ludHMgPC0gYyAoLTIuMCwgMC4wLCAyLjApCiAgICBjb2xvcnMgPC0gIGMgKCcjMjI1NUNDJywgJyM1NTg4REQnLCAnI0ZGRkZGRicsICcjREQ4ODU1JywnI0NDNTUyMicpCiAgICBzZXROb2RlQ29sb3JNYXBwaW5nIChjb2x1bW4sIGNvbnRyb2wucG9pbnRzLCBjb2xvcnMpCmBgYCAKCk5vdywgYWRkIGEgbm9kZSBzaXplIHJ1bGUsIHVzaW5nICpsb2cyZmMqIGFnYWluIGFzIGNvbnRyb2xsaW5nIG5vZGUgdmFsdWVzLgoKYGBge3J9CiAgICBjb250cm9sLnBvaW50cyA9IGMgKC0zLjAsIDIuMCwgMy4wKQogICAgc2l6ZXMgICAgID0gYyAoMjAsIDgwLCA5MCkKICAgIHNldE5vZGVTaXplTWFwcGluZyAoY29sdW1uLCBjb250cm9sLnBvaW50cywgc2l6ZXMpCgpgYGAgCgpJZiB5b3UgcmVjYWxsIHRoZSB0aGlyZCB0eXBlIG9mIG1hcHBpbmcsICpwYXNzdGhyb3VnaCosIHdlIGNhbiBzZWUgaXQgYWxyZWFkeSB3b3JraW5nIGluIG91ciBjdXJyZW50IG5ldHdvcmsgZXhhbXBsZS4gVGhlIG5vZGUgbGFiZWxzISBCeSBkZWZhdWx0LCB0aGUgKm5hbWUqIGNvbHVtbiBpcyBtYXBwZWQgdG8gdGhlIG5vZGUgbGFiZWwgcHJvcGVydHkgdXNpbmcgKnBhc3N0aHJvdWdoKiBsb2dpYzogdGhlIHZhbHVlIGlzIHBhc3NlZCBkaXJlY3RseSB0byB0aGUgc3R5bGUgcHJvcGVydHkuCgo8Y2VudGVyPgohW10oaHR0cHM6Ly9jeXRvc2NhcGUuZ2l0aHViLmlvL2N5dG9zY2FwZS1hdXRvbWF0aW9uL2Zvci1zY3JpcHRlcnMvUi9ub3RlYm9va3MvZGF0YS9pbWcvTXlGaXJzdE5ldHdvcmtfTm9kZVNpemVNYXBwaW5nLnBuZyl7d2lkdGg9NjAlfQo8L2NlbnRlcj4KCgojIyBTZWxlY3Rpbmcgbm9kZXMKCkxldCB1cyBub3cgdHJ5IHNlbGVjdGluZyBub2RlcyBpbiBDeXRvc2NhcGUgZnJvbSBSLiBTZWxlY3QgdGhlIEMgbm9kZSBieSBuYW1lOgoKYGBge3J9CiAgICBzZWxlY3ROb2RlcyAoJ0MnLCduYW1lJykKYGBgCgpgYGB7cn0KICAgIGdldFNlbGVjdGVkTm9kZXMgKCkKYGBgIAoKTm93IHdlIHdpc2ggdG8gZXh0ZW5kIHRoZSBzZWxlY3RlZCBub2RlcyB0byBpbmNsdWRlIHRoZSBmaXJzdCBuZWlnaGJvcnMgb2YgdGhlIGFscmVhZHktc2VsZWN0ZWQgbm9kZSBCLiBUaGlzIGlzIGEgY29tbW9uIG9wZXJhdGlvbjogZm9yIGluc3RhbmNlLCBhZnRlciBzZWxlY3Rpbmcgb25lIG9yIG1vcmUgbm9kZXMgYmFzZWQgb24gZXhwZXJpbWVudGFsIGRhdGEgb3IgYW5ub3RhdGlvbiwgeW91IG1heSB3YW50IHRvIGV4cGxvcmUgdGhlc2UgaW4gdGhlIGNvbnRleHQgb2YgaW50ZXJhY3Rpb24gcGFydG5lcnMgKGluIGEgcHJvdGVpbi1wcm90ZWluIG5ldHdvcmspIG9yIGluIHJlbGF0aW9uIHRvIHVwc3RyZWFtIGFuZCBkb3duc3RyZWFtIHBhcnRuZXJzIGluIGEgc2lnbmFsaW5nIG9yIG1ldGFib2xpYyBuZXR3b3JrLiBUeXBlOgoKYGBge3J9CiAgICBzZWxlY3RGaXJzdE5laWdoYm9ycyAoKQpgYGAgCgpZb3Ugd2lsbCBzZWUgdGhhdCB0aHJlZSBub2RlcyBhcmUgbm93IHNlbGVjdGVkLiBHZXQgdGhlaXIgbmFtZXMgYmFjayB0byBSIGFzIGEgbGlzdDoKCmBgYHtyfQogICAgbm9kZS5uYW1lcyA8LSBnZXRTZWxlY3RlZE5vZGVzICgpCmBgYCAKCkFuZCwgZmluYWxseSwgZGVzZWxlY3Rpb24gd29ya3MgYXMgeW91J2QgZXhwZWN0IGJ5IG1lYW5zIG9mIGEgZ2VuZXJhbCAqY2xlYXJTZWxlY3Rpb24qIGZ1bmN0aW9uOgpgYGB7cn0KICAgIGNsZWFyU2VsZWN0aW9uKCkKICAgID9jbGVhclNlbGVjdGlvbgpgYGAKCiMjIFNhdmluZyBhbmQgZXhwb3J0ClNlc3Npb24gZmlsZXMgc2F2ZSAqZXZlcnl0aGluZyouIEFzIHdpdGggbW9zdCBwcm9qZWN0IHNvZnR3YXJlLCB3ZSByZWNvbW1lbmQgc2F2aW5nIG9mdGVuIQpgYGB7cn0KICAgIHNhdmVTZXNzaW9uKCd2aWduZXR0ZV9zZXNzaW9uJykgIy5jeXMKYGBgCgoqKk5vdGUqKjogSWYgeW91IGRvbid0IHNwZWNpZnkgYSBjb21wbGV0ZSBwYXRoLCB0aGUgZmlsZXMgd2lsbCBiZSBzYXZlZCByZWxhdGl2ZSB0byB5b3VyIGN1cnJlbnQgd29ya2luZyBkaXJlY3RvcnkgaW4gUi4KCiMjIyBTYXZpbmcgaGlnaCByZXNvbHV0aW9uIGltYWdlIGZpbGVzCllvdSBjYW4gZXhwb3J0IGV4dHJlbWVseSBoaWdoIHJlc29sdXRpb24gaW1hZ2VzLCBpbmNsdWRpbmcgdmVjdG9yIGdyYXBoaWMgZm9ybWF0cy4KYGBge3J9CiAgICBmdWxsLnBhdGg9cGFzdGUoZ2V0d2QoKSwndmlnbmV0dGVfaW1hZ2UnLHNlcD0nLycpCiAgICBleHBvcnRJbWFnZShmdWxsLnBhdGgsICdQTkcnLCB6b29tPTIwMCkgIy5wbmcgc2NhbGVkIGJ5IDIwMCUKICAgIGV4cG9ydEltYWdlKGZ1bGwucGF0aCwgJ1BERicpICMucGRmCiAgICA/ZXhwb3J0SW1hZ2UKYGBgCgojIEJyb3dzZSBhdmFpbGFibGUgZnVuY3Rpb25zLCBjb21tYW5kcyBhbmQgYXJndW1lbnRzClJDeTMgZnVuY3Rpb25zCmBgYHtyfQpoZWxwKHBhY2thZ2U9UkN5MykKYGBgCgoKfCBDYXRlZ29yeSB8IERlc2NyaXB0aW9uIHwgRXhhbXBsZXMgfAp8LS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18LS0tLS0tfAp8ICoqQXBwcyoqIHwgSW5zcGVjdGluZyBhbmQgbWFuYWdpbmcgYXBwcyBmb3IgQ3l0b3NjYXBlLiB8IF9pbnN0YWxsQXBwKCkgZGlzYWJsZUFwcCgpIGdldEluc3RhbGxlZEFwcHMoKV8gfAp8ICoqQ29sbGVjdGlvbnMqKiB8IEdldHRpbmcgaW5mb3JtYXRpb24gYWJvdXQgbmV0d29yayBjb2xsZWN0aW9ucy4gfCBfZ2V0Q29sbGVjdGlvbkxpc3QoKSBnZXRDb2xsZWN0aW9uTmV0d29ya3MoKV8gfAp8ICoqQ29tbWFuZHMqKiB8IENvbnN0cnVjdGluZyBhbnkgYXJiaXRyYXJ5IEN5UkVTVCBBUEkgb3IgQ29tbWFuZHMgQVBJIG1ldGhvZCB2aWEgc3RhbmRhcmQgR0VULCBQVVQsIFBPU1QgYW5kIERFTEVURSBwcm90b2NvbHMuIHwgX2N5cmVzdFBPU1QoKSBjb21tYW5kc1BPU1QoKSBjeXJlc3RBUEkoKSBjb21tYW5kc0FQSSgpXyB8CnwgKipDeU5ERXgqKiB8IENvbW11bmljYXRpbmcgd2l0aCBOREV4IGZyb20gd2l0aGluIEN5dG9zY2FwZS4gfCBfaW1wb3J0TmV0d29ya0Zyb21OREV4KCkgZXhwb3J0TmV0d29ya1RvTkRFeCgpXyB8CnwgKipDeXRvc2NhcGUgU3lzdGVtKiogfCBDaGVja2luZyBDeXRvc2NhcGUgU3lzdGVtIGluZm9ybWF0aW9uLCBpbmNsdWRpbmcgdmVyc2lvbnMgYW5kIG1lbW9yeSB1c2FnZS4gfCBfY3l0b3NjYXBlUGluZygpIGN5dG9zY2FwZVZlcnNpb25JbmZvKClfIHwKfCAqKkdyb3VwcyoqIHwgV29ya2luZyB3aXRoIGdyb3VwcyBpbiBDeXRvc2NhcGUuIHwgX2NyZWF0ZUdyb3VwKCkgY29sbGFwc2VHcm91cCgpXyB8CnwgKipMYXlvdXRzKiogfCBQZXJmb3JtaW5nIGxheW91dHMgaW4gYWRkaXRpb24gdG8gZ2V0dGluZyBhbmQgc2V0dGluZyBsYXlvdXQgcHJvcGVydGllcy4gfCBfbGF5b3V0TmV0d29yaygpIGdldExheW91dE5hbWVzKClfIHwKfCAqKk5ldHdvcmtzKiogfCBGdW5jdGlvbnMgZm9yIG5ldHdvcmsgbWFuYWdlbWVudCBhbmQgcmV0cmlldmluZyBpbmZvcm1hdGlvbiBvbiBuZXR3b3Jrcywgbm9kZXMgYW5kIGVkZ2VzLiB8IF9jcmVhdGVOZXR3b3JrRnJvbS4uLigpIGNyZWF0ZS4uLkZyb21OZXR3b3JrKCkgZ2V0TmV0d29ya1N1aWQoKSwgZXhwb3J0TmV0d29yaygpIGdldEFsbE5vZGVzKCkgZ2V0QWxsRWRnZXMoKV8gfAp8ICoqTmV0d29yayBTZWxlY3Rpb24qKiB8IFdvcmtpbmcgd2l0aCBzZWxlY3Rpb24gb2Ygbm9kZXMgYW5kIGVkZ2VzIGluIG5ldHdvcmtzLiB8IF9zZWxlY3ROb2RlcygpIGludmVydE5vZGVTZWxlY3Rpb24oKSBzZWxlY3RGaXJzdE5laWdoYm9ycygpXyB8CnwgKipOZXR3b3JrIFZpZXdzKiogfCBQZXJmb3JtaW5nIHZpZXcgb3BlcmF0aW9ucyBpbiBhZGRpdGlvbiB0byBnZXR0aW5nIGFuZCBzZXR0aW5nIHZpZXcgcHJvcGVydGllcy4gfCBfZ2V0Q3VycmVudFZpZXcoKSBmaXRDb250ZW50KCkgZXhwb3J0SW1hZ2UoKSB0b2dnbGVHcmFwaGljc0RldGFpbHMoKV8gfAp8ICoqU2Vzc2lvbioqIHwgTWFuYWdpbmcgQ3l0b3NjYXBlIHNlc3Npb25zLCBpbmNsdWRpbmcgc2F2ZSwgb3BlbiBhbmQgY2xvc2UuIHwgX29wZW5TZXNzaW9uKCkgc2F2ZVNlc3Npb24oKSBjbG9zZVNlc3Npb24oKV8gfAp8ICoqU3R5bGUgQnlwYXNzZXMqKiB8IFNldHRpbmcgYW5kIGNsZWFyaW5nIGJ5cGFzcyB2YWx1ZXMgZm9yIHZpc3VhbCBwcm9wZXJ0aWVzLiB8IF9zZXROb2RlQ29sb3JCeXBhc3MoKSBzZXRFZGdlTGluZVN0eWxlQnlwYXNzKCkgaGlkZU5vZGVzKClfIHwKfCAqKlN0eWxlIERlZmF1bHRzKiogfCBHZXR0aW5nIGFuZCBzZXR0aW5nIGRlZmF1bHQgdmFsdWVzIGZvciB2aXN1YWwgcHJvcGVydGllcy4gfCBfc2V0Tm9kZVNoYXBlRGVmYXVsdCgpIHNldEVkZ2VMaW5lV2lkdGhEZWZhdWx0KClfIHwKfCAqKlN0eWxlIERlcGVuZGVuY2llcyoqIHwgR2V0dGluZyBhbmQgc2V0dGluZyBzdHlsZSBkZXBlbmRlbmNpZXMuIHwgX2xvY2tOb2RlRGltZW5zaW9ucygpXyB8CnwgKipTdHlsZSBNYXBwaW5ncyoqIHwgRGVmaW5pbmcgbWFwcGluZ3MgYmV0d2VlbiB0YWJsZSBjb2x1bW4gdmFsdWVzIGFuZCB2aXN1YWwgcHJvcGVydGllcy4gfCBfc2V0Tm9kZVNpemVNYXBwaW5nKCkgc2V0RWRnZUNvbG9yTWFwcGluZygpXyB8CnwgKipTdHlsZXMqKiB8IE1hbmFnaW5nIHN0eWxlcyBhbmQgcmV0cmlldmluZyBnZW5lcmFsIGxpc3RzIG9mIHByb3BlcnRpZXMgcmVsZXZhbnQgdG8gbXVsdGlwbGUgc3R5bGUgbW9kZXMuIHwgX2NyZWF0ZVZpc3VhbFN0eWxlKCkgc2V0VmlzdWFsU3R5bGUoKSBleHBvcnRWaXN1YWxTdHlsZXMoKSBnZXRBcnJvd1NoYXBlcygpXyB8CnwgKipTdHlsZSBWYWx1ZXMqKiB8IFJldHJpZXZpbmcgY3VycmVudCB2YWx1ZXMgZm9yIHZpc3VhbCBwcm9wZXJ0aWVzLiB8IF9nZXROb2RlV2lkdGgoKSBnZXRFZGdlQ29sb3IoKSBnZXROZXR3b3JrWm9vbSgpXyB8CnwgKipUYWJsZXMqKiB8IE1hbmFnaW5nIHRhYmxlIGNvbHVtbnMgYW5kIHRhYmxlIGNvbHVtbiBmdW5jdGlvbnMsIGxpa2UgbWFwIGFuZCByZW5hbWUsIGFzIHdlbGwgYXMgbG9hZGluZyBhbmQgZXh0cmFjdGluZyB0YWJsZSBkYXRhIGluIEN5dG9zY2FwZS4gfCBfZ2V0VGFibGVDb2x1bW5zKCkgcmVuYW1lVGFibGVDb2x1bW4oKSBsb2FkVGFibGVEYXRhKCkgbWFwVGFibGVDb2x1bW4oKV8gfAp8ICoqVG9vbHMqKiB8IEZ1bmN0aW9ucyByZWxhdGVkIHRvIGFjdGlvbnMgZm91bmQgaW4gdGhlIFRvb2xzIE1lbnUgaW4gQ3l0b3NjYXBlLiB8IF9jeWJyb3dzZXJEaWFsb2coKSBkaWZmdXNpb25CYXNpYygpXyB8CnwgKipVc2VyIEludGVyZmFjZSoqIHwgRnVuY3Rpb25zIGFmZmVjdGluZyB0aGUgQ3l0b3NjYXBlIHVzZXIgaW50ZXJmYWNlLCBzdWNoIGFzIHBhbmVsIG1hbmFnZW1lbnQuIHwgX2hpZGVQYW5lbCgpIGZsb2F0UGFuZWwoKSBkb2NrUGFuZWwoKV8gfAoKCk9wZW4gc3dhZ2dlciBkb2NzIGZvciBsaXZlIGluc3RhbmNlcyBvZiBDeVJFU1QgQVBJIGFuZCBDb21tYW5kcyBBUEk6CmBgYHtyfQpjeXJlc3RBUEkoKSAgIyBDeVJFU1QgQVBJCmNvbW1hbmRzQVBJKCkgICMgQ29tbWFuZHMgQVBJCmBgYAoKTGlzdCBhdmFpbGFibGUgY29tbWFuZHMgYW5kIGFyZ3VtZW50cyBpbiBSLiBVc2UgImhlbHAiIHRvIGxpc3QgdG9wIGxldmVsOgpgYGB7cn0KY29tbWFuZHNIZWxwKCJoZWxwIikgIApgYGAKCkxpc3QgKipuZXR3b3JrKiogY29tbWFuZHMuIE5vdGUgdGhhdCAiaGVscCIgaXMgb3B0aW9uYWw6CmBgYHtyfQpjb21tYW5kc0hlbHAoImhlbHAgbmV0d29yayIpICAKYGBgCgpMaXN0IGFyZ3VtZW50cyBmb3IgdGhlICoqbmV0d29yayBzZWxlY3QqKiBjb21tYW5kOgpgYGB7cn0KY29tbWFuZHNIZWxwKCJoZWxwIG5ldHdvcmsgc2VsZWN0IikgCmBgYAoKVGhhdCBjb3ZlcnMgdGhlIGJhc2ljcyBvZiBuZXR3b3JrIG1hbmlwdWxhdGlvbi4gQ2hlY2sgb3V0IHRoZSBvdGhlciB2aWduZXR0ZXMgZm9yIGFkZGl0aW9uYWwgYW1kIG1vcmUgY29tcGxleCBleGFtcGxlcy4gQW5kIHdoZW4geW91IGFyZSByZWFkeSB0byB3b3JrIHdpdGggc29tZSByZWFsIGRhdGEsIGNoZWNrIG91dCB0aGUgb3RoZXIgYmFzaWMgYW5kIGFkdmFuY2VkIFIgdHV0b3JpYWxzLCBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL2N5dG9zY2FwZS1hdXRvbWF0aW9uL3RyZWUvbWFzdGVyL2Zvci1zY3JpcHRlcnMvUi4KCiMgTW9yZSBleGFtcGxlcwpEb24ndCBmb3JnZXQgdG8gY2hlY2sgb3V0IHRoZSBvdGhlciB2aWduZXR0ZXMgaW4gdGhpcyBwYWNrYWdlOgpgYGB7cn0KYnJvd3NlVmlnbmV0dGVzKCJSQ3kzIikKYGBgCgpJbiBhZGRpdGlvbiwgdGhlIEN5dG9zY2FwZSB0ZWFtIGlzIGNvbGxlY3Rpbmcgc2NyaXB0cyBmcm9tIHRoZSBjb21tdW5pdHkgaW4gYSBwdWJsaWMgR2l0SHViIHJlcG9zaXRvcnkgYXQgaHR0cHM6Ly9naXRodWIuY29tL2N5dG9zY2FwZS9jeXRvc2NhcGUtYXV0b21hdGlvbi90cmVlL21hc3Rlci9mb3Itc2NyaXB0ZXJzL1IuICAKCiMgRGV2ZWxvcG1lbnQKClRoZSBSQ3kzIHByb2plY3QgY29kZSBhbmQgZG9jdW1lbnRhdGlvbiBpcyBtYWludGFpbmVkIGF0IEdpdEh1YjogaHR0cHM6Ly9naXRodWIuY29tL2N5dG9zY2FwZS9SQ3kzLiBBbGwgYnVncyBhbmQgZmVhdHVyZSByZXF1ZXN0cyBhcmUgdHJhY2tlZCBhcyAqKklzc3VlcyoqLCBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL1JDeTMvaXNzdWVzLiAKCgojIENyZWRpdHMKCiogUGF1bCBTaGFubm9uJ3MgZ2VuZXJvdXMgYWR2aWNlIGFuZCBtZW50b3JzaGlwIHdhcyB2ZXJ5IGltcG9ydGFudCBmb3IgdHJhbnNmb3JtaW5nIHRoaXMgcGFja2FnZSBmcm9tIHVzaW5nIFhNTFJQQyBhbmQgQ3l0b3NjYXBlUlBDIHRvIHVzaW5nIEN5UkVTVC4KKiBEYXZpZCBPdGFzZWssIEtlaWljaGlybyBPbm8gYW5kIEJhcnJ5IERlbWNoYWsga2luZGx5IHByb3ZpZGVkIEN5UkVTVCBhcyB3ZWxsIGFzIGhlbHAgYW5kIHN1cHBvcnQgZm9yIG5ldyBmdW5jdGlvbmFsaXRpZXMgYW5kIGNoYW5nZXMuCiogTWFyayBHcmltZXMgYW5kIFJ1dGggSXNzZXJsaW4ga2luZGx5IHByb3ZpZGVkIGhlbHBmdWwgdXNlciBmZWVkYmFjay4KKiBKdWxpYSBHdXN0YXZzZW4gZ2VuZXJvdXNseSBkZXZlbG9wZWQgdmFyaW91cyB1c2UgY2FzZXMvZXhhbXBsZXMgZm9yIHVzaW5nIFJDeTMgd2l0aCBiaW9sb2dpY2FsIGRhdGEgZHVyaW5nIEdTT0MgMjAxNiwgaHR0cHM6Ly9naXRodWIuY29tL2pvb29saWEvZ3NvY19SY3kzX3ZpZ25ldHRlcy9ibG9iL21hc3Rlci9ibG9nX3Bvc3RfZHJhZnRzL2ZpbmFsX3dvcmtfc3VibWlzc2lvbi5tZC4KKiBUYW5qYSBNdWV0emUgcHJvdmlkZWQgbWFueSB5ZWFycyBvZiBkZXZlbG9wbWVudCwgZGVzaWduLCBtYWludGVuYW5jZSBhbmQgZG9jdW1lbnRhdGlvbiBmb3IgdGhlIFJDeTMgcHJvamVjdC4KKiBBbGwgY29udHJpYnV0b3JzLCBuZXcgYW5kIG9sZCwgYXJlIGR5bmFtaWNhbGx5IGFja25vd2xlZGdlZCBpbiBvdXIgKipDb250cmlidXRvciBHcmFwaCoqLCBodHRwczovL2dpdGh1Yi5jb20vY3l0b3NjYXBlL1JDeTMvZ3JhcGhzL2NvbnRyaWJ1dG9ycwoKIyBSZWZlcmVuY2VzCjEuIFNoYW5ub24gUCwgTWFya2llbCBBLCBPemllciBPLCBCYWxpZ2EgTlMsIFdhbmcgSlQsIFJhbWFnZSBELCBBbWluIE4sIFNjaHdpa293c2tpIEIsIElkZWtlciBULiAyMDAzLiBDeXRvc2NhcGU6IGEgc29mdHdhcmUgZW52aXJvbm1lbnQgZm9yIGludGVncmF0ZWQgbW9kZWxzIG9mIGJpb21vbGVjdWxhciBpbnRlcmFjdGlvbiBuZXR3b3Jrcy4gR2Vub21lIFJlcy4gTm92OzEzKDExKToyNDk4LTUwNAoyLiBIdWJlciBXLCBDYXJleSBWSiwgTG9uZyBMLCBGYWxjb24gUywgR2VudGxlbWFuIFIuIDIwMDcuIEdyYXBocyBpbiBtb2xlY3VsYXIgYmlvbG9neS4gQk1DIEJpb2luZm9ybWF0aWNzLiAyMDA3IFNlcCAyNzs4LgozLiBPbm8gSywgTXVldHplIFQsIEtvbGlzaG92c2tpIEcsIFNoYW5ub24gUCwgRGVtY2hhaywgQi4gQ3lSRVNUOiBUdXJib2NoYXJnaW5nIEN5dG9zY2FwZSBBY2Nlc3MgZm9yIEV4dGVybmFsIFRvb2xzIHZpYSBhIFJFU1RmdWwgQVBJIFt2ZXJzaW9uIDE7IHJlZmVyZWVzOiAyIGFwcHJvdmVkXS4gRjEwMDBSZXNlYXJjaCAyMDE1LCA0OjQ3OC4K