Notebook created by Leslie A. McFarlin, 26 Feb 2022

# Libraries - installations and loading done via Packges pane.
# Uncomment the line below to load the libraries
install.packages('caTools', repos = "https://cran.rstudio.org")
library(readxl, plyr, dplyr, ggplot2, tibble, tidyverse, gt, corrplot, caTools, MLmetrics, knitr)

# Import data set, create data frame, view data
walmart <- data.frame(read.csv("Walmart_Store_sales.csv"), stringsAsFactors = T)
View(walmart)

Basic Tasks

There are 5 basic tasks to perform using this data set: - Identify the store with maximum sales. - Identify the store with the greatest standard deviation, and its coefficient of mean to standard deviation. - Identify which store(s) showed good quarterly growth rate in Q3’2012. - Identify the holidays showing a higher sales than the mean sales of non-holiday stores in one view. - Provide monthly and semester view of sales in units along with insights.

# Check for missing values
walmart[!complete.cases(walmart)] # There does not appear to be any missing data

Identify the store with maximum sales

# Identify the stores
stores <- as.factor(walmart$Store)
store_count <- length(stores) # 45 stores

# Identify the weeks
weeks <- as.factor(walmart$Date)
week_count <- length(weeks) # 143 weeks

# Summary of sales by store
sales_summary <- data.frame(aggregate(walmart$Weekly_Sales,list(walmart$Store), FUN = sum))
View(sales_summary) # Views the returned data frame

max_sales_val <- max(sales_summary$x) # Highest sales: $301,397,792.46
max_sales_store <- sales_summary[sales_summary$x == max_sales_val, 1] # The store with highest sales, #20

# Plot the sales summary per store across the entire data set
sales_summary_bar <- ggplot(sales_summary, aes(x = factor(Group.1), y = x)) +
  geom_col(fill = '#440154') + 
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 0.5)) +
  labs(title = 'Yearly Sales', x = 'Store Number', y = 'Sales (in $)')

sales_summary_bar

Identify the store with the greatest standard deviation, and its coefficient of mean to standard deviation.

# Std dev per store
store_stdev <- data.frame(aggregate(walmart$Weekly_Sales, list(walmart$Store), FUN = sd))
View(store_stdev) # Views the returned data frame

# Coefficient of mean to standard deviation per store
store_mean <- (tapply(walmart$Weekly_Sales, stores, mean))
store_mean_coeff <- numeric()

for (i in 1:length(store_mean)) {
  store_mean_coeff[i] <- store_mean[i]/store_stdev$x[i]
}

store_stdev$store_mean_coeff <- store_mean_coeff
store_stdev$avg <- store_mean

# Do a final merge of dataframes sales_summary and store_stdev
sales_sum_final <- merge(sales_summary, store_stdev, by = "Group.1") # Group.1 is store number, x.x = sum of weekly sales per store, x.y = std dev of all weekly sales per store
View(sales_sum_final)

# Rename columns
sales_sum_final <- plyr::rename(sales_sum_final, c('Group.1' = 'id', 'x.x' = 'sales_sum', 'x.y' = 'std_dev'))

# Find store with the greatest standard deviation and coefficient of the mean to std dev
max_std_dev <- max(sales_sum_final$std_dev) # 317569.95
max_stdev_store <- sales_sum_final[sales_sum_final$std_dev == max_std_dev, 1] # Store 14
max_mean_coef <- max(sales_sum_final$store_mean_coeff) # 23.76
max_mc_store <- sales_sum_final[sales_sum_final$store_mean_coeff == max_mean_coef, 1] # Store 37

Identify which store(s) showed good quarterly growth rate in Q3’2012.

## Quarters of the year ##
# Q1: Jan, Feb, Mar      #
# Q2: Apr, May, Jun      #
# Q3: Jul, Aug, Sept     #
# Q4: Oct, Nov, Dec      #
##########################
q1 <- c('01', '02', '03')
q2 <- c('04','05','06')
q3 <- c('07','08','09')
q4 <- c('10', '11', '12')
# Create new column in walmart dataframe
quarters <- character() 

# Iterate through the date column
for (i in 1:length(walmart$Date)) {
  # Grab the date string
  this_date <- unlist(strsplit(walmart$Date[i], "-"))
  # Mark as Q1
  if(this_date[2] %in% q1) {
    quarters[i] <- 'Q1'
  }
  # Mark as Q2
  else if(this_date[2] %in% q2) {
    quarters[i] <- 'Q2'
  }
  # Mark as Q3
  else if(this_date[2] %in% q3) {
    quarters[i] <- 'Q3'
  }
  # Mark as Q4
  else {
    quarters[i] <- 'Q4'
  }
}

# Add new column to walmart data frame
walmart$Quarter <- quarters

# Get summary of fiscal quarters
quarter_counts <- summary(as.factor(walmart$Quarter))

# Subset data for Q2 and Q3
# Q2
q2_weeklies <- subset(walmart,(substr(Date, 7, 10) %in% c("2012")) & (Quarter == 'Q2'), select = c(Store,  Weekly_Sales))
# Q3
q3_weeklies <- subset(walmart,(substr(Date, 7, 10) %in% c("2012")) & (Quarter == 'Q3'), select = c(Store, Weekly_Sales))

# Combine split weekly data frames
q3_growth <- cbind(q2_weeklies, q3_weeklies$Weekly_Sales)
# Rename columns for clarity
q3_growth <- plyr::rename(q3_growth, c('Weekly_Sales' = 'q2_weekly_sales', 'q3_weeklies$Weekly_Sales' = 'q3_weekly_sales'))

# Create the growth comparison data frame
q3_growth_comparison <- data.frame(aggregate(cbind(q2_weekly_sales,q3_weekly_sales) ~ Store, data = q3_growth, FUN = sum))
# Comparison column
q3_growth_comparison$Q3_Growth_Rate <- ((q3_growth_comparison$q3_weekly_sales - q3_growth_comparison$q2_weekly_sales)/q3_growth_comparison$q2_weekly_sales)
# View
View(q3_growth_comparison)

# Which stores had a positive Q3'2012 growth rate
q3_pos_growth <- data.frame(subset(q3_growth_comparison, q3_growth_comparison$Q3_Growth_Rate > 0, select = c(Q3_Growth_Rate))) # 10 stores: 7, 16, 23, 24, 26, 35, 39, 40, 41, 44

# Line chart
q3_growth_chart <- ggplot(q3_growth_comparison, aes(x = factor(Store), y = Q3_Growth_Rate, group = 1)) +
  geom_line()+
  geom_point(aes(color = Q3_Growth_Rate > 0)) +
  scale_color_manual(name = 'Growth Rate', values = setNames(c('green4', 'red3'), c(T, F)), labels = c('Positive', 'Negative')) +
  labs(title = 'Q3 2012 Growth Rate by Store', x = 'Store Number', y = 'Growth Rate')

q3_growth_chart

Identify the holidays showing a higher sales than the mean sales of non-holiday stores in one view.

  • Super Bowl: 12-Feb-10, 11-Feb-11, 10-Feb-12, 8-Feb-13
  • Labor Day: 10-Sep-10, 9-Sep-11, 7-Sep-12, 6-Sep-13
  • Thanksgiving: 26-Nov-10, 25-Nov-11, 23-Nov-12, 29-Nov-13
  • Christmas: 31-Dec-10, 30-Dec-11, 28-Dec-12, 27-Dec-13
# Create a new dataframe with the a holiday column
holiday <- data.frame(walmart[,c('Store', 'Date', 'Weekly_Sales', 'Holiday_Flag')])
View(holiday)

# Add an empty column to holiday 
holiday[,'Holiday_Name'] <- NA

# Add a year column
holiday[, 'Year'] <- NA

# Add a month column
holiday[, 'Month'] <- NA

# Check against holiday flag + date column
# 1 = holiday, identify holiday based on month
# 0 = not a holiday
for (i in 1:length(holiday$Holiday_Flag)) {
  # When a day is a holiday
  if (holiday$Holiday_Flag[i] == 1) {
    # Retrieve the date from Date
    holi_date <- unlist(strsplit(holiday$Date[i], "-"))
    # Focus on the month to find the specific holiday
    holi_month <- holi_date[2]
    # Superbowl
    if (holi_month == '02') {
      holiday$Holiday_Name[i] <- paste('Superbowl', holi_date[3])
    }
    # Labor Day
    else if (holi_month == '09') {
      holiday$Holiday_Name[i] <- paste('Labor Day', holi_date[3])
    }
    # Thanksgiving
    else if (holi_month == '11') {
      holiday$Holiday_Name[i] <- paste('Thanksgiving', holi_date[3])
    }
    # Christmas
    else if (holi_month == '12') {
      holiday$Holiday_Name[i] <- paste('Christmas', holi_date[3])
    }
  }
  # When a day is not a holiday
  else {
    holiday$Holiday_Name[i] <- 'no holiday'
  }
}

# Get year and month
for (i in 1:length(holiday$Date)) {
  # Retrieve the date from Date
  holi_date <- unlist(strsplit(holiday$Date[i], "-"))
  # Get the year
  holiday$Year[i] <- holi_date[3]
  # Get the month
  holiday$Month[i] <- holi_date[2]
}

##### ACROSS ALL YEARS, NON-HOLIDAY VS. HOLIDAY #####

# Create new dataframes for comparison
# No holidays
no_holidays <- data.frame(subset(holiday, Holiday_Name == 'no holiday', select = -c(Holiday_Flag)))
View(no_holidays)
# Aggregated averages
no_hol_avgs <- data.frame(aggregate(no_holidays$Weekly_Sales, list(no_holidays$Store), FUN = mean))
View(no_hol_avgs)
# Rename columns
no_hol_avgs <- plyr::rename(no_hol_avgs, c('Group.1' = 'Store', 'x' = 'Weekly_Avg_NonHoliday'))

## HOLIDAY DATA FRAMES ##
# Superbowl dataframes
superbowl_2010 <- data.frame(subset(holiday, Holiday_Name == 'Superbowl 2010', select = -c(Holiday_Flag)))
View(superbowl_2010)

superbowl_2011 <- data.frame(subset(holiday, Holiday_Name == 'Superbowl 2011', select = -c(Holiday_Flag)))
View(superbowl_2011)

superbowl_2012 <- data.frame(subset(holiday, Holiday_Name == 'Superbowl 2012', select = -c(Holiday_Flag)))
View(superbowl_2012)

# Labor Day dataframes
laborday_2010 <- data.frame(subset(holiday, Holiday_Name == 'Labor Day 2010', select = -c(Holiday_Flag)))
View(laborday_2010)

laborday_2011 <- data.frame(subset(holiday, Holiday_Name == 'Labor Day 2011', select = -c(Holiday_Flag)))
View(laborday_2011)

laborday_2012 <- data.frame(subset(holiday, Holiday_Name == 'Labor Day 2012', select = -c(Holiday_Flag)))
View(laborday_2012)

# Thanksgiving dataframes
thanksgiving_2010 <- data.frame(subset(holiday, Holiday_Name == 'Thanksgiving 2010', select = -c(Holiday_Flag)))
View(thanksgiving_2010)

thanksgiving_2011 <- data.frame(subset(holiday, Holiday_Name == 'Thanksgiving 2011', select = -c(Holiday_Flag)))
View(thanksgiving_2011)

# Christmas dataframes
christmas_2010 <- data.frame(subset(holiday, Holiday_Name == 'Christmas 2010', select = -c(Holiday_Flag)))
View(christmas_2010)

christmas_2011 <- data.frame(subset(holiday, Holiday_Name == 'Christmas 2011', select = -c(Holiday_Flag)))
View(christmas_2011)

# Merge yearly holiday columns to no_hol_avgs dataframe
# Superbowl
no_hol_avgs$Superbowl_2010 <- superbowl_2010$Weekly_Sales
no_hol_avgs$Superbowl_2011 <- superbowl_2011$Weekly_Sales
no_hol_avgs$Superbowl_2012 <- superbowl_2012$Weekly_Sales

# Labor Day
no_hol_avgs$LaborDay_2010 <- laborday_2010$Weekly_Sales
no_hol_avgs$LaborDay_2011 <- laborday_2011$Weekly_Sales
no_hol_avgs$LaborDay_2012 <- laborday_2012$Weekly_Sales

# Thanksgiving
no_hol_avgs$Thanksgiving_2010 <- thanksgiving_2010$Weekly_Sales
no_hol_avgs$Thanksgiving_2011 <- thanksgiving_2011$Weekly_Sales

# Christmas
no_hol_avgs$Christmas_2010 <- christmas_2010$Weekly_Sales
no_hol_avgs$Christmas_2011 <- christmas_2011$Weekly_Sales

## COMPARISONS ## 
# Superbowl 
no_hol_avgs$superbowl_2010_comp <- ifelse(no_hol_avgs$Superbowl_2010 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")
no_hol_avgs$superbow_2011_comp <- ifelse(no_hol_avgs$Superbowl_2011 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")
no_hol_avgs$superbowl_2012_comp <- ifelse(no_hol_avgs$Superbowl_2012 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")

# Labor Day 
no_hol_avgs$laborday_2010_comp <- ifelse(no_hol_avgs$LaborDay_2010 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")
no_hol_avgs$laborday_2011_comp <- ifelse(no_hol_avgs$LaborDay_2011 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")
no_hol_avgs$laborday_2012_comp <- ifelse(no_hol_avgs$LaborDay_2012 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")

# Thanksgiving 
no_hol_avgs$thanksgiving_2010_comp <- ifelse(no_hol_avgs$Thanksgiving_2010 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")
no_hol_avgs$thanksgiving_2011_comp <- ifelse(no_hol_avgs$Thanksgiving_2011 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")

# Christmas 
no_hol_avgs$christmas_2010_comp <- ifelse(no_hol_avgs$Christmas_2010 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")
no_hol_avgs$christmas_2011_comp <- ifelse(no_hol_avgs$Christmas_2011 > no_hol_avgs$Weekly_Avg_NonHoliday, "greater", "not greater")


##### WITHIN YEAR 2010: NON-HOLIDAY VS. HOLIDAY #####
nh_2010 <- data.frame(subset(no_hol_avgs, select = -c(2:22)))

# February 2010
nh_Feb2010 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2010' & Month == '02') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# September 2010
nh_Sept2010 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2010' & Month == '09') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# November 2010
nh_Nov2010 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2010' & Month == '11') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))


# December 2010
nh_Dec2010 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2010' & Month == '12') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# Bind columns
nh_2010 <- cbind(nh_2010, superbowl_2010$Weekly_Sales, nh_Feb2010$Mean, laborday_2010$Weekly_Sales, nh_Sept2010$Mean, thanksgiving_2010$Weekly_Sales, nh_Nov2010$Mean, christmas_2010$Weekly_Sales, nh_Dec2010$Mean)

# Add comparison columns
nh_2010$superbowl_comparison <- ifelse(superbowl_2010$Weekly_Sales > nh_Feb2010$Mean, "greater", "not greater") # Superbowl
nh_2010$laborday_comparison <- ifelse(laborday_2010$Weekly_Sales > nh_Sept2010$Mean, "greater", "not greater") # Labor Day
nh_2010$thanksgiving_comparison <- ifelse(thanksgiving_2010$Weekly_Sales > nh_Nov2010$Mean, "greater", "not greater") # Thanksgiving
nh_2010$christmas_comparison <- ifelse(christmas_2010$Weekly_Sales > nh_Dec2010$Mean, "greater", "not greater") # Christmas

# View dataframe
View(nh_2010)

##### WITHIN YEAR 2011: NON-HOLIDAY VS. HOLIDAY #####
nh_2011 <- data.frame(subset(no_hol_avgs, select = -c(2:22)))

# February 2011
nh_Feb2011 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2011' & Month == '02') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# September 2011
nh_Sept2011 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2011' & Month == '09') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# November 2011
nh_Nov2011 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2011' & Month == '11') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))


# December 2011
nh_Dec2011 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2011' & Month == '12') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# Bind columns
nh_2011 <- cbind(nh_2011, superbowl_2011$Weekly_Sales, nh_Feb2011$Mean, laborday_2011$Weekly_Sales, nh_Sept2011$Mean, thanksgiving_2011$Weekly_Sales, nh_Nov2011$Mean, christmas_2011$Weekly_Sales, nh_Dec2011$Mean)

# Add comparison columns
nh_2011$superbowl_comparison <- ifelse(superbowl_2011$Weekly_Sales > nh_Feb2011$Mean, "greater", "not greater") # Superbowl
nh_2011$laborday_comparison <- ifelse(laborday_2011$Weekly_Sales > nh_Sept2011$Mean, "greater", "not greater") # Labor Day
nh_2011$thanksgiving_comparison <- ifelse(thanksgiving_2011$Weekly_Sales > nh_Nov2011$Mean, "greater", "not greater") # Thanksgiving
nh_2011$christmas_comparison <- ifelse(christmas_2011$Weekly_Sales > nh_Dec2011$Mean, "greater", "not greater") # Christmas

# View dataframe
View(nh_2011)

##### WITHIN YEAR 2012: NON-HOLIDAY VS. HOLIDAY #####
nh_2012 <- data.frame(subset(no_hol_avgs, select = -c(2:22)))

# February 2011
nh_Feb2012 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2012' & Month == '02') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# September 2012
nh_Sept2012 <- holiday %>% 
                    filter(Holiday_Flag == 0 & Year == '2012' & Month == '09') %>%
                    group_by(Store) %>%
                    summarise(Mean = mean(Weekly_Sales))

# Bind columns
nh_2012 <- cbind(nh_2012, superbowl_2012$Weekly_Sales, nh_Feb2012$Mean, laborday_2012$Weekly_Sales, nh_Sept2012$Mean)

# Add comparison columns
nh_2012$superbowl_comparison <- ifelse(superbowl_2012$Weekly_Sales > nh_Feb2012$Mean, "greater", "not greater") # Superbowl
nh_2012$laborday_comparison <- ifelse(laborday_2012$Weekly_Sales > nh_Sept2012$Mean, "greater", "not greater") # Labor Day

# View dataframe
View(nh_2012)

Visualizations of Comparisons: Superbowl

## 2010 Superbowl Comparison

# Convert data frame into long format
nh_2010sb.long <- nh_2010 %>%
  select('Store','superbowl_2010$Weekly_Sales', 'nh_Feb2010$Mean') %>%
  pivot_longer(-Store, names_to = 'sbvars2010', values_to = 'sbvals2010')

# Create the visualization
sb2010_grids <- ggplot(nh_2010sb.long, aes(x = factor(Store), y= sbvals2010, color = sbvars2010, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Superbowl 2010', 'Regular Monthly Average.')) +
  labs(title = 'February Average Sales vs. Superbowl: 2010', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

sb2010_grids
Feb. Average Sales vs. Superbowl 2010
# Create the gt table object
feb_avgs <- gt(tibble(nh_2010 %>% filter(nh_2010$`nh_Feb2010$Mean` < nh_2010$`superbowl_2010$Weekly_Sales`)))



# Format
feb_avgs <- feb_avgs %>% tab_header(title = 'Stores with Higher Superbowl Sales vs February Non-Holiday Average Sale',
                                    subtitle = '2010') %>%
                         cols_hide(columns = c(4:13)) 

# Display
feb_avgs
## 2011 Superbowl Comparison

# Convert data frame into long format
nh_2011sb.long <- nh_2011 %>%
  select('Store','superbowl_2011$Weekly_Sales', 'nh_Feb2011$Mean') %>%
  pivot_longer(-Store, names_to = 'sbvars2011', values_to = 'sbvals2011')

# Create the visualization
sb2011_grids <- ggplot(nh_2011sb.long, aes(x = factor(Store), y= sbvals2011, color = sbvars2011, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Superbowl 2011', 'Regular Monthly Average.')) +
  labs(title = 'February Average Sales vs. Superbowl: 2011', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

sb2011_grids
Feb. Average Sales vs. Superbowl 2011
# Create the gt table object
feb2011_avgs <- gt(tibble(nh_2011 %>% filter(nh_2011$`nh_Feb2011$Mean` < nh_2011$`superbowl_2011$Weekly_Sales`)))



# Format
feb2011_avgs <- feb2011_avgs %>% tab_header(title = 'Stores with Higher Superbowl Sales vs February Non-Holiday Average Sale',
                                    subtitle = '2011') %>%
                         cols_hide(columns = c(4:13)) 

# Display
feb2011_avgs
## 2012 Superbowl Comparison

# Convert data frame into long format
nh_2012sb.long <- nh_2012 %>%
  select('Store','superbowl_2012$Weekly_Sales', 'nh_Feb2012$Mean') %>%
  pivot_longer(-Store, names_to = 'sbvars2012', values_to = 'sbvals2012')

# Create the visualization
sb2012_grids <- ggplot(nh_2012sb.long, aes(x = factor(Store), y= sbvals2012, color = sbvars2012, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Superbowl 2012', 'Regular Monthly Average.')) +
  labs(title = 'February Average Sales vs. Superbowl: 2012', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

sb2012_grids
Feb. Average Sales vs. Superbowl 2012
# Create the gt table object
feb2012_avgs <- gt(tibble(nh_2012 %>% filter(nh_2012$`nh_Feb2012$Mean` < nh_2012$`superbowl_2012$Weekly_Sales`)))



# Format
feb2012_avgs <- feb2012_avgs %>% tab_header(title = 'Stores with Higher Superbowl Sales vs February Non-Holiday Average Sale',
                                    subtitle = '2012') %>%
                         cols_hide(columns = c(4:7)) 

# Display
feb2012_avgs

All non-holiday seasons vs. all superbowl years

# Convert dataframe into long format
no_hol_avgs.long <- no_hol_avgs %>%
  select('Store','Weekly_Avg_NonHoliday', 'Superbowl_2010', 'Superbowl_2011', 'Superbowl_2012') %>%
  pivot_longer(-Store, names_to = 'sales_vars', values_to = 'sales_vals')

# Create the visualization
sb_grids <- ggplot(no_hol_avgs.long, aes(x = factor(Store), y= sales_vals, color = sales_vars, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Superbowl 2010', 'Superbowl 2011', 'Superbowl 2012', 'Non-Holiday Weekly Avg.')) +
  labs(title = 'Weekly Average Sales vs. Superbowl: 2010 thru 2012', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

sb_grids

Visualization of Comparisons: Labor Day

## 2010 laborday Comparison

# Convert data frame into long format
nh_2010ld.long <- nh_2010 %>%
  select('Store','laborday_2010$Weekly_Sales', 'nh_Sept2010$Mean') %>%
  pivot_longer(-Store, names_to = 'ldvars2010', values_to = 'ldvals2010')

# Create the visualization
ld2010_grids <- ggplot(nh_2010ld.long, aes(x = factor(Store), y= ldvals2010, color = ldvars2010, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Labor Day 2010', 'Regular Monthly Average.')) +
  labs(title = 'September Average Sales vs. Labor Day: 2010', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

ld2010_grids
Sept. Average Sales vs. Labor Day 2010
# Create the gt table object
sept_avgs <- gt(tibble(nh_2010 %>% filter(nh_2010$`nh_Sept2010$Mean` < nh_2010$`laborday_2010$Weekly_Sales`)))



# Format
sept_avgs <- sept_avgs %>% tab_header(title = 'Stores with Higher Labor Day Sales vs Sept Non-Holiday Average Sale',
                                    subtitle = '2010') %>%
                         cols_hide(columns = c(2:3,6:13)) 

# Display
sept_avgs
## 2011 laborday Comparison

# Convert data frame into long format
nh_2011ld.long <- nh_2011 %>%
  select('Store','laborday_2011$Weekly_Sales', 'nh_Sept2011$Mean') %>%
  pivot_longer(-Store, names_to = 'ldvars2011', values_to = 'ldvals2011')

# Create the visualization
ld2011_grids <- ggplot(nh_2011ld.long, aes(x = factor(Store), y= ldvals2011, color = ldvars2011, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Labor Day 2011', 'Regular Monthly Average.')) +
  labs(title = 'September Average Sales vs. Labor Day: 2011', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

ld2011_grids
Sept. Average Sales vs. Labor Day 2011
# Create the gt table object
sept2011_avgs <- gt(tibble(nh_2011 %>% filter(nh_2011$`nh_Sept2011$Mean` < nh_2011$`laborday_2011$Weekly_Sales`)))



# Format
sept2011_avgs <- sept2011_avgs %>% tab_header(title = 'Stores with Higher Labor Day Sales vs Sept Non-Holiday Average Sale',
                                    subtitle = '2011') %>%
                         cols_hide(columns = c(2:3,6:13)) 

# Display
sept2011_avgs
## 2012 laborday Comparison

# Convert data frame into long format
nh_2012ld.long <- nh_2012 %>%
  select('Store','laborday_2012$Weekly_Sales', 'nh_Sept2012$Mean') %>%
  pivot_longer(-Store, names_to = 'ldvars2012', values_to = 'ldvals2012')

# Create the visualization
ld2012_grids <- ggplot(nh_2012ld.long, aes(x = factor(Store), y= ldvals2012, color = ldvars2012, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Labor Day 2012', 'Regular Monthly Average.')) +
  labs(title = 'September Average Sales vs. Labor Day: 2012', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

ld2012_grids
Sept. Average Sales vs. Labor Day 2012
# Create the gt table object
sept2012_avgs <- gt(tibble(nh_2012 %>% filter(nh_2012$`nh_Sept2012$Mean` < nh_2012$`laborday_2012$Weekly_Sales`)))



# Format
sept2012_avgs <- sept2012_avgs %>% tab_header(title = 'Stores with Higher Labor Day Sales vs Sept Non-Holiday Average Sale',
                                    subtitle = '2012') %>%
                         cols_hide(columns = c(2:3,6:7)) 

# Display
sept2012_avgs

All non-holiday vs. all Labor Day years

# Convert dataframe into long format
no_hol_avgs1.long <- no_hol_avgs %>%
  select('Store','Weekly_Avg_NonHoliday', 'LaborDay_2010', 'LaborDay_2011', 'LaborDay_2012') %>%
  pivot_longer(-Store, names_to = 'labor_vars', values_to = 'labor_vals')

# Create the visualization
labor_grids <- ggplot(no_hol_avgs1.long, aes(x = factor(Store), y= labor_vals, color = labor_vars, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Labor Day 2010', 'Labor Day 2011', 'Labor Day 2012', 'Non-Holiday Weekly Avg.')) +
  labs(title = 'Weekly Average Sales vs. Labor Day: 2010 thru 2012', x = 'Store', y = 'Sales Amount', color = 'Sales Event')
  
labor_grids

Visualization of Comparisons: Thanksgiving

## 2010 Thanksgiving Comparison

# Convert data frame into long format
nh_2010t.long <- nh_2010 %>%
  select('Store','thanksgiving_2010$Weekly_Sales', 'nh_Nov2010$Mean') %>%
  pivot_longer(-Store, names_to = 'tvars2010', values_to = 'tvals2010')

# Create the visualization
t2010_grids <- ggplot(nh_2010t.long, aes(x = factor(Store), y= tvals2010, color = tvars2010, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Thanksgiving 2010', 'Regular Monthly Average.')) +
  labs(title = 'November Average Sales vs. Thanksgiving: 2010', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

t2010_grids
Nov. Average Sales vs. Thanksgiving 2010
# Create the gt table object
nov_avgs <- gt(tibble(nh_2010 %>% filter(nh_2010$`nh_Nov2010$Mean` < nh_2010$`thanksgiving_2010$Weekly_Sales`)))



# Format
nov_avgs <- nov_avgs %>% tab_header(title = 'Stores with Higher Thanksgiving Sales vs Nov Non-Holiday Average Sale',
                                    subtitle = '2010') %>%
                         cols_hide(columns = c(2:5,8:13)) 

# Display
nov_avgs
## 2011 Thanksgiving Comparison

# Convert data frame into long format
nh_2011t.long <- nh_2011 %>%
  select('Store','thanksgiving_2011$Weekly_Sales', 'nh_Nov2011$Mean') %>%
  pivot_longer(-Store, names_to = 'tvars2011', values_to = 'tvals2011')

# Create the visualization
t2011_grids <- ggplot(nh_2011t.long, aes(x = factor(Store), y= tvals2011, color = tvars2011, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Thanksgiving 2011', 'Regular Monthly Average.')) +
  labs(title = 'November Average Sales vs. Thanksgiving: 2011', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

t2011_grids
Nov. Average Sales vs. Thanksgiving 2011
# Create the gt table object
nov2011_avgs <- gt(tibble(nh_2011 %>% filter(nh_2011$`nh_Nov2011$Mean` < nh_2011$`thanksgiving_2011$Weekly_Sales`)))



# Format
nov2011_avgs <- nov2011_avgs %>% tab_header(title = 'Stores with Higher Thanksgiving Sales vs Nov Non-Holiday Average Sale',
                                    subtitle = '2011') %>%
                         cols_hide(columns = c(2:5,8:13)) 

# Display
nov2011_avgs

All non-holiday vs. all Thanksgiving years

# Convert dataframe into long format
no_hol_avgs2.long <- no_hol_avgs %>%
  select('Store','Weekly_Avg_NonHoliday', 'Thanksgiving_2010', 'Thanksgiving_2011') %>%
  pivot_longer(-Store, names_to = 'turkey_vars', values_to = 'turkey_vals')

# Create the visualization
turkey_grids <- ggplot(no_hol_avgs2.long, aes(x = factor(Store), y= turkey_vals, color = turkey_vars, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Thanksgiving 2010', 'Thanksgiving 2011', 'Non-Holiday Weekly Avg.')) +
  labs(title = 'Weekly Average Sales vs. Thanksgiving: 2010 thru 2011', x = 'Store', y = 'Sales Amount', color = 'Sales Event')
  
turkey_grids

Visualization of Comparisons: Christmas

## 2010 Christmas Comparison

# Convert data frame into long format
nh_2010c.long <- nh_2010 %>%
  select('Store','christmas_2010$Weekly_Sales', 'nh_Dec2010$Mean') %>%
  pivot_longer(-Store, names_to = 'cvars2010', values_to = 'cvals2010')

# Create the visualization
c2010_grids <- ggplot(nh_2010c.long, aes(x = factor(Store), y= cvals2010, color = cvars2010, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Christmas 2010', 'Regular Monthly Average.')) +
  labs(title = 'December Average Sales vs. Christmas: 2010', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

c2010_grids
Dec. Average Sales vs. Christmas 2010
# Create the gt table object
dec_avgs <- gt(tibble(nh_2010 %>% filter(nh_2010$`nh_Dec2010$Mean` < nh_2010$`christmas_2010$Weekly_Sales`)))



# Format
dec_avgs <- dec_avgs %>% tab_header(title = 'Stores with Higher Christmas Sales vs Nov Non-Holiday Average Sale',
                                    subtitle = '2010') %>%
                         cols_hide(columns = c(2:7,10:13)) 

# Display
dec_avgs
## 2011 Christmas Comparison

# Convert data frame into long format
nh_2011c.long <- nh_2011 %>%
  select('Store','christmas_2011$Weekly_Sales', 'nh_Dec2011$Mean') %>%
  pivot_longer(-Store, names_to = 'cvars2011', values_to = 'cvals2011')

# Create the visualization
c2011_grids <- ggplot(nh_2011c.long, aes(x = factor(Store), y= cvals2011, color = cvars2011, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Christmas 2011', 'Regular Monthly Average.')) +
  labs(title = 'December Average Sales vs. Christmas: 2011', x = 'Store', y = 'Sales Amount', color = 'Sales Event')

c2011_grids
Dec. Average Sales vs. Christmas 2011
# Create the gt table object
dec2011_avgs <- gt(tibble(nh_2011 %>% filter(nh_2011$`nh_Dec2011$Mean` < nh_2011$`christmas_2011$Weekly_Sales`)))



# Format
dec2011_avgs <- dec2011_avgs %>% tab_header(title = 'Stores with Higher Christmas Sales vs Nov Non-Holiday Average Sale',
                                    subtitle = '2011') %>%
                         cols_hide(columns = c(2:7,10:13)) 

# Display
dec2011_avgs

All non-holiday vs. all Christmas years

# Convert dataframe into long format
no_hol_avgs3.long <- no_hol_avgs %>%
  select('Store','Weekly_Avg_NonHoliday', 'Christmas_2010', 'Christmas_2011') %>%
  pivot_longer(-Store, names_to = 'xmas_vars', values_to = 'xmas_vals')

# Create the visualization
xmas_grids <- ggplot(no_hol_avgs3.long, aes(x = factor(Store), y= xmas_vals, color = xmas_vars, group = 1)) +
  geom_line() +
  geom_point() +
  scale_color_viridis_d(labels = c('Christmas 2010', 'Christmas 2011', 'Non-Holiday Weekly Avg.')) +
  labs(title = 'Weekly Average Sales vs. Christmas: 2010 thru 2011', x = 'Store', y = 'Sales Amount', color = 'Sales Event')
  
xmas_grids

Provide monthly and semester view of sales in units along with insights.

# Set up a monthly view
monthly <- data.frame(walmart[, c('Store', 'Date', 'Weekly_Sales', 'Holiday_Flag')])
View(monthly)
# Create a month column
monthly[,'Month'] <- NA
# Create a year column
monthly[,'Year'] <- NA
# Create a semester column
monthly[,'Semester'] <- NA
sem1 <- c("01", "02", "03", "04", "05", "06") # Semester 1
sem2 <- c("07", "08", "09", "10", "11", "12") # Semester 2

for (i in 1:length(monthly$Store)) {
  # Retrieve the date from Date
  monthly_date <- unlist(strsplit(monthly$Date[i], "-"))
  # Focus on the month
  monthly$Month[i] <- switch(monthly_date[2],
                             "01" = "Jan",
                             "02" = "Feb",
                             "03" = "Mar",
                             "04" = "Apr",
                             "05" = "May",
                             "06" = "Jun",
                             "07" = "Jul",
                             "08" = "Aug",
                             "09" = "Sept",
                             "10" = "Oct",
                             "11" = "Nov",
                             "12" = "Dec")
  # Set the year
  monthly$Year[i] <- monthly_date[3]
  # Set the semester
  if(monthly_date[2] %in% sem1) {
    monthly$Semester[i] <- 'Semester 1'
  }
  else if(monthly_date[2] %in% sem2) {
    monthly$Semester[i] <- 'Semester 2'
  }
  else {
    monthly$Semester[i] <- 'Invalid month'
  }
}

# Aggregate by month and year
monthly_sums <- data.frame(aggregate(Weekly_Sales ~ Store + Year + Month, data = monthly, FUN = sum))
View(monthly_sums)
 
# Aggregate by semester
semester_sums <- data.frame(aggregate(Weekly_Sales ~ Store + Year + Semester, data = monthly, FUN = sum))
View(semester_sums)

2010 Monthly Plot: Proportional View

# Monthly plots
monthly2010_bars <- ggplot(subset(monthly_sums, Year %in% 2010), aes(x = factor(Store), y = Weekly_Sales, fill = factor(Month, levels= c('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec')))) +
  geom_col(position = "fill") +
  scale_fill_viridis_d(limits = c('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec')) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 0.5)) +
  labs(title = '2010 Monthly Sales per Store: Proportional View', x = 'Store Number', y = 'Monthly Sales', fill = 'Month')
  
# Show the plot
monthly2010_bars

2011 Monthly Plot: Proportional View

# Monthly plots
monthly2011_bars <- ggplot(subset(monthly_sums, Year %in% 2011), aes(x = factor(Store), y = Weekly_Sales, fill = factor(Month, levels= c('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec')))) +
  geom_col(position = "fill") +
  scale_fill_viridis_d(limits = c('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec')) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 0.5)) +
  labs(title = '2011 Monthly Sales per Store: Proportional View', x = 'Store Number', y = 'Monthly Sales', fill = 'Month')
  
# Show the plot
monthly2011_bars

2012 Monthly Plot: Proportional View

# Monthly plots
monthly2012_bars <- ggplot(subset(monthly_sums, Year %in% 2012), aes(x = factor(Store), y = Weekly_Sales, fill = factor(Month, levels= c('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec')))) +
  geom_col(position = "fill") +
  scale_fill_viridis_d(limits = c('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sept', 'Oct', 'Nov', 'Dec')) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 0.5)) +
  labs(title = '2012 Monthly Sales per Store: Proportional View', x = 'Store Number', y = 'Monthly Sales', fill = 'Month')
  
# Show the plot
monthly2012_bars

Semester Plot: Proportional View

# Semester plot
sem_bars <- ggplot(semester_sums, aes(x = factor(Store), y = Weekly_Sales, fill = factor(Semester))) +
  geom_col(position = "fill") +
  scale_fill_viridis_d() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1, vjust = 0.5)) +
  labs(title = 'Semester Sales per Store: Proportional View', x = 'Store Number', y = 'Semester Sales', fill = 'Semester')
  
# Show the plot
sem_bars

Statistical Model

For Store 1, build prediction models to forecast demand - Linear regression: - Restructure dates beginning at 1 for the earliest date in the order. - Change dates into days by creating a new variable. - Hypothesize if CPI, unemployment, and fuel price have any impact on sales.

# View data points by store
walmart_stores <- summary(as.factor(walmart$Store)) # Verified: 143 data points per store

# Create a new dataframe for the regression analysis
walmart_regression <- data.frame(subset(walmart, Store %in% 1))
View(walmart_regression)

# Date conversion
walmart_regression$day <- c(1:length(walmart_regression$Store))

# Drop unnecessary columns
walmart_regression <- subset(walmart_regression, select = -c(Store, Date, Holiday_Flag, Quarter))

Pair Plots: Temperature, Fuel Price, CPI, Unemployment vs. Weekly Sales

# Scatter plot
par(mfrow = c(3,2)) # Formats the display

for (i in 2:6) {
  plot(walmart_regression[,i],
       walmart_regression$Weekly_Sales,
       main = names(walmart_regression[i]),
       ylab = names(walmart_regression$Weekly_Sales),
       xlab = '', col = '#440154')
}

par(mfrow = c(1,1))

Correlations between Weekly Sales and Independent Variables

The heatmap below shows weak correlations between the Weekly Sales dependent variables and the independent variables of Temperature, Fuel_Price, CPI, and Unemployment. - Temperature and Unemployment have an inverse relationship with Weekly Sales. - Fuel_Price and CPI have a direct (positive) relationship with Weekly Sales.

Of concern in this model are the high correlations between Fuel_Price and CPI, Fuel_Price and Unemployment, and CPI and Unemployment. However, they do not appear to be statistically significant.

walmart_mcc <- cor(walmart_regression[, 1:6]) # Contains the correlation matrix

View(walmart_mcc)

corrplot(walmart_mcc, method = 'color', outline = T, cl.pos = 'n', rect.col = 'black', tl.col = 'indianred4', addCoef.col = "black", number.digits = 2, number.cex = 0.60, tl.cex = 0.7, cl.cex = 1, col = colorRampPalette(c("green4","white","red"))(100))

Hypotheses

  • As temperature increases, weekly sales should decrease.
  • CPI should have no relationship to weekly sales.
  • Unemployment rates should have an inverse relationship with weekly sales.
  • Fuel prices should not show a relationship to weekly sales.

# Split data into train and test with and 80%-20% split
set.seed(1234) # Set the seed
sample_mvr1 <- sample.split(walmart_regression, SplitRatio = 0.80) #Set the split proportions

train <- subset(walmart_regression, sample_mvr1 == T) # Training data
test <- subset(walmart_regression, sample_mvr1 == F) # Test data

Base Model: Multivariate Regression with Day

Uses day and assumes all predictor variables are continuous as well as the dependent variable. This analysis will use an alpha level of 0.05.

# Create the base model regression
model_base <- lm(formula = Weekly_Sales ~., data = train)
# View the base model summary
summary(model_base)

From the summary above, none of the included variables reach significance at the 0.05 level. However, the F-statistic being significant at the 0.05 alpha level indicates that all variables together yield a model that fits the data.

Visualizing Training Predictions

# Prediction
base_train_pred <- predict(model_base, newdata = train)
# Number of predictions
length(base_train_pred)
base_train_pred

# Visualization
base_training_chart <- ggplot() +
  geom_point(aes(x = train$Weekly_Sales, y = base_train_pred)) +
  labs(title = 'Weekly Sales [Base Model]: Training Set vs Predictions', x = 'Actual Weekly Sales', y = 'Predicted Weekly Sales')

base_training_chart

Test the Base Model

# Set up the model for the test data set
base_test_pred <- predict(model_base, newdata = test)
base_test_pred

# Visualization
base_test_chart <- ggplot() +
  geom_point(aes(x = test$Weekly_Sales, y = base_test_pred)) +
  labs(title = 'Weekly Sales [Base Model]: Test Set vs Predictions', x = 'Actual Weekly Sales', y = 'Predicted Weekly Sales')

base_test_chart

Validate the Base Model Accuracy

# Mean Absolute Percentage Error
mape_base <- MAPE(base_test_pred, test$Weekly_Sales)

# Root Mean Square Error
rmse_base <- RMSE(base_test_pred, test$Weekly_Sales)

print(paste('The base mean absolute percentage error is ', mape_base))
print(paste('The base root mean square error is ', rmse_base))

The mean absolute percentage error value is 5.9%. The MAPE standard for evaluating a MAPE value depends on the industry, so without knowing what the MAPE is for retail, it is hard to say if this model alone produces acceptable accuracy. However, it can serve as the baseline comparison against which other models will be judged.

The root mean square error serves as a way to evaluate the fit of a model to the data. It is best in a comparison role against other models, so the value of the base model here will serve as a baseline for other models.

Due to the nature of the Day variable, it will be removed from the next analysis to see if the model improves any.

Model 1: Multivariate Regression without Day

Assuming all predictor variables are continuous as well as the dependent variable. For this analysis we will adopt an alpha level of 0.05.

# Remove day from train and test
train <- subset(train, select = -c(day))
test <- subset(test, select = -c(day))

# Set the linear regression formula
model_mvlin1 <- lm(formula = Weekly_Sales ~ Temperature + Fuel_Price + CPI + Unemployment, data = train)
# View the model summary
summary(model_mvlin1)

From the summary above, it looks as if only CPI is a significant predictor of Weekly_Sales. However, the F-statistic is significant at the selected alpha level for the analysis, so the overall model can be accepted as a fit for the data.

Visualize Training Set Results

# Prediction
weekly_sales_train_pred <- predict(model_mvlin1, newdata = train)
# Number of predictions
length(weekly_sales_train_pred)
weekly_sales_train_pred

# Visualization
training_chart <- ggplot() +
  geom_point(aes(x = train$Weekly_Sales, y = weekly_sales_train_pred)) +
  labs(title = 'Weekly Sales: Training Set vs Predictions', x = 'Actual Weekly Sales', y = 'Predicted Weekly Sales')

training_chart

Test the Model

# Set up the model for the test data set
weekly_sales_test_pred <- predict(model_mvlin1, newdata = test)
weekly_sales_test_pred

# Visualization
test_chart <- ggplot() +
  geom_point(aes(x = test$Weekly_Sales, y = weekly_sales_test_pred)) +
  labs(title = 'Weekly Sales: Test Set vs Predictions', x = 'Actual Weekly Sales', y = 'Predicted Weekly Sales')

test_chart

Validate the Model Accuracy

# Mean Absolute Percentage Error
mape1 <- MAPE(weekly_sales_test_pred, test$Weekly_Sales)

# Root Mean Square Error
rmse1 <- RMSE(weekly_sales_test_pred, test$Weekly_Sales)

print(paste('The mean absolute percentage error for model 1 is ', mape1))
print(paste('The root mean square error for model 1 is ', rmse1))

The mean absolute percentage error for this model is almost identical to the first model. Same with the root mean square error value. Therefore, despite the one statistically significant predictor variable, this model cannot be said to be better than the base model.

Final Model: CPI and Temperature as Predictors of Weekly Sales

This final model will treat CPI as a continuous variable, but will remove Fuel_Price and Unemployment from the prediction model due to their results in the correlation matrix created at the start of the analysis.

# Set the linear regression formula
final_model <- lm(formula = Weekly_Sales ~ Temperature + CPI, data = train)
# View the model summary
summary(final_model)

This particular model shows both predictor variables as statistically significant. Furthermore, the model itself is statistically significant, indicating it is a good fit for the data.

Visualize Training Set Results

# Prediction
final_pred <- predict(final_model, newdata = train)
# Number of predictions
length(final_pred)
final_pred

# Visualization
final_chart1 <- ggplot() +
  geom_point(aes(x = train$Weekly_Sales, y = final_pred)) +
  labs(title = 'Weekly Sales[Final Model]: Training Set vs Predictions', x = 'Actual Weekly Sales', y = 'Predicted Weekly Sales')

final_chart1

Test the Model

# Set up the model for the test data set
final_test_pred <- predict(final_model, newdata = test)
final_test_pred

# Visualization
final_test_chart <- ggplot() +
  geom_point(aes(x = test$Weekly_Sales, y = final_test_pred)) +
  labs(title = 'Weekly Sales [Final Model]: Test Set vs Predictions', x = 'Actual Weekly Sales', y = 'Predicted Weekly Sales')

final_test_chart

Validate the Model Accuracy

# Mean Absolute Percentage Error
final_mape <- MAPE(final_test_pred, test$Weekly_Sales)

# Root Mean Square Error
final_rmse <- RMSE(final_test_pred, test$Weekly_Sales)

print(paste('The mean absolute percentage error for the final model is ', final_mape))
print(paste('The root mean square error for the final model is ', final_rmse))

This final model offers a mean absolute percentage error of 5.6%, versus the 5.9% of the base model. Therefore, this model can be judged as more accurate. The root mean square error is also less for this model than the base model, so it is a better fit than the base model.

Final Prediction Model

\(y_{pred} = -2500.2*temp + 9969.3*cpi - 431311.4\)

LS0tCnRpdGxlOiAiUmV0YWlsIEFuYWx5c2lzIHdpdGggV2FsbWFydCIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICBodG1sX2RvY3VtZW50OiBkZWZhdWx0CiAgcGRmX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQpOb3RlYm9vayBjcmVhdGVkIGJ5IExlc2xpZSBBLiBNY0ZhcmxpbiwgMjYgRmViIDIwMjIKCmBgYHtyfQojIExpYnJhcmllcyAtIGluc3RhbGxhdGlvbnMgYW5kIGxvYWRpbmcgZG9uZSB2aWEgUGFja2dlcyBwYW5lLgojIFVuY29tbWVudCB0aGUgbGluZSBiZWxvdyB0byBsb2FkIHRoZSBsaWJyYXJpZXMKaW5zdGFsbC5wYWNrYWdlcygnY2FUb29scycsIHJlcG9zID0gImh0dHBzOi8vY3Jhbi5yc3R1ZGlvLm9yZyIpCmxpYnJhcnkocmVhZHhsLCBwbHlyLCBkcGx5ciwgZ2dwbG90MiwgdGliYmxlLCB0aWR5dmVyc2UsIGd0LCBjb3JycGxvdCwgY2FUb29scywgTUxtZXRyaWNzLCBrbml0cikKCiMgSW1wb3J0IGRhdGEgc2V0LCBjcmVhdGUgZGF0YSBmcmFtZSwgdmlldyBkYXRhCndhbG1hcnQgPC0gZGF0YS5mcmFtZShyZWFkLmNzdigiV2FsbWFydF9TdG9yZV9zYWxlcy5jc3YiKSwgc3RyaW5nc0FzRmFjdG9ycyA9IFQpClZpZXcod2FsbWFydCkKCmBgYAoKIyMgQmFzaWMgVGFza3MKVGhlcmUgYXJlIDUgYmFzaWMgdGFza3MgdG8gcGVyZm9ybSB1c2luZyB0aGlzIGRhdGEgc2V0OgotIElkZW50aWZ5IHRoZSBzdG9yZSB3aXRoIG1heGltdW0gc2FsZXMuCi0gSWRlbnRpZnkgdGhlIHN0b3JlIHdpdGggdGhlIGdyZWF0ZXN0IHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIGl0cyBjb2VmZmljaWVudCBvZiBtZWFuIHRvIHN0YW5kYXJkIGRldmlhdGlvbi4KLSBJZGVudGlmeSB3aGljaCBzdG9yZShzKSBzaG93ZWQgZ29vZCBxdWFydGVybHkgZ3Jvd3RoIHJhdGUgaW4gUTMnMjAxMi4KLSBJZGVudGlmeSB0aGUgaG9saWRheXMgc2hvd2luZyBhIGhpZ2hlciBzYWxlcyB0aGFuIHRoZSBtZWFuIHNhbGVzIG9mIG5vbi1ob2xpZGF5IHN0b3JlcyBpbiBvbmUgdmlldy4KLSBQcm92aWRlIG1vbnRobHkgYW5kIHNlbWVzdGVyIHZpZXcgb2Ygc2FsZXMgaW4gdW5pdHMgYWxvbmcgd2l0aCBpbnNpZ2h0cy4KCmBgYHtyfQojIENoZWNrIGZvciBtaXNzaW5nIHZhbHVlcwp3YWxtYXJ0WyFjb21wbGV0ZS5jYXNlcyh3YWxtYXJ0KV0gIyBUaGVyZSBkb2VzIG5vdCBhcHBlYXIgdG8gYmUgYW55IG1pc3NpbmcgZGF0YQpgYGAKCiMjIyBJZGVudGlmeSB0aGUgc3RvcmUgd2l0aCBtYXhpbXVtIHNhbGVzCmBgYHtyfQojIElkZW50aWZ5IHRoZSBzdG9yZXMKc3RvcmVzIDwtIGFzLmZhY3Rvcih3YWxtYXJ0JFN0b3JlKQpzdG9yZV9jb3VudCA8LSBsZW5ndGgoc3RvcmVzKSAjIDQ1IHN0b3JlcwoKIyBJZGVudGlmeSB0aGUgd2Vla3MKd2Vla3MgPC0gYXMuZmFjdG9yKHdhbG1hcnQkRGF0ZSkKd2Vla19jb3VudCA8LSBsZW5ndGgod2Vla3MpICMgMTQzIHdlZWtzCgojIFN1bW1hcnkgb2Ygc2FsZXMgYnkgc3RvcmUKc2FsZXNfc3VtbWFyeSA8LSBkYXRhLmZyYW1lKGFnZ3JlZ2F0ZSh3YWxtYXJ0JFdlZWtseV9TYWxlcyxsaXN0KHdhbG1hcnQkU3RvcmUpLCBGVU4gPSBzdW0pKQpWaWV3KHNhbGVzX3N1bW1hcnkpICMgVmlld3MgdGhlIHJldHVybmVkIGRhdGEgZnJhbWUKCm1heF9zYWxlc192YWwgPC0gbWF4KHNhbGVzX3N1bW1hcnkkeCkgIyBIaWdoZXN0IHNhbGVzOiAkMzAxLDM5Nyw3OTIuNDYKbWF4X3NhbGVzX3N0b3JlIDwtIHNhbGVzX3N1bW1hcnlbc2FsZXNfc3VtbWFyeSR4ID09IG1heF9zYWxlc192YWwsIDFdICMgVGhlIHN0b3JlIHdpdGggaGlnaGVzdCBzYWxlcywgIzIwCgojIFBsb3QgdGhlIHNhbGVzIHN1bW1hcnkgcGVyIHN0b3JlIGFjcm9zcyB0aGUgZW50aXJlIGRhdGEgc2V0CnNhbGVzX3N1bW1hcnlfYmFyIDwtIGdncGxvdChzYWxlc19zdW1tYXJ5LCBhZXMoeCA9IGZhY3RvcihHcm91cC4xKSwgeSA9IHgpKSArCiAgZ2VvbV9jb2woZmlsbCA9ICcjNDQwMTU0JykgKyAKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGUgPSAnWWVhcmx5IFNhbGVzJywgeCA9ICdTdG9yZSBOdW1iZXInLCB5ID0gJ1NhbGVzIChpbiAkKScpCgpzYWxlc19zdW1tYXJ5X2JhcgpgYGAKCgojIyMgSWRlbnRpZnkgdGhlIHN0b3JlIHdpdGggdGhlIGdyZWF0ZXN0IHN0YW5kYXJkIGRldmlhdGlvbiwgYW5kIGl0cyBjb2VmZmljaWVudCBvZiBtZWFuIHRvIHN0YW5kYXJkIGRldmlhdGlvbi4KYGBge3J9CiMgU3RkIGRldiBwZXIgc3RvcmUKc3RvcmVfc3RkZXYgPC0gZGF0YS5mcmFtZShhZ2dyZWdhdGUod2FsbWFydCRXZWVrbHlfU2FsZXMsIGxpc3Qod2FsbWFydCRTdG9yZSksIEZVTiA9IHNkKSkKVmlldyhzdG9yZV9zdGRldikgIyBWaWV3cyB0aGUgcmV0dXJuZWQgZGF0YSBmcmFtZQoKIyBDb2VmZmljaWVudCBvZiBtZWFuIHRvIHN0YW5kYXJkIGRldmlhdGlvbiBwZXIgc3RvcmUKc3RvcmVfbWVhbiA8LSAodGFwcGx5KHdhbG1hcnQkV2Vla2x5X1NhbGVzLCBzdG9yZXMsIG1lYW4pKQpzdG9yZV9tZWFuX2NvZWZmIDwtIG51bWVyaWMoKQoKZm9yIChpIGluIDE6bGVuZ3RoKHN0b3JlX21lYW4pKSB7CiAgc3RvcmVfbWVhbl9jb2VmZltpXSA8LSBzdG9yZV9tZWFuW2ldL3N0b3JlX3N0ZGV2JHhbaV0KfQoKc3RvcmVfc3RkZXYkc3RvcmVfbWVhbl9jb2VmZiA8LSBzdG9yZV9tZWFuX2NvZWZmCnN0b3JlX3N0ZGV2JGF2ZyA8LSBzdG9yZV9tZWFuCgojIERvIGEgZmluYWwgbWVyZ2Ugb2YgZGF0YWZyYW1lcyBzYWxlc19zdW1tYXJ5IGFuZCBzdG9yZV9zdGRldgpzYWxlc19zdW1fZmluYWwgPC0gbWVyZ2Uoc2FsZXNfc3VtbWFyeSwgc3RvcmVfc3RkZXYsIGJ5ID0gIkdyb3VwLjEiKSAjIEdyb3VwLjEgaXMgc3RvcmUgbnVtYmVyLCB4LnggPSBzdW0gb2Ygd2Vla2x5IHNhbGVzIHBlciBzdG9yZSwgeC55ID0gc3RkIGRldiBvZiBhbGwgd2Vla2x5IHNhbGVzIHBlciBzdG9yZQpWaWV3KHNhbGVzX3N1bV9maW5hbCkKCiMgUmVuYW1lIGNvbHVtbnMKc2FsZXNfc3VtX2ZpbmFsIDwtIHBseXI6OnJlbmFtZShzYWxlc19zdW1fZmluYWwsIGMoJ0dyb3VwLjEnID0gJ2lkJywgJ3gueCcgPSAnc2FsZXNfc3VtJywgJ3gueScgPSAnc3RkX2RldicpKQoKIyBGaW5kIHN0b3JlIHdpdGggdGhlIGdyZWF0ZXN0IHN0YW5kYXJkIGRldmlhdGlvbiBhbmQgY29lZmZpY2llbnQgb2YgdGhlIG1lYW4gdG8gc3RkIGRldgptYXhfc3RkX2RldiA8LSBtYXgoc2FsZXNfc3VtX2ZpbmFsJHN0ZF9kZXYpICMgMzE3NTY5Ljk1Cm1heF9zdGRldl9zdG9yZSA8LSBzYWxlc19zdW1fZmluYWxbc2FsZXNfc3VtX2ZpbmFsJHN0ZF9kZXYgPT0gbWF4X3N0ZF9kZXYsIDFdICMgU3RvcmUgMTQKbWF4X21lYW5fY29lZiA8LSBtYXgoc2FsZXNfc3VtX2ZpbmFsJHN0b3JlX21lYW5fY29lZmYpICMgMjMuNzYKbWF4X21jX3N0b3JlIDwtIHNhbGVzX3N1bV9maW5hbFtzYWxlc19zdW1fZmluYWwkc3RvcmVfbWVhbl9jb2VmZiA9PSBtYXhfbWVhbl9jb2VmLCAxXSAjIFN0b3JlIDM3CgpgYGAKCiMjIyBJZGVudGlmeSB3aGljaCBzdG9yZShzKSBzaG93ZWQgZ29vZCBxdWFydGVybHkgZ3Jvd3RoIHJhdGUgaW4gUTMnMjAxMi4KYGBge3J9CiMjIFF1YXJ0ZXJzIG9mIHRoZSB5ZWFyICMjCiMgUTE6IEphbiwgRmViLCBNYXIgICAgICAjCiMgUTI6IEFwciwgTWF5LCBKdW4gICAgICAjCiMgUTM6IEp1bCwgQXVnLCBTZXB0ICAgICAjCiMgUTQ6IE9jdCwgTm92LCBEZWMgICAgICAjCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCnExIDwtIGMoJzAxJywgJzAyJywgJzAzJykKcTIgPC0gYygnMDQnLCcwNScsJzA2JykKcTMgPC0gYygnMDcnLCcwOCcsJzA5JykKcTQgPC0gYygnMTAnLCAnMTEnLCAnMTInKQojIENyZWF0ZSBuZXcgY29sdW1uIGluIHdhbG1hcnQgZGF0YWZyYW1lCnF1YXJ0ZXJzIDwtIGNoYXJhY3RlcigpIAoKIyBJdGVyYXRlIHRocm91Z2ggdGhlIGRhdGUgY29sdW1uCmZvciAoaSBpbiAxOmxlbmd0aCh3YWxtYXJ0JERhdGUpKSB7CiAgIyBHcmFiIHRoZSBkYXRlIHN0cmluZwogIHRoaXNfZGF0ZSA8LSB1bmxpc3Qoc3Ryc3BsaXQod2FsbWFydCREYXRlW2ldLCAiLSIpKQogICMgTWFyayBhcyBRMQogIGlmKHRoaXNfZGF0ZVsyXSAlaW4lIHExKSB7CiAgICBxdWFydGVyc1tpXSA8LSAnUTEnCiAgfQogICMgTWFyayBhcyBRMgogIGVsc2UgaWYodGhpc19kYXRlWzJdICVpbiUgcTIpIHsKICAgIHF1YXJ0ZXJzW2ldIDwtICdRMicKICB9CiAgIyBNYXJrIGFzIFEzCiAgZWxzZSBpZih0aGlzX2RhdGVbMl0gJWluJSBxMykgewogICAgcXVhcnRlcnNbaV0gPC0gJ1EzJwogIH0KICAjIE1hcmsgYXMgUTQKICBlbHNlIHsKICAgIHF1YXJ0ZXJzW2ldIDwtICdRNCcKICB9Cn0KCiMgQWRkIG5ldyBjb2x1bW4gdG8gd2FsbWFydCBkYXRhIGZyYW1lCndhbG1hcnQkUXVhcnRlciA8LSBxdWFydGVycwoKIyBHZXQgc3VtbWFyeSBvZiBmaXNjYWwgcXVhcnRlcnMKcXVhcnRlcl9jb3VudHMgPC0gc3VtbWFyeShhcy5mYWN0b3Iod2FsbWFydCRRdWFydGVyKSkKCiMgU3Vic2V0IGRhdGEgZm9yIFEyIGFuZCBRMwojIFEyCnEyX3dlZWtsaWVzIDwtIHN1YnNldCh3YWxtYXJ0LChzdWJzdHIoRGF0ZSwgNywgMTApICVpbiUgYygiMjAxMiIpKSAmIChRdWFydGVyID09ICdRMicpLCBzZWxlY3QgPSBjKFN0b3JlLCAgV2Vla2x5X1NhbGVzKSkKIyBRMwpxM193ZWVrbGllcyA8LSBzdWJzZXQod2FsbWFydCwoc3Vic3RyKERhdGUsIDcsIDEwKSAlaW4lIGMoIjIwMTIiKSkgJiAoUXVhcnRlciA9PSAnUTMnKSwgc2VsZWN0ID0gYyhTdG9yZSwgV2Vla2x5X1NhbGVzKSkKCiMgQ29tYmluZSBzcGxpdCB3ZWVrbHkgZGF0YSBmcmFtZXMKcTNfZ3Jvd3RoIDwtIGNiaW5kKHEyX3dlZWtsaWVzLCBxM193ZWVrbGllcyRXZWVrbHlfU2FsZXMpCiMgUmVuYW1lIGNvbHVtbnMgZm9yIGNsYXJpdHkKcTNfZ3Jvd3RoIDwtIHBseXI6OnJlbmFtZShxM19ncm93dGgsIGMoJ1dlZWtseV9TYWxlcycgPSAncTJfd2Vla2x5X3NhbGVzJywgJ3EzX3dlZWtsaWVzJFdlZWtseV9TYWxlcycgPSAncTNfd2Vla2x5X3NhbGVzJykpCgojIENyZWF0ZSB0aGUgZ3Jvd3RoIGNvbXBhcmlzb24gZGF0YSBmcmFtZQpxM19ncm93dGhfY29tcGFyaXNvbiA8LSBkYXRhLmZyYW1lKGFnZ3JlZ2F0ZShjYmluZChxMl93ZWVrbHlfc2FsZXMscTNfd2Vla2x5X3NhbGVzKSB+IFN0b3JlLCBkYXRhID0gcTNfZ3Jvd3RoLCBGVU4gPSBzdW0pKQojIENvbXBhcmlzb24gY29sdW1uCnEzX2dyb3d0aF9jb21wYXJpc29uJFEzX0dyb3d0aF9SYXRlIDwtICgocTNfZ3Jvd3RoX2NvbXBhcmlzb24kcTNfd2Vla2x5X3NhbGVzIC0gcTNfZ3Jvd3RoX2NvbXBhcmlzb24kcTJfd2Vla2x5X3NhbGVzKS9xM19ncm93dGhfY29tcGFyaXNvbiRxMl93ZWVrbHlfc2FsZXMpCiMgVmlldwpWaWV3KHEzX2dyb3d0aF9jb21wYXJpc29uKQoKIyBXaGljaCBzdG9yZXMgaGFkIGEgcG9zaXRpdmUgUTMnMjAxMiBncm93dGggcmF0ZQpxM19wb3NfZ3Jvd3RoIDwtIGRhdGEuZnJhbWUoc3Vic2V0KHEzX2dyb3d0aF9jb21wYXJpc29uLCBxM19ncm93dGhfY29tcGFyaXNvbiRRM19Hcm93dGhfUmF0ZSA+IDAsIHNlbGVjdCA9IGMoUTNfR3Jvd3RoX1JhdGUpKSkgIyAxMCBzdG9yZXM6IDcsIDE2LCAyMywgMjQsIDI2LCAzNSwgMzksIDQwLCA0MSwgNDQKCiMgTGluZSBjaGFydApxM19ncm93dGhfY2hhcnQgPC0gZ2dwbG90KHEzX2dyb3d0aF9jb21wYXJpc29uLCBhZXMoeCA9IGZhY3RvcihTdG9yZSksIHkgPSBRM19Hcm93dGhfUmF0ZSwgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZSgpKwogIGdlb21fcG9pbnQoYWVzKGNvbG9yID0gUTNfR3Jvd3RoX1JhdGUgPiAwKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lID0gJ0dyb3d0aCBSYXRlJywgdmFsdWVzID0gc2V0TmFtZXMoYygnZ3JlZW40JywgJ3JlZDMnKSwgYyhULCBGKSksIGxhYmVscyA9IGMoJ1Bvc2l0aXZlJywgJ05lZ2F0aXZlJykpICsKICBsYWJzKHRpdGxlID0gJ1EzIDIwMTIgR3Jvd3RoIFJhdGUgYnkgU3RvcmUnLCB4ID0gJ1N0b3JlIE51bWJlcicsIHkgPSAnR3Jvd3RoIFJhdGUnKQoKcTNfZ3Jvd3RoX2NoYXJ0CgpgYGAKIyMjIElkZW50aWZ5IHRoZSBob2xpZGF5cyBzaG93aW5nIGEgaGlnaGVyIHNhbGVzIHRoYW4gdGhlIG1lYW4gc2FsZXMgb2Ygbm9uLWhvbGlkYXkgc3RvcmVzIGluIG9uZSB2aWV3LgotIFN1cGVyIEJvd2w6IDEyLUZlYi0xMCwgMTEtRmViLTExLCAxMC1GZWItMTIsIDgtRmViLTEzCi0gTGFib3IgRGF5OiAxMC1TZXAtMTAsIDktU2VwLTExLCA3LVNlcC0xMiwgNi1TZXAtMTMKLSBUaGFua3NnaXZpbmc6IDI2LU5vdi0xMCwgMjUtTm92LTExLCAyMy1Ob3YtMTIsIDI5LU5vdi0xMwotIENocmlzdG1hczogMzEtRGVjLTEwLCAzMC1EZWMtMTEsIDI4LURlYy0xMiwgMjctRGVjLTEzCgpgYGB7cn0KIyBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIHdpdGggdGhlIGEgaG9saWRheSBjb2x1bW4KaG9saWRheSA8LSBkYXRhLmZyYW1lKHdhbG1hcnRbLGMoJ1N0b3JlJywgJ0RhdGUnLCAnV2Vla2x5X1NhbGVzJywgJ0hvbGlkYXlfRmxhZycpXSkKVmlldyhob2xpZGF5KQoKIyBBZGQgYW4gZW1wdHkgY29sdW1uIHRvIGhvbGlkYXkgCmhvbGlkYXlbLCdIb2xpZGF5X05hbWUnXSA8LSBOQQoKIyBBZGQgYSB5ZWFyIGNvbHVtbgpob2xpZGF5WywgJ1llYXInXSA8LSBOQQoKIyBBZGQgYSBtb250aCBjb2x1bW4KaG9saWRheVssICdNb250aCddIDwtIE5BCgojIENoZWNrIGFnYWluc3QgaG9saWRheSBmbGFnICsgZGF0ZSBjb2x1bW4KIyAxID0gaG9saWRheSwgaWRlbnRpZnkgaG9saWRheSBiYXNlZCBvbiBtb250aAojIDAgPSBub3QgYSBob2xpZGF5CmZvciAoaSBpbiAxOmxlbmd0aChob2xpZGF5JEhvbGlkYXlfRmxhZykpIHsKICAjIFdoZW4gYSBkYXkgaXMgYSBob2xpZGF5CiAgaWYgKGhvbGlkYXkkSG9saWRheV9GbGFnW2ldID09IDEpIHsKICAgICMgUmV0cmlldmUgdGhlIGRhdGUgZnJvbSBEYXRlCiAgICBob2xpX2RhdGUgPC0gdW5saXN0KHN0cnNwbGl0KGhvbGlkYXkkRGF0ZVtpXSwgIi0iKSkKICAgICMgRm9jdXMgb24gdGhlIG1vbnRoIHRvIGZpbmQgdGhlIHNwZWNpZmljIGhvbGlkYXkKICAgIGhvbGlfbW9udGggPC0gaG9saV9kYXRlWzJdCiAgICAjIFN1cGVyYm93bAogICAgaWYgKGhvbGlfbW9udGggPT0gJzAyJykgewogICAgICBob2xpZGF5JEhvbGlkYXlfTmFtZVtpXSA8LSBwYXN0ZSgnU3VwZXJib3dsJywgaG9saV9kYXRlWzNdKQogICAgfQogICAgIyBMYWJvciBEYXkKICAgIGVsc2UgaWYgKGhvbGlfbW9udGggPT0gJzA5JykgewogICAgICBob2xpZGF5JEhvbGlkYXlfTmFtZVtpXSA8LSBwYXN0ZSgnTGFib3IgRGF5JywgaG9saV9kYXRlWzNdKQogICAgfQogICAgIyBUaGFua3NnaXZpbmcKICAgIGVsc2UgaWYgKGhvbGlfbW9udGggPT0gJzExJykgewogICAgICBob2xpZGF5JEhvbGlkYXlfTmFtZVtpXSA8LSBwYXN0ZSgnVGhhbmtzZ2l2aW5nJywgaG9saV9kYXRlWzNdKQogICAgfQogICAgIyBDaHJpc3RtYXMKICAgIGVsc2UgaWYgKGhvbGlfbW9udGggPT0gJzEyJykgewogICAgICBob2xpZGF5JEhvbGlkYXlfTmFtZVtpXSA8LSBwYXN0ZSgnQ2hyaXN0bWFzJywgaG9saV9kYXRlWzNdKQogICAgfQogIH0KICAjIFdoZW4gYSBkYXkgaXMgbm90IGEgaG9saWRheQogIGVsc2UgewogICAgaG9saWRheSRIb2xpZGF5X05hbWVbaV0gPC0gJ25vIGhvbGlkYXknCiAgfQp9CgojIEdldCB5ZWFyIGFuZCBtb250aApmb3IgKGkgaW4gMTpsZW5ndGgoaG9saWRheSREYXRlKSkgewogICMgUmV0cmlldmUgdGhlIGRhdGUgZnJvbSBEYXRlCiAgaG9saV9kYXRlIDwtIHVubGlzdChzdHJzcGxpdChob2xpZGF5JERhdGVbaV0sICItIikpCiAgIyBHZXQgdGhlIHllYXIKICBob2xpZGF5JFllYXJbaV0gPC0gaG9saV9kYXRlWzNdCiAgIyBHZXQgdGhlIG1vbnRoCiAgaG9saWRheSRNb250aFtpXSA8LSBob2xpX2RhdGVbMl0KfQoKIyMjIyMgQUNST1NTIEFMTCBZRUFSUywgTk9OLUhPTElEQVkgVlMuIEhPTElEQVkgIyMjIyMKCiMgQ3JlYXRlIG5ldyBkYXRhZnJhbWVzIGZvciBjb21wYXJpc29uCiMgTm8gaG9saWRheXMKbm9faG9saWRheXMgPC0gZGF0YS5mcmFtZShzdWJzZXQoaG9saWRheSwgSG9saWRheV9OYW1lID09ICdubyBob2xpZGF5Jywgc2VsZWN0ID0gLWMoSG9saWRheV9GbGFnKSkpClZpZXcobm9faG9saWRheXMpCiMgQWdncmVnYXRlZCBhdmVyYWdlcwpub19ob2xfYXZncyA8LSBkYXRhLmZyYW1lKGFnZ3JlZ2F0ZShub19ob2xpZGF5cyRXZWVrbHlfU2FsZXMsIGxpc3Qobm9faG9saWRheXMkU3RvcmUpLCBGVU4gPSBtZWFuKSkKVmlldyhub19ob2xfYXZncykKIyBSZW5hbWUgY29sdW1ucwpub19ob2xfYXZncyA8LSBwbHlyOjpyZW5hbWUobm9faG9sX2F2Z3MsIGMoJ0dyb3VwLjEnID0gJ1N0b3JlJywgJ3gnID0gJ1dlZWtseV9BdmdfTm9uSG9saWRheScpKQoKIyMgSE9MSURBWSBEQVRBIEZSQU1FUyAjIwojIFN1cGVyYm93bCBkYXRhZnJhbWVzCnN1cGVyYm93bF8yMDEwIDwtIGRhdGEuZnJhbWUoc3Vic2V0KGhvbGlkYXksIEhvbGlkYXlfTmFtZSA9PSAnU3VwZXJib3dsIDIwMTAnLCBzZWxlY3QgPSAtYyhIb2xpZGF5X0ZsYWcpKSkKVmlldyhzdXBlcmJvd2xfMjAxMCkKCnN1cGVyYm93bF8yMDExIDwtIGRhdGEuZnJhbWUoc3Vic2V0KGhvbGlkYXksIEhvbGlkYXlfTmFtZSA9PSAnU3VwZXJib3dsIDIwMTEnLCBzZWxlY3QgPSAtYyhIb2xpZGF5X0ZsYWcpKSkKVmlldyhzdXBlcmJvd2xfMjAxMSkKCnN1cGVyYm93bF8yMDEyIDwtIGRhdGEuZnJhbWUoc3Vic2V0KGhvbGlkYXksIEhvbGlkYXlfTmFtZSA9PSAnU3VwZXJib3dsIDIwMTInLCBzZWxlY3QgPSAtYyhIb2xpZGF5X0ZsYWcpKSkKVmlldyhzdXBlcmJvd2xfMjAxMikKCiMgTGFib3IgRGF5IGRhdGFmcmFtZXMKbGFib3JkYXlfMjAxMCA8LSBkYXRhLmZyYW1lKHN1YnNldChob2xpZGF5LCBIb2xpZGF5X05hbWUgPT0gJ0xhYm9yIERheSAyMDEwJywgc2VsZWN0ID0gLWMoSG9saWRheV9GbGFnKSkpClZpZXcobGFib3JkYXlfMjAxMCkKCmxhYm9yZGF5XzIwMTEgPC0gZGF0YS5mcmFtZShzdWJzZXQoaG9saWRheSwgSG9saWRheV9OYW1lID09ICdMYWJvciBEYXkgMjAxMScsIHNlbGVjdCA9IC1jKEhvbGlkYXlfRmxhZykpKQpWaWV3KGxhYm9yZGF5XzIwMTEpCgpsYWJvcmRheV8yMDEyIDwtIGRhdGEuZnJhbWUoc3Vic2V0KGhvbGlkYXksIEhvbGlkYXlfTmFtZSA9PSAnTGFib3IgRGF5IDIwMTInLCBzZWxlY3QgPSAtYyhIb2xpZGF5X0ZsYWcpKSkKVmlldyhsYWJvcmRheV8yMDEyKQoKIyBUaGFua3NnaXZpbmcgZGF0YWZyYW1lcwp0aGFua3NnaXZpbmdfMjAxMCA8LSBkYXRhLmZyYW1lKHN1YnNldChob2xpZGF5LCBIb2xpZGF5X05hbWUgPT0gJ1RoYW5rc2dpdmluZyAyMDEwJywgc2VsZWN0ID0gLWMoSG9saWRheV9GbGFnKSkpClZpZXcodGhhbmtzZ2l2aW5nXzIwMTApCgp0aGFua3NnaXZpbmdfMjAxMSA8LSBkYXRhLmZyYW1lKHN1YnNldChob2xpZGF5LCBIb2xpZGF5X05hbWUgPT0gJ1RoYW5rc2dpdmluZyAyMDExJywgc2VsZWN0ID0gLWMoSG9saWRheV9GbGFnKSkpClZpZXcodGhhbmtzZ2l2aW5nXzIwMTEpCgojIENocmlzdG1hcyBkYXRhZnJhbWVzCmNocmlzdG1hc18yMDEwIDwtIGRhdGEuZnJhbWUoc3Vic2V0KGhvbGlkYXksIEhvbGlkYXlfTmFtZSA9PSAnQ2hyaXN0bWFzIDIwMTAnLCBzZWxlY3QgPSAtYyhIb2xpZGF5X0ZsYWcpKSkKVmlldyhjaHJpc3RtYXNfMjAxMCkKCmNocmlzdG1hc18yMDExIDwtIGRhdGEuZnJhbWUoc3Vic2V0KGhvbGlkYXksIEhvbGlkYXlfTmFtZSA9PSAnQ2hyaXN0bWFzIDIwMTEnLCBzZWxlY3QgPSAtYyhIb2xpZGF5X0ZsYWcpKSkKVmlldyhjaHJpc3RtYXNfMjAxMSkKCiMgTWVyZ2UgeWVhcmx5IGhvbGlkYXkgY29sdW1ucyB0byBub19ob2xfYXZncyBkYXRhZnJhbWUKIyBTdXBlcmJvd2wKbm9faG9sX2F2Z3MkU3VwZXJib3dsXzIwMTAgPC0gc3VwZXJib3dsXzIwMTAkV2Vla2x5X1NhbGVzCm5vX2hvbF9hdmdzJFN1cGVyYm93bF8yMDExIDwtIHN1cGVyYm93bF8yMDExJFdlZWtseV9TYWxlcwpub19ob2xfYXZncyRTdXBlcmJvd2xfMjAxMiA8LSBzdXBlcmJvd2xfMjAxMiRXZWVrbHlfU2FsZXMKCiMgTGFib3IgRGF5Cm5vX2hvbF9hdmdzJExhYm9yRGF5XzIwMTAgPC0gbGFib3JkYXlfMjAxMCRXZWVrbHlfU2FsZXMKbm9faG9sX2F2Z3MkTGFib3JEYXlfMjAxMSA8LSBsYWJvcmRheV8yMDExJFdlZWtseV9TYWxlcwpub19ob2xfYXZncyRMYWJvckRheV8yMDEyIDwtIGxhYm9yZGF5XzIwMTIkV2Vla2x5X1NhbGVzCgojIFRoYW5rc2dpdmluZwpub19ob2xfYXZncyRUaGFua3NnaXZpbmdfMjAxMCA8LSB0aGFua3NnaXZpbmdfMjAxMCRXZWVrbHlfU2FsZXMKbm9faG9sX2F2Z3MkVGhhbmtzZ2l2aW5nXzIwMTEgPC0gdGhhbmtzZ2l2aW5nXzIwMTEkV2Vla2x5X1NhbGVzCgojIENocmlzdG1hcwpub19ob2xfYXZncyRDaHJpc3RtYXNfMjAxMCA8LSBjaHJpc3RtYXNfMjAxMCRXZWVrbHlfU2FsZXMKbm9faG9sX2F2Z3MkQ2hyaXN0bWFzXzIwMTEgPC0gY2hyaXN0bWFzXzIwMTEkV2Vla2x5X1NhbGVzCgojIyBDT01QQVJJU09OUyAjIyAKIyBTdXBlcmJvd2wgCm5vX2hvbF9hdmdzJHN1cGVyYm93bF8yMDEwX2NvbXAgPC0gaWZlbHNlKG5vX2hvbF9hdmdzJFN1cGVyYm93bF8yMDEwID4gbm9faG9sX2F2Z3MkV2Vla2x5X0F2Z19Ob25Ib2xpZGF5LCAiZ3JlYXRlciIsICJub3QgZ3JlYXRlciIpCm5vX2hvbF9hdmdzJHN1cGVyYm93XzIwMTFfY29tcCA8LSBpZmVsc2Uobm9faG9sX2F2Z3MkU3VwZXJib3dsXzIwMTEgPiBub19ob2xfYXZncyRXZWVrbHlfQXZnX05vbkhvbGlkYXksICJncmVhdGVyIiwgIm5vdCBncmVhdGVyIikKbm9faG9sX2F2Z3Mkc3VwZXJib3dsXzIwMTJfY29tcCA8LSBpZmVsc2Uobm9faG9sX2F2Z3MkU3VwZXJib3dsXzIwMTIgPiBub19ob2xfYXZncyRXZWVrbHlfQXZnX05vbkhvbGlkYXksICJncmVhdGVyIiwgIm5vdCBncmVhdGVyIikKCiMgTGFib3IgRGF5IApub19ob2xfYXZncyRsYWJvcmRheV8yMDEwX2NvbXAgPC0gaWZlbHNlKG5vX2hvbF9hdmdzJExhYm9yRGF5XzIwMTAgPiBub19ob2xfYXZncyRXZWVrbHlfQXZnX05vbkhvbGlkYXksICJncmVhdGVyIiwgIm5vdCBncmVhdGVyIikKbm9faG9sX2F2Z3MkbGFib3JkYXlfMjAxMV9jb21wIDwtIGlmZWxzZShub19ob2xfYXZncyRMYWJvckRheV8yMDExID4gbm9faG9sX2F2Z3MkV2Vla2x5X0F2Z19Ob25Ib2xpZGF5LCAiZ3JlYXRlciIsICJub3QgZ3JlYXRlciIpCm5vX2hvbF9hdmdzJGxhYm9yZGF5XzIwMTJfY29tcCA8LSBpZmVsc2Uobm9faG9sX2F2Z3MkTGFib3JEYXlfMjAxMiA+IG5vX2hvbF9hdmdzJFdlZWtseV9BdmdfTm9uSG9saWRheSwgImdyZWF0ZXIiLCAibm90IGdyZWF0ZXIiKQoKIyBUaGFua3NnaXZpbmcgCm5vX2hvbF9hdmdzJHRoYW5rc2dpdmluZ18yMDEwX2NvbXAgPC0gaWZlbHNlKG5vX2hvbF9hdmdzJFRoYW5rc2dpdmluZ18yMDEwID4gbm9faG9sX2F2Z3MkV2Vla2x5X0F2Z19Ob25Ib2xpZGF5LCAiZ3JlYXRlciIsICJub3QgZ3JlYXRlciIpCm5vX2hvbF9hdmdzJHRoYW5rc2dpdmluZ18yMDExX2NvbXAgPC0gaWZlbHNlKG5vX2hvbF9hdmdzJFRoYW5rc2dpdmluZ18yMDExID4gbm9faG9sX2F2Z3MkV2Vla2x5X0F2Z19Ob25Ib2xpZGF5LCAiZ3JlYXRlciIsICJub3QgZ3JlYXRlciIpCgojIENocmlzdG1hcyAKbm9faG9sX2F2Z3MkY2hyaXN0bWFzXzIwMTBfY29tcCA8LSBpZmVsc2Uobm9faG9sX2F2Z3MkQ2hyaXN0bWFzXzIwMTAgPiBub19ob2xfYXZncyRXZWVrbHlfQXZnX05vbkhvbGlkYXksICJncmVhdGVyIiwgIm5vdCBncmVhdGVyIikKbm9faG9sX2F2Z3MkY2hyaXN0bWFzXzIwMTFfY29tcCA8LSBpZmVsc2Uobm9faG9sX2F2Z3MkQ2hyaXN0bWFzXzIwMTEgPiBub19ob2xfYXZncyRXZWVrbHlfQXZnX05vbkhvbGlkYXksICJncmVhdGVyIiwgIm5vdCBncmVhdGVyIikKCgojIyMjIyBXSVRISU4gWUVBUiAyMDEwOiBOT04tSE9MSURBWSBWUy4gSE9MSURBWSAjIyMjIwpuaF8yMDEwIDwtIGRhdGEuZnJhbWUoc3Vic2V0KG5vX2hvbF9hdmdzLCBzZWxlY3QgPSAtYygyOjIyKSkpCgojIEZlYnJ1YXJ5IDIwMTAKbmhfRmViMjAxMCA8LSBob2xpZGF5ICU+JSAKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoSG9saWRheV9GbGFnID09IDAgJiBZZWFyID09ICcyMDEwJyAmIE1vbnRoID09ICcwMicpICU+JQogICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KFN0b3JlKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoTWVhbiA9IG1lYW4oV2Vla2x5X1NhbGVzKSkKCiMgU2VwdGVtYmVyIDIwMTAKbmhfU2VwdDIwMTAgPC0gaG9saWRheSAlPiUgCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEhvbGlkYXlfRmxhZyA9PSAwICYgWWVhciA9PSAnMjAxMCcgJiBNb250aCA9PSAnMDknKSAlPiUKICAgICAgICAgICAgICAgICAgICBncm91cF9ieShTdG9yZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKE1lYW4gPSBtZWFuKFdlZWtseV9TYWxlcykpCgojIE5vdmVtYmVyIDIwMTAKbmhfTm92MjAxMCA8LSBob2xpZGF5ICU+JSAKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoSG9saWRheV9GbGFnID09IDAgJiBZZWFyID09ICcyMDEwJyAmIE1vbnRoID09ICcxMScpICU+JQogICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KFN0b3JlKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoTWVhbiA9IG1lYW4oV2Vla2x5X1NhbGVzKSkKCgojIERlY2VtYmVyIDIwMTAKbmhfRGVjMjAxMCA8LSBob2xpZGF5ICU+JSAKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoSG9saWRheV9GbGFnID09IDAgJiBZZWFyID09ICcyMDEwJyAmIE1vbnRoID09ICcxMicpICU+JQogICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KFN0b3JlKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoTWVhbiA9IG1lYW4oV2Vla2x5X1NhbGVzKSkKCiMgQmluZCBjb2x1bW5zCm5oXzIwMTAgPC0gY2JpbmQobmhfMjAxMCwgc3VwZXJib3dsXzIwMTAkV2Vla2x5X1NhbGVzLCBuaF9GZWIyMDEwJE1lYW4sIGxhYm9yZGF5XzIwMTAkV2Vla2x5X1NhbGVzLCBuaF9TZXB0MjAxMCRNZWFuLCB0aGFua3NnaXZpbmdfMjAxMCRXZWVrbHlfU2FsZXMsIG5oX05vdjIwMTAkTWVhbiwgY2hyaXN0bWFzXzIwMTAkV2Vla2x5X1NhbGVzLCBuaF9EZWMyMDEwJE1lYW4pCgojIEFkZCBjb21wYXJpc29uIGNvbHVtbnMKbmhfMjAxMCRzdXBlcmJvd2xfY29tcGFyaXNvbiA8LSBpZmVsc2Uoc3VwZXJib3dsXzIwMTAkV2Vla2x5X1NhbGVzID4gbmhfRmViMjAxMCRNZWFuLCAiZ3JlYXRlciIsICJub3QgZ3JlYXRlciIpICMgU3VwZXJib3dsCm5oXzIwMTAkbGFib3JkYXlfY29tcGFyaXNvbiA8LSBpZmVsc2UobGFib3JkYXlfMjAxMCRXZWVrbHlfU2FsZXMgPiBuaF9TZXB0MjAxMCRNZWFuLCAiZ3JlYXRlciIsICJub3QgZ3JlYXRlciIpICMgTGFib3IgRGF5Cm5oXzIwMTAkdGhhbmtzZ2l2aW5nX2NvbXBhcmlzb24gPC0gaWZlbHNlKHRoYW5rc2dpdmluZ18yMDEwJFdlZWtseV9TYWxlcyA+IG5oX05vdjIwMTAkTWVhbiwgImdyZWF0ZXIiLCAibm90IGdyZWF0ZXIiKSAjIFRoYW5rc2dpdmluZwpuaF8yMDEwJGNocmlzdG1hc19jb21wYXJpc29uIDwtIGlmZWxzZShjaHJpc3RtYXNfMjAxMCRXZWVrbHlfU2FsZXMgPiBuaF9EZWMyMDEwJE1lYW4sICJncmVhdGVyIiwgIm5vdCBncmVhdGVyIikgIyBDaHJpc3RtYXMKCiMgVmlldyBkYXRhZnJhbWUKVmlldyhuaF8yMDEwKQoKIyMjIyMgV0lUSElOIFlFQVIgMjAxMTogTk9OLUhPTElEQVkgVlMuIEhPTElEQVkgIyMjIyMKbmhfMjAxMSA8LSBkYXRhLmZyYW1lKHN1YnNldChub19ob2xfYXZncywgc2VsZWN0ID0gLWMoMjoyMikpKQoKIyBGZWJydWFyeSAyMDExCm5oX0ZlYjIwMTEgPC0gaG9saWRheSAlPiUgCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEhvbGlkYXlfRmxhZyA9PSAwICYgWWVhciA9PSAnMjAxMScgJiBNb250aCA9PSAnMDInKSAlPiUKICAgICAgICAgICAgICAgICAgICBncm91cF9ieShTdG9yZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKE1lYW4gPSBtZWFuKFdlZWtseV9TYWxlcykpCgojIFNlcHRlbWJlciAyMDExCm5oX1NlcHQyMDExIDwtIGhvbGlkYXkgJT4lIAogICAgICAgICAgICAgICAgICAgIGZpbHRlcihIb2xpZGF5X0ZsYWcgPT0gMCAmIFllYXIgPT0gJzIwMTEnICYgTW9udGggPT0gJzA5JykgJT4lCiAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoU3RvcmUpICU+JQogICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShNZWFuID0gbWVhbihXZWVrbHlfU2FsZXMpKQoKIyBOb3ZlbWJlciAyMDExCm5oX05vdjIwMTEgPC0gaG9saWRheSAlPiUgCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEhvbGlkYXlfRmxhZyA9PSAwICYgWWVhciA9PSAnMjAxMScgJiBNb250aCA9PSAnMTEnKSAlPiUKICAgICAgICAgICAgICAgICAgICBncm91cF9ieShTdG9yZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKE1lYW4gPSBtZWFuKFdlZWtseV9TYWxlcykpCgoKIyBEZWNlbWJlciAyMDExCm5oX0RlYzIwMTEgPC0gaG9saWRheSAlPiUgCiAgICAgICAgICAgICAgICAgICAgZmlsdGVyKEhvbGlkYXlfRmxhZyA9PSAwICYgWWVhciA9PSAnMjAxMScgJiBNb250aCA9PSAnMTInKSAlPiUKICAgICAgICAgICAgICAgICAgICBncm91cF9ieShTdG9yZSkgJT4lCiAgICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKE1lYW4gPSBtZWFuKFdlZWtseV9TYWxlcykpCgojIEJpbmQgY29sdW1ucwpuaF8yMDExIDwtIGNiaW5kKG5oXzIwMTEsIHN1cGVyYm93bF8yMDExJFdlZWtseV9TYWxlcywgbmhfRmViMjAxMSRNZWFuLCBsYWJvcmRheV8yMDExJFdlZWtseV9TYWxlcywgbmhfU2VwdDIwMTEkTWVhbiwgdGhhbmtzZ2l2aW5nXzIwMTEkV2Vla2x5X1NhbGVzLCBuaF9Ob3YyMDExJE1lYW4sIGNocmlzdG1hc18yMDExJFdlZWtseV9TYWxlcywgbmhfRGVjMjAxMSRNZWFuKQoKIyBBZGQgY29tcGFyaXNvbiBjb2x1bW5zCm5oXzIwMTEkc3VwZXJib3dsX2NvbXBhcmlzb24gPC0gaWZlbHNlKHN1cGVyYm93bF8yMDExJFdlZWtseV9TYWxlcyA+IG5oX0ZlYjIwMTEkTWVhbiwgImdyZWF0ZXIiLCAibm90IGdyZWF0ZXIiKSAjIFN1cGVyYm93bApuaF8yMDExJGxhYm9yZGF5X2NvbXBhcmlzb24gPC0gaWZlbHNlKGxhYm9yZGF5XzIwMTEkV2Vla2x5X1NhbGVzID4gbmhfU2VwdDIwMTEkTWVhbiwgImdyZWF0ZXIiLCAibm90IGdyZWF0ZXIiKSAjIExhYm9yIERheQpuaF8yMDExJHRoYW5rc2dpdmluZ19jb21wYXJpc29uIDwtIGlmZWxzZSh0aGFua3NnaXZpbmdfMjAxMSRXZWVrbHlfU2FsZXMgPiBuaF9Ob3YyMDExJE1lYW4sICJncmVhdGVyIiwgIm5vdCBncmVhdGVyIikgIyBUaGFua3NnaXZpbmcKbmhfMjAxMSRjaHJpc3RtYXNfY29tcGFyaXNvbiA8LSBpZmVsc2UoY2hyaXN0bWFzXzIwMTEkV2Vla2x5X1NhbGVzID4gbmhfRGVjMjAxMSRNZWFuLCAiZ3JlYXRlciIsICJub3QgZ3JlYXRlciIpICMgQ2hyaXN0bWFzCgojIFZpZXcgZGF0YWZyYW1lClZpZXcobmhfMjAxMSkKCiMjIyMjIFdJVEhJTiBZRUFSIDIwMTI6IE5PTi1IT0xJREFZIFZTLiBIT0xJREFZICMjIyMjCm5oXzIwMTIgPC0gZGF0YS5mcmFtZShzdWJzZXQobm9faG9sX2F2Z3MsIHNlbGVjdCA9IC1jKDI6MjIpKSkKCiMgRmVicnVhcnkgMjAxMQpuaF9GZWIyMDEyIDwtIGhvbGlkYXkgJT4lIAogICAgICAgICAgICAgICAgICAgIGZpbHRlcihIb2xpZGF5X0ZsYWcgPT0gMCAmIFllYXIgPT0gJzIwMTInICYgTW9udGggPT0gJzAyJykgJT4lCiAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoU3RvcmUpICU+JQogICAgICAgICAgICAgICAgICAgIHN1bW1hcmlzZShNZWFuID0gbWVhbihXZWVrbHlfU2FsZXMpKQoKIyBTZXB0ZW1iZXIgMjAxMgpuaF9TZXB0MjAxMiA8LSBob2xpZGF5ICU+JSAKICAgICAgICAgICAgICAgICAgICBmaWx0ZXIoSG9saWRheV9GbGFnID09IDAgJiBZZWFyID09ICcyMDEyJyAmIE1vbnRoID09ICcwOScpICU+JQogICAgICAgICAgICAgICAgICAgIGdyb3VwX2J5KFN0b3JlKSAlPiUKICAgICAgICAgICAgICAgICAgICBzdW1tYXJpc2UoTWVhbiA9IG1lYW4oV2Vla2x5X1NhbGVzKSkKCiMgQmluZCBjb2x1bW5zCm5oXzIwMTIgPC0gY2JpbmQobmhfMjAxMiwgc3VwZXJib3dsXzIwMTIkV2Vla2x5X1NhbGVzLCBuaF9GZWIyMDEyJE1lYW4sIGxhYm9yZGF5XzIwMTIkV2Vla2x5X1NhbGVzLCBuaF9TZXB0MjAxMiRNZWFuKQoKIyBBZGQgY29tcGFyaXNvbiBjb2x1bW5zCm5oXzIwMTIkc3VwZXJib3dsX2NvbXBhcmlzb24gPC0gaWZlbHNlKHN1cGVyYm93bF8yMDEyJFdlZWtseV9TYWxlcyA+IG5oX0ZlYjIwMTIkTWVhbiwgImdyZWF0ZXIiLCAibm90IGdyZWF0ZXIiKSAjIFN1cGVyYm93bApuaF8yMDEyJGxhYm9yZGF5X2NvbXBhcmlzb24gPC0gaWZlbHNlKGxhYm9yZGF5XzIwMTIkV2Vla2x5X1NhbGVzID4gbmhfU2VwdDIwMTIkTWVhbiwgImdyZWF0ZXIiLCAibm90IGdyZWF0ZXIiKSAjIExhYm9yIERheQoKIyBWaWV3IGRhdGFmcmFtZQpWaWV3KG5oXzIwMTIpCgpgYGAKIyMjIyBWaXN1YWxpemF0aW9ucyBvZiBDb21wYXJpc29uczogU3VwZXJib3dsCgpgYGB7cn0KIyMgMjAxMCBTdXBlcmJvd2wgQ29tcGFyaXNvbgoKIyBDb252ZXJ0IGRhdGEgZnJhbWUgaW50byBsb25nIGZvcm1hdApuaF8yMDEwc2IubG9uZyA8LSBuaF8yMDEwICU+JQogIHNlbGVjdCgnU3RvcmUnLCdzdXBlcmJvd2xfMjAxMCRXZWVrbHlfU2FsZXMnLCAnbmhfRmViMjAxMCRNZWFuJykgJT4lCiAgcGl2b3RfbG9uZ2VyKC1TdG9yZSwgbmFtZXNfdG8gPSAnc2J2YXJzMjAxMCcsIHZhbHVlc190byA9ICdzYnZhbHMyMDEwJykKCiMgQ3JlYXRlIHRoZSB2aXN1YWxpemF0aW9uCnNiMjAxMF9ncmlkcyA8LSBnZ3Bsb3QobmhfMjAxMHNiLmxvbmcsIGFlcyh4ID0gZmFjdG9yKFN0b3JlKSwgeT0gc2J2YWxzMjAxMCwgY29sb3IgPSBzYnZhcnMyMDEwLCBncm91cCA9IDEpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGxhYmVscyA9IGMoJ1N1cGVyYm93bCAyMDEwJywgJ1JlZ3VsYXIgTW9udGhseSBBdmVyYWdlLicpKSArCiAgbGFicyh0aXRsZSA9ICdGZWJydWFyeSBBdmVyYWdlIFNhbGVzIHZzLiBTdXBlcmJvd2w6IDIwMTAnLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCgpzYjIwMTBfZ3JpZHMKYGBgCiMjIyMjIEZlYi4gQXZlcmFnZSBTYWxlcyB2cy4gU3VwZXJib3dsIDIwMTAKYGBge3J9CiMgQ3JlYXRlIHRoZSBndCB0YWJsZSBvYmplY3QKZmViX2F2Z3MgPC0gZ3QodGliYmxlKG5oXzIwMTAgJT4lIGZpbHRlcihuaF8yMDEwJGBuaF9GZWIyMDEwJE1lYW5gIDwgbmhfMjAxMCRgc3VwZXJib3dsXzIwMTAkV2Vla2x5X1NhbGVzYCkpKQoKCgojIEZvcm1hdApmZWJfYXZncyA8LSBmZWJfYXZncyAlPiUgdGFiX2hlYWRlcih0aXRsZSA9ICdTdG9yZXMgd2l0aCBIaWdoZXIgU3VwZXJib3dsIFNhbGVzIHZzIEZlYnJ1YXJ5IE5vbi1Ib2xpZGF5IEF2ZXJhZ2UgU2FsZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gJzIwMTAnKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHNfaGlkZShjb2x1bW5zID0gYyg0OjEzKSkgCgojIERpc3BsYXkKZmViX2F2Z3MKYGBgCgoKCmBgYHtyfQojIyAyMDExIFN1cGVyYm93bCBDb21wYXJpc29uCgojIENvbnZlcnQgZGF0YSBmcmFtZSBpbnRvIGxvbmcgZm9ybWF0Cm5oXzIwMTFzYi5sb25nIDwtIG5oXzIwMTEgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ3N1cGVyYm93bF8yMDExJFdlZWtseV9TYWxlcycsICduaF9GZWIyMDExJE1lYW4nKSAlPiUKICBwaXZvdF9sb25nZXIoLVN0b3JlLCBuYW1lc190byA9ICdzYnZhcnMyMDExJywgdmFsdWVzX3RvID0gJ3NidmFsczIwMTEnKQoKIyBDcmVhdGUgdGhlIHZpc3VhbGl6YXRpb24Kc2IyMDExX2dyaWRzIDwtIGdncGxvdChuaF8yMDExc2IubG9uZywgYWVzKHggPSBmYWN0b3IoU3RvcmUpLCB5PSBzYnZhbHMyMDExLCBjb2xvciA9IHNidmFyczIwMTEsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnU3VwZXJib3dsIDIwMTEnLCAnUmVndWxhciBNb250aGx5IEF2ZXJhZ2UuJykpICsKICBsYWJzKHRpdGxlID0gJ0ZlYnJ1YXJ5IEF2ZXJhZ2UgU2FsZXMgdnMuIFN1cGVyYm93bDogMjAxMScsIHggPSAnU3RvcmUnLCB5ID0gJ1NhbGVzIEFtb3VudCcsIGNvbG9yID0gJ1NhbGVzIEV2ZW50JykKCnNiMjAxMV9ncmlkcwpgYGAKIyMjIyMgRmViLiBBdmVyYWdlIFNhbGVzIHZzLiBTdXBlcmJvd2wgMjAxMQpgYGB7cn0KIyBDcmVhdGUgdGhlIGd0IHRhYmxlIG9iamVjdApmZWIyMDExX2F2Z3MgPC0gZ3QodGliYmxlKG5oXzIwMTEgJT4lIGZpbHRlcihuaF8yMDExJGBuaF9GZWIyMDExJE1lYW5gIDwgbmhfMjAxMSRgc3VwZXJib3dsXzIwMTEkV2Vla2x5X1NhbGVzYCkpKQoKCgojIEZvcm1hdApmZWIyMDExX2F2Z3MgPC0gZmViMjAxMV9hdmdzICU+JSB0YWJfaGVhZGVyKHRpdGxlID0gJ1N0b3JlcyB3aXRoIEhpZ2hlciBTdXBlcmJvd2wgU2FsZXMgdnMgRmVicnVhcnkgTm9uLUhvbGlkYXkgQXZlcmFnZSBTYWxlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAnMjAxMScpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgY29sc19oaWRlKGNvbHVtbnMgPSBjKDQ6MTMpKSAKCiMgRGlzcGxheQpmZWIyMDExX2F2Z3MKYGBgCgpgYGB7cn0KIyMgMjAxMiBTdXBlcmJvd2wgQ29tcGFyaXNvbgoKIyBDb252ZXJ0IGRhdGEgZnJhbWUgaW50byBsb25nIGZvcm1hdApuaF8yMDEyc2IubG9uZyA8LSBuaF8yMDEyICU+JQogIHNlbGVjdCgnU3RvcmUnLCdzdXBlcmJvd2xfMjAxMiRXZWVrbHlfU2FsZXMnLCAnbmhfRmViMjAxMiRNZWFuJykgJT4lCiAgcGl2b3RfbG9uZ2VyKC1TdG9yZSwgbmFtZXNfdG8gPSAnc2J2YXJzMjAxMicsIHZhbHVlc190byA9ICdzYnZhbHMyMDEyJykKCiMgQ3JlYXRlIHRoZSB2aXN1YWxpemF0aW9uCnNiMjAxMl9ncmlkcyA8LSBnZ3Bsb3QobmhfMjAxMnNiLmxvbmcsIGFlcyh4ID0gZmFjdG9yKFN0b3JlKSwgeT0gc2J2YWxzMjAxMiwgY29sb3IgPSBzYnZhcnMyMDEyLCBncm91cCA9IDEpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGxhYmVscyA9IGMoJ1N1cGVyYm93bCAyMDEyJywgJ1JlZ3VsYXIgTW9udGhseSBBdmVyYWdlLicpKSArCiAgbGFicyh0aXRsZSA9ICdGZWJydWFyeSBBdmVyYWdlIFNhbGVzIHZzLiBTdXBlcmJvd2w6IDIwMTInLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCgpzYjIwMTJfZ3JpZHMKYGBgCiMjIyMjIEZlYi4gQXZlcmFnZSBTYWxlcyB2cy4gU3VwZXJib3dsIDIwMTIKYGBge3J9CiMgQ3JlYXRlIHRoZSBndCB0YWJsZSBvYmplY3QKZmViMjAxMl9hdmdzIDwtIGd0KHRpYmJsZShuaF8yMDEyICU+JSBmaWx0ZXIobmhfMjAxMiRgbmhfRmViMjAxMiRNZWFuYCA8IG5oXzIwMTIkYHN1cGVyYm93bF8yMDEyJFdlZWtseV9TYWxlc2ApKSkKCgoKIyBGb3JtYXQKZmViMjAxMl9hdmdzIDwtIGZlYjIwMTJfYXZncyAlPiUgdGFiX2hlYWRlcih0aXRsZSA9ICdTdG9yZXMgd2l0aCBIaWdoZXIgU3VwZXJib3dsIFNhbGVzIHZzIEZlYnJ1YXJ5IE5vbi1Ib2xpZGF5IEF2ZXJhZ2UgU2FsZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gJzIwMTInKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHNfaGlkZShjb2x1bW5zID0gYyg0OjcpKSAKCiMgRGlzcGxheQpmZWIyMDEyX2F2Z3MKYGBgCgpBbGwgbm9uLWhvbGlkYXkgc2Vhc29ucyB2cy4gYWxsIHN1cGVyYm93bCB5ZWFycwpgYGB7cn0KIyBDb252ZXJ0IGRhdGFmcmFtZSBpbnRvIGxvbmcgZm9ybWF0Cm5vX2hvbF9hdmdzLmxvbmcgPC0gbm9faG9sX2F2Z3MgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ1dlZWtseV9BdmdfTm9uSG9saWRheScsICdTdXBlcmJvd2xfMjAxMCcsICdTdXBlcmJvd2xfMjAxMScsICdTdXBlcmJvd2xfMjAxMicpICU+JQogIHBpdm90X2xvbmdlcigtU3RvcmUsIG5hbWVzX3RvID0gJ3NhbGVzX3ZhcnMnLCB2YWx1ZXNfdG8gPSAnc2FsZXNfdmFscycpCgojIENyZWF0ZSB0aGUgdmlzdWFsaXphdGlvbgpzYl9ncmlkcyA8LSBnZ3Bsb3Qobm9faG9sX2F2Z3MubG9uZywgYWVzKHggPSBmYWN0b3IoU3RvcmUpLCB5PSBzYWxlc192YWxzLCBjb2xvciA9IHNhbGVzX3ZhcnMsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnU3VwZXJib3dsIDIwMTAnLCAnU3VwZXJib3dsIDIwMTEnLCAnU3VwZXJib3dsIDIwMTInLCAnTm9uLUhvbGlkYXkgV2Vla2x5IEF2Zy4nKSkgKwogIGxhYnModGl0bGUgPSAnV2Vla2x5IEF2ZXJhZ2UgU2FsZXMgdnMuIFN1cGVyYm93bDogMjAxMCB0aHJ1IDIwMTInLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCgpzYl9ncmlkcwpgYGAKIyMjIyBWaXN1YWxpemF0aW9uIG9mIENvbXBhcmlzb25zOiBMYWJvciBEYXkKYGBge3J9CiMjIDIwMTAgbGFib3JkYXkgQ29tcGFyaXNvbgoKIyBDb252ZXJ0IGRhdGEgZnJhbWUgaW50byBsb25nIGZvcm1hdApuaF8yMDEwbGQubG9uZyA8LSBuaF8yMDEwICU+JQogIHNlbGVjdCgnU3RvcmUnLCdsYWJvcmRheV8yMDEwJFdlZWtseV9TYWxlcycsICduaF9TZXB0MjAxMCRNZWFuJykgJT4lCiAgcGl2b3RfbG9uZ2VyKC1TdG9yZSwgbmFtZXNfdG8gPSAnbGR2YXJzMjAxMCcsIHZhbHVlc190byA9ICdsZHZhbHMyMDEwJykKCiMgQ3JlYXRlIHRoZSB2aXN1YWxpemF0aW9uCmxkMjAxMF9ncmlkcyA8LSBnZ3Bsb3QobmhfMjAxMGxkLmxvbmcsIGFlcyh4ID0gZmFjdG9yKFN0b3JlKSwgeT0gbGR2YWxzMjAxMCwgY29sb3IgPSBsZHZhcnMyMDEwLCBncm91cCA9IDEpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGxhYmVscyA9IGMoJ0xhYm9yIERheSAyMDEwJywgJ1JlZ3VsYXIgTW9udGhseSBBdmVyYWdlLicpKSArCiAgbGFicyh0aXRsZSA9ICdTZXB0ZW1iZXIgQXZlcmFnZSBTYWxlcyB2cy4gTGFib3IgRGF5OiAyMDEwJywgeCA9ICdTdG9yZScsIHkgPSAnU2FsZXMgQW1vdW50JywgY29sb3IgPSAnU2FsZXMgRXZlbnQnKQoKbGQyMDEwX2dyaWRzCmBgYAojIyMjIyBTZXB0LiBBdmVyYWdlIFNhbGVzIHZzLiBMYWJvciBEYXkgMjAxMApgYGB7cn0KIyBDcmVhdGUgdGhlIGd0IHRhYmxlIG9iamVjdApzZXB0X2F2Z3MgPC0gZ3QodGliYmxlKG5oXzIwMTAgJT4lIGZpbHRlcihuaF8yMDEwJGBuaF9TZXB0MjAxMCRNZWFuYCA8IG5oXzIwMTAkYGxhYm9yZGF5XzIwMTAkV2Vla2x5X1NhbGVzYCkpKQoKCgojIEZvcm1hdApzZXB0X2F2Z3MgPC0gc2VwdF9hdmdzICU+JSB0YWJfaGVhZGVyKHRpdGxlID0gJ1N0b3JlcyB3aXRoIEhpZ2hlciBMYWJvciBEYXkgU2FsZXMgdnMgU2VwdCBOb24tSG9saWRheSBBdmVyYWdlIFNhbGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICcyMDEwJykgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xzX2hpZGUoY29sdW1ucyA9IGMoMjozLDY6MTMpKSAKCiMgRGlzcGxheQpzZXB0X2F2Z3MKYGBgCgpgYGB7cn0KIyMgMjAxMSBsYWJvcmRheSBDb21wYXJpc29uCgojIENvbnZlcnQgZGF0YSBmcmFtZSBpbnRvIGxvbmcgZm9ybWF0Cm5oXzIwMTFsZC5sb25nIDwtIG5oXzIwMTEgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ2xhYm9yZGF5XzIwMTEkV2Vla2x5X1NhbGVzJywgJ25oX1NlcHQyMDExJE1lYW4nKSAlPiUKICBwaXZvdF9sb25nZXIoLVN0b3JlLCBuYW1lc190byA9ICdsZHZhcnMyMDExJywgdmFsdWVzX3RvID0gJ2xkdmFsczIwMTEnKQoKIyBDcmVhdGUgdGhlIHZpc3VhbGl6YXRpb24KbGQyMDExX2dyaWRzIDwtIGdncGxvdChuaF8yMDExbGQubG9uZywgYWVzKHggPSBmYWN0b3IoU3RvcmUpLCB5PSBsZHZhbHMyMDExLCBjb2xvciA9IGxkdmFyczIwMTEsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnTGFib3IgRGF5IDIwMTEnLCAnUmVndWxhciBNb250aGx5IEF2ZXJhZ2UuJykpICsKICBsYWJzKHRpdGxlID0gJ1NlcHRlbWJlciBBdmVyYWdlIFNhbGVzIHZzLiBMYWJvciBEYXk6IDIwMTEnLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCgpsZDIwMTFfZ3JpZHMKYGBgCiMjIyMjIFNlcHQuIEF2ZXJhZ2UgU2FsZXMgdnMuIExhYm9yIERheSAyMDExCmBgYHtyfQojIENyZWF0ZSB0aGUgZ3QgdGFibGUgb2JqZWN0CnNlcHQyMDExX2F2Z3MgPC0gZ3QodGliYmxlKG5oXzIwMTEgJT4lIGZpbHRlcihuaF8yMDExJGBuaF9TZXB0MjAxMSRNZWFuYCA8IG5oXzIwMTEkYGxhYm9yZGF5XzIwMTEkV2Vla2x5X1NhbGVzYCkpKQoKCgojIEZvcm1hdApzZXB0MjAxMV9hdmdzIDwtIHNlcHQyMDExX2F2Z3MgJT4lIHRhYl9oZWFkZXIodGl0bGUgPSAnU3RvcmVzIHdpdGggSGlnaGVyIExhYm9yIERheSBTYWxlcyB2cyBTZXB0IE5vbi1Ib2xpZGF5IEF2ZXJhZ2UgU2FsZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gJzIwMTEnKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHNfaGlkZShjb2x1bW5zID0gYygyOjMsNjoxMykpIAoKIyBEaXNwbGF5CnNlcHQyMDExX2F2Z3MKYGBgCgpgYGB7cn0KIyMgMjAxMiBsYWJvcmRheSBDb21wYXJpc29uCgojIENvbnZlcnQgZGF0YSBmcmFtZSBpbnRvIGxvbmcgZm9ybWF0Cm5oXzIwMTJsZC5sb25nIDwtIG5oXzIwMTIgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ2xhYm9yZGF5XzIwMTIkV2Vla2x5X1NhbGVzJywgJ25oX1NlcHQyMDEyJE1lYW4nKSAlPiUKICBwaXZvdF9sb25nZXIoLVN0b3JlLCBuYW1lc190byA9ICdsZHZhcnMyMDEyJywgdmFsdWVzX3RvID0gJ2xkdmFsczIwMTInKQoKIyBDcmVhdGUgdGhlIHZpc3VhbGl6YXRpb24KbGQyMDEyX2dyaWRzIDwtIGdncGxvdChuaF8yMDEybGQubG9uZywgYWVzKHggPSBmYWN0b3IoU3RvcmUpLCB5PSBsZHZhbHMyMDEyLCBjb2xvciA9IGxkdmFyczIwMTIsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnTGFib3IgRGF5IDIwMTInLCAnUmVndWxhciBNb250aGx5IEF2ZXJhZ2UuJykpICsKICBsYWJzKHRpdGxlID0gJ1NlcHRlbWJlciBBdmVyYWdlIFNhbGVzIHZzLiBMYWJvciBEYXk6IDIwMTInLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCgpsZDIwMTJfZ3JpZHMKYGBgCiMjIyMjIFNlcHQuIEF2ZXJhZ2UgU2FsZXMgdnMuIExhYm9yIERheSAyMDEyCmBgYHtyfQojIENyZWF0ZSB0aGUgZ3QgdGFibGUgb2JqZWN0CnNlcHQyMDEyX2F2Z3MgPC0gZ3QodGliYmxlKG5oXzIwMTIgJT4lIGZpbHRlcihuaF8yMDEyJGBuaF9TZXB0MjAxMiRNZWFuYCA8IG5oXzIwMTIkYGxhYm9yZGF5XzIwMTIkV2Vla2x5X1NhbGVzYCkpKQoKCgojIEZvcm1hdApzZXB0MjAxMl9hdmdzIDwtIHNlcHQyMDEyX2F2Z3MgJT4lIHRhYl9oZWFkZXIodGl0bGUgPSAnU3RvcmVzIHdpdGggSGlnaGVyIExhYm9yIERheSBTYWxlcyB2cyBTZXB0IE5vbi1Ib2xpZGF5IEF2ZXJhZ2UgU2FsZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gJzIwMTInKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHNfaGlkZShjb2x1bW5zID0gYygyOjMsNjo3KSkgCgojIERpc3BsYXkKc2VwdDIwMTJfYXZncwpgYGAKCgpBbGwgbm9uLWhvbGlkYXkgdnMuIGFsbCBMYWJvciBEYXkgeWVhcnMKYGBge3J9CiMgQ29udmVydCBkYXRhZnJhbWUgaW50byBsb25nIGZvcm1hdApub19ob2xfYXZnczEubG9uZyA8LSBub19ob2xfYXZncyAlPiUKICBzZWxlY3QoJ1N0b3JlJywnV2Vla2x5X0F2Z19Ob25Ib2xpZGF5JywgJ0xhYm9yRGF5XzIwMTAnLCAnTGFib3JEYXlfMjAxMScsICdMYWJvckRheV8yMDEyJykgJT4lCiAgcGl2b3RfbG9uZ2VyKC1TdG9yZSwgbmFtZXNfdG8gPSAnbGFib3JfdmFycycsIHZhbHVlc190byA9ICdsYWJvcl92YWxzJykKCiMgQ3JlYXRlIHRoZSB2aXN1YWxpemF0aW9uCmxhYm9yX2dyaWRzIDwtIGdncGxvdChub19ob2xfYXZnczEubG9uZywgYWVzKHggPSBmYWN0b3IoU3RvcmUpLCB5PSBsYWJvcl92YWxzLCBjb2xvciA9IGxhYm9yX3ZhcnMsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnTGFib3IgRGF5IDIwMTAnLCAnTGFib3IgRGF5IDIwMTEnLCAnTGFib3IgRGF5IDIwMTInLCAnTm9uLUhvbGlkYXkgV2Vla2x5IEF2Zy4nKSkgKwogIGxhYnModGl0bGUgPSAnV2Vla2x5IEF2ZXJhZ2UgU2FsZXMgdnMuIExhYm9yIERheTogMjAxMCB0aHJ1IDIwMTInLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCiAgCmxhYm9yX2dyaWRzCmBgYAojIyMjIFZpc3VhbGl6YXRpb24gb2YgQ29tcGFyaXNvbnM6IFRoYW5rc2dpdmluZwpgYGB7cn0KIyMgMjAxMCBUaGFua3NnaXZpbmcgQ29tcGFyaXNvbgoKIyBDb252ZXJ0IGRhdGEgZnJhbWUgaW50byBsb25nIGZvcm1hdApuaF8yMDEwdC5sb25nIDwtIG5oXzIwMTAgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ3RoYW5rc2dpdmluZ18yMDEwJFdlZWtseV9TYWxlcycsICduaF9Ob3YyMDEwJE1lYW4nKSAlPiUKICBwaXZvdF9sb25nZXIoLVN0b3JlLCBuYW1lc190byA9ICd0dmFyczIwMTAnLCB2YWx1ZXNfdG8gPSAndHZhbHMyMDEwJykKCiMgQ3JlYXRlIHRoZSB2aXN1YWxpemF0aW9uCnQyMDEwX2dyaWRzIDwtIGdncGxvdChuaF8yMDEwdC5sb25nLCBhZXMoeCA9IGZhY3RvcihTdG9yZSksIHk9IHR2YWxzMjAxMCwgY29sb3IgPSB0dmFyczIwMTAsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnVGhhbmtzZ2l2aW5nIDIwMTAnLCAnUmVndWxhciBNb250aGx5IEF2ZXJhZ2UuJykpICsKICBsYWJzKHRpdGxlID0gJ05vdmVtYmVyIEF2ZXJhZ2UgU2FsZXMgdnMuIFRoYW5rc2dpdmluZzogMjAxMCcsIHggPSAnU3RvcmUnLCB5ID0gJ1NhbGVzIEFtb3VudCcsIGNvbG9yID0gJ1NhbGVzIEV2ZW50JykKCnQyMDEwX2dyaWRzCmBgYAojIyMjIyBOb3YuIEF2ZXJhZ2UgU2FsZXMgdnMuIFRoYW5rc2dpdmluZyAyMDEwCmBgYHtyfQojIENyZWF0ZSB0aGUgZ3QgdGFibGUgb2JqZWN0Cm5vdl9hdmdzIDwtIGd0KHRpYmJsZShuaF8yMDEwICU+JSBmaWx0ZXIobmhfMjAxMCRgbmhfTm92MjAxMCRNZWFuYCA8IG5oXzIwMTAkYHRoYW5rc2dpdmluZ18yMDEwJFdlZWtseV9TYWxlc2ApKSkKCgoKIyBGb3JtYXQKbm92X2F2Z3MgPC0gbm92X2F2Z3MgJT4lIHRhYl9oZWFkZXIodGl0bGUgPSAnU3RvcmVzIHdpdGggSGlnaGVyIFRoYW5rc2dpdmluZyBTYWxlcyB2cyBOb3YgTm9uLUhvbGlkYXkgQXZlcmFnZSBTYWxlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAnMjAxMCcpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgY29sc19oaWRlKGNvbHVtbnMgPSBjKDI6NSw4OjEzKSkgCgojIERpc3BsYXkKbm92X2F2Z3MKYGBgCgpgYGB7cn0KIyMgMjAxMSBUaGFua3NnaXZpbmcgQ29tcGFyaXNvbgoKIyBDb252ZXJ0IGRhdGEgZnJhbWUgaW50byBsb25nIGZvcm1hdApuaF8yMDExdC5sb25nIDwtIG5oXzIwMTEgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ3RoYW5rc2dpdmluZ18yMDExJFdlZWtseV9TYWxlcycsICduaF9Ob3YyMDExJE1lYW4nKSAlPiUKICBwaXZvdF9sb25nZXIoLVN0b3JlLCBuYW1lc190byA9ICd0dmFyczIwMTEnLCB2YWx1ZXNfdG8gPSAndHZhbHMyMDExJykKCiMgQ3JlYXRlIHRoZSB2aXN1YWxpemF0aW9uCnQyMDExX2dyaWRzIDwtIGdncGxvdChuaF8yMDExdC5sb25nLCBhZXMoeCA9IGZhY3RvcihTdG9yZSksIHk9IHR2YWxzMjAxMSwgY29sb3IgPSB0dmFyczIwMTEsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnVGhhbmtzZ2l2aW5nIDIwMTEnLCAnUmVndWxhciBNb250aGx5IEF2ZXJhZ2UuJykpICsKICBsYWJzKHRpdGxlID0gJ05vdmVtYmVyIEF2ZXJhZ2UgU2FsZXMgdnMuIFRoYW5rc2dpdmluZzogMjAxMScsIHggPSAnU3RvcmUnLCB5ID0gJ1NhbGVzIEFtb3VudCcsIGNvbG9yID0gJ1NhbGVzIEV2ZW50JykKCnQyMDExX2dyaWRzCmBgYAojIyMjIyBOb3YuIEF2ZXJhZ2UgU2FsZXMgdnMuIFRoYW5rc2dpdmluZyAyMDExCmBgYHtyfQojIENyZWF0ZSB0aGUgZ3QgdGFibGUgb2JqZWN0Cm5vdjIwMTFfYXZncyA8LSBndCh0aWJibGUobmhfMjAxMSAlPiUgZmlsdGVyKG5oXzIwMTEkYG5oX05vdjIwMTEkTWVhbmAgPCBuaF8yMDExJGB0aGFua3NnaXZpbmdfMjAxMSRXZWVrbHlfU2FsZXNgKSkpCgoKCiMgRm9ybWF0Cm5vdjIwMTFfYXZncyA8LSBub3YyMDExX2F2Z3MgJT4lIHRhYl9oZWFkZXIodGl0bGUgPSAnU3RvcmVzIHdpdGggSGlnaGVyIFRoYW5rc2dpdmluZyBTYWxlcyB2cyBOb3YgTm9uLUhvbGlkYXkgQXZlcmFnZSBTYWxlJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc3VidGl0bGUgPSAnMjAxMScpICU+JQogICAgICAgICAgICAgICAgICAgICAgICAgY29sc19oaWRlKGNvbHVtbnMgPSBjKDI6NSw4OjEzKSkgCgojIERpc3BsYXkKbm92MjAxMV9hdmdzCmBgYAoKQWxsIG5vbi1ob2xpZGF5IHZzLiBhbGwgVGhhbmtzZ2l2aW5nIHllYXJzCmBgYHtyfQojIENvbnZlcnQgZGF0YWZyYW1lIGludG8gbG9uZyBmb3JtYXQKbm9faG9sX2F2Z3MyLmxvbmcgPC0gbm9faG9sX2F2Z3MgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ1dlZWtseV9BdmdfTm9uSG9saWRheScsICdUaGFua3NnaXZpbmdfMjAxMCcsICdUaGFua3NnaXZpbmdfMjAxMScpICU+JQogIHBpdm90X2xvbmdlcigtU3RvcmUsIG5hbWVzX3RvID0gJ3R1cmtleV92YXJzJywgdmFsdWVzX3RvID0gJ3R1cmtleV92YWxzJykKCiMgQ3JlYXRlIHRoZSB2aXN1YWxpemF0aW9uCnR1cmtleV9ncmlkcyA8LSBnZ3Bsb3Qobm9faG9sX2F2Z3MyLmxvbmcsIGFlcyh4ID0gZmFjdG9yKFN0b3JlKSwgeT0gdHVya2V5X3ZhbHMsIGNvbG9yID0gdHVya2V5X3ZhcnMsIGdyb3VwID0gMSkpICsKICBnZW9tX2xpbmUoKSArCiAgZ2VvbV9wb2ludCgpICsKICBzY2FsZV9jb2xvcl92aXJpZGlzX2QobGFiZWxzID0gYygnVGhhbmtzZ2l2aW5nIDIwMTAnLCAnVGhhbmtzZ2l2aW5nIDIwMTEnLCAnTm9uLUhvbGlkYXkgV2Vla2x5IEF2Zy4nKSkgKwogIGxhYnModGl0bGUgPSAnV2Vla2x5IEF2ZXJhZ2UgU2FsZXMgdnMuIFRoYW5rc2dpdmluZzogMjAxMCB0aHJ1IDIwMTEnLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCiAgCnR1cmtleV9ncmlkcwpgYGAKIyMjIyBWaXN1YWxpemF0aW9uIG9mIENvbXBhcmlzb25zOiBDaHJpc3RtYXMKCmBgYHtyfQojIyAyMDEwIENocmlzdG1hcyBDb21wYXJpc29uCgojIENvbnZlcnQgZGF0YSBmcmFtZSBpbnRvIGxvbmcgZm9ybWF0Cm5oXzIwMTBjLmxvbmcgPC0gbmhfMjAxMCAlPiUKICBzZWxlY3QoJ1N0b3JlJywnY2hyaXN0bWFzXzIwMTAkV2Vla2x5X1NhbGVzJywgJ25oX0RlYzIwMTAkTWVhbicpICU+JQogIHBpdm90X2xvbmdlcigtU3RvcmUsIG5hbWVzX3RvID0gJ2N2YXJzMjAxMCcsIHZhbHVlc190byA9ICdjdmFsczIwMTAnKQoKIyBDcmVhdGUgdGhlIHZpc3VhbGl6YXRpb24KYzIwMTBfZ3JpZHMgPC0gZ2dwbG90KG5oXzIwMTBjLmxvbmcsIGFlcyh4ID0gZmFjdG9yKFN0b3JlKSwgeT0gY3ZhbHMyMDEwLCBjb2xvciA9IGN2YXJzMjAxMCwgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChsYWJlbHMgPSBjKCdDaHJpc3RtYXMgMjAxMCcsICdSZWd1bGFyIE1vbnRobHkgQXZlcmFnZS4nKSkgKwogIGxhYnModGl0bGUgPSAnRGVjZW1iZXIgQXZlcmFnZSBTYWxlcyB2cy4gQ2hyaXN0bWFzOiAyMDEwJywgeCA9ICdTdG9yZScsIHkgPSAnU2FsZXMgQW1vdW50JywgY29sb3IgPSAnU2FsZXMgRXZlbnQnKQoKYzIwMTBfZ3JpZHMKYGBgCiMjIyMjIERlYy4gQXZlcmFnZSBTYWxlcyB2cy4gQ2hyaXN0bWFzIDIwMTAKYGBge3J9CiMgQ3JlYXRlIHRoZSBndCB0YWJsZSBvYmplY3QKZGVjX2F2Z3MgPC0gZ3QodGliYmxlKG5oXzIwMTAgJT4lIGZpbHRlcihuaF8yMDEwJGBuaF9EZWMyMDEwJE1lYW5gIDwgbmhfMjAxMCRgY2hyaXN0bWFzXzIwMTAkV2Vla2x5X1NhbGVzYCkpKQoKCgojIEZvcm1hdApkZWNfYXZncyA8LSBkZWNfYXZncyAlPiUgdGFiX2hlYWRlcih0aXRsZSA9ICdTdG9yZXMgd2l0aCBIaWdoZXIgQ2hyaXN0bWFzIFNhbGVzIHZzIE5vdiBOb24tSG9saWRheSBBdmVyYWdlIFNhbGUnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWJ0aXRsZSA9ICcyMDEwJykgJT4lCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xzX2hpZGUoY29sdW1ucyA9IGMoMjo3LDEwOjEzKSkgCgojIERpc3BsYXkKZGVjX2F2Z3MKYGBgCgoKYGBge3J9CiMjIDIwMTEgQ2hyaXN0bWFzIENvbXBhcmlzb24KCiMgQ29udmVydCBkYXRhIGZyYW1lIGludG8gbG9uZyBmb3JtYXQKbmhfMjAxMWMubG9uZyA8LSBuaF8yMDExICU+JQogIHNlbGVjdCgnU3RvcmUnLCdjaHJpc3RtYXNfMjAxMSRXZWVrbHlfU2FsZXMnLCAnbmhfRGVjMjAxMSRNZWFuJykgJT4lCiAgcGl2b3RfbG9uZ2VyKC1TdG9yZSwgbmFtZXNfdG8gPSAnY3ZhcnMyMDExJywgdmFsdWVzX3RvID0gJ2N2YWxzMjAxMScpCgojIENyZWF0ZSB0aGUgdmlzdWFsaXphdGlvbgpjMjAxMV9ncmlkcyA8LSBnZ3Bsb3QobmhfMjAxMWMubG9uZywgYWVzKHggPSBmYWN0b3IoU3RvcmUpLCB5PSBjdmFsczIwMTEsIGNvbG9yID0gY3ZhcnMyMDExLCBncm91cCA9IDEpKSArCiAgZ2VvbV9saW5lKCkgKwogIGdlb21fcG9pbnQoKSArCiAgc2NhbGVfY29sb3JfdmlyaWRpc19kKGxhYmVscyA9IGMoJ0NocmlzdG1hcyAyMDExJywgJ1JlZ3VsYXIgTW9udGhseSBBdmVyYWdlLicpKSArCiAgbGFicyh0aXRsZSA9ICdEZWNlbWJlciBBdmVyYWdlIFNhbGVzIHZzLiBDaHJpc3RtYXM6IDIwMTEnLCB4ID0gJ1N0b3JlJywgeSA9ICdTYWxlcyBBbW91bnQnLCBjb2xvciA9ICdTYWxlcyBFdmVudCcpCgpjMjAxMV9ncmlkcwpgYGAKIyMjIyMgRGVjLiBBdmVyYWdlIFNhbGVzIHZzLiBDaHJpc3RtYXMgMjAxMQpgYGB7cn0KIyBDcmVhdGUgdGhlIGd0IHRhYmxlIG9iamVjdApkZWMyMDExX2F2Z3MgPC0gZ3QodGliYmxlKG5oXzIwMTEgJT4lIGZpbHRlcihuaF8yMDExJGBuaF9EZWMyMDExJE1lYW5gIDwgbmhfMjAxMSRgY2hyaXN0bWFzXzIwMTEkV2Vla2x5X1NhbGVzYCkpKQoKCgojIEZvcm1hdApkZWMyMDExX2F2Z3MgPC0gZGVjMjAxMV9hdmdzICU+JSB0YWJfaGVhZGVyKHRpdGxlID0gJ1N0b3JlcyB3aXRoIEhpZ2hlciBDaHJpc3RtYXMgU2FsZXMgdnMgTm92IE5vbi1Ib2xpZGF5IEF2ZXJhZ2UgU2FsZScsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHN1YnRpdGxlID0gJzIwMTEnKSAlPiUKICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHNfaGlkZShjb2x1bW5zID0gYygyOjcsMTA6MTMpKSAKCiMgRGlzcGxheQpkZWMyMDExX2F2Z3MKYGBgCgoKQWxsIG5vbi1ob2xpZGF5IHZzLiBhbGwgQ2hyaXN0bWFzIHllYXJzCmBgYHtyfQojIENvbnZlcnQgZGF0YWZyYW1lIGludG8gbG9uZyBmb3JtYXQKbm9faG9sX2F2Z3MzLmxvbmcgPC0gbm9faG9sX2F2Z3MgJT4lCiAgc2VsZWN0KCdTdG9yZScsJ1dlZWtseV9BdmdfTm9uSG9saWRheScsICdDaHJpc3RtYXNfMjAxMCcsICdDaHJpc3RtYXNfMjAxMScpICU+JQogIHBpdm90X2xvbmdlcigtU3RvcmUsIG5hbWVzX3RvID0gJ3htYXNfdmFycycsIHZhbHVlc190byA9ICd4bWFzX3ZhbHMnKQoKIyBDcmVhdGUgdGhlIHZpc3VhbGl6YXRpb24KeG1hc19ncmlkcyA8LSBnZ3Bsb3Qobm9faG9sX2F2Z3MzLmxvbmcsIGFlcyh4ID0gZmFjdG9yKFN0b3JlKSwgeT0geG1hc192YWxzLCBjb2xvciA9IHhtYXNfdmFycywgZ3JvdXAgPSAxKSkgKwogIGdlb21fbGluZSgpICsKICBnZW9tX3BvaW50KCkgKwogIHNjYWxlX2NvbG9yX3ZpcmlkaXNfZChsYWJlbHMgPSBjKCdDaHJpc3RtYXMgMjAxMCcsICdDaHJpc3RtYXMgMjAxMScsICdOb24tSG9saWRheSBXZWVrbHkgQXZnLicpKSArCiAgbGFicyh0aXRsZSA9ICdXZWVrbHkgQXZlcmFnZSBTYWxlcyB2cy4gQ2hyaXN0bWFzOiAyMDEwIHRocnUgMjAxMScsIHggPSAnU3RvcmUnLCB5ID0gJ1NhbGVzIEFtb3VudCcsIGNvbG9yID0gJ1NhbGVzIEV2ZW50JykKICAKeG1hc19ncmlkcwpgYGAKCiMjIyBQcm92aWRlIG1vbnRobHkgYW5kIHNlbWVzdGVyIHZpZXcgb2Ygc2FsZXMgaW4gdW5pdHMgYWxvbmcgd2l0aCBpbnNpZ2h0cy4KCmBgYHtyfQojIFNldCB1cCBhIG1vbnRobHkgdmlldwptb250aGx5IDwtIGRhdGEuZnJhbWUod2FsbWFydFssIGMoJ1N0b3JlJywgJ0RhdGUnLCAnV2Vla2x5X1NhbGVzJywgJ0hvbGlkYXlfRmxhZycpXSkKVmlldyhtb250aGx5KQojIENyZWF0ZSBhIG1vbnRoIGNvbHVtbgptb250aGx5WywnTW9udGgnXSA8LSBOQQojIENyZWF0ZSBhIHllYXIgY29sdW1uCm1vbnRobHlbLCdZZWFyJ10gPC0gTkEKIyBDcmVhdGUgYSBzZW1lc3RlciBjb2x1bW4KbW9udGhseVssJ1NlbWVzdGVyJ10gPC0gTkEKc2VtMSA8LSBjKCIwMSIsICIwMiIsICIwMyIsICIwNCIsICIwNSIsICIwNiIpICMgU2VtZXN0ZXIgMQpzZW0yIDwtIGMoIjA3IiwgIjA4IiwgIjA5IiwgIjEwIiwgIjExIiwgIjEyIikgIyBTZW1lc3RlciAyCgpmb3IgKGkgaW4gMTpsZW5ndGgobW9udGhseSRTdG9yZSkpIHsKICAjIFJldHJpZXZlIHRoZSBkYXRlIGZyb20gRGF0ZQogIG1vbnRobHlfZGF0ZSA8LSB1bmxpc3Qoc3Ryc3BsaXQobW9udGhseSREYXRlW2ldLCAiLSIpKQogICMgRm9jdXMgb24gdGhlIG1vbnRoCiAgbW9udGhseSRNb250aFtpXSA8LSBzd2l0Y2gobW9udGhseV9kYXRlWzJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwMSIgPSAiSmFuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMDIiID0gIkZlYiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjAzIiA9ICJNYXIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwNCIgPSAiQXByIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMDUiID0gIk1heSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjA2IiA9ICJKdW4iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIwNyIgPSAiSnVsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMDgiID0gIkF1ZyIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjA5IiA9ICJTZXB0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMTAiID0gIk9jdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjExIiA9ICJOb3YiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIxMiIgPSAiRGVjIikKICAjIFNldCB0aGUgeWVhcgogIG1vbnRobHkkWWVhcltpXSA8LSBtb250aGx5X2RhdGVbM10KICAjIFNldCB0aGUgc2VtZXN0ZXIKICBpZihtb250aGx5X2RhdGVbMl0gJWluJSBzZW0xKSB7CiAgICBtb250aGx5JFNlbWVzdGVyW2ldIDwtICdTZW1lc3RlciAxJwogIH0KICBlbHNlIGlmKG1vbnRobHlfZGF0ZVsyXSAlaW4lIHNlbTIpIHsKICAgIG1vbnRobHkkU2VtZXN0ZXJbaV0gPC0gJ1NlbWVzdGVyIDInCiAgfQogIGVsc2UgewogICAgbW9udGhseSRTZW1lc3RlcltpXSA8LSAnSW52YWxpZCBtb250aCcKICB9Cn0KCiMgQWdncmVnYXRlIGJ5IG1vbnRoIGFuZCB5ZWFyCm1vbnRobHlfc3VtcyA8LSBkYXRhLmZyYW1lKGFnZ3JlZ2F0ZShXZWVrbHlfU2FsZXMgfiBTdG9yZSArIFllYXIgKyBNb250aCwgZGF0YSA9IG1vbnRobHksIEZVTiA9IHN1bSkpClZpZXcobW9udGhseV9zdW1zKQogCiMgQWdncmVnYXRlIGJ5IHNlbWVzdGVyCnNlbWVzdGVyX3N1bXMgPC0gZGF0YS5mcmFtZShhZ2dyZWdhdGUoV2Vla2x5X1NhbGVzIH4gU3RvcmUgKyBZZWFyICsgU2VtZXN0ZXIsIGRhdGEgPSBtb250aGx5LCBGVU4gPSBzdW0pKQpWaWV3KHNlbWVzdGVyX3N1bXMpCgpgYGAKCiMjIyMgMjAxMCBNb250aGx5IFBsb3Q6IFByb3BvcnRpb25hbCBWaWV3CmBgYHtyfQojIE1vbnRobHkgcGxvdHMKbW9udGhseTIwMTBfYmFycyA8LSBnZ3Bsb3Qoc3Vic2V0KG1vbnRobHlfc3VtcywgWWVhciAlaW4lIDIwMTApLCBhZXMoeCA9IGZhY3RvcihTdG9yZSksIHkgPSBXZWVrbHlfU2FsZXMsIGZpbGwgPSBmYWN0b3IoTW9udGgsIGxldmVscz0gYygnSmFuJywgJ0ZlYicsICdNYXInLCAnQXByJywgJ01heScsICdKdW4nLCAnSnVsJywgJ0F1ZycsICdTZXB0JywgJ09jdCcsICdOb3YnLCAnRGVjJykpKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gImZpbGwiKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QobGltaXRzID0gYygnSmFuJywgJ0ZlYicsICdNYXInLCAnQXByJywgJ01heScsICdKdW4nLCAnSnVsJywgJ0F1ZycsICdTZXB0JywgJ09jdCcsICdOb3YnLCAnRGVjJykpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGUgPSAnMjAxMCBNb250aGx5IFNhbGVzIHBlciBTdG9yZTogUHJvcG9ydGlvbmFsIFZpZXcnLCB4ID0gJ1N0b3JlIE51bWJlcicsIHkgPSAnTW9udGhseSBTYWxlcycsIGZpbGwgPSAnTW9udGgnKQogIAojIFNob3cgdGhlIHBsb3QKbW9udGhseTIwMTBfYmFycwpgYGAKCgojIyMjIDIwMTEgTW9udGhseSBQbG90OiBQcm9wb3J0aW9uYWwgVmlldwpgYGB7cn0KIyBNb250aGx5IHBsb3RzCm1vbnRobHkyMDExX2JhcnMgPC0gZ2dwbG90KHN1YnNldChtb250aGx5X3N1bXMsIFllYXIgJWluJSAyMDExKSwgYWVzKHggPSBmYWN0b3IoU3RvcmUpLCB5ID0gV2Vla2x5X1NhbGVzLCBmaWxsID0gZmFjdG9yKE1vbnRoLCBsZXZlbHM9IGMoJ0phbicsICdGZWInLCAnTWFyJywgJ0FwcicsICdNYXknLCAnSnVuJywgJ0p1bCcsICdBdWcnLCAnU2VwdCcsICdPY3QnLCAnTm92JywgJ0RlYycpKSkpICsKICBnZW9tX2NvbChwb3NpdGlvbiA9ICJmaWxsIikgKwogIHNjYWxlX2ZpbGxfdmlyaWRpc19kKGxpbWl0cyA9IGMoJ0phbicsICdGZWInLCAnTWFyJywgJ0FwcicsICdNYXknLCAnSnVuJywgJ0p1bCcsICdBdWcnLCAnU2VwdCcsICdPY3QnLCAnTm92JywgJ0RlYycpKSArCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxLCB2anVzdCA9IDAuNSkpICsKICBsYWJzKHRpdGxlID0gJzIwMTEgTW9udGhseSBTYWxlcyBwZXIgU3RvcmU6IFByb3BvcnRpb25hbCBWaWV3JywgeCA9ICdTdG9yZSBOdW1iZXInLCB5ID0gJ01vbnRobHkgU2FsZXMnLCBmaWxsID0gJ01vbnRoJykKICAKIyBTaG93IHRoZSBwbG90Cm1vbnRobHkyMDExX2JhcnMKYGBgCiMjIyMgMjAxMiBNb250aGx5IFBsb3Q6IFByb3BvcnRpb25hbCBWaWV3CmBgYHtyfQojIE1vbnRobHkgcGxvdHMKbW9udGhseTIwMTJfYmFycyA8LSBnZ3Bsb3Qoc3Vic2V0KG1vbnRobHlfc3VtcywgWWVhciAlaW4lIDIwMTIpLCBhZXMoeCA9IGZhY3RvcihTdG9yZSksIHkgPSBXZWVrbHlfU2FsZXMsIGZpbGwgPSBmYWN0b3IoTW9udGgsIGxldmVscz0gYygnSmFuJywgJ0ZlYicsICdNYXInLCAnQXByJywgJ01heScsICdKdW4nLCAnSnVsJywgJ0F1ZycsICdTZXB0JywgJ09jdCcsICdOb3YnLCAnRGVjJykpKSkgKwogIGdlb21fY29sKHBvc2l0aW9uID0gImZpbGwiKSArCiAgc2NhbGVfZmlsbF92aXJpZGlzX2QobGltaXRzID0gYygnSmFuJywgJ0ZlYicsICdNYXInLCAnQXByJywgJ01heScsICdKdW4nLCAnSnVsJywgJ0F1ZycsICdTZXB0JywgJ09jdCcsICdOb3YnLCAnRGVjJykpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGUgPSAnMjAxMiBNb250aGx5IFNhbGVzIHBlciBTdG9yZTogUHJvcG9ydGlvbmFsIFZpZXcnLCB4ID0gJ1N0b3JlIE51bWJlcicsIHkgPSAnTW9udGhseSBTYWxlcycsIGZpbGwgPSAnTW9udGgnKQogIAojIFNob3cgdGhlIHBsb3QKbW9udGhseTIwMTJfYmFycwpgYGAKCgojIyMjIFNlbWVzdGVyIFBsb3Q6IFByb3BvcnRpb25hbCBWaWV3CmBgYHtyfQojIFNlbWVzdGVyIHBsb3QKc2VtX2JhcnMgPC0gZ2dwbG90KHNlbWVzdGVyX3N1bXMsIGFlcyh4ID0gZmFjdG9yKFN0b3JlKSwgeSA9IFdlZWtseV9TYWxlcywgZmlsbCA9IGZhY3RvcihTZW1lc3RlcikpKSArCiAgZ2VvbV9jb2wocG9zaXRpb24gPSAiZmlsbCIpICsKICBzY2FsZV9maWxsX3ZpcmlkaXNfZCgpICsKICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDQ1LCBoanVzdCA9IDEsIHZqdXN0ID0gMC41KSkgKwogIGxhYnModGl0bGUgPSAnU2VtZXN0ZXIgU2FsZXMgcGVyIFN0b3JlOiBQcm9wb3J0aW9uYWwgVmlldycsIHggPSAnU3RvcmUgTnVtYmVyJywgeSA9ICdTZW1lc3RlciBTYWxlcycsIGZpbGwgPSAnU2VtZXN0ZXInKQogIAojIFNob3cgdGhlIHBsb3QKc2VtX2JhcnMKYGBgCiMjIFN0YXRpc3RpY2FsIE1vZGVsCkZvciBTdG9yZSAxLCBidWlsZCBwcmVkaWN0aW9uIG1vZGVscyB0byBmb3JlY2FzdCBkZW1hbmQKLSBMaW5lYXIgcmVncmVzc2lvbjoKICAtIFJlc3RydWN0dXJlIGRhdGVzIGJlZ2lubmluZyBhdCAxIGZvciB0aGUgZWFybGllc3QgZGF0ZSBpbiB0aGUgb3JkZXIuCiAgLSBDaGFuZ2UgZGF0ZXMgaW50byBkYXlzIGJ5IGNyZWF0aW5nIGEgbmV3IHZhcmlhYmxlLgogIC0gSHlwb3RoZXNpemUgaWYgQ1BJLCB1bmVtcGxveW1lbnQsIGFuZCBmdWVsIHByaWNlIGhhdmUgYW55IGltcGFjdCBvbiBzYWxlcy4KICAKYGBge3J9CiMgVmlldyBkYXRhIHBvaW50cyBieSBzdG9yZQp3YWxtYXJ0X3N0b3JlcyA8LSBzdW1tYXJ5KGFzLmZhY3Rvcih3YWxtYXJ0JFN0b3JlKSkgIyBWZXJpZmllZDogMTQzIGRhdGEgcG9pbnRzIHBlciBzdG9yZQoKIyBDcmVhdGUgYSBuZXcgZGF0YWZyYW1lIGZvciB0aGUgcmVncmVzc2lvbiBhbmFseXNpcwp3YWxtYXJ0X3JlZ3Jlc3Npb24gPC0gZGF0YS5mcmFtZShzdWJzZXQod2FsbWFydCwgU3RvcmUgJWluJSAxKSkKVmlldyh3YWxtYXJ0X3JlZ3Jlc3Npb24pCgojIERhdGUgY29udmVyc2lvbgp3YWxtYXJ0X3JlZ3Jlc3Npb24kZGF5IDwtIGMoMTpsZW5ndGgod2FsbWFydF9yZWdyZXNzaW9uJFN0b3JlKSkKCiMgRHJvcCB1bm5lY2Vzc2FyeSBjb2x1bW5zCndhbG1hcnRfcmVncmVzc2lvbiA8LSBzdWJzZXQod2FsbWFydF9yZWdyZXNzaW9uLCBzZWxlY3QgPSAtYyhTdG9yZSwgRGF0ZSwgSG9saWRheV9GbGFnLCBRdWFydGVyKSkKYGBgCgogIAoKIyMjIyBQYWlyIFBsb3RzOiBUZW1wZXJhdHVyZSwgRnVlbCBQcmljZSwgQ1BJLCBVbmVtcGxveW1lbnQgdnMuIFdlZWtseSBTYWxlcwpgYGB7cn0KIyBTY2F0dGVyIHBsb3QKcGFyKG1mcm93ID0gYygzLDIpKSAjIEZvcm1hdHMgdGhlIGRpc3BsYXkKCmZvciAoaSBpbiAyOjYpIHsKICBwbG90KHdhbG1hcnRfcmVncmVzc2lvblssaV0sCiAgICAgICB3YWxtYXJ0X3JlZ3Jlc3Npb24kV2Vla2x5X1NhbGVzLAogICAgICAgbWFpbiA9IG5hbWVzKHdhbG1hcnRfcmVncmVzc2lvbltpXSksCiAgICAgICB5bGFiID0gbmFtZXMod2FsbWFydF9yZWdyZXNzaW9uJFdlZWtseV9TYWxlcyksCiAgICAgICB4bGFiID0gJycsIGNvbCA9ICcjNDQwMTU0JykKfQoKcGFyKG1mcm93ID0gYygxLDEpKQpgYGAKCiMjIyMgQ29ycmVsYXRpb25zIGJldHdlZW4gV2Vla2x5IFNhbGVzIGFuZCBJbmRlcGVuZGVudCBWYXJpYWJsZXMKVGhlIGhlYXRtYXAgYmVsb3cgc2hvd3Mgd2VhayBjb3JyZWxhdGlvbnMgYmV0d2VlbiB0aGUgV2Vla2x5IFNhbGVzIGRlcGVuZGVudCB2YXJpYWJsZXMgYW5kIHRoZSBpbmRlcGVuZGVudCB2YXJpYWJsZXMgb2YgVGVtcGVyYXR1cmUsIEZ1ZWxfUHJpY2UsIENQSSwgYW5kIFVuZW1wbG95bWVudC4KLSBUZW1wZXJhdHVyZSBhbmQgVW5lbXBsb3ltZW50IGhhdmUgYW4gaW52ZXJzZSByZWxhdGlvbnNoaXAgd2l0aCBXZWVrbHkgU2FsZXMuCi0gRnVlbF9QcmljZSBhbmQgQ1BJIGhhdmUgYSBkaXJlY3QgKHBvc2l0aXZlKSByZWxhdGlvbnNoaXAgd2l0aCBXZWVrbHkgU2FsZXMuCgpPZiBjb25jZXJuIGluIHRoaXMgbW9kZWwgYXJlIHRoZSBoaWdoIGNvcnJlbGF0aW9ucyBiZXR3ZWVuIEZ1ZWxfUHJpY2UgYW5kIENQSSwgRnVlbF9QcmljZSBhbmQgVW5lbXBsb3ltZW50LCBhbmQgQ1BJIGFuZCBVbmVtcGxveW1lbnQuIEhvd2V2ZXIsIHRoZXkgZG8gbm90IGFwcGVhciB0byBiZSBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50LgoKYGBge3J9CndhbG1hcnRfbWNjIDwtIGNvcih3YWxtYXJ0X3JlZ3Jlc3Npb25bLCAxOjZdKSAjIENvbnRhaW5zIHRoZSBjb3JyZWxhdGlvbiBtYXRyaXgKClZpZXcod2FsbWFydF9tY2MpCgpjb3JycGxvdCh3YWxtYXJ0X21jYywgbWV0aG9kID0gJ2NvbG9yJywgb3V0bGluZSA9IFQsIGNsLnBvcyA9ICduJywgcmVjdC5jb2wgPSAnYmxhY2snLCB0bC5jb2wgPSAnaW5kaWFucmVkNCcsIGFkZENvZWYuY29sID0gImJsYWNrIiwgbnVtYmVyLmRpZ2l0cyA9IDIsIG51bWJlci5jZXggPSAwLjYwLCB0bC5jZXggPSAwLjcsIGNsLmNleCA9IDEsIGNvbCA9IGNvbG9yUmFtcFBhbGV0dGUoYygiZ3JlZW40Iiwid2hpdGUiLCJyZWQiKSkoMTAwKSkKYGBgCgojIyMjIEh5cG90aGVzZXMKLSBBcyB0ZW1wZXJhdHVyZSBpbmNyZWFzZXMsIHdlZWtseSBzYWxlcyBzaG91bGQgZGVjcmVhc2UuCi0gQ1BJIHNob3VsZCBoYXZlIG5vIHJlbGF0aW9uc2hpcCB0byB3ZWVrbHkgc2FsZXMuCi0gVW5lbXBsb3ltZW50IHJhdGVzIHNob3VsZCBoYXZlIGFuIGludmVyc2UgcmVsYXRpb25zaGlwIHdpdGggd2Vla2x5IHNhbGVzLgotIEZ1ZWwgcHJpY2VzIHNob3VsZCBub3Qgc2hvdyBhIHJlbGF0aW9uc2hpcCB0byB3ZWVrbHkgc2FsZXMuCgpgYGB7cn0KCiMgU3BsaXQgZGF0YSBpbnRvIHRyYWluIGFuZCB0ZXN0IHdpdGggYW5kIDgwJS0yMCUgc3BsaXQKc2V0LnNlZWQoMTIzNCkgIyBTZXQgdGhlIHNlZWQKc2FtcGxlX212cjEgPC0gc2FtcGxlLnNwbGl0KHdhbG1hcnRfcmVncmVzc2lvbiwgU3BsaXRSYXRpbyA9IDAuODApICNTZXQgdGhlIHNwbGl0IHByb3BvcnRpb25zCgp0cmFpbiA8LSBzdWJzZXQod2FsbWFydF9yZWdyZXNzaW9uLCBzYW1wbGVfbXZyMSA9PSBUKSAjIFRyYWluaW5nIGRhdGEKdGVzdCA8LSBzdWJzZXQod2FsbWFydF9yZWdyZXNzaW9uLCBzYW1wbGVfbXZyMSA9PSBGKSAjIFRlc3QgZGF0YQoKYGBgCgojIyMgQmFzZSBNb2RlbDogTXVsdGl2YXJpYXRlIFJlZ3Jlc3Npb24gd2l0aCBEYXkKVXNlcyBkYXkgYW5kIGFzc3VtZXMgYWxsIHByZWRpY3RvciB2YXJpYWJsZXMgYXJlIGNvbnRpbnVvdXMgYXMgd2VsbCBhcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLiBUaGlzIGFuYWx5c2lzIHdpbGwgdXNlIGFuIGFscGhhIGxldmVsIG9mIDAuMDUuCgpgYGB7cn0KIyBDcmVhdGUgdGhlIGJhc2UgbW9kZWwgcmVncmVzc2lvbgptb2RlbF9iYXNlIDwtIGxtKGZvcm11bGEgPSBXZWVrbHlfU2FsZXMgfi4sIGRhdGEgPSB0cmFpbikKIyBWaWV3IHRoZSBiYXNlIG1vZGVsIHN1bW1hcnkKc3VtbWFyeShtb2RlbF9iYXNlKQoKYGBgCkZyb20gdGhlIHN1bW1hcnkgYWJvdmUsIG5vbmUgb2YgdGhlIGluY2x1ZGVkIHZhcmlhYmxlcyByZWFjaCBzaWduaWZpY2FuY2UgYXQgdGhlIDAuMDUgbGV2ZWwuIEhvd2V2ZXIsIHRoZSBGLXN0YXRpc3RpYyBiZWluZyBzaWduaWZpY2FudCBhdCB0aGUgMC4wNSBhbHBoYSBsZXZlbCBpbmRpY2F0ZXMgdGhhdCBhbGwgdmFyaWFibGVzIHRvZ2V0aGVyIHlpZWxkIGEgbW9kZWwgdGhhdCBmaXRzIHRoZSBkYXRhLgoKIyMjIyBWaXN1YWxpemluZyBUcmFpbmluZyBQcmVkaWN0aW9ucwpgYGB7cn0KIyBQcmVkaWN0aW9uCmJhc2VfdHJhaW5fcHJlZCA8LSBwcmVkaWN0KG1vZGVsX2Jhc2UsIG5ld2RhdGEgPSB0cmFpbikKIyBOdW1iZXIgb2YgcHJlZGljdGlvbnMKbGVuZ3RoKGJhc2VfdHJhaW5fcHJlZCkKYmFzZV90cmFpbl9wcmVkCgojIFZpc3VhbGl6YXRpb24KYmFzZV90cmFpbmluZ19jaGFydCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHRyYWluJFdlZWtseV9TYWxlcywgeSA9IGJhc2VfdHJhaW5fcHJlZCkpICsKICBsYWJzKHRpdGxlID0gJ1dlZWtseSBTYWxlcyBbQmFzZSBNb2RlbF06IFRyYWluaW5nIFNldCB2cyBQcmVkaWN0aW9ucycsIHggPSAnQWN0dWFsIFdlZWtseSBTYWxlcycsIHkgPSAnUHJlZGljdGVkIFdlZWtseSBTYWxlcycpCgpiYXNlX3RyYWluaW5nX2NoYXJ0CmBgYAojIyMjIFRlc3QgdGhlIEJhc2UgTW9kZWwKYGBge3J9CiMgU2V0IHVwIHRoZSBtb2RlbCBmb3IgdGhlIHRlc3QgZGF0YSBzZXQKYmFzZV90ZXN0X3ByZWQgPC0gcHJlZGljdChtb2RlbF9iYXNlLCBuZXdkYXRhID0gdGVzdCkKYmFzZV90ZXN0X3ByZWQKCiMgVmlzdWFsaXphdGlvbgpiYXNlX3Rlc3RfY2hhcnQgPC0gZ2dwbG90KCkgKwogIGdlb21fcG9pbnQoYWVzKHggPSB0ZXN0JFdlZWtseV9TYWxlcywgeSA9IGJhc2VfdGVzdF9wcmVkKSkgKwogIGxhYnModGl0bGUgPSAnV2Vla2x5IFNhbGVzIFtCYXNlIE1vZGVsXTogVGVzdCBTZXQgdnMgUHJlZGljdGlvbnMnLCB4ID0gJ0FjdHVhbCBXZWVrbHkgU2FsZXMnLCB5ID0gJ1ByZWRpY3RlZCBXZWVrbHkgU2FsZXMnKQoKYmFzZV90ZXN0X2NoYXJ0CmBgYAojIyMjIFZhbGlkYXRlIHRoZSBCYXNlIE1vZGVsIEFjY3VyYWN5CmBgYHtyfQojIE1lYW4gQWJzb2x1dGUgUGVyY2VudGFnZSBFcnJvcgptYXBlX2Jhc2UgPC0gTUFQRShiYXNlX3Rlc3RfcHJlZCwgdGVzdCRXZWVrbHlfU2FsZXMpCgojIFJvb3QgTWVhbiBTcXVhcmUgRXJyb3IKcm1zZV9iYXNlIDwtIFJNU0UoYmFzZV90ZXN0X3ByZWQsIHRlc3QkV2Vla2x5X1NhbGVzKQoKcHJpbnQocGFzdGUoJ1RoZSBiYXNlIG1lYW4gYWJzb2x1dGUgcGVyY2VudGFnZSBlcnJvciBpcyAnLCBtYXBlX2Jhc2UpKQpwcmludChwYXN0ZSgnVGhlIGJhc2Ugcm9vdCBtZWFuIHNxdWFyZSBlcnJvciBpcyAnLCBybXNlX2Jhc2UpKQoKYGBgClRoZSBtZWFuIGFic29sdXRlIHBlcmNlbnRhZ2UgZXJyb3IgdmFsdWUgaXMgNS45JS4gVGhlIE1BUEUgc3RhbmRhcmQgZm9yIGV2YWx1YXRpbmcgYSBNQVBFIHZhbHVlIGRlcGVuZHMgb24gdGhlIGluZHVzdHJ5LCBzbyB3aXRob3V0IGtub3dpbmcgd2hhdCB0aGUgTUFQRSBpcyBmb3IgcmV0YWlsLCBpdCBpcyBoYXJkIHRvIHNheSBpZiB0aGlzIG1vZGVsIGFsb25lIHByb2R1Y2VzIGFjY2VwdGFibGUgYWNjdXJhY3kuIEhvd2V2ZXIsIGl0IGNhbiBzZXJ2ZSBhcyB0aGUgYmFzZWxpbmUgY29tcGFyaXNvbiBhZ2FpbnN0IHdoaWNoIG90aGVyIG1vZGVscyB3aWxsIGJlIGp1ZGdlZC4gIAoKVGhlIHJvb3QgbWVhbiBzcXVhcmUgZXJyb3Igc2VydmVzIGFzIGEgd2F5IHRvIGV2YWx1YXRlIHRoZSBmaXQgb2YgYSBtb2RlbCB0byB0aGUgZGF0YS4gSXQgaXMgYmVzdCBpbiBhIGNvbXBhcmlzb24gcm9sZSBhZ2FpbnN0IG90aGVyIG1vZGVscywgc28gdGhlIHZhbHVlIG9mIHRoZSBiYXNlIG1vZGVsIGhlcmUgd2lsbCBzZXJ2ZSBhcyBhIGJhc2VsaW5lIGZvciBvdGhlciBtb2RlbHMuIAoKRHVlIHRvIHRoZSBuYXR1cmUgb2YgdGhlIERheSB2YXJpYWJsZSwgaXQgd2lsbCBiZSByZW1vdmVkIGZyb20gdGhlIG5leHQgYW5hbHlzaXMgdG8gc2VlIGlmIHRoZSBtb2RlbCBpbXByb3ZlcyBhbnkuCgojIyMgTW9kZWwgMTogTXVsdGl2YXJpYXRlIFJlZ3Jlc3Npb24gd2l0aG91dCBEYXkKQXNzdW1pbmcgYWxsIHByZWRpY3RvciB2YXJpYWJsZXMgYXJlIGNvbnRpbnVvdXMgYXMgd2VsbCBhcyB0aGUgZGVwZW5kZW50IHZhcmlhYmxlLiBGb3IgdGhpcyBhbmFseXNpcyB3ZSB3aWxsIGFkb3B0IGFuIGFscGhhIGxldmVsIG9mIDAuMDUuCmBgYHtyfQojIFJlbW92ZSBkYXkgZnJvbSB0cmFpbiBhbmQgdGVzdAp0cmFpbiA8LSBzdWJzZXQodHJhaW4sIHNlbGVjdCA9IC1jKGRheSkpCnRlc3QgPC0gc3Vic2V0KHRlc3QsIHNlbGVjdCA9IC1jKGRheSkpCgojIFNldCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gZm9ybXVsYQptb2RlbF9tdmxpbjEgPC0gbG0oZm9ybXVsYSA9IFdlZWtseV9TYWxlcyB+IFRlbXBlcmF0dXJlICsgRnVlbF9QcmljZSArIENQSSArIFVuZW1wbG95bWVudCwgZGF0YSA9IHRyYWluKQojIFZpZXcgdGhlIG1vZGVsIHN1bW1hcnkKc3VtbWFyeShtb2RlbF9tdmxpbjEpCgpgYGAKRnJvbSB0aGUgc3VtbWFyeSBhYm92ZSwgaXQgbG9va3MgYXMgaWYgb25seSBDUEkgaXMgYSBzaWduaWZpY2FudCBwcmVkaWN0b3Igb2YgV2Vla2x5X1NhbGVzLiBIb3dldmVyLCB0aGUgRi1zdGF0aXN0aWMgaXMgc2lnbmlmaWNhbnQgYXQgdGhlIHNlbGVjdGVkIGFscGhhIGxldmVsIGZvciB0aGUgYW5hbHlzaXMsIHNvIHRoZSBvdmVyYWxsIG1vZGVsIGNhbiBiZSBhY2NlcHRlZCBhcyBhIGZpdCBmb3IgdGhlIGRhdGEuCgojIyMjIFZpc3VhbGl6ZSBUcmFpbmluZyBTZXQgUmVzdWx0cwpgYGB7cn0KIyBQcmVkaWN0aW9uCndlZWtseV9zYWxlc190cmFpbl9wcmVkIDwtIHByZWRpY3QobW9kZWxfbXZsaW4xLCBuZXdkYXRhID0gdHJhaW4pCiMgTnVtYmVyIG9mIHByZWRpY3Rpb25zCmxlbmd0aCh3ZWVrbHlfc2FsZXNfdHJhaW5fcHJlZCkKd2Vla2x5X3NhbGVzX3RyYWluX3ByZWQKCiMgVmlzdWFsaXphdGlvbgp0cmFpbmluZ19jaGFydCA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHRyYWluJFdlZWtseV9TYWxlcywgeSA9IHdlZWtseV9zYWxlc190cmFpbl9wcmVkKSkgKwogIGxhYnModGl0bGUgPSAnV2Vla2x5IFNhbGVzOiBUcmFpbmluZyBTZXQgdnMgUHJlZGljdGlvbnMnLCB4ID0gJ0FjdHVhbCBXZWVrbHkgU2FsZXMnLCB5ID0gJ1ByZWRpY3RlZCBXZWVrbHkgU2FsZXMnKQoKdHJhaW5pbmdfY2hhcnQKYGBgCiMjIyMgVGVzdCB0aGUgTW9kZWwKYGBge3J9CiMgU2V0IHVwIHRoZSBtb2RlbCBmb3IgdGhlIHRlc3QgZGF0YSBzZXQKd2Vla2x5X3NhbGVzX3Rlc3RfcHJlZCA8LSBwcmVkaWN0KG1vZGVsX212bGluMSwgbmV3ZGF0YSA9IHRlc3QpCndlZWtseV9zYWxlc190ZXN0X3ByZWQKCiMgVmlzdWFsaXphdGlvbgp0ZXN0X2NoYXJ0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdGVzdCRXZWVrbHlfU2FsZXMsIHkgPSB3ZWVrbHlfc2FsZXNfdGVzdF9wcmVkKSkgKwogIGxhYnModGl0bGUgPSAnV2Vla2x5IFNhbGVzOiBUZXN0IFNldCB2cyBQcmVkaWN0aW9ucycsIHggPSAnQWN0dWFsIFdlZWtseSBTYWxlcycsIHkgPSAnUHJlZGljdGVkIFdlZWtseSBTYWxlcycpCgp0ZXN0X2NoYXJ0CgpgYGAKIyMjIyBWYWxpZGF0ZSB0aGUgTW9kZWwgQWNjdXJhY3kKYGBge3J9CiMgTWVhbiBBYnNvbHV0ZSBQZXJjZW50YWdlIEVycm9yCm1hcGUxIDwtIE1BUEUod2Vla2x5X3NhbGVzX3Rlc3RfcHJlZCwgdGVzdCRXZWVrbHlfU2FsZXMpCgojIFJvb3QgTWVhbiBTcXVhcmUgRXJyb3IKcm1zZTEgPC0gUk1TRSh3ZWVrbHlfc2FsZXNfdGVzdF9wcmVkLCB0ZXN0JFdlZWtseV9TYWxlcykKCnByaW50KHBhc3RlKCdUaGUgbWVhbiBhYnNvbHV0ZSBwZXJjZW50YWdlIGVycm9yIGZvciBtb2RlbCAxIGlzICcsIG1hcGUxKSkKcHJpbnQocGFzdGUoJ1RoZSByb290IG1lYW4gc3F1YXJlIGVycm9yIGZvciBtb2RlbCAxIGlzICcsIHJtc2UxKSkKCmBgYApUaGUgbWVhbiBhYnNvbHV0ZSBwZXJjZW50YWdlIGVycm9yIGZvciB0aGlzIG1vZGVsIGlzIGFsbW9zdCBpZGVudGljYWwgdG8gdGhlIGZpcnN0IG1vZGVsLiBTYW1lIHdpdGggdGhlIHJvb3QgbWVhbiBzcXVhcmUgZXJyb3IgdmFsdWUuIFRoZXJlZm9yZSwgZGVzcGl0ZSB0aGUgb25lIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQgcHJlZGljdG9yIHZhcmlhYmxlLCB0aGlzIG1vZGVsIGNhbm5vdCBiZSBzYWlkIHRvIGJlIGJldHRlciB0aGFuIHRoZSBiYXNlIG1vZGVsLgoKIyMjIEZpbmFsIE1vZGVsOiBDUEkgYW5kIFRlbXBlcmF0dXJlIGFzIFByZWRpY3RvcnMgb2YgV2Vla2x5IFNhbGVzClRoaXMgZmluYWwgbW9kZWwgd2lsbCB0cmVhdCBDUEkgYXMgYSBjb250aW51b3VzIHZhcmlhYmxlLCBidXQgd2lsbCByZW1vdmUgRnVlbF9QcmljZSBhbmQgVW5lbXBsb3ltZW50IGZyb20gdGhlIHByZWRpY3Rpb24gbW9kZWwgZHVlIHRvIHRoZWlyIHJlc3VsdHMgaW4gdGhlIGNvcnJlbGF0aW9uIG1hdHJpeCBjcmVhdGVkIGF0IHRoZSBzdGFydCBvZiB0aGUgYW5hbHlzaXMuCmBgYHtyfQojIFNldCB0aGUgbGluZWFyIHJlZ3Jlc3Npb24gZm9ybXVsYQpmaW5hbF9tb2RlbCA8LSBsbShmb3JtdWxhID0gV2Vla2x5X1NhbGVzIH4gVGVtcGVyYXR1cmUgKyBDUEksIGRhdGEgPSB0cmFpbikKIyBWaWV3IHRoZSBtb2RlbCBzdW1tYXJ5CnN1bW1hcnkoZmluYWxfbW9kZWwpCmBgYApUaGlzIHBhcnRpY3VsYXIgbW9kZWwgc2hvd3MgYm90aCBwcmVkaWN0b3IgdmFyaWFibGVzIGFzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQuIEZ1cnRoZXJtb3JlLCB0aGUgbW9kZWwgaXRzZWxmIGlzIHN0YXRpc3RpY2FsbHkgc2lnbmlmaWNhbnQsIGluZGljYXRpbmcgaXQgaXMgYSBnb29kIGZpdCBmb3IgdGhlIGRhdGEuCgojIyMjIFZpc3VhbGl6ZSBUcmFpbmluZyBTZXQgUmVzdWx0cwpgYGB7cn0KIyBQcmVkaWN0aW9uCmZpbmFsX3ByZWQgPC0gcHJlZGljdChmaW5hbF9tb2RlbCwgbmV3ZGF0YSA9IHRyYWluKQojIE51bWJlciBvZiBwcmVkaWN0aW9ucwpsZW5ndGgoZmluYWxfcHJlZCkKZmluYWxfcHJlZAoKIyBWaXN1YWxpemF0aW9uCmZpbmFsX2NoYXJ0MSA8LSBnZ3Bsb3QoKSArCiAgZ2VvbV9wb2ludChhZXMoeCA9IHRyYWluJFdlZWtseV9TYWxlcywgeSA9IGZpbmFsX3ByZWQpKSArCiAgbGFicyh0aXRsZSA9ICdXZWVrbHkgU2FsZXNbRmluYWwgTW9kZWxdOiBUcmFpbmluZyBTZXQgdnMgUHJlZGljdGlvbnMnLCB4ID0gJ0FjdHVhbCBXZWVrbHkgU2FsZXMnLCB5ID0gJ1ByZWRpY3RlZCBXZWVrbHkgU2FsZXMnKQoKZmluYWxfY2hhcnQxCgpgYGAKCiMjIyMgVGVzdCB0aGUgTW9kZWwKYGBge3J9CiMgU2V0IHVwIHRoZSBtb2RlbCBmb3IgdGhlIHRlc3QgZGF0YSBzZXQKZmluYWxfdGVzdF9wcmVkIDwtIHByZWRpY3QoZmluYWxfbW9kZWwsIG5ld2RhdGEgPSB0ZXN0KQpmaW5hbF90ZXN0X3ByZWQKCiMgVmlzdWFsaXphdGlvbgpmaW5hbF90ZXN0X2NoYXJ0IDwtIGdncGxvdCgpICsKICBnZW9tX3BvaW50KGFlcyh4ID0gdGVzdCRXZWVrbHlfU2FsZXMsIHkgPSBmaW5hbF90ZXN0X3ByZWQpKSArCiAgbGFicyh0aXRsZSA9ICdXZWVrbHkgU2FsZXMgW0ZpbmFsIE1vZGVsXTogVGVzdCBTZXQgdnMgUHJlZGljdGlvbnMnLCB4ID0gJ0FjdHVhbCBXZWVrbHkgU2FsZXMnLCB5ID0gJ1ByZWRpY3RlZCBXZWVrbHkgU2FsZXMnKQoKZmluYWxfdGVzdF9jaGFydApgYGAKIyMjIyBWYWxpZGF0ZSB0aGUgTW9kZWwgQWNjdXJhY3kKYGBge3J9CiMgTWVhbiBBYnNvbHV0ZSBQZXJjZW50YWdlIEVycm9yCmZpbmFsX21hcGUgPC0gTUFQRShmaW5hbF90ZXN0X3ByZWQsIHRlc3QkV2Vla2x5X1NhbGVzKQoKIyBSb290IE1lYW4gU3F1YXJlIEVycm9yCmZpbmFsX3Jtc2UgPC0gUk1TRShmaW5hbF90ZXN0X3ByZWQsIHRlc3QkV2Vla2x5X1NhbGVzKQoKcHJpbnQocGFzdGUoJ1RoZSBtZWFuIGFic29sdXRlIHBlcmNlbnRhZ2UgZXJyb3IgZm9yIHRoZSBmaW5hbCBtb2RlbCBpcyAnLCBmaW5hbF9tYXBlKSkKcHJpbnQocGFzdGUoJ1RoZSByb290IG1lYW4gc3F1YXJlIGVycm9yIGZvciB0aGUgZmluYWwgbW9kZWwgaXMgJywgZmluYWxfcm1zZSkpCgpgYGAKClRoaXMgZmluYWwgbW9kZWwgb2ZmZXJzIGEgbWVhbiBhYnNvbHV0ZSBwZXJjZW50YWdlIGVycm9yIG9mIDUuNiUsIHZlcnN1cyB0aGUgNS45JSBvZiB0aGUgYmFzZSBtb2RlbC4gVGhlcmVmb3JlLCB0aGlzIG1vZGVsIGNhbiBiZSBqdWRnZWQgYXMgbW9yZSBhY2N1cmF0ZS4gVGhlIHJvb3QgbWVhbiBzcXVhcmUgZXJyb3IgaXMgYWxzbyBsZXNzIGZvciB0aGlzIG1vZGVsIHRoYW4gdGhlIGJhc2UgbW9kZWwsIHNvIGl0IGlzIGEgYmV0dGVyIGZpdCB0aGFuIHRoZSBiYXNlIG1vZGVsLgoKIyMjIEZpbmFsIFByZWRpY3Rpb24gTW9kZWwKCiR5X3twcmVkfSA9IC0yNTAwLjIqdGVtcCArIDk5NjkuMypjcGkgLSA0MzEzMTEuNCQgCgo=