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.

1. Preparation

 input_folder=choose.dir(caption="Choose the working directory")
 knitr::opts_knit$set(root.dir = input_folder) 
setwd(input_folder)
load(file = 'MSstatsLiP_Summarized.rda')
setwd(input_folder)
load(file = 'MSstatsLiP_model.rda')
fasta_file=choose.files(caption = "Choose FASTA file")

2. Quality control

We assess the quality of the acquired data to judge whether the experiment has been conducted correctly.

2.1 Intensity distribution

First, visualize the distribution of intensities in both, the LiP and the TrP samples. The function dataProcessPlotsLiP with the argument type="QCPLOT can be used to visualize the distribution of peptide intensities in both data sets. The argument which.Proteincan be used to select specific proteins, the default setting is "allonly", which visualizes all peptide intensities in a run.

dataProcessPlotsLiP(MSstatsLiP_Summarized,
                    type = 'QCPLOT',
                    address = FALSE,
                    which.Peptide = "allonly",
                    lip.title = "All LiP Peptides",
                    protein.title = "All Proteins")

Additionally, the function dataProcessPlotsLiP can be used to visualize profile plots using the argument type = "ProfilePlot". The argument which.Protein can be used to select a peptide of interest, if "all"is set, the profile plots for all peptides and corresponding proteins.

# dataProcessPlotsLiP(MSstatsLiP_Summarized,
#                     type = 'ProfilePlot',
#                     which.Peptide = c("P37840_AATGFVKK"),
#                     which.Protein=c("P37840"),
#                     ylimUp=FALSE,
#                     ylimDown=10,
#                     address = FALSE)

2.2 Coefficient of variation

Calculate and plot the coefficient of variation (CV) in each group GROUP. The CV is calculated on feature level FEATURE as the standard deviation over the mean of the non-transformed feature intensity INTENSITY.
The plot is based on ggplot and can be modified at convenience. The median CV can be displayed the the plot using the stat_summary function. a) LiP samples

MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
    group_by(FEATURE, GROUP) %>% 
    summarize(cv = sd(INTENSITY) / mean(INTENSITY)) %>% 
    ggplot(aes(x = GROUP, y = cv, fill = GROUP)) + 
    geom_violin() + 
    theme_bw()+
    labs(title = "Coefficient of Variation between Condtions",
         subtitle = "LiP samples",
         y = "Coefficient of Variation", 
         x = "Conditon")+    
    scale_fill_brewer(palette = "Paired")+
    stat_summary(fun.data=function(x){return(data.frame(y=0, label=round(median(x, na.rm = TRUE),4)))}, geom="text", hjust=0.5, vjust=1)

  1. TrP samples
MSstatsLiP_Summarized$TrP$FeatureLevelData%>%
    group_by(FEATURE, GROUP) %>% 
    summarize(cv = sd(INTENSITY) / mean(INTENSITY)) %>% 
    ggplot(aes(x = GROUP, y = cv, fill = GROUP)) + 
    geom_violin() + 
    theme_bw()+
    labs(title = "Coefficient of Variation between Condtions", 
         subtitle = "LiP samples",
         y = "Coefficient of Variation", 
         x = "Conditon")+    
    scale_fill_brewer(palette = "Paired")+
    stat_summary(fun.data=function(x){return(data.frame(y=0, label=round(median(x, na.rm = TRUE),4)))}, geom="text", hjust=0.5, vjust=1)

2.3 Trypticity distribution

Use the built-in trypticity plotting function trypticHistogramLiP() to visualize the distribution of fully- and half-tryptic peptides, defined as peptides with two tryptic ends (FT) and peptides with one tryptic end (HT).

trypticHistogramLiP(MSstatsLiP_Summarized, fasta_file,
                    color_scale = "grey",
                    address = FALSE)

The above definition of trypticity annotates terminal peptides as half-tryptic peptides. The function calculateTrypticity() can discriminate terminal peptides and calculates TRUE/FALSE values for the following characteristics:

value Description
fully_TRI TRUE: peptide is result of tryptic cleavage, FALSE: peptide has one non-tryptic end
NSEMI_TRI TRUE: peptide has the non-tryptic cleavage at the N-terminus
CSEMI_TRI TRUE: peptide has the non-tryptic cleavage at the C-terminus
CTERMINUS TRUE: peptide is at the C-terminus of the protein

Below we plot the trypticity distribution of each replicate SUBJECT in each group GROUP and we choose to visualize the distribution of fully-tryptic and half-tryptic peptides and the position of the non-tryptic end by assessing the variables fill=interaction(fully_TRI,NSEMI_TRI,CSEMI_TRI). The plot is based on ggplot and can be modified at convenience.

MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
  rename(PeptideSequence=PEPTIDE, ProteinName=PROTEIN)%>%
  mutate(PeptideSequence=substr(PeptideSequence, 1,nchar(as.character(PeptideSequence))-2))%>%
  full_join(.,calculateTrypticity(.,fasta_file))%>%
  ggplot()+
  geom_bar(aes(x=SUBJECT, fill=interaction(fully_TRI,NSEMI_TRI,CSEMI_TRI)),position="fill", color="black")+
  facet_grid(~GROUP, scales = "free_x")+
  labs(x="Replicate", 
       y="Trypticity content [%]", 
       fill="Trypticity", 
       title="Distribution of trypticity")+
  theme_bw()+
  scale_fill_grey(labels=c("TRUE.FALSE.FALSE"="fully tryptic", "FALSE.TRUE.FALSE"="N-term half tryptic","FALSE.FALSE.TRUE"="C-term half tryptic", "FALSE.FALSE.FALSE"=""))+
  scale_y_continuous(breaks = seq(0,1,0.2), labels = seq(0,100,20))

2.4 Protein and Peptide Identifications

Visualize the number of identified proteins and peptides in each replicate SUBJECT in each group GROUP. Note that we filter for identified peptides only by using !is.na(ABUNDANCE). The plots are based on ggplot and can be modified at convenience. The function grid.arrangefrom the gridExtra package arranges the plots next to each other.

  1. LiP samples
grid.arrange(
  MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
    filter(!is.na(ABUNDANCE))%>%
    group_by(GROUP,SUBJECT)%>%
    summarize(distinct_peptides=n_distinct(PEPTIDE))%>%
    ggplot(aes(x=SUBJECT, y=distinct_peptides))+
    geom_col(fill="white",color="black")+
    facet_grid(~GROUP, scales = "free")+
    theme_bw()+
    geom_text(aes(label=distinct_peptides), vjust=0, hjust=0.5)+
    labs(x="Replicate", 
         y="Number of identified peptides", 
         title="Number of identified peptides",
         subtitle="LiP sample"),
  
  MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
    filter(!is.na(ABUNDANCE))%>%
    group_by(GROUP,SUBJECT)%>%
    summarize(distinct_proteins=n_distinct(PROTEIN))%>%
    ggplot(aes(x=SUBJECT, y=distinct_proteins))+
    geom_col(fill="white",color="black")+
    facet_grid(~GROUP, scales = "free")+
    theme_bw()+
    geom_text(aes(label=distinct_proteins), vjust=0, hjust=0.5)+
    labs(x="Replicate", 
         y="Number of identified proteins", 
         title="Number of identified proteins",
         subtitle="LiP sample"),

  ncol=2)

  1. TrP samples
grid.arrange(
  MSstatsLiP_Summarized$TrP$FeatureLevelData%>%
    filter(!is.na(ABUNDANCE))%>%
    group_by(GROUP,SUBJECT)%>%
    summarize(distinct_peptides=n_distinct(PEPTIDE))%>%
    ggplot(aes(x=SUBJECT, y=distinct_peptides))+
    geom_col(fill="white",color="black")+
    facet_grid(~GROUP, scales = "free")+
    theme_bw()+
    geom_text(aes(label=distinct_peptides), vjust=0, hjust=0.5)+
    labs(x="Replicate", 
         y="Number of identified peptides", 
         title="Number of identified peptides",
         subtitle="TrP sample"),
  
  MSstatsLiP_Summarized$TrP$FeatureLevelData%>%
    filter(!is.na(ABUNDANCE))%>%
    group_by(GROUP,SUBJECT)%>%
    summarize(distinct_proteins=n_distinct(PROTEIN))%>%
    ggplot(aes(x=SUBJECT, y=distinct_proteins))+
    geom_col(fill="white",color="black")+
    facet_grid(~GROUP, scales = "free")+
    theme_bw()+
    geom_text(aes(label=distinct_proteins), vjust=0, hjust=0.5)+
    labs(x="Replicate", 
         y="Number of identified proteins", 
         title="Number of identified proteins",
         subtitle="TrP sample"),

  ncol=2)

3 Data distribution

We assess the distribution of the data, to ensure that the conditions to be compared separate well. Moreover, we can identify outlier or detect batch effects. In the following, we use the log2-transformed values ABUNDANCE. Alternatively, one can use the non-transformed values INTENSITY.

3.1 Correlation

We perform correlation analysis using pearson correlation and visualize the correlation coefficients in a tile map (as well as the distribution of the intensities in a scatter plot).

option 1: simple correlation graph. This tile map of correlation coefficients is produced by the build-in function correlationPlotLiP. It can be used with big data sets comprising large numbers of samples. The analysis is done on feature level.

correlationPlotLiP(MSstatsLiP_Summarized, address = FALSE)

option 2: Combined tile and scatter plot This combined tile and scatter plot is produced using the ggpairs function within the GGally package. Due to the complexity of the graph, it might require more computing power for large data sets. In addition to displaying the correlation coefficient, it allows assessment of the data distribution within the correlations. We can use the non-normalized intensity values INTENSITYor the log2-transformed values ABUNDANCE. The custom function color_fn sets the constrains for the color range used in the tile map. The default is min_val=0.5, but can be set within the plotting function.

color_fn <- function(data, mapping, method="p", use="complete.obs", min_val=0.5, ...){
              x <- eval_data_col(data, mapping$x)
              y <- eval_data_col(data, mapping$y)
              corr <- cor(x, y, method=method, use=use)
              colFn <- colorRampPalette(c("white", "steelblue"), interpolate ='spline')
              fill <- colFn(100)[findInterval(corr, seq(min_val, 1, length=100))]
              ggally_cor(data = data, mapping = mapping, color="black",method=method, use=use,...) + 
                theme_void() +
                theme(panel.background = element_rect(fill=fill))
}
  1. LiP samples
ggpairs(MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
          ungroup()%>%
          select(INTENSITY, SUBJECT, FEATURE)%>%
          spread(., SUBJECT, INTENSITY)%>%
          select(-FEATURE), 
   upper = list(continuous = wrap(color_fn, min_val=0.97)),
   lower = list(continuous = wrap("points", alpha=0.3)),
   diag = list(continuous = "densityDiag"))+
  ggtitle("LiP samples")

  1. TrP samples
ggpairs(MSstatsLiP_Summarized$TrP$FeatureLevelData%>%
          ungroup()%>%
          select(INTENSITY, SUBJECT, FEATURE)%>%
          spread(., SUBJECT, INTENSITY)%>%
          select(-FEATURE), 
   upper = list(continuous = wrap(color_fn, min_val=0.85)),
   lower = list(continuous = wrap("points", alpha=0.3)),
   diag = list(continuous = "densityDiag"))+
  ggtitle("TrP samples")

3.2 Dendrogram clustering

The dendrogram visualizes the hierarchical clustering of the pearson correlation coefficient. The plot is generated using the dendextent package.
Colored annotations are added using the colored_bars function. The color scheme is defined in dendogram_colors. Here, we define colors for the Condition. If the data set contains batch numbers, they can be assigned colors for visualization too. This helps to identify batch effects.

dendogram_colors=MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
  distinct(RUN,.keep_all=TRUE)%>%
  rowwise()%>%
  select(c(SUBJECT,GROUP))%>%
  arrange(., SUBJECT)%>%
  mutate(group_col=brewer.pal(nlevels(.$GROUP),name="Paired")[GROUP])
  1. LiP samples
par(mar = c(5, 5, 3, 7) + 0.1,
    xpd = NA)           
dend_L=MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
          select(ABUNDANCE, SUBJECT, FEATURE)%>%
          spread(., SUBJECT, ABUNDANCE)%>%
          select(-FEATURE)%>%
  cor(., method = "spearman", use="pairwise.complete.obs")%>%
  subtract(1)%>%
  multiply_by(-1)%>%
  as.dist()%>%
  hclust(method="ward.D2")%>%
  as.dendrogram()
dend_L%>%dendextend::set("labels_cex", 0.6)%>%plot(main="LiP samples")
colored_bars(colors = cbind(dendogram_colors$batch_col,dendogram_colors$group_col), dend = dend_L, rowLabels = c("Batch","Group"))

  1. TrP samples
dend_T=MSstatsLiP_Summarized$TrP$FeatureLevelData%>%
          select(ABUNDANCE, SUBJECT, FEATURE)%>%
          spread(., SUBJECT, ABUNDANCE)%>%
          select(-FEATURE)%>%
  cor(., method = "spearman", use="complete.obs")%>%
  subtract(1)%>%
  multiply_by(-1)%>%
  as.dist()%>%
  hclust(method="ward.D2")%>%
  as.dendrogram()
dend_T%>%dendextend::set("labels_cex", 0.6)%>%plot(main="TrP samples")
colored_bars(colors = cbind(dendogram_colors$batch_col,dendogram_colors$group_col), dend = dend_T, rowLabels = c("Batch","Group"))

3.3 Principal component analysis (PCA)

The Principal component analysis is an alternative way to assess how well the conditions are separated and how well the replicates cluster together. The principal component analysis is performed with the function prcomp from the standard stats package. The PCA is visualized using the ggbiplot function from the ggbiplot package.

  1. LiP samples
n_run=length(unique(MSstatsLiP_Summarized$LiP$FeatureLevelData$RUN))

LiP_PCA=MSstatsLiP_Summarized$LiP$FeatureLevelData%>%
  filter(!is.na(ABUNDANCE))%>%
  dplyr::group_by(FEATURE)%>%
  mutate(complete=n_distinct(RUN)==n_run)%>%
  filter(complete==TRUE)%>%
  select(ABUNDANCE, FEATURE, GROUP, RUN)%>%
  spread(., FEATURE, ABUNDANCE)

ggbiplot(prcomp(LiP_PCA%>%select(-c(GROUP,RUN))),ellipse=T,circle=T,var.axes=F,
              groups=as.factor(LiP_PCA$GROUP))+ 
  theme_bw()+
  labs(color="group", title="LiP samples")+
  scale_color_brewer(palette = "Paired")+
  xlim(-3,3)+
  ylim(-3,3)

  1. TrP samples
TrP_PCA=MSstatsLiP_Summarized$TrP$FeatureLevelData%>%
  group_by(FEATURE)%>%
  filter(!is.na(ABUNDANCE))%>%
  mutate(complete=n_distinct(RUN)==n_run)%>%
  filter(complete==TRUE)%>%
  select(ABUNDANCE, FEATURE, GROUP, RUN)%>%
  spread(., FEATURE, ABUNDANCE)

ggbiplot(prcomp(TrP_PCA%>%select(-c(GROUP,RUN))),ellipse=T,circle=T,var.axes=F,
              groups=as.factor(TrP_PCA$GROUP), alpha = 0)+ 
  geom_point(aes(color=TrP_PCA$GROUP), size=2)+
  theme_bw()+
  labs(color="group", title="TrP samples")+
  scale_color_brewer(palette = "Paired")+
  xlim(-3,3)+
  ylim(-3,3)

4 Statistical analysis

4.1 Volcano plots

Visualize the distribution of altered peptides and proteins using a volcano plot.

You can use the built-in function groupComparisonPlotsLiP() to create a series of different basic volcano plots. The argument type defines the type of plot, either volcano plot or heat map. The argument which.Protein gives the option to display peptides from only one protein, which.comparisona allows to filter for a comparison of interest. You can choose to display the name of the significant peptides as Protein_PeptideSequence with ProteinName=TRUE. The cut-off values for the q-value can be set using the argument sig, the default is sig=0.05, the cut-off value for the fold change is set using FCcutoff.

groupComparisonPlotsLiP(MSstatsLiP_model, 
                        type = "VolcanoPlot", 
                        ProteinName = FALSE,
                        address = FALSE)

For more control over the plot appearance, you can use the following code, based on ggplot and adjust it to convenience

Set the cutoff for the q-value as adj.pvalue.cutoff and the fold change as log2FC.cutoff.

adj.pvalue.cutoff=0.05
log2FC.cutoff=1

Using the function gghighlight from the gghighlight package, you can highlight peptides of a specific protein and color them by whether they are significant or not by using color=significant.
You can also use color=fully_TRI to color peptides by trypticity. Set the filter in gghighlight to significant=="Yes" if you only want to color significant peptides. Use significant=="Yes"&ProteinName=="P37840" to highlight the significant peptides of only one protein, in this case, P37840. Using the argument label_key=PepsideSequence and setting the argument use_direct_label=TRUE, you can add the peptide sequence to the plot. This is only advisable if few peptides are significant. Note that this will only work in a non-faceted plot. For displaying peptide sequence on a faceted plot, disable the gghighlight function and use geom_label_repel or geom_text_repel from the ggrepel package.

  1. LiP data, non-adjusted
MSstatsLiP_model$LiP.Model%>%
  filter(is.na(issue))%>%
  mutate(significant=case_when(abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff~"Yes",TRUE~"No"))%>%
  ggplot(aes(x=log2FC, y=-log10(adj.pvalue),color=significant))+
  geom_point()+
  gghighlight(ProteinName=="P32485", calculate_per_facet = TRUE, use_group_by=FALSE, use_direct_label = FALSE)+
  geom_hline(yintercept = -log10(adj.pvalue.cutoff))+
  geom_vline(xintercept = log2FC.cutoff)+
  geom_vline(xintercept = -log2FC.cutoff)+
  facet_grid(~Label, scales = "free")+
  theme_bw()+
  labs(title="Structurally altered peptides",
       subtitle="LiP sample, non-adjusted")+
  scale_color_manual(values = c("Yes"="darkred", "No"="black"))+
  guides(color=FALSE)

  1. LiP data, adjusted
MSstatsLiP_model$Adjusted.LiP.Model%>%
  filter(is.na(issue))%>%
  mutate(significant=case_when(abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff~"Yes",TRUE~"No"),
         direction=case_when(log2FC<0~"down",log2FC>0~"up"))%>%
  ggplot(aes(x=log2FC, y=-log10(adj.pvalue),color=significant))+
  geom_point()+
  gghighlight(ProteinName=="P32485", calculate_per_facet = TRUE, use_group_by=FALSE, use_direct_label = FALSE)+
  geom_hline(yintercept = -log10(adj.pvalue.cutoff))+
  geom_vline(xintercept = log2FC.cutoff)+
  geom_vline(xintercept = -log2FC.cutoff)+
  facet_grid(~Label, scales = "free")+
  theme_bw()+
  labs(title="Structurally altered peptides",
       subtitle="LiP sample")+
  scale_color_manual(values = c("Yes"="darkred", "No"="black"))+
  guides(color=FALSE)

Visualize deferentially abundant proteins in the data set

MSstatsLiP_model$TrP.Model%>%
  filter(is.na(issue))%>%
  mutate(significant=case_when(abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff~"Yes",TRUE~"No"),
         direction=case_when(log2FC<0~"down",log2FC>0~"up"))%>%
  ggplot(aes(x=log2FC, y=-log10(adj.pvalue),color=significant))+
  geom_point()+
  geom_label_repel(data=.%>%filter(significant=="Yes"), aes(x=log2FC, y=-log10(adj.pvalue), label=Protein), color="black", ylim=c(-log10(adj.pvalue.cutoff), NA))+
  geom_hline(yintercept = -log10(adj.pvalue.cutoff))+
  geom_vline(xintercept = log2FC.cutoff)+
  geom_vline(xintercept = -log2FC.cutoff)+
  facet_grid(~Label, scales = "free")+
  theme_bw()+
  labs(title="Differentiall abundant proteins",
       subtitle="TrP sample")+
  scale_color_manual(values = c("Yes"="darkred", "No"="black"))+
  guides(color=FALSE)

4.2 Quantification of altered proteins and peptides

Quantify the number of proteins and peptides with structural alterations in each comparison in the model Label. If not set in section 5.1, define the cut off values for the fold change log2FC.cutoffand q-value adj.pvalue.cutoff. Use filter(ProteinName=="P37840") to visualize the number of peptides for one particular protein of interest, here P37840.

adj.pvalue.cutoff=0.05
log2FC.cutoff=1
  1. LiP data, not adjusted
grid.arrange(
  MSstatsLiP_model$LiP.Model%>%
    filter(is.na(issue)&abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff)%>%
    group_by(Label)%>%
    summarize(distinct_peptides=n_distinct(PeptideSequence))%>%
    ggplot(aes(x=Label, y=distinct_peptides))+
    geom_col(fill="white",color="black")+
    theme_bw()+
    geom_text(aes(label=distinct_peptides), vjust=0, hjust=0.5)+
    labs(x="Comparison", 
         y="Number of peptides", 
         title="Structurally altered peptides",
         subtitle="LiP sample, non-adjusted"),
  
  MSstatsLiP_model$LiP.Model%>%
    filter(is.na(issue)&abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff)%>%
    group_by(Label)%>%
    summarize(distinct_proteins=n_distinct(ProteinName))%>%
    ggplot(aes(x=Label, y=distinct_proteins))+
    geom_col(fill="white",color="black")+
    theme_bw()+
    geom_text(aes(label=distinct_proteins), vjust=0, hjust=0.5)+
    labs(x="Comparison", 
         y="Number of proteins", 
         title="Structurally altered proteins",
         subtitle="LiP sample, non-adjusted"),
  nrow=1)

  1. LiP data, adjusted
grid.arrange(
  MSstatsLiP_model$Adjusted.LiP.Model%>%
    filter(is.na(issue)&abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff)%>%
    group_by(Label)%>%
    summarize(distinct_peptides=n_distinct(PeptideSequence))%>%
    ggplot(aes(x=Label, y=distinct_peptides))+
    geom_col(fill="white",color="black")+
    theme_bw()+
    geom_text(aes(label=distinct_peptides), vjust=0, hjust=0.5)+
    labs(x="Comparison", 
         y="Number of peptides", 
         title="Structurally altered peptides",
         subtitle="LiP sample, adjusted"),
  
  MSstatsLiP_model$Adjusted.LiP.Model%>%
    filter(is.na(issue)&abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff)%>%
    group_by(Label)%>%
    summarize(distinct_proteins=n_distinct(ProteinName))%>%
    ggplot(aes(x=Label, y=distinct_proteins))+
    geom_col(fill="white",color="black")+
    theme_bw()+
    geom_text(aes(label=distinct_proteins), vjust=0, hjust=0.5)+
    labs(x="Comparison", 
         y="Number of proteins", 
         title="Structurally altered proteins",
         subtitle="LiP sample, adjusted"),
  nrow=1)

Quantify the number of deferentially abundant proteins in the data set

MSstatsLiP_model$TrP.Model%>%
    filter(is.na(issue)&abs(log2FC)>log2FC.cutoff&adj.pvalue<adj.pvalue.cutoff)%>%
    group_by(Label)%>%
    summarize(distinct_proteins=n_distinct(Protein))%>%
    ggplot(aes(x=Label, y=distinct_proteins))+
    geom_col(fill="white",color="black")+
    theme_bw()+
    geom_text(aes(label=distinct_proteins), vjust=0, hjust=0.5)+
    labs(x="Comparison", 
         y="Number of proteins", 
         title="Differentially abundand proteins",
         subtitle="TrP sample")

4.3 Barcode plots

Visualize the positions of structurally altered peptides on the protein sequence using BarcodePlotLiP() function. The argument model_type can be used to define whether the adjusted model or the non-adjusted. The argument which.protein is used to define which protein to visualize. Use the argument FT.only=TRUEto visualize only fully tryptic peptides.

  1. LiP data, non-adjusted
BarcodePlotLiP(MSstatsLiP_model, fasta_file,model_type="Adjusted",which.prot = c("P32485"), address=FALSE, FT.only = TRUE)

LS0tDQp0aXRsZTogIlZpc3VhbGl6YXRpb24gb2YgYSBMaVAtTVMgZGF0YSBzZXQiDQphdXRob3I6ICJNYWxpbm92c2thIExpbGlhbmEgKDxtYWxpbm92c2thQGltc2IuYmlvbC5ldGh6LmNoPiksVmFsZW50aW5hIENhcHBlbGxldHRpICg8Y2FwcGVsbGV0dGlAaW1zYi5iaW9sLmV0aHouY2g+KSINCm91dHB1dDoNCiAgaHRtbF9ub3RlYm9vazoNCiAgICB0b2M6IHllcw0KICAgIGhpZ2hsaWdodDogcHlnbWVudHMNCiAgICB0aGVtZTogbHVtZW4NCi0tLQ0KDQpUaGlzIGlzIGFuIFtSIE1hcmtkb3duXShodHRwOi8vcm1hcmtkb3duLnJzdHVkaW8uY29tKSBOb3RlYm9vayBkZXNjcmliaW5nIHRoZSBhbmFseXNpcyBvZiBhIExpUC1NUyBleHBlcmltZW50IHVzaW5nIFtNU3N0YXRzTGlQXShodHRwczovL2dpdGh1Yi5jb20vVml0ZWstTGFiL01Tc3RhdHNMaVApLiBXaGVuIHlvdSBleGVjdXRlIGNvZGUgd2l0aGluIHRoZSBub3RlYm9vaywgdGhlIHJlc3VsdHMgYXBwZWFyIGJlbmVhdGggdGhlIGNvZGUuDQoNCiMgMS4gUHJlcGFyYXRpb24NCg0KLSBJbnN0YWxsIGFuZCBsb2FkIGFsbCBuZWNlc3NhcnkgcGFja2FnZXMuIFRoZSBpbnN0YWxsYXRpb24gbmVlZHMgdG8gcGVyZm9ybWVkIGF0IGZpcnN0IHVzZSBvbmx5LiBVbi1jb21tZW5kIHRoZSBsaW5lcyBmb3IgZXhlY3V0aW9uLg0KDQpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRSwgZWNobz1GQUxTRX0NCiBrbml0cjo6b3B0c19jaHVuayRzZXQoDQoJbWVzc2FnZSA9IEZBTFNFLA0KCXdhcm5pbmcgPSBGQUxTRSwNCglpbmNsdWRlID0gRkFMU0UNCikNCiMgaW5zdGFsbC5wYWNrYWdlcygiZGV2dG9vbHMiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJjaGVja21hdGUiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJmYWN0b2V4dHJhIikNCiMgaW5zdGFsbC5wYWNrYWdlcygiZ2doaWdobGlnaHQiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJncmlkRXh0cmEiKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJnb2V2ZWciKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJtYWdyaXR0ciIpDQojIGluc3RhbGwucGFja2FnZXMoJ2RlbmRleHRlbmQnKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCdtZXRhZm9saW8nKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCdjb3JycGxvdCcpDQojIGluc3RhbGwucGFja2FnZXMoJ0ZhY3RvTWluZVInKQ0KIyBpbnN0YWxsLnBhY2thZ2VzKCJHR2FsbHkiKQ0KIyBsaWJyYXJ5KGRldnRvb2xzKQ0KIyBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiTVNzdGF0c1BUTSIpDQojIA0KIyBkZXZ0b29sczo6aW5zdGFsbF9naXRodWIoIlZpdGVrLUxhYi9NU3N0YXRzTGlQIikNCiMgaW5zdGFsbF9naXRodWIoInZxdi9nZ2JpcGxvdCIpDQpsaWJyYXJ5KE1Tc3RhdHNMaVApDQpsaWJyYXJ5KGdnaGlnaGxpZ2h0KQ0KbGlicmFyeShncmlkKQ0KbGlicmFyeShncmlkRXh0cmEpDQpsaWJyYXJ5KG1hZ3JpdHRyKQ0KbGlicmFyeShkZW5kZXh0ZW5kKQ0KbGlicmFyeShHR2FsbHkpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZ2diaXBsb3QpDQpsaWJyYXJ5KGRwbHlyKQ0KbGlicmFyeShkYXRhLnRhYmxlKQ0KbGlicmFyeShnZ3JlcGVsKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KLSBTZXQgdGhlIHdvcmtpbmcgZGlyZWN0b3J5DQpOYXZpZ2F0ZSB0byB0aGUgZm9sZGVyIHdoZXJlIHRoZSBzdW1tYXJpemVkIGRhdGEgc2V0IGFuZCB0aGUgTVNzdGF0c0xpUCBtb2RlbCBpcyBkZXBvc2l0ZWQuIA0KDQpgYGB7ciBzZXQgd29ya2luZyBkcmVjdG9yeSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQogaW5wdXRfZm9sZGVyPWNob29zZS5kaXIoY2FwdGlvbj0iQ2hvb3NlIHRoZSB3b3JraW5nIGRpcmVjdG9yeSIpDQoga25pdHI6Om9wdHNfa25pdCRzZXQocm9vdC5kaXIgPSBpbnB1dF9mb2xkZXIpIA0KYGBgDQoNCi0gTG9hZCB0aGUgc3VtbWFyaXplZCBkYXRhIHNldC4gDQoNCmBgYHtyIGxvYWQgc3VtbWFyaXplZCBkYXRhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCnNldHdkKGlucHV0X2ZvbGRlcikNCmxvYWQoZmlsZSA9ICdNU3N0YXRzTGlQX1N1bW1hcml6ZWQucmRhJykNCmBgYA0KDQotIExvYWQgdGhlIE1Tc3RhdHNMaVAgbW9kZWwgY29udGFpbmluZyB0aGUgcmVzdWx0cyBvZiB0aGUgZGlmZmVyZW50aWFsIGFuYWx5c2lzLiANCg0KYGBge3IgbG9hZCBtb2RlbCBkYXRhLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCnNldHdkKGlucHV0X2ZvbGRlcikNCmxvYWQoZmlsZSA9ICdNU3N0YXRzTGlQX21vZGVsLnJkYScpDQpgYGANCg0KLSBMb2FkIHRoZSBmYXN0YSBmaWxlLg0KDQpgYGB7ciBsb2FkIGZhdGEgZmlsZSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpmYXN0YV9maWxlPWNob29zZS5maWxlcyhjYXB0aW9uID0gIkNob29zZSBGQVNUQSBmaWxlIikNCmBgYA0KDQojIDIuIFF1YWxpdHkgY29udHJvbA0KDQpXZSBhc3Nlc3MgdGhlIHF1YWxpdHkgb2YgdGhlIGFjcXVpcmVkIGRhdGEgdG8ganVkZ2Ugd2hldGhlciB0aGUgZXhwZXJpbWVudCBoYXMgYmVlbiBjb25kdWN0ZWQgY29ycmVjdGx5Lg0KDQojIyAyLjEgSW50ZW5zaXR5IGRpc3RyaWJ1dGlvbg0KDQpGaXJzdCwgdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgaW50ZW5zaXRpZXMgaW4gYm90aCwgdGhlIExpUCBhbmQgdGhlIFRyUCBzYW1wbGVzLiBUaGUgZnVuY3Rpb24gYGRhdGFQcm9jZXNzUGxvdHNMaVBgIHdpdGggdGhlIGFyZ3VtZW50IGB0eXBlPSJRQ1BMT1RgIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSB0aGUgZGlzdHJpYnV0aW9uIG9mIHBlcHRpZGUgaW50ZW5zaXRpZXMgaW4gYm90aCBkYXRhIHNldHMuIFRoZSBhcmd1bWVudCBgd2hpY2guUHJvdGVpbmBjYW4gYmUgdXNlZCB0byBzZWxlY3Qgc3BlY2lmaWMgcHJvdGVpbnMsIHRoZSBkZWZhdWx0IHNldHRpbmcgaXMgYCJhbGxvbmx5ImAsIHdoaWNoIHZpc3VhbGl6ZXMgYWxsIHBlcHRpZGUgaW50ZW5zaXRpZXMgaW4gYSBydW4uDQoNCmBgYHtyIFBsb3QgUUNQbG90LCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFLCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCmRhdGFQcm9jZXNzUGxvdHNMaVAoTVNzdGF0c0xpUF9TdW1tYXJpemVkLA0KICAgICAgICAgICAgICAgICAgICB0eXBlID0gJ1FDUExPVCcsDQogICAgICAgICAgICAgICAgICAgIGFkZHJlc3MgPSBGQUxTRSwNCiAgICAgICAgICAgICAgICAgICAgd2hpY2guUGVwdGlkZSA9ICJhbGxvbmx5IiwNCiAgICAgICAgICAgICAgICAgICAgbGlwLnRpdGxlID0gIkFsbCBMaVAgUGVwdGlkZXMiLA0KICAgICAgICAgICAgICAgICAgICBwcm90ZWluLnRpdGxlID0gIkFsbCBQcm90ZWlucyIpDQpgYGANCg0KQWRkaXRpb25hbGx5LCB0aGUgZnVuY3Rpb24gYGRhdGFQcm9jZXNzUGxvdHNMaVBgIGNhbiBiZSB1c2VkIHRvIHZpc3VhbGl6ZSBwcm9maWxlIHBsb3RzIHVzaW5nIHRoZSBhcmd1bWVudCBgdHlwZSA9ICJQcm9maWxlUGxvdCJgLiBUaGUgYXJndW1lbnQgYHdoaWNoLlByb3RlaW5gIGNhbiBiZSB1c2VkIHRvIHNlbGVjdCBhIHBlcHRpZGUgb2YgaW50ZXJlc3QsIGlmIGAiYWxsImBpcyBzZXQsIHRoZSBwcm9maWxlIHBsb3RzIGZvciBhbGwgcGVwdGlkZXMgYW5kIGNvcnJlc3BvbmRpbmcgcHJvdGVpbnMuDQoNCmBgYHtyIFByb2ZpbGUgUGxvdHMsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xNX0NCiMgZGF0YVByb2Nlc3NQbG90c0xpUChNU3N0YXRzTGlQX1N1bW1hcml6ZWQsDQojICAgICAgICAgICAgICAgICAgICAgdHlwZSA9ICdQcm9maWxlUGxvdCcsDQojICAgICAgICAgICAgICAgICAgICAgd2hpY2guUGVwdGlkZSA9IGMoIlAzNzg0MF9BQVRHRlZLSyIpLA0KIyAgICAgICAgICAgICAgICAgICAgIHdoaWNoLlByb3RlaW49YygiUDM3ODQwIiksDQojICAgICAgICAgICAgICAgICAgICAgeWxpbVVwPUZBTFNFLA0KIyAgICAgICAgICAgICAgICAgICAgIHlsaW1Eb3duPTEwLA0KIyAgICAgICAgICAgICAgICAgICAgIGFkZHJlc3MgPSBGQUxTRSkNCmBgYA0KDQojIyAyLjIgQ29lZmZpY2llbnQgb2YgdmFyaWF0aW9uDQoNCkNhbGN1bGF0ZSBhbmQgcGxvdCB0aGUgY29lZmZpY2llbnQgb2YgdmFyaWF0aW9uIChDVikgaW4gZWFjaCBncm91cCBgR1JPVVBgLiBUaGUgQ1YgaXMgY2FsY3VsYXRlZCBvbiBmZWF0dXJlIGxldmVsIGBGRUFUVVJFYCBhcyB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG92ZXIgdGhlIG1lYW4gb2YgdGhlIG5vbi10cmFuc2Zvcm1lZCBmZWF0dXJlIGludGVuc2l0eSBgSU5URU5TSVRZYC5cDQpUaGUgcGxvdCBpcyBiYXNlZCBvbiBnZ3Bsb3QgYW5kIGNhbiBiZSBtb2RpZmllZCBhdCBjb252ZW5pZW5jZS4gVGhlIG1lZGlhbiBDViBjYW4gYmUgZGlzcGxheWVkIHRoZSB0aGUgcGxvdCB1c2luZyB0aGUgYHN0YXRfc3VtbWFyeWAgZnVuY3Rpb24uIGEpIExpUCBzYW1wbGVzDQoNCmBgYHtyIENhbGN1bGF0ZSBhbmQgcGxvdCBDViBMaVAsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpNU3N0YXRzTGlQX1N1bW1hcml6ZWQkTGlQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgICBncm91cF9ieShGRUFUVVJFLCBHUk9VUCkgJT4lIA0KICAgIHN1bW1hcml6ZShjdiA9IHNkKElOVEVOU0lUWSkgLyBtZWFuKElOVEVOU0lUWSkpICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBHUk9VUCwgeSA9IGN2LCBmaWxsID0gR1JPVVApKSArIA0KICAgIGdlb21fdmlvbGluKCkgKyANCiAgICB0aGVtZV9idygpKw0KICAgIGxhYnModGl0bGUgPSAiQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIGJldHdlZW4gQ29uZHRpb25zIiwNCiAgICAgICAgIHN1YnRpdGxlID0gIkxpUCBzYW1wbGVzIiwNCiAgICAgICAgIHkgPSAiQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIiwgDQogICAgICAgICB4ID0gIkNvbmRpdG9uIikrICAgIA0KICAgIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiUGFpcmVkIikrDQogICAgc3RhdF9zdW1tYXJ5KGZ1bi5kYXRhPWZ1bmN0aW9uKHgpe3JldHVybihkYXRhLmZyYW1lKHk9MCwgbGFiZWw9cm91bmQobWVkaWFuKHgsIG5hLnJtID0gVFJVRSksNCkpKX0sIGdlb209InRleHQiLCBoanVzdD0wLjUsIHZqdXN0PTEpDQpgYGANCg0KYikgIFRyUCBzYW1wbGVzDQoNCmBgYHtyIENhbGN1bGF0ZSBhbmQgcGxvdCBDViBUclAsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTcuNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpNU3N0YXRzTGlQX1N1bW1hcml6ZWQkVHJQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgICBncm91cF9ieShGRUFUVVJFLCBHUk9VUCkgJT4lIA0KICAgIHN1bW1hcml6ZShjdiA9IHNkKElOVEVOU0lUWSkgLyBtZWFuKElOVEVOU0lUWSkpICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBHUk9VUCwgeSA9IGN2LCBmaWxsID0gR1JPVVApKSArIA0KICAgIGdlb21fdmlvbGluKCkgKyANCiAgICB0aGVtZV9idygpKw0KICAgIGxhYnModGl0bGUgPSAiQ29lZmZpY2llbnQgb2YgVmFyaWF0aW9uIGJldHdlZW4gQ29uZHRpb25zIiwgDQogICAgICAgICBzdWJ0aXRsZSA9ICJMaVAgc2FtcGxlcyIsDQogICAgICAgICB5ID0gIkNvZWZmaWNpZW50IG9mIFZhcmlhdGlvbiIsIA0KICAgICAgICAgeCA9ICJDb25kaXRvbiIpKyAgICANCiAgICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpKw0KICAgIHN0YXRfc3VtbWFyeShmdW4uZGF0YT1mdW5jdGlvbih4KXtyZXR1cm4oZGF0YS5mcmFtZSh5PTAsIGxhYmVsPXJvdW5kKG1lZGlhbih4LCBuYS5ybSA9IFRSVUUpLDQpKSl9LCBnZW9tPSJ0ZXh0IiwgaGp1c3Q9MC41LCB2anVzdD0xKQ0KYGBgDQoNCiMjIDIuMyBUcnlwdGljaXR5IGRpc3RyaWJ1dGlvbg0KDQpVc2UgdGhlIGJ1aWx0LWluIHRyeXB0aWNpdHkgcGxvdHRpbmcgZnVuY3Rpb24gYHRyeXB0aWNIaXN0b2dyYW1MaVAoKWAgdG8gdmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgZnVsbHktIGFuZCBoYWxmLXRyeXB0aWMgcGVwdGlkZXMsIGRlZmluZWQgYXMgcGVwdGlkZXMgd2l0aCB0d28gdHJ5cHRpYyBlbmRzIChGVCkgYW5kIHBlcHRpZGVzIHdpdGggb25lIHRyeXB0aWMgZW5kIChIVCkuDQoNCmBgYHtyIHZpc3VhbGl6ZSB0cnlwdGljaXR5IGJhc2ljLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQp0cnlwdGljSGlzdG9ncmFtTGlQKE1Tc3RhdHNMaVBfU3VtbWFyaXplZCwgZmFzdGFfZmlsZSwNCiAgICAgICAgICAgICAgICAgICAgY29sb3Jfc2NhbGUgPSAiZ3JleSIsDQogICAgICAgICAgICAgICAgICAgIGFkZHJlc3MgPSBGQUxTRSkNCmBgYA0KDQpUaGUgYWJvdmUgZGVmaW5pdGlvbiBvZiB0cnlwdGljaXR5IGFubm90YXRlcyB0ZXJtaW5hbCBwZXB0aWRlcyBhcyBoYWxmLXRyeXB0aWMgcGVwdGlkZXMuIFRoZSBmdW5jdGlvbiBgY2FsY3VsYXRlVHJ5cHRpY2l0eSgpYCBjYW4gZGlzY3JpbWluYXRlIHRlcm1pbmFsIHBlcHRpZGVzIGFuZCBjYWxjdWxhdGVzIGBUUlVFL0ZBTFNFYCB2YWx1ZXMgZm9yIHRoZSBmb2xsb3dpbmcgY2hhcmFjdGVyaXN0aWNzOg0KDQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IHZhbHVlICAgICAgICAgICAgICAgfCBEZXNjcmlwdGlvbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorOj09PT09PT09PT09PT09PT09PT09Kzo9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0rDQp8IGZ1bGx5X1RSSSAgICAgICAgICAgfCBUUlVFOiBwZXB0aWRlIGlzIHJlc3VsdCBvZiB0cnlwdGljIGNsZWF2YWdlLCBGQUxTRTogcGVwdGlkZSBoYXMgb25lIG5vbi10cnlwdGljIGVuZCB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IE5TRU1JX1RSSSAgICAgICAgICAgfCBUUlVFOiBwZXB0aWRlIGhhcyB0aGUgbm9uLXRyeXB0aWMgY2xlYXZhZ2UgYXQgdGhlIE4tdGVybWludXMgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IENTRU1JX1RSSSAgICAgICAgICAgfCBUUlVFOiBwZXB0aWRlIGhhcyB0aGUgbm9uLXRyeXB0aWMgY2xlYXZhZ2UgYXQgdGhlIEMtdGVybWludXMgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQp8IENURVJNSU5VUyAgICAgICAgICAgfCBUUlVFOiBwZXB0aWRlIGlzIGF0IHRoZSBDLXRlcm1pbnVzIG9mIHRoZSBwcm90ZWluICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQorLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rDQoNCkJlbG93IHdlIHBsb3QgdGhlIHRyeXB0aWNpdHkgZGlzdHJpYnV0aW9uIG9mIGVhY2ggcmVwbGljYXRlIGBTVUJKRUNUYCBpbiBlYWNoIGdyb3VwIGBHUk9VUGAgYW5kIHdlIGNob29zZSB0byB2aXN1YWxpemUgdGhlIGRpc3RyaWJ1dGlvbiBvZiBmdWxseS10cnlwdGljIGFuZCBoYWxmLXRyeXB0aWMgcGVwdGlkZXMgYW5kIHRoZSBwb3NpdGlvbiBvZiB0aGUgbm9uLXRyeXB0aWMgZW5kIGJ5IGFzc2Vzc2luZyB0aGUgdmFyaWFibGVzIGBmaWxsPWludGVyYWN0aW9uKGZ1bGx5X1RSSSxOU0VNSV9UUkksQ1NFTUlfVFJJKWAuIFRoZSBwbG90IGlzIGJhc2VkIG9uIGdncGxvdCBhbmQgY2FuIGJlIG1vZGlmaWVkIGF0IGNvbnZlbmllbmNlLg0KDQpgYGB7ciBjYWxjdWxhdGUgYW5kIHZpc3VhbGl6ZSB0cnlwdGljaXR5LCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNSwgbWVzc2FnZT1GQUxTRSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUUsIHdhcm5pbmc9RkFMU0V9DQpNU3N0YXRzTGlQX1N1bW1hcml6ZWQkTGlQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgcmVuYW1lKFBlcHRpZGVTZXF1ZW5jZT1QRVBUSURFLCBQcm90ZWluTmFtZT1QUk9URUlOKSU+JQ0KICBtdXRhdGUoUGVwdGlkZVNlcXVlbmNlPXN1YnN0cihQZXB0aWRlU2VxdWVuY2UsIDEsbmNoYXIoYXMuY2hhcmFjdGVyKFBlcHRpZGVTZXF1ZW5jZSkpLTIpKSU+JQ0KICBmdWxsX2pvaW4oLixjYWxjdWxhdGVUcnlwdGljaXR5KC4sZmFzdGFfZmlsZSkpJT4lDQogIGdncGxvdCgpKw0KICBnZW9tX2JhcihhZXMoeD1TVUJKRUNULCBmaWxsPWludGVyYWN0aW9uKGZ1bGx5X1RSSSxOU0VNSV9UUkksQ1NFTUlfVFJJKSkscG9zaXRpb249ImZpbGwiLCBjb2xvcj0iYmxhY2siKSsNCiAgZmFjZXRfZ3JpZCh+R1JPVVAsIHNjYWxlcyA9ICJmcmVlX3giKSsNCiAgbGFicyh4PSJSZXBsaWNhdGUiLCANCiAgICAgICB5PSJUcnlwdGljaXR5IGNvbnRlbnQgWyVdIiwgDQogICAgICAgZmlsbD0iVHJ5cHRpY2l0eSIsIA0KICAgICAgIHRpdGxlPSJEaXN0cmlidXRpb24gb2YgdHJ5cHRpY2l0eSIpKw0KICB0aGVtZV9idygpKw0KICBzY2FsZV9maWxsX2dyZXkobGFiZWxzPWMoIlRSVUUuRkFMU0UuRkFMU0UiPSJmdWxseSB0cnlwdGljIiwgIkZBTFNFLlRSVUUuRkFMU0UiPSJOLXRlcm0gaGFsZiB0cnlwdGljIiwiRkFMU0UuRkFMU0UuVFJVRSI9IkMtdGVybSBoYWxmIHRyeXB0aWMiLCAiRkFMU0UuRkFMU0UuRkFMU0UiPSIiKSkrDQogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwxLDAuMiksIGxhYmVscyA9IHNlcSgwLDEwMCwyMCkpDQoNCmBgYA0KDQojIyAyLjQgUHJvdGVpbiBhbmQgUGVwdGlkZSBJZGVudGlmaWNhdGlvbnMNCg0KVmlzdWFsaXplIHRoZSBudW1iZXIgb2YgaWRlbnRpZmllZCBwcm90ZWlucyBhbmQgcGVwdGlkZXMgaW4gZWFjaCByZXBsaWNhdGUgYFNVQkpFQ1RgIGluIGVhY2ggZ3JvdXAgYEdST1VQYC4gTm90ZSB0aGF0IHdlIGZpbHRlciBmb3IgaWRlbnRpZmllZCBwZXB0aWRlcyBvbmx5IGJ5IHVzaW5nIGAhaXMubmEoQUJVTkRBTkNFKWAuIFRoZSBwbG90cyBhcmUgYmFzZWQgb24gZ2dwbG90IGFuZCBjYW4gYmUgbW9kaWZpZWQgYXQgY29udmVuaWVuY2UuIFRoZSBmdW5jdGlvbiBgZ3JpZC5hcnJhbmdlYGZyb20gdGhlIFtncmlkRXh0cmFdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9ncmlkRXh0cmEvdmVyc2lvbnMvMi4zKSBwYWNrYWdlIGFycmFuZ2VzIHRoZSBwbG90cyBuZXh0IHRvIGVhY2ggb3RoZXIuDQoNCmEpICBMaVAgc2FtcGxlcw0KDQpgYGB7ciBQZXB0aWRlIGFuZCBQcm90ZWluIElEIExpUCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KZ3JpZC5hcnJhbmdlKA0KICBNU3N0YXRzTGlQX1N1bW1hcml6ZWQkTGlQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgICBmaWx0ZXIoIWlzLm5hKEFCVU5EQU5DRSkpJT4lDQogICAgZ3JvdXBfYnkoR1JPVVAsU1VCSkVDVCklPiUNCiAgICBzdW1tYXJpemUoZGlzdGluY3RfcGVwdGlkZXM9bl9kaXN0aW5jdChQRVBUSURFKSklPiUNCiAgICBnZ3Bsb3QoYWVzKHg9U1VCSkVDVCwgeT1kaXN0aW5jdF9wZXB0aWRlcykpKw0KICAgIGdlb21fY29sKGZpbGw9IndoaXRlIixjb2xvcj0iYmxhY2siKSsNCiAgICBmYWNldF9ncmlkKH5HUk9VUCwgc2NhbGVzID0gImZyZWUiKSsNCiAgICB0aGVtZV9idygpKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9ZGlzdGluY3RfcGVwdGlkZXMpLCB2anVzdD0wLCBoanVzdD0wLjUpKw0KICAgIGxhYnMoeD0iUmVwbGljYXRlIiwgDQogICAgICAgICB5PSJOdW1iZXIgb2YgaWRlbnRpZmllZCBwZXB0aWRlcyIsIA0KICAgICAgICAgdGl0bGU9Ik51bWJlciBvZiBpZGVudGlmaWVkIHBlcHRpZGVzIiwNCiAgICAgICAgIHN1YnRpdGxlPSJMaVAgc2FtcGxlIiksDQogIA0KICBNU3N0YXRzTGlQX1N1bW1hcml6ZWQkTGlQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgICBmaWx0ZXIoIWlzLm5hKEFCVU5EQU5DRSkpJT4lDQogICAgZ3JvdXBfYnkoR1JPVVAsU1VCSkVDVCklPiUNCiAgICBzdW1tYXJpemUoZGlzdGluY3RfcHJvdGVpbnM9bl9kaXN0aW5jdChQUk9URUlOKSklPiUNCiAgICBnZ3Bsb3QoYWVzKHg9U1VCSkVDVCwgeT1kaXN0aW5jdF9wcm90ZWlucykpKw0KICAgIGdlb21fY29sKGZpbGw9IndoaXRlIixjb2xvcj0iYmxhY2siKSsNCiAgICBmYWNldF9ncmlkKH5HUk9VUCwgc2NhbGVzID0gImZyZWUiKSsNCiAgICB0aGVtZV9idygpKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9ZGlzdGluY3RfcHJvdGVpbnMpLCB2anVzdD0wLCBoanVzdD0wLjUpKw0KICAgIGxhYnMoeD0iUmVwbGljYXRlIiwgDQogICAgICAgICB5PSJOdW1iZXIgb2YgaWRlbnRpZmllZCBwcm90ZWlucyIsIA0KICAgICAgICAgdGl0bGU9Ik51bWJlciBvZiBpZGVudGlmaWVkIHByb3RlaW5zIiwNCiAgICAgICAgIHN1YnRpdGxlPSJMaVAgc2FtcGxlIiksDQoNCiAgbmNvbD0yKQ0KYGBgDQoNCmIpICBUclAgc2FtcGxlcw0KDQpgYGB7ciBQZXB0aWRlIGFuZCBQcm90ZWluIElEIFRyUCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KZ3JpZC5hcnJhbmdlKA0KICBNU3N0YXRzTGlQX1N1bW1hcml6ZWQkVHJQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgICBmaWx0ZXIoIWlzLm5hKEFCVU5EQU5DRSkpJT4lDQogICAgZ3JvdXBfYnkoR1JPVVAsU1VCSkVDVCklPiUNCiAgICBzdW1tYXJpemUoZGlzdGluY3RfcGVwdGlkZXM9bl9kaXN0aW5jdChQRVBUSURFKSklPiUNCiAgICBnZ3Bsb3QoYWVzKHg9U1VCSkVDVCwgeT1kaXN0aW5jdF9wZXB0aWRlcykpKw0KICAgIGdlb21fY29sKGZpbGw9IndoaXRlIixjb2xvcj0iYmxhY2siKSsNCiAgICBmYWNldF9ncmlkKH5HUk9VUCwgc2NhbGVzID0gImZyZWUiKSsNCiAgICB0aGVtZV9idygpKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9ZGlzdGluY3RfcGVwdGlkZXMpLCB2anVzdD0wLCBoanVzdD0wLjUpKw0KICAgIGxhYnMoeD0iUmVwbGljYXRlIiwgDQogICAgICAgICB5PSJOdW1iZXIgb2YgaWRlbnRpZmllZCBwZXB0aWRlcyIsIA0KICAgICAgICAgdGl0bGU9Ik51bWJlciBvZiBpZGVudGlmaWVkIHBlcHRpZGVzIiwNCiAgICAgICAgIHN1YnRpdGxlPSJUclAgc2FtcGxlIiksDQogIA0KICBNU3N0YXRzTGlQX1N1bW1hcml6ZWQkVHJQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgICBmaWx0ZXIoIWlzLm5hKEFCVU5EQU5DRSkpJT4lDQogICAgZ3JvdXBfYnkoR1JPVVAsU1VCSkVDVCklPiUNCiAgICBzdW1tYXJpemUoZGlzdGluY3RfcHJvdGVpbnM9bl9kaXN0aW5jdChQUk9URUlOKSklPiUNCiAgICBnZ3Bsb3QoYWVzKHg9U1VCSkVDVCwgeT1kaXN0aW5jdF9wcm90ZWlucykpKw0KICAgIGdlb21fY29sKGZpbGw9IndoaXRlIixjb2xvcj0iYmxhY2siKSsNCiAgICBmYWNldF9ncmlkKH5HUk9VUCwgc2NhbGVzID0gImZyZWUiKSsNCiAgICB0aGVtZV9idygpKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9ZGlzdGluY3RfcHJvdGVpbnMpLCB2anVzdD0wLCBoanVzdD0wLjUpKw0KICAgIGxhYnMoeD0iUmVwbGljYXRlIiwgDQogICAgICAgICB5PSJOdW1iZXIgb2YgaWRlbnRpZmllZCBwcm90ZWlucyIsIA0KICAgICAgICAgdGl0bGU9Ik51bWJlciBvZiBpZGVudGlmaWVkIHByb3RlaW5zIiwNCiAgICAgICAgIHN1YnRpdGxlPSJUclAgc2FtcGxlIiksDQoNCiAgbmNvbD0yKQ0KYGBgDQoNCiMgMyBEYXRhIGRpc3RyaWJ1dGlvbg0KDQpXZSBhc3Nlc3MgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgZGF0YSwgdG8gZW5zdXJlIHRoYXQgdGhlIGNvbmRpdGlvbnMgdG8gYmUgY29tcGFyZWQgc2VwYXJhdGUgd2VsbC4gTW9yZW92ZXIsIHdlIGNhbiBpZGVudGlmeSBvdXRsaWVyIG9yIGRldGVjdCBiYXRjaCBlZmZlY3RzLiBJbiB0aGUgZm9sbG93aW5nLCB3ZSB1c2UgdGhlIGxvZzItdHJhbnNmb3JtZWQgdmFsdWVzIGBBQlVOREFOQ0VgLiBBbHRlcm5hdGl2ZWx5LCBvbmUgY2FuIHVzZSB0aGUgbm9uLXRyYW5zZm9ybWVkIHZhbHVlcyBgSU5URU5TSVRZYC4NCg0KIyMgMy4xIENvcnJlbGF0aW9uDQoNCldlIHBlcmZvcm0gY29ycmVsYXRpb24gYW5hbHlzaXMgdXNpbmcgcGVhcnNvbiBjb3JyZWxhdGlvbiBhbmQgdmlzdWFsaXplIHRoZSBjb3JyZWxhdGlvbiBjb2VmZmljaWVudHMgaW4gYSB0aWxlIG1hcCAoYXMgd2VsbCBhcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSBpbnRlbnNpdGllcyBpbiBhIHNjYXR0ZXIgcGxvdCkuDQoNCm9wdGlvbiAxOiBzaW1wbGUgY29ycmVsYXRpb24gZ3JhcGguIFRoaXMgdGlsZSBtYXAgb2YgY29ycmVsYXRpb24gY29lZmZpY2llbnRzIGlzIHByb2R1Y2VkIGJ5IHRoZSBidWlsZC1pbiBmdW5jdGlvbiBgY29ycmVsYXRpb25QbG90TGlQYC4gSXQgY2FuIGJlIHVzZWQgd2l0aCBiaWcgZGF0YSBzZXRzIGNvbXByaXNpbmcgbGFyZ2UgbnVtYmVycyBvZiBzYW1wbGVzLiBUaGUgYW5hbHlzaXMgaXMgZG9uZSBvbiBmZWF0dXJlIGxldmVsLg0KDQpgYGB7ciBzaW1wbGUgQ29ycmVsYXRpb24gZ3JhcGgsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KY29ycmVsYXRpb25QbG90TGlQKE1Tc3RhdHNMaVBfU3VtbWFyaXplZCwgYWRkcmVzcyA9IEZBTFNFKQ0KYGBgDQoNCm9wdGlvbiAyOiBDb21iaW5lZCB0aWxlIGFuZCBzY2F0dGVyIHBsb3QgVGhpcyBjb21iaW5lZCB0aWxlIGFuZCBzY2F0dGVyIHBsb3QgaXMgcHJvZHVjZWQgdXNpbmcgdGhlIGBnZ3BhaXJzYCBmdW5jdGlvbiB3aXRoaW4gdGhlIFtHR2FsbHldKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9HR2FsbHkvdmVyc2lvbnMvMS41LjApIHBhY2thZ2UuIER1ZSB0byB0aGUgY29tcGxleGl0eSBvZiB0aGUgZ3JhcGgsIGl0IG1pZ2h0IHJlcXVpcmUgbW9yZSBjb21wdXRpbmcgcG93ZXIgZm9yIGxhcmdlIGRhdGEgc2V0cy4gSW4gYWRkaXRpb24gdG8gZGlzcGxheWluZyB0aGUgY29ycmVsYXRpb24gY29lZmZpY2llbnQsIGl0IGFsbG93cyBhc3Nlc3NtZW50IG9mIHRoZSBkYXRhIGRpc3RyaWJ1dGlvbiB3aXRoaW4gdGhlIGNvcnJlbGF0aW9ucy4gV2UgY2FuIHVzZSB0aGUgbm9uLW5vcm1hbGl6ZWQgaW50ZW5zaXR5IHZhbHVlcyBgSU5URU5TSVRZYG9yIHRoZSBsb2cyLXRyYW5zZm9ybWVkIHZhbHVlcyBgQUJVTkRBTkNFYC4gVGhlIGN1c3RvbSBmdW5jdGlvbiBgY29sb3JfZm5gIHNldHMgdGhlIGNvbnN0cmFpbnMgZm9yIHRoZSBjb2xvciByYW5nZSB1c2VkIGluIHRoZSB0aWxlIG1hcC4gVGhlIGRlZmF1bHQgaXMgYG1pbl92YWw9MC41YCwgYnV0IGNhbiBiZSBzZXQgd2l0aGluIHRoZSBwbG90dGluZyBmdW5jdGlvbi4NCg0KYGBge3IgY3VzdG9tIGZ1bmN0aW9uLCBmaWcuaGVpZ2h0PTE1LCBmaWcud2lkdGg9MTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KY29sb3JfZm4gPC0gZnVuY3Rpb24oZGF0YSwgbWFwcGluZywgbWV0aG9kPSJwIiwgdXNlPSJjb21wbGV0ZS5vYnMiLCBtaW5fdmFsPTAuNSwgLi4uKXsNCiAgICAgICAgICAgICAgeCA8LSBldmFsX2RhdGFfY29sKGRhdGEsIG1hcHBpbmckeCkNCiAgICAgICAgICAgICAgeSA8LSBldmFsX2RhdGFfY29sKGRhdGEsIG1hcHBpbmckeSkNCiAgICAgICAgICAgICAgY29yciA8LSBjb3IoeCwgeSwgbWV0aG9kPW1ldGhvZCwgdXNlPXVzZSkNCiAgICAgICAgICAgICAgY29sRm4gPC0gY29sb3JSYW1wUGFsZXR0ZShjKCJ3aGl0ZSIsICJzdGVlbGJsdWUiKSwgaW50ZXJwb2xhdGUgPSdzcGxpbmUnKQ0KICAgICAgICAgICAgICBmaWxsIDwtIGNvbEZuKDEwMClbZmluZEludGVydmFsKGNvcnIsIHNlcShtaW5fdmFsLCAxLCBsZW5ndGg9MTAwKSldDQogICAgICAgICAgICAgIGdnYWxseV9jb3IoZGF0YSA9IGRhdGEsIG1hcHBpbmcgPSBtYXBwaW5nLCBjb2xvcj0iYmxhY2siLG1ldGhvZD1tZXRob2QsIHVzZT11c2UsLi4uKSArIA0KICAgICAgICAgICAgICAgIHRoZW1lX3ZvaWQoKSArDQogICAgICAgICAgICAgICAgdGhlbWUocGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsPWZpbGwpKQ0KfQ0KYGBgDQoNCmEpICBMaVAgc2FtcGxlcw0KDQpgYGB7ciBDb21iaW5lZCB0aWxlIGFuZCBzY2F0dGVycGxvdCBjb3JyZWxhdGlvbiBncmFwaCBMaVAsIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpnZ3BhaXJzKE1Tc3RhdHNMaVBfU3VtbWFyaXplZCRMaVAkRmVhdHVyZUxldmVsRGF0YSU+JQ0KICAgICAgICAgIHVuZ3JvdXAoKSU+JQ0KICAgICAgICAgIHNlbGVjdChJTlRFTlNJVFksIFNVQkpFQ1QsIEZFQVRVUkUpJT4lDQogICAgICAgICAgc3ByZWFkKC4sIFNVQkpFQ1QsIElOVEVOU0lUWSklPiUNCiAgICAgICAgICBzZWxlY3QoLUZFQVRVUkUpLCANCiAgIHVwcGVyID0gbGlzdChjb250aW51b3VzID0gd3JhcChjb2xvcl9mbiwgbWluX3ZhbD0wLjk3KSksDQogICBsb3dlciA9IGxpc3QoY29udGludW91cyA9IHdyYXAoInBvaW50cyIsIGFscGhhPTAuMykpLA0KICAgZGlhZyA9IGxpc3QoY29udGludW91cyA9ICJkZW5zaXR5RGlhZyIpKSsNCiAgZ2d0aXRsZSgiTGlQIHNhbXBsZXMiKQ0KYGBgDQoNCmIpICBUclAgc2FtcGxlcw0KDQpgYGB7ciBDb21iaW5lZCB0aWxlIGFuZCBzY2F0dGVycGxvdCBjb3JyZWxhdGlvbiBncmFwaCBUclAsIGZpZy5oZWlnaHQ9MTUsIGZpZy53aWR0aD0xNSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpnZ3BhaXJzKE1Tc3RhdHNMaVBfU3VtbWFyaXplZCRUclAkRmVhdHVyZUxldmVsRGF0YSU+JQ0KICAgICAgICAgIHVuZ3JvdXAoKSU+JQ0KICAgICAgICAgIHNlbGVjdChJTlRFTlNJVFksIFNVQkpFQ1QsIEZFQVRVUkUpJT4lDQogICAgICAgICAgc3ByZWFkKC4sIFNVQkpFQ1QsIElOVEVOU0lUWSklPiUNCiAgICAgICAgICBzZWxlY3QoLUZFQVRVUkUpLCANCiAgIHVwcGVyID0gbGlzdChjb250aW51b3VzID0gd3JhcChjb2xvcl9mbiwgbWluX3ZhbD0wLjg1KSksDQogICBsb3dlciA9IGxpc3QoY29udGludW91cyA9IHdyYXAoInBvaW50cyIsIGFscGhhPTAuMykpLA0KICAgZGlhZyA9IGxpc3QoY29udGludW91cyA9ICJkZW5zaXR5RGlhZyIpKSsNCiAgZ2d0aXRsZSgiVHJQIHNhbXBsZXMiKQ0KYGBgDQoNCiMjIDMuMiBEZW5kcm9ncmFtIGNsdXN0ZXJpbmcNCg0KVGhlIGRlbmRyb2dyYW0gdmlzdWFsaXplcyB0aGUgaGllcmFyY2hpY2FsIGNsdXN0ZXJpbmcgb2YgdGhlIHBlYXJzb24gY29ycmVsYXRpb24gY29lZmZpY2llbnQuIFRoZSBwbG90IGlzIGdlbmVyYXRlZCB1c2luZyB0aGUgW2RlbmRleHRlbnRdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9kZW5kZXh0ZW5kL3ZpZ25ldHRlcy9kZW5kZXh0ZW5kLmh0bWwpIHBhY2thZ2UuXA0KQ29sb3JlZCBhbm5vdGF0aW9ucyBhcmUgYWRkZWQgdXNpbmcgdGhlIGBjb2xvcmVkX2JhcnNgIGZ1bmN0aW9uLiBUaGUgY29sb3Igc2NoZW1lIGlzIGRlZmluZWQgaW4gYGRlbmRvZ3JhbV9jb2xvcnNgLiBIZXJlLCB3ZSBkZWZpbmUgY29sb3JzIGZvciB0aGUgQ29uZGl0aW9uLiBJZiB0aGUgZGF0YSBzZXQgY29udGFpbnMgYmF0Y2ggbnVtYmVycywgdGhleSBjYW4gYmUgYXNzaWduZWQgY29sb3JzIGZvciB2aXN1YWxpemF0aW9uIHRvby4gVGhpcyBoZWxwcyB0byBpZGVudGlmeSBiYXRjaCBlZmZlY3RzLg0KDQpgYGB7ciBEZW5kcm9ncmFtIGNsdXN0ZXJpbmcgc2V0IGNvbG9ycywgZmlnLndpZHRoPTE1LCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCmRlbmRvZ3JhbV9jb2xvcnM9TVNzdGF0c0xpUF9TdW1tYXJpemVkJExpUCRGZWF0dXJlTGV2ZWxEYXRhJT4lDQogIGRpc3RpbmN0KFJVTiwua2VlcF9hbGw9VFJVRSklPiUNCiAgcm93d2lzZSgpJT4lDQogIHNlbGVjdChjKFNVQkpFQ1QsR1JPVVApKSU+JQ0KICBhcnJhbmdlKC4sIFNVQkpFQ1QpJT4lDQogIG11dGF0ZShncm91cF9jb2w9YnJld2VyLnBhbChubGV2ZWxzKC4kR1JPVVApLG5hbWU9IlBhaXJlZCIpW0dST1VQXSkNCmBgYA0KDQphKSAgTGlQIHNhbXBsZXMNCg0KYGBge3IgRGVuZHJvZ3JhbSBjbHVzdGVyaW5nIExpUCBzYW1wbGUsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTE1LCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCnBhcihtYXIgPSBjKDUsIDUsIDMsIDcpICsgMC4xLA0KICAgIHhwZCA9IE5BKSAgICAgICAgICAgDQpkZW5kX0w9TVNzdGF0c0xpUF9TdW1tYXJpemVkJExpUCRGZWF0dXJlTGV2ZWxEYXRhJT4lDQogICAgICAgICAgc2VsZWN0KEFCVU5EQU5DRSwgU1VCSkVDVCwgRkVBVFVSRSklPiUNCiAgICAgICAgICBzcHJlYWQoLiwgU1VCSkVDVCwgQUJVTkRBTkNFKSU+JQ0KICAgICAgICAgIHNlbGVjdCgtRkVBVFVSRSklPiUNCiAgY29yKC4sIG1ldGhvZCA9ICJzcGVhcm1hbiIsIHVzZT0icGFpcndpc2UuY29tcGxldGUub2JzIiklPiUNCiAgc3VidHJhY3QoMSklPiUNCiAgbXVsdGlwbHlfYnkoLTEpJT4lDQogIGFzLmRpc3QoKSU+JQ0KICBoY2x1c3QobWV0aG9kPSJ3YXJkLkQyIiklPiUNCiAgYXMuZGVuZHJvZ3JhbSgpDQpkZW5kX0wlPiVkZW5kZXh0ZW5kOjpzZXQoImxhYmVsc19jZXgiLCAwLjYpJT4lcGxvdChtYWluPSJMaVAgc2FtcGxlcyIpDQpjb2xvcmVkX2JhcnMoY29sb3JzID0gY2JpbmQoZGVuZG9ncmFtX2NvbG9ycyRiYXRjaF9jb2wsZGVuZG9ncmFtX2NvbG9ycyRncm91cF9jb2wpLCBkZW5kID0gZGVuZF9MLCByb3dMYWJlbHMgPSBjKCJCYXRjaCIsIkdyb3VwIikpDQpgYGANCg0KYikgIFRyUCBzYW1wbGVzDQoNCmBgYHtyIERlbmRyb2dyYW0gY2x1c3RlcmluZyBUclAgc2FtcGxlLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpkZW5kX1Q9TVNzdGF0c0xpUF9TdW1tYXJpemVkJFRyUCRGZWF0dXJlTGV2ZWxEYXRhJT4lDQogICAgICAgICAgc2VsZWN0KEFCVU5EQU5DRSwgU1VCSkVDVCwgRkVBVFVSRSklPiUNCiAgICAgICAgICBzcHJlYWQoLiwgU1VCSkVDVCwgQUJVTkRBTkNFKSU+JQ0KICAgICAgICAgIHNlbGVjdCgtRkVBVFVSRSklPiUNCiAgY29yKC4sIG1ldGhvZCA9ICJzcGVhcm1hbiIsIHVzZT0iY29tcGxldGUub2JzIiklPiUNCiAgc3VidHJhY3QoMSklPiUNCiAgbXVsdGlwbHlfYnkoLTEpJT4lDQogIGFzLmRpc3QoKSU+JQ0KICBoY2x1c3QobWV0aG9kPSJ3YXJkLkQyIiklPiUNCiAgYXMuZGVuZHJvZ3JhbSgpDQpkZW5kX1QlPiVkZW5kZXh0ZW5kOjpzZXQoImxhYmVsc19jZXgiLCAwLjYpJT4lcGxvdChtYWluPSJUclAgc2FtcGxlcyIpDQpjb2xvcmVkX2JhcnMoY29sb3JzID0gY2JpbmQoZGVuZG9ncmFtX2NvbG9ycyRiYXRjaF9jb2wsZGVuZG9ncmFtX2NvbG9ycyRncm91cF9jb2wpLCBkZW5kID0gZGVuZF9ULCByb3dMYWJlbHMgPSBjKCJCYXRjaCIsIkdyb3VwIikpDQpgYGANCg0KIyMgMy4zIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgKFBDQSkgeyNzZWN0aW9uLXBjYX0NCg0KVGhlIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgaXMgYW4gYWx0ZXJuYXRpdmUgd2F5IHRvIGFzc2VzcyBob3cgd2VsbCB0aGUgY29uZGl0aW9ucyBhcmUgc2VwYXJhdGVkIGFuZCBob3cgd2VsbCB0aGUgcmVwbGljYXRlcyBjbHVzdGVyIHRvZ2V0aGVyLiBUaGUgcHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyBpcyBwZXJmb3JtZWQgd2l0aCB0aGUgZnVuY3Rpb24gYHByY29tcGAgZnJvbSB0aGUgc3RhbmRhcmQgW3N0YXRzXShodHRwczovL3d3dy5yZG9jdW1lbnRhdGlvbi5vcmcvcGFja2FnZXMvc3RhdHMvdmVyc2lvbnMvMy42LjIpIHBhY2thZ2UuIFRoZSBQQ0EgaXMgdmlzdWFsaXplZCB1c2luZyB0aGUgYGdnYmlwbG90YCBmdW5jdGlvbiBmcm9tIHRoZSBbZ2diaXBsb3RdKGh0dHBzOi8vd3d3LnJkb2N1bWVudGF0aW9uLm9yZy9wYWNrYWdlcy9nZ2JpcGxvdC92ZXJzaW9ucy8wLjU1KSBwYWNrYWdlLg0KDQphKSAgTGlQIHNhbXBsZXMNCg0KYGBge3IgUHJpbmNpcGFsIGNvbXBvbmVudCBhbmFseXNpcyBMaVAsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTUsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0Kbl9ydW49bGVuZ3RoKHVuaXF1ZShNU3N0YXRzTGlQX1N1bW1hcml6ZWQkTGlQJEZlYXR1cmVMZXZlbERhdGEkUlVOKSkNCg0KTGlQX1BDQT1NU3N0YXRzTGlQX1N1bW1hcml6ZWQkTGlQJEZlYXR1cmVMZXZlbERhdGElPiUNCiAgZmlsdGVyKCFpcy5uYShBQlVOREFOQ0UpKSU+JQ0KICBkcGx5cjo6Z3JvdXBfYnkoRkVBVFVSRSklPiUNCiAgbXV0YXRlKGNvbXBsZXRlPW5fZGlzdGluY3QoUlVOKT09bl9ydW4pJT4lDQogIGZpbHRlcihjb21wbGV0ZT09VFJVRSklPiUNCiAgc2VsZWN0KEFCVU5EQU5DRSwgRkVBVFVSRSwgR1JPVVAsIFJVTiklPiUNCiAgc3ByZWFkKC4sIEZFQVRVUkUsIEFCVU5EQU5DRSkNCg0KZ2diaXBsb3QocHJjb21wKExpUF9QQ0ElPiVzZWxlY3QoLWMoR1JPVVAsUlVOKSkpLGVsbGlwc2U9VCxjaXJjbGU9VCx2YXIuYXhlcz1GLA0KICAgICAgICAgICAgICBncm91cHM9YXMuZmFjdG9yKExpUF9QQ0EkR1JPVVApKSsgDQogIHRoZW1lX2J3KCkrDQogIGxhYnMoY29sb3I9Imdyb3VwIiwgdGl0bGU9IkxpUCBzYW1wbGVzIikrDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpKw0KICB4bGltKC0zLDMpKw0KICB5bGltKC0zLDMpDQpgYGANCg0KYikgIFRyUCBzYW1wbGVzDQoNCmBgYHtyIFByaW5jaXBhbCBjb21wb25lbnQgYW5hbHlzaXMgVHJQLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD01LCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NClRyUF9QQ0E9TVNzdGF0c0xpUF9TdW1tYXJpemVkJFRyUCRGZWF0dXJlTGV2ZWxEYXRhJT4lDQogIGdyb3VwX2J5KEZFQVRVUkUpJT4lDQogIGZpbHRlcighaXMubmEoQUJVTkRBTkNFKSklPiUNCiAgbXV0YXRlKGNvbXBsZXRlPW5fZGlzdGluY3QoUlVOKT09bl9ydW4pJT4lDQogIGZpbHRlcihjb21wbGV0ZT09VFJVRSklPiUNCiAgc2VsZWN0KEFCVU5EQU5DRSwgRkVBVFVSRSwgR1JPVVAsIFJVTiklPiUNCiAgc3ByZWFkKC4sIEZFQVRVUkUsIEFCVU5EQU5DRSkNCg0KZ2diaXBsb3QocHJjb21wKFRyUF9QQ0ElPiVzZWxlY3QoLWMoR1JPVVAsUlVOKSkpLGVsbGlwc2U9VCxjaXJjbGU9VCx2YXIuYXhlcz1GLA0KICAgICAgICAgICAgICBncm91cHM9YXMuZmFjdG9yKFRyUF9QQ0EkR1JPVVApLCBhbHBoYSA9IDApKyANCiAgZ2VvbV9wb2ludChhZXMoY29sb3I9VHJQX1BDQSRHUk9VUCksIHNpemU9MikrDQogIHRoZW1lX2J3KCkrDQogIGxhYnMoY29sb3I9Imdyb3VwIiwgdGl0bGU9IlRyUCBzYW1wbGVzIikrDQogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlBhaXJlZCIpKw0KICB4bGltKC0zLDMpKw0KICB5bGltKC0zLDMpDQpgYGANCg0KIyA0IFN0YXRpc3RpY2FsIGFuYWx5c2lzDQoNCiMjIDQuMSBWb2xjYW5vIHBsb3RzIHsjdm9sY2Fub30NCg0KVmlzdWFsaXplIHRoZSBkaXN0cmlidXRpb24gb2YgYWx0ZXJlZCBwZXB0aWRlcyBhbmQgcHJvdGVpbnMgdXNpbmcgYSB2b2xjYW5vIHBsb3QuDQoNCllvdSBjYW4gdXNlIHRoZSBidWlsdC1pbiBmdW5jdGlvbiBgZ3JvdXBDb21wYXJpc29uUGxvdHNMaVAoKWAgdG8gY3JlYXRlIGEgc2VyaWVzIG9mIGRpZmZlcmVudCBiYXNpYyB2b2xjYW5vIHBsb3RzLiBUaGUgYXJndW1lbnQgYHR5cGVgIGRlZmluZXMgdGhlIHR5cGUgb2YgcGxvdCwgZWl0aGVyIHZvbGNhbm8gcGxvdCBvciBoZWF0IG1hcC4gVGhlIGFyZ3VtZW50IGB3aGljaC5Qcm90ZWluYCBnaXZlcyB0aGUgb3B0aW9uIHRvIGRpc3BsYXkgcGVwdGlkZXMgZnJvbSBvbmx5IG9uZSBwcm90ZWluLCBgd2hpY2guY29tcGFyaXNvbmBhIGFsbG93cyB0byBmaWx0ZXIgZm9yIGEgY29tcGFyaXNvbiBvZiBpbnRlcmVzdC4gWW91IGNhbiBjaG9vc2UgdG8gZGlzcGxheSB0aGUgbmFtZSBvZiB0aGUgc2lnbmlmaWNhbnQgcGVwdGlkZXMgYXMgUHJvdGVpbl9QZXB0aWRlU2VxdWVuY2Ugd2l0aCBgUHJvdGVpbk5hbWU9VFJVRWAuIFRoZSBjdXQtb2ZmIHZhbHVlcyBmb3IgdGhlIHEtdmFsdWUgY2FuIGJlIHNldCB1c2luZyB0aGUgYXJndW1lbnQgYHNpZ2AsIHRoZSBkZWZhdWx0IGlzIGBzaWc9MC4wNWAsIHRoZSBjdXQtb2ZmIHZhbHVlIGZvciB0aGUgZm9sZCBjaGFuZ2UgaXMgc2V0IHVzaW5nIGBGQ2N1dG9mZmAuDQoNCmBgYHtyIFZvbGNhbm8gcGxvdHMgYnVpbHRpbiwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpncm91cENvbXBhcmlzb25QbG90c0xpUChNU3N0YXRzTGlQX21vZGVsLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiVm9sY2Fub1Bsb3QiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIFByb3RlaW5OYW1lID0gRkFMU0UsDQogICAgICAgICAgICAgICAgICAgICAgICBhZGRyZXNzID0gRkFMU0UpDQpgYGANCg0KRm9yIG1vcmUgY29udHJvbCBvdmVyIHRoZSBwbG90IGFwcGVhcmFuY2UsIHlvdSBjYW4gdXNlIHRoZSBmb2xsb3dpbmcgY29kZSwgYmFzZWQgb24gZ2dwbG90IGFuZCBhZGp1c3QgaXQgdG8gY29udmVuaWVuY2UNCg0KU2V0IHRoZSBjdXRvZmYgZm9yIHRoZSBxLXZhbHVlIGFzIGBhZGoucHZhbHVlLmN1dG9mZmAgYW5kIHRoZSBmb2xkIGNoYW5nZSBhcyBgbG9nMkZDLmN1dG9mZmAuDQoNCmBgYHtyIFZvbGNhbm8gcGxvdCBjdXRvZmZzLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQphZGoucHZhbHVlLmN1dG9mZj0wLjA1DQpsb2cyRkMuY3V0b2ZmPTENCmBgYA0KDQpVc2luZyB0aGUgZnVuY3Rpb24gYGdnaGlnaGxpZ2h0YCBmcm9tIHRoZSBbZ2doaWdobGlnaHRdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9nZ2hpZ2hsaWdodC92aWduZXR0ZXMvZ2doaWdobGlnaHQuaHRtbCkgcGFja2FnZSwgeW91IGNhbiBoaWdobGlnaHQgcGVwdGlkZXMgb2YgYSBzcGVjaWZpYyBwcm90ZWluIGFuZCBjb2xvciB0aGVtIGJ5IHdoZXRoZXIgdGhleSBhcmUgc2lnbmlmaWNhbnQgb3Igbm90IGJ5IHVzaW5nIGBjb2xvcj1zaWduaWZpY2FudGAuXA0KWW91IGNhbiBhbHNvIHVzZSBgY29sb3I9ZnVsbHlfVFJJYCB0byBjb2xvciBwZXB0aWRlcyBieSB0cnlwdGljaXR5LiBTZXQgdGhlIGZpbHRlciBpbiBnZ2hpZ2hsaWdodCB0byBgc2lnbmlmaWNhbnQ9PSJZZXMiYCBpZiB5b3Ugb25seSB3YW50IHRvIGNvbG9yIHNpZ25pZmljYW50IHBlcHRpZGVzLiBVc2UgYHNpZ25pZmljYW50PT0iWWVzIiZQcm90ZWluTmFtZT09IlAzNzg0MCJgIHRvIGhpZ2hsaWdodCB0aGUgc2lnbmlmaWNhbnQgcGVwdGlkZXMgb2Ygb25seSBvbmUgcHJvdGVpbiwgaW4gdGhpcyBjYXNlLCBQMzc4NDAuIFVzaW5nIHRoZSBhcmd1bWVudCBgbGFiZWxfa2V5PVBlcHNpZGVTZXF1ZW5jZWAgYW5kIHNldHRpbmcgdGhlIGFyZ3VtZW50IGB1c2VfZGlyZWN0X2xhYmVsPVRSVUVgLCB5b3UgY2FuIGFkZCB0aGUgcGVwdGlkZSBzZXF1ZW5jZSB0byB0aGUgcGxvdC4gVGhpcyBpcyBvbmx5IGFkdmlzYWJsZSBpZiBmZXcgcGVwdGlkZXMgYXJlIHNpZ25pZmljYW50LiAqKk5vdGUqKiB0aGF0IHRoaXMgd2lsbCBvbmx5IHdvcmsgaW4gYSBub24tZmFjZXRlZCBwbG90LiBGb3IgZGlzcGxheWluZyBwZXB0aWRlIHNlcXVlbmNlIG9uIGEgZmFjZXRlZCBwbG90LCBkaXNhYmxlIHRoZSBnZ2hpZ2hsaWdodCBmdW5jdGlvbiBhbmQgdXNlIGBnZW9tX2xhYmVsX3JlcGVsYCBvciBgZ2VvbV90ZXh0X3JlcGVsYCBmcm9tIHRoZSBbZ2dyZXBlbF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2dncmVwZWwvdmlnbmV0dGVzL2dncmVwZWwuaHRtbCkgcGFja2FnZS4NCg0KYSkgIExpUCBkYXRhLCBub24tYWRqdXN0ZWQNCg0KYGBge3IgVm9sY2FubyBwbG90IExpUCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KTVNzdGF0c0xpUF9tb2RlbCRMaVAuTW9kZWwlPiUNCiAgZmlsdGVyKGlzLm5hKGlzc3VlKSklPiUNCiAgbXV0YXRlKHNpZ25pZmljYW50PWNhc2Vfd2hlbihhYnMobG9nMkZDKT5sb2cyRkMuY3V0b2ZmJmFkai5wdmFsdWU8YWRqLnB2YWx1ZS5jdXRvZmZ+IlllcyIsVFJVRX4iTm8iKSklPiUNCiAgZ2dwbG90KGFlcyh4PWxvZzJGQywgeT0tbG9nMTAoYWRqLnB2YWx1ZSksY29sb3I9c2lnbmlmaWNhbnQpKSsNCiAgZ2VvbV9wb2ludCgpKw0KICBnZ2hpZ2hsaWdodChQcm90ZWluTmFtZT09IlAzMjQ4NSIsIGNhbGN1bGF0ZV9wZXJfZmFjZXQgPSBUUlVFLCB1c2VfZ3JvdXBfYnk9RkFMU0UsIHVzZV9kaXJlY3RfbGFiZWwgPSBGQUxTRSkrDQogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IC1sb2cxMChhZGoucHZhbHVlLmN1dG9mZikpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSBsb2cyRkMuY3V0b2ZmKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gLWxvZzJGQy5jdXRvZmYpKw0KICBmYWNldF9ncmlkKH5MYWJlbCwgc2NhbGVzID0gImZyZWUiKSsNCiAgdGhlbWVfYncoKSsNCiAgbGFicyh0aXRsZT0iU3RydWN0dXJhbGx5IGFsdGVyZWQgcGVwdGlkZXMiLA0KICAgICAgIHN1YnRpdGxlPSJMaVAgc2FtcGxlLCBub24tYWRqdXN0ZWQiKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlllcyI9ImRhcmtyZWQiLCAiTm8iPSJibGFjayIpKSsNCiAgZ3VpZGVzKGNvbG9yPUZBTFNFKQ0KYGBgDQoNCmIpICBMaVAgZGF0YSwgYWRqdXN0ZWQNCg0KYGBge3IgVm9sY2FubyBwbG90IExpUCBhZGp1c3RlZCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0UsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KTVNzdGF0c0xpUF9tb2RlbCRBZGp1c3RlZC5MaVAuTW9kZWwlPiUNCiAgZmlsdGVyKGlzLm5hKGlzc3VlKSklPiUNCiAgbXV0YXRlKHNpZ25pZmljYW50PWNhc2Vfd2hlbihhYnMobG9nMkZDKT5sb2cyRkMuY3V0b2ZmJmFkai5wdmFsdWU8YWRqLnB2YWx1ZS5jdXRvZmZ+IlllcyIsVFJVRX4iTm8iKSwNCiAgICAgICAgIGRpcmVjdGlvbj1jYXNlX3doZW4obG9nMkZDPDB+ImRvd24iLGxvZzJGQz4wfiJ1cCIpKSU+JQ0KICBnZ3Bsb3QoYWVzKHg9bG9nMkZDLCB5PS1sb2cxMChhZGoucHZhbHVlKSxjb2xvcj1zaWduaWZpY2FudCkpKw0KICBnZW9tX3BvaW50KCkrDQogIGdnaGlnaGxpZ2h0KFByb3RlaW5OYW1lPT0iUDMyNDg1IiwgY2FsY3VsYXRlX3Blcl9mYWNldCA9IFRSVUUsIHVzZV9ncm91cF9ieT1GQUxTRSwgdXNlX2RpcmVjdF9sYWJlbCA9IEZBTFNFKSsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gLWxvZzEwKGFkai5wdmFsdWUuY3V0b2ZmKSkrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IGxvZzJGQy5jdXRvZmYpKw0KICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAtbG9nMkZDLmN1dG9mZikrDQogIGZhY2V0X2dyaWQofkxhYmVsLCBzY2FsZXMgPSAiZnJlZSIpKw0KICB0aGVtZV9idygpKw0KICBsYWJzKHRpdGxlPSJTdHJ1Y3R1cmFsbHkgYWx0ZXJlZCBwZXB0aWRlcyIsDQogICAgICAgc3VidGl0bGU9IkxpUCBzYW1wbGUiKSsNCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IGMoIlllcyI9ImRhcmtyZWQiLCAiTm8iPSJibGFjayIpKSsNCiAgZ3VpZGVzKGNvbG9yPUZBTFNFKQ0KYGBgDQoNClZpc3VhbGl6ZSBkZWZlcmVudGlhbGx5IGFidW5kYW50IHByb3RlaW5zIGluIHRoZSBkYXRhIHNldA0KDQpgYGB7ciBWb2xjYW5vIHBsb3QgVHJQLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD0xNSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpNU3N0YXRzTGlQX21vZGVsJFRyUC5Nb2RlbCU+JQ0KICBmaWx0ZXIoaXMubmEoaXNzdWUpKSU+JQ0KICBtdXRhdGUoc2lnbmlmaWNhbnQ9Y2FzZV93aGVuKGFicyhsb2cyRkMpPmxvZzJGQy5jdXRvZmYmYWRqLnB2YWx1ZTxhZGoucHZhbHVlLmN1dG9mZn4iWWVzIixUUlVFfiJObyIpLA0KICAgICAgICAgZGlyZWN0aW9uPWNhc2Vfd2hlbihsb2cyRkM8MH4iZG93biIsbG9nMkZDPjB+InVwIikpJT4lDQogIGdncGxvdChhZXMoeD1sb2cyRkMsIHk9LWxvZzEwKGFkai5wdmFsdWUpLGNvbG9yPXNpZ25pZmljYW50KSkrDQogIGdlb21fcG9pbnQoKSsNCiAgZ2VvbV9sYWJlbF9yZXBlbChkYXRhPS4lPiVmaWx0ZXIoc2lnbmlmaWNhbnQ9PSJZZXMiKSwgYWVzKHg9bG9nMkZDLCB5PS1sb2cxMChhZGoucHZhbHVlKSwgbGFiZWw9UHJvdGVpbiksIGNvbG9yPSJibGFjayIsIHlsaW09YygtbG9nMTAoYWRqLnB2YWx1ZS5jdXRvZmYpLCBOQSkpKw0KICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAtbG9nMTAoYWRqLnB2YWx1ZS5jdXRvZmYpKSsNCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gbG9nMkZDLmN1dG9mZikrDQogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IC1sb2cyRkMuY3V0b2ZmKSsNCiAgZmFjZXRfZ3JpZCh+TGFiZWwsIHNjYWxlcyA9ICJmcmVlIikrDQogIHRoZW1lX2J3KCkrDQogIGxhYnModGl0bGU9IkRpZmZlcmVudGlhbGwgYWJ1bmRhbnQgcHJvdGVpbnMiLA0KICAgICAgIHN1YnRpdGxlPSJUclAgc2FtcGxlIikrDQogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJZZXMiPSJkYXJrcmVkIiwgIk5vIj0iYmxhY2siKSkrDQogIGd1aWRlcyhjb2xvcj1GQUxTRSkNCmBgYA0KDQojIyA0LjIgUXVhbnRpZmljYXRpb24gb2YgYWx0ZXJlZCBwcm90ZWlucyBhbmQgcGVwdGlkZXMNCg0KUXVhbnRpZnkgdGhlIG51bWJlciBvZiBwcm90ZWlucyBhbmQgcGVwdGlkZXMgd2l0aCBzdHJ1Y3R1cmFsIGFsdGVyYXRpb25zIGluIGVhY2ggY29tcGFyaXNvbiBpbiB0aGUgbW9kZWwgYExhYmVsYC4gSWYgbm90IHNldCBpbiBbc2VjdGlvbiA1LjFdKCN2b2xjYW5vKSwgZGVmaW5lIHRoZSBjdXQgb2ZmIHZhbHVlcyBmb3IgdGhlIGZvbGQgY2hhbmdlIGBsb2cyRkMuY3V0b2ZmYGFuZCBxLXZhbHVlIGBhZGoucHZhbHVlLmN1dG9mZmAuIFVzZSBgZmlsdGVyKFByb3RlaW5OYW1lPT0iUDM3ODQwIilgIHRvIHZpc3VhbGl6ZSB0aGUgbnVtYmVyIG9mIHBlcHRpZGVzIGZvciBvbmUgcGFydGljdWxhciBwcm90ZWluIG9mIGludGVyZXN0LCBoZXJlIFAzNzg0MC4NCg0KYGBge3IgY3V0IG9mZiB2YWx1ZXMsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTE1LCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCmFkai5wdmFsdWUuY3V0b2ZmPTAuMDUNCmxvZzJGQy5jdXRvZmY9MQ0KYGBgDQoNCmEpICBMaVAgZGF0YSwgbm90IGFkanVzdGVkDQoNCmBgYHtyIHN0cnVjdHVyYWxseSBhbHRlcmVkIElEcyBMaVAsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTE1LCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCmdyaWQuYXJyYW5nZSgNCiAgTVNzdGF0c0xpUF9tb2RlbCRMaVAuTW9kZWwlPiUNCiAgICBmaWx0ZXIoaXMubmEoaXNzdWUpJmFicyhsb2cyRkMpPmxvZzJGQy5jdXRvZmYmYWRqLnB2YWx1ZTxhZGoucHZhbHVlLmN1dG9mZiklPiUNCiAgICBncm91cF9ieShMYWJlbCklPiUNCiAgICBzdW1tYXJpemUoZGlzdGluY3RfcGVwdGlkZXM9bl9kaXN0aW5jdChQZXB0aWRlU2VxdWVuY2UpKSU+JQ0KICAgIGdncGxvdChhZXMoeD1MYWJlbCwgeT1kaXN0aW5jdF9wZXB0aWRlcykpKw0KICAgIGdlb21fY29sKGZpbGw9IndoaXRlIixjb2xvcj0iYmxhY2siKSsNCiAgICB0aGVtZV9idygpKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9ZGlzdGluY3RfcGVwdGlkZXMpLCB2anVzdD0wLCBoanVzdD0wLjUpKw0KICAgIGxhYnMoeD0iQ29tcGFyaXNvbiIsIA0KICAgICAgICAgeT0iTnVtYmVyIG9mIHBlcHRpZGVzIiwgDQogICAgICAgICB0aXRsZT0iU3RydWN0dXJhbGx5IGFsdGVyZWQgcGVwdGlkZXMiLA0KICAgICAgICAgc3VidGl0bGU9IkxpUCBzYW1wbGUsIG5vbi1hZGp1c3RlZCIpLA0KICANCiAgTVNzdGF0c0xpUF9tb2RlbCRMaVAuTW9kZWwlPiUNCiAgICBmaWx0ZXIoaXMubmEoaXNzdWUpJmFicyhsb2cyRkMpPmxvZzJGQy5jdXRvZmYmYWRqLnB2YWx1ZTxhZGoucHZhbHVlLmN1dG9mZiklPiUNCiAgICBncm91cF9ieShMYWJlbCklPiUNCiAgICBzdW1tYXJpemUoZGlzdGluY3RfcHJvdGVpbnM9bl9kaXN0aW5jdChQcm90ZWluTmFtZSkpJT4lDQogICAgZ2dwbG90KGFlcyh4PUxhYmVsLCB5PWRpc3RpbmN0X3Byb3RlaW5zKSkrDQogICAgZ2VvbV9jb2woZmlsbD0id2hpdGUiLGNvbG9yPSJibGFjayIpKw0KICAgIHRoZW1lX2J3KCkrDQogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1kaXN0aW5jdF9wcm90ZWlucyksIHZqdXN0PTAsIGhqdXN0PTAuNSkrDQogICAgbGFicyh4PSJDb21wYXJpc29uIiwgDQogICAgICAgICB5PSJOdW1iZXIgb2YgcHJvdGVpbnMiLCANCiAgICAgICAgIHRpdGxlPSJTdHJ1Y3R1cmFsbHkgYWx0ZXJlZCBwcm90ZWlucyIsDQogICAgICAgICBzdWJ0aXRsZT0iTGlQIHNhbXBsZSwgbm9uLWFkanVzdGVkIiksDQogIG5yb3c9MSkNCmBgYA0KDQpiKSAgTGlQIGRhdGEsIGFkanVzdGVkDQoNCmBgYHtyIHN0cnVjdHVyYWxseSBhbHRlcmVkIElEcyBhZGp1c3RlZCBMaVAsIGZpZy5oZWlnaHQ9NSwgZmlnLndpZHRoPTE1LCBlY2hvPVRSVUUsIGluY2x1ZGU9VFJVRX0NCmdyaWQuYXJyYW5nZSgNCiAgTVNzdGF0c0xpUF9tb2RlbCRBZGp1c3RlZC5MaVAuTW9kZWwlPiUNCiAgICBmaWx0ZXIoaXMubmEoaXNzdWUpJmFicyhsb2cyRkMpPmxvZzJGQy5jdXRvZmYmYWRqLnB2YWx1ZTxhZGoucHZhbHVlLmN1dG9mZiklPiUNCiAgICBncm91cF9ieShMYWJlbCklPiUNCiAgICBzdW1tYXJpemUoZGlzdGluY3RfcGVwdGlkZXM9bl9kaXN0aW5jdChQZXB0aWRlU2VxdWVuY2UpKSU+JQ0KICAgIGdncGxvdChhZXMoeD1MYWJlbCwgeT1kaXN0aW5jdF9wZXB0aWRlcykpKw0KICAgIGdlb21fY29sKGZpbGw9IndoaXRlIixjb2xvcj0iYmxhY2siKSsNCiAgICB0aGVtZV9idygpKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9ZGlzdGluY3RfcGVwdGlkZXMpLCB2anVzdD0wLCBoanVzdD0wLjUpKw0KICAgIGxhYnMoeD0iQ29tcGFyaXNvbiIsIA0KICAgICAgICAgeT0iTnVtYmVyIG9mIHBlcHRpZGVzIiwgDQogICAgICAgICB0aXRsZT0iU3RydWN0dXJhbGx5IGFsdGVyZWQgcGVwdGlkZXMiLA0KICAgICAgICAgc3VidGl0bGU9IkxpUCBzYW1wbGUsIGFkanVzdGVkIiksDQogIA0KICBNU3N0YXRzTGlQX21vZGVsJEFkanVzdGVkLkxpUC5Nb2RlbCU+JQ0KICAgIGZpbHRlcihpcy5uYShpc3N1ZSkmYWJzKGxvZzJGQyk+bG9nMkZDLmN1dG9mZiZhZGoucHZhbHVlPGFkai5wdmFsdWUuY3V0b2ZmKSU+JQ0KICAgIGdyb3VwX2J5KExhYmVsKSU+JQ0KICAgIHN1bW1hcml6ZShkaXN0aW5jdF9wcm90ZWlucz1uX2Rpc3RpbmN0KFByb3RlaW5OYW1lKSklPiUNCiAgICBnZ3Bsb3QoYWVzKHg9TGFiZWwsIHk9ZGlzdGluY3RfcHJvdGVpbnMpKSsNCiAgICBnZW9tX2NvbChmaWxsPSJ3aGl0ZSIsY29sb3I9ImJsYWNrIikrDQogICAgdGhlbWVfYncoKSsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsPWRpc3RpbmN0X3Byb3RlaW5zKSwgdmp1c3Q9MCwgaGp1c3Q9MC41KSsNCiAgICBsYWJzKHg9IkNvbXBhcmlzb24iLCANCiAgICAgICAgIHk9Ik51bWJlciBvZiBwcm90ZWlucyIsIA0KICAgICAgICAgdGl0bGU9IlN0cnVjdHVyYWxseSBhbHRlcmVkIHByb3RlaW5zIiwNCiAgICAgICAgIHN1YnRpdGxlPSJMaVAgc2FtcGxlLCBhZGp1c3RlZCIpLA0KICBucm93PTEpDQpgYGANCg0KUXVhbnRpZnkgdGhlIG51bWJlciBvZiBkZWZlcmVudGlhbGx5IGFidW5kYW50IHByb3RlaW5zIGluIHRoZSBkYXRhIHNldA0KDQpgYGB7ciBkaWZmZXJlbnRpYWxseSBhYnVuZGFudCBwcm90ZWlucywgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9NSwgZWNobz1UUlVFLCBpbmNsdWRlPVRSVUV9DQpNU3N0YXRzTGlQX21vZGVsJFRyUC5Nb2RlbCU+JQ0KICAgIGZpbHRlcihpcy5uYShpc3N1ZSkmYWJzKGxvZzJGQyk+bG9nMkZDLmN1dG9mZiZhZGoucHZhbHVlPGFkai5wdmFsdWUuY3V0b2ZmKSU+JQ0KICAgIGdyb3VwX2J5KExhYmVsKSU+JQ0KICAgIHN1bW1hcml6ZShkaXN0aW5jdF9wcm90ZWlucz1uX2Rpc3RpbmN0KFByb3RlaW4pKSU+JQ0KICAgIGdncGxvdChhZXMoeD1MYWJlbCwgeT1kaXN0aW5jdF9wcm90ZWlucykpKw0KICAgIGdlb21fY29sKGZpbGw9IndoaXRlIixjb2xvcj0iYmxhY2siKSsNCiAgICB0aGVtZV9idygpKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWw9ZGlzdGluY3RfcHJvdGVpbnMpLCB2anVzdD0wLCBoanVzdD0wLjUpKw0KICAgIGxhYnMoeD0iQ29tcGFyaXNvbiIsIA0KICAgICAgICAgeT0iTnVtYmVyIG9mIHByb3RlaW5zIiwgDQogICAgICAgICB0aXRsZT0iRGlmZmVyZW50aWFsbHkgYWJ1bmRhbmQgcHJvdGVpbnMiLA0KICAgICAgICAgc3VidGl0bGU9IlRyUCBzYW1wbGUiKQ0KYGBgDQoNCiMjIDQuMyBCYXJjb2RlIHBsb3RzDQoNClZpc3VhbGl6ZSB0aGUgcG9zaXRpb25zIG9mIHN0cnVjdHVyYWxseSBhbHRlcmVkIHBlcHRpZGVzIG9uIHRoZSBwcm90ZWluIHNlcXVlbmNlIHVzaW5nIGBCYXJjb2RlUGxvdExpUCgpYCBmdW5jdGlvbi4gVGhlIGFyZ3VtZW50IGBtb2RlbF90eXBlYCBjYW4gYmUgdXNlZCB0byBkZWZpbmUgd2hldGhlciB0aGUgYWRqdXN0ZWQgbW9kZWwgb3IgdGhlIG5vbi1hZGp1c3RlZC4gVGhlIGFyZ3VtZW50IGB3aGljaC5wcm90ZWluYCBpcyB1c2VkIHRvIGRlZmluZSB3aGljaCBwcm90ZWluIHRvIHZpc3VhbGl6ZS4gVXNlIHRoZSBhcmd1bWVudCBgRlQub25seT1UUlVFYHRvIHZpc3VhbGl6ZSBvbmx5IGZ1bGx5IHRyeXB0aWMgcGVwdGlkZXMuDQoNCmEpICBMaVAgZGF0YSwgbm9uLWFkanVzdGVkDQoNCmBgYHtyIGZpZy5oZWlnaHQ9Mi41LCBmaWcud2lkdGg9MTUsIGVjaG89VFJVRSwgaW5jbHVkZT1UUlVFfQ0KQmFyY29kZVBsb3RMaVAoTVNzdGF0c0xpUF9tb2RlbCwgZmFzdGFfZmlsZSxtb2RlbF90eXBlPSJBZGp1c3RlZCIsd2hpY2gucHJvdCA9IGMoIlAzMjQ4NSIpLCBhZGRyZXNzPUZBTFNFLCBGVC5vbmx5ID0gVFJVRSkNCmBgYA0K