In this module, we will:
- learn about software management
- discuss package managers
- discuss conda as a software management solution
- learn how to set up conda
- learn how to create and use our own conda environments
Software Management
Software management is something that is not typically appreciated until it is missing. In your typical day-to-day computing tasks, software management is an automatic background process, and generally this is how we want things to be.
When performing research computing tasks, however, you may run into situations when this automatic background handling of software requirements is not sufficient. You may have very specific version requirements, or you may have disparate (or incompatible!) software needs for different tasks.
Package Managers
We likely all have some experience with software management. I’d like to examine our experience with software management, and promote appreciation for the work that often happens behind the scenes.
A fictional, simple example of a dependency tree
A tangled mess of a dependency tree, for a real academic software package
The Need for Modular Solutions
Here we demonstrate another scenario that we might encounter when trying to manage our software, especially as our list of software requirements becomes increasingly lengthy or increasingly bespoke.
In the figure above, we show what could happen when we try to use a cutting-edge version of our fictional software package ‘Applesauce2’ alongside another package ‘Orange-tools’
Conda
- Broad support for many types of software packages
- Across languages and platforms
- Pluggable - Ability to switch between software environments at will
- Automatically handles software requirements
- A ‘Conda recipe’ is used to list specifications
- Used during environment creation
- Can communicate requirements to others when shared
Allows Us to Manage Our Own Software
For us as users of research software, we’re primarily interested in conda because it grants us the ability to manage our own software. In addition, it provides the immense benefit of providing pluggability to our software management capabilities.
Simplifies Distribution of New Software
When we’re dealing with academic software, a lot of times we’re dealing with software is ‘cutting-edge’ - the developers may be pushing the limits of existing software solutions and run into problems. Often, the develompent of cutting-edge software will find these problems in established libraries, and they will be addressed in tandem with the development of the new cutting-edge software that has flushed out these new edge-cases. This means that we’re often relying on very recent versions, or as we alluded to above, very specific versions of various software dependencies.
The platform allows developers to record a project’s environment in a shareable ‘recipe,’ simplifying the replication of software setups. This streamlines collaboration and ensures that software runs consistently across different machines.
For developers, Conda breaks down software distribution into a few straightforward steps. By defining and packaging an application’s environment, they make it effortlessly accessible, thereby encouraging broader use and facilitating user adoption.
Environments
The environment is what is created by Conda, it is the culmination of the dependency solving and installation completion that conda takes care of during the creation stage. After it is created, it will remain available and can be enabled/disabled at will.
To enable and disable an existing conda environment, we will use the command terms conda activate <environment name>
and conda deactivate
.
With the environment activated, software is made available, relevant commands become callable, etc., and when it’s deactivated, the software becomes unavailable again.
Quick aside about $PATH
- it is one of the tricks that conda uses in order to achieve this pluggable capability that we’ve been talking about. the $PATH
environment variable is used to specify and prioritize software locations. We’ll see this in action during our exercises below.
A Couple of Quick Notes
- Conda provides pluggable capability, but it does not offer a high degree of isolation from other software on your system.
- We use ‘Conda’ as a generalized term. There are several variations including ‘Anaconda’ and ‘Miniconda’.
- Anaconda comes pre-bundled with a plethora of common python and data science packages.
- Miniconda starts out as more of a blank slate.
- We will be installing Miniconda and using it in our exercises.
Packages and Channels
We want to get started using Conda fairly quickly and give practical tips for using it in our HPC ecosystem, so we’re not going to cover some of the finer details about how conda creates environments.
We really start to care about these details when we are creating our own conda environments.
There is an excellent online resource here, covering the detailed structure of conda packages and how it works under the hood
Basically, conda packages are bundled up software, system-level libraries, dependency libraries, metadata, etc. into a particular structure. This organized structure allows conda to achieve its package management duties while retaining the beneficial characteristics and capabilities that we enjoy.
Miniconda Installation and Configuration Details
So far we’ve described many of the details of how conda works and how we can use it, both to create new environments and to activate/deactivate them at will. Since we’ll be installing and configuring it during this workshop, we have some notes here that we’ll quickly cover before jumping in.
Miniconda Installation Overview
- We will use the Miniconda installer and each of us will install miniconda to our
$WORKSHOP_HOME
directory on Great Lakes.
- Link to Miniconda installation instructions
- Together, we’ll verify the file integrity of the installer and then use it to guide us through the installation process.
Conda Configuration with .condarc Overview
- Condarc file contains configuration details for your conda installation.
- Link to documentation on using condarc
- In our configuration exercise we will set
envs_dirs
within our condarc file to a location within our turbo storage.
Exercise - Conda Activate, Make BigWig Files
Following along with instructor, learners will activate an existing environment. We’ll demonstrate addition to the $PATH (and using which
). Then, we’ll create a visualization file for sample_A - A BigWig File.
Conda Activate, Make Bigwig - Solution
mkdir filter_viz_conda
srun --pty --job-name=${USER}_bamcoverage_sample_A --account=bioinf_wkshp_class --partition standard --mem=4000 --cpus-per-task=4 --time=00:30:00 /bin/bash
conda activate /nfs/turbo/umms-bioinf-wkshp/workshop/shared-envs/samtools_deeptools/
echo $PATH
which bamCoverage
bamCoverage -b input_bams/sample_A.genome.bam -o filter_viz_conda/sample_A.genome.bigwig
Note: Now we’ll go to https://igv.org/app/ and visualize what this looks like. Select mm10 as genome. Track URL: https://umbioinfcore-workshop.s3.amazonaws.com/sample_A.genome.bigwig
Demonstration - Conda Create
Instructor will demonstrate creating a simple conda environment on the command line.
We’ll then ask about how to create a slightly more complicated conda environment. We’ll start a thread and learners will respond and/or vote on the command that they would use.
Conda Create Demo - Solution
conda search -c bioconda samtools
srun --pty --job-name=conda_create_demo --account=bioinf_wkshp_class --partition standard --mem=2000 --cpus-per-task=2 --time=00:30:00 /bin/bash
conda create -n samtools -c bioconda -c conda-forge samtools=1.20
Exercise - srun
and Conda Create
Following along with instructor, we’ll launch an srun job and then create a conda environment in it. After it’s created, we’ll test it by activating and deactivating it. While active, we’ll check things like $PATH
and use which
to confirm that it’s working as intended.
Conda Create - Solution
srun --pty --job-name=${USER}_conda_create --account=bioinf_wkshp_class --partition standard --mem=2000 --cpus-per-task=2 --time=00:30:00 /bin/bash
conda create -n samtools_deeptools -c bioconda -c conda-forge samtools=1.20 deeptools=3.5
Exercise - Conda Export
Following along with the instructor, we’ll use Conda’s export functionality to create an export - a more complete recipe with all dependencies and their versions fully listed.
Conda Export - Solution
conda activate samtools_deeptools
conda export > ${WORKSHOP_HOME}/project_analysis/conda_envs/export_samtools_deeptools.yaml
Exercise - srun
, Conda, Filtering our BAMs
Following along with the instructor, we’ll launch an interactive job with srun
. Once we’ve entered the running job, we’ll activate our conda environment and use samtools to filter our BAM file just as we did in the lmod
exercises.
srun
, Conda, Filtering BAM - Solution
srun --pty --job-name=${USER}_conda_filterbams --account=bioinf_wkshp_class --partition standard --mem=2000 --cpus-per-task=2 --time=00:30:00 /bin/bash
samtools view -o filter_viz_conda/sample_A.chr19.bam input_bams/sample_A.genome.bam 19
Note: Make sure to module list
and confirm we’re not using the samtools module - the LMOD module and conda environment would interfere with one another!
Exercise - srun
, Conda, Sanity Check and Index BAM
Following along with the instructor, we’ll launch an interactive job with srun
. Once we’ve entered the running job, we’ll activate our conda environment and use samtools to perform a sanity check on our filtered BAM files. We’ll also index one of our BAM files - sample_A. This will set us up for the next step.
srun
, Conda, Sanity Check, Index BAM - Solution
srun --pty --job-name=${USER}_conda_check_and_index --account=bioinf_wkshp_class --partition standard --mem=2000 --cpus-per-task=2 --time=00:30:00 /bin/bash
conda activate samtools_deeptools
samtools view filter_viz_conda/sample_A.chr19.bam | wc -l
samtools index filter_viz_conda/sample_A.chr19.bam
Exercise - srun
, Conda, Creating Bigwigs
Now we’ll create files for visualization of our filtered BAMs, as another way to check our results.
Following along with the instructor, we’ll launch an interactive job with srun
. Once we’ve entered the running job, we’ll activate our conda environment and use bamCoverage
to create a bigwig file for sample_A.
srun
, Conda, Create Bigwig - Solution
srun --pty --job-name=${USER}_conda_bigwigs --account=bioinf_wkshp_class --partition standard --mem=2000 --cpus-per-task=2 --time=00:30:00 /bin/bash
bamCoverage -b filter_viz_conda/sample_A.chr19.bam -o filter_viz_conda/sample_A.chr19.bigwig
Note: Now we’ll go to https://igv.org/app/ and visualize what this looks like. Select mm10 as genome. Track URL: https://umbioinfcore-workshop.s3.amazonaws.com/sample_A.filtered.bigwig
Review
Conda allows us to install and manage our own software. On a multi-user system like Great Lakes, this is very powerful.
Conda is basically a package manager - it is software that manages bundles of other software - but it has additional capabilities that make it great for reproducibility.
It provides pluggability to our software needs. When we need a wide variety of tools at different times, with some being incompatible with one another, this becomes critical.
When given a set of software requirement specifications, Conda handles all of the dependencies and creates a somewhat contained environment that we can activate
and deactivate
as needed.
LS0tCnRpdGxlOiAiU29mdHdhcmUgTWFuYWdlbWVudCBhbmQgQ29uZGEiCmF1dGhvcjogIlVNIEJpb2luZm9ybWF0aWNzIENvcmUiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5eyAvKiBOb3JtYWwgICovCiAgICAgIGZvbnQtc2l6ZTogMTRwdDsKICB9CnByZSB7CiAgZm9udC1zaXplOiAxMnB0Cn0KPC9zdHlsZT4KCkluIHRoaXMgbW9kdWxlLCB3ZSB3aWxsOgoKKiBsZWFybiBhYm91dCBzb2Z0d2FyZSBtYW5hZ2VtZW50CiogZGlzY3VzcyBwYWNrYWdlIG1hbmFnZXJzCiogZGlzY3VzcyBjb25kYSBhcyBhIHNvZnR3YXJlIG1hbmFnZW1lbnQgc29sdXRpb24KKiBsZWFybiBob3cgdG8gc2V0IHVwIGNvbmRhCiogbGVhcm4gaG93IHRvIGNyZWF0ZSBhbmQgdXNlIG91ciBvd24gY29uZGEgZW52aXJvbm1lbnRzCgojIyBTb2Z0d2FyZSBNYW5hZ2VtZW50CgpTb2Z0d2FyZSBtYW5hZ2VtZW50IGlzIHNvbWV0aGluZyB0aGF0IGlzIG5vdCB0eXBpY2FsbHkgYXBwcmVjaWF0ZWQgdW50aWwgaXQgaXMgbWlzc2luZy4gSW4geW91ciB0eXBpY2FsIGRheS10by1kYXkgY29tcHV0aW5nIHRhc2tzLCBzb2Z0d2FyZSBtYW5hZ2VtZW50IGlzIGFuIGF1dG9tYXRpYyBiYWNrZ3JvdW5kIHByb2Nlc3MsIGFuZCBnZW5lcmFsbHkgdGhpcyBpcyBob3cgd2Ugd2FudCB0aGluZ3MgdG8gYmUuCgpXaGVuIHBlcmZvcm1pbmcgcmVzZWFyY2ggY29tcHV0aW5nIHRhc2tzLCBob3dldmVyLCB5b3UgbWF5IHJ1biBpbnRvIHNpdHVhdGlvbnMgd2hlbiB0aGlzIGF1dG9tYXRpYyBiYWNrZ3JvdW5kIGhhbmRsaW5nIG9mIHNvZnR3YXJlIHJlcXVpcmVtZW50cyBpcyBub3Qgc3VmZmljaWVudC4gWW91IG1heSBoYXZlIHZlcnkgc3BlY2lmaWMgdmVyc2lvbiByZXF1aXJlbWVudHMsIG9yIHlvdSBtYXkgaGF2ZSBkaXNwYXJhdGUgKG9yIGluY29tcGF0aWJsZSEpIHNvZnR3YXJlIG5lZWRzIGZvciBkaWZmZXJlbnQgdGFza3MuCgojIyMgUGFja2FnZSBNYW5hZ2VycwoKV2UgbGlrZWx5IGFsbCBoYXZlIHNvbWUgZXhwZXJpZW5jZSB3aXRoIHNvZnR3YXJlIG1hbmFnZW1lbnQuIEknZCBsaWtlIHRvIGV4YW1pbmUgb3VyIGV4cGVyaWVuY2Ugd2l0aCBzb2Z0d2FyZSBtYW5hZ2VtZW50LCBhbmQgcHJvbW90ZSBhcHByZWNpYXRpb24gZm9yIHRoZSB3b3JrIHRoYXQgb2Z0ZW4gaGFwcGVucyBiZWhpbmQgdGhlIHNjZW5lcy4KCjwhLS0gTElWRV9OT1RFOiBTbGFjayBwb2xsaW5nIGZvciB3aG8gdXNlcyBXaW5kb3dzLCB3aG8gdXNlcyBNYWNPUywgTGludXggLS0+Cgo8IS0tIExJVkVfTk9URTogQWZ0ZXIgcG9sbGluZywgcmVsYXRlIHRvIGF1dG9tYXRpYyB1cGRhdGVzLCBBcHAgU3RvcmUsIFdpbmRvd3MgVXBkYXRlcywgZXRjLiAtLT4KCjxicj4KPGJyPgo8YnI+CgpBIGZpY3Rpb25hbCwgc2ltcGxlIGV4YW1wbGUgb2YgYSBkZXBlbmRlbmN5IHRyZWUKCiFbXShpbWFnZXMvTW9kdWxlMDRfZGVwZW5kZW5jaWVzX2xvbmdfbGlzdC5wbmcpCgo8YnI+Cgo8ZGV0YWlscz4KPHN1bW1hcnk+QSB0YW5nbGVkIG1lc3Mgb2YgYSBkZXBlbmRlbmN5IHRyZWUsIGZvciBhIHJlYWwgYWNhZGVtaWMgc29mdHdhcmUgcGFja2FnZTwvc3VtbWFyeT4KCiFbXShpbWFnZXMvTW9kdWxlMDRfZGVwZW5kZW5jaWVzX3hzZGtfdGFuZ2xlZF9tZXNzLnBuZykKCjwvZGV0YWlscz4KCjxicj4KCiMjIyBUaGUgTmVlZCBmb3IgTW9kdWxhciBTb2x1dGlvbnMKCiFbXShpbWFnZXMvTW9kdWxlMDRfZGVwZW5kZW5jaWVzX2luY29tcGF0aWJpbGl0eS5wbmcpCgpIZXJlIHdlIGRlbW9uc3RyYXRlIGFub3RoZXIgc2NlbmFyaW8gdGhhdCB3ZSBtaWdodCBlbmNvdW50ZXIgd2hlbiB0cnlpbmcgdG8gbWFuYWdlIG91ciBzb2Z0d2FyZSwgZXNwZWNpYWxseSBhcyBvdXIgbGlzdCBvZiBzb2Z0d2FyZSByZXF1aXJlbWVudHMgYmVjb21lcyBpbmNyZWFzaW5nbHkgbGVuZ3RoeSBvciBpbmNyZWFzaW5nbHkgYmVzcG9rZS4KCkluIHRoZSBmaWd1cmUgYWJvdmUsIHdlIHNob3cgd2hhdCBjb3VsZCBoYXBwZW4gd2hlbiB3ZSB0cnkgdG8gdXNlIGEgY3V0dGluZy1lZGdlIHZlcnNpb24gb2Ygb3VyIGZpY3Rpb25hbCBzb2Z0d2FyZSBwYWNrYWdlICdBcHBsZXNhdWNlMicgYWxvbmdzaWRlIGFub3RoZXIgcGFja2FnZSAnT3JhbmdlLXRvb2xzJwoKPCEtLSBMSVZFX05PVEU6IEFzayBhYm91dCBzb2Z0d2FyZSBpbmNvbXBhdGliaWxpdHkgZXhwZXJpZW5jZXMgd2hlbiB1c2luZyByZXNlYXJjaCBzb2Z0d2FyZSAtLT4KCjwhLS0gTElWRV9OT1RFOiBSZWxhdGUgb3VyIHR5cGljYWwgZXhwZXJpZW5jZSBvZiBvbmx5IGhhdmluZyBhIHNpbmdsZSBjb21wdXRlIGVudmlyb25tZW50IHRvIHdvcmsgd2l0aC4gT25jZSB3ZSBpbnN0YWxsIHNvbWV0aGluZywgZ2VuZXJhbGx5IGl0IHN0YXlzIHRoZXJlIGZvcmV2ZXIgdW5sZXNzIHdlIHVuaW5zdGFsbCBpdCBtYW51YWxseS4gRXZlbiBhZnRlciB1bmluc3RhbGxpbmcgaXQsIHNvbWUgYXJ0aWZhY3RzIG1heSByZW1haW4gYW5kIGNhdXNlIGludGVyZmVyZW5jZSAtLT4KCjxicj4KCiMjIENvbmRhCgo8IS0tIEZJWE1FOiBDb25kYSBpY29uIG9yIHdlYnNpdGUgYmFubmVyIGhlcmUgLS0+CgotIEJyb2FkIHN1cHBvcnQgZm9yIG1hbnkgdHlwZXMgb2Ygc29mdHdhcmUgcGFja2FnZXMKICAtIEFjcm9zcyBsYW5ndWFnZXMgYW5kIHBsYXRmb3JtcwotIFBsdWdnYWJsZSAtIEFiaWxpdHkgdG8gc3dpdGNoIGJldHdlZW4gc29mdHdhcmUgZW52aXJvbm1lbnRzIGF0IHdpbGwKLSBBdXRvbWF0aWNhbGx5IGhhbmRsZXMgc29mdHdhcmUgcmVxdWlyZW1lbnRzCi0gQSAnQ29uZGEgcmVjaXBlJyBpcyB1c2VkIHRvIGxpc3Qgc3BlY2lmaWNhdGlvbnMKICAtIFVzZWQgZHVyaW5nIGVudmlyb25tZW50IGNyZWF0aW9uCiAgLSBDYW4gY29tbXVuaWNhdGUgcmVxdWlyZW1lbnRzIHRvIG90aGVycyB3aGVuIHNoYXJlZAoKIyMjIEFsbG93cyBVcyB0byBNYW5hZ2UgT3VyIE93biBTb2Z0d2FyZQoKRm9yIHVzIGFzIHVzZXJzIG9mIHJlc2VhcmNoIHNvZnR3YXJlLCB3ZSdyZSBwcmltYXJpbHkgaW50ZXJlc3RlZCBpbiBjb25kYSBiZWNhdXNlIGl0IGdyYW50cyB1cyB0aGUgYWJpbGl0eSB0byBtYW5hZ2Ugb3VyIG93biBzb2Z0d2FyZS4gSW4gYWRkaXRpb24sIGl0IHByb3ZpZGVzIHRoZSBpbW1lbnNlIGJlbmVmaXQgb2YgcHJvdmlkaW5nIHBsdWdnYWJpbGl0eSB0byBvdXIgc29mdHdhcmUgbWFuYWdlbWVudCBjYXBhYmlsaXRpZXMuCgo8IS0tIExJVkVfTk9URTogUmVsYXRlIHRvIG5vdCBiZWluZyBhYmxlIHRvIHVzZSBkZWVwdG9vbHMsIG5vIG1vZHVsZSBhdmFpbGFibGUgaW4gcHJldiBtb2R1bGUgLS0+CgojIyMgU2ltcGxpZmllcyBEaXN0cmlidXRpb24gb2YgTmV3IFNvZnR3YXJlCgpXaGVuIHdlJ3JlIGRlYWxpbmcgd2l0aCBhY2FkZW1pYyBzb2Z0d2FyZSwgYSBsb3Qgb2YgdGltZXMgd2UncmUgZGVhbGluZyB3aXRoIHNvZnR3YXJlIGlzICdjdXR0aW5nLWVkZ2UnIC0gdGhlIGRldmVsb3BlcnMgbWF5IGJlIHB1c2hpbmcgdGhlIGxpbWl0cyBvZiBleGlzdGluZyBzb2Z0d2FyZSBzb2x1dGlvbnMgYW5kIHJ1biBpbnRvIHByb2JsZW1zLiBPZnRlbiwgdGhlIGRldmVsb21wZW50IG9mIGN1dHRpbmctZWRnZSBzb2Z0d2FyZSB3aWxsIGZpbmQgdGhlc2UgcHJvYmxlbXMgaW4gZXN0YWJsaXNoZWQgbGlicmFyaWVzLCBhbmQgdGhleSB3aWxsIGJlIGFkZHJlc3NlZCBpbiB0YW5kZW0gd2l0aCB0aGUgZGV2ZWxvcG1lbnQgb2YgdGhlIG5ldyBjdXR0aW5nLWVkZ2Ugc29mdHdhcmUgdGhhdCBoYXMgZmx1c2hlZCBvdXQgdGhlc2UgbmV3IGVkZ2UtY2FzZXMuIFRoaXMgbWVhbnMgdGhhdCB3ZSdyZSBvZnRlbiByZWx5aW5nIG9uIHZlcnkgcmVjZW50IHZlcnNpb25zLCBvciBhcyB3ZSBhbGx1ZGVkIHRvIGFib3ZlLCB2ZXJ5IHNwZWNpZmljIHZlcnNpb25zIG9mIHZhcmlvdXMgc29mdHdhcmUgZGVwZW5kZW5jaWVzLiAKClRoZSBwbGF0Zm9ybSBhbGxvd3MgZGV2ZWxvcGVycyB0byByZWNvcmQgYSBwcm9qZWN0J3MgZW52aXJvbm1lbnQgaW4gYSBzaGFyZWFibGUgJ3JlY2lwZSwnIHNpbXBsaWZ5aW5nIHRoZSByZXBsaWNhdGlvbiBvZiBzb2Z0d2FyZSBzZXR1cHMuIFRoaXMgc3RyZWFtbGluZXMgY29sbGFib3JhdGlvbiBhbmQgZW5zdXJlcyB0aGF0IHNvZnR3YXJlIHJ1bnMgY29uc2lzdGVudGx5IGFjcm9zcyBkaWZmZXJlbnQgbWFjaGluZXMuCgpGb3IgZGV2ZWxvcGVycywgQ29uZGEgYnJlYWtzIGRvd24gc29mdHdhcmUgZGlzdHJpYnV0aW9uIGludG8gYSBmZXcgc3RyYWlnaHRmb3J3YXJkIHN0ZXBzLiBCeSBkZWZpbmluZyBhbmQgcGFja2FnaW5nIGFuIGFwcGxpY2F0aW9uJ3MgZW52aXJvbm1lbnQsIHRoZXkgbWFrZSBpdCBlZmZvcnRsZXNzbHkgYWNjZXNzaWJsZSwgdGhlcmVieSBlbmNvdXJhZ2luZyBicm9hZGVyIHVzZSBhbmQgZmFjaWxpdGF0aW5nIHVzZXIgYWRvcHRpb24uCgoKIyMjIEVudmlyb25tZW50cwoKVGhlIGVudmlyb25tZW50IGlzIHdoYXQgaXMgY3JlYXRlZCBieSBDb25kYSwgaXQgaXMgdGhlIGN1bG1pbmF0aW9uIG9mIHRoZSBkZXBlbmRlbmN5IHNvbHZpbmcgYW5kIGluc3RhbGxhdGlvbiBjb21wbGV0aW9uIHRoYXQgY29uZGEgdGFrZXMgY2FyZSBvZiBkdXJpbmcgdGhlIGNyZWF0aW9uIHN0YWdlLiBBZnRlciBpdCBpcyBjcmVhdGVkLCBpdCB3aWxsIHJlbWFpbiBhdmFpbGFibGUgYW5kIGNhbiBiZSBlbmFibGVkL2Rpc2FibGVkIGF0IHdpbGwuCgpUbyBlbmFibGUgYW5kIGRpc2FibGUgYW4gZXhpc3RpbmcgY29uZGEgZW52aXJvbm1lbnQsIHdlIHdpbGwgdXNlIHRoZSBjb21tYW5kIHRlcm1zIGBjb25kYSBhY3RpdmF0ZSA8ZW52aXJvbm1lbnQgbmFtZT5gIGFuZCBgY29uZGEgZGVhY3RpdmF0ZWAuCgpXaXRoIHRoZSBlbnZpcm9ubWVudCBhY3RpdmF0ZWQsIHNvZnR3YXJlIGlzIG1hZGUgYXZhaWxhYmxlLCByZWxldmFudCBjb21tYW5kcyBiZWNvbWUgY2FsbGFibGUsIGV0Yy4sIGFuZCB3aGVuIGl0J3MgZGVhY3RpdmF0ZWQsIHRoZSBzb2Z0d2FyZSBiZWNvbWVzIHVuYXZhaWxhYmxlIGFnYWluLgoKUXVpY2sgYXNpZGUgYWJvdXQgYCRQQVRIYCAtIGl0IGlzIG9uZSBvZiB0aGUgdHJpY2tzIHRoYXQgY29uZGEgdXNlcyBpbiBvcmRlciB0byBhY2hpZXZlIHRoaXMgcGx1Z2dhYmxlIGNhcGFiaWxpdHkgdGhhdCB3ZSd2ZSBiZWVuIHRhbGtpbmcgYWJvdXQuIHRoZSBgJFBBVEhgIGVudmlyb25tZW50IHZhcmlhYmxlIGlzIHVzZWQgdG8gc3BlY2lmeSBhbmQgcHJpb3JpdGl6ZSBzb2Z0d2FyZSBsb2NhdGlvbnMuIFdlJ2xsIHNlZSB0aGlzIGluIGFjdGlvbiBkdXJpbmcgb3VyIGV4ZXJjaXNlcyBiZWxvdy4KCgojIyMgQSBDb3VwbGUgb2YgUXVpY2sgTm90ZXMKCi0gQ29uZGEgcHJvdmlkZXMgcGx1Z2dhYmxlIGNhcGFiaWxpdHksIGJ1dCBpdCBkb2VzIG5vdCBvZmZlciBhIGhpZ2ggZGVncmVlIG9mIGlzb2xhdGlvbiBmcm9tIG90aGVyIHNvZnR3YXJlIG9uIHlvdXIgc3lzdGVtLgotIFdlIHVzZSAnQ29uZGEnIGFzIGEgZ2VuZXJhbGl6ZWQgdGVybS4gVGhlcmUgYXJlIHNldmVyYWwgdmFyaWF0aW9ucyBpbmNsdWRpbmcgJ0FuYWNvbmRhJyBhbmQgJ01pbmljb25kYScuCiAgLSBBbmFjb25kYSBjb21lcyBwcmUtYnVuZGxlZCB3aXRoIGEgcGxldGhvcmEgb2YgY29tbW9uIHB5dGhvbiBhbmQgZGF0YSBzY2llbmNlIHBhY2thZ2VzLgogIC0gTWluaWNvbmRhIHN0YXJ0cyBvdXQgYXMgbW9yZSBvZiBhIGJsYW5rIHNsYXRlLgogIC0gV2Ugd2lsbCBiZSBpbnN0YWxsaW5nIE1pbmljb25kYSBhbmQgdXNpbmcgaXQgaW4gb3VyIGV4ZXJjaXNlcy4KCjxicj4KCjxkZXRhaWxzPgo8c3VtbWFyeT5QYWNrYWdlcyBhbmQgQ2hhbm5lbHM8L3N1bW1hcnk+CgpXZSB3YW50IHRvIGdldCBzdGFydGVkIHVzaW5nIENvbmRhIGZhaXJseSBxdWlja2x5IGFuZCBnaXZlIHByYWN0aWNhbCB0aXBzIGZvciB1c2luZyBpdCBpbiBvdXIgSFBDIGVjb3N5c3RlbSwgc28gd2UncmUgbm90IGdvaW5nIHRvIGNvdmVyIHNvbWUgb2YgdGhlIGZpbmVyIGRldGFpbHMgYWJvdXQgaG93IGNvbmRhIGNyZWF0ZXMgZW52aXJvbm1lbnRzLgoKV2UgcmVhbGx5IHN0YXJ0IHRvIGNhcmUgYWJvdXQgdGhlc2UgZGV0YWlscyB3aGVuIHdlIGFyZSBjcmVhdGluZyBvdXIgb3duIGNvbmRhIGVudmlyb25tZW50cy4gCgpUaGVyZSBpcyBhbiBleGNlbGxlbnQgb25saW5lIHJlc291cmNlIFtoZXJlLCBjb3ZlcmluZyB0aGUgZGV0YWlsZWQgc3RydWN0dXJlIG9mIGNvbmRhIHBhY2thZ2VzIGFuZCBob3cgaXQgd29ya3MgdW5kZXIgdGhlIGhvb2RdKGh0dHBzOi8vZWRjYXJwLmdpdGh1Yi5pby9pbnRyb2R1Y3Rpb24tdG8tY29uZGEtZm9yLWRhdGEtc2NpZW50aXN0cy8wMy11c2luZy1wYWNrYWdlcy1hbmQtY2hhbm5lbHMvaW5kZXguaHRtbCkKCkJhc2ljYWxseSwgY29uZGEgcGFja2FnZXMgYXJlIGJ1bmRsZWQgdXAgc29mdHdhcmUsIHN5c3RlbS1sZXZlbCBsaWJyYXJpZXMsIGRlcGVuZGVuY3kgbGlicmFyaWVzLCBtZXRhZGF0YSwgZXRjLiBpbnRvIGEgcGFydGljdWxhciBzdHJ1Y3R1cmUuIFRoaXMgb3JnYW5pemVkIHN0cnVjdHVyZSBhbGxvd3MgY29uZGEgdG8gYWNoaWV2ZSBpdHMgcGFja2FnZSBtYW5hZ2VtZW50IGR1dGllcyB3aGlsZSByZXRhaW5pbmcgdGhlIGJlbmVmaWNpYWwgY2hhcmFjdGVyaXN0aWNzIGFuZCBjYXBhYmlsaXRpZXMgdGhhdCB3ZSBlbmpveS4KCjwvZGV0YWlscz4KCjxicj4KCiMjIE1pbmljb25kYSBJbnN0YWxsYXRpb24gYW5kIENvbmZpZ3VyYXRpb24gRGV0YWlscwoKU28gZmFyIHdlJ3ZlIGRlc2NyaWJlZCBtYW55IG9mIHRoZSBkZXRhaWxzIG9mIGhvdyBjb25kYSB3b3JrcyBhbmQgaG93IHdlIGNhbiB1c2UgaXQsIGJvdGggdG8gY3JlYXRlIG5ldyBlbnZpcm9ubWVudHMgYW5kIHRvIGFjdGl2YXRlL2RlYWN0aXZhdGUgdGhlbSBhdCB3aWxsLiBTaW5jZSB3ZSdsbCBiZSBpbnN0YWxsaW5nIGFuZCBjb25maWd1cmluZyBpdCBkdXJpbmcgdGhpcyB3b3Jrc2hvcCwgd2UgaGF2ZSBzb21lIG5vdGVzIGhlcmUgdGhhdCB3ZSdsbCBxdWlja2x5IGNvdmVyIGJlZm9yZSBqdW1waW5nIGluLgoKIyMjIE1pbmljb25kYSBJbnN0YWxsYXRpb24gT3ZlcnZpZXcKCi0gV2Ugd2lsbCB1c2UgdGhlIE1pbmljb25kYSBpbnN0YWxsZXIgYW5kIGVhY2ggb2YgdXMgd2lsbCBpbnN0YWxsIG1pbmljb25kYSB0byBvdXIgYCRXT1JLU0hPUF9IT01FYCBkaXJlY3Rvcnkgb24gR3JlYXQgTGFrZXMuCi0gW0xpbmsgdG8gTWluaWNvbmRhIGluc3RhbGxhdGlvbiBpbnN0cnVjdGlvbnNdKGh0dHBzOi8vZG9jcy5hbmFjb25kYS5jb20vZnJlZS9taW5pY29uZGEvbWluaWNvbmRhLWluc3RhbGwvKQotIFRvZ2V0aGVyLCB3ZSdsbCB2ZXJpZnkgdGhlIGZpbGUgaW50ZWdyaXR5IG9mIHRoZSBpbnN0YWxsZXIgYW5kIHRoZW4gdXNlIGl0IHRvIGd1aWRlIHVzIHRocm91Z2ggdGhlIGluc3RhbGxhdGlvbiBwcm9jZXNzLgoKPGJyPgoKIyMjIENvbmRhIENvbmZpZ3VyYXRpb24gd2l0aCAuY29uZGFyYyBPdmVydmlldwoKLSBDb25kYXJjIGZpbGUgY29udGFpbnMgY29uZmlndXJhdGlvbiBkZXRhaWxzIGZvciB5b3VyIGNvbmRhIGluc3RhbGxhdGlvbi4KLSBbTGluayB0byBkb2N1bWVudGF0aW9uIG9uIHVzaW5nIGNvbmRhcmNdKGh0dHBzOi8vY29uZGEuaW8vcHJvamVjdHMvY29uZGEvZW4vbGF0ZXN0L3VzZXItZ3VpZGUvY29uZmlndXJhdGlvbi91c2UtY29uZGFyYy5odG1sKQotIEluIG91ciBjb25maWd1cmF0aW9uIGV4ZXJjaXNlIHdlIHdpbGwgc2V0IGBlbnZzX2RpcnNgIHdpdGhpbiBvdXIgY29uZGFyYyBmaWxlIHRvIGEgbG9jYXRpb24gd2l0aGluIG91ciB0dXJibyBzdG9yYWdlLgoKPGJyPgoKIyMgRXhlcmNpc2UgLSBDb25kYSBJbnN0YWxsIGFuZCBDb25maWd1cmUKCkZvbGxvd2luZyBhbG9uZyB3aXRoIGluc3RydWN0b3IsIGxlYXJuZXJzIHdpbGwgaW5zdGFsbCBtaW5pY29uZGEgYW5kIGNyZWF0ZSBhIGAuY29uZGFyY2AgZmlsZS4KCjxkZXRhaWxzPgo8c3VtbWFyeT5NaW5pY29uZGEgSW5zdGFsbCBhbmQgQ29uZmlndXJlIC0gU29sdXRpb248L3N1bW1hcnk+Cgpgc2hhMjU2c3VtIE1pbmljb25kYTMtbGF0ZXN0LUxpbnV4LXg4Nl82NC5zaCAgIyBDaGVjayByZXN1bHQgYWdhaW5zdCB0aGUgbWluaWNvbmRhIGluc3RhbGxlciBwYWdlYAoKYC4vTWluaWNvbmRhMy1sYXRlc3QtTGludXgteDg2XzY0LnNoIC1wICR7V09SS1NIT1BfSE9NRX0vbWluaWNvbmRhM2AKCj5Ob3RlOiBBdCB0aGUgZW5kLCB3aGVuIGFza2VkIGFib3V0IHNldHRpbmcgdXAgY29uZGEgaW5pdGlhbGl6YXRpb24sIHdlIGNob3NlICdubycsIGFuZCBpbnN0ZWFkIG9wdGVkIHRvIGNyZWF0ZSBhIHNlcGFyYXRlIGNvbmRhX2luaXRpYWxpemF0aW9uIGZpbGUgdGhhdCB3ZSBjYW4gYHNvdXJjZWAgd2hlbiB3ZSB3YW50IHRvIHVzZSBjb25kYS4KCmBuYW5vICR7V09SS1NIT1BfSE9NRX0vaW5pdGlhbGl6ZV9jb25kYV93b3Jrc2hvcC5zaGAKCkNvbnRlbnQgb2YgYGluaXRpYWxpemVfY29uZGFfd29ya3Nob3Auc2hgOgoKYGV2YWwgIiQoL25mcy90dXJiby91bW1zLWJpb2luZi13a3NocC93b3Jrc2hvcC9ob21lLyR7VVNFUn0vbWluaWNvbmRhMy9iaW4vY29uZGEgc2hlbGwuYmFzaCBob29rKSJgCgpUaGVuIHdlIGNhbiBgc291cmNlICR7V09SS1NIT1BfSE9NRX0vaW5pdGlhbGl6ZV9jb25kYV93b3Jrc2hvcC5zaGAgYW5kIG5vdGljZSBgKGJhc2UpYCBhcHBlYXJzIGluIG91ciBwcm9tcHQsIGFuIGluZGljYXRvciB0aGF0IHRoZSBgYmFzZWAgZW52aXJvbm1lbnQgb2Ygb3VyIGNvbmRhIGluc3RhbGxhdGlvbiBpcyBhY3RpdmF0ZWQuCgpgbWtkaXIgcHJvamVjdF9hbmFseXNpcy9jb25kYV9lbnZzYAoKYG5hbm8gfi8uY29uZGFyYyAgIyBBZGQgYW4gZW50cnkgZm9yICdlbnZzX2RpcnMnIGluIG91ciBjb25kYXJjYAoKQXMgYW4gYWx0ZXJuYXRpdmUgdG8gbmFubywgd2UgY2FuIHVzZSB0aGlzIG9uZS1saW5lciAoISBPbmx5IGRvIHRoaXMgaWYgeW91IGFyZSBzdXJlIHlvdSBkb24ndCBoYXZlIGFuIGV4aXN0aW5nIGB+Ly5jb25kYXJjYCwgaXQgd291bGQgYmUgb3ZlcndyaXR0ZW4hKToKCmBlY2hvIC1lICJlbnZzX2RpcnM6XG4gIC0gJHtXT1JLU0hPUF9IT01FfS9wcm9qZWN0X2FuYWx5c2lzL2NvbmRhX2VudnMiID4gfi8uY29uZGFyY2AKCjwvZGV0YWlscz4KCjwhLS0gTElWRV9OT1RFOiBPbmUtbGluZXIgd2l0aCBsaWJtYW1iYSBzb2x2ZXIKYGVjaG8gLWUgImVudnNfZGlyczpcbiAgLSAke1dPUktTSE9QX0hPTUV9L3Byb2plY3RfYW5hbHlzaXMvY29uZGFfZW52c1xuc29sdmVyOiBsaWJtYW1iYSIgPiB+Ly5jb25kYXJjYCAtLT4KCjwhLS0gTElWRV9OT1RFOiBab29tIHBvbGwgdG8gdmVyaWZ5IG1pbmljb25kYSBpbnN0YWxsYXRpb24gbG9jYXRpb24gd2l0aCBgbHNgIGFuZCB2ZXJpZnkgY29uZGFyYyBmaWxlIHdpdGggYGNhdGAgLS0+Cgo8YnI+CgojIyBFeGVyY2lzZSAtIENvbmRhIEFjdGl2YXRlLCBNYWtlIEJpZ1dpZyBGaWxlcwoKRm9sbG93aW5nIGFsb25nIHdpdGggaW5zdHJ1Y3RvciwgbGVhcm5lcnMgd2lsbCBhY3RpdmF0ZSBhbiBleGlzdGluZyBlbnZpcm9ubWVudC4gV2UnbGwgZGVtb25zdHJhdGUgYWRkaXRpb24gdG8gdGhlICRQQVRIIChhbmQgdXNpbmcgYHdoaWNoYCkuIFRoZW4sIHdlJ2xsIGNyZWF0ZSBhIHZpc3VhbGl6YXRpb24gZmlsZSBmb3Igc2FtcGxlX0EgLSBBIEJpZ1dpZyBGaWxlLgoKPGRldGFpbHM+CjxzdW1tYXJ5PkNvbmRhIEFjdGl2YXRlLCBNYWtlIEJpZ3dpZyAtIFNvbHV0aW9uPC9zdW1tYXJ5PgoKYG1rZGlyIGZpbHRlcl92aXpfY29uZGFgCgpgc3J1biAtLXB0eSAtLWpvYi1uYW1lPSR7VVNFUn1fYmFtY292ZXJhZ2Vfc2FtcGxlX0EgLS1hY2NvdW50PWJpb2luZl93a3NocF9jbGFzcyAtLXBhcnRpdGlvbiBzdGFuZGFyZCAtLW1lbT00MDAwIC0tY3B1cy1wZXItdGFzaz00IC0tdGltZT0wMDozMDowMCAvYmluL2Jhc2hgCgpgY29uZGEgYWN0aXZhdGUgL25mcy90dXJiby91bW1zLWJpb2luZi13a3NocC93b3Jrc2hvcC9zaGFyZWQtZW52cy9zYW10b29sc19kZWVwdG9vbHMvYAoKYGVjaG8gJFBBVEhgCgpgd2hpY2ggYmFtQ292ZXJhZ2VgCgpgYmFtQ292ZXJhZ2UgLWIgaW5wdXRfYmFtcy9zYW1wbGVfQS5nZW5vbWUuYmFtIC1vIGZpbHRlcl92aXpfY29uZGEvc2FtcGxlX0EuZ2Vub21lLmJpZ3dpZ2AKCj5Ob3RlOiBOb3cgd2UnbGwgZ28gdG8gaHR0cHM6Ly9pZ3Yub3JnL2FwcC8gYW5kIHZpc3VhbGl6ZSB3aGF0IHRoaXMgbG9va3MgbGlrZS4gU2VsZWN0IG1tMTAgYXMgZ2Vub21lLgo+VHJhY2sgVVJMOiBodHRwczovL3VtYmlvaW5mY29yZS13b3Jrc2hvcC5zMy5hbWF6b25hd3MuY29tL3NhbXBsZV9BLmdlbm9tZS5iaWd3aWcKCjwvZGV0YWlscz4KCjxicj4KCiMjIERlbW9uc3RyYXRpb24gLSBDb25kYSBDcmVhdGUKCkluc3RydWN0b3Igd2lsbCBkZW1vbnN0cmF0ZSBjcmVhdGluZyBhIHNpbXBsZSBjb25kYSBlbnZpcm9ubWVudCBvbiB0aGUgY29tbWFuZCBsaW5lLiAKCldlJ2xsIHRoZW4gYXNrIGFib3V0IGhvdyB0byBjcmVhdGUgYSBzbGlnaHRseSBtb3JlIGNvbXBsaWNhdGVkIGNvbmRhIGVudmlyb25tZW50LiBXZSdsbCBzdGFydCBhIHRocmVhZCBhbmQgbGVhcm5lcnMgd2lsbCByZXNwb25kIGFuZC9vciB2b3RlIG9uIHRoZSBjb21tYW5kIHRoYXQgdGhleSB3b3VsZCB1c2UuCgo8ZGV0YWlscz4KPHN1bW1hcnk+Q29uZGEgQ3JlYXRlIERlbW8gLSBTb2x1dGlvbjwvc3VtbWFyeT4KCmBjb25kYSBzZWFyY2ggLWMgYmlvY29uZGEgc2FtdG9vbHNgCgpgc3J1biAtLXB0eSAtLWpvYi1uYW1lPWNvbmRhX2NyZWF0ZV9kZW1vIC0tYWNjb3VudD1iaW9pbmZfd2tzaHBfY2xhc3MgLS1wYXJ0aXRpb24gc3RhbmRhcmQgLS1tZW09MjAwMCAtLWNwdXMtcGVyLXRhc2s9MiAtLXRpbWU9MDA6MzA6MDAgL2Jpbi9iYXNoYAoKYGNvbmRhIGNyZWF0ZSAtbiBzYW10b29scyAtYyBiaW9jb25kYSAtYyBjb25kYS1mb3JnZSBzYW10b29scz0xLjIwYAoKPC9kZXRhaWxzPgoKPGJyPgoKIyMgRXhlcmNpc2UgLSBgc3J1bmAgYW5kIENvbmRhIENyZWF0ZQoKRm9sbG93aW5nIGFsb25nIHdpdGggaW5zdHJ1Y3Rvciwgd2UnbGwgbGF1bmNoIGFuIHNydW4gam9iIGFuZCB0aGVuIGNyZWF0ZSBhIGNvbmRhIGVudmlyb25tZW50IGluIGl0LiBBZnRlciBpdCdzIGNyZWF0ZWQsIHdlJ2xsIHRlc3QgaXQgYnkgYWN0aXZhdGluZyBhbmQgZGVhY3RpdmF0aW5nIGl0LiBXaGlsZSBhY3RpdmUsIHdlJ2xsIGNoZWNrIHRoaW5ncyBsaWtlIGAkUEFUSGAgYW5kIHVzZSBgd2hpY2hgIHRvIGNvbmZpcm0gdGhhdCBpdCdzIHdvcmtpbmcgYXMgaW50ZW5kZWQuCgo8ZGV0YWlscz4KPHN1bW1hcnk+Q29uZGEgQ3JlYXRlIC0gU29sdXRpb248L3N1bW1hcnk+Cgpgc3J1biAtLXB0eSAtLWpvYi1uYW1lPSR7VVNFUn1fY29uZGFfY3JlYXRlIC0tYWNjb3VudD1iaW9pbmZfd2tzaHBfY2xhc3MgLS1wYXJ0aXRpb24gc3RhbmRhcmQgLS1tZW09MjAwMCAtLWNwdXMtcGVyLXRhc2s9MiAtLXRpbWU9MDA6MzA6MDAgL2Jpbi9iYXNoYAoKYGNvbmRhIGNyZWF0ZSAtbiBzYW10b29sc19kZWVwdG9vbHMgLWMgYmlvY29uZGEgLWMgY29uZGEtZm9yZ2Ugc2FtdG9vbHM9MS4yMCBkZWVwdG9vbHM9My41YAoKPC9kZXRhaWxzPgoKPGJyPgoKIyMgRXhlcmNpc2UgLSBDb25kYSBFeHBvcnQKCkZvbGxvd2luZyBhbG9uZyB3aXRoIHRoZSBpbnN0cnVjdG9yLCB3ZSdsbCB1c2UgQ29uZGEncyBleHBvcnQgZnVuY3Rpb25hbGl0eSB0byBjcmVhdGUgYW4gZXhwb3J0IC0gYSBtb3JlIGNvbXBsZXRlIHJlY2lwZSB3aXRoIGFsbCBkZXBlbmRlbmNpZXMgYW5kIHRoZWlyIHZlcnNpb25zIGZ1bGx5IGxpc3RlZC4KCjxkZXRhaWxzPgo8c3VtbWFyeT5Db25kYSBFeHBvcnQgLSBTb2x1dGlvbjwvc3VtbWFyeT4KCmBjb25kYSBhY3RpdmF0ZSBzYW10b29sc19kZWVwdG9vbHNgCgpgY29uZGEgZXhwb3J0ID4gJHtXT1JLU0hPUF9IT01FfS9wcm9qZWN0X2FuYWx5c2lzL2NvbmRhX2VudnMvZXhwb3J0X3NhbXRvb2xzX2RlZXB0b29scy55YW1sYAoKPCEtLSBMSVZFX05PVEU6IEFsbHVkZSB0byB0aGUgaWRlYSBvZiB0aW1lLWJhc2VkIGR1cmFiaWxpdHkgLS0+Cgo8L2RldGFpbHM+Cgo8YnI+CgojIyBFeGVyY2lzZSAtIGBzcnVuYCwgQ29uZGEsIEZpbHRlcmluZyBvdXIgQkFNcwoKRm9sbG93aW5nIGFsb25nIHdpdGggdGhlIGluc3RydWN0b3IsIHdlJ2xsIGxhdW5jaCBhbiBpbnRlcmFjdGl2ZSBqb2Igd2l0aCBgc3J1bmAuIE9uY2Ugd2UndmUgZW50ZXJlZCB0aGUgcnVubmluZyBqb2IsIHdlJ2xsIGFjdGl2YXRlIG91ciBjb25kYSBlbnZpcm9ubWVudCBhbmQgdXNlIHNhbXRvb2xzIHRvIGZpbHRlciBvdXIgQkFNIGZpbGUganVzdCBhcyB3ZSBkaWQgaW4gdGhlIGBsbW9kYCBleGVyY2lzZXMuCgo8IS0tIExJVkVfTk9URTogV2UnbGwganVzdCBkbyBzYW1wbGVfQSAtLT4KCjwhLS0gTElWRV9OT1RFOiBNYWtlIHN1cmUgdG8gYG1vZHVsZSBsaXN0YCBhbmQgY29uZmlybSB3ZSdyZSBub3QgdXNpbmcgYHNhbXRvb2xzYCAtLT4KCjxkZXRhaWxzPgo8c3VtbWFyeT5gc3J1bmAsIENvbmRhLCBGaWx0ZXJpbmcgQkFNIC0gU29sdXRpb248L3N1bW1hcnk+Cgpgc3J1biAtLXB0eSAtLWpvYi1uYW1lPSR7VVNFUn1fY29uZGFfZmlsdGVyYmFtcyAtLWFjY291bnQ9YmlvaW5mX3drc2hwX2NsYXNzIC0tcGFydGl0aW9uIHN0YW5kYXJkIC0tbWVtPTIwMDAgLS1jcHVzLXBlci10YXNrPTIgLS10aW1lPTAwOjMwOjAwIC9iaW4vYmFzaGAKCmBzYW10b29scyB2aWV3IC1vIGZpbHRlcl92aXpfY29uZGEvc2FtcGxlX0EuY2hyMTkuYmFtIGlucHV0X2JhbXMvc2FtcGxlX0EuZ2Vub21lLmJhbSAxOWAKCj5Ob3RlOiBNYWtlIHN1cmUgdG8gYG1vZHVsZSBsaXN0YCBhbmQgY29uZmlybSB3ZSdyZSBub3QgdXNpbmcgdGhlIHNhbXRvb2xzIG1vZHVsZSAtIHRoZSBMTU9EIG1vZHVsZSBhbmQgY29uZGEgZW52aXJvbm1lbnQgd291bGQgaW50ZXJmZXJlIHdpdGggb25lIGFub3RoZXIhCgo8L2RldGFpbHM+Cgo8YnI+CgojIyBJbmRlcGVuZGVudCBFeGVyY2lzZSAtIFNCQVRDSCwgQ29uZGEsIEZpbHRlcmluZyBvdXQgQkFNcwoKW0xpbmsgdG8gSW5kZXBlbmRlbnQgRXhlcmNpc2VdKGV4ZXJjaXNlLTIuaHRtbCkKCjxicj4KCiMjIEV4ZXJjaXNlIC0gYHNydW5gLCBDb25kYSwgU2FuaXR5IENoZWNrIGFuZCBJbmRleCBCQU0KCjwhLS0gTElWRV9OT1RFOiBKdXN0IGRvaW5nIHNhbXBsZV9BIC0tPgoKRm9sbG93aW5nIGFsb25nIHdpdGggdGhlIGluc3RydWN0b3IsIHdlJ2xsIGxhdW5jaCBhbiBpbnRlcmFjdGl2ZSBqb2Igd2l0aCBgc3J1bmAuIE9uY2Ugd2UndmUgZW50ZXJlZCB0aGUgcnVubmluZyBqb2IsIHdlJ2xsIGFjdGl2YXRlIG91ciBjb25kYSBlbnZpcm9ubWVudCBhbmQgdXNlIHNhbXRvb2xzIHRvIHBlcmZvcm0gYSBzYW5pdHkgY2hlY2sgb24gb3VyIGZpbHRlcmVkIEJBTSBmaWxlcy4gV2UnbGwgYWxzbyBpbmRleCBvbmUgb2Ygb3VyIEJBTSBmaWxlcyAtIHNhbXBsZV9BLiBUaGlzIHdpbGwgc2V0IHVzIHVwIGZvciB0aGUgbmV4dCBzdGVwLgoKPGRldGFpbHM+CjxzdW1tYXJ5PmBzcnVuYCwgQ29uZGEsIFNhbml0eSBDaGVjaywgSW5kZXggQkFNIC0gU29sdXRpb248L3N1bW1hcnk+Cgpgc3J1biAtLXB0eSAtLWpvYi1uYW1lPSR7VVNFUn1fY29uZGFfY2hlY2tfYW5kX2luZGV4IC0tYWNjb3VudD1iaW9pbmZfd2tzaHBfY2xhc3MgLS1wYXJ0aXRpb24gc3RhbmRhcmQgLS1tZW09MjAwMCAtLWNwdXMtcGVyLXRhc2s9MiAtLXRpbWU9MDA6MzA6MDAgL2Jpbi9iYXNoYAoKYGNvbmRhIGFjdGl2YXRlIHNhbXRvb2xzX2RlZXB0b29sc2AKCmBzYW10b29scyB2aWV3IGZpbHRlcl92aXpfY29uZGEvc2FtcGxlX0EuY2hyMTkuYmFtIHwgd2MgLWxgCgpgc2FtdG9vbHMgaW5kZXggZmlsdGVyX3Zpel9jb25kYS9zYW1wbGVfQS5jaHIxOS5iYW1gCgo8L2RldGFpbHM+CgoKIyMgRXhlcmNpc2UgLSBgc3J1bmAsIENvbmRhLCBDcmVhdGluZyBCaWd3aWdzCgpOb3cgd2UnbGwgY3JlYXRlIGZpbGVzIGZvciB2aXN1YWxpemF0aW9uIG9mIG91ciBmaWx0ZXJlZCBCQU1zLCBhcyBhbm90aGVyIHdheSB0byBjaGVjayBvdXIgcmVzdWx0cy4KCkZvbGxvd2luZyBhbG9uZyB3aXRoIHRoZSBpbnN0cnVjdG9yLCB3ZSdsbCBsYXVuY2ggYW4gaW50ZXJhY3RpdmUgam9iIHdpdGggYHNydW5gLiBPbmNlIHdlJ3ZlIGVudGVyZWQgdGhlIHJ1bm5pbmcgam9iLCB3ZSdsbCBhY3RpdmF0ZSBvdXIgY29uZGEgZW52aXJvbm1lbnQgYW5kIHVzZSBgYmFtQ292ZXJhZ2VgIHRvIGNyZWF0ZSBhIGJpZ3dpZyBmaWxlIGZvciBzYW1wbGVfQS4KCjxkZXRhaWxzPgo8c3VtbWFyeT5gc3J1bmAsIENvbmRhLCBDcmVhdGUgQmlnd2lnIC0gU29sdXRpb248L3N1bW1hcnk+Cgpgc3J1biAtLXB0eSAtLWpvYi1uYW1lPSR7VVNFUn1fY29uZGFfYmlnd2lncyAtLWFjY291bnQ9YmlvaW5mX3drc2hwX2NsYXNzIC0tcGFydGl0aW9uIHN0YW5kYXJkIC0tbWVtPTIwMDAgLS1jcHVzLXBlci10YXNrPTIgLS10aW1lPTAwOjMwOjAwIC9iaW4vYmFzaGAKCmBiYW1Db3ZlcmFnZSAtYiBmaWx0ZXJfdml6X2NvbmRhL3NhbXBsZV9BLmNocjE5LmJhbSAtbyBmaWx0ZXJfdml6X2NvbmRhL3NhbXBsZV9BLmNocjE5LmJpZ3dpZ2AKCj5Ob3RlOiBOb3cgd2UnbGwgZ28gdG8gaHR0cHM6Ly9pZ3Yub3JnL2FwcC8gYW5kIHZpc3VhbGl6ZSB3aGF0IHRoaXMgbG9va3MgbGlrZS4gU2VsZWN0IG1tMTAgYXMgZ2Vub21lLgo+VHJhY2sgVVJMOiBodHRwczovL3VtYmlvaW5mY29yZS13b3Jrc2hvcC5zMy5hbWF6b25hd3MuY29tL3NhbXBsZV9BLmZpbHRlcmVkLmJpZ3dpZwoKPC9kZXRhaWxzPgoKIyMgUmV2aWV3CgpDb25kYSBhbGxvd3MgdXMgdG8gaW5zdGFsbCBhbmQgbWFuYWdlIG91ciBvd24gc29mdHdhcmUuIE9uIGEgbXVsdGktdXNlciBzeXN0ZW0gbGlrZSBHcmVhdCBMYWtlcywgdGhpcyBpcyB2ZXJ5IHBvd2VyZnVsLgoKQ29uZGEgaXMgYmFzaWNhbGx5IGEgcGFja2FnZSBtYW5hZ2VyIC0gaXQgaXMgc29mdHdhcmUgdGhhdCBtYW5hZ2VzIGJ1bmRsZXMgb2Ygb3RoZXIgc29mdHdhcmUgLSBidXQgaXQgaGFzIGFkZGl0aW9uYWwgY2FwYWJpbGl0aWVzIHRoYXQgbWFrZSBpdCBncmVhdCBmb3IgcmVwcm9kdWNpYmlsaXR5LgoKPCEtLSBMSVZFX05PVEU6IFJlbWluZCBhYm91dCBjb25kYSByZWNpcGVzIHRoYXQgd2UgY2FuIHNoYXJlLCBhbGxvd2luZyB0aGVtIHRvIGJlIGJ1aWx0IG9uIG90aGVyIHN5c3RlbXMgb3IgYnkgb3RoZXIgcGVvcGxlIC0tPgoKSXQgcHJvdmlkZXMgcGx1Z2dhYmlsaXR5IHRvIG91ciBzb2Z0d2FyZSBuZWVkcy4gV2hlbiB3ZSBuZWVkIGEgd2lkZSB2YXJpZXR5IG9mIHRvb2xzIGF0IGRpZmZlcmVudCB0aW1lcywgd2l0aCBzb21lIGJlaW5nIGluY29tcGF0aWJsZSB3aXRoIG9uZSBhbm90aGVyLCB0aGlzIGJlY29tZXMgY3JpdGljYWwuCgpXaGVuIGdpdmVuIGEgc2V0IG9mIHNvZnR3YXJlIHJlcXVpcmVtZW50IHNwZWNpZmljYXRpb25zLCBDb25kYSBoYW5kbGVzIGFsbCBvZiB0aGUgZGVwZW5kZW5jaWVzIGFuZCBjcmVhdGVzIGEgc29tZXdoYXQgY29udGFpbmVkIGVudmlyb25tZW50IHRoYXQgd2UgY2FuIGBhY3RpdmF0ZWAgYW5kIGBkZWFjdGl2YXRlYCBhcyBuZWVkZWQuCgo8YnI+CgojIyMgSGFuZHkgTGlua3MKCi0gW01pbmljb25kYSBpbnN0YWxsYXRpb24gaW5zdHJ1Y3Rpb25zXShodHRwczovL2RvY3MuYW5hY29uZGEuY29tL2ZyZWUvbWluaWNvbmRhL21pbmljb25kYS1pbnN0YWxsLykKLSBbTWluaWNvbmRhIGRvd25sb2FkcyBwYWdlXShodHRwczovL3JlcG8uYW5hY29uZGEuY29tL21pbmljb25kYS9NaW5pY29uZGEzLWxhdGVzdC1MaW51eC14ODZfNjQuc2gpCi0gW0NvbmZpZ3VyYXRpb24gd2l0aCBjb25kYXJjXShodHRwczovL2NvbmRhLmlvL3Byb2plY3RzL2NvbmRhL2VuL2xhdGVzdC91c2VyLWd1aWRlL2NvbmZpZ3VyYXRpb24vdXNlLWNvbmRhcmMuaHRtbCkKCjxicj4KCi0tLQoKCnwgW1ByZXZpb3VzIGxlc3Nvbl0oTW9kdWxlMDNiX2dyZWF0X2xha2VzX2NsdXN0ZXIuaHRtbCkgfCBbVG9wIG9mIHRoaXMgbGVzc29uXSgjdG9wKSB8IFtOZXh0IGxlc3Nvbl0oTW9kdWxlMDVfY29udGFpbmVyc19kb2NrZXJfc2luZ3VsYXJpdHkuaHRtbCkgfAp8IDotLS0gfCA6LS0tLTogfCAtLS06IHwKCg==