Goals

  • Remove primers and adapters from our sequences.
  • While doing so, we’ll learn:
    • How to load a conda environment.
    • Some basics of writing a shell script.
    • How to run cutadapt.
    • How to submit and monitor a job with the SLURM scheduler.

Before We Get Started

Notes

  • FYI: You can download this Rmd (R Markdown) file by clicking the Code button in the top-right of this page. To convert an Rmd file to an R script, type the following in an R console: knitr::purl(input="<filename>.Rmd").

Setup

  • Login to OSC at https://ondemand.osc.edu.

  • Enter a terminal in your browser at OSC by clicking Cluster > Pitzer Shell Access. (Or use this direct link.)

  • Go to this your directory for this workshop at OSC:

    cd /fs/project/PAS0471/workshops/2020-12_micro/$USER

Getting Started

cutadapt is a software package to remove any adapters and primers that may be present in raw sequence data.

We will use the output from cutadapt as input for the next step in our workflow, ASV Infererence and Taxon Assignment.

Primers

These are the primers that were used in this amplicon sequencing experiment.

  • 515F: GAGTGYCAGCMGCCGCGGTAA
  • 806R: ACGGACTACNVGGGTWTCTAAT
  • 515F reverse complement: TTACCGCGGCKGCTGRCACTC
  • 806R reverse complement: ATTAGAWACCCBNGTAGTCCGT

We will specify these primers when running cutadapt, in order to remove them from the fastq files.

For this tutorial, the original fastq files have been subsampled to about 1/3 of their original size to shorten the time needed for computation.

Load the conda environment with cutadapt

cutadapt is not available as a module at OSC. However, it can be easily installed there through conda, see this webpage. We have already done this for you to save some time during this tutorial, so we just need to load the conda environment.

To work with conda at OSC, we first need to load the conda module:

module load python/3.6-conda5.2

Assuming that you haven’t worked with conda at OSC before, we also need to a bit of additional one-time setup:

# This will add a line to your bash config file, which runs every time you start
# a shell, to run the conda setup script:
echo ". /apps/python/3.6-conda5.2/etc/profile.d/conda.sh" >> ~/.bashrc

# Next, we source (run) the config file in our current shell to also run the conda
# setup script right now:
source ~/.bashrc

Then, we activate our conda environment for cutadapt:

conda activate /users/PAS0471/osu5685/.conda/envs/cutadaptenv


Now, cutadapt should be in $PATH, i.e. we can simply call it by its name to run it. To test this:

cutadapt --version     # Will just print the version number
cutadapt --help        # Will print a whole lot of documentation

A Script to Run cutadapt

Part A: Boilerplate and SLURM directives

  1. Our first line, #!/bin/bash, is the “shebang” line that tells us what program (language) our script uses, which is bash in this case.

  2. We’ll provide the SLURM directives one line at a time, using the #SBATCH keyword followed by an option: nodes for the number of nodes, ntasks-per-node for the number of cores, time for the maximum walltime of the job (here specificed using just minutes), and account for the OSC project that should be billed.

  3. We’ll set a couple of options with set that make bash run safer, by making it stop the script whenever an error is encountered (by default, the script will keep running.)

  4. We’ll use an echo statement to print to screen what script we are running, and the date command to keep track of the script’s runtime. (The -e option to echo allows us to print newlines using \n).

#!/bin/bash                   # 1. First line should be the shebang line
#SBATCH --nodes=1             # 2. Provide sbatch directives
#SBATCH --ntasks-per-node=1
#SBATCH --time=60
#SBATCH --account=PAS0471

# 3. Run bash in "safe mode", basically
set -e -u -o pipefail    

# 4. Report:
echo -e "\n## Starting cutadapt script."
date

Part B: Parse arguments and set up directories

  1. So-called “positional” arguments that are passed onto a script using the syntax ./script.sh arg1 arg2 are represented inside a script as the variables $1, $2, etc. First, we will give these variables more informative names.

  2. We’ll create the output directory if it doesn’t already exist. Using the -p option, mkdir can create multiple levels at once, and will not complain if the dir(s) already exist.

  3. We’ll report the directory variables to screen, which will be helpful e.g. if we’d need to debug our script.

  4. We’ll save the primer sequences as variables.

# SETUP --------------------------------------------------------
# 1. Command-line arguments:
indir=$1
outdir=$2

# 2. Create output directory if it doesn't already exist:
mkdir -p $outdir

# 3. Report:
echo "## Input dir:       $indir"
echo "## Output dir:      $outdir"

# 4. Define primers:
primer_f=GAGTGYCAGCMGCCGCGGTAA
primer_r=ACGGACTACNVGGGTWTCTAAT

primer_f_rc=TTACCGCGGCKGCTGRCACTC
primer_r_rc=ATTAGAWACCCBNGTAGTCCGT

Part C: Loop through fastq files and run cutadapt for each pair:

We will run cutadapt separately for each sample, using a for loop to cycle through all the files. This is made a little more complicated because we have two files for each sample: one with forwards reads (which has “R1” in its filename) and one with reverse reads (“R2”).

  1. We’ll initialize the for loop, which is done using for x in xyz syntax. We’ll loop over all the “R1” fastq files, i.e. the ones with the forward reads. We list all of those by taking advantage of the * wildcard.

  2. Now, $R1 (defined using R1 in the loop initiation line) contains, in each rendition of the loop, a single file name. Because it also contains the dir name, we will extract the file name only using the basename command. The $() notation is called command substitution, and simply allows us to assign the output of a command to a variable.

  3. To get the corresponding file with reverse read (which should always have the exact same name except containing “R2” rather than “R1”), we simply substitute “R1” by “R2” in the file name using a little parameter substitution trick with the syntax ${variable_name/pattern/replacement}.

  4. To make sure all is well, we list the R1 and R2 input files.

  5. Now, we’ll do the primer removal using cutadapt. We use \ just to allow the command to continue across multiple lines for easy of reading (the \ “escapes” the return/newline).

  6. We indicate the end of the loop with the keyword done.

# RUN CUTADAPT --------------------------------------------------------
# 1. Initialize the loop:
for R1 in $indir/*_R1_*.fastq.gz
do
  # 2. Get the filenames for the fastq files with forward (R1) and reverse (R2) reads:
  R1=$(basename $R1)   # `basename` will strip the directory from the name
  
  # 3. 
  R2=${R1/_R1_/_R2_}   # This will substitute "_R1_" with "_R2_" in the filename.
  
  # 4. Report input files:
  echo "## R1 input file:"
  ls -lh $indir/$R1
  echo "## R2 input file:"
  ls -lh $indir/$R2

  # 5. Run cutadapt:
  echo -e "\n\n## Running cutadapt..."
    
  cutadapt -a $primer_f...$primer_r_rc -A $primer_r...$primer_f_rc \
      --discard-untrimmed --pair-filter=any \
      -o $outdir/$R1 -p $outdir/$R2 $indir/$R1 $indir/$R2
    
  # 6. Exit the loop using the `done` keyword
done

Part D: List output files as a check:

Finally, we’ll list our output files, as another way to check whether the script has run as it was supposed, and we’ll also print the date again to check the (date and) time that our script finished running.

# REPORT AND FINALIZE --------------------------------------------------------
echo -e "\n## Listing output files:"
ls -lh $outdir

echo -e "\n## Done with cutadapt script."
date

The entire script

Click here to see the entire script.

#!/bin/bash
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --time=60
#SBATCH --account=PAS0471

set -e -u -o pipefail    # Run bash in "safe mode", basically

# Report:
echo -e "\n## Starting cutadapt script."
date

# SETUP --------------------------------------------------------
# Command-line arguments:
indir=$1
outdir=$2

# Create output directory if it doesn't already exist:
mkdir -p $outdir

# Report:
echo "## Input dir:       $indir"
echo "## Output dir:      $outdir"

# Primers:
primer_f=GAGTGYCAGCMGCCGCGGTAA
primer_r=ACGGACTACNVGGGTWTCTAAT

primer_f_rc=TTACCGCGGCKGCTGRCACTC
primer_r_rc=ATTAGAWACCCBNGTAGTCCGT

# Software - making doubly sure this is all loaded:
module load python/3.6-conda5.2                     # Load conda module
. /apps/python/3.6-conda5.2/etc/profile.d/conda.sh  # Setup conda (should be superfluous)
conda activate /users/PAS0471/osu5685/.conda/envs/cutadaptenv # Activate cutadapt environment

# RUN CUTADAPT --------------------------------------------------------
echo -e "\n## Looping through input files...\n"

for R1 in $indir/*_R1_*.fastq.gz
do
  R1=$(basename $R1)
  R2=${R1/_R1_/_R2_}
  
  # Report input files:
  echo "## R1 input file:"
  ls -lh $indir/$R1
  echo "## R2 input file:"
  ls -lh $indir/$R2

  # Trim:
  echo -e "\n\n## Running cutadapt..."
    
  cutadapt -a $primer_f...$primer_r_rc -A $primer_r...$primer_f_rc \
      --discard-untrimmed --pair-filter=any \
      -o $outdir/$R1 -p $outdir/$R2 $indir/$R1 $indir/$R2
    
  # Options:
  # "-a"/"-A": Primers for R1/R2
  # "--discard-untrimmed": Remove pairs with no primer found
  # "--pair-filter=any": Remove pair if one read is filtered (=Default)

  echo -e "\n\n ------------------------------------------------------------\n"
done

# REPORT AND FINALIZE --------------------------------------------------------
echo -e "\n## Listing output files:"
ls -lh $outdir

echo -e "\n## Done with cutadapt script."
date

Submit the Script

We’ll first check whether we are in the correct directory, so that our relative paths will work:

# Let's check we are in the correct directory:
pwd
# Should return: /fs/project/PAS0471/workshops/2020-12_micro/<your-username>
# If not, run: "cd /fs/project/PAS0471/workshops/2020-12_micro/$USER"


Then we assign our input and output directories to the variables indir and outdir, respectively.

We’ll also assign the name we want to give to the SLURM log file to a variable. Here, we’ll also use the %j SLURM keyword to include the SLURM job number.

indir=data/raw/fastq_subsample
outdir=data/processed/fastq_trimmed

# Give a name to the SLURM output file:
# (The -o flag sets the SLURM log file name, and `%j` represents the job ID)
slurm_file=slurm-cutadapt-$USER-%j.out


Finally, we’re ready to submit the script!

sbatch -o $slurm_file scripts/01-cutadapt.sh $indir $outdir
# Submitted batch job 2526085

Monitor the Job and Its Output

Monitor the job

We can check what’s going on with our job using squeue:

# Initially the job may be queued ("PD" in the "ST" (status) column):
squeue -u $USER
# JOBID   PARTITION     NAME       USER    ST      TIME  NODES NODELIST(REASON)
# 2526085 serial-40     01-cutad   jelmer  PD       0:00      1 (None) 

# Then the job should be running ("R" in the "ST" column):
squeue -u $USER
# JOBID   PARTITION     NAME       USER    ST      TIME  NODES NODELIST(REASON) 
# 2526085 serial-40     01-cutad   jelmer  R       0:02      1 p0002 

# When the job has finished, squeue will return nothing (!):
squeue -u $USER
# JOBID PARTITION     NAME     USER ST       TIME  NODES NODELIST(REASON) 


We could also get more statistics about our job using scontrol:

scontrol show job 2526085   # Replace the number with your JOBID

# UserId=jelmer(33227) GroupId=PAS0471(3773) MCS_label=N/A
# Priority=200005206 Nice=0 Account=pas0471 QOS=pitzer-default
# JobState=RUNNING Reason=None Dependency=(null)
# Requeue=1 Restarts=0 BatchFlag=1 Reboot=0 ExitCode=0:0
# RunTime=00:02:00 TimeLimit=01:00:00 TimeMin=N/A
# SubmitTime=2020-12-14T14:32:44 EligibleTime=2020-12-14T14:32:44
# AccrueTime=2020-12-14T14:32:44
# StartTime=2020-12-14T14:32:47 EndTime=2020-12-14T15:32:47 Deadline=N/A
# SuspendTime=None SecsPreSuspend=0 LastSchedEval=2020-12-14T14:32:47
# Partition=serial-40core AllocNode:Sid=pitzer-login01:57954
# ReqNodeList=(null) ExcNodeList=(null)
# NodeList=p0002
# BatchHost=p0002
# NumNodes=1 NumCPUs=1 NumTasks=1 CPUs/Task=1 ReqB:S:C:T=0:0:*:*
# TRES=cpu=1,mem=4556M,node=1,billing=1,gres/gpfs:project=0
# Socks/Node=* NtasksPerN:B:S:C=1:0:*:1 CoreSpec=*
# MinCPUsNode=1 MinMemoryCPU=4556M MinTmpDiskNode=0
# Features=(null) DelayBoot=00:00:00
# OverSubscribe=OK Contiguous=0 Licenses=(null) Network=(null)
# Command=/fs/project/PAS0471/workshops/2020-12_micro/master/scripts/01-cutadapt.sh data/raw/fastq_subsample # da/processed/fastq_trimmed
# WorkDir=/fs/project/PAS0471/workshops/2020-12_micro/master
# Comment=stdout=/fs/project/PAS0471/workshops/2020-12_micro/master/slurm-cutadapt-jelmer-2526085.out 
# StdErr=/fs/project/PAS0471/workshops/2020-12_micro/master/slurm-cutadapt-jelmer-2526085.out
# StdIn=/dev/null
# StdOut=/fs/project/PAS0471/workshops/2020-12_micro/master/slurm-cutadapt-jelmer-2526085.out
# Power=
# TresPerNode=gpfs:project:1
# MailUser=jelmer MailType=NONE

Check the output

Let’s see if we have a log file:

ls slurm*
# slurm-cutadapt-jelmer-2451088.out

Let’s look at the start of the SLURM log file:

head -n 20 slurm-cutadapt-jelmer-2526085.out

# ## Starting cutadapt script.
# Mon Dec 14 14:32:48 EST 2020
# ## Input dir:       data/raw/fastq_subsample
# ## Output dir:      data/processed/fastq_trimmed
# 
# ## Looping through input files...
# 
# ## R1 input file:
# -rw-r--r-- 1 jelmer PAS0471 3.1M Dec 13 14:10 data/raw/fastq_subsample/101-S1-V4-V5_S1_L001_R1_001.fastq.gz
# ## R2 input file:
# -rw-r--r-- 1 jelmer PAS0471 3.8M Dec 13 14:10 data/raw/fastq_subsample/101-S1-V4-V5_S1_L001_R2_001.fastq.gz
# 
# 
# ## Running cutadapt...
# This is cutadapt 3.1 with Python 3.8.6
# Command line parameters: -a GAGTGYCAGCMGCCGCGGTAA...ATTAGAWACCCBNGTAGTCCGT -A # ACGGACTACNVGGGTWTCTAAT...TTACCGCGGCKGCTGRCACTC --discard-untrimmed --pair-filter=any -o # data/processed/fastq_trimmed/101-S1-V4-V5_S1_L001_R1_001.fastq.gz -p # data/processed/fastq_trimmed/101-S1-V4-V5_S1_L001_R2_001.fastq.gz # data/raw/fastq_subsample/101-S1-V4-V5_S1_L001_R1_001.fastq.gz # data/raw/fastq_subsample/101-S1-V4-V5_S1_L001_R2_001.fastq.gz
# Processing reads on 1 core in paired-end mode ...
# Finished in 2.60 s (113 µs/read; 0.53 M reads/minute).

Let’s also look at the end of the file:

tail slurm-cutadapt-jelmer-2526085.out

# -rw-r--r-- 1 jelmer PAS0471 2.2M Dec 14 14:35 604-S4-V4-V5_S72_L001_R2_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 398K Dec 14 14:35 blankM-V4-V5_S73_L001_R1_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 486K Dec 14 14:35 blankM-V4-V5_S73_L001_R2_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471  23K Dec 14 14:35 H20-V4-V5_S88_L001_R1_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471  26K Dec 14 14:35 H20-V4-V5_S88_L001_R2_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 1.4M Dec 14 14:35 Zymo-V4-V5_S82_L001_R1_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 1.7M Dec 14 14:35 Zymo-V4-V5_S82_L001_R2_001.fastq.gz
# 
# ## Done with cutadapt script.
# Mon Dec 14 14:35:19 EST 2020


Finally, we can directly check the output dir:

ls -lh data/processed/fastq_trimmed/    # Or use $outdir

# total 178M
# -rw-r--r-- 1 jelmer PAS0471 3.0M Dec 14 14:32 101-S1-V4-V5_S1_L001_R1_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 3.6M Dec 14 14:32 101-S1-V4-V5_S1_L001_R2_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 3.7M Dec 14 14:33 101-S4-V4-V5_S49_L001_R1_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 4.5M Dec 14 14:33 101-S4-V4-V5_S49_L001_R2_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 2.6M Dec 14 14:33 102-S4-V4-V5_S50_L001_R1_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 3.1M Dec 14 14:33 102-S4-V4-V5_S50_L001_R2_001.fastq.gz
# -rw-r--r-- 1 jelmer PAS0471 2.4M Dec 14 14:33 103-S4-V4-V5_S51_L001_R1_001.fastq.gz
# ....[other files not shown]


All done! Our next step will be to further process the fastq files with cutadapt.




LS0tCnRpdGxlOiAiPGJyPldvcmtmbG93IHBhcnQgSTo8YnI+UHJpbWVyIGFuZCBBZGFwdGVyIFJlbW92YWwiCm91dHB1dDoKICBybWFya2Rvd246Omh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY2VydWxlYW4KICAgIGhpZ2hsaWdodDogemVuYnVybgogICAgY29kZV9kb3dubG9hZDogdHJ1ZQogICAgdG9jOiB0cnVlCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGFuY2hvcl9zZWN0aW9uczogdHJ1ZQotLS0KCjxicj4KCmBgYHtyIGtuaXRyX29wdGlvbnMsIGVjaG89RkFMU0V9CmtuaXRyOjpvcHRzX2NodW5rJHNldChldmFsID0gRkFMU0UpCmBgYAogCi0tLS0tCgojIyBHb2FscwoKLSBSZW1vdmUgcHJpbWVycyBhbmQgYWRhcHRlcnMgZnJvbSBvdXIgc2VxdWVuY2VzLgotIFdoaWxlIGRvaW5nIHNvLCB3ZSdsbCBsZWFybjoKICAtIEhvdyB0byBsb2FkIGEgYGNvbmRhYCBlbnZpcm9ubWVudC4KICAtIFNvbWUgYmFzaWNzIG9mIHdyaXRpbmcgYSBzaGVsbCBzY3JpcHQuCiAgLSBIb3cgdG8gcnVuIGBjdXRhZGFwdGAuCiAgLSBIb3cgdG8gc3VibWl0IGFuZCBtb25pdG9yIGEgam9iIHdpdGggdGhlICpTTFVSTSogc2NoZWR1bGVyLiAKCi0tLS0tCgojIyBCZWZvcmUgV2UgR2V0IFN0YXJ0ZWQKCiMjIyBOb3RlcwoKLSBGWUk6IFlvdSBjYW4gZG93bmxvYWQgdGhpcyBgUm1kYCAoUiBNYXJrZG93bikgZmlsZSBieSBjbGlja2luZyB0aGUgYENvZGVgCiAgYnV0dG9uIGluIHRoZSB0b3AtcmlnaHQgb2YgdGhpcyBwYWdlLgogIFRvIGNvbnZlcnQgYW4gYFJtZGAgZmlsZSB0byBhbiBSIHNjcmlwdCwKICB0eXBlIHRoZSBmb2xsb3dpbmcgaW4gYW4gUiBjb25zb2xlOiBga25pdHI6OnB1cmwoaW5wdXQ9IjxmaWxlbmFtZT4uUm1kIilgLgoKIyMjIFNldHVwCgotIExvZ2luIHRvIE9TQyBhdCA8aHR0cHM6Ly9vbmRlbWFuZC5vc2MuZWR1Pi4KCi0gRW50ZXIgYSB0ZXJtaW5hbCBpbiB5b3VyIGJyb3dzZXIgYXQgT1NDIGJ5IGNsaWNraW5nIGBDbHVzdGVyYCA+IGBQaXR6ZXIgU2hlbGwgQWNjZXNzYC4KICAoT3IgdXNlIFt0aGlzIGRpcmVjdCBsaW5rXShodHRwczovL29uZGVtYW5kLm9zYy5lZHUvcHVuL3N5cy9zaGVsbC9zc2gvcGl0emVyLm9zYy5lZHUpLikKCi0gR28gdG8gdGhpcyB5b3VyIGRpcmVjdG9yeSBmb3IgdGhpcyB3b3Jrc2hvcCBhdCBPU0M6CgogIGBgYHtiYXNofQogIGNkIC9mcy9wcm9qZWN0L1BBUzA0NzEvd29ya3Nob3BzLzIwMjAtMTJfbWljcm8vJFVTRVIKICBgYGAKCi0tLS0tCgojIyBHZXR0aW5nIFN0YXJ0ZWQKCltgY3V0YWRhcHRgXShodHRwczovL2N1dGFkYXB0LnJlYWR0aGVkb2NzLmlvL2VuL3N0YWJsZS9pbmRleC5odG1sKSBpcyBhIHNvZnR3YXJlCnBhY2thZ2UgdG8gcmVtb3ZlIGFueSBhZGFwdGVycyBhbmQgcHJpbWVycyB0aGF0IG1heSBiZSBwcmVzZW50IGluIHJhdyBzZXF1ZW5jZSBkYXRhLgoKV2Ugd2lsbCB1c2UgdGhlIG91dHB1dCBmcm9tIGBjdXRhZGFwdGAgYXMgaW5wdXQgZm9yIHRoZSBuZXh0IHN0ZXAgaW4gb3VyIHdvcmtmbG93LApbQVNWIEluZmVyZXJlbmNlIGFuZCBUYXhvbiBBc3NpZ25tZW50XSgwNy1BU1YtaW5mZXJlbmNlLmh0bWwpLgoKCiMjIyBQcmltZXJzCgpUaGVzZSBhcmUgdGhlIHByaW1lcnMgdGhhdCB3ZXJlIHVzZWQgaW4gdGhpcyBhbXBsaWNvbiBzZXF1ZW5jaW5nIGV4cGVyaW1lbnQuCgotIDUxNUY6ICpHQUdUR1lDQUdDTUdDQ0dDR0dUQUEqCi0gODA2UjogKkFDR0dBQ1RBQ05WR0dHVFdUQ1RBQVQqCi0gNTE1RiByZXZlcnNlIGNvbXBsZW1lbnQ6ICpUVEFDQ0dDR0dDS0dDVEdSQ0FDVEMqCi0gODA2UiByZXZlcnNlIGNvbXBsZW1lbnQ6ICpBVFRBR0FXQUNDQ0JOR1RBR1RDQ0dUKgoKV2Ugd2lsbCBzcGVjaWZ5IHRoZXNlIHByaW1lcnMgd2hlbiBydW5uaW5nIGBjdXRhZGFwdGAsCmluIG9yZGVyIHRvIHJlbW92ZSB0aGVtIGZyb20gdGhlIGZhc3RxIGZpbGVzLgoKPGRpdiBjbGFzcz0iYWxlcnQgZnlpIj4KPGRpdj4KRm9yIHRoaXMgdHV0b3JpYWwsIHRoZSBvcmlnaW5hbCBmYXN0cSBmaWxlcyBoYXZlIGJlZW4gc3Vic2FtcGxlZCB0byBhYm91dCAxLzMgb2YKdGhlaXIgb3JpZ2luYWwgc2l6ZSB0byBzaG9ydGVuIHRoZSB0aW1lIG5lZWRlZCBmb3IgY29tcHV0YXRpb24uCjwvZGl2CjwvZGl2PgoKIyMjIExvYWQgdGhlIGBjb25kYWAgZW52aXJvbm1lbnQgd2l0aCBgY3V0YWRhcHRgIAoKYGN1dGFkYXB0YCBpcyBub3QgYXZhaWxhYmxlIGFzIGEgbW9kdWxlIGF0IE9TQy4KSG93ZXZlciwgaXQgY2FuIGJlIGVhc2lseSBpbnN0YWxsZWQgdGhlcmUgdGhyb3VnaCBgY29uZGFgLApzZWUgdGhpcyBbd2VicGFnZV0oaHR0cHM6Ly9jdXRhZGFwdC5yZWFkdGhlZG9jcy5pby9lbi9zdGFibGUvaW5zdGFsbGF0aW9uLmh0bWwjaW5zdGFsbGF0aW9uLXdpdGgtY29uZGEpLiBXZSBoYXZlIGFscmVhZHkgZG9uZSB0aGlzIGZvciB5b3UgdG8gc2F2ZSBzb21lIHRpbWUgZHVyaW5nIHRoaXMKdHV0b3JpYWwsIHNvIHdlIGp1c3QgbmVlZCB0byBsb2FkIHRoZSBgY29uZGFgIGVudmlyb25tZW50LgoKVG8gd29yayB3aXRoIGBjb25kYWAgYXQgT1NDLCB3ZSBmaXJzdCBuZWVkIHRvIGxvYWQgdGhlIGBjb25kYWAgbW9kdWxlOgoKYGBge2Jhc2h9Cm1vZHVsZSBsb2FkIHB5dGhvbi8zLjYtY29uZGE1LjIKYGBgCgpBc3N1bWluZyB0aGF0IHlvdSBoYXZlbid0IHdvcmtlZCB3aXRoIGBjb25kYWAgYXQgT1NDIGJlZm9yZSwKd2UgYWxzbyBuZWVkIHRvIGEgYml0IG9mIGFkZGl0aW9uYWwgb25lLXRpbWUgc2V0dXA6CgpgYGB7YmFzaH0KIyBUaGlzIHdpbGwgYWRkIGEgbGluZSB0byB5b3VyIGJhc2ggY29uZmlnIGZpbGUsIHdoaWNoIHJ1bnMgZXZlcnkgdGltZSB5b3Ugc3RhcnQKIyBhIHNoZWxsLCB0byBydW4gdGhlIGNvbmRhIHNldHVwIHNjcmlwdDoKZWNobyAiLiAvYXBwcy9weXRob24vMy42LWNvbmRhNS4yL2V0Yy9wcm9maWxlLmQvY29uZGEuc2giID4+IH4vLmJhc2hyYwoKIyBOZXh0LCB3ZSBzb3VyY2UgKHJ1bikgdGhlIGNvbmZpZyBmaWxlIGluIG91ciBjdXJyZW50IHNoZWxsIHRvIGFsc28gcnVuIHRoZSBjb25kYQojIHNldHVwIHNjcmlwdCByaWdodCBub3c6CnNvdXJjZSB+Ly5iYXNocmMKYGBgCgpUaGVuLCB3ZSBhY3RpdmF0ZSBvdXIgYGNvbmRhYCBlbnZpcm9ubWVudCBmb3IgYGN1dGFkYXB0YDoKCmBgYHtiYXNofQpjb25kYSBhY3RpdmF0ZSAvdXNlcnMvUEFTMDQ3MS9vc3U1Njg1Ly5jb25kYS9lbnZzL2N1dGFkYXB0ZW52CmBgYAoKPGJyPgoKTm93LCBgY3V0YWRhcHRgIHNob3VsZCBiZSBpbiBgJFBBVEhgLCBpLmUuIHdlIGNhbiBzaW1wbHkgY2FsbCBpdCBieSBpdHMgbmFtZSB0byBydW4gaXQuClRvIHRlc3QgdGhpczoKCmBgYHtiYXNofQpjdXRhZGFwdCAtLXZlcnNpb24gICAgICMgV2lsbCBqdXN0IHByaW50IHRoZSB2ZXJzaW9uIG51bWJlcgpjdXRhZGFwdCAtLWhlbHAgICAgICAgICMgV2lsbCBwcmludCBhIHdob2xlIGxvdCBvZiBkb2N1bWVudGF0aW9uCmBgYAoKCi0tLS0tCgojIyBBIFNjcmlwdCB0byBSdW4gYGN1dGFkYXB0YAoKIyMjIFBhcnQgQTogQm9pbGVycGxhdGUgYW5kICpTTFVSTSogZGlyZWN0aXZlcwoKMS4gT3VyIGZpcnN0IGxpbmUsIGAjIS9iaW4vYmFzaGAsIGlzIHRoZSAqInNoZWJhbmciKiBsaW5lIHRoYXQgdGVsbHMgdXMgd2hhdAogIHByb2dyYW0gKGxhbmd1YWdlKSBvdXIgc2NyaXB0IHVzZXMsIHdoaWNoIGlzIGBiYXNoYCBpbiB0aGlzIGNhc2UuCgoyLiBXZSdsbCBwcm92aWRlIHRoZSBgU0xVUk1gIGRpcmVjdGl2ZXMgb25lIGxpbmUgYXQgYSB0aW1lLAogIHVzaW5nIHRoZSBgI1NCQVRDSGAga2V5d29yZCBmb2xsb3dlZCBieSBhbiBvcHRpb246IGBub2Rlc2AgZm9yIHRoZSBudW1iZXIgb2YKICBub2RlcywgYG50YXNrcy1wZXItbm9kZWAgZm9yIHRoZSBudW1iZXIgb2YgY29yZXMsIGB0aW1lYCBmb3IgdGhlIG1heGltdW0KICB3YWxsdGltZSBvZiB0aGUgam9iIChoZXJlIHNwZWNpZmljZWQgdXNpbmcganVzdCBtaW51dGVzKSwgYW5kIGBhY2NvdW50YCBmb3IKICB0aGUgT1NDIHByb2plY3QgdGhhdCBzaG91bGQgYmUgYmlsbGVkLgoKMy4gV2UnbGwgc2V0IGEgY291cGxlIG9mIG9wdGlvbnMgd2l0aCBgc2V0YCB0aGF0IG1ha2UgYGJhc2hgIHJ1biBzYWZlciwKICBieSBtYWtpbmcgaXQgc3RvcCB0aGUgc2NyaXB0IHdoZW5ldmVyIGFuIGVycm9yIGlzIGVuY291bnRlcmVkIChieSBkZWZhdWx0LAogIHRoZSBzY3JpcHQgd2lsbCBrZWVwIHJ1bm5pbmcuKQoKNC4gV2UnbGwgdXNlIGFuIGBlY2hvYCBzdGF0ZW1lbnQgdG8gcHJpbnQgdG8gc2NyZWVuIHdoYXQgc2NyaXB0IHdlIGFyZQogIHJ1bm5pbmcsIGFuZCB0aGUgYGRhdGVgIGNvbW1hbmQgdG8ga2VlcCB0cmFjayBvZiB0aGUgc2NyaXB0J3MgcnVudGltZS4KICAoVGhlIGAtZWAgb3B0aW9uIHRvIGBlY2hvYCBhbGxvd3MgdXMgdG8gcHJpbnQgbmV3bGluZXMgdXNpbmcgYFxuYCkuCgpgYGB7YmFzaH0KIyEvYmluL2Jhc2ggICAgICAgICAgICAgICAgICAgIyAxLiBGaXJzdCBsaW5lIHNob3VsZCBiZSB0aGUgc2hlYmFuZyBsaW5lCiNTQkFUQ0ggLS1ub2Rlcz0xICAgICAgICAgICAgICMgMi4gUHJvdmlkZSBzYmF0Y2ggZGlyZWN0aXZlcwojU0JBVENIIC0tbnRhc2tzLXBlci1ub2RlPTEKI1NCQVRDSCAtLXRpbWU9NjAKI1NCQVRDSCAtLWFjY291bnQ9UEFTMDQ3MQoKIyAzLiBSdW4gYmFzaCBpbiAic2FmZSBtb2RlIiwgYmFzaWNhbGx5CnNldCAtZSAtdSAtbyBwaXBlZmFpbCAgICAKCiMgNC4gUmVwb3J0OgplY2hvIC1lICJcbiMjIFN0YXJ0aW5nIGN1dGFkYXB0IHNjcmlwdC4iCmRhdGUKYGBgCgojIyMgUGFydCBCOiBQYXJzZSBhcmd1bWVudHMgYW5kIHNldCB1cCBkaXJlY3RvcmllcwoKMS4gU28tY2FsbGVkICJwb3NpdGlvbmFsIiBhcmd1bWVudHMgdGhhdCBhcmUgcGFzc2VkIG9udG8gYSBzY3JpcHQgdXNpbmcgdGhlCiAgIHN5bnRheCBgLi9zY3JpcHQuc2ggYXJnMSBhcmcyYCBhcmUgcmVwcmVzZW50ZWQgaW5zaWRlIGEgc2NyaXB0IGFzIHRoZQogICB2YXJpYWJsZXMgYCQxYCwgYCQyYCwgZXRjLiBGaXJzdCwgd2Ugd2lsbCBnaXZlIHRoZXNlIHZhcmlhYmxlcyBtb3JlCiAgIGluZm9ybWF0aXZlIG5hbWVzLgoKMi4gV2UnbGwgY3JlYXRlIHRoZSBvdXRwdXQgZGlyZWN0b3J5IGlmIGl0IGRvZXNuJ3QgYWxyZWFkeSBleGlzdC4KICAgVXNpbmcgdGhlIGAtcGAgb3B0aW9uLCBgbWtkaXJgIGNhbiBjcmVhdGUgbXVsdGlwbGUgbGV2ZWxzIGF0IG9uY2UsCiAgIGFuZCB3aWxsIG5vdCBjb21wbGFpbiBpZiB0aGUgZGlyKHMpIGFscmVhZHkgZXhpc3QuCgozLiBXZSdsbCByZXBvcnQgdGhlIGRpcmVjdG9yeSB2YXJpYWJsZXMgdG8gc2NyZWVuLAogICB3aGljaCB3aWxsIGJlIGhlbHBmdWwgZS5nLiBpZiB3ZSdkIG5lZWQgdG8gZGVidWcgb3VyIHNjcmlwdC4KCjQuIFdlJ2xsIHNhdmUgdGhlIHByaW1lciBzZXF1ZW5jZXMgYXMgdmFyaWFibGVzLgoKYGBge2Jhc2h9CiMgU0VUVVAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxLiBDb21tYW5kLWxpbmUgYXJndW1lbnRzOgppbmRpcj0kMQpvdXRkaXI9JDIKCiMgMi4gQ3JlYXRlIG91dHB1dCBkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCBhbHJlYWR5IGV4aXN0Ogpta2RpciAtcCAkb3V0ZGlyCgojIDMuIFJlcG9ydDoKZWNobyAiIyMgSW5wdXQgZGlyOiAgICAgICAkaW5kaXIiCmVjaG8gIiMjIE91dHB1dCBkaXI6ICAgICAgJG91dGRpciIKCiMgNC4gRGVmaW5lIHByaW1lcnM6CnByaW1lcl9mPUdBR1RHWUNBR0NNR0NDR0NHR1RBQQpwcmltZXJfcj1BQ0dHQUNUQUNOVkdHR1RXVENUQUFUCgpwcmltZXJfZl9yYz1UVEFDQ0dDR0dDS0dDVEdSQ0FDVEMKcHJpbWVyX3JfcmM9QVRUQUdBV0FDQ0NCTkdUQUdUQ0NHVApgYGAKCiMjIyBQYXJ0IEM6IExvb3AgdGhyb3VnaCBgZmFzdHFgIGZpbGVzIGFuZCBydW4gYGN1dGFkYXB0YCBmb3IgZWFjaCBwYWlyOgoKV2Ugd2lsbCBydW4gYGN1dGFkYXB0YCBzZXBhcmF0ZWx5IGZvciBlYWNoIHNhbXBsZSwgdXNpbmcgYSBgZm9yYCBsb29wIHRvIGN5Y2xlCnRocm91Z2ggYWxsIHRoZSBmaWxlcy4KVGhpcyBpcyBtYWRlIGEgbGl0dGxlIG1vcmUgY29tcGxpY2F0ZWQgYmVjYXVzZSB3ZSBoYXZlICoqdHdvIGZpbGVzKiogZm9yIGVhY2ggc2FtcGxlOgpvbmUgd2l0aCBmb3J3YXJkcyByZWFkcyAod2hpY2ggaGFzICJSMSIgaW4gaXRzIGZpbGVuYW1lKQphbmQgb25lIHdpdGggcmV2ZXJzZSByZWFkcyAoIlIyIikuCgoxLiBXZSdsbCBpbml0aWFsaXplIHRoZSBgZm9yYCBsb29wLAogICB3aGljaCBpcyBkb25lIHVzaW5nIGBmb3IgeCBpbiB4eXpgIHN5bnRheC4KICAgV2UnbGwgbG9vcCBvdmVyIGFsbCB0aGUgIlIxIiBmYXN0cSBmaWxlcywKICAgaS5lLiB0aGUgb25lcyB3aXRoIHRoZSBmb3J3YXJkIHJlYWRzLgogICBXZSBsaXN0IGFsbCBvZiB0aG9zZSBieSB0YWtpbmcgYWR2YW50YWdlIG9mIHRoZSBgKmAgd2lsZGNhcmQuCgoyLiBOb3csIGAkUjFgIChkZWZpbmVkIHVzaW5nIGBSMWAgaW4gdGhlIGxvb3AgaW5pdGlhdGlvbiBsaW5lKSBjb250YWlucywKICAgaW4gZWFjaCByZW5kaXRpb24gb2YgdGhlIGxvb3AsIGEgc2luZ2xlIGZpbGUgbmFtZS4KICAgQmVjYXVzZSBpdCBhbHNvIGNvbnRhaW5zIHRoZSBkaXIgbmFtZSwgd2Ugd2lsbCBleHRyYWN0IHRoZSBmaWxlIG5hbWUgb25seQogICB1c2luZyB0aGUgYGJhc2VuYW1lIGNvbW1hbmRgLgogICBUaGUgYCQoKWAgbm90YXRpb24gaXMgY2FsbGVkICpjb21tYW5kIHN1YnN0aXR1dGlvbiosCiAgIGFuZCBzaW1wbHkgYWxsb3dzIHVzIHRvIGFzc2lnbiB0aGUgb3V0cHV0IG9mIGEgY29tbWFuZCB0byBhIHZhcmlhYmxlLgogICAKMy4gVG8gZ2V0IHRoZSBjb3JyZXNwb25kaW5nIGZpbGUgd2l0aCByZXZlcnNlIHJlYWQgKHdoaWNoIHNob3VsZCBhbHdheXMKICAgaGF2ZSB0aGUgZXhhY3Qgc2FtZSBuYW1lIGV4Y2VwdCBjb250YWluaW5nICJSMiIgcmF0aGVyIHRoYW4gIlIxIiksCiAgIHdlIHNpbXBseSBzdWJzdGl0dXRlICJSMSIgYnkgIlIyIiBpbiB0aGUgZmlsZSBuYW1lIHVzaW5nIGEgbGl0dGxlCiAgICpwYXJhbWV0ZXIgc3Vic3RpdHV0aW9uKiB0cmljayB3aXRoIHRoZSBzeW50YXgKICAgYCR7dmFyaWFibGVfbmFtZS9wYXR0ZXJuL3JlcGxhY2VtZW50fWAuCgo0LiBUbyBtYWtlIHN1cmUgYWxsIGlzIHdlbGwsIHdlIGxpc3QgdGhlIFIxIGFuZCBSMiBpbnB1dCBmaWxlcy4KCjUuIE5vdywgd2UnbGwgZG8gdGhlIHByaW1lciByZW1vdmFsIHVzaW5nIGBjdXRhZGFwdGAuCiAgIFdlIHVzZSBgXGAganVzdCB0byBhbGxvdyB0aGUgY29tbWFuZCB0byBjb250aW51ZSBhY3Jvc3MgbXVsdGlwbGUgbGluZXMgZm9yCiAgIGVhc3kgb2YgcmVhZGluZyAodGhlIGBcYCAiZXNjYXBlcyIgdGhlIHJldHVybi9uZXdsaW5lKS4KCjYuIFdlIGluZGljYXRlIHRoZSBlbmQgb2YgdGhlIGxvb3Agd2l0aCB0aGUga2V5d29yZCBgZG9uZWAuCgpgYGB7YmFzaH0KIyBSVU4gQ1VUQURBUFQgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyAxLiBJbml0aWFsaXplIHRoZSBsb29wOgpmb3IgUjEgaW4gJGluZGlyLypfUjFfKi5mYXN0cS5negpkbwogICMgMi4gR2V0IHRoZSBmaWxlbmFtZXMgZm9yIHRoZSBmYXN0cSBmaWxlcyB3aXRoIGZvcndhcmQgKFIxKSBhbmQgcmV2ZXJzZSAoUjIpIHJlYWRzOgogIFIxPSQoYmFzZW5hbWUgJFIxKSAgICMgYGJhc2VuYW1lYCB3aWxsIHN0cmlwIHRoZSBkaXJlY3RvcnkgZnJvbSB0aGUgbmFtZQogIAogICMgMy4gCiAgUjI9JHtSMS9fUjFfL19SMl99ICAgIyBUaGlzIHdpbGwgc3Vic3RpdHV0ZSAiX1IxXyIgd2l0aCAiX1IyXyIgaW4gdGhlIGZpbGVuYW1lLgogIAogICMgNC4gUmVwb3J0IGlucHV0IGZpbGVzOgogIGVjaG8gIiMjIFIxIGlucHV0IGZpbGU6IgogIGxzIC1saCAkaW5kaXIvJFIxCiAgZWNobyAiIyMgUjIgaW5wdXQgZmlsZToiCiAgbHMgLWxoICRpbmRpci8kUjIKCiAgIyA1LiBSdW4gY3V0YWRhcHQ6CiAgZWNobyAtZSAiXG5cbiMjIFJ1bm5pbmcgY3V0YWRhcHQuLi4iCgkKICBjdXRhZGFwdCAtYSAkcHJpbWVyX2YuLi4kcHJpbWVyX3JfcmMgLUEgJHByaW1lcl9yLi4uJHByaW1lcl9mX3JjIFwKCSAgLS1kaXNjYXJkLXVudHJpbW1lZCAtLXBhaXItZmlsdGVyPWFueSBcCgkgIC1vICRvdXRkaXIvJFIxIC1wICRvdXRkaXIvJFIyICRpbmRpci8kUjEgJGluZGlyLyRSMgoJCiAgIyA2LiBFeGl0IHRoZSBsb29wIHVzaW5nIHRoZSBgZG9uZWAga2V5d29yZApkb25lCmBgYAoKIyMjIFBhcnQgRDogTGlzdCBvdXRwdXQgZmlsZXMgYXMgYSBjaGVjazoKCkZpbmFsbHksIHdlJ2xsIGxpc3Qgb3VyIG91dHB1dCBmaWxlcywgYXMgYW5vdGhlciB3YXkgdG8gY2hlY2sgd2hldGhlciB0aGUgc2NyaXB0CmhhcyBydW4gYXMgaXQgd2FzIHN1cHBvc2VkLCBhbmQgd2UnbGwgYWxzbyBwcmludCB0aGUgYGRhdGVgIGFnYWluIHRvIGNoZWNrCnRoZSAoZGF0ZSBhbmQpIHRpbWUgdGhhdCBvdXIgc2NyaXB0IGZpbmlzaGVkIHJ1bm5pbmcuCgpgYGB7YmFzaH0KIyBSRVBPUlQgQU5EIEZJTkFMSVpFIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCmVjaG8gLWUgIlxuIyMgTGlzdGluZyBvdXRwdXQgZmlsZXM6IgpscyAtbGggJG91dGRpcgoKZWNobyAtZSAiXG4jIyBEb25lIHdpdGggY3V0YWRhcHQgc2NyaXB0LiIKZGF0ZQpgYGAKCiMjIyBUaGUgZW50aXJlIHNjcmlwdAoKPGRldGFpbHM+CjxzdW1tYXJ5PgpDbGljayBoZXJlIHRvIHNlZSB0aGUgZW50aXJlIHNjcmlwdC4KPC9zdW1tYXJ5PgoKYGBgc2gKIyEvYmluL2Jhc2gKI1NCQVRDSCAtLW5vZGVzPTEKI1NCQVRDSCAtLW50YXNrcy1wZXItbm9kZT0xCiNTQkFUQ0ggLS10aW1lPTYwCiNTQkFUQ0ggLS1hY2NvdW50PVBBUzA0NzEKCnNldCAtZSAtdSAtbyBwaXBlZmFpbCAgICAjIFJ1biBiYXNoIGluICJzYWZlIG1vZGUiLCBiYXNpY2FsbHkKCiMgUmVwb3J0OgplY2hvIC1lICJcbiMjIFN0YXJ0aW5nIGN1dGFkYXB0IHNjcmlwdC4iCmRhdGUKCiMgU0VUVVAgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KIyBDb21tYW5kLWxpbmUgYXJndW1lbnRzOgppbmRpcj0kMQpvdXRkaXI9JDIKCiMgQ3JlYXRlIG91dHB1dCBkaXJlY3RvcnkgaWYgaXQgZG9lc24ndCBhbHJlYWR5IGV4aXN0Ogpta2RpciAtcCAkb3V0ZGlyCgojIFJlcG9ydDoKZWNobyAiIyMgSW5wdXQgZGlyOiAgICAgICAkaW5kaXIiCmVjaG8gIiMjIE91dHB1dCBkaXI6ICAgICAgJG91dGRpciIKCiMgUHJpbWVyczoKcHJpbWVyX2Y9R0FHVEdZQ0FHQ01HQ0NHQ0dHVEFBCnByaW1lcl9yPUFDR0dBQ1RBQ05WR0dHVFdUQ1RBQVQKCnByaW1lcl9mX3JjPVRUQUNDR0NHR0NLR0NUR1JDQUNUQwpwcmltZXJfcl9yYz1BVFRBR0FXQUNDQ0JOR1RBR1RDQ0dUCgojIFNvZnR3YXJlIC0gbWFraW5nIGRvdWJseSBzdXJlIHRoaXMgaXMgYWxsIGxvYWRlZDoKbW9kdWxlIGxvYWQgcHl0aG9uLzMuNi1jb25kYTUuMiAgICAgICAgICAgICAgICAgICAgICMgTG9hZCBjb25kYSBtb2R1bGUKLiAvYXBwcy9weXRob24vMy42LWNvbmRhNS4yL2V0Yy9wcm9maWxlLmQvY29uZGEuc2ggICMgU2V0dXAgY29uZGEgKHNob3VsZCBiZSBzdXBlcmZsdW91cykKY29uZGEgYWN0aXZhdGUgL3VzZXJzL1BBUzA0NzEvb3N1NTY4NS8uY29uZGEvZW52cy9jdXRhZGFwdGVudiAjIEFjdGl2YXRlIGN1dGFkYXB0IGVudmlyb25tZW50CgojIFJVTiBDVVRBREFQVCAtLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQplY2hvIC1lICJcbiMjIExvb3BpbmcgdGhyb3VnaCBpbnB1dCBmaWxlcy4uLlxuIgoKZm9yIFIxIGluICRpbmRpci8qX1IxXyouZmFzdHEuZ3oKZG8KICBSMT0kKGJhc2VuYW1lICRSMSkKICBSMj0ke1IxL19SMV8vX1IyX30KICAKICAjIFJlcG9ydCBpbnB1dCBmaWxlczoKICBlY2hvICIjIyBSMSBpbnB1dCBmaWxlOiIKICBscyAtbGggJGluZGlyLyRSMQogIGVjaG8gIiMjIFIyIGlucHV0IGZpbGU6IgogIGxzIC1saCAkaW5kaXIvJFIyCgogICMgVHJpbToKICBlY2hvIC1lICJcblxuIyMgUnVubmluZyBjdXRhZGFwdC4uLiIKCQogIGN1dGFkYXB0IC1hICRwcmltZXJfZi4uLiRwcmltZXJfcl9yYyAtQSAkcHJpbWVyX3IuLi4kcHJpbWVyX2ZfcmMgXAoJICAtLWRpc2NhcmQtdW50cmltbWVkIC0tcGFpci1maWx0ZXI9YW55IFwKCSAgLW8gJG91dGRpci8kUjEgLXAgJG91dGRpci8kUjIgJGluZGlyLyRSMSAkaW5kaXIvJFIyCgkKICAjIE9wdGlvbnM6CiAgIyAiLWEiLyItQSI6IFByaW1lcnMgZm9yIFIxL1IyCiAgIyAiLS1kaXNjYXJkLXVudHJpbW1lZCI6IFJlbW92ZSBwYWlycyB3aXRoIG5vIHByaW1lciBmb3VuZAogICMgIi0tcGFpci1maWx0ZXI9YW55IjogUmVtb3ZlIHBhaXIgaWYgb25lIHJlYWQgaXMgZmlsdGVyZWQgKD1EZWZhdWx0KQoKICBlY2hvIC1lICJcblxuIC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLVxuIgpkb25lCgojIFJFUE9SVCBBTkQgRklOQUxJWkUgLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KZWNobyAtZSAiXG4jIyBMaXN0aW5nIG91dHB1dCBmaWxlczoiCmxzIC1saCAkb3V0ZGlyCgplY2hvIC1lICJcbiMjIERvbmUgd2l0aCBjdXRhZGFwdCBzY3JpcHQuIgpkYXRlCmBgYAoKPC9kZXRhaWxzPgoKLS0tLS0KCiMjIFN1Ym1pdCB0aGUgU2NyaXB0CgpXZSdsbCBmaXJzdCBjaGVjayB3aGV0aGVyIHdlIGFyZSBpbiB0aGUgY29ycmVjdCBkaXJlY3RvcnksCnNvIHRoYXQgb3VyIHJlbGF0aXZlIHBhdGhzIHdpbGwgd29yazoKCmBgYHtiYXNofQojIExldCdzIGNoZWNrIHdlIGFyZSBpbiB0aGUgY29ycmVjdCBkaXJlY3Rvcnk6CnB3ZAojIFNob3VsZCByZXR1cm46IC9mcy9wcm9qZWN0L1BBUzA0NzEvd29ya3Nob3BzLzIwMjAtMTJfbWljcm8vPHlvdXItdXNlcm5hbWU+CiMgSWYgbm90LCBydW46ICJjZCAvZnMvcHJvamVjdC9QQVMwNDcxL3dvcmtzaG9wcy8yMDIwLTEyX21pY3JvLyRVU0VSIgpgYGAKCjxicj4KClRoZW4gd2UgYXNzaWduIG91ciBpbnB1dCBhbmQgb3V0cHV0IGRpcmVjdG9yaWVzIHRvIHRoZSB2YXJpYWJsZXMKYGluZGlyYCBhbmQgYG91dGRpcmAsIHJlc3BlY3RpdmVseS4KCldlJ2xsIGFsc28gYXNzaWduIHRoZSBuYW1lIHdlIHdhbnQgdG8gZ2l2ZSB0byB0aGUgKlNMVVJNKiBsb2cgZmlsZSB0byBhIHZhcmlhYmxlLgpIZXJlLCB3ZSdsbCBhbHNvIHVzZSB0aGUgYCVqYCAqU0xVUk0qIGtleXdvcmQgdG8gaW5jbHVkZSB0aGUgKlNMVVJNKiBqb2IgbnVtYmVyLgoKYGBge2Jhc2h9CmluZGlyPWRhdGEvcmF3L2Zhc3RxX3N1YnNhbXBsZQpvdXRkaXI9ZGF0YS9wcm9jZXNzZWQvZmFzdHFfdHJpbW1lZAoKIyBHaXZlIGEgbmFtZSB0byB0aGUgU0xVUk0gb3V0cHV0IGZpbGU6CiMgKFRoZSAtbyBmbGFnIHNldHMgdGhlIFNMVVJNIGxvZyBmaWxlIG5hbWUsIGFuZCBgJWpgIHJlcHJlc2VudHMgdGhlIGpvYiBJRCkKc2x1cm1fZmlsZT1zbHVybS1jdXRhZGFwdC0kVVNFUi0lai5vdXQKYGBgCgo8YnI+CgpGaW5hbGx5LCB3ZSdyZSByZWFkeSB0byAqKnN1Ym1pdCB0aGUgc2NyaXB0KiohCmBgYHtiYXNofQpzYmF0Y2ggLW8gJHNsdXJtX2ZpbGUgc2NyaXB0cy8wMS1jdXRhZGFwdC5zaCAkaW5kaXIgJG91dGRpcgojIFN1Ym1pdHRlZCBiYXRjaCBqb2IgMjUyNjA4NQpgYGAKCi0tLS0tCgojIyBNb25pdG9yIHRoZSBKb2IgYW5kIEl0cyBPdXRwdXQKCgojIyMgTW9uaXRvciB0aGUgam9iCgpXZSBjYW4gY2hlY2sgd2hhdCdzIGdvaW5nIG9uIHdpdGggb3VyIGpvYiB1c2luZyBgc3F1ZXVlYDoKCmBgYHtiYXNofQojIEluaXRpYWxseSB0aGUgam9iIG1heSBiZSBxdWV1ZWQgKCJQRCIgaW4gdGhlICJTVCIgKHN0YXR1cykgY29sdW1uKToKc3F1ZXVlIC11ICRVU0VSCiMgSk9CSUQgICBQQVJUSVRJT04gICAgIE5BTUUgICAgICAgVVNFUiAgICBTVCAgICAgIFRJTUUgIE5PREVTIE5PREVMSVNUKFJFQVNPTikKIyAyNTI2MDg1IHNlcmlhbC00MCAgICAgMDEtY3V0YWQgICBqZWxtZXIgIFBEICAgICAgIDA6MDAgICAgICAxIChOb25lKSAKCiMgVGhlbiB0aGUgam9iIHNob3VsZCBiZSBydW5uaW5nICgiUiIgaW4gdGhlICJTVCIgY29sdW1uKToKc3F1ZXVlIC11ICRVU0VSCiMgSk9CSUQgICBQQVJUSVRJT04gICAgIE5BTUUgICAgICAgVVNFUiAgICBTVCAgICAgIFRJTUUgIE5PREVTIE5PREVMSVNUKFJFQVNPTikgCiMgMjUyNjA4NSBzZXJpYWwtNDAgICAgIDAxLWN1dGFkICAgamVsbWVyICBSICAgICAgIDA6MDIgICAgICAxIHAwMDAyIAoKIyBXaGVuIHRoZSBqb2IgaGFzIGZpbmlzaGVkLCBzcXVldWUgd2lsbCByZXR1cm4gbm90aGluZyAoISk6CnNxdWV1ZSAtdSAkVVNFUgojIEpPQklEIFBBUlRJVElPTiAgICAgTkFNRSAgICAgVVNFUiBTVCAgICAgICBUSU1FICBOT0RFUyBOT0RFTElTVChSRUFTT04pIAoKYGBgCgo8YnI+CgpXZSBjb3VsZCBhbHNvIGdldCBtb3JlIHN0YXRpc3RpY3MgYWJvdXQgb3VyIGpvYiB1c2luZyBgc2NvbnRyb2xgOgoKYGBge2Jhc2h9CnNjb250cm9sIHNob3cgam9iIDI1MjYwODUgICAjIFJlcGxhY2UgdGhlIG51bWJlciB3aXRoIHlvdXIgSk9CSUQKCiMgVXNlcklkPWplbG1lcigzMzIyNykgR3JvdXBJZD1QQVMwNDcxKDM3NzMpIE1DU19sYWJlbD1OL0EKIyBQcmlvcml0eT0yMDAwMDUyMDYgTmljZT0wIEFjY291bnQ9cGFzMDQ3MSBRT1M9cGl0emVyLWRlZmF1bHQKIyBKb2JTdGF0ZT1SVU5OSU5HIFJlYXNvbj1Ob25lIERlcGVuZGVuY3k9KG51bGwpCiMgUmVxdWV1ZT0xIFJlc3RhcnRzPTAgQmF0Y2hGbGFnPTEgUmVib290PTAgRXhpdENvZGU9MDowCiMgUnVuVGltZT0wMDowMjowMCBUaW1lTGltaXQ9MDE6MDA6MDAgVGltZU1pbj1OL0EKIyBTdWJtaXRUaW1lPTIwMjAtMTItMTRUMTQ6MzI6NDQgRWxpZ2libGVUaW1lPTIwMjAtMTItMTRUMTQ6MzI6NDQKIyBBY2NydWVUaW1lPTIwMjAtMTItMTRUMTQ6MzI6NDQKIyBTdGFydFRpbWU9MjAyMC0xMi0xNFQxNDozMjo0NyBFbmRUaW1lPTIwMjAtMTItMTRUMTU6MzI6NDcgRGVhZGxpbmU9Ti9BCiMgU3VzcGVuZFRpbWU9Tm9uZSBTZWNzUHJlU3VzcGVuZD0wIExhc3RTY2hlZEV2YWw9MjAyMC0xMi0xNFQxNDozMjo0NwojIFBhcnRpdGlvbj1zZXJpYWwtNDBjb3JlIEFsbG9jTm9kZTpTaWQ9cGl0emVyLWxvZ2luMDE6NTc5NTQKIyBSZXFOb2RlTGlzdD0obnVsbCkgRXhjTm9kZUxpc3Q9KG51bGwpCiMgTm9kZUxpc3Q9cDAwMDIKIyBCYXRjaEhvc3Q9cDAwMDIKIyBOdW1Ob2Rlcz0xIE51bUNQVXM9MSBOdW1UYXNrcz0xIENQVXMvVGFzaz0xIFJlcUI6UzpDOlQ9MDowOio6KgojIFRSRVM9Y3B1PTEsbWVtPTQ1NTZNLG5vZGU9MSxiaWxsaW5nPTEsZ3Jlcy9ncGZzOnByb2plY3Q9MAojIFNvY2tzL05vZGU9KiBOdGFza3NQZXJOOkI6UzpDPTE6MDoqOjEgQ29yZVNwZWM9KgojIE1pbkNQVXNOb2RlPTEgTWluTWVtb3J5Q1BVPTQ1NTZNIE1pblRtcERpc2tOb2RlPTAKIyBGZWF0dXJlcz0obnVsbCkgRGVsYXlCb290PTAwOjAwOjAwCiMgT3ZlclN1YnNjcmliZT1PSyBDb250aWd1b3VzPTAgTGljZW5zZXM9KG51bGwpIE5ldHdvcms9KG51bGwpCiMgQ29tbWFuZD0vZnMvcHJvamVjdC9QQVMwNDcxL3dvcmtzaG9wcy8yMDIwLTEyX21pY3JvL21hc3Rlci9zY3JpcHRzLzAxLWN1dGFkYXB0LnNoIGRhdGEvcmF3L2Zhc3RxX3N1YnNhbXBsZSAjIGRhL3Byb2Nlc3NlZC9mYXN0cV90cmltbWVkCiMgV29ya0Rpcj0vZnMvcHJvamVjdC9QQVMwNDcxL3dvcmtzaG9wcy8yMDIwLTEyX21pY3JvL21hc3RlcgojIENvbW1lbnQ9c3Rkb3V0PS9mcy9wcm9qZWN0L1BBUzA0NzEvd29ya3Nob3BzLzIwMjAtMTJfbWljcm8vbWFzdGVyL3NsdXJtLWN1dGFkYXB0LWplbG1lci0yNTI2MDg1Lm91dCAKIyBTdGRFcnI9L2ZzL3Byb2plY3QvUEFTMDQ3MS93b3Jrc2hvcHMvMjAyMC0xMl9taWNyby9tYXN0ZXIvc2x1cm0tY3V0YWRhcHQtamVsbWVyLTI1MjYwODUub3V0CiMgU3RkSW49L2Rldi9udWxsCiMgU3RkT3V0PS9mcy9wcm9qZWN0L1BBUzA0NzEvd29ya3Nob3BzLzIwMjAtMTJfbWljcm8vbWFzdGVyL3NsdXJtLWN1dGFkYXB0LWplbG1lci0yNTI2MDg1Lm91dAojIFBvd2VyPQojIFRyZXNQZXJOb2RlPWdwZnM6cHJvamVjdDoxCiMgTWFpbFVzZXI9amVsbWVyIE1haWxUeXBlPU5PTkUKYGBgCgojIyMgQ2hlY2sgdGhlIG91dHB1dAoKTGV0J3Mgc2VlIGlmIHdlIGhhdmUgYSBsb2cgZmlsZToKCmBgYHtiYXNofQpscyBzbHVybSoKIyBzbHVybS1jdXRhZGFwdC1qZWxtZXItMjQ1MTA4OC5vdXQKYGBgCgpMZXQncyBsb29rIGF0IHRoZSBzdGFydCBvZiB0aGUgKlNMVVJNKiBsb2cgZmlsZToKCmBgYHtiYXNofQpoZWFkIC1uIDIwIHNsdXJtLWN1dGFkYXB0LWplbG1lci0yNTI2MDg1Lm91dAoKIyAjIyBTdGFydGluZyBjdXRhZGFwdCBzY3JpcHQuCiMgTW9uIERlYyAxNCAxNDozMjo0OCBFU1QgMjAyMAojICMjIElucHV0IGRpcjogICAgICAgZGF0YS9yYXcvZmFzdHFfc3Vic2FtcGxlCiMgIyMgT3V0cHV0IGRpcjogICAgICBkYXRhL3Byb2Nlc3NlZC9mYXN0cV90cmltbWVkCiMgCiMgIyMgTG9vcGluZyB0aHJvdWdoIGlucHV0IGZpbGVzLi4uCiMgCiMgIyMgUjEgaW5wdXQgZmlsZToKIyAtcnctci0tci0tIDEgamVsbWVyIFBBUzA0NzEgMy4xTSBEZWMgMTMgMTQ6MTAgZGF0YS9yYXcvZmFzdHFfc3Vic2FtcGxlLzEwMS1TMS1WNC1WNV9TMV9MMDAxX1IxXzAwMS5mYXN0cS5negojICMjIFIyIGlucHV0IGZpbGU6CiMgLXJ3LXItLXItLSAxIGplbG1lciBQQVMwNDcxIDMuOE0gRGVjIDEzIDE0OjEwIGRhdGEvcmF3L2Zhc3RxX3N1YnNhbXBsZS8xMDEtUzEtVjQtVjVfUzFfTDAwMV9SMl8wMDEuZmFzdHEuZ3oKIyAKIyAKIyAjIyBSdW5uaW5nIGN1dGFkYXB0Li4uCiMgVGhpcyBpcyBjdXRhZGFwdCAzLjEgd2l0aCBQeXRob24gMy44LjYKIyBDb21tYW5kIGxpbmUgcGFyYW1ldGVyczogLWEgR0FHVEdZQ0FHQ01HQ0NHQ0dHVEFBLi4uQVRUQUdBV0FDQ0NCTkdUQUdUQ0NHVCAtQSAjIEFDR0dBQ1RBQ05WR0dHVFdUQ1RBQVQuLi5UVEFDQ0dDR0dDS0dDVEdSQ0FDVEMgLS1kaXNjYXJkLXVudHJpbW1lZCAtLXBhaXItZmlsdGVyPWFueSAtbyAjIGRhdGEvcHJvY2Vzc2VkL2Zhc3RxX3RyaW1tZWQvMTAxLVMxLVY0LVY1X1MxX0wwMDFfUjFfMDAxLmZhc3RxLmd6IC1wICMgZGF0YS9wcm9jZXNzZWQvZmFzdHFfdHJpbW1lZC8xMDEtUzEtVjQtVjVfUzFfTDAwMV9SMl8wMDEuZmFzdHEuZ3ogIyBkYXRhL3Jhdy9mYXN0cV9zdWJzYW1wbGUvMTAxLVMxLVY0LVY1X1MxX0wwMDFfUjFfMDAxLmZhc3RxLmd6ICMgZGF0YS9yYXcvZmFzdHFfc3Vic2FtcGxlLzEwMS1TMS1WNC1WNV9TMV9MMDAxX1IyXzAwMS5mYXN0cS5negojIFByb2Nlc3NpbmcgcmVhZHMgb24gMSBjb3JlIGluIHBhaXJlZC1lbmQgbW9kZSAuLi4KIyBGaW5pc2hlZCBpbiAyLjYwIHMgKDExMyDCtXMvcmVhZDsgMC41MyBNIHJlYWRzL21pbnV0ZSkuCgpgYGAKCkxldCdzIGFsc28gbG9vayBhdCB0aGUgZW5kIG9mIHRoZSBmaWxlOgoKYGBge2Jhc2h9CnRhaWwgc2x1cm0tY3V0YWRhcHQtamVsbWVyLTI1MjYwODUub3V0CgojIC1ydy1yLS1yLS0gMSBqZWxtZXIgUEFTMDQ3MSAyLjJNIERlYyAxNCAxNDozNSA2MDQtUzQtVjQtVjVfUzcyX0wwMDFfUjJfMDAxLmZhc3RxLmd6CiMgLXJ3LXItLXItLSAxIGplbG1lciBQQVMwNDcxIDM5OEsgRGVjIDE0IDE0OjM1IGJsYW5rTS1WNC1WNV9TNzNfTDAwMV9SMV8wMDEuZmFzdHEuZ3oKIyAtcnctci0tci0tIDEgamVsbWVyIFBBUzA0NzEgNDg2SyBEZWMgMTQgMTQ6MzUgYmxhbmtNLVY0LVY1X1M3M19MMDAxX1IyXzAwMS5mYXN0cS5negojIC1ydy1yLS1yLS0gMSBqZWxtZXIgUEFTMDQ3MSAgMjNLIERlYyAxNCAxNDozNSBIMjAtVjQtVjVfUzg4X0wwMDFfUjFfMDAxLmZhc3RxLmd6CiMgLXJ3LXItLXItLSAxIGplbG1lciBQQVMwNDcxICAyNksgRGVjIDE0IDE0OjM1IEgyMC1WNC1WNV9TODhfTDAwMV9SMl8wMDEuZmFzdHEuZ3oKIyAtcnctci0tci0tIDEgamVsbWVyIFBBUzA0NzEgMS40TSBEZWMgMTQgMTQ6MzUgWnltby1WNC1WNV9TODJfTDAwMV9SMV8wMDEuZmFzdHEuZ3oKIyAtcnctci0tci0tIDEgamVsbWVyIFBBUzA0NzEgMS43TSBEZWMgMTQgMTQ6MzUgWnltby1WNC1WNV9TODJfTDAwMV9SMl8wMDEuZmFzdHEuZ3oKIyAKIyAjIyBEb25lIHdpdGggY3V0YWRhcHQgc2NyaXB0LgojIE1vbiBEZWMgMTQgMTQ6MzU6MTkgRVNUIDIwMjAKYGBgCgoKPGJyPgoKRmluYWxseSwgd2UgY2FuIGRpcmVjdGx5IGNoZWNrIHRoZSBvdXRwdXQgZGlyOgoKYGBge2Jhc2h9CmxzIC1saCBkYXRhL3Byb2Nlc3NlZC9mYXN0cV90cmltbWVkLyAgICAjIE9yIHVzZSAkb3V0ZGlyCgojIHRvdGFsIDE3OE0KIyAtcnctci0tci0tIDEgamVsbWVyIFBBUzA0NzEgMy4wTSBEZWMgMTQgMTQ6MzIgMTAxLVMxLVY0LVY1X1MxX0wwMDFfUjFfMDAxLmZhc3RxLmd6CiMgLXJ3LXItLXItLSAxIGplbG1lciBQQVMwNDcxIDMuNk0gRGVjIDE0IDE0OjMyIDEwMS1TMS1WNC1WNV9TMV9MMDAxX1IyXzAwMS5mYXN0cS5negojIC1ydy1yLS1yLS0gMSBqZWxtZXIgUEFTMDQ3MSAzLjdNIERlYyAxNCAxNDozMyAxMDEtUzQtVjQtVjVfUzQ5X0wwMDFfUjFfMDAxLmZhc3RxLmd6CiMgLXJ3LXItLXItLSAxIGplbG1lciBQQVMwNDcxIDQuNU0gRGVjIDE0IDE0OjMzIDEwMS1TNC1WNC1WNV9TNDlfTDAwMV9SMl8wMDEuZmFzdHEuZ3oKIyAtcnctci0tci0tIDEgamVsbWVyIFBBUzA0NzEgMi42TSBEZWMgMTQgMTQ6MzMgMTAyLVM0LVY0LVY1X1M1MF9MMDAxX1IxXzAwMS5mYXN0cS5negojIC1ydy1yLS1yLS0gMSBqZWxtZXIgUEFTMDQ3MSAzLjFNIERlYyAxNCAxNDozMyAxMDItUzQtVjQtVjVfUzUwX0wwMDFfUjJfMDAxLmZhc3RxLmd6CiMgLXJ3LXItLXItLSAxIGplbG1lciBQQVMwNDcxIDIuNE0gRGVjIDE0IDE0OjMzIDEwMy1TNC1WNC1WNV9TNTFfTDAwMV9SMV8wMDEuZmFzdHEuZ3oKIyAuLi4uW290aGVyIGZpbGVzIG5vdCBzaG93bl0KYGBgCgo8YnI+CgpBbGwgZG9uZSEgT3VyIFtuZXh0IHN0ZXBdKDA3LUFTVi1pbmZlcmVuY2UuaHRtbCkgd2lsbCBiZSB0byBmdXJ0aGVyIHByb2Nlc3MgdGhlIGBmYXN0cWAgZmlsZXMgd2l0aCBgY3V0YWRhcHRgLgoKPGJyPiA8YnI+IDxicj4K