1. load libraries

1. Load your objects


combined_merged <- readRDS("../../../MyData_Gaydosik_Data_Integration/Poglio_Gaydosik_combined_merged_SCT_Normalized.rds")

2. QC


# Remove the percent.mito column
combined_merged$percent.mito <- NULL
Warning: Cannot find cell-level meta data named  percent.mito
# Set identity classes to an existing column in meta data
Idents(object = combined_merged) <- "batch"

# Add percent ribosomal and mitochondrial content
combined_merged[["percent.rb"]] <- PercentageFeatureSet(combined_merged, pattern = "^RP[SL]")
combined_merged$percent.rb <- replace(as.numeric(combined_merged$percent.rb), is.na(combined_merged$percent.rb), 0)

combined_merged[["percent.mt"]] <- PercentageFeatureSet(combined_merged, pattern = "^MT-")
setwd("~/2025_NewHarmony_Integrated_Files/Year3_Analysis/Biomarkers_Validation_with_Public_Data/Gaydosik_Paper_Data_3_SS_Patients/Malignant_HC_Together_Analysis_for_Validation/Azimuth_Annotation")
combined_merged$percent.mt <- replace(as.numeric(combined_merged$percent.mt), is.na(combined_merged$percent.mt), 0)

# ----------------------------
# Filter high `nCount_RNA` cells
# ----------------------------

# Define an upper threshold (e.g., top 0.5% or absolute cutoff)
ncount_thresh <- quantile(combined_merged$nCount_RNA, 0.995)  # top 0.5% outliers
message("Removing cells with nCount_RNA > ", round(ncount_thresh))
Removing cells with nCount_RNA > 32789
# Subset the object to remove both types of outliers
combined_merged <- subset(combined_merged, 
                          subset = nCount_RNA < ncount_thresh & percent.mt < 10)

# ----------------------------
# QC Plots (after filtering)
# ----------------------------

VlnPlot(combined_merged, features = c("nFeature_RNA", 
                                      "nCount_RNA", 
                                      "percent.mt",
                                      "percent.rb"), 
        ncol = 4, pt.size = 0.1) & theme(plot.title = element_text(size=10))
Warning: The `slot` argument of `FetchData()` is deprecated as of SeuratObject 5.0.0.
Please use the `layer` argument instead.Warning: `PackageCheck()` was deprecated in SeuratObject 5.0.0.
Please use `rlang::check_installed()` instead.

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


FeatureScatter(combined_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(combined_merged, 
               feature1 = "nCount_RNA", 
               feature2 = "percent.mt")+
  geom_smooth(method = 'lm')


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

JoinLayers

Assign Cell-Cycle Scores

3. Normalize data

# # Apply SCTransform
# combined_merged <- SCTransform(combined_merged, 
#                                   vars.to.regress = c("percent.rb","percent.mt","CC.Difference", "nCount_RNA"),
#                                   assay = "RNA", 
#                                   do.scale=TRUE, 
#                                   do.center=TRUE, 
#                                   verbose = TRUE)

4. Perform PCA


# Variables_genes <- combined_merged@assays$SCT@var.features
# 
# # Exclude genes starting with "HLA-" AND "Xist" AND "TRBV, TRAV"
# Variables_genes_after_exclusion <- Variables_genes[!grepl("^HLA-|^XIST|^TRBV|^TRAV", Variables_genes)]
# 
# # Set the seed for clustering steps
# set.seed(123)
# 
# # These are now standard steps in the Seurat workflow for visualization and clustering
# combined_merged <- RunPCA(combined_merged,
#                         features = Variables_genes_after_exclusion,
#                         do.print = TRUE, 
#                         pcs.print = 1:5, 
#                         genes.print = 15,
#                         npcs = 50)

# determine dimensionality of the data
ElbowPlot(combined_merged, ndims = 50)

5. Perform PCA TEST



library(ggplot2)
library(RColorBrewer)  

# Assuming you have 10 different cell lines, generating a color palette with 10 colors
n_batches <- length(unique(data$batch))
batch_colors <- setNames(colorRampPalette(brewer.pal(8, "Set3"))(n_batches), unique(data$batch))

# Assuming combined_merged$batch is a factor or character vector containing cell line names
data <- as.data.frame(table(combined_merged$batch))
colnames(data) <- c("batch", "nUMI")  # Change column name to nUMI

ncells <- ggplot(data, aes(x = batch, y = nUMI, fill = batch)) + 
  geom_col() +
  theme_classic() +
  geom_text(aes(label = nUMI), 
            position = position_dodge(width = 0.9), 
            vjust = -0.25) +
  scale_fill_manual(values = batch_colors) + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1),
        plot.title = element_text(hjust = 0.5)) +  # Adjust the title position
  ggtitle("Filtered cells per sample") +
  xlab("batch") +  # Adjust x-axis label
  ylab("Frequency")    # Adjust y-axis label

print(ncells)




# TEST-1
# given that the output of RunPCA is "pca"
# replace "so" by the name of your seurat object

pct <- combined_merged[["pca"]]@stdev / sum(combined_merged[["pca"]]@stdev) * 100
cumu <- cumsum(pct) # Calculate cumulative percents for each PC
# Determine the difference between variation of PC and subsequent PC
co2 <- sort(which((pct[-length(pct)] - pct[-1]) > 0.1), decreasing = T)[1] + 1
# last point where change of % of variation is more than 0.1%. -> co2
co2
[1] 20
# TEST-2
# get significant PCs
stdv <- combined_merged[["pca"]]@stdev
sum.stdv <- sum(combined_merged[["pca"]]@stdev)
percent.stdv <- (stdv / sum.stdv) * 100
cumulative <- cumsum(percent.stdv)
co1 <- which(cumulative > 90 & percent.stdv < 5)[1]
co2 <- sort(which((percent.stdv[1:length(percent.stdv) - 1] - 
                       percent.stdv[2:length(percent.stdv)]) > 0.1), 
              decreasing = T)[1] + 1
min.pc <- min(co1, co2)
min.pc
[1] 20
# Create a dataframe with values
plot_df <- data.frame(pct = percent.stdv, 
           cumu = cumulative, 
           rank = 1:length(percent.stdv))

# Elbow plot to visualize 
  ggplot(plot_df, aes(cumulative, percent.stdv, label = rank, color = rank > min.pc)) + 
  geom_text() + 
  geom_vline(xintercept = 90, color = "grey") + 
  geom_hline(yintercept = min(percent.stdv[percent.stdv > 5]), color = "grey") +
  theme_bw()

NA
NA
NA

6. Clustering


# # Set the seed for clustering steps
# set.seed(123)
# 
# combined_merged <- FindNeighbors(combined_merged, 
#                                 dims = 1:min.pc, 
#                                 verbose = FALSE)
# 
# # understanding resolution
# combined_merged <- FindClusters(combined_merged, 
#                                     resolution = c(0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9, 1, 1.2,1.5))
# 

UMAP Visualization

# # Set the seed for clustering steps
# set.seed(123)
# 
# # non-linear dimensionality reduction --------------
# combined_merged <- RunUMAP(combined_merged, 
#                           dims = 1:min.pc,
#                           verbose = FALSE)
#                                   
# 
# # Define resolution values to plot
# resolutions <- c(0.3, 0.4, 0.5, 0.6, 0.7,0.8, 0.9, 1, 1.2,1.5)
# 
# # Loop through and generate DimPlots
# for (res in resolutions) {
#   res_col <- paste0("SCT_snn_res.", res)
#   
#   p <- DimPlot(combined_merged,
#                group.by = res_col,
#                reduction = "umap",
#                label.size = 3,
#                repel = TRUE,
#                label = TRUE,
#                label.box = TRUE) +
#        ggtitle(paste("Clustering resolution:", res))
#   
#   print(p)
# }
# 

# Set identity classes to an existing column in meta data
Idents(object = combined_merged) <- "SCT_snn_res.0.4"

cluster_table <- table(Idents(combined_merged))


barplot(cluster_table, main = "Number of Cells in Each Cluster", 
                      xlab = "Cluster", 
                      ylab = "Number of Cells", 
                      col = rainbow(length(cluster_table)))


print(cluster_table)

   0    1    2    3    4    5    6    7    8    9   10   11   12   13   14   15   16   17   18   19   20 
9569 7215 6432 6190 4423 5740 5634 5664 5541 5410 5174 2655 1576 1937  213 1386 1429 1179  813  801  798 
  21   22   23   24   25   26 
 527  423  358  338  159   80 
DimPlot(combined_merged, group.by = "batch", 
        reduction = "umap",
        label.size = 3,
        repel = T,
        label = T, label.box = T)

7. clusTree

library(clustree)
Loading required package: ggraph

Attaching package: 'ggraph'

The following object is masked from 'package:sp':

    geometry
clustree(combined_merged, prefix = "SCT_snn_res.")

8. Azimuth Annotation

library(Seurat)
InstallData("pbmcref")
Warning: The following packages are already installed and will not be reinstalled: pbmcref
# The RunAzimuth function can take a Seurat object as input
combined_merged <- RunAzimuth(combined_merged, reference = "pbmcref")
Warning: Overwriting miscellanous data for modelWarning: Adding a dimensional reduction (refUMAP) without the associated assay being presentWarning: Adding a dimensional reduction (refUMAP) without the associated assay being presentdetected inputs from HUMAN with id type Gene.name
reference rownames detected HUMAN with id type Gene.name
Normalizing query using reference SCT model
Warning: 113 features of the features specified were not present in both the reference query assays. 
Continuing with remaining 4887 features.Projecting cell embeddings
Error: Cannot add new cells with [[<-

9. Azimuth Visualization

DimPlot(combined_merged, group.by = "predicted.celltype.l1", 
        reduction = "umap",
        label.size = 3,
        repel = T,
        label = T, label.box = T)
Warning: The following requested variables were not found: predicted.celltype.l1Error in `[.data.frame`(data, , group) : undefined columns selected

Save the Seurat object as an Robj file


saveRDS(combined_merged, file = "Poglio_Gaydosik_combined_merged_SCT_Normalized.rds")

10. Harmony Integration



# Load required libraries
library(Seurat)
library(harmony)
library(ggplot2)

# Run Harmony, adjusting for batch effect using "cell_line" or another grouping variable
combined_merged <- RunHarmony(
  combined_merged,
  group.by.vars = c("batch"),  # Replace with the metadata column specifying batch or cell line
  assay.use="SCT")
Transposing data matrix
Initializing state using k-means centroids initialization
Harmony 1/10
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Harmony 2/10
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Harmony 3/10
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Harmony converged after 3 iterations
# Check results in harmony embeddings
harmony_embeddings <- Embeddings(combined_merged, reduction = "harmony")
head(harmony_embeddings)
                        harmony_1  harmony_2  harmony_3  harmony_4  harmony_5   harmony_6  harmony_7
L1_AAACCTGAGGGCTTCC-1 -18.4855027  7.9423204 -0.2196979  2.4854855  3.0805502 -2.13201971 -1.5175328
L1_AAACCTGGTGCAGGTA-1  -6.5494297 -4.1619010  0.2539774  0.3868676 -0.2058094  0.86031329  0.6284560
L1_AAACCTGGTTAAAGTG-1   4.5029146 -2.5820435 -1.1373015  0.1770274 -2.1259750  4.60306410 -0.6284321
L1_AAACCTGTCAGGTAAA-1   0.5745898 -0.8535429  0.8603061  1.5044380  4.0526170  1.32483585 -1.7043553
L1_AAACCTGTCCCTGACT-1 -13.2222987  7.3356257  1.8729908  0.2086466  0.4211220  0.03685936 -1.0460933
L1_AAACCTGTCCTTCAAT-1  -0.8538374 -4.8084348 -1.5473780 -1.1453762 -2.9576401  1.61335360  0.3296758
                       harmony_8  harmony_9  harmony_10 harmony_11 harmony_12 harmony_13 harmony_14
L1_AAACCTGAGGGCTTCC-1  2.8195613 -3.1815283  0.16958309 -5.4855500  4.9764302  0.1773200  0.8632915
L1_AAACCTGGTGCAGGTA-1  3.1016680  3.2600778 -0.52434897 -4.1796631  0.6155530  0.2923108  1.1184826
L1_AAACCTGGTTAAAGTG-1  1.9698514 -1.4398020  0.50992434 -2.2941862 -0.7746350  1.0098298 -1.0810271
L1_AAACCTGTCAGGTAAA-1 -0.3045706  0.3694501 -0.65804743 -0.1578142 -1.2242120  2.2096673  0.2381271
L1_AAACCTGTCCCTGACT-1  2.1340628 -2.0582358 -0.23791341 -2.2020179  3.1349577 -1.3031110  4.0706958
L1_AAACCTGTCCTTCAAT-1  1.5981304  0.1617858  0.01682314 -1.3374615  0.7898118  0.6148349  0.1959089
                       harmony_15   harmony_16 harmony_17  harmony_18 harmony_19 harmony_20 harmony_21
L1_AAACCTGAGGGCTTCC-1  1.77877175  0.648277318 -1.8146050 -0.60387511  0.7448553  1.0578965   1.297213
L1_AAACCTGGTGCAGGTA-1 -1.25676408 -0.032001930 -1.7105728  0.62126453  2.0266850  1.4024440  -2.900196
L1_AAACCTGGTTAAAGTG-1 -0.03085833 -0.005814651 -0.6493010 -0.72298562  1.9222377 -2.6967548  -0.557706
L1_AAACCTGTCAGGTAAA-1 -0.46478792 -1.207716626  0.5349474 -1.18396607 -0.7381161 -1.1951483  -4.461107
L1_AAACCTGTCCCTGACT-1  1.44715193 -0.066221590 -0.8311151 -1.32487017  0.1419474  1.5816799   0.757584
L1_AAACCTGTCCTTCAAT-1  0.16629524 -0.526111927 -1.0628915 -0.07495768  1.0285287  0.5241696   1.198793
                      harmony_22 harmony_23 harmony_24 harmony_25  harmony_26 harmony_27 harmony_28
L1_AAACCTGAGGGCTTCC-1  3.1438926  4.0525919 -1.8571393  -1.123777 -1.77661793  0.2440655 -1.6636347
L1_AAACCTGGTGCAGGTA-1  2.7535922 -1.7408459  1.4560267  -1.602397 -0.97356067 -0.1840563  0.7404742
L1_AAACCTGGTTAAAGTG-1  0.3655097 -1.9256351 -3.2008633  -2.588555  2.79950517  3.2863951 -1.3789454
L1_AAACCTGTCAGGTAAA-1  3.2063842 -1.7560083  0.5468997  -1.312635  0.01983461  1.0390326  0.2064140
L1_AAACCTGTCCCTGACT-1  2.1987280  3.4204367 -1.1399827  -1.367466 -1.13131567  1.4423038 -0.8344294
L1_AAACCTGTCCTTCAAT-1  1.3319477  0.3365448  0.5921872  -1.603506  1.51891869  0.6538837 -0.5850538
                      harmony_29   harmony_30  harmony_31 harmony_32  harmony_33 harmony_34  harmony_35
L1_AAACCTGAGGGCTTCC-1 -0.6985372  0.001992406 -0.03308453 -0.5344455  0.08165714  1.1561033  0.09751095
L1_AAACCTGGTGCAGGTA-1 -1.4326163 -1.421477162  0.42626754  1.1301252 -0.27722996  0.5523950  0.32210353
L1_AAACCTGGTTAAAGTG-1  1.2013954  0.019446755  1.14350897 -0.8957589 -0.13677914  0.7776574 -1.90726729
L1_AAACCTGTCAGGTAAA-1  0.4212270 -0.203393996  0.71283186  1.4282126  0.63228992 -1.8358861  0.92606916
L1_AAACCTGTCCCTGACT-1 -1.1708837  1.457756837  0.08031120 -0.8225988  0.61428860 -1.4715428  0.63390863
L1_AAACCTGTCCTTCAAT-1 -1.4805198 -0.030434591 -1.82280183  0.1143809  1.89139408 -0.4546119  1.13054387
                       harmony_36  harmony_37 harmony_38 harmony_39 harmony_40  harmony_41 harmony_42
L1_AAACCTGAGGGCTTCC-1 -0.52539264 -0.04873894 -0.5614033  0.8920342 -0.8063888  0.02255221  0.2444488
L1_AAACCTGGTGCAGGTA-1 -0.36683300 -1.59599078  0.1137256 -0.1385294  1.1308844 -0.56512949  1.2670811
L1_AAACCTGGTTAAAGTG-1 -1.45259164  0.47771741  0.2388712  0.7192189  0.4374085  0.15769393  0.5817462
L1_AAACCTGTCAGGTAAA-1  0.01180534 -0.61542923  0.7325384 -0.0920718  2.3385877 -0.62949538 -1.3082965
L1_AAACCTGTCCCTGACT-1  0.70117509  0.82009779  0.7759824  0.2438408 -1.2703849  2.86204005 -0.4627982
L1_AAACCTGTCCTTCAAT-1 -0.90789373 -0.27960555  0.1415306  0.6175270  1.0569101 -0.12742201 -0.6355494
                       harmony_43 harmony_44  harmony_45  harmony_46 harmony_47 harmony_48 harmony_49
L1_AAACCTGAGGGCTTCC-1 -0.88118369  0.7919337 -1.26483643 -0.25145293  0.2382496  0.4712432 -1.6265200
L1_AAACCTGGTGCAGGTA-1  0.59983144  2.8364486 -1.28550254 -1.37360333  1.2220225 -0.1983266 -1.7725065
L1_AAACCTGGTTAAAGTG-1  0.61253631 -0.9935459 -0.04096519 -0.52000917 -0.7439375 -0.7159783 -2.7579180
L1_AAACCTGTCAGGTAAA-1 -0.03403918 -1.0374222 -2.25264836 -0.86893246  2.6223660 -0.5912721  2.5533740
L1_AAACCTGTCCCTGACT-1 -1.23578994 -1.5286682 -1.49695807  0.05879675 -0.1396169  2.1965935  0.7691668
L1_AAACCTGTCCTTCAAT-1 -0.12479116  0.3548016 -0.45706530 -1.13590570  0.4446526 -1.9508394 -1.0077698
                      harmony_50
L1_AAACCTGAGGGCTTCC-1  3.7587366
L1_AAACCTGGTGCAGGTA-1  0.6304744
L1_AAACCTGGTTAAAGTG-1  0.3955415
L1_AAACCTGTCAGGTAAA-1 -0.4746698
L1_AAACCTGTCCCTGACT-1  2.6825509
L1_AAACCTGTCCTTCAAT-1  2.7133421
# Set the seed for clustering steps
set.seed(123)

# Run UMAP on Harmony embeddings
combined_merged <- RunUMAP(combined_merged, reduction = "harmony", dims = 1:16)
10:56:18 UMAP embedding parameters a = 0.9922 b = 1.112
10:56:18 Read 81664 rows and found 16 numeric columns
10:56:18 Using Annoy for neighbor search, n_neighbors = 30
10:56:18 Building Annoy index with metric = cosine, n_trees = 50
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:56:24 Writing NN index file to temp file /tmp/Rtmp2IR4Yl/file8415b17c6fbc3
10:56:24 Searching Annoy index using 1 thread, search_k = 3000
10:56:48 Annoy recall = 100%
10:56:49 Commencing smooth kNN distance calibration using 1 thread with target n_neighbors = 30
10:56:51 Initializing from normalized Laplacian + noise (using RSpectra)
10:56:54 Commencing optimization for 200 epochs, with 3520450 positive edges
10:56:54 Using rng type: pcg
Using method 'umap'
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
10:57:21 Optimization finished
# Set the seed for clustering steps
set.seed(123)

# Optionally, find neighbors and clusters (if you plan to do clustering analysis)
combined_merged <- FindNeighbors(combined_merged, reduction = "harmony", dims = 1:16)
Computing nearest neighbor graph
Computing SNN
combined_merged <- FindClusters(combined_merged, reduction = "harmony", resolution = 0.5)  # Adjust resolution as needed
Warning: The following arguments are not used: reductionWarning: The following arguments are not used: reduction
Modularity Optimizer version 1.3.0 by Ludo Waltman and Nees Jan van Eck

Number of nodes: 81664
Number of edges: 2391723

Running Louvain algorithm...
0%   10   20   30   40   50   60   70   80   90   100%
[----|----|----|----|----|----|----|----|----|----|
**************************************************|
Maximum modularity in 10 random starts: 0.9061
Number of communities: 21
Elapsed time: 25 seconds
# Visualize UMAP
DimPlot(combined_merged, reduction = "umap", group.by = "batch", label = TRUE, pt.size = 0.5) +
    ggtitle("UMAP of Harmony-Integrated Data")



# Visualize UMAP with batch/cell line information
DimPlot(combined_merged, reduction = "umap", group.by = "batch", label = TRUE, pt.size = 0.5) +
    ggtitle("UMAP - Colored by Cell Line (After Harmony Integration)")



# Visualize UMAP with clusters
DimPlot(combined_merged, reduction = "umap", group.by = "seurat_clusters", label = TRUE, pt.size = 0.5) +
    ggtitle("UMAP - Clustered Data (After Harmony Integration)")


# # Visualize specific cell types or other metadata
# DimPlot(combined_merged, reduction = "umap", group.by = "predicted.celltype.l2", label = TRUE, pt.size = 0.5) +
#     ggtitle("UMAP - Cell Types After Harmony Integration")

Save the Seurat object as an Robj file


saveRDS(combined_merged, file = "Poglio_Gaydosik_combined_merged_Harmonized.rds")
LS0tCnRpdGxlOiAiTWVyZ2luZyBQb2dsaW8gYW5kIEdheWRvc2lrIERhdGEiCmF1dGhvcjogTmFzaXIgTWFobW9vZCBBYmJhc2kKZGF0ZTogImByIFN5cy5EYXRlKClgIgpvdXRwdXQ6CiAgI3JtZGZvcm1hdHM6OnJlYWR0aGVkb3duCiAgaHRtbF9ub3RlYm9vazoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICB0b2NfY29sbGFwc2VkOiB0cnVlCgotLS0KCiMgMS4gbG9hZCBsaWJyYXJpZXMKYGBge3IgLCBpbmNsdWRlPUZBTFNFfQoKbGlicmFyeShTZXVyYXQpICAgICAgICAjIENvcmUgU2V1cmF0IGZ1bmN0aW9ucwpsaWJyYXJ5KFNldXJhdERhdGEpICAgICMgRm9yIGxvYWRpbmcvaW5zdGFsbGluZyByZWZlcmVuY2UgZGF0YXNldHMKbGlicmFyeShTZXVyYXREaXNrKSAgICAjIEZvciBjb252ZXJ0aW5nIGJldHdlZW4gaDVTZXVyYXQvaDVhZCBmb3JtYXRzCmxpYnJhcnkoQXppbXV0aCkgICAgICAgIyBJZiB5b3UncmUgdXNpbmcgQXppbXV0aCByZWZlcmVuY2UgbWFwcGluZyBkaXJlY3RseQpsaWJyYXJ5KGhhcm1vbnkpCgoKbGlicmFyeSh0aWR5dmVyc2UpICAgICAgIyBpbmNsdWRlcyBkcGx5ciwgZ2dwbG90MiwgdGliYmxlLCByZWFkciwgZXRjLgpsaWJyYXJ5KGRpdHRvU2VxKQpsaWJyYXJ5KHBsb3RseSkKbGlicmFyeShTZXVyYXREaXNrKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShybWFya2Rvd24pCmxpYnJhcnkoa25pdHIpCmxpYnJhcnkodGlueXRleCkKYGBgCgoKIyAxLiBMb2FkIHlvdXIgb2JqZWN0cwpgYGB7ciAsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTZ9Cgpjb21iaW5lZF9tZXJnZWQgPC0gcmVhZFJEUygiLi4vLi4vLi4vTXlEYXRhX0dheWRvc2lrX0RhdGFfSW50ZWdyYXRpb24vUG9nbGlvX0dheWRvc2lrX2NvbWJpbmVkX21lcmdlZF9TQ1RfTm9ybWFsaXplZC5yZHMiKQpgYGAKCiMgMi4gUUMKYGBge3IgUUMsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE0fQoKIyBSZW1vdmUgdGhlIHBlcmNlbnQubWl0byBjb2x1bW4KY29tYmluZWRfbWVyZ2VkJHBlcmNlbnQubWl0byA8LSBOVUxMCgoKIyBTZXQgaWRlbnRpdHkgY2xhc3NlcyB0byBhbiBleGlzdGluZyBjb2x1bW4gaW4gbWV0YSBkYXRhCklkZW50cyhvYmplY3QgPSBjb21iaW5lZF9tZXJnZWQpIDwtICJiYXRjaCIKCiMgQWRkIHBlcmNlbnQgcmlib3NvbWFsIGFuZCBtaXRvY2hvbmRyaWFsIGNvbnRlbnQKY29tYmluZWRfbWVyZ2VkW1sicGVyY2VudC5yYiJdXSA8LSBQZXJjZW50YWdlRmVhdHVyZVNldChjb21iaW5lZF9tZXJnZWQsIHBhdHRlcm4gPSAiXlJQW1NMXSIpCmNvbWJpbmVkX21lcmdlZCRwZXJjZW50LnJiIDwtIHJlcGxhY2UoYXMubnVtZXJpYyhjb21iaW5lZF9tZXJnZWQkcGVyY2VudC5yYiksIGlzLm5hKGNvbWJpbmVkX21lcmdlZCRwZXJjZW50LnJiKSwgMCkKCmNvbWJpbmVkX21lcmdlZFtbInBlcmNlbnQubXQiXV0gPC0gUGVyY2VudGFnZUZlYXR1cmVTZXQoY29tYmluZWRfbWVyZ2VkLCBwYXR0ZXJuID0gIl5NVC0iKQpjb21iaW5lZF9tZXJnZWQkcGVyY2VudC5tdCA8LSByZXBsYWNlKGFzLm51bWVyaWMoY29tYmluZWRfbWVyZ2VkJHBlcmNlbnQubXQpLCBpcy5uYShjb21iaW5lZF9tZXJnZWQkcGVyY2VudC5tdCksIDApCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBGaWx0ZXIgaGlnaCBgbkNvdW50X1JOQWAgY2VsbHMKIyAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIERlZmluZSBhbiB1cHBlciB0aHJlc2hvbGQgKGUuZy4sIHRvcCAwLjUlIG9yIGFic29sdXRlIGN1dG9mZikKbmNvdW50X3RocmVzaCA8LSBxdWFudGlsZShjb21iaW5lZF9tZXJnZWQkbkNvdW50X1JOQSwgMC45OTUpICAjIHRvcCAwLjUlIG91dGxpZXJzCm1lc3NhZ2UoIlJlbW92aW5nIGNlbGxzIHdpdGggbkNvdW50X1JOQSA+ICIsIHJvdW5kKG5jb3VudF90aHJlc2gpKQoKIyBTdWJzZXQgdGhlIG9iamVjdCB0byByZW1vdmUgYm90aCB0eXBlcyBvZiBvdXRsaWVycwpjb21iaW5lZF9tZXJnZWQgPC0gc3Vic2V0KGNvbWJpbmVkX21lcmdlZCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc3Vic2V0ID0gbkNvdW50X1JOQSA8IG5jb3VudF90aHJlc2ggJiBwZXJjZW50Lm10IDwgMTApCgojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBRQyBQbG90cyAoYWZ0ZXIgZmlsdGVyaW5nKQojIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KClZsblBsb3QoY29tYmluZWRfbWVyZ2VkLCBmZWF0dXJlcyA9IGMoIm5GZWF0dXJlX1JOQSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJuQ291bnRfUk5BIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInBlcmNlbnQubXQiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJwZXJjZW50LnJiIiksIAogICAgICAgIG5jb2wgPSA0LCBwdC5zaXplID0gMC4xKSAmIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0xMCkpCgpGZWF0dXJlU2NhdHRlcihjb21iaW5lZF9tZXJnZWQsIGZlYXR1cmUxID0gInBlcmNlbnQubXQiLCBmZWF0dXJlMiA9ICJwZXJjZW50LnJiIikKCkZlYXR1cmVTY2F0dGVyKGNvbWJpbmVkX21lcmdlZCwgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIGZlYXR1cmUyID0gIm5GZWF0dXJlX1JOQSIpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAnbG0nKQoKYGBgCgojI0ZlYXR1cmVTY2F0dGVyIGlzIHR5cGljYWxseSB1c2VkIHRvIHZpc3VhbGl6ZSBmZWF0dXJlLWZlYXR1cmUgcmVsYXRpb25zaGlwcwojI2ZvciBhbnl0aGluZyBjYWxjdWxhdGVkIGJ5IHRoZSBvYmplY3QsIAojI2kuZS4gY29sdW1ucyBpbiBvYmplY3QgbWV0YWRhdGEsIFBDIHNjb3JlcyBldGMuCgpgYGB7ciAsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKRmVhdHVyZVNjYXR0ZXIoY29tYmluZWRfbWVyZ2VkLCAKICAgICAgICAgICAgICAgZmVhdHVyZTEgPSAibkNvdW50X1JOQSIsIAogICAgICAgICAgICAgICBmZWF0dXJlMiA9ICJwZXJjZW50Lm10IikrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykKCkZlYXR1cmVTY2F0dGVyKGNvbWJpbmVkX21lcmdlZCwgCiAgICAgICAgICAgICAgIGZlYXR1cmUxID0gIm5Db3VudF9STkEiLCAKICAgICAgICAgICAgICAgZmVhdHVyZTIgPSAibkZlYXR1cmVfUk5BIikrCiAgZ2VvbV9zbW9vdGgobWV0aG9kID0gJ2xtJykKCmBgYAojIyAgSm9pbkxheWVycwpgYGB7ciAsIGVjaG89RkFMU0UsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTEwfQoKCiMgRGVmYXVsdEFzc2F5KGNvbWJpbmVkX21lcmdlZCkgPC0gIlJOQSIKIyAKIyAjIE1lcmdlIGFsbCBjb3VudHMuKiBsYXllcnMgaW50byBhIHNpbmdsZSBjb3VudHMgbGF5ZXIKIyBjb21iaW5lZF9tZXJnZWQgPC0gSm9pbkxheWVycyhjb21iaW5lZF9tZXJnZWQsIGFzc2F5ID0gIlJOQSIpCiMgCiMgIyBDb25maXJtIGxheWVycyBub3cKIyBMYXllcnMoY29tYmluZWRfbWVyZ2VkW1siUk5BIl1dKQpgYGAKCiMjICBBc3NpZ24gQ2VsbC1DeWNsZSBTY29yZXMKYGBge3IgLCBlY2hvPUZBTFNFLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCiMgb3B0aW9ucyhmdXR1cmUuZ2xvYmFscy5tYXhTaXplID0gODAwMCAqIDEwMjReMikgICMgU2V0IHRvIDgwMDAgTWlCIChhYm91dCA4IEdCKQojIAojIAojIGNvbWJpbmVkX21lcmdlZCA8LSBTQ1RyYW5zZm9ybShjb21iaW5lZF9tZXJnZWQsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG8uc2NhbGUgPSBGQUxTRSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkby5jZW50ZXIgPSBGQUxTRSkgICMgUmVkdWNlIHRvIDEwMDAgdmFyaWFibGUgZmVhdHVyZXMKIyAKIyAKIyAjIEEgbGlzdCBvZiBjZWxsIGN5Y2xlIG1hcmtlcnMsIGZyb20gVGlyb3NoIGV0IGFsLCAyMDE1LCBpcyBsb2FkZWQgd2l0aCBTZXVyYXQuICBXZSBjYW4KIyAjIHNlZ3JlZ2F0ZSB0aGlzIGxpc3QgaW50byBtYXJrZXJzIG9mIEcyL00gcGhhc2UgYW5kIG1hcmtlcnMgb2YgUyBwaGFzZQojIHMuZ2VuZXMgPC0gY2MuZ2VuZXMkcy5nZW5lcwojIGcybS5nZW5lcyA8LSBjYy5nZW5lcyRnMm0uZ2VuZXMKIyAKIyAKIyBjb21iaW5lZF9tZXJnZWQgPC0gQ2VsbEN5Y2xlU2NvcmluZyhjb21iaW5lZF9tZXJnZWQsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHMuZmVhdHVyZXMgPSBzLmdlbmVzLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnMm0uZmVhdHVyZXMgPSBnMm0uZ2VuZXMsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNldC5pZGVudCA9IFRSVUUpCiMgCiMgRGVmYXVsdEFzc2F5KGNvbWJpbmVkX21lcmdlZCkgPC0gIlJOQSIKIyBjb21iaW5lZF9tZXJnZWQkQ0MuRGlmZmVyZW5jZSA8LSBjb21iaW5lZF9tZXJnZWQkUy5TY29yZSAtIGNvbWJpbmVkX21lcmdlZCRHMk0uU2NvcmUKCmBgYAoKCiMgMy4gTm9ybWFsaXplIGRhdGEKYGBge3IgU0NUTm9ybWFsaXplLCBpbmNsdWRlPVRSVUV9CiMgIyBBcHBseSBTQ1RyYW5zZm9ybQojIGNvbWJpbmVkX21lcmdlZCA8LSBTQ1RyYW5zZm9ybShjb21iaW5lZF9tZXJnZWQsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXJzLnRvLnJlZ3Jlc3MgPSBjKCJwZXJjZW50LnJiIiwicGVyY2VudC5tdCIsIkNDLkRpZmZlcmVuY2UiLCAibkNvdW50X1JOQSIpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBhc3NheSA9ICJSTkEiLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZG8uc2NhbGU9VFJVRSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRvLmNlbnRlcj1UUlVFLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IFRSVUUpCmBgYAoKCiMgNC4gUGVyZm9ybSBQQ0EKYGBge3IgUENBLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCiMgVmFyaWFibGVzX2dlbmVzIDwtIGNvbWJpbmVkX21lcmdlZEBhc3NheXMkU0NUQHZhci5mZWF0dXJlcwojIAojICMgRXhjbHVkZSBnZW5lcyBzdGFydGluZyB3aXRoICJITEEtIiBBTkQgIlhpc3QiIEFORCAiVFJCViwgVFJBViIKIyBWYXJpYWJsZXNfZ2VuZXNfYWZ0ZXJfZXhjbHVzaW9uIDwtIFZhcmlhYmxlc19nZW5lc1shZ3JlcGwoIl5ITEEtfF5YSVNUfF5UUkJWfF5UUkFWIiwgVmFyaWFibGVzX2dlbmVzKV0KIyAKIyAjIFNldCB0aGUgc2VlZCBmb3IgY2x1c3RlcmluZyBzdGVwcwojIHNldC5zZWVkKDEyMykKIyAKIyAjIFRoZXNlIGFyZSBub3cgc3RhbmRhcmQgc3RlcHMgaW4gdGhlIFNldXJhdCB3b3JrZmxvdyBmb3IgdmlzdWFsaXphdGlvbiBhbmQgY2x1c3RlcmluZwojIGNvbWJpbmVkX21lcmdlZCA8LSBSdW5QQ0EoY29tYmluZWRfbWVyZ2VkLAojICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gVmFyaWFibGVzX2dlbmVzX2FmdGVyX2V4Y2x1c2lvbiwKIyAgICAgICAgICAgICAgICAgICAgICAgICBkby5wcmludCA9IFRSVUUsIAojICAgICAgICAgICAgICAgICAgICAgICAgIHBjcy5wcmludCA9IDE6NSwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgZ2VuZXMucHJpbnQgPSAxNSwKIyAgICAgICAgICAgICAgICAgICAgICAgICBucGNzID0gNTApCgojIGRldGVybWluZSBkaW1lbnNpb25hbGl0eSBvZiB0aGUgZGF0YQpFbGJvd1Bsb3QoY29tYmluZWRfbWVyZ2VkLCBuZGltcyA9IDUwKQpgYGAKCiMgNS4gUGVyZm9ybSBQQ0EgVEVTVApgYGB7ciBQQ0EtVEVTVCwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgoKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikgIAoKIyBBc3N1bWluZyB5b3UgaGF2ZSAxMCBkaWZmZXJlbnQgY2VsbCBsaW5lcywgZ2VuZXJhdGluZyBhIGNvbG9yIHBhbGV0dGUgd2l0aCAxMCBjb2xvcnMKbl9iYXRjaGVzIDwtIGxlbmd0aCh1bmlxdWUoZGF0YSRiYXRjaCkpCmJhdGNoX2NvbG9ycyA8LSBzZXROYW1lcyhjb2xvclJhbXBQYWxldHRlKGJyZXdlci5wYWwoOCwgIlNldDMiKSkobl9iYXRjaGVzKSwgdW5pcXVlKGRhdGEkYmF0Y2gpKQoKIyBBc3N1bWluZyBjb21iaW5lZF9tZXJnZWQkYmF0Y2ggaXMgYSBmYWN0b3Igb3IgY2hhcmFjdGVyIHZlY3RvciBjb250YWluaW5nIGNlbGwgbGluZSBuYW1lcwpkYXRhIDwtIGFzLmRhdGEuZnJhbWUodGFibGUoY29tYmluZWRfbWVyZ2VkJGJhdGNoKSkKY29sbmFtZXMoZGF0YSkgPC0gYygiYmF0Y2giLCAiblVNSSIpICAjIENoYW5nZSBjb2x1bW4gbmFtZSB0byBuVU1JCgpuY2VsbHMgPC0gZ2dwbG90KGRhdGEsIGFlcyh4ID0gYmF0Y2gsIHkgPSBuVU1JLCBmaWxsID0gYmF0Y2gpKSArIAogIGdlb21fY29sKCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IG5VTUkpLCAKICAgICAgICAgICAgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZSh3aWR0aCA9IDAuOSksIAogICAgICAgICAgICB2anVzdCA9IC0wLjI1KSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYmF0Y2hfY29sb3JzKSArIAogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSksCiAgICAgICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChoanVzdCA9IDAuNSkpICsgICMgQWRqdXN0IHRoZSB0aXRsZSBwb3NpdGlvbgogIGdndGl0bGUoIkZpbHRlcmVkIGNlbGxzIHBlciBzYW1wbGUiKSArCiAgeGxhYigiYmF0Y2giKSArICAjIEFkanVzdCB4LWF4aXMgbGFiZWwKICB5bGFiKCJGcmVxdWVuY3kiKSAgICAjIEFkanVzdCB5LWF4aXMgbGFiZWwKCnByaW50KG5jZWxscykKCgoKIyBURVNULTEKIyBnaXZlbiB0aGF0IHRoZSBvdXRwdXQgb2YgUnVuUENBIGlzICJwY2EiCiMgcmVwbGFjZSAic28iIGJ5IHRoZSBuYW1lIG9mIHlvdXIgc2V1cmF0IG9iamVjdAoKcGN0IDwtIGNvbWJpbmVkX21lcmdlZFtbInBjYSJdXUBzdGRldiAvIHN1bShjb21iaW5lZF9tZXJnZWRbWyJwY2EiXV1Ac3RkZXYpICogMTAwCmN1bXUgPC0gY3Vtc3VtKHBjdCkgIyBDYWxjdWxhdGUgY3VtdWxhdGl2ZSBwZXJjZW50cyBmb3IgZWFjaCBQQwojIERldGVybWluZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHZhcmlhdGlvbiBvZiBQQyBhbmQgc3Vic2VxdWVudCBQQwpjbzIgPC0gc29ydCh3aGljaCgocGN0Wy1sZW5ndGgocGN0KV0gLSBwY3RbLTFdKSA+IDAuMSksIGRlY3JlYXNpbmcgPSBUKVsxXSArIDEKIyBsYXN0IHBvaW50IHdoZXJlIGNoYW5nZSBvZiAlIG9mIHZhcmlhdGlvbiBpcyBtb3JlIHRoYW4gMC4xJS4gLT4gY28yCmNvMgoKIyBURVNULTIKIyBnZXQgc2lnbmlmaWNhbnQgUENzCnN0ZHYgPC0gY29tYmluZWRfbWVyZ2VkW1sicGNhIl1dQHN0ZGV2CnN1bS5zdGR2IDwtIHN1bShjb21iaW5lZF9tZXJnZWRbWyJwY2EiXV1Ac3RkZXYpCnBlcmNlbnQuc3RkdiA8LSAoc3RkdiAvIHN1bS5zdGR2KSAqIDEwMApjdW11bGF0aXZlIDwtIGN1bXN1bShwZXJjZW50LnN0ZHYpCmNvMSA8LSB3aGljaChjdW11bGF0aXZlID4gOTAgJiBwZXJjZW50LnN0ZHYgPCA1KVsxXQpjbzIgPC0gc29ydCh3aGljaCgocGVyY2VudC5zdGR2WzE6bGVuZ3RoKHBlcmNlbnQuc3RkdikgLSAxXSAtIAogICAgICAgICAgICAgICAgICAgICAgIHBlcmNlbnQuc3RkdlsyOmxlbmd0aChwZXJjZW50LnN0ZHYpXSkgPiAwLjEpLCAKICAgICAgICAgICAgICBkZWNyZWFzaW5nID0gVClbMV0gKyAxCm1pbi5wYyA8LSBtaW4oY28xLCBjbzIpCm1pbi5wYwoKIyBDcmVhdGUgYSBkYXRhZnJhbWUgd2l0aCB2YWx1ZXMKcGxvdF9kZiA8LSBkYXRhLmZyYW1lKHBjdCA9IHBlcmNlbnQuc3RkdiwgCiAgICAgICAgICAgY3VtdSA9IGN1bXVsYXRpdmUsIAogICAgICAgICAgIHJhbmsgPSAxOmxlbmd0aChwZXJjZW50LnN0ZHYpKQoKIyBFbGJvdyBwbG90IHRvIHZpc3VhbGl6ZSAKICBnZ3Bsb3QocGxvdF9kZiwgYWVzKGN1bXVsYXRpdmUsIHBlcmNlbnQuc3RkdiwgbGFiZWwgPSByYW5rLCBjb2xvciA9IHJhbmsgPiBtaW4ucGMpKSArIAogIGdlb21fdGV4dCgpICsgCiAgZ2VvbV92bGluZSh4aW50ZXJjZXB0ID0gOTAsIGNvbG9yID0gImdyZXkiKSArIAogIGdlb21faGxpbmUoeWludGVyY2VwdCA9IG1pbihwZXJjZW50LnN0ZHZbcGVyY2VudC5zdGR2ID4gNV0pLCBjb2xvciA9ICJncmV5IikgKwogIHRoZW1lX2J3KCkKCiAgCgpgYGAKCiMgNi4gQ2x1c3RlcmluZwpgYGB7ciBDMSwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CgojICMgU2V0IHRoZSBzZWVkIGZvciBjbHVzdGVyaW5nIHN0ZXBzCiMgc2V0LnNlZWQoMTIzKQojIAojIGNvbWJpbmVkX21lcmdlZCA8LSBGaW5kTmVpZ2hib3JzKGNvbWJpbmVkX21lcmdlZCwgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaW1zID0gMTptaW4ucGMsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IEZBTFNFKQojIAojICMgdW5kZXJzdGFuZGluZyByZXNvbHV0aW9uCiMgY29tYmluZWRfbWVyZ2VkIDwtIEZpbmRDbHVzdGVycyhjb21iaW5lZF9tZXJnZWQsIAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdXRpb24gPSBjKDAuMywgMC40LCAwLjUsIDAuNiwgMC43LDAuOCwgMC45LCAxLCAxLjIsMS41KSkKIyAKCmBgYAoKIyMgVU1BUCBWaXN1YWxpemF0aW9uCmBgYHtyIEMyLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KIyAjIFNldCB0aGUgc2VlZCBmb3IgY2x1c3RlcmluZyBzdGVwcwojIHNldC5zZWVkKDEyMykKIyAKIyAjIG5vbi1saW5lYXIgZGltZW5zaW9uYWxpdHkgcmVkdWN0aW9uIC0tLS0tLS0tLS0tLS0tCiMgY29tYmluZWRfbWVyZ2VkIDwtIFJ1blVNQVAoY29tYmluZWRfbWVyZ2VkLCAKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpbXMgPSAxOm1pbi5wYywKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIHZlcmJvc2UgPSBGQUxTRSkKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgCiMgCiMgIyBEZWZpbmUgcmVzb2x1dGlvbiB2YWx1ZXMgdG8gcGxvdAojIHJlc29sdXRpb25zIDwtIGMoMC4zLCAwLjQsIDAuNSwgMC42LCAwLjcsMC44LCAwLjksIDEsIDEuMiwxLjUpCiMgCiMgIyBMb29wIHRocm91Z2ggYW5kIGdlbmVyYXRlIERpbVBsb3RzCiMgZm9yIChyZXMgaW4gcmVzb2x1dGlvbnMpIHsKIyAgIHJlc19jb2wgPC0gcGFzdGUwKCJTQ1Rfc25uX3Jlcy4iLCByZXMpCiMgICAKIyAgIHAgPC0gRGltUGxvdChjb21iaW5lZF9tZXJnZWQsCiMgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSByZXNfY29sLAojICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwKIyAgICAgICAgICAgICAgICBsYWJlbC5zaXplID0gMywKIyAgICAgICAgICAgICAgICByZXBlbCA9IFRSVUUsCiMgICAgICAgICAgICAgICAgbGFiZWwgPSBUUlVFLAojICAgICAgICAgICAgICAgIGxhYmVsLmJveCA9IFRSVUUpICsKIyAgICAgICAgZ2d0aXRsZShwYXN0ZSgiQ2x1c3RlcmluZyByZXNvbHV0aW9uOiIsIHJlcykpCiMgICAKIyAgIHByaW50KHApCiMgfQojIAoKIyBTZXQgaWRlbnRpdHkgY2xhc3NlcyB0byBhbiBleGlzdGluZyBjb2x1bW4gaW4gbWV0YSBkYXRhCklkZW50cyhvYmplY3QgPSBjb21iaW5lZF9tZXJnZWQpIDwtICJTQ1Rfc25uX3Jlcy4wLjQiCgpjbHVzdGVyX3RhYmxlIDwtIHRhYmxlKElkZW50cyhjb21iaW5lZF9tZXJnZWQpKQoKCmJhcnBsb3QoY2x1c3Rlcl90YWJsZSwgbWFpbiA9ICJOdW1iZXIgb2YgQ2VsbHMgaW4gRWFjaCBDbHVzdGVyIiwgCiAgICAgICAgICAgICAgICAgICAgICB4bGFiID0gIkNsdXN0ZXIiLCAKICAgICAgICAgICAgICAgICAgICAgIHlsYWIgPSAiTnVtYmVyIG9mIENlbGxzIiwgCiAgICAgICAgICAgICAgICAgICAgICBjb2wgPSByYWluYm93KGxlbmd0aChjbHVzdGVyX3RhYmxlKSkpCgpwcmludChjbHVzdGVyX3RhYmxlKQoKRGltUGxvdChjb21iaW5lZF9tZXJnZWQsIGdyb3VwLmJ5ID0gImJhdGNoIiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgIGxhYmVsLnNpemUgPSAzLAogICAgICAgIHJlcGVsID0gVCwKICAgICAgICBsYWJlbCA9IFQsIGxhYmVsLmJveCA9IFQpCgpgYGAKCiMgNy4gY2x1c1RyZWUKYGBge3IgY2x1c1RyZWUsIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD0xMH0KbGlicmFyeShjbHVzdHJlZSkKY2x1c3RyZWUoY29tYmluZWRfbWVyZ2VkLCBwcmVmaXggPSAiU0NUX3Nubl9yZXMuIikKYGBgCgojIDguIEF6aW11dGggQW5ub3RhdGlvbgpgYGB7ciBBemltdXRoLCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KbGlicmFyeShTZXVyYXQpCgojIEluc3RhbGxEYXRhKCJwYm1jcmVmIiwgZm9yY2UucmVpbnN0YWxsID0gVFJVRSkKIyAKIyAKIyByZWZlcmVuY2VfZGlyIDwtIHN5c3RlbS5maWxlKCJhemltdXRoIiwgcGFja2FnZSA9ICJwYm1jcmVmLlNldXJhdERhdGEiKQojIGlmIChyZWZlcmVuY2VfZGlyID09ICIiKSByZWZlcmVuY2VfZGlyIDwtIHN5c3RlbS5maWxlKHBhY2thZ2UgPSAicGJtY3JlZi5TZXVyYXREYXRhIikKIyBwcmludChyZWZlcmVuY2VfZGlyKQojIGxpc3QuZmlsZXMocmVmZXJlbmNlX2RpcikKCgoKIyBUaGUgUnVuQXppbXV0aCBmdW5jdGlvbiBjYW4gdGFrZSBhIFNldXJhdCBvYmplY3QgYXMgaW5wdXQKY29tYmluZWRfbWVyZ2VkIDwtIFJ1bkF6aW11dGgoY29tYmluZWRfbWVyZ2VkLCByZWZlcmVuY2UgPSAicGJtY3JlZiIpCgpgYGAKCiMgOS4gQXppbXV0aCBWaXN1YWxpemF0aW9uCmBgYHtyIEF6aW11dGhfVmlzdWFsaXphdGlvbiwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9MTB9CkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDEiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkKCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDEiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gRikKCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gVCwgbGFiZWwuYm94ID0gVCkKCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCBncm91cC5ieSA9ICJwcmVkaWN0ZWQuY2VsbHR5cGUubDIiLCAKICAgICAgICByZWR1Y3Rpb24gPSAidW1hcCIsCiAgICAgICAgbGFiZWwuc2l6ZSA9IDMsCiAgICAgICAgcmVwZWwgPSBULAogICAgICAgIGxhYmVsID0gRikKCgpEaW1QbG90KGNvbWJpbmVkX21lcmdlZCwgZ3JvdXAuYnkgPSAicHJlZGljdGVkLmNlbGx0eXBlLmwyIiwgCiAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLAogICAgICAgIGxhYmVsLnNpemUgPSAzLAogICAgICAgIHJlcGVsID0gVCwKICAgICAgICBsYWJlbCA9IEYpCgoKCnRhYmxlKGNvbWJpbmVkX21lcmdlZCRwcmVkaWN0ZWQuY2VsbHR5cGUubDIsIGNvbWJpbmVkX21lcmdlZCRTQ1Rfc25uX3Jlcy4wLjQpCgpgYGAKCiMjIFNhdmUgdGhlIFNldXJhdCBvYmplY3QgYXMgYW4gUm9iaiBmaWxlCmBgYHtyLCBlY2hvPVRSVUV9CgpzYXZlUkRTKGNvbWJpbmVkX21lcmdlZCwgZmlsZSA9ICJQb2dsaW9fR2F5ZG9zaWtfY29tYmluZWRfbWVyZ2VkX1NDVF9Ob3JtYWxpemVkLnJkcyIpCgpgYGAKCgoKIyAxMC4gSGFybW9ueSBJbnRlZ3JhdGlvbgpgYGB7ciBoYXJtb255LCBmaWcuaGVpZ2h0PTYsIGZpZy53aWR0aD0xMH0KCgojIExvYWQgcmVxdWlyZWQgbGlicmFyaWVzCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KGhhcm1vbnkpCmxpYnJhcnkoZ2dwbG90MikKCiMgUnVuIEhhcm1vbnksIGFkanVzdGluZyBmb3IgYmF0Y2ggZWZmZWN0IHVzaW5nICJjZWxsX2xpbmUiIG9yIGFub3RoZXIgZ3JvdXBpbmcgdmFyaWFibGUKY29tYmluZWRfbWVyZ2VkIDwtIFJ1bkhhcm1vbnkoCiAgY29tYmluZWRfbWVyZ2VkLAogIGdyb3VwLmJ5LnZhcnMgPSBjKCJiYXRjaCIpLCAgIyBSZXBsYWNlIHdpdGggdGhlIG1ldGFkYXRhIGNvbHVtbiBzcGVjaWZ5aW5nIGJhdGNoIG9yIGNlbGwgbGluZQogIGFzc2F5LnVzZT0iU0NUIikKCiMgQ2hlY2sgcmVzdWx0cyBpbiBoYXJtb255IGVtYmVkZGluZ3MKaGFybW9ueV9lbWJlZGRpbmdzIDwtIEVtYmVkZGluZ3MoY29tYmluZWRfbWVyZ2VkLCByZWR1Y3Rpb24gPSAiaGFybW9ueSIpCmhlYWQoaGFybW9ueV9lbWJlZGRpbmdzKQoKIyBTZXQgdGhlIHNlZWQgZm9yIGNsdXN0ZXJpbmcgc3RlcHMKc2V0LnNlZWQoMTIzKQoKIyBSdW4gVU1BUCBvbiBIYXJtb255IGVtYmVkZGluZ3MKY29tYmluZWRfbWVyZ2VkIDwtIFJ1blVNQVAoY29tYmluZWRfbWVyZ2VkLCByZWR1Y3Rpb24gPSAiaGFybW9ueSIsIGRpbXMgPSAxOjE2KQoKIyBTZXQgdGhlIHNlZWQgZm9yIGNsdXN0ZXJpbmcgc3RlcHMKc2V0LnNlZWQoMTIzKQoKIyBPcHRpb25hbGx5LCBmaW5kIG5laWdoYm9ycyBhbmQgY2x1c3RlcnMgKGlmIHlvdSBwbGFuIHRvIGRvIGNsdXN0ZXJpbmcgYW5hbHlzaXMpCmNvbWJpbmVkX21lcmdlZCA8LSBGaW5kTmVpZ2hib3JzKGNvbWJpbmVkX21lcmdlZCwgcmVkdWN0aW9uID0gImhhcm1vbnkiLCBkaW1zID0gMToxNikKY29tYmluZWRfbWVyZ2VkIDwtIEZpbmRDbHVzdGVycyhjb21iaW5lZF9tZXJnZWQsIHJlZHVjdGlvbiA9ICJoYXJtb255IiwgcmVzb2x1dGlvbiA9IDAuNSkgICMgQWRqdXN0IHJlc29sdXRpb24gYXMgbmVlZGVkCgojIFZpc3VhbGl6ZSBVTUFQCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImJhdGNoIiwgbGFiZWwgPSBUUlVFLCBwdC5zaXplID0gMC41KSArCiAgICBnZ3RpdGxlKCJVTUFQIG9mIEhhcm1vbnktSW50ZWdyYXRlZCBEYXRhIikKCgojIFZpc3VhbGl6ZSBVTUFQIHdpdGggYmF0Y2gvY2VsbCBsaW5lIGluZm9ybWF0aW9uCkRpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gImJhdGNoIiwgbGFiZWwgPSBUUlVFLCBwdC5zaXplID0gMC41KSArCiAgICBnZ3RpdGxlKCJVTUFQIC0gQ29sb3JlZCBieSBDZWxsIExpbmUgKEFmdGVyIEhhcm1vbnkgSW50ZWdyYXRpb24pIikKCgojIFZpc3VhbGl6ZSBVTUFQIHdpdGggY2x1c3RlcnMKRGltUGxvdChjb21iaW5lZF9tZXJnZWQsIHJlZHVjdGlvbiA9ICJ1bWFwIiwgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwgbGFiZWwgPSBUUlVFLCBwdC5zaXplID0gMC41KSArCiAgICBnZ3RpdGxlKCJVTUFQIC0gQ2x1c3RlcmVkIERhdGEgKEFmdGVyIEhhcm1vbnkgSW50ZWdyYXRpb24pIikKCiMgIyBWaXN1YWxpemUgc3BlY2lmaWMgY2VsbCB0eXBlcyBvciBvdGhlciBtZXRhZGF0YQojIERpbVBsb3QoY29tYmluZWRfbWVyZ2VkLCByZWR1Y3Rpb24gPSAidW1hcCIsIGdyb3VwLmJ5ID0gInByZWRpY3RlZC5jZWxsdHlwZS5sMiIsIGxhYmVsID0gVFJVRSwgcHQuc2l6ZSA9IDAuNSkgKwojICAgICBnZ3RpdGxlKCJVTUFQIC0gQ2VsbCBUeXBlcyBBZnRlciBIYXJtb255IEludGVncmF0aW9uIikKCgoKYGBgCgoKIyMgU2F2ZSB0aGUgU2V1cmF0IG9iamVjdCBhcyBhbiBSb2JqIGZpbGUKYGBge3IsIGVjaG89VFJVRX0KCnNhdmVSRFMoY29tYmluZWRfbWVyZ2VkLCBmaWxlID0gIlBvZ2xpb19HYXlkb3Npa19jb21iaW5lZF9tZXJnZWRfSGFybW9uaXplZC5yZHMiKQoKYGBgCgoKCgo=