Welcome to the 2nd R workshop of LIFE4138!

Last time, we learned a bit about the basics and structure of the R language, using base R. This time, it’s a little bit different. We’ll think about the different coding philosophies of R coding (there are many many many ways to approach R), and today I’ll guide you through a little bit of what is known as the tidyr philosophy. This is an arguably more advanced way to handle and explore your data, and then we’ll get some hands on experience of a different method of visualising data than last time, by using the ggplot2 package.

Right. Our main aim for today is to give you a little bit more insight into how R works, and how we can approach it. Don’t worry if you still don’t feel like you’ve got the basics absolutely nailed down, this is something that will come in time, with continued practice. Remember you can always look back at last session if you’re struggling, but now you’ve got a grounding of the basics, we can begin to build on it.

Keep in mind the language analogy. If you were learning Spanish, you’d appreciate that you have to keep practicing. You’d also realise pretty rapidly that there are often multiple ways to achieve the same result - the same is true for R. For any outcome, there are multiple ways to get there, and the only way to establish yourself as a proficient R (or Python, or unix, or C++, or any other language) user is to get stuck in and try things out!

R philosophies

There are many many ways that we can approach our coding in R. The original way, baseR, is what we covered in the last session. It was originally built on vector-based data, way back when R was first developed in the early 90s.

Another common approach to using R is a method known as tidyR, which utilises a selection of packages called the tidyverse. Each of the packages within the tidyverse are built with the same underlying ethos, which is the ability to make R code more readable and reproducible. The focus of data handling in the tidyverse is the data.frame object as an entire entity, rather than the vectors within.

There are several other popular R philosophies (for e.g. the data.table philosophy), but we wont delve into those here, we’ll stick to baseR and the tidyverse.

It’s worth knowing that you may well come across some rather strong opinions about these two coding methods, particularly on twitter. With some quick searching of the term “tidyverse”, you can quite quickly find some very heated debates! In the interest of balance, I’ll suggest that there are advantages and disadvantages to both approaches, and their applicability or suitability varies massively with the context of what it is that you’re trying to achieve.

In my opinion, it’s better to be bilingual. Use what works for you, when it works for you. I tend to use a bit of a mishmash of both approaches, but then I’m not overly opinionated on the subject. Do what works. Tidyverse can help to make things a little clearer, but in some cases it’s overkill, and baseR is actually simpler to use.

What is the tidyverse?

The tidyverse is a diverse set of packages and tools, which include popular packages such as dplyr, ggplot2, readr, and purrr. Several of these packages you might use without ever explicitly calling them, because of the nature of the way that the tidyverse is installed. The main ones that we’ll focus on here are dplyr and ggplot2. ggplot2, as the name might suggest, is a package explicitly designed to rethink the way that R handles plotting and data visualisation. We’ll talk more about that later though.

dplyr

dplyr is a really useful package for data wrangling and manipulation. It is invaluable when you are trying to get your data into a useable format, or get a quick summary to help you understand your data a little better. It is particularly useful for filtering your dataset. dplyr allows you to extract information to get your data into shape for downstream analysis.

The principle of data wrangling sounds tedious, but it is a crucially important part of bioinformatics and data science. dplyr is explicitly designed to handle data wrangling in R, and uses a variety of tools and functions to make this easy and accessible. As an analogy, if like me, your desk is messy and chaotic, I often find that tidying it up is a great way to help me to focus my mind on what it is I’m meant to be getting on with, rather than just staring at the mess and playing with the random bit of bluetac I’ve found. The same principle can be applied to your data. If you have a messy dataset with loads of variabes and nonsense in, it can become difficult to focus. Filtering out the stuff you don’t need and tidying it up is a vital first step in any analysis.

To get to grips with this, we’re going to use a really popular dataset that’s integrated into dplyr - the starwars dataset. The likelihood of you being a starwars superfan with an encyclopaedic knowledge of the heights and weights of characters from within the starwars universe is low, so it provides an excellent example of how, by using dplyr, you can get to know and understand a dataset.

Let’s get cracking then.

First things first, we need to install and load the packages we need. There are several ways of doing this, you can either install and load the entire collection of tidyverse packages, or we can just install the ones that we’ll need here:

install.packages("tidyverse")
library(tidyverse)

# or

install.packages("dplyr")
install.packages("ggplot2")
library(dplyr)
library(ggplot2)

Now, we can load the starwars dataset into our R session:

data(starwars)

You might notice that the starwars data displays as a tibble rather than the typical data.frame. A tibble is very similar to a data.frame, but with some bonus features. These are not generally immediately apparent (or particularly important in this context). A tibble is essentially an updated data.frame, and is a very neat tidyverse add-on. At first glance, it’s perhaps obvious that the display is a little nicer than we’re used to. Three main differences that you may notice:

We can use tibbles in the same way that we might use a data.frame - for e.g.:

starwars$name

displays the names of all of the characters in the tibble, just as it would in a data.frame format.

The pipe operator %>%

Before we go any further, it’s important to introduce the pipe operator to you. The pipe operator is a really useful bit of kit for coding in R, and it comes from the package magrittr (although the magrittr library is automatically loaded alongside dplyr so don’t worry about installing it!). The reason the pipe-containing package is called magrittr is because of a famous Belgian surrealist artist, who painted the famous “The Treachery of Images”, seen below:

The text, Ceci n’est pas une pipe is French for “this is not a pipe”.

The pipe function, coded as %>% allows you to “pipe” data from one command or function, directly to another. It works in a very similar way to unix pipes, and allows you to chain together functions in a single code block, allowing for more readable code. You rarely, if ever, have to use pipe operators in your code, but we’ll use them lots here to demonstrate their usefulness. I find pipes make for easier coding, as you can break down complex commands into their consituent parts, rather than wrestling with nested functions with a million brackets. Piping helps you to take a simple command, and build up the complexity of it slowly and logically, iteratively adding bits and sense-checking as you go, before you end up with a complex but readable and easily reproducible bit of code.

Let’s demonstrate the usefulness of a pipe alone, before we combine it with dplyr. Say we wanted to work out the length of the name column in the starwars dataset.

We’ll first demonstrate without the pipe operator, and then with it.

length(starwars$name)
[1] 87
starwars$name %>%
  length()
[1] 87

These two bits of code give us the same result (87!) - here, where the code is relatively straightforward and focussing around only a single function, it is perhaps easier to use the baseR version. When we get to more complicated stuff, the usefulness of pipes will be much more apparent. Essentially what the second bit of code is saying is take the name column from within the starwars dataset, and pop it inside the length function.

Piping and dplyr

The select() function

Piping is great for data manipulation, particularly when combined with the tools in dplyr. Say you want to select a column from a dataset. We can of course do this using our previous method of the $ operator, or, we can use the select() function from `dplyr.

starwars$name

starwars %>%
  select(name)

Here, the select() function perhaps makes it more obvious what the code means, particularly for those of you not used to reading R. You also end up with slightly different outputs, in that the tidyverse solution gives you a tibble output.

But, what if we want to select multiple columns in one go? In baseR, we would have to use our vector-based indexing strategies (the square brackets!), with the c() function. It’s much simplier in the tidyR solution:

# The baseR solution:
starwars[ ,c("name","homeworld")]

# Tidyverse:
starwars %>%
  select(name, homeworld)

Note that we do not use the “” in the select() function, because we’ve already told R that we are looking inside the starwars dataset, by piping it to the function. In the baseR solution, we have to use them otherwise R will look for objects first.

The select() function is a really quick and super powerful way of subsetting data. There are also several helper arguments that you can use inside of select() in order to customise how you want to filter. We will have a look at two of them below, but you can have a look at the rest by using the help function on the select command ?select.

First, we can use the helper function contains() to select only the columns that contain a certain character. Let’s try and use the tidyverse to select only columns which have an underscore (“_“) in:

Notice that this time, we used quotation marks as we are asking R to pattern match a bit of a string. We end up with the above tibble - the columns all contain an underscore in their names!

We can also use the starts_with() helper function. This time, we’ll select all the columns whos name start with a lower-case s:

Here, we end up with columns “skin_colour”, “sex”, “species”, and “starships” - all of which start with a lowercase s - so our code works!

The select() function is versatile - we can also use it to select all columns except name and homeworld, by editing the code we used earlier - recall we used starwars %>% select(name, homeworld) - we can just add - symbols in front of the column names we want to remove…

We can scroll through the resulting tibble and see that all columns except name and homeworld are present!

The filter() function

Okay, now we’ve learned how to select columns - how do we filter out rows? The language differs slightly (and so do the functions! when we’re discussing handling rows and columns:

We always select columns, and filter rows

In baseR, we use indexes to remove things. dplyr functions in a similar way, but is optimised for readability. It still inherently functions on the logical `TRUE/FALSE operators, but in a slightly different way. Let’s have a look at an example to better explain what I’m talking about. If we wanted to select all of the humans in our dataset, in baseR, we’d first have to return a series of logical operators, and then work out where to put them in our indexing system. In tidyR, we simply pipe to the filter() function:

# BaseR solution:
starwars$species == "Human" # This is the logical statement producing bit of code, that needs to go in the [] index system somewhere...
starwars[starwars$species == "Human", ] # Don't forget the comma to tell R we are only looking in rows!

# Tidyverse solution:
starwars %>%
  filter(species == "Human")

The particularly discerning will have noticed that these solutions provide us with two slightly different outcomes - the first gives us a tibble with dimensions 39x14, and the tidyverse solution gives us a tibble which is only 35x14. This difference is to do with the way the select() function handles NA values - it automatically removes them, whereas the baseR solution retains them in the output. In order to get rid of them, we need to add another function to our base solution - the which() function. By wrapping our filtering process in which(), we are telling R to remove any characters for which the species is a NA value:

starwars[which(starwars$species == "Human"), ]

This is where the difference between base and tidyR really becomes apparent - as an R novice, the base solution is difficult to interpret - it’s certainly not immediately obvious what is going on!

Right then - we’ve learned how to filter a dataset to show us only the results which contain a string value in a single column - what if we want to filter for more than one thing?! What if we want all humans which were born on Tatooine? We can just add another argument to our filter() function using a comma:

We can establish fairly quickly using the tidyverse approach that there are 8 humans in the starwars universe who were born on Tatooine.

Combining functions

So, we’ve estblished that you can select columns and filter rows - we can also combine these to create an even more powerful data wrangling tool! We can pull out the variables that we’re interested in, which is a key part of the tidyverse. It’s important here to note however that we are doing this process to better understand our data, not to just remove bits of it that we don’t want!

Now, let’s combine the select() and filter() commands using our pipe operator to give us a dataset that contains only the name, height, and birth year of our human characters:

starwars %>%
  filter(species == "Human") %>%
  select(name, height, birth_year)

Remember the differences in when you need to use quotations!

That was a really straightforward and easy to read way of filtering our dataset to contain only the information that we want. We can perform the same function in BaseR by filling in both sides of our square brackets to give a set of information about exactly what rows and columns we want to keep, but it is much clunkier and less user-friendly:

Using dplyr to get summaries of data

We can use the filter() and select() functions to summarise our data and give us some information about it. Summarising data is an important and useful tool - we might want to count the number of observations in a group, or get a mean value of subsets for example. There are a set of summary functions that are very useful for this - we’ll hav a look at tally(), group_by() and summmarise() below - they allow you to create a new tibble for plotting out output as a summary dataset.

Counting by groups

First, let’s learn to count within groups - perhaps we want to count how many of each species there are in our starwars data. We can do this by using the group_by() and tally() functions - lets see how they work:

starwars %>%
  group_by(species)

When you run the above, you end up with this tibble output. You can see at the top next to the dimensions that we have 38 different species in our dataset, but this isn’t obvious from looking at just the data. Let’s use the tally() function to count them up:

starwars %>%
  group_by(species) %>%
  tally()

Tally literally counts the number of occurrences within a single group, and produces a new tibble with a new count column, “n”. We can see the species names, and see that we’ve got 6 Droids and 3 Gungans, and 1 of everything else in the display. If we want more than 10 rows, we can pipe the code to the print() function, and provide how many rows we want in the argument n = within the function. If you want R to print all the data without having to check how many rows there are, you can ask it to print infinite rows with n = Inf:

starwars %>%
  group_by(species) %>%
  tally() %>%
  print(n = Inf)

Okay - but what if we want R to give us some more complex information? What if we want to know the counts of the genders within the species too? We can iteratively make our code more complicated to achieve these things - in this case, we just need to add another argument to the group_by() function:

starwars %>%
  group_by(species, gender) %>%
  tally() %>%
  print(n = Inf)

This technique is useful for counting bits of information, as well as plotting. Maybe you could envision a situation where you would want to plot the results of genders separately.

We can get more detailed still - we take take height and mass for example, and summarise it. This is similar to what we’ve just done, but slightly more complex - let’s work out the average height for each species. Here, we will introduce the summarise() function into our code pipeline.

starwars %>%
  group_by(species) %>%
  summarise(mean_height = mean(height, na.rm = TRUE))

The summarise command is made up of a series of components here:

  • First, mean_height is creating a new column, called “mean_height”
  • We are then using an = to tell R what to populate that column with
  • After the =, we use the mean() function, with two arguments. The first is telling R what we want to work out the mean of (in this case, height), and the second is the na.rm = TRUE argument - recall from last session that lots of functions can’t cope with missing data, which overrides the rest of the data to give an NA output.

We are essentially asking R to look within each species, and return the mean values for height, removing any NA values as it goes.

Now, lets run the same code but add an extra column for mass - remember our coding style of starting small and adding bits on:

starwars %>%
  group_by(species) %>%
  summarise(mean_height = mean(height, na.rm = TRUE),
            mean_mass = mean(mass, na.rm = TRUE))

You can see here that even though we’ve got an na.rm argument, we still have a NaN in our dataset - this is because for whatever reason, it’s apparently impossible to gain data on the mass of a Chagrian!

As with all things in R, there is more than one tidyverse solution to the above problem, we can also use the vars() argument within the summarise_at() function, this is subtly different to the summarise() function, where we ask R to summarise multiple things at once using the same function:

starwars %>%
  group_by(species) %>%
  summarise_at(vars(height, mass), mean, na.rm = TRUE)

Hopefully, here we’ve demonstrated the use of the pipe mechanism in dplyr to build more complex commands which are readable and easy to follow the logic of. Remember to start simple, and build yourself up to the more complicated stuff, using the new “grammar” that you’ve learned to create complex code structures one step at a time. As a general rule, you shouldn’t use more than ten pipes in a single stretch - if you’re doing this, it’s probably best to resassign your object a new name part-way through the process and start from scratch with the new object.

ggplot2

Time for a bit of a tone change, to shift to a different way of exploring data within the tidyverse. Whist dplyr is great for exploring your data and getting to know it, data visualisation is a different approach.

The “gg” in ggplot2 stands for “the grammar of graphics”. The idea behind this package is that it is built to make you think about data vis in a slightly different way. The way that you use ggplot2 is different to plotting in baseR, and if you’re very familiar with base plots, it can seem difficult to get your head around, although it’s certainly worth persevering. However, if you are new to R, it’s a great way of learning how create graphics.

ggplot is extremely flexible, with lots of additional packages which you can install that integrate into it - the possibilities for graphical customisation are almost infinite. It’s a great way to create publication ready high quality plots quickly and easily - I know that I’ve used ggplot every single time I’ve written and published any science!

There are three key components to any graphics created in ggplot:

This sounds quite complicated and abstract, but in reality it’s a simple concept - think of ggplot like a raster or vector based image processing/design software like photoshop or GIMP, where you iteratively add layers, building up to your final plot. Sound familiar? ggplot (and all the tidyverse packages) work on the same code-building philosophy as dplyr!

Before we begin, if you haven’t done so already, remember to load ggplot2 into your R environment:

install.packages("ggplot2")
library(ggplot2)

Scatter plots

The best way to learn is, as always, to have a go yourself. First, we’ll create a scatter plot! This is similar to the baseR plot(x, y) output, but infinitely more customisable. Let’s plot the relationship between height and mass of characters within the starwars universe.

# If you don't already have the starwars data loaded:
data(starwars)

We’ll build up those layers, starting with telling R what dataset we are using, with the ggplot() function, and slowly build up to a complete plot. Note that whilst the package is called ggplot2, the plotting function is just ggplot()

ggplot(starwars)

This generates us a nice blank grey square. It might not seem like a fabulous start, but we’ve managed to fulfull our first of the three necessary inputs - a dataframe.

Let’s build on this by adding the next part - employing a co-ordinate system for us to map our variables on to. We do this by using the aes() argument within the ggplot() function. Here, aes stands for aesthetics - which doesn’t mean what the plot looks like, but tells ggplot exactly what we want to map onto our graph:

ggplot(starwars, aes(x = height, y = mass))

Yay - we have our co-ordinate system and our axes in place ready to map the data onto. Note that in the aes() argument I have specified which is x and which is y, you do not need to do this. R automatically plots x and then y, so typing ggplot(starwars, aes(height, mass)) would yeild the same result.

Now, let’s add the points to the graph. We do this by adding a new layer to the plot, with one of the set of geom functions in ggplot - geom_point(). To add a new layer, we end the previous line of code with a +, which tells R that we weren’t finished with the last line:

ggplot(starwars, aes(x = height, y = mass))+
  geom_point()

You may see a warning that says R has removed 28 rows with missing values - these are just our NA values from the dataset, so you can ignore that for now.

Now, plotting our scatter plot of height and mass has shown us why we might want to plot our data - it seems that we have a single individual with a mass of around 1400 units, whereas everyone else is below about 200! If you know anything about the starwars universe you might be able to guess who this is… For everyone else - let’s see if we can work it out by going back to our dplyr skills that we’ve just learned, and add in a way to only display information about individuals with a mass of greater than 1000!

starwars %>%
  filter(mass > 1000) %>%
  select(name, species, mass)

If you know anything about starwars, perhaps unsurprisingly, our answer is Jabba the Hutt!

Anyway, back to ggplot.

Histograms

Sometimes, you might want to plot the distribution of your data. This is useful for both identifying outliers, and also for seeing what our data look like. In lots of statistical tests, we make assumptions regarding the distribution of our data - often this assumption is that our data follow a normal distribution. We can create a histogram really quickly using ggplot and see if this is true!

We’ll start by building up our data as we did before, but we’ll go straight to adding the co-ordinate system:

ggplot(starwars, aes(mass))

You might have noticed that we only have one axis here - this is because we have only supplied R with a single aesthetic (the x aesthetic). The reason that we have done this, is because histograms plot a single variable against it’s count - we don’t know what that count is yet, ggplot will figure that out for us. Notice also that we can get an idea of the scale just by looking at the x axis, we can see here that our mass ranges from 0 to way above 1000 (which we now know is a direct result of Jabba the Hut!).

Let’s add the histogram geom.

ggplot(starwars, aes(mass))+
  geom_histogram()

We can see that, as in our scatterplot, most individuals are below about 250 units of mass, with a single individual way over 1000!

There are loads of different geoms available, and lots of online resources to check out - the ggplot manual is particularly useful! For now, let’s just have a look at one more, the boxplot. Boxplots are an incredibly useful way of looking at distributions of data within groups, and working out whether particular groups from a single dataset differ from one another in a certain measurement. Let’s plot a boxplot that tells us about the mass of species from within the starwars universe:

ggplot(starwars, aes(x = species, y = mass))+
  geom_boxplot()

Ah. This isn’t a very nice plot. There’s far too much data, and it’s all very messy. We can’t see any of our species names, or really work out what’s going on at all - lets learn how to fix it…

Combining ggplot2 and dplyr

It’s time to combine our new plotting and data-wrangling skillsets! ggplot is a great way of showing data, but really the power of the tidyverse comes when you integrate the packages with one another. The combined philosophies of dplyr and ggplot2 can create us a much nicer plot. Because dplyr and ggplot2 were built together under the tidyverse umbrella, they integrate really smoothly with one another, and combining them is as simple as piping from a dplyr filter into a ggplot function. You can take a dataset you don’t know, and really get a handle on it by combining these tools. Let’s try here.

We’ll go back to our boxpot and use what we’ve learned with dplyr to create a much nicer boxplot that describes the mass of three different species (rather than all of them at once!). So, which species shall we use? In a dataset that contains lots of observations of a single individual of a species, I think it makes sense to select the three most abundant species. Remember how earlier we used the group_by() and the tally() functions to get R to count the number of individuals in each species?

starwars %>%
  group_by(species) %>%
  tally()

This is great, but it would be better if we could order it and see at a glance which species had the most individuals - we can do this with the arrange() function and the desc argument:

starwars %>%
  group_by(species) %>%
  tally() %>%
  arrange(desc(n))

desc(n) tells the arrange() function that we want to display our data in descending order of the number of individuals in each species.

We can see from the resulting tibble that we have 35 humans, 6 droids, our next highest is NA where species wasn’t recorded, so we’ll ignore that, and then 3 gungans. We’ll plot the mass of each of these three species! To do this, we will need to filter our data using dplyr. We can filter for mulitple matches using the OR operator | thus:

starwars %>%
  filter(species == "Human" | species == "Droid" | species == "Gungan")

When we run the code to sense check ourselves, it’s tricky to see if it’s worked in the way we wanted. We can get round this by piping to select so we can only see the relevant columns:

starwars %>%
  filter(species == "Human" | species == "Droid" | species == "Gungan") %>%
  select(name, species, mass)

Right, we still can’t see if we’ve got any Gungans in here. Let’s get around that by adding a tally to display a count of how many species are in our new selection - don’t forget the group_by() to tell tally what it needs to count!

starwars %>%
  filter(species == "Human" | species == "Droid" | species == "Gungan") %>%
  select(name, species, mass) %>%
  group_by(species) %>%
  tally()

Fabulous. It seems that we’ve got all of our data in check, and our code seems to be doing what we want it to! We can delete those final group_by() and tally() sense checks, and pipe straight to ggplot. We’ll just plot the aesthetics first to check if we’ve got the correct mapping and that only three species appear on the x axis. We don’t need to include the name of the data in our ggplot() command this time - R already knows what data we’re using from our piping

starwars %>%
  filter(species == "Human" | species == "Droid" | species == "Gungan") %>%
  select(name, species, mass) %>%
  ggplot(aes(x = species, y = mass))

Excellent. Now, let’s add the boxplot geom.

starwars %>%
  filter(species == "Human" | species == "Droid" | species == "Gungan") %>%
  select(name, species, mass) %>%
  ggplot(aes(x = species, y = mass))+
  geom_boxplot()

This is a much nicer and more successful boxplot than before! We can see that the mean mass of Droids tends to be much lower than Gungans and Humans, but their mass is more variable.

We have successfully combined ggplot and dplyr in a really useful and powerful way, that will make exploring data much quicker and easier.

Reshaping data

The starwars dataset is already in a nice format for plotting, and we haven’t needed to do anything with it to get it there. This won’t always be the case with real world data. Sorting this out is complicated, and it’s okay if you don’t fully grasp the following the first time round - you’ll get used to it by practicing and having a go with data in your own time.

Now, there are two main types of data:

Sometimes, it’s necessary to swap between these types of data so we can produce the plot that we want. The pivot_data functions allow us to do this. pivot_longer will convert a wide dataset into a long one, by reducing the number of columns and increasing the number of rows, converting columns into new rows, and pivot_wider does the opposite.

We’ll use the iris dataset to demonstrate. As a reminder, the iris dataset contains different measurements (petal length, petal width, sepal length, and sepal width) from multiple individuals of three different species of iris. If we view the data with head we can see that the data is in a wide format, where a row represents a single individual with columns of observations:

head(iris)

We want this dataset to be long, with multiple rows per individual, and only a single column of measurements. Let’s have a go at using pivot_longer() to achieve this. The first thing we have to consider, though, is that iris is a data.frame rather than a tibble. We can fix that by piping to as_tibble() before we reshape. We’ll also assign all of this to a new object, that we’ll call iris2.

iris2 <- iris %>%
  as_tibble() %>%
  pivot_longer(col = 1:4, names_to = "measure")

Let’s dissect that pivot_longer() function:

Note that when we assign things, we don’t automatically get them as an output too - let’s call iris2 and see what it looks like:

Great. The data is now in a long format, rather than wide. We can plot it as a boxplot that tells us information about each species and measurement seperately. Let’s try it with what we currently know of ggplot:

ggplot(iris2, aes(x = Species, y = value))+
  geom_boxplot()

This plot has given us three different boxes on our plot - one per species. We’ve conflated all of the measurements together - this is simply because we haven’t told R that we want them separated! We can do this using a tool known as facet grids, which is essentially a fancy way of saying “splitting plots to pull out different measurements”. Let’s try adding a facet_grid() layer to our plot, and including the argument ~measure within it. Remember that a ~ denotes a relationship between things, so by calling facet_grid(~measure) we are asking R to plot the above, but separating the plots between measurement types. Lets have a go:

ggplot(iris2, aes(x = Species, y = value))+
  geom_boxplot()+
  facet_grid(~measure)

Great, so now we’ve managed to separate our measurements out into individual plots, and by doing this we can learn more about the data! We can see that the setosa species tends to be smaller than the other two generally, with the exception of it’s sepal width. Hopefully now you can see how reshaping your data in this way allows you to explore it differently, and get to know it a little better!

Saving plots

So now we’ve got our data plotted and ready to go, we might want to save it. Helpfully, there is an “export graph” button on the plot window that makes this nice and easy. Although there might be a situation where you’ve used R to automate the production of multiple plots, and you’d like to include a save function within the code itself. You can do this fairly simply with the ggsave() function, which basically does what it says on the tin.

You give the function a name and a file extension in the form of “my_plot.png” for example, and R will save it into your working directory. Try the following

ggsave(my_plot.png)

and you should see that R has saved your plot into your project folder. You can test this by navigating to your file tree in Rstudio and clicking on the newly saved plot to open it. Helpfully, R will interpret the .suffix as a file format, so you can change this at will - perhaps you want a .tiff or a .pdf file - just change the suffix and R will do this for you.

A note on dimensions - R will automatically export a plot with the dimensions of the plot window - you can either change that by manually adjusting these, or by hard-coding some dimensions into the ggsave() function - have a look at the ?ggsave help files to find out more!

Customisation

There are loads of ways to customise your R plots, that we don’t have time to discuss now, but a quick google will show you that there are endless possibilities - go away and have fun!

And finally…

So, we’ve learned a bit about the philosophies and approaches to using R, and looked at more advanced ways of handling data with dplyr. We’ve gained some hands-on experience with using ggplot2 to produce plots.

LS0tCnRpdGxlOiAiTElGRSA0MTM4OiBSIFdvcmtzaG9wIDIiCm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KCldlbGNvbWUgdG8gdGhlIDJuZCBSIHdvcmtzaG9wIG9mIExJRkU0MTM4IQoKTGFzdCB0aW1lLCB3ZSBsZWFybmVkIGEgYml0IGFib3V0IHRoZSBiYXNpY3MgYW5kIHN0cnVjdHVyZSBvZiB0aGUgUiBsYW5ndWFnZSwgdXNpbmcgYmFzZSBSLiBUaGlzIHRpbWUsIGl0J3MgYSBsaXR0bGUgYml0IGRpZmZlcmVudC4gV2UnbGwgdGhpbmsgYWJvdXQgdGhlIGRpZmZlcmVudCBjb2RpbmcgcGhpbG9zb3BoaWVzIG9mIFIgY29kaW5nICh0aGVyZSBhcmUgbWFueSBtYW55IG1hbnkgd2F5cyB0byBhcHByb2FjaCBSKSwgYW5kIHRvZGF5IEknbGwgZ3VpZGUgeW91IHRocm91Z2ggYSBsaXR0bGUgYml0IG9mIHdoYXQgaXMga25vd24gYXMgdGhlIHRpZHlyIHBoaWxvc29waHkuIFRoaXMgaXMgYW4gYXJndWFibHkgbW9yZSBhZHZhbmNlZCB3YXkgdG8gaGFuZGxlIGFuZCBleHBsb3JlIHlvdXIgZGF0YSwgYW5kIHRoZW4gd2UnbGwgZ2V0IHNvbWUgaGFuZHMgb24gZXhwZXJpZW5jZSBvZiBhIGRpZmZlcmVudCBtZXRob2Qgb2YgdmlzdWFsaXNpbmcgZGF0YSB0aGFuIGxhc3QgdGltZSwgYnkgdXNpbmcgdGhlIGBgYGdncGxvdDJgYGAgcGFja2FnZS4gCgpSaWdodC4gT3VyIG1haW4gYWltIGZvciB0b2RheSBpcyB0byBnaXZlIHlvdSBhIGxpdHRsZSBiaXQgbW9yZSBpbnNpZ2h0IGludG8gaG93IFIgd29ya3MsIGFuZCBob3cgd2UgY2FuIGFwcHJvYWNoIGl0LiBEb24ndCB3b3JyeSBpZiB5b3Ugc3RpbGwgZG9uJ3QgZmVlbCBsaWtlIHlvdSd2ZSBnb3QgdGhlIGJhc2ljcyBhYnNvbHV0ZWx5IG5haWxlZCBkb3duLCB0aGlzIGlzIHNvbWV0aGluZyB0aGF0IHdpbGwgY29tZSBpbiB0aW1lLCB3aXRoIGNvbnRpbnVlZCBwcmFjdGljZS4gUmVtZW1iZXIgeW91IGNhbiBhbHdheXMgbG9vayBiYWNrIGF0IGxhc3Qgc2Vzc2lvbiBpZiB5b3UncmUgc3RydWdnbGluZywgYnV0IG5vdyB5b3UndmUgZ290IGEgZ3JvdW5kaW5nIG9mIHRoZSBiYXNpY3MsIHdlIGNhbiBiZWdpbiB0byBidWlsZCBvbiBpdC4gCgpLZWVwIGluIG1pbmQgdGhlIGxhbmd1YWdlIGFuYWxvZ3kuIElmIHlvdSB3ZXJlIGxlYXJuaW5nIFNwYW5pc2gsIHlvdSdkIGFwcHJlY2lhdGUgdGhhdCB5b3UgaGF2ZSB0byBrZWVwIHByYWN0aWNpbmcuIFlvdSdkIGFsc28gcmVhbGlzZSBwcmV0dHkgcmFwaWRseSB0aGF0IHRoZXJlIGFyZSBvZnRlbiBtdWx0aXBsZSB3YXlzIHRvIGFjaGlldmUgdGhlIHNhbWUgcmVzdWx0IC0gdGhlIHNhbWUgaXMgdHJ1ZSBmb3IgUi4gRm9yIGFueSBvdXRjb21lLCB0aGVyZSBhcmUgbXVsdGlwbGUgd2F5cyB0byBnZXQgdGhlcmUsIGFuZCB0aGUgb25seSB3YXkgdG8gZXN0YWJsaXNoIHlvdXJzZWxmIGFzIGEgcHJvZmljaWVudCBSIChvciBQeXRob24sIG9yIHVuaXgsIG9yIEMrKywgb3IgYW55IG90aGVyIGxhbmd1YWdlKSB1c2VyIGlzIHRvIGdldCBzdHVjayBpbiBhbmQgdHJ5IHRoaW5ncyBvdXQhIAoKIyBSIHBoaWxvc29waGllcwoKVGhlcmUgYXJlIG1hbnkgbWFueSB3YXlzIHRoYXQgd2UgY2FuIGFwcHJvYWNoIG91ciBjb2RpbmcgaW4gUi4gVGhlIG9yaWdpbmFsIHdheSwgYmFzZVIsIGlzIHdoYXQgd2UgY292ZXJlZCBpbiB0aGUgbGFzdCBzZXNzaW9uLiBJdCB3YXMgb3JpZ2luYWxseSBidWlsdCBvbiB2ZWN0b3ItYmFzZWQgZGF0YSwgd2F5IGJhY2sgd2hlbiBSIHdhcyBmaXJzdCBkZXZlbG9wZWQgaW4gdGhlIGVhcmx5IDkwcy4KCkFub3RoZXIgY29tbW9uIGFwcHJvYWNoIHRvIHVzaW5nIFIgaXMgYSBtZXRob2Qga25vd24gYXMgdGlkeVIsIHdoaWNoIHV0aWxpc2VzIGEgc2VsZWN0aW9uIG9mIHBhY2thZ2VzIGNhbGxlZCAqKnRoZSB0aWR5dmVyc2UqKi4gRWFjaCBvZiB0aGUgcGFja2FnZXMgd2l0aGluIHRoZSB0aWR5dmVyc2UgYXJlIGJ1aWx0IHdpdGggdGhlIHNhbWUgdW5kZXJseWluZyBldGhvcywgd2hpY2ggaXMgdGhlIGFiaWxpdHkgdG8gbWFrZSBSIGNvZGUgbW9yZSByZWFkYWJsZSBhbmQgcmVwcm9kdWNpYmxlLiBUaGUgZm9jdXMgb2YgZGF0YSBoYW5kbGluZyBpbiB0aGUgdGlkeXZlcnNlIGlzIHRoZSBgYGBkYXRhLmZyYW1lYGBgIG9iamVjdCBhcyBhbiBlbnRpcmUgZW50aXR5LCByYXRoZXIgdGhhbiB0aGUgdmVjdG9ycyB3aXRoaW4uCgpUaGVyZSBhcmUgc2V2ZXJhbCBvdGhlciBwb3B1bGFyIFIgcGhpbG9zb3BoaWVzIChmb3IgZS5nLiB0aGUgYGBgZGF0YS50YWJsZWBgYCBwaGlsb3NvcGh5KSwgYnV0IHdlIHdvbnQgZGVsdmUgaW50byB0aG9zZSBoZXJlLCB3ZSdsbCBzdGljayB0byBiYXNlUiBhbmQgdGhlIHRpZHl2ZXJzZS4KCkl0J3Mgd29ydGgga25vd2luZyB0aGF0IHlvdSBtYXkgd2VsbCBjb21lIGFjcm9zcyBzb21lIHJhdGhlciBzdHJvbmcgb3BpbmlvbnMgYWJvdXQgdGhlc2UgdHdvIGNvZGluZyBtZXRob2RzLCBwYXJ0aWN1bGFybHkgb24gdHdpdHRlci4gV2l0aCBzb21lIHF1aWNrIHNlYXJjaGluZyBvZiB0aGUgdGVybSAidGlkeXZlcnNlIiwgeW91IGNhbiBxdWl0ZSBxdWlja2x5IGZpbmQgc29tZSB2ZXJ5IGhlYXRlZCBkZWJhdGVzISBJbiB0aGUgaW50ZXJlc3Qgb2YgYmFsYW5jZSwgSSdsbCBzdWdnZXN0IHRoYXQgdGhlcmUgYXJlIGFkdmFudGFnZXMgYW5kIGRpc2FkdmFudGFnZXMgdG8gYm90aCBhcHByb2FjaGVzLCBhbmQgdGhlaXIgYXBwbGljYWJpbGl0eSBvciBzdWl0YWJpbGl0eSB2YXJpZXMgbWFzc2l2ZWx5IHdpdGggdGhlIGNvbnRleHQgb2Ygd2hhdCBpdCBpcyB0aGF0IHlvdSdyZSB0cnlpbmcgdG8gYWNoaWV2ZS4gCgpJbiBteSBvcGluaW9uLCBpdCdzIGJldHRlciB0byBiZSBiaWxpbmd1YWwuIFVzZSB3aGF0IHdvcmtzIGZvciB5b3UsIHdoZW4gaXQgd29ya3MgZm9yIHlvdS4gSSB0ZW5kIHRvIHVzZSBhIGJpdCBvZiBhIG1pc2htYXNoIG9mIGJvdGggYXBwcm9hY2hlcywgYnV0IHRoZW4gSSdtIG5vdCBvdmVybHkgb3BpbmlvbmF0ZWQgb24gdGhlIHN1YmplY3QuIERvIHdoYXQgd29ya3MuIFRpZHl2ZXJzZSBjYW4gaGVscCB0byBtYWtlIHRoaW5ncyBhIGxpdHRsZSBjbGVhcmVyLCBidXQgaW4gc29tZSBjYXNlcyBpdCdzIG92ZXJraWxsLCBhbmQgYmFzZVIgaXMgYWN0dWFsbHkgc2ltcGxlciB0byB1c2UuCgojIyBXaGF0IGlzIHRoZSB0aWR5dmVyc2U/CgpUaGUgdGlkeXZlcnNlIGlzIGEgZGl2ZXJzZSBzZXQgb2YgcGFja2FnZXMgYW5kIHRvb2xzLCB3aGljaCBpbmNsdWRlIHBvcHVsYXIgcGFja2FnZXMgc3VjaCBhcyBgYGBkcGx5cmBgYCwgYGBgZ2dwbG90MmBgYCwgYGBgcmVhZHJgYGAsIGFuZCBgYGBwdXJycmBgYC4gU2V2ZXJhbCBvZiB0aGVzZSBwYWNrYWdlcyB5b3UgbWlnaHQgdXNlIHdpdGhvdXQgZXZlciBleHBsaWNpdGx5IGNhbGxpbmcgdGhlbSwgYmVjYXVzZSBvZiB0aGUgbmF0dXJlIG9mIHRoZSB3YXkgdGhhdCB0aGUgdGlkeXZlcnNlIGlzIGluc3RhbGxlZC4gVGhlIG1haW4gb25lcyB0aGF0IHdlJ2xsIGZvY3VzIG9uIGhlcmUgYXJlIGBgYGRwbHlyYGBgIGFuZCBgYGBnZ3Bsb3QyYGBgLiBnZ3Bsb3QyLCBhcyB0aGUgbmFtZSBtaWdodCBzdWdnZXN0LCBpcyBhIHBhY2thZ2UgZXhwbGljaXRseSBkZXNpZ25lZCB0byByZXRoaW5rIHRoZSB3YXkgdGhhdCBSIGhhbmRsZXMgcGxvdHRpbmcgYW5kIGRhdGEgdmlzdWFsaXNhdGlvbi4gV2UnbGwgdGFsayBtb3JlIGFib3V0IHRoYXQgbGF0ZXIgdGhvdWdoLgoKCiMgZHBseXIKCmRwbHlyIGlzIGEgcmVhbGx5IHVzZWZ1bCBwYWNrYWdlIGZvciBkYXRhIHdyYW5nbGluZyBhbmQgbWFuaXB1bGF0aW9uLiBJdCBpcyBpbnZhbHVhYmxlIHdoZW4geW91IGFyZSB0cnlpbmcgdG8gZ2V0IHlvdXIgZGF0YSBpbnRvIGEgdXNlYWJsZSBmb3JtYXQsIG9yIGdldCBhIHF1aWNrIHN1bW1hcnkgdG8gaGVscCB5b3UgdW5kZXJzdGFuZCB5b3VyIGRhdGEgYSBsaXR0bGUgYmV0dGVyLiBJdCBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvciBmaWx0ZXJpbmcgeW91ciBkYXRhc2V0LiBkcGx5ciBhbGxvd3MgeW91IHRvIGV4dHJhY3QgaW5mb3JtYXRpb24gdG8gZ2V0IHlvdXIgZGF0YSBpbnRvIHNoYXBlIGZvciBkb3duc3RyZWFtIGFuYWx5c2lzLgoKVGhlIHByaW5jaXBsZSBvZiBkYXRhIHdyYW5nbGluZyBzb3VuZHMgdGVkaW91cywgYnV0IGl0IGlzIGEgY3J1Y2lhbGx5IGltcG9ydGFudCBwYXJ0IG9mIGJpb2luZm9ybWF0aWNzIGFuZCBkYXRhIHNjaWVuY2UuIGRwbHlyIGlzIGV4cGxpY2l0bHkgZGVzaWduZWQgdG8gaGFuZGxlIGRhdGEgd3JhbmdsaW5nIGluIFIsIGFuZCB1c2VzIGEgdmFyaWV0eSBvZiB0b29scyBhbmQgZnVuY3Rpb25zIHRvIG1ha2UgdGhpcyBlYXN5IGFuZCBhY2Nlc3NpYmxlLiBBcyBhbiBhbmFsb2d5LCBpZiBsaWtlIG1lLCB5b3VyIGRlc2sgaXMgbWVzc3kgYW5kIGNoYW90aWMsIEkgb2Z0ZW4gZmluZCB0aGF0IHRpZHlpbmcgaXQgdXAgaXMgYSBncmVhdCB3YXkgdG8gaGVscCBtZSB0byBmb2N1cyBteSBtaW5kIG9uIHdoYXQgaXQgaXMgSSdtIG1lYW50IHRvIGJlIGdldHRpbmcgb24gd2l0aCwgcmF0aGVyIHRoYW4ganVzdCBzdGFyaW5nIGF0IHRoZSBtZXNzIGFuZCBwbGF5aW5nIHdpdGggdGhlIHJhbmRvbSBiaXQgb2YgYmx1ZXRhYyBJJ3ZlIGZvdW5kLiBUaGUgc2FtZSBwcmluY2lwbGUgY2FuIGJlIGFwcGxpZWQgdG8geW91ciBkYXRhLiBJZiB5b3UgaGF2ZSBhIG1lc3N5IGRhdGFzZXQgd2l0aCBsb2FkcyBvZiB2YXJpYWJlcyBhbmQgbm9uc2Vuc2UgaW4sIGl0IGNhbiBiZWNvbWUgZGlmZmljdWx0IHRvIGZvY3VzLiBGaWx0ZXJpbmcgb3V0IHRoZSBzdHVmZiB5b3UgZG9uJ3QgbmVlZCBhbmQgdGlkeWluZyBpdCB1cCBpcyBhIHZpdGFsIGZpcnN0IHN0ZXAgaW4gYW55IGFuYWx5c2lzLgoKVG8gZ2V0IHRvIGdyaXBzIHdpdGggdGhpcywgd2UncmUgZ29pbmcgdG8gdXNlIGEgcmVhbGx5IHBvcHVsYXIgZGF0YXNldCB0aGF0J3MgaW50ZWdyYXRlZCBpbnRvIGRwbHlyIC0gdGhlIGBgYHN0YXJ3YXJzYGBgIGRhdGFzZXQuIFRoZSBsaWtlbGlob29kIG9mIHlvdSBiZWluZyBhIHN0YXJ3YXJzIHN1cGVyZmFuIHdpdGggYW4gZW5jeWNsb3BhZWRpYyBrbm93bGVkZ2Ugb2YgdGhlIGhlaWdodHMgYW5kIHdlaWdodHMgb2YgY2hhcmFjdGVycyBmcm9tIHdpdGhpbiB0aGUgc3RhcndhcnMgdW5pdmVyc2UgaXMgbG93LCBzbyBpdCBwcm92aWRlcyBhbiBleGNlbGxlbnQgZXhhbXBsZSBvZiBob3csIGJ5IHVzaW5nIGRwbHlyLCB5b3UgY2FuIGdldCB0byBrbm93IGFuZCB1bmRlcnN0YW5kIGEgZGF0YXNldC4KCgpMZXQncyBnZXQgY3JhY2tpbmcgdGhlbi4KCkZpcnN0IHRoaW5ncyBmaXJzdCwgd2UgbmVlZCB0byBpbnN0YWxsIGFuZCBsb2FkIHRoZSBwYWNrYWdlcyB3ZSBuZWVkLiBUaGVyZSBhcmUgc2V2ZXJhbCB3YXlzIG9mIGRvaW5nIHRoaXMsIHlvdSBjYW4gZWl0aGVyIGluc3RhbGwgYW5kIGxvYWQgdGhlIGVudGlyZSBjb2xsZWN0aW9uIG9mIHRpZHl2ZXJzZSBwYWNrYWdlcywgb3Igd2UgY2FuIGp1c3QgaW5zdGFsbCB0aGUgb25lcyB0aGF0IHdlJ2xsIG5lZWQgaGVyZToKCmBgYHtyfQppbnN0YWxsLnBhY2thZ2VzKCJ0aWR5dmVyc2UiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgb3IKCmluc3RhbGwucGFja2FnZXMoImRwbHlyIikKaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2dwbG90MikKYGBgCk5vdywgd2UgY2FuIGxvYWQgdGhlIHN0YXJ3YXJzIGRhdGFzZXQgaW50byBvdXIgUiBzZXNzaW9uOgoKYGBge3J9CmRhdGEoc3RhcndhcnMpCmBgYAoKWW91IG1pZ2h0IG5vdGljZSB0aGF0IHRoZSBzdGFyd2FycyBkYXRhIGRpc3BsYXlzIGFzIGEgYGBgdGliYmxlYGBgIHJhdGhlciB0aGFuIHRoZSB0eXBpY2FsIGBgYGRhdGEuZnJhbWVgYGAuIEEgYGBgdGliYmxlYGBgIGlzIHZlcnkgc2ltaWxhciB0byBhIGBgYGRhdGEuZnJhbWVgYGAsIGJ1dCB3aXRoIHNvbWUgYm9udXMgZmVhdHVyZXMuIFRoZXNlIGFyZSBub3QgZ2VuZXJhbGx5IGltbWVkaWF0ZWx5IGFwcGFyZW50IChvciBwYXJ0aWN1bGFybHkgaW1wb3J0YW50IGluIHRoaXMgY29udGV4dCkuIEEgYGBgdGliYmxlYGBgIGlzIGVzc2VudGlhbGx5IGFuIHVwZGF0ZWQgYGBgZGF0YS5mcmFtZWBgYCwgYW5kIGlzIGEgdmVyeSBuZWF0IHRpZHl2ZXJzZSBhZGQtb24uIEF0IGZpcnN0IGdsYW5jZSwgaXQncyBwZXJoYXBzIG9idmlvdXMgdGhhdCB0aGUgZGlzcGxheSBpcyBhIGxpdHRsZSBuaWNlciB0aGFuIHdlJ3JlIHVzZWQgdG8uIFRocmVlIG1haW4gZGlmZmVyZW5jZXMgdGhhdCB5b3UgbWF5IG5vdGljZToKCiogU2hvd3Mgb25seSB0aGUgZmlyc3QgMTAgbGluZXMgLSBgYGBkYXRhLmZyYW1lYGBgIHByaW50cyB0aGUgZW50aXJlIGNvbnRlbnRzIC0gdGhpcyBlc3NlbnRpYWxseSBjYWxscyBgYGBoZWFkKClgYGAgb24gdGhlIGRhdGEuCiogQWxzbyBvbmx5IGRpc3BsYXlzIHRoZSBmaXJzdCBmZXcgY29sdW1ucyAtIHRoZSByZXN0IG9mIHRoZSBpbmZvcm1hdGlvbiBzdWNoIGFzIHRoZSBjb2x1bW5zIG5vdCBzaG93biBpcyBsaXN0ZWQgYmVsb3cgdGhlIHRpYmJsZSBvdXRwdXQuCiogVGhlcmUgaXMgaW5mb3JtYXRpb24gb24gaG93IHRoaW5ncyBhcmUgY29kZWQuIEhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoZSBuYW1lIGlzIGNvZGVkIGFzIGEgY2hhcmFjdGVyIGlucHV0ICh3aGljaCB3ZSBtaWdodCBleHBlY3QpLiBIZWlnaHQgaXMgYW4gaW50ZWdlciwgd2hpY2ggaXMganVzdCBhIHdob2xlIG51bWJlciB3aXRoIG5vIGRlY2ltYWwgcGxhY2VzIChvciwgYSBkaXNjcmV0ZSBudW1lcmljKS4gTWFzcyBpcyB3aGF0IGlzIGtub3duIGluIFIgYXMgYSBkb3VibGUsIG9yIGBgYGRibGBgYCAtIHRoaXMgZXNzZW50aWFsbHkgbWVhbnMgaXQncyBhIGZsb2F0aW5nIHBvaW50IG51bWJlciB3aXRoIGFueSBwb3RlbnRpYWwgdmFsdWUgZnJvbSAtSW5mIHRvIEluZi4gCgpXZSBjYW4gdXNlIHRpYmJsZXMgaW4gdGhlIHNhbWUgd2F5IHRoYXQgd2UgbWlnaHQgdXNlIGEgYGBgZGF0YS5mcmFtZWBgYCAtIGZvciBlLmcuOgpgYGB7cn0Kc3RhcndhcnMkbmFtZQpgYGAKZGlzcGxheXMgdGhlIG5hbWVzIG9mIGFsbCBvZiB0aGUgY2hhcmFjdGVycyBpbiB0aGUgYGBgdGliYmxlYGBgLCBqdXN0IGFzIGl0IHdvdWxkIGluIGEgYGBgZGF0YS5mcmFtZWBgYCBmb3JtYXQuIAoKIyBUaGUgcGlwZSBvcGVyYXRvciBgYGAlPiVgYGAKCkJlZm9yZSB3ZSBnbyBhbnkgZnVydGhlciwgaXQncyBpbXBvcnRhbnQgdG8gaW50cm9kdWNlIHRoZSBwaXBlIG9wZXJhdG9yIHRvIHlvdS4gVGhlIHBpcGUgb3BlcmF0b3IgaXMgYSByZWFsbHkgdXNlZnVsIGJpdCBvZiBraXQgZm9yIGNvZGluZyBpbiBSLCBhbmQgaXQgY29tZXMgZnJvbSB0aGUgcGFja2FnZSBgYGBtYWdyaXR0cmBgYCAoYWx0aG91Z2ggdGhlIGBgYG1hZ3JpdHRyYGBgIGxpYnJhcnkgaXMgYXV0b21hdGljYWxseSBsb2FkZWQgYWxvbmdzaWRlIGBgYGRwbHlyYGBgIHNvIGRvbid0IHdvcnJ5IGFib3V0IGluc3RhbGxpbmcgaXQhKS4gVGhlIHJlYXNvbiB0aGUgcGlwZS1jb250YWluaW5nIHBhY2thZ2UgaXMgY2FsbGVkIGBgYG1hZ3JpdHRyYGBgIGlzIGJlY2F1c2Ugb2YgYSBmYW1vdXMgQmVsZ2lhbiBzdXJyZWFsaXN0IGFydGlzdCwgd2hvIHBhaW50ZWQgdGhlIGZhbW91cyAiVGhlIFRyZWFjaGVyeSBvZiBJbWFnZXMiLCBzZWVuIGJlbG93OgoKIVtdKHBpcGUuanBlZykKClRoZSB0ZXh0LCAqQ2VjaSBuJ2VzdCBwYXMgdW5lIHBpcGUqIGlzIEZyZW5jaCBmb3IgInRoaXMgaXMgbm90IGEgcGlwZSIuCgpUaGUgcGlwZSBmdW5jdGlvbiwgY29kZWQgYXMgYGBgJT4lYGBgIGFsbG93cyB5b3UgdG8gInBpcGUiIGRhdGEgZnJvbSBvbmUgY29tbWFuZCBvciBmdW5jdGlvbiwgZGlyZWN0bHkgdG8gYW5vdGhlci4gSXQgd29ya3MgaW4gYSB2ZXJ5IHNpbWlsYXIgd2F5IHRvIHVuaXggcGlwZXMsIGFuZCBhbGxvd3MgeW91IHRvIGNoYWluIHRvZ2V0aGVyIGZ1bmN0aW9ucyBpbiBhIHNpbmdsZSBjb2RlIGJsb2NrLCBhbGxvd2luZyBmb3IgbW9yZSByZWFkYWJsZSBjb2RlLiBZb3UgcmFyZWx5LCBpZiBldmVyLCAqKmhhdmUqKiB0byB1c2UgcGlwZSBvcGVyYXRvcnMgaW4geW91ciBjb2RlLCBidXQgd2UnbGwgdXNlIHRoZW0gbG90cyBoZXJlIHRvIGRlbW9uc3RyYXRlIHRoZWlyIHVzZWZ1bG5lc3MuIEkgZmluZCBwaXBlcyBtYWtlIGZvciBlYXNpZXIgY29kaW5nLCBhcyB5b3UgY2FuIGJyZWFrIGRvd24gY29tcGxleCBjb21tYW5kcyBpbnRvIHRoZWlyIGNvbnNpdHVlbnQgcGFydHMsIHJhdGhlciB0aGFuIHdyZXN0bGluZyB3aXRoIG5lc3RlZCBmdW5jdGlvbnMgd2l0aCBhIG1pbGxpb24gYnJhY2tldHMuIFBpcGluZyBoZWxwcyB5b3UgdG8gdGFrZSBhIHNpbXBsZSBjb21tYW5kLCBhbmQgYnVpbGQgdXAgdGhlIGNvbXBsZXhpdHkgb2YgaXQgc2xvd2x5IGFuZCBsb2dpY2FsbHksIGl0ZXJhdGl2ZWx5IGFkZGluZyBiaXRzIGFuZCBzZW5zZS1jaGVja2luZyBhcyB5b3UgZ28sIGJlZm9yZSB5b3UgZW5kIHVwIHdpdGggYSBjb21wbGV4IGJ1dCByZWFkYWJsZSBhbmQgZWFzaWx5IHJlcHJvZHVjaWJsZSBiaXQgb2YgY29kZS4KCkxldCdzIGRlbW9uc3RyYXRlIHRoZSB1c2VmdWxuZXNzIG9mIGEgcGlwZSBhbG9uZSwgYmVmb3JlIHdlIGNvbWJpbmUgaXQgd2l0aCBgYGBkcGx5cmBgYC4gU2F5IHdlIHdhbnRlZCB0byB3b3JrIG91dCB0aGUgbGVuZ3RoIG9mIHRoZSBgYGBuYW1lYGBgIGNvbHVtbiBpbiB0aGUgYGBgc3RhcndhcnNgYGAgZGF0YXNldC4KCldlJ2xsIGZpcnN0IGRlbW9uc3RyYXRlIHdpdGhvdXQgdGhlIHBpcGUgb3BlcmF0b3IsIGFuZCB0aGVuIHdpdGggaXQuCgpgYGB7cn0KbGVuZ3RoKHN0YXJ3YXJzJG5hbWUpCgpzdGFyd2FycyRuYW1lICU+JQogIGxlbmd0aCgpCmBgYAoKVGhlc2UgdHdvIGJpdHMgb2YgY29kZSBnaXZlIHVzIHRoZSBzYW1lIHJlc3VsdCAoODchKSAtIGhlcmUsIHdoZXJlIHRoZSBjb2RlIGlzIHJlbGF0aXZlbHkgc3RyYWlnaHRmb3J3YXJkIGFuZCBmb2N1c3NpbmcgYXJvdW5kIG9ubHkgYSBzaW5nbGUgZnVuY3Rpb24sIGl0IGlzIHBlcmhhcHMgZWFzaWVyIHRvIHVzZSB0aGUgYmFzZVIgdmVyc2lvbi4gV2hlbiB3ZSBnZXQgdG8gbW9yZSBjb21wbGljYXRlZCBzdHVmZiwgdGhlIHVzZWZ1bG5lc3Mgb2YgcGlwZXMgd2lsbCBiZSBtdWNoIG1vcmUgYXBwYXJlbnQuIEVzc2VudGlhbGx5IHdoYXQgdGhlIHNlY29uZCBiaXQgb2YgY29kZSBpcyBzYXlpbmcgaXMgdGFrZSB0aGUgbmFtZSBjb2x1bW4gZnJvbSB3aXRoaW4gdGhlIGBgYHN0YXJ3YXJzYGBgIGRhdGFzZXQsIGFuZCBwb3AgaXQgaW5zaWRlIHRoZSBsZW5ndGggZnVuY3Rpb24uCgojIFBpcGluZyBhbmQgZHBseXIKCiMjIFRoZSBgYGBzZWxlY3QoKWBgYCBmdW5jdGlvbgoKUGlwaW5nIGlzIGdyZWF0IGZvciBkYXRhIG1hbmlwdWxhdGlvbiwgcGFydGljdWxhcmx5IHdoZW4gY29tYmluZWQgd2l0aCB0aGUgdG9vbHMgaW4gYGBgZHBseXJgYGAuIFNheSB5b3Ugd2FudCB0byBzZWxlY3QgYSBjb2x1bW4gZnJvbSBhIGRhdGFzZXQuIFdlIGNhbiBvZiBjb3Vyc2UgZG8gdGhpcyB1c2luZyBvdXIgcHJldmlvdXMgbWV0aG9kIG9mIHRoZSBgYGAkYGBgIG9wZXJhdG9yLCBvciwgd2UgY2FuIHVzZSB0aGUgYGBgc2VsZWN0KClgYGAgZnVuY3Rpb24gZnJvbSBgYGBgZHBseXJgYGAuCgpgYGB7cn0Kc3RhcndhcnMkbmFtZQoKc3RhcndhcnMgJT4lCiAgc2VsZWN0KG5hbWUpCmBgYApIZXJlLCB0aGUgYGBgc2VsZWN0KClgYGAgZnVuY3Rpb24gcGVyaGFwcyBtYWtlcyBpdCBtb3JlIG9idmlvdXMgd2hhdCB0aGUgY29kZSBtZWFucywgcGFydGljdWxhcmx5IGZvciB0aG9zZSBvZiB5b3Ugbm90IHVzZWQgdG8gcmVhZGluZyBSLiBZb3UgYWxzbyBlbmQgdXAgd2l0aCBzbGlnaHRseSBkaWZmZXJlbnQgb3V0cHV0cywgaW4gdGhhdCB0aGUgdGlkeXZlcnNlIHNvbHV0aW9uIGdpdmVzIHlvdSBhIHRpYmJsZSBvdXRwdXQuIAoKQnV0LCB3aGF0IGlmIHdlIHdhbnQgdG8gc2VsZWN0IG11bHRpcGxlIGNvbHVtbnMgaW4gb25lIGdvPyBJbiBiYXNlUiwgd2Ugd291bGQgaGF2ZSB0byB1c2Ugb3VyIHZlY3Rvci1iYXNlZCBpbmRleGluZyBzdHJhdGVnaWVzICh0aGUgc3F1YXJlIGJyYWNrZXRzISksIHdpdGggdGhlIGBgYGMoKWBgYCBmdW5jdGlvbi4gSXQncyBtdWNoIHNpbXBsaWVyIGluIHRoZSB0aWR5UiBzb2x1dGlvbjoKCmBgYHtyfQojIFRoZSBiYXNlUiBzb2x1dGlvbjoKc3RhcndhcnNbICxjKCJuYW1lIiwiaG9tZXdvcmxkIildCgojIFRpZHl2ZXJzZToKc3RhcndhcnMgJT4lCiAgc2VsZWN0KG5hbWUsIGhvbWV3b3JsZCkKYGBgCipOb3RlIHRoYXQgd2UgZG8gbm90IHVzZSB0aGUgIiIgaW4gdGhlIGBgYHNlbGVjdCgpYGBgIGZ1bmN0aW9uLCBiZWNhdXNlIHdlJ3ZlIGFscmVhZHkgdG9sZCBSIHRoYXQgd2UgYXJlIGxvb2tpbmcgaW5zaWRlIHRoZSBzdGFyd2FycyBkYXRhc2V0LCBieSBwaXBpbmcgaXQgdG8gdGhlIGZ1bmN0aW9uLiBJbiB0aGUgYmFzZVIgc29sdXRpb24sIHdlIGhhdmUgdG8gdXNlIHRoZW0gb3RoZXJ3aXNlIFIgd2lsbCBsb29rIGZvciBvYmplY3RzIGZpcnN0LioKClRoZSBgYGBzZWxlY3QoKWBgYCBmdW5jdGlvbiBpcyBhIHJlYWxseSBxdWljayBhbmQgc3VwZXIgcG93ZXJmdWwgd2F5IG9mIHN1YnNldHRpbmcgZGF0YS4gVGhlcmUgYXJlIGFsc28gc2V2ZXJhbCBoZWxwZXIgYXJndW1lbnRzIHRoYXQgeW91IGNhbiB1c2UgaW5zaWRlIG9mIGBgYHNlbGVjdCgpYGBgIGluIG9yZGVyIHRvIGN1c3RvbWlzZSBob3cgeW91IHdhbnQgdG8gZmlsdGVyLiBXZSB3aWxsIGhhdmUgYSBsb29rIGF0IHR3byBvZiB0aGVtIGJlbG93LCBidXQgeW91IGNhbiBoYXZlIGEgbG9vayBhdCB0aGUgcmVzdCBieSB1c2luZyB0aGUgaGVscCBmdW5jdGlvbiBvbiB0aGUgc2VsZWN0IGNvbW1hbmQgYGBgP3NlbGVjdGBgYC4KCkZpcnN0LCB3ZSBjYW4gdXNlIHRoZSBoZWxwZXIgZnVuY3Rpb24gYGBgY29udGFpbnMoKWBgYCB0byBzZWxlY3Qgb25seSB0aGUgY29sdW1ucyB0aGF0IGNvbnRhaW4gYSBjZXJ0YWluIGNoYXJhY3Rlci4gTGV0J3MgdHJ5IGFuZCB1c2UgdGhlIHRpZHl2ZXJzZSB0byBzZWxlY3Qgb25seSBjb2x1bW5zIHdoaWNoIGhhdmUgYW4gdW5kZXJzY29yZSAoIl8iKSBpbjoKCmBgYHtyfQpzdGFyd2FycyAlPiUKICBzZWxlY3QoY29udGFpbnMoIl8iKSkKYGBgCk5vdGljZSB0aGF0IHRoaXMgdGltZSwgd2UgdXNlZCBxdW90YXRpb24gbWFya3MgYXMgd2UgYXJlIGFza2luZyBSIHRvIHBhdHRlcm4gbWF0Y2ggYSBiaXQgb2YgYSBzdHJpbmcuIFdlIGVuZCB1cCB3aXRoIHRoZSBhYm92ZSB0aWJibGUgLSB0aGUgY29sdW1ucyBhbGwgY29udGFpbiBhbiB1bmRlcnNjb3JlIGluIHRoZWlyIG5hbWVzIQoKV2UgY2FuIGFsc28gdXNlIHRoZSBgYGBzdGFydHNfd2l0aCgpYGBgIGhlbHBlciBmdW5jdGlvbi4gVGhpcyB0aW1lLCB3ZSdsbCBzZWxlY3QgYWxsIHRoZSBjb2x1bW5zIHdob3MgbmFtZSBzdGFydCB3aXRoIGEgbG93ZXItY2FzZSBzOgoKYGBge3J9CnN0YXJ3YXJzICU+JQogIHNlbGVjdChzdGFydHNfd2l0aCgicyIpKQpgYGAKCkhlcmUsIHdlIGVuZCB1cCB3aXRoIGNvbHVtbnMgInNraW5fY29sb3VyIiwgInNleCIsICJzcGVjaWVzIiwgYW5kICJzdGFyc2hpcHMiIC0gYWxsIG9mIHdoaWNoIHN0YXJ0IHdpdGggYSBsb3dlcmNhc2UgcyAtIHNvIG91ciBjb2RlIHdvcmtzIQoKVGhlIGBgYHNlbGVjdCgpYGBgIGZ1bmN0aW9uIGlzIHZlcnNhdGlsZSAtIHdlIGNhbiBhbHNvIHVzZSBpdCB0byBzZWxlY3QgYWxsIGNvbHVtbnMgKmV4Y2VwdCogbmFtZSBhbmQgaG9tZXdvcmxkLCBieSBlZGl0aW5nIHRoZSBjb2RlIHdlIHVzZWQgZWFybGllciAtIHJlY2FsbCB3ZSB1c2VkIGBgYHN0YXJ3YXJzICU+JSBzZWxlY3QobmFtZSwgaG9tZXdvcmxkKWBgYCAtIHdlIGNhbiBqdXN0IGFkZCAtIHN5bWJvbHMgaW4gZnJvbnQgb2YgdGhlIGNvbHVtbiBuYW1lcyB3ZSB3YW50IHRvIHJlbW92ZS4uLgoKYGBge3J9CnN0YXJ3YXJzICU+JQogIHNlbGVjdCgtbmFtZSwtaG9tZXdvcmxkKQpgYGAKV2UgY2FuIHNjcm9sbCB0aHJvdWdoIHRoZSByZXN1bHRpbmcgdGliYmxlIGFuZCBzZWUgdGhhdCBhbGwgY29sdW1ucyBleGNlcHQgbmFtZSBhbmQgaG9tZXdvcmxkIGFyZSBwcmVzZW50IQoKCiMjIFRoZSBgYGBmaWx0ZXIoKWBgYCBmdW5jdGlvbgoKT2theSwgbm93IHdlJ3ZlIGxlYXJuZWQgaG93IHRvIHNlbGVjdCBjb2x1bW5zIC0gaG93IGRvIHdlIGZpbHRlciBvdXQgcm93cz8gVGhlIGxhbmd1YWdlIGRpZmZlcnMgc2xpZ2h0bHkgKGFuZCBzbyBkbyB0aGUgZnVuY3Rpb25zISB3aGVuIHdlJ3JlIGRpc2N1c3NpbmcgaGFuZGxpbmcgcm93cyBhbmQgY29sdW1uczoKCioqV2UgYWx3YXlzIHNlbGVjdCBjb2x1bW5zLCBhbmQgZmlsdGVyIHJvd3MqKgoKSW4gYmFzZVIsIHdlIHVzZSBpbmRleGVzIHRvIHJlbW92ZSB0aGluZ3MuIGBgYGRwbHlyYGBgIGZ1bmN0aW9ucyBpbiBhIHNpbWlsYXIgd2F5LCBidXQgaXMgb3B0aW1pc2VkIGZvciByZWFkYWJpbGl0eS4gSXQgc3RpbGwgaW5oZXJlbnRseSBmdW5jdGlvbnMgb24gdGhlIGxvZ2ljYWwgYGBgYFRSVUUvRkFMU0VgYGAgb3BlcmF0b3JzLCBidXQgaW4gYSBzbGlnaHRseSBkaWZmZXJlbnQgd2F5LiBMZXQncyBoYXZlIGEgbG9vayBhdCBhbiBleGFtcGxlIHRvIGJldHRlciBleHBsYWluIHdoYXQgSSdtIHRhbGtpbmcgYWJvdXQuIElmIHdlIHdhbnRlZCB0byBzZWxlY3QgYWxsIG9mIHRoZSBodW1hbnMgaW4gb3VyIGRhdGFzZXQsIGluIGJhc2VSLCB3ZSdkIGZpcnN0IGhhdmUgdG8gcmV0dXJuIGEgc2VyaWVzIG9mIGxvZ2ljYWwgb3BlcmF0b3JzLCBhbmQgdGhlbiB3b3JrIG91dCB3aGVyZSB0byBwdXQgdGhlbSBpbiBvdXIgaW5kZXhpbmcgc3lzdGVtLiBJbiB0aWR5Uiwgd2Ugc2ltcGx5IHBpcGUgdG8gdGhlIGBgYGZpbHRlcigpYGBgIGZ1bmN0aW9uOgpgYGB7cn0KIyBCYXNlUiBzb2x1dGlvbjoKc3RhcndhcnMkc3BlY2llcyA9PSAiSHVtYW4iICMgVGhpcyBpcyB0aGUgbG9naWNhbCBzdGF0ZW1lbnQgcHJvZHVjaW5nIGJpdCBvZiBjb2RlLCB0aGF0IG5lZWRzIHRvIGdvIGluIHRoZSBbXSBpbmRleCBzeXN0ZW0gc29tZXdoZXJlLi4uCnN0YXJ3YXJzW3N0YXJ3YXJzJHNwZWNpZXMgPT0gIkh1bWFuIiwgXSAjIERvbid0IGZvcmdldCB0aGUgY29tbWEgdG8gdGVsbCBSIHdlIGFyZSBvbmx5IGxvb2tpbmcgaW4gcm93cyEKCiMgVGlkeXZlcnNlIHNvbHV0aW9uOgpzdGFyd2FycyAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiSHVtYW4iKQpgYGAKVGhlIHBhcnRpY3VsYXJseSBkaXNjZXJuaW5nIHdpbGwgaGF2ZSBub3RpY2VkIHRoYXQgdGhlc2Ugc29sdXRpb25zIHByb3ZpZGUgdXMgd2l0aCB0d28gc2xpZ2h0bHkgZGlmZmVyZW50IG91dGNvbWVzIC0gdGhlIGZpcnN0IGdpdmVzIHVzIGEgdGliYmxlIHdpdGggZGltZW5zaW9ucyAzOXgxNCwgYW5kIHRoZSB0aWR5dmVyc2Ugc29sdXRpb24gZ2l2ZXMgdXMgYSB0aWJibGUgd2hpY2ggaXMgb25seSAzNXgxNC4gVGhpcyBkaWZmZXJlbmNlIGlzIHRvIGRvIHdpdGggdGhlIHdheSB0aGUgYGBgc2VsZWN0KClgYGAgZnVuY3Rpb24gaGFuZGxlcyBgYGBOQWBgYCB2YWx1ZXMgLSBpdCBhdXRvbWF0aWNhbGx5IHJlbW92ZXMgdGhlbSwgd2hlcmVhcyB0aGUgYmFzZVIgc29sdXRpb24gcmV0YWlucyB0aGVtIGluIHRoZSBvdXRwdXQuIEluIG9yZGVyIHRvIGdldCByaWQgb2YgdGhlbSwgd2UgbmVlZCB0byBhZGQgYW5vdGhlciBmdW5jdGlvbiB0byBvdXIgYmFzZSBzb2x1dGlvbiAtIHRoZSBgYGB3aGljaCgpYGBgIGZ1bmN0aW9uLiBCeSB3cmFwcGluZyBvdXIgZmlsdGVyaW5nIHByb2Nlc3MgaW4gYGBgd2hpY2goKWBgYCwgd2UgYXJlIHRlbGxpbmcgUiB0byByZW1vdmUgYW55IGNoYXJhY3RlcnMgZm9yIHdoaWNoIHRoZSBzcGVjaWVzIGlzIGEgYGBgTkFgYGAgdmFsdWU6CgpgYGB7cn0Kc3RhcndhcnNbd2hpY2goc3RhcndhcnMkc3BlY2llcyA9PSAiSHVtYW4iKSwgXQpgYGAKVGhpcyBpcyB3aGVyZSB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGJhc2UgYW5kIHRpZHlSIHJlYWxseSBiZWNvbWVzIGFwcGFyZW50IC0gYXMgYW4gUiBub3ZpY2UsIHRoZSBiYXNlIHNvbHV0aW9uIGlzIGRpZmZpY3VsdCB0byBpbnRlcnByZXQgLSBpdCdzIGNlcnRhaW5seSBub3QgaW1tZWRpYXRlbHkgb2J2aW91cyB3aGF0IGlzIGdvaW5nIG9uIQoKUmlnaHQgdGhlbiAtIHdlJ3ZlIGxlYXJuZWQgaG93IHRvIGZpbHRlciBhIGRhdGFzZXQgdG8gc2hvdyB1cyBvbmx5IHRoZSByZXN1bHRzIHdoaWNoIGNvbnRhaW4gYSBzdHJpbmcgdmFsdWUgaW4gYSBzaW5nbGUgY29sdW1uIC0gd2hhdCBpZiB3ZSB3YW50IHRvIGZpbHRlciBmb3IgbW9yZSB0aGFuIG9uZSB0aGluZz8hIFdoYXQgaWYgd2Ugd2FudCBhbGwgaHVtYW5zIHdoaWNoIHdlcmUgYm9ybiBvbiBUYXRvb2luZT8gV2UgY2FuIGp1c3QgYWRkIGFub3RoZXIgYXJndW1lbnQgdG8gb3VyIGBgYGZpbHRlcigpYGBgIGZ1bmN0aW9uIHVzaW5nIGEgY29tbWE6CgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkh1bWFuIiwKICAgICAgICAgaG9tZXdvcmxkID09ICJUYXRvb2luZSIpCmBgYAoKV2UgY2FuIGVzdGFibGlzaCBmYWlybHkgcXVpY2tseSB1c2luZyB0aGUgdGlkeXZlcnNlIGFwcHJvYWNoIHRoYXQgdGhlcmUgYXJlIDggaHVtYW5zIGluIHRoZSBzdGFyd2FycyB1bml2ZXJzZSB3aG8gd2VyZSBib3JuIG9uIFRhdG9vaW5lLgoKIyMgQ29tYmluaW5nIGZ1bmN0aW9ucwoKU28sIHdlJ3ZlIGVzdGJsaXNoZWQgdGhhdCB5b3UgY2FuIHNlbGVjdCBjb2x1bW5zIGFuZCBmaWx0ZXIgcm93cyAtIHdlIGNhbiBhbHNvIGNvbWJpbmUgdGhlc2UgdG8gY3JlYXRlIGFuIGV2ZW4gbW9yZSBwb3dlcmZ1bCBkYXRhIHdyYW5nbGluZyB0b29sISBXZSBjYW4gcHVsbCBvdXQgdGhlIHZhcmlhYmxlcyB0aGF0IHdlJ3JlIGludGVyZXN0ZWQgaW4sIHdoaWNoIGlzIGEga2V5IHBhcnQgb2YgdGhlIHRpZHl2ZXJzZS4gSXQncyBpbXBvcnRhbnQgaGVyZSB0byBub3RlIGhvd2V2ZXIgdGhhdCB3ZSBhcmUgZG9pbmcgdGhpcyBwcm9jZXNzIHRvIGJldHRlciB1bmRlcnN0YW5kIG91ciBkYXRhLCBub3QgdG8ganVzdCByZW1vdmUgYml0cyBvZiBpdCB0aGF0IHdlIGRvbid0IHdhbnQhCgpOb3csIGxldCdzIGNvbWJpbmUgdGhlIGBgYHNlbGVjdCgpYGBgIGFuZCBgYGBmaWx0ZXIoKWBgYCBjb21tYW5kcyB1c2luZyBvdXIgcGlwZSBvcGVyYXRvciB0byBnaXZlIHVzIGEgZGF0YXNldCB0aGF0IGNvbnRhaW5zIG9ubHkgdGhlIG5hbWUsIGhlaWdodCwgYW5kIGJpcnRoIHllYXIgb2Ygb3VyIGh1bWFuIGNoYXJhY3RlcnM6CgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkh1bWFuIikgJT4lCiAgc2VsZWN0KG5hbWUsIGhlaWdodCwgYmlydGhfeWVhcikKYGBgCgpSZW1lbWJlciB0aGUgZGlmZmVyZW5jZXMgaW4gd2hlbiB5b3UgbmVlZCB0byB1c2UgcXVvdGF0aW9ucyEKClRoYXQgd2FzIGEgcmVhbGx5IHN0cmFpZ2h0Zm9yd2FyZCBhbmQgZWFzeSB0byByZWFkIHdheSBvZiBmaWx0ZXJpbmcgb3VyIGRhdGFzZXQgdG8gY29udGFpbiBvbmx5IHRoZSBpbmZvcm1hdGlvbiB0aGF0IHdlIHdhbnQuIFdlIGNhbiBwZXJmb3JtIHRoZSBzYW1lIGZ1bmN0aW9uIGluIEJhc2VSIGJ5IGZpbGxpbmcgaW4gYm90aCBzaWRlcyBvZiBvdXIgc3F1YXJlIGJyYWNrZXRzIHRvIGdpdmUgYSBzZXQgb2YgaW5mb3JtYXRpb24gYWJvdXQgZXhhY3RseSB3aGF0IHJvd3MgYW5kIGNvbHVtbnMgd2Ugd2FudCB0byBrZWVwLCBidXQgaXQgaXMgbXVjaCBjbHVua2llciBhbmQgbGVzcyB1c2VyLWZyaWVuZGx5OgpgYGB7cn0KIyBEb24ndCBmb3JnZXQgdG8gaW5jbHVkZSB0aGUgd2hpY2ggZnVuY3Rpb24gdG8gcmVtb3ZlIE5BcyEKc3RhcndhcnNbd2hpY2goc3RhcndhcnMkc3BlY2llcyA9PSAiSHVtYW4iKSwKICAgICAgICAgYygibmFtZSIsICJoZWlnaHQiLCAiYmlydGhfeWVhciIpXQpgYGAKCiMgVXNpbmcgYGBgZHBseXJgYGAgdG8gZ2V0IHN1bW1hcmllcyBvZiBkYXRhCgpXZSBjYW4gdXNlIHRoZSBgYGBmaWx0ZXIoKWBgYCBhbmQgYGBgc2VsZWN0KClgYGAgZnVuY3Rpb25zIHRvIHN1bW1hcmlzZSBvdXIgZGF0YSBhbmQgZ2l2ZSB1cyBzb21lIGluZm9ybWF0aW9uIGFib3V0IGl0LiBTdW1tYXJpc2luZyBkYXRhIGlzIGFuIGltcG9ydGFudCBhbmQgdXNlZnVsIHRvb2wgLSB3ZSBtaWdodCB3YW50IHRvIGNvdW50IHRoZSBudW1iZXIgb2Ygb2JzZXJ2YXRpb25zIGluIGEgZ3JvdXAsIG9yIGdldCBhIG1lYW4gdmFsdWUgb2Ygc3Vic2V0cyBmb3IgZXhhbXBsZS4gVGhlcmUgYXJlIGEgc2V0IG9mIHN1bW1hcnkgZnVuY3Rpb25zIHRoYXQgYXJlIHZlcnkgdXNlZnVsIGZvciB0aGlzIC0gd2UnbGwgaGF2IGEgbG9vayBhdCBgYGB0YWxseSgpYGBgLCBgYGBncm91cF9ieSgpYGBgIGFuZCBgYGBzdW1tbWFyaXNlKClgYGAgYmVsb3cgLSB0aGV5IGFsbG93IHlvdSB0byBjcmVhdGUgYSBuZXcgdGliYmxlIGZvciBwbG90dGluZyBvdXQgb3V0cHV0IGFzIGEgc3VtbWFyeSBkYXRhc2V0LiAKCiMjIENvdW50aW5nIGJ5IGdyb3VwcwoKRmlyc3QsIGxldCdzIGxlYXJuIHRvIGNvdW50IHdpdGhpbiBncm91cHMgLSBwZXJoYXBzIHdlIHdhbnQgdG8gY291bnQgaG93IG1hbnkgb2YgZWFjaCBzcGVjaWVzIHRoZXJlIGFyZSBpbiBvdXIgc3RhcndhcnMgZGF0YS4gV2UgY2FuIGRvIHRoaXMgYnkgdXNpbmcgdGhlIGBgYGdyb3VwX2J5KClgYGAgYW5kIGBgYHRhbGx5KClgYGAgZnVuY3Rpb25zIC0gbGV0cyBzZWUgaG93IHRoZXkgd29yazoKCmBgYHtyfQpzdGFyd2FycyAlPiUKICBncm91cF9ieShzcGVjaWVzKQpgYGAKV2hlbiB5b3UgcnVuIHRoZSBhYm92ZSwgeW91IGVuZCB1cCB3aXRoIHRoaXMgdGliYmxlIG91dHB1dC4gWW91IGNhbiBzZWUgYXQgdGhlIHRvcCBuZXh0IHRvIHRoZSBkaW1lbnNpb25zIHRoYXQgd2UgaGF2ZSAzOCBkaWZmZXJlbnQgc3BlY2llcyBpbiBvdXIgZGF0YXNldCwgYnV0IHRoaXMgaXNuJ3Qgb2J2aW91cyBmcm9tIGxvb2tpbmcgYXQganVzdCB0aGUgZGF0YS4gTGV0J3MgdXNlIHRoZSBgYGB0YWxseSgpYGBgIGZ1bmN0aW9uIHRvIGNvdW50IHRoZW0gdXA6CgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lCiAgdGFsbHkoKQpgYGAKClRhbGx5IGxpdGVyYWxseSBjb3VudHMgdGhlIG51bWJlciBvZiBvY2N1cnJlbmNlcyB3aXRoaW4gYSBzaW5nbGUgZ3JvdXAsIGFuZCBwcm9kdWNlcyBhIG5ldyB0aWJibGUgd2l0aCBhIG5ldyBjb3VudCBjb2x1bW4sICJuIi4gV2UgY2FuIHNlZSB0aGUgc3BlY2llcyBuYW1lcywgYW5kIHNlZSB0aGF0IHdlJ3ZlIGdvdCA2IERyb2lkcyBhbmQgMyBHdW5nYW5zLCBhbmQgMSBvZiBldmVyeXRoaW5nIGVsc2UgaW4gdGhlIGRpc3BsYXkuIElmIHdlIHdhbnQgbW9yZSB0aGFuIDEwIHJvd3MsIHdlIGNhbiBwaXBlIHRoZSBjb2RlIHRvIHRoZSBgYGBwcmludCgpYGBgIGZ1bmN0aW9uLCBhbmQgcHJvdmlkZSBob3cgbWFueSByb3dzIHdlIHdhbnQgaW4gdGhlIGFyZ3VtZW50IGBgYG4gPSBgYGAgd2l0aGluIHRoZSBmdW5jdGlvbi4gSWYgeW91IHdhbnQgUiB0byBwcmludCBhbGwgdGhlIGRhdGEgd2l0aG91dCBoYXZpbmcgdG8gY2hlY2sgaG93IG1hbnkgcm93cyB0aGVyZSBhcmUsIHlvdSBjYW4gYXNrIGl0IHRvIHByaW50IGluZmluaXRlIHJvd3Mgd2l0aCBgYGBuID0gSW5mYGBgOgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lCiAgdGFsbHkoKSAlPiUKICBwcmludChuID0gSW5mKQpgYGAKCk9rYXkgLSBidXQgd2hhdCBpZiB3ZSB3YW50IFIgdG8gZ2l2ZSB1cyBzb21lIG1vcmUgY29tcGxleCBpbmZvcm1hdGlvbj8gV2hhdCBpZiB3ZSB3YW50IHRvIGtub3cgdGhlIGNvdW50cyBvZiB0aGUgZ2VuZGVycyB3aXRoaW4gdGhlIHNwZWNpZXMgdG9vPyBXZSBjYW4gaXRlcmF0aXZlbHkgbWFrZSBvdXIgY29kZSBtb3JlIGNvbXBsaWNhdGVkIHRvIGFjaGlldmUgdGhlc2UgdGhpbmdzIC0gaW4gdGhpcyBjYXNlLCB3ZSBqdXN0IG5lZWQgdG8gYWRkIGFub3RoZXIgYXJndW1lbnQgdG8gdGhlIGBgYGdyb3VwX2J5KClgYGAgZnVuY3Rpb246CgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcywgZ2VuZGVyKSAlPiUKICB0YWxseSgpICU+JQogIHByaW50KG4gPSBJbmYpCmBgYApUaGlzIHRlY2huaXF1ZSBpcyB1c2VmdWwgZm9yIGNvdW50aW5nIGJpdHMgb2YgaW5mb3JtYXRpb24sIGFzIHdlbGwgYXMgcGxvdHRpbmcuIE1heWJlIHlvdSBjb3VsZCBlbnZpc2lvbiBhIHNpdHVhdGlvbiB3aGVyZSB5b3Ugd291bGQgd2FudCB0byBwbG90IHRoZSByZXN1bHRzIG9mIGdlbmRlcnMgc2VwYXJhdGVseS4KCldlIGNhbiBnZXQgbW9yZSBkZXRhaWxlZCBzdGlsbCAtIHdlIHRha2UgdGFrZSBoZWlnaHQgYW5kIG1hc3MgZm9yIGV4YW1wbGUsIGFuZCBzdW1tYXJpc2UgaXQuIFRoaXMgaXMgc2ltaWxhciB0byB3aGF0IHdlJ3ZlIGp1c3QgZG9uZSwgYnV0IHNsaWdodGx5IG1vcmUgY29tcGxleCAtIGxldCdzIHdvcmsgb3V0IHRoZSBhdmVyYWdlIGhlaWdodCBmb3IgZWFjaCBzcGVjaWVzLiBIZXJlLCB3ZSB3aWxsIGludHJvZHVjZSB0aGUgYGBgc3VtbWFyaXNlKClgYGAgZnVuY3Rpb24gaW50byBvdXIgY29kZSBwaXBlbGluZS4KCmBgYHtyfQpzdGFyd2FycyAlPiUKICBncm91cF9ieShzcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9oZWlnaHQgPSBtZWFuKGhlaWdodCwgbmEucm0gPSBUUlVFKSkKYGBgClRoZSBgYGBzdW1tYXJpc2VgYGAgY29tbWFuZCBpcyBtYWRlIHVwIG9mIGEgc2VyaWVzIG9mIGNvbXBvbmVudHMgaGVyZToKCiogRmlyc3QsIGBgYG1lYW5faGVpZ2h0YGBgIGlzIGNyZWF0aW5nIGEgbmV3IGNvbHVtbiwgY2FsbGVkICJtZWFuX2hlaWdodCIKKiBXZSBhcmUgdGhlbiB1c2luZyBhbiBgYGA9YGBgIHRvIHRlbGwgUiB3aGF0IHRvIHBvcHVsYXRlIHRoYXQgY29sdW1uIHdpdGgKKiBBZnRlciB0aGUgYGBgPWBgYCwgd2UgdXNlIHRoZSBgYGBtZWFuKClgYGAgZnVuY3Rpb24sIHdpdGggdHdvIGFyZ3VtZW50cy4gVGhlIGZpcnN0IGlzIHRlbGxpbmcgUiB3aGF0IHdlIHdhbnQgdG8gd29yayBvdXQgdGhlIG1lYW4gb2YgKGluIHRoaXMgY2FzZSwgaGVpZ2h0KSwgYW5kIHRoZSBzZWNvbmQgaXMgdGhlIGBgYG5hLnJtID0gVFJVRWBgYCBhcmd1bWVudCAtIHJlY2FsbCBmcm9tIGxhc3Qgc2Vzc2lvbiB0aGF0IGxvdHMgb2YgZnVuY3Rpb25zIGNhbid0IGNvcGUgd2l0aCBtaXNzaW5nIGRhdGEsIHdoaWNoIG92ZXJyaWRlcyB0aGUgcmVzdCBvZiB0aGUgZGF0YSB0byBnaXZlIGFuIGBgYE5BYGBgIG91dHB1dC4KCldlIGFyZSBlc3NlbnRpYWxseSBhc2tpbmcgUiB0byBsb29rIHdpdGhpbiBlYWNoIHNwZWNpZXMsIGFuZCByZXR1cm4gdGhlIG1lYW4gdmFsdWVzIGZvciBoZWlnaHQsIHJlbW92aW5nIGFueSBgYGBOQWBgYCB2YWx1ZXMgYXMgaXQgZ29lcy4gCgpOb3csIGxldHMgcnVuIHRoZSBzYW1lIGNvZGUgYnV0IGFkZCBhbiBleHRyYSBjb2x1bW4gZm9yIG1hc3MgLSByZW1lbWJlciBvdXIgY29kaW5nIHN0eWxlIG9mIHN0YXJ0aW5nIHNtYWxsIGFuZCBhZGRpbmcgYml0cyBvbjoKCmBgYHtyfQpzdGFyd2FycyAlPiUKICBncm91cF9ieShzcGVjaWVzKSAlPiUKICBzdW1tYXJpc2UobWVhbl9oZWlnaHQgPSBtZWFuKGhlaWdodCwgbmEucm0gPSBUUlVFKSwKICAgICAgICAgICAgbWVhbl9tYXNzID0gbWVhbihtYXNzLCBuYS5ybSA9IFRSVUUpKQpgYGAKWW91IGNhbiBzZWUgaGVyZSB0aGF0IGV2ZW4gdGhvdWdoIHdlJ3ZlIGdvdCBhbiBgYGBuYS5ybWBgYCBhcmd1bWVudCwgd2Ugc3RpbGwgaGF2ZSBhIGBgYE5hTmBgYCBpbiBvdXIgZGF0YXNldCAtIHRoaXMgaXMgYmVjYXVzZSBmb3Igd2hhdGV2ZXIgcmVhc29uLCBpdCdzIGFwcGFyZW50bHkgaW1wb3NzaWJsZSB0byBnYWluIGRhdGEgb24gdGhlIG1hc3Mgb2YgYSBDaGFncmlhbiEKCkFzIHdpdGggYWxsIHRoaW5ncyBpbiBSLCB0aGVyZSBpcyBtb3JlIHRoYW4gb25lIHRpZHl2ZXJzZSBzb2x1dGlvbiB0byB0aGUgYWJvdmUgcHJvYmxlbSwgd2UgY2FuIGFsc28gdXNlIHRoZSBgYGB2YXJzKClgYGAgYXJndW1lbnQgd2l0aGluIHRoZSBgYGBzdW1tYXJpc2VfYXQoKWBgYCBmdW5jdGlvbiwgdGhpcyBpcyBzdWJ0bHkgZGlmZmVyZW50IHRvIHRoZSBgYGBzdW1tYXJpc2UoKWBgYCBmdW5jdGlvbiwgd2hlcmUgd2UgYXNrIFIgdG8gc3VtbWFyaXNlIG11bHRpcGxlIHRoaW5ncyBhdCBvbmNlIHVzaW5nIHRoZSBzYW1lIGZ1bmN0aW9uOgoKYGBge3J9CnN0YXJ3YXJzICU+JQogIGdyb3VwX2J5KHNwZWNpZXMpICU+JQogIHN1bW1hcmlzZV9hdCh2YXJzKGhlaWdodCwgbWFzcyksIG1lYW4sIG5hLnJtID0gVFJVRSkKYGBgCgpIb3BlZnVsbHksIGhlcmUgd2UndmUgZGVtb25zdHJhdGVkIHRoZSB1c2Ugb2YgdGhlIHBpcGUgbWVjaGFuaXNtIGluIGBgYGRwbHlyYGBgIHRvIGJ1aWxkIG1vcmUgY29tcGxleCBjb21tYW5kcyB3aGljaCBhcmUgcmVhZGFibGUgYW5kIGVhc3kgdG8gZm9sbG93IHRoZSBsb2dpYyBvZi4gUmVtZW1iZXIgdG8gc3RhcnQgc2ltcGxlLCBhbmQgYnVpbGQgeW91cnNlbGYgdXAgdG8gdGhlIG1vcmUgY29tcGxpY2F0ZWQgc3R1ZmYsIHVzaW5nIHRoZSBuZXcgImdyYW1tYXIiIHRoYXQgeW91J3ZlIGxlYXJuZWQgdG8gY3JlYXRlIGNvbXBsZXggY29kZSBzdHJ1Y3R1cmVzIG9uZSBzdGVwIGF0IGEgdGltZS4gQXMgYSBnZW5lcmFsIHJ1bGUsIHlvdSBzaG91bGRuJ3QgdXNlIG1vcmUgdGhhbiB0ZW4gcGlwZXMgaW4gYSBzaW5nbGUgc3RyZXRjaCAtIGlmIHlvdSdyZSBkb2luZyB0aGlzLCBpdCdzIHByb2JhYmx5IGJlc3QgdG8gcmVzYXNzaWduIHlvdXIgb2JqZWN0IGEgbmV3IG5hbWUgcGFydC13YXkgdGhyb3VnaCB0aGUgcHJvY2VzcyBhbmQgc3RhcnQgZnJvbSBzY3JhdGNoIHdpdGggdGhlIG5ldyBvYmplY3QuIAoKCiMgZ2dwbG90MgoKVGltZSBmb3IgYSBiaXQgb2YgYSB0b25lIGNoYW5nZSwgdG8gc2hpZnQgdG8gYSBkaWZmZXJlbnQgd2F5IG9mIGV4cGxvcmluZyBkYXRhIHdpdGhpbiB0aGUgdGlkeXZlcnNlLiBXaGlzdCBgYGBkcGx5cmBgYCBpcyBncmVhdCBmb3IgZXhwbG9yaW5nIHlvdXIgZGF0YSBhbmQgZ2V0dGluZyB0byBrbm93IGl0LCBkYXRhIHZpc3VhbGlzYXRpb24gaXMgYSBkaWZmZXJlbnQgYXBwcm9hY2guCgpUaGUgImdnIiBpbiBgYGBnZ3Bsb3QyYGBgIHN0YW5kcyBmb3IgInRoZSBncmFtbWFyIG9mIGdyYXBoaWNzIi4gVGhlIGlkZWEgYmVoaW5kIHRoaXMgcGFja2FnZSBpcyB0aGF0IGl0IGlzIGJ1aWx0IHRvIG1ha2UgeW91IHRoaW5rIGFib3V0IGRhdGEgdmlzIGluIGEgc2xpZ2h0bHkgZGlmZmVyZW50IHdheS4gVGhlIHdheSB0aGF0IHlvdSB1c2UgZ2dwbG90MiBpcyBkaWZmZXJlbnQgdG8gcGxvdHRpbmcgaW4gYmFzZVIsIGFuZCBpZiB5b3UncmUgdmVyeSBmYW1pbGlhciB3aXRoIGJhc2UgcGxvdHMsIGl0IGNhbiBzZWVtIGRpZmZpY3VsdCB0byBnZXQgeW91ciBoZWFkIGFyb3VuZCwgYWx0aG91Z2ggaXQncyBjZXJ0YWlubHkgd29ydGggcGVyc2V2ZXJpbmcuIEhvd2V2ZXIsIGlmIHlvdSBhcmUgbmV3IHRvIFIsIGl0J3MgYSBncmVhdCB3YXkgb2YgbGVhcm5pbmcgaG93IGNyZWF0ZSBncmFwaGljcy4KCmdncGxvdCBpcyBleHRyZW1lbHkgZmxleGlibGUsIHdpdGggbG90cyBvZiBhZGRpdGlvbmFsIHBhY2thZ2VzIHdoaWNoIHlvdSBjYW4gaW5zdGFsbCB0aGF0IGludGVncmF0ZSBpbnRvIGl0IC0gdGhlIHBvc3NpYmlsaXRpZXMgZm9yIGdyYXBoaWNhbCBjdXN0b21pc2F0aW9uIGFyZSBhbG1vc3QgaW5maW5pdGUuIEl0J3MgYSBncmVhdCB3YXkgdG8gY3JlYXRlIHB1YmxpY2F0aW9uIHJlYWR5IGhpZ2ggcXVhbGl0eSBwbG90cyBxdWlja2x5IGFuZCBlYXNpbHkgLSBJIGtub3cgdGhhdCBJJ3ZlIHVzZWQgZ2dwbG90IGV2ZXJ5IHNpbmdsZSB0aW1lIEkndmUgd3JpdHRlbiBhbmQgcHVibGlzaGVkIGFueSBzY2llbmNlIQoKVGhlcmUgYXJlIHRocmVlIGtleSBjb21wb25lbnRzIHRvIGFueSBncmFwaGljcyBjcmVhdGVkIGluIGdncGxvdDoKCiogQSBkYXRhLmZyYW1lIG9yIHRpYmJsZSAod2hlcmUgZ2dwbG90IGlzIGdldHRpbmcgdGhlIGRhdGEgdG8gcGxvdCBmcm9tKQoqIEEgY28tb3JkaW5hdGUgYmFzZWQgc3lzdGVtIG9mIG1hcHBhYmxlIHZhcmlhYmxlcyAtIGkuZS4gYW4geCBhbmQgeSBheGlzLCBhbGxvd2luZyBnZ3Bsb3QyIGNhbiBtYXAgdmFyaWFibGVzIHRvIGl0CiogTGF5ZXJzIG9mIGdlb21ldHJ5LCBvciBgYGBnZW9tYGBgLCB3aGljaCBwbG90IHlvdXIgZGF0YS4KClRoaXMgc291bmRzIHF1aXRlIGNvbXBsaWNhdGVkIGFuZCBhYnN0cmFjdCwgYnV0IGluIHJlYWxpdHkgaXQncyBhIHNpbXBsZSBjb25jZXB0IC0gdGhpbmsgb2YgZ2dwbG90IGxpa2UgYSByYXN0ZXIgb3IgdmVjdG9yIGJhc2VkIGltYWdlIHByb2Nlc3NpbmcvZGVzaWduIHNvZnR3YXJlIGxpa2UgcGhvdG9zaG9wIG9yIEdJTVAsIHdoZXJlIHlvdSBpdGVyYXRpdmVseSBhZGQgbGF5ZXJzLCBidWlsZGluZyB1cCB0byB5b3VyIGZpbmFsIHBsb3QuIFNvdW5kIGZhbWlsaWFyPyBnZ3Bsb3QgKGFuZCBhbGwgdGhlIHRpZHl2ZXJzZSBwYWNrYWdlcykgd29yayBvbiB0aGUgc2FtZSBjb2RlLWJ1aWxkaW5nIHBoaWxvc29waHkgYXMgYGBgZHBseXJgYGAhCgpCZWZvcmUgd2UgYmVnaW4sIGlmIHlvdSBoYXZlbid0IGRvbmUgc28gYWxyZWFkeSwgcmVtZW1iZXIgdG8gbG9hZCBgYGBnZ3Bsb3QyYGBgIGludG8geW91ciBSIGVudmlyb25tZW50OgpgYGB7cn0KaW5zdGFsbC5wYWNrYWdlcygiZ2dwbG90MiIpCmxpYnJhcnkoZ2dwbG90MikKYGBgCgoKIyMgU2NhdHRlciBwbG90cwoKVGhlIGJlc3Qgd2F5IHRvIGxlYXJuIGlzLCBhcyBhbHdheXMsIHRvIGhhdmUgYSBnbyB5b3Vyc2VsZi4gRmlyc3QsIHdlJ2xsIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCEgVGhpcyBpcyBzaW1pbGFyIHRvIHRoZSBiYXNlUiBgYGBwbG90KHgsIHkpYGBgIG91dHB1dCwgYnV0IGluZmluaXRlbHkgbW9yZSBjdXN0b21pc2FibGUuIExldCdzIHBsb3QgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIGhlaWdodCBhbmQgbWFzcyBvZiBjaGFyYWN0ZXJzIHdpdGhpbiB0aGUgc3RhcndhcnMgdW5pdmVyc2UuCgpgYGB7cn0KIyBJZiB5b3UgZG9uJ3QgYWxyZWFkeSBoYXZlIHRoZSBzdGFyd2FycyBkYXRhIGxvYWRlZDoKZGF0YShzdGFyd2FycykKYGBgCgpXZSdsbCBidWlsZCB1cCB0aG9zZSBsYXllcnMsIHN0YXJ0aW5nIHdpdGggdGVsbGluZyBSIHdoYXQgZGF0YXNldCB3ZSBhcmUgdXNpbmcsIHdpdGggdGhlIGBgYGdncGxvdCgpYGBgIGZ1bmN0aW9uLCBhbmQgc2xvd2x5IGJ1aWxkIHVwIHRvIGEgY29tcGxldGUgcGxvdC4gKipOb3RlIHRoYXQgd2hpbHN0IHRoZSBwYWNrYWdlIGlzIGNhbGxlZCBgYGBnZ3Bsb3QyYGBgLCB0aGUgcGxvdHRpbmcgZnVuY3Rpb24gaXMganVzdCBgYGBnZ3Bsb3QoKWBgYCoqCgpgYGB7cn0KZ2dwbG90KHN0YXJ3YXJzKQpgYGAKCgpUaGlzIGdlbmVyYXRlcyB1cyBhIG5pY2UgYmxhbmsgZ3JleSBzcXVhcmUuIEl0IG1pZ2h0IG5vdCBzZWVtIGxpa2UgYSBmYWJ1bG91cyBzdGFydCwgYnV0IHdlJ3ZlIG1hbmFnZWQgdG8gZnVsZnVsbCBvdXIgZmlyc3Qgb2YgdGhlIHRocmVlIG5lY2Vzc2FyeSBpbnB1dHMgLSBhIGRhdGFmcmFtZS4KCkxldCdzIGJ1aWxkIG9uIHRoaXMgYnkgYWRkaW5nIHRoZSBuZXh0IHBhcnQgLSBlbXBsb3lpbmcgYSBjby1vcmRpbmF0ZSBzeXN0ZW0gZm9yIHVzIHRvIG1hcCBvdXIgdmFyaWFibGVzIG9uIHRvLiBXZSBkbyB0aGlzIGJ5IHVzaW5nIHRoZSBgYGBhZXMoKWBgYCBhcmd1bWVudCB3aXRoaW4gdGhlIGBgYGdncGxvdCgpYGBgIGZ1bmN0aW9uLiBIZXJlLCBgYGBhZXNgYGAgc3RhbmRzIGZvciBhZXN0aGV0aWNzIC0gd2hpY2ggZG9lc24ndCBtZWFuIHdoYXQgdGhlIHBsb3QgbG9va3MgbGlrZSwgYnV0IHRlbGxzIGdncGxvdCBleGFjdGx5IHdoYXQgd2Ugd2FudCB0byBtYXAgb250byBvdXIgZ3JhcGg6CgpgYGB7cn0KZ2dwbG90KHN0YXJ3YXJzLCBhZXMoeCA9IGhlaWdodCwgeSA9IG1hc3MpKQpgYGAKCllheSAtIHdlIGhhdmUgb3VyIGNvLW9yZGluYXRlIHN5c3RlbSBhbmQgb3VyIGF4ZXMgaW4gcGxhY2UgcmVhZHkgdG8gbWFwIHRoZSBkYXRhIG9udG8uIE5vdGUgdGhhdCBpbiB0aGUgYGBgYWVzKClgYGAgYXJndW1lbnQgSSBoYXZlIHNwZWNpZmllZCB3aGljaCBpcyB4IGFuZCB3aGljaCBpcyB5LCB5b3UgZG8gbm90IG5lZWQgdG8gZG8gdGhpcy4gUiBhdXRvbWF0aWNhbGx5IHBsb3RzIHggYW5kIHRoZW4geSwgc28gdHlwaW5nIGBgYGdncGxvdChzdGFyd2FycywgYWVzKGhlaWdodCwgbWFzcykpYGBgIHdvdWxkIHllaWxkIHRoZSBzYW1lIHJlc3VsdC4gCgpOb3csIGxldCdzIGFkZCB0aGUgcG9pbnRzIHRvIHRoZSBncmFwaC4gV2UgZG8gdGhpcyBieSBhZGRpbmcgYSBuZXcgbGF5ZXIgdG8gdGhlIHBsb3QsIHdpdGggb25lIG9mIHRoZSBzZXQgb2YgYGBgZ2VvbWBgYCBmdW5jdGlvbnMgaW4gZ2dwbG90IC0gYGBgZ2VvbV9wb2ludCgpYGBgLiBUbyBhZGQgYSBuZXcgbGF5ZXIsIHdlIGVuZCB0aGUgcHJldmlvdXMgbGluZSBvZiBjb2RlIHdpdGggYSBgYGArYGBgLCB3aGljaCB0ZWxscyBSIHRoYXQgd2Ugd2VyZW4ndCBmaW5pc2hlZCB3aXRoIHRoZSBsYXN0IGxpbmU6CgpgYGB7cn0KZ2dwbG90KHN0YXJ3YXJzLCBhZXMoeCA9IGhlaWdodCwgeSA9IG1hc3MpKSsKICBnZW9tX3BvaW50KCkKYGBgCllvdSBtYXkgc2VlIGEgd2FybmluZyB0aGF0IHNheXMgUiBoYXMgcmVtb3ZlZCAyOCByb3dzIHdpdGggbWlzc2luZyB2YWx1ZXMgLSB0aGVzZSBhcmUganVzdCBvdXIgYGBgTkFgYGAgdmFsdWVzIGZyb20gdGhlIGRhdGFzZXQsIHNvIHlvdSBjYW4gaWdub3JlIHRoYXQgZm9yIG5vdy4gCgpOb3csIHBsb3R0aW5nIG91ciBzY2F0dGVyIHBsb3Qgb2YgaGVpZ2h0IGFuZCBtYXNzIGhhcyBzaG93biB1cyB3aHkgd2UgbWlnaHQgd2FudCB0byBwbG90IG91ciBkYXRhIC0gaXQgc2VlbXMgdGhhdCB3ZSBoYXZlIGEgc2luZ2xlIGluZGl2aWR1YWwgd2l0aCBhIG1hc3Mgb2YgYXJvdW5kIDE0MDAgdW5pdHMsIHdoZXJlYXMgZXZlcnlvbmUgZWxzZSBpcyBiZWxvdyBhYm91dCAyMDAhIElmIHlvdSBrbm93IGFueXRoaW5nIGFib3V0IHRoZSBzdGFyd2FycyB1bml2ZXJzZSB5b3UgbWlnaHQgYmUgYWJsZSB0byBndWVzcyB3aG8gdGhpcyBpcy4uLiBGb3IgZXZlcnlvbmUgZWxzZSAtIGxldCdzIHNlZSBpZiB3ZSBjYW4gd29yayBpdCBvdXQgYnkgZ29pbmcgYmFjayB0byBvdXIgYGBgZHBseXJgYGAgc2tpbGxzIHRoYXQgd2UndmUganVzdCBsZWFybmVkLCBhbmQgYWRkIGluIGEgd2F5IHRvIG9ubHkgZGlzcGxheSBpbmZvcm1hdGlvbiBhYm91dCBpbmRpdmlkdWFscyB3aXRoIGEgbWFzcyBvZiBncmVhdGVyIHRoYW4gMTAwMCEKCmBgYHtyfQpzdGFyd2FycyAlPiUKICBmaWx0ZXIobWFzcyA+IDEwMDApICU+JQogIHNlbGVjdChuYW1lLCBzcGVjaWVzLCBtYXNzKQpgYGAKSWYgeW91IGtub3cgYW55dGhpbmcgYWJvdXQgc3RhcndhcnMsIHBlcmhhcHMgdW5zdXJwcmlzaW5nbHksIG91ciBhbnN3ZXIgaXMgSmFiYmEgdGhlIEh1dHQhCgpBbnl3YXksIGJhY2sgdG8gZ2dwbG90LgoKIyMgSGlzdG9ncmFtcwoKU29tZXRpbWVzLCB5b3UgbWlnaHQgd2FudCB0byBwbG90IHRoZSBkaXN0cmlidXRpb24gb2YgeW91ciBkYXRhLiBUaGlzIGlzIHVzZWZ1bCBmb3IgYm90aCBpZGVudGlmeWluZyBvdXRsaWVycywgYW5kIGFsc28gZm9yIHNlZWluZyB3aGF0IG91ciBkYXRhIGxvb2sgbGlrZS4gSW4gbG90cyBvZiBzdGF0aXN0aWNhbCB0ZXN0cywgd2UgbWFrZSBhc3N1bXB0aW9ucyByZWdhcmRpbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBvdXIgZGF0YSAtIG9mdGVuIHRoaXMgYXNzdW1wdGlvbiBpcyB0aGF0IG91ciBkYXRhIGZvbGxvdyBhIG5vcm1hbCBkaXN0cmlidXRpb24uIFdlIGNhbiBjcmVhdGUgYSBoaXN0b2dyYW0gcmVhbGx5IHF1aWNrbHkgdXNpbmcgZ2dwbG90IGFuZCBzZWUgaWYgdGhpcyBpcyB0cnVlIQoKV2UnbGwgc3RhcnQgYnkgYnVpbGRpbmcgdXAgb3VyIGRhdGEgYXMgd2UgZGlkIGJlZm9yZSwgYnV0IHdlJ2xsIGdvIHN0cmFpZ2h0IHRvIGFkZGluZyB0aGUgY28tb3JkaW5hdGUgc3lzdGVtOgpgYGB7cn0KZ2dwbG90KHN0YXJ3YXJzLCBhZXMoeCA9IG1hc3MpKQpgYGAKCllvdSBtaWdodCBoYXZlIG5vdGljZWQgdGhhdCB3ZSBvbmx5IGhhdmUgb25lIGF4aXMgaGVyZSAtIHRoaXMgaXMgYmVjYXVzZSB3ZSBoYXZlIG9ubHkgc3VwcGxpZWQgUiB3aXRoIGEgc2luZ2xlIGFlc3RoZXRpYyAodGhlIHggYWVzdGhldGljKS4gVGhlIHJlYXNvbiB0aGF0IHdlIGhhdmUgZG9uZSB0aGlzLCBpcyBiZWNhdXNlIGhpc3RvZ3JhbXMgcGxvdCBhIHNpbmdsZSB2YXJpYWJsZSBhZ2FpbnN0IGl0J3MgY291bnQgLSB3ZSBkb24ndCBrbm93IHdoYXQgdGhhdCBjb3VudCBpcyB5ZXQsIGdncGxvdCB3aWxsIGZpZ3VyZSB0aGF0IG91dCBmb3IgdXMuIE5vdGljZSBhbHNvIHRoYXQgd2UgY2FuIGdldCBhbiBpZGVhIG9mIHRoZSBzY2FsZSBqdXN0IGJ5IGxvb2tpbmcgYXQgdGhlIHggYXhpcywgd2UgY2FuIHNlZSBoZXJlIHRoYXQgb3VyIG1hc3MgcmFuZ2VzIGZyb20gMCB0byB3YXkgYWJvdmUgMTAwMCAod2hpY2ggd2Ugbm93IGtub3cgaXMgYSBkaXJlY3QgcmVzdWx0IG9mIEphYmJhIHRoZSBIdXQhKS4KCkxldCdzIGFkZCB0aGUgaGlzdG9ncmFtIGdlb20uCgpgYGB7cn0KZ2dwbG90KHN0YXJ3YXJzLCBhZXMobWFzcykpKwogIGdlb21faGlzdG9ncmFtKCkKYGBgCgpXZSBjYW4gc2VlIHRoYXQsIGFzIGluIG91ciBzY2F0dGVycGxvdCwgbW9zdCBpbmRpdmlkdWFscyBhcmUgYmVsb3cgYWJvdXQgMjUwIHVuaXRzIG9mIG1hc3MsIHdpdGggYSBzaW5nbGUgaW5kaXZpZHVhbCB3YXkgb3ZlciAxMDAwIQoKVGhlcmUgYXJlIGxvYWRzIG9mIGRpZmZlcmVudCBnZW9tcyBhdmFpbGFibGUsIGFuZCBsb3RzIG9mIG9ubGluZSByZXNvdXJjZXMgdG8gY2hlY2sgb3V0IC0gdGhlIGdncGxvdCBtYW51YWwgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCEgRm9yIG5vdywgbGV0J3MganVzdCBoYXZlIGEgbG9vayBhdCBvbmUgbW9yZSwgdGhlIGJveHBsb3QuIEJveHBsb3RzIGFyZSBhbiBpbmNyZWRpYmx5IHVzZWZ1bCB3YXkgb2YgbG9va2luZyBhdCBkaXN0cmlidXRpb25zIG9mIGRhdGEgd2l0aGluIGdyb3VwcywgYW5kIHdvcmtpbmcgb3V0IHdoZXRoZXIgcGFydGljdWxhciBncm91cHMgZnJvbSBhIHNpbmdsZSBkYXRhc2V0IGRpZmZlciBmcm9tIG9uZSBhbm90aGVyIGluIGEgY2VydGFpbiBtZWFzdXJlbWVudC4gTGV0J3MgcGxvdCBhIGJveHBsb3QgdGhhdCB0ZWxscyB1cyBhYm91dCB0aGUgbWFzcyBvZiBzcGVjaWVzIGZyb20gd2l0aGluIHRoZSBzdGFyd2FycyB1bml2ZXJzZToKCmBgYHtyfQpnZ3Bsb3Qoc3RhcndhcnMsIGFlcyh4ID0gc3BlY2llcywgeSA9IG1hc3MpKSsKICBnZW9tX2JveHBsb3QoKQpgYGAKCkFoLiBUaGlzIGlzbid0IGEgdmVyeSBuaWNlIHBsb3QuIFRoZXJlJ3MgZmFyIHRvbyBtdWNoIGRhdGEsIGFuZCBpdCdzIGFsbCB2ZXJ5IG1lc3N5LiBXZSBjYW4ndCBzZWUgYW55IG9mIG91ciBzcGVjaWVzIG5hbWVzLCBvciByZWFsbHkgd29yayBvdXQgd2hhdCdzIGdvaW5nIG9uIGF0IGFsbCAtIGxldHMgbGVhcm4gaG93IHRvIGZpeCBpdC4uLgoKIyBDb21iaW5pbmcgZ2dwbG90MiBhbmQgZHBseXIKCkl0J3MgdGltZSB0byBjb21iaW5lIG91ciBuZXcgcGxvdHRpbmcgYW5kIGRhdGEtd3JhbmdsaW5nIHNraWxsc2V0cyEgZ2dwbG90IGlzIGEgZ3JlYXQgd2F5IG9mIHNob3dpbmcgZGF0YSwgYnV0IHJlYWxseSB0aGUgcG93ZXIgb2YgdGhlIHRpZHl2ZXJzZSBjb21lcyB3aGVuIHlvdSBpbnRlZ3JhdGUgdGhlIHBhY2thZ2VzIHdpdGggb25lIGFub3RoZXIuIFRoZSBjb21iaW5lZCBwaGlsb3NvcGhpZXMgb2YgZHBseXIgYW5kIGdncGxvdDIgY2FuIGNyZWF0ZSB1cyBhIG11Y2ggbmljZXIgcGxvdC4gQmVjYXVzZSBkcGx5ciBhbmQgZ2dwbG90MiB3ZXJlIGJ1aWx0IHRvZ2V0aGVyIHVuZGVyIHRoZSB0aWR5dmVyc2UgdW1icmVsbGEsIHRoZXkgaW50ZWdyYXRlIHJlYWxseSBzbW9vdGhseSB3aXRoIG9uZSBhbm90aGVyLCBhbmQgY29tYmluaW5nIHRoZW0gaXMgYXMgc2ltcGxlIGFzIHBpcGluZyBmcm9tIGEgZHBseXIgZmlsdGVyIGludG8gYSBnZ3Bsb3QgZnVuY3Rpb24uIFlvdSBjYW4gdGFrZSBhIGRhdGFzZXQgeW91IGRvbid0IGtub3csIGFuZCByZWFsbHkgZ2V0IGEgaGFuZGxlIG9uIGl0IGJ5IGNvbWJpbmluZyB0aGVzZSB0b29scy4gTGV0J3MgdHJ5IGhlcmUuCgpXZSdsbCBnbyBiYWNrIHRvIG91ciBib3hwb3QgYW5kIHVzZSB3aGF0IHdlJ3ZlIGxlYXJuZWQgd2l0aCBkcGx5ciB0byBjcmVhdGUgYSBtdWNoIG5pY2VyIGJveHBsb3QgdGhhdCBkZXNjcmliZXMgdGhlIG1hc3Mgb2YgdGhyZWUgZGlmZmVyZW50IHNwZWNpZXMgKHJhdGhlciB0aGFuIGFsbCBvZiB0aGVtIGF0IG9uY2UhKS4gU28sIHdoaWNoIHNwZWNpZXMgc2hhbGwgd2UgdXNlPyBJbiBhIGRhdGFzZXQgdGhhdCBjb250YWlucyBsb3RzIG9mIG9ic2VydmF0aW9ucyBvZiBhIHNpbmdsZSBpbmRpdmlkdWFsIG9mIGEgc3BlY2llcywgSSB0aGluayBpdCBtYWtlcyBzZW5zZSB0byBzZWxlY3QgdGhlIHRocmVlIG1vc3QgYWJ1bmRhbnQgc3BlY2llcy4gUmVtZW1iZXIgaG93IGVhcmxpZXIgd2UgdXNlZCB0aGUgYGBgZ3JvdXBfYnkoKWBgYCBhbmQgdGhlIGBgYHRhbGx5KClgYGAgZnVuY3Rpb25zIHRvIGdldCBSIHRvIGNvdW50IHRoZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgaW4gZWFjaCBzcGVjaWVzPwoKYGBge3J9CnN0YXJ3YXJzICU+JQogIGdyb3VwX2J5KHNwZWNpZXMpICU+JQogIHRhbGx5KCkKYGBgCgpUaGlzIGlzIGdyZWF0LCBidXQgaXQgd291bGQgYmUgYmV0dGVyIGlmIHdlIGNvdWxkIG9yZGVyIGl0IGFuZCBzZWUgYXQgYSBnbGFuY2Ugd2hpY2ggc3BlY2llcyBoYWQgdGhlIG1vc3QgaW5kaXZpZHVhbHMgLSB3ZSBjYW4gZG8gdGhpcyB3aXRoIHRoZSBgYGBhcnJhbmdlKClgYGAgZnVuY3Rpb24gYW5kIHRoZSBgYGBkZXNjYGBgIGFyZ3VtZW50OgoKYGBge3J9CnN0YXJ3YXJzICU+JQogIGdyb3VwX2J5KHNwZWNpZXMpICU+JQogIHRhbGx5KCkgJT4lCiAgYXJyYW5nZShkZXNjKG4pKQpgYGAKCmBgYGRlc2MobilgYGAgdGVsbHMgdGhlIGBgYGFycmFuZ2UoKWBgYCBmdW5jdGlvbiB0aGF0IHdlIHdhbnQgdG8gZGlzcGxheSBvdXIgZGF0YSBpbiBkZXNjZW5kaW5nIG9yZGVyIG9mIHRoZSBudW1iZXIgb2YgaW5kaXZpZHVhbHMgaW4gZWFjaCBzcGVjaWVzLgoKV2UgY2FuIHNlZSBmcm9tIHRoZSByZXN1bHRpbmcgdGliYmxlIHRoYXQgd2UgaGF2ZSAzNSBodW1hbnMsIDYgZHJvaWRzLCBvdXIgbmV4dCBoaWdoZXN0IGlzIGBgYE5BYGBgIHdoZXJlIHNwZWNpZXMgd2Fzbid0IHJlY29yZGVkLCBzbyB3ZSdsbCBpZ25vcmUgdGhhdCwgYW5kIHRoZW4gMyBndW5nYW5zLiBXZSdsbCBwbG90IHRoZSBtYXNzIG9mIGVhY2ggb2YgdGhlc2UgdGhyZWUgc3BlY2llcyEgVG8gZG8gdGhpcywgd2Ugd2lsbCBuZWVkIHRvIGZpbHRlciBvdXIgZGF0YSB1c2luZyBgYGBkcGx5cmBgYC4gV2UgY2FuIGZpbHRlciBmb3IgbXVsaXRwbGUgbWF0Y2hlcyB1c2luZyB0aGUgT1Igb3BlcmF0b3IgYGBgfGBgYCB0aHVzOgoKYGBge3J9CnN0YXJ3YXJzICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJIdW1hbiIgfCBzcGVjaWVzID09ICJEcm9pZCIgfCBzcGVjaWVzID09ICJHdW5nYW4iKQpgYGAKCldoZW4gd2UgcnVuIHRoZSBjb2RlIHRvIHNlbnNlIGNoZWNrIG91cnNlbHZlcywgaXQncyB0cmlja3kgdG8gc2VlIGlmIGl0J3Mgd29ya2VkIGluIHRoZSB3YXkgd2Ugd2FudGVkLiBXZSBjYW4gZ2V0IHJvdW5kIHRoaXMgYnkgcGlwaW5nIHRvIHNlbGVjdCBzbyB3ZSBjYW4gb25seSBzZWUgdGhlIHJlbGV2YW50IGNvbHVtbnM6CgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkh1bWFuIiB8IHNwZWNpZXMgPT0gIkRyb2lkIiB8IHNwZWNpZXMgPT0gIkd1bmdhbiIpICU+JQogIHNlbGVjdChuYW1lLCBzcGVjaWVzLCBtYXNzKQpgYGAKClJpZ2h0LCB3ZSBzdGlsbCBjYW4ndCBzZWUgaWYgd2UndmUgZ290IGFueSBHdW5nYW5zIGluIGhlcmUuIExldCdzIGdldCBhcm91bmQgdGhhdCBieSBhZGRpbmcgYSB0YWxseSB0byBkaXNwbGF5IGEgY291bnQgb2YgaG93IG1hbnkgc3BlY2llcyBhcmUgaW4gb3VyIG5ldyBzZWxlY3Rpb24gLSBkb24ndCBmb3JnZXQgdGhlIGBgYGdyb3VwX2J5KClgYGAgdG8gdGVsbCB0YWxseSB3aGF0IGl0IG5lZWRzIHRvIGNvdW50IQoKYGBge3J9CnN0YXJ3YXJzICU+JQogIGZpbHRlcihzcGVjaWVzID09ICJIdW1hbiIgfCBzcGVjaWVzID09ICJEcm9pZCIgfCBzcGVjaWVzID09ICJHdW5nYW4iKSAlPiUKICBzZWxlY3QobmFtZSwgc3BlY2llcywgbWFzcykgJT4lCiAgZ3JvdXBfYnkoc3BlY2llcykgJT4lCiAgdGFsbHkoKQpgYGAKRmFidWxvdXMuIEl0IHNlZW1zIHRoYXQgd2UndmUgZ290IGFsbCBvZiBvdXIgZGF0YSBpbiBjaGVjaywgYW5kIG91ciBjb2RlIHNlZW1zIHRvIGJlIGRvaW5nIHdoYXQgd2Ugd2FudCBpdCB0byEgV2UgY2FuIGRlbGV0ZSB0aG9zZSBmaW5hbCBgYGBncm91cF9ieSgpYGBgIGFuZCBgYGB0YWxseSgpYGBgIHNlbnNlIGNoZWNrcywgYW5kIHBpcGUgc3RyYWlnaHQgdG8gZ2dwbG90LiBXZSdsbCBqdXN0IHBsb3QgdGhlIGFlc3RoZXRpY3MgZmlyc3QgdG8gY2hlY2sgaWYgd2UndmUgZ290IHRoZSBjb3JyZWN0IG1hcHBpbmcgYW5kIHRoYXQgb25seSB0aHJlZSBzcGVjaWVzIGFwcGVhciBvbiB0aGUgeCBheGlzLiBXZSBkb24ndCBuZWVkIHRvIGluY2x1ZGUgdGhlIG5hbWUgb2YgdGhlIGRhdGEgaW4gb3VyIGBgYGdncGxvdCgpYGBgIGNvbW1hbmQgdGhpcyB0aW1lIC0gUiBhbHJlYWR5IGtub3dzIHdoYXQgZGF0YSB3ZSdyZSB1c2luZyBmcm9tIG91ciBwaXBpbmcKCmBgYHtyfQpzdGFyd2FycyAlPiUKICBmaWx0ZXIoc3BlY2llcyA9PSAiSHVtYW4iIHwgc3BlY2llcyA9PSAiRHJvaWQiIHwgc3BlY2llcyA9PSAiR3VuZ2FuIikgJT4lCiAgc2VsZWN0KG5hbWUsIHNwZWNpZXMsIG1hc3MpICU+JQogIGdncGxvdChhZXMoeCA9IHNwZWNpZXMsIHkgPSBtYXNzKSkKYGBgCgpFeGNlbGxlbnQuIE5vdywgbGV0J3MgYWRkIHRoZSBib3hwbG90IGdlb20uCgpgYGB7cn0Kc3RhcndhcnMgJT4lCiAgZmlsdGVyKHNwZWNpZXMgPT0gIkh1bWFuIiB8IHNwZWNpZXMgPT0gIkRyb2lkIiB8IHNwZWNpZXMgPT0gIkd1bmdhbiIpICU+JQogIHNlbGVjdChuYW1lLCBzcGVjaWVzLCBtYXNzKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSBzcGVjaWVzLCB5ID0gbWFzcykpKwogIGdlb21fYm94cGxvdCgpCmBgYAoKVGhpcyBpcyBhIG11Y2ggbmljZXIgYW5kIG1vcmUgc3VjY2Vzc2Z1bCBib3hwbG90IHRoYW4gYmVmb3JlISBXZSBjYW4gc2VlIHRoYXQgdGhlIG1lYW4gbWFzcyBvZiBEcm9pZHMgdGVuZHMgdG8gYmUgbXVjaCBsb3dlciB0aGFuIEd1bmdhbnMgYW5kIEh1bWFucywgYnV0IHRoZWlyIG1hc3MgaXMgbW9yZSB2YXJpYWJsZS4gCgpXZSBoYXZlIHN1Y2Nlc3NmdWxseSBjb21iaW5lZCBnZ3Bsb3QgYW5kIGRwbHlyIGluIGEgcmVhbGx5IHVzZWZ1bCBhbmQgcG93ZXJmdWwgd2F5LCB0aGF0IHdpbGwgbWFrZSBleHBsb3JpbmcgZGF0YSBtdWNoIHF1aWNrZXIgYW5kIGVhc2llci4KCiMgUmVzaGFwaW5nIGRhdGEKClRoZSBzdGFyd2FycyBkYXRhc2V0IGlzIGFscmVhZHkgaW4gYSBuaWNlIGZvcm1hdCBmb3IgcGxvdHRpbmcsIGFuZCB3ZSBoYXZlbid0IG5lZWRlZCB0byBkbyBhbnl0aGluZyB3aXRoIGl0IHRvIGdldCBpdCB0aGVyZS4gVGhpcyB3b24ndCBhbHdheXMgYmUgdGhlIGNhc2Ugd2l0aCByZWFsIHdvcmxkIGRhdGEuIFNvcnRpbmcgdGhpcyBvdXQgaXMgY29tcGxpY2F0ZWQsIGFuZCBpdCdzIG9rYXkgaWYgeW91IGRvbid0IGZ1bGx5IGdyYXNwIHRoZSBmb2xsb3dpbmcgdGhlIGZpcnN0IHRpbWUgcm91bmQgLSB5b3UnbGwgZ2V0IHVzZWQgdG8gaXQgYnkgcHJhY3RpY2luZyBhbmQgaGF2aW5nIGEgZ28gd2l0aCBkYXRhIGluIHlvdXIgb3duIHRpbWUuCgpOb3csIHRoZXJlIGFyZSB0d28gbWFpbiB0eXBlcyBvZiBkYXRhOgoKKiBsb25nIGRhdGEgLSBtYW55IHJvd3MsIGJ1dCBmZXcgY29sdW1ucy4gQW4gaW5kaXZpZHVhbCBoYXMgbWFueSByb3dzLCBlYWNoIHdpdGggYSBkaWZmZXJlbnQgb2JzZXJ2YXRpb24gb24gaXQKKiB3aWRlIGRhdGEgLSBtYW55IGNvbHVtbnMsIGZld2VyIHJvd3MuIEVhY2ggaW5kaXZpZHVhbCBpcyBhIHNpbmdsZSByb3csIHdpdGggbWFueSBjb2x1bW5zIG9mIG9ic2VydmF0aW9ucwoKU29tZXRpbWVzLCBpdCdzIG5lY2Vzc2FyeSB0byBzd2FwIGJldHdlZW4gdGhlc2UgdHlwZXMgb2YgZGF0YSBzbyB3ZSBjYW4gcHJvZHVjZSB0aGUgcGxvdCB0aGF0IHdlIHdhbnQuIFRoZSBgYGBwaXZvdF9kYXRhYGBgIGZ1bmN0aW9ucyBhbGxvdyB1cyB0byBkbyB0aGlzLiBgYGBwaXZvdF9sb25nZXJgYGAgd2lsbCBjb252ZXJ0IGEgd2lkZSBkYXRhc2V0IGludG8gYSBsb25nIG9uZSwgYnkgcmVkdWNpbmcgdGhlIG51bWJlciBvZiBjb2x1bW5zIGFuZCBpbmNyZWFzaW5nIHRoZSBudW1iZXIgb2Ygcm93cywgY29udmVydGluZyBjb2x1bW5zIGludG8gbmV3IHJvd3MsIGFuZCBgYGBwaXZvdF93aWRlcmBgYCBkb2VzIHRoZSBvcHBvc2l0ZS4gCgpXZSdsbCB1c2UgdGhlIGBgYGlyaXNgYGAgZGF0YXNldCB0byBkZW1vbnN0cmF0ZS4gQXMgYSByZW1pbmRlciwgdGhlIGlyaXMgZGF0YXNldCBjb250YWlucyBkaWZmZXJlbnQgbWVhc3VyZW1lbnRzIChwZXRhbCBsZW5ndGgsIHBldGFsIHdpZHRoLCBzZXBhbCBsZW5ndGgsIGFuZCBzZXBhbCB3aWR0aCkgZnJvbSBtdWx0aXBsZSBpbmRpdmlkdWFscyBvZiB0aHJlZSBkaWZmZXJlbnQgc3BlY2llcyBvZiBpcmlzLiBJZiB3ZSB2aWV3IHRoZSBkYXRhIHdpdGggYGBgaGVhZGBgYCB3ZSBjYW4gc2VlIHRoYXQgdGhlIGRhdGEgaXMgaW4gYSB3aWRlIGZvcm1hdCwgd2hlcmUgYSByb3cgcmVwcmVzZW50cyBhIHNpbmdsZSBpbmRpdmlkdWFsIHdpdGggY29sdW1ucyBvZiBvYnNlcnZhdGlvbnM6CgpgYGB7cn0KaGVhZChpcmlzKQpgYGAKCldlIHdhbnQgdGhpcyBkYXRhc2V0IHRvIGJlIGxvbmcsIHdpdGggbXVsdGlwbGUgcm93cyBwZXIgaW5kaXZpZHVhbCwgYW5kIG9ubHkgYSBzaW5nbGUgY29sdW1uIG9mIG1lYXN1cmVtZW50cy4gTGV0J3MgaGF2ZSBhIGdvIGF0IHVzaW5nIGBgYHBpdm90X2xvbmdlcigpYGBgIHRvIGFjaGlldmUgdGhpcy4gVGhlIGZpcnN0IHRoaW5nIHdlIGhhdmUgdG8gY29uc2lkZXIsIHRob3VnaCwgaXMgdGhhdCBpcmlzIGlzIGEgZGF0YS5mcmFtZSByYXRoZXIgdGhhbiBhIHRpYmJsZS4gV2UgY2FuIGZpeCB0aGF0IGJ5IHBpcGluZyB0byBgYXNfdGliYmxlKClgIGJlZm9yZSB3ZSByZXNoYXBlLiBXZSdsbCBhbHNvIGFzc2lnbiBhbGwgb2YgdGhpcyB0byBhIG5ldyBvYmplY3QsIHRoYXQgd2UnbGwgY2FsbCBgaXJpczJgLgoKYGBge3J9CmlyaXMyIDwtIGlyaXMgJT4lCiAgYXNfdGliYmxlKCkgJT4lCiAgcGl2b3RfbG9uZ2VyKGNvbCA9IDE6NCwgbmFtZXNfdG8gPSAibWVhc3VyZSIpCmBgYApMZXQncyBkaXNzZWN0IHRoYXQgYHBpdm90X2xvbmdlcigpYCBmdW5jdGlvbjoKCiogdGhlIGBjb2wgPSAxOjRgIGFyZ3VtZW50IHRlbGxzIHRoZSBmdW5jdGlvbiB0aGF0IHdlIHdhbnQgdG8gY29udmVydCB0aGVzZSA0IGNvbHVtbnMgaW50byBpbmRpdmlkdWFsIHJvd3MuIFdlIGRvbid0IGluY2x1ZGUgdGhlIGZpZnRoIGNvbHVtbiwgc3BlY2llcywgYXMgdGhpcyBpcyBhbiBpZGVudGlmaWVyIHJhdGhlciB0aGFuIGEgbWVhc3VyZW1lbnQuCiogdGhlIGBuYW1lc190byA9ICJtZWFzdXJlImAgaXMgc2ltcGx5IGNyZWF0aW5nIGEgbmV3IGNvbHVtbiB3aGVyZSB3ZSBjYW4gc3RvcmUgdGhlIG9yaWdpbmFsIGNvbHVtbiBoZWFkaW5ncy4KCk5vdGUgdGhhdCB3aGVuIHdlIGFzc2lnbiB0aGluZ3MsIHdlIGRvbid0IGF1dG9tYXRpY2FsbHkgZ2V0IHRoZW0gYXMgYW4gb3V0cHV0IHRvbyAtIGxldCdzIGNhbGwgYGlyaXMyYCBhbmQgc2VlIHdoYXQgaXQgbG9va3MgbGlrZToKCmBgYHtyfQppcmlzMgpgYGAKCkdyZWF0LiBUaGUgZGF0YSBpcyBub3cgaW4gYSBsb25nIGZvcm1hdCwgcmF0aGVyIHRoYW4gd2lkZS4gV2UgY2FuIHBsb3QgaXQgYXMgYSBib3hwbG90IHRoYXQgdGVsbHMgdXMgaW5mb3JtYXRpb24gYWJvdXQgZWFjaCBzcGVjaWVzIGFuZCBtZWFzdXJlbWVudCBzZXBlcmF0ZWx5LiBMZXQncyB0cnkgaXQgd2l0aCB3aGF0IHdlIGN1cnJlbnRseSBrbm93IG9mIGdncGxvdDoKCmBgYHtyfQpnZ3Bsb3QoaXJpczIsIGFlcyh4ID0gU3BlY2llcywgeSA9IHZhbHVlKSkrCiAgZ2VvbV9ib3hwbG90KCkKYGBgCgpUaGlzIHBsb3QgaGFzIGdpdmVuIHVzIHRocmVlIGRpZmZlcmVudCBib3hlcyBvbiBvdXIgcGxvdCAtIG9uZSBwZXIgc3BlY2llcy4gV2UndmUgY29uZmxhdGVkIGFsbCBvZiB0aGUgbWVhc3VyZW1lbnRzIHRvZ2V0aGVyIC0gdGhpcyBpcyBzaW1wbHkgYmVjYXVzZSB3ZSBoYXZlbid0IHRvbGQgUiB0aGF0IHdlIHdhbnQgdGhlbSBzZXBhcmF0ZWQhIFdlIGNhbiBkbyB0aGlzIHVzaW5nIGEgdG9vbCBrbm93biBhcyBmYWNldCBncmlkcywgd2hpY2ggaXMgZXNzZW50aWFsbHkgYSBmYW5jeSB3YXkgb2Ygc2F5aW5nICJzcGxpdHRpbmcgcGxvdHMgdG8gcHVsbCBvdXQgZGlmZmVyZW50IG1lYXN1cmVtZW50cyIuIExldCdzIHRyeSBhZGRpbmcgYSBgYGBmYWNldF9ncmlkKClgYGAgbGF5ZXIgdG8gb3VyIHBsb3QsIGFuZCBpbmNsdWRpbmcgdGhlIGFyZ3VtZW50IGBgYH5tZWFzdXJlYGBgIHdpdGhpbiBpdC4gUmVtZW1iZXIgdGhhdCBhIH4gZGVub3RlcyBhIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoaW5ncywgc28gYnkgY2FsbGluZyBgYGBmYWNldF9ncmlkKH5tZWFzdXJlKWBgYCB3ZSBhcmUgYXNraW5nIFIgdG8gcGxvdCB0aGUgYWJvdmUsIGJ1dCBzZXBhcmF0aW5nIHRoZSBwbG90cyBiZXR3ZWVuIG1lYXN1cmVtZW50IHR5cGVzLiBMZXRzIGhhdmUgYSBnbzoKCmBgYHtyfQpnZ3Bsb3QoaXJpczIsIGFlcyh4ID0gU3BlY2llcywgeSA9IHZhbHVlKSkrCiAgZ2VvbV9ib3hwbG90KCkrCiAgZmFjZXRfZ3JpZCh+bWVhc3VyZSkKYGBgCgpHcmVhdCwgc28gbm93IHdlJ3ZlIG1hbmFnZWQgdG8gc2VwYXJhdGUgb3VyIG1lYXN1cmVtZW50cyBvdXQgaW50byBpbmRpdmlkdWFsIHBsb3RzLCBhbmQgYnkgZG9pbmcgdGhpcyB3ZSBjYW4gbGVhcm4gbW9yZSBhYm91dCB0aGUgZGF0YSEgV2UgY2FuIHNlZSB0aGF0IHRoZSBzZXRvc2Egc3BlY2llcyB0ZW5kcyB0byBiZSBzbWFsbGVyIHRoYW4gdGhlIG90aGVyIHR3byBnZW5lcmFsbHksIHdpdGggdGhlIGV4Y2VwdGlvbiBvZiBpdCdzIHNlcGFsIHdpZHRoLiBIb3BlZnVsbHkgbm93IHlvdSBjYW4gc2VlIGhvdyByZXNoYXBpbmcgeW91ciBkYXRhIGluIHRoaXMgd2F5IGFsbG93cyB5b3UgdG8gZXhwbG9yZSBpdCBkaWZmZXJlbnRseSwgYW5kIGdldCB0byBrbm93IGl0IGEgbGl0dGxlIGJldHRlciEKCiMgU2F2aW5nIHBsb3RzCgpTbyBub3cgd2UndmUgZ290IG91ciBkYXRhIHBsb3R0ZWQgYW5kIHJlYWR5IHRvIGdvLCB3ZSBtaWdodCB3YW50IHRvIHNhdmUgaXQuIEhlbHBmdWxseSwgdGhlcmUgaXMgYW4gImV4cG9ydCBncmFwaCIgYnV0dG9uIG9uIHRoZSBwbG90IHdpbmRvdyB0aGF0IG1ha2VzIHRoaXMgbmljZSBhbmQgZWFzeS4gQWx0aG91Z2ggdGhlcmUgbWlnaHQgYmUgYSBzaXR1YXRpb24gd2hlcmUgeW91J3ZlIHVzZWQgUiB0byBhdXRvbWF0ZSB0aGUgcHJvZHVjdGlvbiBvZiBtdWx0aXBsZSBwbG90cywgYW5kIHlvdSdkIGxpa2UgdG8gaW5jbHVkZSBhIHNhdmUgZnVuY3Rpb24gd2l0aGluIHRoZSBjb2RlIGl0c2VsZi4gWW91IGNhbiBkbyB0aGlzIGZhaXJseSBzaW1wbHkgd2l0aCB0aGUgYGdnc2F2ZSgpYCBmdW5jdGlvbiwgd2hpY2ggYmFzaWNhbGx5IGRvZXMgd2hhdCBpdCBzYXlzIG9uIHRoZSB0aW4uCgpZb3UgZ2l2ZSB0aGUgZnVuY3Rpb24gYSBuYW1lIGFuZCBhIGZpbGUgZXh0ZW5zaW9uIGluIHRoZSBmb3JtIG9mICJteV9wbG90LnBuZyIgZm9yIGV4YW1wbGUsIGFuZCBSIHdpbGwgc2F2ZSBpdCBpbnRvIHlvdXIgd29ya2luZyBkaXJlY3RvcnkuIFRyeSB0aGUgZm9sbG93aW5nCgpgYGB7cn0KZ2dzYXZlKG15X3Bsb3QucG5nKQpgYGAKYW5kIHlvdSBzaG91bGQgc2VlIHRoYXQgUiBoYXMgc2F2ZWQgeW91ciBwbG90IGludG8geW91ciBwcm9qZWN0IGZvbGRlci4gWW91IGNhbiB0ZXN0IHRoaXMgYnkgbmF2aWdhdGluZyB0byB5b3VyIGZpbGUgdHJlZSBpbiBSc3R1ZGlvIGFuZCBjbGlja2luZyBvbiB0aGUgbmV3bHkgc2F2ZWQgcGxvdCB0byBvcGVuIGl0LiBIZWxwZnVsbHksIFIgd2lsbCBpbnRlcnByZXQgdGhlIC5zdWZmaXggYXMgYSBmaWxlIGZvcm1hdCwgc28geW91IGNhbiBjaGFuZ2UgdGhpcyBhdCB3aWxsIC0gcGVyaGFwcyB5b3Ugd2FudCBhIC50aWZmIG9yIGEgLnBkZiBmaWxlIC0ganVzdCBjaGFuZ2UgdGhlIHN1ZmZpeCBhbmQgUiB3aWxsIGRvIHRoaXMgZm9yIHlvdS4gCgpBIG5vdGUgb24gZGltZW5zaW9ucyAtIFIgd2lsbCBhdXRvbWF0aWNhbGx5IGV4cG9ydCBhIHBsb3Qgd2l0aCB0aGUgZGltZW5zaW9ucyBvZiB0aGUgcGxvdCB3aW5kb3cgLSB5b3UgY2FuIGVpdGhlciBjaGFuZ2UgdGhhdCBieSBtYW51YWxseSBhZGp1c3RpbmcgdGhlc2UsIG9yIGJ5IGhhcmQtY29kaW5nIHNvbWUgZGltZW5zaW9ucyBpbnRvIHRoZSBgZ2dzYXZlKClgIGZ1bmN0aW9uIC0gaGF2ZSBhIGxvb2sgYXQgdGhlIGA/Z2dzYXZlYCBoZWxwIGZpbGVzIHRvIGZpbmQgb3V0IG1vcmUhCgojIyBDdXN0b21pc2F0aW9uCgpUaGVyZSBhcmUgbG9hZHMgb2Ygd2F5cyB0byBjdXN0b21pc2UgeW91ciBSIHBsb3RzLCB0aGF0IHdlIGRvbid0IGhhdmUgdGltZSB0byBkaXNjdXNzIG5vdywgYnV0IGEgcXVpY2sgZ29vZ2xlIHdpbGwgc2hvdyB5b3UgdGhhdCB0aGVyZSBhcmUgZW5kbGVzcyBwb3NzaWJpbGl0aWVzIC0gZ28gYXdheSBhbmQgaGF2ZSBmdW4hCgojIEFuZCBmaW5hbGx5Li4uCgpTbywgd2UndmUgbGVhcm5lZCBhIGJpdCBhYm91dCB0aGUgcGhpbG9zb3BoaWVzIGFuZCBhcHByb2FjaGVzIHRvIHVzaW5nIFIsIGFuZCBsb29rZWQgYXQgbW9yZSBhZHZhbmNlZCB3YXlzIG9mIGhhbmRsaW5nIGRhdGEgd2l0aCBgZHBseXJgLiBXZSd2ZSBnYWluZWQgc29tZSBoYW5kcy1vbiBleHBlcmllbmNlIHdpdGggdXNpbmcgYGdncGxvdDJgIHRvIHByb2R1Y2UgcGxvdHMuCg==