Build a Snakefile
A Snakefile is a text file that decribes your workflow. It is
interpreted by the snakemake
command. Building a good
Snakefile is the key step in understanding Snakemake, so we’ll begin
there.
Consider a sample bash script
To build a Snakefile it’s helpful to use a concrete example and also
helpful to adapt an existing script. The script below considers the 1868
novel “Little Women”, by Louisa May Alcott. It outputs a file containing
the ranked name counts for each of the four March sisters: Amy, Beth,
Jo, and Laurie. (It also emits a few intermediate files).
alcott_script/alcott_script.sh
|
|
#!/bin/bash
# Which of the March sisters is referred to most often in
# part 1 of the Little Women?
# Split lines into words
cat inputs/little_women_part_1.txt \
| tr -cs '[:alpha:]' '\n' \
> 1.split_words.txt
# Count words
sort 1.split_words.txt | uniq -c \
> 2.count_words.txt
# Sort words by descending count and add header
sort -k1,1nr 2.count_words.txt \
| awk 'BEGIN {print "word\tcount"} { print $2 "\t" $1}' \
> 3.sort_counts.txt
# Select names with respective counts
egrep '^(Jo|Amy|Laurie|Beth)\s' 3.sort_counts.txt > 4.select_words.txt
|
It’s not necessary to understand the command in detail, but it is
helpful to consider the steps with their various outputs:
Inputs, steps, and outputs
|
inputs/little_women_part_1.txt
|
CHAPTER ONE
PLAYING PILGRIMS
"Christmas won't be Christmas without any presents," grumbled Jo, lying
on the rug.
"It's so dreadful to be poor!" sighed Meg, looking down at her old
dress.
...
|
1.split_words.txt
|
CHAPTER
ONE
PLAYING
PILGRIMS
Christmas
won
t
be
Christmas
without
|
2.count_words.txt
|
1992 a
75 A
1 aback
1 abase
3 abashed
1 abed
1 abject
3 able
3 abominable
1 abominably
|
3.sort_counts.txt
|
word count
and 3811
the 3303
to 2303
a 1992
I 1988
her 1660
of 1490
in 1094
you 1010
|
4.select_words.txt
|
Jo 737
Laurie 319
Beth 294
Amy 283
|
To view the results interactively, you can execute the script.
export WORKSHOP_HOME="/nfs/turbo/umms-bioinf-wkshp/workshop/home/${USER}"
cd $WORKSHOP_HOME/project_alcott/alcott_script
./alcott_script.sh
And view excepts of the results in less
. (Hit
q
key to exit less
.)
head inputs/little_women_part_1.txt *.txt | less
Snakemake and the Snakefile
Switch to the alcott_snakemake dir; note there are inputs and a
Snakefile
cd $WORKSHOP_HOME/project_alcott/alcott_snakemake
tree
.
├── inputs
│ ├── little_women_part_1.txt
│ └── little_women_part_2.txt
└── workflow
└── Snakefile
A Snakefile is a simple markdown file.By convention, the snakefile is
named Snakefile
and lives in the workflow
sub-directory. The current Snakefile is simply a stub based on the
earlier bash script:
workflow/Snakefile
|
# split_lines ###########################################################
#cat inputs/little_women_part_1.txt \
# | tr -cs '[:alpha:]' '\n' \
# > 1.split_words.txt
# count_words ###########################################################
#sort 1.split_words.txt | uniq -c \
# > 2.count_words.txt
## sort_count ###########################################################
#sort -k1,1nr 2.count_words.txt \
# | awk 'BEGIN {print "word\tcount"} { print $2 "\t" $1}' \
# > 3.sort_counts.txt
# select_words ##########################################################
#egrep '^(Jo|Amy|Laurie|Beth)\s' 3.sort_counts.txt > 4.select_words.txt
|
A snakefile is composed of rules; each rule has a name and specifies
directives such as
- input (what the rule requires)
- output (what the rule promises to generate)
- shell (how it will make input into output)
We add the first step in the workflow as a new rule. (Adding below
the existing comment block.) Note that we will keep the input files and
result files in separate directories.
rule split_words:
input: "inputs/little_women_part_1.txt"
output: "results/little_women_part_1.split_words.txt"
shell: "cat inputs/little_women_part_1.txt | tr -cs '[:alpha:]' '\n' > results/little_women_part_1.split_words.txt"
Running Snakemake
- The snakemake python program interprets the
Snakefile and executes rules as appropriate to produce the expected
output.
- The snakemake program is installed as a module on Great Lakes.
- When launching snakemake, You specify the number of cores
(i.e. CPUs) with the -c flag. We will use 1 core.
module load snakemake
module list
# confirm you are in the right working directory
tree
Currently Loaded Modules:
1) python3.10-anaconda/2023.03 2) snakemake/7.32.4
$ tree
.
├── inputs
│ ├── little_women_part_1.txt
│ └── little_women_part_2.txt
└── workflow
└── Snakefile
Run snakemake
command as below. Note:
snakemake
will look for a Snakefile in the workflow
directory.
snakemake
emits a log to the screen that include
- run details
- Job stats
- Rule details
- Completion status i.e. (100%) done
snakemake -c1
Building DAG of jobs...
Using shell: /usr/bin/bash
Provided cores: 1 (use --cores to define parallelism)
Rules claiming more threads will be scaled down.
Job stats:
job count
----------- -------
split_words 1
total 1
.
Select jobs to execute...
.
[Mon Jun 10 18:35:41 2024]
rule split_words:
input: inputs/little_women_part_1.txt
output: results/little_women_part_1.split_words.txt
jobid: 0
reason: Missing output files: results/little_women_part_1.split_words.txt
resources: tmpdir=/tmp
.
[Mon Jun 10 18:35:41 2024]
Finished job 0.
1 of 1 steps (100%) done
Complete log: .snakemake/log/2024-06-10T183541.204636.snakemake.log
Check the file system. Note new results/ dir and new file. Note that
snakemake
created the results/ dir automatically.
tree
.
├── inputs
│ ├── little_women_part_1.txt
│ └── little_women_part_2.txt
├── results
│ └── little_women_part_1.split_words.txt
└── workflow
└── Snakefile
Also note that snakemake
creates a hidden directory
.snakemake/
in the working directory. This holds various
info that help snakemake manage the workflow. It’s extremely rare for
you to need to examine the contents of the .snakemake/
directory, but FYI.
If you re-run snakemake
it determines the filesystem is
up to date and no rules need to be triggered.
snakemake -c1
Building DAG of jobs...
Nothing to be done (all requested files are present and up to date).
Complete log: .snakemake/log/2024-06-10T184850.944535.snakemake.log
Add a new rule
In the Snakemake file, add the second step as a new rule below the
first rule. Note that the inputs of this rule match the outputs of the
prior rule.
rule count_words:
input: "results/little_women_part_1.split_words.txt"
output: "results/little_women_part_1.count_words.txt"
shell: "sort results/little_women_part_1.split_words.txt | uniq -c > results/little_women_part_1.count_words.txt"
By default, snakemake
only runs the first rule in the
Snakefile. To trigger the new rule, you can specify the desired end
target on the command line.
snakemake -c1 count_words
Note the log output similar to above, but now shows the new rule.
Building DAG of jobs...
Using shell: /usr/bin/bash
Provided cores: 1 (use --cores to define parallelism)
Rules claiming more threads will be scaled down.
Job stats:
job count
----------- -------
count_words 1
total 1
.
Select jobs to execute...
.
[Mon Jun 10 18:52:07 2024]
rule count_words:
input: results/little_women_part_1.split_words.txt
output: results/little_women_part_1.count_words.txt
jobid: 0
reason: Missing output files: results/little_women_part_1.count_words.txt
resources: tmpdir=/tmp
.
[Mon Jun 10 18:52:08 2024]
Finished job 0.
1 of 1 steps (100%) done
Complete log: .snakemake/log/2024-06-10T185207.598695.snakemake.log
See the new file and review the contents:
head results/*
==> results/little_women_part1.count_words.txt <==
1992 a
75 A
1 aback
1 abase
3 abashed
1 abed
1 abject
3 able
3 abominable
1 abominably
.
==> results/little_women_part_1.split_words.txt <==
CHAPTER
ONE
PLAYING
PILGRIMS
Christmas
won
t
be
Christmas
without
Variables / Rule ordering
Each rule defines input and output
variables; you can use those values in the shell
directive:
rule split_words:
input: "inputs/little_women_part_1.txt"
output: "results/little_women_part_1.split_words.txt"
shell: "cat {input} | tr -cs '[:alpha:]' '\n' > {output}"
rule count_words:
input: "results/little_women_part_1.split_words.txt"
output: "results/little_women_part1.count_words.txt"
shell: "sort {input} | uniq -c > {output}"
Snakemake doesn’t care what order the rules appear in the Snakefile
but it assumes the first rule is the default target. Put the last step
of the workflow at the top of the file and snakemake
will
default to running the whole workflow.
rule count_words:
input: "results/little_women_part_1.split_words.txt"
output: "results/little_women_part1.count_words.txt"
shell: "sort {input} | uniq -c > {output}"
rule split_words:
input: "inputs/little_women_part_1.txt"
output: "results/little_women_part_1.split_words.txt"
shell: "cat {input} | tr -cs '[:alpha:]' '\n' > {output}"
Rerun after making these changes, and check out the log (excepted
below).
- Note that in Job stats all rules are
triggered.
- Note the value of reason in each of the rules. Does the reason make
sense?
snakemake -c1
...
Job stats:
job count
----------- -------
count_words 1
split_words 1
total 2
...
rule split_words:
input: inputs/little_women_part_1.txt
output: results/little_women_part_1.split_words.txt
jobid: 1
reason: Code has changed since last execution
resources: tmpdir=/tmp
...
rule count_words:
input: results/little_women_part_1.split_words.txt
output: results/little_women_part1.count_words.txt
jobid: 0
reason: Input files updated by another job: results/little_women_part_1.split_words.txt
resources: tmpdir=/tmp
Using triple quotes / Escaping curly braces
Add the third step of the transform
- Place it above the existing rules.
- Use {input} and {output} variables.
- We use triple quotes to make multiline shell commands.
- In the shell, any braces ({}) related to the command must be doubled
to distinguish them from Snakefile variables.
rule sort_counts:
input: "results/little_women_part_1.count_words.txt"
output: "results/little_women_part_1.sort_counts.txt"
shell: """
sort -k1,1nr {input} \
| awk 'BEGIN {{print "word\tcount"}} {{ print $2 "\t" $1}}' \
> {output}
"""
Note that when you run snakemake
it only generates the
files it needs to.
snakemake -c1
...
Job stats:
job count
----------- -------
sort_counts 1
total 1
...
rule sort_counts:
input: results/little_women_part_1.count_words.txt
output: results/little_women_part_1.sort_counts.txt
jobid: 0
reason: Missing output files: results/little_women_part_1.sort_counts.txt
resources: tmpdir=/tmp
...
1 of 1 steps (100%) done
Use triple quotes to create multiline shell commands. If your
commands use curly braces ({}) you need to double them so snakemake
doesn’t get confused.
Snakemake dry-run / Interpreting rule errors
Add the fourth rule, placing it above the prior.
rule select_words:
input: "results/little_women_part_1.sort_counts.txt"
output: "results/little_women_part_1.select_words.txt"
shell: "egrep '^(Jo|Amy|Laurie|Beth)\s' {input} > {output}"
You probably have noticed that snakemake
will only run
the rules it needs to, based on changes in files or scripts or missing
files. You can preview what snakemake
will do by using the
dry-run flag --dry-run
or -n
. This will show
the typical snakemake output but will not change any files.
snakemake -c1 --dry-run
Building DAG of jobs...
Job stats:
job count
------------ -------
select_words 1
total 1
.
[Mon Jun 10 19:41:43 2024]
rule select_words:
input: results/little_women_part_1.sort_counts.txt
output: results/little_women_part_1.select_words.txt
jobid: 0
reason: Missing output files: results/little_women_part_1.select_words.txt
resources: tmpdir=/tmp
.
Job stats:
job count
------------ -------
select_words 1
total 1
.
Reasons:
(check individual jobs above for details)
missing output files:
select_words
.
This was a dry-run (flag -n). The order of jobs does not reflect the order of execution.
Developing a Snakefile typically involves some troubleshooting along
the way. I’ve intentionally introduced a typo in the Snakefile and then
did a dry-run to show an Snakefile error message:
$ snakemake -c1 --dry-run
.
A Building DAG of jobs...
B MissingInputException in rule select_words in file .../Snakefile, line 15:
C Missing input files for rule select_words:
D output: results/little_women_part_1.select_words.txt
E affected files:
F result/little_women_part_1.sort_counts.txt
Note:
- B line number (line 15)
- C Missing input files for rule select_words
- E & F result/little_women_part_1.sort_counts.txt
Can you see the error? You will likely need to consider the rule in
context:
rule select_words:
input: "result/little_women_part_1.sort_counts.txt"
output: "results/little_women_part_1.select_words.txt"
shell: "egrep '^(Jo|Amy|Laurie|Beth)\s' {input} > {output}"
rule sort_counts:
input: "results/little_women_part_1.count_words.txt"
output: "results/little_women_part_1.sort_counts.txt"
shell: """
sort -k1,1nr {input} \
| awk 'BEGIN {{print "word\tcount"}} {{ print $2 "\t" $1}}' \
> {output}
"""
rule count_words:
input: "results/little_women_part_1.split_words.txt"
output: "results/little_women_part_1.count_words.txt"
shell: "sort {input} | uniq -c > {output}"
rule split_words:
input: "inputs/little_women_part_1.txt"
output: "results/little_women_part_1.split_words.txt"
shell: "cat {input} | tr -cs '[:alpha:]' '\n' > {output}"
Fix the typo and confirm the fix using --dry-run
to get
a clean output. Then execute snakemake
normally until you
are 100% done.
Visualizing the DAG / rulegraph
In addition to doing a dry run, snakemake
can generate a
diagram of the rules to execute. Snakemake thinks of the rules as a Directed Acyclic Graph (DAG). You can see this with
the --dag
flag and the dot
program (part of Graphviz, an open
source graph visualizer).
snakemake --dag | dot -Tpng > dag.png
As you can see above, by default dot
displays the graphs
vertically with inputs at the top and outputs at the bottom. Our
Snakefile is arranged from output to input, so you can invert the
diagram like so:
snakemake --dag | dot -Grankdir="BT" -Tpng > dag_inverted.png
All target / Wildcards / Visualizaing the rulegraph
To emphasize the endpoint of the workflow and also clarify the
default target, it’s common to place a “rule all” at the top of the
file. (This also allows you to reorder rules within the file
arbitrarily.) It is unusual in that it only has an input directive.
rule all:
input: "results/little_women_part_1.select_words.txt"
Snakemake wildcards make it easy to to extend the workflow
to new inputs. (And they typically reduce repetition of file names.)
Here is an excerpt of the Snakefile showing the original
select_words rule and the same rule with wildcards.
rule all:
input: "results/little_women_part_1.select_words.txt"
# original rule
# rule select_words:
# input: "result/little_women_part_1.sort_counts.txt"
# output: "results/little_women_part_1.select_words.txt"
# shell: "egrep '^(Jo|Amy|Laurie|Beth)\s' {input} > {output}"
# now with wildcards
rule select_words:
input: "result/{base_name}.sort_counts.txt"
output: "results/{base_name}.select_words.txt"
shell: "egrep '^(Jo|Amy|Laurie|Beth)\s' {input} > {output}"
How does this work?
- The all rule needs an input named
“results/little_women_part_1.select_words.txt”
- The select_words rule can produce
“results/{base_name}.select_words.txt” which would match the
all input if the wildcard
{base_name}=“little_women_part_1”
- In select_word input, the rule substitutes the
{base_name} value for it’s wildcard, making
“result/little_women_part_1.sort_counts.txt”
- So the the value of the wildcard is determined by the file name in
the all rule and that value propagates from output to input.
What happens if we run snakemake?
snakemake -c1 --dry-run
Building DAG of jobs...
Nothing to be done (all requested files are present and up to date).
And that actually makes sense: even though we used wildcards, the
rules ended up with identical input and output values.
We can extend wildcards across remaining rules as below. Note that we
need to keep the literal value in the all rule so the
other rules can derive the value of the wildcard.
rule all:
input: "results/little_women_part_1.select_words.txt"
rule select_words:
input: "results/{base_name}.sort_counts.txt"
output: "results/{base_name}.select_words.txt"
shell: "egrep '^(Jo|Amy|Laurie|Beth)\s' {input} > {output}"
rule sort_counts:
input: "results/{base_name}.count_words.txt"
output: "results/{base_name}.sort_counts.txt"
shell: """
sort -k1,1nr {input} \
| awk 'BEGIN {{print "word\tcount"}} {{ print $2 "\t" $1}}' \
> {output}
"""
rule count_words:
input: "results/{base_name}.split_words.txt"
output: "results/{base_name}.count_words.txt"
shell: "sort {input} | uniq -c > {output}"
rule split_words:
input: "inputs/{base_name}.txt"
output: "results/{base_name}.split_words.txt"
shell: "cat {input} | tr -cs '[:alpha:]' '\n' > {output}"
Now that the Snakefile is using wildcards, it is trivial to extend it
to new inputs. We adjust the all rule adding a second
value, separated by a comma:
rule all:
input: "results/little_women_part_1.select_words.txt", "results/little_women_part_2.select_words.txt"
...
The addition of a new wildcard value (little_women_part_2) will
trigger all five rules.
snakemake --dry-run
...
Job stats:
job count
------------ -------
all 1
count_words 1
select_words 1
sort_counts 1
split_words 1
total 5
...
Before we run this for real, let’s add a new flag
--forceall
to tell snakemake to trigger every single rule
for all possible inputs. Basically act like there are no existing
outputs and it needs to start from scratch. But this is still a dry-run,
so no files will be changed.
snakemake --dry-run --forceall
...
Job stats:
job count
------------ -------
all 1
count_words 2
select_words 2
sort_counts 2
split_words 2
total 9
...
Try generating a DAG visualization. (Now it’s truly looking more like
a graph.) Note:
- Each wildcard value gets it’s own path
- The wildcard values are displayed at the input
- Some box outlines are solid and other are dotted; why is that?
snakemake --dag | dot -Grankdir="BT" -Tpng > dag_inverted_wildcards.png
With complex workflows, you sometimes don’t want to see all the
possible inputs and would rather just see the abstract relationship of
the rules. Snakemake supports this with a related flag
--rulegraph
snakemake --rulegraph | dot -Grankdir="BT" -Tpng > rulegraph.png
Ok. Go ahead and run snakemake
until you are 100%
complete and see these files in results:
$ ls results
little_women_part_1.count_words.txt little_women_part_2.count_words.txt
little_women_part_1.select_words.txt little_women_part_2.select_words.txt
little_women_part_1.sort_counts.txt little_women_part_2.sort_counts.txt
little_women_part_1.split_words.txt little_women_part_2.split_words.txt
The expand function / config files / parameters
Ideally, a workflow could be run on a new set of inputs without
changing the Snakefile at all. For example, we should be able to use our
unmodified Snakefile with the input of of Shakespeare’s Romeo and Juliet to see
whether Montague is mentioned more than Capulet. Most of the rules in
the workflow are already general enough; but there are two
exceptions:
- The all rule mentions specific texts.
- The select_words rule mentions specific names.
We can extract these specifics from the Snakefile using a
configuration file. We’ll start with the
select_words rule.
rule select_words:
input: "results/{base_name}.sort_counts.txt"
output: "results/{base_name}.select_words.txt"
shell: "egrep '^(Jo|Amy|Laurie|Beth)\s' {input} > {output}"
Firstly, we’ll pull the regular expression out of the shell directive
into a configuration file. Snakemake supports several
styles of configuration file; we’ll use the YAML syntax. By convention
we’ll create config/config.yaml; you could create and edit this file
manually; alternatively you can also execute the line below:
mkdir config
echo 'select_words_regex: ^(Jo|Amy|Laurie|Beth)\s' > config/config.yaml
cat config/config.yaml
Now add this line above the all rule:
configfile: "config/config.yaml"
Add a parameter initialized to the config key
select_words_regex. Then adjust the select_words rule to
refernce that parameter.
rule select_words:
input: "results/{base_name}.sort_counts.txt"
output: "results/{base_name}.select_words.txt"
params: regex=config[select_words_regex]
shell: "egrep '{params.regex}' {input} > {output}"
The params directive is a way of adjusting the shell
command with a value that isn’t tied to the filesystem. Also note that
params can be named (e.g. x="value"
) and referenced by name
in the shell ({params.x}
).
Now that we’ve made this change, let’s dry-run snakemake. Note we’re
adding the -p
flag so snakemake will print out the actual
commands it will execute:
snakemake --dry-run -p
In the output, note:
- In Job stats, we see snakemake believes we need to rerun
select_words for both inputs.
- In the rule details, the reason the rule is triggered is that code
and params have changed
- Because we used
-p
, you can see the actual egrep
statement (which looks fine).
Building DAG of jobs...
Job stats:
job count
------------ -------
all 1
select_words 2
total 3
.
[Mon Jun 10 23:01:21 2024]
rule select_words:
input: results/little_women_part_1.sort_counts.txt
output: results/little_women_part_1.select_words.txt
jobid: 1
reason: Code has changed since last execution; Params have changed since last execution
wildcards: base_name=little_women_part_1
resources: tmpdir=/tmp
.
egrep '^(Jo|Amy|Laurie|Beth)\s' results/little_women_part_1.sort_counts.txt > results/little_women_part_1.select_words.txt
.
[Mon Jun 10 23:01:21 2024]
rule select_words:
input: results/little_women_part_2.sort_counts.txt
output: results/little_women_part_2.select_words.txt
jobid: 5
reason: Code has changed since last execution; Params have changed since last execution
wildcards: base_name=little_women_part_2
resources: tmpdir=/tmp
.
egrep '^(Jo|Amy|Laurie|Beth)\s' results/little_women_part_2.sort_counts.txt > results/little_women_part_2.select_words.txt
...
Go ahead and run snakemake until 100% done.
Now we’ll pull the file base names from the all rule
into the config file:
select_words_regex: ^(Jo|Amy|Laurie|Beth)\s
input_base_filenames:
- little_women_part_1
- little_women_part_2
To get these values from config.yaml into the all
rule, I need to introduce the expand
function. expand is a helper function that comes with
Snakemake. It’s easier to explain if we show a few examples of inputs
and outputs:
expand("{dataset}/a.txt", dataset=[1, 2])
['1/a.txt', '2/a.txt', '3/a.txt']
.
expand("{a}.{b}", a=['fig1', 'fig2', 'fig3'], b=['png', 'pdf'])
['fig1.png', 'fig1.pdf', 'fig2.png', 'fig2.pdf', 'fig3.png', 'fig3.pdf']
.
expand("results/{x}.select_words.txt", \
... x=["little_women_part_1", "little_women_part_2"])
['results/little_women_part_1.select_words.txt', 'results/little_women_part_2.select_words.txt']
expand
accepts a string pattern with named placeholders
(e.g. {dataset}) and named lists (e.g. dataset=[1, 2]); it builds a list
of strings by inserting the list values into the pattern.
That last example above is exactly what we need in the
all rule. The final step is to adapt the example above
to use the config file, like so:
rule all:
input: expand("results/{x}.select_words.txt", \
x=config['input_base_filenames'])
The Snakefile is now free of any references to specific inputs. The
workflow can be run on a different set of inputs by using a new
config.yaml.
configfile: "config/config.yaml"
rule all:
input: expand("results/{x}.select_words.txt", \
x=config['input_base_filenames'])
rule select_words:
input: "results/{base_name}.sort_counts.txt"
output: "results/{base_name}.select_words.txt"
params: regex=config["select_words_regex"]
shell: "egrep '{params.regex}' {input} > {output}"
rule sort_counts:
input: "results/{base_name}.count_words.txt"
output: "results/{base_name}.sort_counts.txt"
shell: """
sort -k1,1nr {input} \
| awk 'BEGIN {{print "word\tcount"}} {{ print $2 "\t" $1}}' \
> {output}
"""
rule count_words:
input: "results/{base_name}.split_words.txt"
output: "results/{base_name}.count_words.txt"
shell: "sort {input} | uniq -c > {output}"
rule split_words:
input: "inputs/{base_name}.txt"
output: "results/{base_name}.split_words.txt"
shell: "cat {input} | tr -cs '[:alpha:]' '\n' > {output}"
Snakemake on Great Lakes
All this time we’ve been executing the workflow on the login-nodes.
It’s generally fine to execute the snakemake
process itself
on the login-node, but for a more demanding workflow you should execute
the rules on Great Lakes worker nodes.
It’s straightforward to do this by using a profile. A profile is
essentially just a little config file that tells snakemake
how it should submit rules as jobs. Check out snakemake docs for details
on profile options. We’ll start with a simple profile
built for Great Lakes:
cp -r /nfs/turbo/umms-bioinf-wkshp/workshop/shared-data/profile/ config/
config/profile/config.yaml
|
printshellcmds: True
keep-going: True
use-singularity: True
singularity-args: "--cleanenv -B $(pwd)"
cluster: sbatch --job-name=alcott_{rule}_{wildcards} --account=bioinf_wkshp_class --partition=standard --nodes=1 --cpus-per-task={resources.cpus} --mem={resources.mem_mb} --time={resources.time_min} --output=cluster_logs/%x-%j.log --parsable
default-resources: [cpus=1, mem_mb=2000, time_min=120]
rerun-triggers: [mtime]
latency-wait: 300
jobs: 5
|
Invoking snakemake
with this profile submits each rule a
an sbatch job:
# clear out existing files so there's something to do
rm results/*
snakemake --profile config/profile
Note that you can adjust the SLURM job geometry by adding a
resources directive to a rule. These values will be
incorporated into the sbatch request for this rule.
rule select_words:
input: "results/{base_name}.sort_counts.txt"
output: "results/{base_name}.select_words.txt"
resources: cpus=4, mem_mb=4000, time_min=5
params: regex=config["select_words_regex"]
shell: "egrep '{params.regex}' {input} > {output} #--fake-num-threads={resources.cpus}"
Using tmux
tmux is a unix
utility that let’s you create virtual screens inside of a single
session. (It’s like the Unix utility screen, but more powerful
and current.) For our purposes, the killer feature of tmux is captured
in this simple vignette:
- login to great lakes and start a tmux session on the login-node:
tmux
- start a long running task (e.g. a snakemake workflow which submits
to the cluster)
- detach from that tmux session: hit
ctrl-b
d
- turn off your computer, go home, and login to great lakes from
different computer
- reattach to that tmux session:
tmux attach
- you will see your long-running task has been running all along,
completely unaffected by your wanderings
And in fact, tmux can actually do much, much more. Here’s a nifty cheatsheet.
If you start using tmux, keep in mind that when you login into
greatlakes.arc-ts.umich.edu
it’s actually forwarding you on
to one of three login nodes: gl-login1, gl-login2, or
gl-login3. (Which one you get is basically random.) This means if you
want to reconnect to a detached tmux session, you have to get to the
right login node.
So, if you start a tmux session, note which login node you are using
using the hostname
command:
$ hostname
gl-login2.arc-ts.umich.edu
Then when you need to later reconnect to that session, instead of
ssh greatlakes.arc-ts.umich.edu
you connect to the specific
hostname you got above:
ssh gl-login2.arc-ts.umich.edu
# login + Duo
# login banner (blah blah blah)
[cgates@gl-login2 ~]$ tmux ls # list active tmux sessions
0: 1 windows (created Tue Jun 11 20:31:03 2024) [133x25]
[cgates@gl-login2 ~]$ tmux attach # attach to my session
Voila. You are back in your tmux session, right where you left
it.
Lastly, the scrollbar in the terminal/command window does not work in
tmux. If you need to scroll in tmux, you can hit
ctrl
-`b
and then [
to enter
scroll mode. You can then use cursor or page-up
,
page-down
to move around the tmux scroll-buffer. Hit
q
to quit scoll mode and return to the current prompt.
LS0tCnRpdGxlOiAiSW50cm8gdG8gU25ha2VtYWtlIgphdXRob3I6ICJVTSBCaW9pbmZvcm1hdGljcyBDb3JlIgpvdXRwdXQ6CiAgICAgICAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgICAgaW5jbHVkZXM6CiAgICAgICAgICAgICAgICBpbl9oZWFkZXI6IGhlYWRlci5odG1sCiAgICAgICAgICAgIHRoZW1lOiBwYXBlcgogICAgICAgICAgICB0b2M6IHRydWUKICAgICAgICAgICAgdG9jX2RlcHRoOiA0CiAgICAgICAgICAgIHRvY19mbG9hdDogdHJ1ZQogICAgICAgICAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICAgICAgICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICAgICAgICAgIG1hcmtkb3duOiBHRk0KICAgICAgICAgICAgY29kZV9kb3dubG9hZDogdHJ1ZQotLS0KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KYm9keXsgLyogTm9ybWFsICAqLwogICAgICBmb250LXNpemU6IDE0cHQ7CiAgfQpwcmUgewogIGZvbnQtc2l6ZTogMTJwdAp9Cgoud3JhcHBlciB7IG92ZXJmbG93LXg6IGF1dG87IH0KLndyYXBwZXIgdGFibGUgeyB3aGl0ZS1zcGFjZTogbm93cmFwOyB9Cgo8L3N0eWxlPgoKQnkgdGhlIGVuZCBvZiB0aGlzIG1vZHVsZSwgd2Ugd2lsbDoKCiogTGlzdCB0aGUgc29tZSBhZHZhbnRhZ2VzIG9mIGEgcm9idXN0IHdvcmtmbG93IGF1dG9tYXRpb24gc29sdXRpb24gbGlrZSBTbmFrZW1ha2UuCiogRGVzY3JpYmUgdGhlIGZ1bmRhbWVudGFsIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIFNuYWtlbWFrZSwgYSBTbmFrZWZpbGUsIGFuZCB0aGUgZmlsZSBzeXN0ZW0uCiogRGVzY3JpYmUga2V5IHBhcnRzIG9mIGEgU25ha2VtYWtlIHJ1bGUuCiogQnVpbGQgYSBTbmFrZWZpbGUgYW5kIHVzZSBTbmFrZW1ha2UgdG8gYXV0b21hdGUgYSBzaW1wbGUgd29ya2Zsb3cuCiogVXNlIHdpbGRjYXJkcyB0byBlYXNpbHkgZXh0ZW5kIGEgd29ya2Zsb3cgdG8gbmV3IGlucHV0cy4KKiBTaG93IGEgU25ha2VtYWtlIGRyeS1ydW4gYW5kIG90aGVyIHdheXMgdG8gdmlzdWFsaXplIHRoZSB3b3JrZmxvdy4KCgojIyBXaGF0IGlzIFNuYWtlbWFrZT8KClNuYWtlbWFrZSBpcyBhIHB5dGhvbiBwcm9ncmFtIHRoYXQgaW50ZXJwcmV0cyBhIHRleHQgZmlsZSBkZXNjcmliaW5nIHlvdXIKd29ya2Zsb3cgYWdhaW5zdCB0aGUgZmlsZSBzeXN0ZW0gYW5kIHVzZXMgcnVsZXMgaW4gdGhhdCB3b3JrZmxvdyB0byB0cmFuc2Zvcm0KaW5wdXRzIGludG8gb3V0cHV0cyBhcyBuZWNlc3NhcnkuIFNuYWtlbWFrZSBpcyBlc3NlbnRpYWxseSAqKmRlY2xhcmF0aXZlKioKbWVhbmluZyB0aGF0IHlvdSBkb24ndCB0ZWxsIGl0IHdoYXQgdG8gZG8sIHlvdSBzaW1wbHkgYXNrIGZvciB0aGUgZmluYWwgb3V0cHV0CmFuZCBTbmFrZW1ha2UgZmlndXJlcyBvdXQgd2hhdCBpdCBuZWVkcyB0byBkby4gVGhhdCBkZWNsYXJhdGl2ZSBwYXR0ZXJuIHN1cHBvcnRzCnNldmVyYWwga2V5IGF0dHJpYnV0ZXM6CgotIFNuYWtlbWFrZSB3b3JrZmxvd3MgY2FuIGdyYWNlZnVsbHkgZXh0ZW5kIHRvIG5ldyBpbnB1dHMuCi0gU25ha2VtYWtlIGlzIG1vZHVsYXIuCi0gU25ha2VtYWtlIGlzIHBvcnRhYmxlLgotIFNuYWtlbWFrZSBpcyDigJxkdXJhYmxl4oCdIChpLmUgaXQgY3Jhc2hlcyB3aXRoIHBvaXNlKS4KLSBTbmFrZW1ha2UgaXMgZWZmaWNpZW50LgoKCiMjIEJ1aWxkIGEgU25ha2VmaWxlCgpBIFNuYWtlZmlsZSBpcyBhIHRleHQgZmlsZSB0aGF0IGRlY3JpYmVzIHlvdXIgd29ya2Zsb3cuIEl0IGlzIGludGVycHJldGVkIGJ5IHRoZQpgc25ha2VtYWtlYCBjb21tYW5kLiBCdWlsZGluZyBhIGdvb2QgU25ha2VmaWxlIGlzIHRoZSBrZXkgc3RlcCBpbiB1bmRlcnN0YW5kaW5nClNuYWtlbWFrZSwgc28gd2UnbGwgYmVnaW4gdGhlcmUuCgojIyMgQ29uc2lkZXIgYSBzYW1wbGUgYmFzaCBzY3JpcHQKClRvIGJ1aWxkIGEgU25ha2VmaWxlIGl0J3MgaGVscGZ1bCB0byB1c2UgYSBjb25jcmV0ZSBleGFtcGxlIGFuZCBhbHNvIGhlbHBmdWwgdG8KYWRhcHQgYW4gZXhpc3Rpbmcgc2NyaXB0LiBUaGUgc2NyaXB0IGJlbG93IGNvbnNpZGVycyB0aGUgMTg2OCBub3ZlbCAiTGl0dGxlCldvbWVuIiwgYnkgTG91aXNhIE1heSBBbGNvdHQuIEl0IG91dHB1dHMgYSBmaWxlIGNvbnRhaW5pbmcgdGhlIHJhbmtlZCBuYW1lCmNvdW50cyBmb3IgZWFjaCBvZiB0aGUgZm91ciBNYXJjaCBzaXN0ZXJzOiBBbXksIEJldGgsIEpvLCBhbmQgTGF1cmllLiAoSXQKYWxzbyBlbWl0cyBhIGZldyBpbnRlcm1lZGlhdGUgZmlsZXMpLgoKCjx0YWJsZSBjbGFzcz0nZmlnJz48dHI+PHRoIGNsYXNzPSdmaWcnIGNvbHNwYW49IjIiPmFsY290dF9zY3JpcHQvYWxjb3R0X3NjcmlwdC5zaDwvdGg+PC90cj4KPHRyPgo8dGQgY2xhc3M9J2ZpZyc+PGltZyBzcmM9ImltYWdlcy9Nb2R1bGUwN19iYXNoX2RhZy5wbmciLz48L3RkPgo8dGQgY2xhc3M9J2ZpZyc+PHByZT4KIyEvYmluL2Jhc2gKIyBXaGljaCBvZiB0aGUgTWFyY2ggc2lzdGVycyBpcyByZWZlcnJlZCB0byBtb3N0IG9mdGVuIGluCiMgcGFydCAxIG9mIHRoZSBMaXR0bGUgV29tZW4/CgojIFNwbGl0IGxpbmVzIGludG8gd29yZHMKY2F0IGlucHV0cy9saXR0bGVfd29tZW5fcGFydF8xLnR4dCBcCiAgICB8IHRyIC1jcyAnWzphbHBoYTpdJyAnXG4nIFwKICAgID4gMS5zcGxpdF93b3Jkcy50eHQKCiMgQ291bnQgd29yZHMKc29ydCAxLnNwbGl0X3dvcmRzLnR4dCB8IHVuaXEgLWMgXAogICAgPiAyLmNvdW50X3dvcmRzLnR4dAoKIyBTb3J0IHdvcmRzIGJ5IGRlc2NlbmRpbmcgY291bnQgYW5kIGFkZCBoZWFkZXIKc29ydCAtazEsMW5yIDIuY291bnRfd29yZHMudHh0IFwKICAgIHwgYXdrICdCRUdJTiB7cHJpbnQgIndvcmRcdGNvdW50In0geyBwcmludCAkMiAiXHQiICQxfScgXAogICAgPiAzLnNvcnRfY291bnRzLnR4dAoKIyBTZWxlY3QgbmFtZXMgd2l0aCByZXNwZWN0aXZlIGNvdW50cwplZ3JlcCAnXihKb3xBbXl8TGF1cmllfEJldGgpXHMnIDMuc29ydF9jb3VudHMudHh0ID4gNC5zZWxlY3Rfd29yZHMudHh0CjwvcHJlPjwvdGQ+CjwvdHI+PC90YWJsZT4KPGJyLz4KCgpJdCdzIG5vdCBuZWNlc3NhcnkgdG8gdW5kZXJzdGFuZCB0aGUgY29tbWFuZCBpbiBkZXRhaWwsIGJ1dCBpdCBpcyBoZWxwZnVsIHRvIApjb25zaWRlciB0aGUgc3RlcHMgd2l0aCB0aGVpciB2YXJpb3VzIG91dHB1dHM6CgoKPHRhYmxlIGNsYXNzPSdmaWcnPjx0cj48dGggY2xhc3M9J2ZpZyc+SW5wdXRzLCBzdGVwcywgYW5kIG91dHB1dHM8L3RoPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+aW5wdXRzL2xpdHRsZV93b21lbl9wYXJ0XzEudHh0PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+CkNIQVBURVIgT05FCgpQTEFZSU5HIFBJTEdSSU1TCgoiQ2hyaXN0bWFzIHdvbid0IGJlIENocmlzdG1hcyB3aXRob3V0IGFueSBwcmVzZW50cywiIGdydW1ibGVkIEpvLCBseWluZwpvbiB0aGUgcnVnLgoKIkl0J3Mgc28gZHJlYWRmdWwgdG8gYmUgcG9vciEiIHNpZ2hlZCBNZWcsIGxvb2tpbmcgZG93biBhdCBoZXIgb2xkCmRyZXNzLgouLi4KPC9wcmU+PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjEuc3BsaXRfd29yZHMudHh0PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+Q0hBUFRFUgpPTkUKUExBWUlORwpQSUxHUklNUwpDaHJpc3RtYXMKd29uCnQKYmUKQ2hyaXN0bWFzCndpdGhvdXQKPC9wcmU+PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjIuY291bnRfd29yZHMudHh0PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+CiAgIDE5OTIgYQogICAgIDc1IEEKICAgICAgMSBhYmFjawogICAgICAxIGFiYXNlCiAgICAgIDMgYWJhc2hlZAogICAgICAxIGFiZWQKICAgICAgMSBhYmplY3QKICAgICAgMyBhYmxlCiAgICAgIDMgYWJvbWluYWJsZQogICAgICAxIGFib21pbmFibHkKPC9wcmU+PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjMuc29ydF9jb3VudHMudHh0PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+CndvcmQJY291bnQKYW5kCTM4MTEKdGhlCTMzMDMKdG8JMjMwMwphCTE5OTIKSQkxOTg4CmhlcgkxNjYwCm9mCTE0OTAKaW4JMTA5NAp5b3UJMTAxMAo8L3ByZT48L3RkPjwvdHI+Cjx0cj48dGQgY2xhc3M9J2ZpZyc+NC5zZWxlY3Rfd29yZHMudHh0PC90ZD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+CkpvCTczNwpMYXVyaWUJMzE5CkJldGgJMjk0CkFteQkyODMKPC9wcmU+PC90ZD48L3RyPgo8L3RhYmxlPgo8YnIvPgoKClRvIHZpZXcgdGhlIHJlc3VsdHMgaW50ZXJhY3RpdmVseSwgeW91IGNhbiBleGVjdXRlIHRoZSBzY3JpcHQuCgpgYGBzaApleHBvcnQgV09SS1NIT1BfSE9NRT0iL25mcy90dXJiby91bW1zLWJpb2luZi13a3NocC93b3Jrc2hvcC9ob21lLyR7VVNFUn0iCmNkICRXT1JLU0hPUF9IT01FL3Byb2plY3RfYWxjb3R0L2FsY290dF9zY3JpcHQKCi4vYWxjb3R0X3NjcmlwdC5zaApgYGAKCkFuZCB2aWV3IGV4Y2VwdHMgb2YgdGhlIHJlc3VsdHMgaW4gYGxlc3NgLiAoSGl0IGBxYCBrZXkgdG8gZXhpdCBgbGVzc2AuKQoKYGBgc2gKaGVhZCBpbnB1dHMvbGl0dGxlX3dvbWVuX3BhcnRfMS50eHQgKi50eHQgfCBsZXNzCmBgYAoKIyMjIFNuYWtlbWFrZSBhbmQgdGhlIFNuYWtlZmlsZQoKU3dpdGNoIHRvIHRoZSBhbGNvdHRfc25ha2VtYWtlIGRpcjsgbm90ZSB0aGVyZSBhcmUgaW5wdXRzIGFuZCBhIFNuYWtlZmlsZQpgYGBzaApjZCAkV09SS1NIT1BfSE9NRS9wcm9qZWN0X2FsY290dC9hbGNvdHRfc25ha2VtYWtlCnRyZWUKYGBgCj4gYGBgCi4K4pSc4pSA4pSAIGlucHV0cwrilILCoMKgIOKUnOKUgOKUgCBsaXR0bGVfd29tZW5fcGFydF8xLnR4dArilILCoMKgIOKUlOKUgOKUgCBsaXR0bGVfd29tZW5fcGFydF8yLnR4dArilJTilIDilIAgd29ya2Zsb3cKICAgIOKUlOKUgOKUgCBTbmFrZWZpbGUKICBgYGAKCgpBIFNuYWtlZmlsZSBpcyBhIHNpbXBsZSBtYXJrZG93biBmaWxlLkJ5IGNvbnZlbnRpb24sIHRoZQpzbmFrZWZpbGUgaXMgbmFtZWQgYFNuYWtlZmlsZWAgYW5kIGxpdmVzIGluIHRoZSBgd29ya2Zsb3dgIHN1Yi1kaXJlY3RvcnkuIApUaGUgY3VycmVudCBTbmFrZWZpbGUgaXMgc2ltcGx5IGEgc3R1YiBiYXNlZCBvbiB0aGUgZWFybGllciBiYXNoIHNjcmlwdDoKCjx0YWJsZSBjbGFzcz0nZmlnJz48dHI+PHRoIGNsYXNzPSdmaWcnPndvcmtmbG93L1NuYWtlZmlsZTwvdGg+PC90cj4KPHRyPjx0ZCBjbGFzcz0nZmlnJz48cHJlPgojIHNwbGl0X2xpbmVzICMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiNjYXQgaW5wdXRzL2xpdHRsZV93b21lbl9wYXJ0XzEudHh0IFwKIyAgICB8IHRyIC1jcyAnWzphbHBoYTpdJyAnXG4nIFwKIyAgICA+IDEuc3BsaXRfd29yZHMudHh0CiMgY291bnRfd29yZHMgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKI3NvcnQgMS5zcGxpdF93b3Jkcy50eHQgfCB1bmlxIC1jIFwKIyAgICA+IDIuY291bnRfd29yZHMudHh0CiMjIHNvcnRfY291bnQgIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKI3NvcnQgLWsxLDFuciAyLmNvdW50X3dvcmRzLnR4dCBcCiMgICAgfCBhd2sgJ0JFR0lOIHtwcmludCAid29yZFx0Y291bnQifSB7IHByaW50ICQyICJcdCIgJDF9JyBcCiMgICAgPiAzLnNvcnRfY291bnRzLnR4dAojIHNlbGVjdF93b3JkcyAjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiNlZ3JlcCAnXihKb3xBbXl8TGF1cmllfEJldGgpXHMnIDMuc29ydF9jb3VudHMudHh0ID4gNC5zZWxlY3Rfd29yZHMudHh0CjwvcHJlPjwvdGQ+PC90cj48L3RhYmxlPgoKCkEgc25ha2VmaWxlIGlzIGNvbXBvc2VkIG9mIHJ1bGVzOyBlYWNoIHJ1bGUgaGFzIGEgbmFtZSBhbmQgc3BlY2lmaWVzIGRpcmVjdGl2ZXMgc3VjaCBhcwoKLSBpbnB1dCAod2hhdCB0aGUgcnVsZSByZXF1aXJlcykKLSBvdXRwdXQgKHdoYXQgdGhlIHJ1bGUgcHJvbWlzZXMgdG8gZ2VuZXJhdGUpCi0gc2hlbGwgKGhvdyBpdCB3aWxsIG1ha2UgaW5wdXQgaW50byBvdXRwdXQpCgpXZSBhZGQgdGhlIGZpcnN0IHN0ZXAgaW4gdGhlIHdvcmtmbG93IGFzIGEgbmV3IHJ1bGUuIChBZGRpbmcgYmVsb3cgdGhlIGV4aXN0aW5nCmNvbW1lbnQgYmxvY2suKSBOb3RlIHRoYXQgd2Ugd2lsbCBrZWVwIHRoZSBpbnB1dCBmaWxlcyBhbmQgcmVzdWx0IGZpbGVzIGluIApzZXBhcmF0ZSBkaXJlY3Rvcmllcy4KCmBgYApydWxlIHNwbGl0X3dvcmRzOgogICAgaW5wdXQ6ICJpbnB1dHMvbGl0dGxlX3dvbWVuX3BhcnRfMS50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0IiAKICAgIHNoZWxsOiAiY2F0IGlucHV0cy9saXR0bGVfd29tZW5fcGFydF8xLnR4dCB8IHRyIC1jcyAnWzphbHBoYTpdJyAnXG4nID4gcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNwbGl0X3dvcmRzLnR4dCIKYGBgCgojIyMgUnVubmluZyBTbmFrZW1ha2UKCi0gVGhlICoqc25ha2VtYWtlKiogcHl0aG9uIHByb2dyYW0gaW50ZXJwcmV0cyB0aGUgU25ha2VmaWxlIGFuZCBleGVjdXRlcyBydWxlcyBhcyAKYXBwcm9wcmlhdGUgdG8gcHJvZHVjZSB0aGUgZXhwZWN0ZWQgb3V0cHV0LiAKLSBUaGUgc25ha2VtYWtlIHByb2dyYW0gaXMgaW5zdGFsbGVkIGFzIGEgbW9kdWxlIG9uIEdyZWF0IExha2VzLgotIFdoZW4gbGF1bmNoaW5nIHNuYWtlbWFrZSwgWW91IHNwZWNpZnkgdGhlIG51bWJlciBvZiBjb3JlcyAoaS5lLiBDUFVzKSB3aXRoIHRoZQotYyBmbGFnLiBXZSB3aWxsIHVzZSAxIGNvcmUuCgpgYGBzaAptb2R1bGUgbG9hZCBzbmFrZW1ha2UKbW9kdWxlIGxpc3QKIyBjb25maXJtIHlvdSBhcmUgaW4gdGhlIHJpZ2h0IHdvcmtpbmcgZGlyZWN0b3J5CnRyZWUKYGBgCgo+IGBgYHNoPQpDdXJyZW50bHkgTG9hZGVkIE1vZHVsZXM6CiAgMSkgcHl0aG9uMy4xMC1hbmFjb25kYS8yMDIzLjAzICAgMikgc25ha2VtYWtlLzcuMzIuNAokIHRyZWUKLgrilJzilIDilIAgaW5wdXRzCuKUgsKgwqAg4pSc4pSA4pSAIGxpdHRsZV93b21lbl9wYXJ0XzEudHh0CuKUgsKgwqAg4pSU4pSA4pSAIGxpdHRsZV93b21lbl9wYXJ0XzIudHh0CuKUlOKUgOKUgCB3b3JrZmxvdwogICAg4pSU4pSA4pSAIFNuYWtlZmlsZQogIGBgYAoKClJ1biBgc25ha2VtYWtlYCBjb21tYW5kIGFzIGJlbG93LiBOb3RlOgoKLSBgc25ha2VtYWtlYCB3aWxsIGxvb2sgZm9yIGEgU25ha2VmaWxlIGluIHRoZSB3b3JrZmxvdyBkaXJlY3RvcnkuCi0gYHNuYWtlbWFrZWAgZW1pdHMgYSBsb2cgdG8gdGhlIHNjcmVlbiB0aGF0IGluY2x1ZGUgCiAgLSBydW4gZGV0YWlscwogIC0gSm9iIHN0YXRzCiAgLSBSdWxlIGRldGFpbHMKICAtIENvbXBsZXRpb24gc3RhdHVzIGkuZS4gKDEwMCUpIGRvbmUKCmBgYApzbmFrZW1ha2UgLWMxCmBgYAoKCj4gYGBgCiAgICBCdWlsZGluZyBEQUcgb2Ygam9icy4uLgogICAgVXNpbmcgc2hlbGw6IC91c3IvYmluL2Jhc2gKICAgIFByb3ZpZGVkIGNvcmVzOiAxICh1c2UgLS1jb3JlcyB0byBkZWZpbmUgcGFyYWxsZWxpc20pCiAgICBSdWxlcyBjbGFpbWluZyBtb3JlIHRocmVhZHMgd2lsbCBiZSBzY2FsZWQgZG93bi4KICAgIEpvYiBzdGF0czoKICAgIGpvYiAgICAgICAgICAgIGNvdW50CiAgICAtLS0tLS0tLS0tLSAgLS0tLS0tLQogICAgc3BsaXRfd29yZHMgICAgICAgIDEKICAgIHRvdGFsICAgICAgICAgICAgICAxCiAgICAuCiAgICBTZWxlY3Qgam9icyB0byBleGVjdXRlLi4uCiAgICAuCiAgICBbTW9uIEp1biAxMCAxODozNTo0MSAyMDI0XQogICAgcnVsZSBzcGxpdF93b3JkczoKICAgICAgICBpbnB1dDogaW5wdXRzL2xpdHRsZV93b21lbl9wYXJ0XzEudHh0CiAgICAgICAgb3V0cHV0OiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0CiAgICAgICAgam9iaWQ6IDAKICAgICAgICByZWFzb246IE1pc3Npbmcgb3V0cHV0IGZpbGVzOiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0CiAgICAgICAgcmVzb3VyY2VzOiB0bXBkaXI9L3RtcAogICAgLgogICAgW01vbiBKdW4gMTAgMTg6MzU6NDEgMjAyNF0KICAgIEZpbmlzaGVkIGpvYiAwLgogICAgMSBvZiAxIHN0ZXBzICgxMDAlKSBkb25lCiAgICBDb21wbGV0ZSBsb2c6IC5zbmFrZW1ha2UvbG9nLzIwMjQtMDYtMTBUMTgzNTQxLjIwNDYzNi5zbmFrZW1ha2UubG9nCiAgYGBgCgpDaGVjayB0aGUgZmlsZSBzeXN0ZW0uIE5vdGUgbmV3IHJlc3VsdHMvIGRpciBhbmQgbmV3IGZpbGUuIE5vdGUgdGhhdCBgc25ha2VtYWtlYApjcmVhdGVkIHRoZSByZXN1bHRzLyBkaXIgYXV0b21hdGljYWxseS4KCmBgYHNoCnRyZWUKYGBgCgo+IGBgYAouCuKUnOKUgOKUgCBpbnB1dHMK4pSCwqDCoCDilJzilIDilIAgbGl0dGxlX3dvbWVuX3BhcnRfMS50eHQK4pSCwqDCoCDilJTilIDilIAgbGl0dGxlX3dvbWVuX3BhcnRfMi50eHQK4pSc4pSA4pSAIHJlc3VsdHMK4pSCwqDCoCDilJTilIDilIAgbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQK4pSU4pSA4pSAIHdvcmtmbG93CiAgICDilJTilIDilIAgU25ha2VmaWxlCiAgYGBgCgpBbHNvIG5vdGUgdGhhdCBgc25ha2VtYWtlYCBjcmVhdGVzIGEgaGlkZGVuIGRpcmVjdG9yeSBgLnNuYWtlbWFrZS9gIGluIHRoZSB3b3JraW5nIApkaXJlY3RvcnkuIFRoaXMgaG9sZHMgdmFyaW91cyBpbmZvIHRoYXQgaGVscCBzbmFrZW1ha2UgbWFuYWdlIHRoZSB3b3JrZmxvdy4gSXQncwpleHRyZW1lbHkgcmFyZSBmb3IgeW91IHRvIG5lZWQgdG8gZXhhbWluZSB0aGUgY29udGVudHMgb2YgdGhlIGAuc25ha2VtYWtlL2AgCmRpcmVjdG9yeSwgYnV0IEZZSS4KCklmIHlvdSByZS1ydW4gYHNuYWtlbWFrZWAgaXQgZGV0ZXJtaW5lcyB0aGUgZmlsZXN5c3RlbSBpcyB1cCB0byBkYXRlIGFuZCBubyAKcnVsZXMgbmVlZCB0byBiZSB0cmlnZ2VyZWQuCgpgYGBzaApzbmFrZW1ha2UgLWMxCmBgYAoKPiBgYGAKQnVpbGRpbmcgREFHIG9mIGpvYnMuLi4KTm90aGluZyB0byBiZSBkb25lIChhbGwgcmVxdWVzdGVkIGZpbGVzIGFyZSBwcmVzZW50IGFuZCB1cCB0byBkYXRlKS4KQ29tcGxldGUgbG9nOiAuc25ha2VtYWtlL2xvZy8yMDI0LTA2LTEwVDE4NDg1MC45NDQ1MzUuc25ha2VtYWtlLmxvZwogIGBgYAoKCiMjIyBBZGQgYSBuZXcgcnVsZQoKSW4gdGhlIFNuYWtlbWFrZSBmaWxlLCBhZGQgdGhlIHNlY29uZCBzdGVwIGFzIGEgbmV3IHJ1bGUgYmVsb3cgdGhlIGZpcnN0IHJ1bGUuCk5vdGUgdGhhdCB0aGUgaW5wdXRzIG9mIHRoaXMgcnVsZSBtYXRjaCB0aGUgb3V0cHV0cyBvZiB0aGUgcHJpb3IgcnVsZS4KCmBgYApydWxlIGNvdW50X3dvcmRzOgogICAgaW5wdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0IgogICAgb3V0cHV0OiAicmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLmNvdW50X3dvcmRzLnR4dCIKICAgIHNoZWxsOiAic29ydCByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0IHwgdW5pcSAtYyA+IHJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5jb3VudF93b3Jkcy50eHQiCmBgYAoKQnkgZGVmYXVsdCwgYHNuYWtlbWFrZWAgb25seSBydW5zIHRoZSBmaXJzdCBydWxlIGluIHRoZSBTbmFrZWZpbGUuIFRvIHRyaWdnZXIgCnRoZSBuZXcgcnVsZSwgeW91IGNhbiBzcGVjaWZ5IHRoZSBkZXNpcmVkIGVuZCB0YXJnZXQgb24gdGhlIGNvbW1hbmQgbGluZS4KCmBgYHNoCnNuYWtlbWFrZSAtYzEgY291bnRfd29yZHMKYGBgCgpOb3RlIHRoZSBsb2cgb3V0cHV0IHNpbWlsYXIgdG8gYWJvdmUsIGJ1dCBub3cgc2hvd3MgdGhlIG5ldyBydWxlLgoKPiBgYGAKQnVpbGRpbmcgREFHIG9mIGpvYnMuLi4KVXNpbmcgc2hlbGw6IC91c3IvYmluL2Jhc2gKUHJvdmlkZWQgY29yZXM6IDEgKHVzZSAtLWNvcmVzIHRvIGRlZmluZSBwYXJhbGxlbGlzbSkKUnVsZXMgY2xhaW1pbmcgbW9yZSB0aHJlYWRzIHdpbGwgYmUgc2NhbGVkIGRvd24uCkpvYiBzdGF0czoKam9iICAgICAgICAgICAgY291bnQKLS0tLS0tLS0tLS0gIC0tLS0tLS0KY291bnRfd29yZHMgICAgICAgIDEKdG90YWwgICAgICAgICAgICAgIDEKLgpTZWxlY3Qgam9icyB0byBleGVjdXRlLi4uCi4KW01vbiBKdW4gMTAgMTg6NTI6MDcgMjAyNF0KcnVsZSBjb3VudF93b3JkczoKICAgIGlucHV0OiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0CiAgICBvdXRwdXQ6IHJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5jb3VudF93b3Jkcy50eHQKICAgIGpvYmlkOiAwCiAgICByZWFzb246IE1pc3Npbmcgb3V0cHV0IGZpbGVzOiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuY291bnRfd29yZHMudHh0CiAgICByZXNvdXJjZXM6IHRtcGRpcj0vdG1wCi4KW01vbiBKdW4gMTAgMTg6NTI6MDggMjAyNF0KRmluaXNoZWQgam9iIDAuCjEgb2YgMSBzdGVwcyAoMTAwJSkgZG9uZQpDb21wbGV0ZSBsb2c6IC5zbmFrZW1ha2UvbG9nLzIwMjQtMDYtMTBUMTg1MjA3LjU5ODY5NS5zbmFrZW1ha2UubG9nCiAgYGBgCgpTZWUgdGhlIG5ldyBmaWxlIGFuZCByZXZpZXcgdGhlIGNvbnRlbnRzOgoKYGBgc2gKaGVhZCByZXN1bHRzLyoKYGBgCgo+IGBgYAo9PT4gcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydDEuY291bnRfd29yZHMudHh0IDw9PQogICAxOTkyIGEKICAgICA3NSBBCiAgICAgIDEgYWJhY2sKICAgICAgMSBhYmFzZQogICAgICAzIGFiYXNoZWQKICAgICAgMSBhYmVkCiAgICAgIDEgYWJqZWN0CiAgICAgIDMgYWJsZQogICAgICAzIGFib21pbmFibGUKICAgICAgMSBhYm9taW5hYmx5Ci4KPT0+IHJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQgPD09CkNIQVBURVIKT05FClBMQVlJTkcKUElMR1JJTVMKQ2hyaXN0bWFzCndvbgp0CmJlCkNocmlzdG1hcwp3aXRob3V0CiAgYGBgCgojIyMgVmFyaWFibGVzIC8gUnVsZSBvcmRlcmluZwoKRWFjaCBydWxlIGRlZmluZXMgKippbnB1dCoqIGFuZCAqKm91dHB1dCoqIHZhcmlhYmxlczsgeW91IGNhbiB1c2UgdGhvc2UgdmFsdWVzCmluIHRoZSAqKnNoZWxsKiogZGlyZWN0aXZlOgoKYGBgCnJ1bGUgc3BsaXRfd29yZHM6CiAgICBpbnB1dDogImlucHV0cy9saXR0bGVfd29tZW5fcGFydF8xLnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQiCiAgICBzaGVsbDogImNhdCB7aW5wdXR9IHwgdHIgLWNzICdbOmFscGhhOl0nICdcbicgPiB7b3V0cHV0fSIKCnJ1bGUgY291bnRfd29yZHM6CiAgICBpbnB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0MS5jb3VudF93b3Jkcy50eHQiCiAgICBzaGVsbDogInNvcnQge2lucHV0fSB8IHVuaXEgLWMgPiB7b3V0cHV0fSIKYGBgCgpTbmFrZW1ha2UgZG9lc24ndCBjYXJlIHdoYXQgb3JkZXIgdGhlIHJ1bGVzIGFwcGVhciBpbiB0aGUgU25ha2VmaWxlIGJ1dCBpdCAKYXNzdW1lcyB0aGUgZmlyc3QgcnVsZSBpcyB0aGUgZGVmYXVsdCB0YXJnZXQuIFB1dCB0aGUgbGFzdCBzdGVwIG9mIHRoZSB3b3JrZmxvdyAKYXQgdGhlIHRvcCBvZiB0aGUgZmlsZSBhbmQgYHNuYWtlbWFrZWAgd2lsbCBkZWZhdWx0IHRvIHJ1bm5pbmcgdGhlIHdob2xlIHdvcmtmbG93LgoKYGBgCnJ1bGUgY291bnRfd29yZHM6CiAgICBpbnB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0MS5jb3VudF93b3Jkcy50eHQiCiAgICBzaGVsbDogInNvcnQge2lucHV0fSB8IHVuaXEgLWMgPiB7b3V0cHV0fSIKCnJ1bGUgc3BsaXRfd29yZHM6CiAgICBpbnB1dDogImlucHV0cy9saXR0bGVfd29tZW5fcGFydF8xLnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQiCiAgICBzaGVsbDogImNhdCB7aW5wdXR9IHwgdHIgLWNzICdbOmFscGhhOl0nICdcbicgPiB7b3V0cHV0fSIKYGBgCgpSZXJ1biBhZnRlciBtYWtpbmcgdGhlc2UgY2hhbmdlcywgYW5kIGNoZWNrIG91dCB0aGUgbG9nIChleGNlcHRlZCBiZWxvdykuCgogIC0gTm90ZSB0aGF0IGluICoqSm9iIHN0YXRzKiogYWxsIHJ1bGVzIGFyZSB0cmlnZ2VyZWQuCiAgLSBOb3RlIHRoZSB2YWx1ZSBvZiByZWFzb24gaW4gZWFjaCBvZiB0aGUgcnVsZXMuIERvZXMgdGhlIHJlYXNvbiBtYWtlIHNlbnNlPwoKYGBgc2gKc25ha2VtYWtlIC1jMQpgYGAKCj4gYGBgCi4uLgpKb2Igc3RhdHM6CmpvYiAgICAgICAgICAgIGNvdW50Ci0tLS0tLS0tLS0tICAtLS0tLS0tCmNvdW50X3dvcmRzICAgICAgICAxCnNwbGl0X3dvcmRzICAgICAgICAxCnRvdGFsICAgICAgICAgICAgICAyCi4uLgpydWxlIHNwbGl0X3dvcmRzOgogICAgaW5wdXQ6IGlucHV0cy9saXR0bGVfd29tZW5fcGFydF8xLnR4dAogICAgb3V0cHV0OiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0CiAgICBqb2JpZDogMQogICAgcmVhc29uOiBDb2RlIGhhcyBjaGFuZ2VkIHNpbmNlIGxhc3QgZXhlY3V0aW9uCiAgICByZXNvdXJjZXM6IHRtcGRpcj0vdG1wCi4uLgpydWxlIGNvdW50X3dvcmRzOgogICAgaW5wdXQ6IHJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQKICAgIG91dHB1dDogcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydDEuY291bnRfd29yZHMudHh0CiAgICBqb2JpZDogMAogICAgcmVhc29uOiBJbnB1dCBmaWxlcyB1cGRhdGVkIGJ5IGFub3RoZXIgam9iOiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0CiAgICByZXNvdXJjZXM6IHRtcGRpcj0vdG1wCiAgYGBgCgoKIyMjIFVzaW5nIHRyaXBsZSBxdW90ZXMgLyBFc2NhcGluZyBjdXJseSBicmFjZXMKCkFkZCB0aGUgdGhpcmQgc3RlcCBvZiB0aGUgdHJhbnNmb3JtCgotIFBsYWNlIGl0ICoqYWJvdmUqKiB0aGUgZXhpc3RpbmcgcnVsZXMuCi0gVXNlIHtpbnB1dH0gYW5kIHtvdXRwdXR9IHZhcmlhYmxlcy4KLSBXZSB1c2UgdHJpcGxlIHF1b3RlcyB0byBtYWtlIG11bHRpbGluZSBzaGVsbCBjb21tYW5kcy4KLSBJbiB0aGUgc2hlbGwsIGFueSBicmFjZXMgKHt9KSByZWxhdGVkIHRvIHRoZSBjb21tYW5kIG11c3QgYmUgZG91YmxlZCB0byAKZGlzdGluZ3Vpc2ggdGhlbSBmcm9tIFNuYWtlZmlsZSB2YXJpYWJsZXMuCgpgYGAKcnVsZSBzb3J0X2NvdW50czoKICAgIGlucHV0OiAicmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLmNvdW50X3dvcmRzLnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zb3J0X2NvdW50cy50eHQiCiAgICBzaGVsbDogIiIiCglzb3J0IC1rMSwxbnIge2lucHV0fSBcCgkgICAgfCBhd2sgJ0JFR0lOIHt7cHJpbnQgIndvcmRcdGNvdW50In19IHt7IHByaW50ICQyICJcdCIgJDF9fScgXAogICAgICAgICAgICA+IHtvdXRwdXR9CiAgICAgICAgIiIiCmBgYAoKTm90ZSB0aGF0IHdoZW4geW91IHJ1biBgc25ha2VtYWtlYCBpdCBvbmx5IGdlbmVyYXRlcyB0aGUgZmlsZXMgaXQgbmVlZHMgdG8uCgpgYGBzaApzbmFrZW1ha2UgLWMxCmBgYAoKPiBgYGAKLi4uCkpvYiBzdGF0czoKam9iICAgICAgICAgICAgY291bnQKLS0tLS0tLS0tLS0gIC0tLS0tLS0Kc29ydF9jb3VudHMgICAgICAgIDEKdG90YWwgICAgICAgICAgICAgIDEKLi4uCnJ1bGUgc29ydF9jb3VudHM6CiAgICBpbnB1dDogcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLmNvdW50X3dvcmRzLnR4dAogICAgb3V0cHV0OiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc29ydF9jb3VudHMudHh0CiAgICBqb2JpZDogMAogICAgcmVhc29uOiBNaXNzaW5nIG91dHB1dCBmaWxlczogcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNvcnRfY291bnRzLnR4dAogICAgcmVzb3VyY2VzOiB0bXBkaXI9L3RtcAouLi4KMSBvZiAxIHN0ZXBzICgxMDAlKSBkb25lCiAgYGBgClVzZSB0cmlwbGUgcXVvdGVzIHRvIGNyZWF0ZSBtdWx0aWxpbmUgc2hlbGwgY29tbWFuZHMuIElmIHlvdXIgY29tbWFuZHMgdXNlIGN1cmx5IGJyYWNlcyAoe30pIHlvdSBuZWVkIHRvIGRvdWJsZSB0aGVtIHNvIHNuYWtlbWFrZSBkb2Vzbid0IGdldCBjb25mdXNlZC4KCiMjIyBTbmFrZW1ha2UgZHJ5LXJ1biAvIEludGVycHJldGluZyBydWxlIGVycm9ycwoKQWRkIHRoZSBmb3VydGggcnVsZSwgcGxhY2luZyBpdCBhYm92ZSB0aGUgcHJpb3IuCgpgYGAKcnVsZSBzZWxlY3Rfd29yZHM6CiAgICBpbnB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zb3J0X2NvdW50cy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc2VsZWN0X3dvcmRzLnR4dCIKICAgIHNoZWxsOiAiZWdyZXAgJ14oSm98QW15fExhdXJpZXxCZXRoKVxzJyB7aW5wdXR9ID4ge291dHB1dH0iCmBgYAoKWW91IHByb2JhYmx5IGhhdmUgbm90aWNlZCB0aGF0IGBzbmFrZW1ha2VgIHdpbGwgb25seSBydW4gdGhlIHJ1bGVzIGl0IG5lZWRzIHRvLCAKYmFzZWQgb24gY2hhbmdlcyBpbiBmaWxlcyBvciBzY3JpcHRzIG9yIG1pc3NpbmcgZmlsZXMuIFlvdSBjYW4gcHJldmlldyB3aGF0IApgc25ha2VtYWtlYCB3aWxsIGRvIGJ5IHVzaW5nIHRoZSBkcnktcnVuIGZsYWcgYC0tZHJ5LXJ1bmAgb3IgYC1uYC4gVGhpcyB3aWxsCnNob3cgdGhlIHR5cGljYWwgc25ha2VtYWtlIG91dHB1dCBidXQgd2lsbCBub3QgY2hhbmdlIGFueSBmaWxlcy4KCmBgYHNoCnNuYWtlbWFrZSAtYzEgLS1kcnktcnVuCmBgYAoKPiBgYGAKQnVpbGRpbmcgREFHIG9mIGpvYnMuLi4KSm9iIHN0YXRzOgpqb2IgICAgICAgICAgICAgY291bnQKLS0tLS0tLS0tLS0tICAtLS0tLS0tCnNlbGVjdF93b3JkcyAgICAgICAgMQp0b3RhbCAgICAgICAgICAgICAgIDEKLgpbTW9uIEp1biAxMCAxOTo0MTo0MyAyMDI0XQpydWxlIHNlbGVjdF93b3JkczoKICAgIGlucHV0OiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc29ydF9jb3VudHMudHh0CiAgICBvdXRwdXQ6IHJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zZWxlY3Rfd29yZHMudHh0CiAgICBqb2JpZDogMAogICAgcmVhc29uOiBNaXNzaW5nIG91dHB1dCBmaWxlczogcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNlbGVjdF93b3Jkcy50eHQKICAgIHJlc291cmNlczogdG1wZGlyPS90bXAKLgpKb2Igc3RhdHM6CmpvYiAgICAgICAgICAgICBjb3VudAotLS0tLS0tLS0tLS0gIC0tLS0tLS0Kc2VsZWN0X3dvcmRzICAgICAgICAxCnRvdGFsICAgICAgICAgICAgICAgMQouClJlYXNvbnM6CiAgICAoY2hlY2sgaW5kaXZpZHVhbCBqb2JzIGFib3ZlIGZvciBkZXRhaWxzKQogICAgbWlzc2luZyBvdXRwdXQgZmlsZXM6CiAgICAgICAgc2VsZWN0X3dvcmRzCi4KVGhpcyB3YXMgYSBkcnktcnVuIChmbGFnIC1uKS4gVGhlIG9yZGVyIG9mIGpvYnMgZG9lcyBub3QgcmVmbGVjdCB0aGUgb3JkZXIgb2YgZXhlY3V0aW9uLgogIGBgYAoKRGV2ZWxvcGluZyBhIFNuYWtlZmlsZSB0eXBpY2FsbHkgaW52b2x2ZXMgc29tZSB0cm91Ymxlc2hvb3RpbmcgYWxvbmcgdGhlIHdheS4KSSd2ZSBpbnRlbnRpb25hbGx5IGludHJvZHVjZWQgYSB0eXBvIGluIHRoZSBTbmFrZWZpbGUgYW5kIHRoZW4gZGlkIGEgZHJ5LXJ1biB0bwpzaG93IGFuIFNuYWtlZmlsZSBlcnJvciBtZXNzYWdlOgoKPiBgYGAKJCBzbmFrZW1ha2UgLWMxIC0tZHJ5LXJ1bgouCkEgIEJ1aWxkaW5nIERBRyBvZiBqb2JzLi4uCkIgIE1pc3NpbmdJbnB1dEV4Y2VwdGlvbiBpbiBydWxlIHNlbGVjdF93b3JkcyBpbiBmaWxlIC4uLi9TbmFrZWZpbGUsIGxpbmUgMTU6CkMgIE1pc3NpbmcgaW5wdXQgZmlsZXMgZm9yIHJ1bGUgc2VsZWN0X3dvcmRzOgpEICAgIG91dHB1dDogcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNlbGVjdF93b3Jkcy50eHQKRSAgICBhZmZlY3RlZCBmaWxlczoKRiAgICAgICAgcmVzdWx0L2xpdHRsZV93b21lbl9wYXJ0XzEuc29ydF9jb3VudHMudHh0CiAgYGBgCgpOb3RlOgoKLSBCIGxpbmUgbnVtYmVyIChsaW5lIDE1KSAKLSBDIE1pc3NpbmcgaW5wdXQgZmlsZXMgZm9yIHJ1bGUgc2VsZWN0X3dvcmRzCi0gRSAmIEYgcmVzdWx0L2xpdHRsZV93b21lbl9wYXJ0XzEuc29ydF9jb3VudHMudHh0CgpDYW4geW91IHNlZSB0aGUgZXJyb3I/IFlvdSB3aWxsIGxpa2VseSBuZWVkIHRvIGNvbnNpZGVyIHRoZSBydWxlIGluIGNvbnRleHQ6CgpgYGAKcnVsZSBzZWxlY3Rfd29yZHM6CiAgICBpbnB1dDogInJlc3VsdC9saXR0bGVfd29tZW5fcGFydF8xLnNvcnRfY291bnRzLnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zZWxlY3Rfd29yZHMudHh0IgogICAgc2hlbGw6ICJlZ3JlcCAnXihKb3xBbXl8TGF1cmllfEJldGgpXHMnIHtpbnB1dH0gPiB7b3V0cHV0fSIKCnJ1bGUgc29ydF9jb3VudHM6CiAgICBpbnB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5jb3VudF93b3Jkcy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc29ydF9jb3VudHMudHh0IgogICAgc2hlbGw6ICIiIgoJc29ydCAtazEsMW5yIHtpbnB1dH0gXAoJICAgIHwgYXdrICdCRUdJTiB7e3ByaW50ICJ3b3JkXHRjb3VudCJ9fSB7eyBwcmludCAkMiAiXHQiICQxfX0nIFwKICAgICAgICAgICAgPiB7b3V0cHV0fQogICAgICAgICIiIgpydWxlIGNvdW50X3dvcmRzOgogICAgaW5wdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc3BsaXRfd29yZHMudHh0IgogICAgb3V0cHV0OiAicmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLmNvdW50X3dvcmRzLnR4dCIKICAgIHNoZWxsOiAic29ydCB7aW5wdXR9IHwgdW5pcSAtYyA+IHtvdXRwdXR9IgoKcnVsZSBzcGxpdF93b3JkczoKICAgIGlucHV0OiAiaW5wdXRzL2xpdHRsZV93b21lbl9wYXJ0XzEudHh0IgogICAgb3V0cHV0OiAicmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNwbGl0X3dvcmRzLnR4dCIKICAgIHNoZWxsOiAiY2F0IHtpbnB1dH0gfCB0ciAtY3MgJ1s6YWxwaGE6XScgJ1xuJyA+IHtvdXRwdXR9IgpgYGAKCkZpeCB0aGUgdHlwbyBhbmQgY29uZmlybSB0aGUgZml4IHVzaW5nIGAtLWRyeS1ydW5gIHRvIGdldCBhIGNsZWFuIG91dHB1dC4gVGhlbgpleGVjdXRlIGBzbmFrZW1ha2VgIG5vcm1hbGx5IHVudGlsIHlvdSBhcmUgMTAwJSBkb25lLiAKCiMjIyBWaXN1YWxpemluZyB0aGUgREFHIC8gcnVsZWdyYXBoCgpJbiBhZGRpdGlvbiB0byBkb2luZyBhIGRyeSBydW4sIGBzbmFrZW1ha2VgIGNhbiBnZW5lcmF0ZSBhIGRpYWdyYW0gb2YgdGhlIHJ1bGVzCnRvIGV4ZWN1dGUuIFNuYWtlbWFrZSB0aGlua3Mgb2YgdGhlIHJ1bGVzIGFzIGEgW0RpcmVjdGVkIEFjeWNsaWMKR3JhcGhdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0RpcmVjdGVkX2FjeWNsaWNfZ3JhcGgpe3RhcmdldD0iX2JsYW5rIn0KKERBRykuIFlvdSBjYW4gc2VlIHRoaXMgd2l0aCB0aGUgYC0tZGFnYCBmbGFnIGFuZCB0aGUgYGRvdGAgcHJvZ3JhbSAocGFydCBvZgpbR3JhcGh2aXpdKGh0dHBzOi8vZ3JhcGh2aXoub3JnLyl7dGFyZ2V0PSJfYmxhbmsifSwgYW4gb3BlbiBzb3VyY2UgZ3JhcGgKdmlzdWFsaXplcikuCgpgYGBzaApzbmFrZW1ha2UgLS1kYWcgfCBkb3QgLVRwbmcgPiBkYWcucG5nCmBgYAoKIVtkYWcucG5nXShpbWFnZXMvTW9kdWxlMDdfZGFnLnBuZykKPGJyLz4KCkFzIHlvdSBjYW4gc2VlIGFib3ZlLCBieSBkZWZhdWx0IGBkb3RgIGRpc3BsYXlzIHRoZSBncmFwaHMgdmVydGljYWxseSB3aXRoIGlucHV0cwphdCB0aGUgdG9wIGFuZCAgb3V0cHV0cyBhdCB0aGUgYm90dG9tLiBPdXIgU25ha2VmaWxlIGlzIGFycmFuZ2VkIGZyb20gCm91dHB1dCB0byBpbnB1dCwgc28geW91IGNhbiBpbnZlcnQgdGhlIGRpYWdyYW0gbGlrZSBzbzoKCmBgYHNoCnNuYWtlbWFrZSAtLWRhZyB8IGRvdCAtR3JhbmtkaXI9IkJUIiAtVHBuZyA+IGRhZ19pbnZlcnRlZC5wbmcKYGBgCgohW2RhZ19pbnZlcnRlZC5wbmddKGltYWdlcy9Nb2R1bGUwN19kYWdfaW52ZXJ0ZWQucG5nKQo8YnIvPgoKIyMjIEFsbCB0YXJnZXQgLyBXaWxkY2FyZHMgLyBWaXN1YWxpemFpbmcgdGhlIHJ1bGVncmFwaAoKVG8gZW1waGFzaXplIHRoZSBlbmRwb2ludCBvZiB0aGUgd29ya2Zsb3cgYW5kIGFsc28gY2xhcmlmeSB0aGUgZGVmYXVsdCB0YXJnZXQsCml0J3MgY29tbW9uIHRvIHBsYWNlIGEgInJ1bGUgYWxsIiBhdCB0aGUgdG9wIG9mIHRoZSBmaWxlLiAoVGhpcyBhbHNvIGFsbG93cyB5b3UKdG8gcmVvcmRlciBydWxlcyB3aXRoaW4gdGhlIGZpbGUgYXJiaXRyYXJpbHkuKSBJdCBpcyB1bnVzdWFsIGluIHRoYXQgaXQgb25seSBoYXMKYW4gaW5wdXQgZGlyZWN0aXZlLgoKYGBgCnJ1bGUgYWxsOgogICAgaW5wdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc2VsZWN0X3dvcmRzLnR4dCIKYGBgCgpTbmFrZW1ha2UgKndpbGRjYXJkcyogbWFrZSBpdCBlYXN5IHRvIHRvIGV4dGVuZCB0aGUgd29ya2Zsb3cgdG8gbmV3IGlucHV0cy4gKEFuZAp0aGV5IHR5cGljYWxseSByZWR1Y2UgcmVwZXRpdGlvbiBvZiBmaWxlIG5hbWVzLikgSGVyZSBpcyBhbiBleGNlcnB0IG9mIHRoZSAKU25ha2VmaWxlIHNob3dpbmcgdGhlIG9yaWdpbmFsICoqc2VsZWN0X3dvcmRzKiogcnVsZSBhbmQgdGhlIHNhbWUgcnVsZSB3aXRoIAp3aWxkY2FyZHMuCgpgYGAKcnVsZSBhbGw6CiAgICBpbnB1dDogInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zZWxlY3Rfd29yZHMudHh0IgoKIyBvcmlnaW5hbCBydWxlCiMgcnVsZSBzZWxlY3Rfd29yZHM6CiMgICAgaW5wdXQ6ICJyZXN1bHQvbGl0dGxlX3dvbWVuX3BhcnRfMS5zb3J0X2NvdW50cy50eHQiCiMgICAgb3V0cHV0OiAicmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNlbGVjdF93b3Jkcy50eHQiCiMgICAgc2hlbGw6ICJlZ3JlcCAnXihKb3xBbXl8TGF1cmllfEJldGgpXHMnIHtpbnB1dH0gPiB7b3V0cHV0fSIKCiMgbm93IHdpdGggd2lsZGNhcmRzCnJ1bGUgc2VsZWN0X3dvcmRzOgogICAgaW5wdXQ6ICJyZXN1bHQve2Jhc2VfbmFtZX0uc29ydF9jb3VudHMudHh0IgogICAgb3V0cHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5zZWxlY3Rfd29yZHMudHh0IgogICAgc2hlbGw6ICJlZ3JlcCAnXihKb3xBbXl8TGF1cmllfEJldGgpXHMnIHtpbnB1dH0gPiB7b3V0cHV0fSIKCmBgYApIb3cgZG9lcyB0aGlzIHdvcms/CgotIFRoZSAqKmFsbCoqIHJ1bGUgbmVlZHMgYW4gaW5wdXQgbmFtZWQgInJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zZWxlY3Rfd29yZHMudHh0IgotIFRoZSAqKnNlbGVjdF93b3JkcyoqIHJ1bGUgY2FuIHByb2R1Y2UgInJlc3VsdHMve2Jhc2VfbmFtZX0uc2VsZWN0X3dvcmRzLnR4dCIgd2hpY2ggd291bGQgbWF0Y2ggdGhlICoqYWxsKiogaW5wdXQgaWYgdGhlIHdpbGRjYXJkICp7YmFzZV9uYW1lfSo9ImxpdHRsZV93b21lbl9wYXJ0XzEiCi0gSW4gKipzZWxlY3Rfd29yZCoqIGlucHV0LCB0aGUgcnVsZSBzdWJzdGl0dXRlcyB0aGUgKntiYXNlX25hbWV9KiB2YWx1ZSBmb3IgaXQncyB3aWxkY2FyZCwgbWFraW5nICJyZXN1bHQvbGl0dGxlX3dvbWVuX3BhcnRfMS5zb3J0X2NvdW50cy50eHQiCi0gU28gdGhlIHRoZSB2YWx1ZSBvZiB0aGUgd2lsZGNhcmQgaXMgZGV0ZXJtaW5lZCBieSB0aGUgZmlsZSBuYW1lIGluIHRoZSBhbGwgcnVsZSBhbmQgdGhhdCB2YWx1ZSBwcm9wYWdhdGVzIGZyb20gb3V0cHV0IHRvIGlucHV0LgoKV2hhdCBoYXBwZW5zIGlmIHdlIHJ1biBzbmFrZW1ha2U/CgpgYGBzaApzbmFrZW1ha2UgLWMxIC0tZHJ5LXJ1bgpgYGAKCj4gYGBgCkJ1aWxkaW5nIERBRyBvZiBqb2JzLi4uCk5vdGhpbmcgdG8gYmUgZG9uZSAoYWxsIHJlcXVlc3RlZCBmaWxlcyBhcmUgcHJlc2VudCBhbmQgdXAgdG8gZGF0ZSkuCiAgYGBgCgpBbmQgdGhhdCBhY3R1YWxseSBtYWtlcyBzZW5zZTogZXZlbiB0aG91Z2ggd2UgdXNlZCB3aWxkY2FyZHMsIHRoZSBydWxlcyBlbmRlZCB1cAp3aXRoIGlkZW50aWNhbCBpbnB1dCBhbmQgb3V0cHV0IHZhbHVlcy4KCldlIGNhbiBleHRlbmQgd2lsZGNhcmRzIGFjcm9zcyByZW1haW5pbmcgcnVsZXMgYXMgYmVsb3cuIE5vdGUgdGhhdCB3ZSBuZWVkIHRvIAprZWVwIHRoZSBsaXRlcmFsIHZhbHVlIGluIHRoZSAqKmFsbCoqIHJ1bGUgc28gdGhlIG90aGVyIHJ1bGVzIGNhbiBkZXJpdmUgdGhlCnZhbHVlIG9mIHRoZSB3aWxkY2FyZC4KCmBgYApydWxlIGFsbDoKICAgIGlucHV0OiAicmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNlbGVjdF93b3Jkcy50eHQiCgpydWxlIHNlbGVjdF93b3JkczoKICAgIGlucHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5zb3J0X2NvdW50cy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LnNlbGVjdF93b3Jkcy50eHQiCiAgICBzaGVsbDogImVncmVwICdeKEpvfEFteXxMYXVyaWV8QmV0aClccycge2lucHV0fSA+IHtvdXRwdXR9IgoKcnVsZSBzb3J0X2NvdW50czoKICAgIGlucHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5jb3VudF93b3Jkcy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LnNvcnRfY291bnRzLnR4dCIKICAgIHNoZWxsOiAiIiIKCXNvcnQgLWsxLDFuciB7aW5wdXR9IFwKCSAgICB8IGF3ayAnQkVHSU4ge3twcmludCAid29yZFx0Y291bnQifX0ge3sgcHJpbnQgJDIgIlx0IiAkMX19JyBcCiAgICAgICAgICAgID4ge291dHB1dH0KICAgICAgICAiIiIKcnVsZSBjb3VudF93b3JkczoKICAgIGlucHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5zcGxpdF93b3Jkcy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LmNvdW50X3dvcmRzLnR4dCIKICAgIHNoZWxsOiAic29ydCB7aW5wdXR9IHwgdW5pcSAtYyA+IHtvdXRwdXR9IgoKcnVsZSBzcGxpdF93b3JkczoKICAgIGlucHV0OiAiaW5wdXRzL3tiYXNlX25hbWV9LnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMve2Jhc2VfbmFtZX0uc3BsaXRfd29yZHMudHh0IgogICAgc2hlbGw6ICJjYXQge2lucHV0fSB8IHRyIC1jcyAnWzphbHBoYTpdJyAnXG4nID4ge291dHB1dH0iCmBgYAoKTm93IHRoYXQgdGhlIFNuYWtlZmlsZSBpcyB1c2luZyB3aWxkY2FyZHMsIGl0IGlzIHRyaXZpYWwgdG8gZXh0ZW5kIGl0IHRvIG5ldyAKaW5wdXRzLiBXZSBhZGp1c3QgdGhlICoqYWxsKiogcnVsZSBhZGRpbmcgYSBzZWNvbmQgdmFsdWUsIHNlcGFyYXRlZCBieSBhIGNvbW1hOgoKYGBgCnJ1bGUgYWxsOgogICAgaW5wdXQ6ICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc2VsZWN0X3dvcmRzLnR4dCIsICJyZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzIuc2VsZWN0X3dvcmRzLnR4dCIKLi4uCmBgYAoKVGhlIGFkZGl0aW9uIG9mIGEgbmV3IHdpbGRjYXJkIHZhbHVlIChsaXR0bGVfd29tZW5fcGFydF8yKSB3aWxsIHRyaWdnZXIgYWxsIGZpdmUKcnVsZXMuCgpgYGBzaApzbmFrZW1ha2UgLS1kcnktcnVuCmBgYAoKPiBgYGAKLi4uCkpvYiBzdGF0czoKam9iICAgICAgICAgICAgIGNvdW50Ci0tLS0tLS0tLS0tLSAgLS0tLS0tLQphbGwgICAgICAgICAgICAgICAgIDEKY291bnRfd29yZHMgICAgICAgICAxCnNlbGVjdF93b3JkcyAgICAgICAgMQpzb3J0X2NvdW50cyAgICAgICAgIDEKc3BsaXRfd29yZHMgICAgICAgICAxCnRvdGFsICAgICAgICAgICAgICAgNQouLi4KICBgYGAKCkJlZm9yZSB3ZSBydW4gdGhpcyBmb3IgcmVhbCwgbGV0J3MgYWRkIGEgbmV3IGZsYWcgYC0tZm9yY2VhbGxgIHRvIHRlbGwgc25ha2VtYWtlCnRvIHRyaWdnZXIgZXZlcnkgc2luZ2xlIHJ1bGUgZm9yIGFsbCBwb3NzaWJsZSBpbnB1dHMuIEJhc2ljYWxseSBhY3QgbGlrZSB0aGVyZSAKYXJlIG5vIGV4aXN0aW5nIG91dHB1dHMgYW5kIGl0IG5lZWRzIHRvIHN0YXJ0IGZyb20gc2NyYXRjaC4gQnV0IHRoaXMgaXMgc3RpbGwKYSBkcnktcnVuLCBzbyBubyBmaWxlcyB3aWxsIGJlIGNoYW5nZWQuCgpgYGBzaApzbmFrZW1ha2UgLS1kcnktcnVuIC0tZm9yY2VhbGwKYGBgCgo+IGBgYAouLi4KSm9iIHN0YXRzOgpqb2IgICAgICAgICAgICAgY291bnQKLS0tLS0tLS0tLS0tICAtLS0tLS0tCmFsbCAgICAgICAgICAgICAgICAgMQpjb3VudF93b3JkcyAgICAgICAgIDIKc2VsZWN0X3dvcmRzICAgICAgICAyCnNvcnRfY291bnRzICAgICAgICAgMgpzcGxpdF93b3JkcyAgICAgICAgIDIKdG90YWwgICAgICAgICAgICAgICA5Ci4uLgogIGBgYAoKVHJ5IGdlbmVyYXRpbmcgYSBEQUcgdmlzdWFsaXphdGlvbi4gKE5vdyBpdCdzIHRydWx5IGxvb2tpbmcgbW9yZSBsaWtlIGEgCmdyYXBoLikgTm90ZToKCi0gRWFjaCB3aWxkY2FyZCB2YWx1ZSBnZXRzIGl0J3Mgb3duIHBhdGgKLSBUaGUgd2lsZGNhcmQgdmFsdWVzIGFyZSBkaXNwbGF5ZWQgYXQgdGhlIGlucHV0Ci0gU29tZSBib3ggb3V0bGluZXMgYXJlIHNvbGlkIGFuZCBvdGhlciBhcmUgZG90dGVkOyB3aHkgaXMgdGhhdD8KCmBgYHNoCnNuYWtlbWFrZSAtLWRhZyB8IGRvdCAtR3JhbmtkaXI9IkJUIiAtVHBuZyA+IGRhZ19pbnZlcnRlZF93aWxkY2FyZHMucG5nCmBgYAoKIVtkYWdfaW52ZXJ0ZWRfd2lsZGNhcmRzLnBuZ10oaW1hZ2VzL01vZHVsZTA3X2RhZ19pbnZlcnRlZF93aWxkY2FyZHMucG5nKQo8YnIvPgoKV2l0aCBjb21wbGV4IHdvcmtmbG93cywgeW91IHNvbWV0aW1lcyBkb24ndCB3YW50IHRvIHNlZSBhbGwgdGhlIHBvc3NpYmxlIGlucHV0cyAKYW5kIHdvdWxkIHJhdGhlciBqdXN0IHNlZSB0aGUgYWJzdHJhY3QgcmVsYXRpb25zaGlwIG9mIHRoZSBydWxlcy4gU25ha2VtYWtlCnN1cHBvcnRzIHRoaXMgd2l0aCBhIHJlbGF0ZWQgZmxhZyBgLS1ydWxlZ3JhcGhgCgpgYGBzaApzbmFrZW1ha2UgLS1ydWxlZ3JhcGggfCBkb3QgLUdyYW5rZGlyPSJCVCIgLVRwbmcgPiBydWxlZ3JhcGgucG5nCmBgYAoKIVtydWxlZ3JhcGgucG5nXShpbWFnZXMvTW9kdWxlMDdfcnVsZWdyYXBoLnBuZykKPGJyLz4KCgpPay4gR28gYWhlYWQgYW5kIHJ1biBgc25ha2VtYWtlYCB1bnRpbCB5b3UgYXJlIDEwMCUgY29tcGxldGUgYW5kIHNlZSB0aGVzZSBmaWxlcyBpbiAKcmVzdWx0czoKCj4gYGBgCiQgbHMgcmVzdWx0cwpsaXR0bGVfd29tZW5fcGFydF8xLmNvdW50X3dvcmRzLnR4dCAgIGxpdHRsZV93b21lbl9wYXJ0XzIuY291bnRfd29yZHMudHh0CmxpdHRsZV93b21lbl9wYXJ0XzEuc2VsZWN0X3dvcmRzLnR4dCAgbGl0dGxlX3dvbWVuX3BhcnRfMi5zZWxlY3Rfd29yZHMudHh0CmxpdHRsZV93b21lbl9wYXJ0XzEuc29ydF9jb3VudHMudHh0ICAgbGl0dGxlX3dvbWVuX3BhcnRfMi5zb3J0X2NvdW50cy50eHQKbGl0dGxlX3dvbWVuX3BhcnRfMS5zcGxpdF93b3Jkcy50eHQgICBsaXR0bGVfd29tZW5fcGFydF8yLnNwbGl0X3dvcmRzLnR4dAogIGBgYAoKCiMjIyBUaGUgZXhwYW5kIGZ1bmN0aW9uIC8gY29uZmlnIGZpbGVzIC8gcGFyYW1ldGVycwoKSWRlYWxseSwgYSB3b3JrZmxvdyBjb3VsZCBiZSBydW4gb24gYSBuZXcgc2V0IG9mIGlucHV0cyB3aXRob3V0IGNoYW5naW5nIHRoZQpTbmFrZWZpbGUgYXQgYWxsLiBGb3IgZXhhbXBsZSwgd2Ugc2hvdWxkIGJlIGFibGUgdG8gdXNlIG91ciB1bm1vZGlmaWVkIFNuYWtlZmlsZSAKd2l0aCB0aGUgaW5wdXQgb2Ygb2YgU2hha2VzcGVhcmUncyBbUm9tZW8gYW5kCkp1bGlldF0oaHR0cHM6Ly93d3cuZ3V0ZW5iZXJnLm9yZy9lYm9va3MvMTUxMykgdG8gc2VlIHdoZXRoZXIgTW9udGFndWUgaXMKbWVudGlvbmVkIG1vcmUgdGhhbiBDYXB1bGV0LiBNb3N0IG9mIHRoZSBydWxlcyBpbiB0aGUgd29ya2Zsb3cgYXJlIGFscmVhZHkgCmdlbmVyYWwgZW5vdWdoOyBidXQgdGhlcmUgYXJlIHR3byBleGNlcHRpb25zOgoKLSBUaGUgKiphbGwqKiBydWxlIG1lbnRpb25zIHNwZWNpZmljIHRleHRzLgotIFRoZSAqKnNlbGVjdF93b3JkcyoqIHJ1bGUgbWVudGlvbnMgc3BlY2lmaWMgbmFtZXMuCgpXZSBjYW4gZXh0cmFjdCB0aGVzZSBzcGVjaWZpY3MgZnJvbSB0aGUgU25ha2VmaWxlIHVzaW5nIGEgCipjb25maWd1cmF0aW9uIGZpbGUqLiBXZSdsbCBzdGFydCB3aXRoIHRoZSAqKnNlbGVjdF93b3JkcyoqIHJ1bGUuCgoKYGBgCnJ1bGUgc2VsZWN0X3dvcmRzOgogICAgaW5wdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LnNvcnRfY291bnRzLnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMve2Jhc2VfbmFtZX0uc2VsZWN0X3dvcmRzLnR4dCIKICAgIHNoZWxsOiAiZWdyZXAgJ14oSm98QW15fExhdXJpZXxCZXRoKVxzJyB7aW5wdXR9ID4ge291dHB1dH0iCmBgYAoKRmlyc3RseSwgd2UnbGwgcHVsbCB0aGUgcmVndWxhciBleHByZXNzaW9uIG91dCBvZiB0aGUgc2hlbGwgZGlyZWN0aXZlIGludG8gYQpbY29uZmlndXJhdGlvbgpmaWxlXShodHRwczovL3NuYWtlbWFrZS5yZWFkdGhlZG9jcy5pby9lbi9zdGFibGUvc25ha2VmaWxlcy9jb25maWd1cmF0aW9uLmh0bWwjY29uZmlndXJhdGlvbil7dGFyZ2V0PSJfYmxhbmsifS4KU25ha2VtYWtlIHN1cHBvcnRzIHNldmVyYWwgc3R5bGVzIG9mIGNvbmZpZ3VyYXRpb24gZmlsZTsgd2UnbGwgdXNlIHRoZSBZQU1MCnN5bnRheC4gQnkgY29udmVudGlvbiB3ZSdsbCBjcmVhdGUgY29uZmlnL2NvbmZpZy55YW1sOyB5b3UgY291bGQgY3JlYXRlIGFuZCBlZGl0CnRoaXMgZmlsZSBtYW51YWxseTsgYWx0ZXJuYXRpdmVseSB5b3UgY2FuIGFsc28gZXhlY3V0ZSB0aGUgbGluZSBiZWxvdzoKCmBgYHNoCm1rZGlyIGNvbmZpZwplY2hvICdzZWxlY3Rfd29yZHNfcmVnZXg6IF4oSm98QW15fExhdXJpZXxCZXRoKVxzJyA+IGNvbmZpZy9jb25maWcueWFtbApjYXQgY29uZmlnL2NvbmZpZy55YW1sCmBgYAoKTm93IGFkZCB0aGlzIGxpbmUgYWJvdmUgdGhlICoqYWxsKiogcnVsZToKCmBgYApjb25maWdmaWxlOiAiY29uZmlnL2NvbmZpZy55YW1sIgpgYGAKCkFkZCBhIHBhcmFtZXRlciBpbml0aWFsaXplZCB0byB0aGUgY29uZmlnIGtleSAqc2VsZWN0X3dvcmRzX3JlZ2V4Ki4gVGhlbiBhZGp1c3QKdGhlIHNlbGVjdF93b3JkcyBydWxlIHRvIHJlZmVybmNlIHRoYXQgcGFyYW1ldGVyLgoKYGBgCnJ1bGUgc2VsZWN0X3dvcmRzOgogICAgaW5wdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LnNvcnRfY291bnRzLnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMve2Jhc2VfbmFtZX0uc2VsZWN0X3dvcmRzLnR4dCIKICAgIHBhcmFtczogcmVnZXg9Y29uZmlnW3NlbGVjdF93b3Jkc19yZWdleF0KICAgIHNoZWxsOiAiZWdyZXAgJ3twYXJhbXMucmVnZXh9JyB7aW5wdXR9ID4ge291dHB1dH0iCmBgYAoKVGhlIFtwYXJhbXMKZGlyZWN0aXZlXShodHRwczovL3NuYWtlbWFrZS5yZWFkdGhlZG9jcy5pby9lbi9zdGFibGUvc25ha2VmaWxlcy9ydWxlcy5odG1sI25vbi1maWxlLXBhcmFtZXRlcnMtZm9yLXJ1bGVzKXt0YXJnZXQ9Il9ibGFuayJ9CmlzIGEgd2F5IG9mIGFkanVzdGluZyB0aGUgc2hlbGwgY29tbWFuZCB3aXRoIGEgdmFsdWUgdGhhdCBpc24ndCB0aWVkIHRvIHRoZQpmaWxlc3lzdGVtLiBBbHNvIG5vdGUgdGhhdCBwYXJhbXMgY2FuIGJlIG5hbWVkIChlLmcuIGB4PSJ2YWx1ZSJgKSBhbmQgcmVmZXJlbmNlZApieSBuYW1lIGluIHRoZSBzaGVsbCAoYHtwYXJhbXMueH1gKS4KCk5vdyB0aGF0IHdlJ3ZlIG1hZGUgdGhpcyBjaGFuZ2UsIGxldCdzIGRyeS1ydW4gc25ha2VtYWtlLiBOb3RlIHdlJ3JlIGFkZGluZyB0aGUKYC1wYCBmbGFnIHNvIHNuYWtlbWFrZSB3aWxsIHByaW50IG91dCB0aGUgYWN0dWFsIGNvbW1hbmRzIGl0IHdpbGwgZXhlY3V0ZToKCmBgYHNoCnNuYWtlbWFrZSAtLWRyeS1ydW4gLXAKYGBgCgpJbiB0aGUgb3V0cHV0LCBub3RlOgoKLSBJbiBKb2Igc3RhdHMsIHdlIHNlZSBzbmFrZW1ha2UgYmVsaWV2ZXMgd2UgbmVlZCB0byByZXJ1biAqKnNlbGVjdF93b3JkcyoqIGZvciBib3RoIGlucHV0cy4KLSBJbiB0aGUgcnVsZSBkZXRhaWxzLCB0aGUgcmVhc29uIHRoZSBydWxlIGlzIHRyaWdnZXJlZCBpcyB0aGF0IGNvZGUgYW5kIHBhcmFtcyBoYXZlIGNoYW5nZWQKLSBCZWNhdXNlIHdlIHVzZWQgYC1wYCwgeW91IGNhbiBzZWUgdGhlIGFjdHVhbCBlZ3JlcCBzdGF0ZW1lbnQgKHdoaWNoIGxvb2tzIGZpbmUpLgoKPiBgYGAKQnVpbGRpbmcgREFHIG9mIGpvYnMuLi4KSm9iIHN0YXRzOgpqb2IgICAgICAgICAgICAgY291bnQKLS0tLS0tLS0tLS0tICAtLS0tLS0tCmFsbCAgICAgICAgICAgICAgICAgMQpzZWxlY3Rfd29yZHMgICAgICAgIDIKdG90YWwgICAgICAgICAgICAgICAzCi4KW01vbiBKdW4gMTAgMjM6MDE6MjEgMjAyNF0KcnVsZSBzZWxlY3Rfd29yZHM6CiAgICBpbnB1dDogcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNvcnRfY291bnRzLnR4dAogICAgb3V0cHV0OiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzEuc2VsZWN0X3dvcmRzLnR4dAogICAgam9iaWQ6IDEKICAgIHJlYXNvbjogQ29kZSBoYXMgY2hhbmdlZCBzaW5jZSBsYXN0IGV4ZWN1dGlvbjsgUGFyYW1zIGhhdmUgY2hhbmdlZCBzaW5jZSBsYXN0IGV4ZWN1dGlvbgogICAgd2lsZGNhcmRzOiBiYXNlX25hbWU9bGl0dGxlX3dvbWVuX3BhcnRfMQogICAgcmVzb3VyY2VzOiB0bXBkaXI9L3RtcAouCmVncmVwICdeKEpvfEFteXxMYXVyaWV8QmV0aClccycgcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNvcnRfY291bnRzLnR4dCA+IHJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMS5zZWxlY3Rfd29yZHMudHh0Ci4KW01vbiBKdW4gMTAgMjM6MDE6MjEgMjAyNF0KcnVsZSBzZWxlY3Rfd29yZHM6CiAgICBpbnB1dDogcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8yLnNvcnRfY291bnRzLnR4dAogICAgb3V0cHV0OiByZXN1bHRzL2xpdHRsZV93b21lbl9wYXJ0XzIuc2VsZWN0X3dvcmRzLnR4dAogICAgam9iaWQ6IDUKICAgIHJlYXNvbjogQ29kZSBoYXMgY2hhbmdlZCBzaW5jZSBsYXN0IGV4ZWN1dGlvbjsgUGFyYW1zIGhhdmUgY2hhbmdlZCBzaW5jZSBsYXN0IGV4ZWN1dGlvbgogICAgd2lsZGNhcmRzOiBiYXNlX25hbWU9bGl0dGxlX3dvbWVuX3BhcnRfMgogICAgcmVzb3VyY2VzOiB0bXBkaXI9L3RtcAouCmVncmVwICdeKEpvfEFteXxMYXVyaWV8QmV0aClccycgcmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8yLnNvcnRfY291bnRzLnR4dCA+IHJlc3VsdHMvbGl0dGxlX3dvbWVuX3BhcnRfMi5zZWxlY3Rfd29yZHMudHh0Ci4uLgogIGBgYAogIApHbyBhaGVhZCBhbmQgcnVuIHNuYWtlbWFrZSB1bnRpbCAxMDAlIGRvbmUuCgpOb3cgd2UnbGwgcHVsbCB0aGUgZmlsZSBiYXNlIG5hbWVzIGZyb20gdGhlICoqYWxsKiogcnVsZSBpbnRvIHRoZSBjb25maWcgZmlsZToKCmBgYApzZWxlY3Rfd29yZHNfcmVnZXg6IF4oSm98QW15fExhdXJpZXxCZXRoKVxzCgppbnB1dF9iYXNlX2ZpbGVuYW1lczoKICAtIGxpdHRsZV93b21lbl9wYXJ0XzEKICAtIGxpdHRsZV93b21lbl9wYXJ0XzIKYGBgCgpUbyBnZXQgdGhlc2UgdmFsdWVzIGZyb20gY29uZmlnLnlhbWwgaW50byB0aGUKKiphbGwqKiBydWxlLCBJIG5lZWQgdG8gaW50cm9kdWNlIHRoZSBgZXhwYW5kYCBmdW5jdGlvbi4KW2V4cGFuZF0oaHR0cHM6Ly9zbmFrZW1ha2UucmVhZHRoZWRvY3MuaW8vZW4vc3RhYmxlL3NuYWtlZmlsZXMvcnVsZXMuaHRtbCN0aGUtZXhwYW5kLWZ1bmN0aW9uKXt0YXJnZXQ9Il9ibGFuayJ9CmlzIGEgaGVscGVyIGZ1bmN0aW9uIHRoYXQgY29tZXMgd2l0aCBTbmFrZW1ha2UuIEl0J3MgZWFzaWVyIHRvIGV4cGxhaW4gaWYgd2UKc2hvdyBhIGZldyBleGFtcGxlcyBvZiBpbnB1dHMgYW5kIG91dHB1dHM6Cgo+IGBgYApleHBhbmQoIntkYXRhc2V0fS9hLnR4dCIsIGRhdGFzZXQ9WzEsIDJdKQpbJzEvYS50eHQnLCAnMi9hLnR4dCcsICczL2EudHh0J10KLgpleHBhbmQoInthfS57Yn0iLCBhPVsnZmlnMScsICdmaWcyJywgJ2ZpZzMnXSwgYj1bJ3BuZycsICdwZGYnXSkKWydmaWcxLnBuZycsICdmaWcxLnBkZicsICdmaWcyLnBuZycsICdmaWcyLnBkZicsICdmaWczLnBuZycsICdmaWczLnBkZiddCi4KZXhwYW5kKCJyZXN1bHRzL3t4fS5zZWxlY3Rfd29yZHMudHh0IiwgXAouLi4gICAgICAgICB4PVsibGl0dGxlX3dvbWVuX3BhcnRfMSIsICJsaXR0bGVfd29tZW5fcGFydF8yIl0pClsncmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8xLnNlbGVjdF93b3Jkcy50eHQnLCAncmVzdWx0cy9saXR0bGVfd29tZW5fcGFydF8yLnNlbGVjdF93b3Jkcy50eHQnXQogIGBgYAoKYGV4cGFuZGAgYWNjZXB0cyBhIHN0cmluZyBwYXR0ZXJuIHdpdGggbmFtZWQgcGxhY2Vob2xkZXJzIChlLmcuIHtkYXRhc2V0fSkgYW5kCm5hbWVkIGxpc3RzIChlLmcuIGRhdGFzZXQ9WzEsIDJdKTsgaXQgYnVpbGRzIGEgbGlzdCBvZiBzdHJpbmdzIGJ5IGluc2VydGluZyB0aGUKbGlzdCB2YWx1ZXMgaW50byB0aGUgcGF0dGVybi4KClRoYXQgbGFzdCBleGFtcGxlIGFib3ZlIGlzIGV4YWN0bHkgd2hhdCB3ZSBuZWVkIGluIHRoZSAqKmFsbCoqIHJ1bGUuIFRoZSBmaW5hbCAKc3RlcCBpcyB0byBhZGFwdCB0aGUgZXhhbXBsZSBhYm92ZSB0byB1c2UgdGhlIGNvbmZpZyBmaWxlLCBsaWtlIHNvOgoKYGBgCnJ1bGUgYWxsOgogICAgaW5wdXQ6IGV4cGFuZCgicmVzdWx0cy97eH0uc2VsZWN0X3dvcmRzLnR4dCIsIFwKICAgICAgICB4PWNvbmZpZ1snaW5wdXRfYmFzZV9maWxlbmFtZXMnXSkKYGBgCgpUaGUgU25ha2VmaWxlIGlzIG5vdyBmcmVlIG9mIGFueSByZWZlcmVuY2VzIHRvIHNwZWNpZmljIGlucHV0cy4gVGhlIHdvcmtmbG93CmNhbiBiZSBydW4gb24gYSBkaWZmZXJlbnQgc2V0IG9mIGlucHV0cyBieSB1c2luZyBhIG5ldyBjb25maWcueWFtbC4KCmBgYApjb25maWdmaWxlOiAiY29uZmlnL2NvbmZpZy55YW1sIgoKcnVsZSBhbGw6CiAgICBpbnB1dDogZXhwYW5kKCJyZXN1bHRzL3t4fS5zZWxlY3Rfd29yZHMudHh0IiwgXAogICAgeD1jb25maWdbJ2lucHV0X2Jhc2VfZmlsZW5hbWVzJ10pCgoKcnVsZSBzZWxlY3Rfd29yZHM6CiAgICBpbnB1dDogInJlc3VsdHMve2Jhc2VfbmFtZX0uc29ydF9jb3VudHMudHh0IgogICAgb3V0cHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5zZWxlY3Rfd29yZHMudHh0IgogICAgcGFyYW1zOiByZWdleD1jb25maWdbInNlbGVjdF93b3Jkc19yZWdleCJdCiAgICBzaGVsbDogImVncmVwICd7cGFyYW1zLnJlZ2V4fScge2lucHV0fSA+IHtvdXRwdXR9IgoKcnVsZSBzb3J0X2NvdW50czoKICAgIGlucHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5jb3VudF93b3Jkcy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LnNvcnRfY291bnRzLnR4dCIKICAgIHNoZWxsOiAiIiIKCXNvcnQgLWsxLDFuciB7aW5wdXR9IFwKCSAgICB8IGF3ayAnQkVHSU4ge3twcmludCAid29yZFx0Y291bnQifX0ge3sgcHJpbnQgJDIgIlx0IiAkMX19JyBcCiAgICAgICAgICAgID4ge291dHB1dH0KICAgICAgICAiIiIKcnVsZSBjb3VudF93b3JkczoKICAgIGlucHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5zcGxpdF93b3Jkcy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LmNvdW50X3dvcmRzLnR4dCIKICAgIHNoZWxsOiAic29ydCB7aW5wdXR9IHwgdW5pcSAtYyA+IHtvdXRwdXR9IgoKcnVsZSBzcGxpdF93b3JkczoKICAgIGlucHV0OiAiaW5wdXRzL3tiYXNlX25hbWV9LnR4dCIKICAgIG91dHB1dDogInJlc3VsdHMve2Jhc2VfbmFtZX0uc3BsaXRfd29yZHMudHh0IgogICAgc2hlbGw6ICJjYXQge2lucHV0fSB8IHRyIC1jcyAnWzphbHBoYTpdJyAnXG4nID4ge291dHB1dH0iCgpgYGAKCi0tLQoKCiMjIyBTbmFrZW1ha2Ugb24gR3JlYXQgTGFrZXMKCgpBbGwgdGhpcyB0aW1lIHdlJ3ZlIGJlZW4gZXhlY3V0aW5nIHRoZSB3b3JrZmxvdyBvbiB0aGUgbG9naW4tbm9kZXMuIEl0J3MKZ2VuZXJhbGx5IGZpbmUgdG8gZXhlY3V0ZSB0aGUgYHNuYWtlbWFrZWAgcHJvY2VzcyBpdHNlbGYgb24gdGhlIGxvZ2luLW5vZGUsIGJ1dApmb3IgYSBtb3JlIGRlbWFuZGluZyB3b3JrZmxvdyB5b3Ugc2hvdWxkIGV4ZWN1dGUgdGhlIHJ1bGVzIG9uIEdyZWF0IExha2VzIHdvcmtlcgpub2Rlcy4KCkl0J3Mgc3RyYWlnaHRmb3J3YXJkIHRvIGRvIHRoaXMgYnkgdXNpbmcgYSBwcm9maWxlLiBBIHByb2ZpbGUgaXMgZXNzZW50aWFsbHkKanVzdCBhIGxpdHRsZSBjb25maWcgZmlsZSB0aGF0IHRlbGxzIGBzbmFrZW1ha2VgIGhvdyBpdCBzaG91bGQgc3VibWl0IHJ1bGVzIGFzCmpvYnMuIENoZWNrIG91dCBzbmFrZW1ha2UgZG9jcyBmb3IgZGV0YWlscyBvbgpbcHJvZmlsZV0oaHR0cHM6Ly9zbmFrZW1ha2UucmVhZHRoZWRvY3MuaW8vZW4vc3RhYmxlL2V4ZWN1dGluZy9jbGkuaHRtbCNwcm9maWxlcyl7dGFyZ2V0PSJfYmxhbmsifQpvcHRpb25zLiBXZSdsbCBzdGFydCB3aXRoIGEgc2ltcGxlIHByb2ZpbGUgYnVpbHQgZm9yIEdyZWF0IExha2VzOgoKYGBgc2gKY3AgLXIgL25mcy90dXJiby91bW1zLWJpb2luZi13a3NocC93b3Jrc2hvcC9zaGFyZWQtZGF0YS9wcm9maWxlLyBjb25maWcvCmBgYAo8ZGl2IGNsYXNzPSJ3cmFwcGVyIj4KPHRhYmxlIGNsYXNzPSdmaWcnPjx0cj48dGggY2xhc3M9J2ZpZycgc3R5bGU9InRleHQtYWxpZ246IGxlZnQiPmNvbmZpZy9wcm9maWxlL2NvbmZpZy55YW1sPC90aD48L3RyPgo8dHI+PHRkIGNsYXNzPSdmaWcnPjxwcmU+CnByaW50c2hlbGxjbWRzOiBUcnVlCmtlZXAtZ29pbmc6IFRydWUKdXNlLXNpbmd1bGFyaXR5OiBUcnVlCnNpbmd1bGFyaXR5LWFyZ3M6ICItLWNsZWFuZW52IC1CICQocHdkKSIKY2x1c3Rlcjogc2JhdGNoIC0tam9iLW5hbWU9YWxjb3R0X3tydWxlfV97d2lsZGNhcmRzfSAtLWFjY291bnQ9YmlvaW5mX3drc2hwX2NsYXNzIC0tcGFydGl0aW9uPXN0YW5kYXJkIC0tbm9kZXM9MSAtLWNwdXMtcGVyLXRhc2s9e3Jlc291cmNlcy5jcHVzfSAtLW1lbT17cmVzb3VyY2VzLm1lbV9tYn0gLS10aW1lPXtyZXNvdXJjZXMudGltZV9taW59IC0tb3V0cHV0PWNsdXN0ZXJfbG9ncy8leC0lai5sb2cgLS1wYXJzYWJsZQpkZWZhdWx0LXJlc291cmNlczogW2NwdXM9MSwgbWVtX21iPTIwMDAsIHRpbWVfbWluPTEyMF0KcmVydW4tdHJpZ2dlcnM6IFttdGltZV0KbGF0ZW5jeS13YWl0OiAzMDAKam9iczogNQo8L3ByZT48L3RkPjwvdHI+PC90YWJsZT4KPC9kaXY+Cjxici8+CgpJbnZva2luZyBgc25ha2VtYWtlYCB3aXRoIHRoaXMgcHJvZmlsZSBzdWJtaXRzIGVhY2ggcnVsZSBhIGFuIHNiYXRjaCBqb2I6CgpgYGBzaAojIGNsZWFyIG91dCBleGlzdGluZyBmaWxlcyBzbyB0aGVyZSdzIHNvbWV0aGluZyB0byBkbwpybSByZXN1bHRzLyoKCnNuYWtlbWFrZSAtLXByb2ZpbGUgY29uZmlnL3Byb2ZpbGUKYGBgCgpOb3RlIHRoYXQgeW91IGNhbiBhZGp1c3QgdGhlIFNMVVJNIGpvYiBnZW9tZXRyeSBieSBhZGRpbmcgYSAqcmVzb3VyY2VzKiBkaXJlY3RpdmUKdG8gYSBydWxlLiBUaGVzZSB2YWx1ZXMgd2lsbCBiZSBpbmNvcnBvcmF0ZWQgaW50byB0aGUgc2JhdGNoIHJlcXVlc3QgZm9yIHRoaXMKcnVsZS4KCmBgYApydWxlIHNlbGVjdF93b3JkczoKICAgIGlucHV0OiAicmVzdWx0cy97YmFzZV9uYW1lfS5zb3J0X2NvdW50cy50eHQiCiAgICBvdXRwdXQ6ICJyZXN1bHRzL3tiYXNlX25hbWV9LnNlbGVjdF93b3Jkcy50eHQiCiAgICByZXNvdXJjZXM6IGNwdXM9NCwgbWVtX21iPTQwMDAsIHRpbWVfbWluPTUgIAogICAgcGFyYW1zOiByZWdleD1jb25maWdbInNlbGVjdF93b3Jkc19yZWdleCJdCiAgICBzaGVsbDogImVncmVwICd7cGFyYW1zLnJlZ2V4fScge2lucHV0fSA+IHtvdXRwdXR9ICMtLWZha2UtbnVtLXRocmVhZHM9e3Jlc291cmNlcy5jcHVzfSIKYGBgCjxici8+CgojIyMgVXNpbmcgdG11eAoKW3RtdXhdKGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL1RtdXgpIGlzIGEgdW5peCB1dGlsaXR5IHRoYXQgbGV0J3MgeW91IApjcmVhdGUgdmlydHVhbCBzY3JlZW5zIGluc2lkZSBvZiBhIHNpbmdsZSBzZXNzaW9uLiAoSXQncyBsaWtlIHRoZSBVbml4IHV0aWxpdHkgCipzY3JlZW4qLCBidXQgbW9yZSBwb3dlcmZ1bCBhbmQgY3VycmVudC4pIEZvciBvdXIgcHVycG9zZXMsIHRoZSBraWxsZXIgZmVhdHVyZSAKb2YgdG11eCBpcyBjYXB0dXJlZCBpbiB0aGlzIHNpbXBsZSB2aWduZXR0ZToKCi0gbG9naW4gdG8gZ3JlYXQgbGFrZXMgYW5kIHN0YXJ0IGEgdG11eCBzZXNzaW9uIG9uIHRoZSBsb2dpbi1ub2RlOiBgdG11eGAKLSBzdGFydCBhIGxvbmcgcnVubmluZyB0YXNrIChlLmcuIGEgc25ha2VtYWtlIHdvcmtmbG93IHdoaWNoIHN1Ym1pdHMgdG8gdGhlIGNsdXN0ZXIpCi0gZGV0YWNoIGZyb20gdGhhdCB0bXV4IHNlc3Npb246IGhpdCBgY3RybC1iYCBgZGAKLSB0dXJuIG9mZiB5b3VyIGNvbXB1dGVyLCBnbyBob21lLCBhbmQgbG9naW4gdG8gZ3JlYXQgbGFrZXMgZnJvbSBkaWZmZXJlbnQgY29tcHV0ZXIKLSByZWF0dGFjaCB0byB0aGF0IHRtdXggc2Vzc2lvbjogYHRtdXggYXR0YWNoYAotIHlvdSB3aWxsIHNlZSB5b3VyIGxvbmctcnVubmluZyB0YXNrIGhhcyBiZWVuIHJ1bm5pbmcgYWxsIGFsb25nLCBjb21wbGV0ZWx5IHVuYWZmZWN0ZWQgYnkgeW91ciB3YW5kZXJpbmdzCgpBbmQgaW4gZmFjdCwgdG11eCBjYW4gYWN0dWFsbHkgZG8gbXVjaCwgbXVjaCBtb3JlLiBIZXJlJ3MgYSBuaWZ0eSBbY2hlYXRzaGVldF0oaHR0cHM6Ly9vcGVuc291cmNlLmNvbS9zaXRlcy9kZWZhdWx0L2ZpbGVzL2dhdGVkLWNvbnRlbnQvb3NkY19jaGVhdHNoZWV0LXRtdXgtMjAyMS42LjI1LnBkZil7dGFyZ2V0PSJfYmxhbmsifS4KCklmIHlvdSBzdGFydCB1c2luZyB0bXV4LCBrZWVwIGluIG1pbmQgdGhhdCB3aGVuIHlvdSBsb2dpbiBpbnRvCmBncmVhdGxha2VzLmFyYy10cy51bWljaC5lZHVgIGl0J3MgYWN0dWFsbHkgZm9yd2FyZGluZyB5b3Ugb24gdG8gb25lIG9mICp0aHJlZSoKbG9naW4gbm9kZXM6IGdsLWxvZ2luMSwgZ2wtbG9naW4yLCBvciBnbC1sb2dpbjMuIChXaGljaCBvbmUgeW91IGdldCBpcyBiYXNpY2FsbHkKcmFuZG9tLikgVGhpcyBtZWFucyBpZiB5b3Ugd2FudCB0byByZWNvbm5lY3QgdG8gYSBkZXRhY2hlZCB0bXV4IHNlc3Npb24sIHlvdSAKaGF2ZSB0byBnZXQgdG8gdGhlIHJpZ2h0IGxvZ2luIG5vZGUuCgpTbywgaWYgeW91IHN0YXJ0IGEgdG11eCBzZXNzaW9uLCBub3RlIHdoaWNoIGxvZ2luIG5vZGUgeW91IGFyZSB1c2luZyB1c2luZyB0aGUgCmBob3N0bmFtZWAgY29tbWFuZDoKCj4gYGBgCiQgaG9zdG5hbWUKZ2wtbG9naW4yLmFyYy10cy51bWljaC5lZHUKICBgYGAKClRoZW4gd2hlbiB5b3UgbmVlZCB0byBsYXRlciByZWNvbm5lY3QgdG8gdGhhdCBzZXNzaW9uLCBpbnN0ZWFkIG9mIGBzc2gKZ3JlYXRsYWtlcy5hcmMtdHMudW1pY2guZWR1YCB5b3UgY29ubmVjdCB0byB0aGUgc3BlY2lmaWMgaG9zdG5hbWUgeW91IGdvdCBhYm92ZToKCj4gYGBgCnNzaCBnbC1sb2dpbjIuYXJjLXRzLnVtaWNoLmVkdQojIGxvZ2luICsgRHVvCiMgbG9naW4gYmFubmVyIChibGFoIGJsYWggYmxhaCkKW2NnYXRlc0BnbC1sb2dpbjIgfl0kIHRtdXggbHMgIyBsaXN0IGFjdGl2ZSB0bXV4IHNlc3Npb25zCjA6IDEgd2luZG93cyAoY3JlYXRlZCBUdWUgSnVuIDExIDIwOjMxOjAzIDIwMjQpIFsxMzN4MjVdCltjZ2F0ZXNAZ2wtbG9naW4yIH5dJCB0bXV4IGF0dGFjaCAjIGF0dGFjaCB0byBteSBzZXNzaW9uCiAgYGBgCgpWb2lsYS4gWW91IGFyZSBiYWNrIGluIHlvdXIgdG11eCBzZXNzaW9uLCByaWdodCB3aGVyZSB5b3UgbGVmdCBpdC4KCkxhc3RseSwgdGhlIHNjcm9sbGJhciBpbiB0aGUgdGVybWluYWwvY29tbWFuZCB3aW5kb3cgZG9lcyBub3Qgd29yayBpbiB0bXV4LiBJZgp5b3UgbmVlZCB0byBzY3JvbGwgaW4gdG11eCwgeW91IGNhbiBoaXQgYGN0cmxgLWBgYmAgYW5kIHRoZW4gYFtgIHRvIGVudGVyIHNjcm9sbAptb2RlLiBZb3UgY2FuIHRoZW4gdXNlIGN1cnNvciBvciBgcGFnZS11cGAsIGBwYWdlLWRvd25gIHRvIG1vdmUgYXJvdW5kIHRoZSB0bXV4CnNjcm9sbC1idWZmZXIuIEhpdCBgcWAgdG8gcXVpdCBzY29sbCBtb2RlIGFuZCByZXR1cm4gdG8gdGhlIGN1cnJlbnQgcHJvbXB0LgoKLS0tCgojIyBQcm8gdGlwcwoKMS4gU3RhcnQgbGVhcm5pbmcgU25ha2VtYWtlIHdpdGggYSBzaW1wbGUgd29ya2Zsb3cuIERvbid0IHN0YXJ0IHdpdGggYSByZWFsbHkKICAgY29tcGxleCB3b3JrZmxvdy4KMi4gSXQncyBvayB0byBidWlsZCB5b3VyIFNuYWtlZmlsZSBmcm9tIHRoZSBmaXJzdCBzdGVwcyB0b3dhcmQgdGhlIGxhc3QsCiAgIGJ1dCByZW1lbWJlciB0aGF0IFNuYWtlbWFrZSAidGhpbmtzIiBmcm9tIHRoZSBsYXN0IG91dHB1dCBiYWNrd2FyZC4KMy4gS2VlcCB5b3VyIGlucHV0cywgb3V0cHV0cywgYW5kIHdvcmtmbG93IGluIHNlcGFyYXRlIGRpcmVjdG9yaWVzLgo0LiBCdWlsZCB5b3VyIFNuYWtlZmlsZSBpbmNyZW1lbnRhbGx5LgogICBhKSBTdGFydCB3aXRoIGEgc2luZ2xlIGlucHV0LgogICBiKSBBZGQgYSBydWxlIGF0IGEgdGltZS4KICAgYykgTWFrZSBzbWFsbCBjaGFuZ2VzIGFuZCAgdXNlIGBkcnktcnVuYCBsaWJlcmFsbHkuCiAgIGQpIE9LIHRvIHN0YXJ0IGJ5IGhhcmRjb2RpbmcgZmlsZW5hbWVzOyBwdXQgdGhlIHdpbGRjYXJkcyBpbiBhZnRlcndhcmQuCjUuIEtlZXAgY29uc2lzdGVudCBuYW1pbmcgY29udmVudGlvbnMuIEluY2x1ZGluZyB5b3VyIHJ1bGUgbmFtZXMgaW4geW91ciBmaWxlCiAgIG5hbWVzIHdpbGwgaGVscCB5b3UgdHJvdWJsZXNob290LiBTb21lIHBlb3BsZSBpbmNsdWRlIG51bWJlcnMgaW4gdGhlIGZpbGUKICAgbmFtZXMgb3IgcGxhY2UgZGlmZmVyZW50IG91dHB1dHMgaW4gZGlmZmVyZW50IGRpcmVjdG9yaWVzLiBDaG9vc2UgYSBwYXR0ZXJuCiAgIHRoYXQgd29ya3MgZm9yIHlvdSBhbmQgYmUgY29uc2lzdGVudC4KNi4gSWYgeW91IHN1YnNldCB5b3VyIGlucHV0IGRhdGEgc28gdGhhdCB5b3VyIHN0ZXBzIGFyZSBmYXN0IGFuZCBsaWdodCwgeW91IAogICBjYW4gcHJvdG90eXBlIHRoZSBTbmFrZWZpbGUgb24gdGhlIGxvZ2luLW5vZGVzLiAoQnV0IGJlIHJlYWR5IHRvIGhpdCBjb250cm9sLUMKICAgaWYgeW91IHdhbmRlciBpbnRvIGEgY29tcHV0YXRpb25hbGx5IGludGVuc2l2ZSBzdGVwLikKNy4gVXNlIHNwYWNlcyBub3QgdGFicy4KOC4gVXNlIGNvbmZpZyBmaWxlcyB0byBhdm9pZCBoYXJkY29kaW5nIHBhdGhzIG9yICJtYWdpYyB2YWx1ZXMiIGluIHRoZSBTbmFrZWZpbGUuCjkuIENoZWNrb3V0IFNuYWtlbWFrZSdzIHN0eWxlZ3VpZGUgb24gW0Rpc3RyaWJ1dGlvbiBhbmQgUmVwcm9kdWNpYmlsaXR5XShodHRwczovL3NuYWtlbWFrZS5yZWFkdGhlZG9jcy5pby9lbi9zdGFibGUvc25ha2VmaWxlcy9kZXBsb3ltZW50Lmh0bWwjZGlzdHJpYnV0aW9uLWFuZC1yZXByb2R1Y2liaWxpdHkpLiAKICAgSXQgaGFzIGdyZWF0IHJlY29tbWVuZGF0aW9ucyBvbiBob3cgdG8gb3JnYW5pemUgZmlsZXMgYW5kIGRpcmVjdG9yaWVzLgoKLS0tCgojIyBLZXkgaWRlYXMKCiogQSByb2J1c3Qgd29ya2Zsb3cgYXV0b21hdGlvbiBzb2x1dGlvbiBpcyBncmVhdCBiZWNhdXNlOgogICogaXQgc3VwcG9ydHMgc2ltcGxlIChlLmcuIGxpbmVhcikgYW5kIGNvbXBsZXggKGUuZy4gZ3JhcGgpCiAgICB0cmFuc2Zvcm1hdGlvbnMgZXF1YWxseSB3ZWxsCiAgKiBvbmx5IG91dHN0YW5kaW5nIHN0ZXBzIGFyZSBydW4gYW5kIGluZGl2aWR1YWwgc3RlcHMgYXJlIGF0b21pYwogICogaXQncyBkZXNpZ25lZCB3aXRoIHNvZnR3YXJlIGRlcGVuZGVuY3kgbWFuYWdlbWVudCBpbiBtaW5kCiAgKiBpdCdzIGVhc3kgdG8gdHVuZSwgcmVzdWx0aW5nIGluIGhpZ2hlciByZXNvdXJjZSBlZmZpY2llbmN5CiAgKiBpdCdzIHBvcnRhYmxlIGFjcm9zcyBleGVjdXRpb24gZW52aXJvbm1lbnRzCiAgKiBpdCdzIGRlc2lnbmVkIHRvIGdyYWNlZnVsbHkgc2NhbGUgdG8gbWFueSBpbnB1dHMKICAqIGFsbCB0aGUgY2FwYWJpbGl0aWVzIGFib3ZlIGRvbid0IG9ic2N1cmUgdGhlIGFjdHVhbCBkYXRhIHRyYW5zZnJvbWF0aW9uCiogU25ha2VtYWtlIGlzIGEgUHl0aG9uIHByb2dyYW0gdGhhdCBldmFsdWF0ZXMgdGhlIHJ1bGVzIGluIGEgU25ha2VmaWxlIGFnYWluc3QKICB0aGUgZmlsZSBzeXN0ZW0gYW5kIGFwcGxpZXMgcnVsZXMgYXMgbmVjZXNzYXJ5IHRvIHByb2R1Y2UgdGhlIGRlc2lyZWQgb3V0cHV0IAogIGZpbGVzLgoqIFRoZSBTbmFrZWZpbGUgaXMgYSBzaW1wbGUgbWFya2Rvd24gZmlsZSBjb21wb3NlZCBvZiBydWxlcy4gRWFjaCBydWxlIGNvbnRhaW5zCiAgKipkaXJlY3RpdmVzKiogdGhhdCBzcGVjaWZ5IGlucHV0cywgb3V0cHV0cywgY29tbWFuZHMgdG8gcnVuIGZvZSB0aGF0IHJ1bGUuCiogU25ha2VtYWtlIGJ1aWxkcyB0aGUgaW50ZW5kZWQgc2V0IG9mIHJ1bGVzIGludG8gYSAqKmRpcmVjdGVkIGFjeWNsaWMgZ3JhcGgqKiAKICAoREFHKS4KKiBUaGUgKipkcnktcnVuKiogZW5hYmxlcyB5b3UgdG8gc2VlIHRoZSBydWxlcyBpdCB3aWxsIGV4ZWN1dGUgaW4gYWR2YW5jZS4KKiBZb3UgY2FuIHZpc3VhbGl6ZSB0aGUgU25ha2VtYWtlIERBRyBvciBydWxlZ3JhcGggdXNpbmcgYGRvdGAuCiogQ29tbWFuZCBsaW5lIGZsYWdzIHdlIHJldmlld2VkOgogICogLWMKICAqIC1uIC0tZHJ5LXJ1bgogICogLS1kYWcgLS1ydWxlZ3JhcGgKICAqIC0tZm9yY2VhbGwKICAqIC1wIC0tcHJpbnRzaGVsbGNtZHMKCi0tLQoKIyMgRXhlcmNpc2VzCgojIyMgRXhlcmNpc2UgMUE6CgpjZCB0byB0aGUgcHJvamVjdCBwdWJsaWNhdGlvbl9zbmFrZW1ha2VfMQoKR2l2ZW4gdGhpcyBEQUcgY2FuIHlvdSBmaWxsIGluIHRoZSBtaXNzaW5nIHBhcnRzIG9mIHRoZSBTbmFrZWZpbGUgYmVsb3cgYW5kIApyZWdlbmVyYXRlIGEgc2ltaWxhciBEQUcgZmlndXJlPwoKIVtdKGltYWdlcy9Nb2R1bGUwN19leGVyY2lzZTFfZGFnLnBuZykKCkZZSToKCi0gQWxsIHRoZSBydWxlcyB5b3UgbmVlZCBhcmUgaW4gcGxhY2UsIGJ1dCBtb3N0IG9mIHRoZW0gaGF2ZSBpbmNvbXBsZXRlIGlucHV0CmFuZCBvdXRwdXQgZGlyZWN0aXZlcy4KLSBUaGUgc2hlbGwgZGlyZWN0aXZlcyBpbiB0aGlzIFNuYWtlZmlsZSByZWZlcmVuY2UgImV4dGVybmFsIiBzY3JpcHRzCmluIHRoZSB3b3JrZmxvdyBzY3JpcHRzIGRpcmVjdG9yeS4gWW91IHdpbGwgKm5vdCogbmVlZCB0byBjaGFuZ2UgYW55IHNoZWxsIApkaXJlY3RpdmVzIGZvciB0aGlzIGV4ZXJjaXNlLiAKLSBUaGUgYWN0dWFsIHJlc3VsdHMgZmlsZXMgZm9yIHRoaXMgd29ya2Zsb3cgYXJlIG1lcmVseSBzdHVicyBhbmQgbm90IGludGVyZXN0aW5nLiAKR2VuZXJhdGluZyB0aGUgcmlnaHQgU25ha2VmaWxlIC8gREFHIGlzIHRoZSBmb2N1cy4KCmBgYApydWxlIGFsbDoKICAgIGlucHV0OiAncmVzdWx0cy9rbml0X3BhcGVyLmh0bWwnCgpydWxlIGtuaXRfcGFwZXI6CiAgICBpbnB1dDogJ2lucHV0cy9pbnRyby5tZCcsID8/CiAgICBvdXRwdXQ6ID8/CiAgICBzaGVsbDogJ3dvcmtmbG93L3NjcmlwdHMva25pdF9wYXBlci5zaCB7aW5wdXR9IHtvdXRwdXR9JwoKcnVsZSBtYWtlX3Bsb3Q6CiAgICBpbnB1dDogPz8KICAgIG91dHB1dDogPz8KICAgIHNoZWxsOiAnd29ya2Zsb3cvc2NyaXB0cy9tYWtlX3Bsb3Quc2gge2lucHV0fSB7b3V0cHV0fScKCnJ1bGUgbWFrZV90YWJsZToKICAgIGlucHV0OiA/PwogICAgb3V0cHV0OiA/PwogICAgc2hlbGw6ICd3b3JrZmxvdy9zY3JpcHRzL21ha2VfdGFibGUuc2gge2lucHV0fSB7b3V0cHV0fScKCnJ1bGUgZmlsdGVyOgogICAgaW5wdXQ6ID8/CiAgICBvdXRwdXQ6ID8/CiAgICBwYXJhbXM6IGNocm9tb3NvbWU9JzYnCiAgICBzaGVsbDogJ3dvcmtmbG93L3NjcmlwdHMvZmlsdGVyLnNoIHtpbnB1dH0ge291dHB1dH0ge3BhcmFtcy5jaHJvbW9zb21lfScKCnJ1bGUgZG93bmxvYWRfZGF0YToKICAgIGlucHV0OiAnL25mcy90dXJiby91bW1zLWJpb2luZi13a3NocC93b3Jrc2hvcC9zaGFyZWQtZGF0YS9hZ2MtZW5kcG9pbnQvMDA0Mi1XUy9zYW1wbGVfQS5nZW5vbWUuYmFtJwogICAgb3V0cHV0OgogICAgc2hlbGw6ICd3b3JrZmxvdy9zY3JpcHRzL2Rvd25sb2FkX2RhdGEuc2gge2lucHV0fSB7b3V0cHV0fScKCmBgYAoKIyMjIEV4ZXJjaXNlIDFCOgoKVXNlIHBhcmFtcyBhbmQgYSBjb25maWcgZmlsZSB0byBleHRyYWN0IGhhcmRjb2RlZCBhYnNvbHV0ZSBwYXRocyBhbmQgYW55IG90aGVyIAoibWFnaWMgdmFsdWVzIi4KCgojIyMgRXhlcmNpc2UgMUM6CgpFeHRlbmQgdGhlIHdvcmtmbG93IHRvIGNvdmVyIG1vcmUgaW5wdXRzIGFzIGlsbHVzdHJhdGVkIGluIHRoZSBEQUcgYmVsb3c6CgohW10oaW1hZ2VzL01vZHVsZTA3X2V4ZXJjaXNlMUNfZGFnLnBuZykKCgojIyMgRXhlcmNpc2UgMUQ6CgpDb3B5IHRoZSBwcm9maWxlL2NvbmZpZy55YW1sIGZyb20gcHJvamVjdF9hbGNvdHQvYWxjb3R0X3NuYWtlbWFrZSBhbmQgZm9yY2UgCnJlLXJ1biB0aGUgU25ha2VmaWxlIHVzaW5nIHdvcmtlciBub2RlcyBpbnN0ZWFkIG9mIHRoZSBsb2dpbi1ub2RlLgoKLS0tCgojIyBSZWZlcmVuY2VzIGFuZCBsaW5rczoKLSBodHRwczovL3NuYWtlbWFrZS5yZWFkdGhlZG9jcy5pby9lbi9zdGFibGUvaW5kZXguaHRtbAogIC0gaHR0cHM6Ly9zbmFrZW1ha2UucmVhZHRoZWRvY3MuaW8vZW4vc3RhYmxlL2V4ZWN1dGluZy9jbGkuaHRtbCNhbGwtb3B0aW9ucwogIC0gaHR0cHM6Ly9zbmFrZW1ha2UucmVhZHRoZWRvY3MuaW8vZW4vc3RhYmxlL3NuYWtlZmlsZXMvd3JpdGluZ19zbmFrZWZpbGVzLmh0bWwjZ3JhbW1hcgoKCi0tLQoKfCBbUHJldmlvdXMgbGVzc29uXShNb2R1bGUwNi1pbnRyby10by13b3JrZmxvdy1hdXRvbWF0aW9uLmh0bWwpIHwgW1RvcCBvZiB0aGlzIGxlc3Nvbl0oI3RvcCkgfCBbV3JhcCB1cF0oTW9kdWxlOTlfV3JhcF91cC5odG1sKSB8CnwgOi0tLSB8IDotLS0tOiB8IC0tLTogfAo=