Using jsPsych and cognition.run

Session 3 - intermediate/advanced


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:

  • Running in full screen mode
  • Presenting instructions
  • Using consent forms
  • Demographic questions
  • Feedback forms
  • Distributing links

Recap

In the last session we should have:

  • built a lexical decision experiment
  • learnt some basic css and html
  • used plugins
  • worked with timeline variables
  • created json stimuli files using R

Making a complete experiment

In the previous worksheet we were able to set up a working lexical decision experiment. However, this experiment only presented the lexical decision trials, with nothing else presented. This is probably not how we would want to run the experiment though. Instead, what would be good is to have various other parts to our experiment, so that we can ensure that the participant knows what to do.

In this section we will focus on adding in these parts.

Running in full screen mode

For online experiments, it is sensible to ensure that the participant has as few distractions whilst completing the experiment. One useful approach is to make the experiment run in full screen mode.

To do this we can use the fullscreen plugin.

https://www.jspsych.org/7.3/plugins/fullscreen/

This means we can present a message on the screen and a button. The message will probably say something to the participant and when they click on the button, the experiment will enter full screen mode.

var enter_fullscreen = {
  type: jsPsychFullscreen,
  fullscreen_mode: true,
  message: '<p>some message</p>',
  button_label: 'enter full screen mode'
};

We will store the trial as a var called enter_fullscreen.

In the example above there are 4 different parameters:

type - this is where we specify the jsPsychFullscreen plugin

fullscreen_mode - this is true as we want the experiment to be in full screen mode when the button is clicked. If this is set to false, it will exit full screen mode when the button is clicked. You could use the false version if you want to let participants exit full screen mode manually, maybe at the end of the experiment

message - this is the message that will appear above the button on the screen. We can use this to inform the participant that when they click the button the experiment will enter full screen. You can say whatever you want here. Note the use of <p> at the start and </p> at the end. This is a html tag to say you want the text to be a paragraph. If you remove it, there will be no line break between the text and the button. You can use as many paragraph tags as you want, e.g. if you had '<p>This is the first paragraph.</p><p>This is another paragraph.</p>.

button_label - this is the text shown in the button. It can again be anything you want.

Don’t forget, if we want to show the trial to the participant, we will have to push it to our timeline.

timeline.push(enter_fullscreen);

Instructions page

To make sure that the participants understand what the basic outline of the experiment is, we need to present the participants with a general instruction page.

To do this we can use the instructions plugin.

https://www.jspsych.org/7.3/plugins/instructions/

This means we can present a message on the screen and a button.

var instructions_page = {
    type: jsPsychInstructions,
    pages: [
    'Welcome to the experiment. Click next or press the Enter key to begin.',
    'In this experiment you will...'
    ],
    show_clickable_nav: true,
    key_forward: 'Enter',
    key_backward: 'ArrowLeft,
    allow_backward: true,
    button_label_next: 'next',
    button_label_previous: 'back'
};

We will store the trial as a var called instructions_page.

In the example above there are 7 different parameters:

type - this is where we specify the jsPsychInstructions plugin

pages - this takes an array, i.e. you use [ and ], with the relevant information contained within the array.

The instructions are written as a string, e.g. ‘Welcome to the experiment. Click next or press the Enter key to begin.’.If you want to show multiple instructions pages, you can separate them using a comma, e.g pages: ['instructions page 1', 'instructions page 2'].

The strings can be written simply without any formatting, or you can use html and css.

show_clickable_nav - this makes the buttons visible, e.g. you can see the ‘next’ and ‘back’ buttons underneath the instructions. If this is false, the buttons will not be shown

key_forward - this is the keyboard key that you specify if you want to allow the participant to press a key, as well as the button, to go to the next instructions page

key_backward - this is the keyboard key that you specify if you want to allow the participant to press a key, as well as the button, to go back to the last instructions page

allow_backward - this makes the back button/response active, so that the participant can go back to the previous instructions page. If this is false, the button/response to go back will not work

button_label_next - this is the text in the button to go to the next instructions page or finish the instructions. It is a string, but can also take html and css

button_label_previous - this is the text in the button to go to the previous instructions page. It is a string, but can also take html and css

Don’t forget, if we want to show the trial to the participant, we will have to push it to our timeline.

timeline.push(instructions_page)

If you want to make the instructions page look a bit more fancy, we can use html and css for our instructions. For example, if you want to add a university/lab logo you can use html.

In the example below, we use an img html tag, to show an image from a link available on the internet.

Specifically https://sites2.ff.cuni.cz/ssol/wp-content/uploads/sites/103/2023/06/cropped-navrh_wider.png

We specify the source of the image, or where it is located using src, followed by a = then the link in quotations/". If you want to use a file that you have uploaded to cognition.run, make sure you upload it to the stimuli section and then just specify the file name as the src, e.g. '<img src="image.png">'

pages: ['<img src="https://sites2.ff.cuni.cz/ssol/wp-content/uploads/sites/103/2023/06/cropped-navrh_wider.png">']

You might notice that the image is not particularly a useful size. This is because we have to specify the size if we want it to be different from the original.

We can do this with width and height arguments, in the example below we specify the width as 400 and the height as 140, this is in pixels. You can just specify the width if you want to keep the aspect ratio the same as the original:

pages: ['<img src="https://sites2.ff.cuni.cz/ssol/wp-content/uploads/sites/103/2023/06/cropped-navrh_wider.png" width="400" height="140">']

Finally, we can add a title to the page and the instructions themselves. This is all done using html and css.

Some quick html and css:

<img> is an image tag, use it if you want to include images, these can be most image formats, even .gif

<br/> makes a line break

<hr> makes a horizontal rule across the width of the page

<div> is a divider

style is where you specify the css

text-align: left make the text left aligned

margin-right: 150px makes the right side margin set to 150px

margin-left: 150px makes the left side margin set to 150px

<h2> is a header tag, it makes the text a set size and bold, you can use it for titles

<p> is a paragraph tag, you can use it to make paragraphs

<b> is a bold font tag, it makes the text bold

var instructions_page = {
    type: jsPsychInstructions,
    pages: [
    '<img src="https://sites2.ff.cuni.cz/ssol/wp-content/uploads/sites/103/2023/06/cropped-navrh_wider.png" width="400" height="140"></br><hr><div style="text-align: left; margin-right: 150px; margin-left: 150px;"><h2>Instructions</h2><p>In this experiment you will see a word on the screen.</p><p><b>If you think the word is a real word press the "d" key, if you do not think it is a real word, press the "h" key.</b></p><p>Press next to continue.</div>',
    'In this experiment you will...'
    ],
    show_clickable_nav: true,
    key_forward: 'Enter',
    key_backward: 'ArrowLeft,
    allow_backward: true,
    button_label_next: 'next',
    button_label_previous: 'back'
};

Demographic questions

Another important part of an experiment is the collection of demographic information, such as age, gender, email address etc.

To do this we can use the survey-html-form plugin.

https://www.jspsych.org/7.3/plugins/survey-html-form/

This means we can present a variety of questions with different response options, e.g. radio buttons, checkboxes, text input etc.

var demographics_page = {
  type: jsPsychSurveyHtmlForm,
  html: '<div style="text-align: left;">'+
  '<p>Question 1 </p><input id="Q1" name="Q1" type="text" />'+
  '<p>Question 2 </p><input id="Q2" name="Q2" type="number" />'+
  '<p>Question 3 </p><input id="Q3" name="Q3" type="email" />'+
  '<p>Question 4</p>'+
  '<input type="radio" id="Q4" name="Q4" value="a"><label>a</label><br>'+
  '<input type="radio" id="Q4" name="Q4" value="b"><label>b</label><br>'+
  '<input type="radio" id="Q4" name="Q4" value="c"><label>c</label><br>'+
  '<p>Question 5</p>'+
  '<input type="checkbox" id="Q5" name="Q5" value="a"><label>a</label><br>'+
  '<input type="checkbox" id="Q5" name="Q5" value="b"><label>b</label><br>'+
  '<input type="checkbox" id="Q5" name="Q4" value="c"><label>c</label><br>'+
  '<p>Question 6</p>'+
  `<textarea id="feedback_comments" name="feedback_comments" rows="4" cols="50"/></textarea>`+
  '</br></div>',
  button_label: 'next',
  autofocus: 'Q1'
};

We will store the trial as a var called demographics_page.

In the example above there are 4 different parameters:

type - this is where we specify the jsPsychSurveyHtmlForm plugin

html - this is where we write the survey code using html

button_label - this is where we give the text in the button

autofocus - this is where we can specify the focus to be on a specific question when the page loads. It takes the value from the id of a question in your html section

In order to understand how the html works, we will look at each part of the code. Note that the end of each line there is a + this means that it will interpret the lines as one big html chunk. This is simply to make it easier to read the html.

The first part of the html section is a <div> where we specify some css. All questions are within this <div> so all of them have the css applied.

'<div style="text-align: left;">'+

Here we use text-align: left; to make sure all the text is left aligned

In the above code we have 5 questions written in the html section:

Question 1 - text input

'<p>Question 1 </p><input id="Q1" name="Q1" type="text" />'

Here we start with a <p> tag, where we put the question text, i.e. Question 1

We then have an <input> tag, this means we are expecting some for of input response

We give the input an id, this should be unique for the specific question, e.g. “Q1”

We also give it a name, again this should be unique for the specific question, e.g. “Q1”, this is important for when we get the data as the data will store the name and the response only

Finally we specify the type of input, here we use "text", which will give us a text input field, the input provided by the participant will be stored in the data

Question 2 - number input

'<p>Question 2 </p><input id="Q2" name="Q2" type="number" />'

This question is similar to Question 1, but we have changed the type to "number" so only numeric values are allowed

Question 3 - email input:

'<p>Question 3 </p><input id="Q3" name="Q3" type="email" />'

This question is similar to Question 1, but we have changed the type to "email" so only values containing an @ are allowed

Question 4 - radio button input

'<p>Question 4</p>'+
  '<input type="radio" id="Q4" name="Q4" value="a"><label>a</label><br>'+
  '<input type="radio" id="Q4" name="Q4" value="b"><label>b</label><br>'+
  '<input type="radio" id="Q4" name="Q4" value="c"><label>c</label><br>'+
  

This question use "radio" as the type, meaning you get a radio button selection as the response.

As well as the id and name options, we also include a value. The value is the information that will be stored in your data, so if the participant chooses the first button, they will have in their data "Q4":"a", as they chose the “a” option for this question.

There is a <label> tag, this is important as it will provide the text that the participant sees next to the radio button. So <label>a</label> is the part of the code that is seen, but the value is what is stored in the data.

Question 5 - checkbox input

'<p>Question 5</p>'+
  '<input type="checkbox" id="Q5" name="Q5" value="a"><label>a</label><br>'+
  '<input type="checkbox" id="Q5" name="Q5" value="b"><label>b</label><br>'+
  '<input type="checkbox" id="Q5" name="Q4" value="c"><label>c</label><br>'+
  

This is similar to Question 4, but instead of radio buttons we have checkbox as the type, meaning there are checkboxes as the input.

Now the participant can choose multiple options.

Question 6 - textarea

'<p>Question 6</p>'+
  `<textarea id="Q6" name="Q6" rows="4" cols="50"/></textarea>`+
  

This question is similar to Question 1, in that it is a text input, but instead of using an <input> tag, we use the <textarea> tag, which allows us to specify additional options:

rows="4" means there are 4 rows, or the height of the box is 4 rows

cols="50" means there are 50 columns, or the width of the box is 50 columns

required responses

If we want to make a question a forced response, i.e. the participant can not leave it blank, then we use the required option.

This is placed within the <input> tag.

So for Question 1 we would use:

'<p>Question 1 </p><input id="Q1" name="Q1" type="text" required/>'

This now means the question will give a warning when the participant tries to click on next, saying that they have to provide a response.

Feedback form

After the main experiment has finished, we need to let the participant know that they have completed the experiment.

To do this, we can make a feedback form, that will present a message to the participant and ask some basic questions.

To do this we will use the survey-html-form plugin, like we did for the demographic information.

var feedback_page = {
  type: jsPsychSurveyHtmlForm,
  html: '<div style="text-align: left;">'+
  '<p>That is the end of the experiment.</p>'+
  '<p>How easy did you find the experiment? </p>'+
  '<input type="radio" id="feedback_ease" name="feedback_ease" value="difficult"><label>difficult</label><br>'+
  '<input type="radio" id="feedback_ease" name="feedback_ease" value="easy"><label>easy</label><br>'+
  '<p>If you have any feedback about the experiment, please write your comments below.</p>'+
  `<textarea id="feedback_comments" name="feedback_comments" rows="4" cols="50"/></textarea>`+
  '</br></div>',
  button_label: 'Submit results',
  autofocus: 'feedback_ease'
};

The whole experiment

Now we have the following components:

  • full screen mode
  • instructions page
  • consent page
  • demographic form
  • feedback form

If we combine this with the lexical decision experiment we made in the previous section, we would have something like this:

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

// inititate jspsych
var jsPsych = initJsPsych();

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

var enter_fullscreen = {
  type: jsPsychFullscreen,
  fullscreen_mode: true,
  message: '<p>some message</p>',
  button_label: 'enter full screen mode'
};

var instructions_page = {
    type: jsPsychInstructions,
    pages: [
    '<img src="https://sites2.ff.cuni.cz/ssol/wp-content/uploads/sites/103/2023/06/cropped-navrh_wider.png" width="400" height="140"></br><hr><div style="text-align: left; margin-right: 150px; margin-left: 150px;"><h2>Instructions</h2><p>In this experiment you will see a word on the screen.</p><p><b>If you think the word is a real word press the "d" key, if you do not think it is a real word, press the "h" key.</b></p><p>Press next to continue.</div>',
    'In this experiment you will...'
    ],
    show_clickable_nav: true,
    key_forward: 'Enter',
    key_backward: 'ArrowLeft',
    allow_backward: true,
    show_clickable_nav: true,
    button_label_previous: 'back',
    button_label_next: 'next'
};

var consent_page = {
    type: jsPsychInstructions,
    pages: [
    'Details of informed consent...',
    ],
    show_clickable_nav: true,
    allow_backward: false,
    button_label_next: 'I consent to participating in this experiment'
};

  var demographics_page = {
  type: jsPsychSurveyHtmlForm,
  html: '<div style="text-align: left;">'+
  '<p>Question 1 </p><input id="Q1" name="Q1" type="text" required/>'+
  '<p>Question 2 </p><input id="Q2" name="Q2" type="number" />'+
  '<p>Question 3 </p><input id="Q3" name="Q3" type="email" />'+
  '<p>Question 4</p>'+
  '<input type="radio" id="Q4" name="Q4" value="a"><label>a</label><br>'+
  '<input type="radio" id="Q4" name="Q4" value="b"><label>b</label><br>'+
  '<input type="radio" id="Q4" name="Q4" value="c"><label>c</label><br>'+
  '<p>Question 5</p>'+
  '<input type="checkbox" id="Q5" name="Q5" value="a"><label>a</label><br>'+
  '<input type="checkbox" id="Q5" name="Q5" value="b"><label>b</label><br>'+
  '<input type="checkbox" id="Q5" name="Q4" value="c"><label>c</label><br>'+
  '<p>Question 6</p>'+
  `<textarea id="feedback_comments" name="feedback_comments" rows="4" cols="50"/></textarea>`+
  '</br></div>',
  button_label: 'next',
  autofocus: 'Q1'
};

var lexical_decision_trial = {
  type: jsPsychHtmlKeyboardResponse,
  stimulus: jsPsych.timelineVariable("item"),
  choices: ['d', 'h'],
  prompt: '<div style="font-size:12pt; 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')
    }
};

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: 500,
  data: {
    task: 'fixation'
  }
};

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

var feedback_page = {
  type: jsPsychSurveyHtmlForm,
  html: '<div style="text-align: left;">'+
  '<p>That is the end of the experiment.</p>'+
  '<p>How easy did you find the experiment? </p>'+
  '<input type="radio" id="feedback_ease" name="feedback_ease" value="difficult"><label>difficult</label><br>'+
  '<input type="radio" id="feedback_ease" name="feedback_ease" value="easy"><label>easy</label><br>'+
  '<p>If you have any feedback about the experiment, please write your comments below.</p>'+
  `<textarea id="feedback_comments" name="feedback_comments" rows="4" cols="50"/></textarea>`+
  '</br></div>',
  button_label: 'Submit results',
  autofocus: 'feedback_ease'
};

timeline.push(enter_fullscreen);
timeline.push(instructions_page);
timeline.push(consent_page);
timeline.push(demographics_page);

timeline.push(lexical_decision_combined);

timeline.push(feedback_page);

//run the experiment
jsPsych.run(timeline);
LS0tCnRpdGxlOiAiVXNpbmcganNQc3ljaCBhbmQgY29nbml0aW9uLnJ1biIKc3VidGl0bGU6ICJTZXNzaW9uIDMgLSBpbnRlcm1lZGlhdGUvYWR2YW5jZWQiCmF1dGhvcjogIkphbWVzIEJyYW5kIgpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiCm91dHB1dDoKICBybWRmb3JtYXRzOjpyZWFkdGhlZG93bjoKICAgIHBhbmRvY19hcmdzOiAiLS1oaWdobGlnaHQtc3R5bGU9bXkudGhlbWUiCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzCiAgICB0b2NfZmxvYXQ6IHRydWUKICAgIGNvZGVfZG93bmxvYWQ6IHRydWUKICAgIHRvY19kZXB0aDogMwogICAgY29sbGFwc2VkOiBmYWxzZQogICAgZGZfcHJpbnQ6IHBhZ2VkCiAgICBsaWdodGJveDogVFJVRQogICAgZ2FsbGVyeTogVFJVRQogICAgY3NzOiAiY3NzL3N0eWxlLmNzcyIKICAgIAotLS0KCmBgYHtyIGVjaG89RkFMU0UsIHdhcm5pbmc9RkFMU0V9CmxpYnJhcnkoc2xpY2tSKQpsaWJyYXJ5KGh0bWx0b29scykKbGlicmFyeSh4YXJpbmdhbkV4dHJhKQpsaWJyYXJ5KHJtYXJrZG93bikKbGlicmFyeShmb250YXdlc29tZSkKbGlicmFyeShic3BsdXMpCgpgYGAKCmBgYHtyIHNldHVwLCB3YXJuaW5nPUZBTFNFLCBlY2hvPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICBldmFsID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICBjb21tZW50ID0gTkEsCiAgICAgICAgICAgICAgICAgICAgICBtZXNzYWdlID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICB3YXJuaW5nID0gRkFMU0UpCgprbml0cjo6a25pdF9ob29rcyRzZXQoCiAgbWVzc2FnZSA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgICBwYXN0ZSgnPGJ1dHRvbiB0eXBlPSJidXR0b24iIGNsYXNzPSJjb2xsYXBzaWJsZTEiPjxzdHJvbmc+JywKICAgICBmYShuYW1lID0gImNpcmNsZS1pbmZvIiksCiAgICAgJyBtb3JlIGluZm88L3N0cm9uZz48L2J1dHRvbj4nLCAnPGRpdiBjbGFzcz0iY29udGVudDEiPjxwPicsCiAgICAgZ3N1YignIyMnLCAnXG4nLCB4KSwKICAgICAnPC9wPjwvZGl2PicsCiAgICAgc2VwID0gJ1xuJykKICAgfSkKCmNvZGVibG9jayA9IGZ1bmN0aW9uKHgsIG9wdGlvbnMpIHsKICAgICBjYXQocGFzdGUoJzxkaXYgY2xhc3M9ImNvZGVibG9jayI+JywKICAgICBwYXN0ZTAoeCksCiAgICAgJzwvZGl2PicsCiAgICAgc2VwID0gJ1xuJykpCiAgIH0KCmBgYAoKLS0tCgojIyBgciBmYSgibGFuZ3VhZ2UiKWAgVHJhbnNsYXRpb25zIGF2YWlsYWJsZQoKRGlzY2xhaW1lcjogbWF5IG5vdCBiZSB2ZXJ5IGFjY3VyYXRlLi4uCgo8ZGl2IGlkPSJnb29nbGVfdHJhbnNsYXRlX2VsZW1lbnQiPjwvZGl2PgoKLS0tCgojIFdvcmtzaGVldCBvdmVydmlldwoKIyMgYHIgZmEoImNyb3NzaGFpcnMiKWAgQWltcwoKQnkgdGhlIGVuZCBvZiB0aGlzIHdvcmtzaGVldCB5b3Ugc2hvdWxkIGJlIGFibGUgdG86CgotICoqcHJvZ3JhbSoqIHlvdXIgb3duIGV4cGVyaW1lbnRzIGluIGpzUHN5Y2gKLSAqKmhvc3QqKiB0aGUgZXhwZXJpbWVudCBvbmxpbmUgdXNpbmcgY29nbml0aW9uLnJ1bgotICoqdXNlKiogdGhlIHBhcnRpY2lwYW50IGRhdGEgZm9yIGFuYWx5c2lzCi0gKiphcHBseSoqIHRoZSBiYXNpYyBza2lsbHMgeW91IGhhdmUgbGVhcm50IGZvciB5b3VyIG93biBwdXJwb3NlcwotICoqbGVhcm4qKiBzb21lIGV4dHJhIHNraWxscyBzdWNoIGFzIEhUTUwsIGphdmFzY3JpcHQsIENTUyBhbmQgSlNPTgoKIyMgYHIgZmEoInVzZXItZ3JhZHVhdGUiKWAgUHJlLXJlcXVpc2l0ZXMKClRvIGNvbXBsZXRlIHRoZSBhaW1zIHlvdSB3aWxsIG5lZWQgdG86CgotICoqZm9sbG93KiogdGhpcyB3b3Jrc2hlZXQKLSAqKmFzayoqIHF1ZXN0aW9ucyBpZiB5b3UgYXJlIG5vdCBzdXJlL2JlIGFibGUgdG8gZ29vZ2xlCi0gKipoYXZlKiogYSB3b3JraW5nIGNvbXB1dGVyIGFuZCBpbnRlcm5ldCBjb25uZWN0aW9uCi0gKipiZSBwYXRpZW50Kiogd2hlbiB0aGluZ3MgZG8gbm90IHdvcmsKCllvdSBkbyBub3QgbmVlZCB0bzoKCi0gaGF2ZSBhbnkgKipwcm9ncmFtbWluZyBrbm93bGVkZ2UqKgotIGhhdmUgaGlnaCAqKmNvbXB1dGVyIGxpdGVyYWN5KioKLSBrbm93IGFueXRoaW5nIGFib3V0ICoqanNQc3ljaCwgY29nbml0aW9uLnJ1biwgaHRtbCwgY3NzIG9yIGphdmFzY3JpcHQqKgotIGJlIGEgKipsaW5ndWlzdCoqCgojIyBgciBmYSgiZm9sZGVyLXRyZWUiKWAgU3RydWN0dXJlCgpUaGUgd29ya3NoZWV0IHdpbGwgZ28gdGhyb3VnaCB0aGUgZm9sbG93aW5nIHNlY3Rpb25zOgoKLSBSdW5uaW5nIGluIGZ1bGwgc2NyZWVuIG1vZGUKLSBQcmVzZW50aW5nIGluc3RydWN0aW9ucwotIFVzaW5nIGNvbnNlbnQgZm9ybXMKLSBEZW1vZ3JhcGhpYyBxdWVzdGlvbnMKLSBGZWVkYmFjayBmb3JtcwotIERpc3RyaWJ1dGluZyBsaW5rcwoKPCEtLSAtIFJ1bm5pbmcgb3RoZXIgdHlwZXMgb2YgZXhwZXJpbWVudHMgLS0+Cgo8IS0tICAgICAtIHdvcmtpbmcgd2l0aCBhdWRpbywgaW1hZ2VzIGFuZCB2aWRlbyAtLT4KPCEtLSAgICAgLSBzZWxmIHBhY2VkIHJlYWRpbmcgLS0+CjwhLS0gICAgIC0gcmF0aW5nIGV4cGVyaW1lbnRzIC0tPgo8IS0tICAgICAtIGNvbmlkdGlvbmFsIGFuZCBpZiB0cmlhbHMgLS0+CjwhLS0gPGJyLz48YnIvPiAtLT4KPCEtLSAtIFdvcmtpbmcgd2l0aCByZXN1bHRzIGZpbGVzIGluIFIgLS0+Cgo8IS0tICAgICAtIGxvYWRpbmcgaW4gbXVsdGlwbGUgZmlsZXMgcXVpY2tseSAtLT4KPCEtLSAgICAgLSBnZXR0aW5nIGluZm9ybWF0aW9uIGZyb20gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBleHBlcmltZW50IC0tPgo8IS0tICAgICAtIGNvbnZlcnRpbmcganNvbiBmb3JtYXR0ZWQgZGF0YSB0byBjb2x1bW5zIC0tPgoKIyMgYHIgZmEoImxpZ2h0YnVsYiIpYCBSZWNhcAoKSW4gdGhlIGxhc3Qgc2Vzc2lvbiB3ZSBzaG91bGQgaGF2ZToKCi0gYnVpbHQgYSBsZXhpY2FsIGRlY2lzaW9uIGV4cGVyaW1lbnQKLSBsZWFybnQgc29tZSBiYXNpYyBjc3MgYW5kIGh0bWwKLSB1c2VkIHBsdWdpbnMKLSB3b3JrZWQgd2l0aCB0aW1lbGluZSB2YXJpYWJsZXMKLSBjcmVhdGVkIGpzb24gc3RpbXVsaSBmaWxlcyB1c2luZyBSCgotLS0KCiMgTWFraW5nIGEgY29tcGxldGUgZXhwZXJpbWVudAoKSW4gdGhlIHByZXZpb3VzIHdvcmtzaGVldCB3ZSB3ZXJlIGFibGUgdG8gc2V0IHVwIGEgd29ya2luZyBsZXhpY2FsIGRlY2lzaW9uIGV4cGVyaW1lbnQuIEhvd2V2ZXIsIHRoaXMgZXhwZXJpbWVudCBvbmx5IHByZXNlbnRlZCB0aGUgbGV4aWNhbCBkZWNpc2lvbiB0cmlhbHMsIHdpdGggbm90aGluZyBlbHNlIHByZXNlbnRlZC4gVGhpcyBpcyBwcm9iYWJseSBub3QgaG93IHdlIHdvdWxkIHdhbnQgdG8gcnVuIHRoZSBleHBlcmltZW50IHRob3VnaC4gSW5zdGVhZCwgd2hhdCB3b3VsZCBiZSBnb29kIGlzIHRvIGhhdmUgdmFyaW91cyBvdGhlciBwYXJ0cyB0byBvdXIgZXhwZXJpbWVudCwgc28gdGhhdCB3ZSBjYW4gZW5zdXJlIHRoYXQgdGhlIHBhcnRpY2lwYW50IGtub3dzIHdoYXQgdG8gZG8uCgpJbiB0aGlzIHNlY3Rpb24gd2Ugd2lsbCBmb2N1cyBvbiBhZGRpbmcgaW4gdGhlc2UgcGFydHMuCgojIFJ1bm5pbmcgaW4gZnVsbCBzY3JlZW4gbW9kZQoKRm9yIG9ubGluZSBleHBlcmltZW50cywgaXQgaXMgc2Vuc2libGUgdG8gZW5zdXJlIHRoYXQgdGhlIHBhcnRpY2lwYW50IGhhcyBhcyBmZXcgZGlzdHJhY3Rpb25zIHdoaWxzdCBjb21wbGV0aW5nIHRoZSBleHBlcmltZW50LiBPbmUgdXNlZnVsIGFwcHJvYWNoIGlzIHRvIG1ha2UgdGhlIGV4cGVyaW1lbnQgcnVuIGluIGZ1bGwgc2NyZWVuIG1vZGUuCgpUbyBkbyB0aGlzIHdlIGNhbiB1c2UgdGhlIGBmdWxsc2NyZWVuYCBwbHVnaW4uCgpodHRwczovL3d3dy5qc3BzeWNoLm9yZy83LjMvcGx1Z2lucy9mdWxsc2NyZWVuLwoKVGhpcyBtZWFucyB3ZSBjYW4gcHJlc2VudCBhIG1lc3NhZ2Ugb24gdGhlIHNjcmVlbiBhbmQgYSBidXR0b24uIFRoZSBtZXNzYWdlIHdpbGwgcHJvYmFibHkgc2F5IHNvbWV0aGluZyB0byB0aGUgcGFydGljaXBhbnQgYW5kIHdoZW4gdGhleSBjbGljayBvbiB0aGUgYnV0dG9uLCB0aGUgZXhwZXJpbWVudCB3aWxsIGVudGVyIGZ1bGwgc2NyZWVuIG1vZGUuCgpgYGB7anN9CnZhciBlbnRlcl9mdWxsc2NyZWVuID0gewogIHR5cGU6IGpzUHN5Y2hGdWxsc2NyZWVuLAogIGZ1bGxzY3JlZW5fbW9kZTogdHJ1ZSwKICBtZXNzYWdlOiAnPHA+c29tZSBtZXNzYWdlPC9wPicsCiAgYnV0dG9uX2xhYmVsOiAnZW50ZXIgZnVsbCBzY3JlZW4gbW9kZScKfTsKCmBgYAoKV2Ugd2lsbCBzdG9yZSB0aGUgdHJpYWwgYXMgYSBgdmFyYCBjYWxsZWQgYGVudGVyX2Z1bGxzY3JlZW5gLgoKSW4gdGhlIGV4YW1wbGUgYWJvdmUgdGhlcmUgYXJlIDQgZGlmZmVyZW50IHBhcmFtZXRlcnM6CgpgdHlwZWAgLSB0aGlzIGlzIHdoZXJlIHdlIHNwZWNpZnkgdGhlIGBqc1BzeWNoRnVsbHNjcmVlbmAgcGx1Z2luCgpgZnVsbHNjcmVlbl9tb2RlYCAtIHRoaXMgaXMgYHRydWVgIGFzIHdlIHdhbnQgdGhlIGV4cGVyaW1lbnQgdG8gYmUgaW4gZnVsbCBzY3JlZW4gbW9kZSB3aGVuIHRoZSBidXR0b24gaXMgY2xpY2tlZC4gSWYgdGhpcyBpcyBzZXQgdG8gYGZhbHNlYCwgaXQgd2lsbCBleGl0IGZ1bGwgc2NyZWVuIG1vZGUgd2hlbiB0aGUgYnV0dG9uIGlzIGNsaWNrZWQuIFlvdSBjb3VsZCB1c2UgdGhlIGBmYWxzZWAgdmVyc2lvbiBpZiB5b3Ugd2FudCB0byBsZXQgcGFydGljaXBhbnRzIGV4aXQgZnVsbCBzY3JlZW4gbW9kZSBtYW51YWxseSwgbWF5YmUgYXQgdGhlIGVuZCBvZiB0aGUgZXhwZXJpbWVudAoKYG1lc3NhZ2VgIC0gdGhpcyBpcyB0aGUgbWVzc2FnZSB0aGF0IHdpbGwgYXBwZWFyIGFib3ZlIHRoZSBidXR0b24gb24gdGhlIHNjcmVlbi4gV2UgY2FuIHVzZSB0aGlzIHRvIGluZm9ybSB0aGUgcGFydGljaXBhbnQgdGhhdCB3aGVuIHRoZXkgY2xpY2sgdGhlIGJ1dHRvbiB0aGUgZXhwZXJpbWVudCB3aWxsIGVudGVyIGZ1bGwgc2NyZWVuLiBZb3UgY2FuIHNheSB3aGF0ZXZlciB5b3Ugd2FudCBoZXJlLiBOb3RlIHRoZSB1c2Ugb2YgYDxwPmAgYXQgdGhlIHN0YXJ0IGFuZCBgPC9wPmAgYXQgdGhlIGVuZC4gVGhpcyBpcyBhIGBodG1sIHRhZ2AgdG8gc2F5IHlvdSB3YW50IHRoZSB0ZXh0IHRvIGJlIGEgYHBhcmFncmFwaGAuIElmIHlvdSByZW1vdmUgaXQsIHRoZXJlIHdpbGwgYmUgbm8gbGluZSBicmVhayBiZXR3ZWVuIHRoZSB0ZXh0IGFuZCB0aGUgYnV0dG9uLiBZb3UgY2FuIHVzZSBhcyBtYW55IHBhcmFncmFwaCB0YWdzIGFzIHlvdSB3YW50LCBlLmcuIGlmIHlvdSBoYWQgYCc8cD5UaGlzIGlzIHRoZSBmaXJzdCBwYXJhZ3JhcGguPC9wPjxwPlRoaXMgaXMgYW5vdGhlciBwYXJhZ3JhcGguPC9wPmAuCgpgYnV0dG9uX2xhYmVsYCAtIHRoaXMgaXMgdGhlIHRleHQgc2hvd24gaW4gdGhlIGJ1dHRvbi4gSXQgY2FuIGFnYWluIGJlIGFueXRoaW5nIHlvdSB3YW50LgoKRG9uJ3QgZm9yZ2V0LCBpZiB3ZSB3YW50IHRvIHNob3cgdGhlIHRyaWFsIHRvIHRoZSBwYXJ0aWNpcGFudCwgd2Ugd2lsbCBoYXZlIHRvIGBwdXNoYCBpdCB0byBvdXIgdGltZWxpbmUuCgpgYGB7anN9CnRpbWVsaW5lLnB1c2goZW50ZXJfZnVsbHNjcmVlbik7CgpgYGAKCiMgSW5zdHJ1Y3Rpb25zIHBhZ2UKClRvIG1ha2Ugc3VyZSB0aGF0IHRoZSBwYXJ0aWNpcGFudHMgdW5kZXJzdGFuZCB3aGF0IHRoZSBiYXNpYyBvdXRsaW5lIG9mIHRoZSBleHBlcmltZW50IGlzLCB3ZSBuZWVkIHRvIHByZXNlbnQgdGhlIHBhcnRpY2lwYW50cyB3aXRoIGEgZ2VuZXJhbCBpbnN0cnVjdGlvbiBwYWdlLgoKVG8gZG8gdGhpcyB3ZSBjYW4gdXNlIHRoZSBgaW5zdHJ1Y3Rpb25zYCBwbHVnaW4uCgpodHRwczovL3d3dy5qc3BzeWNoLm9yZy83LjMvcGx1Z2lucy9pbnN0cnVjdGlvbnMvCgpUaGlzIG1lYW5zIHdlIGNhbiBwcmVzZW50IGEgbWVzc2FnZSBvbiB0aGUgc2NyZWVuIGFuZCBhIGJ1dHRvbi4KCmBgYHtqc30KdmFyIGluc3RydWN0aW9uc19wYWdlID0gewogICAgdHlwZToganNQc3ljaEluc3RydWN0aW9ucywKICAgIHBhZ2VzOiBbCiAgICAnV2VsY29tZSB0byB0aGUgZXhwZXJpbWVudC4gQ2xpY2sgbmV4dCBvciBwcmVzcyB0aGUgRW50ZXIga2V5IHRvIGJlZ2luLicsCiAgICAnSW4gdGhpcyBleHBlcmltZW50IHlvdSB3aWxsLi4uJwogICAgXSwKICAgIHNob3dfY2xpY2thYmxlX25hdjogdHJ1ZSwKICAgIGtleV9mb3J3YXJkOiAnRW50ZXInLAogICAga2V5X2JhY2t3YXJkOiAnQXJyb3dMZWZ0LAogICAgYWxsb3dfYmFja3dhcmQ6IHRydWUsCiAgICBidXR0b25fbGFiZWxfbmV4dDogJ25leHQnLAogICAgYnV0dG9uX2xhYmVsX3ByZXZpb3VzOiAnYmFjaycKfTsKCmBgYAoKV2Ugd2lsbCBzdG9yZSB0aGUgdHJpYWwgYXMgYSBgdmFyYCBjYWxsZWQgYGluc3RydWN0aW9uc19wYWdlYC4KCkluIHRoZSBleGFtcGxlIGFib3ZlIHRoZXJlIGFyZSA3IGRpZmZlcmVudCBwYXJhbWV0ZXJzOgoKYHR5cGVgIC0gdGhpcyBpcyB3aGVyZSB3ZSBzcGVjaWZ5IHRoZSBganNQc3ljaEluc3RydWN0aW9uc2AgcGx1Z2luCgpgcGFnZXNgIC0gdGhpcyB0YWtlcyBhbiBhcnJheSwgaS5lLiB5b3UgdXNlIGBbYCBhbmQgYF1gLCB3aXRoIHRoZSByZWxldmFudCBpbmZvcm1hdGlvbiBjb250YWluZWQgd2l0aGluIHRoZSBhcnJheS4KClRoZSBpbnN0cnVjdGlvbnMgYXJlIHdyaXR0ZW4gYXMgYSBzdHJpbmcsIGUuZy4gJ1dlbGNvbWUgdG8gdGhlIGV4cGVyaW1lbnQuIENsaWNrIG5leHQgb3IgcHJlc3MgdGhlIEVudGVyIGtleSB0byBiZWdpbi4nLklmIHlvdSB3YW50IHRvIHNob3cgbXVsdGlwbGUgaW5zdHJ1Y3Rpb25zIHBhZ2VzLCB5b3UgY2FuIHNlcGFyYXRlIHRoZW0gdXNpbmcgYSBjb21tYSwgZS5nIGBwYWdlczogWydpbnN0cnVjdGlvbnMgcGFnZSAxJywgJ2luc3RydWN0aW9ucyBwYWdlIDInXWAuCgpUaGUgc3RyaW5ncyBjYW4gYmUgd3JpdHRlbiBzaW1wbHkgd2l0aG91dCBhbnkgZm9ybWF0dGluZywgb3IgeW91IGNhbiB1c2UgaHRtbCBhbmQgY3NzLgoKYHNob3dfY2xpY2thYmxlX25hdmAgLSB0aGlzIG1ha2VzIHRoZSBidXR0b25zIHZpc2libGUsIGUuZy4geW91IGNhbiBzZWUgdGhlICduZXh0JyBhbmQgJ2JhY2snIGJ1dHRvbnMgdW5kZXJuZWF0aCB0aGUgaW5zdHJ1Y3Rpb25zLiBJZiB0aGlzIGlzIGZhbHNlLCB0aGUgYnV0dG9ucyB3aWxsIG5vdCBiZSBzaG93bgoKYGtleV9mb3J3YXJkYCAtIHRoaXMgaXMgdGhlIGtleWJvYXJkIGtleSB0aGF0IHlvdSBzcGVjaWZ5IGlmIHlvdSB3YW50IHRvIGFsbG93IHRoZSBwYXJ0aWNpcGFudCB0byBwcmVzcyBhIGtleSwgYXMgd2VsbCBhcyB0aGUgYnV0dG9uLCB0byBnbyB0byB0aGUgbmV4dCBpbnN0cnVjdGlvbnMgcGFnZQoKYGtleV9iYWNrd2FyZGAgLSB0aGlzIGlzIHRoZSBrZXlib2FyZCBrZXkgdGhhdCB5b3Ugc3BlY2lmeSBpZiB5b3Ugd2FudCB0byBhbGxvdyB0aGUgcGFydGljaXBhbnQgdG8gcHJlc3MgYSBrZXksIGFzIHdlbGwgYXMgdGhlIGJ1dHRvbiwgdG8gZ28gYmFjayB0byB0aGUgbGFzdCBpbnN0cnVjdGlvbnMgcGFnZQoKYGFsbG93X2JhY2t3YXJkYCAtIHRoaXMgbWFrZXMgdGhlIGJhY2sgYnV0dG9uL3Jlc3BvbnNlIGFjdGl2ZSwgc28gdGhhdCB0aGUgcGFydGljaXBhbnQgY2FuIGdvIGJhY2sgdG8gdGhlIHByZXZpb3VzIGluc3RydWN0aW9ucyBwYWdlLiBJZiB0aGlzIGlzIGZhbHNlLCB0aGUgYnV0dG9uL3Jlc3BvbnNlIHRvIGdvIGJhY2sgd2lsbCBub3Qgd29yawoKYGJ1dHRvbl9sYWJlbF9uZXh0YCAtIHRoaXMgaXMgdGhlIHRleHQgaW4gdGhlIGJ1dHRvbiB0byBnbyB0byB0aGUgbmV4dCBpbnN0cnVjdGlvbnMgcGFnZSBvciBmaW5pc2ggdGhlIGluc3RydWN0aW9ucy4gSXQgaXMgYSBzdHJpbmcsIGJ1dCBjYW4gYWxzbyB0YWtlIGh0bWwgYW5kIGNzcwoKYGJ1dHRvbl9sYWJlbF9wcmV2aW91c2AgLSB0aGlzIGlzIHRoZSB0ZXh0IGluIHRoZSBidXR0b24gdG8gZ28gdG8gdGhlIHByZXZpb3VzIGluc3RydWN0aW9ucyBwYWdlLiBJdCBpcyBhIHN0cmluZywgYnV0IGNhbiBhbHNvIHRha2UgaHRtbCBhbmQgY3NzCgoKRG9uJ3QgZm9yZ2V0LCBpZiB3ZSB3YW50IHRvIHNob3cgdGhlIHRyaWFsIHRvIHRoZSBwYXJ0aWNpcGFudCwgd2Ugd2lsbCBoYXZlIHRvIGBwdXNoYCBpdCB0byBvdXIgdGltZWxpbmUuCgpgYGB7anN9CnRpbWVsaW5lLnB1c2goaW5zdHJ1Y3Rpb25zX3BhZ2UpCgpgYGAKCklmIHlvdSB3YW50IHRvIG1ha2UgdGhlIGluc3RydWN0aW9ucyBwYWdlIGxvb2sgYSBiaXQgbW9yZSBmYW5jeSwgd2UgY2FuIHVzZSBodG1sIGFuZCBjc3MgZm9yIG91ciBpbnN0cnVjdGlvbnMuIEZvciBleGFtcGxlLCBpZiB5b3Ugd2FudCB0byBhZGQgYSB1bml2ZXJzaXR5L2xhYiBsb2dvIHlvdSBjYW4gdXNlIGh0bWwuCgpJbiB0aGUgZXhhbXBsZSBiZWxvdywgd2UgdXNlIGFuIGBpbWdgIGh0bWwgdGFnLCB0byBzaG93IGFuIGltYWdlIGZyb20gYSBsaW5rIGF2YWlsYWJsZSBvbiB0aGUgaW50ZXJuZXQuCgpTcGVjaWZpY2FsbHkgaHR0cHM6Ly9zaXRlczIuZmYuY3VuaS5jei9zc29sL3dwLWNvbnRlbnQvdXBsb2Fkcy9zaXRlcy8xMDMvMjAyMy8wNi9jcm9wcGVkLW5hdnJoX3dpZGVyLnBuZwoKIVtdKGh0dHBzOi8vc2l0ZXMyLmZmLmN1bmkuY3ovc3NvbC93cC1jb250ZW50L3VwbG9hZHMvc2l0ZXMvMTAzLzIwMjMvMDYvY3JvcHBlZC1uYXZyaF93aWRlci5wbmcpCgpXZSBzcGVjaWZ5IHRoZSBzb3VyY2Ugb2YgdGhlIGltYWdlLCBvciB3aGVyZSBpdCBpcyBsb2NhdGVkIHVzaW5nIGBzcmNgLCBmb2xsb3dlZCBieSBhIGA9YCB0aGVuIHRoZSBsaW5rIGluIHF1b3RhdGlvbnMvYCJgLiBJZiB5b3Ugd2FudCB0byB1c2UgYSBmaWxlIHRoYXQgeW91IGhhdmUgdXBsb2FkZWQgdG8gY29nbml0aW9uLnJ1biwgbWFrZSBzdXJlIHlvdSB1cGxvYWQgaXQgdG8gdGhlIHN0aW11bGkgc2VjdGlvbiBhbmQgdGhlbiBqdXN0IHNwZWNpZnkgdGhlIGZpbGUgbmFtZSBhcyB0aGUgYHNyY2AsIGUuZy4gYCc8aW1nIHNyYz0iaW1hZ2UucG5nIj4nYAoKYGBge2pzfQpwYWdlczogWyc8aW1nIHNyYz0iaHR0cHM6Ly9zaXRlczIuZmYuY3VuaS5jei9zc29sL3dwLWNvbnRlbnQvdXBsb2Fkcy9zaXRlcy8xMDMvMjAyMy8wNi9jcm9wcGVkLW5hdnJoX3dpZGVyLnBuZyI+J10KCmBgYAoKWW91IG1pZ2h0IG5vdGljZSB0aGF0IHRoZSBpbWFnZSBpcyBub3QgcGFydGljdWxhcmx5IGEgdXNlZnVsIHNpemUuIFRoaXMgaXMgYmVjYXVzZSB3ZSBoYXZlIHRvIHNwZWNpZnkgdGhlIHNpemUgaWYgd2Ugd2FudCBpdCB0byBiZSBkaWZmZXJlbnQgZnJvbSB0aGUgb3JpZ2luYWwuCgpXZSBjYW4gZG8gdGhpcyB3aXRoIGB3aWR0aGAgYW5kIGBoZWlnaHRgIGFyZ3VtZW50cywgaW4gdGhlIGV4YW1wbGUgYmVsb3cgd2Ugc3BlY2lmeSB0aGUgd2lkdGggYXMgNDAwIGFuZCB0aGUgaGVpZ2h0IGFzIDE0MCwgdGhpcyBpcyBpbiBwaXhlbHMuIFlvdSBjYW4ganVzdCBzcGVjaWZ5IHRoZSB3aWR0aCBpZiB5b3Ugd2FudCB0byBrZWVwIHRoZSBhc3BlY3QgcmF0aW8gdGhlIHNhbWUgYXMgdGhlIG9yaWdpbmFsOgoKYGBge2pzfQpwYWdlczogWyc8aW1nIHNyYz0iaHR0cHM6Ly9zaXRlczIuZmYuY3VuaS5jei9zc29sL3dwLWNvbnRlbnQvdXBsb2Fkcy9zaXRlcy8xMDMvMjAyMy8wNi9jcm9wcGVkLW5hdnJoX3dpZGVyLnBuZyIgd2lkdGg9IjQwMCIgaGVpZ2h0PSIxNDAiPiddCgpgYGAKCkZpbmFsbHksIHdlIGNhbiBhZGQgYSB0aXRsZSB0byB0aGUgcGFnZSBhbmQgdGhlIGluc3RydWN0aW9ucyB0aGVtc2VsdmVzLiBUaGlzIGlzIGFsbCBkb25lIHVzaW5nIGh0bWwgYW5kIGNzcy4KClNvbWUgcXVpY2sgaHRtbCBhbmQgY3NzOgoKYDxpbWc+YCBpcyBhbiBpbWFnZSB0YWcsIHVzZSBpdCBpZiB5b3Ugd2FudCB0byBpbmNsdWRlIGltYWdlcywgdGhlc2UgY2FuIGJlIG1vc3QgaW1hZ2UgZm9ybWF0cywgZXZlbiAuZ2lmCgpgPGJyLz5gIG1ha2VzIGEgbGluZSBicmVhawoKYDxocj5gIG1ha2VzIGEgaG9yaXpvbnRhbCBydWxlIGFjcm9zcyB0aGUgd2lkdGggb2YgdGhlIHBhZ2UKCmA8ZGl2PmAgaXMgYSBkaXZpZGVyCgpgc3R5bGVgIGlzIHdoZXJlIHlvdSBzcGVjaWZ5IHRoZSBjc3MKCmB0ZXh0LWFsaWduOiBsZWZ0YCBtYWtlIHRoZSB0ZXh0IGxlZnQgYWxpZ25lZAoKYG1hcmdpbi1yaWdodDogMTUwcHhgIG1ha2VzIHRoZSByaWdodCBzaWRlIG1hcmdpbiBzZXQgdG8gMTUwcHgKCmBtYXJnaW4tbGVmdDogMTUwcHhgIG1ha2VzIHRoZSBsZWZ0IHNpZGUgbWFyZ2luIHNldCB0byAxNTBweAoKYDxoMj5gIGlzIGEgaGVhZGVyIHRhZywgaXQgbWFrZXMgdGhlIHRleHQgYSBzZXQgc2l6ZSBhbmQgYm9sZCwgeW91IGNhbiB1c2UgaXQgZm9yIHRpdGxlcwoKYDxwPmAgaXMgYSBwYXJhZ3JhcGggdGFnLCB5b3UgY2FuIHVzZSBpdCB0byBtYWtlIHBhcmFncmFwaHMKCmA8Yj5gIGlzIGEgYm9sZCBmb250IHRhZywgaXQgbWFrZXMgdGhlIHRleHQgYm9sZAoKYGBge2pzfQp2YXIgaW5zdHJ1Y3Rpb25zX3BhZ2UgPSB7CiAgICB0eXBlOiBqc1BzeWNoSW5zdHJ1Y3Rpb25zLAogICAgcGFnZXM6IFsKICAgICc8aW1nIHNyYz0iaHR0cHM6Ly9zaXRlczIuZmYuY3VuaS5jei9zc29sL3dwLWNvbnRlbnQvdXBsb2Fkcy9zaXRlcy8xMDMvMjAyMy8wNi9jcm9wcGVkLW5hdnJoX3dpZGVyLnBuZyIgd2lkdGg9IjQwMCIgaGVpZ2h0PSIxNDAiPjwvYnI+PGhyPjxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7IG1hcmdpbi1yaWdodDogMTUwcHg7IG1hcmdpbi1sZWZ0OiAxNTBweDsiPjxoMj5JbnN0cnVjdGlvbnM8L2gyPjxwPkluIHRoaXMgZXhwZXJpbWVudCB5b3Ugd2lsbCBzZWUgYSB3b3JkIG9uIHRoZSBzY3JlZW4uPC9wPjxwPjxiPklmIHlvdSB0aGluayB0aGUgd29yZCBpcyBhIHJlYWwgd29yZCBwcmVzcyB0aGUgImQiIGtleSwgaWYgeW91IGRvIG5vdCB0aGluayBpdCBpcyBhIHJlYWwgd29yZCwgcHJlc3MgdGhlICJoIiBrZXkuPC9iPjwvcD48cD5QcmVzcyBuZXh0IHRvIGNvbnRpbnVlLjwvZGl2PicsCiAgICAnSW4gdGhpcyBleHBlcmltZW50IHlvdSB3aWxsLi4uJwogICAgXSwKICAgIHNob3dfY2xpY2thYmxlX25hdjogdHJ1ZSwKICAgIGtleV9mb3J3YXJkOiAnRW50ZXInLAogICAga2V5X2JhY2t3YXJkOiAnQXJyb3dMZWZ0LAogICAgYWxsb3dfYmFja3dhcmQ6IHRydWUsCiAgICBidXR0b25fbGFiZWxfbmV4dDogJ25leHQnLAogICAgYnV0dG9uX2xhYmVsX3ByZXZpb3VzOiAnYmFjaycKfTsKCmBgYAoKIyBDb25zZW50IGZvcm1zCgpJZiB5b3UgYXJlIHJ1bm5pbmcgYW4gb25saW5lIGV4cGVyaW1lbnQsIHlvdSB3aWxsIGxpa2VseSBiZSBjb2xsZWN0aW5nIGRhdGEgZnJvbSBwYXJ0aWNpcGFudHMgdGhhdCB0aGV5IG5lZWQgdG8gY29uc2VudCB0byBpZiB0aGV5IGFyZSB0byBwYXJ0aWNpcGF0ZS4KCk9uY2UgeW91IGhhdmUgZXRoaWNzIGFwcHJvdmFsLCB5b3Ugd2lsbCBuZWVkIHRvIGluY2x1ZGUgYSBwYXJ0aWNpcGFudCBjb25zZW50IHNlY3Rpb24sIHdoZXJlIHRoZSBwYXJ0aWNpcGFudCBpcyBzaG93biB0aGUgY29uZGl0aW9ucyBvZiBwYXJ0aWNpcGF0aW9uLiBJZiB0aGV5IGFyZSB0byBwYXJ0aWNpcGF0ZSB0aGV5IHdpbGwgbmVlZCB0byBjb25maXJtIHRoZWlyIGNvbnNlbnQuCgpXZSBjYW4gYWdhaW4gdXNlIHRoZSBgaW5zdHJ1Y3Rpb25zYCBwbHVnaW4gdG8gZG8gdGhpcy4KCkluIHRoaXMgZXhhbXBsZSwgd2UgcHJlc2VudCBqdXN0IG9uZSBwYWdlIGFuZCBoYXZlIGEgYnV0dG9uIHRoYXQgY2xlYXJseSBjb25maXJtcyB0aGUgcGFydGljaXBhbnQgY29uc2VudGluZyB0byB0YWtlIHBhcnQuIFRoZXJlIGlzIG9ubHkgb25lIG9wdGlvbiBoZXJlLCBzbyBpZiB0aGUgcGFydGljaXBhbnQgd2FudHMgdG8gdGFrZSBwYXJ0LCB0aGV5IGhhdmUgdG8gY2xpY2sgdGhlIGJ1dHRvbi4KCmBgYHtqc30KdmFyIGNvbnNlbnRfcGFnZSA9IHsKICAgIHR5cGU6IGpzUHN5Y2hJbnN0cnVjdGlvbnMsCiAgICBwYWdlczogWwogICAgJ0RldGFpbHMgb2YgaW5mb3JtZWQgY29uc2VudC4uLicsCiAgICBdLAogICAgc2hvd19jbGlja2FibGVfbmF2OiB0cnVlLAogICAgYWxsb3dfYmFja3dhcmQ6IGZhbHNlLAogICAgYnV0dG9uX2xhYmVsX25leHQ6ICdJIGNvbnNlbnQgdG8gcGFydGljaXBhdGluZyBpbiB0aGlzIGV4cGVyaW1lbnQnCn07CgpgYGAKCiMgRGVtb2dyYXBoaWMgcXVlc3Rpb25zCgpBbm90aGVyIGltcG9ydGFudCBwYXJ0IG9mIGFuIGV4cGVyaW1lbnQgaXMgdGhlIGNvbGxlY3Rpb24gb2YgZGVtb2dyYXBoaWMgaW5mb3JtYXRpb24sIHN1Y2ggYXMgYWdlLCBnZW5kZXIsIGVtYWlsIGFkZHJlc3MgZXRjLgoKVG8gZG8gdGhpcyB3ZSBjYW4gdXNlIHRoZSBgc3VydmV5LWh0bWwtZm9ybWAgcGx1Z2luLgoKaHR0cHM6Ly93d3cuanNwc3ljaC5vcmcvNy4zL3BsdWdpbnMvc3VydmV5LWh0bWwtZm9ybS8KClRoaXMgbWVhbnMgd2UgY2FuIHByZXNlbnQgYSB2YXJpZXR5IG9mIHF1ZXN0aW9ucyB3aXRoIGRpZmZlcmVudCByZXNwb25zZSBvcHRpb25zLCBlLmcuIHJhZGlvIGJ1dHRvbnMsIGNoZWNrYm94ZXMsIHRleHQgaW5wdXQgZXRjLgoKYGBge2pzfQp2YXIgZGVtb2dyYXBoaWNzX3BhZ2UgPSB7CiAgdHlwZToganNQc3ljaFN1cnZleUh0bWxGb3JtLAogIGh0bWw6ICc8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyI+JysKICAnPHA+UXVlc3Rpb24gMSA8L3A+PGlucHV0IGlkPSJRMSIgbmFtZT0iUTEiIHR5cGU9InRleHQiIC8+JysKICAnPHA+UXVlc3Rpb24gMiA8L3A+PGlucHV0IGlkPSJRMiIgbmFtZT0iUTIiIHR5cGU9Im51bWJlciIgLz4nKwogICc8cD5RdWVzdGlvbiAzIDwvcD48aW5wdXQgaWQ9IlEzIiBuYW1lPSJRMyIgdHlwZT0iZW1haWwiIC8+JysKICAnPHA+UXVlc3Rpb24gNDwvcD4nKwogICc8aW5wdXQgdHlwZT0icmFkaW8iIGlkPSJRNCIgbmFtZT0iUTQiIHZhbHVlPSJhIj48bGFiZWw+YTwvbGFiZWw+PGJyPicrCiAgJzxpbnB1dCB0eXBlPSJyYWRpbyIgaWQ9IlE0IiBuYW1lPSJRNCIgdmFsdWU9ImIiPjxsYWJlbD5iPC9sYWJlbD48YnI+JysKICAnPGlucHV0IHR5cGU9InJhZGlvIiBpZD0iUTQiIG5hbWU9IlE0IiB2YWx1ZT0iYyI+PGxhYmVsPmM8L2xhYmVsPjxicj4nKwogICc8cD5RdWVzdGlvbiA1PC9wPicrCiAgJzxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9IlE1IiBuYW1lPSJRNSIgdmFsdWU9ImEiPjxsYWJlbD5hPC9sYWJlbD48YnI+JysKICAnPGlucHV0IHR5cGU9ImNoZWNrYm94IiBpZD0iUTUiIG5hbWU9IlE1IiB2YWx1ZT0iYiI+PGxhYmVsPmI8L2xhYmVsPjxicj4nKwogICc8aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJRNSIgbmFtZT0iUTQiIHZhbHVlPSJjIj48bGFiZWw+YzwvbGFiZWw+PGJyPicrCiAgJzxwPlF1ZXN0aW9uIDY8L3A+JysKICBgPHRleHRhcmVhIGlkPSJmZWVkYmFja19jb21tZW50cyIgbmFtZT0iZmVlZGJhY2tfY29tbWVudHMiIHJvd3M9IjQiIGNvbHM9IjUwIi8+PC90ZXh0YXJlYT5gKwogICc8L2JyPjwvZGl2PicsCiAgYnV0dG9uX2xhYmVsOiAnbmV4dCcsCiAgYXV0b2ZvY3VzOiAnUTEnCn07CgpgYGAKCldlIHdpbGwgc3RvcmUgdGhlIHRyaWFsIGFzIGEgYHZhcmAgY2FsbGVkIGBkZW1vZ3JhcGhpY3NfcGFnZWAuCgpJbiB0aGUgZXhhbXBsZSBhYm92ZSB0aGVyZSBhcmUgNCBkaWZmZXJlbnQgcGFyYW1ldGVyczoKCmB0eXBlYCAtIHRoaXMgaXMgd2hlcmUgd2Ugc3BlY2lmeSB0aGUgYGpzUHN5Y2hTdXJ2ZXlIdG1sRm9ybWAgcGx1Z2luCgpgaHRtbGAgLSB0aGlzIGlzIHdoZXJlIHdlIHdyaXRlIHRoZSBzdXJ2ZXkgY29kZSB1c2luZyBodG1sCgpgYnV0dG9uX2xhYmVsYCAtIHRoaXMgaXMgd2hlcmUgd2UgZ2l2ZSB0aGUgdGV4dCBpbiB0aGUgYnV0dG9uCgpgYXV0b2ZvY3VzYCAtIHRoaXMgaXMgd2hlcmUgd2UgY2FuIHNwZWNpZnkgdGhlIGZvY3VzIHRvIGJlIG9uIGEgc3BlY2lmaWMgcXVlc3Rpb24gd2hlbiB0aGUgcGFnZSBsb2Fkcy4gSXQgdGFrZXMgdGhlIHZhbHVlIGZyb20gdGhlIGBpZGAgb2YgYSBxdWVzdGlvbiBpbiB5b3VyIGh0bWwgc2VjdGlvbgoKSW4gb3JkZXIgdG8gdW5kZXJzdGFuZCBob3cgdGhlIGh0bWwgd29ya3MsIHdlIHdpbGwgbG9vayBhdCBlYWNoIHBhcnQgb2YgdGhlIGNvZGUuIE5vdGUgdGhhdCB0aGUgZW5kIG9mIGVhY2ggbGluZSB0aGVyZSBpcyBhIGArYCB0aGlzIG1lYW5zIHRoYXQgaXQgd2lsbCBpbnRlcnByZXQgdGhlIGxpbmVzIGFzIG9uZSBiaWcgaHRtbCBjaHVuay4gVGhpcyBpcyBzaW1wbHkgdG8gbWFrZSBpdCBlYXNpZXIgdG8gcmVhZCB0aGUgaHRtbC4KClRoZSBmaXJzdCBwYXJ0IG9mIHRoZSBodG1sIHNlY3Rpb24gaXMgYSBgPGRpdj5gIHdoZXJlIHdlIHNwZWNpZnkgc29tZSBjc3MuIEFsbCBxdWVzdGlvbnMgYXJlIHdpdGhpbiB0aGlzIGA8ZGl2PmAgc28gYWxsIG9mIHRoZW0gaGF2ZSB0aGUgY3NzIGFwcGxpZWQuCgpgJzxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7Ij4nK2AKCkhlcmUgd2UgdXNlIGB0ZXh0LWFsaWduOiBsZWZ0O2AgdG8gbWFrZSBzdXJlIGFsbCB0aGUgdGV4dCBpcyBsZWZ0IGFsaWduZWQKCkluIHRoZSBhYm92ZSBjb2RlIHdlIGhhdmUgNSBxdWVzdGlvbnMgd3JpdHRlbiBpbiB0aGUgYGh0bWxgIHNlY3Rpb246CgojIyBRdWVzdGlvbiAxIC0gdGV4dCBpbnB1dAoKYGBge3J9Cic8cD5RdWVzdGlvbiAxIDwvcD48aW5wdXQgaWQ9IlExIiBuYW1lPSJRMSIgdHlwZT0idGV4dCIgLz4nCgpgYGAKCkhlcmUgd2Ugc3RhcnQgd2l0aCBhIGA8cD5gIHRhZywgd2hlcmUgd2UgcHV0IHRoZSBxdWVzdGlvbiB0ZXh0LCBpLmUuIFF1ZXN0aW9uIDEKCldlIHRoZW4gaGF2ZSBhbiBgPGlucHV0PmAgdGFnLCB0aGlzIG1lYW5zIHdlIGFyZSBleHBlY3Rpbmcgc29tZSBmb3Igb2YgaW5wdXQgcmVzcG9uc2UKCldlIGdpdmUgdGhlIGlucHV0IGFuIGBpZGAsIHRoaXMgc2hvdWxkIGJlIHVuaXF1ZSBmb3IgdGhlIHNwZWNpZmljIHF1ZXN0aW9uLCBlLmcuICJRMSIKCldlIGFsc28gZ2l2ZSBpdCBhIGBuYW1lYCwgYWdhaW4gdGhpcyBzaG91bGQgYmUgdW5pcXVlIGZvciB0aGUgc3BlY2lmaWMgcXVlc3Rpb24sIGUuZy4gIlExIiwgdGhpcyBpcyBpbXBvcnRhbnQgZm9yIHdoZW4gd2UgZ2V0IHRoZSBkYXRhIGFzIHRoZSBkYXRhIHdpbGwgc3RvcmUgdGhlIG5hbWUgYW5kIHRoZSByZXNwb25zZSBvbmx5CgpGaW5hbGx5IHdlIHNwZWNpZnkgdGhlIGB0eXBlYCBvZiBpbnB1dCwgaGVyZSB3ZSB1c2UgYCJ0ZXh0ImAsIHdoaWNoIHdpbGwgZ2l2ZSB1cyBhIHRleHQgaW5wdXQgZmllbGQsIHRoZSBpbnB1dCBwcm92aWRlZCBieSB0aGUgcGFydGljaXBhbnQgd2lsbCBiZSBzdG9yZWQgaW4gdGhlIGRhdGEKCiMjIFF1ZXN0aW9uIDIgLSBudW1iZXIgaW5wdXQKCmBgYHtqc30KJzxwPlF1ZXN0aW9uIDIgPC9wPjxpbnB1dCBpZD0iUTIiIG5hbWU9IlEyIiB0eXBlPSJudW1iZXIiIC8+JwoKYGBgCgpUaGlzIHF1ZXN0aW9uIGlzIHNpbWlsYXIgdG8gUXVlc3Rpb24gMSwgYnV0IHdlIGhhdmUgY2hhbmdlZCB0aGUgdHlwZSB0byBgIm51bWJlciJgIHNvIG9ubHkgbnVtZXJpYyB2YWx1ZXMgYXJlIGFsbG93ZWQKCiMjIFF1ZXN0aW9uIDMgLSBlbWFpbCBpbnB1dDoKCmBgYHtqc30KJzxwPlF1ZXN0aW9uIDMgPC9wPjxpbnB1dCBpZD0iUTMiIG5hbWU9IlEzIiB0eXBlPSJlbWFpbCIgLz4nCgpgYGAKClRoaXMgcXVlc3Rpb24gaXMgc2ltaWxhciB0byBRdWVzdGlvbiAxLCBidXQgd2UgaGF2ZSBjaGFuZ2VkIHRoZSB0eXBlIHRvIGAiZW1haWwiYCBzbyBvbmx5IHZhbHVlcyBjb250YWluaW5nIGFuIGBAYCBhcmUgYWxsb3dlZAoKIyMgUXVlc3Rpb24gNCAtIHJhZGlvIGJ1dHRvbiBpbnB1dAoKYGBge2pzfQonPHA+UXVlc3Rpb24gNDwvcD4nKwogICc8aW5wdXQgdHlwZT0icmFkaW8iIGlkPSJRNCIgbmFtZT0iUTQiIHZhbHVlPSJhIj48bGFiZWw+YTwvbGFiZWw+PGJyPicrCiAgJzxpbnB1dCB0eXBlPSJyYWRpbyIgaWQ9IlE0IiBuYW1lPSJRNCIgdmFsdWU9ImIiPjxsYWJlbD5iPC9sYWJlbD48YnI+JysKICAnPGlucHV0IHR5cGU9InJhZGlvIiBpZD0iUTQiIG5hbWU9IlE0IiB2YWx1ZT0iYyI+PGxhYmVsPmM8L2xhYmVsPjxicj4nKwogIApgYGAKClRoaXMgcXVlc3Rpb24gdXNlIGAicmFkaW8iYCBhcyB0aGUgdHlwZSwgbWVhbmluZyB5b3UgZ2V0IGEgcmFkaW8gYnV0dG9uIHNlbGVjdGlvbiBhcyB0aGUgcmVzcG9uc2UuCgpBcyB3ZWxsIGFzIHRoZSBgaWRgIGFuZCBgbmFtZWAgb3B0aW9ucywgd2UgYWxzbyBpbmNsdWRlIGEgYHZhbHVlYC4gVGhlIHZhbHVlIGlzIHRoZSBpbmZvcm1hdGlvbiB0aGF0IHdpbGwgYmUgc3RvcmVkIGluIHlvdXIgZGF0YSwgc28gaWYgdGhlIHBhcnRpY2lwYW50IGNob29zZXMgdGhlIGZpcnN0IGJ1dHRvbiwgdGhleSB3aWxsIGhhdmUgaW4gdGhlaXIgZGF0YSBgIlE0IjoiYSJgLCBhcyB0aGV5IGNob3NlIHRoZSAiYSIgb3B0aW9uIGZvciB0aGlzIHF1ZXN0aW9uLgoKVGhlcmUgaXMgYSBgPGxhYmVsPmAgdGFnLCB0aGlzIGlzIGltcG9ydGFudCBhcyBpdCB3aWxsIHByb3ZpZGUgdGhlIHRleHQgdGhhdCB0aGUgcGFydGljaXBhbnQgc2VlcyBuZXh0IHRvIHRoZSByYWRpbyBidXR0b24uIFNvIGA8bGFiZWw+YTwvbGFiZWw+YCBpcyB0aGUgcGFydCBvZiB0aGUgY29kZSB0aGF0IGlzIHNlZW4sIGJ1dCB0aGUgYHZhbHVlYCBpcyB3aGF0IGlzIHN0b3JlZCBpbiB0aGUgZGF0YS4KCiMjIFF1ZXN0aW9uIDUgLSBjaGVja2JveCBpbnB1dAoKYGBge2pzfQonPHA+UXVlc3Rpb24gNTwvcD4nKwogICc8aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJRNSIgbmFtZT0iUTUiIHZhbHVlPSJhIj48bGFiZWw+YTwvbGFiZWw+PGJyPicrCiAgJzxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9IlE1IiBuYW1lPSJRNSIgdmFsdWU9ImIiPjxsYWJlbD5iPC9sYWJlbD48YnI+JysKICAnPGlucHV0IHR5cGU9ImNoZWNrYm94IiBpZD0iUTUiIG5hbWU9IlE0IiB2YWx1ZT0iYyI+PGxhYmVsPmM8L2xhYmVsPjxicj4nKwogIApgYGAKClRoaXMgaXMgc2ltaWxhciB0byBRdWVzdGlvbiA0LCBidXQgaW5zdGVhZCBvZiByYWRpbyBidXR0b25zIHdlIGhhdmUgYGNoZWNrYm94YCBhcyB0aGUgdHlwZSwgbWVhbmluZyB0aGVyZSBhcmUgY2hlY2tib3hlcyBhcyB0aGUgaW5wdXQuCgpOb3cgdGhlIHBhcnRpY2lwYW50IGNhbiBjaG9vc2UgbXVsdGlwbGUgb3B0aW9ucy4KCiMjIFF1ZXN0aW9uIDYgLSB0ZXh0YXJlYQoKYGBge2pzfQonPHA+UXVlc3Rpb24gNjwvcD4nKwogIGA8dGV4dGFyZWEgaWQ9IlE2IiBuYW1lPSJRNiIgcm93cz0iNCIgY29scz0iNTAiLz48L3RleHRhcmVhPmArCiAgCmBgYAoKVGhpcyBxdWVzdGlvbiBpcyBzaW1pbGFyIHRvIFF1ZXN0aW9uIDEsIGluIHRoYXQgaXQgaXMgYSB0ZXh0IGlucHV0LCBidXQgaW5zdGVhZCBvZiB1c2luZyBhbiBgPGlucHV0PmAgdGFnLCB3ZSB1c2UgdGhlIGA8dGV4dGFyZWE+YCB0YWcsIHdoaWNoIGFsbG93cyB1cyB0byBzcGVjaWZ5IGFkZGl0aW9uYWwgb3B0aW9uczoKCmByb3dzPSI0ImAgbWVhbnMgdGhlcmUgYXJlIDQgcm93cywgb3IgdGhlIGhlaWdodCBvZiB0aGUgYm94IGlzIDQgcm93cwoKYGNvbHM9IjUwImAgbWVhbnMgdGhlcmUgYXJlIDUwIGNvbHVtbnMsIG9yIHRoZSB3aWR0aCBvZiB0aGUgYm94IGlzIDUwIGNvbHVtbnMKCiMjIHJlcXVpcmVkIHJlc3BvbnNlcwoKSWYgd2Ugd2FudCB0byBtYWtlIGEgcXVlc3Rpb24gYSBmb3JjZWQgcmVzcG9uc2UsIGkuZS4gdGhlIHBhcnRpY2lwYW50IGNhbiBub3QgbGVhdmUgaXQgYmxhbmssIHRoZW4gd2UgdXNlIHRoZSBgcmVxdWlyZWRgIG9wdGlvbi4KClRoaXMgaXMgcGxhY2VkIHdpdGhpbiB0aGUgYDxpbnB1dD5gIHRhZy4KClNvIGZvciBRdWVzdGlvbiAxIHdlIHdvdWxkIHVzZToKCmBgYHtqc30KJzxwPlF1ZXN0aW9uIDEgPC9wPjxpbnB1dCBpZD0iUTEiIG5hbWU9IlExIiB0eXBlPSJ0ZXh0IiByZXF1aXJlZC8+JwoKYGBgCgpUaGlzIG5vdyBtZWFucyB0aGUgcXVlc3Rpb24gd2lsbCBnaXZlIGEgd2FybmluZyB3aGVuIHRoZSBwYXJ0aWNpcGFudCB0cmllcyB0byBjbGljayBvbiBuZXh0LCBzYXlpbmcgdGhhdCB0aGV5IGhhdmUgdG8gcHJvdmlkZSBhIHJlc3BvbnNlLgoKIyBGZWVkYmFjayBmb3JtCgpBZnRlciB0aGUgbWFpbiBleHBlcmltZW50IGhhcyBmaW5pc2hlZCwgd2UgbmVlZCB0byBsZXQgdGhlIHBhcnRpY2lwYW50IGtub3cgdGhhdCB0aGV5IGhhdmUgY29tcGxldGVkIHRoZSBleHBlcmltZW50LgoKVG8gZG8gdGhpcywgd2UgY2FuIG1ha2UgYSBmZWVkYmFjayBmb3JtLCB0aGF0IHdpbGwgcHJlc2VudCBhIG1lc3NhZ2UgdG8gdGhlIHBhcnRpY2lwYW50IGFuZCBhc2sgc29tZSBiYXNpYyBxdWVzdGlvbnMuCgpUbyBkbyB0aGlzIHdlIHdpbGwgdXNlIHRoZSBgc3VydmV5LWh0bWwtZm9ybWAgcGx1Z2luLCBsaWtlIHdlIGRpZCBmb3IgdGhlIGRlbW9ncmFwaGljIGluZm9ybWF0aW9uLgoKYGBge2pzfQp2YXIgZmVlZGJhY2tfcGFnZSA9IHsKICB0eXBlOiBqc1BzeWNoU3VydmV5SHRtbEZvcm0sCiAgaHRtbDogJzxkaXYgc3R5bGU9InRleHQtYWxpZ246IGxlZnQ7Ij4nKwogICc8cD5UaGF0IGlzIHRoZSBlbmQgb2YgdGhlIGV4cGVyaW1lbnQuPC9wPicrCiAgJzxwPkhvdyBlYXN5IGRpZCB5b3UgZmluZCB0aGUgZXhwZXJpbWVudD8gPC9wPicrCiAgJzxpbnB1dCB0eXBlPSJyYWRpbyIgaWQ9ImZlZWRiYWNrX2Vhc2UiIG5hbWU9ImZlZWRiYWNrX2Vhc2UiIHZhbHVlPSJkaWZmaWN1bHQiPjxsYWJlbD5kaWZmaWN1bHQ8L2xhYmVsPjxicj4nKwogICc8aW5wdXQgdHlwZT0icmFkaW8iIGlkPSJmZWVkYmFja19lYXNlIiBuYW1lPSJmZWVkYmFja19lYXNlIiB2YWx1ZT0iZWFzeSI+PGxhYmVsPmVhc3k8L2xhYmVsPjxicj4nKwogICc8cD5JZiB5b3UgaGF2ZSBhbnkgZmVlZGJhY2sgYWJvdXQgdGhlIGV4cGVyaW1lbnQsIHBsZWFzZSB3cml0ZSB5b3VyIGNvbW1lbnRzIGJlbG93LjwvcD4nKwogIGA8dGV4dGFyZWEgaWQ9ImZlZWRiYWNrX2NvbW1lbnRzIiBuYW1lPSJmZWVkYmFja19jb21tZW50cyIgcm93cz0iNCIgY29scz0iNTAiLz48L3RleHRhcmVhPmArCiAgJzwvYnI+PC9kaXY+JywKICBidXR0b25fbGFiZWw6ICdTdWJtaXQgcmVzdWx0cycsCiAgYXV0b2ZvY3VzOiAnZmVlZGJhY2tfZWFzZScKfTsKCmBgYAoKIyBUaGUgd2hvbGUgZXhwZXJpbWVudAoKTm93IHdlIGhhdmUgdGhlIGZvbGxvd2luZyBjb21wb25lbnRzOgoKLSBmdWxsIHNjcmVlbiBtb2RlCi0gaW5zdHJ1Y3Rpb25zIHBhZ2UKLSBjb25zZW50IHBhZ2UKLSBkZW1vZ3JhcGhpYyBmb3JtCi0gZmVlZGJhY2sgZm9ybQoKSWYgd2UgY29tYmluZSB0aGlzIHdpdGggdGhlIGxleGljYWwgZGVjaXNpb24gZXhwZXJpbWVudCB3ZSBtYWRlIGluIHRoZSBwcmV2aW91cyBzZWN0aW9uLCB3ZSB3b3VsZCBoYXZlIHNvbWV0aGluZyBsaWtlIHRoaXM6CgpgYGB7anN9Ci8vIC0tLS0tLS0tCi8vIFRpdGxlOiBEZW1vIGV4cGVyaW1lbnQKLy8ganNQc3ljaCB2ZXJzaW9uOiA3LjMuMQovLyBkYXRlOiBbdG9kYXldCi8vIGF1dGhvcjogW3lvdXIgbmFtZV0KLy8tLS0tLS0tLS0tCgovLyBpbml0aXRhdGUganNwc3ljaAp2YXIganNQc3ljaCA9IGluaXRKc1BzeWNoKCk7CgovLyBzZXQgdXAgbWFpbiB0aW1lbGluZQp2YXIgdGltZWxpbmUgPSBbXTsKCnZhciBlbnRlcl9mdWxsc2NyZWVuID0gewogIHR5cGU6IGpzUHN5Y2hGdWxsc2NyZWVuLAogIGZ1bGxzY3JlZW5fbW9kZTogdHJ1ZSwKICBtZXNzYWdlOiAnPHA+c29tZSBtZXNzYWdlPC9wPicsCiAgYnV0dG9uX2xhYmVsOiAnZW50ZXIgZnVsbCBzY3JlZW4gbW9kZScKfTsKCnZhciBpbnN0cnVjdGlvbnNfcGFnZSA9IHsKICAgIHR5cGU6IGpzUHN5Y2hJbnN0cnVjdGlvbnMsCiAgICBwYWdlczogWwogICAgJzxpbWcgc3JjPSJodHRwczovL3NpdGVzMi5mZi5jdW5pLmN6L3Nzb2wvd3AtY29udGVudC91cGxvYWRzL3NpdGVzLzEwMy8yMDIzLzA2L2Nyb3BwZWQtbmF2cmhfd2lkZXIucG5nIiB3aWR0aD0iNDAwIiBoZWlnaHQ9IjE0MCI+PC9icj48aHI+PGRpdiBzdHlsZT0idGV4dC1hbGlnbjogbGVmdDsgbWFyZ2luLXJpZ2h0OiAxNTBweDsgbWFyZ2luLWxlZnQ6IDE1MHB4OyI+PGgyPkluc3RydWN0aW9uczwvaDI+PHA+SW4gdGhpcyBleHBlcmltZW50IHlvdSB3aWxsIHNlZSBhIHdvcmQgb24gdGhlIHNjcmVlbi48L3A+PHA+PGI+SWYgeW91IHRoaW5rIHRoZSB3b3JkIGlzIGEgcmVhbCB3b3JkIHByZXNzIHRoZSAiZCIga2V5LCBpZiB5b3UgZG8gbm90IHRoaW5rIGl0IGlzIGEgcmVhbCB3b3JkLCBwcmVzcyB0aGUgImgiIGtleS48L2I+PC9wPjxwPlByZXNzIG5leHQgdG8gY29udGludWUuPC9kaXY+JywKICAgICdJbiB0aGlzIGV4cGVyaW1lbnQgeW91IHdpbGwuLi4nCiAgICBdLAogICAgc2hvd19jbGlja2FibGVfbmF2OiB0cnVlLAogICAga2V5X2ZvcndhcmQ6ICdFbnRlcicsCiAgICBrZXlfYmFja3dhcmQ6ICdBcnJvd0xlZnQnLAogICAgYWxsb3dfYmFja3dhcmQ6IHRydWUsCiAgICBzaG93X2NsaWNrYWJsZV9uYXY6IHRydWUsCiAgICBidXR0b25fbGFiZWxfcHJldmlvdXM6ICdiYWNrJywKICAgIGJ1dHRvbl9sYWJlbF9uZXh0OiAnbmV4dCcKfTsKCnZhciBjb25zZW50X3BhZ2UgPSB7CiAgICB0eXBlOiBqc1BzeWNoSW5zdHJ1Y3Rpb25zLAogICAgcGFnZXM6IFsKICAgICdEZXRhaWxzIG9mIGluZm9ybWVkIGNvbnNlbnQuLi4nLAogICAgXSwKICAgIHNob3dfY2xpY2thYmxlX25hdjogdHJ1ZSwKICAgIGFsbG93X2JhY2t3YXJkOiBmYWxzZSwKICAgIGJ1dHRvbl9sYWJlbF9uZXh0OiAnSSBjb25zZW50IHRvIHBhcnRpY2lwYXRpbmcgaW4gdGhpcyBleHBlcmltZW50Jwp9OwoKICB2YXIgZGVtb2dyYXBoaWNzX3BhZ2UgPSB7CiAgdHlwZToganNQc3ljaFN1cnZleUh0bWxGb3JtLAogIGh0bWw6ICc8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyI+JysKICAnPHA+UXVlc3Rpb24gMSA8L3A+PGlucHV0IGlkPSJRMSIgbmFtZT0iUTEiIHR5cGU9InRleHQiIHJlcXVpcmVkLz4nKwogICc8cD5RdWVzdGlvbiAyIDwvcD48aW5wdXQgaWQ9IlEyIiBuYW1lPSJRMiIgdHlwZT0ibnVtYmVyIiAvPicrCiAgJzxwPlF1ZXN0aW9uIDMgPC9wPjxpbnB1dCBpZD0iUTMiIG5hbWU9IlEzIiB0eXBlPSJlbWFpbCIgLz4nKwogICc8cD5RdWVzdGlvbiA0PC9wPicrCiAgJzxpbnB1dCB0eXBlPSJyYWRpbyIgaWQ9IlE0IiBuYW1lPSJRNCIgdmFsdWU9ImEiPjxsYWJlbD5hPC9sYWJlbD48YnI+JysKICAnPGlucHV0IHR5cGU9InJhZGlvIiBpZD0iUTQiIG5hbWU9IlE0IiB2YWx1ZT0iYiI+PGxhYmVsPmI8L2xhYmVsPjxicj4nKwogICc8aW5wdXQgdHlwZT0icmFkaW8iIGlkPSJRNCIgbmFtZT0iUTQiIHZhbHVlPSJjIj48bGFiZWw+YzwvbGFiZWw+PGJyPicrCiAgJzxwPlF1ZXN0aW9uIDU8L3A+JysKICAnPGlucHV0IHR5cGU9ImNoZWNrYm94IiBpZD0iUTUiIG5hbWU9IlE1IiB2YWx1ZT0iYSI+PGxhYmVsPmE8L2xhYmVsPjxicj4nKwogICc8aW5wdXQgdHlwZT0iY2hlY2tib3giIGlkPSJRNSIgbmFtZT0iUTUiIHZhbHVlPSJiIj48bGFiZWw+YjwvbGFiZWw+PGJyPicrCiAgJzxpbnB1dCB0eXBlPSJjaGVja2JveCIgaWQ9IlE1IiBuYW1lPSJRNCIgdmFsdWU9ImMiPjxsYWJlbD5jPC9sYWJlbD48YnI+JysKICAnPHA+UXVlc3Rpb24gNjwvcD4nKwogIGA8dGV4dGFyZWEgaWQ9ImZlZWRiYWNrX2NvbW1lbnRzIiBuYW1lPSJmZWVkYmFja19jb21tZW50cyIgcm93cz0iNCIgY29scz0iNTAiLz48L3RleHRhcmVhPmArCiAgJzwvYnI+PC9kaXY+JywKICBidXR0b25fbGFiZWw6ICduZXh0JywKICBhdXRvZm9jdXM6ICdRMScKfTsKCnZhciBsZXhpY2FsX2RlY2lzaW9uX3RyaWFsID0gewogIHR5cGU6IGpzUHN5Y2hIdG1sS2V5Ym9hcmRSZXNwb25zZSwKICBzdGltdWx1czoganNQc3ljaC50aW1lbGluZVZhcmlhYmxlKCJpdGVtIiksCiAgY2hvaWNlczogWydkJywgJ2gnXSwKICBwcm9tcHQ6ICc8ZGl2IHN0eWxlPSJmb250LXNpemU6MTJwdDsgcG9zaXRpb246cmVsYXRpdmU7IGJvdHRvbToxNTBweDsiPnByZXNzICJkIiBpZiB5b3UgdGhpbmsgaXQgaXMgYSByZWFsIHdvcmQsIHByZXNzICJoIiBpZiB5b3UgdGhpbmsgaXQgaXMgbm90IGEgcmVhbCB3b3JkPC9kaXY+JywKICBkYXRhOiB7CiAgICB0YXNrOiAnbGV4aWNhbF9kZWNpc2lvbicsCiAgICByZWFsX3dvcmQ6IGpzUHN5Y2gudGltZWxpbmVWYXJpYWJsZSgncmVhbF93b3JkJyksCiAgICBjb3JyZWN0X2tleToganNQc3ljaC50aW1lbGluZVZhcmlhYmxlKCdjb3JyZWN0X2tleScpCiAgICB9Cn07Cgp2YXIgZml4YXRpb25fdHJpYWwgPSB7CiAgdHlwZToganNQc3ljaEh0bWxLZXlib2FyZFJlc3BvbnNlLAogIHN0aW11bHVzOiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjMwcHQ7Ij4rPC9kaXY+JywKICBjaG9pY2VzOiAnTk9fS0VZUycsCiAgcHJvbXB0OiAnPGRpdiBzdHlsZT0iZm9udC1zaXplOjEwcHQ7IHBvc2l0aW9uOnJlbGF0aXZlOyBib3R0b206MTUwcHg7Ij48YnIvPjwvZGl2PicsCiAgdHJpYWxfZHVyYXRpb246IDUwMCwKICBkYXRhOiB7CiAgICB0YXNrOiAnZml4YXRpb24nCiAgfQp9OwoKdmFyIGxleGljYWxfZGVjaXNpb25fY29tYmluZWQgPSB7CiAgdGltZWxpbmU6IFtmaXhhdGlvbl90cmlhbCwgbGV4aWNhbF9kZWNpc2lvbl90cmlhbF0sCiAgdGltZWxpbmVfdmFyaWFibGVzOiBzdGltdWxpLAogIHJhbmRvbWl6ZV9vcmRlcjogdHJ1ZQp9OwoKdmFyIGZlZWRiYWNrX3BhZ2UgPSB7CiAgdHlwZToganNQc3ljaFN1cnZleUh0bWxGb3JtLAogIGh0bWw6ICc8ZGl2IHN0eWxlPSJ0ZXh0LWFsaWduOiBsZWZ0OyI+JysKICAnPHA+VGhhdCBpcyB0aGUgZW5kIG9mIHRoZSBleHBlcmltZW50LjwvcD4nKwogICc8cD5Ib3cgZWFzeSBkaWQgeW91IGZpbmQgdGhlIGV4cGVyaW1lbnQ/IDwvcD4nKwogICc8aW5wdXQgdHlwZT0icmFkaW8iIGlkPSJmZWVkYmFja19lYXNlIiBuYW1lPSJmZWVkYmFja19lYXNlIiB2YWx1ZT0iZGlmZmljdWx0Ij48bGFiZWw+ZGlmZmljdWx0PC9sYWJlbD48YnI+JysKICAnPGlucHV0IHR5cGU9InJhZGlvIiBpZD0iZmVlZGJhY2tfZWFzZSIgbmFtZT0iZmVlZGJhY2tfZWFzZSIgdmFsdWU9ImVhc3kiPjxsYWJlbD5lYXN5PC9sYWJlbD48YnI+JysKICAnPHA+SWYgeW91IGhhdmUgYW55IGZlZWRiYWNrIGFib3V0IHRoZSBleHBlcmltZW50LCBwbGVhc2Ugd3JpdGUgeW91ciBjb21tZW50cyBiZWxvdy48L3A+JysKICBgPHRleHRhcmVhIGlkPSJmZWVkYmFja19jb21tZW50cyIgbmFtZT0iZmVlZGJhY2tfY29tbWVudHMiIHJvd3M9IjQiIGNvbHM9IjUwIi8+PC90ZXh0YXJlYT5gKwogICc8L2JyPjwvZGl2PicsCiAgYnV0dG9uX2xhYmVsOiAnU3VibWl0IHJlc3VsdHMnLAogIGF1dG9mb2N1czogJ2ZlZWRiYWNrX2Vhc2UnCn07Cgp0aW1lbGluZS5wdXNoKGVudGVyX2Z1bGxzY3JlZW4pOwp0aW1lbGluZS5wdXNoKGluc3RydWN0aW9uc19wYWdlKTsKdGltZWxpbmUucHVzaChjb25zZW50X3BhZ2UpOwp0aW1lbGluZS5wdXNoKGRlbW9ncmFwaGljc19wYWdlKTsKCnRpbWVsaW5lLnB1c2gobGV4aWNhbF9kZWNpc2lvbl9jb21iaW5lZCk7Cgp0aW1lbGluZS5wdXNoKGZlZWRiYWNrX3BhZ2UpOwoKLy9ydW4gdGhlIGV4cGVyaW1lbnQKanNQc3ljaC5ydW4odGltZWxpbmUpOwoKYGBgCgojIERpc3RyaWJ1dGluZyBsaW5rcwoKV2UgY2FuIG5vdyBnbyBiYWNrIHRvIHRoZSBleHBlcmltZW50IGRhc2hib2FyZCBpbiBjb2duaXRpb24ucnVuIGJ5IGNsaWNraW5nIG9uIHRoZSBgZGVtb2AgYnV0dG9uLiBUaGlzIG1pZ2h0IGxvb2sgZGlmZmVyZW50IGlmIHlvdSBuYW1lZCB5b3VyIGV4cGVyaW1lbnQgc29tZXRoaW5nIGVsc2UuCgohW10oaW1hZ2VzL2Rhc2hib2FyZDEucG5nKQoKV2UgY2FuIG5vdyB1c2UgdGhlIGBsaW5rYCBvcHRpb24gdG8gc2VlIGhvdyB0aGUgZXhwZXJpbWVudCBsb29rcyBpbiB0aGUgbGl2ZSB2ZXJzaW9uLgoKIVtdKGltYWdlcy9kYXNoYm9hcmQyLnBuZykKCldoZW4geW91IGNsaWNrIG9uIHRoZSBsaW5rIHlvdSBzaG91bGQgc2VlIGEgbmV3IHRhYiBvcGVuIHRoYXQgaGFzIHlvdXIgd29ya2luZyBleHBlcmltZW50LgoKWW91IGNhbiBkaXN0cmlidXRlIHRoaXMgbGluayB0byBwYXJ0aWNpcGFudHMgYW5kIGNvbGxlY3QgeW91ciBkYXRhLgoKT25jZSB0aGUgcGFydGljaXBhbnQgaGFzIGNvbXBsZXRlZCB0aGUgZXhwZXJpbWVudCB5b3Ugd2lsbCBzZWUgdGhlaXIgZGF0YSBhcHBlYXIgaW4gdGhlIGRhc2hib2FyZC4KCiFbXShpbWFnZXMvZGFzaGJvYXJkMy5wbmcpCgpFYWNoIHBhcnRpY2lwYW50IHdpbGwgaGF2ZSBhIHN0b3JlZCBkYXRhIGZpbGUgaW4gY29nbml0aW9uLnJ1biBhbmQgaWYgeW91IGFyZSBjb2xsZWN0aW5nIGRhdGEgZnJvbSBsb3RzIG9mIHBhcnRpY2lwYW50cyB5b3UgY2FuIGRvd25sb2FkIGFsbCB0aGUgZGF0YSBieSBjbGlja2luZyBgRG93bmxvYWQgZGF0YWAuCgpZb3Ugd2lsbCB0aGVuIHNlZSBhIHNjcmVlbiBhc2tpbmc6CgpgRGF0YSBzZXRgOiBUaGlzIGlzIGVpdGhlciBhbGwgcnVucywgZmluaXNoZWQgcnVucyBvciBub24tZmluaXNoZWQgcnVucywgdHlwaWNhbGx5IHlvdSB3YW50IG9ubHkgdGhlIGZpbmlzaGVkIHJ1bnMuCgpgRGF0YSBlbmNvZGluZ2A6IFRoZXJlIGFyZSBhIGZldyBvcHRpb25zIGhlcmUsIGJ1dCB0eXBpY2FsbHkgeW91IHdvdWxkIHdhbnQgdGhlIGNzdiBvcHRpb24uCgpgRmlsZSBmb3JtYXRgOiBUaGlzIGdpdmVzIHlvdSB0aGUgb3B0aW9uIHRvIGRvd25sb2FkIGV2ZXJ5dGhpbmcgaW4gb25lIHNpbmdsZSBmaWxlLCBzbyBhbGwgdGhlIHBhcnRpY2lwYW50IGRhdGEgaXMgc3RvcmVkIHRvZ2V0aGVyLiBJZiB5b3UgY2hvb3NlIGEgemlwIGZvbGRlciwgdGhlcmUgd2lsbCBiZSBpbmRpdmlkdWFsIGZpbGVzIGZvciBlYWNoIHBhcnRpY2lwYW50LgoKTm90ZSwgdGhhdCB5b3UgY2FuIGlkZW50aWZ5IGRpZmZlcmVudCBwYXJ0aWNpcGFudHMgYnkgbG9va2luZyBhdCB0aGUgYHJ1bl9pZGAgY29sdW1uLCB3aGljaCBzaG91bGQgYmUgYSBudW1iZXIsIGUuZy4gMSB3b3VsZCBiZSB0aGUgZmlyc3QgcGFydGljaXBhbnQsIDIgdGhlIHNlY29uZCBldGMuCgohW10oaW1hZ2VzL2NvZ25pdGlvbl9kYXRhX2Rvd25sb2FkLmdpZikKCgo8IS0tICMgV29ya2luZyB3aXRoIHJlc3VsdHMgZmlsZXMgaW4gUiAtLT4KCjwhLS0gIyMgbG9hZGluZyBpbiBtdWx0aXBsZSBmaWxlcyBxdWlja2x5IC0tPgoKPCEtLSAjIyBnZXR0aW5nIGluZm9ybWF0aW9uIGZyb20gZGlmZmVyZW50IHBhcnRzIG9mIHRoZSBleHBlcmltZW50IC0tPgoKPCEtLSAjIyBjb252ZXJ0aW5nIGpzb24gZm9ybWF0dGVkIGRhdGEgdG8gY29sdW1ucyAtLT4KCgoKCjwhLS0gIyBSdW5uaW5nIG90aGVyIHR5cGVzIG9mIGV4cGVyaW1lbnRzIC0tPgoKPCEtLSAjIyB3b3JraW5nIHdpdGggYXVkaW8sIGltYWdlcyBhbmQgdmlkZW8gLS0+Cgo8IS0tICMjIHNlbGYgcGFjZWQgcmVhZGluZyAtLT4KCjwhLS0gIyMgcmF0aW5nIGV4cGVyaW1lbnRzIC0tPgoKPCEtLSAjIyBjb25pZHRpb25hbCBhbmQgaWYgdHJpYWxzIC0tPgoKCgpgYGB7ciBlY2hvPUZBTFNFLCBldmFsPVRSVUUsIHdhcm5pbmc9RkFMU0V9Cmh0bWx0b29sczo6dGFncyRzY3JpcHQoc3JjID0gImpzL3RyYW5zbGF0ZS5qcyIpCiMgaHRtbHRvb2xzOjp0YWdzJHNjcmlwdChzcmMgPSAianMvaW5mb2JveC5qcyIpCmh0bWx0b29sczo6dGFncyRzY3JpcHQoc3JjPSIvL3RyYW5zbGF0ZS5nb29nbGUuY29tL3RyYW5zbGF0ZV9hL2VsZW1lbnQuanM/Y2I9Z29vZ2xlVHJhbnNsYXRlRWxlbWVudEluaXQiKQoKaHRtbHRvb2xzOjp0YWdMaXN0KAogIHhhcmluZ2FuRXh0cmE6OnVzZV9jbGlwYm9hcmQoCiAgICBidXR0b25fdGV4dCA9ICI8aSBjbGFzcz1cImZhIGZhLWNsaXBib2FyZFwiIHN0eWxlPVwiZm9udC1zaXplOiAyNXB4XCI+PC9pPiIsCiAgICBzdWNjZXNzX3RleHQgPSAiPGkgY2xhc3M9XCJmYSBmYS1jaGVja1wiIHN0eWxlPVwiY29sb3I6ICM5MEJFNkQ7IGZvbnQtc2l6ZTogMjVweFwiPjwvaT4iLAogICksCiAgcm1hcmtkb3duOjpodG1sX2RlcGVuZGVuY3lfZm9udF9hd2Vzb21lKCkKKQoKYGBgCgpgYGB7anMgZWNobz1GQUxTRSwgZXZhbD1UUlVFfQp2YXIgY29sbCA9IGRvY3VtZW50LmdldEVsZW1lbnRzQnlDbGFzc05hbWUoImNvbGxhcHNpYmxlMSIpOwp2YXIgaTsKCmZvciAoaSA9IDA7IGkgPCBjb2xsLmxlbmd0aDsgaSsrKSB7CiAgY29sbFtpXS5hZGRFdmVudExpc3RlbmVyKCJjbGljayIsIGZ1bmN0aW9uKCkgewogICAgdGhpcy5jbGFzc0xpc3QudG9nZ2xlKCJhY3RpdmUxIik7CiAgICB2YXIgY29udGVudCA9IHRoaXMubmV4dEVsZW1lbnRTaWJsaW5nOwogICAgaWYgKGNvbnRlbnQuc3R5bGUubWF4SGVpZ2h0KXsKICAgICAgY29udGVudC5zdHlsZS5tYXhIZWlnaHQgPSBudWxsOwogICAgfSBlbHNlIHsKICAgICAgY29udGVudC5zdHlsZS5tYXhIZWlnaHQgPSBjb250ZW50LnNjcm9sbEhlaWdodCArICJweCI7CiAgICB9CiAgfSk7Cn0KCmBgYAo=