
Please note, still in draft, expected to finalised in Jan 2019
Introduction
The basic idea of Docker is that it gives you the ability to have complete control over your tech stack. Maybe you want a particular version of Linux with really specific software versions installed. Or maybe, if you are one of those data science types, you always like to work with a particular versions of RStudio or Jupyter Notebook and specific versions of libraries. Docker provides a way to create the exact set up you need, and then lets you easily pass this specification to other people, so you can be confident that all your stuff will just work on different machines.
Many of the tutorials you will come across for Docker assume you are trying to set up complex, multi-server environments because you might want to deploy applications across different machines and networks.But for the data science types, the concerns are often a little different, and I will focus on how Docker can help you create data science environments that help with your workflow. At the end of the tutorial I have some of that other stuff and can point you to further resources if you would like to go deeper (and I recommend you do as Docker is just an amazing piece of tech).
First steps
Now I am going to go ahead and assume you have Docker up and running. You will find that, Docker is generally pretty plug and play for Mac, Linux and Windows. I am more of Linux guy, but I use Docker mostly on my work machine which is Windows and I had to configure a couple of things to do this, but there is lots of documentation out there to to do this. There are also a ton of tutorials around to help you set it up. Note that you need admin rights on your machine to use Docker.
Peraps the simplest I could do to give you an intution about docker is to open a command shell (such as PowerShell on Windows, the Terminal on Mac, or Bash on Linux), and try something like the following to start up a container (and stay calm, I will explain what a container is in a moment):
docker container run –p 80:80 nginx
Ok, lets now shut the container down. You should be able to just ctrl-C. If you run into any issue, the easiest thing to do right now is to first run a command to list all the running containers (you will see a big long ID string). Then, run a second command to stop the running container. Note that if you just put the few characters of the container ID, Docker will know which one you are talking about.
docker container ls docker container stop [CONTAINER ID]
Let’s try this again and start up a new container. But this time, we will give the container a name with the –nameflag and also run it in detached mode with the -d flag
docker container run –publish 80:80 –detach –name webhost nginx
Close it down by running those close down commands above.
Before we go much further, let’s introduce a couple of key ideas from Docker: image and container. You can think of an image as a master copy of the stuff needed for a particular tech set up. Like maybe you have an Ubuntu image which has all libraries and binaries to run Ubuntu. Or maybe your image holds all the specs for Debian linux environment with but has React.js, Node.js and MongoDB installed.
The people that created Docker also created this neat thing called DockerHub, which is a repository of all kinds of different images. You can think of Dockerhub asa really cool place that holds a huge variety of master copies for all the different tech set ups you want. It has images that hold Linux machines with Python Anaconda Stack, or an image that has the set up for a R Shiny Server, or an image that has a set up for ElasticDB. You can grab them and use them, or change them and create your own images.
A container is just an instance of an image. So let’s say you get hold of the Ubuntu image from dockerhub. Then you can run an instance of it. Or you might run a bunch of instances. To provide an intuition, imagine if someone handed you a copy of Ubuntu operating on a CD or flash drive (an image). Then, you could install this a whole bunch of desktop computers - each of these desktops would be like a container.
Now that we have a little more context, let’s break down what happened when I ran that last run command:
docker container run –publish 80:80 –detach –name webhost nginx
- First, Docker tried to create a container from an image called nginx. It could not find it on my local machine, so it went to try and find it on Dockerhub
- Dockerhub had a look and found an image called nginx, and sent it across the network to my local machine
- Now armed with image, my local machine then set about creating an instance of it (called a container)
- Along the way it set up some things based on instructions I provided (such as
-publish, -detach and –name. This tells docker to forward my local port 80 to the containers port 80, to run it in detached mode, or in the background, and give ia a name called ‘webhost’
Now lets get a little more comfortable with using different images to create different containers. You can just stop the containers as you did above, by listing them and stoping them. Note you can stop multiple containers by typing:
docker containenr stop [FIRST CONTIANER ID] [SECOND CONTAINER ID]…etc….
Ok, create and stop following containers now
docker container run -p 3306:3306 –detach –name mysql -e MYSQL_RANDOM_ROOT_PASSWORD=yes mysql
docker container run -p 8080:80 -d –name apachehttpd httpd
docker container run -p 80:80 -d –name nginx nginx
There are also some neat ways to get hold of information about containers. Seeing what is happening on your container is just like runining the Task Manager on Windows, or Activitity Monitor on Mac, to see the kinds of processes that are happening. There are some different commends you can use to get different types of information
docker container logs [CONTAINER NAME]
docker container top [CONTAINER NAME]
docker container inspect [CONTAINER NAME]
docker container stats [CONTAINER NAME]
You might be starting to notice that Docker is full of handy ways to give you info about what is going on. You can give it all kinds of instructions such as -p and -d. Docker has really nice documentation for all of this and if you put “help” at the end of any commands, you can can access this.
docker container stats help
Ok, so right now we still haven’t done very much. But I hope you will at least agree that the idea behind all this does seem pretty cool for data scientists. I like the idea of being able to spin up different machines and customise the software in them for my data science needs. I can have a container with all my favourite Python libraries and Juptyer notebook setup. I can have another one with R Shiny Server set up. I can have another one Graph DBs. And I can get all these to communicate to each other too (and we will get to all of this soon).
Next steps
Given that these containers are kind of like machines we have spun up, a logical next question might be how to get into the machines to run commands. And of course you can do this. So close down all your running containers, and the run the following command to try this now:
docker container run -it –name nginx nginx bash
Much of this will be stuff we have seen before. The new thing though is -it. This tells Docker to start up a container put us into into it in interactive mode. It also tells us (with the “bash” word) that we want to be dropped into the bash prompt. This image is based off a Debian linux which has a bash prompt so its no problem
You will notice though that, that we didn’t run this container in -d flag. So its not running in the background, and as soon as we jump out of the container (by using ctrl-C or just typing ‘exit’) the container itself just stops. It would make more sense if we could start a container, leave it running, and jump in and out of it as needed. So lets stop any running containers, and first start up a new container.
docker container run -d –name myUbuntu ubuntu
We can see that it is running by doing a docker container ls. Then we can jump into this container using the exec command. Note that we could do this by using the name of the container that we created, or the container ID (or even the first few characters of the container ID)
docker container exec -it ubuntu bash
Once you are in the container it is just the same as being on the ubuntu command line. You can create directories, install things (in this case using apt-get as its ubuntu linux), and do anything you might do in a standard linux machine.
Now don’t get foolled by a little gotcha here. The above calls exec and then passes it bash. So you need the bash command line to be available in the container ubuntu. If I was running an alpine linux image (which does not have a bash shell, but a sh shell), things would be a little different:
docker container run -d alpine alpine
docker container exec -it alpine sh
Dockerfiles
Pretty soon you will want to customise some of the images that yo uare using. You might want a standard Debian image, but you also want to install a little extra software. You don’t want to have to use the exec command to jump into a container and do all this set up. And this is where a Dockerfile comes in. It is kind of like a shell script that has bunch of simple commands that specify your image, and any extra set up you would like.
The best way to provide an intuition around this is to start with a super simple example. Start by creating a new directory (call it DockerTest or something logical) and inside of that, create a new file and call it “Dockerfile”. Note it doesn’t need any kind of extension, its just “Dockerfile”. Now open it up in your text editor of choice (I mostly use Atom or Sublime Text these days but anything you have is fine) and type in the following:
#
# A first super simple example of a Dockerfile
#
MAINTAINER Jamie Gabriel "email@someemail.com"
FROM ubuntu:latest
RUN apt-get update
RUN apt-get install -y python python-pip wget
RUN pip install Flask
PORT 5000 5000
ADD hello.py /home/hello.py
WORKDIR /home
The syntax here is pretty straightforward. Let’s take a walk through it. The # symbol makes whatever follows it a comment. All the all-capital-letter commands, things like like MAINTAINER, FROM and RUN are keywords that allow Docker to have some basic instructions on building an image and adding things into it. It is also possible to include some basic metadata about this image (here using the keywoard MAINTAINER).
The above Dockerfile starts with a comment, then uses the MAINTAINER instruction, which allows me to include a little metadata saying who is building and maintaining this Dockerfile. The it uses the FROM command to specifiy a base image that it will get from Dockerhub if it is not found on my local machine.
Then, it uses a series of RUN commands to run commands on the commandline of this container one it is created. The commands tell the image (an ubuntu image in this case) to do an apt-get udpate to make sure all ubuntu standard packages are up to date, then do an apt-get install to get some libraries (in this case, python, python-pip, and wget), and then does pip install Flask to install the Flask web framework. Finally it uses the keyword PORT to indicate which port from my local machine will be forwarded to the port on the container (in this case all my local traffic on port 5000 will go to) The I am using the PORT command to indicate PORT
One done, we have built a base image of ubuntu, and then customised to our own needs, and kept all this in a handy config script. Lets now build this image by doing the following:
docker image build -t mycustomimage .
Note the use of the . in the above command. This tells Docker to look for something in the current directory with the name of “Dockerfile”. You could also just write docker image build -t cusomnginx Dockerfile
Now you can run this customised container in a way that we could expect, as following:
docker container run -p 5000:5000 mycustomimage
Note another little gotch here. Even though we specified the port with the keyword PORT in the Dockerfile, we still needed to put it on the command line with the -p flag. This is a quirk of the Dockerfile, and you can think of the PORT keyword in the Dockerfile, just there for the purpose of telling anyone using it that they will need to use the -p with flag. Just accept it as weird quirk. There is a neat way to get around this with docker-compose that I will explain later.
.
There is great documentation the keywords used in the Dockerfiles, and I encourage you to check it out
here. Even better, get hold of some docker files (like this one, this one, or this one) and play around with them. Once you spend a few days getting use to creating Dockerfiles and customising them to your needs, you will really start to see the benefits of how this might help you do data science.
Interacting with your local file system
So we can do quiet a bit now. We can build images. We can create containers from them and go into those containers and run commands. We can build custom images to suit our needs using Dockerfiles. As a data science, this gives me the control to have a any kind of tech stack I can think of set up and ready to go.
But there are still some other things we really want to be able to do here. Right at the top of the list, is the ability to interact with my local files. Imagine this scenario: I have a bunch of .R files on my local machine. But I want to use a version or RStudio Server (like the image here). I want this container to be able to see my files, and make changes to them.
This question is really realted to how you deal with persistant data You want to be able to spin up containers, as needed, and you can throw them away and grab news ones at any time. But you don’t want to do that with your data. To deal with this Docker provides some really powerful solutions. The first solution is volumes. These are actually created at the same time as containers to hold data created by the container. Wen you throw out a container, volumes still around, and you can check them out with the following:
docker volume ls
The second solution, which is far more handy for datascience, is the use of bind-mounting. This lets you access local files and directories from inside a Docker container. This is the really handy one for data scientist setups.
Using docker-compose for bind mounts and ports
bind mounting - using docker for local develo mappign of host file or dorectory to cointainer…locations point same…. host files win…. cant specifiy in Dockerfile….need to bind…but you can do this
… run -v //c/users/bret:/path/container check out dockerfile sample-2 - no volumes in here….
docker cotnainer run -d –name nginx -p 80:80 -v $(pwd):/usr/share/nginx nginx
open another termingal….edit file on the hose ,and hceck in container….
go into container and check, and edit….so edit from both places….
now think about complicated dev setups…..this solves all that!
not you can look at logs of container when you are doing dev…..
Updates Assignment Lecture 50 - bind mounts…..
Idea - mount data into….
Docker compose - bind mounting simpler…. ports simpler…. Jekyll is github pages….
use bindmount-smaple-1
edit these, then container running background
docker run -p 80:4000 -v $(pwd):/site/bretfisher/jekyll-serve # this will open things up and then refresh browser to see changes…..
you can change _posts directory
assignment - use bindmount-sample-1 directory…. docker container run -p 80:4000 -v$(pwd):/site bretfisher/jekyll-serve #image…. this will start server….
asdfasdfa
Going deeper into Docker
So at about this point, as a data science type, you should start to have a enough go forward. , you really have everything you need to to ake control of your data science stack. You are now good to go for data science. Download different images, check them out. But of course Docker has some other huge features, things like networks,docker-compose and docker swarm which lets
Volumes…. can put this into a docker file
check out mysql docker image hub….look at latest docker file….
VOLUME /var/lib/mysql ….. this creates volume and puts it in director NOte that volumes need manual delition
docker pull mysql
docker image inspect mysql # you can see the volume
docker container run -d –name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=True mysql
docker cotnainer inspect mysql # will show volume, see where volume living on host (whcih is really in a linux vm)
docker volume ls
docker volume inspect
consider, rerun mysql see two volumens now…. when you stop containers, volumes still there….
Named volumes…..
docker container run -d –name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=True -v mysql-db:/var/lib/mysql mysql # note put pair of values, like dict betwen colon
docker volume ls docker volume inspect # much easier to work with docker volume create #couple of cases where you createa volume..but thats a future ting
Take home…..create containers with named volumes
Networks in Docker
There is super cool thing in Docker called docker-composer that lets you spin up multiple containers at once and becomes really handy in some situations. But to get into this, we need to talk about some other aspects of docker first! So lets shift gears a little and talk about networks in Docker
Because Docker is not just about images and containers, its about networks too. Docker lets us spin up any kind of containers, and then gives us a way to set these containers up on networks so the containers can communicate to themselves and the outside world. Docker is geared up to make working with networks easy, but its also completely customiseable so you can do really advanced setups.
docker container run -p # this -p means you can expose ports Need to know a bit out TCIP, Neworks detach batteries included but removable - you can do huge customisation -p exposers containers on to the physical netowrk All containers connected to private virtual network “bridge” so has its own bridge Each virtual network routes throug a NAT firewall on host IP -p not needed for containers talking All settings customisable You can attach to multiple virtual networks You can skip virtual netwoks and use host IP (–net=host) Can use different drivers ocker container run -p 80:80 –name webhost -d nginx docker container port webhost # get ports forwarding traffic container uses different IP to get IP: docker container inspect –format ‘{{.NetworkSettings.IPAddress}}’ webhost docker container inspect webhost can also get all specs on container using above docker is 172 network…. Host connected to physical network via firewall (blocks traffic) and any traffic Natted concept of virtual networks in machines (bridge or Docker0) new container conencts to virtual and then network connects to your computer address, -p 80:80 means any traffic through network second container can be set up on same network, but need to expose externally Can create another network with its own -p and will take traffic from outside You can have seperate networks and utilise publishable ports on machine, or you can connect via network Anywhere I do a docker container run nginx , where nginx is the image you should use, replace that with nginx:alpine , which still has ping command in it. bridge is default network that connects to firewall docker network create my_app_net # creates a new virtual network default driver is bridge not advanced. docker network create –help docker cotnainer run -d –name new_nginx –network my_app_net nginx creates and connects to a created network docker network connect so lets create another container docker container run -p 8080:80 -d –name apachehttpd httpd and then connect it to the same network and then run docker network inspect Also disconnect docker network disconnect app_stack 37e gives you lots of options…. makes it safer
how containers find each other DNS and the affect on containers can’t rely on IP addresses as things are dynamic naming crucial - docker users container names to talk to each other brige networks have auto dns I can do docker container run -d –name nginx –network my_app_net nginx docker container run -d –name nginxTwo –network my_app_net nginx thenI can pop into one and ping the other docker container exec nginx ping nginxTwo this solves the dynamic problem to do this you need to create new network, bridge doesn’t have this ASSIGNMENT Open 2 linux boxes, talk to each other note that you need to run debian / ubuntu with -it bash docker container run -it –name debian –network my_app_net debian bash round robin testing create network docker network create dude create container docker container run -d –net dude –net-alias search elasticsearch:2 docker container run -d –net dude –net-alias search elasticsearch:2 docker container run –rm alpine nslookup search docker container run –rm –net dude centos curl -s search:9200
IMAGES AND DOCKERHUB
application binaries and dependencies and metadata of how to run things
No OS in image…just binaries application needs
Not virtual machine….
can get images from dockerhub
log in and see all the images
use images that are popular first
start with official…will be noted this way….
no forward slash in the name
official are created at Docker
good docs in official images
note there are different versions in official…
latest is a special tag….getting latest version
docker pull nginx:1.11.9 # to specifiy version, which migh tbe latest # make sure you control version # notes in docker hub will have different docker pull nginx:1.11.0-alpine # smaller version # look for number of stars and number of pulls # images designed with union file system idea…. docker image ls # lists images docker image history neo4j # shows images that come together and run it # each layer of image gets own unique shah, and they share them # you can see DockerFile style commands in here # image share where possible…. idea of cache of image layers…. # You run container off image….docker creates read write layer on image… # and thats all the container is… # you can do copy on write….meaning you have multiple containers… # that might change config on an image….but this is kept in the # container…. # means that these are not images…just layers, don’t need image id docker image inspect neo4j # all metadata, env variables, ports etc…. # Tagging and uploading images # tagging needs specific formatting # images don’t have name…. docker image ls # no names, but shows us repo (username/repo or repo) # tag isn’t version or branch, kind of in between…pointer # tag might be 1.11.10 or 1.11.10-alpine… # you can add tag to image that you didn’t make…. docker image push jgab3103/centos #create new repo etc…
plot(cars)
Add a new chunk by clicking the Insert Chunk button on the toolbar or by pressing Ctrl+Alt+I.
When you save the notebook, an HTML file containing the code and output will be saved alongside it (click the Preview button or press Ctrl+Shift+K to preview the HTML file).
LS0tDQp0aXRsZTogIkRvY2tlciBmb3IgRGF0YSBTY2llbnRpc3RzIg0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCg0KDQoNCiMhW0hlbGxvIGRvY2tlciB3aGFsZSFdKGRvY2tlckxvZ28ucG5nKQ0KPGJyLz4NCg0KPGk+UGxlYXNlIG5vdGUsIHN0aWxsIGluIGRyYWZ0LCBleHBlY3RlZCB0byBmaW5hbGlzZWQgaW4gSmFuIDIwMTk8L2k+DQo8YnIvPg0KPGg0PkludHJvZHVjdGlvbjwvaDQ+DQo8ZGl2PlRoZSBiYXNpYyBpZGVhIG9mIERvY2tlciBpcyB0aGF0IGl0IGdpdmVzIHlvdSB0aGUgYWJpbGl0eSB0byBoYXZlIGNvbXBsZXRlIGNvbnRyb2wgb3ZlciB5b3VyIHRlY2ggc3RhY2suIE1heWJlIHlvdSB3YW50IGEgcGFydGljdWxhciB2ZXJzaW9uIG9mIExpbnV4IHdpdGggcmVhbGx5IHNwZWNpZmljIHNvZnR3YXJlIHZlcnNpb25zIGluc3RhbGxlZC4gT3IgbWF5YmUsIGlmIHlvdSBhcmUgb25lIG9mIHRob3NlIGRhdGEgc2NpZW5jZSB0eXBlcywgeW91IGFsd2F5cyBsaWtlIHRvIHdvcmsgd2l0aCBhIHBhcnRpY3VsYXIgdmVyc2lvbnMgb2YgUlN0dWRpbyBvciBKdXB5dGVyIE5vdGVib29rIGFuZCBzcGVjaWZpYyB2ZXJzaW9ucyBvZiBsaWJyYXJpZXMuIERvY2tlciBwcm92aWRlcyBhIHdheSB0byBjcmVhdGUgdGhlIGV4YWN0IHNldCB1cCB5b3UgbmVlZCwgYW5kIHRoZW4gbGV0cyB5b3UgZWFzaWx5IHBhc3MgdGhpcyBzcGVjaWZpY2F0aW9uIHRvIG90aGVyIHBlb3BsZSwgc28geW91IGNhbiBiZSBjb25maWRlbnQgdGhhdCBhbGwgeW91ciBzdHVmZiB3aWxsIGp1c3Qgd29yayBvbiBkaWZmZXJlbnQgbWFjaGluZXMuPC9kaXY+DQoNCjxici8+DQo8ZGl2Pk1hbnkgb2YgdGhlIHR1dG9yaWFscyB5b3Ugd2lsbCBjb21lIGFjcm9zcyBmb3IgRG9ja2VyIGFzc3VtZSB5b3UgYXJlIHRyeWluZyB0byBzZXQgdXAgY29tcGxleCwgbXVsdGktc2VydmVyIGVudmlyb25tZW50cyBiZWNhdXNlIHlvdSBtaWdodCB3YW50IHRvIGRlcGxveSBhcHBsaWNhdGlvbnMgYWNyb3NzIGRpZmZlcmVudCBtYWNoaW5lcyBhbmQgbmV0d29ya3MuQnV0IGZvciB0aGUgZGF0YSBzY2llbmNlIHR5cGVzLCB0aGUgY29uY2VybnMgYXJlIG9mdGVuIGEgbGl0dGxlIGRpZmZlcmVudCwgYW5kIEkgd2lsbCBmb2N1cyBvbiBob3cgRG9ja2VyIGNhbiBoZWxwIHlvdSBjcmVhdGUgZGF0YSBzY2llbmNlIGVudmlyb25tZW50cyB0aGF0IGhlbHAgd2l0aCB5b3VyIHdvcmtmbG93LiBBdCB0aGUgZW5kIG9mIHRoZSB0dXRvcmlhbCBJIGhhdmUgc29tZSBvZiB0aGF0IG90aGVyIHN0dWZmIGFuZCBjYW4gcG9pbnQgeW91IHRvIGZ1cnRoZXIgcmVzb3VyY2VzIGlmIHlvdSB3b3VsZCBsaWtlIHRvIGdvIGRlZXBlciAoYW5kIEkgcmVjb21tZW5kIHlvdSBkbyBhcyBEb2NrZXIgaXMganVzdCBhbiBhbWF6aW5nIHBpZWNlIG9mIHRlY2gpLjwvZGl2Pg0KPGJyLz4NCjxoND5GaXJzdCBzdGVwczwvaDQ+DQo8ZGl2Pk5vdyBJIGFtIGdvaW5nIHRvIGdvIGFoZWFkIGFuZCBhc3N1bWUgeW91IGhhdmUgRG9ja2VyIHVwIGFuZCBydW5uaW5nLiBZb3Ugd2lsbCBmaW5kIHRoYXQsIERvY2tlciBpcyBnZW5lcmFsbHkgcHJldHR5IHBsdWcgYW5kIHBsYXkgZm9yIE1hYywgTGludXggYW5kIFdpbmRvd3MuIEkgYW0gbW9yZSBvZiBMaW51eCBndXksIGJ1dCBJIHVzZSBEb2NrZXIgbW9zdGx5IG9uIG15IHdvcmsgbWFjaGluZSB3aGljaCBpcyBXaW5kb3dzIGFuZCBJIGhhZCB0byBjb25maWd1cmUgYSBjb3VwbGUgb2YgdGhpbmdzIHRvIGRvIHRoaXMsIGJ1dCB0aGVyZSBpcyBsb3RzIG9mIGRvY3VtZW50YXRpb24gb3V0IHRoZXJlIHRvIHRvIGRvIHRoaXMuIFRoZXJlIGFyZSBhbHNvIGEgIDxpPnRvbjwvaT4gb2YgdHV0b3JpYWxzIGFyb3VuZCB0byBoZWxwIHlvdSBzZXQgaXQgdXAuIE5vdGUgdGhhdCB5b3UgbmVlZCBhZG1pbiByaWdodHMgb24geW91ciBtYWNoaW5lIHRvIHVzZSBEb2NrZXIuPC9kaXY+DQo8YnIvPg0KPGRpdj5QZXJhcHMgdGhlIHNpbXBsZXN0IEkgY291bGQgZG8gdG8gZ2l2ZSB5b3UgYW4gaW50dXRpb24gYWJvdXQgZG9ja2VyIGlzIHRvIG9wZW4gYSBjb21tYW5kIHNoZWxsIChzdWNoIGFzIFBvd2VyU2hlbGwgb24gV2luZG93cywgdGhlIFRlcm1pbmFsIG9uIE1hYywgb3IgQmFzaCBvbiBMaW51eCksIGFuZCB0cnkgc29tZXRoaW5nIGxpa2UgdGhlIGZvbGxvd2luZyB0byBzdGFydCB1cCBhIGNvbnRhaW5lciAoYW5kIHN0YXkgY2FsbSwgSSB3aWxsIGV4cGxhaW4gd2hhdCBhIGNvbnRhaW5lciBpcyBpbiBhIG1vbWVudCk6PC9kaXY+DQo8YnIvPg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBydW4g4oCTcCA4MDo4MCBuZ2lueDwvY29kZT4NCjxici8+PGJyLz4NCjxkaXY+T2ssIGxldHMgbm93IHNodXQgdGhlIGNvbnRhaW5lciBkb3duLiBZb3Ugc2hvdWxkIGJlIGFibGUgdG8ganVzdCA8Y29kZT5jdHJsLUM8L2NvZGU+LiBJZiB5b3UgcnVuIGludG8gYW55IGlzc3VlLCAgdGhlIGVhc2llc3QgdGhpbmcgdG8gZG8gcmlnaHQgbm93IGlzIHRvIGZpcnN0IHJ1biBhIGNvbW1hbmQgdG8gbGlzdCBhbGwgdGhlIHJ1bm5pbmcgY29udGFpbmVycyAoeW91IHdpbGwgc2VlIGEgYmlnIGxvbmcgSUQgc3RyaW5nKS4gVGhlbiwgcnVuIGEgc2Vjb25kIGNvbW1hbmQgdG8gc3RvcCB0aGUgcnVubmluZyBjb250YWluZXIuIE5vdGUgdGhhdCBpZiB5b3UganVzdCBwdXQgdGhlIGZldyBjaGFyYWN0ZXJzIG9mIHRoZSBjb250YWluZXIgSUQsIERvY2tlciB3aWxsIGtub3cgd2hpY2ggb25lIHlvdSBhcmUgdGFsa2luZyBhYm91dC48L2Rpdj4NCjxici8+DQo8Y29kZT5kb2NrZXIgY29udGFpbmVyIGxzIDwvY29kZT4NCjxici8+DQo8Y29kZT5kb2NrZXIgY29udGFpbmVyIHN0b3AgW0NPTlRBSU5FUiBJRF0gPC9jb2RlPg0KPGJyLz48YnIvPg0KPGRpdj5MZXQncyB0cnkgdGhpcyBhZ2FpbiBhbmQgc3RhcnQgdXAgYSBuZXcgY29udGFpbmVyLiBCdXQgdGhpcyB0aW1lLCB3ZSB3aWxsIGdpdmUgdGhlIGNvbnRhaW5lciBhIG5hbWUgd2l0aCB0aGUgPGNvZGU+LS1uYW1lPC9jb2RlPmZsYWcgYW5kIGFsc28gcnVuIGl0IGluIGRldGFjaGVkIG1vZGUgd2l0aCB0aGUgPGNvZGU+LWQ8L2NvZGU+IGZsYWc8L2Rpdj4NCjxici8+DQo8Y29kZT4gZG9ja2VyIGNvbnRhaW5lciBydW4g4oCTcHVibGlzaCA4MDo4MCDigJNkZXRhY2ggLS1uYW1lIHdlYmhvc3QgbmdpbnggPC9jb2RlPg0KPGJyLz48YnIvPg0KPGRpdj5DbG9zZSBpdCBkb3duIGJ5IHJ1bm5pbmcgdGhvc2UgY2xvc2UgZG93biBjb21tYW5kcyBhYm92ZS48L2Rpdj4NCjxici8+DQo8ZGl2PkJlZm9yZSB3ZSBnbyBtdWNoIGZ1cnRoZXIsIGxldCdzIGludHJvZHVjZSBhIGNvdXBsZSBvZiBrZXkgaWRlYXMgZnJvbSBEb2NrZXI6IDxzdHJvbmc+aW1hZ2U8L3N0cm9uZz4gYW5kIDxzdHJvbmc+Y29udGFpbmVyPC9zdHJvbmc+LiBZb3UgY2FuIHRoaW5rIG9mIGFuIGltYWdlIGFzIGEgbWFzdGVyIGNvcHkgb2YgdGhlIHN0dWZmIG5lZWRlZCBmb3IgYSBwYXJ0aWN1bGFyIHRlY2ggc2V0IHVwLiBMaWtlIG1heWJlIHlvdSBoYXZlIGFuIFVidW50dSBpbWFnZSB3aGljaCBoYXMgYWxsIGxpYnJhcmllcyBhbmQgYmluYXJpZXMgdG8gcnVuIFVidW50dS4gT3IgbWF5YmUgeW91ciBpbWFnZSBob2xkcyBhbGwgdGhlIHNwZWNzIGZvciBEZWJpYW4gbGludXggZW52aXJvbm1lbnQgd2l0aCBidXQgaGFzIFJlYWN0LmpzLCBOb2RlLmpzIGFuZCBNb25nb0RCIGluc3RhbGxlZC48L2Rpdj4NCjxici8+DQo8ZGl2PlRoZSBwZW9wbGUgdGhhdCBjcmVhdGVkIERvY2tlciBhbHNvIGNyZWF0ZWQgdGhpcyBuZWF0IHRoaW5nIGNhbGxlZCBEb2NrZXJIdWIsIHdoaWNoIGlzIGEgcmVwb3NpdG9yeSBvZiBhbGwga2luZHMgb2YgZGlmZmVyZW50IGltYWdlcy4gWW91IGNhbiB0aGluayBvZiBEb2NrZXJodWIgYXNhIHJlYWxseSBjb29sIHBsYWNlIHRoYXQgaG9sZHMgYSBodWdlIHZhcmlldHkgb2YgbWFzdGVyIGNvcGllcyBmb3IgYWxsIHRoZSBkaWZmZXJlbnQgdGVjaCBzZXQgdXBzIHlvdSB3YW50LiBJdCBoYXMgaW1hZ2VzIHRoYXQgaG9sZCBMaW51eCBtYWNoaW5lcyB3aXRoIFB5dGhvbiBBbmFjb25kYSBTdGFjaywgb3IgYW4gaW1hZ2UgdGhhdCBoYXMgdGhlIHNldCB1cCBmb3IgYSBSIFNoaW55IFNlcnZlciwgb3IgYW4gaW1hZ2UgdGhhdCBoYXMgYSBzZXQgdXAgZm9yIEVsYXN0aWNEQi4gWW91IGNhbiBncmFiIHRoZW0gYW5kIHVzZSB0aGVtLCBvciBjaGFuZ2UgdGhlbSBhbmQgY3JlYXRlIHlvdXIgb3duIGltYWdlcy48L2Rpdj4NCg0KPGJyLz4NCjxkaXY+QSBjb250YWluZXIgaXMganVzdCBhbiBpbnN0YW5jZSBvZiBhbiBpbWFnZS4gU28gbGV0J3Mgc2F5IHlvdSBnZXQgaG9sZCBvZiB0aGUgVWJ1bnR1IGltYWdlIGZyb20gZG9ja2VyaHViLiBUaGVuIHlvdSBjYW4gcnVuIGFuIGluc3RhbmNlIG9mIGl0LiBPciB5b3UgbWlnaHQgcnVuIGEgYnVuY2ggb2YgaW5zdGFuY2VzLiBUbyBwcm92aWRlIGFuIGludHVpdGlvbiwgaW1hZ2luZSBpZiBzb21lb25lIGhhbmRlZCB5b3UgYSBjb3B5IG9mIFVidW50dSBvcGVyYXRpbmcgb24gYSBDRCBvciBmbGFzaCBkcml2ZSAoYW4gaW1hZ2UpLiBUaGVuLCB5b3UgY291bGQgaW5zdGFsbCB0aGlzIGEgd2hvbGUgYnVuY2ggb2YgZGVza3RvcCBjb21wdXRlcnMgLSBlYWNoIG9mIHRoZXNlIGRlc2t0b3BzIHdvdWxkIGJlIGxpa2UgYSBjb250YWluZXIuPC9kaXY+DQo8YnIvPg0KPGJyLz4NCjxkaXY+Tm93IHRoYXQgd2UgaGF2ZSBhIGxpdHRsZSBtb3JlIGNvbnRleHQsIGxldCdzIGJyZWFrIGRvd24gd2hhdCBoYXBwZW5lZCB3aGVuIEkgcmFuIHRoYXQgbGFzdCBydW4gY29tbWFuZDogPC9kaXY+DQoNCjxici8+DQo8Y29kZT4gZG9ja2VyIGNvbnRhaW5lciBydW4g4oCTcHVibGlzaCA4MDo4MCDigJNkZXRhY2ggLS1uYW1lIHdlYmhvc3QgbmdpbnggPC9jb2RlPg0KPGJyLz4NCjxici8+DQoNCjEuIEZpcnN0LCBEb2NrZXIgdHJpZWQgdG8gY3JlYXRlIGEgY29udGFpbmVyIGZyb20gYW4gaW1hZ2UgY2FsbGVkIG5naW54LiBJdCBjb3VsZCBub3QgZmluZCBpdCBvbiBteSBsb2NhbCBtYWNoaW5lLCBzbyBpdCB3ZW50IHRvIHRyeSBhbmQgZmluZCBpdCBvbiBEb2NrZXJodWINCjIuIERvY2tlcmh1YiBoYWQgYSBsb29rIGFuZCBmb3VuZCBhbiBpbWFnZSBjYWxsZWQgbmdpbngsIGFuZCBzZW50IGl0IGFjcm9zcyB0aGUgbmV0d29yayB0byBteSBsb2NhbCBtYWNoaW5lDQozLiBOb3cgYXJtZWQgd2l0aCBpbWFnZSwgbXkgbG9jYWwgbWFjaGluZSB0aGVuIHNldCBhYm91dCBjcmVhdGluZyBhbiBpbnN0YW5jZSBvZiBpdCAoY2FsbGVkIGEgY29udGFpbmVyKQ0KNC4gQWxvbmcgdGhlIHdheSBpdCBzZXQgdXAgc29tZSB0aGluZ3MgYmFzZWQgb24gaW5zdHJ1Y3Rpb25zIEkgcHJvdmlkZWQgKHN1Y2ggYXMgPGNvZGU+LXB1Ymxpc2g8L2NvZGU+LCA8Y29kZT4tZGV0YWNoPC9jb2RlPiBhbmQgPGNvZGU+LS1uYW1lPC9jb2RlPi4gVGhpcyB0ZWxscyBkb2NrZXIgdG8gZm9yd2FyZCBteSBsb2NhbCBwb3J0IDgwIHRvIHRoZSBjb250YWluZXJzIHBvcnQgODAsIHRvIHJ1biBpdCBpbiBkZXRhY2hlZCBtb2RlLCBvciBpbiB0aGUgYmFja2dyb3VuZCwgYW5kIGdpdmUgaWEgYSBuYW1lIGNhbGxlZCAnd2ViaG9zdCcNCg0KPGRpdj5Ob3cgbGV0cyBnZXQgYSBsaXR0bGUgbW9yZSBjb21mb3J0YWJsZSB3aXRoIHVzaW5nIGRpZmZlcmVudCBpbWFnZXMgdG8gY3JlYXRlIGRpZmZlcmVudCBjb250YWluZXJzLiBZb3UgY2FuIGp1c3Qgc3RvcCB0aGUgY29udGFpbmVycyBhcyB5b3UgZGlkIGFib3ZlLCBieSBsaXN0aW5nIHRoZW0gYW5kIHN0b3BpbmcgdGhlbS4gTm90ZSB5b3UgY2FuIHN0b3AgbXVsdGlwbGUgY29udGFpbmVycyBieSB0eXBpbmc6IDxici8+PGJyLz4NCjxjb2RlPmRvY2tlciBjb250YWluZW5yIHN0b3AgW0ZJUlNUIENPTlRJQU5FUiBJRF0gW1NFQ09ORCBDT05UQUlORVIgSURdLi4uZXRjLi4uLjwvY29kZT4NCjxici8+DQoNCjxkaXY+T2ssIGNyZWF0ZSBhbmQgc3RvcCBmb2xsb3dpbmcgY29udGFpbmVycyBub3c8L2Rpdj48YnIvPg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBydW4gLXAgMzMwNjozMzA2IC0tZGV0YWNoIC0tbmFtZSBteXNxbCAtZSBNWVNRTF9SQU5ET01fUk9PVF9QQVNTV09SRD15ZXMgbXlzcWw8L2NvZGU+DQo8YnIvPg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBydW4gLXAgODA4MDo4MCAtZCAtLW5hbWUgYXBhY2hlaHR0cGQgaHR0cGQ8L2NvZGU+DQo8YnIvPg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBydW4gLXAgODA6ODAgLWQgLS1uYW1lIG5naW54IG5naW54PC9jb2RlPg0KPGJyLz48YnIvPg0KDQo8ZGl2PlRoZXJlIGFyZSBhbHNvIHNvbWUgbmVhdCB3YXlzIHRvIGdldCBob2xkIG9mIGluZm9ybWF0aW9uIGFib3V0IGNvbnRhaW5lcnMuIFNlZWluZyB3aGF0IGlzIGhhcHBlbmluZyBvbiB5b3VyIGNvbnRhaW5lciBpcyBqdXN0IGxpa2UgcnVuaW5pbmcgdGhlIFRhc2sgTWFuYWdlciBvbiBXaW5kb3dzLCBvciBBY3Rpdml0aXR5IE1vbml0b3Igb24gTWFjLCB0byBzZWUgdGhlIGtpbmRzIG9mIHByb2Nlc3NlcyB0aGF0IGFyZSBoYXBwZW5pbmcuIFRoZXJlIGFyZSBzb21lIGRpZmZlcmVudCBjb21tZW5kcyB5b3UgY2FuIHVzZSB0byBnZXQgZGlmZmVyZW50IHR5cGVzIG9mIGluZm9ybWF0aW9uIDwvZGl2Pg0KPGJyLz4NCg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBsb2dzIFtDT05UQUlORVIgTkFNRV08L2NvZGU+PGJyLz4NCjxjb2RlPmRvY2tlciBjb250YWluZXIgdG9wIFtDT05UQUlORVIgTkFNRV08L2NvZGU+PGJyLz4NCjxjb2RlPmRvY2tlciBjb250YWluZXIgaW5zcGVjdCBbQ09OVEFJTkVSIE5BTUVdPC9jb2RlPjxici8+DQo8Y29kZT5kb2NrZXIgY29udGFpbmVyIHN0YXRzIFtDT05UQUlORVIgTkFNRV08L2NvZGU+PGJyLz4NCjxici8+PGJyLz4NCg0KPGRpdj5Zb3UgbWlnaHQgYmUgc3RhcnRpbmcgdG8gbm90aWNlIHRoYXQgRG9ja2VyIGlzIGZ1bGwgb2YgaGFuZHkgd2F5cyB0byBnaXZlIHlvdSBpbmZvIGFib3V0IHdoYXQgaXMgZ29pbmcgb24uIFlvdSBjYW4gZ2l2ZSBpdCBhbGwga2luZHMgb2YgaW5zdHJ1Y3Rpb25zIHN1Y2ggYXMgPGNvZGU+LXA8L2NvZGU+IGFuZCA8Y29kZT4tZDwvY29kZT4uIERvY2tlciBoYXMgcmVhbGx5IG5pY2UgZG9jdW1lbnRhdGlvbiBmb3IgYWxsIG9mIHRoaXMgYW5kIGlmIHlvdSBwdXQgImhlbHAiIGF0IHRoZSBlbmQgb2YgYW55IGNvbW1hbmRzLCB5b3UgY2FuIGNhbiBhY2Nlc3MgdGhpcy4NCjwvZGl2Pg0KDQo8YnIvPg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBzdGF0cyBoZWxwPC9jb2RlPjxici8+DQo8YnIvPg0KDQo8ZGl2Pk9rLCBzbyByaWdodCBub3cgd2Ugc3RpbGwgaGF2ZW4ndCBkb25lIHZlcnkgbXVjaC4gQnV0IEkgaG9wZSB5b3Ugd2lsbCBhdCBsZWFzdCBhZ3JlZSB0aGF0IHRoZSBpZGVhIGJlaGluZCBhbGwgdGhpcyBkb2VzIHNlZW0gcHJldHR5IGNvb2wgZm9yIGRhdGEgc2NpZW50aXN0cy4gSSBsaWtlIHRoZSBpZGVhIG9mIGJlaW5nIGFibGUgdG8gc3BpbiB1cCBkaWZmZXJlbnQgbWFjaGluZXMgYW5kIGN1c3RvbWlzZSB0aGUgc29mdHdhcmUgaW4gdGhlbSBmb3IgbXkgZGF0YSBzY2llbmNlIG5lZWRzLiBJIGNhbiBoYXZlIGEgY29udGFpbmVyIHdpdGggYWxsIG15IGZhdm91cml0ZSBQeXRob24gbGlicmFyaWVzIGFuZCBKdXB0eWVyIG5vdGVib29rIHNldHVwLiBJIGNhbiBoYXZlIGFub3RoZXIgb25lIHdpdGggUiBTaGlueSBTZXJ2ZXIgc2V0IHVwLiBJIGNhbiBoYXZlIGFub3RoZXIgb25lIEdyYXBoIERCcy4gQW5kIEkgY2FuIGdldCBhbGwgdGhlc2UgdG8gY29tbXVuaWNhdGUgdG8gZWFjaCBvdGhlciB0b28gKGFuZCB3ZSB3aWxsIGdldCB0byBhbGwgb2YgdGhpcyBzb29uKS48L2Rpdj4NCg0KPGJyLz4NCjxoND5OZXh0IHN0ZXBzPC9oND4NCjxkaXY+R2l2ZW4gdGhhdCB0aGVzZSBjb250YWluZXJzIGFyZSBraW5kIG9mIGxpa2UgbWFjaGluZXMgd2UgaGF2ZSBzcHVuIHVwLCBhIGxvZ2ljYWwgbmV4dCBxdWVzdGlvbiBtaWdodCBiZSBob3cgdG8gZ2V0IGludG8gdGhlIG1hY2hpbmVzIHRvIHJ1biBjb21tYW5kcy4gQW5kIG9mIGNvdXJzZSB5b3UgY2FuIGRvIHRoaXMuIFNvIGNsb3NlIGRvd24gYWxsIHlvdXIgcnVubmluZyBjb250YWluZXJzLCBhbmQgdGhlIHJ1biB0aGUgZm9sbG93aW5nIGNvbW1hbmQgdG8gdHJ5IHRoaXMgbm93OiA8L2Rpdj4NCg0KPGJyLz4NCjxjb2RlPmRvY2tlciBjb250YWluZXIgcnVuIC1pdCAtLW5hbWUgbmdpbnggbmdpbnggYmFzaDwvY29kZT48YnIvPg0KPGJyLz4NCjxkaXY+IA0KIE11Y2ggb2YgdGhpcyB3aWxsIGJlIHN0dWZmIHdlIGhhdmUgc2VlbiBiZWZvcmUuIFRoZSBuZXcgdGhpbmcgdGhvdWdoIGlzIDxjb2RlPi1pdDwvY29kZT4uIFRoaXMgdGVsbHMgRG9ja2VyIHRvIHN0YXJ0IHVwIGEgY29udGFpbmVyIHB1dCB1cyBpbnRvIGludG8gaXQgaW4gaW50ZXJhY3RpdmUgbW9kZS4gSXQgYWxzbyB0ZWxscyB1cyAod2l0aCB0aGUgImJhc2giIHdvcmQpIHRoYXQgd2Ugd2FudCB0byBiZSBkcm9wcGVkIGludG8gdGhlIGJhc2ggcHJvbXB0LiBUaGlzIGltYWdlIGlzIGJhc2VkIG9mZiBhIERlYmlhbiBsaW51eCB3aGljaCBoYXMgYSBiYXNoIHByb21wdCBzbyBpdHMgbm8gcHJvYmxlbQ0KPC9kaXY+DQo8YnIvPg0KPGRpdj4NCllvdSB3aWxsIG5vdGljZSB0aG91Z2ggdGhhdCwgdGhhdCB3ZSBkaWRuJ3QgcnVuIHRoaXMgY29udGFpbmVyIGluIDxjb2RlPi1kPC9jb2RlPiBmbGFnLiBTbyBpdHMgbm90IHJ1bm5pbmcgaW4gdGhlIGJhY2tncm91bmQsIGFuZCBhcyBzb29uIGFzIHdlIGp1bXAgb3V0IG9mIHRoZSBjb250YWluZXIgKGJ5IHVzaW5nIDxjb2RlPmN0cmwtQzwvY29kZT4gb3IganVzdCB0eXBpbmcgJ2V4aXQnKSB0aGUgY29udGFpbmVyIGl0c2VsZiBqdXN0IHN0b3BzLiBJdCB3b3VsZCBtYWtlIG1vcmUgc2Vuc2UgaWYgd2UgY291bGQgc3RhcnQgYSBjb250YWluZXIsIGxlYXZlIGl0IHJ1bm5pbmcsIGFuZCBqdW1wIGluIGFuZCBvdXQgb2YgaXQgYXMgbmVlZGVkLiBTbyBsZXRzIHN0b3AgYW55IHJ1bm5pbmcgY29udGFpbmVycywgYW5kIGZpcnN0IHN0YXJ0IHVwIGEgbmV3IGNvbnRhaW5lci4NCjwvZGl2Pg0KDQo8YnIvPg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBydW4gLWQgIC0tbmFtZSBteVVidW50dSB1YnVudHU8L2NvZGU+PGJyLz4NCjxici8+DQo8ZGl2PldlIGNhbiBzZWUgdGhhdCBpdCBpcyBydW5uaW5nIGJ5IGRvaW5nIGEgPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBsczwvY29kZT4uIFRoZW4gd2UgY2FuIGp1bXAgaW50byB0aGlzIGNvbnRhaW5lciB1c2luZyB0aGUgPGNvZGU+ZXhlYzwvY29kZT4gY29tbWFuZC4gTm90ZSB0aGF0IHdlIGNvdWxkIGRvIHRoaXMgYnkgdXNpbmcgdGhlIG5hbWUgb2YgdGhlIGNvbnRhaW5lciB0aGF0IHdlIGNyZWF0ZWQsIG9yIHRoZSBjb250YWluZXIgSUQgKG9yIGV2ZW4gdGhlIGZpcnN0IGZldyBjaGFyYWN0ZXJzIG9mIHRoZSBjb250YWluZXIgSUQpPC9kaXY+DQo8YnIvPg0KPGNvZGU+ZG9ja2VyIGNvbnRhaW5lciBleGVjIC1pdCB1YnVudHUgYmFzaDwvY29kZT48YnIvPg0KPGJyLz4NCjxkaXY+T25jZSB5b3UgYXJlIGluIHRoZSBjb250YWluZXIgaXQgaXMganVzdCB0aGUgc2FtZSBhcyBiZWluZyBvbiB0aGUgdWJ1bnR1IGNvbW1hbmQgbGluZS4gWW91IGNhbiBjcmVhdGUgZGlyZWN0b3JpZXMsIGluc3RhbGwgdGhpbmdzIChpbiB0aGlzIGNhc2UgdXNpbmcgYXB0LWdldCBhcyBpdHMgdWJ1bnR1IGxpbnV4KSwgYW5kIGRvIGFueXRoaW5nIHlvdSBtaWdodCBkbyBpbiBhIHN0YW5kYXJkIGxpbnV4IG1hY2hpbmUuIA0KPC9kaXY+DQo8YnIvPg0KPGRpdj4NCk5vdyBkb24ndCBnZXQgZm9vbGxlZCBieSBhIGxpdHRsZSBnb3RjaGEgaGVyZS4gVGhlIGFib3ZlIGNhbGxzIDxjb2RlPmV4ZWM8L2NvZGU+IGFuZCB0aGVuIHBhc3NlcyBpdCA8Y29kZT5iYXNoPC9jb2RlPi4gU28geW91IG5lZWQgdGhlIGJhc2ggY29tbWFuZCBsaW5lIHRvIGJlIGF2YWlsYWJsZSBpbiB0aGUgY29udGFpbmVyIHVidW50dS4gSWYgSSB3YXMgcnVubmluZyBhbiBhbHBpbmUgbGludXggaW1hZ2UgKHdoaWNoIGRvZXMgbm90IGhhdmUgYSBiYXNoIHNoZWxsLCBidXQgYSBzaCBzaGVsbCksIHRoaW5ncyB3b3VsZCBiZSBhIGxpdHRsZSBkaWZmZXJlbnQ6DQo8L2Rpdj4NCjxici8+DQo8Y29kZT5kb2NrZXIgY29udGFpbmVyIHJ1biAtZCBhbHBpbmUgYWxwaW5lPC9jb2RlPjxici8+DQo8Y29kZT5kb2NrZXIgY29udGFpbmVyIGV4ZWMgLWl0IGFscGluZSBzaDwvY29kZT48YnIvPg0KPGJyLz4NCg0KDQoNCjxoND5Eb2NrZXJmaWxlczwvaDQ+DQoNCg0KPGRpdj5QcmV0dHkgc29vbiB5b3Ugd2lsbCB3YW50IHRvIGN1c3RvbWlzZSBzb21lIG9mIHRoZSBpbWFnZXMgdGhhdCB5byB1YXJlIHVzaW5nLiBZb3UgbWlnaHQgd2FudCBhIHN0YW5kYXJkIERlYmlhbiBpbWFnZSwgYnV0IHlvdSBhbHNvIHdhbnQgdG8gaW5zdGFsbCBhIGxpdHRsZSBleHRyYSBzb2Z0d2FyZS4gWW91IGRvbid0IHdhbnQgdG8gaGF2ZSB0byB1c2UgdGhlIDxjb2RlPmV4ZWM8L2NvZGU+IGNvbW1hbmQgdG8ganVtcCBpbnRvIGEgY29udGFpbmVyIGFuZCBkbyBhbGwgdGhpcyBzZXQgdXAuIEFuZCB0aGlzIGlzIHdoZXJlIGEgRG9ja2VyZmlsZSBjb21lcyBpbi4gSXQgaXMga2luZCBvZiBsaWtlIGEgc2hlbGwgc2NyaXB0IHRoYXQgaGFzIGJ1bmNoIG9mIHNpbXBsZSBjb21tYW5kcyB0aGF0IHNwZWNpZnkgeW91ciBpbWFnZSwgYW5kIGFueSBleHRyYSBzZXQgdXAgeW91IHdvdWxkIGxpa2UuPC9kaXY+DQo8YnIvPg0KPGRpdj5UaGUgYmVzdCB3YXkgdG8gcHJvdmlkZSBhbiBpbnR1aXRpb24gYXJvdW5kIHRoaXMgaXMgdG8gc3RhcnQgd2l0aCBhIHN1cGVyIHNpbXBsZSBleGFtcGxlLiBTdGFydCBieSBjcmVhdGluZyBhIG5ldyBkaXJlY3RvcnkgKGNhbGwgaXQgRG9ja2VyVGVzdCBvciBzb21ldGhpbmcgbG9naWNhbCkgYW5kIGluc2lkZSBvZiB0aGF0LCBjcmVhdGUgYSBuZXcgZmlsZSBhbmQgY2FsbCBpdCAiRG9ja2VyZmlsZSIuIE5vdGUgaXQgZG9lc24ndCBuZWVkIGFueSBraW5kIG9mIGV4dGVuc2lvbiwgaXRzIGp1c3QgIkRvY2tlcmZpbGUiLiBOb3cgb3BlbiBpdCB1cCBpbiB5b3VyIHRleHQgZWRpdG9yIG9mIGNob2ljZSAoSSBtb3N0bHkgdXNlIEF0b20gb3IgU3VibGltZSBUZXh0IHRoZXNlIGRheXMgYnV0IGFueXRoaW5nIHlvdSBoYXZlIGlzIGZpbmUpIGFuZCB0eXBlIGluIHRoZSBmb2xsb3dpbmc6IDwvZGl2Pg0KDQo8Y29kZT4NCjxici8+DQojPGJyLz4NCiMgQSBmaXJzdCBzdXBlciBzaW1wbGUgZXhhbXBsZSBvZiBhIERvY2tlcmZpbGUgPGJyLz4NCiM8YnIvPg0KPGJyLz5NQUlOVEFJTkVSIEphbWllIEdhYnJpZWwgImVtYWlsQHNvbWVlbWFpbC5jb20iDQo8YnIvPkZST00gdWJ1bnR1OmxhdGVzdA0KPGJyLz4NCjxici8+UlVOIGFwdC1nZXQgdXBkYXRlDQo8YnIvPlJVTiBhcHQtZ2V0IGluc3RhbGwgLXkgcHl0aG9uIHB5dGhvbi1waXAgd2dldA0KPGJyLz5SVU4gcGlwIGluc3RhbGwgRmxhc2sNCjxici8+UE9SVCA1MDAwIDUwMDANCjxici8+QUREIGhlbGxvLnB5IC9ob21lL2hlbGxvLnB5DQo8YnIvPldPUktESVIgL2hvbWUNCjwvY29kZT4NCjxici8+DQo8YnIvPg0KDQo8ZGl2PlRoZSBzeW50YXggaGVyZSBpcyBwcmV0dHkgc3RyYWlnaHRmb3J3YXJkLiBMZXQncyB0YWtlIGEgd2FsayB0aHJvdWdoIGl0LiBUaGUgPGNvZGU+IzwvY29kZT4gc3ltYm9sIG1ha2VzIHdoYXRldmVyIGZvbGxvd3MgaXQgYSBjb21tZW50LiBBbGwgdGhlIGFsbC1jYXBpdGFsLWxldHRlciBjb21tYW5kcywgdGhpbmdzIGxpa2UgbGlrZSA8Yj5NQUlOVEFJTkVSPC9iPiwgPGI+RlJPTTwvYj4gYW5kIDxiPlJVTjwvYj4gYXJlIGtleXdvcmRzIHRoYXQgYWxsb3cgRG9ja2VyIHRvIGhhdmUgc29tZSBiYXNpYyBpbnN0cnVjdGlvbnMgb24gYnVpbGRpbmcgYW4gaW1hZ2UgYW5kIGFkZGluZyB0aGluZ3MgaW50byBpdC4gSXQgaXMgYWxzbyBwb3NzaWJsZSB0byBpbmNsdWRlIHNvbWUgYmFzaWMgbWV0YWRhdGEgYWJvdXQgdGhpcyBpbWFnZSAoaGVyZSB1c2luZyB0aGUga2V5d29hcmQgPGI+TUFJTlRBSU5FUjwvYj4pLiA8L2Rpdj48YnIvPg0KDQo8ZGl2PlRoZSBhYm92ZSBEb2NrZXJmaWxlIHN0YXJ0cyB3aXRoIGEgY29tbWVudCwgdGhlbiB1c2VzIHRoZSA8Yj5NQUlOVEFJTkVSPC9iPiBpbnN0cnVjdGlvbiwgd2hpY2ggYWxsb3dzIG1lIHRvIGluY2x1ZGUgYSBsaXR0bGUgbWV0YWRhdGEgc2F5aW5nIHdobyBpcyBidWlsZGluZyBhbmQgbWFpbnRhaW5pbmcgdGhpcyBEb2NrZXJmaWxlLiBUaGUgaXQgdXNlcyB0aGUgPGI+RlJPTTwvYj4gY29tbWFuZCB0byBzcGVjaWZpeSBhIGJhc2UgaW1hZ2UgdGhhdCBpdCB3aWxsIGdldCBmcm9tIERvY2tlcmh1YiBpZiBpdCBpcyBub3QgZm91bmQgb24gbXkgbG9jYWwgbWFjaGluZS4gPC9kaXY+DQo8YnIvPg0KPGRpdj5UaGVuLCBpdCB1c2VzIGEgc2VyaWVzIG9mIDxiPlJVTjwvYj4gY29tbWFuZHMgdG8gcnVuIGNvbW1hbmRzIG9uIHRoZSBjb21tYW5kbGluZSBvZiB0aGlzIGNvbnRhaW5lciBvbmUgaXQgaXMgY3JlYXRlZC4gVGhlIGNvbW1hbmRzIHRlbGwgdGhlIGltYWdlIChhbiB1YnVudHUgaW1hZ2UgaW4gdGhpcyBjYXNlKSB0byBkbyBhbiA8Y29kZT5hcHQtZ2V0IHVkcGF0ZTwvY29kZT4gdG8gbWFrZSBzdXJlIGFsbCB1YnVudHUgc3RhbmRhcmQgcGFja2FnZXMgYXJlIHVwIHRvIGRhdGUsIHRoZW4gZG8gYW4gPGNvZGU+YXB0LWdldCBpbnN0YWxsPC9jb2RlPiB0byBnZXQgc29tZSBsaWJyYXJpZXMgKGluIHRoaXMgY2FzZSwgcHl0aG9uLCBweXRob24tcGlwLCBhbmQgd2dldCksIGFuZCB0aGVuIGRvZXMgPGNvZGU+cGlwIGluc3RhbGwgRmxhc2s8L2NvZGU+IHRvIGluc3RhbGwgdGhlIEZsYXNrIHdlYiBmcmFtZXdvcmsuIEZpbmFsbHkgaXQgdXNlcyB0aGUga2V5d29yZCA8Yj5QT1JUPC9iPiB0byBpbmRpY2F0ZSB3aGljaCBwb3J0IGZyb20gbXkgbG9jYWwgbWFjaGluZSB3aWxsIGJlIGZvcndhcmRlZCB0byB0aGUgcG9ydCBvbiB0aGUgY29udGFpbmVyIChpbiB0aGlzIGNhc2UgYWxsIG15IGxvY2FsIHRyYWZmaWMgb24gcG9ydCA1MDAwIHdpbGwgZ28gdG8pIFRoZSBJIGFtIHVzaW5nIHRoZSBQT1JUIGNvbW1hbmQgdG8gaW5kaWNhdGUgUE9SVDwvZGl2PiANCjxici8+DQo8ZGl2Pk9uZSBkb25lLCB3ZSBoYXZlIGJ1aWx0IGEgYmFzZSBpbWFnZSBvZiB1YnVudHUsIGFuZCB0aGVuIGN1c3RvbWlzZWQgdG8gb3VyIG93biBuZWVkcywgYW5kIGtlcHQgYWxsIHRoaXMgaW4gYSBoYW5keSBjb25maWcgc2NyaXB0LiBMZXRzIG5vdyBidWlsZCB0aGlzIGltYWdlIGJ5IGRvaW5nIHRoZSBmb2xsb3dpbmc6PC9kaXY+IA0KDQo8YnIvPg0KPGNvZGU+ZG9ja2VyIGltYWdlIGJ1aWxkIC10IG15Y3VzdG9taW1hZ2UgLiA8L2NvZGU+DQo8YnIvPg0KPGJyLz4NCjxkaXY+Tm90ZSB0aGUgdXNlIG9mIHRoZSA8Y29kZT4uPC9jb2RlPiBpbiB0aGUgYWJvdmUgY29tbWFuZC4gVGhpcyB0ZWxscyBEb2NrZXIgdG8gbG9vayBmb3Igc29tZXRoaW5nIGluIHRoZSBjdXJyZW50IGRpcmVjdG9yeSB3aXRoIHRoZSBuYW1lIG9mICJEb2NrZXJmaWxlIi4gWW91IGNvdWxkIGFsc28ganVzdCB3cml0ZSA8Y29kZT5kb2NrZXIgaW1hZ2UgYnVpbGQgLXQgY3Vzb21uZ2lueCBEb2NrZXJmaWxlIDwvY29kZT4gDQo8L2Rpdj4NCjxici8+DQo8ZGl2Pg0KTm93IHlvdSBjYW4gcnVuIHRoaXMgY3VzdG9taXNlZCBjb250YWluZXIgaW4gYSB3YXkgdGhhdCB3ZSBjb3VsZCBleHBlY3QsIGFzIGZvbGxvd2luZzoNCjwvZGl2Pg0KPGJyLz4NCjxjb2RlPmRvY2tlciBjb250YWluZXIgcnVuIC1wIDUwMDA6NTAwMCBteWN1c3RvbWltYWdlIDwvY29kZT48YnIvPg0KPGJyLz4NCjxkaXY+DQpOb3RlIGFub3RoZXIgbGl0dGxlIGdvdGNoIGhlcmUuIEV2ZW4gdGhvdWdoIHdlIHNwZWNpZmllZCB0aGUgcG9ydCB3aXRoIHRoZSBrZXl3b3JkIDxiPlBPUlQ8L2I+IGluIHRoZSBEb2NrZXJmaWxlLCB3ZSBzdGlsbCBuZWVkZWQgdG8gcHV0IGl0IG9uIHRoZSBjb21tYW5kIGxpbmUgd2l0aCB0aGUgLXAgZmxhZy4gVGhpcyBpcyBhIHF1aXJrIG9mIHRoZSBEb2NrZXJmaWxlLCBhbmQgeW91IGNhbiB0aGluayBvZiB0aGUgPGI+UE9SVDwvYj4ga2V5d29yZCBpbiB0aGUgRG9ja2VyZmlsZSwganVzdCB0aGVyZSBmb3IgdGhlIHB1cnBvc2Ugb2YgdGVsbGluZyBhbnlvbmUgdXNpbmcgaXQgdGhhdCB0aGV5IHdpbGwgbmVlZCB0byB1c2UgdGhlIDxjb2RlPi1wPC9jb2RlPiB3aXRoICBmbGFnLiBKdXN0IGFjY2VwdCBpdCBhcyB3ZWlyZCBxdWlyay4gVGhlcmUgaXMgYSBuZWF0IHdheSB0byBnZXQgYXJvdW5kIHRoaXMgd2l0aCBkb2NrZXItY29tcG9zZSB0aGF0IEkgd2lsbCBleHBsYWluIGxhdGVyLiANCjwvZGl2Pi4NCjxici8+DQo8ZGl2PlRoZXJlIGlzIGdyZWF0IGRvY3VtZW50YXRpb24gdGhlIGtleXdvcmRzIHVzZWQgaW4gdGhlIERvY2tlcmZpbGVzLCBhbmQgSSBlbmNvdXJhZ2UgeW91IHRvIGNoZWNrIGl0IG91dCA8YSBocmVmPSIiPmhlcmU8L2E+LiBFdmVuIGJldHRlciwgZ2V0IGhvbGQgb2Ygc29tZSBkb2NrZXIgZmlsZXMgKGxpa2UgdGhpcyBvbmUsIHRoaXMgb25lLCBvciB0aGlzIG9uZSkgYW5kIHBsYXkgYXJvdW5kIHdpdGggdGhlbS4gT25jZSB5b3Ugc3BlbmQgYSBmZXcgZGF5cyBnZXR0aW5nIHVzZSB0byBjcmVhdGluZyBEb2NrZXJmaWxlcyBhbmQgY3VzdG9taXNpbmcgdGhlbSB0byB5b3VyIG5lZWRzLCB5b3Ugd2lsbCByZWFsbHkgc3RhcnQgdG8gc2VlIHRoZSBiZW5lZml0cyBvZiBob3cgdGhpcyBtaWdodCBoZWxwIHlvdSBkbyBkYXRhIHNjaWVuY2UuPC9kaXY+DQoNCg0KPGJyLz4NCjxoND5JbnRlcmFjdGluZyB3aXRoIHlvdXIgbG9jYWwgZmlsZSBzeXN0ZW08L2g0Pg0KPGRpdj5TbyB3ZSBjYW4gZG8gcXVpZXQgYSBiaXQgbm93LiBXZSBjYW4gYnVpbGQgaW1hZ2VzLiBXZSBjYW4gY3JlYXRlIGNvbnRhaW5lcnMgZnJvbSB0aGVtIGFuZCBnbyBpbnRvIHRob3NlIGNvbnRhaW5lcnMgYW5kIHJ1biBjb21tYW5kcy4gV2UgY2FuIGJ1aWxkIGN1c3RvbSBpbWFnZXMgdG8gc3VpdCBvdXIgbmVlZHMgdXNpbmcgRG9ja2VyZmlsZXMuIEFzIGEgZGF0YSBzY2llbmNlLCB0aGlzIGdpdmVzIG1lIHRoZSBjb250cm9sIHRvIGhhdmUgYSBhbnkga2luZCBvZiB0ZWNoIHN0YWNrIEkgY2FuIHRoaW5rIG9mIHNldCB1cCBhbmQgcmVhZHkgdG8gZ28uIDwvZGl2Pg0KPGJyLz4NCjxkaXY+DQpCdXQgdGhlcmUgYXJlIHN0aWxsIHNvbWUgb3RoZXIgdGhpbmdzIHdlIHJlYWxseSB3YW50IHRvIGJlIGFibGUgdG8gZG8gaGVyZS4gUmlnaHQgYXQgdGhlIHRvcCBvZiB0aGUgbGlzdCwgaXMgdGhlIGFiaWxpdHkgdG8gIGludGVyYWN0IHdpdGggbXkgbG9jYWwgZmlsZXMuIEltYWdpbmUgdGhpcyBzY2VuYXJpbzogSSBoYXZlIGEgYnVuY2ggb2YgLlIgZmlsZXMgb24gbXkgbG9jYWwgbWFjaGluZS4gQnV0IEkgd2FudCB0byB1c2UgYSB2ZXJzaW9uIG9yIFJTdHVkaW8gU2VydmVyIChsaWtlIHRoZSBpbWFnZSBoZXJlKS4gSSB3YW50IHRoaXMgY29udGFpbmVyIHRvIGJlIGFibGUgdG8gc2VlIG15IGZpbGVzLCBhbmQgbWFrZSBjaGFuZ2VzIHRvIHRoZW0uPC9kaXY+DQo8YnIvPg0KDQo8ZGl2PlRoaXMgcXVlc3Rpb24gaXMgcmVhbGx5IHJlYWx0ZWQgdG8gaG93IHlvdSBkZWFsIHdpdGggcGVyc2lzdGFudCBkYXRhIFlvdSB3YW50IHRvIGJlIGFibGUgdG8gc3BpbiB1cCBjb250YWluZXJzLCBhcyBuZWVkZWQsIGFuZCB5b3UgY2FuIHRocm93IHRoZW0gYXdheSBhbmQgZ3JhYiBuZXdzIG9uZXMgYXQgYW55IHRpbWUuIEJ1dCB5b3UgZG9uJ3Qgd2FudCB0byBkbyB0aGF0IHdpdGggeW91ciBkYXRhLiBUbyBkZWFsIHdpdGggdGhpcyBEb2NrZXIgcHJvdmlkZXMgc29tZSByZWFsbHkgcG93ZXJmdWwgc29sdXRpb25zLiBUaGUgZmlyc3Qgc29sdXRpb24gaXMgPHN0cm9uZz52b2x1bWVzPC9zdHJvbmc+LiBUaGVzZSBhcmUgYWN0dWFsbHkgY3JlYXRlZCBhdCB0aGUgc2FtZSB0aW1lIGFzIGNvbnRhaW5lcnMgdG8gaG9sZCBkYXRhIGNyZWF0ZWQgYnkgdGhlIGNvbnRhaW5lci4gV2VuIHlvdSB0aHJvdyBvdXQgYSBjb250YWluZXIsIHZvbHVtZXMgc3RpbGwgYXJvdW5kLCBhbmQgeW91IGNhbiBjaGVjayB0aGVtIG91dCB3aXRoIHRoZSBmb2xsb3dpbmc6IA0KPGJyLz4NCjxjb2RlPmRvY2tlciB2b2x1bWUgbHM8L2NvZGU+DQo8L2Rpdj4NCg0KPGRpdj4NClRoZSBzZWNvbmQgc29sdXRpb24sIHdoaWNoIGlzIGZhciBtb3JlIGhhbmR5IGZvciBkYXRhc2NpZW5jZSwgaXMgdGhlIHVzZSBvZiA8c3Ryb25nPmJpbmQtbW91bnRpbmc8L3N0cm9uZz4uIFRoaXMgbGV0cyB5b3UgYWNjZXNzIGxvY2FsIGZpbGVzIGFuZCBkaXJlY3RvcmllcyBmcm9tIGluc2lkZSBhIERvY2tlciBjb250YWluZXIuIFRoaXMgaXMgdGhlIHJlYWxseSBoYW5keSBvbmUgZm9yIGRhdGEgc2NpZW50aXN0IHNldHVwcy4gDQo8L2Rpdj4NCg0KDQoNCg0KDQoNCjxoND5Vc2luZyBkb2NrZXItY29tcG9zZSBmb3IgYmluZCBtb3VudHMgYW5kIHBvcnRzPC9oND4NCg0KDQogYmluZCBtb3VudGluZyAtIHVzaW5nIGRvY2tlciBmb3IgbG9jYWwgZGV2ZWxvDQogbWFwcGlnbiBvZiBob3N0IGZpbGUgb3IgZG9yZWN0b3J5IHRvIGNvaW50YWluZXIuLi5sb2NhdGlvbnMgcG9pbnQgc2FtZS4uLi4NCiBob3N0IGZpbGVzIHdpbi4uLi4NCiBjYW50IHNwZWNpZml5IGluIERvY2tlcmZpbGUuLi4ubmVlZCB0byBiaW5kLi4uYnV0IHlvdSBjYW4gZG8gdGhpcyANCiANCiAuLi4gcnVuIC12IC8vYy91c2Vycy9icmV0Oi9wYXRoL2NvbnRhaW5lcg0KICBjaGVjayBvdXQgZG9ja2VyZmlsZSBzYW1wbGUtMiAtIG5vIHZvbHVtZXMgaW4gaGVyZS4uLi4NCiAgDQogIGRvY2tlciBjb3RuYWluZXIgcnVuIC1kIC0tbmFtZSBuZ2lueCAtcCA4MDo4MCAtdiAkKHB3ZCk6L3Vzci9zaGFyZS9uZ2lueCBuZ2lueA0KICANCiAgb3BlbiBhbm90aGVyIHRlcm1pbmdhbC4uLi5lZGl0IGZpbGUgb24gdGhlIGhvc2UgLGFuZCBoY2VjayBpbiBjb250YWluZXIuLi4uDQogIA0KICBnbyBpbnRvIGNvbnRhaW5lciBhbmQgY2hlY2ssIGFuZCBlZGl0Li4uLnNvIGVkaXQgZnJvbSBib3RoIHBsYWNlcy4uLi4NCiAgDQogIG5vdyB0aGluayBhYm91dCBjb21wbGljYXRlZCBkZXYgc2V0dXBzLi4uLi50aGlzIHNvbHZlcyBhbGwgdGhhdCENCiAgDQogIG5vdCB5b3UgY2FuIGxvb2sgYXQgbG9ncyBvZiBjb250YWluZXIgd2hlbiB5b3UgYXJlIGRvaW5nIGRldi4uLi4uDQogIA0KICANCiAgDQogIA0KICA+PiBVcGRhdGVzIEFzc2lnbm1lbnQgTGVjdHVyZSA1MCAtIGJpbmQgbW91bnRzLi4uLi4NCiAgDQogIA0KICANCiAgSWRlYSAtIG1vdW50IGRhdGEgaW50by4uLi4NCiAgDQogIERvY2tlciBjb21wb3NlIC0gYmluZCBtb3VudGluZyBzaW1wbGVyLi4uLiBwb3J0cyBzaW1wbGVyLi4uLiBKZWt5bGwgaXMgZ2l0aHViIHBhZ2VzLi4uLg0KICANCiAgdXNlIGJpbmRtb3VudC1zbWFwbGUtMQ0KICANCiAgZWRpdCB0aGVzZSwgdGhlbiBjb250YWluZXIgcnVubmluZyBiYWNrZ3JvdW5kDQogIA0KICBkb2NrZXIgcnVuIC1wIDgwOjQwMDAgLXYgJChwd2QpOi9zaXRlL2JyZXRmaXNoZXIvamVreWxsLXNlcnZlICMgdGhpcyB3aWxsIG9wZW4gdGhpbmdzIHVwIGFuZCB0aGVuIHJlZnJlc2ggYnJvd3NlciB0byBzZWUgY2hhbmdlcy4uLi4uDQogIA0KICB5b3UgY2FuIGNoYW5nZSBfcG9zdHMgZGlyZWN0b3J5IA0KICANCiAgYXNzaWdubWVudCAtIHVzZSBiaW5kbW91bnQtc2FtcGxlLTEgZGlyZWN0b3J5Li4uLg0KICBkb2NrZXIgY29udGFpbmVyIHJ1biAtcCA4MDo0MDAwIC12JChwd2QpOi9zaXRlIGJyZXRmaXNoZXIvamVreWxsLXNlcnZlICAjaW1hZ2UuLi4uIHRoaXMgd2lsbCBzdGFydCBzZXJ2ZXIuLi4uDQogIA0KICANCiAgDQogIDEuIA0KDQphc2RmYXNkZmENCjwvZGl2Pg0KPGg0PkdvaW5nIGRlZXBlciBpbnRvIERvY2tlcjwvaDQ+DQo8ZGl2PlNvIGF0IGFib3V0IHRoaXMgcG9pbnQsIGFzIGEgZGF0YSBzY2llbmNlIHR5cGUsIHlvdSBzaG91bGQgc3RhcnQgdG8gaGF2ZSBhIGVub3VnaCBnbyBmb3J3YXJkLiAsIHlvdSByZWFsbHkgaGF2ZSBldmVyeXRoaW5nIHlvdSBuZWVkIHRvIHRvIGFrZSBjb250cm9sIG9mIHlvdXIgZGF0YSBzY2llbmNlIHN0YWNrLiBZb3UgYXJlIG5vdyBnb29kIHRvIGdvIGZvciBkYXRhIHNjaWVuY2UuIERvd25sb2FkIGRpZmZlcmVudCBpbWFnZXMsIGNoZWNrIHRoZW0gb3V0LiBCdXQgb2YgY291cnNlIERvY2tlciBoYXMgc29tZSBvdGhlciBodWdlIGZlYXR1cmVzLCB0aGluZ3MgbGlrZSA8c3Ryb25nPm5ldHdvcmtzPC9zdHJvbmc+LDxzdHJvbmc+ZG9ja2VyLWNvbXBvc2U8L3N0cm9uZz4gYW5kIDxzdHJvbmc+ZG9ja2VyIHN3YXJtPC9zdHJvbmc+IHdoaWNoIGxldHM8L2Rpdj4NCjxici8+DQo8aHIvPg0KDQpWb2x1bWVzLi4uLg0KY2FuIHB1dCB0aGlzIGludG8gYSBkb2NrZXIgZmlsZQ0KDQpjaGVjayBvdXQgbXlzcWwgZG9ja2VyIGltYWdlIGh1Yi4uLi5sb29rIGF0IGxhdGVzdCBkb2NrZXIgZmlsZS4uLi4NCg0KPiBWT0xVTUUgL3Zhci9saWIvbXlzcWwgLi4uLi4gdGhpcyBjcmVhdGVzIHZvbHVtZSBhbmQgcHV0cyBpdCBpbiBkaXJlY3Rvcg0KIE5PdGUgdGhhdCB2b2x1bWVzIG5lZWQgbWFudWFsIGRlbGl0aW9uDQoNCj4gZG9ja2VyIHB1bGwgbXlzcWwNCg0KPiBkb2NrZXIgaW1hZ2UgaW5zcGVjdCBteXNxbCAjIHlvdSBjYW4gc2VlIHRoZSB2b2x1bWUNCg0KPiBkb2NrZXIgY29udGFpbmVyIHJ1biAtZCAtLW5hbWUgbXlzcWwgLWUgTVlTUUxfQUxMT1dfRU1QVFlfUEFTU1dPUkQ9VHJ1ZSBteXNxbA0KDQo+IGRvY2tlciBjb3RuYWluZXIgaW5zcGVjdCBteXNxbCAjIHdpbGwgc2hvdyB2b2x1bWUsIHNlZSB3aGVyZSB2b2x1bWUgbGl2aW5nIG9uIGhvc3QgKHdoY2loIGlzIHJlYWxseSBpbiBhIGxpbnV4IHZtKQ0KDQo+IGRvY2tlciB2b2x1bWUgbHMgDQoNCj4gZG9ja2VyIHZvbHVtZSBpbnNwZWN0DQoNCmNvbnNpZGVyLCByZXJ1biBteXNxbCBzZWUgdHdvIHZvbHVtZW5zIG5vdy4uLi4gd2hlbiB5b3Ugc3RvcCBjb250YWluZXJzLCB2b2x1bWVzIHN0aWxsIHRoZXJlLi4uLg0KDQpOYW1lZCB2b2x1bWVzLi4uLi4NCg0KPiBkb2NrZXIgY29udGFpbmVyIHJ1biAtZCAtLW5hbWUgbXlzcWwgLWUgTVlTUUxfQUxMT1dfRU1QVFlfUEFTU1dPUkQ9VHJ1ZSAtdiBteXNxbC1kYjovdmFyL2xpYi9teXNxbCBteXNxbCAjIG5vdGUgcHV0IHBhaXIgb2YgdmFsdWVzLCBsaWtlIGRpY3QgYmV0d2VuIGNvbG9uDQoNCj4gZG9ja2VyIHZvbHVtZSBscw0KPiBkb2NrZXIgdm9sdW1lIGluc3BlY3QgIyBtdWNoIGVhc2llciB0byB3b3JrIHdpdGgNCj4gZG9ja2VyIHZvbHVtZSBjcmVhdGUgI2NvdXBsZSBvZiBjYXNlcyB3aGVyZSB5b3UgY3JlYXRlYSAgdm9sdW1lLi5idXQgdGhhdHMgYSBmdXR1cmUgdGluZw0KDQpUYWtlIGhvbWUuLi4uLmNyZWF0ZSBjb250YWluZXJzIHdpdGggbmFtZWQgdm9sdW1lcw0KDQoNCjxoND5OZXR3b3JrcyBpbiBEb2NrZXI8L2g0Pg0KPGRpdj5UaGVyZSBpcyBzdXBlciBjb29sIHRoaW5nIGluIERvY2tlciBjYWxsZWQgPHN0cm9uZz5kb2NrZXItY29tcG9zZXI8L3N0cm9uZz4gdGhhdCBsZXRzIHlvdSBzcGluIHVwIG11bHRpcGxlIGNvbnRhaW5lcnMgYXQgb25jZSBhbmQgYmVjb21lcyByZWFsbHkgaGFuZHkgaW4gc29tZSBzaXR1YXRpb25zLiBCdXQgdG8gZ2V0IGludG8gdGhpcywgd2UgbmVlZCB0byB0YWxrIGFib3V0IHNvbWUgb3RoZXIgYXNwZWN0cyBvZiBkb2NrZXIgZmlyc3QhIFNvIGxldHMgc2hpZnQgZ2VhcnMgYSBsaXR0bGUgYW5kIHRhbGsgYWJvdXQgbmV0d29ya3MgaW4gRG9ja2VyPC9kaXY+DQo8YnIvPg0KPGRpdj5CZWNhdXNlIERvY2tlciBpcyBub3QganVzdCBhYm91dCBpbWFnZXMgYW5kIGNvbnRhaW5lcnMsIGl0cyBhYm91dCBuZXR3b3JrcyB0b28uIERvY2tlciBsZXRzIHVzIHNwaW4gdXAgYW55IGtpbmQgb2YgY29udGFpbmVycywgYW5kIHRoZW4gZ2l2ZXMgdXMgYSB3YXkgdG8gc2V0IHRoZXNlIGNvbnRhaW5lcnMgdXAgb24gbmV0d29ya3Mgc28gdGhlIGNvbnRhaW5lcnMgY2FuIGNvbW11bmljYXRlIHRvIHRoZW1zZWx2ZXMgYW5kIHRoZSBvdXRzaWRlIHdvcmxkLiAgRG9ja2VyIGlzIGdlYXJlZCB1cCB0byBtYWtlIHdvcmtpbmcgd2l0aCBuZXR3b3JrcyBlYXN5LCBidXQgaXRzIGFsc28gY29tcGxldGVseSBjdXN0b21pc2VhYmxlIHNvIHlvdSBjYW4gZG8gcmVhbGx5IGFkdmFuY2VkIHNldHVwcy4NCg0KDQoNCg0KZG9ja2VyIGNvbnRhaW5lciBydW4gLXAgIyB0aGlzIC1wIG1lYW5zIHlvdSBjYW4gZXhwb3NlIHBvcnRzDQogTmVlZCB0byBrbm93IGEgYml0IG91dCBUQ0lQLCBOZXdvcmtzIGRldGFjaA0KIGJhdHRlcmllcyBpbmNsdWRlZCBidXQgcmVtb3ZhYmxlIC0geW91IGNhbiBkbyBodWdlIGN1c3RvbWlzYXRpb24NCiAtcCBleHBvc2VycyBjb250YWluZXJzIG9uIHRvIHRoZSBwaHlzaWNhbCBuZXRvd3JrDQogQWxsIGNvbnRhaW5lcnMgY29ubmVjdGVkIHRvIHByaXZhdGUgdmlydHVhbCBuZXR3b3JrICJicmlkZ2UiDQpzbyBoYXMgaXRzIG93biBicmlkZ2UNCiBFYWNoIHZpcnR1YWwgbmV0d29yayByb3V0ZXMgdGhyb3VnIGEgTkFUIGZpcmV3YWxsIG9uIGhvc3QgSVANCiAtcCBub3QgbmVlZGVkIGZvciBjb250YWluZXJzIHRhbGtpbmcNCiBBbGwgc2V0dGluZ3MgY3VzdG9taXNhYmxlDQogWW91IGNhbiBhdHRhY2ggdG8gbXVsdGlwbGUgdmlydHVhbCBuZXR3b3Jrcw0KIFlvdSBjYW4gc2tpcCB2aXJ0dWFsIG5ldHdva3MgYW5kIHVzZSBob3N0IElQICgtLW5ldD1ob3N0KQ0KIENhbiB1c2UgZGlmZmVyZW50IGRyaXZlcnMNCm9ja2VyIGNvbnRhaW5lciBydW4gLXAgODA6ODAgLS1uYW1lIHdlYmhvc3QgLWQgbmdpbngNCmRvY2tlciBjb250YWluZXIgcG9ydCB3ZWJob3N0ICMgZ2V0IHBvcnRzIGZvcndhcmRpbmcgdHJhZmZpYw0KIGNvbnRhaW5lciB1c2VzIGRpZmZlcmVudCBJUA0KIHRvIGdldCBJUDoNCmRvY2tlciBjb250YWluZXIgaW5zcGVjdCAtLWZvcm1hdCAne3suTmV0d29ya1NldHRpbmdzLklQQWRkcmVzc319JyB3ZWJob3N0DQpkb2NrZXIgY29udGFpbmVyIGluc3BlY3Qgd2ViaG9zdA0KIGNhbiBhbHNvIGdldCBhbGwgc3BlY3Mgb24gY29udGFpbmVyIHVzaW5nIGFib3ZlDQogZG9ja2VyIGlzIDE3MiBuZXR3b3JrLi4uLg0KIEhvc3QgY29ubmVjdGVkIHRvIHBoeXNpY2FsIG5ldHdvcmsgdmlhIGZpcmV3YWxsIChibG9ja3MgdHJhZmZpYykgYW5kDQogYW55IHRyYWZmaWMgTmF0dGVkDQogY29uY2VwdCBvZiB2aXJ0dWFsIG5ldHdvcmtzIGluIG1hY2hpbmVzIChicmlkZ2Ugb3IgRG9ja2VyMCkNCiBuZXcgY29udGFpbmVyIGNvbmVuY3RzIHRvIHZpcnR1YWwgYW5kIHRoZW4gbmV0d29yayBjb25uZWN0cyB0bw0KIHlvdXIgY29tcHV0ZXIgYWRkcmVzcywgLXAgODA6ODAgbWVhbnMgYW55IHRyYWZmaWMgdGhyb3VnaCBuZXR3b3JrDQogc2Vjb25kIGNvbnRhaW5lciBjYW4gYmUgc2V0IHVwIG9uIHNhbWUgbmV0d29yaywgYnV0IG5lZWQgdG8gZXhwb3NlIGV4dGVybmFsbHkNCiBDYW4gY3JlYXRlIGFub3RoZXIgbmV0d29yayB3aXRoIGl0cyBvd24gLXAgYW5kIHdpbGwgdGFrZSB0cmFmZmljIGZyb20gb3V0c2lkZQ0KIFlvdSBjYW4gaGF2ZSBzZXBlcmF0ZSBuZXR3b3JrcyBhbmQgdXRpbGlzZSBwdWJsaXNoYWJsZSBwb3J0cyBvbiBtYWNoaW5lLA0KIG9yIHlvdSBjYW4gY29ubmVjdCB2aWEgbmV0d29yaw0KQW55d2hlcmUgSSBkbyBhIGRvY2tlciBjb250YWluZXIgcnVuIDxzdHVmZj4gbmdpbnggLCB3aGVyZSBuZ2lueCAgaXMgdGhlIGltYWdlIHlvdSBzaG91bGQgdXNlLCByZXBsYWNlIHRoYXQgd2l0aCBuZ2lueDphbHBpbmUgLCB3aGljaCBzdGlsbCBoYXMgcGluZyBjb21tYW5kIGluIGl0Lg0KIGJyaWRnZSBpcyBkZWZhdWx0IG5ldHdvcmsgdGhhdCBjb25uZWN0cyB0byBmaXJld2FsbA0KIGRvY2tlciBuZXR3b3JrIGNyZWF0ZSBteV9hcHBfbmV0ICMgY3JlYXRlcyBhIG5ldyB2aXJ0dWFsIG5ldHdvcmsNCiBkZWZhdWx0IGRyaXZlciBpcyBicmlkZ2Ugbm90IGFkdmFuY2VkLg0KZG9ja2VyIG5ldHdvcmsgY3JlYXRlIC0taGVscA0KZG9ja2VyIGNvdG5haW5lciBydW4gLWQgLS1uYW1lIG5ld19uZ2lueCAtLW5ldHdvcmsgbXlfYXBwX25ldCBuZ2lueA0KIGNyZWF0ZXMgYW5kIGNvbm5lY3RzIHRvIGEgY3JlYXRlZCBuZXR3b3JrDQpkb2NrZXIgbmV0d29yayBjb25uZWN0IDxuZXcgbmV0d29yaz4gPGNvbnRhaW5lciB0byBjb25uZWN0Pg0KIHNvIGxldHMgY3JlYXRlIGFub3RoZXIgY29udGFpbmVyDQpkb2NrZXIgY29udGFpbmVyIHJ1biAtcCA4MDgwOjgwIC1kIC0tbmFtZSBhcGFjaGVodHRwZCBodHRwZA0KIGFuZCB0aGVuIGNvbm5lY3QgaXQgdG8gdGhlIHNhbWUgbmV0d29yaw0KIGFuZCB0aGVuIHJ1biBkb2NrZXIgbmV0d29yayBpbnNwZWN0IDxuZXR3b3JrX25hbWU+DQogQWxzbyBkaXNjb25uZWN0DQpkb2NrZXIgbmV0d29yayBkaXNjb25uZWN0IGFwcF9zdGFjayAzN2UNCiBnaXZlcyB5b3UgbG90cyBvZiBvcHRpb25zLi4uLg0KIG1ha2VzIGl0IHNhZmVyDQoNCiBob3cgY29udGFpbmVycyBmaW5kIGVhY2ggb3RoZXINCiBETlMgYW5kIHRoZSBhZmZlY3Qgb24gY29udGFpbmVycw0KIGNhbid0IHJlbHkgb24gSVAgYWRkcmVzc2VzIGFzIHRoaW5ncyBhcmUgZHluYW1pYw0KIG5hbWluZyBjcnVjaWFsIC0gZG9ja2VyIHVzZXJzIGNvbnRhaW5lciBuYW1lcyB0byB0YWxrIHRvIGVhY2ggb3RoZXINCiBicmlnZSBuZXR3b3JrcyBoYXZlIGF1dG8gZG5zDQogSSBjYW4gZG8NCmRvY2tlciBjb250YWluZXIgcnVuIC1kIC0tbmFtZSBuZ2lueCAtLW5ldHdvcmsgbXlfYXBwX25ldCBuZ2lueA0KZG9ja2VyIGNvbnRhaW5lciBydW4gLWQgLS1uYW1lIG5naW54VHdvIC0tbmV0d29yayBteV9hcHBfbmV0IG5naW54DQogdGhlbkkgY2FuIHBvcCBpbnRvIG9uZSBhbmQgcGluZyB0aGUgb3RoZXINCmRvY2tlciBjb250YWluZXIgZXhlYyBuZ2lueCBwaW5nIG5naW54VHdvDQogdGhpcyBzb2x2ZXMgdGhlIGR5bmFtaWMgcHJvYmxlbQ0KIHRvIGRvIHRoaXMgeW91IG5lZWQgdG8gY3JlYXRlIG5ldyBuZXR3b3JrLCBicmlkZ2UgZG9lc24ndCBoYXZlIHRoaXMNCiBBU1NJR05NRU5UDQpPcGVuIDIgbGludXggYm94ZXMsIHRhbGsgdG8gZWFjaCBvdGhlcg0KIG5vdGUgdGhhdCB5b3UgbmVlZCB0byBydW4gZGViaWFuIC8gdWJ1bnR1IHdpdGggLWl0IGJhc2gNCmRvY2tlciBjb250YWluZXIgcnVuIC1pdCAtLW5hbWUgZGViaWFuIC0tbmV0d29yayBteV9hcHBfbmV0IGRlYmlhbiBiYXNoDQogcm91bmQgcm9iaW4gdGVzdGluZw0KIGNyZWF0ZSBuZXR3b3JrDQpkb2NrZXIgbmV0d29yayBjcmVhdGUgZHVkZQ0KIGNyZWF0ZSBjb250YWluZXINCmRvY2tlciBjb250YWluZXIgcnVuIC1kIC0tbmV0IGR1ZGUgLS1uZXQtYWxpYXMgc2VhcmNoIGVsYXN0aWNzZWFyY2g6Mg0KZG9ja2VyIGNvbnRhaW5lciBydW4gLWQgLS1uZXQgZHVkZSAtLW5ldC1hbGlhcyBzZWFyY2ggZWxhc3RpY3NlYXJjaDoyDQpkb2NrZXIgY29udGFpbmVyIHJ1biAtLXJtIGFscGluZSBuc2xvb2t1cCBzZWFyY2gNCmRvY2tlciBjb250YWluZXIgcnVuIC0tcm0gLS1uZXQgZHVkZSBjZW50b3MgY3VybCAtcyBzZWFyY2g6OTIwMA0KDQoNCg0KDQoNCiMjIElNQUdFUyBBTkQgRE9DS0VSSFVCDQoNCiMgYXBwbGljYXRpb24gYmluYXJpZXMgYW5kIGRlcGVuZGVuY2llcyBhbmQgbWV0YWRhdGEgb2YgaG93IHRvIHJ1biB0aGluZ3MNCiMgTm8gT1MgaW4gaW1hZ2UuLi5qdXN0IGJpbmFyaWVzIGFwcGxpY2F0aW9uIG5lZWRzDQojIE5vdCB2aXJ0dWFsIG1hY2hpbmUuLi4uDQoNCiMgY2FuIGdldCBpbWFnZXMgZnJvbSBkb2NrZXJodWINCiMgbG9nIGluIGFuZCBzZWUgYWxsIHRoZSBpbWFnZXMNCiMgdXNlIGltYWdlcyB0aGF0IGFyZSBwb3B1bGFyIGZpcnN0DQojIHN0YXJ0IHdpdGggb2ZmaWNpYWwuLi53aWxsIGJlIG5vdGVkIHRoaXMgd2F5Li4uLg0KIyBubyBmb3J3YXJkIHNsYXNoIGluIHRoZSBuYW1lDQojIG9mZmljaWFsIGFyZSBjcmVhdGVkIGF0IERvY2tlcg0KIyBnb29kIGRvY3MgaW4gb2ZmaWNpYWwgaW1hZ2VzDQojIG5vdGUgdGhlcmUgYXJlIGRpZmZlcmVudCB2ZXJzaW9ucyBpbiBvZmZpY2lhbC4uLg0KIyBsYXRlc3QgaXMgYSBzcGVjaWFsIHRhZy4uLi5nZXR0aW5nIGxhdGVzdCB2ZXJzaW9uDQpkb2NrZXIgcHVsbCBuZ2lueDoxLjExLjkgIyB0byBzcGVjaWZpeSB2ZXJzaW9uLCB3aGljaCBtaWdoIHRiZSBsYXRlc3QNCiMgbWFrZSBzdXJlIHlvdSBjb250cm9sIHZlcnNpb24NCiMgbm90ZXMgaW4gZG9ja2VyIGh1YiB3aWxsIGhhdmUgZGlmZmVyZW50DQpkb2NrZXIgcHVsbCBuZ2lueDoxLjExLjAtYWxwaW5lICMgc21hbGxlciB2ZXJzaW9uDQojIGxvb2sgZm9yIG51bWJlciBvZiBzdGFycyBhbmQgbnVtYmVyIG9mIHB1bGxzDQojIGltYWdlcyBkZXNpZ25lZCB3aXRoIHVuaW9uIGZpbGUgc3lzdGVtIGlkZWEuLi4uDQpkb2NrZXIgaW1hZ2UgbHMgIyBsaXN0cyBpbWFnZXMNCmRvY2tlciBpbWFnZSBoaXN0b3J5IG5lbzRqICMgc2hvd3MgaW1hZ2VzIHRoYXQgY29tZSB0b2dldGhlciBhbmQgcnVuIGl0DQojIGVhY2ggbGF5ZXIgb2YgaW1hZ2UgZ2V0cyBvd24gdW5pcXVlIHNoYWgsIGFuZCB0aGV5IHNoYXJlIHRoZW0NCiMgeW91IGNhbiBzZWUgRG9ja2VyRmlsZSBzdHlsZSBjb21tYW5kcyBpbiBoZXJlDQojIGltYWdlIHNoYXJlIHdoZXJlIHBvc3NpYmxlLi4uLiBpZGVhIG9mIGNhY2hlIG9mIGltYWdlIGxheWVycy4uLi4NCiMgWW91IHJ1biBjb250YWluZXIgb2ZmIGltYWdlLi4uLmRvY2tlciBjcmVhdGVzIHJlYWQgd3JpdGUgbGF5ZXIgb24gaW1hZ2UuLi4NCiMgYW5kIHRoYXRzIGFsbCB0aGUgY29udGFpbmVyIGlzLi4uDQojIHlvdSBjYW4gZG8gY29weSBvbiB3cml0ZS4uLi5tZWFuaW5nIHlvdSBoYXZlIG11bHRpcGxlIGNvbnRhaW5lcnMuLi4NCiMgdGhhdCBtaWdodCBjaGFuZ2UgY29uZmlnIG9uIGFuIGltYWdlLi4uLmJ1dCB0aGlzIGlzIGtlcHQgaW4gdGhlDQojIGNvbnRhaW5lci4uLi4NCiMgPG1pc3Npbmc+IG1lYW5zIHRoYXQgdGhlc2UgYXJlIG5vdCBpbWFnZXMuLi5qdXN0IGxheWVycywgZG9uJ3QgbmVlZCBpbWFnZSBpZA0KZG9ja2VyIGltYWdlIGluc3BlY3QgbmVvNGogIyBhbGwgbWV0YWRhdGEsIGVudiB2YXJpYWJsZXMsIHBvcnRzIGV0Yy4uLi4NCiMgVGFnZ2luZyBhbmQgdXBsb2FkaW5nIGltYWdlcw0KIyB0YWdnaW5nIG5lZWRzIHNwZWNpZmljIGZvcm1hdHRpbmcNCiMgaW1hZ2VzIGRvbid0IGhhdmUgbmFtZS4uLi4NCmRvY2tlciBpbWFnZSBscyAjIG5vIG5hbWVzLCBidXQgc2hvd3MgdXMgcmVwbyAodXNlcm5hbWUvcmVwbyBvciByZXBvKQ0KIyB0YWcgaXNuJ3QgdmVyc2lvbiBvciBicmFuY2gsIGtpbmQgb2YgaW4gYmV0d2Vlbi4uLnBvaW50ZXINCiMgdGFnIG1pZ2h0IGJlIDEuMTEuMTAgb3IgMS4xMS4xMC1hbHBpbmUuLi4NCiMgeW91IGNhbiBhZGQgdGFnIHRvIGltYWdlIHRoYXQgeW91IGRpZG4ndCBtYWtlLi4uLg0KZG9ja2VyIGltYWdlIHB1c2ggamdhYjMxMDMvY2VudG9zICNjcmVhdGUgbmV3IHJlcG8gZXRjLi4uDQoNCg0KYGBge3J9DQpwbG90KGNhcnMpDQpgYGANCg0KQWRkIGEgbmV3IGNodW5rIGJ5IGNsaWNraW5nIHRoZSAqSW5zZXJ0IENodW5rKiBidXR0b24gb24gdGhlIHRvb2xiYXIgb3IgYnkgcHJlc3NpbmcgKkN0cmwrQWx0K0kqLg0KDQpXaGVuIHlvdSBzYXZlIHRoZSBub3RlYm9vaywgYW4gSFRNTCBmaWxlIGNvbnRhaW5pbmcgdGhlIGNvZGUgYW5kIG91dHB1dCB3aWxsIGJlIHNhdmVkIGFsb25nc2lkZSBpdCAoY2xpY2sgdGhlICpQcmV2aWV3KiBidXR0b24gb3IgcHJlc3MgKkN0cmwrU2hpZnQrSyogdG8gcHJldmlldyB0aGUgSFRNTCBmaWxlKS4NCg==