Data Science Module

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.


1 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.

1.1

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.

1.2

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)

1.3

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.

1.4

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.

1.5

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.

1.6

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 (/).

1.7

Now it’s time to try making your own meme.

  1. Find an appropriate image of your choice online (please ensure you pick content suitable for university and work).

  2. Copy the url.

  3. Assign this url to an object in R.

  4. Use the meme function to add words to your image.

  5. 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.

2 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)

2.1

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.

2.2

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.

2.3

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.

3 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).

3.1

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")

3.2

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()

3.3

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.

4 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.

4.1

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)

4.2

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.

5 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.

5.1

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)

5.2

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'))

5.3

To produce this plotly histogram, run the R code below. Your histogram should appear in the Viewer section of RStudio.

penguin_hist_base

5.4

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.

5.5

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")

5.6

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.

5.7

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.


References

Ding, Liuyong. 2021. rfishdraw: Automatically Generated Fish Drawings via JavaScript. https://github.com/Otoliths/rfishdraw.
Horst, Allison Marie, Alison Presmanes Hill, and Kristen B Gorman. 2020. Palmerpenguins: Palmer Archipelago (Antarctica) Penguin Data. https://doi.org/10.5281/zenodo.3960218.
Ooms, Jeroen. 2021. magick: advanced graphics and image-processing in R. https://docs.ropensci.org/magick/.
Sievert, Carson. 2020. Interactive Web-Based Data Visualization with r, Plotly, and Shiny. Chapman; Hall/CRC. https://plotly-r.com.
Yu, Guangchuang. 2021. meme: create memes in R. https://github.com/GuangchuangYu/meme/.


These notes have been prepared by Rupert Kuveke. The copyright for the material in these notes resides with the author named above, with the Department of Mathematics and Statistics and with La Trobe University. Copyright in this work is vested in La Trobe University including all La Trobe University branding and naming. Unless otherwise stated, material within this work is licensed under a Creative Commons Attribution-Non Commercial-Non Derivatives License BY-NC-ND.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAxQiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBNb2R1bGUgey19DQoNCiMjIyBUb3BpYyAxQjogRGF0YSBWaXN1YWxpc2F0aW9uIEkgey19DQoNCjxicj4NCg0KV2VsY29tZSB0byB0aGUgZmlyc3QgRGF0YSBTY2llbmNlIGNvbXB1dGVyIGxhYiBmb3IgU1RNMTAwMSENCg0KVGhyb3VnaG91dCB0aGUgc2VtZXN0ZXIsIHdlIHdpbGwgdXNlIHRoZSBSIHNvZnR3YXJlIGVudmlyb25tZW50IGZvciBhbGwgb3VyIHdvcmsuDQpSIGlzIHdpZGVseSB1c2VkIGZvciBzdGF0aXN0aWNhbCBjb21wdXRpbmcgYW5kIGRhdGEgdmlzdWFsaXNhdGlvbiwgYW5kIGluZGVlZCB0aGUgZmlyc3QgZm91ciBjb21wdXRlciBsYWJzIG9mIHRoZSBTVE0xMDAxIERhdGEgU2NpZW5jZSBtb2R1bGUgZm9jdXMgb24gZGF0YSB2aXN1YWxpc2F0aW9uIGluIFIuIA0KDQpJbiB0aGlzIGZpcnN0IGxhYiwgd2Ugd2lsbCBleHBsb3JlIHNvbWUgb2YgdGhlIG1vcmUgbGlnaHQtaGVhcnRlZCBvcHRpb25zIGF2YWlsYWJsZSB0byBSIHVzZXJzLCBhbmQgdGhlbiBpbnRyb2R1Y2UgYW4gZXhjZWxsZW50IHBhY2thZ2UsIGBwbG90bHlgIFtAcGxvdGx5XSwgd2hpY2ggYWxsb3dzIHVzIHRvIGNyZWF0ZSBpbnRlcmFjdGl2ZSBkYXRhIHZpc3VhbGlzYXRpb25zLg0KDQpCeSB0aGUgZW5kIG9mIHRoaXMgbGFiLCB5b3Ugc2hvdWxkIGZlZWwgY29tZm9ydGFibGUgbG9hZGluZyBhbmQgdXNpbmcgcGFja2FnZXMgaW4gUiwgYW5kIGJlIGFibGUgdG8gY3JlYXRlIGEgc2ltcGxlIGludGVyYWN0aXZlIGhpc3RvZ3JhbSB1c2luZyBgcGxvdGx5YC4NCg0KPGJyPg0KDQojIE1ha2luZyBtZW1lcyBpbiBSDQoNCkJhc2UgUiBjb250YWlucyBtYW55IGZ1bmN0aW9ucywgYW5kIGlzIHBlcmZlY3RseSBzdWZmaWNpZW50IGZvciBhIG51bWJlciBvZiBkYXRhIGFuYWx5c2lzIG1ldGhvZHMuDQpIb3dldmVyLCBvbmUgb2YgdGhlIGdyZWF0IGJlbmVmaXRzIG9mIFIgaXMgdGhhdCBhbnlvbmUgY2FuIGNyZWF0ZSAqKnBhY2thZ2VzKiogKGJ1bmRsZXMgb2YgY29kZSwgZGF0YSBhbmQgZnVuY3Rpb25zKSB3aGljaCBjYW4gYmUgdXBsb2FkZWQgdG8gZ2xvYmFsIHJlcG9zaXRvcmllcyAoc3VjaCBhcyBDUkFOIG9yIEJpb2NvbmR1Y3RvciksIGFuZCBtYWRlIGF2YWlsYWJsZSBmb3IgYW55b25lIGFyb3VuZCB0aGUgd29ybGQgdG8gZG93bmxvYWQgYW5kIHVzZSBpbiB0aGVpciB2ZXJzaW9uIG9mIFIuIA0KDQpPZnRlbiwgdGhlc2UgcGFja2FnZXMgYXJlIGV4dHJlbWVseSBoZWxwZnVsLiBUaGV5IG1heSBjb250YWluIHVzZWZ1bCBkYXRhIHNldHMgZm9yIGEgc3BlY2lmaWMgZmllbGQgb2YgcmVzZWFyY2gsIGFkZHJlc3MgYSBzaG9ydGNvbWluZyB3aXRoIHRoZSBiYXNlIFIgc3VpdGUgb2YgZnVuY3Rpb25zLCBhbGxvdyB1c2VycyB0byBwZXJmb3JtIHNwZWNpYWxpc2VkIGFuYWx5c2VzLCBhbmQvb3Igb2ZmZXIgdXNlcnMgc29tZSBhZGRpdGlvbmFsIGZ1bmN0aW9uYWxpdGllcy4gIA0KDQpPbiB0aGUgb3RoZXIgaGFuZCwgc29tZXRpbWVzIHRoZXNlIHBhY2thZ2VzIGFyZSBtb3JlIGxpZ2h0LWhlYXJ0ZWQsIHN1Y2ggYXMgdGhlIGBtZW1lYCBwYWNrYWdlIFtAbWVtZXNdLCB3aGljaCBhbGxvd3MgdXNlcnMgdG8gY3JlYXRlIHNpbXBsZSBtZW1lcyB3aXRoaW4gUi4gTGV0J3MgdGFrZSBhIGxvb2sgYXQgdGhpcyBwYWNrYWdlIG5vdy4NCg0KIyMgDQoNCkJlY2F1c2UgdGhlIGBtZW1lYCBwYWNrYWdlIGlzIG5vdCBpbnN0YWxsZWQgaW4gYmFzZSBSLCB3ZSBuZWVkIHRvIGRvd25sb2FkIGl0IGJlZm9yZSB3ZSBjYW4gdXNlIGl0Lg0KV2UgY2FuIHVzZSB0aGUgYGluc3RhbGwucGFja2FnZXMoKWAgUiBmdW5jdGlvbiB0byBkbyB0aGlzLCBhcyBzaG93biBpbiB0aGUgY29kZSBiZWxvdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgaW5jbHVkZSA9IEZ9DQppbnN0YWxsLnBhY2thZ2VzKCJtZW1lIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmluc3RhbGwucGFja2FnZXMoIm1lbWUiKQ0KYGBgDQoNCk9wZW4gdXAgUlN0dWRpbyBhbmQgcnVuIHRoaXMgY29kZSBub3cuDQoNCiMjDQoNCk9uY2UgdGhlIGBtZW1lYCBwYWNrYWdlIGlzIGRvd25sb2FkZWQgYW5kIGluc3RhbGxlZCwgd2UgbmVlZCB0byBsb2FkIGl0IGluIG91ciBjdXJyZW50IFIgc2Vzc2lvbi4NClJ1biB0aGUgZm9sbG93aW5nIGNvZGUgdG8gbG9hZCB0aGUgYG1lbWVgIHBhY2thZ2UuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KbGlicmFyeShtZW1lKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KG1lbWUpDQpgYGANCg0KIyMNCg0KR3JlYXQsIG5vdyB3ZSBjYW4gc3RhcnQgdG8gbWFrZSBzb21lIHNpbXBsZSBtZW1lcyENCldlIHJlYWxseSBvbmx5IG5lZWQgdHdvIGxpbmVzIG9mIGNvZGUgZm9yIHRoaXMuDQoNCkZpcnN0bHksIHdlIG5lZWQgdG8gZmluZCBhbiBhcHByb3ByaWF0ZSBpbWFnZS4gRm9yIHRoaXMgZXhhbXBsZSwgd2Ugd2lsbCB1c2UgYW4gaW1hZ2Ugb2YgSGFncmlkLCBmcm9tIHRoZSBIYXJyeSBQb3R0ZXIgc2VyaWVzLiBXZSBoYXZlIGxvY2F0ZWQgdGhpcyBpbWFnZSBvbmxpbmUsIGFuZCBjb3BpZWQgdGhlIHVybC4gSW4gUiwgd2UgYXNzaWduIHRoaXMgdXJsIHRvIHRoZSBvYmplY3QgYGhhZ3JpZGAsIGFzIHNob3duIGJlbG93Og0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KaGFncmlkIDwtICJodHRwczovL2kuaW1nZmxpcC5jb20vMTN3YjJ0LmpwZyINCmBgYA0KDQoqTm90ZSB0aGF0IHRoZSB1cmwgbmVlZHMgdG8gYmUgY29udGFpbmVkIHdpdGhpbiBxdW90YXRpb24gbWFya3MuKg0KDQpNYWtlIHN1cmUgdG8gcnVuIHRoaXMgY29kZSBiZWZvcmUgbW92aW5nIG9uIHRvIHRoZSBuZXh0IHN0ZXAuDQoNCiMjDQoNCk5leHQsIHdlIHVzZSB0aGUgYG1lbWVgIGZ1bmN0aW9uIHRvIGFkZCBzb21lIHdvcmRzIHRvIHRoaXMgaW1hZ2UuIFRyeSBydW5uaW5nIHRoZSBjb2RlIGJlbG93LCBhbmQgc2VlIHdoYXQgaGFwcGVucy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCm1lbWUoaGFncmlkLCAiWWVyIGEgd2l6YXJkIiwgIndpdGggY29kaW5nIiwgZm9udCA9ICJzYW5zIikNCmBgYA0KDQoqTm90ZTogU29tZSB3YXJuaW5ncyBtYXkgYXBwZWFyIGluIHlvdXIgUiBDb25zb2xlIGFzIHRoaXMgY29kZSBpcyBleGVjdXRpbmcuIERvbid0IHdvcnJ5IGFib3V0IGl0LCBpdCBpcyBzYWZlIHRvIGlnbm9yZSB0aGVzZSB3YXJuaW5ncy4qIA0KDQojIw0KDQpJZiB5b3Ugd291bGQgbGlrZSB0byBzYXZlIHRoZSBtZW1lIHlvdSBoYXZlIG1hZGUsIGl0IGlzIGhlbHBmdWwgdG8gYXNzaWduIHRoZSBvdXRwdXQgb2YgdGhlIGBtZW1lYCBmdW5jdGlvbiB0byBhbiBvYmplY3QuDQpJbiB0aGUgY29kZSBiZWxvdywgd2UgbWFrZSBhIG5ldyBtZW1lLCBhbmQgYXNzaWduIGl0IHRvIHRoZSBvYmplY3QgYHN1Y2Nlc3NgLiBUcnkgcnVubmluZyB0aGlzIGNvZGUgbm93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0Kc3VjY2Vzc19raWQgPC0gImh0dHA6Ly9pMC5reW0tY2RuLmNvbS9lbnRyaWVzL2ljb25zL21vYmlsZS8wMDAvMDAwLzc0NS9zdWNjZXNzLmpwZyINCnN1Y2Nlc3MgPC0gbWVtZShzdWNjZXNzX2tpZCwgIlVzaW5nIFIiLCAidG8gbWFrZSBtZW1lcyIsIGZvbnQgPSAic2FucyIpDQpzdWNjZXNzDQpgYGANCg0KKkhpbnQ6IE5vdGljZSB0aGF0IHdlIG5lZWQgdG8gaW5jbHVkZSB0aGUgZmluYWwgbGluZSBvZiBjb2RlLCBjYWxsaW5nIHRoZSBvYmplY3QgYHN1Y2Nlc3NgLCBpbiBvcmRlciBmb3IgdGhlIGltYWdlIHRvIGJlIHNob3duLioNCg0KIyMgDQoNCk5vdyB3ZSBjYW4gc2F2ZSBvdXIgbWVtZSwgdXNpbmcgdGhlIGZ1bmN0aW9uIGBtZW1lX3NhdmVgLiBUYWtlIGEgbG9vayBhdCB0aGUgY29kZSBiZWxvdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCm1lbWVfc2F2ZShzdWNjZXNzLCBmaWxlPSJjOi9TVE0xMDAxL0RhdGEgU2NpZW5jZS9zdWNjZXNzX2tpZF9SX21lbWUucG5nIikgDQpgYGANCg0KSGVyZSwgd2UgYXJlIHNhdmluZyBvdXIgYHN1Y2Nlc3NgIG1lbWUsIHRvIHRoZSBmaWxlIGxvY2F0aW9uIGBjOlxTVE0xMDAxXERhdGEgU2NpZW5jZVxgLCB3aXRoIHRoZSBuYW1lIGBzdWNjZXNzX2tpZF9SX21lbWUucG5nYC4gDQoNCk5vdGUgdGhhdCBhbHRob3VnaCB0aGUgZmlsZSBwYXRoIG9uIG91ciBjb21wdXRlciBpbmNsdWRlcyBiYWNrc2xhc2hlcyAoYFxgKSwgaW4gUiBjb2RlIHRoZXNlIG5lZWQgdG8gYmUgY2hhbmdlZCB0byBmb3J3YXJkIHNsYXNoZXMgKGAvYCkuDQoNCiMjDQoNCk5vdyBpdCdzIHRpbWUgdG8gdHJ5IG1ha2luZyB5b3VyIG93biBtZW1lLiANCg0KYS4gRmluZCBhbiBhcHByb3ByaWF0ZSBpbWFnZSBvZiB5b3VyIGNob2ljZSBvbmxpbmUgKHBsZWFzZSBlbnN1cmUgeW91IHBpY2sgIGNvbnRlbnQgc3VpdGFibGUgZm9yIHVuaXZlcnNpdHkgYW5kIHdvcmspLiANCmIuIENvcHkgdGhlIHVybC4gDQoNCmMuIEFzc2lnbiB0aGlzIHVybCB0byBhbiBvYmplY3QgaW4gUi4NCmQuIFVzZSB0aGUgYG1lbWVgIGZ1bmN0aW9uIHRvIGFkZCB3b3JkcyB0byB5b3VyIGltYWdlLg0KZS4gU2F2ZSB5b3VyIG1lbWUgdXNpbmcgdGhlIGBtZW1lX3NhdmVgIGZ1bmN0aW9uLg0KDQoqSGludDogSWYgeW91IGFyZSBub3QgcXVpdGUgc3VyZSBob3cgdG8gYmVnaW4sIGNsaWNrIHRoZSBgQ29kZWAgYnV0dG9uIHRvIHRoZSByaWdodCBiZWxvdy4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQojIEZpcnN0LCB3ZSBuZWVkIHRvIGZpbmQgYW4gaW1hZ2UsIGFuZCBhc3NpZ24gaXQgdG8gYW4gb2JqZWN0IA0KIyAoaGVyZSB3ZSB1c2UgdGhlIGdlbmVyaWMgb2JqZWN0IG5hbWUgJ2ltYWdlX25hbWUnKQ0KIyBKdXN0IHJlcGxhY2UgdGhlIC4uLnMgd2l0aCB0aGUgdXJsIG9mIHlvdXIgaW1hZ2UNCmltYWdlX25hbWUgPC0gIi4uLiINCiMgTmV4dCwgd2UgbmVlZCB0byB1c2UgdGhlIG1lbWUgZnVuY3Rpb24sIHRvIGFkZCBzb21lIHdvcmRzIChqdXN0IHJlcGxhY2UgdGhlIC4uLnMpDQpteV9tZW1lIDwtIG1lbWUoaW1hZ2VfbmFtZSwgIi4uLiIsICIuLi4iLCBmb250ID0gInNhbnMiKQ0KIyBOb3RlIHRoYXQgeW91IG5lZWQgdG8gaW5jbHVkZSB0aGUgYCwgZm9udCA9ICJzYW5zImAgcGFydCB0byBlbnN1cmUgUiBrbm93IHdoaWNoIGZvbnQgdG8gdXNlLg0KIyBOb3cgYWxsIHRoYXQncyBsZWZ0IGlzIHRvIHNhdmUgeW91ciBtZW1lIC0ganVzdCByZWZlciB0byB0aGUgY29kZSBhYm92ZS4NCmBgYA0KDQpDb25ncmF0dWxhdGlvbnMhIFlvdSB3ZXJlIHByb2JhYmx5IG5vdCBleHBlY3RpbmcgdG8gbWFrZSBhIG1lbWUgaW4geW91ciBmaXJzdCBkYXRhIHNjaWVuY2UgY29tcHV0ZXIgbGFiLCBhbmQgdGhpcyBwcm9iYWJseSB3b24ndCBiZSBvbiB0aGUgZmluYWwgZXhhbSwgYnV0IGhvcGVmdWxseSB5b3UgYXJlIHN0YXJ0aW5nIHRvIHJlYWxpc2UgdGhhdCBSIGlzIHZlcnkgdmVyc2F0aWxlLg0KDQojIEN1c3RvbWl6aW5nIEdJRnMgaW4gUg0KDQpSIGlzIG5vdCBsaW1pdGVkIHRvIHdvcmtpbmcgd2l0aCBzdGF0aWMgaW1hZ2VzIC0gd2UgY2FuIG1vZGlmeSBhbmQgY3JlYXRlIGdpZnMgYW5kIGFuaW1hdGlvbnMuDQpJbiB0aGlzIHNlY3Rpb24sIHdlIHdpbGwgdXNlIGFub3RoZXIgZnVuIHBhY2thZ2UsIHRoZSBgbWFnaWNrYCBwYWNrYWdlIFtAbWFnaWNrXSwgdG8gY3VzdG9taXplIGEgZ2lmLg0KDQpSdW4gdGhlIGZvbGxvd2luZyBjb2RlIHRvIGRvd25sb2FkLCBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgbWFnaWNrYCBwYWNrYWdlIGluIHlvdXIgY3VycmVudCBSIHNlc3Npb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIGluY2x1ZGUgPSBGfQ0KaW5zdGFsbC5wYWNrYWdlcygibWFnaWNrIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgaW5jbHVkZSA9IEZ9DQpsaWJyYXJ5KG1hZ2ljaykNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbGlicmFyeShtYWdpY2spDQpgYGANCg0KIyMNCg0KSnVzdCBhcyB3ZSBvYnRhaW5lZCBvbmxpbmUgaW1hZ2VzIG9mIGBoYWdyaWRgIGFuZCBgc3VjY2VzcyBraWRgLCBzbyB0b28gY2FuIHdlIHVzZSB1cmxzIHRvIGdpZnMgYW5kIGFuaW1hdGlvbnMuDQpGb3IgdGhpcyBleGFtcGxlLCB3ZSBoYXZlIHVzZWQgdGhlIHVybCB0byBhIHJvdGF0aW5nIGVhcnRoIGdpZi4NCg0KV2UgdXNlIHRoZSBgaW1hZ2VfcmVhZGAgZnVuY3Rpb24gdG8gcmVhZCB0aGlzIGdpZiBpbnRvIFIsIGFuZCBhc3NpZ24gaXQgdG8gdGhlIG9iamVjdCBgRWFydGhgLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCAsIGZpZy5hbGlnbiA9ICJjZW50ZXIifQ0KRWFydGggPC0gaW1hZ2VfcmVhZCgiaHR0cHM6Ly9pLmdpcGh5LmNvbS9tZWRpYS9tZjhVYklEZXc3ZThnL2dpcGh5LmdpZiIpDQpFYXJ0aA0KYGBgDQoNCk1ha2Ugc3VyZSB0byBydW4gdGhpcyBjb2RlIGJlZm9yZSBtb3Zpbmcgb24gdG8gdGhlIG5leHQgc3RlcCAoZG9uJ3Qgd29ycnkgaWYgaXQgdGFrZXMgYSBmZXcgc2Vjb25kcykuIFRoZSBnaWYgc2hvdWxkIGFwcGVhciBpbiB0aGUgYFZpZXdlcmAgc2VjdGlvbiBvZiBSU3R1ZGlvLg0KDQojIw0KDQpVc2luZyB0aGUgYG1hZ2lja2AgcGFja2FnZSwgd2UgY2FuIGVhc2lseSBtYWtlIHNvbWUgY2hhbmdlcyB0byB0aGlzIGdpZi4NCg0KVGFrZSBhIGxvb2sgYXQgdGhlIGNvZGUgYmVsb3cuIFlvdSB3aWxsIG5vdGljZSBoZXJlIHRoYXQ6IA0KDQoqIFdlIGhhdmUgIHJldmVyc2VkIHRoZSBnaWYsIHVzaW5nIHRoZSBgcmV2YCBmdW5jdGlvbg0KKiBXZSBoYXZlICBmbGlwcGVkIHRoZSBnaWYsIHVzaW5nIHRoZSBgaW1hZ2VfZmxpcGAgZnVuY3Rpb24sIGFuZA0KKiBXZSBoYXZlIGFkZGVkIHRleHQgdG8gdGhpcyBnaWYgdXNpbmcgdGhlIGBpbWFnZV9hbm5vdGF0ZWAgZnVuY3Rpb24NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnJldihFYXJ0aCkgJT4lIA0KICAgICAgICAgICBpbWFnZV9mbGlwKCkgJT4lIA0KICAgICAgICAgICBpbWFnZV9hbm5vdGF0ZSgiICAgICAgICBNZWFud2hpbGUsIGluIEF1c3RyYWxpYSIsIHNpemUgPSA0MCwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNClRyeSBydW5uaW5nIHRoaXMgY29kZSBub3cuDQoNCiMjDQoNClRoaXMgaXMgcmVhbGx5IGp1c3Qgc2NyYXRjaGluZyB0aGUgc3VyZmFjZSBvZiB0aGUgYG1hZ2lja2AgcGFja2FnZS4gSG93ZXZlciwgb3VyIGludGVudGlvbiBmb3IgdGhpcyBmaXJzdCBjb21wdXRlciBsYWIgaXMgdG8gZ2l2ZSB5b3UgYSB0YXN0ZSBvZiBzb21lIG9mIHRoZSBkaWZmZXJlbnQgcG9zc2liaWxpdGllcyBhdmFpbGFibGUgaW4gUiwgc28gZm9yIHRoZSBtb21lbnQsIGxldCdzIG1vdmUgb24uDQoNCiMgRHJhd2luZyBhIGZpc2ggaW4gUg0KDQpJbnN0ZWFkIG9mIHVzaW5nIGEgcHJlLWV4aXN0aW5nIGltYWdlIG9yIGdpZiwgbGV0J3Mgbm93IHRyeSB0byBjcmVhdGUgb25lIGZyb20gc2NyYXRjaC4gU3BlY2lmaWNhbGx5LCBsZXQncyBkcmF3IGEgZmlzaC4NClRvIGRvIHRoaXMsIHdlIGNhbiB1c2UgdGhlIGFwcHJvcHJpYXRlbHkgbmFtZWQgYHJmaXNoZHJhd2AgcGFja2FnZSBbQHJmaXNoZHJhd10uDQoNCiMjDQoNCkxldCdzIGRvd25sb2FkIGFuZCBpbnN0YWxsIHRoZSBgcmZpc2hkcmF3YCBwYWNrYWdlIG5vdy4gSW4gb3JkZXIgdG8gdXNlIHRoaXMgcGFja2FnZSwgd2Ugd2lsbCBhbHNvIG5lZWQgdG8gZG93bmxvYWQgYW5kIGluc3RhbGwgc29tZSBhZGRpdGlvbmFsIHBhY2thZ2VzLCB1cG9uIHdoaWNoIHRoZSBgcmZpc2hkcmF3YCBwYWNrYWdlIGRlcGVuZHMuIFN1Y2ggcGFja2FnZXMgYXJlIGtub3duIGFzICoqZGVwZW5kZW5jaWVzKiosIGFuZCBpdCBpcyBjb21tb24gZm9yIG1vcmUgc29waGlzdGljYXRlZCBSIHBhY2thZ2VzIHRvIGhhdmUgbXVsdGlwbGUgZGVwZW5kZW5jaWVzLg0KDQoqTm90ZSB0aGF0IHRoZXNlIGRlcGVuZGVuY2llcyBhcmUgcGFja2FnZXMgaW4gdGhlaXIgb3duIHJpZ2h0LioNCg0KUnVuIHRoaXMgY29kZSBpbiBSIG5vdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgaW5jbHVkZSA9IEZ9DQppbnN0YWxsLnBhY2thZ2VzKCJyZmlzaGRyYXciLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCmluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJyZmlzaGRyYXciKQ0KaW5zdGFsbC5wYWNrYWdlcygicGF0Y2h3b3JrIikNCmluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KbGlicmFyeSgicmZpc2hkcmF3IikNCmxpYnJhcnkoInBhdGNod29yayIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbGlicmFyeSgicmZpc2hkcmF3IikNCmxpYnJhcnkoInBhdGNod29yayIpDQpsaWJyYXJ5KCJnZ3Bsb3QyIikNCmBgYA0KDQojIw0KDQpJZiB5b3Ugbm93IHJ1biB0aGUgY29kZSBiZWxvdywgYSBkZXRhaWxlZCBkcmF3aW5nIG9mIGEgZmlzaCBzaG91bGQgYXBwZWFyIGluIGEgbmV3IHdpbmRvdyENCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmdldF9wb2x5bGluZXMocGF0aCA9ICJpbnN0L2Zpc2hkcmF3LmpzIiwNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIGZvcm1hdCA9ICJzbWlsIiwNCiAgICAgICAgICAgICAgDQogICAgICAgICAgICAgIG91dHB1dCA9ICJhbmltYXRlZC5zdmciLA0KICAgICAgICAgICAgICANCiAgICAgICAgICAgICAgZHJhd190eXBlID0gInJhbmRvbSIpDQoNCndpbmRvd3MoKSAjIElmIHlvdSBhcmUgdXNpbmcgYSBNYWMsIHJlcGxhY2Ugd2luZG93cygpIHdpdGg6IHF1YXJ0eigpDQoNCmZpc2hfZHJhdygpDQpgYGANCg0KIyMNCg0KU3VwcG9zZSB3ZSB3b3VsZCBsaWtlIHRvIGNoYW5nZSB0aGUgY29sb3VyIG9mIG91ciBmaXNoLiBXZSBjYW4gZG8gdGhpcywgYnkgaW5jbHVkaW5nIHRoZSBhcmd1bWVudCBgY29sID0gIi4uLiJgIHdpdGhpbiB0aGUgZnVuY3Rpb24gYGZpc2hfZHJhd2AuIEZvciBleGFtcGxlLCBpZiB3ZSB3b3VsZCBsaWtlIG91ciBmaXNoIHRvIGJlIGJsdWUsIHdlIGNhbiB3cml0ZQ0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KZmlzaF9kcmF3KGNvbCA9ICJibHVlIikNCmBgYA0KDQpUcnkgY2hhbmdpbmcgdGhpcyBjb2xvdXIgdG8gYSBkaWZmZXJlbnQgY29sb3VyLCBhbmQgdGhlbiBydW4gdGhlIGNvZGUuDQoNCiMgUGFsbWVyIFBlbmd1aW5zIERhdGEgU2V0IHsjcGVuZ3VpbnN9DQoNCk5vdyB0aGF0IHdlIGhhdmUgaGFkIGEgdGFzdGUgb2Ygc29tZSBvZiB0aGUgbW9yZSBsaWdodC1oZWFydGVkIFIgcGFja2FnZXMgb3V0IHRoZXJlLCBsZXQncyBjb25zaWRlciBhIHBhY2thZ2Ugd2hpY2ggY29udGFpbnMgc29tZSB1c2VmdWwgZGF0YS4NCg0KVGhlIGBwYWxtZXJwZW5ndWluc2AgUiBwYWNrYWdlIFtAcGVuZ3VpbnNdIGNvbnRhaW5zIGRhdGEsIGNvbGxlY3RlZCBvdmVyIHRoZSBjb3Vyc2Ugb2Ygc2V2ZXJhbCB5ZWFycywgb24gMyBzcGVjaWVzIG9mIHBlbmd1aW4gbGl2aW5nIG9uIGRpZmZlcmVudCBpc2xhbmRzIGluIHRoZSBQYWxtZXIgYXJjaGlwZWxhZ28sIG9mZiB0aGUgY29hc3Qgb2YgQW50YXJjdGljYS4gDQpGb3IgbW9yZSBkZXRhaWxzLCB5b3UgY2FuIHJlZmVyIHRvIFtTZWN0aW9uIDIgb2YgdGhlIERhdGEgVmlzdWFsaXNhdGlvbiBpbiBSIHN1cHBsZW1lbnRdKGh0dHBzOi8vYm9va2Rvd24ub3JnL3JlaGsvc3RtMTAwMV9kc21fZGF0YV92aXN1YWxpc2F0aW9uX2luX3IvcGVuZ3VpbnMuaHRtbCkuDQoNCiMjDQoNCkp1c3QgbGlrZSB0aGUgcHJldmlvdXMgcGFja2FnZXMsIHdlIHdpbGwgbmVlZCB0byBkb3dubG9hZCBhbmQgbG9hZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlIGJlZm9yZSB3ZSBjYW4gYmVnaW4gd29ya2luZyB3aXRoIHRoaXMgcGVuZ3VpbiBkYXRhLg0KDQpSdW4gdGhlIGNvZGUgYmVsb3cgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBwYWNrYWdlIGluIFIuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiLCByZXBvcyA9ICJodHRwOi8vY3Jhbi51cy5yLXByb2plY3Qub3JnIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KaW5zdGFsbC5wYWNrYWdlcygicGFsbWVycGVuZ3VpbnMiKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmc9Rn0NCmxpYnJhcnkocGFsbWVycGVuZ3VpbnMpDQpgYGANCg0KIyMNCg0KV2UgY2FuIHVzZSB0aGUgYHN1bW1hcnlgIGZ1bmN0aW9uIHRvIG9idGFpbiBhIHF1aWNrIG92ZXJ2aWV3IG9mIHRoZSBkYXRhIGNvbnRhaW5lZCB3aXRoaW4gdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQojIFRoaXMgY29kZSBzdW1tYXJpc2VzIHRoZSBkYXRhIGluIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UuDQpzdW1tYXJ5KHBlbmd1aW5zKQ0KYGBgDQoNCkRvbid0IHdvcnJ5IHRvbyBtdWNoIGFib3V0IHRoZSB2YWx1ZXMgc2hvd24gaW4gdGhlIHN1bW1hcnkgdGFibGUgLSB0aGUgbWFpbiB0aGluZ3MgdG8gbm90ZSBhdCB0aGlzIHN0YWdlIGFyZSB0aGUgZGlmZmVyZW50IHZhcmlhYmxlcywgbmFtZWx5IGBzcGVjaWVzYCwgYGlzbGFuZGAsIGBiaWxsX2xlbmd0aF9tbWAsIGBiaWxsX2RlcHRoX21tYCwgYGZsaXBwZXJfbGVuZ3RoX21tYCwgYGJvZHlfbWFzc19nYCwgYHNleGAgYW5kIGB5ZWFyYC4NCg0KIyBJbnRlcmFjdGl2ZSBIaXN0b2dyYW1zDQoNClN1cHBvc2UgdGhhdCB3ZSB3b3VsZCBsaWtlIHRvIHByb2R1Y2UgaGlzdG9ncmFtcyBzaG93aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHBlbmd1aW5zJyBgYm9keV9tYXNzX2dgIHZhbHVlcyAodGhlaXIgYm9keSBtYXNzIGluIGdyYW1zKS4gV2UgY291bGQgY3JlYXRlIGEgc2ltcGxlIGhpc3RvZ3JhbSB1c2luZyB0aGUgYmFzZSBSIGBoaXN0YCBmdW5jdGlvbiB2aWEgdGhlIGZvbGxvd2luZyBjb2RlOg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBUfQ0KaGlzdChwZW5ndWlucyRib2R5X21hc3NfZywgYnJlYWtzID0gMTkpDQpgYGANCg0KSG93ZXZlciwgdGhpcyBoaXN0b2dyYW0gaGFzIHNvbWUgc2hvcnRjb21pbmdzLiBGaXJzdGx5LCBpdCBpcyBzdGF0aWMuIFdlIGNhbid0IGludGVyYWN0IHdpdGggdGhlIGltYWdlLCBhbmQgd2UgY2FuJ3QgbWFuaXB1bGF0ZSBpdCBpbiByZWFsIHRpbWUgdG8gZGlzcGxheSBkaWZmZXJlbnQgZGV0YWlscy4gDQoNCkZvciBleGFtcGxlLCBwZXJoYXBzIHdlIHdvdWxkIGxpa2UgdG8gc2VlIHRoZSBkaXN0cmlidXRpb24gb2YgdGhlIHBlbmd1aW5zJyBgYm9keV9tYXNzX2dgIHZhbHVlcywgYnV0IG9ubHkgZm9yIHRoZSBwZW5ndWlucyBvbiBhIHNwZWNpZmljIGlzbGFuZC4gV2Ugd291bGQgbmVlZCB0byBkbyBzb21lIG1vcmUgY29kaW5nIHRvIHByb2R1Y2Ugc3VjaCBhIGhpc3RvZ3JhbSBpbiBiYXNlIFIuIEV2ZW4gdGhlbiwgaWYgd2Ugd291bGQgbGlrZSB0byBoYXZlIHNpbWlsYXIgaGlzdG9ncmFtcyBmb3IgdGhlIG90aGVyIHR3byBpc2xhbmRzLCB0aGlzIHdvdWxkIG1lYW4gZnVydGhlciBjb2RpbmcuDQoNCkFsdGVybmF0aXZlbHksIHdlIGNvdWxkIHVzZSB0aGUgYHBsb3RseWAgcGFja2FnZSB0byBjcmVhdGUgYW4gaW50ZXJhY3RpdmUsIHJlc3BvbnNpdmUgaGlzdG9ncmFtLiBMZXQncyB0YWtlIGEgbG9vayBhdCBob3cgdG8gZG8gdGhpcyBub3cuDQoNCiMjDQoNClRvIGJlZ2luLCBqdXN0IGFzIGZvciB0aGUgcHJldmlvdXMgcGFja2FnZXMsIHdlIHdpbGwgbmVlZCB0byBkb3dubG9hZCBhbmQgbG9hZCB0aGUgYHBsb3RseWAgcGFja2FnZSBpbiBSLCBiZWZvcmUgd2UgY2FuIHVzZSBhbnkgYHBsb3RseWAgZnVuY3Rpb25zLg0KDQpSdW4gdGhlIGNvZGUgYmVsb3cgdG8gaW5zdGFsbCBhbmQgbG9hZCB0aGUgYHBsb3RseWAgcGFja2FnZSBpbiBSLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBGLCBpbmNsdWRlID0gRn0NCmluc3RhbGwucGFja2FnZXMoInBsb3RseSIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIG1lc3NhZ2UgPSBGLCB3YXJuaW5nID0gRn0NCmxpYnJhcnkocGxvdGx5KQ0KYGBgDQoNCiMjDQoNClRvIGNyZWF0ZSBgcGxvdGx5YCBwbG90cywgd2UgIHVzZSB0aGUgZnVuY3Rpb24gYHBsb3RfbHkoKWAuIFdlIHdvbid0IHdvcnJ5IHRvbyBtdWNoIGFib3V0IHRoZSBjb21wb3NpdGlvbiBvZiB0aGlzIGZ1bmN0aW9uIGp1c3QgeWV0IC0gd2UnbGwgY292ZXIgdGhpcyBpbiBtb3JlIGRldGFpbCBuZXh0IHdlZWsuIEZvciB0aGUgbW9tZW50LCB0YWtlIGEgbG9vayBhdCB0aGUgY29kZSBiZWxvdywgYW5kIHNlZSBpZiB5b3UgY2FuIGdldCBhIGdlbmVyYWwgaWRlYSBvZiB3aGF0J3MgZ29pbmcgb24uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRn0NCnBlbmd1aW5faGlzdF9iYXNlIDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIikNCg0KcGVuZ3Vpbl9oaXN0X2Jhc2UgPC0gcGVuZ3Vpbl9oaXN0X2Jhc2UgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSAnY291bnQnKSkNCmBgYA0KDQpCZWZvcmUgeW91IG1vdmUgb24gdG8gdGhlIG5leHQgcXVlc3Rpb24sIHJ1biB0aGlzIGNvZGUgaW4gUi4NCg0KKk5vdGU6IE9uY2UgeW91IGhhdmUgdGFrZW4gc29tZSB0aW1lIHRvIGNvbnNpZGVyIHRoZSBjb2RlIGFib3ZlLCBpZiB5b3Ugd291bGQgbGlrZSBtb3JlIGRldGFpbHMgb3Igd291bGQgbGlrZSB0byBjaGVjayB0aGUgYWNjdXJhY3kgb2YgeW91ciBpbnRlcnByZXRhdGlvbiwgY2xpY2sgdGhlIGBDb2RlYCBidXR0b24gYmVsb3cgZm9yIGEgYnJpZWYgZXhwbGFuYXRpb24uKg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1oaWRlIiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KIyBIZXJlLCB3ZSBhcmUgY3JlYXRpbmcgYSBwbG90bHkgb2JqZWN0IGNhbGxlZCAicGVuZ3Vpbl9oaXN0X2Jhc2UiDQpwZW5ndWluX2hpc3RfYmFzZSA8LSBwbG90X2x5KGRhdGEgPSBwZW5ndWlucywgIyBXZSBhcmUgdXNpbmcgdGhlIHBlbmd1aW5zIGRhdGENCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgeCA9IH5ib2R5X21hc3NfZywgIyBhbmQgbW9kZWxsaW5nIHRoZSBib2R5X21hc3NfZyBkYXRhDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIikgIyBpbiBhIGhpc3RvZ3JhbSBmb3JtYXQNCg0KIyBUaGUgY29kZSBiZWxvdyBpcyB1c2VkIHRvIG1vZGlmeSB0aGUgbGF5b3V0IG9mIHRoZSBoaXN0b2dyYW0NCiMgdG8gaW5jbHVkZSBhIGxhYmVsIGZvciB0aGUgeS1heGlzDQpwZW5ndWluX2hpc3RfYmFzZSA8LSBwZW5ndWluX2hpc3RfYmFzZSAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9ICdjb3VudCcpKQ0KYGBgDQoNCiMjIHsjYmFzZWhpc3R9DQoNClRvIHByb2R1Y2UgdGhpcyBgcGxvdGx5YCBoaXN0b2dyYW0sIHJ1biB0aGUgUiBjb2RlIGJlbG93LiBZb3VyIGhpc3RvZ3JhbSBzaG91bGQgYXBwZWFyIGluIHRoZSBgVmlld2VyYCBzZWN0aW9uIG9mIFJTdHVkaW8uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluX2hpc3RfYmFzZQ0KYGBgDQoNCiMjDQoNCkFzIHdlIG5vdGVkIGVhcmxpZXIsIGBwbG90bHlgIGdyYXBocywgdW5saWtlIGJhc2UgUiBncmFwaHMsIGFyZSBpbnRlcmFjdGl2ZSENCg0KTm90aWNlIHRoYXQgaWYgeW91IGhvdmVyIG92ZXIgdGhlIGRhdGEgaW4gdGhlIGhpc3RvZ3JhbSBpbiBcQHJlZihiYXNlaGlzdCksIHlvdSBjYW4gc2VlIHRoZSBzcGVjaWZpYyBkZXRhaWxzIChub3RlIHRoYXQgdGhlIGdyYXBoIGluIHRoaXMgZG9jdW1lbnQgaXMgYWxzbyBpbnRlcmFjdGl2ZSEpLiANCklmIHlvdSBsZWZ0LWNsaWNrIGFuZCBkcmFnIHlvdXIgY3Vyc29yIG92ZXIgYSBzZWN0aW9uIHRvIGNyZWF0ZSBhIGJveCwgeW91IGNhbiBhbHNvIHpvb20gaW4gb24gYSBwYXJ0aWN1bGFyIHNlY3Rpb24gb2YgdGhlIHBsb3QuIEp1c3QgZG91YmxlIGxlZnQtY2xpY2sgdG8gem9vbSBiYWNrIG91dC4NCg0KIyMNCg0KUGVyaGFwcyB5b3UgYXJlIG5vdCBpbXByZXNzZWQgd2l0aCBgcGxvdGx5YCB5ZXQuIEFmdGVyIGFsbCwgb3VyIGhpc3RvZ3JhbSBkb2Vzbid0IGxvb2sgdGhhdCBkaWZmZXJlbnQgdG8gdGhlIGJhc2UgUiB2ZXJzaW9uLCBzbyB3aGF0IGlzIGFsbCB0aGUgZnVzcyBhYm91dD8NCg0KV2VsbCwgaXQgaXMgdmVyeSBlYXN5IHRvIG1vZGlmeSBvdXIgYHBlbmd1aW5faGlzdF9iYXNlYCBgcGxvdF9seWAgZ3JhcGggdG8gc2hvdyBleHRyYSBkZXRhaWwuIEZvciBleGFtcGxlLCB3ZSBjYW4gZWFzaWx5IHByb2R1Y2Ugc2VwYXJhdGUgaGlzdG9ncmFtcyBmb3IgdGhlIHBlbmd1aW5zIG9uIGVhY2ggaXNsYW5kLiBUYWtlIGEgbG9vayBhdCB0aGUgUiBjb2RlIGJlbG93LCB3aGljaCBidWlsZHMgdXBvbiB3aGF0IHdlIHVzZWQgaW4gYHBlbmd1aW5faGlzdF9iYXNlYC4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gVCwgd2FybmluZyA9IEYsIG1lc3NhZ2UgPSBGfQ0KcGVuZ3Vpbl9oaXN0IDwtIHBsb3RfbHkoZGF0YSA9IHBlbmd1aW5zLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHggPSB+Ym9keV9tYXNzX2csIA0KICAgICAgICAgICAgICAgICAgICAgICAgY29sb3IgPSB+aXNsYW5kLCANCiAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAiaGlzdG9ncmFtIiwgYWxwaGEgPSAwLjYpDQoNCnBlbmd1aW5faGlzdCA8LSBwZW5ndWluX2hpc3QgJT4lIGxheW91dCh5YXhpcyA9IGxpc3QodGl0bGUgPSAnY291bnQnKSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYmFybW9kZSA9Im92ZXJsYXkiKQ0KYGBgDQoNCkJlZm9yZSB5b3UgbW92ZSBvbiB0byB0aGUgbmV4dCBxdWVzdGlvbiwgcnVuIHRoaXMgY29kZSBpbiBSLg0KDQoqTm90ZTogT25jZSB5b3UgaGF2ZSB0YWtlbiBzb21lIHRpbWUgdG8gY29uc2lkZXIgdGhlIGNvZGUgYWJvdmUsIGlmIHlvdSB3b3VsZCBsaWtlIG1vcmUgZGV0YWlscyBvciB3b3VsZCBsaWtlIHRvIGNoZWNrIHRoZSBhY2N1cmFjeSBvZiB5b3VyIGludGVycHJldGF0aW9uLCBjbGljayB0aGUgYENvZGVgIGJ1dHRvbiBiZWxvdyBmb3IgYSBicmllZiBleHBsYW5hdGlvbi4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQojIEhlcmUsIHdlIGFyZSBjcmVhdGluZyBhIHBsb3RseSBvYmplY3QgY2FsbGVkICJwZW5ndWluX2hpc3QiDQpwZW5ndWluX2hpc3QgPC0gcGxvdF9seShkYXRhID0gcGVuZ3VpbnMsICMgV2UgYXJlIHVzaW5nIHRoZSBwZW5ndWlucyBkYXRhDQogICAgICAgICAgICAgICAgICAgICAgICB4ID0gfmJvZHlfbWFzc19nLCAjIGFuZCBtb2RlbGxpbmcgdGhlIGJvZHlfbWFzc19nIGRhdGENCiAgICAgICAgICAgICAgICAgICAgICAgIGNvbG9yID0gfmlzbGFuZCwgdHlwZSA9ICJoaXN0b2dyYW0iLCBhbHBoYSA9IDAuNikNCiMgV2UgYXJlIHByb2R1Y2luZyBhIGhpc3RvZ3JhbSBmb3IgdGhpcyBkYXRhLCB3aXRoIHBvaW50cyBjb2xvdXJlZCBkaWZmZXJlbnRseSwgDQojIGRlcGVuZGluZyBvbiB0aGUgaXNsYW5kIG9uIHdoaWNoIHRoZSBwZW5ndWluIGlzIGxvY2F0ZWQNCg0KIyBUaGUgY29kZSBiZWxvdyBpcyB1c2VkIHRvIG1vZGlmeSB0aGUgbGF5b3V0IG9mIHRoZSBoaXN0b2dyYW0NCiMgVGhpcyBpbmNsdWRlcyBhZGRpbmcgYSBsYWJlbCB0byB0aGUgeS1heGlzDQojIGFuZCBzZXR0aW5nIHRoZSBoaXN0b2dyYW1zIHRvIGJlIGxheWVyZWQgb3ZlciBlYWNoIG90aGVyDQojIChoZW5jZSB0aGUgYWxwaGEgPSAwLjYgYWJvdmUgdG8gY2hhbmdlIHRoZSBvcGFjaXR5KQ0KcGVuZ3Vpbl9oaXN0IDwtIHBlbmd1aW5faGlzdCAlPiUgbGF5b3V0KHlheGlzID0gbGlzdCh0aXRsZSA9ICdjb3VudCcpLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBiYXJtb2RlID0ib3ZlcmxheSIpDQpgYGANCg0KIyMgeyNpc2xhbmRzaGlzdH0NCg0KVG8gcHJvZHVjZSB0aGlzIG5ldyBgcGxvdGx5YCBoaXN0b2dyYW0sIHJ1biB0aGUgUiBjb2RlIGJlbG93LiBZb3VyIGhpc3RvZ3JhbSBzaG91bGQgYXBwZWFyIGluIHRoZSBgVmlld2VyYCBzZWN0aW9uIG9mIFJTdHVkaW8uDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFQsIHdhcm5pbmcgPSBGLCBtZXNzYWdlID0gRiwgZmlnLmFsaWduID0gImNlbnRlciJ9DQpwZW5ndWluX2hpc3QNCmBgYA0KDQpUaGlzIGlzIGxvb2tpbmcgYmV0dGVyIHRoYW4gb3VyIHByZXZpb3VzIGhpc3RvZ3JhbSEgQmVjYXVzZSB3ZSBoYXZlIHRvbGQgb3VyIGBwbG90X2x5YCBmdW5jdGlvbiB0byBhc3NpZ24gZGlmZmVyZW50IGNvbG91cnMgdG8gdGhlIGRpZmZlcmVudCBpc2xhbmRzLCB3ZSBub3cgaGF2ZSB0aHJlZSBoaXN0b2dyYW1zLCByYXRoZXIgdGhhbiBvbmUgd2l0aCBhbGwgdGhlIGRhdGEgY2x1bXBlZCB0b2dldGhlci4NCg0KRXZlbiBiZXR0ZXIsIHRoZXNlIGFyZSBhbGwgcHJlc2VudGVkIHdpdGhpbiB0aGUgb25lIHBsb3QsIHdoaWNoIGFsc28gaW5jbHVkZXMgYSBoYW5keSBsZWdlbmQuIEhvcGVmdWxseSB5b3UgYXJlIG5vdyBiZWdpbm5pbmcgdG8gYXBwcmVjaWF0ZSB0aGUgaW5jcmVhc2VkIGZ1bmN0aW9uYWxpdHkgb2ZmZXJlZCBieSBgcGxvdGx5YCBvdmVyIGJhc2UgUiBwbG90cy4NCg0KIyMNCg0KRmluYWxseSwgYW5kIHBlcmhhcHMgbW9zdCBpbXBvcnRhbnRseSBmb3IgdGhpcyBzcGVjaWZpYyBleGFtcGxlLCBpdCBpcyBpbXBvcnRhbnQgdG8gbm90ZSB0aGF0IHdlIGNhbiBkeW5hbWljYWxseSBmaWx0ZXIgb3V0IG9ic2VydmF0aW9ucywgdG8gZm9jdXMgb24gZGF0YSBmcm9tIGEgc3BlY2lmaWMgaXNsYW5kLiBTaW1wbHkgY2xpY2sgb24gb25lIG9mIHRoZSBsaW5lcyBpbiB0aGUgbGVnZW5kIGluIHRoZSB0b3AgcmlnaHQgb2Ygb3VyIGhpc3RvZ3JhbSBpbiBcQHJlZihpc2xhbmRzaGlzdCksIHRvIHJlbW92ZSB0aGF0IGRhdGEgZnJvbSBhc3Nlc3NtZW50IChub3RlIHRoYXQgdGhlIGF4ZXMgZHluYW1pY2FsbHkgYWRqdXN0IHRvbykuIA0KDQpUcnkgZm9jdXNpbmcganVzdCBvbiB0aGUgRHJlYW0gaXNsYW5kIHBlbmd1aW5zLg0KDQoqSGludDogVG8gYnJpbmcgdGhlIHJlbW92ZWQgZGF0YSBiYWNrLCBzaW1wbHkgY2xpY2sgb25jZSBtb3JlIG9uIHRoZSByZWxldmFudCBsaW5lIGluIHRoZSBsZWdlbmQuKg0KDQo8YnI+DQoNCiMjIyMgVGhhdCdzIHRoZSBlbmQgb2YgdGhlIGZpcnN0IGRhdGEgc2NpZW5jZSBjb21wdXRlciBsYWIhICMjIyMgey19DQoNCkhvcGVmdWxseSB5b3UgaGF2ZSBlbmpveWVkIHRoaXMgZmlyc3QgY29tcHV0ZXIgbGFiLCBhbmQgbm93IGhhdmUgYSBiZXR0ZXIgaWRlYSBvZiBqdXN0IGhvdyB2ZXJzYXRpbGUgUiBjYW4gYmUuIERvbid0IHdvcnJ5IGlmIHNvbWUgb2YgdGhlIGNvZGUgc2VlbXMgZGlmZmljdWx0IGF0IHRoZSBtb21lbnQgLSB0aGlzIGlzIG9ubHkgdGhlIGZpcnN0IHdlZWsgYWZ0ZXIgYWxsISBOZXh0IHdlZWssIHdlIHdpbGwgY29udGludWUgd29ya2luZyB3aXRoIGBwbG90bHlgIGFuZCB0aGUgYHBhbG1lcnBlbmd1aW5zYCBkYXRhIHNldCwgdG8gcHJvZHVjZSBldmVuIG1vcmUgZGV0YWlsZWQgaW50ZXJhY3RpdmUgcGxvdHMuDQoNCkJlZm9yZSB5b3UgZmluaXNoIHVwLCBpZiB5b3UgaGF2ZSBiZWVuIHdyaXRpbmcgYW5kIHJ1bm5pbmcgeW91ciBjb2RlIGluIFJTdHVkaW8sIG1ha2Ugc3VyZSB0byBzYXZlIHlvdXIgc2NyaXB0IGZpbGUgc29tZXdoZXJlIHNhZmUgLSBpdCBtaWdodCBjb21lIGluIGhhbmR5IGxhdGVyIG9uLiANCg0KPGJyPg0KDQojIFJlZmVyZW5jZXMgey0gI1JlZn0NCjxkaXYgaWQ9InJlZnMiPjwvZGl2Pg0KDQo8YnI+DQoNCjxmb250IGNvbG9yID0gImdyZXkiPg0KVGhlc2Ugbm90ZXMgaGF2ZSBiZWVuIHByZXBhcmVkIGJ5IFJ1cGVydCBLdXZla2UuIFRoZSBjb3B5cmlnaHQgZm9yIHRoZSBtYXRlcmlhbCBpbiB0aGVzZSBub3RlcyByZXNpZGVzIHdpdGggdGhlIGF1dGhvciBuYW1lZCBhYm92ZSwgd2l0aCB0aGUgRGVwYXJ0bWVudCBvZiBNYXRoZW1hdGljcyBhbmQgU3RhdGlzdGljcyBhbmQgd2l0aCBMYSBUcm9iZSBVbml2ZXJzaXR5LiBDb3B5cmlnaHQgaW4gdGhpcyB3b3JrIGlzIHZlc3RlZCBpbiBMYSBUcm9iZSBVbml2ZXJzaXR5IGluY2x1ZGluZyBhbGwgTGEgVHJvYmUgVW5pdmVyc2l0eSBicmFuZGluZyBhbmQgbmFtaW5nLiBVbmxlc3Mgb3RoZXJ3aXNlIHN0YXRlZCwgbWF0ZXJpYWwgd2l0aGluIHRoaXMgd29yayBpcyBsaWNlbnNlZCB1bmRlciBhIENyZWF0aXZlIENvbW1vbnMgQXR0cmlidXRpb24tTm9uIENvbW1lcmNpYWwtTm9uIERlcml2YXRpdmVzIExpY2Vuc2UgDQo8YSBocmVmID0gImh0dHBzOi8vY3JlYXRpdmVjb21tb25zLm9yZy9saWNlbnNlcy9ieS1uYy1uZC80LjAvQ0MiIHRhcmdldD0iX2JsYW5rIj4gQlktTkMtTkQuIDwvYT4NCjwvZm9udD4=