In this module, we will:

Commonalities Between Software Management Tools

Overview of Containerized Software

The software management tools we’ve discussed so far are extremely helpful and important for reproducible research. However, they do have some limitations that we’ve mentioned, which we’ll bring into focus and elaborate in this module as we consider some additional options - containerized software. We’ll discuss in a moment what we mean by this term, but to start I’ll mention some of the additional things that containerized software can grant us:

The simplest way to think of containers is as if there is a virtual computer system, running inside of your computer. You can imagine that the hardware layout of your typical computer can also be recreated through software, and that is kind of what is happening when we use software containers. This is of course a gross simplification, but for now we can think of it in this way.




You may have already heard the term “virtual machine” in the past, and this is a technology that is related to, but distinct from containers. We will be discussing containerization software specifically, and focusing on these because containers are more efficient, streamlined, and perfectly suited for reproducibility, when compared to virtual machines. When comparing between these, some things I’d like to highlight that work highly in favor of containerized software are:


To cover some terminology and to give a broad overview of the flow of using containers, we’ll briefly discuss this figure.


Sharing Software Environments


Let’s revisit the topic of sharing analyses once again. We’ve discussed how some of you may have experienced friction when trying to share some analysis code with a collaborator or with a colleague - when they try to run the analysis on their machine they are unsuccessful. Maybe they don’t have the required software. Maybe they have some certain configuration details that are set incorrectly. Or maybe the software that you’ve given them is actually incompatible with some software that’s running on their system. In the figure above, we’ve shown a very simplistic idea for getting around these issues, which is to just share your entire laptop with your colleague. However, you can imagine that there are many practical reasons why this is not a very good solution.

We’ve discussed some solutions that can aid in these sharing tasks, without having to resort to actually sharing your entire computer with your colleague. In many ways, these solutions allow us to get past this friction. Let’s take a moment to think about our experiences with LMOD modules and conda, and describe their strengths and limitations.


Now we shift our focus back to containers, and how they can offer some solutions to the limitations of other options. An important aspect of containers that makes them powerful is that after they’re built according to specifications, the container is basically unchangeable. Once again, this is a simplification, but for the most common usages, in terms of shareability and reproducibility, we can consider it to be so.

In addition to being stable once created, it is also shareable after creation. This benefit cannot be overstated. By being able to share the static environment after it has been created, and to know that it will remain stable and usable far into the future, we can ensure reproducibility far into the future as well.

There are several ways that you can share containerized software:

  • Sharing the specification files (akin to sharing a conda recipe)
  • Sharing by hosting the images in an accessible repository (Docker Hub, Quay IO, Biocontainers)
  • Sharing the image directly (Singularity Image File)


Environment Isolation

Another important aspect of containers is their relative isolation from the host system when compared to something like Conda. Remember that when we are running software within a container, the software interacts with a completely virtualized version of the hardware system. By nature of that, that means that there is additional separation between the software in the container and the software that is outside of the container. Conda makes no such distinction, but rather uses a couple of tricks to make it easy to switch between environments.


Static Images are Stable Across Time




We’ll briefly revisit this idea and relate it to the incompatibility scenario that we described in the previous module.


Not Just Software

Containers can be bundled with more than software - things like environment variables, configuration files, etc. can be included. For some tools, this is a critical aspect of providing simple plug-and-play usability.


Docker

We’ve discussed containerized software as a concept, and now it’s time to briefly discuss a specific containerization tool - in fact arguably the most widely used container software today - Docker.


Example of a Dockerfile
FROM centos:centos7

RUN yum -y install wget gdb

COPY foo.yaml /tmp

RUN wget -P /tmp "https://download.example.com/example-installer-3.2.1.el7.x86_64.rpm" && \
    rpm -i /tmp/example-installer-3.2.1.el7.x86_64.rpm && \
    rm /tmp/example-installer-3.2.1.el7.x86_64.rpm

ENV PATH ${PATH}:/opt/foo/bin


Singularity

Example of a Singularity definition file:

BootStrap: debootstrap
OSVersion: stable
MirrorURL: http://ftp.us.debian.org/debian/

%runscript
    echo "This is what happens when you run the container..."

%post
    echo "Hello from inside the container"
    apt-get update
    apt-get -y install fortune cowsay lolcat

%environment
    export PATH=$PATH:/usr/games

Some Quick Notes on Singularity

Exercise - srun, singularity shell, Filter and Index a BAM

Following along with the instructor, we’ll launch an interactive job with srun. Once we’ve entered the running job, we will use singularity with an existing samtools docker image and launch a ‘singularity shell’. With the samtools software now available, we’ll filter and index sample_A.

After we complete this, we’ll delete the filtered & indexed BAM file, to start fresh for our SBATCH exercises.

srun, singularity shell, Filter and Index BAM - Solution

srun --pty --job-name=${USER}_singularity_filter_and_index --account=bioinf_wkshp_class --partition standard --mem=2000 --cpus-per-task=2 --time=00:20:00 /bin/bash

mkdir filter_viz_singularity

module load singularity

singularity shell docker://quay.io/biocontainers/samtools:1.20--h50ea8bc_0

samtools view -o filter_viz_singularity/sample_A.chr19.bam input_bams/sample_A.genome.bam 19

We’ll also do a sanity check here:

samtools view filter_viz_singularity/sample_A.chr19.bam | wc -l

As well as index the BAM (bamCoverage will need the index in the next step)

samtools index filter_viz_singularity/sample_A.chr19.bam


Exercise - SBATCH, singularity exec, Filter and Index a BAM

Following along with the instructor, we’ll use the nano editor to create an SBATCH file and use singularity exec to filter and index our sample_A BAM file. We’ll submit the job with sbatch and review its log file and output.


Exercise - SBATCH, singularity exec, Filter and Index all BAMs

Following along with the instructor, we’ll quickly create additional sbatch files that can use singularity exec to filter and index the rest our samples. We’ll submit those jobs with sbatch and review once again.


Exercise - srun, singularity shell, Sanity Check All Samples

Following along with the instructor, we’ll launch an interactive job with srun. Once we’ve entered the running job, we’ll use singularity shell and quickly perform a sanity check on all of our filtered BAM files, counting the entries in each file.


Exercise - srun, singularity exec, Create a Bigwig

Following along with the instructor, we’ll launch an interactive job with srun. Once we’ve entered the running job, we’ll use singularity with an existing deeptools docker image and launch a ‘singularity shell’. With the bamCoverage command now available, we’ll create a Bigwig for sample_A.

After we complete this, we’ll delete the newly created Bigwig file, so that we can start fresh on our SBATCH exercises.

srun, singularity exec, Create a Bigwig - Solution

srun --pty --job-name=${USER}_singularity_create_bigwig --account=bioinf_wkshp_class --partition standard --mem=2000 --cpus-per-task=2 --time=00:30:00 /bin/bash

module list

module load singularity

singularity exec docker://quay.io/biocontainers/deeptools:3.5.5--pyhdfd78af_0 bamCoverage -b filter_viz_singularity/sample_A.chr19.bam -o filter_viz_singularity/sample_A.chr19.bigwig


Exercise - SBATCH, Singularity, Create Bigwigs for All BAMs

In the same pattern as we’ve established for our filtering task, we’ll with a single sample, sample_A and craft an SBATCH file to create a Bigwig file for that one sample before moving on to others.


Demonstration - Remote Build of a Singularity Image

So far we’ve only demonstrated uses where we use existing docker or singularity images. While we’ve shown that this is powerful and easy to use, we aren’t taking full advantage of singularity’s capabilities without building new images.

This topic is outside the scope of this workshop, but we want to demonstrate that it is possible to build singularity containers. It’s not quite straightforward to do so on Great Lakes, but with a little bit of extra setup it is possible to use a remote builder, which we’ll demonstrate.

For more information about the remote build process, see the documentation here:

Link to documentation on remote builds

Demo Remote Build - Solution

We’ll create a singularity definition file called cowsay.def

nano cowsay.def

Contents of cowsay.def are shown in the singularity definition file shown in the above lesson.

Note, to use the remote builder with the --remote flag, you must follow the instructions on remote builds above - create an account and generate a token online, then use singularity remote login.

singularity build --remote cowsay.sif cowsay.def

After this, we have a singularity image file, cowsay.sif that we can use with singularity shell cowsay.sif or singularity exec cowsay.sif cowsay Mooo





Review of LMOD, Conda, Docker, and Singularity

In choosing any tool, we must consider the inherent trade-offs between simplicity, portability, and durability.



Previous lesson Top of this lesson Next lesson
LS0tCnRpdGxlOiAiQ29udGFpbmVycyAtIERvY2tlciBhbmQgU2luZ3VsYXJpdHkiCmF1dGhvcjogIlVNIEJpb2luZm9ybWF0aWNzIENvcmUiCm91dHB1dDoKICAgICAgICBodG1sX2RvY3VtZW50OgogICAgICAgICAgICBpbmNsdWRlczoKICAgICAgICAgICAgICAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICAgICAgICAgdGhlbWU6IHBhcGVyCiAgICAgICAgICAgIG51bWJlcl9zZWN0aW9uczogZmFsc2UKICAgICAgICAgICAgZmlnX2NhcHRpb246IHRydWUKICAgICAgICAgICAgbWFya2Rvd246IEdGTQogICAgICAgICAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCi0tLQo8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgpib2R5eyAvKiBOb3JtYWwgICovCiAgICAgIGZvbnQtc2l6ZTogMTRwdDsKICB9CnByZSB7CiAgZm9udC1zaXplOiAxMnB0Cn0KPC9zdHlsZT4KCkluIHRoaXMgbW9kdWxlLCB3ZSB3aWxsOgoKKiBsZWFybiBhYm91dCBjb250YWluZXIgc3lzdGVtcyBEb2NrZXIgYW5kIFNpbmd1bGFyaXR5CiogZGlzY3VzcyB0aGUgd2hhdCBhbmQgd2h5IG9mIGNvbnRhaW5lcml6YXRpb24KKiBjb21wYXJlIGNvbnRhaW5lciBzb2x1dGlvbnMgdG8gb3RoZXIgc29mdHdhcmUgbWFuYWdlbWVudCB0b29scyB0aGF0IHdlJ3ZlIGRpc2N1c3NlZAoqIGxlYXJuIGhvdyB0byB1dGlsaXplIHB1YmxpY2FsbHkgYXZhaWxhYmxlIERvY2tlciBvciBTaW5ndWxhcml0eSBjb250YWluZXJzCiogc2VlIGhvdyB0byBidWlsZCBhIG5ldyBzaW5ndWxhcml0eSBjb250YWluZXIKCiMjIENvbW1vbmFsaXRpZXMgQmV0d2VlbiBTb2Z0d2FyZSBNYW5hZ2VtZW50IFRvb2xzCgohW10oaW1hZ2VzL01vZHVsZTA1X3NvZnR3YXJlX21hbmFnZW1lbnRfY29tbW9uYWxpdGllcy5wbmcpCgojIyBPdmVydmlldyBvZiBDb250YWluZXJpemVkIFNvZnR3YXJlCgpUaGUgc29mdHdhcmUgbWFuYWdlbWVudCB0b29scyB3ZSd2ZSBkaXNjdXNzZWQgc28gZmFyIGFyZSBleHRyZW1lbHkgaGVscGZ1bCBhbmQgaW1wb3J0YW50IGZvciByZXByb2R1Y2libGUgcmVzZWFyY2guIEhvd2V2ZXIsIHRoZXkgZG8gaGF2ZSBzb21lIGxpbWl0YXRpb25zIHRoYXQgd2UndmUgbWVudGlvbmVkLCB3aGljaCB3ZSdsbCBicmluZyBpbnRvIGZvY3VzIGFuZCBlbGFib3JhdGUgaW4gdGhpcyBtb2R1bGUgYXMgd2UgY29uc2lkZXIgc29tZSBhZGRpdGlvbmFsIG9wdGlvbnMgLSBjb250YWluZXJpemVkIHNvZnR3YXJlLiBXZSdsbCBkaXNjdXNzIGluIGEgbW9tZW50IHdoYXQgd2UgbWVhbiBieSB0aGlzIHRlcm0sIGJ1dCB0byBzdGFydCBJJ2xsIG1lbnRpb24gc29tZSBvZiB0aGUgYWRkaXRpb25hbCB0aGluZ3MgdGhhdCBjb250YWluZXJpemVkIHNvZnR3YXJlIGNhbiBncmFudCB1czoKCi0gQWRkaXRpb25hbCBwb3J0YWJpbGl0eQotIEFkZGl0aW9uYWwgaXNvbGF0aW9uIG9mIGNvbXB1dGUgZW52aXJvbm1lbnRzCi0gQWRkaXRpb25hbCBsb25nLXRlcm0gcmVsaWFiaWxpdHkKCgpUaGUgc2ltcGxlc3Qgd2F5IHRvIHRoaW5rIG9mIGNvbnRhaW5lcnMgaXMgYXMgaWYgdGhlcmUgaXMgYSB2aXJ0dWFsIGNvbXB1dGVyIHN5c3RlbSwgcnVubmluZyBpbnNpZGUgb2YgeW91ciBjb21wdXRlci4gWW91IGNhbiBpbWFnaW5lIHRoYXQgdGhlIGhhcmR3YXJlIGxheW91dCBvZiB5b3VyIHR5cGljYWwgY29tcHV0ZXIgY2FuIGFsc28gYmUgcmVjcmVhdGVkIHRocm91Z2ggc29mdHdhcmUsIGFuZCB0aGF0IGlzIGtpbmQgb2Ygd2hhdCBpcyBoYXBwZW5pbmcgd2hlbiB3ZSB1c2Ugc29mdHdhcmUgY29udGFpbmVycy4gVGhpcyBpcyBvZiBjb3Vyc2UgYSBncm9zcyBzaW1wbGlmaWNhdGlvbiwgYnV0IGZvciBub3cgd2UgY2FuIHRoaW5rIG9mIGl0IGluIHRoaXMgd2F5LgoKPGJyPgo8YnI+CgohW10oaW1hZ2VzL01vZHVsZTA1X2NvbXB1dGVyX2luX2NvbXB1dGVyLnBuZykKCjxicj4KCllvdSBtYXkgaGF2ZSBhbHJlYWR5IGhlYXJkIHRoZSB0ZXJtICJ2aXJ0dWFsIG1hY2hpbmUiIGluIHRoZSBwYXN0LCBhbmQgdGhpcyBpcyBhIHRlY2hub2xvZ3kgdGhhdCBpcyByZWxhdGVkIHRvLCBidXQgZGlzdGluY3QgZnJvbSBjb250YWluZXJzLiBXZSB3aWxsIGJlIGRpc2N1c3NpbmcgY29udGFpbmVyaXphdGlvbiBzb2Z0d2FyZSBzcGVjaWZpY2FsbHksIGFuZCBmb2N1c2luZyBvbiB0aGVzZSBiZWNhdXNlIGNvbnRhaW5lcnMgYXJlIG1vcmUgZWZmaWNpZW50LCBzdHJlYW1saW5lZCwgYW5kIHBlcmZlY3RseSBzdWl0ZWQgZm9yIHJlcHJvZHVjaWJpbGl0eSwgd2hlbiBjb21wYXJlZCB0byB2aXJ0dWFsIG1hY2hpbmVzLiBXaGVuIGNvbXBhcmluZyBiZXR3ZWVuIHRoZXNlLCBzb21lIHRoaW5ncyBJJ2QgbGlrZSB0byBoaWdobGlnaHQgdGhhdCB3b3JrIGhpZ2hseSBpbiBmYXZvciBvZiBjb250YWluZXJpemVkIHNvZnR3YXJlIGFyZToKCi0gVGhlIHVzYWdlIG9mIHN0YW5kYXJkaXplZCBzcGVjaWZpY2F0aW9uIGZpbGVzCiAgLSBlLmcuIERvY2tlcmZpbGVzLCBTaW5ndWxhcml0eSBkZWZpbml0aW9uIGZpbGVzCiAgLSBhbGxvdyBmb3Igc3VwZXJpb3IgdHJhbnNwYXJlbmN5IGFuZCByZXByb2R1Y2liaWxpdHkuCi0gVGhlIGxheWVyZWQgbmF0dXJlIG9mIGNvbnRhaW5lciBhcmNoaXRlY3R1cmUKICAtIER1cmluZyBjcmVhdGlvbiwgdGhlIHJlc3VsdCBvZiBlYWNoIHRhc2sgaXMgZm9ybWVkIGludG8gaXRzIG93biBsYXllcgogIC0gVGhlIHNlcGFyYXRlIGxheWVycyBhcmUgInN0YWNrZWQiIG9uZSBvdmVyIGFub3RoZXIgdW50aWwgdGhlIGVudGlyZSBlbnZpcm9ubWVudCBpcyBidWlsdAogIC0gQWxsb3dzIGZvciBoaWdoIHRyYW5zcGFyZW5jeSBhbmQgcG9ydGFiaWxpdHksIGFuZCBzdHJlYW1saW5lcyB0aGUgYnVpbGQgcHJvY2VzcwoKPGJyPgoKIVtdKGltYWdlcy9Nb2R1bGUwNV9maWxlX2ltYWdlX2NvbnRhaW5lcl9nZW5lcmFsaXplZC5wbmcpCgpUbyBjb3ZlciBzb21lIHRlcm1pbm9sb2d5IGFuZCB0byBnaXZlIGEgYnJvYWQgb3ZlcnZpZXcgb2YgdGhlIGZsb3cgb2YgdXNpbmcgY29udGFpbmVycywgd2UnbGwgYnJpZWZseSBkaXNjdXNzIHRoaXMgZmlndXJlLgoKPGJyPgoKCiMjIyBTaGFyaW5nIFNvZnR3YXJlIEVudmlyb25tZW50cwoKPGJyPgoKIVtdKGltYWdlcy9Nb2R1bGUwNV9zaGFyaW5nX2FuYWx5c2VzLnBuZykKCgpMZXQncyByZXZpc2l0IHRoZSB0b3BpYyBvZiBzaGFyaW5nIGFuYWx5c2VzIG9uY2UgYWdhaW4uIFdlJ3ZlIGRpc2N1c3NlZCBob3cgc29tZSBvZiB5b3UgbWF5IGhhdmUgZXhwZXJpZW5jZWQgZnJpY3Rpb24gd2hlbiB0cnlpbmcgdG8gc2hhcmUgc29tZSBhbmFseXNpcyBjb2RlIHdpdGggYSBjb2xsYWJvcmF0b3Igb3Igd2l0aCBhIGNvbGxlYWd1ZSAtIHdoZW4gdGhleSB0cnkgdG8gcnVuIHRoZSBhbmFseXNpcyBvbiB0aGVpciBtYWNoaW5lIHRoZXkgYXJlIHVuc3VjY2Vzc2Z1bC4gTWF5YmUgdGhleSBkb24ndCBoYXZlIHRoZSByZXF1aXJlZCBzb2Z0d2FyZS4gTWF5YmUgdGhleSBoYXZlIHNvbWUgY2VydGFpbiBjb25maWd1cmF0aW9uIGRldGFpbHMgdGhhdCBhcmUgc2V0IGluY29ycmVjdGx5LiBPciBtYXliZSB0aGUgc29mdHdhcmUgdGhhdCB5b3UndmUgZ2l2ZW4gdGhlbSBpcyBhY3R1YWxseSBpbmNvbXBhdGlibGUgd2l0aCBzb21lIHNvZnR3YXJlIHRoYXQncyBydW5uaW5nIG9uIHRoZWlyIHN5c3RlbS4gSW4gdGhlIGZpZ3VyZSBhYm92ZSwgd2UndmUgc2hvd24gYSB2ZXJ5IHNpbXBsaXN0aWMgaWRlYSBmb3IgZ2V0dGluZyBhcm91bmQgdGhlc2UgaXNzdWVzLCB3aGljaCBpcyB0byBqdXN0IHNoYXJlIHlvdXIgZW50aXJlIGxhcHRvcCB3aXRoIHlvdXIgY29sbGVhZ3VlLiBIb3dldmVyLCB5b3UgY2FuIGltYWdpbmUgdGhhdCB0aGVyZSBhcmUgbWFueSBwcmFjdGljYWwgcmVhc29ucyB3aHkgdGhpcyBpcyBub3QgYSB2ZXJ5IGdvb2Qgc29sdXRpb24uIAoKV2UndmUgZGlzY3Vzc2VkIHNvbWUgc29sdXRpb25zIHRoYXQgY2FuIGFpZCBpbiB0aGVzZSBzaGFyaW5nIHRhc2tzLCB3aXRob3V0IGhhdmluZyB0byByZXNvcnQgdG8gYWN0dWFsbHkgc2hhcmluZyB5b3VyIGVudGlyZSBjb21wdXRlciB3aXRoIHlvdXIgY29sbGVhZ3VlLiBJbiBtYW55IHdheXMsIHRoZXNlIHNvbHV0aW9ucyBhbGxvdyB1cyB0byBnZXQgcGFzdCB0aGlzIGZyaWN0aW9uLiBMZXQncyB0YWtlIGEgbW9tZW50IHRvIHRoaW5rIGFib3V0IG91ciBleHBlcmllbmNlcyB3aXRoIExNT0QgbW9kdWxlcyBhbmQgY29uZGEsIGFuZCBkZXNjcmliZSB0aGVpciBzdHJlbmd0aHMgYW5kIGxpbWl0YXRpb25zLgoKPCEtLSBMSVZFX05PVEU6IEhpZ2hsaWdodCB0aGUgbGltaXRhdGlvbiBvZiBjb25kYSAtIHdoaWxlIHdlIGNhbiBzaGFyZSByZWNpcGVzLCB3ZSBjYW4ndCByZWFsbHkgc2hhcmUgdGhlIGJ1aWx0IGVudmlyb25tZW50LiBSZWxhdGUgdG8gc3RhbGUgbGlua3Mgb24gdGhlIGludGVybmV0IC0gaXQncyBub3QgZ3VhcmFudGVlZCB0byBzdGF5IGludGFjdCBmb3JldmVyISAtLT4KCjxicj4KCk5vdyB3ZSBzaGlmdCBvdXIgZm9jdXMgYmFjayB0byBjb250YWluZXJzLCBhbmQgaG93IHRoZXkgY2FuIG9mZmVyIHNvbWUgc29sdXRpb25zIHRvIHRoZSBsaW1pdGF0aW9ucyBvZiBvdGhlciBvcHRpb25zLiBBbiBpbXBvcnRhbnQgYXNwZWN0IG9mIGNvbnRhaW5lcnMgdGhhdCBtYWtlcyB0aGVtIHBvd2VyZnVsIGlzIHRoYXQgYWZ0ZXIgdGhleSdyZSBidWlsdCBhY2NvcmRpbmcgdG8gc3BlY2lmaWNhdGlvbnMsIHRoZSBjb250YWluZXIgaXMgYmFzaWNhbGx5IHVuY2hhbmdlYWJsZS4gT25jZSBhZ2FpbiwgdGhpcyBpcyBhIHNpbXBsaWZpY2F0aW9uLCBidXQgZm9yIHRoZSBtb3N0IGNvbW1vbiB1c2FnZXMsIGluIHRlcm1zIG9mIHNoYXJlYWJpbGl0eSBhbmQgcmVwcm9kdWNpYmlsaXR5LCB3ZSBjYW4gY29uc2lkZXIgaXQgdG8gYmUgc28uCgpJbiBhZGRpdGlvbiB0byBiZWluZyBzdGFibGUgb25jZSBjcmVhdGVkLCBpdCBpcyBhbHNvIHNoYXJlYWJsZSBhZnRlciBjcmVhdGlvbi4gVGhpcyBiZW5lZml0IGNhbm5vdCBiZSBvdmVyc3RhdGVkLiBCeSBiZWluZyBhYmxlIHRvIHNoYXJlIHRoZSBzdGF0aWMgZW52aXJvbm1lbnQgYWZ0ZXIgaXQgaGFzIGJlZW4gY3JlYXRlZCwgYW5kIHRvIGtub3cgdGhhdCBpdCB3aWxsIHJlbWFpbiBzdGFibGUgYW5kIHVzYWJsZSBmYXIgaW50byB0aGUgZnV0dXJlLCB3ZSBjYW4gZW5zdXJlIHJlcHJvZHVjaWJpbGl0eSBmYXIgaW50byB0aGUgZnV0dXJlIGFzIHdlbGwuCgpUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIHRoYXQgeW91IGNhbiBzaGFyZSBjb250YWluZXJpemVkIHNvZnR3YXJlOgoKLSBTaGFyaW5nIHRoZSBzcGVjaWZpY2F0aW9uIGZpbGVzIChha2luIHRvIHNoYXJpbmcgYSBjb25kYSByZWNpcGUpCi0gU2hhcmluZyBieSBob3N0aW5nIHRoZSBpbWFnZXMgaW4gYW4gYWNjZXNzaWJsZSByZXBvc2l0b3J5IChEb2NrZXIgSHViLCBRdWF5IElPLCBCaW9jb250YWluZXJzKQotIFNoYXJpbmcgdGhlIGltYWdlIGRpcmVjdGx5IChTaW5ndWxhcml0eSBJbWFnZSBGaWxlKQoKPGJyPgoKIyMjIEVudmlyb25tZW50IElzb2xhdGlvbgoKIVtdKGltYWdlcy9Nb2R1bGUwNV9jb250YWluZXJfaXNvbGF0aW9uLnBuZykKCkFub3RoZXIgaW1wb3J0YW50IGFzcGVjdCBvZiBjb250YWluZXJzIGlzIHRoZWlyIHJlbGF0aXZlIGlzb2xhdGlvbiBmcm9tIHRoZSBob3N0IHN5c3RlbSB3aGVuIGNvbXBhcmVkIHRvIHNvbWV0aGluZyBsaWtlIENvbmRhLiBSZW1lbWJlciB0aGF0IHdoZW4gd2UgYXJlIHJ1bm5pbmcgc29mdHdhcmUgd2l0aGluIGEgY29udGFpbmVyLCB0aGUgc29mdHdhcmUgaW50ZXJhY3RzIHdpdGggYSBjb21wbGV0ZWx5IHZpcnR1YWxpemVkIHZlcnNpb24gb2YgdGhlIGhhcmR3YXJlIHN5c3RlbS4gQnkgbmF0dXJlIG9mIHRoYXQsIHRoYXQgbWVhbnMgdGhhdCB0aGVyZSBpcyBhZGRpdGlvbmFsIHNlcGFyYXRpb24gYmV0d2VlbiB0aGUgc29mdHdhcmUgaW4gdGhlIGNvbnRhaW5lciBhbmQgdGhlIHNvZnR3YXJlIHRoYXQgaXMgb3V0c2lkZSBvZiB0aGUgY29udGFpbmVyLiBDb25kYSBtYWtlcyBubyBzdWNoIGRpc3RpbmN0aW9uLCBidXQgcmF0aGVyIHVzZXMgYSBjb3VwbGUgb2YgdHJpY2tzIHRvIG1ha2UgaXQgZWFzeSB0byBzd2l0Y2ggYmV0d2VlbiBlbnZpcm9ubWVudHMuIAoKCjxicj4KCiMjIyBTdGF0aWMgSW1hZ2VzIGFyZSBTdGFibGUgQWNyb3NzIFRpbWUKCiFbXShpbWFnZXMvTW9kdWxlMDRfZGVwZW5kZW5jaWVzX2luY29tcGF0aWJpbGl0eS5wbmcpCgo8YnI+Cjxicj4KPGJyPgoKIVtdKGltYWdlcy9Nb2R1bGUwNV9kZXBlbmRlbmNpZXNfb3Zlcl90aW1lLnBuZykKCldlJ2xsIGJyaWVmbHkgcmV2aXNpdCB0aGlzIGlkZWEgYW5kIHJlbGF0ZSBpdCB0byB0aGUgaW5jb21wYXRpYmlsaXR5IHNjZW5hcmlvIHRoYXQgd2UgZGVzY3JpYmVkIGluIHRoZSBwcmV2aW91cyBtb2R1bGUuCgo8YnI+CgojIyMgTm90IEp1c3QgU29mdHdhcmUKCkNvbnRhaW5lcnMgY2FuIGJlIGJ1bmRsZWQgd2l0aCBtb3JlIHRoYW4gc29mdHdhcmUgLSB0aGluZ3MgbGlrZSBlbnZpcm9ubWVudCB2YXJpYWJsZXMsIGNvbmZpZ3VyYXRpb24gZmlsZXMsIGV0Yy4gY2FuIGJlIGluY2x1ZGVkLiBGb3Igc29tZSB0b29scywgdGhpcyBpcyBhIGNyaXRpY2FsIGFzcGVjdCBvZiBwcm92aWRpbmcgc2ltcGxlIHBsdWctYW5kLXBsYXkgdXNhYmlsaXR5LgoKPGJyPgoKIyMgRG9ja2VyCgpXZSd2ZSBkaXNjdXNzZWQgY29udGFpbmVyaXplZCBzb2Z0d2FyZSBhcyBhIGNvbmNlcHQsIGFuZCBub3cgaXQncyB0aW1lIHRvIGJyaWVmbHkgZGlzY3VzcyBhIHNwZWNpZmljIGNvbnRhaW5lcml6YXRpb24gdG9vbCAtIGluIGZhY3QgYXJndWFibHkgdGhlIG1vc3Qgd2lkZWx5IHVzZWQgY29udGFpbmVyIHNvZnR3YXJlIHRvZGF5IC0gRG9ja2VyLgoKIVtdKGltYWdlcy9Nb2R1bGUwNV9maWxlX2ltYWdlX2NvbnRhaW5lcl9kb2NrZXIucG5nKQoKPCEtLSBMSVZFX05PVEU6IEVtcGhhc2l6ZSB0aGF0IGltYWdlcyBhcmUgc2hhcmVhYmxlIG9uIERvY2tlckh1YiwgYW5kIHRoZW4gZXhlY3V0YWJsZS4gQWxzbyB3YW50IHRvIGhpZ2hsaWdodCB0aGF0IHdlIGNhbid0IGRpcmVjdGx5IHVzZSBEb2NrZXIgb24gR3JlYXQgTGFrZXMsIGJ1dCB3ZSBjYW4gc3RpbGwgdXNlIGRvY2tlciBpbWFnZXMgd2l0aCB0aGUgbmV4dCBzb2Z0d2FyZSB3ZSdsbCBkaXNjdXNzLCBzaW5ndWxhcml0eSAtLT4KCjwhLS0gTElWRV9OT1RFOiBFbXBoYXNpemUgdGhhdCBEb2NrZXIgYXNzdW1lcyB0aGF0IHlvdSBoYXZlIGZ1bGwgY29udHJvbCBvdmVyIGEgc3lzdGVtLiBPbiBhbiBIUEMsIHRoaXMgaXMgbm90IHRoZSBjYXNlLCB0aGVyZSBhcmUgc3lzdGVtcyBpbiBwbGFjZSB0aGF0IGtlZXAgdXMgZnJvbSBzdGVwcGluZyBvbiBlYWNoLW90aGVycyB0b2VzLiAtLT4KCjxicj4KCjxkZXRhaWxzPgo8c3VtbWFyeT5FeGFtcGxlIG9mIGEgRG9ja2VyZmlsZTwvc3VtbWFyeT4KCmBgYApGUk9NIGNlbnRvczpjZW50b3M3CgpSVU4geXVtIC15IGluc3RhbGwgd2dldCBnZGIKCkNPUFkgZm9vLnlhbWwgL3RtcAoKUlVOIHdnZXQgLVAgL3RtcCAiaHR0cHM6Ly9kb3dubG9hZC5leGFtcGxlLmNvbS9leGFtcGxlLWluc3RhbGxlci0zLjIuMS5lbDcueDg2XzY0LnJwbSIgJiYgXAogICAgcnBtIC1pIC90bXAvZXhhbXBsZS1pbnN0YWxsZXItMy4yLjEuZWw3Lng4Nl82NC5ycG0gJiYgXAogICAgcm0gL3RtcC9leGFtcGxlLWluc3RhbGxlci0zLjIuMS5lbDcueDg2XzY0LnJwbQoKRU5WIFBBVEggJHtQQVRIfTovb3B0L2Zvby9iaW4KYGBgCgo8L2RldGFpbHM+Cgo8YnI+CgojIyBTaW5ndWxhcml0eQoKIVtdKGltYWdlcy9Nb2R1bGUwNV9maWxlX2ltYWdlX2NvbnRhaW5lcl9zaW5ndWxhcml0eS5wbmcpCgpFeGFtcGxlIG9mIGEgU2luZ3VsYXJpdHkgZGVmaW5pdGlvbiBmaWxlOgoKYGBgCkJvb3RTdHJhcDogZGVib290c3RyYXAKT1NWZXJzaW9uOiBzdGFibGUKTWlycm9yVVJMOiBodHRwOi8vZnRwLnVzLmRlYmlhbi5vcmcvZGViaWFuLwoKJXJ1bnNjcmlwdAogICAgZWNobyAiVGhpcyBpcyB3aGF0IGhhcHBlbnMgd2hlbiB5b3UgcnVuIHRoZSBjb250YWluZXIuLi4iCgolcG9zdAogICAgZWNobyAiSGVsbG8gZnJvbSBpbnNpZGUgdGhlIGNvbnRhaW5lciIKICAgIGFwdC1nZXQgdXBkYXRlCiAgICBhcHQtZ2V0IC15IGluc3RhbGwgZm9ydHVuZSBjb3dzYXkgbG9sY2F0CgolZW52aXJvbm1lbnQKICAgIGV4cG9ydCBQQVRIPSRQQVRIOi91c3IvZ2FtZXMKYGBgCgojIyMgU29tZSBRdWljayBOb3RlcyBvbiBTaW5ndWxhcml0eQoKLSBJdCBpcyBhIHB1cnBvc2UtYnVpbHQgY29udGFpbmVyaXphdGlvbiBzb2Z0d2FyZSBmb3IgYW4gSFBDIHN5c3RlbQotIFNpbmd1bGFyaXR5IGNhbiBhbHNvIHJ1biBEb2NrZXIgY29udGFpbmVycwotIFdlIGhhdmUgc2V2ZXJhbCByZXBvc2l0b3JpZXMgYXQgb3VyIGZpbmdlcnRpcHMKICAtIERvY2tlciBIdWIgLSBodHRwczovL2h1Yi5kb2NrZXIuY29tCiAgLSBCaW9jb250YWluZXJzIC0gaHR0cHM6Ly9iaW9jb250YWluZXJzLnBybwogIC0gUXVheSBJTyAtIGh0dHBzOi8vcXVheS5pbwogIC0gR2FsYXh5IFByb2plY3QgLSBodHRwczovL2RlcG90LmdhbGF4eXByb2plY3Qub3JnL3Npbmd1bGFyaXR5LwoKCjwhLS0gTElWRV9OT1RFOiBEbyBhIGRlbW9uc3RyYXRpb24gb2YgZ29pbmcgdG8gUXVheS5pbyBhbmQgZ2V0dGluZyBzYW10b29scyBhbmQgZGVlcHRvb2xzIGltYWdlcyAtLT4KCiMjIEV4ZXJjaXNlIC0gYHNydW5gLCBgc2luZ3VsYXJpdHkgc2hlbGxgLCBGaWx0ZXIgYW5kIEluZGV4IGEgQkFNCgo8IS0tIExJVkVfTk9URTogQWxzbyBkbyBzYW5pdHkgY2hlY2sgLS0+CgpGb2xsb3dpbmcgYWxvbmcgd2l0aCB0aGUgaW5zdHJ1Y3Rvciwgd2UnbGwgbGF1bmNoIGFuIGludGVyYWN0aXZlIGpvYiB3aXRoIGBzcnVuYC4gT25jZSB3ZSd2ZSBlbnRlcmVkIHRoZSBydW5uaW5nIGpvYiwgd2Ugd2lsbCB1c2Ugc2luZ3VsYXJpdHkgd2l0aCBhbiBleGlzdGluZyBzYW10b29scyBkb2NrZXIgaW1hZ2UgYW5kIGxhdW5jaCBhICdzaW5ndWxhcml0eSBzaGVsbCcuIFdpdGggdGhlIHNhbXRvb2xzIHNvZnR3YXJlIG5vdyBhdmFpbGFibGUsIHdlJ2xsIGZpbHRlciBhbmQgaW5kZXggc2FtcGxlX0EuCgpBZnRlciB3ZSBjb21wbGV0ZSB0aGlzLCB3ZSdsbCBkZWxldGUgdGhlIGZpbHRlcmVkICYgaW5kZXhlZCBCQU0gZmlsZSwgdG8gc3RhcnQgZnJlc2ggZm9yIG91ciBTQkFUQ0ggZXhlcmNpc2VzLgoKPGRldGFpbHM+CjxzdW1tYXJ5PmBzcnVuYCwgYHNpbmd1bGFyaXR5IHNoZWxsYCwgRmlsdGVyIGFuZCBJbmRleCBCQU0gLSBTb2x1dGlvbjwvc3VtbWFyeT4KCmBzcnVuIC0tcHR5IC0tam9iLW5hbWU9JHtVU0VSfV9zaW5ndWxhcml0eV9maWx0ZXJfYW5kX2luZGV4IC0tYWNjb3VudD1iaW9pbmZfd2tzaHBfY2xhc3MgLS1wYXJ0aXRpb24gc3RhbmRhcmQgLS1tZW09MjAwMCAtLWNwdXMtcGVyLXRhc2s9MiAtLXRpbWU9MDA6MjA6MDAgL2Jpbi9iYXNoYAoKYG1rZGlyIGZpbHRlcl92aXpfc2luZ3VsYXJpdHlgCgpgbW9kdWxlIGxvYWQgc2luZ3VsYXJpdHlgCgpgc2luZ3VsYXJpdHkgc2hlbGwgZG9ja2VyOi8vcXVheS5pby9iaW9jb250YWluZXJzL3NhbXRvb2xzOjEuMjAtLWg1MGVhOGJjXzBgCgpgc2FtdG9vbHMgdmlldyAtbyBmaWx0ZXJfdml6X3Npbmd1bGFyaXR5L3NhbXBsZV9BLmNocjE5LmJhbSBpbnB1dF9iYW1zL3NhbXBsZV9BLmdlbm9tZS5iYW0gMTlgCgpXZSdsbCBhbHNvIGRvIGEgc2FuaXR5IGNoZWNrIGhlcmU6Cgpgc2FtdG9vbHMgdmlldyBmaWx0ZXJfdml6X3Npbmd1bGFyaXR5L3NhbXBsZV9BLmNocjE5LmJhbSB8IHdjIC1sYAoKQXMgd2VsbCBhcyBpbmRleCB0aGUgQkFNIChiYW1Db3ZlcmFnZSB3aWxsIG5lZWQgdGhlIGluZGV4IGluIHRoZSBuZXh0IHN0ZXApCgpgc2FtdG9vbHMgaW5kZXggZmlsdGVyX3Zpel9zaW5ndWxhcml0eS9zYW1wbGVfQS5jaHIxOS5iYW1gCgo8L2RldGFpbHM+Cgo8YnI+Cgo8IS0tIExJVkVfTk9URTogU2tpcCBzZXZlcmFsIGV4ZXJjaXNlcyAtLT4KCiMjIEV4ZXJjaXNlIC0gU0JBVENILCBgc2luZ3VsYXJpdHkgZXhlY2AsIEZpbHRlciBhbmQgSW5kZXggYSBCQU0KCkZvbGxvd2luZyBhbG9uZyB3aXRoIHRoZSBpbnN0cnVjdG9yLCB3ZSdsbCB1c2UgdGhlIG5hbm8gZWRpdG9yIHRvIGNyZWF0ZSBhbiBTQkFUQ0ggZmlsZSBhbmQgdXNlIGBzaW5ndWxhcml0eSBleGVjYCB0byBmaWx0ZXIgYW5kIGluZGV4IG91ciBzYW1wbGVfQSBCQU0gZmlsZS4gV2UnbGwgc3VibWl0IHRoZSBqb2Igd2l0aCBgc2JhdGNoYCBhbmQgcmV2aWV3IGl0cyBsb2cgZmlsZSBhbmQgb3V0cHV0LgoKPGJyPgoKIyMgRXhlcmNpc2UgLSBTQkFUQ0gsIGBzaW5ndWxhcml0eSBleGVjYCwgRmlsdGVyIGFuZCBJbmRleCBhbGwgQkFNcwoKRm9sbG93aW5nIGFsb25nIHdpdGggdGhlIGluc3RydWN0b3IsIHdlJ2xsIHF1aWNrbHkgY3JlYXRlIGFkZGl0aW9uYWwgc2JhdGNoIGZpbGVzIHRoYXQgY2FuIHVzZSBgc2luZ3VsYXJpdHkgZXhlY2AgdG8gZmlsdGVyIGFuZCBpbmRleCB0aGUgcmVzdCBvdXIgc2FtcGxlcy4gV2UnbGwgc3VibWl0IHRob3NlIGpvYnMgd2l0aCBgc2JhdGNoYCBhbmQgcmV2aWV3IG9uY2UgYWdhaW4uCgo8YnI+CgojIyBFeGVyY2lzZSAtIGBzcnVuYCwgYHNpbmd1bGFyaXR5IHNoZWxsYCwgU2FuaXR5IENoZWNrIEFsbCBTYW1wbGVzCgpGb2xsb3dpbmcgYWxvbmcgd2l0aCB0aGUgaW5zdHJ1Y3Rvciwgd2UnbGwgbGF1bmNoIGFuIGludGVyYWN0aXZlIGpvYiB3aXRoIGBzcnVuYC4gT25jZSB3ZSd2ZSBlbnRlcmVkIHRoZSBydW5uaW5nIGpvYiwgd2UnbGwgdXNlIGBzaW5ndWxhcml0eSBzaGVsbGAgYW5kIHF1aWNrbHkgcGVyZm9ybSBhIHNhbml0eSBjaGVjayBvbiBhbGwgb2Ygb3VyIGZpbHRlcmVkIEJBTSBmaWxlcywgY291bnRpbmcgdGhlIGVudHJpZXMgaW4gZWFjaCBmaWxlLgoKPCEtLSBMSVZFX05PVEU6IHdyaXRlIG91ciBzYW5pdHkgY2hlY2sgY29tbWFuZCB0byBhIGZpbGUgdG8gcmVjb3JkIGl0IGZvciBmdXR1cmUgLS0+Cgo8YnI+CgojIyBFeGVyY2lzZSAtIGBzcnVuYCwgYHNpbmd1bGFyaXR5IGV4ZWNgLCBDcmVhdGUgYSBCaWd3aWcKCjwhLS0gTElWRV9OT1RFOiBXZSdsbCBkZW1vbnN0cmF0ZSBzaW5ndWxhcml0eSBleGVjIGluIHRoaXMgZXhlcmNpc2UgLS0+CgpGb2xsb3dpbmcgYWxvbmcgd2l0aCB0aGUgaW5zdHJ1Y3Rvciwgd2UnbGwgbGF1bmNoIGFuIGludGVyYWN0aXZlIGpvYiB3aXRoIGBzcnVuYC4gT25jZSB3ZSd2ZSBlbnRlcmVkIHRoZSBydW5uaW5nIGpvYiwgd2UnbGwgdXNlIHNpbmd1bGFyaXR5IHdpdGggYW4gZXhpc3RpbmcgZGVlcHRvb2xzIGRvY2tlciBpbWFnZSBhbmQgbGF1bmNoIGEgJ3Npbmd1bGFyaXR5IHNoZWxsJy4gV2l0aCB0aGUgYGJhbUNvdmVyYWdlYCBjb21tYW5kIG5vdyBhdmFpbGFibGUsIHdlJ2xsIGNyZWF0ZSBhIEJpZ3dpZyBmb3Igc2FtcGxlX0EuCgpBZnRlciB3ZSBjb21wbGV0ZSB0aGlzLCB3ZSdsbCBkZWxldGUgdGhlIG5ld2x5IGNyZWF0ZWQgQmlnd2lnIGZpbGUsIHNvIHRoYXQgd2UgY2FuIHN0YXJ0IGZyZXNoIG9uIG91ciBTQkFUQ0ggZXhlcmNpc2VzLgoKPGRldGFpbHM+CjxzdW1tYXJ5PmBzcnVuYCwgYHNpbmd1bGFyaXR5IGV4ZWNgLCBDcmVhdGUgYSBCaWd3aWcgLSBTb2x1dGlvbjwvc3VtbWFyeT4KCmBzcnVuIC0tcHR5IC0tam9iLW5hbWU9JHtVU0VSfV9zaW5ndWxhcml0eV9jcmVhdGVfYmlnd2lnIC0tYWNjb3VudD1iaW9pbmZfd2tzaHBfY2xhc3MgLS1wYXJ0aXRpb24gc3RhbmRhcmQgLS1tZW09MjAwMCAtLWNwdXMtcGVyLXRhc2s9MiAtLXRpbWU9MDA6MzA6MDAgL2Jpbi9iYXNoYAoKYG1vZHVsZSBsaXN0YAoKYG1vZHVsZSBsb2FkIHNpbmd1bGFyaXR5YAoKYHNpbmd1bGFyaXR5IGV4ZWMgZG9ja2VyOi8vcXVheS5pby9iaW9jb250YWluZXJzL2RlZXB0b29sczozLjUuNS0tcHloZGZkNzhhZl8wIGJhbUNvdmVyYWdlIC1iIGZpbHRlcl92aXpfc2luZ3VsYXJpdHkvc2FtcGxlX0EuY2hyMTkuYmFtIC1vIGZpbHRlcl92aXpfc2luZ3VsYXJpdHkvc2FtcGxlX0EuY2hyMTkuYmlnd2lnYAoKPC9kZXRhaWxzPgoKPGJyPgoKPCEtLSBMSVZFX05PVEU6IFNraXAgY3JlYXRlIGFsbCBiaWd3aWdzIGV4ZXJjaXNlIC0tPgoKIyMgRXhlcmNpc2UgLSBTQkFUQ0gsIFNpbmd1bGFyaXR5LCBDcmVhdGUgQmlnd2lncyBmb3IgQWxsIEJBTXMKCkluIHRoZSBzYW1lIHBhdHRlcm4gYXMgd2UndmUgZXN0YWJsaXNoZWQgZm9yIG91ciBmaWx0ZXJpbmcgdGFzaywgd2UnbGwgd2l0aCBhIHNpbmdsZSBzYW1wbGUsIHNhbXBsZV9BIGFuZCBjcmFmdCBhbiBTQkFUQ0ggZmlsZSB0byBjcmVhdGUgYSBCaWd3aWcgZmlsZSBmb3IgdGhhdCBvbmUgc2FtcGxlIGJlZm9yZSBtb3Zpbmcgb24gdG8gb3RoZXJzLgoKPGJyPgoKPCEtLSBMSVZFX05PVEU6IFJlbWVtYmVyIHRvIGhhdmUgdXNlcnMgbW92ZSB0aGVpciBjb25kYSBlbnYgLS0+Cgo8IS0tIExJVkVfTk9URTogVGFrZSB0aW1lIHRvIGRvY3VtZW50IGFuZCB0byB0aWR5IHVwIC0tPgoKCiMjIERlbW9uc3RyYXRpb24gLSBSZW1vdGUgQnVpbGQgb2YgYSBTaW5ndWxhcml0eSBJbWFnZQoKU28gZmFyIHdlJ3ZlIG9ubHkgZGVtb25zdHJhdGVkIHVzZXMgd2hlcmUgd2UgdXNlIGV4aXN0aW5nIGRvY2tlciBvciBzaW5ndWxhcml0eSBpbWFnZXMuIFdoaWxlIHdlJ3ZlIHNob3duIHRoYXQgdGhpcyBpcyBwb3dlcmZ1bCBhbmQgZWFzeSB0byB1c2UsIHdlIGFyZW4ndCB0YWtpbmcgZnVsbCBhZHZhbnRhZ2Ugb2Ygc2luZ3VsYXJpdHkncyBjYXBhYmlsaXRpZXMgd2l0aG91dCBidWlsZGluZyBuZXcgaW1hZ2VzLgoKVGhpcyB0b3BpYyBpcyBvdXRzaWRlIHRoZSBzY29wZSBvZiB0aGlzIHdvcmtzaG9wLCBidXQgd2Ugd2FudCB0byBkZW1vbnN0cmF0ZSB0aGF0IGl0IGlzIHBvc3NpYmxlIHRvIGJ1aWxkIHNpbmd1bGFyaXR5IGNvbnRhaW5lcnMuIEl0J3Mgbm90IHF1aXRlIHN0cmFpZ2h0Zm9yd2FyZCB0byBkbyBzbyBvbiBHcmVhdCBMYWtlcywgYnV0IHdpdGggYSBsaXR0bGUgYml0IG9mIGV4dHJhIHNldHVwIGl0IGlzIHBvc3NpYmxlIHRvIHVzZSBhIHJlbW90ZSBidWlsZGVyLCB3aGljaCB3ZSdsbCBkZW1vbnN0cmF0ZS4KCkZvciBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSByZW1vdGUgYnVpbGQgcHJvY2Vzcywgc2VlIHRoZSBkb2N1bWVudGF0aW9uIGhlcmU6CgpbTGluayB0byBkb2N1bWVudGF0aW9uIG9uIHJlbW90ZSBidWlsZHNdKGh0dHBzOi8vZG9jcy5zeWxhYnMuaW8vZ3VpZGVzL2xhdGVzdC91c2VyLWd1aWRlL2J1aWxkX2FfY29udGFpbmVyLmh0bWwjcmVtb3RlLWJ1aWxkcykKCjxkZXRhaWxzPgo8c3VtbWFyeT5EZW1vIFJlbW90ZSBCdWlsZCAtIFNvbHV0aW9uPC9zdW1tYXJ5PgoKV2UnbGwgY3JlYXRlIGEgc2luZ3VsYXJpdHkgZGVmaW5pdGlvbiBmaWxlIGNhbGxlZCBgY293c2F5LmRlZmAKCmBuYW5vIGNvd3NheS5kZWZgCgpDb250ZW50cyBvZiBgY293c2F5LmRlZmAgYXJlIHNob3duIGluIHRoZSBzaW5ndWxhcml0eSBkZWZpbml0aW9uIGZpbGUgc2hvd24gaW4gdGhlIGFib3ZlIGxlc3Nvbi4KCk5vdGUsIHRvIHVzZSB0aGUgcmVtb3RlIGJ1aWxkZXIgd2l0aCB0aGUgYC0tcmVtb3RlYCBmbGFnLCB5b3UgbXVzdCBmb2xsb3cgdGhlIGluc3RydWN0aW9ucyBvbiByZW1vdGUgYnVpbGRzIGFib3ZlIC0gY3JlYXRlIGFuIGFjY291bnQgYW5kIGdlbmVyYXRlIGEgdG9rZW4gb25saW5lLCB0aGVuIHVzZSBgc2luZ3VsYXJpdHkgcmVtb3RlIGxvZ2luYC4KCmBzaW5ndWxhcml0eSBidWlsZCAtLXJlbW90ZSBjb3dzYXkuc2lmIGNvd3NheS5kZWZgCgpBZnRlciB0aGlzLCB3ZSBoYXZlIGEgc2luZ3VsYXJpdHkgaW1hZ2UgZmlsZSwgYGNvd3NheS5zaWZgIHRoYXQgd2UgY2FuIHVzZSB3aXRoIGBzaW5ndWxhcml0eSBzaGVsbCBjb3dzYXkuc2lmYCBvciBgc2luZ3VsYXJpdHkgZXhlYyBjb3dzYXkuc2lmIGNvd3NheSBNb29vYAoKPC9kZXRhaWxzPgoKPGJyPgo8YnI+Cjxicj4KPGJyPgoKIyMgUmV2aWV3IG9mIExNT0QsIENvbmRhLCBEb2NrZXIsIGFuZCBTaW5ndWxhcml0eQoKIVtdKGltYWdlcy9Nb2R1bGUwNV9jb21wYXJpc29uX3NvZnR3YXJlX21hbmFnZW1lbnQucG5nKQoKSW4gY2hvb3NpbmcgYW55IHRvb2wsIHdlIG11c3QgY29uc2lkZXIgdGhlIGluaGVyZW50IHRyYWRlLW9mZnMgYmV0d2VlbiBzaW1wbGljaXR5LCBwb3J0YWJpbGl0eSwgYW5kIGR1cmFiaWxpdHkuCgo8YnI+CgotLS0KCgp8IFtQcmV2aW91cyBsZXNzb25dKE1vZHVsZTA0X3NvZnR3YXJlX21hbmFnZW1lbnRfY29uZGEuaHRtbCkgfCBbVG9wIG9mIHRoaXMgbGVzc29uXSgjdG9wKSB8IFtOZXh0IGxlc3Nvbl0oTW9kdWxlMDYtaW50cm8tdG8td29ya2Zsb3ctYXV0b21hdGlvbi5odG1sKSB8CnwgOi0tLSB8IDotLS0tOiB8IC0tLTogfAo=