Objectives

Upon completion of this lesson, you will be able to:

  • explore data visually using bar charts, scatterplots, and line graphs
  • create common chart types in Base R
  • identify correlations and outliers visually

Overview

Exploratory data visualization is an essential step in the data analytics process, particularly for college students learning the fundamentals of data science. It involves creating visual representations of data to help identify patterns, trends, and anomalies, thereby facilitating a better understanding of the underlying information. As an effective way to convey complex ideas and explore data, it is a vital skill for data analysts.

This overview aims to introduce the basic concepts of exploratory data visualization, its importance in data analytics, and some common tools and techniques.

The examples use data from two CSV files:

Download the files to try the example code yourself and to experiment with the parameters of each function.

Importance of Exploratory Data Visualization

  • Identifying Patterns and Trends: Visualizations help analysts detect relationships, trends, and patterns within data that might not be easily discernible otherwise.
  • Outlier Detection: By visually exploring data, analysts can quickly identify potential outliers or errors in the dataset.
  • Hypothesis Generation: Visualizations can help generate hypotheses about the data, which can later be tested using statistical methods.
  • Effective Communication: Visual representations of data allow analysts to communicate their findings to both technical and non-technical audiences.

Common Types of Data Visualizations

The three most common visualizations for exploring data are:

  • Scatter Plots: Suitable for exploring the relationship between two continuous variables.
  • Bar Charts: Useful for comparing categorical data or showing changes over time.
  • Line Charts: Ideal for visualizing continuous data, such as trends over time.

These three are also useful, but are not addressed in this lesson:

  • Bubble Charts: A scatterplot where the size of the points encode another data dimension.
  • Pie Charts: Great for representing proportions or percentages of a whole.
  • Heat Maps: Effective for visualizing large datasets and identifying patterns or clusters.

Tips for Effective Data Visualization

  • Choose the Right Chart Type: Select a visualization type that best represents the data and the story you want to tell.
  • Keep It Simple: Focus on clarity and avoid clutter by limiting the number of colors, shapes, and labels.
  • Use Appropriate Scales: Choose appropriate scales and axis limits to avoid misleading representations of the data.
  • Consider Accessibility: Ensure that visualizations are accessible to those with color vision deficiencies and other impairments.

Scatterplots

Scatterplots, also known as scatter graphs or scatter diagrams, are a type of data visualization used to display the relationship between two continuous variables. In a scatterplot, each data point is represented as a dot (or other symbol) on a Cartesian coordinate plane, with one variable plotted on the x-axis and the other on the y-axis.

Scatterplots are particularly useful for identifying patterns or trends between the two variables, such as positive or negative correlations, and for detecting outliers. By visually examining a scatterplot, one can gain insights into the strength and direction of the relationship between the variables.

For example, if the data points in a scatterplot form an upward-sloping pattern, it indicates a positive correlation between the two variables, meaning that as one variable increases, so does the other. Conversely, if the data points form a downward-sloping pattern, it suggests a negative correlation, meaning that as one variable increases, the other decreases. If the data points are scattered randomly without any discernible pattern, it indicates that there may be little or no correlation between the two variables.

To create a scatterplot in R, you can use the plot() function, which is a built-in function in the base R package. Here’s a simple example that demonstrates how to create a scatterplot of two variables:

# Sample data
df <- read.csv("customertxndata.csv")
df.agg <- aggregate(df$Total, list(df$NumVisits), FUN=mean)

# Create a scatterplot using the plot() function
plot(x = df.agg[,1], 
     y = df.agg[,2], 
     main = "Number of Visits vs Average Total Spend", 
     xlab = "#Visits", 
     ylab = "Avg Total ($)",
     type = 'p')

In the example above, we load a CSV of website visits into a dataframe and then use the aggregate() function to calculate the average total for each number of visits. We then plot the average spend for each number of visits to determine if there’s a correlation.

The pattern reveals a positive correlation, i.e., the average total spend increases with the number of visits. Once a correlation has been visually detected, a statistical analysis of the correlation should be made by calculating an appropriate correlation metric such as Pearson Moment or Spearman Rank.

Bar Charts

Bar charts, also known as bar graphs, are a type of data visualization used to display and compare categorical or discrete data. In a bar chart, data categories are represented by rectangular bars, where the length or height of each bar is proportional to the value or frequency of the category it represents. Bar charts can be plotted either vertically or horizontally, with the x-axis representing the categories and the y-axis representing the values or frequencies. If they are plotted vertically, they are also sometimes referred to as column charts.

Bar charts are best used when:

  • Comparing data across categories: Bar charts are an effective way to compare values or frequencies across different categories, making it easy to identify the highest or lowest values and observe general trends.

  • Showing changes over time: When the categories represent time periods (e.g., months, years), bar charts can be used to visualize changes in the data over time. In this case, it’s essential to maintain a consistent time interval between bars to avoid misinterpretation of the data.

  • Displaying distribution or composition: Bar charts can be used to show the distribution of data across different categories or the composition of a whole by using stacked or grouped bar charts.

  • Visualizing small to moderate-sized datasets: Bar charts are most effective for displaying small to moderate-sized datasets with a limited number of categories. For larger datasets or those with numerous categories, other visualization techniques like line charts or heat maps might be more appropriate.

Keep in mind that bar charts are not suitable for visualizing continuous data, as the data needs to be grouped into discrete categories for this type of chart. For continuous data, consider using line charts, scatterplots, or other visualization methods.

Bar plots can be created in R using the barplot() function. It takes a vector and the plot will have bars with their heights equal to the elements in the vector. The example below creates a column chart from a CSV containing data about visits to a website.

In the example below, the function table() is used to count how many visits come from each OS.

# Sample data
df <- read.csv("customertxndata.csv")

# count number of visits by OS
visitsByOS <- table(df$OS)

barplot(visitsByOS,
        main = "Visits by OS",
        xlab = "OS",
        ylab = "#Visits",
        names.arg = c("Android", "iOS"),
        col = "darkred",
        horiz = FALSE)

The barplot() function has a few key parameters

  • main is the main title for the chart
  • sub is a subtitle for the chart
  • horiz can be set to TRUE or FALSE; if FALSE then the plot is a column chart
  • xlab and ylab are the x- and y-axis labels
  • names.arg is a vector of labels for each bar
  • col is the color of the bars

The second example is a bar chart showing the total revenue by operating system.

# Sample data
df <- read.csv("customertxndata.csv")

# calculate the revenue by OS
df.agg <- aggregate(df$Total, list(df$OS), FUN=sum)

barplot(df.agg$x,
        main = "Revenue by OS",
        sub = "in US$ for Q1 2023",
        xlab = "OS",
        ylab = "Revenue (US$)",
        names.arg = c("Android", "iOS"),
        col = "navy",
        horiz = TRUE)

Line Graphs

The examples below use the data from pharmaSalesTxn.csv containing sales transactions with dates.

# read data from CSV
df <- read.csv("pharmaSalesTxn.csv")

# for each transaction (row), extract amount and month (mm/dd/yyyy)
df.sales <- data.frame(
  month = df$date,
  amount = df$amount
)

n <- nrow(df.sales)
for (i in 1:n) {
  df.sales$month[i] <- strsplit(df.sales$month[i], "/")[[1]][1]
}

# sum the sales per month
df.agg <- aggregate(df.sales$amount, list(df.sales$month), FUN=sum)

# rename the columns
colnames(df.agg) <- c("month","amount")

# convert months to integers and order by month
df.agg$month <- as.integer(df.agg$month)
df.agg <- df.agg[order(df.agg$month), ]

# plot the revenue total by month
plot(x = df.agg$month, y = df.agg$amount, 
     type = "b", pch = 19, 
     col = "red", 
     xlab = "Month", ylab = "Total Sales ($)")

Stacking Plots

Several plots can be added to the same visualization. The plot() function creates the canvas for the visualization and the call to lines() adds a line to the existing canvas created by plot().

When stacking plots, i.e., when adding two plots to a single canvas, it is critical that the values for both are accommodated by the range of x and y values of the first plot created with the plot() function.

# read data from CSV
df <- read.csv("pharmaSalesTxn.csv")

# for each transaction (row), extract amount and month (mm/dd/yyyy)
df.sales <- data.frame(
  month = df$date,
  amount = df$amount
)

n <- nrow(df.sales)
for (i in 1:n) {
  df.sales$month[i] <- strsplit(df.sales$month[i], "/")[[1]][1]
}

# sum and average the sales per month
df.agg <- aggregate(df.sales$amount, list(df.sales$month), 
                    FUN = function(x) c(total = sum(x), avg = mean(x) ))

# extract data to new dataframe
df <- data.frame(
  month = as.integer(df.agg$Group.1),
  total = as.numeric(df.agg$x[,1]),
  avg = as.numeric(df.agg$x[,2]),
  stringsAsFactors = FALSE,
  row.names = NULL)

# order by month
df <- df[order(df$month), ]

df <- data.frame(
  month = 1:12,
  total = sample(2000:3000, 12),
  avg = sample(1000:1800, 12)
)

In the code block below, we add lines for the “total” and the “average” sold per month, so we need a range of values that accommodates both numeric ranges.

# determine minimum and maximum y axis values
ylim.min = min(df$total,df$avg) * 0.8
ylim.max = max(df$total,df$avg) * 1.2

# Create a first line: monthly sales total
plot(df$month, df$total, type = "b", frame = FALSE, pch = 19, 
     xlim = c(1, 12),
     ylim = c(ylim.min, ylim.max),
     main = "Average vs Total Sales Per Month",
     sub = "Year 2020",
     col = "red", xlab = "Month", ylab = "Amount ($)")

# Add a second line: monthly average sales
lines(df$month, df$avg, pch = 18, col = "blue", type = "b", lty = 2)

Customizing Plots

R offers several options to customize the chart appearance with parameters to the plot() function.

  • cex → shape size
  • lwd → line width
  • col → control colors
  • lty → line type
  • pch → marker shape
  • type → link between dots

The argument type determines how the line segment (or, to be more precise, the connection between the points) are drawn:

  • ‘p’ – only draw points and no lines which creates a scatterplot
  • ‘b’ – connect segments but leave a gap around each point
  • ‘l’ – fully connect the points with lines

The argument pch determines how each point is drawn and is a value between 1 and 19. For example:

  • 19 – large dot
  • 4 – cross

The argument lwd determines the line width. It is a value from 1 to 4.

The argument lty determines the line type. Line types can either be specified as an integer (0=blank, 1=solid (default), 2=dashed, 3=dotted, 4=dotdash, 5=longdash, 6=twodash) or as one of the character strings “blank”, “solid”, “dashed”, “dotted”, “dotdash”, “longdash”, or “twodash”, where “blank” uses ‘invisible lines’ (i.e., does not draw them).

The cheat sheet below helps summarize the values:

Adding Legends

Legends can be added to a plot using the legend() function. It uses the same dataframe and values from a previous block.

# total per month
plot(df$month, df$total, type = "b", frame = FALSE, pch = 19, 
     xlim = c(1, 12),
     ylim = c(ylim.min, ylim.max),
     main = "Total vs Average Sales Per Month",
     col = "red", xlab = "Month", ylab = "$")

# average per month
lines(df$month, df$avg, pch = 18, col = "blue", type = "b", lty = 2)

# add a legend to the plot
legend("topleft", legend=c("Total Sales", "Average Sales"),
       col=c("red", "blue"), lty = 1:2, cex=0.8)

Summary

By gaining a strong foundation in exploratory data visualization, data analysts can enhance their data analytics skills and effectively communicate their findings. With the right tools and techniques, visualizations can unlock valuable insights and support data-driven decision-making.


Files & Resources

All Files for Lesson 6.724

References

None.

Errata

None collected yet. Let us know.

LS0tCnRpdGxlOiAiQmFzaWMgRXhwbG9yYXRvcnkgRGF0YSBWaXN1YWxpemF0aW9uIGluIFIiCnBhcmFtczoKICBjYXRlZ29yeTogNgogIG51bWJlcjogNzI0CiAgdGltZTogNDUKICBsZXZlbDogYmVnaW5uZXIKICB0YWdzOiAicixzY2F0dGVyIHBsb3QsYnViYmxlIGNoYXJ0LHBsb3RseSIKICBkZXNjcmlwdGlvbjogIlNob3dzIGhvdyB0byBjcmVhdGUgc2NhdHRlciBwbG90cywgYmFyIGNoYXJ0cywgaGlzdG9ncmFtcyBhbmQgCiAgICAgICAgICAgICAgICBsaW5lIGdyYXBocyBpbiBSIHRvIHZpc3VhbGl6ZSByZWxhdGlvbnNoaXBzLiBQcmVzZW50cyBtZXRob2RzCiAgICAgICAgICAgICAgICBmb3IgZXhwbG9yYXRvcnkgZGF0YSB2aXN1YWxpemF0aW9uIgpkYXRlOiAiPHNtYWxsPmByIFN5cy5EYXRlKClgPC9zbWFsbD4iCmF1dGhvcjogIjxzbWFsbD5NYXJ0aW4gU2NoZWRsYmF1ZXI8L3NtYWxsPiIKZW1haWw6ICJtLnNjaGVkbGJhdWVyQG5ldS5lZHUiCmFmZmlsaXRhdGlvbjogIk5vcnRoZWFzdGVybiBVbml2ZXJzaXR5IgpvdXRwdXQ6IAogIGJvb2tkb3duOjpodG1sX2RvY3VtZW50MjoKICAgIHRvYzogdHJ1ZQogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBjb2xsYXBzZWQ6IGZhbHNlCiAgICBudW1iZXJfc2VjdGlvbnM6IGZhbHNlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICB0aGVtZTogc3BhY2VsYWIKICAgIGhpZ2hsaWdodDogdGFuZ28KLS0tCgotLS0KdGl0bGU6ICI8c21hbGw+YHIgcGFyYW1zJGNhdGVnb3J5YC5gciBwYXJhbXMkbnVtYmVyYDwvc21hbGw+PGJyLz48c3BhbiBzdHlsZT0nY29sb3I6ICMyRTQwNTM7IGZvbnQtc2l6ZTogMC45ZW0nPmByIHJtYXJrZG93bjo6bWV0YWRhdGEkdGl0bGVgPC9zcGFuPiIKLS0tCgpgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9faW5zZXJ0MkRCLlInKSksIGluY2x1ZGUgPSBGQUxTRX0KYGBgCgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIE9iamVjdGl2ZXMKClVwb24gY29tcGxldGlvbiBvZiB0aGlzIGxlc3NvbiwgeW91IHdpbGwgYmUgYWJsZSB0bzoKCi0gICBleHBsb3JlIGRhdGEgdmlzdWFsbHkgdXNpbmcgYmFyIGNoYXJ0cywgc2NhdHRlcnBsb3RzLCBhbmQgbGluZSBncmFwaHMKLSAgIGNyZWF0ZSBjb21tb24gY2hhcnQgdHlwZXMgaW4gQmFzZSBSCi0gICBpZGVudGlmeSBjb3JyZWxhdGlvbnMgYW5kIG91dGxpZXJzIHZpc3VhbGx5CgotLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0KCiMjIE92ZXJ2aWV3CgpFeHBsb3JhdG9yeSBkYXRhIHZpc3VhbGl6YXRpb24gaXMgYW4gZXNzZW50aWFsIHN0ZXAgaW4gdGhlIGRhdGEgYW5hbHl0aWNzIHByb2Nlc3MsIHBhcnRpY3VsYXJseSBmb3IgY29sbGVnZSBzdHVkZW50cyBsZWFybmluZyB0aGUgZnVuZGFtZW50YWxzIG9mIGRhdGEgc2NpZW5jZS4gSXQgaW52b2x2ZXMgY3JlYXRpbmcgdmlzdWFsIHJlcHJlc2VudGF0aW9ucyBvZiBkYXRhIHRvIGhlbHAgaWRlbnRpZnkgcGF0dGVybnMsIHRyZW5kcywgYW5kIGFub21hbGllcywgdGhlcmVieSBmYWNpbGl0YXRpbmcgYSBiZXR0ZXIgdW5kZXJzdGFuZGluZyBvZiB0aGUgdW5kZXJseWluZyBpbmZvcm1hdGlvbi4gQXMgYW4gZWZmZWN0aXZlIHdheSB0byBjb252ZXkgY29tcGxleCBpZGVhcyBhbmQgZXhwbG9yZSBkYXRhLCBpdCBpcyBhIHZpdGFsIHNraWxsIGZvciBkYXRhIGFuYWx5c3RzLgoKVGhpcyBvdmVydmlldyBhaW1zIHRvIGludHJvZHVjZSB0aGUgYmFzaWMgY29uY2VwdHMgb2YgZXhwbG9yYXRvcnkgZGF0YSB2aXN1YWxpemF0aW9uLCBpdHMgaW1wb3J0YW5jZSBpbiBkYXRhIGFuYWx5dGljcywgYW5kIHNvbWUgY29tbW9uIHRvb2xzIGFuZCB0ZWNobmlxdWVzLgoKVGhlIGV4YW1wbGVzIHVzZSBkYXRhIGZyb20gdHdvIENTViBmaWxlczoKCi0gICBbY3VzdG9tZXJ0eG5kYXRhLmNzdl0oY3VzdG9tZXJ0eG5kYXRhLmNzdikKLSAgIFtwaGFybWFTYWxlc1R4bi5jc3ZdKHBoYXJtYVNhbGVzVHhuLmNzdikKCkRvd25sb2FkIHRoZSBmaWxlcyB0byB0cnkgdGhlIGV4YW1wbGUgY29kZSB5b3Vyc2VsZiBhbmQgdG8gZXhwZXJpbWVudCB3aXRoIHRoZSBwYXJhbWV0ZXJzIG9mIGVhY2ggZnVuY3Rpb24uCgojIyBJbXBvcnRhbmNlIG9mIEV4cGxvcmF0b3J5IERhdGEgVmlzdWFsaXphdGlvbgoKLSAgICoqSWRlbnRpZnlpbmcgUGF0dGVybnMgYW5kIFRyZW5kcyoqOiBWaXN1YWxpemF0aW9ucyBoZWxwIGFuYWx5c3RzIGRldGVjdCByZWxhdGlvbnNoaXBzLCB0cmVuZHMsIGFuZCBwYXR0ZXJucyB3aXRoaW4gZGF0YSB0aGF0IG1pZ2h0IG5vdCBiZSBlYXNpbHkgZGlzY2VybmlibGUgb3RoZXJ3aXNlLgotICAgKipPdXRsaWVyIERldGVjdGlvbioqOiBCeSB2aXN1YWxseSBleHBsb3JpbmcgZGF0YSwgYW5hbHlzdHMgY2FuIHF1aWNrbHkgaWRlbnRpZnkgcG90ZW50aWFsIG91dGxpZXJzIG9yIGVycm9ycyBpbiB0aGUgZGF0YXNldC4KLSAgICoqSHlwb3RoZXNpcyBHZW5lcmF0aW9uKio6IFZpc3VhbGl6YXRpb25zIGNhbiBoZWxwIGdlbmVyYXRlIGh5cG90aGVzZXMgYWJvdXQgdGhlIGRhdGEsIHdoaWNoIGNhbiBsYXRlciBiZSB0ZXN0ZWQgdXNpbmcgc3RhdGlzdGljYWwgbWV0aG9kcy4KLSAgICoqRWZmZWN0aXZlIENvbW11bmljYXRpb24qKjogVmlzdWFsIHJlcHJlc2VudGF0aW9ucyBvZiBkYXRhIGFsbG93IGFuYWx5c3RzIHRvIGNvbW11bmljYXRlIHRoZWlyIGZpbmRpbmdzIHRvIGJvdGggdGVjaG5pY2FsIGFuZCBub24tdGVjaG5pY2FsIGF1ZGllbmNlcy4KCiMjIENvbW1vbiBUeXBlcyBvZiBEYXRhIFZpc3VhbGl6YXRpb25zCgpUaGUgdGhyZWUgbW9zdCBjb21tb24gdmlzdWFsaXphdGlvbnMgZm9yIGV4cGxvcmluZyBkYXRhIGFyZToKCi0gICAqKlNjYXR0ZXIgUGxvdHMqKjogU3VpdGFibGUgZm9yIGV4cGxvcmluZyB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdHdvIGNvbnRpbnVvdXMgdmFyaWFibGVzLgotICAgKipCYXIgQ2hhcnRzKio6IFVzZWZ1bCBmb3IgY29tcGFyaW5nIGNhdGVnb3JpY2FsIGRhdGEgb3Igc2hvd2luZyBjaGFuZ2VzIG92ZXIgdGltZS4KLSAgICoqTGluZSBDaGFydHMqKjogSWRlYWwgZm9yIHZpc3VhbGl6aW5nIGNvbnRpbnVvdXMgZGF0YSwgc3VjaCBhcyB0cmVuZHMgb3ZlciB0aW1lLgoKVGhlc2UgdGhyZWUgYXJlIGFsc28gdXNlZnVsLCBidXQgYXJlIG5vdCBhZGRyZXNzZWQgaW4gdGhpcyBsZXNzb246CgotICAgKipCdWJibGUgQ2hhcnRzKio6IEEgc2NhdHRlcnBsb3Qgd2hlcmUgdGhlIHNpemUgb2YgdGhlIHBvaW50cyBlbmNvZGUgYW5vdGhlciBkYXRhIGRpbWVuc2lvbi4KLSAgICoqUGllIENoYXJ0cyoqOiBHcmVhdCBmb3IgcmVwcmVzZW50aW5nIHByb3BvcnRpb25zIG9yIHBlcmNlbnRhZ2VzIG9mIGEgd2hvbGUuCi0gICAqKkhlYXQgTWFwcyoqOiBFZmZlY3RpdmUgZm9yIHZpc3VhbGl6aW5nIGxhcmdlIGRhdGFzZXRzIGFuZCBpZGVudGlmeWluZyBwYXR0ZXJucyBvciBjbHVzdGVycy4KCiMjIFBvcHVsYXIgVG9vbHMgZm9yIERhdGEgVmlzdWFsaXphdGlvbgoKLSAgICoqTWljcm9zb2Z0IEV4Y2VsKio6IEEgd2lkZWx5IHVzZWQgc3ByZWFkc2hlZXQgYXBwbGljYXRpb24gdGhhdCBvZmZlcnMgdmFyaW91cyB2aXN1YWxpemF0aW9uIGNhcGFiaWxpdGllcy4KLSAgICoqVGFibGVhdSoqOiBBIHBvd2VyZnVsLCB1c2VyLWZyaWVuZGx5IGRhdGEgdmlzdWFsaXphdGlvbiBzb2Z0d2FyZSB0aGF0IHJlcXVpcmVzIG1pbmltYWwgcHJvZ3JhbW1pbmcga25vd2xlZGdlLgotICAgKipQeXRob24qKjogTGlicmFyaWVzIGxpa2UgTWF0cGxvdGxpYiwgU2VhYm9ybiwgYW5kIFBsb3RseSBvZmZlciByb2J1c3QgdmlzdWFsaXphdGlvbiBvcHRpb25zIGZvciB0aG9zZSBmYW1pbGlhciB3aXRoIFB5dGhvbi4KLSAgICoqUioqOiBBIHByb2dyYW1taW5nIGxhbmd1YWdlIGFuZCBzb2Z0d2FyZSBlbnZpcm9ubWVudCBmb3Igc3RhdGlzdGljYWwgY29tcHV0aW5nIGFuZCBncmFwaGljcywgd2l0aCBtYW55IHBhY2thZ2VzIGZvciBkYXRhIHZpc3VhbGl6YXRpb24sIHN1Y2ggYXMgKipnZ3Bsb3QyKiogYW5kICoqcGxvdGx5KiouCgpUaGlzIGxlc3NvbiB3aWxsIGZvY3VzIG9uIGRhdGEgdmlzdWFsaXphdGlvbiB1c2luZyBCYXNlIFIuIE90aGVyIGxlc3NvbnMgcHJvdmlkZSBtb3JlIGluZm9ybWF0aW9uIG9uICoqZ2dwbG90MioqCgojIyBUaXBzIGZvciBFZmZlY3RpdmUgRGF0YSBWaXN1YWxpemF0aW9uCgotICAgKipDaG9vc2UgdGhlIFJpZ2h0IENoYXJ0IFR5cGUqKjogU2VsZWN0IGEgdmlzdWFsaXphdGlvbiB0eXBlIHRoYXQgYmVzdCByZXByZXNlbnRzIHRoZSBkYXRhIGFuZCB0aGUgc3RvcnkgeW91IHdhbnQgdG8gdGVsbC4KLSAgICoqS2VlcCBJdCBTaW1wbGUqKjogRm9jdXMgb24gY2xhcml0eSBhbmQgYXZvaWQgY2x1dHRlciBieSBsaW1pdGluZyB0aGUgbnVtYmVyIG9mIGNvbG9ycywgc2hhcGVzLCBhbmQgbGFiZWxzLgotICAgKipVc2UgQXBwcm9wcmlhdGUgU2NhbGVzKio6IENob29zZSBhcHByb3ByaWF0ZSBzY2FsZXMgYW5kIGF4aXMgbGltaXRzIHRvIGF2b2lkIG1pc2xlYWRpbmcgcmVwcmVzZW50YXRpb25zIG9mIHRoZSBkYXRhLgotICAgKipDb25zaWRlciBBY2Nlc3NpYmlsaXR5Kio6IEVuc3VyZSB0aGF0IHZpc3VhbGl6YXRpb25zIGFyZSBhY2Nlc3NpYmxlIHRvIHRob3NlIHdpdGggY29sb3IgdmlzaW9uIGRlZmljaWVuY2llcyBhbmQgb3RoZXIgaW1wYWlybWVudHMuCgojIyBTY2F0dGVycGxvdHMKClNjYXR0ZXJwbG90cywgYWxzbyBrbm93biBhcyBzY2F0dGVyIGdyYXBocyBvciBzY2F0dGVyIGRpYWdyYW1zLCBhcmUgYSB0eXBlIG9mIGRhdGEgdmlzdWFsaXphdGlvbiB1c2VkIHRvIGRpc3BsYXkgdGhlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHR3byBjb250aW51b3VzIHZhcmlhYmxlcy4gSW4gYSBzY2F0dGVycGxvdCwgZWFjaCBkYXRhIHBvaW50IGlzIHJlcHJlc2VudGVkIGFzIGEgZG90IChvciBvdGhlciBzeW1ib2wpIG9uIGEgQ2FydGVzaWFuIGNvb3JkaW5hdGUgcGxhbmUsIHdpdGggb25lIHZhcmlhYmxlIHBsb3R0ZWQgb24gdGhlIHgtYXhpcyBhbmQgdGhlIG90aGVyIG9uIHRoZSB5LWF4aXMuCgpTY2F0dGVycGxvdHMgYXJlIHBhcnRpY3VsYXJseSB1c2VmdWwgZm9yIGlkZW50aWZ5aW5nIHBhdHRlcm5zIG9yIHRyZW5kcyBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLCBzdWNoIGFzIHBvc2l0aXZlIG9yIG5lZ2F0aXZlIGNvcnJlbGF0aW9ucywgYW5kIGZvciBkZXRlY3Rpbmcgb3V0bGllcnMuIEJ5IHZpc3VhbGx5IGV4YW1pbmluZyBhIHNjYXR0ZXJwbG90LCBvbmUgY2FuIGdhaW4gaW5zaWdodHMgaW50byB0aGUgc3RyZW5ndGggYW5kIGRpcmVjdGlvbiBvZiB0aGUgcmVsYXRpb25zaGlwIGJldHdlZW4gdGhlIHZhcmlhYmxlcy4KCkZvciBleGFtcGxlLCBpZiB0aGUgZGF0YSBwb2ludHMgaW4gYSBzY2F0dGVycGxvdCBmb3JtIGFuIHVwd2FyZC1zbG9waW5nIHBhdHRlcm4sIGl0IGluZGljYXRlcyBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMsIG1lYW5pbmcgdGhhdCBhcyBvbmUgdmFyaWFibGUgaW5jcmVhc2VzLCBzbyBkb2VzIHRoZSBvdGhlci4gQ29udmVyc2VseSwgaWYgdGhlIGRhdGEgcG9pbnRzIGZvcm0gYSBkb3dud2FyZC1zbG9waW5nIHBhdHRlcm4sIGl0IHN1Z2dlc3RzIGEgbmVnYXRpdmUgY29ycmVsYXRpb24sIG1lYW5pbmcgdGhhdCBhcyBvbmUgdmFyaWFibGUgaW5jcmVhc2VzLCB0aGUgb3RoZXIgZGVjcmVhc2VzLiBJZiB0aGUgZGF0YSBwb2ludHMgYXJlIHNjYXR0ZXJlZCByYW5kb21seSB3aXRob3V0IGFueSBkaXNjZXJuaWJsZSBwYXR0ZXJuLCBpdCBpbmRpY2F0ZXMgdGhhdCB0aGVyZSBtYXkgYmUgbGl0dGxlIG9yIG5vIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhlIHR3byB2YXJpYWJsZXMuCgpUbyBjcmVhdGUgYSBzY2F0dGVycGxvdCBpbiBSLCB5b3UgY2FuIHVzZSB0aGUgYHBsb3QoKWAgZnVuY3Rpb24sIHdoaWNoIGlzIGEgYnVpbHQtaW4gZnVuY3Rpb24gaW4gdGhlIGJhc2UgUiBwYWNrYWdlLiBIZXJlJ3MgYSBzaW1wbGUgZXhhbXBsZSB0aGF0IGRlbW9uc3RyYXRlcyBob3cgdG8gY3JlYXRlIGEgc2NhdHRlcnBsb3Qgb2YgdHdvIHZhcmlhYmxlczoKCmBgYHtyIHNjYXR0ZXJQbG90fQojIFNhbXBsZSBkYXRhCmRmIDwtIHJlYWQuY3N2KCJjdXN0b21lcnR4bmRhdGEuY3N2IikKZGYuYWdnIDwtIGFnZ3JlZ2F0ZShkZiRUb3RhbCwgbGlzdChkZiROdW1WaXNpdHMpLCBGVU49bWVhbikKCiMgQ3JlYXRlIGEgc2NhdHRlcnBsb3QgdXNpbmcgdGhlIHBsb3QoKSBmdW5jdGlvbgpwbG90KHggPSBkZi5hZ2dbLDFdLCAKICAgICB5ID0gZGYuYWdnWywyXSwgCiAgICAgbWFpbiA9ICJOdW1iZXIgb2YgVmlzaXRzIHZzIEF2ZXJhZ2UgVG90YWwgU3BlbmQiLCAKICAgICB4bGFiID0gIiNWaXNpdHMiLCAKICAgICB5bGFiID0gIkF2ZyBUb3RhbCAoJCkiLAogICAgIHR5cGUgPSAncCcpCgpgYGAKCkluIHRoZSBleGFtcGxlIGFib3ZlLCB3ZSBsb2FkIGEgW0NTVl0oY3VzdG9tZXJ0eG5kYXRhLmNzdikgb2Ygd2Vic2l0ZSB2aXNpdHMgaW50byBhIGRhdGFmcmFtZSBhbmQgdGhlbiB1c2UgdGhlIGBhZ2dyZWdhdGUoKWAgZnVuY3Rpb24gdG8gY2FsY3VsYXRlIHRoZSBhdmVyYWdlIHRvdGFsIGZvciBlYWNoIG51bWJlciBvZiB2aXNpdHMuIFdlIHRoZW4gcGxvdCB0aGUgYXZlcmFnZSBzcGVuZCBmb3IgZWFjaCBudW1iZXIgb2YgdmlzaXRzIHRvIGRldGVybWluZSBpZiB0aGVyZSdzIGEgY29ycmVsYXRpb24uCgpUaGUgcGF0dGVybiByZXZlYWxzIGEgcG9zaXRpdmUgY29ycmVsYXRpb24sICppLmUuKiwgdGhlIGF2ZXJhZ2UgdG90YWwgc3BlbmQgaW5jcmVhc2VzIHdpdGggdGhlIG51bWJlciBvZiB2aXNpdHMuIE9uY2UgYSBjb3JyZWxhdGlvbiBoYXMgYmVlbiB2aXN1YWxseSBkZXRlY3RlZCwgYSBzdGF0aXN0aWNhbCBhbmFseXNpcyBvZiB0aGUgY29ycmVsYXRpb24gc2hvdWxkIGJlIG1hZGUgYnkgY2FsY3VsYXRpbmcgYW4gYXBwcm9wcmlhdGUgY29ycmVsYXRpb24gbWV0cmljIHN1Y2ggYXMgUGVhcnNvbiBNb21lbnQgb3IgU3BlYXJtYW4gUmFuay4KCiMjIEJhciBDaGFydHMKCkJhciBjaGFydHMsIGFsc28ga25vd24gYXMgYmFyIGdyYXBocywgYXJlIGEgdHlwZSBvZiBkYXRhIHZpc3VhbGl6YXRpb24gdXNlZCB0byBkaXNwbGF5IGFuZCBjb21wYXJlIGNhdGVnb3JpY2FsIG9yIGRpc2NyZXRlIGRhdGEuIEluIGEgYmFyIGNoYXJ0LCBkYXRhIGNhdGVnb3JpZXMgYXJlIHJlcHJlc2VudGVkIGJ5IHJlY3Rhbmd1bGFyIGJhcnMsIHdoZXJlIHRoZSBsZW5ndGggb3IgaGVpZ2h0IG9mIGVhY2ggYmFyIGlzIHByb3BvcnRpb25hbCB0byB0aGUgdmFsdWUgb3IgZnJlcXVlbmN5IG9mIHRoZSBjYXRlZ29yeSBpdCByZXByZXNlbnRzLiBCYXIgY2hhcnRzIGNhbiBiZSBwbG90dGVkIGVpdGhlciB2ZXJ0aWNhbGx5IG9yIGhvcml6b250YWxseSwgd2l0aCB0aGUgeC1heGlzIHJlcHJlc2VudGluZyB0aGUgY2F0ZWdvcmllcyBhbmQgdGhlIHktYXhpcyByZXByZXNlbnRpbmcgdGhlIHZhbHVlcyBvciBmcmVxdWVuY2llcy4gSWYgdGhleSBhcmUgcGxvdHRlZCB2ZXJ0aWNhbGx5LCB0aGV5IGFyZSBhbHNvIHNvbWV0aW1lcyByZWZlcnJlZCB0byBhcyBjb2x1bW4gY2hhcnRzLgoKQmFyIGNoYXJ0cyBhcmUgYmVzdCB1c2VkIHdoZW46CgotICAgQ29tcGFyaW5nIGRhdGEgYWNyb3NzIGNhdGVnb3JpZXM6IEJhciBjaGFydHMgYXJlIGFuIGVmZmVjdGl2ZSB3YXkgdG8gY29tcGFyZSB2YWx1ZXMgb3IgZnJlcXVlbmNpZXMgYWNyb3NzIGRpZmZlcmVudCBjYXRlZ29yaWVzLCBtYWtpbmcgaXQgZWFzeSB0byBpZGVudGlmeSB0aGUgaGlnaGVzdCBvciBsb3dlc3QgdmFsdWVzIGFuZCBvYnNlcnZlIGdlbmVyYWwgdHJlbmRzLgoKLSAgIFNob3dpbmcgY2hhbmdlcyBvdmVyIHRpbWU6IFdoZW4gdGhlIGNhdGVnb3JpZXMgcmVwcmVzZW50IHRpbWUgcGVyaW9kcyAoZS5nLiwgbW9udGhzLCB5ZWFycyksIGJhciBjaGFydHMgY2FuIGJlIHVzZWQgdG8gdmlzdWFsaXplIGNoYW5nZXMgaW4gdGhlIGRhdGEgb3ZlciB0aW1lLiBJbiB0aGlzIGNhc2UsIGl0J3MgZXNzZW50aWFsIHRvIG1haW50YWluIGEgY29uc2lzdGVudCB0aW1lIGludGVydmFsIGJldHdlZW4gYmFycyB0byBhdm9pZCBtaXNpbnRlcnByZXRhdGlvbiBvZiB0aGUgZGF0YS4KCi0gICBEaXNwbGF5aW5nIGRpc3RyaWJ1dGlvbiBvciBjb21wb3NpdGlvbjogQmFyIGNoYXJ0cyBjYW4gYmUgdXNlZCB0byBzaG93IHRoZSBkaXN0cmlidXRpb24gb2YgZGF0YSBhY3Jvc3MgZGlmZmVyZW50IGNhdGVnb3JpZXMgb3IgdGhlIGNvbXBvc2l0aW9uIG9mIGEgd2hvbGUgYnkgdXNpbmcgc3RhY2tlZCBvciBncm91cGVkIGJhciBjaGFydHMuCgotICAgVmlzdWFsaXppbmcgc21hbGwgdG8gbW9kZXJhdGUtc2l6ZWQgZGF0YXNldHM6IEJhciBjaGFydHMgYXJlIG1vc3QgZWZmZWN0aXZlIGZvciBkaXNwbGF5aW5nIHNtYWxsIHRvIG1vZGVyYXRlLXNpemVkIGRhdGFzZXRzIHdpdGggYSBsaW1pdGVkIG51bWJlciBvZiBjYXRlZ29yaWVzLiBGb3IgbGFyZ2VyIGRhdGFzZXRzIG9yIHRob3NlIHdpdGggbnVtZXJvdXMgY2F0ZWdvcmllcywgb3RoZXIgdmlzdWFsaXphdGlvbiB0ZWNobmlxdWVzIGxpa2UgbGluZSBjaGFydHMgb3IgaGVhdCBtYXBzIG1pZ2h0IGJlIG1vcmUgYXBwcm9wcmlhdGUuCgpLZWVwIGluIG1pbmQgdGhhdCBiYXIgY2hhcnRzIGFyZSBub3Qgc3VpdGFibGUgZm9yIHZpc3VhbGl6aW5nIGNvbnRpbnVvdXMgZGF0YSwgYXMgdGhlIGRhdGEgbmVlZHMgdG8gYmUgZ3JvdXBlZCBpbnRvIGRpc2NyZXRlIGNhdGVnb3JpZXMgZm9yIHRoaXMgdHlwZSBvZiBjaGFydC4gRm9yIGNvbnRpbnVvdXMgZGF0YSwgY29uc2lkZXIgdXNpbmcgbGluZSBjaGFydHMsIHNjYXR0ZXJwbG90cywgb3Igb3RoZXIgdmlzdWFsaXphdGlvbiBtZXRob2RzLgoKQmFyIHBsb3RzIGNhbiBiZSBjcmVhdGVkIGluIFIgdXNpbmcgdGhlIGBiYXJwbG90KClgIGZ1bmN0aW9uLiBJdCB0YWtlcyBhIHZlY3RvciBhbmQgdGhlIHBsb3Qgd2lsbCBoYXZlIGJhcnMgd2l0aCB0aGVpciBoZWlnaHRzIGVxdWFsIHRvIHRoZSBlbGVtZW50cyBpbiB0aGUgdmVjdG9yLiBUaGUgZXhhbXBsZSBiZWxvdyBjcmVhdGVzIGEgY29sdW1uIGNoYXJ0IGZyb20gYSBbQ1NWXShjdXN0b21lcnR4bmRhdGEuY3N2KSBjb250YWluaW5nIGRhdGEgYWJvdXQgdmlzaXRzIHRvIGEgd2Vic2l0ZS4KCkluIHRoZSBleGFtcGxlIGJlbG93LCB0aGUgZnVuY3Rpb24gYHRhYmxlKClgIGlzIHVzZWQgdG8gY291bnQgaG93IG1hbnkgdmlzaXRzIGNvbWUgZnJvbSBlYWNoIE9TLgoKYGBge3IgY29sdW1uUGxvdH0KIyBTYW1wbGUgZGF0YQpkZiA8LSByZWFkLmNzdigiY3VzdG9tZXJ0eG5kYXRhLmNzdiIpCgojIGNvdW50IG51bWJlciBvZiB2aXNpdHMgYnkgT1MKdmlzaXRzQnlPUyA8LSB0YWJsZShkZiRPUykKCmJhcnBsb3QodmlzaXRzQnlPUywKICAgICAgICBtYWluID0gIlZpc2l0cyBieSBPUyIsCiAgICAgICAgeGxhYiA9ICJPUyIsCiAgICAgICAgeWxhYiA9ICIjVmlzaXRzIiwKICAgICAgICBuYW1lcy5hcmcgPSBjKCJBbmRyb2lkIiwgImlPUyIpLAogICAgICAgIGNvbCA9ICJkYXJrcmVkIiwKICAgICAgICBob3JpeiA9IEZBTFNFKQpgYGAKClRoZSBgYmFycGxvdCgpYCBmdW5jdGlvbiBoYXMgYSBmZXcga2V5IHBhcmFtZXRlcnMKCi0gICAqbWFpbiogaXMgdGhlIG1haW4gdGl0bGUgZm9yIHRoZSBjaGFydAotICAgKnN1YiogaXMgYSBzdWJ0aXRsZSBmb3IgdGhlIGNoYXJ0Ci0gICAqaG9yaXoqIGNhbiBiZSBzZXQgdG8gKlRSVUUqIG9yICpGQUxTRSo7IGlmICpGQUxTRSogdGhlbiB0aGUgcGxvdCBpcyBhIGNvbHVtbiBjaGFydAotICAgKnhsYWIqIGFuZCAqeWxhYiogYXJlIHRoZSB4LSBhbmQgeS1heGlzIGxhYmVscwotICAgKm5hbWVzLmFyZyogaXMgYSB2ZWN0b3Igb2YgbGFiZWxzIGZvciBlYWNoIGJhcgotICAgKmNvbCogaXMgdGhlIGNvbG9yIG9mIHRoZSBiYXJzCgpUaGUgc2Vjb25kIGV4YW1wbGUgaXMgYSBiYXIgY2hhcnQgc2hvd2luZyB0aGUgdG90YWwgcmV2ZW51ZSBieSBvcGVyYXRpbmcgc3lzdGVtLgoKYGBge3IgYmFyUGxvdH0KIyBTYW1wbGUgZGF0YQpkZiA8LSByZWFkLmNzdigiY3VzdG9tZXJ0eG5kYXRhLmNzdiIpCgojIGNhbGN1bGF0ZSB0aGUgcmV2ZW51ZSBieSBPUwpkZi5hZ2cgPC0gYWdncmVnYXRlKGRmJFRvdGFsLCBsaXN0KGRmJE9TKSwgRlVOPXN1bSkKCmJhcnBsb3QoZGYuYWdnJHgsCiAgICAgICAgbWFpbiA9ICJSZXZlbnVlIGJ5IE9TIiwKICAgICAgICBzdWIgPSAiaW4gVVMkIGZvciBRMSAyMDIzIiwKICAgICAgICB4bGFiID0gIk9TIiwKICAgICAgICB5bGFiID0gIlJldmVudWUgKFVTJCkiLAogICAgICAgIG5hbWVzLmFyZyA9IGMoIkFuZHJvaWQiLCAiaU9TIiksCiAgICAgICAgY29sID0gIm5hdnkiLAogICAgICAgIGhvcml6ID0gVFJVRSkKYGBgCgojIyBMaW5lIEdyYXBocwoKVGhlIGV4YW1wbGVzIGJlbG93IHVzZSB0aGUgZGF0YSBmcm9tIFtwaGFybWFTYWxlc1R4bi5jc3ZdKHBoYXJtYVNhbGVzVHhuLmNzdikgY29udGFpbmluZyBzYWxlcyB0cmFuc2FjdGlvbnMgd2l0aCBkYXRlcy4KCmBgYHtyIGxpbmVHcmFwaH0KIyByZWFkIGRhdGEgZnJvbSBDU1YKZGYgPC0gcmVhZC5jc3YoInBoYXJtYVNhbGVzVHhuLmNzdiIpCgojIGZvciBlYWNoIHRyYW5zYWN0aW9uIChyb3cpLCBleHRyYWN0IGFtb3VudCBhbmQgbW9udGggKG1tL2RkL3l5eXkpCmRmLnNhbGVzIDwtIGRhdGEuZnJhbWUoCiAgbW9udGggPSBkZiRkYXRlLAogIGFtb3VudCA9IGRmJGFtb3VudAopCgpuIDwtIG5yb3coZGYuc2FsZXMpCmZvciAoaSBpbiAxOm4pIHsKICBkZi5zYWxlcyRtb250aFtpXSA8LSBzdHJzcGxpdChkZi5zYWxlcyRtb250aFtpXSwgIi8iKVtbMV1dWzFdCn0KCiMgc3VtIHRoZSBzYWxlcyBwZXIgbW9udGgKZGYuYWdnIDwtIGFnZ3JlZ2F0ZShkZi5zYWxlcyRhbW91bnQsIGxpc3QoZGYuc2FsZXMkbW9udGgpLCBGVU49c3VtKQoKIyByZW5hbWUgdGhlIGNvbHVtbnMKY29sbmFtZXMoZGYuYWdnKSA8LSBjKCJtb250aCIsImFtb3VudCIpCgojIGNvbnZlcnQgbW9udGhzIHRvIGludGVnZXJzIGFuZCBvcmRlciBieSBtb250aApkZi5hZ2ckbW9udGggPC0gYXMuaW50ZWdlcihkZi5hZ2ckbW9udGgpCmRmLmFnZyA8LSBkZi5hZ2dbb3JkZXIoZGYuYWdnJG1vbnRoKSwgXQoKIyBwbG90IHRoZSByZXZlbnVlIHRvdGFsIGJ5IG1vbnRoCnBsb3QoeCA9IGRmLmFnZyRtb250aCwgeSA9IGRmLmFnZyRhbW91bnQsIAogICAgIHR5cGUgPSAiYiIsIHBjaCA9IDE5LCAKICAgICBjb2wgPSAicmVkIiwgCiAgICAgeGxhYiA9ICJNb250aCIsIHlsYWIgPSAiVG90YWwgU2FsZXMgKCQpIikKYGBgCgojIyBTdGFja2luZyBQbG90cwoKU2V2ZXJhbCBwbG90cyBjYW4gYmUgYWRkZWQgdG8gdGhlIHNhbWUgdmlzdWFsaXphdGlvbi4gVGhlIGBwbG90KClgIGZ1bmN0aW9uIGNyZWF0ZXMgdGhlIGNhbnZhcyBmb3IgdGhlIHZpc3VhbGl6YXRpb24gYW5kIHRoZSBjYWxsIHRvIGBsaW5lcygpYCBhZGRzIGEgbGluZSB0byB0aGUgZXhpc3RpbmcgY2FudmFzIGNyZWF0ZWQgYnkgYHBsb3QoKWAuCgpXaGVuIHN0YWNraW5nIHBsb3RzLCAqaS5lLiosIHdoZW4gYWRkaW5nIHR3byBwbG90cyB0byBhIHNpbmdsZSBjYW52YXMsIGl0IGlzIGNyaXRpY2FsIHRoYXQgdGhlIHZhbHVlcyBmb3IgYm90aCBhcmUgYWNjb21tb2RhdGVkIGJ5IHRoZSByYW5nZSBvZiB4IGFuZCB5IHZhbHVlcyBvZiB0aGUgZmlyc3QgcGxvdCBjcmVhdGVkIHdpdGggdGhlIGBwbG90KClgIGZ1bmN0aW9uLgoKYGBge3Igc3RhY2tlZFBsb3REYXRhfQojIHJlYWQgZGF0YSBmcm9tIENTVgpkZiA8LSByZWFkLmNzdigicGhhcm1hU2FsZXNUeG4uY3N2IikKCiMgZm9yIGVhY2ggdHJhbnNhY3Rpb24gKHJvdyksIGV4dHJhY3QgYW1vdW50IGFuZCBtb250aCAobW0vZGQveXl5eSkKZGYuc2FsZXMgPC0gZGF0YS5mcmFtZSgKICBtb250aCA9IGRmJGRhdGUsCiAgYW1vdW50ID0gZGYkYW1vdW50CikKCm4gPC0gbnJvdyhkZi5zYWxlcykKZm9yIChpIGluIDE6bikgewogIGRmLnNhbGVzJG1vbnRoW2ldIDwtIHN0cnNwbGl0KGRmLnNhbGVzJG1vbnRoW2ldLCAiLyIpW1sxXV1bMV0KfQoKIyBzdW0gYW5kIGF2ZXJhZ2UgdGhlIHNhbGVzIHBlciBtb250aApkZi5hZ2cgPC0gYWdncmVnYXRlKGRmLnNhbGVzJGFtb3VudCwgbGlzdChkZi5zYWxlcyRtb250aCksIAogICAgICAgICAgICAgICAgICAgIEZVTiA9IGZ1bmN0aW9uKHgpIGModG90YWwgPSBzdW0oeCksIGF2ZyA9IG1lYW4oeCkgKSkKCiMgZXh0cmFjdCBkYXRhIHRvIG5ldyBkYXRhZnJhbWUKZGYgPC0gZGF0YS5mcmFtZSgKICBtb250aCA9IGFzLmludGVnZXIoZGYuYWdnJEdyb3VwLjEpLAogIHRvdGFsID0gYXMubnVtZXJpYyhkZi5hZ2ckeFssMV0pLAogIGF2ZyA9IGFzLm51bWVyaWMoZGYuYWdnJHhbLDJdKSwKICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgcm93Lm5hbWVzID0gTlVMTCkKCiMgb3JkZXIgYnkgbW9udGgKZGYgPC0gZGZbb3JkZXIoZGYkbW9udGgpLCBdCgpkZiA8LSBkYXRhLmZyYW1lKAogIG1vbnRoID0gMToxMiwKICB0b3RhbCA9IHNhbXBsZSgyMDAwOjMwMDAsIDEyKSwKICBhdmcgPSBzYW1wbGUoMTAwMDoxODAwLCAxMikKKQpgYGAKCkluIHRoZSBjb2RlIGJsb2NrIGJlbG93LCB3ZSBhZGQgbGluZXMgZm9yIHRoZSAidG90YWwiIGFuZCB0aGUgImF2ZXJhZ2UiIHNvbGQgcGVyIG1vbnRoLCBzbyB3ZSBuZWVkIGEgcmFuZ2Ugb2YgdmFsdWVzIHRoYXQgYWNjb21tb2RhdGVzIGJvdGggbnVtZXJpYyByYW5nZXMuCgpgYGB7ciBzdGFja2VkUGxvdH0KIyBkZXRlcm1pbmUgbWluaW11bSBhbmQgbWF4aW11bSB5IGF4aXMgdmFsdWVzCnlsaW0ubWluID0gbWluKGRmJHRvdGFsLGRmJGF2ZykgKiAwLjgKeWxpbS5tYXggPSBtYXgoZGYkdG90YWwsZGYkYXZnKSAqIDEuMgoKIyBDcmVhdGUgYSBmaXJzdCBsaW5lOiBtb250aGx5IHNhbGVzIHRvdGFsCnBsb3QoZGYkbW9udGgsIGRmJHRvdGFsLCB0eXBlID0gImIiLCBmcmFtZSA9IEZBTFNFLCBwY2ggPSAxOSwgCiAgICAgeGxpbSA9IGMoMSwgMTIpLAogICAgIHlsaW0gPSBjKHlsaW0ubWluLCB5bGltLm1heCksCiAgICAgbWFpbiA9ICJBdmVyYWdlIHZzIFRvdGFsIFNhbGVzIFBlciBNb250aCIsCiAgICAgc3ViID0gIlllYXIgMjAyMCIsCiAgICAgY29sID0gInJlZCIsIHhsYWIgPSAiTW9udGgiLCB5bGFiID0gIkFtb3VudCAoJCkiKQoKIyBBZGQgYSBzZWNvbmQgbGluZTogbW9udGhseSBhdmVyYWdlIHNhbGVzCmxpbmVzKGRmJG1vbnRoLCBkZiRhdmcsIHBjaCA9IDE4LCBjb2wgPSAiYmx1ZSIsIHR5cGUgPSAiYiIsIGx0eSA9IDIpCmBgYAoKIyMgQ3VzdG9taXppbmcgUGxvdHMKClIgb2ZmZXJzIHNldmVyYWwgb3B0aW9ucyB0byBjdXN0b21pemUgdGhlIGNoYXJ0IGFwcGVhcmFuY2Ugd2l0aCBwYXJhbWV0ZXJzIHRvIHRoZSBgcGxvdCgpYCBmdW5jdGlvbi4KCi0gICAqY2V4KiDihpIgc2hhcGUgc2l6ZQotICAgKmx3ZCog4oaSIGxpbmUgd2lkdGgKLSAgICpjb2wqIOKGkiBjb250cm9sIGNvbG9ycwotICAgKmx0eSog4oaSIGxpbmUgdHlwZQotICAgKnBjaCog4oaSIG1hcmtlciBzaGFwZQotICAgKnR5cGUqIOKGkiBsaW5rIGJldHdlZW4gZG90cwoKVGhlIGFyZ3VtZW50ICp0eXBlKiBkZXRlcm1pbmVzIGhvdyB0aGUgbGluZSBzZWdtZW50IChvciwgdG8gYmUgbW9yZSBwcmVjaXNlLCB0aGUgY29ubmVjdGlvbiBiZXR3ZWVuIHRoZSBwb2ludHMpIGFyZSBkcmF3bjoKCi0gICAncCcgLS0gb25seSBkcmF3IHBvaW50cyBhbmQgbm8gbGluZXMgd2hpY2ggY3JlYXRlcyBhIHNjYXR0ZXJwbG90Ci0gICAnYicgLS0gY29ubmVjdCBzZWdtZW50cyBidXQgbGVhdmUgYSBnYXAgYXJvdW5kIGVhY2ggcG9pbnQKLSAgICdsJyAtLSBmdWxseSBjb25uZWN0IHRoZSBwb2ludHMgd2l0aCBsaW5lcwoKVGhlIGFyZ3VtZW50ICpwY2gqIGRldGVybWluZXMgaG93IGVhY2ggcG9pbnQgaXMgZHJhd24gYW5kIGlzIGEgdmFsdWUgYmV0d2VlbiAxIGFuZCAxOS4gRm9yIGV4YW1wbGU6CgotICAgMTkgLS0gbGFyZ2UgZG90Ci0gICA0IC0tIGNyb3NzCgpUaGUgYXJndW1lbnQgKmx3ZCogZGV0ZXJtaW5lcyB0aGUgbGluZSB3aWR0aC4gSXQgaXMgYSB2YWx1ZSBmcm9tIDEgdG8gNC4KClRoZSBhcmd1bWVudCAqbHR5KiBkZXRlcm1pbmVzIHRoZSBsaW5lIHR5cGUuIExpbmUgdHlwZXMgY2FuIGVpdGhlciBiZSBzcGVjaWZpZWQgYXMgYW4gaW50ZWdlciAoMD1ibGFuaywgMT1zb2xpZCAoZGVmYXVsdCksIDI9ZGFzaGVkLCAzPWRvdHRlZCwgND1kb3RkYXNoLCA1PWxvbmdkYXNoLCA2PXR3b2Rhc2gpIG9yIGFzIG9uZSBvZiB0aGUgY2hhcmFjdGVyIHN0cmluZ3MgImJsYW5rIiwgInNvbGlkIiwgImRhc2hlZCIsICJkb3R0ZWQiLCAiZG90ZGFzaCIsICJsb25nZGFzaCIsIG9yICJ0d29kYXNoIiwgd2hlcmUgImJsYW5rIiB1c2VzICdpbnZpc2libGUgbGluZXMnICgqaS5lLiosIGRvZXMgbm90IGRyYXcgdGhlbSkuCgpUaGUgY2hlYXQgc2hlZXQgYmVsb3cgaGVscHMgc3VtbWFyaXplIHRoZSB2YWx1ZXM6CgpgYGB7ciBjaGVhdFNoZWV0T3B0aW9uc1Bsb3QsIGVjaG89Rn0KcGFyKG1hcj1jKDMsMywzLDMpKQpudW0gPC0gMCA7IApudW0xIDwtIDAKcGxvdCgwLDAgLCB4bGltPWMoMCwyMSkgLCB5bGltPWMoMC41LDYuNSksIGNvbD0id2hpdGUiICwgeWF4dD0ibiIgLCB5bGFiPSJBcmd1bWVudCIgLCB4bGFiPSIiKQogCiNmaWxsIHRoZSBncmFwaApmb3IgKGkgaW4gc2VxKDEsMjApKXsKICBwb2ludHMoaSwxICwgcGNoPWkgLCBjZXg9MykKICBwb2ludHMoaSwyICwgY29sPWkgLCBwY2g9MTYgLCBjZXg9MykKICBwb2ludHMoaSwzICwgY29sPSJibGFjayIgLCBwY2g9MTYgLCBjZXg9aSowLjI1KQogIAogICNsdHkKICBpZihpICVpbiUgYyhzZXEoMSwxOCwzKSkpewogICAgICAgIG51bT1udW0rMQogICAgcG9pbnRzKGMoaSxpKzIpLCBjKDQsNCkgLCBjb2w9ImJsYWNrIiAsIGx0eT1udW0gLCB0eXBlPSJsIiAsIGx3ZD0yKQogICAgICAgIHRleHQoaSsxLjEgLCA0LjE1ICwgbnVtKQogICAgICAgIH0KICAKICAjdHlwZSBhbmQgbHdkIAogIGlmKGkgJWluJSBjKHNlcSgxLDIwLDUpKSl7CiAgICBudW0xPW51bTErMQogICAgcG9pbnRzKGMoaSxpKzEsaSsyLGkrMyksIGMoNSw1LDUsNSkgLCBjb2w9ImJsYWNrIiAgLCB0eXBlPWMoInAiLCJsIiwiYiIsIm8iKVtudW0xXSAsIGx3ZD0yKQogICAgdGV4dChpKzEuMSAsIDUuMiAsIGMoInAiLCJsIiwiYiIsIm8iKVtudW0xXSApCiAgICBwb2ludHMoYyhpLGkrMSxpKzIsaSszKSwgYyg2LDYsNiw2KSAsIGNvbD0iYmxhY2siICAsIHR5cGU9ImwiLCAgbHdkPW51bTEpCiAgICB0ZXh0KGkrMS4xICwgNi4yICwgbnVtMSApCiAKICAgIH0KICB9CiAKI2FkZCBheGlzCmF4aXMoMiwgYXQgPSBjKDEsMiwzLDQsNSw2KSwgbGFiZWxzID0gYygicGNoIiAsICJjb2wiICwgImNleCIgLCAibHR5IiwgInR5cGUiICwgImx3ZCIgKSwgCiAgICAgdGljayA9IFRSVUUsIGNvbCA9ICJibGFjayIsIGxhcyA9IDEsIGNleC5heGlzID0gMC44KQpgYGAKCiMjIEFkZGluZyBMZWdlbmRzCgpMZWdlbmRzIGNhbiBiZSBhZGRlZCB0byBhIHBsb3QgdXNpbmcgdGhlIGBsZWdlbmQoKWAgZnVuY3Rpb24uIEl0IHVzZXMgdGhlIHNhbWUgZGF0YWZyYW1lIGFuZCB2YWx1ZXMgZnJvbSBhIHByZXZpb3VzIGJsb2NrLgoKYGBge3IgYWRkTGVnZW5kfQojIHRvdGFsIHBlciBtb250aApwbG90KGRmJG1vbnRoLCBkZiR0b3RhbCwgdHlwZSA9ICJiIiwgZnJhbWUgPSBGQUxTRSwgcGNoID0gMTksIAogICAgIHhsaW0gPSBjKDEsIDEyKSwKICAgICB5bGltID0gYyh5bGltLm1pbiwgeWxpbS5tYXgpLAogICAgIG1haW4gPSAiVG90YWwgdnMgQXZlcmFnZSBTYWxlcyBQZXIgTW9udGgiLAogICAgIGNvbCA9ICJyZWQiLCB4bGFiID0gIk1vbnRoIiwgeWxhYiA9ICIkIikKCiMgYXZlcmFnZSBwZXIgbW9udGgKbGluZXMoZGYkbW9udGgsIGRmJGF2ZywgcGNoID0gMTgsIGNvbCA9ICJibHVlIiwgdHlwZSA9ICJiIiwgbHR5ID0gMikKCiMgYWRkIGEgbGVnZW5kIHRvIHRoZSBwbG90CmxlZ2VuZCgidG9wbGVmdCIsIGxlZ2VuZD1jKCJUb3RhbCBTYWxlcyIsICJBdmVyYWdlIFNhbGVzIiksCiAgICAgICBjb2w9YygicmVkIiwgImJsdWUiKSwgbHR5ID0gMToyLCBjZXg9MC44KQpgYGAKCiMjIFN1bW1hcnkKCkJ5IGdhaW5pbmcgYSBzdHJvbmcgZm91bmRhdGlvbiBpbiBleHBsb3JhdG9yeSBkYXRhIHZpc3VhbGl6YXRpb24sIGRhdGEgYW5hbHlzdHMgY2FuIGVuaGFuY2UgdGhlaXIgZGF0YSBhbmFseXRpY3Mgc2tpbGxzIGFuZCBlZmZlY3RpdmVseSBjb21tdW5pY2F0ZSB0aGVpciBmaW5kaW5ncy4gV2l0aCB0aGUgcmlnaHQgdG9vbHMgYW5kIHRlY2huaXF1ZXMsIHZpc3VhbGl6YXRpb25zIGNhbiB1bmxvY2sgdmFsdWFibGUgaW5zaWdodHMgYW5kIHN1cHBvcnQgZGF0YS1kcml2ZW4gZGVjaXNpb24tbWFraW5nLgoKLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tCgojIyBGaWxlcyAmIFJlc291cmNlcwoKYGBge3IgemlwRmlsZXMsIGVjaG89RkFMU0V9CnppcE5hbWUgPSBzcHJpbnRmKCJMZXNzb25GaWxlcy0lcy0lcy56aXAiLCAKICAgICAgICAgICAgICAgICBwYXJhbXMkY2F0ZWdvcnksCiAgICAgICAgICAgICAgICAgcGFyYW1zJG51bWJlcikKCnRleHRBTGluayA9IHBhc3RlMCgiQWxsIEZpbGVzIGZvciBMZXNzb24gIiwgCiAgICAgICAgICAgICAgIHBhcmFtcyRjYXRlZ29yeSwiLiIscGFyYW1zJG51bWJlcikKCiMgZG93bmxvYWRGaWxlc0xpbmsoKSBpcyBpbmNsdWRlZCBmcm9tIF9pbnNlcnQyREIuUgprbml0cjo6cmF3X2h0bWwoZG93bmxvYWRGaWxlc0xpbmsoIi4iLCB6aXBOYW1lLCB0ZXh0QUxpbmspKQpgYGAKCi0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLQoKIyMgUmVmZXJlbmNlcwoKTm9uZS4KCiMjIEVycmF0YQoKTm9uZSBjb2xsZWN0ZWQgeWV0LiBMZXQgdXMga25vdy4KCmBgYHs9aHRtbH0KPHNjcmlwdCBzcmM9Imh0dHBzOi8vZm9ybS5qb3Rmb3JtLmNvbS9zdGF0aWMvZmVlZGJhY2syLmpzIiB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiPgogIG5ldyBKb3Rmb3JtRmVlZGJhY2soewogICAgZm9ybUlkOiAiMjEyMTg3MDcyNzg0MTU3IiwKICAgIGJ1dHRvblRleHQ6ICJGZWVkYmFjayIsCiAgICBiYXNlOiAiaHR0cHM6Ly9mb3JtLmpvdGZvcm0uY29tLyIsCiAgICBiYWNrZ3JvdW5kOiAiI0Y1OTIwMiIsCiAgICBmb250Q29sb3I6ICIjRkZGRkZGIiwKICAgIGJ1dHRvblNpZGU6ICJsZWZ0IiwKICAgIGJ1dHRvbkFsaWduOiAiY2VudGVyIiwKICAgIHR5cGU6IGZhbHNlLAogICAgd2lkdGg6IDcwMCwKICAgIGhlaWdodDogNTAwLAogICAgaXNDYXJkRm9ybTogZmFsc2UKICB9KTsKPC9zY3JpcHQ+CmBgYApgYGB7ciBjb2RlPXhmdW46OnJlYWRfdXRmOChwYXN0ZTAoaGVyZTo6aGVyZSgpLCcvUi9fZGVwbG95S25pdC5SJykpLCBpbmNsdWRlID0gRkFMU0V9CmBgYAo=