Creative Commons License

This is part of the online course Proteomics Data Analysis (PDA)

1 Background

Researchers have assessed the proteome in different regions of the heart for 3 patients (identifiers 3, 4, and 8). For each patient they sampled the left atrium (LA), right atrium (RA), left ventricle (LV) and the right ventricle (RV). The data are a small subset of the public dataset PXD006675 on PRIDE.

Suppose that researchers are mainly interested in comparing the ventricular to the atrial proteome. Particularly, they would like to compare the left atrium to the left ventricle, the right atrium to the right ventricle, the average ventricular vs atrial proteome and if ventricular vs atrial proteome shifts differ between left and right heart region.

Representation of the heart

Representation of the heart

2 Data

We first import the peptides.txt file. This is the file that contains your peptide-level intensities. For a MaxQuant search [6], this peptides.txt file can be found by default in the “path_to_raw_files/combined/txt/” folder from the MaxQuant output, with “path_to_raw_files” the folder where raw files were saved. In this tutorial, we will use a MaxQuant peptides file from MaxQuant that can be found in the data tree of the SGA2020 github repository https://github.com/statOmics/SGA2020/tree/data/quantification/heart .

2.1 Data import

Click to see background and code

To import the data we use the QFeatures package.

We generate the object peptideRawFile with the path to the peptideRaws.txt file. Using the grepEcols function, we find the columns that contain the expression data of the peptideRaws in the peptideRaws.txt file.

library(tidyverse)
library(limma)
library(QFeatures)
library(msqrob2)
library(plotly)

peptidesFile <- "https://raw.githubusercontent.com/statOmics/PDA21/data/quantification/heart/peptides.txt"

ecols <- grep("Intensity\\.", names(read.delim(peptidesFile)))

pe <- readQFeatures(
  table = peptidesFile,
  fnames = 1,
  ecol = ecols,
  name = "peptideRaw", sep="\t")

pe
## An instance of class QFeatures containing 1 assays:
##  [1] peptideRaw: SummarizedExperiment with 31319 rows and 12 columns
pe[["peptideRaw"]]
## class: SummarizedExperiment 
## dim: 31319 12 
## metadata(0):
## assays(1): ''
## rownames(31319): AAAAAAAAAK AAAAAAAAEQQSSNGPVK ... YYTPVPCESATAK
##   YYTYLIMNK
## rowData names(91): Sequence N.term.cleavage.window ...
##   Oxidation..M..site.IDs MS.MS.Count
## colnames(12): Intensity.LA3 Intensity.LA4 ... Intensity.RV4
##   Intensity.RV8
## colData names(0):

We will make use from data wrangling functionalities from the tidyverse package. The %>% operator allows us to pipe the output of one function to the next function.

colData(pe)$location <- substr(
  colnames(pe[["peptideRaw"]]),
  11,
  11) %>%
  unlist %>%  
  as.factor

colData(pe)$tissue <- substr(
    colnames(pe[["peptideRaw"]]),
    12,
    12) %>%
    unlist %>%  
    as.factor

colData(pe)$patient <- substr(
  colnames(pe[["peptideRaw"]]),
  13,
  13) %>%
  unlist %>%  
  as.factor

We calculate how many non zero intensities we have per peptide and this will be useful for filtering.

rowData(pe[["peptideRaw"]])$nNonZero <- rowSums(assay(pe[["peptideRaw"]]) > 0)

Peptides with zero intensities are missing peptides and should be represent with a NA value rather than 0.

pe <- zeroIsNA(pe, "peptideRaw") # convert 0 to NA

2.2 Data exploration

Click to see background and code

63% of all peptide intensities are missing and for some peptides we do not even measure a signal in any sample. The missingness is similar across samples.

3 Preprocessing

Click to see background and code

This section preforms standard preprocessing for the peptide data. This include log transformation, filtering and summarisation of the data.

3.1 Log transform the data

pe <- logTransform(pe, base = 2, i = "peptideRaw", name = "peptideLog")
limma::plotDensities(assay(pe[["peptideLog"]]))

3.2 Filtering

Click to see background and code

3.2.1 Handling overlapping protein groups

In our approach a peptide can map to multiple proteins, as long as there is none of these proteins present in a smaller subgroup.

pe <- filterFeatures(pe, ~ Proteins %in% smallestUniqueGroups(rowData(pe[["peptideLog"]])$Proteins))

3.2.2 Remove reverse sequences (decoys) and contaminants

We now remove the contaminants, peptides that map to decoy sequences, and proteins which were only identified by peptides with modifications.

First look to the names of the variables for the peptide features

pe[["peptideLog"]] %>%
  rowData %>%
  names
##  [1] "Sequence"                "N.term.cleavage.window" 
##  [3] "C.term.cleavage.window"  "Amino.acid.before"      
##  [5] "First.amino.acid"        "Second.amino.acid"      
##  [7] "Second.last.amino.acid"  "Last.amino.acid"        
##  [9] "Amino.acid.after"        "A.Count"                
## [11] "R.Count"                 "N.Count"                
## [13] "D.Count"                 "C.Count"                
## [15] "Q.Count"                 "E.Count"                
## [17] "G.Count"                 "H.Count"                
## [19] "I.Count"                 "L.Count"                
## [21] "K.Count"                 "M.Count"                
## [23] "F.Count"                 "P.Count"                
## [25] "S.Count"                 "T.Count"                
## [27] "W.Count"                 "Y.Count"                
## [29] "V.Count"                 "U.Count"                
## [31] "O.Count"                 "Length"                 
## [33] "Missed.cleavages"        "Mass"                   
## [35] "Proteins"                "Leading.razor.protein"  
## [37] "Start.position"          "End.position"           
## [39] "Gene.names"              "Protein.names"          
## [41] "Unique..Groups."         "Unique..Proteins."      
## [43] "Charges"                 "PEP"                    
## [45] "Score"                   "Identification.type.LA3"
## [47] "Identification.type.LA4" "Identification.type.LA8"
## [49] "Identification.type.LV3" "Identification.type.LV4"
## [51] "Identification.type.LV8" "Identification.type.RA3"
## [53] "Identification.type.RA4" "Identification.type.RA8"
## [55] "Identification.type.RV3" "Identification.type.RV4"
## [57] "Identification.type.RV8" "Fraction.Average"       
## [59] "Fraction.Std..Dev."      "Fraction.1"             
## [61] "Fraction.2"              "Fraction.3"             
## [63] "Fraction.4"              "Fraction.5"             
## [65] "Fraction.6"              "Fraction.7"             
## [67] "Fraction.8"              "Fraction.100"           
## [69] "Experiment.LA3"          "Experiment.LA4"         
## [71] "Experiment.LA8"          "Experiment.LV3"         
## [73] "Experiment.LV4"          "Experiment.LV8"         
## [75] "Experiment.RA3"          "Experiment.RA4"         
## [77] "Experiment.RA8"          "Experiment.RV3"         
## [79] "Experiment.RV4"          "Experiment.RV8"         
## [81] "Intensity"               "Reverse"                
## [83] "Potential.contaminant"   "id"                     
## [85] "Protein.group.IDs"       "Mod..peptide.IDs"       
## [87] "Evidence.IDs"            "MS.MS.IDs"              
## [89] "Best.MS.MS"              "Oxidation..M..site.IDs" 
## [91] "MS.MS.Count"             "nNonZero"

No information on decoys.

pe <- filterFeatures(pe,~ Potential.contaminant != "+")

3.2.3 Drop peptides that were only identified in one sample

We keep peptides that were observed at least twice.

pe <- filterFeatures(pe,~nNonZero >= 2)
nrow(pe[["peptideLog"]])
## [1] 17432

We keep 17432 peptides after filtering.

3.3 Normalize the data

Click to see background and code

pe <- normalize(pe, 
                i = "peptideLog", 
                name = "peptideNorm", 
                method = "center.median")

3.3.1 Explore normalized data

Click to see background and code

After normalisation the density curves for all samples are comparable.

limma::plotDensities(assay(pe[["peptideNorm"]]))

This is more clearly seen is a boxplot.

boxplot(assay(pe[["peptideNorm"]]), col = palette()[-1],
       main = "Peptide distribtutions after normalisation", ylab = "intensity")

We can visualize our data using a Multi Dimensional Scaling plot, eg. as provided by the limma package.

limma::plotMDS(assay(pe[["peptideNorm"]]),
  col = colData(pe)$location:colData(pe)$tissue %>%
    as.numeric,
  labels = colData(pe) %>%
    rownames %>%  
    substr(start = 11, stop = 13)
  )

The first axis in the plot is showing the leading log fold changes (differences on the log scale) between the samples.

3.4 Summarization to protein level

Click to see background and code

We use robust summarization in aggregateFeatures. This is the default workflow of aggregateFeatures so you do not have to specifiy the argument fun. However, because we compare methods we have included the fun argument to show the summarization method explicitely.

pe <- aggregateFeatures(pe,
 i = "peptideNorm",
 fcol = "Proteins",
 na.rm = TRUE,
 name = "proteinRobust",
 fun = MsCoreUtils::robustSummary)
## Your quantitative and row data contain missing values. Please read the
## relevant section(s) in the aggregateFeatures manual page regarding the
## effects of missing values on data aggregation.
plotMDS(assay(pe[["proteinRobust"]]),
  col = colData(pe)$location:colData(pe)$tissue %>%
    as.numeric,
  labels = colData(pe) %>%
    rownames %>%  
    substr(start = 11, stop = 13)
)

4 Data Analysis

4.1 Estimation

We model the protein level expression values using msqrob. By default msqrob2 estimates the model parameters using robust regression.

pe <- msqrob(
  object = pe,
  i = "proteinRobust",
  formula = ~ location*tissue + patient)

4.2 Inference

Explore Design

library(ExploreModelMatrix)
VisualizeDesign(colData(pe),~ location*tissue + patient)$plotlist
## $`location = L`

## 
## $`location = R`

design <- model.matrix(~location*tissue + patient, data = colData(pe))
L <- makeContrast(
  c(
    "tissueV = 0",
    "tissueV + locationR:tissueV = 0",
    "tissueV + 0.5*locationR:tissueV = 0","locationR:tissueV = 0"),
  parameterNames = colnames(design)
  )


pe <- hypothesisTest(object = pe, i = "proteinRobust", contrast = L, overwrite=TRUE)

4.3 Evaluate results contrast \(\log_2 FC_{V-A}^L\)

4.3.1 Volcano-plot

volcanoLeft <- ggplot(rowData(pe[["proteinRobust"]])$"tissueV",
                 aes(x = logFC, y = -log10(pval), color = adjPval < 0.05)) +
 geom_point(cex = 2.5) +
 scale_color_manual(values = alpha(c("black", "red"), 0.5)) + theme_minimal()
volcanoLeft

4.3.2 Heatmap

We first select the names of the proteins that were declared signficant.

sigNamesLeft <- rowData(pe[["proteinRobust"]])$tissueV %>%
 rownames_to_column("proteinRobust") %>%
 filter(adjPval<0.05) %>%
 pull(proteinRobust)
heatmap(assay(pe[["proteinRobust"]])[sigNamesLeft, ])

There are 199 proteins significantly differentially expressed at the 5% FDR level.

rowData(pe[["proteinRobust"]])$tissueV %>%
  cbind(.,rowData(pe[["proteinRobust"]])$Protein.names) %>%
  na.exclude %>%
  filter(adjPval<0.05) %>%
  arrange(pval)  %>%
  head(10) %>%
  knitr::kable(.) 
logFC se df t pval adjPval rowData(pe[[“proteinRobust”]])$Protein.names
P08590 7.967951 0.4374873 9.367147 18.212987 0.00e+00 0.0000254 Myosin light chain 3
P12883 4.669883 0.3716628 9.181668 12.564838 4.00e-07 0.0004379 Myosin-7
P10916 6.926109 0.5291456 7.367147 13.089231 2.30e-06 0.0015506 Myosin regulatory light chain 2, ventricular/cardiac muscle isoform
Q6UWY5 -3.253643 0.3828898 9.113260 -8.497596 1.26e-05 0.0046705 Olfactomedin-like protein 1
O75368 -2.271594 0.2715292 9.289260 -8.365932 1.27e-05 0.0046705 SH3 domain-binding glutamic acid-rich-like protein
P46821 -2.166880 0.2632626 9.367147 -8.230871 1.38e-05 0.0046705 Microtubule-associated protein 1B;MAP1B heavy chain;MAP1 light chain LC1
O95865 -2.173219 0.2826203 9.367147 -7.689536 2.42e-05 0.0065478 N(G),N(G)-dimethylarginine dimethylaminohydrolase 2
Q8N474 -3.309023 0.3804443 7.897481 -8.697787 2.58e-05 0.0065478 Secreted frizzled-related protein 1
Q9ULL5-3 -3.416540 0.3998973 7.846127 -8.543546 3.05e-05 0.0068882 Proline-rich protein 12
P14854 2.401913 0.3057866 8.431469 7.854864 3.71e-05 0.0069621 Cytochrome c oxidase subunit 6B1

4.4 Evaluate results contrast \(\log_2 FC_{V-A}^R\)

4.4.1 Volcano-plot

volcanoRight <- ggplot(rowData(pe[["proteinRobust"]])$"tissueV + locationR:tissueV",
                 aes(x = logFC, y = -log10(pval), color = adjPval < 0.05)) +
 geom_point(cex = 2.5) +
 scale_color_manual(values = alpha(c("black", "red"), 0.5)) + theme_minimal()
volcanoRight

4.4.2 Heatmap

We first select the names of the proteins that were declared signficant.

sigNamesRight <- rowData(pe[["proteinRobust"]])$"tissueV + locationR:tissueV" %>%
 rownames_to_column("proteinRobust") %>%
 filter(adjPval<0.05) %>%
 pull(proteinRobust)
heatmap(assay(pe[["proteinRobust"]])[sigNamesRight, ])

There are 87 proteins significantly differentially expressed at the 5% FDR level.

rowData(pe[["proteinRobust"]])$"tissueV + locationR:tissueV"  %>%
  cbind(.,rowData(pe[["proteinRobust"]])$Protein.names) %>%
  na.exclude %>%
  filter(adjPval<0.05) %>%
  arrange(pval) %>%
  head(10) %>%
  knitr::kable(.)
logFC se df t pval adjPval rowData(pe[[“proteinRobust”]])$Protein.names
P08590 5.503838 0.4374873 9.367147 12.580565 4.00e-07 0.0007167 Myosin light chain 3
P06858 3.354674 0.3544630 9.249267 9.464102 4.60e-06 0.0047205 Lipoprotein lipase
Q9ULD0 -3.575906 0.3737222 7.367147 -9.568355 2.05e-05 0.0138839 2-oxoglutarate dehydrogenase-like, mitochondrial
P35442 -3.246460 0.4065828 8.367147 -7.984746 3.43e-05 0.0146265 Thrombospondin-2
P02776 -2.818363 0.3421511 7.975973 -8.237189 3.60e-05 0.0146265 Platelet factor 4;Platelet factor 4, short form
P21810 -2.924783 0.4081159 8.756796 -7.166550 6.06e-05 0.0197128 Biglycan
O75368 -1.819326 0.2697137 9.289260 -6.745396 7.23e-05 0.0197128 SH3 domain-binding glutamic acid-rich-like protein
A6NMZ7 -3.070848 0.4709254 9.367147 -6.520879 9.07e-05 0.0197128 Collagen alpha-6(VI) chain
P54652 -2.726681 0.4117048 9.078544 -6.622904 9.29e-05 0.0197128 Heat shock-related 70 kDa protein 2
Q6UWY5 -2.456874 0.3739352 9.113260 -6.570320 9.70e-05 0.0197128 Olfactomedin-like protein 1

4.5 Evaluate results average contrast \(\log_2 FC_{V-A}\)

4.5.1 Volcano-plot

volcanoAvg <- ggplot(rowData(pe[["proteinRobust"]])$"tissueV + 0.5 * locationR:tissueV",
                 aes(x = logFC, y = -log10(pval), color = adjPval < 0.05)) +
 geom_point(cex = 2.5) +
 scale_color_manual(values = alpha(c("black", "red"), 0.5)) + theme_minimal()
volcanoAvg

4.5.2 Heatmap

We first select the names of the proteins that were declared signficant.

sigNamesAvg <- rowData(pe[["proteinRobust"]])$"tissueV + 0.5 * locationR:tissueV" %>%
 rownames_to_column("proteinRobust") %>%
 filter(adjPval<0.05) %>%
 pull(proteinRobust)
heatmap(assay(pe[["proteinRobust"]])[sigNamesAvg, ])

There are 449 proteins significantly differentially expressed at the 5% FDR level.

rowData(pe[["proteinRobust"]])$"tissueV + 0.5 * locationR:tissueV" %>%
  cbind(.,rowData(pe[["proteinRobust"]])$Protein.names) %>%
  na.exclude %>%
  filter(adjPval<0.05) %>%
  arrange(pval) %>%
  head(10) %>%
  knitr::kable(.) 
logFC se df t pval adjPval rowData(pe[[“proteinRobust”]])$Protein.names
P08590 6.735894 0.3093503 9.367147 21.77433 0.0e+00 0.0000049 Myosin light chain 3
P12883 3.319491 0.2606314 9.181668 12.73634 4.0e-07 0.0003891 Myosin-7
O75368 -2.045460 0.1913593 9.289260 -10.68911 1.6e-06 0.0007082 SH3 domain-binding glutamic acid-rich-like protein
Q6UWY5 -2.855258 0.2675968 9.113260 -10.67000 1.9e-06 0.0007082 Olfactomedin-like protein 1
P10916 4.923642 0.3661150 7.367147 13.44835 1.9e-06 0.0007082 Myosin regulatory light chain 2, ventricular/cardiac muscle isoform
P46821 -1.886709 0.1861547 9.367147 -10.13517 2.4e-06 0.0007082 Microtubule-associated protein 1B;MAP1B heavy chain;MAP1 light chain LC1
P06858 2.571933 0.2519480 9.249267 10.20819 2.4e-06 0.0007082 Lipoprotein lipase
P21810 -3.069662 0.2931217 8.756796 -10.47231 3.0e-06 0.0007662 Biglycan
P05546 -1.829841 0.1916993 9.271661 -9.54537 4.2e-06 0.0009134 Heparin cofactor 2
P35442 -2.773755 0.2689292 8.367147 -10.31407 4.8e-06 0.0009134 Thrombospondin-2

4.6 Interaction

4.6.1 Volcano-plot

volcanoInt <- ggplot(rowData(pe[["proteinRobust"]])$"locationR:tissueV",
                 aes(x = logFC, y = -log10(pval), color = adjPval < 0.05)) +
 geom_point(cex = 2.5) +
 scale_color_manual(values = alpha(c("black", "red"), 0.5)) + theme_minimal()
volcanoInt

4.6.2 Heatmap

There were no genes significant at the 5% FDR level. We return the top 25 genes.

sigNamesInt <- rowData(pe[["proteinRobust"]])$"locationR:tissueV" %>%
 rownames_to_column("proteinRobust") %>%
 filter(adjPval<0.05) %>%
 pull(proteinRobust)
hlp <- order((rowData(pe[["proteinRobust"]])$"locationR:tissueV")[,"adjPval"])[1:25]
heatmap(assay(pe[["proteinRobust"]])[hlp, ])

There are 0 proteins significantly differentially expressed at the 5% FDR level.

rowData(pe[["proteinRobust"]])$"locationR:tissueV" %>%
  cbind(.,rowData(pe[["proteinRobust"]])$Protein.names) %>%
  na.exclude %>%
  filter(adjPval<0.05) %>%
  head(10) %>%
  knitr::kable(.) 
logFC se df t pval adjPval rowData(pe[[“proteinRobust”]])$Protein.names
LS0tCnRpdGxlOiAiUHJvdGVvbWljcyBkYXRhIGFuYWx5c2lzOiBoZWFydCIKYXV0aG9yOiAiTGlldmVuIENsZW1lbnQiCmRhdGU6ICJzdGF0T21pY3MsIEdoZW50IFVuaXZlcnNpdHkgKGh0dHBzOi8vc3RhdG9taWNzLmdpdGh1Yi5pbykiCm91dHB1dDoKICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgICAgdGhlbWU6IGNvc21vCiAgICAgIHRvYzogdHJ1ZQogICAgICB0b2NfZmxvYXQ6IAogICAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgICBoaWdobGlnaHQ6IHRhbmdvCiAgICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgcGRmX2RvY3VtZW50OgogICAgICB0b2M6IHRydWUKICAgICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCmxpbmtjb2xvcjogYmx1ZQp1cmxjb2xvcjogYmx1ZQpjaXRlY29sb3I6IGJsdWUKLS0tCgo8YSByZWw9ImxpY2Vuc2UiIGhyZWY9Imh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1zYS80LjAvIj48aW1nIGFsdD0iQ3JlYXRpdmUgQ29tbW9ucyBMaWNlbnNlIiBzdHlsZT0iYm9yZGVyLXdpZHRoOjAiIHNyYz0iaHR0cHM6Ly9pLmNyZWF0aXZlY29tbW9ucy5vcmcvbC9ieS1zYS80LjAvODh4MzEucG5nIiAvPjwvYT4KClRoaXMgaXMgcGFydCBvZiB0aGUgb25saW5lIGNvdXJzZSBbUHJvdGVvbWljcyBEYXRhIEFuYWx5c2lzIChQREEpXShodHRwczovL3N0YXRvbWljcy5naXRodWIuaW8vUERBLykKCiMgQmFja2dyb3VuZAoKUmVzZWFyY2hlcnMgaGF2ZSBhc3Nlc3NlZCB0aGUgcHJvdGVvbWUgaW4gZGlmZmVyZW50IHJlZ2lvbnMgb2YgdGhlIGhlYXJ0IGZvciAzIHBhdGllbnRzIChpZGVudGlmaWVycyAzLCA0LCBhbmQgOCkuIEZvciBlYWNoIHBhdGllbnQgdGhleSBzYW1wbGVkIHRoZSBsZWZ0IGF0cml1bSAoTEEpLCByaWdodCBhdHJpdW0gKFJBKSwgbGVmdCB2ZW50cmljbGUgKExWKSBhbmQgdGhlIHJpZ2h0IHZlbnRyaWNsZSAoUlYpLiBUaGUgZGF0YSBhcmUgYSBzbWFsbCBzdWJzZXQgb2YgdGhlIHB1YmxpYyBkYXRhc2V0IFBYRDAwNjY3NSBvbiBQUklERS4KClN1cHBvc2UgdGhhdCByZXNlYXJjaGVycyBhcmUgbWFpbmx5IGludGVyZXN0ZWQgaW4gY29tcGFyaW5nIHRoZSB2ZW50cmljdWxhciB0byB0aGUgYXRyaWFsIHByb3Rlb21lLiBQYXJ0aWN1bGFybHksIHRoZXkgd291bGQgbGlrZSB0byBjb21wYXJlIHRoZSBsZWZ0IGF0cml1bSB0byB0aGUgbGVmdCB2ZW50cmljbGUsIHRoZSByaWdodCBhdHJpdW0gdG8gdGhlIHJpZ2h0IHZlbnRyaWNsZSwgdGhlIGF2ZXJhZ2UgdmVudHJpY3VsYXIgdnMgYXRyaWFsIHByb3Rlb21lIGFuZCBpZiB2ZW50cmljdWxhciB2cyBhdHJpYWwgcHJvdGVvbWUgc2hpZnRzIGRpZmZlciBiZXR3ZWVuIGxlZnQgYW5kIHJpZ2h0IGhlYXJ0IHJlZ2lvbi4KCmBgYHtyIHByZXNzdXJlLCBlY2hvPUZBTFNFLCBmaWcuY2FwPSJSZXByZXNlbnRhdGlvbiBvZiB0aGUgaGVhcnQiLCBvdXQud2lkdGggPSAnODAlJ30Ka25pdHI6OmluY2x1ZGVfZ3JhcGhpY3MoIi4vZmlndXJlcy9oZWFydC5wbmciKQpgYGAKCiMgRGF0YQoKV2UgZmlyc3QgaW1wb3J0IHRoZSBwZXB0aWRlcy50eHQgZmlsZS4gVGhpcyBpcyB0aGUgZmlsZSB0aGF0IGNvbnRhaW5zIHlvdXIgcGVwdGlkZS1sZXZlbCBpbnRlbnNpdGllcy4gRm9yIGEgTWF4UXVhbnQgc2VhcmNoIFs2XSwgdGhpcyBwZXB0aWRlcy50eHQgZmlsZSBjYW4gYmUgZm91bmQgYnkgZGVmYXVsdCBpbiB0aGUgInBhdGhfdG9fcmF3X2ZpbGVzL2NvbWJpbmVkL3R4dC8iIGZvbGRlciBmcm9tIHRoZSBNYXhRdWFudCBvdXRwdXQsIHdpdGggInBhdGhfdG9fcmF3X2ZpbGVzIiB0aGUgZm9sZGVyIHdoZXJlIHJhdyBmaWxlcyB3ZXJlIHNhdmVkLiBJbiB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIHVzZSBhIE1heFF1YW50IHBlcHRpZGVzIGZpbGUgZnJvbSBNYXhRdWFudCB0aGF0IGNhbiBiZSBmb3VuZCBpbiB0aGUgZGF0YSB0cmVlIG9mIHRoZSBTR0EyMDIwIGdpdGh1YiByZXBvc2l0b3J5IGh0dHBzOi8vZ2l0aHViLmNvbS9zdGF0T21pY3MvU0dBMjAyMC90cmVlL2RhdGEvcXVhbnRpZmljYXRpb24vaGVhcnQgLgoKIyMgRGF0YSBpbXBvcnQKCjxkZXRhaWxzPjxzdW1tYXJ5PiBDbGljayB0byBzZWUgYmFja2dyb3VuZCBhbmQgY29kZSA8L3N1bW1hcnk+PHA+CgpUbyBpbXBvcnQgdGhlIGRhdGEgd2UgdXNlIHRoZSBgUUZlYXR1cmVzYCBwYWNrYWdlLgoKV2UgZ2VuZXJhdGUgdGhlIG9iamVjdCBwZXB0aWRlUmF3RmlsZSB3aXRoIHRoZSBwYXRoIHRvIHRoZSBwZXB0aWRlUmF3cy50eHQgZmlsZS4KVXNpbmcgdGhlIGBncmVwRWNvbHNgIGZ1bmN0aW9uLCB3ZSBmaW5kIHRoZSBjb2x1bW5zIHRoYXQgY29udGFpbiB0aGUgZXhwcmVzc2lvbgpkYXRhIG9mIHRoZSBwZXB0aWRlUmF3cyBpbiB0aGUgcGVwdGlkZVJhd3MudHh0IGZpbGUuCgpgYGB7ciwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkobGltbWEpCmxpYnJhcnkoUUZlYXR1cmVzKQpsaWJyYXJ5KG1zcXJvYjIpCmxpYnJhcnkocGxvdGx5KQoKcGVwdGlkZXNGaWxlIDwtICJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3RhdE9taWNzL1BEQTIxL2RhdGEvcXVhbnRpZmljYXRpb24vaGVhcnQvcGVwdGlkZXMudHh0IgoKZWNvbHMgPC0gZ3JlcCgiSW50ZW5zaXR5XFwuIiwgbmFtZXMocmVhZC5kZWxpbShwZXB0aWRlc0ZpbGUpKSkKCnBlIDwtIHJlYWRRRmVhdHVyZXMoCiAgdGFibGUgPSBwZXB0aWRlc0ZpbGUsCiAgZm5hbWVzID0gMSwKICBlY29sID0gZWNvbHMsCiAgbmFtZSA9ICJwZXB0aWRlUmF3Iiwgc2VwPSJcdCIpCgpwZQpwZVtbInBlcHRpZGVSYXciXV0KYGBgCgpXZSB3aWxsIG1ha2UgdXNlIGZyb20gZGF0YSB3cmFuZ2xpbmcgZnVuY3Rpb25hbGl0aWVzIGZyb20gdGhlIHRpZHl2ZXJzZSBwYWNrYWdlLgpUaGUgJT4lIG9wZXJhdG9yIGFsbG93cyB1cyB0byBwaXBlIHRoZSBvdXRwdXQgb2Ygb25lIGZ1bmN0aW9uIHRvIHRoZSBuZXh0IGZ1bmN0aW9uLgoKYGBge3J9CmNvbERhdGEocGUpJGxvY2F0aW9uIDwtIHN1YnN0cigKICBjb2xuYW1lcyhwZVtbInBlcHRpZGVSYXciXV0pLAogIDExLAogIDExKSAlPiUKICB1bmxpc3QgJT4lICAKICBhcy5mYWN0b3IKCmNvbERhdGEocGUpJHRpc3N1ZSA8LSBzdWJzdHIoCiAgICBjb2xuYW1lcyhwZVtbInBlcHRpZGVSYXciXV0pLAogICAgMTIsCiAgICAxMikgJT4lCiAgICB1bmxpc3QgJT4lICAKICAgIGFzLmZhY3RvcgoKY29sRGF0YShwZSkkcGF0aWVudCA8LSBzdWJzdHIoCiAgY29sbmFtZXMocGVbWyJwZXB0aWRlUmF3Il1dKSwKICAxMywKICAxMykgJT4lCiAgdW5saXN0ICU+JSAgCiAgYXMuZmFjdG9yCmBgYAoKCldlIGNhbGN1bGF0ZSBob3cgbWFueSBub24gemVybyBpbnRlbnNpdGllcyB3ZSBoYXZlIHBlciBwZXB0aWRlIGFuZCB0aGlzCndpbGwgYmUgdXNlZnVsIGZvciBmaWx0ZXJpbmcuCgpgYGB7cn0Kcm93RGF0YShwZVtbInBlcHRpZGVSYXciXV0pJG5Ob25aZXJvIDwtIHJvd1N1bXMoYXNzYXkocGVbWyJwZXB0aWRlUmF3Il1dKSA+IDApCmBgYAoKClBlcHRpZGVzIHdpdGggemVybyBpbnRlbnNpdGllcyBhcmUgbWlzc2luZyBwZXB0aWRlcyBhbmQgc2hvdWxkIGJlIHJlcHJlc2VudAp3aXRoIGEgYE5BYCB2YWx1ZSByYXRoZXIgdGhhbiBgMGAuCmBgYHtyfQpwZSA8LSB6ZXJvSXNOQShwZSwgInBlcHRpZGVSYXciKSAjIGNvbnZlcnQgMCB0byBOQQpgYGAKCjwvcD48L2RldGFpbHM+CgoKIyMgRGF0YSBleHBsb3JhdGlvbgoKPGRldGFpbHM+PHN1bW1hcnk+IENsaWNrIHRvIHNlZSBiYWNrZ3JvdW5kIGFuZCBjb2RlIDwvc3VtbWFyeT48cD4KCgpgciBmb3JtYXQobWVhbihpcy5uYShhc3NheShwZVtbInBlcHRpZGVSYXciXV0pKSkqMTAwLGRpZ2l0cz0yKWAlIG9mIGFsbCBwZXB0aWRlCmludGVuc2l0aWVzIGFyZSBtaXNzaW5nIGFuZCBmb3Igc29tZSBwZXB0aWRlcyB3ZSBkbyBub3QgZXZlbiBtZWFzdXJlIGEgc2lnbmFsCmluIGFueSBzYW1wbGUuIFRoZSBtaXNzaW5nbmVzcyBpcyBzaW1pbGFyIGFjcm9zcyBzYW1wbGVzLgoKCiMgUHJlcHJvY2Vzc2luZwoKPGRldGFpbHM+PHN1bW1hcnk+IENsaWNrIHRvIHNlZSBiYWNrZ3JvdW5kIGFuZCBjb2RlIDwvc3VtbWFyeT48cD4KCgpUaGlzIHNlY3Rpb24gcHJlZm9ybXMgc3RhbmRhcmQgcHJlcHJvY2Vzc2luZyBmb3IgdGhlIHBlcHRpZGUgZGF0YS4gVGhpcwppbmNsdWRlIGxvZyB0cmFuc2Zvcm1hdGlvbiwgZmlsdGVyaW5nIGFuZCBzdW1tYXJpc2F0aW9uIG9mIHRoZSBkYXRhLgoKIyMgTG9nIHRyYW5zZm9ybSB0aGUgZGF0YQoKYGBge3J9CnBlIDwtIGxvZ1RyYW5zZm9ybShwZSwgYmFzZSA9IDIsIGkgPSAicGVwdGlkZVJhdyIsIG5hbWUgPSAicGVwdGlkZUxvZyIpCmxpbW1hOjpwbG90RGVuc2l0aWVzKGFzc2F5KHBlW1sicGVwdGlkZUxvZyJdXSkpCmBgYAoKPC9wPjwvZGV0YWlscz4KCgojIyBGaWx0ZXJpbmcKCjxkZXRhaWxzPjxzdW1tYXJ5PiBDbGljayB0byBzZWUgYmFja2dyb3VuZCBhbmQgY29kZSA8L3N1bW1hcnk+PHA+CgoKIyMjIEhhbmRsaW5nIG92ZXJsYXBwaW5nIHByb3RlaW4gZ3JvdXBzCkluIG91ciBhcHByb2FjaCBhIHBlcHRpZGUgY2FuIG1hcCB0byBtdWx0aXBsZSBwcm90ZWlucywgYXMgbG9uZyBhcyB0aGVyZSBpcwpub25lIG9mIHRoZXNlIHByb3RlaW5zIHByZXNlbnQgaW4gYSBzbWFsbGVyIHN1Ymdyb3VwLgoKYGBge3J9CnBlIDwtIGZpbHRlckZlYXR1cmVzKHBlLCB+IFByb3RlaW5zICVpbiUgc21hbGxlc3RVbmlxdWVHcm91cHMocm93RGF0YShwZVtbInBlcHRpZGVMb2ciXV0pJFByb3RlaW5zKSkKYGBgCgojIyMgUmVtb3ZlIHJldmVyc2Ugc2VxdWVuY2VzIChkZWNveXMpIGFuZCBjb250YW1pbmFudHMKCldlIG5vdyByZW1vdmUgdGhlIGNvbnRhbWluYW50cywgcGVwdGlkZXMgdGhhdCBtYXAgdG8gZGVjb3kgc2VxdWVuY2VzLCBhbmQgcHJvdGVpbnMKd2hpY2ggd2VyZSBvbmx5IGlkZW50aWZpZWQgYnkgcGVwdGlkZXMgd2l0aCBtb2RpZmljYXRpb25zLgoKRmlyc3QgbG9vayB0byB0aGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcyBmb3IgdGhlIHBlcHRpZGUgZmVhdHVyZXMKYGBge3J9CnBlW1sicGVwdGlkZUxvZyJdXSAlPiUKICByb3dEYXRhICU+JQogIG5hbWVzCmBgYAoKTm8gaW5mb3JtYXRpb24gb24gZGVjb3lzLgoKYGBge3J9CnBlIDwtIGZpbHRlckZlYXR1cmVzKHBlLH4gUG90ZW50aWFsLmNvbnRhbWluYW50ICE9ICIrIikKYGBgCgoKIyMjIERyb3AgcGVwdGlkZXMgdGhhdCB3ZXJlIG9ubHkgaWRlbnRpZmllZCBpbiBvbmUgc2FtcGxlCgpXZSBrZWVwIHBlcHRpZGVzIHRoYXQgd2VyZSBvYnNlcnZlZCBhdCBsZWFzdCB0d2ljZS4KCmBgYHtyfQpwZSA8LSBmaWx0ZXJGZWF0dXJlcyhwZSx+bk5vblplcm8gPj0gMikKbnJvdyhwZVtbInBlcHRpZGVMb2ciXV0pCmBgYAoKV2Uga2VlcCBgciBucm93KHBlW1sicGVwdGlkZUxvZyJdXSlgIHBlcHRpZGVzIGFmdGVyIGZpbHRlcmluZy4KCjwvcD48L2RldGFpbHM+CgojIyBOb3JtYWxpemUgdGhlIGRhdGEKCjxkZXRhaWxzPjxzdW1tYXJ5PiBDbGljayB0byBzZWUgYmFja2dyb3VuZCBhbmQgY29kZSA8L3N1bW1hcnk+PHA+CgoKYGBge3J9CnBlIDwtIG5vcm1hbGl6ZShwZSwgCiAgICAgICAgICAgICAgICBpID0gInBlcHRpZGVMb2ciLCAKICAgICAgICAgICAgICAgIG5hbWUgPSAicGVwdGlkZU5vcm0iLCAKICAgICAgICAgICAgICAgIG1ldGhvZCA9ICJjZW50ZXIubWVkaWFuIikKYGBgCgo8L3A+PC9kZXRhaWxzPgoKIyMjIEV4cGxvcmUgbm9ybWFsaXplZCBkYXRhCgo8ZGV0YWlscz48c3VtbWFyeT4gQ2xpY2sgdG8gc2VlIGJhY2tncm91bmQgYW5kIGNvZGUgPC9zdW1tYXJ5PjxwPgoKCkFmdGVyICBub3JtYWxpc2F0aW9uIHRoZSBkZW5zaXR5IGN1cnZlcyBmb3IgYWxsIHNhbXBsZXMgYXJlIGNvbXBhcmFibGUuCgpgYGB7cn0KbGltbWE6OnBsb3REZW5zaXRpZXMoYXNzYXkocGVbWyJwZXB0aWRlTm9ybSJdXSkpCmBgYAoKVGhpcyBpcyBtb3JlIGNsZWFybHkgc2VlbiBpcyBhIGJveHBsb3QuCgpgYGB7cix9CmJveHBsb3QoYXNzYXkocGVbWyJwZXB0aWRlTm9ybSJdXSksIGNvbCA9IHBhbGV0dGUoKVstMV0sCiAgICAgICBtYWluID0gIlBlcHRpZGUgZGlzdHJpYnR1dGlvbnMgYWZ0ZXIgbm9ybWFsaXNhdGlvbiIsIHlsYWIgPSAiaW50ZW5zaXR5IikKYGBgCgoKV2UgY2FuIHZpc3VhbGl6ZSBvdXIgZGF0YSB1c2luZyBhIE11bHRpIERpbWVuc2lvbmFsIFNjYWxpbmcgcGxvdCwKZWcuIGFzIHByb3ZpZGVkIGJ5IHRoZSBgbGltbWFgIHBhY2thZ2UuCgpgYGB7cn0KbGltbWE6OnBsb3RNRFMoYXNzYXkocGVbWyJwZXB0aWRlTm9ybSJdXSksCiAgY29sID0gY29sRGF0YShwZSkkbG9jYXRpb246Y29sRGF0YShwZSkkdGlzc3VlICU+JQogICAgYXMubnVtZXJpYywKICBsYWJlbHMgPSBjb2xEYXRhKHBlKSAlPiUKICAgIHJvd25hbWVzICU+JSAgCiAgICBzdWJzdHIoc3RhcnQgPSAxMSwgc3RvcCA9IDEzKQogICkKYGBgCgpUaGUgZmlyc3QgYXhpcyBpbiB0aGUgcGxvdCBpcyBzaG93aW5nIHRoZSBsZWFkaW5nIGxvZyBmb2xkIGNoYW5nZXMKKGRpZmZlcmVuY2VzIG9uIHRoZSBsb2cgc2NhbGUpIGJldHdlZW4gdGhlIHNhbXBsZXMuCgo8L3A+PC9kZXRhaWxzPgoKCiMjIFN1bW1hcml6YXRpb24gdG8gcHJvdGVpbiBsZXZlbAoKPGRldGFpbHM+PHN1bW1hcnk+IENsaWNrIHRvIHNlZSBiYWNrZ3JvdW5kIGFuZCBjb2RlIDwvc3VtbWFyeT48cD4KCldlIHVzZSByb2J1c3Qgc3VtbWFyaXphdGlvbiBpbiBhZ2dyZWdhdGVGZWF0dXJlcy4gVGhpcyBpcyB0aGUgZGVmYXVsdCB3b3JrZmxvdyBvZiBhZ2dyZWdhdGVGZWF0dXJlcyBzbyB5b3UgZG8gbm90IGhhdmUgdG8gc3BlY2lmaXkgdGhlIGFyZ3VtZW50IGBmdW5gLgpIb3dldmVyLCBiZWNhdXNlIHdlIGNvbXBhcmUgbWV0aG9kcyB3ZSBoYXZlIGluY2x1ZGVkIHRoZSBgZnVuYCBhcmd1bWVudCB0byBzaG93IHRoZSBzdW1tYXJpemF0aW9uIG1ldGhvZCBleHBsaWNpdGVseS4KCmBgYHtyLHdhcm5pbmc9RkFMU0V9CnBlIDwtIGFnZ3JlZ2F0ZUZlYXR1cmVzKHBlLAogaSA9ICJwZXB0aWRlTm9ybSIsCiBmY29sID0gIlByb3RlaW5zIiwKIG5hLnJtID0gVFJVRSwKIG5hbWUgPSAicHJvdGVpblJvYnVzdCIsCiBmdW4gPSBNc0NvcmVVdGlsczo6cm9idXN0U3VtbWFyeSkKYGBgCgpgYGB7cn0KcGxvdE1EUyhhc3NheShwZVtbInByb3RlaW5Sb2J1c3QiXV0pLAogIGNvbCA9IGNvbERhdGEocGUpJGxvY2F0aW9uOmNvbERhdGEocGUpJHRpc3N1ZSAlPiUKICAgIGFzLm51bWVyaWMsCiAgbGFiZWxzID0gY29sRGF0YShwZSkgJT4lCiAgICByb3duYW1lcyAlPiUgIAogICAgc3Vic3RyKHN0YXJ0ID0gMTEsIHN0b3AgPSAxMykKKQpgYGAKCjwvcD48L2RldGFpbHM+CgoKIyBEYXRhIEFuYWx5c2lzCgojIyBFc3RpbWF0aW9uCgpXZSBtb2RlbCB0aGUgcHJvdGVpbiBsZXZlbCBleHByZXNzaW9uIHZhbHVlcyB1c2luZyBgbXNxcm9iYC4KQnkgZGVmYXVsdCBgbXNxcm9iMmAgZXN0aW1hdGVzIHRoZSBtb2RlbCBwYXJhbWV0ZXJzIHVzaW5nIHJvYnVzdCByZWdyZXNzaW9uLiAgCgpgYGB7ciwgd2FybmluZz1GQUxTRX0KcGUgPC0gbXNxcm9iKAogIG9iamVjdCA9IHBlLAogIGkgPSAicHJvdGVpblJvYnVzdCIsCiAgZm9ybXVsYSA9IH4gbG9jYXRpb24qdGlzc3VlICsgcGF0aWVudCkKYGBgCgojIyBJbmZlcmVuY2UKCkV4cGxvcmUgRGVzaWduCmBgYHtyfQpsaWJyYXJ5KEV4cGxvcmVNb2RlbE1hdHJpeCkKVmlzdWFsaXplRGVzaWduKGNvbERhdGEocGUpLH4gbG9jYXRpb24qdGlzc3VlICsgcGF0aWVudCkkcGxvdGxpc3QKYGBgCgoKCmBgYHtyfQpkZXNpZ24gPC0gbW9kZWwubWF0cml4KH5sb2NhdGlvbip0aXNzdWUgKyBwYXRpZW50LCBkYXRhID0gY29sRGF0YShwZSkpCkwgPC0gbWFrZUNvbnRyYXN0KAogIGMoCiAgICAidGlzc3VlViA9IDAiLAogICAgInRpc3N1ZVYgKyBsb2NhdGlvblI6dGlzc3VlViA9IDAiLAogICAgInRpc3N1ZVYgKyAwLjUqbG9jYXRpb25SOnRpc3N1ZVYgPSAwIiwibG9jYXRpb25SOnRpc3N1ZVYgPSAwIiksCiAgcGFyYW1ldGVyTmFtZXMgPSBjb2xuYW1lcyhkZXNpZ24pCiAgKQoKCnBlIDwtIGh5cG90aGVzaXNUZXN0KG9iamVjdCA9IHBlLCBpID0gInByb3RlaW5Sb2J1c3QiLCBjb250cmFzdCA9IEwsIG92ZXJ3cml0ZT1UUlVFKQpgYGAKCiMjIEV2YWx1YXRlIHJlc3VsdHMgY29udHJhc3QgJFxsb2dfMiBGQ197Vi1BfV5MJAoKIyMjIFZvbGNhbm8tcGxvdAoKCmBgYHtyLHdhcm5pbmc9RkFMU0V9CnZvbGNhbm9MZWZ0IDwtIGdncGxvdChyb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkInRpc3N1ZVYiLAogICAgICAgICAgICAgICAgIGFlcyh4ID0gbG9nRkMsIHkgPSAtbG9nMTAocHZhbCksIGNvbG9yID0gYWRqUHZhbCA8IDAuMDUpKSArCiBnZW9tX3BvaW50KGNleCA9IDIuNSkgKwogc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGFscGhhKGMoImJsYWNrIiwgInJlZCIpLCAwLjUpKSArIHRoZW1lX21pbmltYWwoKQp2b2xjYW5vTGVmdApgYGAKCgojIyMgSGVhdG1hcAoKV2UgZmlyc3Qgc2VsZWN0IHRoZSBuYW1lcyBvZiB0aGUgcHJvdGVpbnMgdGhhdCB3ZXJlIGRlY2xhcmVkIHNpZ25maWNhbnQuCgpgYGB7cn0Kc2lnTmFtZXNMZWZ0IDwtIHJvd0RhdGEocGVbWyJwcm90ZWluUm9idXN0Il1dKSR0aXNzdWVWICU+JQogcm93bmFtZXNfdG9fY29sdW1uKCJwcm90ZWluUm9idXN0IikgJT4lCiBmaWx0ZXIoYWRqUHZhbDwwLjA1KSAlPiUKIHB1bGwocHJvdGVpblJvYnVzdCkKaGVhdG1hcChhc3NheShwZVtbInByb3RlaW5Sb2J1c3QiXV0pW3NpZ05hbWVzTGVmdCwgXSkKYGBgCgpUaGVyZSBhcmUgYHIgbGVuZ3RoKHNpZ05hbWVzTGVmdClgIHByb3RlaW5zIHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGF0IHRoZSA1JSBGRFIgbGV2ZWwuCgpgYGB7cn0Kcm93RGF0YShwZVtbInByb3RlaW5Sb2J1c3QiXV0pJHRpc3N1ZVYgJT4lCiAgY2JpbmQoLixyb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkUHJvdGVpbi5uYW1lcykgJT4lCiAgbmEuZXhjbHVkZSAlPiUKICBmaWx0ZXIoYWRqUHZhbDwwLjA1KSAlPiUKICBhcnJhbmdlKHB2YWwpICAlPiUKICBoZWFkKDEwKSAlPiUKICBrbml0cjo6a2FibGUoLikgCmBgYAoKIyMgRXZhbHVhdGUgcmVzdWx0cyBjb250cmFzdCAkXGxvZ18yIEZDX3tWLUF9XlIkCgojIyMgVm9sY2Fuby1wbG90CgoKYGBge3Isd2FybmluZz1GQUxTRX0Kdm9sY2Fub1JpZ2h0IDwtIGdncGxvdChyb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkInRpc3N1ZVYgKyBsb2NhdGlvblI6dGlzc3VlViIsCiAgICAgICAgICAgICAgICAgYWVzKHggPSBsb2dGQywgeSA9IC1sb2cxMChwdmFsKSwgY29sb3IgPSBhZGpQdmFsIDwgMC4wNSkpICsKIGdlb21fcG9pbnQoY2V4ID0gMi41KSArCiBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYWxwaGEoYygiYmxhY2siLCAicmVkIiksIDAuNSkpICsgdGhlbWVfbWluaW1hbCgpCnZvbGNhbm9SaWdodApgYGAKCgojIyMgSGVhdG1hcAoKV2UgZmlyc3Qgc2VsZWN0IHRoZSBuYW1lcyBvZiB0aGUgcHJvdGVpbnMgdGhhdCB3ZXJlIGRlY2xhcmVkIHNpZ25maWNhbnQuCgpgYGB7cn0Kc2lnTmFtZXNSaWdodCA8LSByb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkInRpc3N1ZVYgKyBsb2NhdGlvblI6dGlzc3VlViIgJT4lCiByb3duYW1lc190b19jb2x1bW4oInByb3RlaW5Sb2J1c3QiKSAlPiUKIGZpbHRlcihhZGpQdmFsPDAuMDUpICU+JQogcHVsbChwcm90ZWluUm9idXN0KQpoZWF0bWFwKGFzc2F5KHBlW1sicHJvdGVpblJvYnVzdCJdXSlbc2lnTmFtZXNSaWdodCwgXSkKYGBgCgpUaGVyZSBhcmUgYHIgbGVuZ3RoKHNpZ05hbWVzUmlnaHQpYCBwcm90ZWlucyBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBhdCB0aGUgNSUgRkRSIGxldmVsLgoKYGBge3J9CnJvd0RhdGEocGVbWyJwcm90ZWluUm9idXN0Il1dKSQidGlzc3VlViArIGxvY2F0aW9uUjp0aXNzdWVWIiAgJT4lCiAgY2JpbmQoLixyb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkUHJvdGVpbi5uYW1lcykgJT4lCiAgbmEuZXhjbHVkZSAlPiUKICBmaWx0ZXIoYWRqUHZhbDwwLjA1KSAlPiUKICBhcnJhbmdlKHB2YWwpICU+JQogIGhlYWQoMTApICU+JQogIGtuaXRyOjprYWJsZSguKQpgYGAKCgojIyBFdmFsdWF0ZSByZXN1bHRzIGF2ZXJhZ2UgY29udHJhc3QgJFxsb2dfMiBGQ197Vi1BfSQKCiMjIyBWb2xjYW5vLXBsb3QKCgpgYGB7cix3YXJuaW5nPUZBTFNFfQp2b2xjYW5vQXZnIDwtIGdncGxvdChyb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkInRpc3N1ZVYgKyAwLjUgKiBsb2NhdGlvblI6dGlzc3VlViIsCiAgICAgICAgICAgICAgICAgYWVzKHggPSBsb2dGQywgeSA9IC1sb2cxMChwdmFsKSwgY29sb3IgPSBhZGpQdmFsIDwgMC4wNSkpICsKIGdlb21fcG9pbnQoY2V4ID0gMi41KSArCiBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYWxwaGEoYygiYmxhY2siLCAicmVkIiksIDAuNSkpICsgdGhlbWVfbWluaW1hbCgpCnZvbGNhbm9BdmcKYGBgCgoKIyMjIEhlYXRtYXAKCldlIGZpcnN0IHNlbGVjdCB0aGUgbmFtZXMgb2YgdGhlIHByb3RlaW5zIHRoYXQgd2VyZSBkZWNsYXJlZCBzaWduZmljYW50LgoKYGBge3J9CnNpZ05hbWVzQXZnIDwtIHJvd0RhdGEocGVbWyJwcm90ZWluUm9idXN0Il1dKSQidGlzc3VlViArIDAuNSAqIGxvY2F0aW9uUjp0aXNzdWVWIiAlPiUKIHJvd25hbWVzX3RvX2NvbHVtbigicHJvdGVpblJvYnVzdCIpICU+JQogZmlsdGVyKGFkalB2YWw8MC4wNSkgJT4lCiBwdWxsKHByb3RlaW5Sb2J1c3QpCmhlYXRtYXAoYXNzYXkocGVbWyJwcm90ZWluUm9idXN0Il1dKVtzaWdOYW1lc0F2ZywgXSkKYGBgCgpUaGVyZSBhcmUgYHIgbGVuZ3RoKHNpZ05hbWVzQXZnKWAgcHJvdGVpbnMgc2lnbmlmaWNhbnRseSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgYXQgdGhlIDUlIEZEUiBsZXZlbC4KCmBgYHtyfQpyb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkInRpc3N1ZVYgKyAwLjUgKiBsb2NhdGlvblI6dGlzc3VlViIgJT4lCiAgY2JpbmQoLixyb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkUHJvdGVpbi5uYW1lcykgJT4lCiAgbmEuZXhjbHVkZSAlPiUKICBmaWx0ZXIoYWRqUHZhbDwwLjA1KSAlPiUKICBhcnJhbmdlKHB2YWwpICU+JQogIGhlYWQoMTApICU+JQogIGtuaXRyOjprYWJsZSguKSAKYGBgCgoKCiMjIEludGVyYWN0aW9uCgojIyMgVm9sY2Fuby1wbG90CgoKYGBge3Isd2FybmluZz1GQUxTRX0Kdm9sY2Fub0ludCA8LSBnZ3Bsb3Qocm93RGF0YShwZVtbInByb3RlaW5Sb2J1c3QiXV0pJCJsb2NhdGlvblI6dGlzc3VlViIsCiAgICAgICAgICAgICAgICAgYWVzKHggPSBsb2dGQywgeSA9IC1sb2cxMChwdmFsKSwgY29sb3IgPSBhZGpQdmFsIDwgMC4wNSkpICsKIGdlb21fcG9pbnQoY2V4ID0gMi41KSArCiBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gYWxwaGEoYygiYmxhY2siLCAicmVkIiksIDAuNSkpICsgdGhlbWVfbWluaW1hbCgpCnZvbGNhbm9JbnQKYGBgCgojIyMgSGVhdG1hcAoKVGhlcmUgd2VyZSBubyBnZW5lcyBzaWduaWZpY2FudCBhdCB0aGUgNSUgRkRSIGxldmVsLgpXZSByZXR1cm4gdGhlIHRvcCAyNSBnZW5lcy4KCmBgYHtyfQpzaWdOYW1lc0ludCA8LSByb3dEYXRhKHBlW1sicHJvdGVpblJvYnVzdCJdXSkkImxvY2F0aW9uUjp0aXNzdWVWIiAlPiUKIHJvd25hbWVzX3RvX2NvbHVtbigicHJvdGVpblJvYnVzdCIpICU+JQogZmlsdGVyKGFkalB2YWw8MC4wNSkgJT4lCiBwdWxsKHByb3RlaW5Sb2J1c3QpCmhscCA8LSBvcmRlcigocm93RGF0YShwZVtbInByb3RlaW5Sb2J1c3QiXV0pJCJsb2NhdGlvblI6dGlzc3VlViIpWywiYWRqUHZhbCJdKVsxOjI1XQpoZWF0bWFwKGFzc2F5KHBlW1sicHJvdGVpblJvYnVzdCJdXSlbaGxwLCBdKQpgYGAKClRoZXJlIGFyZSBgciBsZW5ndGgoc2lnTmFtZXNJbnQpYCBwcm90ZWlucyBzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBhdCB0aGUgNSUgRkRSIGxldmVsLgoKYGBge3J9CnJvd0RhdGEocGVbWyJwcm90ZWluUm9idXN0Il1dKSQibG9jYXRpb25SOnRpc3N1ZVYiICU+JQogIGNiaW5kKC4scm93RGF0YShwZVtbInByb3RlaW5Sb2J1c3QiXV0pJFByb3RlaW4ubmFtZXMpICU+JQogIG5hLmV4Y2x1ZGUgJT4lCiAgZmlsdGVyKGFkalB2YWw8MC4wNSkgJT4lCiAgaGVhZCgxMCkgJT4lCiAga25pdHI6OmthYmxlKC4pIApgYGAKCgo=