This script performs differential expressed genes (DEG) analysis and Gene Set Enrichment Analysis (GSEA) per cell type/cluster. Cluster free analysis is covered in script 5). More info about the package cacoa here: https://github.com/kharchenkolab/cacoa pre-print: https://www.biorxiv.org/content/10.1101/2022.03.15.484475v1.full.pdf Vignette: https://pklab.med.harvard.edu/viktor/cacoa/walkthrough_short.html

Setup

#Load helper functions
source("/people/nrq364/NeuSiC/scRNA_helper_new.R")

devtools::load_all("/people/nrq364/cacoa_dev")
#library(cacoa)
library(magrittr)
library(conos)
library(pagoda2)
library(qs)
library(ggplot2)
library(ggrastr)
library(openxlsx)
library(EnhancedVolcano)
library(cowplot)

1. Read in data

Read in data generated in notebook 3.

cao <- qread("cao.qs", nthreads = 10)

2. Differentially expressed genes (DEGs) analysis

DESeq2 uses samplewise pseudobulk gene expression.

  • Larger clusters generally lead to more DEGs due to increased statistical power and higher overall counts in the pseudobulk approach.
  • Smaller clusters may yield fewer DEGs because of lower counts, higher variability, and reduced statistical power.
cao$estimateDEPerCellType(n.cores=50, test = "DESeq2.Wald", verbose = T, independent.filtering = T) 

2.1 Save DEG result as table

Extract DEG results from cao object. DEG is a list. Contains all genes.

DEG <- cao$test.results$de %>% lapply("[[", "res")

Save as an excel file, with one sheet per cell type.

write.xlsx(x = DEG, file= "DEG.xlsx")

Save only DE genes with padj < 0.05

DEG_sig <- lapply(DEG, function(df) df[df$padj < 0.05, ])

Save as excel file with one sheet per cell type.

write.xlsx(x = DEG_sig, file="DEG_sig.xlsx")

2.2 Visualization of DE genes as Volcano plot

Use the package EnhancedVolcano to plot volcano plots of cell types of interest. Default cut-offs: p.adj < 0.05, log2FC > |1.5|

  EnhancedVolcano(DEG$CGE_IPC,
    lab = rownames(DEG$CGE_IPC),
    x = 'log2FoldChange',
    y = 'padj',
    title = 'CGE IPCs',
    pCutoff = 0.05,
    FCcutoff = 1.5,
    pointSize = 2.0,
    labSize = 4.0,
    colAlpha = 0.8,
    legendPosition = 'right',
    legendLabSize = 12,
    legendIconSize = 2.0,
    drawConnectors = TRUE,
    widthConnectors = 0.75, ylim = c(0,6), subtitleLabSize = 0)

Plot all cell types.

volcano_list <- lapply(names(DEG), function(cell_type) {
  EnhancedVolcano(DEG[[cell_type]],
                  lab = rownames(DEG[[cell_type]]),
                  x = 'log2FoldChange',
                  y = 'pvalue',
                  title = cell_type,
                  pCutoff = 0.05,
                  FCcutoff = 1.5,
                  pointSize = 2.0,
                  labSize = 4.0,
                  colAlpha = 0.8,
                  drawConnectors = TRUE,
                  widthConnectors = 0.75,
                  titleLabSize = 8,      # title font
                  subtitleLabSize = 0,   # subtitle font
                  captionLabSize = 4,     # caption font
                  axisLabSize = 8, 
                  ylim = c(0,6)) + theme(
    legend.position = "none")
})

library(patchwork)
wrap_plots(volcano_list, ncol = 4)

3. Gene set enrichment analysis (GSEA)

GSEA takes the whole ranked list of DE genes, so n.top.genes parameter is not relevant here. BH method used for multiple comparison correction.

cao$estimateOntology(type="GSEA", org.db=org.Mm.eg.db::org.Mm.eg.db)

3.1 Save GSEA result as table

Use a function to save terms with p.adj < 0.05 as a .tsv file and read it into R. It is one huge table with all cell types and all 3 subtypes of gene ontologies (biological process = BP, molecular function = MF, cellular compartment = CC).

cao$saveOntologyAsTable("GSEA.tsv", name="GSEA")
GSEA <- read.table("GSEA.tsv", sep="\t", header = T)

Transform GSEA table into a list of tables (one table per cell type).

cell_types <- unique(GSEA$CellType)
GSEA_list <- lapply(cell_types, {function(x) GSEA[GSEA$CellType == x, ]})
names(GSEA_list) <- cell_types
write.xlsx(x = GSEA_list, file= "GSEA.xlsx")

Explanation of result table

  • Description: Gene set name
  • setSize: number of genes with gene-level statistic values. To be more specific, if we input a pathway gene set with 58 genes (e.g. HALLMARK_MYC_TARGETS_V2 ), while our gene list only have 54 of it, so the result will only show setSize = 54
  • enrichmentScore: also called ES, same as in Broad GSEA implementation. It reflects the degree to which a gene set is overrepresented at the top or bottom of a ranked list of genes.
  • NES: normalized enrichment score is the primary statistic for examining gene set enrichment results. By normalizing the enrichment score, GSEA accounts for differences in gene set size and in correlations between gene sets and the expression dataset. Therefore, NES can be used to compare analysis results across gene sets. A positive normalized enrichment scores (NES) will indicate that genes in set S will be mostly represented at the top of your list (logFC > 0 or up-regulated genes).
  • rank: The position in the ranked list at which the maximum enrichment score occurred. If gene sets achieve the maximum enrichment score near the top or bottom of the ranked list, the rank at max is either very small or very large.
  • leading_edge: includes three statistics
    • tags: The percentage of gene hits before (for positive ES) or after (for negative ES) the peak in the running enrichment score, which indicates the percentage of genes contributing to the enrichment score.
    • list: The percentage of genes in the ranked gene list before (for positive ES) or after (for negative ES) the peak in the running enrichment score. This gives an indication of where in the list the enrichment score is attained.
    • signal: If the gene set is entirely within the first N positions in the list, then the signal strength is maximal or 100%. If the gene set is spread throughout the list, then the signal strength decreases towards 0%.

3.2 Visualization of GSEA results as heatmap

As default only GO terms of BP (biological process) subtype are plotted. Similar GO terms are clustered together to reduce redundancy. As a default we plot the top 20 up- and downregulated GO terms.

cao$plotOntologyHeatmap(name="GSEA", genes="down", subtype="BP", top.n = 20, cluster = F)
Loading required package: DOSE

Registered S3 method overwritten by 'data.table':
  method           from
  print.data.table     
DOSE v4.0.1 Learn more at https://yulab-smu.top/contribution-knowledge-mining/

Please cite:

Guangchuang Yu, Li-Gen Wang, Guang-Rong Yan, Qing-Yu He. DOSE: an R/Bioconductor package for Disease Ontology
Semantic and Enrichment analysis. Bioinformatics. 2015, 31(4):608-609

cao$plotOntologyHeatmap(name="GSEA", genes="up", subtype="BP", top.n = 20, cluster = F)

LS0tCnRpdGxlOiAiNCkgRGlmZmVyZW50aWFsIGFuYWx5c2lzIgpkYXRlOiAiYHIgU3lzLkRhdGUoKWAiCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZm9sZGluZzogbm9uZQotLS0KClRoaXMgc2NyaXB0IHBlcmZvcm1zIGRpZmZlcmVudGlhbCBleHByZXNzZWQgZ2VuZXMgKERFRykgYW5hbHlzaXMgYW5kIEdlbmUgU2V0IEVucmljaG1lbnQgQW5hbHlzaXMgKEdTRUEpIHBlciBjZWxsIHR5cGUvY2x1c3Rlci4gQ2x1c3RlciBmcmVlIGFuYWx5c2lzIGlzIGNvdmVyZWQgaW4gc2NyaXB0IDUpLiAKTW9yZSBpbmZvIGFib3V0IHRoZSBwYWNrYWdlIGNhY29hIGhlcmU6IGh0dHBzOi8vZ2l0aHViLmNvbS9raGFyY2hlbmtvbGFiL2NhY29hCnByZS1wcmludDogaHR0cHM6Ly93d3cuYmlvcnhpdi5vcmcvY29udGVudC8xMC4xMTAxLzIwMjIuMDMuMTUuNDg0NDc1djEuZnVsbC5wZGYKVmlnbmV0dGU6IGh0dHBzOi8vcGtsYWIubWVkLmhhcnZhcmQuZWR1L3Zpa3Rvci9jYWNvYS93YWxrdGhyb3VnaF9zaG9ydC5odG1sCgojIFNldHVwCmBgYHtyLCB3YXJuaW5nPUZBTFNFLG1lc3NhZ2U9RkFMU0V9CiNMb2FkIGhlbHBlciBmdW5jdGlvbnMKc291cmNlKCIvcGVvcGxlL25ycTM2NC9OZXVTaUMvc2NSTkFfaGVscGVyX25ldy5SIikKCmRldnRvb2xzOjpsb2FkX2FsbCgiL3Blb3BsZS9ucnEzNjQvY2Fjb2FfZGV2IikKI2xpYnJhcnkoY2Fjb2EpCmxpYnJhcnkobWFncml0dHIpCmxpYnJhcnkoY29ub3MpCmxpYnJhcnkocGFnb2RhMikKbGlicmFyeShxcykKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGdncmFzdHIpCmxpYnJhcnkob3Blbnhsc3gpCmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpsaWJyYXJ5KGNvd3Bsb3QpCmBgYAoKIyAxLiBSZWFkIGluIGRhdGEKUmVhZCBpbiBkYXRhIGdlbmVyYXRlZCBpbiBub3RlYm9vayAzLgpgYGB7cn0KY2FvIDwtIHFyZWFkKCJjYW8ucXMiLCBudGhyZWFkcyA9IDEwKQpgYGAKCgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GfQojIFVNQVAgdmlzdWFsaXphdGlvbiBqdXN0IHRvIG1ha2Ugc3VyZSByaWdodCBkYXRhIGlzIGxvYWRlZApjYW8kcGxvdEVtYmVkZGluZyhjb2xvci5ieT0nY2VsbC5ncm91cHMnLCBhbHBoYT0xLCBzaXplPTIsIHRpdGxlPScnLHBsb3QubmE9RkFMU0UsIHNob3cubGVnZW5kPUYsIGZvbnQuc2l6ZT1jKDgsMTIpLCBsZWdlbmQucG9zaXRpb249ImJvdHRvbSIpCmBgYAoKIyAyLiBEaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgKERFR3MpIGFuYWx5c2lzCkRFU2VxMiB1c2VzIHNhbXBsZXdpc2UgcHNldWRvYnVsayBnZW5lIGV4cHJlc3Npb24uCgoqIExhcmdlciBjbHVzdGVycyBnZW5lcmFsbHkgbGVhZCB0byBtb3JlIERFR3MgZHVlIHRvIGluY3JlYXNlZCBzdGF0aXN0aWNhbCBwb3dlciBhbmQgaGlnaGVyIG92ZXJhbGwgY291bnRzIGluIHRoZSBwc2V1ZG9idWxrIGFwcHJvYWNoLgoqIFNtYWxsZXIgY2x1c3RlcnMgbWF5IHlpZWxkIGZld2VyIERFR3MgYmVjYXVzZSBvZiBsb3dlciBjb3VudHMsIGhpZ2hlciB2YXJpYWJpbGl0eSwgYW5kIHJlZHVjZWQgc3RhdGlzdGljYWwgcG93ZXIuCgpgYGB7ciwgZXZhbD1GQUxTRX0KY2FvJGVzdGltYXRlREVQZXJDZWxsVHlwZShuLmNvcmVzPTUwLCB0ZXN0ID0gIkRFU2VxMi5XYWxkIiwgdmVyYm9zZSA9IFQsIGluZGVwZW5kZW50LmZpbHRlcmluZyA9IFQpIApgYGAKCmBgYHtyLCBpbmNsdWRlPUYsIGV2YWw9Rn0KI2NhbyRwbG90TnVtYmVyT2ZERUdlbmVzKCkKYGBgCgojIyAyLjEgU2F2ZSBERUcgcmVzdWx0IGFzIHRhYmxlCkV4dHJhY3QgREVHIHJlc3VsdHMgZnJvbSBjYW8gb2JqZWN0LiBERUcgaXMgYSBsaXN0LiBDb250YWlucyBhbGwgZ2VuZXMuCmBgYHtyfQpERUcgPC0gY2FvJHRlc3QucmVzdWx0cyRkZSAlPiUgbGFwcGx5KCJbWyIsICJyZXMiKQpgYGAKU2F2ZSBhcyBhbiBleGNlbCBmaWxlLCB3aXRoIG9uZSBzaGVldCBwZXIgY2VsbCB0eXBlLgpgYGB7cn0Kd3JpdGUueGxzeCh4ID0gREVHLCBmaWxlPSAiREVHLnhsc3giKQpgYGAKU2F2ZSBvbmx5IERFIGdlbmVzIHdpdGggcGFkaiA8IDAuMDUKYGBge3J9CkRFR19zaWcgPC0gbGFwcGx5KERFRywgZnVuY3Rpb24oZGYpIGRmW2RmJHBhZGogPCAwLjA1LCBdKQpgYGAKU2F2ZSBhcyBleGNlbCBmaWxlIHdpdGggb25lIHNoZWV0IHBlciBjZWxsIHR5cGUuCmBgYHtyfQp3cml0ZS54bHN4KHggPSBERUdfc2lnLCBmaWxlPSJERUdfc2lnLnhsc3giKQpgYGAKYGBge3IsIGVjaG89Rn0KI3RvIHJlbmFtZSBhIGNsdXN0ZXIKbmFtZXMoREVHKVtuYW1lcyhERUcpID09ICdQSUNfZW5yaWNoZWRfaW1tYXR1cmVfcHJpbmNpcGFsX25ldXJvbnMnXSA8LSAnUElDX2ltbWF0dXJlX3ByaW5jaXBhbF9uZXUnCmBgYAoKIyMgMi4yIFZpc3VhbGl6YXRpb24gb2YgREUgZ2VuZXMgYXMgVm9sY2FubyBwbG90CmBgYHtyLCB3YXJuaW5nPUYsIGluY2x1ZGUgPUZ9CiNjYW8kcGxvdFZvbGNhbm8oeGxpbT1jKC0zLCAzKSwgeWxpbT1jKDAsIDcuNSksIGNlbGwudHlwZXMgPSBjKCJDR0VfSVBDIiwgIk1HRV9JUEMiKSkKYGBgClVzZSB0aGUgcGFja2FnZSBFbmhhbmNlZFZvbGNhbm8gdG8gcGxvdCB2b2xjYW5vIHBsb3RzIG9mIGNlbGwgdHlwZXMgb2YgaW50ZXJlc3QuCkRlZmF1bHQgY3V0LW9mZnM6IHAuYWRqIDwgMC4wNSwgbG9nMkZDID4gfDEuNXwKCmBgYHtyLCBmaWcuaGVpZ2h0PTcsIGZpZy53aWR0aD03fQogIEVuaGFuY2VkVm9sY2FubyhERUckQ0dFX0lQQywKICAgIGxhYiA9IHJvd25hbWVzKERFRyRDR0VfSVBDKSwKICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLAogICAgeSA9ICdwYWRqJywKICAgIHRpdGxlID0gJ0NHRSBJUENzJywKICAgIHBDdXRvZmYgPSAwLjA1LAogICAgRkNjdXRvZmYgPSAxLjUsCiAgICBwb2ludFNpemUgPSAyLjAsCiAgICBsYWJTaXplID0gNC4wLAogICAgY29sQWxwaGEgPSAwLjgsCiAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgICBsZWdlbmRMYWJTaXplID0gMTIsCiAgICBsZWdlbmRJY29uU2l6ZSA9IDIuMCwKICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgIHdpZHRoQ29ubmVjdG9ycyA9IDAuNzUsIHlsaW0gPSBjKDAsNiksIHN1YnRpdGxlTGFiU2l6ZSA9IDApCmBgYAoKCmBgYHtyLCBpbmNsdWRlPUYsIGV2YWwgPUZ9CiNQbG90IHNldmVyYWwgY2VsbCB0eXBlcyBuZXh0IHRvIGVhY2ggb3RoZXIuCnAxIDwtIEVuaGFuY2VkVm9sY2FubyhERUckQ0dFX0lQQywKICAgIGxhYiA9IHJvd25hbWVzKERFRyRDR0VfSVBDKSwKICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLAogICAgeSA9ICdwYWRqJywKICAgIHRpdGxlID0gJ0NHRSBJUENzJywKICAgIHBDdXRvZmYgPSAwLjA1LAogICAgRkNjdXRvZmYgPSAxLjUsCiAgICBwb2ludFNpemUgPSAyLjAsCiAgICBsYWJTaXplID0gNC4wLAogICAgY29sQWxwaGEgPSAwLjgsCiAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgICBsZWdlbmRMYWJTaXplID0gMTIsCiAgICBsZWdlbmRJY29uU2l6ZSA9IDIuMCwKICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgIHdpZHRoQ29ubmVjdG9ycyA9IDAuNzUsCiAgICB5bGltID0gYygwLDYpKQoKcDIgPC0gIEVuaGFuY2VkVm9sY2FubyhERUckRG9yc2FsX0lQQywKICAgIGxhYiA9IHJvd25hbWVzKERFRyREb3JzYWxfSVBDKSwKICAgIHggPSAnbG9nMkZvbGRDaGFuZ2UnLAogICAgeSA9ICdwYWRqJywKICAgIHRpdGxlID0gJ0RvcnNhbCBJUENzJywKICAgIHBDdXRvZmYgPSAwLjA1LAogICAgRkNjdXRvZmYgPSAxLjUsCiAgICBwb2ludFNpemUgPSAyLjAsCiAgICBsYWJTaXplID0gNC4wLAogICAgY29sQWxwaGEgPSAwLjgsCiAgICBsZWdlbmRQb3NpdGlvbiA9ICdyaWdodCcsCiAgICBsZWdlbmRMYWJTaXplID0gMTIsCiAgICBsZWdlbmRJY29uU2l6ZSA9IDIuMCwKICAgIGRyYXdDb25uZWN0b3JzID0gVFJVRSwKICAgIHdpZHRoQ29ubmVjdG9ycyA9IDAuNzUsCiAgICB5bGltID0gYygwLDYpKQpgYGAKCgpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTYsIGluY2x1ZGU9RiwgZXZhbD1GfQpwbG90X2dyaWQocDEsIHAyKQpgYGAKCgpQbG90IGFsbCBjZWxsIHR5cGVzLgpgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTIwLCB3YXJuaW5nPUZ9CnZvbGNhbm9fbGlzdCA8LSBsYXBwbHkobmFtZXMoREVHKSwgZnVuY3Rpb24oY2VsbF90eXBlKSB7CiAgRW5oYW5jZWRWb2xjYW5vKERFR1tbY2VsbF90eXBlXV0sCiAgICAgICAgICAgICAgICAgIGxhYiA9IHJvd25hbWVzKERFR1tbY2VsbF90eXBlXV0pLAogICAgICAgICAgICAgICAgICB4ID0gJ2xvZzJGb2xkQ2hhbmdlJywKICAgICAgICAgICAgICAgICAgeSA9ICdwdmFsdWUnLAogICAgICAgICAgICAgICAgICB0aXRsZSA9IGNlbGxfdHlwZSwKICAgICAgICAgICAgICAgICAgcEN1dG9mZiA9IDAuMDUsCiAgICAgICAgICAgICAgICAgIEZDY3V0b2ZmID0gMS41LAogICAgICAgICAgICAgICAgICBwb2ludFNpemUgPSAyLjAsCiAgICAgICAgICAgICAgICAgIGxhYlNpemUgPSA0LjAsCiAgICAgICAgICAgICAgICAgIGNvbEFscGhhID0gMC44LAogICAgICAgICAgICAgICAgICBkcmF3Q29ubmVjdG9ycyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgIHdpZHRoQ29ubmVjdG9ycyA9IDAuNzUsCiAgICAgICAgICAgICAgICAgIHRpdGxlTGFiU2l6ZSA9IDgsICAgICAgIyB0aXRsZSBmb250CiAgICAgICAgICAgICAgICAgIHN1YnRpdGxlTGFiU2l6ZSA9IDAsICAgIyBzdWJ0aXRsZSBmb250CiAgICAgICAgICAgICAgICAgIGNhcHRpb25MYWJTaXplID0gNCwgICAgICMgY2FwdGlvbiBmb250CiAgICAgICAgICAgICAgICAgIGF4aXNMYWJTaXplID0gOCwgCiAgICAgICAgICAgICAgICAgIHlsaW0gPSBjKDAsNikpICsgdGhlbWUoCiAgICBsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpCn0pCgpsaWJyYXJ5KHBhdGNod29yaykKd3JhcF9wbG90cyh2b2xjYW5vX2xpc3QsIG5jb2wgPSA0KQpgYGAKCgojIDMuIEdlbmUgc2V0IGVucmljaG1lbnQgYW5hbHlzaXMgKEdTRUEpCkdTRUEgdGFrZXMgdGhlIHdob2xlIHJhbmtlZCBsaXN0IG9mIERFIGdlbmVzLCBzbyBuLnRvcC5nZW5lcyBwYXJhbWV0ZXIgaXMgbm90IHJlbGV2YW50IGhlcmUuCkJIIG1ldGhvZCB1c2VkIGZvciBtdWx0aXBsZSBjb21wYXJpc29uIGNvcnJlY3Rpb24uCmBgYHtyfQpjYW8kZXN0aW1hdGVPbnRvbG9neSh0eXBlPSJHU0VBIiwgb3JnLmRiPW9yZy5NbS5lZy5kYjo6b3JnLk1tLmVnLmRiKQpgYGAKCiMjIDMuMSBTYXZlIEdTRUEgcmVzdWx0IGFzIHRhYmxlClVzZSBhIGZ1bmN0aW9uIHRvIHNhdmUgdGVybXMgd2l0aCBwLmFkaiA8IDAuMDUgYXMgYSAudHN2IGZpbGUgYW5kIHJlYWQgaXQgaW50byBSLiBJdCBpcyBvbmUgaHVnZSB0YWJsZSB3aXRoIGFsbCBjZWxsIHR5cGVzIGFuZCBhbGwgMyBzdWJ0eXBlcyBvZiBnZW5lIG9udG9sb2dpZXMgKGJpb2xvZ2ljYWwgcHJvY2VzcyA9IEJQLCBtb2xlY3VsYXIgZnVuY3Rpb24gPSBNRiwgY2VsbHVsYXIgY29tcGFydG1lbnQgPSBDQykuCmBgYHtyfQpjYW8kc2F2ZU9udG9sb2d5QXNUYWJsZSgiR1NFQS50c3YiLCBuYW1lPSJHU0VBIikKR1NFQSA8LSByZWFkLnRhYmxlKCJHU0VBLnRzdiIsIHNlcD0iXHQiLCBoZWFkZXIgPSBUKQpgYGAKClRyYW5zZm9ybSBHU0VBIHRhYmxlIGludG8gYSBsaXN0IG9mIHRhYmxlcyAob25lIHRhYmxlIHBlciBjZWxsIHR5cGUpLgpgYGB7cn0KY2VsbF90eXBlcyA8LSB1bmlxdWUoR1NFQSRDZWxsVHlwZSkKR1NFQV9saXN0IDwtIGxhcHBseShjZWxsX3R5cGVzLCB7ZnVuY3Rpb24oeCkgR1NFQVtHU0VBJENlbGxUeXBlID09IHgsIF19KQpuYW1lcyhHU0VBX2xpc3QpIDwtIGNlbGxfdHlwZXMKYGBgCmBgYHtyLCBlY2hvPUZ9CiNyZW5hbWUgYSBjZWxsIHR5cGU6CiNuYW1lcyhHU0VBX2xpc3QpW25hbWVzKEdTRUFfbGlzdCkgPT0gJ1BvbHlJQ19lbnJpY2hlZF9pbW1hdHVyZV9wcmluY2lwYWxfbmV1cm9ucyddIDwtICdQSUNfaW1tYXR1cmVfcHJpbmNpcGFsX25ldScKYGBgCmBgYHtyfQp3cml0ZS54bHN4KHggPSBHU0VBX2xpc3QsIGZpbGU9ICJHU0VBLnhsc3giKQpgYGAKCkV4cGxhbmF0aW9uIG9mIHJlc3VsdCB0YWJsZQoKKiBEZXNjcmlwdGlvbjogR2VuZSBzZXQgbmFtZQoqIHNldFNpemU6IG51bWJlciBvZiBnZW5lcyB3aXRoIGdlbmUtbGV2ZWwgc3RhdGlzdGljIHZhbHVlcy4gVG8gYmUgbW9yZSBzcGVjaWZpYywgaWYgd2UgaW5wdXQgYSBwYXRod2F5IGdlbmUgc2V0IHdpdGggNTggZ2VuZXMgKGUuZy4gSEFMTE1BUktfTVlDX1RBUkdFVFNfVjIgKSwgd2hpbGUgb3VyIGdlbmUgbGlzdCBvbmx5IGhhdmUgNTQgb2YgaXQsIHNvIHRoZSByZXN1bHQgd2lsbCBvbmx5IHNob3cgc2V0U2l6ZSA9IDU0CiogZW5yaWNobWVudFNjb3JlOiBhbHNvIGNhbGxlZCBFUywgc2FtZSBhcyBpbiBCcm9hZCBHU0VBIGltcGxlbWVudGF0aW9uLiBJdCByZWZsZWN0cyB0aGUgZGVncmVlIHRvIHdoaWNoIGEgZ2VuZSBzZXQgaXMgb3ZlcnJlcHJlc2VudGVkIGF0IHRoZSB0b3Agb3IgYm90dG9tIG9mIGEgcmFua2VkIGxpc3Qgb2YgZ2VuZXMuCiogTkVTOiBub3JtYWxpemVkIGVucmljaG1lbnQgc2NvcmUgaXMgdGhlIHByaW1hcnkgc3RhdGlzdGljIGZvciBleGFtaW5pbmcgZ2VuZSBzZXQgZW5yaWNobWVudCByZXN1bHRzLiBCeSBub3JtYWxpemluZyB0aGUgZW5yaWNobWVudCBzY29yZSwgR1NFQSBhY2NvdW50cyBmb3IgZGlmZmVyZW5jZXMgaW4gZ2VuZSBzZXQgc2l6ZSBhbmQgaW4gY29ycmVsYXRpb25zIGJldHdlZW4gZ2VuZSBzZXRzIGFuZCB0aGUgZXhwcmVzc2lvbiBkYXRhc2V0LiBUaGVyZWZvcmUsIE5FUyBjYW4gYmUgdXNlZCB0byBjb21wYXJlIGFuYWx5c2lzIHJlc3VsdHMgYWNyb3NzIGdlbmUgc2V0cy4gQSBwb3NpdGl2ZSBub3JtYWxpemVkIGVucmljaG1lbnQgc2NvcmVzIChORVMpIHdpbGwgaW5kaWNhdGUgdGhhdCBnZW5lcyBpbiBzZXQgUyB3aWxsIGJlIG1vc3RseSByZXByZXNlbnRlZCBhdCB0aGUgdG9wIG9mIHlvdXIgbGlzdCAobG9nRkMgPiAwIG9yIHVwLXJlZ3VsYXRlZCBnZW5lcykuCiogcmFuazogVGhlIHBvc2l0aW9uIGluIHRoZSByYW5rZWQgbGlzdCBhdCB3aGljaCB0aGUgbWF4aW11bSBlbnJpY2htZW50IHNjb3JlIG9jY3VycmVkLiBJZiBnZW5lIHNldHMgYWNoaWV2ZSB0aGUgbWF4aW11bSBlbnJpY2htZW50IHNjb3JlIG5lYXIgdGhlIHRvcCBvciBib3R0b20gb2YgdGhlIHJhbmtlZCBsaXN0LCB0aGUgcmFuayBhdCBtYXggaXMgZWl0aGVyIHZlcnkgc21hbGwgb3IgdmVyeSBsYXJnZS4KKiBsZWFkaW5nX2VkZ2U6IGluY2x1ZGVzIHRocmVlIHN0YXRpc3RpY3MKICAqIHRhZ3M6IFRoZSBwZXJjZW50YWdlIG9mIGdlbmUgaGl0cyBiZWZvcmUgKGZvciBwb3NpdGl2ZSBFUykgb3IgYWZ0ZXIgKGZvciBuZWdhdGl2ZSBFUykgdGhlIHBlYWsgaW4gdGhlIHJ1bm5pbmcgZW5yaWNobWVudCBzY29yZSwgd2hpY2ggaW5kaWNhdGVzIHRoZSBwZXJjZW50YWdlIG9mIGdlbmVzIGNvbnRyaWJ1dGluZyB0byB0aGUgZW5yaWNobWVudCBzY29yZS4KICAqIGxpc3Q6IFRoZSBwZXJjZW50YWdlIG9mIGdlbmVzIGluIHRoZSByYW5rZWQgZ2VuZSBsaXN0IGJlZm9yZSAoZm9yIHBvc2l0aXZlIEVTKSBvciBhZnRlciAoZm9yIG5lZ2F0aXZlIEVTKSB0aGUgcGVhayBpbiB0aGUgcnVubmluZyBlbnJpY2htZW50IHNjb3JlLiBUaGlzIGdpdmVzIGFuIGluZGljYXRpb24gb2Ygd2hlcmUgaW4gdGhlIGxpc3QgdGhlIGVucmljaG1lbnQgc2NvcmUgaXMgYXR0YWluZWQuCiAgKiBzaWduYWw6IElmIHRoZSBnZW5lIHNldCBpcyBlbnRpcmVseSB3aXRoaW4gdGhlIGZpcnN0IE4gcG9zaXRpb25zIGluIHRoZSBsaXN0LCB0aGVuIHRoZSBzaWduYWwgc3RyZW5ndGggaXMgbWF4aW1hbCBvciAxMDAlLiBJZiB0aGUgZ2VuZSBzZXQgaXMgc3ByZWFkIHRocm91Z2hvdXQgdGhlIGxpc3QsIHRoZW4gdGhlIHNpZ25hbCBzdHJlbmd0aCBkZWNyZWFzZXMgdG93YXJkcyAwJS4KCiMjIDMuMiBWaXN1YWxpemF0aW9uIG9mIEdTRUEgcmVzdWx0cyBhcyBoZWF0bWFwCgpBcyBkZWZhdWx0IG9ubHkgR08gdGVybXMgb2YgQlAgKGJpb2xvZ2ljYWwgcHJvY2Vzcykgc3VidHlwZSBhcmUgcGxvdHRlZC4gU2ltaWxhciBHTyB0ZXJtcyBhcmUgY2x1c3RlcmVkIHRvZ2V0aGVyIHRvIHJlZHVjZSByZWR1bmRhbmN5LiBBcyBhIGRlZmF1bHQgd2UgcGxvdCB0aGUgdG9wIDIwIHVwLSBhbmQgZG93bnJlZ3VsYXRlZCBHTyB0ZXJtcy4KCmBgYHtyLCB3YXJuaW5nPUZ9CmNhbyRwbG90T250b2xvZ3lIZWF0bWFwKG5hbWU9IkdTRUEiLCBnZW5lcz0iZG93biIsIHN1YnR5cGU9IkJQIiwgdG9wLm4gPSAyMCwgY2x1c3RlciA9IEYpCmBgYAoKYGBge3IsIHdhcm5pbmc9Rn0KY2FvJHBsb3RPbnRvbG9neUhlYXRtYXAobmFtZT0iR1NFQSIsIGdlbmVzPSJ1cCIsIHN1YnR5cGU9IkJQIiwgdG9wLm4gPSAyMCwgY2x1c3RlciA9IEYpCmBgYAoKCmBgYHtyLCB3YXJuaW5nPUYsIGluY2x1ZGU9Rn0KIyBZb3UgY2FuIGFsc28gcGxvdCBvbmx5IHRoZSB0ZXJtcyBvZiBpbnRlcmVzdCBieSBmaWx0ZXJpbmcgZm9yIHNwZWNpZmljIHdvcmRzIHdpdGhpbiB0aGUgR08gdGVybXMuCiNjYW8kcGxvdE9udG9sb2d5SGVhdG1hcChuYW1lPSJHU0VBIiwgZ2VuZXM9ImRvd24iLCBzdWJ0eXBlPSAiQlAiLCB0b3AubiA9IDIwLCBkZXNjcmlwdGlvbi5yZWdleCA9ICJuZXVyb258ZGV2ZWxvcG1lbnR8Y3l0b2tpbmV8YmVoYXZpb3J8c3luYXBzZSIpCmBgYAoKYGBge3IsIGZpZy5oZWlnaHQ9Niwgd2FybmluZz1GLCBpbmNsdWRlPUZ9CiMgVGhlcmUgaXMgYSBmdW5jdGlvbiB0aGF0IGNvbGxhcHNlcyBvbnRvbG9naWVzIHdpdGggc2ltaWxhciBlbnJpY2hlZCBnZW5lcyBvciBzaW1pbGFyIGVucmljaG1lbnQgcGF0dGVybiBhbmQgdXNlcyB0aGUgNSBtb3N0IGZyZXF1ZW50IHdvcmRzIGFzIHRoZSBuYW1lIG9mIHRoZSBjbHVzdGVyLgojIGNhbyRwbG90T250b2xvZ3lIZWF0bWFwQ29sbGFwc2VkKG5hbWU9IkdTRUEiLCBnZW5lcz0iZG93biIsIG49MjAsIGNsdXN0Lm1ldGhvZD0id2FyZC5EIikgI2RvdWJsZSBjaGVjayBjbHVzdGVyIG1ldGhvZApgYGAKCg==