Introduction

This practical session is designed to help you understand the importance of normalization, scaling, and regression in single-cell RNA-seq analysis. You will apply these steps using different methods and visualize the effects on the data.

Input data: Seurat object containing the filtered count matrix from the previous class. It’s called 05_TD3A_S5_Doublets.Filtered_12508.4035.RDS.

Output data: Seurat object after the normalization, scaling and regression. It’s called sobj_TD3A_normalized.rds.

Learning Objectives

  • Discuss why normalizing counts is necessary to compare cells
  • Be able to understand different normalization approaches
  • Be able to decide when to regress out a given variable, by evaluating the effects from any unwanted sources of variation

Motivation

Up to this point, we have filtered out low-quality cells, ambient RNA contamination, and doublets. The data is now a count matrix with cells as columns and genes as rows. These counts reflect the capture, reverse transcription, and sequencing steps of the scRNA-seq experiment, each introducing variability. This means the observed differences in gene expression may be influenced by sampling noise rather than true biological differences, resulting in uneven variance across the dataset.

Normalization addresses this by adjusting raw counts to minimize the effects of variable sampling, making the data more suitable for analysis, which often assumes uniform variance. Various normalization techniques exist, each tailored to support different downstream analyses.

A recent study by Ahlmann-Eltze and Huber (2023) benchmarked 22 normalization methods. It’s essential to select the normalization method based on the specific analysis goals. However, for this practical we will just explore the most well-known way to doing single-cell normalization.

Setup

We first load the packages that are needed :

library(Seurat)
library(ggplot2)
library(patchwork)
library(scater)
library(dplyr)

We then import the data that have been previously filtered (reminder : we removed low quality cells, removed ambient RNA and scored doublets) :

sobj <- readRDS("input/05_TD3A_S5_Doublets.Filtered_12508.4035.RDS")
sobj
## An object of class Seurat 
## 12508 features across 4035 samples within 1 assay 
## Active assay: RNA (12508 features, 0 variable features)
##  2 layers present: counts, data

For the sake of this practical session, we will use a smaller dataset which contains only 1000 cells. In order to do this, we can use the Seurat objet as a dataframe, where rows are genes, and columns are cells. In R language, this corresponds to : sobj[genes,cells].

We will take the first 1000 cells, by using the range 1:1000, like this :

# Select the cells to keep 
sobj_tiny <- sobj[,1:1000]
# Check that the object contains 1000 cells
sobj_tiny
## An object of class Seurat 
## 12508 features across 1000 samples within 1 assay 
## Active assay: RNA (12508 features, 0 variable features)
##  2 layers present: counts, data

Exercices 1 : explore NormalizeData function

Let’s start by exploring the NormalizeData function.

What is the input of this function ?

Answers

The input is your Seurat object (for us : sobj_tiny)

How many normalization methods are they ?

Can you spot which is the default one ?

Answers

There are 3 normalization approaches included in Seurat : LogNormalize, CLR and RC. The default one is LogNormalize.

Run the NormalizeData function on the sobj_tiny

You can now explore your new object : where are the normalized values stored ? Can you print the values of the first 4 genes in the 10 first cells ?

Answers
sobj_tiny <- NormalizeData(sobj_tiny)

# The normalized values are stored in the slot 'data' : 
sobj_tiny@assays$RNA$data[1:4, 1:10]
## 4 x 10 sparse Matrix of class "dgCMatrix"
##                                                                          
## Mrpl15  . 2.365796 .        .        .        .        .        0.6103322
## Lypla1  . .        .        1.844929 .        1.627274 1.442936 0.6103322
## Gm37988 . .        .        .        .        .        .        .        
## Tcea1   . .        1.781829 .        1.708152 1.627274 2.010388 .        
##                          
## Mrpl15  .        .       
## Lypla1  .        1.052803
## Gm37988 .        .       
## Tcea1   1.701157 1.052803
# You can compare it to the slot 'count' (raw count) :
sobj_tiny@assays$RNA$count[1:4, 1:10]
## 4 x 10 sparse Matrix of class "dgCMatrix"
##                            
## Mrpl15  . 2 . . . . . 1 . .
## Lypla1  . . . 1 . 1 1 1 . 1
## Gm37988 . . . . . . . . . .
## Tcea1   . . 1 . 1 1 2 . 1 1

Exercises 2 : let’s test some different normalization strategies

Method 1: LogNormalize

LogNormalize method or “scaling normalization” is the simplest and most commonly used class of normalization approaches. This involves dividing all counts for each cell by a cell-specific scaling factor, often called a “size factor” (Anders and Huber 2010). The assumption here is that any cell-specific bias (e.g., in capture or amplification efficiency) affects all genes equally via scaling of the expected mean count for that cell. The size factor for each cell represents the estimate of the relative bias in that cell, so division of its counts by its size factor should remove that bias. The resulting “normalized expression values” can then be used for downstream analyses such as clustering and dimensionality reduction. (From https://bioconductor.org/books/3.14/OSCA.basic/normalization.html)

# Apply LogNormalize
sobj_tiny <- NormalizeData(sobj_tiny,
                          normalization.method = "LogNormalize",
                          scale.factor = 10000)

We can now visualize the effect of LogNormalize with some violin plots :

VlnPlot(sobj_tiny,
        features = c("Srm", "Apoe", "Top2a"),
        layer = "data")

Can you explain to your neighbor what does this plot mean ?

Violin plots are sometimes hard to read. Here is a function to build box plots out of Seurat objects :

BoxPlotFromSeurat <- function(seurat_obj, features, log = TRUE, layer = "data", plot.title = "Box Plot") {
  # Extract the data for the features of interest
  df <- Seurat::FetchData(seurat_obj, vars = features, layer = layer)
  
  # Add metadata for grouping if group.by is specified
  df <- cbind(df, seurat_obj[[]])
  
  # Reshape the data for ggplot2
  df_long <- tidyr::gather(df, key = "feature", value = "expression", group)
  df_long <- df %>% group_by_(group.by) %>%
    tidyr::pivot_longer(cols = features)

  # Create box plots using ggplot2
  if (log == TRUE) {
    p <- ggplot(df_long, ggplot2::aes(x = name, y = log10(value))) +
    geom_boxplot(outlier.shape = NA) +
    theme_minimal() +
    labs(x = "Feature", y = "Expression Level (log10)") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
  } else {
    p <- ggplot(df_long, ggplot2::aes(x = name, y = value)) +
    geom_boxplot(outlier.shape = NA) +
    theme_minimal() +
    labs(x = "Feature", y = "Expression Level") +
    theme(axis.text.x = element_text(angle = 45, hjust = 1))
  }
  return(p)
}

# Example usage
# BoxPlotFromSeurat(sobj_tiny, features = c("Srm", "Apoe", "Top2a"))

Can you do the same Violin plots but with the raw counts ?

You can try also with the boxplots.

Answers
VlnPlot(sobj_tiny,
        features = c("Srm", "Apoe", "Top2a"), 
        layer = "count")

Can you plot the distribution of the values ?

Try to do an histogram, with 1) the raw counts values and 2) the log normalized values. You can use the R hist() function. What do you observe ? You will need to extract your data with a command such as :

raw_counts <- as.vector(as.matrix(sobj_tiny@assays$RNA$count))
Answers
# Raw values
raw_counts <- as.vector(as.matrix(sobj_tiny@assays$RNA$count))
hist(raw_counts)

# Log Norm values
logNorm_counts <- as.vector(as.matrix(sobj_tiny@assays$RNA$data))
hist(logNorm_counts)

# If you want to visualize the counts without the zero values, add these lines :
raw_counts <- raw_counts[raw_counts > 0]
logNorm_counts <- logNorm_counts[logNorm_counts > 0]

# And then again : 
hist(raw_counts)

hist(logNorm_counts)

# You can play with the "breaks" parameters to have a better visualization :
hist(raw_counts, breaks = 100)

hist(logNorm_counts, breaks = 100)

We can also plot the distribution of all genes in our dataset, and visualize it with some boxplots.

# Prepare the data 
raw_df <- data.frame(Expression = raw_counts, Type = "Raw Counts")
norm_df <- data.frame(Expression = logNorm_counts, Type = "Normalized Counts")
plot_data <- rbind(raw_df, norm_df)

# Create the boxplot
boxplot_plot <- ggplot(plot_data, aes(x = Type, y = Expression, fill = Type)) +
  geom_boxplot(outlier.shape = NA) +
  scale_y_log10() +
  ggtitle("Boxplots of Gene Expression Across All Genes (Before and After Normalization)") +
  xlab("Normalization Status") +
  ylab("Expression (Log10 Scale)") +
  theme_minimal()

# Print the boxplot
print(boxplot_plot)

What can you observe when comparing the 2 boxplots ?

How do we know that we effectively normalized the data by reading the boxplots ?

Until now, we explored the variability among the genes count values. We can do the same with the cells :

# Prepare the data 
df <- as.data.frame(sobj_tiny[[]])
df$cell_index <- rownames(df)

# Plots before and after normalization
ggplot(df, aes(x = cell_index, y = nCount_RNA)) + geom_bar(stat="identity")

ggplot(df, aes(x = cell_index, y = log10_nCount_RNA)) + geom_bar(stat="identity")

What can you conclude about the sequencing depths in the cells ?

Finally, we want to see the impact of the normalization on a highly variable gene. We chose to focus on “Tmsb10”.

Can you plot the total number of Tmsb10 expression versus the total number of UMI ?

You can use the FeatureScatter function for this.

Answers
FeatureScatter(sobj_tiny,
               feature1 = "nCount_RNA",
               feature2 = "Tmsb10",
               slot = "count")

Compare it with the normalized values

What can you observe ?

Answers
FeatureScatter(sobj_tiny,
               feature1 = "nCount_RNA",
               feature2 = "Tmsb10",
               slot = "data")

Discussion : LogNormalize reduces the impact of sequencing depth differences but may not handle highly variable genes effectively.

Method 2: SCTransform

SCTransform is a normalization method designed for single-cell RNA-seq data. It uses a variance-stabilizing transformation (VST) to model and remove technical noise, making it particularly effective for datasets with high variability and technical noise, such as dropouts and differences in sequencing depth. SCTransform is available in the Seurat package and is often used as an alternative to traditional log normalization.

Key Features of SCTransform :

  • Variance stabilization: SCTransform aims to stabilize the variance across genes, making it less affected by highly variable or highly expressed genes.

  • Data modeling: it uses a generalized linear model (GLM) to model the counts as a function of sequencing depth, accounting for technical noise.

  • No manual scaling needed: SCTransform automatically standardizes the data, so additional scaling is not required before PCA.

# Apply SCTransform
sobj_tiny <- SCTransform(sobj_tiny,
                         verbose = FALSE)

# Visualize the effect of SCTransform
VlnPlot(sobj_tiny,
        features = c("Srm", "Apoe", "Top2a"),
        log = TRUE)

Try to answers all the same questions we asked above for SCTransform

Compare the results between log normalization and SCTransform

Here is an example of the results on the Violin Plots :

# Normalize data using LogNormalize (for comparison)
sobj_tiny_log <- NormalizeData(sobj_tiny, normalization.method = "LogNormalize")
sobj_tiny_log <- ScaleData(sobj_tiny_log)

# Normalize data using SCTransform
sobj_tiny_sct <- SCTransform(sobj_tiny, verbose = FALSE)

# Compare the distributions of gene expression using violin plots
vln_log <- VlnPlot(sobj_tiny_log, features = c("Srm", "Apoe", "Top2a"), log = TRUE)
vln_sct <- VlnPlot(sobj_tiny_sct, features = c("Srm", "Apoe", "Top2a"))

# Display the plots side by side
vln_log

vln_sct

Discussion: SCTransform handles variability better, making it suitable for noisy datasets.

Exercises 3 : Scaling

Until now, we have talked a lot about the normalization but what about the scaling ??? Well, it is just an additional step that we use to standardize the expression values across all cells.

A few words on scaling

In single-cell RNA-seq, scaling typically refers to standardizing the expression of each gene across all cells (not across genes !). The goal is to make the expression of each gene comparable across all cells, ensuring that highly expressed genes with large variance don’t dominate the analysis.

It typically involves transforming each gene’s expression values so that they have a mean of 0 and a standard deviation of 1 (z-score transformation). This ensures that each gene contributes equally to downstream analyses, such as PCA, clustering, and UMAP.

If you want a comparison, it’s like converting temperatures from Celsius and Fahrenheit to a common scale so we can compare them easily. Moreover, we scale to ensure that highly expressed genes don’t dominate the analysis (e.g., in PCA or clustering). Without scaling, a small set of genes with very high variance can bias the results.

Please note that not all genes need to be scaled (e.g., marker genes of interest). Be careful also that scaling should be done after normalization, not before.

How it works ?

For each gene, we take the expression values across all cells and :

1- Center them by subtracting the mean expression of the gene

2- Scale them by dividing by the standard deviation of the gene

\[z = \frac{x - \text{mean}}{\text{standard deviation}}\]

Where, \(x\) is the expression value for a particular gene in a specific cell. Mean and standard deviation are calculated across all cells for that gene.

What it is not ? Scaling is not about making the expression of each cell comparable across all genes. That’s what normalization (e.g., LogNormalize, SCTransform) does : adjusting for sequencing depth differences across cells.

How to scale the data in Seurat ? With the function ScaleData.

Start by exploring the ScaleData function.

What are the arguments you think will be useful for us ?

Scale the data of the tiny Seurat object

Answers
sobj_tiny@active.assay <- "RNA"
sobj_tiny <- ScaleData(sobj_tiny)

Store the new scaled data in a matrix called scaled_data

Answers
scaled_data <- as.matrix(sobj_tiny@assays$RNA@layers$scale.data)

After scaling, the expression values for each gene will have a mean of 0 and a standard deviation of 1.

Can you check that you also find those values ?

You will probably need the R functions rowMeans and rowSds. If you don’t know them, have a look at their help page.

Answers
# Calculate the mean and standard deviation for each gene without using apply()
gene_means <- rowMeans(scaled_data)
gene_sds <- rowSds(scaled_data)

# Check the results
summary(gene_means)
##     Min.  1st Qu.   Median     Mean  3rd Qu.     Max. 
## -0.02466 -0.00290  0.00000 -0.00453  0.00000  0.00000
summary(gene_sds)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.0000  0.9327  1.0000  0.8859  1.0000  1.0000

Advanced : use scaled data for differential expression analysis

Let’s see what happens when we use scaled data for DE analysis. You can run the analyses with the functions FindMarkers or FindAllMarkers. For this exercise, it will be more useful if you first run a clustering analyses in order to get identified groups of cells.

Example :

sobj_tiny <- NormalizeData(sobj_tiny)
sobj_tiny <- FindVariableFeatures(sobj_tiny)
sobj_tiny <- ScaleData(sobj_tiny)
sobj_tiny <- RunPCA(sobj_tiny, dims = 1:20)
sobj_tiny <- FindNeighbors(sobj_tiny, dims = 1:20)
sobj_tiny <- FindClusters(sobj_tiny, resolution = 0.5)
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 1000
## Number of edges: 41202
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7202
## Number of communities: 7
## Elapsed time: 0 seconds
sobj_tiny <- RunUMAP(sobj_tiny, dims = 1:20)
DimPlot(sobj_tiny, reduction = "umap")

You can also run a first differential expression analyses to be able to compare after your results :

# Perform DE analyses
sobj_tiny.markers <- FindAllMarkers(sobj_tiny, only.pos = TRUE)

# Extract top 10 genes per cluster
top10 <- sobj_tiny.markers %>%
    group_by(cluster) %>%
    dplyr::filter(avg_log2FC > 1) %>%
    slice_head(n = 10) %>%
    ungroup()

# Display heatmap of DE results
DoHeatmap(sobj_tiny, features = top10$gene) + NoLegend() + theme(text = element_text(size = 7))
## Warning in DoHeatmap(sobj_tiny, features = top10$gene): The following features
## were omitted as they were not found in the scale.data slot for the RNA assay:
## Gm11725, Nln, Notch3, Dennd3, Pdk1, Slc43a1, Tle2, Smoc1, Cep85, Tdrd5,
## BB031773, Plxdc1, Patj

# Display top DE results
head(top10, n = 30)
## # A tibble: 30 × 7
##            p_val avg_log2FC pct.1 pct.2 p_val_adj cluster gene    
##            <dbl>      <dbl> <dbl> <dbl>     <dbl> <fct>   <chr>   
##  1 0.00000000185       1.68 0.242 0.114 0.0000231 0       Patj    
##  2 0.00000000657       1.05 0.374 0.247 0.0000821 0       Plxdc1  
##  3 0.0000000760        1.60 0.206 0.096 0.000951  0       Slc6a19 
##  4 0.000000922         1.48 0.16  0.068 0.0115    0       BB031773
##  5 0.00000100          1.41 0.204 0.1   0.0125    0       Tdrd5   
##  6 0.00000290          1.04 0.364 0.278 0.0362    0       Cep85   
##  7 0.00000474          2.02 0.107 0.036 0.0593    0       Smoc1   
##  8 0.00000877          1.76 0.115 0.043 0.110     0       Tle2    
##  9 0.0000318           1.06 0.318 0.236 0.398     0       Slc43a1 
## 10 0.0000360           1.23 0.262 0.181 0.451     0       Pdk1    
## # ℹ 20 more rows
Answers
# Attempt DE analysis using scaled data
sobj_tiny_scaled.markers <- FindAllMarkers(sobj_tiny, only.pos = TRUE, assay = "RNA", slot = "scale.data")
## Warning: No DE genes identified
## Warning: The following tests were not performed:
## Warning: When testing 0 versus all:
##  subscript out of bounds
## Warning: When testing 1 versus all:
##  subscript out of bounds
## Warning: When testing 2 versus all:
##  subscript out of bounds
## Warning: When testing 3 versus all:
##  subscript out of bounds
## Warning: When testing 4 versus all:
##  subscript out of bounds
## Warning: When testing 5 versus all:
##  subscript out of bounds
## Warning: When testing 6 versus all:
##  subscript out of bounds
# Not working !! Scaled data is designed for clustering, dimensionality reduction, and visualizations.

Can you try also with raw counts (not normalized) ? What do you observe ?

Answers
markers <- FindAllMarkers(sobj_tiny, only.pos = TRUE, slot = "counts")

top10 <- markers %>%
    group_by(cluster) %>%
    dplyr::filter(avg_log2FC > 1) %>%
    slice_head(n = 10) %>%
    ungroup()

# Display heatmap of DE results
DoHeatmap(sobj_tiny, features = top10$gene) + NoLegend() + theme(text = element_text(size = 7))

# Display top DE results
head(markers)
##                p_val avg_log2FC pct.1 pct.2    p_val_adj cluster    gene
## mt-Co2  1.493196e-21  0.1506044 0.997 0.995 1.867690e-17       0  mt-Co2
## Eif1    4.006820e-18  0.1422588 0.977 0.992 5.011731e-14       0    Eif1
## Cd3d    1.441059e-17  0.1042215 0.936 0.979 1.802476e-13       0    Cd3d
## Serf2   1.783625e-15  0.1231241 0.952 0.980 2.230958e-11       0   Serf2
## Laptm5  2.012218e-15  0.1146634 0.944 0.959 2.516882e-11       0  Laptm5
## mt-Atp8 3.073331e-15  0.1966736 1.000 0.995 3.844122e-11       0 mt-Atp8

Discussion : * Why might the DE results be non-significant or unexpected ? * What information is lost during scaling that is critical for DE analysis ?

Advanced: change the values of the features option

This value is set by default to 2000. It represents the number of highly variable genes to take into account. You can explore the impact it has on the PCA. Hint : use the RunPCA function from Seurat.

To answer this question, you will need a function to reinitialize your Seurat object as follows :

# Function to reinitialize the Seurat object
reinitialize_sobj <- function(seurat_obj) {
  
  # Copy the object
  seurat_copy <- seurat_obj
  # Remove variable features
  seurat_copy@assays$RNA@meta.data <- data.frame()
  # Remove normalized and scales data
  seurat_copy@assays$RNA@layers$data <- NULL
  seurat_copy@assays$RNA@layers$scale.data <- NULL
  # Remove PCA and UMAP
  seurat_copy@reductions <- list()
  # Remove SCT
  seurat_copy@assays$SCT <- NULL
  # Set active assay to RNA
  seurat_copy@active.assay <- "RNA"
  
  return(seurat_copy)
}
Answers
# Function to perform analysis with a specified number of variable features
perform_analysis <- function(seurat_obj, num_features) {
  # Create a new Seurat object from raw counts
  seurat_copy <- reinitialize_sobj(seurat_obj)
  
  # Normalize the data
  seurat_copy <- NormalizeData(seurat_copy)
  
  # Set the variable features (up to the specified number)
  seurat_copy <- FindVariableFeatures(seurat_copy,
                                     selection.method = "vst",
                                     nfeatures = num_features)
  
  # Identify the 10 most highly variable genes
  top_features <- head(VariableFeatures(seurat_copy), 20)
  print(top_features)
  
  # Scale the data, and run PCA
  seurat_copy <- ScaleData(seurat_copy)
  seurat_copy <- RunPCA(seurat_copy,
                        features = VariableFeatures(seurat_copy),
                        verbose = FALSE)
  
  # Generate PCA plot
  pca_plot <- DimPlot(seurat_copy, reduction = "pca") + ggtitle(paste(num_features, "Variable Features"))
  
  # Generate DimHeatmap for the first two principal components
  DimHeatmap(seurat_copy,
             dims = 1:2,
             balanced = TRUE,
             reduction = "pca")
  
  # Cluster the cells and run UMAP
  seurat_copy <- FindNeighbors(seurat_copy, dims = 1:20)
  seurat_copy <- FindClusters(seurat_copy, resolution = 0.5)
  seurat_copy <- RunUMAP(seurat_copy, dims = 1:20)
  umap_plot <- DimPlot(seurat_copy, reduction = "umap")
  
  # Find markers
  seurat_copy.markers <- FindAllMarkers(seurat_copy, only.pos = TRUE)
  top10 <- seurat_copy.markers %>%
    group_by(cluster) %>%
    dplyr::filter(avg_log2FC > 1) %>%
    slice_head(n = 10) %>%
    ungroup()
  heatmap_markers_plot <- DoHeatmap(seurat_copy, features = top10$gene) + NoLegend()
  
  # Generate Variable Feature Plot with labeled top 20 features
  vf_plot <- VariableFeaturePlot(seurat_copy)
  vf_plot <- LabelPoints(plot = vf_plot, points = top_features, repel = TRUE) +
    ggtitle(paste("Top Variable Features:", num_features))
  
  # Print your new Seurat object
  print(seurat_copy)
  
  # Return a list of plots
  return(list(
    SeuratObject = seurat_copy,
    PCA = pca_plot,
    VariableFeatures = vf_plot,
    UMAP_plot = umap_plot,
    Heatmap_markers = heatmap_markers_plot))
}

# Analyze with different numbers of variable features
results_100 <- perform_analysis(sobj_tiny, 100)
##  [1] "Hist1h2ao" "Pclaf"     "Hist1h2ap" "Rrm2"      "Top2a"     "Hist1h2ae"
##  [7] "Hmgb2"     "Ms4a4b"    "Tuba1b"    "Itm2a"     "Birc5"     "Trbv16"   
## [13] "Ifi27l2a"  "Trbv29"    "Trbv15"    "Trbv5"     "H2-K1"     "Nkg7"     
## [19] "Ube2c"     "Hist1h1b"
## Warning in irlba(A = t(x = object), nv = npcs, ...): You're computing too large
## a percentage of total singular values, use a standard svd instead.
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 1000
## Number of edges: 34398
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.8030
## Number of communities: 14
## Elapsed time: 0 seconds
## Warning in DoHeatmap(seurat_copy, features = top10$gene): The following
## features were omitted as they were not found in the scale.data slot for the RNA
## assay: Tyrobp, Vcam1, Ear2, C1qb, Lgals3, C1qc, Cd74, Fcer1g, C1qa, Rflnb, Cd7,
## Gimap4, H2-Q7, Gm2682, S1pr1, Itgae, Tmc4, Gm20517, Trdj1, Ephb6, Trbj2-1,
## Cyp11a1, Trbj1-5, Traj2, Gm49355, Sdr42e1, Comp, Gabrd, Sdcbp2, Paqr8,
## C920008G01Rik, E2f8, Clspn, Rad51, Tmie, Pcsk1, Grik1, Gm30373, Zc2hc1a, Fbln1,
## Trbv13-2, Lypd1, Cd69, Cd40lg, Tigit, Nab2, Egr2, 0610005C13Rik, Sbsn, Uckl1os,
## 1700029I15Rik, Catspere2, 4732491K20Rik, Gm29019, Trav3-4, Gm44731, Zfp316,
## Gm43062, Gm15417, Tyro3, Gm38037, Myo5b, Slc9a3r2, Slit3, Trbj2-2, Gm17349,
## Gm34552, Gm30906, Trav6-2, Ubap1l, A830035O19Rik, Gm12840, Isl2, Star, Eif2s3y,
## Gm26526, Trav6-4, Mgam, Trim80, Cabyr, Tes, Casp9, Pam, Gm45623, Gm13920,
## 2810414N06Rik, Gm13919, Tpbgl, Trbj2-4, Fbln2, Ddx3y, 5830405F06Rik, Gm33104,
## Trbv24, Pde4dip, Tle2, Trbv23, Trbv21

## An object of class Seurat 
## 12508 features across 1000 samples within 1 assay 
## Active assay: RNA (12508 features, 100 variable features)
##  3 layers present: counts, data, scale.data
##  2 dimensional reductions calculated: pca, umap
results_2000 <- perform_analysis(sobj_tiny, 2000)
##  [1] "Hist1h2ao" "Pclaf"     "Hist1h2ap" "Rrm2"      "Top2a"     "Hist1h2ae"
##  [7] "Hmgb2"     "Ms4a4b"    "Tuba1b"    "Itm2a"     "Birc5"     "Trbv16"   
## [13] "Ifi27l2a"  "Trbv29"    "Trbv15"    "Trbv5"     "H2-K1"     "Nkg7"     
## [19] "Ube2c"     "Hist1h1b"
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 1000
## Number of edges: 41202
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7202
## Number of communities: 7
## Elapsed time: 0 seconds
## Warning in DoHeatmap(seurat_copy, features = top10$gene): The following
## features were omitted as they were not found in the scale.data slot for the RNA
## assay: Gm11725, Nln, Notch3, Dennd3, Pdk1, Slc43a1, Tle2, Smoc1, Cep85, Tdrd5,
## BB031773, Plxdc1, Patj

## An object of class Seurat 
## 12508 features across 1000 samples within 1 assay 
## Active assay: RNA (12508 features, 2000 variable features)
##  3 layers present: counts, data, scale.data
##  2 dimensional reductions calculated: pca, umap
results_5000 <- perform_analysis(sobj_tiny, 5000)
##  [1] "Hist1h2ao" "Pclaf"     "Hist1h2ap" "Rrm2"      "Top2a"     "Hist1h2ae"
##  [7] "Hmgb2"     "Ms4a4b"    "Tuba1b"    "Itm2a"     "Birc5"     "Trbv16"   
## [13] "Ifi27l2a"  "Trbv29"    "Trbv15"    "Trbv5"     "H2-K1"     "Nkg7"     
## [19] "Ube2c"     "Hist1h1b"
## Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck
## 
## Number of nodes: 1000
## Number of edges: 40708
## 
## Running Louvain algorithm...
## Maximum modularity in 10 random starts: 0.7142
## Number of communities: 6
## Elapsed time: 0 seconds
## Warning in DoHeatmap(seurat_copy, features = top10$gene): The following
## features were omitted as they were not found in the scale.data slot for the RNA
## assay: St8sia1, Ccnd3, Tiparp, Gm44175, Cep85, Plxdc1, Gtf2h4, Arl5c

## An object of class Seurat 
## 12508 features across 1000 samples within 1 assay 
## Active assay: RNA (12508 features, 5000 variable features)
##  3 layers present: counts, data, scale.data
##  2 dimensional reductions calculated: pca, umap
# Display PCA plots for comparison
print(results_100$PCA)

print(results_2000$PCA)

print(results_5000$PCA)

# Display Variable Feature Plots
print(results_100$VariableFeatures)
## Warning in scale_x_log10(): log-10 transformation introduced infinite values.
## Warning: ggrepel: 4 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

print(results_2000$VariableFeatures)
## Warning in scale_x_log10(): log-10 transformation introduced infinite values.
## ggrepel: 4 unlabeled data points (too many overlaps). Consider increasing max.overlaps

print(results_5000$VariableFeatures)
## Warning in scale_x_log10(): log-10 transformation introduced infinite values.
## Warning: ggrepel: 2 unlabeled data points (too many overlaps). Consider
## increasing max.overlaps

# Display UMAPs
print(results_100$UMAP_plot)

print(results_2000$UMAP_plot)

print(results_5000$UMAP_plot)

# Display UMAPs
print(results_100$Heatmap_markers)

print(results_2000$Heatmap_markers)

print(results_5000$Heatmap_markers)

Advanced: compare the distributions of means and SD values in SCTransform and scaled data

Answers
# Set all genes as variable features for consistency
#VariableFeatures(sobj_tiny) <- rownames(sobj_tiny)

# Apply SCTransform using all genes
sobj_tiny_sct <- SCTransform(sobj_tiny,
                         verbose = FALSE,
                         variable.features.n = 2000)

# Normalize and scale using LogNormalize + ScaleData for all genes
sobj_tiny@active.assay <- "RNA"
sobj_tiny_log <- NormalizeData(sobj_tiny, normalization.method = "LogNormalize", scale.factor = 10000)
sobj_tiny_log <- FindVariableFeatures(sobj_tiny_log)
sobj_tiny_log <- ScaleData(sobj_tiny_log, features = VariableFeatures(sobj_tiny_log))

# Extract the scaled data matrices for comparison
sct_data <- as.matrix(sobj_tiny_sct@assays$SCT@scale.data)
scaled_data <- as.matrix(sobj_tiny_log@assays$RNA@layers$scale.data)

# Check if the dimensions are now consistent
dim(sct_data)
## [1] 2000 1000
dim(scaled_data)
## [1] 2000 1000
# Calculate mean and standard deviation for SCTransform
sct_means <- rowMeans(sct_data)
sct_sds <- rowSds(sct_data)

# Calculate mean and standard deviation for ScaleData
scaled_means <- rowMeans(scaled_data)
scaled_sds <- rowSds(scaled_data)

# Create a data frame for comparison
comparison_df <- data.frame(
  Gene = rownames(sct_data),
  SCT_Mean = sct_means,
  SCT_SD = sct_sds,
  Scaled_Mean = scaled_means,
  Scaled_SD = scaled_sds
)

summary(comparison_df$SCT_SD)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.3781  1.0342  1.0687  1.0704  1.1125  2.4202
summary(comparison_df$Scaled_SD)
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.3563  0.9531  1.0000  0.9340  1.0000  1.0000
# Plot histograms of the standard deviations for both methods
hist_plot <- ggplot(comparison_df, aes(x = SCT_SD)) +
  geom_histogram(bins = 30, fill = "blue", alpha = 0.5) +
  geom_histogram(aes(x = Scaled_SD), bins = 30, fill = "red", alpha = 0.5) +
  ggtitle("Comparison of Standard Deviations: SCTransform vs. ScaleData") +
  xlab("Standard Deviation") +
  ylab("Frequency") +
  theme_minimal()

summary(comparison_df$Scaled_Mean)
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
## -0.024654 -0.004104  0.000000 -0.003902  0.000000  0.000000
summary(comparison_df$SCT_Mean)
##       Min.    1st Qu.     Median       Mean    3rd Qu.       Max. 
## -9.015e-17 -8.882e-18  1.874e-19  7.455e-19  9.770e-18  3.025e-16
# Scatter plot comparing mean expression values
scatter_plot <- ggplot(comparison_df, aes(x = Scaled_Mean, y = SCT_Mean)) +
  geom_point(alpha = 0.5) +
  geom_abline(slope = 1, intercept = 0, linetype = "dashed", color = "red") +
  ggtitle("Scatter Plot of Mean Expression: ScaleData vs. SCTransform") +
  xlab("Mean Expression (ScaleData)") +
  ylab("Mean Expression (SCTransform)") +
  theme_minimal()

# Display the plots
hist_plot

scatter_plot

Exercices 4 : Regression of unwanted signal

What do you mean by regression ?

In single-cell RNA-seq, regressing out variables (e.g., mitochondrial content, cell cycle effects) removes the influence of those variables on gene expression. The goal is to mitigate technical or unwanted biological variability, leaving a dataset that better reflects the biological phenomena of interest.

How can I remove unwanted sources of variation ?

In Seurat, you can use the ScaleData function to remove unwanted sources of variation. For example, we could ‘regress out’ heterogeneity associated with cell cycle stage (“CC_Seurat_Phase”), or mitochondrial contamination (“percent.mt”). This adjusts each gene’s expression by fitting a linear model for the specified variables and replacing the values with the residuals. The command to regress out mitochondrial contamination would be :

sobj_tiny <- ScaleData(sobj_tiny, vars.to.regress = "percent.mt")

SCTransform also allows to do a regression. If you run SCTransform, you don’t have to then run ScaleData as it’s all included in the SCTransform function. We can specify the variable to regress out like this :

sobj_tiny <- SCTransform(sobj_tiny, vars.to.regress = "percent.mt")

Compare the impact of regression of cell cycle on a few genes

You can take these 3 genes that we used before (“Srm”, “Apoe”, “Top2a”), or any other of your choice. In the correction, we use “Cdk1”. Use the function FeatureScatter to easily visualize the effect.

Answers
feature <- "Cdk1"
sobj_tiny@active.assay <- "RNA"
sobj_tiny <- NormalizeData(sobj_tiny)
sobj_tiny <- FindVariableFeatures(sobj_tiny)

# Scatter plot before regression
sobj_tiny <- ScaleData(sobj_tiny)
sobj_tiny$feature_value <- as.vector(LayerData(sobj_tiny, assay = "RNA", layer = "scale.data")[feature,]) # copy the values of the feature in the metadata 
FeatureScatter(sobj_tiny, feature1 = "CC_Seurat_G2M.Score", feature2 = "feature_value", slot = "scale.data")

FeatureScatter(sobj_tiny, feature1 = "CC_Seurat_S.Score", feature2 = "feature_value", slot = "scale.data") 

# Regress out cell cycle phase
sobj_tiny_cc_reg <- ScaleData(sobj_tiny, vars.to.regress = "CC_Seurat_Phase")
sobj_tiny_cc_reg$feature_value <- as.vector(LayerData(sobj_tiny_cc_reg, assay = "RNA", layer = "scale.data")[feature,]) # copy the values of the feature in the metadata 
FeatureScatter(sobj_tiny_cc_reg, feature1 = "CC_Seurat_G2M.Score", feature2 = "feature_value", slot = "scale.data") 

FeatureScatter(sobj_tiny_cc_reg, feature1 = "CC_Seurat_S.Score", feature2 = "feature_value", slot = "scale.data")

Run the PCA in both cases and check if it has an impact on the 2 first components

Answers
# Before regression
sobj_tiny <- RunPCA(sobj_tiny, verbose = FALSE)
DimHeatmap(sobj_tiny, dims = 1:2, balanced = TRUE, reduction = "pca")

# After regression
sobj_tiny <- RunPCA(sobj_tiny_cc_reg, verbose = FALSE)
DimHeatmap(sobj_tiny_cc_reg, dims = 1:2, balanced = TRUE, reduction = "pca") 

Can you spot other genes ?

Try to find genes that are highly affected by the regression, or other that expression do not move after regression.

Do the same with SCTransform (compare with and without regression)

Super advanced exercise

Take several pipelines that we have seen, and evaluate how it impacts the results of the differentially expressed genes.

For example, you could compare :

  • Normalization with scaling with \(x\) variables genes
  • Normalization with scaling with \(x\) variables genes with regression of the cell cycle
  • SCTransform with \(x\) variables genes
  • SCTransform with \(x\) variables genes with regression of the cell cycle And you can repeat with any value of \(x\).

Summary and key takeaways

Congrats ! You’ve made it through your first experience with normalization, scaling and regression. Here are a few takeaway messages :

  • Normalization adjusts expression values across cells, accounting for sequencing depth.
  • Scaling adjusts expression values across genes, standardizing them for equal variance.
  • Use raw counts for DE analysis, normalized data for visualization, and scaled data for PCA.
  • Different normalization methods can lead to different biological interpretations; choose based on the characteristics of your dataset.
  • Always visualize your data at each step to check if the transformations are appropriate.
Session Info
sessionInfo()
## R version 4.3.1 (2023-06-16)
## Platform: aarch64-apple-darwin20 (64-bit)
## Running under: macOS Ventura 13.4
## 
## Matrix products: default
## BLAS:   /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRblas.0.dylib 
## LAPACK: /Library/Frameworks/R.framework/Versions/4.3-arm64/Resources/lib/libRlapack.dylib;  LAPACK version 3.11.0
## 
## locale:
## [1] en_US.UTF-8/en_US.UTF-8/en_US.UTF-8/C/en_US.UTF-8/en_US.UTF-8
## 
## time zone: Europe/Paris
## tzcode source: internal
## 
## attached base packages:
## [1] stats4    stats     graphics  grDevices utils     datasets  methods  
## [8] base     
## 
## other attached packages:
##  [1] dplyr_1.1.4                 scater_1.30.1              
##  [3] scuttle_1.12.0              SingleCellExperiment_1.24.0
##  [5] SummarizedExperiment_1.32.0 Biobase_2.62.0             
##  [7] GenomicRanges_1.54.1        GenomeInfoDb_1.38.8        
##  [9] IRanges_2.36.0              S4Vectors_0.40.2           
## [11] BiocGenerics_0.48.1         MatrixGenerics_1.14.0      
## [13] matrixStats_1.4.0           patchwork_1.2.0            
## [15] ggplot2_3.5.1               Seurat_5.1.0               
## [17] SeuratObject_5.0.2          sp_2.1-4                   
## 
## loaded via a namespace (and not attached):
##   [1] RcppAnnoy_0.0.22          splines_4.3.1            
##   [3] later_1.3.2               bitops_1.0-8             
##   [5] tibble_3.2.1              polyclip_1.10-7          
##   [7] fastDummies_1.7.4         lifecycle_1.0.4          
##   [9] globals_0.16.3            lattice_0.22-5           
##  [11] MASS_7.3-60               magrittr_2.0.3           
##  [13] limma_3.58.1              plotly_4.10.4            
##  [15] sass_0.4.9                rmarkdown_2.28           
##  [17] jquerylib_0.1.4           yaml_2.3.10              
##  [19] httpuv_1.6.15             sctransform_0.4.1        
##  [21] spam_2.10-0               spatstat.sparse_3.1-0    
##  [23] reticulate_1.39.0         cowplot_1.1.3            
##  [25] pbapply_1.7-2             RColorBrewer_1.1-3       
##  [27] abind_1.4-5               zlibbioc_1.48.2          
##  [29] Rtsne_0.17                purrr_1.0.2              
##  [31] RCurl_1.98-1.16           GenomeInfoDbData_1.2.11  
##  [33] ggrepel_0.9.5             irlba_2.3.5.1            
##  [35] listenv_0.9.1             spatstat.utils_3.1-0     
##  [37] goftest_1.2-3             RSpectra_0.16-2          
##  [39] spatstat.random_3.3-1     fitdistrplus_1.2-1       
##  [41] parallelly_1.38.0         DelayedMatrixStats_1.24.0
##  [43] leiden_0.4.3.1            codetools_0.2-19         
##  [45] DelayedArray_0.28.0       tidyselect_1.2.1         
##  [47] farver_2.1.2              ScaledMatrix_1.10.0      
##  [49] viridis_0.6.5             spatstat.explore_3.3-2   
##  [51] jsonlite_1.8.8            BiocNeighbors_1.20.1     
##  [53] progressr_0.14.0          ggridges_0.5.6           
##  [55] survival_3.5-7            tools_4.3.1              
##  [57] ica_1.0-3                 Rcpp_1.0.13              
##  [59] glue_1.7.0                gridExtra_2.3            
##  [61] SparseArray_1.2.2         xfun_0.49                
##  [63] withr_3.0.1               fastmap_1.2.0            
##  [65] fansi_1.0.6               digest_0.6.37            
##  [67] rsvd_1.0.5                R6_2.5.1                 
##  [69] mime_0.12                 colorspace_2.1-1         
##  [71] scattermore_1.2           tensor_1.5               
##  [73] spatstat.data_3.1-2       utf8_1.2.4               
##  [75] tidyr_1.3.1               generics_0.1.3           
##  [77] data.table_1.16.0         httr_1.4.7               
##  [79] htmlwidgets_1.6.4         S4Arrays_1.2.0           
##  [81] uwot_0.1.16               pkgconfig_2.0.3          
##  [83] gtable_0.3.5              lmtest_0.9-40            
##  [85] XVector_0.42.0            htmltools_0.5.8.1        
##  [87] dotCall64_1.1-1           scales_1.3.0             
##  [89] png_0.1-8                 spatstat.univar_3.0-1    
##  [91] knitr_1.48                rstudioapi_0.15.0        
##  [93] reshape2_1.4.4            nlme_3.1-164             
##  [95] cachem_1.1.0              zoo_1.8-12               
##  [97] stringr_1.5.1             KernSmooth_2.23-22       
##  [99] parallel_4.3.1            miniUI_0.1.1.1           
## [101] vipor_0.4.7               ggrastr_1.0.2            
## [103] pillar_1.9.0              grid_4.3.1               
## [105] vctrs_0.6.5               RANN_2.6.2               
## [107] promises_1.3.0            BiocSingular_1.18.0      
## [109] beachmat_2.18.0           xtable_1.8-4             
## [111] cluster_2.1.6             beeswarm_0.4.0           
## [113] evaluate_0.24.0           cli_3.6.3                
## [115] compiler_4.3.1            rlang_1.1.4              
## [117] crayon_1.5.3              future.apply_1.11.2      
## [119] labeling_0.4.3            plyr_1.8.9               
## [121] ggbeeswarm_0.7.2          stringi_1.8.4            
## [123] viridisLite_0.4.2         deldir_2.0-4             
## [125] BiocParallel_1.36.0       munsell_0.5.1            
## [127] lazyeval_0.2.2            spatstat.geom_3.3-2      
## [129] Matrix_1.6-4              RcppHNSW_0.6.0           
## [131] sparseMatrixStats_1.14.0  future_1.34.0            
## [133] statmod_1.5.0             shiny_1.9.1              
## [135] highr_0.11                ROCR_1.0-11              
## [137] igraph_2.0.3              bslib_0.8.0
LS0tCnRpdGxlOiAiUHJvY2Vzc2luZzogbm9ybWFsaXphdGlvbiwgc2NhbGluZyBhbmQgcmVncmVzc2lvbiIKc3VidGl0bGU6ICJFQkFJSSBuMSAyMDI0OiBzY1JOQS1TZXEgd29ya3Nob3AiCmF1dGhvcjogIk5hdGhhbGllIExlaG1hbm4iCmRhdGU6ICIyMDI0LTExLTIwIgpvdXRwdXQ6CiAgaHRtbF9kb2N1bWVudDogCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQotLS0KCmBgYHs9aHRtbH0KPHN0eWxlPgovKiBDdXN0b20gQ1NTIGZvciBoZWFkZXIgY29sb3JzICovCmgxIHsKICBjb2xvcjogIzJjM2U1MDsgLyogRGFyayBibHVlICovCn0KaDIgewogIGNvbG9yOiAjMzQ5OGRiOyAvKiBMaWdodCBibHVlICovCn0KaDMgewogIGNvbG9yOiAjOGU0NGFkOyAvKiBQdXJwbGUgKi8KfQpoNCB7CiAgY29sb3I6ICMzMmE4NTY7IC8qIERhcmsgZ3JlZW4gKi8KfQpoNSB7CiAgY29sb3I6ICNmMzljMTI7IC8qIE9yYW5nZSAqLwp9Cjwvc3R5bGU+CmBgYApgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgbWVzc2FnZSA9IEZBTFNFKQpgYGAKCiMgSW50cm9kdWN0aW9uCgpUaGlzIHByYWN0aWNhbCBzZXNzaW9uIGlzIGRlc2lnbmVkIHRvIGhlbHAgeW91IHVuZGVyc3RhbmQgdGhlIGltcG9ydGFuY2Ugb2Ygbm9ybWFsaXphdGlvbiwgc2NhbGluZywgYW5kIHJlZ3Jlc3Npb24gaW4gc2luZ2xlLWNlbGwgUk5BLXNlcSBhbmFseXNpcy4gWW91IHdpbGwgYXBwbHkgdGhlc2Ugc3RlcHMgdXNpbmcgZGlmZmVyZW50IG1ldGhvZHMgYW5kIHZpc3VhbGl6ZSB0aGUgZWZmZWN0cyBvbiB0aGUgZGF0YS4KCioqSW5wdXQgZGF0YSoqOiBTZXVyYXQgb2JqZWN0IGNvbnRhaW5pbmcgdGhlIGZpbHRlcmVkIGNvdW50IG1hdHJpeCBmcm9tIHRoZSBwcmV2aW91cyBjbGFzcy4gSXQncyBjYWxsZWQgYDA1X1REM0FfUzVfRG91YmxldHMuRmlsdGVyZWRfMTI1MDguNDAzNS5SRFNgLgoKKipPdXRwdXQgZGF0YSoqOiBTZXVyYXQgb2JqZWN0IGFmdGVyIHRoZSBub3JtYWxpemF0aW9uLCBzY2FsaW5nIGFuZCByZWdyZXNzaW9uLiBJdCdzIGNhbGxlZCBgc29ial9URDNBX25vcm1hbGl6ZWQucmRzYC4KCiMjIExlYXJuaW5nIE9iamVjdGl2ZXMKCi0gICBEaXNjdXNzIHdoeSBub3JtYWxpemluZyBjb3VudHMgaXMgbmVjZXNzYXJ5IHRvIGNvbXBhcmUgY2VsbHMKLSAgIEJlIGFibGUgdG8gdW5kZXJzdGFuZCBkaWZmZXJlbnQgbm9ybWFsaXphdGlvbiBhcHByb2FjaGVzCi0gICBCZSBhYmxlIHRvIGRlY2lkZSB3aGVuIHRvIHJlZ3Jlc3Mgb3V0IGEgZ2l2ZW4gdmFyaWFibGUsIGJ5IGV2YWx1YXRpbmcgdGhlIGVmZmVjdHMgZnJvbSBhbnkgdW53YW50ZWQgc291cmNlcyBvZiB2YXJpYXRpb24KCiMjIE1vdGl2YXRpb24KClVwIHRvIHRoaXMgcG9pbnQsIHdlIGhhdmUgZmlsdGVyZWQgb3V0IGxvdy1xdWFsaXR5IGNlbGxzLCBhbWJpZW50IFJOQSBjb250YW1pbmF0aW9uLCBhbmQgZG91YmxldHMuIFRoZSBkYXRhIGlzIG5vdyBhIGNvdW50IG1hdHJpeCB3aXRoIGNlbGxzIGFzIGNvbHVtbnMgYW5kIGdlbmVzIGFzIHJvd3MuIFRoZXNlIGNvdW50cyByZWZsZWN0IHRoZSBjYXB0dXJlLCByZXZlcnNlIHRyYW5zY3JpcHRpb24sIGFuZCBzZXF1ZW5jaW5nIHN0ZXBzIG9mIHRoZSBzY1JOQS1zZXEgZXhwZXJpbWVudCwgZWFjaCBpbnRyb2R1Y2luZyB2YXJpYWJpbGl0eS4gVGhpcyBtZWFucyB0aGUgb2JzZXJ2ZWQgZGlmZmVyZW5jZXMgaW4gZ2VuZSBleHByZXNzaW9uIG1heSBiZSBpbmZsdWVuY2VkIGJ5IHNhbXBsaW5nIG5vaXNlIHJhdGhlciB0aGFuIHRydWUgYmlvbG9naWNhbCBkaWZmZXJlbmNlcywgcmVzdWx0aW5nIGluIHVuZXZlbiB2YXJpYW5jZSBhY3Jvc3MgdGhlIGRhdGFzZXQuCgpOb3JtYWxpemF0aW9uIGFkZHJlc3NlcyB0aGlzIGJ5IGFkanVzdGluZyByYXcgY291bnRzIHRvIG1pbmltaXplIHRoZSBlZmZlY3RzIG9mIHZhcmlhYmxlIHNhbXBsaW5nLCBtYWtpbmcgdGhlIGRhdGEgbW9yZSBzdWl0YWJsZSBmb3IgYW5hbHlzaXMsIHdoaWNoIG9mdGVuIGFzc3VtZXMgdW5pZm9ybSB2YXJpYW5jZS4gVmFyaW91cyBub3JtYWxpemF0aW9uIHRlY2huaXF1ZXMgZXhpc3QsIGVhY2ggdGFpbG9yZWQgdG8gc3VwcG9ydCBkaWZmZXJlbnQgZG93bnN0cmVhbSBhbmFseXNlcy4KCkEgcmVjZW50IHN0dWR5IGJ5IEFobG1hbm4tRWx0emUgYW5kIEh1YmVyICgyMDIzKSBiZW5jaG1hcmtlZCAyMiBub3JtYWxpemF0aW9uIG1ldGhvZHMuIEl0J3MgZXNzZW50aWFsIHRvIHNlbGVjdCB0aGUgbm9ybWFsaXphdGlvbiBtZXRob2QgYmFzZWQgb24gdGhlIHNwZWNpZmljIGFuYWx5c2lzIGdvYWxzLiBIb3dldmVyLCBmb3IgdGhpcyBwcmFjdGljYWwgd2Ugd2lsbCBqdXN0IGV4cGxvcmUgdGhlIG1vc3Qgd2VsbC1rbm93biB3YXkgdG8gZG9pbmcgc2luZ2xlLWNlbGwgbm9ybWFsaXphdGlvbi4KCiMjIFNldHVwCgpXZSBmaXJzdCBsb2FkIHRoZSBwYWNrYWdlcyB0aGF0IGFyZSBuZWVkZWQgOgoKYGBge3IgbG9hZC1saWJyYXJpZXMsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KHNjYXRlcikKbGlicmFyeShkcGx5cikKYGBgCgpXZSB0aGVuIGltcG9ydCB0aGUgZGF0YSB0aGF0IGhhdmUgYmVlbiBwcmV2aW91c2x5IGZpbHRlcmVkIChyZW1pbmRlciA6IHdlIHJlbW92ZWQgbG93IHF1YWxpdHkgY2VsbHMsIHJlbW92ZWQgYW1iaWVudCBSTkEgYW5kIHNjb3JlZCBkb3VibGV0cykgOgoKYGBge3J9CnNvYmogPC0gcmVhZFJEUygiaW5wdXQvMDVfVEQzQV9TNV9Eb3VibGV0cy5GaWx0ZXJlZF8xMjUwOC40MDM1LlJEUyIpCnNvYmoKYGBgCgpGb3IgdGhlIHNha2Ugb2YgdGhpcyBwcmFjdGljYWwgc2Vzc2lvbiwgd2Ugd2lsbCB1c2UgYSBzbWFsbGVyIGRhdGFzZXQgd2hpY2ggY29udGFpbnMgb25seSAxMDAwIGNlbGxzLiBJbiBvcmRlciB0byBkbyB0aGlzLCB3ZSBjYW4gdXNlIHRoZSBTZXVyYXQgb2JqZXQgYXMgYSBkYXRhZnJhbWUsIHdoZXJlIHJvd3MgYXJlIGdlbmVzLCBhbmQgY29sdW1ucyBhcmUgY2VsbHMuIEluIFIgbGFuZ3VhZ2UsIHRoaXMgY29ycmVzcG9uZHMgdG8gOiBgc29ialtnZW5lcyxjZWxsc11gLgoKV2Ugd2lsbCB0YWtlIHRoZSBmaXJzdCAxMDAwIGNlbGxzLCBieSB1c2luZyB0aGUgcmFuZ2UgYDE6MTAwMGAsIGxpa2UgdGhpcyA6CgpgYGB7cn0KIyBTZWxlY3QgdGhlIGNlbGxzIHRvIGtlZXAgCnNvYmpfdGlueSA8LSBzb2JqWywxOjEwMDBdCiMgQ2hlY2sgdGhhdCB0aGUgb2JqZWN0IGNvbnRhaW5zIDEwMDAgY2VsbHMKc29ial90aW55CmBgYAoKIyBFeGVyY2ljZXMgMSA6IGV4cGxvcmUgYE5vcm1hbGl6ZURhdGFgIGZ1bmN0aW9uCgpMZXQncyBzdGFydCBieSBleHBsb3JpbmcgdGhlIGBOb3JtYWxpemVEYXRhYCBmdW5jdGlvbi4KCiMjIyBXaGF0IGlzIHRoZSBpbnB1dCBvZiB0aGlzIGZ1bmN0aW9uID8KCjxkZXRhaWxzPgoKPHN1bW1hcnk+QW5zd2Vyczwvc3VtbWFyeT4KClRoZSBpbnB1dCBpcyB5b3VyIFNldXJhdCBvYmplY3QgKGZvciB1cyA6IGBzb2JqX3RpbnlgKQoKPC9kZXRhaWxzPgoKIyMjIEhvdyBtYW55IG5vcm1hbGl6YXRpb24gbWV0aG9kcyBhcmUgdGhleSA/CgpDYW4geW91IHNwb3Qgd2hpY2ggaXMgdGhlIGRlZmF1bHQgb25lID8KCjxkZXRhaWxzPgoKPHN1bW1hcnk+QW5zd2Vyczwvc3VtbWFyeT4KClRoZXJlIGFyZSAzIG5vcm1hbGl6YXRpb24gYXBwcm9hY2hlcyBpbmNsdWRlZCBpbiBTZXVyYXQgOiBMb2dOb3JtYWxpemUsIENMUiBhbmQgUkMuIFRoZSBkZWZhdWx0IG9uZSBpcyBMb2dOb3JtYWxpemUuCgo8L2RldGFpbHM+CgojIyMgUnVuIHRoZSBgTm9ybWFsaXplRGF0YWAgZnVuY3Rpb24gb24gdGhlIGBzb2JqX3RpbnlgCgpZb3UgY2FuIG5vdyBleHBsb3JlIHlvdXIgbmV3IG9iamVjdCA6IHdoZXJlIGFyZSB0aGUgbm9ybWFsaXplZCB2YWx1ZXMgc3RvcmVkID8gQ2FuIHlvdSBwcmludCB0aGUgdmFsdWVzIG9mIHRoZSBmaXJzdCA0IGdlbmVzIGluIHRoZSAxMCBmaXJzdCBjZWxscyA/Cgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcnM8L3N1bW1hcnk+CgpgYGB7cn0Kc29ial90aW55IDwtIE5vcm1hbGl6ZURhdGEoc29ial90aW55KQoKIyBUaGUgbm9ybWFsaXplZCB2YWx1ZXMgYXJlIHN0b3JlZCBpbiB0aGUgc2xvdCAnZGF0YScgOiAKc29ial90aW55QGFzc2F5cyRSTkEkZGF0YVsxOjQsIDE6MTBdCiMgWW91IGNhbiBjb21wYXJlIGl0IHRvIHRoZSBzbG90ICdjb3VudCcgKHJhdyBjb3VudCkgOgpzb2JqX3RpbnlAYXNzYXlzJFJOQSRjb3VudFsxOjQsIDE6MTBdCmBgYAoKPC9kZXRhaWxzPgoKIyBFeGVyY2lzZXMgMiA6IGxldCdzIHRlc3Qgc29tZSBkaWZmZXJlbnQgbm9ybWFsaXphdGlvbiBzdHJhdGVnaWVzCgojIyBNZXRob2QgMTogYExvZ05vcm1hbGl6ZWAKCmBMb2dOb3JtYWxpemVgIG1ldGhvZCBvciAic2NhbGluZyBub3JtYWxpemF0aW9uIiBpcyB0aGUgc2ltcGxlc3QgYW5kIG1vc3QgY29tbW9ubHkgdXNlZCBjbGFzcyBvZiBub3JtYWxpemF0aW9uIGFwcHJvYWNoZXMuIFRoaXMgaW52b2x2ZXMgZGl2aWRpbmcgYWxsIGNvdW50cyBmb3IgZWFjaCBjZWxsIGJ5IGEgY2VsbC1zcGVjaWZpYyBzY2FsaW5nIGZhY3Rvciwgb2Z0ZW4gY2FsbGVkIGEgInNpemUgZmFjdG9yIiAoQW5kZXJzIGFuZCBIdWJlciAyMDEwKS4gVGhlIGFzc3VtcHRpb24gaGVyZSBpcyB0aGF0IGFueSBjZWxsLXNwZWNpZmljIGJpYXMgKGUuZy4sIGluIGNhcHR1cmUgb3IgYW1wbGlmaWNhdGlvbiBlZmZpY2llbmN5KSBhZmZlY3RzIGFsbCBnZW5lcyBlcXVhbGx5IHZpYSBzY2FsaW5nIG9mIHRoZSBleHBlY3RlZCBtZWFuIGNvdW50IGZvciB0aGF0IGNlbGwuIFRoZSBzaXplIGZhY3RvciBmb3IgZWFjaCBjZWxsIHJlcHJlc2VudHMgdGhlIGVzdGltYXRlIG9mIHRoZSByZWxhdGl2ZSBiaWFzIGluIHRoYXQgY2VsbCwgc28gZGl2aXNpb24gb2YgaXRzIGNvdW50cyBieSBpdHMgc2l6ZSBmYWN0b3Igc2hvdWxkIHJlbW92ZSB0aGF0IGJpYXMuIFRoZSByZXN1bHRpbmcgIm5vcm1hbGl6ZWQgZXhwcmVzc2lvbiB2YWx1ZXMiIGNhbiB0aGVuIGJlIHVzZWQgZm9yIGRvd25zdHJlYW0gYW5hbHlzZXMgc3VjaCBhcyBjbHVzdGVyaW5nIGFuZCBkaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24uIChGcm9tIDxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xNC9PU0NBLmJhc2ljL25vcm1hbGl6YXRpb24uaHRtbD4pCgpgYGB7ciBsb2dub3JtYWxpemV9CiMgQXBwbHkgTG9nTm9ybWFsaXplCnNvYmpfdGlueSA8LSBOb3JtYWxpemVEYXRhKHNvYmpfdGlueSwKICAgICAgICAgICAgICAgICAgICAgICAgICBub3JtYWxpemF0aW9uLm1ldGhvZCA9ICJMb2dOb3JtYWxpemUiLAogICAgICAgICAgICAgICAgICAgICAgICAgIHNjYWxlLmZhY3RvciA9IDEwMDAwKQpgYGAKCldlIGNhbiBub3cgdmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgYExvZ05vcm1hbGl6ZWAgd2l0aCBzb21lIHZpb2xpbiBwbG90cyA6CgpgYGB7cn0KVmxuUGxvdChzb2JqX3RpbnksCiAgICAgICAgZmVhdHVyZXMgPSBjKCJTcm0iLCAiQXBvZSIsICJUb3AyYSIpLAogICAgICAgIGxheWVyID0gImRhdGEiKQpgYGAKCiMjIyBDYW4geW91IGV4cGxhaW4gdG8geW91ciBuZWlnaGJvciB3aGF0IGRvZXMgdGhpcyBwbG90IG1lYW4gPwoKVmlvbGluIHBsb3RzIGFyZSBzb21ldGltZXMgaGFyZCB0byByZWFkLiBIZXJlIGlzIGEgZnVuY3Rpb24gdG8gYnVpbGQgYm94IHBsb3RzIG91dCBvZiBTZXVyYXQgb2JqZWN0cyA6CgpgYGB7cn0KQm94UGxvdEZyb21TZXVyYXQgPC0gZnVuY3Rpb24oc2V1cmF0X29iaiwgZmVhdHVyZXMsIGxvZyA9IFRSVUUsIGxheWVyID0gImRhdGEiLCBwbG90LnRpdGxlID0gIkJveCBQbG90IikgewogICMgRXh0cmFjdCB0aGUgZGF0YSBmb3IgdGhlIGZlYXR1cmVzIG9mIGludGVyZXN0CiAgZGYgPC0gU2V1cmF0OjpGZXRjaERhdGEoc2V1cmF0X29iaiwgdmFycyA9IGZlYXR1cmVzLCBsYXllciA9IGxheWVyKQogIAogICMgQWRkIG1ldGFkYXRhIGZvciBncm91cGluZyBpZiBncm91cC5ieSBpcyBzcGVjaWZpZWQKICBkZiA8LSBjYmluZChkZiwgc2V1cmF0X29ialtbXV0pCiAgCiAgIyBSZXNoYXBlIHRoZSBkYXRhIGZvciBnZ3Bsb3QyCiAgZGZfbG9uZyA8LSB0aWR5cjo6Z2F0aGVyKGRmLCBrZXkgPSAiZmVhdHVyZSIsIHZhbHVlID0gImV4cHJlc3Npb24iLCBncm91cCkKICBkZl9sb25nIDwtIGRmICU+JSBncm91cF9ieV8oZ3JvdXAuYnkpICU+JQogICAgdGlkeXI6OnBpdm90X2xvbmdlcihjb2xzID0gZmVhdHVyZXMpCgogICMgQ3JlYXRlIGJveCBwbG90cyB1c2luZyBnZ3Bsb3QyCiAgaWYgKGxvZyA9PSBUUlVFKSB7CiAgICBwIDwtIGdncGxvdChkZl9sb25nLCBnZ3Bsb3QyOjphZXMoeCA9IG5hbWUsIHkgPSBsb2cxMCh2YWx1ZSkpKSArCiAgICBnZW9tX2JveHBsb3Qob3V0bGllci5zaGFwZSA9IE5BKSArCiAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgbGFicyh4ID0gIkZlYXR1cmUiLCB5ID0gIkV4cHJlc3Npb24gTGV2ZWwgKGxvZzEwKSIpICsKICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpCiAgfSBlbHNlIHsKICAgIHAgPC0gZ2dwbG90KGRmX2xvbmcsIGdncGxvdDI6OmFlcyh4ID0gbmFtZSwgeSA9IHZhbHVlKSkgKwogICAgZ2VvbV9ib3hwbG90KG91dGxpZXIuc2hhcGUgPSBOQSkgKwogICAgdGhlbWVfbWluaW1hbCgpICsKICAgIGxhYnMoeCA9ICJGZWF0dXJlIiwgeSA9ICJFeHByZXNzaW9uIExldmVsIikgKwogICAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKICB9CiAgcmV0dXJuKHApCn0KCiMgRXhhbXBsZSB1c2FnZQojIEJveFBsb3RGcm9tU2V1cmF0KHNvYmpfdGlueSwgZmVhdHVyZXMgPSBjKCJTcm0iLCAiQXBvZSIsICJUb3AyYSIpKQpgYGAKCiMjIyBDYW4geW91IGRvIHRoZSBzYW1lIFZpb2xpbiBwbG90cyBidXQgd2l0aCB0aGUgcmF3IGNvdW50cyA/CgpZb3UgY2FuIHRyeSBhbHNvIHdpdGggdGhlIGJveHBsb3RzLgoKPGRldGFpbHM+Cgo8c3VtbWFyeT5BbnN3ZXJzPC9zdW1tYXJ5PgoKYGBge3J9ClZsblBsb3Qoc29ial90aW55LAogICAgICAgIGZlYXR1cmVzID0gYygiU3JtIiwgIkFwb2UiLCAiVG9wMmEiKSwgCiAgICAgICAgbGF5ZXIgPSAiY291bnQiKQpgYGAKCjwvZGV0YWlscz4KCiMjIyBDYW4geW91IHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiB0aGUgdmFsdWVzID8KClRyeSB0byBkbyBhbiBoaXN0b2dyYW0sIHdpdGggMSkgdGhlIHJhdyBjb3VudHMgdmFsdWVzIGFuZCAyKSB0aGUgbG9nIG5vcm1hbGl6ZWQgdmFsdWVzLiBZb3UgY2FuIHVzZSB0aGUgUiBgaGlzdCgpYCBmdW5jdGlvbi4gV2hhdCBkbyB5b3Ugb2JzZXJ2ZSA/IFlvdSB3aWxsIG5lZWQgdG8gZXh0cmFjdCB5b3VyIGRhdGEgd2l0aCBhIGNvbW1hbmQgc3VjaCBhcyA6CgpgYGB7cn0KcmF3X2NvdW50cyA8LSBhcy52ZWN0b3IoYXMubWF0cml4KHNvYmpfdGlueUBhc3NheXMkUk5BJGNvdW50KSkKYGBgCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcnM8L3N1bW1hcnk+CgpgYGB7cn0KIyBSYXcgdmFsdWVzCnJhd19jb3VudHMgPC0gYXMudmVjdG9yKGFzLm1hdHJpeChzb2JqX3RpbnlAYXNzYXlzJFJOQSRjb3VudCkpCmhpc3QocmF3X2NvdW50cykKCiMgTG9nIE5vcm0gdmFsdWVzCmxvZ05vcm1fY291bnRzIDwtIGFzLnZlY3Rvcihhcy5tYXRyaXgoc29ial90aW55QGFzc2F5cyRSTkEkZGF0YSkpCmhpc3QobG9nTm9ybV9jb3VudHMpCgojIElmIHlvdSB3YW50IHRvIHZpc3VhbGl6ZSB0aGUgY291bnRzIHdpdGhvdXQgdGhlIHplcm8gdmFsdWVzLCBhZGQgdGhlc2UgbGluZXMgOgpyYXdfY291bnRzIDwtIHJhd19jb3VudHNbcmF3X2NvdW50cyA+IDBdCmxvZ05vcm1fY291bnRzIDwtIGxvZ05vcm1fY291bnRzW2xvZ05vcm1fY291bnRzID4gMF0KCiMgQW5kIHRoZW4gYWdhaW4gOiAKaGlzdChyYXdfY291bnRzKQpoaXN0KGxvZ05vcm1fY291bnRzKQoKIyBZb3UgY2FuIHBsYXkgd2l0aCB0aGUgImJyZWFrcyIgcGFyYW1ldGVycyB0byBoYXZlIGEgYmV0dGVyIHZpc3VhbGl6YXRpb24gOgpoaXN0KHJhd19jb3VudHMsIGJyZWFrcyA9IDEwMCkKaGlzdChsb2dOb3JtX2NvdW50cywgYnJlYWtzID0gMTAwKQpgYGAKCjwvZGV0YWlscz4KCldlIGNhbiBhbHNvIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhbGwgZ2VuZXMgaW4gb3VyIGRhdGFzZXQsIGFuZCB2aXN1YWxpemUgaXQgd2l0aCBzb21lIGJveHBsb3RzLgoKYGBge3J9CiMgUHJlcGFyZSB0aGUgZGF0YSAKcmF3X2RmIDwtIGRhdGEuZnJhbWUoRXhwcmVzc2lvbiA9IHJhd19jb3VudHMsIFR5cGUgPSAiUmF3IENvdW50cyIpCm5vcm1fZGYgPC0gZGF0YS5mcmFtZShFeHByZXNzaW9uID0gbG9nTm9ybV9jb3VudHMsIFR5cGUgPSAiTm9ybWFsaXplZCBDb3VudHMiKQpwbG90X2RhdGEgPC0gcmJpbmQocmF3X2RmLCBub3JtX2RmKQoKIyBDcmVhdGUgdGhlIGJveHBsb3QKYm94cGxvdF9wbG90IDwtIGdncGxvdChwbG90X2RhdGEsIGFlcyh4ID0gVHlwZSwgeSA9IEV4cHJlc3Npb24sIGZpbGwgPSBUeXBlKSkgKwogIGdlb21fYm94cGxvdChvdXRsaWVyLnNoYXBlID0gTkEpICsKICBzY2FsZV95X2xvZzEwKCkgKwogIGdndGl0bGUoIkJveHBsb3RzIG9mIEdlbmUgRXhwcmVzc2lvbiBBY3Jvc3MgQWxsIEdlbmVzIChCZWZvcmUgYW5kIEFmdGVyIE5vcm1hbGl6YXRpb24pIikgKwogIHhsYWIoIk5vcm1hbGl6YXRpb24gU3RhdHVzIikgKwogIHlsYWIoIkV4cHJlc3Npb24gKExvZzEwIFNjYWxlKSIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgUHJpbnQgdGhlIGJveHBsb3QKcHJpbnQoYm94cGxvdF9wbG90KQpgYGAKCiMjIyBXaGF0IGNhbiB5b3Ugb2JzZXJ2ZSB3aGVuIGNvbXBhcmluZyB0aGUgMiBib3hwbG90cyA/CgpIb3cgZG8gd2Uga25vdyB0aGF0IHdlIGVmZmVjdGl2ZWx5IG5vcm1hbGl6ZWQgdGhlIGRhdGEgYnkgcmVhZGluZyB0aGUgYm94cGxvdHMgPwoKVW50aWwgbm93LCB3ZSBleHBsb3JlZCB0aGUgdmFyaWFiaWxpdHkgYW1vbmcgdGhlIGdlbmVzIGNvdW50IHZhbHVlcy4gV2UgY2FuIGRvIHRoZSBzYW1lIHdpdGggdGhlIGNlbGxzIDoKCmBgYHtyfQojIFByZXBhcmUgdGhlIGRhdGEgCmRmIDwtIGFzLmRhdGEuZnJhbWUoc29ial90aW55W1tdXSkKZGYkY2VsbF9pbmRleCA8LSByb3duYW1lcyhkZikKCiMgUGxvdHMgYmVmb3JlIGFuZCBhZnRlciBub3JtYWxpemF0aW9uCmdncGxvdChkZiwgYWVzKHggPSBjZWxsX2luZGV4LCB5ID0gbkNvdW50X1JOQSkpICsgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiKQpnZ3Bsb3QoZGYsIGFlcyh4ID0gY2VsbF9pbmRleCwgeSA9IGxvZzEwX25Db3VudF9STkEpKSArIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IikKYGBgCgojIyMgV2hhdCBjYW4geW91IGNvbmNsdWRlIGFib3V0IHRoZSBzZXF1ZW5jaW5nIGRlcHRocyBpbiB0aGUgY2VsbHMgPwoKRmluYWxseSwgd2Ugd2FudCB0byBzZWUgdGhlIGltcGFjdCBvZiB0aGUgbm9ybWFsaXphdGlvbiBvbiBhIGhpZ2hseSB2YXJpYWJsZSBnZW5lLiBXZSBjaG9zZSB0byBmb2N1cyBvbiAiVG1zYjEwIi4KCiMjIyBDYW4geW91IHBsb3QgdGhlIHRvdGFsIG51bWJlciBvZiBUbXNiMTAgZXhwcmVzc2lvbiB2ZXJzdXMgdGhlIHRvdGFsIG51bWJlciBvZiBVTUkgPwoKWW91IGNhbiB1c2UgdGhlIGBGZWF0dXJlU2NhdHRlcmAgZnVuY3Rpb24gZm9yIHRoaXMuCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcnM8L3N1bW1hcnk+CgpgYGB7cn0KRmVhdHVyZVNjYXR0ZXIoc29ial90aW55LAogICAgICAgICAgICAgICBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAiVG1zYjEwIiwKICAgICAgICAgICAgICAgc2xvdCA9ICJjb3VudCIpCmBgYAoKPC9kZXRhaWxzPgoKIyMjIENvbXBhcmUgaXQgd2l0aCB0aGUgbm9ybWFsaXplZCB2YWx1ZXMKCldoYXQgY2FuIHlvdSBvYnNlcnZlID8KCjxkZXRhaWxzPgoKPHN1bW1hcnk+QW5zd2Vyczwvc3VtbWFyeT4KCmBgYHtyfQpGZWF0dXJlU2NhdHRlcihzb2JqX3RpbnksCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLAogICAgICAgICAgICAgICBmZWF0dXJlMiA9ICJUbXNiMTAiLAogICAgICAgICAgICAgICBzbG90ID0gImRhdGEiKQpgYGAKCjwvZGV0YWlscz4KCioqRGlzY3Vzc2lvbiA6KiogTG9nTm9ybWFsaXplIHJlZHVjZXMgdGhlIGltcGFjdCBvZiBzZXF1ZW5jaW5nIGRlcHRoIGRpZmZlcmVuY2VzIGJ1dCBtYXkgbm90IGhhbmRsZSBoaWdobHkgdmFyaWFibGUgZ2VuZXMgZWZmZWN0aXZlbHkuCgojIyBNZXRob2QgMjogYFNDVHJhbnNmb3JtYAoKYFNDVHJhbnNmb3JtYCBpcyBhIG5vcm1hbGl6YXRpb24gbWV0aG9kIGRlc2lnbmVkIGZvciBzaW5nbGUtY2VsbCBSTkEtc2VxIGRhdGEuIEl0IHVzZXMgYSB2YXJpYW5jZS1zdGFiaWxpemluZyB0cmFuc2Zvcm1hdGlvbiAoVlNUKSB0byBtb2RlbCBhbmQgcmVtb3ZlIHRlY2huaWNhbCBub2lzZSwgbWFraW5nIGl0IHBhcnRpY3VsYXJseSBlZmZlY3RpdmUgZm9yIGRhdGFzZXRzIHdpdGggaGlnaCB2YXJpYWJpbGl0eSBhbmQgdGVjaG5pY2FsIG5vaXNlLCBzdWNoIGFzIGRyb3BvdXRzIGFuZCBkaWZmZXJlbmNlcyBpbiBzZXF1ZW5jaW5nIGRlcHRoLiBgU0NUcmFuc2Zvcm1gIGlzIGF2YWlsYWJsZSBpbiB0aGUgU2V1cmF0IHBhY2thZ2UgYW5kIGlzIG9mdGVuIHVzZWQgYXMgYW4gYWx0ZXJuYXRpdmUgdG8gdHJhZGl0aW9uYWwgbG9nIG5vcm1hbGl6YXRpb24uCgoqKktleSBGZWF0dXJlcyBvZiBTQ1RyYW5zZm9ybSA6KioKCi0gICBWYXJpYW5jZSBzdGFiaWxpemF0aW9uOiBTQ1RyYW5zZm9ybSBhaW1zIHRvIHN0YWJpbGl6ZSB0aGUgdmFyaWFuY2UgYWNyb3NzIGdlbmVzLCBtYWtpbmcgaXQgbGVzcyBhZmZlY3RlZCBieSBoaWdobHkgdmFyaWFibGUgb3IgaGlnaGx5IGV4cHJlc3NlZCBnZW5lcy4KCi0gICBEYXRhIG1vZGVsaW5nOiBpdCB1c2VzIGEgZ2VuZXJhbGl6ZWQgbGluZWFyIG1vZGVsIChHTE0pIHRvIG1vZGVsIHRoZSBjb3VudHMgYXMgYSBmdW5jdGlvbiBvZiBzZXF1ZW5jaW5nIGRlcHRoLCBhY2NvdW50aW5nIGZvciB0ZWNobmljYWwgbm9pc2UuCgotICAgTm8gbWFudWFsIHNjYWxpbmcgbmVlZGVkOiBTQ1RyYW5zZm9ybSBhdXRvbWF0aWNhbGx5IHN0YW5kYXJkaXplcyB0aGUgZGF0YSwgc28gYWRkaXRpb25hbCBzY2FsaW5nIGlzIG5vdCByZXF1aXJlZCBiZWZvcmUgUENBLgoKYGBge3Igc2N0cmFuc2Zvcm0sIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgQXBwbHkgU0NUcmFuc2Zvcm0Kc29ial90aW55IDwtIFNDVHJhbnNmb3JtKHNvYmpfdGlueSwKICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKCiMgVmlzdWFsaXplIHRoZSBlZmZlY3Qgb2YgU0NUcmFuc2Zvcm0KVmxuUGxvdChzb2JqX3RpbnksCiAgICAgICAgZmVhdHVyZXMgPSBjKCJTcm0iLCAiQXBvZSIsICJUb3AyYSIpLAogICAgICAgIGxvZyA9IFRSVUUpCmBgYAoKIyMjIFRyeSB0byBhbnN3ZXJzIGFsbCB0aGUgc2FtZSBxdWVzdGlvbnMgd2UgYXNrZWQgYWJvdmUgZm9yIFNDVHJhbnNmb3JtCgojIyMgQ29tcGFyZSB0aGUgcmVzdWx0cyBiZXR3ZWVuIGxvZyBub3JtYWxpemF0aW9uIGFuZCBTQ1RyYW5zZm9ybQoKSGVyZSBpcyBhbiBleGFtcGxlIG9mIHRoZSByZXN1bHRzIG9uIHRoZSBWaW9saW4gUGxvdHMgOgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgTm9ybWFsaXplIGRhdGEgdXNpbmcgTG9nTm9ybWFsaXplIChmb3IgY29tcGFyaXNvbikKc29ial90aW55X2xvZyA8LSBOb3JtYWxpemVEYXRhKHNvYmpfdGlueSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIikKc29ial90aW55X2xvZyA8LSBTY2FsZURhdGEoc29ial90aW55X2xvZykKCiMgTm9ybWFsaXplIGRhdGEgdXNpbmcgU0NUcmFuc2Zvcm0Kc29ial90aW55X3NjdCA8LSBTQ1RyYW5zZm9ybShzb2JqX3RpbnksIHZlcmJvc2UgPSBGQUxTRSkKCiMgQ29tcGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBnZW5lIGV4cHJlc3Npb24gdXNpbmcgdmlvbGluIHBsb3RzCnZsbl9sb2cgPC0gVmxuUGxvdChzb2JqX3RpbnlfbG9nLCBmZWF0dXJlcyA9IGMoIlNybSIsICJBcG9lIiwgIlRvcDJhIiksIGxvZyA9IFRSVUUpCnZsbl9zY3QgPC0gVmxuUGxvdChzb2JqX3Rpbnlfc2N0LCBmZWF0dXJlcyA9IGMoIlNybSIsICJBcG9lIiwgIlRvcDJhIikpCgojIERpc3BsYXkgdGhlIHBsb3RzIHNpZGUgYnkgc2lkZQp2bG5fbG9nCnZsbl9zY3QKYGBgCgoqKkRpc2N1c3Npb246KiogU0NUcmFuc2Zvcm0gaGFuZGxlcyB2YXJpYWJpbGl0eSBiZXR0ZXIsIG1ha2luZyBpdCBzdWl0YWJsZSBmb3Igbm9pc3kgZGF0YXNldHMuCgojIEV4ZXJjaXNlcyAzIDogU2NhbGluZwoKVW50aWwgbm93LCB3ZSBoYXZlIHRhbGtlZCBhIGxvdCBhYm91dCB0aGUgbm9ybWFsaXphdGlvbiBidXQgd2hhdCBhYm91dCB0aGUgc2NhbGluZyA/Pz8gV2VsbCwgaXQgaXMganVzdCBhbiBhZGRpdGlvbmFsIHN0ZXAgdGhhdCB3ZSB1c2UgdG8gc3RhbmRhcmRpemUgdGhlIGV4cHJlc3Npb24gdmFsdWVzIGFjcm9zcyBhbGwgY2VsbHMuCgojIyBBIGZldyB3b3JkcyBvbiBzY2FsaW5nCgpJbiBzaW5nbGUtY2VsbCBSTkEtc2VxLCBzY2FsaW5nIHR5cGljYWxseSByZWZlcnMgdG8gc3RhbmRhcmRpemluZyB0aGUgZXhwcmVzc2lvbiBvZiBlYWNoIGdlbmUgYWNyb3NzIGFsbCAqKmNlbGxzKiogKG5vdCBhY3Jvc3MgZ2VuZXMgISkuIFRoZSBnb2FsIGlzIHRvIG1ha2UgdGhlIGV4cHJlc3Npb24gb2YgZWFjaCBnZW5lIGNvbXBhcmFibGUgYWNyb3NzIGFsbCBjZWxscywgZW5zdXJpbmcgdGhhdCBoaWdobHkgZXhwcmVzc2VkIGdlbmVzIHdpdGggbGFyZ2UgdmFyaWFuY2UgZG9uJ3QgZG9taW5hdGUgdGhlIGFuYWx5c2lzLgoKSXQgdHlwaWNhbGx5IGludm9sdmVzIHRyYW5zZm9ybWluZyBlYWNoIGdlbmUncyBleHByZXNzaW9uIHZhbHVlcyBzbyB0aGF0IHRoZXkgaGF2ZSBhIG1lYW4gb2YgMCBhbmQgYSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgMSAoei1zY29yZSB0cmFuc2Zvcm1hdGlvbikuIFRoaXMgZW5zdXJlcyB0aGF0IGVhY2ggZ2VuZSBjb250cmlidXRlcyBlcXVhbGx5IHRvIGRvd25zdHJlYW0gYW5hbHlzZXMsIHN1Y2ggYXMgUENBLCBjbHVzdGVyaW5nLCBhbmQgVU1BUC4KCklmIHlvdSB3YW50IGEgY29tcGFyaXNvbiwgaXQncyBsaWtlIGNvbnZlcnRpbmcgdGVtcGVyYXR1cmVzIGZyb20gQ2Vsc2l1cyBhbmQgRmFocmVuaGVpdCB0byBhIGNvbW1vbiBzY2FsZSBzbyB3ZSBjYW4gY29tcGFyZSB0aGVtIGVhc2lseS4gTW9yZW92ZXIsIHdlIHNjYWxlIHRvIGVuc3VyZSB0aGF0IGhpZ2hseSBleHByZXNzZWQgZ2VuZXMgZG9uJ3QgZG9taW5hdGUgdGhlIGFuYWx5c2lzIChlLmcuLCBpbiBQQ0Egb3IgY2x1c3RlcmluZykuIFdpdGhvdXQgc2NhbGluZywgYSBzbWFsbCBzZXQgb2YgZ2VuZXMgd2l0aCB2ZXJ5IGhpZ2ggdmFyaWFuY2UgY2FuIGJpYXMgdGhlIHJlc3VsdHMuCgpQbGVhc2Ugbm90ZSB0aGF0IG5vdCBhbGwgZ2VuZXMgbmVlZCB0byBiZSBzY2FsZWQgKGUuZy4sIG1hcmtlciBnZW5lcyBvZiBpbnRlcmVzdCkuIEJlIGNhcmVmdWwgYWxzbyB0aGF0IHNjYWxpbmcgc2hvdWxkIGJlIGRvbmUgYWZ0ZXIgbm9ybWFsaXphdGlvbiwgbm90IGJlZm9yZS4KCioqSG93IGl0IHdvcmtzID8qKgoKRm9yIGVhY2ggZ2VuZSwgd2UgdGFrZSB0aGUgZXhwcmVzc2lvbiB2YWx1ZXMgYWNyb3NzIGFsbCBjZWxscyBhbmQgOgoKMS0gQ2VudGVyIHRoZW0gYnkgc3VidHJhY3RpbmcgdGhlIG1lYW4gZXhwcmVzc2lvbiBvZiB0aGUgZ2VuZQoKMi0gU2NhbGUgdGhlbSBieSBkaXZpZGluZyBieSB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBnZW5lCgokJHogPSBcZnJhY3t4IC0gXHRleHR7bWVhbn19e1x0ZXh0e3N0YW5kYXJkIGRldmlhdGlvbn19JCQKCldoZXJlLCAkeCQgaXMgdGhlIGV4cHJlc3Npb24gdmFsdWUgZm9yIGEgcGFydGljdWxhciBnZW5lIGluIGEgc3BlY2lmaWMgY2VsbC4gTWVhbiBhbmQgc3RhbmRhcmQgZGV2aWF0aW9uIGFyZSBjYWxjdWxhdGVkIGFjcm9zcyBhbGwgY2VsbHMgZm9yIHRoYXQgZ2VuZS4KCioqV2hhdCBpdCBpcyBub3QgPyoqIFNjYWxpbmcgaXMgbm90IGFib3V0IG1ha2luZyB0aGUgZXhwcmVzc2lvbiBvZiBlYWNoIGNlbGwgY29tcGFyYWJsZSBhY3Jvc3MgYWxsIGdlbmVzLiBUaGF0J3Mgd2hhdCBub3JtYWxpemF0aW9uIChlLmcuLCBMb2dOb3JtYWxpemUsIFNDVHJhbnNmb3JtKSBkb2VzIDogYWRqdXN0aW5nIGZvciBzZXF1ZW5jaW5nIGRlcHRoIGRpZmZlcmVuY2VzIGFjcm9zcyBjZWxscy4KCioqSG93IHRvIHNjYWxlIHRoZSBkYXRhIGluIFNldXJhdCA/KiogV2l0aCB0aGUgZnVuY3Rpb24gYFNjYWxlRGF0YWAuCgojIyMgU3RhcnQgYnkgZXhwbG9yaW5nIHRoZSBgU2NhbGVEYXRhYCBmdW5jdGlvbi4KCldoYXQgYXJlIHRoZSBhcmd1bWVudHMgeW91IHRoaW5rIHdpbGwgYmUgdXNlZnVsIGZvciB1cyA/CgojIyMgU2NhbGUgdGhlIGRhdGEgb2YgdGhlIHRpbnkgU2V1cmF0IG9iamVjdAoKPGRldGFpbHM+Cgo8c3VtbWFyeT5BbnN3ZXJzPC9zdW1tYXJ5PgoKYGBge3J9CnNvYmpfdGlueUBhY3RpdmUuYXNzYXkgPC0gIlJOQSIKc29ial90aW55IDwtIFNjYWxlRGF0YShzb2JqX3RpbnkpCmBgYAoKPC9kZXRhaWxzPgoKIyMjIFN0b3JlIHRoZSBuZXcgc2NhbGVkIGRhdGEgaW4gYSBtYXRyaXggY2FsbGVkIGBzY2FsZWRfZGF0YWAKCjxkZXRhaWxzPgoKPHN1bW1hcnk+QW5zd2Vyczwvc3VtbWFyeT4KCmBgYHtyfQpzY2FsZWRfZGF0YSA8LSBhcy5tYXRyaXgoc29ial90aW55QGFzc2F5cyRSTkFAbGF5ZXJzJHNjYWxlLmRhdGEpCmBgYAoKPC9kZXRhaWxzPgoKQWZ0ZXIgc2NhbGluZywgdGhlIGV4cHJlc3Npb24gdmFsdWVzIGZvciBlYWNoIGdlbmUgd2lsbCBoYXZlIGEgbWVhbiBvZiAwIGFuZCBhIHN0YW5kYXJkIGRldmlhdGlvbiBvZiAxLgoKIyMjIENhbiB5b3UgY2hlY2sgdGhhdCB5b3UgYWxzbyBmaW5kIHRob3NlIHZhbHVlcyA/CgpZb3Ugd2lsbCBwcm9iYWJseSBuZWVkIHRoZSBSIGZ1bmN0aW9ucyBgcm93TWVhbnNgIGFuZCBgcm93U2RzYC4gSWYgeW91IGRvbid0IGtub3cgdGhlbSwgaGF2ZSBhIGxvb2sgYXQgdGhlaXIgaGVscCBwYWdlLgoKPGRldGFpbHM+Cgo8c3VtbWFyeT5BbnN3ZXJzPC9zdW1tYXJ5PgoKYGBge3J9CiMgQ2FsY3VsYXRlIHRoZSBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24gZm9yIGVhY2ggZ2VuZSB3aXRob3V0IHVzaW5nIGFwcGx5KCkKZ2VuZV9tZWFucyA8LSByb3dNZWFucyhzY2FsZWRfZGF0YSkKZ2VuZV9zZHMgPC0gcm93U2RzKHNjYWxlZF9kYXRhKQoKIyBDaGVjayB0aGUgcmVzdWx0cwpzdW1tYXJ5KGdlbmVfbWVhbnMpCnN1bW1hcnkoZ2VuZV9zZHMpCmBgYAoKPC9kZXRhaWxzPgoKIyMjIEFkdmFuY2VkIDogdXNlIHNjYWxlZCBkYXRhIGZvciBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcwoKTGV0J3Mgc2VlIHdoYXQgaGFwcGVucyB3aGVuIHdlIHVzZSBzY2FsZWQgZGF0YSBmb3IgREUgYW5hbHlzaXMuIFlvdSBjYW4gcnVuIHRoZSBhbmFseXNlcyB3aXRoIHRoZSBmdW5jdGlvbnMgYEZpbmRNYXJrZXJzYCBvciBgRmluZEFsbE1hcmtlcnNgLiBGb3IgdGhpcyBleGVyY2lzZSwgaXQgd2lsbCBiZSBtb3JlIHVzZWZ1bCBpZiB5b3UgZmlyc3QgcnVuIGEgY2x1c3RlcmluZyBhbmFseXNlcyBpbiBvcmRlciB0byBnZXQgaWRlbnRpZmllZCBncm91cHMgb2YgY2VsbHMuCgpFeGFtcGxlIDoKCmBgYHtyLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQpzb2JqX3RpbnkgPC0gTm9ybWFsaXplRGF0YShzb2JqX3RpbnkpCnNvYmpfdGlueSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzb2JqX3RpbnkpCnNvYmpfdGlueSA8LSBTY2FsZURhdGEoc29ial90aW55KQpzb2JqX3RpbnkgPC0gUnVuUENBKHNvYmpfdGlueSwgZGltcyA9IDE6MjApCnNvYmpfdGlueSA8LSBGaW5kTmVpZ2hib3JzKHNvYmpfdGlueSwgZGltcyA9IDE6MjApCnNvYmpfdGlueSA8LSBGaW5kQ2x1c3RlcnMoc29ial90aW55LCByZXNvbHV0aW9uID0gMC41KQpzb2JqX3RpbnkgPC0gUnVuVU1BUChzb2JqX3RpbnksIGRpbXMgPSAxOjIwKQpEaW1QbG90KHNvYmpfdGlueSwgcmVkdWN0aW9uID0gInVtYXAiKQpgYGAKCllvdSBjYW4gYWxzbyBydW4gYSBmaXJzdCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNlcyB0byBiZSBhYmxlIHRvIGNvbXBhcmUgYWZ0ZXIgeW91ciByZXN1bHRzIDoKCmBgYHtyfQojIFBlcmZvcm0gREUgYW5hbHlzZXMKc29ial90aW55Lm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc29ial90aW55LCBvbmx5LnBvcyA9IFRSVUUpCgojIEV4dHJhY3QgdG9wIDEwIGdlbmVzIHBlciBjbHVzdGVyCnRvcDEwIDwtIHNvYmpfdGlueS5tYXJrZXJzICU+JQogICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2cyRkMgPiAxKSAlPiUKICAgIHNsaWNlX2hlYWQobiA9IDEwKSAlPiUKICAgIHVuZ3JvdXAoKQoKIyBEaXNwbGF5IGhlYXRtYXAgb2YgREUgcmVzdWx0cwpEb0hlYXRtYXAoc29ial90aW55LCBmZWF0dXJlcyA9IHRvcDEwJGdlbmUpICsgTm9MZWdlbmQoKSArIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDcpKQoKIyBEaXNwbGF5IHRvcCBERSByZXN1bHRzCmhlYWQodG9wMTAsIG4gPSAzMCkKYGBgCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcnM8L3N1bW1hcnk+CgpgYGB7cn0KIyBBdHRlbXB0IERFIGFuYWx5c2lzIHVzaW5nIHNjYWxlZCBkYXRhCnNvYmpfdGlueV9zY2FsZWQubWFya2VycyA8LSBGaW5kQWxsTWFya2Vycyhzb2JqX3RpbnksIG9ubHkucG9zID0gVFJVRSwgYXNzYXkgPSAiUk5BIiwgc2xvdCA9ICJzY2FsZS5kYXRhIikKIyBOb3Qgd29ya2luZyAhISBTY2FsZWQgZGF0YSBpcyBkZXNpZ25lZCBmb3IgY2x1c3RlcmluZywgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uLCBhbmQgdmlzdWFsaXphdGlvbnMuCmBgYAoKPC9kZXRhaWxzPgoKQ2FuIHlvdSB0cnkgYWxzbyB3aXRoIHJhdyBjb3VudHMgKG5vdCBub3JtYWxpemVkKSA/IFdoYXQgZG8geW91IG9ic2VydmUgPwoKPGRldGFpbHM+Cgo8c3VtbWFyeT5BbnN3ZXJzPC9zdW1tYXJ5PgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9Cm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc29ial90aW55LCBvbmx5LnBvcyA9IFRSVUUsIHNsb3QgPSAiY291bnRzIikKCnRvcDEwIDwtIG1hcmtlcnMgJT4lCiAgICBncm91cF9ieShjbHVzdGVyKSAlPiUKICAgIGRwbHlyOjpmaWx0ZXIoYXZnX2xvZzJGQyA+IDEpICU+JQogICAgc2xpY2VfaGVhZChuID0gMTApICU+JQogICAgdW5ncm91cCgpCgojIERpc3BsYXkgaGVhdG1hcCBvZiBERSByZXN1bHRzCkRvSGVhdG1hcChzb2JqX3RpbnksIGZlYXR1cmVzID0gdG9wMTAkZ2VuZSkgKyBOb0xlZ2VuZCgpICsgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gNykpCgojIERpc3BsYXkgdG9wIERFIHJlc3VsdHMKaGVhZChtYXJrZXJzKQpgYGAKCjwvZGV0YWlscz4KCioqRGlzY3Vzc2lvbiA6KioKKiBXaHkgbWlnaHQgdGhlIERFIHJlc3VsdHMgYmUgbm9uLXNpZ25pZmljYW50IG9yIHVuZXhwZWN0ZWQgPwoqIFdoYXQgaW5mb3JtYXRpb24gaXMgbG9zdCBkdXJpbmcgc2NhbGluZyB0aGF0IGlzIGNyaXRpY2FsIGZvciBERSBhbmFseXNpcyA/CgojIyMgQWR2YW5jZWQ6IGNoYW5nZSB0aGUgdmFsdWVzIG9mIHRoZSBgZmVhdHVyZXNgIG9wdGlvbgoKVGhpcyB2YWx1ZSBpcyBzZXQgYnkgZGVmYXVsdCB0byAyMDAwLiBJdCByZXByZXNlbnRzIHRoZSBudW1iZXIgb2YgaGlnaGx5IHZhcmlhYmxlIGdlbmVzIHRvIHRha2UgaW50byBhY2NvdW50LiBZb3UgY2FuIGV4cGxvcmUgdGhlIGltcGFjdCBpdCBoYXMgb24gdGhlIFBDQS4gSGludCA6IHVzZSB0aGUgYFJ1blBDQWAgZnVuY3Rpb24gZnJvbSBTZXVyYXQuCgpUbyBhbnN3ZXIgdGhpcyBxdWVzdGlvbiwgeW91IHdpbGwgbmVlZCBhIGZ1bmN0aW9uIHRvIHJlaW5pdGlhbGl6ZSB5b3VyIFNldXJhdCBvYmplY3QgYXMgZm9sbG93cyA6CgpgYGB7cn0KIyBGdW5jdGlvbiB0byByZWluaXRpYWxpemUgdGhlIFNldXJhdCBvYmplY3QKcmVpbml0aWFsaXplX3NvYmogPC0gZnVuY3Rpb24oc2V1cmF0X29iaikgewogIAogICMgQ29weSB0aGUgb2JqZWN0CiAgc2V1cmF0X2NvcHkgPC0gc2V1cmF0X29iagogICMgUmVtb3ZlIHZhcmlhYmxlIGZlYXR1cmVzCiAgc2V1cmF0X2NvcHlAYXNzYXlzJFJOQUBtZXRhLmRhdGEgPC0gZGF0YS5mcmFtZSgpCiAgIyBSZW1vdmUgbm9ybWFsaXplZCBhbmQgc2NhbGVzIGRhdGEKICBzZXVyYXRfY29weUBhc3NheXMkUk5BQGxheWVycyRkYXRhIDwtIE5VTEwKICBzZXVyYXRfY29weUBhc3NheXMkUk5BQGxheWVycyRzY2FsZS5kYXRhIDwtIE5VTEwKICAjIFJlbW92ZSBQQ0EgYW5kIFVNQVAKICBzZXVyYXRfY29weUByZWR1Y3Rpb25zIDwtIGxpc3QoKQogICMgUmVtb3ZlIFNDVAogIHNldXJhdF9jb3B5QGFzc2F5cyRTQ1QgPC0gTlVMTAogICMgU2V0IGFjdGl2ZSBhc3NheSB0byBSTkEKICBzZXVyYXRfY29weUBhY3RpdmUuYXNzYXkgPC0gIlJOQSIKICAKICByZXR1cm4oc2V1cmF0X2NvcHkpCn0KYGBgCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcnM8L3N1bW1hcnk+CgpgYGB7cn0KIyBGdW5jdGlvbiB0byBwZXJmb3JtIGFuYWx5c2lzIHdpdGggYSBzcGVjaWZpZWQgbnVtYmVyIG9mIHZhcmlhYmxlIGZlYXR1cmVzCnBlcmZvcm1fYW5hbHlzaXMgPC0gZnVuY3Rpb24oc2V1cmF0X29iaiwgbnVtX2ZlYXR1cmVzKSB7CiAgIyBDcmVhdGUgYSBuZXcgU2V1cmF0IG9iamVjdCBmcm9tIHJhdyBjb3VudHMKICBzZXVyYXRfY29weSA8LSByZWluaXRpYWxpemVfc29iaihzZXVyYXRfb2JqKQogIAogICMgTm9ybWFsaXplIHRoZSBkYXRhCiAgc2V1cmF0X2NvcHkgPC0gTm9ybWFsaXplRGF0YShzZXVyYXRfY29weSkKICAKICAjIFNldCB0aGUgdmFyaWFibGUgZmVhdHVyZXMgKHVwIHRvIHRoZSBzcGVjaWZpZWQgbnVtYmVyKQogIHNldXJhdF9jb3B5IDwtIEZpbmRWYXJpYWJsZUZlYXR1cmVzKHNldXJhdF9jb3B5LAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmZlYXR1cmVzID0gbnVtX2ZlYXR1cmVzKQogIAogICMgSWRlbnRpZnkgdGhlIDEwIG1vc3QgaGlnaGx5IHZhcmlhYmxlIGdlbmVzCiAgdG9wX2ZlYXR1cmVzIDwtIGhlYWQoVmFyaWFibGVGZWF0dXJlcyhzZXVyYXRfY29weSksIDIwKQogIHByaW50KHRvcF9mZWF0dXJlcykKICAKICAjIFNjYWxlIHRoZSBkYXRhLCBhbmQgcnVuIFBDQQogIHNldXJhdF9jb3B5IDwtIFNjYWxlRGF0YShzZXVyYXRfY29weSkKICBzZXVyYXRfY29weSA8LSBSdW5QQ0Eoc2V1cmF0X2NvcHksCiAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gVmFyaWFibGVGZWF0dXJlcyhzZXVyYXRfY29weSksCiAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKICAKICAjIEdlbmVyYXRlIFBDQSBwbG90CiAgcGNhX3Bsb3QgPC0gRGltUGxvdChzZXVyYXRfY29weSwgcmVkdWN0aW9uID0gInBjYSIpICsgZ2d0aXRsZShwYXN0ZShudW1fZmVhdHVyZXMsICJWYXJpYWJsZSBGZWF0dXJlcyIpKQogIAogICMgR2VuZXJhdGUgRGltSGVhdG1hcCBmb3IgdGhlIGZpcnN0IHR3byBwcmluY2lwYWwgY29tcG9uZW50cwogIERpbUhlYXRtYXAoc2V1cmF0X2NvcHksCiAgICAgICAgICAgICBkaW1zID0gMToyLAogICAgICAgICAgICAgYmFsYW5jZWQgPSBUUlVFLAogICAgICAgICAgICAgcmVkdWN0aW9uID0gInBjYSIpCiAgCiAgIyBDbHVzdGVyIHRoZSBjZWxscyBhbmQgcnVuIFVNQVAKICBzZXVyYXRfY29weSA8LSBGaW5kTmVpZ2hib3JzKHNldXJhdF9jb3B5LCBkaW1zID0gMToyMCkKICBzZXVyYXRfY29weSA8LSBGaW5kQ2x1c3RlcnMoc2V1cmF0X2NvcHksIHJlc29sdXRpb24gPSAwLjUpCiAgc2V1cmF0X2NvcHkgPC0gUnVuVU1BUChzZXVyYXRfY29weSwgZGltcyA9IDE6MjApCiAgdW1hcF9wbG90IDwtIERpbVBsb3Qoc2V1cmF0X2NvcHksIHJlZHVjdGlvbiA9ICJ1bWFwIikKICAKICAjIEZpbmQgbWFya2VycwogIHNldXJhdF9jb3B5Lm1hcmtlcnMgPC0gRmluZEFsbE1hcmtlcnMoc2V1cmF0X2NvcHksIG9ubHkucG9zID0gVFJVRSkKICB0b3AxMCA8LSBzZXVyYXRfY29weS5tYXJrZXJzICU+JQogICAgZ3JvdXBfYnkoY2x1c3RlcikgJT4lCiAgICBkcGx5cjo6ZmlsdGVyKGF2Z19sb2cyRkMgPiAxKSAlPiUKICAgIHNsaWNlX2hlYWQobiA9IDEwKSAlPiUKICAgIHVuZ3JvdXAoKQogIGhlYXRtYXBfbWFya2Vyc19wbG90IDwtIERvSGVhdG1hcChzZXVyYXRfY29weSwgZmVhdHVyZXMgPSB0b3AxMCRnZW5lKSArIE5vTGVnZW5kKCkKICAKICAjIEdlbmVyYXRlIFZhcmlhYmxlIEZlYXR1cmUgUGxvdCB3aXRoIGxhYmVsZWQgdG9wIDIwIGZlYXR1cmVzCiAgdmZfcGxvdCA8LSBWYXJpYWJsZUZlYXR1cmVQbG90KHNldXJhdF9jb3B5KQogIHZmX3Bsb3QgPC0gTGFiZWxQb2ludHMocGxvdCA9IHZmX3Bsb3QsIHBvaW50cyA9IHRvcF9mZWF0dXJlcywgcmVwZWwgPSBUUlVFKSArCiAgICBnZ3RpdGxlKHBhc3RlKCJUb3AgVmFyaWFibGUgRmVhdHVyZXM6IiwgbnVtX2ZlYXR1cmVzKSkKICAKICAjIFByaW50IHlvdXIgbmV3IFNldXJhdCBvYmplY3QKICBwcmludChzZXVyYXRfY29weSkKICAKICAjIFJldHVybiBhIGxpc3Qgb2YgcGxvdHMKICByZXR1cm4obGlzdCgKICAgIFNldXJhdE9iamVjdCA9IHNldXJhdF9jb3B5LAogICAgUENBID0gcGNhX3Bsb3QsCiAgICBWYXJpYWJsZUZlYXR1cmVzID0gdmZfcGxvdCwKICAgIFVNQVBfcGxvdCA9IHVtYXBfcGxvdCwKICAgIEhlYXRtYXBfbWFya2VycyA9IGhlYXRtYXBfbWFya2Vyc19wbG90KSkKfQoKIyBBbmFseXplIHdpdGggZGlmZmVyZW50IG51bWJlcnMgb2YgdmFyaWFibGUgZmVhdHVyZXMKcmVzdWx0c18xMDAgPC0gcGVyZm9ybV9hbmFseXNpcyhzb2JqX3RpbnksIDEwMCkKcmVzdWx0c18yMDAwIDwtIHBlcmZvcm1fYW5hbHlzaXMoc29ial90aW55LCAyMDAwKQpyZXN1bHRzXzUwMDAgPC0gcGVyZm9ybV9hbmFseXNpcyhzb2JqX3RpbnksIDUwMDApCgojIERpc3BsYXkgUENBIHBsb3RzIGZvciBjb21wYXJpc29uCnByaW50KHJlc3VsdHNfMTAwJFBDQSkKcHJpbnQocmVzdWx0c18yMDAwJFBDQSkKcHJpbnQocmVzdWx0c181MDAwJFBDQSkKCiMgRGlzcGxheSBWYXJpYWJsZSBGZWF0dXJlIFBsb3RzCnByaW50KHJlc3VsdHNfMTAwJFZhcmlhYmxlRmVhdHVyZXMpCnByaW50KHJlc3VsdHNfMjAwMCRWYXJpYWJsZUZlYXR1cmVzKQpwcmludChyZXN1bHRzXzUwMDAkVmFyaWFibGVGZWF0dXJlcykKCiMgRGlzcGxheSBVTUFQcwpwcmludChyZXN1bHRzXzEwMCRVTUFQX3Bsb3QpCnByaW50KHJlc3VsdHNfMjAwMCRVTUFQX3Bsb3QpCnByaW50KHJlc3VsdHNfNTAwMCRVTUFQX3Bsb3QpCgojIERpc3BsYXkgVU1BUHMKcHJpbnQocmVzdWx0c18xMDAkSGVhdG1hcF9tYXJrZXJzKQpwcmludChyZXN1bHRzXzIwMDAkSGVhdG1hcF9tYXJrZXJzKQpwcmludChyZXN1bHRzXzUwMDAkSGVhdG1hcF9tYXJrZXJzKQpgYGAKCjwvZGV0YWlscz4KCiMjIyBBZHZhbmNlZDogY29tcGFyZSB0aGUgZGlzdHJpYnV0aW9ucyBvZiBtZWFucyBhbmQgU0QgdmFsdWVzIGluIFNDVHJhbnNmb3JtIGFuZCBzY2FsZWQgZGF0YQoKPGRldGFpbHM+Cgo8c3VtbWFyeT5BbnN3ZXJzPC9zdW1tYXJ5PgoKYGBge3IsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgU2V0IGFsbCBnZW5lcyBhcyB2YXJpYWJsZSBmZWF0dXJlcyBmb3IgY29uc2lzdGVuY3kKI1ZhcmlhYmxlRmVhdHVyZXMoc29ial90aW55KSA8LSByb3duYW1lcyhzb2JqX3RpbnkpCgojIEFwcGx5IFNDVHJhbnNmb3JtIHVzaW5nIGFsbCBnZW5lcwpzb2JqX3Rpbnlfc2N0IDwtIFNDVHJhbnNmb3JtKHNvYmpfdGlueSwKICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgIHZhcmlhYmxlLmZlYXR1cmVzLm4gPSAyMDAwKQoKIyBOb3JtYWxpemUgYW5kIHNjYWxlIHVzaW5nIExvZ05vcm1hbGl6ZSArIFNjYWxlRGF0YSBmb3IgYWxsIGdlbmVzCnNvYmpfdGlueUBhY3RpdmUuYXNzYXkgPC0gIlJOQSIKc29ial90aW55X2xvZyA8LSBOb3JtYWxpemVEYXRhKHNvYmpfdGlueSwgbm9ybWFsaXphdGlvbi5tZXRob2QgPSAiTG9nTm9ybWFsaXplIiwgc2NhbGUuZmFjdG9yID0gMTAwMDApCnNvYmpfdGlueV9sb2cgPC0gRmluZFZhcmlhYmxlRmVhdHVyZXMoc29ial90aW55X2xvZykKc29ial90aW55X2xvZyA8LSBTY2FsZURhdGEoc29ial90aW55X2xvZywgZmVhdHVyZXMgPSBWYXJpYWJsZUZlYXR1cmVzKHNvYmpfdGlueV9sb2cpKQoKIyBFeHRyYWN0IHRoZSBzY2FsZWQgZGF0YSBtYXRyaWNlcyBmb3IgY29tcGFyaXNvbgpzY3RfZGF0YSA8LSBhcy5tYXRyaXgoc29ial90aW55X3NjdEBhc3NheXMkU0NUQHNjYWxlLmRhdGEpCnNjYWxlZF9kYXRhIDwtIGFzLm1hdHJpeChzb2JqX3RpbnlfbG9nQGFzc2F5cyRSTkFAbGF5ZXJzJHNjYWxlLmRhdGEpCgojIENoZWNrIGlmIHRoZSBkaW1lbnNpb25zIGFyZSBub3cgY29uc2lzdGVudApkaW0oc2N0X2RhdGEpCmRpbShzY2FsZWRfZGF0YSkKCiMgQ2FsY3VsYXRlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgU0NUcmFuc2Zvcm0Kc2N0X21lYW5zIDwtIHJvd01lYW5zKHNjdF9kYXRhKQpzY3Rfc2RzIDwtIHJvd1NkcyhzY3RfZGF0YSkKCiMgQ2FsY3VsYXRlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgU2NhbGVEYXRhCnNjYWxlZF9tZWFucyA8LSByb3dNZWFucyhzY2FsZWRfZGF0YSkKc2NhbGVkX3NkcyA8LSByb3dTZHMoc2NhbGVkX2RhdGEpCgojIENyZWF0ZSBhIGRhdGEgZnJhbWUgZm9yIGNvbXBhcmlzb24KY29tcGFyaXNvbl9kZiA8LSBkYXRhLmZyYW1lKAogIEdlbmUgPSByb3duYW1lcyhzY3RfZGF0YSksCiAgU0NUX01lYW4gPSBzY3RfbWVhbnMsCiAgU0NUX1NEID0gc2N0X3NkcywKICBTY2FsZWRfTWVhbiA9IHNjYWxlZF9tZWFucywKICBTY2FsZWRfU0QgPSBzY2FsZWRfc2RzCikKCnN1bW1hcnkoY29tcGFyaXNvbl9kZiRTQ1RfU0QpCnN1bW1hcnkoY29tcGFyaXNvbl9kZiRTY2FsZWRfU0QpCgojIFBsb3QgaGlzdG9ncmFtcyBvZiB0aGUgc3RhbmRhcmQgZGV2aWF0aW9ucyBmb3IgYm90aCBtZXRob2RzCmhpc3RfcGxvdCA8LSBnZ3Bsb3QoY29tcGFyaXNvbl9kZiwgYWVzKHggPSBTQ1RfU0QpKSArCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDMwLCBmaWxsID0gImJsdWUiLCBhbHBoYSA9IDAuNSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh4ID0gU2NhbGVkX1NEKSwgYmlucyA9IDMwLCBmaWxsID0gInJlZCIsIGFscGhhID0gMC41KSArCiAgZ2d0aXRsZSgiQ29tcGFyaXNvbiBvZiBTdGFuZGFyZCBEZXZpYXRpb25zOiBTQ1RyYW5zZm9ybSB2cy4gU2NhbGVEYXRhIikgKwogIHhsYWIoIlN0YW5kYXJkIERldmlhdGlvbiIpICsKICB5bGFiKCJGcmVxdWVuY3kiKSArCiAgdGhlbWVfbWluaW1hbCgpCgpzdW1tYXJ5KGNvbXBhcmlzb25fZGYkU2NhbGVkX01lYW4pCnN1bW1hcnkoY29tcGFyaXNvbl9kZiRTQ1RfTWVhbikKCiMgU2NhdHRlciBwbG90IGNvbXBhcmluZyBtZWFuIGV4cHJlc3Npb24gdmFsdWVzCnNjYXR0ZXJfcGxvdCA8LSBnZ3Bsb3QoY29tcGFyaXNvbl9kZiwgYWVzKHggPSBTY2FsZWRfTWVhbiwgeSA9IFNDVF9NZWFuKSkgKwogIGdlb21fcG9pbnQoYWxwaGEgPSAwLjUpICsKICBnZW9tX2FibGluZShzbG9wZSA9IDEsIGludGVyY2VwdCA9IDAsIGxpbmV0eXBlID0gImRhc2hlZCIsIGNvbG9yID0gInJlZCIpICsKICBnZ3RpdGxlKCJTY2F0dGVyIFBsb3Qgb2YgTWVhbiBFeHByZXNzaW9uOiBTY2FsZURhdGEgdnMuIFNDVHJhbnNmb3JtIikgKwogIHhsYWIoIk1lYW4gRXhwcmVzc2lvbiAoU2NhbGVEYXRhKSIpICsKICB5bGFiKCJNZWFuIEV4cHJlc3Npb24gKFNDVHJhbnNmb3JtKSIpICsKICB0aGVtZV9taW5pbWFsKCkKCiMgRGlzcGxheSB0aGUgcGxvdHMKaGlzdF9wbG90CnNjYXR0ZXJfcGxvdApgYGAKCjwvZGV0YWlscz4KCiMgRXhlcmNpY2VzIDQgOiBSZWdyZXNzaW9uIG9mIHVud2FudGVkIHNpZ25hbAoKIyMgV2hhdCBkbyB5b3UgbWVhbiBieSByZWdyZXNzaW9uID8KCkluIHNpbmdsZS1jZWxsIFJOQS1zZXEsIHJlZ3Jlc3Npbmcgb3V0IHZhcmlhYmxlcyAoZS5nLiwgbWl0b2Nob25kcmlhbCBjb250ZW50LCBjZWxsIGN5Y2xlIGVmZmVjdHMpIHJlbW92ZXMgdGhlIGluZmx1ZW5jZSBvZiB0aG9zZSB2YXJpYWJsZXMgb24gZ2VuZSBleHByZXNzaW9uLiBUaGUgZ29hbCBpcyB0byBtaXRpZ2F0ZSB0ZWNobmljYWwgb3IgdW53YW50ZWQgYmlvbG9naWNhbCB2YXJpYWJpbGl0eSwgbGVhdmluZyBhIGRhdGFzZXQgdGhhdCBiZXR0ZXIgcmVmbGVjdHMgdGhlIGJpb2xvZ2ljYWwgcGhlbm9tZW5hIG9mIGludGVyZXN0LgoKIyMgSG93IGNhbiBJIHJlbW92ZSB1bndhbnRlZCBzb3VyY2VzIG9mIHZhcmlhdGlvbiA/CgpJbiBTZXVyYXQsIHlvdSBjYW4gdXNlIHRoZSBgU2NhbGVEYXRhYCBmdW5jdGlvbiB0byByZW1vdmUgdW53YW50ZWQgc291cmNlcyBvZiB2YXJpYXRpb24uIEZvciBleGFtcGxlLCB3ZSBjb3VsZCAncmVncmVzcyBvdXQnIGhldGVyb2dlbmVpdHkgYXNzb2NpYXRlZCB3aXRoIGNlbGwgY3ljbGUgc3RhZ2UgKCJDQ19TZXVyYXRfUGhhc2UiKSwgb3IgbWl0b2Nob25kcmlhbCBjb250YW1pbmF0aW9uICgicGVyY2VudC5tdCIpLiBUaGlzIGFkanVzdHMgZWFjaCBnZW5lJ3MgZXhwcmVzc2lvbiBieSBmaXR0aW5nIGEgbGluZWFyIG1vZGVsIGZvciB0aGUgc3BlY2lmaWVkIHZhcmlhYmxlcyBhbmQgcmVwbGFjaW5nIHRoZSB2YWx1ZXMgd2l0aCB0aGUgcmVzaWR1YWxzLiBUaGUgY29tbWFuZCB0byByZWdyZXNzIG91dCBtaXRvY2hvbmRyaWFsIGNvbnRhbWluYXRpb24gd291bGQgYmUgOgoKYGBge3IsIGV2YWw9RkFMU0V9CnNvYmpfdGlueSA8LSBTY2FsZURhdGEoc29ial90aW55LCB2YXJzLnRvLnJlZ3Jlc3MgPSAicGVyY2VudC5tdCIpCmBgYAoKYFNDVHJhbnNmb3JtYCBhbHNvIGFsbG93cyB0byBkbyBhIHJlZ3Jlc3Npb24uIElmIHlvdSBydW4gYFNDVHJhbnNmb3JtYCwgeW91IGRvbid0IGhhdmUgdG8gdGhlbiBydW4gYFNjYWxlRGF0YWAgYXMgaXQncyBhbGwgaW5jbHVkZWQgaW4gdGhlIGBTQ1RyYW5zZm9ybWAgZnVuY3Rpb24uIFdlIGNhbiBzcGVjaWZ5IHRoZSB2YXJpYWJsZSB0byByZWdyZXNzIG91dCBsaWtlIHRoaXMgOgoKYGBge3IsIGV2YWw9RkFMU0UsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CnNvYmpfdGlueSA8LSBTQ1RyYW5zZm9ybShzb2JqX3RpbnksIHZhcnMudG8ucmVncmVzcyA9ICJwZXJjZW50Lm10IikKYGBgCgojIyMgQ29tcGFyZSB0aGUgaW1wYWN0IG9mIHJlZ3Jlc3Npb24gb2YgY2VsbCBjeWNsZSBvbiBhIGZldyBnZW5lcwoKWW91IGNhbiB0YWtlIHRoZXNlIDMgZ2VuZXMgdGhhdCB3ZSB1c2VkIGJlZm9yZSAoIlNybSIsICJBcG9lIiwgIlRvcDJhIiksIG9yIGFueSBvdGhlciBvZiB5b3VyIGNob2ljZS4gSW4gdGhlIGNvcnJlY3Rpb24sIHdlIHVzZSAiQ2RrMSIuIFVzZSB0aGUgZnVuY3Rpb24gYEZlYXR1cmVTY2F0dGVyYCB0byBlYXNpbHkgdmlzdWFsaXplIHRoZSBlZmZlY3QuCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PkFuc3dlcnM8L3N1bW1hcnk+CgpgYGB7cn0KZmVhdHVyZSA8LSAiQ2RrMSIKc29ial90aW55QGFjdGl2ZS5hc3NheSA8LSAiUk5BIgpzb2JqX3RpbnkgPC0gTm9ybWFsaXplRGF0YShzb2JqX3RpbnkpCnNvYmpfdGlueSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhzb2JqX3RpbnkpCgojIFNjYXR0ZXIgcGxvdCBiZWZvcmUgcmVncmVzc2lvbgpzb2JqX3RpbnkgPC0gU2NhbGVEYXRhKHNvYmpfdGlueSkKc29ial90aW55JGZlYXR1cmVfdmFsdWUgPC0gYXMudmVjdG9yKExheWVyRGF0YShzb2JqX3RpbnksIGFzc2F5ID0gIlJOQSIsIGxheWVyID0gInNjYWxlLmRhdGEiKVtmZWF0dXJlLF0pICMgY29weSB0aGUgdmFsdWVzIG9mIHRoZSBmZWF0dXJlIGluIHRoZSBtZXRhZGF0YSAKRmVhdHVyZVNjYXR0ZXIoc29ial90aW55LCBmZWF0dXJlMSA9ICJDQ19TZXVyYXRfRzJNLlNjb3JlIiwgZmVhdHVyZTIgPSAiZmVhdHVyZV92YWx1ZSIsIHNsb3QgPSAic2NhbGUuZGF0YSIpCkZlYXR1cmVTY2F0dGVyKHNvYmpfdGlueSwgZmVhdHVyZTEgPSAiQ0NfU2V1cmF0X1MuU2NvcmUiLCBmZWF0dXJlMiA9ICJmZWF0dXJlX3ZhbHVlIiwgc2xvdCA9ICJzY2FsZS5kYXRhIikgCgojIFJlZ3Jlc3Mgb3V0IGNlbGwgY3ljbGUgcGhhc2UKc29ial90aW55X2NjX3JlZyA8LSBTY2FsZURhdGEoc29ial90aW55LCB2YXJzLnRvLnJlZ3Jlc3MgPSAiQ0NfU2V1cmF0X1BoYXNlIikKc29ial90aW55X2NjX3JlZyRmZWF0dXJlX3ZhbHVlIDwtIGFzLnZlY3RvcihMYXllckRhdGEoc29ial90aW55X2NjX3JlZywgYXNzYXkgPSAiUk5BIiwgbGF5ZXIgPSAic2NhbGUuZGF0YSIpW2ZlYXR1cmUsXSkgIyBjb3B5IHRoZSB2YWx1ZXMgb2YgdGhlIGZlYXR1cmUgaW4gdGhlIG1ldGFkYXRhIApGZWF0dXJlU2NhdHRlcihzb2JqX3RpbnlfY2NfcmVnLCBmZWF0dXJlMSA9ICJDQ19TZXVyYXRfRzJNLlNjb3JlIiwgZmVhdHVyZTIgPSAiZmVhdHVyZV92YWx1ZSIsIHNsb3QgPSAic2NhbGUuZGF0YSIpIApGZWF0dXJlU2NhdHRlcihzb2JqX3RpbnlfY2NfcmVnLCBmZWF0dXJlMSA9ICJDQ19TZXVyYXRfUy5TY29yZSIsIGZlYXR1cmUyID0gImZlYXR1cmVfdmFsdWUiLCBzbG90ID0gInNjYWxlLmRhdGEiKQpgYGAKCjwvZGV0YWlscz4KCiMjIyBSdW4gdGhlIFBDQSBpbiBib3RoIGNhc2VzIGFuZCBjaGVjayBpZiBpdCBoYXMgYW4gaW1wYWN0IG9uIHRoZSAyIGZpcnN0IGNvbXBvbmVudHMKCjxkZXRhaWxzPgoKPHN1bW1hcnk+QW5zd2Vyczwvc3VtbWFyeT4KCmBgYHtyfQojIEJlZm9yZSByZWdyZXNzaW9uCnNvYmpfdGlueSA8LSBSdW5QQ0Eoc29ial90aW55LCB2ZXJib3NlID0gRkFMU0UpCkRpbUhlYXRtYXAoc29ial90aW55LCBkaW1zID0gMToyLCBiYWxhbmNlZCA9IFRSVUUsIHJlZHVjdGlvbiA9ICJwY2EiKQoKIyBBZnRlciByZWdyZXNzaW9uCnNvYmpfdGlueSA8LSBSdW5QQ0Eoc29ial90aW55X2NjX3JlZywgdmVyYm9zZSA9IEZBTFNFKQpEaW1IZWF0bWFwKHNvYmpfdGlueV9jY19yZWcsIGRpbXMgPSAxOjIsIGJhbGFuY2VkID0gVFJVRSwgcmVkdWN0aW9uID0gInBjYSIpIApgYGAKCjwvZGV0YWlscz4KCiMjIyBDYW4geW91IHNwb3Qgb3RoZXIgZ2VuZXMgPwoKVHJ5IHRvIGZpbmQgZ2VuZXMgdGhhdCBhcmUgaGlnaGx5IGFmZmVjdGVkIGJ5IHRoZSByZWdyZXNzaW9uLCBvciBvdGhlciB0aGF0IGV4cHJlc3Npb24gZG8gbm90IG1vdmUgYWZ0ZXIgcmVncmVzc2lvbi4KCiMjIyBEbyB0aGUgc2FtZSB3aXRoIFNDVHJhbnNmb3JtIChjb21wYXJlIHdpdGggYW5kIHdpdGhvdXQgcmVncmVzc2lvbikKCiMgU3VwZXIgYWR2YW5jZWQgZXhlcmNpc2UKClRha2Ugc2V2ZXJhbCBwaXBlbGluZXMgdGhhdCB3ZSBoYXZlIHNlZW4sIGFuZCBldmFsdWF0ZSBob3cgaXQgaW1wYWN0cyB0aGUgcmVzdWx0cyBvZiB0aGUgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLgoKRm9yIGV4YW1wbGUsIHlvdSBjb3VsZCBjb21wYXJlIDoKCiogTm9ybWFsaXphdGlvbiB3aXRoIHNjYWxpbmcgd2l0aCAkeCQgdmFyaWFibGVzIGdlbmVzIAoqIE5vcm1hbGl6YXRpb24gd2l0aCBzY2FsaW5nIHdpdGggJHgkIHZhcmlhYmxlcyBnZW5lcyB3aXRoIHJlZ3Jlc3Npb24gb2YgdGhlIGNlbGwgY3ljbGUKKiBTQ1RyYW5zZm9ybSB3aXRoICR4JCB2YXJpYWJsZXMgZ2VuZXMKKiBTQ1RyYW5zZm9ybSB3aXRoICR4JCB2YXJpYWJsZXMgZ2VuZXMgd2l0aCByZWdyZXNzaW9uIG9mIHRoZSBjZWxsIGN5Y2xlIEFuZCB5b3UgY2FuIHJlcGVhdCB3aXRoIGFueSB2YWx1ZSBvZiAkeCQuCgojIFN1bW1hcnkgYW5kIGtleSB0YWtlYXdheXMKCkNvbmdyYXRzICEgWW91J3ZlIG1hZGUgaXQgdGhyb3VnaCB5b3VyIGZpcnN0IGV4cGVyaWVuY2Ugd2l0aCBub3JtYWxpemF0aW9uLCBzY2FsaW5nIGFuZCByZWdyZXNzaW9uLiBIZXJlIGFyZSBhIGZldyB0YWtlYXdheSBtZXNzYWdlcyA6CgoqIE5vcm1hbGl6YXRpb24gYWRqdXN0cyBleHByZXNzaW9uIHZhbHVlcyBhY3Jvc3MgY2VsbHMsIGFjY291bnRpbmcgZm9yIHNlcXVlbmNpbmcgZGVwdGguCiogU2NhbGluZyBhZGp1c3RzIGV4cHJlc3Npb24gdmFsdWVzIGFjcm9zcyBnZW5lcywgc3RhbmRhcmRpemluZyB0aGVtIGZvciBlcXVhbCB2YXJpYW5jZS4KKiBVc2UgKipyYXcgY291bnRzIGZvciBERSBhbmFseXNpcyoqLCAqKm5vcm1hbGl6ZWQgZGF0YSBmb3IgdmlzdWFsaXphdGlvbioqLCBhbmQgKipzY2FsZWQgZGF0YSBmb3IgUENBKiouCiogRGlmZmVyZW50IG5vcm1hbGl6YXRpb24gbWV0aG9kcyBjYW4gbGVhZCB0byBkaWZmZXJlbnQgYmlvbG9naWNhbCBpbnRlcnByZXRhdGlvbnM7IGNob29zZSBiYXNlZCBvbiB0aGUgY2hhcmFjdGVyaXN0aWNzIG9mIHlvdXIgZGF0YXNldC4KKiBBbHdheXMgdmlzdWFsaXplIHlvdXIgZGF0YSBhdCBlYWNoIHN0ZXAgdG8gY2hlY2sgaWYgdGhlIHRyYW5zZm9ybWF0aW9ucyBhcmUgYXBwcm9wcmlhdGUuCgo8ZGV0YWlscz4KCjxzdW1tYXJ5PlNlc3Npb24gSW5mbzwvc3VtbWFyeT4KCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoKPC9kZXRhaWxzPgo=