Set Bash variables
{
echo "#### Assign Variables ####"
echo ""
echo "# Data directories"
echo 'export timeseries_dir=/home/shared/8TB_HDD_02/shedurkin/timeseries_molecular'
echo 'export output_dir_top="${timeseries_dir}/D-Apul/output/03.00-D-Apul-RNAseq-gene-expression-DESeq2"'
echo ""
echo "# Output files"
echo 'export coldata="${output_dir_top}/DESeq2-coldata.tab"'
echo ""
echo "# Paths to programs"
echo 'export programs_dir="/home/shared"'
echo "# Set number of CPUs to use"
echo 'export threads=40'
echo ""
echo "# Print formatting"
echo 'export line="--------------------------------------------------------"'
echo ""
} > .bashvars
cat .bashvars
#### Assign Variables ####
# Data directories
export timeseries_dir=/home/shared/8TB_HDD_02/shedurkin/timeseries_molecular
export output_dir_top="${timeseries_dir}/D-Apul/output/03.00-D-Apul-RNAseq-gene-expression-DESeq2"
# Output files
export coldata="${output_dir_top}/DESeq2-coldata.tab"
# Paths to programs
export programs_dir="/home/shared"
# Set number of CPUs to use
export threads=40
# Print formatting
export line="--------------------------------------------------------"
Set R variables
# Define the output directory path
output_dir <- "../output/03.00-D-Apul-RNAseq-gene-expression-DESeq2/"
# Set desired false discovery rate threshold (i.e. adjusted p-value, padj)
fdr <- 0.05
# Set log2 fold change threshold (a value of '1' is equal to a fold change of '2')
log2fc <- 1
Create coldat file
# Load bash variables into memory
source .bashvars
# Create output directory, if it doesn't exist
mkdir --parents "${output_dir_top}"
# Create associative array with sample and timepoint
metadata="../../M-multi-species/data/rna_metadata.csv"
# Create DESeq2-formatted coldata file
## Create header
printf "\t%s\t%s\n" "time.point" "colony.id"> "${coldata}"
## Read the metadata file line by line
while IFS=',' read -r sample_number sample_name plate well_number azenta_sample_name colony_id timepoint sample_type species_strain SampleBuffer; do
# Check if the species is "Acropora pulchra"
if [[ "${species_strain}" == "Acropora pulchra" ]]; then
printf "%s\t%s\t%s\n" "${azenta_sample_name}" "${timepoint}" "${colony_id}"
fi
done < <(tail -n +2 "${metadata}") \
| sort -k1,1 \
>> "${coldata}"
## Tab-delimited output of sample and timepoint
for sample in "${!sample_timepoint_map[@]}"
do
printf "%s\t%s\n" "$sample" "${sample_timepoint_map[$sample]}"
done | sort -k1,1 \
>> "${coldata}"
# Peek at output
head "${coldata}" | column -t
echo ""
echo "${line}"
echo ""
wc -l "${coldata}"
echo ""
echo "${line}"
echo ""
echo "Colony counts:"
echo ""
awk 'NR > 1 {print $3}' "${coldata}" | sort | uniq -c
Read in gene counts and coldata files
gene.counts <- as.matrix(read.csv(file = "../output/02.20-D-Apul-RNAseq-alignment-HiSat2/apul-gene_count_matrix.csv", row.names="gene_id", check.names = FALSE))
coldata <- read.csv(file = "../output/03.00-D-Apul-RNAseq-gene-expression-DESeq2/DESeq2-coldata.tab", row.names=1, sep = "\t")
coldata$time.point <- factor(coldata$time.point)
head(gene.counts)
head(coldata)
Verify rownames match
all(rownames(coldata) == colnames(gene.counts))
Create DESeq2 data set
dds <- DESeqDataSetFromMatrix(countData = gene.counts,
colData = coldata,
design = ~ time.point + colony.id)
dds
Add gene columun feature
featureData <- data.frame(gene=rownames(gene.counts))
mcols(dds) <- DataFrame(mcols(dds), featureData)
mcols(dds)
dds$time.point <- factor(dds$time.point, levels = c("TP1","TP2", "TP3", "TP4"))
DESeq analysis
dds <- DESeq(dds)
Pairwise results tables
Full
tp1.v.tp2.results <- results(dds, contrast=c("time.point","TP1","TP2"), alpha = fdr, lfcThreshold = log2fc)
tp1.v.tp3.results <- results(dds, contrast=c("time.point","TP1","TP3"), alpha = fdr, lfcThreshold = log2fc)
tp1.v.tp4.results <- results(dds, contrast=c("time.point","TP1","TP4"), alpha = fdr, lfcThreshold = log2fc)
tp2.v.tp3.results <- results(dds, contrast=c("time.point","TP2","TP3"), alpha = fdr, lfcThreshold = log2fc)
tp2.v.tp4.results <- results(dds, contrast=c("time.point","TP2","TP4"), alpha = fdr, lfcThreshold = log2fc)
tp3.v.tp4.results <- results(dds, contrast=c("time.point","TP3","TP4"), alpha = fdr, lfcThreshold = log2fc)
tp2.v.tp4.results
summary(tp2.v.tp4.results)
table(tp2.v.tp4.results$padj < 0.05)
Write DDS results tables to CSVs
# Create a named list of the data frames
results_list <- list(
tp1.v.tp2.results = tp1.v.tp2.results,
tp1.v.tp3.results = tp1.v.tp3.results,
tp1.v.tp4.results = tp1.v.tp4.results,
tp2.v.tp3.results = tp2.v.tp3.results,
tp2.v.tp4.results = tp2.v.tp4.results,
tp3.v.tp4.results = tp3.v.tp4.results
)
# Loop through the list and write each data frame to a CSV file in the specified directory
for (df_name in names(results_list)) {
write.csv(results_list[[df_name]], file = paste0(output_dir, df_name, ".table.csv"), row.names = FALSE, quote = FALSE)
}
Plotting
Sample distances
sampleDists <- dist(t(assay(vsd)))
sampleDistMatrix <- as.matrix( sampleDists )
rownames(sampleDistMatrix) <- paste( vsd$colony.id, vsd$time.point, sep = " - " )
colnames(sampleDistMatrix) <- NULL
colors <- colorRampPalette( rev(brewer.pal(9, "Blues")) )(255)
pheatmap(sampleDistMatrix,
clustering_distance_rows = sampleDists,
clustering_distance_cols = sampleDists,
col = colors)
PCA - All time points
Visualize sample clustering via PCA (after transformation)
# PCA with points color coded by time point
plotPCA(vsd, intgroup = c("time.point"))
# PCA with points color coded by colony ID
plotPCA(vsd, intgroup = c("colony.id"))
Time points 1 and 4 are clustering together, and time points 2 and 3 are clustering together. It also looks like colonies cluster somewhat.
Heatmap - All time points
top_20_counts_all <- order(rowMeans(counts(dds,normalized=TRUE)),
decreasing=TRUE)[1:200]
timepoint_annotation = colData(dds) %>% as.data.frame() %>% select(time.point)
pheatmap(assay(vsd)[top_20_counts_all,],
cluster_rows=FALSE,
show_rownames=FALSE,
cluster_cols=TRUE,
annotation_col = timepoint_annotation)
LS0tCnRpdGxlOiAiMDMuMDAtRC1BcHVsLVJOQXNlcS1nZW5lLWV4cHJlc3Npb24tREVTZXEyIgphdXRob3I6ICJTYW0gV2hpdGUiCmRhdGU6ICIyMDI0LTEwLTE1IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRoZW1lOiBjb3NtbwogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBzaG93CiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgZ2l0aHViX2RvY3VtZW50OgogICAgdG9jOiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICBodG1sX2RvY3VtZW50OgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKYmlibGlvZ3JhcGh5OiByZWZlcmVuY2VzLmJpYgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQpsaWJyYXJ5KCJSQ29sb3JCcmV3ZXIiKQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGtuaXRyKQpsaWJyYXJ5KHBoZWF0bWFwKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLCAgICAgICAgICMgRGlzcGxheSBjb2RlIGNodW5rcwogIGV2YWwgPSBGQUxTRSwgICAgICAgICMgRXZhbHVhdGUgY29kZSBjaHVua3MKICB3YXJuaW5nID0gRkFMU0UsICAgICAjIEhpZGUgd2FybmluZ3MKICBtZXNzYWdlID0gRkFMU0UsICAgICAjIEhpZGUgbWVzc2FnZXMKICBjb21tZW50ID0gIiIgICAgICAgICAjIFByZXZlbnRzIGFwcGVuZGluZyAnIyMnIHRvIGJlZ2lubmluZyBvZiBsaW5lcyBpbiBjb2RlIG91dHB1dAopCmBgYAoKIyBTZXQgQmFzaCB2YXJpYWJsZXMKYGBge3Igc2F2ZS1iYXNoLXZhcmlhYmxlcy10by1ydmFycy1maWxlLCBlbmdpbmU9J2Jhc2gnLCBldmFsPVRSVUV9CnsKZWNobyAiIyMjIyBBc3NpZ24gVmFyaWFibGVzICMjIyMiCmVjaG8gIiIKCmVjaG8gIiMgRGF0YSBkaXJlY3RvcmllcyIKZWNobyAnZXhwb3J0IHRpbWVzZXJpZXNfZGlyPS9ob21lL3NoYXJlZC84VEJfSEREXzAyL3NoZWR1cmtpbi90aW1lc2VyaWVzX21vbGVjdWxhcicKZWNobyAnZXhwb3J0IG91dHB1dF9kaXJfdG9wPSIke3RpbWVzZXJpZXNfZGlyfS9ELUFwdWwvb3V0cHV0LzAzLjAwLUQtQXB1bC1STkFzZXEtZ2VuZS1leHByZXNzaW9uLURFU2VxMiInCmVjaG8gIiIKCgoKZWNobyAiIyBPdXRwdXQgZmlsZXMiCmVjaG8gJ2V4cG9ydCBjb2xkYXRhPSIke291dHB1dF9kaXJfdG9wfS9ERVNlcTItY29sZGF0YS50YWIiJwplY2hvICIiCgplY2hvICIjIFBhdGhzIHRvIHByb2dyYW1zIgplY2hvICdleHBvcnQgcHJvZ3JhbXNfZGlyPSIvaG9tZS9zaGFyZWQiJwoKCmVjaG8gIiMgU2V0IG51bWJlciBvZiBDUFVzIHRvIHVzZSIKZWNobyAnZXhwb3J0IHRocmVhZHM9NDAnCmVjaG8gIiIKCgplY2hvICIjIFByaW50IGZvcm1hdHRpbmciCmVjaG8gJ2V4cG9ydCBsaW5lPSItLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLSInCmVjaG8gIiIKfSA+IC5iYXNodmFycwoKY2F0IC5iYXNodmFycwpgYGAKIyBTZXQgUiB2YXJpYWJsZXMKYGBge3Igc2V0LXZhcmlhYmxlc30KIyBEZWZpbmUgdGhlIG91dHB1dCBkaXJlY3RvcnkgcGF0aApvdXRwdXRfZGlyIDwtICIuLi9vdXRwdXQvMDMuMDAtRC1BcHVsLVJOQXNlcS1nZW5lLWV4cHJlc3Npb24tREVTZXEyLyIKCiMgU2V0IGRlc2lyZWQgZmFsc2UgZGlzY292ZXJ5IHJhdGUgdGhyZXNob2xkIChpLmUuIGFkanVzdGVkIHAtdmFsdWUsIHBhZGopCmZkciA8LSAwLjA1CgojIFNldCBsb2cyIGZvbGQgY2hhbmdlIHRocmVzaG9sZCAoYSB2YWx1ZSBvZiAnMScgaXMgZXF1YWwgdG8gYSBmb2xkIGNoYW5nZSBvZiAnMicpCmxvZzJmYyA8LSAxCmBgYAoKCiMgQ3JlYXRlIGNvbGRhdCBmaWxlCmBgYHtyIGNyZWF0ZS1jb2xkYXRhLWZpbGUsIGVuZ2luZT0nYmFzaCcsIGV2YWw9RkFMU0V9CiMgTG9hZCBiYXNoIHZhcmlhYmxlcyBpbnRvIG1lbW9yeQpzb3VyY2UgLmJhc2h2YXJzCgojIENyZWF0ZSBvdXRwdXQgZGlyZWN0b3J5LCBpZiBpdCBkb2Vzbid0IGV4aXN0Cm1rZGlyIC0tcGFyZW50cyAiJHtvdXRwdXRfZGlyX3RvcH0iCgojIENyZWF0ZSBhc3NvY2lhdGl2ZSBhcnJheSB3aXRoIHNhbXBsZSBhbmQgdGltZXBvaW50Cm1ldGFkYXRhPSIuLi8uLi9NLW11bHRpLXNwZWNpZXMvZGF0YS9ybmFfbWV0YWRhdGEuY3N2IgoKIyBDcmVhdGUgREVTZXEyLWZvcm1hdHRlZCBjb2xkYXRhIGZpbGUKCiMjIENyZWF0ZSBoZWFkZXIKcHJpbnRmICJcdCVzXHQlc1xuIiAidGltZS5wb2ludCIgImNvbG9ueS5pZCI+ICIke2NvbGRhdGF9IgoKIyMgUmVhZCB0aGUgbWV0YWRhdGEgZmlsZSBsaW5lIGJ5IGxpbmUKd2hpbGUgSUZTPScsJyByZWFkIC1yIHNhbXBsZV9udW1iZXIgc2FtcGxlX25hbWUgcGxhdGUgd2VsbF9udW1iZXIgYXplbnRhX3NhbXBsZV9uYW1lIGNvbG9ueV9pZCB0aW1lcG9pbnQgc2FtcGxlX3R5cGUgc3BlY2llc19zdHJhaW4gU2FtcGxlQnVmZmVyOyBkbwogICAgIyBDaGVjayBpZiB0aGUgc3BlY2llcyBpcyAiQWNyb3BvcmEgcHVsY2hyYSIKICAgIGlmIFtbICIke3NwZWNpZXNfc3RyYWlufSIgPT0gIkFjcm9wb3JhIHB1bGNocmEiIF1dOyB0aGVuCiAgICAgIHByaW50ZiAiJXNcdCVzXHQlc1xuIiAiJHthemVudGFfc2FtcGxlX25hbWV9IiAiJHt0aW1lcG9pbnR9IiAiJHtjb2xvbnlfaWR9IgogICAgZmkKZG9uZSA8IDwodGFpbCAtbiArMiAiJHttZXRhZGF0YX0iKSBcCnwgc29ydCAtazEsMSBcCj4+ICIke2NvbGRhdGF9IgoKCgojIyBUYWItZGVsaW1pdGVkIG91dHB1dCBvZiBzYW1wbGUgYW5kIHRpbWVwb2ludApmb3Igc2FtcGxlIGluICIkeyFzYW1wbGVfdGltZXBvaW50X21hcFtAXX0iCmRvCiAgcHJpbnRmICIlc1x0JXNcbiIgIiRzYW1wbGUiICIke3NhbXBsZV90aW1lcG9pbnRfbWFwWyRzYW1wbGVdfSIKZG9uZSB8IHNvcnQgLWsxLDEgXAo+PiAiJHtjb2xkYXRhfSIKCiMgUGVlayBhdCBvdXRwdXQKaGVhZCAiJHtjb2xkYXRhfSIgfCBjb2x1bW4gLXQKCmVjaG8gIiIKZWNobyAiJHtsaW5lfSIKZWNobyAiIgoKd2MgLWwgIiR7Y29sZGF0YX0iCgplY2hvICIiCmVjaG8gIiR7bGluZX0iCmVjaG8gIiIKCmVjaG8gIkNvbG9ueSBjb3VudHM6IgplY2hvICIiCmF3ayAnTlIgPiAxIHtwcmludCAkM30nICIke2NvbGRhdGF9IiB8IHNvcnQgfCB1bmlxIC1jCmBgYAoKIyBSZWFkIGluIGdlbmUgY291bnRzIGFuZCBjb2xkYXRhIGZpbGVzCmBgYHtyIHJlYWQtZ2VuZS1jb3VudHN9CmdlbmUuY291bnRzIDwtIGFzLm1hdHJpeChyZWFkLmNzdihmaWxlID0gIi4uL291dHB1dC8wMi4yMC1ELUFwdWwtUk5Bc2VxLWFsaWdubWVudC1IaVNhdDIvYXB1bC1nZW5lX2NvdW50X21hdHJpeC5jc3YiLCByb3cubmFtZXM9ImdlbmVfaWQiLCBjaGVjay5uYW1lcyA9IEZBTFNFKSkKCmNvbGRhdGEgPC0gcmVhZC5jc3YoZmlsZSA9ICIuLi9vdXRwdXQvMDMuMDAtRC1BcHVsLVJOQXNlcS1nZW5lLWV4cHJlc3Npb24tREVTZXEyL0RFU2VxMi1jb2xkYXRhLnRhYiIsIHJvdy5uYW1lcz0xLCBzZXAgPSAiXHQiKQpjb2xkYXRhJHRpbWUucG9pbnQgPC0gZmFjdG9yKGNvbGRhdGEkdGltZS5wb2ludCkKCmhlYWQoZ2VuZS5jb3VudHMpCgpoZWFkKGNvbGRhdGEpCmBgYAoKIyMgVmVyaWZ5IHJvd25hbWVzIG1hdGNoCmBgYHtyIGNoZWNrLXJvd25hbWVzfQphbGwocm93bmFtZXMoY29sZGF0YSkgPT0gY29sbmFtZXMoZ2VuZS5jb3VudHMpKQpgYGAKCiMgQ3JlYXRlIERFU2VxMiBkYXRhIHNldApgYGB7ciBjcmVhdGUtZGVzZXEyLWRhdGEtc2V0LCBjYWNoZT1UUlVFfQpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBnZW5lLmNvdW50cywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gdGltZS5wb2ludCArIGNvbG9ueS5pZCkKZGRzCgpgYGAKCiMjIEFkZCBnZW5lIGNvbHVtdW4gZmVhdHVyZQpgYGB7ciBhZGQtZ2VuZS1mZWF0dXJlfQpmZWF0dXJlRGF0YSA8LSBkYXRhLmZyYW1lKGdlbmU9cm93bmFtZXMoZ2VuZS5jb3VudHMpKQptY29scyhkZHMpIDwtIERhdGFGcmFtZShtY29scyhkZHMpLCBmZWF0dXJlRGF0YSkKbWNvbHMoZGRzKQpgYGAKCmBgYHtyIGxldmVsfQpkZHMkdGltZS5wb2ludCA8LSBmYWN0b3IoZGRzJHRpbWUucG9pbnQsIGxldmVscyA9IGMoIlRQMSIsIlRQMiIsICJUUDMiLCAiVFA0IikpCmBgYAoKIyBERVNlcSBhbmFseXNpcwpgYGB7ciBkZXNlcX0KZGRzIDwtIERFU2VxKGRkcykKCmBgYAoKIyMgUGFpcndpc2UgcmVzdWx0cyB0YWJsZXMKCiMjIyBGdWxsIApgYGB7ciBkZXNlcTItcGFpcndpc2UtcmVzdWx0cy10YWJsZXN9CnRwMS52LnRwMi5yZXN1bHRzIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdD1jKCJ0aW1lLnBvaW50IiwiVFAxIiwiVFAyIiksIGFscGhhID0gZmRyLCBsZmNUaHJlc2hvbGQgPSBsb2cyZmMpCnRwMS52LnRwMy5yZXN1bHRzIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdD1jKCJ0aW1lLnBvaW50IiwiVFAxIiwiVFAzIiksIGFscGhhID0gZmRyLCBsZmNUaHJlc2hvbGQgPSBsb2cyZmMpCnRwMS52LnRwNC5yZXN1bHRzIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdD1jKCJ0aW1lLnBvaW50IiwiVFAxIiwiVFA0IiksIGFscGhhID0gZmRyLCBsZmNUaHJlc2hvbGQgPSBsb2cyZmMpCnRwMi52LnRwMy5yZXN1bHRzIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdD1jKCJ0aW1lLnBvaW50IiwiVFAyIiwiVFAzIiksIGFscGhhID0gZmRyLCBsZmNUaHJlc2hvbGQgPSBsb2cyZmMpCnRwMi52LnRwNC5yZXN1bHRzIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdD1jKCJ0aW1lLnBvaW50IiwiVFAyIiwiVFA0IiksIGFscGhhID0gZmRyLCBsZmNUaHJlc2hvbGQgPSBsb2cyZmMpCnRwMy52LnRwNC5yZXN1bHRzIDwtIHJlc3VsdHMoZGRzLCBjb250cmFzdD1jKCJ0aW1lLnBvaW50IiwiVFAzIiwiVFA0IiksIGFscGhhID0gZmRyLCBsZmNUaHJlc2hvbGQgPSBsb2cyZmMpCgp0cDIudi50cDQucmVzdWx0cwoKc3VtbWFyeSh0cDIudi50cDQucmVzdWx0cykKCnRhYmxlKHRwMi52LnRwNC5yZXN1bHRzJHBhZGogPCAwLjA1KQpgYGAKCiMgV3JpdGUgRERTIHJlc3VsdHMgdGFibGVzIHRvIENTVnMKYGBge3Igd3JpdGUtZGRzLXJlc3VsdHMtY3N2fQojIENyZWF0ZSBhIG5hbWVkIGxpc3Qgb2YgdGhlIGRhdGEgZnJhbWVzCnJlc3VsdHNfbGlzdCA8LSBsaXN0KAogIHRwMS52LnRwMi5yZXN1bHRzID0gdHAxLnYudHAyLnJlc3VsdHMsCiAgdHAxLnYudHAzLnJlc3VsdHMgPSB0cDEudi50cDMucmVzdWx0cywKICB0cDEudi50cDQucmVzdWx0cyA9IHRwMS52LnRwNC5yZXN1bHRzLAogIHRwMi52LnRwMy5yZXN1bHRzID0gdHAyLnYudHAzLnJlc3VsdHMsCiAgdHAyLnYudHA0LnJlc3VsdHMgPSB0cDIudi50cDQucmVzdWx0cywKICB0cDMudi50cDQucmVzdWx0cyA9IHRwMy52LnRwNC5yZXN1bHRzCikKCiMgTG9vcCB0aHJvdWdoIHRoZSBsaXN0IGFuZCB3cml0ZSBlYWNoIGRhdGEgZnJhbWUgdG8gYSBDU1YgZmlsZSBpbiB0aGUgc3BlY2lmaWVkIGRpcmVjdG9yeQpmb3IgKGRmX25hbWUgaW4gbmFtZXMocmVzdWx0c19saXN0KSkgewogIHdyaXRlLmNzdihyZXN1bHRzX2xpc3RbW2RmX25hbWVdXSwgZmlsZSA9IHBhc3RlMChvdXRwdXRfZGlyLCBkZl9uYW1lLCAiLnRhYmxlLmNzdiIpLCByb3cubmFtZXMgPSBGQUxTRSwgcXVvdGUgPSBGQUxTRSkKfQpgYGAKCiMgVmFyaWFuY2Ugc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb25zIChWU1QpCi0gSGVyZSB3ZSB0cmFuc2Zvcm0gY291bnRzIHVzaW5nIGEgdmFyaWFuY2Ugc3RhYmlsaXppbmcgdHJhbnNmb3JtYXRpb24gKFZTVCksIHNpbmNlIHdlIGhhdmUgbWFueSBzYW1wbGVzLiAKYGBge3IgVlNUfQp2c2QgPC0gdmFyaWFuY2VTdGFiaWxpemluZ1RyYW5zZm9ybWF0aW9uKGRkcywgYmxpbmQ9RkFMU0UpCmBgYAoKCk5PVEU6IEhvdmVyIG92ZXIgcG9pbnRzIHRvIHNlZSB0aGUgc2FtcGxlIG51bWJlcnMKCiMgUGxvdHRpbmcKCiMjIFNhbXBsZSBkaXN0YW5jZXMKYGBge3IgcGxvdC1zYW1wbGUtZGlzdGFuY2VzfQpzYW1wbGVEaXN0cyA8LSBkaXN0KHQoYXNzYXkodnNkKSkpCgpzYW1wbGVEaXN0TWF0cml4IDwtIGFzLm1hdHJpeCggc2FtcGxlRGlzdHMgKQoKcm93bmFtZXMoc2FtcGxlRGlzdE1hdHJpeCkgPC0gcGFzdGUoIHZzZCRjb2xvbnkuaWQsIHZzZCR0aW1lLnBvaW50LCBzZXAgPSAiIC0gIiApCgpjb2xuYW1lcyhzYW1wbGVEaXN0TWF0cml4KSA8LSBOVUxMCgpjb2xvcnMgPC0gY29sb3JSYW1wUGFsZXR0ZSggcmV2KGJyZXdlci5wYWwoOSwgIkJsdWVzIikpICkoMjU1KQoKcGhlYXRtYXAoc2FtcGxlRGlzdE1hdHJpeCwKICAgICAgICAgY2x1c3RlcmluZ19kaXN0YW5jZV9yb3dzID0gc2FtcGxlRGlzdHMsCiAgICAgICAgIGNsdXN0ZXJpbmdfZGlzdGFuY2VfY29scyA9IHNhbXBsZURpc3RzLAogICAgICAgICBjb2wgPSBjb2xvcnMpCmBgYAoKIyMgUENBIC0gQWxsIHRpbWUgcG9pbnRzClZpc3VhbGl6ZSBzYW1wbGUgY2x1c3RlcmluZyB2aWEgUENBIChhZnRlciB0cmFuc2Zvcm1hdGlvbikKYGBge3IgcGNhLWFsbC10aW1lcG9pbnRzfQojIFBDQSB3aXRoIHBvaW50cyBjb2xvciBjb2RlZCBieSB0aW1lIHBvaW50IApwbG90UENBKHZzZCwgaW50Z3JvdXAgPSBjKCJ0aW1lLnBvaW50IikpCgojIFBDQSB3aXRoIHBvaW50cyBjb2xvciBjb2RlZCBieSBjb2xvbnkgSUQgCnBsb3RQQ0EodnNkLCBpbnRncm91cCA9IGMoImNvbG9ueS5pZCIpKQpgYGAKVGltZSBwb2ludHMgMSBhbmQgNCBhcmUgY2x1c3RlcmluZyB0b2dldGhlciwgYW5kIHRpbWUgcG9pbnRzIDIgYW5kIDMgYXJlIGNsdXN0ZXJpbmcgdG9nZXRoZXIuIEl0IGFsc28gbG9va3MgbGlrZSBjb2xvbmllcyBjbHVzdGVyIHNvbWV3aGF0LgoKIyMgSGVhdG1hcCAtIEFsbCB0aW1lIHBvaW50cwpgYGB7ciBoZWF0bWFwLWFsbC10aW1lcG9pbnRzLXRvcDIwfQp0b3BfMjBfY291bnRzX2FsbCA8LSBvcmRlcihyb3dNZWFucyhjb3VudHMoZGRzLG5vcm1hbGl6ZWQ9VFJVRSkpLAogICAgICAgICAgICAgICAgZGVjcmVhc2luZz1UUlVFKVsxOjIwMF0KCnRpbWVwb2ludF9hbm5vdGF0aW9uID0gY29sRGF0YShkZHMpICU+JSBhcy5kYXRhLmZyYW1lKCkgJT4lIHNlbGVjdCh0aW1lLnBvaW50KQoKCnBoZWF0bWFwKGFzc2F5KHZzZClbdG9wXzIwX2NvdW50c19hbGwsXSwgCiAgICAgICAgIGNsdXN0ZXJfcm93cz1GQUxTRSwgCiAgICAgICAgIHNob3dfcm93bmFtZXM9RkFMU0UsCiAgICAgICAgIGNsdXN0ZXJfY29scz1UUlVFLCAKICAgICAgICAgYW5ub3RhdGlvbl9jb2wgPSB0aW1lcG9pbnRfYW5ub3RhdGlvbikKCmBgYAo=