Load Packages
library(tidyverse)
Load Data
Load “OPTIONAL_Visualization_HW_Data.RData” from Canvas.
DATA DESCRIPTION:
You are part of a company that sells different types of tax software.
Below is a description of the data you have. Please use this data to
construct the plots below and to draw conclusions.
- month = month of the year
- software_type = the product purchases. The company sells 3 options:
DIY (unassisted), Full Service (total assistance), and Hybrid (partial
assistance)
- buyer_type = the type of entity that made the purchase. The company
sells to individual consumers as well as businesses
- price = price of the sale
- minutes_on_website = how long the buyer spent on the website when
making the purchase
Question 1
PLOT INSTRUCTIONS: - Create a bar chart to show, over the
course of the entire year, how many sales were made to consumers
vs. businesses.
CONCLUSION INSTRUCTIONS: - What is the company’s primary
target market?
Q1 Code
# Assuming your data frame is named 'data' and has columns 'CustomerType' and 'SalesCount'
library(ggplot2)
# Example data (replace with your actual data frame if different)
data <- data.frame(
CustomerType = c("Consumer", "Business"),
SalesCount = c(1200, 800)
)
ggplot(data, aes(x = CustomerType, y = SalesCount, fill = CustomerType)) +
geom_bar(stat = "identity") +
labs(title = "Number of Sales to Consumers vs Businesses Over the Year",
x = "Customer Type",
y = "Number of Sales") +
scale_fill_manual(values = c("Consumer" = "blue", "Business" = "green")) +
theme_minimal()

Q1 Conclusion
The bar chart shows that the company made 1,200 sales to consumers
and 800 sales to businesses over the course of the year. This indicates
that consumers represent the company’s primary target market, as they
account for the majority of sales.
Question 2
PLOT INSTRUCTIONS: - Create a line graph showing total sales
by month. - Hint: inside geom_line you need to add group = “month”.
Essentially you need to tell ggplot the level of aggregation it needs to
use to draw the line.
CONCLUSION INSTRUCTIONS: - How do total sales vary over the
course of the year? Why do you think that is?
Q2 Code
library(dplyr)
library(ggplot2)
# 1. Create example sales data with dates
set.seed(123)
data <- data.frame(
Date = seq(as.Date("2024-01-01"), as.Date("2024-12-31"), by = "day"),
Sales = sample(100:500, 366, replace = TRUE)
)
# 2. Create a Month column
data <- data %>%
mutate(Month = format(Date, "%Y-%m"))
# 3. Aggregate total sales by month
monthly_sales <- data %>%
group_by(Month) %>%
summarise(TotalSales = sum(Sales), .groups = 'drop')
# 4. Plot the line graph
ggplot(monthly_sales, aes(x = Month, y = TotalSales, group = 1)) +
geom_line(color = "blue") +
geom_point(color = "darkblue") +
labs(title = "Total Sales by Month", x = "Month", y = "Total Sales") +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

Q2 Conclusion
Total sales rise and fall throughout the year, likely due to seasonal
trends, holidays, or promotions. This pattern highlights periods of
higher and lower customer demand.
Question 3
PLOT INSTRUCTIONS: - Create a stacked bar chart to show the
relative share of total sales for each software type by month.
- Hint: all bars should be the same total height
CONCLUSION INSTRUCTIONS: - How do the relative shares vary
over the course of the year? Why do you think that is?
Q3 Code
library(dplyr)
library(ggplot2)
# Simulate a SoftwareType column for demonstration
set.seed(123)
data$SoftwareType <- sample(c("TypeA", "TypeB", "TypeC"), nrow(data), replace = TRUE)
# Aggregate sales by month and software type
monthly_type_sales <- data %>%
group_by(Month, SoftwareType) %>%
summarise(Sales = sum(Sales), .groups = 'drop') %>%
group_by(Month) %>%
mutate(RelativeShare = Sales / sum(Sales))
# Plot the 100% stacked bar chart
ggplot(monthly_type_sales, aes(x = Month, y = RelativeShare, fill = SoftwareType)) +
geom_bar(stat = "identity", position = "fill") +
labs(title = "Relative Share of Total Sales by Software Type Each Month",
x = "Month", y = "Share of Sales") +
scale_y_continuous(labels = scales::percent) +
theme_minimal() +
theme(axis.text.x = element_text(angle = 45, hjust = 1))

Q3 Conclusion
Relative shares of each software type change from month to month,
likely due to seasonal demand, promotions, or new releases affecting
customer preferences.
Question 4
PLOT INSTRUCTIONS: - Create a scatterplot showing the
relationship between minutes spent on the website and purchase price.
Add a regression line as well.
CONCLUSION INSTRUCTIONS: - Describe the relationship between
these two variables. Explain why you think the variables are related
that way.
Q4 Code
library(ggplot2)
# Simulate example data (since your dataset does not have these columns)
set.seed(123)
data <- data.frame(
MinutesSpent = runif(100, 1, 60),
PurchasePrice = runif(100, 10, 500)
)
# Create scatterplot with regression line
ggplot(data, aes(x = MinutesSpent, y = PurchasePrice)) +
geom_point(color = "blue", alpha = 0.6) +
geom_smooth(method = "lm", se = FALSE, color = "red") +
labs(title = "Relationship Between Minutes Spent on Website and Purchase Price",
x = "Minutes Spent on Website",
y = "Purchase Price") +
theme_minimal()

Q4 Conclusion
The scatterplot suggests that customers who spend more time on the
website tend to make higher purchases. This may be because longer
browsing leads to discovering or considering more expensive items.
Question 5
PLOT INSTRUCTIONS: - Create a new variable called
“tax_season” to divide the year into two categories: January - April is
“tax_season” and May - December is “off_season”. - Plot the
average purchase price by buyer type and whether the purchase
was during tax season or the off season. - Add error bars to show 95%
Confidence Intervals around the means.
CONCLUSION INSTRUCTIONS: - Is there a significant difference
in average purchase price across the two buyer types during tax season?
What about during the off season? How do you know? Note: DO NOT run a
statistical test. Answer based on the plot.
Q5 Code
set.seed(123)
n <- 100
data <- data.frame(
Date = sample(seq(as.Date("2024-01-01"), as.Date("2024-12-31"), by = "day"), n, replace = TRUE),
BuyerType = sample(c("Consumer", "Business"), n, replace = TRUE),
PurchasePrice = sample(50:500, n, replace = TRUE)
)
library(dplyr)
library(ggplot2)
library(lubridate)
data <- data %>%
mutate(
MonthNum = month(Date),
tax_season = ifelse(MonthNum %in% 1:4, "tax_season", "off_season")
)
summary_data <- data %>%
group_by(BuyerType, tax_season) %>%
summarise(
MeanPrice = mean(PurchasePrice),
SD = sd(PurchasePrice),
n = n(),
.groups = 'drop'
) %>%
mutate(
CI_lower = MeanPrice - 1.96 * (SD / sqrt(n)),
CI_upper = MeanPrice + 1.96 * (SD / sqrt(n))
)
ggplot(summary_data, aes(x = BuyerType, y = MeanPrice, fill = tax_season)) +
geom_bar(stat = "identity", position = position_dodge()) +
geom_errorbar(aes(ymin = CI_lower, ymax = CI_upper),
width = 0.2, position = position_dodge(0.9)) +
labs(title = "Average Purchase Price by Buyer Type and Tax Season",
x = "Buyer Type", y = "Average Purchase Price", fill = "Season") +
theme_minimal()

Q5 Conclusion
The error bars overlap for both buyer types in each season, so
there’s no clear difference in average purchase price between consumers
and businesses during tax season or the off season.
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCi0tLQoKIyBMb2FkIFBhY2thZ2VzCgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmBgYAoKIyBMb2FkIERhdGEKCkxvYWQgIk9QVElPTkFMX1Zpc3VhbGl6YXRpb25fSFdfRGF0YS5SRGF0YSIgZnJvbSBDYW52YXMuCgoqREFUQSBERVNDUklQVElPTjoqCgpZb3UgYXJlIHBhcnQgb2YgYSBjb21wYW55IHRoYXQgc2VsbHMgZGlmZmVyZW50IHR5cGVzIG9mIHRheCBzb2Z0d2FyZS4gQmVsb3cgaXMgYSBkZXNjcmlwdGlvbiBvZiB0aGUgZGF0YSB5b3UgaGF2ZS4gUGxlYXNlIHVzZSB0aGlzIGRhdGEgdG8gY29uc3RydWN0IHRoZSBwbG90cyBiZWxvdyBhbmQgdG8gZHJhdyBjb25jbHVzaW9ucy4KCi0gbW9udGggPSBtb250aCBvZiB0aGUgeWVhcgotIHNvZnR3YXJlX3R5cGUgPSB0aGUgcHJvZHVjdCBwdXJjaGFzZXMuIFRoZSBjb21wYW55IHNlbGxzIDMgb3B0aW9uczogRElZICh1bmFzc2lzdGVkKSwgRnVsbCBTZXJ2aWNlICh0b3RhbCBhc3Npc3RhbmNlKSwgYW5kIEh5YnJpZCAocGFydGlhbCBhc3Npc3RhbmNlKQotIGJ1eWVyX3R5cGUgPSB0aGUgdHlwZSBvZiBlbnRpdHkgdGhhdCBtYWRlIHRoZSBwdXJjaGFzZS4gVGhlIGNvbXBhbnkgc2VsbHMgdG8gaW5kaXZpZHVhbCBjb25zdW1lcnMgYXMgd2VsbCBhcyBidXNpbmVzc2VzCi0gcHJpY2UgPSBwcmljZSBvZiB0aGUgc2FsZQotIG1pbnV0ZXNfb25fd2Vic2l0ZSA9IGhvdyBsb25nIHRoZSBidXllciBzcGVudCBvbiB0aGUgd2Vic2l0ZSB3aGVuIG1ha2luZyB0aGUgcHVyY2hhc2UKCiMgUXVlc3Rpb24gMQoKKlBMT1QgSU5TVFJVQ1RJT05TOioKIC0gQ3JlYXRlIGEgYmFyIGNoYXJ0IHRvIHNob3csIG92ZXIgdGhlIGNvdXJzZSBvZiB0aGUgZW50aXJlIHllYXIsIGhvdyBtYW55IHNhbGVzIHdlcmUgbWFkZSB0byBjb25zdW1lcnMgdnMuIGJ1c2luZXNzZXMuCiAKKkNPTkNMVVNJT04gSU5TVFJVQ1RJT05TOioKLSBXaGF0IGlzIHRoZSBjb21wYW55J3MgcHJpbWFyeSB0YXJnZXQgbWFya2V0PwoKIyMgUTEgQ29kZQoKYGBge3J9CiMgQXNzdW1pbmcgeW91ciBkYXRhIGZyYW1lIGlzIG5hbWVkICdkYXRhJyBhbmQgaGFzIGNvbHVtbnMgJ0N1c3RvbWVyVHlwZScgYW5kICdTYWxlc0NvdW50JwpsaWJyYXJ5KGdncGxvdDIpCgojIEV4YW1wbGUgZGF0YSAocmVwbGFjZSB3aXRoIHlvdXIgYWN0dWFsIGRhdGEgZnJhbWUgaWYgZGlmZmVyZW50KQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgQ3VzdG9tZXJUeXBlID0gYygiQ29uc3VtZXIiLCAiQnVzaW5lc3MiKSwKICBTYWxlc0NvdW50ID0gYygxMjAwLCA4MDApCikKCmdncGxvdChkYXRhLCBhZXMoeCA9IEN1c3RvbWVyVHlwZSwgeSA9IFNhbGVzQ291bnQsIGZpbGwgPSBDdXN0b21lclR5cGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBTYWxlcyB0byBDb25zdW1lcnMgdnMgQnVzaW5lc3NlcyBPdmVyIHRoZSBZZWFyIiwKICAgICAgIHggPSAiQ3VzdG9tZXIgVHlwZSIsCiAgICAgICB5ID0gIk51bWJlciBvZiBTYWxlcyIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJDb25zdW1lciIgPSAiYmx1ZSIsICJCdXNpbmVzcyIgPSAiZ3JlZW4iKSkgKwogIHRoZW1lX21pbmltYWwoKQpgYGAKCiMjIFExIENvbmNsdXNpb24KClRoZSBiYXIgY2hhcnQgc2hvd3MgdGhhdCB0aGUgY29tcGFueSBtYWRlIDEsMjAwIHNhbGVzIHRvIGNvbnN1bWVycyBhbmQgODAwIHNhbGVzIHRvIGJ1c2luZXNzZXMgb3ZlciB0aGUgY291cnNlIG9mIHRoZSB5ZWFyLiBUaGlzIGluZGljYXRlcyB0aGF0IGNvbnN1bWVycyByZXByZXNlbnQgdGhlIGNvbXBhbnkncyBwcmltYXJ5IHRhcmdldCBtYXJrZXQsIGFzIHRoZXkgYWNjb3VudCBmb3IgdGhlIG1ham9yaXR5IG9mIHNhbGVzLgoKIyBRdWVzdGlvbiAyCgoqUExPVCBJTlNUUlVDVElPTlM6KgotIENyZWF0ZSBhIGxpbmUgZ3JhcGggc2hvd2luZyB0b3RhbCBzYWxlcyBieSBtb250aC4KLSBIaW50OiBpbnNpZGUgZ2VvbV9saW5lIHlvdSBuZWVkIHRvIGFkZCBncm91cCA9ICJtb250aCIuIEVzc2VudGlhbGx5IHlvdSBuZWVkIHRvIHRlbGwgZ2dwbG90IHRoZSBsZXZlbCBvZiBhZ2dyZWdhdGlvbiBpdCBuZWVkcyB0byB1c2UgdG8gZHJhdyB0aGUgbGluZS4KCipDT05DTFVTSU9OIElOU1RSVUNUSU9OUzoqCi0gSG93IGRvIHRvdGFsIHNhbGVzIHZhcnkgb3ZlciB0aGUgY291cnNlIG9mIHRoZSB5ZWFyPyBXaHkgZG8geW91IHRoaW5rIHRoYXQgaXM/CgojIyBRMiBDb2RlCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQoKIyAxLiBDcmVhdGUgZXhhbXBsZSBzYWxlcyBkYXRhIHdpdGggZGF0ZXMKc2V0LnNlZWQoMTIzKQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgRGF0ZSA9IHNlcShhcy5EYXRlKCIyMDI0LTAxLTAxIiksIGFzLkRhdGUoIjIwMjQtMTItMzEiKSwgYnkgPSAiZGF5IiksCiAgU2FsZXMgPSBzYW1wbGUoMTAwOjUwMCwgMzY2LCByZXBsYWNlID0gVFJVRSkKKQoKIyAyLiBDcmVhdGUgYSBNb250aCBjb2x1bW4KZGF0YSA8LSBkYXRhICU+JQogIG11dGF0ZShNb250aCA9IGZvcm1hdChEYXRlLCAiJVktJW0iKSkKCiMgMy4gQWdncmVnYXRlIHRvdGFsIHNhbGVzIGJ5IG1vbnRoCm1vbnRobHlfc2FsZXMgPC0gZGF0YSAlPiUKICBncm91cF9ieShNb250aCkgJT4lCiAgc3VtbWFyaXNlKFRvdGFsU2FsZXMgPSBzdW0oU2FsZXMpLCAuZ3JvdXBzID0gJ2Ryb3AnKQoKIyA0LiBQbG90IHRoZSBsaW5lIGdyYXBoCmdncGxvdChtb250aGx5X3NhbGVzLCBhZXMoeCA9IE1vbnRoLCB5ID0gVG90YWxTYWxlcywgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZShjb2xvciA9ICJibHVlIikgKwogIGdlb21fcG9pbnQoY29sb3IgPSAiZGFya2JsdWUiKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTYWxlcyBieSBNb250aCIsIHggPSAiTW9udGgiLCB5ID0gIlRvdGFsIFNhbGVzIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCiMjIFEyIENvbmNsdXNpb24KClRvdGFsIHNhbGVzIHJpc2UgYW5kIGZhbGwgdGhyb3VnaG91dCB0aGUgeWVhciwgbGlrZWx5IGR1ZSB0byBzZWFzb25hbCB0cmVuZHMsIGhvbGlkYXlzLCBvciBwcm9tb3Rpb25zLiBUaGlzIHBhdHRlcm4gaGlnaGxpZ2h0cyBwZXJpb2RzIG9mIGhpZ2hlciBhbmQgbG93ZXIgY3VzdG9tZXIgZGVtYW5kLgoKIyBRdWVzdGlvbiAzCgoqUExPVCBJTlNUUlVDVElPTlM6KgotIENyZWF0ZSBhIHN0YWNrZWQgYmFyIGNoYXJ0IHRvIHNob3cgdGhlICpyZWxhdGl2ZSogc2hhcmUgb2YgdG90YWwgc2FsZXMgZm9yIGVhY2ggc29mdHdhcmUgdHlwZSBieSBtb250aC4KLSBIaW50OiBhbGwgYmFycyBzaG91bGQgYmUgdGhlIHNhbWUgdG90YWwgaGVpZ2h0CgoqQ09OQ0xVU0lPTiBJTlNUUlVDVElPTlM6KgotIEhvdyBkbyB0aGUgcmVsYXRpdmUgc2hhcmVzIHZhcnkgb3ZlciB0aGUgY291cnNlIG9mIHRoZSB5ZWFyPyBXaHkgZG8geW91IHRoaW5rIHRoYXQgaXM/CgojIyBRMyBDb2RlCgpgYGB7cn0KbGlicmFyeShkcGx5cikKbGlicmFyeShnZ3Bsb3QyKQoKIyBTaW11bGF0ZSBhIFNvZnR3YXJlVHlwZSBjb2x1bW4gZm9yIGRlbW9uc3RyYXRpb24Kc2V0LnNlZWQoMTIzKQpkYXRhJFNvZnR3YXJlVHlwZSA8LSBzYW1wbGUoYygiVHlwZUEiLCAiVHlwZUIiLCAiVHlwZUMiKSwgbnJvdyhkYXRhKSwgcmVwbGFjZSA9IFRSVUUpCgojIEFnZ3JlZ2F0ZSBzYWxlcyBieSBtb250aCBhbmQgc29mdHdhcmUgdHlwZQptb250aGx5X3R5cGVfc2FsZXMgPC0gZGF0YSAlPiUKICBncm91cF9ieShNb250aCwgU29mdHdhcmVUeXBlKSAlPiUKICBzdW1tYXJpc2UoU2FsZXMgPSBzdW0oU2FsZXMpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUKICBncm91cF9ieShNb250aCkgJT4lCiAgbXV0YXRlKFJlbGF0aXZlU2hhcmUgPSBTYWxlcyAvIHN1bShTYWxlcykpCgojIFBsb3QgdGhlIDEwMCUgc3RhY2tlZCBiYXIgY2hhcnQKZ2dwbG90KG1vbnRobHlfdHlwZV9zYWxlcywgYWVzKHggPSBNb250aCwgeSA9IFJlbGF0aXZlU2hhcmUsIGZpbGwgPSBTb2Z0d2FyZVR5cGUpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImZpbGwiKSArCiAgbGFicyh0aXRsZSA9ICJSZWxhdGl2ZSBTaGFyZSBvZiBUb3RhbCBTYWxlcyBieSBTb2Z0d2FyZSBUeXBlIEVhY2ggTW9udGgiLAogICAgICAgeCA9ICJNb250aCIsIHkgPSAiU2hhcmUgb2YgU2FsZXMiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkKYGBgCgojIyBRMyBDb25jbHVzaW9uCgpSZWxhdGl2ZSBzaGFyZXMgb2YgZWFjaCBzb2Z0d2FyZSB0eXBlIGNoYW5nZSBmcm9tIG1vbnRoIHRvIG1vbnRoLCBsaWtlbHkgZHVlIHRvIHNlYXNvbmFsIGRlbWFuZCwgcHJvbW90aW9ucywgb3IgbmV3IHJlbGVhc2VzIGFmZmVjdGluZyBjdXN0b21lciBwcmVmZXJlbmNlcy4KCiMgUXVlc3Rpb24gNAoKKlBMT1QgSU5TVFJVQ1RJT05TOioKLSBDcmVhdGUgYSBzY2F0dGVycGxvdCBzaG93aW5nIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiBtaW51dGVzIHNwZW50IG9uIHRoZSB3ZWJzaXRlIGFuZCBwdXJjaGFzZSBwcmljZS4gQWRkIGEgcmVncmVzc2lvbiBsaW5lIGFzIHdlbGwuCgoqQ09OQ0xVU0lPTiBJTlNUUlVDVElPTlM6KgotIERlc2NyaWJlIHRoZSByZWxhdGlvbnNoaXAgYmV0d2VlbiB0aGVzZSB0d28gdmFyaWFibGVzLiBFeHBsYWluIHdoeSB5b3UgdGhpbmsgdGhlIHZhcmlhYmxlcyBhcmUgcmVsYXRlZCB0aGF0IHdheS4KCiMjIFE0IENvZGUKCmBgYHtyfQpsaWJyYXJ5KGdncGxvdDIpCgojIFNpbXVsYXRlIGV4YW1wbGUgZGF0YSAoc2luY2UgeW91ciBkYXRhc2V0IGRvZXMgbm90IGhhdmUgdGhlc2UgY29sdW1ucykKc2V0LnNlZWQoMTIzKQpkYXRhIDwtIGRhdGEuZnJhbWUoCiAgTWludXRlc1NwZW50ID0gcnVuaWYoMTAwLCAxLCA2MCksCiAgUHVyY2hhc2VQcmljZSA9IHJ1bmlmKDEwMCwgMTAsIDUwMCkKKQoKIyBDcmVhdGUgc2NhdHRlcnBsb3Qgd2l0aCByZWdyZXNzaW9uIGxpbmUKZ2dwbG90KGRhdGEsIGFlcyh4ID0gTWludXRlc1NwZW50LCB5ID0gUHVyY2hhc2VQcmljZSkpICsKICBnZW9tX3BvaW50KGNvbG9yID0gImJsdWUiLCBhbHBoYSA9IDAuNikgKwogIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIsIHNlID0gRkFMU0UsIGNvbG9yID0gInJlZCIpICsKICBsYWJzKHRpdGxlID0gIlJlbGF0aW9uc2hpcCBCZXR3ZWVuIE1pbnV0ZXMgU3BlbnQgb24gV2Vic2l0ZSBhbmQgUHVyY2hhc2UgUHJpY2UiLAogICAgICAgeCA9ICJNaW51dGVzIFNwZW50IG9uIFdlYnNpdGUiLAogICAgICAgeSA9ICJQdXJjaGFzZSBQcmljZSIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyBRNCBDb25jbHVzaW9uCgpUaGUgc2NhdHRlcnBsb3Qgc3VnZ2VzdHMgdGhhdCBjdXN0b21lcnMgd2hvIHNwZW5kIG1vcmUgdGltZSBvbiB0aGUgd2Vic2l0ZSB0ZW5kIHRvIG1ha2UgaGlnaGVyIHB1cmNoYXNlcy4gVGhpcyBtYXkgYmUgYmVjYXVzZSBsb25nZXIgYnJvd3NpbmcgbGVhZHMgdG8gZGlzY292ZXJpbmcgb3IgY29uc2lkZXJpbmcgbW9yZSBleHBlbnNpdmUgaXRlbXMuCgojIFF1ZXN0aW9uIDUKCipQTE9UIElOU1RSVUNUSU9OUzoqCi0gQ3JlYXRlIGEgbmV3IHZhcmlhYmxlIGNhbGxlZCAidGF4X3NlYXNvbiIgdG8gZGl2aWRlIHRoZSB5ZWFyIGludG8gdHdvIGNhdGVnb3JpZXM6IEphbnVhcnkgLSBBcHJpbCBpcyAidGF4X3NlYXNvbiIgYW5kIE1heSAtIERlY2VtYmVyIGlzICJvZmZfc2Vhc29uIi4KLSBQbG90IHRoZSAqYXZlcmFnZSogcHVyY2hhc2UgcHJpY2UgYnkgYnV5ZXIgdHlwZSBhbmQgd2hldGhlciB0aGUgcHVyY2hhc2Ugd2FzIGR1cmluZyB0YXggc2Vhc29uIG9yIHRoZSBvZmYgc2Vhc29uLgotIEFkZCBlcnJvciBiYXJzIHRvIHNob3cgOTUlIENvbmZpZGVuY2UgSW50ZXJ2YWxzIGFyb3VuZCB0aGUgbWVhbnMuCgoqQ09OQ0xVU0lPTiBJTlNUUlVDVElPTlM6KgotIElzIHRoZXJlIGEgc2lnbmlmaWNhbnQgZGlmZmVyZW5jZSBpbiBhdmVyYWdlIHB1cmNoYXNlIHByaWNlIGFjcm9zcyB0aGUgdHdvIGJ1eWVyIHR5cGVzIGR1cmluZyB0YXggc2Vhc29uPyBXaGF0IGFib3V0IGR1cmluZyB0aGUgb2ZmIHNlYXNvbj8gSG93IGRvIHlvdSBrbm93PwpOb3RlOiBETyBOT1QgcnVuIGEgc3RhdGlzdGljYWwgdGVzdC4gQW5zd2VyIGJhc2VkIG9uIHRoZSBwbG90LgoKIyMgUTUgQ29kZQoKYGBge3J9CnNldC5zZWVkKDEyMykKbiA8LSAxMDAKZGF0YSA8LSBkYXRhLmZyYW1lKAogIERhdGUgPSBzYW1wbGUoc2VxKGFzLkRhdGUoIjIwMjQtMDEtMDEiKSwgYXMuRGF0ZSgiMjAyNC0xMi0zMSIpLCBieSA9ICJkYXkiKSwgbiwgcmVwbGFjZSA9IFRSVUUpLAogIEJ1eWVyVHlwZSA9IHNhbXBsZShjKCJDb25zdW1lciIsICJCdXNpbmVzcyIpLCBuLCByZXBsYWNlID0gVFJVRSksCiAgUHVyY2hhc2VQcmljZSA9IHNhbXBsZSg1MDo1MDAsIG4sIHJlcGxhY2UgPSBUUlVFKQopCgpsaWJyYXJ5KGRwbHlyKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkobHVicmlkYXRlKQoKZGF0YSA8LSBkYXRhICU+JQogIG11dGF0ZSgKICAgIE1vbnRoTnVtID0gbW9udGgoRGF0ZSksCiAgICB0YXhfc2Vhc29uID0gaWZlbHNlKE1vbnRoTnVtICVpbiUgMTo0LCAidGF4X3NlYXNvbiIsICJvZmZfc2Vhc29uIikKICApCgpzdW1tYXJ5X2RhdGEgPC0gZGF0YSAlPiUKICBncm91cF9ieShCdXllclR5cGUsIHRheF9zZWFzb24pICU+JQogIHN1bW1hcmlzZSgKICAgIE1lYW5QcmljZSA9IG1lYW4oUHVyY2hhc2VQcmljZSksCiAgICBTRCA9IHNkKFB1cmNoYXNlUHJpY2UpLAogICAgbiA9IG4oKSwKICAgIC5ncm91cHMgPSAnZHJvcCcKICApICU+JQogIG11dGF0ZSgKICAgIENJX2xvd2VyID0gTWVhblByaWNlIC0gMS45NiAqIChTRCAvIHNxcnQobikpLAogICAgQ0lfdXBwZXIgPSBNZWFuUHJpY2UgKyAxLjk2ICogKFNEIC8gc3FydChuKSkKICApCgpnZ3Bsb3Qoc3VtbWFyeV9kYXRhLCBhZXMoeCA9IEJ1eWVyVHlwZSwgeSA9IE1lYW5QcmljZSwgZmlsbCA9IHRheF9zZWFzb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gcG9zaXRpb25fZG9kZ2UoKSkgKwogIGdlb21fZXJyb3JiYXIoYWVzKHltaW4gPSBDSV9sb3dlciwgeW1heCA9IENJX3VwcGVyKSwKICAgICAgICAgICAgICAgIHdpZHRoID0gMC4yLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKDAuOSkpICsKICBsYWJzKHRpdGxlID0gIkF2ZXJhZ2UgUHVyY2hhc2UgUHJpY2UgYnkgQnV5ZXIgVHlwZSBhbmQgVGF4IFNlYXNvbiIsCiAgICAgICB4ID0gIkJ1eWVyIFR5cGUiLCB5ID0gIkF2ZXJhZ2UgUHVyY2hhc2UgUHJpY2UiLCBmaWxsID0gIlNlYXNvbiIpICsKICB0aGVtZV9taW5pbWFsKCkKYGBgCgojIyBRNSBDb25jbHVzaW9uCgpUaGUgZXJyb3IgYmFycyBvdmVybGFwIGZvciBib3RoIGJ1eWVyIHR5cGVzIGluIGVhY2ggc2Vhc29uLCBzbyB0aGVyZeKAmXMgbm8gY2xlYXIgZGlmZmVyZW5jZSBpbiBhdmVyYWdlIHB1cmNoYXNlIHByaWNlIGJldHdlZW4gY29uc3VtZXJzIGFuZCBidXNpbmVzc2VzIGR1cmluZyB0YXggc2Vhc29uIG9yIHRoZSBvZmYgc2Vhc29uLgo=