Introduction

What is Shiny?

The best way to have an idea of the possibilities offered by R/Shiny is to have a quick look at the following galleries (where you can find, and study, the associated R code!):

Explore now for a few minutes these galleries:

Note: Another way is to use the runExample() function. The Shiny package has eleven built-in examples that each demonstrate how Shiny works. Each example is a self-contained Shiny app. For example

library(shiny)
runExample("01_hello") # a histogram
runExample("02_text") # tables and data frames
runExample("03_reactivity") # a reactive expression
runExample("04_mpg") # global variables
runExample("05_sliders") # slider bars
runExample("06_tabsets") # tabbed panels
runExample("07_widgets") # help text and submit buttons
runExample("08_html") # Shiny app built from HTML
runExample("09_upload") # file upload wizard
runExample("10_download") # file download wizard
runExample("11_timer") # an automated timer

Due credits: in this course, we will explore material that can be found at these URL:

Prerequisites

You need a good knowledge about the R software: try this quiz to check your skills. If you want to improve your basic R skill, see for instance this book.

You also need a good knowledge about ggplot2. Download the Data Visualization Cheat Sheet.

Some basic knowledge about HTML, Javascript and CSS might help.

The Shiny (client) software can run on Windows/MacOS/Linux. Works best with Chrome.

If not already done, download and install the last version of RStudio, which appears to be 1.0.136 at the date this course is prepared (This information is found from RStudio via the menu Help/About RStudio.)

Also, from Rstudio:

install.packages("shiny")

Download the Shiny Cheat Sheet.

Your first Shiny app

Launch RStudio, then go to File/New File/Shiny Web App…

The following window pops up.

Change the entries as follows:

When you click on Create, this should open two new tabs in RStudio: ui.R and server.R. The corresponding files are stored in ~/My_First_Shiny_App. They already contain some R code that we will comment (and modify) in a while.

But, first let’s execute this code to see what happens. Select the ui.R (or the server.R) tab in RStudio. Then click on the Run App button. This should launch a web app in your favorite web browser. You can then interact with a slider to choose the number of bins to use in order to draw a histogram of the “Old Faithful Geyser Data”. Change the slider value and observe how the histogram is redrawn.

Geyser example

Geyser example

Note 1: You should also see in the console of RStudio a red message displayed. On my laptop, it is: “Listening on http://127.0.0.1:3452”. This means that in the background, RStudio launches locally a Shiny daemon web server on the port 3452. (This port number might be different on your computer.) This can be checked for example under Linux by typing the following command in a terminal and noting that the STATE is “open”:

[lafaye 15:16:51 ~] nmap localhost -p 3452

Starting Nmap 6.47 ( http://nmap.org ) at 2016-12-28 15:17 CET
Nmap scan report for localhost (127.0.0.1)
Host is up (0.000027s latency).
Other addresses for localhost (not scanned): 127.0.0.1
PORT     STATE SERVICE
3452/tcp open  unknown

Nmap done: 1 IP address (1 host up) scanned in 0.05 seconds

Note 2: Your R session will be busy while the Hello Shiny app is active, so you will not be able to run any R commands. R is monitoring the app and executing the app’s reactions. To get your R session back, hit escape or click the stop sign icon (found in the upper right corner of the RStudio console panel).

Let’s comment the R code in the ui.R/server.R files

First, note that a full description of all Shiny functions (e.g., the ones used in the code of our current versions of ui.R and server.R) is given at the Shiny reference pages. Have a look at this webpage to see how it is organized.

Some vocabulary: “A typical web application consists of a number of user interface (UI) elements, say a button or a checkbox. Each of these elements can be interacted with, for example you can push the button. This button push then triggers an action, running a piece of code. This can then change the state of the web application, for example retrieve a piece of information from the server or draw a picture in the application. This style of programming is called event-driven programming. The piece of code that is executed based on an event is called an event handler.”

The structure of Shiny apps

“Shiny apps follow typical structure of web applications. However, as a user you only have to specify which UI elements you want to show, and the underlying R code that draws a plot, shows some text, or a table. These pieces of information are stored in two interacting codes (script) stored in the following files:

  • ui.R, for the user interface (ui) elements, and
  • server.R, for the content code."

The ui.R code creates the options a user can change, and controls what is displayed on the app.

  • This code also creates a list object (named input) to store all options
  • The UI operates using “output” pieces created by the server.

Ther server.R code runs everything that responds to the changes made by the user in the app.

  • The server operates using the “input” list created by the UI, and creates an “output” list to store all rendered objects and values.
  • This code is reactive. This means that every time you change any option in the interface, shiny will scan through the server for any instance where that option is mentioned, and re run the corresponding code.

Note: As of version 0.10.2, Shiny supports single-file applications. You no longer need to build separate server.R and ui.R files for your app; you can just create a file called app.R that contains both the server and UI components. You can learn more about building a Shiny app in a single file here, however this tutorial will focus on the two file structure for building a Shiny app.

As you can see, the ui.R file starts (after some comments) with the instruction:

library(shiny)

that will load the shiny package. Next, a call to the shinyUI() function is made. This is not really necessary as can be read in the documentation of this function:

“Historically this function was used in ui.R files to register a user interface with Shiny. It is no longer required as of Shiny 0.10; simply ensure that the last expression to be returned from ui.R is a user interface. This function is kept for backwards compatibility with older applications. It returns the value that is passed to it.”

You can thus remove it. But leave the fluidPage stuff.

The fluidPage() function creates fluid page layouts. A fluid page layout consists of rows which in turn include columns. Rows exist for the purpose of making sure their elements appear on the same line (if the browser has adequate width). Columns exist for the purpose of defining how much horizontal space within a 12-unit wide grid its elements should occupy. Fluid pages scale their components in realtime to fill all available browser width.

Our current fluidPage contains two rows:

  • The first one builds a title thanks to the titlePanel() function. This row only displays “Old Faithful Geyser Data”.
  • The second one is created via the sidebarLayout() function. It contains a sidebar (on the left) and a main area. The sidebar is displayed with a distinct background color (light blue) and typically contains input controls (currently only a slider input). The main area occupies by default 2/3 of the horizontal width and typically contains outputs (currently our histogram).
Layout

Layout

The help page for this latter function is here. Have a look at it. From it, you can also access the sidebarPanel() and mainPanel() help pages.

In our case, the sidePanel contains only one UI element: a sliderInput. The mainPanel also contains one input element: a plotOutput. Look at the description of these functions here.

Note: You can run a Shiny app by giving the name of its directory to the function runApp. For example if your Shiny app is in a directory called my_app, run it with the following code:

library(shiny)
runApp("my_app")

If you would like your app to display in showcase mode, you can run runApp("my_app", display.mode = "showcase").

HTML Content

You can use one of Shiny’s HTML tag functions. These functions parallel common HTML5 tags. Let’s try out a few of them.

shiny function HTML5 equivalent creates
p() <p> A paragraph of text
h1() <h1> A first level header
h2() <h2> A second level header
h3() <h3> A third level header
h4() <h4> A fourth level header
h5() <h5> A fifth level header
h6() <h6> A sixth level header
a() <a> A hyper link
br() <br> A line break (e.g. a blank line)
div() <div> A division of text with a uniform style
span() <span> An in-line division of text with a uniform style
pre() <pre> Text ‘as is’ in a fixed width font
code() <code> A formatted block of code
img() <img> An image
strong() <strong> Bold text
em() <em> Italicized text
HTML() Directly passes a character string as HTML code

Integrating dynamic content

If only the ui.R file was present, no dynamic content would be available. This is the purpose of the server.R file to add interactivity.

At this point, we have to explain how the ui.R and server.R files communicate.

In the server.R file, we see a rendering function called renderPlot(), as well as two interesting pieces of code: output$distPlot and input$bins. Note that distPlot is the ID (outputId) used when we called the plotOutput() function, while bins is the ID (inputId) used when we called the sliderInput() function. Consult the help pages for these two functions.

It is through these ID that Shiny couples the UI elements defined in ui.R to the dynamic content in server.R.

The input variable of the shinyServer() functions contains all the UI elements, the output variable returns the dynamic content to the UI.

Exercise 1: Modify the ui.R and server.R files so as to add a textInput in the sidebarPanel (below the sliderInput) that will enable you to modify the title of the histogram. The histogram should now be created using the functions ggplot(), geom_histogram() and ggtitle() from the ggplot2 R package.

Important note on code execution

The location of code in server.R determines how often it is run, and thus impacts the performance of the app. There are three zones where code is run more or less often:

# Zone 1
library(shiny)
library(ggplot2)

shinyServer(function(input, output) {
  # Zone 2
  output$distPlot <- renderPlot({
    # Zone 3 : run each time a user changes a widget that output$map relies on
  })
})
  • Zone 1, this code is only executed once when the app is started. Loading libraries is typically done here, as are reading large static datasets.
  • Zone 2, this code is ran each time a user requests/visits the app. This can for example be used to load data for the last \(x\) days, as this will be different for each user.
  • Zone 3, this code is ran each time a change is detected in the UI. This typically contains plotting code and such, or data that is requested based on changes in the UI (date range for example). If you put your code in the wrong zone, the app’s performance can be seriously impacted. For example, if reading a large static dataset is put into Zone 3, this large dataset will be read each time the plot needs to be redrawn.

Examples/exercises below will help to clarify these notions.

Reactive expressions

Normally, Shiny automatically reruns the code in the different zones as explained above. But consider the following fictitious example where two lines were added in Zone 3:

# Zone 1
library(shiny)
library(ggplot2)

shinyServer(function(input, output) {
  # Zone 2
  output$distPlot <- renderPlot({
    # Zone 3
    dat <- get_data(input$ui_element1, input$ui_element2)
    plot(dat, input$ui_element3)
  })
})

The (hypothetical, user-defined) get_data() function is called each time any of the three UI elements is updated (since it is in Zone 3). However, if only ui_element3 is updated the data should not be reloaded (to prevent time loss). This is where reactive expressions come in, as seen below:

# Zone 1
library(ggplot2)

shinyServer(function(input, output) {
  # Zone 2
  input_data <- reactive({
    dat <- get_data(input$ui_element1, input$ui_element2)
  })
  output$distPlot <- renderPlot({
    # Zone 3
    plot(input_data(), input$ui_element3)
  })
})

We wrap the data loading in a reactive block, and in the plotting refer to the new function input_data(). The advantage of this approach is that for a reactive expression Shiny first checks if the data actually needs to be updated or not. If only ui_element3 is updated, the reactive simply returns the previously stored data (i.e., caching), and the plot is redrawn without the costly step of re-reading the data.

At this point, you might want to have a better look at the Shiny cheatsheet that you downloaded previously.

Exercises

(Solutions can be accessed here)

Exercise 2. Open a new Shiny app in RStudio (File > New project > New directory > Shiny Web Application), which is essentially a new project in itself. Then edit the ui.R and server.R files to obtain the example given below (the actual plot is of mtcars$mpg as a function of mtcars$wt). Use the ggplot2 package to make the plot. For this exercise, no interactivity is required. In the next couple of excercises you will build a visualisation app for the mtcars dataset.

Exercise 3. Add another dropdown menu in the sidebar. The two dropdown menus should now select which variables from mtcars will be respectively plotted on the \(x\) and \(y\) axes. Tip: have a look at aes_string() from ggplot2. Also, the textInput in the sidebarPanel should now be connected interactively to the actual title of the plot. And the names of the two variables selected should now be displayed above the plot in an interactive manner (“You selected variables … and …”).

Exercise 4. Add a checkbox which enables the user to choose whether or not to include a stat_smooth() call (from ggplot2 package).

Exercise 5. Add a checkbox and a dropdown menu that allow the user to toggle the use of facetting and select which variable to facet with. Tip: see for instance this web page.

In the next couple of excercises we will build a stock price visualisation app (inspired by Shiny tutorial).

Exercise 6. We will download the stock price data using getSymbols() from the quantmod package. Ensure that the value of the argument auto.assign is set to FALSE, and you can use the from and to input arguments to select time. Extract stockprice data for AAPL (Apple), YHOO (Yahoo), and GS (Goldman Sachs). Create a chart of that data by calling the candleChart() function from quantmod on the resulting object from getSymbols(). Change the theme from black to white.

Exercise 7. Create a new Shiny app, and create a ui.R file that results in this user interface:

The list of possible stock symbols is AAPL (Apple), YHOO (Yahoo), and GS (Goldman Sachs). The themes that can be selected are white and black. Select appropriate defaults for the data range.

Create a server.R file that loads the data for the selected stock symbol, and the selected data range using getSymbols() and plots it using candleChart().

Ensure that you have used a reactive expression for reading the stock data. Ensure that the theme of the graph can be changed without reloading the data.

Exercise 8. Design a Shiny app to investigate the effects of sample size \(n\), standard deviation \(sigma\), etc for the following function:

fit.true <- function(n = 100, sigma = 10, intercept = 0, slope = 5, 
                     min = -10, max = 10, legend = "topleft") {
   x <- runif(n, min = min, max = max)
   y <- rnorm(n, mean = int + slope * x, sd = sigma)
   mod <- lm(y ~ x)
   plot(x, y, pch = 20, las = 1)
   abline(a = int, b = slope)
   abline(a = mod$coef[1], b = mod$coef[2], col = 'red')
   legend(legend, c(paste("y = ", int, " + ", slope, " x, True Model", sep = ""),
      paste("y = ", round(mod$coef[1],2), " + ", round(mod$coef[2], 2), 
            " x, Estimated Model ", sep = "")), lty = c(1, 1), col = c('lack', 'red'))
}

Exercise 9. Challenge. Will you be able to retrieve automatically all data from diamondse and then recreate the same web app? If yes, send me your solution!

Exercice 10. Propose some nice app for data extracted here.

Exercise 11. Propose some nice app for data (e.g., real time) extracted here.

Custom UI

See this webpage.

You can make your own User Interface using HTML directly. In this case there is no ui.R file. The structure of files/folders should be the following:

 < application-dir >
|-- www/
    |-- css/
    |-- js/
    |-- index.html
|-- server.R

Binding between shiny inputs/outputs and HTML elements is done by:

<select name="dist">

<input type="number" name="n" value="500" min="1" max="1000" />

<pre id="summary" class="shiny-text-output"></pre>

<div id="plot" class="shiny-plot-output" style="width: 100%; height: 400px"></div>

<div id="table" class="shiny-html-output"></div>

Have a look at the index.html file in the 08_html/ subfolder of Examples/. Try to understand the connection between this file and the server.R file.

Isolate

Sometimes you don’t want things running automatically as soon as you change an input. With the previous example typing in 123 for the number of points gives a graph for \(n=1\), then \(n=12\), then \(n=123\).

We can use the actionButton to get around this.

server.R:

data <- reactive({       
   dist <- switch(input$dist,
           norm = rnorm,
           unif = runif,
           lnorm = rlnorm,
           exp = rexp,
           rnorm)

   input$goButton

   isolate(dist(input$n))
})

ui.R:

downloadButton()

Images

It is easy to insert images on your webpage:

img(src = "my_image.png", height = 72, width = 72)

Control widgets

A widget is a web element that your users can interact with. Widgets provide a way for your users to send messages to the Shiny app. We have already encountered several widgets.

The standard Shiny widgets are:

function widget
actionButton() Action Button
checkboxGroupInput() A group of check boxes
checkboxInput() A single check box
dateInput() A calendar to aid date selection
dateRangeInput() A pair of calendars for selecting a date range
fileInput() A file upload control wizard
helpText() Help text that can be added to an input form
numericInput() A field to enter numbers
radioButtons() A set of radio buttons
selectInput() A box with choices to select from
sliderInput() A slider bar
submitButton() A submit button
textInput() A field to enter text

Share your apps

You can now build a useful Shiny app, but how can you share it with others?

You have two basic options:

  1. Share your Shiny app as two files: server.R and ui.R. This is the simplest way to share an app, but it works only if your users have R (and Shiny) on their own computer (and know how to use it). Users can use these scripts to launch the app from their own R session, just like you’ve been launching the apps.

  2. Share your Shiny app as a web page. This is definitely the most user friendly way to share a Shiny app. Your users can navigate to your app through the internet with a web browser. They will find your app fully rendered, up to date, and ready to go.

Share as two R files

Anyone with R can run your Shiny app. They will need a copy of your server.R and ui.R files, as well as any supplementary materials used in your app (e.g., www folders or helpers.R files).

Share as a web page

RStudio offers three ways to host your Shiny app as a web page:

The first one is the easiest to use. It comes with several formula (from $0/month to $299/month). The second one lets you install (for free) a Shiny server on your own web server. This guide might help you. Finally, Shiny Server Pro allows you to install (not free) a Shiny Server with many more features on your own web server.

LS0tCnRpdGxlOiAiUiBTaGlueSAtIENvdXJzZSBnaXZlbiBhdCBFTlNBSSAoMjAxNykiCmF1dGhvcjogIkxhZmF5ZSBkZSBNaWNoZWF1eCBQaWVycmUiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIHBkZl9kb2N1bWVudDoKICAgIGNpdGF0aW9uX3BhY2thZ2U6IG5hdGJpYgogICAgaW5jbHVkZXM6CiAgICAgIGluX2hlYWRlcjogaGVhZGVyLnRleAogICAga2VlcF90ZXg6IHllcwogICAgdG9jOiB5ZXMKaGVhZGVyLWluY2x1ZGVzOiBcdXNlcGFja2FnZXthbXNtYXRofQotLS0KCiMgSW50cm9kdWN0aW9uCgoqKldoYXQgaXMgU2hpbnk/KioKCi0gT3BlbiBTb3VyY2VkIGJ5IFJTdHVkaW8gTm92ZW1iZXIgMjAxMgotIEEgd2F5IHRvIGNyZWF0ZSBhIHVzZXIgaW50ZXJmYWNlIGZvciBSCi0gZW5hYmxlcyBleHBlcmllbmNlZCBSIHByb2dyYW1tZXJzIHRvIHByb2R1Y2UgZ3JhcGhpY2FsIHVzZXIgaW50ZXJmYWNlcyB3aXRoIGN1c3RvbWl6ZWQgYWxnb3JpdGhtcyB1bmRlciB0aGUgaG9vZC4gIFRoaXMgZ2l2ZXMgcG9pbnQtYW5kLWNsaWNrIGZ1bmN0aW9uYWxpdHkgdG8gb3RoZXJzLCBlbmFibGluZyBjb21wbGV4IGFuYWx5c2lzIGZvciB0aG9zZSB3aG8gYXJlIG5vdCBhZHZhbmNlZCBwcm9ncmFtbWVycwotIEludGVyYWN0aXZlIHdlYiBhcHBsaWNhdGlvbnMgYXJvdW5kIHlvdXIgUiBhbmFseXNlcwotIFplcm8gSFRNTC9DU1MvSmF2YVNjcmlwdCBrbm93bGVkZ2UgaXMgcmVxdWlyZWTigKYKLSAuLi4gYnV0IGZ1bGx5IGN1c3RvbWl6YWJsZSBhbmQgZXh0ZW5zaWJsZSB3aXRoIEhUTUwvQ1NTL0phdmFTY3JpcHQKLSBNb2Rlcm4gd2ViIFVJIHdpdGggYXR0cmFjdGl2ZSBkZWZhdWx0cwotIERlc2lnbmVkIHRvIGludGVncmF0ZSB3aXRoIGV4aXN0aW5nIEphdmFTY3JpcHQgbGlicmFyaWVzCi0gVXNlcyBhIF9yZWFjdGl2ZSBwcm9ncmFtbWluZ18gbW9kZWwgd2hpY2ggYWxsb3dzIGRyYW1hdGljYWxseSBzaW1wbGVyIGNvZGUgdGhhbiB0cmFkaXRpb25hbCBVSSBvciB3ZWIgcHJvZ3JhbW1pbmcKLSBBcHBsaWNhdGlvbnMgY2FuIGJlIGRlcGxveWVkIHRvIHRoZSBjbG91ZCAoU2hpbnlBcHBzLmlvKSBvciBvbi1wcmVtaXNlcyAoU2hpbnkgU2VydmVyKSwgd2l0aCBib3RoIGZyZWUgYW5kIGNvbW1lcmNpYWwgb3B0aW9ucyBhdmFpbGFibGUKLSBWaWJyYW50IGFuZCBhY3RpdmUgY29tbXVuaXR5Ci0gV2ViIHNvY2tldHMgZm9yIGNvbW11bmljYXRpb24gYmV0d2VlbiBjbGllbnQgYW5kIHNlcnZlcgoKVGhlIGJlc3Qgd2F5IHRvIGhhdmUgYW4gaWRlYSBvZiB0aGUgcG9zc2liaWxpdGllcyBvZmZlcmVkIGJ5IFIvU2hpbnkgaXMgdG8gaGF2ZSBhIHF1aWNrIGxvb2sgYXQgdGhlIGZvbGxvd2luZyBnYWxsZXJpZXMgKHdoZXJlIHlvdSBjYW4gZmluZCwgYW5kIHN0dWR5LCB0aGUgYXNzb2NpYXRlZCBSIGNvZGUhKToKCi0gW09mZmljaWFsIFJzdHVkaW8gU2hpbnkgR2FsbGVyeV0oaHR0cDovL3NoaW55LnJzdHVkaW8uY29tL2dhbGxlcnkvKQotIFtTaG93IG1lIFNoaW55XShodHRwOi8vd3d3LnNob3dtZXNoaW55LmNvbS8pCi0gW0stbWVhbnNdKGh0dHBzOi8vamNoZW5nLnNoaW55YXBwcy5pby9rbWVhbnMvKQotIFtTdXBlciBaaXBzXShodHRwczovL2pjaGVuZy5zaGlueWFwcHMuaW8vc3VwZXJ6aXAvKSAod2l0aCBzb3VyY2U6IGh0dHBzOi8vZ2l0aHViLmNvbS9qY2hlbmc1L3N1cGVyemlwKQotIFtSYWRpYW50XShodHRwczovL3ZuaWpzLnNoaW55YXBwcy5pby9yYWRpYW50LykgKHdpdGggc291cmNlOiBodHRwczovL2dpdGh1Yi5jb20vcmFkaWFudC1yc3RhdHMpCi0gW0NSQU4gbG9nc10oaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vamNoZW5nNS9lMjgwMjUyNDBjNDFiMmZiMmM4MCkKCkV4cGxvcmUgbm93IGZvciBhIGZldyBtaW51dGVzIHRoZXNlIGdhbGxlcmllczoKCi0gSW50ZXJhY3RpdmUgdmlzdWFsaXphdGlvbnMKLSBTdGFydCBzaW1wbGUKLSBXaWRnZXRzCi0gQXBwbGljYXRpb24gbGF5b3V0Ci0gRHluYW1pYyB1c2VyIGludGVyZmFjZQotIFJlYWN0aXZlIHByb2dyYW1taW5nCi0gQWR2YW5jZWQgU2hpbnkKLSBJbnRlcmFjdGl2ZSBwbG90cwotIFNoaW55IFNlcnZlciBQcm8KLSBJbnRlcm5hdGlvbmFsaXphdGlvbgoKKipOb3RlOioqIEFub3RoZXIgd2F5IGlzIHRvIHVzZSB0aGUgYHJ1bkV4YW1wbGUoKWAgZnVuY3Rpb24uIFRoZSBTaGlueSBwYWNrYWdlIGhhcyBlbGV2ZW4gYnVpbHQtaW4gZXhhbXBsZXMgdGhhdCBlYWNoIGRlbW9uc3RyYXRlIGhvdyBTaGlueSB3b3Jrcy4gRWFjaCBleGFtcGxlIGlzIGEgc2VsZi1jb250YWluZWQgU2hpbnkgYXBwLiBGb3IgZXhhbXBsZQpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShzaGlueSkKcnVuRXhhbXBsZSgiMDFfaGVsbG8iKSAjIGEgaGlzdG9ncmFtCnJ1bkV4YW1wbGUoIjAyX3RleHQiKSAjIHRhYmxlcyBhbmQgZGF0YSBmcmFtZXMKcnVuRXhhbXBsZSgiMDNfcmVhY3Rpdml0eSIpICMgYSByZWFjdGl2ZSBleHByZXNzaW9uCnJ1bkV4YW1wbGUoIjA0X21wZyIpICMgZ2xvYmFsIHZhcmlhYmxlcwpydW5FeGFtcGxlKCIwNV9zbGlkZXJzIikgIyBzbGlkZXIgYmFycwpydW5FeGFtcGxlKCIwNl90YWJzZXRzIikgIyB0YWJiZWQgcGFuZWxzCnJ1bkV4YW1wbGUoIjA3X3dpZGdldHMiKSAjIGhlbHAgdGV4dCBhbmQgc3VibWl0IGJ1dHRvbnMKcnVuRXhhbXBsZSgiMDhfaHRtbCIpICMgU2hpbnkgYXBwIGJ1aWx0IGZyb20gSFRNTApydW5FeGFtcGxlKCIwOV91cGxvYWQiKSAjIGZpbGUgdXBsb2FkIHdpemFyZApydW5FeGFtcGxlKCIxMF9kb3dubG9hZCIpICMgZmlsZSBkb3dubG9hZCB3aXphcmQKcnVuRXhhbXBsZSgiMTFfdGltZXIiKSAjIGFuIGF1dG9tYXRlZCB0aW1lcgpgYGAKCgpEdWUgY3JlZGl0czogaW4gdGhpcyBjb3Vyc2UsIHdlIHdpbGwgZXhwbG9yZSBtYXRlcmlhbCB0aGF0IGNhbiBiZSBmb3VuZCBhdCB0aGVzZSBVUkw6CgotIGh0dHA6Ly9zdGNvcnAubmwvUl9jb3Vyc2UvdHV0b3JpYWxfc2hpbnkuaHRtbAotIGh0dHA6Ly9zaGlueS5yc3R1ZGlvLmNvbS8KLSBodHRwOi8vc2hpbnkucnN0dWRpby5jb20vdHV0b3JpYWwvCi0gaHR0cDovL3d3dy5zaG93bWVzaGlueS5jb20vCi0gaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvc2hpbnkvc2hpbnktdXNlci1zaG93Y2FzZS8KLSBodHRwOi8vZGF0YXNjaWVuY2UubGEvc2hpbnktci1tYWRlLWludGVyYWN0aXZlLXVzZXItMjAxNC8KLSBodHRwOi8vdXNlcjIwMTYub3JnL3R1dG9yaWFscy8wMi5odG1sCi0gaHR0cDovL3d3dy5saXNhLnN0YXQudnQuZWR1Lz9xPW5vZGUvNzkzNAotIGh0dHA6Ly9kYXRhc29jaWV0eS5jby9jb3Vyc2VzL2ludGVyYWN0aXZlLXZpc3VhbGl6YXRpb25zLwotIGh0dHA6Ly93d3cuaGFuc2Vsc29sdXRpb25zLmNvbS9ibG9nL3N1cmYtdGFsay9zaGlueS1zdXJmLmh0bWwKLSBodHRwczovL2RvY3MuZ29vZ2xlLmNvbS9maWxlL2QvMEJ3UkQ1a0pqZS05VFZFZFlOWGx1Wm1ZNU0wMC9lZGl0P3VzcD1zaGFyaW5nCi0gaHR0cDovL3JwdWJzLmNvbS9qY2hlbmcvdXNlUi0yMDE0CgojIFByZXJlcXVpc2l0ZXMKCllvdSBuZWVkIGEgZ29vZCBrbm93bGVkZ2UgYWJvdXQgdGhlIFIgc29mdHdhcmU6IHRyeSBbdGhpcyBxdWl6XShodHRwOi8vc2hpbnkucnN0dWRpby5jb20vdHV0b3JpYWwvcXVpei8pIHRvIGNoZWNrIHlvdXIgc2tpbGxzLiBJZiB5b3Ugd2FudCB0byBpbXByb3ZlIHlvdXIgYmFzaWMgUiBza2lsbCwgc2VlIGZvciBpbnN0YW5jZSBbdGhpcyBib29rXShodHRwOi8vbGluay5zcHJpbmdlci5jb20vYm9vay8xMC4xMDA3JTJGOTc4LTEtNDYxNC05MDIwLTMpLgoKWW91IGFsc28gbmVlZCBhIGdvb2Qga25vd2xlZGdlIGFib3V0IFtgZ2dwbG90MmBdKGh0dHA6Ly9nZ3Bsb3QyLm9yZy8pLiBEb3dubG9hZCB0aGUgW0RhdGEgVmlzdWFsaXphdGlvbiBDaGVhdCBTaGVldF0oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcmVzb3VyY2VzL2NoZWF0c2hlZXRzLykuCgpTb21lIGJhc2ljIGtub3dsZWRnZSBhYm91dCBIVE1MLCBKYXZhc2NyaXB0IGFuZCBDU1MgbWlnaHQgaGVscC4KClRoZSBTaGlueSAoY2xpZW50KSBzb2Z0d2FyZSBjYW4gcnVuIG9uIFdpbmRvd3MvTWFjT1MvTGludXguIFdvcmtzIGJlc3Qgd2l0aCBDaHJvbWUuCgpJZiBub3QgYWxyZWFkeSBkb25lLCBbZG93bmxvYWRdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Byb2R1Y3RzL3JzdHVkaW8vZG93bmxvYWQzLykgYW5kIGluc3RhbGwgdGhlIGxhc3QgdmVyc2lvbiBvZiBSU3R1ZGlvLCB3aGljaCBhcHBlYXJzIHRvIGJlIDEuMC4xMzYgYXQgdGhlIGRhdGUgdGhpcyBjb3Vyc2UgaXMgcHJlcGFyZWQgKFRoaXMgaW5mb3JtYXRpb24gaXMgZm91bmQgZnJvbSBSU3R1ZGlvIHZpYSB0aGUgbWVudSBIZWxwL0Fib3V0IFJTdHVkaW8uKQoKQWxzbywgZnJvbSBSc3R1ZGlvOgpgYGB7ciwgZXZhbD1GQUxTRX0KaW5zdGFsbC5wYWNrYWdlcygic2hpbnkiKQpgYGAKCkRvd25sb2FkIHRoZSBbU2hpbnkgQ2hlYXQgU2hlZXRdKGh0dHBzOi8vd3d3LnJzdHVkaW8uY29tL3Jlc291cmNlcy9jaGVhdHNoZWV0cy8pLgoKIyBZb3VyIGZpcnN0IFNoaW55IGFwcAoKTGF1bmNoIFJTdHVkaW8sIHRoZW4gZ28gdG8gRmlsZS9OZXcgRmlsZS9TaGlueSBXZWIgQXBwLi4uCgpUaGUgZm9sbG93aW5nIHdpbmRvdyBwb3BzIHVwLgoKIVtdKEltYWdlcy9maWcxLnBuZykKCkNoYW5nZSB0aGUgZW50cmllcyBhcyBmb2xsb3dzOgoKLSBBcHBsaWNhdGlvbiBOYW1lOiBNeV9GaXJzdF9TaGlueV9BcHAKLSBBcHBsaWNhdGlvbiB0eXBlOiBNdWx0aXBsZSBGaWxlICh1aS5SL3NlcnZlci5SKQotIENyZWF0ZSB3aXRoaW4gZGlyZWN0b3J5OiB+CgpXaGVuIHlvdSBjbGljayBvbiBDcmVhdGUsIHRoaXMgc2hvdWxkIG9wZW4gdHdvIG5ldyB0YWJzIGluIFJTdHVkaW86IHVpLlIgYW5kIHNlcnZlci5SLiBUaGUgY29ycmVzcG9uZGluZyBmaWxlcyBhcmUgc3RvcmVkIGluIH4vTXlfRmlyc3RfU2hpbnlfQXBwLiBUaGV5IGFscmVhZHkgY29udGFpbiBzb21lIFIgY29kZSB0aGF0IHdlIHdpbGwgY29tbWVudCAoYW5kIG1vZGlmeSkgaW4gYSB3aGlsZS4KCkJ1dCwgZmlyc3QgbGV0J3MgZXhlY3V0ZSB0aGlzIGNvZGUgdG8gc2VlIHdoYXQgaGFwcGVucy4gU2VsZWN0IHRoZSB1aS5SIChvciB0aGUgc2VydmVyLlIpIHRhYiBpbiBSU3R1ZGlvLiBUaGVuIGNsaWNrIG9uIHRoZSAhW1J1biBBcHBdKEltYWdlcy9maWcyLnBuZykgYnV0dG9uLiBUaGlzIHNob3VsZCBsYXVuY2ggYSB3ZWIgYXBwIGluIHlvdXIgZmF2b3JpdGUgd2ViIGJyb3dzZXIuIFlvdSBjYW4gdGhlbiBpbnRlcmFjdCB3aXRoIGEgc2xpZGVyIHRvIGNob29zZSB0aGUgbnVtYmVyIG9mIGJpbnMgdG8gdXNlIGluIG9yZGVyIHRvIGRyYXcgYSBoaXN0b2dyYW0gb2YgdGhlICJPbGQgRmFpdGhmdWwgR2V5c2VyIERhdGEiLiBDaGFuZ2UgdGhlIHNsaWRlciB2YWx1ZSBhbmQgb2JzZXJ2ZSBob3cgdGhlIGhpc3RvZ3JhbSBpcyByZWRyYXduLgoKIVtHZXlzZXIgZXhhbXBsZV0oSW1hZ2VzL2ZpZ19HZXlzZXIucG5nKQoKKipOb3RlIDE6KiogWW91IHNob3VsZCBhbHNvIHNlZSBpbiB0aGUgY29uc29sZSBvZiBSU3R1ZGlvIGEgcmVkIG1lc3NhZ2UgZGlzcGxheWVkLiBPbiBteSBsYXB0b3AsIGl0IGlzOiAiTGlzdGVuaW5nIG9uIGh0dHA6Ly8xMjcuMC4wLjE6MzQ1MiIuIFRoaXMgbWVhbnMgdGhhdCBpbiB0aGUgYmFja2dyb3VuZCwgUlN0dWRpbyBsYXVuY2hlcyBfbG9jYWxseV8gYSBTaGlueSBkYWVtb24gd2ViIHNlcnZlciBvbiB0aGUgcG9ydCAzNDUyLiAoVGhpcyBwb3J0IG51bWJlciBtaWdodCBiZSBkaWZmZXJlbnQgb24geW91ciBjb21wdXRlci4pIFRoaXMgY2FuIGJlIGNoZWNrZWQgZm9yIGV4YW1wbGUgdW5kZXIgTGludXggYnkgdHlwaW5nIHRoZSBmb2xsb3dpbmcgY29tbWFuZCBpbiBhIHRlcm1pbmFsIGFuZCBub3RpbmcgdGhhdCB0aGUgU1RBVEUgaXMgIm9wZW4iOgoKYGBge2Jhc2gsIGV2YWw9RkFMU0V9CltsYWZheWUgMTU6MTY6NTEgfl0gbm1hcCBsb2NhbGhvc3QgLXAgMzQ1MgoKU3RhcnRpbmcgTm1hcCA2LjQ3ICggaHR0cDovL25tYXAub3JnICkgYXQgMjAxNi0xMi0yOCAxNToxNyBDRVQKTm1hcCBzY2FuIHJlcG9ydCBmb3IgbG9jYWxob3N0ICgxMjcuMC4wLjEpCkhvc3QgaXMgdXAgKDAuMDAwMDI3cyBsYXRlbmN5KS4KT3RoZXIgYWRkcmVzc2VzIGZvciBsb2NhbGhvc3QgKG5vdCBzY2FubmVkKTogMTI3LjAuMC4xClBPUlQgICAgIFNUQVRFIFNFUlZJQ0UKMzQ1Mi90Y3Agb3BlbiAgdW5rbm93bgoKTm1hcCBkb25lOiAxIElQIGFkZHJlc3MgKDEgaG9zdCB1cCkgc2Nhbm5lZCBpbiAwLjA1IHNlY29uZHMKYGBgCgoqKk5vdGUgMjoqKiBZb3VyIFIgc2Vzc2lvbiB3aWxsIGJlIGJ1c3kgd2hpbGUgdGhlIEhlbGxvIFNoaW55IGFwcCBpcyBhY3RpdmUsIHNvIHlvdSB3aWxsIG5vdCBiZSBhYmxlIHRvIHJ1biBhbnkgUiBjb21tYW5kcy4gUiBpcyBtb25pdG9yaW5nIHRoZSBhcHAgYW5kIGV4ZWN1dGluZyB0aGUgYXBw4oCZcyByZWFjdGlvbnMuIFRvIGdldCB5b3VyIFIgc2Vzc2lvbiBiYWNrLCBoaXQgZXNjYXBlIG9yIGNsaWNrIHRoZSBzdG9wIHNpZ24gaWNvbiAoZm91bmQgaW4gdGhlIHVwcGVyIHJpZ2h0IGNvcm5lciBvZiB0aGUgUlN0dWRpbyBjb25zb2xlIHBhbmVsKS4KCiMgTGV0J3MgY29tbWVudCB0aGUgUiBjb2RlIGluIHRoZSB1aS5SL3NlcnZlci5SIGZpbGVzCgpGaXJzdCwgbm90ZSB0aGF0IGEgZnVsbCBkZXNjcmlwdGlvbiBvZiBhbGwgU2hpbnkgZnVuY3Rpb25zIChlLmcuLCB0aGUgb25lcyB1c2VkIGluIHRoZSBjb2RlIG9mIG91ciBjdXJyZW50IHZlcnNpb25zIG9mIHVpLlIgYW5kIHNlcnZlci5SKSBpcyBnaXZlbiBhdCB0aGUgW1NoaW55IHJlZmVyZW5jZSBwYWdlc10oaHR0cDovL3NoaW55LnJzdHVkaW8uY29tL3JlZmVyZW5jZS9zaGlueS9sYXRlc3QvKS4gSGF2ZSBhIGxvb2sgYXQgdGhpcyB3ZWJwYWdlIHRvIHNlZSBob3cgaXQgaXMgb3JnYW5pemVkLgoKCioqU29tZSB2b2NhYnVsYXJ5OioqICJBIHR5cGljYWwgd2ViIGFwcGxpY2F0aW9uIGNvbnNpc3RzIG9mIGEgbnVtYmVyIG9mIF91c2VyIGludGVyZmFjZSAoVUkpIGVsZW1lbnRzXywgc2F5IGEgYnV0dG9uIG9yIGEgY2hlY2tib3guIEVhY2ggb2YgdGhlc2UgZWxlbWVudHMgY2FuIGJlIGludGVyYWN0ZWQgd2l0aCwgZm9yIGV4YW1wbGUgeW91IGNhbiBwdXNoIHRoZSBidXR0b24uIFRoaXMgYnV0dG9uIHB1c2ggdGhlbiB0cmlnZ2VycyBhbiBhY3Rpb24sIHJ1bm5pbmcgYSBwaWVjZSBvZiBjb2RlLiBUaGlzIGNhbiB0aGVuIGNoYW5nZSB0aGUgc3RhdGUgb2YgdGhlIHdlYiBhcHBsaWNhdGlvbiwgZm9yIGV4YW1wbGUgcmV0cmlldmUgYSBwaWVjZSBvZiBpbmZvcm1hdGlvbiBmcm9tIHRoZSBzZXJ2ZXIgb3IgZHJhdyBhIHBpY3R1cmUgaW4gdGhlIGFwcGxpY2F0aW9uLiBUaGlzIHN0eWxlIG9mIHByb2dyYW1taW5nIGlzIGNhbGxlZCBfZXZlbnQtZHJpdmVuIHByb2dyYW1taW5nXy4gVGhlIHBpZWNlIG9mIGNvZGUgdGhhdCBpcyBleGVjdXRlZCBiYXNlZCBvbiBhbiBldmVudCBpcyBjYWxsZWQgYW4gX2V2ZW50IGhhbmRsZXJfLiIKCiMjIFRoZSBzdHJ1Y3R1cmUgb2YgU2hpbnkgYXBwcwoKIlNoaW55IGFwcHMgZm9sbG93IHR5cGljYWwgc3RydWN0dXJlIG9mIHdlYiBhcHBsaWNhdGlvbnMuIEhvd2V2ZXIsIGFzIGEgdXNlciB5b3Ugb25seSBoYXZlIHRvIHNwZWNpZnkgd2hpY2ggVUkgZWxlbWVudHMgeW91IHdhbnQgdG8gc2hvdywgYW5kIHRoZSB1bmRlcmx5aW5nIFIgY29kZSB0aGF0IGRyYXdzIGEgcGxvdCwgc2hvd3Mgc29tZSB0ZXh0LCBvciBhIHRhYmxlLiBUaGVzZSBwaWVjZXMgb2YgaW5mb3JtYXRpb24gYXJlIHN0b3JlZCBpbiB0d28gKippbnRlcmFjdGluZyBjb2RlcyAoc2NyaXB0KSoqIHN0b3JlZCBpbiB0aGUgZm9sbG93aW5nIGZpbGVzOgoKLSBgdWkuUmAsIGZvciB0aGUgdXNlciBpbnRlcmZhY2UgKHVpKSBlbGVtZW50cywgYW5kCi0gYHNlcnZlci5SYCwgZm9yIHRoZSBjb250ZW50IGNvZGUuIgoKVGhlIGB1aS5SYCBjb2RlIGNyZWF0ZXMgdGhlIG9wdGlvbnMgYSB1c2VyIGNhbiBjaGFuZ2UsIGFuZCBjb250cm9scyB3aGF0IGlzIGRpc3BsYXllZCBvbiB0aGUgYXBwLgoKLSBUaGlzIGNvZGUgYWxzbyBjcmVhdGVzIGEgbGlzdCBvYmplY3QgKG5hbWVkIGBpbnB1dGApIHRvIHN0b3JlIGFsbCBvcHRpb25zCi0gVGhlIFVJIG9wZXJhdGVzIHVzaW5nICJvdXRwdXQiIHBpZWNlcyBjcmVhdGVkIGJ5IHRoZSBzZXJ2ZXIuCgpUaGVyIGBzZXJ2ZXIuUmAgY29kZSBydW5zIGV2ZXJ5dGhpbmcgdGhhdCByZXNwb25kcyB0byB0aGUgY2hhbmdlcyBtYWRlIGJ5IHRoZSB1c2VyIGluIHRoZSBhcHAuCgotIFRoZSBzZXJ2ZXIgb3BlcmF0ZXMgdXNpbmcgdGhlICJpbnB1dCIgbGlzdCBjcmVhdGVkIGJ5IHRoZSBVSSwgYW5kIGNyZWF0ZXMgYW4gIm91dHB1dCIgbGlzdCB0byBzdG9yZSBhbGwgcmVuZGVyZWQgb2JqZWN0cyBhbmQgdmFsdWVzLgotIFRoaXMgY29kZSBpcyByZWFjdGl2ZS4gVGhpcyBtZWFucyB0aGF0IGV2ZXJ5IHRpbWUgeW91IGNoYW5nZSBhbnkgb3B0aW9uIGluIHRoZSBpbnRlcmZhY2UsIHNoaW55IHdpbGwgc2NhbiB0aHJvdWdoIHRoZSBzZXJ2ZXIgZm9yIGFueSBpbnN0YW5jZSB3aGVyZSB0aGF0IG9wdGlvbiBpcyBtZW50aW9uZWQsIGFuZCByZSBydW4gdGhlIGNvcnJlc3BvbmRpbmcgY29kZS4KCioqTm90ZToqKiBBcyBvZiB2ZXJzaW9uIDAuMTAuMiwgU2hpbnkgc3VwcG9ydHMgc2luZ2xlLWZpbGUgYXBwbGljYXRpb25zLiBZb3Ugbm8gbG9uZ2VyIG5lZWQgdG8gYnVpbGQgc2VwYXJhdGUgc2VydmVyLlIgYW5kIHVpLlIgZmlsZXMgZm9yIHlvdXIgYXBwOyB5b3UgY2FuIGp1c3QgY3JlYXRlIGEgZmlsZSBjYWxsZWQgYXBwLlIgdGhhdCBjb250YWlucyBib3RoIHRoZSBzZXJ2ZXIgYW5kIFVJIGNvbXBvbmVudHMuIFlvdSBjYW4gbGVhcm4gbW9yZSBhYm91dCBidWlsZGluZyBhIFNoaW55IGFwcCBpbiBhIHNpbmdsZSBmaWxlIGhlcmUsIGhvd2V2ZXIgdGhpcyB0dXRvcmlhbCB3aWxsIGZvY3VzIG9uIHRoZSB0d28gZmlsZSBzdHJ1Y3R1cmUgZm9yIGJ1aWxkaW5nIGEgU2hpbnkgYXBwLgoKQXMgeW91IGNhbiBzZWUsIHRoZSBgdWkuUmAgZmlsZSBzdGFydHMgKGFmdGVyIHNvbWUgY29tbWVudHMpIHdpdGggdGhlIGluc3RydWN0aW9uOgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShzaGlueSkKYGBgCnRoYXQgd2lsbCBsb2FkIHRoZSBgc2hpbnlgIHBhY2thZ2UuIE5leHQsIGEgY2FsbCB0byB0aGUgYHNoaW55VUkoKWAgZnVuY3Rpb24gaXMgbWFkZS4gVGhpcyBpcyBub3QgcmVhbGx5IG5lY2Vzc2FyeSBhcyBjYW4gYmUgcmVhZCBpbiB0aGUgW2RvY3VtZW50YXRpb25dKGh0dHA6Ly9zaGlueS5yc3R1ZGlvLmNvbS9yZWZlcmVuY2Uvc2hpbnkvbGF0ZXN0L3NoaW55VUkuaHRtbCkgb2YgdGhpcyBmdW5jdGlvbjoKCiJIaXN0b3JpY2FsbHkgdGhpcyBmdW5jdGlvbiB3YXMgdXNlZCBpbiB1aS5SIGZpbGVzIHRvIHJlZ2lzdGVyIGEgdXNlciBpbnRlcmZhY2Ugd2l0aCBTaGlueS4gSXQgaXMgbm8gbG9uZ2VyIHJlcXVpcmVkIGFzIG9mIFNoaW55IDAuMTA7IHNpbXBseSBlbnN1cmUgdGhhdCB0aGUgbGFzdCBleHByZXNzaW9uIHRvIGJlIHJldHVybmVkIGZyb20gdWkuUiBpcyBhIHVzZXIgaW50ZXJmYWNlLiBUaGlzIGZ1bmN0aW9uIGlzIGtlcHQgZm9yIGJhY2t3YXJkcyBjb21wYXRpYmlsaXR5IHdpdGggb2xkZXIgYXBwbGljYXRpb25zLiBJdCByZXR1cm5zIHRoZSB2YWx1ZSB0aGF0IGlzIHBhc3NlZCB0byBpdC4iCgpZb3UgY2FuIHRodXMgcmVtb3ZlIGl0LiBCdXQgbGVhdmUgdGhlIGBmbHVpZFBhZ2VgIHN0dWZmLgoKVGhlIGBmbHVpZFBhZ2UoKWAgZnVuY3Rpb24gY3JlYXRlcyBmbHVpZCBwYWdlIGxheW91dHMuIEEgZmx1aWQgcGFnZSBsYXlvdXQgY29uc2lzdHMgb2Ygcm93cyB3aGljaCBpbiB0dXJuIGluY2x1ZGUgY29sdW1ucy4gUm93cyBleGlzdCBmb3IgdGhlIHB1cnBvc2Ugb2YgbWFraW5nIHN1cmUgdGhlaXIgZWxlbWVudHMgYXBwZWFyIG9uIHRoZSBzYW1lIGxpbmUgKGlmIHRoZSBicm93c2VyIGhhcyBhZGVxdWF0ZSB3aWR0aCkuIENvbHVtbnMgZXhpc3QgZm9yIHRoZSBwdXJwb3NlIG9mIGRlZmluaW5nIGhvdyBtdWNoIGhvcml6b250YWwgc3BhY2Ugd2l0aGluIGEgMTItdW5pdCB3aWRlIGdyaWQgaXRzIGVsZW1lbnRzIHNob3VsZCBvY2N1cHkuIEZsdWlkIHBhZ2VzIHNjYWxlIHRoZWlyIGNvbXBvbmVudHMgaW4gcmVhbHRpbWUgdG8gZmlsbCBhbGwgYXZhaWxhYmxlIGJyb3dzZXIgd2lkdGguCgpPdXIgY3VycmVudCBmbHVpZFBhZ2UgY29udGFpbnMgdHdvIHJvd3M6CgotIFRoZSBmaXJzdCBvbmUgYnVpbGRzIGEgdGl0bGUgdGhhbmtzIHRvIHRoZSAKYHRpdGxlUGFuZWwoKWAgZnVuY3Rpb24uIFRoaXMgcm93IG9ubHkgZGlzcGxheXMgIk9sZCBGYWl0aGZ1bCBHZXlzZXIgRGF0YSIuCi0gVGhlIHNlY29uZCBvbmUgaXMgY3JlYXRlZCB2aWEgdGhlIGAgc2lkZWJhckxheW91dCgpYCBmdW5jdGlvbi4gSXQgY29udGFpbnMgYSBzaWRlYmFyIChvbiB0aGUgbGVmdCkgYW5kIGEgbWFpbiBhcmVhLiBUaGUgc2lkZWJhciBpcyBkaXNwbGF5ZWQgd2l0aCBhIGRpc3RpbmN0IGJhY2tncm91bmQgY29sb3IgKGxpZ2h0IGJsdWUpIGFuZCB0eXBpY2FsbHkgY29udGFpbnMgaW5wdXQgY29udHJvbHMgKGN1cnJlbnRseSBvbmx5IGEgc2xpZGVyIGlucHV0KS4gVGhlIG1haW4gYXJlYSBvY2N1cGllcyBieSBkZWZhdWx0IDIvMyBvZiB0aGUgaG9yaXpvbnRhbCB3aWR0aCBhbmQgdHlwaWNhbGx5IGNvbnRhaW5zIG91dHB1dHMgKGN1cnJlbnRseSBvdXIgaGlzdG9ncmFtKS4KCiFbTGF5b3V0XShJbWFnZXMvZmlnMC5wbmcpCgpUaGUgaGVscCBwYWdlIGZvciB0aGlzIGxhdHRlciBmdW5jdGlvbiBpcyBbaGVyZV0oaHR0cDovL3NoaW55LnJzdHVkaW8uY29tL3JlZmVyZW5jZS9zaGlueS9sYXRlc3Qvc2lkZWJhckxheW91dC5odG1sKS4gSGF2ZSBhIGxvb2sgYXQgaXQuIEZyb20gaXQsIHlvdSBjYW4gYWxzbyBhY2Nlc3MgdGhlIGBzaWRlYmFyUGFuZWwoKWAgYW5kIGBtYWluUGFuZWwoKWAgaGVscCBwYWdlcy4KCkluIG91ciBjYXNlLCB0aGUgc2lkZVBhbmVsIGNvbnRhaW5zIG9ubHkgb25lIFVJIGVsZW1lbnQ6IGEgYHNsaWRlcklucHV0YC4gVGhlIG1haW5QYW5lbCBhbHNvIGNvbnRhaW5zIG9uZSBpbnB1dCBlbGVtZW50OiBhIGBwbG90T3V0cHV0YC4gCkxvb2sgYXQgdGhlIGRlc2NyaXB0aW9uIG9mIHRoZXNlIGZ1bmN0aW9ucyBbaGVyZV0oaHR0cDovL3NoaW55LnJzdHVkaW8uY29tL3JlZmVyZW5jZS9zaGlueS9sYXRlc3QvKS4KCioqTm90ZToqKiBZb3UgY2FuIHJ1biBhIFNoaW55IGFwcCBieSBnaXZpbmcgdGhlIG5hbWUgb2YgaXRzIGRpcmVjdG9yeSB0byB0aGUgZnVuY3Rpb24gcnVuQXBwLiBGb3IgZXhhbXBsZSBpZiB5b3VyIFNoaW55IGFwcCBpcyBpbiBhIGRpcmVjdG9yeSBjYWxsZWQgbXlfYXBwLCBydW4gaXQgd2l0aCB0aGUgZm9sbG93aW5nIGNvZGU6CgpgYGB7ciwgZXZhbD1GQUxTRX0KbGlicmFyeShzaGlueSkKcnVuQXBwKCJteV9hcHAiKQpgYGAKSWYgeW91IHdvdWxkIGxpa2UgeW91ciBhcHAgdG8gZGlzcGxheSBpbiBzaG93Y2FzZSBtb2RlLCB5b3UgY2FuIHJ1biBgcnVuQXBwKCJteV9hcHAiLCBkaXNwbGF5Lm1vZGUgPSAic2hvd2Nhc2UiKWAuCgojIyBIVE1MIENvbnRlbnQKCllvdSBjYW4gdXNlIG9uZSBvZiBTaGlueSdzIEhUTUwgdGFnIGZ1bmN0aW9ucy4gVGhlc2UgZnVuY3Rpb25zIHBhcmFsbGVsIGNvbW1vbiBbSFRNTDUgdGFnc10oaHR0cDovL3d3dy53M3NjaG9vbHMuY29tL3RhZ3MvdGFnX2huLmFzcCkuIExldCdzIHRyeSBvdXQgYSBmZXcgb2YgdGhlbS4KCnwqKnNoaW55IGZ1bmN0aW9uKiogfCAqKkhUTUw1IGVxdWl2YWxlbnQqKiB8ICoqY3JlYXRlcyoqICAgICAgICAgICAgICAgICAgICAgfAp8LS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS18LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXwKfCBgcCgpYCAgICAgIHwJYDxwPmAgICAgICB8IEEgcGFyYWdyYXBoIG9mIHRleHQgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgaDEoKWAgICAgIHwgCWA8aDE+YCAgICAgfCBBIGZpcnN0IGxldmVsIGhlYWRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGgyKClgICAgICB8CWA8aDI+YCAgICAgfCBBIHNlY29uZCBsZXZlbCBoZWFkZXIgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGgzKClgICAgICB8CWA8aDM+YCAgICAgfCBBIHRoaXJkIGxldmVsIGhlYWRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGg0KClgICAgICB8CWA8aDQ+YCAgICAgfCBBIGZvdXJ0aCBsZXZlbCBoZWFkZXIgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGg1KClgICAgICB8CWA8aDU+YCAgICAgfCBBIGZpZnRoIGxldmVsIGhlYWRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGg2KClgICAgICB8CWA8aDY+YCAgICAgfCBBIHNpeHRoIGxldmVsIGhlYWRlciAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGEoKWAgICAgICB8CWA8YT5gICAgICAgfCBBIGh5cGVyIGxpbmsgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGJyKClgICAgICB8IAlgPGJyPmAgICAgIHwgQSBsaW5lIGJyZWFrIChlLmcuIGEgYmxhbmsgbGluZSkgICAgICAgICAgICAgICAgfAp8IGBkaXYoKWAgICAgfAlgPGRpdj5gICAgIHwgQSBkaXZpc2lvbiBvZiB0ZXh0IHdpdGggYSB1bmlmb3JtIHN0eWxlICAgICAgICAgfAp8IGBzcGFuKClgICAgfAlgPHNwYW4+YCAgIHwgQW4gaW4tbGluZSBkaXZpc2lvbiBvZiB0ZXh0IHdpdGggYSB1bmlmb3JtIHN0eWxlfAp8IGBwcmUoKWAgICAgfAlgPHByZT5gICAgIHwgVGV4dCDigJhhcyBpc+KAmSBpbiBhIGZpeGVkIHdpZHRoIGZvbnQgICAgICAgICAgICAgIHwKfCBgY29kZSgpYCAgIHwgCWA8Y29kZT5gICAgfCBBIGZvcm1hdHRlZCBibG9jayBvZiBjb2RlICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGltZygpYCAgICB8CWA8aW1nPmAgICAgfCBBbiBpbWFnZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYHN0cm9uZygpYCB8IAlgPHN0cm9uZz5gIHwgQm9sZCB0ZXh0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGBlbSgpYCAgICAgfAlgPGVtPmAgICAgIHwgSXRhbGljaXplZCB0ZXh0ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGBIVE1MKClgICAgfCAgICAgICAgICAgICB8IERpcmVjdGx5IHBhc3NlcyBhIGNoYXJhY3RlciBzdHJpbmcgYXMgSFRNTCBjb2RlIHwKCiMjIEludGVncmF0aW5nIGR5bmFtaWMgY29udGVudAoKSWYgb25seSB0aGUgYHVpLlJgIGZpbGUgd2FzIHByZXNlbnQsIG5vIGR5bmFtaWMgY29udGVudCB3b3VsZCBiZSBhdmFpbGFibGUuIFRoaXMgaXMgdGhlIHB1cnBvc2Ugb2YgdGhlIGBzZXJ2ZXIuUmAgZmlsZSB0byBhZGQgaW50ZXJhY3Rpdml0eS4KCkF0IHRoaXMgcG9pbnQsIHdlIGhhdmUgdG8gZXhwbGFpbiBob3cgdGhlIGB1aS5SYCBhbmQgYHNlcnZlci5SYCBmaWxlcyBjb21tdW5pY2F0ZS4KCkluIHRoZSBgc2VydmVyLlJgIGZpbGUsIHdlIHNlZSBhIF9yZW5kZXJpbmcgZnVuY3Rpb25fIGNhbGxlZCBgcmVuZGVyUGxvdCgpYCwgYXMgd2VsbCBhcyB0d28gaW50ZXJlc3RpbmcgcGllY2VzIG9mIGNvZGU6IGBvdXRwdXQkZGlzdFBsb3RgIGFuZCBgaW5wdXQkYmluc2AuIE5vdGUgdGhhdCBgZGlzdFBsb3RgIGlzIHRoZSBJRCAoX291dHB1dElkXykgdXNlZCB3aGVuIHdlIGNhbGxlZCB0aGUgYHBsb3RPdXRwdXQoKWAgZnVuY3Rpb24sIHdoaWxlIGBiaW5zYCBpcyB0aGUgSUQgKF9pbnB1dElkXykgdXNlZCB3aGVuIHdlIGNhbGxlZCB0aGUgYHNsaWRlcklucHV0KClgIGZ1bmN0aW9uLiBDb25zdWx0IHRoZSBoZWxwIHBhZ2VzIGZvciB0aGVzZSB0d28gZnVuY3Rpb25zLgoKKipJdCBpcyB0aHJvdWdoIHRoZXNlIElEIHRoYXQgU2hpbnkgY291cGxlcyB0aGUgVUkgZWxlbWVudHMgZGVmaW5lZCBpbiBgdWkuUmAgdG8gdGhlIGR5bmFtaWMgY29udGVudCBpbiBgc2VydmVyLlJgLioqCgpUaGUgYGlucHV0YCB2YXJpYWJsZSBvZiB0aGUgYHNoaW55U2VydmVyKClgIGZ1bmN0aW9ucyBjb250YWlucyBhbGwgdGhlIFVJIGVsZW1lbnRzLCB0aGUgYG91dHB1dGAgdmFyaWFibGUgcmV0dXJucyB0aGUgZHluYW1pYyBjb250ZW50IHRvIHRoZSBVSS4KCioqRXhlcmNpc2UgMToqKiBNb2RpZnkgdGhlIGB1aS5SYCBhbmQgYHNlcnZlci5SYCBmaWxlcyBzbyBhcyB0byBhZGQgYSBgdGV4dElucHV0YCBpbiB0aGUgc2lkZWJhclBhbmVsIChiZWxvdyB0aGUgc2xpZGVySW5wdXQpIHRoYXQgd2lsbCBlbmFibGUgeW91IHRvIG1vZGlmeSB0aGUgdGl0bGUgb2YgdGhlIGhpc3RvZ3JhbS4gVGhlIGhpc3RvZ3JhbSBzaG91bGQgbm93IGJlIGNyZWF0ZWQgdXNpbmcgdGhlIGZ1bmN0aW9ucyBgZ2dwbG90KClgLCBgZ2VvbV9oaXN0b2dyYW0oKWAgYW5kIGBnZ3RpdGxlKClgIGZyb20gdGhlIFtgZ2dwbG90MmBdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzLykgUiBwYWNrYWdlLgoKIyMgSW1wb3J0YW50IG5vdGUgb24gY29kZSBleGVjdXRpb24KClRoZSBsb2NhdGlvbiBvZiBjb2RlIGluIGBzZXJ2ZXIuUmAgZGV0ZXJtaW5lcyBob3cgb2Z0ZW4gaXQgaXMgcnVuLCBhbmQgdGh1cyBpbXBhY3RzIHRoZSBwZXJmb3JtYW5jZSBvZiB0aGUgYXBwLiBUaGVyZSBhcmUgdGhyZWUgem9uZXMgd2hlcmUgY29kZSBpcyBydW4gbW9yZSBvciBsZXNzIG9mdGVuOgoKCmBgYHtyLCBldmFsPUZBTFNFfQoKIyBab25lIDEKbGlicmFyeShzaGlueSkKbGlicmFyeShnZ3Bsb3QyKQoKc2hpbnlTZXJ2ZXIoZnVuY3Rpb24oaW5wdXQsIG91dHB1dCkgewogICMgWm9uZSAyCiAgb3V0cHV0JGRpc3RQbG90IDwtIHJlbmRlclBsb3QoewogICAgIyBab25lIDMgOiBydW4gZWFjaCB0aW1lIGEgdXNlciBjaGFuZ2VzIGEgd2lkZ2V0IHRoYXQgb3V0cHV0JG1hcCByZWxpZXMgb24KICB9KQp9KQpgYGAKCi0gKipab25lIDEqKiwgdGhpcyBjb2RlIGlzIG9ubHkgZXhlY3V0ZWQgb25jZSB3aGVuIHRoZSBhcHAgaXMgc3RhcnRlZC4gTG9hZGluZyBsaWJyYXJpZXMgaXMgdHlwaWNhbGx5IGRvbmUgaGVyZSwgYXMgYXJlIHJlYWRpbmcgbGFyZ2Ugc3RhdGljIGRhdGFzZXRzLgotICoqWm9uZSAyKiosIHRoaXMgY29kZSBpcyByYW4gZWFjaCB0aW1lIGEgdXNlciByZXF1ZXN0cy92aXNpdHMgdGhlIGFwcC4gVGhpcyBjYW4gZm9yIGV4YW1wbGUgYmUgdXNlZCB0byBsb2FkIGRhdGEgZm9yIHRoZSBsYXN0ICR4JCBkYXlzLCBhcyB0aGlzIHdpbGwgYmUgZGlmZmVyZW50IGZvciBlYWNoIHVzZXIuCi0gKipab25lIDMqKiwgdGhpcyBjb2RlIGlzIHJhbiBlYWNoIHRpbWUgYSBjaGFuZ2UgaXMgZGV0ZWN0ZWQgaW4gdGhlIFVJLiBUaGlzIHR5cGljYWxseSBjb250YWlucyBwbG90dGluZyBjb2RlIGFuZCBzdWNoLCBvciBkYXRhIHRoYXQgaXMgcmVxdWVzdGVkIGJhc2VkIG9uIGNoYW5nZXMgaW4gdGhlIFVJIChkYXRlIHJhbmdlIGZvciBleGFtcGxlKS4KSWYgeW91IHB1dCB5b3VyIGNvZGUgaW4gdGhlIHdyb25nIHpvbmUsIHRoZSBhcHDigJlzIHBlcmZvcm1hbmNlIGNhbiBiZSBzZXJpb3VzbHkgaW1wYWN0ZWQuIEZvciBleGFtcGxlLCBpZiByZWFkaW5nIGEgbGFyZ2Ugc3RhdGljIGRhdGFzZXQgaXMgcHV0IGludG8gWm9uZSAzLCB0aGlzIGxhcmdlIGRhdGFzZXQgd2lsbCBiZSByZWFkIGVhY2ggdGltZSB0aGUgcGxvdCBuZWVkcyB0byBiZSByZWRyYXduLgoKRXhhbXBsZXMvZXhlcmNpc2VzIGJlbG93IHdpbGwgaGVscCB0byBjbGFyaWZ5IHRoZXNlIG5vdGlvbnMuCgojIyBSZWFjdGl2ZSBleHByZXNzaW9ucwoKTm9ybWFsbHksIFNoaW55IGF1dG9tYXRpY2FsbHkgcmVydW5zIHRoZSBjb2RlIGluIHRoZSBkaWZmZXJlbnQgem9uZXMgYXMgZXhwbGFpbmVkIGFib3ZlLiBCdXQgY29uc2lkZXIgdGhlIGZvbGxvd2luZyBmaWN0aXRpb3VzIGV4YW1wbGUgd2hlcmUgdHdvIGxpbmVzIHdlcmUgYWRkZWQgaW4gWm9uZSAzOgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgWm9uZSAxCmxpYnJhcnkoc2hpbnkpCmxpYnJhcnkoZ2dwbG90MikKCnNoaW55U2VydmVyKGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQpIHsKICAjIFpvbmUgMgogIG91dHB1dCRkaXN0UGxvdCA8LSByZW5kZXJQbG90KHsKICAgICMgWm9uZSAzCiAgICBkYXQgPC0gZ2V0X2RhdGEoaW5wdXQkdWlfZWxlbWVudDEsIGlucHV0JHVpX2VsZW1lbnQyKQogICAgcGxvdChkYXQsIGlucHV0JHVpX2VsZW1lbnQzKQogIH0pCn0pCmBgYAoKVGhlIChoeXBvdGhldGljYWwsIHVzZXItZGVmaW5lZCkgYGdldF9kYXRhKClgIGZ1bmN0aW9uIGlzIGNhbGxlZCBlYWNoIHRpbWUgYW55IG9mIHRoZSB0aHJlZSBVSSBlbGVtZW50cyBpcyB1cGRhdGVkIChzaW5jZSBpdCBpcyBpbiBab25lIDMpLiBIb3dldmVyLCBpZiBvbmx5IGB1aV9lbGVtZW50M2AgaXMgdXBkYXRlZCB0aGUgZGF0YSBzaG91bGQgbm90IGJlIHJlbG9hZGVkICh0byBwcmV2ZW50IHRpbWUgbG9zcykuIFRoaXMgaXMgd2hlcmUgX3JlYWN0aXZlIGV4cHJlc3Npb25zXyBjb21lIGluLCBhcyBzZWVuIGJlbG93OgoKYGBge3IsIGV2YWw9RkFMU0V9CiMgWm9uZSAxCmxpYnJhcnkoZ2dwbG90MikKCnNoaW55U2VydmVyKGZ1bmN0aW9uKGlucHV0LCBvdXRwdXQpIHsKICAjIFpvbmUgMgogIGlucHV0X2RhdGEgPC0gcmVhY3RpdmUoewogICAgZGF0IDwtIGdldF9kYXRhKGlucHV0JHVpX2VsZW1lbnQxLCBpbnB1dCR1aV9lbGVtZW50MikKICB9KQogIG91dHB1dCRkaXN0UGxvdCA8LSByZW5kZXJQbG90KHsKICAgICMgWm9uZSAzCiAgICBwbG90KGlucHV0X2RhdGEoKSwgaW5wdXQkdWlfZWxlbWVudDMpCiAgfSkKfSkKYGBgCgpXZSB3cmFwIHRoZSBkYXRhIGxvYWRpbmcgaW4gYSBfcmVhY3RpdmUgYmxvY2tfLCBhbmQgaW4gdGhlIHBsb3R0aW5nIHJlZmVyIHRvIHRoZSBuZXcgZnVuY3Rpb24gYGlucHV0X2RhdGEoKWAuIFRoZSBhZHZhbnRhZ2Ugb2YgdGhpcyBhcHByb2FjaCBpcyB0aGF0IGZvciBhIHJlYWN0aXZlIGV4cHJlc3Npb24gU2hpbnkgZmlyc3QgY2hlY2tzIGlmIHRoZSBkYXRhIGFjdHVhbGx5IG5lZWRzIHRvIGJlIHVwZGF0ZWQgb3Igbm90LiBJZiBvbmx5IGB1aV9lbGVtZW50M2AgaXMgdXBkYXRlZCwgdGhlIHJlYWN0aXZlIHNpbXBseSByZXR1cm5zIHRoZSBwcmV2aW91c2x5IHN0b3JlZCBkYXRhIChpLmUuLCBjYWNoaW5nKSwgYW5kIHRoZSBwbG90IGlzIHJlZHJhd24gd2l0aG91dCB0aGUgY29zdGx5IHN0ZXAgb2YgcmUtcmVhZGluZyB0aGUgZGF0YS4KCkF0IHRoaXMgcG9pbnQsIHlvdSBtaWdodCB3YW50IHRvIGhhdmUgYSBiZXR0ZXIgbG9vayBhdCB0aGUgU2hpbnkgY2hlYXRzaGVldCB0aGF0IHlvdSBkb3dubG9hZGVkIHByZXZpb3VzbHkuCgojIEV4ZXJjaXNlcwoKKipFeGVyY2lzZSAyLioqIE9wZW4gYSBuZXcgU2hpbnkgYXBwIGluIFJTdHVkaW8gKEZpbGUgPiBOZXcgcHJvamVjdCA+IE5ldyBkaXJlY3RvcnkgPiBTaGlueSBXZWIgQXBwbGljYXRpb24pLCB3aGljaCBpcyBlc3NlbnRpYWxseSBhIG5ldyBwcm9qZWN0IGluIGl0c2VsZi4gVGhlbiBlZGl0IHRoZSBgdWkuUmAgYW5kIGBzZXJ2ZXIuUmAgZmlsZXMgdG8gb2J0YWluIHRoZSBleGFtcGxlIGdpdmVuIGJlbG93ICh0aGUgYWN0dWFsIHBsb3QgaXMgb2YgYG10Y2FycyRtcGdgIGFzIGEgZnVuY3Rpb24gb2YgYG10Y2FycyR3dGApLiBVc2UgdGhlIGBnZ3Bsb3QyYCBwYWNrYWdlIHRvIG1ha2UgdGhlIHBsb3QuIEZvciB0aGlzIGV4ZXJjaXNlLCBubyBpbnRlcmFjdGl2aXR5IGlzIHJlcXVpcmVkLiBJbiB0aGUgbmV4dCBjb3VwbGUgb2YgZXhjZXJjaXNlcyB5b3Ugd2lsbCBidWlsZCBhIHZpc3VhbGlzYXRpb24gYXBwIGZvciB0aGUgYG10Y2Fyc2AgZGF0YXNldC4KCiFbXShJbWFnZXMvZmlnMy5wbmcpCgoqKkV4ZXJjaXNlIDMuKiogQWRkIGFub3RoZXIgZHJvcGRvd24gbWVudSBpbiB0aGUgc2lkZWJhci4gVGhlIHR3byBkcm9wZG93biBtZW51cyBzaG91bGQgbm93IHNlbGVjdCB3aGljaCB2YXJpYWJsZXMgZnJvbSBgbXRjYXJzYCB3aWxsIGJlIHJlc3BlY3RpdmVseSBwbG90dGVkIG9uIHRoZSAkeCQgYW5kICR5JCBheGVzLiBUaXA6IGhhdmUgYSBsb29rIGF0IGBhZXNfc3RyaW5nKClgIGZyb20gYGdncGxvdDJgLiBBbHNvLCB0aGUgYHRleHRJbnB1dGAgaW4gdGhlIHNpZGViYXJQYW5lbCBzaG91bGQgbm93IGJlIGNvbm5lY3RlZCBpbnRlcmFjdGl2ZWx5IHRvIHRoZSBhY3R1YWwgdGl0bGUgb2YgdGhlIHBsb3QuIEFuZCB0aGUgbmFtZXMgb2YgdGhlIHR3byB2YXJpYWJsZXMgc2VsZWN0ZWQgc2hvdWxkIG5vdyBiZSBkaXNwbGF5ZWQgYWJvdmUgdGhlIHBsb3QgaW4gYW4gaW50ZXJhY3RpdmUgbWFubmVyICgiWW91IHNlbGVjdGVkIHZhcmlhYmxlcyAuLi4gYW5kIC4uLiIpLiAKCioqRXhlcmNpc2UgNC4qKiBBZGQgYSBjaGVja2JveCB3aGljaCBlbmFibGVzIHRoZSB1c2VyIHRvIGNob29zZSB3aGV0aGVyIG9yIG5vdCB0byBpbmNsdWRlIGEgYHN0YXRfc21vb3RoKClgIGNhbGwgKGZyb20gYGdncGxvdDJgIHBhY2thZ2UpLgoKKipFeGVyY2lzZSA1LioqIEFkZCBhIGNoZWNrYm94IGFuZCBhIGRyb3Bkb3duIG1lbnUgdGhhdCBhbGxvdyB0aGUgdXNlciB0byB0b2dnbGUgdGhlIHVzZSBvZiBmYWNldHRpbmcgYW5kIHNlbGVjdCB3aGljaCB2YXJpYWJsZSB0byBmYWNldCB3aXRoLiBUaXA6IHNlZSBmb3IgaW5zdGFuY2UgW3RoaXMgd2ViIHBhZ2VdKGh0dHA6Ly93d3cuY29va2Jvb2stci5jb20vR3JhcGhzL0ZhY2V0c18lMjhnZ3Bsb3QyJTI5LykuCgpJbiB0aGUgbmV4dCBjb3VwbGUgb2YgZXhjZXJjaXNlcyB3ZSB3aWxsIGJ1aWxkIGEgc3RvY2sgcHJpY2UgdmlzdWFsaXNhdGlvbiBhcHAgKGluc3BpcmVkIGJ5IFNoaW55IHR1dG9yaWFsKS4KCioqRXhlcmNpc2UgNi4qKiBXZSB3aWxsIGRvd25sb2FkIHRoZSBzdG9jayBwcmljZSBkYXRhIHVzaW5nIFtgZ2V0U3ltYm9scygpYF0oaHR0cDovL3d3dy5xdWFudG1vZC5jb20vZG9jdW1lbnRhdGlvbi9nZXRTeW1ib2xzLmh0bWwpIGZyb20gdGhlIFtgcXVhbnRtb2RgXShodHRwOi8vd3d3LnF1YW50bW9kLmNvbS8pIHBhY2thZ2UuIEVuc3VyZSB0aGF0IHRoZSB2YWx1ZSBvZiB0aGUgYXJndW1lbnQgYGF1dG8uYXNzaWduYCBpcyBzZXQgdG8gYEZBTFNFYCwgYW5kIHlvdSBjYW4gdXNlIHRoZSBgZnJvbWAgYW5kIGB0b2AgaW5wdXQgYXJndW1lbnRzIHRvIHNlbGVjdCB0aW1lLiBFeHRyYWN0IHN0b2NrcHJpY2UgZGF0YSBmb3IgQUFQTCAoQXBwbGUpLCBZSE9PIChZYWhvbyksIGFuZCBHUyAoR29sZG1hbiBTYWNocykuIENyZWF0ZSBhIGNoYXJ0IG9mIHRoYXQgZGF0YSBieSBjYWxsaW5nIHRoZSBgY2FuZGxlQ2hhcnQoKWAgZnVuY3Rpb24gZnJvbSBgcXVhbnRtb2RgIG9uIHRoZSByZXN1bHRpbmcgb2JqZWN0IGZyb20gYGdldFN5bWJvbHMoKWAuIENoYW5nZSB0aGUgdGhlbWUgZnJvbSBibGFjayB0byB3aGl0ZS4KCioqRXhlcmNpc2UgNy4qKiBDcmVhdGUgYSBuZXcgU2hpbnkgYXBwLCBhbmQgY3JlYXRlIGEgYHVpLlJgIGZpbGUgdGhhdCByZXN1bHRzIGluIHRoaXMgdXNlciBpbnRlcmZhY2U6CgohW10oSW1hZ2VzL2ZpZzQucG5nKQoKVGhlIGxpc3Qgb2YgcG9zc2libGUgc3RvY2sgc3ltYm9scyBpcyBBQVBMIChBcHBsZSksIFlIT08gKFlhaG9vKSwgYW5kIEdTIChHb2xkbWFuIFNhY2hzKS4gVGhlIHRoZW1lcyB0aGF0IGNhbiBiZSBzZWxlY3RlZCBhcmUgd2hpdGUgYW5kIGJsYWNrLiBTZWxlY3QgYXBwcm9wcmlhdGUgZGVmYXVsdHMgZm9yIHRoZSBkYXRhIHJhbmdlLgoKQ3JlYXRlIGEgYHNlcnZlci5SYCBmaWxlIHRoYXQgbG9hZHMgdGhlIGRhdGEgZm9yIHRoZSBzZWxlY3RlZCBzdG9jayBzeW1ib2wsIGFuZCB0aGUgc2VsZWN0ZWQgZGF0YSByYW5nZSB1c2luZyBgZ2V0U3ltYm9scygpYCBhbmQgcGxvdHMgaXQgdXNpbmcgYGNhbmRsZUNoYXJ0KClgLgoKRW5zdXJlIHRoYXQgeW91IGhhdmUgdXNlZCBhIHJlYWN0aXZlIGV4cHJlc3Npb24gZm9yIHJlYWRpbmcgdGhlIHN0b2NrIGRhdGEuIEVuc3VyZSB0aGF0IHRoZSB0aGVtZSBvZiB0aGUgZ3JhcGggY2FuIGJlIGNoYW5nZWQgd2l0aG91dCByZWxvYWRpbmcgdGhlIGRhdGEuCgoqKkV4ZXJjaXNlIDguKiogRGVzaWduIGEgU2hpbnkgYXBwIHRvIGludmVzdGlnYXRlIHRoZSBlZmZlY3RzIG9mIHNhbXBsZSBzaXplICRuJCwgc3RhbmRhcmQgZGV2aWF0aW9uICRzaWdtYSQsIGV0YyBmb3IgdGhlIGZvbGxvd2luZyBmdW5jdGlvbjoKYGBge3IgZXZhbD1GQUxTRX0KZml0LnRydWUgPC0gZnVuY3Rpb24obiA9IDEwMCwgc2lnbWEgPSAxMCwgaW50ZXJjZXB0ID0gMCwgc2xvcGUgPSA1LCAKICAgICAgICAgICAgICAgICAgICAgbWluID0gLTEwLCBtYXggPSAxMCwgbGVnZW5kID0gInRvcGxlZnQiKSB7CiAgIHggPC0gcnVuaWYobiwgbWluID0gbWluLCBtYXggPSBtYXgpCiAgIHkgPC0gcm5vcm0obiwgbWVhbiA9IGludCArIHNsb3BlICogeCwgc2QgPSBzaWdtYSkKICAgbW9kIDwtIGxtKHkgfiB4KQogICBwbG90KHgsIHksIHBjaCA9IDIwLCBsYXMgPSAxKQogICBhYmxpbmUoYSA9IGludCwgYiA9IHNsb3BlKQogICBhYmxpbmUoYSA9IG1vZCRjb2VmWzFdLCBiID0gbW9kJGNvZWZbMl0sIGNvbCA9ICdyZWQnKQogICBsZWdlbmQobGVnZW5kLCBjKHBhc3RlKCJ5ID0gIiwgaW50LCAiICsgIiwgc2xvcGUsICIgeCwgVHJ1ZSBNb2RlbCIsIHNlcCA9ICIiKSwKICAgICAgcGFzdGUoInkgPSAiLCByb3VuZChtb2QkY29lZlsxXSwyKSwgIiArICIsIHJvdW5kKG1vZCRjb2VmWzJdLCAyKSwgCiAgICAgICAgICAgICIgeCwgRXN0aW1hdGVkIE1vZGVsICIsIHNlcCA9ICIiKSksIGx0eSA9IGMoMSwgMSksIGNvbCA9IGMoJ2xhY2snLCAncmVkJykpCn0KYGBgCgoqKkV4ZXJjaXNlIDkuKiogQ2hhbGxlbmdlLiBXaWxsIHlvdSBiZSBhYmxlIHRvIHJldHJpZXZlIGF1dG9tYXRpY2FsbHkgYWxsIGRhdGEgZnJvbSBbZGlhbW9uZHNlXShodHRwOi8vd3d3LmRpYW1vbmRzZS5pbmZvL2RpYW1vbmQtcHJpY2VzLmFzcCkgYW5kIHRoZW4gcmVjcmVhdGUgdGhlIHNhbWUgd2ViIGFwcD8gSWYgeWVzLCBzZW5kIG1lIHlvdXIgc29sdXRpb24hCgoqKkV4ZXJjaWNlIDEwLioqIFByb3Bvc2Ugc29tZSBuaWNlIGFwcCBmb3IgZGF0YSBleHRyYWN0ZWQgW2hlcmVdKGh0dHBzOi8vd2Vic2VudGkudTcwNy5qdXNzaWV1LmZyL3NlbnRpd2ViLz9wYWdlPXRhYmxlKS4KCioqRXhlcmNpc2UgMTEuKiogUHJvcG9zZSBzb21lIG5pY2UgYXBwIGZvciBkYXRhIChlLmcuLCByZWFsIHRpbWUpIGV4dHJhY3RlZCBbaGVyZV0oaHR0cHM6Ly9kYXRhLnJhdHAuZnIpLgoKIyBDdXN0b20gVUkKClNlZSB0aGlzIFt3ZWJwYWdlXShodHRwczovL3NoaW55LnJzdHVkaW8uY29tL2FydGljbGVzL2h0bWwtdWkuaHRtbCkuCgpZb3UgY2FuIG1ha2UgeW91ciBvd24gVXNlciBJbnRlcmZhY2UgdXNpbmcgSFRNTCBkaXJlY3RseS4gIEluIHRoaXMgY2FzZSB0aGVyZSBpcyBubyBgdWkuUmAgZmlsZS4gVGhlIHN0cnVjdHVyZSBvZiBmaWxlcy9mb2xkZXJzIHNob3VsZCBiZSB0aGUgZm9sbG93aW5nOgoKYGBgCiA8IGFwcGxpY2F0aW9uLWRpciA+CnwtLSB3d3cvCiAgICB8LS0gY3NzLwogICAgfC0tIGpzLwogICAgfC0tIGluZGV4Lmh0bWwKfC0tIHNlcnZlci5SCmBgYAoKQmluZGluZyBiZXR3ZWVuIHNoaW55IGlucHV0cy9vdXRwdXRzIGFuZCBIVE1MIGVsZW1lbnRzIGlzIGRvbmUgYnk6CgotIEhUTUwgZm9ybSBlbGVtZW50cyBhcmUgYm91bmR0byBgaW5wdXRgIHNsb3RzIHVzaW5nIHRoZWlyIEhUTUwgYG5hbWVgIGF0dHJpYnV0ZQoKIGA8c2VsZWN0IG5hbWU9ImRpc3QiPmAKCiBgPGlucHV0IHR5cGU9Im51bWJlciIgbmFtZT0ibiIgdmFsdWU9IjUwMCIgbWluPSIxIiBtYXg9IjEwMDAiIC8+IGAKCi0gT3V0cHV0IGlzIHJlbmRlcmVkIGludG8gSFRNTCBlbGVtZW50cyBvbiBgaWRgIGF0dHJpYnV0ZXMKCiBgPHByZSBpZD0ic3VtbWFyeSIgY2xhc3M9InNoaW55LXRleHQtb3V0cHV0Ij48L3ByZT5gCiAKIGA8ZGl2IGlkPSJwbG90IiBjbGFzcz0ic2hpbnktcGxvdC1vdXRwdXQiIHN0eWxlPSJ3aWR0aDogMTAwJTsgaGVpZ2h0OiA0MDBweCI+PC9kaXY+YAogCiBgPGRpdiBpZD0idGFibGUiIGNsYXNzPSJzaGlueS1odG1sLW91dHB1dCI+PC9kaXY+YAogCkhhdmUgYSBsb29rIGF0IHRoZSBgaW5kZXguaHRtbGAgZmlsZSBpbiB0aGUgYDA4X2h0bWwvYCBzdWJmb2xkZXIgb2YgYEV4YW1wbGVzL2AuIFRyeSB0byB1bmRlcnN0YW5kIHRoZSBjb25uZWN0aW9uIGJldHdlZW4gdGhpcyBmaWxlIGFuZCB0aGUgYHNlcnZlci5SYCBmaWxlLgogCgojIElzb2xhdGUKClNvbWV0aW1lcyB5b3UgZG9uJ3Qgd2FudCB0aGluZ3MgcnVubmluZyBhdXRvbWF0aWNhbGx5IGFzIHNvb24gYXMgeW91IGNoYW5nZSBhbiBpbnB1dC4KV2l0aCB0aGUgcHJldmlvdXMgZXhhbXBsZSB0eXBpbmcgaW4gMTIzIGZvciB0aGUgbnVtYmVyIG9mIHBvaW50cyBnaXZlcyBhIGdyYXBoIGZvciAkbj0xJCwgdGhlbiAkbj0xMiQsIHRoZW4gJG49MTIzJC4KCldlIGNhbiB1c2UgdGhlIGBhY3Rpb25CdXR0b25gIHRvIGdldCBhcm91bmQgdGhpcy4KCmBzZXJ2ZXIuUmA6CmBgYHtyLCBldmFsPUZBTFNFfQpkYXRhIDwtIHJlYWN0aXZlKHsgICAgICAgCiAgIGRpc3QgPC0gc3dpdGNoKGlucHV0JGRpc3QsCiAgICAgICAgICAgbm9ybSA9IHJub3JtLAogICAgICAgICAgIHVuaWYgPSBydW5pZiwKICAgICAgICAgICBsbm9ybSA9IHJsbm9ybSwKICAgICAgICAgICBleHAgPSByZXhwLAogICAgICAgICAgIHJub3JtKQoKICAgaW5wdXQkZ29CdXR0b24KCiAgIGlzb2xhdGUoZGlzdChpbnB1dCRuKSkKfSkKYGBgCgpgdWkuUmA6CmBgYHtyLCBldmFsPUZBTFNFfQpkb3dubG9hZEJ1dHRvbigpCmBgYAoKIyBJbWFnZXMKCkl0IGlzIGVhc3kgdG8gaW5zZXJ0IGltYWdlcyBvbiB5b3VyIHdlYnBhZ2U6CgpgaW1nKHNyYyA9ICJteV9pbWFnZS5wbmciLCBoZWlnaHQgPSA3Miwgd2lkdGggPSA3MilgCgojIENvbnRyb2wgd2lkZ2V0cyAKCkEgd2lkZ2V0IGlzIGEgd2ViIGVsZW1lbnQgdGhhdCB5b3VyIHVzZXJzIGNhbiBpbnRlcmFjdCB3aXRoLiBXaWRnZXRzIHByb3ZpZGUgYSB3YXkgZm9yIHlvdXIgdXNlcnMgdG8gc2VuZCBtZXNzYWdlcyB0byB0aGUgU2hpbnkgYXBwLiBXZSBoYXZlIGFscmVhZHkgZW5jb3VudGVyZWQgc2V2ZXJhbCB3aWRnZXRzLgoKVGhlIHN0YW5kYXJkIFNoaW55IHdpZGdldHMgYXJlOgoKfCAqKmZ1bmN0aW9uKiogICAgICAgICAgIHwgKip3aWRnZXQqKiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfC0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS18CnwgYGFjdGlvbkJ1dHRvbigpYCAgICAgICB8CUFjdGlvbiBCdXR0b24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfAp8IGBjaGVja2JveEdyb3VwSW5wdXQoKWAgfAlBIGdyb3VwIG9mIGNoZWNrIGJveGVzICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgY2hlY2tib3hJbnB1dCgpYCAgICAgIHwJQSBzaW5nbGUgY2hlY2sgYm94ICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYGRhdGVJbnB1dCgpYCAgICAgICAgICB8CUEgY2FsZW5kYXIgdG8gYWlkIGRhdGUgc2VsZWN0aW9uICAgICAgICAgICAgICAgfAp8IGBkYXRlUmFuZ2VJbnB1dCgpYCAgICAgfCAJQSBwYWlyIG9mIGNhbGVuZGFycyBmb3Igc2VsZWN0aW5nIGEgZGF0ZSByYW5nZSB8CnwgYGZpbGVJbnB1dCgpYCAgICAgICAgICB8IAlBIGZpbGUgdXBsb2FkIGNvbnRyb2wgd2l6YXJkICAgICAgICAgICAgICAgICAgIHwKfCBgaGVscFRleHQoKWAgICAgICAgICAgIHwgCUhlbHAgdGV4dCB0aGF0IGNhbiBiZSBhZGRlZCB0byBhbiBpbnB1dCBmb3JtICAgfAp8IGBudW1lcmljSW5wdXQoKWAgICAgICAgfCAJQSBmaWVsZCB0byBlbnRlciBudW1iZXJzICAgICAgICAgICAgICAgICAgICAgICB8CnwgYHJhZGlvQnV0dG9ucygpYCAgICAgICB8IAlBIHNldCBvZiByYWRpbyBidXR0b25zICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgc2VsZWN0SW5wdXQoKWAgICAgICAgIHwgCUEgYm94IHdpdGggY2hvaWNlcyB0byBzZWxlY3QgZnJvbSAgICAgICAgICAgICAgfAp8IGBzbGlkZXJJbnB1dCgpYCAgICAgICAgfCAJQSBzbGlkZXIgYmFyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8CnwgYHN1Ym1pdEJ1dHRvbigpYCAgICAgICB8IAlBIHN1Ym1pdCBidXR0b24gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwKfCBgdGV4dElucHV0KClgICAgICAgICAgIHwgCUEgZmllbGQgdG8gZW50ZXIgdGV4dCAgICAgICAgICAgICAgICAgICAgICAgICAgfAoKIyBTaGFyZSB5b3VyIGFwcHMgCgpZb3UgY2FuIG5vdyBidWlsZCBhIHVzZWZ1bCBTaGlueSBhcHAsIGJ1dCBob3cgY2FuIHlvdSBzaGFyZSBpdCB3aXRoIG90aGVycz8KCllvdSBoYXZlIHR3byBiYXNpYyBvcHRpb25zOgoKMS4gU2hhcmUgeW91ciBTaGlueSBhcHAgYXMgdHdvIGZpbGVzOiBgc2VydmVyLlJgIGFuZCBgdWkuUmAuIFRoaXMgaXMgdGhlIHNpbXBsZXN0IHdheSB0byBzaGFyZSBhbiBhcHAsIGJ1dCBpdCB3b3JrcyBvbmx5IGlmIHlvdXIgdXNlcnMgaGF2ZSBSIChhbmQgU2hpbnkpIG9uIHRoZWlyIG93biBjb21wdXRlciAoYW5kIGtub3cgaG93IHRvIHVzZSBpdCkuIFVzZXJzIGNhbiB1c2UgdGhlc2Ugc2NyaXB0cyB0byBsYXVuY2ggdGhlIGFwcCBmcm9tIHRoZWlyIG93biBSIHNlc3Npb24sIGp1c3QgbGlrZSB5b3UndmUgYmVlbiBsYXVuY2hpbmcgdGhlIGFwcHMuCgoyLiBTaGFyZSB5b3VyIFNoaW55IGFwcCBhcyBhIHdlYiBwYWdlLiBUaGlzIGlzIGRlZmluaXRlbHkgdGhlIG1vc3QgdXNlciBmcmllbmRseSB3YXkgdG8gc2hhcmUgYSBTaGlueSBhcHAuIFlvdXIgdXNlcnMgY2FuIG5hdmlnYXRlIHRvIHlvdXIgYXBwIHRocm91Z2ggdGhlIGludGVybmV0IHdpdGggYSB3ZWIgYnJvd3Nlci4gVGhleSB3aWxsIGZpbmQgeW91ciBhcHAgZnVsbHkgcmVuZGVyZWQsIHVwIHRvIGRhdGUsIGFuZCByZWFkeSB0byBnby4KCiMjIFNoYXJlIGFzIHR3byBSIGZpbGVzCgpBbnlvbmUgd2l0aCBSIGNhbiBydW4geW91ciBTaGlueSBhcHAuIFRoZXkgd2lsbCBuZWVkIGEgY29weSBvZiB5b3VyIHNlcnZlci5SIGFuZCB1aS5SIGZpbGVzLCBhcyB3ZWxsIGFzIGFueSBzdXBwbGVtZW50YXJ5IG1hdGVyaWFscyB1c2VkIGluIHlvdXIgYXBwIChlLmcuLCB3d3cgZm9sZGVycyBvciBoZWxwZXJzLlIgZmlsZXMpLgoKIyMgU2hhcmUgYXMgYSB3ZWIgcGFnZQoKUlN0dWRpbyBvZmZlcnMgdGhyZWUgd2F5cyB0byBob3N0IHlvdXIgU2hpbnkgYXBwIGFzIGEgd2ViIHBhZ2U6CgotIFtTaGlueWFwcHMuaW9dKGh0dHA6Ly9zaGlueWFwcHMuaW8vKQotIFNoaW55IFNlcnZlcgotIFNoaW55IFNlcnZlciBQcm8KClRoZSBmaXJzdCBvbmUgaXMgdGhlIGVhc2llc3QgdG8gdXNlLiBJdCBjb21lcyB3aXRoIHNldmVyYWwgZm9ybXVsYSAoZnJvbSAkMC9tb250aCB0byAkMjk5L21vbnRoKS4gVGhlIHNlY29uZCBvbmUgbGV0cyB5b3UgaW5zdGFsbCAoZm9yIGZyZWUpIGEgU2hpbnkgc2VydmVyIG9uIHlvdXIgb3duIHdlYiBzZXJ2ZXIuIFRoaXMgIFtndWlkZV0oaHR0cHM6Ly9naXRodWIuY29tL3JzdHVkaW8vc2hpbnktc2VydmVyL2Jsb2IvbWFzdGVyL1JFQURNRS5tZCkgbWlnaHQgaGVscCB5b3UuIEZpbmFsbHksIFtTaGlueSBTZXJ2ZXIgUHJvXShodHRwOi8vd3d3LnJzdHVkaW8uY29tL3NoaW55L3NlcnZlci8pIGFsbG93cyB5b3UgdG8gaW5zdGFsbCAobm90IGZyZWUpIGEgU2hpbnkgU2VydmVyIHdpdGggbWFueSBbbW9yZSBmZWF0dXJlc10oaHR0cHM6Ly93d3cucnN0dWRpby5jb20vcHJvZHVjdHMvc2hpbnkvc2hpbnktc2VydmVyLykgb24geW91ciBvd24gd2ViIHNlcnZlci4KCg==