Load data

samples.df <- readRDS(file.path(dir.objects, "metadata.rds"))
samples.df
so.astro <- readRDS(file.path(dir.objects, "so.astro.rds"))

#[email protected]$ident <- as.factor([email protected]$integrated_snn_res.0.2)

Idents(so.astro) <- "integrated_snn_res.0.2"

# Prepare a vector of colors for the clusters
clusters <- levels(Idents(so.astro)) %>% as.numeric()
cluster.cols <- muscat:::.cluster_colors[seq_along(clusters)]
names(cluster.cols) <- clusters

clusters
 [1] 0 1 2 3 4 5 6 7 8 9
astro.markers <- readRDS(file.path(dir.objects, "astro.markers.v1.rds")) %>% 
  remove_rownames() %>% 
  mutate(gene.name = word(gene, 2, sep = "\\."))
head(astro.markers)

Fig. 2b. Astrocytes saline vs LPS by sex

DimPlot(
  so.astro,
  reduction = "tsne",
  group.by = "integrated_snn_res.0.2",
  split.by = "group_id"
) +
  scale_color_manual(values = cluster.cols)

Fig. 2c. Genes statistically enriched in each cluster

Subset of genes used for plotting

astro.markers.subset <- astro.markers %>% 
  group_by(cluster) %>%
  slice_max(n = 15, order_by = avg_log2FC)
astro.markers.subset
# Expression matrix
cells_to_clusters <- data.frame(cluster = so.astro$integrated_snn_res.0.2) %>% 
  rownames_to_column("cell")

geneset <- unique(astro.markers.subset$gene)

mat.scale.ours_data <-
  so.astro@assays$integrated@scale.data[geneset, cells_to_clusters$cell]

cluster_indices <-
  split(cells_to_clusters$cell, cells_to_clusters$cluster)

cluster_means <- sapply(cluster_indices, function(indices) {
  Matrix::rowMeans(mat.scale.ours_data[, indices, drop = FALSE])
})

rownames(cluster_means) <-
  word(rownames(cluster_means), 2, sep = "\\.")

# Gene cluster color bar annotation
# by cluster order
genes.clusters.cols <- astro.markers.subset %>%
  select(gene, cluster) %>%
  mutate(gene.full = gene,
         gene = word(gene.full, 2, sep = "\\.")) %>%
  group_by(gene) %>%
  slice_min(n = 1, order_by = cluster) %>%
  ungroup() %>%
  arrange(cluster) %>%
  left_join(., enframe(cluster.cols, name = "cluster", value = "colors"))
rownames(genes.clusters.cols) <- genes.clusters.cols$gene
genes.clusters.cols <-
  genes.clusters.cols[rownames(cluster_means),]

row.anno.genes <-
  rowAnnotation(
    "Cluster" = genes.clusters.cols$cluster,
    col = list("Cluster" = cluster.cols),
    show_annotation_name = FALSE
  )

# Define the range and breaks
breaks <-
  seq(0, 1, length.out = 11)  # This creates 11 points between 0 and 1

colors <- plasma(11)

# Create a color function that maps the range [0, 1] to the viridis palette
color_mapping <- colorRamp2(breaks, colors)

# Use the adjusted col argument
Heatmap(
  cluster_means,
  name = "Normal. expression",
  col = color_mapping,
  cluster_rows = FALSE,
  show_row_dend = FALSE,
  cluster_columns = FALSE,
  column_names_side = "bottom",
  row_names_side = "left",
  column_names_rot = 0,
  column_names_centered = TRUE,
  row_names_gp = gpar(fontsize = 8),
  left_annotation = row.anno.genes
)

Spatial plots

# Specify the directory containing the H5 file and the image data
dir.Spatial_H5 <- file.path(wd, "GSM5026144")

# Load the data into a Seurat object
GSM5026144_S7_Seurat <- Load10X_Spatial(
  data.dir = dir.Spatial_H5,
  filename = "GSM5026144_S7_filtered_feature_bc_matrix.h5",
  assay = "Spatial",
  slice = "slice1",
  filter.matrix = TRUE,
  to.upper = FALSE,
  image = NULL
)
GSM5026144_S7_Seurat <-
  SCTransform(
    object = GSM5026144_S7_Seurat,
    assay = 'Spatial',
    return.only.var.genes = F,
    verbose = FALSE
  )
GSM5026144_S7_Seurat
An object of class Seurat 
50573 features across 2511 samples within 2 assays 
Active assay: SCT (18288 features, 3000 variable features)
 3 layers present: counts, data, scale.data
 1 other assay present: Spatial
 1 image present: slice1
astrocyte_markers <- c("Aldh1l1", "Slc1a2", "S100b", "Gfap", "Myoc")

cluster_0.module <- list(c("Gm42418", "Arhgap31", "Kcnq1ot1", "Rmst", "Cit", "Gli1", "Ttll3", "Neat1", "Gm26917"))
cluster_1.module <- list(c("Mfge8", "Igfbp2", "Btbd17"))
cluster_2.module <- list(c("Dbi", "Agt", "Nnat", "Ppia", "Tmsb4x", "Scrg1", "Mt3", "Gstm1", "Nkain4", "S100a1"))
cluster_3.module <- list(c("Thrsp", "Actb", "Chchd10", "Hint1", "Mrpl41", "Mpc1", "Camk2n1", "Ndufc2", "Nupr1", "Igfbp2"))
cluster_4.module <- list(c("Lcn2", "Ifitm3", "Timp1", "B2m", "Vim", "Gfap", "Psmb8", "Bst2"))
cluster_5.module <- list(c("Crym", "Prss56", "Slc43a3", "Mgst1", "Slc1a5", "Tuba1a", "Itm2b", "Chil1", "Ptn"))
cluster_6.module <- list(c("Thbs4", "Igfbp5", "Fxyd6", "Gsn", "Fxyd1", "Cd9", "Meg3"))
cluster_7.module <- list(c("Itih3", "Sparc", "Nkx6-2", "Etnppl", "Gria1", "Spon1", "5031439G07Rik"))
cluster_8.module <- list(c("Igtp", "Gm4951", "Ifit3", "Iigp1", "Irgm1", "Tap1", "Gbp2", "Ifit1", "Isg15", "Cxcl10"))
cluster_9.module <- list(c("Zic1", "Slc38a1", "Col1a2", "Kcnk2", "Luzp2", "Mgll", "Zic4", "Hopx"))

cluster_modules.all <- c("cluster_0.module", "cluster_1.module", "cluster_2.module", "cluster_3.module", "cluster_4.module", "cluster_5.module", "cluster_6.module", "cluster_7.module", "cluster_8.module", "cluster_9.module")

for (gene in astrocyte_markers) {
  GSM5026144_S7_Seurat <-
    AddModuleScore(object = GSM5026144_S7_Seurat,
                   features = list(gene),
                   name = gene)
}

for (cluster_module_name in cluster_modules.all) {
  # Retrieve the actual module list using get()
  cluster_module <- get(cluster_module_name)

  # Add the module score to the Seurat object
  GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_module, name = cluster_module_name)
}
Warning: The following features are not present in the object: Gm4951, not searching for symbol synonyms
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_0.module, name = "Cluster0_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_1.module, name = "Cluster1_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_2.module, name = "Cluster2_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_3.module, name = "Cluster3_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_4.module, name = "Cluster4_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_5.module, name = "Cluster5_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_6.module, name = "Cluster6_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_7.module, name = "Cluster7_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_8.module, name = "Cluster8_ModuleScore")
# GSM5026144_S7_Seurat <- AddModuleScore(object = GSM5026144_S7_Seurat, features = cluster_9.module, name = "Cluster9_ModuleScore")
# List to store individual plots
plots <- list()

features_list <- list(
  "Aldh1l11" = "Aldh1l1",
  "Slc1a21" = "Slc1a2",
  "S100b1" = "S100b",
  "Gfap1" = "Gfap",
  "Myoc1" = "Myoc",
  "cluster_0.module1" = "0",
  "cluster_1.module1" = "1",
  "cluster_2.module1" = "2",
  "cluster_3.module1" = "3",
  "cluster_4.module1" = "4",
  "cluster_5.module1" = "5",
  "cluster_6.module1" = "6",
  "cluster_7.module1" = "7",
  "cluster_8.module1" = "8",
  "cluster_9.module1" = "9"
)

legend_only <-
  SpatialPlot(
    GSM5026144_S7_Seurat,
    features = "cluster_9.module1",
    pt.size.factor = 0,
    image.alpha = 0
  ) + theme(legend.text = element_blank(), legend.title = element_blank())

# Add the legend_only plot to the the plots list
plots[[length(plots) + 1]] <- legend_only

# Create an image-only ggplot
image_only <-
  SpatialPlot(GSM5026144_S7_Seurat,
              features = "cluster_9.module1",
              pt.size.factor = 0) + theme(legend.position = "none") +
  annotate(
    "text",
    x = Inf,
    y = Inf,
    label = "H&E",
    hjust = 1.1,
    vjust = 1.1,
    fontface = "italic",
    color = "black",
    size = 2
  )
  
# Add the image-only plot to the the plots list
plots[[length(plots) + 1]] <- image_only

# Generate the plots
for (feature in names(features_list)) {
  # Create a spatial feature plot for each feature
  p <- SpatialFeaturePlot(GSM5026144_S7_Seurat, features = feature) + 
       theme(legend.position = "none") +
       annotate("text", x = Inf, y = Inf, label = features_list[[feature]], 
                hjust = 1.1, vjust = 1.1, fontface = "italic", color = "black", size = 2) 

  # Store the plot in the list
  plots[[feature]] <- p
}
# Arrange plots in a grid
grid.arrange(grobs = plots, ncol = 5)

With our analysis markers at 0.35 LogFC Cutoff

# Load the data into a Seurat object
ours_GSM5026144_S7_Seurat <- Load10X_Spatial(
  data.dir = dir.Spatial_H5,
  filename = "GSM5026144_S7_filtered_feature_bc_matrix.h5",
  assay = "Spatial",
  slice = "slice1",
  filter.matrix = TRUE,
  to.upper = FALSE,
  image = NULL
)

ours_GSM5026144_S7_Seurat <-
  SCTransform(
    ours_GSM5026144_S7_Seurat,
    assay = 'Spatial',
    return.only.var.genes = F,
    verbose = FALSE
  )
# Subset data for avg_logFC > 0.35
astro.cluster_markers.subset.0.35 <- astro.markers[astro.markers$avg_log2FC > 0.35, ]

# Remove ensemble ID from gene names
astro.cluster_markers.subset.0.35$gene <- sub(".*\\.", "", astro.cluster_markers.subset.0.35$gene)

# Specify the list of clusters
specified_clusters <- c(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)

# Loop through each specified cluster
for (cluster in specified_clusters) {
  
  # Subset data for the current cluster
  cluster_data <- astro.cluster_markers.subset.0.35[astro.cluster_markers.subset.0.35$cluster == cluster, ]
  
  # Create a comma-separated gene list with each gene in quotes
  gene_list <- sprintf('"%s"', cluster_data$gene)
  gene_list_string <- paste(gene_list, collapse = ", ")
  
  # Create a list with the gene names and assign it to a named variable
  variable_name <- paste0("cluster_", cluster, "_ours_module")
  assign(variable_name, list(eval(parse(text = paste("c(", gene_list_string, ")")))), envir = .GlobalEnv)
}

# Initialize an empty list to store the cluster variables
cluster_vars <- list()

# Loop through each specified cluster
for (cluster in specified_clusters) {
  
  # Subset data for the current cluster
  cluster_data <- astro.cluster_markers.subset.0.35[astro.cluster_markers.subset.0.35$cluster == cluster, ]
  
  # Create a comma-separated gene list with each gene in quotes
  gene_list <- sprintf('"%s"', cluster_data$gene)
  gene_list_string <- paste(gene_list, collapse = ", ")
  
  # Create a list with the gene names and assign it to a named variable
  variable_name <- paste0("cluster_", cluster, "_ours_module")
  variable_value <- list(eval(parse(text = paste("c(", gene_list_string, ")"))))
  assign(variable_name, variable_value, envir = .GlobalEnv)
  
  # Add the variable name to the list of cluster variables
  cluster_vars <- append(cluster_vars, variable_name)
}

cluster_vars 
[[1]]
[1] "cluster_0_ours_module"

[[2]]
[1] "cluster_1_ours_module"

[[3]]
[1] "cluster_2_ours_module"

[[4]]
[1] "cluster_3_ours_module"

[[5]]
[1] "cluster_4_ours_module"

[[6]]
[1] "cluster_5_ours_module"

[[7]]
[1] "cluster_6_ours_module"

[[8]]
[1] "cluster_7_ours_module"

[[9]]
[1] "cluster_8_ours_module"

[[10]]
[1] "cluster_9_ours_module"
for (gene in astrocyte_markers) {
  ours_GSM5026144_S7_Seurat <-
    AddModuleScore(object = ours_GSM5026144_S7_Seurat, features = list(gene), name = gene)
}

# Loop through each cluster variable
for (cluster_var in cluster_vars) {
  
  # Extract the cluster number from the variable name
  cluster_number <- gsub("\\D", "", cluster_var)
  
  # Create the name for the module score
  module_score_name <- paste("cluster_", cluster_number, ".ours_module", sep = "")
  
  # Add the module score
  ours_GSM5026144_S7_Seurat <- AddModuleScore(
    object = ours_GSM5026144_S7_Seurat,
    features = get(cluster_var),
    name = module_score_name
  )
}
Warning: The following features are not present in the object: Fam212b, not searching for symbol synonymsWarning: The following features are not present in the object: 1110008F13Rik, Usmg5, 2410015M20Rik, Pyurf, 2010107E04Rik, 1500011K16Rik, Minos1, Fam213a, Tmem5, Aes, Pla2g16, D10Jhu81e, 1110004E09Rik, Fam96a, Lao1, 5930412G12Rik, 1, not searching for symbol synonymsWarning: The following features are not present in the object: Gm26772, Gm26870, Cd27, Gm10600, C130073E24Rik, Gm11713, Gm26909, 1810041L15Rik, B430010I23Rik, A330069E16Rik, not searching for symbol synonymsWarning: The following features are not present in the object: Fam213a, Pyurf, Pla2g16, 1, 1110004E09Rik, 5930412G12Rik, not searching for symbol synonymsWarning: The following features are not present in the object: Cxcl9, Mx1, Gm4951, 2410015M20Rik, Tmem5, Fam213a, F830016B08Rik, Pla2g16, 1500015O10Rik, 1, Fam19a5, Gm8186, 2900011O08Rik, Slc10a6, not searching for symbol synonymsWarning: The following features are not present in the object: Slc10a6, 1110004E09Rik, Cxcl9, Gm10600, 1, 1500015O10Rik, 2900011O08Rik, Cyr61, Gm8186, Fam19a5, not searching for symbol synonymsWarning: The following features are not present in the object: 1500015O10Rik, C130073E24Rik, Mx1, Gm26772, Gm26909, Cxcl9, Fam19a5, 1, Fam212b, 1500011K16Rik, 2900011O08Rik, 2410015M20Rik, Gm8186, Aes, Pyurf, Minos1, Usmg5, Pla2g16, Gm26870, D10Jhu81e, 2010107E04Rik, 1110004E09Rik, not searching for symbol synonymsWarning: The following features are not present in the object: Fam19a1, 1810041L15Rik, Cd27, not searching for symbol synonymsWarning: The following features are not present in the object: Gm4951, F830016B08Rik, Slc10a6, Cxcl9, Mx1, Slco1b2, not searching for symbol synonymsWarning: The following features are not present in the object: 2010107E04Rik, Fam213a, Usmg5, 2410015M20Rik, Minos1, Cxcl9, 1500011K16Rik, 1500015O10Rik, Aes, Gm8186, 2900011O08Rik, 1110008F13Rik, Lao1, Pla2g16, not searching for symbol synonyms
# List to store individual plots
our_plots <- list()

features_list <- list(
  "Aldh1l11" = "Aldh1l1",
  "Slc1a21" = "Slc1a2",
  "S100b1" = "S100b",
  "Gfap1" = "Gfap",
  "Myoc1" = "Myoc",
  "cluster_0.ours_module1" = "0",
  "cluster_1.ours_module1" = "1",
  "cluster_2.ours_module1" = "2",
  "cluster_3.ours_module1" = "3",
  "cluster_4.ours_module1" = "4",
  "cluster_5.ours_module1" = "5",
  "cluster_6.ours_module1" = "6",
  "cluster_7.ours_module1" = "7",
  "cluster_8.ours_module1" = "8",
  "cluster_9.ours_module1" = "9"
)

ours_legend_only <- SpatialPlot(ours_GSM5026144_S7_Seurat, features = "cluster_9.ours_module1", pt.size.factor = 0, image.alpha = 0) + theme(legend.text = element_blank(), legend.title = element_blank())

# Add the legend_only plot to the the plots list
our_plots[[length(our_plots) + 1]] <- ours_legend_only

# Create an image-only ggplot
ours_image_only <- SpatialPlot(ours_GSM5026144_S7_Seurat, features = "cluster_9.ours_module1", pt.size.factor = 0) + theme(legend.position = "none")
  
# Add the image-only plot to the the plots list
our_plots[[length(our_plots) + 1]] <- ours_image_only

# Generate the plots
for (feature in names(features_list)) {
  # Create a spatial feature plot for each feature
  p <- SpatialFeaturePlot(ours_GSM5026144_S7_Seurat, features = feature) + 
       theme(legend.position = "none") +
       annotate("text", x = Inf, y = Inf, label = features_list[[feature]], 
                hjust = 1.1, vjust = 1.1, fontface = "italic", color = "black", size = 2)  
  # Store the plot in the list
  our_plots[[feature]] <- p
}
# Arrange plots in a grid
grid.arrange(grobs = our_plots, ncol = 5)

sessionInfo()
LS0tCnRpdGxlOiAiQXN0cm9jeXRlcyAtIGNsdXN0ZXIgYW5hbHlzaXMiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICB0b2M6IHllcwogIGh0bWxfbm90ZWJvb2s6CiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGhpZ2hsaWdodDoga2F0ZQogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDoKICAgICAgdG9jX2NvbGxhcHNlZDogbm8KLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZSA9IEZBTFNFfQpsaWJyYXJ5KGNvd3Bsb3QpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoZ3JpZEV4dHJhKQpsaWJyYXJ5KG11c2NhdCkKbGlicmFyeShwdXJycikKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShVcFNldFIpCmxpYnJhcnkoU2V1cmF0KQpsaWJyYXJ5KENvbXBsZXhIZWF0bWFwKQpsaWJyYXJ5KGNpcmNsaXplKQpsaWJyYXJ5KHZpcmlkaXMpCmxpYnJhcnkoZ2dyaWRnZXMpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoaW1hZ2VyKQoKIyBhYnNvbHV0ZSBwYXRoIHRvIHRoZSB3b3JraW5nIGRpcmVjdG9yeSBhbmQgdGhlIGN1cnJlbnQgb2JqZWN0cyBkaXJlY3RvcnkKd2QgPC0gIi9ob21lL2JlbmNoLXVzZXIvZGF0YSIKZGlyLm9iamVjdHMgPC0gZmlsZS5wYXRoKHdkLCAib2JqZWN0cyIsICJudi52MV8yMDIzLTExLTA1LyIpCmBgYAoKIyBMb2FkIGRhdGEKCmBgYHtyfQpzYW1wbGVzLmRmIDwtIHJlYWRSRFMoZmlsZS5wYXRoKGRpci5vYmplY3RzLCAibWV0YWRhdGEucmRzIikpCnNhbXBsZXMuZGYKYGBgCgoKYGBge3IgbG9hZC1kYXRhfQpzby5hc3RybyA8LSByZWFkUkRTKGZpbGUucGF0aChkaXIub2JqZWN0cywgInNvLmFzdHJvLnJkcyIpKQoKI3NvLmFzdHJvQG1ldGEuZGF0YSRpZGVudCA8LSBhcy5mYWN0b3Ioc28uYXN0cm9AbWV0YS5kYXRhJGludGVncmF0ZWRfc25uX3Jlcy4wLjIpCgpJZGVudHMoc28uYXN0cm8pIDwtICJpbnRlZ3JhdGVkX3Nubl9yZXMuMC4yIgoKIyBQcmVwYXJlIGEgdmVjdG9yIG9mIGNvbG9ycyBmb3IgdGhlIGNsdXN0ZXJzCmNsdXN0ZXJzIDwtIGxldmVscyhJZGVudHMoc28uYXN0cm8pKSAlPiUgYXMubnVtZXJpYygpCmNsdXN0ZXIuY29scyA8LSBtdXNjYXQ6OjouY2x1c3Rlcl9jb2xvcnNbc2VxX2Fsb25nKGNsdXN0ZXJzKV0KbmFtZXMoY2x1c3Rlci5jb2xzKSA8LSBjbHVzdGVycwoKY2x1c3RlcnMKYGBgCgpgYGB7cn0KYXN0cm8ubWFya2VycyA8LSByZWFkUkRTKGZpbGUucGF0aChkaXIub2JqZWN0cywgImFzdHJvLm1hcmtlcnMudjEucmRzIikpICU+JSAKICByZW1vdmVfcm93bmFtZXMoKSAlPiUgCiAgbXV0YXRlKGdlbmUubmFtZSA9IHdvcmQoZ2VuZSwgMiwgc2VwID0gIlxcLiIpKQpoZWFkKGFzdHJvLm1hcmtlcnMpCmBgYAoKIyBGaWcuIDJiLiBBc3Ryb2N5dGVzIHNhbGluZSB2cyBMUFMgYnkgc2V4CgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTR9CkRpbVBsb3QoCiAgc28uYXN0cm8sCiAgcmVkdWN0aW9uID0gInRzbmUiLAogIGdyb3VwLmJ5ID0gImludGVncmF0ZWRfc25uX3Jlcy4wLjIiLAogIHNwbGl0LmJ5ID0gImdyb3VwX2lkIgopICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzID0gY2x1c3Rlci5jb2xzKQpgYGAKCiMgRmlnLiAyYy4gR2VuZXMgc3RhdGlzdGljYWxseSBlbnJpY2hlZCBpbiBlYWNoIGNsdXN0ZXIKCiMjIyBTdWJzZXQgb2YgZ2VuZXMgdXNlZCBmb3IgcGxvdHRpbmcKCmBgYHtyIGZpZy5oZWlnaHQ9MTIsIGZpZy53aWR0aD01LCBtZXNzYWdlPUZBTFNFfQphc3Ryby5tYXJrZXJzLnN1YnNldCA8LSBhc3Ryby5tYXJrZXJzICU+JSAKICBncm91cF9ieShjbHVzdGVyKSAlPiUKICBzbGljZV9tYXgobiA9IDE1LCBvcmRlcl9ieSA9IGF2Z19sb2cyRkMpCmFzdHJvLm1hcmtlcnMuc3Vic2V0CmBgYAoKCgoKYGBge3IgZmlnLmhlaWdodD0xNCwgZmlnLndpZHRoPTgsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9CiMgRXhwcmVzc2lvbiBtYXRyaXgKY2VsbHNfdG9fY2x1c3RlcnMgPC0gZGF0YS5mcmFtZShjbHVzdGVyID0gc28uYXN0cm8kaW50ZWdyYXRlZF9zbm5fcmVzLjAuMikgJT4lIAogIHJvd25hbWVzX3RvX2NvbHVtbigiY2VsbCIpCgpnZW5lc2V0IDwtIHVuaXF1ZShhc3Ryby5tYXJrZXJzLnN1YnNldCRnZW5lKQoKbWF0LnNjYWxlLm91cnNfZGF0YSA8LQogIHNvLmFzdHJvQGFzc2F5cyRpbnRlZ3JhdGVkQHNjYWxlLmRhdGFbZ2VuZXNldCwgY2VsbHNfdG9fY2x1c3RlcnMkY2VsbF0KCmNsdXN0ZXJfaW5kaWNlcyA8LQogIHNwbGl0KGNlbGxzX3RvX2NsdXN0ZXJzJGNlbGwsIGNlbGxzX3RvX2NsdXN0ZXJzJGNsdXN0ZXIpCgpjbHVzdGVyX21lYW5zIDwtIHNhcHBseShjbHVzdGVyX2luZGljZXMsIGZ1bmN0aW9uKGluZGljZXMpIHsKICBNYXRyaXg6OnJvd01lYW5zKG1hdC5zY2FsZS5vdXJzX2RhdGFbLCBpbmRpY2VzLCBkcm9wID0gRkFMU0VdKQp9KQoKcm93bmFtZXMoY2x1c3Rlcl9tZWFucykgPC0KICB3b3JkKHJvd25hbWVzKGNsdXN0ZXJfbWVhbnMpLCAyLCBzZXAgPSAiXFwuIikKCiMgR2VuZSBjbHVzdGVyIGNvbG9yIGJhciBhbm5vdGF0aW9uCiMgYnkgY2x1c3RlciBvcmRlcgpnZW5lcy5jbHVzdGVycy5jb2xzIDwtIGFzdHJvLm1hcmtlcnMuc3Vic2V0ICU+JQogIHNlbGVjdChnZW5lLCBjbHVzdGVyKSAlPiUKICBtdXRhdGUoZ2VuZS5mdWxsID0gZ2VuZSwKICAgICAgICAgZ2VuZSA9IHdvcmQoZ2VuZS5mdWxsLCAyLCBzZXAgPSAiXFwuIikpICU+JQogIGdyb3VwX2J5KGdlbmUpICU+JQogIHNsaWNlX21pbihuID0gMSwgb3JkZXJfYnkgPSBjbHVzdGVyKSAlPiUKICB1bmdyb3VwKCkgJT4lCiAgYXJyYW5nZShjbHVzdGVyKSAlPiUKICBsZWZ0X2pvaW4oLiwgZW5mcmFtZShjbHVzdGVyLmNvbHMsIG5hbWUgPSAiY2x1c3RlciIsIHZhbHVlID0gImNvbG9ycyIpKQpyb3duYW1lcyhnZW5lcy5jbHVzdGVycy5jb2xzKSA8LSBnZW5lcy5jbHVzdGVycy5jb2xzJGdlbmUKZ2VuZXMuY2x1c3RlcnMuY29scyA8LQogIGdlbmVzLmNsdXN0ZXJzLmNvbHNbcm93bmFtZXMoY2x1c3Rlcl9tZWFucyksXQoKcm93LmFubm8uZ2VuZXMgPC0KICByb3dBbm5vdGF0aW9uKAogICAgIkNsdXN0ZXIiID0gZ2VuZXMuY2x1c3RlcnMuY29scyRjbHVzdGVyLAogICAgY29sID0gbGlzdCgiQ2x1c3RlciIgPSBjbHVzdGVyLmNvbHMpLAogICAgc2hvd19hbm5vdGF0aW9uX25hbWUgPSBGQUxTRQogICkKCiMgRGVmaW5lIHRoZSByYW5nZSBhbmQgYnJlYWtzCmJyZWFrcyA8LQogIHNlcSgwLCAxLCBsZW5ndGgub3V0ID0gMTEpICAjIFRoaXMgY3JlYXRlcyAxMSBwb2ludHMgYmV0d2VlbiAwIGFuZCAxCgpjb2xvcnMgPC0gcGxhc21hKDExKQoKIyBDcmVhdGUgYSBjb2xvciBmdW5jdGlvbiB0aGF0IG1hcHMgdGhlIHJhbmdlIFswLCAxXSB0byB0aGUgdmlyaWRpcyBwYWxldHRlCmNvbG9yX21hcHBpbmcgPC0gY29sb3JSYW1wMihicmVha3MsIGNvbG9ycykKCiMgVXNlIHRoZSBhZGp1c3RlZCBjb2wgYXJndW1lbnQKSGVhdG1hcCgKICBjbHVzdGVyX21lYW5zLAogIG5hbWUgPSAiTm9ybWFsLiBleHByZXNzaW9uIiwKICBjb2wgPSBjb2xvcl9tYXBwaW5nLAogIGNsdXN0ZXJfcm93cyA9IEZBTFNFLAogIHNob3dfcm93X2RlbmQgPSBGQUxTRSwKICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICBjb2x1bW5fbmFtZXNfc2lkZSA9ICJib3R0b20iLAogIHJvd19uYW1lc19zaWRlID0gImxlZnQiLAogIGNvbHVtbl9uYW1lc19yb3QgPSAwLAogIGNvbHVtbl9uYW1lc19jZW50ZXJlZCA9IFRSVUUsCiAgcm93X25hbWVzX2dwID0gZ3Bhcihmb250c2l6ZSA9IDgpLAogIGxlZnRfYW5ub3RhdGlvbiA9IHJvdy5hbm5vLmdlbmVzCikKYGBgCgojIFNwYXRpYWwgcGxvdHMKCmBgYHtyfQojIFNwZWNpZnkgdGhlIGRpcmVjdG9yeSBjb250YWluaW5nIHRoZSBINSBmaWxlIGFuZCB0aGUgaW1hZ2UgZGF0YQpkaXIuU3BhdGlhbF9INSA8LSBmaWxlLnBhdGgod2QsICJHU001MDI2MTQ0IikKCiMgTG9hZCB0aGUgZGF0YSBpbnRvIGEgU2V1cmF0IG9iamVjdApHU001MDI2MTQ0X1M3X1NldXJhdCA8LSBMb2FkMTBYX1NwYXRpYWwoCiAgZGF0YS5kaXIgPSBkaXIuU3BhdGlhbF9INSwKICBmaWxlbmFtZSA9ICJHU001MDI2MTQ0X1M3X2ZpbHRlcmVkX2ZlYXR1cmVfYmNfbWF0cml4Lmg1IiwKICBhc3NheSA9ICJTcGF0aWFsIiwKICBzbGljZSA9ICJzbGljZTEiLAogIGZpbHRlci5tYXRyaXggPSBUUlVFLAogIHRvLnVwcGVyID0gRkFMU0UsCiAgaW1hZ2UgPSBOVUxMCikKYGBgCgoKYGBge3J9CkdTTTUwMjYxNDRfUzdfU2V1cmF0IDwtCiAgU0NUcmFuc2Zvcm0oCiAgICBvYmplY3QgPSBHU001MDI2MTQ0X1M3X1NldXJhdCwKICAgIGFzc2F5ID0gJ1NwYXRpYWwnLAogICAgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwKICAgIHZlcmJvc2UgPSBGQUxTRQogICkKR1NNNTAyNjE0NF9TN19TZXVyYXQKYGBgCgoKYGBge3J9CmFzdHJvY3l0ZV9tYXJrZXJzIDwtIGMoIkFsZGgxbDEiLCAiU2xjMWEyIiwgIlMxMDBiIiwgIkdmYXAiLCAiTXlvYyIpCgpjbHVzdGVyXzAubW9kdWxlIDwtIGxpc3QoYygiR200MjQxOCIsICJBcmhnYXAzMSIsICJLY25xMW90MSIsICJSbXN0IiwgIkNpdCIsICJHbGkxIiwgIlR0bGwzIiwgIk5lYXQxIiwgIkdtMjY5MTciKSkKY2x1c3Rlcl8xLm1vZHVsZSA8LSBsaXN0KGMoIk1mZ2U4IiwgIklnZmJwMiIsICJCdGJkMTciKSkKY2x1c3Rlcl8yLm1vZHVsZSA8LSBsaXN0KGMoIkRiaSIsICJBZ3QiLCAiTm5hdCIsICJQcGlhIiwgIlRtc2I0eCIsICJTY3JnMSIsICJNdDMiLCAiR3N0bTEiLCAiTmthaW40IiwgIlMxMDBhMSIpKQpjbHVzdGVyXzMubW9kdWxlIDwtIGxpc3QoYygiVGhyc3AiLCAiQWN0YiIsICJDaGNoZDEwIiwgIkhpbnQxIiwgIk1ycGw0MSIsICJNcGMxIiwgIkNhbWsybjEiLCAiTmR1ZmMyIiwgIk51cHIxIiwgIklnZmJwMiIpKQpjbHVzdGVyXzQubW9kdWxlIDwtIGxpc3QoYygiTGNuMiIsICJJZml0bTMiLCAiVGltcDEiLCAiQjJtIiwgIlZpbSIsICJHZmFwIiwgIlBzbWI4IiwgIkJzdDIiKSkKY2x1c3Rlcl81Lm1vZHVsZSA8LSBsaXN0KGMoIkNyeW0iLCAiUHJzczU2IiwgIlNsYzQzYTMiLCAiTWdzdDEiLCAiU2xjMWE1IiwgIlR1YmExYSIsICJJdG0yYiIsICJDaGlsMSIsICJQdG4iKSkKY2x1c3Rlcl82Lm1vZHVsZSA8LSBsaXN0KGMoIlRoYnM0IiwgIklnZmJwNSIsICJGeHlkNiIsICJHc24iLCAiRnh5ZDEiLCAiQ2Q5IiwgIk1lZzMiKSkKY2x1c3Rlcl83Lm1vZHVsZSA8LSBsaXN0KGMoIkl0aWgzIiwgIlNwYXJjIiwgIk5reDYtMiIsICJFdG5wcGwiLCAiR3JpYTEiLCAiU3BvbjEiLCAiNTAzMTQzOUcwN1JpayIpKQpjbHVzdGVyXzgubW9kdWxlIDwtIGxpc3QoYygiSWd0cCIsICJHbTQ5NTEiLCAiSWZpdDMiLCAiSWlncDEiLCAiSXJnbTEiLCAiVGFwMSIsICJHYnAyIiwgIklmaXQxIiwgIklzZzE1IiwgIkN4Y2wxMCIpKQpjbHVzdGVyXzkubW9kdWxlIDwtIGxpc3QoYygiWmljMSIsICJTbGMzOGExIiwgIkNvbDFhMiIsICJLY25rMiIsICJMdXpwMiIsICJNZ2xsIiwgIlppYzQiLCAiSG9weCIpKQoKY2x1c3Rlcl9tb2R1bGVzLmFsbCA8LSBjKCJjbHVzdGVyXzAubW9kdWxlIiwgImNsdXN0ZXJfMS5tb2R1bGUiLCAiY2x1c3Rlcl8yLm1vZHVsZSIsICJjbHVzdGVyXzMubW9kdWxlIiwgImNsdXN0ZXJfNC5tb2R1bGUiLCAiY2x1c3Rlcl81Lm1vZHVsZSIsICJjbHVzdGVyXzYubW9kdWxlIiwgImNsdXN0ZXJfNy5tb2R1bGUiLCAiY2x1c3Rlcl84Lm1vZHVsZSIsICJjbHVzdGVyXzkubW9kdWxlIikKCmZvciAoZ2VuZSBpbiBhc3Ryb2N5dGVfbWFya2VycykgewogIEdTTTUwMjYxNDRfUzdfU2V1cmF0IDwtCiAgICBBZGRNb2R1bGVTY29yZShvYmplY3QgPSBHU001MDI2MTQ0X1M3X1NldXJhdCwKICAgICAgICAgICAgICAgICAgIGZlYXR1cmVzID0gbGlzdChnZW5lKSwKICAgICAgICAgICAgICAgICAgIG5hbWUgPSBnZW5lKQp9Cgpmb3IgKGNsdXN0ZXJfbW9kdWxlX25hbWUgaW4gY2x1c3Rlcl9tb2R1bGVzLmFsbCkgewogICMgUmV0cmlldmUgdGhlIGFjdHVhbCBtb2R1bGUgbGlzdCB1c2luZyBnZXQoKQogIGNsdXN0ZXJfbW9kdWxlIDwtIGdldChjbHVzdGVyX21vZHVsZV9uYW1lKQoKICAjIEFkZCB0aGUgbW9kdWxlIHNjb3JlIHRvIHRoZSBTZXVyYXQgb2JqZWN0CiAgR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0gQWRkTW9kdWxlU2NvcmUob2JqZWN0ID0gR1NNNTAyNjE0NF9TN19TZXVyYXQsIGZlYXR1cmVzID0gY2x1c3Rlcl9tb2R1bGUsIG5hbWUgPSBjbHVzdGVyX21vZHVsZV9uYW1lKQp9CmBgYAoKYGBge3J9CiMgTGlzdCB0byBzdG9yZSBpbmRpdmlkdWFsIHBsb3RzCnBsb3RzIDwtIGxpc3QoKQoKZmVhdHVyZXNfbGlzdCA8LSBsaXN0KAogICJBbGRoMWwxMSIgPSAiQWxkaDFsMSIsCiAgIlNsYzFhMjEiID0gIlNsYzFhMiIsCiAgIlMxMDBiMSIgPSAiUzEwMGIiLAogICJHZmFwMSIgPSAiR2ZhcCIsCiAgIk15b2MxIiA9ICJNeW9jIiwKICAiY2x1c3Rlcl8wLm1vZHVsZTEiID0gIjAiLAogICJjbHVzdGVyXzEubW9kdWxlMSIgPSAiMSIsCiAgImNsdXN0ZXJfMi5tb2R1bGUxIiA9ICIyIiwKICAiY2x1c3Rlcl8zLm1vZHVsZTEiID0gIjMiLAogICJjbHVzdGVyXzQubW9kdWxlMSIgPSAiNCIsCiAgImNsdXN0ZXJfNS5tb2R1bGUxIiA9ICI1IiwKICAiY2x1c3Rlcl82Lm1vZHVsZTEiID0gIjYiLAogICJjbHVzdGVyXzcubW9kdWxlMSIgPSAiNyIsCiAgImNsdXN0ZXJfOC5tb2R1bGUxIiA9ICI4IiwKICAiY2x1c3Rlcl85Lm1vZHVsZTEiID0gIjkiCikKCmxlZ2VuZF9vbmx5IDwtCiAgU3BhdGlhbFBsb3QoCiAgICBHU001MDI2MTQ0X1M3X1NldXJhdCwKICAgIGZlYXR1cmVzID0gImNsdXN0ZXJfOS5tb2R1bGUxIiwKICAgIHB0LnNpemUuZmFjdG9yID0gMCwKICAgIGltYWdlLmFscGhhID0gMAogICkgKyB0aGVtZShsZWdlbmQudGV4dCA9IGVsZW1lbnRfYmxhbmsoKSwgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpKQoKIyBBZGQgdGhlIGxlZ2VuZF9vbmx5IHBsb3QgdG8gdGhlIHRoZSBwbG90cyBsaXN0CnBsb3RzW1tsZW5ndGgocGxvdHMpICsgMV1dIDwtIGxlZ2VuZF9vbmx5CgojIENyZWF0ZSBhbiBpbWFnZS1vbmx5IGdncGxvdAppbWFnZV9vbmx5IDwtCiAgU3BhdGlhbFBsb3QoR1NNNTAyNjE0NF9TN19TZXVyYXQsCiAgICAgICAgICAgICAgZmVhdHVyZXMgPSAiY2x1c3Rlcl85Lm1vZHVsZTEiLAogICAgICAgICAgICAgIHB0LnNpemUuZmFjdG9yID0gMCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICBhbm5vdGF0ZSgKICAgICJ0ZXh0IiwKICAgIHggPSBJbmYsCiAgICB5ID0gSW5mLAogICAgbGFiZWwgPSAiSCZFIiwKICAgIGhqdXN0ID0gMS4xLAogICAgdmp1c3QgPSAxLjEsCiAgICBmb250ZmFjZSA9ICJpdGFsaWMiLAogICAgY29sb3IgPSAiYmxhY2siLAogICAgc2l6ZSA9IDIKICApCiAgCiMgQWRkIHRoZSBpbWFnZS1vbmx5IHBsb3QgdG8gdGhlIHRoZSBwbG90cyBsaXN0CnBsb3RzW1tsZW5ndGgocGxvdHMpICsgMV1dIDwtIGltYWdlX29ubHkKCiMgR2VuZXJhdGUgdGhlIHBsb3RzCmZvciAoZmVhdHVyZSBpbiBuYW1lcyhmZWF0dXJlc19saXN0KSkgewogICMgQ3JlYXRlIGEgc3BhdGlhbCBmZWF0dXJlIHBsb3QgZm9yIGVhY2ggZmVhdHVyZQogIHAgPC0gU3BhdGlhbEZlYXR1cmVQbG90KEdTTTUwMjYxNDRfUzdfU2V1cmF0LCBmZWF0dXJlcyA9IGZlYXR1cmUpICsgCiAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IEluZiwgeSA9IEluZiwgbGFiZWwgPSBmZWF0dXJlc19saXN0W1tmZWF0dXJlXV0sIAogICAgICAgICAgICAgICAgaGp1c3QgPSAxLjEsIHZqdXN0ID0gMS4xLCBmb250ZmFjZSA9ICJpdGFsaWMiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSAKCiAgIyBTdG9yZSB0aGUgcGxvdCBpbiB0aGUgbGlzdAogIHBsb3RzW1tmZWF0dXJlXV0gPC0gcAp9CmBgYAoKYGBge3IsIHdhcm5pbmc9RkFMU0UsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD0xMH0KIyBBcnJhbmdlIHBsb3RzIGluIGEgZ3JpZApncmlkLmFycmFuZ2UoZ3JvYnMgPSBwbG90cywgbmNvbCA9IDUpCmBgYAoKV2l0aCBvdXIgYW5hbHlzaXMgbWFya2VycyBhdCAwLjM1IExvZ0ZDIEN1dG9mZgoKYGBge3J9CiMgTG9hZCB0aGUgZGF0YSBpbnRvIGEgU2V1cmF0IG9iamVjdApvdXJzX0dTTTUwMjYxNDRfUzdfU2V1cmF0IDwtIExvYWQxMFhfU3BhdGlhbCgKICBkYXRhLmRpciA9IGRpci5TcGF0aWFsX0g1LAogIGZpbGVuYW1lID0gIkdTTTUwMjYxNDRfUzdfZmlsdGVyZWRfZmVhdHVyZV9iY19tYXRyaXguaDUiLAogIGFzc2F5ID0gIlNwYXRpYWwiLAogIHNsaWNlID0gInNsaWNlMSIsCiAgZmlsdGVyLm1hdHJpeCA9IFRSVUUsCiAgdG8udXBwZXIgPSBGQUxTRSwKICBpbWFnZSA9IE5VTEwKKQoKb3Vyc19HU001MDI2MTQ0X1M3X1NldXJhdCA8LQogIFNDVHJhbnNmb3JtKAogICAgb3Vyc19HU001MDI2MTQ0X1M3X1NldXJhdCwKICAgIGFzc2F5ID0gJ1NwYXRpYWwnLAogICAgcmV0dXJuLm9ubHkudmFyLmdlbmVzID0gRiwKICAgIHZlcmJvc2UgPSBGQUxTRQogICkKYGBgCgoKYGBge3J9CiMgU3Vic2V0IGRhdGEgZm9yIGF2Z19sb2dGQyA+IDAuMzUKYXN0cm8uY2x1c3Rlcl9tYXJrZXJzLnN1YnNldC4wLjM1IDwtIGFzdHJvLm1hcmtlcnNbYXN0cm8ubWFya2VycyRhdmdfbG9nMkZDID4gMC4zNSwgXQoKIyBSZW1vdmUgZW5zZW1ibGUgSUQgZnJvbSBnZW5lIG5hbWVzCmFzdHJvLmNsdXN0ZXJfbWFya2Vycy5zdWJzZXQuMC4zNSRnZW5lIDwtIHN1YigiLipcXC4iLCAiIiwgYXN0cm8uY2x1c3Rlcl9tYXJrZXJzLnN1YnNldC4wLjM1JGdlbmUpCgojIFNwZWNpZnkgdGhlIGxpc3Qgb2YgY2x1c3RlcnMKc3BlY2lmaWVkX2NsdXN0ZXJzIDwtIGMoMCwgMSwgMiwgMywgNCwgNSwgNiwgNywgOCwgOSkKCiMgTG9vcCB0aHJvdWdoIGVhY2ggc3BlY2lmaWVkIGNsdXN0ZXIKZm9yIChjbHVzdGVyIGluIHNwZWNpZmllZF9jbHVzdGVycykgewogIAogICMgU3Vic2V0IGRhdGEgZm9yIHRoZSBjdXJyZW50IGNsdXN0ZXIKICBjbHVzdGVyX2RhdGEgPC0gYXN0cm8uY2x1c3Rlcl9tYXJrZXJzLnN1YnNldC4wLjM1W2FzdHJvLmNsdXN0ZXJfbWFya2Vycy5zdWJzZXQuMC4zNSRjbHVzdGVyID09IGNsdXN0ZXIsIF0KICAKICAjIENyZWF0ZSBhIGNvbW1hLXNlcGFyYXRlZCBnZW5lIGxpc3Qgd2l0aCBlYWNoIGdlbmUgaW4gcXVvdGVzCiAgZ2VuZV9saXN0IDwtIHNwcmludGYoJyIlcyInLCBjbHVzdGVyX2RhdGEkZ2VuZSkKICBnZW5lX2xpc3Rfc3RyaW5nIDwtIHBhc3RlKGdlbmVfbGlzdCwgY29sbGFwc2UgPSAiLCAiKQogIAogICMgQ3JlYXRlIGEgbGlzdCB3aXRoIHRoZSBnZW5lIG5hbWVzIGFuZCBhc3NpZ24gaXQgdG8gYSBuYW1lZCB2YXJpYWJsZQogIHZhcmlhYmxlX25hbWUgPC0gcGFzdGUwKCJjbHVzdGVyXyIsIGNsdXN0ZXIsICJfb3Vyc19tb2R1bGUiKQogIGFzc2lnbih2YXJpYWJsZV9uYW1lLCBsaXN0KGV2YWwocGFyc2UodGV4dCA9IHBhc3RlKCJjKCIsIGdlbmVfbGlzdF9zdHJpbmcsICIpIikpKSksIGVudmlyID0gLkdsb2JhbEVudikKfQoKIyBJbml0aWFsaXplIGFuIGVtcHR5IGxpc3QgdG8gc3RvcmUgdGhlIGNsdXN0ZXIgdmFyaWFibGVzCmNsdXN0ZXJfdmFycyA8LSBsaXN0KCkKCiMgTG9vcCB0aHJvdWdoIGVhY2ggc3BlY2lmaWVkIGNsdXN0ZXIKZm9yIChjbHVzdGVyIGluIHNwZWNpZmllZF9jbHVzdGVycykgewogIAogICMgU3Vic2V0IGRhdGEgZm9yIHRoZSBjdXJyZW50IGNsdXN0ZXIKICBjbHVzdGVyX2RhdGEgPC0gYXN0cm8uY2x1c3Rlcl9tYXJrZXJzLnN1YnNldC4wLjM1W2FzdHJvLmNsdXN0ZXJfbWFya2Vycy5zdWJzZXQuMC4zNSRjbHVzdGVyID09IGNsdXN0ZXIsIF0KICAKICAjIENyZWF0ZSBhIGNvbW1hLXNlcGFyYXRlZCBnZW5lIGxpc3Qgd2l0aCBlYWNoIGdlbmUgaW4gcXVvdGVzCiAgZ2VuZV9saXN0IDwtIHNwcmludGYoJyIlcyInLCBjbHVzdGVyX2RhdGEkZ2VuZSkKICBnZW5lX2xpc3Rfc3RyaW5nIDwtIHBhc3RlKGdlbmVfbGlzdCwgY29sbGFwc2UgPSAiLCAiKQogIAogICMgQ3JlYXRlIGEgbGlzdCB3aXRoIHRoZSBnZW5lIG5hbWVzIGFuZCBhc3NpZ24gaXQgdG8gYSBuYW1lZCB2YXJpYWJsZQogIHZhcmlhYmxlX25hbWUgPC0gcGFzdGUwKCJjbHVzdGVyXyIsIGNsdXN0ZXIsICJfb3Vyc19tb2R1bGUiKQogIHZhcmlhYmxlX3ZhbHVlIDwtIGxpc3QoZXZhbChwYXJzZSh0ZXh0ID0gcGFzdGUoImMoIiwgZ2VuZV9saXN0X3N0cmluZywgIikiKSkpKQogIGFzc2lnbih2YXJpYWJsZV9uYW1lLCB2YXJpYWJsZV92YWx1ZSwgZW52aXIgPSAuR2xvYmFsRW52KQogIAogICMgQWRkIHRoZSB2YXJpYWJsZSBuYW1lIHRvIHRoZSBsaXN0IG9mIGNsdXN0ZXIgdmFyaWFibGVzCiAgY2x1c3Rlcl92YXJzIDwtIGFwcGVuZChjbHVzdGVyX3ZhcnMsIHZhcmlhYmxlX25hbWUpCn0KCmNsdXN0ZXJfdmFycyAKCmZvciAoZ2VuZSBpbiBhc3Ryb2N5dGVfbWFya2VycykgewogIG91cnNfR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0KICAgIEFkZE1vZHVsZVNjb3JlKG9iamVjdCA9IG91cnNfR1NNNTAyNjE0NF9TN19TZXVyYXQsIGZlYXR1cmVzID0gbGlzdChnZW5lKSwgbmFtZSA9IGdlbmUpCn0KCiMgTG9vcCB0aHJvdWdoIGVhY2ggY2x1c3RlciB2YXJpYWJsZQpmb3IgKGNsdXN0ZXJfdmFyIGluIGNsdXN0ZXJfdmFycykgewogIAogICMgRXh0cmFjdCB0aGUgY2x1c3RlciBudW1iZXIgZnJvbSB0aGUgdmFyaWFibGUgbmFtZQogIGNsdXN0ZXJfbnVtYmVyIDwtIGdzdWIoIlxcRCIsICIiLCBjbHVzdGVyX3ZhcikKICAKICAjIENyZWF0ZSB0aGUgbmFtZSBmb3IgdGhlIG1vZHVsZSBzY29yZQogIG1vZHVsZV9zY29yZV9uYW1lIDwtIHBhc3RlKCJjbHVzdGVyXyIsIGNsdXN0ZXJfbnVtYmVyLCAiLm91cnNfbW9kdWxlIiwgc2VwID0gIiIpCiAgCiAgIyBBZGQgdGhlIG1vZHVsZSBzY29yZQogIG91cnNfR1NNNTAyNjE0NF9TN19TZXVyYXQgPC0gQWRkTW9kdWxlU2NvcmUoCiAgICBvYmplY3QgPSBvdXJzX0dTTTUwMjYxNDRfUzdfU2V1cmF0LAogICAgZmVhdHVyZXMgPSBnZXQoY2x1c3Rlcl92YXIpLAogICAgbmFtZSA9IG1vZHVsZV9zY29yZV9uYW1lCiAgKQp9CmBgYAoKCgoKYGBge3J9CiMgTGlzdCB0byBzdG9yZSBpbmRpdmlkdWFsIHBsb3RzCm91cl9wbG90cyA8LSBsaXN0KCkKCmZlYXR1cmVzX2xpc3QgPC0gbGlzdCgKICAiQWxkaDFsMTEiID0gIkFsZGgxbDEiLAogICJTbGMxYTIxIiA9ICJTbGMxYTIiLAogICJTMTAwYjEiID0gIlMxMDBiIiwKICAiR2ZhcDEiID0gIkdmYXAiLAogICJNeW9jMSIgPSAiTXlvYyIsCiAgImNsdXN0ZXJfMC5vdXJzX21vZHVsZTEiID0gIjAiLAogICJjbHVzdGVyXzEub3Vyc19tb2R1bGUxIiA9ICIxIiwKICAiY2x1c3Rlcl8yLm91cnNfbW9kdWxlMSIgPSAiMiIsCiAgImNsdXN0ZXJfMy5vdXJzX21vZHVsZTEiID0gIjMiLAogICJjbHVzdGVyXzQub3Vyc19tb2R1bGUxIiA9ICI0IiwKICAiY2x1c3Rlcl81Lm91cnNfbW9kdWxlMSIgPSAiNSIsCiAgImNsdXN0ZXJfNi5vdXJzX21vZHVsZTEiID0gIjYiLAogICJjbHVzdGVyXzcub3Vyc19tb2R1bGUxIiA9ICI3IiwKICAiY2x1c3Rlcl84Lm91cnNfbW9kdWxlMSIgPSAiOCIsCiAgImNsdXN0ZXJfOS5vdXJzX21vZHVsZTEiID0gIjkiCikKCm91cnNfbGVnZW5kX29ubHkgPC0gU3BhdGlhbFBsb3Qob3Vyc19HU001MDI2MTQ0X1M3X1NldXJhdCwgZmVhdHVyZXMgPSAiY2x1c3Rlcl85Lm91cnNfbW9kdWxlMSIsIHB0LnNpemUuZmFjdG9yID0gMCwgaW1hZ2UuYWxwaGEgPSAwKSArIHRoZW1lKGxlZ2VuZC50ZXh0ID0gZWxlbWVudF9ibGFuaygpLCBsZWdlbmQudGl0bGUgPSBlbGVtZW50X2JsYW5rKCkpCgojIEFkZCB0aGUgbGVnZW5kX29ubHkgcGxvdCB0byB0aGUgdGhlIHBsb3RzIGxpc3QKb3VyX3Bsb3RzW1tsZW5ndGgob3VyX3Bsb3RzKSArIDFdXSA8LSBvdXJzX2xlZ2VuZF9vbmx5CgojIENyZWF0ZSBhbiBpbWFnZS1vbmx5IGdncGxvdApvdXJzX2ltYWdlX29ubHkgPC0gU3BhdGlhbFBsb3Qob3Vyc19HU001MDI2MTQ0X1M3X1NldXJhdCwgZmVhdHVyZXMgPSAiY2x1c3Rlcl85Lm91cnNfbW9kdWxlMSIsIHB0LnNpemUuZmFjdG9yID0gMCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCiAgCiMgQWRkIHRoZSBpbWFnZS1vbmx5IHBsb3QgdG8gdGhlIHRoZSBwbG90cyBsaXN0Cm91cl9wbG90c1tbbGVuZ3RoKG91cl9wbG90cykgKyAxXV0gPC0gb3Vyc19pbWFnZV9vbmx5CgojIEdlbmVyYXRlIHRoZSBwbG90cwpmb3IgKGZlYXR1cmUgaW4gbmFtZXMoZmVhdHVyZXNfbGlzdCkpIHsKICAjIENyZWF0ZSBhIHNwYXRpYWwgZmVhdHVyZSBwbG90IGZvciBlYWNoIGZlYXR1cmUKICBwIDwtIFNwYXRpYWxGZWF0dXJlUGxvdChvdXJzX0dTTTUwMjYxNDRfUzdfU2V1cmF0LCBmZWF0dXJlcyA9IGZlYXR1cmUpICsgCiAgICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsKICAgICAgIGFubm90YXRlKCJ0ZXh0IiwgeCA9IEluZiwgeSA9IEluZiwgbGFiZWwgPSBmZWF0dXJlc19saXN0W1tmZWF0dXJlXV0sIAogICAgICAgICAgICAgICAgaGp1c3QgPSAxLjEsIHZqdXN0ID0gMS4xLCBmb250ZmFjZSA9ICJpdGFsaWMiLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyKSAgCiAgIyBTdG9yZSB0aGUgcGxvdCBpbiB0aGUgbGlzdAogIG91cl9wbG90c1tbZmVhdHVyZV1dIDwtIHAKfQpgYGAKCmBgYHtyLCB3YXJuaW5nPUZBTFNFLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CiMgQXJyYW5nZSBwbG90cyBpbiBhIGdyaWQKZ3JpZC5hcnJhbmdlKGdyb2JzID0gb3VyX3Bsb3RzLCBuY29sID0gNSkKYGBgCgoKCmBgYHtyfQpzZXNzaW9uSW5mbygpCmBgYAoKCgoKCg==