Original Authors: Belinda Phipson, Anna Trigos, Matt Ritchie, Maria Doyle, Harriet Dashnow, Charity Law Based on the course RNAseq analysis in R delivered on May 11/12th 2016

Before starting this section, we will make sure we have all the relevant objects from the Differential Expression analysis present.

load("Robjects/DE.Rdata")

Overview

Main Materials

Create a table of differential expression results

We have a list of significantly differentially expressed genes, but the only annotation we can see is the Entrez Gene ID, which is not very informative.

results <- as.data.frame(topTags(lrt_BvsL, n = Inf))
head(results)

Visualise results with a ‘Smear plot’

edgeR provides a function plotSmear that allows us to visualise the results of a DE analysis. This plot shows the log-fold change against log-counts per million, with DE genes highlighted:

# identify differentially expressed genes
de <- decideTestsDGE(lrt_BvsL)
summary(de)
       CellTypeluminal
Down              2957
NotSig           11232
Up                1615
# create a vector of differentially expressed genes
detags <- rownames(dgeObj)[as.logical(de)]
# plot smear highlighting DE genes
plotSmear(lrt_BvsL, de.tags = detags)

However, on such a plot it would be nice to add labels to highlight the genes with most evidence for being DE, or our favourite genes. To perform such a task we need to map between the identifiers we have in the edgeR output and more familiar names.

Finally, we will look at sophisticated visualisations that allow us to incorporate information about the structure of a gene, level of sequencing coverage.

Adding annotation to the edgeR results

There are a number of ways to add annotation, but we will demonstrate how to do this using the org.Mm.eg.db package. This package is one of several organism-level packages which are re-built every 6 months. These packages are listed on the annotation section of the Bioconductor, and are installed in the same way as regular Bioconductor packages. An alternative approach is to use biomaRt, an interface to the BioMart resource. BioMart is much more comprehensive, but the organism packages fit better into the Bioconductor workflow.

# source("http://www.bioconductor.org/biocLite.R")
# # for mouse
# biocLite("org.Mm.eg.db")
# #For Human
# biocLite("org.Hs.eg.db")

The packages are larger in size that Bioconductor software pacakges, but essentially they are databases that can be used to make offline queries.

library(org.Mm.eg.db)

First we need to decide what information we want. In order to see what we can extract we can run the columns function on the annotation database.

columns(org.Mm.eg.db)
 [1] "ACCNUM"       "ALIAS"        "ENSEMBL"      "ENSEMBLPROT"  "ENSEMBLTRANS" "ENTREZID"     "ENZYME"       "EVIDENCE"    
 [9] "EVIDENCEALL"  "GENENAME"     "GO"           "GOALL"        "IPI"          "MGI"          "ONTOLOGY"     "ONTOLOGYALL" 
[17] "PATH"         "PFAM"         "PMID"         "PROSITE"      "REFSEQ"       "SYMBOL"       "UNIGENE"      "UNIPROT"     

We are going to filter the database by a key or set of keys in order to extract the information we want. Valid names for the key can be retrieved with the keytypes function.

keytypes(org.Mm.eg.db)
 [1] "ACCNUM"       "ALIAS"        "ENSEMBL"      "ENSEMBLPROT"  "ENSEMBLTRANS" "ENTREZID"     "ENZYME"       "EVIDENCE"    
 [9] "EVIDENCEALL"  "GENENAME"     "GO"           "GOALL"        "IPI"          "MGI"          "ONTOLOGY"     "ONTOLOGYALL" 
[17] "PATH"         "PFAM"         "PMID"         "PROSITE"      "REFSEQ"       "SYMBOL"       "UNIGENE"      "UNIPROT"     

We should see ENTREZID, which is the type of key we are going to use in this case. If we are unsure what values are acceptable for the key, we can check what keys are valid with keys

keys(org.Mm.eg.db, keytype="ENTREZID")[1:10]
 [1] "11287" "11298" "11302" "11303" "11304" "11305" "11306" "11307" "11308" "11350"

It is a useful sanity check to make sure that the keys you want to use are all valid. We could use %in% in this case.

## Build up the query step-by-step
my.keys <- rownames(results)[1:10]
my.keys %in% keys(org.Mm.eg.db, keytype="ENTREZID")
 [1] TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE TRUE
all(my.keys %in% keys(org.Mm.eg.db, keytype="ENTREZID"))
[1] TRUE

To annotate our results, we definitely want gene symbols and perhaps the full gene name. Let’s build up our annotation information in a separate data frame using the select function.

annot <- select(org.Mm.eg.db,
              keys=rownames(results), 
              columns=c("ENTREZID","SYMBOL","GENENAME"))
'select()' returned 1:1 mapping between keys and columns
# Have a look at the annotation
head(annot)

Let’s double check that the ENTREZID column matches exactly to our results rownames.

table(annot$ENTREZID==rownames(results))

 TRUE 
15804 

We can bind in the annotation information to the results data frame. (Please note that if the select function returns a 1:many mapping then you can’t just append the annotation to the fit object.)

results.annotated <- cbind(results, annot)
head(results.annotated)

We can save the results table using the write.csv function, which writes the results out to a csv file that you can open in excel.

write.csv(results.annotated,
          file="B.PregVsLacResults.csv",
          row.names=FALSE)

A note about deciding how many genes are significant: In order to decide which genes are differentially expressed, we usually take a cut-off of 0.05 on the adjusted p-value, NOT the raw p-value. This is because we are testing more than 15000 genes, and the chances of finding differentially expressed genes is very high when you do that many tests. Hence we need to control the false discovery rate, which is the adjusted p-value column in the results table. What this means is that if 100 genes are significant at a 5% false discovery rate, we are willing to accept that 5 will be false positives. Note that the decideTests function displays significant genes at 5% FDR.

Challenge

Re-visit the plotSmear plot from above and use the text function to add labels for the names of the top 20 most DE genes

Further visualisation

Volcano plot

Another common visualisation is the volcano plot which displays a measure of significance on the y-axis and fold-change on the x-axis.

signif <- -log10(results.annotated$FDR)
plot(results.annotated$logFC, 
     signif,
     bg="black",
     pch=21,
     xlab="log2(Fold Change)")
points(results.annotated[detags,"logFC"],
       -log10(results.annotated[detags,"FDR"]),
       pch=21,
       bg="red")

Strip chart for gene expression

Before following up on the DE genes with further lab work, a recommended sanity check is to have a look at the expression levels of the individual samples for the genes of interest. We can quickly look at grouped expression using stripchart. We can use the normalised log expression values in the dgeCounts object (dgeCounts$counts).

normCounts <- cpm(dgeObj, log = T)
# Let's look at the first gene in the topTable, Krt5, which has a rowname 110308
par(mar=c(8,4,2,2)) #adjust the plot margins the x-labels are visible - see ?par
stripchart(normCounts["110308",]~sampleinfo$Group,
           xlab="",
           ylab="log2(Counts)",
           vertical=TRUE,
           las=2,
           pch=21,
           col=1:6,
           cex=2)

Interactive StripChart with Glimma

An interactive version of the volcano plot above that includes the raw per sample values in a separate panel is possible via the glXYPlot function in the Glimma package.

library(Glimma)
group <- sampleinfo$Group
levels(group) <- c("basal.lactate","basal.preg","basal.virgin","lum.lactate", "lum.preg", "lum.virgin")
annot.mod <- annot
colnames(annot.mod)[1] <- "GeneID"
de <- as.numeric(results$FDR<=0.01)
glXYPlot(x=results$logFC, y=-log10(results$FDR),
         xlab="logFC", ylab="B", main="B.PregVsLac",
         counts=normCounts, groups=group, status=de,
         anno=annot.mod, id.column="ENTREZID", folder="volcano")

This function creates an html page (./volcano/XY-Plot.html) with a volcano plot on the left and a plot showing the log-CPM per sample for a selected gene on the right. A search bar is available to search for genes of interest.

Retrieving Genomic Locations

It might seem natural to add genomic locations to our annotation table, and possibly a bit odd that the org.Mm.eg.db package does not supply such mappings. In fact, there is a whole suite of package for performing this, and more-advanced queries that relate to the location of genes. These are listed on the Bioconductor annotation page and have the prefix TxDb.

The package we will be using is TxDb.Mmusculus.UCSC.mm10.knownGene. Packages are available for other organisms and genome builds. It is even possible to build your own database if one does not exist. See vignette("GenomicFeatures") for details

# source("http://www.bioconductor.org/biocLite.R")
## For mouse
# biocLite("TxDb.Mmusculus.UCSC.mm10.knownGene")
## For Humans
# biocLite("TxDb.Hsapiens.UCSC.hg19.knownGene")

We load the library in the usual fashion and create a new object to save some typing. As with the org. packages, we can query what columns are available with columns,

library(TxDb.Mmusculus.UCSC.mm10.knownGene)
tx <- TxDb.Mmusculus.UCSC.mm10.knownGene
columns(tx)
 [1] "CDSCHROM"   "CDSEND"     "CDSID"      "CDSNAME"    "CDSSTART"   "CDSSTRAND"  "EXONCHROM"  "EXONEND"    "EXONID"    
[10] "EXONNAME"   "EXONRANK"   "EXONSTART"  "EXONSTRAND" "GENEID"     "TXCHROM"    "TXEND"      "TXID"       "TXNAME"    
[19] "TXSTART"    "TXSTRAND"   "TXTYPE"    

The select function is used in the same manner as the org.Mm.eg.db packages.

Challenge 2

Use the TxDb.Mmusculus.UCSC.mm10.knownGene package to retrieve the exon coordinates for the genes 50916, 110308, 12293

Overview of GenomicRanges

One of the real strengths of the txdb.. packages is the ability of interface with GenomicRanges, which is the object type used throughout Bioconductor to manipulate Genomic Intervals.

These object types permit us to perform common operations on intervals such as overlapping and counting. We can define the chromosome, start and end position of each region (also strand too, but not shown here).

library(GenomicRanges)
simple_range <- GRanges(seqnames = "1", ranges = IRanges(start=1000, end=2000))
simple_range
GRanges object with 1 range and 0 metadata columns:
      seqnames    ranges strand
         <Rle> <IRanges>  <Rle>
  [1]        1 1000-2000      *
  -------
  seqinfo: 1 sequence from an unspecified genome; no seqlengths

We don’t have to have all our ranges located on the same chromosome

chrs <- c("chr13", "chr15", "chr5")
start <- c(73000000, 101000000, 15000000)
end <- c(74000000, 102000000, 16000000)
my_ranges <- GRanges(seqnames = rep(chrs, 3),
                     ranges = IRanges(start = rep(start, each = 3),
                                      end = rep(end, each = 3))
                     )
my_ranges
GRanges object with 9 ranges and 0 metadata columns:
      seqnames              ranges strand
         <Rle>           <IRanges>  <Rle>
  [1]    chr13   73000000-74000000      *
  [2]    chr15   73000000-74000000      *
  [3]     chr5   73000000-74000000      *
  [4]    chr13 101000000-102000000      *
  [5]    chr15 101000000-102000000      *
  [6]     chr5 101000000-102000000      *
  [7]    chr13   15000000-16000000      *
  [8]    chr15   15000000-16000000      *
  [9]     chr5   15000000-16000000      *
  -------
  seqinfo: 3 sequences from an unspecified genome; no seqlengths

There are a number of useful functions for calculating properties of the data (such as coverage or sorting). Not so much for RNA-seq analysis, but GenomicRanges are used throughout Bioconductor for the analysis of NGS data.

For instance, we can quickly identify overlapping regions between two GenomicRanges.

keys <- c("50916", "110308", "12293")
genePos <- select(tx,
                  keys = keys,
                  keytype = "GENEID",
                  columns = c("EXONCHROM", "EXONSTART", "EXONEND")
                  )
'select()' returned 1:many mapping between keys and columns
geneRanges <- GRanges(genePos$EXONCHROM, 
                      ranges = IRanges(genePos$EXONSTART, genePos$EXONEND), 
                      GENEID = genePos$GENEID)
geneRanges
GRanges object with 58 ranges and 1 metadata column:
       seqnames            ranges strand |      GENEID
          <Rle>         <IRanges>  <Rle> | <character>
   [1]    chr13 73260497-73260876      * |       50916
   [2]    chr13 73264848-73264979      * |       50916
   [3]    chr13 73265458-73265709      * |       50916
   [4]    chr13 73266596-73266708      * |       50916
   [5]    chr13 73267504-73267832      * |       50916
   ...      ...               ...    ... .         ...
  [54]     chr5 16370558-16374511      * |       12293
  [55]     chr5 16341990-16342010      * |       12293
  [56]     chr5 16326327-16326383      * |       12293
  [57]     chr5 16322539-16322595      * |       12293
  [58]     chr5 16267376-16268604      * |       12293
  -------
  seqinfo: 3 sequences from an unspecified genome; no seqlengths
findOverlaps(my_ranges, geneRanges)
Hits object with 16 hits and 0 metadata columns:
       queryHits subjectHits
       <integer>   <integer>
   [1]         1           1
   [2]         1           2
   [3]         1           3
   [4]         1           4
   [5]         1           5
   ...       ...         ...
  [12]         5          12
  [13]         5          13
  [14]         5          14
  [15]         5          15
  [16]         9          16
  -------
  queryLength: 9 / subjectLength: 58

However, we have to pay attention to the naming convention used for each object. seqlevelsStyle can help.

seqlevelsStyle(geneRanges)
[1] "UCSC"
seqlevelsStyle(my_ranges)
[1] "UCSC"
seqlevelsStyle(simple_range)
[1] "NCBI"    "Ensembl" "MSU6"    "AGPvF"  

Exporting tracks

It is also possible to save the results of a Bioconductor analysis in a browser to enable interactive analysis and integration with other data types, or sharing with collaborators. For instance, we might want a browser track to indicate where our differentially-expressed genes are located. We shall use the bed format to display these locations. We will annotate the ranges with information from our analysis such as the fold-change and significance.

First we create a data frame for just the DE genes.

sigGenes <- results.annotated[detags,]
message("Number of significantly DE genes: ", nrow(sigGenes))
Number of significantly DE genes: 4572
head(sigGenes)

Create a genomic ranges object

Several convenience functions exist to retrieve the structure of every gene from a given TxDb object in one list. The output of exonsBy is a list, where each item in the list is the exon co-ordinates of a particular gene, however, we do not need this level of granularity for the bed output, so we will collapse to a single region for each gene.

First we use the range function to obtain a single range for every gene and tranform to a more convenient object with unlist.

exo <- exonsBy(tx, "gene")
exoRanges <- unlist(range(exo))
sigRegions <- exoRanges[na.omit(match(sigGenes$ENTREZID, names(exoRanges)))]
sigRegions
GRanges object with 4393 ranges and 0 metadata columns:
         seqnames              ranges strand
            <Rle>           <IRanges>  <Rle>
  497097     chr1     3214482-3671498      -
   20671     chr1     4490928-4497354      -
   58175     chr1     4909576-5070285      -
   76187     chr1     9548046-9577968      +
   72481     chr1     9560833-9631092      -
     ...      ...                 ...    ...
  195727     chrX 161836430-162159441      -
  108012     chrX 163909017-163933666      +
   56078     chrX 163976822-164028010      -
   54156     chrX 166523007-166585716      -
  333605     chrX 167471306-168577233      -
  -------
  seqinfo: 66 sequences (1 circular) from mm10 genome

For visualisation purposes, we are going to restrict the data to genes that are located on chromosomes 1 to 19 and the sex chromosomes. This can be done with the keepSeqLevels function.

seqlevels(sigRegions)
 [1] "chr1"                 "chr2"                 "chr3"                 "chr4"                 "chr5"                
 [6] "chr6"                 "chr7"                 "chr8"                 "chr9"                 "chr10"               
[11] "chr11"                "chr12"                "chr13"                "chr14"                "chr15"               
[16] "chr16"                "chr17"                "chr18"                "chr19"                "chrX"                
[21] "chrY"                 "chrM"                 "chr1_GL456210_random" "chr1_GL456211_random" "chr1_GL456212_random"
[26] "chr1_GL456213_random" "chr1_GL456221_random" "chr4_GL456216_random" "chr4_GL456350_random" "chr4_JH584292_random"
[31] "chr4_JH584293_random" "chr4_JH584294_random" "chr4_JH584295_random" "chr5_GL456354_random" "chr5_JH584296_random"
[36] "chr5_JH584297_random" "chr5_JH584298_random" "chr5_JH584299_random" "chr7_GL456219_random" "chrX_GL456233_random"
[41] "chrY_JH584300_random" "chrY_JH584301_random" "chrY_JH584302_random" "chrY_JH584303_random" "chrUn_GL456239"      
[46] "chrUn_GL456359"       "chrUn_GL456360"       "chrUn_GL456366"       "chrUn_GL456367"       "chrUn_GL456368"      
[51] "chrUn_GL456370"       "chrUn_GL456372"       "chrUn_GL456378"       "chrUn_GL456379"       "chrUn_GL456381"      
[56] "chrUn_GL456382"       "chrUn_GL456383"       "chrUn_GL456385"       "chrUn_GL456387"       "chrUn_GL456389"      
[61] "chrUn_GL456390"       "chrUn_GL456392"       "chrUn_GL456393"       "chrUn_GL456394"       "chrUn_GL456396"      
[66] "chrUn_JH584304"      
sigRegions <- keepSeqlevels(sigRegions, 
                            value = paste0("chr", c(1:19,"X","Y")),
                            pruning.mode="tidy")
seqlevels(sigRegions)
 [1] "chr1"  "chr2"  "chr3"  "chr4"  "chr5"  "chr6"  "chr7"  "chr8"  "chr9"  "chr10" "chr11" "chr12" "chr13" "chr14" "chr15"
[16] "chr16" "chr17" "chr18" "chr19" "chrX"  "chrY" 

Add metadata to GRanges object

A useful propery of GenomicRanges is that we can attach metadata to each range using the mcols function. The metadata can be supplied in the form of a data frame.

mcols(sigRegions) <- sigGenes[match(names(sigRegions), rownames(sigGenes)), ]
sigRegions
GRanges object with 4392 ranges and 8 metadata columns:
         seqnames              ranges strand |             logFC             logCPM               LR               PValue
            <Rle>           <IRanges>  <Rle> |         <numeric>          <numeric>        <numeric>            <numeric>
  497097     chr1     3214482-3671498      - | -10.9472399580736    2.5236514829963  23.590694348389 1.19162399345158e-06
   20671     chr1     4490928-4497354      - | -2.67313117621058   1.24186399534385 10.5872405573132  0.00113870810229744
   58175     chr1     4909576-5070285      - |  4.47143440821072   1.12401146729236 14.3734409590833 0.000149901775017694
   76187     chr1     9548046-9577968      + |   3.0330034607647   2.40710126232655 8.47563041277367  0.00359935626571388
   72481     chr1     9560833-9631092      - |  2.13661774722519 -0.247275168961162 6.13758113260831   0.0132338222037952
     ...      ...                 ...    ... .               ...                ...              ...                  ...
  195727     chrX 161836430-162159441      - | -4.69261763279776   3.10777653736015 13.9121195008167 0.000191559297917662
  108012     chrX 163909017-163933666      + | -2.17699584124451   2.32098302864258 10.2017044061552  0.00140310912515326
   56078     chrX 163976822-164028010      - |   2.5883614796138    5.1331378732911 6.92309295315483  0.00850896813252038
   54156     chrX 166523007-166585716      - | -6.96434996075043   4.39157591753061 19.4442035074456 1.03581719496295e-05
  333605     chrX 167471306-168577233      - | -3.89607089373906  0.656193636646942 6.91789090661196  0.00853375663282725
                          FDR    ENTREZID        SYMBOL                                           GENENAME
                    <numeric> <character>   <character>                                        <character>
  497097 0.000437796062030272      497097          Xkr4                  X-linked Kx blood group related 4
   20671  0.00785535505018016       20671         Sox17              SRY (sex determining region Y)-box 17
   58175  0.00201964846750182       58175         Rgs20                regulator of G-protein signaling 20
   76187   0.0182496716148034       76187        Adhfe1          alcohol dehydrogenase, iron containing, 1
   72481   0.0468231044850456       72481 2610203C22Rik                         RIKEN cDNA 2610203C22 gene
     ...                  ...         ...           ...                                                ...
  195727  0.00234386623585872      195727           Nhs                     NHS actin remodeling regulator
  108012  0.00917069338871881      108012         Ap1s2 adaptor-related protein complex 1, sigma 2 subunit
   56078    0.033890053519746       56078         Car5b               carbonic anhydrase 5b, mitochondrial
   54156 0.000520513371686814       54156         Egfl6                        EGF-like-domain, multiple 6
  333605   0.0339270997902079      333605        Frmpd4                   FERM and PDZ domain containing 4
  -------
  seqinfo: 21 sequences from mm10 genome

Scores and colour on exported tracks

The .bed file format is commonly used to store genomic locations for display in genome browsers (e.g. the UCSC browser or IGV) as tracks. Rather than just representing the genomic locations, the .bed format is also able to colour each range according to some property of the analysis (e.g. direction and magnitude of change) to help highlight particular regions of interest. A score can also be displayed when a particular region is clicked-on.

For the score we can use the \(-log_{10}\) of the adjusted p-value and colour scheme for the regions based on the fold-change

colorRampPalette is a useful function in base R for constructing a palette between two extremes. When choosing colour palettes, make sure they are colour blind friendly. The red / green colour scheme traditionally-applied to microarrays is a bad choice.

We will also truncate the fold-changes to between -5 and 5 to and divide this range into 10 equal bins

rbPal <- colorRampPalette(c("red", "blue"))
logfc <- pmax(sigRegions$logFC, -5)
logfc <- pmin(logfc , 5)
Cols <- rbPal(10)[as.numeric(cut(logfc, breaks = 10))]

The colours and score have to be saved in the GRanges object as score and itemRgb columns respectively, and will be used to construct the browser track. The rtracklayer package can be used to import and export browsers tracks.

Now we can export the signifcant results from the DE analysis as a .bed track using rtracklayer. You can load the resulting file in IGV, if you wish.

mcols(sigRegions)$score <- -log10(sigRegions$FDR)
mcols(sigRegions)$itemRgb <- Cols
sigRegions
GRanges object with 4392 ranges and 10 metadata columns:
         seqnames              ranges strand |             logFC             logCPM               LR               PValue
            <Rle>           <IRanges>  <Rle> |         <numeric>          <numeric>        <numeric>            <numeric>
  497097     chr1     3214482-3671498      - | -10.9472399580736    2.5236514829963  23.590694348389 1.19162399345158e-06
   20671     chr1     4490928-4497354      - | -2.67313117621058   1.24186399534385 10.5872405573132  0.00113870810229744
   58175     chr1     4909576-5070285      - |  4.47143440821072   1.12401146729236 14.3734409590833 0.000149901775017694
   76187     chr1     9548046-9577968      + |   3.0330034607647   2.40710126232655 8.47563041277367  0.00359935626571388
   72481     chr1     9560833-9631092      - |  2.13661774722519 -0.247275168961162 6.13758113260831   0.0132338222037952
     ...      ...                 ...    ... .               ...                ...              ...                  ...
  195727     chrX 161836430-162159441      - | -4.69261763279776   3.10777653736015 13.9121195008167 0.000191559297917662
  108012     chrX 163909017-163933666      + | -2.17699584124451   2.32098302864258 10.2017044061552  0.00140310912515326
   56078     chrX 163976822-164028010      - |   2.5883614796138    5.1331378732911 6.92309295315483  0.00850896813252038
   54156     chrX 166523007-166585716      - | -6.96434996075043   4.39157591753061 19.4442035074456 1.03581719496295e-05
  333605     chrX 167471306-168577233      - | -3.89607089373906  0.656193636646942 6.91789090661196  0.00853375663282725
                          FDR    ENTREZID        SYMBOL                                           GENENAME            score
                    <numeric> <character>   <character>                                        <character>        <numeric>
  497097 0.000437796062030272      497097          Xkr4                  X-linked Kx blood group related 4 3.35872814922358
   20671  0.00785535505018016       20671         Sox17              SRY (sex determining region Y)-box 17 2.10483418072595
   58175  0.00201964846750182       58175         Rgs20                regulator of G-protein signaling 20 2.69472421565727
   76187   0.0182496716148034       76187        Adhfe1          alcohol dehydrogenase, iron containing, 1 1.73874494584652
   72481   0.0468231044850456       72481 2610203C22Rik                         RIKEN cDNA 2610203C22 gene  1.3295397949105
     ...                  ...         ...           ...                                                ...              ...
  195727  0.00234386623585872      195727           Nhs                     NHS actin remodeling regulator 2.63006717707634
  108012  0.00917069338871881      108012         Ap1s2 adaptor-related protein complex 1, sigma 2 subunit  2.0375978264336
   56078    0.033890053519746       56078         Car5b               carbonic anhydrase 5b, mitochondrial 1.46992774531689
   54156 0.000520513371686814       54156         Egfl6                        EGF-like-domain, multiple 6 3.28356810923663
  333605   0.0339270997902079      333605        Frmpd4                   FERM and PDZ domain containing 4 1.46945326381326
             itemRgb
         <character>
  497097     #FF0000
   20671     #C60038
   58175     #0000FF
   76187     #1C00E2
   72481     #3800C6
     ...         ...
  195727     #FF0000
  108012     #C60038
   56078     #3800C6
   54156     #FF0000
  333605     #E2001C
  -------
  seqinfo: 21 sequences from mm10 genome
library(rtracklayer)
export(sigRegions , con = "topHits.bed")

Additional Materials

Extracting Reads

As we have been using counts as our starting point, we haven’t investigated the aligned reads from our experiment, and how they are represented. As you may be aware, aligned reads are usually stored in a bam file that can be manipulated with open-source command-line tools such as samtools and picard. Bioconductor provide a low-level interface to data/bam/sam files in the form of the Rsamtools package. The GenomicAlignments package can also be used to retrieve the reads mapping to a particular genomic region in an efficient manner.

library(GenomicAlignments)

In the directory bams_small there should be .bam files for some of the samples in the example study. The workflow to produce these files is described in a supplmentary page for the course. In brief, the raw reads (fastq) were downloaded from the Short Read Archive (SRA) and aligned with hisat2. Each bam file was named according to the file name in SRA, but we have renamed the files according to their name in the study. An index file (.bai) has been generated for each bam file. In order to reduce the size, the bam files used here only contain a subset of the reads that were aligned in the region chr15:101707000-101713000.

list.files("data/bams_small/")
[1] "MCL1.DG.small.bam"     "MCL1.DG.small.bam.bai" "MCL1.DH.small.bam"     "MCL1.DH.small.bam.bai" "MCL1.LA.small.bam"    
[6] "MCL1.LA.small.bam.bai" "MCL1.LB.small.bam"     "MCL1.LB.small.bam.bai"

The readGAlignments function provides a simple interface to interrogate the aligned reads for a particular sample. It can also utilise the index file in order to retrieve only the reads that correspond to a specific region in an efficient manner. The output includes the genomic location of each aligned read and the CIGAR (Compact Idiosyncratic Gapped Alignment Report); where M denotes an match to the genome and I, D correspond to insertions and deletions.

generegion <- exo[["110308"]]
my.reads <- readGAlignments(file="data/bams_small/MCL1.DG.small.bam",
                       param=ScanBamParam(which=generegion))
my.reads
GAlignments object with 87191 alignments and 0 metadata columns:
          seqnames strand                   cigar    qwidth     start       end     width     njunc
             <Rle>  <Rle>             <character> <integer> <integer> <integer> <integer> <integer>
      [1]    chr15      -           20M181102N80M       100 101527773 101708974    181202         1
      [2]    chr15      - 3M814N34M1...35M724N13M       100 101556357 101708888    152532         4
      [3]    chr15      -  11S10M88694N51M394N28M       100 101618968 101708144     89177         2
      [4]    chr15      - 27M263561N27M1611N45M1S       100 101627088 101892358    265271         2
      [5]    chr15      - 23M19128N2...9M64743N1M       100 101627092 101711603     84512         3
      ...      ...    ...                     ...       ...       ...       ...       ...       ...
  [87187]    chr15      +                    100M       100 101712864 101712963       100         0
  [87188]    chr15      +                   3S97M       100 101712865 101712961        97         0
  [87189]    chr15      -                    100M       100 101712876 101712975       100         0
  [87190]    chr15      -                    100M       100 101712883 101712982       100         0
  [87191]    chr15      +                   1S99M       100 101712888 101712986        99         0
  -------
  seqinfo: 66 sequences from an unspecified genome

It is possible to tweak the function to retrieve other potentially-useful information from the bam file, such as the mapping quality and flag.

my.reads <- readGAlignments(file="data/bams_small//MCL1.DG.small.bam",
                       param=ScanBamParam(which=generegion,
                                          what=c("seq","mapq","flag")))
my.reads
GAlignments object with 87191 alignments and 3 metadata columns:
          seqnames strand                   cigar    qwidth     start       end     width     njunc |                     seq
             <Rle>  <Rle>             <character> <integer> <integer> <integer> <integer> <integer> |          <DNAStringSet>
      [1]    chr15      -           20M181102N80M       100 101527773 101708974    181202         1 | CATGAGCTCC...CAGCAGCCTG
      [2]    chr15      - 3M814N34M1...35M724N13M       100 101556357 101708888    152532         4 | ATACTTGGTC...CACTCCTCGC
      [3]    chr15      -  11S10M88694N51M394N28M       100 101618968 101708144     89177         2 | CACAAACCCC...CTCCTTCCCC
      [4]    chr15      - 27M263561N27M1611N45M1S       100 101627088 101892358    265271         2 | CAGCAGGGCC...CTTGATCTGC
      [5]    chr15      - 23M19128N2...9M64743N1M       100 101627092 101711603     84512         3 | AGGGCCCACT...ATCTGCTCCC
      ...      ...    ...                     ...       ...       ...       ...       ...       ... .                     ...
  [87187]    chr15      +                    100M       100 101712864 101712963       100         0 | CGAGGTCAGC...GCAGAGGAGC
  [87188]    chr15      +                   3S97M       100 101712865 101712961        97         0 | GGTGAGGTCA...GGGCAGAGGA
  [87189]    chr15      -                    100M       100 101712876 101712975       100         0 | CGTTCAACAG...TCGAGCTGTG
  [87190]    chr15      -                    100M       100 101712883 101712982       100         0 | CAGGACGCTG...GTGAATGCTT
  [87191]    chr15      +                   1S99M       100 101712888 101712986        99         0 | GCGCTGTGGG...ATGATTAGTG
               mapq      flag
          <integer> <integer>
      [1]        60      1040
      [2]        60      1040
      [3]        60      1040
      [4]         0        16
      [5]         0       272
      ...       ...       ...
  [87187]        60         0
  [87188]        60      1024
  [87189]        60        16
  [87190]        60        16
  [87191]        60         0
  -------
  seqinfo: 66 sequences from an unspecified genome

The flag can represent useful QC information. e.g.

  • Read is unmapped
  • Read is paired / unpaired
  • Read failed QC
  • Read is a PCR duplicate (see later)

The combination of any of these properties is used to derive a numeric value, as illustrated in this useful resource

Particular attributes of the reads can be extracted and visualised

hist(mcols(my.reads)$mapq, main="", xlab="MAPQ")

However, there are more-sophisticated visualisation options for aligned reads and range data. We will use the ggbio package, which first requires some discussion of the ggplot2 plotting package.

Brief Introduction to ggplot2

The ggplot2 package has emerged as an attractive alternative to the traditional plots provided by base R. A full overview of all capabilities of the package is available from the cheatsheet.

A simple scatter plot, equivalent to plotSmear from before, can be generated as follows:-

library(ggplot2)
ggplot(results, aes(x = logCPM, y=logFC)) + geom_point() 

In brief:-

  • results is our data frame containing the variables we wish to plot
  • aes creates a mpping between the variables in our data frame to the aesthetic proprties of the plot
    • the x-axis is mapped to logCPM, y-axis is mapped to logFC
  • geom_point specifies the particular type of plot we want (in this case a scatter plot)

The real advantage of ggplot2 is the ability to change the appearance of our plot by mapping other variables to aspects of the plot. For example, we could colour the points based on a p-value cut-off. The colours are automatically chosen by ggplot2, but we can specifiy particular values.

ggplot(results, aes(x = logCPM, y=logFC, fill=FDR < 0.05)) + 
    geom_point(pch=21)

ggplot(results, aes(x = logCPM, y=logFC, fill=FDR < 0.05)) + 
    geom_point(alpha=0.4, pch=21) + 
    scale_fill_manual(values=c("yellow","red"))

The volcano plot can be constructed in a similar manner

ggplot(results, aes(x = logFC, y=-log10(FDR))) + 
    geom_point(aes(fill=FDR < 0.05), pch=21, size=2)

Composing plots with ggbio

We will now take a brief look at one of the visualisation packages in Bioconductor that takes advantage of the GenomicRanges and GenomicFeatures object-types. In this section we will show a worked example of how to combine several types of genomic data on the same plot. The documentation for ggbio is very extensive and contains lots of examples.

http://www.tengfei.name/ggbio/docs/

The Gviz package is another Bioconductor package that specialising in genomic visualisations, but we will not explore this package in the course.

The Manhattan plot is a common way of visualising genome-wide results, especially when one is concerned with the results of a GWAS study and identifying strongly-associated hits.

The profile is supposed to resemble the Manhattan skyline with particular skyscrapers towering about the lower level buildings.

This type of plot is implemented as the plotGrandLinear function. We have to supply a value to display on the y-axis using the aes function, which is inherited from ggplot2. The positioning of points on the x-axis is handled automatically by ggbio, using the ranges information to get the genomic coordinates of the ranges of interest.

To stop the plots from being too cluttered we will consider the top 200 genes only.

library(ggbio)
Need specific help about ggbio? try mailing 
 the maintainer or visit http://tengfei.github.com/ggbio/

Attaching package: 'ggbio'

The following objects are masked from 'package:ggplot2':

    geom_bar, geom_rect, geom_segment, ggsave, stat_bin, stat_identity, xlim
top200 <- sigRegions[order(sigRegions$LR,decreasing = TRUE)[1:200]]
plotGrandLinear(top200 , aes(y = logFC))
using coord:genome to parse x scale

ggbio has alternated the colours of the chromosomes. However, an appealing feature of ggplot2 is the ability to map properties of your plot to variables present in your data. For example, we could create a variable to distinguish between up- and down-regulated genes. The variables used for aesthetic mapping must be present in the mcols section of your ranges object.

mcols(top200)$UpRegulated <- mcols(top200)$logFC > 0
plotGrandLinear(top200, aes(y = logFC, col = UpRegulated))
using coord:genome to parse x scale

plotGrandLinear is a special function in ggbio with preset options for the manhattan style of plot. More often, users will call the autoplot function and ggbio will choose the most appropriate layout. One such layout is the karyogram.

autoplot(top200,layout="karyogram",aes(color=UpRegulated,
                                       fill=UpRegulated))
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.

ggbio is also able to plot the structure of genes according to a particular model represented by a GenomicFeatures object, such as the object we created earlier with the exon coordinates for each gene in the mm10 genome.

autoplot(tx, which=exo[["110308"]])
Parsing transcripts...
Parsing exons...
Parsing cds...
Parsing utrs...
------exons...
------cdss...
------introns...
------utr...
aggregating...
Done
Constructing graphics...

We can even plot the location of sequencing reads if they have been imported using readGAlignments function (or similar).

myreg <- flank(reduce(exo[["110308"]]), 1000, both = T)
bam <- readGappedReads(file="data/bams_small/MCL1.DG.small.bam",
                       param=ScanBamParam(which=myreg),use.names = TRUE)
autoplot(bam, geom = "rect") + 
    xlim(GRanges("chr15", IRanges(101707000, 101713000)))
extracting information...

Like ggplot2, ggbio plots can be saved as objects that can later be modified, or combined together to form more complicated plots. If saved in this way, the plot will only be displayed on a plotting device when we query the object. This strategy is useful when we want to add a common element (such as an ideogram) to a plot composition and don’t want to repeat the code to generate the plot every time.

#idPlot <- plotIdeogram(genome = "mm10",subchr = "chr1")
#idPlot
geneMod <- autoplot(tx, which = myreg)
Parsing transcripts...
Parsing exons...
Parsing cds...
Parsing utrs...
------exons...
------cdss...
------introns...
------utr...
aggregating...
Done
Constructing graphics...
reads.MCL1.DG <- autoplot(bam, stat = "coverage")  + 
    xlim(GRanges("chr15", IRanges(101707000, 101713000))) +
    labs(title="MCL1.DG")
extracting information...
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing scale.
tracks(mm10=geneMod, MCL1.DG=reads.MCL1.DG ) 

Challenge

Create tracks to compare the coverage of the gene Krt5 for the samples MCL1.DG, MCL1.DH, MCL1.LA and MCL1.LB

LS0tCnRpdGxlOiAiUk5BLXNlcSBBbmFseXNpcyBpbiBSIgpzdWJ0aXRsZTogIkFubm90YXRpb24gYW5kIFZpc3VhbGlzYXRpb24gb2YgUk5BLXNlcSByZXN1bHRzIgphdXRob3I6ICJTdGVwaGFuZSBCYWxsZXJlYXUsIE1hcmsgRHVubmluZywgT3NjYXIgUnVlZGEsIEFzaGxleSBTYXdsZSIKZGF0ZTogJ2ByIGZvcm1hdChTeXMudGltZSgpLCAiTGFzdCBtb2RpZmllZDogJWQgJWIgJVkiKWAnCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICBodG1sX25vdGVib29rOgogICAgdG9jOiB5ZXMKbWludXRlczogMzAwCmxheW91dDogcGFnZQpiaWJsaW9ncmFwaHk6IHJlZi5iaWIKLS0tCgoqKk9yaWdpbmFsIEF1dGhvcnM6IEJlbGluZGEgUGhpcHNvbiwgQW5uYSBUcmlnb3MsIE1hdHQgUml0Y2hpZSwgTWFyaWEgRG95bGUsIEhhcnJpZXQgRGFzaG5vdywgQ2hhcml0eSBMYXcqKgpCYXNlZCBvbiB0aGUgY291cnNlIFtSTkFzZXEgYW5hbHlzaXMgaW4gUl0oaHR0cDovL2NvbWJpbmUtYXVzdHJhbGlhLmdpdGh1Yi5pby8yMDE2LTA1LTExLVJOQXNlcS8pIGRlbGl2ZXJlZCBvbiBNYXkgMTEvMTJ0aCAyMDE2CgoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoZWRnZVIpCmBgYAoKQmVmb3JlIHN0YXJ0aW5nIHRoaXMgc2VjdGlvbiwgd2Ugd2lsbCBtYWtlIHN1cmUgd2UgaGF2ZSBhbGwgdGhlIHJlbGV2YW50IG9iamVjdHMgZnJvbSB0aGUgRGlmZmVyZW50aWFsIEV4cHJlc3Npb24gYW5hbHlzaXMgcHJlc2VudC4KCmBgYHtyIGxvYWREYXRhfQpsb2FkKCJSb2JqZWN0cy9ERS5SZGF0YSIpCmBgYAoKIyBPdmVydmlldwoKLSBWaXN1YWxpc2luZyBERSByZXN1bHRzCi0gR2V0dGluZyBhbm5vdGF0aW9uCi0gUmV0cmlldmluZyBnZW5lIG1vZGVscwotIEV4cG9ydGluZyBicm93c2VyIHRyYWVja3MKLSBWaXN1YWxpc2luZyByZXN1bHRzIHdpdGggcmVzcGVjdCB0byBnZW5vbWljIGxvY2F0aW9uCgojIE1haW4gTWF0ZXJpYWxzCgojIyBDcmVhdGUgYSB0YWJsZSBvZiBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiByZXN1bHRzCgpXZSBoYXZlIGEgbGlzdCBvZiBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcywgYnV0IHRoZSBvbmx5IGFubm90YXRpb24gd2UgY2FuIHNlZSBpcyB0aGUgRW50cmV6IEdlbmUgSUQsIHdoaWNoIGlzIG5vdCB2ZXJ5IGluZm9ybWF0aXZlLiAKCmBgYHtyIGNyZWF0ZURlVGFibGV9CnJlc3VsdHMgPC0gYXMuZGF0YS5mcmFtZSh0b3BUYWdzKGxydF9CdnNMLCBuID0gSW5mKSkKaGVhZChyZXN1bHRzKQpgYGAKCiMjIyBWaXN1YWxpc2UgcmVzdWx0cyB3aXRoIGEgJ1NtZWFyIHBsb3QnCgpgZWRnZVJgIHByb3ZpZGVzIGEgZnVuY3Rpb24gYHBsb3RTbWVhcmAgdGhhdCBhbGxvd3MgdXMgdG8gdmlzdWFsaXNlIHRoZSByZXN1bHRzIG9mIGEgREUgYW5hbHlzaXMuIApUaGlzIHBsb3Qgc2hvd3MgdGhlIGxvZy1mb2xkIGNoYW5nZSBhZ2FpbnN0IGxvZy1jb3VudHMgcGVyIG1pbGxpb24sIHdpdGggREUgZ2VuZXMgaGlnaGxpZ2h0ZWQ6CgpgYGB7ciBwbG90U21lYXJ9CiMgaWRlbnRpZnkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCmRlIDwtIGRlY2lkZVRlc3RzREdFKGxydF9CdnNMKQpzdW1tYXJ5KGRlKQoKIyBjcmVhdGUgYSB2ZWN0b3Igb2YgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzCmRldGFncyA8LSByb3duYW1lcyhkZ2VPYmopW2FzLmxvZ2ljYWwoZGUpXQoKIyBwbG90IHNtZWFyIGhpZ2hsaWdodGluZyBERSBnZW5lcwpwbG90U21lYXIobHJ0X0J2c0wsIGRlLnRhZ3MgPSBkZXRhZ3MpCmBgYApIb3dldmVyLCBvbiBzdWNoIGEgcGxvdCBpdCB3b3VsZCBiZSBuaWNlIHRvIGFkZCBsYWJlbHMgdG8gaGlnaGxpZ2h0IHRoZSBnZW5lcyB3aXRoIG1vc3QgZXZpZGVuY2UgZm9yIGJlaW5nIERFLCBvciBvdXIgZmF2b3VyaXRlIGdlbmVzLiBUbyBwZXJmb3JtIHN1Y2ggYSB0YXNrIHdlIG5lZWQgdG8gbWFwIGJldHdlZW4gdGhlIGlkZW50aWZpZXJzIHdlIGhhdmUgaW4gdGhlIGBlZGdlUmAgb3V0cHV0IGFuZCBtb3JlIGZhbWlsaWFyIG5hbWVzLgoKRmluYWxseSwgd2Ugd2lsbCBsb29rIGF0IHNvcGhpc3RpY2F0ZWQgdmlzdWFsaXNhdGlvbnMgdGhhdCBhbGxvdyB1cyB0byBpbmNvcnBvcmF0ZSBpbmZvcm1hdGlvbiBhYm91dCB0aGUgc3RydWN0dXJlIG9mIGEgZ2VuZSwgbGV2ZWwgb2Ygc2VxdWVuY2luZyBjb3ZlcmFnZS4KCiMjIEFkZGluZyBhbm5vdGF0aW9uIHRvIHRoZSBlZGdlUiByZXN1bHRzCgpUaGVyZSBhcmUgYSBudW1iZXIgb2Ygd2F5cyB0byBhZGQgYW5ub3RhdGlvbiwgYnV0IHdlIHdpbGwgZGVtb25zdHJhdGUgaG93IHRvIGRvIHRoaXMgdXNpbmcgdGhlICpvcmcuTW0uZWcuZGIqIHBhY2thZ2UuIFRoaXMgcGFja2FnZSBpcyBvbmUgb2Ygc2V2ZXJhbCAqb3JnYW5pc20tbGV2ZWwqIHBhY2thZ2VzIHdoaWNoIGFyZSByZS1idWlsdCBldmVyeSA2IG1vbnRocy4gVGhlc2UgcGFja2FnZXMgYXJlIGxpc3RlZCBvbiB0aGUgW2Fubm90YXRpb24gc2VjdGlvbl0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9CaW9jVmlld3MuaHRtbCNfX19Bbm5vdGF0aW9uRGF0YSkgb2YgdGhlIEJpb2NvbmR1Y3RvciwgYW5kIGFyZSBpbnN0YWxsZWQgaW4gdGhlIHNhbWUgd2F5IGFzIHJlZ3VsYXIgQmlvY29uZHVjdG9yIHBhY2thZ2VzLiBBbiBhbHRlcm5hdGl2ZSBhcHByb2FjaCBpcyB0byB1c2UgYGJpb21hUnRgLCBhbiBpbnRlcmZhY2UgdG8gdGhlIFtCaW9NYXJ0XShodHRwOi8vd3d3LmJpb21hcnQub3JnLykgcmVzb3VyY2UuIEJpb01hcnQgaXMgbXVjaCBtb3JlIGNvbXByZWhlbnNpdmUsIGJ1dCB0aGUgb3JnYW5pc20gcGFja2FnZXMgZml0IGJldHRlciBpbnRvIHRoZSBCaW9jb25kdWN0b3Igd29ya2Zsb3cuCgoKYGBge3IgaW5zdGFsbGluZ0RCcywgZXZhbD1GQUxTRX0KIyBzb3VyY2UoImh0dHA6Ly93d3cuYmlvY29uZHVjdG9yLm9yZy9iaW9jTGl0ZS5SIikKIyAjIGZvciBtb3VzZQojIGJpb2NMaXRlKCJvcmcuTW0uZWcuZGIiKQojICNGb3IgSHVtYW4KIyBiaW9jTGl0ZSgib3JnLkhzLmVnLmRiIikKYGBgCgpUaGUgcGFja2FnZXMgYXJlIGxhcmdlciBpbiBzaXplIHRoYXQgQmlvY29uZHVjdG9yIHNvZnR3YXJlIHBhY2FrZ2VzLCBidXQgZXNzZW50aWFsbHkgdGhleSBhcmUgZGF0YWJhc2VzIHRoYXQgY2FuIGJlIHVzZWQgdG8gbWFrZSAqb2ZmbGluZSogcXVlcmllcy4gCgpgYGB7ciBsb2FkREIsIG1lc3NhZ2U9RkFMU0V9CmxpYnJhcnkob3JnLk1tLmVnLmRiKQpgYGAKCgpGaXJzdCB3ZSBuZWVkIHRvIGRlY2lkZSB3aGF0IGluZm9ybWF0aW9uIHdlIHdhbnQuIEluIG9yZGVyIHRvIHNlZSB3aGF0IHdlIGNhbiBleHRyYWN0IHdlIGNhbiBydW4gdGhlIGBjb2x1bW5zYCBmdW5jdGlvbiBvbiB0aGUgYW5ub3RhdGlvbiBkYXRhYmFzZS4KCmBgYHtyIGNoZWNrQ29sdW1uc30KY29sdW1ucyhvcmcuTW0uZWcuZGIpCmBgYAoKV2UgYXJlIGdvaW5nIHRvIGZpbHRlciB0aGUgZGF0YWJhc2UgYnkgYSBrZXkgb3Igc2V0IG9mIGtleXMgaW4gb3JkZXIgdG8gZXh0cmFjdCB0aGUgaW5mb3JtYXRpb24gd2Ugd2FudC4gVmFsaWQgbmFtZXMgZm9yIHRoZSBrZXkgY2FuIGJlIHJldHJpZXZlZCB3aXRoIHRoZSBga2V5dHlwZXNgIGZ1bmN0aW9uLgoKYGBge3IgY2hlY2tLZXl0eXBlc30Ka2V5dHlwZXMob3JnLk1tLmVnLmRiKQpgYGAKCldlIHNob3VsZCBzZWUgYEVOVFJFWklEYCwgd2hpY2ggaXMgdGhlIHR5cGUgb2Yga2V5IHdlIGFyZSBnb2luZyB0byB1c2UgaW4gdGhpcyBjYXNlLiBJZiB3ZSBhcmUgdW5zdXJlIHdoYXQgdmFsdWVzIGFyZSBhY2NlcHRhYmxlIGZvciB0aGUga2V5LCB3ZSBjYW4gY2hlY2sgd2hhdCBrZXlzIGFyZSB2YWxpZCB3aXRoIGBrZXlzYAoKYGBge3Igdmlld0VOVFJFWmtleX0Ka2V5cyhvcmcuTW0uZWcuZGIsIGtleXR5cGU9IkVOVFJFWklEIilbMToxMF0KYGBgCgpJdCBpcyBhIHVzZWZ1bCBzYW5pdHkgY2hlY2sgdG8gbWFrZSBzdXJlIHRoYXQgdGhlIGtleXMgeW91IHdhbnQgdG8gdXNlIGFyZSBhbGwgCnZhbGlkLiBXZSBjb3VsZCB1c2UgYCVpbiVgIGluIHRoaXMgY2FzZS4KCmBgYHtyIGNvbXBhcmVLZXlzfQojIyBCdWlsZCB1cCB0aGUgcXVlcnkgc3RlcC1ieS1zdGVwCm15LmtleXMgPC0gcm93bmFtZXMocmVzdWx0cylbMToxMF0KbXkua2V5cyAlaW4lIGtleXMob3JnLk1tLmVnLmRiLCBrZXl0eXBlPSJFTlRSRVpJRCIpCmFsbChteS5rZXlzICVpbiUga2V5cyhvcmcuTW0uZWcuZGIsIGtleXR5cGU9IkVOVFJFWklEIikpCmBgYAoKVG8gYW5ub3RhdGUgb3VyIHJlc3VsdHMsIHdlIGRlZmluaXRlbHkgd2FudCBnZW5lIHN5bWJvbHMgYW5kIHBlcmhhcHMgdGhlIGZ1bGwgZ2VuZSBuYW1lLiBMZXQncyBidWlsZCB1cCBvdXIgYW5ub3RhdGlvbiBpbmZvcm1hdGlvbiBpbiBhIHNlcGFyYXRlIGRhdGEgZnJhbWUgdXNpbmcgdGhlIGBzZWxlY3RgIGZ1bmN0aW9uLgoKYGBge3IgbWFrZUFubm90YXRpb259CmFubm90IDwtIHNlbGVjdChvcmcuTW0uZWcuZGIsCiAgICAgICAgICAgICAga2V5cz1yb3duYW1lcyhyZXN1bHRzKSwgCiAgICAgICAgICAgICAgY29sdW1ucz1jKCJFTlRSRVpJRCIsIlNZTUJPTCIsIkdFTkVOQU1FIikpCgojIEhhdmUgYSBsb29rIGF0IHRoZSBhbm5vdGF0aW9uCmhlYWQoYW5ub3QpCmBgYAoKTGV0J3MgZG91YmxlIGNoZWNrIHRoYXQgdGhlIGBFTlRSRVpJRGAgY29sdW1uIG1hdGNoZXMgZXhhY3RseSB0byBvdXIgYHJlc3VsdHNgIHJvd25hbWVzLgoKYGBge3IgY2hlY2tBbm5vdGF0aW9ufQp0YWJsZShhbm5vdCRFTlRSRVpJRD09cm93bmFtZXMocmVzdWx0cykpCmBgYAoKV2UgY2FuIGJpbmQgaW4gdGhlIGFubm90YXRpb24gaW5mb3JtYXRpb24gdG8gdGhlIGByZXN1bHRzYCBkYXRhIGZyYW1lLiAoUGxlYXNlIG5vdGUgdGhhdCBpZiB0aGUgYHNlbGVjdGAgZnVuY3Rpb24gcmV0dXJucyBhIDE6bWFueSBtYXBwaW5nIHRoZW4geW91IGNhbid0IGp1c3QgYXBwZW5kIHRoZSBhbm5vdGF0aW9uIHRvIHRoZSBmaXQgb2JqZWN0LikKCmBgYHtyIGFubm90YXRlUmVzdWx0c30KcmVzdWx0cy5hbm5vdGF0ZWQgPC0gY2JpbmQocmVzdWx0cywgYW5ub3QpCmhlYWQocmVzdWx0cy5hbm5vdGF0ZWQpCmBgYAoKCldlIGNhbiBzYXZlIHRoZSByZXN1bHRzIHRhYmxlIHVzaW5nIHRoZSBgd3JpdGUuY3N2YCBmdW5jdGlvbiwgd2hpY2ggd3JpdGVzIHRoZSByZXN1bHRzIG91dCB0byBhIGNzdiBmaWxlIHRoYXQgeW91IGNhbiBvcGVuIGluIGV4Y2VsLgoKYGBge3Igb3V0cHV0REV0YWJsZX0Kd3JpdGUuY3N2KHJlc3VsdHMuYW5ub3RhdGVkLAogICAgICAgICAgZmlsZT0iQi5QcmVnVnNMYWNSZXN1bHRzLmNzdiIsCiAgICAgICAgICByb3cubmFtZXM9RkFMU0UpCmBgYAoKKipBIG5vdGUgYWJvdXQgZGVjaWRpbmcgaG93IG1hbnkgZ2VuZXMgYXJlIHNpZ25pZmljYW50Kio6IEluIG9yZGVyIHRvIGRlY2lkZSB3aGljaCBnZW5lcyBhcmUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLCB3ZSB1c3VhbGx5IHRha2UgYSBjdXQtb2ZmIG9mIDAuMDUgb24gdGhlIGFkanVzdGVkIHAtdmFsdWUsIE5PVCB0aGUgcmF3IHAtdmFsdWUuIFRoaXMgaXMgYmVjYXVzZSB3ZSBhcmUgdGVzdGluZyBtb3JlIHRoYW4gMTUwMDAgZ2VuZXMsIGFuZCB0aGUgY2hhbmNlcyBvZiBmaW5kaW5nIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBpcyB2ZXJ5IGhpZ2ggd2hlbiB5b3UgZG8gdGhhdCBtYW55IHRlc3RzLiBIZW5jZSB3ZSBuZWVkIHRvIGNvbnRyb2wgdGhlIGZhbHNlIGRpc2NvdmVyeSByYXRlLCB3aGljaCBpcyB0aGUgYWRqdXN0ZWQgcC12YWx1ZSBjb2x1bW4gaW4gdGhlIHJlc3VsdHMgdGFibGUuIFdoYXQgdGhpcyBtZWFucyBpcyB0aGF0IGlmIDEwMCBnZW5lcyBhcmUgc2lnbmlmaWNhbnQgYXQgYSA1XCUgZmFsc2UgZGlzY292ZXJ5IHJhdGUsIHdlIGFyZSB3aWxsaW5nIHRvIGFjY2VwdCB0aGF0IDUgd2lsbCBiZSBmYWxzZSBwb3NpdGl2ZXMuIE5vdGUgdGhhdCB0aGUgYGRlY2lkZVRlc3RzYCBmdW5jdGlvbiBkaXNwbGF5cyBzaWduaWZpY2FudCBnZW5lcyBhdCA1XCUgRkRSLgoKPiAjIyMgQ2hhbGxlbmdlIHsuY2hhbGxlbmdlfQo+Cj4gUmUtdmlzaXQgdGhlIGBwbG90U21lYXJgIHBsb3QgZnJvbSBhYm92ZSBhbmQgdXNlIHRoZSBgdGV4dGAgZnVuY3Rpb24gdG8gYWRkIGxhYmVscyBmb3IgdGhlIG5hbWVzIG9mIHRoZSB0b3AgMjAgbW9zdCBERSBnZW5lcwo+CgpgYGB7ciBzb2x1dGlvbkNoYWxsZW5nZTEsZWNobz1GQUxTRSxmaWcuaGVpZ2h0PTUsZmlnLndpZHRoPTEwfQoKYGBgCgojIyBGdXJ0aGVyIHZpc3VhbGlzYXRpb24KCiMjIyBWb2xjYW5vIHBsb3QKCkFub3RoZXIgY29tbW9uIHZpc3VhbGlzYXRpb24gaXMgdGhlIApbKnZvbGNhbm8gcGxvdCpdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1ZvbGNhbm9fcGxvdF8oc3RhdGlzdGljcykpIHdoaWNoIApkaXNwbGF5cyBhIG1lYXN1cmUgb2Ygc2lnbmlmaWNhbmNlIG9uIHRoZSB5LWF4aXMgYW5kIGZvbGQtY2hhbmdlIG9uIHRoZSB4LWF4aXMuIAoKYGBge3Igdm9sY2Fub1Bsb3QsZmlnLmhlaWdodD01LGZpZy53aWR0aD0xMH0KCnNpZ25pZiA8LSAtbG9nMTAocmVzdWx0cy5hbm5vdGF0ZWQkRkRSKQoKcGxvdChyZXN1bHRzLmFubm90YXRlZCRsb2dGQywgCiAgICAgc2lnbmlmLAogICAgIGJnPSJibGFjayIsCiAgICAgcGNoPTIxLAogICAgIHhsYWI9ImxvZzIoRm9sZCBDaGFuZ2UpIikKcG9pbnRzKHJlc3VsdHMuYW5ub3RhdGVkW2RldGFncywibG9nRkMiXSwKICAgICAgIC1sb2cxMChyZXN1bHRzLmFubm90YXRlZFtkZXRhZ3MsIkZEUiJdKSwKICAgICAgIHBjaD0yMSwKICAgICAgIGJnPSJyZWQiKQoKYGBgCgojIyMgU3RyaXAgY2hhcnQgZm9yIGdlbmUgZXhwcmVzc2lvbgoKQmVmb3JlIGZvbGxvd2luZyB1cCBvbiB0aGUgREUgZ2VuZXMgd2l0aCBmdXJ0aGVyIGxhYiB3b3JrLCBhIHJlY29tbWVuZGVkICpzYW5pdHkgY2hlY2sqIGlzIHRvIGhhdmUgYSBsb29rIGF0IHRoZSBleHByZXNzaW9uIGxldmVscyBvZiB0aGUgaW5kaXZpZHVhbCBzYW1wbGVzIGZvciB0aGUgZ2VuZXMgb2YgaW50ZXJlc3QuIFdlIGNhbiBxdWlja2x5IGxvb2sgYXQgZ3JvdXBlZCBleHByZXNzaW9uIHVzaW5nIGBzdHJpcGNoYXJ0YC4gV2UgY2FuIHVzZSB0aGUgbm9ybWFsaXNlZCBsb2cgZXhwcmVzc2lvbiB2YWx1ZXMgaW4gdGhlICBgZGdlQ291bnRzYCBvYmplY3QgKGBkZ2VDb3VudHMkY291bnRzYCkuCgpgYGB7ciBnZW5lQ291bnRTdHJpcGNoYXJ0LCBmaWcud2lkdGg9NSwgZmlnLmhlaWdodD01LCBmaWcuYWxpZ249ImNlbnRlciJ9Cm5vcm1Db3VudHMgPC0gY3BtKGRnZU9iaiwgbG9nID0gVCkKCiMgTGV0J3MgbG9vayBhdCB0aGUgZmlyc3QgZ2VuZSBpbiB0aGUgdG9wVGFibGUsIEtydDUsIHdoaWNoIGhhcyBhIHJvd25hbWUgMTEwMzA4CnBhcihtYXI9Yyg4LDQsMiwyKSkgI2FkanVzdCB0aGUgcGxvdCBtYXJnaW5zIHRoZSB4LWxhYmVscyBhcmUgdmlzaWJsZSAtIHNlZSA/cGFyCnN0cmlwY2hhcnQobm9ybUNvdW50c1siMTEwMzA4IixdfnNhbXBsZWluZm8kR3JvdXAsCiAgICAgICAgICAgeGxhYj0iIiwKICAgICAgICAgICB5bGFiPSJsb2cyKENvdW50cykiLAogICAgICAgICAgIHZlcnRpY2FsPVRSVUUsCiAgICAgICAgICAgbGFzPTIsCiAgICAgICAgICAgcGNoPTIxLAogICAgICAgICAgIGNvbD0xOjYsCiAgICAgICAgICAgY2V4PTIpCmBgYAoKIyMjIEludGVyYWN0aXZlIFN0cmlwQ2hhcnQgd2l0aCBHbGltbWEKCkFuIGludGVyYWN0aXZlIHZlcnNpb24gb2YgdGhlIHZvbGNhbm8gcGxvdCBhYm92ZSB0aGF0IGluY2x1ZGVzIHRoZSByYXcgcGVyIHNhbXBsZSB2YWx1ZXMgaW4gYSBzZXBhcmF0ZSBwYW5lbCBpcyBwb3NzaWJsZSB2aWEgdGhlIGBnbFhZUGxvdGAgZnVuY3Rpb24gaW4gdGhlICpHbGltbWEqIHBhY2thZ2UuCgoKYGBge3J9CmxpYnJhcnkoR2xpbW1hKQpncm91cCA8LSBzYW1wbGVpbmZvJEdyb3VwCmxldmVscyhncm91cCkgPC0gYygiYmFzYWwubGFjdGF0ZSIsImJhc2FsLnByZWciLCJiYXNhbC52aXJnaW4iLCJsdW0ubGFjdGF0ZSIsICJsdW0ucHJlZyIsICJsdW0udmlyZ2luIikKYW5ub3QubW9kIDwtIGFubm90CmNvbG5hbWVzKGFubm90Lm1vZClbMV0gPC0gIkdlbmVJRCIKZGUgPC0gYXMubnVtZXJpYyhyZXN1bHRzJEZEUjw9MC4wMSkKZ2xYWVBsb3QoeD1yZXN1bHRzJGxvZ0ZDLCB5PS1sb2cxMChyZXN1bHRzJEZEUiksCiAgICAgICAgIHhsYWI9ImxvZ0ZDIiwgeWxhYj0iQiIsIG1haW49IkIuUHJlZ1ZzTGFjIiwKICAgICAgICAgY291bnRzPW5vcm1Db3VudHMsIGdyb3Vwcz1ncm91cCwgc3RhdHVzPWRlLAogICAgICAgICBhbm5vPWFubm90Lm1vZCwgaWQuY29sdW1uPSJFTlRSRVpJRCIsIGZvbGRlcj0idm9sY2FubyIpCmBgYAoKVGhpcyBmdW5jdGlvbiBjcmVhdGVzIGFuIGh0bWwgcGFnZSAoLi92b2xjYW5vL1hZLVBsb3QuaHRtbCkgd2l0aCBhIHZvbGNhbm8gcGxvdCBvbiB0aGUgbGVmdCBhbmQgYSBwbG90IHNob3dpbmcgdGhlIGxvZy1DUE0gcGVyIHNhbXBsZSBmb3IgYSBzZWxlY3RlZCBnZW5lIG9uIHRoZSByaWdodC4gQSBzZWFyY2ggYmFyIGlzIGF2YWlsYWJsZSB0byBzZWFyY2ggZm9yIGdlbmVzIG9mIGludGVyZXN0LgoKIyMgUmV0cmlldmluZyBHZW5vbWljIExvY2F0aW9ucwoKSXQgbWlnaHQgc2VlbSBuYXR1cmFsIHRvIGFkZCBnZW5vbWljIGxvY2F0aW9ucyB0byBvdXIgYW5ub3RhdGlvbiB0YWJsZSwgYW5kIHBvc3NpYmx5IGEgYml0IG9kZCB0aGF0IHRoZSBgb3JnLk1tLmVnLmRiYCBwYWNrYWdlIGRvZXMgbm90IHN1cHBseSBzdWNoIG1hcHBpbmdzLiBJbiBmYWN0LCB0aGVyZSBpcyBhIHdob2xlIHN1aXRlIG9mIHBhY2thZ2UgZm9yIHBlcmZvcm1pbmcgdGhpcywgYW5kIG1vcmUtYWR2YW5jZWQgcXVlcmllcyB0aGF0IHJlbGF0ZSB0byB0aGUgbG9jYXRpb24gb2YgZ2VuZXMuIFRoZXNlIGFyZSBsaXN0ZWQgb24gdGhlIEJpb2NvbmR1Y3RvciBbYW5ub3RhdGlvbiBwYWdlXShodHRwOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL0Jpb2NWaWV3cy5odG1sI19fX0Fubm90YXRpb25EYXRhKSBhbmQgaGF2ZSB0aGUgcHJlZml4IGBUeERiLmAKClRoZSBwYWNrYWdlIHdlIHdpbGwgYmUgdXNpbmcgaXMgYFR4RGIuTW11c2N1bHVzLlVDU0MubW0xMC5rbm93bkdlbmVgLiBQYWNrYWdlcyBhcmUgYXZhaWxhYmxlIGZvciBvdGhlciBvcmdhbmlzbXMgYW5kIGdlbm9tZSBidWlsZHMuIEl0IGlzIGV2ZW4gcG9zc2libGUgdG8gKmJ1aWxkIHlvdXIgb3duIGRhdGFiYXNlKiBpZiBvbmUgZG9lcyBub3QgZXhpc3QuIFNlZSBgdmlnbmV0dGUoIkdlbm9taWNGZWF0dXJlcyIpYCBmb3IgZGV0YWlscwoKYGBge3IgaW5zdGFsbFR4RGIsIGV2YWw9RkFMU0V9CiMgc291cmNlKCJodHRwOi8vd3d3LmJpb2NvbmR1Y3Rvci5vcmcvYmlvY0xpdGUuUiIpCiMjIEZvciBtb3VzZQojIGJpb2NMaXRlKCJUeERiLk1tdXNjdWx1cy5VQ1NDLm1tMTAua25vd25HZW5lIikKIyMgRm9yIEh1bWFucwojIGJpb2NMaXRlKCJUeERiLkhzYXBpZW5zLlVDU0MuaGcxOS5rbm93bkdlbmUiKQpgYGAKCldlIGxvYWQgdGhlIGxpYnJhcnkgaW4gdGhlIHVzdWFsIGZhc2hpb24gYW5kIGNyZWF0ZSBhIG5ldyBvYmplY3QgdG8gc2F2ZSBzb21lIHR5cGluZy4gQXMgd2l0aCB0aGUgYG9yZy5gIHBhY2thZ2VzLCB3ZSBjYW4gcXVlcnkgd2hhdCBjb2x1bW5zIGFyZSBhdmFpbGFibGUgd2l0aCBgY29sdW1uc2AsCgpgYGB7ciBsb2FkVHhEQiwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShUeERiLk1tdXNjdWx1cy5VQ1NDLm1tMTAua25vd25HZW5lKQp0eCA8LSBUeERiLk1tdXNjdWx1cy5VQ1NDLm1tMTAua25vd25HZW5lCmNvbHVtbnModHgpCmBgYAoKVGhlIGBzZWxlY3RgIGZ1bmN0aW9uIGlzIHVzZWQgaW4gdGhlIHNhbWUgbWFubmVyIGFzIHRoZSBgb3JnLk1tLmVnLmRiYCBwYWNrYWdlcy4gCgoKPiAjIyMgQ2hhbGxlbmdlIDIgey5jaGFsbGVuZ2V9Cj4KPiBVc2UgdGhlIFR4RGIuTW11c2N1bHVzLlVDU0MubW0xMC5rbm93bkdlbmUgcGFja2FnZSB0byByZXRyaWV2ZSB0aGUgZXhvbiBjb29yZGluYXRlcyBmb3IgdGhlIGdlbmVzIGA1MDkxNmAsIGAxMTAzMDhgLCBgMTIyOTNgIAo+CgpgYGB7ciBzb2x1dGlvbkNoYWxsZW5nZTIsIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9CgoKCgpgYGAKCiMjIE92ZXJ2aWV3IG9mIEdlbm9taWNSYW5nZXMKCk9uZSBvZiB0aGUgcmVhbCBzdHJlbmd0aHMgb2YgdGhlIGB0eGRiLi5gIHBhY2thZ2VzIGlzIHRoZSBhYmlsaXR5IG9mIGludGVyZmFjZSB3aXRoIGBHZW5vbWljUmFuZ2VzYCwgd2hpY2ggaXMgdGhlIG9iamVjdCB0eXBlIHVzZWQgdGhyb3VnaG91dCBCaW9jb25kdWN0b3IgW3RvIG1hbmlwdWxhdGUgR2Vub21pYyBJbnRlcnZhbHNdKGh0dHBzOi8vd3d3Lm5jYmkubmxtLm5paC5nb3YvcG1jL2FydGljbGVzL1BNQzM3Mzg0NTgvcGRmL3BjYmkuMTAwMzExOC5wZGYpLiAKClRoZXNlIG9iamVjdCB0eXBlcyBwZXJtaXQgdXMgdG8gcGVyZm9ybSBjb21tb24gb3BlcmF0aW9ucyBvbiBpbnRlcnZhbHMgc3VjaCBhcyBvdmVybGFwcGluZyBhbmQgY291bnRpbmcuIFdlIGNhbiBkZWZpbmUgdGhlIGNocm9tb3NvbWUsIHN0YXJ0IGFuZCBlbmQgcG9zaXRpb24gb2YgZWFjaCByZWdpb24gKGFsc28gc3RyYW5kIHRvbywgYnV0IG5vdCBzaG93biBoZXJlKS4KCmBgYHtyIHNpbXBsZUdSfQpsaWJyYXJ5KEdlbm9taWNSYW5nZXMpCnNpbXBsZV9yYW5nZSA8LSBHUmFuZ2VzKHNlcW5hbWVzID0gIjEiLCByYW5nZXMgPSBJUmFuZ2VzKHN0YXJ0PTEwMDAsIGVuZD0yMDAwKSkKc2ltcGxlX3JhbmdlCgpgYGAKCldlIGRvbid0IGhhdmUgdG8gaGF2ZSBhbGwgb3VyIHJhbmdlcyBsb2NhdGVkIG9uIHRoZSBzYW1lIGNocm9tb3NvbWUKCmBgYHtyIGdyRm9yVGhyZWVHZW5lc30KY2hycyA8LSBjKCJjaHIxMyIsICJjaHIxNSIsICJjaHI1IikKc3RhcnQgPC0gYyg3MzAwMDAwMCwgMTAxMDAwMDAwLCAxNTAwMDAwMCkKZW5kIDwtIGMoNzQwMDAwMDAsIDEwMjAwMDAwMCwgMTYwMDAwMDApCgpteV9yYW5nZXMgPC0gR1JhbmdlcyhzZXFuYW1lcyA9IHJlcChjaHJzLCAzKSwKICAgICAgICAgICAgICAgICAgICAgcmFuZ2VzID0gSVJhbmdlcyhzdGFydCA9IHJlcChzdGFydCwgZWFjaCA9IDMpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGVuZCA9IHJlcChlbmQsIGVhY2ggPSAzKSkKICAgICAgICAgICAgICAgICAgICAgKQpteV9yYW5nZXMKYGBgCgpUaGVyZSBhcmUgYSBudW1iZXIgb2YgdXNlZnVsIGZ1bmN0aW9ucyBmb3IgY2FsY3VsYXRpbmcgcHJvcGVydGllcyBvZiB0aGUgZGF0YSAoc3VjaCBhcyAqY292ZXJhZ2UqIG9yIHNvcnRpbmcpLiBOb3Qgc28gbXVjaCBmb3IgUk5BLXNlcSBhbmFseXNpcywgYnV0IGBHZW5vbWljUmFuZ2VzYCBhcmUgdXNlZCB0aHJvdWdob3V0IEJpb2NvbmR1Y3RvciBmb3IgdGhlIGFuYWx5c2lzIG9mIE5HUyBkYXRhLiAKCkZvciBpbnN0YW5jZSwgd2UgY2FuIHF1aWNrbHkgaWRlbnRpZnkgb3ZlcmxhcHBpbmcgcmVnaW9ucyBiZXR3ZWVuIHR3byBgR2Vub21pY1Jhbmdlc2AuIAoKYGBge3IgZmluZE92ZXJsYXBzfQprZXlzIDwtIGMoIjUwOTE2IiwgIjExMDMwOCIsICIxMjI5MyIpCmdlbmVQb3MgPC0gc2VsZWN0KHR4LAogICAgICAgICAgICAgICAgICBrZXlzID0ga2V5cywKICAgICAgICAgICAgICAgICAga2V5dHlwZSA9ICJHRU5FSUQiLAogICAgICAgICAgICAgICAgICBjb2x1bW5zID0gYygiRVhPTkNIUk9NIiwgIkVYT05TVEFSVCIsICJFWE9ORU5EIikKICAgICAgICAgICAgICAgICAgKQoKZ2VuZVJhbmdlcyA8LSBHUmFuZ2VzKGdlbmVQb3MkRVhPTkNIUk9NLCAKICAgICAgICAgICAgICAgICAgICAgIHJhbmdlcyA9IElSYW5nZXMoZ2VuZVBvcyRFWE9OU1RBUlQsIGdlbmVQb3MkRVhPTkVORCksIAogICAgICAgICAgICAgICAgICAgICAgR0VORUlEID0gZ2VuZVBvcyRHRU5FSUQpCmdlbmVSYW5nZXMKCmZpbmRPdmVybGFwcyhteV9yYW5nZXMsIGdlbmVSYW5nZXMpCmBgYAoKSG93ZXZlciwgd2UgaGF2ZSB0byBwYXkgYXR0ZW50aW9uIHRvIHRoZSBuYW1pbmcgY29udmVudGlvbiB1c2VkIGZvciBlYWNoIG9iamVjdC4gCmBzZXFsZXZlbHNTdHlsZWAgY2FuIGhlbHAuCgpgYGB7ciBzZXFOYW1pbmdTdHlsZX0Kc2VxbGV2ZWxzU3R5bGUoZ2VuZVJhbmdlcykKc2VxbGV2ZWxzU3R5bGUobXlfcmFuZ2VzKQpzZXFsZXZlbHNTdHlsZShzaW1wbGVfcmFuZ2UpCmBgYAoKCiMjIyBFeHBvcnRpbmcgdHJhY2tzCgpJdCBpcyBhbHNvIHBvc3NpYmxlIHRvIHNhdmUgdGhlIHJlc3VsdHMgb2YgYSBCaW9jb25kdWN0b3IgYW5hbHlzaXMgaW4gYSBicm93c2VyIHRvIGVuYWJsZSBpbnRlcmFjdGl2ZSBhbmFseXNpcyBhbmQgaW50ZWdyYXRpb24gd2l0aCBvdGhlciBkYXRhIHR5cGVzLCBvciBzaGFyaW5nIHdpdGggY29sbGFib3JhdG9ycy4gRm9yIGluc3RhbmNlLCB3ZSBtaWdodCB3YW50IGEgYnJvd3NlciB0cmFjayB0byBpbmRpY2F0ZSB3aGVyZSBvdXIgZGlmZmVyZW50aWFsbHktZXhwcmVzc2VkIGdlbmVzIGFyZSBsb2NhdGVkLiBXZSBzaGFsbCB1c2UgdGhlIGBiZWRgIGZvcm1hdCB0byBkaXNwbGF5IHRoZXNlIGxvY2F0aW9ucy4gV2Ugd2lsbCBhbm5vdGF0ZSB0aGUgcmFuZ2VzIHdpdGggaW5mb3JtYXRpb24gZnJvbSBvdXIgYW5hbHlzaXMgc3VjaCBhcyB0aGUgZm9sZC1jaGFuZ2UgYW5kIHNpZ25pZmljYW5jZS4KCkZpcnN0IHdlIGNyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIGp1c3QgdGhlIERFIGdlbmVzLgpgYGB7ciB0YWJsZU9mREVHZW5lc30Kc2lnR2VuZXMgPC0gcmVzdWx0cy5hbm5vdGF0ZWRbZGV0YWdzLF0KbWVzc2FnZSgiTnVtYmVyIG9mIHNpZ25pZmljYW50bHkgREUgZ2VuZXM6ICIsIG5yb3coc2lnR2VuZXMpKQpoZWFkKHNpZ0dlbmVzKQpgYGAKCiMjIyBDcmVhdGUgYSBnZW5vbWljIHJhbmdlcyBvYmplY3QKClNldmVyYWwgY29udmVuaWVuY2UgZnVuY3Rpb25zIGV4aXN0IHRvIHJldHJpZXZlIHRoZSBzdHJ1Y3R1cmUgb2YgZXZlcnkgZ2VuZSBmcm9tCmEgZ2l2ZW4gVHhEYiBvYmplY3QgaW4gb25lIGxpc3QuIFRoZSBvdXRwdXQgb2YgYGV4b25zQnlgIGlzIGEgbGlzdCwgd2hlcmUgZWFjaCAKaXRlbSBpbiB0aGUgbGlzdCBpcyB0aGUgZXhvbiBjby1vcmRpbmF0ZXMgb2YgYSBwYXJ0aWN1bGFyIGdlbmUsIGhvd2V2ZXIsIHdlIGRvIApub3QgbmVlZCB0aGlzIGxldmVsIG9mIGdyYW51bGFyaXR5IGZvciB0aGUgYmVkIG91dHB1dCwgc28gd2Ugd2lsbCBjb2xsYXBzZSB0byBhIApzaW5nbGUgcmVnaW9uIGZvciBlYWNoIGdlbmUuIAoKRmlyc3Qgd2UgdXNlIHRoZSBgcmFuZ2VgIGZ1bmN0aW9uIHRvIG9idGFpbiBhIHNpbmdsZSByYW5nZSBmb3IgZXZlcnkgZ2VuZSBhbmQgCnRyYW5mb3JtIHRvIGEgbW9yZSBjb252ZW5pZW50IG9iamVjdCB3aXRoIGB1bmxpc3RgLgoKYGBge3IgZ2V0R2VuZVJhbmdlc30KZXhvIDwtIGV4b25zQnkodHgsICJnZW5lIikKZXhvUmFuZ2VzIDwtIHVubGlzdChyYW5nZShleG8pKQpzaWdSZWdpb25zIDwtIGV4b1Jhbmdlc1tuYS5vbWl0KG1hdGNoKHNpZ0dlbmVzJEVOVFJFWklELCBuYW1lcyhleG9SYW5nZXMpKSldCnNpZ1JlZ2lvbnMKYGBgCgpGb3IgdmlzdWFsaXNhdGlvbiBwdXJwb3Nlcywgd2UgYXJlIGdvaW5nIHRvIHJlc3RyaWN0IHRoZSBkYXRhIHRvIGdlbmVzIHRoYXQgYXJlIGxvY2F0ZWQgb24gY2hyb21vc29tZXMgMSB0byAxOSBhbmQgdGhlIHNleCBjaHJvbW9zb21lcy4gVGhpcyBjYW4gYmUgZG9uZSB3aXRoIHRoZSBga2VlcFNlcUxldmVsc2AgZnVuY3Rpb24uCgpgYGB7ciB0cmltU2VxdWVuY2VzfQpzZXFsZXZlbHMoc2lnUmVnaW9ucykKc2lnUmVnaW9ucyA8LSBrZWVwU2VxbGV2ZWxzKHNpZ1JlZ2lvbnMsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgdmFsdWUgPSBwYXN0ZTAoImNociIsIGMoMToxOSwiWCIsIlkiKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICBwcnVuaW5nLm1vZGU9InRpZHkiKQpzZXFsZXZlbHMoc2lnUmVnaW9ucykKYGBgCgojIyMgQWRkIG1ldGFkYXRhIHRvIEdSYW5nZXMgb2JqZWN0CgpBIHVzZWZ1bCBwcm9wZXJ5IG9mIEdlbm9taWNSYW5nZXMgaXMgdGhhdCB3ZSBjYW4gYXR0YWNoICptZXRhZGF0YSogdG8gZWFjaCByYW5nZSB1c2luZyB0aGUgYG1jb2xzYApmdW5jdGlvbi4gVGhlIG1ldGFkYXRhIGNhbiBiZSBzdXBwbGllZCBpbiB0aGUgZm9ybSBvZiBhIGRhdGEgZnJhbWUuCgpgYGB7ciBhZGRERVJlc3VsdHN9Cm1jb2xzKHNpZ1JlZ2lvbnMpIDwtIHNpZ0dlbmVzW21hdGNoKG5hbWVzKHNpZ1JlZ2lvbnMpLCByb3duYW1lcyhzaWdHZW5lcykpLCBdCnNpZ1JlZ2lvbnMKYGBgCgojIyMgU2NvcmVzIGFuZCBjb2xvdXIgb24gZXhwb3J0ZWQgdHJhY2tzCgpUaGUgYC5iZWRgIGZpbGUgZm9ybWF0IGlzIGNvbW1vbmx5IHVzZWQgdG8gc3RvcmUgZ2Vub21pYyBsb2NhdGlvbnMgZm9yIGRpc3BsYXkgCmluIGdlbm9tZSBicm93c2VycyAoZS5nLiB0aGUgVUNTQyBicm93c2VyIG9yIElHVikgYXMgdHJhY2tzLiBSYXRoZXIgdGhhbiBqdXN0IApyZXByZXNlbnRpbmcgdGhlIGdlbm9taWMgbG9jYXRpb25zLCB0aGUgYC5iZWRgIGZvcm1hdCBpcyBhbHNvIGFibGUgdG8gY29sb3VyIAplYWNoIHJhbmdlIGFjY29yZGluZyB0byBzb21lIHByb3BlcnR5IG9mIHRoZSBhbmFseXNpcyAoZS5nLiBkaXJlY3Rpb24gYW5kIAptYWduaXR1ZGUgb2YgY2hhbmdlKSB0byBoZWxwIGhpZ2hsaWdodCBwYXJ0aWN1bGFyIHJlZ2lvbnMgb2YgaW50ZXJlc3QuIEEgc2NvcmUKY2FuIGFsc28gYmUgZGlzcGxheWVkIHdoZW4gYSBwYXJ0aWN1bGFyIHJlZ2lvbiBpcyBjbGlja2VkLW9uLgoKRm9yIHRoZSBzY29yZSB3ZSBjYW4gdXNlIHRoZSAkLWxvZ197MTB9JCBvZiB0aGUgYWRqdXN0ZWQgcC12YWx1ZSBhbmQgCmNvbG91ciBzY2hlbWUgZm9yIHRoZSByZWdpb25zIGJhc2VkIG9uIHRoZSBmb2xkLWNoYW5nZQoKYGNvbG9yUmFtcFBhbGV0dGVgIGlzIGEgdXNlZnVsIGZ1bmN0aW9uIGluIGJhc2UgUiBmb3IgY29uc3RydWN0aW5nIGEgcGFsZXR0ZSBiZXR3ZWVuIHR3byBleHRyZW1lcy4gKipXaGVuIGNob29zaW5nIGNvbG91ciBwYWxldHRlcywgbWFrZSBzdXJlIHRoZXkgYXJlIGNvbG91ciBibGluZCBmcmllbmRseSoqLiBUaGUgcmVkIC8gZ3JlZW4gY29sb3VyIHNjaGVtZSB0cmFkaXRpb25hbGx5LWFwcGxpZWQgdG8gbWljcm9hcnJheXMgaXMgYSAqKipiYWQqKiogY2hvaWNlLgoKV2Ugd2lsbCBhbHNvIHRydW5jYXRlIHRoZSBmb2xkLWNoYW5nZXMgdG8gYmV0d2VlbiAtNSBhbmQgNSB0byBhbmQgZGl2aWRlIHRoaXMgcmFuZ2UgaW50byAxMCBlcXVhbCBiaW5zCgpgYGB7cn0KcmJQYWwgPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJyZWQiLCAiYmx1ZSIpKQpsb2dmYyA8LSBwbWF4KHNpZ1JlZ2lvbnMkbG9nRkMsIC01KQpsb2dmYyA8LSBwbWluKGxvZ2ZjICwgNSkKCkNvbHMgPC0gcmJQYWwoMTApW2FzLm51bWVyaWMoY3V0KGxvZ2ZjLCBicmVha3MgPSAxMCkpXQpgYGAKClRoZSBjb2xvdXJzIGFuZCBzY29yZSBoYXZlIHRvIGJlIHNhdmVkIGluIHRoZSBHUmFuZ2VzIG9iamVjdCBhcyBgc2NvcmVgIGFuZCBgaXRlbVJnYmAgY29sdW1ucyByZXNwZWN0aXZlbHksIGFuZCB3aWxsIGJlIHVzZWQgdG8gY29uc3RydWN0IHRoZSBicm93c2VyIHRyYWNrLiBUaGUgcnRyYWNrbGF5ZXIgcGFja2FnZSBjYW4gYmUgdXNlZCB0byBpbXBvcnQgYW5kIGV4cG9ydCBicm93c2VycyB0cmFja3MuCgpOb3cgd2UgY2FuIGV4cG9ydCB0aGUgc2lnbmlmY2FudCByZXN1bHRzIGZyb20gdGhlIERFIGFuYWx5c2lzIGFzIGEgYC5iZWRgIHRyYWNrIHVzaW5nIGBydHJhY2tsYXllcmAuIFlvdSBjYW4gbG9hZCB0aGUgcmVzdWx0aW5nIGZpbGUgaW4gSUdWLCBpZiB5b3Ugd2lzaC4KCmBgYHtyfQptY29scyhzaWdSZWdpb25zKSRzY29yZSA8LSAtbG9nMTAoc2lnUmVnaW9ucyRGRFIpCm1jb2xzKHNpZ1JlZ2lvbnMpJGl0ZW1SZ2IgPC0gQ29scwpzaWdSZWdpb25zCgpsaWJyYXJ5KHJ0cmFja2xheWVyKQpleHBvcnQoc2lnUmVnaW9ucyAsIGNvbiA9ICJ0b3BIaXRzLmJlZCIpCmBgYAoKIyBBZGRpdGlvbmFsIE1hdGVyaWFscwoKIyMgRXh0cmFjdGluZyBSZWFkcwoKQXMgd2UgaGF2ZSBiZWVuIHVzaW5nIGNvdW50cyBhcyBvdXIgc3RhcnRpbmcgcG9pbnQsIHdlIGhhdmVuJ3QgaW52ZXN0aWdhdGVkIHRoZSBhbGlnbmVkIHJlYWRzIGZyb20gb3VyIGV4cGVyaW1lbnQsIGFuZCBob3cgdGhleSBhcmUgcmVwcmVzZW50ZWQuIEFzIHlvdSBtYXkgYmUgYXdhcmUsIGFsaWduZWQgcmVhZHMgYXJlIHVzdWFsbHkgc3RvcmVkIGluIGEgKmJhbSogZmlsZSB0aGF0IGNhbiBiZSBtYW5pcHVsYXRlZCB3aXRoIG9wZW4tc291cmNlIGNvbW1hbmQtbGluZSB0b29scyBzdWNoIGFzIFsqc2FtdG9vbHMqXShodHRwOi8vd3d3Lmh0c2xpYi5vcmcvKSBhbmQgWypwaWNhcmQqXShodHRwczovL2Jyb2FkaW5zdGl0dXRlLmdpdGh1Yi5pby9waWNhcmQvKS4gQmlvY29uZHVjdG9yIHByb3ZpZGUgYSBsb3ctbGV2ZWwgaW50ZXJmYWNlIHRvIGRhdGEvYmFtL3NhbSBmaWxlcyBpbiB0aGUgZm9ybSBvZiB0aGUgYFJzYW10b29sc2AgcGFja2FnZS4gVGhlIGBHZW5vbWljQWxpZ25tZW50c2AgcGFja2FnZSBjYW4gYWxzbyBiZSB1c2VkIHRvIHJldHJpZXZlIHRoZSByZWFkcyBtYXBwaW5nIHRvIGEgcGFydGljdWxhciBnZW5vbWljIHJlZ2lvbiBpbiBhbiBlZmZpY2llbnQgbWFubmVyLgoKYGBge3IgbWVzc2FnZT1GQUxTRX0KbGlicmFyeShHZW5vbWljQWxpZ25tZW50cykKYGBgCgpJbiB0aGUgZGlyZWN0b3J5IGBiYW1zX3NtYWxsYCB0aGVyZSBzaG91bGQgYmUgYC5iYW1gIGZpbGVzIGZvciBzb21lIG9mIHRoZSBzYW1wbGVzIGluIHRoZSBleGFtcGxlIHN0dWR5LiBUaGUgd29ya2Zsb3cgdG8gcHJvZHVjZSB0aGVzZSBmaWxlcyBpcyBkZXNjcmliZWQgaW4gYSBbc3VwcGxtZW50YXJ5IHBhZ2VdKC4uL1N1cHBsZW1lbnRhcnlfTWF0ZXJpYWxzL1MxX0dldHRpbmdfcmF3X3JlYWRzX2Zyb21fU1JBLm5iLmh0bWwpIGZvciB0aGUgY291cnNlLiBJbiBicmllZiwgdGhlIHJhdyByZWFkcyAoYGZhc3RxYCkgd2VyZSBkb3dubG9hZGVkIGZyb20gdGhlIFNob3J0IFJlYWQgQXJjaGl2ZSAoU1JBKSBhbmQgYWxpZ25lZCB3aXRoIGBoaXNhdDJgLiBFYWNoIGJhbSBmaWxlIHdhcyBuYW1lZCBhY2NvcmRpbmcgdG8gdGhlIGZpbGUgbmFtZSBpbiBTUkEsIGJ1dCB3ZSBoYXZlIHJlbmFtZWQgdGhlIGZpbGVzIGFjY29yZGluZyB0byB0aGVpciBuYW1lIGluIHRoZSBzdHVkeS4gQW4gaW5kZXggZmlsZSAoYC5iYWlgKSBoYXMgYmVlbiBnZW5lcmF0ZWQgZm9yIGVhY2ggYmFtIGZpbGUuIEluIG9yZGVyIHRvIHJlZHVjZSB0aGUgc2l6ZSwgdGhlIGJhbSBmaWxlcyB1c2VkIGhlcmUgb25seSBjb250YWluIGEgc3Vic2V0IG9mIHRoZSByZWFkcyB0aGF0IHdlcmUgYWxpZ25lZCBpbiB0aGUgcmVnaW9uIGNocjE1OjEwMTcwNzAwMC0xMDE3MTMwMDAuCgoKYGBge3J9Cmxpc3QuZmlsZXMoImRhdGEvYmFtc19zbWFsbC8iKQpgYGAKClRoZSBgcmVhZEdBbGlnbm1lbnRzYCBmdW5jdGlvbiBwcm92aWRlcyBhIHNpbXBsZSBpbnRlcmZhY2UgdG8gaW50ZXJyb2dhdGUgdGhlIGFsaWduZWQgcmVhZHMgZm9yIGEgcGFydGljdWxhciBzYW1wbGUuIEl0IGNhbiBhbHNvIHV0aWxpc2UgdGhlICppbmRleCogZmlsZSBpbiBvcmRlciB0byByZXRyaWV2ZSBvbmx5IHRoZSByZWFkcyB0aGF0IGNvcnJlc3BvbmQgdG8gYSBzcGVjaWZpYyByZWdpb24gaW4gYW4gZWZmaWNpZW50IG1hbm5lci4gVGhlIG91dHB1dCBpbmNsdWRlcyB0aGUgZ2Vub21pYyBsb2NhdGlvbiBvZiBlYWNoIGFsaWduZWQgcmVhZCBhbmQgdGhlIENJR0FSICgqKkMqKm9tcGFjdCAqKkkqKmRpb3N5bmNyYXRpYyAqKkcqKmFwcGVkICoqQSoqbGlnbm1lbnQgKipSKiplcG9ydCk7IHdoZXJlICpNKiBkZW5vdGVzIGFuIG1hdGNoIHRvIHRoZSBnZW5vbWUgYW5kICpJKiwgKkQqIGNvcnJlc3BvbmQgdG8gaW5zZXJ0aW9ucyBhbmQgZGVsZXRpb25zLgoKYGBge3J9CmdlbmVyZWdpb24gPC0gZXhvW1siMTEwMzA4Il1dCgpteS5yZWFkcyA8LSByZWFkR0FsaWdubWVudHMoZmlsZT0iZGF0YS9iYW1zX3NtYWxsL01DTDEuREcuc21hbGwuYmFtIiwKICAgICAgICAgICAgICAgICAgICAgICBwYXJhbT1TY2FuQmFtUGFyYW0od2hpY2g9Z2VuZXJlZ2lvbikpCm15LnJlYWRzCmBgYAoKSXQgaXMgcG9zc2libGUgdG8gdHdlYWsgdGhlIGZ1bmN0aW9uIHRvIHJldHJpZXZlIG90aGVyIHBvdGVudGlhbGx5LXVzZWZ1bCBpbmZvcm1hdGlvbiBmcm9tIHRoZSBiYW0gZmlsZSwgc3VjaCBhcyB0aGUgbWFwcGluZyBxdWFsaXR5IGFuZCBmbGFnLgoKYGBge3J9Cm15LnJlYWRzIDwtIHJlYWRHQWxpZ25tZW50cyhmaWxlPSJkYXRhL2JhbXNfc21hbGwvL01DTDEuREcuc21hbGwuYmFtIiwKICAgICAgICAgICAgICAgICAgICAgICBwYXJhbT1TY2FuQmFtUGFyYW0od2hpY2g9Z2VuZXJlZ2lvbiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgd2hhdD1jKCJzZXEiLCJtYXBxIiwiZmxhZyIpKSkKbXkucmVhZHMKYGBgCgpUaGUgZmxhZyBjYW4gcmVwcmVzZW50IHVzZWZ1bCBRQyBpbmZvcm1hdGlvbi4gZS5nLgoKICArIFJlYWQgaXMgdW5tYXBwZWQKICArIFJlYWQgaXMgcGFpcmVkIC8gdW5wYWlyZWQKICArIFJlYWQgZmFpbGVkIFFDCiAgKyBSZWFkIGlzIGEgUENSIGR1cGxpY2F0ZSAoc2VlIGxhdGVyKQoKVGhlIGNvbWJpbmF0aW9uIG9mIGFueSBvZiB0aGVzZSBwcm9wZXJ0aWVzIGlzIHVzZWQgdG8gZGVyaXZlIGEgbnVtZXJpYyB2YWx1ZSwgYXMgaWxsdXN0cmF0ZWQgaW4gdGhpcyB1c2VmdWwgW3Jlc291cmNlXShodHRwczovL2Jyb2FkaW5zdGl0dXRlLmdpdGh1Yi5pby9waWNhcmQvZXhwbGFpbi1mbGFncy5odG1sKQoKUGFydGljdWxhciBhdHRyaWJ1dGVzIG9mIHRoZSByZWFkcyBjYW4gYmUgZXh0cmFjdGVkIGFuZCB2aXN1YWxpc2VkCgpgYGB7cn0KaGlzdChtY29scyhteS5yZWFkcykkbWFwcSwgbWFpbj0iIiwgeGxhYj0iTUFQUSIpCmBgYAoKSG93ZXZlciwgdGhlcmUgYXJlIG1vcmUtc29waGlzdGljYXRlZCB2aXN1YWxpc2F0aW9uIG9wdGlvbnMgZm9yIGFsaWduZWQgcmVhZHMgYW5kIHJhbmdlIGRhdGEuIFdlIHdpbGwgdXNlIHRoZSBgZ2diaW9gIHBhY2thZ2UsIHdoaWNoIGZpcnN0IHJlcXVpcmVzIHNvbWUgZGlzY3Vzc2lvbiBvZiB0aGUgYGdncGxvdDJgIHBsb3R0aW5nIHBhY2thZ2UuCgoKIyMgQnJpZWYgSW50cm9kdWN0aW9uIHRvIGdncGxvdDIKClRoZSBbYGdncGxvdDJgXShodHRwOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLykgcGFja2FnZSBoYXMgZW1lcmdlZCBhcyBhbiBhdHRyYWN0aXZlIGFsdGVybmF0aXZlIHRvIHRoZSB0cmFkaXRpb25hbCBwbG90cyBwcm92aWRlZCBieSBiYXNlIFIuIEEgZnVsbCBvdmVydmlldyBvZiBhbGwgY2FwYWJpbGl0aWVzIG9mIHRoZSBwYWNrYWdlIGlzIGF2YWlsYWJsZSBmcm9tIHRoZSBbY2hlYXRzaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vd3AtY29udGVudC91cGxvYWRzLzIwMTUvMDMvZ2dwbG90Mi1jaGVhdHNoZWV0LnBkZikuCgpBIHNpbXBsZSBzY2F0dGVyIHBsb3QsIGVxdWl2YWxlbnQgdG8gYHBsb3RTbWVhcmAgZnJvbSBiZWZvcmUsIGNhbiBiZSBnZW5lcmF0ZWQgYXMgZm9sbG93czotCgpgYGB7cixmaWcud2lkdGg9MTIsZmlnLmhlaWdodD01fQpsaWJyYXJ5KGdncGxvdDIpCmdncGxvdChyZXN1bHRzLCBhZXMoeCA9IGxvZ0NQTSwgeT1sb2dGQykpICsgZ2VvbV9wb2ludCgpIAoKYGBgCgpJbiBicmllZjotCgotIGByZXN1bHRzYCBpcyBvdXIgZGF0YSBmcmFtZSBjb250YWluaW5nIHRoZSB2YXJpYWJsZXMgd2Ugd2lzaCB0byBwbG90Ci0gYGFlc2AgY3JlYXRlcyBhIG1wcGluZyBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMgaW4gb3VyIGRhdGEgZnJhbWUgdG8gdGhlICphZXMqdGhldGljIHByb3BydGllcyBvZiB0aGUgcGxvdAogICAgKyB0aGUgeC1heGlzIGlzIG1hcHBlZCB0byBgbG9nQ1BNYCwgeS1heGlzIGlzIG1hcHBlZCB0byBgbG9nRkNgCi0gYGdlb21fcG9pbnRgIHNwZWNpZmllcyB0aGUgcGFydGljdWxhciB0eXBlIG9mIHBsb3Qgd2Ugd2FudCAoaW4gdGhpcyBjYXNlIGEgc2NhdHRlciBwbG90KQogICAgKyBzZWUgW3RoZSBjaGVhdHNoZWV0XShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS93cC1jb250ZW50L3VwbG9hZHMvMjAxNS8wMy9nZ3Bsb3QyLWNoZWF0c2hlZXQucGRmKSBmb3Igb3RoZXIgcGxvdCB0eXBlcwoKVGhlIHJlYWwgYWR2YW50YWdlIG9mIGBnZ3Bsb3QyYCBpcyB0aGUgYWJpbGl0eSB0byBjaGFuZ2UgdGhlIGFwcGVhcmFuY2Ugb2Ygb3VyIHBsb3QgYnkgbWFwcGluZyBvdGhlciB2YXJpYWJsZXMgdG8gYXNwZWN0cyBvZiB0aGUgcGxvdC4gRm9yIGV4YW1wbGUsIHdlIGNvdWxkIGNvbG91ciB0aGUgcG9pbnRzIGJhc2VkIG9uIGEgcC12YWx1ZSBjdXQtb2ZmLiBUaGUgY29sb3VycyBhcmUgYXV0b21hdGljYWxseSBjaG9zZW4gYnkgYGdncGxvdDJgLCBidXQgd2UgY2FuIHNwZWNpZml5IHBhcnRpY3VsYXIgdmFsdWVzLgoKYGBge3IsZmlnLndpZHRoPTEyLGZpZy5oZWlnaHQ9NX0KZ2dwbG90KHJlc3VsdHMsIGFlcyh4ID0gbG9nQ1BNLCB5PWxvZ0ZDLCBmaWxsPUZEUiA8IDAuMDUpKSArIAogICAgZ2VvbV9wb2ludChwY2g9MjEpCgpnZ3Bsb3QocmVzdWx0cywgYWVzKHggPSBsb2dDUE0sIHk9bG9nRkMsIGZpbGw9RkRSIDwgMC4wNSkpICsgCiAgICBnZW9tX3BvaW50KGFscGhhPTAuNCwgcGNoPTIxKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoInllbGxvdyIsInJlZCIpKQpgYGAKClRoZSB2b2xjYW5vIHBsb3QgY2FuIGJlIGNvbnN0cnVjdGVkIGluIGEgc2ltaWxhciBtYW5uZXIKCmBgYHtyLGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD01fQpnZ3Bsb3QocmVzdWx0cywgYWVzKHggPSBsb2dGQywgeT0tbG9nMTAoRkRSKSkpICsgCiAgICBnZW9tX3BvaW50KGFlcyhmaWxsPUZEUiA8IDAuMDUpLCBwY2g9MjEsIHNpemU9MikKYGBgCgoKIyMgQ29tcG9zaW5nIHBsb3RzIHdpdGggZ2diaW8KCldlIHdpbGwgbm93IHRha2UgYSBicmllZiBsb29rIGF0IG9uZSBvZiB0aGUgdmlzdWFsaXNhdGlvbiBwYWNrYWdlcyBpbiBCaW9jb25kdWN0b3IgdGhhdCB0YWtlcyBhZHZhbnRhZ2UKb2YgdGhlIEdlbm9taWNSYW5nZXMgYW5kIEdlbm9taWNGZWF0dXJlcyBvYmplY3QtdHlwZXMuIEluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHNob3cgYSB3b3JrZWQKZXhhbXBsZSBvZiBob3cgdG8gY29tYmluZSBzZXZlcmFsIHR5cGVzIG9mIGdlbm9taWMgZGF0YSBvbiB0aGUgc2FtZSBwbG90LiBUaGUgZG9jdW1lbnRhdGlvbiBmb3IKZ2diaW8gaXMgdmVyeSBleHRlbnNpdmUgYW5kIGNvbnRhaW5zIGxvdHMgb2YgZXhhbXBsZXMuCgpodHRwOi8vd3d3LnRlbmdmZWkubmFtZS9nZ2Jpby9kb2NzLwoKVGhlIGBHdml6YCBwYWNrYWdlIGlzIGFub3RoZXIgQmlvY29uZHVjdG9yIHBhY2thZ2UgdGhhdCBzcGVjaWFsaXNpbmcgaW4gZ2Vub21pYyB2aXN1YWxpc2F0aW9ucywgYnV0IHdlCndpbGwgbm90IGV4cGxvcmUgdGhpcyBwYWNrYWdlIGluIHRoZSBjb3Vyc2UuCgpUaGUgTWFuaGF0dGFuIHBsb3QgaXMgYSBjb21tb24gd2F5IG9mIHZpc3VhbGlzaW5nIGdlbm9tZS13aWRlIHJlc3VsdHMsIGVzcGVjaWFsbHkgd2hlbiBvbmUgaXMgY29uY2VybmVkIHdpdGggdGhlIHJlc3VsdHMgb2YgYSBHV0FTIHN0dWR5IGFuZCBpZGVudGlmeWluZyBzdHJvbmdseS1hc3NvY2lhdGVkIGhpdHMuIAoKVGhlIHByb2ZpbGUgaXMgc3VwcG9zZWQgdG8gcmVzZW1ibGUgdGhlIE1hbmhhdHRhbiBza3lsaW5lIHdpdGggcGFydGljdWxhciBza3lzY3JhcGVycyB0b3dlcmluZyBhYm91dCB0aGUgbG93ZXIgbGV2ZWwgYnVpbGRpbmdzLgoKIVtdKGh0dHBzOi8vdXBsb2FkLndpa2ltZWRpYS5vcmcvd2lraXBlZGlhL2NvbW1vbnMvMS8xMi9NYW5oYXR0YW5fUGxvdC5wbmcpCgpUaGlzIHR5cGUgb2YgcGxvdCBpcyBpbXBsZW1lbnRlZCBhcyB0aGUgYHBsb3RHcmFuZExpbmVhcmAgZnVuY3Rpb24uIFdlIGhhdmUgdG8gc3VwcGx5IGEgdmFsdWUgdG8gZGlzcGxheSBvbiB0aGUgeS1heGlzIHVzaW5nIHRoZSBgYWVzYCBmdW5jdGlvbiwKd2hpY2ggaXMgaW5oZXJpdGVkIGZyb20gZ2dwbG90Mi4gVGhlIHBvc2l0aW9uaW5nIG9mIHBvaW50cyBvbiB0aGUgeC1heGlzIGlzIGhhbmRsZWQgYXV0b21hdGljYWxseSBieQpnZ2JpbywgdXNpbmcgdGhlIHJhbmdlcyBpbmZvcm1hdGlvbiB0byBnZXQgdGhlIGdlbm9taWMgY29vcmRpbmF0ZXMgb2YgdGhlIHJhbmdlcyBvZiBpbnRlcmVzdC4KClRvIHN0b3AgdGhlIHBsb3RzIGZyb20gYmVpbmcgdG9vIGNsdXR0ZXJlZCB3ZSB3aWxsIGNvbnNpZGVyIHRoZSB0b3AgMjAwIGdlbmVzIG9ubHkuCgpgYGB7cixmaWcud2lkdGg9MTIsZmlnLmhlaWdodD01fQpsaWJyYXJ5KGdnYmlvKQp0b3AyMDAgPC0gc2lnUmVnaW9uc1tvcmRlcihzaWdSZWdpb25zJExSLGRlY3JlYXNpbmcgPSBUUlVFKVsxOjIwMF1dCgpwbG90R3JhbmRMaW5lYXIodG9wMjAwICwgYWVzKHkgPSBsb2dGQykpCgpgYGAKCmBnZ2Jpb2AgaGFzIGFsdGVybmF0ZWQgdGhlIGNvbG91cnMgb2YgdGhlIGNocm9tb3NvbWVzLiBIb3dldmVyLCBhbiBhcHBlYWxpbmcgZmVhdHVyZSBvZiBgZ2dwbG90MmAgaXMgdGhlIGFiaWxpdHkgdG8gbWFwIHByb3BlcnRpZXMgb2YgeW91ciBwbG90IHRvIHZhcmlhYmxlcyBwcmVzZW50IGluIHlvdXIgZGF0YS4gRm9yIGV4YW1wbGUsIHdlIGNvdWxkIGNyZWF0ZSBhIHZhcmlhYmxlIHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gdXAtIGFuZCBkb3duLXJlZ3VsYXRlZCBnZW5lcy4gVGhlIHZhcmlhYmxlcyB1c2VkIGZvciBhZXN0aGV0aWMgbWFwcGluZyBtdXN0IGJlIHByZXNlbnQgaW4gdGhlIGBtY29sc2Agc2VjdGlvbiBvZiB5b3VyIHJhbmdlcyBvYmplY3QuCgpgYGB7cixmaWcud2lkdGg9MTIsZmlnLmhlaWdodD01fQptY29scyh0b3AyMDApJFVwUmVndWxhdGVkIDwtIG1jb2xzKHRvcDIwMCkkbG9nRkMgPiAwCgpwbG90R3JhbmRMaW5lYXIodG9wMjAwLCBhZXMoeSA9IGxvZ0ZDLCBjb2wgPSBVcFJlZ3VsYXRlZCkpCmBgYAoKYHBsb3RHcmFuZExpbmVhcmAgaXMgYSBzcGVjaWFsIGZ1bmN0aW9uIGluIGBnZ2Jpb2Agd2l0aCBwcmVzZXQgb3B0aW9ucyBmb3IgdGhlIG1hbmhhdHRhbiBzdHlsZSBvZiBwbG90LiBNb3JlIG9mdGVuLCB1c2VycyB3aWxsIGNhbGwgdGhlIGBhdXRvcGxvdGAgZnVuY3Rpb24gYW5kIGBnZ2Jpb2Agd2lsbCBjaG9vc2UgdGhlIG1vc3QgYXBwcm9wcmlhdGUgbGF5b3V0LiBPbmUgc3VjaCBsYXlvdXQgaXMgdGhlICprYXJ5b2dyYW0qLiAKCmBgYHtyLGZpZy53aWR0aD0xMixmaWcuaGVpZ2h0PTV9CgphdXRvcGxvdCh0b3AyMDAsbGF5b3V0PSJrYXJ5b2dyYW0iLGFlcyhjb2xvcj1VcFJlZ3VsYXRlZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbD1VcFJlZ3VsYXRlZCkpCgpgYGAKCgoKYGdnYmlvYCBpcyBhbHNvIGFibGUgdG8gcGxvdCB0aGUgc3RydWN0dXJlIG9mIGdlbmVzIGFjY29yZGluZyB0byBhIHBhcnRpY3VsYXIgbW9kZWwgcmVwcmVzZW50ZWQgYnkgYSBgR2Vub21pY0ZlYXR1cmVzYCBvYmplY3QsIHN1Y2ggYXMgdGhlIG9iamVjdCB3ZSBjcmVhdGVkIGVhcmxpZXIgd2l0aCB0aGUgZXhvbiBjb29yZGluYXRlcyBmb3IgZWFjaCBnZW5lIGluIHRoZSBtbTEwIGdlbm9tZS4KCgpgYGB7cn0KYXV0b3Bsb3QodHgsIHdoaWNoPWV4b1tbIjExMDMwOCJdXSkKYGBgCgpXZSBjYW4gZXZlbiBwbG90IHRoZSBsb2NhdGlvbiBvZiBzZXF1ZW5jaW5nIHJlYWRzIGlmIHRoZXkgaGF2ZSBiZWVuIGltcG9ydGVkIHVzaW5nIHJlYWRHQWxpZ25tZW50cyBmdW5jdGlvbiAob3Igc2ltaWxhcikuCgpgYGB7cn0KbXlyZWcgPC0gZmxhbmsocmVkdWNlKGV4b1tbIjExMDMwOCJdXSksIDEwMDAsIGJvdGggPSBUKQoKYmFtIDwtIHJlYWRHYXBwZWRSZWFkcyhmaWxlPSJkYXRhL2JhbXNfc21hbGwvTUNMMS5ERy5zbWFsbC5iYW0iLAogICAgICAgICAgICAgICAgICAgICAgIHBhcmFtPVNjYW5CYW1QYXJhbSh3aGljaD1teXJlZyksdXNlLm5hbWVzID0gVFJVRSkKCmF1dG9wbG90KGJhbSwgZ2VvbSA9ICJyZWN0IikgKyAKICAgIHhsaW0oR1JhbmdlcygiY2hyMTUiLCBJUmFuZ2VzKDEwMTcwNzAwMCwgMTAxNzEzMDAwKSkpCmBgYAoKTGlrZSBnZ3Bsb3QyLCBnZ2JpbyBwbG90cyBjYW4gYmUgc2F2ZWQgYXMgb2JqZWN0cyB0aGF0IGNhbiBsYXRlciBiZSBtb2RpZmllZCwgb3IgY29tYmluZWQgdG9nZXRoZXIgdG8KZm9ybSBtb3JlIGNvbXBsaWNhdGVkIHBsb3RzLiBJZiBzYXZlZCBpbiB0aGlzIHdheSwgdGhlIHBsb3Qgd2lsbCBvbmx5IGJlIGRpc3BsYXllZCBvbiBhIHBsb3R0aW5nIGRldmljZQp3aGVuIHdlIHF1ZXJ5IHRoZSBvYmplY3QuIFRoaXMgc3RyYXRlZ3kgaXMgdXNlZnVsIHdoZW4gd2Ugd2FudCB0byBhZGQgYSBjb21tb24gZWxlbWVudCAoc3VjaCBhcwphbiBpZGVvZ3JhbSkgdG8gYSBwbG90IGNvbXBvc2l0aW9uIGFuZCBkb27igJl0IHdhbnQgdG8gcmVwZWF0IHRoZSBjb2RlIHRvIGdlbmVyYXRlIHRoZSBwbG90IGV2ZXJ5IHRpbWUuCgpgYGB7cn0KI2lkUGxvdCA8LSBwbG90SWRlb2dyYW0oZ2Vub21lID0gIm1tMTAiLHN1YmNociA9ICJjaHIxIikKI2lkUGxvdApnZW5lTW9kIDwtIGF1dG9wbG90KHR4LCB3aGljaCA9IG15cmVnKQpyZWFkcy5NQ0wxLkRHIDwtIGF1dG9wbG90KGJhbSwgc3RhdCA9ICJjb3ZlcmFnZSIpICArIAogICAgeGxpbShHUmFuZ2VzKCJjaHIxNSIsIElSYW5nZXMoMTAxNzA3MDAwLCAxMDE3MTMwMDApKSkgKwogICAgbGFicyh0aXRsZT0iTUNMMS5ERyIpCnRyYWNrcyhtbTEwPWdlbmVNb2QsIE1DTDEuREc9cmVhZHMuTUNMMS5ERyApIApgYGAKCj4gIyMgQ2hhbGxlbmdlIHsuY2hhbGxlbmdlfQo+Cj4gQ3JlYXRlIHRyYWNrcyB0byBjb21wYXJlIHRoZSBjb3ZlcmFnZSBvZiB0aGUgZ2VuZSBLcnQ1IGZvciB0aGUgc2FtcGxlcyBNQ0wxLkRHLCBNQ0wxLkRILCBNQ0wxLkxBIGFuZCBNQ0wxLkxCCj4KCmBgYHtyLGVjaG89RkFMU0UsZmlnLmhlaWdodD01LGZpZy53aWR0aD0xMH0KCgpgYGAKCg==