Data Science Stream

Topic 1B: Using RStudio


Welcome to the first Data Science computer lab for STM1001!

Throughout the semester, we will use the R software environment in our computer labs and assessments. R is free, flexible, and used by millions of people for statistical computing and data visualisation.

Learning R can be challenging at first. To make our learning experience more enjoyable, we will be using RStudio rather than base R for all our R coding. RStudio is an integrated development environment (IDE) for R, and offers several helpful features and user-interface options missing from base R.

In this first Data Science computer lab we will take things slowly, and focus on practicing and reinforcing key R coding skills you began developing in the first core Computer Lab, via some light-hearted examples. A solid R foundation will ensure that in subsequent computer labs, you will be able to pick up and apply new R coding skills more easily.

By the end of this lab, you should feel comfortable using simple R commands, creating and naming new objects, installing, loading and using R packages, and saving images generated in RStudio.


Checklist

Before we continue, make sure that you have done the following:

  • Installed R and RStudio on your personal device (this will be helpful for assignment work, even if you intend to complete the computer labs on university computers)
  • Completed the first core Computer Lab
  • Looked over the different books in the Introduction to R content on LMS
  • Confirmed that you are in the correct stream

If you have any questions about any of these items, please ask your computer lab demonstrator for assistance.

1 Installing new R packages

R contains many in-built functions, and by itself 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), and made available for anyone around the world to download and use in their version of R.

These packages are often 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 missing from base R.

On the other hand, sometimes user-created packages are more light-hearted, or are pet projects not necessarily intended for serious data analysis. One such example is the meme package (Yu 2021), which allows users to create simple memes using R code.

Let’s install and load the meme package in RStudio now.

1.1

Because the meme package is not installed in base R, we need to download it before we can use it. Recall that we can use the install.packages() R function to do this. Run the code below to do this now:

install.packages("meme")

1.2

When you install an R package, you will see some text appear in the RStudio Console window. Often, some of this text is red, which at first can be unnerving - you may think that an error has occurred. Don’t worry! While it is always a good idea to check these red text messages, they are not necessarily errors, and often can be safely ignored.

If you check the final part of the text output, which is in black not red, you should see something like package ‘meme’ successfully unpacked and MD5 sums checked. This is reassuring - the package has installed correctly, and the red text above is just telling us how R went about installing it.

Note: If you see a message about installing Rtools, ignore it, we don’t need it for the purposes of this lab.

1.3

Once the meme package is downloaded and installed, we need to load it in our current RStudio session. Run the following code to load the meme package.

library(meme)

2 Making memes in RStudio

With the meme package installed and loaded in RStudio, we can now start to make some simple memes! We really only need two lines of code for this, as we will demonstrate with the following example.

2.1

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 RStudio, 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.

Note: For a refresher on objects, check the R Coding Fundamentals book in the Introduction to R content on LMS.

2.2

Next, we use the meme function (which is only available because we installed and loaded the meme package) 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 the Console section of RStudio as this code is executing. Don’t worry about them, it is safe to ignore these warnings.

2.3 Saving images in RStudio

If you would like to save the meme you have made, we have a couple of options, which we will demonstrate now, via another example.

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.

2.3.1

If we would like to save our new meme using R code, we can use 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: Although the file path on our computer includes backslashes (\), in R code these need to be changed to forward slashes (/).

2.3.2

Instead of using R code, we can save our image manually, by navigating to the Plots window in RStudio, clicking Export, and selecting either Save as Image... or Save as PDF..., as shown below:

Try this now, and save your success meme as a pdf.

2.4

Now it’s time to make your own meme in RStudio. Follow the steps below:

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

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

  5. Save your meme using either the meme_save function or the manual approach.

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. While this won’t be on the final exam, the R skills you are developing here are important, and hopefully you are starting to realise that R is very versatile.

3 Customizing GIFs in RStudio

R is not limited to working with static images - we can modify and create gifs and animations (and in future weeks we will make animated, interactive graphs using real data). In this section, we will use another fun package, the magick package (Ooms 2021), to customize a gif.

3.1

Run the following code to download, install and load the magick package in your current RStudio session.

install.packages("magick")
library(magick)

3.2

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 gif of a rotating Earth.

We can use the image_read function to read this gif into RStudio. Run the code below to 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.

3.3

Using the magick package, we can easily make some changes to this Earth gif.

Run the following code, and inspect the output.

rev(Earth) %>% 
           image_flip() %>% 
           image_annotate("        Meanwhile, in Australia", size = 40, color = "white")

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

This is really just scratching the surface of the magick package. For the moment though, let’s move on.

4 Drawing a fish in RStudio

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

4.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: These dependencies are packages in their own right.

Run this code in RStudio now.

# Main package
install.packages("rfishdraw")
# Dependencies
install.packages("patchwork")
install.packages("ggplot2")
library("rfishdraw")
library("patchwork")
library("ggplot2")

4.2

If you now run the code below (making the appropriate selection between windows() and quartz()), 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")

# For Windows users, use
windows() 
fish_draw()

# For Mac users, instead use
quartz()
fish_draw()

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

Note: As we will see in future labs, many R functions for producing visualisations incorporate an argument like col = to allow for colour specification.

5 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. Over the course of the next few data science computer labs, we will create various interactive data visualisations using the penguins data from this package.

For more details on the penguins data set, and a taste of what’s ahead in future labs, you can refer to Section 2 of the Data Visualisation in R supplement.

For this lab, let’s use some R functions to inspect the penguins data set.

5.1

Just like the previous packages, to begin we will need to download and load the palmerpenguins package.

Using what you have practiced earlier in this computer lab, install and load the palmerpenguins package in RStudio now.

Note: You may already have the palmerpenguins package installed, if you are using the same device you used for the first core Computer Lab.

5.2

Recall from the first core Computer Lab that you can easily check the dimensions of your data using the dim, nrow and ncol functions. Use these now to assess the penguins data set.

Note: Check the Code button below if you would like a refresher.

# This code checks the dimensions of the penguins data set
dim(penguins)

5.3

Use the summary function to obtain a quick overview of the data contained within the penguins data set.

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

Often, when we begin working with a new data set, it is helpful to take a quick look at some of the recorded values. We can use the head function to look at the recorded values for the first 6 observations in a data set.

Try using the head function now, with the penguins data set. What do you observe?

5.5

When a data set has multiple columns of information, we can assess the information in specific column by writing the name of the object, adding a $ at the end, and then writing the name of the specific column we would like to inspect.

For example, we could use the following code to check the recorded bill length measurements of the penguins:

penguins$bill_length_mm

Run this code now.

Note: When we have a large data set, output may appear over several lines in the Console. The numbers that appear in brackets to the left of each line of output are not observations. Rather, these denote the position number for the first observation on that line of output. E.g. a [17] would denote that the observation directly to the right of the [17] is the 17th recorded observation in the data set, for the variable being considered.

5.6

Try using this $ approach to check the recorded bill depths and body masses of the penguins.

Note: Notice that once you type the $, RStudio will helpfully prompt you with possible selections.


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 (particularly when using the helpful RStudio GUI). Don’t worry if some of the code seems difficult at the moment - this is only the first lab after all!

In the next data science computer lab we will continue working with the palmerpenguins data set, and cover how to create interactive plots using a new package.

Important Notes

  • If you have any questions about the content in this lab, or are stuck on some R code used in the lab, please ask your lab demonstrator for assistance.

  • Make sure to save your R script file somewhere safe - it may be a helpful reference source for later work.

  • Make sure that you finish off your readings of the Introduction to R content on LMS prior to the second data science computer lab.


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/.
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 Mathematical and Physical Sciences 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.

LS0tDQp0aXRsZTogIlNUTTEwMDE6IENvbXB1dGVyIExhYiAxQiINCm91dHB1dDoNCiAgYm9va2Rvd246Omh0bWxfZG9jdW1lbnQyOiANCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IHRydWUNCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlDQogICAgdGhlbWU6IHJlYWRhYmxlDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQpiaWJsaW9ncmFwaHk6IFNUTTEwMDFfRFNfQ0xfcmVmZXJlbmNlcy5iaWIgDQpsaW5rLWNpdGF0aW9uczogeWVzDQotLS0NCg0KPHN0eWxlPg0KI1RPQyB7DQogIGJhY2tncm91bmQ6IHVybCgiaHR0cHM6Ly93d3cubGF0cm9iZS5lZHUuYXUvX21lZGlhL2xhLXRyb2JlLWFwaS92NS9pbWcvbG9nby5zdmciKTsNCiAgYmFja2dyb3VuZC1zaXplOiBjb250YWluOw0KICBwYWRkaW5nLXRvcDogODBweCAhaW1wb3J0YW50Ow0KICBiYWNrZ3JvdW5kLXJlcGVhdDogbm8tcmVwZWF0Ow0KfQ0KPC9zdHlsZT4NCg0KIyMjIERhdGEgU2NpZW5jZSBTdHJlYW0gey19DQoNCiMjIyBUb3BpYyAxQjogVXNpbmcgUlN0dWRpbyB7LX0NCg0KPGJyPg0KDQpXZWxjb21lIHRvIHRoZSBmaXJzdCBEYXRhIFNjaWVuY2UgY29tcHV0ZXIgbGFiIGZvciBTVE0xMDAxIQ0KDQpUaHJvdWdob3V0IHRoZSBzZW1lc3Rlciwgd2Ugd2lsbCB1c2UgdGhlICoqUiBzb2Z0d2FyZSBlbnZpcm9ubWVudCoqIGluIG91ciBjb21wdXRlciBsYWJzIGFuZCBhc3Nlc3NtZW50cy4NClIgaXMgZnJlZSwgZmxleGlibGUsIGFuZCB1c2VkIGJ5IG1pbGxpb25zIG9mIHBlb3BsZSBmb3Igc3RhdGlzdGljYWwgY29tcHV0aW5nIGFuZCBkYXRhIHZpc3VhbGlzYXRpb24uDQoNCkxlYXJuaW5nIFIgY2FuIGJlIGNoYWxsZW5naW5nIGF0IGZpcnN0LiBUbyBtYWtlIG91ciBsZWFybmluZyBleHBlcmllbmNlIG1vcmUgZW5qb3lhYmxlLCB3ZSB3aWxsIGJlIHVzaW5nICoqUlN0dWRpbyoqIHJhdGhlciB0aGFuIGJhc2UgUiBmb3IgYWxsIG91ciBSIGNvZGluZy4gUlN0dWRpbyBpcyBhbiBpbnRlZ3JhdGVkIGRldmVsb3BtZW50IGVudmlyb25tZW50IChJREUpIGZvciBSLCBhbmQgb2ZmZXJzIHNldmVyYWwgaGVscGZ1bCBmZWF0dXJlcyBhbmQgdXNlci1pbnRlcmZhY2Ugb3B0aW9ucyBtaXNzaW5nIGZyb20gYmFzZSBSLg0KDQpJbiB0aGlzIGZpcnN0IERhdGEgU2NpZW5jZSBjb21wdXRlciBsYWIgd2Ugd2lsbCB0YWtlIHRoaW5ncyBzbG93bHksIGFuZCBmb2N1cyBvbiBwcmFjdGljaW5nIGFuZCByZWluZm9yY2luZyBrZXkgUiBjb2Rpbmcgc2tpbGxzIHlvdSBiZWdhbiBkZXZlbG9waW5nIGluIFt0aGUgZmlyc3QgY29yZSBDb21wdXRlciBMYWJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0NMMSksIHZpYSBzb21lIGxpZ2h0LWhlYXJ0ZWQgZXhhbXBsZXMuIEEgc29saWQgUiBmb3VuZGF0aW9uIHdpbGwgZW5zdXJlIHRoYXQgaW4gc3Vic2VxdWVudCBjb21wdXRlciBsYWJzLCB5b3Ugd2lsbCBiZSBhYmxlIHRvIHBpY2sgdXAgYW5kIGFwcGx5IG5ldyBSIGNvZGluZyBza2lsbHMgbW9yZSBlYXNpbHkuDQoNCg0KQnkgdGhlIGVuZCBvZiB0aGlzIGxhYiwgeW91IHNob3VsZCBmZWVsIGNvbWZvcnRhYmxlIHVzaW5nIHNpbXBsZSBSIGNvbW1hbmRzLCBjcmVhdGluZyBhbmQgbmFtaW5nIG5ldyBvYmplY3RzLCBpbnN0YWxsaW5nLCBsb2FkaW5nIGFuZCB1c2luZyBSIHBhY2thZ2VzLCBhbmQgc2F2aW5nIGltYWdlcyBnZW5lcmF0ZWQgaW4gUlN0dWRpby4NCg0KPGJyPg0KDQojIENoZWNrbGlzdCB7LX0NCg0KQmVmb3JlIHdlIGNvbnRpbnVlLCBtYWtlIHN1cmUgdGhhdCB5b3UgaGF2ZSBkb25lIHRoZSBmb2xsb3dpbmc6DQoNCiogSW5zdGFsbGVkIFIgYW5kIFJTdHVkaW8gb24geW91ciBwZXJzb25hbCBkZXZpY2UgKHRoaXMgd2lsbCBiZSBoZWxwZnVsIGZvciBhc3NpZ25tZW50IHdvcmssIGV2ZW4gaWYgeW91IGludGVuZCB0byBjb21wbGV0ZSB0aGUgY29tcHV0ZXIgbGFicyBvbiB1bml2ZXJzaXR5IGNvbXB1dGVycykNCiogQ29tcGxldGVkIFt0aGUgZmlyc3QgY29yZSBDb21wdXRlciBMYWJdKGh0dHBzOi8vcnB1YnMuY29tL0xUVV9TVE0xMDAxL0NMMSkNCiogTG9va2VkIG92ZXIgdGhlIGRpZmZlcmVudCBib29rcyBpbiB0aGUgKkludHJvZHVjdGlvbiB0byBSKiBjb250ZW50IG9uIExNUw0KKiBDb25maXJtZWQgdGhhdCB5b3UgYXJlIGluIHRoZSBjb3JyZWN0IHN0cmVhbQ0KDQpJZiB5b3UgaGF2ZSBhbnkgcXVlc3Rpb25zIGFib3V0IGFueSBvZiB0aGVzZSBpdGVtcywgcGxlYXNlIGFzayB5b3VyIGNvbXB1dGVyIGxhYiBkZW1vbnN0cmF0b3IgZm9yIGFzc2lzdGFuY2UuDQoNCiMgSW5zdGFsbGluZyBuZXcgUiBwYWNrYWdlcw0KDQpSIGNvbnRhaW5zIG1hbnkgaW4tYnVpbHQgZnVuY3Rpb25zLCBhbmQgYnkgaXRzZWxmIGlzIHBlcmZlY3RseSBzdWZmaWNpZW50IGZvciBhIG51bWJlciBvZiBkYXRhIGFuYWx5c2lzIG1ldGhvZHMuDQpIb3dldmVyLCBvbmUgb2YgdGhlIGdyZWF0IGJlbmVmaXRzIG9mIFIgaXMgdGhhdCBhbnlvbmUgY2FuIGNyZWF0ZSAqKnBhY2thZ2VzKiogKGJ1bmRsZXMgb2YgY29kZSwgZGF0YSBhbmQgZnVuY3Rpb25zKSB3aGljaCBjYW4gYmUgdXBsb2FkZWQgdG8gZ2xvYmFsIHJlcG9zaXRvcmllcyAoc3VjaCBhcyBDUkFOKSwgYW5kIG1hZGUgYXZhaWxhYmxlIGZvciBhbnlvbmUgYXJvdW5kIHRoZSB3b3JsZCB0byBkb3dubG9hZCBhbmQgdXNlIGluIHRoZWlyIHZlcnNpb24gb2YgUi4gDQoNClRoZXNlIHBhY2thZ2VzIGFyZSBvZnRlbiBleHRyZW1lbHkgaGVscGZ1bC4gVGhleSBtYXkgY29udGFpbiB1c2VmdWwgZGF0YSBzZXRzIGZvciBhIHNwZWNpZmljIGZpZWxkIG9mIHJlc2VhcmNoLCBhZGRyZXNzIGEgc2hvcnRjb21pbmcgd2l0aCB0aGUgYmFzZSBSIHN1aXRlIG9mIGZ1bmN0aW9ucywgYWxsb3cgdXNlcnMgdG8gcGVyZm9ybSBzcGVjaWFsaXNlZCBhbmFseXNlcywgYW5kL29yIG9mZmVyIHVzZXJzIHNvbWUgYWRkaXRpb25hbCBmdW5jdGlvbmFsaXRpZXMgbWlzc2luZyBmcm9tIGJhc2UgUi4gIA0KDQpPbiB0aGUgb3RoZXIgaGFuZCwgc29tZXRpbWVzIHVzZXItY3JlYXRlZCBwYWNrYWdlcyBhcmUgbW9yZSBsaWdodC1oZWFydGVkLCBvciBhcmUgcGV0IHByb2plY3RzIG5vdCBuZWNlc3NhcmlseSBpbnRlbmRlZCBmb3Igc2VyaW91cyBkYXRhIGFuYWx5c2lzLiBPbmUgc3VjaCBleGFtcGxlIGlzIHRoZSBgbWVtZWAgcGFja2FnZSBbQG1lbWVzXSwgd2hpY2ggYWxsb3dzIHVzZXJzIHRvIGNyZWF0ZSBzaW1wbGUgbWVtZXMgdXNpbmcgUiBjb2RlLg0KDQpMZXQncyBpbnN0YWxsIGFuZCBsb2FkIHRoZSBgbWVtZWAgcGFja2FnZSBpbiBSU3R1ZGlvIG5vdy4NCg0KIyMgDQoNCkJlY2F1c2UgdGhlIGBtZW1lYCBwYWNrYWdlIGlzIG5vdCBpbnN0YWxsZWQgaW4gYmFzZSBSLCB3ZSBuZWVkIHRvIGRvd25sb2FkIGl0IGJlZm9yZSB3ZSBjYW4gdXNlIGl0Lg0KUmVjYWxsIHRoYXQgd2UgY2FuIHVzZSB0aGUgYGluc3RhbGwucGFja2FnZXMoKWAgUiBmdW5jdGlvbiB0byBkbyB0aGlzLiBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gZG8gdGhpcyBub3c6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFQsIGluY2x1ZGUgPSBGfQ0KaW5zdGFsbC5wYWNrYWdlcygibWVtZSIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJtZW1lIikNCmBgYA0KDQojIw0KDQpXaGVuIHlvdSBpbnN0YWxsIGFuIFIgcGFja2FnZSwgeW91IHdpbGwgc2VlIHNvbWUgdGV4dCBhcHBlYXIgaW4gdGhlIFJTdHVkaW8gYENvbnNvbGVgIHdpbmRvdy4gT2Z0ZW4sIHNvbWUgb2YgdGhpcyB0ZXh0IGlzIDxzcGFuIHN0eWxlPSJjb2xvcjpyZWQiPnJlZDwvc3Bhbj4sIHdoaWNoIGF0IGZpcnN0IGNhbiBiZSB1bm5lcnZpbmcgLSB5b3UgbWF5IHRoaW5rIHRoYXQgYW4gZXJyb3IgaGFzIG9jY3VycmVkLiAqKkRvbid0IHdvcnJ5ISoqIFdoaWxlIGl0IGlzIGFsd2F5cyBhIGdvb2QgaWRlYSB0byBjaGVjayB0aGVzZSByZWQgdGV4dCBtZXNzYWdlcywgdGhleSBhcmUgbm90IG5lY2Vzc2FyaWx5IGVycm9ycywgYW5kIG9mdGVuIGNhbiBiZSBzYWZlbHkgaWdub3JlZC4gDQoNCklmIHlvdSBjaGVjayB0aGUgZmluYWwgcGFydCBvZiB0aGUgdGV4dCBvdXRwdXQsIHdoaWNoIGlzIGluIGJsYWNrIG5vdCByZWQsIHlvdSBzaG91bGQgc2VlIHNvbWV0aGluZyBsaWtlIGBwYWNrYWdlIOKAmG1lbWXigJkgc3VjY2Vzc2Z1bGx5IHVucGFja2VkIGFuZCBNRDUgc3VtcyBjaGVja2VkYC4gVGhpcyBpcyByZWFzc3VyaW5nIC0gdGhlIHBhY2thZ2UgaGFzIGluc3RhbGxlZCBjb3JyZWN0bHksIGFuZCB0aGUgcmVkIHRleHQgYWJvdmUgaXMganVzdCB0ZWxsaW5nIHVzIGhvdyBSIHdlbnQgYWJvdXQgaW5zdGFsbGluZyBpdC4NCg0KKk5vdGU6IElmIHlvdSBzZWUgYSBtZXNzYWdlIGFib3V0IGluc3RhbGxpbmcgYFJ0b29sc2AsIGlnbm9yZSBpdCwgd2UgZG9uJ3QgbmVlZCBpdCBmb3IgdGhlIHB1cnBvc2VzIG9mIHRoaXMgbGFiLioNCg0KIyMgDQoNCk9uY2UgdGhlIGBtZW1lYCBwYWNrYWdlIGlzIGRvd25sb2FkZWQgYW5kIGluc3RhbGxlZCwgd2UgbmVlZCB0byBsb2FkIGl0IGluIG91ciBjdXJyZW50IFJTdHVkaW8gc2Vzc2lvbi4NClJ1biB0aGUgZm9sbG93aW5nIGNvZGUgdG8gbG9hZCB0aGUgYG1lbWVgIHBhY2thZ2UuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KbGlicmFyeShtZW1lKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpsaWJyYXJ5KG1lbWUpDQpgYGANCg0KIyBNYWtpbmcgbWVtZXMgaW4gUlN0dWRpbw0KDQpXaXRoIHRoZSBgbWVtZWAgcGFja2FnZSBpbnN0YWxsZWQgYW5kIGxvYWRlZCBpbiBSU3R1ZGlvLCB3ZSBjYW4gbm93IHN0YXJ0IHRvIG1ha2Ugc29tZSBzaW1wbGUgbWVtZXMhDQpXZSByZWFsbHkgb25seSBuZWVkIHR3byBsaW5lcyBvZiBjb2RlIGZvciB0aGlzLCBhcyB3ZSB3aWxsIGRlbW9uc3RyYXRlIHdpdGggdGhlIGZvbGxvd2luZyBleGFtcGxlLg0KDQojIw0KDQpGaXJzdGx5LCB3ZSBuZWVkIHRvIGZpbmQgYW4gYXBwcm9wcmlhdGUgaW1hZ2UuIEZvciB0aGlzIGV4YW1wbGUsIHdlIHdpbGwgdXNlIGFuIGltYWdlIG9mIEhhZ3JpZCwgZnJvbSB0aGUgSGFycnkgUG90dGVyIHNlcmllcy4gV2UgaGF2ZSBsb2NhdGVkIHRoaXMgaW1hZ2Ugb25saW5lLCBhbmQgY29waWVkIHRoZSB1cmwuIEluIFJTdHVkaW8sIHdlIGFzc2lnbiB0aGlzIHVybCB0byB0aGUgb2JqZWN0IGBoYWdyaWRgLCBhcyBzaG93biBiZWxvdzogDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IFR9DQpoYWdyaWQgPC0gImh0dHBzOi8vaS5pbWdmbGlwLmNvbS8xM3diMnQuanBnIg0KIyBOb3RlIHRoYXQgdGhlIHVybCBuZWVkcyB0byBiZSBjb250YWluZWQgd2l0aGluIHF1b3RhdGlvbiBtYXJrcw0KYGBgDQoNCk1ha2Ugc3VyZSB0byBydW4gdGhpcyBjb2RlIGJlZm9yZSBtb3Zpbmcgb24gdG8gdGhlIG5leHQgc3RlcC4NCg0KKk5vdGU6IEZvciBhIHJlZnJlc2hlciBvbiBvYmplY3RzLCBjaGVjayB0aGUgUiBDb2RpbmcgRnVuZGFtZW50YWxzIGJvb2sgaW4gdGhlIEludHJvZHVjdGlvbiB0byBSIGNvbnRlbnQgb24gTE1TLioNCg0KIyMNCg0KTmV4dCwgd2UgdXNlIHRoZSBgbWVtZWAgZnVuY3Rpb24gKHdoaWNoIGlzIG9ubHkgYXZhaWxhYmxlIGJlY2F1c2Ugd2UgaW5zdGFsbGVkIGFuZCBsb2FkZWQgdGhlIGBtZW1lYCBwYWNrYWdlKSB0byBhZGQgc29tZSB3b3JkcyB0byB0aGlzIGltYWdlLiBUcnkgcnVubmluZyB0aGUgY29kZSBiZWxvdywgYW5kIHNlZSB3aGF0IGhhcHBlbnMuDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQptZW1lKGhhZ3JpZCwgIlllciBhIHdpemFyZCIsICJ3aXRoIGNvZGluZyIsIGZvbnQgPSAic2FucyIpDQpgYGANCg0KKk5vdGU6IFNvbWUgd2FybmluZ3MgbWF5IGFwcGVhciBpbiB0aGUgYENvbnNvbGVgIHNlY3Rpb24gb2YgUlN0dWRpbyBhcyB0aGlzIGNvZGUgaXMgZXhlY3V0aW5nLiBEb24ndCB3b3JyeSBhYm91dCB0aGVtLCBpdCBpcyBzYWZlIHRvIGlnbm9yZSB0aGVzZSB3YXJuaW5ncy4qIA0KDQojIyBTYXZpbmcgaW1hZ2VzIGluIFJTdHVkaW8NCg0KSWYgeW91IHdvdWxkIGxpa2UgdG8gc2F2ZSB0aGUgbWVtZSB5b3UgaGF2ZSBtYWRlLCB3ZSBoYXZlIGEgY291cGxlIG9mIG9wdGlvbnMsIHdoaWNoIHdlIHdpbGwgZGVtb25zdHJhdGUgbm93LCB2aWEgYW5vdGhlciBleGFtcGxlLg0KDQpJbiB0aGUgY29kZSBiZWxvdywgd2UgbWFrZSBhIG5ldyBtZW1lLCBhbmQgYXNzaWduIGl0IHRvIHRoZSBvYmplY3QgYHN1Y2Nlc3NgLiBUcnkgcnVubmluZyB0aGlzIGNvZGUgbm93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0Kc3VjY2Vzc19raWQgPC0gImh0dHA6Ly9pMC5reW0tY2RuLmNvbS9lbnRyaWVzL2ljb25zL21vYmlsZS8wMDAvMDAwLzc0NS9zdWNjZXNzLmpwZyINCnN1Y2Nlc3MgPC0gbWVtZShzdWNjZXNzX2tpZCwgIlVzaW5nIFIiLCAidG8gbWFrZSBtZW1lcyIsIGZvbnQgPSAic2FucyIpDQpzdWNjZXNzDQpgYGANCg0KKkhpbnQ6IE5vdGljZSB0aGF0IHdlIG5lZWQgdG8gaW5jbHVkZSB0aGUgZmluYWwgbGluZSBvZiBjb2RlLCBjYWxsaW5nIHRoZSBvYmplY3QgYHN1Y2Nlc3NgLCBpbiBvcmRlciBmb3IgdGhlIGltYWdlIHRvIGJlIHNob3duLioNCg0KIyMjIA0KDQpJZiB3ZSB3b3VsZCBsaWtlIHRvIHNhdmUgb3VyIG5ldyBtZW1lIHVzaW5nIFIgY29kZSwgd2UgY2FuIHVzZSB0aGUgZnVuY3Rpb24gYG1lbWVfc2F2ZWAuIFRha2UgYSBsb29rIGF0IHRoZSBjb2RlIGJlbG93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KbWVtZV9zYXZlKHN1Y2Nlc3MsIGZpbGU9ImM6L1NUTTEwMDEvRGF0YSBTY2llbmNlL3N1Y2Nlc3Nfa2lkX1JfbWVtZS5wbmciKSANCmBgYA0KDQpIZXJlLCB3ZSBhcmUgc2F2aW5nIG91ciBgc3VjY2Vzc2AgbWVtZSB0byB0aGUgZmlsZSBsb2NhdGlvbiBgYzpcU1RNMTAwMVxEYXRhIFNjaWVuY2VcYCwgd2l0aCB0aGUgbmFtZSBgc3VjY2Vzc19raWRfUl9tZW1lLnBuZ2AuIA0KDQoqTm90ZTogQWx0aG91Z2ggdGhlIGZpbGUgcGF0aCBvbiBvdXIgY29tcHV0ZXIgaW5jbHVkZXMgYmFja3NsYXNoZXMgKGBcYCksIGluIFIgY29kZSB0aGVzZSBuZWVkIHRvIGJlIGNoYW5nZWQgdG8gZm9yd2FyZCBzbGFzaGVzIChgL2ApLioNCg0KIyMjDQoNCkluc3RlYWQgb2YgdXNpbmcgUiBjb2RlLCB3ZSBjYW4gc2F2ZSBvdXIgaW1hZ2UgbWFudWFsbHksIGJ5IG5hdmlnYXRpbmcgdG8gdGhlIGBQbG90c2Agd2luZG93IGluIFJTdHVkaW8sIGNsaWNraW5nIGBFeHBvcnRgLCBhbmQgc2VsZWN0aW5nIGVpdGhlciBgU2F2ZSBhcyBJbWFnZS4uLmAgb3IgYFNhdmUgYXMgUERGLi4uYCwgYXMgc2hvd24gYmVsb3c6IA0KDQo8Y2VudGVyPjxpbWcgc3JjPSJzYXZlX2ltYWdlLmpwZyIgd2lkdGggPSAzMDA+PC9jZW50ZXI+DQoNClRyeSB0aGlzIG5vdywgYW5kIHNhdmUgeW91ciBgc3VjY2Vzc2AgbWVtZSBhcyBhIHBkZi4NCg0KIyMNCg0KTm93IGl0J3MgdGltZSB0byBtYWtlIHlvdXIgb3duIG1lbWUgaW4gUlN0dWRpby4gRm9sbG93IHRoZSBzdGVwcyBiZWxvdzoNCg0KYS4gRmluZCBhbiBhcHByb3ByaWF0ZSBpbWFnZSBvZiB5b3VyIGNob2ljZSBvbmxpbmUgKHBsZWFzZSBlbnN1cmUgeW91IHBpY2sgY29udGVudCBzdWl0YWJsZSBmb3IgdW5pdmVyc2l0eSBhbmQgd29yaykuIA0KDQpiLiBDb3B5IHRoZSB1cmwuIA0KDQpjLiBBc3NpZ24gdGhpcyB1cmwgdG8gYW4gb2JqZWN0IGluIFJTdHVkaW8uDQoNCmQuIFVzZSB0aGUgYG1lbWVgIGZ1bmN0aW9uIHRvIGFkZCB3b3JkcyB0byB5b3VyIGltYWdlLg0KDQplLiBTYXZlIHlvdXIgbWVtZSB1c2luZyBlaXRoZXIgdGhlIGBtZW1lX3NhdmVgIGZ1bmN0aW9uIG9yIHRoZSBtYW51YWwgYXBwcm9hY2guDQoNCipIaW50OiBJZiB5b3UgYXJlIG5vdCBxdWl0ZSBzdXJlIGhvdyB0byBiZWdpbiwgY2xpY2sgdGhlIGBDb2RlYCBidXR0b24gdG8gdGhlIHJpZ2h0IGJlbG93LioNCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtaGlkZSIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiMgRmlyc3QsIHdlIG5lZWQgdG8gZmluZCBhbiBpbWFnZSwgYW5kIGFzc2lnbiBpdCB0byBhbiBvYmplY3QgDQojIChoZXJlIHdlIHVzZSB0aGUgZ2VuZXJpYyBvYmplY3QgbmFtZSAnaW1hZ2VfbmFtZScpDQojIEp1c3QgcmVwbGFjZSB0aGUgLi4ucyB3aXRoIHRoZSB1cmwgb2YgeW91ciBpbWFnZQ0KaW1hZ2VfbmFtZSA8LSAiLi4uIg0KIyBOZXh0LCB3ZSBuZWVkIHRvIHVzZSB0aGUgbWVtZSBmdW5jdGlvbiwgdG8gYWRkIHNvbWUgd29yZHMgKGp1c3QgcmVwbGFjZSB0aGUgLi4ucykNCm15X21lbWUgPC0gbWVtZShpbWFnZV9uYW1lLCAiLi4uIiwgIi4uLiIsIGZvbnQgPSAic2FucyIpDQojIE5vdGUgdGhhdCB5b3UgbmVlZCB0byBpbmNsdWRlIHRoZSBgLCBmb250ID0gInNhbnMiYCBwYXJ0IHRvIGVuc3VyZSBSIGtub3cgd2hpY2ggZm9udCB0byB1c2UuDQojIE5vdyBhbGwgdGhhdCdzIGxlZnQgaXMgdG8gc2F2ZSB5b3VyIG1lbWUgLSBqdXN0IHJlZmVyIHRvIHRoZSBjb2RlIGFib3ZlLg0KYGBgDQoNCkNvbmdyYXR1bGF0aW9ucyEgWW91IHdlcmUgcHJvYmFibHkgbm90IGV4cGVjdGluZyB0byBtYWtlIGEgbWVtZSBpbiB5b3VyIGZpcnN0IGRhdGEgc2NpZW5jZSBjb21wdXRlciBsYWIuIFdoaWxlIHRoaXMgd29uJ3QgYmUgb24gdGhlIGZpbmFsIGV4YW0sIHRoZSBSIHNraWxscyB5b3UgYXJlIGRldmVsb3BpbmcgaGVyZSBhcmUgaW1wb3J0YW50LCBhbmQgaG9wZWZ1bGx5IHlvdSBhcmUgc3RhcnRpbmcgdG8gcmVhbGlzZSB0aGF0IFIgaXMgdmVyeSB2ZXJzYXRpbGUuDQoNCiMgQ3VzdG9taXppbmcgR0lGcyBpbiBSU3R1ZGlvDQoNClIgaXMgbm90IGxpbWl0ZWQgdG8gd29ya2luZyB3aXRoIHN0YXRpYyBpbWFnZXMgLSB3ZSBjYW4gbW9kaWZ5IGFuZCBjcmVhdGUgZ2lmcyBhbmQgYW5pbWF0aW9ucyAoYW5kIGluIGZ1dHVyZSB3ZWVrcyB3ZSB3aWxsIG1ha2UgYW5pbWF0ZWQsIGludGVyYWN0aXZlIGdyYXBocyB1c2luZyByZWFsIGRhdGEpLg0KSW4gdGhpcyBzZWN0aW9uLCB3ZSB3aWxsIHVzZSBhbm90aGVyIGZ1biBwYWNrYWdlLCB0aGUgYG1hZ2lja2AgcGFja2FnZSBbQG1hZ2lja10sIHRvIGN1c3RvbWl6ZSBhIGdpZi4NCg0KIyMNCg0KUnVuIHRoZSBmb2xsb3dpbmcgY29kZSB0byBkb3dubG9hZCwgaW5zdGFsbCBhbmQgbG9hZCB0aGUgYG1hZ2lja2AgcGFja2FnZSBpbiB5b3VyIGN1cnJlbnQgUlN0dWRpbyBzZXNzaW9uLg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCBpbmNsdWRlID0gRn0NCmluc3RhbGwucGFja2FnZXMoIm1hZ2ljayIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQppbnN0YWxsLnBhY2thZ2VzKCJtYWdpY2siKQ0KYGBgDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gVCwgZWNobyA9IEYsIGluY2x1ZGUgPSBGfQ0KbGlicmFyeShtYWdpY2spDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmxpYnJhcnkobWFnaWNrKQ0KYGBgDQoNCiMjDQoNCkp1c3QgYXMgd2Ugb2J0YWluZWQgb25saW5lIGltYWdlcyBvZiBgaGFncmlkYCBhbmQgYHN1Y2Nlc3Mga2lkYCwgc28gdG9vIGNhbiB3ZSB1c2UgdXJscyB0byBnaWZzIGFuZCBhbmltYXRpb25zLg0KRm9yIHRoaXMgZXhhbXBsZSwgd2UgaGF2ZSB1c2VkIHRoZSB1cmwgdG8gYSBnaWYgb2YgYSByb3RhdGluZyBFYXJ0aC4NCg0KV2UgY2FuIHVzZSB0aGUgYGltYWdlX3JlYWRgIGZ1bmN0aW9uIHRvIHJlYWQgdGhpcyBnaWYgaW50byBSU3R1ZGlvLiBSdW4gdGhlIGNvZGUgYmVsb3cgdG8gYXNzaWduIGl0IHRvIHRoZSBvYmplY3QgYEVhcnRoYC4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCkVhcnRoIDwtIGltYWdlX3JlYWQoImh0dHBzOi8vaS5naXBoeS5jb20vbWVkaWEvbWY4VWJJRGV3N2U4Zy9naXBoeS5naWYiKQ0KRWFydGgNCmBgYA0KDQpNYWtlIHN1cmUgdG8gcnVuIHRoaXMgY29kZSBiZWZvcmUgbW92aW5nIG9uIHRvIHRoZSBuZXh0IHN0ZXAgKGRvbid0IHdvcnJ5IGlmIGl0IHRha2VzIGEgZmV3IHNlY29uZHMpLiBUaGUgZ2lmIHNob3VsZCBhcHBlYXIgaW4gdGhlIGBWaWV3ZXJgIHNlY3Rpb24gb2YgUlN0dWRpby4NCg0KIyMNCg0KVXNpbmcgdGhlIGBtYWdpY2tgIHBhY2thZ2UsIHdlIGNhbiBlYXNpbHkgbWFrZSBzb21lIGNoYW5nZXMgdG8gdGhpcyBgRWFydGhgIGdpZi4NCg0KUnVuIHRoZSBmb2xsb3dpbmcgY29kZSwgYW5kIGluc3BlY3QgdGhlIG91dHB1dC4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVCwgLCBmaWcuYWxpZ24gPSAiY2VudGVyIn0NCnJldihFYXJ0aCkgJT4lIA0KICAgICAgICAgICBpbWFnZV9mbGlwKCkgJT4lIA0KICAgICAgICAgICBpbWFnZV9hbm5vdGF0ZSgiICAgICAgICBNZWFud2hpbGUsIGluIEF1c3RyYWxpYSIsIHNpemUgPSA0MCwgY29sb3IgPSAid2hpdGUiKQ0KYGBgDQoNCllvdSB3aWxsIG5vdGljZSBoZXJlIHRoYXQ6IA0KDQoqIFdlIGhhdmUgcmV2ZXJzZWQgdGhlIGdpZiwgdXNpbmcgdGhlIGByZXZgIGZ1bmN0aW9uDQoqIFdlIGhhdmUgZmxpcHBlZCB0aGUgZ2lmLCB1c2luZyB0aGUgYGltYWdlX2ZsaXBgIGZ1bmN0aW9uLCBhbmQNCiogV2UgaGF2ZSBhZGRlZCB0ZXh0IHRvIHRoaXMgZ2lmIHVzaW5nIHRoZSBgaW1hZ2VfYW5ub3RhdGVgIGZ1bmN0aW9uDQoNClRoaXMgaXMgcmVhbGx5IGp1c3Qgc2NyYXRjaGluZyB0aGUgc3VyZmFjZSBvZiB0aGUgYG1hZ2lja2AgcGFja2FnZS4gRm9yIHRoZSBtb21lbnQgdGhvdWdoLCBsZXQncyBtb3ZlIG9uLg0KDQojIERyYXdpbmcgYSBmaXNoIGluIFJTdHVkaW8NCg0KSW5zdGVhZCBvZiB1c2luZyBhIHByZS1leGlzdGluZyBpbWFnZSBvciBnaWYsIGxldCdzIG5vdyB0cnkgdG8gY3JlYXRlIG9uZSBmcm9tIHNjcmF0Y2guIFNwZWNpZmljYWxseSwgbGV0J3MgZHJhdyBhIGZpc2guIFRvIGRvIHRoaXMsIHdlIGNhbiB1c2UgdGhlIGFwcHJvcHJpYXRlbHkgbmFtZWQgYHJmaXNoZHJhd2AgcGFja2FnZSBbQHJmaXNoZHJhd10uDQoNCiMjDQoNCkxldCdzIGRvd25sb2FkIGFuZCBpbnN0YWxsIHRoZSBgcmZpc2hkcmF3YCBwYWNrYWdlIG5vdy4gSW4gb3JkZXIgdG8gdXNlIHRoaXMgcGFja2FnZSwgd2Ugd2lsbCBhbHNvIG5lZWQgdG8gZG93bmxvYWQgYW5kIGluc3RhbGwgc29tZSBhZGRpdGlvbmFsIHBhY2thZ2VzLCB1cG9uIHdoaWNoIHRoZSBgcmZpc2hkcmF3YCBwYWNrYWdlIGRlcGVuZHMuIFN1Y2ggcGFja2FnZXMgYXJlIGtub3duIGFzICoqZGVwZW5kZW5jaWVzKiosIGFuZCBpdCBpcyBjb21tb24gZm9yIG1vcmUgc29waGlzdGljYXRlZCBSIHBhY2thZ2VzIHRvIGhhdmUgbXVsdGlwbGUgZGVwZW5kZW5jaWVzLg0KDQoqTm90ZTogVGhlc2UgZGVwZW5kZW5jaWVzIGFyZSBwYWNrYWdlcyBpbiB0aGVpciBvd24gcmlnaHQuKg0KDQpSdW4gdGhpcyBjb2RlIGluIFJTdHVkaW8gbm93Lg0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBULCBpbmNsdWRlID0gRn0NCmluc3RhbGwucGFja2FnZXMoInJmaXNoZHJhdyIsIHJlcG9zID0gImh0dHA6Ly9jcmFuLnVzLnItcHJvamVjdC5vcmciKQ0KaW5zdGFsbC5wYWNrYWdlcygicGF0Y2h3b3JrIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQppbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIiwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCiMgTWFpbiBwYWNrYWdlDQppbnN0YWxsLnBhY2thZ2VzKCJyZmlzaGRyYXciKQ0KIyBEZXBlbmRlbmNpZXMNCmluc3RhbGwucGFja2FnZXMoInBhdGNod29yayIpDQppbnN0YWxsLnBhY2thZ2VzKCJnZ3Bsb3QyIikNCmBgYA0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IFQsIGVjaG8gPSBGLCBpbmNsdWRlID0gRn0NCmxpYnJhcnkoInJmaXNoZHJhdyIpDQpsaWJyYXJ5KCJwYXRjaHdvcmsiKQ0KbGlicmFyeSgiZ2dwbG90MiIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gVH0NCmxpYnJhcnkoInJmaXNoZHJhdyIpDQpsaWJyYXJ5KCJwYXRjaHdvcmsiKQ0KbGlicmFyeSgiZ2dwbG90MiIpDQpgYGANCg0KIyMNCg0KSWYgeW91IG5vdyBydW4gdGhlIGNvZGUgYmVsb3cgKG1ha2luZyB0aGUgYXBwcm9wcmlhdGUgc2VsZWN0aW9uIGJldHdlZW4gYHdpbmRvd3MoKWAgYW5kIGBxdWFydHooKWApLCBhIGRldGFpbGVkIGRyYXdpbmcgb2YgYSBmaXNoIHNob3VsZCBhcHBlYXIgaW4gYSBuZXcgd2luZG93IQ0KDQpgYGB7ciBjbGFzcy5zb3VyY2UgPSAiZm9sZC1zaG93IiwgZXZhbCA9IEYsIGVjaG8gPSBUfQ0KZ2V0X3BvbHlsaW5lcyhwYXRoID0gImluc3QvZmlzaGRyYXcuanMiLA0KICAgICAgICAgICAgICBmb3JtYXQgPSAic21pbCIsDQogICAgICAgICAgICAgIG91dHB1dCA9ICJhbmltYXRlZC5zdmciLA0KICAgICAgICAgICAgICBkcmF3X3R5cGUgPSAicmFuZG9tIikNCg0KIyBGb3IgV2luZG93cyB1c2VycywgdXNlDQp3aW5kb3dzKCkgDQpmaXNoX2RyYXcoKQ0KDQojIEZvciBNYWMgdXNlcnMsIGluc3RlYWQgdXNlDQpxdWFydHooKQ0KZmlzaF9kcmF3KCkNCmBgYA0KDQojIw0KDQpTdXBwb3NlIHdlIHdvdWxkIGxpa2UgdG8gY2hhbmdlIHRoZSBjb2xvdXIgb2Ygb3VyIGZpc2guIFdlIGNhbiBkbyB0aGlzLCBieSBpbmNsdWRpbmcgdGhlIGFyZ3VtZW50IGBjb2wgPSAiLi4uImAgd2l0aGluIHRoZSBmdW5jdGlvbiBgZmlzaF9kcmF3YC4gRm9yIGV4YW1wbGUsIGlmIHdlIHdvdWxkIGxpa2Ugb3VyIGZpc2ggdG8gYmUgYmx1ZSwgd2UgY2FuIHdyaXRlDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpmaXNoX2RyYXcoY29sID0gImJsdWUiKQ0KYGBgDQoNClRyeSBjaGFuZ2luZyB0aGlzIGNvbG91ciB0byBhIGRpZmZlcmVudCBjb2xvdXIsIGFuZCB0aGVuIHJ1biB0aGUgY29kZS4NCg0KKk5vdGU6IEFzIHdlIHdpbGwgc2VlIGluIGZ1dHVyZSBsYWJzLCBtYW55IFIgZnVuY3Rpb25zIGZvciBwcm9kdWNpbmcgdmlzdWFsaXNhdGlvbnMgaW5jb3Jwb3JhdGUgYW4gYXJndW1lbnQgbGlrZSBgY29sID0gYCB0byBhbGxvdyBmb3IgY29sb3VyIHNwZWNpZmljYXRpb24uKg0KDQojIFBhbG1lciBQZW5ndWlucyBEYXRhIFNldCB7I3Blbmd1aW5zfQ0KDQpOb3cgdGhhdCB3ZSBoYXZlIGhhZCBhIHRhc3RlIG9mIHNvbWUgb2YgdGhlIG1vcmUgbGlnaHQtaGVhcnRlZCBSIHBhY2thZ2VzIG91dCB0aGVyZSwgbGV0J3MgY29uc2lkZXIgYSBwYWNrYWdlIHdoaWNoIGNvbnRhaW5zIHNvbWUgdXNlZnVsIGRhdGEuDQoNClRoZSBgcGFsbWVycGVuZ3VpbnNgIFIgcGFja2FnZSBbQHBlbmd1aW5zXSBjb250YWlucyBkYXRhLCBjb2xsZWN0ZWQgb3ZlciB0aGUgY291cnNlIG9mIHNldmVyYWwgeWVhcnMsIG9uIDMgc3BlY2llcyBvZiBwZW5ndWluIGxpdmluZyBvbiBkaWZmZXJlbnQgaXNsYW5kcyBpbiB0aGUgUGFsbWVyIGFyY2hpcGVsYWdvLCBvZmYgdGhlIGNvYXN0IG9mIEFudGFyY3RpY2EuIE92ZXIgdGhlIGNvdXJzZSBvZiB0aGUgbmV4dCBmZXcgZGF0YSBzY2llbmNlIGNvbXB1dGVyIGxhYnMsIHdlIHdpbGwgY3JlYXRlIHZhcmlvdXMgaW50ZXJhY3RpdmUgZGF0YSB2aXN1YWxpc2F0aW9ucyB1c2luZyB0aGUgYHBlbmd1aW5zYCBkYXRhIGZyb20gdGhpcyBwYWNrYWdlLg0KDQpGb3IgbW9yZSBkZXRhaWxzIG9uIHRoZSBgcGVuZ3VpbnNgIGRhdGEgc2V0LCBhbmQgYSB0YXN0ZSBvZiB3aGF0J3MgYWhlYWQgaW4gZnV0dXJlIGxhYnMsIHlvdSBjYW4gcmVmZXIgdG8gW1NlY3Rpb24gMiBvZiB0aGUgRGF0YSBWaXN1YWxpc2F0aW9uIGluIFIgc3VwcGxlbWVudF0oaHR0cHM6Ly9ib29rZG93bi5vcmcvcmVoay9zdG0xMDAxX2RzbV9kYXRhX3Zpc3VhbGlzYXRpb25faW5fci9wZW5ndWlucy5odG1sKS4NCg0KRm9yIHRoaXMgbGFiLCBsZXQncyB1c2Ugc29tZSBSIGZ1bmN0aW9ucyB0byBpbnNwZWN0IHRoZSAgYHBlbmd1aW5zYCBkYXRhIHNldC4NCg0KIyMNCg0KSnVzdCBsaWtlIHRoZSBwcmV2aW91cyBwYWNrYWdlcywgdG8gYmVnaW4gd2Ugd2lsbCBuZWVkIHRvIGRvd25sb2FkIGFuZCBsb2FkIHRoZSBgcGFsbWVycGVuZ3VpbnNgIHBhY2thZ2UuDQoNClVzaW5nIHdoYXQgeW91IGhhdmUgcHJhY3RpY2VkIGVhcmxpZXIgaW4gdGhpcyBjb21wdXRlciBsYWIsIGluc3RhbGwgYW5kIGxvYWQgdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZSBpbiBSU3R1ZGlvIG5vdy4NCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBGLCBlY2hvID0gRiwgaW5jbHVkZSA9IEZ9DQppbnN0YWxsLnBhY2thZ2VzKHNldGRpZmYoInBhbG1lcnBlbmd1aW5zIiwgcm93bmFtZXMoaW5zdGFsbGVkLnBhY2thZ2VzKCkpKSwgcmVwb3MgPSAiaHR0cDovL2NyYW4udXMuci1wcm9qZWN0Lm9yZyIpDQpgYGANCg0KYGBge3IgY2xhc3Muc291cmNlID0gImZvbGQtc2hvdyIsIGV2YWwgPSBULCBlY2hvID0gRiwgd2FybmluZz1GfQ0KbGlicmFyeShwYWxtZXJwZW5ndWlucykNCmBgYA0KDQoqTm90ZTogWW91IG1heSBhbHJlYWR5IGhhdmUgdGhlIGBwYWxtZXJwZW5ndWluc2AgcGFja2FnZSBpbnN0YWxsZWQsIGlmIHlvdSBhcmUgdXNpbmcgdGhlIHNhbWUgZGV2aWNlIHlvdSB1c2VkIGZvciBbdGhlIGZpcnN0IGNvcmUgQ29tcHV0ZXIgTGFiXShodHRwczovL3JwdWJzLmNvbS9MVFVfU1RNMTAwMS9DTDEpLioNCg0KIyMNCg0KUmVjYWxsIGZyb20gW3RoZSBmaXJzdCBjb3JlIENvbXB1dGVyIExhYl0oaHR0cHM6Ly9ycHVicy5jb20vTFRVX1NUTTEwMDEvQ0wxKSB0aGF0IHlvdSBjYW4gZWFzaWx5IGNoZWNrIHRoZSBkaW1lbnNpb25zIG9mIHlvdXIgZGF0YSB1c2luZyB0aGUgYGRpbWAsIGBucm93YCBhbmQgYG5jb2xgIGZ1bmN0aW9ucy4gVXNlIHRoZXNlIG5vdyB0byBhc3Nlc3MgdGhlIGBwZW5ndWluc2AgZGF0YSBzZXQuDQoNCipOb3RlOiBDaGVjayB0aGUgYENvZGVgIGJ1dHRvbiBiZWxvdyBpZiB5b3Ugd291bGQgbGlrZSBhIHJlZnJlc2hlci4qDQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLWhpZGUiLCBldmFsID0gRiwgZWNobyA9IFR9DQojIFRoaXMgY29kZSBjaGVja3MgdGhlIGRpbWVuc2lvbnMgb2YgdGhlIHBlbmd1aW5zIGRhdGEgc2V0DQpkaW0ocGVuZ3VpbnMpDQpgYGANCg0KIyMNCg0KVXNlIHRoZSBgc3VtbWFyeWAgZnVuY3Rpb24gdG8gb2J0YWluIGEgcXVpY2sgb3ZlcnZpZXcgb2YgdGhlIGRhdGEgY29udGFpbmVkIHdpdGhpbiB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldC4NCg0KRG9uJ3Qgd29ycnkgdG9vIG11Y2ggYWJvdXQgdGhlIHZhbHVlcyBzaG93biBpbiB0aGUgc3VtbWFyeSB0YWJsZSAtIHRoZSBtYWluIHRoaW5ncyB0byBub3RlIGF0IHRoaXMgc3RhZ2UgYXJlIHRoZSBkaWZmZXJlbnQgdmFyaWFibGVzLCBuYW1lbHkgYHNwZWNpZXNgLCBgaXNsYW5kYCwgYGJpbGxfbGVuZ3RoX21tYCwgYGJpbGxfZGVwdGhfbW1gLCBgZmxpcHBlcl9sZW5ndGhfbW1gLCBgYm9keV9tYXNzX2dgLCBgc2V4YCBhbmQgYHllYXJgLg0KDQojIw0KDQpPZnRlbiwgd2hlbiB3ZSBiZWdpbiB3b3JraW5nIHdpdGggYSBuZXcgZGF0YSBzZXQsIGl0IGlzIGhlbHBmdWwgdG8gdGFrZSBhIHF1aWNrIGxvb2sgYXQgc29tZSBvZiB0aGUgcmVjb3JkZWQgdmFsdWVzLiBXZSBjYW4gdXNlIHRoZSBgaGVhZGAgZnVuY3Rpb24gdG8gbG9vayBhdCB0aGUgcmVjb3JkZWQgdmFsdWVzIGZvciB0aGUgZmlyc3QgNiBvYnNlcnZhdGlvbnMgaW4gYSBkYXRhIHNldC4gDQoNClRyeSB1c2luZyB0aGUgYGhlYWRgIGZ1bmN0aW9uIG5vdywgd2l0aCB0aGUgYHBlbmd1aW5zYCBkYXRhIHNldC4gV2hhdCBkbyB5b3Ugb2JzZXJ2ZT8NCg0KIyMNCg0KV2hlbiBhIGRhdGEgc2V0IGhhcyBtdWx0aXBsZSBjb2x1bW5zIG9mIGluZm9ybWF0aW9uLCB3ZSBjYW4gYXNzZXNzIHRoZSBpbmZvcm1hdGlvbiBpbiBzcGVjaWZpYyBjb2x1bW4gYnkgd3JpdGluZyB0aGUgbmFtZSBvZiB0aGUgb2JqZWN0LCBhZGRpbmcgYSBgJGAgYXQgdGhlIGVuZCwgYW5kIHRoZW4gd3JpdGluZyB0aGUgbmFtZSBvZiB0aGUgc3BlY2lmaWMgY29sdW1uIHdlIHdvdWxkIGxpa2UgdG8gaW5zcGVjdC4NCg0KRm9yIGV4YW1wbGUsIHdlIGNvdWxkIHVzZSB0aGUgZm9sbG93aW5nIGNvZGUgdG8gY2hlY2sgdGhlIHJlY29yZGVkIGJpbGwgbGVuZ3RoIG1lYXN1cmVtZW50cyBvZiB0aGUgcGVuZ3VpbnM6DQoNCmBgYHtyIGNsYXNzLnNvdXJjZSA9ICJmb2xkLXNob3ciLCBldmFsID0gRiwgZWNobyA9IFR9DQpwZW5ndWlucyRiaWxsX2xlbmd0aF9tbQ0KYGBgDQoNClJ1biB0aGlzIGNvZGUgbm93Lg0KDQoqTm90ZTogV2hlbiB3ZSBoYXZlIGEgbGFyZ2UgZGF0YSBzZXQsIG91dHB1dCBtYXkgYXBwZWFyIG92ZXIgc2V2ZXJhbCBsaW5lcyBpbiB0aGUgYENvbnNvbGVgLiBUaGUgbnVtYmVycyB0aGF0IGFwcGVhciBpbiBicmFja2V0cyB0byB0aGUgbGVmdCBvZiBlYWNoIGxpbmUgb2Ygb3V0cHV0IGFyZSBub3Qgb2JzZXJ2YXRpb25zLiBSYXRoZXIsIHRoZXNlIGRlbm90ZSB0aGUgcG9zaXRpb24gbnVtYmVyIGZvciB0aGUgZmlyc3Qgb2JzZXJ2YXRpb24gb24gdGhhdCBsaW5lIG9mIG91dHB1dC4gRS5nLiBhIGBbMTddYCB3b3VsZCBkZW5vdGUgdGhhdCB0aGUgb2JzZXJ2YXRpb24gZGlyZWN0bHkgdG8gdGhlIHJpZ2h0IG9mIHRoZSBgWzE3XWAgaXMgdGhlIDE3dGggcmVjb3JkZWQgb2JzZXJ2YXRpb24gaW4gdGhlIGRhdGEgc2V0LCBmb3IgdGhlIHZhcmlhYmxlIGJlaW5nIGNvbnNpZGVyZWQuKg0KDQojIw0KDQpUcnkgdXNpbmcgdGhpcyBgJGAgYXBwcm9hY2ggdG8gY2hlY2sgdGhlIHJlY29yZGVkIGJpbGwgZGVwdGhzIGFuZCBib2R5IG1hc3NlcyBvZiB0aGUgcGVuZ3VpbnMuDQoNCipOb3RlOiBOb3RpY2UgdGhhdCBvbmNlIHlvdSB0eXBlIHRoZSBgJGAsIFJTdHVkaW8gd2lsbCBoZWxwZnVsbHkgcHJvbXB0IHlvdSB3aXRoIHBvc3NpYmxlIHNlbGVjdGlvbnMuKg0KDQo8YnI+DQoNCiMjIyMgVGhhdCdzIHRoZSBlbmQgb2YgdGhlIGZpcnN0IGRhdGEgc2NpZW5jZSBjb21wdXRlciBsYWIhICMjIyMgey19DQoNCkhvcGVmdWxseSB5b3UgaGF2ZSBlbmpveWVkIHRoaXMgZmlyc3QgY29tcHV0ZXIgbGFiLCBhbmQgbm93IGhhdmUgYSBiZXR0ZXIgaWRlYSBvZiBqdXN0IGhvdyB2ZXJzYXRpbGUgUiBjYW4gYmUgKHBhcnRpY3VsYXJseSB3aGVuIHVzaW5nIHRoZSBoZWxwZnVsIFJTdHVkaW8gR1VJKS4gRG9uJ3Qgd29ycnkgaWYgc29tZSBvZiB0aGUgY29kZSBzZWVtcyBkaWZmaWN1bHQgYXQgdGhlIG1vbWVudCAtIHRoaXMgaXMgb25seSB0aGUgZmlyc3QgbGFiIGFmdGVyIGFsbCEgDQoNCkluIHRoZSBuZXh0IGRhdGEgc2NpZW5jZSBjb21wdXRlciBsYWIgd2Ugd2lsbCBjb250aW51ZSB3b3JraW5nIHdpdGggdGhlIGBwYWxtZXJwZW5ndWluc2AgZGF0YSBzZXQsIGFuZCBjb3ZlciBob3cgdG8gY3JlYXRlIGludGVyYWN0aXZlIHBsb3RzIHVzaW5nIGEgbmV3IHBhY2thZ2UuDQoNCiMjIyMgSW1wb3J0YW50IE5vdGVzICMjIyMgey19DQoNCg0KKiAqKklmIHlvdSBoYXZlIGFueSBxdWVzdGlvbnMgYWJvdXQgdGhlIGNvbnRlbnQgaW4gdGhpcyBsYWIsIG9yIGFyZSBzdHVjayBvbiBzb21lIFIgY29kZSB1c2VkIGluIHRoZSBsYWIsIHBsZWFzZSBhc2sgeW91ciBsYWIgZGVtb25zdHJhdG9yIGZvciBhc3Npc3RhbmNlLioqDQoNCiogKipNYWtlIHN1cmUgdG8gc2F2ZSB5b3VyIFIgc2NyaXB0IGZpbGUgc29tZXdoZXJlIHNhZmUgLSBpdCBtYXkgYmUgYSBoZWxwZnVsIHJlZmVyZW5jZSBzb3VyY2UgZm9yIGxhdGVyIHdvcmsuKioNCg0KKiAqKk1ha2Ugc3VyZSB0aGF0IHlvdSBmaW5pc2ggb2ZmIHlvdXIgcmVhZGluZ3Mgb2YgdGhlIEludHJvZHVjdGlvbiB0byBSIGNvbnRlbnQgb24gTE1TIHByaW9yIHRvIHRoZSBzZWNvbmQgZGF0YSBzY2llbmNlIGNvbXB1dGVyIGxhYi4qKg0KDQo8YnI+DQoNCiMgUmVmZXJlbmNlcyB7LSAjUmVmfQ0KPGRpdiBpZD0icmVmcyI+PC9kaXY+DQoNCjxicj4NCg0KPGZvbnQgY29sb3IgPSAiZ3JleSI+DQpUaGVzZSBub3RlcyBoYXZlIGJlZW4gcHJlcGFyZWQgYnkgUnVwZXJ0IEt1dmVrZS4gVGhlIGNvcHlyaWdodCBmb3IgdGhlIG1hdGVyaWFsIGluIHRoZXNlIG5vdGVzIHJlc2lkZXMgd2l0aCB0aGUgYXV0aG9yIG5hbWVkIGFib3ZlLCB3aXRoIHRoZSBEZXBhcnRtZW50IG9mIE1hdGhlbWF0aWNhbCBhbmQgUGh5c2ljYWwgU2NpZW5jZXMgYW5kIHdpdGggTGEgVHJvYmUgVW5pdmVyc2l0eS4gQ29weXJpZ2h0IGluIHRoaXMgd29yayBpcyB2ZXN0ZWQgaW4gTGEgVHJvYmUgVW5pdmVyc2l0eSBpbmNsdWRpbmcgYWxsIExhIFRyb2JlIFVuaXZlcnNpdHkgYnJhbmRpbmcgYW5kIG5hbWluZy4gVW5sZXNzIG90aGVyd2lzZSBzdGF0ZWQsIG1hdGVyaWFsIHdpdGhpbiB0aGlzIHdvcmsgaXMgbGljZW5zZWQgdW5kZXIgYSBDcmVhdGl2ZSBDb21tb25zIEF0dHJpYnV0aW9uLU5vbiBDb21tZXJjaWFsLU5vbiBEZXJpdmF0aXZlcyBMaWNlbnNlIA0KPGEgaHJlZiA9ICJodHRwczovL2NyZWF0aXZlY29tbW9ucy5vcmcvbGljZW5zZXMvYnktbmMtbmQvNC4wL0NDIiB0YXJnZXQ9Il9ibGFuayI+IEJZLU5DLU5ELiA8L2E+DQo8L2ZvbnQ+