1 Forewords

1.1 TLDR: R command lines

In this presentation, there will be screen captures for you to follow the lesson. There will also be every single R command lines. Do not take care of the command lines if you find them too challenging. Our goal here, is to understand the main mechanism of Differential Expression Analysis. R is just a tool.

Below are the libraries we need to perform this whole session:

base::library(package = "BiocParallel")    # Optionally multithread some steps
base::library(package = "DT")              # Display nice table in HTML
base::library(package = "ggplot2")         # Draw graphs and plots
base::library(package = "ggpubr")          # Draw nicer graphs
base::library(package = "rstatix")         # Base R statistics
base::library(package = "knitr")           # Build this presentation
base::library(package = "dplyr")           # Handle big tables
base::library(package = "Seurat")          # Handle SingleCell analyses
base::library(package = "SeuratObject")    # Handle SingleCell objects
base::library(package = "SingleCellExperiment") # Handle SingleCell file formats
base::library(package = "escape")          # Perform exploratory enrichment analysis
base::library(package = "clusterProfiler") # Perform GSEA analysis
base::library(package = "dittoSeq")        # Draw nice plots based on Seurat
base::library(package = "org.Mm.eg.db")    # Mouse genome annotation
base::library(package = "Cairo")           # Graphs library
base::library(package = "pathview")        # For the whole pathway graph

First, we load Seurat object:

sobj <- base::readRDS(
  # Path to the RDS file
  file = "DEA_Scaled_Normalized_Filtered.RDS"
)

Then we launch enrichment exploration on all counts:

# Acquire gene sets
mh_hallmark <- escape::getGeneSets(
  species = "Mus musculus",
  library = "H"
)

# Run enrichment
enrichment_matrix <- escape::escape.matrix(
  input.data = sobj,
  gene.sets = mh_hallmark,
  method = "ssGSEA",
  groups =  1000,
  min.size = 5,
  BPPARAM = BiocParallel::SnowParam(workers = 2),
)

# Save enrichment in seurat object
sobj[["escape"]] <- SeuratObject::CreateAssay5Object(
  data=t(enrichment_matrix),
)

saveRDS(sobj, file = "sobj_GSEA.RDS")

1.2 Purpose of this session

Up to now, we have:

  1. Identified to which cell each sequenced reads come from
  2. Identified to which gene each read come from
  3. Identified possible bias in gene expression for each cell
  4. Filtered and corrected these bias as well as we can
  5. Found differentially expressed genes across multiple conditions
  6. Annotated cell clusters

We would like to identify the functions of genes among several clusters, or differentially expressed genes.

At the end of this session you will know:

  1. What is gene set analysis
  2. How to choose a Gene Set database
  3. How to perform an enrichment analysis
  4. How to read Gene set analysis results

2 Select a database

2.1 Gene: Plac8

Let’s search information about this gene on the web. For mice, one of the best web-site for human is: MGI.

If we search for the gene Plac8, we find the following:

plac8_mgi

As we click on “Phenotype reference”, we can see that the MGI database can give us PubMeb IDs related to the gene Plca8. Below, on the same page, we can see regulation pathways, protein ontology, gene interactions, etc.

plca8_pathway

We illustrate here, that GSEA does not only include gene-to-function, or gene-to-pathway. They usually include many more information.

2.2 Database types

db_types

There is a database for pretty much everything you might want. From pharmacology, to regulatory objects, etc.

Our dataset contains gene expression. But the same methods can be applied to other kind of datasets. E.g.

ppis

Protein-protein interactions, drug interactions, SNP-interactions, etc.

Within R, you may find genome databases for a wide range of organisms:

org_bd

2.3 Some noticeable databases

For this session, we are going to use MSigBD.

2.4 How to perform GSEA

There are two main types of Gene Set Enrichment Analysis: 1. A blind search among all genes in order to identify pathways or mechanisms among cells. 1. A specific search through genes of interest

The first is called “Exploratory” analysis and starts on the Seurat object.

Many packages perform this kind of exploratory analysis, I chose to present escape for its simple and intuitive usage.

What really matters is the GSEA method used in back-end. There are several very well known methods: fgsea, gsva, DOSE, and the MSigDB’s tool. While choosing your favorite R package to perform GSEA, if that package uses one of these methods, then you should not have odd questions through your publication process.

These methods are very alike, based on the same statistical resorts, and suffer for the same limitations. Let’s illustrate it with examples.

3 Exploratory analysis

3.1 Gene sets

Up to now, we already define what’s a gene set: a group of genes of interest. We also know these king of relationships are stored in GMT files or TSV files.

Since the beginning of our week, we’ve been working with R, and the question naturally comes: how to make/use gene sets in R?

3.1.1 Create your own gene set

Very easily. We need a “named list of vectors”, all coming from base R package.

# We use the function list from base package
pathways <- base::list(
  # We use the function `c` from base package
  proliferation = base::c(
    "Ifng", "Kras", "Mgat5",    
    "Prf1", "Trem1",    "Trp53"
  ),
  growth = base::c(
    "Cdkn2a", "Fos", "Ifnb1",
    "Rras", "Apc", "Sell"
  )
)
utils::head(pathways)
$proliferation
[1] "Ifng"  "Kras"  "Mgat5" "Prf1"  "Trem1" "Trp53"

$growth
[1] "Cdkn2a" "Fos"    "Ifnb1"  "Rras"   "Apc"    "Sell"  

This method is very useful when you want to compare your cells toward unpublished data, gene sets from an article not sourced in official databases, etc.

3.1.2 Load a gene set from disk

We can also load a GMT file from a file. This is very usefull when we expect both known and new pathways to be tested, or known pathways that should be updated.

This is done with the function read.gmt from the clusterProfiler package.

pathways <- clusterProfiler::read.gmt(
  gmtfile = "m5.mpt.v2023.1.Mm.symbols.gmt"
)
pathways <- BiocGenerics::lapply(
  S4Vectors::split(
    pathways[-1], pathways$term
  ),
  function(x) x[x != ""]
)
utils::head(pathways)
$MP_ABNORMAL_TUMOR_VASCULARIZATION
 [1] "Adamts12" "Aggf1"    "Amotl1"   "Anxa1"    "Arhgef4"  "Cav2"    
 [7] "Ccm2l"    "Cd82"     "Dock4"    "Fkbpl"    "Gpr4"     "H2ax"    
[13] "Idh2"     "Ifngr1"   "Il12rb2"  "Il1a"     "Il1b"     "Itga6"   
[19] "Itgb3"    "Jcad"     "Mmrn2"    "Myct1"    "Ntn4"     "Peak1"   
[25] "Pld1"     "Rhoj"     "Rras"     "S1pr2"    "Stard13" 

$MP_DECREASED_CIRCULATING_TUMOR_NECROSIS_FACTOR_LEVEL
 [1] "Adam17"   "Apba3"    "Arid5a"   "Bbs12"    "C3"       "Cd14"    
 [7] "Cd19"     "Cd44"     "Clic4"    "Enpp2"    "F3"       "Ffar2"   
[13] "Foxn1"    "Foxo1"    "Gba"      "Hectd3"   "Ifi204"   "Ifi35"   
[19] "Ifit2"    "Ifngr1"   "Ikbkb"    "Il22"     "Il6ra"    "Irak2"   
[25] "Irak4"    "Irf5"     "Lacc1"    "Lep"      "Lrrc19"   "Lrrc8e"  
[31] "Mapkapk2" "Mbl1"     "Mcpt1"    "Mif"      "Msmp"     "Mstn"    
[37] "Myd88"    "Nfkbib"   "Nkg7"     "Nmi"      "Npy1r"    "Parp1"   
[43] "Peli1"    "Pilrb1"   "Plekhf1"  "Prkce"    "Rhbdf2"   "Sgms1"   
[49] "Siglec1"  "Snx8"     "Sra1"     "Thbd"     "Ticam1"   "Tlr2"    
[55] "Tnf"      "Tradd"    "Zdhhc1"  

$MP_DECREASED_INCIDENCE_OF_INDUCED_TUMORS
 [1] "Ccng1"   "Cd151"   "Cdk1"    "Cebpb"   "Ct55"    "Cyp2c23" "Eif2s2" 
 [8] "H2-Ob"   "Il6"     "Klk6"    "Krt10"   "Lrig2"   "Pgr"     "Terc"   
[15] "Uqcc3"   "Zmiz1"   "a"      

$MP_DECREASED_INCIDENCE_OF_TUMORS_BY_CHEMICAL_INDUCTION
 [1] "Agtr2"     "Ahrr"      "Ar"        "Arl6ip5"   "Birc5"     "Brca2"    
 [7] "Cacfd1"    "Camk2g"    "Ccl2"      "Cd151"     "Cd34"      "Cd96"     
[13] "Cdkn1b"    "Cdkn2a"    "Cebpb"     "Chd1l"     "Cyp1a2"    "Cyp1b1"   
[19] "Cyp2e1"    "Dek"       "Dnajc27"   "Endov"     "Ephx1"     "Fgf22"    
[25] "Fntb"      "Foxm1"     "Foxn1"     "Fxyd5"     "Fzr1"      "Gata6os"  
[31] "Hcst"      "Hipk1"     "Hras"      "Ier3"      "Il12b"     "Il1r1"    
[37] "Il23a"     "Il6"       "Il6ra"     "Itgb1"     "Kdm4c"     "Klk6"     
[43] "Lonp1"     "Loxl2"     "Lypd3"     "Mapkapk2"  "Mgat3"     "Mif"      
[49] "Mir21a"    "Mir301"    "Mki67"     "Mmp11"     "Mmp19"     "Mmp1a"    
[55] "Mpg"       "Mtdh"      "Muc4"      "Myd88"     "Neu3"      "Pard3"    
[61] "Pik3ca"    "Pla2g4a"   "Plce1"     "Prdx1"     "Pten"      "Ptger1"   
[67] "Ptger2"    "Ptger4"    "Ptgs2"     "Ptk2"      "Ptp4a3"    "Ptprj"    
[73] "Pvr"       "Pycard"    "Ralgds"    "Retnlb"    "Skil"      "Slc3a2"   
[79] "Spp1"      "Stag1"     "Stat3"     "Strap"     "Terc"      "Tert"     
[85] "Tiam1"     "Tlr4"      "Tnf"       "Tnfaip8l3" "Tnik"      "Trem1"    
[91] "Trim27"    "Trp53"     "Uri1"     

$MP_DECREASED_LIVER_TUMOR_INCIDENCE
[1] "Foxm1" "Mtdh"  "Myd88" "Tert"  "Tlr2" 

$MP_DECREASED_LUNG_TUMOR_INCIDENCE
[1] "Ggta1" "Kras"  "Lexm"  "Met"   "Mtdh"  "Tlr2" 

3.1.3 Load a gene set from the web

We can also get pathways from the web using the function getGeneSets from package escape.

# Pay attention to the genome name, it's case sensitive
# and follows MSigDB naming scheme
mh_hallmark <- escape::getGeneSets(
  species = "Mus musculus", 
  library = "H"
)
utils::head(pathways)
$MP_ABNORMAL_TUMOR_VASCULARIZATION
 [1] "Adamts12" "Aggf1"    "Amotl1"   "Anxa1"    "Arhgef4"  "Cav2"    
 [7] "Ccm2l"    "Cd82"     "Dock4"    "Fkbpl"    "Gpr4"     "H2ax"    
[13] "Idh2"     "Ifngr1"   "Il12rb2"  "Il1a"     "Il1b"     "Itga6"   
[19] "Itgb3"    "Jcad"     "Mmrn2"    "Myct1"    "Ntn4"     "Peak1"   
[25] "Pld1"     "Rhoj"     "Rras"     "S1pr2"    "Stard13" 

$MP_DECREASED_CIRCULATING_TUMOR_NECROSIS_FACTOR_LEVEL
 [1] "Adam17"   "Apba3"    "Arid5a"   "Bbs12"    "C3"       "Cd14"    
 [7] "Cd19"     "Cd44"     "Clic4"    "Enpp2"    "F3"       "Ffar2"   
[13] "Foxn1"    "Foxo1"    "Gba"      "Hectd3"   "Ifi204"   "Ifi35"   
[19] "Ifit2"    "Ifngr1"   "Ikbkb"    "Il22"     "Il6ra"    "Irak2"   
[25] "Irak4"    "Irf5"     "Lacc1"    "Lep"      "Lrrc19"   "Lrrc8e"  
[31] "Mapkapk2" "Mbl1"     "Mcpt1"    "Mif"      "Msmp"     "Mstn"    
[37] "Myd88"    "Nfkbib"   "Nkg7"     "Nmi"      "Npy1r"    "Parp1"   
[43] "Peli1"    "Pilrb1"   "Plekhf1"  "Prkce"    "Rhbdf2"   "Sgms1"   
[49] "Siglec1"  "Snx8"     "Sra1"     "Thbd"     "Ticam1"   "Tlr2"    
[55] "Tnf"      "Tradd"    "Zdhhc1"  

$MP_DECREASED_INCIDENCE_OF_INDUCED_TUMORS
 [1] "Ccng1"   "Cd151"   "Cdk1"    "Cebpb"   "Ct55"    "Cyp2c23" "Eif2s2" 
 [8] "H2-Ob"   "Il6"     "Klk6"    "Krt10"   "Lrig2"   "Pgr"     "Terc"   
[15] "Uqcc3"   "Zmiz1"   "a"      

$MP_DECREASED_INCIDENCE_OF_TUMORS_BY_CHEMICAL_INDUCTION
 [1] "Agtr2"     "Ahrr"      "Ar"        "Arl6ip5"   "Birc5"     "Brca2"    
 [7] "Cacfd1"    "Camk2g"    "Ccl2"      "Cd151"     "Cd34"      "Cd96"     
[13] "Cdkn1b"    "Cdkn2a"    "Cebpb"     "Chd1l"     "Cyp1a2"    "Cyp1b1"   
[19] "Cyp2e1"    "Dek"       "Dnajc27"   "Endov"     "Ephx1"     "Fgf22"    
[25] "Fntb"      "Foxm1"     "Foxn1"     "Fxyd5"     "Fzr1"      "Gata6os"  
[31] "Hcst"      "Hipk1"     "Hras"      "Ier3"      "Il12b"     "Il1r1"    
[37] "Il23a"     "Il6"       "Il6ra"     "Itgb1"     "Kdm4c"     "Klk6"     
[43] "Lonp1"     "Loxl2"     "Lypd3"     "Mapkapk2"  "Mgat3"     "Mif"      
[49] "Mir21a"    "Mir301"    "Mki67"     "Mmp11"     "Mmp19"     "Mmp1a"    
[55] "Mpg"       "Mtdh"      "Muc4"      "Myd88"     "Neu3"      "Pard3"    
[61] "Pik3ca"    "Pla2g4a"   "Plce1"     "Prdx1"     "Pten"      "Ptger1"   
[67] "Ptger2"    "Ptger4"    "Ptgs2"     "Ptk2"      "Ptp4a3"    "Ptprj"    
[73] "Pvr"       "Pycard"    "Ralgds"    "Retnlb"    "Skil"      "Slc3a2"   
[79] "Spp1"      "Stag1"     "Stat3"     "Strap"     "Terc"      "Tert"     
[85] "Tiam1"     "Tlr4"      "Tnf"       "Tnfaip8l3" "Tnik"      "Trem1"    
[91] "Trim27"    "Trp53"     "Uri1"     

$MP_DECREASED_LIVER_TUMOR_INCIDENCE
[1] "Foxm1" "Mtdh"  "Myd88" "Tert"  "Tlr2" 

$MP_DECREASED_LUNG_TUMOR_INCIDENCE
[1] "Ggta1" "Kras"  "Lexm"  "Met"   "Mtdh"  "Tlr2" 

3.2 Run exploratory analysis

Let’s time to practice. Use the function escape.matrix from the package escape in order to perform GSEA on your Seurat object. Perform the analysis on cell cycle phase if possible. Save the results in a variable, for example sobj_enrich.

Answer

It is not possible to specify what group we are considering. GSEA/Enrichment analysis do not perform any differential analysis.

# Save in a variable called `sobj_enrich`
# the result of the function `runEscape` from package `escape`
enrichment_matrix <- escape::escape.matrix(
  # Out Seurat object
  input.data = sobj,
  # Provide gene sets
  gene.sets = mh_hallmark,
  # Select your favorite method
  method = "ssGSEA",
  # ssGSEA parameters
  groups =  1000,
  min.size = 5,
  # Use 2 threads/CPU/workers
  BPPARAM = BiocParallel::SnowParam(workers = 2),
)
[1] "Using sets of 1000 cells. Running 4 times."
[1] "Calculating ranks..."
[1] "Calculating absolute values from ranks..."
[1] "Calculating ranks..."
[1] "Calculating absolute values from ranks..."
[1] "Calculating ranks..."
[1] "Calculating absolute values from ranks..."
[1] "Calculating ranks..."
[1] "Calculating absolute values from ranks..."

If you have reserved more than one core, this function can be multi-threaded, making it faster !


3.3 Visualize results

We can visualise the gene sets enriched in cell clusters using the function FeaturePlot from Seurat package. We just need to color the cells with ssGSEA’s results:

Seurat::FeaturePlot(object = sobj, features = "HALLMARK-DNA-REPAIR")

4 On purpose analysis

In order to understand what happened, where do these results come from, we need to go step by step and perform both enrichment and GSE analysis manually.

In general, the exploratory method is never used on a raw Seurat object, we usually filter this object in order to search pathways that are related to our biological question.

With the previous method, what ever our biological question was, the cell cycle phase was one of the top gene expression drivers.

4.1 Gene Names and Gene identifiers

Let’s search information about the gene ARP2.

arp2_multiple

So, this gene exists in multiple organisms. Thanks, we know we are working on mice, and we told gene set retriever function to consider ‘mouse’ datasets.

arp2_names

So in the Human genome, ARP2 refers to multiple genes through the alias names. When we searched for gene sets with [escape], we did not perform any disambiguation.

The same with arabidopsis thaliana, ARP2 can refer to actin related protein, or a root development protein.

In mus musculus, ARP2 refers to an actin related protein on chromosome 11, or a subunit adaptor protein on chromosome 5.

We now understand that escape analysis may be completely wrong since it used human-intelligible gene names. These names include synonyms, homonyms, within a single organism, or between multiple ones.

We need to use unique identifiers. These identifiers are called gene identifiers and we usually consider EnsemblID or EntrezID.

For example:

  1. ARP2 from chromosome 11 equals ENSMUSG00000020152 at Ensembl.
  2. ARP2 from chromosome 5 equals ENSMUSG00000019518 at Ensembl.

These identifiers are very unpleasant to use for human beings. On monday meeting, please talk about ARP2 and not ENSMUSG00000020152. However, when machines are working, please given them EnsemblID or EntrezID.

Let’s translate all gene identifiers and evaluate possible number of errors in escape analysis.

First, let’s load the DEA results based on Wilcoxon analysis:

# We load our differential expression analysis result
# using the function `readRDS` from `base` package
sobj_wilcox <- base::readRDS(file = "sobj_wilcox.RDS")
# Copy gene names in a column it makes the future operation easier
sobj_wilcox$SYMBOL <- base::rownames(sobj_wilcox)

Then, we use the function bitr (standing for biological translator) from the package clusterProfiler.

# We store the result of `bitr` function
# from `clusterProfiler` package
# in a variable called `annotation`
annotation <- clusterProfiler::bitr(
  # We provide the variable pointing to gene names
  geneID = base::rownames(sobj_wilcox),
  # We tell the translator to which gene identifiers
  # translation must be done
  toType = base::c("ENTREZID", "ENSEMBL"),
  # We tell the translator which type of gene name we have
  fromType = "SYMBOL",
  # We provide mmu database
  OrgDb = org.Mm.eg.db
)

Now we would like to have these annotation alongside with adjusted pvalues and fold change information. In order to do so, we use the function merge from base package. Not from Seurat package ! This is used to merger Seurat objects, but we have dataframes here!

# We filter these results on **ADJUSTED** pvalue
sobj_wilcox <- sobj_wilcox[sobj_wilcox$p_val_adj <= 0.05, ]
# Use the function `merge` from package `base` in order
# to add annotation to wixocon DEA result.
sobj_wilcox <- base::merge(
  # Variable pointing to the wilcoxon results
  x = sobj_wilcox,
  # Variable pointing to the annotation results
  y = annotation,
  # We tell how to merge sobj_wilcox
  by.x = "SYMBOL",
  # We tell how to merge annotation
  by.y = "SYMBOL"
)

4.2 Restricted sed of genes

As we perform the analysis, we are going to provide a numeric variable to the GSEA/Enrichment.

We have the following columns:

  1. p_val: Ignore this column. Always ignore raw p-values. Look at corrected ones, and if they are missing, then compute them.
  2. avg_log2FC: Average Log2(FoldChange). Illustrates how much a gene is differentially expessed between samples in each condition.
  3. pct.1: Percent of cells with gene expression in condition one, here in “G1” phase.
  4. pct.2: Percent of cells with gene expression in condition two, here in “S” phase.
  5. p_val_adj: The confidence we have in the result. The closer to 0, the lesser is the risk of error.

Is it a good idea to use p_val ? What are the consequences ?

Answer

No. Never. Never ever use raw P-Value. It is never a good idea.


Is it a good idea to use avg_log2FC ? What are the consequences ?

Answer

It is a good idea, we could answer biological questions like : “Considering differentially expressed genes, what are the pathways with highest expression change ?”


Is it a good idea to use pct.1 ? What are the consequences ?

Answer

It is a good idea, we could answer biological questions like : “Considering differentially expressed genes, what are the expression of genes in pathway XXX in the first condition ?”


Is it a good idea to use pct.2 ? What are the consequences ?

Answer

It is a good idea, we could answer biological questions like : “Considering differentially expressed genes, what are the expression of genes in pathway XXX in the second condition ?”


Is it a good idea to use p_val_adj ? What are the consequences ?

Answer

It is a good idea, we could answer biological questions like : “Which pathways are differentially expressed with highest confidence interval ?”

But in order to perform surch test, use -log10(Adjusted P-Value) instead of the raw adjusted p-value. Remember, 0 is a good confidence interval, and 1 a bad one. So we need the values close to 0 to be highly positive.


4.3 Enrichment vs GSEA

Previously, we used a function called enrichIt. We talked about enrichment analysis, yet this talk is called GSEA.

This is due to the fact that both techniques exists and are different.

Enrichment tests a list of genes. Their expression, confidence interval, etc. Nothing else matters than their name.

For each cell, enrichIt removes zeros and tests the list of remaining genes. Their expression does not matter. Their order in the gene list does not matter. Their impact on the pathway does not matter. Their differential expression status does not matter.

Behind the scenes, it’s a very simple tests answering the following question: “Among expressed genes, what are the odds that it belongs to the pathway XXX?”

We can do enrichment tests on our wilxoc results, using the function enrichGO from the package clusterProfiler:

# We store the result of the function `enrichGO` from package `clusterProfiler`
# in the function `enrich_wilcox`
enrich_wilcox <- clusterProfiler::enrichGO(
  # We provide the list of gene identifiers to test
  gene = sobj_wilcox$ENTREZID,
  # We provide the complete list of genes we quantified
  universe = annotation$ENTREZID,
  # We provide mouse annotations
  OrgDb = org.Mm.eg.db,
  # We tell the function that we use entrez-identifiers
  keyType = "ENTREZID",
  # We search results on Biological Process"
  ont = "BP",
  # We want all the results
  pvalueCutoff = 1,
  # We are humans, we wan human-readable gene names
  readable = TRUE
)

We can have a look at pathways including the word G2M, using the functions with and grepl from base package.

# Get the result table
results_enrich_wilcox <- enrich_wilcox@result

# We store the result of the line selection
# in a variable called `g2m_enrich`
g2m_enrich <- results_enrich_wilcox[
  # We select rows containing a given information
  # using the function `with` from `base` package
  base::with(
    # We provide the variable pointing to enrichment results
    data = results_enrich_wilcox,
    # We provide the term we are searching and the column in which
    # the term should be searched
    base::grepl("G2M", Description)
  ), # Do not forget his comma, since we are filtering a dataframe on rows
]

GSEA tests the gene names, and uses the numeric value associated with that gene in order to weight the results. It tests the name, the rank, and the numeric value.

First, we need to create a named list of gene weights. For this example, we use the average fold change as weights.

# We extract gene average fold change
gene_list <- sobj_wilcox$avg_log2FC
# We extract genes Entrez ID
base::names(gene_list) <- sobj_wilcox$ENTREZID
# We sort that list deacreasingly
gene_list <- base::sort(gene_list, decreasing=TRUE)

# Check the results with the function `head` from `utils` package
utils::head(gene_list)
   12504    12259    12262    14960    16149    14969 
8.970173 8.696339 8.573637 8.452598 8.323054 8.257669 

And now, we can run the gseGO from clusterProfiler. You’ll see, the interfacce is almost the same as for the enrichment:

# We use the function `gseGO` from the package `clusterProfiler`
# and store the result in a variable called `gsea_wilcox`
gsea_wilcox <- clusterProfiler::gseGO(
  # We provide the variable pointing to named gene list
  geneList = gene_list,
  # We provide mouse annotations
  OrgDb = org.Mm.eg.db,
  # We tell the function that we use entrez-identifiers
  keyType = "ENTREZID",
  # We search results on Biological Process"
  ont = "BP",
  # We want all the results
  pvalueCutoff = 1
)

Just like before, we can have a look at pathways including the word G2M, using the functions with and grepl from base package.

# Get the result table
results_gse_wilcox <- enrich_wilcox@result

# We store the result of the line selection
# in a variable called `g2m_enrich`
g2m_gse <- results_gse_wilcox[
  # We select rows containing a given information
  # using the function `with` from `base` package
  base::with(
    # We provide the variable pointing to enrichment results
    data = results_gse_wilcox,
    # We provide the term we are searching and the column in which
    # the term should be searched
    base::grepl("G2/M", Description)
  ), # Do not forget his comma, since we are filtering a dataframe on rows !
]

# Check the results with the function `head` from `utils` package
utils::head(g2m_gse)
                   ID
GO:1902751 GO:1902751
GO:0010971 GO:0010971
GO:1902749 GO:1902749
GO:0044839 GO:0044839
GO:0000086 GO:0000086
GO:0010389 GO:0010389
                                                            Description
GO:1902751      positive regulation of cell cycle G2/M phase transition
GO:0010971 positive regulation of G2/M transition of mitotic cell cycle
GO:1902749               regulation of cell cycle G2/M phase transition
GO:0044839                             cell cycle G2/M phase transition
GO:0000086                        G2/M transition of mitotic cell cycle
GO:0010389          regulation of G2/M transition of mitotic cell cycle
           GeneRatio  BgRatio     pvalue  p.adjust    qvalue
GO:1902751     3/394  23/9476 0.06825935 0.4259430 0.3891670
GO:0010971     2/394  20/9476 0.20114319 0.6661140 0.6086015
GO:1902749     5/394  90/9476 0.31953152 0.7945751 0.7259713
GO:0044839     6/394 112/9476 0.32235419 0.7945751 0.7259713
GO:0000086     5/394  97/9476 0.37773602 0.7945751 0.7259713
GO:0010389     4/394  80/9476 0.42724795 0.7945751 0.7259713
                                     geneID Count
GO:1902751                   Cdk4/Mta3/Npm1     3
GO:0010971                        Cdk4/Mta3     2
GO:1902749       Babam2/Cdk4/Cdk6/Mta3/Npm1     5
GO:0044839 Babam2/Calm1/Cdk4/Cdk6/Mta3/Npm1     6
GO:0000086      Babam2/Calm1/Cdk4/Cdk6/Mta3     5
GO:0010389            Babam2/Cdk4/Cdk6/Mta3     4

4.4 Visualize results

This makes a lot of tables. Let’s make a lot of graphs !

We can use the function barplot from package graphics ; and not the ones from any other package, or it won’t work !

# We use the function `barplot` from package `enrichplot`
graphics::barplot(
  # We provide the variable pointing to enrichment results
  height = enrich_wilcox,
  # We display the best 15 results
  howCategory=15
)

We can also use the function ditplot from package enrichplot ; and not the ones from any other package, or it won’t work ! Note the change in the input parameter name:

# We use the function `barplot` from package `enrichplot`
enrichplot::dotplot(
  # We provide the variable pointing to enrichment results
  object = enrich_wilcox,
  # We display the best 15 results
  showCategory=15
)

We can display traditional GSEA graph using gseaplot2 from package enrichplot:

# We use the function `barplot` from package `enrichplot`
enrichplot::gseaplot2(
  # We provide the variable pointing to enrichment results
  x = gsea_wilcox,
  # We display the best result
  geneSetID = 1
)

With GSEA, you dot not test if a pathway is up or down regulated. A pathway contains both enhancers and suppressors genes. An up-regulation of enhancer genes and a down-regulation of suppressor genes will lead to a “bad” enrichment score. However, this will lead to a strong change in your pathway activity!

If your favorite pathway does not have a “good enrichment score”, it does not mean that pathway is not affected.

The heatplot displays both a heatmap of enriched pathways and their genes in commons:

# We use the function `heatplot` from `enrichplot` package
enrichplot::heatplot(
  # We probide the variable pointing to GSEA results
  x = gsea_wilcox,
  # We show the 15 best results
  showCategory = 15,
  # We color according to FoldChange
  foldChange = gene_list
)

The genes in common between multiple gene sets are also visible through an uspet plot:

# We use the function `upsetplot` from `enrichplot` package
enrichplot::upsetplot(
  # We probide the variable pointing to GSEA results
  x = gsea_wilcox,
  # We show the 10 best results
  n = 10
)

Finally, we can display whole pathways using KEGG database:

# We use the function pathview from pathview package
pathview::pathview(
  gene.data = gene_list, # Our gene list
  pathway.id = "mmu04110", # Our pathway
  species = "mmu", # Our organism
  # The color limits
  limit = list(gene=max(abs(gene_list))),
  gene.idtype = "ENTREZID" # The genes identifiers
)

pathview_results

4.5 Session Info

This list of all packages used while you work should be included in each en every R presentation:

utils::sessionInfo()
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 20.04.6 LTS

Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.27.so;  LAPACK version 3.12.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Paris
tzcode source: system (glibc)

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
 [1] pathview_1.44.0             Cairo_1.6-2                
 [3] org.Mm.eg.db_3.19.1         AnnotationDbi_1.66.0       
 [5] dittoSeq_1.16.0             clusterProfiler_4.12.6     
 [7] escape_2.0.0                SingleCellExperiment_1.26.0
 [9] SummarizedExperiment_1.34.0 Biobase_2.64.0             
[11] GenomicRanges_1.56.2        GenomeInfoDb_1.40.1        
[13] IRanges_2.38.1              S4Vectors_0.42.1           
[15] BiocGenerics_0.50.0         MatrixGenerics_1.16.0      
[17] matrixStats_1.4.1           Seurat_5.1.0               
[19] SeuratObject_5.0.2          sp_2.1-4                   
[21] dplyr_1.1.4                 knitr_1.48                 
[23] rstatix_0.7.2               ggpubr_0.6.0               
[25] ggplot2_3.5.1               DT_0.33                    
[27] BiocParallel_1.38.0        

loaded via a namespace (and not attached):
  [1] SpatialExperiment_1.14.0  R.methodsS3_1.8.2        
  [3] GSEABase_1.66.0           nnet_7.3-19              
  [5] goftest_1.2-3             Biostrings_2.72.1        
  [7] HDF5Array_1.32.0          vctrs_0.6.5              
  [9] spatstat.random_3.3-2     digest_0.6.37            
 [11] png_0.1-8                 ggrepel_0.9.6            
 [13] deldir_2.0-4              parallelly_1.38.0        
 [15] magick_2.8.5              MASS_7.3-61              
 [17] reshape2_1.4.4            httpuv_1.6.15            
 [19] qvalue_2.36.0             withr_3.0.1              
 [21] xfun_0.48                 ggfun_0.1.6              
 [23] survival_3.7-0            memoise_2.0.1            
 [25] gson_0.1.0                tidytree_0.4.6           
 [27] zoo_1.8-12                KEGGgraph_1.64.0         
 [29] pbapply_1.7-2             ggdist_3.3.2             
 [31] R.oo_1.26.0               Formula_1.2-5            
 [33] KEGGREST_1.44.0           promises_1.3.0           
 [35] httr_1.4.7                globals_0.16.3           
 [37] fitdistrplus_1.2-1        rhdf5filters_1.16.0      
 [39] rhdf5_2.48.0              rstudioapi_0.17.0        
 [41] UCSC.utils_1.0.0          miniUI_0.1.1.1           
 [43] generics_0.1.3            DOSE_3.30.1              
 [45] base64enc_0.1-3           babelgene_22.9           
 [47] zlibbioc_1.50.0           ScaledMatrix_1.12.0      
 [49] ggraph_2.2.1              polyclip_1.10-7          
 [51] GenomeInfoDbData_1.2.12   SparseArray_1.4.8        
 [53] xtable_1.8-4              stringr_1.5.1            
 [55] evaluate_1.0.1            S4Arrays_1.4.1           
 [57] irlba_2.3.5.1             colorspace_2.1-1         
 [59] ROCR_1.0-11               reticulate_1.39.0        
 [61] spatstat.data_3.1-2       Rgraphviz_2.48.0         
 [63] magrittr_2.0.3            lmtest_0.9-40            
 [65] later_1.3.2               viridis_0.6.5            
 [67] ggtree_3.12.0             lattice_0.22-6           
 [69] spatstat.geom_3.3-3       future.apply_1.11.2      
 [71] scattermore_1.2           XML_3.99-0.17            
 [73] shadowtext_0.1.3          cowplot_1.1.3            
 [75] ggupset_0.4.0             RcppAnnoy_0.0.22         
 [77] Hmisc_5.1-3               pillar_1.9.0             
 [79] nlme_3.1-165              compiler_4.4.1           
 [81] beachmat_2.20.0           RSpectra_0.16-2          
 [83] stringi_1.8.4             tensor_1.5               
 [85] plyr_1.8.9                msigdbr_7.5.1            
 [87] crayon_1.5.3              abind_1.4-8              
 [89] gridGraphics_0.5-1        org.Hs.eg.db_3.19.1      
 [91] graphlayouts_1.1.1        bit_4.5.0                
 [93] fastmatch_1.1-4           codetools_0.2-20         
 [95] BiocSingular_1.20.0       crosstalk_1.2.1          
 [97] bslib_0.8.0               plotly_4.10.4            
 [99] mime_0.12                 splines_4.4.1            
[101] Rcpp_1.0.13               fastDummies_1.7.4        
[103] sparseMatrixStats_1.16.0  HDO.db_0.99.1            
[105] blob_1.2.4                utf8_1.2.4               
[107] fs_1.6.4                  listenv_0.9.1            
[109] checkmate_2.3.1           DelayedMatrixStats_1.26.0
[111] GSVA_1.52.3               ggsignif_0.6.4           
[113] ggplotify_0.1.2           tibble_3.2.1             
[115] Matrix_1.7-1              tweenr_2.0.3             
[117] pkgconfig_2.0.3           pheatmap_1.0.12          
[119] tools_4.4.1               cachem_1.1.0             
[121] RSQLite_2.3.7             viridisLite_0.4.2        
[123] DBI_1.2.3                 fastmap_1.2.0            
[125] rmarkdown_2.28            scales_1.3.0             
[127] grid_4.4.1                ica_1.0-3                
[129] broom_1.0.7               sass_0.4.9               
[131] patchwork_1.3.0           dotCall64_1.2            
[133] graph_1.82.0              carData_3.0-5            
[135] RANN_2.6.2                rpart_4.1.23             
[137] snow_0.4-4                farver_2.1.2             
[139] tidygraph_1.3.1           scatterpie_0.2.4         
[141] yaml_2.3.10               foreign_0.8-86           
[143] cli_3.6.3                 purrr_1.0.2              
[145] UCell_2.8.0               leiden_0.4.3.1           
[147] lifecycle_1.0.4           uwot_0.2.2               
[149] backports_1.5.0           annotate_1.82.0          
[151] gtable_0.3.5              rjson_0.2.21             
[153] ggridges_0.5.6            progressr_0.14.0         
[155] parallel_4.4.1            ape_5.8                  
[157] jsonlite_1.8.9            bitops_1.0-9             
[159] RcppHNSW_0.6.0            bit64_4.5.2              
[161] Rtsne_0.17                yulab.utils_0.1.7        
[163] spatstat.utils_3.1-0      BiocNeighbors_1.22.0     
[165] highr_0.11                jquerylib_0.1.4          
[167] GOSemSim_2.30.2           spatstat.univar_3.0-1    
[169] distributional_0.4.0      R.utils_2.12.3           
[171] lazyeval_0.2.2            shiny_1.9.1              
[173] htmltools_0.5.8.1         enrichplot_1.24.4        
[175] GO.db_3.19.1              sctransform_0.4.1        
[177] rappdirs_0.3.3            glue_1.8.0               
[179] spam_2.11-0               httr2_1.0.1              
[181] XVector_0.44.0            RCurl_1.98-1.14          
[183] treeio_1.28.0             gridExtra_2.3            
[185] AUCell_1.26.0             igraph_2.1.1             
[187] R6_2.5.1                  tidyr_1.3.1              
[189] labeling_0.4.3            ggpointdensity_0.1.0     
[191] cluster_2.1.6             Rhdf5lib_1.26.0          
[193] aplot_0.2.3               DelayedArray_0.30.1      
[195] tidyselect_1.2.1          htmlTable_2.4.2          
[197] ggforce_0.4.2             car_3.1-3                
[199] future_1.34.0             rsvd_1.0.5               
[201] munsell_0.5.1             KernSmooth_2.23-24       
[203] data.table_1.16.2         htmlwidgets_1.6.4        
[205] fgsea_1.30.0              RColorBrewer_1.1-3       
[207] rlang_1.1.4               spatstat.sparse_3.1-0    
[209] spatstat.explore_3.3-2    fansi_1.0.6              
LS0tCnRpdGxlOiAiPENFTlRFUj5FQkFJSSBuMSAyMDI0PEJSIC8+PEI+R2VuZSBTZXQgRW5yaWNobWVudCBBbmFseXNpczwvQj48L0NFTlRFUj4iCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKYXV0aG9yOgogIC0gbmFtZTogIlRoaWJhdWx0IERBWVJJUyIKICAgIGVtYWlsOiAidGhpYmF1bHQuZGF5cmlzQGd1c3RhdmVyb3Vzc3kuZnIiCiAgLSBuYW1lOiAiQmFzdGllbiBKT0IiCiAgICBlbWFpbDogImJhc3RpZW4uam9iQGd1c3RhdmVyb3Vzc3kuZnIiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiAgIyBEZWZhdXRsIHZpZXcKICAgIGhpZ2hsaWdodDogdGFuZ28gICMjIFRoZW1lIGZvciB0aGUgY29kZSBjaHVua3MKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZSAgIyMgQWRkcyBudW1iZXIgdG8gaGVhZGVycyAoc2VjdGlvbnMpCiAgICB0aGVtZTogZmxhdGx5ICAjIyBDU1MgdGhlbWUgZm9yIHRoZSBIVE1MIHBhZ2UKICAgIHRvYzogdHJ1ZSAgIyMgQWRkcyBhIHRhYmxlIG9mIGNvbnRlbnQKICAgIHRvY19mbG9hdDogICMjIFRPQyBvcHRpb25zCiAgICAgIGNvbGxhcHNlZDogdHJ1ZSAgIyMgQnkgZGVmYXVsdCwgdGhlIFRPQyBpcyBmb2xkZWQKICAgICAgc21vb3RoX3Njcm9sbDogdHJ1ZSAjIyBTbW9vdGggc2Nyb2xsIG9mIHRoZSBIVE1MIHBhZ2UKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlICMjIEluY2x1ZGVzIGFsbCBwbG90cy9pbWFnZXMgd2l0aGluIHRoZSBIVE1MCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMjIEFkZHMgYSBidXR0b24gdG8gZG93bmxvYWQgdGhlIFJtZAogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICB0aHVtYm5haWxzOiBmYWxzZQogICAgbGlnaHRib3g6IHRydWUKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBnYWxsZXJ5OiB0cnVlCiAgICB1c2VfYm9va2Rvd246IHRydWUKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUgIyMgQWxsb3cgcGxhaW4gSFRNTCBjb2RlIGluIHRoZSBSbWQKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDg4Ci0tLQoKCjwhLS0gQWRkIHRoZSBSb3Njb2ZmIGJhbm5lciAtLT4KCmBgYHtjc3MgYmFubmVyLCBlY2hvID0gRkFMU0V9CmJvZHkgewogIGJhY2tncm91bmQtaW1hZ2U6IHVybCgnZWJhaWlfYmFubmVyLnBuZycpOwogIGJhY2tncm91bmQtcmVwZWF0OiBuby1yZXBlYXQ7CiAgYmFja2dyb3VuZC1zaXplOiAxMDAlOwogIG1hcmdpbjogMTAlCn0KCmRpdiB7CiAgYmFja2dyb3VuZC1jb2xvcjogcmdiYSgyNTUsIDI1NSwgMjU1LCAwLjM1KSAgIC8qIDM1JSBvcGFxdWUgd2hpdGUgKi87CiAgcGFkZGluZzogMC4yNWVtOwp9CmBgYAoKPCEtLSBBbGxvd3MgdG8gaGlkZSB0aGUgVE9DIGJ5IGRlZmF1bHQsIGRpc3BsYXkgaXQgd2l0aCBhIGJ1dHRvbiwgbW92ZSBpdCB0byB0aGUgcmlnaHQgb3IgbGVmdCBvZiB0aGUgcGFnZSAtLT4KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFLCBlY2hvPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAjIFByaW50IHRoZSBjb2RlCiAgZXZhbCA9IFRSVUUsICAgICAgICMgRG8gbm90IHJ1biBjb21tYW5kIGxpbmVzCiAgbWVzc2FnZSA9IEZBTFNFLCAgICAjIFByaW50IG1lc3NhZ2VzCiAgcHJvbXB0ID0gRkFMU0UsICAgICAjIERvIG5vdCBkaXNwbGF5IHByb21wdAogIGNvbW1lbnQgPSBOQSwgICAgICAgIyBObyBjb21tZW50cyBvbiB0aGlzIHNlY3Rpb24KICB3YXJuaW5nID0gRkFMU0UsICAgICAjIERpc3BsYXkgd2FybmluZ3MKICB0aWR5ID0gRkFMU0UsCiAgZmlnLmFsaWduID0gImNlbnRlciIsCiAgd2lkdGggPSAxMDAgICAgICAgIyBOdW1iZXIgb2YgY2hhcmFjdGVycyBwZXIgbGluZQopCmhvb2tzID0ga25pdHI6OmtuaXRfaG9va3MkZ2V0KCkKaG9va19mb2xkYWJsZSA9IGZ1bmN0aW9uKHR5cGUpIHsKICBmb3JjZSh0eXBlKQogIGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgIHJlcyA9IGhvb2tzW1t0eXBlXV0oeCwgb3B0aW9ucykKCiAgICBpZiAoaXNGQUxTRShvcHRpb25zW1twYXN0ZTAoImZvbGQuIiwgdHlwZSldXSkpIHJldHVybihyZXMpCgogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+U2hvdyAiLCB0eXBlLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0KSG1pc2M6OmhpZGluZ1RPQygKICBidXR0b25MYWJlbCA9ICdTaG93IFRPQycsIAogIGhpZGRlbiA9IFRSVUUsIAogIHRvY1NpZGUgPSAnbGVmdCcsIAogIGJ1dHRvblNpZGU9J2xlZnQnLCAKICBwb3NDb2xsYXBzZSA9ICdtYXJnaW4nLCAKICBsZXZlbHMgPSAzCikKbXlfc2VlZCA8LSAxMzM3TApgYGAKCjwhLS0gQ1NTIHRvIGNvbG9yIGNodW5rcyBhbmQgb3V0cHV0cyAtLT4KCmBgYHtjc3MgY2h1bmtzLCBlY2hvID0gRkFMU0V9CmRpdi5iZXlvbmQgcHJlIHsgYmFja2dyb3VuZC1jb2xvcjogcGluazsgY29sb3IgOiBibGFjazsgfQpkaXYuYmV5b25kIHByZS5yIHsgYmFja2dyb3VuZC1jb2xvcjogbGlnaHRibHVlOyBib3JkZXI6IDNweCBzb2xpZCBibHVlOyB9CmRpdi5ub3RydW4gcHJlIHsgYmFja2dyb3VuZC1jb2xvcjogbGlnaHR5ZWxsb3c7IGNvbG9yIDogYnJvd247IH0KZGl2Lm5vdHJ1biBwcmUuciB7IGJhY2tncm91bmQtY29sb3I6IGxpZ2h0Z3JleTsgYm9yZGVyOiAzcHggc29saWQgYmxhY2s7IH0KYGBgCgo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpkZXRhaWxzOmhvdmVyIHsgCiAgY3Vyc29yOiBwb2ludGVyIAp9CmJvZHkgewogIHRleHQtYWxpZ246IGp1c3RpZnkKfQouY29sdW1uLWxlZnR7CiAgZmxvYXQ6IGxlZnQ7CiAgd2lkdGg6IDQ3JTsKICB0ZXh0LWFsaWduOiBsZWZ0Owp9Ci5jb2x1bW4tcmlnaHR7CiAgZmxvYXQ6IHJpZ2h0OwogIHdpZHRoOiA0NyU7CiAgdGV4dC1hbGlnbjogbGVmdDsKfQo8L3N0eWxlPgoKIyBGb3Jld29yZHMKCiMjIFRMRFI6IFIgY29tbWFuZCBsaW5lcwoKSW4gdGhpcyBwcmVzZW50YXRpb24sIHRoZXJlIHdpbGwgYmUgc2NyZWVuIGNhcHR1cmVzIGZvciB5b3UgdG8gZm9sbG93IHRoZSAKbGVzc29uLiBUaGVyZSB3aWxsIGFsc28gYmUgZXZlcnkgc2luZ2xlIFIgY29tbWFuZCBsaW5lcy4gCkRvIG5vdCB0YWtlIGNhcmUgb2YgdGhlIGNvbW1hbmQgbGluZXMgaWYgeW91IGZpbmQgdGhlbSB0b28gY2hhbGxlbmdpbmcuIApPdXIgZ29hbCBoZXJlLCBpcyB0byB1bmRlcnN0YW5kIHRoZSBtYWluIG1lY2hhbmlzbSBvZiBEaWZmZXJlbnRpYWwgCkV4cHJlc3Npb24gQW5hbHlzaXMuIFIgaXMganVzdCBhIHRvb2wuCgpCZWxvdyBhcmUgdGhlIGxpYnJhcmllcyB3ZSBuZWVkIHRvIHBlcmZvcm0gdGhpcyB3aG9sZSBzZXNzaW9uOgoKYGBge3IgbG9hZF9saWJyYXJpZXMsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQpiYXNlOjpsaWJyYXJ5KHBhY2thZ2UgPSAiQmlvY1BhcmFsbGVsIikgICAgIyBPcHRpb25hbGx5IG11bHRpdGhyZWFkIHNvbWUgc3RlcHMKYmFzZTo6bGlicmFyeShwYWNrYWdlID0gIkRUIikgICAgICAgICAgICAgICMgRGlzcGxheSBuaWNlIHRhYmxlIGluIEhUTUwKYmFzZTo6bGlicmFyeShwYWNrYWdlID0gImdncGxvdDIiKSAgICAgICAgICMgRHJhdyBncmFwaHMgYW5kIHBsb3RzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJnZ3B1YnIiKSAgICAgICAgICAjIERyYXcgbmljZXIgZ3JhcGhzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJyc3RhdGl4IikgICAgICAgICAjIEJhc2UgUiBzdGF0aXN0aWNzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJrbml0ciIpICAgICAgICAgICAjIEJ1aWxkIHRoaXMgcHJlc2VudGF0aW9uCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJkcGx5ciIpICAgICAgICAgICAjIEhhbmRsZSBiaWcgdGFibGVzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJTZXVyYXQiKSAgICAgICAgICAjIEhhbmRsZSBTaW5nbGVDZWxsIGFuYWx5c2VzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJTZXVyYXRPYmplY3QiKSAgICAjIEhhbmRsZSBTaW5nbGVDZWxsIG9iamVjdHMKYmFzZTo6bGlicmFyeShwYWNrYWdlID0gIlNpbmdsZUNlbGxFeHBlcmltZW50IikgIyBIYW5kbGUgU2luZ2xlQ2VsbCBmaWxlIGZvcm1hdHMKYmFzZTo6bGlicmFyeShwYWNrYWdlID0gImVzY2FwZSIpICAgICAgICAgICMgUGVyZm9ybSBleHBsb3JhdG9yeSBlbnJpY2htZW50IGFuYWx5c2lzCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJjbHVzdGVyUHJvZmlsZXIiKSAjIFBlcmZvcm0gR1NFQSBhbmFseXNpcwpiYXNlOjpsaWJyYXJ5KHBhY2thZ2UgPSAiZGl0dG9TZXEiKSAgICAgICAgIyBEcmF3IG5pY2UgcGxvdHMgYmFzZWQgb24gU2V1cmF0CmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJvcmcuTW0uZWcuZGIiKSAgICAjIE1vdXNlIGdlbm9tZSBhbm5vdGF0aW9uCmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJDYWlybyIpICAgICAgICAgICAjIEdyYXBocyBsaWJyYXJ5CmJhc2U6OmxpYnJhcnkocGFja2FnZSA9ICJwYXRodmlldyIpICAgICAgICAjIEZvciB0aGUgd2hvbGUgcGF0aHdheSBncmFwaApgYGAKCkZpcnN0LCB3ZSBsb2FkIFNldXJhdCBvYmplY3Q6CgpgYGB7ciBsb2FkX3Jkc190bGRyLCBldmFsPVRSVUUsIGVjaG89VFJVRX0Kc29iaiA8LSBiYXNlOjpyZWFkUkRTKAogICMgUGF0aCB0byB0aGUgUkRTIGZpbGUKICBmaWxlID0gIkRFQV9TY2FsZWRfTm9ybWFsaXplZF9GaWx0ZXJlZC5SRFMiCikKYGBgCgpUaGVuIHdlIGxhdW5jaCBlbnJpY2htZW50IGV4cGxvcmF0aW9uIG9uIGFsbCBjb3VudHM6CgpgYGB7ciBlc2NhcGVfZW5yaWNobWVudF90bGRyLCBldmFsPUZBTFNFLCBlY2hvPVRSVUV9CiMgQWNxdWlyZSBnZW5lIHNldHMKbWhfaGFsbG1hcmsgPC0gZXNjYXBlOjpnZXRHZW5lU2V0cygKICBzcGVjaWVzID0gIk11cyBtdXNjdWx1cyIsCiAgbGlicmFyeSA9ICJIIgopCgojIFJ1biBlbnJpY2htZW50CmVucmljaG1lbnRfbWF0cml4IDwtIGVzY2FwZTo6ZXNjYXBlLm1hdHJpeCgKICBpbnB1dC5kYXRhID0gc29iaiwKICBnZW5lLnNldHMgPSBtaF9oYWxsbWFyaywKICBtZXRob2QgPSAic3NHU0VBIiwKICBncm91cHMgPSAgMTAwMCwKICBtaW4uc2l6ZSA9IDUsCiAgQlBQQVJBTSA9IEJpb2NQYXJhbGxlbDo6U25vd1BhcmFtKHdvcmtlcnMgPSAyKSwKKQoKIyBTYXZlIGVucmljaG1lbnQgaW4gc2V1cmF0IG9iamVjdApzb2JqW1siZXNjYXBlIl1dIDwtIFNldXJhdE9iamVjdDo6Q3JlYXRlQXNzYXk1T2JqZWN0KAogIGRhdGE9dChlbnJpY2htZW50X21hdHJpeCksCikKCnNhdmVSRFMoc29iaiwgZmlsZSA9ICJzb2JqX0dTRUEuUkRTIikKYGBgCgoKIyMgUHVycG9zZSBvZiB0aGlzIHNlc3Npb24KClVwIHRvIG5vdywgd2UgaGF2ZToKCjEuIElkZW50aWZpZWQgdG8gd2hpY2ggY2VsbCBlYWNoIHNlcXVlbmNlZCByZWFkcyBjb21lIGZyb20KMS4gSWRlbnRpZmllZCB0byB3aGljaCBnZW5lIGVhY2ggcmVhZCBjb21lIGZyb20KMS4gSWRlbnRpZmllZCBwb3NzaWJsZSBiaWFzIGluIGdlbmUgZXhwcmVzc2lvbiBmb3IgZWFjaCBjZWxsCjEuIEZpbHRlcmVkIGFuZCBjb3JyZWN0ZWQgdGhlc2UgYmlhcyBhcyB3ZWxsIGFzIHdlIGNhbgoxLiBGb3VuZCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgYWNyb3NzIG11bHRpcGxlIGNvbmRpdGlvbnMKMS4gQW5ub3RhdGVkIGNlbGwgY2x1c3RlcnMKCldlIHdvdWxkIGxpa2UgdG8gaWRlbnRpZnkgdGhlIGZ1bmN0aW9ucyBvZiBnZW5lcyBhbW9uZyBzZXZlcmFsIGNsdXN0ZXJzLApvciBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMuCgpBdCB0aGUgZW5kIG9mIHRoaXMgc2Vzc2lvbiB5b3Ugd2lsbCBrbm93OgoKMS4gV2hhdCBpcyBnZW5lIHNldCBhbmFseXNpcwoxLiBIb3cgdG8gY2hvb3NlIGEgR2VuZSBTZXQgZGF0YWJhc2UKMS4gSG93IHRvIHBlcmZvcm0gYW4gZW5yaWNobWVudCBhbmFseXNpcwoxLiBIb3cgdG8gcmVhZCBHZW5lIHNldCBhbmFseXNpcyByZXN1bHRzCgoKIyBTZWxlY3QgYSBkYXRhYmFzZQoKIyMgR2VuZTogUGxhYzgKCkxldCdzIHNlYXJjaCBpbmZvcm1hdGlvbiBhYm91dCB0aGlzIGdlbmUgb24gdGhlIHdlYi4gRm9yIG1pY2UsIG9uZSBvZiB0aGUKYmVzdCB3ZWItc2l0ZSBmb3IgaHVtYW4gaXM6IFtNR0ldKGh0dHBzOi8vd3d3LmluZm9ybWF0aWNzLmpheC5vcmcvKS4KCklmIHdlIHNlYXJjaCBmb3IgdGhlIGdlbmUgW1BsYWM4XShodHRwczovL3d3dy5pbmZvcm1hdGljcy5qYXgub3JnL21hcmtlci9NR0k6MjQ0NTI4OSksCndlIGZpbmQgdGhlIGZvbGxvd2luZzoKCiFbcGxhYzhfbWdpXShpbWFnZXMvcGxjYThfbWdpLnBuZykKCkFzIHdlIGNsaWNrIG9uICJQaGVub3R5cGUgcmVmZXJlbmNlIiwgd2UgY2FuIHNlZSB0aGF0IHRoZSBNR0kgZGF0YWJhc2UgY2FuIGdpdmUKdXMgW1B1Yk1lYiBJRHNdKGh0dHBzOi8vcHVibWVkLm5jYmkubmxtLm5paC5nb3YvKSByZWxhdGVkIHRvIHRoZSBnZW5lIFBsY2E4LgpCZWxvdywgb24gdGhlIHNhbWUgcGFnZSwgd2UgY2FuIHNlZSByZWd1bGF0aW9uIHBhdGh3YXlzLCBwcm90ZWluIG9udG9sb2d5LApnZW5lIGludGVyYWN0aW9ucywgZXRjLgoKIVtwbGNhOF9wYXRod2F5XShpbWFnZXMvcGxhYzhfcGF0aHdheXMucG5nKQoKV2UgaWxsdXN0cmF0ZSBoZXJlLCB0aGF0IEdTRUEgZG9lcyBub3Qgb25seSBpbmNsdWRlIGdlbmUtdG8tZnVuY3Rpb24sIG9yCmdlbmUtdG8tcGF0aHdheS4gVGhleSB1c3VhbGx5IGluY2x1ZGUgbWFueSBtb3JlIGluZm9ybWF0aW9uLgoKIyMgRGF0YWJhc2UgdHlwZXMKCiFbZGJfdHlwZXNdKGltYWdlcy9nZW5lX3NldHMucG5nKQoKVGhlcmUgaXMgYSBkYXRhYmFzZSBmb3IgcHJldHR5IG11Y2ggZXZlcnl0aGluZyB5b3UgbWlnaHQgd2FudC4gRnJvbQpwaGFybWFjb2xvZ3ksIHRvIHJlZ3VsYXRvcnkgb2JqZWN0cywgZXRjLgoKT3VyIGRhdGFzZXQgY29udGFpbnMgZ2VuZSBleHByZXNzaW9uLiBCdXQgdGhlIHNhbWUgbWV0aG9kcyBjYW4gYmUgYXBwbGllZCB0bwpvdGhlciBraW5kIG9mIGRhdGFzZXRzLiBfRS5nLl8KCiFbcHBpc10oaW1hZ2VzL3BwaXMucG5nKQoKUHJvdGVpbi1wcm90ZWluIGludGVyYWN0aW9ucywgZHJ1ZyBpbnRlcmFjdGlvbnMsIFNOUC1pbnRlcmFjdGlvbnMsIGV0Yy4KCldpdGhpbiBSLCB5b3UgbWF5IGZpbmQgZ2Vub21lIGRhdGFiYXNlcyBmb3IgYSB3aWRlIHJhbmdlIG9mIG9yZ2FuaXNtczoKCiFbb3JnX2JkXShpbWFnZXMvb3JnX2RiLnBuZykKCiMjIFNvbWUgbm90aWNlYWJsZSBkYXRhYmFzZXMKCmBgYHtyIGRhdGFiYXNlc19leGFtcGxlLCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CmRhdGFiYXNlcyA8LSBiYXNlOjpkYXRhLmZyYW1lKAogIE1TaWdEQiA9IGMoImh0dHBzOi8vd3d3LmdzZWEtbXNpZ2RiLm9yZy9nc2VhL2luZGV4LmpzcCIpLAogIEtFR0cgPSBjKCJodHRwczovL3d3dy5nZW5vbWUuanAva2VnZy8iKSwKICBHTyA9IGMoImh0dHBzOi8vd3d3LmdlbmVvbnRvbG9neS5vcmcvIiksCiAgRW5zZW1ibCA9IGMoImh0dHBzOi8vZnRwLmVuc2VtYmwub3JnL3B1Yi8iKSwKICBQYW50aGVyID0gYygiaHR0cHM6Ly9wYW50aGVyZGIub3JnLyIpLAogIEphc3BhciA9IGMoImh0dHBzOi8vamFzcGFyLmdlbmVyZWcubmV0LyIpLAogIEdXQVNjYXQgPSBjKCJodHRwczovL3d3dy5lYmkuYWMudWsvZ3dhcy8iKSwKICBCU19HZW5vbWUgPSBjKCJodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9CaW9jVmlld3MuaHRtbCNfX19CU2dlbm9tZSIpLAogIE1lU0ggPSBjKCJodHRwczovL3d3dy5ubG0ubmloLmdvdi9tZXNoL21lc2hob21lLmh0bWwiKSwKICBPcmdEYiA9IGMoImh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL0Jpb2NWaWV3cy5odG1sI19fX09yZ0RiIiksCiAgTkNHID0gYygiaHR0cDovL3d3dy5uZXR3b3JrLWNhbmNlci1nZW5lcy5vcmcvIiksCiAgUGhhcm1HS0IgPSBjKCJodHRwczovL3d3dy5waGFybWdrYi5vcmcvIikKKQoKYmFzZTo6cm93bmFtZXMoZGF0YWJhc2VzKSA8LSBjKCJBZGRyZXNzIikKCkRUOjpkYXRhdGFibGUoYmFzZTo6dChkYXRhYmFzZXMpKQpgYGAKCkZvciB0aGlzIHNlc3Npb24sIHdlIGFyZSBnb2luZyB0byB1c2UgTVNpZ0JELgoKIyMgSG93IHRvIHBlcmZvcm0gR1NFQQoKVGhlcmUgYXJlIHR3byBtYWluIHR5cGVzIG9mIEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXM6CjEuIEEgYmxpbmQgc2VhcmNoIGFtb25nIGFsbCBnZW5lcyBpbiBvcmRlciB0byBpZGVudGlmeSBwYXRod2F5cyBvciBtZWNoYW5pc21zIGFtb25nIGNlbGxzLgoxLiBBIHNwZWNpZmljIHNlYXJjaCB0aHJvdWdoIGdlbmVzIG9mIGludGVyZXN0CgpUaGUgZmlyc3QgaXMgY2FsbGVkICJFeHBsb3JhdG9yeSIgYW5hbHlzaXMgYW5kIHN0YXJ0cyBvbiB0aGUgYFNldXJhdGAgb2JqZWN0LgoKTWFueSBwYWNrYWdlcyBwZXJmb3JtIHRoaXMga2luZCBvZiBleHBsb3JhdG9yeSBhbmFseXNpcywgSSBjaG9zZSB0byBwcmVzZW50IApbYGVzY2FwZWBdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9lc2NhcGUuaHRtbCkKZm9yIGl0cyBzaW1wbGUgYW5kIGludHVpdGl2ZSB1c2FnZS4KCldoYXQgcmVhbGx5IG1hdHRlcnMgaXMgdGhlIEdTRUEgbWV0aG9kIHVzZWQgaW4gYmFjay1lbmQuIFRoZXJlIGFyZSBzZXZlcmFsCnZlcnkgd2VsbCBrbm93biBtZXRob2RzOiBbYGZnc2VhYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2Znc2VhLmh0bWwpLApbYGdzdmFgXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL0dTVkEuaHRtbCksCltgRE9TRWBdKGh0dHBzOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9iaW9jL2h0bWwvRE9TRS5odG1sKSwKYW5kIHRoZSBbYE1TaWdEQmBdKGh0dHBzOi8vd3d3LmdzZWEtbXNpZ2RiLm9yZy9nc2VhL2luZGV4LmpzcCkncyB0b29sLiBXaGlsZQpjaG9vc2luZyB5b3VyIGZhdm9yaXRlIFIgcGFja2FnZSB0byBwZXJmb3JtIEdTRUEsIGlmIHRoYXQgcGFja2FnZSB1c2VzIG9uZQpvZiB0aGVzZSBtZXRob2RzLCB0aGVuIHlvdSBzaG91bGQgbm90IGhhdmUgb2RkIHF1ZXN0aW9ucyB0aHJvdWdoIHlvdXIgCnB1YmxpY2F0aW9uIHByb2Nlc3MuCgpUaGVzZSBtZXRob2RzIGFyZSB2ZXJ5IGFsaWtlLCBiYXNlZCBvbiB0aGUgc2FtZSBzdGF0aXN0aWNhbCByZXNvcnRzLCBhbmQKc3VmZmVyIGZvciB0aGUgc2FtZSBsaW1pdGF0aW9ucy4gTGV0J3MgaWxsdXN0cmF0ZSBpdCB3aXRoIGV4YW1wbGVzLgoKIyBFeHBsb3JhdG9yeSBhbmFseXNpcwoKIyMgR2VuZSBzZXRzCgpVcCB0byBub3csIHdlIGFscmVhZHkgZGVmaW5lIHdoYXQncyBhIF9nZW5lIHNldF86IGEgZ3JvdXAgb2YgZ2VuZXMgb2YgaW50ZXJlc3QuCldlIGFsc28ga25vdyB0aGVzZSBraW5nIG9mIHJlbGF0aW9uc2hpcHMgYXJlIHN0b3JlZCBpbiBbYEdNVGBdKGh0dHBzOi8vc29mdHdhcmUuYnJvYWRpbnN0aXR1dGUub3JnL2NhbmNlci9zb2Z0d2FyZS9nc2VhL3dpa2kvaW5kZXgucGhwL0RhdGFfZm9ybWF0cyNHTVQ6X0dlbmVfTWF0cml4X1RyYW5zcG9zZWRfZmlsZV9mb3JtYXRfLjI4LjJBLmdtdC4yOSkKZmlsZXMgb3IgW2BUU1ZgXShodHRwczovL2ZyLndpa2lwZWRpYS5vcmcvd2lraS9UYWJ1bGF0aW9uLXNlcGFyYXRlZF92YWx1ZXMpIApmaWxlcy4KClNpbmNlIHRoZSBiZWdpbm5pbmcgb2Ygb3VyIHdlZWssIHdlJ3ZlIGJlZW4gd29ya2luZyB3aXRoIFIsIGFuZCB0aGUgcXVlc3Rpb24gCm5hdHVyYWxseSBjb21lczogaG93IHRvIG1ha2UvdXNlIGdlbmUgc2V0cyBpbiBSPwoKIyMjIENyZWF0ZSB5b3VyIG93biBnZW5lIHNldAoKVmVyeSBlYXNpbHkuIFdlIG5lZWQgYSAibmFtZWQgW2xpc3RdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9iYXNlL3ZlcnNpb25zLzMuNi4yL3RvcGljcy9saXN0KQpvZiBbdmVjdG9yc10oaHR0cHM6Ly93d3cucmRvY3VtZW50YXRpb24ub3JnL3BhY2thZ2VzL2Jhc2UvdmVyc2lvbnMvMy42LjIvdG9waWNzL2MpIiwKYWxsIGNvbWluZyBmcm9tIGBiYXNlYCBSIHBhY2thZ2UuCgpgYGB7ciBuYW1lZF9saXN0X3ZlY3RvcnMsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQojIFdlIHVzZSB0aGUgZnVuY3Rpb24gbGlzdCBmcm9tIGJhc2UgcGFja2FnZQpwYXRod2F5cyA8LSBiYXNlOjpsaXN0KAogICMgV2UgdXNlIHRoZSBmdW5jdGlvbiBgY2AgZnJvbSBiYXNlIHBhY2thZ2UKICBwcm9saWZlcmF0aW9uID0gYmFzZTo6YygKICAgICJJZm5nIiwJIktyYXMiLAkiTWdhdDUiLAkKICAgICJQcmYxIiwJIlRyZW0xIiwJIlRycDUzIgogICksCiAgZ3Jvd3RoID0gYmFzZTo6YygKICAgICJDZGtuMmEiLCAiRm9zIiwgIklmbmIxIiwKICAgICJScmFzIiwgIkFwYyIsICJTZWxsIgogICkKKQp1dGlsczo6aGVhZChwYXRod2F5cykKYGBgCgpUaGlzIG1ldGhvZCBpcyB2ZXJ5IHVzZWZ1bCB3aGVuIHlvdSB3YW50IHRvIGNvbXBhcmUgeW91ciBjZWxscyB0b3dhcmQKdW5wdWJsaXNoZWQgZGF0YSwgZ2VuZSBzZXRzIGZyb20gYW4gYXJ0aWNsZSBub3Qgc291cmNlZCBpbiBvZmZpY2lhbCBkYXRhYmFzZXMsCmV0Yy4KCiMjIyBMb2FkIGEgZ2VuZSBzZXQgZnJvbSBkaXNrCgpXZSBjYW4gYWxzbyBsb2FkIGEgR01UIGZpbGUgZnJvbSBhIGZpbGUuIFRoaXMgaXMgdmVyeSB1c2VmdWxsIHdoZW4gd2UKZXhwZWN0IGJvdGgga25vd24gYW5kIG5ldyBwYXRod2F5cyB0byBiZSB0ZXN0ZWQsIG9yIGtub3duIHBhdGh3YXlzIHRoYXQgCnNob3VsZCBiZSB1cGRhdGVkLgoKVGhpcyBpcyBkb25lICB3aXRoIHRoZSBmdW5jdGlvbiBbYHJlYWQuZ210YF0oaHR0cHM6Ly9yZHJyLmlvL2Jpb2MvY2x1c3RlclByb2ZpbGVyL21hbi9yZWFkLWdtdC5odG1sKQpmcm9tIHRoZSBbYGNsdXN0ZXJQcm9maWxlcmBdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9jbHVzdGVyUHJvZmlsZXIuaHRtbCkKcGFja2FnZS4KCmBgYHtyIGdldF9nZW5lX3NldHNfZmlsZSwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CnBhdGh3YXlzIDwtIGNsdXN0ZXJQcm9maWxlcjo6cmVhZC5nbXQoCiAgZ210ZmlsZSA9ICJtNS5tcHQudjIwMjMuMS5NbS5zeW1ib2xzLmdtdCIKKQpwYXRod2F5cyA8LSBCaW9jR2VuZXJpY3M6OmxhcHBseSgKICBTNFZlY3RvcnM6OnNwbGl0KAogICAgcGF0aHdheXNbLTFdLCBwYXRod2F5cyR0ZXJtCiAgKSwKICBmdW5jdGlvbih4KSB4W3ggIT0gIiJdCikKdXRpbHM6OmhlYWQocGF0aHdheXMpCmBgYAoKIyMjIExvYWQgYSBnZW5lIHNldCBmcm9tIHRoZSB3ZWIKCldlIGNhbiBhbHNvIGdldCBwYXRod2F5cyBmcm9tIHRoZSB3ZWIgdXNpbmcgdGhlIGZ1bmN0aW9uIFtgZ2V0R2VuZVNldHNgXShodHRwczovL3JkcnIuaW8vYmlvYy9lc2NhcGUvbWFuL2dldEdlbmVTZXRzLmh0bWwpCmZyb20gcGFja2FnZSBbYGVzY2FwZWBdKGh0dHBzOi8vcmRyci5pby9iaW9jL2VzY2FwZS8pLgoKYGBge3IgZ2V0X2dlbmVfc2V0c19tc2lnZGIsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQojIFBheSBhdHRlbnRpb24gdG8gdGhlIGdlbm9tZSBuYW1lLCBpdCdzIGNhc2Ugc2Vuc2l0aXZlCiMgYW5kIGZvbGxvd3MgTVNpZ0RCIG5hbWluZyBzY2hlbWUKbWhfaGFsbG1hcmsgPC0gZXNjYXBlOjpnZXRHZW5lU2V0cygKICBzcGVjaWVzID0gIk11cyBtdXNjdWx1cyIsIAogIGxpYnJhcnkgPSAiSCIKKQp1dGlsczo6aGVhZChwYXRod2F5cykKYGBgCgojIyBSdW4gZXhwbG9yYXRvcnkgYW5hbHlzaXMKCkxldCdzIHRpbWUgdG8gcHJhY3RpY2UuIFVzZSB0aGUgZnVuY3Rpb24gW2Blc2NhcGUubWF0cml4YF0oaHR0cHM6Ly9yZHJyLmlvL2dpdGh1Yi9uY2JvcmNoZXJkaW5nL2VzY2FwZS9tYW4vZXNjYXBlLm1hdHJpeC5odG1sKQpmcm9tIHRoZSBwYWNrYWdlIFtgZXNjYXBlYF0oaHR0cHM6Ly9yZHJyLmlvL2Jpb2MvZXNjYXBlLykgaW4gb3JkZXIgdG8gcGVyZm9ybQpHU0VBIG9uIHlvdXIgW2BTZXVyYXRgXSgpCm9iamVjdC4gUGVyZm9ybSB0aGUgYW5hbHlzaXMgb24gY2VsbCBjeWNsZSBwaGFzZSBpZiBwb3NzaWJsZS4gClNhdmUgdGhlIHJlc3VsdHMgaW4gYSAgdmFyaWFibGUsIGZvciBleGFtcGxlIGBzb2JqX2VucmljaGAuCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcjwvc3VtbWFyeT4KCkl0IGlzIG5vdCBwb3NzaWJsZSB0byBzcGVjaWZ5IHdoYXQgZ3JvdXAgd2UgYXJlIGNvbnNpZGVyaW5nLiBHU0VBL0VucmljaG1lbnQgCmFuYWx5c2lzIGRvIG5vdCBwZXJmb3JtIGFueSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMuCgpgYGB7ciBlc2NhcGVfZW5yaWNoLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBTYXZlIGluIGEgdmFyaWFibGUgY2FsbGVkIGBzb2JqX2VucmljaGAKIyB0aGUgcmVzdWx0IG9mIHRoZSBmdW5jdGlvbiBgcnVuRXNjYXBlYCBmcm9tIHBhY2thZ2UgYGVzY2FwZWAKZW5yaWNobWVudF9tYXRyaXggPC0gZXNjYXBlOjplc2NhcGUubWF0cml4KAogICMgT3V0IFNldXJhdCBvYmplY3QKICBpbnB1dC5kYXRhID0gc29iaiwKICAjIFByb3ZpZGUgZ2VuZSBzZXRzCiAgZ2VuZS5zZXRzID0gbWhfaGFsbG1hcmssCiAgIyBTZWxlY3QgeW91ciBmYXZvcml0ZSBtZXRob2QKICBtZXRob2QgPSAic3NHU0VBIiwKICAjIHNzR1NFQSBwYXJhbWV0ZXJzCiAgZ3JvdXBzID0gIDEwMDAsCiAgbWluLnNpemUgPSA1LAogICMgVXNlIDIgdGhyZWFkcy9DUFUvd29ya2VycwogIEJQUEFSQU0gPSBCaW9jUGFyYWxsZWw6OlNub3dQYXJhbSh3b3JrZXJzID0gMiksCikKYGBgCgpJZiB5b3UgaGF2ZSByZXNlcnZlZCBtb3JlIHRoYW4gb25lIGNvcmUsIHRoaXMgZnVuY3Rpb24gY2FuIGJlIG11bHRpLXRocmVhZGVkLAptYWtpbmcgaXQgZmFzdGVyICEKCjwvZGV0YWlscz4KPGJyIC8+CgoKIyMgVmlzdWFsaXplIHJlc3VsdHMKCgpXZSBjYW4gdmlzdWFsaXNlIHRoZSBnZW5lIHNldHMgZW5yaWNoZWQgaW4gY2VsbCBjbHVzdGVycyB1c2luZyB0aGUgZnVuY3Rpb24gW2BGZWF0dXJlUGxvdGBdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvcmVmZXJlbmNlL2ZlYXR1cmVwbG90KSBmcm9tCmBTZXVyYXRgIHBhY2thZ2UuIFdlIGp1c3QgbmVlZCB0byBjb2xvciB0aGUgY2VsbHMgd2l0aCBgc3NHU0VBYCdzIHJlc3VsdHM6CgpgYGB7ciB1bnNlZW5fbG9hZCwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpzb2JqIDwtIHJlYWRSRFMoInNvYmpfR1NFQS5SRFMiKQpgYGAKCmBgYHtyIGZlYXR1cmVwbG90X3NldXJhdF9zc2dzZWEsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQpTZXVyYXQ6OkZlYXR1cmVQbG90KG9iamVjdCA9IHNvYmosIGZlYXR1cmVzID0gIkhBTExNQVJLLUROQS1SRVBBSVIiKQpgYGAKCiMgT24gcHVycG9zZSBhbmFseXNpcwoKSW4gb3JkZXIgdG8gdW5kZXJzdGFuZCB3aGF0IGhhcHBlbmVkLCB3aGVyZSBkbyB0aGVzZSByZXN1bHRzIGNvbWUgZnJvbSwKd2UgbmVlZCB0byBnbyBzdGVwIGJ5IHN0ZXAgYW5kIHBlcmZvcm0gYm90aCBlbnJpY2htZW50IGFuZCBHU0UgYW5hbHlzaXMKbWFudWFsbHkuCgpJbiBnZW5lcmFsLCB0aGUgZXhwbG9yYXRvcnkgbWV0aG9kIGlzIG5ldmVyIHVzZWQgb24gYSByYXcgU2V1cmF0IG9iamVjdCwKd2UgdXN1YWxseSBmaWx0ZXIgdGhpcyBvYmplY3QgaW4gb3JkZXIgdG8gc2VhcmNoIHBhdGh3YXlzIHRoYXQgYXJlIHJlbGF0ZWQgdG8Kb3VyIGJpb2xvZ2ljYWwgcXVlc3Rpb24uCgpXaXRoIHRoZSBwcmV2aW91cyBtZXRob2QsIHdoYXQgZXZlciBvdXIgYmlvbG9naWNhbCBxdWVzdGlvbiB3YXMsIHRoZSBjZWxsCmN5Y2xlIHBoYXNlIHdhcyBvbmUgb2YgdGhlIHRvcCBnZW5lIGV4cHJlc3Npb24gZHJpdmVycy4KCiMjIEdlbmUgTmFtZXMgYW5kIEdlbmUgaWRlbnRpZmllcnMKCkxldCdzIHNlYXJjaCBpbmZvcm1hdGlvbiBhYm91dCB0aGUgZ2VuZSBgQVJQMmAuCgohW2FycDJfbXVsdGlwbGVdKGltYWdlcy9hcnAyX211bHRpcGxlX2dlbm9tZS5wbmcpCgpTbywgdGhpcyBnZW5lIGV4aXN0cyBpbiBtdWx0aXBsZSBvcmdhbmlzbXMuIFRoYW5rcywgd2Uga25vdyB3ZSBhcmUgd29ya2luZwpvbiBtaWNlLCBhbmQgd2UgdG9sZCBnZW5lIHNldCByZXRyaWV2ZXIgZnVuY3Rpb24gdG8gY29uc2lkZXIgJ21vdXNlJyBkYXRhc2V0cy4KCiFbYXJwMl9uYW1lc10oaW1hZ2VzL2FycDJfbmFtZS5wbmcpCgpTbyBpbiB0aGUgSHVtYW4gZ2Vub21lLCBBUlAyIHJlZmVycyB0byBtdWx0aXBsZSBnZW5lcyB0aHJvdWdoIHRoZSBhbGlhcyBuYW1lcy4KV2hlbiB3ZSBzZWFyY2hlZCBmb3IgZ2VuZSBzZXRzIHdpdGggW2Blc2NhcGVgXSwgd2UgZGlkIG5vdCBwZXJmb3JtIGFueSAKZGlzYW1iaWd1YXRpb24uCgpUaGUgc2FtZSB3aXRoIF9hcmFiaWRvcHNpcyB0aGFsaWFuYV8sIEFSUDIgY2FuIHJlZmVyIHRvIGFjdGluIHJlbGF0ZWQgcHJvdGVpbiwKb3IgYSByb290IGRldmVsb3BtZW50IHByb3RlaW4uCgpJbiBfbXVzIG11c2N1bHVzXywgQVJQMiByZWZlcnMgdG8gYW4gYWN0aW4gcmVsYXRlZCBwcm90ZWluIG9uIGNocm9tb3NvbWUgMTEsIG9yCmEgc3VidW5pdCBhZGFwdG9yIHByb3RlaW4gb24gY2hyb21vc29tZSA1LgoKV2Ugbm93IHVuZGVyc3RhbmQgdGhhdCBgZXNjYXBlYCBhbmFseXNpcyBtYXkgYmUgY29tcGxldGVseSB3cm9uZyBzaW5jZSBpdCB1c2VkCmh1bWFuLWludGVsbGlnaWJsZSBnZW5lIG5hbWVzLiBUaGVzZSBuYW1lcyBpbmNsdWRlIHN5bm9ueW1zLCBob21vbnltcywgd2l0aGluCmEgc2luZ2xlIG9yZ2FuaXNtLCBvciBiZXR3ZWVuIG11bHRpcGxlIG9uZXMuCgpXZSBuZWVkIHRvIHVzZSB1bmlxdWUgaWRlbnRpZmllcnMuIFRoZXNlIGlkZW50aWZpZXJzIGFyZSBjYWxsZWQgZ2VuZSBpZGVudGlmaWVycwphbmQgd2UgdXN1YWxseSBjb25zaWRlciBbYEVuc2VtYmxJRGBdKGh0dHBzOi8vZnRwLmVuc2VtYmwub3JnL3B1Yi9yZWxlYXNlLTYwL2d0Zi9tdXNfbXVzY3VsdXMvKQpvciBbYEVudHJleklEYF0oaHR0cHM6Ly93d3cubmNiaS5ubG0ubmloLmdvdi9XZWIvU2VhcmNoL2VudHJlemZzLmh0bWwpLgoKRm9yIGV4YW1wbGU6CgoxLiBBUlAyIGZyb20gY2hyb21vc29tZSAxMSBlcXVhbHMgW2BFTlNNVVNHMDAwMDAwMjAxNTJgXShodHRwOi8vd3d3LmVuc2VtYmwub3JnL011c19tdXNjdWx1cy9HZW5lL1N1bW1hcnk/Zz1FTlNNVVNHMDAwMDAwMjAxNTI7cj0xMToyMDAxMjMwNC0yMDA2MjkxMykgYXQgRW5zZW1ibC4KMS4gQVJQMiBmcm9tIGNocm9tb3NvbWUgNSBlcXVhbHMgW2BFTlNNVVNHMDAwMDAwMTk1MThgXShodHRwOi8vd3d3LmVuc2VtYmwub3JnL011c19tdXNjdWx1cy9HZW5lL1N1bW1hcnk/Zz1FTlNNVVNHMDAwMDAwMTk1MTg7cj01OjEzODE3MDI2NC0xMzgxNzg2OTEpIGF0IEVuc2VtYmwuCgpUaGVzZSBpZGVudGlmaWVycyBhcmUgdmVyeSB1bnBsZWFzYW50IHRvIHVzZSBmb3IgaHVtYW4gYmVpbmdzLiBPbiBtb25kYXkgbWVldGluZywKcGxlYXNlIHRhbGsgYWJvdXQgYEFSUDJgIGFuZCBub3QgYEVOU01VU0cwMDAwMDAyMDE1MmAuIEhvd2V2ZXIsIHdoZW4gbWFjaGluZXMKYXJlIHdvcmtpbmcsIHBsZWFzZSBnaXZlbiB0aGVtIGBFbnNlbWJsSURgIG9yIGBFbnRyZXpJRGAuCgpMZXQncyB0cmFuc2xhdGUgYWxsIGdlbmUgaWRlbnRpZmllcnMgYW5kIGV2YWx1YXRlIHBvc3NpYmxlIG51bWJlciBvZiBlcnJvcnMKaW4gZXNjYXBlIGFuYWx5c2lzLgoKRmlyc3QsIGxldCdzIGxvYWQgdGhlIERFQSByZXN1bHRzIGJhc2VkIG9uIFdpbGNveG9uIGFuYWx5c2lzOgoKYGBge3IgZ2V0X2RlX2dlbmVfbmFtZXMsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQojIFdlIGxvYWQgb3VyIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzIHJlc3VsdAojIHVzaW5nIHRoZSBmdW5jdGlvbiBgcmVhZFJEU2AgZnJvbSBgYmFzZWAgcGFja2FnZQpzb2JqX3dpbGNveCA8LSBiYXNlOjpyZWFkUkRTKGZpbGUgPSAic29ial93aWxjb3guUkRTIikKIyBDb3B5IGdlbmUgbmFtZXMgaW4gYSBjb2x1bW4gaXQgbWFrZXMgdGhlIGZ1dHVyZSBvcGVyYXRpb24gZWFzaWVyCnNvYmpfd2lsY294JFNZTUJPTCA8LSBiYXNlOjpyb3duYW1lcyhzb2JqX3dpbGNveCkKYGBgCgpUaGVuLCB3ZSB1c2UgdGhlIGZ1bmN0aW9uIFtgYml0cmBdKGh0dHBzOi8vcmRyci5pby9iaW9jL2NsdXN0ZXJQcm9maWxlci9tYW4vYml0ci5odG1sKQooc3RhbmRpbmcgZm9yIF9iaW9sb2dpY2FsIHRyYW5zbGF0b3JfKSBmcm9tIHRoZSBwYWNrYWdlIFtgY2x1c3RlclByb2ZpbGVyYF0oaHR0cHM6Ly9iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2NsdXN0ZXJQcm9maWxlci5odG1sKS4KCmBgYHtyIGNwX2JpdHJfd2lsY294LCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBXZSBzdG9yZSB0aGUgcmVzdWx0IG9mIGBiaXRyYCBmdW5jdGlvbgojIGZyb20gYGNsdXN0ZXJQcm9maWxlcmAgcGFja2FnZQojIGluIGEgdmFyaWFibGUgY2FsbGVkIGBhbm5vdGF0aW9uYAphbm5vdGF0aW9uIDwtIGNsdXN0ZXJQcm9maWxlcjo6Yml0cigKICAjIFdlIHByb3ZpZGUgdGhlIHZhcmlhYmxlIHBvaW50aW5nIHRvIGdlbmUgbmFtZXMKICBnZW5lSUQgPSBiYXNlOjpyb3duYW1lcyhzb2JqX3dpbGNveCksCiAgIyBXZSB0ZWxsIHRoZSB0cmFuc2xhdG9yIHRvIHdoaWNoIGdlbmUgaWRlbnRpZmllcnMKICAjIHRyYW5zbGF0aW9uIG11c3QgYmUgZG9uZQogIHRvVHlwZSA9IGJhc2U6OmMoIkVOVFJFWklEIiwgIkVOU0VNQkwiKSwKICAjIFdlIHRlbGwgdGhlIHRyYW5zbGF0b3Igd2hpY2ggdHlwZSBvZiBnZW5lIG5hbWUgd2UgaGF2ZQogIGZyb21UeXBlID0gIlNZTUJPTCIsCiAgIyBXZSBwcm92aWRlIG1tdSBkYXRhYmFzZQogIE9yZ0RiID0gb3JnLk1tLmVnLmRiCikKYGBgCgpgYGB7ciBkaXNwbHlfYW5ub3RhdGlvbiwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpEVDo6ZGF0YXRhYmxlKGFubm90YXRpb24pCmBgYAoKTm93IHdlIHdvdWxkIGxpa2UgdG8gaGF2ZSB0aGVzZSBhbm5vdGF0aW9uIGFsb25nc2lkZSB3aXRoIGFkanVzdGVkIHB2YWx1ZXMKYW5kIGZvbGQgY2hhbmdlIGluZm9ybWF0aW9uLiBJbiBvcmRlciB0byBkbyBzbywgd2UgdXNlIHRoZSBmdW5jdGlvbiBbYG1lcmdlYF0oaHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9tZXJnZS5odG1sKQpmcm9tIFtgYmFzZWBdKGh0dHBzOi8vcmRyci5pby9yLykgcGFja2FnZS4gTm90IGZyb20gW2BTZXVyYXRgXShodHRwczovL3JkcnIuaW8vZ2l0aHViL2F0YWthbmVraXovU2V1cmF0My4wL21hbi9tZXJnZS5TZXVyYXQuaHRtbCkKcGFja2FnZSAhIFRoaXMgaXMgdXNlZCB0byBtZXJnZXIgU2V1cmF0IG9iamVjdHMsIGJ1dCB3ZSBoYXZlIGRhdGFmcmFtZXMgaGVyZSEKCmBgYHtyIG1lcmdlX2Fubm90YXRpb25fd2lsY294LCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBXZSBmaWx0ZXIgdGhlc2UgcmVzdWx0cyBvbiAqKkFESlVTVEVEKiogcHZhbHVlCnNvYmpfd2lsY294IDwtIHNvYmpfd2lsY294W3NvYmpfd2lsY294JHBfdmFsX2FkaiA8PSAwLjA1LCBdCiMgVXNlIHRoZSBmdW5jdGlvbiBgbWVyZ2VgIGZyb20gcGFja2FnZSBgYmFzZWAgaW4gb3JkZXIKIyB0byBhZGQgYW5ub3RhdGlvbiB0byB3aXhvY29uIERFQSByZXN1bHQuCnNvYmpfd2lsY294IDwtIGJhc2U6Om1lcmdlKAogICMgVmFyaWFibGUgcG9pbnRpbmcgdG8gdGhlIHdpbGNveG9uIHJlc3VsdHMKICB4ID0gc29ial93aWxjb3gsCiAgIyBWYXJpYWJsZSBwb2ludGluZyB0byB0aGUgYW5ub3RhdGlvbiByZXN1bHRzCiAgeSA9IGFubm90YXRpb24sCiAgIyBXZSB0ZWxsIGhvdyB0byBtZXJnZSBzb2JqX3dpbGNveAogIGJ5LnggPSAiU1lNQk9MIiwKICAjIFdlIHRlbGwgaG93IHRvIG1lcmdlIGFubm90YXRpb24KICBieS55ID0gIlNZTUJPTCIKKQpgYGAKCmBgYHtyIGRpc3BsYXlfc29ial93aWxjb3gsIGV2YWw9VFJVRSwgZWNobz1GQUxTRX0KRFQ6OmRhdGF0YWJsZShzb2JqX3dpbGNveCkKYGBgCgojIyBSZXN0cmljdGVkIHNlZCBvZiBnZW5lcwoKQXMgd2UgcGVyZm9ybSB0aGUgYW5hbHlzaXMsIHdlIGFyZSBnb2luZyB0byBwcm92aWRlIGEgbnVtZXJpYyAKdmFyaWFibGUgdG8gdGhlIEdTRUEvRW5yaWNobWVudC4KCldlIGhhdmUgdGhlIGZvbGxvd2luZyBjb2x1bW5zOgoKMS4gYHBfdmFsYDogSWdub3JlIHRoaXMgY29sdW1uLiBBbHdheXMgaWdub3JlIHJhdyBwLXZhbHVlcy4gTG9vayBhdCBjb3JyZWN0ZWQgb25lcywgYW5kIGlmIHRoZXkgYXJlIG1pc3NpbmcsIHRoZW4gY29tcHV0ZSB0aGVtLgoxLiBgYXZnX2xvZzJGQ2A6IEF2ZXJhZ2UgTG9nMihGb2xkQ2hhbmdlKS4gSWxsdXN0cmF0ZXMgaG93IG11Y2ggYSBnZW5lIGlzIGRpZmZlcmVudGlhbGx5IGV4cGVzc2VkIGJldHdlZW4gc2FtcGxlcyBpbiBlYWNoIGNvbmRpdGlvbi4KMS4gYHBjdC4xYDogUGVyY2VudCBvZiBjZWxscyB3aXRoIGdlbmUgZXhwcmVzc2lvbiBpbiBjb25kaXRpb24gb25lLCBoZXJlIGluICJHMSIgcGhhc2UuCjEuIGBwY3QuMmA6IFBlcmNlbnQgb2YgY2VsbHMgd2l0aCBnZW5lIGV4cHJlc3Npb24gaW4gY29uZGl0aW9uIHR3bywgaGVyZSBpbiAiUyIgcGhhc2UuCjEuIGBwX3ZhbF9hZGpgOiBUaGUgY29uZmlkZW5jZSB3ZSBoYXZlIGluIHRoZSByZXN1bHQuIFRoZSBjbG9zZXIgdG8gMCwgdGhlIGxlc3NlciBpcyB0aGUgcmlzayBvZiBlcnJvci4KCklzIGl0IGEgZ29vZCBpZGVhIHRvIHVzZSBgcF92YWxgID8gV2hhdCBhcmUgdGhlIGNvbnNlcXVlbmNlcyA/Cgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcjwvc3VtbWFyeT4KCk5vLiBOZXZlci4gTmV2ZXIgZXZlciB1c2UgcmF3IFAtVmFsdWUuIEl0IGlzIG5ldmVyIGEgZ29vZCBpZGVhLgoKPC9kZXRhaWxzPgo8YnIgLz4KCklzIGl0IGEgZ29vZCBpZGVhIHRvIHVzZSBgYXZnX2xvZzJGQ2AgPyBXaGF0IGFyZSB0aGUgY29uc2VxdWVuY2VzID8KCjxkZXRhaWxzPgoKPHN1bW1hcnk+QW5zd2VyPC9zdW1tYXJ5PgoKSXQgaXMgYSBnb29kIGlkZWEsIHdlIGNvdWxkIGFuc3dlciBiaW9sb2dpY2FsIHF1ZXN0aW9ucyBsaWtlIDogIkNvbnNpZGVyaW5nCmRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcywgd2hhdCBhcmUgdGhlIHBhdGh3YXlzIHdpdGggaGlnaGVzdCBleHByZXNzaW9uCmNoYW5nZSA/IgoKPC9kZXRhaWxzPgo8YnIgLz4KCklzIGl0IGEgZ29vZCBpZGVhIHRvIHVzZSBgcGN0LjFgID8gV2hhdCBhcmUgdGhlIGNvbnNlcXVlbmNlcyA/Cgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcjwvc3VtbWFyeT4KCkl0IGlzIGEgZ29vZCBpZGVhLCB3ZSBjb3VsZCBhbnN3ZXIgYmlvbG9naWNhbCBxdWVzdGlvbnMgbGlrZSA6ICJDb25zaWRlcmluZwpkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMsIHdoYXQgYXJlIHRoZSBleHByZXNzaW9uIG9mIGdlbmVzIGluIHBhdGh3YXkKWFhYIGluIHRoZSBmaXJzdCBjb25kaXRpb24gPyIKCjwvZGV0YWlscz4KPGJyIC8+CgpJcyBpdCBhIGdvb2QgaWRlYSB0byB1c2UgYHBjdC4yYCA/IFdoYXQgYXJlIHRoZSBjb25zZXF1ZW5jZXMgPwoKPGRldGFpbHM+Cgo8c3VtbWFyeT5BbnN3ZXI8L3N1bW1hcnk+CgpJdCBpcyBhIGdvb2QgaWRlYSwgd2UgY291bGQgYW5zd2VyIGJpb2xvZ2ljYWwgcXVlc3Rpb25zIGxpa2UgOiAiQ29uc2lkZXJpbmcKZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLCB3aGF0IGFyZSB0aGUgZXhwcmVzc2lvbiBvZiBnZW5lcyBpbiBwYXRod2F5ClhYWCBpbiB0aGUgc2Vjb25kIGNvbmRpdGlvbiA/IgoKPC9kZXRhaWxzPgo8YnIgLz4KCklzIGl0IGEgZ29vZCBpZGVhIHRvIHVzZSBgcF92YWxfYWRqYCA/IFdoYXQgYXJlIHRoZSBjb25zZXF1ZW5jZXMgPwoKPGRldGFpbHM+Cgo8c3VtbWFyeT5BbnN3ZXI8L3N1bW1hcnk+CgpJdCBpcyBhIGdvb2QgaWRlYSwgd2UgY291bGQgYW5zd2VyIGJpb2xvZ2ljYWwgcXVlc3Rpb25zIGxpa2UgOiAiV2hpY2ggcGF0aHdheXMKYXJlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCB3aXRoIGhpZ2hlc3QgY29uZmlkZW5jZSBpbnRlcnZhbCA/IgoKQnV0IGluIG9yZGVyIHRvIHBlcmZvcm0gc3VyY2ggdGVzdCwgdXNlIGAtbG9nMTAoQWRqdXN0ZWQgUC1WYWx1ZSlgIGluc3RlYWQgb2YKdGhlIHJhdyBhZGp1c3RlZCBwLXZhbHVlLiBSZW1lbWJlciwgMCBpcyBhIGdvb2QgY29uZmlkZW5jZSBpbnRlcnZhbCwgYW5kIDEgYQpiYWQgb25lLiBTbyB3ZSBuZWVkIHRoZSB2YWx1ZXMgY2xvc2UgdG8gMCB0byBiZSBoaWdobHkgcG9zaXRpdmUuCgo8L2RldGFpbHM+CjxiciAvPgoKIyMgRW5yaWNobWVudCB2cyBHU0VBCgpQcmV2aW91c2x5LCB3ZSB1c2VkIGEgZnVuY3Rpb24gY2FsbGVkIGBlbnJpY2hJdGAuIFdlIHRhbGtlZCBhYm91dCBlbnJpY2htZW50CmFuYWx5c2lzLCB5ZXQgdGhpcyB0YWxrIGlzIGNhbGxlZCBHU0VBLgoKVGhpcyBpcyBkdWUgdG8gdGhlIGZhY3QgdGhhdCBib3RoIHRlY2huaXF1ZXMgZXhpc3RzIGFuZCBhcmUgZGlmZmVyZW50LgoKRW5yaWNobWVudCB0ZXN0cyBhIGxpc3Qgb2YgZ2VuZXMuIFRoZWlyIGV4cHJlc3Npb24sIGNvbmZpZGVuY2UKaW50ZXJ2YWwsIGV0Yy4gTm90aGluZyBlbHNlIG1hdHRlcnMgdGhhbiB0aGVpciBuYW1lLgoKRm9yIGVhY2ggY2VsbCwgYGVucmljaEl0YCByZW1vdmVzIHplcm9zIGFuZCB0ZXN0cyB0aGUgbGlzdCBvZiByZW1haW5pbmcgZ2VuZXMuClRoZWlyIGV4cHJlc3Npb24gZG9lcyBub3QgbWF0dGVyLiBUaGVpciBvcmRlciBpbiB0aGUgZ2VuZSBsaXN0IGRvZXMgbm90IG1hdHRlci4KVGhlaXIgaW1wYWN0IG9uIHRoZSBwYXRod2F5IGRvZXMgbm90IG1hdHRlci4gVGhlaXIgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24Kc3RhdHVzIGRvZXMgbm90IG1hdHRlci4KCkJlaGluZCB0aGUgc2NlbmVzLCBpdCdzIGEgdmVyeSBzaW1wbGUgdGVzdHMgYW5zd2VyaW5nIHRoZSBmb2xsb3dpbmcgcXVlc3Rpb246IAoiQW1vbmcgZXhwcmVzc2VkIGdlbmVzLCB3aGF0IGFyZSB0aGUgb2RkcyB0aGF0IGl0IGJlbG9uZ3MgdG8gdGhlIHBhdGh3YXkgWFhYPyIKCldlIGNhbiBkbyBlbnJpY2htZW50IHRlc3RzIG9uIG91ciB3aWx4b2MgcmVzdWx0cywgdXNpbmcgdGhlIGZ1bmN0aW9uIFtgZW5yaWNoR09gXShodHRwczovL3JkcnIuaW8vYmlvYy9jbHVzdGVyUHJvZmlsZXIvbWFuL2VucmljaEdPLmh0bWwpCmZyb20gdGhlIHBhY2thZ2UgW2BjbHVzdGVyUHJvZmlsZXJgXShodHRwczovL3JkcnIuaW8vYmlvYy9jbHVzdGVyUHJvZmlsZXIvKToKCmBgYHtyIGNsdXN0ZXJwcm9maWxlcl9lbnJpY2htZW50LCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBXZSBzdG9yZSB0aGUgcmVzdWx0IG9mIHRoZSBmdW5jdGlvbiBgZW5yaWNoR09gIGZyb20gcGFja2FnZSBgY2x1c3RlclByb2ZpbGVyYAojIGluIHRoZSBmdW5jdGlvbiBgZW5yaWNoX3dpbGNveGAKZW5yaWNoX3dpbGNveCA8LSBjbHVzdGVyUHJvZmlsZXI6OmVucmljaEdPKAogICMgV2UgcHJvdmlkZSB0aGUgbGlzdCBvZiBnZW5lIGlkZW50aWZpZXJzIHRvIHRlc3QKICBnZW5lID0gc29ial93aWxjb3gkRU5UUkVaSUQsCiAgIyBXZSBwcm92aWRlIHRoZSBjb21wbGV0ZSBsaXN0IG9mIGdlbmVzIHdlIHF1YW50aWZpZWQKICB1bml2ZXJzZSA9IGFubm90YXRpb24kRU5UUkVaSUQsCiAgIyBXZSBwcm92aWRlIG1vdXNlIGFubm90YXRpb25zCiAgT3JnRGIgPSBvcmcuTW0uZWcuZGIsCiAgIyBXZSB0ZWxsIHRoZSBmdW5jdGlvbiB0aGF0IHdlIHVzZSBlbnRyZXotaWRlbnRpZmllcnMKICBrZXlUeXBlID0gIkVOVFJFWklEIiwKICAjIFdlIHNlYXJjaCByZXN1bHRzIG9uIEJpb2xvZ2ljYWwgUHJvY2VzcyIKICBvbnQgPSAiQlAiLAogICMgV2Ugd2FudCBhbGwgdGhlIHJlc3VsdHMKICBwdmFsdWVDdXRvZmYgPSAxLAogICMgV2UgYXJlIGh1bWFucywgd2Ugd2FuIGh1bWFuLXJlYWRhYmxlIGdlbmUgbmFtZXMKICByZWFkYWJsZSA9IFRSVUUKKQpgYGAKCmBgYHtyIGRpc3BsYXlfZW5yaWNoX3dpbGNveCwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpEVDo6ZGF0YXRhYmxlKGVucmljaF93aWxjb3hAcmVzdWx0KQpgYGAKCldlIGNhbiBoYXZlIGEgbG9vayBhdCBwYXRod2F5cyBpbmNsdWRpbmcgdGhlIHdvcmQgYEcyTWAsIHVzaW5nIHRoZSBmdW5jdGlvbnMKW2B3aXRoYF0oaHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS93aXRoLmh0bWwpIGFuZCBbYGdyZXBsYF0oaHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9ncmVwLmh0bWwpCmZyb20gYGJhc2VgIHBhY2thZ2UuCgpgYGB7ciBncmVwX2cybV9lbnJpY2hfY3AsIGV2YWw9VFJVRSwgZWNobz1UUlVFfQojIEdldCB0aGUgcmVzdWx0IHRhYmxlCnJlc3VsdHNfZW5yaWNoX3dpbGNveCA8LSBlbnJpY2hfd2lsY294QHJlc3VsdAoKIyBXZSBzdG9yZSB0aGUgcmVzdWx0IG9mIHRoZSBsaW5lIHNlbGVjdGlvbgojIGluIGEgdmFyaWFibGUgY2FsbGVkIGBnMm1fZW5yaWNoYApnMm1fZW5yaWNoIDwtIHJlc3VsdHNfZW5yaWNoX3dpbGNveFsKICAjIFdlIHNlbGVjdCByb3dzIGNvbnRhaW5pbmcgYSBnaXZlbiBpbmZvcm1hdGlvbgogICMgdXNpbmcgdGhlIGZ1bmN0aW9uIGB3aXRoYCBmcm9tIGBiYXNlYCBwYWNrYWdlCiAgYmFzZTo6d2l0aCgKICAgICMgV2UgcHJvdmlkZSB0aGUgdmFyaWFibGUgcG9pbnRpbmcgdG8gZW5yaWNobWVudCByZXN1bHRzCiAgICBkYXRhID0gcmVzdWx0c19lbnJpY2hfd2lsY294LAogICAgIyBXZSBwcm92aWRlIHRoZSB0ZXJtIHdlIGFyZSBzZWFyY2hpbmcgYW5kIHRoZSBjb2x1bW4gaW4gd2hpY2gKICAgICMgdGhlIHRlcm0gc2hvdWxkIGJlIHNlYXJjaGVkCiAgICBiYXNlOjpncmVwbCgiRzJNIiwgRGVzY3JpcHRpb24pCiAgKSwgIyBEbyBub3QgZm9yZ2V0IGhpcyBjb21tYSwgc2luY2Ugd2UgYXJlIGZpbHRlcmluZyBhIGRhdGFmcmFtZSBvbiByb3dzCl0KYGBgCgpgYGB7ciBkaXNwbGF5X2VucmljaF9HMk1fd2lsY294LCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CkRUOjpkYXRhdGFibGUoZzJtX2VucmljaCkKYGBgCgpHU0VBIHRlc3RzIHRoZSBnZW5lIG5hbWVzLCBhbmQgdXNlcyB0aGUgbnVtZXJpYyB2YWx1ZSBhc3NvY2lhdGVkIHdpdGgKdGhhdCBnZW5lIGluIG9yZGVyIHRvIHdlaWdodCB0aGUgcmVzdWx0cy4gSXQgdGVzdHMgdGhlIG5hbWUsIHRoZSByYW5rLAphbmQgdGhlIG51bWVyaWMgdmFsdWUuCgpGaXJzdCwgd2UgbmVlZCB0byBjcmVhdGUgYSBuYW1lZCBsaXN0IG9mIGdlbmUgd2VpZ2h0cy4gRm9yIHRoaXMgZXhhbXBsZSwKd2UgdXNlIHRoZSBhdmVyYWdlIGZvbGQgY2hhbmdlIGFzIHdlaWdodHMuCgpgYGB7ciBnc2VhZ29fd2lsY294X3ByZXBhcmVfZ2VuZV9saXN0LCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBXZSBleHRyYWN0IGdlbmUgYXZlcmFnZSBmb2xkIGNoYW5nZQpnZW5lX2xpc3QgPC0gc29ial93aWxjb3gkYXZnX2xvZzJGQwojIFdlIGV4dHJhY3QgZ2VuZXMgRW50cmV6IElECmJhc2U6Om5hbWVzKGdlbmVfbGlzdCkgPC0gc29ial93aWxjb3gkRU5UUkVaSUQKIyBXZSBzb3J0IHRoYXQgbGlzdCBkZWFjcmVhc2luZ2x5CmdlbmVfbGlzdCA8LSBiYXNlOjpzb3J0KGdlbmVfbGlzdCwgZGVjcmVhc2luZz1UUlVFKQoKIyBDaGVjayB0aGUgcmVzdWx0cyB3aXRoIHRoZSBmdW5jdGlvbiBgaGVhZGAgZnJvbSBgdXRpbHNgIHBhY2thZ2UKdXRpbHM6OmhlYWQoZ2VuZV9saXN0KQpgYGAKCkFuZCBub3csIHdlIGNhbiBydW4gdGhlIFtgZ3NlR09gXShodHRwczovL3JkcnIuaW8vYmlvYy9jbHVzdGVyUHJvZmlsZXIvbWFuL2dzZUdPLmh0bWwpCmZyb20gW2BjbHVzdGVyUHJvZmlsZXJgXShodHRwczovL3JkcnIuaW8vYmlvYy9jbHVzdGVyUHJvZmlsZXIvKS4gWW91J2xsIHNlZSwKdGhlIGludGVyZmFjY2UgaXMgYWxtb3N0IHRoZSBzYW1lIGFzIGZvciB0aGUgZW5yaWNobWVudDoKCmBgYHtyIGdzZWFnb193aWxjb3hfcnVuLCBldmFsPVRSVUUsIGVjaG89VFJVRX0KIyBXZSB1c2UgdGhlIGZ1bmN0aW9uIGBnc2VHT2AgZnJvbSB0aGUgcGFja2FnZSBgY2x1c3RlclByb2ZpbGVyYAojIGFuZCBzdG9yZSB0aGUgcmVzdWx0IGluIGEgdmFyaWFibGUgY2FsbGVkIGBnc2VhX3dpbGNveGAKZ3NlYV93aWxjb3ggPC0gY2x1c3RlclByb2ZpbGVyOjpnc2VHTygKICAjIFdlIHByb3ZpZGUgdGhlIHZhcmlhYmxlIHBvaW50aW5nIHRvIG5hbWVkIGdlbmUgbGlzdAogIGdlbmVMaXN0ID0gZ2VuZV9saXN0LAogICMgV2UgcHJvdmlkZSBtb3VzZSBhbm5vdGF0aW9ucwogIE9yZ0RiID0gb3JnLk1tLmVnLmRiLAogICMgV2UgdGVsbCB0aGUgZnVuY3Rpb24gdGhhdCB3ZSB1c2UgZW50cmV6LWlkZW50aWZpZXJzCiAga2V5VHlwZSA9ICJFTlRSRVpJRCIsCiAgIyBXZSBzZWFyY2ggcmVzdWx0cyBvbiBCaW9sb2dpY2FsIFByb2Nlc3MiCiAgb250ID0gIkJQIiwKICAjIFdlIHdhbnQgYWxsIHRoZSByZXN1bHRzCiAgcHZhbHVlQ3V0b2ZmID0gMQopCmBgYAoKYGBge3IgZGlzcGxheV9nc2VhX3dpbGNveCwgZXZhbD1UUlVFLCBlY2hvPUZBTFNFfQpEVDo6ZGF0YXRhYmxlKGdzZWFfd2lsY294QHJlc3VsdCkKYGBgCgpKdXN0IGxpa2UgYmVmb3JlLCB3ZSBjYW4gaGF2ZSBhIGxvb2sgYXQgcGF0aHdheXMgaW5jbHVkaW5nIHRoZSB3b3JkIApgRzJNYCwgdXNpbmcgdGhlIGZ1bmN0aW9ucyBbYHdpdGhgXShodHRwczovL3JkcnIuaW8vci9iYXNlL3dpdGguaHRtbCkgCmFuZCBbYGdyZXBsYF0oaHR0cHM6Ly9yZHJyLmlvL3IvYmFzZS9ncmVwLmh0bWwpIGZyb20gYGJhc2VgIHBhY2thZ2UuCgpgYGB7ciBncmVwX2cybV9nc2Vnb19jcCwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CiMgR2V0IHRoZSByZXN1bHQgdGFibGUKcmVzdWx0c19nc2Vfd2lsY294IDwtIGVucmljaF93aWxjb3hAcmVzdWx0CgojIFdlIHN0b3JlIHRoZSByZXN1bHQgb2YgdGhlIGxpbmUgc2VsZWN0aW9uCiMgaW4gYSB2YXJpYWJsZSBjYWxsZWQgYGcybV9lbnJpY2hgCmcybV9nc2UgPC0gcmVzdWx0c19nc2Vfd2lsY294WwogICMgV2Ugc2VsZWN0IHJvd3MgY29udGFpbmluZyBhIGdpdmVuIGluZm9ybWF0aW9uCiAgIyB1c2luZyB0aGUgZnVuY3Rpb24gYHdpdGhgIGZyb20gYGJhc2VgIHBhY2thZ2UKICBiYXNlOjp3aXRoKAogICAgIyBXZSBwcm92aWRlIHRoZSB2YXJpYWJsZSBwb2ludGluZyB0byBlbnJpY2htZW50IHJlc3VsdHMKICAgIGRhdGEgPSByZXN1bHRzX2dzZV93aWxjb3gsCiAgICAjIFdlIHByb3ZpZGUgdGhlIHRlcm0gd2UgYXJlIHNlYXJjaGluZyBhbmQgdGhlIGNvbHVtbiBpbiB3aGljaAogICAgIyB0aGUgdGVybSBzaG91bGQgYmUgc2VhcmNoZWQKICAgIGJhc2U6OmdyZXBsKCJHMi9NIiwgRGVzY3JpcHRpb24pCiAgKSwgIyBEbyBub3QgZm9yZ2V0IGhpcyBjb21tYSwgc2luY2Ugd2UgYXJlIGZpbHRlcmluZyBhIGRhdGFmcmFtZSBvbiByb3dzICEKXQoKIyBDaGVjayB0aGUgcmVzdWx0cyB3aXRoIHRoZSBmdW5jdGlvbiBgaGVhZGAgZnJvbSBgdXRpbHNgIHBhY2thZ2UKdXRpbHM6OmhlYWQoZzJtX2dzZSkKYGBgCgpgYGB7ciBkaXNwbGF5X2dzZV9HMk1fd2lsY294LCBldmFsPVRSVUUsIGVjaG89RkFMU0V9CkRUOjpkYXRhdGFibGUoZzJtX2dzZSkKYGBgCgojIyBWaXN1YWxpemUgcmVzdWx0cwoKVGhpcyBtYWtlcyBhIGxvdCBvZiB0YWJsZXMuIExldCdzIG1ha2UgYSBsb3Qgb2YgZ3JhcGhzICEKCldlIGNhbiB1c2UgdGhlIGZ1bmN0aW9uIFtgYmFycGxvdGBdKGh0dHBzOi8vcmRyci5pby9iaW9jL2VucmljaHBsb3QvbWFuL2JhcnBsb3QuZW5yaWNoUmVzdWx0Lmh0bWwpCmZyb20gcGFja2FnZSBbYGdyYXBoaWNzYF0oaHR0cHM6Ly9yZHJyLmlvL2Jpb2MvZ3JhcGhpY3MvKSA7IGFuZCBub3QgdGhlIG9uZXMKZnJvbSBhbnkgb3RoZXIgcGFja2FnZSwgb3IgaXQgd29uJ3Qgd29yayAhCgpgYGB7ciBiYXJwbG90X2VnbywgZXZhbD1UUlVFLCBlY2hvPVRSVUUsIGZpZy5oZWlnaHQ9MTB9CiMgV2UgdXNlIHRoZSBmdW5jdGlvbiBgYmFycGxvdGAgZnJvbSBwYWNrYWdlIGBlbnJpY2hwbG90YApncmFwaGljczo6YmFycGxvdCgKICAjIFdlIHByb3ZpZGUgdGhlIHZhcmlhYmxlIHBvaW50aW5nIHRvIGVucmljaG1lbnQgcmVzdWx0cwogIGhlaWdodCA9IGVucmljaF93aWxjb3gsCiAgIyBXZSBkaXNwbGF5IHRoZSBiZXN0IDE1IHJlc3VsdHMKICBob3dDYXRlZ29yeT0xNQopCmBgYAoKV2UgY2FuIGFsc28gdXNlIHRoZSBmdW5jdGlvbiBbYGRpdHBsb3RgXShodHRwczovL3JkcnIuaW8vYmlvYy9lbnJpY2hwbG90L21hbi9iYXJwbG90LmVucmljaFJlc3VsdC5odG1sKQpmcm9tIHBhY2thZ2UgW2BlbnJpY2hwbG90YF0oaHR0cHM6Ly9yZHJyLmlvL2Jpb2MvZW5yaWNocGxvdC8pIDsgYW5kIG5vdCB0aGUgb25lcwpmcm9tIGFueSBvdGhlciBwYWNrYWdlLCBvciBpdCB3b24ndCB3b3JrICEgTm90ZSB0aGUgY2hhbmdlIGluIHRoZSBpbnB1dApwYXJhbWV0ZXIgbmFtZToKCmBgYHtyIGRvdHBsb3RfZWdvLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgZmlnLmhlaWdodD0xMH0KIyBXZSB1c2UgdGhlIGZ1bmN0aW9uIGBiYXJwbG90YCBmcm9tIHBhY2thZ2UgYGVucmljaHBsb3RgCmVucmljaHBsb3Q6OmRvdHBsb3QoCiAgIyBXZSBwcm92aWRlIHRoZSB2YXJpYWJsZSBwb2ludGluZyB0byBlbnJpY2htZW50IHJlc3VsdHMKICBvYmplY3QgPSBlbnJpY2hfd2lsY294LAogICMgV2UgZGlzcGxheSB0aGUgYmVzdCAxNSByZXN1bHRzCiAgc2hvd0NhdGVnb3J5PTE1CikKYGBgCgpXZSBjYW4gZGlzcGxheSB0cmFkaXRpb25hbCBHU0VBIGdyYXBoIHVzaW5nIFtgZ3NlYXBsb3QyYF0oaHR0cHM6Ly9yZHJyLmlvL2Jpb2MvZW5yaWNocGxvdC9tYW4vZ3NlYXBsb3QyLmh0bWwpCmZyb20gcGFja2FnZSBbYGVucmljaHBsb3RgXShodHRwczovL3JkcnIuaW8vYmlvYy9lbnJpY2hwbG90Lyk6CgpgYGB7ciBnc2VhcGxvdCwgZXZhbD1UUlVFLCBlY2hvPVRSVUV9CiMgV2UgdXNlIHRoZSBmdW5jdGlvbiBgYmFycGxvdGAgZnJvbSBwYWNrYWdlIGBlbnJpY2hwbG90YAplbnJpY2hwbG90Ojpnc2VhcGxvdDIoCiAgIyBXZSBwcm92aWRlIHRoZSB2YXJpYWJsZSBwb2ludGluZyB0byBlbnJpY2htZW50IHJlc3VsdHMKICB4ID0gZ3NlYV93aWxjb3gsCiAgIyBXZSBkaXNwbGF5IHRoZSBiZXN0IHJlc3VsdAogIGdlbmVTZXRJRCA9IDEKKQpgYGAKCldpdGggR1NFQSwgeW91IGRvdCBub3QgdGVzdCBpZiBhIHBhdGh3YXkgaXMgdXAgb3IgZG93biByZWd1bGF0ZWQuCkEgcGF0aHdheSBjb250YWlucyBib3RoIGVuaGFuY2VycyBhbmQgc3VwcHJlc3NvcnMgZ2VuZXMuIEFuCnVwLXJlZ3VsYXRpb24gb2YgZW5oYW5jZXIgZ2VuZXMgYW5kIGEgZG93bi1yZWd1bGF0aW9uIG9mIHN1cHByZXNzb3IgZ2VuZXMKd2lsbCBsZWFkIHRvIGEg4oCcYmFk4oCdIGVucmljaG1lbnQgc2NvcmUuIEhvd2V2ZXIsIHRoaXMgd2lsbCBsZWFkIHRvIGEgc3Ryb25nCmNoYW5nZSBpbiB5b3VyIHBhdGh3YXkgYWN0aXZpdHkhCgpJZiB5b3VyIGZhdm9yaXRlIHBhdGh3YXkgZG9lcyBub3QgaGF2ZSBhIOKAnGdvb2QgZW5yaWNobWVudCBzY29yZeKAnSwgaXQgZG9lcwpub3QgbWVhbiB0aGF0IHBhdGh3YXkgaXMgbm90IGFmZmVjdGVkLgoKVGhlIFtgaGVhdHBsb3RgXShodHRwczovL3JkcnIuaW8vYmlvYy9lbnJpY2hwbG90L21hbi9oZWF0cGxvdC5odG1sKSBkaXNwbGF5cwpib3RoIGEgaGVhdG1hcCBvZiBlbnJpY2hlZCBwYXRod2F5cyAqYW5kKiB0aGVpciBnZW5lcyBpbiBjb21tb25zOgoKYGBge3IgaGVhdHBsb3RfZ3NlYSwgZWNobz1UUlVFLCBldmFsPVRSVUUsIGZpZy5oZWlnaHQ9MTB9CiMgV2UgdXNlIHRoZSBmdW5jdGlvbiBgaGVhdHBsb3RgIGZyb20gYGVucmljaHBsb3RgIHBhY2thZ2UKZW5yaWNocGxvdDo6aGVhdHBsb3QoCiAgIyBXZSBwcm9iaWRlIHRoZSB2YXJpYWJsZSBwb2ludGluZyB0byBHU0VBIHJlc3VsdHMKICB4ID0gZ3NlYV93aWxjb3gsCiAgIyBXZSBzaG93IHRoZSAxNSBiZXN0IHJlc3VsdHMKICBzaG93Q2F0ZWdvcnkgPSAxNSwKICAjIFdlIGNvbG9yIGFjY29yZGluZyB0byBGb2xkQ2hhbmdlCiAgZm9sZENoYW5nZSA9IGdlbmVfbGlzdAopCmBgYAoKVGhlIGdlbmVzIGluIGNvbW1vbiBiZXR3ZWVuIG11bHRpcGxlIGdlbmUgc2V0cyBhcmUgYWxzbyB2aXNpYmxlIHRocm91Z2ggYW4KdXNwZXQgcGxvdDoKCmBgYHtyIHVwc2V0X2dzZWEsIGVjaG89VFJVRSwgZXZhbD1UUlVFLCBmaWcuaGVpZ2h0PTEwfQojIFdlIHVzZSB0aGUgZnVuY3Rpb24gYHVwc2V0cGxvdGAgZnJvbSBgZW5yaWNocGxvdGAgcGFja2FnZQplbnJpY2hwbG90Ojp1cHNldHBsb3QoCiAgIyBXZSBwcm9iaWRlIHRoZSB2YXJpYWJsZSBwb2ludGluZyB0byBHU0VBIHJlc3VsdHMKICB4ID0gZ3NlYV93aWxjb3gsCiAgIyBXZSBzaG93IHRoZSAxMCBiZXN0IHJlc3VsdHMKICBuID0gMTAKKQpgYGAKCkZpbmFsbHksIHdlIGNhbiBkaXNwbGF5IHdob2xlIHBhdGh3YXlzIHVzaW5nIEtFR0cgZGF0YWJhc2U6CgpgYGB7ciBwYXRodmlld19nZW5lX2xpc3QsIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KIyBXZSB1c2UgdGhlIGZ1bmN0aW9uIHBhdGh2aWV3IGZyb20gcGF0aHZpZXcgcGFja2FnZQpwYXRodmlldzo6cGF0aHZpZXcoCiAgZ2VuZS5kYXRhID0gZ2VuZV9saXN0LCAjIE91ciBnZW5lIGxpc3QKICBwYXRod2F5LmlkID0gIm1tdTA0MTEwIiwgIyBPdXIgcGF0aHdheQogIHNwZWNpZXMgPSAibW11IiwgIyBPdXIgb3JnYW5pc20KICAjIFRoZSBjb2xvciBsaW1pdHMKICBsaW1pdCA9IGxpc3QoZ2VuZT1tYXgoYWJzKGdlbmVfbGlzdCkpKSwKICBnZW5lLmlkdHlwZSA9ICJFTlRSRVpJRCIgIyBUaGUgZ2VuZXMgaWRlbnRpZmllcnMKKQpgYGAKCiFbcGF0aHZpZXdfcmVzdWx0c10obW11MDQxMTAucGF0aHZpZXcucG5nKQoKIyMgU2Vzc2lvbiBJbmZvCgpUaGlzIGxpc3Qgb2YgYWxsIHBhY2thZ2VzIHVzZWQgd2hpbGUgeW91IHdvcmsgc2hvdWxkIGJlIGluY2x1ZGVkCmluIGVhY2ggZW4gZXZlcnkgUiBwcmVzZW50YXRpb246CgpgYGB7ciBzZXNzaW9uX2luZm8sIGV2YWw9VFJVRSwgZWNobz1UUlVFfQp1dGlsczo6c2Vzc2lvbkluZm8oKQpgYGA=