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.

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=