By the end of this module, we will:
- Understand what workflow automation is and how it helps
reproducibility.
- Review several different ways to execute repetitive tasks on Great
Lakes.
- Introduce the idea of job/task geometries to visualize approachs and
their advantages and limitations.
Workflow automation helps reproducibility
Data-intensive research entails transforming raw data into more
meaningful/valuable results. Often, this involves a series of step-wise
transformation tasks. To make this research reproducible by someone
else, all the transformations must either be documented so that a human
can reproduce them, or automated so a computer can. A reproducible
solution has a blend of documentation and automation.
Data-intensive research often involves repeating transformation tasks
many times. Also overtime transformations evolve to be more complex,
more computationally demanding, or take longer. A
workflow describes the key transformation tasks and
their relationships to the inputs and outputs.
Workflow automation describes the tools and
techniques to systematically assemble these tasks into a executable,
repeatable, robust solution. Building an automated workflow appears
harder than documenting it to be run manually, but there are many
benefits to reproducibility:
- Automation facilitates repetition.
- Automation can simplify manual documentation.
- Automation simplifies validation of your workflow.
- Automation streamlines sharing.
- Automation scales to larger inputs
There are many ways to build an automated workflow. In this module we
will consider several ways of executing pleasingly parallel tasks on Great Lakes:
- A serial task loop
- Parallelizing tasks using driver scripts and sbatch files.
- The SLURM Launcher
All these approaches execute the same workflow in different ways. The
workflow produces word pangrams. A word pangram is like
an anagram that allows repeating letters, e.g. the sequence of letters
ACEHMNT can be rearranged to create the pangrams ATTACHMENT, CATCHMENT,
ENCHANTMENT, and ENHANCEMENT.
The workflow accepts a text file containing list of letter sequences
separated by lines; for each letter sequence it produces a file
containing one or more pangrams.
Pangram: A serial task loop
# Orient on project pangram
cd /nfs/turbo/umms-bioinf-wkshp/workshop/home/$USER
cd project_pangrams
ls -1
pangram_job_launcher
pangram_parallel_sbatch
pangram_serial_loop
README.md
cd pangram_serial_loop
ls -1
find_pangrams.sbat
find_pangrams.sh
letters.txt
pangram.sh
README.md
Let’s consider a few of these files in turn, starting with the
README.
README.md
|
# pangram_serial_loop
- Produces pangrams for inputs in letters.txt.
Makes one file for each line in letters.
- cgates 6/1/2024
- Usage:
./find_pangrams.sh #to run locally
or
sbatch find_pangrams.sbat # to submit to worker node
Files:
- find_pangrams.sbat: SLURM batch file; calls find_pangrams.sh
- find_pangrams.sh: Loops throught input and calls pangram.sh
- letters.txt: list of letter sequences seperated by newlines.
- pangram.sh: accepts a single letter sequence and prints all pangrams.
|
The pangram.sh script is the workhorse of this workflow. You are
welcome to look at the implementation, but for our purposes we can treat
it as a black box. We’ll run it once to see it in action.
./pangram.sh lovely
lovely
lovey
volley
Consider the input to the workflow:
letters.txt
|
Ndefglu
Hacilno
Tdghnou
Nailmpt
Pbegikn
Yacilrt
Achnopy
Uginoqt
Eachkmn
Alhyidn
|
And finally, the script we will launch to execute the workflow:
find_pangrams.sh
|
#/bin/bash
set -eu
for letters in $(cat letters.txt); do
echo pangrams for: $letters >> /dev/stderr
./pangram.sh $letters > results.${letters}.txt
done
echo done >> /dev/stderr
|
Having reviewed the inputs and scripts, we can launch the workflow
like as shown below. (This project is called “serial loop” because in
this workflow we are looping over the inputs and processing one at a
time.)
./find_pangrams.sh
pangrams for: Ndefglu
pangrams for: Hacilno
pangrams for: Tdghnou
pangrams for: Nailmpt
pangrams for: Pbegikn
pangrams for: Yacilrt
pangrams for: Achnopy
pangrams for: Uginoqt
pangrams for: Eachkmn
pangrams for: Alhyidn
We see the results files have been added:
ls
find_pangrams.sbat results.Achnopy.txt results.Ndefglu.txt
find_pangrams.sh results.Alhyidn.txt results.Pbegikn.txt
letters.txt results.Eachkmn.txt results.Tdghnou.txt
pangram.sh results.Hacilno.txt results.Uginoqt.txt
README.md results.Nailmpt.txt results.Yacilrt.txt
cat results.Achnopy.txt
cacophony
Question: Which letter combination generated the most
pangrams?
Let’s run it again but instead of using the login-node, we’ll submit
this to a worker node using the provided sbat script:
# first clear out the old results
rm results.*
sbatch find_pangrams.sbat
Submitted batch job 1234567
Use squeue -u $USER
to see when the job is finished and
then review the outputs. We now see the results files and also the slurm
log file:
find_pangrams.sbat results.Alhyidn.txt results.Tdghnou.txt
find_pangrams.sh results.Eachkmn.txt results.Uginoqt.txt
letters.txt results.Hacilno.txt results.Yacilrt.txt
pangram.sh results.Nailmpt.txt slurm-1234567.out
README.md results.Ndefglu.txt
results.Achnopy.txt results.Pbegikn.txt
This approach is correct, clear, and reproducible; however it’s not
ideal. Consider how the tasks are contained within a job:
Job/task geometry of the serial loop approach
|
|
Each sbatch request is a job script; a job script may be composed of
multiple tasks. Key attributes of a job script are
- what sub-tasks will I run?
- how many resources do I need?
- how long will I need to run?
You can represent these graphically by making boxes for each job and
their tasks (height = resource request and length = time). In the case
above, there are many similar tasks contained in a single job. This
diagram is a rough representation of the job/task
geometry. This is a useful way of visualizing and comparing
approaches; also, the job geometry is critically useful information to
the scheduler which is trying to pack everyone’s jobs into the available
clusters as neatly/efficiently as possible.
|
Considering that each of these pangram tasks are completely
independent of each other (i.e. pleasingly parallel). We might be able
to make better use of our ~16000 CPUs by parallelizing the workflow. The
approaches below show two different ways to accomplish this.
Pangram: Parallel SBATCH
# Orient on project
cd /nfs/turbo/umms-bioinf-wkshp/workshop/home/$USER
cd project_pangrams/pangram_parallel_sbatch
ls -1
letters.txt
make_sbat_scripts.sh
pangram.sh
README.md
run_sbat_scripts.sh
README.md
|
# pangram_parallel_sbatch
- Produces pangrams for letters.txt. Makes one file per line in letters.
- cgates 6/1/2024
- Usage:
`./make_sbat_scripts.sh`
`./run_sbat_scripts.sh`
Files:
- letters.txt: list of letter sequences seperated by newlines.
- pangram.sh: accepts a single letter sequence and prints all pangrams.
- make_sbat_scripts.sh : Build sbat scripts based on letters.txt;
for each row in letters.txt adds a new sbat file.
- run_sbat_scripts.sh : Submit all sbat scripts for cluster execution
|
Executing make_sbat_scripts creates a new directory and adding a
collection of sbat files.
./make_sbat_scripts.sh
ls sbat_scripts
...
Achnopy.sbat Nailmpt.sbat Uginoqt.sbat
Alhyidn.sbat Ndefglu.sbat Yacilrt.sbat
Eachkmn.sbat Pbegikn.sbat
Hacilno.sbat Tdghnou.sbat
Consider a single sbat file.
sbat_scripts/Achnopy.sbat
|
#!/bin/bash
#SBATCH --job-name=pangram_Achnopy
#SBATCH --cpus-per-task=1
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --mem-per-cpu=400m
#SBATCH --time=00:05:00
#SBATCH --account=bioinf_wkshp_class
#SBATCH --partition=standard
./pangram.sh Achnopy > results.Achnopy.txt
|
Briefly consider the script to see how these sbat files was
constructed. Note the SLURM preamble directives are integrated into the
new files using a HereDoc.
make_sbat_scripts.sh
|
#!/bin/bash
set -eu
mkdir -p sbat_scripts
for letters in $(cat letters.txt); do
echo sbat for: $letters >> /dev/stderr
cat << HERE_DOC > sbat_scripts/$letters.sbat
#!/bin/bash
#SBATCH --job-name=pangram_${letters}
#SBATCH --cpus-per-task=1
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=1
#SBATCH --mem-per-cpu=400m
#SBATCH --time=00:05:00
#SBATCH --account=bioinf_wkshp_class
#SBATCH --partition=standard
./pangram.sh $letters > results.${letters}.txt
HERE_DOC
done
echo done >> /dev/stderr
|
We can submit jobs one at a time using sbatch. OR we could build a
for loop to automate submission; conveniently
run_sbat_scripts.sh
has done this for us.
run_sbat_scripts.sh
|
for sbat in $(ls sbat_scripts/*.sbat); do
sbatch $sbat
done
|
Before we execute run_sbat_scripts.sh
, you might
consider opening a separate window to monitor the SLURM job queue. In
this second window, you can execute watch squeue -u $USER
to see how the jobs are being scheduled. Hit ctrl-C to exit watch. (More
info on watch.)
./run_sbat_scripts.sh
Submitted batch job 9289496
Submitted batch job 9289497
Submitted batch job 9289498
Submitted batch job 9289499
Submitted batch job 9289500
Submitted batch job 9289501
Submitted batch job 9289502
Submitted batch job 9289503
Submitted batch job 9289504
Submitted batch job 9289505
And in a few short seconds, you see the results and SLURM log files.
This approach is correct, more complex than the serial loop, and
reproducible. And because the tasks are working in parallel, it’s
much faster. Contrast this job/task geometry with the
serial loop approach from above:
Job/task geometries: serial loop vs parallel sbatch
|
|
|
This is great. But there’s two to three minor drawbacks to this
approach:
- Between the the sbat files, the result files, and the slum log
files, it’s created quite a lot more files. They are smallish files, but
it’s more output to keep track of.
- When the scheduler is under a heavy load, for very quick jobs
(<=60 seconds) it can take longer to schedule a job than it takes to
run the job. In these circumstances the serial approach might be faster.
(If we scaled up from 10 jobs to 1000 jobs in parallel, we might see
this kind of slowdown.)
- Each ARC account has an upper limit on the number of jobs that can
be submitted and the number actively running. If your account exceeds
this limit the jobs will start to queue up, awaiting a turn at
scheduling and execution. That’s not a big problem, except for the fact
that you share the account with other users. If you saturate your queue,
others will have to wait until your job finishes before starting
theirs.
To address these concerns, the Texas Advanced Computing Center built
a SLURM tool called launcher detailed below.
Pangram: Launcher
# Orient on project
cd /nfs/turbo/umms-bioinf-wkshp/workshop/home/$USER
cd project_pangrams/pangram_launcher
ls -1
launcher.sbat
letters.txt
make_launcher_tasks.sh
pangram.sh
README.md
README.md
|
# pangram_launcher
- Produces pangrams for letters.txt. Makes one file per line in letters.
- cgates 6/1/2024
- Usage:
`./make_launcher_tasks.sh`
`sbatch launcher.sbat`
Files:
- launcher.sbat: sbatch file to start the launcher.
- letters.txt: list of letter sequences seperated by newlines.
- make_launcher_tasks.sh: builds a single file for all tasks to be executed
by the launcher.
- pangram.sh: accepts a single letter sequence and prints all pangrams.
|
Run make_launcher_tasks.sh
and note it creates one new
file launcher_tasks.txt
.
./make_launcher_tasks.sh
launcher_tasks.txt
|
./pangram.sh Ndefglu > results.Ndefglu.txt
./pangram.sh Hacilno > results.Hacilno.txt
./pangram.sh Tdghnou > results.Tdghnou.txt
./pangram.sh Nailmpt > results.Nailmpt.txt
./pangram.sh Pbegikn > results.Pbegikn.txt
./pangram.sh Yacilrt > results.Yacilrt.txt
./pangram.sh Achnopy > results.Achnopy.txt
./pangram.sh Uginoqt > results.Uginoqt.txt
./pangram.sh Eachkmn > results.Eachkmn.txt
./pangram.sh Alhyidn > results.Alhyidn.txt
|
This might remind you of the serial loop approach, but there’s a
twist and to see it you need to consider the launcher.sbat
file:
launcher.sbat
|
#!/bin/bash
#SBATCH --account=bioinf_wkshp_class
#SBATCH --partition=standard
#SBATCH --nodes=1
#SBATCH --ntasks-per-node=5
#SBATCH --cpus-per-task=1
#SBATCH --time=0:30:00
module load launcher
export LAUNCHER_JOB_FILE=launcher_tasks.txt
paramrun
|
Some details:
- The file starts with a basic SLURM preamble. Note that it’s asking
for 1 node, and 5 CPUs (5 tasks/node * 1 cpu/task) for 30 minutes.
- The last three lines establish that this is a launcher job and the
tasks to execute live in launcher_tasks.txt.
|
Given this setup, sbatch will allocate a node with 5 CPUs for 30
minutes. Then the launcher will start looping through the launcher_tasks
and as each one completes it will send another one through until all
tasks are complete.
Consider running watch squeue -u $USER
in another window
before you run the sbatch command:
sbatch launcher.sbat
Submitted batch job 9290535
Note that all the tasks are running but they are running “inside”” of
the one job. The job should finish in a few seconds. It will produce the
familliar results.*
files and also a single SLURM log file
which is a bit more interesting than the previous log files.
slurm-9290535.out
|
WARNING (06/09/24 15:38:39): LAUNCHER_WORKDIR variable not set. Using current directory.
windowsP is false
NOTE (06/09/24 15:38:40): Started dynamic task service on port 9471
Launcher: Setup complete.
------------- SUMMARY ---------------
Number of hosts: 1
Working directory: /nfs/turbo/umms-bioinf-wkshp/workshop/home/cgates/project_pangrams/pangram_launcher
Processes per host: 5
Total processes: 5
Total jobs: 10
Scheduling method: dynamic
-------------------------------------
Launcher: Starting parallel tasks...
using /tmp/launcher.9290535.hostlist.GYzdOWsB to get hosts
starting job on gl3079
Warning: Permanently added the ED25519 host key for IP address '10.164.8.129'
to the list of known hosts.
Launcher: Task 0 running job 1 on gl3079.arc-ts.umich.edu (./pangram.sh Ndefglu > results.Ndefglu.txt)
Launcher: Job 1 completed in 1 seconds.
Launcher: Task 0 running job 2 on gl3079.arc-ts.umich.edu (./pangram.sh Hacilno > results.Hacilno.txt)
Launcher: Task 2 running job 3 on gl3079.arc-ts.umich.edu (./pangram.sh Tdghnou > results.Tdghnou.txt)
Launcher: Job 3 completed in 1 seconds.
...
Launcher: Task 0 done. Exiting.
Launcher: Done. Job exited without errors
|
The launcher solution is correct, clear, and efficient. It is a very
nice option if you have many independent tasks that each run
quickly (<=60 seconds) and each tasks has a modest compute request
(e.g. each task needs a single CPU).
Geometries and dependencies
Job/task geometries compared
|
|
|
|
The three job geometries diagrammed above hint that we quietly made a
a few simplifying assumptions along the way:
- We assumed that all the tasks were independent (and thus pleasingly
parallel).
- We assumed that all the tasks in a workflow were the same
transformation applied many different inputs.
Commonly workflows contain several different steps which where the input
of one step often depends on the output of the previous.
Also steps in a workflow often have variable resrouce needs and run
times:
Moreover, workflows are not always linear; the logical flow of steps may
join the outputs of two steps as an input to a third:
The techniques we reviewed above are execllent for smaller, simpler
workflows, a more complex, more resource intensive workflow will require
either a much more nuanced set of scripts or a more sophisticated
approach altogether. SLURM supports these more complex scenarios
natively with something called job arrays (see job arrays in links below). In the next module, we
introduce the Snakemake workflow automation framework
to address these more complicated scenarios.
Pro tips
Automating workflows is a learning process. Here’s a few ideas to
consider along the way:
Do not try to automate something that you cannot do by
hand.
Make it right. Make it clear. Make it efficient. (In that
order.)
Build a README for each workflow. Consider
including:
- Your name/email
- The date
- How to install the workflow
- How to run the workflow
- Any necessary context/constraints that would help your future
collaborator reproduce your results.
Automate the workflow with the data you have.
Don’t generalize a workflow too soon. You might see that a workflow
could be parameterized/extended to apply to new types of data. Feel that
excitement, note the opportunity in the README, and trust that you will
make that change when you need to.
Instead of developing the whole workflow end to end,
consider an iterative and incremental approach.
Break workflow development into steps:
- do part of the workflow for one sample and verify correctness as you
add steps. (For a large dataset consider subsetting/downsampling your
inputs so you can iterate quicker.)
- run a single sample end to end
- scale to a few samples and check those outputs; tune resource
allocations
- run the whole batch
Exercise: Project Railfence
This project is focused around a transformation that can encode or
decode a specific kind of encryption called a rail fence cipher. The details of this encryption
are interesting and I encourage you to check out the link, but for the
purposes of this exercise it’s ok to treat it as magic/black-box
transformation.
Review the project directory here:
cd /nfs/turbo/umms-bioinf-wkshp/workshop/home/$USER
cd project_railfence
ls -1
codes.txt
railfence_decode.py
railfence_encode.py
README.md
The railfence_decode.py
script accepts two arguments
separated by a comma:
- a number (a positive integer)
- a quoted string (the cipher text)
It returns the decoded clear text. You can run the
railfence_decode.py script like so:
# need to load python once in the session
module load python
./railfence_decode.py 3,"wrivdetceaedsoee-lea ne crf o!"
we are discovered-flee at once!
There is a list of encrypted codes in the
codes.txt
file:
codes.txt
|
2,"onttyt uoaesmtigta o antd yhn"d o r oatmt oehn htyucno ob ad
3,"e hM craifi.nar.mk trgt aei la.Mk tefcet I htodr)aii.kte e in(t e"
4,"aefie aamth klwth ayhvuoetwro htdto et ow au"
5,"rrnmpce eaadeeap"odatt rn rhniniieictlocs vnaa
3,"t DwieaRAM"r EE
3,"lsie"m!
|
Your task:
- Consider the three automation approaches outlined in the lessons
above: serial loop, parallel batch, and launcher.
- Choose one approach, and, using the patterns above as a template,
create script(s) that will decrypt the codes in
code.txt
.
- If you complete the exercise with a one approach, repeat the
exercise with a different approach.
Key ideas
- Achieving reproducible research requires a blend of documentation
and automation.
- Be kind to your future self; they will thank you for the README you
left them.
- Automation helps reproducibility:
- Automation shrinks your README.
- Automation simplifies validation of your workflow.
- Automation enables repetition.
- Automation streamlines sharing.
- Automation scales to larger inputs
- Job/task geometries help visualize how different approaches are
executed. (It also hints at the n-dimensional game of Tetris the
job-scheduler is playing to pack everyones jobs as neatly as
possible.
- The SLURM launcher allows you to gather many
parallel tasks into a main job, in effect creating a transient
sub-cluster within the main HPC.
- For a more complex transformation, ascript can be either simple or
resource efficient - choose one. Consider more robust solutions (e.g.
Snakemake) as necessary.
Links and references
UM ITS docs on launcher
UM ARC docs on job arrays
SLURM docs on job arrays
UM ARC miVideo on advanced SLURM techniques (including
launcher, job arrays, and more)
For more examples of pangrams in action, checkout:
LS0tCnRpdGxlOiAiSW50cm8gdG8gd29ya2Zsb3cgYXV0b21hdGlvbiIKYXV0aG9yOiAiVU0gQmlvaW5mb3JtYXRpY3MgQ29yZSIKb3V0cHV0OgogICAgICAgIGh0bWxfZG9jdW1lbnQ6CiAgICAgICAgICAgIGluY2x1ZGVzOgogICAgICAgICAgICAgICAgaW5faGVhZGVyOiBoZWFkZXIuaHRtbAogICAgICAgICAgICB0aGVtZTogcGFwZXIKICAgICAgICAgICAgdG9jOiB0cnVlCiAgICAgICAgICAgIHRvY19kZXB0aDogNAogICAgICAgICAgICB0b2NfZmxvYXQ6IHRydWUKICAgICAgICAgICAgbnVtYmVyX3NlY3Rpb25zOiBmYWxzZQogICAgICAgICAgICBmaWdfY2FwdGlvbjogdHJ1ZQogICAgICAgICAgICBtYXJrZG93bjogR0ZNCiAgICAgICAgICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKLS0tCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+CmJvZHl7IC8qIE5vcm1hbCAgKi8KICAgICAgZm9udC1zaXplOiAxNHB0OwogIH0KcHJlIHsKICBmb250LXNpemU6IDEycHQKfQp0YWJsZS5maWcsIHRoLmZpZywgdGQuZmlnIHsKICBib3JkZXI6IDFweCBzb2xpZCBibGFjazsKICBib3JkZXItY29sbGFwc2U6IGNvbGxhcHNlOwogIHBhZGRpbmc6IDE1cHg7Cn0KPC9zdHlsZT4KCkJ5IHRoZSBlbmQgb2YgdGhpcyBtb2R1bGUsIHdlIHdpbGw6CgotIFVuZGVyc3RhbmQgd2hhdCB3b3JrZmxvdyBhdXRvbWF0aW9uIGlzIGFuZCBob3cgaXQgaGVscHMgcmVwcm9kdWNpYmlsaXR5LgotIFJldmlldyBzZXZlcmFsIGRpZmZlcmVudCB3YXlzIHRvIGV4ZWN1dGUgcmVwZXRpdGl2ZSB0YXNrcyBvbiBHcmVhdCBMYWtlcy4KLSBJbnRyb2R1Y2UgdGhlIGlkZWEgb2Ygam9iL3Rhc2sgZ2VvbWV0cmllcyB0byB2aXN1YWxpemUgYXBwcm9hY2hzIGFuZCB0aGVpciBhZHZhbnRhZ2VzIGFuZCBsaW1pdGF0aW9ucy4KCgojIyBXb3JrZmxvdyBhdXRvbWF0aW9uIGhlbHBzIHJlcHJvZHVjaWJpbGl0eQoKRGF0YS1pbnRlbnNpdmUgcmVzZWFyY2ggZW50YWlscyB0cmFuc2Zvcm1pbmcgcmF3IGRhdGEgaW50byBtb3JlCm1lYW5pbmdmdWwvdmFsdWFibGUgcmVzdWx0cy4gT2Z0ZW4sIHRoaXMgaW52b2x2ZXMgYSBzZXJpZXMgb2Ygc3RlcC13aXNlCnRyYW5zZm9ybWF0aW9uIHRhc2tzLiBUbyBtYWtlIHRoaXMgcmVzZWFyY2ggcmVwcm9kdWNpYmxlIGJ5IHNvbWVvbmUgZWxzZSwgYWxsIHRoZSAKdHJhbnNmb3JtYXRpb25zIG11c3QgZWl0aGVyIGJlIGRvY3VtZW50ZWQgc28gdGhhdCBhIGh1bWFuIGNhbiByZXByb2R1Y2UgdGhlbSwgb3IKYXV0b21hdGVkIHNvIGEgY29tcHV0ZXIgY2FuLiBBIHJlcHJvZHVjaWJsZSBzb2x1dGlvbiBoYXMgYSBibGVuZCBvZiAKZG9jdW1lbnRhdGlvbiBhbmQgYXV0b21hdGlvbi4KCkRhdGEtaW50ZW5zaXZlIHJlc2VhcmNoIG9mdGVuIGludm9sdmVzIHJlcGVhdGluZyB0cmFuc2Zvcm1hdGlvbiB0YXNrcyBtYW55CnRpbWVzLiBBbHNvIG92ZXJ0aW1lIHRyYW5zZm9ybWF0aW9ucyBldm9sdmUgdG8gYmUgbW9yZSBjb21wbGV4LCBtb3JlCmNvbXB1dGF0aW9uYWxseSBkZW1hbmRpbmcsIG9yIHRha2UgbG9uZ2VyLiBBICoqd29ya2Zsb3cqKiBkZXNjcmliZXMgdGhlIGtleQp0cmFuc2Zvcm1hdGlvbiB0YXNrcyBhbmQgdGhlaXIgcmVsYXRpb25zaGlwcyB0byB0aGUgaW5wdXRzIGFuZCBvdXRwdXRzLgoKKipXb3JrZmxvdyBhdXRvbWF0aW9uKiogZGVzY3JpYmVzIHRoZSB0b29scyBhbmQgdGVjaG5pcXVlcyB0byBzeXN0ZW1hdGljYWxseQphc3NlbWJsZSB0aGVzZSB0YXNrcyBpbnRvIGEgZXhlY3V0YWJsZSwgcmVwZWF0YWJsZSwgcm9idXN0IHNvbHV0aW9uLiBCdWlsZGluZyBhbgphdXRvbWF0ZWQgd29ya2Zsb3cgYXBwZWFycyBoYXJkZXIgdGhhbiBkb2N1bWVudGluZyBpdCB0byBiZSBydW4gbWFudWFsbHksIGJ1dAp0aGVyZSBhcmUgbWFueSBiZW5lZml0cyB0byByZXByb2R1Y2liaWxpdHk6CgogIC0gQXV0b21hdGlvbiBmYWNpbGl0YXRlcyByZXBldGl0aW9uLgogIC0gQXV0b21hdGlvbiBjYW4gc2ltcGxpZnkgbWFudWFsIGRvY3VtZW50YXRpb24uCiAgLSBBdXRvbWF0aW9uIHNpbXBsaWZpZXMgdmFsaWRhdGlvbiBvZiB5b3VyIHdvcmtmbG93LgogIC0gQXV0b21hdGlvbiBzdHJlYW1saW5lcyBzaGFyaW5nLgogIC0gQXV0b21hdGlvbiBzY2FsZXMgdG8gbGFyZ2VyIGlucHV0cwoKVGhlcmUgYXJlIG1hbnkgd2F5cyB0byBidWlsZCBhbiBhdXRvbWF0ZWQgd29ya2Zsb3cuIEluIHRoaXMgbW9kdWxlIHdlIHdpbGwKY29uc2lkZXIgc2V2ZXJhbCB3YXlzIG9mIGV4ZWN1dGluZyBbcGxlYXNpbmdseQpwYXJhbGxlbF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRW1iYXJyYXNzaW5nbHlfcGFyYWxsZWwpe3RhcmdldD0iX2JsYW5rIn0KdGFza3Mgb24gR3JlYXQgTGFrZXM6CgotIEEgc2VyaWFsIHRhc2sgbG9vcAotIFBhcmFsbGVsaXppbmcgdGFza3MgdXNpbmcgZHJpdmVyIHNjcmlwdHMgYW5kIHNiYXRjaCBmaWxlcy4KLSBUaGUgU0xVUk0gTGF1bmNoZXIKCkFsbCB0aGVzZSBhcHByb2FjaGVzIGV4ZWN1dGUgdGhlIHNhbWUgd29ya2Zsb3cgaW4gZGlmZmVyZW50IHdheXMuIFRoZSB3b3JrZmxvdwpwcm9kdWNlcyB3b3JkIHBhbmdyYW1zLiBBICoqd29yZCBwYW5ncmFtKiogaXMgbGlrZSBhbiBhbmFncmFtIHRoYXQgYWxsb3dzCnJlcGVhdGluZyBsZXR0ZXJzLCBlLmcuIHRoZSBzZXF1ZW5jZSBvZiBsZXR0ZXJzIEFDRUhNTlQgY2FuIGJlIHJlYXJyYW5nZWQgdG8KY3JlYXRlIHRoZSBwYW5ncmFtcyBBVFRBQ0hNRU5ULCBDQVRDSE1FTlQsIEVOQ0hBTlRNRU5ULCBhbmQgRU5IQU5DRU1FTlQuCgpUaGUgd29ya2Zsb3cgYWNjZXB0cyBhIHRleHQgZmlsZSBjb250YWluaW5nIGxpc3Qgb2YgbGV0dGVyIHNlcXVlbmNlcyBzZXBhcmF0ZWQKYnkgbGluZXM7IGZvciBlYWNoIGxldHRlciBzZXF1ZW5jZSBpdCBwcm9kdWNlcyBhIGZpbGUgY29udGFpbmluZyBvbmUgb3IgbW9yZSAKcGFuZ3JhbXMuCgo8aW1nIHNyYz0iaW1hZ2VzL01vZHVsZTA2X3BhbmdyYW1fd29ya2Zsb3cucG5nIiB3aWR0aD0iNjAlIiBoZWlnaHQ9IjYwJSIvPgoKPGJyLz4KCi0tLQoKIyMgUGFuZ3JhbTogQSBzZXJpYWwgdGFzayBsb29wCgpgYGByCiMgT3JpZW50IG9uIHByb2plY3QgcGFuZ3JhbQpjZCAvbmZzL3R1cmJvL3VtbXMtYmlvaW5mLXdrc2hwL3dvcmtzaG9wL2hvbWUvJFVTRVIKY2QgcHJvamVjdF9wYW5ncmFtcwpscyAtMQpgYGAKCj4gYGBgCnBhbmdyYW1fam9iX2xhdW5jaGVyCnBhbmdyYW1fcGFyYWxsZWxfc2JhdGNoCnBhbmdyYW1fc2VyaWFsX2xvb3AKUkVBRE1FLm1kCiAgICBgYGAKCmBgYHIKY2QgcGFuZ3JhbV9zZXJpYWxfbG9vcApscyAtMQpgYGAKCj4gYGBgCmZpbmRfcGFuZ3JhbXMuc2JhdApmaW5kX3BhbmdyYW1zLnNoCmxldHRlcnMudHh0CnBhbmdyYW0uc2gKUkVBRE1FLm1kCiAgYGBgCgpMZXQncyBjb25zaWRlciBhIGZldyBvZiB0aGVzZSBmaWxlcyBpbiB0dXJuLCBzdGFydGluZyB3aXRoIHRoZSBSRUFETUUuCgo8dGFibGUgY2xhc3M9J2ZpZyc+PHRyPjx0aCBjbGFzcz0nZmlnJz5SRUFETUUubWQ8L3RoPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+PHByZT4KIyBwYW5ncmFtX3NlcmlhbF9sb29wCgotIFByb2R1Y2VzIHBhbmdyYW1zIGZvciBpbnB1dHMgaW4gbGV0dGVycy50eHQuIAogIE1ha2VzIG9uZSBmaWxlIGZvciBlYWNoIGxpbmUgaW4gbGV0dGVycy4KLSBjZ2F0ZXMgNi8xLzIwMjQKLSBVc2FnZTogCiAgLi9maW5kX3BhbmdyYW1zLnNoICN0byBydW4gbG9jYWxseQogIG9yCiAgc2JhdGNoIGZpbmRfcGFuZ3JhbXMuc2JhdCAjIHRvIHN1Ym1pdCB0byB3b3JrZXIgbm9kZQoKRmlsZXM6Ci0gZmluZF9wYW5ncmFtcy5zYmF0OiBTTFVSTSBiYXRjaCBmaWxlOyBjYWxscyBmaW5kX3BhbmdyYW1zLnNoCi0gZmluZF9wYW5ncmFtcy5zaDogTG9vcHMgdGhyb3VnaHQgaW5wdXQgYW5kIGNhbGxzIHBhbmdyYW0uc2gKLSBsZXR0ZXJzLnR4dDogbGlzdCBvZiBsZXR0ZXIgc2VxdWVuY2VzIHNlcGVyYXRlZCBieSBuZXdsaW5lcy4KLSBwYW5ncmFtLnNoOiBhY2NlcHRzIGEgc2luZ2xlIGxldHRlciBzZXF1ZW5jZSBhbmQgcHJpbnRzIGFsbCBwYW5ncmFtcy4KPC9wcmU+PC90ZD48L3RyPjwvdGFibGU+Cjxici8+Cjxici8+CgpUaGUgcGFuZ3JhbS5zaCBzY3JpcHQgaXMgdGhlIHdvcmtob3JzZSBvZiB0aGlzIHdvcmtmbG93LiBZb3UgYXJlIHdlbGNvbWUgdG8gbG9vawphdCB0aGUgaW1wbGVtZW50YXRpb24sIGJ1dCBmb3Igb3VyIHB1cnBvc2VzIHdlIGNhbiB0cmVhdCBpdCBhcyBhIGJsYWNrIGJveC4KV2UnbGwgcnVuIGl0IG9uY2UgdG8gc2VlIGl0IGluIGFjdGlvbi4KCmBgYHIKLi9wYW5ncmFtLnNoIGxvdmVseQpgYGAKCj4gYGBgCmxvdmVseQpsb3ZleQp2b2xsZXkKICBgYGAKCgpDb25zaWRlciB0aGUgaW5wdXQgdG8gdGhlIHdvcmtmbG93OgoKPHRhYmxlIGNsYXNzPSdmaWcnIHdpZHRoPScxMDAlJz48dHI+PHRoIGNsYXNzPSdmaWcnPmxldHRlcnMudHh0PC90aD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPgpgYGAKTmRlZmdsdQpIYWNpbG5vClRkZ2hub3UKTmFpbG1wdApQYmVnaWtuCllhY2lscnQKQWNobm9weQpVZ2lub3F0CkVhY2hrbW4KQWxoeWlkbgpgYGAKPC90ZD48L3RyPjwvdGFibGU+Cjxici8+Cjxici8+CgpBbmQgZmluYWxseSwgdGhlIHNjcmlwdCB3ZSB3aWxsIGxhdW5jaCB0byBleGVjdXRlIHRoZSB3b3JrZmxvdzoKCjx0YWJsZSBjbGFzcz0nZmlnJyB3aWR0aD0nMTAwJSc+PHRyPjx0aCBjbGFzcz0nZmlnJz5maW5kX3BhbmdyYW1zLnNoPC90aD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPgpgYGAKIy9iaW4vYmFzaApzZXQgLWV1Cgpmb3IgbGV0dGVycyBpbiAkKGNhdCBsZXR0ZXJzLnR4dCk7IGRvCiAgICBlY2hvIHBhbmdyYW1zIGZvcjogJGxldHRlcnMgPj4gL2Rldi9zdGRlcnIKICAgIC4vcGFuZ3JhbS5zaCAkbGV0dGVycyA+IHJlc3VsdHMuJHtsZXR0ZXJzfS50eHQKZG9uZQplY2hvIGRvbmUgPj4gL2Rldi9zdGRlcnIKYGBgCjwvdGQ+PC90cj48L3RhYmxlPgo8YnIvPgo8YnIvPgoKSGF2aW5nIHJldmlld2VkIHRoZSBpbnB1dHMgYW5kIHNjcmlwdHMsIHdlIGNhbiBsYXVuY2ggdGhlIHdvcmtmbG93IGxpa2UgYXMgCnNob3duIGJlbG93LiAoVGhpcyBwcm9qZWN0IGlzIGNhbGxlZCAic2VyaWFsIGxvb3AiIGJlY2F1c2UgaW4gdGhpcyB3b3JrZmxvdyAKd2UgYXJlIGxvb3Bpbmcgb3ZlciB0aGUgaW5wdXRzIGFuZCBwcm9jZXNzaW5nIG9uZSBhdCBhIHRpbWUuKQoKYGBgcgouL2ZpbmRfcGFuZ3JhbXMuc2gKYGBgCgo+IGBgYApwYW5ncmFtcyBmb3I6IE5kZWZnbHUKcGFuZ3JhbXMgZm9yOiBIYWNpbG5vCnBhbmdyYW1zIGZvcjogVGRnaG5vdQpwYW5ncmFtcyBmb3I6IE5haWxtcHQKcGFuZ3JhbXMgZm9yOiBQYmVnaWtuCnBhbmdyYW1zIGZvcjogWWFjaWxydApwYW5ncmFtcyBmb3I6IEFjaG5vcHkKcGFuZ3JhbXMgZm9yOiBVZ2lub3F0CnBhbmdyYW1zIGZvcjogRWFjaGttbgpwYW5ncmFtcyBmb3I6IEFsaHlpZG4KICBgYGAKCldlIHNlZSB0aGUgcmVzdWx0cyBmaWxlcyBoYXZlIGJlZW4gYWRkZWQ6CgpgYGByCmxzCmBgYAoKPiBgYGAKZmluZF9wYW5ncmFtcy5zYmF0ICByZXN1bHRzLkFjaG5vcHkudHh0ICByZXN1bHRzLk5kZWZnbHUudHh0CmZpbmRfcGFuZ3JhbXMuc2ggICAgcmVzdWx0cy5BbGh5aWRuLnR4dCAgcmVzdWx0cy5QYmVnaWtuLnR4dApsZXR0ZXJzLnR4dCAgICAgICAgIHJlc3VsdHMuRWFjaGttbi50eHQgIHJlc3VsdHMuVGRnaG5vdS50eHQKcGFuZ3JhbS5zaCAgICAgICAgICByZXN1bHRzLkhhY2lsbm8udHh0ICByZXN1bHRzLlVnaW5vcXQudHh0ClJFQURNRS5tZCAgICAgICAgICAgcmVzdWx0cy5OYWlsbXB0LnR4dCAgcmVzdWx0cy5ZYWNpbHJ0LnR4dAogIGBgYAogIApgYGByCmNhdCByZXN1bHRzLkFjaG5vcHkudHh0CmBgYAo+IGBgYApjYWNvcGhvbnkKICBgYGAKCipRdWVzdGlvbjogV2hpY2ggbGV0dGVyIGNvbWJpbmF0aW9uIGdlbmVyYXRlZCB0aGUgbW9zdCBwYW5ncmFtcz8qCgpMZXQncyBydW4gaXQgYWdhaW4gYnV0IGluc3RlYWQgb2YgdXNpbmcgdGhlIGxvZ2luLW5vZGUsIHdlJ2xsIHN1Ym1pdCB0aGlzIHRvIGEKd29ya2VyIG5vZGUgdXNpbmcgdGhlIHByb3ZpZGVkIHNiYXQgc2NyaXB0OgoKYGBgcgojIGZpcnN0IGNsZWFyIG91dCB0aGUgb2xkIHJlc3VsdHMKcm0gcmVzdWx0cy4qCnNiYXRjaCBmaW5kX3BhbmdyYW1zLnNiYXQgCmBgYAoKPiBgYGAKU3VibWl0dGVkIGJhdGNoIGpvYiAxMjM0NTY3CiAgYGBgCgpVc2UgYHNxdWV1ZSAtdSAkVVNFUmAgdG8gc2VlIHdoZW4gdGhlIGpvYiBpcyBmaW5pc2hlZCBhbmQgdGhlbiByZXZpZXcgdGhlIApvdXRwdXRzLiBXZSBub3cgc2VlIHRoZSByZXN1bHRzIGZpbGVzIGFuZCBhbHNvIHRoZSBzbHVybSBsb2cgZmlsZToKCj4gYGBgCmZpbmRfcGFuZ3JhbXMuc2JhdCAgIHJlc3VsdHMuQWxoeWlkbi50eHQgIHJlc3VsdHMuVGRnaG5vdS50eHQKZmluZF9wYW5ncmFtcy5zaCAgICAgcmVzdWx0cy5FYWNoa21uLnR4dCAgcmVzdWx0cy5VZ2lub3F0LnR4dApsZXR0ZXJzLnR4dCAgICAgICAgICByZXN1bHRzLkhhY2lsbm8udHh0ICByZXN1bHRzLllhY2lscnQudHh0CnBhbmdyYW0uc2ggICAgICAgICAgIHJlc3VsdHMuTmFpbG1wdC50eHQgIHNsdXJtLTEyMzQ1Njcub3V0ClJFQURNRS5tZCAgICAgICAgICAgIHJlc3VsdHMuTmRlZmdsdS50eHQKcmVzdWx0cy5BY2hub3B5LnR4dCAgcmVzdWx0cy5QYmVnaWtuLnR4dAogIGBgYAoKVGhpcyBhcHByb2FjaCBpcyBjb3JyZWN0LCBjbGVhciwgYW5kIHJlcHJvZHVjaWJsZTsgaG93ZXZlciBpdCdzIG5vdCBpZGVhbC4KQ29uc2lkZXIgaG93IHRoZSB0YXNrcyBhcmUgY29udGFpbmVkIHdpdGhpbiBhIGpvYjoKCjx0YWJsZSBjbGFzcz0nZmlnJyB3aWR0aD0nMTAwJSc+PHRyPjx0aCBjbGFzcz0nZmlnJz5Kb2IvdGFzayBnZW9tZXRyeSBvZiB0aGUgc2VyaWFsIGxvb3AgYXBwcm9hY2g8L3RoPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+IVtdKGltYWdlcy9Nb2R1bGUwNl9nZW9tZXRyeV9zZXJpYWxfbG9vcC5wbmcpPC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPkVhY2ggc2JhdGNoIHJlcXVlc3QgaXMgYSBqb2Igc2NyaXB0OyBhIGpvYiBzY3JpcHQgbWF5IGJlCmNvbXBvc2VkIG9mIG11bHRpcGxlIHRhc2tzLiBLZXkgYXR0cmlidXRlcyBvZiBhICoqam9iIHNjcmlwdCoqIGFyZSAKCi0gd2hhdCBzdWItdGFza3Mgd2lsbCBJIHJ1bj8KLSBob3cgbWFueSByZXNvdXJjZXMgZG8gSSBuZWVkPwotIGhvdyBsb25nIHdpbGwgSSBuZWVkIHRvIHJ1bj8KCllvdSBjYW4gcmVwcmVzZW50IHRoZXNlIGdyYXBoaWNhbGx5IGJ5IG1ha2luZyBib3hlcyBmb3IgZWFjaCBqb2IgYW5kIHRoZWlyIHRhc2tzCihoZWlnaHQgPSByZXNvdXJjZSByZXF1ZXN0IGFuZCBsZW5ndGggPSB0aW1lKS4gSW4gdGhlIGNhc2UgYWJvdmUsIHRoZXJlIGFyZSBtYW55CnNpbWlsYXIgdGFza3MgY29udGFpbmVkIGluIGEgc2luZ2xlIGpvYi4gVGhpcyBkaWFncmFtIGlzIGEgcm91Z2ggcmVwcmVzZW50YXRpb24Kb2YgdGhlICoqam9iL3Rhc2sgZ2VvbWV0cnkqKi4gVGhpcyBpcyBhIHVzZWZ1bCB3YXkgb2YgdmlzdWFsaXppbmcgYW5kIGNvbXBhcmluZwphcHByb2FjaGVzOyBhbHNvLCB0aGUgam9iIGdlb21ldHJ5IGlzIGNyaXRpY2FsbHkgdXNlZnVsIGluZm9ybWF0aW9uIHRvIHRoZQpzY2hlZHVsZXIgd2hpY2ggaXMgdHJ5aW5nIHRvIHBhY2sgZXZlcnlvbmUncyBqb2JzIGludG8gdGhlIGF2YWlsYWJsZSBjbHVzdGVycyBhcwpuZWF0bHkvZWZmaWNpZW50bHkgYXMgcG9zc2libGUuCgo8L3RkPjwvdHI+PC90YWJsZT4KPGJyLz4KCkNvbnNpZGVyaW5nIHRoYXQgZWFjaCBvZiB0aGVzZSBwYW5ncmFtIHRhc2tzIGFyZSBjb21wbGV0ZWx5IGluZGVwZW5kZW50IG9mIGVhY2gKb3RoZXIgKGkuZS4gcGxlYXNpbmdseSBwYXJhbGxlbCkuIFdlIG1pZ2h0IGJlIGFibGUgdG8gbWFrZSBiZXR0ZXIgdXNlIG9mIG91cgp+MTYwMDAgQ1BVcyBieSBwYXJhbGxlbGl6aW5nIHRoZSB3b3JrZmxvdy4gVGhlIGFwcHJvYWNoZXMgYmVsb3cgc2hvdyB0d28gCmRpZmZlcmVudCB3YXlzIHRvIGFjY29tcGxpc2ggdGhpcy4KCgo8YnIvPgoKLS0tCgojIyBQYW5ncmFtOiBQYXJhbGxlbCBTQkFUQ0gKCmBgYHIKIyBPcmllbnQgb24gcHJvamVjdApjZCAvbmZzL3R1cmJvL3VtbXMtYmlvaW5mLXdrc2hwL3dvcmtzaG9wL2hvbWUvJFVTRVIKY2QgcHJvamVjdF9wYW5ncmFtcy9wYW5ncmFtX3BhcmFsbGVsX3NiYXRjaApscyAtMQpgYGAKCj4gYGBgCmxldHRlcnMudHh0Cm1ha2Vfc2JhdF9zY3JpcHRzLnNoCnBhbmdyYW0uc2gKUkVBRE1FLm1kCnJ1bl9zYmF0X3NjcmlwdHMuc2gKICBgYGAKCjx0YWJsZSBjbGFzcz0nZmlnJz48dHI+PHRoIGNsYXNzPSdmaWcnPlJFQURNRS5tZDwvdGg+PC90cj4KPHRyPjx0ZCBjbGFzcz0nZmlnJz48cHJlPgojIHBhbmdyYW1fcGFyYWxsZWxfc2JhdGNoCgotIFByb2R1Y2VzIHBhbmdyYW1zIGZvciBsZXR0ZXJzLnR4dC4gTWFrZXMgb25lIGZpbGUgcGVyIGxpbmUgaW4gbGV0dGVycy4KLSBjZ2F0ZXMgNi8xLzIwMjQKLSBVc2FnZTogCiAgYC4vbWFrZV9zYmF0X3NjcmlwdHMuc2hgCiAgYC4vcnVuX3NiYXRfc2NyaXB0cy5zaGAKCkZpbGVzOgotIGxldHRlcnMudHh0OiBsaXN0IG9mIGxldHRlciBzZXF1ZW5jZXMgc2VwZXJhdGVkIGJ5IG5ld2xpbmVzLgotIHBhbmdyYW0uc2g6IGFjY2VwdHMgYSBzaW5nbGUgbGV0dGVyIHNlcXVlbmNlIGFuZCBwcmludHMgYWxsIHBhbmdyYW1zLgotIG1ha2Vfc2JhdF9zY3JpcHRzLnNoIDogQnVpbGQgc2JhdCBzY3JpcHRzIGJhc2VkIG9uIGxldHRlcnMudHh0OyAKICBmb3IgZWFjaCByb3cgaW4gbGV0dGVycy50eHQgYWRkcyBhIG5ldyBzYmF0IGZpbGUuCi0gcnVuX3NiYXRfc2NyaXB0cy5zaCA6IFN1Ym1pdCBhbGwgc2JhdCBzY3JpcHRzIGZvciBjbHVzdGVyIGV4ZWN1dGlvbiAKPC9wcmU+PC90ZD48L3RyPjwvdGFibGU+Cjxici8+CgpFeGVjdXRpbmcgbWFrZV9zYmF0X3NjcmlwdHMgY3JlYXRlcyBhIG5ldyBkaXJlY3RvcnkgYW5kIGFkZGluZyBhIGNvbGxlY3Rpb24gb2YgCnNiYXQgZmlsZXMuCgpgYGByCi4vbWFrZV9zYmF0X3NjcmlwdHMuc2ggCmxzIHNiYXRfc2NyaXB0cwpgYGAKCj4gYGBgCi4uLgpBY2hub3B5LnNiYXQgIE5haWxtcHQuc2JhdCAgVWdpbm9xdC5zYmF0CkFsaHlpZG4uc2JhdCAgTmRlZmdsdS5zYmF0ICBZYWNpbHJ0LnNiYXQKRWFjaGttbi5zYmF0ICBQYmVnaWtuLnNiYXQKSGFjaWxuby5zYmF0ICBUZGdobm91LnNiYXQKICBgYGAKCkNvbnNpZGVyIGEgc2luZ2xlIHNiYXQgZmlsZS4KCjx0YWJsZSBjbGFzcz0nZmlnJz48dHI+PHRoIGNsYXNzPSdmaWcnPnNiYXRfc2NyaXB0cy9BY2hub3B5LnNiYXQ8L3RoPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+PHByZT4KIyEvYmluL2Jhc2gKCiNTQkFUQ0ggLS1qb2ItbmFtZT1wYW5ncmFtX0FjaG5vcHkKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MQojU0JBVENIIC0tbm9kZXM9MQojU0JBVENIIC0tbnRhc2tzLXBlci1ub2RlPTEKI1NCQVRDSCAtLW1lbS1wZXItY3B1PTQwMG0KI1NCQVRDSCAtLXRpbWU9MDA6MDU6MDAKI1NCQVRDSCAtLWFjY291bnQ9YmlvaW5mX3drc2hwX2NsYXNzCiNTQkFUQ0ggLS1wYXJ0aXRpb249c3RhbmRhcmQKLi9wYW5ncmFtLnNoIEFjaG5vcHkgPiByZXN1bHRzLkFjaG5vcHkudHh0CjwvcHJlPjwvdGQ+PC90cj48L3RhYmxlPgo8YnIvPgoKQnJpZWZseSBjb25zaWRlciB0aGUgc2NyaXB0IHRvIHNlZSBob3cgdGhlc2Ugc2JhdCBmaWxlcyB3YXMgY29uc3RydWN0ZWQuIE5vdGUgCnRoZSBTTFVSTSBwcmVhbWJsZSBkaXJlY3RpdmVzIGFyZSBpbnRlZ3JhdGVkIGludG8gdGhlIG5ldyBmaWxlcyB1c2luZyBhIApbSGVyZURvY10oaHR0cHM6Ly9waG9lbml4bmFwLmNvbS9rYi9iYXNoLWhlcmVkb2Mpe3RhcmdldD0iX2JsYW5rIn0uCgo8dGFibGUgY2xhc3M9J2ZpZycgd2lkdGg9IjEwMCUiPjx0cj48dGggY2xhc3M9J2ZpZyc+bWFrZV9zYmF0X3NjcmlwdHMuc2g8L3RoPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+PHByZT4KIyEvYmluL2Jhc2gKc2V0IC1ldQoKbWtkaXIgLXAgc2JhdF9zY3JpcHRzCgpmb3IgbGV0dGVycyBpbiAkKGNhdCBsZXR0ZXJzLnR4dCk7IGRvCiAgICBlY2hvIHNiYXQgZm9yOiAkbGV0dGVycyA+PiAvZGV2L3N0ZGVycgogICAgY2F0IDw8IEhFUkVfRE9DID4gc2JhdF9zY3JpcHRzLyRsZXR0ZXJzLnNiYXQKIyEvYmluL2Jhc2gKCiNTQkFUQ0ggLS1qb2ItbmFtZT1wYW5ncmFtXyR7bGV0dGVyc30KI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MQojU0JBVENIIC0tbm9kZXM9MQojU0JBVENIIC0tbnRhc2tzLXBlci1ub2RlPTEKI1NCQVRDSCAtLW1lbS1wZXItY3B1PTQwMG0KI1NCQVRDSCAtLXRpbWU9MDA6MDU6MDAKI1NCQVRDSCAtLWFjY291bnQ9YmlvaW5mX3drc2hwX2NsYXNzCiNTQkFUQ0ggLS1wYXJ0aXRpb249c3RhbmRhcmQKLi9wYW5ncmFtLnNoICRsZXR0ZXJzID4gcmVzdWx0cy4ke2xldHRlcnN9LnR4dApIRVJFX0RPQwpkb25lCmVjaG8gZG9uZSA+PiAvZGV2L3N0ZGVycgo8L3ByZT48L3RkPjwvdHI+PC90YWJsZT4KPGJyLz4KCldlIGNhbiBzdWJtaXQgam9icyBvbmUgYXQgYSB0aW1lIHVzaW5nIHNiYXRjaC4gT1Igd2UgY291bGQgYnVpbGQgYSAKZm9yIGxvb3AgdG8gYXV0b21hdGUgc3VibWlzc2lvbjsgY29udmVuaWVudGx5IGBydW5fc2JhdF9zY3JpcHRzLnNoYCBoYXMKZG9uZSB0aGlzIGZvciB1cy4KCjx0YWJsZSBjbGFzcz0nZmlnJyB3aWR0aD0iMTAwJSI+PHRyPjx0aCBjbGFzcz0nZmlnJz5ydW5fc2JhdF9zY3JpcHRzLnNoPC90aD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+CmZvciBzYmF0IGluICQobHMgc2JhdF9zY3JpcHRzLyouc2JhdCk7IGRvIAogIHNiYXRjaCAkc2JhdApkb25lCjwvcHJlPjwvdGQ+PC90cj48L3RhYmxlPgo8YnIvPgoKQmVmb3JlIHdlIGV4ZWN1dGUgYHJ1bl9zYmF0X3NjcmlwdHMuc2hgLCB5b3UgbWlnaHQgY29uc2lkZXIgb3BlbmluZyBhIHNlcGFyYXRlIAp3aW5kb3cgdG8gbW9uaXRvciB0aGUgU0xVUk0gam9iIHF1ZXVlLiBJbiB0aGlzIHNlY29uZCB3aW5kb3csIHlvdSBjYW4gZXhlY3V0ZSAKYHdhdGNoIHNxdWV1ZSAtdSAkVVNFUmAgdG8gc2VlIGhvdyB0aGUgam9icyBhcmUgYmVpbmcgc2NoZWR1bGVkLiBIaXQgY3RybC1DIHRvIApleGl0IHdhdGNoLiAoTW9yZSBpbmZvIG9uIFt3YXRjaF0oaHR0cHM6Ly9saW51eC5kaWUubmV0L21hbi8xL3dhdGNoKS4pCgpgYGByCi4vcnVuX3NiYXRfc2NyaXB0cy5zaCAKYGBgCgo+IGBgYApTdWJtaXR0ZWQgYmF0Y2ggam9iIDkyODk0OTYKU3VibWl0dGVkIGJhdGNoIGpvYiA5Mjg5NDk3ClN1Ym1pdHRlZCBiYXRjaCBqb2IgOTI4OTQ5OApTdWJtaXR0ZWQgYmF0Y2ggam9iIDkyODk0OTkKU3VibWl0dGVkIGJhdGNoIGpvYiA5Mjg5NTAwClN1Ym1pdHRlZCBiYXRjaCBqb2IgOTI4OTUwMQpTdWJtaXR0ZWQgYmF0Y2ggam9iIDkyODk1MDIKU3VibWl0dGVkIGJhdGNoIGpvYiA5Mjg5NTAzClN1Ym1pdHRlZCBiYXRjaCBqb2IgOTI4OTUwNApTdWJtaXR0ZWQgYmF0Y2ggam9iIDkyODk1MDUKICBgYGAKCkFuZCBpbiBhIGZldyBzaG9ydCBzZWNvbmRzLCB5b3Ugc2VlIHRoZSByZXN1bHRzIGFuZCBTTFVSTSBsb2cgZmlsZXMuIFRoaXMKYXBwcm9hY2ggaXMgY29ycmVjdCwgbW9yZSBjb21wbGV4IHRoYW4gdGhlIHNlcmlhbCBsb29wLCBhbmQgcmVwcm9kdWNpYmxlLiBBbmQKYmVjYXVzZSB0aGUgdGFza3MgYXJlIHdvcmtpbmcgaW4gcGFyYWxsZWwsIGl0J3MgKiptdWNoKiogZmFzdGVyLiBDb250cmFzdCB0aGlzCmpvYi90YXNrIGdlb21ldHJ5IHdpdGggdGhlIHNlcmlhbCBsb29wIGFwcHJvYWNoIGZyb20gYWJvdmU6Cgo8dGFibGUgY2xhc3M9J2ZpZycgd2lkdGg9JzEwMCUnPjx0cj48dGggY2xhc3M9J2ZpZyc+Sm9iL3Rhc2sgZ2VvbWV0cmllczogc2VyaWFsIGxvb3AgdnMgcGFyYWxsZWwgc2JhdGNoPC90aD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxpbWcgc3JjPSJpbWFnZXMvTW9kdWxlMDZfZ2VvbWV0cnlfc2VyaWFsX2xvb3Bfc21hbGwucG5nIi8+PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxpbWcgc3JjPSJpbWFnZXMvTW9kdWxlMDZfZ2VvbWV0cnlfcGFyYWxsZWwucG5nIiBoZWlnaHQ9IjQwJSIgd2lkdGg9IjQwJSIvPjwvdGQ+PC90cj4KPC90YWJsZT4KClRoaXMgaXMgZ3JlYXQuIEJ1dCB0aGVyZSdzIHR3byB0byB0aHJlZSBtaW5vciBkcmF3YmFja3MgdG8gdGhpcyBhcHByb2FjaDoKCi0gQmV0d2VlbiB0aGUgdGhlIHNiYXQgZmlsZXMsIHRoZSByZXN1bHQgZmlsZXMsIGFuZCB0aGUgc2x1bSBsb2cgZmlsZXMsIGl0J3MKY3JlYXRlZCBxdWl0ZSBhIGxvdCBtb3JlIGZpbGVzLiBUaGV5IGFyZSBzbWFsbGlzaCBmaWxlcywgYnV0IGl0J3MgbW9yZSBvdXRwdXQgdG8Ka2VlcCB0cmFjayBvZi4KLSBXaGVuIHRoZSBzY2hlZHVsZXIgaXMgdW5kZXIgYSBoZWF2eSBsb2FkLCBmb3IgdmVyeSBxdWljayBqb2JzICg8PTYwIHNlY29uZHMpCml0IGNhbiB0YWtlIGxvbmdlciB0byBzY2hlZHVsZSBhIGpvYiB0aGFuIGl0IHRha2VzIHRvIHJ1biB0aGUgam9iLiBJbiB0aGVzZQpjaXJjdW1zdGFuY2VzIHRoZSBzZXJpYWwgYXBwcm9hY2ggbWlnaHQgYmUgZmFzdGVyLiAoSWYgd2Ugc2NhbGVkIHVwIGZyb20gMTAgam9icwp0byAxMDAwIGpvYnMgaW4gcGFyYWxsZWwsIHdlIG1pZ2h0IHNlZSB0aGlzIGtpbmQgb2Ygc2xvd2Rvd24uKQotIEVhY2ggQVJDIGFjY291bnQgaGFzIGFuIHVwcGVyIGxpbWl0IG9uIHRoZSBudW1iZXIgb2Ygam9icyB0aGF0IGNhbiBiZSAKc3VibWl0dGVkIGFuZCB0aGUgbnVtYmVyIGFjdGl2ZWx5IHJ1bm5pbmcuIElmIHlvdXIgYWNjb3VudCBleGNlZWRzIHRoaXMgbGltaXQgCnRoZSBqb2JzIHdpbGwgc3RhcnQgdG8gcXVldWUgdXAsIGF3YWl0aW5nIGEgdHVybiBhdCBzY2hlZHVsaW5nIGFuZCBleGVjdXRpb24uIApUaGF0J3Mgbm90IGEgYmlnIHByb2JsZW0sIGV4Y2VwdCBmb3IgdGhlIGZhY3QgdGhhdCB5b3Ugc2hhcmUgdGhlIGFjY291bnQgd2l0aApvdGhlciB1c2Vycy4gSWYgeW91IHNhdHVyYXRlIHlvdXIgcXVldWUsIG90aGVycyB3aWxsIGhhdmUgdG8gd2FpdCB1bnRpbCB5b3VyIGpvYgpmaW5pc2hlcyBiZWZvcmUgc3RhcnRpbmcgdGhlaXJzLiAKClRvIGFkZHJlc3MgdGhlc2UgY29uY2VybnMsIHRoZSBUZXhhcyBBZHZhbmNlZCBDb21wdXRpbmcgQ2VudGVyIGJ1aWx0IGEgU0xVUk0KdG9vbCBjYWxsZWQgW2xhdW5jaGVyXShodHRwczovL2FyYy51bWljaC5lZHUvZ3JlYXRsYWtlcy9zb2Z0d2FyZS9sYXVuY2hlci8pe3RhcmdldD0iX2JsYW5rIn0KZGV0YWlsZWQgYmVsb3cuCgo8YnIvPgoKLS0tCgojIyBQYW5ncmFtOiBMYXVuY2hlcgoKYGBgcgojIE9yaWVudCBvbiBwcm9qZWN0CmNkIC9uZnMvdHVyYm8vdW1tcy1iaW9pbmYtd2tzaHAvd29ya3Nob3AvaG9tZS8kVVNFUgpjZCBwcm9qZWN0X3BhbmdyYW1zL3BhbmdyYW1fbGF1bmNoZXIKbHMgLTEKYGBgCgo+IGBgYApsYXVuY2hlci5zYmF0CmxldHRlcnMudHh0Cm1ha2VfbGF1bmNoZXJfdGFza3Muc2gKcGFuZ3JhbS5zaApSRUFETUUubWQKICBgYGAKCjx0YWJsZSBjbGFzcz0nZmlnJz48dHI+PHRoIGNsYXNzPSdmaWcnPlJFQURNRS5tZDwvdGg+PC90cj4KPHRyPjx0ZCBjbGFzcz0nZmlnJz48cHJlPgojIHBhbmdyYW1fbGF1bmNoZXIKCi0gUHJvZHVjZXMgcGFuZ3JhbXMgZm9yIGxldHRlcnMudHh0LiBNYWtlcyBvbmUgZmlsZSBwZXIgbGluZSBpbiBsZXR0ZXJzLgotIGNnYXRlcyA2LzEvMjAyNAotIFVzYWdlOiAKICBgLi9tYWtlX2xhdW5jaGVyX3Rhc2tzLnNoYAogIGBzYmF0Y2ggbGF1bmNoZXIuc2JhdGAKCkZpbGVzOgotIGxhdW5jaGVyLnNiYXQ6IHNiYXRjaCBmaWxlIHRvIHN0YXJ0IHRoZSBsYXVuY2hlci4KLSBsZXR0ZXJzLnR4dDogbGlzdCBvZiBsZXR0ZXIgc2VxdWVuY2VzIHNlcGVyYXRlZCBieSBuZXdsaW5lcy4KLSBtYWtlX2xhdW5jaGVyX3Rhc2tzLnNoOiBidWlsZHMgYSBzaW5nbGUgZmlsZSBmb3IgYWxsIHRhc2tzIHRvIGJlIGV4ZWN1dGVkIAogIGJ5IHRoZSBsYXVuY2hlci4KLSBwYW5ncmFtLnNoOiBhY2NlcHRzIGEgc2luZ2xlIGxldHRlciBzZXF1ZW5jZSBhbmQgcHJpbnRzIGFsbCBwYW5ncmFtcy4KPC9wcmU+PC90ZD48L3RyPjwvdGFibGU+Cjxici8+CgpSdW4gYG1ha2VfbGF1bmNoZXJfdGFza3Muc2hgIGFuZCBub3RlIGl0IGNyZWF0ZXMgb25lIG5ldyBmaWxlIGBsYXVuY2hlcl90YXNrcy50eHRgLgoKYGBgcgouL21ha2VfbGF1bmNoZXJfdGFza3Muc2ggCmBgYAoKPHRhYmxlIGNsYXNzPSdmaWcnPjx0cj48dGggY2xhc3M9J2ZpZyc+bGF1bmNoZXJfdGFza3MudHh0PC90aD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+Ci4vcGFuZ3JhbS5zaCBOZGVmZ2x1ID4gcmVzdWx0cy5OZGVmZ2x1LnR4dAouL3BhbmdyYW0uc2ggSGFjaWxubyA+IHJlc3VsdHMuSGFjaWxuby50eHQKLi9wYW5ncmFtLnNoIFRkZ2hub3UgPiByZXN1bHRzLlRkZ2hub3UudHh0Ci4vcGFuZ3JhbS5zaCBOYWlsbXB0ID4gcmVzdWx0cy5OYWlsbXB0LnR4dAouL3BhbmdyYW0uc2ggUGJlZ2lrbiA+IHJlc3VsdHMuUGJlZ2lrbi50eHQKLi9wYW5ncmFtLnNoIFlhY2lscnQgPiByZXN1bHRzLllhY2lscnQudHh0Ci4vcGFuZ3JhbS5zaCBBY2hub3B5ID4gcmVzdWx0cy5BY2hub3B5LnR4dAouL3BhbmdyYW0uc2ggVWdpbm9xdCA+IHJlc3VsdHMuVWdpbm9xdC50eHQKLi9wYW5ncmFtLnNoIEVhY2hrbW4gPiByZXN1bHRzLkVhY2hrbW4udHh0Ci4vcGFuZ3JhbS5zaCBBbGh5aWRuID4gcmVzdWx0cy5BbGh5aWRuLnR4dAo8L3ByZT48L3RkPjwvdHI+PC90YWJsZT4KPGJyLz4KClRoaXMgbWlnaHQgcmVtaW5kIHlvdSBvZiB0aGUgc2VyaWFsIGxvb3AgYXBwcm9hY2gsIGJ1dCB0aGVyZSdzIGEgdHdpc3QgYW5kIHRvIApzZWUgaXQgeW91IG5lZWQgdG8gY29uc2lkZXIgdGhlIGBsYXVuY2hlci5zYmF0YCBmaWxlOgoKPHRhYmxlIGNsYXNzPSdmaWcnPjx0cj48dGggY2xhc3M9J2ZpZyc+bGF1bmNoZXIuc2JhdDwvdGg+PC90cj4KPHRyPjx0ZCBjbGFzcz0nZmlnJz48cHJlPgojIS9iaW4vYmFzaAojU0JBVENIIC0tYWNjb3VudD1iaW9pbmZfd2tzaHBfY2xhc3MKI1NCQVRDSCAtLXBhcnRpdGlvbj1zdGFuZGFyZAojU0JBVENIIC0tbm9kZXM9MQojU0JBVENIIC0tbnRhc2tzLXBlci1ub2RlPTUKI1NCQVRDSCAtLWNwdXMtcGVyLXRhc2s9MQojU0JBVENIIC0tdGltZT0wOjMwOjAwCgptb2R1bGUgbG9hZCBsYXVuY2hlcgpleHBvcnQgTEFVTkNIRVJfSk9CX0ZJTEU9bGF1bmNoZXJfdGFza3MudHh0CnBhcmFtcnVuCjwvcHJlPjwvdGQ+PC90cj4KPHRyPjx0ZCBjbGFzcz0nZmlnJz5Tb21lIGRldGFpbHM6CgotIFRoZSBmaWxlIHN0YXJ0cyB3aXRoIGEgYmFzaWMgU0xVUk0gcHJlYW1ibGUuIE5vdGUgdGhhdCBpdCdzIGFza2luZyBmb3IgMSBub2RlLAogIGFuZCA1IENQVXMgKDUgdGFza3Mvbm9kZSAqIDEgY3B1L3Rhc2spIGZvciAzMCBtaW51dGVzLgotIFRoZSBsYXN0IHRocmVlIGxpbmVzIGVzdGFibGlzaCB0aGF0IHRoaXMgaXMgYSBsYXVuY2hlciBqb2IgYW5kIHRoZSB0YXNrcyB0bwogIGV4ZWN1dGUgbGl2ZSBpbiBsYXVuY2hlcl90YXNrcy50eHQuCjwvdGQ+PC90cj4KPC90YWJsZT4KPGJyLz4KCkdpdmVuIHRoaXMgc2V0dXAsIHNiYXRjaCB3aWxsIGFsbG9jYXRlIGEgbm9kZSB3aXRoIDUgQ1BVcyBmb3IgMzAgbWludXRlcy4gVGhlbiAKdGhlIGxhdW5jaGVyIHdpbGwgc3RhcnQgbG9vcGluZyB0aHJvdWdoIHRoZSBsYXVuY2hlcl90YXNrcyBhbmQgYXMgZWFjaCBvbmUKY29tcGxldGVzIGl0IHdpbGwgc2VuZCBhbm90aGVyIG9uZSB0aHJvdWdoIHVudGlsIGFsbCB0YXNrcyBhcmUgY29tcGxldGUuCgpDb25zaWRlciBydW5uaW5nIGB3YXRjaCBzcXVldWUgLXUgJFVTRVJgIGluIGFub3RoZXIgd2luZG93IGJlZm9yZSB5b3UgcnVuIHRoZSAKc2JhdGNoIGNvbW1hbmQ6CmBgYHIKc2JhdGNoIGxhdW5jaGVyLnNiYXQKYGBgCj4gYGBgClN1Ym1pdHRlZCBiYXRjaCBqb2IgOTI5MDUzNQogIGBgYAoKTm90ZSB0aGF0IGFsbCB0aGUgdGFza3MgYXJlIHJ1bm5pbmcgYnV0IHRoZXkgYXJlIHJ1bm5pbmcgImluc2lkZSIiIG9mIHRoZSBvbmUgam9iLgpUaGUgam9iIHNob3VsZCBmaW5pc2ggaW4gYSBmZXcgc2Vjb25kcy4gSXQgd2lsbCBwcm9kdWNlIHRoZSBmYW1pbGxpYXIgYHJlc3VsdHMuKmAKZmlsZXMgYW5kIGFsc28gYSBzaW5nbGUgU0xVUk0gbG9nIGZpbGUgd2hpY2ggaXMgYSBiaXQgbW9yZSBpbnRlcmVzdGluZyB0aGFuIHRoZSAKcHJldmlvdXMgbG9nIGZpbGVzLgoKPHRhYmxlIGNsYXNzPSdmaWcnPjx0cj48dGggY2xhc3M9J2ZpZyc+c2x1cm0tOTI5MDUzNS5vdXQ8L3RoPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+PHByZSBjbGFzcz0icHJlLXNjcm9sbGFibGUiPgpXQVJOSU5HICgwNi8wOS8yNCAxNTozODozOSk6IExBVU5DSEVSX1dPUktESVIgdmFyaWFibGUgbm90IHNldC4gVXNpbmcgY3VycmVudCBkaXJlY3RvcnkuCndpbmRvd3NQIGlzIGZhbHNlCk5PVEUgKDA2LzA5LzI0IDE1OjM4OjQwKTogU3RhcnRlZCBkeW5hbWljIHRhc2sgc2VydmljZSBvbiBwb3J0IDk0NzEKTGF1bmNoZXI6IFNldHVwIGNvbXBsZXRlLgoKLS0tLS0tLS0tLS0tLSBTVU1NQVJZIC0tLS0tLS0tLS0tLS0tLQogICBOdW1iZXIgb2YgaG9zdHM6ICAgIDEKICAgV29ya2luZyBkaXJlY3Rvcnk6ICAvbmZzL3R1cmJvL3VtbXMtYmlvaW5mLXdrc2hwL3dvcmtzaG9wL2hvbWUvY2dhdGVzL3Byb2plY3RfcGFuZ3JhbXMvcGFuZ3JhbV9sYXVuY2hlcgogICBQcm9jZXNzZXMgcGVyIGhvc3Q6IDUKICAgVG90YWwgcHJvY2Vzc2VzOiAgICA1CiAgIFRvdGFsIGpvYnM6ICAgICAgICAgMTAKICAgU2NoZWR1bGluZyBtZXRob2Q6ICBkeW5hbWljCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCkxhdW5jaGVyOiBTdGFydGluZyBwYXJhbGxlbCB0YXNrcy4uLgp1c2luZyAvdG1wL2xhdW5jaGVyLjkyOTA1MzUuaG9zdGxpc3QuR1l6ZE9Xc0IgdG8gZ2V0IGhvc3RzCnN0YXJ0aW5nIGpvYiBvbiBnbDMwNzkKV2FybmluZzogUGVybWFuZW50bHkgYWRkZWQgdGhlIEVEMjU1MTkgaG9zdCBrZXkgZm9yIElQIGFkZHJlc3MgJzEwLjE2NC44LjEyOScgCiB0byB0aGUgbGlzdCBvZiBrbm93biBob3N0cy4KTGF1bmNoZXI6IFRhc2sgMCBydW5uaW5nIGpvYiAxIG9uIGdsMzA3OS5hcmMtdHMudW1pY2guZWR1ICguL3BhbmdyYW0uc2ggTmRlZmdsdSA+IHJlc3VsdHMuTmRlZmdsdS50eHQpCkxhdW5jaGVyOiBKb2IgMSBjb21wbGV0ZWQgaW4gMSBzZWNvbmRzLgpMYXVuY2hlcjogVGFzayAwIHJ1bm5pbmcgam9iIDIgb24gZ2wzMDc5LmFyYy10cy51bWljaC5lZHUgKC4vcGFuZ3JhbS5zaCBIYWNpbG5vID4gcmVzdWx0cy5IYWNpbG5vLnR4dCkKTGF1bmNoZXI6IFRhc2sgMiBydW5uaW5nIGpvYiAzIG9uIGdsMzA3OS5hcmMtdHMudW1pY2guZWR1ICguL3BhbmdyYW0uc2ggVGRnaG5vdSA+IHJlc3VsdHMuVGRnaG5vdS50eHQpCkxhdW5jaGVyOiBKb2IgMyBjb21wbGV0ZWQgaW4gMSBzZWNvbmRzLgouLi4KTGF1bmNoZXI6IFRhc2sgMCBkb25lLiBFeGl0aW5nLgpMYXVuY2hlcjogRG9uZS4gSm9iIGV4aXRlZCB3aXRob3V0IGVycm9ycwo8L3ByZT48L3RkPjwvdHI+PC90YWJsZT4KPGJyLz4KClRoZSBsYXVuY2hlciBzb2x1dGlvbiBpcyBjb3JyZWN0LCBjbGVhciwgYW5kIGVmZmljaWVudC4gSXQgaXMgYSB2ZXJ5IG5pY2Ugb3B0aW9uCmlmIHlvdSBoYXZlICptYW55KiBpbmRlcGVuZGVudCB0YXNrcyB0aGF0IGVhY2ggcnVuIHF1aWNrbHkgKDw9NjAgc2Vjb25kcykgYW5kIAplYWNoIHRhc2tzIGhhcyBhIG1vZGVzdCBjb21wdXRlIHJlcXVlc3QgKGUuZy4gZWFjaCB0YXNrIG5lZWRzIGEgc2luZ2xlIENQVSkuCgo8YnIvPgoKLS0tCgojIyBHZW9tZXRyaWVzIGFuZCBkZXBlbmRlbmNpZXMKCjx0YWJsZSBjbGFzcz0nZmlnJyB3aWR0aD0nMTAwJSc+PHRyPjx0aCBjbGFzcz0nZmlnJz5Kb2IvdGFzayBnZW9tZXRyaWVzIGNvbXBhcmVkPC90aD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxpbWcgc3JjPSJpbWFnZXMvTW9kdWxlMDZfZ2VvbWV0cnlfc2VyaWFsX2xvb3Bfc21hbGwucG5nIi8+PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxpbWcgc3JjPSJpbWFnZXMvTW9kdWxlMDZfZ2VvbWV0cnlfcGFyYWxsZWwucG5nIiBoZWlnaHQ9IjQwJSIgd2lkdGg9IjQwJSIvPjwvdGQ+PC90cj4KPHRyPjx0ZCBjbGFzcz0nZmlnJz48aW1nIHNyYz0iaW1hZ2VzL01vZHVsZTA2X2dlb21ldHJ5X2xhdW5jaGVyLnBuZyIgaGVpZ2h0PSI0MCUiIHdpZHRoPSI0MCUiLz48L3RkPjwvdHI+CjwvdGFibGU+CgpUaGUgdGhyZWUgam9iIGdlb21ldHJpZXMgZGlhZ3JhbW1lZCBhYm92ZSBoaW50IHRoYXQgd2UgcXVpZXRseSBtYWRlIGEKYSBmZXcgc2ltcGxpZnlpbmcgYXNzdW1wdGlvbnMgYWxvbmcgdGhlIHdheToKCi0gV2UgYXNzdW1lZCB0aGF0IGFsbCB0aGUgdGFza3Mgd2VyZSBpbmRlcGVuZGVudCAoYW5kIHRodXMgcGxlYXNpbmdseSBwYXJhbGxlbCkuCi0gV2UgYXNzdW1lZCB0aGF0IGFsbCB0aGUgdGFza3MgaW4gYSB3b3JrZmxvdyB3ZXJlIHRoZSBzYW1lIHRyYW5zZm9ybWF0aW9uIAphcHBsaWVkIG1hbnkgZGlmZmVyZW50IGlucHV0cy4KCkNvbW1vbmx5IHdvcmtmbG93cyBjb250YWluIHNldmVyYWwgZGlmZmVyZW50IHN0ZXBzIHdoaWNoIHdoZXJlIHRoZSBpbnB1dCBvZiBvbmUgCnN0ZXAgb2Z0ZW4gZGVwZW5kcyBvbiB0aGUgb3V0cHV0IG9mIHRoZSBwcmV2aW91cy4KPGRpdiBzdHlsZT0idGV4dC1hbGlnbjogY2VudGVyOyI+CjxpbWcgc3JjPSJpbWFnZXMvTW9kdWxlMDZfZ2VvbWV0cnlfb2ZfbXVsdGlzdGVwX2pvYi5wbmciIGhlaWdodD0iMjAlIiB3aWR0aD0iMjAlIi8+CjwvZGl2PgoKQWxzbyBzdGVwcyBpbiBhIHdvcmtmbG93IG9mdGVuIGhhdmUgdmFyaWFibGUgcmVzcm91Y2UgbmVlZHMgYW5kIHJ1biB0aW1lczoKCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPgo8aW1nIHNyYz0iaW1hZ2VzL01vZHVsZTA2X2hldGVyb2dlbmVpdHlfb2ZfbXVsdGlzdGVwX2pvYi5wbmciIGhlaWdodD0iMzAlIiB3aWR0aD0iMzAlIi8+CjwvZGl2PgoKTW9yZW92ZXIsIHdvcmtmbG93cyBhcmUgbm90IGFsd2F5cyBsaW5lYXI7IHRoZSBsb2dpY2FsIGZsb3cgb2Ygc3RlcHMgbWF5IGpvaW4gdGhlIG91dHB1dHMgb2YgdHdvIHN0ZXBzIGFzIGFuIGlucHV0IHRvIGEgdGhpcmQ6CjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPgo8aW1nIHNyYz0iaW1hZ2VzL01vZHVsZTA2X25vbmxpbmVhcl93b3JrZmxvdy5wbmciIGhlaWdodD0iNTAlIiB3aWR0aD0iNTAlIi8+CjwvZGl2PgoKCgpUaGUgdGVjaG5pcXVlcyB3ZSByZXZpZXdlZCBhYm92ZSBhcmUgZXhlY2xsZW50IGZvciBzbWFsbGVyLCBzaW1wbGVyIHdvcmtmbG93cywgYQptb3JlIGNvbXBsZXgsIG1vcmUgcmVzb3VyY2UgaW50ZW5zaXZlIHdvcmtmbG93IHdpbGwgcmVxdWlyZSBlaXRoZXIgYSBtdWNoIG1vcmUKbnVhbmNlZCBzZXQgb2Ygc2NyaXB0cyBvciBhIG1vcmUgc29waGlzdGljYXRlZCBhcHByb2FjaCBhbHRvZ2V0aGVyLiBTTFVSTQpzdXBwb3J0cyB0aGVzZSBtb3JlIGNvbXBsZXggc2NlbmFyaW9zIG5hdGl2ZWx5IHdpdGggc29tZXRoaW5nIGNhbGxlZCBqb2IgYXJyYXlzCihzZWUgam9iIGFycmF5cyBpbiBbbGlua3NdKCNsaW5rcy1hbmQtcmVmZXJlbmNlcykgYmVsb3cpLiBJbiB0aGUgbmV4dCBtb2R1bGUsIHdlCmludHJvZHVjZSB0aGUKKipTbmFrZW1ha2UqKiB3b3JrZmxvdyBhdXRvbWF0aW9uIGZyYW1ld29yayB0byBhZGRyZXNzIHRoZXNlIG1vcmUgY29tcGxpY2F0ZWQKc2NlbmFyaW9zLgoKPGJyLz4KCi0tLQoKIyMgUHJvIHRpcHMKCkF1dG9tYXRpbmcgd29ya2Zsb3dzIGlzIGEgbGVhcm5pbmcgcHJvY2Vzcy4gSGVyZSdzIGEgZmV3IGlkZWFzIHRvIGNvbnNpZGVyIGFsb25nCnRoZSB3YXk6CgoxLiAqKkRvIG5vdCB0cnkgdG8gYXV0b21hdGUgc29tZXRoaW5nIHRoYXQgeW91IGNhbm5vdCBkbyBieSBoYW5kLioqCjIuICoqTWFrZSBpdCByaWdodC4gTWFrZSBpdCBjbGVhci4gTWFrZSBpdCBlZmZpY2llbnQuIChJbiB0aGF0IG9yZGVyLikqKgozLiAqKkJ1aWxkIGEgUkVBRE1FIGZvciBlYWNoIHdvcmtmbG93LioqIAogICBDb25zaWRlciBpbmNsdWRpbmc6CiAgIC0gWW91ciBuYW1lL2VtYWlsCiAgIC0gVGhlIGRhdGUKICAgLSBIb3cgdG8gaW5zdGFsbCB0aGUgd29ya2Zsb3cKICAgLSBIb3cgdG8gcnVuIHRoZSB3b3JrZmxvdwogICAtIEFueSBuZWNlc3NhcnkgY29udGV4dC9jb25zdHJhaW50cyB0aGF0IHdvdWxkIGhlbHAgeW91ciBmdXR1cmUgY29sbGFib3JhdG9yCiAgICAgcmVwcm9kdWNlIHlvdXIgcmVzdWx0cy4KCjQuICoqQXV0b21hdGUgdGhlIHdvcmtmbG93IHdpdGggdGhlIGRhdGEgeW91IGhhdmUuKiogCiAgIERvbid0IGdlbmVyYWxpemUgYSB3b3JrZmxvdyB0b28gc29vbi4gWW91IG1pZ2h0IHNlZSB0aGF0IGEgd29ya2Zsb3cgY291bGQgYmUgCiAgIHBhcmFtZXRlcml6ZWQvZXh0ZW5kZWQgdG8gYXBwbHkgdG8gbmV3IHR5cGVzIG9mIGRhdGEuIEZlZWwgdGhhdCBleGNpdGVtZW50LCAKICAgbm90ZSB0aGUgb3Bwb3J0dW5pdHkgaW4gdGhlIFJFQURNRSwgYW5kIHRydXN0IHRoYXQgeW91IHdpbGwgbWFrZSB0aGF0IGNoYW5nZQogICB3aGVuIHlvdSBuZWVkIHRvLgoKNS4gKipJbnN0ZWFkIG9mIGRldmVsb3BpbmcgdGhlIHdob2xlIHdvcmtmbG93IGVuZCB0byBlbmQsIGNvbnNpZGVyIGFuIGl0ZXJhdGl2ZSBhbmQgaW5jcmVtZW50YWwgYXBwcm9hY2guKiogPGJyLz4KCjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGNlbnRlcjsiPgo8aW1nIHNyYz0iaW1hZ2VzL01vZHVsZTA2X2l0ZXJhdGl2ZV9kZXZlbG9wbWVudC5wbmciIGhlaWdodD0iODAlIiB3aWR0aD0iODAlIi8+CjwvZGl2PgoKICAgQnJlYWsgd29ya2Zsb3cgZGV2ZWxvcG1lbnQgaW50byBzdGVwczoKICAgCiAgMSkgZG8gcGFydCBvZiB0aGUgd29ya2Zsb3cgZm9yIG9uZSBzYW1wbGUgYW5kIHZlcmlmeSBjb3JyZWN0bmVzcyBhcyB5b3UgYWRkCiAgc3RlcHMuIChGb3IgYSBsYXJnZSBkYXRhc2V0IGNvbnNpZGVyIHN1YnNldHRpbmcvZG93bnNhbXBsaW5nIHlvdXIgaW5wdXRzIHNvCiAgeW91IGNhbiBpdGVyYXRlIHF1aWNrZXIuKQogIDIpIHJ1biBhIHNpbmdsZSBzYW1wbGUgZW5kIHRvIGVuZAogIDMpIHNjYWxlIHRvIGEgZmV3IHNhbXBsZXMgYW5kIGNoZWNrIHRob3NlIG91dHB1dHM7IHR1bmUgcmVzb3VyY2UgYWxsb2NhdGlvbnMgCiAgNCkgcnVuIHRoZSB3aG9sZSBiYXRjaAoKLS0tCgojIyBFeGVyY2lzZTogUHJvamVjdCBSYWlsZmVuY2UKClRoaXMgcHJvamVjdCBpcyBmb2N1c2VkIGFyb3VuZCBhIHRyYW5zZm9ybWF0aW9uIHRoYXQgY2FuIGVuY29kZSBvciBkZWNvZGUgYSAKc3BlY2lmaWMga2luZCBvZiBlbmNyeXB0aW9uIGNhbGxlZCBhIFtyYWlsIGZlbmNlIGNpcGhlcl0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUmFpbF9mZW5jZV9jaXBoZXIpe3RhcmdldD0iX2JsYW5rIn0uIFRoZQpkZXRhaWxzIG9mIHRoaXMgZW5jcnlwdGlvbiBhcmUgaW50ZXJlc3RpbmcgYW5kIEkgZW5jb3VyYWdlIHlvdSB0byBjaGVjayBvdXQgdGhlCmxpbmssIGJ1dCBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgZXhlcmNpc2UgaXQncyBvayB0byB0cmVhdCBpdCBhcyAKbWFnaWMvYmxhY2stYm94IHRyYW5zZm9ybWF0aW9uLgoKUmV2aWV3IHRoZSBwcm9qZWN0IGRpcmVjdG9yeSBoZXJlOgpgYGBzaApjZCAvbmZzL3R1cmJvL3VtbXMtYmlvaW5mLXdrc2hwL3dvcmtzaG9wL2hvbWUvJFVTRVIKY2QgcHJvamVjdF9yYWlsZmVuY2UKbHMgLTEKYGBgCgo+IGBgYApjb2Rlcy50eHQKcmFpbGZlbmNlX2RlY29kZS5weQpyYWlsZmVuY2VfZW5jb2RlLnB5ClJFQURNRS5tZAogIGBgYAoKClRoZSBgcmFpbGZlbmNlX2RlY29kZS5weWAgc2NyaXB0IGFjY2VwdHMgdHdvIGFyZ3VtZW50cyBzZXBhcmF0ZWQgYnkgYSBjb21tYToKCiAgLSBhIG51bWJlciAoYSBwb3NpdGl2ZSBpbnRlZ2VyKQogIC0gYSBxdW90ZWQgc3RyaW5nICh0aGUgY2lwaGVyIHRleHQpCgpJdCByZXR1cm5zIHRoZSBkZWNvZGVkIGNsZWFyIHRleHQuIFlvdSBjYW4gcnVuIHRoZSByYWlsZmVuY2VfZGVjb2RlLnB5IHNjcmlwdApsaWtlIHNvOgoKYGBgc2gKIyBuZWVkIHRvIGxvYWQgcHl0aG9uIG9uY2UgaW4gdGhlIHNlc3Npb24KbW9kdWxlIGxvYWQgcHl0aG9uCgouL3JhaWxmZW5jZV9kZWNvZGUucHkgMywid3JpdmRldGNlYWVkc29lZS1sZWEgbmUgIGNyZiBvISIKYGBgCgo+IGBgYAp3ZSBhcmUgZGlzY292ZXJlZC1mbGVlIGF0IG9uY2UhCiAgYGBgCgpUaGVyZSBpcyBhIGxpc3Qgb2YgZW5jcnlwdGVkIGNvZGVzIGluIHRoZSBgY29kZXMudHh0YCBmaWxlOgo8dGFibGUgY2xhc3M9J2ZpZyc+PHRyPjx0aCBjbGFzcz0nZmlnJz5jb2Rlcy50eHQ8L3RoPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+PHByZT4KMiwib250dHl0IHVvYWVzbXRpZ3RhIG8gYW50ZCB5aG4iZCBvIHIgb2F0bXQgb2VobiBodHl1Y25vIG9iIGFkCjMsImUgaE0gY3JhaWZpLm5hci5tayB0cmd0IGFlaSBsYS5NayB0ZWZjZXQgSSBodG9kcilhaWkua3RlIGUgaW4odCBlIgo0LCJhZWZpZSBhYW10aCBrbHd0aCBheWh2dW9ldHdybyBodGR0byBldCBvdyBhdSIKNSwicnJubXBjZSBlYWFkZWVhcCJvZGF0dCAgcm4gcmhuaW5paWVpY3Rsb2NzIHZuYWEKMywidCBEd2llYVJBTSJyIEVFCjMsImxzaWUibSEKPC9wcmU+PC90ZD48L3RyPjwvdGFibGU+Cjxici8+CgoqKllvdXIgdGFzazoqKgoKLSBDb25zaWRlciB0aGUgdGhyZWUgYXV0b21hdGlvbiBhcHByb2FjaGVzIG91dGxpbmVkIGluIHRoZSBsZXNzb25zIGFib3ZlOiBzZXJpYWwgCmxvb3AsIHBhcmFsbGVsIGJhdGNoLCBhbmQgbGF1bmNoZXIuCi0gQ2hvb3NlIG9uZSBhcHByb2FjaCwgYW5kLCB1c2luZyB0aGUgcGF0dGVybnMgYWJvdmUgYXMgYSB0ZW1wbGF0ZSwgY3JlYXRlCnNjcmlwdChzKSB0aGF0IHdpbGwgZGVjcnlwdCB0aGUgY29kZXMgaW4gYGNvZGUudHh0YC4KLSBJZiB5b3UgY29tcGxldGUgdGhlIGV4ZXJjaXNlIHdpdGggYSBvbmUgYXBwcm9hY2gsIHJlcGVhdCB0aGUKZXhlcmNpc2Ugd2l0aCBhIGRpZmZlcmVudCBhcHByb2FjaC4KCgotLS0KCiMjIEtleSBpZGVhcwoKKiBBY2hpZXZpbmcgcmVwcm9kdWNpYmxlIHJlc2VhcmNoIHJlcXVpcmVzIGEgYmxlbmQgb2YgZG9jdW1lbnRhdGlvbiBhbmQgYXV0b21hdGlvbi4KKiBCZSBraW5kIHRvIHlvdXIgZnV0dXJlIHNlbGY7IHRoZXkgd2lsbCB0aGFuayB5b3UgZm9yIHRoZSBSRUFETUUgeW91IGxlZnQgdGhlbS4KKiBBdXRvbWF0aW9uIGhlbHBzIHJlcHJvZHVjaWJpbGl0eToKICAqIEF1dG9tYXRpb24gc2hyaW5rcyB5b3VyIFJFQURNRS4KICAqIEF1dG9tYXRpb24gc2ltcGxpZmllcyB2YWxpZGF0aW9uIG9mIHlvdXIgd29ya2Zsb3cuCiAgKiBBdXRvbWF0aW9uIGVuYWJsZXMgcmVwZXRpdGlvbi4KICAqIEF1dG9tYXRpb24gc3RyZWFtbGluZXMgc2hhcmluZy4KICAqIEF1dG9tYXRpb24gc2NhbGVzIHRvIGxhcmdlciBpbnB1dHMKKiBKb2IvdGFzayBnZW9tZXRyaWVzIGhlbHAgdmlzdWFsaXplIGhvdyBkaWZmZXJlbnQgYXBwcm9hY2hlcyBhcmUgZXhlY3V0ZWQuIChJdCAKYWxzbyBoaW50cyBhdCB0aGUgbi1kaW1lbnNpb25hbCBnYW1lIG9mIFRldHJpcyB0aGUgam9iLXNjaGVkdWxlciBpcyBwbGF5aW5nIHRvCnBhY2sgZXZlcnlvbmVzIGpvYnMgYXMgbmVhdGx5IGFzIHBvc3NpYmxlLgoqIFRoZSAqKlNMVVJNIGxhdW5jaGVyKiogYWxsb3dzIHlvdSB0byBnYXRoZXIgbWFueSBwYXJhbGxlbCB0YXNrcyBpbnRvIGEgbWFpbiBqb2IsCiAgaW4gZWZmZWN0IGNyZWF0aW5nIGEgdHJhbnNpZW50IHN1Yi1jbHVzdGVyIHdpdGhpbiB0aGUgbWFpbiBIUEMuCiogRm9yIGEgbW9yZSBjb21wbGV4IHRyYW5zZm9ybWF0aW9uLCBhc2NyaXB0IGNhbiBiZSBlaXRoZXIgc2ltcGxlIG9yCiAgcmVzb3VyY2UgZWZmaWNpZW50IC0gY2hvb3NlIG9uZS4gQ29uc2lkZXIgbW9yZSByb2J1c3Qgc29sdXRpb25zIChlLmcuCiAgU25ha2VtYWtlKSBhcyBuZWNlc3NhcnkuCgotLS0KCiMjIExpbmtzIGFuZCByZWZlcmVuY2VzCgotIFVNIElUUyBkb2NzIG9uIFtsYXVuY2hlcl0oaHR0cHM6Ly9hcmMudW1pY2guZWR1L2dyZWF0bGFrZXMvc29mdHdhcmUvbGF1bmNoZXIvKXt0YXJnZXQ9Il9ibGFuayJ9Ci0gVU0gQVJDIGRvY3Mgb24gW2pvYiBhcnJheXNdKGh0dHBzOi8vZG9jcy5zdXBwb3J0LmFyYy51bWljaC5lZHUvc2x1cm0vYXJyYXkvKXt0YXJnZXQ9Il9ibGFuayJ9Ci0gU0xVUk0gZG9jcyBvbiBbam9iIGFycmF5c10oaHR0cHM6Ly9zbHVybS5zY2hlZG1kLmNvbS9qb2JfYXJyYXkuaHRtbCl7dGFyZ2V0PSJfYmxhbmsifQotIFVNIEFSQyBbbWlWaWRlb10oaHR0cHM6Ly93d3cubWl2aWRlby5pdC51bWljaC5lZHUvbWVkaWEvdC8xX3o0ZGY4NHRpLzE4MTg2MDU2MSl7dGFyZ2V0PSJfYmxhbmsifSBvbiBhZHZhbmNlZCBTTFVSTSB0ZWNobmlxdWVzIChpbmNsdWRpbmcgbGF1bmNoZXIsIGpvYiBhcnJheXMsIGFuZCBtb3JlKQotIEZvciBtb3JlIGV4YW1wbGVzIG9mIHBhbmdyYW1zIGluIGFjdGlvbiwgY2hlY2tvdXQ6CgogIC0gW1RoZSBOZXcgWW9yayBUaW1lcyBTcGVsbGluZyBCZWUgUHV6emxlXShodHRwczovL3d3dy5ueXRpbWVzLmNvbS9wdXp6bGVzL3NwZWxsaW5nLWJlZSl7dGFyZ2V0PSJfYmxhbmsifQogIC0gW1NwZWxsaW5nIEJlZSBwdXp6bGUgc29sdmVyXShodHRwczovL3d3dy5zYnNvbHZlci5jb20vYXJjaGl2ZSl7dGFyZ2V0PSJfYmxhbmsifQoKPGJyLz4KPGJyLz4KCi0tLQoKfCBbUHJldmlvdXMgbGVzc29uXShNb2R1bGUwNV9jb250YWluZXJzX2RvY2tlcl9zaW5ndWxhcml0eS5odG1sKSB8IFtUb3Agb2YgdGhpcyBsZXNzb25dKCN0b3ApIHwgW05leHQgbGVzc29uXShNb2R1bGUwNy1pbnRyby10by1zbmFrZW1ha2UuaHRtbCkgfAp8IDotLS0gfCA6LS0tLTogfCAtLS06IHwK