General instructions (read before running the code snippets)

In this workflow, we will run identify affected pathways in two pancreatic cancer subtypes, and visualize the data on the pathway models.

Setup

Loading libraries

options(connectionObserver = NULL)
# uncomment if you need to install the libraries
#BiocManager::install(c("GO.db","DO.db","readr","here","dplyr","tibble","clusterProfiler","org.Hs.eg.db","tidyverse","rWikiPathways","RCy3","RColorBrewer","EnhancedVolcano","reshape","data.table","svglite","VennDiagram","BridgeDbR","rstudioapi"))


library(readr)
library(here)
library(dplyr)
library(tibble)
library(clusterProfiler)
library(org.Hs.eg.db)
library(tidyverse)
library(rWikiPathways)
library(RCy3)
library(RColorBrewer)
library(EnhancedVolcano)
library(reshape)
library(data.table)
library(svglite)
library(VennDiagram)
library(BridgeDbR)
library(rstudioapi)
setwd(dirname(getActiveDocumentContext()$path))

Differential expression data visualization

We will use a publicly available dataset, which identified two different subtypes of pancreatic cancer - classical and basal (GSE71729).

First, let’s import the data and use a volcano plot to visualize the result of the differential gene expression analysis result, and use a Venn diagram to study how many differentially expressed genes are shared between the subtypes.

Interpretation

  • Q1: Are there more up- or down-regulated genes? What cutoffs have been used for pvalue and log2FC (see line 63)?
  • Q2: What are the most significantly changed genes? Do a quick Pubmed search to see if they have a known link to pancreatic cancer.
  • Q3: How many differentially expressed genes are shared between the two subtypes?

Pathway enrichment analysis

We will perform pathway enrichment with the gene sets of all pathway models in WikiPathways (human only).

gmt <- rWikiPathways::downloadPathwayArchive(organism = "Homo sapiens",format = "gmt")
wp2gene <- readPathwayGMT(gmt)
wpid2gene <- wp2gene %>% dplyr::select(wpid,gene) #TERM2GENE
wpid2name <- wp2gene %>% dplyr::select(wpid,name) #TERM2NAME

bkgd.genes <- unique(data.panc[,c(1,2)])

Interpretation

  • Q4: How many pathway models are in the collection? (wpid2name)

The clusterProfiler R-package is used to perform overrepresentation analysis (ORA). The function can be easily replaced to use other enrichment methods (GSEA / rSEA / etc). We will run the analysis separately for basal and classical subtype.

ewp.basal <- clusterProfiler::enricher(
  deg.basal$GeneId,
  universe = bkgd.genes$GeneId,
  pAdjustMethod = "fdr",
  pvalueCutoff = 0.05,
  qvalueCutoff = 0.02,
  TERM2GENE = wpid2gene,
  TERM2NAME = wpid2name)
ewp.basal.res <- as.data.frame(ewp.basal) 

# number of genes measured in pathways
length(ewp.basal@universe)
# number of DEG in pathways
length(deg.basal$GeneId[deg.basal$GeneId %in% unique(wp2gene$gene)])

num.pathways.basal <- dim(ewp.basal.res)[1]

# export enrichment result
png('output1/basal_barplot.png', width = 1200, height=1000)
ggplot(ewp.basal[1:num.pathways.basal], aes(x=reorder(Description, -pvalue), y=Count)) +
  geom_bar(stat ="identity", fill="#BA8CD7") +
  coord_flip() +
  labs(x="", y="Basal DEG gene count", fill="") +
  theme(axis.text=element_text(size=25)) + 
  theme(legend.position="none")
dev.off()
write.table(ewp.basal.res, file="output1/basal_enrich_res.txt", sep="\t", quote=FALSE, row.names = FALSE)

Interpretation

  • Q5: How many pathways are altered in the basal subtype and how do they link to pancreatic cancer (expected or unexpected)?
ewp.classical <- clusterProfiler::enricher(
  deg.classical$GeneId,
  universe = bkgd.genes$GeneId,
  pAdjustMethod = "fdr",
  pvalueCutoff = 0.05,
  qvalueCutoff = 0.2,
  TERM2GENE = wpid2gene,
  TERM2NAME = wpid2name)
ewp.classical.res <- as.data.frame(ewp.classical) 

# number of genes measured in pathways
length(ewp.classical@universe)
# number of DEG in pathways
length(deg.classical$GeneId[deg.classical$GeneId %in% unique(wp2gene$gene)])

num.pathways.classical <- dim(ewp.classical.res)[1]

# export enrichment result
png('output1/classical_barplot.png', width = 1200, height=1000)
ggplot(ewp.classical[1:num.pathways.classical], aes(x=reorder(Description, -pvalue), y=Count)) +
  geom_bar(stat ="identity", fill="#BA8CD7") +
  coord_flip() +
  labs(x="", y="Classical DEG gene count", fill="") +
  theme(axis.text=element_text(size=25)) + 
  theme(legend.position="none")
dev.off()
write.table(ewp.classical.res, file="output1/classical_enrich_res.txt", sep="\t", quote=FALSE, row.names = FALSE)

Interpretation

  • Q6: How many pathways are altered in the classical subtype and how do they link to pancreatic cancer (expected or unexpected)?

Venn diagrams

venn.diagram(x = list(ewp.basal.res$ID, ewp.classical.res$ID),
  category.names = c("Basal" , "Classical"),
  filename = 'output1/venn_pathways.png',
  output=TRUE,
  col=c("#440154ff", '#21908dff'),
  fill = c(alpha("#440154ff",0.3), alpha('#21908dff',0.3)),
  cex = 1.5,
)

Interpretation

  • Q7: How many pathways are altered in both subtypes?

Pathway visualization

The pathways can then be visualized with the gene expression data as shown with the “Vitamin D receptor pathway” (WP2877) pathway from WikiPathways. The pathway was altered in both subtypes.

You can easily replace the WikiPathways identifier in line 209 with another pathway of interest and visualize the data on that pathway.

RCy3::cytoscapePing()
RCy3::installApp(c("wikipathways","CyTargetLinker"))

RCy3::commandsRun('wikipathways import-as-pathway id=WP2877') 
toggleGraphicsDetails()
RCy3::mapTableColumn("Ensembl", "Homo sapiens", "Ensembl", "Entrez Gene")
loadTableData(data.panc, data.key.column = "GeneId", table.key.column = "Entrez Gene")

RCy3::installApp("enhancedGraphics")
RCy3::copyVisualStyle("WikiPathways", "my_style_heatmap")

RCy3::setNodeCustomHeatMapChart(c("B_logFC","C_logFC"), slot = 2, style.name = "my_style_heatmap", colors = c("#CC3300","#FFFFFF","#6699FF","#CCCCCC"))

RCy3::setVisualStyle("my_style_heatmap")

# Saving output
#png.file <- file.path("figures/PathwayVisualization.png")
#exportImage(png.file,'PNG', zoom = 500)
#cys.file <- file.path(getwd(),"output/PathwayVisualization.cys")
#saveSession(cys.file) 
#comment following line if you want to manipulate the visualization in Cytoscape
#RCy3::closeSession(save.before.closing = F)

Interpretation

  • Q8: Do you see any differences between the two subtypes in the vitamin D receptor pathway? (left is basal and right is classical)

Pathway overlap visualization

There is often crosstalk and overlap between pathways enriched in gene expression analyses. The following step visualizes the overlap between the enriched pathways in a pathway-gene network.

The genes not present in any pathway are included in the visualization but can be filtered in a follow-up step if preferred.

pwy <- unique(ewp.basal.res[,c(1,2)])
colnames(pwy) <- c("id","label")
pwy$type <- 'pathway'
edges <- wpid2gene[wpid2gene$wpid %in% pwy$id,]
colnames(edges) <- c("source", "target")
genes <- unique(deg.basal)
colnames(genes) <- c("id","label")
genes <- transform(genes, id = as.character(id))
genes$type <- 'gene'
edges <- unique(edges[edges$target %in% genes$id,])
genes <- genes[genes$id %in% edges$target,]
nodes <- dplyr::bind_rows(genes, pwy)
rownames(nodes) <- NULL
createNetworkFromDataFrames(nodes=nodes,edges=edges,title="Pathway-Gene-Associations", collection="PathwayGeneCrosstalk")
loadTableData(data.panc, data.key.column = "GeneId", table.key.column = "id")
# Visual style
RCy3::copyVisualStyle("default","wp.vis")
RCy3::setNodeLabelMapping("label", style.name="wp.vis")
RCy3::lockNodeDimensions(TRUE, style.name="wp.vis")
RCy3::setNodeShapeMapping('type', c('gene','pathway'), c("ellipse","hexagon"), style.name="wp.vis")
RCy3::setNodeSizeMapping('type', c('gene','pathway'), c(40,25), mapping.type = "d", style.name = "wp.vis")
data.values<-c(-1,0,1) 
node.colors <- c(rev(brewer.pal(length(data.values), "RdBu")))
setNodeColorMapping("B_logFC", data.values, node.colors, default.color = "#99FF99", style.name = "wp.vis")
RCy3::setVisualStyle("wp.vis")
RCy3::toggleGraphicsDetails()

# Saving output
svg.file <- file.path(getwd(), "output1/PathwayCrosstalk.svg")
exportImage(svg.file,'SVG')
png.file <- file.path(getwd(), "output1/PathwayCrosstalk.png")
exportImage(png.file,'PNG', zoom = 500)
cys.file <- file.path(getwd(), "output1/PathwayCrosstalk.cys")
saveSession(cys.file) 
#comment following line if you want to manipulate the visualization in Cytoscape
#RCy3::closeSession(save.before.closing = F)

Interpretation

  • Q9: Do the affected pathways share any genes?

Drug target information

Next, we will add information about known drug-target interactions for the genes in the affected pathways using information from DrugBank using the CyTargetLinker app.

We will show this for the classical subtype.

RCy3::cytoscapePing()
installApp('CyTargetLinker') 

pwy <- unique(ewp.classical.res[,c(1,2)])
colnames(pwy) <- c("id","label")
pwy$type <- 'pathway'
edges <- wpid2gene[wpid2gene$wpid %in% pwy$id,]
colnames(edges) <- c("source", "target")
genes <- unique(deg.basal)
colnames(genes) <- c("id","label")
genes <- transform(genes, id = as.character(id))
genes$type <- 'gene'
edges <- unique(edges[edges$target %in% genes$id,])
genes <- genes[genes$id %in% edges$target,]
nodes <- dplyr::bind_rows(genes, pwy)
rownames(nodes) <- NULL
createNetworkFromDataFrames(nodes=nodes,edges=edges,title="Pathway-Gene-Associations", collection="PathwayGeneCrosstalk")
loadTableData(data.panc, data.key.column = "GeneId", table.key.column = "id")
# Visual style
RCy3::copyVisualStyle("default","wp.vis")
RCy3::setNodeLabelMapping("label", style.name="wp.vis")
RCy3::lockNodeDimensions(TRUE, style.name="wp.vis")
RCy3::setNodeShapeMapping('type', c('gene','pathway'), c("ellipse","hexagon"), style.name="wp.vis")
RCy3::setNodeSizeMapping('type', c('gene','pathway'), c(40,25), mapping.type = "d", style.name = "wp.vis")
data.values<-c(-1,0,1) 
node.colors <- c(rev(brewer.pal(length(data.values), "RdBu")))
setNodeColorMapping("C_logFC", data.values, node.colors, default.color = "#99FF99", style.name = "wp.vis")
RCy3::setVisualStyle("wp.vis")
RCy3::toggleGraphicsDetails()

drugbank <- file.path(getwd(), "data/drugbank-5.1.0.xgmml")

# run CyTargetLinker
commandsRun(paste0('cytargetlinker extend idAttribute="id" linkSetFiles="', drugbank, '"'))
commandsRun('cytargetlinker applyLayout network="current"')
RCy3::setVisualStyle("wp.vis")

#let's change the visualization of the drugs in the network using the ByPass option
selected <- RCy3::selectNodes(nodes="drug", by.col = "CTL.Type")
RCy3::setNodeShapeBypass(node.names = selected$nodes, new.shapes = "Triangle")
RCy3::setNodeColorBypass(node.names = selected$nodes, "#FFFFCE")
RCy3::setNodeBorderColorBypass(node.names = selected$nodes, "#000000")
RCy3::setNodeBorderWidthBypass(node.names = selected$nodes, 4)
RCy3::clearSelection()
RCy3::toggleGraphicsDetails()

Interpretation

  • Q10: Are there proteins targeted by many known drugs? Can you find supporting literature regarding these genes or drugs in the context of pancreatic cancer?
LS0tDQp0aXRsZTogIlBhbmNDYW5OZXQgLSBXb3JrZmxvdyAxIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQphdXRob3I6ICJNYXJ0aW5hIFN1bW1lci1LdXRtb24iDQpkYXRlOiAiMSBTZXB0ZW1iZXIgMjAyMSINCnZlcnNpb246IDEuMA0KbGljZW5zZTogIk1JVCBMaWNlbnNlIg0KLS0tDQoNCiMgR2VuZXJhbCBpbnN0cnVjdGlvbnMgKHJlYWQgYmVmb3JlIHJ1bm5pbmcgdGhlIGNvZGUgc25pcHBldHMpDQpJbiB0aGlzIHdvcmtmbG93LCB3ZSB3aWxsIHJ1biBpZGVudGlmeSBhZmZlY3RlZCBwYXRod2F5cyBpbiB0d28gcGFuY3JlYXRpYyBjYW5jZXIgc3VidHlwZXMsIGFuZCB2aXN1YWxpemUgdGhlIGRhdGEgb24gdGhlIHBhdGh3YXkgbW9kZWxzLiANCg0KKiBUaGUgc2NyaXB0IGNvbnRhaW5zIHNldmVyYWwgY29kZSBzbmlwcGV0cyB3aGljaCBzaG91bGQgYmUgcnVuIG9uZSBhZnRlciB0aGUgb3RoZXIuIA0KKiBNYWtlIHN1cmUgYWxsIHRoZSByZXF1aXJlZCBwYWNrYWdlcyBhcmUgaW5zdGFsbGVkIGJlZm9yZWhhbmQgKEJpb2NNYW5hZ2VyOjppbnN0YWxsKC4uLikpLiANCiogTWFrZSBzdXJlIHlvdSBoYXZlIEN5dG9zY2FwZSBpbnN0YWxsZWQgKHZlcnNpb24gMy44LjArKSBhbmQgcnVubmluZyBiZWZvcmUgeW91IHN0YXJ0IHJ1bm5pbmcgdGhlIHNjcmlwdC4gDQoNCiMgU2V0dXANCg0KTG9hZGluZyBsaWJyYXJpZXMNCmBgYHtyfQ0Kb3B0aW9ucyhjb25uZWN0aW9uT2JzZXJ2ZXIgPSBOVUxMKQ0KIyB1bmNvbW1lbnQgaWYgeW91IG5lZWQgdG8gaW5zdGFsbCB0aGUgbGlicmFyaWVzDQojQmlvY01hbmFnZXI6Omluc3RhbGwoYygiR08uZGIiLCJETy5kYiIsInJlYWRyIiwiaGVyZSIsImRwbHlyIiwidGliYmxlIiwiY2x1c3RlclByb2ZpbGVyIiwib3JnLkhzLmVnLmRiIiwidGlkeXZlcnNlIiwicldpa2lQYXRod2F5cyIsIlJDeTMiLCJSQ29sb3JCcmV3ZXIiLCJFbmhhbmNlZFZvbGNhbm8iLCJyZXNoYXBlIiwiZGF0YS50YWJsZSIsInN2Z2xpdGUiLCJWZW5uRGlhZ3JhbSIsIkJyaWRnZURiUiIsInJzdHVkaW9hcGkiKSkNCg0KDQpsaWJyYXJ5KHJlYWRyKQ0KbGlicmFyeShoZXJlKQ0KbGlicmFyeShkcGx5cikNCmxpYnJhcnkodGliYmxlKQ0KbGlicmFyeShjbHVzdGVyUHJvZmlsZXIpDQpsaWJyYXJ5KG9yZy5Icy5lZy5kYikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShyV2lraVBhdGh3YXlzKQ0KbGlicmFyeShSQ3kzKQ0KbGlicmFyeShSQ29sb3JCcmV3ZXIpDQpsaWJyYXJ5KEVuaGFuY2VkVm9sY2FubykNCmxpYnJhcnkocmVzaGFwZSkNCmxpYnJhcnkoZGF0YS50YWJsZSkNCmxpYnJhcnkoc3ZnbGl0ZSkNCmxpYnJhcnkoVmVubkRpYWdyYW0pDQpsaWJyYXJ5KEJyaWRnZURiUikNCmxpYnJhcnkocnN0dWRpb2FwaSkNCg0KYGBgDQoNCg0KYGBge3J9DQpzZXR3ZChkaXJuYW1lKGdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgpKQ0KYGBgDQoNCiMjIERpZmZlcmVudGlhbCBleHByZXNzaW9uIGRhdGEgdmlzdWFsaXphdGlvbg0KDQpXZSB3aWxsIHVzZSBhIHB1YmxpY2x5IGF2YWlsYWJsZSBkYXRhc2V0LCB3aGljaCBpZGVudGlmaWVkIHR3byBkaWZmZXJlbnQgc3VidHlwZXMgb2YgcGFuY3JlYXRpYyBjYW5jZXIgLSBjbGFzc2ljYWwgYW5kIGJhc2FsIChHU0U3MTcyOSkuIA0KDQpGaXJzdCwgbGV0J3MgaW1wb3J0IHRoZSBkYXRhIGFuZCB1c2UgYSB2b2xjYW5vIHBsb3QgdG8gdmlzdWFsaXplIHRoZSByZXN1bHQgb2YgdGhlIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMgcmVzdWx0LCBhbmQgdXNlIGEgVmVubiBkaWFncmFtIHRvIHN0dWR5IGhvdyBtYW55IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhcmUgc2hhcmVkIGJldHdlZW4gdGhlIHN1YnR5cGVzLg0KDQpgYGB7ciwgZWNobz1GQUxTRSxyZXN1bHRzPSdoaWRlJyxmaWcua2VlcD0nYWxsJ30NCg0KZGF0YXNldCA8LSByZWFkLmRlbGltKCJkYXRhL0dTRTcxNzI5LWRhdGFzZXQudHh0IikNCg0KIyBmaWx0ZXIgZ2VuZXMgd2l0aG91dCBFbnRyZXogR2VuZSBpZGVudGlmaWVyDQpkYXRhLnBhbmMgPC0gZGF0YXNldCAlPiUgdGlkeXI6OmRyb3BfbmEoRW50cmV6LkdlbmUpDQpjb2xuYW1lcyhkYXRhLnBhbmMpWzJdIDwtICJHZW5lTmFtZSINCmNvbG5hbWVzKGRhdGEucGFuYylbMV0gPC0gIkdlbmVJZCINCg0KcG5nKCdvdXRwdXQxL3Z1bGNhbm9wbG90LWNsYXNzaWMucG5nJykNCkVuaGFuY2VkVm9sY2FubyhzdWJzZXQoZGF0YS5wYW5jLCBzZWxlY3Q9YygxOjcpKSwgdGl0bGUgPSAiQ2xhc3NpYyBzdWJ0eXBlIiwgbGFiID0gZGF0YS5wYW5jJEdlbmVOYW1lLCBsYWJTaXplID0gMywgeCA9ICdDX2xvZ0ZDJywgeSA9ICdDX1AuVmFsdWUnLCBwQ3V0b2ZmID0gMC4wNSwgRkNjdXRvZmYgPSAwLjU4NSkNCmRldi5vZmYoKQ0KDQpwbmcoJ291dHB1dDEvdnVsY2Fub3Bsb3QtYmFzYWwucG5nJykNCkVuaGFuY2VkVm9sY2FubyhzdWJzZXQoZGF0YS5wYW5jLCBzZWxlY3Q9YygxLDIsODoxMikpLCB0aXRsZSA9ICJCYXNhbCBzdWJ0eXBlIiwgbGFiID0gZGF0YS5wYW5jJEdlbmVOYW1lLCBsYWJTaXplID0gMywgeCA9ICdCX2xvZ0ZDJywgeSA9ICdCX1AuVmFsdWUnLCBwQ3V0b2ZmID0gMC4wNSwgRkNjdXRvZmYgPSAwLjU4NSkNCmRldi5vZmYoKQ0KDQpkZWcuYmFzYWwgPC0gdW5pcXVlKGRhdGEucGFuY1shaXMubmEoZGF0YS5wYW5jJEJfUC5WYWx1ZSkgJiBkYXRhLnBhbmMkQl9QLlZhbHVlIDwgMC4wNSAmIGFicyhkYXRhLnBhbmMkQl9sb2dGQykgPiAwLjU4LGMoMSwyKV0pDQpiYXNhbC51cCA8LSB1bmlxdWUoZGF0YS5wYW5jWyFpcy5uYShkYXRhLnBhbmMkQl9QLlZhbHVlKSAmIGRhdGEucGFuYyRCX1AuVmFsdWUgPCAwLjA1ICYgZGF0YS5wYW5jJEJfbG9nRkMgPiAwLjU4LGMoMSwyKV0pDQpiYXNhbC5kb3duIDwtIHVuaXF1ZShkYXRhLnBhbmNbIWlzLm5hKGRhdGEucGFuYyRCX1AuVmFsdWUpICYgZGF0YS5wYW5jJEJfUC5WYWx1ZSA8IDAuMDUgJiBkYXRhLnBhbmMkQl9sb2dGQyA8IC0wLjU4LGMoMSwyKV0pDQoNCmRlZy5jbGFzc2ljYWwgPC0gdW5pcXVlKGRhdGEucGFuY1shaXMubmEoZGF0YS5wYW5jJENfUC5WYWx1ZSkgJiBkYXRhLnBhbmMkQ19QLlZhbHVlIDwgMC4wNSAmIGFicyhkYXRhLnBhbmMkQ19sb2dGQykgPiAwLjU4LGMoMSwyKV0pDQpjbGFzc2ljYWwudXAgPC0gdW5pcXVlKGRhdGEucGFuY1shaXMubmEoZGF0YS5wYW5jJENfUC5WYWx1ZSkgJiBkYXRhLnBhbmMkQ19QLlZhbHVlIDwgMC4wNSAmIGRhdGEucGFuYyRDX2xvZ0ZDID4gMC41OCxjKDEsMildKQ0KY2xhc3NpY2FsLmRvd24gPC0gdW5pcXVlKGRhdGEucGFuY1shaXMubmEoZGF0YS5wYW5jJENfUC5WYWx1ZSkgJiBkYXRhLnBhbmMkQ19QLlZhbHVlIDwgMC4wNSAmIGRhdGEucGFuYyRDX2xvZ0ZDIDwgLTAuNTgsYygxLDIpXSkNCg0KdmVubi5kaWFncmFtKHggPSBsaXN0KGJhc2FsLnVwJEdlbmVJZCwgYmFzYWwuZG93biRHZW5lSWQsIGNsYXNzaWNhbC51cCRHZW5lSWQsIGNsYXNzaWNhbC5kb3duJEdlbmVJZCksDQogIGNhdGVnb3J5Lm5hbWVzID0gYygiQmFzYWwgdXAiLCAiQmFzYWwgZG93biIgLCJDbGFzc2ljYWwgdXAiLCAiQ2xhc3NpY2FsIGRvd24iKSwNCiAgZmlsZW5hbWUgPSAnb3V0cHV0MS92ZW5uX2dlbmVzLnBuZycsDQogIG91dHB1dD1GQUxTRSwNCiAgY29sPWMoIiM0NDAxNTRmZiIsIiM0NDAxNTRmZiIsICcjMjE5MDhkZmYnLCcjMjE5MDhkZmYnKSwNCiAgZmlsbCA9IGMoYWxwaGEoIiM0NDAxNTRmZiIsMC4zKSxhbHBoYSgiIzQ0MDE1NGZmIiwwLjMpLCBhbHBoYSgnIzIxOTA4ZGZmJywwLjMpLGFscGhhKCcjMjE5MDhkZmYnLDAuMykpLA0KICBjZXggPSAxLjUsDQopDQpgYGANCg0KPiBJbnRlcnByZXRhdGlvbg0KDQotICoqUTEqKjogQXJlIHRoZXJlIG1vcmUgdXAtIG9yIGRvd24tcmVndWxhdGVkIGdlbmVzPyBXaGF0IGN1dG9mZnMgaGF2ZSBiZWVuIHVzZWQgZm9yIHB2YWx1ZSBhbmQgbG9nMkZDIChzZWUgbGluZSA2Myk/DQotICoqUTIqKjogV2hhdCBhcmUgdGhlIG1vc3Qgc2lnbmlmaWNhbnRseSBjaGFuZ2VkIGdlbmVzPyBEbyBhIHF1aWNrIFB1Ym1lZCBzZWFyY2ggdG8gc2VlIGlmIHRoZXkgaGF2ZSBhIGtub3duIGxpbmsgdG8gcGFuY3JlYXRpYyBjYW5jZXIuDQotICoqUTMqKjogSG93IG1hbnkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIGFyZSBzaGFyZWQgYmV0d2VlbiB0aGUgdHdvIHN1YnR5cGVzPw0KDQojIyBQYXRod2F5IGVucmljaG1lbnQgYW5hbHlzaXMNCg0KV2Ugd2lsbCBwZXJmb3JtIHBhdGh3YXkgZW5yaWNobWVudCB3aXRoIHRoZSBnZW5lIHNldHMgb2YgYWxsIHBhdGh3YXkgbW9kZWxzIGluIFdpa2lQYXRod2F5cyAoaHVtYW4gb25seSkuDQoNCmBgYHtyfQ0KZ210IDwtIHJXaWtpUGF0aHdheXM6OmRvd25sb2FkUGF0aHdheUFyY2hpdmUob3JnYW5pc20gPSAiSG9tbyBzYXBpZW5zIixmb3JtYXQgPSAiZ210IikNCndwMmdlbmUgPC0gcmVhZFBhdGh3YXlHTVQoZ210KQ0Kd3BpZDJnZW5lIDwtIHdwMmdlbmUgJT4lIGRwbHlyOjpzZWxlY3Qod3BpZCxnZW5lKSAjVEVSTTJHRU5FDQp3cGlkMm5hbWUgPC0gd3AyZ2VuZSAlPiUgZHBseXI6OnNlbGVjdCh3cGlkLG5hbWUpICNURVJNMk5BTUUNCg0KYmtnZC5nZW5lcyA8LSB1bmlxdWUoZGF0YS5wYW5jWyxjKDEsMildKQ0KYGBgDQoNCj4gSW50ZXJwcmV0YXRpb24NCg0KLSAqKlE0Kio6IEhvdyBtYW55IHBhdGh3YXkgbW9kZWxzIGFyZSBpbiB0aGUgY29sbGVjdGlvbj8gKHdwaWQybmFtZSkNCg0KDQpUaGUgY2x1c3RlclByb2ZpbGVyIFItcGFja2FnZSBpcyB1c2VkIHRvIHBlcmZvcm0gb3ZlcnJlcHJlc2VudGF0aW9uIGFuYWx5c2lzIChPUkEpLiBUaGUgZnVuY3Rpb24gY2FuIGJlIGVhc2lseSByZXBsYWNlZCB0byB1c2Ugb3RoZXIgZW5yaWNobWVudCBtZXRob2RzIChHU0VBIC8gclNFQSAvIGV0YykuIFdlIHdpbGwgcnVuIHRoZSBhbmFseXNpcyBzZXBhcmF0ZWx5IGZvciBiYXNhbCBhbmQgY2xhc3NpY2FsIHN1YnR5cGUuDQoNCmBgYHtyfQ0KZXdwLmJhc2FsIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoZXIoDQogIGRlZy5iYXNhbCRHZW5lSWQsDQogIHVuaXZlcnNlID0gYmtnZC5nZW5lcyRHZW5lSWQsDQogIHBBZGp1c3RNZXRob2QgPSAiZmRyIiwNCiAgcHZhbHVlQ3V0b2ZmID0gMC4wNSwNCiAgcXZhbHVlQ3V0b2ZmID0gMC4wMiwNCiAgVEVSTTJHRU5FID0gd3BpZDJnZW5lLA0KICBURVJNMk5BTUUgPSB3cGlkMm5hbWUpDQpld3AuYmFzYWwucmVzIDwtIGFzLmRhdGEuZnJhbWUoZXdwLmJhc2FsKSANCg0KIyBudW1iZXIgb2YgZ2VuZXMgbWVhc3VyZWQgaW4gcGF0aHdheXMNCmxlbmd0aChld3AuYmFzYWxAdW5pdmVyc2UpDQojIG51bWJlciBvZiBERUcgaW4gcGF0aHdheXMNCmxlbmd0aChkZWcuYmFzYWwkR2VuZUlkW2RlZy5iYXNhbCRHZW5lSWQgJWluJSB1bmlxdWUod3AyZ2VuZSRnZW5lKV0pDQoNCm51bS5wYXRod2F5cy5iYXNhbCA8LSBkaW0oZXdwLmJhc2FsLnJlcylbMV0NCg0KIyBleHBvcnQgZW5yaWNobWVudCByZXN1bHQNCnBuZygnb3V0cHV0MS9iYXNhbF9iYXJwbG90LnBuZycsIHdpZHRoID0gMTIwMCwgaGVpZ2h0PTEwMDApDQpnZ3Bsb3QoZXdwLmJhc2FsWzE6bnVtLnBhdGh3YXlzLmJhc2FsXSwgYWVzKHg9cmVvcmRlcihEZXNjcmlwdGlvbiwgLXB2YWx1ZSksIHk9Q291bnQpKSArDQogIGdlb21fYmFyKHN0YXQgPSJpZGVudGl0eSIsIGZpbGw9IiNCQThDRDciKSArDQogIGNvb3JkX2ZsaXAoKSArDQogIGxhYnMoeD0iIiwgeT0iQmFzYWwgREVHIGdlbmUgY291bnQiLCBmaWxsPSIiKSArDQogIHRoZW1lKGF4aXMudGV4dD1lbGVtZW50X3RleHQoc2l6ZT0yNSkpICsgDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpDQpkZXYub2ZmKCkNCndyaXRlLnRhYmxlKGV3cC5iYXNhbC5yZXMsIGZpbGU9Im91dHB1dDEvYmFzYWxfZW5yaWNoX3Jlcy50eHQiLCBzZXA9Ilx0IiwgcXVvdGU9RkFMU0UsIHJvdy5uYW1lcyA9IEZBTFNFKQ0KDQpgYGANCj4gSW50ZXJwcmV0YXRpb24NCg0KLSAqKlE1Kio6IEhvdyBtYW55IHBhdGh3YXlzIGFyZSBhbHRlcmVkIGluIHRoZSBiYXNhbCBzdWJ0eXBlIGFuZCBob3cgZG8gdGhleSBsaW5rIHRvIHBhbmNyZWF0aWMgY2FuY2VyIChleHBlY3RlZCBvciB1bmV4cGVjdGVkKT8NCg0KYGBge3J9DQpld3AuY2xhc3NpY2FsIDwtIGNsdXN0ZXJQcm9maWxlcjo6ZW5yaWNoZXIoDQogIGRlZy5jbGFzc2ljYWwkR2VuZUlkLA0KICB1bml2ZXJzZSA9IGJrZ2QuZ2VuZXMkR2VuZUlkLA0KICBwQWRqdXN0TWV0aG9kID0gImZkciIsDQogIHB2YWx1ZUN1dG9mZiA9IDAuMDUsDQogIHF2YWx1ZUN1dG9mZiA9IDAuMiwNCiAgVEVSTTJHRU5FID0gd3BpZDJnZW5lLA0KICBURVJNMk5BTUUgPSB3cGlkMm5hbWUpDQpld3AuY2xhc3NpY2FsLnJlcyA8LSBhcy5kYXRhLmZyYW1lKGV3cC5jbGFzc2ljYWwpIA0KDQojIG51bWJlciBvZiBnZW5lcyBtZWFzdXJlZCBpbiBwYXRod2F5cw0KbGVuZ3RoKGV3cC5jbGFzc2ljYWxAdW5pdmVyc2UpDQojIG51bWJlciBvZiBERUcgaW4gcGF0aHdheXMNCmxlbmd0aChkZWcuY2xhc3NpY2FsJEdlbmVJZFtkZWcuY2xhc3NpY2FsJEdlbmVJZCAlaW4lIHVuaXF1ZSh3cDJnZW5lJGdlbmUpXSkNCg0KbnVtLnBhdGh3YXlzLmNsYXNzaWNhbCA8LSBkaW0oZXdwLmNsYXNzaWNhbC5yZXMpWzFdDQoNCiMgZXhwb3J0IGVucmljaG1lbnQgcmVzdWx0DQpwbmcoJ291dHB1dDEvY2xhc3NpY2FsX2JhcnBsb3QucG5nJywgd2lkdGggPSAxMjAwLCBoZWlnaHQ9MTAwMCkNCmdncGxvdChld3AuY2xhc3NpY2FsWzE6bnVtLnBhdGh3YXlzLmNsYXNzaWNhbF0sIGFlcyh4PXJlb3JkZXIoRGVzY3JpcHRpb24sIC1wdmFsdWUpLCB5PUNvdW50KSkgKw0KICBnZW9tX2JhcihzdGF0ID0iaWRlbnRpdHkiLCBmaWxsPSIjQkE4Q0Q3IikgKw0KICBjb29yZF9mbGlwKCkgKw0KICBsYWJzKHg9IiIsIHk9IkNsYXNzaWNhbCBERUcgZ2VuZSBjb3VudCIsIGZpbGw9IiIpICsNCiAgdGhlbWUoYXhpcy50ZXh0PWVsZW1lbnRfdGV4dChzaXplPTI1KSkgKyANCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJub25lIikNCmRldi5vZmYoKQ0Kd3JpdGUudGFibGUoZXdwLmNsYXNzaWNhbC5yZXMsIGZpbGU9Im91dHB1dDEvY2xhc3NpY2FsX2VucmljaF9yZXMudHh0Iiwgc2VwPSJcdCIsIHF1b3RlPUZBTFNFLCByb3cubmFtZXMgPSBGQUxTRSkNCg0KYGBgDQoNCj4gSW50ZXJwcmV0YXRpb24NCg0KLSAqKlE2Kio6IEhvdyBtYW55IHBhdGh3YXlzIGFyZSBhbHRlcmVkIGluIHRoZSBjbGFzc2ljYWwgc3VidHlwZSBhbmQgaG93IGRvIHRoZXkgbGluayB0byBwYW5jcmVhdGljIGNhbmNlciAoZXhwZWN0ZWQgb3IgdW5leHBlY3RlZCk/DQoNClZlbm4gZGlhZ3JhbXMNCmBgYHtyfQ0KdmVubi5kaWFncmFtKHggPSBsaXN0KGV3cC5iYXNhbC5yZXMkSUQsIGV3cC5jbGFzc2ljYWwucmVzJElEKSwNCiAgY2F0ZWdvcnkubmFtZXMgPSBjKCJCYXNhbCIgLCAiQ2xhc3NpY2FsIiksDQogIGZpbGVuYW1lID0gJ291dHB1dDEvdmVubl9wYXRod2F5cy5wbmcnLA0KICBvdXRwdXQ9VFJVRSwNCiAgY29sPWMoIiM0NDAxNTRmZiIsICcjMjE5MDhkZmYnKSwNCiAgZmlsbCA9IGMoYWxwaGEoIiM0NDAxNTRmZiIsMC4zKSwgYWxwaGEoJyMyMTkwOGRmZicsMC4zKSksDQogIGNleCA9IDEuNSwNCikNCg0KYGBgDQo+IEludGVycHJldGF0aW9uDQoNCi0gKipRNyoqOiBIb3cgbWFueSBwYXRod2F5cyBhcmUgYWx0ZXJlZCBpbiBib3RoIHN1YnR5cGVzPyANCg0KDQojIyBQYXRod2F5IHZpc3VhbGl6YXRpb24NCg0KVGhlIHBhdGh3YXlzIGNhbiB0aGVuIGJlIHZpc3VhbGl6ZWQgd2l0aCB0aGUgZ2VuZSBleHByZXNzaW9uIGRhdGEgYXMgc2hvd24gd2l0aCB0aGUgDQoiVml0YW1pbiBEIHJlY2VwdG9yIHBhdGh3YXkiIChXUDI4NzcpIHBhdGh3YXkgZnJvbSBXaWtpUGF0aHdheXMuIFRoZSBwYXRod2F5IHdhcyBhbHRlcmVkIGluIGJvdGggc3VidHlwZXMuIA0KDQpZb3UgY2FuIGVhc2lseSByZXBsYWNlIHRoZSBXaWtpUGF0aHdheXMgaWRlbnRpZmllciBpbiBsaW5lIDIwOSB3aXRoIGFub3RoZXIgcGF0aHdheSBvZiBpbnRlcmVzdCBhbmQgdmlzdWFsaXplIHRoZSBkYXRhIG9uIHRoYXQgcGF0aHdheS4gDQoNCmBgYHtyfQ0KUkN5Mzo6Y3l0b3NjYXBlUGluZygpDQpSQ3kzOjppbnN0YWxsQXBwKGMoIndpa2lwYXRod2F5cyIsIkN5VGFyZ2V0TGlua2VyIikpDQoNClJDeTM6OmNvbW1hbmRzUnVuKCd3aWtpcGF0aHdheXMgaW1wb3J0LWFzLXBhdGh3YXkgaWQ9V1AyODc3JykgDQp0b2dnbGVHcmFwaGljc0RldGFpbHMoKQ0KUkN5Mzo6bWFwVGFibGVDb2x1bW4oIkVuc2VtYmwiLCAiSG9tbyBzYXBpZW5zIiwgIkVuc2VtYmwiLCAiRW50cmV6IEdlbmUiKQ0KbG9hZFRhYmxlRGF0YShkYXRhLnBhbmMsIGRhdGEua2V5LmNvbHVtbiA9ICJHZW5lSWQiLCB0YWJsZS5rZXkuY29sdW1uID0gIkVudHJleiBHZW5lIikNCg0KUkN5Mzo6aW5zdGFsbEFwcCgiZW5oYW5jZWRHcmFwaGljcyIpDQpSQ3kzOjpjb3B5VmlzdWFsU3R5bGUoIldpa2lQYXRod2F5cyIsICJteV9zdHlsZV9oZWF0bWFwIikNCg0KUkN5Mzo6c2V0Tm9kZUN1c3RvbUhlYXRNYXBDaGFydChjKCJCX2xvZ0ZDIiwiQ19sb2dGQyIpLCBzbG90ID0gMiwgc3R5bGUubmFtZSA9ICJteV9zdHlsZV9oZWF0bWFwIiwgY29sb3JzID0gYygiI0NDMzMwMCIsIiNGRkZGRkYiLCIjNjY5OUZGIiwiI0NDQ0NDQyIpKQ0KDQpSQ3kzOjpzZXRWaXN1YWxTdHlsZSgibXlfc3R5bGVfaGVhdG1hcCIpDQoNCiMgU2F2aW5nIG91dHB1dA0KI3BuZy5maWxlIDwtIGZpbGUucGF0aCgiZmlndXJlcy9QYXRod2F5VmlzdWFsaXphdGlvbi5wbmciKQ0KI2V4cG9ydEltYWdlKHBuZy5maWxlLCdQTkcnLCB6b29tID0gNTAwKQ0KI2N5cy5maWxlIDwtIGZpbGUucGF0aChnZXR3ZCgpLCJvdXRwdXQvUGF0aHdheVZpc3VhbGl6YXRpb24uY3lzIikNCiNzYXZlU2Vzc2lvbihjeXMuZmlsZSkgDQojY29tbWVudCBmb2xsb3dpbmcgbGluZSBpZiB5b3Ugd2FudCB0byBtYW5pcHVsYXRlIHRoZSB2aXN1YWxpemF0aW9uIGluIEN5dG9zY2FwZQ0KI1JDeTM6OmNsb3NlU2Vzc2lvbihzYXZlLmJlZm9yZS5jbG9zaW5nID0gRikNCmBgYA0KDQo+IEludGVycHJldGF0aW9uDQoNCi0gKipROCoqOiBEbyB5b3Ugc2VlIGFueSBkaWZmZXJlbmNlcyBiZXR3ZWVuIHRoZSB0d28gc3VidHlwZXMgaW4gdGhlIHZpdGFtaW4gRCByZWNlcHRvciBwYXRod2F5PyAobGVmdCBpcyBiYXNhbCBhbmQgcmlnaHQgaXMgY2xhc3NpY2FsKQ0KDQoNCg0KIyMgUGF0aHdheSBvdmVybGFwIHZpc3VhbGl6YXRpb24NCg0KVGhlcmUgaXMgb2Z0ZW4gY3Jvc3N0YWxrIGFuZCBvdmVybGFwIGJldHdlZW4gcGF0aHdheXMgZW5yaWNoZWQgaW4gZ2VuZSBleHByZXNzaW9uIGFuYWx5c2VzLiBUaGUgZm9sbG93aW5nIHN0ZXAgdmlzdWFsaXplcyB0aGUgb3ZlcmxhcCBiZXR3ZWVuIHRoZSBlbnJpY2hlZCBwYXRod2F5cyBpbiBhIHBhdGh3YXktZ2VuZSBuZXR3b3JrLiANCg0KVGhlIGdlbmVzIG5vdCBwcmVzZW50IGluIGFueSBwYXRod2F5IGFyZSBpbmNsdWRlZCBpbiB0aGUgdmlzdWFsaXphdGlvbiBidXQgY2FuIGJlIGZpbHRlcmVkIGluIGEgZm9sbG93LXVwIHN0ZXAgaWYgcHJlZmVycmVkLiANCg0KYGBge3J9DQpwd3kgPC0gdW5pcXVlKGV3cC5iYXNhbC5yZXNbLGMoMSwyKV0pDQpjb2xuYW1lcyhwd3kpIDwtIGMoImlkIiwibGFiZWwiKQ0KcHd5JHR5cGUgPC0gJ3BhdGh3YXknDQplZGdlcyA8LSB3cGlkMmdlbmVbd3BpZDJnZW5lJHdwaWQgJWluJSBwd3kkaWQsXQ0KY29sbmFtZXMoZWRnZXMpIDwtIGMoInNvdXJjZSIsICJ0YXJnZXQiKQ0KZ2VuZXMgPC0gdW5pcXVlKGRlZy5iYXNhbCkNCmNvbG5hbWVzKGdlbmVzKSA8LSBjKCJpZCIsImxhYmVsIikNCmdlbmVzIDwtIHRyYW5zZm9ybShnZW5lcywgaWQgPSBhcy5jaGFyYWN0ZXIoaWQpKQ0KZ2VuZXMkdHlwZSA8LSAnZ2VuZScNCmVkZ2VzIDwtIHVuaXF1ZShlZGdlc1tlZGdlcyR0YXJnZXQgJWluJSBnZW5lcyRpZCxdKQ0KZ2VuZXMgPC0gZ2VuZXNbZ2VuZXMkaWQgJWluJSBlZGdlcyR0YXJnZXQsXQ0Kbm9kZXMgPC0gZHBseXI6OmJpbmRfcm93cyhnZW5lcywgcHd5KQ0Kcm93bmFtZXMobm9kZXMpIDwtIE5VTEwNCmNyZWF0ZU5ldHdvcmtGcm9tRGF0YUZyYW1lcyhub2Rlcz1ub2RlcyxlZGdlcz1lZGdlcyx0aXRsZT0iUGF0aHdheS1HZW5lLUFzc29jaWF0aW9ucyIsIGNvbGxlY3Rpb249IlBhdGh3YXlHZW5lQ3Jvc3N0YWxrIikNCmxvYWRUYWJsZURhdGEoZGF0YS5wYW5jLCBkYXRhLmtleS5jb2x1bW4gPSAiR2VuZUlkIiwgdGFibGUua2V5LmNvbHVtbiA9ICJpZCIpDQojIFZpc3VhbCBzdHlsZQ0KUkN5Mzo6Y29weVZpc3VhbFN0eWxlKCJkZWZhdWx0Iiwid3AudmlzIikNClJDeTM6OnNldE5vZGVMYWJlbE1hcHBpbmcoImxhYmVsIiwgc3R5bGUubmFtZT0id3AudmlzIikNClJDeTM6OmxvY2tOb2RlRGltZW5zaW9ucyhUUlVFLCBzdHlsZS5uYW1lPSJ3cC52aXMiKQ0KUkN5Mzo6c2V0Tm9kZVNoYXBlTWFwcGluZygndHlwZScsIGMoJ2dlbmUnLCdwYXRod2F5JyksIGMoImVsbGlwc2UiLCJoZXhhZ29uIiksIHN0eWxlLm5hbWU9IndwLnZpcyIpDQpSQ3kzOjpzZXROb2RlU2l6ZU1hcHBpbmcoJ3R5cGUnLCBjKCdnZW5lJywncGF0aHdheScpLCBjKDQwLDI1KSwgbWFwcGluZy50eXBlID0gImQiLCBzdHlsZS5uYW1lID0gIndwLnZpcyIpDQpkYXRhLnZhbHVlczwtYygtMSwwLDEpIA0Kbm9kZS5jb2xvcnMgPC0gYyhyZXYoYnJld2VyLnBhbChsZW5ndGgoZGF0YS52YWx1ZXMpLCAiUmRCdSIpKSkNCnNldE5vZGVDb2xvck1hcHBpbmcoIkJfbG9nRkMiLCBkYXRhLnZhbHVlcywgbm9kZS5jb2xvcnMsIGRlZmF1bHQuY29sb3IgPSAiIzk5RkY5OSIsIHN0eWxlLm5hbWUgPSAid3AudmlzIikNClJDeTM6OnNldFZpc3VhbFN0eWxlKCJ3cC52aXMiKQ0KUkN5Mzo6dG9nZ2xlR3JhcGhpY3NEZXRhaWxzKCkNCg0KIyBTYXZpbmcgb3V0cHV0DQpzdmcuZmlsZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwgIm91dHB1dDEvUGF0aHdheUNyb3NzdGFsay5zdmciKQ0KZXhwb3J0SW1hZ2Uoc3ZnLmZpbGUsJ1NWRycpDQpwbmcuZmlsZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwgIm91dHB1dDEvUGF0aHdheUNyb3NzdGFsay5wbmciKQ0KZXhwb3J0SW1hZ2UocG5nLmZpbGUsJ1BORycsIHpvb20gPSA1MDApDQpjeXMuZmlsZSA8LSBmaWxlLnBhdGgoZ2V0d2QoKSwgIm91dHB1dDEvUGF0aHdheUNyb3NzdGFsay5jeXMiKQ0Kc2F2ZVNlc3Npb24oY3lzLmZpbGUpIA0KI2NvbW1lbnQgZm9sbG93aW5nIGxpbmUgaWYgeW91IHdhbnQgdG8gbWFuaXB1bGF0ZSB0aGUgdmlzdWFsaXphdGlvbiBpbiBDeXRvc2NhcGUNCiNSQ3kzOjpjbG9zZVNlc3Npb24oc2F2ZS5iZWZvcmUuY2xvc2luZyA9IEYpDQpgYGANCg0KPiBJbnRlcnByZXRhdGlvbg0KDQotICoqUTkqKjogRG8gdGhlIGFmZmVjdGVkIHBhdGh3YXlzIHNoYXJlIGFueSBnZW5lcz8gDQoNCg0KIyMgRHJ1ZyB0YXJnZXQgaW5mb3JtYXRpb24NCg0KTmV4dCwgd2Ugd2lsbCBhZGQgaW5mb3JtYXRpb24gYWJvdXQga25vd24gZHJ1Zy10YXJnZXQgaW50ZXJhY3Rpb25zIGZvciB0aGUgZ2VuZXMgaW4gdGhlIGFmZmVjdGVkIHBhdGh3YXlzIHVzaW5nIGluZm9ybWF0aW9uIGZyb20gRHJ1Z0JhbmsgdXNpbmcgdGhlIEN5VGFyZ2V0TGlua2VyIGFwcC4NCg0KV2Ugd2lsbCBzaG93IHRoaXMgZm9yIHRoZSBjbGFzc2ljYWwgc3VidHlwZS4gDQoNCmBgYHtyfQ0KUkN5Mzo6Y3l0b3NjYXBlUGluZygpDQppbnN0YWxsQXBwKCdDeVRhcmdldExpbmtlcicpIA0KDQpwd3kgPC0gdW5pcXVlKGV3cC5jbGFzc2ljYWwucmVzWyxjKDEsMildKQ0KY29sbmFtZXMocHd5KSA8LSBjKCJpZCIsImxhYmVsIikNCnB3eSR0eXBlIDwtICdwYXRod2F5Jw0KZWRnZXMgPC0gd3BpZDJnZW5lW3dwaWQyZ2VuZSR3cGlkICVpbiUgcHd5JGlkLF0NCmNvbG5hbWVzKGVkZ2VzKSA8LSBjKCJzb3VyY2UiLCAidGFyZ2V0IikNCmdlbmVzIDwtIHVuaXF1ZShkZWcuYmFzYWwpDQpjb2xuYW1lcyhnZW5lcykgPC0gYygiaWQiLCJsYWJlbCIpDQpnZW5lcyA8LSB0cmFuc2Zvcm0oZ2VuZXMsIGlkID0gYXMuY2hhcmFjdGVyKGlkKSkNCmdlbmVzJHR5cGUgPC0gJ2dlbmUnDQplZGdlcyA8LSB1bmlxdWUoZWRnZXNbZWRnZXMkdGFyZ2V0ICVpbiUgZ2VuZXMkaWQsXSkNCmdlbmVzIDwtIGdlbmVzW2dlbmVzJGlkICVpbiUgZWRnZXMkdGFyZ2V0LF0NCm5vZGVzIDwtIGRwbHlyOjpiaW5kX3Jvd3MoZ2VuZXMsIHB3eSkNCnJvd25hbWVzKG5vZGVzKSA8LSBOVUxMDQpjcmVhdGVOZXR3b3JrRnJvbURhdGFGcmFtZXMobm9kZXM9bm9kZXMsZWRnZXM9ZWRnZXMsdGl0bGU9IlBhdGh3YXktR2VuZS1Bc3NvY2lhdGlvbnMiLCBjb2xsZWN0aW9uPSJQYXRod2F5R2VuZUNyb3NzdGFsayIpDQpsb2FkVGFibGVEYXRhKGRhdGEucGFuYywgZGF0YS5rZXkuY29sdW1uID0gIkdlbmVJZCIsIHRhYmxlLmtleS5jb2x1bW4gPSAiaWQiKQ0KIyBWaXN1YWwgc3R5bGUNClJDeTM6OmNvcHlWaXN1YWxTdHlsZSgiZGVmYXVsdCIsIndwLnZpcyIpDQpSQ3kzOjpzZXROb2RlTGFiZWxNYXBwaW5nKCJsYWJlbCIsIHN0eWxlLm5hbWU9IndwLnZpcyIpDQpSQ3kzOjpsb2NrTm9kZURpbWVuc2lvbnMoVFJVRSwgc3R5bGUubmFtZT0id3AudmlzIikNClJDeTM6OnNldE5vZGVTaGFwZU1hcHBpbmcoJ3R5cGUnLCBjKCdnZW5lJywncGF0aHdheScpLCBjKCJlbGxpcHNlIiwiaGV4YWdvbiIpLCBzdHlsZS5uYW1lPSJ3cC52aXMiKQ0KUkN5Mzo6c2V0Tm9kZVNpemVNYXBwaW5nKCd0eXBlJywgYygnZ2VuZScsJ3BhdGh3YXknKSwgYyg0MCwyNSksIG1hcHBpbmcudHlwZSA9ICJkIiwgc3R5bGUubmFtZSA9ICJ3cC52aXMiKQ0KZGF0YS52YWx1ZXM8LWMoLTEsMCwxKSANCm5vZGUuY29sb3JzIDwtIGMocmV2KGJyZXdlci5wYWwobGVuZ3RoKGRhdGEudmFsdWVzKSwgIlJkQnUiKSkpDQpzZXROb2RlQ29sb3JNYXBwaW5nKCJDX2xvZ0ZDIiwgZGF0YS52YWx1ZXMsIG5vZGUuY29sb3JzLCBkZWZhdWx0LmNvbG9yID0gIiM5OUZGOTkiLCBzdHlsZS5uYW1lID0gIndwLnZpcyIpDQpSQ3kzOjpzZXRWaXN1YWxTdHlsZSgid3AudmlzIikNClJDeTM6OnRvZ2dsZUdyYXBoaWNzRGV0YWlscygpDQoNCmRydWdiYW5rIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAiZGF0YS9kcnVnYmFuay01LjEuMC54Z21tbCIpDQoNCiMgcnVuIEN5VGFyZ2V0TGlua2VyDQpjb21tYW5kc1J1bihwYXN0ZTAoJ2N5dGFyZ2V0bGlua2VyIGV4dGVuZCBpZEF0dHJpYnV0ZT0iaWQiIGxpbmtTZXRGaWxlcz0iJywgZHJ1Z2JhbmssICciJykpDQpjb21tYW5kc1J1bignY3l0YXJnZXRsaW5rZXIgYXBwbHlMYXlvdXQgbmV0d29yaz0iY3VycmVudCInKQ0KUkN5Mzo6c2V0VmlzdWFsU3R5bGUoIndwLnZpcyIpDQoNCiNsZXQncyBjaGFuZ2UgdGhlIHZpc3VhbGl6YXRpb24gb2YgdGhlIGRydWdzIGluIHRoZSBuZXR3b3JrIHVzaW5nIHRoZSBCeVBhc3Mgb3B0aW9uDQpzZWxlY3RlZCA8LSBSQ3kzOjpzZWxlY3ROb2Rlcyhub2Rlcz0iZHJ1ZyIsIGJ5LmNvbCA9ICJDVEwuVHlwZSIpDQpSQ3kzOjpzZXROb2RlU2hhcGVCeXBhc3Mobm9kZS5uYW1lcyA9IHNlbGVjdGVkJG5vZGVzLCBuZXcuc2hhcGVzID0gIlRyaWFuZ2xlIikNClJDeTM6OnNldE5vZGVDb2xvckJ5cGFzcyhub2RlLm5hbWVzID0gc2VsZWN0ZWQkbm9kZXMsICIjRkZGRkNFIikNClJDeTM6OnNldE5vZGVCb3JkZXJDb2xvckJ5cGFzcyhub2RlLm5hbWVzID0gc2VsZWN0ZWQkbm9kZXMsICIjMDAwMDAwIikNClJDeTM6OnNldE5vZGVCb3JkZXJXaWR0aEJ5cGFzcyhub2RlLm5hbWVzID0gc2VsZWN0ZWQkbm9kZXMsIDQpDQpSQ3kzOjpjbGVhclNlbGVjdGlvbigpDQpSQ3kzOjp0b2dnbGVHcmFwaGljc0RldGFpbHMoKQ0KDQpgYGANCg0KPiBJbnRlcnByZXRhdGlvbg0KDQotICoqUTEwKio6IEFyZSB0aGVyZSBwcm90ZWlucyB0YXJnZXRlZCBieSBtYW55IGtub3duIGRydWdzPyBDYW4geW91IGZpbmQgc3VwcG9ydGluZyBsaXRlcmF0dXJlIHJlZ2FyZGluZyB0aGVzZSBnZW5lcyBvciBkcnVncyBpbiB0aGUgY29udGV4dCBvZiBwYW5jcmVhdGljIGNhbmNlcj8NCg0K