Introduction to Plotting with R

Welcome to our comprehensive lecture on creating plots from dataframes in R! Data visualization is a crucial skill in data analysis, allowing us to communicate complex information clearly and efficiently. In this session, we’ll explore various plotting techniques using built-in R dataframes and the powerful ggplot2 library.

Throughout this lecture, we’ll cover different types of plots, their purposes, and when to use them. Each section will include detailed explanations and practical exercises to reinforce your learning.

1. Basic Plotting with Base R

Introduction

We’ll start our journey with R’s built-in plotting functions. These functions provide a quick and straightforward way to visualize data. While they may not be as flexible as more advanced libraries, understanding base R plotting is fundamental and can be useful for quick data exploration.

Purpose and Utility

Scatter plots, which we’ll create in this section, are excellent for visualizing relationships between two continuous variables. They’re particularly useful when you want to: - Identify correlations between variables - Detect outliers or unusual patterns in your data - Understand the distribution of data points across two dimensions

Scatter plots are widely used in various fields, including: - Economics: plotting GDP against life expectancy - Biology: comparing gene expression levels - Environmental science: examining the relationship between temperature and pollution levels

# Load the mtcars dataset
data(mtcars)

# Create a simple scatter plot
plot(mtcars$wt, mtcars$mpg, 
     main = "Car Weight vs. Miles Per Gallon",
     xlab = "Weight (1000 lbs)", 
     ylab = "Miles Per Gallon",
     pch = 19, 
     col = "blue")

In this example, we: 1. Load the mtcars dataset, which is built into R. 2. Use the plot() function to create a scatter plot. 3. Set the main title with main, x-axis label with xlab, and y-axis label with ylab. 4. Use pch = 19 for solid circle points and col = "blue" for blue color.

Exercises

  1. Create a scatter plot using the mtcars dataset to visualize the relationship between horsepower (hp) and quarter-mile time (qsec). Use red triangles for the points.

  2. Using the iris dataset (another built-in R dataset), create a scatter plot of sepal length vs. sepal width. Color the points based on the species. Hint: You’ll need to use the col parameter with a vector of colors corresponding to the species.

2. Introduction to ggplot2

Introduction

Now we’ll dive into ggplot2, a powerful and flexible plotting library in R. ggplot2 is based on the Grammar of Graphics, a coherent system for describing and building graphs. This system allows for highly customizable and layered graphics.

Purpose and Utility

The ggplot2 library offers several advantages over base R plotting: - Consistent and intuitive syntax - Layered approach to building complex graphics - Beautiful default aesthetics - Extensive customization options

ggplot2 is particularly useful when: - Creating publication-quality graphics - Building complex, multi-layered plots - Needing to quickly change aesthetic properties of plots - Working with large datasets

# Install and load ggplot2 if not already installed
# install.packages("ggplot2")
library(ggplot2)

# Create a scatter plot using ggplot2
ggplot(mtcars, aes(x = wt, y = mpg)) +
  geom_point() +
  labs(title = "Car Weight vs. Miles Per Gallon",
       x = "Weight (1000 lbs)",
       y = "Miles Per Gallon") +
  theme_minimal()

Here’s what we did: 1. Load the ggplot2 library. 2. Use ggplot() to initialize the plot, specifying the data and aesthetics. 3. Add points with geom_point(). 4. Set labels with labs(). 5. Apply a minimal theme with theme_minimal().

Exercises

  1. Using ggplot2 and the economics dataset (comes with ggplot2), create a line plot of unemployment over time. Use the date column for the x-axis and unemploy for the y-axis. Add appropriate labels and a title.

  2. With the mpg dataset (also included in ggplot2), create a scatter plot of engine displacement (displ) vs. highway miles per gallon (hwy). Color the points by the class of the vehicle. Add a title and appropriate axis labels.

3. Enhancing Plots with Color and Shape

Introduction

In this section, we’ll explore how to enhance our plots by incorporating additional variables through color and shape. This technique allows us to display multidimensional data in a two-dimensional plot, increasing the information density of our visualizations.

Purpose and Utility

Adding color and shape to plots serves several important purposes: - Grouping: It helps viewers quickly identify different categories or groups within the data. - Pattern recognition: It makes it easier to spot trends or patterns specific to certain groups. - Information density: It allows for the representation of additional variables without adding more dimensions to the plot.

This technique is particularly useful when: - Comparing multiple categories within a dataset - Identifying how different factors interact with the main variables being plotted - Presenting complex, multivariable data in a single, comprehensible visualization

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(cyl), shape = factor(am))) +
  geom_point(size = 3) +
  labs(title = "Car Weight vs. MPG",
       x = "Weight (1000 lbs)",
       y = "Miles Per Gallon",
       color = "Cylinders",
       shape = "Transmission") +
  scale_color_brewer(palette = "Set1") +
  theme_light()

In this example: 1. We use color = factor(cyl) to color points by number of cylinders. 2. shape = factor(am) changes point shapes based on transmission type. 3. scale_color_brewer() applies a color palette from ColorBrewer. 4. theme_light() gives a light background theme.

Exercises

  1. Using the diamonds dataset (included in ggplot2), create a scatter plot of price vs. carat. Use color to represent the cut quality and shape to represent the clarity. Add appropriate labels and a title.

  2. With the iris dataset, create a scatter plot of petal length vs. petal width. Use color to represent the species. Instead of using different shapes, vary the size of the points based on the sepal width. Add a legend for both color and size.

4. Creating Bar Plots

Introduction

Bar plots are one of the most common and effective ways to visualize categorical data. They allow for easy comparison of quantities across different categories or groups.

Purpose and Utility

Bar plots are particularly useful for: - Comparing quantities or frequencies across different categories - Displaying the distribution of a categorical variable - Showing changes in a quantity over time (when categories are time periods) - Presenting survey results or other categorical data

You might use bar plots when: - Analyzing market share across different products or companies - Comparing sales figures across different regions - Visualizing the distribution of responses in a survey - Presenting budget allocations across different departments

# Prepare data
cylinders <- as.data.frame(table(mtcars$cyl))
colnames(cylinders) <- c("Cylinders", "Count")

ggplot(cylinders, aes(x = Cylinders, y = Count, fill = Cylinders)) +
  geom_bar(stat = "identity") +
  labs(title = "Number of Cars by Cylinder Count",
       x = "Number of Cylinders",
       y = "Count") +
  theme_classic() +
  scale_fill_brewer(palette = "Pastel1")

Here’s what we did: 1. Create a summary dataframe of cylinder counts. 2. Use geom_bar() with stat = "identity" to create bars of specified heights. 3. Fill bars with different colors based on cylinder count. 4. Apply a classic theme and a pastel color palette.

Exercises

  1. Using the mpg dataset, create a bar plot showing the count of cars for each manufacturer. Order the bars from highest to lowest count. Add appropriate labels and a title.

  2. With the diamonds dataset, create a stacked bar plot showing the proportion of different cuts (fair, good, very good, premium, ideal) for each clarity category. Use different colors for each cut. Add a legend and appropriate labels.

5. Box Plots for Comparing Distributions

Introduction

Box plots, also known as box-and-whisker plots, are an excellent tool for visualizing the distribution of a continuous variable across different categories. They provide a concise summary of the data’s central tendency, spread, and potential outliers.

Purpose and Utility

Box plots are particularly useful for: - Comparing distributions across different groups or categories - Identifying the median, quartiles, and potential outliers in a dataset - Detecting skewness in the data distribution - Comparing the spread of data across different groups

You might use box plots when: - Comparing salary distributions across different departments - Analyzing the distribution of test scores across different schools - Examining the variability of measurement data in scientific experiments - Comparing the performance of different algorithms or methods

ggplot(mtcars, aes(x = factor(cyl), y = mpg, fill = factor(cyl))) +
  geom_boxplot() +
  labs(title = "Distribution of MPG by Number of Cylinders",
       x = "Number of Cylinders",
       y = "Miles Per Gallon") +
  theme_bw() +
  scale_fill_brewer(palette = "Set2") +
  theme(legend.position = "none")

In this example: 1. We create box plots using geom_boxplot(). 2. Group and fill by number of cylinders. 3. Remove the legend as it’s redundant with x-axis labels.

Exercises

  1. Using the diamonds dataset, create a box plot showing the distribution of price for each cut category. Add color to the boxes based on the cut. Include appropriate labels and a title.

  2. With the gapminder dataset (you may need to install the gapminder package), create a box plot showing the distribution of life expectancy for each continent. Arrange the continents in descending order of median life expectancy. Add color and appropriate labels.

6. Histograms and Density Plots

Introduction

Histograms and density plots are powerful tools for visualizing the distribution of a single continuous variable. They provide insights into the shape, central tendency, and spread of the data.

Purpose and Utility

Histograms and density plots are particularly useful for: - Visualizing the overall distribution of a continuous variable - Identifying the mode(s) of a distribution - Detecting skewness or unusual patterns in the data - Comparing the distribution of a variable across different groups

You might use these plots when: - Analyzing the distribution of ages in a population - Examining the distribution of response times in a psychology experiment - Investigating the distribution of prices in a real estate market - Comparing the distribution of a variable before and after an intervention

ggplot(mtcars, aes(x = mpg)) +
  geom_histogram(aes(y = ..density..), binwidth = 2, fill = "skyblue", color = "black") +
  geom_density(color = "red", size = 1) +
  labs(title = "Distribution of Miles Per Gallon",
       x = "Miles Per Gallon",
       y = "Density") +
  theme_minimal()
Warning: Using `size` aesthetic for lines was deprecated in ggplot2 3.4.0.
ℹ Please use `linewidth` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.
Warning: The dot-dot notation (`..density..`) was deprecated in ggplot2 3.4.0.
ℹ Please use `after_stat(density)` instead.
This warning is displayed once every 8 hours.
Call `lifecycle::last_lifecycle_warnings()` to see where this warning was generated.

Here’s what we did: 1. Create a histogram with geom_histogram(), setting y = ..density.. for density scale. 2. Overlay a density curve with geom_density(). 3. Customize colors and labels for clarity.

Exercises

  1. Using the diamonds dataset, create a histogram of the ‘price’ variable. Experiment with different bin widths to see how it affects the visualization. Add a density curve on top of the histogram. Include appropriate labels and a title.

  2. With the faithful dataset (built into R), create two density plots on the same graph: one for eruption duration and one for waiting time between eruptions. Use different colors for each density curve and add a legend. Normalize the scales so that both curves use the same y-axis. Add appropriate labels and a title.

7. Faceting for Multi-panel Plots

Introduction

Faceting is a powerful technique in data visualization that allows you to create multiple panels or subplots based on categorical variables. This approach is particularly useful when you want to compare patterns across different subgroups of your data.

Purpose and Utility

Faceting is especially useful for: - Comparing trends or patterns across different categories - Visualizing how the relationship between variables changes across different groups - Displaying multiple aspects of a dataset in a single, organized figure - Reducing overplotting in complex datasets

You might use faceting when: - Comparing sales trends across different regions over time - Analyzing how the relationship between two variables varies across different categories - Visualizing multiple related metrics for different groups - Exploring how a distribution changes based on one or more categorical variables

ggplot(mtcars, aes(x = wt, y = mpg, color = factor(am))) +
  geom_point() +
  geom_smooth(method = "lm", se = FALSE) +
  facet_wrap(~cyl, nrow = 1) +
  labs(title = "Weight vs. MPG by Cylinders and Transmission",
       x = "Weight (1000 lbs)",
       y = "Miles Per Gallon",
       color = "Transmission") +
  theme_bw() +
  scale_color_brewer(palette = "Set1", labels = c("Automatic", "Manual"))
`geom_smooth()` using formula = 'y ~ x'

In this example: 1. We use facet_wrap() to create separate panels for each cylinder count. 2. Add trend lines with geom_smooth(). 3. Color points and lines by transmission type. 4. Customize labels and theme for better readability.

Exercises

  1. Using the diamonds dataset, create a scatter plot of price vs. carat. Facet the plot by cut, creating a 2x3 grid of subplots. Color the points by clarity. Add a smooth trend line to each facet. Include appropriate labels and a title.

  2. With the mpg dataset, create a box plot of highway fuel efficiency (hwy) for different car classes. Facet the plot by the number of cylinders (cyl). Color the boxes by the type of drive (drv). Arrange the facets in a single row. Add appropriate labels and a title.

Conclusion

This lecture has covered a range of plotting techniques in R, from basic scatter plots to more complex, multi-layered visualizations. Remember, the key to effective data visualization is choosing the right plot type for your data and research question. Practice with different datasets and experiment with various ggplot2 functions to become proficient in creating informative and visually appealing plots.

Additional Resources

Happy plotting!

LS0tCnRpdGxlOiAiQ3JlYXRpbmcgUGxvdHMgZnJvbSBEYXRhZnJhbWVzIGluIFIiCmF1dGhvcjogIk5heWVsIEJldHRhY2hlIgpkYXRlOiAiMjAyNC0wOS0xOCIKb3V0cHV0OiBodG1sX25vdGVib29rCmVkaXRvcl9vcHRpb25zOiAKICBtYXJrZG93bjogCiAgICB3cmFwOiA3MgotLS0KCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUpCmBgYAoKIyBJbnRyb2R1Y3Rpb24gdG8gUGxvdHRpbmcgd2l0aCBSCgpXZWxjb21lIHRvIG91ciBjb21wcmVoZW5zaXZlIGxlY3R1cmUgb24gY3JlYXRpbmcgcGxvdHMgZnJvbSBkYXRhZnJhbWVzCmluIFIhIERhdGEgdmlzdWFsaXphdGlvbiBpcyBhIGNydWNpYWwgc2tpbGwgaW4gZGF0YSBhbmFseXNpcywgYWxsb3dpbmcKdXMgdG8gY29tbXVuaWNhdGUgY29tcGxleCBpbmZvcm1hdGlvbiBjbGVhcmx5IGFuZCBlZmZpY2llbnRseS4gSW4gdGhpcwpzZXNzaW9uLCB3ZSdsbCBleHBsb3JlIHZhcmlvdXMgcGxvdHRpbmcgdGVjaG5pcXVlcyB1c2luZyBidWlsdC1pbiBSCmRhdGFmcmFtZXMgYW5kIHRoZSBwb3dlcmZ1bCBgZ2dwbG90MmAgbGlicmFyeS4KClRocm91Z2hvdXQgdGhpcyBsZWN0dXJlLCB3ZSdsbCBjb3ZlciBkaWZmZXJlbnQgdHlwZXMgb2YgcGxvdHMsIHRoZWlyCnB1cnBvc2VzLCBhbmQgd2hlbiB0byB1c2UgdGhlbS4gRWFjaCBzZWN0aW9uIHdpbGwgaW5jbHVkZSBkZXRhaWxlZApleHBsYW5hdGlvbnMgYW5kIHByYWN0aWNhbCBleGVyY2lzZXMgdG8gcmVpbmZvcmNlIHlvdXIgbGVhcm5pbmcuCgojIyAxLiBCYXNpYyBQbG90dGluZyB3aXRoIEJhc2UgUgoKIyMjIEludHJvZHVjdGlvbgoKV2UnbGwgc3RhcnQgb3VyIGpvdXJuZXkgd2l0aCBSJ3MgYnVpbHQtaW4gcGxvdHRpbmcgZnVuY3Rpb25zLiBUaGVzZQpmdW5jdGlvbnMgcHJvdmlkZSBhIHF1aWNrIGFuZCBzdHJhaWdodGZvcndhcmQgd2F5IHRvIHZpc3VhbGl6ZSBkYXRhLgpXaGlsZSB0aGV5IG1heSBub3QgYmUgYXMgZmxleGlibGUgYXMgbW9yZSBhZHZhbmNlZCBsaWJyYXJpZXMsCnVuZGVyc3RhbmRpbmcgYmFzZSBSIHBsb3R0aW5nIGlzIGZ1bmRhbWVudGFsIGFuZCBjYW4gYmUgdXNlZnVsIGZvciBxdWljawpkYXRhIGV4cGxvcmF0aW9uLgoKIyMjIFB1cnBvc2UgYW5kIFV0aWxpdHkKClNjYXR0ZXIgcGxvdHMsIHdoaWNoIHdlJ2xsIGNyZWF0ZSBpbiB0aGlzIHNlY3Rpb24sIGFyZSBleGNlbGxlbnQgZm9yCnZpc3VhbGl6aW5nIHJlbGF0aW9uc2hpcHMgYmV0d2VlbiB0d28gY29udGludW91cyB2YXJpYWJsZXMuIFRoZXkncmUKcGFydGljdWxhcmx5IHVzZWZ1bCB3aGVuIHlvdSB3YW50IHRvOiAtIElkZW50aWZ5IGNvcnJlbGF0aW9ucyBiZXR3ZWVuCnZhcmlhYmxlcyAtIERldGVjdCBvdXRsaWVycyBvciB1bnVzdWFsIHBhdHRlcm5zIGluIHlvdXIgZGF0YSAtClVuZGVyc3RhbmQgdGhlIGRpc3RyaWJ1dGlvbiBvZiBkYXRhIHBvaW50cyBhY3Jvc3MgdHdvIGRpbWVuc2lvbnMKClNjYXR0ZXIgcGxvdHMgYXJlIHdpZGVseSB1c2VkIGluIHZhcmlvdXMgZmllbGRzLCBpbmNsdWRpbmc6IC0gRWNvbm9taWNzOgpwbG90dGluZyBHRFAgYWdhaW5zdCBsaWZlIGV4cGVjdGFuY3kgLSBCaW9sb2d5OiBjb21wYXJpbmcgZ2VuZQpleHByZXNzaW9uIGxldmVscyAtIEVudmlyb25tZW50YWwgc2NpZW5jZTogZXhhbWluaW5nIHRoZSByZWxhdGlvbnNoaXAKYmV0d2VlbiB0ZW1wZXJhdHVyZSBhbmQgcG9sbHV0aW9uIGxldmVscwoKYGBge3J9CiMgTG9hZCB0aGUgbXRjYXJzIGRhdGFzZXQKZGF0YShtdGNhcnMpCgojIENyZWF0ZSBhIHNpbXBsZSBzY2F0dGVyIHBsb3QKcGxvdChtdGNhcnMkd3QsIG10Y2FycyRtcGcsIAogICAgIG1haW4gPSAiQ2FyIFdlaWdodCB2cy4gTWlsZXMgUGVyIEdhbGxvbiIsCiAgICAgeGxhYiA9ICJXZWlnaHQgKDEwMDAgbGJzKSIsIAogICAgIHlsYWIgPSAiTWlsZXMgUGVyIEdhbGxvbiIsCiAgICAgcGNoID0gMTksIAogICAgIGNvbCA9ICJibHVlIikKYGBgCgpJbiB0aGlzIGV4YW1wbGUsIHdlOiAxLiBMb2FkIHRoZSBgbXRjYXJzYCBkYXRhc2V0LCB3aGljaCBpcyBidWlsdCBpbnRvClIuIDIuIFVzZSB0aGUgYHBsb3QoKWAgZnVuY3Rpb24gdG8gY3JlYXRlIGEgc2NhdHRlciBwbG90LiAzLiBTZXQgdGhlCm1haW4gdGl0bGUgd2l0aCBgbWFpbmAsIHgtYXhpcyBsYWJlbCB3aXRoIGB4bGFiYCwgYW5kIHktYXhpcyBsYWJlbCB3aXRoCmB5bGFiYC4gNC4gVXNlIGBwY2ggPSAxOWAgZm9yIHNvbGlkIGNpcmNsZSBwb2ludHMgYW5kIGBjb2wgPSAiYmx1ZSJgIGZvcgpibHVlIGNvbG9yLgoKIyMjIEV4ZXJjaXNlcwoKMS4gIENyZWF0ZSBhIHNjYXR0ZXIgcGxvdCB1c2luZyB0aGUgYG10Y2Fyc2AgZGF0YXNldCB0byB2aXN1YWxpemUgdGhlCiAgICByZWxhdGlvbnNoaXAgYmV0d2VlbiBob3JzZXBvd2VyIChgaHBgKSBhbmQgcXVhcnRlci1taWxlIHRpbWUKICAgIChgcXNlY2ApLiBVc2UgcmVkIHRyaWFuZ2xlcyBmb3IgdGhlIHBvaW50cy4KCjIuICBVc2luZyB0aGUgYGlyaXNgIGRhdGFzZXQgKGFub3RoZXIgYnVpbHQtaW4gUiBkYXRhc2V0KSwgY3JlYXRlIGEKICAgIHNjYXR0ZXIgcGxvdCBvZiBzZXBhbCBsZW5ndGggdnMuIHNlcGFsIHdpZHRoLiBDb2xvciB0aGUgcG9pbnRzIGJhc2VkCiAgICBvbiB0aGUgc3BlY2llcy4gSGludDogWW91J2xsIG5lZWQgdG8gdXNlIHRoZSBgY29sYCBwYXJhbWV0ZXIgd2l0aCBhCiAgICB2ZWN0b3Igb2YgY29sb3JzIGNvcnJlc3BvbmRpbmcgdG8gdGhlIHNwZWNpZXMuCgojIyAyLiBJbnRyb2R1Y3Rpb24gdG8gZ2dwbG90MgoKIyMjIEludHJvZHVjdGlvbgoKTm93IHdlJ2xsIGRpdmUgaW50byBgZ2dwbG90MmAsIGEgcG93ZXJmdWwgYW5kIGZsZXhpYmxlIHBsb3R0aW5nIGxpYnJhcnkKaW4gUi4gYGdncGxvdDJgIGlzIGJhc2VkIG9uIHRoZSBHcmFtbWFyIG9mIEdyYXBoaWNzLCBhIGNvaGVyZW50IHN5c3RlbQpmb3IgZGVzY3JpYmluZyBhbmQgYnVpbGRpbmcgZ3JhcGhzLiBUaGlzIHN5c3RlbSBhbGxvd3MgZm9yIGhpZ2hseQpjdXN0b21pemFibGUgYW5kIGxheWVyZWQgZ3JhcGhpY3MuCgojIyMgUHVycG9zZSBhbmQgVXRpbGl0eQoKVGhlIGBnZ3Bsb3QyYCBsaWJyYXJ5IG9mZmVycyBzZXZlcmFsIGFkdmFudGFnZXMgb3ZlciBiYXNlIFIgcGxvdHRpbmc6IC0KQ29uc2lzdGVudCBhbmQgaW50dWl0aXZlIHN5bnRheCAtIExheWVyZWQgYXBwcm9hY2ggdG8gYnVpbGRpbmcgY29tcGxleApncmFwaGljcyAtIEJlYXV0aWZ1bCBkZWZhdWx0IGFlc3RoZXRpY3MgLSBFeHRlbnNpdmUgY3VzdG9taXphdGlvbgpvcHRpb25zCgpgZ2dwbG90MmAgaXMgcGFydGljdWxhcmx5IHVzZWZ1bCB3aGVuOiAtIENyZWF0aW5nIHB1YmxpY2F0aW9uLXF1YWxpdHkKZ3JhcGhpY3MgLSBCdWlsZGluZyBjb21wbGV4LCBtdWx0aS1sYXllcmVkIHBsb3RzIC0gTmVlZGluZyB0byBxdWlja2x5CmNoYW5nZSBhZXN0aGV0aWMgcHJvcGVydGllcyBvZiBwbG90cyAtIFdvcmtpbmcgd2l0aCBsYXJnZSBkYXRhc2V0cwoKYGBge3J9CiMgSW5zdGFsbCBhbmQgbG9hZCBnZ3Bsb3QyIGlmIG5vdCBhbHJlYWR5IGluc3RhbGxlZAojIGluc3RhbGwucGFja2FnZXMoImdncGxvdDIiKQpsaWJyYXJ5KGdncGxvdDIpCgojIENyZWF0ZSBhIHNjYXR0ZXIgcGxvdCB1c2luZyBnZ3Bsb3QyCmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcpKSArCiAgZ2VvbV9wb2ludCgpICsKICBsYWJzKHRpdGxlID0gIkNhciBXZWlnaHQgdnMuIE1pbGVzIFBlciBHYWxsb24iLAogICAgICAgeCA9ICJXZWlnaHQgKDEwMDAgbGJzKSIsCiAgICAgICB5ID0gIk1pbGVzIFBlciBHYWxsb24iKSArCiAgdGhlbWVfbWluaW1hbCgpCmBgYAoKSGVyZSdzIHdoYXQgd2UgZGlkOiAxLiBMb2FkIHRoZSBgZ2dwbG90MmAgbGlicmFyeS4gMi4gVXNlIGBnZ3Bsb3QoKWAgdG8KaW5pdGlhbGl6ZSB0aGUgcGxvdCwgc3BlY2lmeWluZyB0aGUgZGF0YSBhbmQgYWVzdGhldGljcy4gMy4gQWRkIHBvaW50cwp3aXRoIGBnZW9tX3BvaW50KClgLiA0LiBTZXQgbGFiZWxzIHdpdGggYGxhYnMoKWAuIDUuIEFwcGx5IGEgbWluaW1hbAp0aGVtZSB3aXRoIGB0aGVtZV9taW5pbWFsKClgLgoKIyMjIEV4ZXJjaXNlcwoKMS4gIFVzaW5nIGBnZ3Bsb3QyYCBhbmQgdGhlIGBlY29ub21pY3NgIGRhdGFzZXQgKGNvbWVzIHdpdGggZ2dwbG90MiksCiAgICBjcmVhdGUgYSBsaW5lIHBsb3Qgb2YgdW5lbXBsb3ltZW50IG92ZXIgdGltZS4gVXNlIHRoZSBgZGF0ZWAgY29sdW1uCiAgICBmb3IgdGhlIHgtYXhpcyBhbmQgYHVuZW1wbG95YCBmb3IgdGhlIHktYXhpcy4gQWRkIGFwcHJvcHJpYXRlIGxhYmVscwogICAgYW5kIGEgdGl0bGUuCgoyLiAgV2l0aCB0aGUgYG1wZ2AgZGF0YXNldCAoYWxzbyBpbmNsdWRlZCBpbiBnZ3Bsb3QyKSwgY3JlYXRlIGEgc2NhdHRlcgogICAgcGxvdCBvZiBlbmdpbmUgZGlzcGxhY2VtZW50IChgZGlzcGxgKSB2cy4gaGlnaHdheSBtaWxlcyBwZXIgZ2FsbG9uCiAgICAoYGh3eWApLiBDb2xvciB0aGUgcG9pbnRzIGJ5IHRoZSBgY2xhc3NgIG9mIHRoZSB2ZWhpY2xlLiBBZGQgYSB0aXRsZQogICAgYW5kIGFwcHJvcHJpYXRlIGF4aXMgbGFiZWxzLgoKIyMgMy4gRW5oYW5jaW5nIFBsb3RzIHdpdGggQ29sb3IgYW5kIFNoYXBlCgojIyMgSW50cm9kdWN0aW9uCgpJbiB0aGlzIHNlY3Rpb24sIHdlJ2xsIGV4cGxvcmUgaG93IHRvIGVuaGFuY2Ugb3VyIHBsb3RzIGJ5IGluY29ycG9yYXRpbmcKYWRkaXRpb25hbCB2YXJpYWJsZXMgdGhyb3VnaCBjb2xvciBhbmQgc2hhcGUuIFRoaXMgdGVjaG5pcXVlIGFsbG93cyB1cwp0byBkaXNwbGF5IG11bHRpZGltZW5zaW9uYWwgZGF0YSBpbiBhIHR3by1kaW1lbnNpb25hbCBwbG90LCBpbmNyZWFzaW5nCnRoZSBpbmZvcm1hdGlvbiBkZW5zaXR5IG9mIG91ciB2aXN1YWxpemF0aW9ucy4KCiMjIyBQdXJwb3NlIGFuZCBVdGlsaXR5CgpBZGRpbmcgY29sb3IgYW5kIHNoYXBlIHRvIHBsb3RzIHNlcnZlcyBzZXZlcmFsIGltcG9ydGFudCBwdXJwb3NlczogLQpHcm91cGluZzogSXQgaGVscHMgdmlld2VycyBxdWlja2x5IGlkZW50aWZ5IGRpZmZlcmVudCBjYXRlZ29yaWVzIG9yCmdyb3VwcyB3aXRoaW4gdGhlIGRhdGEuIC0gUGF0dGVybiByZWNvZ25pdGlvbjogSXQgbWFrZXMgaXQgZWFzaWVyIHRvCnNwb3QgdHJlbmRzIG9yIHBhdHRlcm5zIHNwZWNpZmljIHRvIGNlcnRhaW4gZ3JvdXBzLiAtIEluZm9ybWF0aW9uCmRlbnNpdHk6IEl0IGFsbG93cyBmb3IgdGhlIHJlcHJlc2VudGF0aW9uIG9mIGFkZGl0aW9uYWwgdmFyaWFibGVzCndpdGhvdXQgYWRkaW5nIG1vcmUgZGltZW5zaW9ucyB0byB0aGUgcGxvdC4KClRoaXMgdGVjaG5pcXVlIGlzIHBhcnRpY3VsYXJseSB1c2VmdWwgd2hlbjogLSBDb21wYXJpbmcgbXVsdGlwbGUKY2F0ZWdvcmllcyB3aXRoaW4gYSBkYXRhc2V0IC0gSWRlbnRpZnlpbmcgaG93IGRpZmZlcmVudCBmYWN0b3JzIGludGVyYWN0CndpdGggdGhlIG1haW4gdmFyaWFibGVzIGJlaW5nIHBsb3R0ZWQgLSBQcmVzZW50aW5nIGNvbXBsZXgsCm11bHRpdmFyaWFibGUgZGF0YSBpbiBhIHNpbmdsZSwgY29tcHJlaGVuc2libGUgdmlzdWFsaXphdGlvbgoKYGBge3J9CmdncGxvdChtdGNhcnMsIGFlcyh4ID0gd3QsIHkgPSBtcGcsIGNvbG9yID0gZmFjdG9yKGN5bCksIHNoYXBlID0gZmFjdG9yKGFtKSkpICsKICBnZW9tX3BvaW50KHNpemUgPSAzKSArCiAgbGFicyh0aXRsZSA9ICJDYXIgV2VpZ2h0IHZzLiBNUEciLAogICAgICAgeCA9ICJXZWlnaHQgKDEwMDAgbGJzKSIsCiAgICAgICB5ID0gIk1pbGVzIFBlciBHYWxsb24iLAogICAgICAgY29sb3IgPSAiQ3lsaW5kZXJzIiwKICAgICAgIHNoYXBlID0gIlRyYW5zbWlzc2lvbiIpICsKICBzY2FsZV9jb2xvcl9icmV3ZXIocGFsZXR0ZSA9ICJTZXQxIikgKwogIHRoZW1lX2xpZ2h0KCkKYGBgCgpJbiB0aGlzIGV4YW1wbGU6IDEuIFdlIHVzZSBgY29sb3IgPSBmYWN0b3IoY3lsKWAgdG8gY29sb3IgcG9pbnRzIGJ5Cm51bWJlciBvZiBjeWxpbmRlcnMuIDIuIGBzaGFwZSA9IGZhY3RvcihhbSlgIGNoYW5nZXMgcG9pbnQgc2hhcGVzIGJhc2VkCm9uIHRyYW5zbWlzc2lvbiB0eXBlLiAzLiBgc2NhbGVfY29sb3JfYnJld2VyKClgIGFwcGxpZXMgYSBjb2xvciBwYWxldHRlCmZyb20gQ29sb3JCcmV3ZXIuIDQuIGB0aGVtZV9saWdodCgpYCBnaXZlcyBhIGxpZ2h0IGJhY2tncm91bmQgdGhlbWUuCgojIyMgRXhlcmNpc2VzCgoxLiAgVXNpbmcgdGhlIGBkaWFtb25kc2AgZGF0YXNldCAoaW5jbHVkZWQgaW4gZ2dwbG90MiksIGNyZWF0ZSBhIHNjYXR0ZXIKICAgIHBsb3Qgb2YgcHJpY2UgdnMuIGNhcmF0LiBVc2UgY29sb3IgdG8gcmVwcmVzZW50IHRoZSBjdXQgcXVhbGl0eSBhbmQKICAgIHNoYXBlIHRvIHJlcHJlc2VudCB0aGUgY2xhcml0eS4gQWRkIGFwcHJvcHJpYXRlIGxhYmVscyBhbmQgYSB0aXRsZS4KCjIuICBXaXRoIHRoZSBgaXJpc2AgZGF0YXNldCwgY3JlYXRlIGEgc2NhdHRlciBwbG90IG9mIHBldGFsIGxlbmd0aCB2cy4KICAgIHBldGFsIHdpZHRoLiBVc2UgY29sb3IgdG8gcmVwcmVzZW50IHRoZSBzcGVjaWVzLiBJbnN0ZWFkIG9mIHVzaW5nCiAgICBkaWZmZXJlbnQgc2hhcGVzLCB2YXJ5IHRoZSBzaXplIG9mIHRoZSBwb2ludHMgYmFzZWQgb24gdGhlIHNlcGFsCiAgICB3aWR0aC4gQWRkIGEgbGVnZW5kIGZvciBib3RoIGNvbG9yIGFuZCBzaXplLgoKIyMgNC4gQ3JlYXRpbmcgQmFyIFBsb3RzCgojIyMgSW50cm9kdWN0aW9uCgpCYXIgcGxvdHMgYXJlIG9uZSBvZiB0aGUgbW9zdCBjb21tb24gYW5kIGVmZmVjdGl2ZSB3YXlzIHRvIHZpc3VhbGl6ZQpjYXRlZ29yaWNhbCBkYXRhLiBUaGV5IGFsbG93IGZvciBlYXN5IGNvbXBhcmlzb24gb2YgcXVhbnRpdGllcyBhY3Jvc3MKZGlmZmVyZW50IGNhdGVnb3JpZXMgb3IgZ3JvdXBzLgoKIyMjIFB1cnBvc2UgYW5kIFV0aWxpdHkKCkJhciBwbG90cyBhcmUgcGFydGljdWxhcmx5IHVzZWZ1bCBmb3I6IC0gQ29tcGFyaW5nIHF1YW50aXRpZXMgb3IKZnJlcXVlbmNpZXMgYWNyb3NzIGRpZmZlcmVudCBjYXRlZ29yaWVzIC0gRGlzcGxheWluZyB0aGUgZGlzdHJpYnV0aW9uIG9mCmEgY2F0ZWdvcmljYWwgdmFyaWFibGUgLSBTaG93aW5nIGNoYW5nZXMgaW4gYSBxdWFudGl0eSBvdmVyIHRpbWUgKHdoZW4KY2F0ZWdvcmllcyBhcmUgdGltZSBwZXJpb2RzKSAtIFByZXNlbnRpbmcgc3VydmV5IHJlc3VsdHMgb3Igb3RoZXIKY2F0ZWdvcmljYWwgZGF0YQoKWW91IG1pZ2h0IHVzZSBiYXIgcGxvdHMgd2hlbjogLSBBbmFseXppbmcgbWFya2V0IHNoYXJlIGFjcm9zcyBkaWZmZXJlbnQKcHJvZHVjdHMgb3IgY29tcGFuaWVzIC0gQ29tcGFyaW5nIHNhbGVzIGZpZ3VyZXMgYWNyb3NzIGRpZmZlcmVudApyZWdpb25zIC0gVmlzdWFsaXppbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiByZXNwb25zZXMgaW4gYSBzdXJ2ZXkgLQpQcmVzZW50aW5nIGJ1ZGdldCBhbGxvY2F0aW9ucyBhY3Jvc3MgZGlmZmVyZW50IGRlcGFydG1lbnRzCgpgYGB7cn0KIyBQcmVwYXJlIGRhdGEKY3lsaW5kZXJzIDwtIGFzLmRhdGEuZnJhbWUodGFibGUobXRjYXJzJGN5bCkpCmNvbG5hbWVzKGN5bGluZGVycykgPC0gYygiQ3lsaW5kZXJzIiwgIkNvdW50IikKCmdncGxvdChjeWxpbmRlcnMsIGFlcyh4ID0gQ3lsaW5kZXJzLCB5ID0gQ291bnQsIGZpbGwgPSBDeWxpbmRlcnMpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBDYXJzIGJ5IEN5bGluZGVyIENvdW50IiwKICAgICAgIHggPSAiTnVtYmVyIG9mIEN5bGluZGVycyIsCiAgICAgICB5ID0gIkNvdW50IikgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJQYXN0ZWwxIikKYGBgCgpIZXJlJ3Mgd2hhdCB3ZSBkaWQ6IDEuIENyZWF0ZSBhIHN1bW1hcnkgZGF0YWZyYW1lIG9mIGN5bGluZGVyIGNvdW50cy4gMi4KVXNlIGBnZW9tX2JhcigpYCB3aXRoIGBzdGF0ID0gImlkZW50aXR5ImAgdG8gY3JlYXRlIGJhcnMgb2Ygc3BlY2lmaWVkCmhlaWdodHMuIDMuIEZpbGwgYmFycyB3aXRoIGRpZmZlcmVudCBjb2xvcnMgYmFzZWQgb24gY3lsaW5kZXIgY291bnQuIDQuCkFwcGx5IGEgY2xhc3NpYyB0aGVtZSBhbmQgYSBwYXN0ZWwgY29sb3IgcGFsZXR0ZS4KCiMjIyBFeGVyY2lzZXMKCjEuICBVc2luZyB0aGUgYG1wZ2AgZGF0YXNldCwgY3JlYXRlIGEgYmFyIHBsb3Qgc2hvd2luZyB0aGUgY291bnQgb2YgY2FycwogICAgZm9yIGVhY2ggbWFudWZhY3R1cmVyLiBPcmRlciB0aGUgYmFycyBmcm9tIGhpZ2hlc3QgdG8gbG93ZXN0IGNvdW50LgogICAgQWRkIGFwcHJvcHJpYXRlIGxhYmVscyBhbmQgYSB0aXRsZS4KCjIuICBXaXRoIHRoZSBgZGlhbW9uZHNgIGRhdGFzZXQsIGNyZWF0ZSBhIHN0YWNrZWQgYmFyIHBsb3Qgc2hvd2luZyB0aGUKICAgIHByb3BvcnRpb24gb2YgZGlmZmVyZW50IGN1dHMgKGZhaXIsIGdvb2QsIHZlcnkgZ29vZCwgcHJlbWl1bSwgaWRlYWwpCiAgICBmb3IgZWFjaCBjbGFyaXR5IGNhdGVnb3J5LiBVc2UgZGlmZmVyZW50IGNvbG9ycyBmb3IgZWFjaCBjdXQuIEFkZCBhCiAgICBsZWdlbmQgYW5kIGFwcHJvcHJpYXRlIGxhYmVscy4KCiMjIDUuIEJveCBQbG90cyBmb3IgQ29tcGFyaW5nIERpc3RyaWJ1dGlvbnMKCiMjIyBJbnRyb2R1Y3Rpb24KCkJveCBwbG90cywgYWxzbyBrbm93biBhcyBib3gtYW5kLXdoaXNrZXIgcGxvdHMsIGFyZSBhbiBleGNlbGxlbnQgdG9vbApmb3IgdmlzdWFsaXppbmcgdGhlIGRpc3RyaWJ1dGlvbiBvZiBhIGNvbnRpbnVvdXMgdmFyaWFibGUgYWNyb3NzCmRpZmZlcmVudCBjYXRlZ29yaWVzLiBUaGV5IHByb3ZpZGUgYSBjb25jaXNlIHN1bW1hcnkgb2YgdGhlIGRhdGEncwpjZW50cmFsIHRlbmRlbmN5LCBzcHJlYWQsIGFuZCBwb3RlbnRpYWwgb3V0bGllcnMuCgojIyMgUHVycG9zZSBhbmQgVXRpbGl0eQoKQm94IHBsb3RzIGFyZSBwYXJ0aWN1bGFybHkgdXNlZnVsIGZvcjogLSBDb21wYXJpbmcgZGlzdHJpYnV0aW9ucyBhY3Jvc3MKZGlmZmVyZW50IGdyb3VwcyBvciBjYXRlZ29yaWVzIC0gSWRlbnRpZnlpbmcgdGhlIG1lZGlhbiwgcXVhcnRpbGVzLCBhbmQKcG90ZW50aWFsIG91dGxpZXJzIGluIGEgZGF0YXNldCAtIERldGVjdGluZyBza2V3bmVzcyBpbiB0aGUgZGF0YQpkaXN0cmlidXRpb24gLSBDb21wYXJpbmcgdGhlIHNwcmVhZCBvZiBkYXRhIGFjcm9zcyBkaWZmZXJlbnQgZ3JvdXBzCgpZb3UgbWlnaHQgdXNlIGJveCBwbG90cyB3aGVuOiAtIENvbXBhcmluZyBzYWxhcnkgZGlzdHJpYnV0aW9ucyBhY3Jvc3MKZGlmZmVyZW50IGRlcGFydG1lbnRzIC0gQW5hbHl6aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgdGVzdCBzY29yZXMgYWNyb3NzCmRpZmZlcmVudCBzY2hvb2xzIC0gRXhhbWluaW5nIHRoZSB2YXJpYWJpbGl0eSBvZiBtZWFzdXJlbWVudCBkYXRhIGluCnNjaWVudGlmaWMgZXhwZXJpbWVudHMgLSBDb21wYXJpbmcgdGhlIHBlcmZvcm1hbmNlIG9mIGRpZmZlcmVudAphbGdvcml0aG1zIG9yIG1ldGhvZHMKCmBgYHtyfQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IGZhY3RvcihjeWwpLCB5ID0gbXBnLCBmaWxsID0gZmFjdG9yKGN5bCkpKSArCiAgZ2VvbV9ib3hwbG90KCkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIE1QRyBieSBOdW1iZXIgb2YgQ3lsaW5kZXJzIiwKICAgICAgIHggPSAiTnVtYmVyIG9mIEN5bGluZGVycyIsCiAgICAgICB5ID0gIk1pbGVzIFBlciBHYWxsb24iKSArCiAgdGhlbWVfYncoKSArCiAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikKYGBgCgpJbiB0aGlzIGV4YW1wbGU6IDEuIFdlIGNyZWF0ZSBib3ggcGxvdHMgdXNpbmcgYGdlb21fYm94cGxvdCgpYC4gMi4gR3JvdXAKYW5kIGZpbGwgYnkgbnVtYmVyIG9mIGN5bGluZGVycy4gMy4gUmVtb3ZlIHRoZSBsZWdlbmQgYXMgaXQncyByZWR1bmRhbnQKd2l0aCB4LWF4aXMgbGFiZWxzLgoKIyMjIEV4ZXJjaXNlcwoKMS4gIFVzaW5nIHRoZSBgZGlhbW9uZHNgIGRhdGFzZXQsIGNyZWF0ZSBhIGJveCBwbG90IHNob3dpbmcgdGhlCiAgICBkaXN0cmlidXRpb24gb2YgcHJpY2UgZm9yIGVhY2ggY3V0IGNhdGVnb3J5LiBBZGQgY29sb3IgdG8gdGhlIGJveGVzCiAgICBiYXNlZCBvbiB0aGUgY3V0LiBJbmNsdWRlIGFwcHJvcHJpYXRlIGxhYmVscyBhbmQgYSB0aXRsZS4KCjIuICBXaXRoIHRoZSBgZ2FwbWluZGVyYCBkYXRhc2V0ICh5b3UgbWF5IG5lZWQgdG8gaW5zdGFsbCB0aGUgZ2FwbWluZGVyCiAgICBwYWNrYWdlKSwgY3JlYXRlIGEgYm94IHBsb3Qgc2hvd2luZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGxpZmUKICAgIGV4cGVjdGFuY3kgZm9yIGVhY2ggY29udGluZW50LiBBcnJhbmdlIHRoZSBjb250aW5lbnRzIGluIGRlc2NlbmRpbmcKICAgIG9yZGVyIG9mIG1lZGlhbiBsaWZlIGV4cGVjdGFuY3kuIEFkZCBjb2xvciBhbmQgYXBwcm9wcmlhdGUgbGFiZWxzLgoKIyMgNi4gSGlzdG9ncmFtcyBhbmQgRGVuc2l0eSBQbG90cwoKIyMjIEludHJvZHVjdGlvbgoKSGlzdG9ncmFtcyBhbmQgZGVuc2l0eSBwbG90cyBhcmUgcG93ZXJmdWwgdG9vbHMgZm9yIHZpc3VhbGl6aW5nIHRoZQpkaXN0cmlidXRpb24gb2YgYSBzaW5nbGUgY29udGludW91cyB2YXJpYWJsZS4gVGhleSBwcm92aWRlIGluc2lnaHRzIGludG8KdGhlIHNoYXBlLCBjZW50cmFsIHRlbmRlbmN5LCBhbmQgc3ByZWFkIG9mIHRoZSBkYXRhLgoKIyMjIFB1cnBvc2UgYW5kIFV0aWxpdHkKCkhpc3RvZ3JhbXMgYW5kIGRlbnNpdHkgcGxvdHMgYXJlIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yOiAtIFZpc3VhbGl6aW5nCnRoZSBvdmVyYWxsIGRpc3RyaWJ1dGlvbiBvZiBhIGNvbnRpbnVvdXMgdmFyaWFibGUgLSBJZGVudGlmeWluZyB0aGUKbW9kZShzKSBvZiBhIGRpc3RyaWJ1dGlvbiAtIERldGVjdGluZyBza2V3bmVzcyBvciB1bnVzdWFsIHBhdHRlcm5zIGluCnRoZSBkYXRhIC0gQ29tcGFyaW5nIHRoZSBkaXN0cmlidXRpb24gb2YgYSB2YXJpYWJsZSBhY3Jvc3MgZGlmZmVyZW50Cmdyb3VwcwoKWW91IG1pZ2h0IHVzZSB0aGVzZSBwbG90cyB3aGVuOiAtIEFuYWx5emluZyB0aGUgZGlzdHJpYnV0aW9uIG9mIGFnZXMgaW4KYSBwb3B1bGF0aW9uIC0gRXhhbWluaW5nIHRoZSBkaXN0cmlidXRpb24gb2YgcmVzcG9uc2UgdGltZXMgaW4gYQpwc3ljaG9sb2d5IGV4cGVyaW1lbnQgLSBJbnZlc3RpZ2F0aW5nIHRoZSBkaXN0cmlidXRpb24gb2YgcHJpY2VzIGluIGEKcmVhbCBlc3RhdGUgbWFya2V0IC0gQ29tcGFyaW5nIHRoZSBkaXN0cmlidXRpb24gb2YgYSB2YXJpYWJsZSBiZWZvcmUgYW5kCmFmdGVyIGFuIGludGVydmVudGlvbgoKYGBge3J9CmdncGxvdChtdGNhcnMsIGFlcyh4ID0gbXBnKSkgKwogIGdlb21faGlzdG9ncmFtKGFlcyh5ID0gLi5kZW5zaXR5Li4pLCBiaW53aWR0aCA9IDIsIGZpbGwgPSAic2t5Ymx1ZSIsIGNvbG9yID0gImJsYWNrIikgKwogIGdlb21fZGVuc2l0eShjb2xvciA9ICJyZWQiLCBzaXplID0gMSkgKwogIGxhYnModGl0bGUgPSAiRGlzdHJpYnV0aW9uIG9mIE1pbGVzIFBlciBHYWxsb24iLAogICAgICAgeCA9ICJNaWxlcyBQZXIgR2FsbG9uIiwKICAgICAgIHkgPSAiRGVuc2l0eSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgpIZXJlJ3Mgd2hhdCB3ZSBkaWQ6IDEuIENyZWF0ZSBhIGhpc3RvZ3JhbSB3aXRoIGBnZW9tX2hpc3RvZ3JhbSgpYCwKc2V0dGluZyBgeSA9IC4uZGVuc2l0eS4uYCBmb3IgZGVuc2l0eSBzY2FsZS4gMi4gT3ZlcmxheSBhIGRlbnNpdHkgY3VydmUKd2l0aCBgZ2VvbV9kZW5zaXR5KClgLiAzLiBDdXN0b21pemUgY29sb3JzIGFuZCBsYWJlbHMgZm9yIGNsYXJpdHkuCgojIyMgRXhlcmNpc2VzCgoxLiAgVXNpbmcgdGhlIGBkaWFtb25kc2AgZGF0YXNldCwgY3JlYXRlIGEgaGlzdG9ncmFtIG9mIHRoZSAncHJpY2UnCiAgICB2YXJpYWJsZS4gRXhwZXJpbWVudCB3aXRoIGRpZmZlcmVudCBiaW4gd2lkdGhzIHRvIHNlZSBob3cgaXQgYWZmZWN0cwogICAgdGhlIHZpc3VhbGl6YXRpb24uIEFkZCBhIGRlbnNpdHkgY3VydmUgb24gdG9wIG9mIHRoZSBoaXN0b2dyYW0uCiAgICBJbmNsdWRlIGFwcHJvcHJpYXRlIGxhYmVscyBhbmQgYSB0aXRsZS4KCjIuICBXaXRoIHRoZSBgZmFpdGhmdWxgIGRhdGFzZXQgKGJ1aWx0IGludG8gUiksIGNyZWF0ZSB0d28gZGVuc2l0eSBwbG90cwogICAgb24gdGhlIHNhbWUgZ3JhcGg6IG9uZSBmb3IgZXJ1cHRpb24gZHVyYXRpb24gYW5kIG9uZSBmb3Igd2FpdGluZwogICAgdGltZSBiZXR3ZWVuIGVydXB0aW9ucy4gVXNlIGRpZmZlcmVudCBjb2xvcnMgZm9yIGVhY2ggZGVuc2l0eSBjdXJ2ZQogICAgYW5kIGFkZCBhIGxlZ2VuZC4gTm9ybWFsaXplIHRoZSBzY2FsZXMgc28gdGhhdCBib3RoIGN1cnZlcyB1c2UgdGhlCiAgICBzYW1lIHktYXhpcy4gQWRkIGFwcHJvcHJpYXRlIGxhYmVscyBhbmQgYSB0aXRsZS4KCiMjIDcuIEZhY2V0aW5nIGZvciBNdWx0aS1wYW5lbCBQbG90cwoKIyMjIEludHJvZHVjdGlvbgoKRmFjZXRpbmcgaXMgYSBwb3dlcmZ1bCB0ZWNobmlxdWUgaW4gZGF0YSB2aXN1YWxpemF0aW9uIHRoYXQgYWxsb3dzIHlvdQp0byBjcmVhdGUgbXVsdGlwbGUgcGFuZWxzIG9yIHN1YnBsb3RzIGJhc2VkIG9uIGNhdGVnb3JpY2FsIHZhcmlhYmxlcy4KVGhpcyBhcHByb2FjaCBpcyBwYXJ0aWN1bGFybHkgdXNlZnVsIHdoZW4geW91IHdhbnQgdG8gY29tcGFyZSBwYXR0ZXJucwphY3Jvc3MgZGlmZmVyZW50IHN1Ymdyb3VwcyBvZiB5b3VyIGRhdGEuCgojIyMgUHVycG9zZSBhbmQgVXRpbGl0eQoKRmFjZXRpbmcgaXMgZXNwZWNpYWxseSB1c2VmdWwgZm9yOiAtIENvbXBhcmluZyB0cmVuZHMgb3IgcGF0dGVybnMgYWNyb3NzCmRpZmZlcmVudCBjYXRlZ29yaWVzIC0gVmlzdWFsaXppbmcgaG93IHRoZSByZWxhdGlvbnNoaXAgYmV0d2Vlbgp2YXJpYWJsZXMgY2hhbmdlcyBhY3Jvc3MgZGlmZmVyZW50IGdyb3VwcyAtIERpc3BsYXlpbmcgbXVsdGlwbGUgYXNwZWN0cwpvZiBhIGRhdGFzZXQgaW4gYSBzaW5nbGUsIG9yZ2FuaXplZCBmaWd1cmUgLSBSZWR1Y2luZyBvdmVycGxvdHRpbmcgaW4KY29tcGxleCBkYXRhc2V0cwoKWW91IG1pZ2h0IHVzZSBmYWNldGluZyB3aGVuOiAtIENvbXBhcmluZyBzYWxlcyB0cmVuZHMgYWNyb3NzIGRpZmZlcmVudApyZWdpb25zIG92ZXIgdGltZSAtIEFuYWx5emluZyBob3cgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byB2YXJpYWJsZXMKdmFyaWVzIGFjcm9zcyBkaWZmZXJlbnQgY2F0ZWdvcmllcyAtIFZpc3VhbGl6aW5nIG11bHRpcGxlIHJlbGF0ZWQKbWV0cmljcyBmb3IgZGlmZmVyZW50IGdyb3VwcyAtIEV4cGxvcmluZyBob3cgYSBkaXN0cmlidXRpb24gY2hhbmdlcwpiYXNlZCBvbiBvbmUgb3IgbW9yZSBjYXRlZ29yaWNhbCB2YXJpYWJsZXMKCmBgYHtyfQpnZ3Bsb3QobXRjYXJzLCBhZXMoeCA9IHd0LCB5ID0gbXBnLCBjb2xvciA9IGZhY3RvcihhbSkpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iLCBzZSA9IEZBTFNFKSArCiAgZmFjZXRfd3JhcCh+Y3lsLCBucm93ID0gMSkgKwogIGxhYnModGl0bGUgPSAiV2VpZ2h0IHZzLiBNUEcgYnkgQ3lsaW5kZXJzIGFuZCBUcmFuc21pc3Npb24iLAogICAgICAgeCA9ICJXZWlnaHQgKDEwMDAgbGJzKSIsCiAgICAgICB5ID0gIk1pbGVzIFBlciBHYWxsb24iLAogICAgICAgY29sb3IgPSAiVHJhbnNtaXNzaW9uIikgKwogIHRoZW1lX2J3KCkgKwogIHNjYWxlX2NvbG9yX2JyZXdlcihwYWxldHRlID0gIlNldDEiLCBsYWJlbHMgPSBjKCJBdXRvbWF0aWMiLCAiTWFudWFsIikpCmBgYAoKSW4gdGhpcyBleGFtcGxlOiAxLiBXZSB1c2UgYGZhY2V0X3dyYXAoKWAgdG8gY3JlYXRlIHNlcGFyYXRlIHBhbmVscyBmb3IKZWFjaCBjeWxpbmRlciBjb3VudC4gMi4gQWRkIHRyZW5kIGxpbmVzIHdpdGggYGdlb21fc21vb3RoKClgLiAzLiBDb2xvcgpwb2ludHMgYW5kIGxpbmVzIGJ5IHRyYW5zbWlzc2lvbiB0eXBlLiA0LiBDdXN0b21pemUgbGFiZWxzIGFuZCB0aGVtZSBmb3IKYmV0dGVyIHJlYWRhYmlsaXR5LgoKIyMjIEV4ZXJjaXNlcwoKMS4gIFVzaW5nIHRoZSBgZGlhbW9uZHNgIGRhdGFzZXQsIGNyZWF0ZSBhIHNjYXR0ZXIgcGxvdCBvZiBwcmljZSB2cy4KICAgIGNhcmF0LiBGYWNldCB0aGUgcGxvdCBieSBjdXQsIGNyZWF0aW5nIGEgMngzIGdyaWQgb2Ygc3VicGxvdHMuIENvbG9yCiAgICB0aGUgcG9pbnRzIGJ5IGNsYXJpdHkuIEFkZCBhIHNtb290aCB0cmVuZCBsaW5lIHRvIGVhY2ggZmFjZXQuCiAgICBJbmNsdWRlIGFwcHJvcHJpYXRlIGxhYmVscyBhbmQgYSB0aXRsZS4KCjIuICBXaXRoIHRoZSBgbXBnYCBkYXRhc2V0LCBjcmVhdGUgYSBib3ggcGxvdCBvZiBoaWdod2F5IGZ1ZWwgZWZmaWNpZW5jeQogICAgKGh3eSkgZm9yIGRpZmZlcmVudCBjYXIgY2xhc3Nlcy4gRmFjZXQgdGhlIHBsb3QgYnkgdGhlIG51bWJlciBvZgogICAgY3lsaW5kZXJzIChjeWwpLiBDb2xvciB0aGUgYm94ZXMgYnkgdGhlIHR5cGUgb2YgZHJpdmUgKGRydikuIEFycmFuZ2UKICAgIHRoZSBmYWNldHMgaW4gYSBzaW5nbGUgcm93LiBBZGQgYXBwcm9wcmlhdGUgbGFiZWxzIGFuZCBhIHRpdGxlLgoKIyMgQ29uY2x1c2lvbgoKVGhpcyBsZWN0dXJlIGhhcyBjb3ZlcmVkIGEgcmFuZ2Ugb2YgcGxvdHRpbmcgdGVjaG5pcXVlcyBpbiBSLCBmcm9tIGJhc2ljCnNjYXR0ZXIgcGxvdHMgdG8gbW9yZSBjb21wbGV4LCBtdWx0aS1sYXllcmVkIHZpc3VhbGl6YXRpb25zLiBSZW1lbWJlciwKdGhlIGtleSB0byBlZmZlY3RpdmUgZGF0YSB2aXN1YWxpemF0aW9uIGlzIGNob29zaW5nIHRoZSByaWdodCBwbG90IHR5cGUKZm9yIHlvdXIgZGF0YSBhbmQgcmVzZWFyY2ggcXVlc3Rpb24uIFByYWN0aWNlIHdpdGggZGlmZmVyZW50IGRhdGFzZXRzCmFuZCBleHBlcmltZW50IHdpdGggdmFyaW91cyBgZ2dwbG90MmAgZnVuY3Rpb25zIHRvIGJlY29tZSBwcm9maWNpZW50IGluCmNyZWF0aW5nIGluZm9ybWF0aXZlIGFuZCB2aXN1YWxseSBhcHBlYWxpbmcgcGxvdHMuCgojIyBBZGRpdGlvbmFsIFJlc291cmNlcwoKLSAgIGdncGxvdDIgZG9jdW1lbnRhdGlvbjogPGh0dHBzOi8vZ2dwbG90Mi50aWR5dmVyc2Uub3JnLz4KLSAgIFIgR3JhcGhpY3MgQ29va2Jvb2s6IDxodHRwczovL3ItZ3JhcGhpY3Mub3JnLz4KLSAgIERhdGFjYW1wIGdncGxvdDIgdHV0b3JpYWw6CiAgICA8aHR0cHM6Ly93d3cuZGF0YWNhbXAuY29tL2NvbW11bml0eS90dXRvcmlhbHMvZ2dwbG90Mi10dXRvcmlhbC1yPgoKSGFwcHkgcGxvdHRpbmchCg==