Introduction
What is Shiny?
- Open Sourced by RStudio November 2012
- A way to create a user interface for R
- enables experienced R programmers to produce graphical user interfaces with customized algorithms under the hood. This gives point-and-click functionality to others, enabling complex analysis for those who are not advanced programmers
- Interactive web applications around your R analyses
- Zero HTML/CSS/JavaScript knowledge is required…
- … but fully customizable and extensible with HTML/CSS/JavaScript
- Modern web UI with attractive defaults
- Designed to integrate with existing JavaScript libraries
- Uses a reactive programming model which allows dramatically simpler code than traditional UI or web programming
- Applications can be deployed to the cloud (ShinyApps.io) or on-premises (Shiny Server), with both free and commercial options available
- Vibrant and active community
- Web sockets for communication between client and server
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:
- Interactive visualizations
- Start simple
- Widgets
- Application layout
- Dynamic user interface
- Reactive programming
- Advanced Shiny
- Interactive plots
- Shiny Server Pro
- Internationalization
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:
- Application Name: My_First_Shiny_App
- Application type: Multiple File (ui.R/server.R)
- Create within directory: ~
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 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.
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).
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:
- HTML form elements are boundto
input
slots using their HTML name
attribute
<select name="dist">
<input type="number" name="n" value="500" min="1" max="1000" />
- Output is rendered into HTML elements on
id
attributes
<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)
Share your apps
You can now build a useful Shiny app, but how can you share it with others?
You have two basic options:
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.
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==