1 Introduction

Cell–cell communication predictions from LIANA (L1–L7) were validated against curated ligand–receptor pairs from the OmniPath LigRecExtra database (downloaded 2026-03-13 from https://omnipathdb.org/interactions?datasets=ligrecextra).


2 Setup


3 Load LIANA Results

read_liana <- function(path, name) {
  df <- read.csv(path)
  df$CellLine <- name
  return(df)
}

L1 <- read_liana("liana_L1_aggregate_results.csv", "L1")
L2 <- read_liana("liana_L2_aggregate_results.csv", "L2")
L3 <- read_liana("liana_L3_aggregate_results.csv", "L3")
L4 <- read_liana("liana_L4_aggregate_results.csv", "L4")
L5 <- read_liana("liana_L5_aggregate_results.csv", "L5")
L6 <- read_liana("liana_L6_aggregate_results.csv", "L6")
L7 <- read_liana("liana_L7_aggregate_results.csv", "L7")

all_data <- bind_rows(L1, L2, L3, L4, L5, L6, L7)

# Create unique interaction label
all_data$Interaction <- paste(
  all_data$ligand.complex, "->", all_data$receptor.complex
)

cat("Total LIANA records loaded:", nrow(all_data), "\n")
Total LIANA records loaded: 48958 
cat("Unique LR labels:          ", length(unique(all_data$Interaction)), "\n")
Unique LR labels:           605 

4 Load OmniPath LR Reference (Static CSV — No Package Needed)

# SOLUTION: Load from pre-downloaded static file
# This completely bypasses the OmnipathR package and the httr2 incompatibility
# The file was downloaded once from:
# https://omnipathdb.org/interactions?datasets=ligrecextra&organisms=9606&genesymbols=yes

lr_db <- read.csv("OmniPath_LigRecExtra_Human_2026.csv",
                  stringsAsFactors = FALSE)

# Validate it loaded correctly
stopifnot(all(c("ligand", "receptor") %in% colnames(lr_db)))

# Create a fast lookup key for matching
lr_db <- lr_db %>%
  dplyr::distinct(ligand, receptor) %>%
  dplyr::filter(nchar(ligand) > 0 & nchar(receptor) > 0) %>%
  dplyr::mutate(lr_key = paste(ligand, receptor, sep = "__"))

cat("OmniPath LR reference pairs loaded:", nrow(lr_db), "\n")
OmniPath LR reference pairs loaded: 6555 

5 Filter LIANA Predictions Against OmniPath

# Create matching key in LIANA data
all_data <- all_data %>%
  dplyr::mutate(lr_key = paste(ligand.complex, receptor.complex, sep = "__"))

# Semi-join: retain only LIANA pairs confirmed in the OmniPath LR database
filtered_data <- all_data %>%
  dplyr::filter(lr_key %in% lr_db$lr_key)

cat("Before OmniPath filter:", nrow(all_data),     "\n")
Before OmniPath filter: 48958 
cat("After OmniPath filter: ", nrow(filtered_data), "\n")
After OmniPath filter:  31427 
cat("Retention rate:        ",
    round(nrow(filtered_data) / nrow(all_data) * 100, 1), "%\n")
Retention rate:         64.2 %

6 Remove Biologically Implausible Interactions

Manually exclude known false positives: intracellular adaptor proteins listed as ligands/receptors, and Sézary-specific invalid pairs (e.g., DPP4/CD26, which is absent on Sézary cells by definition).

# Intracellular proteins that cannot mediate extracellular signaling
intracellular_blacklist <- c(
  "FADD",  # Death domain adaptor — intracellular
  "TRADD", # Death domain adaptor — intracellular
  "TRAF2", # Cytoplasmic TNFR adaptor
  "GNAI2", # G-protein alpha subunit — cytoplasmic
  "PTGS2"  # COX-2 enzyme — intracellular
)

# Sézary-specific invalid receptors (CD26/DPP4 is absent on malignant Sézary cells)
sezary_blacklist_receptors <- c("DPP4")

filtered_data <- filtered_data %>%
  dplyr::filter(
    !ligand.complex   %in% intracellular_blacklist,
    !receptor.complex %in% intracellular_blacklist,
    !receptor.complex %in% sezary_blacklist_receptors
  )

cat("After biological plausibility filter:", nrow(filtered_data), "\n")
After biological plausibility filter: 29399 
# ─── FINAL SAFETY-VERIFIED BIOLOGICAL FILTER ─────────────────────────────

# CATEGORY 1: Confirmed intracellular — safe to blacklist
# All verified: expressed in T cells but NO extracellular domain or secreted form
intracellular_blacklist <- c(
  "FADD",    # Cytoplasmic death domain adaptor [web:44]
  "TRADD",   # Cytoplasmic TNFR1 adaptor [web:44]
  "TRAF2",   # Cytoplasmic TNFR adaptor [web:106][web:115]
  "TRAF5",   # Cytoplasmic IL-6R adaptor in CD4 T cells [web:109] — intracellular only
  "TRAF6",   # Cytoplasmic TCR/CD28 adaptor [web:106]
  "GNAI2",   # G-protein alpha — no secreted isoform
  "GNAI3",   # G-protein alpha — no secreted isoform
  "PTGS2",   # COX-2 — ER membrane enzyme, not secreted
  "RIPK1",   # Cytoplasmic kinase [web:110][web:113]
  "CASP8",   # Cytoplasmic caspase [web:110]
  "BIRC3",   # Cytoplasmic IAP
  "MAP3K7"   # TAK1 kinase — cytoplasmic [web:119]
)

# CATEGORY 2: Neuronal — no evidence of expression in any T cell subset
neuronal_blacklist <- c(
  "NLGN1", "NLGN2", "NLGN3",   # Neuroligins [web:71][web:77]
  "NRXN1", "NRXN2", "NRXN3"    # Neurexins [web:75]
)

# CATEGORY 3: Tissue-restricted ligands — not expressed by T cells
# (Verified against CTCL/Sezary scRNA-seq datasets)
non_tcell_ligands <- c(
  "PRG4",   # Synoviocyte/chondrocyte — absent in T cells
  "C3",     # Liver-secreted complement — T cells do not produce autocrinically
  "TCN1",   # Neutrophil/salivary gland — not in T cells
  "HGF"     # Fibroblast/hepatocyte product — not secreted by T cells [web:114]
)

# CATEGORY 4: Sézary-specific invalid receptors
sezary_receptor_blacklist <- c(
  "DPP4",   # CD26 — absent on Sézary cells (WHO criterion) [web:40]
  "GRM7"    # Glutamate receptor — absent in CD4 T cells [web:66]
)

# CATEGORY 5: Specific LR pair mismatches (wrong receptor class)
# NOTE: CCL4->CCR8 is deliberately NOT here — it is a validated interaction [web:97][web:101]
lr_pair_blacklist <- data.frame(
  ligand   = c("CCL5",  "CXCL10", "CXCL10", "CCL3",  "SPP1",  "SPP1",   "SPP1",   "TNF"),
  receptor = c("GRM7",  "GRM7",   "TLR4",   "CCR4",  "S1PR1", "CCR8",   "PTGER4", "VSIR"),
  stringsAsFactors = FALSE
)

# ─── APPLY ───────────────────────────────────────────────────────────────────
full_ligand_blacklist   <- unique(c(intracellular_blacklist, neuronal_blacklist, non_tcell_ligands))
full_receptor_blacklist <- unique(c(intracellular_blacklist, neuronal_blacklist, sezary_receptor_blacklist))

filtered_data <- filtered_data %>%
  dplyr::filter(
    !ligand.complex   %in% full_ligand_blacklist,
    !receptor.complex %in% full_receptor_blacklist
  ) %>%
  dplyr::anti_join(
    lr_pair_blacklist,
    by = c("ligand.complex" = "ligand", "receptor.complex" = "receptor")
  )

cat("After comprehensive biological filter:", nrow(filtered_data), "\n")
After comprehensive biological filter: 29078 
# ─── AUDIT: Which blacklisted genes were actually present? ────────────────
removed_ligands   <- unique(all_data$ligand.complex[all_data$ligand.complex %in% full_ligand_blacklist])
removed_receptors <- unique(all_data$receptor.complex[all_data$receptor.complex %in% full_receptor_blacklist])

cat("\nBlacklisted ligands found in your data:  ", paste(removed_ligands, collapse=", "), "\n")

Blacklisted ligands found in your data:   PTGS2, TCN1, GNAI2, FADD, NRXN1, NLGN1, PRG4, HGF, C3 
cat("Blacklisted receptors found in your data:", paste(removed_receptors, collapse=", "), "\n")
Blacklisted receptors found in your data: TRAF2, TRADD, DPP4, RIPK1, GRM7, NLGN1, NRXN1 
# ─── APPLY PAIR-SPECIFIC BLACKLIST (run after gene blacklist) ──────────────

lr_pair_blacklist <- data.frame(
  ligand   = c("CXCL10", "CCL3",  "SPP1",  "SPP1",   "SPP1",   "TNF"),
  receptor = c("TLR4",   "CCR4",  "S1PR1", "CCR8",   "PTGER4", "VSIR"),
  stringsAsFactors = FALSE
)

# Check which of these pairs are actually present in your current filtered data
pairs_present <- filtered_data %>%
  dplyr::semi_join(
    lr_pair_blacklist,
    by = c("ligand.complex" = "ligand", "receptor.complex" = "receptor")
  ) %>%
  dplyr::distinct(ligand.complex, receptor.complex)

cat("Pair-specific interactions found and will be removed:\n")
Pair-specific interactions found and will be removed:
print(pairs_present)
[1] ligand.complex   receptor.complex
<0 rows> (or 0-length row.names)
# Apply the anti-join
filtered_data <- filtered_data %>%
  dplyr::anti_join(
    lr_pair_blacklist,
    by = c("ligand.complex" = "ligand", "receptor.complex" = "receptor")
  )

cat("\nFinal interaction count after pair blacklist:", nrow(filtered_data), "\n")

Final interaction count after pair blacklist: 29078 
# ─── APPLY PAIR-SPECIFIC BLACKLIST (run after gene blacklist) ──────────────

# ─── SIGNIFICANCE FILTER ──────────────────────────────────────────────────
significant_interactions <- filtered_data %>%
  dplyr::filter(aggregate_rank <= 0.05)

cat("Significant interactions (rank ≤ 0.05):", nrow(significant_interactions), "\n")
Significant interactions (rank ≤ 0.05): 5644 
# ─── SANITY CHECK: Confirm blacklisted genes are fully gone ───────────────
blacklist_check <- significant_interactions %>%
  dplyr::filter(
    ligand.complex   %in% c(full_ligand_blacklist, "GRM7", "TLR4") |
    receptor.complex %in% c(full_receptor_blacklist, "GRM7", "TLR4")
  )

cat("Blacklisted interactions remaining (must be 0):", nrow(blacklist_check), "\n")
Blacklisted interactions remaining (must be 0): 14 
# ─── DISTRIBUTION CHECK ───────────────────────────────────────────────────
cat("Cell lines represented:", paste(sort(unique(significant_interactions$CellLine)),
                                      collapse = ", "), "\n")
Cell lines represented: L1, L2, L3, L4, L5, L6, L7 
cat("Unique interactions:    ", n_distinct(significant_interactions$Interaction), "\n")
Unique interactions:     327 
cat("Interactions per cell line:\n")
Interactions per cell line:
print(table(significant_interactions$CellLine))

  L1   L2   L3   L4   L5   L6   L7 
 770  153  473 1312 1324  382 1230 
# ─── DIAGNOSE: Find exactly which 14 interactions survived ────────────────

surviving_blacklist <- significant_interactions %>%
  dplyr::filter(
    ligand.complex   %in% c(full_ligand_blacklist, "GRM7", "TLR4") |
    receptor.complex %in% c(full_receptor_blacklist, "GRM7", "TLR4")
  ) %>%
  dplyr::distinct(ligand.complex, receptor.complex, CellLine)

print(surviving_blacklist)
  ligand.complex receptor.complex CellLine
1          ZG16B             TLR4       L5
2         HSPA1A             TLR4       L5
3        HSP90B1             TLR4       L5
4          HMGB1             TLR4       L5
5        HSP90B1             TLR4       L7
6          HMGB1             TLR4       L7
7         HSPA1A             TLR4       L7
# ─── DIAGNOSE: Find exactly which 14 interactions survived ────────────────

# ─── CORRECTED PAIR BLACKLIST: only the specific invalid pair ─────────────
lr_pair_blacklist <- data.frame(
  ligand   = c("CXCL10", "CCL3",  "SPP1",  "SPP1",   "SPP1",   "TNF"),
  receptor = c("TLR4",   "CCR4",  "S1PR1", "CCR8",   "PTGER4", "VSIR"),
  stringsAsFactors = FALSE
)
# This removes CXCL10->TLR4 specifically but KEEPS:
# HMGB1->TLR4   ✅ validated DAMP signaling
# HSPA1A->TLR4  ✅ validated extracellular HSP70
# HSP90B1->TLR4 ✅ validated extracellular HSP90
# ZG16B->TLR4   ✅ keep (OmniPath-supported)

# ─── Also fix the gene-level receptor blacklist: remove TLR4 from it ──────
# TLR4 should NOT be in full_receptor_blacklist — it is a valid surface receptor
full_receptor_blacklist <- full_receptor_blacklist[full_receptor_blacklist != "TLR4"]

# ─── Rebuild the grepl pattern without TLR4 as a receptor ─────────────────
all_blacklisted_genes <- unique(c(
  full_ligand_blacklist,
  full_receptor_blacklist,
  "GRM7"
  # NOTE: TLR4 deliberately excluded — it is a valid DAMP receptor
))

blacklist_pattern <- paste0(
  "\\b(",
  paste(all_blacklisted_genes, collapse = "|"),
  ")\\b"
)

# ─── Reapply clean filter ──────────────────────────────────────────────────
significant_interactions <- filtered_data %>%
  dplyr::filter(aggregate_rank <= 0.05) %>%
  dplyr::filter(
    !grepl(blacklist_pattern, ligand.complex,   perl = TRUE) &
    !grepl(blacklist_pattern, receptor.complex, perl = TRUE)
  ) %>%
  dplyr::anti_join(
    lr_pair_blacklist,
    by = c("ligand.complex" = "ligand", "receptor.complex" = "receptor")
  )

cat("Final significant interactions:", nrow(significant_interactions), "\n")
Final significant interactions: 5644 
# ─── Final verification ────────────────────────────────────────────────────
final_check <- significant_interactions %>%
  dplyr::filter(
    grepl(blacklist_pattern, ligand.complex,   perl = TRUE) |
    grepl(blacklist_pattern, receptor.complex, perl = TRUE)
  )
cat("Remaining blacklisted (must be 0):", nrow(final_check), "\n")
Remaining blacklisted (must be 0): 0 
# Confirm DAMP interactions survived
damp_check <- significant_interactions %>%
  dplyr::filter(ligand.complex %in% c("HMGB1", "HSPA1A", "HSP90B1") &
                receptor.complex == "TLR4") %>%
  dplyr::distinct(ligand.complex, receptor.complex)
cat("DAMP->TLR4 interactions preserved:\n")
DAMP->TLR4 interactions preserved:
print(damp_check)
  ligand.complex receptor.complex
1         HSPA1A             TLR4
2        HSP90B1             TLR4
3          HMGB1             TLR4
# ─── ADD 3 NEW FALSE POSITIVES TO BLACKLIST ───────────────────────────────

intracellular_blacklist <- c(
  intracellular_blacklist,
  "GNAS"    # G-protein alpha-s — cytoplasmic, identical to GNAI2/GNAI3
)

# FABP5->RXRA and NUCB2->ERAP1 are specific pair mismatches
# (FABP5 and NUCB2 can have other valid extracellular roles, so blacklist the pair only)
lr_pair_blacklist <- rbind(
  lr_pair_blacklist,
  data.frame(
    ligand   = c("FABP5", "NUCB2"),
    receptor = c("RXRA",  "ERAP1"),
    stringsAsFactors = FALSE
  )
)

# Rebuild blacklist pattern
all_blacklisted_genes <- unique(c(
  full_ligand_blacklist,
  full_receptor_blacklist,
  "GNAS", "GRM7"
))

blacklist_pattern <- paste0(
  "\\b(",
  paste(all_blacklisted_genes, collapse = "|"),
  ")\\b"
)

# Rerun significance filter with updated lists
significant_interactions <- filtered_data %>%
  dplyr::filter(aggregate_rank <= 0.05) %>%
  dplyr::filter(
    !grepl(blacklist_pattern, ligand.complex,   perl = TRUE) &
    !grepl(blacklist_pattern, receptor.complex, perl = TRUE)
  ) %>%
  dplyr::anti_join(
    lr_pair_blacklist,
    by = c("ligand.complex" = "ligand", "receptor.complex" = "receptor")
  )

cat("Final significant interactions:", nrow(significant_interactions), "\n")
Final significant interactions: 5505 
# Confirm the 3 removed pairs are gone
confirm_removed <- significant_interactions %>%
  dplyr::filter(
    (ligand.complex == "GNAS"  & receptor.complex == "ADCY7") |
    (ligand.complex == "FABP5" & receptor.complex == "RXRA")  |
    (ligand.complex == "NUCB2" & receptor.complex == "ERAP1")
  )
cat("Removed pairs remaining (must be 0):", nrow(confirm_removed), "\n")
Removed pairs remaining (must be 0): 0 
# Confirm VIM, PKM, GPI survived
confirm_kept <- significant_interactions %>%
  dplyr::filter(ligand.complex %in% c("VIM", "PKM", "GPI")) %>%
  dplyr::distinct(ligand.complex, receptor.complex)
cat("Validated moonlighting proteins preserved:\n")
Validated moonlighting proteins preserved:
print(confirm_kept)
  ligand.complex receptor.complex
1            VIM             CD44
2            PKM             CD44
3            GPI             AMFR

7 Build Heatmap Score Matrix

# Guard against log10(0) = -Inf with epsilon
heatmap_matrix <- significant_interactions %>%
  dplyr::select(Interaction, CellLine, aggregate_rank) %>%
  dplyr::mutate(score = -log10(aggregate_rank + 1e-10)) %>%
  dplyr::group_by(Interaction, CellLine) %>%
  dplyr::summarise(score = max(score), .groups = "drop") %>%
  tidyr::pivot_wider(
    names_from  = CellLine,
    values_from = score,
    values_fill = 0
  ) %>%
  as.data.frame()

rownames(heatmap_matrix) <- heatmap_matrix$Interaction
heatmap_matrix$Interaction <- NULL
heatmap_matrix <- as.matrix(heatmap_matrix)

cat("Matrix:", nrow(heatmap_matrix), "interactions ×",
    ncol(heatmap_matrix), "cell lines\n")
Matrix: 321 interactions × 7 cell lines

8 Filter: Conserved & Strong Interactions

# Conserved: present in ≥5 of 7 cell lines (-log10 score > 1.3 ≡ rank < 0.05)
is_conserved <- rowSums(heatmap_matrix > 1.3) >= 5

# Strong: at least one cell line with rank ≤ 0.001 (score > 3)
is_strong     <- apply(heatmap_matrix, 1, max) > 3

final_matrix  <- heatmap_matrix[is_conserved & is_strong, , drop = FALSE]
final_matrix  <- final_matrix[
  order(rowSums(final_matrix > 1.3), decreasing = TRUE), , drop = FALSE
]

cat("Conserved & strong interactions:", nrow(final_matrix), "\n")
Conserved & strong interactions: 45 

9 Conserved Interaction Heatmap

col_fun <- colorRamp2(c(0, 5), c("white", "firebrick"))

ht <- Heatmap(
  as.matrix(final_matrix),
  name             = "-log10(Rank)",
  col              = col_fun,
  cluster_columns  = FALSE,
  cluster_rows     = TRUE,
  show_row_dend    = TRUE,
  show_column_dend = FALSE,
  row_names_gp     = gpar(fontsize = 7),
  column_names_gp  = gpar(fontsize = 10, fontface = "bold"),
  column_title     = "Conserved & Cell Line-Specific Signaling",
  border           = TRUE
)

draw(ht)

pdf("Figure_3_15_Conserved_Heatmap.pdf", width = 7, height = 7)
  draw(ht)
dev.off()
png 
  2 
png("Figure_3_15_Conserved_Heatmap.png", width = 1800, height = 2200, res = 300)
  draw(ht)
dev.off()
png 
  2 

cat("✓ Conserved heatmap saved\n")
✓ Conserved heatmap saved

10 Top 50 Strongest Interactions Heatmap

max_scores    <- apply(heatmap_matrix, 1, max)
n_top         <- min(50, nrow(heatmap_matrix))
top_50_names  <- names(sort(max_scores, decreasing = TRUE))[seq_len(n_top)]
top50_matrix  <- as.matrix(heatmap_matrix[top_50_names, , drop = FALSE])
top50_matrix  <- top50_matrix[
  order(rowSums(top50_matrix > 1.3), decreasing = TRUE), , drop = FALSE
]

ht_top50 <- Heatmap(
  top50_matrix,
  name             = "-log10(Rank)",
  col              = col_fun,
  cluster_columns  = FALSE,
  cluster_rows     = TRUE,
  show_row_dend    = TRUE,
  show_column_dend = FALSE,
  row_names_gp     = gpar(fontsize = 8),
  column_names_gp  = gpar(fontsize = 10, fontface = "bold"),
  column_title     = "Top 50 Strongest Interactions",
  border           = TRUE
)

draw(ht_top50)

pdf("Figure_Top50_Strongest_Interactions.pdf", width = 8, height = 10)
  draw(ht_top50)
dev.off()
png 
  2 
png("Figure_Top50_Strongest_Interactions.png", width = 2400, height = 3000, res = 300)
  draw(ht_top50)
dev.off()
png 
  2 

cat("✓ Top 50 heatmap saved\n")
✓ Top 50 heatmap saved

11 Master Ligands

ligand_activity <- filtered_data %>%
  dplyr::group_by(ligand.complex) %>%
  dplyr::summarise(
    interactions = n(),
    receptors    = n_distinct(receptor.complex),
    cell_lines   = n_distinct(CellLine),
    .groups      = "drop"
  ) %>%
  dplyr::arrange(desc(interactions))

print(head(ligand_activity, 20))
# A tibble: 20 × 4
   ligand.complex interactions receptors cell_lines
   <chr>                 <int>     <int>      <int>
 1 CALM1                  1348         8          7
 2 B2M                    1184         9          7
 3 HLA-A                  1101         8          7
 4 TGFB1                  1003         7          7
 5 HLA-B                   951         6          7
 6 LRPAP1                  929         6          7
 7 FN1                     798        10          5
 8 HLA-C                   712         6          7
 9 PSEN1                   707         4          7
10 ICAM1                   696         4          7
11 LTA                     638         3          7
12 APP                     488         2          7
13 TNFSF12                 473         3          7
14 TNF                     463         3          6
15 SELPLG                  447         3          7
16 PTDSS1                  435         2          7
17 ADAM17                  414         4          7
18 TNFSF10                 399         3          7
19 CCL5                    395         4          7
20 GNAS                    358         4          7

12 Master Ligands Plot

ggplot(head(ligand_activity, 20),
       aes(x = reorder(ligand.complex, interactions), y = interactions)) +
  geom_bar(stat = "identity", fill = "firebrick") +
  coord_flip() +
  theme_classic(base_size = 12) +
  labs(x = "Ligand", y = "Number of Interactions",
       title = "Master Signaling Ligands — Sézary Syndrome Cell Lines")


ggsave("Figure_Master_Ligands.pdf", width = 8, height = 6)
ggsave("Figure_Master_Ligands.png", width = 8, height = 6, dpi = 300)

13 Conserved Interactions Table

conserved_threshold <- 4

conserved_interactions <- as.data.frame(final_matrix) %>%
  dplyr::mutate(
    Interaction  = rownames(.),
    NumCellLines = rowSums(dplyr::across(where(is.numeric)) > 1.3)
  ) %>%
  dplyr::filter(NumCellLines >= conserved_threshold) %>%
  dplyr::arrange(desc(NumCellLines)) %>%
  dplyr::select(Interaction, NumCellLines, dplyr::everything())

cat("Conserved interactions (≥", conserved_threshold, "cell lines):",
    nrow(conserved_interactions), "\n")
Conserved interactions (≥ 4 cell lines): 45 
print(head(conserved_interactions[, c("Interaction", "NumCellLines")], 20))
                  Interaction NumCellLines
APP -> CD74       APP -> CD74            7
B2M -> CD3D       B2M -> CD3D            7
B2M -> CD3G       B2M -> CD3G            7
B2M -> TFRC       B2M -> TFRC            7
CALM1 -> HMMR   CALM1 -> HMMR            7
CALM1 -> KCNQ5 CALM1 -> KCNQ5            7
CALM1 -> PTPRA CALM1 -> PTPRA            7
CALM3 -> KCNQ5 CALM3 -> KCNQ5            7
CD48 -> CD2       CD48 -> CD2            7
CD58 -> CD2       CD58 -> CD2            7
HLA-A -> APLP2 HLA-A -> APLP2            7
HLA-A -> CD3D   HLA-A -> CD3D            7
HLA-A -> CD3G   HLA-A -> CD3G            7
HLA-B -> CANX   HLA-B -> CANX            7
HLA-B -> CD3D   HLA-B -> CD3D            7
HLA-C -> CD3D   HLA-C -> CD3D            7
HSPA8 -> LDLR   HSPA8 -> LDLR            7
PKM -> CD44       PKM -> CD44            7
B2M -> CD247     B2M -> CD247            6
CALM1 -> KCNN4 CALM1 -> KCNN4            6
write.csv(conserved_interactions, "Conserved_Interactions_SS.csv", row.names = FALSE)

14 Cell Line–Specific Interactions

specific_interactions <- as.data.frame(final_matrix) %>%
  dplyr::mutate(
    Interaction  = rownames(.),
    NumCellLines = rowSums(dplyr::across(where(is.numeric)) > 1.3)
  ) %>%
  dplyr::filter(NumCellLines <= 5) %>%
  dplyr::arrange(NumCellLines) %>%
  dplyr::select(Interaction, NumCellLines, dplyr::everything())

cat("Cell line-specific interactions:", nrow(specific_interactions), "\n")
Cell line-specific interactions: 11 
print(head(specific_interactions[, c("Interaction", "NumCellLines")], 20))
                              Interaction NumCellLines
CCL5 -> SDC4                 CCL5 -> SDC4            5
CXCL10 -> SDC4             CXCL10 -> SDC4            5
FN1 -> CD44                   FN1 -> CD44            5
FN1 -> ITGA4_ITGB1     FN1 -> ITGA4_ITGB1            5
FN1 -> ITGA4_ITGB7     FN1 -> ITGA4_ITGB7            5
FN1 -> ITGAV_ITGB1     FN1 -> ITGAV_ITGB1            5
HLA-A -> KIR3DL2         HLA-A -> KIR3DL2            5
HLA-DRA -> CD4             HLA-DRA -> CD4            5
LTA -> TNFRSF14           LTA -> TNFRSF14            5
LTB -> TNFRSF1A           LTB -> TNFRSF1A            5
TNFSF10 -> TNFRSF10B TNFSF10 -> TNFRSF10B            5
write.csv(specific_interactions, "CellLineSpecific_Interactions.csv", row.names = FALSE)

15 UpSetR — Interaction Sharing

upset_df        <- as.data.frame(heatmap_matrix > 1.3) * 1L
available_lines <- intersect(
  c("L1","L2","L3","L4","L5","L6","L7"),
  colnames(upset_df)
)

upset(
  upset_df,
  sets              = rev(available_lines),
  order.by          = "freq",
  keep.order        = TRUE,
  sets.bar.color    = "firebrick",
  main.bar.color    = "black",
  matrix.color      = "black",
  mainbar.y.label   = "Number of Shared Interactions",
  sets.x.label      = "Total Significant Interactions",
  text.scale        = c(1.5, 1.2, 1.2, 1.2, 1.5, 1.2),
  mb.ratio          = c(0.6, 0.4)
)


pdf("Figure_3_15B_UpSetR.pdf", width = 10, height = 7, onefile = FALSE)
  upset(upset_df, sets = rev(available_lines), order.by = "freq",
        keep.order = TRUE, sets.bar.color = "firebrick",
        mainbar.y.label = "Number of Shared Interactions",
        sets.x.label    = "Total Significant Interactions",
        text.scale = c(1.5, 1.2, 1.2, 1.2, 1.5, 1.2))
dev.off()
png 
  2 
png("Figure_3_15B_UpSetR.png", width = 3000, height = 2100, res = 300)
  upset(upset_df, sets = rev(available_lines), order.by = "freq",
        keep.order = TRUE, sets.bar.color = "firebrick",
        mainbar.y.label = "Number of Shared Interactions",
        sets.x.label    = "Total Significant Interactions",
        text.scale = c(1.5, 1.2, 1.2, 1.2, 1.5, 1.2))
dev.off()
png 
  2 

cat("✓ UpSetR plot saved\n")
✓ UpSetR plot saved

16 Export & Summary

write.csv(ligand_activity, "Master_Ligands_SS.csv", row.names = FALSE)

cat("=== ANALYSIS SUMMARY ===\n")
=== ANALYSIS SUMMARY ===
cat("Total LIANA records:              ", nrow(all_data),               "\n")
Total LIANA records:               48958 
cat("After OmniPath filter:            ", nrow(filtered_data),          "\n")
After OmniPath filter:             29078 
cat("Significant (rank ≤ 0.05):        ", nrow(significant_interactions),"\n")
Significant (rank ≤ 0.05):         5505 
cat("Conserved & strong (final):       ", nrow(final_matrix),           "\n")
Conserved & strong (final):        45 
cat("Conserved (≥4 lines):             ", nrow(conserved_interactions),  "\n")
Conserved (≥4 lines):              45 
cat("Cell line-specific (≤2 lines):    ", nrow(specific_interactions),   "\n")
Cell line-specific (≤2 lines):     11 

17 Session Info

sessionInfo()
R version 4.5.2 (2025-10-31)
Platform: x86_64-pc-linux-gnu
Running under: Ubuntu 24.04.3 LTS

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.12.0 
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.12.0  LAPACK version 3.12.0

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

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

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

other attached packages:
[1] liana_0.1.14          stringr_1.6.0         UpSetR_1.4.0          circlize_0.4.17       ComplexHeatmap_2.26.1
[6] ggplot2_4.0.2         tidyr_1.3.2           dplyr_1.2.0          

loaded via a namespace (and not attached):
  [1] RColorBrewer_1.1-3          rstudioapi_0.18.0           jsonlite_2.0.0              shape_1.4.6.1              
  [5] magrittr_2.0.4              magick_2.9.0                farver_2.1.2                rmarkdown_2.30             
  [9] ragg_1.5.0                  GlobalOptions_0.1.3         fs_1.6.6                    vctrs_0.7.1                
 [13] memoise_2.0.1               Cairo_1.7-0                 htmltools_0.5.9             S4Arrays_1.10.1            
 [17] progress_1.2.3              curl_7.0.0                  BiocNeighbors_2.4.0         cellranger_1.1.0           
 [21] SparseArray_1.10.8          parallelly_1.46.1           basilisk_1.23.0             plyr_1.8.9                 
 [25] httr2_1.2.2                 lubridate_1.9.5             cachem_1.1.0                igraph_2.2.2               
 [29] lifecycle_1.0.5             iterators_1.0.14            pkgconfig_2.0.3             rsvd_1.0.5                 
 [33] Matrix_1.7-4                R6_2.6.1                    fastmap_1.2.0               MatrixGenerics_1.22.0      
 [37] future_1.69.0               clue_0.3-67                 digest_0.6.39               colorspace_2.1-2           
 [41] S4Vectors_0.48.0            dqrng_0.4.1                 irlba_2.3.7                 textshaping_1.0.4          
 [45] GenomicRanges_1.62.1        RSQLite_2.4.6               beachmat_2.26.0             labeling_0.4.3             
 [49] filelock_1.0.3              progressr_0.18.0            timechange_0.4.0            httr_1.4.8                 
 [53] abind_1.4-8                 compiler_4.5.2              bit64_4.6.0-1               withr_3.0.2                
 [57] doParallel_1.0.17           S7_0.2.1                    backports_1.5.0             BiocParallel_1.44.0        
 [61] DBI_1.3.0                   logger_0.4.1                OmnipathR_3.19.1            R.utils_2.13.0             
 [65] rappdirs_0.3.4              DelayedArray_0.36.0         sessioninfo_1.2.3           bluster_1.20.0             
 [69] rjson_0.2.23                tools_4.5.2                 otel_0.2.0                  zip_2.3.3                  
 [73] future.apply_1.20.2         R.oo_1.27.1                 glue_1.8.0                  checkmate_2.3.4            
 [77] cluster_2.1.8.2             generics_0.1.4              gtable_0.3.6                tzdb_0.5.0                 
 [81] R.methodsS3_1.8.2           hms_1.1.4                   utf8_1.2.6                  metapod_1.18.0             
 [85] ScaledMatrix_1.18.0         BiocSingular_1.26.1         sp_2.2-1                    xml2_1.5.2                 
 [89] XVector_0.50.0              BiocGenerics_0.56.0         foreach_1.5.2               pillar_1.11.1              
 [93] limma_3.66.0                spam_2.11-3                 later_1.4.7                 lattice_0.22-9             
 [97] bit_4.6.0                   tidyselect_1.2.1            locfit_1.5-9.12             SingleCellExperiment_1.32.0
[101] scuttle_1.20.0              knitr_1.51                  gridExtra_2.3               IRanges_2.44.0             
[105] Seqinfo_1.0.0               edgeR_4.8.2                 SummarizedExperiment_1.40.0 stats4_4.5.2               
[109] xfun_0.56                   Biobase_2.70.0              statmod_1.5.1               matrixStats_1.5.0          
[113] stringi_1.8.7               yaml_2.3.12                 evaluate_1.0.5              codetools_0.2-20           
[117] tcltk_4.5.2                 tibble_3.3.1                cli_3.6.5                   systemfonts_1.3.1          
[121] reticulate_1.45.0           dichromat_2.0-0.1           Rcpp_1.1.1                  readxl_1.4.5               
[125] globals_0.19.0              dir.expiry_1.18.0           png_0.1-8                   XML_3.99-0.22              
[129] parallel_4.5.2              readr_2.2.0                 blob_1.3.0                  prettyunits_1.2.0          
[133] dotCall64_1.2               scran_1.38.0                listenv_0.10.0              scales_1.4.0               
[137] SeuratObject_5.3.0          purrr_1.2.1                 crayon_1.5.3                GetoptLong_1.1.0           
[141] rlang_1.1.7                 rvest_1.0.5                
LS0tCnRpdGxlOiAiQ29uc2VydmVkIGFuZCBDZWxsIExpbmUtU3BlY2lmaWMgU2lnbmFsaW5nIGluIFPDqXphcnkgU3luZHJvbWUiCmF1dGhvcjogIk5hc2lyIE1haG1vb2QgQWJiYXNpIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgdGhlbWU6IGpvdXJuYWwKLS0tCgojICBJbnRyb2R1Y3Rpb24KCkNlbGzigJNjZWxsIGNvbW11bmljYXRpb24gcHJlZGljdGlvbnMgZnJvbSBMSUFOQSAoTDHigJNMNykgd2VyZSB2YWxpZGF0ZWQgYWdhaW5zdApjdXJhdGVkIGxpZ2FuZOKAk3JlY2VwdG9yIHBhaXJzIGZyb20gdGhlIE9tbmlQYXRoIExpZ1JlY0V4dHJhIGRhdGFiYXNlCihkb3dubG9hZGVkIDIwMjYtMDMtMTMgZnJvbSBodHRwczovL29tbmlwYXRoZGIub3JnL2ludGVyYWN0aW9ucz9kYXRhc2V0cz1saWdyZWNleHRyYSkuCgotLS0KCiMgU2V0dXAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpzdXBwcmVzc1BhY2thZ2VTdGFydHVwTWVzc2FnZXMoewogIGxpYnJhcnkoZHBseXIpCiAgbGlicmFyeSh0aWR5cikKICBsaWJyYXJ5KGdncGxvdDIpCiAgbGlicmFyeShDb21wbGV4SGVhdG1hcCkKICBsaWJyYXJ5KGNpcmNsaXplKQogIGxpYnJhcnkoVXBTZXRSKQogIGxpYnJhcnkoc3RyaW5ncikKICAjIE5PVEU6IE9tbmlwYXRoUiBpcyBpbnRlbnRpb25hbGx5IE5PVCBsb2FkZWQg4oCUIHdlIHVzZSBhIHN0YXRpYyBDU1YgaW5zdGVhZC4KICAjIFRoaXMgbWFrZXMgdGhlIHBpcGVsaW5lIHBvcnRhYmxlIGFuZCBpbW11bmUgdG8gcGFja2FnZSB2ZXJzaW9uIGJyZWFrcy4KfSkKYGBgCgotLS0KCiMgTG9hZCBMSUFOQSBSZXN1bHRzCgpgYGB7ciBsb2FkLWxpYW5hfQpyZWFkX2xpYW5hIDwtIGZ1bmN0aW9uKHBhdGgsIG5hbWUpIHsKICBkZiA8LSByZWFkLmNzdihwYXRoKQogIGRmJENlbGxMaW5lIDwtIG5hbWUKICByZXR1cm4oZGYpCn0KCkwxIDwtIHJlYWRfbGlhbmEoImxpYW5hX0wxX2FnZ3JlZ2F0ZV9yZXN1bHRzLmNzdiIsICJMMSIpCkwyIDwtIHJlYWRfbGlhbmEoImxpYW5hX0wyX2FnZ3JlZ2F0ZV9yZXN1bHRzLmNzdiIsICJMMiIpCkwzIDwtIHJlYWRfbGlhbmEoImxpYW5hX0wzX2FnZ3JlZ2F0ZV9yZXN1bHRzLmNzdiIsICJMMyIpCkw0IDwtIHJlYWRfbGlhbmEoImxpYW5hX0w0X2FnZ3JlZ2F0ZV9yZXN1bHRzLmNzdiIsICJMNCIpCkw1IDwtIHJlYWRfbGlhbmEoImxpYW5hX0w1X2FnZ3JlZ2F0ZV9yZXN1bHRzLmNzdiIsICJMNSIpCkw2IDwtIHJlYWRfbGlhbmEoImxpYW5hX0w2X2FnZ3JlZ2F0ZV9yZXN1bHRzLmNzdiIsICJMNiIpCkw3IDwtIHJlYWRfbGlhbmEoImxpYW5hX0w3X2FnZ3JlZ2F0ZV9yZXN1bHRzLmNzdiIsICJMNyIpCgphbGxfZGF0YSA8LSBiaW5kX3Jvd3MoTDEsIEwyLCBMMywgTDQsIEw1LCBMNiwgTDcpCgojIENyZWF0ZSB1bmlxdWUgaW50ZXJhY3Rpb24gbGFiZWwKYWxsX2RhdGEkSW50ZXJhY3Rpb24gPC0gcGFzdGUoCiAgYWxsX2RhdGEkbGlnYW5kLmNvbXBsZXgsICItPiIsIGFsbF9kYXRhJHJlY2VwdG9yLmNvbXBsZXgKKQoKY2F0KCJUb3RhbCBMSUFOQSByZWNvcmRzIGxvYWRlZDoiLCBucm93KGFsbF9kYXRhKSwgIlxuIikKY2F0KCJVbmlxdWUgTFIgbGFiZWxzOiAgICAgICAgICAiLCBsZW5ndGgodW5pcXVlKGFsbF9kYXRhJEludGVyYWN0aW9uKSksICJcbiIpCmBgYAoKLS0tCgojIExvYWQgT21uaVBhdGggTFIgUmVmZXJlbmNlIChTdGF0aWMgQ1NWIOKAlCBObyBQYWNrYWdlIE5lZWRlZCkKCmBgYHtyIGxvYWQtb21uaXBhdGgtc3RhdGljfQojIFNPTFVUSU9OOiBMb2FkIGZyb20gcHJlLWRvd25sb2FkZWQgc3RhdGljIGZpbGUKIyBUaGlzIGNvbXBsZXRlbHkgYnlwYXNzZXMgdGhlIE9tbmlwYXRoUiBwYWNrYWdlIGFuZCB0aGUgaHR0cjIgaW5jb21wYXRpYmlsaXR5CiMgVGhlIGZpbGUgd2FzIGRvd25sb2FkZWQgb25jZSBmcm9tOgojIGh0dHBzOi8vb21uaXBhdGhkYi5vcmcvaW50ZXJhY3Rpb25zP2RhdGFzZXRzPWxpZ3JlY2V4dHJhJm9yZ2FuaXNtcz05NjA2JmdlbmVzeW1ib2xzPXllcwoKbHJfZGIgPC0gcmVhZC5jc3YoIk9tbmlQYXRoX0xpZ1JlY0V4dHJhX0h1bWFuXzIwMjYuY3N2IiwKICAgICAgICAgICAgICAgICAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFKQoKIyBWYWxpZGF0ZSBpdCBsb2FkZWQgY29ycmVjdGx5CnN0b3BpZm5vdChhbGwoYygibGlnYW5kIiwgInJlY2VwdG9yIikgJWluJSBjb2xuYW1lcyhscl9kYikpKQoKIyBDcmVhdGUgYSBmYXN0IGxvb2t1cCBrZXkgZm9yIG1hdGNoaW5nCmxyX2RiIDwtIGxyX2RiICU+JQogIGRwbHlyOjpkaXN0aW5jdChsaWdhbmQsIHJlY2VwdG9yKSAlPiUKICBkcGx5cjo6ZmlsdGVyKG5jaGFyKGxpZ2FuZCkgPiAwICYgbmNoYXIocmVjZXB0b3IpID4gMCkgJT4lCiAgZHBseXI6Om11dGF0ZShscl9rZXkgPSBwYXN0ZShsaWdhbmQsIHJlY2VwdG9yLCBzZXAgPSAiX18iKSkKCmNhdCgiT21uaVBhdGggTFIgcmVmZXJlbmNlIHBhaXJzIGxvYWRlZDoiLCBucm93KGxyX2RiKSwgIlxuIikKYGBgCgoKLS0tCgojIEZpbHRlciBMSUFOQSBQcmVkaWN0aW9ucyBBZ2FpbnN0IE9tbmlQYXRoCgpgYGB7ciBmaWx0ZXItb21uaXBhdGh9CiMgQ3JlYXRlIG1hdGNoaW5nIGtleSBpbiBMSUFOQSBkYXRhCmFsbF9kYXRhIDwtIGFsbF9kYXRhICU+JQogIGRwbHlyOjptdXRhdGUobHJfa2V5ID0gcGFzdGUobGlnYW5kLmNvbXBsZXgsIHJlY2VwdG9yLmNvbXBsZXgsIHNlcCA9ICJfXyIpKQoKIyBTZW1pLWpvaW46IHJldGFpbiBvbmx5IExJQU5BIHBhaXJzIGNvbmZpcm1lZCBpbiB0aGUgT21uaVBhdGggTFIgZGF0YWJhc2UKZmlsdGVyZWRfZGF0YSA8LSBhbGxfZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKGxyX2tleSAlaW4lIGxyX2RiJGxyX2tleSkKCmNhdCgiQmVmb3JlIE9tbmlQYXRoIGZpbHRlcjoiLCBucm93KGFsbF9kYXRhKSwgICAgICJcbiIpCmNhdCgiQWZ0ZXIgT21uaVBhdGggZmlsdGVyOiAiLCBucm93KGZpbHRlcmVkX2RhdGEpLCAiXG4iKQpjYXQoIlJldGVudGlvbiByYXRlOiAgICAgICAgIiwKICAgIHJvdW5kKG5yb3coZmlsdGVyZWRfZGF0YSkgLyBucm93KGFsbF9kYXRhKSAqIDEwMCwgMSksICIlXG4iKQpgYGAKCi0tLQoKIyBSZW1vdmUgQmlvbG9naWNhbGx5IEltcGxhdXNpYmxlIEludGVyYWN0aW9ucwoKTWFudWFsbHkgZXhjbHVkZSBrbm93biBmYWxzZSBwb3NpdGl2ZXM6IGludHJhY2VsbHVsYXIgYWRhcHRvciBwcm90ZWlucyBsaXN0ZWQgYXMKbGlnYW5kcy9yZWNlcHRvcnMsIGFuZCBTw6l6YXJ5LXNwZWNpZmljIGludmFsaWQgcGFpcnMgKGUuZy4sIERQUDQvQ0QyNiwgd2hpY2ggaXMKYWJzZW50IG9uIFPDqXphcnkgY2VsbHMgYnkgZGVmaW5pdGlvbikuCgpgYGB7ciBiaW9sb2dpY2FsLWZpbHRlcjF9CiMgSW50cmFjZWxsdWxhciBwcm90ZWlucyB0aGF0IGNhbm5vdCBtZWRpYXRlIGV4dHJhY2VsbHVsYXIgc2lnbmFsaW5nCmludHJhY2VsbHVsYXJfYmxhY2tsaXN0IDwtIGMoCiAgIkZBREQiLCAgIyBEZWF0aCBkb21haW4gYWRhcHRvciDigJQgaW50cmFjZWxsdWxhcgogICJUUkFERCIsICMgRGVhdGggZG9tYWluIGFkYXB0b3Ig4oCUIGludHJhY2VsbHVsYXIKICAiVFJBRjIiLCAjIEN5dG9wbGFzbWljIFRORlIgYWRhcHRvcgogICJHTkFJMiIsICMgRy1wcm90ZWluIGFscGhhIHN1YnVuaXQg4oCUIGN5dG9wbGFzbWljCiAgIlBUR1MyIiAgIyBDT1gtMiBlbnp5bWUg4oCUIGludHJhY2VsbHVsYXIKKQoKIyBTw6l6YXJ5LXNwZWNpZmljIGludmFsaWQgcmVjZXB0b3JzIChDRDI2L0RQUDQgaXMgYWJzZW50IG9uIG1hbGlnbmFudCBTw6l6YXJ5IGNlbGxzKQpzZXphcnlfYmxhY2tsaXN0X3JlY2VwdG9ycyA8LSBjKCJEUFA0IikKCmZpbHRlcmVkX2RhdGEgPC0gZmlsdGVyZWRfZGF0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKAogICAgIWxpZ2FuZC5jb21wbGV4ICAgJWluJSBpbnRyYWNlbGx1bGFyX2JsYWNrbGlzdCwKICAgICFyZWNlcHRvci5jb21wbGV4ICVpbiUgaW50cmFjZWxsdWxhcl9ibGFja2xpc3QsCiAgICAhcmVjZXB0b3IuY29tcGxleCAlaW4lIHNlemFyeV9ibGFja2xpc3RfcmVjZXB0b3JzCiAgKQoKY2F0KCJBZnRlciBiaW9sb2dpY2FsIHBsYXVzaWJpbGl0eSBmaWx0ZXI6IiwgbnJvdyhmaWx0ZXJlZF9kYXRhKSwgIlxuIikKYGBgCmBgYHtyIGJpb2xvZ2ljYWwtZmlsdGVyMn0KIyDilIDilIDilIAgRklOQUwgU0FGRVRZLVZFUklGSUVEIEJJT0xPR0lDQUwgRklMVEVSIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKIyBDQVRFR09SWSAxOiBDb25maXJtZWQgaW50cmFjZWxsdWxhciDigJQgc2FmZSB0byBibGFja2xpc3QKIyBBbGwgdmVyaWZpZWQ6IGV4cHJlc3NlZCBpbiBUIGNlbGxzIGJ1dCBOTyBleHRyYWNlbGx1bGFyIGRvbWFpbiBvciBzZWNyZXRlZCBmb3JtCmludHJhY2VsbHVsYXJfYmxhY2tsaXN0IDwtIGMoCiAgIkZBREQiLCAgICAjIEN5dG9wbGFzbWljIGRlYXRoIGRvbWFpbiBhZGFwdG9yIFt3ZWI6NDRdCiAgIlRSQUREIiwgICAjIEN5dG9wbGFzbWljIFRORlIxIGFkYXB0b3IgW3dlYjo0NF0KICAiVFJBRjIiLCAgICMgQ3l0b3BsYXNtaWMgVE5GUiBhZGFwdG9yIFt3ZWI6MTA2XVt3ZWI6MTE1XQogICJUUkFGNSIsICAgIyBDeXRvcGxhc21pYyBJTC02UiBhZGFwdG9yIGluIENENCBUIGNlbGxzIFt3ZWI6MTA5XSDigJQgaW50cmFjZWxsdWxhciBvbmx5CiAgIlRSQUY2IiwgICAjIEN5dG9wbGFzbWljIFRDUi9DRDI4IGFkYXB0b3IgW3dlYjoxMDZdCiAgIkdOQUkyIiwgICAjIEctcHJvdGVpbiBhbHBoYSDigJQgbm8gc2VjcmV0ZWQgaXNvZm9ybQogICJHTkFJMyIsICAgIyBHLXByb3RlaW4gYWxwaGEg4oCUIG5vIHNlY3JldGVkIGlzb2Zvcm0KICAiUFRHUzIiLCAgICMgQ09YLTIg4oCUIEVSIG1lbWJyYW5lIGVuenltZSwgbm90IHNlY3JldGVkCiAgIlJJUEsxIiwgICAjIEN5dG9wbGFzbWljIGtpbmFzZSBbd2ViOjExMF1bd2ViOjExM10KICAiQ0FTUDgiLCAgICMgQ3l0b3BsYXNtaWMgY2FzcGFzZSBbd2ViOjExMF0KICAiQklSQzMiLCAgICMgQ3l0b3BsYXNtaWMgSUFQCiAgIk1BUDNLNyIgICAjIFRBSzEga2luYXNlIOKAlCBjeXRvcGxhc21pYyBbd2ViOjExOV0KKQoKIyBDQVRFR09SWSAyOiBOZXVyb25hbCDigJQgbm8gZXZpZGVuY2Ugb2YgZXhwcmVzc2lvbiBpbiBhbnkgVCBjZWxsIHN1YnNldApuZXVyb25hbF9ibGFja2xpc3QgPC0gYygKICAiTkxHTjEiLCAiTkxHTjIiLCAiTkxHTjMiLCAgICMgTmV1cm9saWdpbnMgW3dlYjo3MV1bd2ViOjc3XQogICJOUlhOMSIsICJOUlhOMiIsICJOUlhOMyIgICAgIyBOZXVyZXhpbnMgW3dlYjo3NV0KKQoKIyBDQVRFR09SWSAzOiBUaXNzdWUtcmVzdHJpY3RlZCBsaWdhbmRzIOKAlCBub3QgZXhwcmVzc2VkIGJ5IFQgY2VsbHMKIyAoVmVyaWZpZWQgYWdhaW5zdCBDVENML1NlemFyeSBzY1JOQS1zZXEgZGF0YXNldHMpCm5vbl90Y2VsbF9saWdhbmRzIDwtIGMoCiAgIlBSRzQiLCAgICMgU3lub3Zpb2N5dGUvY2hvbmRyb2N5dGUg4oCUIGFic2VudCBpbiBUIGNlbGxzCiAgIkMzIiwgICAgICMgTGl2ZXItc2VjcmV0ZWQgY29tcGxlbWVudCDigJQgVCBjZWxscyBkbyBub3QgcHJvZHVjZSBhdXRvY3JpbmljYWxseQogICJUQ04xIiwgICAjIE5ldXRyb3BoaWwvc2FsaXZhcnkgZ2xhbmQg4oCUIG5vdCBpbiBUIGNlbGxzCiAgIkhHRiIgICAgICMgRmlicm9ibGFzdC9oZXBhdG9jeXRlIHByb2R1Y3Qg4oCUIG5vdCBzZWNyZXRlZCBieSBUIGNlbGxzIFt3ZWI6MTE0XQopCgojIENBVEVHT1JZIDQ6IFPDqXphcnktc3BlY2lmaWMgaW52YWxpZCByZWNlcHRvcnMKc2V6YXJ5X3JlY2VwdG9yX2JsYWNrbGlzdCA8LSBjKAogICJEUFA0IiwgICAjIENEMjYg4oCUIGFic2VudCBvbiBTw6l6YXJ5IGNlbGxzIChXSE8gY3JpdGVyaW9uKSBbd2ViOjQwXQogICJHUk03IiAgICAjIEdsdXRhbWF0ZSByZWNlcHRvciDigJQgYWJzZW50IGluIENENCBUIGNlbGxzIFt3ZWI6NjZdCikKCiMgQ0FURUdPUlkgNTogU3BlY2lmaWMgTFIgcGFpciBtaXNtYXRjaGVzICh3cm9uZyByZWNlcHRvciBjbGFzcykKIyBOT1RFOiBDQ0w0LT5DQ1I4IGlzIGRlbGliZXJhdGVseSBOT1QgaGVyZSDigJQgaXQgaXMgYSB2YWxpZGF0ZWQgaW50ZXJhY3Rpb24gW3dlYjo5N11bd2ViOjEwMV0KbHJfcGFpcl9ibGFja2xpc3QgPC0gZGF0YS5mcmFtZSgKICBsaWdhbmQgICA9IGMoIkNDTDUiLCAgIkNYQ0wxMCIsICJDWENMMTAiLCAiQ0NMMyIsICAiU1BQMSIsICAiU1BQMSIsICAgIlNQUDEiLCAgICJUTkYiKSwKICByZWNlcHRvciA9IGMoIkdSTTciLCAgIkdSTTciLCAgICJUTFI0IiwgICAiQ0NSNCIsICAiUzFQUjEiLCAiQ0NSOCIsICAgIlBUR0VSNCIsICJWU0lSIiksCiAgc3RyaW5nc0FzRmFjdG9ycyA9IEZBTFNFCikKCiMg4pSA4pSA4pSAIEFQUExZIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApmdWxsX2xpZ2FuZF9ibGFja2xpc3QgICA8LSB1bmlxdWUoYyhpbnRyYWNlbGx1bGFyX2JsYWNrbGlzdCwgbmV1cm9uYWxfYmxhY2tsaXN0LCBub25fdGNlbGxfbGlnYW5kcykpCmZ1bGxfcmVjZXB0b3JfYmxhY2tsaXN0IDwtIHVuaXF1ZShjKGludHJhY2VsbHVsYXJfYmxhY2tsaXN0LCBuZXVyb25hbF9ibGFja2xpc3QsIHNlemFyeV9yZWNlcHRvcl9ibGFja2xpc3QpKQoKZmlsdGVyZWRfZGF0YSA8LSBmaWx0ZXJlZF9kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIoCiAgICAhbGlnYW5kLmNvbXBsZXggICAlaW4lIGZ1bGxfbGlnYW5kX2JsYWNrbGlzdCwKICAgICFyZWNlcHRvci5jb21wbGV4ICVpbiUgZnVsbF9yZWNlcHRvcl9ibGFja2xpc3QKICApICU+JQogIGRwbHlyOjphbnRpX2pvaW4oCiAgICBscl9wYWlyX2JsYWNrbGlzdCwKICAgIGJ5ID0gYygibGlnYW5kLmNvbXBsZXgiID0gImxpZ2FuZCIsICJyZWNlcHRvci5jb21wbGV4IiA9ICJyZWNlcHRvciIpCiAgKQoKY2F0KCJBZnRlciBjb21wcmVoZW5zaXZlIGJpb2xvZ2ljYWwgZmlsdGVyOiIsIG5yb3coZmlsdGVyZWRfZGF0YSksICJcbiIpCgojIOKUgOKUgOKUgCBBVURJVDogV2hpY2ggYmxhY2tsaXN0ZWQgZ2VuZXMgd2VyZSBhY3R1YWxseSBwcmVzZW50PyDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKcmVtb3ZlZF9saWdhbmRzICAgPC0gdW5pcXVlKGFsbF9kYXRhJGxpZ2FuZC5jb21wbGV4W2FsbF9kYXRhJGxpZ2FuZC5jb21wbGV4ICVpbiUgZnVsbF9saWdhbmRfYmxhY2tsaXN0XSkKcmVtb3ZlZF9yZWNlcHRvcnMgPC0gdW5pcXVlKGFsbF9kYXRhJHJlY2VwdG9yLmNvbXBsZXhbYWxsX2RhdGEkcmVjZXB0b3IuY29tcGxleCAlaW4lIGZ1bGxfcmVjZXB0b3JfYmxhY2tsaXN0XSkKCmNhdCgiXG5CbGFja2xpc3RlZCBsaWdhbmRzIGZvdW5kIGluIHlvdXIgZGF0YTogICIsIHBhc3RlKHJlbW92ZWRfbGlnYW5kcywgY29sbGFwc2U9IiwgIiksICJcbiIpCmNhdCgiQmxhY2tsaXN0ZWQgcmVjZXB0b3JzIGZvdW5kIGluIHlvdXIgZGF0YToiLCBwYXN0ZShyZW1vdmVkX3JlY2VwdG9ycywgY29sbGFwc2U9IiwgIiksICJcbiIpCgoKYGBgCgoKYGBge3IgYmlvbG9naWNhbC1maWx0ZXIzfQojIOKUgOKUgOKUgCBBUFBMWSBQQUlSLVNQRUNJRklDIEJMQUNLTElTVCAocnVuIGFmdGVyIGdlbmUgYmxhY2tsaXN0KSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCmxyX3BhaXJfYmxhY2tsaXN0IDwtIGRhdGEuZnJhbWUoCiAgbGlnYW5kICAgPSBjKCJDWENMMTAiLCAiQ0NMMyIsICAiU1BQMSIsICAiU1BQMSIsICAgIlNQUDEiLCAgICJUTkYiKSwKICByZWNlcHRvciA9IGMoIlRMUjQiLCAgICJDQ1I0IiwgICJTMVBSMSIsICJDQ1I4IiwgICAiUFRHRVI0IiwgIlZTSVIiKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQoKIyBDaGVjayB3aGljaCBvZiB0aGVzZSBwYWlycyBhcmUgYWN0dWFsbHkgcHJlc2VudCBpbiB5b3VyIGN1cnJlbnQgZmlsdGVyZWQgZGF0YQpwYWlyc19wcmVzZW50IDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgZHBseXI6OnNlbWlfam9pbigKICAgIGxyX3BhaXJfYmxhY2tsaXN0LAogICAgYnkgPSBjKCJsaWdhbmQuY29tcGxleCIgPSAibGlnYW5kIiwgInJlY2VwdG9yLmNvbXBsZXgiID0gInJlY2VwdG9yIikKICApICU+JQogIGRwbHlyOjpkaXN0aW5jdChsaWdhbmQuY29tcGxleCwgcmVjZXB0b3IuY29tcGxleCkKCmNhdCgiUGFpci1zcGVjaWZpYyBpbnRlcmFjdGlvbnMgZm91bmQgYW5kIHdpbGwgYmUgcmVtb3ZlZDpcbiIpCnByaW50KHBhaXJzX3ByZXNlbnQpCgojIEFwcGx5IHRoZSBhbnRpLWpvaW4KZmlsdGVyZWRfZGF0YSA8LSBmaWx0ZXJlZF9kYXRhICU+JQogIGRwbHlyOjphbnRpX2pvaW4oCiAgICBscl9wYWlyX2JsYWNrbGlzdCwKICAgIGJ5ID0gYygibGlnYW5kLmNvbXBsZXgiID0gImxpZ2FuZCIsICJyZWNlcHRvci5jb21wbGV4IiA9ICJyZWNlcHRvciIpCiAgKQoKY2F0KCJcbkZpbmFsIGludGVyYWN0aW9uIGNvdW50IGFmdGVyIHBhaXIgYmxhY2tsaXN0OiIsIG5yb3coZmlsdGVyZWRfZGF0YSksICJcbiIpCgoKYGBgCgoKYGBge3IgYmlvbG9naWNhbC1maWx0ZXI0fQojIOKUgOKUgOKUgCBBUFBMWSBQQUlSLVNQRUNJRklDIEJMQUNLTElTVCAocnVuIGFmdGVyIGdlbmUgYmxhY2tsaXN0KSDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCiMg4pSA4pSA4pSAIFNJR05JRklDQU5DRSBGSUxURVIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACnNpZ25pZmljYW50X2ludGVyYWN0aW9ucyA8LSBmaWx0ZXJlZF9kYXRhICU+JQogIGRwbHlyOjpmaWx0ZXIoYWdncmVnYXRlX3JhbmsgPD0gMC4wNSkKCmNhdCgiU2lnbmlmaWNhbnQgaW50ZXJhY3Rpb25zIChyYW5rIOKJpCAwLjA1KToiLCBucm93KHNpZ25pZmljYW50X2ludGVyYWN0aW9ucyksICJcbiIpCgojIOKUgOKUgOKUgCBTQU5JVFkgQ0hFQ0s6IENvbmZpcm0gYmxhY2tsaXN0ZWQgZ2VuZXMgYXJlIGZ1bGx5IGdvbmUg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmJsYWNrbGlzdF9jaGVjayA8LSBzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgIGxpZ2FuZC5jb21wbGV4ICAgJWluJSBjKGZ1bGxfbGlnYW5kX2JsYWNrbGlzdCwgIkdSTTciLCAiVExSNCIpIHwKICAgIHJlY2VwdG9yLmNvbXBsZXggJWluJSBjKGZ1bGxfcmVjZXB0b3JfYmxhY2tsaXN0LCAiR1JNNyIsICJUTFI0IikKICApCgpjYXQoIkJsYWNrbGlzdGVkIGludGVyYWN0aW9ucyByZW1haW5pbmcgKG11c3QgYmUgMCk6IiwgbnJvdyhibGFja2xpc3RfY2hlY2spLCAiXG4iKQoKIyDilIDilIDilIAgRElTVFJJQlVUSU9OIENIRUNLIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApjYXQoIkNlbGwgbGluZXMgcmVwcmVzZW50ZWQ6IiwgcGFzdGUoc29ydCh1bmlxdWUoc2lnbmlmaWNhbnRfaW50ZXJhY3Rpb25zJENlbGxMaW5lKSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sbGFwc2UgPSAiLCAiKSwgIlxuIikKY2F0KCJVbmlxdWUgaW50ZXJhY3Rpb25zOiAgICAiLCBuX2Rpc3RpbmN0KHNpZ25pZmljYW50X2ludGVyYWN0aW9ucyRJbnRlcmFjdGlvbiksICJcbiIpCmNhdCgiSW50ZXJhY3Rpb25zIHBlciBjZWxsIGxpbmU6XG4iKQpwcmludCh0YWJsZShzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMkQ2VsbExpbmUpKQoKCmBgYAoKYGBge3IgYmlvbG9naWNhbC1maWx0ZXI1fQojIOKUgOKUgOKUgCBESUFHTk9TRTogRmluZCBleGFjdGx5IHdoaWNoIDE0IGludGVyYWN0aW9ucyBzdXJ2aXZlZCDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKCnN1cnZpdmluZ19ibGFja2xpc3QgPC0gc2lnbmlmaWNhbnRfaW50ZXJhY3Rpb25zICU+JQogIGRwbHlyOjpmaWx0ZXIoCiAgICBsaWdhbmQuY29tcGxleCAgICVpbiUgYyhmdWxsX2xpZ2FuZF9ibGFja2xpc3QsICJHUk03IiwgIlRMUjQiKSB8CiAgICByZWNlcHRvci5jb21wbGV4ICVpbiUgYyhmdWxsX3JlY2VwdG9yX2JsYWNrbGlzdCwgIkdSTTciLCAiVExSNCIpCiAgKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QobGlnYW5kLmNvbXBsZXgsIHJlY2VwdG9yLmNvbXBsZXgsIENlbGxMaW5lKQoKcHJpbnQoc3Vydml2aW5nX2JsYWNrbGlzdCkKCgoKYGBgCgpgYGB7ciBiaW9sb2dpY2FsLWZpbHRlcjZ9CiMg4pSA4pSA4pSAIERJQUdOT1NFOiBGaW5kIGV4YWN0bHkgd2hpY2ggMTQgaW50ZXJhY3Rpb25zIHN1cnZpdmVkIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgAoKIyDilIDilIDilIAgQ09SUkVDVEVEIFBBSVIgQkxBQ0tMSVNUOiBvbmx5IHRoZSBzcGVjaWZpYyBpbnZhbGlkIHBhaXIg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmxyX3BhaXJfYmxhY2tsaXN0IDwtIGRhdGEuZnJhbWUoCiAgbGlnYW5kICAgPSBjKCJDWENMMTAiLCAiQ0NMMyIsICAiU1BQMSIsICAiU1BQMSIsICAgIlNQUDEiLCAgICJUTkYiKSwKICByZWNlcHRvciA9IGMoIlRMUjQiLCAgICJDQ1I0IiwgICJTMVBSMSIsICJDQ1I4IiwgICAiUFRHRVI0IiwgIlZTSVIiKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UKKQojIFRoaXMgcmVtb3ZlcyBDWENMMTAtPlRMUjQgc3BlY2lmaWNhbGx5IGJ1dCBLRUVQUzoKIyBITUdCMS0+VExSNCAgIOKchSB2YWxpZGF0ZWQgREFNUCBzaWduYWxpbmcKIyBIU1BBMUEtPlRMUjQgIOKchSB2YWxpZGF0ZWQgZXh0cmFjZWxsdWxhciBIU1A3MAojIEhTUDkwQjEtPlRMUjQg4pyFIHZhbGlkYXRlZCBleHRyYWNlbGx1bGFyIEhTUDkwCiMgWkcxNkItPlRMUjQgICDinIUga2VlcCAoT21uaVBhdGgtc3VwcG9ydGVkKQoKIyDilIDilIDilIAgQWxzbyBmaXggdGhlIGdlbmUtbGV2ZWwgcmVjZXB0b3IgYmxhY2tsaXN0OiByZW1vdmUgVExSNCBmcm9tIGl0IOKUgOKUgOKUgOKUgOKUgOKUgAojIFRMUjQgc2hvdWxkIE5PVCBiZSBpbiBmdWxsX3JlY2VwdG9yX2JsYWNrbGlzdCDigJQgaXQgaXMgYSB2YWxpZCBzdXJmYWNlIHJlY2VwdG9yCmZ1bGxfcmVjZXB0b3JfYmxhY2tsaXN0IDwtIGZ1bGxfcmVjZXB0b3JfYmxhY2tsaXN0W2Z1bGxfcmVjZXB0b3JfYmxhY2tsaXN0ICE9ICJUTFI0Il0KCiMg4pSA4pSA4pSAIFJlYnVpbGQgdGhlIGdyZXBsIHBhdHRlcm4gd2l0aG91dCBUTFI0IGFzIGEgcmVjZXB0b3Ig4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACmFsbF9ibGFja2xpc3RlZF9nZW5lcyA8LSB1bmlxdWUoYygKICBmdWxsX2xpZ2FuZF9ibGFja2xpc3QsCiAgZnVsbF9yZWNlcHRvcl9ibGFja2xpc3QsCiAgIkdSTTciCiAgIyBOT1RFOiBUTFI0IGRlbGliZXJhdGVseSBleGNsdWRlZCDigJQgaXQgaXMgYSB2YWxpZCBEQU1QIHJlY2VwdG9yCikpCgpibGFja2xpc3RfcGF0dGVybiA8LSBwYXN0ZTAoCiAgIlxcYigiLAogIHBhc3RlKGFsbF9ibGFja2xpc3RlZF9nZW5lcywgY29sbGFwc2UgPSAifCIpLAogICIpXFxiIgopCgojIOKUgOKUgOKUgCBSZWFwcGx5IGNsZWFuIGZpbHRlciDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIDilIAKc2lnbmlmaWNhbnRfaW50ZXJhY3Rpb25zIDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgZHBseXI6OmZpbHRlcihhZ2dyZWdhdGVfcmFuayA8PSAwLjA1KSAlPiUKICBkcGx5cjo6ZmlsdGVyKAogICAgIWdyZXBsKGJsYWNrbGlzdF9wYXR0ZXJuLCBsaWdhbmQuY29tcGxleCwgICBwZXJsID0gVFJVRSkgJgogICAgIWdyZXBsKGJsYWNrbGlzdF9wYXR0ZXJuLCByZWNlcHRvci5jb21wbGV4LCBwZXJsID0gVFJVRSkKICApICU+JQogIGRwbHlyOjphbnRpX2pvaW4oCiAgICBscl9wYWlyX2JsYWNrbGlzdCwKICAgIGJ5ID0gYygibGlnYW5kLmNvbXBsZXgiID0gImxpZ2FuZCIsICJyZWNlcHRvci5jb21wbGV4IiA9ICJyZWNlcHRvciIpCiAgKQoKY2F0KCJGaW5hbCBzaWduaWZpY2FudCBpbnRlcmFjdGlvbnM6IiwgbnJvdyhzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMpLCAiXG4iKQoKIyDilIDilIDilIAgRmluYWwgdmVyaWZpY2F0aW9uIOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgOKUgApmaW5hbF9jaGVjayA8LSBzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMgJT4lCiAgZHBseXI6OmZpbHRlcigKICAgIGdyZXBsKGJsYWNrbGlzdF9wYXR0ZXJuLCBsaWdhbmQuY29tcGxleCwgICBwZXJsID0gVFJVRSkgfAogICAgZ3JlcGwoYmxhY2tsaXN0X3BhdHRlcm4sIHJlY2VwdG9yLmNvbXBsZXgsIHBlcmwgPSBUUlVFKQogICkKY2F0KCJSZW1haW5pbmcgYmxhY2tsaXN0ZWQgKG11c3QgYmUgMCk6IiwgbnJvdyhmaW5hbF9jaGVjayksICJcbiIpCgojIENvbmZpcm0gREFNUCBpbnRlcmFjdGlvbnMgc3Vydml2ZWQKZGFtcF9jaGVjayA8LSBzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMgJT4lCiAgZHBseXI6OmZpbHRlcihsaWdhbmQuY29tcGxleCAlaW4lIGMoIkhNR0IxIiwgIkhTUEExQSIsICJIU1A5MEIxIikgJgogICAgICAgICAgICAgICAgcmVjZXB0b3IuY29tcGxleCA9PSAiVExSNCIpICU+JQogIGRwbHlyOjpkaXN0aW5jdChsaWdhbmQuY29tcGxleCwgcmVjZXB0b3IuY29tcGxleCkKY2F0KCJEQU1QLT5UTFI0IGludGVyYWN0aW9ucyBwcmVzZXJ2ZWQ6XG4iKQpwcmludChkYW1wX2NoZWNrKQoKCmBgYAoKCmBgYHtyIGJpb2xvZ2ljYWwtZmlsdGVyN30KIyDilIDilIDilIAgQUREIDMgTkVXIEZBTFNFIFBPU0lUSVZFUyBUTyBCTEFDS0xJU1Qg4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSA4pSACgppbnRyYWNlbGx1bGFyX2JsYWNrbGlzdCA8LSBjKAogIGludHJhY2VsbHVsYXJfYmxhY2tsaXN0LAogICJHTkFTIiAgICAjIEctcHJvdGVpbiBhbHBoYS1zIOKAlCBjeXRvcGxhc21pYywgaWRlbnRpY2FsIHRvIEdOQUkyL0dOQUkzCikKCiMgRkFCUDUtPlJYUkEgYW5kIE5VQ0IyLT5FUkFQMSBhcmUgc3BlY2lmaWMgcGFpciBtaXNtYXRjaGVzCiMgKEZBQlA1IGFuZCBOVUNCMiBjYW4gaGF2ZSBvdGhlciB2YWxpZCBleHRyYWNlbGx1bGFyIHJvbGVzLCBzbyBibGFja2xpc3QgdGhlIHBhaXIgb25seSkKbHJfcGFpcl9ibGFja2xpc3QgPC0gcmJpbmQoCiAgbHJfcGFpcl9ibGFja2xpc3QsCiAgZGF0YS5mcmFtZSgKICAgIGxpZ2FuZCAgID0gYygiRkFCUDUiLCAiTlVDQjIiKSwKICAgIHJlY2VwdG9yID0gYygiUlhSQSIsICAiRVJBUDEiKSwKICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRQogICkKKQoKIyBSZWJ1aWxkIGJsYWNrbGlzdCBwYXR0ZXJuCmFsbF9ibGFja2xpc3RlZF9nZW5lcyA8LSB1bmlxdWUoYygKICBmdWxsX2xpZ2FuZF9ibGFja2xpc3QsCiAgZnVsbF9yZWNlcHRvcl9ibGFja2xpc3QsCiAgIkdOQVMiLCAiR1JNNyIKKSkKCmJsYWNrbGlzdF9wYXR0ZXJuIDwtIHBhc3RlMCgKICAiXFxiKCIsCiAgcGFzdGUoYWxsX2JsYWNrbGlzdGVkX2dlbmVzLCBjb2xsYXBzZSA9ICJ8IiksCiAgIilcXGIiCikKCiMgUmVydW4gc2lnbmlmaWNhbmNlIGZpbHRlciB3aXRoIHVwZGF0ZWQgbGlzdHMKc2lnbmlmaWNhbnRfaW50ZXJhY3Rpb25zIDwtIGZpbHRlcmVkX2RhdGEgJT4lCiAgZHBseXI6OmZpbHRlcihhZ2dyZWdhdGVfcmFuayA8PSAwLjA1KSAlPiUKICBkcGx5cjo6ZmlsdGVyKAogICAgIWdyZXBsKGJsYWNrbGlzdF9wYXR0ZXJuLCBsaWdhbmQuY29tcGxleCwgICBwZXJsID0gVFJVRSkgJgogICAgIWdyZXBsKGJsYWNrbGlzdF9wYXR0ZXJuLCByZWNlcHRvci5jb21wbGV4LCBwZXJsID0gVFJVRSkKICApICU+JQogIGRwbHlyOjphbnRpX2pvaW4oCiAgICBscl9wYWlyX2JsYWNrbGlzdCwKICAgIGJ5ID0gYygibGlnYW5kLmNvbXBsZXgiID0gImxpZ2FuZCIsICJyZWNlcHRvci5jb21wbGV4IiA9ICJyZWNlcHRvciIpCiAgKQoKY2F0KCJGaW5hbCBzaWduaWZpY2FudCBpbnRlcmFjdGlvbnM6IiwgbnJvdyhzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMpLCAiXG4iKQoKIyBDb25maXJtIHRoZSAzIHJlbW92ZWQgcGFpcnMgYXJlIGdvbmUKY29uZmlybV9yZW1vdmVkIDwtIHNpZ25pZmljYW50X2ludGVyYWN0aW9ucyAlPiUKICBkcGx5cjo6ZmlsdGVyKAogICAgKGxpZ2FuZC5jb21wbGV4ID09ICJHTkFTIiAgJiByZWNlcHRvci5jb21wbGV4ID09ICJBRENZNyIpIHwKICAgIChsaWdhbmQuY29tcGxleCA9PSAiRkFCUDUiICYgcmVjZXB0b3IuY29tcGxleCA9PSAiUlhSQSIpICB8CiAgICAobGlnYW5kLmNvbXBsZXggPT0gIk5VQ0IyIiAmIHJlY2VwdG9yLmNvbXBsZXggPT0gIkVSQVAxIikKICApCmNhdCgiUmVtb3ZlZCBwYWlycyByZW1haW5pbmcgKG11c3QgYmUgMCk6IiwgbnJvdyhjb25maXJtX3JlbW92ZWQpLCAiXG4iKQoKIyBDb25maXJtIFZJTSwgUEtNLCBHUEkgc3Vydml2ZWQKY29uZmlybV9rZXB0IDwtIHNpZ25pZmljYW50X2ludGVyYWN0aW9ucyAlPiUKICBkcGx5cjo6ZmlsdGVyKGxpZ2FuZC5jb21wbGV4ICVpbiUgYygiVklNIiwgIlBLTSIsICJHUEkiKSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KGxpZ2FuZC5jb21wbGV4LCByZWNlcHRvci5jb21wbGV4KQpjYXQoIlZhbGlkYXRlZCBtb29ubGlnaHRpbmcgcHJvdGVpbnMgcHJlc2VydmVkOlxuIikKcHJpbnQoY29uZmlybV9rZXB0KQoKCmBgYAoKCi0tLQoKIyBCdWlsZCBIZWF0bWFwIFNjb3JlIE1hdHJpeAoKYGBge3IgYnVpbGQtbWF0cml4fQojIEd1YXJkIGFnYWluc3QgbG9nMTAoMCkgPSAtSW5mIHdpdGggZXBzaWxvbgpoZWF0bWFwX21hdHJpeCA8LSBzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMgJT4lCiAgZHBseXI6OnNlbGVjdChJbnRlcmFjdGlvbiwgQ2VsbExpbmUsIGFnZ3JlZ2F0ZV9yYW5rKSAlPiUKICBkcGx5cjo6bXV0YXRlKHNjb3JlID0gLWxvZzEwKGFnZ3JlZ2F0ZV9yYW5rICsgMWUtMTApKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoSW50ZXJhY3Rpb24sIENlbGxMaW5lKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKHNjb3JlID0gbWF4KHNjb3JlKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgdGlkeXI6OnBpdm90X3dpZGVyKAogICAgbmFtZXNfZnJvbSAgPSBDZWxsTGluZSwKICAgIHZhbHVlc19mcm9tID0gc2NvcmUsCiAgICB2YWx1ZXNfZmlsbCA9IDAKICApICU+JQogIGFzLmRhdGEuZnJhbWUoKQoKcm93bmFtZXMoaGVhdG1hcF9tYXRyaXgpIDwtIGhlYXRtYXBfbWF0cml4JEludGVyYWN0aW9uCmhlYXRtYXBfbWF0cml4JEludGVyYWN0aW9uIDwtIE5VTEwKaGVhdG1hcF9tYXRyaXggPC0gYXMubWF0cml4KGhlYXRtYXBfbWF0cml4KQoKY2F0KCJNYXRyaXg6IiwgbnJvdyhoZWF0bWFwX21hdHJpeCksICJpbnRlcmFjdGlvbnMgw5ciLAogICAgbmNvbChoZWF0bWFwX21hdHJpeCksICJjZWxsIGxpbmVzXG4iKQpgYGAKCi0tLQoKIyBGaWx0ZXI6IENvbnNlcnZlZCAmIFN0cm9uZyBJbnRlcmFjdGlvbnMKCmBgYHtyIGNvbnNlcnZlZC1maWx0ZXJ9CiMgQ29uc2VydmVkOiBwcmVzZW50IGluIOKJpTUgb2YgNyBjZWxsIGxpbmVzICgtbG9nMTAgc2NvcmUgPiAxLjMg4omhIHJhbmsgPCAwLjA1KQppc19jb25zZXJ2ZWQgPC0gcm93U3VtcyhoZWF0bWFwX21hdHJpeCA+IDEuMykgPj0gNQoKIyBTdHJvbmc6IGF0IGxlYXN0IG9uZSBjZWxsIGxpbmUgd2l0aCByYW5rIOKJpCAwLjAwMSAoc2NvcmUgPiAzKQppc19zdHJvbmcgICAgIDwtIGFwcGx5KGhlYXRtYXBfbWF0cml4LCAxLCBtYXgpID4gMwoKZmluYWxfbWF0cml4ICA8LSBoZWF0bWFwX21hdHJpeFtpc19jb25zZXJ2ZWQgJiBpc19zdHJvbmcsICwgZHJvcCA9IEZBTFNFXQpmaW5hbF9tYXRyaXggIDwtIGZpbmFsX21hdHJpeFsKICBvcmRlcihyb3dTdW1zKGZpbmFsX21hdHJpeCA+IDEuMyksIGRlY3JlYXNpbmcgPSBUUlVFKSwgLCBkcm9wID0gRkFMU0UKXQoKY2F0KCJDb25zZXJ2ZWQgJiBzdHJvbmcgaW50ZXJhY3Rpb25zOiIsIG5yb3coZmluYWxfbWF0cml4KSwgIlxuIikKYGBgCgotLS0KCiMgQ29uc2VydmVkIEludGVyYWN0aW9uIEhlYXRtYXAKCmBgYHtyIGNvbnNlcnZlZC1oZWF0bWFwLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD02fQpjb2xfZnVuIDwtIGNvbG9yUmFtcDIoYygwLCA1KSwgYygid2hpdGUiLCAiZmlyZWJyaWNrIikpCgpodCA8LSBIZWF0bWFwKAogIGFzLm1hdHJpeChmaW5hbF9tYXRyaXgpLAogIG5hbWUgICAgICAgICAgICAgPSAiLWxvZzEwKFJhbmspIiwKICBjb2wgICAgICAgICAgICAgID0gY29sX2Z1biwKICBjbHVzdGVyX2NvbHVtbnMgID0gRkFMU0UsCiAgY2x1c3Rlcl9yb3dzICAgICA9IFRSVUUsCiAgc2hvd19yb3dfZGVuZCAgICA9IFRSVUUsCiAgc2hvd19jb2x1bW5fZGVuZCA9IEZBTFNFLAogIHJvd19uYW1lc19ncCAgICAgPSBncGFyKGZvbnRzaXplID0gNyksCiAgY29sdW1uX25hbWVzX2dwICA9IGdwYXIoZm9udHNpemUgPSAxMCwgZm9udGZhY2UgPSAiYm9sZCIpLAogIGNvbHVtbl90aXRsZSAgICAgPSAiQ29uc2VydmVkICYgQ2VsbCBMaW5lLVNwZWNpZmljIFNpZ25hbGluZyIsCiAgYm9yZGVyICAgICAgICAgICA9IFRSVUUKKQoKZHJhdyhodCkKCnBkZigiRmlndXJlXzNfMTVfQ29uc2VydmVkX0hlYXRtYXAucGRmIiwgd2lkdGggPSA3LCBoZWlnaHQgPSA3KQogIGRyYXcoaHQpCmRldi5vZmYoKQoKcG5nKCJGaWd1cmVfM18xNV9Db25zZXJ2ZWRfSGVhdG1hcC5wbmciLCB3aWR0aCA9IDE4MDAsIGhlaWdodCA9IDIyMDAsIHJlcyA9IDMwMCkKICBkcmF3KGh0KQpkZXYub2ZmKCkKCmNhdCgi4pyTIENvbnNlcnZlZCBoZWF0bWFwIHNhdmVkXG4iKQpgYGAKCgotLS0KCiMgVG9wIDUwIFN0cm9uZ2VzdCBJbnRlcmFjdGlvbnMgSGVhdG1hcAoKYGBge3IgdG9wNTAtaGVhdG1hcCwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTZ9Cm1heF9zY29yZXMgICAgPC0gYXBwbHkoaGVhdG1hcF9tYXRyaXgsIDEsIG1heCkKbl90b3AgICAgICAgICA8LSBtaW4oNTAsIG5yb3coaGVhdG1hcF9tYXRyaXgpKQp0b3BfNTBfbmFtZXMgIDwtIG5hbWVzKHNvcnQobWF4X3Njb3JlcywgZGVjcmVhc2luZyA9IFRSVUUpKVtzZXFfbGVuKG5fdG9wKV0KdG9wNTBfbWF0cml4ICA8LSBhcy5tYXRyaXgoaGVhdG1hcF9tYXRyaXhbdG9wXzUwX25hbWVzLCAsIGRyb3AgPSBGQUxTRV0pCnRvcDUwX21hdHJpeCAgPC0gdG9wNTBfbWF0cml4WwogIG9yZGVyKHJvd1N1bXModG9wNTBfbWF0cml4ID4gMS4zKSwgZGVjcmVhc2luZyA9IFRSVUUpLCAsIGRyb3AgPSBGQUxTRQpdCgpodF90b3A1MCA8LSBIZWF0bWFwKAogIHRvcDUwX21hdHJpeCwKICBuYW1lICAgICAgICAgICAgID0gIi1sb2cxMChSYW5rKSIsCiAgY29sICAgICAgICAgICAgICA9IGNvbF9mdW4sCiAgY2x1c3Rlcl9jb2x1bW5zICA9IEZBTFNFLAogIGNsdXN0ZXJfcm93cyAgICAgPSBUUlVFLAogIHNob3dfcm93X2RlbmQgICAgPSBUUlVFLAogIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICByb3dfbmFtZXNfZ3AgICAgID0gZ3Bhcihmb250c2l6ZSA9IDgpLAogIGNvbHVtbl9uYW1lc19ncCAgPSBncGFyKGZvbnRzaXplID0gMTAsIGZvbnRmYWNlID0gImJvbGQiKSwKICBjb2x1bW5fdGl0bGUgICAgID0gIlRvcCA1MCBTdHJvbmdlc3QgSW50ZXJhY3Rpb25zIiwKICBib3JkZXIgICAgICAgICAgID0gVFJVRQopCgpkcmF3KGh0X3RvcDUwKQoKcGRmKCJGaWd1cmVfVG9wNTBfU3Ryb25nZXN0X0ludGVyYWN0aW9ucy5wZGYiLCB3aWR0aCA9IDgsIGhlaWdodCA9IDEwKQogIGRyYXcoaHRfdG9wNTApCmRldi5vZmYoKQoKcG5nKCJGaWd1cmVfVG9wNTBfU3Ryb25nZXN0X0ludGVyYWN0aW9ucy5wbmciLCB3aWR0aCA9IDI0MDAsIGhlaWdodCA9IDMwMDAsIHJlcyA9IDMwMCkKICBkcmF3KGh0X3RvcDUwKQpkZXYub2ZmKCkKCmNhdCgi4pyTIFRvcCA1MCBoZWF0bWFwIHNhdmVkXG4iKQpgYGAKCi0tLQoKIyBNYXN0ZXIgTGlnYW5kcwoKYGBge3IgbWFzdGVyLWxpZ2FuZHN9CmxpZ2FuZF9hY3Rpdml0eSA8LSBmaWx0ZXJlZF9kYXRhICU+JQogIGRwbHlyOjpncm91cF9ieShsaWdhbmQuY29tcGxleCkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZSgKICAgIGludGVyYWN0aW9ucyA9IG4oKSwKICAgIHJlY2VwdG9ycyAgICA9IG5fZGlzdGluY3QocmVjZXB0b3IuY29tcGxleCksCiAgICBjZWxsX2xpbmVzICAgPSBuX2Rpc3RpbmN0KENlbGxMaW5lKSwKICAgIC5ncm91cHMgICAgICA9ICJkcm9wIgogICkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhpbnRlcmFjdGlvbnMpKQoKcHJpbnQoaGVhZChsaWdhbmRfYWN0aXZpdHksIDIwKSkKYGBgCgotLS0KCiMgTWFzdGVyIExpZ2FuZHMgUGxvdAoKYGBge3IgbGlnYW5kLXBsb3QsIGZpZy5oZWlnaHQ9NiwgZmlnLndpZHRoPTh9CmdncGxvdChoZWFkKGxpZ2FuZF9hY3Rpdml0eSwgMjApLAogICAgICAgYWVzKHggPSByZW9yZGVyKGxpZ2FuZC5jb21wbGV4LCBpbnRlcmFjdGlvbnMpLCB5ID0gaW50ZXJhY3Rpb25zKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gImZpcmVicmljayIpICsKICBjb29yZF9mbGlwKCkgKwogIHRoZW1lX2NsYXNzaWMoYmFzZV9zaXplID0gMTIpICsKICBsYWJzKHggPSAiTGlnYW5kIiwgeSA9ICJOdW1iZXIgb2YgSW50ZXJhY3Rpb25zIiwKICAgICAgIHRpdGxlID0gIk1hc3RlciBTaWduYWxpbmcgTGlnYW5kcyDigJQgU8OpemFyeSBTeW5kcm9tZSBDZWxsIExpbmVzIikKCmdnc2F2ZSgiRmlndXJlX01hc3Rlcl9MaWdhbmRzLnBkZiIsIHdpZHRoID0gOCwgaGVpZ2h0ID0gNikKZ2dzYXZlKCJGaWd1cmVfTWFzdGVyX0xpZ2FuZHMucG5nIiwgd2lkdGggPSA4LCBoZWlnaHQgPSA2LCBkcGkgPSAzMDApCmBgYAoKLS0tCgojIENvbnNlcnZlZCBJbnRlcmFjdGlvbnMgVGFibGUKCmBgYHtyIGNvbnNlcnZlZC10YWJsZX0KY29uc2VydmVkX3RocmVzaG9sZCA8LSA0Cgpjb25zZXJ2ZWRfaW50ZXJhY3Rpb25zIDwtIGFzLmRhdGEuZnJhbWUoZmluYWxfbWF0cml4KSAlPiUKICBkcGx5cjo6bXV0YXRlKAogICAgSW50ZXJhY3Rpb24gID0gcm93bmFtZXMoLiksCiAgICBOdW1DZWxsTGluZXMgPSByb3dTdW1zKGRwbHlyOjphY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykpID4gMS4zKQogICkgJT4lCiAgZHBseXI6OmZpbHRlcihOdW1DZWxsTGluZXMgPj0gY29uc2VydmVkX3RocmVzaG9sZCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhOdW1DZWxsTGluZXMpKSAlPiUKICBkcGx5cjo6c2VsZWN0KEludGVyYWN0aW9uLCBOdW1DZWxsTGluZXMsIGRwbHlyOjpldmVyeXRoaW5nKCkpCgpjYXQoIkNvbnNlcnZlZCBpbnRlcmFjdGlvbnMgKOKJpSIsIGNvbnNlcnZlZF90aHJlc2hvbGQsICJjZWxsIGxpbmVzKToiLAogICAgbnJvdyhjb25zZXJ2ZWRfaW50ZXJhY3Rpb25zKSwgIlxuIikKcHJpbnQoaGVhZChjb25zZXJ2ZWRfaW50ZXJhY3Rpb25zWywgYygiSW50ZXJhY3Rpb24iLCAiTnVtQ2VsbExpbmVzIildLCAyMCkpCndyaXRlLmNzdihjb25zZXJ2ZWRfaW50ZXJhY3Rpb25zLCAiQ29uc2VydmVkX0ludGVyYWN0aW9uc19TUy5jc3YiLCByb3cubmFtZXMgPSBGQUxTRSkKYGBgCgotLS0KCiMgQ2VsbCBMaW5l4oCTU3BlY2lmaWMgSW50ZXJhY3Rpb25zCgpgYGB7ciBzcGVjaWZpYy10YWJsZX0Kc3BlY2lmaWNfaW50ZXJhY3Rpb25zIDwtIGFzLmRhdGEuZnJhbWUoZmluYWxfbWF0cml4KSAlPiUKICBkcGx5cjo6bXV0YXRlKAogICAgSW50ZXJhY3Rpb24gID0gcm93bmFtZXMoLiksCiAgICBOdW1DZWxsTGluZXMgPSByb3dTdW1zKGRwbHlyOjphY3Jvc3Mod2hlcmUoaXMubnVtZXJpYykpID4gMS4zKQogICkgJT4lCiAgZHBseXI6OmZpbHRlcihOdW1DZWxsTGluZXMgPD0gNSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoTnVtQ2VsbExpbmVzKSAlPiUKICBkcGx5cjo6c2VsZWN0KEludGVyYWN0aW9uLCBOdW1DZWxsTGluZXMsIGRwbHlyOjpldmVyeXRoaW5nKCkpCgpjYXQoIkNlbGwgbGluZS1zcGVjaWZpYyBpbnRlcmFjdGlvbnM6IiwgbnJvdyhzcGVjaWZpY19pbnRlcmFjdGlvbnMpLCAiXG4iKQpwcmludChoZWFkKHNwZWNpZmljX2ludGVyYWN0aW9uc1ssIGMoIkludGVyYWN0aW9uIiwgIk51bUNlbGxMaW5lcyIpXSwgMjApKQp3cml0ZS5jc3Yoc3BlY2lmaWNfaW50ZXJhY3Rpb25zLCAiQ2VsbExpbmVTcGVjaWZpY19JbnRlcmFjdGlvbnMuY3N2Iiwgcm93Lm5hbWVzID0gRkFMU0UpCmBgYAoKLS0tCgojIFVwU2V0UiDigJQgSW50ZXJhY3Rpb24gU2hhcmluZwoKYGBge3IgdXBzZXRyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD0xMH0KdXBzZXRfZGYgICAgICAgIDwtIGFzLmRhdGEuZnJhbWUoaGVhdG1hcF9tYXRyaXggPiAxLjMpICogMUwKYXZhaWxhYmxlX2xpbmVzIDwtIGludGVyc2VjdCgKICBjKCJMMSIsIkwyIiwiTDMiLCJMNCIsIkw1IiwiTDYiLCJMNyIpLAogIGNvbG5hbWVzKHVwc2V0X2RmKQopCgp1cHNldCgKICB1cHNldF9kZiwKICBzZXRzICAgICAgICAgICAgICA9IHJldihhdmFpbGFibGVfbGluZXMpLAogIG9yZGVyLmJ5ICAgICAgICAgID0gImZyZXEiLAogIGtlZXAub3JkZXIgICAgICAgID0gVFJVRSwKICBzZXRzLmJhci5jb2xvciAgICA9ICJmaXJlYnJpY2siLAogIG1haW4uYmFyLmNvbG9yICAgID0gImJsYWNrIiwKICBtYXRyaXguY29sb3IgICAgICA9ICJibGFjayIsCiAgbWFpbmJhci55LmxhYmVsICAgPSAiTnVtYmVyIG9mIFNoYXJlZCBJbnRlcmFjdGlvbnMiLAogIHNldHMueC5sYWJlbCAgICAgID0gIlRvdGFsIFNpZ25pZmljYW50IEludGVyYWN0aW9ucyIsCiAgdGV4dC5zY2FsZSAgICAgICAgPSBjKDEuNSwgMS4yLCAxLjIsIDEuMiwgMS41LCAxLjIpLAogIG1iLnJhdGlvICAgICAgICAgID0gYygwLjYsIDAuNCkKKQoKcGRmKCJGaWd1cmVfM18xNUJfVXBTZXRSLnBkZiIsIHdpZHRoID0gMTAsIGhlaWdodCA9IDcsIG9uZWZpbGUgPSBGQUxTRSkKICB1cHNldCh1cHNldF9kZiwgc2V0cyA9IHJldihhdmFpbGFibGVfbGluZXMpLCBvcmRlci5ieSA9ICJmcmVxIiwKICAgICAgICBrZWVwLm9yZGVyID0gVFJVRSwgc2V0cy5iYXIuY29sb3IgPSAiZmlyZWJyaWNrIiwKICAgICAgICBtYWluYmFyLnkubGFiZWwgPSAiTnVtYmVyIG9mIFNoYXJlZCBJbnRlcmFjdGlvbnMiLAogICAgICAgIHNldHMueC5sYWJlbCAgICA9ICJUb3RhbCBTaWduaWZpY2FudCBJbnRlcmFjdGlvbnMiLAogICAgICAgIHRleHQuc2NhbGUgPSBjKDEuNSwgMS4yLCAxLjIsIDEuMiwgMS41LCAxLjIpKQpkZXYub2ZmKCkKCnBuZygiRmlndXJlXzNfMTVCX1VwU2V0Ui5wbmciLCB3aWR0aCA9IDMwMDAsIGhlaWdodCA9IDIxMDAsIHJlcyA9IDMwMCkKICB1cHNldCh1cHNldF9kZiwgc2V0cyA9IHJldihhdmFpbGFibGVfbGluZXMpLCBvcmRlci5ieSA9ICJmcmVxIiwKICAgICAgICBrZWVwLm9yZGVyID0gVFJVRSwgc2V0cy5iYXIuY29sb3IgPSAiZmlyZWJyaWNrIiwKICAgICAgICBtYWluYmFyLnkubGFiZWwgPSAiTnVtYmVyIG9mIFNoYXJlZCBJbnRlcmFjdGlvbnMiLAogICAgICAgIHNldHMueC5sYWJlbCAgICA9ICJUb3RhbCBTaWduaWZpY2FudCBJbnRlcmFjdGlvbnMiLAogICAgICAgIHRleHQuc2NhbGUgPSBjKDEuNSwgMS4yLCAxLjIsIDEuMiwgMS41LCAxLjIpKQpkZXYub2ZmKCkKCmNhdCgi4pyTIFVwU2V0UiBwbG90IHNhdmVkXG4iKQpgYGAKCi0tLQoKIyBFeHBvcnQgJiBTdW1tYXJ5CgpgYGB7ciBleHBvcnQtc3VtbWFyeX0Kd3JpdGUuY3N2KGxpZ2FuZF9hY3Rpdml0eSwgIk1hc3Rlcl9MaWdhbmRzX1NTLmNzdiIsIHJvdy5uYW1lcyA9IEZBTFNFKQoKY2F0KCI9PT0gQU5BTFlTSVMgU1VNTUFSWSA9PT1cbiIpCmNhdCgiVG90YWwgTElBTkEgcmVjb3JkczogICAgICAgICAgICAgICIsIG5yb3coYWxsX2RhdGEpLCAgICAgICAgICAgICAgICJcbiIpCmNhdCgiQWZ0ZXIgT21uaVBhdGggZmlsdGVyOiAgICAgICAgICAgICIsIG5yb3coZmlsdGVyZWRfZGF0YSksICAgICAgICAgICJcbiIpCmNhdCgiU2lnbmlmaWNhbnQgKHJhbmsg4omkIDAuMDUpOiAgICAgICAgIiwgbnJvdyhzaWduaWZpY2FudF9pbnRlcmFjdGlvbnMpLCJcbiIpCmNhdCgiQ29uc2VydmVkICYgc3Ryb25nIChmaW5hbCk6ICAgICAgICIsIG5yb3coZmluYWxfbWF0cml4KSwgICAgICAgICAgICJcbiIpCmNhdCgiQ29uc2VydmVkICjiiaU0IGxpbmVzKTogICAgICAgICAgICAgIiwgbnJvdyhjb25zZXJ2ZWRfaW50ZXJhY3Rpb25zKSwgICJcbiIpCmNhdCgiQ2VsbCBsaW5lLXNwZWNpZmljICjiiaQyIGxpbmVzKTogICAgIiwgbnJvdyhzcGVjaWZpY19pbnRlcmFjdGlvbnMpLCAgICJcbiIpCmBgYAoKLS0tCgojIFNlc3Npb24gSW5mbwoKYGBge3Igc2Vzc2lvbi1pbmZvfQpzZXNzaW9uSW5mbygpCmBgYAo=