Intro
This notebook will download raw WGBS FastQs, concatenate them (there were multiple lanes run), and then trim them using fastp. Samples which generate an error during trimming will attempt to be repaired using BBTools repairl.sh
(BBMap – Bushnell B. – sourceforge.net/projects/bbmap/). Trimming results will be analyzed with FastQC and summarized by MultiQC (Ewels et al. 2016).
Outputs
The expected outputs will be:
multiqc_report.html
: A summary report of the alignment results generated by MultiQC, in HTML format.
*fastp-trim*.fq.gz
: Trimmed FastQ files.
Create a Bash variables file
This allows usage of Bash variables across R Markdown chunks.
{
echo "#### Assign Variables ####"
echo ""
echo "# Data directories"
echo 'export repo_dir=/home/shared/8TB_HDD_01/sam/gitrepos/ceasmallr'
echo 'export output_dir_top=${repo_dir}/output/00.00-trimming-fastp'
echo 'export raw_reads_url="https://owl.fish.washington.edu/nightingales/C_virginica/2018OALarvae_DNAm_discovery/"'
echo 'export raw_reads_dir="${repo_dir}/data/raw_reads"'
echo 'export trimmed_fastqs_dir="${output_dir_top}/trimmed-fastqs"'
echo ""
echo "# Paths to programs"
echo 'export programs_dir="/home/shared"'
echo 'export bbmap_repair="${programs_dir}/bbmap-39.12/repair.sh"'
echo 'export bismark_dir="${programs_dir}/Bismark-0.24.0"'
echo 'export bowtie2_dir="${programs_dir}/bowtie2-2.4.4-linux-x86_64"'
echo 'export fastp="${programs_dir}/fastp-v0.24.0/fastp"'
echo 'export fastqc="${programs_dir}/FastQC-0.12.1/fastqc"'
echo 'export multiqc="/home/sam/programs/mambaforge/bin/multiqc"'
echo 'export samtools_dir="${programs_dir}/samtools-1.12"'
echo ""
echo "# Set FastQ filename patterns"
echo "export fastq_pattern='*.fastq.gz'"
echo "export R1_fastq_pattern='*_R1_*.fastq.gz'"
echo "export R2_fastq_pattern='*_R2_*.fastq.gz'"
echo "export trimmed_fastq_pattern='*fastp-trim*.fq.gz'"
echo ""
echo "# Set number of CPUs to use"
echo 'export threads=40'
echo ""
echo "## Inititalize arrays"
echo 'export fastq_array_R1=()'
echo 'export fastq_array_R2=()'
echo 'export trimmed_fastqs_array=()'
echo 'export R1_names_array=()'
echo 'export R2_names_array=()'
echo ""
echo "# Print formatting"
echo 'export line="--------------------------------------------------------"'
echo ""
} > .bashvars
cat .bashvars
#### Assign Variables ####
# Data directories
export repo_dir=/home/shared/8TB_HDD_01/sam/gitrepos/ceasmallr
export output_dir_top=${repo_dir}/output/00.00-trimming-fastp
export raw_reads_url="https://owl.fish.washington.edu/nightingales/C_virginica/2018OALarvae_DNAm_discovery/"
export raw_reads_dir="${repo_dir}/data/raw_reads"
export trimmed_fastqs_dir="${output_dir_top}/trimmed-fastqs"
# Paths to programs
export programs_dir="/home/shared"
export bbmap_repair="${programs_dir}/bbmap-39.12/repair.sh"
export bismark_dir="${programs_dir}/Bismark-0.24.0"
export bowtie2_dir="${programs_dir}/bowtie2-2.4.4-linux-x86_64"
export fastp="${programs_dir}/fastp-v0.24.0/fastp"
export fastqc="${programs_dir}/FastQC-0.12.1/fastqc"
export multiqc="/home/sam/programs/mambaforge/bin/multiqc"
export samtools_dir="${programs_dir}/samtools-1.12"
# Set FastQ filename patterns
export fastq_pattern='*.fastq.gz'
export R1_fastq_pattern='*_R1_*.fastq.gz'
export R2_fastq_pattern='*_R2_*.fastq.gz'
export trimmed_fastq_pattern='*fastp-trim*.fq.gz'
# Set number of CPUs to use
export threads=40
## Inititalize arrays
export fastq_array_R1=()
export fastq_array_R2=()
export trimmed_fastqs_array=()
export R1_names_array=()
export R2_names_array=()
# Print formatting
export line="--------------------------------------------------------"
Download raw reads
The --cut-dirs 2
command cuts the preceding directory structure (i.e. nightingales/C_virginica/
)
so that we just end up with the reads.
# Load bash variables into memory
source .bashvars
# Create directory, if it doesn't exist
mkdir --parents ${raw_reads_dir}
wget \
--directory-prefix ${raw_reads_dir} \
--recursive \
--no-check-certificate \
--continue \
--cut-dirs 2 \
--no-parent \
--no-host-directories \
--quiet \
${raw_reads_url}
ls -lh "${raw_reads_dir}"
Verify checkums
# Load bash variables into memory
source .bashvars
cd "${raw_reads_dir}"/2018OALarvae_DNAm_discovery
md5sum --check md5sum_list.txt
echo ""
echo "${line}"
echo ""
cd second_lane
md5sum --check md5sum_list.txt
Concatenate reads
Concatenation also handles samples where there might be a missing set of R2 reads in the second round of sequencing.
# Load bash variables into memory
source .bashvars
cd "${raw_reads_dir}"
# Concatenate FastQ files from 1st and 2nd runs
# Do NOT quote fastq_pattern variable
# Declare an associative array to keep track of processed files
declare -A processed_files
for first_run_fastq in "${raw_reads_dir}"/2018OALarvae_DNAm_discovery/${fastq_pattern}
do
# Strip full path to just get filename.
first_run_fastq_name="${first_run_fastq##*/}"
# Initialize a flag to check if a match is found
match_found=false
# Process second run and concatenate with corresponding FastQ from first run
# Do NOT quote fastq_pattern variable
for second_run_fastq in "${raw_reads_dir}"/2018OALarvae_DNAm_discovery/second_lane/${fastq_pattern}
do
# Strip full path to just get filename.
second_run_fastq_name="${second_run_fastq##*/}"
# Concatenate FastQs with same filenames
if [[ "${first_run_fastq_name}" == "${second_run_fastq_name}" ]]
then
echo "Concatenating ${first_run_fastq} with ${second_run_fastq} to ${raw_reads_dir}/${first_run_fastq_name}"
echo ""
cat "${first_run_fastq}" "${second_run_fastq}" >> "${raw_reads_dir}/${first_run_fastq_name}"
match_found=true
processed_files["${first_run_fastq_name}"]=true
break
fi
done
# If no match is found, copy the file to the target directory
if [[ "${match_found}" == false ]]
then
if [[ -z "${processed_files[${first_run_fastq_name}]}" ]]
then
echo "NO MATCH!"
echo "Copying ${first_run_fastq} to ${raw_reads_dir}"
echo ""
cp "${first_run_fastq}" "${raw_reads_dir}"
processed_files["${first_run_fastq_name}"]=true
fi
fi
echo "Generating checksums for concatenated FastQs..."
md5sum "${first_run_fastq_name}" | tee --append "${first_run_fastq_name}".md5
echo ""
echo "${line}"
echo ""
done
Trimming and Error Repair
Trimming will remove any Illumina sequencing adapters, as well as polyG and polyX (primarily polyA) sequences. Trimming will also remove the first 15bp from the 5’ end of each read.
Samples generating an error during trimming will attempt to be repaired with BBTools’ repair.sh
script.
# Load bash variables into memory
source .bashvars
# Make output directory, if it doesn't exist
mkdir --parents ${output_dir_top}
cd "${raw_reads_dir}"
# Create arrays of fastq R1 files and sample names
# Do NOT quote R1_fastq_pattern variable
for fastq in ${R1_fastq_pattern}
do
fastq_array_R1+=("${fastq}")
# Use parameter substitution to remove all text up to and including last "." from
# right side of string.
R1_names_array+=("${fastq%%.*}")
done
# Create array of fastq R2 files
# Do NOT quote R2_fastq_pattern variable
for fastq in ${R2_fastq_pattern}
do
fastq_array_R2+=("${fastq}")
# Use parameter substitution to remove all text up to and including last "." from
# right side of string.
R2_names_array+=(${fastq%%.*})
done
# Run fastp on files
# Adds JSON report output for downstream usage by MultiQC
echo "Beginning fastp trimming."
echo ""
for index in "${!fastq_array_R1[@]}"
do
R1_sample_name="${R1_names_array[index]}"
R2_sample_name="${R2_names_array[index]}"
stderr_PE_name=$(echo ${R1_sample_name} | awk -F"_" '{print $1}')
${fastp} \
--in1 ${fastq_array_R1[index]} \
--in2 ${fastq_array_R2[index]} \
--detect_adapter_for_pe \
--trim_poly_g \
--trim_poly_x \
--thread 16 \
--trim_front1 15 \
--trim_front2 15 \
--html ${output_dir_top}/"${R1_sample_name}".fastp-trim.report.html \
--json ${output_dir_top}/"${R1_sample_name}".fastp-trim.report.json \
--out1 ${output_dir_top}/"${R1_sample_name}".fastp-trim.fq.gz \
--out2 ${output_dir_top}/"${R2_sample_name}".fastp-trim.fq.gz \
2> ${output_dir_top}/"${stderr_PE_name}".fastp-trim.stderr
grep --before-context 5 "ERROR" ${output_dir_top}/"${stderr_PE_name}".fastp-trim.stderr
# Check for fastp errors and then repair
if grep --quiet "ERROR" ${output_dir_top}/"${stderr_PE_name}".fastp-trim.stderr; then
rm ${output_dir_top}/"${R1_sample_name}".fastp-trim.fq.gz
rm ${output_dir_top}/"${R2_sample_name}".fastp-trim.fq.gz
${bbmap_repair} \
in1=${fastq_array_R1[index]} \
in2=${fastq_array_R2[index]} \
out1="${R1_sample_name}".REPAIRED.fastq.gz \
out2="${R2_sample_name}".REPAIRED.fastq.gz \
outs=/dev/null \
2> "${R1_sample_name}".REPAIRED.stderr
${fastp} \
--in1 "${R1_sample_name}".REPAIRED.fastq.gz \
--in2 "${R2_sample_name}".REPAIRED.fastq.gz \
--detect_adapter_for_pe \
--trim_poly_g \
--trim_poly_x \
--thread 16 \
--trim_front1 15 \
--trim_front2 15 \
--html ${output_dir_top}/"${R1_sample_name}".fastp-trim.REPAIRED.report.html \
--json ${output_dir_top}/"${R1_sample_name}".fastp-trim.REPAIRED.report.json \
--out1 ${output_dir_top}/"${R1_sample_name}".fastp-trim.REPAIRED.fq.gz \
--out2 ${output_dir_top}/"${R2_sample_name}".fastp-trim.REPAIRED.fq.gz \
2> ${output_dir_top}/"${stderr_PE_name}".fastp-trim.REPAIRED.stderr
if grep --quiet "ERROR" ${output_dir_top}/"${stderr_PE_name}".fastp-trim.REPAIRED.stderr; then
echo "These ${stderr_PE_name} samples are broken."
echo "Just give up. :("
echo ""
fi
fi
echo "Finished trimming:"
echo "${fastq_array_R1[index]}"
echo "${fastq_array_R1[index]}"
echo ""
# Generate md5 checksums for newly trimmed files
cd "${output_dir_top}"
md5sum "${R1_sample_name}".fastp-trim.fq.gz > "${R1_sample_name}".fastp-trim.fq.gz.md5
md5sum "${R2_sample_name}".fastp-trim.fq.gz > "${R2_sample_name}".fastp-trim.fq.gz.md5
cd "${raw_reads_dir}"
done
FastQC
# Load bash variables into memory
source .bashvars
cd "${output_dir_top}"
############ RUN FASTQC ############
# Create array of trimmed FastQs
trimmed_fastqs_array=(${trimmed_fastq_pattern})
# Pass array contents to new variable as space-delimited list
trimmed_fastqc_list=$(echo "${trimmed_fastqs_array[*]}")
echo "Beginning FastQC on trimmed reads..."
echo ""
# Run FastQC
### NOTE: Do NOT quote raw_fastqc_list
${fastqc} \
--threads ${threads} \
--outdir "${output_dir_top}" \
--quiet \
${trimmed_fastqc_list}
echo "FastQC on trimmed reads complete!"
echo ""
############ END FASTQC ############
MultiQC
# Load bash variables into memory
source .bashvars
cd "${output_dir_top}"
${multiqc} .
Ewels, Philip, Måns Magnusson, Sverker Lundin, and Max Käller. 2016.
“MultiQC: Summarize Analysis Results for Multiple Tools and Samples in a Single Report.” Bioinformatics 32 (19): 3047–48.
https://doi.org/10.1093/bioinformatics/btw354.
LS0tCnRpdGxlOiAiMDAuMDAtdHJpbW1pbmctZmFzdHAiCmF1dGhvcjogIlNhbSBXaGl0ZSIKZGF0ZTogIjIwMjQtMTEtMTUiCm91dHB1dDogCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOgogICAgdGhlbWU6IGNvc21vCiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQogICAgbnVtYmVyX3NlY3Rpb25zOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICBnaXRodWJfZG9jdW1lbnQ6CiAgICB0b2M6IHRydWUKICAgIG51bWJlcl9zZWN0aW9uczogdHJ1ZQogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY29zbW8KICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgY29kZV9kb3dubG9hZDogdHJ1ZQpiaWJsaW9ncmFwaHk6IHJlZmVyZW5jZXMuYmliCi0tLQojIEludHJvCgpUaGlzIG5vdGVib29rIHdpbGwgZG93bmxvYWQgcmF3IFdHQlMgRmFzdFFzLCBjb25jYXRlbmF0ZSB0aGVtICh0aGVyZSB3ZXJlIG11bHRpcGxlIGxhbmVzIHJ1biksIGFuZCB0aGVuIHRyaW0gdGhlbSB1c2luZyBbZmFzdHBdKGh0dHBzOi8vZ2l0aHViLmNvbS9PcGVuR2VuZS9mYXN0cCkuIFNhbXBsZXMgd2hpY2ggZ2VuZXJhdGUgYW4gZXJyb3IgZHVyaW5nIHRyaW1taW5nIHdpbGwgYXR0ZW1wdCB0byBiZSByZXBhaXJlZCB1c2luZyBbQkJUb29scyBgcmVwYWlybC5zaGBdKGh0dHBzOi8vamdpLmRvZS5nb3YvZGF0YS1hbmQtdG9vbHMvc29mdHdhcmUtdG9vbHMvYmJ0b29scy8pIChCQk1hcCDigJMgQnVzaG5lbGwgQi4g4oCTIHNvdXJjZWZvcmdlLm5ldC9wcm9qZWN0cy9iYm1hcC8pLiBUcmltbWluZyByZXN1bHRzIHdpbGwgYmUgYW5hbHl6ZWQgd2l0aCBGYXN0UUMgYW5kIHN1bW1hcml6ZWQgYnkgW011bHRpUUNdKGh0dHBzOi8vZ2l0aHViLmNvbS9NdWx0aVFDL011bHRpUUMpIFtAZXdlbHMyMDE2XS4KCiMjIElucHV0cwoKUmF3IEZhc3RRIGZpbGVzIHdpdGggdGhlIGZvbGxvd2luZyBwYXR0ZXJuOgoKLSBgKi5mYXN0cS5nemAKCgojIyBPdXRwdXRzCgpUaGUgZXhwZWN0ZWQgb3V0cHV0cyB3aWxsIGJlOgoKLSBgbXVsdGlxY19yZXBvcnQuaHRtbGA6IEEgc3VtbWFyeSByZXBvcnQgb2YgdGhlIGFsaWdubWVudCByZXN1bHRzIGdlbmVyYXRlZCBieSBbTXVsdGlRQ10oaHR0cHM6Ly9naXRodWIuY29tL011bHRpUUMvTXVsdGlRQyksIGluIEhUTUwgZm9ybWF0LgoKLSBgKmZhc3RwLXRyaW0qLmZxLmd6YDogVHJpbW1lZCBGYXN0USBmaWxlcy4KCiAgLSBEdWUgdG8gdGhlIGxhcmdlIGZpbGUgc2l6ZXMgb2YgRmFzdFFzLCB0aGVzZSBjYW5ub3QgYmUgaG9zdGVkIGluIHRoZSBbY2Vhc21hbGxyIEdpdEh1YiByZXBvXShodHRwczovL2dpdGh1Yi5jb20vc3IzMjAvY2Vhc21hbGxyKS4gQXMgc3VjaCB0aGVzZSBmaWxlcyBhcmUgYXZhaWxhYmxlIGZvciBkb3dubG9hZCBoZXJlOgoKICAgIC0gW2h0dHBzOi8vZ2FubmV0LmZpc2gud2FzaGluZ3Rvbi5lZHUvZ2l0cmVwb3MvY2Vhc21hbGxyL291dHB1dC8wMC4wMC10cmltbWluZy1mYXN0cC9dKGh0dHBzOi8vZ2FubmV0LmZpc2gud2FzaGluZ3Rvbi5lZHUvZ2l0cmVwb3MvY2Vhc21hbGxyL291dHB1dC8wMC4wMC10cmltbWluZy1mYXN0cC8pCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0KbGlicmFyeShrbml0cikKa25pdHI6Om9wdHNfY2h1bmskc2V0KAogIGVjaG8gPSBUUlVFLCAgICAgICAgICMgRGlzcGxheSBjb2RlIGNodW5rcwogIGV2YWwgPSBGQUxTRSwgICAgICAgICMgRXZhbHVhdGUgY29kZSBjaHVua3MKICB3YXJuaW5nID0gRkFMU0UsICAgICAjIEhpZGUgd2FybmluZ3MKICBtZXNzYWdlID0gRkFMU0UsICAgICAjIEhpZGUgbWVzc2FnZXMKICBjb21tZW50ID0gIiIgICAgICAgICAjIFByZXZlbnRzIGFwcGVuZGluZyAnIyMnIHRvIGJlZ2lubmluZyBvZiBsaW5lcyBpbiBjb2RlIG91dHB1dAopCmBgYAoKIyBDcmVhdGUgYSBCYXNoIHZhcmlhYmxlcyBmaWxlCgpUaGlzIGFsbG93cyB1c2FnZSBvZiBCYXNoIHZhcmlhYmxlcyBhY3Jvc3MgUiBNYXJrZG93biBjaHVua3MuCgpgYGB7ciBzYXZlLWJhc2gtdmFyaWFibGVzLXRvLXJ2YXJzLWZpbGUsIGVuZ2luZT0nYmFzaCcsIGV2YWw9VFJVRX0KewplY2hvICIjIyMjIEFzc2lnbiBWYXJpYWJsZXMgIyMjIyIKZWNobyAiIgoKZWNobyAiIyBEYXRhIGRpcmVjdG9yaWVzIgplY2hvICdleHBvcnQgcmVwb19kaXI9L2hvbWUvc2hhcmVkLzhUQl9IRERfMDEvc2FtL2dpdHJlcG9zL2NlYXNtYWxscicKZWNobyAnZXhwb3J0IG91dHB1dF9kaXJfdG9wPSR7cmVwb19kaXJ9L291dHB1dC8wMC4wMC10cmltbWluZy1mYXN0cCcKZWNobyAnZXhwb3J0IHJhd19yZWFkc191cmw9Imh0dHBzOi8vb3dsLmZpc2gud2FzaGluZ3Rvbi5lZHUvbmlnaHRpbmdhbGVzL0NfdmlyZ2luaWNhLzIwMThPQUxhcnZhZV9ETkFtX2Rpc2NvdmVyeS8iJwplY2hvICdleHBvcnQgcmF3X3JlYWRzX2Rpcj0iJHtyZXBvX2Rpcn0vZGF0YS9yYXdfcmVhZHMiJwplY2hvICdleHBvcnQgdHJpbW1lZF9mYXN0cXNfZGlyPSIke291dHB1dF9kaXJfdG9wfS90cmltbWVkLWZhc3RxcyInCmVjaG8gIiIKCmVjaG8gIiMgUGF0aHMgdG8gcHJvZ3JhbXMiCmVjaG8gJ2V4cG9ydCBwcm9ncmFtc19kaXI9Ii9ob21lL3NoYXJlZCInCmVjaG8gJ2V4cG9ydCBiYm1hcF9yZXBhaXI9IiR7cHJvZ3JhbXNfZGlyfS9iYm1hcC0zOS4xMi9yZXBhaXIuc2giJwplY2hvICdleHBvcnQgYmlzbWFya19kaXI9IiR7cHJvZ3JhbXNfZGlyfS9CaXNtYXJrLTAuMjQuMCInCmVjaG8gJ2V4cG9ydCBib3d0aWUyX2Rpcj0iJHtwcm9ncmFtc19kaXJ9L2Jvd3RpZTItMi40LjQtbGludXgteDg2XzY0IicKZWNobyAnZXhwb3J0IGZhc3RwPSIke3Byb2dyYW1zX2Rpcn0vZmFzdHAtdjAuMjQuMC9mYXN0cCInCmVjaG8gJ2V4cG9ydCBmYXN0cWM9IiR7cHJvZ3JhbXNfZGlyfS9GYXN0UUMtMC4xMi4xL2Zhc3RxYyInCmVjaG8gJ2V4cG9ydCBtdWx0aXFjPSIvaG9tZS9zYW0vcHJvZ3JhbXMvbWFtYmFmb3JnZS9iaW4vbXVsdGlxYyInCmVjaG8gJ2V4cG9ydCBzYW10b29sc19kaXI9IiR7cHJvZ3JhbXNfZGlyfS9zYW10b29scy0xLjEyIicKZWNobyAiIgoKCmVjaG8gIiMgU2V0IEZhc3RRIGZpbGVuYW1lIHBhdHRlcm5zIgplY2hvICJleHBvcnQgZmFzdHFfcGF0dGVybj0nKi5mYXN0cS5neiciCmVjaG8gImV4cG9ydCBSMV9mYXN0cV9wYXR0ZXJuPScqX1IxXyouZmFzdHEuZ3onIgplY2hvICJleHBvcnQgUjJfZmFzdHFfcGF0dGVybj0nKl9SMl8qLmZhc3RxLmd6JyIKZWNobyAiZXhwb3J0IHRyaW1tZWRfZmFzdHFfcGF0dGVybj0nKmZhc3RwLXRyaW0qLmZxLmd6JyIKZWNobyAiIgoKZWNobyAiIyBTZXQgbnVtYmVyIG9mIENQVXMgdG8gdXNlIgplY2hvICdleHBvcnQgdGhyZWFkcz00MCcKZWNobyAiIgoKCmVjaG8gIiMjIEluaXRpdGFsaXplIGFycmF5cyIKZWNobyAnZXhwb3J0IGZhc3RxX2FycmF5X1IxPSgpJwplY2hvICdleHBvcnQgZmFzdHFfYXJyYXlfUjI9KCknCmVjaG8gJ2V4cG9ydCB0cmltbWVkX2Zhc3Rxc19hcnJheT0oKScKZWNobyAnZXhwb3J0IFIxX25hbWVzX2FycmF5PSgpJwplY2hvICdleHBvcnQgUjJfbmFtZXNfYXJyYXk9KCknCmVjaG8gIiIKCmVjaG8gIiMgUHJpbnQgZm9ybWF0dGluZyIKZWNobyAnZXhwb3J0IGxpbmU9Ii0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tIicKZWNobyAiIgp9ID4gLmJhc2h2YXJzCgpjYXQgLmJhc2h2YXJzCmBgYAoKCiMgRG93bmxvYWQgcmF3IHJlYWRzCgpUaGUgYC0tY3V0LWRpcnMgMmAgY29tbWFuZCBjdXRzIHRoZSBwcmVjZWRpbmcgZGlyZWN0b3J5IHN0cnVjdHVyZSAoaS5lLiBgbmlnaHRpbmdhbGVzL0NfdmlyZ2luaWNhL2ApCnNvIHRoYXQgd2UganVzdCBlbmQgdXAgd2l0aCB0aGUgcmVhZHMuCgpgYGB7ciBkb3dubG9hZC1yYXctcmVhZHMsIGVuZ2luZT0nYmFzaCcsIGV2YWw9RkFMU0V9CgojIExvYWQgYmFzaCB2YXJpYWJsZXMgaW50byBtZW1vcnkKc291cmNlIC5iYXNodmFycwoKIyBDcmVhdGUgZGlyZWN0b3J5LCBpZiBpdCBkb2Vzbid0IGV4aXN0Cm1rZGlyIC0tcGFyZW50cyAke3Jhd19yZWFkc19kaXJ9Cgp3Z2V0IFwKLS1kaXJlY3RvcnktcHJlZml4ICR7cmF3X3JlYWRzX2Rpcn0gXAotLXJlY3Vyc2l2ZSBcCi0tbm8tY2hlY2stY2VydGlmaWNhdGUgXAotLWNvbnRpbnVlIFwKLS1jdXQtZGlycyAyIFwKLS1uby1wYXJlbnQgXAotLW5vLWhvc3QtZGlyZWN0b3JpZXMgXAotLXF1aWV0IFwKJHtyYXdfcmVhZHNfdXJsfQoKbHMgLWxoICIke3Jhd19yZWFkc19kaXJ9IgpgYGAKCgojIyBWZXJpZnkgY2hlY2t1bXMKYGBge3IgdmVyaWZ5LWNoZWNrc3VtcywgZW5naW5lPSdiYXNoJywgZXZhbD1GQUxTRX0KIyBMb2FkIGJhc2ggdmFyaWFibGVzIGludG8gbWVtb3J5CnNvdXJjZSAuYmFzaHZhcnMKCmNkICIke3Jhd19yZWFkc19kaXJ9Ii8yMDE4T0FMYXJ2YWVfRE5BbV9kaXNjb3ZlcnkKCm1kNXN1bSAtLWNoZWNrIG1kNXN1bV9saXN0LnR4dAoKZWNobyAiIgplY2hvICIke2xpbmV9IgplY2hvICIiCgpjZCBzZWNvbmRfbGFuZQoKbWQ1c3VtIC0tY2hlY2sgbWQ1c3VtX2xpc3QudHh0CmBgYAoKIyBDb25jYXRlbmF0ZSByZWFkcwoKQ29uY2F0ZW5hdGlvbiBhbHNvIGhhbmRsZXMgc2FtcGxlcyB3aGVyZSB0aGVyZSBtaWdodCBiZSBhIG1pc3Npbmcgc2V0IG9mIFIyIHJlYWRzIGluIHRoZSBzZWNvbmQgcm91bmQgb2Ygc2VxdWVuY2luZy4KCmBgYHtyIGNvbmNhdGVuYXRlLXJlYWRzLCBlbmdpbmU9J2Jhc2gnLCBldmFsPUZBTFNFfQojIExvYWQgYmFzaCB2YXJpYWJsZXMgaW50byBtZW1vcnkKc291cmNlIC5iYXNodmFycwoKY2QgIiR7cmF3X3JlYWRzX2Rpcn0iCgoKIyBDb25jYXRlbmF0ZSBGYXN0USBmaWxlcyBmcm9tIDFzdCBhbmQgMm5kIHJ1bnMKIyBEbyBOT1QgcXVvdGUgZmFzdHFfcGF0dGVybiB2YXJpYWJsZQoKIyBEZWNsYXJlIGFuIGFzc29jaWF0aXZlIGFycmF5IHRvIGtlZXAgdHJhY2sgb2YgcHJvY2Vzc2VkIGZpbGVzCmRlY2xhcmUgLUEgcHJvY2Vzc2VkX2ZpbGVzCgpmb3IgZmlyc3RfcnVuX2Zhc3RxIGluICIke3Jhd19yZWFkc19kaXJ9Ii8yMDE4T0FMYXJ2YWVfRE5BbV9kaXNjb3ZlcnkvJHtmYXN0cV9wYXR0ZXJufQpkbwogICMgU3RyaXAgZnVsbCBwYXRoIHRvIGp1c3QgZ2V0IGZpbGVuYW1lLgogIGZpcnN0X3J1bl9mYXN0cV9uYW1lPSIke2ZpcnN0X3J1bl9mYXN0cSMjKi99IgoKICAjIEluaXRpYWxpemUgYSBmbGFnIHRvIGNoZWNrIGlmIGEgbWF0Y2ggaXMgZm91bmQKICBtYXRjaF9mb3VuZD1mYWxzZQoKICAjIFByb2Nlc3Mgc2Vjb25kIHJ1biBhbmQgY29uY2F0ZW5hdGUgd2l0aCBjb3JyZXNwb25kaW5nIEZhc3RRIGZyb20gZmlyc3QgcnVuCiAgIyBEbyBOT1QgcXVvdGUgZmFzdHFfcGF0dGVybiB2YXJpYWJsZQogIGZvciBzZWNvbmRfcnVuX2Zhc3RxIGluICIke3Jhd19yZWFkc19kaXJ9Ii8yMDE4T0FMYXJ2YWVfRE5BbV9kaXNjb3Zlcnkvc2Vjb25kX2xhbmUvJHtmYXN0cV9wYXR0ZXJufQogIGRvCiAgICAjIFN0cmlwIGZ1bGwgcGF0aCB0byBqdXN0IGdldCBmaWxlbmFtZS4KICAgIHNlY29uZF9ydW5fZmFzdHFfbmFtZT0iJHtzZWNvbmRfcnVuX2Zhc3RxIyMqL30iCgogICAgIyBDb25jYXRlbmF0ZSBGYXN0UXMgd2l0aCBzYW1lIGZpbGVuYW1lcwogICAgaWYgW1sgIiR7Zmlyc3RfcnVuX2Zhc3RxX25hbWV9IiA9PSAiJHtzZWNvbmRfcnVuX2Zhc3RxX25hbWV9IiBdXQogICAgdGhlbgogICAgICBlY2hvICJDb25jYXRlbmF0aW5nICR7Zmlyc3RfcnVuX2Zhc3RxfSB3aXRoICR7c2Vjb25kX3J1bl9mYXN0cX0gdG8gJHtyYXdfcmVhZHNfZGlyfS8ke2ZpcnN0X3J1bl9mYXN0cV9uYW1lfSIKICAgICAgZWNobyAiIgogICAgICBjYXQgIiR7Zmlyc3RfcnVuX2Zhc3RxfSIgIiR7c2Vjb25kX3J1bl9mYXN0cX0iID4+ICIke3Jhd19yZWFkc19kaXJ9LyR7Zmlyc3RfcnVuX2Zhc3RxX25hbWV9IgogICAgICBtYXRjaF9mb3VuZD10cnVlCiAgICAgIHByb2Nlc3NlZF9maWxlc1siJHtmaXJzdF9ydW5fZmFzdHFfbmFtZX0iXT10cnVlCiAgICAgIGJyZWFrCiAgICBmaQogIGRvbmUKCiAgIyBJZiBubyBtYXRjaCBpcyBmb3VuZCwgY29weSB0aGUgZmlsZSB0byB0aGUgdGFyZ2V0IGRpcmVjdG9yeQogIGlmIFtbICIke21hdGNoX2ZvdW5kfSIgPT0gZmFsc2UgXV0KICB0aGVuCiAgICBpZiBbWyAteiAiJHtwcm9jZXNzZWRfZmlsZXNbJHtmaXJzdF9ydW5fZmFzdHFfbmFtZX1dfSIgXV0KICAgIHRoZW4KICAgICAgZWNobyAiTk8gTUFUQ0ghIgogICAgICBlY2hvICJDb3B5aW5nICR7Zmlyc3RfcnVuX2Zhc3RxfSB0byAke3Jhd19yZWFkc19kaXJ9IgogICAgICBlY2hvICIiCiAgICAgIGNwICIke2ZpcnN0X3J1bl9mYXN0cX0iICIke3Jhd19yZWFkc19kaXJ9IgogICAgICBwcm9jZXNzZWRfZmlsZXNbIiR7Zmlyc3RfcnVuX2Zhc3RxX25hbWV9Il09dHJ1ZQogICAgZmkKICBmaQoKICBlY2hvICJHZW5lcmF0aW5nIGNoZWNrc3VtcyBmb3IgY29uY2F0ZW5hdGVkIEZhc3RRcy4uLiIKICBtZDVzdW0gIiR7Zmlyc3RfcnVuX2Zhc3RxX25hbWV9IiB8IHRlZSAtLWFwcGVuZCAiJHtmaXJzdF9ydW5fZmFzdHFfbmFtZX0iLm1kNQogIGVjaG8gIiIKICBlY2hvICIke2xpbmV9IgogIGVjaG8gIiIKZG9uZQpgYGAKCgojIFRyaW1taW5nIGFuZCBFcnJvciBSZXBhaXIKClRyaW1taW5nIHdpbGwgcmVtb3ZlIGFueSBJbGx1bWluYSBzZXF1ZW5jaW5nIGFkYXB0ZXJzLCBhcyB3ZWxsIGFzIHBvbHlHIGFuZCBwb2x5WCAocHJpbWFyaWx5IHBvbHlBKSBzZXF1ZW5jZXMuIFRyaW1taW5nIHdpbGwgYWxzbyByZW1vdmUgdGhlIGZpcnN0IDE1YnAgZnJvbSB0aGUgNScgZW5kIG9mIGVhY2ggcmVhZC4KClNhbXBsZXMgZ2VuZXJhdGluZyBhbiBlcnJvciBkdXJpbmcgdHJpbW1pbmcgd2lsbCBhdHRlbXB0IHRvIGJlIHJlcGFpcmVkIHdpdGggQkJUb29scycgYHJlcGFpci5zaGAgc2NyaXB0LgoKYGBge3IgZmFzdHAtdHJpbW1pbmcsIGVuZ2luZT0nYmFzaCcsIGV2YWw9RkFMU0V9CiMgTG9hZCBiYXNoIHZhcmlhYmxlcyBpbnRvIG1lbW9yeQpzb3VyY2UgLmJhc2h2YXJzCgojIE1ha2Ugb3V0cHV0IGRpcmVjdG9yeSwgaWYgaXQgZG9lc24ndCBleGlzdApta2RpciAtLXBhcmVudHMgJHtvdXRwdXRfZGlyX3RvcH0KCmNkICIke3Jhd19yZWFkc19kaXJ9IgoKCiMgQ3JlYXRlIGFycmF5cyBvZiBmYXN0cSBSMSBmaWxlcyBhbmQgc2FtcGxlIG5hbWVzCiMgRG8gTk9UIHF1b3RlIFIxX2Zhc3RxX3BhdHRlcm4gdmFyaWFibGUKCmZvciBmYXN0cSBpbiAke1IxX2Zhc3RxX3BhdHRlcm59CmRvCiAgZmFzdHFfYXJyYXlfUjErPSgiJHtmYXN0cX0iKQoKICAjIFVzZSBwYXJhbWV0ZXIgc3Vic3RpdHV0aW9uIHRvIHJlbW92ZSBhbGwgdGV4dCB1cCB0byBhbmQgaW5jbHVkaW5nIGxhc3QgIi4iIGZyb20KICAjIHJpZ2h0IHNpZGUgb2Ygc3RyaW5nLgogIFIxX25hbWVzX2FycmF5Kz0oIiR7ZmFzdHElJS4qfSIpCmRvbmUKCiMgQ3JlYXRlIGFycmF5IG9mIGZhc3RxIFIyIGZpbGVzCiMgRG8gTk9UIHF1b3RlIFIyX2Zhc3RxX3BhdHRlcm4gdmFyaWFibGUKZm9yIGZhc3RxIGluICR7UjJfZmFzdHFfcGF0dGVybn0KZG8KICBmYXN0cV9hcnJheV9SMis9KCIke2Zhc3RxfSIpCgogICMgVXNlIHBhcmFtZXRlciBzdWJzdGl0dXRpb24gdG8gcmVtb3ZlIGFsbCB0ZXh0IHVwIHRvIGFuZCBpbmNsdWRpbmcgbGFzdCAiLiIgZnJvbQogICMgcmlnaHQgc2lkZSBvZiBzdHJpbmcuCiAgUjJfbmFtZXNfYXJyYXkrPSgke2Zhc3RxJSUuKn0pCmRvbmUKCgoKIyBSdW4gZmFzdHAgb24gZmlsZXMKIyBBZGRzIEpTT04gcmVwb3J0IG91dHB1dCBmb3IgZG93bnN0cmVhbSB1c2FnZSBieSBNdWx0aVFDCgplY2hvICJCZWdpbm5pbmcgZmFzdHAgdHJpbW1pbmcuIgplY2hvICIiCgpmb3IgaW5kZXggaW4gIiR7IWZhc3RxX2FycmF5X1IxW0BdfSIKZG8KICBSMV9zYW1wbGVfbmFtZT0iJHtSMV9uYW1lc19hcnJheVtpbmRleF19IgogIFIyX3NhbXBsZV9uYW1lPSIke1IyX25hbWVzX2FycmF5W2luZGV4XX0iCiAgCiAgc3RkZXJyX1BFX25hbWU9JChlY2hvICR7UjFfc2FtcGxlX25hbWV9IHwgYXdrIC1GIl8iICd7cHJpbnQgJDF9JykKICAKICAke2Zhc3RwfSBcCiAgLS1pbjEgJHtmYXN0cV9hcnJheV9SMVtpbmRleF19IFwKICAtLWluMiAke2Zhc3RxX2FycmF5X1IyW2luZGV4XX0gXAogIC0tZGV0ZWN0X2FkYXB0ZXJfZm9yX3BlIFwKICAtLXRyaW1fcG9seV9nIFwKICAtLXRyaW1fcG9seV94IFwKICAtLXRocmVhZCAxNiBcCiAgLS10cmltX2Zyb250MSAxNSBcCiAgLS10cmltX2Zyb250MiAxNSBcCiAgLS1odG1sICR7b3V0cHV0X2Rpcl90b3B9LyIke1IxX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5yZXBvcnQuaHRtbCBcCiAgLS1qc29uICR7b3V0cHV0X2Rpcl90b3B9LyIke1IxX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5yZXBvcnQuanNvbiBcCiAgLS1vdXQxICR7b3V0cHV0X2Rpcl90b3B9LyIke1IxX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5mcS5neiBcCiAgLS1vdXQyICR7b3V0cHV0X2Rpcl90b3B9LyIke1IyX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5mcS5neiBcCiAgMj4gJHtvdXRwdXRfZGlyX3RvcH0vIiR7c3RkZXJyX1BFX25hbWV9Ii5mYXN0cC10cmltLnN0ZGVycgogIAogIGdyZXAgLS1iZWZvcmUtY29udGV4dCA1ICJFUlJPUiIgJHtvdXRwdXRfZGlyX3RvcH0vIiR7c3RkZXJyX1BFX25hbWV9Ii5mYXN0cC10cmltLnN0ZGVycgogIAogICMgQ2hlY2sgZm9yIGZhc3RwIGVycm9ycyBhbmQgdGhlbiByZXBhaXIKICBpZiBncmVwIC0tcXVpZXQgIkVSUk9SIiAke291dHB1dF9kaXJfdG9wfS8iJHtzdGRlcnJfUEVfbmFtZX0iLmZhc3RwLXRyaW0uc3RkZXJyOyB0aGVuCiAgICAgIAogICAgcm0gJHtvdXRwdXRfZGlyX3RvcH0vIiR7UjFfc2FtcGxlX25hbWV9Ii5mYXN0cC10cmltLmZxLmd6CiAgICBybSAke291dHB1dF9kaXJfdG9wfS8iJHtSMl9zYW1wbGVfbmFtZX0iLmZhc3RwLXRyaW0uZnEuZ3oKICAgIAogICAgCiAgICAke2JibWFwX3JlcGFpcn0gXAogICAgaW4xPSR7ZmFzdHFfYXJyYXlfUjFbaW5kZXhdfSBcCiAgICBpbjI9JHtmYXN0cV9hcnJheV9SMltpbmRleF19IFwKICAgIG91dDE9IiR7UjFfc2FtcGxlX25hbWV9Ii5SRVBBSVJFRC5mYXN0cS5neiBcCiAgICBvdXQyPSIke1IyX3NhbXBsZV9uYW1lfSIuUkVQQUlSRUQuZmFzdHEuZ3ogXAogICAgb3V0cz0vZGV2L251bGwgXAogICAgMj4gIiR7UjFfc2FtcGxlX25hbWV9Ii5SRVBBSVJFRC5zdGRlcnIKICAgIAogICAgJHtmYXN0cH0gXAogICAgLS1pbjEgIiR7UjFfc2FtcGxlX25hbWV9Ii5SRVBBSVJFRC5mYXN0cS5neiBcCiAgICAtLWluMiAiJHtSMl9zYW1wbGVfbmFtZX0iLlJFUEFJUkVELmZhc3RxLmd6IFwKICAgIC0tZGV0ZWN0X2FkYXB0ZXJfZm9yX3BlIFwKICAgIC0tdHJpbV9wb2x5X2cgXAogICAgLS10cmltX3BvbHlfeCBcCiAgICAtLXRocmVhZCAxNiBcCiAgICAtLXRyaW1fZnJvbnQxIDE1IFwKICAgIC0tdHJpbV9mcm9udDIgMTUgXAogICAgLS1odG1sICR7b3V0cHV0X2Rpcl90b3B9LyIke1IxX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5SRVBBSVJFRC5yZXBvcnQuaHRtbCBcCiAgICAtLWpzb24gJHtvdXRwdXRfZGlyX3RvcH0vIiR7UjFfc2FtcGxlX25hbWV9Ii5mYXN0cC10cmltLlJFUEFJUkVELnJlcG9ydC5qc29uIFwKICAgIC0tb3V0MSAke291dHB1dF9kaXJfdG9wfS8iJHtSMV9zYW1wbGVfbmFtZX0iLmZhc3RwLXRyaW0uUkVQQUlSRUQuZnEuZ3ogXAogICAgLS1vdXQyICR7b3V0cHV0X2Rpcl90b3B9LyIke1IyX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5SRVBBSVJFRC5mcS5neiBcCiAgICAyPiAke291dHB1dF9kaXJfdG9wfS8iJHtzdGRlcnJfUEVfbmFtZX0iLmZhc3RwLXRyaW0uUkVQQUlSRUQuc3RkZXJyCiAKICAgIAogICAgaWYgZ3JlcCAtLXF1aWV0ICJFUlJPUiIgJHtvdXRwdXRfZGlyX3RvcH0vIiR7c3RkZXJyX1BFX25hbWV9Ii5mYXN0cC10cmltLlJFUEFJUkVELnN0ZGVycjsgdGhlbgogICAgICBlY2hvICJUaGVzZSAke3N0ZGVycl9QRV9uYW1lfSBzYW1wbGVzIGFyZSBicm9rZW4uIgogICAgICBlY2hvICJKdXN0IGdpdmUgdXAuICA6KCIKICAgICAgZWNobyAiIgogICAgZmkKICBmaQoKICBlY2hvICJGaW5pc2hlZCB0cmltbWluZzoiCiAgZWNobyAiJHtmYXN0cV9hcnJheV9SMVtpbmRleF19IgogIGVjaG8gIiR7ZmFzdHFfYXJyYXlfUjFbaW5kZXhdfSIKICBlY2hvICIiCiAgCiAgIyBHZW5lcmF0ZSBtZDUgY2hlY2tzdW1zIGZvciBuZXdseSB0cmltbWVkIGZpbGVzCiAgY2QgIiR7b3V0cHV0X2Rpcl90b3B9IgogIG1kNXN1bSAiJHtSMV9zYW1wbGVfbmFtZX0iLmZhc3RwLXRyaW0uZnEuZ3ogPiAiJHtSMV9zYW1wbGVfbmFtZX0iLmZhc3RwLXRyaW0uZnEuZ3oubWQ1CiAgbWQ1c3VtICIke1IyX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5mcS5neiA+ICIke1IyX3NhbXBsZV9uYW1lfSIuZmFzdHAtdHJpbS5mcS5nei5tZDUKICAKICBjZCAiJHtyYXdfcmVhZHNfZGlyfSIKZG9uZQpgYGAKCgoKCgojIEZhc3RRQwpgYGB7ciBmYXN0cWMsIGVuZ2luZT0nYmFzaCd9CiMgTG9hZCBiYXNoIHZhcmlhYmxlcyBpbnRvIG1lbW9yeQpzb3VyY2UgLmJhc2h2YXJzCgpjZCAiJHtvdXRwdXRfZGlyX3RvcH0iCgojIyMjIyMjIyMjIyMgUlVOIEZBU1RRQyAjIyMjIyMjIyMjIyMKCgojIENyZWF0ZSBhcnJheSBvZiB0cmltbWVkIEZhc3RRcwp0cmltbWVkX2Zhc3Rxc19hcnJheT0oJHt0cmltbWVkX2Zhc3RxX3BhdHRlcm59KQoKIyBQYXNzIGFycmF5IGNvbnRlbnRzIHRvIG5ldyB2YXJpYWJsZSBhcyBzcGFjZS1kZWxpbWl0ZWQgbGlzdAp0cmltbWVkX2Zhc3RxY19saXN0PSQoZWNobyAiJHt0cmltbWVkX2Zhc3Rxc19hcnJheVsqXX0iKQoKZWNobyAiQmVnaW5uaW5nIEZhc3RRQyBvbiB0cmltbWVkIHJlYWRzLi4uIgplY2hvICIiCgojIFJ1biBGYXN0UUMKIyMjIE5PVEU6IERvIE5PVCBxdW90ZSByYXdfZmFzdHFjX2xpc3QKJHtmYXN0cWN9IFwKLS10aHJlYWRzICR7dGhyZWFkc30gXAotLW91dGRpciAiJHtvdXRwdXRfZGlyX3RvcH0iIFwKLS1xdWlldCBcCiR7dHJpbW1lZF9mYXN0cWNfbGlzdH0KCmVjaG8gIkZhc3RRQyBvbiB0cmltbWVkIHJlYWRzIGNvbXBsZXRlISIKZWNobyAiIgoKIyMjIyMjIyMjIyMjIEVORCBGQVNUUUMgIyMjIyMjIyMjIyMjCmBgYAoKCgojIE11bHRpUUMKYGBge3IgbXVsdGlxYywgZW5naW5lPSdiYXNoJ30KIyBMb2FkIGJhc2ggdmFyaWFibGVzIGludG8gbWVtb3J5CnNvdXJjZSAuYmFzaHZhcnMKCmNkICIke291dHB1dF9kaXJfdG9wfSIKCiR7bXVsdGlxY30gLgpgYGA=