Using jsPsych and cognition.run

Session 2 - intermediate


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:

  • Building a lexical decision experiment

    • using a keyboard response plugin
    • customising the stimuli with CSS
    • customising the plugin parameters
    • combining trials into a timeline

  • Timeline variables

    • how to use them
    • adding variables to your results data


  • Working with stimuli files

    • starting with csv files
    • using R to convert to JSON
    • uploading to cognition.run

Recap

In the last session we should have:

  • set up an account for cognition.run
  • become familiar with the cognition.run interface
  • become familiar with JsPsych
  • learnt what a plugin is and how they work
  • built a very simple experiment

Building a lexical decision experiment

One of the most common measurements collected in experiments are reaction times (rt). In this section we will be using a keyboard response plugin, i.e. where the participant presses a specific key on their keyboard in response to a stimuli in order to measure how long it takes the participant to respond.

The paradigm that we will use to practice using this plugin will be the lexical decision task, where a word or non-word will be presented on the screen and the participant has to respond to the stimuli with one of two key options, they will press key1 if they think the word is real, key2 if they think the word is not real.

the html-keyboard-response plugin

The basic structure of the html-keyboard-response plugin is:

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'my_stimulus',
  choices: ['key1', 'key2', 'key_n']
};

If we used this in a full experiment in cognition.run, we would just see my_stimulus on the screen and it would just wait for a valid keyboard response from the participant.

  • The type parameter declares which plugin to use, here we are using the jsPsychHtmlKeyboardResponse plugin

  • The stimulus parameter declares what stimulus to present, here we just use the string 'my_stimulus'

  • The choices parameter declares which keys are valid for the trial, here we use an array, as shown by the [ and ] at the start and end. The strings 'key1', 'key2' and 'key_n' are not actually valid keys and would not be recognised by jspsych, they are just for generalisation. Instead, we would probably use something like ['a', 'b', 'c'] if we wanted to allow responses only from the keys a, b and c.

    Note
    If you want to allow all possible keys, we would use choices: 'ALL_KEYS'.

    If you want to prevent all keys from being allowed, we would use choices: 'NO_KEYS'.

    If you are unsure how to specify the key you want, refer to https://docs.google.com/spreadsheets/d/19v9OGreNGmmOAacJ33cSrouNO_OfBxqa4hiXTwex5dY/edit?usp=sharing

For this tutorial, we will make the following trial as the basis for our lexical decision experiment:

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h']
};

We now have the stimulus as word and the choices as d or h.

Remember, that if we want to preview all of our experiment in cognition.run, we need to have a fully working jspsych code, so the full code we would need is:

// --------
// Title: lexical decision experiment
// jsPsych version: 7.3.1
// date: [today]
// author: [your name]
//----------

var jsPsych = initJsPsych();

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h']
};

var timeline = [];

timeline.push(my_keyboard_trial);

jsPsych.run(timeline);

This is what it should look like in cognition.run:

customising the stimulus with CSS

Our stimulus may not look how we want it to look, e.g. the font, colour, size etc might not be the preferred style for our experiment.

This is because jspsych has certain default values for the appearance of things like text. But, it can easily be modified with some extra code.

This is based on CSS (Cascading Style Sheets), which allows html to be styled in a specific way. We will cover CSS in more detail later, but it is quite a large topic and is a separate language to html and javascript, so it is not within the scope of this level of tutorial to cover it thoroughly now.

What we will do is apply some basic CSS styling to our stimulus, to demonstrate how to customise some aspects of the appearance. Crucially, this can be done within some basic html code. This keeps things simple.

Currently, our stimulus is 'word', which is presented with the following defaults:

  • font-family: Arial;
  • font-size: 18px;
  • color: black;
  • font-weight: normal;

If we want to change any of this, or indeed anything else about the appearance, we need to add some inline css to the stimulus.

We can do this by modifying the stimulus to look more like html with css:

stimulus: '<div>word</div>

You will see the added <div> and </div> at the start and end of the word.

This is html.

It identifies the text as a content division element. Note that there is <div> at the start to indicate the start of the content and a </div> a the end to indicate the end of the content. Nothing really changes at the moment as jspsych already defines the text in this way by default.

However, what we can now do is add css to our div. We do this in the following way:

stimulus: '<div style="color:red;">word</div>

Now we have added some css to our html object. This is done by the style="color:red" part.

When adding css directly to html in this way, we first have to declare that we want to style the specific element, in this example the text contained in our div. Therefore it goes within the starting <div>.

It is evaluated as css by the style function, with different elements of the styling being declared within the " " after the =.

In the above example, we change the css of the text color to red. Note the ; which we use to indicate the end of the color parameter changes.

We can now use this basic structure to change other parameters related to the styling of the text in our div element.

stimulus: '<div style="color:red; background: pink; font-size: 30pt; font-family: monospace; font-weight: bold; position: absolute; top: 0px; left: 0;">word</div>

Now we have the following parameters declared:

  • colour: red makes the text color red, you can use color names or hex codes e.g. #00000 is black
  • background: pink makes the text background pink, again you can use color names or hex codes
  • font-size: 30pt makes the text size 30 point, you can also specify other units
  • font-family: monospace makes the font monospace, other web safe fonts can be used
  • font-weight: bold makes the font bold, other weights can be used
  • position: absolute makes the positioning of the text absolute in relation to the screen, it needs additional parameters to be specified such as top, right, bottom or left
  • top: 0px makes the absolute positioning to be 0px from the top
  • left: 0px makes the absolute positioning to be 0px from the left

This is what it should like:

customising the plugin parameters

As we do not really need much styling to our stimuli, we will return to the original defaults of:

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h']
};

However, what we can do is change specific parameters of our trial.

If we refer to the plugin documentation on the jspsych website - https://www.jspsych.org/7.0/plugins/html-keyboard-response/ we can see that there are many other parameters that we can modify with this plugin.

prompt

We might for example want to include a prompt if the trials are practice trials, so the participant can be reminded of which keys to press:

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h'],
  prompt: 'press "d" if you think it is a real word, press "h" if you think it is not a real word'
};

As you can see, the prompt is displayed in a quite inconvenient position. So we can now modify this using the html and css code we used previously:

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>'
};

Now, we have changed the font size to 10pt so it is smaller than the stimulus text.

We have also change the position, this time using position:relative; bottom:150px, unlike in the previous example where we used position:absolute, the use of relative means we move relative to where it was originally placed, so in this example we move it 150px from the bottom, i.e. 150px vertically upwards from the bottom.

This is how it should now look:

stimulus_duration

We might also want to only show the stimulus on the screen for a specified amount of time. To do this, we can add the stimulus_duration parameter:

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>',
  stimulus_duration: 500
};

In the example above, we have set this parameter to 500, which means that the stimulus will be shown only for 500ms. Note that the trial does not finish until a response has been made.

trial_duration

If we want to make the trial end after a specified time, we can also add the trial_duration parameter:

var my_keyboard_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>',
  stimulus_duration: 500,
  trial_duration: 1000
};

Now, the trial will end after 1000ms or if there is a response by the participant in within the specified time.

This is how it should look:

combining trials into a timeline

At the moment our experiment comprises of just one trial…, which is not particularly useful. However, what we have covered so far should allow us to put together the building blocks of an experiment.

In this section we will combine multiple trials into a timeline that is nested within our main timeline.

This will involve adding a new variable that has a fixation trial, so that our lexical decision trial is preceeded by this fixation trial.

Lets first make the lexical decision trial, we will keep it as just a keyboard response with no duration parameters:

var lexical_decision_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>'
};

Now we will make the fixation trial, which will show a + stimulus on the screen for a set amount of time, in this case 2000ms.

Note the font-size is set to 30pt and the choices is NO_KEYS, this mean we will just see the + on the screen and there will be no keys that end the trial, just the time limit of 2000ms:

var fixation_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: '<div style="font-size:30pt;">+</div>',
  choices: 'NO_KEYS',
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;"><br/></div>',
  trial_duration: 2000
};

We can now make a new variable called lexical_decision_combined, this will combine the fixation_trial and the lexical_decision_trial in a specified order, within a specified timeline.

We do this using the timeline parameter, which we can specify within a standard var.

We assign the timeline an array, [fixation_trial, lexical_decision_trial], which means that the timeline will first show the fixation_trial, then the lexical_decision_trial. If we changed the positions of the items in the array this will change the order of the presentation.

var lexical_decision_combined = {
  timeline: [fixation_trial, lexical_decision_trial]
};

If we now push lexical_decision_combined to our timeline, using the timeline.push, we should see that it presents the two trials together.

This is how your code should look so far:

// --------
// Title: Demo experiment
// jsPsych version: 7.3.1
// date: [today]
// author: [your name]
//----------

var jsPsych = initJsPsych();

var fixation_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: '<div style="font-size:30pt;">+</div>',
  choices: 'NO_KEYS',
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;"><br/></div>',
  trial_duration: 2000
};

var lexical_decision_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: 'word',
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>'
};

var lexical_decision_combined = {
  timeline: [fixation_trial, lexical_decision_trial]
};

var timeline = [];
timeline.push(lexical_decision_combined);

jsPsych.run(timeline);

Timeline variables

The experiment is not really an experiment so far, it presents just one word…

So what we will focus on now is using larger stimuli sets, and using them within our timeline.

This means we can iterate through a list of stimuli, presenting them to the participant and collecting their responses during each trial.

introducing JSON

An important thing to note about jspsych is that you can not just give it a csv or excel formatted file containing your stimuli. Instead, jspsych and javascript more generally, prefers to use JSON formatted files.

So an important concept to learn is how to work with this type of file.

Here is a framework example of what JSON formatted data looks like:

var stimuli = [
    {"variable_name1": "first_item", "variable_name2":"1"},
    {"variable_name1": "second_item", "variable_name2":"2"},
    {"variable_name1": "third_item", "variable_name2":"3"},
    {"variable_name1": "fourth_item", "variable_name2":"4"}
];

As a csv or excel file this would look like this:

In the stimuli variable above, we have a very specific way of coding the data:

  • everything is stored in an array, i.e. we use [ and ] around the information, this will allow jspsych to understand everything in a way similar to each item in the array being like a row in a csv or excel file

  • each item in the array is enclosed in { and }, the information within each of these items is essentially like a row of data. Each item is separated by a ,

  • the structure of the items is "variable_name1":"value", which means the variable name is specified as a string, i.e. " " followed by a :, with the corresponding value following, also normally specified as a string. The next variable follows the same structure, with new variables separated by a ,

  • in order for jspsych to read the information in a JSON formatted object, we need to assign it as a var, e.g. var stimuli =

introducing timeline variables

As mentioned before, we probably want our experiment to iterate through a list of stimuli, presenting those stimuli within a timeline.

To do this, we can use the timeline_variables parameter. This is normally done in a var that contains a timeline, e.g. like the lexical_decision_combined var we made previously.

First lets make some example stimuli suitable for a lexical decision task:

var stimuli = [
    {"item": "hello", "real_word":"yes", "correct_key":"d"},
    {"item": "hxllo", "real_word":"no", "correct_key":"h"},
    {"item": "world", "real_word":"yes", "correct_key":"d"},
    {"item": "wxrld", "real_word":"no", "correct_key":"h"}
];

Here the stimuli have three variables:

  • item the stimuli to be presented during the trial
  • real_word whether the word is considered to be real or not
  • correct_key the key the expect the participant to respond with, where d is a real word and h is a non-word

Now we can use this var for our timeline_variables parameter:

var lexical_decision_combined = {
  timeline: [fixation_trial, lexical_decision_trial],
  timeline_variables: stimuli
};

The next step is to update the stimuli parameter in the lexical_decision_trial var:

var lexical_decision_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: jsPsych.timelineVariable("item"),
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>'
};

One thing you might have noticed is that the stimuli are presented in the same order as they are specified in the stimuli this is because if we want the presentation order to be randomised, we need to add an extra parameter to our lexical_decision_combined var. This is randomize_order, which we can set to true:

var lexical_decision_combined = {
  timeline: [fixation_trial, lexical_decision_trial],
  timeline_variables: stimuli,
  randomize_order: true
};

Now we have a randomised version of our lexical decision experiment, using the stimuli stored in the stimuli var, where we iterate through the stimuli, presenting a fixation and then the lexical decision trial.

This is what it should look like:

Understanding the results

inspecting the data

Now we have a working lexical decision experiment, it might be worth looking at the results.

We can do this in cognition.run by looking at the bottom right box:

As you complete trials in the experiment, it will add a new row of data to this section. So every time you advance in the experiment, the data is stored automatically.

It is easy to have a quick look at the data using this box, but you can also download it as a csv file using the download button. Which should give you a file called demo_preview.csv.

If you open this in excel or R you should see something like this:

There should be 18 columns and 8 rows of data, we can quickly inspect what these are:

The key variables to consider are:

  • rt this is the reaction time measured in ms
  • stimulus this is the stimulus shown to the participant, including any html
  • response this is the recorded value for the trial, in this example it will be the key pressed
  • trial_type this is the plugin used
  • trial_index this is the trial number, it increases sequentially after each trial
  • time_elapsed this is the duration of the experiment, recorded at the end of each trial, it is cumulative

All the other variables are quite technical: - internal_node_id this is a way to distinguish between different parts of the experiment - run_id this is the id of the individual attempt at the experiment - condition this is the condition assigned to the participant - recorded_at this is the date and time of the recorded data - source_code_version this is probably something related to cognition.run - ip this is the ip address of the participant - user_agent this contains the details of the browser being used by the participant - device this is the make of the computer being used by the participant - browser this is the browser being used by the participant - browser_version this is the version of the browser being used by the participant - platform this is the operating system being used by the participant - platform_version this is the version of the operating system being used by the participant

If we look at the rt column, we can see that there are values for our lexical decision trials. In the stimulus column we can see which item was being responded to, e.g. světe had a rt of 571 in the above screenshot.

Note that data for the fixation trials will also be stored as a row, even though we might not actually need it for analysis.

adding variables to our results

What would be helpful however, is to add new columns to our data, such as the additional ones in our stimuli var.

To do this, we need to add some extra parameters to our lexical_decision_trial var, specifically a data parameter:

var lexical_decision_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: jsPsych.timelineVariable("item"),
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>',
  data: {
    task: 'lexical_decision',
    real_word: jsPsych.timelineVariable('real_word'),
    correct_key: jsPsych.timelineVariable('correct_key')
    }
};

The format of this parameter takes an object format, so the information needs to have { and } around the information.

Now we can define the variables to be stored in the data:

  • task will store just a string of lexical_decision for all of these trials
  • real_word will store the value in our timeline variable relating to the real_word variable, so in this example it will be either yes or no
  • correct_key will again store the value in our timeline variable, but for the correct_key variable, so in this example either d or h

The names of the variables in our data object can be anything you choose, assuming they are valid names, i.e. no spaces etc. if you are wanting to store something from the timeline_variables var, you must use jsPsych.timlineVariable() with the name of the specific variable you want to refer to inside the brackets, written as a string, e.g. 'real_word'.

Now lets add some data to our fixation trial too, so we can distinguish it from the lexical decision trial in the task variable:

var fixation_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: '<div style="font-size:30pt;">+</div>',
  choices: 'NO_KEYS',
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;"><br/></div>',
  trial_duration: 2000,
  data: {
    task: 'fixation'
  }
};

Now if we complete the experiment and look at the data, we should see (note: I have removed some columns for ease of reading the screenshot, the new variables stored in the data will be at the right of the csv after platform_version):

The full code should be:

// --------
// Title: Demo experiment
// jsPsych version: 7.3.1
// date: [today]
// author: [your name]
//----------

var jsPsych = initJsPsych();

var stimuli = [
    {"item": "hello", "real_word":"yes", "correct_key":"d"},
    {"item": "hxllo", "real_word":"no", "correct_key":"h"},
    {"item": "world", "real_word":"yes", "correct_key":"d"},
    {"item": "wxrld", "real_word":"no", "correct_key":"h"}
];

var fixation_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: '<div style="font-size:30pt;">+</div>',
  choices: 'NO_KEYS',
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;"><br/></div>',
  trial_duration: 2000,
  data: {
    task: 'fixation'
  }
};

var lexical_decision_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: jsPsych.timelineVariable("item"),
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>'
};

var lexical_decision_combined = {
  timeline: [fixation_trial, lexical_decision_trial],
  timeline_variables: stimuli,
  randomize_order: true
};

var timeline = [];
timeline.push(lexical_decision_combined);

jsPsych.run(timeline);

Formatting files from csv to javascript/JSON in R

One annoying thing about all this, is the idea of having to reformat all your stimuli to a javascript variable in JSON format, which may be an unfamiliar format and most researchers use excel or R to prepare their stimuli files in csv format.

There is a solution to this problem by using R!

There is a great package called jsonlite which is really useful for working with data in json format. This means we can easily format a csv file to json and save it as a javascript var with just a few lines of code.

You can read more about jsonlite here.

So our workflow will be this:

  1. create a stimuli file in the traditional way, e.g. using excel or other spreadsheet style software
  2. export that file to .csv format
  3. load in useful packages
  4. load that file in to R
  5. convert the R version of the data to JSON using the toJSON function
  6. add some text to make it a javascript object
  7. export that file to .js format
  8. upload it as an `external JS/CSS file in cognition.run
  9. use the var as a timeline variable

1. create a stimuli file

Open up a spreadsheet software and make your stimuli file.

In this worksheet we will continue with the lexical decision style stimuli, i.e. words and non-words.

There should be 3 columns: item, real_word and correct_key There should be 20 rows: 10 of which are real words and the other 10 are non-words

The data should look like this:

2. export to csv

If you are working with your own file, export it as a .csv file, preferably with utf-8 encoding.

The filename should be lexical_decision_stimuli.csv

You can do this in Microsoft Excel by following this recording:

Alternatively, you can download the file:

lexical_decision_stimuli.csv.

3. load in packages

Open up RStudio on your computer and open a new R script.

You will need the jsonlite the tidyverse packages installed and loaded in to R.

If you have not got any of these installed, first run this code:

install.packages("jsonlite")
install.packages("tidyverse")

Once you have them installed, you should now load them in to R:

library(jsonlite)
library(tidyverse)

4. load in to R

We will need to load in the csv file in to R as an object. To do this, we need to tell R where the file is - the working directory or folder containing the file.

If you are not familiar with R, you can set the working directory by clicking on the options in this screenshot, then choose the folder where your csv file is saved:

Now store the file as an object called lexical_decision_stimuli.

lexical_decision_stimuli <- read_delim("lexical_decision_stimuli.csv", delim = ";")

5. convert to JSON

We will now be able to use the toJSON function.

We will assign the json version to an object called lexical_decision_stimuli_JSON.

The toJSON function takes as the first argument your data, so in this case lexical_decision_stimuli.

We use the additional argument pretty=TRUE to make the output look pretty and easy to read.

lexical_decision_stimuli_JSON <- toJSON(lexical_decision_stimuli, pretty=TRUE)

lexical_decision_stimuli_JSON

We now have JSON formatted data!

6. make into javascript var

You will see there is no var assignment in the lexical_decision_stimuli_JSON object, it is just text.

But we can add this information quite easily using the paste0 function:

lexical_decision_stimuli_JSON <- paste0("var stimuli = ", lexical_decision_stimuli_JSON, ";")

lexical_decision_stimuli_JSON

We should now see that the start of the text has var stimuli =, followed by the JSON formatted data, and finally the text ends with a ;.

7. export that file to javascript

Now we have the data formatted correctly, we can save the file using the write_file function, note that you should specify the exact file path of where you want the file to be saved:

write_file(x = lexical_decision_stimuli_JSON, file = "lexical_decision_stimuli_JSON.js")

You should now see the file in your working directory, importantly this is a js or javascript file.

You can download this file

here, in case you had problems with running the R code.

It should look like this:

Here is the full R code in a slightly more efficient way:

install.packages("jsonlite")
install.packages("tidyverse")

library(jsonlite)
library(tidyverse)

paste0("var stimuli = ",
       toJSON(read_delim("lexical_decision_stimuli.csv", delim = ";"),
              pretty=TRUE),
       ";") %>%
  write_file(file = "lexical_decision_stimuli_JSON.js")

8. upload that file to cognition.run

You will see in cognition.run that there is an option to upload external JS/CSS, this is how we will now upload our stimuli file to the experiment.

Click on the browse button and find the lexical_decision_stimuli_JSON.js file.

Once it has been uploaded, you should see it appear underneath the browse button.

9. using the stimuli as a timeline_variable

Notice that we probably still have the following code in our script:

var stimuli = [
    {"item": "hello", "real_word":"yes", "correct_key":"d"},
    {"item": "hxllo", "real_word":"no", "correct_key":"h"},
    {"item": "world", "real_word":"yes", "correct_key":"d"},
    {"item": "wxrld", "real_word":"no", "correct_key":"h"}
];

This means that our new stimuli will be overwritten by this section of the code. This is because we named our new stimuli var stimuli.

To resolve this issue, and use our new stimuli, we need to delete the above code in our script.

This means our final code should be:

// --------
// Title: Demo experiment
// jsPsych version: 7.3.1
// date: [today]
// author: [your name]
//----------

// inititate jspsych
var jsPsych = initJsPsych();

// make a fixation trial
var fixation_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: '<div style="font-size:30pt;">+</div>',
  choices: 'NO_KEYS',
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;"><br/></div>',
  trial_duration: 2000,
  data: {
    task: 'fixation'
  }
};

// make a lexical decision trial
var lexical_decision_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: jsPsych.timelineVariable("item"),
  choices: ['d', 'h'],
  prompt: '<div style="font-size:10pt; position:relative; bottom:150px;">press "d" if you think it is a real word, press "h" if you think it is not a real word</div>',
  data: {
    task: 'lexical_decision',
    real_word: jsPsych.timelineVariable('real_word'),
    correct_key: jsPsych.timelineVariable('correct_key')
    }
};

// make a variable which combines the fixation and lexical decision together
// use stimuli as timeline_variables and randomise the presentation order
var lexical_decision_combined = {
  timeline: [fixation_trial, lexical_decision_trial],
  timeline_variables: stimuli,
  randomize_order: true
};

// set up main timeline
var timeline = [];

// push the lexical_decision_combined variable to the timeline
timeline.push(lexical_decision_combined);

//run the experiment
jsPsych.run(timeline);
LS0tCnRpdGxlOiAiVXNpbmcganNQc3ljaCBhbmQgY29nbml0aW9uLnJ1biIKc3VidGl0bGU6ICJTZXNzaW9uIDIgLSBpbnRlcm1lZGlhdGUiCmF1dGhvcjogIkphbWVzIEJyYW5kIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIHBhbmRvY19hcmdzOiAiLS1oaWdobGlnaHQtc3R5bGU9bXkudGhlbWUiCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgY29sbGFwc2VkOiBmYWxzZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBsaWdodGJveDogVFJVRQogICAgZ2FsbGVyeTogVFJVRQogICAgY3NzOiAiY3NzL3N0eWxlLmNzcyIKICAgIAotLS0KCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc2xpY2tSKQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeSh4YXJpbmdhbkV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShmb250YXdlc29tZSkKbGlicmFyeShic3BsdXMpCgpgYGAKCmBgYHtyIHNldHVwLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBldmFsID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UpCgprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgbWVzc2FnZSA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgICBwYXN0ZSgnPGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJjb2xsYXBzaWJsZTEiPjxzdHJvbmc+JywKICAgICBmYShuYW1lID0gImNpcmNsZS1pbmZvIiksCiAgICAgJyBtb3JlIGluZm88L3N0cm9uZz48L2J1dHRvbj4nLCAnPGRpdiBjbGFzcz0iY29udGVudDEiPjxwPicsCiAgICAgZ3N1YignIyMnLCAnXG4nLCB4KSwKICAgICAnPC9wPjwvZGl2PicsCiAgICAgc2VwID0gJ1xuJykKICAgfSkKCmNvZGVibG9jayA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgICBjYXQocGFzdGUoJzxkaXYgY2xhc3M9ImNvZGVibG9jayI+JywKICAgICBwYXN0ZTAoeCksCiAgICAgJzwvZGl2PicsCiAgICAgc2VwID0gJ1xuJykpCiAgIH0KCmBgYAoKLS0tCgojIyBgciBmYSgibGFuZ3VhZ2UiKWAgVHJhbnNsYXRpb25zIGF2YWlsYWJsZQoKRGlzY2xhaW1lcjogbWF5IG5vdCBiZSB2ZXJ5IGFjY3VyYXRlLi4uCgo8ZGl2IGlkPSJnb29nbGVfdHJhbnNsYXRlX2VsZW1lbnQiPjwvZGl2PgoKLS0tCgojIFdvcmtzaGVldCBvdmVydmlldwoKIyMgYHIgZmEoImNyb3NzaGFpcnMiKWAgQWltcwoKQnkgdGhlIGVuZCBvZiB0aGlzIHdvcmtzaGVldCB5b3Ugc2hvdWxkIGJlIGFibGUgdG86CgotICoqcHJvZ3JhbSoqIHlvdXIgb3duIGV4cGVyaW1lbnRzIGluIGpzUHN5Y2gKLSAqKmhvc3QqKiB0aGUgZXhwZXJpbWVudCBvbmxpbmUgdXNpbmcgY29nbml0aW9uLnJ1bgotICoqdXNlKiogdGhlIHBhcnRpY2lwYW50IGRhdGEgZm9yIGFuYWx5c2lzCi0gKiphcHBseSoqIHRoZSBiYXNpYyBza2lsbHMgeW91IGhhdmUgbGVhcm50IGZvciB5b3VyIG93biBwdXJwb3NlcwotICoqbGVhcm4qKiBzb21lIGV4dHJhIHNraWxscyBzdWNoIGFzIEhUTUwsIGphdmFzY3JpcHQsIENTUyBhbmQgSlNPTgoKIyMgYHIgZmEoInVzZXItZ3JhZHVhdGUiKWAgUHJlLXJlcXVpc2l0ZXMKClRvIGNvbXBsZXRlIHRoZSBhaW1zIHlvdSB3aWxsIG5lZWQgdG86CgotICoqZm9sbG93KiogdGhpcyB3b3Jrc2hlZXQKLSAqKmFzayoqIHF1ZXN0aW9ucyBpZiB5b3UgYXJlIG5vdCBzdXJlL2JlIGFibGUgdG8gZ29vZ2xlCi0gKipoYXZlKiogYSB3b3JraW5nIGNvbXB1dGVyIGFuZCBpbnRlcm5ldCBjb25uZWN0aW9uCi0gKipiZSBwYXRpZW50Kiogd2hlbiB0aGluZ3MgZG8gbm90IHdvcmsKCllvdSBkbyBub3QgbmVlZCB0bzoKCi0gaGF2ZSBhbnkgKipwcm9ncmFtbWluZyBrbm93bGVkZ2UqKgotIGhhdmUgaGlnaCAqKmNvbXB1dGVyIGxpdGVyYWN5KioKLSBrbm93IGFueXRoaW5nIGFib3V0ICoqanNQc3ljaCwgY29nbml0aW9uLnJ1biwgaHRtbCwgY3NzIG9yIGphdmFzY3JpcHQqKgotIGJlIGEgKipsaW5ndWlzdCoqCgojIyBgciBmYSgiZm9sZGVyLXRyZWUiKWAgU3RydWN0dXJlCgpUaGUgd29ya3NoZWV0IHdpbGwgZ28gdGhyb3VnaCB0aGUgZm9sbG93aW5nIHNlY3Rpb25zOgoKLSBCdWlsZGluZyBhIGxleGljYWwgZGVjaXNpb24gZXhwZXJpbWVudAoKICAgIC0gdXNpbmcgYSBrZXlib2FyZCByZXNwb25zZSBwbHVnaW4KICAgIC0gY3VzdG9taXNpbmcgdGhlIHN0aW11bGkgd2l0aCBDU1MKICAgIC0gY3VzdG9taXNpbmcgdGhlIHBsdWdpbiBwYXJhbWV0ZXJzCiAgICAtIGNvbWJpbmluZyB0cmlhbHMgaW50byBhIHRpbWVsaW5lCjxici8+PGJyLz4KLSBUaW1lbGluZSB2YXJpYWJsZXMKCiAgICAtIGhvdyB0byB1c2UgdGhlbQogICAgLSBhZGRpbmcgdmFyaWFibGVzIHRvIHlvdXIgcmVzdWx0cyBkYXRhCiAgICAtIAo8YnIvPjxici8+Ci0gV29ya2luZyB3aXRoIHN0aW11bGkgZmlsZXMKCiAgICAtIHN0YXJ0aW5nIHdpdGggY3N2IGZpbGVzCiAgICAtIHVzaW5nIFIgdG8gY29udmVydCB0byBKU09OCiAgICAtIHVwbG9hZGluZyB0byBjb2duaXRpb24ucnVuCgo8IS0tICMjIGByIGZhKCJhcnJvdy1yaWdodCIpYCBVcCBuZXh0IC0tPgoKPCEtLSBJbiB0aGUgbmV4dCBzZXNzaW9ucyB3ZSB3aWxsIGNvdmVyOiAtLT4KCjwhLS0gLSBidWlsZGluZyBhIHByb3BlciBleHBlcmltZW50IC0tPgo8IS0tIC0gdXNpbmcgc3RpbXVsaSBmaWxlcyAtLT4KPCEtLSAtIGN1c3RvbWlzaW5nIHBsdWdpbnMgLS0+CjwhLS0gLSBjdXN0b21pc2luZyBhcHBlYXJhbmNlIC0tPgo8IS0tIC0gd29ya2luZyB3aXRoIHRoZSBkYXRhIC0tPgoKIyMgYHIgZmEoImxpZ2h0YnVsYiIpYCBSZWNhcAoKSW4gdGhlIGxhc3Qgc2Vzc2lvbiB3ZSBzaG91bGQgaGF2ZToKCi0gc2V0IHVwIGFuIGFjY291bnQgZm9yIGNvZ25pdGlvbi5ydW4KLSBiZWNvbWUgZmFtaWxpYXIgd2l0aCB0aGUgY29nbml0aW9uLnJ1biBpbnRlcmZhY2UKLSBiZWNvbWUgZmFtaWxpYXIgd2l0aCBKc1BzeWNoCi0gbGVhcm50IHdoYXQgYSBwbHVnaW4gaXMgYW5kIGhvdyB0aGV5IHdvcmsKLSBidWlsdCBhIHZlcnkgc2ltcGxlIGV4cGVyaW1lbnQKCi0tLQoKIyBCdWlsZGluZyBhIGxleGljYWwgZGVjaXNpb24gZXhwZXJpbWVudAoKT25lIG9mIHRoZSBtb3N0IGNvbW1vbiBtZWFzdXJlbWVudHMgY29sbGVjdGVkIGluIGV4cGVyaW1lbnRzIGFyZSByZWFjdGlvbiB0aW1lcyAocnQpLiBJbiB0aGlzIHNlY3Rpb24gd2Ugd2lsbCBiZSB1c2luZyBhIGtleWJvYXJkIHJlc3BvbnNlIHBsdWdpbiwgaS5lLiB3aGVyZSB0aGUgcGFydGljaXBhbnQgcHJlc3NlcyBhIHNwZWNpZmljIGtleSBvbiB0aGVpciBrZXlib2FyZCBpbiByZXNwb25zZSB0byBhIHN0aW11bGkgaW4gb3JkZXIgdG8gbWVhc3VyZSBob3cgbG9uZyBpdCB0YWtlcyB0aGUgcGFydGljaXBhbnQgdG8gcmVzcG9uZC4KClRoZSBwYXJhZGlnbSB0aGF0IHdlIHdpbGwgdXNlIHRvIHByYWN0aWNlIHVzaW5nIHRoaXMgcGx1Z2luIHdpbGwgYmUgdGhlICoqbGV4aWNhbCBkZWNpc2lvbiB0YXNrKiosIHdoZXJlIGEgd29yZCBvciBub24td29yZCB3aWxsIGJlIHByZXNlbnRlZCBvbiB0aGUgc2NyZWVuIGFuZCB0aGUgcGFydGljaXBhbnQgaGFzIHRvIHJlc3BvbmQgdG8gdGhlIHN0aW11bGkgd2l0aCBvbmUgb2YgdHdvIGtleSBvcHRpb25zLCB0aGV5IHdpbGwgcHJlc3MgYGtleTFgIGlmIHRoZXkgdGhpbmsgdGhlIHdvcmQgaXMgcmVhbCwgYGtleTJgIGlmIHRoZXkgdGhpbmsgdGhlIHdvcmQgaXMgbm90IHJlYWwuCgojIyB0aGUgaHRtbC1rZXlib2FyZC1yZXNwb25zZSBwbHVnaW4KClRoZSBiYXNpYyBzdHJ1Y3R1cmUgb2YgdGhlIGBodG1sLWtleWJvYXJkLXJlc3BvbnNlYCBwbHVnaW4gaXM6CgpgYGB7anN9CnZhciBteV9rZXlib2FyZF90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICdteV9zdGltdWx1cycsCiAgY2hvaWNlczogWydrZXkxJywgJ2tleTInLCAna2V5X24nXQp9OwoKYGBgCgpJZiB3ZSB1c2VkIHRoaXMgaW4gYSBmdWxsIGV4cGVyaW1lbnQgaW4gY29nbml0aW9uLnJ1biwgd2Ugd291bGQganVzdCBzZWUgYG15X3N0aW11bHVzYCBvbiB0aGUgc2NyZWVuIGFuZCBpdCB3b3VsZCBqdXN0IHdhaXQgZm9yIGEgdmFsaWQga2V5Ym9hcmQgcmVzcG9uc2UgZnJvbSB0aGUgcGFydGljaXBhbnQuCgotIFRoZSBgdHlwZWAgcGFyYW1ldGVyIGRlY2xhcmVzIHdoaWNoIHBsdWdpbiB0byB1c2UsIGhlcmUgd2UgYXJlIHVzaW5nIHRoZSBganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlYCBwbHVnaW4KCi0gVGhlIGBzdGltdWx1c2AgcGFyYW1ldGVyIGRlY2xhcmVzIHdoYXQgc3RpbXVsdXMgdG8gcHJlc2VudCwgaGVyZSB3ZSBqdXN0IHVzZSB0aGUgc3RyaW5nIGAnbXlfc3RpbXVsdXMnYAoKLSBUaGUgYGNob2ljZXNgIHBhcmFtZXRlciBkZWNsYXJlcyB3aGljaCBrZXlzIGFyZSB2YWxpZCBmb3IgdGhlIHRyaWFsLCBoZXJlIHdlIHVzZSBhbiBhcnJheSwgYXMgc2hvd24gYnkgdGhlIGBbYCBhbmQgYF1gIGF0IHRoZSBzdGFydCBhbmQgZW5kLiBUaGUgc3RyaW5ncyBgJ2tleTEnYCwgYCdrZXkyJ2AgYW5kIGAna2V5X24nYCBhcmUgbm90IGFjdHVhbGx5IHZhbGlkIGtleXMgYW5kIHdvdWxkIG5vdCBiZSByZWNvZ25pc2VkIGJ5IGpzcHN5Y2gsIHRoZXkgYXJlIGp1c3QgZm9yIGdlbmVyYWxpc2F0aW9uLiBJbnN0ZWFkLCB3ZSB3b3VsZCBwcm9iYWJseSB1c2Ugc29tZXRoaW5nIGxpa2UgYFsnYScsICdiJywgJ2MnXWAgaWYgd2Ugd2FudGVkIHRvIGFsbG93IHJlc3BvbnNlcyBvbmx5IGZyb20gdGhlIGtleXMgYGFgLCBgYmAgYW5kIGBjYC48YnIvPjxici8+KipOb3RlKio8YnIvPklmIHlvdSB3YW50IHRvIGFsbG93ICoqYWxsIHBvc3NpYmxlIGtleXMqKiwgd2Ugd291bGQgdXNlIGBjaG9pY2VzOiAnQUxMX0tFWVMnYC48YnIvPjxici8+SWYgeW91IHdhbnQgdG8gKipwcmV2ZW50IGFsbCBrZXlzKiogZnJvbSBiZWluZyBhbGxvd2VkLCB3ZSB3b3VsZCB1c2UgYGNob2ljZXM6ICdOT19LRVlTJ2AuPGJyLz48YnIvPklmIHlvdSBhcmUgdW5zdXJlIGhvdyB0byBzcGVjaWZ5IHRoZSBrZXkgeW91IHdhbnQsIHJlZmVyIHRvIGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL3NwcmVhZHNoZWV0cy9kLzE5djlPR3JlTkdtbU9BYWNKMzNjU3JvdU5PX09mQnhxYTRoaVhUd2V4NWRZL2VkaXQ/dXNwPXNoYXJpbmcKCkZvciB0aGlzIHR1dG9yaWFsLCB3ZSB3aWxsIG1ha2UgdGhlIGZvbGxvd2luZyB0cmlhbCBhcyB0aGUgYmFzaXMgZm9yIG91ciBsZXhpY2FsIGRlY2lzaW9uIGV4cGVyaW1lbnQ6CgpgYGB7anN9CnZhciBteV9rZXlib2FyZF90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICd3b3JkJywKICBjaG9pY2VzOiBbJ2QnLCAnaCddCn07CgpgYGAKCldlIG5vdyBoYXZlIHRoZSBzdGltdWx1cyBhcyBgd29yZGAgYW5kIHRoZSBjaG9pY2VzIGFzIGBkYCBvciBgaGAuCgpSZW1lbWJlciwgdGhhdCBpZiB3ZSB3YW50IHRvIHByZXZpZXcgYWxsIG9mIG91ciBleHBlcmltZW50IGluIGNvZ25pdGlvbi5ydW4sIHdlIG5lZWQgdG8gaGF2ZSBhIGZ1bGx5IHdvcmtpbmcganNwc3ljaCBjb2RlLCBzbyB0aGUgZnVsbCBjb2RlIHdlIHdvdWxkIG5lZWQgaXM6CgpgYGB7anN9Ci8vIC0tLS0tLS0tCi8vIFRpdGxlOiBsZXhpY2FsIGRlY2lzaW9uIGV4cGVyaW1lbnQKLy8ganNQc3ljaCB2ZXJzaW9uOiA3LjMuMQovLyBkYXRlOiBbdG9kYXldCi8vIGF1dGhvcjogW3lvdXIgbmFtZV0KLy8tLS0tLS0tLS0tCgp2YXIganNQc3ljaCA9IGluaXRKc1BzeWNoKCk7Cgp2YXIgbXlfa2V5Ym9hcmRfdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiAnd29yZCcsCiAgY2hvaWNlczogWydkJywgJ2gnXQp9OwoKdmFyIHRpbWVsaW5lID0gW107Cgp0aW1lbGluZS5wdXNoKG15X2tleWJvYXJkX3RyaWFsKTsKCmpzUHN5Y2gucnVuKHRpbWVsaW5lKTsKCmBgYAoKVGhpcyBpcyB3aGF0IGl0IHNob3VsZCBsb29rIGxpa2UgaW4gY29nbml0aW9uLnJ1bjoKCiFbXShpbWFnZXMvd29yZF90cmlhbC5wbmcpCgojIyBjdXN0b21pc2luZyB0aGUgc3RpbXVsdXMgd2l0aCBDU1MKCk91ciBzdGltdWx1cyBtYXkgbm90IGxvb2sgaG93IHdlIHdhbnQgaXQgdG8gbG9vaywgZS5nLiB0aGUgZm9udCwgY29sb3VyLCBzaXplIGV0YyBtaWdodCBub3QgYmUgdGhlIHByZWZlcnJlZCBzdHlsZSBmb3Igb3VyIGV4cGVyaW1lbnQuCgpUaGlzIGlzIGJlY2F1c2UganNwc3ljaCBoYXMgY2VydGFpbiBkZWZhdWx0IHZhbHVlcyBmb3IgdGhlIGFwcGVhcmFuY2Ugb2YgdGhpbmdzIGxpa2UgdGV4dC4gQnV0LCBpdCBjYW4gZWFzaWx5IGJlIG1vZGlmaWVkIHdpdGggc29tZSBleHRyYSBjb2RlLgoKVGhpcyBpcyBiYXNlZCBvbiBDU1MgKENhc2NhZGluZyBTdHlsZSBTaGVldHMpLCB3aGljaCBhbGxvd3MgaHRtbCB0byBiZSBzdHlsZWQgaW4gYSBzcGVjaWZpYyB3YXkuIFdlIHdpbGwgY292ZXIgQ1NTIGluIG1vcmUgZGV0YWlsIGxhdGVyLCBidXQgaXQgaXMgcXVpdGUgYSBsYXJnZSB0b3BpYyBhbmQgaXMgYSBzZXBhcmF0ZSBsYW5ndWFnZSB0byBodG1sIGFuZCBqYXZhc2NyaXB0LCBzbyBpdCBpcyBub3Qgd2l0aGluIHRoZSBzY29wZSBvZiB0aGlzIGxldmVsIG9mIHR1dG9yaWFsIHRvIGNvdmVyIGl0IHRob3JvdWdobHkgbm93LgoKV2hhdCB3ZSB3aWxsIGRvIGlzIGFwcGx5IHNvbWUgYmFzaWMgQ1NTIHN0eWxpbmcgdG8gb3VyIHN0aW11bHVzLCB0byBkZW1vbnN0cmF0ZSBob3cgdG8gY3VzdG9taXNlIHNvbWUgYXNwZWN0cyBvZiB0aGUgYXBwZWFyYW5jZS4gQ3J1Y2lhbGx5LCB0aGlzIGNhbiBiZSBkb25lIHdpdGhpbiBzb21lIGJhc2ljIGh0bWwgY29kZS4gVGhpcyBrZWVwcyB0aGluZ3Mgc2ltcGxlLgoKQ3VycmVudGx5LCBvdXIgc3RpbXVsdXMgaXMgYCd3b3JkJ2AsIHdoaWNoIGlzIHByZXNlbnRlZCB3aXRoIHRoZSBmb2xsb3dpbmcgZGVmYXVsdHM6CgotIGZvbnQtZmFtaWx5OiBBcmlhbDsKLSBmb250LXNpemU6IDE4cHg7Ci0gY29sb3I6IGJsYWNrOwotIGZvbnQtd2VpZ2h0OiBub3JtYWw7CgpJZiB3ZSB3YW50IHRvIGNoYW5nZSBhbnkgb2YgdGhpcywgb3IgaW5kZWVkIGFueXRoaW5nIGVsc2UgYWJvdXQgdGhlIGFwcGVhcmFuY2UsIHdlIG5lZWQgdG8gYWRkIHNvbWUgaW5saW5lIGNzcyB0byB0aGUgc3RpbXVsdXMuCgpXZSBjYW4gZG8gdGhpcyBieSBtb2RpZnlpbmcgdGhlIHN0aW11bHVzIHRvIGxvb2sgbW9yZSBsaWtlIGh0bWwgd2l0aCBjc3M6CgpgYGB7anN9CnN0aW11bHVzOiAnPGRpdj53b3JkPC9kaXY+CgpgYGAKCllvdSB3aWxsIHNlZSB0aGUgYWRkZWQgYDxkaXY+YCBhbmQgYDwvZGl2PmAgYXQgdGhlIHN0YXJ0IGFuZCBlbmQgb2YgdGhlIHdvcmQuCgpUaGlzIGlzIGh0bWwuCgpJdCBpZGVudGlmaWVzIHRoZSB0ZXh0IGFzIGEgYGNvbnRlbnQgZGl2aXNpb24gZWxlbWVudGAuIE5vdGUgdGhhdCB0aGVyZSBpcyBgPGRpdj5gIGF0IHRoZSBzdGFydCB0byBpbmRpY2F0ZSB0aGUgc3RhcnQgb2YgdGhlIGNvbnRlbnQgYW5kIGEgYDwvZGl2PmAgYSB0aGUgZW5kIHRvIGluZGljYXRlIHRoZSBlbmQgb2YgdGhlIGNvbnRlbnQuIE5vdGhpbmcgcmVhbGx5IGNoYW5nZXMgYXQgdGhlIG1vbWVudCBhcyBqc3BzeWNoIGFscmVhZHkgZGVmaW5lcyB0aGUgdGV4dCBpbiB0aGlzIHdheSBieSBkZWZhdWx0LgoKSG93ZXZlciwgd2hhdCB3ZSBjYW4gbm93IGRvIGlzIGFkZCBjc3MgdG8gb3VyIGRpdi4gV2UgZG8gdGhpcyBpbiB0aGUgZm9sbG93aW5nIHdheToKCmBgYHtqc30Kc3RpbXVsdXM6ICc8ZGl2IHN0eWxlPSJjb2xvcjpyZWQ7Ij53b3JkPC9kaXY+CgpgYGAKCk5vdyB3ZSBoYXZlIGFkZGVkIHNvbWUgY3NzIHRvIG91ciBodG1sIG9iamVjdC4gVGhpcyBpcyBkb25lIGJ5IHRoZSBgc3R5bGU9ImNvbG9yOnJlZCJgIHBhcnQuCgpXaGVuIGFkZGluZyBjc3MgZGlyZWN0bHkgdG8gaHRtbCBpbiB0aGlzIHdheSwgd2UgZmlyc3QgaGF2ZSB0byBkZWNsYXJlIHRoYXQgd2Ugd2FudCB0byBzdHlsZSB0aGUgc3BlY2lmaWMgZWxlbWVudCwgaW4gdGhpcyBleGFtcGxlIHRoZSB0ZXh0IGNvbnRhaW5lZCBpbiBvdXIgYGRpdmAuIFRoZXJlZm9yZSBpdCBnb2VzIHdpdGhpbiB0aGUgc3RhcnRpbmcgYDxkaXY+YC4KCkl0IGlzIGV2YWx1YXRlZCBhcyBjc3MgYnkgdGhlIGBzdHlsZWAgZnVuY3Rpb24sIHdpdGggZGlmZmVyZW50IGVsZW1lbnRzIG9mIHRoZSBzdHlsaW5nIGJlaW5nIGRlY2xhcmVkIHdpdGhpbiB0aGUgYCIgImAgYWZ0ZXIgdGhlIGA9YC4KCkluIHRoZSBhYm92ZSBleGFtcGxlLCB3ZSBjaGFuZ2UgdGhlIGNzcyBvZiB0aGUgdGV4dCBgY29sb3JgIHRvIGByZWRgLiBOb3RlIHRoZSBgO2Agd2hpY2ggd2UgdXNlIHRvIGluZGljYXRlIHRoZSBlbmQgb2YgdGhlIGNvbG9yIHBhcmFtZXRlciBjaGFuZ2VzLgoKV2UgY2FuIG5vdyB1c2UgdGhpcyBiYXNpYyBzdHJ1Y3R1cmUgdG8gY2hhbmdlIG90aGVyIHBhcmFtZXRlcnMgcmVsYXRlZCB0byB0aGUgc3R5bGluZyBvZiB0aGUgdGV4dCBpbiBvdXIgZGl2IGVsZW1lbnQuCgpgYGB7anN9CnN0aW11bHVzOiAnPGRpdiBzdHlsZT0iY29sb3I6cmVkOyBiYWNrZ3JvdW5kOiBwaW5rOyBmb250LXNpemU6IDMwcHQ7IGZvbnQtZmFtaWx5OiBtb25vc3BhY2U7IGZvbnQtd2VpZ2h0OiBib2xkOyBwb3NpdGlvbjogYWJzb2x1dGU7IHRvcDogMHB4OyBsZWZ0OiAwOyI+d29yZDwvZGl2PgoKYGBgCgpOb3cgd2UgaGF2ZSB0aGUgZm9sbG93aW5nIHBhcmFtZXRlcnMgZGVjbGFyZWQ6CgotIGBjb2xvdXI6IHJlZGAgbWFrZXMgdGhlIHRleHQgY29sb3IgcmVkLCB5b3UgY2FuIHVzZSBbY29sb3IgbmFtZXNdKGh0dHBzOi8vaHRtbGNvbG9yY29kZXMuY29tL2NvbG9yLW5hbWVzLykgb3IgW2hleCBjb2Rlc10oaHR0cHM6Ly9odG1sY29sb3Jjb2Rlcy5jb20vY29sb3ItcGlja2VyLykgZS5nLiAjMDAwMDAgaXMgYmxhY2sKLSBgYmFja2dyb3VuZDogcGlua2AgbWFrZXMgdGhlIHRleHQgYmFja2dyb3VuZCBwaW5rLCBhZ2FpbiB5b3UgY2FuIHVzZSBjb2xvciBuYW1lcyBvciBoZXggY29kZXMKLSBgZm9udC1zaXplOiAzMHB0YCBtYWtlcyB0aGUgdGV4dCBzaXplIDMwIHBvaW50LCB5b3UgY2FuIGFsc28gc3BlY2lmeSBvdGhlciBbdW5pdHNdKGh0dHBzOi8vd3d3LmRpZ2l0YWxvY2Vhbi5jb20vY29tbXVuaXR5L3R1dG9yaWFscy9jc3MtY3NzLXVuaXRzLWV4cGxhaW5lZCkKLSBgZm9udC1mYW1pbHk6IG1vbm9zcGFjZWAgbWFrZXMgdGhlIGZvbnQgbW9ub3NwYWNlLCBvdGhlciBbd2ViIHNhZmUgZm9udHNdKGh0dHBzOi8vd3d3Lnczc2Nob29scy5jb20vY3NzcmVmL2Nzc193ZWJzYWZlX2ZvbnRzLnBocCkgY2FuIGJlIHVzZWQKLSBgZm9udC13ZWlnaHQ6IGJvbGRgIG1ha2VzIHRoZSBmb250IGJvbGQsIG90aGVyIFt3ZWlnaHRzXShodHRwczovL2RldmVsb3Blci5tb3ppbGxhLm9yZy9lbi1VUy9kb2NzL1dlYi9DU1MvZm9udC13ZWlnaHQpIGNhbiBiZSB1c2VkCi0gYHBvc2l0aW9uOiBhYnNvbHV0ZWAgbWFrZXMgdGhlIHBvc2l0aW9uaW5nIG9mIHRoZSB0ZXh0IGFic29sdXRlIGluIHJlbGF0aW9uIHRvIHRoZSBzY3JlZW4sIGl0IG5lZWRzIGFkZGl0aW9uYWwgcGFyYW1ldGVycyB0byBiZSBzcGVjaWZpZWQgc3VjaCBhcyB0b3AsIHJpZ2h0LCBib3R0b20gb3IgbGVmdAotIGB0b3A6IDBweGAgbWFrZXMgdGhlIGFic29sdXRlIHBvc2l0aW9uaW5nIHRvIGJlIDBweCBmcm9tIHRoZSB0b3AKLSBgbGVmdDogMHB4YCBtYWtlcyB0aGUgYWJzb2x1dGUgcG9zaXRpb25pbmcgdG8gYmUgMHB4IGZyb20gdGhlIGxlZnQKClRoaXMgaXMgd2hhdCBpdCBzaG91bGQgbGlrZToKCiFbXShpbWFnZXMvd29yZF90cmlhbF9jc3MucG5nKQoKIyMgY3VzdG9taXNpbmcgdGhlIHBsdWdpbiBwYXJhbWV0ZXJzCgpBcyB3ZSBkbyBub3QgcmVhbGx5IG5lZWQgbXVjaCBzdHlsaW5nIHRvIG91ciBzdGltdWxpLCB3ZSB3aWxsIHJldHVybiB0byB0aGUgb3JpZ2luYWwgZGVmYXVsdHMgb2Y6CgpgYGB7anN9CnZhciBteV9rZXlib2FyZF90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICd3b3JkJywKICBjaG9pY2VzOiBbJ2QnLCAnaCddCn07CgpgYGAKCkhvd2V2ZXIsIHdoYXQgd2UgY2FuIGRvIGlzIGNoYW5nZSBzcGVjaWZpYyBwYXJhbWV0ZXJzIG9mIG91ciB0cmlhbC4KCklmIHdlIHJlZmVyIHRvIHRoZSBwbHVnaW4gZG9jdW1lbnRhdGlvbiBvbiB0aGUganNwc3ljaCB3ZWJzaXRlIC0gaHR0cHM6Ly93d3cuanNwc3ljaC5vcmcvNy4wL3BsdWdpbnMvaHRtbC1rZXlib2FyZC1yZXNwb25zZS8gd2UgY2FuIHNlZSB0aGF0IHRoZXJlIGFyZSBtYW55IG90aGVyIHBhcmFtZXRlcnMgdGhhdCB3ZSBjYW4gbW9kaWZ5IHdpdGggdGhpcyBwbHVnaW4uCgojIyMgcHJvbXB0CgpXZSBtaWdodCBmb3IgZXhhbXBsZSB3YW50IHRvIGluY2x1ZGUgYSBgcHJvbXB0YCBpZiB0aGUgdHJpYWxzIGFyZSBwcmFjdGljZSB0cmlhbHMsIHNvIHRoZSBwYXJ0aWNpcGFudCBjYW4gYmUgcmVtaW5kZWQgb2Ygd2hpY2gga2V5cyB0byBwcmVzczoKCmBgYHtqc30KdmFyIG15X2tleWJvYXJkX3RyaWFsID0gewogIHR5cGU6IGpzUHN5Y2hIdG1sS2V5Ym9hcmRSZXNwb25zZSwKICBzdGltdWx1czogJ3dvcmQnLAogIGNob2ljZXM6IFsnZCcsICdoJ10sCiAgcHJvbXB0OiAncHJlc3MgImQiIGlmIHlvdSB0aGluayBpdCBpcyBhIHJlYWwgd29yZCwgcHJlc3MgImgiIGlmIHlvdSB0aGluayBpdCBpcyBub3QgYSByZWFsIHdvcmQnCn07CgpgYGAKCkFzIHlvdSBjYW4gc2VlLCB0aGUgcHJvbXB0IGlzIGRpc3BsYXllZCBpbiBhIHF1aXRlIGluY29udmVuaWVudCBwb3NpdGlvbi4gU28gd2UgY2FuIG5vdyBtb2RpZnkgdGhpcyB1c2luZyB0aGUgaHRtbCBhbmQgY3NzIGNvZGUgd2UgdXNlZCBwcmV2aW91c2x5OgoKYGBge2pzfQp2YXIgbXlfa2V5Ym9hcmRfdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiAnd29yZCcsCiAgY2hvaWNlczogWydkJywgJ2gnXSwKICBwcm9tcHQ6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MTBwdDsgcG9zaXRpb246cmVsYXRpdmU7IGJvdHRvbToxNTBweDsiPnByZXNzICJkIiBpZiB5b3UgdGhpbmsgaXQgaXMgYSByZWFsIHdvcmQsIHByZXNzICJoIiBpZiB5b3UgdGhpbmsgaXQgaXMgbm90IGEgcmVhbCB3b3JkPC9kaXY+Jwp9OwoKYGBgCgpOb3csIHdlIGhhdmUgY2hhbmdlZCB0aGUgZm9udCBzaXplIHRvIGAxMHB0YCBzbyBpdCBpcyBzbWFsbGVyIHRoYW4gdGhlIHN0aW11bHVzIHRleHQuCgpXZSBoYXZlIGFsc28gY2hhbmdlIHRoZSBgcG9zaXRpb25gLCB0aGlzIHRpbWUgdXNpbmcgYHBvc2l0aW9uOnJlbGF0aXZlOyBib3R0b206MTUwcHhgLCB1bmxpa2UgaW4gdGhlIHByZXZpb3VzIGV4YW1wbGUgd2hlcmUgd2UgdXNlZCBgcG9zaXRpb246YWJzb2x1dGVgLCB0aGUgdXNlIG9mIGByZWxhdGl2ZWAgbWVhbnMgd2UgbW92ZSByZWxhdGl2ZSB0byB3aGVyZSBpdCB3YXMgb3JpZ2luYWxseSBwbGFjZWQsIHNvIGluIHRoaXMgZXhhbXBsZSB3ZSBtb3ZlIGl0IGAxNTBweGAgZnJvbSB0aGUgYGJvdHRvbWAsIGkuZS4gMTUwcHggdmVydGljYWxseSB1cHdhcmRzIGZyb20gdGhlIGJvdHRvbS4KClRoaXMgaXMgaG93IGl0IHNob3VsZCBub3cgbG9vazoKCiFbXShpbWFnZXMvd29yZF90cmlhbF9wcm9tcHQucG5nKQoKIyMjIHN0aW11bHVzX2R1cmF0aW9uCgpXZSBtaWdodCBhbHNvIHdhbnQgdG8gb25seSBzaG93IHRoZSBzdGltdWx1cyBvbiB0aGUgc2NyZWVuIGZvciBhIHNwZWNpZmllZCBhbW91bnQgb2YgdGltZS4gVG8gZG8gdGhpcywgd2UgY2FuIGFkZCB0aGUgYHN0aW11bHVzX2R1cmF0aW9uYCBwYXJhbWV0ZXI6CgpgYGB7anN9CnZhciBteV9rZXlib2FyZF90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICd3b3JkJywKICBjaG9pY2VzOiBbJ2QnLCAnaCddLAogIHByb21wdDogJzxkaXYgc3R5bGU9ImZvbnQtc2l6ZToxMHB0OyBwb3NpdGlvbjpyZWxhdGl2ZTsgYm90dG9tOjE1MHB4OyI+cHJlc3MgImQiIGlmIHlvdSB0aGluayBpdCBpcyBhIHJlYWwgd29yZCwgcHJlc3MgImgiIGlmIHlvdSB0aGluayBpdCBpcyBub3QgYSByZWFsIHdvcmQ8L2Rpdj4nLAogIHN0aW11bHVzX2R1cmF0aW9uOiA1MDAKfTsKCmBgYAoKSW4gdGhlIGV4YW1wbGUgYWJvdmUsIHdlIGhhdmUgc2V0IHRoaXMgcGFyYW1ldGVyIHRvIGA1MDBgLCB3aGljaCBtZWFucyB0aGF0IHRoZSBzdGltdWx1cyB3aWxsIGJlIHNob3duIG9ubHkgZm9yIDUwMG1zLiBOb3RlIHRoYXQgdGhlIHRyaWFsIGRvZXMgbm90IGZpbmlzaCB1bnRpbCBhIHJlc3BvbnNlIGhhcyBiZWVuIG1hZGUuCgojIyMgdHJpYWxfZHVyYXRpb24KCklmIHdlIHdhbnQgdG8gbWFrZSB0aGUgdHJpYWwgZW5kIGFmdGVyIGEgc3BlY2lmaWVkIHRpbWUsIHdlIGNhbiBhbHNvIGFkZCB0aGUgYHRyaWFsX2R1cmF0aW9uYCBwYXJhbWV0ZXI6CgpgYGB7anN9CnZhciBteV9rZXlib2FyZF90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICd3b3JkJywKICBjaG9pY2VzOiBbJ2QnLCAnaCddLAogIHByb21wdDogJzxkaXYgc3R5bGU9ImZvbnQtc2l6ZToxMHB0OyBwb3NpdGlvbjpyZWxhdGl2ZTsgYm90dG9tOjE1MHB4OyI+cHJlc3MgImQiIGlmIHlvdSB0aGluayBpdCBpcyBhIHJlYWwgd29yZCwgcHJlc3MgImgiIGlmIHlvdSB0aGluayBpdCBpcyBub3QgYSByZWFsIHdvcmQ8L2Rpdj4nLAogIHN0aW11bHVzX2R1cmF0aW9uOiA1MDAsCiAgdHJpYWxfZHVyYXRpb246IDEwMDAKfTsKCmBgYAoKTm93LCB0aGUgdHJpYWwgd2lsbCBlbmQgYWZ0ZXIgYDEwMDBtc2Agb3IgaWYgdGhlcmUgaXMgYSByZXNwb25zZSBieSB0aGUgcGFydGljaXBhbnQgaW4gd2l0aGluIHRoZSBzcGVjaWZpZWQgdGltZS4KClRoaXMgaXMgaG93IGl0IHNob3VsZCBsb29rOgoKIVtdKGltYWdlcy93b3JkX3RyaWFsX2R1cmF0aW9uLmdpZikKCiMjIGNvbWJpbmluZyB0cmlhbHMgaW50byBhIHRpbWVsaW5lCgpBdCB0aGUgbW9tZW50IG91ciBleHBlcmltZW50IGNvbXByaXNlcyBvZiBqdXN0IG9uZSB0cmlhbC4uLiwgd2hpY2ggaXMgbm90IHBhcnRpY3VsYXJseSB1c2VmdWwuIEhvd2V2ZXIsIHdoYXQgd2UgaGF2ZSBjb3ZlcmVkIHNvIGZhciBzaG91bGQgYWxsb3cgdXMgdG8gcHV0IHRvZ2V0aGVyIHRoZSBidWlsZGluZyBibG9ja3Mgb2YgYW4gZXhwZXJpbWVudC4KCkluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIGNvbWJpbmUgbXVsdGlwbGUgdHJpYWxzIGludG8gYSB0aW1lbGluZSB0aGF0IGlzIG5lc3RlZCB3aXRoaW4gb3VyIG1haW4gdGltZWxpbmUuCgpUaGlzIHdpbGwgaW52b2x2ZSBhZGRpbmcgYSBuZXcgdmFyaWFibGUgdGhhdCBoYXMgYSBgZml4YXRpb24gdHJpYWxgLCBzbyB0aGF0IG91ciBsZXhpY2FsIGRlY2lzaW9uIHRyaWFsIGlzIHByZWNlZWRlZCBieSB0aGlzIGZpeGF0aW9uIHRyaWFsLgoKTGV0cyBmaXJzdCBtYWtlIHRoZSBsZXhpY2FsIGRlY2lzaW9uIHRyaWFsLCB3ZSB3aWxsIGtlZXAgaXQgYXMganVzdCBhIGtleWJvYXJkIHJlc3BvbnNlIHdpdGggbm8gZHVyYXRpb24gcGFyYW1ldGVyczoKCmBgYHtqc30KdmFyIGxleGljYWxfZGVjaXNpb25fdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiAnd29yZCcsCiAgY2hvaWNlczogWydkJywgJ2gnXSwKICBwcm9tcHQ6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MTBwdDsgcG9zaXRpb246cmVsYXRpdmU7IGJvdHRvbToxNTBweDsiPnByZXNzICJkIiBpZiB5b3UgdGhpbmsgaXQgaXMgYSByZWFsIHdvcmQsIHByZXNzICJoIiBpZiB5b3UgdGhpbmsgaXQgaXMgbm90IGEgcmVhbCB3b3JkPC9kaXY+Jwp9OwoKYGBgCgpOb3cgd2Ugd2lsbCBtYWtlIHRoZSBmaXhhdGlvbiB0cmlhbCwgd2hpY2ggd2lsbCBzaG93IGEgYCtgIHN0aW11bHVzIG9uIHRoZSBzY3JlZW4gZm9yIGEgc2V0IGFtb3VudCBvZiB0aW1lLCBpbiB0aGlzIGNhc2UgYDIwMDBtc2AuCgpOb3RlIHRoZSBgZm9udC1zaXplYCBpcyBzZXQgdG8gMzBwdCBhbmQgdGhlIGNob2ljZXMgaXMgYE5PX0tFWVNgLCB0aGlzIG1lYW4gd2Ugd2lsbCBqdXN0IHNlZSB0aGUgYCtgIG9uIHRoZSBzY3JlZW4gYW5kIHRoZXJlIHdpbGwgYmUgbm8ga2V5cyB0aGF0IGVuZCB0aGUgdHJpYWwsIGp1c3QgdGhlIHRpbWUgbGltaXQgb2YgYDIwMDBtc2A6CgpgYGB7anN9CnZhciBmaXhhdGlvbl90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MzBwdDsiPis8L2Rpdj4nLAogIGNob2ljZXM6ICdOT19LRVlTJywKICBwcm9tcHQ6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MTBwdDsgcG9zaXRpb246cmVsYXRpdmU7IGJvdHRvbToxNTBweDsiPjxici8+PC9kaXY+JywKICB0cmlhbF9kdXJhdGlvbjogMjAwMAp9OwoKYGBgCgpXZSBjYW4gbm93IG1ha2UgYSBuZXcgdmFyaWFibGUgY2FsbGVkIGBsZXhpY2FsX2RlY2lzaW9uX2NvbWJpbmVkYCwgdGhpcyB3aWxsIGNvbWJpbmUgdGhlIGBmaXhhdGlvbl90cmlhbGAgYW5kIHRoZSBgbGV4aWNhbF9kZWNpc2lvbl90cmlhbGAgaW4gYSBzcGVjaWZpZWQgb3JkZXIsIHdpdGhpbiBhIHNwZWNpZmllZCB0aW1lbGluZS4KCldlIGRvIHRoaXMgdXNpbmcgdGhlIGB0aW1lbGluZWAgcGFyYW1ldGVyLCB3aGljaCB3ZSBjYW4gc3BlY2lmeSB3aXRoaW4gYSBzdGFuZGFyZCBgdmFyYC4KCldlIGFzc2lnbiB0aGUgdGltZWxpbmUgYW4gYXJyYXksIGBbZml4YXRpb25fdHJpYWwsIGxleGljYWxfZGVjaXNpb25fdHJpYWxdYCwgd2hpY2ggbWVhbnMgdGhhdCB0aGUgdGltZWxpbmUgd2lsbCBmaXJzdCBzaG93IHRoZSBgZml4YXRpb25fdHJpYWxgLCB0aGVuIHRoZSBgbGV4aWNhbF9kZWNpc2lvbl90cmlhbGAuIElmIHdlIGNoYW5nZWQgdGhlIHBvc2l0aW9ucyBvZiB0aGUgaXRlbXMgaW4gdGhlIGFycmF5IHRoaXMgd2lsbCBjaGFuZ2UgdGhlIG9yZGVyIG9mIHRoZSBwcmVzZW50YXRpb24uCgpgYGB7anN9CnZhciBsZXhpY2FsX2RlY2lzaW9uX2NvbWJpbmVkID0gewogIHRpbWVsaW5lOiBbZml4YXRpb25fdHJpYWwsIGxleGljYWxfZGVjaXNpb25fdHJpYWxdCn07CgpgYGAKCklmIHdlIG5vdyBwdXNoIGBsZXhpY2FsX2RlY2lzaW9uX2NvbWJpbmVkYCB0byBvdXIgdGltZWxpbmUsIHVzaW5nIHRoZSBgdGltZWxpbmUucHVzaGAsIHdlIHNob3VsZCBzZWUgdGhhdCBpdCBwcmVzZW50cyB0aGUgdHdvIHRyaWFscyB0b2dldGhlci4KClRoaXMgaXMgaG93IHlvdXIgY29kZSBzaG91bGQgbG9vayBzbyBmYXI6CgpgYGB7anN9Ci8vIC0tLS0tLS0tCi8vIFRpdGxlOiBEZW1vIGV4cGVyaW1lbnQKLy8ganNQc3ljaCB2ZXJzaW9uOiA3LjMuMQovLyBkYXRlOiBbdG9kYXldCi8vIGF1dGhvcjogW3lvdXIgbmFtZV0KLy8tLS0tLS0tLS0tCgp2YXIganNQc3ljaCA9IGluaXRKc1BzeWNoKCk7Cgp2YXIgZml4YXRpb25fdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjMwcHQ7Ij4rPC9kaXY+JywKICBjaG9pY2VzOiAnTk9fS0VZUycsCiAgcHJvbXB0OiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjEwcHQ7IHBvc2l0aW9uOnJlbGF0aXZlOyBib3R0b206MTUwcHg7Ij48YnIvPjwvZGl2PicsCiAgdHJpYWxfZHVyYXRpb246IDIwMDAKfTsKCnZhciBsZXhpY2FsX2RlY2lzaW9uX3RyaWFsID0gewogIHR5cGU6IGpzUHN5Y2hIdG1sS2V5Ym9hcmRSZXNwb25zZSwKICBzdGltdWx1czogJ3dvcmQnLAogIGNob2ljZXM6IFsnZCcsICdoJ10sCiAgcHJvbXB0OiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjEwcHQ7IHBvc2l0aW9uOnJlbGF0aXZlOyBib3R0b206MTUwcHg7Ij5wcmVzcyAiZCIgaWYgeW91IHRoaW5rIGl0IGlzIGEgcmVhbCB3b3JkLCBwcmVzcyAiaCIgaWYgeW91IHRoaW5rIGl0IGlzIG5vdCBhIHJlYWwgd29yZDwvZGl2PicKfTsKCnZhciBsZXhpY2FsX2RlY2lzaW9uX2NvbWJpbmVkID0gewogIHRpbWVsaW5lOiBbZml4YXRpb25fdHJpYWwsIGxleGljYWxfZGVjaXNpb25fdHJpYWxdCn07Cgp2YXIgdGltZWxpbmUgPSBbXTsKdGltZWxpbmUucHVzaChsZXhpY2FsX2RlY2lzaW9uX2NvbWJpbmVkKTsKCmpzUHN5Y2gucnVuKHRpbWVsaW5lKTsKCmBgYAoKCiMgVGltZWxpbmUgdmFyaWFibGVzCgpUaGUgZXhwZXJpbWVudCBpcyBub3QgcmVhbGx5IGFuIGV4cGVyaW1lbnQgc28gZmFyLCBpdCBwcmVzZW50cyBqdXN0IG9uZSB3b3JkLi4uCgpTbyB3aGF0IHdlIHdpbGwgZm9jdXMgb24gbm93IGlzIHVzaW5nIGxhcmdlciBzdGltdWxpIHNldHMsIGFuZCB1c2luZyB0aGVtIHdpdGhpbiBvdXIgdGltZWxpbmUuCgpUaGlzIG1lYW5zIHdlIGNhbiBpdGVyYXRlIHRocm91Z2ggYSBsaXN0IG9mIHN0aW11bGksIHByZXNlbnRpbmcgdGhlbSB0byB0aGUgcGFydGljaXBhbnQgYW5kIGNvbGxlY3RpbmcgdGhlaXIgcmVzcG9uc2VzIGR1cmluZyBlYWNoIHRyaWFsLgoKIyMgaW50cm9kdWNpbmcgSlNPTgoKQW4gaW1wb3J0YW50IHRoaW5nIHRvIG5vdGUgYWJvdXQganNwc3ljaCBpcyB0aGF0IHlvdSBjYW4gbm90IGp1c3QgZ2l2ZSBpdCBhIGNzdiBvciBleGNlbCBmb3JtYXR0ZWQgZmlsZSBjb250YWluaW5nIHlvdXIgc3RpbXVsaS4gSW5zdGVhZCwganNwc3ljaCBhbmQgamF2YXNjcmlwdCBtb3JlIGdlbmVyYWxseSwgcHJlZmVycyB0byB1c2UgSlNPTiBmb3JtYXR0ZWQgZmlsZXMuCgpTbyBhbiBpbXBvcnRhbnQgY29uY2VwdCB0byBsZWFybiBpcyBob3cgdG8gd29yayB3aXRoIHRoaXMgdHlwZSBvZiBmaWxlLgoKSGVyZSBpcyBhIGZyYW1ld29yayBleGFtcGxlIG9mIHdoYXQgSlNPTiBmb3JtYXR0ZWQgZGF0YSBsb29rcyBsaWtlOgoKYGBge2pzfQp2YXIgc3RpbXVsaSA9IFsKICAgIHsidmFyaWFibGVfbmFtZTEiOiAiZmlyc3RfaXRlbSIsICJ2YXJpYWJsZV9uYW1lMiI6IjEifSwKICAgIHsidmFyaWFibGVfbmFtZTEiOiAic2Vjb25kX2l0ZW0iLCAidmFyaWFibGVfbmFtZTIiOiIyIn0sCiAgICB7InZhcmlhYmxlX25hbWUxIjogInRoaXJkX2l0ZW0iLCAidmFyaWFibGVfbmFtZTIiOiIzIn0sCiAgICB7InZhcmlhYmxlX25hbWUxIjogImZvdXJ0aF9pdGVtIiwgInZhcmlhYmxlX25hbWUyIjoiNCJ9Cl07CgpgYGAKCkFzIGEgY3N2IG9yIGV4Y2VsIGZpbGUgdGhpcyB3b3VsZCBsb29rIGxpa2UgdGhpczoKCmBgYHtyIHdhcm5pbmc9RkFMU0UsIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0KIyBsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShEVCkKCmBgYAoKYGBge3IgZWNobz1GQUxTRSwgZXZhbD1UUlVFLCB3YXJuaW5nPUZBTFNFfQpkYXRhdGFibGUoZGF0YS5mcmFtZSh2YXJpYWJsZV9uYW1lMSA9IGMoImZpcnN0X2l0ZW0iLCAic2Vjb25kX2l0ZW0iLCAidGhpcmRfaXRlbSIsICJmb3VydGhfaXRlbSIpLAogICAgICAgICAgICAgICAgIHZhcmlhYmxlX25hbWUyID0gYygxLCAyLCAzLCA0KSksIHJvd25hbWVzID0gRkFMU0UsIGZpbHRlciA9ICJub25lIiwgYXV0b0hpZGVOYXZpZ2F0aW9uID0gVFJVRSwgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ3QnKSkKCmBgYAoKSW4gdGhlIGBzdGltdWxpYCB2YXJpYWJsZSBhYm92ZSwgd2UgaGF2ZSBhIHZlcnkgc3BlY2lmaWMgd2F5IG9mIGNvZGluZyB0aGUgZGF0YToKCi0gZXZlcnl0aGluZyBpcyBzdG9yZWQgaW4gYW4gYGFycmF5YCwgaS5lLiB3ZSB1c2UgYFtgIGFuZCBgXWAgYXJvdW5kIHRoZSBpbmZvcm1hdGlvbiwgdGhpcyB3aWxsIGFsbG93IGpzcHN5Y2ggdG8gdW5kZXJzdGFuZCBldmVyeXRoaW5nIGluIGEgd2F5IHNpbWlsYXIgdG8gZWFjaCBpdGVtIGluIHRoZSBhcnJheSBiZWluZyBsaWtlIGEgcm93IGluIGEgY3N2IG9yIGV4Y2VsIGZpbGUKCi0gZWFjaCBgaXRlbWAgaW4gdGhlIGFycmF5IGlzIGVuY2xvc2VkIGluIGB7YCBhbmQgYH1gLCB0aGUgaW5mb3JtYXRpb24gd2l0aGluIGVhY2ggb2YgdGhlc2UgaXRlbXMgaXMgZXNzZW50aWFsbHkgbGlrZSBhIHJvdyBvZiBkYXRhLiBFYWNoIGl0ZW0gaXMgc2VwYXJhdGVkIGJ5IGEgYCxgCgotIHRoZSBzdHJ1Y3R1cmUgb2YgdGhlIGl0ZW1zIGlzIGAidmFyaWFibGVfbmFtZTEiOiJ2YWx1ZSJgLCB3aGljaCBtZWFucyB0aGUgdmFyaWFibGUgbmFtZSBpcyBzcGVjaWZpZWQgYXMgYSBzdHJpbmcsIGkuZS4gYCIgImAgZm9sbG93ZWQgYnkgYSBgOmAsIHdpdGggdGhlIGNvcnJlc3BvbmRpbmcgdmFsdWUgZm9sbG93aW5nLCBhbHNvIG5vcm1hbGx5IHNwZWNpZmllZCBhcyBhIHN0cmluZy4gVGhlIG5leHQgdmFyaWFibGUgZm9sbG93cyB0aGUgc2FtZSBzdHJ1Y3R1cmUsIHdpdGggbmV3IHZhcmlhYmxlcyBzZXBhcmF0ZWQgYnkgYSBgLGAKCi0gaW4gb3JkZXIgZm9yIGpzcHN5Y2ggdG8gcmVhZCB0aGUgaW5mb3JtYXRpb24gaW4gYSBKU09OIGZvcm1hdHRlZCBvYmplY3QsIHdlIG5lZWQgdG8gYXNzaWduIGl0IGFzIGEgYHZhcmAsIGUuZy4gYHZhciBzdGltdWxpID0gYAoKIyMgaW50cm9kdWNpbmcgdGltZWxpbmUgdmFyaWFibGVzCgpBcyBtZW50aW9uZWQgYmVmb3JlLCB3ZSBwcm9iYWJseSB3YW50IG91ciBleHBlcmltZW50IHRvIGl0ZXJhdGUgdGhyb3VnaCBhIGxpc3Qgb2Ygc3RpbXVsaSwgcHJlc2VudGluZyB0aG9zZSBzdGltdWxpIHdpdGhpbiBhIHRpbWVsaW5lLgoKVG8gZG8gdGhpcywgd2UgY2FuIHVzZSB0aGUgYHRpbWVsaW5lX3ZhcmlhYmxlc2AgcGFyYW1ldGVyLiBUaGlzIGlzIG5vcm1hbGx5IGRvbmUgaW4gYSB2YXIgdGhhdCBjb250YWlucyBhIHRpbWVsaW5lLCBlLmcuIGxpa2UgdGhlIGBsZXhpY2FsX2RlY2lzaW9uX2NvbWJpbmVkYCB2YXIgd2UgbWFkZSBwcmV2aW91c2x5LgoKRmlyc3QgbGV0cyBtYWtlIHNvbWUgZXhhbXBsZSBzdGltdWxpIHN1aXRhYmxlIGZvciBhIGxleGljYWwgZGVjaXNpb24gdGFzazoKCmBgYHtqc30KdmFyIHN0aW11bGkgPSBbCiAgICB7Iml0ZW0iOiAiaGVsbG8iLCAicmVhbF93b3JkIjoieWVzIiwgImNvcnJlY3Rfa2V5IjoiZCJ9LAogICAgeyJpdGVtIjogImh4bGxvIiwgInJlYWxfd29yZCI6Im5vIiwgImNvcnJlY3Rfa2V5IjoiaCJ9LAogICAgeyJpdGVtIjogIndvcmxkIiwgInJlYWxfd29yZCI6InllcyIsICJjb3JyZWN0X2tleSI6ImQifSwKICAgIHsiaXRlbSI6ICJ3eHJsZCIsICJyZWFsX3dvcmQiOiJubyIsICJjb3JyZWN0X2tleSI6ImgifQpdOwoKYGBgCgpIZXJlIHRoZSBzdGltdWxpIGhhdmUgdGhyZWUgdmFyaWFibGVzOgoKLSBgaXRlbWAgdGhlIHN0aW11bGkgdG8gYmUgcHJlc2VudGVkIGR1cmluZyB0aGUgdHJpYWwKLSBgcmVhbF93b3JkYCB3aGV0aGVyIHRoZSB3b3JkIGlzIGNvbnNpZGVyZWQgdG8gYmUgcmVhbCBvciBub3QKLSBgY29ycmVjdF9rZXlgIHRoZSBrZXkgdGhlIGV4cGVjdCB0aGUgcGFydGljaXBhbnQgdG8gcmVzcG9uZCB3aXRoLCB3aGVyZSBgZGAgaXMgYSByZWFsIHdvcmQgYW5kIGBoYCBpcyBhIG5vbi13b3JkCgpOb3cgd2UgY2FuIHVzZSB0aGlzIHZhciBmb3Igb3VyIGB0aW1lbGluZV92YXJpYWJsZXNgIHBhcmFtZXRlcjoKCmBgYHtqc30KdmFyIGxleGljYWxfZGVjaXNpb25fY29tYmluZWQgPSB7CiAgdGltZWxpbmU6IFtmaXhhdGlvbl90cmlhbCwgbGV4aWNhbF9kZWNpc2lvbl90cmlhbF0sCiAgdGltZWxpbmVfdmFyaWFibGVzOiBzdGltdWxpCn07CgpgYGAKClRoZSBuZXh0IHN0ZXAgaXMgdG8gdXBkYXRlIHRoZSBgc3RpbXVsaWAgcGFyYW1ldGVyIGluIHRoZSBgbGV4aWNhbF9kZWNpc2lvbl90cmlhbGAgdmFyOgoKYGBge2pzfQp2YXIgbGV4aWNhbF9kZWNpc2lvbl90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6IGpzUHN5Y2gudGltZWxpbmVWYXJpYWJsZSgiaXRlbSIpLAogIGNob2ljZXM6IFsnZCcsICdoJ10sCiAgcHJvbXB0OiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjEwcHQ7IHBvc2l0aW9uOnJlbGF0aXZlOyBib3R0b206MTUwcHg7Ij5wcmVzcyAiZCIgaWYgeW91IHRoaW5rIGl0IGlzIGEgcmVhbCB3b3JkLCBwcmVzcyAiaCIgaWYgeW91IHRoaW5rIGl0IGlzIG5vdCBhIHJlYWwgd29yZDwvZGl2PicKfTsKCmBgYAoKT25lIHRoaW5nIHlvdSBtaWdodCBoYXZlIG5vdGljZWQgaXMgdGhhdCB0aGUgc3RpbXVsaSBhcmUgcHJlc2VudGVkIGluIHRoZSBzYW1lIG9yZGVyIGFzIHRoZXkgYXJlIHNwZWNpZmllZCBpbiB0aGUgYHN0aW11bGlgIHRoaXMgaXMgYmVjYXVzZSBpZiB3ZSB3YW50IHRoZSBwcmVzZW50YXRpb24gb3JkZXIgdG8gYmUgcmFuZG9taXNlZCwgd2UgbmVlZCB0byBhZGQgYW4gZXh0cmEgcGFyYW1ldGVyIHRvIG91ciBgbGV4aWNhbF9kZWNpc2lvbl9jb21iaW5lZGAgdmFyLiBUaGlzIGlzIGByYW5kb21pemVfb3JkZXJgLCB3aGljaCB3ZSBjYW4gc2V0IHRvIGB0cnVlYDoKCmBgYHtqc30KdmFyIGxleGljYWxfZGVjaXNpb25fY29tYmluZWQgPSB7CiAgdGltZWxpbmU6IFtmaXhhdGlvbl90cmlhbCwgbGV4aWNhbF9kZWNpc2lvbl90cmlhbF0sCiAgdGltZWxpbmVfdmFyaWFibGVzOiBzdGltdWxpLAogIHJhbmRvbWl6ZV9vcmRlcjogdHJ1ZQp9OwoKYGBgCgpOb3cgd2UgaGF2ZSBhIHJhbmRvbWlzZWQgdmVyc2lvbiBvZiBvdXIgbGV4aWNhbCBkZWNpc2lvbiBleHBlcmltZW50LCB1c2luZyB0aGUgc3RpbXVsaSBzdG9yZWQgaW4gdGhlIGBzdGltdWxpYCB2YXIsIHdoZXJlIHdlIGl0ZXJhdGUgdGhyb3VnaCB0aGUgc3RpbXVsaSwgcHJlc2VudGluZyBhIGZpeGF0aW9uIGFuZCB0aGVuIHRoZSBsZXhpY2FsIGRlY2lzaW9uIHRyaWFsLgoKVGhpcyBpcyB3aGF0IGl0IHNob3VsZCBsb29rIGxpa2U6CgohW10oaW1hZ2VzL2xleGljYWxfZGVjaXNpb24uZ2lmKQoKCiMgVW5kZXJzdGFuZGluZyB0aGUgcmVzdWx0cwoKIyMgaW5zcGVjdGluZyB0aGUgZGF0YQoKTm93IHdlIGhhdmUgYSB3b3JraW5nIGxleGljYWwgZGVjaXNpb24gZXhwZXJpbWVudCwgaXQgbWlnaHQgYmUgd29ydGggbG9va2luZyBhdCB0aGUgcmVzdWx0cy4KCldlIGNhbiBkbyB0aGlzIGluIGNvZ25pdGlvbi5ydW4gYnkgbG9va2luZyBhdCB0aGUgKipib3R0b20gcmlnaHQgYm94Kio6CgohW10oaW1hZ2VzL2xleGljYWxfZGVjaXNpb25fZGF0YS5wbmcpCgpBcyB5b3UgY29tcGxldGUgdHJpYWxzIGluIHRoZSBleHBlcmltZW50LCBpdCB3aWxsIGFkZCBhIG5ldyByb3cgb2YgZGF0YSB0byB0aGlzIHNlY3Rpb24uIFNvIGV2ZXJ5IHRpbWUgeW91IGFkdmFuY2UgaW4gdGhlIGV4cGVyaW1lbnQsIHRoZSBkYXRhIGlzIHN0b3JlZCBhdXRvbWF0aWNhbGx5LgoKSXQgaXMgZWFzeSB0byBoYXZlIGEgcXVpY2sgbG9vayBhdCB0aGUgZGF0YSB1c2luZyB0aGlzIGJveCwgYnV0IHlvdSBjYW4gYWxzbyBgZG93bmxvYWRgIGl0IGFzIGEgY3N2IGZpbGUgdXNpbmcgdGhlIGBkb3dubG9hZGAgYnV0dG9uLiBXaGljaCBzaG91bGQgZ2l2ZSB5b3UgYSBmaWxlIGNhbGxlZCBgZGVtb19wcmV2aWV3LmNzdmAuCgpJZiB5b3Ugb3BlbiB0aGlzIGluIGV4Y2VsIG9yIFIgeW91IHNob3VsZCBzZWUgc29tZXRoaW5nIGxpa2UgdGhpczoKCiFbXShpbWFnZXMvbGV4aWNhbF9kZWNpc2lvbl9kYXRhX2Nzdi5wbmcpCgpUaGVyZSBzaG91bGQgYmUgMTggY29sdW1ucyBhbmQgOCByb3dzIG9mIGRhdGEsIHdlIGNhbiBxdWlja2x5IGluc3BlY3Qgd2hhdCB0aGVzZSBhcmU6CgpUaGUga2V5IHZhcmlhYmxlcyB0byBjb25zaWRlciBhcmU6CgotIGBydGAgdGhpcyBpcyB0aGUgcmVhY3Rpb24gdGltZSBtZWFzdXJlZCBpbiBtcwotIGBzdGltdWx1c2AgdGhpcyBpcyB0aGUgc3RpbXVsdXMgc2hvd24gdG8gdGhlIHBhcnRpY2lwYW50LCBpbmNsdWRpbmcgYW55IGh0bWwKLSBgcmVzcG9uc2VgIHRoaXMgaXMgdGhlIHJlY29yZGVkIHZhbHVlIGZvciB0aGUgdHJpYWwsIGluIHRoaXMgZXhhbXBsZSBpdCB3aWxsIGJlIHRoZSBrZXkgcHJlc3NlZAotIGB0cmlhbF90eXBlYCB0aGlzIGlzIHRoZSBwbHVnaW4gdXNlZAotIGB0cmlhbF9pbmRleGAgdGhpcyBpcyB0aGUgdHJpYWwgbnVtYmVyLCBpdCBpbmNyZWFzZXMgc2VxdWVudGlhbGx5IGFmdGVyIGVhY2ggdHJpYWwKLSBgdGltZV9lbGFwc2VkYCB0aGlzIGlzIHRoZSBkdXJhdGlvbiBvZiB0aGUgZXhwZXJpbWVudCwgcmVjb3JkZWQgYXQgdGhlIGVuZCBvZiBlYWNoIHRyaWFsLCBpdCBpcyBjdW11bGF0aXZlCgpBbGwgdGhlIG90aGVyIHZhcmlhYmxlcyBhcmUgcXVpdGUgdGVjaG5pY2FsOgotIGBpbnRlcm5hbF9ub2RlX2lkYCB0aGlzIGlzIGEgd2F5IHRvIGRpc3Rpbmd1aXNoIGJldHdlZW4gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBleHBlcmltZW50Ci0gYHJ1bl9pZGAgdGhpcyBpcyB0aGUgaWQgb2YgdGhlIGluZGl2aWR1YWwgYXR0ZW1wdCBhdCB0aGUgZXhwZXJpbWVudAotIGBjb25kaXRpb25gIHRoaXMgaXMgdGhlIGNvbmRpdGlvbiBhc3NpZ25lZCB0byB0aGUgcGFydGljaXBhbnQKLSBgcmVjb3JkZWRfYXRgIHRoaXMgaXMgdGhlIGRhdGUgYW5kIHRpbWUgb2YgdGhlIHJlY29yZGVkIGRhdGEKLSBgc291cmNlX2NvZGVfdmVyc2lvbmAgdGhpcyBpcyBwcm9iYWJseSBzb21ldGhpbmcgcmVsYXRlZCB0byBjb2duaXRpb24ucnVuCi0gYGlwYCB0aGlzIGlzIHRoZSBpcCBhZGRyZXNzIG9mIHRoZSBwYXJ0aWNpcGFudAotIGB1c2VyX2FnZW50YCB0aGlzIGNvbnRhaW5zIHRoZSBkZXRhaWxzIG9mIHRoZSBicm93c2VyIGJlaW5nIHVzZWQgYnkgdGhlIHBhcnRpY2lwYW50Ci0gYGRldmljZWAgdGhpcyBpcyB0aGUgbWFrZSBvZiB0aGUgY29tcHV0ZXIgYmVpbmcgdXNlZCBieSB0aGUgcGFydGljaXBhbnQKLSBgYnJvd3NlcmAgdGhpcyBpcyB0aGUgYnJvd3NlciBiZWluZyB1c2VkIGJ5IHRoZSBwYXJ0aWNpcGFudAotIGBicm93c2VyX3ZlcnNpb25gIHRoaXMgaXMgdGhlIHZlcnNpb24gb2YgdGhlIGJyb3dzZXIgYmVpbmcgdXNlZCBieSB0aGUgcGFydGljaXBhbnQKLSBgcGxhdGZvcm1gIHRoaXMgaXMgdGhlIG9wZXJhdGluZyBzeXN0ZW0gYmVpbmcgdXNlZCBieSB0aGUgcGFydGljaXBhbnQKLSBgcGxhdGZvcm1fdmVyc2lvbmAgdGhpcyBpcyB0aGUgdmVyc2lvbiBvZiB0aGUgb3BlcmF0aW5nIHN5c3RlbSBiZWluZyB1c2VkIGJ5IHRoZSBwYXJ0aWNpcGFudAoKSWYgd2UgbG9vayBhdCB0aGUgYHJ0YCBjb2x1bW4sIHdlIGNhbiBzZWUgdGhhdCB0aGVyZSBhcmUgdmFsdWVzIGZvciBvdXIgbGV4aWNhbCBkZWNpc2lvbiB0cmlhbHMuIEluIHRoZSBgc3RpbXVsdXNgIGNvbHVtbiB3ZSBjYW4gc2VlIHdoaWNoIGl0ZW0gd2FzIGJlaW5nIHJlc3BvbmRlZCB0bywgZS5nLiBgc3bEm3RlYCBoYWQgYSBydCBvZiBgNTcxYCBpbiB0aGUgYWJvdmUgc2NyZWVuc2hvdC4KCk5vdGUgdGhhdCBkYXRhIGZvciB0aGUgZml4YXRpb24gdHJpYWxzIHdpbGwgYWxzbyBiZSBzdG9yZWQgYXMgYSByb3csIGV2ZW4gdGhvdWdoIHdlIG1pZ2h0IG5vdCBhY3R1YWxseSBuZWVkIGl0IGZvciBhbmFseXNpcy4KCiMjIGFkZGluZyB2YXJpYWJsZXMgdG8gb3VyIHJlc3VsdHMKCldoYXQgd291bGQgYmUgaGVscGZ1bCBob3dldmVyLCBpcyB0byBhZGQgbmV3IGNvbHVtbnMgdG8gb3VyIGRhdGEsIHN1Y2ggYXMgdGhlIGFkZGl0aW9uYWwgb25lcyBpbiBvdXIgYHN0aW11bGlgIHZhci4KClRvIGRvIHRoaXMsIHdlIG5lZWQgdG8gYWRkIHNvbWUgZXh0cmEgcGFyYW1ldGVycyB0byBvdXIgYGxleGljYWxfZGVjaXNpb25fdHJpYWxgIHZhciwgc3BlY2lmaWNhbGx5IGEgYGRhdGFgIHBhcmFtZXRlcjoKCmBgYHtqc30KdmFyIGxleGljYWxfZGVjaXNpb25fdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiBqc1BzeWNoLnRpbWVsaW5lVmFyaWFibGUoIml0ZW0iKSwKICBjaG9pY2VzOiBbJ2QnLCAnaCddLAogIHByb21wdDogJzxkaXYgc3R5bGU9ImZvbnQtc2l6ZToxMHB0OyBwb3NpdGlvbjpyZWxhdGl2ZTsgYm90dG9tOjE1MHB4OyI+cHJlc3MgImQiIGlmIHlvdSB0aGluayBpdCBpcyBhIHJlYWwgd29yZCwgcHJlc3MgImgiIGlmIHlvdSB0aGluayBpdCBpcyBub3QgYSByZWFsIHdvcmQ8L2Rpdj4nLAogIGRhdGE6IHsKICAgIHRhc2s6ICdsZXhpY2FsX2RlY2lzaW9uJywKICAgIHJlYWxfd29yZDoganNQc3ljaC50aW1lbGluZVZhcmlhYmxlKCdyZWFsX3dvcmQnKSwKICAgIGNvcnJlY3Rfa2V5OiBqc1BzeWNoLnRpbWVsaW5lVmFyaWFibGUoJ2NvcnJlY3Rfa2V5JykKICAgIH0KfTsKCmBgYAoKVGhlIGZvcm1hdCBvZiB0aGlzIHBhcmFtZXRlciB0YWtlcyBhbiBvYmplY3QgZm9ybWF0LCBzbyB0aGUgaW5mb3JtYXRpb24gbmVlZHMgdG8gaGF2ZSBge2AgYW5kIGB9YCBhcm91bmQgdGhlIGluZm9ybWF0aW9uLgoKTm93IHdlIGNhbiBkZWZpbmUgdGhlIHZhcmlhYmxlcyB0byBiZSBzdG9yZWQgaW4gdGhlIGRhdGE6CgotIGB0YXNrYCB3aWxsIHN0b3JlIGp1c3QgYSBzdHJpbmcgb2YgYGxleGljYWxfZGVjaXNpb25gIGZvciBhbGwgb2YgdGhlc2UgdHJpYWxzCi0gYHJlYWxfd29yZGAgd2lsbCBzdG9yZSB0aGUgdmFsdWUgaW4gb3VyIHRpbWVsaW5lIHZhcmlhYmxlIHJlbGF0aW5nIHRvIHRoZSBgcmVhbF93b3JkYCB2YXJpYWJsZSwgc28gaW4gdGhpcyBleGFtcGxlIGl0IHdpbGwgYmUgZWl0aGVyIGB5ZXNgIG9yIGBub2AKLSBgY29ycmVjdF9rZXlgIHdpbGwgYWdhaW4gc3RvcmUgdGhlIHZhbHVlIGluIG91ciB0aW1lbGluZSB2YXJpYWJsZSwgYnV0IGZvciB0aGUgYGNvcnJlY3Rfa2V5YCB2YXJpYWJsZSwgc28gaW4gdGhpcyBleGFtcGxlIGVpdGhlciBgZGAgb3IgYGhgCgpUaGUgbmFtZXMgb2YgdGhlIHZhcmlhYmxlcyBpbiBvdXIgZGF0YSBvYmplY3QgY2FuIGJlIGFueXRoaW5nIHlvdSBjaG9vc2UsIGFzc3VtaW5nIHRoZXkgYXJlIHZhbGlkIG5hbWVzLCBpLmUuIG5vIHNwYWNlcyBldGMuIGlmIHlvdSBhcmUgd2FudGluZyB0byBzdG9yZSBzb21ldGhpbmcgZnJvbSB0aGUgdGltZWxpbmVfdmFyaWFibGVzIHZhciwgeW91IG11c3QgdXNlIGBqc1BzeWNoLnRpbWxpbmVWYXJpYWJsZSgpYCB3aXRoIHRoZSBuYW1lIG9mIHRoZSBzcGVjaWZpYyB2YXJpYWJsZSB5b3Ugd2FudCB0byByZWZlciB0byBpbnNpZGUgdGhlIGJyYWNrZXRzLCB3cml0dGVuIGFzIGEgc3RyaW5nLCBlLmcuIGAncmVhbF93b3JkJ2AuCgpOb3cgbGV0cyBhZGQgc29tZSBkYXRhIHRvIG91ciBmaXhhdGlvbiB0cmlhbCB0b28sIHNvIHdlIGNhbiBkaXN0aW5ndWlzaCBpdCBmcm9tIHRoZSBsZXhpY2FsIGRlY2lzaW9uIHRyaWFsIGluIHRoZSBgdGFza2AgdmFyaWFibGU6CgpgYGB7anN9CnZhciBmaXhhdGlvbl90cmlhbCA9IHsKICB0eXBlOiBqc1BzeWNoSHRtbEtleWJvYXJkUmVzcG9uc2UsCiAgc3RpbXVsdXM6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MzBwdDsiPis8L2Rpdj4nLAogIGNob2ljZXM6ICdOT19LRVlTJywKICBwcm9tcHQ6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MTBwdDsgcG9zaXRpb246cmVsYXRpdmU7IGJvdHRvbToxNTBweDsiPjxici8+PC9kaXY+JywKICB0cmlhbF9kdXJhdGlvbjogMjAwMCwKICBkYXRhOiB7CiAgICB0YXNrOiAnZml4YXRpb24nCiAgfQp9OwoKYGBgCgpOb3cgaWYgd2UgY29tcGxldGUgdGhlIGV4cGVyaW1lbnQgYW5kIGxvb2sgYXQgdGhlIGRhdGEsIHdlIHNob3VsZCBzZWUgKG5vdGU6IEkgaGF2ZSByZW1vdmVkIHNvbWUgY29sdW1ucyBmb3IgZWFzZSBvZiByZWFkaW5nIHRoZSBzY3JlZW5zaG90LCB0aGUgbmV3IHZhcmlhYmxlcyBzdG9yZWQgaW4gdGhlIGRhdGEgd2lsbCBiZSBhdCB0aGUgcmlnaHQgb2YgdGhlIGNzdiBhZnRlciBgcGxhdGZvcm1fdmVyc2lvbmApOgoKIVtdKGltYWdlcy9sZXhpY2FsX2RlY2lzaW9uX2RhdGFfY3N2XzEucG5nKQoKVGhlIGZ1bGwgY29kZSBzaG91bGQgYmU6CgpgYGB7anN9Ci8vIC0tLS0tLS0tCi8vIFRpdGxlOiBEZW1vIGV4cGVyaW1lbnQKLy8ganNQc3ljaCB2ZXJzaW9uOiA3LjMuMQovLyBkYXRlOiBbdG9kYXldCi8vIGF1dGhvcjogW3lvdXIgbmFtZV0KLy8tLS0tLS0tLS0tCgp2YXIganNQc3ljaCA9IGluaXRKc1BzeWNoKCk7Cgp2YXIgc3RpbXVsaSA9IFsKICAgIHsiaXRlbSI6ICJoZWxsbyIsICJyZWFsX3dvcmQiOiJ5ZXMiLCAiY29ycmVjdF9rZXkiOiJkIn0sCiAgICB7Iml0ZW0iOiAiaHhsbG8iLCAicmVhbF93b3JkIjoibm8iLCAiY29ycmVjdF9rZXkiOiJoIn0sCiAgICB7Iml0ZW0iOiAid29ybGQiLCAicmVhbF93b3JkIjoieWVzIiwgImNvcnJlY3Rfa2V5IjoiZCJ9LAogICAgeyJpdGVtIjogInd4cmxkIiwgInJlYWxfd29yZCI6Im5vIiwgImNvcnJlY3Rfa2V5IjoiaCJ9Cl07Cgp2YXIgZml4YXRpb25fdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjMwcHQ7Ij4rPC9kaXY+JywKICBjaG9pY2VzOiAnTk9fS0VZUycsCiAgcHJvbXB0OiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjEwcHQ7IHBvc2l0aW9uOnJlbGF0aXZlOyBib3R0b206MTUwcHg7Ij48YnIvPjwvZGl2PicsCiAgdHJpYWxfZHVyYXRpb246IDIwMDAsCiAgZGF0YTogewogICAgdGFzazogJ2ZpeGF0aW9uJwogIH0KfTsKCnZhciBsZXhpY2FsX2RlY2lzaW9uX3RyaWFsID0gewogIHR5cGU6IGpzUHN5Y2hIdG1sS2V5Ym9hcmRSZXNwb25zZSwKICBzdGltdWx1czoganNQc3ljaC50aW1lbGluZVZhcmlhYmxlKCJpdGVtIiksCiAgY2hvaWNlczogWydkJywgJ2gnXSwKICBwcm9tcHQ6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MTBwdDsgcG9zaXRpb246cmVsYXRpdmU7IGJvdHRvbToxNTBweDsiPnByZXNzICJkIiBpZiB5b3UgdGhpbmsgaXQgaXMgYSByZWFsIHdvcmQsIHByZXNzICJoIiBpZiB5b3UgdGhpbmsgaXQgaXMgbm90IGEgcmVhbCB3b3JkPC9kaXY+Jwp9OwoKdmFyIGxleGljYWxfZGVjaXNpb25fY29tYmluZWQgPSB7CiAgdGltZWxpbmU6IFtmaXhhdGlvbl90cmlhbCwgbGV4aWNhbF9kZWNpc2lvbl90cmlhbF0sCiAgdGltZWxpbmVfdmFyaWFibGVzOiBzdGltdWxpLAogIHJhbmRvbWl6ZV9vcmRlcjogdHJ1ZQp9OwoKdmFyIHRpbWVsaW5lID0gW107CnRpbWVsaW5lLnB1c2gobGV4aWNhbF9kZWNpc2lvbl9jb21iaW5lZCk7Cgpqc1BzeWNoLnJ1bih0aW1lbGluZSk7CgpgYGAKCiMgRm9ybWF0dGluZyBmaWxlcyBmcm9tIGNzdiB0byBqYXZhc2NyaXB0L0pTT04gaW4gUgoKT25lIGFubm95aW5nIHRoaW5nIGFib3V0IGFsbCB0aGlzLCBpcyB0aGUgaWRlYSBvZiBoYXZpbmcgdG8gcmVmb3JtYXQgYWxsIHlvdXIgc3RpbXVsaSB0byBhIGphdmFzY3JpcHQgdmFyaWFibGUgaW4gSlNPTiBmb3JtYXQsIHdoaWNoIG1heSBiZSBhbiB1bmZhbWlsaWFyIGZvcm1hdCBhbmQgbW9zdCByZXNlYXJjaGVycyB1c2UgZXhjZWwgb3IgUiB0byBwcmVwYXJlIHRoZWlyIHN0aW11bGkgZmlsZXMgaW4gY3N2IGZvcm1hdC4KClRoZXJlIGlzIGEgc29sdXRpb24gdG8gdGhpcyBwcm9ibGVtIGJ5IHVzaW5nIFIhCgpUaGVyZSBpcyBhIGdyZWF0IHBhY2thZ2UgY2FsbGVkIGBqc29ubGl0ZWAgd2hpY2ggaXMgcmVhbGx5IHVzZWZ1bCBmb3Igd29ya2luZyB3aXRoIGRhdGEgaW4ganNvbiBmb3JtYXQuIFRoaXMgbWVhbnMgd2UgY2FuIGVhc2lseSBmb3JtYXQgYSBjc3YgZmlsZSB0byBqc29uIGFuZCBzYXZlIGl0IGFzIGEgamF2YXNjcmlwdCB2YXIgd2l0aCBqdXN0IGEgZmV3IGxpbmVzIG9mIGNvZGUuCgpZb3UgY2FuIHJlYWQgbW9yZSBhYm91dCBqc29ubGl0ZSBbaGVyZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2pzb25saXRlL3ZpZ25ldHRlcy9qc29uLWFhcXVpY2tzdGFydC5odG1sKS4KClNvIG91ciB3b3JrZmxvdyB3aWxsIGJlIHRoaXM6CgoxLiBjcmVhdGUgYSBzdGltdWxpIGZpbGUgaW4gdGhlIHRyYWRpdGlvbmFsIHdheSwgZS5nLiB1c2luZyBleGNlbCBvciBvdGhlciBzcHJlYWRzaGVldCBzdHlsZSBzb2Z0d2FyZQoyLiBleHBvcnQgdGhhdCBmaWxlIHRvIC5jc3YgZm9ybWF0CjMuIGxvYWQgaW4gdXNlZnVsIHBhY2thZ2VzCjQuIGxvYWQgdGhhdCBmaWxlIGluIHRvIFIKNS4gY29udmVydCB0aGUgUiB2ZXJzaW9uIG9mIHRoZSBkYXRhIHRvIEpTT04gdXNpbmcgdGhlIHRvSlNPTiBmdW5jdGlvbgo2LiBhZGQgc29tZSB0ZXh0IHRvIG1ha2UgaXQgYSBqYXZhc2NyaXB0IG9iamVjdAo3LiBleHBvcnQgdGhhdCBmaWxlIHRvIC5qcyBmb3JtYXQKOC4gdXBsb2FkIGl0IGFzIGFuIGBleHRlcm5hbCBKUy9DU1MgZmlsZSBpbiBjb2duaXRpb24ucnVuCjkuIHVzZSB0aGUgdmFyIGFzIGEgdGltZWxpbmUgdmFyaWFibGUKCiMjIyAxLiBjcmVhdGUgYSBzdGltdWxpIGZpbGUKCk9wZW4gdXAgYSBzcHJlYWRzaGVldCBzb2Z0d2FyZSBhbmQgbWFrZSB5b3VyIHN0aW11bGkgZmlsZS4KCkluIHRoaXMgd29ya3NoZWV0IHdlIHdpbGwgY29udGludWUgd2l0aCB0aGUgbGV4aWNhbCBkZWNpc2lvbiBzdHlsZSBzdGltdWxpLCBpLmUuIHdvcmRzIGFuZCBub24td29yZHMuCgpUaGVyZSBzaG91bGQgYmUgMyBjb2x1bW5zOiBgaXRlbWAsIGByZWFsX3dvcmRgIGFuZCBgY29ycmVjdF9rZXlgClRoZXJlIHNob3VsZCBiZSAyMCByb3dzOiAxMCBvZiB3aGljaCBhcmUgcmVhbCB3b3JkcyBhbmQgdGhlIG90aGVyIDEwIGFyZSBub24td29yZHMKClRoZSBkYXRhIHNob3VsZCBsb29rIGxpa2UgdGhpczoKCmBgYHtyIGVjaG89RkFMU0UsIGV2YWw9VFJVRSwgd2FybmluZz1GQUxTRX0KZGF0YXRhYmxlKGRhdGEuZnJhbWUoaXRlbSA9IGMoImZveCIsICJjaGVlc2UiLCAic2hlZXAiLCAiYmFuYW5hIiwgImNoYWlyIiwgInBlYSIsICJoZWFydCIsICJwdW1wa2luIiwgImN1Y3VtYmVyIiwgImhpcHBvIiwgImZ6eCIsICJjaGVlc3giLCAic2h4eHAiLCAiYmFueG5hIiwgImNoeGlyIiwgInB4YSIsICJoZXhydCIsICJwdW1wa3huIiwgImN4Y3VtYmVyIiwgImh4cHBvIiksCiAgICAgICAgICAgICAgICAgcmVhbF93b3JkID0gYyhyZXAoInllcyIsIDEwKSwgcmVwKCJubyIsIDEwKSksCiAgICAgICAgICAgICAgICAgY29ycmVjdF9rZXkgPSBjKHJlcCgiZCIsIDEwKSwgcmVwKCJoIiwgMTApKSksIHJvd25hbWVzID0gRkFMU0UsIGZpbHRlciA9ICJub25lIiwgYXV0b0hpZGVOYXZpZ2F0aW9uID0gVFJVRSwgb3B0aW9ucyA9IGxpc3QoZG9tID0gJ3QnLCBwYWdlTGVuZ3RoID0gMjApKQoKYGBgCgo8IS0tICFbXShpbWFnZXMvc3RpbXVsaV9jc3YucG5nKSAtLT4KCiMjIyAyLiBleHBvcnQgdG8gY3N2CgpJZiB5b3UgYXJlIHdvcmtpbmcgd2l0aCB5b3VyIG93biBmaWxlLCBleHBvcnQgaXQgYXMgYSAuY3N2IGZpbGUsIHByZWZlcmFibHkgd2l0aCB1dGYtOCBlbmNvZGluZy4KClRoZSBmaWxlbmFtZSBzaG91bGQgYmUgYGxleGljYWxfZGVjaXNpb25fc3RpbXVsaS5jc3ZgCgpZb3UgY2FuIGRvIHRoaXMgaW4gTWljcm9zb2Z0IEV4Y2VsIGJ5IGZvbGxvd2luZyB0aGlzIHJlY29yZGluZzoKCiFbXShpbWFnZXMvbGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpX2V4Y2VsLmdpZikKCkFsdGVybmF0aXZlbHksIHlvdSBjYW4gZG93bmxvYWQgdGhlIGZpbGU6CgpgciBmYSgiZG93bmxvYWQiKWBbbGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpLmNzdl0oaHR0cHM6Ly9naXRodWIuY29tL2phbWVzYnJhbmRzY2llbmNlL2pzcHN5Y2gtdHV0b3JpYWwvcmF3L21haW4vbGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpLmNzdikuCgojIyMgMy4gbG9hZCBpbiBwYWNrYWdlcwoKT3BlbiB1cCBSU3R1ZGlvIG9uIHlvdXIgY29tcHV0ZXIgYW5kIG9wZW4gYSBuZXcgUiBzY3JpcHQuCgpZb3Ugd2lsbCBuZWVkIHRoZSBganNvbmxpdGVgIHRoZSBgdGlkeXZlcnNlYCBwYWNrYWdlcyBpbnN0YWxsZWQgYW5kIGxvYWRlZCBpbiB0byBSLgoKSWYgeW91IGhhdmUgbm90IGdvdCBhbnkgb2YgdGhlc2UgaW5zdGFsbGVkLCBmaXJzdCBydW4gdGhpcyBjb2RlOgoKYGBge3IgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQppbnN0YWxsLnBhY2thZ2VzKCJqc29ubGl0ZSIpCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCgpgYGAKCk9uY2UgeW91IGhhdmUgdGhlbSBpbnN0YWxsZWQsIHlvdSBzaG91bGQgbm93IGxvYWQgdGhlbSBpbiB0byBSOgoKYGBge3IgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQpsaWJyYXJ5KGpzb25saXRlKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmBgYAoKIyMjIDQuIGxvYWQgaW4gdG8gUgoKV2Ugd2lsbCBuZWVkIHRvIGxvYWQgaW4gdGhlIGNzdiBmaWxlIGluIHRvIFIgYXMgYW4gb2JqZWN0LiBUbyBkbyB0aGlzLCB3ZSBuZWVkIHRvIHRlbGwgUiB3aGVyZSB0aGUgZmlsZSBpcyAtIHRoZSBgd29ya2luZyBkaXJlY3RvcnlgIG9yIGZvbGRlciBjb250YWluaW5nIHRoZSBmaWxlLgoKSWYgeW91IGFyZSBub3QgZmFtaWxpYXIgd2l0aCBSLCB5b3UgY2FuIHNldCB0aGUgd29ya2luZyBkaXJlY3RvcnkgYnkgY2xpY2tpbmcgb24gdGhlIG9wdGlvbnMgaW4gdGhpcyBzY3JlZW5zaG90LCB0aGVuIGNob29zZSB0aGUgZm9sZGVyIHdoZXJlIHlvdXIgY3N2IGZpbGUgaXMgc2F2ZWQ6CgohW10oaW1hZ2VzL3dvcmtpbmdfZGlyZWN0b3J5LnBuZykKCk5vdyBzdG9yZSB0aGUgZmlsZSBhcyBhbiBvYmplY3QgY2FsbGVkIGBsZXhpY2FsX2RlY2lzaW9uX3N0aW11bGlgLgoKYGBge3IgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQpsZXhpY2FsX2RlY2lzaW9uX3N0aW11bGkgPC0gcmVhZF9kZWxpbSgibGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpLmNzdiIsIGRlbGltID0gIjsiKQoKYGBgCgojIyMgNS4gY29udmVydCB0byBKU09OCgpXZSB3aWxsIG5vdyBiZSBhYmxlIHRvIHVzZSB0aGUgYHRvSlNPTmAgZnVuY3Rpb24uCgpXZSB3aWxsIGFzc2lnbiB0aGUganNvbiB2ZXJzaW9uIHRvIGFuIG9iamVjdCBjYWxsZWQgYGxleGljYWxfZGVjaXNpb25fc3RpbXVsaV9KU09OYC4KClRoZSBgdG9KU09OYCBmdW5jdGlvbiB0YWtlcyBhcyB0aGUgZmlyc3QgYXJndW1lbnQgeW91ciBkYXRhLCBzbyBpbiB0aGlzIGNhc2UgYGxleGljYWxfZGVjaXNpb25fc3RpbXVsaWAuCgpXZSB1c2UgdGhlIGFkZGl0aW9uYWwgYXJndW1lbnQgYHByZXR0eT1UUlVFYCB0byBtYWtlIHRoZSBvdXRwdXQgbG9vayBwcmV0dHkgYW5kIGVhc3kgdG8gcmVhZC4KCmBgYHtyIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0KbGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpX0pTT04gPC0gdG9KU09OKGxleGljYWxfZGVjaXNpb25fc3RpbXVsaSwgcHJldHR5PVRSVUUpCgpsZXhpY2FsX2RlY2lzaW9uX3N0aW11bGlfSlNPTgoKYGBgCgpXZSBub3cgaGF2ZSBKU09OIGZvcm1hdHRlZCBkYXRhIQoKIyMjIDYuIG1ha2UgaW50byBqYXZhc2NyaXB0IHZhcgoKWW91IHdpbGwgc2VlIHRoZXJlIGlzIG5vIGB2YXJgIGFzc2lnbm1lbnQgaW4gdGhlIGBsZXhpY2FsX2RlY2lzaW9uX3N0aW11bGlfSlNPTmAgb2JqZWN0LCBpdCBpcyBqdXN0IHRleHQuCgpCdXQgd2UgY2FuIGFkZCB0aGlzIGluZm9ybWF0aW9uIHF1aXRlIGVhc2lseSB1c2luZyB0aGUgYHBhc3RlMGAgZnVuY3Rpb246CgpgYGB7ciBldmFsPUZBTFNFLCBlY2hvPVRSVUV9CmxleGljYWxfZGVjaXNpb25fc3RpbXVsaV9KU09OIDwtIHBhc3RlMCgidmFyIHN0aW11bGkgPSAiLCBsZXhpY2FsX2RlY2lzaW9uX3N0aW11bGlfSlNPTiwgIjsiKQoKbGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpX0pTT04KCmBgYAoKV2Ugc2hvdWxkIG5vdyBzZWUgdGhhdCB0aGUgc3RhcnQgb2YgdGhlIHRleHQgaGFzIGB2YXIgc3RpbXVsaSA9IGAsIGZvbGxvd2VkIGJ5IHRoZSBKU09OIGZvcm1hdHRlZCBkYXRhLCBhbmQgZmluYWxseSB0aGUgdGV4dCBlbmRzIHdpdGggYSBgO2AuCgojIyMgNy4gZXhwb3J0IHRoYXQgZmlsZSB0byBqYXZhc2NyaXB0CgpOb3cgd2UgaGF2ZSB0aGUgZGF0YSBmb3JtYXR0ZWQgY29ycmVjdGx5LCB3ZSBjYW4gc2F2ZSB0aGUgZmlsZSB1c2luZyB0aGUgYHdyaXRlX2ZpbGVgIGZ1bmN0aW9uLCBub3RlIHRoYXQgeW91IHNob3VsZCBzcGVjaWZ5IHRoZSBleGFjdCBmaWxlIHBhdGggb2Ygd2hlcmUgeW91IHdhbnQgdGhlIGBmaWxlYCB0byBiZSBzYXZlZDoKCmBgYHtyIGV2YWw9RkFMU0UsIGVjaG89VFJVRX0Kd3JpdGVfZmlsZSh4ID0gbGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpX0pTT04sIGZpbGUgPSAibGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpX0pTT04uanMiKQoKYGBgCgpZb3Ugc2hvdWxkIG5vdyBzZWUgdGhlIGZpbGUgaW4geW91ciB3b3JraW5nIGRpcmVjdG9yeSwgaW1wb3J0YW50bHkgdGhpcyBpcyBhIGBqc2Agb3IgamF2YXNjcmlwdCBmaWxlLgoKWW91IGNhbiBkb3dubG9hZCB0aGlzIGZpbGUKCmByIGZhKCJkb3dubG9hZCIpYCBbaGVyZV0obGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpX0pTT04uanMpLCBpbiBjYXNlIHlvdSBoYWQgcHJvYmxlbXMgd2l0aCBydW5uaW5nIHRoZSBSIGNvZGUuCgpJdCBzaG91bGQgbG9vayBsaWtlIHRoaXM6CgohW10oaW1hZ2VzL2xleGljYWxfZGVjaXNpb25fc3RpbXVsaV9KU09OLnBuZykKCkhlcmUgaXMgdGhlIGZ1bGwgUiBjb2RlIGluIGEgc2xpZ2h0bHkgbW9yZSBlZmZpY2llbnQgd2F5OgoKYGBge3IgZXZhbD1GQUxTRSwgZWNobz1UUlVFfQppbnN0YWxsLnBhY2thZ2VzKCJqc29ubGl0ZSIpCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpCgpsaWJyYXJ5KGpzb25saXRlKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCnBhc3RlMCgidmFyIHN0aW11bGkgPSAiLAogICAgICAgdG9KU09OKHJlYWRfZGVsaW0oImxleGljYWxfZGVjaXNpb25fc3RpbXVsaS5jc3YiLCBkZWxpbSA9ICI7IiksCiAgICAgICAgICAgICAgcHJldHR5PVRSVUUpLAogICAgICAgIjsiKSAlPiUKICB3cml0ZV9maWxlKGZpbGUgPSAibGV4aWNhbF9kZWNpc2lvbl9zdGltdWxpX0pTT04uanMiKQoKYGBgCgojIyMgOC4gdXBsb2FkIHRoYXQgZmlsZSB0byBjb2duaXRpb24ucnVuCgpZb3Ugd2lsbCBzZWUgaW4gY29nbml0aW9uLnJ1biB0aGF0IHRoZXJlIGlzIGFuIG9wdGlvbiB0byB1cGxvYWQgYGV4dGVybmFsIEpTL0NTU2AsIHRoaXMgaXMgaG93IHdlIHdpbGwgbm93IHVwbG9hZCBvdXIgc3RpbXVsaSBmaWxlIHRvIHRoZSBleHBlcmltZW50LgoKIVtdKGltYWdlcy9jb2duaXRpb25fanNfdXBsb2FkLnBuZykKCkNsaWNrIG9uIHRoZSBgYnJvd3NlYCBidXR0b24gYW5kIGZpbmQgdGhlIGBsZXhpY2FsX2RlY2lzaW9uX3N0aW11bGlfSlNPTi5qc2AgZmlsZS4KCk9uY2UgaXQgaGFzIGJlZW4gdXBsb2FkZWQsIHlvdSBzaG91bGQgc2VlIGl0IGFwcGVhciB1bmRlcm5lYXRoIHRoZSBicm93c2UgYnV0dG9uLgoKIyMjIDkuIHVzaW5nIHRoZSBzdGltdWxpIGFzIGEgdGltZWxpbmVfdmFyaWFibGUKCk5vdGljZSB0aGF0IHdlIHByb2JhYmx5IHN0aWxsIGhhdmUgdGhlIGZvbGxvd2luZyBjb2RlIGluIG91ciBzY3JpcHQ6CgpgYGB7anN9CnZhciBzdGltdWxpID0gWwogICAgeyJpdGVtIjogImhlbGxvIiwgInJlYWxfd29yZCI6InllcyIsICJjb3JyZWN0X2tleSI6ImQifSwKICAgIHsiaXRlbSI6ICJoeGxsbyIsICJyZWFsX3dvcmQiOiJubyIsICJjb3JyZWN0X2tleSI6ImgifSwKICAgIHsiaXRlbSI6ICJ3b3JsZCIsICJyZWFsX3dvcmQiOiJ5ZXMiLCAiY29ycmVjdF9rZXkiOiJkIn0sCiAgICB7Iml0ZW0iOiAid3hybGQiLCAicmVhbF93b3JkIjoibm8iLCAiY29ycmVjdF9rZXkiOiJoIn0KXTsKCmBgYAoKVGhpcyBtZWFucyB0aGF0IG91ciBuZXcgc3RpbXVsaSB3aWxsIGJlIG92ZXJ3cml0dGVuIGJ5IHRoaXMgc2VjdGlvbiBvZiB0aGUgY29kZS4gVGhpcyBpcyBiZWNhdXNlIHdlIG5hbWVkIG91ciBuZXcgc3RpbXVsaSB2YXIgYHN0aW11bGlgLgoKVG8gcmVzb2x2ZSB0aGlzIGlzc3VlLCBhbmQgdXNlIG91ciBuZXcgc3RpbXVsaSwgd2UgbmVlZCB0byAqKmRlbGV0ZSoqIHRoZSBhYm92ZSBjb2RlIGluIG91ciBzY3JpcHQuCgpUaGlzIG1lYW5zIG91ciBmaW5hbCBjb2RlIHNob3VsZCBiZToKCmBgYHtqc30KLy8gLS0tLS0tLS0KLy8gVGl0bGU6IERlbW8gZXhwZXJpbWVudAovLyBqc1BzeWNoIHZlcnNpb246IDcuMy4xCi8vIGRhdGU6IFt0b2RheV0KLy8gYXV0aG9yOiBbeW91ciBuYW1lXQovLy0tLS0tLS0tLS0KCi8vIGluaXRpdGF0ZSBqc3BzeWNoCnZhciBqc1BzeWNoID0gaW5pdEpzUHN5Y2goKTsKCi8vIG1ha2UgYSBmaXhhdGlvbiB0cmlhbAp2YXIgZml4YXRpb25fdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjMwcHQ7Ij4rPC9kaXY+JywKICBjaG9pY2VzOiAnTk9fS0VZUycsCiAgcHJvbXB0OiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjEwcHQ7IHBvc2l0aW9uOnJlbGF0aXZlOyBib3R0b206MTUwcHg7Ij48YnIvPjwvZGl2PicsCiAgdHJpYWxfZHVyYXRpb246IDIwMDAsCiAgZGF0YTogewogICAgdGFzazogJ2ZpeGF0aW9uJwogIH0KfTsKCi8vIG1ha2UgYSBsZXhpY2FsIGRlY2lzaW9uIHRyaWFsCnZhciBsZXhpY2FsX2RlY2lzaW9uX3RyaWFsID0gewogIHR5cGU6IGpzUHN5Y2hIdG1sS2V5Ym9hcmRSZXNwb25zZSwKICBzdGltdWx1czoganNQc3ljaC50aW1lbGluZVZhcmlhYmxlKCJpdGVtIiksCiAgY2hvaWNlczogWydkJywgJ2gnXSwKICBwcm9tcHQ6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MTBwdDsgcG9zaXRpb246cmVsYXRpdmU7IGJvdHRvbToxNTBweDsiPnByZXNzICJkIiBpZiB5b3UgdGhpbmsgaXQgaXMgYSByZWFsIHdvcmQsIHByZXNzICJoIiBpZiB5b3UgdGhpbmsgaXQgaXMgbm90IGEgcmVhbCB3b3JkPC9kaXY+JywKICBkYXRhOiB7CiAgICB0YXNrOiAnbGV4aWNhbF9kZWNpc2lvbicsCiAgICByZWFsX3dvcmQ6IGpzUHN5Y2gudGltZWxpbmVWYXJpYWJsZSgncmVhbF93b3JkJyksCiAgICBjb3JyZWN0X2tleToganNQc3ljaC50aW1lbGluZVZhcmlhYmxlKCdjb3JyZWN0X2tleScpCiAgICB9Cn07CgovLyBtYWtlIGEgdmFyaWFibGUgd2hpY2ggY29tYmluZXMgdGhlIGZpeGF0aW9uIGFuZCBsZXhpY2FsIGRlY2lzaW9uIHRvZ2V0aGVyCi8vIHVzZSBzdGltdWxpIGFzIHRpbWVsaW5lX3ZhcmlhYmxlcyBhbmQgcmFuZG9taXNlIHRoZSBwcmVzZW50YXRpb24gb3JkZXIKdmFyIGxleGljYWxfZGVjaXNpb25fY29tYmluZWQgPSB7CiAgdGltZWxpbmU6IFtmaXhhdGlvbl90cmlhbCwgbGV4aWNhbF9kZWNpc2lvbl90cmlhbF0sCiAgdGltZWxpbmVfdmFyaWFibGVzOiBzdGltdWxpLAogIHJhbmRvbWl6ZV9vcmRlcjogdHJ1ZQp9OwoKLy8gc2V0IHVwIG1haW4gdGltZWxpbmUKdmFyIHRpbWVsaW5lID0gW107CgovLyBwdXNoIHRoZSBsZXhpY2FsX2RlY2lzaW9uX2NvbWJpbmVkIHZhcmlhYmxlIHRvIHRoZSB0aW1lbGluZQp0aW1lbGluZS5wdXNoKGxleGljYWxfZGVjaXNpb25fY29tYmluZWQpOwoKLy9ydW4gdGhlIGV4cGVyaW1lbnQKanNQc3ljaC5ydW4odGltZWxpbmUpOwoKCmBgYAoKCgoKYGBge3IgZWNobz1GQUxTRSwgZXZhbD1UUlVFLCB3YXJuaW5nPUZBTFNFfQpodG1sdG9vbHM6OnRhZ3Mkc2NyaXB0KHNyYyA9ICJqcy90cmFuc2xhdGUuanMiKQojIGh0bWx0b29sczo6dGFncyRzY3JpcHQoc3JjID0gImpzL2luZm9ib3guanMiKQpodG1sdG9vbHM6OnRhZ3Mkc2NyaXB0KHNyYz0iLy90cmFuc2xhdGUuZ29vZ2xlLmNvbS90cmFuc2xhdGVfYS9lbGVtZW50LmpzP2NiPWdvb2dsZVRyYW5zbGF0ZUVsZW1lbnRJbml0IikKCmh0bWx0b29sczo6dGFnTGlzdCgKICB4YXJpbmdhbkV4dHJhOjp1c2VfY2xpcGJvYXJkKAogICAgYnV0dG9uX3RleHQgPSAiPGkgY2xhc3M9XCJmYSBmYS1jbGlwYm9hcmRcIiBzdHlsZT1cImZvbnQtc2l6ZTogMjVweFwiPjwvaT4iLAogICAgc3VjY2Vzc190ZXh0ID0gIjxpIGNsYXNzPVwiZmEgZmEtY2hlY2tcIiBzdHlsZT1cImNvbG9yOiAjOTBCRTZEOyBmb250LXNpemU6IDI1cHhcIj48L2k+IiwKICApLAogIHJtYXJrZG93bjo6aHRtbF9kZXBlbmRlbmN5X2ZvbnRfYXdlc29tZSgpCikKCmBgYAoKYGBge2pzIGVjaG89RkFMU0UsIGV2YWw9VFJVRX0KdmFyIGNvbGwgPSBkb2N1bWVudC5nZXRFbGVtZW50c0J5Q2xhc3NOYW1lKCJjb2xsYXBzaWJsZTEiKTsKdmFyIGk7Cgpmb3IgKGkgPSAwOyBpIDwgY29sbC5sZW5ndGg7IGkrKykgewogIGNvbGxbaV0uYWRkRXZlbnRMaXN0ZW5lcigiY2xpY2siLCBmdW5jdGlvbigpIHsKICAgIHRoaXMuY2xhc3NMaXN0LnRvZ2dsZSgiYWN0aXZlMSIpOwogICAgdmFyIGNvbnRlbnQgPSB0aGlzLm5leHRFbGVtZW50U2libGluZzsKICAgIGlmIChjb250ZW50LnN0eWxlLm1heEhlaWdodCl7CiAgICAgIGNvbnRlbnQuc3R5bGUubWF4SGVpZ2h0ID0gbnVsbDsKICAgIH0gZWxzZSB7CiAgICAgIGNvbnRlbnQuc3R5bGUubWF4SGVpZ2h0ID0gY29udGVudC5zY3JvbGxIZWlnaHQgKyAicHgiOwogICAgfQogIH0pOwp9CgpgYGAK