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