1 Introduction

Single-cell sequencing is an emerging technology in the field of immunology and oncology that allows researchers to couple RNA quantification and other modalities, like immune cell receptor profiling at the level of an individual cell. A number of workflows and software packages have been created to process and analyze single-cell transcriptomic data. These packages allow users to take the vast dimensionality of the data generated in single-cell-based experiments and distill the data into novel insights. Unlike the transcriptomic field, there is a lack of options for software that allow for single-cell immune receptor profiling. Enabling users to easily combine RNA and immune profiling, the scRepertoire framework supports use of 10x, single-cell clonal formats and interaction with popular R-based single-cell data pipelines.

scRepertoire is designed to take filter contig outputs from the 10x Genomics Cell Ranger pipeline, process that data to assign clonotype based on two TCR or Ig chains and analyze the clonotype dynamics. The latter can be separated into 1) clonotype-only analysis functions, such as unique clonotypes or clonal space quantification, and 2) interaction with mRNA expression data using Seurat or SingleCellExperiment packages.

References:

Prerequisite: Ensure the All_samples_Merged Seurat object is loaded into your R environment before running the chunks below.

1.1 Load libraries

1.2 Load Seurat Object


#Load Seurat Object merged from cell lines and a control(PBMC) after filtration
All_samples_Merged <- readRDS("../../../0-Seurat_RDS_Final_OBJECT/All_samples_Merged_with_Renamed_Clusters_Cell_state-03-12-2025.rds")

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

1.3 Load contigs

scRepertoire functions using the filtered_contig_annotations.csv output from the 10x Genomics Cell Ranger. This file is located in the ./outs/ directory of the VDJ alignment folder. To generate a list of contigs to use for scRepertoire:

- load the filtered_contig_annotations.csv for each of the samples.

- make a list in the R environment.

2 Combining Contigs into Clones

There are varying definitions of clones in the literature. For the purposes of scRepertoire, we define a clone as cells with shared/trackable complementarity-determining region 3 (CDR3) sequences. Within this definition, one might use amino acid (aa) sequences of one or both chains to define a clone. Alternatively, we could use nucleotide (nt) or the V(D)JC genes (genes) to define a clone. The latter, genes, would be a more permissive definition of “clones,” as multiple amino acid or nucleotide sequences can result from the same gene combination. Another option to define a clone is the use of the V(D)JC and nucleotide sequence (strict). scRepertoire allows for the use of all these definitions of clones and enables users to select both or individual chains to examine.

2.1 Combining Contigs into Clones


combined.TCR <- combineTCR(contig_list, 
                          samples = samples,
                          removeNA = FALSE, 
                          removeMulti = FALSE, 
                          filterMulti = FALSE)

# Export raw data
combined_TCR_df <- do.call(rbind, combined.TCR) %>% mutate(sample = samples[as.numeric(sample)])
write.csv(combined_TCR_df, "combined_TCR_clean.csv", row.names = FALSE)
head(combined_TCR_df[,1:10])
NA

2.2 Prepare Seurat Object (SINGLE CALL)

TCR <- All_samples_Merged
DefaultAssay(TCR) <- "SCT"
TCR <- quietTCRgenes(TCR)

# YOUR ORIGINAL WORKING PARAMETERS (from script)
TCR <- combineExpression(combined.TCR, TCR, 
                        cloneCall = "gene",
                        group.by = "sample",     # ← FIXED: Use "sample" (exists in combined.TCR)
                        proportion = FALSE,
                        cloneSize = c(Single = 1, Small = 5, Medium = 20, Large = 100, Hyperexpanded = 500))

print("✅ SUCCESS - Clone metadata added")
[1] "✅ SUCCESS - Clone metadata added"
DimPlot(TCR, group.by = "cloneSize", reduction = "umap") + 
  scale_color_manual(values = rev(colorblind_vector[1:5]))

3 Combining Clones and Single-Cell Objects

DefaultAssay(TCR) <- "SCT"

# If All_samples_Merged is already loaded in the environment:
TCR <- All_samples_Merged

# Define color palette
colorblind_vector <- hcl.colors(n=7, palette = "inferno", fixup = TRUE)

# Combine expression with filtering of NA clonotype cells
TCR <- combineExpression(
    combined.TCR,
    TCR,
    cloneCall = "gene",
    group.by = "sample",
    proportion = TRUE,
    filterNA = TRUE  # This will exclude cells without clonotype info
)

# You no longer need the manual barcode matching or NA replacements



# Plot UMAP colored by cloneSize
DimPlot(TCR, group.by = "cloneSize", reduction = "umap") +
    scale_color_manual(values = rev(colorblind_vector[c(1, 3, 4, 5, 7)]))




DimPlot(TCR, group.by = "cloneSize", reduction = "umap")


DimPlot(TCR, group.by = "cloneSize", reduction = "umap") +
    scale_color_manual(values=rev(colorblind_vector[c(1,3,4,5,6)]))




#Define color palette 
colorblind_vector <- hcl.colors(n=9, palette = "inferno", fixup = TRUE)

Seurat::DimPlot(TCR, group.by = "cloneSize", reduction = "umap") +
    scale_color_manual(values=rev(colorblind_vector[c(1,3,4,5,7)]))

TCR <- combineExpression(combined.TCR, 
                                   TCR, 
                                   cloneCall="gene", 
                                   group.by = "sample", 
                                   proportion = FALSE, 
                                   cloneSize=c(Single=1, Small=5, Medium=20, Large=100, Hyperexpanded=500))

Seurat::DimPlot(TCR, group.by = "cloneSize", reduction = "umap") +
    scale_color_manual(values=rev(colorblind_vector[c(1,3,4,5,7)]))

4 Visualizations for Single-Cell Objects



clonalOverlay(TCR, 
              reduction = "umap", 
              cutpoint = 1, 
              bins = 10, 
              facet.by = "orig.ident") + 
              guides(color = "none")


#clonalNetwork
#ggraph needs to be loaded due to issues with ggplot
library(ggraph)

clonalNetwork(TCR, 
              reduction = "umap", 
              group.by = "seurat_clusters",
              filter.clones = NULL,
              filter.identity = NULL,
              cloneCall = "aa")



#Examining Cluster 3 only
clonalNetwork(TCR, 
              reduction = "umap", 
              group.by = "seurat_clusters",
              filter.identity = 8,
              cloneCall = "aa")



shared.clones <- clonalNetwork(TCR, 
                               reduction = "umap", 
                               group.by = "seurat_clusters",
                               cloneCall = "aa", 
                               exportClones = TRUE)
head(shared.clones)

#ggraph needs to be loaded due to issues with ggplot
library(ggraph)

#No Identity filter
clonalNetwork(TCR, 
              reduction = "umap", 
              group.by = "seurat_clusters",
              filter.clones = NULL,
              filter.identity = NULL,
              cloneCall = "aa")

# clonalOccupy
#clonalOccupy
clonalOccupy(TCR, 
              x.axis = "seurat_clusters")


clonalOccupy(TCR, 
              x.axis = "orig.ident")

# clonalOccupy
clonalOccupy(TCR, 
              x.axis = "orig.ident")


clonalOccupy(TCR, 
                     x.axis = "orig.ident", 
                     proportion = TRUE, 
                     label = FALSE)


# getCirclize

library(circlize)
library(scales)

circles <- getCirclize(TCR, 
                       group.by = "seurat_clusters")

#Just assigning the normal colors to each cluster
grid.cols <- hue_pal()(length(unique(TCR$seurat_clusters)))
names(grid.cols) <- unique(TCR$seurat_clusters)

#Graphing the chord diagram
chordDiagram(circles, self.link = 1, grid.col = grid.cols)



circles <- getCirclize(TCR, group.by = "orig.ident")

grid.cols <- scales::hue_pal()(length(unique(TCR@active.ident)))
names(grid.cols) <- levels(TCR@active.ident)

chordDiagram(circles, 
             self.link = 1, 
             grid.col = grid.cols)

5 Quantifying Clonal Bias

# # StartracDiversity # From the excellent work by Lei Zhang, et al., the authors introduce new methods for looking at clones by cellular origins and cluster identification. Their STARTRAC software has been adapted to work with scRepertoire and please read and cite their excellent work. # # In order to use the StartracDiversity() function, you will need to include the product of the combinedExpression() function. The second requirement is a column header in the meta data of the Seurat object that has tissue of origin. In the example data, type corresponds to the column “Type”, which includes the “P” and “T” classifiers. The indices can be subsetted for a specific patient or examined overall using the by variable. Importantly, the function uses only the strict definition of a clone of the VDJC genes and the CDR3 nucleotide sequence. # # The indices output includes: # # expa - Clonal Expansion # migr - Cross-tissue Migration # tran - State Transition


Idents(TCR) <- "seurat_clusters"

StartracDiversity(TCR, 
                  type = "orig.ident",
                  group.by = "orig.ident")

NA
NA

6 clonalBias


clonalBias(TCR, 
           cloneCall = "aa", 
           split.by = "orig.ident", 
           group.by = "seurat_clusters",
           n.boots = 10, 
           min.expand =5)

7 TRBV CLONALITY

8 COMPLETE ANALYSIS: TRBV + TRAV (BOTH CHAINS)


library(dplyr)
library(tidyr)
library(stringr)

# COMPLETE FIXED ANALYSIS: TRBV (TCR2) + TRAV (TCR1)
vgene_v_all_counts <- data.frame()

for(i in seq_along(combined.TCR)) {
    contigs <- combined.TCR[[i]]
    sample_name <- names(combined.TCR)[i]
    
    # BETA CHAIN: TRBV from TCR2 (TRBC+ cells)
    beta_counts <- contigs %>%
        filter(grepl("TRBC", TCR2)) %>%
        mutate(v_gene = str_extract(TCR2, "TRBV[^.]+"),
               chain = "TRBV") %>%
        group_by(v_gene, chain) %>%
        summarise(nCells = n(), sample = sample_name, .groups = "drop") %>%
        filter(!is.na(v_gene))
    
    # ALPHA CHAIN: TRAV from TCR1 (TRAV+ cells)
    alpha_counts <- contigs %>%
        filter(grepl("TRAV", TCR1)) %>%
        mutate(v_gene = str_extract(TCR1, "TRAV[^.]+"),
               chain = "TRAV") %>%
        group_by(v_gene, chain) %>%
        summarise(nCells = n(), sample = sample_name, .groups = "drop") %>%
        filter(!is.na(v_gene))
    
    vgene_v_all_counts <- bind_rows(vgene_v_all_counts, beta_counts, alpha_counts)
}

# L4_B low-frequency genes (<0.1% = <7 cells)
l4b_low_freq <- vgene_v_all_counts %>%
    filter(sample == "L4_B", nCells < 7) %>%
    arrange(chain, desc(nCells))

# Wide format (samples × V-genes)
vgene_wide <- vgene_v_all_counts %>%
    select(-chain) %>%
    pivot_wider(names_from = sample, values_from = nCells, values_fill = 0)

# Summary statistics
total_summary <- vgene_v_all_counts %>%
    group_by(sample, chain) %>%
    summarise(total_cells = sum(nCells), .groups = "drop")

# Percentages (% usage per sample/chain)
vgene_percent <- vgene_v_all_counts %>%
    group_by(sample, chain) %>%
    mutate(percent = round(nCells / sum(nCells) * 100, 2)) %>%
    ungroup() %>%
    select(sample, chain, v_gene, nCells, percent)

## SAVE 5 PUBLICATION-READY FILES
write.csv(vgene_wide, "01_Vgene_counts_wide.csv", row.names = FALSE)
write.csv(l4b_low_freq, "02_L4B_lowfreq_Vgenes.csv", row.names = FALSE)
write.csv(total_summary, "03_Vgene_totals_summary.csv", row.names = FALSE)
write.csv(vgene_percent, "04_Vgene_percentages.csv", row.names = FALSE)
write.csv(vgene_v_all_counts, "05_Vgene_long_format.csv", row.names = FALSE)

## COMPREHENSIVE PREVIEW
cat("📊 TOTAL CELLS SUMMARY:\n")
📊 TOTAL CELLS SUMMARY:
print(total_summary)
# A tibble: 16 × 3
   sample chain total_cells
   <chr>  <chr>       <int>
 1 L1     TRAV         5965
 2 L1     TRBV         5997
 3 L2     TRAV         5975
 4 L2     TRBV         5986
 5 L3_B   TRAV         5413
 6 L3_B   TRBV         6075
 7 L4_B   TRAV         6098
 8 L4_B   TRBV         6109
 9 L5     TRAV         5694
10 L5     TRBV         5542
11 L6     TRAV         5015
12 L6     TRBV         5021
13 L7     TRAV         5783
14 L7     TRBV         5809
15 PBMC   TRAV         5257
16 PBMC   TRBV         6031
cat(sprintf("\n🎯 L4_B low-freq genes (<0.1%%): %d\n", nrow(l4b_low_freq)))

🎯 L4_B low-freq genes (<0.1%): 25
print("Top 10 L4_B low-freq:")
[1] "Top 10 L4_B low-freq:"
print(head(l4b_low_freq, 10))
       v_gene chain nCells sample
1    TRAV12-2  TRAV      2   L4_B
2      TRAV21  TRAV      2   L4_B
3    TRAV26-2  TRAV      2   L4_B
4      TRAV10  TRAV      1   L4_B
5    TRAV13-1  TRAV      1   L4_B
6    TRAV13-2  TRAV      1   L4_B
7      TRAV16  TRAV      1   L4_B
8      TRAV17  TRAV      1   L4_B
9      TRAV27  TRAV      1   L4_B
10 TRAV29/DV5  TRAV      1   L4_B
cat("\n🔥 TOP 3 V-GENES PER SAMPLE/CHAIN:\n")

🔥 TOP 3 V-GENES PER SAMPLE/CHAIN:
vgene_v_all_counts %>%
    group_by(sample, chain) %>%
    slice_max(nCells, n = 3) %>%
    ungroup() %>%
    arrange(sample, chain, desc(nCells)) %>%
    mutate(percent = round(nCells / total_summary$total_cells[match(sample, total_summary$sample)], 1)) %>%
    select(sample, chain, v_gene, nCells, percent) %>%
    print(n = Inf)
# A tibble: 28 × 5
   sample chain v_gene       nCells percent
   <chr>  <chr> <chr>         <int>   <dbl>
 1 L1     TRAV  TRAV38-2/DV8   5965     1  
 2 L1     TRBV  TRBV20-1       5997     1  
 3 L2     TRAV  TRAV38-2/DV8   5975     1  
 4 L2     TRBV  TRBV20-1       5986     1  
 5 L3_B   TRAV  TRAV4          5413     1  
 6 L3_B   TRBV  TRBV20-1       6075     1.1
 7 L4_B   TRAV  TRAV4          6083     1  
 8 L4_B   TRAV  TRAV12-2          2     0  
 9 L4_B   TRAV  TRAV21            2     0  
10 L4_B   TRAV  TRAV26-2          2     0  
11 L4_B   TRBV  TRBV20-1       6083     1  
12 L4_B   TRBV  TRBV24-1         12     0  
13 L4_B   TRBV  TRBV9             2     0  
14 L5     TRAV  TRAV17         5382     0.9
15 L5     TRAV  TRAV9-2         312     0.1
16 L5     TRBV  TRBV20-1       5542     1  
17 L6     TRAV  TRAV17         4887     1  
18 L6     TRAV  TRAV9-2         128     0  
19 L6     TRBV  TRBV20-1       5021     1  
20 L7     TRAV  TRAV17         5555     1  
21 L7     TRAV  TRAV9-2         228     0  
22 L7     TRBV  TRBV20-1       5809     1  
23 PBMC   TRAV  TRAV29/DV5      406     0.1
24 PBMC   TRAV  TRAV13-1        308     0.1
25 PBMC   TRAV  TRAV9-2         257     0  
26 PBMC   TRBV  TRBV20-1        530     0.1
27 PBMC   TRBV  TRBV5-1         529     0.1
28 PBMC   TRBV  TRBV2           348     0.1
cat("\n✅ 5 FILES SAVED FOR THESIS:\n")

✅ 5 FILES SAVED FOR THESIS:
cat("- 01_Vgene_counts_wide.csv (heatmap input)\n")
- 01_Vgene_counts_wide.csv (heatmap input)
cat("- 04_Vgene_percentages.csv (Fig Table)\n")
- 04_Vgene_percentages.csv (Fig Table)
cat("- 02_L4B_lowfreq_Vgenes.csv (heterogeneity markers)\n")
- 02_L4B_lowfreq_Vgenes.csv (heterogeneity markers)

9 Vgene-heatmap-visualization


library(reshape2)
library(ggplot2)
library(dplyr)
library(stringr)

# FIXED: Clean sample names + TRBV24-1 contrast
vgene_percent <- read.csv("04_Vgene_percentages.csv")

# Sample name mapping (display-friendly)
sample_mapping <- c("L1" = "L1", "L2" = "L2", "L3_B" = "L3", "L4_B" = "L4", 
                    "L5" = "L5", "L6" = "L6", "L7" = "L7")
cell_lines <- names(sample_mapping)

# FILTER: TRBV20-1 + TRBV24-1 (cell lines only)
target_genes <- c("TRBV20-1", "TRBV24-1")
gene_table_filtered <- vgene_percent %>%
    filter(v_gene %in% target_genes,
           sample %in% cell_lines) %>%
    mutate(sample_display = sample_mapping[sample]) %>%
    select(sample_display, v_gene, percent) %>%
    pivot_wider(names_from = sample_display, values_from = percent, values_fill = 0) %>%
    column_to_rownames("v_gene") %>%
    as.matrix()

cat("Filtered genes:", nrow(gene_table_filtered), "\n")
Filtered genes: 2 
print(rownames(gene_table_filtered))
[1] "TRBV20-1" "TRBV24-1"
# Melt with display names
mat_melt <- melt(as.matrix(gene_table_filtered))
colnames(mat_melt) <- c("Gene", "Sample", "Percent")

# FIXED HEATMAP: Better contrast for TRBV24-1
p1 <- ggplot(mat_melt, aes(x = Sample, y = Gene, fill = Percent)) +
    geom_tile(lwd = 0.6, color = "white", height = 0.85, width = 0.85) +
    scale_fill_gradientn(name = "% of T-cells",
                         colors = c("navy", "gold", "darkred"),
                         limits = c(0, 100),
                         breaks = c(0, 25, 50, 75, 100),
                         na.value = "grey90") +
    theme_classic(base_size = 13) +
    theme(axis.text.x  = element_text(angle = 45, vjust = 1, hjust = 1, size = 12, face = "bold"),
          axis.text.y  = element_text(size = 13, face = "bold"),
          axis.title   = element_blank(),
          legend.position = "right",
          legend.title.align = 0.5,
          legend.title = element_text(size = 12, face = "bold"),
          plot.title = element_text(size = 15, face = "bold", hjust = 0.5)) +
    labs(title = "TRBV Oligoclonality in Sézary Cell Lines\nTRBV20-1 dominates (≥99%); TRBV24-1: L4-specific minor clone")

print(p1)

# HIGH-RES PUBLICATION OUTPUT
ggsave("Fig_TRBV_heatmap_L3_L4_clean.png", p1, width = 11, height = 4.5, dpi = 600, bg = "white")
ggsave("Fig_TRBV_heatmap_L3_L4_clean.pdf", p1, width = 11, height = 4.5)


cat("\n✅ FIXED HEATMAP SAVED (L3/L4 labels + TRBV24-1 visible!)\n")

✅ FIXED HEATMAP SAVED (L3/L4 labels + TRBV24-1 visible!)

10 Vgene-heatmap-visualization


library(reshape2)
library(ggplot2)
library(dplyr)
library(stringr)

vgene_percent <- read.csv("04_Vgene_percentages.csv")
sample_mapping <- c("L1" = "L1", "L2" = "L2", "L3_B" = "L3", "L4_B" = "L4", 
                    "L5" = "L5", "L6" = "L6", "L7" = "L7")
cell_lines <- names(sample_mapping)

# === PLOT 1: TRBV20-1 ONLY (Dominance) ===
trbv20_data <- vgene_percent %>%
    filter(v_gene == "TRBV20-1", sample %in% cell_lines) %>%
    mutate(sample_display = sample_mapping[sample])

p20 <- ggplot(trbv20_data, aes(x = reorder(sample_display, percent), y = percent)) +
    geom_col(fill = "darkred", alpha = 0.8, width = 0.7) +
    geom_text(aes(label = sprintf("%.1f%%", percent)), hjust = -0.1, size = 4) +
    coord_flip() +
    scale_y_continuous(limits = c(0, 102), expand = expansion(mult = c(0, 0.08))) +
    labs(title = "TRBV20-1 Dominance (≥99% in all Sézary lines)", 
         x = "Cell Line", y = "% of TRB+ T-cells") +
    theme_classic(base_size = 13) +
    theme(plot.title = element_text(size = 15, face = "bold"),
          axis.text.y = element_text(size = 12, face = "bold"),
          axis.title = element_text(size = 12))

# === PLOT 2: TRBV24-1 ONLY (L4-specific minor clone) ===
trbv24_data <- vgene_percent %>%
    filter(v_gene == "TRBV24-1", sample %in% cell_lines) %>%
    mutate(sample_display = sample_mapping[sample])

p24 <- ggplot(trbv24_data, aes(x = reorder(sample_display, percent), y = percent)) +
    geom_col(aes(fill = sample_display == "L4"), width = 0.7, alpha = 0.8) +
    scale_fill_manual(values = c("FALSE" = "steelblue", "TRUE" = "orange"), guide = "none") +
    geom_text(aes(label = sprintf("%.3f%%", percent)), hjust = -0.1, size = 4) +
    coord_flip() +
    scale_y_continuous(limits = c(0, 0.3), expand = expansion(mult = c(0, 0.15))) +
    labs(title = "TRBV24-1: L4-specific minor clone (0.196%)", 
         x = "Cell Line", y = "% of TRB+ T-cells") +
    theme_classic(base_size = 13) +
    theme(plot.title = element_text(size = 15, face = "bold"),
          axis.text.y = element_text(size = 12, face = "bold"),
          axis.title = element_text(size = 12))

# COMBINED FIGURE
p_combined <- p20 + p24 + plot_layout(ncol = 2) & theme(legend.position = "none")
print(p_combined)

# SAVE THESIS FIGURE
ggsave("Fig_TRBV20-24_separate_L4_highlight.png", p_combined, width = 14, height = 7, dpi = 600, bg = "white")
ggsave("Fig_TRBV20-24_separate_L4_highlight.pdf", p_combined, width = 14, height = 7)


cat("✅ SEPARATE PLOTS SAVED - L4 TRBV24-1 highlighted in ORANGE!\n")
✅ SEPARATE PLOTS SAVED - L4 TRBV24-1 highlighted in ORANGE!
library(ggplot2)
library(dplyr)
library(scales)
library(RColorBrewer)

sample_mapping <- c("L1" = "L1", "L2" = "L2", "L3_B" = "L3", "L4_B" = "L4", 
                    "L5" = "L5", "L6" = "L6", "L7" = "L7")
cell_lines <- c("L1", "L2", "L3_B", "L4_B", "L5", "L6", "L7")

percent_data <- vgene_percent %>%
    filter(chain == "TRBV") %>%
    mutate(sample_display = sample_mapping[sample]) %>%
    group_by(sample_display) %>%
    mutate(percent_scRepertoire = nCells / sum(nCells) * 100) %>%
    ungroup()

l4_trbv24 <- percent_data %>% filter(sample_display == "L4", v_gene == "TRBV24-1")
trbv20_data <- percent_data %>% filter(v_gene == "TRBV20-1", sample %in% cell_lines)
minor_data <- percent_data %>%
    filter(sample %in% cell_lines, v_gene != "TRBV20-1", v_gene != "TRBV24-1") %>%
    group_by(sample_display) %>%
    slice_max(percent_scRepertoire, n = 10) %>%
    ungroup()

minor_colors <- colorRampPalette(RColorBrewer::brewer.pal(9, "PuBuGn")[3:9])(length(unique(minor_data$v_gene)))

p_final <- ggplot() +
    geom_col(data = minor_data, 
             aes(x = sample_display, y = percent_scRepertoire/100, fill = v_gene),
             position = "stack", alpha = 0.7, width = 0.85, color = NA) +
    scale_fill_manual(values = minor_colors, guide = "none") +
    geom_col(data = trbv20_data, 
             aes(x = sample_display, y = percent_scRepertoire/100),
             fill = "#E63946", alpha = 0.95, width = 0.85, color = "white", linewidth = 0.5) +
    geom_col(data = l4_trbv24, 
             aes(x = sample_display, y = percent_scRepertoire/100),
             fill = "#F4A261", alpha = 1, width = 0.85, color = "black", linewidth = 0.8) +
    geom_text(data = trbv20_data,
              aes(x = sample_display, y = 0.85, label = sprintf("%.0f%%", percent_scRepertoire)),
              size = 4.5, fontface = "bold", color = "white") +
    geom_text(data = l4_trbv24,
              aes(x = sample_display, y = 0.15, 
                  label = sprintf("TRBV24-1\n0.20%%")),
              hjust = -0.1, size = 4.2, fontface = "bold", color = "black") +
    scale_y_continuous(labels = percent_format(accuracy = 1), expand = expansion(mult = c(0, 0.12))) +
    coord_flip() +
    labs(title = "TRBV Monoclonal Dominance in Sézary Cell Lines",
         subtitle = "TRBV20-1 (99-100%) + L4-specific TRBV24-1 (0.20%)") +
    theme_classic(base_size = 15) +
    theme(axis.text.y = element_text(size = 14, face = "bold"),
          axis.title.y = element_blank(),
          legend.position = "none",
          plot.title = element_text(size = 18, face = "bold", hjust = 0.5),
          plot.subtitle = element_text(size = 14, hjust = 0.5))

print(p_final)


ggsave("Fig_TRBV_monoclonal_final.png", p_final, width = 12, height = 9, dpi = 600, bg = "white")
ggsave("Fig_TRBV_monoclonal_final.pdf", p_final, width = 12, height = 9, bg = "white")

cat("✅ MONOCLONAL title + L4 biclonal highlight!\n")
✅ MONOCLONAL title + L4 biclonal highlight!

```

LS0tCnRpdGxlOiAiVENSIEFuYWx5c2lzLTIzLTAyLTIwMjZfVmlzdWFsaXphdGlvbiIKYXV0aG9yOiBOYXNpciBNYWhtb29kIEFiYmFzaQpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgY3NzOiBzdHlsZS5jc3MKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgoKIyAqKkludHJvZHVjdGlvbioqCgpTaW5nbGUtY2VsbCBzZXF1ZW5jaW5nIGlzIGFuIGVtZXJnaW5nIHRlY2hub2xvZ3kgaW4gdGhlIGZpZWxkIG9mIGltbXVub2xvZ3kgYW5kIG9uY29sb2d5IHRoYXQgYWxsb3dzIHJlc2VhcmNoZXJzIHRvIGNvdXBsZSBSTkEgcXVhbnRpZmljYXRpb24gYW5kIG90aGVyIG1vZGFsaXRpZXMsIGxpa2UgaW1tdW5lIGNlbGwgcmVjZXB0b3IgcHJvZmlsaW5nIGF0IHRoZSBsZXZlbCBvZiBhbiBpbmRpdmlkdWFsIGNlbGwuIEEgbnVtYmVyIG9mIHdvcmtmbG93cyBhbmQgc29mdHdhcmUgcGFja2FnZXMgaGF2ZSBiZWVuIGNyZWF0ZWQgdG8gcHJvY2VzcyBhbmQgYW5hbHl6ZSBzaW5nbGUtY2VsbCB0cmFuc2NyaXB0b21pYyBkYXRhLiBUaGVzZSBwYWNrYWdlcyBhbGxvdyB1c2VycyB0byB0YWtlIHRoZSB2YXN0IGRpbWVuc2lvbmFsaXR5IG9mIHRoZSBkYXRhIGdlbmVyYXRlZCBpbiBzaW5nbGUtY2VsbC1iYXNlZCBleHBlcmltZW50cyBhbmQgZGlzdGlsbCB0aGUgZGF0YSBpbnRvIG5vdmVsIGluc2lnaHRzLiBVbmxpa2UgdGhlIHRyYW5zY3JpcHRvbWljIGZpZWxkLCB0aGVyZSBpcyBhIGxhY2sgb2Ygb3B0aW9ucyBmb3Igc29mdHdhcmUgdGhhdCBhbGxvdyBmb3Igc2luZ2xlLWNlbGwgaW1tdW5lIHJlY2VwdG9yIHByb2ZpbGluZy4gRW5hYmxpbmcgdXNlcnMgdG8gZWFzaWx5IGNvbWJpbmUgUk5BIGFuZCBpbW11bmUgcHJvZmlsaW5nLCB0aGUgc2NSZXBlcnRvaXJlIGZyYW1ld29yayBzdXBwb3J0cyB1c2Ugb2YgMTB4LCBzaW5nbGUtY2VsbCBjbG9uYWwgZm9ybWF0cyBhbmQgaW50ZXJhY3Rpb24gd2l0aCBwb3B1bGFyIFItYmFzZWQgc2luZ2xlLWNlbGwgZGF0YSBwaXBlbGluZXMuCgpzY1JlcGVydG9pcmUgaXMgZGVzaWduZWQgdG8gdGFrZSBmaWx0ZXIgY29udGlnIG91dHB1dHMgZnJvbSB0aGUgMTB4IEdlbm9taWNzIENlbGwgUmFuZ2VyIHBpcGVsaW5lLCBwcm9jZXNzIHRoYXQgZGF0YSB0byBhc3NpZ24gY2xvbm90eXBlIGJhc2VkIG9uIHR3byBUQ1Igb3IgSWcgY2hhaW5zIGFuZCBhbmFseXplIHRoZSBjbG9ub3R5cGUgZHluYW1pY3MuIFRoZSBsYXR0ZXIgY2FuIGJlIHNlcGFyYXRlZCBpbnRvIDEpIGNsb25vdHlwZS1vbmx5IGFuYWx5c2lzIGZ1bmN0aW9ucywgc3VjaCBhcyB1bmlxdWUgY2xvbm90eXBlcyBvciBjbG9uYWwgc3BhY2UgcXVhbnRpZmljYXRpb24sIGFuZCAyKSBpbnRlcmFjdGlvbiB3aXRoIG1STkEgZXhwcmVzc2lvbiBkYXRhIHVzaW5nIFNldXJhdCBvciBTaW5nbGVDZWxsRXhwZXJpbWVudCBwYWNrYWdlcy4KCioqUmVmZXJlbmNlczoqKgoKLSBbc2NSZXBlcnRvaXJlIFZpZ25ldHRlXShodHRwczovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy92aWduZXR0ZXMvc2NSZXBlcnRvaXJlL2luc3QvZG9jL3ZpZ25ldHRlLmh0bWwpICAKLSBbQm9yY2guZGV2IHNjUmVwZXJ0b2lyZV0oaHR0cHM6Ly93d3cuYm9yY2guZGV2L3VwbG9hZHMvc2NyZXBlcnRvaXJlLykKCgoKKipQcmVyZXF1aXNpdGU6KiogRW5zdXJlIHRoZSBgQWxsX3NhbXBsZXNfTWVyZ2VkYCBTZXVyYXQgb2JqZWN0IGlzIGxvYWRlZCBpbnRvIHlvdXIgUiBlbnZpcm9ubWVudCBiZWZvcmUgcnVubmluZyB0aGUgY2h1bmtzIGJlbG93LgoKIyMgTG9hZCBsaWJyYXJpZXMKYGBge3IsIGluY2x1ZGU9RkFMU0V9CgojIENvcmUKbGlicmFyeShTZXVyYXQpCmxpYnJhcnkoU2V1cmF0T2JqZWN0KQpsaWJyYXJ5KFNldXJhdERhdGEpCmxpYnJhcnkocGF0Y2h3b3JrKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeSh0aW55dGV4KQpsaWJyYXJ5KGdyaWQpCmxpYnJhcnkoY293cGxvdCkKbGlicmFyeShwcmVzdG8pCgojIFRDUiBBbmFseXNpcwpsaWJyYXJ5KHNjUmVwZXJ0b2lyZSkKbGlicmFyeShTaW5nbGVDZWxsRXhwZXJpbWVudCkKbGlicmFyeShjaXJjbGl6ZSkKbGlicmFyeShzY2FsZXMpCgojIFRhYmxlcy9leHBvcnRzCmxpYnJhcnkoZ3QpCmxpYnJhcnkod3JpdGV4bCkKCiMgQ29sb3JzIChjb25zaXN0ZW50IHRocm91Z2hvdXQpCmNvbG9yYmxpbmRfdmVjdG9yIDwtIGhjbC5jb2xvcnMoOSwgImluZmVybm8iLCBmaXh1cCA9IFRSVUUpCm9wdGlvbnMocmVwci5wbG90LndpZHRoPTEyLCByZXByLnBsb3QuaGVpZ2h0PTgpCgpgYGAKCiMjIExvYWQgU2V1cmF0IE9iamVjdApgYGB7cn0KCiNMb2FkIFNldXJhdCBPYmplY3QgbWVyZ2VkIGZyb20gY2VsbCBsaW5lcyBhbmQgYSBjb250cm9sKFBCTUMpIGFmdGVyIGZpbHRyYXRpb24KQWxsX3NhbXBsZXNfTWVyZ2VkIDwtIHJlYWRSRFMoIi4uLy4uLy4uLzAtU2V1cmF0X1JEU19GaW5hbF9PQkpFQ1QvQWxsX3NhbXBsZXNfTWVyZ2VkX3dpdGhfUmVuYW1lZF9DbHVzdGVyc19DZWxsX3N0YXRlLTAzLTEyLTIwMjUucmRzIikKCkFsbF9zYW1wbGVzX01lcmdlZApgYGAKCiMjIExvYWQgY29udGlncwoKc2NSZXBlcnRvaXJlIGZ1bmN0aW9ucyB1c2luZyB0aGUgZmlsdGVyZWRfY29udGlnX2Fubm90YXRpb25zLmNzdiBvdXRwdXQgZnJvbSB0aGUgMTB4IEdlbm9taWNzIENlbGwgUmFuZ2VyLiBUaGlzIGZpbGUgaXMgbG9jYXRlZCBpbiB0aGUgLi9vdXRzLyBkaXJlY3Rvcnkgb2YgdGhlIFZESiBhbGlnbm1lbnQgZm9sZGVyLiBUbyBnZW5lcmF0ZSBhIGxpc3Qgb2YgY29udGlncyB0byB1c2UgZm9yIHNjUmVwZXJ0b2lyZToKCioqLSAgIGxvYWQgdGhlIGZpbHRlcmVkX2NvbnRpZ19hbm5vdGF0aW9ucy5jc3YgZm9yIGVhY2ggb2YgdGhlIHNhbXBsZXMuKioKCioqLSAgIG1ha2UgYSBsaXN0IGluIHRoZSBSIGVudmlyb25tZW50LioqCgoKYGBge3IgVENSLCBpbmNsdWRlPUZBTFNFfQojIENvbnRpZyBmaWxlcyAoUk9CVVNUIFBBVEhTIC0gcmVwbGFjZSB3aXRoIHlvdXIgYWN0dWFsIHBhdGhzKQpjb250aWdfcGF0aHMgPC0gbGlzdCgKICBMMSA9ICIvcnVuL3VzZXIvMTAwMC9ndmZzL3NtYi1zaGFyZTpzZXJ2ZXI9MTAuMTQ0LjE0Mi4xMzEsc2hhcmU9Y29tbXVuL05hc2lyL0FsbF9EYXRhX1NTL0F1ZHJleV9Hcm9zL0NlbGxSYW5nZXIvTDEvb3V0cy9wZXJfc2FtcGxlX291dHMvTDEvdmRqX3QvZmlsdGVyZWRfY29udGlnX2Fubm90YXRpb25zLmNzdiIsCiAgTDIgPSAiL3J1bi91c2VyLzEwMDAvZ3Zmcy9zbWItc2hhcmU6c2VydmVyPTEwLjE0NC4xNDIuMTMxLHNoYXJlPWNvbW11bi9OYXNpci9BbGxfRGF0YV9TUy9BdWRyZXlfR3Jvcy9DZWxsUmFuZ2VyL0wyL291dHMvcGVyX3NhbXBsZV9vdXRzL0wyL3Zkal90L2ZpbHRlcmVkX2NvbnRpZ19hbm5vdGF0aW9ucy5jc3YiLAogIEwzX0IgPSAiL3J1bi91c2VyLzEwMDAvZ3Zmcy9zbWItc2hhcmU6c2VydmVyPTEwLjE0NC4xNDIuMTMxLHNoYXJlPWNvbW11bi9OYXNpci9BbGxfRGF0YV9TUy9BdWRyZXlfR3Jvcy9DZWxsUmFuZ2VyL0wzX0NJVEVfQi9vdXRzL3Blcl9zYW1wbGVfb3V0cy9MM19DSVRFX0IvdmRqX3QvZmlsdGVyZWRfY29udGlnX2Fubm90YXRpb25zLmNzdiIsCiAgTDRfQiA9ICIvcnVuL3VzZXIvMTAwMC9ndmZzL3NtYi1zaGFyZTpzZXJ2ZXI9MTAuMTQ0LjE0Mi4xMzEsc2hhcmU9Y29tbXVuL05hc2lyL0FsbF9EYXRhX1NTL0F1ZHJleV9Hcm9zL0NlbGxSYW5nZXIvTDRfQi9vdXRzL3Blcl9zYW1wbGVfb3V0cy9MNF9CL3Zkal90L2ZpbHRlcmVkX2NvbnRpZ19hbm5vdGF0aW9ucy5jc3YiLAogIEw1ID0gIi9ydW4vdXNlci8xMDAwL2d2ZnMvc21iLXNoYXJlOnNlcnZlcj0xMC4xNDQuMTQyLjEzMSxzaGFyZT1jb21tdW4vTmFzaXIvQWxsX0RhdGFfU1MvQXVkcmV5X0dyb3MvQ2VsbFJhbmdlci9MNS9vdXRzL3Blcl9zYW1wbGVfb3V0cy9MNS92ZGpfdC9maWx0ZXJlZF9jb250aWdfYW5ub3RhdGlvbnMuY3N2IiwKICBMNiA9ICIvcnVuL3VzZXIvMTAwMC9ndmZzL3NtYi1zaGFyZTpzZXJ2ZXI9MTAuMTQ0LjE0Mi4xMzEsc2hhcmU9Y29tbXVuL05hc2lyL0FsbF9EYXRhX1NTL0F1ZHJleV9Hcm9zL0NlbGxSYW5nZXIvTDZfQ0lURS9vdXRzL3Blcl9zYW1wbGVfb3V0cy9MNl9DSVRFL3Zkal90L2ZpbHRlcmVkX2NvbnRpZ19hbm5vdGF0aW9ucy5jc3YiLAogIEw3ID0gIi9ydW4vdXNlci8xMDAwL2d2ZnMvc21iLXNoYXJlOnNlcnZlcj0xMC4xNDQuMTQyLjEzMSxzaGFyZT1jb21tdW4vTmFzaXIvQWxsX0RhdGFfU1MvQXVkcmV5X0dyb3MvQ2VsbFJhbmdlci9MNy9vdXRzL3Blcl9zYW1wbGVfb3V0cy9MNy92ZGpfdC9maWx0ZXJlZF9jb250aWdfYW5ub3RhdGlvbnMuY3N2IiwKICBQQk1DID0gIi9ydW4vdXNlci8xMDAwL2d2ZnMvc21iLXNoYXJlOnNlcnZlcj0xMC4xNDQuMTQyLjEzMSxzaGFyZT1jb21tdW4vTmFzaXIvQWxsX0RhdGFfU1MvQXVkcmV5X0dyb3MvQ2VsbFJhbmdlci9QQk1DL291dHMvcGVyX3NhbXBsZV9vdXRzL1BCTUMvdmRqX3QvZmlsdGVyZWRfY29udGlnX2Fubm90YXRpb25zLmNzdiIKKQoKIyBMb2FkIGNvbnRpZ3Mgd2l0aCBlcnJvciBjaGVja2luZwpjb250aWdfbGlzdCA8LSBsaXN0KCkKc2FtcGxlcyA8LSBuYW1lcyhjb250aWdfcGF0aHMpCmZvcihpIGluIHNlcV9hbG9uZyhjb250aWdfcGF0aHMpKSB7CiAgaWYoZmlsZS5leGlzdHMoY29udGlnX3BhdGhzW1tpXV0pKSB7CiAgICBjb250aWdfbGlzdFtbc2FtcGxlc1tpXV1dIDwtIHJlYWQuY3N2KGNvbnRpZ19wYXRoc1tbaV1dKQogIH0gZWxzZSB7CiAgICB3YXJuaW5nKCJNaXNzaW5nOiAiLCBjb250aWdfcGF0aHNbW2ldXSkKICB9Cn0KYGBgCgojICoqQ29tYmluaW5nIENvbnRpZ3MgaW50byBDbG9uZXMqKgoKVGhlcmUgYXJlIHZhcnlpbmcgZGVmaW5pdGlvbnMgb2YgY2xvbmVzIGluIHRoZSBsaXRlcmF0dXJlLiBGb3IgdGhlIHB1cnBvc2VzIG9mIHNjUmVwZXJ0b2lyZSwgd2UgZGVmaW5lIGEgY2xvbmUgYXMgY2VsbHMgd2l0aCBzaGFyZWQvdHJhY2thYmxlIGNvbXBsZW1lbnRhcml0eS1kZXRlcm1pbmluZyByZWdpb24gMyAoQ0RSMykgc2VxdWVuY2VzLiBXaXRoaW4gdGhpcyBkZWZpbml0aW9uLCBvbmUgbWlnaHQgdXNlIGFtaW5vIGFjaWQgKGFhKSBzZXF1ZW5jZXMgb2Ygb25lIG9yIGJvdGggY2hhaW5zIHRvIGRlZmluZSBhIGNsb25lLiBBbHRlcm5hdGl2ZWx5LCB3ZSBjb3VsZCB1c2UgbnVjbGVvdGlkZSAobnQpIG9yIHRoZSBWKEQpSkMgZ2VuZXMgKGdlbmVzKSB0byBkZWZpbmUgYSBjbG9uZS4gVGhlIGxhdHRlciwgZ2VuZXMsIHdvdWxkIGJlIGEgbW9yZSBwZXJtaXNzaXZlIGRlZmluaXRpb24gb2Yg4oCcY2xvbmVzLOKAnSBhcyBtdWx0aXBsZSBhbWlubyBhY2lkIG9yIG51Y2xlb3RpZGUgc2VxdWVuY2VzIGNhbiByZXN1bHQgZnJvbSB0aGUgc2FtZSBnZW5lIGNvbWJpbmF0aW9uLiBBbm90aGVyIG9wdGlvbiB0byBkZWZpbmUgYSBjbG9uZSBpcyB0aGUgdXNlIG9mIHRoZSBWKEQpSkMgYW5kIG51Y2xlb3RpZGUgc2VxdWVuY2UgKHN0cmljdCkuIHNjUmVwZXJ0b2lyZSBhbGxvd3MgZm9yIHRoZSB1c2Ugb2YgYWxsIHRoZXNlIGRlZmluaXRpb25zIG9mIGNsb25lcyBhbmQgZW5hYmxlcyB1c2VycyB0byBzZWxlY3QgYm90aCBvciBpbmRpdmlkdWFsIGNoYWlucyB0byBleGFtaW5lLgoKIyMgQ29tYmluaW5nIENvbnRpZ3MgaW50byBDbG9uZXMKYGBge3IgY29tYmluZWRUQ1IsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTZ9Cgpjb21iaW5lZC5UQ1IgPC0gY29tYmluZVRDUihjb250aWdfbGlzdCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgc2FtcGxlcyA9IHNhbXBsZXMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlTkEgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgcmVtb3ZlTXVsdGkgPSBGQUxTRSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsdGVyTXVsdGkgPSBGQUxTRSkKCiMgRXhwb3J0IHJhdyBkYXRhCmNvbWJpbmVkX1RDUl9kZiA8LSBkby5jYWxsKHJiaW5kLCBjb21iaW5lZC5UQ1IpICU+JSBtdXRhdGUoc2FtcGxlID0gc2FtcGxlc1thcy5udW1lcmljKHNhbXBsZSldKQp3cml0ZS5jc3YoY29tYmluZWRfVENSX2RmLCAiY29tYmluZWRfVENSX2NsZWFuLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQpoZWFkKGNvbWJpbmVkX1RDUl9kZlssMToxMF0pCgpgYGAKCiMjIFByZXBhcmUgU2V1cmF0IE9iamVjdCAoU0lOR0xFIENBTEwpCmBgYHtyICwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTJ9ClRDUiA8LSBBbGxfc2FtcGxlc19NZXJnZWQKRGVmYXVsdEFzc2F5KFRDUikgPC0gIlNDVCIKVENSIDwtIHF1aWV0VENSZ2VuZXMoVENSKQoKIyBZT1VSIE9SSUdJTkFMIFdPUktJTkcgUEFSQU1FVEVSUyAoZnJvbSBzY3JpcHQpClRDUiA8LSBjb21iaW5lRXhwcmVzc2lvbihjb21iaW5lZC5UQ1IsIFRDUiwgCiAgICAgICAgICAgICAgICAgICAgICAgIGNsb25lQ2FsbCA9ICJnZW5lIiwKICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2FtcGxlIiwgICAgICMg4oaQIEZJWEVEOiBVc2UgInNhbXBsZSIgKGV4aXN0cyBpbiBjb21iaW5lZC5UQ1IpCiAgICAgICAgICAgICAgICAgICAgICAgIHByb3BvcnRpb24gPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgY2xvbmVTaXplID0gYyhTaW5nbGUgPSAxLCBTbWFsbCA9IDUsIE1lZGl1bSA9IDIwLCBMYXJnZSA9IDEwMCwgSHlwZXJleHBhbmRlZCA9IDUwMCkpCgpwcmludCgi4pyFIFNVQ0NFU1MgLSBDbG9uZSBtZXRhZGF0YSBhZGRlZCIpCkRpbVBsb3QoVENSLCBncm91cC5ieSA9ICJjbG9uZVNpemUiLCByZWR1Y3Rpb24gPSAidW1hcCIpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHJldihjb2xvcmJsaW5kX3ZlY3RvclsxOjVdKSkKYGBgCgojICoqQ29tYmluaW5nIENsb25lcyBhbmQgU2luZ2xlLUNlbGwgT2JqZWN0cyoqCmBgYHtyICwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9OH0KRGVmYXVsdEFzc2F5KFRDUikgPC0gIlNDVCIKCiMgSWYgQWxsX3NhbXBsZXNfTWVyZ2VkIGlzIGFscmVhZHkgbG9hZGVkIGluIHRoZSBlbnZpcm9ubWVudDoKVENSIDwtIEFsbF9zYW1wbGVzX01lcmdlZAoKIyBEZWZpbmUgY29sb3IgcGFsZXR0ZQpjb2xvcmJsaW5kX3ZlY3RvciA8LSBoY2wuY29sb3JzKG49NywgcGFsZXR0ZSA9ICJpbmZlcm5vIiwgZml4dXAgPSBUUlVFKQoKIyBDb21iaW5lIGV4cHJlc3Npb24gd2l0aCBmaWx0ZXJpbmcgb2YgTkEgY2xvbm90eXBlIGNlbGxzClRDUiA8LSBjb21iaW5lRXhwcmVzc2lvbigKICAgIGNvbWJpbmVkLlRDUiwKICAgIFRDUiwKICAgIGNsb25lQ2FsbCA9ICJnZW5lIiwKICAgIGdyb3VwLmJ5ID0gInNhbXBsZSIsCiAgICBwcm9wb3J0aW9uID0gVFJVRSwKICAgIGZpbHRlck5BID0gVFJVRSAgIyBUaGlzIHdpbGwgZXhjbHVkZSBjZWxscyB3aXRob3V0IGNsb25vdHlwZSBpbmZvCikKCiMgWW91IG5vIGxvbmdlciBuZWVkIHRoZSBtYW51YWwgYmFyY29kZSBtYXRjaGluZyBvciBOQSByZXBsYWNlbWVudHMKCgoKIyBQbG90IFVNQVAgY29sb3JlZCBieSBjbG9uZVNpemUKRGltUGxvdChUQ1IsIGdyb3VwLmJ5ID0gImNsb25lU2l6ZSIsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcyA9IHJldihjb2xvcmJsaW5kX3ZlY3RvcltjKDEsIDMsIDQsIDUsIDcpXSkpCgoKCkRpbVBsb3QoVENSLCBncm91cC5ieSA9ICJjbG9uZVNpemUiLCByZWR1Y3Rpb24gPSAidW1hcCIpCgpEaW1QbG90KFRDUiwgZ3JvdXAuYnkgPSAiY2xvbmVTaXplIiwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXJldihjb2xvcmJsaW5kX3ZlY3RvcltjKDEsMyw0LDUsNildKSkKCgoKI0RlZmluZSBjb2xvciBwYWxldHRlIApjb2xvcmJsaW5kX3ZlY3RvciA8LSBoY2wuY29sb3JzKG49OSwgcGFsZXR0ZSA9ICJpbmZlcm5vIiwgZml4dXAgPSBUUlVFKQoKU2V1cmF0OjpEaW1QbG90KFRDUiwgZ3JvdXAuYnkgPSAiY2xvbmVTaXplIiwgcmVkdWN0aW9uID0gInVtYXAiKSArCiAgICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXJldihjb2xvcmJsaW5kX3ZlY3RvcltjKDEsMyw0LDUsNyldKSkKYGBgCgoKCgpgYGB7ciAsIGZpZy5oZWlnaHQ9NCwgZmlnLndpZHRoPTh9ClRDUiA8LSBjb21iaW5lRXhwcmVzc2lvbihjb21iaW5lZC5UQ1IsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFRDUiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xvbmVDYWxsPSJnZW5lIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2FtcGxlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcHJvcG9ydGlvbiA9IEZBTFNFLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbG9uZVNpemU9YyhTaW5nbGU9MSwgU21hbGw9NSwgTWVkaXVtPTIwLCBMYXJnZT0xMDAsIEh5cGVyZXhwYW5kZWQ9NTAwKSkKClNldXJhdDo6RGltUGxvdChUQ1IsIGdyb3VwLmJ5ID0gImNsb25lU2l6ZSIsIHJlZHVjdGlvbiA9ICJ1bWFwIikgKwogICAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1yZXYoY29sb3JibGluZF92ZWN0b3JbYygxLDMsNCw1LDcpXSkpCmBgYAoKIyAqKlZpc3VhbGl6YXRpb25zIGZvciBTaW5nbGUtQ2VsbCBPYmplY3RzKioKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCgpjbG9uYWxPdmVybGF5KFRDUiwgCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgICBjdXRwb2ludCA9IDEsIAogICAgICAgICAgICAgIGJpbnMgPSAxMCwgCiAgICAgICAgICAgICAgZmFjZXQuYnkgPSAib3JpZy5pZGVudCIpICsgCiAgICAgICAgICAgICAgZ3VpZGVzKGNvbG9yID0gIm5vbmUiKQoKYGBgCgoKYGBge3IgLCBmaWcuaGVpZ2h0PTE0LCBmaWcud2lkdGg9MTh9CgojY2xvbmFsTmV0d29yawojZ2dyYXBoIG5lZWRzIHRvIGJlIGxvYWRlZCBkdWUgdG8gaXNzdWVzIHdpdGggZ2dwbG90CmxpYnJhcnkoZ2dyYXBoKQoKY2xvbmFsTmV0d29yayhUQ1IsIAogICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgICAgICAgICAgICBmaWx0ZXIuY2xvbmVzID0gTlVMTCwKICAgICAgICAgICAgICBmaWx0ZXIuaWRlbnRpdHkgPSBOVUxMLAogICAgICAgICAgICAgIGNsb25lQ2FsbCA9ICJhYSIpCgoKI0V4YW1pbmluZyBDbHVzdGVyIDMgb25seQpjbG9uYWxOZXR3b3JrKFRDUiwgCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgICAgICAgIGZpbHRlci5pZGVudGl0eSA9IDgsCiAgICAgICAgICAgICAgY2xvbmVDYWxsID0gImFhIikKCgpzaGFyZWQuY2xvbmVzIDwtIGNsb25hbE5ldHdvcmsoVENSLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlZHVjdGlvbiA9ICJ1bWFwIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2xvbmVDYWxsID0gImFhIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleHBvcnRDbG9uZXMgPSBUUlVFKQpoZWFkKHNoYXJlZC5jbG9uZXMpCgojZ2dyYXBoIG5lZWRzIHRvIGJlIGxvYWRlZCBkdWUgdG8gaXNzdWVzIHdpdGggZ2dwbG90CmxpYnJhcnkoZ2dyYXBoKQoKI05vIElkZW50aXR5IGZpbHRlcgpjbG9uYWxOZXR3b3JrKFRDUiwgCiAgICAgICAgICAgICAgcmVkdWN0aW9uID0gInVtYXAiLCAKICAgICAgICAgICAgICBncm91cC5ieSA9ICJzZXVyYXRfY2x1c3RlcnMiLAogICAgICAgICAgICAgIGZpbHRlci5jbG9uZXMgPSBOVUxMLAogICAgICAgICAgICAgIGZpbHRlci5pZGVudGl0eSA9IE5VTEwsCiAgICAgICAgICAgICAgY2xvbmVDYWxsID0gImFhIikKYGBgCgoKCgpgYGB7ciAsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTE2fQojIGNsb25hbE9jY3VweQojY2xvbmFsT2NjdXB5CmNsb25hbE9jY3VweShUQ1IsIAogICAgICAgICAgICAgIHguYXhpcyA9ICJzZXVyYXRfY2x1c3RlcnMiKQoKY2xvbmFsT2NjdXB5KFRDUiwgCiAgICAgICAgICAgICAgeC5heGlzID0gIm9yaWcuaWRlbnQiKQoKYGBgCgoKYGBge3IgLCBmaWcuaGVpZ2h0PTIwLCBmaWcud2lkdGg9MTR9CiMgY2xvbmFsT2NjdXB5CmNsb25hbE9jY3VweShUQ1IsIAogICAgICAgICAgICAgIHguYXhpcyA9ICJvcmlnLmlkZW50IikKCmNsb25hbE9jY3VweShUQ1IsIAogICAgICAgICAgICAgICAgICAgICB4LmF4aXMgPSAib3JpZy5pZGVudCIsIAogICAgICAgICAgICAgICAgICAgICBwcm9wb3J0aW9uID0gVFJVRSwgCiAgICAgICAgICAgICAgICAgICAgIGxhYmVsID0gRkFMU0UpCmBgYAoKCmBgYHtyICwgZmlnLmhlaWdodD00LCBmaWcud2lkdGg9Nn0KCiMgZ2V0Q2lyY2xpemUKCmxpYnJhcnkoY2lyY2xpemUpCmxpYnJhcnkoc2NhbGVzKQoKY2lyY2xlcyA8LSBnZXRDaXJjbGl6ZShUQ1IsIAogICAgICAgICAgICAgICAgICAgICAgIGdyb3VwLmJ5ID0gInNldXJhdF9jbHVzdGVycyIpCgojSnVzdCBhc3NpZ25pbmcgdGhlIG5vcm1hbCBjb2xvcnMgdG8gZWFjaCBjbHVzdGVyCmdyaWQuY29scyA8LSBodWVfcGFsKCkobGVuZ3RoKHVuaXF1ZShUQ1Ikc2V1cmF0X2NsdXN0ZXJzKSkpCm5hbWVzKGdyaWQuY29scykgPC0gdW5pcXVlKFRDUiRzZXVyYXRfY2x1c3RlcnMpCgojR3JhcGhpbmcgdGhlIGNob3JkIGRpYWdyYW0KY2hvcmREaWFncmFtKGNpcmNsZXMsIHNlbGYubGluayA9IDEsIGdyaWQuY29sID0gZ3JpZC5jb2xzKQoKCmNpcmNsZXMgPC0gZ2V0Q2lyY2xpemUoVENSLCBncm91cC5ieSA9ICJvcmlnLmlkZW50IikKCmdyaWQuY29scyA8LSBzY2FsZXM6Omh1ZV9wYWwoKShsZW5ndGgodW5pcXVlKFRDUkBhY3RpdmUuaWRlbnQpKSkKbmFtZXMoZ3JpZC5jb2xzKSA8LSBsZXZlbHMoVENSQGFjdGl2ZS5pZGVudCkKCmNob3JkRGlhZ3JhbShjaXJjbGVzLCAKICAgICAgICAgICAgIHNlbGYubGluayA9IDEsIAogICAgICAgICAgICAgZ3JpZC5jb2wgPSBncmlkLmNvbHMpCmBgYAoKCiMgKipRdWFudGlmeWluZyBDbG9uYWwgQmlhcyoqCgoqKiMgIyBTdGFydHJhY0RpdmVyc2l0eQojIEZyb20gdGhlIGV4Y2VsbGVudCB3b3JrIGJ5IExlaSBaaGFuZywgZXQgYWwuLCB0aGUgYXV0aG9ycyBpbnRyb2R1Y2UgbmV3IG1ldGhvZHMgZm9yIGxvb2tpbmcgYXQgY2xvbmVzIGJ5IGNlbGx1bGFyIG9yaWdpbnMgYW5kIGNsdXN0ZXIgaWRlbnRpZmljYXRpb24uIFRoZWlyIFNUQVJUUkFDIHNvZnR3YXJlIGhhcyBiZWVuIGFkYXB0ZWQgdG8gd29yayB3aXRoIHNjUmVwZXJ0b2lyZSBhbmQgcGxlYXNlIHJlYWQgYW5kIGNpdGUgdGhlaXIgZXhjZWxsZW50IHdvcmsuCiMgCiMgSW4gb3JkZXIgdG8gdXNlIHRoZSBTdGFydHJhY0RpdmVyc2l0eSgpIGZ1bmN0aW9uLCB5b3Ugd2lsbCBuZWVkIHRvIGluY2x1ZGUgdGhlIHByb2R1Y3Qgb2YgdGhlIGNvbWJpbmVkRXhwcmVzc2lvbigpIGZ1bmN0aW9uLiBUaGUgc2Vjb25kIHJlcXVpcmVtZW50IGlzIGEgY29sdW1uIGhlYWRlciBpbiB0aGUgbWV0YSBkYXRhIG9mIHRoZSBTZXVyYXQgb2JqZWN0IHRoYXQgaGFzIHRpc3N1ZSBvZiBvcmlnaW4uIEluIHRoZSBleGFtcGxlIGRhdGEsIHR5cGUgY29ycmVzcG9uZHMgdG8gdGhlIGNvbHVtbiDigJxUeXBl4oCdLCB3aGljaCBpbmNsdWRlcyB0aGUg4oCcUOKAnSBhbmQg4oCcVOKAnSBjbGFzc2lmaWVycy4gVGhlIGluZGljZXMgY2FuIGJlIHN1YnNldHRlZCBmb3IgYSBzcGVjaWZpYyBwYXRpZW50IG9yIGV4YW1pbmVkIG92ZXJhbGwgdXNpbmcgdGhlIGJ5IHZhcmlhYmxlLiBJbXBvcnRhbnRseSwgdGhlIGZ1bmN0aW9uIHVzZXMgb25seSB0aGUgc3RyaWN0IGRlZmluaXRpb24gb2YgYSBjbG9uZSBvZiB0aGUgVkRKQyBnZW5lcyBhbmQgdGhlIENEUjMgbnVjbGVvdGlkZSBzZXF1ZW5jZS4KIyAKIyBUaGUgaW5kaWNlcyBvdXRwdXQgaW5jbHVkZXM6CiMgCiMgZXhwYSAtIENsb25hbCBFeHBhbnNpb24KIyBtaWdyIC0gQ3Jvc3MtdGlzc3VlIE1pZ3JhdGlvbgojIHRyYW4gLSBTdGF0ZSBUcmFuc2l0aW9uCioqCgoKYGBge3IgLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMn0KCklkZW50cyhUQ1IpIDwtICJzZXVyYXRfY2x1c3RlcnMiCgpTdGFydHJhY0RpdmVyc2l0eShUQ1IsIAogICAgICAgICAgICAgICAgICB0eXBlID0gIm9yaWcuaWRlbnQiLAogICAgICAgICAgICAgICAgICBncm91cC5ieSA9ICJvcmlnLmlkZW50IikKCgpgYGAKCiMgKipjbG9uYWxCaWFzKioKYGBge3IgLCBmaWcuaGVpZ2h0PTQsIGZpZy53aWR0aD0xMH0KCmNsb25hbEJpYXMoVENSLCAKICAgICAgICAgICBjbG9uZUNhbGwgPSAiYWEiLCAKICAgICAgICAgICBzcGxpdC5ieSA9ICJvcmlnLmlkZW50IiwgCiAgICAgICAgICAgZ3JvdXAuYnkgPSAic2V1cmF0X2NsdXN0ZXJzIiwKICAgICAgICAgICBuLmJvb3RzID0gMTAsIAogICAgICAgICAgIG1pbi5leHBhbmQgPTUpCgpgYGAKCgojICoqVFJCViBDTE9OQUxJVFkqKgoKIyBDT01QTEVURSBBTkFMWVNJUzogVFJCViArIFRSQVYgKEJPVEggQ0hBSU5TKQpgYGB7cn0KCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkodGlkeXIpCmxpYnJhcnkoc3RyaW5ncikKCiMgQ09NUExFVEUgRklYRUQgQU5BTFlTSVM6IFRSQlYgKFRDUjIpICsgVFJBViAoVENSMSkKdmdlbmVfdl9hbGxfY291bnRzIDwtIGRhdGEuZnJhbWUoKQoKZm9yKGkgaW4gc2VxX2Fsb25nKGNvbWJpbmVkLlRDUikpIHsKICAgIGNvbnRpZ3MgPC0gY29tYmluZWQuVENSW1tpXV0KICAgIHNhbXBsZV9uYW1lIDwtIG5hbWVzKGNvbWJpbmVkLlRDUilbaV0KICAgIAogICAgIyBCRVRBIENIQUlOOiBUUkJWIGZyb20gVENSMiAoVFJCQysgY2VsbHMpCiAgICBiZXRhX2NvdW50cyA8LSBjb250aWdzICU+JQogICAgICAgIGZpbHRlcihncmVwbCgiVFJCQyIsIFRDUjIpKSAlPiUKICAgICAgICBtdXRhdGUodl9nZW5lID0gc3RyX2V4dHJhY3QoVENSMiwgIlRSQlZbXi5dKyIpLAogICAgICAgICAgICAgICBjaGFpbiA9ICJUUkJWIikgJT4lCiAgICAgICAgZ3JvdXBfYnkodl9nZW5lLCBjaGFpbikgJT4lCiAgICAgICAgc3VtbWFyaXNlKG5DZWxscyA9IG4oKSwgc2FtcGxlID0gc2FtcGxlX25hbWUsIC5ncm91cHMgPSAiZHJvcCIpICU+JQogICAgICAgIGZpbHRlcighaXMubmEodl9nZW5lKSkKICAgIAogICAgIyBBTFBIQSBDSEFJTjogVFJBViBmcm9tIFRDUjEgKFRSQVYrIGNlbGxzKQogICAgYWxwaGFfY291bnRzIDwtIGNvbnRpZ3MgJT4lCiAgICAgICAgZmlsdGVyKGdyZXBsKCJUUkFWIiwgVENSMSkpICU+JQogICAgICAgIG11dGF0ZSh2X2dlbmUgPSBzdHJfZXh0cmFjdChUQ1IxLCAiVFJBVlteLl0rIiksCiAgICAgICAgICAgICAgIGNoYWluID0gIlRSQVYiKSAlPiUKICAgICAgICBncm91cF9ieSh2X2dlbmUsIGNoYWluKSAlPiUKICAgICAgICBzdW1tYXJpc2UobkNlbGxzID0gbigpLCBzYW1wbGUgPSBzYW1wbGVfbmFtZSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgICAgICAgZmlsdGVyKCFpcy5uYSh2X2dlbmUpKQogICAgCiAgICB2Z2VuZV92X2FsbF9jb3VudHMgPC0gYmluZF9yb3dzKHZnZW5lX3ZfYWxsX2NvdW50cywgYmV0YV9jb3VudHMsIGFscGhhX2NvdW50cykKfQoKIyBMNF9CIGxvdy1mcmVxdWVuY3kgZ2VuZXMgKDwwLjElID0gPDcgY2VsbHMpCmw0Yl9sb3dfZnJlcSA8LSB2Z2VuZV92X2FsbF9jb3VudHMgJT4lCiAgICBmaWx0ZXIoc2FtcGxlID09ICJMNF9CIiwgbkNlbGxzIDwgNykgJT4lCiAgICBhcnJhbmdlKGNoYWluLCBkZXNjKG5DZWxscykpCgojIFdpZGUgZm9ybWF0IChzYW1wbGVzIMOXIFYtZ2VuZXMpCnZnZW5lX3dpZGUgPC0gdmdlbmVfdl9hbGxfY291bnRzICU+JQogICAgc2VsZWN0KC1jaGFpbikgJT4lCiAgICBwaXZvdF93aWRlcihuYW1lc19mcm9tID0gc2FtcGxlLCB2YWx1ZXNfZnJvbSA9IG5DZWxscywgdmFsdWVzX2ZpbGwgPSAwKQoKIyBTdW1tYXJ5IHN0YXRpc3RpY3MKdG90YWxfc3VtbWFyeSA8LSB2Z2VuZV92X2FsbF9jb3VudHMgJT4lCiAgICBncm91cF9ieShzYW1wbGUsIGNoYWluKSAlPiUKICAgIHN1bW1hcmlzZSh0b3RhbF9jZWxscyA9IHN1bShuQ2VsbHMpLCAuZ3JvdXBzID0gImRyb3AiKQoKIyBQZXJjZW50YWdlcyAoJSB1c2FnZSBwZXIgc2FtcGxlL2NoYWluKQp2Z2VuZV9wZXJjZW50IDwtIHZnZW5lX3ZfYWxsX2NvdW50cyAlPiUKICAgIGdyb3VwX2J5KHNhbXBsZSwgY2hhaW4pICU+JQogICAgbXV0YXRlKHBlcmNlbnQgPSByb3VuZChuQ2VsbHMgLyBzdW0obkNlbGxzKSAqIDEwMCwgMikpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgc2VsZWN0KHNhbXBsZSwgY2hhaW4sIHZfZ2VuZSwgbkNlbGxzLCBwZXJjZW50KQoKIyMgU0FWRSA1IFBVQkxJQ0FUSU9OLVJFQURZIEZJTEVTCndyaXRlLmNzdih2Z2VuZV93aWRlLCAiMDFfVmdlbmVfY291bnRzX3dpZGUuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdihsNGJfbG93X2ZyZXEsICIwMl9MNEJfbG93ZnJlcV9WZ2VuZXMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdih0b3RhbF9zdW1tYXJ5LCAiMDNfVmdlbmVfdG90YWxzX3N1bW1hcnkuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdih2Z2VuZV9wZXJjZW50LCAiMDRfVmdlbmVfcGVyY2VudGFnZXMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCndyaXRlLmNzdih2Z2VuZV92X2FsbF9jb3VudHMsICIwNV9WZ2VuZV9sb25nX2Zvcm1hdC5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKCiMjIENPTVBSRUhFTlNJVkUgUFJFVklFVwpjYXQoIvCfk4ogVE9UQUwgQ0VMTFMgU1VNTUFSWTpcbiIpCnByaW50KHRvdGFsX3N1bW1hcnkpCgpjYXQoc3ByaW50ZigiXG7wn46vIEw0X0IgbG93LWZyZXEgZ2VuZXMgKDwwLjElJSk6ICVkXG4iLCBucm93KGw0Yl9sb3dfZnJlcSkpKQpwcmludCgiVG9wIDEwIEw0X0IgbG93LWZyZXE6IikKcHJpbnQoaGVhZChsNGJfbG93X2ZyZXEsIDEwKSkKCmNhdCgiXG7wn5SlIFRPUCAzIFYtR0VORVMgUEVSIFNBTVBMRS9DSEFJTjpcbiIpCnZnZW5lX3ZfYWxsX2NvdW50cyAlPiUKICAgIGdyb3VwX2J5KHNhbXBsZSwgY2hhaW4pICU+JQogICAgc2xpY2VfbWF4KG5DZWxscywgbiA9IDMpICU+JQogICAgdW5ncm91cCgpICU+JQogICAgYXJyYW5nZShzYW1wbGUsIGNoYWluLCBkZXNjKG5DZWxscykpICU+JQogICAgbXV0YXRlKHBlcmNlbnQgPSByb3VuZChuQ2VsbHMgLyB0b3RhbF9zdW1tYXJ5JHRvdGFsX2NlbGxzW21hdGNoKHNhbXBsZSwgdG90YWxfc3VtbWFyeSRzYW1wbGUpXSwgMSkpICU+JQogICAgc2VsZWN0KHNhbXBsZSwgY2hhaW4sIHZfZ2VuZSwgbkNlbGxzLCBwZXJjZW50KSAlPiUKICAgIHByaW50KG4gPSBJbmYpCgpjYXQoIlxu4pyFIDUgRklMRVMgU0FWRUQgRk9SIFRIRVNJUzpcbiIpCmNhdCgiLSAwMV9WZ2VuZV9jb3VudHNfd2lkZS5jc3YgKGhlYXRtYXAgaW5wdXQpXG4iKQpjYXQoIi0gMDRfVmdlbmVfcGVyY2VudGFnZXMuY3N2IChGaWcgVGFibGUpXG4iKQpjYXQoIi0gMDJfTDRCX2xvd2ZyZXFfVmdlbmVzLmNzdiAoaGV0ZXJvZ2VuZWl0eSBtYXJrZXJzKVxuIikKCmBgYAoKIyBWZ2VuZS1oZWF0bWFwLXZpc3VhbGl6YXRpb24KYGBge3J9CgpsaWJyYXJ5KHJlc2hhcGUyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc3RyaW5ncikKCiMgRklYRUQ6IENsZWFuIHNhbXBsZSBuYW1lcyArIFRSQlYyNC0xIGNvbnRyYXN0CnZnZW5lX3BlcmNlbnQgPC0gcmVhZC5jc3YoIjA0X1ZnZW5lX3BlcmNlbnRhZ2VzLmNzdiIpCgojIFNhbXBsZSBuYW1lIG1hcHBpbmcgKGRpc3BsYXktZnJpZW5kbHkpCnNhbXBsZV9tYXBwaW5nIDwtIGMoIkwxIiA9ICJMMSIsICJMMiIgPSAiTDIiLCAiTDNfQiIgPSAiTDMiLCAiTDRfQiIgPSAiTDQiLCAKICAgICAgICAgICAgICAgICAgICAiTDUiID0gIkw1IiwgIkw2IiA9ICJMNiIsICJMNyIgPSAiTDciKQpjZWxsX2xpbmVzIDwtIG5hbWVzKHNhbXBsZV9tYXBwaW5nKQoKIyBGSUxURVI6IFRSQlYyMC0xICsgVFJCVjI0LTEgKGNlbGwgbGluZXMgb25seSkKdGFyZ2V0X2dlbmVzIDwtIGMoIlRSQlYyMC0xIiwgIlRSQlYyNC0xIikKZ2VuZV90YWJsZV9maWx0ZXJlZCA8LSB2Z2VuZV9wZXJjZW50ICU+JQogICAgZmlsdGVyKHZfZ2VuZSAlaW4lIHRhcmdldF9nZW5lcywKICAgICAgICAgICBzYW1wbGUgJWluJSBjZWxsX2xpbmVzKSAlPiUKICAgIG11dGF0ZShzYW1wbGVfZGlzcGxheSA9IHNhbXBsZV9tYXBwaW5nW3NhbXBsZV0pICU+JQogICAgc2VsZWN0KHNhbXBsZV9kaXNwbGF5LCB2X2dlbmUsIHBlcmNlbnQpICU+JQogICAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbSA9IHNhbXBsZV9kaXNwbGF5LCB2YWx1ZXNfZnJvbSA9IHBlcmNlbnQsIHZhbHVlc19maWxsID0gMCkgJT4lCiAgICBjb2x1bW5fdG9fcm93bmFtZXMoInZfZ2VuZSIpICU+JQogICAgYXMubWF0cml4KCkKCmNhdCgiRmlsdGVyZWQgZ2VuZXM6IiwgbnJvdyhnZW5lX3RhYmxlX2ZpbHRlcmVkKSwgIlxuIikKcHJpbnQocm93bmFtZXMoZ2VuZV90YWJsZV9maWx0ZXJlZCkpCgojIE1lbHQgd2l0aCBkaXNwbGF5IG5hbWVzCm1hdF9tZWx0IDwtIG1lbHQoYXMubWF0cml4KGdlbmVfdGFibGVfZmlsdGVyZWQpKQpjb2xuYW1lcyhtYXRfbWVsdCkgPC0gYygiR2VuZSIsICJTYW1wbGUiLCAiUGVyY2VudCIpCgojIEZJWEVEIEhFQVRNQVA6IEJldHRlciBjb250cmFzdCBmb3IgVFJCVjI0LTEKcDEgPC0gZ2dwbG90KG1hdF9tZWx0LCBhZXMoeCA9IFNhbXBsZSwgeSA9IEdlbmUsIGZpbGwgPSBQZXJjZW50KSkgKwogICAgZ2VvbV90aWxlKGx3ZCA9IDAuNiwgY29sb3IgPSAid2hpdGUiLCBoZWlnaHQgPSAwLjg1LCB3aWR0aCA9IDAuODUpICsKICAgIHNjYWxlX2ZpbGxfZ3JhZGllbnRuKG5hbWUgPSAiJSBvZiBULWNlbGxzIiwKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9ycyA9IGMoIm5hdnkiLCAiZ29sZCIsICJkYXJrcmVkIiksCiAgICAgICAgICAgICAgICAgICAgICAgICBsaW1pdHMgPSBjKDAsIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICBicmVha3MgPSBjKDAsIDI1LCA1MCwgNzUsIDEwMCksCiAgICAgICAgICAgICAgICAgICAgICAgICBuYS52YWx1ZSA9ICJncmV5OTAiKSArCiAgICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEzKSArCiAgICB0aGVtZShheGlzLnRleHQueCAgPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgdmp1c3QgPSAxLCBoanVzdCA9IDEsIHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRleHQueSAgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEzLCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgIGF4aXMudGl0bGUgICA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJyaWdodCIsCiAgICAgICAgICBsZWdlbmQudGl0bGUuYWxpZ24gPSAwLjUsCiAgICAgICAgICBsZWdlbmQudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyLCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSkpICsKICAgIGxhYnModGl0bGUgPSAiVFJCViBPbGlnb2Nsb25hbGl0eSBpbiBTw6l6YXJ5IENlbGwgTGluZXNcblRSQlYyMC0xIGRvbWluYXRlcyAo4omlOTklKTsgVFJCVjI0LTE6IEw0LXNwZWNpZmljIG1pbm9yIGNsb25lIikKCnByaW50KHAxKQoKIyBISUdILVJFUyBQVUJMSUNBVElPTiBPVVRQVVQKZ2dzYXZlKCJGaWdfVFJCVl9oZWF0bWFwX0wzX0w0X2NsZWFuLnBuZyIsIHAxLCB3aWR0aCA9IDExLCBoZWlnaHQgPSA0LjUsIGRwaSA9IDYwMCwgYmcgPSAid2hpdGUiKQpnZ3NhdmUoIkZpZ19UUkJWX2hlYXRtYXBfTDNfTDRfY2xlYW4ucGRmIiwgcDEsIHdpZHRoID0gMTEsIGhlaWdodCA9IDQuNSkKCmNhdCgiXG7inIUgRklYRUQgSEVBVE1BUCBTQVZFRCAoTDMvTDQgbGFiZWxzICsgVFJCVjI0LTEgdmlzaWJsZSEpXG4iKQoKYGBgCgoKCiMgVmdlbmUtaGVhdG1hcC12aXN1YWxpemF0aW9uCmBgYHtyfQoKbGlicmFyeShyZXNoYXBlMikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KHN0cmluZ3IpCgp2Z2VuZV9wZXJjZW50IDwtIHJlYWQuY3N2KCIwNF9WZ2VuZV9wZXJjZW50YWdlcy5jc3YiKQpzYW1wbGVfbWFwcGluZyA8LSBjKCJMMSIgPSAiTDEiLCAiTDIiID0gIkwyIiwgIkwzX0IiID0gIkwzIiwgIkw0X0IiID0gIkw0IiwgCiAgICAgICAgICAgICAgICAgICAgIkw1IiA9ICJMNSIsICJMNiIgPSAiTDYiLCAiTDciID0gIkw3IikKY2VsbF9saW5lcyA8LSBuYW1lcyhzYW1wbGVfbWFwcGluZykKCiMgPT09IFBMT1QgMTogVFJCVjIwLTEgT05MWSAoRG9taW5hbmNlKSA9PT0KdHJidjIwX2RhdGEgPC0gdmdlbmVfcGVyY2VudCAlPiUKICAgIGZpbHRlcih2X2dlbmUgPT0gIlRSQlYyMC0xIiwgc2FtcGxlICVpbiUgY2VsbF9saW5lcykgJT4lCiAgICBtdXRhdGUoc2FtcGxlX2Rpc3BsYXkgPSBzYW1wbGVfbWFwcGluZ1tzYW1wbGVdKQoKcDIwIDwtIGdncGxvdCh0cmJ2MjBfZGF0YSwgYWVzKHggPSByZW9yZGVyKHNhbXBsZV9kaXNwbGF5LCBwZXJjZW50KSwgeSA9IHBlcmNlbnQpKSArCiAgICBnZW9tX2NvbChmaWxsID0gImRhcmtyZWQiLCBhbHBoYSA9IDAuOCwgd2lkdGggPSAwLjcpICsKICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBzcHJpbnRmKCIlLjFmJSUiLCBwZXJjZW50KSksIGhqdXN0ID0gLTAuMSwgc2l6ZSA9IDQpICsKICAgIGNvb3JkX2ZsaXAoKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGltaXRzID0gYygwLCAxMDIpLCBleHBhbmQgPSBleHBhbnNpb24obXVsdCA9IGMoMCwgMC4wOCkpKSArCiAgICBsYWJzKHRpdGxlID0gIlRSQlYyMC0xIERvbWluYW5jZSAo4omlOTklIGluIGFsbCBTw6l6YXJ5IGxpbmVzKSIsIAogICAgICAgICB4ID0gIkNlbGwgTGluZSIsIHkgPSAiJSBvZiBUUkIrIFQtY2VsbHMiKSArCiAgICB0aGVtZV9jbGFzc2ljKGJhc2Vfc2l6ZSA9IDEzKSArCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNSwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIsIGZhY2UgPSAiYm9sZCIpLAogICAgICAgICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTIpKQoKIyA9PT0gUExPVCAyOiBUUkJWMjQtMSBPTkxZIChMNC1zcGVjaWZpYyBtaW5vciBjbG9uZSkgPT09CnRyYnYyNF9kYXRhIDwtIHZnZW5lX3BlcmNlbnQgJT4lCiAgICBmaWx0ZXIodl9nZW5lID09ICJUUkJWMjQtMSIsIHNhbXBsZSAlaW4lIGNlbGxfbGluZXMpICU+JQogICAgbXV0YXRlKHNhbXBsZV9kaXNwbGF5ID0gc2FtcGxlX21hcHBpbmdbc2FtcGxlXSkKCnAyNCA8LSBnZ3Bsb3QodHJidjI0X2RhdGEsIGFlcyh4ID0gcmVvcmRlcihzYW1wbGVfZGlzcGxheSwgcGVyY2VudCksIHkgPSBwZXJjZW50KSkgKwogICAgZ2VvbV9jb2woYWVzKGZpbGwgPSBzYW1wbGVfZGlzcGxheSA9PSAiTDQiKSwgd2lkdGggPSAwLjcsIGFscGhhID0gMC44KSArCiAgICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJGQUxTRSIgPSAic3RlZWxibHVlIiwgIlRSVUUiID0gIm9yYW5nZSIpLCBndWlkZSA9ICJub25lIikgKwogICAgZ2VvbV90ZXh0KGFlcyhsYWJlbCA9IHNwcmludGYoIiUuM2YlJSIsIHBlcmNlbnQpKSwgaGp1c3QgPSAtMC4xLCBzaXplID0gNCkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDAuMyksIGV4cGFuZCA9IGV4cGFuc2lvbihtdWx0ID0gYygwLCAwLjE1KSkpICsKICAgIGxhYnModGl0bGUgPSAiVFJCVjI0LTE6IEw0LXNwZWNpZmljIG1pbm9yIGNsb25lICgwLjE5NiUpIiwgCiAgICAgICAgIHggPSAiQ2VsbCBMaW5lIiwgeSA9ICIlIG9mIFRSQisgVC1jZWxscyIpICsKICAgIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTMpICsKICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE1LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiwgZmFjZSA9ICJib2xkIiksCiAgICAgICAgICBheGlzLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMikpCgojIENPTUJJTkVEIEZJR1VSRQpwX2NvbWJpbmVkIDwtIHAyMCArIHAyNCArIHBsb3RfbGF5b3V0KG5jb2wgPSAyKSAmIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKcHJpbnQocF9jb21iaW5lZCkKCiMgU0FWRSBUSEVTSVMgRklHVVJFCmdnc2F2ZSgiRmlnX1RSQlYyMC0yNF9zZXBhcmF0ZV9MNF9oaWdobGlnaHQucG5nIiwgcF9jb21iaW5lZCwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gNywgZHBpID0gNjAwLCBiZyA9ICJ3aGl0ZSIpCmdnc2F2ZSgiRmlnX1RSQlYyMC0yNF9zZXBhcmF0ZV9MNF9oaWdobGlnaHQucGRmIiwgcF9jb21iaW5lZCwgd2lkdGggPSAxNCwgaGVpZ2h0ID0gNykKCmNhdCgi4pyFIFNFUEFSQVRFIFBMT1RTIFNBVkVEIC0gTDQgVFJCVjI0LTEgaGlnaGxpZ2h0ZWQgaW4gT1JBTkdFIVxuIikKCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTEwfQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoc2NhbGVzKQpsaWJyYXJ5KFJDb2xvckJyZXdlcikKCnNhbXBsZV9tYXBwaW5nIDwtIGMoIkwxIiA9ICJMMSIsICJMMiIgPSAiTDIiLCAiTDNfQiIgPSAiTDMiLCAiTDRfQiIgPSAiTDQiLCAKICAgICAgICAgICAgICAgICAgICAiTDUiID0gIkw1IiwgIkw2IiA9ICJMNiIsICJMNyIgPSAiTDciKQpjZWxsX2xpbmVzIDwtIGMoIkwxIiwgIkwyIiwgIkwzX0IiLCAiTDRfQiIsICJMNSIsICJMNiIsICJMNyIpCgpwZXJjZW50X2RhdGEgPC0gdmdlbmVfcGVyY2VudCAlPiUKICAgIGZpbHRlcihjaGFpbiA9PSAiVFJCViIpICU+JQogICAgbXV0YXRlKHNhbXBsZV9kaXNwbGF5ID0gc2FtcGxlX21hcHBpbmdbc2FtcGxlXSkgJT4lCiAgICBncm91cF9ieShzYW1wbGVfZGlzcGxheSkgJT4lCiAgICBtdXRhdGUocGVyY2VudF9zY1JlcGVydG9pcmUgPSBuQ2VsbHMgLyBzdW0obkNlbGxzKSAqIDEwMCkgJT4lCiAgICB1bmdyb3VwKCkKCmw0X3RyYnYyNCA8LSBwZXJjZW50X2RhdGEgJT4lIGZpbHRlcihzYW1wbGVfZGlzcGxheSA9PSAiTDQiLCB2X2dlbmUgPT0gIlRSQlYyNC0xIikKdHJidjIwX2RhdGEgPC0gcGVyY2VudF9kYXRhICU+JSBmaWx0ZXIodl9nZW5lID09ICJUUkJWMjAtMSIsIHNhbXBsZSAlaW4lIGNlbGxfbGluZXMpCm1pbm9yX2RhdGEgPC0gcGVyY2VudF9kYXRhICU+JQogICAgZmlsdGVyKHNhbXBsZSAlaW4lIGNlbGxfbGluZXMsIHZfZ2VuZSAhPSAiVFJCVjIwLTEiLCB2X2dlbmUgIT0gIlRSQlYyNC0xIikgJT4lCiAgICBncm91cF9ieShzYW1wbGVfZGlzcGxheSkgJT4lCiAgICBzbGljZV9tYXgocGVyY2VudF9zY1JlcGVydG9pcmUsIG4gPSAxMCkgJT4lCiAgICB1bmdyb3VwKCkKCm1pbm9yX2NvbG9ycyA8LSBjb2xvclJhbXBQYWxldHRlKFJDb2xvckJyZXdlcjo6YnJld2VyLnBhbCg5LCAiUHVCdUduIilbMzo5XSkobGVuZ3RoKHVuaXF1ZShtaW5vcl9kYXRhJHZfZ2VuZSkpKQoKcF9maW5hbCA8LSBnZ3Bsb3QoKSArCiAgICBnZW9tX2NvbChkYXRhID0gbWlub3JfZGF0YSwgCiAgICAgICAgICAgICBhZXMoeCA9IHNhbXBsZV9kaXNwbGF5LCB5ID0gcGVyY2VudF9zY1JlcGVydG9pcmUvMTAwLCBmaWxsID0gdl9nZW5lKSwKICAgICAgICAgICAgIHBvc2l0aW9uID0gInN0YWNrIiwgYWxwaGEgPSAwLjcsIHdpZHRoID0gMC44NSwgY29sb3IgPSBOQSkgKwogICAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gbWlub3JfY29sb3JzLCBndWlkZSA9ICJub25lIikgKwogICAgZ2VvbV9jb2woZGF0YSA9IHRyYnYyMF9kYXRhLCAKICAgICAgICAgICAgIGFlcyh4ID0gc2FtcGxlX2Rpc3BsYXksIHkgPSBwZXJjZW50X3NjUmVwZXJ0b2lyZS8xMDApLAogICAgICAgICAgICAgZmlsbCA9ICIjRTYzOTQ2IiwgYWxwaGEgPSAwLjk1LCB3aWR0aCA9IDAuODUsIGNvbG9yID0gIndoaXRlIiwgbGluZXdpZHRoID0gMC41KSArCiAgICBnZW9tX2NvbChkYXRhID0gbDRfdHJidjI0LCAKICAgICAgICAgICAgIGFlcyh4ID0gc2FtcGxlX2Rpc3BsYXksIHkgPSBwZXJjZW50X3NjUmVwZXJ0b2lyZS8xMDApLAogICAgICAgICAgICAgZmlsbCA9ICIjRjRBMjYxIiwgYWxwaGEgPSAxLCB3aWR0aCA9IDAuODUsIGNvbG9yID0gImJsYWNrIiwgbGluZXdpZHRoID0gMC44KSArCiAgICBnZW9tX3RleHQoZGF0YSA9IHRyYnYyMF9kYXRhLAogICAgICAgICAgICAgIGFlcyh4ID0gc2FtcGxlX2Rpc3BsYXksIHkgPSAwLjg1LCBsYWJlbCA9IHNwcmludGYoIiUuMGYlJSIsIHBlcmNlbnRfc2NSZXBlcnRvaXJlKSksCiAgICAgICAgICAgICAgc2l6ZSA9IDQuNSwgZm9udGZhY2UgPSAiYm9sZCIsIGNvbG9yID0gIndoaXRlIikgKwogICAgZ2VvbV90ZXh0KGRhdGEgPSBsNF90cmJ2MjQsCiAgICAgICAgICAgICAgYWVzKHggPSBzYW1wbGVfZGlzcGxheSwgeSA9IDAuMTUsIAogICAgICAgICAgICAgICAgICBsYWJlbCA9IHNwcmludGYoIlRSQlYyNC0xXG4wLjIwJSUiKSksCiAgICAgICAgICAgICAgaGp1c3QgPSAtMC4xLCBzaXplID0gNC4yLCBmb250ZmFjZSA9ICJib2xkIiwgY29sb3IgPSAiYmxhY2siKSArCiAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gcGVyY2VudF9mb3JtYXQoYWNjdXJhY3kgPSAxKSwgZXhwYW5kID0gZXhwYW5zaW9uKG11bHQgPSBjKDAsIDAuMTIpKSkgKwogICAgY29vcmRfZmxpcCgpICsKICAgIGxhYnModGl0bGUgPSAiVFJCViBNb25vY2xvbmFsIERvbWluYW5jZSBpbiBTw6l6YXJ5IENlbGwgTGluZXMiLAogICAgICAgICBzdWJ0aXRsZSA9ICJUUkJWMjAtMSAoOTktMTAwJSkgKyBMNC1zcGVjaWZpYyBUUkJWMjQtMSAoMC4yMCUpIikgKwogICAgdGhlbWVfY2xhc3NpYyhiYXNlX3NpemUgPSAxNSkgKwogICAgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0LCBmYWNlID0gImJvbGQiKSwKICAgICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIiwKICAgICAgICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4LCBmYWNlID0gImJvbGQiLCBoanVzdCA9IDAuNSksCiAgICAgICAgICBwbG90LnN1YnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCwgaGp1c3QgPSAwLjUpKQoKcHJpbnQocF9maW5hbCkKCmdnc2F2ZSgiRmlnX1RSQlZfbW9ub2Nsb25hbF9maW5hbC5wbmciLCBwX2ZpbmFsLCB3aWR0aCA9IDEyLCBoZWlnaHQgPSA5LCBkcGkgPSA2MDAsIGJnID0gIndoaXRlIikKZ2dzYXZlKCJGaWdfVFJCVl9tb25vY2xvbmFsX2ZpbmFsLnBkZiIsIHBfZmluYWwsIHdpZHRoID0gMTIsIGhlaWdodCA9IDksIGJnID0gIndoaXRlIikKCmNhdCgi4pyFIE1PTk9DTE9OQUwgdGl0bGUgKyBMNCBiaWNsb25hbCBoaWdobGlnaHQhXG4iKQpgYGAKCgoKCgpgYGAKCg==