EB3I n1 2025 scRNAseq
-
CELL ANNOTATION





1 PREAMBLE

1.1 Purpose of this session

We are reaching out a challenging task of the analysis (and a very exciting one !).

  • What types of cells did we capture in the analysis ?
  • Do we identify the expected cell types and can we distinguish different sub-populations ?
  • Do we identify “novel” or “unexpected” cell types ?

The aim of this session is to understand the different methods that will help you to explore the biological cell types captured by your dataset.



2 Start Rstudio



3 Warm-up

  • We set common parameters we will use throughout this session :
# setparam


## Set your project name
# WARNING : Do not just copy-paste this ! It's MY project name ! Put YOURS !!
project_name <- "ebaii_sc_teachers"

## Control if the project_name exists on the cluster
cat('PATH CHECK : ', dir.exists(paste0('/shared/projects/', project_name)))
Show output
PATH CHECK :  TRUE
## The current EB3I session ID
sessionid <- '2538_eb3i_n1_2025
'


4 Prepare the data structure

We will do the same as for former steps, just changing the session name.

4.1 Main directory

# maindir

## Preparing the path
TD_dir <- paste0("/shared/projects/", project_name, "/SC_TD")

## Creating the root directory (already exists at this step)
# dir.create(path = TD_dir, recursive = TRUE)

## Print the root directory on-screen
print(TD_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD"

4.2 Current session

# sessiondir

## Creating the session (Preproc.3) directory
session_dir <- paste0(TD_dir, "/08_Cell.Annotation")
dir.create(path = session_dir, recursive = TRUE)

## Print the session directory on-screen
print(session_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/08_Cell.Annotation"

4.3 Input directory

# indir

## Creating the INPUT data directory
input_dir <- paste0(session_dir, "/DATA")
dir.create(path = input_dir, recursive = TRUE)

## Print the input directory on-screen
print(input_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/08_Cell.Annotation/DATA"

4.4 Genelists directory

This is a directory where we will store additional information from knowledge bases about genes used to estimate the cell cycle phase of cells.

# resdir

res_dir <- paste0(TD_dir, "/Resources")
glist_dir <- paste0(res_dir, "/Genelists")

## Create the directory
dir.create(path = glist_dir, recursive = TRUE)

## Print the resources directory on-screen
print(glist_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/Resources/Genelists"

4.5 Output directory

# outdir

## Creating the OUTPUT data directory
output_dir <- paste0(session_dir, "/RESULTS")
dir.create(path = output_dir, recursive = TRUE)

## Print the output directory on-screen
print(output_dir)
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/08_Cell.Annotation/RESULTS"


5 Reload the Seurat Object

  • We can reload the object we saved at the former step
# dataload


## This is the path to the current EB3I backup
sessionid <- '2538_eb3i_n1_2025'


## The latest Seurat object saved as RDS (name)
sobj_file <- "12_TD3A.TDCT_S5_Integrated_12926.3886.RDS"

## The latest Seurat object saved as RDS (full path)
sobj_path <- paste0(TD_dir, 
                    "/06_Integration/RESULTS/",
                    sobj_file)

force <- FALSE  ## To force a re-download of a Zenodo-hosted backup
local <- FALSE  ## To force a loading from a local backup

## In case of error/lost data : force a reload from a Zenodo backup repository
if(force) {
  zen_id <- "14035293"
  zen_backup_file <- paste0("https://zenodo.org/records/",
                            zen_id,
                            "/files/",
                            sobj_file)
  ## Recreate the expected path if it does not exist
  dir.create(path = dirname(sobj_path), recursive = TRUE)
  ## Download the file
  download.file(url = zen_backup_file,
                destfile = sobj_path)
}

## In case of error/lost data : force a reload from a local backup repository
if(local) {
  sobj_path <- paste0(
    "/shared/projects/", sessionid, "/atelier_scrnaseq/TD/BACKUP/RDS/",
    sobj_file)
}

## Load the object
sobj <- readRDS(file = sobj_path)


6 Overview of the scRNAseq pipeline

Workflow before annotation

At this step of the analysis, we have :

  • a gene expression matrix : for each cell, gene expression is available

  • a reduced space : gene expression matrix is summarized in N dimensions

  • a clustering : each cell belongs to a specific cluster

  • a 2D space : cells can be visualized on a 2D representation (UMAP)

On the cell visualization, we also searched for clusters of cells. The clustering resolution shown multiple cell clusters that we can now associate to cell types.

For this you need :

  • your biological knowledge on your dataset

  • an internet connection :)



7 Different methods to annotate cell types

The annotation methods aim at defining marker genes that help to identify the cell types in each cluster.

But the logic across methods is similar :

You identify genes or set of genes that have a pattern of expression specific and that represents a large number of cells for the cluster.

Different methods exist :

  1. MANUAL : You can either do it manually, use set of marker genes from bibliography, or use other datasets that have been annotated to transfert the annotation to your clustering if similar expression patterns are found.

  2. AUTOMATIC : You use a published database and collect sets of marker genes for cell types, or published reference single cell atlas already annotated, or you can also use published RNAseq on a specific cell type that you know is in your dataset…

For this practical session, we will try both approaches. We use the dataset previously filtered and pre-processed (the Seurat object contains the 3 dimension reductions previously performed - pca, umap and harmony).

7.1 Manual annotation

We will perform manual annotation using differential expression.

7.1.1 Overview of the analysis

For manual annotation of cell clusters using marker genes, you need :

  • A Seurat object with normalized counts.
  • To choose a clustering resolution that you want to use for the annotation.
  • A reduced space to visualize the results.

7.1.2 Overview of the functions to be used

# funcs

## Seurat functions to be used in the TD for manual annotation
Seurat::FindClusters()
SeuratObject::JoinLayers()
Seurat::FindAllMarkers()

## Seurat functions to be used for visualization purpose 
### Function to color cell by a "dimension" of their metadata
### (ex : sample of origin ie the annotation called "orig.ident")
Seurat::DimPlot()
### Function to color cell by the expression of one feature
Seurat::FeaturePlot()
### Function to built a heatmap of expression of the marker genes identified
Seurat::DoHeatmap()
### Function to visualize how feature expression changes across different identity classes
Seurat::DotPlot()

7.1.3 Current Seurat object

# descobj

## A quick overview of the object
sobj
Show output
An object of class Seurat 
12926 features across 3886 samples within 1 assay 
Active assay: RNA (12926 features, 2000 variable features)
 5 layers present: counts.TD3A, counts.TDCT, data.TD3A, data.TDCT, scale.data
 6 dimensional reductions calculated: pca, CCAIntegration, umap, RPCAIntegration, HarmonyIntegration, HarmonyStandalone

You see that in the reductions calculated, there are novel reductions you didn’t use before. Those reduction come from the Integration steps used to merge 2 samples into a 2D representation with less batch effect.

# dp1

## Look at the content of the Seurat object post integration 
Seurat::DimPlot(sobj, 
                ## use the reduction umap of the integrated object
                reduction = "umap",
                ## color the cells by their sample of origin
                group.by = c("orig.ident"))
Show plot

Clusters to annotate

Manual annotation is based on the identification of marker genes that characterize a cell population. For this, we must define which groups of cells we want to annotate.

This choice is based on the clustering (Look back at Proc.2). After the integration step, we can re-perform the 2 steps to get a clustering as we now have an object with the integration of both TD3A and TDCT samples.

# reclusters

## Find neighboors to prepare the data for clustering using the first 20 PCs
sobj <- Seurat::FindNeighbors(
  object = sobj, 
  dims = 1:20,
  reduction = "HarmonyIntegration")

## Louvain resolutions to test
resol <- c(0.1,0.2,0.3,0.8,1.5)

## Clustering
sobj <- Seurat::FindClusters(
  object = sobj,
  ## Vector of different resolutions defined above
  resolution = resol,
  ## Makes the function quiet
  verbose = FALSE,
  ## Algorithm for modularity optimization :
  ## . 1 : original Louvain algorithm
  ## . 2 : Louvain algorithm with multilevel refinement
  ## . 3 : SLM algorithm
  ## . 4 : Leiden algorithm. Requires the "leidenalg" python library
  algorithm = 1)

Multiplot with the different clustering results :

# diffresol

## Plot the clustering results
Seurat::DimPlot(object = sobj, 
                reduction = "umap",
                group.by = paste0('RNA_snn_res.', resol),
                pt.size = 1,
                label = TRUE,
                repel = TRUE
) + ggplot2::theme(legend.position = "bottom")
Show plot

Now we have all the data needed to find marker genes per clusters, but we must first choose the clustering resolution we want to annotate.

Advice : Always start by a low resolution to have a first idea of the broad cell types you are capturing.

Here I decided to choose the resolution 0.2 for the purpose of simpler classes, I could be wise to start by the resolution 0.1 to have an even more general idea of marker genes per cluster.

# setidents

Seurat::Idents(sobj) = sobj$RNA_snn_res.0.2

Differential expression between clusters

One way to annotate the clusters of cell is to look at the genes highly expressed in one cluster of cells compared to all the other cells: we can do a differential expression (DE) analysis.

DEA (differential expression analysis) is performed on the normalized count matrix (“data”), which is the case in our integrated Seurat object.

To perform the manual annotation, we will use the Seurat::FindAllMarkers function.

This function compare each cluster against all others to identify genes differentially expressed that are potentially marker genes.

WARNING : In Seurat v5, the integrated object contains the 2 normalized datasets still separated in their gene/cell assays of origin. To identify marker genes, we must join the two assays together with the command JoinLayers.

# joinlayers

sobj = SeuratObject::JoinLayers(sobj)

Now, we can use the Function FindAllMarkers to perform differential gene expression of each cluster against all the others. The objective is to find genes ““specific”” of each cluster to try to annotate them.

# FindAllMarkers

## find markers for every cluster compared to all remaining cells,
## report only genes with positive DE 
all_markers = Seurat::FindAllMarkers(
  object = sobj,
  ## Only keep genes more expressed in the cluster of interest than the others
  only.pos = TRUE,
  ## Minimum % of cells expressing the marker in the cluster of interest
  min.pct = 0.25,
  ## Minimum absolute logFC between conditions
  logfc.threshold = 0.25) 

# This command can take up to 2 mins !

WARNING : the execution time for this command can take several minutes !

Once the markers per cluster have been identified, we can look at the number of markers identified by cluster.

# table_markers

# Number of markers identified by cluster
table(all_markers$cluster)
Show output

   0    1    2    3    4    5    6 
 656  211  554 1725  150  118 1093 

In this table, the first line shows the cluster name, and the last line gives the amount of marker genes (ie DEG) identified for this cluster.

Here we see that many markers (ie deferentially expressed genes) have been identified. We cannot look at all of them, but we can choose to look at the top 3 markers per cluster and use our biological knowledge to identify cell populations.

# top10

`%>%` <- magrittr::`%>%`
top10_markers = as.data.frame(all_markers %>% 
                               dplyr::group_by(cluster) %>% 
                               dplyr::top_n(n = 10, wt = avg_log2FC))
# top10h

## Visualize the top 10 marker gene expression per cluster using the default heatmap function of Seurat.
Seurat::DoHeatmap(sobj, features = top10_markers$gene) + Seurat::NoLegend()
Show plot

# top10dt

## A dotplot
Seurat::DotPlot(sobj, features = unique(top10_markers$gene)) +
  ## Just aesthetics
  ggplot2::theme(axis.text.x = ggplot2::element_text(angle = 90, 
                                                     vjust = 1,
                                                     size = 8, 
                                                     hjust = 1)) 
Show plot

On this plot we can see that some markers display a very high and specific expression for a single cluster, while others are expressed in 2 or more cluster.

Biology in this plot

  • What is the function of each marker gene ? is it a known marker gene for a cell type ?

  • Is there litterature about its pattern of expression ?

Here we need biological knowledge and back-and-forth marker genes computing using different cluster resolutions to understand which cell populations are present in the data.

At this step, using known marker genes from your experiment/knowledge is also useful, and easy to perform

Let’s focus on 2 genes :

  • Rag1, a key gene of T-cells maturation in the variable/diversity/joining (V[D]J) rearrangement

  • Cd5, a marker of self-reactivity of T-cells

# UMAP2markers

Seurat::FeaturePlot(
  object = sobj, 
  features = c("Cd5", "Rag1"), 
  reduction = "umap")
Show plot

Here we can see that those markers may actually be very informative to distinguish cell Tcells maturation process.

… at this moment using your biological knowledge on your dataset is critical, you can also test manually any marker of your choice !

7.1.4 Conclusion

  • Perform differential expression for each cluster VS all the others with a normalized matrix

  • Look at the gene expression of the markers identified in the 2D representation to validate specificity and representation

  • Find the cell population corresponding to these markers and annotate this cluster

Advantages Limits
  • Easy to implement

  • Sometimes the only solution (ex : novel tissue)

  • Everything is possible

  • Clustering : resolution, merged clusters, “bio-informatic” cluster

  • Change clustering ? Change annotation…

  • Knowledge : time-consuming

7.2 Automatic annotation using reference markers

For some tissues, the different cell types have already been largely described and databases exist with referenced marker genes.

Another way to annotate your dataset will be to find a database with relevant annotation for your dataset and use tools of automatic annotation to annotate your clusters.

Let’s see how it works in practice !

We will use a database focused on immunological cell types called ImmGen, thanks to the celldex R package that “provides a collection of reference expression datasets with curated cell type labels, for use in procedures like automated annotation of single-cell data or deconvolution of bulk RNA-seq”

NOTE : In the following chunk of code, you will load the annotation file from the IFB server where we downloaded it. In real life and with a version of dbdypr inf or equal to 2.3, you can also use this command :

annotation = ImmGenData(ensembl = FALSE) 
# ensembl set to TRUE if we want ENSEMBL ID gene names, FALSE will get the annotation with Gene Symbols
# refloading

## Loading the ImmGen database
annotation = readRDS(paste0(
  '/shared/projects/',
  sessionid,
  '/atelier_scrnaseq/TD/RESOURCES/ImmGenData.RDS'))

## A quick description of the db
annotation
Show output
class: SummarizedExperiment 
dim: 22134 830 
metadata(0):
assays(1): logcounts
rownames(22134): Zglp1 Vmn2r65 ... Tiparp Kdm1a
rowData names(0):
colnames(830):
  GSM1136119_EA07068_260297_MOGENE-1_0-ST-V1_MF.11C-11B+.LU_1.CEL
  GSM1136120_EA07068_260298_MOGENE-1_0-ST-V1_MF.11C-11B+.LU_2.CEL ...
  GSM920654_EA07068_201214_MOGENE-1_0-ST-V1_TGD.VG4+24ALO.E17.TH_1.CEL
  GSM920655_EA07068_201215_MOGENE-1_0-ST-V1_TGD.VG4+24ALO.E17.TH_2.CEL
colData names(3): label.main label.fine label.ont

This database contains 3 levels of granularity :

  • A “main” level (coarse grain)

  • A “fine” level (self-explanatory)

  • The “ONT” level (data are mapped to a defined ontology)

As we are in a context of sorted cells of the same lineage, we’re going to use the fine label.

Let’s see how many cell types are described in this ImmGen database :

# Immgen_ct

length(unique(annotation$label.fine))
Show output
[1] 253

The tool we will use to perform the automatic cell type annotation, SingleR works better with the normalized data. Thus, we will extract the normalized matrix from our Seurat object :

# norm_mat

## Extraction of the normalized data 
norm_exp_mat = Seurat::GetAssayData(
  object = sobj,
  assay = "RNA",
  slot = "data"
)

## Matrix dimensions
dim(norm_exp_mat)
Show output
[1] 12926  3886

We are ready to start the annotation.

The following SingleR command performs the prediction of cell types for each cell of the dataset.

# annpred

## Run in ~ 3-5 min depending on the number of CPU and memory defined
ann_predictions = SingleR::SingleR(
  ## our normalized matrix  
  test = norm_exp_mat, 
  ## The ImmGen DB
  ref = annotation, 
  ## The annotation grain level
  labels = annotation$label.fine, 
  ## Marker dectection scheme 
  de.method="classic",
  assay.type.test = "logcounts",
  assay.type.ref = "logcounts",
  BPPARAM = BiocParallel::SerialParam())

The resulting object is a special kind of data.frame . Each row contains the ID of a cell and the prediction score associated by SingleR. Cell labels associated to each cell are stored in the column $labels

How many different kinds of labels were identified ?

# nlabels

length(unique(ann_predictions$labels))
Show output
[1] 35

Besides scoring, SingleR assesses the score quality, and prunes bad results.

How many cells got a poor quality annotation ?

# prunedcells

summary(is.na(ann_predictions$pruned.labels))
Show output
   Mode   FALSE    TRUE 
logical    3847      39 

Annotation diagnostic

SingleR allows to visualize some control plots :

  • We can visualize the score of each cell, split by cell type label, as a heatmap :
# heatmap_predicition

SingleR::plotScoreHeatmap(ann_predictions)
Show plot

How do you interpret this heatmap ?

Add the annotation to the Seurat object

We add a new metadata containing the annotation of each cell to our Seurat object.

# predmeta

sobj$singler_cells_labels = ann_predictions$labels

Visualization of our annotation on UMAP

We can visualize cells annotation the the UMAP.

# UMAPlabeled

## Just for aesthetics (sets a color palette to use)
seeable_palette = setNames(
  c(RColorBrewer::brewer.pal(name = "Dark2", n = 8),
    c(1:(length(unique(ann_predictions$labels)) - 8))),
  nm = names(sort(table(ann_predictions$labels), decreasing = TRUE)))

## UMAP with the predicted annotation by cell
ann_plot = Seurat::DimPlot(
  object = sobj, 
  reduction = "umap", 
  group.by = "singler_cells_labels",  
  pt.size = 2,
  cols = seeable_palette
) + ggplot2::theme(legend.position = "bottom")

# UMAP with the cluster numbers (before annotation)
clust_plot = Seurat::DimPlot(
  object = sobj, 
  reduction = "umap", 
  group.by = "RNA_snn_res.0.2",
  pt.size = 2,
  label = TRUE,
  repel = TRUE
)

print(ann_plot + clust_plot)
Show plot

A lookup at the contingency table of cells projected to the ImmGen reference

# projIG

table(sobj$singler_cells_labels)
Show output

                      DC (DC.8-4-11B-)        Macrophages (MF.11CLOSER.SALM3) 
                                     1                                      1 
                   NKT (NKT.44+NK1.1-)                       T cells (T.4.Pa) 
                                     1                                      1 
                  T cells (T.4FP3+25+)                       T cells (T.4Nve) 
                                     1                                      2 
                  T cells (T.4SP24int)       T cells (T.8EFF.OT1.48HR.LISOVA) 
                                    22                                      2 
       T cells (T.8MEM.OT1.D45.LISOVA)                       T cells (T.8Mem) 
                                     1                                      2 
T cells (T.8MEMKLRG1-CD127+.D8.LISOVA)                   T cells (T.8NVE.OT1) 
                                     1                                      1 
                      T cells (T.8Nve)                     T cells (T.8SP24-) 
                                    25                                      8 
                  T cells (T.8SP24int)                     T cells (T.8SP69+) 
                                     3                                      7 
                    T cells (T.CD4.5H)                  T cells (T.CD4TESTCJ) 
                                     5                                      5 
                    T cells (T.CD8.5H)                    T cells (T.CD8.CTR) 
                                     2                                      2 
                      T cells (T.DN2B)                      T cells (T.DN3-4) 
                                     2                                      1 
                      T cells (T.DN3A)                       T cells (T.DN3B) 
                                    48                                      6 
                       T cells (T.DN4)                         T cells (T.DP) 
                                     1                                      1 
                     T cells (T.DP69+)                       T cells (T.DPbl) 
                                   218                                     71 
                      T cells (T.DPsm)                        T cells (T.ISP) 
                                  3174                                    263 
                     T cells (T.Tregs)                 Tgd (Tgd.imm.VG1+VD6+) 
                                     1                                      2 
                        Tgd (Tgd.VG2+)                    Tgd (Tgd.VG5+24AHI) 
                                     2                                      2 
                             Tgd (Tgd) 
                                     1 

From this rapid prediction, it seems that our dataset contains T-cells mostly, and particularly T.DPsm, T-DP69+ and T-ISP .

Maybe the annotation is not perfectly suited for our dataset. Some cell populations in the annotation are closely related, and this leads to annotation competition for our cells.

It is possible to run the annotation at the cluster level : it will be cleaner than the single cell level annotation. But, be sure that the clustering is not merging several different cell populations.

We can check the number of cells attributed to labels from each cluster :

# cellpercluster

table(sobj$singler_cells_labels,
      sobj$RNA_snn_res.0.2)
Show output
                                        
                                            0    1    2    3    4    5    6
  DC (DC.8-4-11B-)                          0    0    0    0    0    1    0
  Macrophages (MF.11CLOSER.SALM3)           0    0    0    0    0    1    0
  NKT (NKT.44+NK1.1-)                       0    0    0    1    0    0    0
  T cells (T.4.Pa)                          0    0    1    0    0    0    0
  T cells (T.4FP3+25+)                      0    0    1    0    0    0    0
  T cells (T.4Nve)                          0    0    2    0    0    0    0
  T cells (T.4SP24int)                      1    0   21    0    0    0    0
  T cells (T.8EFF.OT1.48HR.LISOVA)          0    0    1    1    0    0    0
  T cells (T.8MEM.OT1.D45.LISOVA)           0    0    1    0    0    0    0
  T cells (T.8Mem)                          0    0    2    0    0    0    0
  T cells (T.8MEMKLRG1-CD127+.D8.LISOVA)    0    0    1    0    0    0    0
  T cells (T.8NVE.OT1)                      0    0    1    0    0    0    0
  T cells (T.8Nve)                          0    0   25    0    0    0    0
  T cells (T.8SP24-)                        0    0    8    0    0    0    0
  T cells (T.8SP24int)                      0    0    2    1    0    0    0
  T cells (T.8SP69+)                        0    0    7    0    0    0    0
  T cells (T.CD4.5H)                        0    0    5    0    0    0    0
  T cells (T.CD4TESTCJ)                     0    0    5    0    0    0    0
  T cells (T.CD8.5H)                        0    0    2    0    0    0    0
  T cells (T.CD8.CTR)                       0    0    2    0    0    0    0
  T cells (T.DN2B)                          0    0    0    1    0    0    1
  T cells (T.DN3-4)                         0    0    0    1    0    0    0
  T cells (T.DN3A)                          0    0    0    2    0    0   46
  T cells (T.DN3B)                          0    1    0    5    0    0    0
  T cells (T.DN4)                           0    0    1    0    0    0    0
  T cells (T.DP)                            0    0    0    0    0    1    0
  T cells (T.DP69+)                         2    3  211    0    2    0    0
  T cells (T.DPbl)                          0   17    0   54    0    0    0
  T cells (T.DPsm)                       2067  827  137    0   73   70    0
  T cells (T.ISP)                           1    4    0  258    0    0    0
  T cells (T.Tregs)                         0    0    1    0    0    0    0
  Tgd (Tgd.imm.VG1+VD6+)                    0    0    1    1    0    0    0
  Tgd (Tgd.VG2+)                            0    0    1    0    0    0    1
  Tgd (Tgd.VG5+24AHI)                       0    0    2    0    0    0    0
  Tgd (Tgd)                                 0    0    0    0    0    0    1

We can eventually check if some clusters contain multiple cell types. We compute the proportion of each cell type in each cluster. If a cluster is composed of two cell types (or more), maybe this resolution for the clustering is too low ?

# propcell

## Compute the proportion of cell types per cluster
pop_by_cluster = prop.table(table(sobj$singler_cells_labels,
                                  sobj$RNA_snn_res.0.2),
                            margin = 2)

## Print number of cell types per cluster with >=30% from this cluster
colSums(pop_by_cluster > 0.3)
Show output
0 1 2 3 4 5 6 
1 1 2 1 1 1 1 

Beware :

  • small weird clusters of cells : they might be of interest BUT they can also be clustering artefacts

  • very large clusters of cells : if you notice that marker genes are representative of only a fraction of this large cluster, you might need to adjust the clustering parameters to be more discriminating.

7.2.1 Conclusion

  1. Find a good marker gene reference (PanglaoDB, CellMarker, CancerSEA…)

  2. Select a tool / model : classifier, scoring function

  3. Annotate your dataset

Advantages Limitations
  • Annotation for every single cell is possible

  • Design your own reference

  • Find the good reference markers

  • Cell types arborescence

  • Limited number of cell types : all cells are annotated, or “unknown” is possible ?

7.3 Automatic annotation using a reference scRNAseq

Another possibility is to use a published single-cell dataset as a reference for the cluster annotation.

This is very useful when you work on a tissue that is close to one tissue already studied, or if you work on another species and you want to have a quick overview of what the predicted annotation would look like. Multiple tools exist to transfer the annotations on your own dataset (SingleR, Azimuth, Symphony, classifiers like SVMs …). Many methods do exist, choose the one you know well first, or people of your lab / bioinfo use to have help if needed. (then you can try others…).

Annotation transfert with reference dataset

We are not going to use this method today but you might want to use it for your practicals.

Here are the main command from Single R.

# singlerpub

# Load the reference dataset in RDS format (it can also be loaded in another format, see the doc of SingleR to convert your reference to a suitable format for the prediction)

REF_SNRNASEQ = readRDS("reference_scRNAseq.RDS")

## This command removes from the object the cells with a metadata "Cell.type" that is a NA.
REF_SNRNASEQ = REF_SNRNASEQ[,!is.na(REF_SNRNASEQ$Cell.type)]

## Normalize the library (SingleR needs normalized data)
REF_SNRNASEQ <- scatter::logNormCounts(REF_SNRNASEQ)

## Create a SingleCellExperiment object for your reference dataset
REF_SCE = Seurat::as.SingleCellExperiment(sx)

## Create a SingleCellExperiment object for your dataset
sx_sce = Seurat::as.SingleCellExperiment(sx)

## RUN SINGLER
pred.grun = SingleR(test = sx_sce,
                    ref = REF_SCE,
                    labels = REF_SNRNASEQ$Cell.type.labels,
                    de.method = "wilcox")

7.3.1 Conclusion

  1. Find a quality reference dataset : several bulk RNA-seq data, one scRNAseq…

  2. Select a tool to transfer annotation (SingleR, …)

  3. Annotate your dataset

Advantages Limitations
  • Single cell level

  • Design your own reference

  • Find the good reference dataset

  • Limited number of cell types (you can only find cell types present in the reference dataset)

  • Never trust 100% the prediction of this automatic annotation, some tools do not have the option to say “we don’t find correspondance” and will force to find a label for all cells.



8 General Conclusion

Method Advantages Limitations
Manual cluster annotation using differential expression
  • Easy to implement

  • May be the only solution

  • Everything is possible

  • Clustering : resolution, merged clusters, “bio-informatic” cluster

  • Change clustering ? Change annotation…

  • Knowledge : time-consuming

Automatic annotation using reference markers
  • Single cell level is possible

  • Design your own reference

  • Find the good reference markers

  • Cell types arborescence

  • Limited number of cell types : all cells are annotated, or “unknown” ?

Automatic annotation using reference dataset
  • Single cell level

  • Design your own reference

  • Find the good reference dataset

  • Limited number of cell types : all cells are annotated, or “unknown” ?

A few advices :)

  • It is recommended to combine multiple methods to annotate your data

    • Use manual cluster annotation to identify quickly your cell populations

    • Identify good markers for each cell populations → your reference markers

    • Use automatic cell annotation using your set of marker → your reference dataset

    • Use your references to annotate new dataset and go back to manual annotation to refine your analysis.

  • Sometimes, annotation reveals that the dataset would benefit from a re-clustering if you realize that some cluster could group 2 cell types or on the contrary, when two different cluster expressed very similar markers and should be merged.

  • During annotation, do not hesitate to look at the expression of Mitochondrial or Ribosomal genes (or any other set of genes) in your clusters. It might help you to identify a cluster of cells that are looks weird to you. Clusters of “artificial” cells - cells of low quality- could lead to the identification of weird (novel?!) cells that have no real biological significance. But be careful a cluster with a high expression of mitochondrial or ribosomal genes can have biological meaning sometimes.

Note about automatic annotation :

If you are working with non-model species or with multiple species : it is not trivial to transfert an annotation from one species to another. Genes markers are not always conserved across the evolution. In this case, manual annotation is a very important sanity check of any automatic annotation !!



9 Optional Part

9.1 Annotation transfer

SingleR can transfer cell annotations from a reference query to your dataset, at the cluster level.

9.1.1 Cluster level annotation

9.1.1.1 Explanation

It is possible to run the prediction of cell types with SingleR per cluster instead of per cell. The idea is similar, but instead of annotated every single cell to its best match in the reference dataset, it annotates every cluster from your query dataset to it’s average best match in the reference dataset. (SingleR will summarize the expression profiles of all cells from the same cluster, and then assess the resulting aggregation) :

Note : we run the same command as before (SingleR), we only add the parameter “cluster” to SingleR function to annotate by cluster and not by cell.

Advantage(s)

  • Much Faster at the cluster level : SingleR can be time-consuming to run on every single cell or you dataset, particularly if the reference and the query dataset are big. Sometimes, you might want to perform a first quick prediction at the cluster level just to have a general idea on how the prediction works with your dataset, and which cell types you capture in your dataset.

  • Check if the reference dataset will be of any help : before running a long prediction of cell annotation with SingleR, (again if your dataset is big), you want to know if the reference you are using is helping at the cluster level. If it fails to identify cell types, even “general” cell types, forget it.

9.1.1.2 Code :

# repred

# Rerun a prediction using clustering information 
# This command is much faster because the prediction is only performed for the 7 clusters and not for each cell.
clust_ann_predictions  =
    SingleR::SingleR(
    test = norm_exp_mat,
    clusters = sobj$RNA_snn_res.0.2,
    ref = annotation,
    labels = annotation$label.fine,
    assay.type.test = "logcounts",
    assay.type.ref = "logcounts",
    BPPARAM = BiocParallel::SerialParam()
  )

Note : we run the same command as before (SingleR), we only add the parameter “cluster” to SingleR function to annotate by cluster and not by cell.

How many clusters have been labelled for each annotation label ?

# cclab

## EXPLANATION OF THE COMMAND BELOW

head(sort(table(clust_ann_predictions$labels), decreasing = TRUE))
Show output

 T cells (T.DPsm)  T cells (T.DN3A) T cells (T.DP69+)   T cells (T.ISP) 
                4                 1                 1                 1 
## This command take the table of annotation labels (clust_ann_predictions$labels)
## It uses the function table to create a contingency table saying "how many time the "labels" from the reference dataset were assigns across clusters"
## Then the function sort order in a "decreasing" order this table to have first the labels assigned the most
## Finally we show only the first 5 lines of the sorted table using the function head

For how many clusters was the annotation of poor quality ?

# pruned_clusters

## EXPLANATION OF THE COMMAND BELOW

summary(is.na(clust_ann_predictions$pruned.labels))
Show output
   Mode   FALSE 
logical       7 
## This command takes the column "pruned.labels" from the table of prediction 
## then the command "is.na" looks for NA value in the column "pruned.labels"
## Finally, the function summary gives the mean / max/ etc metrics of the values in the column pruned labels.

Annotation diagnostic

We can visualize the scores for each cell type, to each cell, as a heatmap :

# heatbc

## Heatmap using the annotation prediction by cluster
SingleR::plotScoreHeatmap(clust_ann_predictions)
Show plot

What do you observe here ? What is the difference with the annotation by cell ?

Add annotation to metadata

We add the annotation to our Seurat object.

# add2

## Save the name of future annotation
clust_labels_col = "singler_clust_labels"

## Create a column with this name in the metadata and fill it with the cluster levels of each cell
sobj@meta.data[[clust_labels_col]] = sobj@meta.data$RNA_snn_res.0.2

## Fill associate each cluster with its annotation 
levels(sobj@meta.data[[clust_labels_col]]) = clust_ann_predictions$labels
# displaypred

clust_ann_predictions$labels
Show output
[1] "T cells (T.DPsm)"  "T cells (T.DPsm)"  "T cells (T.DP69+)"
[4] "T cells (T.ISP)"   "T cells (T.DPsm)"  "T cells (T.DPsm)" 
[7] "T cells (T.DN3A)" 
levels(sobj@meta.data[[clust_labels_col]])
Show output
[1] "T cells (T.DPsm)"  "T cells (T.DP69+)" "T cells (T.ISP)"  
[4] "T cells (T.DN3A)" 

Visualization

We can visualize cells annotation the the 2D projection :

# umapcomp

ann_cluster_plot = Seurat::DimPlot(
  object = sobj, 
  reduction = "umap", 
  group.by = clust_labels_col,
  pt.size = 2,
  label = FALSE, 
  cols = seeable_palette
) + ggplot2::theme(legend.position = "bottom")

ann_cell_plot = Seurat::DimPlot(
  object = sobj, 
  reduction = "umap", 
  group.by = "singler_cells_labels",
  pt.size = 2,
  label = FALSE,
  repel = TRUE, 
  cols = seeable_palette
) + ggplot2::theme(legend.position = "bottom")

ann_cluster_plot + ann_cell_plot
Show plot

Save your Seurat object annotated

We save the annotated Seurat object :

# saverds3

## Save our Seurat object (rich naming)
out_name <- paste0(
          output_dir, "/", paste(
            c("13", Seurat::Project(sobj), "S5", 
              "Integrated_Annotated"
            ), collapse = "_"),
            ".RDS")

## Check
print(out_name)
Show output
[1] "/shared/projects/ebaii_sc_teachers/SC_TD/08_Cell.Annotation/RESULTS/13_TD3A.TDCT_S5_Integrated_Annotated.RDS"
## Write on disk
saveRDS(object = sobj, 
        file = out_name)

10 References

Good practices for single cell analysis : https://www.sc-best-practices.org/preamble.html

Sanger Single cell course : https://www.singlecellcourse.org/index.html

SingleR : https://bioconductor.org/books/3.12/SingleRBook/

11 Ressources

For human :

GeneCard : https://www.genecards.org

Human Protein Atlas : https://www.proteinatlas.org/search/H2-K1






12 Rsession

# rsession

utils::sessionInfo()
Show output
R version 4.4.1 (2024-06-14)
Platform: x86_64-conda-linux-gnu
Running under: Ubuntu 22.04.5 LTS

Matrix products: default
BLAS/LAPACK: /shared/ifbstor1/software/miniconda/envs/r-4.4.1/lib/libopenblasp-r0.3.29.so;  LAPACK version 3.12.0

locale:
 [1] LC_CTYPE=en_US.UTF-8       LC_NUMERIC=C              
 [3] LC_TIME=en_US.UTF-8        LC_COLLATE=en_US.UTF-8    
 [5] LC_MONETARY=en_US.UTF-8    LC_MESSAGES=en_US.UTF-8   
 [7] LC_PAPER=en_US.UTF-8       LC_NAME=C                 
 [9] LC_ADDRESS=C               LC_TELEPHONE=C            
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C       

time zone: Europe/Paris
tzcode source: system (glibc)

attached base packages:
[1] stats4    stats     graphics  grDevices utils     datasets  methods  
[8] base     

other attached packages:
 [1] SummarizedExperiment_1.34.0 Biobase_2.64.0             
 [3] GenomicRanges_1.56.2        GenomeInfoDb_1.40.1        
 [5] IRanges_2.38.1              S4Vectors_0.42.1           
 [7] BiocGenerics_0.50.0         MatrixGenerics_1.16.0      
 [9] matrixStats_1.5.0           future_1.49.0              

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3        rstudioapi_0.17.1        
  [3] jsonlite_2.0.0            magrittr_2.0.3           
  [5] spatstat.utils_3.1-4      farver_2.1.2             
  [7] rmarkdown_2.29            zlibbioc_1.50.0          
  [9] vctrs_0.6.5               ROCR_1.0-11              
 [11] DelayedMatrixStats_1.26.0 spatstat.explore_3.4-3   
 [13] S4Arrays_1.4.1            htmltools_0.5.8.1        
 [15] SparseArray_1.4.8         sass_0.4.10              
 [17] sctransform_0.4.2         parallelly_1.45.0        
 [19] KernSmooth_2.23-24        bslib_0.9.0              
 [21] htmlwidgets_1.6.4         ica_1.0-3                
 [23] plyr_1.8.9                plotly_4.10.4            
 [25] zoo_1.8-14                cachem_1.1.0             
 [27] igraph_2.1.4              mime_0.13                
 [29] lifecycle_1.0.4           pkgconfig_2.0.3          
 [31] rsvd_1.0.5                Matrix_1.7-3             
 [33] R6_2.6.1                  fastmap_1.2.0            
 [35] GenomeInfoDbData_1.2.12   fitdistrplus_1.2-2       
 [37] shiny_1.10.0              digest_0.6.37            
 [39] patchwork_1.3.0           Seurat_5.3.0             
 [41] tensor_1.5                RSpectra_0.16-2          
 [43] irlba_2.3.5.1             beachmat_2.20.0          
 [45] labeling_0.4.3            progressr_0.15.1         
 [47] spatstat.sparse_3.1-0     httr_1.4.7               
 [49] polyclip_1.10-7           abind_1.4-8              
 [51] compiler_4.4.1            withr_3.0.2              
 [53] BiocParallel_1.38.0       viridis_0.6.5            
 [55] fastDummies_1.7.5         MASS_7.3-65              
 [57] DelayedArray_0.30.1       tools_4.4.1              
 [59] lmtest_0.9-40             httpuv_1.6.15            
 [61] future.apply_1.11.3       goftest_1.2-3            
 [63] glue_1.8.0                nlme_3.1-165             
 [65] promises_1.3.2            grid_4.4.1               
 [67] Rtsne_0.17                cluster_2.1.6            
 [69] reshape2_1.4.4            generics_0.1.4           
 [71] gtable_0.3.6              spatstat.data_3.1-6      
 [73] rmdformats_1.0.4          tidyr_1.3.1              
 [75] data.table_1.17.4         ScaledMatrix_1.12.0      
 [77] BiocSingular_1.20.0       XVector_0.44.0           
 [79] sp_2.2-0                  spatstat.geom_3.4-1      
 [81] RcppAnnoy_0.0.22          ggrepel_0.9.6            
 [83] RANN_2.6.2                pillar_1.10.2            
 [85] stringr_1.5.1             spam_2.11-1              
 [87] RcppHNSW_0.6.0            limma_3.60.6             
 [89] later_1.4.2               splines_4.4.1            
 [91] dplyr_1.1.4               lattice_0.22-6           
 [93] survival_3.7-0            deldir_2.0-4             
 [95] tidyselect_1.2.1          miniUI_0.1.2             
 [97] pbapply_1.7-2             knitr_1.50               
 [99] gridExtra_2.3             bookdown_0.39            
[101] scattermore_1.2           xfun_0.52                
[103] statmod_1.5.0             pheatmap_1.0.12          
[105] UCSC.utils_1.0.0          stringi_1.8.7            
[107] lazyeval_0.2.2            yaml_2.3.10              
[109] evaluate_1.0.3            codetools_0.2-20         
[111] tibble_3.2.1              cli_3.6.5                
[113] uwot_0.2.3                xtable_1.8-4             
[115] reticulate_1.42.0         jquerylib_0.1.4          
[117] dichromat_2.0-0.1         Rcpp_1.0.14              
[119] globals_0.18.0            spatstat.random_3.4-1    
[121] png_0.1-8                 spatstat.univar_3.1-3    
[123] parallel_4.4.1            ggplot2_3.5.2            
[125] presto_1.0.0              SingleR_2.6.0            
[127] dotCall64_1.2             sparseMatrixStats_1.16.0 
[129] listenv_0.9.1             viridisLite_0.4.2        
[131] scales_1.4.0              ggridges_0.5.6           
[133] crayon_1.5.3              SeuratObject_5.1.0       
[135] purrr_1.0.4               rlang_1.1.6              
[137] cowplot_1.1.3            
LS0tCnRpdGxlOiAiPENFTlRFUj5FQjNJIG4xIDIwMjUgc2NSTkFzZXE8QlI+LTxCUj48Qj5DRUxMIEFOTk9UQVRJT048L0I+PEJSPjwvQ0VOVEVSPiIKZGF0ZTogIjIwMjUtMTYtMjEuMjIiCmF1dGhvcjoKICAtIG5hbWU6ICJFQjNJIG4xIHNjUk5Bc2VxIFRlYW0iCiAgLSBuYW1lOiAiTWFyZ290IFRSQUdJTiIKICAgIGVtYWlsOiAibWFyZ290LnRyYWdpbkBpbnN0aXR1dGltYWdpbmUub3JnIgogIC0gbmFtZTogIlPDqWJhc3RpZW4gTUVMTEEiCiAgICBlbWFpbDogInNlYmFzdGllbi5tZWxsYUBwYXN0ZXVyLmZyIiAKb3V0cHV0OgogIHJtZGZvcm1hdHM6OnJlYWR0aGVkb3duOgogICAgZmlnX3dpZHRoOiA4CiAgICBmaWdfaGVpZ2h0OiA2CiAgICBoaWdobGlnaHQ6IHRhbmdvICAjIyBUaGVtZSBmb3IgdGhlIGNvZGUgY2h1bmtzCiAgICBlbWJlZF9mb250czogVFJVRQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlICAjIyBBZGRzIG51bWJlciB0byBoZWFkZXJzIChzZWN0aW9ucykKICAgIHRoZW1lOiBmbGF0bHkgICMjIENTUyB0aGVtZSBmb3IgdGhlIEhUTUwgcGFnZQogICAgY29sbGFwc2VkOiB0cnVlICAjIyBCeSBkZWZhdWx0LCB0aGUgVE9DIGlzIGZvbGRlZAogICAgdG9jX2RlcHRoOiAzCiAgICBzbW9vdGhfc2Nyb2xsOiB0cnVlICMjIFNtb290aCBzY3JvbGwgb2YgdGhlIEhUTUwgcGFnZQogICAgc2VsZl9jb250YWluZWQ6IHRydWUgIyMgSW5jbHVkZXMgYWxsIHBsb3RzL2ltYWdlcyB3aXRoaW4gdGhlIEhUTUwKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUgIyMgQWRkcyBhIGJ1dHRvbiB0byBkb3dubG9hZCB0aGUgUm1kCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIHRodW1ibmFpbHM6IGZhbHNlCiAgICBsaWdodGJveDogdHJ1ZQogICAgZmlnX2NhcHRpb246IGZhbHNlCiAgICBnYWxsZXJ5OiB0cnVlCiAgICB1c2VfYm9va2Rvd246IHRydWUKYWx3YXlzX2FsbG93X2h0bWw6IHRydWUgIyMgQWxsb3cgcGxhaW4gSFRNTCBjb2RlIGluIHRoZSBSbWQKZWRpdG9yX29wdGlvbnM6IAogIG1hcmtkb3duOiAKICAgIHdyYXA6IDcyCi0tLQoKPCEtLSBrbml0IHNldHVwIC0tPgoKYGBge3Iga25pdF9zZXR1cCwgZWNobyA9IEZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAjIFByaW50IHRoZSBjb2RlCiAgZXZhbCA9IFRSVUUsICAgICAgICAjIFJ1biBjb21tYW5kIGxpbmVzCiAgbWVzc2FnZSA9IEZBTFNFLCAgICAjIFByaW50IG1lc3NhZ2VzCiAgcHJvbXB0ID0gRkFMU0UsICAgICAjIERvIG5vdCBkaXNwbGF5IHByb21wdAogIGNvbW1lbnQgPSBOQSwgICAgICAgIyBObyBjb21tZW50cyBvbiB0aGlzIHNlY3Rpb24KICB3YXJuaW5nID0gRkFMU0UsICAgICMgRGlzcGxheSB3YXJuaW5ncwogIHRpZHkgPSBGQUxTRSwKICBmaWcuYWxpZ249ImNlbnRlciIsIAogICMgcmVzdWx0cyA9ICdoaWRlJywKICB3aWR0aCA9IDEwMCAgICAgICAjIE51bWJlciBvZiBjaGFyYWN0ZXJzIHBlciBsaW5lCikKYGBgCgo8IS0tIENTUyB0byBjb2xvciBjaHVua3MgYW5kIG91dHB1dHMgLS0+CgpgYGB7Y3NzLCBlY2hvPUZBTFNFfQoubm90cnVuIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBibGFjayAhaW1wb3J0YW50Owp9Ci5ub3RydW5vIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBsaWdodGdyZXkgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLnF1ZXN0aW9uIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBhcXVhbWFyaW5lICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50OwogIGJvcmRlcjogM3B4IHNvbGlkIGxpbWVncmVlbiAhaW1wb3J0YW50Owp9Ci5xdWVzdGlvbm8gewogIGJhY2tncm91bmQtY29sb3I6IGFxdWFtYXJpbmUgIWltcG9ydGFudDsKICBjb2xvciA6IGJsYWNrICFpbXBvcnRhbnQ7Cn0KLmFuc3dlciB7CiAgYmFja2dyb3VuZC1jb2xvcjogbmF2YWpvd2hpdGUgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBicm93biAhaW1wb3J0YW50Owp9Ci5hbnN3ZXJvIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiBuYXZham93aGl0ZSAhaW1wb3J0YW50OwogIGNvbG9yIDogYmxhY2sgIWltcG9ydGFudDsKfQouYmV5b25kIHsKICBiYWNrZ3JvdW5kLWNvbG9yOiB2aW9sZXQgIWltcG9ydGFudDsKICBib3JkZXI6IDNweCBzb2xpZCBwdXJwbGUgIWltcG9ydGFudDsKfQouYmV5b25kbyB7CiAgYmFja2dyb3VuZC1jb2xvcjogdmlvbGV0ICFpbXBvcnRhbnQ7CiAgY29sb3IgOiBibGFjayAhaW1wb3J0YW50Owp9CmBgYAoKPCEtLSBIb29rIHRvIGhhbmRsZSBjb2RlIGJsb2NrcyBvdXRwdXQgZm9sZGluZyAtLT4KCmBgYHtyIGtuaXRfaG9vaywgZWNobyA9IEZBTFNFfQpob29rcyA9IGtuaXRyOjprbml0X2hvb2tzJGdldCgpCmhvb2tfZm9sZGFibGUgPSBmdW5jdGlvbih0eXBlKSB7CiAgZm9yY2UodHlwZSkKICBmdW5jdGlvbih4LCBvcHRpb25zKSB7CiAgICByZXMgPSBob29rc1tbdHlwZV1dKHgsIG9wdGlvbnMpCiAgICAKICAgIGlmIChpc0ZBTFNFKG9wdGlvbnNbW3Bhc3RlMCgiZm9sZC4iLCB0eXBlKV1dKSkgcmV0dXJuKHJlcykKICAgIAogICAgcGFzdGUwKAogICAgICAiPGRldGFpbHM+PHN1bW1hcnk+U2hvdyAiLCB0eXBlLCAiPC9zdW1tYXJ5PlxuXG4iLAogICAgICByZXMsCiAgICAgICJcblxuPC9kZXRhaWxzPiIKICAgICkKICB9Cn0Ka25pdHI6OmtuaXRfaG9va3Mkc2V0KAogIG91dHB1dCA9IGhvb2tfZm9sZGFibGUoIm91dHB1dCIpLAogIHBsb3QgPSBob29rX2ZvbGRhYmxlKCJwbG90IikKKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgo8Y2VudGVyPiFbXShlYjNpX2Jhbm5lci5wbmcpPC9jZW50ZXI+CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQUkVBTUJMRQoKIyMgUHVycG9zZSBvZiB0aGlzIHNlc3Npb24KCldlIGFyZSByZWFjaGluZyBvdXQgYSBjaGFsbGVuZ2luZyB0YXNrIG9mIHRoZSBhbmFseXNpcyAoYW5kIGEgdmVyeSBleGNpdGluZyBvbmUgISkuCgotICAgV2hhdCAqKnR5cGVzIG9mIGNlbGxzKiogZGlkIHdlIGNhcHR1cmUgaW4gdGhlIGFuYWx5c2lzID8gCi0gICBEbyB3ZSBpZGVudGlmeSB0aGUgZXhwZWN0ZWQgY2VsbCB0eXBlcyBhbmQgY2FuIHdlIGRpc3Rpbmd1aXNoIGRpZmZlcmVudCAKICAgIHN1Yi1wb3B1bGF0aW9ucyA/IAotICAgRG8gd2UgaWRlbnRpZnkgIm5vdmVsIiBvciAidW5leHBlY3RlZCIgY2VsbCB0eXBlcyA/CgpUaGUgYWltIG9mIHRoaXMgc2Vzc2lvbiBpcyB0byB1bmRlcnN0YW5kIHRoZSBkaWZmZXJlbnQgbWV0aG9kcyB0aGF0IHdpbGwgaGVscAp5b3UgdG8gZXhwbG9yZSB0aGUgYmlvbG9naWNhbCBjZWxsIHR5cGVzIGNhcHR1cmVkIGJ5IHlvdXIgZGF0YXNldC4KCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBTdGFydCBSc3R1ZGlvCgotICAgVXNpbmcgdGhlIFtPcGVuT25EZW1hbmQvUnN0dWRpbyBjaGVhdAogICAgc2hlZXRdKGh0dHBzOi8vbW9vZGxlLmZyYW5jZS1iaW9pbmZvcm1hdGlxdWUuZnIvcGx1Z2luZmlsZS5waHAvMTQ3NS9tb2RfZm9sZGVyL2NvbnRlbnQvMC9Pb0RfUl9Sc3R1ZGlvLmh0bWwpe3RhcmdldD0iX2JsYW5rIn0sCiAgICBjb25uZWN0IHRvIHRoZSBbT3Blbk9uRGVtYW5kCiAgICBwb3J0YWxdKGh0dHBzOi8vb25kZW1hbmQuY2x1c3Rlci5mcmFuY2UtYmlvaW5mb3JtYXRpcXVlLmZyKXt0YXJnZXQ9Il9ibGFuayJ9IGFuZAogICAgKipjcmVhdGUgYSBSc3R1ZGlvIHNlc3Npb24qKiB3aXRoIHRoZSByaWdodCByZXNvdXJjZSByZXF1aXJlbWVudHMuCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBXYXJtLXVwCgotICAgV2Ugc2V0ICoqY29tbW9uIHBhcmFtZXRlcnMqKiB3ZSB3aWxsIHVzZSB0aHJvdWdob3V0IHRoaXMgc2Vzc2lvbiA6CgpgYGB7ciBzZXRwYXJhbX0KIyBzZXRwYXJhbQoKCiMjIFNldCB5b3VyIHByb2plY3QgbmFtZQojIFdBUk5JTkcgOiBEbyBub3QganVzdCBjb3B5LXBhc3RlIHRoaXMgISBJdCdzIE1ZIHByb2plY3QgbmFtZSAhIFB1dCBZT1VSUyAhIQpwcm9qZWN0X25hbWUgPC0gImViYWlpX3NjX3RlYWNoZXJzIgoKIyMgQ29udHJvbCBpZiB0aGUgcHJvamVjdF9uYW1lIGV4aXN0cyBvbiB0aGUgY2x1c3RlcgpjYXQoJ1BBVEggQ0hFQ0sgOiAnLCBkaXIuZXhpc3RzKHBhc3RlMCgnL3NoYXJlZC9wcm9qZWN0cy8nLCBwcm9qZWN0X25hbWUpKSkKCiMjIFRoZSBjdXJyZW50IEVCM0kgc2Vzc2lvbiBJRApzZXNzaW9uaWQgPC0gJzI1MzhfZWIzaV9uMV8yMDI1CicKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBQcmVwYXJlIHRoZSBkYXRhIHN0cnVjdHVyZQoKV2Ugd2lsbCBkbyB0aGUgc2FtZSBhcyBmb3IgZm9ybWVyIHN0ZXBzLCBqdXN0IGNoYW5naW5nIHRoZSBzZXNzaW9uIG5hbWUuCgojIyBNYWluIGRpcmVjdG9yeQoKYGBge3IgbWFpbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyBtYWluZGlyCgojIyBQcmVwYXJpbmcgdGhlIHBhdGgKVERfZGlyIDwtIHBhc3RlMCgiL3NoYXJlZC9wcm9qZWN0cy8iLCBwcm9qZWN0X25hbWUsICIvU0NfVEQiKQoKIyMgQ3JlYXRpbmcgdGhlIHJvb3QgZGlyZWN0b3J5IChhbHJlYWR5IGV4aXN0cyBhdCB0aGlzIHN0ZXApCiMgZGlyLmNyZWF0ZShwYXRoID0gVERfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHJvb3QgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChURF9kaXIpCgpgYGAKCiMjIEN1cnJlbnQgc2Vzc2lvbgoKYGBge3Igc2Vzc2lvbmRpciwgZm9sZC5vdXRwdXQgPSBGQUxTRX0KIyBzZXNzaW9uZGlyCgojIyBDcmVhdGluZyB0aGUgc2Vzc2lvbiAoUHJlcHJvYy4zKSBkaXJlY3RvcnkKc2Vzc2lvbl9kaXIgPC0gcGFzdGUwKFREX2RpciwgIi8wOF9DZWxsLkFubm90YXRpb24iKQpkaXIuY3JlYXRlKHBhdGggPSBzZXNzaW9uX2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBzZXNzaW9uIGRpcmVjdG9yeSBvbi1zY3JlZW4KcHJpbnQoc2Vzc2lvbl9kaXIpCgpgYGAKCiMjIElucHV0IGRpcmVjdG9yeQoKYGBge3IgaW5kaXIsIGZvbGQub3V0cHV0ID0gRkFMU0V9CiMgaW5kaXIKCiMjIENyZWF0aW5nIHRoZSBJTlBVVCBkYXRhIGRpcmVjdG9yeQppbnB1dF9kaXIgPC0gcGFzdGUwKHNlc3Npb25fZGlyLCAiL0RBVEEiKQpkaXIuY3JlYXRlKHBhdGggPSBpbnB1dF9kaXIsIHJlY3Vyc2l2ZSA9IFRSVUUpCgojIyBQcmludCB0aGUgaW5wdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChpbnB1dF9kaXIpCgpgYGAKCiMjIEdlbmVsaXN0cyBkaXJlY3RvcnkKClRoaXMgaXMgYSBkaXJlY3Rvcnkgd2hlcmUgd2Ugd2lsbCBzdG9yZSBhZGRpdGlvbmFsIGluZm9ybWF0aW9uIGZyb20gCmtub3dsZWRnZSBiYXNlcyBhYm91dCBnZW5lcyB1c2VkIHRvIGVzdGltYXRlIHRoZSBjZWxsIGN5Y2xlIHBoYXNlIG9mIGNlbGxzLgoKYGBge3IgcmVzZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIHJlc2RpcgoKcmVzX2RpciA8LSBwYXN0ZTAoVERfZGlyLCAiL1Jlc291cmNlcyIpCmdsaXN0X2RpciA8LSBwYXN0ZTAocmVzX2RpciwgIi9HZW5lbGlzdHMiKQoKIyMgQ3JlYXRlIHRoZSBkaXJlY3RvcnkKZGlyLmNyZWF0ZShwYXRoID0gZ2xpc3RfZGlyLCByZWN1cnNpdmUgPSBUUlVFKQoKIyMgUHJpbnQgdGhlIHJlc291cmNlcyBkaXJlY3Rvcnkgb24tc2NyZWVuCnByaW50KGdsaXN0X2RpcikKCmBgYAoKIyMgT3V0cHV0IGRpcmVjdG9yeQoKYGBge3Igb3V0ZGlyLCBmb2xkLm91dHB1dCA9IEZBTFNFfQojIG91dGRpcgoKIyMgQ3JlYXRpbmcgdGhlIE9VVFBVVCBkYXRhIGRpcmVjdG9yeQpvdXRwdXRfZGlyIDwtIHBhc3RlMChzZXNzaW9uX2RpciwgIi9SRVNVTFRTIikKZGlyLmNyZWF0ZShwYXRoID0gb3V0cHV0X2RpciwgcmVjdXJzaXZlID0gVFJVRSkKCiMjIFByaW50IHRoZSBvdXRwdXQgZGlyZWN0b3J5IG9uLXNjcmVlbgpwcmludChvdXRwdXRfZGlyKQoKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBSZWxvYWQgdGhlIFNldXJhdCBPYmplY3QKCi0gICBXZSBjYW4gcmVsb2FkIHRoZSBvYmplY3Qgd2Ugc2F2ZWQgYXQgdGhlIGZvcm1lciBzdGVwCgpgYGB7ciBkYXRhbG9hZH0KIyBkYXRhbG9hZAoKCiMjIFRoaXMgaXMgdGhlIHBhdGggdG8gdGhlIGN1cnJlbnQgRUIzSSBiYWNrdXAKc2Vzc2lvbmlkIDwtICcyNTM4X2ViM2lfbjFfMjAyNScKCgojIyBUaGUgbGF0ZXN0IFNldXJhdCBvYmplY3Qgc2F2ZWQgYXMgUkRTIChuYW1lKQpzb2JqX2ZpbGUgPC0gIjEyX1REM0EuVERDVF9TNV9JbnRlZ3JhdGVkXzEyOTI2LjM4ODYuUkRTIgoKIyMgVGhlIGxhdGVzdCBTZXVyYXQgb2JqZWN0IHNhdmVkIGFzIFJEUyAoZnVsbCBwYXRoKQpzb2JqX3BhdGggPC0gcGFzdGUwKFREX2RpciwgCiAgICAgICAgICAgICAgICAgICAgIi8wNl9JbnRlZ3JhdGlvbi9SRVNVTFRTLyIsCiAgICAgICAgICAgICAgICAgICAgc29ial9maWxlKQoKZm9yY2UgPC0gRkFMU0UgICMjIFRvIGZvcmNlIGEgcmUtZG93bmxvYWQgb2YgYSBaZW5vZG8taG9zdGVkIGJhY2t1cApsb2NhbCA8LSBGQUxTRSAgIyMgVG8gZm9yY2UgYSBsb2FkaW5nIGZyb20gYSBsb2NhbCBiYWNrdXAKCiMjIEluIGNhc2Ugb2YgZXJyb3IvbG9zdCBkYXRhIDogZm9yY2UgYSByZWxvYWQgZnJvbSBhIFplbm9kbyBiYWNrdXAgcmVwb3NpdG9yeQppZihmb3JjZSkgewogIHplbl9pZCA8LSAiMTQwMzUyOTMiCiAgemVuX2JhY2t1cF9maWxlIDwtIHBhc3RlMCgiaHR0cHM6Ly96ZW5vZG8ub3JnL3JlY29yZHMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHplbl9pZCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICIvZmlsZXMvIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNvYmpfZmlsZSkKICAjIyBSZWNyZWF0ZSB0aGUgZXhwZWN0ZWQgcGF0aCBpZiBpdCBkb2VzIG5vdCBleGlzdAogIGRpci5jcmVhdGUocGF0aCA9IGRpcm5hbWUoc29ial9wYXRoKSwgcmVjdXJzaXZlID0gVFJVRSkKICAjIyBEb3dubG9hZCB0aGUgZmlsZQogIGRvd25sb2FkLmZpbGUodXJsID0gemVuX2JhY2t1cF9maWxlLAogICAgICAgICAgICAgICAgZGVzdGZpbGUgPSBzb2JqX3BhdGgpCn0KCiMjIEluIGNhc2Ugb2YgZXJyb3IvbG9zdCBkYXRhIDogZm9yY2UgYSByZWxvYWQgZnJvbSBhIGxvY2FsIGJhY2t1cCByZXBvc2l0b3J5CmlmKGxvY2FsKSB7CiAgc29ial9wYXRoIDwtIHBhc3RlMCgKICAgICIvc2hhcmVkL3Byb2plY3RzLyIsIHNlc3Npb25pZCwgIi9hdGVsaWVyX3Njcm5hc2VxL1REL0JBQ0tVUC9SRFMvIiwKICAgIHNvYmpfZmlsZSkKfQoKIyMgTG9hZCB0aGUgb2JqZWN0CnNvYmogPC0gcmVhZFJEUyhmaWxlID0gc29ial9wYXRoKQoKYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyBPdmVydmlldyBvZiB0aGUgc2NSTkFzZXEgcGlwZWxpbmUKCiFbV29ya2Zsb3cgYmVmb3JlIGFubm90YXRpb25dKHdvcmtmbG93X292ZXJ2aWV3LnBuZykKCkF0IHRoaXMgc3RlcCBvZiB0aGUgYW5hbHlzaXMsIHdlIGhhdmUgOgoKLSAgICoqYSBnZW5lIGV4cHJlc3Npb24gbWF0cml4KiogOiBmb3IgZWFjaCBjZWxsLCBnZW5lIGV4cHJlc3Npb24gaXMgYXZhaWxhYmxlCgotICAgKiphIHJlZHVjZWQgc3BhY2UqKiA6IGdlbmUgZXhwcmVzc2lvbiBtYXRyaXggaXMgc3VtbWFyaXplZCBpbiBOIGRpbWVuc2lvbnMKCi0gICAqKmEgY2x1c3RlcmluZyoqIDogZWFjaCBjZWxsIGJlbG9uZ3MgdG8gYSBzcGVjaWZpYyBjbHVzdGVyCgotICAgKiphIDJEIHNwYWNlKiogOiBjZWxscyBjYW4gYmUgdmlzdWFsaXplZCBvbiBhIDJEIHJlcHJlc2VudGF0aW9uIChVTUFQKQoKT24gdGhlIGNlbGwgdmlzdWFsaXphdGlvbiwgd2UgYWxzbyBzZWFyY2hlZCBmb3IgY2x1c3RlcnMgb2YgY2VsbHMuIFRoZSBjbHVzdGVyaW5nIHJlc29sdXRpb24gc2hvd24gbXVsdGlwbGUgY2VsbCBjbHVzdGVycyB0aGF0IHdlIGNhbiBub3cgYXNzb2NpYXRlIHRvIGNlbGwgdHlwZXMuCgpGb3IgdGhpcyB5b3UgbmVlZCA6CgotICAgKip5b3VyIGJpb2xvZ2ljYWwga25vd2xlZGdlKiogb24geW91ciBkYXRhc2V0CgotICAgKiphbiBpbnRlcm5ldCBjb25uZWN0aW9uKiogOikKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgoKIyBEaWZmZXJlbnQgbWV0aG9kcyB0byBhbm5vdGF0ZSBjZWxsIHR5cGVzCgpUaGUgYW5ub3RhdGlvbiBtZXRob2RzIGFpbSBhdCBkZWZpbmluZyAqKm1hcmtlciBnZW5lcyoqIHRoYXQgaGVscCB0byBpZGVudGlmeSB0aGUgY2VsbCB0eXBlcyBpbiBlYWNoIGNsdXN0ZXIuCgpCdXQgdGhlIGxvZ2ljIGFjcm9zcyBtZXRob2RzIGlzIHNpbWlsYXIgOgoKWW91IGlkZW50aWZ5IGdlbmVzIG9yIHNldCBvZiBnZW5lcyB0aGF0IGhhdmUgYSBwYXR0ZXJuIG9mIGV4cHJlc3Npb24gKipzcGVjaWZpYyoqIGFuZCB0aGF0ICoqcmVwcmVzZW50cyBhIGxhcmdlIG51bWJlciBvZiBjZWxscyoqIGZvciB0aGUgY2x1c3Rlci4KCkRpZmZlcmVudCBtZXRob2RzIGV4aXN0IDoKCjEuICAqKk1BTlVBTCA6KiogWW91IGNhbiBlaXRoZXIgZG8gaXQgbWFudWFsbHksIHVzZSBzZXQgb2YgbWFya2VyIGdlbmVzIGZyb20gYmlibGlvZ3JhcGh5LCBvciB1c2Ugb3RoZXIgZGF0YXNldHMgdGhhdCBoYXZlIGJlZW4gYW5ub3RhdGVkIHRvIHRyYW5zZmVydCB0aGUgYW5ub3RhdGlvbiB0byB5b3VyIGNsdXN0ZXJpbmcgaWYgc2ltaWxhciBleHByZXNzaW9uIHBhdHRlcm5zIGFyZSBmb3VuZC4KCjIuICAqKkFVVE9NQVRJQyoqIDogWW91IHVzZSBhIHB1Ymxpc2hlZCBkYXRhYmFzZSBhbmQgY29sbGVjdCBzZXRzIG9mIG1hcmtlciBnZW5lcyBmb3IgY2VsbCB0eXBlcywgb3IgcHVibGlzaGVkIHJlZmVyZW5jZSBzaW5nbGUgY2VsbCBhdGxhcyBhbHJlYWR5IGFubm90YXRlZCwgb3IgeW91IGNhbiBhbHNvIHVzZSBwdWJsaXNoZWQgUk5Bc2VxIG9uIGEgc3BlY2lmaWMgY2VsbCB0eXBlIHRoYXQgeW91IGtub3cgaXMgaW4geW91ciBkYXRhc2V0Li4uCgpGb3IgdGhpcyBwcmFjdGljYWwgc2Vzc2lvbiwgd2Ugd2lsbCB0cnkgYm90aCBhcHByb2FjaGVzLiBXZSB1c2UgdGhlIGRhdGFzZXQgcHJldmlvdXNseSBmaWx0ZXJlZCBhbmQgcHJlLXByb2Nlc3NlZCAodGhlIFNldXJhdCBvYmplY3QgY29udGFpbnMgdGhlIDMgZGltZW5zaW9uIHJlZHVjdGlvbnMgcHJldmlvdXNseSBwZXJmb3JtZWQgLSBwY2EsIHVtYXAgYW5kIGhhcm1vbnkpLgoKIyMgTWFudWFsIGFubm90YXRpb24KCldlIHdpbGwgcGVyZm9ybSBtYW51YWwgYW5ub3RhdGlvbiB1c2luZyBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4KCiMjIyBPdmVydmlldyBvZiB0aGUgYW5hbHlzaXMKCkZvciBtYW51YWwgYW5ub3RhdGlvbiBvZiBjZWxsIGNsdXN0ZXJzIHVzaW5nIG1hcmtlciBnZW5lcywgeW91IG5lZWQgOgoKLSAgIEEgU2V1cmF0IG9iamVjdCB3aXRoIG5vcm1hbGl6ZWQgY291bnRzLgotICAgVG8gY2hvb3NlIGEgY2x1c3RlcmluZyByZXNvbHV0aW9uIHRoYXQgeW91IHdhbnQgdG8gdXNlIGZvciB0aGUgYW5ub3RhdGlvbi4KLSAgIEEgcmVkdWNlZCBzcGFjZSB0byB2aXN1YWxpemUgdGhlIHJlc3VsdHMuCgojIyMgT3ZlcnZpZXcgb2YgdGhlIGZ1bmN0aW9ucyB0byBiZSB1c2VkCgpgYGB7ciBmdW5jcywgZXZhbD1GQUxTRX0KIyBmdW5jcwoKIyMgU2V1cmF0IGZ1bmN0aW9ucyB0byBiZSB1c2VkIGluIHRoZSBURCBmb3IgbWFudWFsIGFubm90YXRpb24KU2V1cmF0OjpGaW5kQ2x1c3RlcnMoKQpTZXVyYXRPYmplY3Q6OkpvaW5MYXllcnMoKQpTZXVyYXQ6OkZpbmRBbGxNYXJrZXJzKCkKCiMjIFNldXJhdCBmdW5jdGlvbnMgdG8gYmUgdXNlZCBmb3IgdmlzdWFsaXphdGlvbiBwdXJwb3NlIAojIyMgRnVuY3Rpb24gdG8gY29sb3IgY2VsbCBieSBhICJkaW1lbnNpb24iIG9mIHRoZWlyIG1ldGFkYXRhCiMjIyAoZXggOiBzYW1wbGUgb2Ygb3JpZ2luIGllIHRoZSBhbm5vdGF0aW9uIGNhbGxlZCAib3JpZy5pZGVudCIpClNldXJhdDo6RGltUGxvdCgpCiMjIyBGdW5jdGlvbiB0byBjb2xvciBjZWxsIGJ5IHRoZSBleHByZXNzaW9uIG9mIG9uZSBmZWF0dXJlClNldXJhdDo6RmVhdHVyZVBsb3QoKQojIyMgRnVuY3Rpb24gdG8gYnVpbHQgYSBoZWF0bWFwIG9mIGV4cHJlc3Npb24gb2YgdGhlIG1hcmtlciBnZW5lcyBpZGVudGlmaWVkClNldXJhdDo6RG9IZWF0bWFwKCkKIyMjIEZ1bmN0aW9uIHRvIHZpc3VhbGl6ZSBob3cgZmVhdHVyZSBleHByZXNzaW9uIGNoYW5nZXMgYWNyb3NzIGRpZmZlcmVudCBpZGVudGl0eSBjbGFzc2VzClNldXJhdDo6RG90UGxvdCgpCgpgYGAKCgojIyMgQ3VycmVudCBTZXVyYXQgb2JqZWN0CgpgYGB7ciBkZXNjb2JqfQojIGRlc2NvYmoKCiMjIEEgcXVpY2sgb3ZlcnZpZXcgb2YgdGhlIG9iamVjdApzb2JqCgpgYGAKCllvdSBzZWUgdGhhdCBpbiB0aGUgcmVkdWN0aW9ucyBjYWxjdWxhdGVkLCB0aGVyZSBhcmUgbm92ZWwgcmVkdWN0aW9ucyB5b3UgZGlkbid0IHVzZSBiZWZvcmUuIFRob3NlIHJlZHVjdGlvbiBjb21lIGZyb20gdGhlIEludGVncmF0aW9uIHN0ZXBzIHVzZWQgdG8gbWVyZ2UgMiBzYW1wbGVzIGludG8gYSAyRCByZXByZXNlbnRhdGlvbiB3aXRoIGxlc3MgYmF0Y2ggZWZmZWN0LgoKYGBge3IgZHAxfQojIGRwMQoKIyMgTG9vayBhdCB0aGUgY29udGVudCBvZiB0aGUgU2V1cmF0IG9iamVjdCBwb3N0IGludGVncmF0aW9uIApTZXVyYXQ6OkRpbVBsb3Qoc29iaiwgCiAgICAgICAgICAgICAgICAjIyB1c2UgdGhlIHJlZHVjdGlvbiB1bWFwIG9mIHRoZSBpbnRlZ3JhdGVkIG9iamVjdAogICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgIyMgY29sb3IgdGhlIGNlbGxzIGJ5IHRoZWlyIHNhbXBsZSBvZiBvcmlnaW4KICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gYygib3JpZy5pZGVudCIpKQoKYGBgCgoqKkNsdXN0ZXJzIHRvIGFubm90YXRlKioKCk1hbnVhbCBhbm5vdGF0aW9uIGlzIGJhc2VkIG9uIHRoZSBpZGVudGlmaWNhdGlvbiBvZiBtYXJrZXIgZ2VuZXMgdGhhdCBjaGFyYWN0ZXJpemUgYSBjZWxsIHBvcHVsYXRpb24uIEZvciB0aGlzLCB3ZSBtdXN0IGRlZmluZSB3aGljaCBncm91cHMgb2YgY2VsbHMgd2Ugd2FudCB0byBhbm5vdGF0ZS4KClRoaXMgY2hvaWNlIGlzIGJhc2VkIG9uIHRoZSBjbHVzdGVyaW5nIChMb29rIGJhY2sgYXQgYFByb2MuMmApLiBBZnRlciB0aGUgaW50ZWdyYXRpb24gc3RlcCwgd2UgY2FuIHJlLXBlcmZvcm0gdGhlIDIgc3RlcHMgdG8gZ2V0IGEgY2x1c3RlcmluZyBhcyB3ZSBub3cgaGF2ZSBhbiBvYmplY3Qgd2l0aCB0aGUgaW50ZWdyYXRpb24gb2YgYm90aCBgVEQzQWAgYW5kIGBURENUYCBzYW1wbGVzLgoKYGBge3IgcmVjbHVzdGVyc30KIyByZWNsdXN0ZXJzCgojIyBGaW5kIG5laWdoYm9vcnMgdG8gcHJlcGFyZSB0aGUgZGF0YSBmb3IgY2x1c3RlcmluZyB1c2luZyB0aGUgZmlyc3QgMjAgUENzCnNvYmogPC0gU2V1cmF0OjpGaW5kTmVpZ2hib3JzKAogIG9iamVjdCA9IHNvYmosIAogIGRpbXMgPSAxOjIwLAogIHJlZHVjdGlvbiA9ICJIYXJtb255SW50ZWdyYXRpb24iKQoKIyMgTG91dmFpbiByZXNvbHV0aW9ucyB0byB0ZXN0CnJlc29sIDwtIGMoMC4xLDAuMiwwLjMsMC44LDEuNSkKCiMjIENsdXN0ZXJpbmcKc29iaiA8LSBTZXVyYXQ6OkZpbmRDbHVzdGVycygKICBvYmplY3QgPSBzb2JqLAogICMjIFZlY3RvciBvZiBkaWZmZXJlbnQgcmVzb2x1dGlvbnMgZGVmaW5lZCBhYm92ZQogIHJlc29sdXRpb24gPSByZXNvbCwKICAjIyBNYWtlcyB0aGUgZnVuY3Rpb24gcXVpZXQKICB2ZXJib3NlID0gRkFMU0UsCiAgIyMgQWxnb3JpdGhtIGZvciBtb2R1bGFyaXR5IG9wdGltaXphdGlvbiA6CiAgIyMgLiAxIDogb3JpZ2luYWwgTG91dmFpbiBhbGdvcml0aG0KICAjIyAuIDIgOiBMb3V2YWluIGFsZ29yaXRobSB3aXRoIG11bHRpbGV2ZWwgcmVmaW5lbWVudAogICMjIC4gMyA6IFNMTSBhbGdvcml0aG0KICAjIyAuIDQgOiBMZWlkZW4gYWxnb3JpdGhtLiBSZXF1aXJlcyB0aGUgImxlaWRlbmFsZyIgcHl0aG9uIGxpYnJhcnkKICBhbGdvcml0aG0gPSAxKQpgYGAKCk11bHRpcGxvdCB3aXRoIHRoZSBkaWZmZXJlbnQgY2x1c3RlcmluZyByZXN1bHRzIDoKCmBgYHtyIGRpZmZyZXNvbCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTZ9CiMgZGlmZnJlc29sCgojIyBQbG90IHRoZSBjbHVzdGVyaW5nIHJlc3VsdHMKU2V1cmF0OjpEaW1QbG90KG9iamVjdCA9IHNvYmosIAogICAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSBwYXN0ZTAoJ1JOQV9zbm5fcmVzLicsIHJlc29sKSwKICAgICAgICAgICAgICAgIHB0LnNpemUgPSAxLAogICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLAogICAgICAgICAgICAgICAgcmVwZWwgPSBUUlVFCikgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKYGBgCgpOb3cgd2UgaGF2ZSBhbGwgdGhlIGRhdGEgbmVlZGVkIHRvIGZpbmQgbWFya2VyIGdlbmVzIHBlciBjbHVzdGVycywgYnV0IHdlIG11c3QgZmlyc3QgY2hvb3NlIHRoZSBjbHVzdGVyaW5nIHJlc29sdXRpb24gd2Ugd2FudCB0byBhbm5vdGF0ZS4KCipBZHZpY2UgOiogQWx3YXlzIHN0YXJ0IGJ5IGEgbG93IHJlc29sdXRpb24gdG8gaGF2ZSBhIGZpcnN0IGlkZWEgb2YgdGhlIGJyb2FkIGNlbGwgdHlwZXMgeW91IGFyZSBjYXB0dXJpbmcuCgpIZXJlIEkgZGVjaWRlZCB0byBjaG9vc2UgdGhlIHJlc29sdXRpb24gMC4yIGZvciB0aGUgcHVycG9zZSBvZiBzaW1wbGVyIGNsYXNzZXMsIEkgY291bGQgYmUgd2lzZSB0byBzdGFydCBieSB0aGUgcmVzb2x1dGlvbiAwLjEgdG8gaGF2ZSBhbiBldmVuIG1vcmUgZ2VuZXJhbCBpZGVhIG9mIG1hcmtlciBnZW5lcyBwZXIgY2x1c3Rlci4KCmBgYHtyIHNldGlkZW50c30KIyBzZXRpZGVudHMKClNldXJhdDo6SWRlbnRzKHNvYmopID0gc29iaiRSTkFfc25uX3Jlcy4wLjIKCmBgYAoKKipEaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBiZXR3ZWVuIGNsdXN0ZXJzKioKCk9uZSB3YXkgdG8gYW5ub3RhdGUgdGhlIGNsdXN0ZXJzIG9mIGNlbGwgaXMgdG8gbG9vayBhdCB0aGUgZ2VuZXMgaGlnaGx5IGV4cHJlc3NlZCBpbiBvbmUgY2x1c3RlciBvZiBjZWxscyBjb21wYXJlZCB0byBhbGwgdGhlIG90aGVyIGNlbGxzOiB3ZSBjYW4gZG8gYSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiAoREUpIGFuYWx5c2lzLgoKREVBIChkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBhbmFseXNpcykgaXMgcGVyZm9ybWVkIG9uIHRoZSAqKm5vcm1hbGl6ZWQgY291bnQgbWF0cml4KiogKCJkYXRhIiksIHdoaWNoIGlzIHRoZSBjYXNlIGluIG91ciBpbnRlZ3JhdGVkIFNldXJhdCBvYmplY3QuCgpUbyBwZXJmb3JtIHRoZSBtYW51YWwgYW5ub3RhdGlvbiwgd2Ugd2lsbCB1c2UgdGhlIFtgU2V1cmF0OjpGaW5kQWxsTWFya2Vyc2BdKGh0dHBzOi8vc2F0aWphbGFiLm9yZy9zZXVyYXQvcmVmZXJlbmNlL2ZpbmRhbGxtYXJrZXJzKSBmdW5jdGlvbi4KClRoaXMgZnVuY3Rpb24gY29tcGFyZSBlYWNoIGNsdXN0ZXIgYWdhaW5zdCBhbGwgb3RoZXJzIHRvIGlkZW50aWZ5IGdlbmVzIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCB0aGF0IGFyZSBwb3RlbnRpYWxseSBtYXJrZXIgZ2VuZXMuCgoqKldBUk5JTkcqKiA6IEluIGBTZXVyYXRgIHY1LCB0aGUgaW50ZWdyYXRlZCBvYmplY3QgY29udGFpbnMgdGhlIDIgbm9ybWFsaXplZCBkYXRhc2V0cyBzdGlsbCBzZXBhcmF0ZWQgaW4gdGhlaXIgZ2VuZS9jZWxsIGFzc2F5cyBvZiBvcmlnaW4uIFRvIGlkZW50aWZ5IG1hcmtlciBnZW5lcywgd2UgbXVzdCBqb2luIHRoZSB0d28gYXNzYXlzIHRvZ2V0aGVyIHdpdGggdGhlIGNvbW1hbmQgYEpvaW5MYXllcnNgLgoKYGBge3Igam9pbmxheWVyc30KIyBqb2lubGF5ZXJzCgpzb2JqID0gU2V1cmF0T2JqZWN0OjpKb2luTGF5ZXJzKHNvYmopCgpgYGAKCk5vdywgd2UgY2FuIHVzZSB0aGUgRnVuY3Rpb24gYEZpbmRBbGxNYXJrZXJzYCB0byBwZXJmb3JtIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gb2YgZWFjaCBjbHVzdGVyIGFnYWluc3QgYWxsIHRoZSBvdGhlcnMuIFRoZSBvYmplY3RpdmUgaXMgdG8gZmluZCBnZW5lcyAiInNwZWNpZmljIiIgb2YgZWFjaCBjbHVzdGVyIHRvIHRyeSB0byBhbm5vdGF0ZSB0aGVtLgoKYGBge3IgRmluZEFsbE1hcmtlcnMsIG1lc3NhZ2U9RkFMU0V9CiMgRmluZEFsbE1hcmtlcnMKCiMjIGZpbmQgbWFya2VycyBmb3IgZXZlcnkgY2x1c3RlciBjb21wYXJlZCB0byBhbGwgcmVtYWluaW5nIGNlbGxzLAojIyByZXBvcnQgb25seSBnZW5lcyB3aXRoIHBvc2l0aXZlIERFIAphbGxfbWFya2VycyA9IFNldXJhdDo6RmluZEFsbE1hcmtlcnMoCiAgb2JqZWN0ID0gc29iaiwKICAjIyBPbmx5IGtlZXAgZ2VuZXMgbW9yZSBleHByZXNzZWQgaW4gdGhlIGNsdXN0ZXIgb2YgaW50ZXJlc3QgdGhhbiB0aGUgb3RoZXJzCiAgb25seS5wb3MgPSBUUlVFLAogICMjIE1pbmltdW0gJSBvZiBjZWxscyBleHByZXNzaW5nIHRoZSBtYXJrZXIgaW4gdGhlIGNsdXN0ZXIgb2YgaW50ZXJlc3QKICBtaW4ucGN0ID0gMC4yNSwKICAjIyBNaW5pbXVtIGFic29sdXRlIGxvZ0ZDIGJldHdlZW4gY29uZGl0aW9ucwogIGxvZ2ZjLnRocmVzaG9sZCA9IDAuMjUpIAoKIyBUaGlzIGNvbW1hbmQgY2FuIHRha2UgdXAgdG8gMiBtaW5zICEKYGBgCgoqKldBUk5JTkcqKiA6IHRoZSBleGVjdXRpb24gdGltZSBmb3IgdGhpcyBjb21tYW5kIGNhbiB0YWtlIHNldmVyYWwgbWludXRlcyAhCgpPbmNlIHRoZSBtYXJrZXJzIHBlciBjbHVzdGVyIGhhdmUgYmVlbiBpZGVudGlmaWVkLCB3ZSBjYW4gbG9vayBhdCB0aGUgbnVtYmVyIG9mIG1hcmtlcnMgaWRlbnRpZmllZCBieSBjbHVzdGVyLgoKYGBge3IgdGFibGVfbWFya2VycywgZWNobz1UUlVFfQojIHRhYmxlX21hcmtlcnMKCiMgTnVtYmVyIG9mIG1hcmtlcnMgaWRlbnRpZmllZCBieSBjbHVzdGVyCnRhYmxlKGFsbF9tYXJrZXJzJGNsdXN0ZXIpCgpgYGAKCkluIHRoaXMgdGFibGUsIHRoZSBmaXJzdCBsaW5lIHNob3dzIHRoZSBjbHVzdGVyIG5hbWUsIGFuZCB0aGUgbGFzdCBsaW5lIGdpdmVzIHRoZSBhbW91bnQgb2YgbWFya2VyIGdlbmVzIChpZSBERUcpIGlkZW50aWZpZWQgZm9yIHRoaXMgY2x1c3Rlci4KCkhlcmUgd2Ugc2VlIHRoYXQgbWFueSBtYXJrZXJzIChpZSBkZWZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcykgaGF2ZSBiZWVuIGlkZW50aWZpZWQuIFdlIGNhbm5vdCBsb29rIGF0IGFsbCBvZiB0aGVtLCBidXQgd2UgY2FuIGNob29zZSB0byBsb29rIGF0IHRoZSB0b3AgMyBtYXJrZXJzIHBlciBjbHVzdGVyIGFuZCB1c2Ugb3VyIGJpb2xvZ2ljYWwga25vd2xlZGdlIHRvIGlkZW50aWZ5IGNlbGwgcG9wdWxhdGlvbnMuCgpgYGB7ciB0b3AxMH0KIyB0b3AxMAoKYCU+JWAgPC0gbWFncml0dHI6OmAlPiVgCnRvcDEwX21hcmtlcnMgPSBhcy5kYXRhLmZyYW1lKGFsbF9tYXJrZXJzICU+JSAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRwbHlyOjpncm91cF9ieShjbHVzdGVyKSAlPiUgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkcGx5cjo6dG9wX24obiA9IDEwLCB3dCA9IGF2Z19sb2cyRkMpKQoKYGBgCgpgYGB7ciB0b3AxMGgsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD0xMH0KIyB0b3AxMGgKCiMjIFZpc3VhbGl6ZSB0aGUgdG9wIDEwIG1hcmtlciBnZW5lIGV4cHJlc3Npb24gcGVyIGNsdXN0ZXIgdXNpbmcgdGhlIGRlZmF1bHQgaGVhdG1hcCBmdW5jdGlvbiBvZiBTZXVyYXQuClNldXJhdDo6RG9IZWF0bWFwKHNvYmosIGZlYXR1cmVzID0gdG9wMTBfbWFya2VycyRnZW5lKSArIFNldXJhdDo6Tm9MZWdlbmQoKQoKYGBgCgoKYGBge3IgdG9wMTBkdCwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTJ9CiMgdG9wMTBkdAoKIyMgQSBkb3RwbG90ClNldXJhdDo6RG90UGxvdChzb2JqLCBmZWF0dXJlcyA9IHVuaXF1ZSh0b3AxMF9tYXJrZXJzJGdlbmUpKSArCiAgIyMgSnVzdCBhZXN0aGV0aWNzCiAgZ2dwbG90Mjo6dGhlbWUoYXhpcy50ZXh0LnggPSBnZ3Bsb3QyOjplbGVtZW50X3RleHQoYW5nbGUgPSA5MCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmp1c3QgPSAxLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpemUgPSA4LCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDEpKSAKCmBgYAoKT24gdGhpcyBwbG90IHdlIGNhbiBzZWUgdGhhdCBzb21lIG1hcmtlcnMgZGlzcGxheSBhIHZlcnkgaGlnaCBhbmQgc3BlY2lmaWMgZXhwcmVzc2lvbiBmb3IgYSBzaW5nbGUgY2x1c3Rlciwgd2hpbGUgb3RoZXJzIGFyZSBleHByZXNzZWQgaW4gMiBvciBtb3JlIGNsdXN0ZXIuCgoKKioqQmlvbG9neSBpbiB0aGlzIHBsb3QqKioKCi0gICBXaGF0IGlzIHRoZSBmdW5jdGlvbiBvZiBlYWNoIG1hcmtlciBnZW5lID8gaXMgaXQgYSBrbm93biBtYXJrZXIgZ2VuZSBmb3IgYSBjZWxsIHR5cGUgPwoKLSAgIElzIHRoZXJlIGxpdHRlcmF0dXJlIGFib3V0IGl0cyBwYXR0ZXJuIG9mIGV4cHJlc3Npb24gPwoKSGVyZSB3ZSBuZWVkIGJpb2xvZ2ljYWwga25vd2xlZGdlIGFuZCBiYWNrLWFuZC1mb3J0aCBtYXJrZXIgZ2VuZXMgY29tcHV0aW5nIHVzaW5nIGRpZmZlcmVudCBjbHVzdGVyIHJlc29sdXRpb25zIHRvIHVuZGVyc3RhbmQgd2hpY2ggY2VsbCBwb3B1bGF0aW9ucyBhcmUgcHJlc2VudCBpbiB0aGUgZGF0YS4KCkF0IHRoaXMgc3RlcCwgdXNpbmcga25vd24gbWFya2VyIGdlbmVzIGZyb20geW91ciBleHBlcmltZW50L2tub3dsZWRnZSBpcyBhbHNvIHVzZWZ1bCwgYW5kIGVhc3kgdG8gcGVyZm9ybQoKTGV0J3MgZm9jdXMgb24gMiBnZW5lcyA6CgotICAgYFJhZzFgLCBhIGtleSBnZW5lIG9mIFQtY2VsbHMgbWF0dXJhdGlvbiBpbiB0aGUgdmFyaWFibGUvZGl2ZXJzaXR5L2pvaW5pbmcgKFZbRF1KKSByZWFycmFuZ2VtZW50CgotICAgYENkNWAsIGEgbWFya2VyIG9mIHNlbGYtcmVhY3Rpdml0eSBvZiBULWNlbGxzCgpgYGB7ciBVTUFQMm1hcmtlcnMsIGZpZy53aWR0aD0xNiwgZmlnLmhlaWdodD04fQojIFVNQVAybWFya2VycwoKU2V1cmF0OjpGZWF0dXJlUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICBmZWF0dXJlcyA9IGMoIkNkNSIsICJSYWcxIiksIAogIHJlZHVjdGlvbiA9ICJ1bWFwIikKCmBgYAoKSGVyZSB3ZSBjYW4gc2VlIHRoYXQgdGhvc2UgbWFya2VycyBtYXkgYWN0dWFsbHkgYmUgdmVyeSBpbmZvcm1hdGl2ZSB0byBkaXN0aW5ndWlzaCBjZWxsIFRjZWxscyBtYXR1cmF0aW9uIHByb2Nlc3MuCgouLi4gYXQgdGhpcyBtb21lbnQgdXNpbmcgeW91ciBiaW9sb2dpY2FsIGtub3dsZWRnZSBvbiB5b3VyIGRhdGFzZXQgaXMgY3JpdGljYWwsIHlvdSBjYW4gYWxzbyB0ZXN0IG1hbnVhbGx5IGFueSBtYXJrZXIgb2YgeW91ciBjaG9pY2UgIQoKCiMjIyBDb25jbHVzaW9uCgotICAgUGVyZm9ybSBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiBmb3IgZWFjaCBjbHVzdGVyIFZTIGFsbCB0aGUgb3RoZXJzIHdpdGggYSBub3JtYWxpemVkIG1hdHJpeAoKLSAgIExvb2sgYXQgdGhlIGdlbmUgZXhwcmVzc2lvbiBvZiB0aGUgbWFya2VycyBpZGVudGlmaWVkIGluIHRoZSAyRCByZXByZXNlbnRhdGlvbiB0byAqKnZhbGlkYXRlIHNwZWNpZmljaXR5IGFuZCByZXByZXNlbnRhdGlvbioqCgotICAgRmluZCB0aGUgY2VsbCBwb3B1bGF0aW9uIGNvcnJlc3BvbmRpbmcgdG8gdGhlc2UgbWFya2VycyBhbmQgYW5ub3RhdGUgdGhpcyBjbHVzdGVyCgorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgQWR2YW50YWdlcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IExpbWl0cyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PSs9PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kwp8IC0gICAqKkVhc3kqKiB0byBpbXBsZW1lbnQgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAtICAgKipDbHVzdGVyaW5nKiogOiByZXNvbHV0aW9uLCBtZXJnZWQgY2x1c3RlcnMsICJiaW8taW5mb3JtYXRpYyIgY2x1c3RlciB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAtICAgU29tZXRpbWVzIHRoZSAqKm9ubHkqKiBzb2x1dGlvbiAoZXggOiBub3ZlbCB0aXNzdWUpIHwgLSAgIENoYW5nZSBjbHVzdGVyaW5nID8gQ2hhbmdlIGFubm90YXRpb24uLi4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgLSAgICoqRXZlcnl0aGluZyoqIGlzIHBvc3NpYmxlICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICAqKktub3dsZWRnZSoqIDogdGltZS1jb25zdW1pbmcgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwoKCgojIyBBdXRvbWF0aWMgYW5ub3RhdGlvbiB1c2luZyByZWZlcmVuY2UgbWFya2VycwoKRm9yIHNvbWUgdGlzc3VlcywgdGhlIGRpZmZlcmVudCBjZWxsIHR5cGVzIGhhdmUgYWxyZWFkeSBiZWVuIGxhcmdlbHkgZGVzY3JpYmVkIGFuZCBkYXRhYmFzZXMgZXhpc3Qgd2l0aCByZWZlcmVuY2VkIG1hcmtlciBnZW5lcy4gCgpBbm90aGVyIHdheSB0byBhbm5vdGF0ZSB5b3VyIGRhdGFzZXQgd2lsbCBiZSB0byBmaW5kIGEgZGF0YWJhc2Ugd2l0aCByZWxldmFudCBhbm5vdGF0aW9uIGZvciB5b3VyIGRhdGFzZXQgYW5kIHVzZSB0b29scyBvZiBhdXRvbWF0aWMgYW5ub3RhdGlvbiB0byBhbm5vdGF0ZSB5b3VyIGNsdXN0ZXJzLgoKKipMZXQncyBzZWUgaG93IGl0IHdvcmtzIGluIHByYWN0aWNlICEqKgoKV2Ugd2lsbCB1c2UgYSBkYXRhYmFzZSBmb2N1c2VkIG9uIGltbXVub2xvZ2ljYWwgY2VsbCB0eXBlcyBjYWxsZWQgW2BJbW1HZW5gXShodHRwczovL3d3dy5pbW1nZW4ub3JnLyksIHRoYW5rcyB0byB0aGUgW2BjZWxsZGV4YF0oaHR0cDovL2Jpb2NvbmR1Y3Rvci5vcmcvcGFja2FnZXMvcmVsZWFzZS9kYXRhL2V4cGVyaW1lbnQvaHRtbC9jZWxsZGV4Lmh0bWwpIFIgcGFja2FnZSB0aGF0IF8icHJvdmlkZXMgYSBjb2xsZWN0aW9uIG9mIHJlZmVyZW5jZSBleHByZXNzaW9uIGRhdGFzZXRzIHdpdGggY3VyYXRlZCBjZWxsIHR5cGUgbGFiZWxzLCBmb3IgdXNlIGluIHByb2NlZHVyZXMgbGlrZSBhdXRvbWF0ZWQgYW5ub3RhdGlvbiBvZiBzaW5nbGUtY2VsbCBkYXRhIG9yIGRlY29udm9sdXRpb24gb2YgYnVsayBSTkEtc2VxIl8KCioqKk5PVEUqKiA6IEluIHRoZSBmb2xsb3dpbmcgY2h1bmsgb2YgY29kZSwgeW91IHdpbGwgbG9hZCB0aGUgYW5ub3RhdGlvbiBmaWxlIGZyb20gdGhlIElGQiBzZXJ2ZXIgd2hlcmUgd2UgZG93bmxvYWRlZCBpdC4gSW4gcmVhbCBsaWZlIGFuZCB3aXRoIGEgdmVyc2lvbiBvZiBkYmR5cHIgaW5mIG9yIGVxdWFsIHRvIDIuMywgeW91IGNhbiBhbHNvIHVzZSB0aGlzIGNvbW1hbmQgOioKCmBgYCAgICAgICAgIAphbm5vdGF0aW9uID0gSW1tR2VuRGF0YShlbnNlbWJsID0gRkFMU0UpIAojIGVuc2VtYmwgc2V0IHRvIFRSVUUgaWYgd2Ugd2FudCBFTlNFTUJMIElEIGdlbmUgbmFtZXMsIEZBTFNFIHdpbGwgZ2V0IHRoZSBhbm5vdGF0aW9uIHdpdGggR2VuZSBTeW1ib2xzCmBgYAoKYGBge3IgcmVmbG9hZGluZywgZWNobz1UUlVFfQojIHJlZmxvYWRpbmcKCiMjIExvYWRpbmcgdGhlIEltbUdlbiBkYXRhYmFzZQphbm5vdGF0aW9uID0gcmVhZFJEUyhwYXN0ZTAoCiAgJy9zaGFyZWQvcHJvamVjdHMvJywKICBzZXNzaW9uaWQsCiAgJy9hdGVsaWVyX3Njcm5hc2VxL1REL1JFU09VUkNFUy9JbW1HZW5EYXRhLlJEUycpKQoKIyMgQSBxdWljayBkZXNjcmlwdGlvbiBvZiB0aGUgZGIKYW5ub3RhdGlvbgoKYGBgCgpUaGlzIGRhdGFiYXNlIGNvbnRhaW5zIDMgbGV2ZWxzIG9mIGdyYW51bGFyaXR5IDoKCi0gICBBICJtYWluIiBsZXZlbCAoY29hcnNlIGdyYWluKQoKLSAgIEEgImZpbmUiIGxldmVsIChzZWxmLWV4cGxhbmF0b3J5KQoKLSAgIFRoZSAiT05UIiBsZXZlbCAoZGF0YSBhcmUgbWFwcGVkIHRvIGEgZGVmaW5lZCBvbnRvbG9neSkKCkFzIHdlIGFyZSBpbiBhIGNvbnRleHQgb2Ygc29ydGVkIGNlbGxzIG9mIHRoZSBzYW1lIGxpbmVhZ2UsIHdlJ3JlIGdvaW5nIHRvIHVzZSB0aGUgKipmaW5lKiogbGFiZWwuCgpMZXQncyBzZWUgaG93IG1hbnkgY2VsbCB0eXBlcyBhcmUgZGVzY3JpYmVkIGluIHRoaXMgSW1tR2VuIGRhdGFiYXNlIDoKCmBgYHtyIEltbWdlbl9jdCwgZWNobz1UUlVFfQojIEltbWdlbl9jdAoKbGVuZ3RoKHVuaXF1ZShhbm5vdGF0aW9uJGxhYmVsLmZpbmUpKQoKYGBgCgpUaGUgdG9vbCB3ZSB3aWxsIHVzZSB0byBwZXJmb3JtIHRoZSBhdXRvbWF0aWMgY2VsbCB0eXBlIGFubm90YXRpb24sIFtgU2luZ2xlUmBdKGh0dHBzOi8vYmlvY29uZHVjdG9yLm9yZy9wYWNrYWdlcy9yZWxlYXNlL2Jpb2MvaHRtbC9TaW5nbGVSLmh0bWwpIHdvcmtzIGJldHRlciB3aXRoIHRoZSBub3JtYWxpemVkIGRhdGEuIFRodXMsIHdlIHdpbGwgZXh0cmFjdCB0aGUgbm9ybWFsaXplZCBtYXRyaXggZnJvbSBvdXIgU2V1cmF0IG9iamVjdCA6CgpgYGB7ciBub3JtX21hdCwgd2FybmluZz1GQUxTRX0KIyBub3JtX21hdAoKIyMgRXh0cmFjdGlvbiBvZiB0aGUgbm9ybWFsaXplZCBkYXRhIApub3JtX2V4cF9tYXQgPSBTZXVyYXQ6OkdldEFzc2F5RGF0YSgKICBvYmplY3QgPSBzb2JqLAogIGFzc2F5ID0gIlJOQSIsCiAgc2xvdCA9ICJkYXRhIgopCgojIyBNYXRyaXggZGltZW5zaW9ucwpkaW0obm9ybV9leHBfbWF0KQpgYGAKCldlIGFyZSByZWFkeSB0byBzdGFydCB0aGUgYW5ub3RhdGlvbi4KClRoZSBmb2xsb3dpbmcgYFNpbmdsZVJgIGNvbW1hbmQgcGVyZm9ybXMgdGhlIHByZWRpY3Rpb24gb2YgY2VsbCB0eXBlcyBmb3IgZWFjaCBjZWxsIG9mIHRoZSBkYXRhc2V0LgoKYGBge3IgYW5ucHJlZCwgd2FybmluZz1GQUxTRX0KIyBhbm5wcmVkCgojIyBSdW4gaW4gfiAzLTUgbWluIGRlcGVuZGluZyBvbiB0aGUgbnVtYmVyIG9mIENQVSBhbmQgbWVtb3J5IGRlZmluZWQKYW5uX3ByZWRpY3Rpb25zID0gU2luZ2xlUjo6U2luZ2xlUigKICAjIyBvdXIgbm9ybWFsaXplZCBtYXRyaXggIAogIHRlc3QgPSBub3JtX2V4cF9tYXQsIAogICMjIFRoZSBJbW1HZW4gREIKICByZWYgPSBhbm5vdGF0aW9uLCAKICAjIyBUaGUgYW5ub3RhdGlvbiBncmFpbiBsZXZlbAogIGxhYmVscyA9IGFubm90YXRpb24kbGFiZWwuZmluZSwgCiAgIyMgTWFya2VyIGRlY3RlY3Rpb24gc2NoZW1lIAogIGRlLm1ldGhvZD0iY2xhc3NpYyIsCiAgYXNzYXkudHlwZS50ZXN0ID0gImxvZ2NvdW50cyIsCiAgYXNzYXkudHlwZS5yZWYgPSAibG9nY291bnRzIiwKICBCUFBBUkFNID0gQmlvY1BhcmFsbGVsOjpTZXJpYWxQYXJhbSgpKQoKYGBgCgpUaGUgcmVzdWx0aW5nIG9iamVjdCBpcyBhIHNwZWNpYWwga2luZCBvZiBgZGF0YS5mcmFtZWAgLiBFYWNoIHJvdyBjb250YWlucyB0aGUgSUQgb2YgYSBjZWxsIGFuZCB0aGUgcHJlZGljdGlvbiBzY29yZSBhc3NvY2lhdGVkIGJ5IFNpbmdsZVIuIENlbGwgbGFiZWxzIGFzc29jaWF0ZWQgdG8gZWFjaCBjZWxsIGFyZSBzdG9yZWQgaW4gdGhlIGNvbHVtbiBgJGxhYmVsc2AKCkhvdyBtYW55IGRpZmZlcmVudCBraW5kcyBvZiBsYWJlbHMgd2VyZSBpZGVudGlmaWVkID8KCmBgYHtyIG5sYWJlbHN9CiMgbmxhYmVscwoKbGVuZ3RoKHVuaXF1ZShhbm5fcHJlZGljdGlvbnMkbGFiZWxzKSkKCmBgYAoKQmVzaWRlcyBzY29yaW5nLCBgU2luZ2xlUmAgYXNzZXNzZXMgdGhlIHNjb3JlIHF1YWxpdHksIGFuZCBwcnVuZXMgYmFkIHJlc3VsdHMuCgpIb3cgbWFueSBjZWxscyBnb3QgYSBwb29yIHF1YWxpdHkgYW5ub3RhdGlvbiA/CgpgYGB7ciBwcnVuZWRjZWxsc30KIyBwcnVuZWRjZWxscwoKc3VtbWFyeShpcy5uYShhbm5fcHJlZGljdGlvbnMkcHJ1bmVkLmxhYmVscykpCgpgYGAKCioqQW5ub3RhdGlvbiBkaWFnbm9zdGljKioKCmBTaW5nbGVSYCBhbGxvd3MgdG8gdmlzdWFsaXplIHNvbWUgY29udHJvbCBwbG90cyA6CgotICAgV2UgY2FuIHZpc3VhbGl6ZSB0aGUgc2NvcmUgb2YgZWFjaCBjZWxsLCBzcGxpdCBieSBjZWxsIHR5cGUgbGFiZWwsIGFzIGEgaGVhdG1hcCA6CgpgYGB7ciBoZWF0bWFwX3ByZWRpY2l0aW9uLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9OH0KIyBoZWF0bWFwX3ByZWRpY2l0aW9uCgpTaW5nbGVSOjpwbG90U2NvcmVIZWF0bWFwKGFubl9wcmVkaWN0aW9ucykKCmBgYAoKKkhvdyBkbyB5b3UgaW50ZXJwcmV0IHRoaXMgaGVhdG1hcCA/KgoKKipBZGQgdGhlIGFubm90YXRpb24gdG8gdGhlIFNldXJhdCBvYmplY3QqKgoKV2UgYWRkIGEgbmV3IG1ldGFkYXRhIGNvbnRhaW5pbmcgdGhlIGFubm90YXRpb24gb2YgZWFjaCBjZWxsIHRvIG91ciBTZXVyYXQgb2JqZWN0LgoKYGBge3IgcHJlZG1ldGF9CiMgcHJlZG1ldGEKCnNvYmokc2luZ2xlcl9jZWxsc19sYWJlbHMgPSBhbm5fcHJlZGljdGlvbnMkbGFiZWxzCgpgYGAKCioqVmlzdWFsaXphdGlvbiBvZiBvdXIgYW5ub3RhdGlvbiBvbiBVTUFQKioKCldlIGNhbiB2aXN1YWxpemUgY2VsbHMgYW5ub3RhdGlvbiB0aGUgdGhlIFVNQVAuCgpgYGB7ciBVTUFQbGFiZWxlZCwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CiMgVU1BUGxhYmVsZWQKCiMjIEp1c3QgZm9yIGFlc3RoZXRpY3MgKHNldHMgYSBjb2xvciBwYWxldHRlIHRvIHVzZSkKc2VlYWJsZV9wYWxldHRlID0gc2V0TmFtZXMoCiAgYyhSQ29sb3JCcmV3ZXI6OmJyZXdlci5wYWwobmFtZSA9ICJEYXJrMiIsIG4gPSA4KSwKICAgIGMoMToobGVuZ3RoKHVuaXF1ZShhbm5fcHJlZGljdGlvbnMkbGFiZWxzKSkgLSA4KSkpLAogIG5tID0gbmFtZXMoc29ydCh0YWJsZShhbm5fcHJlZGljdGlvbnMkbGFiZWxzKSwgZGVjcmVhc2luZyA9IFRSVUUpKSkKCiMjIFVNQVAgd2l0aCB0aGUgcHJlZGljdGVkIGFubm90YXRpb24gYnkgY2VsbAphbm5fcGxvdCA9IFNldXJhdDo6RGltUGxvdCgKICBvYmplY3QgPSBzb2JqLCAKICByZWR1Y3Rpb24gPSAidW1hcCIsIAogIGdyb3VwLmJ5ID0gInNpbmdsZXJfY2VsbHNfbGFiZWxzIiwgIAogIHB0LnNpemUgPSAyLAogIGNvbHMgPSBzZWVhYmxlX3BhbGV0dGUKKSArIGdncGxvdDI6OnRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQoKIyBVTUFQIHdpdGggdGhlIGNsdXN0ZXIgbnVtYmVycyAoYmVmb3JlIGFubm90YXRpb24pCmNsdXN0X3Bsb3QgPSBTZXVyYXQ6OkRpbVBsb3QoCiAgb2JqZWN0ID0gc29iaiwgCiAgcmVkdWN0aW9uID0gInVtYXAiLCAKICBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4wLjIiLAogIHB0LnNpemUgPSAyLAogIGxhYmVsID0gVFJVRSwKICByZXBlbCA9IFRSVUUKKQoKcHJpbnQoYW5uX3Bsb3QgKyBjbHVzdF9wbG90KQoKYGBgCgpBIGxvb2t1cCBhdCB0aGUgY29udGluZ2VuY3kgdGFibGUgb2YgY2VsbHMgcHJvamVjdGVkIHRvIHRoZSBJbW1HZW4gcmVmZXJlbmNlCmBgYHtyIHByb2pJR30KIyBwcm9qSUcKCnRhYmxlKHNvYmokc2luZ2xlcl9jZWxsc19sYWJlbHMpCgpgYGAKCkZyb20gdGhpcyByYXBpZCBwcmVkaWN0aW9uLCBpdCBzZWVtcyB0aGF0IG91ciBkYXRhc2V0IGNvbnRhaW5zIFQtY2VsbHMgbW9zdGx5LCBhbmQgcGFydGljdWxhcmx5IGBULkRQc21gLCAgYFQtRFA2OStgIGFuZCBgVC1JU1BgIC4KCk1heWJlIHRoZSBhbm5vdGF0aW9uIGlzIG5vdCBwZXJmZWN0bHkgc3VpdGVkIGZvciBvdXIgZGF0YXNldC4gU29tZSBjZWxsIHBvcHVsYXRpb25zIGluIHRoZSBhbm5vdGF0aW9uIGFyZSBjbG9zZWx5IHJlbGF0ZWQsIGFuZCB0aGlzIGxlYWRzIHRvIGFubm90YXRpb24gY29tcGV0aXRpb24gZm9yIG91ciBjZWxscy4KCkl0IGlzIHBvc3NpYmxlIHRvIHJ1biB0aGUgYW5ub3RhdGlvbiBhdCB0aGUgY2x1c3RlciBsZXZlbCA6IGl0IHdpbGwgYmUgY2xlYW5lciB0aGFuIHRoZSBzaW5nbGUgY2VsbCBsZXZlbCBhbm5vdGF0aW9uLiBCdXQsIGJlIHN1cmUgdGhhdCB0aGUgY2x1c3RlcmluZyBpcyBub3QgbWVyZ2luZyBzZXZlcmFsIGRpZmZlcmVudCBjZWxsIHBvcHVsYXRpb25zLgoKV2UgY2FuIGNoZWNrIHRoZSBudW1iZXIgb2YgY2VsbHMgYXR0cmlidXRlZCB0byBsYWJlbHMgZnJvbSBlYWNoIGNsdXN0ZXIgOgoKYGBge3IgY2VsbHBlcmNsdXN0ZXJ9CiMgY2VsbHBlcmNsdXN0ZXIKCnRhYmxlKHNvYmokc2luZ2xlcl9jZWxsc19sYWJlbHMsCiAgICAgIHNvYmokUk5BX3Nubl9yZXMuMC4yKQoKYGBgCgpXZSBjYW4gZXZlbnR1YWxseSBjaGVjayBpZiBzb21lIGNsdXN0ZXJzIGNvbnRhaW4gbXVsdGlwbGUgY2VsbCB0eXBlcy4gV2UgY29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiBlYWNoIGNlbGwgdHlwZSBpbiBlYWNoIGNsdXN0ZXIuIElmIGEgY2x1c3RlciBpcyBjb21wb3NlZCBvZiB0d28gY2VsbCB0eXBlcyAob3IgbW9yZSksIG1heWJlIHRoaXMgcmVzb2x1dGlvbiBmb3IgdGhlIGNsdXN0ZXJpbmcgaXMgdG9vIGxvdyA/CgpgYGB7ciBwcm9wY2VsbH0KIyBwcm9wY2VsbAoKIyMgQ29tcHV0ZSB0aGUgcHJvcG9ydGlvbiBvZiBjZWxsIHR5cGVzIHBlciBjbHVzdGVyCnBvcF9ieV9jbHVzdGVyID0gcHJvcC50YWJsZSh0YWJsZShzb2JqJHNpbmdsZXJfY2VsbHNfbGFiZWxzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc29iaiRSTkFfc25uX3Jlcy4wLjIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbWFyZ2luID0gMikKCiMjIFByaW50IG51bWJlciBvZiBjZWxsIHR5cGVzIHBlciBjbHVzdGVyIHdpdGggPj0zMCUgZnJvbSB0aGlzIGNsdXN0ZXIKY29sU3Vtcyhwb3BfYnlfY2x1c3RlciA+IDAuMykKCmBgYAoKQmV3YXJlIDoKCi0gICAqKnNtYWxsIHdlaXJkIGNsdXN0ZXJzIG9mIGNlbGxzKiogOiB0aGV5IG1pZ2h0IGJlIG9mIGludGVyZXN0IEJVVCB0aGV5IGNhbiBhbHNvIGJlIGNsdXN0ZXJpbmcgYXJ0ZWZhY3RzCgotICAgKip2ZXJ5IGxhcmdlIGNsdXN0ZXJzIG9mIGNlbGxzKiogOiBpZiB5b3Ugbm90aWNlIHRoYXQgbWFya2VyIGdlbmVzIGFyZSByZXByZXNlbnRhdGl2ZSBvZiBvbmx5IGEgZnJhY3Rpb24gb2YgdGhpcyBsYXJnZSBjbHVzdGVyLCB5b3UgbWlnaHQgbmVlZCB0byBhZGp1c3QgdGhlIGNsdXN0ZXJpbmcgcGFyYW1ldGVycyB0byBiZSBtb3JlIGRpc2NyaW1pbmF0aW5nLgoKIyMjIENvbmNsdXNpb24KCjEuICBGaW5kIGEgZ29vZCBtYXJrZXIgZ2VuZSByZWZlcmVuY2UgKFBhbmdsYW9EQiwgQ2VsbE1hcmtlciwgQ2FuY2VyU0VBLi4uKQoKMi4gIFNlbGVjdCBhIHRvb2wgLyBtb2RlbCA6ICoqY2xhc3NpZmllcioqLCAqKnNjb3JpbmcgZnVuY3Rpb24qKiAuLi4KCjMuICBBbm5vdGF0ZSB5b3VyIGRhdGFzZXQKCistLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSsKfCBBZHZhbnRhZ2VzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBMaW1pdGF0aW9ucyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAorPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0rCnwgLSAgIEFubm90YXRpb24gZm9yIGV2ZXJ5IHNpbmdsZSBjZWxsIGlzIHBvc3NpYmxlIHwgLSAgIEZpbmQgdGhlIGdvb2QgcmVmZXJlbmNlIG1hcmtlcnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IC0gICBEZXNpZ24geW91ciAqKm93biByZWZlcmVuY2UqKiAgICAgICAgICAgICAgICB8IC0gICBDZWxsIHR5cGVzICoqYXJib3Jlc2NlbmNlKiogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAtICAgKipMaW1pdGVkKiogbnVtYmVyIG9mIGNlbGwgdHlwZXMgOiBhbGwgY2VsbHMgYXJlIGFubm90YXRlZCwgb3IgInVua25vd24iIGlzIHBvc3NpYmxlID8gfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCgojIyBBdXRvbWF0aWMgYW5ub3RhdGlvbiB1c2luZyBhIHJlZmVyZW5jZSBzY1JOQXNlcQoKQW5vdGhlciBwb3NzaWJpbGl0eSBpcyB0byB1c2UgYSBwdWJsaXNoZWQgc2luZ2xlLWNlbGwgZGF0YXNldCBhcyBhIHJlZmVyZW5jZSBmb3IgdGhlIGNsdXN0ZXIgYW5ub3RhdGlvbi4KClRoaXMgaXMgdmVyeSB1c2VmdWwgd2hlbiB5b3Ugd29yayBvbiBhIHRpc3N1ZSB0aGF0IGlzIGNsb3NlIHRvIG9uZSB0aXNzdWUgYWxyZWFkeSBzdHVkaWVkLCBvciBpZiB5b3Ugd29yayBvbiBhbm90aGVyIHNwZWNpZXMgYW5kIHlvdSB3YW50IHRvIGhhdmUgYSBxdWljayBvdmVydmlldyBvZiB3aGF0IHRoZSBwcmVkaWN0ZWQgYW5ub3RhdGlvbiB3b3VsZCBsb29rIGxpa2UuIE11bHRpcGxlIHRvb2xzIGV4aXN0IHRvIHRyYW5zZmVyIHRoZSBhbm5vdGF0aW9ucyBvbiB5b3VyIG93biBkYXRhc2V0IChgU2luZ2xlUmAsIGBBemltdXRoYCwgYFN5bXBob255YCwgY2xhc3NpZmllcnMgbGlrZSBgU1ZNYHMgLi4uKS4gTWFueSBtZXRob2RzIGRvIGV4aXN0LCBjaG9vc2UgdGhlIG9uZSB5b3Uga25vdyB3ZWxsIGZpcnN0LCBvciBwZW9wbGUgb2YgeW91ciBsYWIgLyBiaW9pbmZvIHVzZSB0byBoYXZlIGhlbHAgaWYgbmVlZGVkLiAodGhlbiB5b3UgY2FuIHRyeSBvdGhlcnMuLi4pLgoKIVtBbm5vdGF0aW9uIHRyYW5zZmVydCB3aXRoIHJlZmVyZW5jZSBkYXRhc2V0XShhdXRvbWF0aWNfYW5ub3RhdGlvbl9yZWZkYXRhc2V0LnBuZykKCldlIGFyZSBub3QgZ29pbmcgdG8gdXNlIHRoaXMgbWV0aG9kIHRvZGF5IGJ1dCB5b3UgbWlnaHQgd2FudCB0byB1c2UgaXQgZm9yIHlvdXIgcHJhY3RpY2Fscy4KCkhlcmUgYXJlIHRoZSBtYWluIGNvbW1hbmQgZnJvbSBTaW5nbGUgUi4KCmBgYHtyIHNpbmdsZXJwdWIsIGNsYXNzLnNvdXJjZT0ibm90cnVuIiwgY2xhc3Mub3V0cHV0PSJub3RydW5vIiwgZXZhbCA9IEZBTFNFfQojIHNpbmdsZXJwdWIKCiMgTG9hZCB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgaW4gUkRTIGZvcm1hdCAoaXQgY2FuIGFsc28gYmUgbG9hZGVkIGluIGFub3RoZXIgZm9ybWF0LCBzZWUgdGhlIGRvYyBvZiBTaW5nbGVSIHRvIGNvbnZlcnQgeW91ciByZWZlcmVuY2UgdG8gYSBzdWl0YWJsZSBmb3JtYXQgZm9yIHRoZSBwcmVkaWN0aW9uKQoKUkVGX1NOUk5BU0VRID0gcmVhZFJEUygicmVmZXJlbmNlX3NjUk5Bc2VxLlJEUyIpCgojIyBUaGlzIGNvbW1hbmQgcmVtb3ZlcyBmcm9tIHRoZSBvYmplY3QgdGhlIGNlbGxzIHdpdGggYSBtZXRhZGF0YSAiQ2VsbC50eXBlIiB0aGF0IGlzIGEgTkEuClJFRl9TTlJOQVNFUSA9IFJFRl9TTlJOQVNFUVssIWlzLm5hKFJFRl9TTlJOQVNFUSRDZWxsLnR5cGUpXQoKIyMgTm9ybWFsaXplIHRoZSBsaWJyYXJ5IChTaW5nbGVSIG5lZWRzIG5vcm1hbGl6ZWQgZGF0YSkKUkVGX1NOUk5BU0VRIDwtIHNjYXR0ZXI6OmxvZ05vcm1Db3VudHMoUkVGX1NOUk5BU0VRKQoKIyMgQ3JlYXRlIGEgU2luZ2xlQ2VsbEV4cGVyaW1lbnQgb2JqZWN0IGZvciB5b3VyIHJlZmVyZW5jZSBkYXRhc2V0ClJFRl9TQ0UgPSBTZXVyYXQ6OmFzLlNpbmdsZUNlbGxFeHBlcmltZW50KHN4KQoKIyMgQ3JlYXRlIGEgU2luZ2xlQ2VsbEV4cGVyaW1lbnQgb2JqZWN0IGZvciB5b3VyIGRhdGFzZXQKc3hfc2NlID0gU2V1cmF0Ojphcy5TaW5nbGVDZWxsRXhwZXJpbWVudChzeCkKCiMjIFJVTiBTSU5HTEVSCnByZWQuZ3J1biA9IFNpbmdsZVIodGVzdCA9IHN4X3NjZSwKICAgICAgICAgICAgICAgICAgICByZWYgPSBSRUZfU0NFLAogICAgICAgICAgICAgICAgICAgIGxhYmVscyA9IFJFRl9TTlJOQVNFUSRDZWxsLnR5cGUubGFiZWxzLAogICAgICAgICAgICAgICAgICAgIGRlLm1ldGhvZCA9ICJ3aWxjb3giKQoKYGBgCgojIyMgQ29uY2x1c2lvbgoKMS4gIEZpbmQgYSBxdWFsaXR5IHJlZmVyZW5jZSBkYXRhc2V0IDogc2V2ZXJhbCBidWxrIFJOQS1zZXEgZGF0YSwgb25lIHNjUk5Bc2VxLi4uCgoyLiAgU2VsZWN0IGEgdG9vbCB0byB0cmFuc2ZlciBhbm5vdGF0aW9uIChTaW5nbGVSLCAuLi4pCgozLiAgQW5ub3RhdGUgeW91ciBkYXRhc2V0CgorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgQWR2YW50YWdlcyAgICAgICAgICAgICAgICAgICAgICAgIHwgTGltaXRhdGlvbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kwp8IC0gICAqKlNpbmdsZSBjZWxsKiogbGV2ZWwgICAgICAgICB8IC0gICBGaW5kIHRoZSBnb29kIHJlZmVyZW5jZSBkYXRhc2V0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAtICAgRGVzaWduIHlvdXIgKipvd24gcmVmZXJlbmNlKiogfCAtICAgKipMaW1pdGVkKiogbnVtYmVyIG9mIGNlbGwgdHlwZXMgKHlvdSBjYW4gb25seSBmaW5kIGNlbGwgdHlwZXMgcHJlc2VudCBpbiB0aGUgcmVmZXJlbmNlIGRhdGFzZXQpICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgLSAgIE5ldmVyIHRydXN0IDEwMCUgdGhlIHByZWRpY3Rpb24gb2YgdGhpcyBhdXRvbWF0aWMgYW5ub3RhdGlvbiwgc29tZSB0b29scyBkbyBub3QgaGF2ZSB0aGUgb3B0aW9uIHRvIHNheSAid2UgZG9uJ3QgZmluZCBjb3JyZXNwb25kYW5jZSIgYW5kIHdpbGwgZm9yY2UgdG8gZmluZCBhIGxhYmVsIGZvciBhbGwgY2VsbHMuIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwoKCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKCiMgR2VuZXJhbCBDb25jbHVzaW9uCgorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCnwgTWV0aG9kICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBBZHZhbnRhZ2VzICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgTGltaXRhdGlvbnMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT0rPT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kz09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09PT09Kwp8ICoqTWFudWFsIGNsdXN0ZXIgYW5ub3RhdGlvbioqIHVzaW5nIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIHwgLSAgICoqRWFzeSoqIHRvIGltcGxlbWVudCAgICAgICAgICAgICB8IC0gICAqKkNsdXN0ZXJpbmcqKiA6IHJlc29sdXRpb24sIG1lcmdlZCBjbHVzdGVycywgImJpby1pbmZvcm1hdGljIiBjbHVzdGVyICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBNYXkgYmUgdGhlICoqb25seSoqIHNvbHV0aW9uICAgICAgfCAtICAgQ2hhbmdlIGNsdXN0ZXJpbmcgPyBDaGFuZ2UgYW5ub3RhdGlvbi4uLiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAtICAgKipFdmVyeXRoaW5nKiogaXMgcG9zc2libGUgICAgICAgIHwgLSAgICoqS25vd2xlZGdlKiogOiB0aW1lLWNvbnN1bWluZyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IEF1dG9tYXRpYyBhbm5vdGF0aW9uIHVzaW5nICoqcmVmZXJlbmNlIG1hcmtlcnMqKiAgICAgICAgICAgIHwgLSAgICoqU2luZ2xlIGNlbGwqKiBsZXZlbCBpcyBwb3NzaWJsZSB8IC0gICBGaW5kIHRoZSBnb29kIHJlZmVyZW5jZSBtYXJrZXJzICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBEZXNpZ24geW91ciAqKm93biByZWZlcmVuY2UqKiAgICAgfCAtICAgQ2VsbCB0eXBlcyAqKmFyYm9yZXNjZW5jZSoqICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgLSAgICoqTGltaXRlZCoqIG51bWJlciBvZiBjZWxsIHR5cGVzIDogYWxsIGNlbGxzIGFyZSBhbm5vdGF0ZWQsIG9yICJ1bmtub3duIiA/IHwKKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKy0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tKwp8IEF1dG9tYXRpYyBhbm5vdGF0aW9uIHVzaW5nICoqcmVmZXJlbmNlIGRhdGFzZXQqKiAgICAgICAgICAgIHwgLSAgICoqU2luZ2xlIGNlbGwqKiBsZXZlbCAgICAgICAgICAgICB8IC0gICBGaW5kIHRoZSBnb29kIHJlZmVyZW5jZSBkYXRhc2V0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IC0gICBEZXNpZ24geW91ciAqKm93biByZWZlcmVuY2UqKiAgICAgfCAtICAgKipMaW1pdGVkKiogbnVtYmVyIG9mIGNlbGwgdHlwZXMgOiBhbGwgY2VsbHMgYXJlIGFubm90YXRlZCwgb3IgInVua25vd24iID8gfAorLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSstLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0rCgoqKipBIGZldyBhZHZpY2VzIDopKioqCgotICAgSXQgaXMgcmVjb21tZW5kZWQgdG8gY29tYmluZSBtdWx0aXBsZSBtZXRob2RzIHRvIGFubm90YXRlIHlvdXIgZGF0YQoKICAgIC0gICBVc2UgbWFudWFsIGNsdXN0ZXIgYW5ub3RhdGlvbiB0byBpZGVudGlmeSBxdWlja2x5IHlvdXIgY2VsbCBwb3B1bGF0aW9ucwoKICAgIC0gICBJZGVudGlmeSBnb29kIG1hcmtlcnMgZm9yIGVhY2ggY2VsbCBwb3B1bGF0aW9ucyDihpIgeW91ciByZWZlcmVuY2UgbWFya2VycwoKICAgIC0gICBVc2UgYXV0b21hdGljIGNlbGwgYW5ub3RhdGlvbiB1c2luZyB5b3VyIHNldCBvZiBtYXJrZXIg4oaSIHlvdXIgcmVmZXJlbmNlIGRhdGFzZXQKCiAgICAtICAgVXNlIHlvdXIgcmVmZXJlbmNlcyB0byBhbm5vdGF0ZSBuZXcgZGF0YXNldCBhbmQgZ28gYmFjayB0byBtYW51YWwgYW5ub3RhdGlvbiB0byByZWZpbmUgeW91ciBhbmFseXNpcy4KCi0gICBTb21ldGltZXMsIGFubm90YXRpb24gcmV2ZWFscyB0aGF0IHRoZSBkYXRhc2V0IHdvdWxkIGJlbmVmaXQgZnJvbSBhIHJlLWNsdXN0ZXJpbmcgaWYgeW91IHJlYWxpemUgdGhhdCBzb21lIGNsdXN0ZXIgY291bGQgZ3JvdXAgMiBjZWxsIHR5cGVzIG9yIG9uIHRoZSBjb250cmFyeSwgd2hlbiB0d28gZGlmZmVyZW50IGNsdXN0ZXIgZXhwcmVzc2VkIHZlcnkgc2ltaWxhciBtYXJrZXJzIGFuZCBzaG91bGQgYmUgbWVyZ2VkLgoKLSAgIER1cmluZyBhbm5vdGF0aW9uLCBkbyBub3QgaGVzaXRhdGUgdG8gbG9vayBhdCB0aGUgZXhwcmVzc2lvbiBvZiBNaXRvY2hvbmRyaWFsIG9yIFJpYm9zb21hbCBnZW5lcyAob3IgYW55IG90aGVyIHNldCBvZiBnZW5lcykgaW4geW91ciBjbHVzdGVycy4gSXQgbWlnaHQgaGVscCB5b3UgdG8gaWRlbnRpZnkgYSBjbHVzdGVyIG9mIGNlbGxzIHRoYXQgYXJlIGxvb2tzIHdlaXJkIHRvIHlvdS4gQ2x1c3RlcnMgb2YgImFydGlmaWNpYWwiIGNlbGxzIC0gY2VsbHMgb2YgbG93IHF1YWxpdHktIGNvdWxkIGxlYWQgdG8gdGhlIGlkZW50aWZpY2F0aW9uIG9mIHdlaXJkIChub3ZlbD8hKSBjZWxscyB0aGF0IGhhdmUgbm8gcmVhbCBiaW9sb2dpY2FsIHNpZ25pZmljYW5jZS4gQnV0ICoqYmUgY2FyZWZ1bCoqIGEgY2x1c3RlciB3aXRoIGEgaGlnaCBleHByZXNzaW9uIG9mIG1pdG9jaG9uZHJpYWwgb3Igcmlib3NvbWFsIGdlbmVzIGNhbiBoYXZlIGJpb2xvZ2ljYWwgbWVhbmluZyBzb21ldGltZXMuCgpbKk5vdGUgYWJvdXQgYXV0b21hdGljIGFubm90YXRpb24gOipdey51bmRlcmxpbmV9CgoqSWYgeW91IGFyZSB3b3JraW5nIHdpdGggKipub24tbW9kZWwgc3BlY2llcyoqIG9yIHdpdGggKiptdWx0aXBsZSBzcGVjaWVzKiogOiBpdCBpcyBub3QgdHJpdmlhbCB0byB0cmFuc2ZlcnQgYW4gYW5ub3RhdGlvbiBmcm9tIG9uZSBzcGVjaWVzIHRvIGFub3RoZXIuIEdlbmVzIG1hcmtlcnMgYXJlIG5vdCBhbHdheXMgY29uc2VydmVkIGFjcm9zcyB0aGUgZXZvbHV0aW9uLiBJbiB0aGlzIGNhc2UsICoqbWFudWFsIGFubm90YXRpb24qKiBpcyBhIHZlcnkgaW1wb3J0YW50IHNhbml0eSBjaGVjayBvZiBhbnkgYXV0b21hdGljIGFubm90YXRpb24gISEqCgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCgojIE9wdGlvbmFsIFBhcnQKCiMjIEFubm90YXRpb24gdHJhbnNmZXIKClNpbmdsZVIgY2FuIHRyYW5zZmVyIGNlbGwgYW5ub3RhdGlvbnMgZnJvbSBhIHJlZmVyZW5jZSBxdWVyeSB0byB5b3VyIGRhdGFzZXQsIGF0IHRoZSBjbHVzdGVyIGxldmVsLgoKIyMjIENsdXN0ZXIgbGV2ZWwgYW5ub3RhdGlvbgoKIyMjIyBFeHBsYW5hdGlvbgoKSXQgaXMgcG9zc2libGUgdG8gcnVuIHRoZSBwcmVkaWN0aW9uIG9mIGNlbGwgdHlwZXMgd2l0aCBgU2luZ2xlUmAgcGVyIGNsdXN0ZXIgaW5zdGVhZCBvZiBwZXIgY2VsbC4gVGhlIGlkZWEgaXMgc2ltaWxhciwgYnV0IGluc3RlYWQgb2YgYW5ub3RhdGVkIGV2ZXJ5IHNpbmdsZSBjZWxsIHRvIGl0cyBiZXN0IG1hdGNoIGluIHRoZSByZWZlcmVuY2UgZGF0YXNldCwgaXQgYW5ub3RhdGVzIGV2ZXJ5IGNsdXN0ZXIgZnJvbSB5b3VyIHF1ZXJ5IGRhdGFzZXQgdG8gaXQncyBhdmVyYWdlIGJlc3QgbWF0Y2ggaW4gdGhlIHJlZmVyZW5jZSBkYXRhc2V0LiAoYFNpbmdsZVJgIHdpbGwgc3VtbWFyaXplIHRoZSBleHByZXNzaW9uIHByb2ZpbGVzIG9mIGFsbCBjZWxscyBmcm9tIHRoZSBzYW1lIGNsdXN0ZXIsIGFuZCB0aGVuIGFzc2VzcyB0aGUgcmVzdWx0aW5nIGFnZ3JlZ2F0aW9uKSA6CgoqTm90ZSA6IHdlIHJ1biB0aGUgc2FtZSBjb21tYW5kIGFzIGJlZm9yZSAoU2luZ2xlUiksIHdlIG9ubHkgYWRkIHRoZSBwYXJhbWV0ZXIgImNsdXN0ZXIiIHRvIFNpbmdsZVIgZnVuY3Rpb24gdG8gYW5ub3RhdGUgYnkgY2x1c3RlciBhbmQgbm90IGJ5IGNlbGwuKgoKKipBZHZhbnRhZ2UocykqKgoKLSAgICoqTXVjaCBGYXN0ZXIqKiBhdCB0aGUgY2x1c3RlciBsZXZlbCA6IFNpbmdsZVIgY2FuIGJlIHRpbWUtY29uc3VtaW5nIHRvIHJ1biBvbiBldmVyeSBzaW5nbGUgY2VsbCBvciB5b3UgZGF0YXNldCwgcGFydGljdWxhcmx5IGlmIHRoZSByZWZlcmVuY2UgYW5kIHRoZSBxdWVyeSBkYXRhc2V0IGFyZSBiaWcuIFNvbWV0aW1lcywgeW91IG1pZ2h0IHdhbnQgdG8gcGVyZm9ybSBhIGZpcnN0IHF1aWNrIHByZWRpY3Rpb24gYXQgdGhlIGNsdXN0ZXIgbGV2ZWwganVzdCB0byBoYXZlIGEgZ2VuZXJhbCBpZGVhIG9uIGhvdyB0aGUgcHJlZGljdGlvbiB3b3JrcyB3aXRoIHlvdXIgZGF0YXNldCwgYW5kIHdoaWNoIGNlbGwgdHlwZXMgeW91IGNhcHR1cmUgaW4geW91ciBkYXRhc2V0LgoKLSAgICoqQ2hlY2sgaWYgdGhlIHJlZmVyZW5jZSBkYXRhc2V0IHdpbGwgYmUgb2YgYW55IGhlbHAqKiA6IGJlZm9yZSBydW5uaW5nIGEgbG9uZyBwcmVkaWN0aW9uIG9mIGNlbGwgYW5ub3RhdGlvbiB3aXRoIFNpbmdsZVIsIChhZ2FpbiBpZiB5b3VyIGRhdGFzZXQgaXMgYmlnKSwgeW91IHdhbnQgdG8ga25vdyBpZiB0aGUgcmVmZXJlbmNlIHlvdSBhcmUgdXNpbmcgaXMgaGVscGluZyBhdCB0aGUgY2x1c3RlciBsZXZlbC4gSWYgaXQgZmFpbHMgdG8gaWRlbnRpZnkgY2VsbCB0eXBlcywgZXZlbiAiZ2VuZXJhbCIgY2VsbCB0eXBlcywgZm9yZ2V0IGl0LgoKIyMjIyBDb2RlIDoKCmBgYHtyIHJlcHJlZH0KIyByZXByZWQKCiMgUmVydW4gYSBwcmVkaWN0aW9uIHVzaW5nIGNsdXN0ZXJpbmcgaW5mb3JtYXRpb24gCiMgVGhpcyBjb21tYW5kIGlzIG11Y2ggZmFzdGVyIGJlY2F1c2UgdGhlIHByZWRpY3Rpb24gaXMgb25seSBwZXJmb3JtZWQgZm9yIHRoZSA3IGNsdXN0ZXJzIGFuZCBub3QgZm9yIGVhY2ggY2VsbC4KY2x1c3RfYW5uX3ByZWRpY3Rpb25zICA9CiAgICBTaW5nbGVSOjpTaW5nbGVSKAogICAgdGVzdCA9IG5vcm1fZXhwX21hdCwKICAgIGNsdXN0ZXJzID0gc29iaiRSTkFfc25uX3Jlcy4wLjIsCiAgICByZWYgPSBhbm5vdGF0aW9uLAogICAgbGFiZWxzID0gYW5ub3RhdGlvbiRsYWJlbC5maW5lLAogICAgYXNzYXkudHlwZS50ZXN0ID0gImxvZ2NvdW50cyIsCiAgICBhc3NheS50eXBlLnJlZiA9ICJsb2djb3VudHMiLAogICAgQlBQQVJBTSA9IEJpb2NQYXJhbGxlbDo6U2VyaWFsUGFyYW0oKQogICkKCmBgYAoKKk5vdGUgOiB3ZSBydW4gdGhlIHNhbWUgY29tbWFuZCBhcyBiZWZvcmUgKFNpbmdsZVIpLCB3ZSBvbmx5IGFkZCB0aGUgcGFyYW1ldGVyICJjbHVzdGVyIiB0byBTaW5nbGVSIGZ1bmN0aW9uIHRvIGFubm90YXRlIGJ5IGNsdXN0ZXIgYW5kIG5vdCBieSBjZWxsLioKCkhvdyBtYW55IGNsdXN0ZXJzIGhhdmUgYmVlbiBsYWJlbGxlZCBmb3IgZWFjaCBhbm5vdGF0aW9uIGxhYmVsID8KCmBgYHtyIGNjbGFifQojIGNjbGFiCgojIyBFWFBMQU5BVElPTiBPRiBUSEUgQ09NTUFORCBCRUxPVwoKaGVhZChzb3J0KHRhYmxlKGNsdXN0X2Fubl9wcmVkaWN0aW9ucyRsYWJlbHMpLCBkZWNyZWFzaW5nID0gVFJVRSkpCgojIyBUaGlzIGNvbW1hbmQgdGFrZSB0aGUgdGFibGUgb2YgYW5ub3RhdGlvbiBsYWJlbHMgKGNsdXN0X2Fubl9wcmVkaWN0aW9ucyRsYWJlbHMpCiMjIEl0IHVzZXMgdGhlIGZ1bmN0aW9uIHRhYmxlIHRvIGNyZWF0ZSBhIGNvbnRpbmdlbmN5IHRhYmxlIHNheWluZyAiaG93IG1hbnkgdGltZSB0aGUgImxhYmVscyIgZnJvbSB0aGUgcmVmZXJlbmNlIGRhdGFzZXQgd2VyZSBhc3NpZ25zIGFjcm9zcyBjbHVzdGVycyIKIyMgVGhlbiB0aGUgZnVuY3Rpb24gc29ydCBvcmRlciBpbiBhICJkZWNyZWFzaW5nIiBvcmRlciB0aGlzIHRhYmxlIHRvIGhhdmUgZmlyc3QgdGhlIGxhYmVscyBhc3NpZ25lZCB0aGUgbW9zdAojIyBGaW5hbGx5IHdlIHNob3cgb25seSB0aGUgZmlyc3QgNSBsaW5lcyBvZiB0aGUgc29ydGVkIHRhYmxlIHVzaW5nIHRoZSBmdW5jdGlvbiBoZWFkCgpgYGAKCkZvciBob3cgbWFueSBjbHVzdGVycyB3YXMgdGhlIGFubm90YXRpb24gb2YgcG9vciBxdWFsaXR5ID8KCmBgYHtyIHBydW5lZF9jbHVzdGVyc30KIyBwcnVuZWRfY2x1c3RlcnMKCiMjIEVYUExBTkFUSU9OIE9GIFRIRSBDT01NQU5EIEJFTE9XCgpzdW1tYXJ5KGlzLm5hKGNsdXN0X2Fubl9wcmVkaWN0aW9ucyRwcnVuZWQubGFiZWxzKSkKCiMjIFRoaXMgY29tbWFuZCB0YWtlcyB0aGUgY29sdW1uICJwcnVuZWQubGFiZWxzIiBmcm9tIHRoZSB0YWJsZSBvZiBwcmVkaWN0aW9uIAojIyB0aGVuIHRoZSBjb21tYW5kICJpcy5uYSIgbG9va3MgZm9yIE5BIHZhbHVlIGluIHRoZSBjb2x1bW4gInBydW5lZC5sYWJlbHMiCiMjIEZpbmFsbHksIHRoZSBmdW5jdGlvbiBzdW1tYXJ5IGdpdmVzIHRoZSBtZWFuIC8gbWF4LyBldGMgbWV0cmljcyBvZiB0aGUgdmFsdWVzIGluIHRoZSBjb2x1bW4gcHJ1bmVkIGxhYmVscy4KYGBgCgoqKkFubm90YXRpb24gZGlhZ25vc3RpYyoqCgpXZSBjYW4gdmlzdWFsaXplIHRoZSBzY29yZXMgZm9yIGVhY2ggY2VsbCB0eXBlLCB0byBlYWNoIGNlbGwsIGFzIGEgaGVhdG1hcCA6CgpgYGB7ciBoZWF0YmMsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD04fQojIGhlYXRiYwoKIyMgSGVhdG1hcCB1c2luZyB0aGUgYW5ub3RhdGlvbiBwcmVkaWN0aW9uIGJ5IGNsdXN0ZXIKU2luZ2xlUjo6cGxvdFNjb3JlSGVhdG1hcChjbHVzdF9hbm5fcHJlZGljdGlvbnMpCgpgYGAKCipXaGF0IGRvIHlvdSBvYnNlcnZlIGhlcmUgPyBXaGF0IGlzIHRoZSBkaWZmZXJlbmNlIHdpdGggdGhlIGFubm90YXRpb24gYnkgY2VsbCA/KgoKKipBZGQgYW5ub3RhdGlvbiB0byBtZXRhZGF0YSoqCgpXZSBhZGQgdGhlIGFubm90YXRpb24gdG8gb3VyIFNldXJhdCBvYmplY3QuCgpgYGB7ciBhZGQyfQojIGFkZDIKCiMjIFNhdmUgdGhlIG5hbWUgb2YgZnV0dXJlIGFubm90YXRpb24KY2x1c3RfbGFiZWxzX2NvbCA9ICJzaW5nbGVyX2NsdXN0X2xhYmVscyIKCiMjIENyZWF0ZSBhIGNvbHVtbiB3aXRoIHRoaXMgbmFtZSBpbiB0aGUgbWV0YWRhdGEgYW5kIGZpbGwgaXQgd2l0aCB0aGUgY2x1c3RlciBsZXZlbHMgb2YgZWFjaCBjZWxsCnNvYmpAbWV0YS5kYXRhW1tjbHVzdF9sYWJlbHNfY29sXV0gPSBzb2JqQG1ldGEuZGF0YSRSTkFfc25uX3Jlcy4wLjIKCiMjIEZpbGwgYXNzb2NpYXRlIGVhY2ggY2x1c3RlciB3aXRoIGl0cyBhbm5vdGF0aW9uIApsZXZlbHMoc29iakBtZXRhLmRhdGFbW2NsdXN0X2xhYmVsc19jb2xdXSkgPSBjbHVzdF9hbm5fcHJlZGljdGlvbnMkbGFiZWxzCgpgYGAKCmBgYHtyIGRpc3BsYXlwcmVkfQojIGRpc3BsYXlwcmVkCgpjbHVzdF9hbm5fcHJlZGljdGlvbnMkbGFiZWxzCmxldmVscyhzb2JqQG1ldGEuZGF0YVtbY2x1c3RfbGFiZWxzX2NvbF1dKQoKYGBgCgoqKlZpc3VhbGl6YXRpb24qKgoKV2UgY2FuIHZpc3VhbGl6ZSBjZWxscyBhbm5vdGF0aW9uIHRoZSB0aGUgMkQgcHJvamVjdGlvbiA6CgpgYGB7ciB1bWFwY29tcCwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTh9CiMgdW1hcGNvbXAKCmFubl9jbHVzdGVyX3Bsb3QgPSBTZXVyYXQ6OkRpbVBsb3QoCiAgb2JqZWN0ID0gc29iaiwgCiAgcmVkdWN0aW9uID0gInVtYXAiLCAKICBncm91cC5ieSA9IGNsdXN0X2xhYmVsc19jb2wsCiAgcHQuc2l6ZSA9IDIsCiAgbGFiZWwgPSBGQUxTRSwgCiAgY29scyA9IHNlZWFibGVfcGFsZXR0ZQopICsgZ2dwbG90Mjo6dGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgphbm5fY2VsbF9wbG90ID0gU2V1cmF0OjpEaW1QbG90KAogIG9iamVjdCA9IHNvYmosIAogIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgZ3JvdXAuYnkgPSAic2luZ2xlcl9jZWxsc19sYWJlbHMiLAogIHB0LnNpemUgPSAyLAogIGxhYmVsID0gRkFMU0UsCiAgcmVwZWwgPSBUUlVFLCAKICBjb2xzID0gc2VlYWJsZV9wYWxldHRlCikgKyBnZ3Bsb3QyOjp0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKCmFubl9jbHVzdGVyX3Bsb3QgKyBhbm5fY2VsbF9wbG90CgpgYGAKCioqU2F2ZSB5b3VyIFNldXJhdCBvYmplY3QgYW5ub3RhdGVkKioKCldlIHNhdmUgdGhlIGFubm90YXRlZCBTZXVyYXQgb2JqZWN0IDoKCmBgYHtyIHNhdmVyZHN9CiMgc2F2ZXJkczMKCiMjIFNhdmUgb3VyIFNldXJhdCBvYmplY3QgKHJpY2ggbmFtaW5nKQpvdXRfbmFtZSA8LSBwYXN0ZTAoCiAgICAgICAgICBvdXRwdXRfZGlyLCAiLyIsIHBhc3RlKAogICAgICAgICAgICBjKCIxMyIsIFNldXJhdDo6UHJvamVjdChzb2JqKSwgIlM1IiwgCiAgICAgICAgICAgICAgIkludGVncmF0ZWRfQW5ub3RhdGVkIgogICAgICAgICAgICApLCBjb2xsYXBzZSA9ICJfIiksCiAgICAgICAgICAgICIuUkRTIikKCiMjIENoZWNrCnByaW50KG91dF9uYW1lKQoKIyMgV3JpdGUgb24gZGlzawpzYXZlUkRTKG9iamVjdCA9IHNvYmosIAogICAgICAgIGZpbGUgPSBvdXRfbmFtZSkKCmBgYAoKCiMgUmVmZXJlbmNlcwoKR29vZCBwcmFjdGljZXMgZm9yIHNpbmdsZSBjZWxsIGFuYWx5c2lzIDogPGh0dHBzOi8vd3d3LnNjLWJlc3QtcHJhY3RpY2VzLm9yZy9wcmVhbWJsZS5odG1sPgoKU2FuZ2VyIFNpbmdsZSBjZWxsIGNvdXJzZSA6IDxodHRwczovL3d3dy5zaW5nbGVjZWxsY291cnNlLm9yZy9pbmRleC5odG1sPgoKU2luZ2xlUiA6IDxodHRwczovL2Jpb2NvbmR1Y3Rvci5vcmcvYm9va3MvMy4xMi9TaW5nbGVSQm9vay8+CgojIFJlc3NvdXJjZXMKCioqKkZvciBodW1hbiA6KioqCgpHZW5lQ2FyZCA6IDxodHRwczovL3d3dy5nZW5lY2FyZHMub3JnPgoKSHVtYW4gUHJvdGVpbiBBdGxhcyA6IDxodHRwczovL3d3dy5wcm90ZWluYXRsYXMub3JnL3NlYXJjaC9IMi1LMT4KCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKPGJyPjxicj48YnI+CgojIFJzZXNzaW9uCgpgYGB7ciByc2Vzc2lvbiwgY2xhc3Muc291cmNlPSJub3RydW4iLCBjbGFzcy5vdXRwdXQ9Im5vdHJ1bm8ifQojIHJzZXNzaW9uCgp1dGlsczo6c2Vzc2lvbkluZm8oKQoKYGBgCg==