1. load libraries

2. Load Seurat Object


#Load Seurat Object merged from cell lines and a control(PBMC) after filtration
load("0-R_Objects/SS_CD4_Tcells_Azimuth_Annotated_PBMC10x_final_for_SCT_and_Integration.robj")

All_samples_Merged <- filtered_seurat

All_samples_Merged
An object of class Seurat 
36752 features across 49372 samples within 5 assays 
Active assay: RNA (36601 features, 0 variable features)
 2 layers present: data, counts
 4 other assays present: ADT, prediction.score.celltype.l1, prediction.score.celltype.l2, prediction.score.celltype.l3
 2 dimensional reductions calculated: integrated_dr, ref.umap

Summarizing Seurat Object


# Load necessary libraries
library(Seurat)

# Display basic metadata summary
head(All_samples_Merged@meta.data)

# Check if columns such as `orig.ident`, `nCount_RNA`, `nFeature_RNA`, `nUMI`, `ngene`, and any other necessary columns exist
required_columns <- c("orig.ident", "nCount_RNA", "nFeature_RNA", "nUMI", "ngene")
missing_columns <- setdiff(required_columns, colnames(All_samples_Merged@meta.data))

if (length(missing_columns) > 0) {
    cat("Missing columns:", paste(missing_columns, collapse = ", "), "\n")
} else {
    cat("All required columns are present.\n")
}
All required columns are present.
# Check cell counts and features
cat("Number of cells:", ncol(All_samples_Merged), "\n")
Number of cells: 49372 
cat("Number of features:", nrow(All_samples_Merged), "\n")
Number of features: 36601 
# Verify that each `orig.ident` label has the correct number of cells
cat("Cell counts per group:\n")
Cell counts per group:
print(table(All_samples_Merged$orig.ident))

     L1      L2      L3      L4      L5      L6      L7    PBMC PBMC10x 
   5825    5935    6428    6007    6022    5148    5331    5171    3505 
# Check that the cell IDs are unique (which ensures no issues from merging)
if (any(duplicated(colnames(All_samples_Merged)))) {
    cat("Warning: There are duplicated cell IDs.\n")
} else {
    cat("Cell IDs are unique.\n")
}
Cell IDs are unique.
# Check the assay consistency for RNA
DefaultAssay(All_samples_Merged) <- "RNA"

# Check dimensions of the RNA counts layer using the new method
cat("Dimensions of the RNA counts layer:", dim(GetAssayData(All_samples_Merged, layer = "counts")), "\n")
Dimensions of the RNA counts layer: 36601 49372 
cat("Dimensions of the RNA data layer:", dim(GetAssayData(All_samples_Merged, layer = "data")), "\n")
Dimensions of the RNA data layer: 36601 49372 
# Check the ADT assay (optional)
if ("ADT" %in% names(All_samples_Merged@assays)) {
    cat("ADT assay is present.\n")
    cat("Dimensions of the ADT counts layer:", dim(GetAssayData(All_samples_Merged, assay = "ADT", layer = "counts")), "\n")
} else {
    cat("ADT assay is not present.\n")
}
ADT assay is present.
Dimensions of the ADT counts layer: 56 49372 

3. QC


# Remove the percent.mito column
All_samples_Merged$percent.mito <- NULL


# Set identity classes to an existing column in meta data
Idents(object = All_samples_Merged) <- "cell_line"

All_samples_Merged[["percent.rb"]] <- PercentageFeatureSet(All_samples_Merged, 
                                                           pattern = "^RP[SL]")
# Convert 'percent.mt' to numeric, replacing "NaN" with 0
All_samples_Merged$percent.rb <- replace(as.numeric(All_samples_Merged$percent.rb), is.na(All_samples_Merged$percent.rb), 0)



# The [[ operator can add columns to object metadata. This is a great place to stash QC stats
All_samples_Merged[["percent.mt"]] <- PercentageFeatureSet(All_samples_Merged, pattern = "^MT-")

# Convert 'percent.mt' to numeric, replacing "NaN" with 0
All_samples_Merged$percent.mt <- replace(as.numeric(All_samples_Merged$percent.mt), is.na(All_samples_Merged$percent.mt), 0)





VlnPlot(All_samples_Merged, features = c("nFeature_RNA", 
                                         "nCount_RNA", 
                                         "percent.mt",
                                         "percent.rb"), 
                            ncol = 4, pt.size = 0.1) & 
              theme(plot.title = element_text(size=10))


FeatureScatter(All_samples_Merged, feature1 = "percent.mt", 
                                  feature2 = "percent.rb")


VlnPlot(All_samples_Merged, features = c("nFeature_RNA", 
                                    "nCount_RNA", 
                                    "percent.mt"), 
                                      ncol = 3)


FeatureScatter(All_samples_Merged, 
               feature1 = "percent.mt", 
               feature2 = "percent.rb") +
        geom_smooth(method = 'lm')


FeatureScatter(All_samples_Merged, 
               feature1 = "nCount_RNA", 
               feature2 = "nFeature_RNA") +
        geom_smooth(method = 'lm')

##FeatureScatter is typically used to visualize feature-feature relationships ##for anything calculated by the object, ##i.e. columns in object metadata, PC scores etc.


FeatureScatter(All_samples_Merged, 
               feature1 = "nCount_RNA", 
               feature2 = "percent.mt")+
  geom_smooth(method = 'lm')


FeatureScatter(All_samples_Merged, 
               feature1 = "nCount_RNA", 
               feature2 = "nFeature_RNA")+
  geom_smooth(method = 'lm')

4. Data PREPARATION

options(future.globals.maxSize = 8000 * 1024^2)  # Set to 8000 MiB (about 8 GB)
# Data Preparation for Seurat v5
alldata <- All_samples_Merged

# Split the object by 'orig.ident' for individual dataset processing
alldata.list <- SplitObject(alldata, split.by = "orig.ident")

# Normalize and identify variable features for each dataset in the list
for (i in 1:length(alldata.list)) {
    alldata.list[[i]] <- SCTransform(alldata.list[[i]], vars.to.regress = c("percent.rb","percent.mt", "nCount_RNA"), verbose = FALSE)
    alldata.list[[i]] <- FindVariableFeatures(alldata.list[[i]], selection.method = "vst", nfeatures = 3000, verbose = FALSE)
}

# Get the variable genes for each dataset
hvgs_per_dataset <- lapply(alldata.list, VariableFeatures)

# Include the variable genes selected on the whole dataset
hvgs_per_dataset$all <- VariableFeatures(alldata)

# Create a heatmap to show overlap across datasets
temp <- unique(unlist(hvgs_per_dataset))
overlap <- sapply(hvgs_per_dataset, function(x) temp %in% x)
pheatmap::pheatmap(t(overlap * 1), cluster_rows = FALSE,
                   color = c("grey90", "grey20"))


# Select integration features across datasets
hvgs_all <- SelectIntegrationFeatures(alldata.list)
# Exclude specific genes
hvgs_all <- hvgs_all[!grepl("^HLA-|^XIST|^TRBV|^TRAV", hvgs_all)]

hvgs_per_dataset$all_ranks <- hvgs_all

# Generate the overlap heatmap for all selected integration features
temp <- unique(unlist(hvgs_per_dataset))
overlap <- sapply(hvgs_per_dataset, function(x) temp %in% x)
pheatmap::pheatmap(t(overlap * 1), cluster_rows = FALSE,
                   color = c("grey90", "grey20"))


# Scale and PCA on each dataset using selected integration features
alldata.list <- lapply(alldata.list, function(x) {
    x <- RunPCA(x, features = hvgs_all, verbose = FALSE)
})

5. rpca-integration

alldata.int <- FindClusters(alldata.int, resolution = 0.5, verbose = FALSE)



wrap_plots(

    DimPlot(alldata.int, reduction = "pca_rpca", group.by = "orig.ident")+NoAxes()+ggtitle("PCA integrated"),
    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "orig.ident")+NoAxes()+ggtitle("UMAP integrated"),

    DimPlot(alldata.int, reduction = "pca_rpca", group.by = "rpca_snn_res.0.5")+NoAxes()+ggtitle("PCA_integrated"),
    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "rpca_snn_res.0.5")+NoAxes()+ggtitle("UMAP_integrated"),
    ncol = 2) + plot_layout(guides = "collect")

    DimPlot(alldata.int, reduction = "pca_rpca", group.by = "orig.ident")+NoAxes()+ggtitle("PCA integrated")

    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "orig.ident")+NoAxes()+ggtitle("UMAP integrated")

    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "orig.ident", label = T, label.box = T)+NoAxes()+ggtitle("UMAP integrated")

    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "predicted.celltype.l2")+NoAxes()+ggtitle("UMAP integrated")


    DimPlot(alldata.int, reduction = "pca_rpca", group.by = "rpca_snn_res.0.5")+NoAxes()+ggtitle("PCA integrated")

    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "rpca_snn_res.0.5")+NoAxes()+ggtitle("UMAP integrated")

    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "rpca_snn_res.0.5", label = T, label.box = T)+NoAxes()+ggtitle("UMAP integrated")

    DimPlot(alldata.int, reduction = "umap_rpca", group.by = "predicted.celltype.l2")+NoAxes()+ggtitle("UMAP integrated")

clean memory


# remove all objects that will not be used
rm(alldata, filtered_seurat,  alldata.anchors)

gc()
             used    (Mb) gc trigger    (Mb)   max used    (Mb)
Ncells   16126043   861.3   25929034  1384.8   25929034  1384.8
Vcells 3191280457 24347.6 7119864201 54320.3 7102197598 54185.5

Marker Gene Visualization



# Set marker genes specific to requested immune cell types
myfeatures <- c("CD19", "CD79A", "MS4A1", # B cells
                "CD14", "LYZ", "FCGR3A", # Monocytes
                "CSF1R", "CD68", # Macrophages
                "NKG7", "GNLY", "KIR3DL1", # NK cells
                "MKI67", # Proliferating NK cells
                "CD34", "KIT", # HSPCs
                "CD3E", "CCR7", # T cells
                "SELL", "CD45RO", # Tnaive, Tcm
                "CD44", "CD45RA") # Tem, Temra

# # Visualize marker genes for RPCA
# FeaturePlot(alldata.int, features = myfeatures, reduction = "pca_rpca", ncol = 4) + 
#   ggtitle("Marker Gene Expression - RPCA Integration") +
#   NoLegend()

# Visualize marker genes for CCA
FeaturePlot(alldata.int, features = myfeatures, reduction = "umap_rpca", ncol = 4) + 
  ggtitle("Marker Gene Expression - rpca Integration") +
  NoLegend()
Warning: Found the following features in more than one assay, excluding the default. We will not include these in the final data frame: CD19, CD79A, MS4A1, CD14, LYZ, FCGR3A, CSF1R, CD68, GNLY, KIR3DL1, KITWarning: Could not find CD34 in the default search locations, found in 'RNA' assay insteadWarning: Could not find CD45RO in the default search locations, found in 'ADT' assay insteadWarning: Could not find CD45RA in the default search locations, found in 'ADT' assay insteadWarning: The following requested variables were not found (10 out of 11 shown): CD19, CD79A, MS4A1, CD14, LYZ, FCGR3A, CSF1R, CD68, GNLY, KIR3DL1

6. CCA-integration


alldata.anchors <- FindIntegrationAnchors(object.list = alldata.list, dims = 1:20, reduction = "cca", anchor.features = hvgs_all)

alldata.int <- IntegrateData(anchorset = alldata.anchors, dims = 1:20, new.assay.name = "CCA")

names(alldata.int@assays)

alldata.int@active.assay

DefaultAssay(alldata.int) <- "CCA"

#Run Dimensionality reduction on integrated space
alldata.int <- ScaleData(alldata.int, verbose = TRUE)
alldata.int <- RunPCA(alldata.int, features = hvgs_all, reduction.name = "pca_CCA", do.print = TRUE, pcs.print = 1:5, genes.print = 15, verbose = FALSE)
alldata.int <- RunUMAP(alldata.int, reduction = "pca_CCA", reduction.name = "umap_CCA", dims = 1:20, verbose = FALSE)
alldata.int <- RunTSNE(alldata.int, reduction = "pca_CCA",reduction.name = "tsne_CCA",dims = 1:20, verbose = FALSE)
alldata.int <- FindNeighbors(alldata.int, reduction = "pca_CCA", dims = 1:20, verbose = FALSE)
alldata.int <- FindClusters(alldata.int, resolution = 1.2, verbose = FALSE)



wrap_plots(

    DimPlot(alldata.int, reduction = "pca_CCA", group.by = "orig.ident")+NoAxes()+ggtitle("PCA integrated"),
    DimPlot(alldata.int, reduction = "tsne_CCA", group.by = "orig.ident")+NoAxes()+ggtitle("tSNE integrated"),
    DimPlot(alldata.int, reduction = "umap_CCA", group.by = "orig.ident")+NoAxes()+ggtitle("UMAP integrated"),

    DimPlot(alldata.int, reduction = "pca_CCA", group.by = "CCA_snn_res.1.2")+NoAxes()+ggtitle("PCA integrated"),
    DimPlot(alldata.int, reduction = "tsne_CCA", group.by = "CCA_snn_res.1.2")+NoAxes()+ggtitle("tSNE integrated"),
    DimPlot(alldata.int, reduction = "umap_CCA", group.by = "CCA_snn_res.1.2")+NoAxes()+ggtitle("UMAP integrated"),
    ncol = 3) + plot_layout(guides = "collect")

    DimPlot(alldata.int, reduction = "pca_CCA", group.by = "orig.ident")+NoAxes()+ggtitle("PCA integrated")
    DimPlot(alldata.int, reduction = "tsne_CCA", group.by = "orig.ident")+NoAxes()+ggtitle("tSNE integrated")
    DimPlot(alldata.int, reduction = "umap_CCA", group.by = "orig.ident")+NoAxes()+ggtitle("UMAP integrated")
    DimPlot(alldata.int, reduction = "umap_CCA", group.by = "predicted.celltype.l2")+NoAxes()+ggtitle("UMAP integrated")
    
    DimPlot(alldata.int, reduction = "pca_CCA", group.by = "CCA_snn_res.1.2")+NoAxes()+ggtitle("PCA integrated")
    DimPlot(alldata.int, reduction = "tsne_CCA", group.by = "CCA_snn_res.1.2")+NoAxes()+ggtitle("tSNE integrated")
    DimPlot(alldata.int, reduction = "umap_CCA", group.by = "CCA_snn_res.1.2")+NoAxes()+ggtitle("UMAP integrated")
    DimPlot(alldata.int, reduction = "umap_CCA", group.by = "predicted.celltype.l2")+NoAxes()+ggtitle("UMAP integrated")

clean memory

# remove all objects that will not be used
rm(alldata.anchors)

gc()

7. Harmony-integration

 wrap_plots(

    DimPlot(alldata.int, reduction = "pca_harmony", group.by = "orig.ident")+NoAxes()+ggtitle("PCA integrated"),
    DimPlot(alldata.int, reduction = "tsne_harmony", group.by = "orig.ident")+NoAxes()+ggtitle("tSNE integrated"),
    DimPlot(alldata.int, reduction = "umap_harmony", group.by = "orig.ident")+NoAxes()+ggtitle("UMAP integrated"),

    DimPlot(alldata.int, reduction = "pca_harmony", group.by = "RNA_snn_res.1.2")+NoAxes()+ggtitle("PCA integrated"),
    DimPlot(alldata.int, reduction = "tsne_harmony", group.by = "RNA_snn_res.1.2")+NoAxes()+ggtitle("tSNE integrated"),
    DimPlot(alldata.int, reduction = "umap_harmony", group.by = "RNA_snn_res.1.2")+NoAxes()+ggtitle("UMAP integrated"),
    ncol = 3) + plot_layout(guides = "collect")
 
    DimPlot(alldata.int, reduction = "pca_harmony", group.by = "orig.ident")+NoAxes()+ggtitle("PCA integrated")

    DimPlot(alldata.int, reduction = "tsne_harmony", group.by = "orig.ident")+NoAxes()+ggtitle("tSNE integrated")

    DimPlot(alldata.int, reduction = "umap_harmony", group.by = "orig.ident", label = T, label.box = T)+NoAxes()+ggtitle("UMAP integrated")

    DimPlot(alldata.int, reduction = "umap_harmony", group.by = "predicted.celltype.l2")+NoAxes()+ggtitle("UMAP integrated")

    
    DimPlot(alldata.int, reduction = "pca_harmony", group.by = "RNA_snn_res.1.2")+NoAxes()+ggtitle("PCA integrated")

    DimPlot(alldata.int, reduction = "tsne_harmony", group.by = "RNA_snn_res.1.2")+NoAxes()+ggtitle("tSNE integrated")

    DimPlot(alldata.int, reduction = "umap_harmony", group.by = "RNA_snn_res.1.2", label = T, label.box = T)+NoAxes()+ggtitle("UMAP integrated")

    DimPlot(alldata.int, reduction = "umap_harmony", group.by = "predicted.celltype.l2")+NoAxes()+ggtitle("UMAP integrated")

NA

8. Marker Gene Visualization



# Set marker genes specific to requested immune cell types
myfeatures <- c("CD19", "CD79A", "MS4A1", # B cells
                "CD14", "LYZ", "FCGR3A", # Monocytes
                "CSF1R", "CD68", # Macrophages
                "NKG7", "GNLY", "KIR3DL1", # NK cells
                "MKI67", # Proliferating NK cells
                "CD34", "KIT", # HSPCs
                "CD3E", "CCR7", # T cells
                "SELL", "CD45RO", # Tnaive, Tcm
                "CD44", "CD45RA") # Tem, Temra

# # Visualize marker genes for RPCA
# FeaturePlot(alldata.int, features = myfeatures, reduction = "pca_rpca", ncol = 4) + 
#   ggtitle("Marker Gene Expression - RPCA Integration") +
#   NoLegend()

# Visualize marker genes for CCA
FeaturePlot(alldata.int, features = myfeatures, reduction = "umap_rpca", ncol = 4) + 
  ggtitle("Marker Gene Expression - CCA Integration") +
  NoLegend()
Warning: Could not find CD45RO in the default search locations, found in 'ADT' assay insteadWarning: Could not find CD45RA in the default search locations, found in 'ADT' assay instead

9. Save the Seurat object as an Robj file


# save(All_samples_Merged_Harmony_Integrated, file = "All_samples_Merged_Harmony_Integrated.Robj")
LS0tCnRpdGxlOiAiUlBDQS1DQ0EtSGFybW9ueSBJbnRlZ3JhdGlvbiBvZiBQQk1DMTB4IHdpdGggU0NUIG9uIHNhbXBsZXMgcGFydDEiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgI3JtZGZvcm1hdHM6OnJlYWR0aGVkb3duCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfY29sbGFwc2VkOiB0cnVlCi0tLQoKIyAxLiBsb2FkIGxpYnJhcmllcwpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQpsaWJyYXJ5KFNldXJhdERhdGEpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGhhcm1vbnkpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShyZXRpY3VsYXRlKQpsaWJyYXJ5KEF6aW11dGgpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoUnRzbmUpCmxpYnJhcnkoaGFybW9ueSkKCmBgYAoKCgoKIyAyLiBMb2FkIFNldXJhdCBPYmplY3QgCmBgYHtyIGxvYWRfc2V1cmF0fQoKI0xvYWQgU2V1cmF0IE9iamVjdCBtZXJnZWQgZnJvbSBjZWxsIGxpbmVzIGFuZCBhIGNvbnRyb2woUEJNQykgYWZ0ZXIgZmlsdHJhdGlvbgpsb2FkKCIwLVJfT2JqZWN0cy9TU19DRDRfVGNlbGxzX0F6aW11dGhfQW5ub3RhdGVkX1BCTUMxMHhfZmluYWxfZm9yX1NDVF9hbmRfSW50ZWdyYXRpb24ucm9iaiIpCgpBbGxfc2FtcGxlc19NZXJnZWQgPC0gZmlsdGVyZWRfc2V1cmF0CgpBbGxfc2FtcGxlc19NZXJnZWQKCmBgYAojIyBTdW1tYXJpemluZyBTZXVyYXQgT2JqZWN0CmBgYHtyIHN1bW1hcnksIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKIyBMb2FkIG5lY2Vzc2FyeSBsaWJyYXJpZXMKbGlicmFyeShTZXVyYXQpCgojIERpc3BsYXkgYmFzaWMgbWV0YWRhdGEgc3VtbWFyeQpoZWFkKEFsbF9zYW1wbGVzX01lcmdlZEBtZXRhLmRhdGEpCgojIENoZWNrIGlmIGNvbHVtbnMgc3VjaCBhcyBgb3JpZy5pZGVudGAsIGBuQ291bnRfUk5BYCwgYG5GZWF0dXJlX1JOQWAsIGBuVU1JYCwgYG5nZW5lYCwgYW5kIGFueSBvdGhlciBuZWNlc3NhcnkgY29sdW1ucyBleGlzdApyZXF1aXJlZF9jb2x1bW5zIDwtIGMoIm9yaWcuaWRlbnQiLCAibkNvdW50X1JOQSIsICJuRmVhdHVyZV9STkEiLCAiblVNSSIsICJuZ2VuZSIpCm1pc3NpbmdfY29sdW1ucyA8LSBzZXRkaWZmKHJlcXVpcmVkX2NvbHVtbnMsIGNvbG5hbWVzKEFsbF9zYW1wbGVzX01lcmdlZEBtZXRhLmRhdGEpKQoKaWYgKGxlbmd0aChtaXNzaW5nX2NvbHVtbnMpID4gMCkgewogICAgY2F0KCJNaXNzaW5nIGNvbHVtbnM6IiwgcGFzdGUobWlzc2luZ19jb2x1bW5zLCBjb2xsYXBzZSA9ICIsICIpLCAiXG4iKQp9IGVsc2UgewogICAgY2F0KCJBbGwgcmVxdWlyZWQgY29sdW1ucyBhcmUgcHJlc2VudC5cbiIpCn0KCiMgQ2hlY2sgY2VsbCBjb3VudHMgYW5kIGZlYXR1cmVzCmNhdCgiTnVtYmVyIG9mIGNlbGxzOiIsIG5jb2woQWxsX3NhbXBsZXNfTWVyZ2VkKSwgIlxuIikKY2F0KCJOdW1iZXIgb2YgZmVhdHVyZXM6IiwgbnJvdyhBbGxfc2FtcGxlc19NZXJnZWQpLCAiXG4iKQoKIyBWZXJpZnkgdGhhdCBlYWNoIGBvcmlnLmlkZW50YCBsYWJlbCBoYXMgdGhlIGNvcnJlY3QgbnVtYmVyIG9mIGNlbGxzCmNhdCgiQ2VsbCBjb3VudHMgcGVyIGdyb3VwOlxuIikKcHJpbnQodGFibGUoQWxsX3NhbXBsZXNfTWVyZ2VkJG9yaWcuaWRlbnQpKQoKIyBDaGVjayB0aGF0IHRoZSBjZWxsIElEcyBhcmUgdW5pcXVlICh3aGljaCBlbnN1cmVzIG5vIGlzc3VlcyBmcm9tIG1lcmdpbmcpCmlmIChhbnkoZHVwbGljYXRlZChjb2xuYW1lcyhBbGxfc2FtcGxlc19NZXJnZWQpKSkpIHsKICAgIGNhdCgiV2FybmluZzogVGhlcmUgYXJlIGR1cGxpY2F0ZWQgY2VsbCBJRHMuXG4iKQp9IGVsc2UgewogICAgY2F0KCJDZWxsIElEcyBhcmUgdW5pcXVlLlxuIikKfQoKIyBDaGVjayB0aGUgYXNzYXkgY29uc2lzdGVuY3kgZm9yIFJOQQpEZWZhdWx0QXNzYXkoQWxsX3NhbXBsZXNfTWVyZ2VkKSA8LSAiUk5BIgoKIyBDaGVjayBkaW1lbnNpb25zIG9mIHRoZSBSTkEgY291bnRzIGxheWVyIHVzaW5nIHRoZSBuZXcgbWV0aG9kCmNhdCgiRGltZW5zaW9ucyBvZiB0aGUgUk5BIGNvdW50cyBsYXllcjoiLCBkaW0oR2V0QXNzYXlEYXRhKEFsbF9zYW1wbGVzX01lcmdlZCwgbGF5ZXIgPSAiY291bnRzIikpLCAiXG4iKQpjYXQoIkRpbWVuc2lvbnMgb2YgdGhlIFJOQSBkYXRhIGxheWVyOiIsIGRpbShHZXRBc3NheURhdGEoQWxsX3NhbXBsZXNfTWVyZ2VkLCBsYXllciA9ICJkYXRhIikpLCAiXG4iKQoKIyBDaGVjayB0aGUgQURUIGFzc2F5IChvcHRpb25hbCkKaWYgKCJBRFQiICVpbiUgbmFtZXMoQWxsX3NhbXBsZXNfTWVyZ2VkQGFzc2F5cykpIHsKICAgIGNhdCgiQURUIGFzc2F5IGlzIHByZXNlbnQuXG4iKQogICAgY2F0KCJEaW1lbnNpb25zIG9mIHRoZSBBRFQgY291bnRzIGxheWVyOiIsIGRpbShHZXRBc3NheURhdGEoQWxsX3NhbXBsZXNfTWVyZ2VkLCBhc3NheSA9ICJBRFQiLCBsYXllciA9ICJjb3VudHMiKSksICJcbiIpCn0gZWxzZSB7CiAgICBjYXQoIkFEVCBhc3NheSBpcyBub3QgcHJlc2VudC5cbiIpCn0KCgpgYGAKIyAzLiBRQwpgYGB7ciBRQywgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgojIFJlbW92ZSB0aGUgcGVyY2VudC5taXRvIGNvbHVtbgpBbGxfc2FtcGxlc19NZXJnZWQkcGVyY2VudC5taXRvIDwtIE5VTEwKCgojIFNldCBpZGVudGl0eSBjbGFzc2VzIHRvIGFuIGV4aXN0aW5nIGNvbHVtbiBpbiBtZXRhIGRhdGEKSWRlbnRzKG9iamVjdCA9IEFsbF9zYW1wbGVzX01lcmdlZCkgPC0gImNlbGxfbGluZSIKCkFsbF9zYW1wbGVzX01lcmdlZFtbInBlcmNlbnQucmIiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoQWxsX3NhbXBsZXNfTWVyZ2VkLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gIl5SUFtTTF0iKQojIENvbnZlcnQgJ3BlcmNlbnQubXQnIHRvIG51bWVyaWMsIHJlcGxhY2luZyAiTmFOIiB3aXRoIDAKQWxsX3NhbXBsZXNfTWVyZ2VkJHBlcmNlbnQucmIgPC0gcmVwbGFjZShhcy5udW1lcmljKEFsbF9zYW1wbGVzX01lcmdlZCRwZXJjZW50LnJiKSwgaXMubmEoQWxsX3NhbXBsZXNfTWVyZ2VkJHBlcmNlbnQucmIpLCAwKQoKCgojIFRoZSBbWyBvcGVyYXRvciBjYW4gYWRkIGNvbHVtbnMgdG8gb2JqZWN0IG1ldGFkYXRhLiBUaGlzIGlzIGEgZ3JlYXQgcGxhY2UgdG8gc3Rhc2ggUUMgc3RhdHMKQWxsX3NhbXBsZXNfTWVyZ2VkW1sicGVyY2VudC5tdCJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChBbGxfc2FtcGxlc19NZXJnZWQsIHBhdHRlcm4gPSAiXk1ULSIpCgojIENvbnZlcnQgJ3BlcmNlbnQubXQnIHRvIG51bWVyaWMsIHJlcGxhY2luZyAiTmFOIiB3aXRoIDAKQWxsX3NhbXBsZXNfTWVyZ2VkJHBlcmNlbnQubXQgPC0gcmVwbGFjZShhcy5udW1lcmljKEFsbF9zYW1wbGVzX01lcmdlZCRwZXJjZW50Lm10KSwgaXMubmEoQWxsX3NhbXBsZXNfTWVyZ2VkJHBlcmNlbnQubXQpLCAwKQoKCgoKClZsblBsb3QoQWxsX3NhbXBsZXNfTWVyZ2VkLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuQ291bnRfUk5BIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBlcmNlbnQubXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwZXJjZW50LnJiIiksIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDQsIHB0LnNpemUgPSAwLjEpICYgCiAgICAgICAgICAgICAgdGhlbWUocGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplPTEwKSkKCkZlYXR1cmVTY2F0dGVyKEFsbF9zYW1wbGVzX01lcmdlZCwgZmVhdHVyZTEgPSAicGVyY2VudC5tdCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAicGVyY2VudC5yYiIpCgpWbG5QbG90KEFsbF9zYW1wbGVzX01lcmdlZCwgZmVhdHVyZXMgPSBjKCJuRmVhdHVyZV9STkEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBlcmNlbnQubXQiKSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmNvbCA9IDMpCgpGZWF0dXJlU2NhdHRlcihBbGxfc2FtcGxlc19NZXJnZWQsIAogICAgICAgICAgICAgICBmZWF0dXJlMSA9ICJwZXJjZW50Lm10IiwgCiAgICAgICAgICAgICAgIGZlYXR1cmUyID0gInBlcmNlbnQucmIiKSArCiAgICAgICAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykKCkZlYXR1cmVTY2F0dGVyKEFsbF9zYW1wbGVzX01lcmdlZCwgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikgKwogICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpCgpgYGAKCiMjRmVhdHVyZVNjYXR0ZXIgaXMgdHlwaWNhbGx5IHVzZWQgdG8gdmlzdWFsaXplIGZlYXR1cmUtZmVhdHVyZSByZWxhdGlvbnNoaXBzCiMjZm9yIGFueXRoaW5nIGNhbGN1bGF0ZWQgYnkgdGhlIG9iamVjdCwgCiMjaS5lLiBjb2x1bW5zIGluIG9iamVjdCBtZXRhZGF0YSwgUEMgc2NvcmVzIGV0Yy4KCmBgYHtyIEZDLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCkZlYXR1cmVTY2F0dGVyKEFsbF9zYW1wbGVzX01lcmdlZCwgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAicGVyY2VudC5tdCIpKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpCgpGZWF0dXJlU2NhdHRlcihBbGxfc2FtcGxlc19NZXJnZWQsIAogICAgICAgICAgICAgICBmZWF0dXJlMSA9ICJuQ291bnRfUk5BIiwgCiAgICAgICAgICAgICAgIGZlYXR1cmUyID0gIm5GZWF0dXJlX1JOQSIpKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScpCgpgYGAKCgojIDQuIERhdGEgUFJFUEFSQVRJT04KYGBge3IgZGF0YSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9Cm9wdGlvbnMoZnV0dXJlLmdsb2JhbHMubWF4U2l6ZSA9IDgwMDAgKiAxMDI0XjIpICAjIFNldCB0byA4MDAwIE1pQiAoYWJvdXQgOCBHQikKIyBEYXRhIFByZXBhcmF0aW9uIGZvciBTZXVyYXQgdjUKYWxsZGF0YSA8LSBBbGxfc2FtcGxlc19NZXJnZWQKCiMgU3BsaXQgdGhlIG9iamVjdCBieSAnb3JpZy5pZGVudCcgZm9yIGluZGl2aWR1YWwgZGF0YXNldCBwcm9jZXNzaW5nCmFsbGRhdGEubGlzdCA8LSBTcGxpdE9iamVjdChhbGxkYXRhLCBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IikKCiMgTm9ybWFsaXplIGFuZCBpZGVudGlmeSB2YXJpYWJsZSBmZWF0dXJlcyBmb3IgZWFjaCBkYXRhc2V0IGluIHRoZSBsaXN0CmZvciAoaSBpbiAxOmxlbmd0aChhbGxkYXRhLmxpc3QpKSB7CiAgICBhbGxkYXRhLmxpc3RbW2ldXSA8LSBTQ1RyYW5zZm9ybShhbGxkYXRhLmxpc3RbW2ldXSwgdmFycy50by5yZWdyZXNzID0gYygicGVyY2VudC5yYiIsInBlcmNlbnQubXQiLCAibkNvdW50X1JOQSIpLCB2ZXJib3NlID0gRkFMU0UpCiAgICBhbGxkYXRhLmxpc3RbW2ldXSA8LSBGaW5kVmFyaWFibGVGZWF0dXJlcyhhbGxkYXRhLmxpc3RbW2ldXSwgc2VsZWN0aW9uLm1ldGhvZCA9ICJ2c3QiLCBuZmVhdHVyZXMgPSAzMDAwLCB2ZXJib3NlID0gRkFMU0UpCn0KCiMgR2V0IHRoZSB2YXJpYWJsZSBnZW5lcyBmb3IgZWFjaCBkYXRhc2V0Cmh2Z3NfcGVyX2RhdGFzZXQgPC0gbGFwcGx5KGFsbGRhdGEubGlzdCwgVmFyaWFibGVGZWF0dXJlcykKCiMgSW5jbHVkZSB0aGUgdmFyaWFibGUgZ2VuZXMgc2VsZWN0ZWQgb24gdGhlIHdob2xlIGRhdGFzZXQKaHZnc19wZXJfZGF0YXNldCRhbGwgPC0gVmFyaWFibGVGZWF0dXJlcyhhbGxkYXRhKQoKIyBDcmVhdGUgYSBoZWF0bWFwIHRvIHNob3cgb3ZlcmxhcCBhY3Jvc3MgZGF0YXNldHMKdGVtcCA8LSB1bmlxdWUodW5saXN0KGh2Z3NfcGVyX2RhdGFzZXQpKQpvdmVybGFwIDwtIHNhcHBseShodmdzX3Blcl9kYXRhc2V0LCBmdW5jdGlvbih4KSB0ZW1wICVpbiUgeCkKcGhlYXRtYXA6OnBoZWF0bWFwKHQob3ZlcmxhcCAqIDEpLCBjbHVzdGVyX3Jvd3MgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgIGNvbG9yID0gYygiZ3JleTkwIiwgImdyZXkyMCIpKQoKIyBTZWxlY3QgaW50ZWdyYXRpb24gZmVhdHVyZXMgYWNyb3NzIGRhdGFzZXRzCmh2Z3NfYWxsIDwtIFNlbGVjdEludGVncmF0aW9uRmVhdHVyZXMoYWxsZGF0YS5saXN0KQojIEV4Y2x1ZGUgc3BlY2lmaWMgZ2VuZXMKaHZnc19hbGwgPC0gaHZnc19hbGxbIWdyZXBsKCJeSExBLXxeWElTVHxeVFJCVnxeVFJBViIsIGh2Z3NfYWxsKV0KCmh2Z3NfcGVyX2RhdGFzZXQkYWxsX3JhbmtzIDwtIGh2Z3NfYWxsCgojIEdlbmVyYXRlIHRoZSBvdmVybGFwIGhlYXRtYXAgZm9yIGFsbCBzZWxlY3RlZCBpbnRlZ3JhdGlvbiBmZWF0dXJlcwp0ZW1wIDwtIHVuaXF1ZSh1bmxpc3QoaHZnc19wZXJfZGF0YXNldCkpCm92ZXJsYXAgPC0gc2FwcGx5KGh2Z3NfcGVyX2RhdGFzZXQsIGZ1bmN0aW9uKHgpIHRlbXAgJWluJSB4KQpwaGVhdG1hcDo6cGhlYXRtYXAodChvdmVybGFwICogMSksIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgY29sb3IgPSBjKCJncmV5OTAiLCAiZ3JleTIwIikpCgojIFNjYWxlIGFuZCBQQ0Egb24gZWFjaCBkYXRhc2V0IHVzaW5nIHNlbGVjdGVkIGludGVncmF0aW9uIGZlYXR1cmVzCmFsbGRhdGEubGlzdCA8LSBsYXBwbHkoYWxsZGF0YS5saXN0LCBmdW5jdGlvbih4KSB7CiAgICB4IDwtIFJ1blBDQSh4LCBmZWF0dXJlcyA9IGh2Z3NfYWxsLCB2ZXJib3NlID0gRkFMU0UpCn0pCgpgYGAKCgojIDUuIHJwY2EtaW50ZWdyYXRpb24KYGBge3IgaW50ZWdyYXRpb24tcnBjYSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgphbGxkYXRhLmFuY2hvcnMgPC0gRmluZEludGVncmF0aW9uQW5jaG9ycyhvYmplY3QubGlzdCA9IGFsbGRhdGEubGlzdCwgZGltcyA9IDE6MjAsIHJlZHVjdGlvbiA9ICJycGNhIiwgYW5jaG9yLmZlYXR1cmVzID0gaHZnc19hbGwpCgphbGxkYXRhLmludCA8LSBJbnRlZ3JhdGVEYXRhKGFuY2hvcnNldCA9IGFsbGRhdGEuYW5jaG9ycywgZGltcyA9IDE6MjAsIG5ldy5hc3NheS5uYW1lID0gInJwY2EiKQoKbmFtZXMoYWxsZGF0YS5pbnRAYXNzYXlzKQoKYWxsZGF0YS5pbnRAYWN0aXZlLmFzc2F5CgpEZWZhdWx0QXNzYXkoYWxsZGF0YS5pbnQpIDwtICJycGNhIgoKI1J1biBEaW1lbnNpb25hbGl0eSByZWR1Y3Rpb24gb24gaW50ZWdyYXRlZCBzcGFjZQphbGxkYXRhLmludCA8LSBTY2FsZURhdGEoYWxsZGF0YS5pbnQsIHZlcmJvc2UgPSBGQUxTRSkKYWxsZGF0YS5pbnQgPC0gUnVuUENBKGFsbGRhdGEuaW50LCBmZWF0dXJlcyA9IGh2Z3NfYWxsLCByZWR1Y3Rpb24ubmFtZSA9ICJwY2FfcnBjYSIsIGRvLnByaW50ID0gVFJVRSwgcGNzLnByaW50ID0gMTo1LCBnZW5lcy5wcmludCA9IDE1LCB2ZXJib3NlID0gRkFMU0UpCmFsbGRhdGEuaW50IDwtIFJ1blVNQVAoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfcnBjYSIsIHJlZHVjdGlvbi5uYW1lID0gInVtYXBfcnBjYSIsIGRpbXMgPSAxOjIwLCB2ZXJib3NlID0gRkFMU0UpCgphbGxkYXRhLmludCA8LSBGaW5kTmVpZ2hib3JzKGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAicGNhX3JwY2EiLCBkaW1zID0gMToyMCwgdmVyYm9zZSA9IEZBTFNFKQphbGxkYXRhLmludCA8LSBGaW5kQ2x1c3RlcnMoYWxsZGF0YS5pbnQsIHJlc29sdXRpb24gPSAwLjUsIHZlcmJvc2UgPSBGQUxTRSkKCgoKd3JhcF9wbG90cygKCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAicGNhX3JwY2EiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikrTm9BeGVzKCkrZ2d0aXRsZSgiUENBIGludGVncmF0ZWQiKSwKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX3JwY2EiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikrTm9BeGVzKCkrZ2d0aXRsZSgiVU1BUCBpbnRlZ3JhdGVkIiksCgogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInBjYV9ycGNhIiwgZ3JvdXAuYnkgPSAicnBjYV9zbm5fcmVzLjAuNSIpK05vQXhlcygpK2dndGl0bGUoIlBDQV9pbnRlZ3JhdGVkIiksCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidW1hcF9ycGNhIiwgZ3JvdXAuYnkgPSAicnBjYV9zbm5fcmVzLjAuNSIpK05vQXhlcygpK2dndGl0bGUoIlVNQVBfaW50ZWdyYXRlZCIpLAogICAgbmNvbCA9IDIpICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKQoKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfcnBjYSIsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKStOb0F4ZXMoKStnZ3RpdGxlKCJQQ0EgaW50ZWdyYXRlZCIpCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidW1hcF9ycGNhIiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpK05vQXhlcygpK2dndGl0bGUoIlVNQVAgaW50ZWdyYXRlZCIpCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidW1hcF9ycGNhIiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkrTm9BeGVzKCkrZ2d0aXRsZSgiVU1BUCBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX3JwY2EiLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKQoKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfcnBjYSIsIGdyb3VwLmJ5ID0gInJwY2Ffc25uX3Jlcy4wLjUiKStOb0F4ZXMoKStnZ3RpdGxlKCJQQ0EgaW50ZWdyYXRlZCIpCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidW1hcF9ycGNhIiwgZ3JvdXAuYnkgPSAicnBjYV9zbm5fcmVzLjAuNSIpK05vQXhlcygpK2dndGl0bGUoIlVNQVAgaW50ZWdyYXRlZCIpCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidW1hcF9ycGNhIiwgZ3JvdXAuYnkgPSAicnBjYV9zbm5fcmVzLjAuNSIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkrTm9BeGVzKCkrZ2d0aXRsZSgiVU1BUCBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX3JwY2EiLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKQoKYGBgCgojIGNsZWFuIG1lbW9yeQpgYGB7ciBjbGVhbk1lbW9yeTF9CgojIHJlbW92ZSBhbGwgb2JqZWN0cyB0aGF0IHdpbGwgbm90IGJlIHVzZWQKcm0oYWxsZGF0YSwgZmlsdGVyZWRfc2V1cmF0LCAgYWxsZGF0YS5hbmNob3JzKQoKZ2MoKQpgYGAKCiMjIE1hcmtlciBHZW5lIFZpc3VhbGl6YXRpb24KYGBge3IgZmVhdHVyZXBsb3QtcnBjYSwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9CgoKIyBTZXQgbWFya2VyIGdlbmVzIHNwZWNpZmljIHRvIHJlcXVlc3RlZCBpbW11bmUgY2VsbCB0eXBlcwpteWZlYXR1cmVzIDwtIGMoIkNEMTkiLCAiQ0Q3OUEiLCAiTVM0QTEiLCAjIEIgY2VsbHMKICAgICAgICAgICAgICAgICJDRDE0IiwgIkxZWiIsICJGQ0dSM0EiLCAjIE1vbm9jeXRlcwogICAgICAgICAgICAgICAgIkNTRjFSIiwgIkNENjgiLCAjIE1hY3JvcGhhZ2VzCiAgICAgICAgICAgICAgICAiTktHNyIsICJHTkxZIiwgIktJUjNETDEiLCAjIE5LIGNlbGxzCiAgICAgICAgICAgICAgICAiTUtJNjciLCAjIFByb2xpZmVyYXRpbmcgTksgY2VsbHMKICAgICAgICAgICAgICAgICJDRDM0IiwgIktJVCIsICMgSFNQQ3MKICAgICAgICAgICAgICAgICJDRDNFIiwgIkNDUjciLCAjIFQgY2VsbHMKICAgICAgICAgICAgICAgICJTRUxMIiwgIkNENDVSTyIsICMgVG5haXZlLCBUY20KICAgICAgICAgICAgICAgICJDRDQ0IiwgIkNENDVSQSIpICMgVGVtLCBUZW1yYQoKIyAjIFZpc3VhbGl6ZSBtYXJrZXIgZ2VuZXMgZm9yIFJQQ0EKIyBGZWF0dXJlUGxvdChhbGxkYXRhLmludCwgZmVhdHVyZXMgPSBteWZlYXR1cmVzLCByZWR1Y3Rpb24gPSAicGNhX3JwY2EiLCBuY29sID0gNCkgKyAKIyAgIGdndGl0bGUoIk1hcmtlciBHZW5lIEV4cHJlc3Npb24gLSBSUENBIEludGVncmF0aW9uIikgKwojICAgTm9MZWdlbmQoKQoKIyBWaXN1YWxpemUgbWFya2VyIGdlbmVzIGZvciBDQ0EKRmVhdHVyZVBsb3QoYWxsZGF0YS5pbnQsIGZlYXR1cmVzID0gbXlmZWF0dXJlcywgcmVkdWN0aW9uID0gInVtYXBfcnBjYSIsIG5jb2wgPSA0KSArIAogIGdndGl0bGUoIk1hcmtlciBHZW5lIEV4cHJlc3Npb24gLSBycGNhIEludGVncmF0aW9uIikgKwogIE5vTGVnZW5kKCkKCgoKYGBgCgoKIyA2LiBDQ0EtaW50ZWdyYXRpb24KYGBge3IgaW50ZWdyYXRpb24tQ0NBLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCmFsbGRhdGEuYW5jaG9ycyA8LSBGaW5kSW50ZWdyYXRpb25BbmNob3JzKG9iamVjdC5saXN0ID0gYWxsZGF0YS5saXN0LCBkaW1zID0gMToyMCwgcmVkdWN0aW9uID0gImNjYSIsIGFuY2hvci5mZWF0dXJlcyA9IGh2Z3NfYWxsKQoKYWxsZGF0YS5pbnQgPC0gSW50ZWdyYXRlRGF0YShhbmNob3JzZXQgPSBhbGxkYXRhLmFuY2hvcnMsIGRpbXMgPSAxOjIwLCBuZXcuYXNzYXkubmFtZSA9ICJDQ0EiKQoKbmFtZXMoYWxsZGF0YS5pbnRAYXNzYXlzKQoKYWxsZGF0YS5pbnRAYWN0aXZlLmFzc2F5CgpEZWZhdWx0QXNzYXkoYWxsZGF0YS5pbnQpIDwtICJDQ0EiCgojUnVuIERpbWVuc2lvbmFsaXR5IHJlZHVjdGlvbiBvbiBpbnRlZ3JhdGVkIHNwYWNlCmFsbGRhdGEuaW50IDwtIFNjYWxlRGF0YShhbGxkYXRhLmludCwgdmVyYm9zZSA9IFRSVUUpCmFsbGRhdGEuaW50IDwtIFJ1blBDQShhbGxkYXRhLmludCwgZmVhdHVyZXMgPSBodmdzX2FsbCwgcmVkdWN0aW9uLm5hbWUgPSAicGNhX0NDQSIsIGRvLnByaW50ID0gVFJVRSwgcGNzLnByaW50ID0gMTo1LCBnZW5lcy5wcmludCA9IDE1LCB2ZXJib3NlID0gRkFMU0UpCmFsbGRhdGEuaW50IDwtIFJ1blVNQVAoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfQ0NBIiwgcmVkdWN0aW9uLm5hbWUgPSAidW1hcF9DQ0EiLCBkaW1zID0gMToyMCwgdmVyYm9zZSA9IEZBTFNFKQphbGxkYXRhLmludCA8LSBSdW5UU05FKGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAicGNhX0NDQSIscmVkdWN0aW9uLm5hbWUgPSAidHNuZV9DQ0EiLGRpbXMgPSAxOjIwLCB2ZXJib3NlID0gRkFMU0UpCmFsbGRhdGEuaW50IDwtIEZpbmROZWlnaGJvcnMoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfQ0NBIiwgZGltcyA9IDE6MjAsIHZlcmJvc2UgPSBGQUxTRSkKYWxsZGF0YS5pbnQgPC0gRmluZENsdXN0ZXJzKGFsbGRhdGEuaW50LCByZXNvbHV0aW9uID0gMS4yLCB2ZXJib3NlID0gRkFMU0UpCgoKCndyYXBfcGxvdHMoCgogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInBjYV9DQ0EiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikrTm9BeGVzKCkrZ2d0aXRsZSgiUENBIGludGVncmF0ZWQiKSwKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ0c25lX0NDQSIsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKStOb0F4ZXMoKStnZ3RpdGxlKCJ0U05FIGludGVncmF0ZWQiKSwKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX0NDQSIsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKSwKCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAicGNhX0NDQSIsIGdyb3VwLmJ5ID0gIkNDQV9zbm5fcmVzLjEuMiIpK05vQXhlcygpK2dndGl0bGUoIlBDQSBpbnRlZ3JhdGVkIiksCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidHNuZV9DQ0EiLCBncm91cC5ieSA9ICJDQ0Ffc25uX3Jlcy4xLjIiKStOb0F4ZXMoKStnZ3RpdGxlKCJ0U05FIGludGVncmF0ZWQiKSwKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX0NDQSIsIGdyb3VwLmJ5ID0gIkNDQV9zbm5fcmVzLjEuMiIpK05vQXhlcygpK2dndGl0bGUoIlVNQVAgaW50ZWdyYXRlZCIpLAogICAgbmNvbCA9IDMpICsgcGxvdF9sYXlvdXQoZ3VpZGVzID0gImNvbGxlY3QiKQoKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfQ0NBIiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpK05vQXhlcygpK2dndGl0bGUoIlBDQSBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ0c25lX0NDQSIsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKStOb0F4ZXMoKStnZ3RpdGxlKCJ0U05FIGludGVncmF0ZWQiKQogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInVtYXBfQ0NBIiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpK05vQXhlcygpK2dndGl0bGUoIlVNQVAgaW50ZWdyYXRlZCIpCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidW1hcF9DQ0EiLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKQogICAgCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAicGNhX0NDQSIsIGdyb3VwLmJ5ID0gIkNDQV9zbm5fcmVzLjEuMiIpK05vQXhlcygpK2dndGl0bGUoIlBDQSBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ0c25lX0NDQSIsIGdyb3VwLmJ5ID0gIkNDQV9zbm5fcmVzLjEuMiIpK05vQXhlcygpK2dndGl0bGUoInRTTkUgaW50ZWdyYXRlZCIpCiAgICBEaW1QbG90KGFsbGRhdGEuaW50LCByZWR1Y3Rpb24gPSAidW1hcF9DQ0EiLCBncm91cC5ieSA9ICJDQ0Ffc25uX3Jlcy4xLjIiKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKQogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInVtYXBfQ0NBIiwgZ3JvdXAuYnkgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIikrTm9BeGVzKCkrZ2d0aXRsZSgiVU1BUCBpbnRlZ3JhdGVkIikKYGBgCgoKIyBjbGVhbiBtZW1vcnkKYGBge3IgY2xlYW5NZW1vcnkyfQojIHJlbW92ZSBhbGwgb2JqZWN0cyB0aGF0IHdpbGwgbm90IGJlIHVzZWQKcm0oYWxsZGF0YS5hbmNob3JzKQoKZ2MoKQoKCgpgYGAKCgojIDcuIEhhcm1vbnktaW50ZWdyYXRpb24KYGBge3IgaW50ZWdyYXRpb24taGFybW9ueSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgphbGxkYXRhLmludEBhY3RpdmUuYXNzYXkgPSAiUk5BIgpWYXJpYWJsZUZlYXR1cmVzKGFsbGRhdGEuaW50KSA9IGh2Z3NfYWxsCmFsbGRhdGEuaW50ID0gU2NhbGVEYXRhKGFsbGRhdGEuaW50LCB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJwZXJjZW50Lm10IiwgIm5GZWF0dXJlX1JOQSIpKQphbGxkYXRhLmludCA9IFJ1blBDQShhbGxkYXRhLmludCwgcmVkdWN0aW9uLm5hbWUgPSAicGNhX2hhcm1vbnkiKQoKCgphbGxkYXRhLmludCA8LSBSdW5IYXJtb255KAogIGFsbGRhdGEuaW50LAogIGdyb3VwLmJ5LnZhcnMgPSAib3JpZy5pZGVudCIsCiAgcmVkdWN0aW9uLnVzZSA9ICJwY2FfaGFybW9ueSIsCiAgdGhldGEgPTAuNSwKICBkaW1zLnVzZSA9IDE6MjAsCiAgYXNzYXkudXNlID0gIlJOQSIpCgoKYWxsZGF0YS5pbnQgPC0gUnVuVU1BUChhbGxkYXRhLmludCwgZGltcyA9IDE6MjAsIHJlZHVjdGlvbiA9ICJoYXJtb255IiwgcmVkdWN0aW9uLm5hbWUgPSAidW1hcF9oYXJtb255IikKYWxsZGF0YS5pbnQgPC0gUnVuVFNORShhbGxkYXRhLmludCwgZGltcyA9IDE6MjAsIHJlZHVjdGlvbiA9ICJoYXJtb255IiwgcmVkdWN0aW9uLm5hbWUgPSAidHNuZV9oYXJtb255IikKYWxsZGF0YS5pbnQgPC0gRmluZE5laWdoYm9ycyhhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInBjYV9oYXJtb255IiwgZGltcyA9IDE6MjAsIHZlcmJvc2UgPSBGQUxTRSkKYWxsZGF0YS5pbnQgPC0gRmluZENsdXN0ZXJzKGFsbGRhdGEuaW50LCByZXNvbHV0aW9uID0gMS4yLCB2ZXJib3NlID0gRkFMU0UpCgoKIAogCiB3cmFwX3Bsb3RzKAoKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfaGFybW9ueSIsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKStOb0F4ZXMoKStnZ3RpdGxlKCJQQ0EgaW50ZWdyYXRlZCIpLAogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInRzbmVfaGFybW9ueSIsIGdyb3VwLmJ5ID0gIm9yaWcuaWRlbnQiKStOb0F4ZXMoKStnZ3RpdGxlKCJ0U05FIGludGVncmF0ZWQiKSwKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX2hhcm1vbnkiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikrTm9BeGVzKCkrZ2d0aXRsZSgiVU1BUCBpbnRlZ3JhdGVkIiksCgogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInBjYV9oYXJtb255IiwgZ3JvdXAuYnkgPSAiUk5BX3Nubl9yZXMuMS4yIikrTm9BeGVzKCkrZ2d0aXRsZSgiUENBIGludGVncmF0ZWQiKSwKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ0c25lX2hhcm1vbnkiLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4xLjIiKStOb0F4ZXMoKStnZ3RpdGxlKCJ0U05FIGludGVncmF0ZWQiKSwKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX2hhcm1vbnkiLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4xLjIiKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKSwKICAgIG5jb2wgPSAzKSArIHBsb3RfbGF5b3V0KGd1aWRlcyA9ICJjb2xsZWN0IikKIAogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInBjYV9oYXJtb255IiwgZ3JvdXAuYnkgPSAib3JpZy5pZGVudCIpK05vQXhlcygpK2dndGl0bGUoIlBDQSBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ0c25lX2hhcm1vbnkiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikrTm9BeGVzKCkrZ2d0aXRsZSgidFNORSBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX2hhcm1vbnkiLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IiwgbGFiZWwgPSBULCBsYWJlbC5ib3ggPSBUKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKQogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInVtYXBfaGFybW9ueSIsIGdyb3VwLmJ5ID0gInByZWRpY3RlZC5jZWxsdHlwZS5sMiIpK05vQXhlcygpK2dndGl0bGUoIlVNQVAgaW50ZWdyYXRlZCIpCiAgICAKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJwY2FfaGFybW9ueSIsIGdyb3VwLmJ5ID0gIlJOQV9zbm5fcmVzLjEuMiIpK05vQXhlcygpK2dndGl0bGUoIlBDQSBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ0c25lX2hhcm1vbnkiLCBncm91cC5ieSA9ICJSTkFfc25uX3Jlcy4xLjIiKStOb0F4ZXMoKStnZ3RpdGxlKCJ0U05FIGludGVncmF0ZWQiKQogICAgRGltUGxvdChhbGxkYXRhLmludCwgcmVkdWN0aW9uID0gInVtYXBfaGFybW9ueSIsIGdyb3VwLmJ5ID0gIlJOQV9zbm5fcmVzLjEuMiIsIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkrTm9BeGVzKCkrZ2d0aXRsZSgiVU1BUCBpbnRlZ3JhdGVkIikKICAgIERpbVBsb3QoYWxsZGF0YS5pbnQsIHJlZHVjdGlvbiA9ICJ1bWFwX2hhcm1vbnkiLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiKStOb0F4ZXMoKStnZ3RpdGxlKCJVTUFQIGludGVncmF0ZWQiKQogICAgCmBgYAoKCgoKCgoKCiMgOC4gTWFya2VyIEdlbmUgVmlzdWFsaXphdGlvbgpgYGB7ciBmZWF0dXJlcGxvdC1oYXJtb255LCBmaWcuaGVpZ2h0PTEwLCBmaWcud2lkdGg9MTR9CgoKIyBTZXQgbWFya2VyIGdlbmVzIHNwZWNpZmljIHRvIHJlcXVlc3RlZCBpbW11bmUgY2VsbCB0eXBlcwpteWZlYXR1cmVzIDwtIGMoIkNEMTkiLCAiQ0Q3OUEiLCAiTVM0QTEiLCAjIEIgY2VsbHMKICAgICAgICAgICAgICAgICJDRDE0IiwgIkxZWiIsICJGQ0dSM0EiLCAjIE1vbm9jeXRlcwogICAgICAgICAgICAgICAgIkNTRjFSIiwgIkNENjgiLCAjIE1hY3JvcGhhZ2VzCiAgICAgICAgICAgICAgICAiTktHNyIsICJHTkxZIiwgIktJUjNETDEiLCAjIE5LIGNlbGxzCiAgICAgICAgICAgICAgICAiTUtJNjciLCAjIFByb2xpZmVyYXRpbmcgTksgY2VsbHMKICAgICAgICAgICAgICAgICJDRDM0IiwgIktJVCIsICMgSFNQQ3MKICAgICAgICAgICAgICAgICJDRDNFIiwgIkNDUjciLCAjIFQgY2VsbHMKICAgICAgICAgICAgICAgICJTRUxMIiwgIkNENDVSTyIsICMgVG5haXZlLCBUY20KICAgICAgICAgICAgICAgICJDRDQ0IiwgIkNENDVSQSIpICMgVGVtLCBUZW1yYQoKIyAjIFZpc3VhbGl6ZSBtYXJrZXIgZ2VuZXMgZm9yIFJQQ0EKIyBGZWF0dXJlUGxvdChhbGxkYXRhLmludCwgZmVhdHVyZXMgPSBteWZlYXR1cmVzLCByZWR1Y3Rpb24gPSAicGNhX3JwY2EiLCBuY29sID0gNCkgKyAKIyAgIGdndGl0bGUoIk1hcmtlciBHZW5lIEV4cHJlc3Npb24gLSBSUENBIEludGVncmF0aW9uIikgKwojICAgTm9MZWdlbmQoKQoKIyBWaXN1YWxpemUgbWFya2VyIGdlbmVzIGZvciBDQ0EKRmVhdHVyZVBsb3QoYWxsZGF0YS5pbnQsIGZlYXR1cmVzID0gbXlmZWF0dXJlcywgcmVkdWN0aW9uID0gInVtYXBfcnBjYSIsIG5jb2wgPSA0KSArIAogIGdndGl0bGUoIk1hcmtlciBHZW5lIEV4cHJlc3Npb24gLSBDQ0EgSW50ZWdyYXRpb24iKSArCiAgTm9MZWdlbmQoKQoKIyBWaXN1YWxpemUgbWFya2VyIGdlbmVzIGZvciBIYXJtb255CkZlYXR1cmVQbG90KGFsbGRhdGEuaW50LCBmZWF0dXJlcyA9IG15ZmVhdHVyZXMsIHJlZHVjdGlvbiA9ICJ1bWFwX2hhcm1vbnkiLCBuY29sID0gNCkgKyAKICBnZ3RpdGxlKCJNYXJrZXIgR2VuZSBFeHByZXNzaW9uIC0gSGFybW9ueSBJbnRlZ3JhdGlvbiIpICsKICBOb0xlZ2VuZCgpCgpgYGAKCgojIDkuIFNhdmUgdGhlIFNldXJhdCBvYmplY3QgYXMgYW4gUm9iaiBmaWxlCmBgYHtyIHNhdmVST0JKfQoKIyBzYXZlKEFsbF9zYW1wbGVzX01lcmdlZF9IYXJtb255X0ludGVncmF0ZWQsIGZpbGUgPSAiQWxsX3NhbXBsZXNfTWVyZ2VkX0hhcm1vbnlfSW50ZWdyYXRlZC5Sb2JqIikKCgpgYGAKCgoKCg==