This is product offers a workflow to download fastq files an perform differential gene expession analyis using kallisto for alignment and DESeq to statistal analysis.


A few weeks ago I perfected software installation, so I will not demonstrate that here. Please see this notebook for more.

1 Kallisto Alignment

1.1 Getting sequencing reads

Sample SampleID
D-control D54
D-control D55
D-control D56
D-control D57
D-control D58
D-control D59
D-control M45
D-control M46
D-control M48
D-control M49
D-control M89
D-control M90
D-desiccation N48
D-desiccation N49
D-desiccation N50
D-desiccation N51
D-desiccation N52
D-desiccation N53
D-desiccation N54
D-desiccation N55
D-desiccation N56
D-desiccation N57
D-desiccation N58
D-desiccation N59

the files are located at https://gannet.fish.washington.edu/seashell/bu-github/nb-2023/Cgigas/data/nopp/

cd ../data 
wget --recursive --no-parent --no-directories \
--accept '*001.fastq.gz' \
https://gannet.fish.washington.edu/seashell/bu-github/nb-2023/Cgigas/data/nopp/
cd ../data 
/home/shared/FastQC/fastqc *fastq.gz -o ../output
eval "$(/opt/anaconda/anaconda3/bin/conda shell.bash hook)"
conda activate
which multiqc

cd ../output

multiqc .

MultiQC report available at https://gannet.fish.washington.edu/seashell/bu-github/steven-coursework/assignments/output/multiqc_report.html

1.2 Obtain the reference

cd ../data
curl -O https://gannet.fish.washington.edu/seashell/bu-github/nb-2023/Cgigas/data/rna.fna

1.3 Index Reference

/home/shared/kallisto/kallisto \
index -i \
../data/cgigas_roslin_rna.index \
../data/rna.fna

1.4 Align reads

mkdir ../output/kallisto_01

find ../data/*_L001_R1_001.fastq.gz \
| xargs basename -s _L001_R1_001.fastq.gz | xargs -I{} /home/shared/kallisto/kallisto \
quant -i ../data/cgigas_roslin_rna.index \
-o ../output/kallisto_01/{} \
-t 40 \
--single -l 100 -s 10 ../data/{}_L001_R1_001.fastq.gz

1.5 Merge quant data

perl /home/shared/trinityrnaseq-v2.12.0/util/abundance_estimates_to_matrix.pl \
--est_method kallisto \
    --gene_trans_map none \
    --out_prefix ../output/kallisto_01 \
    --name_sample_by_basedir \
    ../output/kallisto_01/D54_S145/abundance.tsv \
    ../output/kallisto_01/D56_S136/abundance.tsv \
    ../output/kallisto_01/D58_S144/abundance.tsv \
    ../output/kallisto_01/M45_S140/abundance.tsv \
    ../output/kallisto_01/M48_S137/abundance.tsv \
    ../output/kallisto_01/M89_S138/abundance.tsv \
    ../output/kallisto_01/D55_S146/abundance.tsv \
    ../output/kallisto_01/D57_S143/abundance.tsv \
    ../output/kallisto_01/D59_S142/abundance.tsv \
    ../output/kallisto_01/M46_S141/abundance.tsv \
    ../output/kallisto_01/M49_S139/abundance.tsv \
    ../output/kallisto_01/M90_S147/abundance.tsv \
    ../output/kallisto_01/N48_S194/abundance.tsv \
    ../output/kallisto_01/N50_S187/abundance.tsv \
    ../output/kallisto_01/N52_S184/abundance.tsv \
    ../output/kallisto_01/N54_S193/abundance.tsv \
    ../output/kallisto_01/N56_S192/abundance.tsv \
    ../output/kallisto_01/N58_S195/abundance.tsv \
    ../output/kallisto_01/N49_S185/abundance.tsv \
    ../output/kallisto_01/N51_S186/abundance.tsv \
    ../output/kallisto_01/N53_S188/abundance.tsv \
    ../output/kallisto_01/N55_S190/abundance.tsv \
    ../output/kallisto_01/N57_S191/abundance.tsv \
    ../output/kallisto_01/N59_S189/abundance.tsv

2 DESeq2

countmatrix <- read.delim("../output/kallisto_01.isoform.counts.matrix", header = TRUE, sep = '\t')
rownames(countmatrix) <- countmatrix$X
countmatrix <- countmatrix[,-1]
head(countmatrix)
##                D54_S145 D56_S136 D58_S144 M45_S140 M48_S137 M89_S138 D55_S146
## XM_011416156.3        0        0        0        0        0   0.0000        0
## XM_034446348.1        0        0        2        0        0   0.0000        0
## XM_034459753.1        0        0        0        0        0   0.0000        0
## XR_004602598.1        0        0        0        0        0   3.3511        0
## XM_034463888.1        0        0        0        0        0   0.0000        0
## XR_004601019.1        0        1        0        0        0   0.0000        4
##                D57_S143 D59_S142 M46_S141 M49_S139 M90_S147 N48_S194 N50_S187
## XM_011416156.3        0        0        0        0        0        0        0
## XM_034446348.1        0        0        1        0        0        1        0
## XM_034459753.1        0        0        0        0        0        0        0
## XR_004602598.1        0        0        0        0        0        0        0
## XM_034463888.1        0        0        0        0        0        0        0
## XR_004601019.1        0        0        0        0        0        0        0
##                N52_S184 N54_S193 N56_S192 N58_S195 N49_S185 N51_S186 N53_S188
## XM_011416156.3  0.00000  0.00000        0        0        0        0  0.00000
## XM_034446348.1  0.00000  1.00000        0        0        0        0  0.00000
## XM_034459753.1  0.00000  0.00000        0        0        0        0  0.00000
## XR_004602598.1  5.98477  3.27488        0        0        0        0  1.15991
## XM_034463888.1  0.00000  0.00000        0        0        0        0  0.00000
## XR_004601019.1  2.00000  0.00000        0        0        0        0  0.00000
##                N55_S190 N57_S191 N59_S189
## XM_011416156.3        0        0   0.0000
## XM_034446348.1        0        0   0.0000
## XM_034459753.1        0        0  12.9937
## XR_004602598.1        0        0   0.0000
## XM_034463888.1        0        0   0.0000
## XR_004601019.1        0        0   0.0000
countmatrix <- round(countmatrix, 0)
str(countmatrix)
## 'data.frame':    73307 obs. of  24 variables:
##  $ D54_S145: num  0 0 0 0 0 0 0 2 0 0 ...
##  $ D56_S136: num  0 0 0 0 0 1 0 2 0 1 ...
##  $ D58_S144: num  0 2 0 0 0 0 0 5 0 0 ...
##  $ M45_S140: num  0 0 0 0 0 0 0 4 1 1 ...
##  $ M48_S137: num  0 0 0 0 0 0 9 5 1 0 ...
##  $ M89_S138: num  0 0 0 3 0 0 0 16 1 1 ...
##  $ D55_S146: num  0 0 0 0 0 4 0 7 1 1 ...
##  $ D57_S143: num  0 0 0 0 0 0 5 5 0 0 ...
##  $ D59_S142: num  0 0 0 0 0 0 0 6 1 0 ...
##  $ M46_S141: num  0 1 0 0 0 0 0 9 0 1 ...
##  $ M49_S139: num  0 0 0 0 0 0 0 7 1 0 ...
##  $ M90_S147: num  0 0 0 0 0 0 0 5 0 1 ...
##  $ N48_S194: num  0 1 0 0 0 0 0 4 1 1 ...
##  $ N50_S187: num  0 0 0 0 0 0 0 6 0 1 ...
##  $ N52_S184: num  0 0 0 6 0 2 2 4 0 0 ...
##  $ N54_S193: num  0 1 0 3 0 0 0 12 0 1 ...
##  $ N56_S192: num  0 0 0 0 0 0 0 2 0 0 ...
##  $ N58_S195: num  0 0 0 0 0 0 0 8 0 0 ...
##  $ N49_S185: num  0 0 0 0 0 0 2 13 0 1 ...
##  $ N51_S186: num  0 0 0 0 0 0 0 7 0 0 ...
##  $ N53_S188: num  0 0 0 1 0 0 0 2 0 2 ...
##  $ N55_S190: num  0 0 0 0 0 0 0 7 0 1 ...
##  $ N57_S191: num  0 0 0 0 0 0 0 2 0 0 ...
##  $ N59_S189: num  0 0 13 0 0 0 0 5 0 0 ...
dim(countmatrix)
dim(deseq2.colData)
length(colnames(data))
deseq2.colData <- data.frame(condition=factor(c(rep("control", 12), rep("desicated", 12))), 
                             type=factor(rep("single-read", 24)))
rownames(deseq2.colData) <- colnames(data)
deseq2.dds <- DESeqDataSetFromMatrix(countData = countmatrix,
                                     colData = deseq2.colData, 
                                     design = ~ condition)
deseq2.dds <- DESeq(deseq2.dds)
deseq2.res <- results(deseq2.dds)
deseq2.res <- deseq2.res[order(rownames(deseq2.res)), ]
vsd <- vst(deseq2.dds, blind = FALSE)
plotPCA(vsd, intgroup = "condition")

# Select top 50 differentially expressed genes
res <- results(deseq2.dds)
res_ordered <- res[order(res$padj), ]
top_genes <- row.names(res_ordered)[1:50]

# Extract counts and normalize
counts <- counts(deseq2.dds, normalized = TRUE)
counts_top <- counts[top_genes, ]

# Log-transform counts
log_counts_top <- log2(counts_top + 1)

# Generate heatmap
pheatmap(log_counts_top, scale = "row")

head(deseq2.res)
## log2 fold change (MLE): condition desicated vs control 
## Wald test p-value: condition desicated vs control 
## DataFrame with 6 rows and 6 columns
##                  baseMean log2FoldChange     lfcSE      stat      pvalue
##                 <numeric>      <numeric> <numeric> <numeric>   <numeric>
## NM_001305288.1   0.180896      1.0376053  3.002647  0.345564 7.29671e-01
## NM_001305289.1   0.883924     -2.8199991  1.067976 -2.640509 8.27816e-03
## NM_001305290.1 145.563666      0.4576780  0.116258  3.936753 8.25915e-05
## NM_001305291.1   0.263385      0.5540863  1.585370  0.349500 7.26714e-01
## NM_001305292.1   2.903611     -1.2204901  0.763903 -1.597703 1.10109e-01
## NM_001305293.1 233.891565      0.0644989  0.131819  0.489298 6.24631e-01
##                      padj
##                 <numeric>
## NM_001305288.1         NA
## NM_001305289.1         NA
## NM_001305290.1 0.00956885
## NM_001305291.1         NA
## NM_001305292.1 0.58676490
## NM_001305293.1 0.95304181
# Count number of hits with adjusted p-value less then 0.05
dim(deseq2.res[!is.na(deseq2.res$padj) & deseq2.res$padj <= 0.05, ])
## [1] 609   6
tmp <- deseq2.res
# The main plot
plot(tmp$baseMean, tmp$log2FoldChange, pch=20, cex=0.45, ylim=c(-3, 3), log="x", col="darkgray",
     main="DEG Dessication  (pval <= 0.05)",
     xlab="mean of normalized counts",
     ylab="Log2 Fold Change")
# Getting the significant points and plotting them again so they're a different color
tmp.sig <- deseq2.res[!is.na(deseq2.res$padj) & deseq2.res$padj <= 0.05, ]
points(tmp.sig$baseMean, tmp.sig$log2FoldChange, pch=20, cex=0.45, col="red")
# 2 FC lines
abline(h=c(-1,1), col="blue")

# Prepare the data for plotting
res_df <- as.data.frame(deseq2.res)
res_df$gene <- row.names(res_df)

# Create volcano plot
volcano_plot <- ggplot(res_df, aes(x = log2FoldChange, y = -log10(padj), color = padj < 0.05)) +
  geom_point(alpha = 0.6, size = 1.5) +
  scale_color_manual(values = c("grey", "red")) +
  labs(title = "Volcano Plot",
       x = "Log2 Fold Change",
       y = "-Log10 Adjusted P-value",
       color = "Significantly\nDifferentially Expressed") +
  theme_minimal() +
  theme(panel.grid.major = element_blank(),
        panel.grid.minor = element_blank(),
        legend.position = "top")

print(volcano_plot)

write.table(tmp.sig, "../output/DEGlist.tab", sep = '\t', row.names = T)
deglist <- read.csv("../output/DEGlist.tab", sep = '\t', header = TRUE)
deglist$RowName <- rownames(deglist)
deglist2 <- deglist[, c("RowName", "pvalue")] # Optionally, reorder the columns
datatable(deglist)

3 Annotation?

cg_sp <- read.csv("https://raw.githubusercontent.com/sr320/nb-2022/main/C_gigas/analyses/CgR-blastp-sp.tab", header = FALSE, sep="\t")  %>%
  distinct(V1, .keep_all = TRUE)
loc <- read.csv("https://raw.githubusercontent.com/sr320/nb-2022/main/C_gigas/analyses/LOC_Acc.tab", sep = " ", header = FALSE)
comb <- left_join(loc, cg_sp, by = c("V2" = "V1")) %>%
  left_join(deglist, by = c("V1" = "RowName"))

4 Gene Enrichment Analysis

gene_deg_status <- res_df %>%
  mutate(degstaus = ifelse(padj < 0.05, 1, 0)) 
# Read the FASTA file
fasta_data <- readDNAStringSet("../data/rna.fna")

# Calculate gene lengths
gene_lengths <- width(fasta_data)


# Extract gene names/IDs from sequence IDs
gene_names <- sapply(names(fasta_data), function(x) strsplit(x, " ")[[1]][1])

# Create a data frame with gene IDs and lengths
gene_lengths_df <- data.frame(geneID = gene_names, length = gene_lengths)

4.1 Need GO Mappings

pwf <- nullp(gene_data, bias.data = gene_lengths)

GO_analysis <- goseq(pwf, gene2cat = "your_organism_GO_mapping")
LS0tCnRpdGxlOiAiUk5BLXNlcSIKYXV0aG9yOiBTdGV2ZW4gUm9iZXJ0cwpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiICAKb3V0cHV0OiAKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IHJlYWRhYmxlCiAgICBoaWdobGlnaHQ6IHplbmJ1cm4KICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShrYWJsZUV4dHJhKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShwaGVhdG1hcCkKbGlicmFyeShSQ29sb3JCcmV3ZXIpCmxpYnJhcnkoZGF0YS50YWJsZSkKbGlicmFyeShEVCkKbGlicmFyeShCaW9zdHJpbmdzKQprbml0cjo6b3B0c19jaHVuayRzZXQoCiAgZWNobyA9IFRSVUUsICAgICAgICAgIyBEaXNwbGF5IGNvZGUgY2h1bmtzCiAgZXZhbCA9IEZBTFNFLCAgICAgICAgICMgRXZhbHVhdGUgY29kZSBjaHVua3MKICB3YXJuaW5nID0gRkFMU0UsICAgICAjIEhpZGUgd2FybmluZ3MKICBtZXNzYWdlID0gRkFMU0UsICAgICAjIEhpZGUgbWVzc2FnZXMKICBmaWcud2lkdGggPSA2LCAgICAgICAjIFNldCBwbG90IHdpZHRoIGluIGluY2hlcwogIGZpZy5oZWlnaHQgPSA0LCAgICAgICMgU2V0IHBsb3QgaGVpZ2h0IGluIGluY2hlcwogIGZpZy5hbGlnbiA9ICJjZW50ZXIiICMgQWxpZ24gcGxvdHMgdG8gdGhlIGNlbnRlcgopCmBgYAoKVGhpcyBpcyBwcm9kdWN0IG9mZmVycyBhIHdvcmtmbG93IHRvIGRvd25sb2FkIGZhc3RxIGZpbGVzIGFuIHBlcmZvcm0gZGlmZmVyZW50aWFsIGdlbmUgZXhwZXNzaW9uIGFuYWx5aXMgdXNpbmcga2FsbGlzdG8gZm9yIGFsaWdubWVudCBhbmQgREVTZXEgdG8gc3RhdGlzdGFsIGFuYWx5c2lzLiAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKQSBmZXcgd2Vla3MgYWdvIEkgcGVyZmVjdGVkIHNvZnR3YXJlIGluc3RhbGxhdGlvbiwgc28gSSB3aWxsIG5vdCBkZW1vbnN0cmF0ZSB0aGF0IGhlcmUuIFBsZWFzZSBzZWUgdGhpcyBub3RlYm9vayBmb3IgbW9yZS4KCiMgS2FsbGlzdG8gQWxpZ25tZW50CiMjIEdldHRpbmcgc2VxdWVuY2luZyByZWFkcwoKfCAgICAgICAgICAgICAgIHwgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS18CnwgU2FtcGxlICAgICAgICB8IFNhbXBsZUlEIHwKfCBELWNvbnRyb2wgICAgIHwgRDU0ICAgICAgfAp8IEQtY29udHJvbCAgICAgfCBENTUgICAgICB8CnwgRC1jb250cm9sICAgICB8IEQ1NiAgICAgIHwKfCBELWNvbnRyb2wgICAgIHwgRDU3ICAgICAgfAp8IEQtY29udHJvbCAgICAgfCBENTggICAgICB8CnwgRC1jb250cm9sICAgICB8IEQ1OSAgICAgIHwKfCBELWNvbnRyb2wgICAgIHwgTTQ1ICAgICAgfAp8IEQtY29udHJvbCAgICAgfCBNNDYgICAgICB8CnwgRC1jb250cm9sICAgICB8IE00OCAgICAgIHwKfCBELWNvbnRyb2wgICAgIHwgTTQ5ICAgICAgfAp8IEQtY29udHJvbCAgICAgfCBNODkgICAgICB8CnwgRC1jb250cm9sICAgICB8IE05MCAgICAgIHwKfCBELWRlc2ljY2F0aW9uIHwgTjQ4ICAgICAgfAp8IEQtZGVzaWNjYXRpb24gfCBONDkgICAgICB8CnwgRC1kZXNpY2NhdGlvbiB8IE41MCAgICAgIHwKfCBELWRlc2ljY2F0aW9uIHwgTjUxICAgICAgfAp8IEQtZGVzaWNjYXRpb24gfCBONTIgICAgICB8CnwgRC1kZXNpY2NhdGlvbiB8IE41MyAgICAgIHwKfCBELWRlc2ljY2F0aW9uIHwgTjU0ICAgICAgfAp8IEQtZGVzaWNjYXRpb24gfCBONTUgICAgICB8CnwgRC1kZXNpY2NhdGlvbiB8IE41NiAgICAgIHwKfCBELWRlc2ljY2F0aW9uIHwgTjU3ICAgICAgfAp8IEQtZGVzaWNjYXRpb24gfCBONTggICAgICB8CnwgRC1kZXNpY2NhdGlvbiB8IE41OSAgICAgIHwKCgp0aGUgZmlsZXMgYXJlIGxvY2F0ZWQgYXQgaHR0cHM6Ly9nYW5uZXQuZmlzaC53YXNoaW5ndG9uLmVkdS9zZWFzaGVsbC9idS1naXRodWIvbmItMjAyMy9DZ2lnYXMvZGF0YS9ub3BwLwoKCgpgYGB7ciBwdWxsLCBlbmdpbmU9J2Jhc2gnfQpjZCAuLi9kYXRhIAp3Z2V0IC0tcmVjdXJzaXZlIC0tbm8tcGFyZW50IC0tbm8tZGlyZWN0b3JpZXMgXAotLWFjY2VwdCAnKjAwMS5mYXN0cS5neicgXApodHRwczovL2dhbm5ldC5maXNoLndhc2hpbmd0b24uZWR1L3NlYXNoZWxsL2J1LWdpdGh1Yi9uYi0yMDIzL0NnaWdhcy9kYXRhL25vcHAvCmBgYAoKCgpgYGB7ciBmYXN0cWMsIGVuZ2luZT0nYmFzaCd9CmNkIC4uL2RhdGEgCi9ob21lL3NoYXJlZC9GYXN0UUMvZmFzdHFjICpmYXN0cS5neiAtbyAuLi9vdXRwdXQKYGBgCgpgYGB7YmFzaH0KZXZhbCAiJCgvb3B0L2FuYWNvbmRhL2FuYWNvbmRhMy9iaW4vY29uZGEgc2hlbGwuYmFzaCBob29rKSIKY29uZGEgYWN0aXZhdGUKd2hpY2ggbXVsdGlxYwoKY2QgLi4vb3V0cHV0CgptdWx0aXFjIC4KYGBgCgoKCk11bHRpUUMgcmVwb3J0IGF2YWlsYWJsZSBhdApodHRwczovL2dhbm5ldC5maXNoLndhc2hpbmd0b24uZWR1L3NlYXNoZWxsL2J1LWdpdGh1Yi9zdGV2ZW4tY291cnNld29yay9hc3NpZ25tZW50cy9vdXRwdXQvbXVsdGlxY19yZXBvcnQuaHRtbAoKCjxpZnJhbWUgc3JjPSJodHRwczovL2dhbm5ldC5maXNoLndhc2hpbmd0b24uZWR1L3NlYXNoZWxsL2J1LWdpdGh1Yi9zdGV2ZW4tY291cnNld29yay9hc3NpZ25tZW50cy9vdXRwdXQvbXVsdGlxY19yZXBvcnQuaHRtbCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iODAwcHgiIGZyYW1lYm9yZGVyPSIwIj48L2lmcmFtZT4KCiMjIE9idGFpbiB0aGUgcmVmZXJlbmNlCgpgYGB7ciBwdWxscmVmLCBlbmdpbmU9J2Jhc2gnfQpjZCAuLi9kYXRhCmN1cmwgLU8gaHR0cHM6Ly9nYW5uZXQuZmlzaC53YXNoaW5ndG9uLmVkdS9zZWFzaGVsbC9idS1naXRodWIvbmItMjAyMy9DZ2lnYXMvZGF0YS9ybmEuZm5hCmBgYAoKCiMjIEluZGV4IFJlZmVyZW5jZQoKYGBge3Iga2FsbGlzdG8sIGVuZ2luZT0nYmFzaCd9Ci9ob21lL3NoYXJlZC9rYWxsaXN0by9rYWxsaXN0byBcCmluZGV4IC1pIFwKLi4vZGF0YS9jZ2lnYXNfcm9zbGluX3JuYS5pbmRleCBcCi4uL2RhdGEvcm5hLmZuYQpgYGAKCiMjIEFsaWduIHJlYWRzCgpgYGB7ciBrYWxsaXN0by1hbGlnbiwgZW5naW5lPSdiYXNoJ30KbWtkaXIgLi4vb3V0cHV0L2thbGxpc3RvXzAxCgpmaW5kIC4uL2RhdGEvKl9MMDAxX1IxXzAwMS5mYXN0cS5neiBcCnwgeGFyZ3MgYmFzZW5hbWUgLXMgX0wwMDFfUjFfMDAxLmZhc3RxLmd6IHwgeGFyZ3MgLUl7fSAvaG9tZS9zaGFyZWQva2FsbGlzdG8va2FsbGlzdG8gXApxdWFudCAtaSAuLi9kYXRhL2NnaWdhc19yb3NsaW5fcm5hLmluZGV4IFwKLW8gLi4vb3V0cHV0L2thbGxpc3RvXzAxL3t9IFwKLXQgNDAgXAotLXNpbmdsZSAtbCAxMDAgLXMgMTAgLi4vZGF0YS97fV9MMDAxX1IxXzAwMS5mYXN0cS5negpgYGAKIyMgTWVyZ2UgcXVhbnQgZGF0YQoKYGBge3IgbWF0cml4LCBlbmdpbmU9J2Jhc2gnfQpwZXJsIC9ob21lL3NoYXJlZC90cmluaXR5cm5hc2VxLXYyLjEyLjAvdXRpbC9hYnVuZGFuY2VfZXN0aW1hdGVzX3RvX21hdHJpeC5wbCBcCi0tZXN0X21ldGhvZCBrYWxsaXN0byBcCiAgICAtLWdlbmVfdHJhbnNfbWFwIG5vbmUgXAogICAgLS1vdXRfcHJlZml4IC4uL291dHB1dC9rYWxsaXN0b18wMSBcCiAgICAtLW5hbWVfc2FtcGxlX2J5X2Jhc2VkaXIgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL0Q1NF9TMTQ1L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL0Q1Nl9TMTM2L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL0Q1OF9TMTQ0L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL000NV9TMTQwL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL000OF9TMTM3L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL004OV9TMTM4L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL0Q1NV9TMTQ2L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL0Q1N19TMTQzL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL0Q1OV9TMTQyL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL000Nl9TMTQxL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL000OV9TMTM5L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL005MF9TMTQ3L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL040OF9TMTk0L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041MF9TMTg3L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041Ml9TMTg0L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041NF9TMTkzL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041Nl9TMTkyL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041OF9TMTk1L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL040OV9TMTg1L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041MV9TMTg2L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041M19TMTg4L2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041NV9TMTkwL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041N19TMTkxL2FidW5kYW5jZS50c3YgXAogICAgLi4vb3V0cHV0L2thbGxpc3RvXzAxL041OV9TMTg5L2FidW5kYW5jZS50c3YKYGBgCgoKCgotLS0gCgojIERFU2VxMgoKCgpgYGB7ciwgZXZhbD1UUlVFfQpjb3VudG1hdHJpeCA8LSByZWFkLmRlbGltKCIuLi9vdXRwdXQva2FsbGlzdG9fMDEuaXNvZm9ybS5jb3VudHMubWF0cml4IiwgaGVhZGVyID0gVFJVRSwgc2VwID0gJ1x0JykKcm93bmFtZXMoY291bnRtYXRyaXgpIDwtIGNvdW50bWF0cml4JFgKY291bnRtYXRyaXggPC0gY291bnRtYXRyaXhbLC0xXQpoZWFkKGNvdW50bWF0cml4KQpgYGAKCmBgYHtyLGV2YWw9VFJVRX0KY291bnRtYXRyaXggPC0gcm91bmQoY291bnRtYXRyaXgsIDApCnN0cihjb3VudG1hdHJpeCkKYGBgCgoKYGBge3J9CmRpbShjb3VudG1hdHJpeCkKZGltKGRlc2VxMi5jb2xEYXRhKQpgYGAKYGBge3J9Cmxlbmd0aChjb2xuYW1lcyhkYXRhKSkKYGBgCgoKYGBge3IsIGV2YWw9VFJVRX0KZGVzZXEyLmNvbERhdGEgPC0gZGF0YS5mcmFtZShjb25kaXRpb249ZmFjdG9yKGMocmVwKCJjb250cm9sIiwgMTIpLCByZXAoImRlc2ljYXRlZCIsIDEyKSkpLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0eXBlPWZhY3RvcihyZXAoInNpbmdsZS1yZWFkIiwgMjQpKSkKcm93bmFtZXMoZGVzZXEyLmNvbERhdGEpIDwtIGNvbG5hbWVzKGRhdGEpCmRlc2VxMi5kZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjb3VudG1hdHJpeCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbERhdGEgPSBkZXNlcTIuY29sRGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkZXNpZ24gPSB+IGNvbmRpdGlvbikKYGBgCgoKCmBgYHtyLCBldmFsPVRSVUUsIGNhY2hlPVRSVUV9CmRlc2VxMi5kZHMgPC0gREVTZXEoZGVzZXEyLmRkcykKZGVzZXEyLnJlcyA8LSByZXN1bHRzKGRlc2VxMi5kZHMpCmRlc2VxMi5yZXMgPC0gZGVzZXEyLnJlc1tvcmRlcihyb3duYW1lcyhkZXNlcTIucmVzKSksIF0KYGBgCgpgYGB7ciBQQ0EsIGV2YWw9VFJVRX0KdnNkIDwtIHZzdChkZXNlcTIuZGRzLCBibGluZCA9IEZBTFNFKQpwbG90UENBKHZzZCwgaW50Z3JvdXAgPSAiY29uZGl0aW9uIikKYGBgCgpgYGB7ciBoZWF0bWFwLCBldmFsPVRSVUV9CiMgU2VsZWN0IHRvcCA1MCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMKcmVzIDwtIHJlc3VsdHMoZGVzZXEyLmRkcykKcmVzX29yZGVyZWQgPC0gcmVzW29yZGVyKHJlcyRwYWRqKSwgXQp0b3BfZ2VuZXMgPC0gcm93Lm5hbWVzKHJlc19vcmRlcmVkKVsxOjUwXQoKIyBFeHRyYWN0IGNvdW50cyBhbmQgbm9ybWFsaXplCmNvdW50cyA8LSBjb3VudHMoZGVzZXEyLmRkcywgbm9ybWFsaXplZCA9IFRSVUUpCmNvdW50c190b3AgPC0gY291bnRzW3RvcF9nZW5lcywgXQoKIyBMb2ctdHJhbnNmb3JtIGNvdW50cwpsb2dfY291bnRzX3RvcCA8LSBsb2cyKGNvdW50c190b3AgKyAxKQoKIyBHZW5lcmF0ZSBoZWF0bWFwCnBoZWF0bWFwKGxvZ19jb3VudHNfdG9wLCBzY2FsZSA9ICJyb3ciKQpgYGAKCgpgYGB7ciwgZXZhbD1UUlVFfQpoZWFkKGRlc2VxMi5yZXMpCmBgYAoKCgpgYGB7ciwgZXZhbD1UUlVFfQojIENvdW50IG51bWJlciBvZiBoaXRzIHdpdGggYWRqdXN0ZWQgcC12YWx1ZSBsZXNzIHRoZW4gMC4wNQpkaW0oZGVzZXEyLnJlc1shaXMubmEoZGVzZXEyLnJlcyRwYWRqKSAmIGRlc2VxMi5yZXMkcGFkaiA8PSAwLjA1LCBdKQpgYGAKCgpgYGB7ciwgZXZhbD1UUlVFfQp0bXAgPC0gZGVzZXEyLnJlcwojIFRoZSBtYWluIHBsb3QKcGxvdCh0bXAkYmFzZU1lYW4sIHRtcCRsb2cyRm9sZENoYW5nZSwgcGNoPTIwLCBjZXg9MC40NSwgeWxpbT1jKC0zLCAzKSwgbG9nPSJ4IiwgY29sPSJkYXJrZ3JheSIsCiAgICAgbWFpbj0iREVHIERlc3NpY2F0aW9uICAocHZhbCA8PSAwLjA1KSIsCiAgICAgeGxhYj0ibWVhbiBvZiBub3JtYWxpemVkIGNvdW50cyIsCiAgICAgeWxhYj0iTG9nMiBGb2xkIENoYW5nZSIpCiMgR2V0dGluZyB0aGUgc2lnbmlmaWNhbnQgcG9pbnRzIGFuZCBwbG90dGluZyB0aGVtIGFnYWluIHNvIHRoZXkncmUgYSBkaWZmZXJlbnQgY29sb3IKdG1wLnNpZyA8LSBkZXNlcTIucmVzWyFpcy5uYShkZXNlcTIucmVzJHBhZGopICYgZGVzZXEyLnJlcyRwYWRqIDw9IDAuMDUsIF0KcG9pbnRzKHRtcC5zaWckYmFzZU1lYW4sIHRtcC5zaWckbG9nMkZvbGRDaGFuZ2UsIHBjaD0yMCwgY2V4PTAuNDUsIGNvbD0icmVkIikKIyAyIEZDIGxpbmVzCmFibGluZShoPWMoLTEsMSksIGNvbD0iYmx1ZSIpCmBgYAoKYGBge3IgbmV3cGxvdCwgZXZhbD1UUlVFfQojIFByZXBhcmUgdGhlIGRhdGEgZm9yIHBsb3R0aW5nCnJlc19kZiA8LSBhcy5kYXRhLmZyYW1lKGRlc2VxMi5yZXMpCnJlc19kZiRnZW5lIDwtIHJvdy5uYW1lcyhyZXNfZGYpCgojIENyZWF0ZSB2b2xjYW5vIHBsb3QKdm9sY2Fub19wbG90IDwtIGdncGxvdChyZXNfZGYsIGFlcyh4ID0gbG9nMkZvbGRDaGFuZ2UsIHkgPSAtbG9nMTAocGFkaiksIGNvbG9yID0gcGFkaiA8IDAuMDUpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNiwgc2l6ZSA9IDEuNSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXMgPSBjKCJncmV5IiwgInJlZCIpKSArCiAgbGFicyh0aXRsZSA9ICJWb2xjYW5vIFBsb3QiLAogICAgICAgeCA9ICJMb2cyIEZvbGQgQ2hhbmdlIiwKICAgICAgIHkgPSAiLUxvZzEwIEFkanVzdGVkIFAtdmFsdWUiLAogICAgICAgY29sb3IgPSAiU2lnbmlmaWNhbnRseVxuRGlmZmVyZW50aWFsbHkgRXhwcmVzc2VkIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciA9IGVsZW1lbnRfYmxhbmsoKSwKICAgICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpLAogICAgICAgIGxlZ2VuZC5wb3NpdGlvbiA9ICJ0b3AiKQoKcHJpbnQodm9sY2Fub19wbG90KQpgYGAKCgoKYGBge3IsIGV2YWw9VFJVRX0Kd3JpdGUudGFibGUodG1wLnNpZywgIi4uL291dHB1dC9ERUdsaXN0LnRhYiIsIHNlcCA9ICdcdCcsIHJvdy5uYW1lcyA9IFQpCmBgYAoKYGBge3IsIGV2YWw9VFJVRX0KZGVnbGlzdCA8LSByZWFkLmNzdigiLi4vb3V0cHV0L0RFR2xpc3QudGFiIiwgc2VwID0gJ1x0JywgaGVhZGVyID0gVFJVRSkKZGVnbGlzdCRSb3dOYW1lIDwtIHJvd25hbWVzKGRlZ2xpc3QpCmRlZ2xpc3QyIDwtIGRlZ2xpc3RbLCBjKCJSb3dOYW1lIiwgInB2YWx1ZSIpXSAjIE9wdGlvbmFsbHksIHJlb3JkZXIgdGhlIGNvbHVtbnMKYGBgCgoKYGBge3IsIGV2YWw9VFJVRX0KZGF0YXRhYmxlKGRlZ2xpc3QpCmBgYAoKCiMgQW5ub3RhdGlvbj8KYGBge3J9CmNnX3NwIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3IzMjAvbmItMjAyMi9tYWluL0NfZ2lnYXMvYW5hbHlzZXMvQ2dSLWJsYXN0cC1zcC50YWIiLCBoZWFkZXIgPSBGQUxTRSwgc2VwPSJcdCIpICAlPiUKICBkaXN0aW5jdChWMSwgLmtlZXBfYWxsID0gVFJVRSkKYGBgCgpgYGB7cn0KbG9jIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3IzMjAvbmItMjAyMi9tYWluL0NfZ2lnYXMvYW5hbHlzZXMvTE9DX0FjYy50YWIiLCBzZXAgPSAiICIsIGhlYWRlciA9IEZBTFNFKQpgYGAKCmBgYHtyfQpjb21iIDwtIGxlZnRfam9pbihsb2MsIGNnX3NwLCBieSA9IGMoIlYyIiA9ICJWMSIpKSAlPiUKICBsZWZ0X2pvaW4oZGVnbGlzdCwgYnkgPSBjKCJWMSIgPSAiUm93TmFtZSIpKQpgYGAKCgoKIyBHZW5lIEVucmljaG1lbnQgQW5hbHlzaXMKCmBgYHtyfQpnZW5lX2RlZ19zdGF0dXMgPC0gcmVzX2RmICU+JQogIG11dGF0ZShkZWdzdGF1cyA9IGlmZWxzZShwYWRqIDwgMC4wNSwgMSwgMCkpIApgYGAKCmBgYHtyfQojIFJlYWQgdGhlIEZBU1RBIGZpbGUKZmFzdGFfZGF0YSA8LSByZWFkRE5BU3RyaW5nU2V0KCIuLi9kYXRhL3JuYS5mbmEiKQoKIyBDYWxjdWxhdGUgZ2VuZSBsZW5ndGhzCmdlbmVfbGVuZ3RocyA8LSB3aWR0aChmYXN0YV9kYXRhKQoKCiMgRXh0cmFjdCBnZW5lIG5hbWVzL0lEcyBmcm9tIHNlcXVlbmNlIElEcwpnZW5lX25hbWVzIDwtIHNhcHBseShuYW1lcyhmYXN0YV9kYXRhKSwgZnVuY3Rpb24oeCkgc3Ryc3BsaXQoeCwgIiAiKVtbMV1dWzFdKQoKIyBDcmVhdGUgYSBkYXRhIGZyYW1lIHdpdGggZ2VuZSBJRHMgYW5kIGxlbmd0aHMKZ2VuZV9sZW5ndGhzX2RmIDwtIGRhdGEuZnJhbWUoZ2VuZUlEID0gZ2VuZV9uYW1lcywgbGVuZ3RoID0gZ2VuZV9sZW5ndGhzKQoKYGBgCgoKIyMgIE5lZWQgR08gTWFwcGluZ3MgCgoKCgpgYGB7cn0KcHdmIDwtIG51bGxwKGdlbmVfZGF0YSwgYmlhcy5kYXRhID0gZ2VuZV9sZW5ndGhzKQoKR09fYW5hbHlzaXMgPC0gZ29zZXEocHdmLCBnZW5lMmNhdCA9ICJ5b3VyX29yZ2FuaXNtX0dPX21hcHBpbmciKQoKYGBgCgo=