MSstatsLiP Workflow: Protease resistance analysis
This is an R Markdown
Notebook describing the analysis of a LiP-MS experiment using MSstatsLiP. When you
execute code within the notebook, the results appear beneath the
code.
Here, we use LiP-MS data of human alpha-Synuclein in the monomeric
(M) and fibrillar form (F) spiked into a S.cerevisiae lysate at
5 pmol/ug lysate (M1 and F1) and 20 pmol/ug lysate (M2 and F2).The data
set is composed of four biological replicates per condition.
1. Installation
- Install and load all necessary packages. The installation needs to
be performed at first use only. Un-comment the lines for execution.
knitr::opts_chunk$set(include = FALSE)
if (!requireNamespace("BiocManager", quietly = TRUE))
install.packages("BiocManager")
BiocManager::install("MSstatsLiP")
- Set the working directory
input_folder=choose.dir(caption="Choose the working directory")
knitr::opts_knit$set(root.dir = input_folder)
2. Data preprocessing
2.1 Load datasets
Load the data from the Spectronaut export. LiP data is loaded as
raw_lip
, trypsin-only control data (TrP data) is loaded as
raw_prot
. The function choose.files()
enables
browsing for the input file.
CAVE: Make sure the separator delim
is
set correctly. For comma-separated values (csv), the separator is set to
delim=","
.
raw_lip <- read_delim(file=choose.files(caption="Choose LiP dataset"),
delim=",", escape_double = FALSE, trim_ws = TRUE)
raw_prot <- read_delim(file=choose.files(caption="Choose TrP dataset"),
delim=",", escape_double = FALSE, trim_ws = TRUE)
raw_lip <- raw_lip %>% mutate_all(funs(ifelse(.=="P37840.1", "P37840", .)))
raw_prot <- raw_prot %>% mutate_all(funs(ifelse(.=="P37840.1", "P37840", .)))
Load the fasta file that was used in the Spectronaut search.
fasta_file=choose.files(caption = "Choose FASTA file")
Convert the data to MSstatsLiP format. Load first the LiP data set
raw_lip
, then the FASTA file fasta_file
used
for searches. If the experiment contains TrP data, raw_prot
is loaded last.
To remove information on iRT peptides, the default setting is
removeiRT = TRUE
. As default, peptides containing
modifications are filtered, but this can be changed using the argument
removeModifications
. Also, peptides with multiple protein
annotations are filtered as default. However, for data sets containing
protein isoforms, this argument can be set to
removeNonUniqueProteins = FALSE
.
The default settings use PeakArea as measure of intensity,
filter features based on the q-value, with a q-value cut-off of 0.01 and
import all conditions. You can adjust the settings accordingly. For
information on each option, refer to the vignette of the function.
msstats_data <- SpectronauttoMSstatsLiPFormat(raw_lip, fasta_file, raw_prot)
2.2 Select only fully tryptic (FT) peptides in both LiP and TrP
dataset
Proteolytic resistance is calculated as the of the intensity of fully
tryptic peptides in the LiP condition to the TrP condition. Half-tryptic
(HT) peptides are excluded from this analysis. The function
“calculateTrypticity” is used to annotate FT and HT peptides in the LiP
dataset. Next, from the TrP dataset we filtered out FT peptides not
identified in the LiP dataset.The msstats_data list will finally contain
only FT peptides measured in both LiP and TrP datasets.
FullyTrP <- msstats_data[["LiP"]] %>%
distinct(ProteinName, PeptideSequence) %>%
calculateTrypticity(fasta_file) %>%
filter(fully_TRI) %>%
filter(MissedCleavage == FALSE) %>%
select(ProteinName, PeptideSequence, StartPos, EndPos)
msstats_data[["LiP"]] <- msstats_data[["LiP"]] %>%
select(-ProteinName) %>% inner_join(FullyTrP)
msstats_data[["TrP"]] <- msstats_data[["TrP"]] %>%
select(-ProteinName) %>% inner_join(FullyTrP)
2.3 Correct nomenclature
Step 1:
Ensure that the Condition
nomenclature is identical in
both data sets. If the output is TRUE
for all conditions,
continue to step 2.
unique(msstats_data[["LiP"]]$Condition)%in%unique(msstats_data[["TrP"]]$Condition)
To correct the condition nomenclature, display the condition for both
data sets.
paste("LiP Condition nomenclature:", unique(msstats_data[["LiP"]]$Condition), ",",
"TrP Condition nomenclature:",unique(msstats_data[["TrP"]]$Condition))
If necessary, un-comment following lines to correct the condition
nomenclature in either of the data sets. E.g. change the nomenclature of
the TrP samples from Cond1
to cond1
.
# msstats_data[["TrP"]] = msstats_data[["TrP"]] %>%
# mutate(Condition = case_when(Condition == "Cond1" ~ "cond1",
# Condition == "Cond2" ~ "cond2"))
Step 2:
Ensure that BioReplicate
nomenclature is correctly
annotated (see also MSstats
user manual. The BioReplicate needs a unique nomenclature, while the
technical replicates can have duplicate numbering. If the replicate
nomenclature is correct, proceed to section
2.3.
paste("LiP BioReplicate nomenclature:", unique(msstats_data[["LiP"]]$BioReplicate), ",",
"TrP BioReplicate nomenclature:",unique(msstats_data[["TrP"]]$BioReplicate))
Adjust BioReplicate
column to correct nomenclature for a
Case-control experiment.
msstats_data[["LiP"]] = msstats_data[["LiP"]] %>%
mutate(BioReplicate = paste0(Condition,".",BioReplicate))
msstats_data[["TrP"]] = msstats_data[["TrP"]] %>%
mutate(BioReplicate = paste0(Condition,".",BioReplicate))
Inspect corrected BioReplicate
column.
paste("LiP BioReplicate nomenclature:", unique(msstats_data[["LiP"]]$BioReplicate), ",",
"TrP BioReplicate nomenclature:",unique(msstats_data[["TrP"]]$BioReplicate))
2.4 Data Summarization
Summarize the data. The default settings use a log2-transformation
and normalize the data using the "equalizeMedians"
method.
The default summary method is "TMP"
and imputation is set
to "FALSE"
. For detailed information on all settings,
please refer to the function vignette.
This function will take some time and memory. If memory is limited,
it is advisable to remove the raw files using the rm()
function and clearing the memory cache using the gc()
function.
MSstatsLiP_Summarized <- dataSummarizationLiP(msstats_data, normalization.LiP = "equalizeMedians")
Inspect MSstatsLiP_Summarized
.
names(MSstatsLiP_Summarized[["LiP"]])
head(MSstatsLiP_Summarized[["LiP"]]$FeatureLevelData)
head(MSstatsLiP_Summarized[["LiP"]]$ProteinLevelData)
head(MSstatsLiP_Summarized[["TrP"]]$FeatureLevelData)
head(MSstatsLiP_Summarized[["TrP"]]$ProteinLevelData)
Save and/or load summarized data.
save(MSstatsLiP_Summarized, file = 'MSstatsLiP_summarized.rda')
load(file = 'MSstatsLiP_summarized.rda')
3. Modelling
Run the modeling to obtain significantly altered peptides and
proteins. The function groupComparisonLiP
outputs a list
with three separate models: 1. LiP.Model
, which contains
the differential analysis on peptide level in the LiP sample without
correction for protein abundance alterations. 2.
Adjusted.LiP.Model
, which contains the differential
analysis on peptide level in the LiP sample with correction for protein
abundance alterations 3. TrP.Model
, which contains the
differential analysis on protein level. The default setting of the
function is a pairwise comparison of all existing groups. Alternatively,
a contrast matrix can be provided to specify the comparisons of
interest. See Vignette for details.
MSstatsLiP_model = groupComparisonLiP(MSstatsLiP_Summarized)
Inspect MSstatsLiP_model
.
head(MSstatsLiP_model[["LiP.Model"]])
head(MSstatsLiP_model[["TrP.Model"]])
Save and/or load model data.
save(MSstatsLiP_model, file = 'MSstatsLiP_model.rda')
load(file = 'MSstatsLiP_model.rda')
4. Calculate proteolytic resistance ratios
Proteolytic resistance ratios are calculated as the ratio of the
intensity of fully tryptic peptides in the LiP condition to the TrP
condition. In general, a low protease resistance value is indicative of
high extent of cleavage, while high protease resistance values indicate
low cleavage extent.
Accessibility = calculateProteolyticResistance(MSstatsLiP_Summarized,
fasta_file,
differential_analysis = TRUE)
Accessibility$RunLevelData
ResistanceBarcodePlotLiP(Accessibility,
fasta_file,
which.prot = "P16622",
which.condition = "F1",
address = FALSE)
5. Proteolytic resistance differential analysis
In this paragraph we described how to compare proteolytic resistance
patterns of different conditions, as reported in Cappelletti et al.,
2021, Figure 3. As described in the “Protease digestion accessibility
analysis” paragraph of Cappelletti et al., proteolytic resistance is
calculated as the ratio of the intensity of fully tryptic peptides in
the LiP condition to the TrP condition and can be compared across
different conditions using the linear mixed effects models-based
differential analysis implemented in the MSstatsLiP package. First,
infinite values are filtered out from the result of the
groupComparisonLiP function. Next, logFCs and standard errors of the LiP
(log2FC, s2) and TrP (log2FC_ref,s2_ref) models are combined and
Student’s T-test is applied to compare proteolytic resistance between
different conditions.Finally, p-values are adjusted for multiple
comparisons (default is Benjamini & Hochberg method). In general, a
low Proteolytic resistance value is indicative of high extent of
cleavage, while high Proteolytic resistance values indicate low cleavage
extent.
Accessibility$groupComparison
Save and/or load model data
# save(FullyTrp.Model, file = 'Protection_model.rda')
# load(file = 'Protection_model.rda')
6. Save outputs
Save the output of the modeling in a .csv file.
# write.csv(FullyTrp.Model, "Proteolytic_resistance_DA.csv")
7. Plot aSynuclein proteolytic resistance DA result as barcode
Proteolytic resistance barcodes can be used to visualize FT peptides
along the sequence of aSynucelin. Significant peptides showing high
protease resistance are colored in red, significant peptides showing a
decreased protease resistance are colored in blue and non-significant
peptides (no change in protease resistance between conditions) are
colored in grey. Black regions represent regions with no identified
matching peptide. Position of the NAC domain is indicated by a
rectangle.
ResistanceBarcodePlotLiP(Accessibility,
fasta_file,
which.prot = "P16622",
which.condition = "F1",
differential_analysis = TRUE,
which.comp = "F1 vs F2",
address = FALSE)
LS0tCnRpdGxlOiAiUHJvdGVvbHl0aWMgcmVzaXN0YW5jZSBhbmFseXNpcyIKYXV0aG9yOiBWYWxlbnRpbmEgQ2FwcGVsbGV0dGkgKDxjYXBwZWxsZXR0aUBpbXNiLmJpb2wuZXRoei5jaD4pLAogIE1hbGlub3Zza2EgTGlsaWFuYSAoPG1hbGlub3Zza2FAaW1zYi5iaW9sLmV0aHouY2g+KSwgCiAgRGV2b24gS29obGVyICg8a29obGVyLmRAbm9ydGhlYXN0ZXJuLmVkdT4pCmRhdGU6ICJgciBTeXMuRGF0ZSgpYCIKdmlnbmV0dGU6ID4KICAlXFZpZ25ldHRlSW5kZXhFbnRyeXtNU3N0YXRzTGlQIFByb3Rlb2x5dGljIFdvcmtmbG93fQogICVcVmlnbmV0dGVFbmdpbmV7a25pdHI6OnJtYXJrZG93bn0KICAlXFZpZ25ldHRlRW5jb2Rpbmd7VVRGLTh9Cm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHllcwogICAgaGlnaGxpZ2h0OiBweWdtZW50cwogICAgdGhlbWU6IGx1bWVuCiAgcGRmX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKLS0tCiMgTVNzdGF0c0xpUCBXb3JrZmxvdzogUHJvdGVhc2UgcmVzaXN0YW5jZSBhbmFseXNpcwoKVGhpcyBpcyBhbiBbUiBNYXJrZG93bl0oaHR0cDovL3JtYXJrZG93bi5yc3R1ZGlvLmNvbSkgTm90ZWJvb2sgZGVzY3JpYmluZyB0aGUgYW5hbHlzaXMgb2YgYSBMaVAtTVMgZXhwZXJpbWVudCB1c2luZyBbTVNzdGF0c0xpUF0oaHR0cHM6Ly9naXRodWIuY29tL1ZpdGVrLUxhYi9NU3N0YXRzTGlQKS4gV2hlbiB5b3UgZXhlY3V0ZSBjb2RlIHdpdGhpbiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIGFwcGVhciBiZW5lYXRoIHRoZSBjb2RlLiAKCkhlcmUsIHdlIHVzZSBMaVAtTVMgZGF0YSBvZiBodW1hbiBhbHBoYS1TeW51Y2xlaW4gaW4gdGhlIG1vbm9tZXJpYyAoTSkgYW5kIGZpYnJpbGxhciBmb3JtIChGKSBzcGlrZWQgaW50byBhICpTLmNlcmV2aXNpYWUqIGx5c2F0ZSBhdCA1IHBtb2wvdWcgbHlzYXRlIChNMSBhbmQgRjEpIGFuZCAyMCBwbW9sL3VnIGx5c2F0ZSAoTTIgYW5kIEYyKS5UaGUgZGF0YSBzZXQgaXMgY29tcG9zZWQgb2YgZm91ciBiaW9sb2dpY2FsIHJlcGxpY2F0ZXMgcGVyIGNvbmRpdGlvbi4KCiMjIDEuIEluc3RhbGxhdGlvbgoKLSBJbnN0YWxsIGFuZCBsb2FkIGFsbCBuZWNlc3NhcnkgcGFja2FnZXMuIFRoZSBpbnN0YWxsYXRpb24gbmVlZHMgdG8gYmUgcGVyZm9ybWVkIGF0IGZpcnN0IHVzZSBvbmx5LiBVbi1jb21tZW50IHRoZSBsaW5lcyBmb3IgZXhlY3V0aW9uLgoKYGBge3Igc2V0dXB9CiBrbml0cjo6b3B0c19jaHVuayRzZXQoaW5jbHVkZSA9IEZBTFNFKQpgYGAKCmBgYCAge3IsIGV2YWwgPSBGQUxTRX0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkKICAgIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKCkJpb2NNYW5hZ2VyOjppbnN0YWxsKCJNU3N0YXRzTGlQIikKYGBgCgpgYGB7cixpbmNsdWRlPVRSVUUscmVzdWx0cz0iaGlkZSIsbWVzc2FnZT1GQUxTRSx3YXJuaW5nPUZBTFNFfQpsaWJyYXJ5KE1Tc3RhdHNMaVApCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGRhdGEudGFibGUpCmxpYnJhcnkoZ2doaWdobGlnaHQpCmBgYAoKLSBTZXQgdGhlIHdvcmtpbmcgZGlyZWN0b3J5CgpgYGB7ciBzZXQgd29ya2luZyBkcmVjdG9yeSwgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBpbmNsdWRlPVRSVUUsIGV2YWwgPSBGQUxTRX0KaW5wdXRfZm9sZGVyPWNob29zZS5kaXIoY2FwdGlvbj0iQ2hvb3NlIHRoZSB3b3JraW5nIGRpcmVjdG9yeSIpCmtuaXRyOjpvcHRzX2tuaXQkc2V0KHJvb3QuZGlyID0gaW5wdXRfZm9sZGVyKSAKYGBgCgojIDIuIERhdGEgcHJlcHJvY2Vzc2luZwoKIyMgMi4xIExvYWQgZGF0YXNldHMKCkxvYWQgdGhlIGRhdGEgZnJvbSB0aGUgU3BlY3Ryb25hdXQgZXhwb3J0LiBMaVAgZGF0YSBpcyBsb2FkZWQgYXMgYHJhd19saXBgLCAgdHJ5cHNpbi1vbmx5IGNvbnRyb2wgZGF0YSAoVHJQIGRhdGEpIGlzIGxvYWRlZCBhcyBgcmF3X3Byb3RgLiBUaGUgZnVuY3Rpb24gYGNob29zZS5maWxlcygpYCBlbmFibGVzIGJyb3dzaW5nIGZvciB0aGUgaW5wdXQgZmlsZS4gCgoqKkNBVkU6KiogTWFrZSBzdXJlIHRoZSBzZXBhcmF0b3IgYGRlbGltYCBpcyBzZXQgY29ycmVjdGx5LiBGb3IgY29tbWEtc2VwYXJhdGVkIHZhbHVlcyAoY3N2KSwgdGhlIHNlcGFyYXRvciBpcyBzZXQgdG8gYGRlbGltPSIsImAuCgpgYGB7ciBsb2FkIExpUCBkYXRhLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9VFJVRSwgZXZhbCA9IEZBTFNFfQpyYXdfbGlwIDwtIHJlYWRfZGVsaW0oZmlsZT1jaG9vc2UuZmlsZXMoY2FwdGlvbj0iQ2hvb3NlIExpUCBkYXRhc2V0IiksIAogICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09IiwiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIHRyaW1fd3MgPSBUUlVFKQpgYGAKCmBgYHtyIGxvYWQgVHJQIGRhdGEsIGV2YWw9RkFMU0UsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1UUlVFfQoKcmF3X3Byb3QgPC0gcmVhZF9kZWxpbShmaWxlPWNob29zZS5maWxlcyhjYXB0aW9uPSJDaG9vc2UgVHJQIGRhdGFzZXQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZGVsaW09IiwiLCBlc2NhcGVfZG91YmxlID0gRkFMU0UsIHRyaW1fd3MgPSBUUlVFKQpgYGAKCmBgYHtyIEFkanVzdCBhU3ludWNsZWluIG5vbWVuY2xhdHVyZSB0byBtYXRjaCBmYXN0YSBmaWxlLCBldmFsPVRSVUUsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1UUlVFfQpyYXdfbGlwIDwtIHJhd19saXAgJT4lIG11dGF0ZV9hbGwoZnVucyhpZmVsc2UoLj09IlAzNzg0MC4xIiwgIlAzNzg0MCIsIC4pKSkKcmF3X3Byb3QgPC0gcmF3X3Byb3QgJT4lIG11dGF0ZV9hbGwoZnVucyhpZmVsc2UoLj09IlAzNzg0MC4xIiwgIlAzNzg0MCIsIC4pKSkKYGBgCgpMb2FkIHRoZSBmYXN0YSBmaWxlIHRoYXQgd2FzIHVzZWQgaW4gdGhlIFNwZWN0cm9uYXV0IHNlYXJjaC4KCmBgYHtyIGxvYWQgZmFzdGEgZmlsZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRSwgZXZhbCA9IEZBTFNFfQpmYXN0YV9maWxlPWNob29zZS5maWxlcyhjYXB0aW9uID0gIkNob29zZSBGQVNUQSBmaWxlIikKYGBgCgpgYGB7ciBpbmNsdWRlPUZBTFNFfQpmYXN0YV9maWxlID0gIi4uL2luc3QvZXh0ZGF0YS9wcm90ZW9seXRpY19mYXN0YV9kYXRhLmZhc3RhIgpgYGAKCkNvbnZlcnQgdGhlIGRhdGEgdG8gTVNzdGF0c0xpUCBmb3JtYXQuIExvYWQgZmlyc3QgdGhlIExpUCBkYXRhIHNldCBgcmF3X2xpcGAsIHRoZW4gdGhlIEZBU1RBIGZpbGUgYGZhc3RhX2ZpbGVgIHVzZWQgZm9yIHNlYXJjaGVzLiBJZiB0aGUgZXhwZXJpbWVudCBjb250YWlucyBUclAgZGF0YSwgYHJhd19wcm90YCBpcyBsb2FkZWQgbGFzdC4KClRvIHJlbW92ZSBpbmZvcm1hdGlvbiBvbiBpUlQgcGVwdGlkZXMsIHRoZSBkZWZhdWx0IHNldHRpbmcgaXMgYHJlbW92ZWlSVCA9IFRSVUVgLiBBcyBkZWZhdWx0LCBwZXB0aWRlcyBjb250YWluaW5nIG1vZGlmaWNhdGlvbnMgYXJlIGZpbHRlcmVkLCBidXQgdGhpcyBjYW4gYmUgY2hhbmdlZCB1c2luZyB0aGUgYXJndW1lbnQgYHJlbW92ZU1vZGlmaWNhdGlvbnNgLiBBbHNvLCBwZXB0aWRlcyB3aXRoIG11bHRpcGxlIHByb3RlaW4gYW5ub3RhdGlvbnMgYXJlIGZpbHRlcmVkIGFzIGRlZmF1bHQuIEhvd2V2ZXIsIGZvciBkYXRhIHNldHMgY29udGFpbmluZyBwcm90ZWluIGlzb2Zvcm1zLCB0aGlzIGFyZ3VtZW50IGNhbiBiZSBzZXQgdG8gYHJlbW92ZU5vblVuaXF1ZVByb3RlaW5zID0gRkFMU0VgLgoKVGhlIGRlZmF1bHQgc2V0dGluZ3MgdXNlICpQZWFrQXJlYSogYXMgbWVhc3VyZSBvZiBpbnRlbnNpdHksIGZpbHRlciBmZWF0dXJlcyBiYXNlZCBvbiB0aGUgcS12YWx1ZSwgd2l0aCBhIHEtdmFsdWUgY3V0LW9mZiBvZiAwLjAxIGFuZCBpbXBvcnQgYWxsIGNvbmRpdGlvbnMuIFlvdSBjYW4gYWRqdXN0IHRoZSBzZXR0aW5ncyBhY2NvcmRpbmdseS4gRm9yIGluZm9ybWF0aW9uIG9uIGVhY2ggb3B0aW9uLCByZWZlciB0byB0aGUgdmlnbmV0dGUgb2YgdGhlIGZ1bmN0aW9uLgoKYGBge3IgY29udmVydCB0byBNU3N0YXRzTGlQIGZvcm1hdCwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSxlY2hvPVRSVUUsaW5jbHVkZT1UUlVFfQptc3N0YXRzX2RhdGEgPC0gU3BlY3Ryb25hdXR0b01Tc3RhdHNMaVBGb3JtYXQocmF3X2xpcCwgZmFzdGFfZmlsZSwgcmF3X3Byb3QpCmBgYAoKIyMgMi4yIFNlbGVjdCBvbmx5IGZ1bGx5IHRyeXB0aWMgKEZUKSBwZXB0aWRlcyBpbiBib3RoIExpUCBhbmQgVHJQIGRhdGFzZXQKClByb3Rlb2x5dGljIHJlc2lzdGFuY2UgaXMgY2FsY3VsYXRlZCBhcyB0aGUgb2YgdGhlIGludGVuc2l0eSBvZiBmdWxseSB0cnlwdGljIHBlcHRpZGVzIGluIHRoZSBMaVAgY29uZGl0aW9uIHRvIHRoZSBUclAgY29uZGl0aW9uLiBIYWxmLXRyeXB0aWMgKEhUKSBwZXB0aWRlcyBhcmUgZXhjbHVkZWQgZnJvbSB0aGlzIGFuYWx5c2lzLiBUaGUgZnVuY3Rpb24gImNhbGN1bGF0ZVRyeXB0aWNpdHkiIGlzIHVzZWQgdG8gYW5ub3RhdGUgRlQgYW5kIEhUIHBlcHRpZGVzIGluIHRoZSBMaVAgZGF0YXNldC4gTmV4dCwgZnJvbSB0aGUgVHJQIGRhdGFzZXQgd2UgZmlsdGVyZWQgb3V0IEZUIHBlcHRpZGVzIG5vdCBpZGVudGlmaWVkIGluIHRoZSBMaVAgZGF0YXNldC5UaGUgbXNzdGF0c19kYXRhIGxpc3Qgd2lsbCBmaW5hbGx5IGNvbnRhaW4gb25seSBGVCBwZXB0aWRlcyBtZWFzdXJlZCBpbiBib3RoIExpUCBhbmQgVHJQIGRhdGFzZXRzLiAgCgpgYGB7ciBmaW5kIEZUIHBlcHRpZGVzLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsaW5jbHVkZT1UUlVFfQoKRnVsbHlUclAgPC0gbXNzdGF0c19kYXRhW1siTGlQIl1dICU+JSAKICBkaXN0aW5jdChQcm90ZWluTmFtZSwgUGVwdGlkZVNlcXVlbmNlKSAlPiUgCiAgY2FsY3VsYXRlVHJ5cHRpY2l0eShmYXN0YV9maWxlKSAlPiUgCiAgZmlsdGVyKGZ1bGx5X1RSSSkgJT4lCiAgZmlsdGVyKE1pc3NlZENsZWF2YWdlID09IEZBTFNFKSAlPiUgCiAgc2VsZWN0KFByb3RlaW5OYW1lLCBQZXB0aWRlU2VxdWVuY2UsIFN0YXJ0UG9zLCBFbmRQb3MpCmBgYAoKYGBge3Igc2VsZWN0IEZUIHBlcHRpZGVzIGluIExpUCBkYXRhLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsaW5jbHVkZT1UUlVFfQptc3N0YXRzX2RhdGFbWyJMaVAiXV0gPC0gbXNzdGF0c19kYXRhW1siTGlQIl1dICU+JSAKICBzZWxlY3QoLVByb3RlaW5OYW1lKSAlPiUgaW5uZXJfam9pbihGdWxseVRyUCkKYGBgCgpgYGB7ciBzZWxlY3QgRlQgcGVwdGlkZXMgaW4gVHJQIGRhdGEsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1UUlVFfQptc3N0YXRzX2RhdGFbWyJUclAiXV0gPC0gbXNzdGF0c19kYXRhW1siVHJQIl1dICU+JSAKICBzZWxlY3QoLVByb3RlaW5OYW1lKSAlPiUgaW5uZXJfam9pbihGdWxseVRyUCkKYGBgCgojIyAyLjMgQ29ycmVjdCBub21lbmNsYXR1cmUKCiMjIyMgU3RlcCAxOiAKRW5zdXJlIHRoYXQgdGhlIGBDb25kaXRpb25gIG5vbWVuY2xhdHVyZSBpcyBpZGVudGljYWwgaW4gYm90aCBkYXRhIHNldHMuIElmIHRoZSBvdXRwdXQgaXMgYFRSVUVgIGZvciBhbGwgY29uZGl0aW9ucywgY29udGludWUgdG8gW3N0ZXAgMl0oI3N0ZXB0d28pLgoKYGBge3IgVGVzdCBDb25kdGlvbiBub21lbmNsYXR1cmUsIGVjaG89VFJVRSxpbmNsdWRlPVRSVUV9CnVuaXF1ZShtc3N0YXRzX2RhdGFbWyJMaVAiXV0kQ29uZGl0aW9uKSVpbiV1bmlxdWUobXNzdGF0c19kYXRhW1siVHJQIl1dJENvbmRpdGlvbikKYGBgCgpUbyBjb3JyZWN0IHRoZSBjb25kaXRpb24gbm9tZW5jbGF0dXJlLCBkaXNwbGF5IHRoZSBjb25kaXRpb24gZm9yIGJvdGggZGF0YSBzZXRzLgpgYGB7ciBEaXNwbGF5IENvbmR0aW9uIG5vbWVuY2xhdHVyZSwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KcGFzdGUoIkxpUCBDb25kaXRpb24gbm9tZW5jbGF0dXJlOiIsIHVuaXF1ZShtc3N0YXRzX2RhdGFbWyJMaVAiXV0kQ29uZGl0aW9uKSwgIiwiLAogICAgICAiVHJQIENvbmRpdGlvbiBub21lbmNsYXR1cmU6Iix1bmlxdWUobXNzdGF0c19kYXRhW1siVHJQIl1dJENvbmRpdGlvbikpCmBgYAoKSWYgbmVjZXNzYXJ5LCB1bi1jb21tZW50IGZvbGxvd2luZyBsaW5lcyB0byBjb3JyZWN0IHRoZSBjb25kaXRpb24gbm9tZW5jbGF0dXJlIGluIGVpdGhlciBvZiB0aGUgZGF0YSBzZXRzLiBFLmcuIGNoYW5nZSB0aGUgbm9tZW5jbGF0dXJlIG9mIHRoZSBUclAgc2FtcGxlcyBmcm9tIGBDb25kMWAgdG8gYGNvbmQxYC4KCmBgYHtyIENvcnJlY3QgQ29uZGl0aW9uIG5vbWVuY2xhdHVyZSwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KIyBtc3N0YXRzX2RhdGFbWyJUclAiXV0gPSBtc3N0YXRzX2RhdGFbWyJUclAiXV0gJT4lIAojICAgbXV0YXRlKENvbmRpdGlvbiA9IGNhc2Vfd2hlbihDb25kaXRpb24gPT0gIkNvbmQxIiB+ICJjb25kMSIsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIENvbmRpdGlvbiA9PSAiQ29uZDIiIH4gImNvbmQyIikpCmBgYAoKCiMjIyMgU3RlcCAyOiB7I3N0ZXB0d299CgpFbnN1cmUgdGhhdCBgQmlvUmVwbGljYXRlYCBub21lbmNsYXR1cmUgaXMgY29ycmVjdGx5IGFubm90YXRlZCAoc2VlIGFsc28gIFtNU3N0YXRzXShodHRwOi8vbXNzdGF0cy5vcmcvd3AtY29udGVudC91cGxvYWRzLzIwMjAvMDIvTVNzdGF0c192My4xOC4xX21hbnVhbF8yMDIwRmViMjYtdjIucGRmKSB1c2VyIG1hbnVhbC4gVGhlIEJpb1JlcGxpY2F0ZSBuZWVkcyBhIHVuaXF1ZSBub21lbmNsYXR1cmUsIHdoaWxlIHRoZSB0ZWNobmljYWwgcmVwbGljYXRlcyBjYW4gaGF2ZSBkdXBsaWNhdGUgbnVtYmVyaW5nLiBJZiB0aGUgcmVwbGljYXRlIG5vbWVuY2xhdHVyZSBpcyBjb3JyZWN0LCBwcm9jZWVkIHRvIFtzZWN0aW9uIDIuM10oI2RhdGEtc3VtbSkuCgpgYGB7ciBEaXNwbGF5IEJpb1JlcGxpY2F0ZSBub21lbmNsYXR1cmUsIGVjaG89VFJVRSxpbmNsdWRlPVRSVUV9CnBhc3RlKCJMaVAgQmlvUmVwbGljYXRlIG5vbWVuY2xhdHVyZToiLCB1bmlxdWUobXNzdGF0c19kYXRhW1siTGlQIl1dJEJpb1JlcGxpY2F0ZSksICIsIiwKICAgICAgIlRyUCBCaW9SZXBsaWNhdGUgbm9tZW5jbGF0dXJlOiIsdW5pcXVlKG1zc3RhdHNfZGF0YVtbIlRyUCJdXSRCaW9SZXBsaWNhdGUpKQpgYGAKCkFkanVzdCBgQmlvUmVwbGljYXRlYCBjb2x1bW4gdG8gY29ycmVjdCBub21lbmNsYXR1cmUgZm9yIGEgQ2FzZS1jb250cm9sIGV4cGVyaW1lbnQuIAoKYGBge3IgQ29ycmVjdCByZXBsaWNhdGUgbm9tZW5jbGF0dXJlLCBlY2hvPVRSVUUsaW5jbHVkZT1UUlVFfQptc3N0YXRzX2RhdGFbWyJMaVAiXV0gPSBtc3N0YXRzX2RhdGFbWyJMaVAiXV0gJT4lIAogIG11dGF0ZShCaW9SZXBsaWNhdGUgPSBwYXN0ZTAoQ29uZGl0aW9uLCIuIixCaW9SZXBsaWNhdGUpKQoKbXNzdGF0c19kYXRhW1siVHJQIl1dID0gbXNzdGF0c19kYXRhW1siVHJQIl1dICU+JSAKICBtdXRhdGUoQmlvUmVwbGljYXRlID0gcGFzdGUwKENvbmRpdGlvbiwiLiIsQmlvUmVwbGljYXRlKSkKYGBgCgpJbnNwZWN0IGNvcnJlY3RlZCBgQmlvUmVwbGljYXRlYCBjb2x1bW4uIAoKYGBge3IgRGlzcGxheSBjb3JyZWN0ZWQgQmlvUmVwbGljYXRlIG5vbWVuY2xhdHVyZSwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KcGFzdGUoIkxpUCBCaW9SZXBsaWNhdGUgbm9tZW5jbGF0dXJlOiIsIHVuaXF1ZShtc3N0YXRzX2RhdGFbWyJMaVAiXV0kQmlvUmVwbGljYXRlKSwgIiwiLAogICAgICAiVHJQIEJpb1JlcGxpY2F0ZSBub21lbmNsYXR1cmU6Iix1bmlxdWUobXNzdGF0c19kYXRhW1siVHJQIl1dJEJpb1JlcGxpY2F0ZSkpCmBgYAoKIyMgMi40IERhdGEgU3VtbWFyaXphdGlvbnsjZGF0YS1zdW1tfQoKU3VtbWFyaXplIHRoZSBkYXRhLiBUaGUgZGVmYXVsdCBzZXR0aW5ncyB1c2UgYSBsb2cyLXRyYW5zZm9ybWF0aW9uIGFuZCBub3JtYWxpemUgdGhlIGRhdGEgdXNpbmcgdGhlICBgImVxdWFsaXplTWVkaWFucyJgIG1ldGhvZC4gVGhlIGRlZmF1bHQgc3VtbWFyeSBtZXRob2QgaXMgYCJUTVAiYCBhbmQgaW1wdXRhdGlvbiBpcyBzZXQgdG8gYCJGQUxTRSJgLiBGb3IgZGV0YWlsZWQgaW5mb3JtYXRpb24gb24gYWxsIHNldHRpbmdzLCBwbGVhc2UgcmVmZXIgdG8gdGhlIGZ1bmN0aW9uIHZpZ25ldHRlLiAKClRoaXMgZnVuY3Rpb24gd2lsbCB0YWtlIHNvbWUgdGltZSBhbmQgbWVtb3J5LiBJZiBtZW1vcnkgaXMgbGltaXRlZCwgaXQgaXMgYWR2aXNhYmxlIHRvIHJlbW92ZSB0aGUgcmF3IGZpbGVzIHVzaW5nIHRoZSBgcm0oKWAgZnVuY3Rpb24gYW5kIGNsZWFyaW5nIHRoZSBtZW1vcnkgY2FjaGUgdXNpbmcgdGhlIGBnYygpYCBmdW5jdGlvbi4KCmBgYHtyIERhdGEgc3VtbWFyaXphdGlvbiwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KTVNzdGF0c0xpUF9TdW1tYXJpemVkIDwtIGRhdGFTdW1tYXJpemF0aW9uTGlQKG1zc3RhdHNfZGF0YSwgbm9ybWFsaXphdGlvbi5MaVAgPSAiZXF1YWxpemVNZWRpYW5zIikKCmBgYAoKSW5zcGVjdCBgTVNzdGF0c0xpUF9TdW1tYXJpemVkYC4gCmBgYHtyIEluc3BlY3Qgc3VtbWFyaXplZCBkYXRhLCBlY2hvPVRSVUUsaW5jbHVkZT1UUlVFfQpuYW1lcyhNU3N0YXRzTGlQX1N1bW1hcml6ZWRbWyJMaVAiXV0pCgpoZWFkKE1Tc3RhdHNMaVBfU3VtbWFyaXplZFtbIkxpUCJdXSRGZWF0dXJlTGV2ZWxEYXRhKQpoZWFkKE1Tc3RhdHNMaVBfU3VtbWFyaXplZFtbIkxpUCJdXSRQcm90ZWluTGV2ZWxEYXRhKQoKaGVhZChNU3N0YXRzTGlQX1N1bW1hcml6ZWRbWyJUclAiXV0kRmVhdHVyZUxldmVsRGF0YSkKaGVhZChNU3N0YXRzTGlQX1N1bW1hcml6ZWRbWyJUclAiXV0kUHJvdGVpbkxldmVsRGF0YSkKYGBgCgpTYXZlIGFuZC9vciBsb2FkIHN1bW1hcml6ZWQgZGF0YS4gCmBgYHtyIFNhdmUgc3VtbWFyaXplZCBkYXRhLCBlY2hvPVRSVUUsaW5jbHVkZT1UUlVFLCBldmFsPUZBTFNFfQpzYXZlKE1Tc3RhdHNMaVBfU3VtbWFyaXplZCwgZmlsZSA9ICdNU3N0YXRzTGlQX3N1bW1hcml6ZWQucmRhJykKbG9hZChmaWxlID0gJ01Tc3RhdHNMaVBfc3VtbWFyaXplZC5yZGEnKQpgYGAKCiMgMy4gTW9kZWxsaW5nCgpSdW4gdGhlIG1vZGVsaW5nIHRvIG9idGFpbiBzaWduaWZpY2FudGx5IGFsdGVyZWQgcGVwdGlkZXMgYW5kIHByb3RlaW5zLiBUaGUgZnVuY3Rpb24gYGdyb3VwQ29tcGFyaXNvbkxpUGBvdXRwdXRzIGEgbGlzdCB3aXRoIHRocmVlIHNlcGFyYXRlIG1vZGVsczogMS4gYExpUC5Nb2RlbGAsIHdoaWNoIGNvbnRhaW5zIHRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgb24gcGVwdGlkZSBsZXZlbCBpbiB0aGUgTGlQIHNhbXBsZSB3aXRob3V0IGNvcnJlY3Rpb24gZm9yIHByb3RlaW4gYWJ1bmRhbmNlIGFsdGVyYXRpb25zLiAyLiBgQWRqdXN0ZWQuTGlQLk1vZGVsYCwgd2hpY2ggY29udGFpbnMgdGhlIGRpZmZlcmVudGlhbCBhbmFseXNpcyBvbiBwZXB0aWRlIGxldmVsIGluIHRoZSBMaVAgc2FtcGxlIHdpdGggY29ycmVjdGlvbiBmb3IgcHJvdGVpbiBhYnVuZGFuY2UgYWx0ZXJhdGlvbnMgMy4gYFRyUC5Nb2RlbGAsIHdoaWNoIGNvbnRhaW5zIHRoZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgb24gcHJvdGVpbiBsZXZlbC4gVGhlIGRlZmF1bHQgc2V0dGluZyBvZiB0aGUgZnVuY3Rpb24gaXMgYSBwYWlyd2lzZSBjb21wYXJpc29uIG9mIGFsbCBleGlzdGluZyBncm91cHMuIEFsdGVybmF0aXZlbHksIGEgY29udHJhc3QgbWF0cml4IGNhbiBiZSBwcm92aWRlZCB0byBzcGVjaWZ5IHRoZSBjb21wYXJpc29ucyBvZiBpbnRlcmVzdC4gU2VlIFZpZ25ldHRlIGZvciBkZXRhaWxzLgoKYGBge3IgTW9kZWxsaW5nLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsaW5jbHVkZT1UUlVFfQpNU3N0YXRzTGlQX21vZGVsID0gZ3JvdXBDb21wYXJpc29uTGlQKE1Tc3RhdHNMaVBfU3VtbWFyaXplZCkKCmBgYAoKSW5zcGVjdCBgTVNzdGF0c0xpUF9tb2RlbGAuIApgYGB7ciBJbnNwZWN0IG1vZGVsLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsaW5jbHVkZT1UUlVFfQpoZWFkKE1Tc3RhdHNMaVBfbW9kZWxbWyJMaVAuTW9kZWwiXV0pCmhlYWQoTVNzdGF0c0xpUF9tb2RlbFtbIlRyUC5Nb2RlbCJdXSkKYGBgCgpTYXZlIGFuZC9vciBsb2FkIG1vZGVsIGRhdGEuIApgYGB7ciBTYXZlIG1vZGVsLCBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGluY2x1ZGU9VFJVRSwgZXZhbD1GQUxTRX0Kc2F2ZShNU3N0YXRzTGlQX21vZGVsLCBmaWxlID0gJ01Tc3RhdHNMaVBfbW9kZWwucmRhJykKbG9hZChmaWxlID0gJ01Tc3RhdHNMaVBfbW9kZWwucmRhJykKYGBgCgojIDQuIENhbGN1bGF0ZSBwcm90ZW9seXRpYyByZXNpc3RhbmNlIHJhdGlvcwoKUHJvdGVvbHl0aWMgcmVzaXN0YW5jZSByYXRpb3MgYXJlIGNhbGN1bGF0ZWQgYXMgdGhlIHJhdGlvIG9mIHRoZSBpbnRlbnNpdHkgb2YgZnVsbHkgdHJ5cHRpYyBwZXB0aWRlcyBpbiB0aGUgTGlQIGNvbmRpdGlvbiB0byB0aGUgVHJQIGNvbmRpdGlvbi4gSW4gZ2VuZXJhbCwgYSBsb3cgcHJvdGVhc2UgcmVzaXN0YW5jZSB2YWx1ZSBpcyBpbmRpY2F0aXZlIG9mIGhpZ2ggZXh0ZW50IG9mIGNsZWF2YWdlLCB3aGlsZSBoaWdoIHByb3RlYXNlIHJlc2lzdGFuY2UgdmFsdWVzIGluZGljYXRlIGxvdyBjbGVhdmFnZSBleHRlbnQuCgpgYGB7ciBjYWxjdWxhdGUgYWNjZXNzaWJpbGl0eSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KCkFjY2Vzc2liaWxpdHkgPSBjYWxjdWxhdGVQcm90ZW9seXRpY1Jlc2lzdGFuY2UoTVNzdGF0c0xpUF9TdW1tYXJpemVkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYXN0YV9maWxlLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaWZmZXJlbnRpYWxfYW5hbHlzaXMgPSBUUlVFKQoKQWNjZXNzaWJpbGl0eSRSdW5MZXZlbERhdGEKCmBgYAoKYGBge3IgQmFycGxvdCBvZiBwcm90ZWFzZSByZXNpc3RhbmNlIG9mIGFTeW51Y2xlaW4gKG1vbm9tZXIgLSBNIGFuZCBmaWJyaWwgLSBGKSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9MTAsZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KClJlc2lzdGFuY2VCYXJjb2RlUGxvdExpUChBY2Nlc3NpYmlsaXR5LAogICAgICAgICAgICAgICAgICAgICAgICAgZmFzdGFfZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLnByb3QgPSAiUDE2NjIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLmNvbmRpdGlvbiA9ICJGMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZGRyZXNzID0gRkFMU0UpCgoKYGBgCgoKIyA1LiBQcm90ZW9seXRpYyByZXNpc3RhbmNlIGRpZmZlcmVudGlhbCBhbmFseXNpcwoKSW4gdGhpcyBwYXJhZ3JhcGggd2UgZGVzY3JpYmVkIGhvdyB0byBjb21wYXJlIHByb3Rlb2x5dGljIHJlc2lzdGFuY2UgcGF0dGVybnMgb2YgZGlmZmVyZW50IGNvbmRpdGlvbnMsIGFzIHJlcG9ydGVkIGluIENhcHBlbGxldHRpIGV0IGFsLiwgMjAyMSwgRmlndXJlIDMuIEFzIGRlc2NyaWJlZCBpbiB0aGUgIlByb3RlYXNlIGRpZ2VzdGlvbiBhY2Nlc3NpYmlsaXR5IGFuYWx5c2lzIiBwYXJhZ3JhcGggb2YgQ2FwcGVsbGV0dGkgZXQgYWwuLCBwcm90ZW9seXRpYyByZXNpc3RhbmNlIGlzIGNhbGN1bGF0ZWQgYXMgdGhlIHJhdGlvIG9mIHRoZSBpbnRlbnNpdHkgb2YgZnVsbHkgdHJ5cHRpYyBwZXB0aWRlcyBpbiB0aGUgTGlQIGNvbmRpdGlvbiB0byB0aGUgVHJQIGNvbmRpdGlvbiBhbmQgY2FuIGJlIGNvbXBhcmVkIGFjcm9zcyBkaWZmZXJlbnQgY29uZGl0aW9ucyB1c2luZyB0aGUgbGluZWFyIG1peGVkIGVmZmVjdHMgbW9kZWxzLWJhc2VkIGRpZmZlcmVudGlhbCBhbmFseXNpcyBpbXBsZW1lbnRlZCBpbiB0aGUgTVNzdGF0c0xpUCBwYWNrYWdlLiBGaXJzdCwgaW5maW5pdGUgdmFsdWVzIGFyZSBmaWx0ZXJlZCBvdXQgZnJvbSB0aGUgcmVzdWx0IG9mIHRoZSBncm91cENvbXBhcmlzb25MaVAgZnVuY3Rpb24uIE5leHQsIGxvZ0ZDcyBhbmQgc3RhbmRhcmQgZXJyb3JzIG9mIHRoZSBMaVAgKGxvZzJGQywgczIpIGFuZCBUclAgKGxvZzJGQ19yZWYsczJfcmVmKSBtb2RlbHMgYXJlIGNvbWJpbmVkIGFuZCBTdHVkZW504oCZcyBULXRlc3QgaXMgYXBwbGllZCB0byBjb21wYXJlIHByb3Rlb2x5dGljIHJlc2lzdGFuY2UgYmV0d2VlbiBkaWZmZXJlbnQgY29uZGl0aW9ucy5GaW5hbGx5LCBwLXZhbHVlcyBhcmUgYWRqdXN0ZWQgZm9yIG11bHRpcGxlIGNvbXBhcmlzb25zIChkZWZhdWx0IGlzIEJlbmphbWluaSAmIEhvY2hiZXJnIG1ldGhvZCkuCkluIGdlbmVyYWwsIGEgbG93IFByb3Rlb2x5dGljIHJlc2lzdGFuY2UgdmFsdWUgaXMgaW5kaWNhdGl2ZSBvZiBoaWdoIGV4dGVudCBvZiBjbGVhdmFnZSwgd2hpbGUgaGlnaCBQcm90ZW9seXRpYyByZXNpc3RhbmNlIHZhbHVlcyBpbmRpY2F0ZSBsb3cgY2xlYXZhZ2UgZXh0ZW50LgoKCmBgYHtyIFBlcmZvcm0gUHJvdGVvbHl0aWMgcmVzaXN0YW5jZSBkaWZmZXJlbnRpYWwgYW5hbHlzaXMgb24gRnVsbHkgdHJ5cHRpYyBwZXB0aWRlcywgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KCkFjY2Vzc2liaWxpdHkkZ3JvdXBDb21wYXJpc29uCgpgYGAKClNhdmUgYW5kL29yIGxvYWQgbW9kZWwgZGF0YQpgYGB7ciBTYXZlIHByb3RlY3Rpb24gbW9kZWwsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1UUlVFLCBldmFsPUZBTFNFfQojIHNhdmUoRnVsbHlUcnAuTW9kZWwsIGZpbGUgPSAnUHJvdGVjdGlvbl9tb2RlbC5yZGEnKQojIGxvYWQoZmlsZSA9ICdQcm90ZWN0aW9uX21vZGVsLnJkYScpCmBgYAoKIyA2LiBTYXZlIG91dHB1dHMKClNhdmUgdGhlIG91dHB1dCBvZiB0aGUgbW9kZWxpbmcgaW4gYSAuY3N2IGZpbGUuIApgYGB7ciBTYXZlIG91dHB1dCwgZWNobz1UUlVFLGluY2x1ZGU9VFJVRSwgZXZhbD1GQUxTRX0KIyB3cml0ZS5jc3YoRnVsbHlUcnAuTW9kZWwsICJQcm90ZW9seXRpY19yZXNpc3RhbmNlX0RBLmNzdiIpCmBgYAoKIyA3LiBQbG90IGFTeW51Y2xlaW4gcHJvdGVvbHl0aWMgcmVzaXN0YW5jZSBEQSByZXN1bHQgYXMgYmFyY29kZQoKUHJvdGVvbHl0aWMgcmVzaXN0YW5jZSBiYXJjb2RlcyBjYW4gYmUgdXNlZCB0byB2aXN1YWxpemUgRlQgcGVwdGlkZXMgYWxvbmcgdGhlIHNlcXVlbmNlIG9mIGFTeW51Y2VsaW4uIFNpZ25pZmljYW50IHBlcHRpZGVzIHNob3dpbmcgaGlnaCBwcm90ZWFzZSByZXNpc3RhbmNlIGFyZSBjb2xvcmVkIGluIHJlZCwgc2lnbmlmaWNhbnQgcGVwdGlkZXMgc2hvd2luZyBhIGRlY3JlYXNlZCBwcm90ZWFzZSByZXNpc3RhbmNlIGFyZSBjb2xvcmVkIGluIGJsdWUgYW5kIG5vbi1zaWduaWZpY2FudCBwZXB0aWRlcyAobm8gY2hhbmdlIGluIHByb3RlYXNlIHJlc2lzdGFuY2UgYmV0d2VlbiBjb25kaXRpb25zKSBhcmUgY29sb3JlZCBpbiBncmV5LiBCbGFjayByZWdpb25zIHJlcHJlc2VudCByZWdpb25zIHdpdGggbm8gaWRlbnRpZmllZCBtYXRjaGluZyBwZXB0aWRlLiBQb3NpdGlvbiBvZiB0aGUgTkFDIGRvbWFpbiBpcyBpbmRpY2F0ZWQgYnkgYSByZWN0YW5nbGUuCgoKYGBge3IgQmFycGxvdCBvZiBEQSBvZiBwcm90ZWFzZSByZXNpc3RhbmNlIG9mIGFTeW51Y2xlaW4gKG1vbm9tZXIgLSBNIGFuZCBmaWJyaWwgLSBGKSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZmlnLmhlaWdodD0yLCBmaWcud2lkdGg9MTAsZWNobz1UUlVFLGluY2x1ZGU9VFJVRX0KClJlc2lzdGFuY2VCYXJjb2RlUGxvdExpUChBY2Nlc3NpYmlsaXR5LAogICAgICAgICAgICAgICAgICAgICAgICAgZmFzdGFfZmlsZSwKICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLnByb3QgPSAiUDE2NjIyIiwKICAgICAgICAgICAgICAgICAgICAgICAgIHdoaWNoLmNvbmRpdGlvbiA9ICJGMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICBkaWZmZXJlbnRpYWxfYW5hbHlzaXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgd2hpY2guY29tcCA9ICJGMSB2cyBGMiIsCiAgICAgICAgICAgICAgICAgICAgICAgICBhZGRyZXNzID0gRkFMU0UpCgoKYGBgCg==