Topic 1B: Data Visualisation I
Welcome to the first Data Science computer lab for STM1001!
Throughout the semester, we will use the R software environment for all our work.
R is widely used for statistical computing and data visualisation, and indeed the first four computer labs of the STM1001 Data Science module focus on data visualisation in R.
In this first lab, we will explore some of the more light-hearted options available to R users, and then introduce an excellent package, plotly
(Sievert 2020), which allows us to create interactive data visualisations.
By the end of this lab, you should feel comfortable loading and using packages in R, and be able to create a simple interactive histogram using plotly
.
Making memes in R
Base R contains many functions, and is perfectly sufficient for a number of data analysis methods.
However, one of the great benefits of R is that anyone can create packages (bundles of code, data and functions) which can be uploaded to global repositories (such as CRAN or Bioconductor), and made available for anyone around the world to download and use in their version of R.
Often, these packages are extremely helpful. They may contain useful data sets for a specific field of research, address a shortcoming with the base R suite of functions, allow users to perform specialised analyses, and/or offer users some additional functionalities.
On the other hand, sometimes these packages are more light-hearted, such as the meme
package (Yu 2021), which allows users to create simple memes within R. Let’s take a look at this package now.
Because the meme
package is not installed in base R, we need to download it before we can use it.
We can use the install.packages()
R function to do this, as shown in the code below.
install.packages("meme")
Open up RStudio and run this code now.
Once the meme
package is downloaded and installed, we need to load it in our current R session.
Run the following code to load the meme
package.
library(meme)
Great, now we can start to make some simple memes!
We really only need two lines of code for this.
Firstly, we need to find an appropriate image. For this example, we will use an image of Hagrid, from the Harry Potter series. We have located this image online, and copied the url. In R, we assign this url to the object hagrid
, as shown below:
hagrid <- "https://i.imgflip.com/13wb2t.jpg"
Note that the url needs to be contained within quotation marks.
Make sure to run this code before moving on to the next step.
Next, we use the meme
function to add some words to this image. Try running the code below, and see what happens.
meme(hagrid, "Yer a wizard", "with coding", font = "sans")
Note: Some warnings may appear in your R Console as this code is executing. Don’t worry about it, it is safe to ignore these warnings.
If you would like to save the meme you have made, it is helpful to assign the output of the meme
function to an object.
In the code below, we make a new meme, and assign it to the object success
. Try running this code now.
success_kid <- "http://i0.kym-cdn.com/entries/icons/mobile/000/000/745/success.jpg"
success <- meme(success_kid, "Using R", "to make memes", font = "sans")
success
Hint: Notice that we need to include the final line of code, calling the object success
, in order for the image to be shown.
Now we can save our meme, using the function meme_save
. Take a look at the code below.
meme_save(success, file="c:/STM1001/Data Science/success_kid_R_meme.png")
Here, we are saving our success
meme, to the file location c:\STM1001\Data Science\
, with the name success_kid_R_meme.png
.
Note that although the file path on our computer includes backslashes (\
), in R code these need to be changed to forward slashes (/
).
Now it’s time to try making your own meme.
Find an appropriate image of your choice online (please ensure you pick content suitable for university and work).
Copy the url.
Assign this url to an object in R.
Use the meme
function to add words to your image.
Save your meme using the meme_save
function.
Hint: If you are not quite sure how to begin, click the Code
button to the right below.
# First, we need to find an image, and assign it to an object
# (here we use the generic object name 'image_name')
# Just replace the ...s with the url of your image
image_name <- "..."
# Next, we need to use the meme function, to add some words (just replace the ...s)
my_meme <- meme(image_name, "...", "...", font = "sans")
# Note that you need to include the `, font = "sans"` part to ensure R know which font to use.
# Now all that's left is to save your meme - just refer to the code above.
Congratulations! You were probably not expecting to make a meme in your first data science computer lab, and this probably won’t be on the final exam, but hopefully you are starting to realise that R is very versatile.
Customizing GIFs in R
R is not limited to working with static images - we can modify and create gifs and animations.
In this section, we will use another fun package, the magick
package (Ooms 2021), to customize a gif.
Run the following code to download, install and load the magick
package in your current R session.
install.packages("magick")
library(magick)
Just as we obtained online images of hagrid
and success kid
, so too can we use urls to gifs and animations.
For this example, we have used the url to a rotating earth gif.
We use the image_read
function to read this gif into R, and assign it to the object Earth
.
Earth <- image_read("https://i.giphy.com/media/mf8UbIDew7e8g/giphy.gif")
Earth
Make sure to run this code before moving on to the next step (don’t worry if it takes a few seconds). The gif should appear in the Viewer
section of RStudio.
Using the magick
package, we can easily make some changes to this gif.
Take a look at the code below. You will notice here that:
- We have reversed the gif, using the
rev
function
- We have flipped the gif, using the
image_flip
function, and
- We have added text to this gif using the
image_annotate
function
rev(Earth) %>%
image_flip() %>%
image_annotate(" Meanwhile, in Australia", size = 40, color = "white")
Try running this code now.
This is really just scratching the surface of the magick
package. However, our intention for this first computer lab is to give you a taste of some of the different possibilities available in R, so for the moment, let’s move on.
Drawing a fish in R
Instead of using a pre-existing image or gif, let’s now try to create one from scratch. Specifically, let’s draw a fish.
To do this, we can use the appropriately named rfishdraw
package (Ding 2021).
Let’s download and install the rfishdraw
package now. In order to use this package, we will also need to download and install some additional packages, upon which the rfishdraw
package depends. Such packages are known as dependencies, and it is common for more sophisticated R packages to have multiple dependencies.
Note that these dependencies are packages in their own right.
Run this code in R now.
install.packages("rfishdraw")
install.packages("patchwork")
install.packages("ggplot2")
library("rfishdraw")
library("patchwork")
library("ggplot2")
If you now run the code below, a detailed drawing of a fish should appear in a new window!
get_polylines(path = "inst/fishdraw.js",
format = "smil",
output = "animated.svg",
draw_type = "random")
windows() # If you are using a Mac, replace windows() with: quartz()
fish_draw()
Suppose we would like to change the colour of our fish. We can do this, by including the argument col = "..."
within the function fish_draw
. For example, if we would like our fish to be blue, we can write
fish_draw(col = "blue")
Try changing this colour to a different colour, and then run the code.
Palmer Penguins Data Set
Now that we have had a taste of some of the more light-hearted R packages out there, let’s consider a package which contains some useful data.
The palmerpenguins
R package (Horst, Hill, and Gorman 2020) contains data, collected over the course of several years, on 3 species of penguin living on different islands in the Palmer archipelago, off the coast of Antarctica.
For more details, you can refer to Section 2 of the Data Visualisation in R supplement.
Just like the previous packages, we will need to download and load the palmerpenguins
package before we can begin working with this penguin data.
Run the code below to install and load the palmerpenguins
package in R.
install.packages("palmerpenguins")
library(palmerpenguins)
We can use the summary
function to obtain a quick overview of the data contained within the penguins
data set.
# This code summarises the data in the `palmerpenguins` package.
summary(penguins)
Don’t worry too much about the values shown in the summary table - the main things to note at this stage are the different variables, namely species
, island
, bill_length_mm
, bill_depth_mm
, flipper_length_mm
, body_mass_g
, sex
and year
.
Interactive Histograms
Suppose that we would like to produce histograms showing the distribution of the penguins’ body_mass_g
values (their body mass in grams). We could create a simple histogram using the base R hist
function via the following code:
hist(penguins$body_mass_g, breaks = 19)

However, this histogram has some shortcomings. Firstly, it is static. We can’t interact with the image, and we can’t manipulate it in real time to display different details.
For example, perhaps we would like to see the distribution of the penguins’ body_mass_g
values, but only for the penguins on a specific island. We would need to do some more coding to produce such a histogram in base R. Even then, if we would like to have similar histograms for the other two islands, this would mean further coding.
Alternatively, we could use the plotly
package to create an interactive, responsive histogram. Let’s take a look at how to do this now.
To begin, just as for the previous packages, we will need to download and load the plotly
package in R, before we can use any plotly
functions.
Run the code below to install and load the plotly
package in R.
install.packages("plotly")
library(plotly)
To create plotly
plots, we use the function plot_ly()
. We won’t worry too much about the composition of this function just yet - we’ll cover this in more detail next week. For the moment, take a look at the code below, and see if you can get a general idea of what’s going on.
penguin_hist_base <- plot_ly(data = penguins,
x = ~body_mass_g,
type = "histogram")
penguin_hist_base <- penguin_hist_base %>% layout(yaxis = list(title = 'count'))
Before you move on to the next question, run this code in R.
Note: Once you have taken some time to consider the code above, if you would like more details or would like to check the accuracy of your interpretation, click the Code
button below for a brief explanation.
# Here, we are creating a plotly object called "penguin_hist_base"
penguin_hist_base <- plot_ly(data = penguins, # We are using the penguins data
x = ~body_mass_g, # and modelling the body_mass_g data
type = "histogram") # in a histogram format
# The code below is used to modify the layout of the histogram
# to include a label for the y-axis
penguin_hist_base <- penguin_hist_base %>% layout(yaxis = list(title = 'count'))
To produce this plotly
histogram, run the R code below. Your histogram should appear in the Viewer
section of RStudio.
penguin_hist_base
As we noted earlier, plotly
graphs, unlike base R graphs, are interactive!
Notice that if you hover over the data in the histogram in 5.3, you can see the specific details (note that the graph in this document is also interactive!).
If you left-click and drag your cursor over a section to create a box, you can also zoom in on a particular section of the plot. Just double left-click to zoom back out.
Perhaps you are not impressed with plotly
yet. After all, our histogram doesn’t look that different to the base R version, so what is all the fuss about?
Well, it is very easy to modify our penguin_hist_base
plot_ly
graph to show extra detail. For example, we can easily produce separate histograms for the penguins on each island. Take a look at the R code below, which builds upon what we used in penguin_hist_base
.
penguin_hist <- plot_ly(data = penguins,
x = ~body_mass_g,
color = ~island,
type = "histogram", alpha = 0.6)
penguin_hist <- penguin_hist %>% layout(yaxis = list(title = 'count'),
barmode ="overlay")
Before you move on to the next question, run this code in R.
Note: Once you have taken some time to consider the code above, if you would like more details or would like to check the accuracy of your interpretation, click the Code
button below for a brief explanation.
# Here, we are creating a plotly object called "penguin_hist"
penguin_hist <- plot_ly(data = penguins, # We are using the penguins data
x = ~body_mass_g, # and modelling the body_mass_g data
color = ~island, type = "histogram", alpha = 0.6)
# We are producing a histogram for this data, with points coloured differently,
# depending on the island on which the penguin is located
# The code below is used to modify the layout of the histogram
# This includes adding a label to the y-axis
# and setting the histograms to be layered over each other
# (hence the alpha = 0.6 above to change the opacity)
penguin_hist <- penguin_hist %>% layout(yaxis = list(title = 'count'),
barmode ="overlay")
To produce this new plotly
histogram, run the R code below. Your histogram should appear in the Viewer
section of RStudio.
penguin_hist
This is looking better than our previous histogram! Because we have told our plot_ly
function to assign different colours to the different islands, we now have three histograms, rather than one with all the data clumped together.
Even better, these are all presented within the one plot, which also includes a handy legend. Hopefully you are now beginning to appreciate the increased functionality offered by plotly
over base R plots.
Finally, and perhaps most importantly for this specific example, it is important to note that we can dynamically filter out observations, to focus on data from a specific island. Simply click on one of the lines in the legend in the top right of our histogram in 5.6, to remove that data from assessment (note that the axes dynamically adjust too).
Try focusing just on the Dream island penguins.
Hint: To bring the removed data back, simply click once more on the relevant line in the legend.
That’s the end of the first data science computer lab!
Hopefully you have enjoyed this first computer lab, and now have a better idea of just how versatile R can be. Don’t worry if some of the code seems difficult at the moment - this is only the first week after all! Next week, we will continue working with plotly
and the palmerpenguins
data set, to produce even more detailed interactive plots.
Before you finish up, if you have been writing and running your code in RStudio, make sure to save your script file somewhere safe - it might come in handy later on.
LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAxQiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBNb2R1bGUgey19DQoNCiMjIyBUb3BpYyAxQjogRGF0YSBWaXN1YWxpc2F0aW9uIEkgey19DQoNCjxicj4NCg0KV2VsY29tZSB0byB0aGUgZmlyc3QgRGF0YSBTY2llbmNlIGNvbXB1dGVyIGxhYiBmb3IgU1RNMTAwMSENCg0KVGhyb3VnaG91dCB0aGUgc2VtZXN0ZXIsIHdlIHdpbGwgdXNlIHRoZSBSIHNvZnR3YXJlIGVudmlyb25tZW50IGZvciBhbGwgb3VyIHdvcmsuDQpSIGlzIHdpZGVseSB1c2VkIGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgYW5kIGRhdGEgdmlzdWFsaXNhdGlvbiwgYW5kIGluZGVlZCB0aGUgZmlyc3QgZm91ciBjb21wdXRlciBsYWJzIG9mIHRoZSBTVE0xMDAxIERhdGEgU2NpZW5jZSBtb2R1bGUgZm9jdXMgb24gZGF0YSB2aXN1YWxpc2F0aW9uIGluIFIuIA0KDQpJbiB0aGlzIGZpcnN0IGxhYiwgd2Ugd2lsbCBleHBsb3JlIHNvbWUgb2YgdGhlIG1vcmUgbGlnaHQtaGVhcnRlZCBvcHRpb25zIGF2YWlsYWJsZSB0byBSIHVzZXJzLCBhbmQgdGhlbiBpbnRyb2R1Y2UgYW4gZXhjZWxsZW50IHBhY2thZ2UsIGBwbG90bHlgIFtAcGxvdGx5XSwgd2hpY2ggYWxsb3dzIHVzIHRvIGNyZWF0ZSBpbnRlcmFjdGl2ZSBkYXRhIHZpc3VhbGlzYXRpb25zLg0KDQpCeSB0aGUgZW5kIG9mIHRoaXMgbGFiLCB5b3Ugc2hvdWxkIGZlZWwgY29tZm9ydGFibGUgbG9hZGluZyBhbmQgdXNpbmcgcGFja2FnZXMgaW4gUiwgYW5kIGJlIGFibGUgdG8gY3JlYXRlIGEgc2ltcGxlIGludGVyYWN0aXZlIGhpc3RvZ3JhbSB1c2luZyBgcGxvdGx5YC4NCg0KPGJyPg0KDQojIE1ha2luZyBtZW1lcyBpbiBSDQoNCkJhc2UgUiBjb250YWlucyBtYW55IGZ1bmN0aW9ucywgYW5kIGlzIHBlcmZlY3RseSBzdWZmaWNpZW50IGZvciBhIG51bWJlciBvZiBkYXRhIGFuYWx5c2lzIG1ldGhvZHMuDQpIb3dldmVyLCBvbmUgb2YgdGhlIGdyZWF0IGJlbmVmaXRzIG9mIFIgaXMgdGhhdCBhbnlvbmUgY2FuIGNyZWF0ZSAqKnBhY2thZ2VzKiogKGJ1bmRsZXMgb2YgY29kZSwgZGF0YSBhbmQgZnVuY3Rpb25zKSB3aGljaCBjYW4gYmUgdXBsb2FkZWQgdG8gZ2xvYmFsIHJlcG9zaXRvcmllcyAoc3VjaCBhcyBDUkFOIG9yIEJpb2NvbmR1Y3RvciksIGFuZCBtYWRlIGF2YWlsYWJsZSBmb3IgYW55b25lIGFyb3VuZCB0aGUgd29ybGQgdG8gZG93bmxvYWQgYW5kIHVzZSBpbiB0aGVpciB2ZXJzaW9uIG9mIFIuIA0KDQpPZnRlbiwgdGhlc2UgcGFja2FnZXMgYXJlIGV4dHJlbWVseSBoZWxwZnVsLiBUaGV5IG1heSBjb250YWluIHVzZWZ1bCBkYXRhIHNldHMgZm9yIGEgc3BlY2lmaWMgZmllbGQgb2YgcmVzZWFyY2gsIGFkZHJlc3MgYSBzaG9ydGNvbWluZyB3aXRoIHRoZSBiYXNlIFIgc3VpdGUgb2YgZnVuY3Rpb25zLCBhbGxvdyB1c2VycyB0byBwZXJmb3JtIHNwZWNpYWxpc2VkIGFuYWx5c2VzLCBhbmQvb3Igb2ZmZXIgdXNlcnMgc29tZSBhZGRpdGlvbmFsIGZ1bmN0aW9uYWxpdGllcy4gIA0KDQpPbiB0aGUgb3RoZXIgaGFuZCwgc29tZXRpbWVzIHRoZXNlIHBhY2thZ2VzIGFyZSBtb3JlIGxpZ2h0LWhlYXJ0ZWQsIHN1Y2ggYXMgdGhlIGBtZW1lYCBwYWNrYWdlIFtAbWVtZXNdLCB3aGljaCBhbGxvd3MgdXNlcnMgdG8gY3JlYXRlIHNpbXBsZSBtZW1lcyB3aXRoaW4gUi4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhpcyBwYWNrYWdlIG5vdy4NCg0KIyMgDQoNCkJlY2F1c2UgdGhlIGBtZW1lYCBwYWNrYWdlIGlzIG5vdCBpbnN0YWxsZWQgaW4gYmFzZSBSLCB3ZSBuZWVkIHRvIGRvd25sb2FkIGl0IGJlZm9yZSB3ZSBjYW4gdXNlIGl0Lg0KV2UgY2FuIHVzZSB0aGUgYGluc3RhbGwucGFja2FnZXMoKWAgUiBmdW5jdGlvbiB0byBkbyB0aGlzLCBhcyBzaG93biBpbiB0aGUgY29kZSBiZWxvdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgaW5jbHVkZSA9IEZ9DQppbnN0YWxsLnBhY2thZ2VzKCJtZW1lIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmluc3RhbGwucGFja2FnZXMoIm1lbWUiKQ0KYGBgDQoNCk9wZW4gdXAgUlN0dWRpbyBhbmQgcnVuIHRoaXMgY29kZSBub3cuDQoNCiMjDQoNCk9uY2UgdGhlIGBtZW1lYCBwYWNrYWdlIGlzIGRvd25sb2FkZWQgYW5kIGluc3RhbGxlZCwgd2UgbmVlZCB0byBsb2FkIGl0IGluIG91ciBjdXJyZW50IFIgc2Vzc2lvbi4NClJ1biB0aGUgZm9sbG93aW5nIGNvZGUgdG8gbG9hZCB0aGUgYG1lbWVgIHBhY2thZ2UuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KbGlicmFyeShtZW1lKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KG1lbWUpDQpgYGANCg0KIyMNCg0KR3JlYXQsIG5vdyB3ZSBjYW4gc3RhcnQgdG8gbWFrZSBzb21lIHNpbXBsZSBtZW1lcyENCldlIHJlYWxseSBvbmx5IG5lZWQgdHdvIGxpbmVzIG9mIGNvZGUgZm9yIHRoaXMuDQoNCkZpcnN0bHksIHdlIG5lZWQgdG8gZmluZCBhbiBhcHByb3ByaWF0ZSBpbWFnZS4gRm9yIHRoaXMgZXhhbXBsZSwgd2Ugd2lsbCB1c2UgYW4gaW1hZ2Ugb2YgSGFncmlkLCBmcm9tIHRoZSBIYXJyeSBQb3R0ZXIgc2VyaWVzLiBXZSBoYXZlIGxvY2F0ZWQgdGhpcyBpbWFnZSBvbmxpbmUsIGFuZCBjb3BpZWQgdGhlIHVybC4gSW4gUiwgd2UgYXNzaWduIHRoaXMgdXJsIHRvIHRoZSBvYmplY3QgYGhhZ3JpZGAsIGFzIHNob3duIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KaGFncmlkIDwtICJodHRwczovL2kuaW1nZmxpcC5jb20vMTN3YjJ0LmpwZyINCmBgYA0KDQoqTm90ZSB0aGF0IHRoZSB1cmwgbmVlZHMgdG8gYmUgY29udGFpbmVkIHdpdGhpbiBxdW90YXRpb24gbWFya3MuKg0KDQpNYWtlIHN1cmUgdG8gcnVuIHRoaXMgY29kZSBiZWZvcmUgbW92aW5nIG9uIHRvIHRoZSBuZXh0IHN0ZXAuDQoNCiMjDQoNCk5leHQsIHdlIHVzZSB0aGUgYG1lbWVgIGZ1bmN0aW9uIHRvIGFkZCBzb21lIHdvcmRzIHRvIHRoaXMgaW1hZ2UuIFRyeSBydW5uaW5nIHRoZSBjb2RlIGJlbG93LCBhbmQgc2VlIHdoYXQgaGFwcGVucy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCm1lbWUoaGFncmlkLCAiWWVyIGEgd2l6YXJkIiwgIndpdGggY29kaW5nIiwgZm9udCA9ICJzYW5zIikNCmBgYA0KDQoqTm90ZTogU29tZSB3YXJuaW5ncyBtYXkgYXBwZWFyIGluIHlvdXIgUiBDb25zb2xlIGFzIHRoaXMgY29kZSBpcyBleGVjdXRpbmcuIERvbid0IHdvcnJ5IGFib3V0IGl0LCBpdCBpcyBzYWZlIHRvIGlnbm9yZSB0aGVzZSB3YXJuaW5ncy4qIA0KDQojIw0KDQpJZiB5b3Ugd291bGQgbGlrZSB0byBzYXZlIHRoZSBtZW1lIHlvdSBoYXZlIG1hZGUsIGl0IGlzIGhlbHBmdWwgdG8gYXNzaWduIHRoZSBvdXRwdXQgb2YgdGhlIGBtZW1lYCBmdW5jdGlvbiB0byBhbiBvYmplY3QuDQpJbiB0aGUgY29kZSBiZWxvdywgd2UgbWFrZSBhIG5ldyBtZW1lLCBhbmQgYXNzaWduIGl0IHRvIHRoZSBvYmplY3QgYHN1Y2Nlc3NgLiBUcnkgcnVubmluZyB0aGlzIGNvZGUgbm93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0Kc3VjY2Vzc19raWQgPC0gImh0dHA6Ly9pMC5reW0tY2RuLmNvbS9lbnRyaWVzL2ljb25zL21vYmlsZS8wMDAvMDAwLzc0NS9zdWNjZXNzLmpwZyINCnN1Y2Nlc3MgPC0gbWVtZShzdWNjZXNzX2tpZCwgIlVzaW5nIFIiLCAidG8gbWFrZSBtZW1lcyIsIGZvbnQgPSAic2FucyIpDQpzdWNjZXNzDQpgYGANCg0KKkhpbnQ6IE5vdGljZSB0aGF0IHdlIG5lZWQgdG8gaW5jbHVkZSB0aGUgZmluYWwgbGluZSBvZiBjb2RlLCBjYWxsaW5nIHRoZSBvYmplY3QgYHN1Y2Nlc3NgLCBpbiBvcmRlciBmb3IgdGhlIGltYWdlIHRvIGJlIHNob3duLioNCg0KIyMgDQoNCk5vdyB3ZSBjYW4gc2F2ZSBvdXIgbWVtZSwgdXNpbmcgdGhlIGZ1bmN0aW9uIGBtZW1lX3NhdmVgLiBUYWtlIGEgbG9vayBhdCB0aGUgY29kZSBiZWxvdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCm1lbWVfc2F2ZShzdWNjZXNzLCBmaWxlPSJjOi9TVE0xMDAxL0RhdGEgU2NpZW5jZS9zdWNjZXNzX2tpZF9SX21lbWUucG5nIikgDQpgYGANCg0KSGVyZSwgd2UgYXJlIHNhdmluZyBvdXIgYHN1Y2Nlc3NgIG1lbWUsIHRvIHRoZSBmaWxlIGxvY2F0aW9uIGBjOlxTVE0xMDAxXERhdGEgU2NpZW5jZVxgLCB3aXRoIHRoZSBuYW1lIGBzdWNjZXNzX2tpZF9SX21lbWUucG5nYC4gDQoNCk5vdGUgdGhhdCBhbHRob3VnaCB0aGUgZmlsZSBwYXRoIG9uIG91ciBjb21wdXRlciBpbmNsdWRlcyBiYWNrc2xhc2hlcyAoYFxgKSwgaW4gUiBjb2RlIHRoZXNlIG5lZWQgdG8gYmUgY2hhbmdlZCB0byBmb3J3YXJkIHNsYXNoZXMgKGAvYCkuDQoNCiMjDQoNCk5vdyBpdCdzIHRpbWUgdG8gdHJ5IG1ha2luZyB5b3VyIG93biBtZW1lLiANCg0KYS4gRmluZCBhbiBhcHByb3ByaWF0ZSBpbWFnZSBvZiB5b3VyIGNob2ljZSBvbmxpbmUgKHBsZWFzZSBlbnN1cmUgeW91IHBpY2sgIGNvbnRlbnQgc3VpdGFibGUgZm9yIHVuaXZlcnNpdHkgYW5kIHdvcmspLiANCmIuIENvcHkgdGhlIHVybC4gDQoNCmMuIEFzc2lnbiB0aGlzIHVybCB0byBhbiBvYmplY3QgaW4gUi4NCmQuIFVzZSB0aGUgYG1lbWVgIGZ1bmN0aW9uIHRvIGFkZCB3b3JkcyB0byB5b3VyIGltYWdlLg0KZS4gU2F2ZSB5b3VyIG1lbWUgdXNpbmcgdGhlIGBtZW1lX3NhdmVgIGZ1bmN0aW9uLg0KDQoqSGludDogSWYgeW91IGFyZSBub3QgcXVpdGUgc3VyZSBob3cgdG8gYmVnaW4sIGNsaWNrIHRoZSBgQ29kZWAgYnV0dG9uIHRvIHRoZSByaWdodCBiZWxvdy4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQojIEZpcnN0LCB3ZSBuZWVkIHRvIGZpbmQgYW4gaW1hZ2UsIGFuZCBhc3NpZ24gaXQgdG8gYW4gb2JqZWN0IA0KIyAoaGVyZSB3ZSB1c2UgdGhlIGdlbmVyaWMgb2JqZWN0IG5hbWUgJ2ltYWdlX25hbWUnKQ0KIyBKdXN0IHJlcGxhY2UgdGhlIC4uLnMgd2l0aCB0aGUgdXJsIG9mIHlvdXIgaW1hZ2UNCmltYWdlX25hbWUgPC0gIi4uLiINCiMgTmV4dCwgd2UgbmVlZCB0byB1c2UgdGhlIG1lbWUgZnVuY3Rpb24sIHRvIGFkZCBzb21lIHdvcmRzIChqdXN0IHJlcGxhY2UgdGhlIC4uLnMpDQpteV9tZW1lIDwtIG1lbWUoaW1hZ2VfbmFtZSwgIi4uLiIsICIuLi4iLCBmb250ID0gInNhbnMiKQ0KIyBOb3RlIHRoYXQgeW91IG5lZWQgdG8gaW5jbHVkZSB0aGUgYCwgZm9udCA9ICJzYW5zImAgcGFydCB0byBlbnN1cmUgUiBrbm93IHdoaWNoIGZvbnQgdG8gdXNlLg0KIyBOb3cgYWxsIHRoYXQncyBsZWZ0IGlzIHRvIHNhdmUgeW91ciBtZW1lIC0ganVzdCByZWZlciB0byB0aGUgY29kZSBhYm92ZS4NCmBgYA0KDQpDb25ncmF0dWxhdGlvbnMhIFlvdSB3ZXJlIHByb2JhYmx5IG5vdCBleHBlY3RpbmcgdG8gbWFrZSBhIG1lbWUgaW4geW91ciBmaXJzdCBkYXRhIHNjaWVuY2UgY29tcHV0ZXIgbGFiLCBhbmQgdGhpcyBwcm9iYWJseSB3b24ndCBiZSBvbiB0aGUgZmluYWwgZXhhbSwgYnV0IGhvcGVmdWxseSB5b3UgYXJlIHN0YXJ0aW5nIHRvIHJlYWxpc2UgdGhhdCBSIGlzIHZlcnkgdmVyc2F0aWxlLg0KDQojIEN1c3RvbWl6aW5nIEdJRnMgaW4gUg0KDQpSIGlzIG5vdCBsaW1pdGVkIHRvIHdvcmtpbmcgd2l0aCBzdGF0aWMgaW1hZ2VzIC0gd2UgY2FuIG1vZGlmeSBhbmQgY3JlYXRlIGdpZnMgYW5kIGFuaW1hdGlvbnMuDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgdXNlIGFub3RoZXIgZnVuIHBhY2thZ2UsIHRoZSBgbWFnaWNrYCBwYWNrYWdlIFtAbWFnaWNrXSwgdG8gY3VzdG9taXplIGEgZ2lmLg0KDQpSdW4gdGhlIGZvbGxvd2luZyBjb2RlIHRvIGRvd25sb2FkLCBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgbWFnaWNrYCBwYWNrYWdlIGluIHlvdXIgY3VycmVudCBSIHNlc3Npb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIGluY2x1ZGUgPSBGfQ0KaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgaW5jbHVkZSA9IEZ9DQpsaWJyYXJ5KG1hZ2ljaykNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbGlicmFyeShtYWdpY2spDQpgYGANCg0KIyMNCg0KSnVzdCBhcyB3ZSBvYnRhaW5lZCBvbmxpbmUgaW1hZ2VzIG9mIGBoYWdyaWRgIGFuZCBgc3VjY2VzcyBraWRgLCBzbyB0b28gY2FuIHdlIHVzZSB1cmxzIHRvIGdpZnMgYW5kIGFuaW1hdGlvbnMuDQpGb3IgdGhpcyBleGFtcGxlLCB3ZSBoYXZlIHVzZWQgdGhlIHVybCB0byBhIHJvdGF0aW5nIGVhcnRoIGdpZi4NCg0KV2UgdXNlIHRoZSBgaW1hZ2VfcmVhZGAgZnVuY3Rpb24gdG8gcmVhZCB0aGlzIGdpZiBpbnRvIFIsIGFuZCBhc3NpZ24gaXQgdG8gdGhlIG9iamVjdCBgRWFydGhgLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCAsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KRWFydGggPC0gaW1hZ2VfcmVhZCgiaHR0cHM6Ly9pLmdpcGh5LmNvbS9tZWRpYS9tZjhVYklEZXc3ZThnL2dpcGh5LmdpZiIpDQpFYXJ0aA0KYGBgDQoNCk1ha2Ugc3VyZSB0byBydW4gdGhpcyBjb2RlIGJlZm9yZSBtb3Zpbmcgb24gdG8gdGhlIG5leHQgc3RlcCAoZG9uJ3Qgd29ycnkgaWYgaXQgdGFrZXMgYSBmZXcgc2Vjb25kcykuIFRoZSBnaWYgc2hvdWxkIGFwcGVhciBpbiB0aGUgYFZpZXdlcmAgc2VjdGlvbiBvZiBSU3R1ZGlvLg0KDQojIw0KDQpVc2luZyB0aGUgYG1hZ2lja2AgcGFja2FnZSwgd2UgY2FuIGVhc2lseSBtYWtlIHNvbWUgY2hhbmdlcyB0byB0aGlzIGdpZi4NCg0KVGFrZSBhIGxvb2sgYXQgdGhlIGNvZGUgYmVsb3cuIFlvdSB3aWxsIG5vdGljZSBoZXJlIHRoYXQ6IA0KDQoqIFdlIGhhdmUgIHJldmVyc2VkIHRoZSBnaWYsIHVzaW5nIHRoZSBgcmV2YCBmdW5jdGlvbg0KKiBXZSBoYXZlICBmbGlwcGVkIHRoZSBnaWYsIHVzaW5nIHRoZSBgaW1hZ2VfZmxpcGAgZnVuY3Rpb24sIGFuZA0KKiBXZSBoYXZlIGFkZGVkIHRleHQgdG8gdGhpcyBnaWYgdXNpbmcgdGhlIGBpbWFnZV9hbm5vdGF0ZWAgZnVuY3Rpb24NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnJldihFYXJ0aCkgJT4lIA0KICAgICAgICAgICBpbWFnZV9mbGlwKCkgJT4lIA0KICAgICAgICAgICBpbWFnZV9hbm5vdGF0ZSgiICAgICAgICBNZWFud2hpbGUsIGluIEF1c3RyYWxpYSIsIHNpemUgPSA0MCwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRyeSBydW5uaW5nIHRoaXMgY29kZSBub3cuDQoNCiMjDQoNClRoaXMgaXMgcmVhbGx5IGp1c3Qgc2NyYXRjaGluZyB0aGUgc3VyZmFjZSBvZiB0aGUgYG1hZ2lja2AgcGFja2FnZS4gSG93ZXZlciwgb3VyIGludGVudGlvbiBmb3IgdGhpcyBmaXJzdCBjb21wdXRlciBsYWIgaXMgdG8gZ2l2ZSB5b3UgYSB0YXN0ZSBvZiBzb21lIG9mIHRoZSBkaWZmZXJlbnQgcG9zc2liaWxpdGllcyBhdmFpbGFibGUgaW4gUiwgc28gZm9yIHRoZSBtb21lbnQsIGxldCdzIG1vdmUgb24uDQoNCiMgRHJhd2luZyBhIGZpc2ggaW4gUg0KDQpJbnN0ZWFkIG9mIHVzaW5nIGEgcHJlLWV4aXN0aW5nIGltYWdlIG9yIGdpZiwgbGV0J3Mgbm93IHRyeSB0byBjcmVhdGUgb25lIGZyb20gc2NyYXRjaC4gU3BlY2lmaWNhbGx5LCBsZXQncyBkcmF3IGEgZmlzaC4NClRvIGRvIHRoaXMsIHdlIGNhbiB1c2UgdGhlIGFwcHJvcHJpYXRlbHkgbmFtZWQgYHJmaXNoZHJhd2AgcGFja2FnZSBbQHJmaXNoZHJhd10uDQoNCiMjDQoNCkxldCdzIGRvd25sb2FkIGFuZCBpbnN0YWxsIHRoZSBgcmZpc2hkcmF3YCBwYWNrYWdlIG5vdy4gSW4gb3JkZXIgdG8gdXNlIHRoaXMgcGFja2FnZSwgd2Ugd2lsbCBhbHNvIG5lZWQgdG8gZG93bmxvYWQgYW5kIGluc3RhbGwgc29tZSBhZGRpdGlvbmFsIHBhY2thZ2VzLCB1cG9uIHdoaWNoIHRoZSBgcmZpc2hkcmF3YCBwYWNrYWdlIGRlcGVuZHMuIFN1Y2ggcGFja2FnZXMgYXJlIGtub3duIGFzICoqZGVwZW5kZW5jaWVzKiosIGFuZCBpdCBpcyBjb21tb24gZm9yIG1vcmUgc29waGlzdGljYXRlZCBSIHBhY2thZ2VzIHRvIGhhdmUgbXVsdGlwbGUgZGVwZW5kZW5jaWVzLg0KDQoqTm90ZSB0aGF0IHRoZXNlIGRlcGVuZGVuY2llcyBhcmUgcGFja2FnZXMgaW4gdGhlaXIgb3duIHJpZ2h0LioNCg0KUnVuIHRoaXMgY29kZSBpbiBSIG5vdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgaW5jbHVkZSA9IEZ9DQppbnN0YWxsLnBhY2thZ2VzKCJyZmlzaGRyYXciLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCmluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJyZmlzaGRyYXciKQ0KaW5zdGFsbC5wYWNrYWdlcygicGF0Y2h3b3JrIikNCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KbGlicmFyeSgicmZpc2hkcmF3IikNCmxpYnJhcnkoInBhdGNod29yayIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbGlicmFyeSgicmZpc2hkcmF3IikNCmxpYnJhcnkoInBhdGNod29yayIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmBgYA0KDQojIw0KDQpJZiB5b3Ugbm93IHJ1biB0aGUgY29kZSBiZWxvdywgYSBkZXRhaWxlZCBkcmF3aW5nIG9mIGEgZmlzaCBzaG91bGQgYXBwZWFyIGluIGEgbmV3IHdpbmRvdyENCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmdldF9wb2x5bGluZXMocGF0aCA9ICJpbnN0L2Zpc2hkcmF3LmpzIiwNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIGZvcm1hdCA9ICJzbWlsIiwNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIG91dHB1dCA9ICJhbmltYXRlZC5zdmciLA0KICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgZHJhd190eXBlID0gInJhbmRvbSIpDQoNCndpbmRvd3MoKSAjIElmIHlvdSBhcmUgdXNpbmcgYSBNYWMsIHJlcGxhY2Ugd2luZG93cygpIHdpdGg6IHF1YXJ0eigpDQoNCmZpc2hfZHJhdygpDQpgYGANCg0KIyMNCg0KU3VwcG9zZSB3ZSB3b3VsZCBsaWtlIHRvIGNoYW5nZSB0aGUgY29sb3VyIG9mIG91ciBmaXNoLiBXZSBjYW4gZG8gdGhpcywgYnkgaW5jbHVkaW5nIHRoZSBhcmd1bWVudCBgY29sID0gIi4uLiJgIHdpdGhpbiB0aGUgZnVuY3Rpb24gYGZpc2hfZHJhd2AuIEZvciBleGFtcGxlLCBpZiB3ZSB3b3VsZCBsaWtlIG91ciBmaXNoIHRvIGJlIGJsdWUsIHdlIGNhbiB3cml0ZQ0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KZmlzaF9kcmF3KGNvbCA9ICJibHVlIikNCmBgYA0KDQpUcnkgY2hhbmdpbmcgdGhpcyBjb2xvdXIgdG8gYSBkaWZmZXJlbnQgY29sb3VyLCBhbmQgdGhlbiBydW4gdGhlIGNvZGUuDQoNCiMgUGFsbWVyIFBlbmd1aW5zIERhdGEgU2V0IHsjcGVuZ3VpbnN9DQoNCk5vdyB0aGF0IHdlIGhhdmUgaGFkIGEgdGFzdGUgb2Ygc29tZSBvZiB0aGUgbW9yZSBsaWdodC1oZWFydGVkIFIgcGFja2FnZXMgb3V0IHRoZXJlLCBsZXQncyBjb25zaWRlciBhIHBhY2thZ2Ugd2hpY2ggY29udGFpbnMgc29tZSB1c2VmdWwgZGF0YS4NCg0KVGhlIGBwYWxtZXJwZW5ndWluc2AgUiBwYWNrYWdlIFtAcGVuZ3VpbnNdIGNvbnRhaW5zIGRhdGEsIGNvbGxlY3RlZCBvdmVyIHRoZSBjb3Vyc2Ugb2Ygc2V2ZXJhbCB5ZWFycywgb24gMyBzcGVjaWVzIG9mIHBlbmd1aW4gbGl2aW5nIG9uIGRpZmZlcmVudCBpc2xhbmRzIGluIHRoZSBQYWxtZXIgYXJjaGlwZWxhZ28sIG9mZiB0aGUgY29hc3Qgb2YgQW50YXJjdGljYS4gDQpGb3IgbW9yZSBkZXRhaWxzLCB5b3UgY2FuIHJlZmVyIHRvIFtTZWN0aW9uIDIgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvcGVuZ3VpbnMuaHRtbCkuDQoNCiMjDQoNCkp1c3QgbGlrZSB0aGUgcHJldmlvdXMgcGFja2FnZXMsIHdlIHdpbGwgbmVlZCB0byBkb3dubG9hZCBhbmQgbG9hZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlIGJlZm9yZSB3ZSBjYW4gYmVnaW4gd29ya2luZyB3aXRoIHRoaXMgcGVuZ3VpbiBkYXRhLg0KDQpSdW4gdGhlIGNvZGUgYmVsb3cgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlIGluIFIuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmc9Rn0NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpgYGANCg0KIyMNCg0KV2UgY2FuIHVzZSB0aGUgYHN1bW1hcnlgIGZ1bmN0aW9uIHRvIG9idGFpbiBhIHF1aWNrIG92ZXJ2aWV3IG9mIHRoZSBkYXRhIGNvbnRhaW5lZCB3aXRoaW4gdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQojIFRoaXMgY29kZSBzdW1tYXJpc2VzIHRoZSBkYXRhIGluIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UuDQpzdW1tYXJ5KHBlbmd1aW5zKQ0KYGBgDQoNCkRvbid0IHdvcnJ5IHRvbyBtdWNoIGFib3V0IHRoZSB2YWx1ZXMgc2hvd24gaW4gdGhlIHN1bW1hcnkgdGFibGUgLSB0aGUgbWFpbiB0aGluZ3MgdG8gbm90ZSBhdCB0aGlzIHN0YWdlIGFyZSB0aGUgZGlmZmVyZW50IHZhcmlhYmxlcywgbmFtZWx5IGBzcGVjaWVzYCwgYGlzbGFuZGAsIGBiaWxsX2xlbmd0aF9tbWAsIGBiaWxsX2RlcHRoX21tYCwgYGZsaXBwZXJfbGVuZ3RoX21tYCwgYGJvZHlfbWFzc19nYCwgYHNleGAgYW5kIGB5ZWFyYC4NCg0KIyBJbnRlcmFjdGl2ZSBIaXN0b2dyYW1zDQoNClN1cHBvc2UgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIHByb2R1Y2UgaGlzdG9ncmFtcyBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHBlbmd1aW5zJyBgYm9keV9tYXNzX2dgIHZhbHVlcyAodGhlaXIgYm9keSBtYXNzIGluIGdyYW1zKS4gV2UgY291bGQgY3JlYXRlIGEgc2ltcGxlIGhpc3RvZ3JhbSB1c2luZyB0aGUgYmFzZSBSIGBoaXN0YCBmdW5jdGlvbiB2aWEgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KaGlzdChwZW5ndWlucyRib2R5X21hc3NfZywgYnJlYWtzID0gMTkpDQpgYGANCg0KSG93ZXZlciwgdGhpcyBoaXN0b2dyYW0gaGFzIHNvbWUgc2hvcnRjb21pbmdzLiBGaXJzdGx5LCBpdCBpcyBzdGF0aWMuIFdlIGNhbid0IGludGVyYWN0IHdpdGggdGhlIGltYWdlLCBhbmQgd2UgY2FuJ3QgbWFuaXB1bGF0ZSBpdCBpbiByZWFsIHRpbWUgdG8gZGlzcGxheSBkaWZmZXJlbnQgZGV0YWlscy4gDQoNCkZvciBleGFtcGxlLCBwZXJoYXBzIHdlIHdvdWxkIGxpa2UgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHBlbmd1aW5zJyBgYm9keV9tYXNzX2dgIHZhbHVlcywgYnV0IG9ubHkgZm9yIHRoZSBwZW5ndWlucyBvbiBhIHNwZWNpZmljIGlzbGFuZC4gV2Ugd291bGQgbmVlZCB0byBkbyBzb21lIG1vcmUgY29kaW5nIHRvIHByb2R1Y2Ugc3VjaCBhIGhpc3RvZ3JhbSBpbiBiYXNlIFIuIEV2ZW4gdGhlbiwgaWYgd2Ugd291bGQgbGlrZSB0byBoYXZlIHNpbWlsYXIgaGlzdG9ncmFtcyBmb3IgdGhlIG90aGVyIHR3byBpc2xhbmRzLCB0aGlzIHdvdWxkIG1lYW4gZnVydGhlciBjb2RpbmcuDQoNCkFsdGVybmF0aXZlbHksIHdlIGNvdWxkIHVzZSB0aGUgYHBsb3RseWAgcGFja2FnZSB0byBjcmVhdGUgYW4gaW50ZXJhY3RpdmUsIHJlc3BvbnNpdmUgaGlzdG9ncmFtLiBMZXQncyB0YWtlIGEgbG9vayBhdCBob3cgdG8gZG8gdGhpcyBub3cuDQoNCiMjDQoNClRvIGJlZ2luLCBqdXN0IGFzIGZvciB0aGUgcHJldmlvdXMgcGFja2FnZXMsIHdlIHdpbGwgbmVlZCB0byBkb3dubG9hZCBhbmQgbG9hZCB0aGUgYHBsb3RseWAgcGFja2FnZSBpbiBSLCBiZWZvcmUgd2UgY2FuIHVzZSBhbnkgYHBsb3RseWAgZnVuY3Rpb25zLg0KDQpSdW4gdGhlIGNvZGUgYmVsb3cgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYHBsb3RseWAgcGFja2FnZSBpbiBSLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBGLCBpbmNsdWRlID0gRn0NCmluc3RhbGwucGFja2FnZXMoInBsb3RseSIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0NCmxpYnJhcnkocGxvdGx5KQ0KYGBgDQoNCiMjDQoNClRvIGNyZWF0ZSBgcGxvdGx5YCBwbG90cywgd2UgIHVzZSB0aGUgZnVuY3Rpb24gYHBsb3RfbHkoKWAuIFdlIHdvbid0IHdvcnJ5IHRvbyBtdWNoIGFib3V0IHRoZSBjb21wb3NpdGlvbiBvZiB0aGlzIGZ1bmN0aW9uIGp1c3QgeWV0IC0gd2UnbGwgY292ZXIgdGhpcyBpbiBtb3JlIGRldGFpbCBuZXh0IHdlZWsuIEZvciB0aGUgbW9tZW50LCB0YWtlIGEgbG9vayBhdCB0aGUgY29kZSBiZWxvdywgYW5kIHNlZSBpZiB5b3UgY2FuIGdldCBhIGdlbmVyYWwgaWRlYSBvZiB3aGF0J3MgZ29pbmcgb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnBlbmd1aW5faGlzdF9iYXNlIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIikNCg0KcGVuZ3Vpbl9oaXN0X2Jhc2UgPC0gcGVuZ3Vpbl9oaXN0X2Jhc2UgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSAnY291bnQnKSkNCmBgYA0KDQpCZWZvcmUgeW91IG1vdmUgb24gdG8gdGhlIG5leHQgcXVlc3Rpb24sIHJ1biB0aGlzIGNvZGUgaW4gUi4NCg0KKk5vdGU6IE9uY2UgeW91IGhhdmUgdGFrZW4gc29tZSB0aW1lIHRvIGNvbnNpZGVyIHRoZSBjb2RlIGFib3ZlLCBpZiB5b3Ugd291bGQgbGlrZSBtb3JlIGRldGFpbHMgb3Igd291bGQgbGlrZSB0byBjaGVjayB0aGUgYWNjdXJhY3kgb2YgeW91ciBpbnRlcnByZXRhdGlvbiwgY2xpY2sgdGhlIGBDb2RlYCBidXR0b24gYmVsb3cgZm9yIGEgYnJpZWYgZXhwbGFuYXRpb24uKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KIyBIZXJlLCB3ZSBhcmUgY3JlYXRpbmcgYSBwbG90bHkgb2JqZWN0IGNhbGxlZCAicGVuZ3Vpbl9oaXN0X2Jhc2UiDQpwZW5ndWluX2hpc3RfYmFzZSA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgIyBXZSBhcmUgdXNpbmcgdGhlIHBlbmd1aW5zIGRhdGENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgIyBhbmQgbW9kZWxsaW5nIHRoZSBib2R5X21hc3NfZyBkYXRhDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIikgIyBpbiBhIGhpc3RvZ3JhbSBmb3JtYXQNCg0KIyBUaGUgY29kZSBiZWxvdyBpcyB1c2VkIHRvIG1vZGlmeSB0aGUgbGF5b3V0IG9mIHRoZSBoaXN0b2dyYW0NCiMgdG8gaW5jbHVkZSBhIGxhYmVsIGZvciB0aGUgeS1heGlzDQpwZW5ndWluX2hpc3RfYmFzZSA8LSBwZW5ndWluX2hpc3RfYmFzZSAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9ICdjb3VudCcpKQ0KYGBgDQoNCiMjIHsjYmFzZWhpc3R9DQoNClRvIHByb2R1Y2UgdGhpcyBgcGxvdGx5YCBoaXN0b2dyYW0sIHJ1biB0aGUgUiBjb2RlIGJlbG93LiBZb3VyIGhpc3RvZ3JhbSBzaG91bGQgYXBwZWFyIGluIHRoZSBgVmlld2VyYCBzZWN0aW9uIG9mIFJTdHVkaW8uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluX2hpc3RfYmFzZQ0KYGBgDQoNCiMjDQoNCkFzIHdlIG5vdGVkIGVhcmxpZXIsIGBwbG90bHlgIGdyYXBocywgdW5saWtlIGJhc2UgUiBncmFwaHMsIGFyZSBpbnRlcmFjdGl2ZSENCg0KTm90aWNlIHRoYXQgaWYgeW91IGhvdmVyIG92ZXIgdGhlIGRhdGEgaW4gdGhlIGhpc3RvZ3JhbSBpbiBcQHJlZihiYXNlaGlzdCksIHlvdSBjYW4gc2VlIHRoZSBzcGVjaWZpYyBkZXRhaWxzIChub3RlIHRoYXQgdGhlIGdyYXBoIGluIHRoaXMgZG9jdW1lbnQgaXMgYWxzbyBpbnRlcmFjdGl2ZSEpLiANCklmIHlvdSBsZWZ0LWNsaWNrIGFuZCBkcmFnIHlvdXIgY3Vyc29yIG92ZXIgYSBzZWN0aW9uIHRvIGNyZWF0ZSBhIGJveCwgeW91IGNhbiBhbHNvIHpvb20gaW4gb24gYSBwYXJ0aWN1bGFyIHNlY3Rpb24gb2YgdGhlIHBsb3QuIEp1c3QgZG91YmxlIGxlZnQtY2xpY2sgdG8gem9vbSBiYWNrIG91dC4NCg0KIyMNCg0KUGVyaGFwcyB5b3UgYXJlIG5vdCBpbXByZXNzZWQgd2l0aCBgcGxvdGx5YCB5ZXQuIEFmdGVyIGFsbCwgb3VyIGhpc3RvZ3JhbSBkb2Vzbid0IGxvb2sgdGhhdCBkaWZmZXJlbnQgdG8gdGhlIGJhc2UgUiB2ZXJzaW9uLCBzbyB3aGF0IGlzIGFsbCB0aGUgZnVzcyBhYm91dD8NCg0KV2VsbCwgaXQgaXMgdmVyeSBlYXN5IHRvIG1vZGlmeSBvdXIgYHBlbmd1aW5faGlzdF9iYXNlYCBgcGxvdF9seWAgZ3JhcGggdG8gc2hvdyBleHRyYSBkZXRhaWwuIEZvciBleGFtcGxlLCB3ZSBjYW4gZWFzaWx5IHByb2R1Y2Ugc2VwYXJhdGUgaGlzdG9ncmFtcyBmb3IgdGhlIHBlbmd1aW5zIG9uIGVhY2ggaXNsYW5kLiBUYWtlIGEgbG9vayBhdCB0aGUgUiBjb2RlIGJlbG93LCB3aGljaCBidWlsZHMgdXBvbiB3aGF0IHdlIHVzZWQgaW4gYHBlbmd1aW5faGlzdF9iYXNlYC4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3Vpbl9oaXN0IDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+aXNsYW5kLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIiwgYWxwaGEgPSAwLjYpDQoNCnBlbmd1aW5faGlzdCA8LSBwZW5ndWluX2hpc3QgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSAnY291bnQnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFybW9kZSA9Im92ZXJsYXkiKQ0KYGBgDQoNCkJlZm9yZSB5b3UgbW92ZSBvbiB0byB0aGUgbmV4dCBxdWVzdGlvbiwgcnVuIHRoaXMgY29kZSBpbiBSLg0KDQoqTm90ZTogT25jZSB5b3UgaGF2ZSB0YWtlbiBzb21lIHRpbWUgdG8gY29uc2lkZXIgdGhlIGNvZGUgYWJvdmUsIGlmIHlvdSB3b3VsZCBsaWtlIG1vcmUgZGV0YWlscyBvciB3b3VsZCBsaWtlIHRvIGNoZWNrIHRoZSBhY2N1cmFjeSBvZiB5b3VyIGludGVycHJldGF0aW9uLCBjbGljayB0aGUgYENvZGVgIGJ1dHRvbiBiZWxvdyBmb3IgYSBicmllZiBleHBsYW5hdGlvbi4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQojIEhlcmUsIHdlIGFyZSBjcmVhdGluZyBhIHBsb3RseSBvYmplY3QgY2FsbGVkICJwZW5ndWluX2hpc3QiDQpwZW5ndWluX2hpc3QgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsICMgV2UgYXJlIHVzaW5nIHRoZSBwZW5ndWlucyBkYXRhDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCAjIGFuZCBtb2RlbGxpbmcgdGhlIGJvZHlfbWFzc19nIGRhdGENCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfmlzbGFuZCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBhbHBoYSA9IDAuNikNCiMgV2UgYXJlIHByb2R1Y2luZyBhIGhpc3RvZ3JhbSBmb3IgdGhpcyBkYXRhLCB3aXRoIHBvaW50cyBjb2xvdXJlZCBkaWZmZXJlbnRseSwgDQojIGRlcGVuZGluZyBvbiB0aGUgaXNsYW5kIG9uIHdoaWNoIHRoZSBwZW5ndWluIGlzIGxvY2F0ZWQNCg0KIyBUaGUgY29kZSBiZWxvdyBpcyB1c2VkIHRvIG1vZGlmeSB0aGUgbGF5b3V0IG9mIHRoZSBoaXN0b2dyYW0NCiMgVGhpcyBpbmNsdWRlcyBhZGRpbmcgYSBsYWJlbCB0byB0aGUgeS1heGlzDQojIGFuZCBzZXR0aW5nIHRoZSBoaXN0b2dyYW1zIHRvIGJlIGxheWVyZWQgb3ZlciBlYWNoIG90aGVyDQojIChoZW5jZSB0aGUgYWxwaGEgPSAwLjYgYWJvdmUgdG8gY2hhbmdlIHRoZSBvcGFjaXR5KQ0KcGVuZ3Vpbl9oaXN0IDwtIHBlbmd1aW5faGlzdCAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9ICdjb3VudCcpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXJtb2RlID0ib3ZlcmxheSIpDQpgYGANCg0KIyMgeyNpc2xhbmRzaGlzdH0NCg0KVG8gcHJvZHVjZSB0aGlzIG5ldyBgcGxvdGx5YCBoaXN0b2dyYW0sIHJ1biB0aGUgUiBjb2RlIGJlbG93LiBZb3VyIGhpc3RvZ3JhbSBzaG91bGQgYXBwZWFyIGluIHRoZSBgVmlld2VyYCBzZWN0aW9uIG9mIFJTdHVkaW8uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluX2hpc3QNCmBgYA0KDQpUaGlzIGlzIGxvb2tpbmcgYmV0dGVyIHRoYW4gb3VyIHByZXZpb3VzIGhpc3RvZ3JhbSEgQmVjYXVzZSB3ZSBoYXZlIHRvbGQgb3VyIGBwbG90X2x5YCBmdW5jdGlvbiB0byBhc3NpZ24gZGlmZmVyZW50IGNvbG91cnMgdG8gdGhlIGRpZmZlcmVudCBpc2xhbmRzLCB3ZSBub3cgaGF2ZSB0aHJlZSBoaXN0b2dyYW1zLCByYXRoZXIgdGhhbiBvbmUgd2l0aCBhbGwgdGhlIGRhdGEgY2x1bXBlZCB0b2dldGhlci4NCg0KRXZlbiBiZXR0ZXIsIHRoZXNlIGFyZSBhbGwgcHJlc2VudGVkIHdpdGhpbiB0aGUgb25lIHBsb3QsIHdoaWNoIGFsc28gaW5jbHVkZXMgYSBoYW5keSBsZWdlbmQuIEhvcGVmdWxseSB5b3UgYXJlIG5vdyBiZWdpbm5pbmcgdG8gYXBwcmVjaWF0ZSB0aGUgaW5jcmVhc2VkIGZ1bmN0aW9uYWxpdHkgb2ZmZXJlZCBieSBgcGxvdGx5YCBvdmVyIGJhc2UgUiBwbG90cy4NCg0KIyMNCg0KRmluYWxseSwgYW5kIHBlcmhhcHMgbW9zdCBpbXBvcnRhbnRseSBmb3IgdGhpcyBzcGVjaWZpYyBleGFtcGxlLCBpdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHdlIGNhbiBkeW5hbWljYWxseSBmaWx0ZXIgb3V0IG9ic2VydmF0aW9ucywgdG8gZm9jdXMgb24gZGF0YSBmcm9tIGEgc3BlY2lmaWMgaXNsYW5kLiBTaW1wbHkgY2xpY2sgb24gb25lIG9mIHRoZSBsaW5lcyBpbiB0aGUgbGVnZW5kIGluIHRoZSB0b3AgcmlnaHQgb2Ygb3VyIGhpc3RvZ3JhbSBpbiBcQHJlZihpc2xhbmRzaGlzdCksIHRvIHJlbW92ZSB0aGF0IGRhdGEgZnJvbSBhc3Nlc3NtZW50IChub3RlIHRoYXQgdGhlIGF4ZXMgZHluYW1pY2FsbHkgYWRqdXN0IHRvbykuIA0KDQpUcnkgZm9jdXNpbmcganVzdCBvbiB0aGUgRHJlYW0gaXNsYW5kIHBlbmd1aW5zLg0KDQoqSGludDogVG8gYnJpbmcgdGhlIHJlbW92ZWQgZGF0YSBiYWNrLCBzaW1wbHkgY2xpY2sgb25jZSBtb3JlIG9uIHRoZSByZWxldmFudCBsaW5lIGluIHRoZSBsZWdlbmQuKg0KDQo8YnI+DQoNCiMjIyMgVGhhdCdzIHRoZSBlbmQgb2YgdGhlIGZpcnN0IGRhdGEgc2NpZW5jZSBjb21wdXRlciBsYWIhICMjIyMgey19DQoNCkhvcGVmdWxseSB5b3UgaGF2ZSBlbmpveWVkIHRoaXMgZmlyc3QgY29tcHV0ZXIgbGFiLCBhbmQgbm93IGhhdmUgYSBiZXR0ZXIgaWRlYSBvZiBqdXN0IGhvdyB2ZXJzYXRpbGUgUiBjYW4gYmUuIERvbid0IHdvcnJ5IGlmIHNvbWUgb2YgdGhlIGNvZGUgc2VlbXMgZGlmZmljdWx0IGF0IHRoZSBtb21lbnQgLSB0aGlzIGlzIG9ubHkgdGhlIGZpcnN0IHdlZWsgYWZ0ZXIgYWxsISBOZXh0IHdlZWssIHdlIHdpbGwgY29udGludWUgd29ya2luZyB3aXRoIGBwbG90bHlgIGFuZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBkYXRhIHNldCwgdG8gcHJvZHVjZSBldmVuIG1vcmUgZGV0YWlsZWQgaW50ZXJhY3RpdmUgcGxvdHMuDQoNCkJlZm9yZSB5b3UgZmluaXNoIHVwLCBpZiB5b3UgaGF2ZSBiZWVuIHdyaXRpbmcgYW5kIHJ1bm5pbmcgeW91ciBjb2RlIGluIFJTdHVkaW8sIG1ha2Ugc3VyZSB0byBzYXZlIHlvdXIgc2NyaXB0IGZpbGUgc29tZXdoZXJlIHNhZmUgLSBpdCBtaWdodCBjb21lIGluIGhhbmR5IGxhdGVyIG9uLiANCg0KPGJyPg0KDQojIFJlZmVyZW5jZXMgey0gI1JlZn0NCjxkaXYgaWQ9InJlZnMiPjwvZGl2Pg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UuIFRoZSBjb3B5cmlnaHQgZm9yIHRoZSBtYXRlcmlhbCBpbiB0aGVzZSBub3RlcyByZXNpZGVzIHdpdGggdGhlIGF1dGhvciBuYW1lZCBhYm92ZSwgd2l0aCB0aGUgRGVwYXJ0bWVudCBvZiBNYXRoZW1hdGljcyBhbmQgU3RhdGlzdGljcyBhbmQgd2l0aCBMYSBUcm9iZSBVbml2ZXJzaXR5LiBDb3B5cmlnaHQgaW4gdGhpcyB3b3JrIGlzIHZlc3RlZCBpbiBMYSBUcm9iZSBVbml2ZXJzaXR5IGluY2x1ZGluZyBhbGwgTGEgVHJvYmUgVW5pdmVyc2l0eSBicmFuZGluZyBhbmQgbmFtaW5nLiBVbmxlc3Mgb3RoZXJ3aXNlIHN0YXRlZCwgbWF0ZXJpYWwgd2l0aGluIHRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tTm9uIENvbW1lcmNpYWwtTm9uIERlcml2YXRpdmVzIExpY2Vuc2UgDQo8YSBocmVmID0gImh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1uZC80LjAvQ0MiIHRhcmdldD0iX2JsYW5rIj4gQlktTkMtTkQuIDwvYT4NCjwvZm9udD4=