Using jsPsych and cognition.run

Session 1 - basics


Translations available

Disclaimer: may not be very accurate…


Worksheet overview

Aims

By the end of this worksheet you should be able to:

  • program your own experiments in jsPsych
  • host the experiment online using cognition.run
  • use the participant data for analysis
  • apply the basic skills you have learnt for your own purposes
  • learn some extra skills such as HTML, javascript, CSS and JSON

Pre-requisites

To complete the aims you will need to:

  • follow this worksheet
  • ask questions if you are not sure/be able to google
  • have a working computer and internet connection
  • be patient when things do not work

You do not need to:

  • have any programming knowledge
  • have high computer literacy
  • know anything about jsPsych, cognition.run, html, css or javascript
  • be a linguist

Structure

The worksheet will go through the following sections:

  • Introducing cognition.run

    • creating an account
    • considerations when using it
    • familiarisation with the interface

  • Introducing jsPsych

    • what it is
    • considerations when using it
    • using official documentation

  • Running experiments

    • integrating JsPsych with cognition.run
    • familiarisation with JsPsych programming
    • building a very simple experiment

In the next sessions we will cover:

  • building a proper experiment
  • using stimuli files
  • customising plugins
  • customising appearance
  • working with the data

Introducing cognition.run and jsPsych

cognition.run

What is it?

A free and easy way to host your jsPsych experiments.

Why would I use it?

If you do not have access to your own server, or do not want to pay for alternative online server options, this is a nice and simple solution for researchers.

Do I have to spend lots of time learning how it works?

Not really, once you learn how to use jsPsych the interface is really good for beginners to navigate and use.

The free account will allow you to:

  • run your jsPsych experiments online
  • code and preview your experiment
  • access your data as a csv file
  • collect a maximum of 80 participants per experiment
  • learn the basics and complete this worksheet

It will not be suitable if you:

  • need to collect a large amount of data from lots of participants
  • collect data that would not be usable in csv format (e.g. audio recordings from participants)
  • have stimuli that are very large in size (>2 MB, e.g. videos)
  • are looking to host your experiments on your own server
  • generally have more complex experimental designs
screenshot from https://www.cognition.run/#features taken on 11th June 2023
screenshot from https://www.cognition.run/#features taken on 11th June 2023

Setting up an account

  1. Go to https://www.cognition.run/ and click on the create an account button.

  2. Register for a free account by filling in the form.

  3. You will then see a dashboard, where you can see your experiments. Click on the + New task button to create a new experiment.

  4. Where it says task name type the name demo (you can choose a different name if you want, but this is the name that we will use). If you click on the Show advanced configuration button to see additional settings, which may be relevant depending on your ethics application (e.g. data storage location, IP address logging). Click the Save button to create the experiment.

  5. Now you will see the experiment’s dashboard. There are several options on this page which we will cover later, but for now we need to click on the Source code button.

  6. On this page you can start writing your code that makes your experiment. Before we start writing our code we will need to know how jsPsych works, which will be covered in the next section. We will return to the cognition.run interface too and see how to interact with it in more detail.

Things might look different

The screenshots above might look different if you are reading this worksheet at a later date. This is because websites change, especially ones that are constantly developing, which also means the cost of using the service might also change.


jsPsych

What is it?
A free and open source library that allows users to create and run experiments online. It relies on javascript, a programming language that most websites use, to implement plugins that can be used to make your experiments.

See:

https://www.jspsych.org/

de Leeuw, J. R. (2015). jsPsych: A JavaScript library for creating behavioral experiments in a web browser. Behavior Research Methods, 47(1), 1-12. doi:10.3758/s13428-014-0458-y.

Why should I use it?
It is an extremely adaptable and easy to use, with a growing community of users, support and contributors, making it more accessible for everybody to conduct research online. It is also free and open source.

Do I have to spend lots of time learning how to use it?
Probably. There is definitely a learning curve, which will take time to get around, but you learn lots of skills along the way. The more you learn, the more you can do. However, if you want to learn the basics you do not need too long to get a working experiment programmed and available to participants.

Should I be worried if I do not know what javascript is?
No. The great thing about jsPsych is that much of the hard work is already done by others, which means you can get by without worrying about the really complex stuff.

Versions of jsPsych

You might have noticed that jsPsych has different versions, e.g. 7.3.1, 6.3.0. This is somewhat confusing at first as you would hope that there would be no major differences in which version you are using, or that you would just choose the latest version. However, there are some important differences between the versions that can affect whether your script will work. These are largely based on whether you are using version 6.x.x or 7.x.x

In this tutorial we will be using version 7.3.1

You can see an outline of the differences at https://www.jspsych.org/7.3/support/migration-v7/

Always try to write somewhere in your script which version of jsPsych you are working with.


Running experiments

In order to run your experiments, you have write code that will tell jsPsych what you want it to do. This requires you to speak the same way as jsPsych, so that your instructions can be interpreted properly.

Imagine you have to guide somebody from point A to point B on a map, you will have to learn how to give basic instructions like turn left, stop, continue, this is what your code will have to do (but in the context of a linguistics experiment, so more like show this stimuli, collect this input, measure the time it takes to press this button).

Using cognition.run with jsPsych

To run your experiment, you will need somewhere to write your code. We will therefore return to cognition.run.

The image below outlines which parts of the interface can be used for different purposes. For now, we will focus on 1 and 3 of the interface (writing code and previewing how it looks), with 3 and 4 being explained in more detail later.

0. Commenting your code

Before you start writing proper code that does things, it is important to remember that it is not just the computer who will interpret you write… you and other human brains need to as well.

To make it easier for humans, you can add comments to your code.

Comments don’t do anything to your actual code, but add information to make it easier to understand and interpret. Imagine talking to somebody in a language that you both know, but there is somebody else in the group who does not speak that language, so you might offer a simplified translation to that person, so that they know what is happening. This is sort of similar to what comments do.

You can write a comment in the code section of the cognition.run interface in the following way:

// This is a comment

These can be as long as you want, but must have // at the start of each new line.

Let’s try writing a useful comment in our script, where we say:

  • the title of the experiment
  • what version of jsPsych we are using
  • when we wrote the code [today]
  • who is the author of the code [your name]
// --------
// Title: My first experiment
// jsPsych version: 7.3.1
// date: [today]
// author: [your name]
//----------

1. Initiating jsPsych

Now we can start writing actual code that will allow us to use jsPsych properly.

In order for your experiment to work, the first line of code in your script should be:

var jsPsych = initJsPsych();

Notice that this is not a comment and has a specific structure, or syntax, which is shown by the colour highlighting of the text. We can deconstruct this to understand each part of the code:

  • var this indicates that we want to create a variable, which we can use in our experiment.
    Notice that it is in a different colour to the rest of the text, this is because it is a keyword that javascript understands to be important, i.e. that you want to create a variable.

  • jsPsych this is the name of our variable, in theory you can name variables anything that you want.

  • = this is an operator it is used to assign a value to a variable, in this case whatever is on the right of the equals sign will be assigned to our jsPsych variable.

  • initJsPsych() this is a function specifically made for jsPsych experiments, it allows us to initiate an experiment, without this function we would not be able to do anything. Think of it as turning on the power button, nothing will happen until you have this function stored as a variable.

  • ; this is a semi-colon, it is an important part of your code as it will allow the script to know that your specific bit of code (or statement) is finished and you can move on to your next one.

What is a variable?

See this page for more information on what var means.

Choosing a name for your variable

When naming your variable you should follow these rules:

never include spaces (use_underscores_instead)
never begin with a number
never use a special javascript name (e.g. ‘var’ is not a good name for your variable)

always make them unique to other variables
always make them memorable
always be aware of upper and lower case (‘myVariable’ will be distinct from ‘myvariable’)

What goes inside the brackets?

When you see brackets for a function, there are normally some additional parameters that you can set, to see what these are you can always refer to the official jsPsych documentation, for now we will keep our experiment to the default settings, so we do not need to add anything inside the brackets.

2. Experimental trials and plugins

Now we can actually make a trial to show to the participant.

Trials can be almost anything you can think of, all you need to do is convert your idea for an experiment into the suitable trials. This may take a bit more expertise for really complex experiments, but luckily JsPsych has already done a lot of the work and has a toolbox full of typical trials that you would normally need to run an experiment.

To do this we use plugins

Introducing plugins

Plugins are a key feature of JsPsych, they are outlined really nicely by the official JsPsych page on plugins, which is pasted below:

In jsPsych, plugins define the kinds of trials or events that should occur during the experiment. Some plugins define very general events, like displaying a set of instructions pages, displaying an image and recording a keyboard response, or playing a sound file and recording a button response. Other plugins are more specific, like those that display particular kinds of stimuli (e.g., a circular visual search array), or run a specific version of particular kind of task (e.g., the Implicit Association Test). Part of creating an experiment with jsPsych involves figuring out which plugins are needed to create the tasks you want your participants to perform.

Plugins provide a structure for a particular trial or task, but often allow for significant customization and flexibility. For example, the image-keyboard-response plugin defines a simple structure for showing an image and collecting a keyboard response. You can specify the what the stimulus is, what keys the participant is allowed to press, how long the stimulus should be on the screen, how long the participant has to respond, and so on. Many of these options have reasonable default values; even though the image plugin has many different parameters, you only need to specify the image stimulus in order to use it. Each plugin has its own documentation page, which describes what the plugin does, what options are available, and what kind of data it collects.

If we look at the JsPsych official documentation, we can see there are lots of plugins that we can choose to use.

The documentation page is really nicely written and has examples of each of the plugins, with sensible names so it should be relatively easy to choose the plugin that suits your needs.

We will work through a demo of one of these plugins to demonstrate how to implement them.

This Google Sheet can also be used as a quick reference point for the different plugins with descriptions of what they can be used for (note that all of the information in this file is taken from the official JsPsych plugin pages, it was simply compiled in this format by me):

https://docs.google.com/spreadsheets/d/11AqZu6KnCaneZmv4iFQ8JuakIShvvzJWqHU5gkcD3fw/edit?usp=sharing

Plugin parameters

In order to use a plugin we need to set up a trial as a var so that we can use it in our experiment. This is done by using code that would look similar to this:

var my_trial = {
  type: 'plugin_name',
  parameter1: something,
  parameter2: something_else
};

The structure of the code inside the {} is important, there will normally be a list of parameters which are predefined by the specific JsPsych plugin you are using, e.g. type, followed by a :, the information to the right of this is the information relevant to this specific parameter.

You will need to know what parameters to use and the corresponding information to provide in order to make the plugin work as intended.

Some are always likely to be used, e.g. the type parameter defines which plugin you want to use for your trial.

The best place to find this information is the JsPsych page for each of the plugins under the parameters section.

Below is the page for the html-button-response page:

Plugin example

The first plugin we will use is essentially a button response. The participant will see a stimulus on the screen and a button underneath it. Once the button is pressed, the trial will end.

To do this we use the html-button-response - see the documentation page here.

Below is the code used to create a single trial.

var button_trial = {
  type: jsPsychHtmlButtonResponse,
  stimulus: 'Hello world',
  choices: ['click me!'],
};

You can see it has the following structure:

var: we use var to declare that we are making a variable

button_trial: this is where we define the name for our variable, in this case we are calling it button_trial, remember to choose a sensible name for your trial

=: this denotes that everything on the right of the equals will be assigned to our button_trial variables

{}: this means we can assign multiple values to our variable, instead of just one. Everything inside the {} will be stored as information relevant to our variable.

type::this is the parameter used to identify which plugin you want to use for your trial, for any JsPsych trial that uses a specific plugin, you will have to use type: followed by the specific plugin name

jsPsychHtmlButtonResponse: this is the specific plugin name for a button response

stimulus:: this is the standard way to identify what your stimulus will be

'Hello world': this is our stimulus. It is a string/text stimuli, as denoted by the use of ' around the text. Strings can contain letters, numbers or special characters e.g. ‘Hello world1!’ will still work as a valid stimulus

choices:: this is the parameter used to identify what your button will say

['click me!']: this is what the button says in our trial. We again use a text/string for the button label. Note we also use [] around the string, this is because a button response trial can take multiple strings, so if you add another string inside the [], e.g. ['click me!', 'do not click me!'], will give you two buttons

You can see from the parameters documentation that there are a lot more parameters that you can use for this plugin, whereas the example code we have written above is quite simple, but it still works. We will cover more complex trials later.

Important differences between cognition.run and normal jsPsych experiments

Your experiment in cognition.run will look a but different from other JsPsysch experiments, e.g. the ones you might see that were hosted on private servers. This is because cognition.run does some things automatically, so you do not need to worry about adding specific code.

One of the main differences is that all of the standard JsPsych plugins are automatically loaded for you. We will discuss this later in the worksheet, but if you have used JsPsych before, or are copying code from a non cognition.run experiment you might need to be aware of this.

Essentially, the code you have in your cognition.run experiment will all be javascript, whereas a typical JsPsych experiment will be a html file, with the javascript embedded within that file.

3. Timelines

For our experiment to work we need to add a timeline. We use these to store each of the trials of our experiment, e.g. informed consent, instructions, practice trials, experimental trials, ending message.

The timeline is simply the place where we store all of the trials, ensuring that they will be presented to the participant in a specific order.

We would normally create a timeline before making the trials, but it seemed logical to introduce plugins first for this worksheet.

This can be done by creating a var called timeline and then the thing that this variable will store is an empty array, which is denoted by the [].

Once we have set up the timeline, then we can make the trials and push them into the timeline array. So our timeline would eventually look like [informed consent, instructions, practice trials, experimental trials, ending message].

var timeline = [];

Now we have a timeline and a trial, we can push the trial into our timeline, so that when we run the experiment, the trial is part of the timeline.

We do this in the following way:

timeline.push(button_trial);

By doing this, our timeline will now look like this: [button_trial], which would be the same as writing var timeline = [button_trial]. Normally we would be having many trials in our timeline, so using push is a quick and easy way to test and debug specific trials in your timeline.

Does it have to be called timeline?

No, you can call your timeline anything you want when declaring it as a var.

It is important that you know what your timeline will be called though, as this will change the code when you push your trial

e.g. if you define a timeline called my_timeline, then to push your trial to this array, you will need to use my_timeline.push(button_trial)

4. Running your experiment

You might have noticed in the preview box in cognition.run that you have not seen your experiment yet. This is because there is one last important piece of code that we need in order to let JsPsych know that we are ready to run our timeline.

This is:

jsPsych.run(timeline);

You can see here that we use the jsPsych as our first function, then the .run part lets JsPsych know that we want to start the experiment. Inside the () we write the name of our timeline.

5. The full example code

You should now have in your code section your complete code to run a very basic experiment.

This should include:

  • Initiating JsPsych
  • Using a plugin to make a trial
  • Creating a timeline and pushing your trial to the timeline
  • Running your timeline

Here is an example of code that should work, if you want to copy and paste it:

This is how it should look in cognition.run:

LS0tCnRpdGxlOiAiVXNpbmcganNQc3ljaCBhbmQgY29nbml0aW9uLnJ1biIKc3VidGl0bGU6ICJTZXNzaW9uIDEgLSBiYXNpY3MiCmF1dGhvcjogIkphbWVzIEJyYW5kIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIHBhbmRvY19hcmdzOiAiLS1oaWdobGlnaHQtc3R5bGU9bXkudGhlbWUiCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgY29sbGFwc2VkOiBmYWxzZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBsaWdodGJveDogVFJVRQogICAgZ2FsbGVyeTogVFJVRQogICAgY3NzOiAiY3NzL3N0eWxlLmNzcyIKICAgIAotLS0KCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc2xpY2tSKQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeSh4YXJpbmdhbkV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShmb250YXdlc29tZSkKbGlicmFyeShic3BsdXMpCgpgYGAKCmBgYHtyIHNldHVwLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBldmFsID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEpCgprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgbWVzc2FnZSA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgICBwYXN0ZSgnPGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJjb2xsYXBzaWJsZTEiPjxzdHJvbmc+JywKICAgICBmYShuYW1lID0gImNpcmNsZS1pbmZvIiksCiAgICAgJyBtb3JlIGluZm88L3N0cm9uZz48L2J1dHRvbj4nLCAnPGRpdiBjbGFzcz0iY29udGVudDEiPjxwPicsCiAgICAgZ3N1YignIyMnLCAnXG4nLCB4KSwKICAgICAnPC9wPjwvZGl2PicsCiAgICAgc2VwID0gJ1xuJykKICAgfSkKCmNvZGVibG9jayA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgICBjYXQocGFzdGUoJzxkaXYgY2xhc3M9ImNvZGVibG9jayI+JywKICAgICBwYXN0ZTAoeCksCiAgICAgJzwvZGl2PicsCiAgICAgc2VwID0gJ1xuJykpCiAgIH0KCmBgYAoKLS0tCgojIyBgciBmYSgibGFuZ3VhZ2UiKWAgVHJhbnNsYXRpb25zIGF2YWlsYWJsZQoKRGlzY2xhaW1lcjogbWF5IG5vdCBiZSB2ZXJ5IGFjY3VyYXRlLi4uCgo8ZGl2IGlkPSJnb29nbGVfdHJhbnNsYXRlX2VsZW1lbnQiPjwvZGl2PgoKLS0tCgojIFdvcmtzaGVldCBvdmVydmlldwoKIyMgYHIgZmEoImNyb3NzaGFpcnMiKWAgQWltcwoKQnkgdGhlIGVuZCBvZiB0aGlzIHdvcmtzaGVldCB5b3Ugc2hvdWxkIGJlIGFibGUgdG86CgotICoqcHJvZ3JhbSoqIHlvdXIgb3duIGV4cGVyaW1lbnRzIGluIGpzUHN5Y2gKLSAqKmhvc3QqKiB0aGUgZXhwZXJpbWVudCBvbmxpbmUgdXNpbmcgY29nbml0aW9uLnJ1bgotICoqdXNlKiogdGhlIHBhcnRpY2lwYW50IGRhdGEgZm9yIGFuYWx5c2lzCi0gKiphcHBseSoqIHRoZSBiYXNpYyBza2lsbHMgeW91IGhhdmUgbGVhcm50IGZvciB5b3VyIG93biBwdXJwb3NlcwotICoqbGVhcm4qKiBzb21lIGV4dHJhIHNraWxscyBzdWNoIGFzIEhUTUwsIGphdmFzY3JpcHQsIENTUyBhbmQgSlNPTgoKIyMgYHIgZmEoInVzZXItZ3JhZHVhdGUiKWAgUHJlLXJlcXVpc2l0ZXMKClRvIGNvbXBsZXRlIHRoZSBhaW1zIHlvdSB3aWxsIG5lZWQgdG86CgotICoqZm9sbG93KiogdGhpcyB3b3Jrc2hlZXQKLSAqKmFzayoqIHF1ZXN0aW9ucyBpZiB5b3UgYXJlIG5vdCBzdXJlL2JlIGFibGUgdG8gZ29vZ2xlCi0gKipoYXZlKiogYSB3b3JraW5nIGNvbXB1dGVyIGFuZCBpbnRlcm5ldCBjb25uZWN0aW9uCi0gKipiZSBwYXRpZW50Kiogd2hlbiB0aGluZ3MgZG8gbm90IHdvcmsKCllvdSBkbyBub3QgbmVlZCB0bzoKCi0gaGF2ZSBhbnkgKipwcm9ncmFtbWluZyBrbm93bGVkZ2UqKgotIGhhdmUgaGlnaCAqKmNvbXB1dGVyIGxpdGVyYWN5KioKLSBrbm93IGFueXRoaW5nIGFib3V0ICoqanNQc3ljaCwgY29nbml0aW9uLnJ1biwgaHRtbCwgY3NzIG9yIGphdmFzY3JpcHQqKgotIGJlIGEgKipsaW5ndWlzdCoqCgojIyBgciBmYSgiZm9sZGVyLXRyZWUiKWAgU3RydWN0dXJlCgpUaGUgd29ya3NoZWV0IHdpbGwgZ28gdGhyb3VnaCB0aGUgZm9sbG93aW5nIHNlY3Rpb25zOgoKLSBJbnRyb2R1Y2luZyBjb2duaXRpb24ucnVuCgogICAgLSBjcmVhdGluZyBhbiBhY2NvdW50CiAgICAtIGNvbnNpZGVyYXRpb25zIHdoZW4gdXNpbmcgaXQKICAgIC0gZmFtaWxpYXJpc2F0aW9uIHdpdGggdGhlIGludGVyZmFjZQo8YnIvPjxici8+Ci0gSW50cm9kdWNpbmcganNQc3ljaAoKICAgIC0gd2hhdCBpdCBpcwogICAgLSBjb25zaWRlcmF0aW9ucyB3aGVuIHVzaW5nIGl0CiAgICAtIHVzaW5nIG9mZmljaWFsIGRvY3VtZW50YXRpb24KPGJyLz48YnIvPgotIFJ1bm5pbmcgZXhwZXJpbWVudHMKCiAgICAtIGludGVncmF0aW5nIEpzUHN5Y2ggd2l0aCBjb2duaXRpb24ucnVuCiAgICAtIGZhbWlsaWFyaXNhdGlvbiB3aXRoIEpzUHN5Y2ggcHJvZ3JhbW1pbmcKICAgIC0gYnVpbGRpbmcgYSB2ZXJ5IHNpbXBsZSBleHBlcmltZW50CgpJbiB0aGUgbmV4dCBzZXNzaW9ucyB3ZSB3aWxsIGNvdmVyOgoKLSBidWlsZGluZyBhIHByb3BlciBleHBlcmltZW50Ci0gdXNpbmcgc3RpbXVsaSBmaWxlcwotIGN1c3RvbWlzaW5nIHBsdWdpbnMKLSBjdXN0b21pc2luZyBhcHBlYXJhbmNlCi0gd29ya2luZyB3aXRoIHRoZSBkYXRhCgotLS0KCiMgSW50cm9kdWNpbmcgY29nbml0aW9uLnJ1biBhbmQganNQc3ljaAoKIyMgY29nbml0aW9uLnJ1bgoKKipXaGF0IGlzIGl0PyoqCgpBIGZyZWUgYW5kIGVhc3kgd2F5IHRvIGhvc3QgeW91ciBqc1BzeWNoIGV4cGVyaW1lbnRzLgoKKipXaHkgd291bGQgSSB1c2UgaXQ/KioKCklmIHlvdSBkbyBub3QgaGF2ZSBhY2Nlc3MgdG8geW91ciBvd24gc2VydmVyLCBvciBkbyBub3Qgd2FudCB0byBwYXkgZm9yIGFsdGVybmF0aXZlIG9ubGluZSBzZXJ2ZXIgb3B0aW9ucywgdGhpcyBpcyBhIG5pY2UgYW5kIHNpbXBsZSBzb2x1dGlvbiBmb3IgcmVzZWFyY2hlcnMuCgoqKkRvIEkgaGF2ZSB0byBzcGVuZCBsb3RzIG9mIHRpbWUgbGVhcm5pbmcgaG93IGl0IHdvcmtzPyoqCgpOb3QgcmVhbGx5LCBvbmNlIHlvdSBsZWFybiBob3cgdG8gdXNlIGpzUHN5Y2ggdGhlIGludGVyZmFjZSBpcyByZWFsbHkgZ29vZCBmb3IgYmVnaW5uZXJzIHRvIG5hdmlnYXRlIGFuZCB1c2UuCgoqKlRoZSBmcmVlIGFjY291bnQgd2lsbCBhbGxvdyB5b3UgdG86KioKCi0gcnVuIHlvdXIganNQc3ljaCBleHBlcmltZW50cyBvbmxpbmUKLSBjb2RlIGFuZCBwcmV2aWV3IHlvdXIgZXhwZXJpbWVudAotIGFjY2VzcyB5b3VyIGRhdGEgYXMgYSBjc3YgZmlsZQotIGNvbGxlY3QgYSBtYXhpbXVtIG9mIDgwIHBhcnRpY2lwYW50cyBwZXIgZXhwZXJpbWVudAotIGxlYXJuIHRoZSBiYXNpY3MgYW5kIGNvbXBsZXRlIHRoaXMgd29ya3NoZWV0CgoqKkl0IHdpbGwgbm90IGJlIHN1aXRhYmxlIGlmIHlvdToqKgoKLSBuZWVkIHRvIGNvbGxlY3QgYSBsYXJnZSBhbW91bnQgb2YgZGF0YSBmcm9tIGxvdHMgb2YgcGFydGljaXBhbnRzCi0gY29sbGVjdCBkYXRhIHRoYXQgd291bGQgbm90IGJlIHVzYWJsZSBpbiBjc3YgZm9ybWF0IChlLmcuIGF1ZGlvIHJlY29yZGluZ3MgZnJvbSBwYXJ0aWNpcGFudHMpCi0gaGF2ZSBzdGltdWxpIHRoYXQgYXJlIHZlcnkgbGFyZ2UgaW4gc2l6ZSAoPjIgTUIsIGUuZy4gdmlkZW9zKQotIGFyZSBsb29raW5nIHRvIGhvc3QgeW91ciBleHBlcmltZW50cyBvbiB5b3VyIG93biBzZXJ2ZXIKLSBnZW5lcmFsbHkgaGF2ZSBtb3JlIGNvbXBsZXggZXhwZXJpbWVudGFsIGRlc2lnbnMKCiFbKnNjcmVlbnNob3QgZnJvbSBodHRwczovL3d3dy5jb2duaXRpb24ucnVuLyNmZWF0dXJlcyB0YWtlbiBvbiAxMXRoIEp1bmUgMjAyMypdKGltYWdlcy9jb2duaXRpb25fcHJpY2luZy5wbmcpCgojIyMgU2V0dGluZyB1cCBhbiBhY2NvdW50CgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUV9CnNsaWNrUihvYmo9YygiaW1hZ2VzL2NvZ25pdGlvbjEucG5nIiwiaW1hZ2VzL2NvZ25pdGlvbjIucG5nIiwgImltYWdlcy9jb2duaXRpb24zLnBuZyIsImltYWdlcy9jb2duaXRpb240LnBuZyIsICJpbWFnZXMvY29nbml0aW9uNS5wbmciLCAiaW1hZ2VzL2NvZ25pdGlvbjYucG5nIiksIHdpZHRoID0gIjk1JSIsIGhlaWdodCA9IDMwMCkgICsgCiAgc2V0dGluZ3MoZG90cyA9IFRSVUUsIGZvY3VzT25TZWxlY3QgPSBUUlVFLCBjdXN0b21QYWdpbmcgPSBodG1sd2lkZ2V0czo6SlMoImZ1bmN0aW9uKHNsaWNrLGluZGV4KSB7CiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gJzxhIHN0eWxlPVwiZm9udC1mYW1pbHk6YXJpYWw7Y29sb3I6IzlGMUY0MjtcIj4nKyhpbmRleCsxKSsnPC9hPic7CiAgICAgICAgICAgICAgICAgICAgICAgfSIpKQoKYGBgCgoKMS4gR28gdG8gW2h0dHBzOi8vd3d3LmNvZ25pdGlvbi5ydW4vXShodHRwczovL3d3dy5jb2duaXRpb24ucnVuLykgYW5kIGNsaWNrIG9uIHRoZSBgY3JlYXRlIGFuIGFjY291bnRgIGJ1dHRvbi48YnIvPjxici8+CgoyLiBSZWdpc3RlciBmb3IgYSBmcmVlIGFjY291bnQgYnkgZmlsbGluZyBpbiB0aGUgZm9ybS48YnIvPjxici8+CgozLiBZb3Ugd2lsbCB0aGVuIHNlZSBhIGRhc2hib2FyZCwgd2hlcmUgeW91IGNhbiBzZWUgeW91ciBleHBlcmltZW50cy4gQ2xpY2sgb24gdGhlIGArIE5ldyB0YXNrYCBidXR0b24gdG8gY3JlYXRlIGEgbmV3IGV4cGVyaW1lbnQuPGJyLz48YnIvPgoKNC4gV2hlcmUgaXQgc2F5cyBgdGFzayBuYW1lYCB0eXBlIHRoZSBuYW1lICoqZGVtbyoqICh5b3UgY2FuIGNob29zZSBhIGRpZmZlcmVudCBuYW1lIGlmIHlvdSB3YW50LCBidXQgdGhpcyBpcyB0aGUgbmFtZSB0aGF0IHdlIHdpbGwgdXNlKS4gSWYgeW91IGNsaWNrIG9uIHRoZSBgU2hvdyBhZHZhbmNlZCBjb25maWd1cmF0aW9uYCBidXR0b24gdG8gc2VlIGFkZGl0aW9uYWwgc2V0dGluZ3MsIHdoaWNoIG1heSBiZSByZWxldmFudCBkZXBlbmRpbmcgb24geW91ciBldGhpY3MgYXBwbGljYXRpb24gKGUuZy4gZGF0YSBzdG9yYWdlIGxvY2F0aW9uLCBJUCBhZGRyZXNzIGxvZ2dpbmcpLiBDbGljayB0aGUgYFNhdmVgIGJ1dHRvbiB0byBjcmVhdGUgdGhlIGV4cGVyaW1lbnQuPGJyLz48YnIvPgoKNS4gTm93IHlvdSB3aWxsIHNlZSB0aGUgZXhwZXJpbWVudCdzIGRhc2hib2FyZC4gVGhlcmUgYXJlIHNldmVyYWwgb3B0aW9ucyBvbiB0aGlzIHBhZ2Ugd2hpY2ggd2Ugd2lsbCBjb3ZlciBsYXRlciwgYnV0IGZvciBub3cgd2UgbmVlZCB0byBjbGljayBvbiB0aGUgYFNvdXJjZSBjb2RlYCBidXR0b24uPGJyLz48YnIvPgoKNi4gT24gdGhpcyBwYWdlIHlvdSBjYW4gc3RhcnQgd3JpdGluZyB5b3VyIGNvZGUgdGhhdCBtYWtlcyB5b3VyIGV4cGVyaW1lbnQuIEJlZm9yZSB3ZSBzdGFydCB3cml0aW5nIG91ciBjb2RlIHdlIHdpbGwgbmVlZCB0byBrbm93IGhvdyBqc1BzeWNoIHdvcmtzLCB3aGljaCB3aWxsIGJlIGNvdmVyZWQgaW4gdGhlIG5leHQgc2VjdGlvbi4gV2Ugd2lsbCByZXR1cm4gdG8gdGhlIGNvZ25pdGlvbi5ydW4gaW50ZXJmYWNlIHRvbyBhbmQgc2VlIGhvdyB0byBpbnRlcmFjdCB3aXRoIGl0IGluIG1vcmUgZGV0YWlsLjxici8+PGJyLz4KCmBgYHtyIGVjaG89RkFMU0UsIGV2YWw9VFJVRSwgcmVzdWx0cz0nbWFya3VwJ30KbWVzc2FnZSh4ID0gcGFzdGUoIjxiPlRoaW5ncyBtaWdodCBsb29rIGRpZmZlcmVudDwvYj4iLAogICAgICAgICAgICAgICJUaGUgc2NyZWVuc2hvdHMgYWJvdmUgbWlnaHQgbG9vayBkaWZmZXJlbnQgaWYgeW91IGFyZSByZWFkaW5nIHRoaXMgd29ya3NoZWV0IGF0IGEgbGF0ZXIgZGF0ZS4gVGhpcyBpcyBiZWNhdXNlIHdlYnNpdGVzIGNoYW5nZSwgZXNwZWNpYWxseSBvbmVzIHRoYXQgYXJlIGNvbnN0YW50bHkgZGV2ZWxvcGluZywgd2hpY2ggYWxzbyBtZWFucyB0aGUgY29zdCBvZiB1c2luZyB0aGUgc2VydmljZSBtaWdodCBhbHNvIGNoYW5nZS4iLAogICAgICAgICAgICAgIHNlcCA9ICI8YnIvPjxici8+IikpCmBgYAoKLS0tCgojIyBqc1BzeWNoCgoqKldoYXQgaXMgaXQ/Kio8YnIvPgpBIGZyZWUgYW5kIG9wZW4gc291cmNlIGxpYnJhcnkgdGhhdCBhbGxvd3MgdXNlcnMgdG8gY3JlYXRlIGFuZCBydW4gZXhwZXJpbWVudHMgb25saW5lLiBJdCByZWxpZXMgb24gKmphdmFzY3JpcHQqLCBhIHByb2dyYW1taW5nIGxhbmd1YWdlIHRoYXQgbW9zdCB3ZWJzaXRlcyB1c2UsIHRvIGltcGxlbWVudCAqcGx1Z2lucyogdGhhdCBjYW4gYmUgdXNlZCB0byBtYWtlIHlvdXIgZXhwZXJpbWVudHMuCgpTZWU6CgpbaHR0cHM6Ly93d3cuanNwc3ljaC5vcmcvXShodHRwczovL3d3dy5qc3BzeWNoLm9yZy8pCgpkZSBMZWV1dywgSi4gUi4gKDIwMTUpLiBqc1BzeWNoOiBBIEphdmFTY3JpcHQgbGlicmFyeSBmb3IgY3JlYXRpbmcgYmVoYXZpb3JhbCBleHBlcmltZW50cyBpbiBhIHdlYiBicm93c2VyLiAqQmVoYXZpb3IgUmVzZWFyY2ggTWV0aG9kcyosIDQ3KDEpLCAxLTEyLiBbZG9pOjEwLjM3NTgvczEzNDI4LTAxNC0wNDU4LXldKGh0dHBzOi8vZG9pLm9yZy8xMC4zNzU4L3MxMzQyOC0wMTQtMDQ1OC15KS4KCioqV2h5IHNob3VsZCBJIHVzZSBpdD8qKjxici8+Ckl0IGlzIGFuIGV4dHJlbWVseSBhZGFwdGFibGUgYW5kIGVhc3kgdG8gdXNlLCB3aXRoIGEgZ3Jvd2luZyBjb21tdW5pdHkgb2YgdXNlcnMsIHN1cHBvcnQgYW5kIGNvbnRyaWJ1dG9ycywgbWFraW5nIGl0IG1vcmUgYWNjZXNzaWJsZSBmb3IgZXZlcnlib2R5IHRvIGNvbmR1Y3QgcmVzZWFyY2ggb25saW5lLiBJdCBpcyBhbHNvIGZyZWUgYW5kIG9wZW4gc291cmNlLgoKKipEbyBJIGhhdmUgdG8gc3BlbmQgbG90cyBvZiB0aW1lIGxlYXJuaW5nIGhvdyB0byB1c2UgaXQ/Kio8YnIvPgpQcm9iYWJseS4gVGhlcmUgaXMgZGVmaW5pdGVseSBhIGxlYXJuaW5nIGN1cnZlLCB3aGljaCB3aWxsIHRha2UgdGltZSB0byBnZXQgYXJvdW5kLCBidXQgeW91IGxlYXJuIGxvdHMgb2Ygc2tpbGxzIGFsb25nIHRoZSB3YXkuIFRoZSBtb3JlIHlvdSBsZWFybiwgdGhlIG1vcmUgeW91IGNhbiBkby4gSG93ZXZlciwgaWYgeW91IHdhbnQgdG8gbGVhcm4gdGhlIGJhc2ljcyB5b3UgZG8gbm90IG5lZWQgdG9vIGxvbmcgdG8gZ2V0IGEgd29ya2luZyBleHBlcmltZW50IHByb2dyYW1tZWQgYW5kIGF2YWlsYWJsZSB0byBwYXJ0aWNpcGFudHMuCgoqKlNob3VsZCBJIGJlIHdvcnJpZWQgaWYgSSBkbyBub3Qga25vdyB3aGF0IGphdmFzY3JpcHQgaXM/Kio8YnIvPgpOby4gVGhlIGdyZWF0IHRoaW5nIGFib3V0IGpzUHN5Y2ggaXMgdGhhdCBtdWNoIG9mIHRoZSBoYXJkIHdvcmsgaXMgYWxyZWFkeSBkb25lIGJ5IG90aGVycywgd2hpY2ggbWVhbnMgeW91IGNhbiBnZXQgYnkgd2l0aG91dCB3b3JyeWluZyBhYm91dCB0aGUgcmVhbGx5IGNvbXBsZXggc3R1ZmYuCgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUV9Cm1lc3NhZ2UoeCA9IHBhc3RlKCI8Yj5WZXJzaW9ucyBvZiBqc1BzeWNoPC9iPiIsCiAgICAgICAgICAgICAgIllvdSBtaWdodCBoYXZlIG5vdGljZWQgdGhhdCBqc1BzeWNoIGhhcyBkaWZmZXJlbnQgdmVyc2lvbnMsIGUuZy4gNy4zLjEsIDYuMy4wLiBUaGlzIGlzIHNvbWV3aGF0IGNvbmZ1c2luZyBhdCBmaXJzdCBhcyB5b3Ugd291bGQgaG9wZSB0aGF0IHRoZXJlIHdvdWxkIGJlIG5vIG1ham9yIGRpZmZlcmVuY2VzIGluIHdoaWNoIHZlcnNpb24geW91IGFyZSB1c2luZywgb3IgdGhhdCB5b3Ugd291bGQganVzdCBjaG9vc2UgdGhlIGxhdGVzdCB2ZXJzaW9uLiBIb3dldmVyLCB0aGVyZSBhcmUgc29tZSBpbXBvcnRhbnQgZGlmZmVyZW5jZXMgYmV0d2VlbiB0aGUgdmVyc2lvbnMgdGhhdCBjYW4gYWZmZWN0IHdoZXRoZXIgeW91ciBzY3JpcHQgd2lsbCB3b3JrLiBUaGVzZSBhcmUgbGFyZ2VseSBiYXNlZCBvbiB3aGV0aGVyIHlvdSBhcmUgdXNpbmcgdmVyc2lvbiA2LngueCBvciA3LngueCIsCiAgICAgICAgICAgICAgIkluIHRoaXMgdHV0b3JpYWwgd2Ugd2lsbCBiZSB1c2luZyA8Yj52ZXJzaW9uIDcuMy4xPC9iPiIsCiAgICAgICAgICAgICAgIllvdSBjYW4gc2VlIGFuIG91dGxpbmUgb2YgdGhlIGRpZmZlcmVuY2VzIGF0IDxhPmh0dHBzOi8vd3d3LmpzcHN5Y2gub3JnLzcuMy9zdXBwb3J0L21pZ3JhdGlvbi12Ny88L2E+IiwKICAgICAgICAgICAgICAiQWx3YXlzIHRyeSB0byB3cml0ZSBzb21ld2hlcmUgaW4geW91ciBzY3JpcHQgd2hpY2ggdmVyc2lvbiBvZiBqc1BzeWNoIHlvdSBhcmUgd29ya2luZyB3aXRoLiIsCiAgICAgICAgICAgICAgc2VwID0gIjxici8+PGJyLz4iKSkKCmBgYAoKLS0tCgojIFJ1bm5pbmcgZXhwZXJpbWVudHMKCkluIG9yZGVyIHRvIHJ1biB5b3VyIGV4cGVyaW1lbnRzLCB5b3UgaGF2ZSB3cml0ZSBjb2RlIHRoYXQgd2lsbCB0ZWxsIGpzUHN5Y2ggd2hhdCB5b3Ugd2FudCBpdCB0byBkby4gVGhpcyByZXF1aXJlcyB5b3UgdG8gc3BlYWsgdGhlIHNhbWUgd2F5IGFzIGpzUHN5Y2gsIHNvIHRoYXQgeW91ciBpbnN0cnVjdGlvbnMgY2FuIGJlIGludGVycHJldGVkIHByb3Blcmx5LgoKSW1hZ2luZSB5b3UgaGF2ZSB0byBndWlkZSBzb21lYm9keSBmcm9tIHBvaW50IEEgdG8gcG9pbnQgQiBvbiBhIG1hcCwgeW91IHdpbGwgaGF2ZSB0byBsZWFybiBob3cgdG8gZ2l2ZSBiYXNpYyBpbnN0cnVjdGlvbnMgbGlrZSAqdHVybiBsZWZ0KiwgKnN0b3AqLCAqY29udGludWUqLCB0aGlzIGlzIHdoYXQgeW91ciBjb2RlIHdpbGwgaGF2ZSB0byBkbyAoYnV0IGluIHRoZSBjb250ZXh0IG9mIGEgbGluZ3Vpc3RpY3MgZXhwZXJpbWVudCwgc28gbW9yZSBsaWtlICpzaG93IHRoaXMgc3RpbXVsaSosICpjb2xsZWN0IHRoaXMgaW5wdXQqLCAqbWVhc3VyZSB0aGUgdGltZSBpdCB0YWtlcyB0byBwcmVzcyB0aGlzIGJ1dHRvbiopLgoKIyMgVXNpbmcgY29nbml0aW9uLnJ1biB3aXRoIGpzUHN5Y2gKClRvIHJ1biB5b3VyIGV4cGVyaW1lbnQsIHlvdSB3aWxsIG5lZWQgc29tZXdoZXJlIHRvIHdyaXRlIHlvdXIgY29kZS4gV2Ugd2lsbCB0aGVyZWZvcmUgcmV0dXJuIHRvIGBjb2duaXRpb24ucnVuYC4KClRoZSBpbWFnZSBiZWxvdyBvdXRsaW5lcyB3aGljaCBwYXJ0cyBvZiB0aGUgaW50ZXJmYWNlIGNhbiBiZSB1c2VkIGZvciBkaWZmZXJlbnQgcHVycG9zZXMuIEZvciBub3csIHdlIHdpbGwgKipmb2N1cyBvbiAxIGFuZCAzKiogb2YgdGhlIGludGVyZmFjZSAod3JpdGluZyBjb2RlIGFuZCBwcmV2aWV3aW5nIGhvdyBpdCBsb29rcyksIHdpdGggMyBhbmQgNCBiZWluZyBleHBsYWluZWQgaW4gbW9yZSBkZXRhaWwgbGF0ZXIuCgohW10oaW1hZ2VzL2NvZ25pdGlvbjcucG5nKQoKIyMgMC4gQ29tbWVudGluZyB5b3VyIGNvZGUKCkJlZm9yZSB5b3Ugc3RhcnQgd3JpdGluZyBwcm9wZXIgY29kZSB0aGF0IGRvZXMgdGhpbmdzLCBpdCBpcyBpbXBvcnRhbnQgdG8gcmVtZW1iZXIgdGhhdCBpdCBpcyBub3QganVzdCB0aGUgY29tcHV0ZXIgd2hvIHdpbGwgaW50ZXJwcmV0IHlvdSB3cml0ZS4uLiB5b3UgYW5kIG90aGVyIGh1bWFuIGJyYWlucyBuZWVkIHRvIGFzIHdlbGwuCgpUbyBtYWtlIGl0IGVhc2llciBmb3IgaHVtYW5zLCB5b3UgY2FuIGFkZCAqKmNvbW1lbnRzKiogdG8geW91ciBjb2RlLgoKQ29tbWVudHMgZG9uJ3QgZG8gYW55dGhpbmcgdG8geW91ciBhY3R1YWwgY29kZSwgYnV0IGFkZCBpbmZvcm1hdGlvbiB0byBtYWtlIGl0IGVhc2llciB0byB1bmRlcnN0YW5kIGFuZCBpbnRlcnByZXQuIEltYWdpbmUgdGFsa2luZyB0byBzb21lYm9keSBpbiBhIGxhbmd1YWdlIHRoYXQgeW91IGJvdGgga25vdywgYnV0IHRoZXJlIGlzIHNvbWVib2R5IGVsc2UgaW4gdGhlIGdyb3VwIHdobyBkb2VzIG5vdCBzcGVhayB0aGF0IGxhbmd1YWdlLCBzbyB5b3UgbWlnaHQgb2ZmZXIgYSBzaW1wbGlmaWVkIHRyYW5zbGF0aW9uIHRvIHRoYXQgcGVyc29uLCBzbyB0aGF0IHRoZXkga25vdyB3aGF0IGlzIGhhcHBlbmluZy4gVGhpcyBpcyBzb3J0IG9mIHNpbWlsYXIgdG8gd2hhdCBjb21tZW50cyBkby4KCllvdSBjYW4gd3JpdGUgYSBjb21tZW50IGluIHRoZSBjb2RlIHNlY3Rpb24gb2YgdGhlIGNvZ25pdGlvbi5ydW4gaW50ZXJmYWNlIGluIHRoZSBmb2xsb3dpbmcgd2F5OgoKYGBge2pzfQovLyBUaGlzIGlzIGEgY29tbWVudApgYGAKClRoZXNlIGNhbiBiZSBhcyBsb25nIGFzIHlvdSB3YW50LCBidXQgbXVzdCBoYXZlIGAvL2AgYXQgdGhlIHN0YXJ0IG9mIGVhY2ggbmV3IGxpbmUuCgpMZXQncyB0cnkgd3JpdGluZyBhIHVzZWZ1bCBjb21tZW50IGluIG91ciBzY3JpcHQsIHdoZXJlIHdlIHNheToKCi0gdGhlIHRpdGxlIG9mIHRoZSBleHBlcmltZW50Ci0gd2hhdCB2ZXJzaW9uIG9mIGpzUHN5Y2ggd2UgYXJlIHVzaW5nCi0gd2hlbiB3ZSB3cm90ZSB0aGUgY29kZSBbdG9kYXldCi0gd2hvIGlzIHRoZSBhdXRob3Igb2YgdGhlIGNvZGUgW3lvdXIgbmFtZV0KCmBgYHtqc30KLy8gLS0tLS0tLS0KLy8gVGl0bGU6IE15IGZpcnN0IGV4cGVyaW1lbnQKLy8ganNQc3ljaCB2ZXJzaW9uOiA3LjMuMQovLyBkYXRlOiBbdG9kYXldCi8vIGF1dGhvcjogW3lvdXIgbmFtZV0KLy8tLS0tLS0tLS0tCgpgYGAKCgojIyAxLiBJbml0aWF0aW5nIGpzUHN5Y2gKCk5vdyB3ZSBjYW4gc3RhcnQgd3JpdGluZyBhY3R1YWwgY29kZSB0aGF0IHdpbGwgYWxsb3cgdXMgdG8gdXNlIGpzUHN5Y2ggcHJvcGVybHkuCgpJbiBvcmRlciBmb3IgeW91ciBleHBlcmltZW50IHRvIHdvcmssIHRoZSBmaXJzdCBsaW5lIG9mIGNvZGUgaW4geW91ciBzY3JpcHQgc2hvdWxkIGJlOgoKYGBge2pzfQp2YXIganNQc3ljaCA9IGluaXRKc1BzeWNoKCk7CmBgYAoKTm90aWNlIHRoYXQgdGhpcyBpcyBub3QgYSBjb21tZW50IGFuZCBoYXMgYSBzcGVjaWZpYyBzdHJ1Y3R1cmUsIG9yICpzeW50YXgqLCB3aGljaCBpcyBzaG93biBieSB0aGUgY29sb3VyIGhpZ2hsaWdodGluZyBvZiB0aGUgdGV4dC4gV2UgY2FuIGRlY29uc3RydWN0IHRoaXMgdG8gdW5kZXJzdGFuZCBlYWNoIHBhcnQgb2YgdGhlIGNvZGU6PGJyLz48YnIvPgoKLSBgdmFyYCB0aGlzIGluZGljYXRlcyB0aGF0IHdlIHdhbnQgdG8gY3JlYXRlIGEgKnZhcmlhYmxlKiwgd2hpY2ggd2UgY2FuIHVzZSBpbiBvdXIgZXhwZXJpbWVudC48YnIvPk5vdGljZSB0aGF0IGl0IGlzIGluIGEgZGlmZmVyZW50IGNvbG91ciB0byB0aGUgcmVzdCBvZiB0aGUgdGV4dCwgdGhpcyBpcyBiZWNhdXNlIGl0IGlzIGEgKmtleXdvcmQqIHRoYXQgamF2YXNjcmlwdCB1bmRlcnN0YW5kcyB0byBiZSBpbXBvcnRhbnQsIGkuZS4gdGhhdCB5b3Ugd2FudCB0byBjcmVhdGUgYSB2YXJpYWJsZS48YnIvPjxici8+CgotIGBqc1BzeWNoYCB0aGlzIGlzIHRoZSBuYW1lIG9mIG91ciB2YXJpYWJsZSwgaW4gdGhlb3J5IHlvdSBjYW4gbmFtZSB2YXJpYWJsZXMgYW55dGhpbmcgdGhhdCB5b3Ugd2FudC48YnIvPjxici8+CiAgICAKLSBgPWAgdGhpcyBpcyBhbiAqb3BlcmF0b3IqIGl0IGlzIHVzZWQgdG8gYXNzaWduIGEgdmFsdWUgdG8gYSB2YXJpYWJsZSwgaW4gdGhpcyBjYXNlIHdoYXRldmVyIGlzIG9uIHRoZSByaWdodCBvZiB0aGUgZXF1YWxzIHNpZ24gd2lsbCBiZSBhc3NpZ25lZCB0byBvdXIganNQc3ljaCB2YXJpYWJsZS48YnIvPjxici8+CgotIGBpbml0SnNQc3ljaCgpYCB0aGlzIGlzIGEgZnVuY3Rpb24gc3BlY2lmaWNhbGx5IG1hZGUgZm9yIGpzUHN5Y2ggZXhwZXJpbWVudHMsIGl0IGFsbG93cyB1cyB0byBpbml0aWF0ZSBhbiBleHBlcmltZW50LCB3aXRob3V0IHRoaXMgZnVuY3Rpb24gd2Ugd291bGQgbm90IGJlIGFibGUgdG8gZG8gYW55dGhpbmcuIFRoaW5rIG9mIGl0IGFzIHR1cm5pbmcgb24gdGhlIHBvd2VyIGJ1dHRvbiwgbm90aGluZyB3aWxsIGhhcHBlbiB1bnRpbCB5b3UgaGF2ZSB0aGlzIGZ1bmN0aW9uIHN0b3JlZCBhcyBhIHZhcmlhYmxlLjxici8+PGJyLz4KCi0gYDtgIHRoaXMgaXMgYSBzZW1pLWNvbG9uLCBpdCBpcyBhbiBpbXBvcnRhbnQgcGFydCBvZiB5b3VyIGNvZGUgYXMgaXQgd2lsbCBhbGxvdyB0aGUgc2NyaXB0IHRvIGtub3cgdGhhdCB5b3VyIHNwZWNpZmljIGJpdCBvZiBjb2RlIChvciBzdGF0ZW1lbnQpIGlzIGZpbmlzaGVkIGFuZCB5b3UgY2FuIG1vdmUgb24gdG8geW91ciBuZXh0IG9uZS48YnIvPjxici8+CgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUV9Cm1lc3NhZ2UoeCA9IHBhc3RlKCI8Yj5XaGF0IGlzIGEgdmFyaWFibGU/PC9iPjxici8+PGJyLz4iLAogICAgICAgICAgICAgICJTZWUgW3RoaXNdKGh0dHBzOi8vd3d3LmZyZWVjb2RlY2FtcC5vcmcvbmV3cy9qYXZhc2NyaXB0LXZhcmlhYmxlcy1iZWdpbm5lcnMtZ3VpZGUvKSBwYWdlIGZvciBtb3JlIGluZm9ybWF0aW9uIG9uIHdoYXQgYHZhcmAgbWVhbnMuPGJyLz48YnIvPiIsCiAgICAgICAgICAgICAgIjxiPkNob29zaW5nIGEgbmFtZSBmb3IgeW91ciB2YXJpYWJsZTwvYj48YnIvPjxici8+IiwKICAgICAgICAgICAgICAiV2hlbiBuYW1pbmcgeW91ciB2YXJpYWJsZSB5b3Ugc2hvdWxkIGZvbGxvdyB0aGVzZSBydWxlczo8YnIvPjxici8+IiwKICAgICAgICAgICAgICBwYXN0ZTAoZmEoImNpcmNsZS14bWFyayIsIGZpbGwgPSAicmVkIiksICIgbmV2ZXIgaW5jbHVkZSBzcGFjZXMgKHVzZV91bmRlcnNjb3Jlc19pbnN0ZWFkKTxici8+IiksCiAgICAgICAgICAgICAgcGFzdGUwKGZhKCJjaXJjbGUteG1hcmsiLCBmaWxsID0gInJlZCIpLCAiIG5ldmVyIGJlZ2luIHdpdGggYSBudW1iZXI8YnIvPiIpLAogICAgICAgICAgICAgIHBhc3RlMChmYSgiY2lyY2xlLXhtYXJrIiwgZmlsbCA9ICJyZWQiKSwgIiBuZXZlciB1c2UgYSBzcGVjaWFsIGphdmFzY3JpcHQgbmFtZSAoZS5nLiAndmFyJyBpcyBub3QgYSBnb29kIG5hbWUgZm9yIHlvdXIgdmFyaWFibGUpPGJyLz48YnIvPiIpLAogICAgICAgICAgICAgIHBhc3RlMChmYSgiY2lyY2xlLWNoZWNrIiwgZmlsbCA9ICJncmVlbiIpLCAiIGFsd2F5cyBtYWtlIHRoZW0gdW5pcXVlIHRvIG90aGVyIHZhcmlhYmxlczxici8+IiksCiAgICAgICAgICAgICAgcGFzdGUwKGZhKCJjaXJjbGUtY2hlY2siLCBmaWxsID0gImdyZWVuIiksICIgYWx3YXlzIG1ha2UgdGhlbSBtZW1vcmFibGU8YnIvPiIpLAogICAgICAgICAgICAgIHBhc3RlMChmYSgiY2lyY2xlLWNoZWNrIiwgZmlsbCA9ICJncmVlbiIpLCAiIGFsd2F5cyBiZSBhd2FyZSBvZiB1cHBlciBhbmQgbG93ZXIgY2FzZSAoJ215VmFyaWFibGUnIHdpbGwgYmUgZGlzdGluY3QgZnJvbSAnbXl2YXJpYWJsZScpPGJyLz48YnIvPiIpLAogICAgICAgICAgICAgICI8Yj5XaGF0IGdvZXMgaW5zaWRlIHRoZSBicmFja2V0cz88L2I+PGJyLz48YnIvPiIsCiAgICAgICAgICAgICAgIldoZW4geW91IHNlZSBicmFja2V0cyBmb3IgYSBmdW5jdGlvbiwgdGhlcmUgYXJlIG5vcm1hbGx5IHNvbWUgYWRkaXRpb25hbCBwYXJhbWV0ZXJzIHRoYXQgeW91IGNhbiBzZXQsIHRvIHNlZSB3aGF0IHRoZXNlIGFyZSB5b3UgY2FuIGFsd2F5cyByZWZlciB0byB0aGUgb2ZmaWNpYWwgW2pzUHN5Y2ggZG9jdW1lbnRhdGlvbl0oaHR0cHM6Ly93d3cuanNwc3ljaC5vcmcvNy4zL3JlZmVyZW5jZS9qc3BzeWNoLyksIGZvciBub3cgd2Ugd2lsbCBrZWVwIG91ciBleHBlcmltZW50IHRvIHRoZSBkZWZhdWx0IHNldHRpbmdzLCBzbyB3ZSBkbyBub3QgbmVlZCB0byBhZGQgYW55dGhpbmcgaW5zaWRlIHRoZSBicmFja2V0cy48YnIvPjxici8+IgogICAgICAgICAgICAgICkpCgpgYGAKCiMjIDIuIEV4cGVyaW1lbnRhbCB0cmlhbHMgYW5kIHBsdWdpbnMKCk5vdyB3ZSBjYW4gYWN0dWFsbHkgbWFrZSBhIHRyaWFsIHRvIHNob3cgdG8gdGhlIHBhcnRpY2lwYW50LgoKVHJpYWxzIGNhbiBiZSBhbG1vc3QgYW55dGhpbmcgeW91IGNhbiB0aGluayBvZiwgYWxsIHlvdSBuZWVkIHRvIGRvIGlzIGNvbnZlcnQgeW91ciBpZGVhIGZvciBhbiBleHBlcmltZW50IGludG8gdGhlIHN1aXRhYmxlIHRyaWFscy4gVGhpcyBtYXkgdGFrZSBhIGJpdCBtb3JlIGV4cGVydGlzZSBmb3IgcmVhbGx5IGNvbXBsZXggZXhwZXJpbWVudHMsIGJ1dCBsdWNraWx5IEpzUHN5Y2ggaGFzIGFscmVhZHkgZG9uZSBhIGxvdCBvZiB0aGUgd29yayBhbmQgaGFzIGEgdG9vbGJveCBmdWxsIG9mIHR5cGljYWwgdHJpYWxzIHRoYXQgeW91IHdvdWxkIG5vcm1hbGx5IG5lZWQgdG8gcnVuIGFuIGV4cGVyaW1lbnQuCgpUbyBkbyB0aGlzIHdlIHVzZSBgcGx1Z2luc2AuLi4KCiMjIyBJbnRyb2R1Y2luZyBwbHVnaW5zCgpQbHVnaW5zIGFyZSBhIGtleSBmZWF0dXJlIG9mIEpzUHN5Y2gsIHRoZXkgYXJlIG91dGxpbmVkIHJlYWxseSBuaWNlbHkgYnkgdGhlIG9mZmljaWFsIEpzUHN5Y2ggW3BhZ2Ugb24gcGx1Z2luc10oaHR0cHM6Ly93d3cuanNwc3ljaC5vcmcvNy4zL292ZXJ2aWV3L3BsdWdpbnMvKSwgd2hpY2ggaXMgcGFzdGVkIGJlbG93OgoKPiBJbiBqc1BzeWNoLCBwbHVnaW5zIGRlZmluZSB0aGUga2luZHMgb2YgdHJpYWxzIG9yIGV2ZW50cyB0aGF0IHNob3VsZCBvY2N1ciBkdXJpbmcgdGhlIGV4cGVyaW1lbnQuIFNvbWUgcGx1Z2lucyBkZWZpbmUgdmVyeSBnZW5lcmFsIGV2ZW50cywgbGlrZSBkaXNwbGF5aW5nIGEgc2V0IG9mIGluc3RydWN0aW9ucyBwYWdlcywgZGlzcGxheWluZyBhbiBpbWFnZSBhbmQgcmVjb3JkaW5nIGEga2V5Ym9hcmQgcmVzcG9uc2UsIG9yIHBsYXlpbmcgYSBzb3VuZCBmaWxlIGFuZCByZWNvcmRpbmcgYSBidXR0b24gcmVzcG9uc2UuIE90aGVyIHBsdWdpbnMgYXJlIG1vcmUgc3BlY2lmaWMsIGxpa2UgdGhvc2UgdGhhdCBkaXNwbGF5IHBhcnRpY3VsYXIga2luZHMgb2Ygc3RpbXVsaSAoZS5nLiwgYSBjaXJjdWxhciB2aXN1YWwgc2VhcmNoIGFycmF5KSwgb3IgcnVuIGEgc3BlY2lmaWMgdmVyc2lvbiBvZiBwYXJ0aWN1bGFyIGtpbmQgb2YgdGFzayAoZS5nLiwgdGhlIEltcGxpY2l0IEFzc29jaWF0aW9uIFRlc3QpLiBQYXJ0IG9mIGNyZWF0aW5nIGFuIGV4cGVyaW1lbnQgd2l0aCBqc1BzeWNoIGludm9sdmVzIGZpZ3VyaW5nIG91dCB3aGljaCBwbHVnaW5zIGFyZSBuZWVkZWQgdG8gY3JlYXRlIHRoZSB0YXNrcyB5b3Ugd2FudCB5b3VyIHBhcnRpY2lwYW50cyB0byBwZXJmb3JtLjxici8+PGJyLz5QbHVnaW5zIHByb3ZpZGUgYSBzdHJ1Y3R1cmUgZm9yIGEgcGFydGljdWxhciB0cmlhbCBvciB0YXNrLCBidXQgb2Z0ZW4gYWxsb3cgZm9yIHNpZ25pZmljYW50IGN1c3RvbWl6YXRpb24gYW5kIGZsZXhpYmlsaXR5LiBGb3IgZXhhbXBsZSwgdGhlIGltYWdlLWtleWJvYXJkLXJlc3BvbnNlIHBsdWdpbiBkZWZpbmVzIGEgc2ltcGxlIHN0cnVjdHVyZSBmb3Igc2hvd2luZyBhbiBpbWFnZSBhbmQgY29sbGVjdGluZyBhIGtleWJvYXJkIHJlc3BvbnNlLiBZb3UgY2FuIHNwZWNpZnkgdGhlIHdoYXQgdGhlIHN0aW11bHVzIGlzLCB3aGF0IGtleXMgdGhlIHBhcnRpY2lwYW50IGlzIGFsbG93ZWQgdG8gcHJlc3MsIGhvdyBsb25nIHRoZSBzdGltdWx1cyBzaG91bGQgYmUgb24gdGhlIHNjcmVlbiwgaG93IGxvbmcgdGhlIHBhcnRpY2lwYW50IGhhcyB0byByZXNwb25kLCBhbmQgc28gb24uIE1hbnkgb2YgdGhlc2Ugb3B0aW9ucyBoYXZlIHJlYXNvbmFibGUgZGVmYXVsdCB2YWx1ZXM7IGV2ZW4gdGhvdWdoIHRoZSBpbWFnZSBwbHVnaW4gaGFzIG1hbnkgZGlmZmVyZW50IHBhcmFtZXRlcnMsIHlvdSBvbmx5IG5lZWQgdG8gc3BlY2lmeSB0aGUgaW1hZ2Ugc3RpbXVsdXMgaW4gb3JkZXIgdG8gdXNlIGl0LiBFYWNoIHBsdWdpbiBoYXMgaXRzIG93biBkb2N1bWVudGF0aW9uIHBhZ2UsIHdoaWNoIGRlc2NyaWJlcyB3aGF0IHRoZSBwbHVnaW4gZG9lcywgd2hhdCBvcHRpb25zIGFyZSBhdmFpbGFibGUsIGFuZCB3aGF0IGtpbmQgb2YgZGF0YSBpdCBjb2xsZWN0cy4KCklmIHdlIGxvb2sgYXQgdGhlIFtKc1BzeWNoIG9mZmljaWFsIGRvY3VtZW50YXRpb25dKGh0dHBzOi8vd3d3LmpzcHN5Y2gub3JnLzcuMy9wbHVnaW5zL2xpc3Qtb2YtcGx1Z2lucy8pLCB3ZSBjYW4gc2VlIHRoZXJlIGFyZSBsb3RzIG9mIHBsdWdpbnMgdGhhdCB3ZSBjYW4gY2hvb3NlIHRvIHVzZS4KClRoZSBkb2N1bWVudGF0aW9uIHBhZ2UgaXMgcmVhbGx5IG5pY2VseSB3cml0dGVuIGFuZCBoYXMgZXhhbXBsZXMgb2YgZWFjaCBvZiB0aGUgcGx1Z2lucywgd2l0aCBzZW5zaWJsZSBuYW1lcyBzbyBpdCBzaG91bGQgYmUgcmVsYXRpdmVseSBlYXN5IHRvIGNob29zZSB0aGUgcGx1Z2luIHRoYXQgc3VpdHMgeW91ciBuZWVkcy4KCldlIHdpbGwgd29yayB0aHJvdWdoIGEgZGVtbyBvZiBvbmUgb2YgdGhlc2UgcGx1Z2lucyB0byBkZW1vbnN0cmF0ZSBob3cgdG8gaW1wbGVtZW50IHRoZW0uCgpUaGlzIEdvb2dsZSBTaGVldCBjYW4gYWxzbyBiZSB1c2VkIGFzIGEgcXVpY2sgcmVmZXJlbmNlIHBvaW50IGZvciB0aGUgZGlmZmVyZW50IHBsdWdpbnMgd2l0aCBkZXNjcmlwdGlvbnMgb2Ygd2hhdCB0aGV5IGNhbiBiZSB1c2VkIGZvciAobm90ZSB0aGF0IGFsbCBvZiB0aGUgaW5mb3JtYXRpb24gaW4gdGhpcyBmaWxlIGlzIHRha2VuIGZyb20gdGhlIG9mZmljaWFsIEpzUHN5Y2ggcGx1Z2luIHBhZ2VzLCBpdCB3YXMgc2ltcGx5IGNvbXBpbGVkIGluIHRoaXMgZm9ybWF0IGJ5IG1lKToKCmh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzExQXFadTZLbkNhbmVabXY0aUZROEp1YWtJU2h2dnpKV3FIVTVna2NEM2Z3L2VkaXQ/dXNwPXNoYXJpbmcKCiMjIyBQbHVnaW4gcGFyYW1ldGVycwoKSW4gb3JkZXIgdG8gdXNlIGEgcGx1Z2luIHdlIG5lZWQgdG8gc2V0IHVwIGEgdHJpYWwgYXMgYSBgdmFyYCBzbyB0aGF0IHdlIGNhbiB1c2UgaXQgaW4gb3VyIGV4cGVyaW1lbnQuIFRoaXMgaXMgZG9uZSBieSB1c2luZyBjb2RlIHRoYXQgd291bGQgbG9vayBzaW1pbGFyIHRvIHRoaXM6CgpgYGB7anN9CnZhciBteV90cmlhbCA9IHsKICB0eXBlOiAncGx1Z2luX25hbWUnLAogIHBhcmFtZXRlcjE6IHNvbWV0aGluZywKICBwYXJhbWV0ZXIyOiBzb21ldGhpbmdfZWxzZQp9OwpgYGAKClRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGNvZGUgaW5zaWRlIHRoZSB7fSBpcyBpbXBvcnRhbnQsIHRoZXJlIHdpbGwgbm9ybWFsbHkgYmUgYSBsaXN0IG9mIHBhcmFtZXRlcnMgd2hpY2ggYXJlIHByZWRlZmluZWQgYnkgdGhlIHNwZWNpZmljIEpzUHN5Y2ggcGx1Z2luIHlvdSBhcmUgdXNpbmcsIGUuZy4gYHR5cGVgLCBmb2xsb3dlZCBieSBhIGA6YCwgdGhlIGluZm9ybWF0aW9uIHRvIHRoZSByaWdodCBvZiB0aGlzIGlzIHRoZSBpbmZvcm1hdGlvbiByZWxldmFudCB0byB0aGlzIHNwZWNpZmljIHBhcmFtZXRlci4KCllvdSB3aWxsIG5lZWQgdG8ga25vdyB3aGF0IHBhcmFtZXRlcnMgdG8gdXNlIGFuZCB0aGUgY29ycmVzcG9uZGluZyBpbmZvcm1hdGlvbiB0byBwcm92aWRlIGluIG9yZGVyIHRvIG1ha2UgdGhlIHBsdWdpbiB3b3JrIGFzIGludGVuZGVkLgoKU29tZSBhcmUgYWx3YXlzIGxpa2VseSB0byBiZSB1c2VkLCBlLmcuIHRoZSBgdHlwZWAgcGFyYW1ldGVyIGRlZmluZXMgd2hpY2ggcGx1Z2luIHlvdSB3YW50IHRvIHVzZSBmb3IgeW91ciB0cmlhbC4KClRoZSBiZXN0IHBsYWNlIHRvIGZpbmQgdGhpcyBpbmZvcm1hdGlvbiBpcyB0aGUgSnNQc3ljaCBwYWdlIGZvciBlYWNoIG9mIHRoZSBwbHVnaW5zIHVuZGVyIHRoZSBgcGFyYW1ldGVyc2Agc2VjdGlvbi4KCkJlbG93IGlzIHRoZSBwYWdlIGZvciB0aGUgYGh0bWwtYnV0dG9uLXJlc3BvbnNlYCBwYWdlOgoKIVtdKGltYWdlcy9qc3BzeWNoX3BhcmFtZXRlcnMucG5nKQoKIyMjIFBsdWdpbiBleGFtcGxlCgpUaGUgZmlyc3QgcGx1Z2luIHdlIHdpbGwgdXNlIGlzIGVzc2VudGlhbGx5IGEgYnV0dG9uIHJlc3BvbnNlLiBUaGUgcGFydGljaXBhbnQgd2lsbCBzZWUgYSBzdGltdWx1cyBvbiB0aGUgc2NyZWVuIGFuZCBhIGJ1dHRvbiB1bmRlcm5lYXRoIGl0LiBPbmNlIHRoZSBidXR0b24gaXMgcHJlc3NlZCwgdGhlIHRyaWFsIHdpbGwgZW5kLgoKVG8gZG8gdGhpcyB3ZSB1c2UgdGhlIGBodG1sLWJ1dHRvbi1yZXNwb25zZWAgLSBzZWUgdGhlIGRvY3VtZW50YXRpb24gcGFnZSBbaGVyZV0oaHR0cHM6Ly93d3cuanNwc3ljaC5vcmcvNy4zL3BsdWdpbnMvaHRtbC1idXR0b24tcmVzcG9uc2UvKS4KCkJlbG93IGlzIHRoZSBjb2RlIHVzZWQgdG8gY3JlYXRlIGEgc2luZ2xlIHRyaWFsLgoKYGBge2pzfQp2YXIgYnV0dG9uX3RyaWFsID0gewogIHR5cGU6IGpzUHN5Y2hIdG1sQnV0dG9uUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICdIZWxsbyB3b3JsZCcsCiAgY2hvaWNlczogWydjbGljayBtZSEnXSwKfTsKCmBgYAoKWW91IGNhbiBzZWUgaXQgaGFzIHRoZSBmb2xsb3dpbmcgc3RydWN0dXJlOgoKYHZhcmA6IHdlIHVzZSB2YXIgdG8gZGVjbGFyZSB0aGF0IHdlIGFyZSBtYWtpbmcgYSB2YXJpYWJsZQoKYGJ1dHRvbl90cmlhbGA6IHRoaXMgaXMgd2hlcmUgd2UgZGVmaW5lIHRoZSBuYW1lIGZvciBvdXIgdmFyaWFibGUsIGluIHRoaXMgY2FzZSB3ZSBhcmUgY2FsbGluZyBpdCBgYnV0dG9uX3RyaWFsYCwgcmVtZW1iZXIgdG8gY2hvb3NlIGEgc2Vuc2libGUgbmFtZSBmb3IgeW91ciB0cmlhbAoKYD1gOiB0aGlzIGRlbm90ZXMgdGhhdCBldmVyeXRoaW5nIG9uIHRoZSByaWdodCBvZiB0aGUgZXF1YWxzIHdpbGwgYmUgYXNzaWduZWQgdG8gb3VyIGJ1dHRvbl90cmlhbCB2YXJpYWJsZXMKCmB7fWA6IHRoaXMgbWVhbnMgd2UgY2FuIGFzc2lnbiBtdWx0aXBsZSB2YWx1ZXMgdG8gb3VyIHZhcmlhYmxlLCBpbnN0ZWFkIG9mIGp1c3Qgb25lLiBFdmVyeXRoaW5nIGluc2lkZSB0aGUge30gd2lsbCBiZSBzdG9yZWQgYXMgaW5mb3JtYXRpb24gcmVsZXZhbnQgdG8gb3VyIHZhcmlhYmxlLgoKYHR5cGU6YDp0aGlzIGlzIHRoZSBwYXJhbWV0ZXIgdXNlZCB0byBpZGVudGlmeSB3aGljaCBwbHVnaW4geW91IHdhbnQgdG8gdXNlIGZvciB5b3VyIHRyaWFsLCBmb3IgYW55IEpzUHN5Y2ggdHJpYWwgdGhhdCB1c2VzIGEgc3BlY2lmaWMgcGx1Z2luLCB5b3Ugd2lsbCBoYXZlIHRvIHVzZSBgdHlwZTpgIGZvbGxvd2VkIGJ5IHRoZSBzcGVjaWZpYyBwbHVnaW4gbmFtZQoKYGpzUHN5Y2hIdG1sQnV0dG9uUmVzcG9uc2VgOiB0aGlzIGlzIHRoZSBzcGVjaWZpYyBwbHVnaW4gbmFtZSBmb3IgYSBidXR0b24gcmVzcG9uc2UKCmBzdGltdWx1czpgOiB0aGlzIGlzIHRoZSBzdGFuZGFyZCB3YXkgdG8gaWRlbnRpZnkgd2hhdCB5b3VyIHN0aW11bHVzIHdpbGwgYmUKCmAnSGVsbG8gd29ybGQnYDogdGhpcyBpcyBvdXIgc3RpbXVsdXMuIEl0IGlzIGEgc3RyaW5nL3RleHQgc3RpbXVsaSwgYXMgZGVub3RlZCBieSB0aGUgdXNlIG9mIGAnYCBhcm91bmQgdGhlIHRleHQuIFN0cmluZ3MgY2FuIGNvbnRhaW4gbGV0dGVycywgbnVtYmVycyBvciBzcGVjaWFsIGNoYXJhY3RlcnMgZS5nLiAnSGVsbG8gd29ybGQxIScgd2lsbCBzdGlsbCB3b3JrIGFzIGEgdmFsaWQgc3RpbXVsdXMKCmBjaG9pY2VzOmA6IHRoaXMgaXMgdGhlIHBhcmFtZXRlciB1c2VkIHRvIGlkZW50aWZ5IHdoYXQgeW91ciBidXR0b24gd2lsbCBzYXkKCmBbJ2NsaWNrIG1lISddYDogdGhpcyBpcyB3aGF0IHRoZSBidXR0b24gc2F5cyBpbiBvdXIgdHJpYWwuIFdlIGFnYWluIHVzZSBhIHRleHQvc3RyaW5nIGZvciB0aGUgYnV0dG9uIGxhYmVsLiBOb3RlIHdlIGFsc28gdXNlIGBbXWAgYXJvdW5kIHRoZSBzdHJpbmcsIHRoaXMgaXMgYmVjYXVzZSBhIGJ1dHRvbiByZXNwb25zZSB0cmlhbCBjYW4gdGFrZSBtdWx0aXBsZSBzdHJpbmdzLCBzbyBpZiB5b3UgYWRkIGFub3RoZXIgc3RyaW5nIGluc2lkZSB0aGUgW10sIGUuZy4gYFsnY2xpY2sgbWUhJywgJ2RvIG5vdCBjbGljayBtZSEnXWAsIHdpbGwgZ2l2ZSB5b3UgdHdvIGJ1dHRvbnMKCllvdSBjYW4gc2VlIGZyb20gdGhlIHBhcmFtZXRlcnMgZG9jdW1lbnRhdGlvbiB0aGF0IHRoZXJlIGFyZSBhIGxvdCBtb3JlIHBhcmFtZXRlcnMgdGhhdCB5b3UgY2FuIHVzZSBmb3IgdGhpcyBwbHVnaW4sIHdoZXJlYXMgdGhlIGV4YW1wbGUgY29kZSB3ZSBoYXZlIHdyaXR0ZW4gYWJvdmUgaXMgcXVpdGUgc2ltcGxlLCBidXQgaXQgc3RpbGwgd29ya3MuIFdlIHdpbGwgY292ZXIgbW9yZSBjb21wbGV4IHRyaWFscyBsYXRlci4KCmBgYHtyIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0KbWVzc2FnZSh4ID0gcGFzdGUoIjxiPkltcG9ydGFudCBkaWZmZXJlbmNlcyBiZXR3ZWVuIGNvZ25pdGlvbi5ydW4gYW5kIG5vcm1hbCBqc1BzeWNoIGV4cGVyaW1lbnRzPC9iPiIsCiAgICAgICAgICAgICAgIllvdXIgZXhwZXJpbWVudCBpbiBjb2duaXRpb24ucnVuIHdpbGwgbG9vayBhIGJ1dCBkaWZmZXJlbnQgZnJvbSBvdGhlciBKc1BzeXNjaCBleHBlcmltZW50cywgZS5nLiB0aGUgb25lcyB5b3UgbWlnaHQgc2VlIHRoYXQgd2VyZSBob3N0ZWQgb24gcHJpdmF0ZSBzZXJ2ZXJzLiBUaGlzIGlzIGJlY2F1c2UgY29nbml0aW9uLnJ1biBkb2VzIHNvbWUgdGhpbmdzIGF1dG9tYXRpY2FsbHksIHNvIHlvdSBkbyBub3QgbmVlZCB0byB3b3JyeSBhYm91dCBhZGRpbmcgc3BlY2lmaWMgY29kZS4iLAogICAgICAgICAgICAgICJPbmUgb2YgdGhlIG1haW4gZGlmZmVyZW5jZXMgaXMgdGhhdCBhbGwgb2YgdGhlIHN0YW5kYXJkIEpzUHN5Y2ggcGx1Z2lucyBhcmUgYXV0b21hdGljYWxseSBsb2FkZWQgZm9yIHlvdS4gV2Ugd2lsbCBkaXNjdXNzIHRoaXMgbGF0ZXIgaW4gdGhlIHdvcmtzaGVldCwgYnV0IGlmIHlvdSBoYXZlIHVzZWQgSnNQc3ljaCBiZWZvcmUsIG9yIGFyZSBjb3B5aW5nIGNvZGUgZnJvbSBhIG5vbiBjb2duaXRpb24ucnVuIGV4cGVyaW1lbnQgeW91IG1pZ2h0IG5lZWQgdG8gYmUgYXdhcmUgb2YgdGhpcy4iLAogICAgICAgICAgICAgICJFc3NlbnRpYWxseSwgdGhlIGNvZGUgeW91IGhhdmUgaW4geW91ciBjb2duaXRpb24ucnVuIGV4cGVyaW1lbnQgd2lsbCBhbGwgYmUgamF2YXNjcmlwdCwgd2hlcmVhcyBhIHR5cGljYWwgSnNQc3ljaCBleHBlcmltZW50IHdpbGwgYmUgYSBodG1sIGZpbGUsIHdpdGggdGhlIGphdmFzY3JpcHQgZW1iZWRkZWQgd2l0aGluIHRoYXQgZmlsZS4iLAogICAgICAgICAgICAgIHNlcCA9ICI8YnIvPjxici8+IikpCgpgYGAKCiMjIDMuIFRpbWVsaW5lcwoKRm9yIG91ciBleHBlcmltZW50IHRvIHdvcmsgd2UgbmVlZCB0byBhZGQgYSBgdGltZWxpbmVgLiBXZSB1c2UgdGhlc2UgdG8gc3RvcmUgZWFjaCBvZiB0aGUgKip0cmlhbHMqKiBvZiBvdXIgZXhwZXJpbWVudCwgZS5nLiBpbmZvcm1lZCBjb25zZW50LCBpbnN0cnVjdGlvbnMsIHByYWN0aWNlIHRyaWFscywgZXhwZXJpbWVudGFsIHRyaWFscywgZW5kaW5nIG1lc3NhZ2UuCgpUaGUgYHRpbWVsaW5lYCBpcyBzaW1wbHkgdGhlIHBsYWNlIHdoZXJlIHdlIHN0b3JlIGFsbCBvZiB0aGUgdHJpYWxzLCBlbnN1cmluZyB0aGF0IHRoZXkgd2lsbCBiZSBwcmVzZW50ZWQgdG8gdGhlIHBhcnRpY2lwYW50IGluIGEgc3BlY2lmaWMgb3JkZXIuCgoqKldlIHdvdWxkIG5vcm1hbGx5IGNyZWF0ZSBhICoqYHRpbWVsaW5lYCAqKmJlZm9yZSBtYWtpbmcgdGhlIHRyaWFscyoqLCBidXQgaXQgc2VlbWVkIGxvZ2ljYWwgdG8gaW50cm9kdWNlIHBsdWdpbnMgZmlyc3QgZm9yIHRoaXMgd29ya3NoZWV0LgoKVGhpcyBjYW4gYmUgZG9uZSBieSBjcmVhdGluZyBhIGB2YXJgIGNhbGxlZCBgdGltZWxpbmVgIGFuZCB0aGVuIHRoZSB0aGluZyB0aGF0IHRoaXMgdmFyaWFibGUgd2lsbCBzdG9yZSBpcyBhbiAqKmVtcHR5IGFycmF5KiosIHdoaWNoIGlzIGRlbm90ZWQgYnkgdGhlIGBbXWAuCgpPbmNlIHdlIGhhdmUgc2V0IHVwIHRoZSB0aW1lbGluZSwgdGhlbiB3ZSBjYW4gbWFrZSB0aGUgdHJpYWxzIGFuZCAqKnB1c2gqKiB0aGVtIGludG8gdGhlIHRpbWVsaW5lIGFycmF5LiBTbyBvdXIgdGltZWxpbmUgd291bGQgZXZlbnR1YWxseSBsb29rIGxpa2UgW2luZm9ybWVkIGNvbnNlbnQsIGluc3RydWN0aW9ucywgcHJhY3RpY2UgdHJpYWxzLCBleHBlcmltZW50YWwgdHJpYWxzLCBlbmRpbmcgbWVzc2FnZV0uCgpgYGB7anMgfQp2YXIgdGltZWxpbmUgPSBbXTsKYGBgCgpOb3cgd2UgaGF2ZSBhIHRpbWVsaW5lIGFuZCBhIHRyaWFsLCB3ZSBjYW4gKipwdXNoKiogdGhlIHRyaWFsIGludG8gb3VyIHRpbWVsaW5lLCBzbyB0aGF0IHdoZW4gd2UgcnVuIHRoZSBleHBlcmltZW50LCB0aGUgdHJpYWwgaXMgcGFydCBvZiB0aGUgdGltZWxpbmUuCgpXZSBkbyB0aGlzIGluIHRoZSBmb2xsb3dpbmcgd2F5OgoKYGBge2pzfQp0aW1lbGluZS5wdXNoKGJ1dHRvbl90cmlhbCk7CmBgYAoKQnkgZG9pbmcgdGhpcywgb3VyIHRpbWVsaW5lIHdpbGwgbm93IGxvb2sgbGlrZSB0aGlzOiBgW2J1dHRvbl90cmlhbF1gLCB3aGljaCB3b3VsZCBiZSB0aGUgc2FtZSBhcyB3cml0aW5nIGB2YXIgdGltZWxpbmUgPSBbYnV0dG9uX3RyaWFsXWAuIE5vcm1hbGx5IHdlIHdvdWxkIGJlIGhhdmluZyBtYW55IHRyaWFscyBpbiBvdXIgdGltZWxpbmUsIHNvIHVzaW5nIGBwdXNoYCBpcyBhIHF1aWNrIGFuZCBlYXN5IHdheSB0byB0ZXN0IGFuZCBkZWJ1ZyBzcGVjaWZpYyB0cmlhbHMgaW4geW91ciB0aW1lbGluZS4KCmBgYHtyIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0KbWVzc2FnZSh4ID0gcGFzdGUoIjxiPkRvZXMgaXQgaGF2ZSB0byBiZSBjYWxsZWQgdGltZWxpbmU/PC9iPiIsCiAgICAgICAgICAgICAgIk5vLCB5b3UgY2FuIGNhbGwgeW91ciB0aW1lbGluZSBhbnl0aGluZyB5b3Ugd2FudCB3aGVuIGRlY2xhcmluZyBpdCBhcyBhIHZhci4iLAogICAgICAgICAgICAgICJJdCBpcyBpbXBvcnRhbnQgdGhhdCB5b3Uga25vdyB3aGF0IHlvdXIgdGltZWxpbmUgd2lsbCBiZSBjYWxsZWQgdGhvdWdoLCBhcyB0aGlzIHdpbGwgY2hhbmdlIHRoZSBjb2RlIHdoZW4geW91IHB1c2ggeW91ciB0cmlhbCIsCiAgICAgICAgICAgICAgImUuZy4gaWYgeW91IGRlZmluZSBhIHRpbWVsaW5lIGNhbGxlZCBteV90aW1lbGluZSwgdGhlbiB0byBwdXNoIHlvdXIgdHJpYWwgdG8gdGhpcyBhcnJheSwgeW91IHdpbGwgbmVlZCB0byB1c2UgbXlfdGltZWxpbmUucHVzaChidXR0b25fdHJpYWwpIiwKICAgICAgICAgICAgICBzZXAgPSAiPGJyLz48YnIvPiIpKQoKYGBgCgojIyA0LiBSdW5uaW5nIHlvdXIgZXhwZXJpbWVudAoKWW91IG1pZ2h0IGhhdmUgbm90aWNlZCBpbiB0aGUgcHJldmlldyBib3ggaW4gY29nbml0aW9uLnJ1biB0aGF0IHlvdSBoYXZlIG5vdCBzZWVuIHlvdXIgZXhwZXJpbWVudCB5ZXQuIFRoaXMgaXMgYmVjYXVzZSB0aGVyZSBpcyBvbmUgbGFzdCBpbXBvcnRhbnQgcGllY2Ugb2YgY29kZSB0aGF0IHdlIG5lZWQgaW4gb3JkZXIgdG8gbGV0IEpzUHN5Y2gga25vdyB0aGF0IHdlIGFyZSByZWFkeSB0byBydW4gb3VyIHRpbWVsaW5lLgoKVGhpcyBpczoKCmBgYHtqc30KanNQc3ljaC5ydW4odGltZWxpbmUpOwpgYGAKCllvdSBjYW4gc2VlIGhlcmUgdGhhdCB3ZSB1c2UgdGhlIGBqc1BzeWNoYCBhcyBvdXIgZmlyc3QgZnVuY3Rpb24sIHRoZW4gdGhlIGAucnVuYCBwYXJ0IGxldHMgSnNQc3ljaCBrbm93IHRoYXQgd2Ugd2FudCB0byBzdGFydCB0aGUgZXhwZXJpbWVudC4gSW5zaWRlIHRoZSBgKClgIHdlIHdyaXRlIHRoZSBuYW1lIG9mIG91ciB0aW1lbGluZS4KCiMjIDUuIFRoZSBmdWxsIGV4YW1wbGUgY29kZQoKWW91IHNob3VsZCBub3cgaGF2ZSBpbiB5b3VyIGNvZGUgc2VjdGlvbiB5b3VyIGNvbXBsZXRlIGNvZGUgdG8gcnVuIGEgdmVyeSBiYXNpYyBleHBlcmltZW50LgoKVGhpcyBzaG91bGQgaW5jbHVkZToKCi0gSW5pdGlhdGluZyBKc1BzeWNoCi0gVXNpbmcgYSBwbHVnaW4gdG8gbWFrZSBhIHRyaWFsCi0gQ3JlYXRpbmcgYSB0aW1lbGluZSBhbmQgcHVzaGluZyB5b3VyIHRyaWFsIHRvIHRoZSB0aW1lbGluZQotIFJ1bm5pbmcgeW91ciB0aW1lbGluZQoKSGVyZSBpcyBhbiBleGFtcGxlIG9mIGNvZGUgdGhhdCBzaG91bGQgd29yaywgaWYgeW91IHdhbnQgdG8gY29weSBhbmQgcGFzdGUgaXQ6CgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUUsIG91dC53aWR0aD0iOTAlIn0Ka25pdHI6OmluY2x1ZGVfdXJsKCJjb2RlX2V4YW1wbGVzL1VudGl0bGVkMS5odG1sIikKCmBgYAoKVGhpcyBpcyBob3cgaXQgc2hvdWxkIGxvb2sgaW4gY29nbml0aW9uLnJ1bjoKCiFbXShpbWFnZXMvZGVtbzEuZ2lmKQoKCgoKCmBgYHtyIGVjaG89RkFMU0UsIGV2YWw9VFJVRSwgd2FybmluZz1GQUxTRX0KaHRtbHRvb2xzOjp0YWdzJHNjcmlwdChzcmMgPSAianMvdHJhbnNsYXRlLmpzIikKIyBodG1sdG9vbHM6OnRhZ3Mkc2NyaXB0KHNyYyA9ICJqcy9pbmZvYm94LmpzIikKaHRtbHRvb2xzOjp0YWdzJHNjcmlwdChzcmM9Ii8vdHJhbnNsYXRlLmdvb2dsZS5jb20vdHJhbnNsYXRlX2EvZWxlbWVudC5qcz9jYj1nb29nbGVUcmFuc2xhdGVFbGVtZW50SW5pdCIpCgpodG1sdG9vbHM6OnRhZ0xpc3QoCiAgeGFyaW5nYW5FeHRyYTo6dXNlX2NsaXBib2FyZCgKICAgIGJ1dHRvbl90ZXh0ID0gIjxpIGNsYXNzPVwiZmEgZmEtY2xpcGJvYXJkXCIgc3R5bGU9XCJmb250LXNpemU6IDI1cHhcIj48L2k+IiwKICAgIHN1Y2Nlc3NfdGV4dCA9ICI8aSBjbGFzcz1cImZhIGZhLWNoZWNrXCIgc3R5bGU9XCJjb2xvcjogIzkwQkU2RDsgZm9udC1zaXplOiAyNXB4XCI+PC9pPiIsCiAgKSwKICBybWFya2Rvd246Omh0bWxfZGVwZW5kZW5jeV9mb250X2F3ZXNvbWUoKQopCgpgYGAKCmBgYHtqcyBlY2hvPUZBTFNFLCBldmFsPVRSVUV9CnZhciBjb2xsID0gZG9jdW1lbnQuZ2V0RWxlbWVudHNCeUNsYXNzTmFtZSgiY29sbGFwc2libGUxIik7CnZhciBpOwoKZm9yIChpID0gMDsgaSA8IGNvbGwubGVuZ3RoOyBpKyspIHsKICBjb2xsW2ldLmFkZEV2ZW50TGlzdGVuZXIoImNsaWNrIiwgZnVuY3Rpb24oKSB7CiAgICB0aGlzLmNsYXNzTGlzdC50b2dnbGUoImFjdGl2ZTEiKTsKICAgIHZhciBjb250ZW50ID0gdGhpcy5uZXh0RWxlbWVudFNpYmxpbmc7CiAgICBpZiAoY29udGVudC5zdHlsZS5tYXhIZWlnaHQpewogICAgICBjb250ZW50LnN0eWxlLm1heEhlaWdodCA9IG51bGw7CiAgICB9IGVsc2UgewogICAgICBjb250ZW50LnN0eWxlLm1heEhlaWdodCA9IGNvbnRlbnQuc2Nyb2xsSGVpZ2h0ICsgInB4IjsKICAgIH0KICB9KTsKfQoKYGBgCg==