Data Visualization in R

Data visualization is an important part of exploring, understanding, and sharing our data.

Data visualization is a critical part of the data science workflow. Through visualization we can explore and understand our own data, ultimately informing further analyses. Additionally, data visualizations are a powerful tool for communicating data and research findings to other people. Visuals can often more efficiently and more effectively tell the story of your data, rather than only relying on writing. Well done data visualizations will often have the biggest impact on an audience in a science communication context.

Source

In this session we have a few main goals:

  1. Introduction to types of data visualizations

  2. Discussion of what makes a good visualization

  3. Review and critique some data visualization examples

  4. Introduction to ggplot to generate visualizations in R

Let’s get started!

Types of Data Visualization

Data visualizations can be descriptive in nature, such as portraying the demographic distribution of a group of people, or can represent statistical findings, such as a regression line with a confidence interval overlaid on a scatter plot of data. In certain contexts it is also common to present data and information in more easily digestible infographics (check out some examples here) that enter more of a graphic design space. Different data visualizations fulfill different goals. Having a toolbox of a variety of data visualization types can help you pick the best type to fit your needs for a given project.

Table

Tables to collect and organize data are one of the most common and basic data visualizations. But don’t discount them! They can be efficient ways to convey a large amount of information about your data all at once.

Pie Chart

A classic pie chart is useful for representing parts that add up to a whole. This also allows for comparing group sizes. They are often used for demographic variables, with the whole representing the whole sample, or for representing money, with the whole representing a budget or total money spent/made. Be careful to use a pie chart only when it really adds to the story. For instance, if there are only two parts to the whole, a pie chart might not convey much more information. Conversely, if there are many groups, it can become difficult to really see all of them and compare them.

Box Plots

Box plots (also known as box and whisker plots) are good for understanding and comparing variance between groups. They typically depict the median and minimum/maximum of each group for a certain variable. The box portion represents the 1st and 3rd quartiles of the distribution. You can also show change over time with this type of plot.

Histogram

Histograms display the distribution of one variable. The height of the bar represents how many times that value was represented in the data. This is typically what you view if you want to visually inspect if a variable is normally distributed.

Bar Chart

Bar charts are good for representing data from groups or categories, with the bars representing different categories. The bar height usually represent a mean, a count, or a percentage, by category. These can be useful for comparing groups or showing change over time.

Scatter Plot

Scatter plots include data points that are plotted along an x and y axis, showing the relationship between these two variables. Often researchers add a line to these plots to show the statistical relationship between the variables.

Line Chart

Line charts use connected straight lines to display data. They are good for showing change over time on a continuous variable. They are often similar in purpose to bar charts, but visually simpler if there are many time points. An example of where we often see line charts used in the news is to visualize the stock market. (Plotting the various age groups here is not particularly meaningful since this data was only collected at one time point, but is used to illustrate this type of data visualization.)

Interactive Data Visualizations

As research and publications move more online and away from print, this can allow for more interactive data visualizations. These are types of data visualizations that allow a person to select and change what is being shown. Check out this great example from Gapminder. The default visualization shows GDP and life expectancy over time, and by country. However, you can change the variables included to view other data.

So many more!

  • Heat Map
  • Stacked Bar or Stacked Area Chart
  • Violin Plot
  • Gantt Chart
  • Choropleth Map

What Makes a Good Visualization?

There is an art to picking the best data visualization that fits your data and the story you are telling. By nature, data visualizations are abstract representations of our data, with color, shape, and position representing the data points. This both hides the exact data itself, while also allowing us to highlight bigger picture ideas about the data, depending on what we choose to emphasize. When deciding on what type of data visualization to use, consider the following:

  • What question are you exploring with your data and how will it inform future analyses?
  • What is your data visualization adding to your science communication?
  • What take-away message are you conveying with the image?
  • What type of variables do you have? Continuous? Categorical? And what type of abstraction (e.g., color, shape) best suits that variable?
  • Are you comparing groups?
  • Are you showing change over time?
  • Are you visualizing a relationship between variables?

It can be easy to get in the habit of using the same types of data visualization over and over again. Check out this website that gives many creative data visualization options (with R code!), categorized by goal: https://r-graph-gallery.com/

Good Data Visualization Princicples

Once you pick the format that fits your needs best, these are some principles to keep in mind when crafting your visualization to make sure it is clear to your audience.

Principle Considerations
Clear Data Consider the format of your data when you include it. Your data needs to be unambiguously communicated to your audience. For instance, do you have so many groups that they are difficult to differentiate? Are your data points stacked on the same spot so the audience can’t see the density of your data points?
Clear Labels All data visualizations need labels. This may be the axis on a scatter plot, legends for your bar graph, or percentages on your pie chart. You must tell the audience what they are looking at. If different colors are used, they should represent some aspect of the data and be clearly labeled.
Clear Scales Clear and consistent scales are important to avoid misinterpretation of your data visualization. An axis should be clearly labeled and include the full scale range. Make it clear if the scale does not start at 0. Scales should be consistent across visualizations to allow for comparisons.
Simplicity Aim to have uncluttered data visualizations. It is easy to get excited about all you can do creatively in the world of data visualizations, but sometimes adding too much (e.g., extra colors, pictures) can actually obscure your main message. Avoid extra info that doesn’t add to the story you are telling.
Accessibility Consider how your data visualization design would be viewed by a variety of people. Text font and size for legends and axis labels should be clear and not too small. Try not to rely only on color to distinguish groups (e.g., lines can be dashed or dotted), or pick colors/hues that are distinguishable by people who are colorblind. Include alternative text descriptions that can be read aloud by a screen reader.

For more information on design principles and the visual hierarchy of elements, check out this article. Size, color, contrast, alignment, repetition, proximity, whitespace, and texture can all be used to draw focus to particular visual elements.

Your Turn!

One of the best ways to get into data visualization is to get inspired from some of the amazing data visualizations that already exist!

We have two websites with a variety of data visualization examples.

  • R Graph Gallery Includes many examples of data visualizations created in R. These also include tutorials/R Code used to make them. These examples go beyond what we will cover in this workshop, but serve as great inspiration for how much you can do in R!

  • Tableau Viz Gallery Includes examples created with the platform Tableau. This is a proprietary (paid) platform which we are not using in this workshop, but this gallery still has some great examples to help inspire your data visualization creativity!

In small groups, on either of these websites, find a data visualization example that jumps out to you as interesting and then answer the following questions.

  1. What captured your attention about this data visualization? The topic? The design?
  2. What is the story this example is trying to tell? What is one of the take-away messages it is conveying?
  3. What do you like about this visualization? Does it convey information about the data in a uniquely effective way? Does it adhere to the principles we discussed?
  4. Is there anything you find confusing about this example? Anything you think is missing or that you would change to improve it?

ggplot R Package

Now that we have our data visualization imaginations going, let’s get into how we can visually represent our data in R.

The go-to package for data visualization in R is ggplot2, which is part of the tidyverse. You can find more information about ggplot2 on the tidyverse website.

This package approaches data visualization through “a grammar of graphics.” In other words, using the same syntax, you can create an infinite number of data visualizations. Although there are a lot of functions and components to learn at first, once you understand the overall structure of building graphics in ggplot2, you can replicate and expand on this structure to visualize data in an unlimited number of ways.

If you have installed the tidyverse, then ggplot2 is included. Otherwise you can install it now. Let’s also load our dataset for today.

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

There are many ways to make data visualizations in R; however, other approaches tend to be more automatic and consequently limit the amount you can change and adapt your visualization to your needs. ggplot2 works in layers, allowing for maximum control and flexibility.

Here are some of the most common layers (i.e., functions) used in ggplot2. Typically you connect these layers using the + symbol. There is often more than one way to build the same plot with the ggplot package.

- ggplot(): how you will start most plots you build in ggplot2. The rest of the information goes within this function.

- aes(): this is the aesthetic mapping function, in which you can control aesthetic components of the plot. You can add colors, axis labels, font sizes and more within this function. Color and shape can be defined both within and outside of the aesthetic function.

- geom_point(): used for making a scatter plot

- geom_line(): used for adding a line to a plot

- geom_histogram(): used for making a histogram

- geom_col(): used for making a bar plot

- xlab, ylab, and labs(title = ): used for adding axis labels and an overall title to plots

- color: assigns a color to part of the plot, such as different groups or the data points

- fill: assigns the interior color of part of the plot, such as a confidence band or the bars in bar plots

- alpha: used to change the transparency of a component of the plot. Useful if you lots of have overlapping data points or distributions from multiple groups.

- size: used to set the size of part of the plot, such as how big the data points or text should be

There are many more data visualization options in ggplot but to get started today we are going to focus on making a bar plot (good for categorical data) and a scatter plot (good for continuous data).

Basic Bar Plot: Counts

Let’s make our first plot in R! We are going to slowly add layers, building up to a box plot representing the number of people in each group in the isFeelRushed variable.

First we create the blank plot on which we will add our data. We nearly always start with the function ggplot and then telling the function what dataset to use.

ggplot(js_data)

This creates our blank canvas.

Next we tell ggplot what variable we want to use and put it in the aes() function. If you want a bar chart representing the number of people in each group, you can add just one variable to the aes() function. We include the as.factor function around the isFeelRushed variable so it is treated as a categorical variable for the box plot instead of a numeric variable.

ggplot(js_data, aes(x = as.factor(isFeelRushed)))

You now see that the grid represents a scale relevant to that variable.

Next, we tell ggplot what type of data visualization we want. To create a bar chart we use the function geom_bar(). To see how many people are in each of the isFeelRushed groups, we use the default geom_bar(stat = "count"). Remember that we connect layers with a + symbol.

ggplot(js_data, aes(x = as.factor(isFeelRushed))) +
  geom_bar(stat = "count")

We’ve got a bar chart!

Lastly, let’s add some labels to the x-axis and y-axis to make it clear what is being plotted.

ggplot(js_data, aes(x = as.factor(isFeelRushed))) +
  geom_bar(stat = "count") + 
  xlab("Feeling Rushed") +
  ylab ("Number of Participants") 

Basic Bar Plot: Group Means

Building from yesterday, let’s see if people who feel rushed tend to work more than people who do not feel rushed. To represent the mean for each group or some other variable, you add both an x and a y variable to the aes() function and use the geom_bar(stat = "summary"). Note that the Y-axis scale has now adjusted to a scale that matches the variable we are using (i.e., mean number of minutes spent working for each group).

ggplot(js_data, aes(x = as.factor(isFeelRushed), y = durWork)) +
  geom_bar(stat = "summary") + 
  xlab("Feeling Rushed") +
  ylab ("Average Minutes Working") 

  • Most plots will start with the ggplot()function
  • You have to include the object where ggplot() will get the information for the plot from. In this case, it’s our dataset js_data
  • Within the aes() function, we identify what the x and y variables are for this plot
  • We add layers using the + sign
  • Next we tell ggplot() what type of plot we are making; in this case we are creating a bar plot using the function geom_bar()
  • Then we add what type of statistic we want presented on the plot. Here we ask for the mean of each group for the variable durWork using the function geom_bar(stat = "summary")
  • We add a label to the x-axis with lab("Feeling Rushed")
  • We add the label to the y-axis with ylab ("Time Working")

Improved Box Plot!

This plot gets the idea across, but we can add more layers and functions to make more adjustments. See the walkthrough below for how we made all these changes.

plotlabels <- c("Not Rushed", "Rushed", "Did Not Respond")

ggplot(js_data, aes(x = as.factor(isFeelRushed), y = durWork)) +
  geom_bar(stat = "summary", fill = "#2D5E7F") + 
  xlab("Feeling Rushed") +
  ylab ("Average Minutes Working") +
  labs(title = "Working and Feeling Rushed") +
  scale_x_discrete(labels = plotlabels) +
  theme(text = element_text(size = 18),
        axis.text.x = element_text(angle = 25, hjust = 1))

  • We can change the color of the bars using fill =. Here we added the specific color using a hex code. But you can also write in the names of colors such as “blue”.
  • Using labs(title = "") we added an overall title to the plot
  • We probably want to indicate what each category represents, rather than the “0”, “1” and “NA” labels. To add text labels, we first create an object with each of those labels in order (plotlabels <- c("Not Rushed", "Rushed", "Did Not Respond")). Then in the scale_x_discrete(labels = plotlabels) function we call to that object we created as the labels for the x-axis. There are many other ways to adjust the labels for each axis, but this method works well for a small number of groups.
  • In the theme() layer you can add many different specifications. Here we added text = element_text(size = 18) to make the text size bigger than the default and made the x-axis labels angled so they fit better using axis.text.x = element_text(angle = 25, hjust = 1). The hjust = adjusts the vertical location of the axis labels so they don’t overlap with the plot itself. vjust = can be used to move the labels right and left.

Try manipulating this plot in some way. Can you change the color of the bars? What happens if you change angle = 90?

Basic Scatter Plot

Now let’s make a scatter plot to visualize two continuous variables. We are going to check if it looks like there is a correlation between how much time people work (durWork) and how much they sleep (durSleep).

ggplot(js_data, aes(durWork, durSleep)) +
  geom_point() +
  geom_smooth() +
  xlab("Minutes Spent Working") +
  ylab ("Minutes Spent Sleeping")

  • Again we start with the ggplot() function, including telling it to use the dataset js_data
  • In the aes() function we list the x and y variables (here durWork and durSleep)
  • The geom_point() function is what makes a scatter plot
  • The geom_smooth() is what adds the correlation line to the plot
  • The xlab() and ylab() add the x-axis and y-axis labels to the plot

Improved Scatter Plot!

Now let’s add some layers and aesthetic adjustments to improve this plot.

ggplot(js_data, aes(durWork, durSleep)) +
  geom_point(color = "#2D5E7F", alpha = .2) +
  geom_smooth(method = lm, color = "black") +
  xlab("Minutes Spent Working") +
  ylab ("Minutes Spent Sleeping") +
  scale_x_continuous(breaks = seq(0, 1500, 250)) +
  labs(title = "Association Between Working and Sleeping") +
  theme(text = element_text(size = 18))

  • We can change the color of the data points by adding color = "#2D5E7F" and change the transparency of the data points (so you can see where there are overlapping clusters) with the addition of alpha = .2.
  • In the geom_smooth() function we changed the method for calculating the line to be linear (instead of the ggplot default) using method = lm and adjusted the color of the line with color =. Remember that you can write hex code numbers or the names of colors to adjust colors. Make sure both are in quotes to avoid error messages.
  • We adjust the x-axis tick marks with the scale_x_continuous(breaks = seq(0, 1500, 250)) part, which tells R to plot the x-axis on a sequence from 0 to 1500 (this captures all of the responses in our data), with labels every 250 minutes.
  • The labs(title = "Association Between Working and Sleeping") adds an overall title to the plot
  • Lastly, theme(text = element_text(size = 18)) adjust the text size

Try manipulating this plot in some way. Can you make the data points more and less transparent? Can you change the color of the line? What happens if the x-axis has labels every 100 minutes?

Your Turn!

Take a look at our data set and make two new plots.

  1. Make a plot comparing groups (i.e., a categorical variable) on one of the duration variables (i.e., a continuous variable).

  2. Make a plot comparing two continuous variables.

Challenge: What is one component of your plot you would like to change? Can you look up a solution?

LS0tCnRpdGxlOiAiUjogRGF0YSBWaXN1YWxpemF0aW9uIgpwYWdldGl0bGU6ICJSOiBEYXRhIFZpc3VhbGl6YXRpb24iCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgY29kZV9mb2xkaW5nOiBzaG93ICMgYWxsb3dzIHRvZ2dsaW5nIG9mIHNob3dpbmcgYW5kIGhpZGluZyBjb2RlLiBSZW1vdmUgaWYgbm90IHVzaW5nIGNvZGUuCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlICMgYWxsb3dzIHRoZSB1c2VyIHRvIGRvd25sb2FkIHRoZSBzb3VyY2UgLlJtZCBmaWxlLiBSZW1vdmUgaWYgbm90IHVzaW5nIGNvZGUuCiAgICBpbmNsdWRlczoKICAgICAgYWZ0ZXJfYm9keTogZm9vdGVyLmh0bWwgIyBpbmNsdWRlIGEgY3VzdG9tIGZvb3Rlci4KICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAzCiAgICB0b2NfZmxvYXQ6CiAgICAgIGNvbGxhcHNlZDogZmFsc2UKICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UKLS0tCgpgYGB7ciwgbGlicmFyaWVzLCBpbmNsdWRlID0gRkFMU0V9CmxpYnJhcnkoa2FibGVFeHRyYSkKbGlicmFyeShnZ3Bsb3QyKQpsaWJyYXJ5KGRwbHlyKQpgYGAKCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZ3MgPSBGQUxTRSkKYGBgCgpgYGB7ciwgaW5jbHVkZSA9IEZBTFNFfQpsb2FkKCJkYXRhL3RpbWV1c2VfZGF5M18yLlJEYXRhIikKYGBgCgoKCiMjIERhdGEgVmlzdWFsaXphdGlvbiBpbiBSCgo6OjppbnRybwpEYXRhIHZpc3VhbGl6YXRpb24gaXMgYW4gaW1wb3J0YW50IHBhcnQgb2YgZXhwbG9yaW5nLCB1bmRlcnN0YW5kaW5nLCBhbmQgc2hhcmluZyBvdXIgZGF0YS4KOjo6CgpEYXRhIHZpc3VhbGl6YXRpb24gaXMgYSBjcml0aWNhbCBwYXJ0IG9mIHRoZSBkYXRhIHNjaWVuY2Ugd29ya2Zsb3cuIFRocm91Z2ggdmlzdWFsaXphdGlvbiB3ZSBjYW4gZXhwbG9yZSBhbmQgdW5kZXJzdGFuZCBvdXIgb3duIGRhdGEsIHVsdGltYXRlbHkgaW5mb3JtaW5nIGZ1cnRoZXIgYW5hbHlzZXMuIEFkZGl0aW9uYWxseSwgZGF0YSB2aXN1YWxpemF0aW9ucyBhcmUgYSBwb3dlcmZ1bCB0b29sIGZvciBjb21tdW5pY2F0aW5nIGRhdGEgYW5kIHJlc2VhcmNoIGZpbmRpbmdzIHRvIG90aGVyIHBlb3BsZS4gVmlzdWFscyBjYW4gb2Z0ZW4gbW9yZSBlZmZpY2llbnRseSBhbmQgbW9yZSBlZmZlY3RpdmVseSB0ZWxsIHRoZSBzdG9yeSBvZiB5b3VyIGRhdGEsIHJhdGhlciB0aGFuIG9ubHkgcmVseWluZyBvbiB3cml0aW5nLiBXZWxsIGRvbmUgZGF0YSB2aXN1YWxpemF0aW9ucyB3aWxsIG9mdGVuIGhhdmUgdGhlIGJpZ2dlc3QgaW1wYWN0IG9uIGFuIGF1ZGllbmNlIGluIGEgc2NpZW5jZSBjb21tdW5pY2F0aW9uIGNvbnRleHQuIAoKIVtdKGltYWdlcy90aWR5dmVyc2Utd29ya2Zsb3cucG5nKQpbU291cmNlXShodHRwczovL3RlbGFwcHMubG9uZG9uLmVkdS9hbmFseXRpY3Nfd2l0aF9SL3RpZHl2ZXJzZS5odG1sKQoKOjo6bm90ZQpJbiB0aGlzIHNlc3Npb24gd2UgaGF2ZSBhIGZldyBtYWluIGdvYWxzOgoKMS4gSW50cm9kdWN0aW9uIHRvIHR5cGVzIG9mIGRhdGEgdmlzdWFsaXphdGlvbnMKCjIuIERpc2N1c3Npb24gb2Ygd2hhdCBtYWtlcyBhIGdvb2QgdmlzdWFsaXphdGlvbgoKMy4gUmV2aWV3IGFuZCBjcml0aXF1ZSBzb21lIGRhdGEgdmlzdWFsaXphdGlvbiBleGFtcGxlcwoKNC4gSW50cm9kdWN0aW9uIHRvIGdncGxvdCB0byBnZW5lcmF0ZSB2aXN1YWxpemF0aW9ucyBpbiBSCgo6OjoKCkxldCdzIGdldCBzdGFydGVkISAKCiMjIyBUeXBlcyBvZiBEYXRhIFZpc3VhbGl6YXRpb24KCkRhdGEgdmlzdWFsaXphdGlvbnMgY2FuIGJlIGRlc2NyaXB0aXZlIGluIG5hdHVyZSwgc3VjaCBhcyBwb3J0cmF5aW5nIHRoZSBkZW1vZ3JhcGhpYyBkaXN0cmlidXRpb24gb2YgYSBncm91cCBvZiBwZW9wbGUsIG9yIGNhbiByZXByZXNlbnQgc3RhdGlzdGljYWwgZmluZGluZ3MsIHN1Y2ggYXMgYSByZWdyZXNzaW9uIGxpbmUgd2l0aCBhIGNvbmZpZGVuY2UgaW50ZXJ2YWwgb3ZlcmxhaWQgb24gYSBzY2F0dGVyIHBsb3Qgb2YgZGF0YS4gSW4gY2VydGFpbiBjb250ZXh0cyBpdCBpcyBhbHNvIGNvbW1vbiB0byBwcmVzZW50IGRhdGEgYW5kIGluZm9ybWF0aW9uIGluIG1vcmUgZWFzaWx5IGRpZ2VzdGlibGUgaW5mb2dyYXBoaWNzIDxhIGhyZWY9Imh0dHBzOi8vY29vbGluZm9ncmFwaGljcy5jb20vIj4oY2hlY2sgb3V0IHNvbWUgZXhhbXBsZXMgaGVyZSk8L2E+IHRoYXQgZW50ZXIgbW9yZSBvZiBhIGdyYXBoaWMgZGVzaWduIHNwYWNlLiBEaWZmZXJlbnQgZGF0YSB2aXN1YWxpemF0aW9ucyBmdWxmaWxsIGRpZmZlcmVudCBnb2Fscy4gSGF2aW5nIGEgdG9vbGJveCBvZiBhIHZhcmlldHkgb2YgZGF0YSB2aXN1YWxpemF0aW9uIHR5cGVzIGNhbiBoZWxwIHlvdSBwaWNrIHRoZSBiZXN0IHR5cGUgdG8gZml0IHlvdXIgbmVlZHMgZm9yIGEgZ2l2ZW4gcHJvamVjdC4gCgojIyMjICoqVGFibGUqKgpUYWJsZXMgdG8gY29sbGVjdCBhbmQgb3JnYW5pemUgZGF0YSBhcmUgb25lIG9mIHRoZSBtb3N0IGNvbW1vbiBhbmQgYmFzaWMgZGF0YSB2aXN1YWxpemF0aW9ucy4gQnV0IGRvbid0IGRpc2NvdW50IHRoZW0hIFRoZXkgY2FuIGJlIGVmZmljaWVudCB3YXlzIHRvIGNvbnZleSBhIGxhcmdlIGFtb3VudCBvZiBpbmZvcm1hdGlvbiBhYm91dCB5b3VyIGRhdGEgYWxsIGF0IG9uY2UuIAoKIVtdKGltYWdlcy9kYXk0X2V4YW1wbGV0YWJsZS5wbmcpCgojIyMjICoqUGllIENoYXJ0KioKQSBjbGFzc2ljIHBpZSBjaGFydCBpcyB1c2VmdWwgZm9yIHJlcHJlc2VudGluZyBwYXJ0cyB0aGF0IGFkZCB1cCB0byBhIHdob2xlLiBUaGlzIGFsc28gYWxsb3dzIGZvciBjb21wYXJpbmcgZ3JvdXAgc2l6ZXMuIFRoZXkgYXJlIG9mdGVuIHVzZWQgZm9yIGRlbW9ncmFwaGljIHZhcmlhYmxlcywgd2l0aCB0aGUgd2hvbGUgcmVwcmVzZW50aW5nIHRoZSB3aG9sZSBzYW1wbGUsIG9yIGZvciByZXByZXNlbnRpbmcgbW9uZXksIHdpdGggdGhlIHdob2xlIHJlcHJlc2VudGluZyBhIGJ1ZGdldCBvciB0b3RhbCBtb25leSBzcGVudC9tYWRlLiBCZSBjYXJlZnVsIHRvIHVzZSBhIHBpZSBjaGFydCBvbmx5IHdoZW4gaXQgcmVhbGx5IGFkZHMgdG8gdGhlIHN0b3J5LiBGb3IgaW5zdGFuY2UsIGlmIHRoZXJlIGFyZSBvbmx5IHR3byBwYXJ0cyB0byB0aGUgd2hvbGUsIGEgcGllIGNoYXJ0IG1pZ2h0IG5vdCBjb252ZXkgbXVjaCBtb3JlIGluZm9ybWF0aW9uLiBDb252ZXJzZWx5LCBpZiB0aGVyZSBhcmUgbWFueSBncm91cHMsIGl0IGNhbiBiZWNvbWUgZGlmZmljdWx0IHRvIHJlYWxseSBzZWUgYWxsIG9mIHRoZW0gYW5kIGNvbXBhcmUgdGhlbS4gCgohW10oaW1hZ2VzL2RheTRfcGllY2hhcnRleGFtcGxlLnBuZykKCiMjIyMgKipCb3ggUGxvdHMqKgpCb3ggcGxvdHMgKGFsc28ga25vd24gYXMgYm94IGFuZCB3aGlza2VyIHBsb3RzKSBhcmUgZ29vZCBmb3IgdW5kZXJzdGFuZGluZyBhbmQgY29tcGFyaW5nIHZhcmlhbmNlIGJldHdlZW4gZ3JvdXBzLiBUaGV5IHR5cGljYWxseSBkZXBpY3QgdGhlIG1lZGlhbiBhbmQgbWluaW11bS9tYXhpbXVtIG9mIGVhY2ggZ3JvdXAgZm9yIGEgY2VydGFpbiB2YXJpYWJsZS4gVGhlIGJveCBwb3J0aW9uIHJlcHJlc2VudHMgdGhlIDFzdCBhbmQgM3JkIHF1YXJ0aWxlcyBvZiB0aGUgZGlzdHJpYnV0aW9uLiBZb3UgY2FuIGFsc28gc2hvdyBjaGFuZ2Ugb3ZlciB0aW1lIHdpdGggdGhpcyB0eXBlIG9mIHBsb3QuIAoKYGBge3IsIGVjaG8gPSBGQUxTRX0KZ2dwbG90KGpzX2RhdGEsIGFlcyh4PWFzLmZhY3Rvcihwcm92aW5jZV9mYWN0KSwgeT1kdXJTbGVlcCkpICsgCiAgICBnZW9tX2JveHBsb3QoZmlsbD0iIzJENUU3RiIsIGFscGhhPTAuNikgKyAKICAgIHhsYWIoIlByb3ZpbmNlIikgKwogICAgeWxhYigiTWludXRlcyBTbGVlcGluZyIpICsKIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkKYGBgCgojIyMjICoqSGlzdG9ncmFtKiogCkhpc3RvZ3JhbXMgZGlzcGxheSB0aGUgZGlzdHJpYnV0aW9uIG9mIG9uZSB2YXJpYWJsZS4gVGhlIGhlaWdodCBvZiB0aGUgYmFyIHJlcHJlc2VudHMgaG93IG1hbnkgdGltZXMgdGhhdCB2YWx1ZSB3YXMgcmVwcmVzZW50ZWQgaW4gdGhlIGRhdGEuIFRoaXMgaXMgdHlwaWNhbGx5IHdoYXQgeW91IHZpZXcgaWYgeW91IHdhbnQgdG8gdmlzdWFsbHkgaW5zcGVjdCBpZiBhIHZhcmlhYmxlIGlzIG5vcm1hbGx5IGRpc3RyaWJ1dGVkLiAKCmBgYCB7ciwgZWNobyA9IEZBTFNFfQoKZ2dwbG90KGpzX2RhdGEsIGFlcyh4PWR1clNsZWVwKSkgKyAKICBnZW9tX2hpc3RvZ3JhbShmaWxsID0gIiMyRDVFN0YiLCBjb2xvciA9ICJibGFjayIpKwogIHhsYWIoIk51bWJlciBvZiBNaW51dGVzIFNsZWVwaW5nIikgKwogIHlsYWIgKCJOdW1iZXIgb2YgUGFydGljaXBhbnRzIikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkKYGBgCgojIyMjICoqQmFyIENoYXJ0KiogCkJhciBjaGFydHMgYXJlIGdvb2QgZm9yIHJlcHJlc2VudGluZyBkYXRhIGZyb20gZ3JvdXBzIG9yIGNhdGVnb3JpZXMsIHdpdGggdGhlIGJhcnMgcmVwcmVzZW50aW5nIGRpZmZlcmVudCBjYXRlZ29yaWVzLiBUaGUgYmFyIGhlaWdodCB1c3VhbGx5IHJlcHJlc2VudCBhIG1lYW4sIGEgY291bnQsIG9yIGEgcGVyY2VudGFnZSwgYnkgY2F0ZWdvcnkuIFRoZXNlIGNhbiBiZSB1c2VmdWwgZm9yIGNvbXBhcmluZyBncm91cHMgb3Igc2hvd2luZyBjaGFuZ2Ugb3ZlciB0aW1lLiAKCmBgYCB7ciwgZWNobyA9IEZBTFNFfQpnZ3Bsb3QoanNfZGF0YSwgYWVzKHg9bWFyaXRhbFN0YXRfZmFjdCwgZmlsbCA9IGFzLmZhY3RvcihtYXJpdGFsU3RhdF9mYWN0KSkpICsKICBnZW9tX2JhcigpICsgCiAgeGxhYigiTWFyaXRhbCBTdGF0dXMiKSArCiAgeWxhYiAoIk51bWJlciBvZiBQYXJ0aWNpcGFudHMiKSArCiAgc2NhbGVfZmlsbF9odWUoYyA9IDYwKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLAogICAgICAgIHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDM1LCBoanVzdCA9IDEpKQpgYGAKCiMjIyMgKipTY2F0dGVyIFBsb3QqKiAKU2NhdHRlciBwbG90cyBpbmNsdWRlIGRhdGEgcG9pbnRzIHRoYXQgYXJlIHBsb3R0ZWQgYWxvbmcgYW4geCBhbmQgeSBheGlzLCBzaG93aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSB0d28gdmFyaWFibGVzLiBPZnRlbiByZXNlYXJjaGVycyBhZGQgYSBsaW5lIHRvIHRoZXNlIHBsb3RzIHRvIHNob3cgdGhlIHN0YXRpc3RpY2FsIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMuIAoKYGBge3IsIGVjaG8gPSBGQUxTRX0KZ2dwbG90KGpzX2RhdGEsIGFlcyhkdXJXb3JrLCBkdXJTbGVlcCkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gIiMyRDVFN0YiLCBhbHBoYSA9IC4yKSArCiAgZ2VvbV9zbW9vdGgobWV0aG9kPWxtLCBzZT1UUlVFLCBjb2xvciA9ICJibGFjayIpICsKICB4bGFiKCJNaW51dGVzIFNwZW50IFdvcmtpbmciKSArCiAgeWxhYiAoIk1pbnV0ZXMgU3BlbnQgU2xlZXBpbmciKSArCiAgbGFicyh0aXRsZSA9ICJBc3NvY2lhdGlvbiBCZXR3ZWVuIFdvcmtpbmcgYW5kIFNsZWVwaW5nIikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkKYGBgCgojIyMjICoqTGluZSBDaGFydCoqIApMaW5lIGNoYXJ0cyB1c2UgY29ubmVjdGVkIHN0cmFpZ2h0IGxpbmVzIHRvIGRpc3BsYXkgZGF0YS4gVGhleSBhcmUgZ29vZCBmb3Igc2hvd2luZyBjaGFuZ2Ugb3ZlciB0aW1lIG9uIGEgY29udGludW91cyB2YXJpYWJsZS4gVGhleSBhcmUgb2Z0ZW4gc2ltaWxhciBpbiBwdXJwb3NlIHRvIGJhciBjaGFydHMsIGJ1dCB2aXN1YWxseSBzaW1wbGVyIGlmIHRoZXJlIGFyZSBtYW55IHRpbWUgcG9pbnRzLiBBbiBleGFtcGxlIG9mIHdoZXJlIHdlIG9mdGVuIHNlZSBsaW5lIGNoYXJ0cyB1c2VkIGluIHRoZSBuZXdzIGlzIHRvIHZpc3VhbGl6ZSB0aGUgc3RvY2sgbWFya2V0LiAoUGxvdHRpbmcgdGhlIHZhcmlvdXMgYWdlIGdyb3VwcyBoZXJlIGlzIG5vdCBwYXJ0aWN1bGFybHkgbWVhbmluZ2Z1bCBzaW5jZSB0aGlzIGRhdGEgd2FzIG9ubHkgY29sbGVjdGVkIGF0IG9uZSB0aW1lIHBvaW50LCBidXQgaXMgdXNlZCB0byBpbGx1c3RyYXRlIHRoaXMgdHlwZSBvZiBkYXRhIHZpc3VhbGl6YXRpb24uKQoKYGBge3IsIGVjaG8gPSBGQUxTRX0KeFZhbHVlIDwtIDE6Nwp5VmFsdWUgPC0gYygxMzAzLCAyMTI3LCAyNTk3LCAyNzg5LCAzNzQxLCAyOTU4LCAxODc1KQpwbG90ZGF0YSA8LSBkYXRhLmZyYW1lKHhWYWx1ZSx5VmFsdWUpCgpnZ3Bsb3QocGxvdGRhdGEsIGFlcyh4PXhWYWx1ZSwgeT15VmFsdWUpKSArCiAgZ2VvbV9saW5lKGNvbG9yID0gIiMyRDVFN0YiKSArCiAgeGxhYigiQWdlIEdyb3VwcyIpICsKICB5bGFiICgiTnVtYmVyIG9mIFBhcnRpY2lwYW50cyIpICsKICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBQYXJ0aWNpcGFudHMgcGVyIEFnZSBHcm91cCIpICsKICB0aGVtZSh0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOCkpICsKICBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzID0gc2VxKDEsIDcsIDEpKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCA0MDAwLCA1MDApKQoKYGBgCgojIyMjICoqSW50ZXJhY3RpdmUgRGF0YSBWaXN1YWxpemF0aW9ucyoqIApBcyByZXNlYXJjaCBhbmQgcHVibGljYXRpb25zIG1vdmUgbW9yZSBvbmxpbmUgYW5kIGF3YXkgZnJvbSBwcmludCwgdGhpcyBjYW4gYWxsb3cgZm9yIG1vcmUgaW50ZXJhY3RpdmUgZGF0YSB2aXN1YWxpemF0aW9ucy4gVGhlc2UgYXJlIHR5cGVzIG9mIGRhdGEgdmlzdWFsaXphdGlvbnMgdGhhdCBhbGxvdyBhIHBlcnNvbiB0byBzZWxlY3QgYW5kIGNoYW5nZSB3aGF0IGlzIGJlaW5nIHNob3duLiBDaGVjayBvdXQgdGhpcyBncmVhdCBleGFtcGxlIGZyb20gPGEgaHJlZj0iaHR0cHM6Ly93d3cuZ2FwbWluZGVyLm9yZy90b29scy8jJGNoYXJ0LXR5cGU9YnViYmxlcyZ1cmw9djIpIj5HYXBtaW5kZXI8L2E+LiBUaGUgZGVmYXVsdCB2aXN1YWxpemF0aW9uIHNob3dzIEdEUCBhbmQgbGlmZSBleHBlY3RhbmN5IG92ZXIgdGltZSwgYW5kIGJ5IGNvdW50cnkuIEhvd2V2ZXIsIHlvdSBjYW4gY2hhbmdlIHRoZSB2YXJpYWJsZXMgaW5jbHVkZWQgdG8gdmlldyBvdGhlciBkYXRhLiAKCiMjIyMgKipTbyBtYW55IG1vcmUhKiogCi0gSGVhdCBNYXAKLSBTdGFja2VkIEJhciBvciBTdGFja2VkIEFyZWEgQ2hhcnQKLSBWaW9saW4gUGxvdAotIEdhbnR0IENoYXJ0Ci0gQ2hvcm9wbGV0aCBNYXAKCiMjIyBXaGF0IE1ha2VzIGEgR29vZCBWaXN1YWxpemF0aW9uPwpUaGVyZSBpcyBhbiBhcnQgdG8gcGlja2luZyB0aGUgYmVzdCBkYXRhIHZpc3VhbGl6YXRpb24gdGhhdCBmaXRzIHlvdXIgZGF0YSBhbmQgdGhlIHN0b3J5IHlvdSBhcmUgdGVsbGluZy4gQnkgbmF0dXJlLCBkYXRhIHZpc3VhbGl6YXRpb25zIGFyZSBhYnN0cmFjdCByZXByZXNlbnRhdGlvbnMgb2Ygb3VyIGRhdGEsIHdpdGggY29sb3IsIHNoYXBlLCBhbmQgcG9zaXRpb24gcmVwcmVzZW50aW5nIHRoZSBkYXRhIHBvaW50cy4gVGhpcyBib3RoIGhpZGVzIHRoZSBleGFjdCBkYXRhIGl0c2VsZiwgd2hpbGUgYWxzbyBhbGxvd2luZyB1cyB0byBoaWdobGlnaHQgYmlnZ2VyIHBpY3R1cmUgaWRlYXMgYWJvdXQgdGhlIGRhdGEsIGRlcGVuZGluZyBvbiB3aGF0IHdlIGNob29zZSB0byBlbXBoYXNpemUuIFdoZW4gZGVjaWRpbmcgb24gd2hhdCB0eXBlIG9mIGRhdGEgdmlzdWFsaXphdGlvbiB0byB1c2UsIGNvbnNpZGVyIHRoZSBmb2xsb3dpbmc6IAoKLSBXaGF0IHF1ZXN0aW9uIGFyZSB5b3UgZXhwbG9yaW5nIHdpdGggeW91ciBkYXRhIGFuZCBob3cgd2lsbCBpdCBpbmZvcm0gZnV0dXJlIGFuYWx5c2VzPwotIFdoYXQgaXMgeW91ciBkYXRhIHZpc3VhbGl6YXRpb24gYWRkaW5nIHRvIHlvdXIgc2NpZW5jZSBjb21tdW5pY2F0aW9uPyAKLSBXaGF0IHRha2UtYXdheSBtZXNzYWdlIGFyZSB5b3UgY29udmV5aW5nIHdpdGggdGhlIGltYWdlPyAKLSBXaGF0IHR5cGUgb2YgdmFyaWFibGVzIGRvIHlvdSBoYXZlPyBDb250aW51b3VzPyBDYXRlZ29yaWNhbD8gQW5kIHdoYXQgdHlwZSBvZiBhYnN0cmFjdGlvbiAoZS5nLiwgY29sb3IsIHNoYXBlKSBiZXN0IHN1aXRzIHRoYXQgdmFyaWFibGU/Ci0gQXJlIHlvdSBjb21wYXJpbmcgZ3JvdXBzPwotIEFyZSB5b3Ugc2hvd2luZyBjaGFuZ2Ugb3ZlciB0aW1lPwotIEFyZSB5b3UgdmlzdWFsaXppbmcgYSByZWxhdGlvbnNoaXAgYmV0d2VlbiB2YXJpYWJsZXM/CgpJdCBjYW4gYmUgZWFzeSB0byBnZXQgaW4gdGhlIGhhYml0IG9mIHVzaW5nIHRoZSBzYW1lIHR5cGVzIG9mIGRhdGEgdmlzdWFsaXphdGlvbiBvdmVyIGFuZCBvdmVyIGFnYWluLiBDaGVjayBvdXQgdGhpcyB3ZWJzaXRlIHRoYXQgZ2l2ZXMgbWFueSBjcmVhdGl2ZSBkYXRhIHZpc3VhbGl6YXRpb24gb3B0aW9ucyAod2l0aCBSIGNvZGUhKSwgY2F0ZWdvcml6ZWQgYnkgZ29hbDogPGEgaHJlZj0iaHR0cHM6Ly9yLWdyYXBoLWdhbGxlcnkuY29tLyAiPmh0dHBzOi8vci1ncmFwaC1nYWxsZXJ5LmNvbS8gPC9hPgoKIyMjIyBHb29kIERhdGEgVmlzdWFsaXphdGlvbiBQcmluY2ljcGxlcwoKT25jZSB5b3UgcGljayB0aGUgZm9ybWF0IHRoYXQgZml0cyB5b3VyIG5lZWRzIGJlc3QsIHRoZXNlIGFyZSBzb21lIHByaW5jaXBsZXMgdG8ga2VlcCBpbiBtaW5kIHdoZW4gY3JhZnRpbmcgeW91ciB2aXN1YWxpemF0aW9uIHRvIG1ha2Ugc3VyZSBpdCBpcyBjbGVhciB0byB5b3VyIGF1ZGllbmNlLgoKOjo6bWQtdGFibGUKfCBQcmluY2lwbGUgfCBDb25zaWRlcmF0aW9ucyB8CnwgOi0tLSB8IDotLS0gfAp8IENsZWFyIERhdGEgfCBDb25zaWRlciB0aGUgZm9ybWF0IG9mIHlvdXIgZGF0YSB3aGVuIHlvdSBpbmNsdWRlIGl0LiBZb3VyIGRhdGEgbmVlZHMgdG8gYmUgdW5hbWJpZ3VvdXNseSBjb21tdW5pY2F0ZWQgdG8geW91ciBhdWRpZW5jZS4gRm9yIGluc3RhbmNlLCBkbyB5b3UgaGF2ZSBzbyBtYW55IGdyb3VwcyB0aGF0IHRoZXkgYXJlIGRpZmZpY3VsdCB0byBkaWZmZXJlbnRpYXRlPyBBcmUgeW91ciBkYXRhIHBvaW50cyBzdGFja2VkIG9uIHRoZSBzYW1lIHNwb3Qgc28gdGhlIGF1ZGllbmNlIGNhbid0IHNlZSB0aGUgZGVuc2l0eSBvZiB5b3VyIGRhdGEgcG9pbnRzPyB8CnwgQ2xlYXIgTGFiZWxzIHwgQWxsIGRhdGEgdmlzdWFsaXphdGlvbnMgbmVlZCBsYWJlbHMuIFRoaXMgbWF5IGJlIHRoZSBheGlzIG9uIGEgc2NhdHRlciBwbG90LCBsZWdlbmRzIGZvciB5b3VyIGJhciBncmFwaCwgb3IgcGVyY2VudGFnZXMgb24geW91ciBwaWUgY2hhcnQuIFlvdSBtdXN0IHRlbGwgdGhlIGF1ZGllbmNlIHdoYXQgdGhleSBhcmUgbG9va2luZyBhdC4gSWYgZGlmZmVyZW50IGNvbG9ycyBhcmUgdXNlZCwgdGhleSBzaG91bGQgcmVwcmVzZW50IHNvbWUgYXNwZWN0IG9mIHRoZSBkYXRhIGFuZCBiZSBjbGVhcmx5IGxhYmVsZWQuIHwKfCBDbGVhciBTY2FsZXMgfCBDbGVhciBhbmQgY29uc2lzdGVudCBzY2FsZXMgYXJlIGltcG9ydGFudCB0byBhdm9pZCBtaXNpbnRlcnByZXRhdGlvbiBvZiB5b3VyIGRhdGEgdmlzdWFsaXphdGlvbi4gQW4gYXhpcyBzaG91bGQgYmUgY2xlYXJseSBsYWJlbGVkIGFuZCBpbmNsdWRlIHRoZSBmdWxsIHNjYWxlIHJhbmdlLiBNYWtlIGl0IGNsZWFyIGlmIHRoZSBzY2FsZSBkb2VzIG5vdCBzdGFydCBhdCAwLiBTY2FsZXMgc2hvdWxkIGJlIGNvbnNpc3RlbnQgYWNyb3NzIHZpc3VhbGl6YXRpb25zIHRvIGFsbG93IGZvciBjb21wYXJpc29ucy4gfAp8IFNpbXBsaWNpdHkgfCBBaW0gdG8gaGF2ZSB1bmNsdXR0ZXJlZCBkYXRhIHZpc3VhbGl6YXRpb25zLiBJdCBpcyBlYXN5IHRvIGdldCBleGNpdGVkIGFib3V0IGFsbCB5b3UgY2FuIGRvIGNyZWF0aXZlbHkgaW4gdGhlIHdvcmxkIG9mIGRhdGEgdmlzdWFsaXphdGlvbnMsIGJ1dCBzb21ldGltZXMgYWRkaW5nIHRvbyBtdWNoIChlLmcuLCBleHRyYSBjb2xvcnMsIHBpY3R1cmVzKSBjYW4gYWN0dWFsbHkgb2JzY3VyZSB5b3VyIG1haW4gbWVzc2FnZS4gQXZvaWQgZXh0cmEgaW5mbyB0aGF0IGRvZXNuJ3QgYWRkIHRvIHRoZSBzdG9yeSB5b3UgYXJlIHRlbGxpbmcuIHwKfCBBY2Nlc3NpYmlsaXR5IHwgQ29uc2lkZXIgaG93IHlvdXIgZGF0YSB2aXN1YWxpemF0aW9uIGRlc2lnbiB3b3VsZCBiZSB2aWV3ZWQgYnkgYSB2YXJpZXR5IG9mIHBlb3BsZS4gVGV4dCBmb250IGFuZCBzaXplIGZvciBsZWdlbmRzIGFuZCBheGlzIGxhYmVscyBzaG91bGQgYmUgY2xlYXIgYW5kIG5vdCB0b28gc21hbGwuIFRyeSBub3QgdG8gcmVseSBvbmx5IG9uIGNvbG9yIHRvIGRpc3Rpbmd1aXNoIGdyb3VwcyAoZS5nLiwgbGluZXMgY2FuIGJlIGRhc2hlZCBvciBkb3R0ZWQpLCBvciBwaWNrIGNvbG9ycy9odWVzIHRoYXQgYXJlIGRpc3Rpbmd1aXNoYWJsZSBieSBwZW9wbGUgd2hvIGFyZSBjb2xvcmJsaW5kLiBJbmNsdWRlIGFsdGVybmF0aXZlIHRleHQgZGVzY3JpcHRpb25zIHRoYXQgY2FuIGJlIHJlYWQgYWxvdWQgYnkgYSBzY3JlZW4gcmVhZGVyLiB8Cjo6OgoKRm9yIG1vcmUgaW5mb3JtYXRpb24gb24gZGVzaWduIHByaW5jaXBsZXMgYW5kIHRoZSB2aXN1YWwgaGllcmFyY2h5IG9mIGVsZW1lbnRzLCBjaGVjayBvdXQgPGEgaHJlZj0iaHR0cHM6Ly93d3cuaW50ZXJhY3Rpb24tZGVzaWduLm9yZy9saXRlcmF0dXJlL3RvcGljcy92aXN1YWwtaGllcmFyY2h5P3Nyc2x0aWQ9QWZtQk9vb1U3N1hQaVZzWFNrRTd0MkdxQWF5YU95aDBWeGR3R2ozYkphUDFRajN4Y2M1QTQ0QlciPnRoaXMgYXJ0aWNsZTwvYT4uIFNpemUsIGNvbG9yLCBjb250cmFzdCwgYWxpZ25tZW50LCByZXBldGl0aW9uLCBwcm94aW1pdHksIHdoaXRlc3BhY2UsIGFuZCB0ZXh0dXJlIGNhbiBhbGwgYmUgdXNlZCB0byBkcmF3IGZvY3VzIHRvIHBhcnRpY3VsYXIgdmlzdWFsIGVsZW1lbnRzLiAKCgojIyMgWW91ciBUdXJuIQpPbmUgb2YgdGhlIGJlc3Qgd2F5cyB0byBnZXQgaW50byBkYXRhIHZpc3VhbGl6YXRpb24gaXMgdG8gZ2V0IGluc3BpcmVkIGZyb20gc29tZSBvZiB0aGUgYW1hemluZyBkYXRhIHZpc3VhbGl6YXRpb25zIHRoYXQgYWxyZWFkeSBleGlzdCEgCgpXZSBoYXZlIHR3byB3ZWJzaXRlcyB3aXRoIGEgdmFyaWV0eSBvZiBkYXRhIHZpc3VhbGl6YXRpb24gZXhhbXBsZXMuIAoKLSA8YSBocmVmPSJodHRwczovL3ItZ3JhcGgtZ2FsbGVyeS5jb20vYmVzdC1yLWNoYXJ0LWV4YW1wbGVzIj5SIEdyYXBoIEdhbGxlcnk8L2E+CkluY2x1ZGVzIG1hbnkgZXhhbXBsZXMgb2YgZGF0YSB2aXN1YWxpemF0aW9ucyBjcmVhdGVkIGluIFIuIFRoZXNlIGFsc28gaW5jbHVkZSB0dXRvcmlhbHMvUiBDb2RlIHVzZWQgdG8gbWFrZSB0aGVtLiBUaGVzZSBleGFtcGxlcyBnbyBiZXlvbmQgd2hhdCB3ZSB3aWxsIGNvdmVyIGluIHRoaXMgd29ya3Nob3AsIGJ1dCBzZXJ2ZSBhcyBncmVhdCBpbnNwaXJhdGlvbiBmb3IgaG93IG11Y2ggeW91IGNhbiBkbyBpbiBSIQoKLSA8YSBocmVmPSJodHRwczovL3d3dy50YWJsZWF1LmNvbS92aXotZ2FsbGVyeSI+VGFibGVhdSBWaXogR2FsbGVyeTwvYT4KSW5jbHVkZXMgZXhhbXBsZXMgY3JlYXRlZCB3aXRoIHRoZSBwbGF0Zm9ybSBUYWJsZWF1LiBUaGlzIGlzIGEgcHJvcHJpZXRhcnkgKHBhaWQpIHBsYXRmb3JtIHdoaWNoIHdlIGFyZSBub3QgdXNpbmcgaW4gdGhpcyB3b3Jrc2hvcCwgYnV0IHRoaXMgZ2FsbGVyeSBzdGlsbCBoYXMgc29tZSBncmVhdCBleGFtcGxlcyB0byBoZWxwIGluc3BpcmUgeW91ciBkYXRhIHZpc3VhbGl6YXRpb24gY3JlYXRpdml0eSEgCgo6OjpxdWVzdGlvbgpJbiBzbWFsbCBncm91cHMsIG9uIGVpdGhlciBvZiB0aGVzZSB3ZWJzaXRlcywgZmluZCBhIGRhdGEgdmlzdWFsaXphdGlvbiBleGFtcGxlIHRoYXQganVtcHMgb3V0IHRvIHlvdSBhcyBpbnRlcmVzdGluZyBhbmQgdGhlbiBhbnN3ZXIgdGhlIGZvbGxvd2luZyBxdWVzdGlvbnMuIAoKMS4gV2hhdCBjYXB0dXJlZCB5b3VyIGF0dGVudGlvbiBhYm91dCB0aGlzIGRhdGEgdmlzdWFsaXphdGlvbj8gVGhlIHRvcGljPyBUaGUgZGVzaWduPyAKMi4gV2hhdCBpcyB0aGUgc3RvcnkgdGhpcyBleGFtcGxlIGlzIHRyeWluZyB0byB0ZWxsPyBXaGF0IGlzIG9uZSBvZiB0aGUgdGFrZS1hd2F5IG1lc3NhZ2VzIGl0IGlzIGNvbnZleWluZz8KMy4gV2hhdCBkbyB5b3UgbGlrZSBhYm91dCB0aGlzIHZpc3VhbGl6YXRpb24/IERvZXMgaXQgY29udmV5IGluZm9ybWF0aW9uIGFib3V0IHRoZSBkYXRhIGluIGEgdW5pcXVlbHkgZWZmZWN0aXZlIHdheT8gRG9lcyBpdCBhZGhlcmUgdG8gdGhlIHByaW5jaXBsZXMgd2UgZGlzY3Vzc2VkPyAKNC4gSXMgdGhlcmUgYW55dGhpbmcgeW91IGZpbmQgY29uZnVzaW5nIGFib3V0IHRoaXMgZXhhbXBsZT8gQW55dGhpbmcgeW91IHRoaW5rIGlzIG1pc3Npbmcgb3IgdGhhdCB5b3Ugd291bGQgY2hhbmdlIHRvIGltcHJvdmUgaXQ/IAo6OjoKCiMjIyBnZ3Bsb3QgUiBQYWNrYWdlCgpOb3cgdGhhdCB3ZSBoYXZlIG91ciBkYXRhIHZpc3VhbGl6YXRpb24gaW1hZ2luYXRpb25zIGdvaW5nLCBsZXQncyBnZXQgaW50byBob3cgd2UgY2FuIHZpc3VhbGx5IHJlcHJlc2VudCBvdXIgZGF0YSBpbiBSLgoKVGhlIGdvLXRvIHBhY2thZ2UgZm9yIGRhdGEgdmlzdWFsaXphdGlvbiBpbiBSIGlzIGBnZ3Bsb3QyYCwgd2hpY2ggaXMgcGFydCBvZiB0aGUgdGlkeXZlcnNlLiBZb3UgY2FuIGZpbmQgbW9yZSBpbmZvcm1hdGlvbiBhYm91dCBgZ2dwbG90MmAgb24gdGhlIDxhIGhyZWY9Imh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLyI+dGlkeXZlcnNlIHdlYnNpdGU8L2E+LgoKVGhpcyBwYWNrYWdlIGFwcHJvYWNoZXMgZGF0YSB2aXN1YWxpemF0aW9uIHRocm91Z2ggImEgZ3JhbW1hciBvZiBncmFwaGljcy4iIEluIG90aGVyIHdvcmRzLCB1c2luZyB0aGUgc2FtZSBzeW50YXgsIHlvdSBjYW4gY3JlYXRlIGFuIGluZmluaXRlIG51bWJlciBvZiBkYXRhIHZpc3VhbGl6YXRpb25zLiBBbHRob3VnaCB0aGVyZSBhcmUgYSBsb3Qgb2YgZnVuY3Rpb25zIGFuZCBjb21wb25lbnRzIHRvIGxlYXJuIGF0IGZpcnN0LCBvbmNlIHlvdSB1bmRlcnN0YW5kIHRoZSBvdmVyYWxsIHN0cnVjdHVyZSBvZiBidWlsZGluZyBncmFwaGljcyBpbiBgZ2dwbG90MmAsIHlvdSBjYW4gcmVwbGljYXRlIGFuZCBleHBhbmQgb24gdGhpcyBzdHJ1Y3R1cmUgdG8gdmlzdWFsaXplIGRhdGEgaW4gYW4gdW5saW1pdGVkIG51bWJlciBvZiB3YXlzLiAKCklmIHlvdSBoYXZlIGluc3RhbGxlZCB0aGUgdGlkeXZlcnNlLCB0aGVuIGBnZ3Bsb3QyYCBpcyBpbmNsdWRlZC4gT3RoZXJ3aXNlIHlvdSBjYW4gaW5zdGFsbCBpdCBub3cuIExldCdzIGFsc28gbG9hZCBvdXIgZGF0YXNldCBmb3IgdG9kYXkuCgpgYGB7cn0KI2luc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQojbGlicmFyeShnZ3Bsb3QyKQpgYGAKClRoZXJlIGFyZSBtYW55IHdheXMgdG8gbWFrZSBkYXRhIHZpc3VhbGl6YXRpb25zIGluIFI7IGhvd2V2ZXIsIG90aGVyIGFwcHJvYWNoZXMgdGVuZCB0byBiZSBtb3JlIGF1dG9tYXRpYyBhbmQgY29uc2VxdWVudGx5IGxpbWl0IHRoZSBhbW91bnQgeW91IGNhbiBjaGFuZ2UgYW5kIGFkYXB0IHlvdXIgdmlzdWFsaXphdGlvbiB0byB5b3VyIG5lZWRzLiBgZ2dwbG90MmAgd29ya3MgaW4gbGF5ZXJzLCBhbGxvd2luZyBmb3IgbWF4aW11bSBjb250cm9sIGFuZCBmbGV4aWJpbGl0eS4gCgpIZXJlIGFyZSBzb21lIG9mIHRoZSBtb3N0IGNvbW1vbiBsYXllcnMgKGkuZS4sIGZ1bmN0aW9ucykgdXNlZCBpbiBgZ2dwbG90MmAuIFR5cGljYWxseSB5b3UgY29ubmVjdCB0aGVzZSBsYXllcnMgdXNpbmcgdGhlIGArYCBzeW1ib2wuIFRoZXJlIGlzIG9mdGVuIG1vcmUgdGhhbiBvbmUgd2F5IHRvIGJ1aWxkIHRoZSBzYW1lIHBsb3Qgd2l0aCB0aGUgYGdncGxvdGAgcGFja2FnZS4gCgoqKi0gYGdncGxvdCgpYDoqKiBob3cgeW91IHdpbGwgc3RhcnQgbW9zdCBwbG90cyB5b3UgYnVpbGQgaW4gYGdncGxvdDJgLiBUaGUgcmVzdCBvZiB0aGUgaW5mb3JtYXRpb24gZ29lcyB3aXRoaW4gdGhpcyBmdW5jdGlvbi4KCioqLSBgYWVzKClgOioqIHRoaXMgaXMgdGhlIGFlc3RoZXRpYyBtYXBwaW5nIGZ1bmN0aW9uLCBpbiB3aGljaCB5b3UgY2FuIGNvbnRyb2wgYWVzdGhldGljIGNvbXBvbmVudHMgb2YgdGhlIHBsb3QuIFlvdSBjYW4gYWRkIGNvbG9ycywgYXhpcyBsYWJlbHMsIGZvbnQgc2l6ZXMgYW5kIG1vcmUgd2l0aGluIHRoaXMgZnVuY3Rpb24uIENvbG9yIGFuZCBzaGFwZSBjYW4gYmUgZGVmaW5lZCBib3RoIHdpdGhpbiBhbmQgb3V0c2lkZSBvZiB0aGUgYWVzdGhldGljIGZ1bmN0aW9uLiAKCioqLSBgZ2VvbV9wb2ludCgpYDoqKiB1c2VkIGZvciBtYWtpbmcgYSBzY2F0dGVyIHBsb3QKCioqLSBgZ2VvbV9saW5lKClgOioqIHVzZWQgZm9yIGFkZGluZyBhIGxpbmUgdG8gYSBwbG90CgoqKi0gYGdlb21faGlzdG9ncmFtKClgOioqIHVzZWQgZm9yIG1ha2luZyBhIGhpc3RvZ3JhbQoKKiotIGBnZW9tX2NvbCgpYDoqKiB1c2VkIGZvciBtYWtpbmcgYSBiYXIgcGxvdAoKKiotIGB4bGFiYCwgYHlsYWJgLCBhbmQgYGxhYnModGl0bGUgPSApYDoqKiB1c2VkIGZvciBhZGRpbmcgYXhpcyBsYWJlbHMgYW5kIGFuIG92ZXJhbGwgdGl0bGUgdG8gcGxvdHMKCioqLSBgY29sb3JgOioqIGFzc2lnbnMgYSBjb2xvciB0byBwYXJ0IG9mIHRoZSBwbG90LCBzdWNoIGFzIGRpZmZlcmVudCBncm91cHMgb3IgdGhlIGRhdGEgcG9pbnRzCgoqKi0gYGZpbGxgOioqIGFzc2lnbnMgdGhlIGludGVyaW9yIGNvbG9yIG9mIHBhcnQgb2YgdGhlIHBsb3QsIHN1Y2ggYXMgYSBjb25maWRlbmNlIGJhbmQgb3IgdGhlIGJhcnMgaW4gYmFyIHBsb3RzCgoqKi0gYGFscGhhYDoqKiB1c2VkIHRvIGNoYW5nZSB0aGUgdHJhbnNwYXJlbmN5IG9mIGEgY29tcG9uZW50IG9mIHRoZSBwbG90LiBVc2VmdWwgaWYgeW91IGxvdHMgb2YgaGF2ZSBvdmVybGFwcGluZyBkYXRhIHBvaW50cyBvciBkaXN0cmlidXRpb25zIGZyb20gbXVsdGlwbGUgZ3JvdXBzLiAKCioqLSBgc2l6ZWA6KiogdXNlZCB0byBzZXQgdGhlIHNpemUgb2YgcGFydCBvZiB0aGUgcGxvdCwgc3VjaCBhcyBob3cgYmlnIHRoZSBkYXRhIHBvaW50cyBvciB0ZXh0IHNob3VsZCBiZQoKVGhlcmUgYXJlIG1hbnkgbW9yZSBkYXRhIHZpc3VhbGl6YXRpb24gb3B0aW9ucyBpbiBgZ2dwbG90YCBidXQgdG8gZ2V0IHN0YXJ0ZWQgdG9kYXkgd2UgYXJlIGdvaW5nIHRvIGZvY3VzIG9uIG1ha2luZyBhIGJhciBwbG90IChnb29kIGZvciBjYXRlZ29yaWNhbCBkYXRhKSBhbmQgYSBzY2F0dGVyIHBsb3QgKGdvb2QgZm9yIGNvbnRpbnVvdXMgZGF0YSkuIAoKIyMjIEJhc2ljIEJhciBQbG90OiBDb3VudHMKTGV0J3MgbWFrZSBvdXIgZmlyc3QgcGxvdCBpbiBSISBXZSBhcmUgZ29pbmcgdG8gc2xvd2x5IGFkZCBsYXllcnMsIGJ1aWxkaW5nIHVwIHRvIGEgYm94IHBsb3QgcmVwcmVzZW50aW5nIHRoZSBudW1iZXIgb2YgcGVvcGxlIGluIGVhY2ggZ3JvdXAgaW4gdGhlIGBpc0ZlZWxSdXNoZWRgIHZhcmlhYmxlLiAKCkZpcnN0IHdlIGNyZWF0ZSB0aGUgYmxhbmsgcGxvdCBvbiB3aGljaCB3ZSB3aWxsIGFkZCBvdXIgZGF0YS4gV2UgbmVhcmx5IGFsd2F5cyBzdGFydCB3aXRoIHRoZSBmdW5jdGlvbiBgZ2dwbG90YCBhbmQgdGhlbiB0ZWxsaW5nIHRoZSBmdW5jdGlvbiB3aGF0IGRhdGFzZXQgdG8gdXNlLiAKCmBgYHtyfQpnZ3Bsb3QoanNfZGF0YSkKYGBgCgpUaGlzIGNyZWF0ZXMgb3VyIGJsYW5rIGNhbnZhcy4KCk5leHQgd2UgdGVsbCBnZ3Bsb3Qgd2hhdCB2YXJpYWJsZSB3ZSB3YW50IHRvIHVzZSBhbmQgcHV0IGl0IGluIHRoZSBgYWVzKClgIGZ1bmN0aW9uLiBJZiB5b3Ugd2FudCBhIGJhciBjaGFydCByZXByZXNlbnRpbmcgdGhlIG51bWJlciBvZiBwZW9wbGUgaW4gZWFjaCBncm91cCwgeW91IGNhbiBhZGQganVzdCBvbmUgdmFyaWFibGUgdG8gdGhlIGBhZXMoKWAgZnVuY3Rpb24uIFdlIGluY2x1ZGUgdGhlIGBhcy5mYWN0b3JgIGZ1bmN0aW9uIGFyb3VuZCB0aGUgYGlzRmVlbFJ1c2hlZGAgdmFyaWFibGUgc28gaXQgaXMgdHJlYXRlZCBhcyBhIGNhdGVnb3JpY2FsIHZhcmlhYmxlIGZvciB0aGUgYm94IHBsb3QgaW5zdGVhZCBvZiBhIG51bWVyaWMgdmFyaWFibGUuIAoKYGBge3J9CmdncGxvdChqc19kYXRhLCBhZXMoeCA9IGFzLmZhY3Rvcihpc0ZlZWxSdXNoZWQpKSkKYGBgCgpZb3Ugbm93IHNlZSB0aGF0IHRoZSBncmlkIHJlcHJlc2VudHMgYSBzY2FsZSByZWxldmFudCB0byB0aGF0IHZhcmlhYmxlLiAKCk5leHQsIHdlIHRlbGwgZ2dwbG90IHdoYXQgdHlwZSBvZiBkYXRhIHZpc3VhbGl6YXRpb24gd2Ugd2FudC4gVG8gY3JlYXRlIGEgYmFyIGNoYXJ0IHdlIHVzZSB0aGUgZnVuY3Rpb24gYGdlb21fYmFyKClgLiBUbyBzZWUgaG93IG1hbnkgcGVvcGxlIGFyZSBpbiBlYWNoIG9mIHRoZSBgaXNGZWVsUnVzaGVkYCBncm91cHMsIHdlIHVzZSB0aGUgZGVmYXVsdCBgZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIpYC4gUmVtZW1iZXIgdGhhdCB3ZSBjb25uZWN0IGxheWVycyB3aXRoIGEgYCtgIHN5bWJvbC4gCgpgYGB7cn0KZ2dwbG90KGpzX2RhdGEsIGFlcyh4ID0gYXMuZmFjdG9yKGlzRmVlbFJ1c2hlZCkpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJjb3VudCIpCmBgYAoKV2UndmUgZ290IGEgYmFyIGNoYXJ0IQoKTGFzdGx5LCBsZXQncyBhZGQgc29tZSBsYWJlbHMgdG8gdGhlIHgtYXhpcyBhbmQgeS1heGlzIHRvIG1ha2UgaXQgY2xlYXIgd2hhdCBpcyBiZWluZyBwbG90dGVkLiAKCmBgYHtyfQpnZ3Bsb3QoanNfZGF0YSwgYWVzKHggPSBhcy5mYWN0b3IoaXNGZWVsUnVzaGVkKSkpICsKICBnZW9tX2JhcihzdGF0ID0gImNvdW50IikgKyAKICB4bGFiKCJGZWVsaW5nIFJ1c2hlZCIpICsKICB5bGFiICgiTnVtYmVyIG9mIFBhcnRpY2lwYW50cyIpIApgYGAKCiMjIyBCYXNpYyBCYXIgUGxvdDogR3JvdXAgTWVhbnMKCkJ1aWxkaW5nIGZyb20geWVzdGVyZGF5LCBsZXQncyBzZWUgaWYgcGVvcGxlIHdobyBmZWVsIHJ1c2hlZCB0ZW5kIHRvIHdvcmsgbW9yZSB0aGFuIHBlb3BsZSB3aG8gZG8gbm90IGZlZWwgcnVzaGVkLiBUbyByZXByZXNlbnQgdGhlIG1lYW4gZm9yIGVhY2ggZ3JvdXAgb3Igc29tZSBvdGhlciB2YXJpYWJsZSwgeW91IGFkZCBib3RoIGFuIHggYW5kIGEgeSB2YXJpYWJsZSB0byB0aGUgYGFlcygpYCBmdW5jdGlvbiBhbmQgdXNlIHRoZSBgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IilgLiBOb3RlIHRoYXQgdGhlIFktYXhpcyBzY2FsZSBoYXMgbm93IGFkanVzdGVkIHRvIGEgc2NhbGUgdGhhdCBtYXRjaGVzIHRoZSB2YXJpYWJsZSB3ZSBhcmUgdXNpbmcgKGkuZS4sIG1lYW4gbnVtYmVyIG9mIG1pbnV0ZXMgc3BlbnQgd29ya2luZyBmb3IgZWFjaCBncm91cCkuIAoKYGBge3J9CmdncGxvdChqc19kYXRhLCBhZXMoeCA9IGFzLmZhY3Rvcihpc0ZlZWxSdXNoZWQpLCB5ID0gZHVyV29yaykpICsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiKSArIAogIHhsYWIoIkZlZWxpbmcgUnVzaGVkIikgKwogIHlsYWIgKCJBdmVyYWdlIE1pbnV0ZXMgV29ya2luZyIpIApgYGAKCjo6OndhbGt0aHJvdWdoCiAgLSBNb3N0IHBsb3RzIHdpbGwgc3RhcnQgd2l0aCB0aGUgYGdncGxvdCgpYGZ1bmN0aW9uCiAgLSBZb3UgaGF2ZSB0byBpbmNsdWRlIHRoZSBvYmplY3Qgd2hlcmUgYGdncGxvdCgpYCB3aWxsIGdldCB0aGUgaW5mb3JtYXRpb24gZm9yIHRoZSBwbG90IGZyb20uIEluIHRoaXMgY2FzZSwgaXQncyBvdXIgZGF0YXNldCBganNfZGF0YWAKICAtIFdpdGhpbiB0aGUgYGFlcygpYCBmdW5jdGlvbiwgd2UgaWRlbnRpZnkgd2hhdCB0aGUgeCBhbmQgeSB2YXJpYWJsZXMgYXJlIGZvciB0aGlzIHBsb3QKICAtIFdlIGFkZCBsYXllcnMgdXNpbmcgdGhlIGArYCBzaWduCiAgLSBOZXh0IHdlIHRlbGwgYGdncGxvdCgpYCB3aGF0IHR5cGUgb2YgcGxvdCB3ZSBhcmUgbWFraW5nOyBpbiB0aGlzIGNhc2Ugd2UgYXJlIGNyZWF0aW5nIGEgYmFyIHBsb3QgdXNpbmcgdGhlIGZ1bmN0aW9uIGBnZW9tX2JhcigpYAogIC0gVGhlbiB3ZSBhZGQgd2hhdCB0eXBlIG9mIHN0YXRpc3RpYyB3ZSB3YW50IHByZXNlbnRlZCBvbiB0aGUgcGxvdC4gSGVyZSB3ZSBhc2sgZm9yIHRoZSBtZWFuIG9mIGVhY2ggZ3JvdXAgZm9yIHRoZSB2YXJpYWJsZSBkdXJXb3JrIHVzaW5nIHRoZSBmdW5jdGlvbiBgZ2VvbV9iYXIoc3RhdCA9ICJzdW1tYXJ5IilgCiAgLSBXZSBhZGQgYSBsYWJlbCB0byB0aGUgeC1heGlzIHdpdGggYGxhYigiRmVlbGluZyBSdXNoZWQiKWAKICAtIFdlIGFkZCB0aGUgbGFiZWwgdG8gdGhlIHktYXhpcyB3aXRoIGB5bGFiICgiVGltZSBXb3JraW5nIilgCiAgCjo6OgoKIyMjIEltcHJvdmVkIEJveCBQbG90ISAKVGhpcyBwbG90IGdldHMgdGhlIGlkZWEgYWNyb3NzLCBidXQgd2UgY2FuIGFkZCBtb3JlIGxheWVycyBhbmQgZnVuY3Rpb25zIHRvIG1ha2UgbW9yZSBhZGp1c3RtZW50cy4gU2VlIHRoZSB3YWxrdGhyb3VnaCBiZWxvdyBmb3IgaG93IHdlIG1hZGUgYWxsIHRoZXNlIGNoYW5nZXMuCgpgYGB7cn0KCnBsb3RsYWJlbHMgPC0gYygiTm90IFJ1c2hlZCIsICJSdXNoZWQiLCAiRGlkIE5vdCBSZXNwb25kIikKCmdncGxvdChqc19kYXRhLCBhZXMoeCA9IGFzLmZhY3Rvcihpc0ZlZWxSdXNoZWQpLCB5ID0gZHVyV29yaykpICsKICBnZW9tX2JhcihzdGF0ID0gInN1bW1hcnkiLCBmaWxsID0gIiMyRDVFN0YiKSArIAogIHhsYWIoIkZlZWxpbmcgUnVzaGVkIikgKwogIHlsYWIgKCJBdmVyYWdlIE1pbnV0ZXMgV29ya2luZyIpICsKICBsYWJzKHRpdGxlID0gIldvcmtpbmcgYW5kIEZlZWxpbmcgUnVzaGVkIikgKwogIHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gcGxvdGxhYmVscykgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSwKICAgICAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI1LCBoanVzdCA9IDEpKQogIApgYGAKCjo6OndhbGt0aHJvdWdoCiAgLSBXZSBjYW4gY2hhbmdlIHRoZSBjb2xvciBvZiB0aGUgYmFycyB1c2luZyBgZmlsbCA9IGAuIEhlcmUgd2UgYWRkZWQgdGhlIHNwZWNpZmljIGNvbG9yIHVzaW5nIGEgaGV4IGNvZGUuIEJ1dCB5b3UgY2FuIGFsc28gd3JpdGUgaW4gdGhlIG5hbWVzIG9mIGNvbG9ycyBzdWNoIGFzICJibHVlIi4KICAtIFVzaW5nIGBsYWJzKHRpdGxlID0gIiIpYCB3ZSBhZGRlZCBhbiBvdmVyYWxsIHRpdGxlIHRvIHRoZSBwbG90CiAgLSBXZSBwcm9iYWJseSB3YW50IHRvIGluZGljYXRlIHdoYXQgZWFjaCBjYXRlZ29yeSByZXByZXNlbnRzLCByYXRoZXIgdGhhbiB0aGUgIjAiLCAiMSIgYW5kICJOQSIgbGFiZWxzLiBUbyBhZGQgdGV4dCBsYWJlbHMsIHdlIGZpcnN0IGNyZWF0ZSBhbiBvYmplY3Qgd2l0aCBlYWNoIG9mIHRob3NlIGxhYmVscyBpbiBvcmRlciAoYHBsb3RsYWJlbHMgPC0gYygiTm90IFJ1c2hlZCIsICJSdXNoZWQiLCAiRGlkIE5vdCBSZXNwb25kIilgKS4gVGhlbiBpbiB0aGUgYHNjYWxlX3hfZGlzY3JldGUobGFiZWxzID0gcGxvdGxhYmVscylgIGZ1bmN0aW9uIHdlIGNhbGwgdG8gdGhhdCBvYmplY3Qgd2UgY3JlYXRlZCBhcyB0aGUgbGFiZWxzIGZvciB0aGUgeC1heGlzLiBUaGVyZSBhcmUgbWFueSBvdGhlciB3YXlzIHRvIGFkanVzdCB0aGUgbGFiZWxzIGZvciBlYWNoIGF4aXMsIGJ1dCB0aGlzIG1ldGhvZCB3b3JrcyB3ZWxsIGZvciBhIHNtYWxsIG51bWJlciBvZiBncm91cHMuCiAgLSBJbiB0aGUgYHRoZW1lKClgIGxheWVyIHlvdSBjYW4gYWRkIG1hbnkgZGlmZmVyZW50IHNwZWNpZmljYXRpb25zLiBIZXJlIHdlIGFkZGVkIGB0ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxOClgIHRvIG1ha2UgdGhlIHRleHQgc2l6ZSBiaWdnZXIgdGhhbiB0aGUgZGVmYXVsdCBhbmQgbWFkZSB0aGUgeC1heGlzIGxhYmVscyBhbmdsZWQgc28gdGhleSBmaXQgYmV0dGVyIHVzaW5nIGBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDI1LCBoanVzdCA9IDEpYC4gVGhlIGBoanVzdCA9IGAgYWRqdXN0cyB0aGUgdmVydGljYWwgbG9jYXRpb24gb2YgdGhlIGF4aXMgbGFiZWxzIHNvIHRoZXkgZG9uJ3Qgb3ZlcmxhcCB3aXRoIHRoZSBwbG90IGl0c2VsZi4gYHZqdXN0ID0gYCBjYW4gYmUgdXNlZCB0byBtb3ZlIHRoZSBsYWJlbHMgcmlnaHQgYW5kIGxlZnQuIAogIAo6OjoKCjo6OnF1ZXN0aW9uClRyeSBtYW5pcHVsYXRpbmcgdGhpcyBwbG90IGluIHNvbWUgd2F5LiBDYW4geW91IGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGJhcnM/IFdoYXQgaGFwcGVucyBpZiB5b3UgY2hhbmdlIGBhbmdsZSA9IDkwYD8KOjo6CgojIyMgQmFzaWMgU2NhdHRlciBQbG90Ck5vdyBsZXQncyBtYWtlIGEgc2NhdHRlciBwbG90IHRvIHZpc3VhbGl6ZSB0d28gY29udGludW91cyB2YXJpYWJsZXMuIFdlIGFyZSBnb2luZyB0byBjaGVjayBpZiBpdCBsb29rcyBsaWtlIHRoZXJlIGlzIGEgY29ycmVsYXRpb24gYmV0d2VlbiBob3cgbXVjaCB0aW1lIHBlb3BsZSB3b3JrIChkdXJXb3JrKSBhbmQgaG93IG11Y2ggdGhleSBzbGVlcCAoZHVyU2xlZXApLgoKYGBge3J9CmdncGxvdChqc19kYXRhLCBhZXMoZHVyV29yaywgZHVyU2xlZXApKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aCgpICsKICB4bGFiKCJNaW51dGVzIFNwZW50IFdvcmtpbmciKSArCiAgeWxhYiAoIk1pbnV0ZXMgU3BlbnQgU2xlZXBpbmciKQogICAgICAKYGBgCgo6Ojp3YWxrdGhyb3VnaAogIC0gQWdhaW4gd2Ugc3RhcnQgd2l0aCB0aGUgYGdncGxvdCgpYCBmdW5jdGlvbiwgaW5jbHVkaW5nIHRlbGxpbmcgaXQgdG8gdXNlIHRoZSBkYXRhc2V0IGBqc19kYXRhYAogIC0gSW4gdGhlIGBhZXMoKWAgZnVuY3Rpb24gd2UgbGlzdCB0aGUgeCBhbmQgeSB2YXJpYWJsZXMgKGhlcmUgZHVyV29yayBhbmQgZHVyU2xlZXApCiAgLSBUaGUgYGdlb21fcG9pbnQoKWAgZnVuY3Rpb24gaXMgd2hhdCBtYWtlcyBhIHNjYXR0ZXIgcGxvdAogIC0gVGhlIGdlb21fc21vb3RoKCkgaXMgd2hhdCBhZGRzIHRoZSBjb3JyZWxhdGlvbiBsaW5lIHRvIHRoZSBwbG90CiAgLSBUaGUgYHhsYWIoKWAgYW5kIGB5bGFiKClgIGFkZCB0aGUgeC1heGlzIGFuZCB5LWF4aXMgbGFiZWxzIHRvIHRoZSBwbG90CiAgCjo6OgojIyMgSW1wcm92ZWQgU2NhdHRlciBQbG90IQoKTm93IGxldCdzIGFkZCBzb21lIGxheWVycyBhbmQgYWVzdGhldGljIGFkanVzdG1lbnRzIHRvIGltcHJvdmUgdGhpcyBwbG90LgoKYGBge3J9CmdncGxvdChqc19kYXRhLCBhZXMoZHVyV29yaywgZHVyU2xlZXApKSArCiAgZ2VvbV9wb2ludChjb2xvciA9ICIjMkQ1RTdGIiwgYWxwaGEgPSAuMikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9IGxtLCBjb2xvciA9ICJibGFjayIpICsKICB4bGFiKCJNaW51dGVzIFNwZW50IFdvcmtpbmciKSArCiAgeWxhYiAoIk1pbnV0ZXMgU3BlbnQgU2xlZXBpbmciKSArCiAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxNTAwLCAyNTApKSArCiAgbGFicyh0aXRsZSA9ICJBc3NvY2lhdGlvbiBCZXR3ZWVuIFdvcmtpbmcgYW5kIFNsZWVwaW5nIikgKwogIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE4KSkKYGBgCgo6Ojp3YWxrdGhyb3VnaAogIC0gV2UgY2FuIGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGRhdGEgcG9pbnRzIGJ5IGFkZGluZyBgY29sb3IgPSAiIzJENUU3RiJgIGFuZCBjaGFuZ2UgdGhlIHRyYW5zcGFyZW5jeSBvZiB0aGUgZGF0YSBwb2ludHMgKHNvIHlvdSBjYW4gc2VlIHdoZXJlIHRoZXJlIGFyZSBvdmVybGFwcGluZyBjbHVzdGVycykgd2l0aCB0aGUgYWRkaXRpb24gb2YgYGFscGhhID0gLjJgLiAKICAtIEluIHRoZSBgZ2VvbV9zbW9vdGgoKWAgZnVuY3Rpb24gd2UgY2hhbmdlZCB0aGUgbWV0aG9kIGZvciBjYWxjdWxhdGluZyB0aGUgbGluZSB0byBiZSBsaW5lYXIgKGluc3RlYWQgb2YgdGhlIGdncGxvdCBkZWZhdWx0KSB1c2luZyBgbWV0aG9kID0gbG1gIGFuZCBhZGp1c3RlZCB0aGUgY29sb3Igb2YgdGhlIGxpbmUgd2l0aCBgY29sb3IgPSBgLiBSZW1lbWJlciB0aGF0IHlvdSBjYW4gd3JpdGUgaGV4IGNvZGUgbnVtYmVycyBvciB0aGUgbmFtZXMgb2YgY29sb3JzIHRvIGFkanVzdCBjb2xvcnMuIE1ha2Ugc3VyZSBib3RoIGFyZSBpbiBxdW90ZXMgdG8gYXZvaWQgZXJyb3IgbWVzc2FnZXMuIAogIC0gV2UgYWRqdXN0IHRoZSB4LWF4aXMgdGljayBtYXJrcyB3aXRoIHRoZSBgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCAxNTAwLCAyNTApKWAgcGFydCwgd2hpY2ggdGVsbHMgUiB0byBwbG90IHRoZSB4LWF4aXMgb24gYSBzZXF1ZW5jZSBmcm9tIDAgdG8gMTUwMCAodGhpcyBjYXB0dXJlcyBhbGwgb2YgdGhlIHJlc3BvbnNlcyBpbiBvdXIgZGF0YSksIHdpdGggbGFiZWxzIGV2ZXJ5IDI1MCBtaW51dGVzLiAKICAtIFRoZSBgbGFicyh0aXRsZSA9ICJBc3NvY2lhdGlvbiBCZXR3ZWVuIFdvcmtpbmcgYW5kIFNsZWVwaW5nIilgIGFkZHMgYW4gb3ZlcmFsbCB0aXRsZSB0byB0aGUgcGxvdAogIC0gTGFzdGx5LCBgdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gMTgpKWAgYWRqdXN0IHRoZSB0ZXh0IHNpemUKICAKOjo6Cgo6OjpxdWVzdGlvbgpUcnkgbWFuaXB1bGF0aW5nIHRoaXMgcGxvdCBpbiBzb21lIHdheS4gQ2FuIHlvdSBtYWtlIHRoZSBkYXRhIHBvaW50cyBtb3JlIGFuZCBsZXNzIHRyYW5zcGFyZW50PyBDYW4geW91IGNoYW5nZSB0aGUgY29sb3Igb2YgdGhlIGxpbmU/IFdoYXQgaGFwcGVucyBpZiB0aGUgeC1heGlzIGhhcyBsYWJlbHMgZXZlcnkgMTAwIG1pbnV0ZXM/Cjo6OgoKIyMjIFlvdXIgVHVybiEKOjo6IHF1ZXN0aW9uClRha2UgYSBsb29rIGF0IG91ciBkYXRhIHNldCBhbmQgbWFrZSB0d28gbmV3IHBsb3RzLiAKCjEuIE1ha2UgYSBwbG90IGNvbXBhcmluZyBncm91cHMgKGkuZS4sIGEgY2F0ZWdvcmljYWwgdmFyaWFibGUpIG9uIG9uZSBvZiB0aGUgZHVyYXRpb24gdmFyaWFibGVzIChpLmUuLCBhIGNvbnRpbnVvdXMgdmFyaWFibGUpLgoKMi4gTWFrZSBhIHBsb3QgY29tcGFyaW5nIHR3byBjb250aW51b3VzIHZhcmlhYmxlcy4gCjo6OgoKOjo6IHF1ZXN0aW9uCgoqKkNoYWxsZW5nZToqKiBXaGF0IGlzIG9uZSBjb21wb25lbnQgb2YgeW91ciBwbG90IHlvdSB3b3VsZCBsaWtlIHRvIGNoYW5nZT8gQ2FuIHlvdSBsb29rIHVwIGEgc29sdXRpb24/IAo6OjoKCg==