Group 6 - Midterm Project

Introduction

The business problem we addressed is how different types of promotion campaigns affect sales value across various product departments and display/mailer locations at Regork. Understanding which campaign types and locations leading to higher sales value is crucial for optimizing promotion strategies, improving customer engagement, and boosting revenue.

We utilized sales transaction data, focusing on coupon types and their redemption rates, campaign display locations, and total sales across different departments. The methodology included analyzing sales value distributions across these variables to identify patterns in consumer purchases.

This analysis offers actionable insights, such as which campaign types drive the most sales and which locations are most effective. This can help Regork stakeholders make data-driven decisions on future promotions, improve resource allocation, and enhance overall sales strategies, especially by focusing on high-performing areas like grocery or drugstore departments.

Packages Required

library(tidyverse)               # collection of R packages for data manipulation and visualization
library(here)                    # simplifies file path management in projects
here() starts at /Users/ezishr/Documents/CINCY/Fall 2024/BANA 4080
library(lubridate)               # tools for working with date and time data
library(ggplot2)                 # data visualization using "Grammar of Graphics"
library(readr)                   # fast reading and writing of data files
library(scales)                  # provides functions for scaling and formatting

Attaching package: ‘scales’

The following object is masked from ‘package:purrr’:

    discard

The following object is masked from ‘package:readr’:

    col_factor
library(completejourney)
Welcome to the completejourney package! Learn more about these data sets at
http://bit.ly/completejourney.

Data Preparation

Setup

c(promotions, transactions) %<-% get_data(which = 'both', verbose = FALSE)

# Extract the date for transactions and change data type for product_id
transactions <- transactions %>% mutate(date = as.Date(transaction_timestamp))
transactions$product_id <- as.double(transactions$product_id)
promotions$product_id <- as.double(promotions$product_id)
products$product_id <- as.double(products$product_id)
coupons$product_id <- as.double(coupons$product_id)

# Vectors of display and mailer locations to actual names
display_location_labels <- c("0"="No display", 
                             "1"="Store front", 
                             "2"="Store rear", 
                             "3"="Front end cap", 
                             "4"="Mid-aisle end cap", 
                             "5"="Rear end cap", 
                             "6"="Side aisle end cap", 
                             "7"="In-aisle", 
                             "9"="Secondary location display", 
                             "A"="In-shelf")

mailer_location_labels <- c("0"="Not on ad", 
                            "A"="Interior page feature", 
                            "C"="Interior page line item", 
                            "D"="Front page feature", 
                            "F"="Back page feature", 
                            "H"="Wrap front feature", 
                            "J"="Wrap interior coupon", 
                            "L"="Wrap back feature", 
                            "P"="Interior page coupon", 
                            "X"="Free on interior page", 
                            "Z"="Free on front page/back page/or wrap")

Data Transformation

Assumption: transactions with any discount are redeemed coupons

# Select only relevant columns in transactions
trans1 <- transactions %>% 
  dplyr::filter(retail_disc>0 | coupon_disc>0 | coupon_match_disc>0) %>%
  select(household_id, store_id, product_id, sales_value:coupon_match_disc, week, date)

# Join coupons, coupon_redemptions, campaign_description to get full information of coupons
redemptions <- coupons %>%
  inner_join(campaign_descriptions) %>%
  inner_join(coupon_redemptions, relationship = 'many-to-many') %>%
  rename(date = redemption_date)
Joining with `by = join_by(campaign_id)`Joining with `by = join_by(coupon_upc, campaign_id)`
# Get every transaction with redemption
sample_df <- redemptions %>%
  left_join(trans1, by = c('household_id','product_id','date'), relationship = 'many-to-many')

colSums(is.na(sample_df))
       coupon_upc        product_id       campaign_id     campaign_type        start_date 
                0                 0                 0                 0                 0 
         end_date      household_id              date          store_id       sales_value 
                0                 0                 0           2167372           2167372 
      retail_disc       coupon_disc coupon_match_disc              week 
          2167372           2167372           2167372           2167372 

Note: There are roughly 2M coupons redemption without transaction records. We got rid of those transactions in further EDA.

# Combine necessary information for transactions having coupon redeemed
transWithRedemptions <- redemptions %>%
  inner_join(trans1, by = c('household_id','product_id','date'), relationship = 'many-to-many') %>%
  arrange(desc(product_id))

# Total sales from transactions with redemption, grouped by product_id and store_id
transWithRedemptions_groupedByProductStoreId <- transWithRedemptions %>%
  group_by(product_id, store_id) %>%
  summarise(total_sales = sum(sales_value))
`summarise()` has grouped output by 'product_id'. You can override using the `.groups` argument.
# Change for shorter name
transWithRedemptions_grouped <- transWithRedemptions_groupedByProductStoreId

#### Check if there is duplicate comb of product_id and store_id in every row
duplicates <- transWithRedemptions_grouped %>%
  dplyr::group_by(product_id, store_id) %>%
  dplyr::summarize(count = n()) %>%
  dplyr::filter(count > 1) %>%

print(duplicates)
`summarise()` has grouped output by 'product_id'. You can override using the `.groups` argument.

Note: There is no duplicate, which is expected.

# Get unique existing product id and store id from the above
product_info_id <- unique(transWithRedemptions_groupedByProductStoreId$product_id)
store_info_id <- unique(transWithRedemptions_groupedByProductStoreId$store_id)

# Get rid of display_location=0 (not display)
promos_display <- promotions %>% 
  select(-(mailer_location:week)) %>% 
  dplyr::distinct(product_id, store_id, display_location, .keep_all = TRUE) %>%
  dplyr::filter(display_location != 0)

# Join display location to transactions with redemption by keys: product_id and store_id
transWithRedemptions_displayLocation <- transWithRedemptions_groupedByProductStoreId %>%
  inner_join(promos_display, by=c('product_id', 'store_id')) %>%
  left_join(products, by='product_id') %>%
  select(-c('manufacturer_id', 'brand','package_size'))

# Get rid of mailer_location=0 (not presented in mail)
promos_mailer <- promotions %>% 
  select(-c(display_location, week)) %>% 
  dplyr::distinct(product_id, store_id, mailer_location, .keep_all = TRUE) %>%
  dplyr::filter(mailer_location != 0)

# Join mailer location to transactions with redemption by keys: product_id and store_id
transWithRedemptions_mailerLocation <- promos_mailer %>%
  inner_join(transWithRedemptions_groupedByProductStoreId, by=c('product_id', 'store_id')) %>%
  left_join(products, by='product_id') %>%
  select(-c('manufacturer_id', 'brand','package_size'))

print(paste("Structure of transactions with redemptions, display location ver:"))
[1] "Structure of transactions with redemptions, display location ver:"
print(transWithRedemptions_displayLocation, n = 5)

print(paste("Structure of transactions with redemptions, mailer location ver:"))
[1] "Structure of transactions with redemptions, mailer location ver:"
print(transWithRedemptions_mailerLocation, n = 5)
# Promotions with display_location = 0, which is not displayed
promos_display0 <- promotions %>% 
  select(-c(mailer_location, week)) %>% 
  dplyr::filter(display_location == 0) %>%
  dplyr::distinct(product_id, store_id, .keep_all = TRUE)
  
# Join display location = 0 to transactions with redemption by keys product_id and store_id
transWithRedemptions_displayLocationIs0 <- transWithRedemptions_grouped %>%
  semi_join(promos_display0, by = c("product_id", "store_id")) %>%
  left_join(products, by='product_id') %>%
  select(-c('manufacturer_id', 'brand','package_size'))

# Promotions with mailer_location = 0, which is not displayed
promos_mailer0 <- promotions %>% 
  select(-c(display_location, week)) %>% 
  dplyr::filter(mailer_location == 0) %>%
  dplyr::distinct(product_id, store_id, .keep_all = TRUE)

# Join mailer location to transactions with redemptions by keys product_id and store_id
transWithRedemptions_mailerLocationIs0 <- transWithRedemptions_grouped %>%
  semi_join(promos_mailer0, by = c("product_id", "store_id")) %>%
  left_join(products, by='product_id') %>%
  select(-c('manufacturer_id', 'brand','package_size'))
print(paste("Structure of transactions with redemptions with no display location:"))
[1] "Structure of transactions with redemptions with no display location:"
print(transWithRedemptions_displayLocationIs0, n = 5)
print(paste("Structure of transactions with redemptions with no mailer location:"))
[1] "Structure of transactions with redemptions with no mailer location:"
print(transWithRedemptions_mailerLocationIs0, n = 5)

Rename datasets for shorter purpose

full_transactions <- transWithRedemptions
trans_w_display <- transWithRedemptions_displayLocation
trans_w_mailer <- transWithRedemptions_mailerLocation
trans_w_display0 <- transWithRedemptions_displayLocationIs0
trans_w_mailer0 <- transWithRedemptions_mailerLocationIs0

Exploratory Data Analysis

Campaign Types Effect on Sales Value

  • Assuming ‘campaign_type’ contains different coupon types

We began by analyzing the performance of each campaign type in terms of redemption counts and total sales value. To do this, we first merged the coupon-related datasets to gather all available coupon information. Next, we joined this with the transactions dataset to isolate those transactions where coupons were redeemed, creating a dataset called full_transactions. We then grouped the data by campaign_type to examine the trend in total sales value and redemption counts.

summary_transactions <- full_transactions %>%
  group_by(campaign_type) %>%
  summarise(
    total_sales = sum(sales_value),
    count_type = n()
)

ggplot(data = summary_transactions, aes(x = campaign_type)) + 
  geom_col(aes(y = count_type, fill = 'Count of campaign type'), width = 0.5) +
  geom_line(aes(y = total_sales, color = 'Total Sales'), group = 1) +
  geom_point(aes(y = total_sales, color = 'Total Sales'), size = 3.0) + 
  scale_y_continuous(
    name = "Count of campaign type",
    breaks = seq(0, max(summary_transactions$count_type) + 1, by = 400),
    sec.axis = sec_axis(~., 
                        name = "Total Sales",
                        breaks = seq(0, max(summary_transactions$total_sales) + 1, by = 400),
                        labels = scales::dollar)
  ) +
  labs(x = 'Campaign Types', title = "Total Sales Value and Count of Redemptions Per Campaign Type") + 
  scale_fill_manual(name = '', values = c("Count of campaign type" = "lightblue")) + 
  scale_color_manual(name = '', values = c("Total Sales" = 'red')) + 
  theme_classic() + 
  theme(
    plot.title = element_text(size = 20),
    axis.title.x = element_text(margin = margin(t = 10), size = 14),
    axis.title.y.left = element_text(margin = margin(r = 10), size = 14),
    axis.title.y.right = element_text(margin = margin(l = 10), size = 14),
    legend.position = 'bottom',
    legend.title = element_text(size = 14),
    legend.text = element_text(size = 14)
  ) +
  annotate("text", x = 1, y = 7603, label = "Exceptionally High", color = "blue", size = 6, vjust = -1)

This analysis revealed that Campaign Type A significantly outperformed other campaigns in both metrics, indicating its effectiveness in driving customer engagement with coupon-related products.

Building on this, we further analyzed the total sales value by department for each campaign to better understand their impact across different product departments.

sample <- full_transactions %>%
  group_by(product_id, campaign_type) %>%
  summarise(total = sum(sales_value), .groups="drop") %>%
  left_join(products, by="product_id") %>%
  select(campaign_type, department, total) %>%
  group_by(campaign_type, department) %>%
  summarise(total_new = sum(total), .groups="drop")
  
ggplot(data = sample, aes(x = campaign_type, y = department, fill = total_new)) +
  geom_tile() +
  scale_fill_gradient2(low="white", 
                       mid="lightblue", 
                       high="blue", 
                       name = "Sales Value",
                       labels = label_dollar()) +
  geom_text(aes(label = label_dollar()(total_new)), color="black", size=4) +
  theme_classic() +
  labs(title = "Distribution of Sales Value by Campaign Types and Departments",
       x = "Campaign Types",
       y = "Deparments") +
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        )

  • The GROCERY department consistently led in total sales value across all campaign types, likely due to the essential nature of these products. For further analysis, we can exclude the ‘GROCERY’ department to focus on the performance of other departments.

  • Campaign Type A consistently drove the highest sales across all departments, suggesting its promotional strategy was highly effective and widely appealing. The strong performance of Type A indicates its potential as a model for future campaigns.

  • Campaign Types B and C showed no sales in several departments, possibly indicating that their promotions were not well-targeted or lacked appeal in certain product categories.

Display Location Analysis

Hypothesis: Products placed at certain display locations, when combined with coupon promotions, will result in the highest number of purchases compared to other display locations.

  • As previously noted, we excluded the GROCERY department from this EDA to focus on other departments.

  • Using the dataset that includes transaction details with display locations, we calculated the total sales value by both display location and department for a more detailed analysis.

sample <- trans_w_display %>%
  dplyr::filter(department != "GROCERY") %>%
  group_by(display_location, department) %>%
  summarize(total = sum(total_sales), .groups = 'drop')

print(sample, n = 5)

a. Stack bar plot with actual sales value for each display location

ggplot(data = sample, aes(x = display_location, y = total, fill = department)) + 
  geom_bar(stat = "identity", position = "stack") +
  scale_fill_manual(values = c("DELI" = "#FF9999", 
                               "DRUG GM" = "#66CC99", 
                               "MEAT-PCKGD" = "#FFCC00", 
                               "NUTRITION" = "#3399FF", 
                               "MEAT" = "#FF66CC", 
                               "PASTRY" = "#FF9966", 
                               "SEAFOOD-PCKGD" = "#99CCFF",
                               "COSMETICS" = "#b3b3b3",
                               "PRODUCE" = "#ff7f00")) +
  scale_x_discrete(labels = display_location_labels) +
  scale_y_continuous(labels = label_dollar()) +
  theme_classic() +
  labs(title = "Total Sales by Department and Display Locations",
       x = "Display Locations",
       y = "Sales Value",
       fill = "Departments") +
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        axis.text.y = element_text(size = 14),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        )

  • With display locations included, we identified 9 distinct departments, with the DRUG GM department consistently having the highest total sales value. The location Store rear exhibited the most significant total sales value across all departments, particularly for MEAT-PCKGD. This pattern suggests that customers are more likely to purchase products displayed at the store rear, especially packaged meat.

  • The COSMETICS department showed notable sales value at the Front end cap but lacked visibility in other locations, possibly due to low exposure in those areas.

  • Although the products dataset contains 32 unique departments, only 9 departments were represented in the graph with transactions involving coupon redemptions. Regork might want to explore further how display locations impact sales value if coupons are introduced for the remaining departments.

To provide a clearer perspective on department contributions, we converted the above graph into a percentage format, highlighting how each department contributed to the sales value at different display locations.

b. Stack bar plot with sales value being normalize to percentage

total_sales_per_display_location <- sample %>%
  group_by(display_location) %>%
  summarise(total_sales_location = sum(total), .groups = "drop")

final_df <- sample %>%
  left_join(total_sales_per_display_location, by = "display_location") %>%
  mutate(percentage = round((total / total_sales_location) * 100,2))

ggplot(final_df, aes(x = display_location, y = percentage, fill = department)) +
  geom_bar(stat = "identity", position = "stack") +
  scale_fill_manual(values = c("DELI" = "#FF9999", 
                               "DRUG GM" = "#66CC99", 
                               "MEAT-PCKGD" = "#FFCC00", 
                               "NUTRITION" = "#3399FF", 
                               "MEAT" = "#FF66CC", 
                               "PASTRY" = "#FF9966", 
                               "SEAFOOD-PCKGD" = "#99CCFF",
                               "COSMETICS" = "#b3b3b3",
                               "PRODUCE" = "#ff7f00")) +
  scale_x_discrete(labels = display_location_labels) +
  scale_y_continuous(labels = scales::percent_format(scale = 1)) +
  theme_classic() +
  labs(title = "Total Sales by Department and Campaign Type in Percentage",
       x = "Display Locations",
       y = "Percentage Sales",
       fill = "Departments") +
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        axis.text.y = element_text(size = 14),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        )

In this percentage view, it was evident that DRUG GM consistently maintained its strong performance across multiple locations. In contrast, PRODUCE had a significant contribution at the In Shelf location but underperformed in others.

To optimize sales, we should consider relocating more PRODUCE products from the underperforming locations or adjusting coupon strategies to boost sales in those areas. This could help distribute sales more evenly and take advantage of the untapped potential in different locations.

Comparisons: Being displayed vs. not displayed - Having coupons redeemed

# Display
sample1 <- trans_w_display %>%
  group_by(department) %>%
  summarize(total = sum(total_sales), .groups = 'drop') %>%
  arrange(desc(department)) %>%
  mutate(type = "Being Displayed")

# Not Display
sample2 <- trans_w_display0 %>%
  group_by(department) %>%
  summarise(total = sum(total_sales)) %>%
  arrange(desc(department)) %>%
  mutate(type = "Not Being Displayed")

combined <- bind_rows(sample1, sample2)
combined
ggplot(data = combined, aes(x = department, y = total, fill = type)) +
  geom_bar(stat = "identity", position = "dodge") +
  scale_y_continuous(breaks = seq(0, max(combined$total) + 1, by = 500),
                     labels = label_dollar()) +
  geom_text(aes(label = scales::label_dollar()(total)), 
            position = position_dodge(width = 0.9), 
            vjust = -1, 
            color = "black") +
  labs(title = "Total Sales Across Departments with Display/Not Display",
       x = "Departments",
       y = "Total Sales",
       fill = "Display Type") +
  theme_classic() + 
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        axis.text.y = element_text(size = 14),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        ) +
  scale_fill_manual(values = c("Being Displayed" = "lightblue", 
                               "Not Being Displayed" = "lightgreen"))

  • Despite not being actively displayed, the GROCERY department continued to lead in total sales value, followed by the DRUG GM department. Notably, the MEAT department, which likely includes fresh meat, appeared to be purchased primarily when not displayed.

  • Another observation is that the TRAVEL & LEISURE department only saw purchases when its products were not displayed, suggesting a unique purchasing behavior in this category.

Mailer Location Analysis

Hypothesis: Customer is likely to redeem coupons if products are placed at interior page feature in mail.

Similar to display locations, we also wanted to learn the total sales value across departments with mailer locations.

We first obtained the data by performing grouping and summary:

sample <- trans_w_mailer %>%
  dplyr::filter(department != "GROCERY") %>%
  group_by(mailer_location, department) %>%
  summarise(total = sum(total_sales), .groups = "drop") %>%
  arrange(desc(total))

print(sample, n=5)

a. Stack bar plot with actual sales value for each mailer location

ggplot(data = sample, aes(x = mailer_location, y = total, fill = department)) +
  geom_bar(stat = "identity", position = "stack") +
  scale_x_discrete(labels = mailer_location_labels) + 
  scale_fill_manual(values = c("DRUG GM" = "#8da0cb",
                               "MEAT" = "#a6d854",         
                               "MEAT-PCKGD" = "#e78ac3",   
                               "PRODUCE" = "#66c2a5",
                               "SEAFOOD-PCKGD" = "#fb9a99",
                               "DELI" = "#e5c494",          
                               "SEAFOOD" = "#b3b3b3",      
                               "NUTRITION" = "#ffd92f",     
                               "COSMETICS" = "#33a02c",    
                               "COUPON" = "#ff7f00",
                               "PASTRY" = "#1f78b4",       
                               "TRAVEL & LEISURE" = "#e31a1c")) +
  scale_y_continuous(breaks = seq(0, 3500, by = 500),
                     labels = label_dollar()) +
  theme_classic() + 
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        axis.text.y = element_text(size = 14),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        ) +
  labs(title = "Total Sales by Department and Mailer Locations",
       x = "Mailer Locations",
       y = "Total Sale",
       fill = "Departments")

  • With mailer locations, more departments were represented compared to display locations, but MEAT and MEAT-PCKGD still dominated in total sales value across various locations. Interestingly, DRUG GM now concentrated its sales value primarily on the Interior page feature, unlike the broader distribution seen in display locations.

  • As noted earlier, while the products dataset includes 32 unique departments, only 13 were reflected in this analysis. Regork may want to explore opportunities to promote the remaining departments using coupons in mailer locations.

  • The TRAVEL & LEISURE department, which wasn’t seen in the display locations analysis, showed a small sales value across three different mailer locations. It may be worth conducting a survey to determine whether increasing this department’s presence in mailers, particularly in the Interior page feature, could boost its sales value.

b. Stack bar plot with sales value being normalize to percentage

total_sales_per_mailer_location <- sample %>%
  group_by(mailer_location) %>%
  summarise(total_sales_location = sum(total), .groups = "drop")

final_df <- sample %>%
  left_join(total_sales_per_mailer_location, by = "mailer_location") %>%
  mutate(percentage = round((total / total_sales_location) * 100,2))

ggplot(final_df, aes(x = mailer_location, y = percentage, fill = department)) +
  geom_bar(stat = "identity", position = "stack") +
  scale_fill_manual(values = c("DRUG GM" = "#8da0cb",
                               "MEAT" = "#a6d854",         
                               "MEAT-PCKGD" = "#e78ac3",   
                               "PRODUCE" = "#66c2a5",
                               "SEAFOOD-PCKGD" = "#fb9a99",
                               "DELI" = "#e5c494",          
                               "SEAFOOD" = "#b3b3b3",      
                               "NUTRITION" = "#ffd92f",     
                               "COSMETICS" = "#33a02c",    
                               "COUPON" = "#ff7f00",
                               "PASTRY" = "#1f78b4",       
                               "TRAVEL & LEISURE" = "#e31a1c")) +
  scale_x_discrete(labels = mailer_location_labels) +
  theme_classic() +
  scale_y_continuous(expand = c(0,2),
                     labels = scales::percent_format(scale = 1)) +
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        axis.text.y = element_text(size = 14),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        ) +
  labs(title = "Total Sales by Department and Campaign Type in Percentage",
       x = "Mailer Locations",
       y = "Percentage Sales",
       fill = "Departments")

  • With percentage view, DELI department contribution to sales value was seen more clearly now. There were too many departments being presented at Interior page feature. We can try to minimize this and further study the concentration of each department on specific mailer location.

Comparisons: Being presented and not presented in mail - Having coupons redeemed

# Mail presented
sample1 <- trans_w_mailer %>%
  dplyr::filter(department!="GROCERY") %>%
  group_by(department) %>%
  summarize(total = sum(total_sales), .groups = 'drop') %>%
  arrange(desc(department)) %>%
  mutate(type = "Mail Presented")

# Not Mail presented
sample2 <- trans_w_mailer0 %>%
  dplyr::filter(department != "GROCERY") %>%
  group_by(department) %>%
  summarise(total = sum(total_sales)) %>%
  arrange(desc(department)) %>%
  mutate(type = "Not Mail Presented")

combined <- bind_rows(sample1, sample2)
combined
ggplot(data = combined, aes(x = department, y = total, fill = type)) +
  geom_bar(stat = "identity", position = "dodge") +  
  scale_y_continuous(breaks = seq(0, max(combined$total) + 100, by = 500),
                     labels = label_dollar()) +
  geom_text(aes(label = scales::label_dollar()(total)), 
            position = position_dodge(width = 0.9), 
            vjust = -1, 
            color = "black") +
  labs(title = "Total Sales Across Departments with Mail Presence",
       x = "Departments",
       y = "Total Sales",
       fill = "Mail Presence Type") +
  theme_classic() + 
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        axis.text.y = element_text(size = 14),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        ) +
  scale_fill_manual(values = c("Mail Presented" = "lightblue", 
                               "Not Mail Presented" = "lightgreen"))

It is evident that no department generated higher sales value without being featured in the mail. Notably, COUPON and TRAVEL & LEISURE were the only two departments that saw no sales when not included in mail promotions. In contrast to the display location analysis, the MEAT department showed a significant increase in sales when featured in mailers, with sales approximately 200 times higher than not being displayed. This underscores the importance of ensuring that MEAT continues to be prominently featured in mail campaigns.

Display vs. Mailer Promotions

Next, we aimed to compare the sales values between departments based on their presentation type: display location and mailer location. This analysis will help us identify which promotional strategy—display or mailer—is more effective for each department, allowing us to better understand how presentation type influences customer purchasing behavior and overall sales performance across departments.

mailer_promo <- trans_w_mailer %>%
  select(department, total_sales) %>%
  group_by(department) %>%
  summarise(total = sum(total_sales), .groups="drop") %>%
  mutate(promo_type = "Mail")

display_promo <- trans_w_display %>%
  ungroup() %>%
  select(department, total_sales) %>%
  group_by(department) %>%
  summarise(total = sum(total_sales), .groups="drop") %>%
  mutate(promo_type = "Display")

summary_data <- bind_rows(mailer_promo, display_promo)

ggplot(data = summary_data, aes(x = department, y = total, color = promo_type)) +
  geom_point(alpha = 0.6, size = 10) +
  geom_segment(aes(xend = department, yend = 0), linetype = "dotted", linewidth = 1.5) + 
  labs(title = "Sales Comparison Between Display and Mailer Promotions",
       x = "Department",
       y = "Total Sales",
       color = "Presented Type") +
  scale_y_continuous(breaks = seq(0, max(summary_data$total) + 1, by = 300),
                     labels = label_dollar()) +
  theme_classic() +
  theme(plot.title = element_text(size = 20),
        axis.title.x = element_text(margin = margin(t = 10), size = 14),
        axis.title.y = element_text(margin = margin(r = 10), size = 14),
        axis.text.y = element_text(size = 12),
        legend.title = element_text(size = 14),
        legend.text = element_text(size = 14)
        )

Nevertheless, the GROCERY department remained the top performer across all promotion types. However, it appears that presenting promotions through mail generally led to higher sales. This suggests that customers tend to purchase and redeem coupons more frequently when they see promotions in the mail, indicating a shift in consumer behavior toward online shopping.

Regork could consider conducting a study on online shopping behavior and specifically test the effectiveness of Type A promotion campaigns via mailing.

Summary

1. Insights

  • The Type A promotion campaign proved to be the most effective promotional strategy, generating over $7,600 in sales value and approximately 2,400 redemption counts across all departments. This success suggests that Type A can serve as a model for future marketing campaigns. However, there are departments that were not included in this campaign. We recommend conducting further analysis to determine how well Type A strategies could be applied to those departments and whether it could lead to an increase in their sales value.

  • Additionally, Type B and Type C campaigns did not generate sales from several departments. This warrants further investigation into the factors that may have contributed to this lack of sales, which could help refine future promotional strategies and increase their effectiveness.

a. Display Locations

  • In the analysis of display locations, the MEAT-PCKGD department had significantly higher sales value at the Store rear compared to other locations. This suggests that Regork might want to consider placing more products from the MEAT-PCKGD department in the store rear. A similar strategy could be applied to the PRODUCE department, where placing products in the In shelf location could boost sales.

  • When comparing sales value between displayed and non-displayed products, the results were surprising. Except for GROCERY, MEAT had significantly higher sales value when not displayed compared to when it was displayed. This trend was also observed in the PRODUCE and TRAVEL & LEISURE departments, with the latter not appearing in the display location analysis. We recommend running a campaign that places TRAVEL & LEISURE products in display locations with coupons to see if this generates a noticeable increase in sales value.

b. Mailer Locations

  • In the mailer locations analysis, more departments showed sales value compared to the display locations. Specifically, departments like TRAVEL & LEISURE and DELI, which were not seen in the display location analysis, became more prominent when looking at the percentage view. The total sales value in mail locations was generally higher than in display locations, suggesting a shift in consumer behavior toward online shopping. Regork may want to consider investing further in technologies that support online promotions to capitalize on this trend and drive higher revenue.

  • A comparison between mail presence and no mail presence indicates that sales value was significantly higher when promotions were presented via mail. This approach should be continued, and further campaign strategies should be developed to optimize the effectiveness of this promotional method.

c. Display vs. Mailer

  • As noted earlier, there has been a noticeable shift in customer behavior towards online shopping. This is a key area where Regork can further invest. By analyzing the impact of mailer locations, the marketing team at Regork can study how to integrate departments that were not previously included in promotions. This could present opportunities to increase sales value for those departments and offer coupons for their products, potentially driving higher engagement and revenue.

2. Limitations

  • We have not yet considered the impact of time frames on these promotional campaigns. Future work should include an analysis of how the timing of promotions influences sales performance.

  • The assumption made at the beginning of the report, that all transactions with any discount are redeemed coupons, has yet to be confirmed. Further validation of this assumption is necessary to ensure the accuracy of the analysis.

  • There are approximately 2 million coupon redemptions without corresponding transaction records. Collecting more data on these redemptions could provide valuable insights and potentially affect the total sales value of the different campaign types.

LS0tCnRpdGxlOgpvdXRwdXQ6CiAgaHRtbF9ub3RlYm9vazogZGVmYXVsdAogIGh0bWxfZG9jdW1lbnQ6CiAgICB0aGVtZTogY2VydWxlYW4KICAgIHRvYzogdHJ1ZQogICAgdG9jX2RlcHRoOiAyCi0tLQojIEdyb3VwIDYgLSBNaWR0ZXJtIFByb2plY3Qgey50YWJzZXR9CgojIyBJbnRyb2R1Y3Rpb24KClRoZSBidXNpbmVzcyBwcm9ibGVtIHdlIGFkZHJlc3NlZCBpcyBob3cgZGlmZmVyZW50IHR5cGVzIG9mIHByb21vdGlvbiBjYW1wYWlnbnMgYWZmZWN0IHNhbGVzIHZhbHVlIGFjcm9zcyB2YXJpb3VzIHByb2R1Y3QgZGVwYXJ0bWVudHMgYW5kIGRpc3BsYXkvbWFpbGVyIGxvY2F0aW9ucyBhdCBSZWdvcmsuIFVuZGVyc3RhbmRpbmcgd2hpY2ggY2FtcGFpZ24gdHlwZXMgYW5kIGxvY2F0aW9ucyBsZWFkaW5nIHRvIGhpZ2hlciBzYWxlcyB2YWx1ZSBpcyBjcnVjaWFsIGZvciBvcHRpbWl6aW5nIHByb21vdGlvbiBzdHJhdGVnaWVzLCBpbXByb3ZpbmcgY3VzdG9tZXIgZW5nYWdlbWVudCwgYW5kIGJvb3N0aW5nIHJldmVudWUuCgpXZSB1dGlsaXplZCBzYWxlcyB0cmFuc2FjdGlvbiBkYXRhLCBmb2N1c2luZyBvbiBjb3Vwb24gdHlwZXMgYW5kIHRoZWlyIHJlZGVtcHRpb24gcmF0ZXMsIGNhbXBhaWduIGRpc3BsYXkgbG9jYXRpb25zLCBhbmQgdG90YWwgc2FsZXMgYWNyb3NzIGRpZmZlcmVudCBkZXBhcnRtZW50cy4gVGhlIG1ldGhvZG9sb2d5IGluY2x1ZGVkIGFuYWx5emluZyBzYWxlcyB2YWx1ZSBkaXN0cmlidXRpb25zIGFjcm9zcyB0aGVzZSB2YXJpYWJsZXMgdG8gaWRlbnRpZnkgcGF0dGVybnMgaW4gY29uc3VtZXIgcHVyY2hhc2VzLgoKVGhpcyBhbmFseXNpcyBvZmZlcnMgYWN0aW9uYWJsZSBpbnNpZ2h0cywgc3VjaCBhcyB3aGljaCBjYW1wYWlnbiB0eXBlcyBkcml2ZSB0aGUgbW9zdCBzYWxlcyBhbmQgd2hpY2ggbG9jYXRpb25zIGFyZSBtb3N0IGVmZmVjdGl2ZS4gVGhpcyBjYW4gaGVscCBSZWdvcmsgc3Rha2Vob2xkZXJzIG1ha2UgZGF0YS1kcml2ZW4gZGVjaXNpb25zIG9uIGZ1dHVyZSBwcm9tb3Rpb25zLCBpbXByb3ZlIHJlc291cmNlIGFsbG9jYXRpb24sIGFuZCBlbmhhbmNlIG92ZXJhbGwgc2FsZXMgc3RyYXRlZ2llcywgZXNwZWNpYWxseSBieSBmb2N1c2luZyBvbiBoaWdoLXBlcmZvcm1pbmcgYXJlYXMgbGlrZSBncm9jZXJ5IG9yIGRydWdzdG9yZSBkZXBhcnRtZW50cy4KCiMjIFBhY2thZ2VzIFJlcXVpcmVkCmBgYHtyfQpsaWJyYXJ5KHRpZHl2ZXJzZSkgICAgICAgICAgICAgICAjIGNvbGxlY3Rpb24gb2YgUiBwYWNrYWdlcyBmb3IgZGF0YSBtYW5pcHVsYXRpb24gYW5kIHZpc3VhbGl6YXRpb24KbGlicmFyeShoZXJlKSAgICAgICAgICAgICAgICAgICAgIyBzaW1wbGlmaWVzIGZpbGUgcGF0aCBtYW5hZ2VtZW50IGluIHByb2plY3RzCmxpYnJhcnkobHVicmlkYXRlKSAgICAgICAgICAgICAgICMgdG9vbHMgZm9yIHdvcmtpbmcgd2l0aCBkYXRlIGFuZCB0aW1lIGRhdGEKbGlicmFyeShnZ3Bsb3QyKSAgICAgICAgICAgICAgICAgIyBkYXRhIHZpc3VhbGl6YXRpb24gdXNpbmcgIkdyYW1tYXIgb2YgR3JhcGhpY3MiCmxpYnJhcnkocmVhZHIpICAgICAgICAgICAgICAgICAgICMgZmFzdCByZWFkaW5nIGFuZCB3cml0aW5nIG9mIGRhdGEgZmlsZXMKbGlicmFyeShzY2FsZXMpICAgICAgICAgICAgICAgICAgIyBwcm92aWRlcyBmdW5jdGlvbnMgZm9yIHNjYWxpbmcgYW5kIGZvcm1hdHRpbmcKbGlicmFyeShjb21wbGV0ZWpvdXJuZXkpCmBgYAojIyBEYXRhIFByZXBhcmF0aW9uIHsudGFic2V0fQoKIyMjIFNldHVwCmBgYHtyfQpjKHByb21vdGlvbnMsIHRyYW5zYWN0aW9ucykgJTwtJSBnZXRfZGF0YSh3aGljaCA9ICdib3RoJywgdmVyYm9zZSA9IEZBTFNFKQoKIyBFeHRyYWN0IHRoZSBkYXRlIGZvciB0cmFuc2FjdGlvbnMgYW5kIGNoYW5nZSBkYXRhIHR5cGUgZm9yIHByb2R1Y3RfaWQKdHJhbnNhY3Rpb25zIDwtIHRyYW5zYWN0aW9ucyAlPiUgbXV0YXRlKGRhdGUgPSBhcy5EYXRlKHRyYW5zYWN0aW9uX3RpbWVzdGFtcCkpCnRyYW5zYWN0aW9ucyRwcm9kdWN0X2lkIDwtIGFzLmRvdWJsZSh0cmFuc2FjdGlvbnMkcHJvZHVjdF9pZCkKcHJvbW90aW9ucyRwcm9kdWN0X2lkIDwtIGFzLmRvdWJsZShwcm9tb3Rpb25zJHByb2R1Y3RfaWQpCnByb2R1Y3RzJHByb2R1Y3RfaWQgPC0gYXMuZG91YmxlKHByb2R1Y3RzJHByb2R1Y3RfaWQpCmNvdXBvbnMkcHJvZHVjdF9pZCA8LSBhcy5kb3VibGUoY291cG9ucyRwcm9kdWN0X2lkKQoKIyBWZWN0b3JzIG9mIGRpc3BsYXkgYW5kIG1haWxlciBsb2NhdGlvbnMgdG8gYWN0dWFsIG5hbWVzCmRpc3BsYXlfbG9jYXRpb25fbGFiZWxzIDwtIGMoIjAiPSJObyBkaXNwbGF5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjEiPSJTdG9yZSBmcm9udCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIyIj0iU3RvcmUgcmVhciIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICIzIj0iRnJvbnQgZW5kIGNhcCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0Ij0iTWlkLWFpc2xlIGVuZCBjYXAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiNSI9IlJlYXIgZW5kIGNhcCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI2Ij0iU2lkZSBhaXNsZSBlbmQgY2FwIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIjciPSJJbi1haXNsZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICI5Ij0iU2Vjb25kYXJ5IGxvY2F0aW9uIGRpc3BsYXkiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQSI9IkluLXNoZWxmIikKCm1haWxlcl9sb2NhdGlvbl9sYWJlbHMgPC0gYygiMCI9Ik5vdCBvbiBhZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkEiPSJJbnRlcmlvciBwYWdlIGZlYXR1cmUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDIj0iSW50ZXJpb3IgcGFnZSBsaW5lIGl0ZW0iLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJEIj0iRnJvbnQgcGFnZSBmZWF0dXJlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRiI9IkJhY2sgcGFnZSBmZWF0dXJlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAiSCI9IldyYXAgZnJvbnQgZmVhdHVyZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkoiPSJXcmFwIGludGVyaW9yIGNvdXBvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIkwiPSJXcmFwIGJhY2sgZmVhdHVyZSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlAiPSJJbnRlcmlvciBwYWdlIGNvdXBvbiIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgIlgiPSJGcmVlIG9uIGludGVyaW9yIHBhZ2UiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICJaIj0iRnJlZSBvbiBmcm9udCBwYWdlL2JhY2sgcGFnZS9vciB3cmFwIikKCmBgYAoKIyMjIERhdGEgVHJhbnNmb3JtYXRpb24KQXNzdW1wdGlvbjogdHJhbnNhY3Rpb25zIHdpdGggYW55IGRpc2NvdW50IGFyZSByZWRlZW1lZCBjb3Vwb25zCmBgYHtyfQojIFNlbGVjdCBvbmx5IHJlbGV2YW50IGNvbHVtbnMgaW4gdHJhbnNhY3Rpb25zCnRyYW5zMSA8LSB0cmFuc2FjdGlvbnMgJT4lIAogIGRwbHlyOjpmaWx0ZXIocmV0YWlsX2Rpc2M+MCB8IGNvdXBvbl9kaXNjPjAgfCBjb3Vwb25fbWF0Y2hfZGlzYz4wKSAlPiUKICBzZWxlY3QoaG91c2Vob2xkX2lkLCBzdG9yZV9pZCwgcHJvZHVjdF9pZCwgc2FsZXNfdmFsdWU6Y291cG9uX21hdGNoX2Rpc2MsIHdlZWssIGRhdGUpCgojIEpvaW4gY291cG9ucywgY291cG9uX3JlZGVtcHRpb25zLCBjYW1wYWlnbl9kZXNjcmlwdGlvbiB0byBnZXQgZnVsbCBpbmZvcm1hdGlvbiBvZiBjb3Vwb25zCnJlZGVtcHRpb25zIDwtIGNvdXBvbnMgJT4lCiAgaW5uZXJfam9pbihjYW1wYWlnbl9kZXNjcmlwdGlvbnMpICU+JQogIGlubmVyX2pvaW4oY291cG9uX3JlZGVtcHRpb25zLCByZWxhdGlvbnNoaXAgPSAnbWFueS10by1tYW55JykgJT4lCiAgcmVuYW1lKGRhdGUgPSByZWRlbXB0aW9uX2RhdGUpCgojIEdldCBldmVyeSB0cmFuc2FjdGlvbiB3aXRoIHJlZGVtcHRpb24Kc2FtcGxlX2RmIDwtIHJlZGVtcHRpb25zICU+JQogIGxlZnRfam9pbih0cmFuczEsIGJ5ID0gYygnaG91c2Vob2xkX2lkJywncHJvZHVjdF9pZCcsJ2RhdGUnKSwgcmVsYXRpb25zaGlwID0gJ21hbnktdG8tbWFueScpCgpjb2xTdW1zKGlzLm5hKHNhbXBsZV9kZikpCmBgYApOb3RlOiBUaGVyZSBhcmUgcm91Z2hseSAyTSBjb3Vwb25zIHJlZGVtcHRpb24gd2l0aG91dCB0cmFuc2FjdGlvbiByZWNvcmRzLiBXZSBnb3QgcmlkIG9mIHRob3NlIHRyYW5zYWN0aW9ucyBpbiBmdXJ0aGVyIEVEQS4KCmBgYHtyfQojIENvbWJpbmUgbmVjZXNzYXJ5IGluZm9ybWF0aW9uIGZvciB0cmFuc2FjdGlvbnMgaGF2aW5nIGNvdXBvbiByZWRlZW1lZAp0cmFuc1dpdGhSZWRlbXB0aW9ucyA8LSByZWRlbXB0aW9ucyAlPiUKICBpbm5lcl9qb2luKHRyYW5zMSwgYnkgPSBjKCdob3VzZWhvbGRfaWQnLCdwcm9kdWN0X2lkJywnZGF0ZScpLCByZWxhdGlvbnNoaXAgPSAnbWFueS10by1tYW55JykgJT4lCiAgYXJyYW5nZShkZXNjKHByb2R1Y3RfaWQpKQoKIyBUb3RhbCBzYWxlcyBmcm9tIHRyYW5zYWN0aW9ucyB3aXRoIHJlZGVtcHRpb24sIGdyb3VwZWQgYnkgcHJvZHVjdF9pZCBhbmQgc3RvcmVfaWQKdHJhbnNXaXRoUmVkZW1wdGlvbnNfZ3JvdXBlZEJ5UHJvZHVjdFN0b3JlSWQgPC0gdHJhbnNXaXRoUmVkZW1wdGlvbnMgJT4lCiAgZ3JvdXBfYnkocHJvZHVjdF9pZCwgc3RvcmVfaWQpICU+JQogIHN1bW1hcmlzZSh0b3RhbF9zYWxlcyA9IHN1bShzYWxlc192YWx1ZSkpCgojIENoYW5nZSBmb3Igc2hvcnRlciBuYW1lCnRyYW5zV2l0aFJlZGVtcHRpb25zX2dyb3VwZWQgPC0gdHJhbnNXaXRoUmVkZW1wdGlvbnNfZ3JvdXBlZEJ5UHJvZHVjdFN0b3JlSWQKCiMjIyMgQ2hlY2sgaWYgdGhlcmUgaXMgZHVwbGljYXRlIGNvbWIgb2YgcHJvZHVjdF9pZCBhbmQgc3RvcmVfaWQgaW4gZXZlcnkgcm93CmR1cGxpY2F0ZXMgPC0gdHJhbnNXaXRoUmVkZW1wdGlvbnNfZ3JvdXBlZCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkocHJvZHVjdF9pZCwgc3RvcmVfaWQpICU+JQogIGRwbHlyOjpzdW1tYXJpemUoY291bnQgPSBuKCkpICU+JQogIGRwbHlyOjpmaWx0ZXIoY291bnQgPiAxKSAlPiUKCnByaW50KGR1cGxpY2F0ZXMpCmBgYApOb3RlOiBUaGVyZSBpcyBubyBkdXBsaWNhdGUsIHdoaWNoIGlzIGV4cGVjdGVkLgoKYGBge3J9CiMgR2V0IHVuaXF1ZSBleGlzdGluZyBwcm9kdWN0IGlkIGFuZCBzdG9yZSBpZCBmcm9tIHRoZSBhYm92ZQpwcm9kdWN0X2luZm9faWQgPC0gdW5pcXVlKHRyYW5zV2l0aFJlZGVtcHRpb25zX2dyb3VwZWRCeVByb2R1Y3RTdG9yZUlkJHByb2R1Y3RfaWQpCnN0b3JlX2luZm9faWQgPC0gdW5pcXVlKHRyYW5zV2l0aFJlZGVtcHRpb25zX2dyb3VwZWRCeVByb2R1Y3RTdG9yZUlkJHN0b3JlX2lkKQoKIyBHZXQgcmlkIG9mIGRpc3BsYXlfbG9jYXRpb249MCAobm90IGRpc3BsYXkpCnByb21vc19kaXNwbGF5IDwtIHByb21vdGlvbnMgJT4lIAogIHNlbGVjdCgtKG1haWxlcl9sb2NhdGlvbjp3ZWVrKSkgJT4lIAogIGRwbHlyOjpkaXN0aW5jdChwcm9kdWN0X2lkLCBzdG9yZV9pZCwgZGlzcGxheV9sb2NhdGlvbiwgLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgZHBseXI6OmZpbHRlcihkaXNwbGF5X2xvY2F0aW9uICE9IDApCgojIEpvaW4gZGlzcGxheSBsb2NhdGlvbiB0byB0cmFuc2FjdGlvbnMgd2l0aCByZWRlbXB0aW9uIGJ5IGtleXM6IHByb2R1Y3RfaWQgYW5kIHN0b3JlX2lkCnRyYW5zV2l0aFJlZGVtcHRpb25zX2Rpc3BsYXlMb2NhdGlvbiA8LSB0cmFuc1dpdGhSZWRlbXB0aW9uc19ncm91cGVkQnlQcm9kdWN0U3RvcmVJZCAlPiUKICBpbm5lcl9qb2luKHByb21vc19kaXNwbGF5LCBieT1jKCdwcm9kdWN0X2lkJywgJ3N0b3JlX2lkJykpICU+JQogIGxlZnRfam9pbihwcm9kdWN0cywgYnk9J3Byb2R1Y3RfaWQnKSAlPiUKICBzZWxlY3QoLWMoJ21hbnVmYWN0dXJlcl9pZCcsICdicmFuZCcsJ3BhY2thZ2Vfc2l6ZScpKQoKIyBHZXQgcmlkIG9mIG1haWxlcl9sb2NhdGlvbj0wIChub3QgcHJlc2VudGVkIGluIG1haWwpCnByb21vc19tYWlsZXIgPC0gcHJvbW90aW9ucyAlPiUgCiAgc2VsZWN0KC1jKGRpc3BsYXlfbG9jYXRpb24sIHdlZWspKSAlPiUgCiAgZHBseXI6OmRpc3RpbmN0KHByb2R1Y3RfaWQsIHN0b3JlX2lkLCBtYWlsZXJfbG9jYXRpb24sIC5rZWVwX2FsbCA9IFRSVUUpICU+JQogIGRwbHlyOjpmaWx0ZXIobWFpbGVyX2xvY2F0aW9uICE9IDApCgojIEpvaW4gbWFpbGVyIGxvY2F0aW9uIHRvIHRyYW5zYWN0aW9ucyB3aXRoIHJlZGVtcHRpb24gYnkga2V5czogcHJvZHVjdF9pZCBhbmQgc3RvcmVfaWQKdHJhbnNXaXRoUmVkZW1wdGlvbnNfbWFpbGVyTG9jYXRpb24gPC0gcHJvbW9zX21haWxlciAlPiUKICBpbm5lcl9qb2luKHRyYW5zV2l0aFJlZGVtcHRpb25zX2dyb3VwZWRCeVByb2R1Y3RTdG9yZUlkLCBieT1jKCdwcm9kdWN0X2lkJywgJ3N0b3JlX2lkJykpICU+JQogIGxlZnRfam9pbihwcm9kdWN0cywgYnk9J3Byb2R1Y3RfaWQnKSAlPiUKICBzZWxlY3QoLWMoJ21hbnVmYWN0dXJlcl9pZCcsICdicmFuZCcsJ3BhY2thZ2Vfc2l6ZScpKQoKcHJpbnQocGFzdGUoIlN0cnVjdHVyZSBvZiB0cmFuc2FjdGlvbnMgd2l0aCByZWRlbXB0aW9ucywgZGlzcGxheSBsb2NhdGlvbiB2ZXI6IikpCnByaW50KHRyYW5zV2l0aFJlZGVtcHRpb25zX2Rpc3BsYXlMb2NhdGlvbiwgbiA9IDUpCgpwcmludChwYXN0ZSgiU3RydWN0dXJlIG9mIHRyYW5zYWN0aW9ucyB3aXRoIHJlZGVtcHRpb25zLCBtYWlsZXIgbG9jYXRpb24gdmVyOiIpKQpwcmludCh0cmFuc1dpdGhSZWRlbXB0aW9uc19tYWlsZXJMb2NhdGlvbiwgbiA9IDUpCmBgYAoKYGBge3J9CiMgUHJvbW90aW9ucyB3aXRoIGRpc3BsYXlfbG9jYXRpb24gPSAwLCB3aGljaCBpcyBub3QgZGlzcGxheWVkCnByb21vc19kaXNwbGF5MCA8LSBwcm9tb3Rpb25zICU+JSAKICBzZWxlY3QoLWMobWFpbGVyX2xvY2F0aW9uLCB3ZWVrKSkgJT4lIAogIGRwbHlyOjpmaWx0ZXIoZGlzcGxheV9sb2NhdGlvbiA9PSAwKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QocHJvZHVjdF9pZCwgc3RvcmVfaWQsIC5rZWVwX2FsbCA9IFRSVUUpCiAgCiMgSm9pbiBkaXNwbGF5IGxvY2F0aW9uID0gMCB0byB0cmFuc2FjdGlvbnMgd2l0aCByZWRlbXB0aW9uIGJ5IGtleXMgcHJvZHVjdF9pZCBhbmQgc3RvcmVfaWQKdHJhbnNXaXRoUmVkZW1wdGlvbnNfZGlzcGxheUxvY2F0aW9uSXMwIDwtIHRyYW5zV2l0aFJlZGVtcHRpb25zX2dyb3VwZWQgJT4lCiAgc2VtaV9qb2luKHByb21vc19kaXNwbGF5MCwgYnkgPSBjKCJwcm9kdWN0X2lkIiwgInN0b3JlX2lkIikpICU+JQogIGxlZnRfam9pbihwcm9kdWN0cywgYnk9J3Byb2R1Y3RfaWQnKSAlPiUKICBzZWxlY3QoLWMoJ21hbnVmYWN0dXJlcl9pZCcsICdicmFuZCcsJ3BhY2thZ2Vfc2l6ZScpKQoKIyBQcm9tb3Rpb25zIHdpdGggbWFpbGVyX2xvY2F0aW9uID0gMCwgd2hpY2ggaXMgbm90IGRpc3BsYXllZApwcm9tb3NfbWFpbGVyMCA8LSBwcm9tb3Rpb25zICU+JSAKICBzZWxlY3QoLWMoZGlzcGxheV9sb2NhdGlvbiwgd2VlaykpICU+JSAKICBkcGx5cjo6ZmlsdGVyKG1haWxlcl9sb2NhdGlvbiA9PSAwKSAlPiUKICBkcGx5cjo6ZGlzdGluY3QocHJvZHVjdF9pZCwgc3RvcmVfaWQsIC5rZWVwX2FsbCA9IFRSVUUpCgojIEpvaW4gbWFpbGVyIGxvY2F0aW9uIHRvIHRyYW5zYWN0aW9ucyB3aXRoIHJlZGVtcHRpb25zIGJ5IGtleXMgcHJvZHVjdF9pZCBhbmQgc3RvcmVfaWQKdHJhbnNXaXRoUmVkZW1wdGlvbnNfbWFpbGVyTG9jYXRpb25JczAgPC0gdHJhbnNXaXRoUmVkZW1wdGlvbnNfZ3JvdXBlZCAlPiUKICBzZW1pX2pvaW4ocHJvbW9zX21haWxlcjAsIGJ5ID0gYygicHJvZHVjdF9pZCIsICJzdG9yZV9pZCIpKSAlPiUKICBsZWZ0X2pvaW4ocHJvZHVjdHMsIGJ5PSdwcm9kdWN0X2lkJykgJT4lCiAgc2VsZWN0KC1jKCdtYW51ZmFjdHVyZXJfaWQnLCAnYnJhbmQnLCdwYWNrYWdlX3NpemUnKSkKYGBgCgpgYGB7cn0KcHJpbnQocGFzdGUoIlN0cnVjdHVyZSBvZiB0cmFuc2FjdGlvbnMgd2l0aCByZWRlbXB0aW9ucyB3aXRoIG5vIGRpc3BsYXkgbG9jYXRpb246IikpCnByaW50KHRyYW5zV2l0aFJlZGVtcHRpb25zX2Rpc3BsYXlMb2NhdGlvbklzMCwgbiA9IDUpCnByaW50KHBhc3RlKCJTdHJ1Y3R1cmUgb2YgdHJhbnNhY3Rpb25zIHdpdGggcmVkZW1wdGlvbnMgd2l0aCBubyBtYWlsZXIgbG9jYXRpb246IikpCnByaW50KHRyYW5zV2l0aFJlZGVtcHRpb25zX21haWxlckxvY2F0aW9uSXMwLCBuID0gNSkKYGBgCgoKCgoKCiMjIyBSZW5hbWUgZGF0YXNldHMgZm9yIHNob3J0ZXIgcHVycG9zZQpgYGB7cn0KZnVsbF90cmFuc2FjdGlvbnMgPC0gdHJhbnNXaXRoUmVkZW1wdGlvbnMKdHJhbnNfd19kaXNwbGF5IDwtIHRyYW5zV2l0aFJlZGVtcHRpb25zX2Rpc3BsYXlMb2NhdGlvbgp0cmFuc193X21haWxlciA8LSB0cmFuc1dpdGhSZWRlbXB0aW9uc19tYWlsZXJMb2NhdGlvbgp0cmFuc193X2Rpc3BsYXkwIDwtIHRyYW5zV2l0aFJlZGVtcHRpb25zX2Rpc3BsYXlMb2NhdGlvbklzMAp0cmFuc193X21haWxlcjAgPC0gdHJhbnNXaXRoUmVkZW1wdGlvbnNfbWFpbGVyTG9jYXRpb25JczAKYGBgCgojIyBFeHBsb3JhdG9yeSBEYXRhIEFuYWx5c2lzIHsudGFic2V0fQojIyMgQ2FtcGFpZ24gVHlwZXMgRWZmZWN0IG9uIFNhbGVzIFZhbHVlCi0gQXNzdW1pbmcgJ2NhbXBhaWduX3R5cGUnIGNvbnRhaW5zIGRpZmZlcmVudCBjb3Vwb24gdHlwZXMKCldlIGJlZ2FuIGJ5IGFuYWx5emluZyB0aGUgcGVyZm9ybWFuY2Ugb2YgZWFjaCBjYW1wYWlnbiB0eXBlIGluIHRlcm1zIG9mIHJlZGVtcHRpb24gY291bnRzIGFuZCB0b3RhbCBzYWxlcyB2YWx1ZS4gVG8gZG8gdGhpcywgd2UgZmlyc3QgbWVyZ2VkIHRoZSBjb3Vwb24tcmVsYXRlZCBkYXRhc2V0cyB0byBnYXRoZXIgYWxsIGF2YWlsYWJsZSBjb3Vwb24gaW5mb3JtYXRpb24uIE5leHQsIHdlIGpvaW5lZCB0aGlzIHdpdGggdGhlIHRyYW5zYWN0aW9ucyBkYXRhc2V0IHRvIGlzb2xhdGUgdGhvc2UgdHJhbnNhY3Rpb25zIHdoZXJlIGNvdXBvbnMgd2VyZSByZWRlZW1lZCwgY3JlYXRpbmcgYSBkYXRhc2V0IGNhbGxlZCBgZnVsbF90cmFuc2FjdGlvbnMuYCBXZSB0aGVuIGdyb3VwZWQgdGhlIGRhdGEgYnkgYGNhbXBhaWduX3R5cGVgIHRvIGV4YW1pbmUgdGhlIHRyZW5kIGluIHRvdGFsIHNhbGVzIHZhbHVlIGFuZCByZWRlbXB0aW9uIGNvdW50cy4KCmBgYHtyLCBmaWcud2lkdGg9MTMsIGZpZy5oZWlnaHQ9MTJ9CnN1bW1hcnlfdHJhbnNhY3Rpb25zIDwtIGZ1bGxfdHJhbnNhY3Rpb25zICU+JQogIGdyb3VwX2J5KGNhbXBhaWduX3R5cGUpICU+JQogIHN1bW1hcmlzZSgKICAgIHRvdGFsX3NhbGVzID0gc3VtKHNhbGVzX3ZhbHVlKSwKICAgIGNvdW50X3R5cGUgPSBuKCkKKQoKZ2dwbG90KGRhdGEgPSBzdW1tYXJ5X3RyYW5zYWN0aW9ucywgYWVzKHggPSBjYW1wYWlnbl90eXBlKSkgKyAKICBnZW9tX2NvbChhZXMoeSA9IGNvdW50X3R5cGUsIGZpbGwgPSAnQ291bnQgb2YgY2FtcGFpZ24gdHlwZScpLCB3aWR0aCA9IDAuNSkgKwogIGdlb21fbGluZShhZXMoeSA9IHRvdGFsX3NhbGVzLCBjb2xvciA9ICdUb3RhbCBTYWxlcycpLCBncm91cCA9IDEpICsKICBnZW9tX3BvaW50KGFlcyh5ID0gdG90YWxfc2FsZXMsIGNvbG9yID0gJ1RvdGFsIFNhbGVzJyksIHNpemUgPSAzLjApICsgCiAgc2NhbGVfeV9jb250aW51b3VzKAogICAgbmFtZSA9ICJDb3VudCBvZiBjYW1wYWlnbiB0eXBlIiwKICAgIGJyZWFrcyA9IHNlcSgwLCBtYXgoc3VtbWFyeV90cmFuc2FjdGlvbnMkY291bnRfdHlwZSkgKyAxLCBieSA9IDQwMCksCiAgICBzZWMuYXhpcyA9IHNlY19heGlzKH4uLCAKICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJUb3RhbCBTYWxlcyIsCiAgICAgICAgICAgICAgICAgICAgICAgIGJyZWFrcyA9IHNlcSgwLCBtYXgoc3VtbWFyeV90cmFuc2FjdGlvbnMkdG90YWxfc2FsZXMpICsgMSwgYnkgPSA0MDApLAogICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzY2FsZXM6OmRvbGxhcikKICApICsKICBsYWJzKHggPSAnQ2FtcGFpZ24gVHlwZXMnLCB0aXRsZSA9ICJUb3RhbCBTYWxlcyBWYWx1ZSBhbmQgQ291bnQgb2YgUmVkZW1wdGlvbnMgUGVyIENhbXBhaWduIFR5cGUiKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWUgPSAnJywgdmFsdWVzID0gYygiQ291bnQgb2YgY2FtcGFpZ24gdHlwZSIgPSAibGlnaHRibHVlIikpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWUgPSAnJywgdmFsdWVzID0gYygiVG90YWwgU2FsZXMiID0gJ3JlZCcpKSArIAogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKAogICAgcGxvdC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMjApLAogICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMTApLCBzaXplID0gMTQpLAogICAgYXhpcy50aXRsZS55LmxlZnQgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHIgPSAxMCksIHNpemUgPSAxNCksCiAgICBheGlzLnRpdGxlLnkucmlnaHQgPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKGwgPSAxMCksIHNpemUgPSAxNCksCiAgICBsZWdlbmQucG9zaXRpb24gPSAnYm90dG9tJywKICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KQogICkgKwogIGFubm90YXRlKCJ0ZXh0IiwgeCA9IDEsIHkgPSA3NjAzLCBsYWJlbCA9ICJFeGNlcHRpb25hbGx5IEhpZ2giLCBjb2xvciA9ICJibHVlIiwgc2l6ZSA9IDYsIHZqdXN0ID0gLTEpCmBgYAoKVGhpcyBhbmFseXNpcyByZXZlYWxlZCB0aGF0IENhbXBhaWduIFR5cGUgQSBzaWduaWZpY2FudGx5IG91dHBlcmZvcm1lZCBvdGhlciBjYW1wYWlnbnMgaW4gYm90aCBtZXRyaWNzLCBpbmRpY2F0aW5nIGl0cyBlZmZlY3RpdmVuZXNzIGluIGRyaXZpbmcgY3VzdG9tZXIgZW5nYWdlbWVudCB3aXRoIGNvdXBvbi1yZWxhdGVkIHByb2R1Y3RzLgoKQnVpbGRpbmcgb24gdGhpcywgd2UgZnVydGhlciBhbmFseXplZCB0aGUgdG90YWwgc2FsZXMgdmFsdWUgYnkgZGVwYXJ0bWVudCBmb3IgZWFjaCBjYW1wYWlnbiB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGVpciBpbXBhY3QgYWNyb3NzIGRpZmZlcmVudCBwcm9kdWN0IGRlcGFydG1lbnRzLgoKYGBge3IsIGZpZy53aWR0aD0xMn0Kc2FtcGxlIDwtIGZ1bGxfdHJhbnNhY3Rpb25zICU+JQogIGdyb3VwX2J5KHByb2R1Y3RfaWQsIGNhbXBhaWduX3R5cGUpICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IHN1bShzYWxlc192YWx1ZSksIC5ncm91cHM9ImRyb3AiKSAlPiUKICBsZWZ0X2pvaW4ocHJvZHVjdHMsIGJ5PSJwcm9kdWN0X2lkIikgJT4lCiAgc2VsZWN0KGNhbXBhaWduX3R5cGUsIGRlcGFydG1lbnQsIHRvdGFsKSAlPiUKICBncm91cF9ieShjYW1wYWlnbl90eXBlLCBkZXBhcnRtZW50KSAlPiUKICBzdW1tYXJpc2UodG90YWxfbmV3ID0gc3VtKHRvdGFsKSwgLmdyb3Vwcz0iZHJvcCIpCiAgCmdncGxvdChkYXRhID0gc2FtcGxlLCBhZXMoeCA9IGNhbXBhaWduX3R5cGUsIHkgPSBkZXBhcnRtZW50LCBmaWxsID0gdG90YWxfbmV3KSkgKwogIGdlb21fdGlsZSgpICsKICBzY2FsZV9maWxsX2dyYWRpZW50Mihsb3c9IndoaXRlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgbWlkPSJsaWdodGJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICBoaWdoPSJibHVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJTYWxlcyBWYWx1ZSIsCiAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gbGFiZWxfZG9sbGFyKCkpICsKICBnZW9tX3RleHQoYWVzKGxhYmVsID0gbGFiZWxfZG9sbGFyKCkodG90YWxfbmV3KSksIGNvbG9yPSJibGFjayIsIHNpemU9NCkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgU2FsZXMgVmFsdWUgYnkgQ2FtcGFpZ24gVHlwZXMgYW5kIERlcGFydG1lbnRzIiwKICAgICAgIHggPSAiQ2FtcGFpZ24gVHlwZXMiLAogICAgICAgeSA9ICJEZXBhcm1lbnRzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAxMCksIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkKICAgICAgICApCmBgYAotIFRoZSBgR1JPQ0VSWWAgZGVwYXJ0bWVudCBjb25zaXN0ZW50bHkgbGVkIGluIHRvdGFsIHNhbGVzIHZhbHVlIGFjcm9zcyBhbGwgY2FtcGFpZ24gdHlwZXMsIGxpa2VseSBkdWUgdG8gdGhlIGVzc2VudGlhbCBuYXR1cmUgb2YgdGhlc2UgcHJvZHVjdHMuIEZvciBmdXJ0aGVyIGFuYWx5c2lzLCB3ZSBjYW4gZXhjbHVkZSB0aGUgJ0dST0NFUlknIGRlcGFydG1lbnQgdG8gZm9jdXMgb24gdGhlIHBlcmZvcm1hbmNlIG9mIG90aGVyIGRlcGFydG1lbnRzLgoKLSBDYW1wYWlnbiBUeXBlIEEgY29uc2lzdGVudGx5IGRyb3ZlIHRoZSBoaWdoZXN0IHNhbGVzIGFjcm9zcyBhbGwgZGVwYXJ0bWVudHMsIHN1Z2dlc3RpbmcgaXRzIHByb21vdGlvbmFsIHN0cmF0ZWd5IHdhcyBoaWdobHkgZWZmZWN0aXZlIGFuZCB3aWRlbHkgYXBwZWFsaW5nLiBUaGUgc3Ryb25nIHBlcmZvcm1hbmNlIG9mIFR5cGUgQSBpbmRpY2F0ZXMgaXRzIHBvdGVudGlhbCBhcyBhIG1vZGVsIGZvciBmdXR1cmUgY2FtcGFpZ25zLgoKLSBDYW1wYWlnbiBUeXBlcyBCIGFuZCBDIHNob3dlZCBubyBzYWxlcyBpbiBzZXZlcmFsIGRlcGFydG1lbnRzLCBwb3NzaWJseSBpbmRpY2F0aW5nIHRoYXQgdGhlaXIgcHJvbW90aW9ucyB3ZXJlIG5vdCB3ZWxsLXRhcmdldGVkIG9yIGxhY2tlZCBhcHBlYWwgaW4gY2VydGFpbiBwcm9kdWN0IGNhdGVnb3JpZXMuCgoKCiMjIyBEaXNwbGF5IExvY2F0aW9uIEFuYWx5c2lzCiMjIyMgKipIeXBvdGhlc2lzKio6IFByb2R1Y3RzIHBsYWNlZCBhdCBjZXJ0YWluIGRpc3BsYXkgbG9jYXRpb25zLCB3aGVuIGNvbWJpbmVkIHdpdGggY291cG9uIHByb21vdGlvbnMsIHdpbGwgcmVzdWx0IGluIHRoZSBoaWdoZXN0IG51bWJlciBvZiBwdXJjaGFzZXMgY29tcGFyZWQgdG8gb3RoZXIgZGlzcGxheSBsb2NhdGlvbnMuCi0gQXMgcHJldmlvdXNseSBub3RlZCwgd2UgZXhjbHVkZWQgdGhlIGBHUk9DRVJZYCBkZXBhcnRtZW50IGZyb20gdGhpcyBFREEgdG8gZm9jdXMgb24gb3RoZXIgZGVwYXJ0bWVudHMuCgotIFVzaW5nIHRoZSBkYXRhc2V0IHRoYXQgaW5jbHVkZXMgdHJhbnNhY3Rpb24gZGV0YWlscyB3aXRoIGRpc3BsYXkgbG9jYXRpb25zLCB3ZSBjYWxjdWxhdGVkIHRoZSB0b3RhbCBzYWxlcyB2YWx1ZSBieSBib3RoIGRpc3BsYXkgbG9jYXRpb24gYW5kIGRlcGFydG1lbnQgZm9yIGEgbW9yZSBkZXRhaWxlZCBhbmFseXNpcy4KYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD04fQpzYW1wbGUgPC0gdHJhbnNfd19kaXNwbGF5ICU+JQogIGRwbHlyOjpmaWx0ZXIoZGVwYXJ0bWVudCAhPSAiR1JPQ0VSWSIpICU+JQogIGdyb3VwX2J5KGRpc3BsYXlfbG9jYXRpb24sIGRlcGFydG1lbnQpICU+JQogIHN1bW1hcml6ZSh0b3RhbCA9IHN1bSh0b3RhbF9zYWxlcyksIC5ncm91cHMgPSAnZHJvcCcpCgpwcmludChzYW1wbGUsIG4gPSA1KQpgYGAKCioqYS4gU3RhY2sgYmFyIHBsb3Qgd2l0aCBhY3R1YWwgc2FsZXMgdmFsdWUgZm9yIGVhY2ggZGlzcGxheSBsb2NhdGlvbioqCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9MTB9CmdncGxvdChkYXRhID0gc2FtcGxlLCBhZXMoeCA9IGRpc3BsYXlfbG9jYXRpb24sIHkgPSB0b3RhbCwgZmlsbCA9IGRlcGFydG1lbnQpKSArIAogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJERUxJIiA9ICIjRkY5OTk5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRFJVRyBHTSIgPSAiIzY2Q0M5OSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1FQVQtUENLR0QiID0gIiNGRkNDMDAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOVVRSSVRJT04iID0gIiMzMzk5RkYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNRUFUIiA9ICIjRkY2NkNDIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUEFTVFJZIiA9ICIjRkY5OTY2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0VBRk9PRC1QQ0tHRCIgPSAiIzk5Q0NGRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ09TTUVUSUNTIiA9ICIjYjNiM2IzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQUk9EVUNFIiA9ICIjZmY3ZjAwIikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGRpc3BsYXlfbG9jYXRpb25fbGFiZWxzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IGxhYmVsX2RvbGxhcigpKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFNhbGVzIGJ5IERlcGFydG1lbnQgYW5kIERpc3BsYXkgTG9jYXRpb25zIiwKICAgICAgIHggPSAiRGlzcGxheSBMb2NhdGlvbnMiLAogICAgICAgeSA9ICJTYWxlcyBWYWx1ZSIsCiAgICAgICBmaWxsID0gIkRlcGFydG1lbnRzIikgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAxMCksIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KQogICAgICAgICkKYGBgCi0gV2l0aCBkaXNwbGF5IGxvY2F0aW9ucyBpbmNsdWRlZCwgd2UgaWRlbnRpZmllZCA5IGRpc3RpbmN0IGRlcGFydG1lbnRzLCB3aXRoIHRoZSBgRFJVRyBHTWAgZGVwYXJ0bWVudCBjb25zaXN0ZW50bHkgaGF2aW5nIHRoZSBoaWdoZXN0IHRvdGFsIHNhbGVzIHZhbHVlLiBUaGUgbG9jYXRpb24gYFN0b3JlIHJlYXJgIGV4aGliaXRlZCB0aGUgbW9zdCBzaWduaWZpY2FudCB0b3RhbCBzYWxlcyB2YWx1ZSBhY3Jvc3MgYWxsIGRlcGFydG1lbnRzLCBwYXJ0aWN1bGFybHkgZm9yIGBNRUFULVBDS0dEYC4gVGhpcyBwYXR0ZXJuIHN1Z2dlc3RzIHRoYXQgY3VzdG9tZXJzIGFyZSBtb3JlIGxpa2VseSB0byBwdXJjaGFzZSBwcm9kdWN0cyBkaXNwbGF5ZWQgYXQgdGhlIHN0b3JlIHJlYXIsIGVzcGVjaWFsbHkgcGFja2FnZWQgbWVhdC4KCi0gVGhlIGBDT1NNRVRJQ1NgIGRlcGFydG1lbnQgc2hvd2VkIG5vdGFibGUgc2FsZXMgdmFsdWUgYXQgdGhlIGBGcm9udCBlbmQgY2FwYCBidXQgbGFja2VkIHZpc2liaWxpdHkgaW4gb3RoZXIgbG9jYXRpb25zLCBwb3NzaWJseSBkdWUgdG8gbG93IGV4cG9zdXJlIGluIHRob3NlIGFyZWFzLgoKLSBBbHRob3VnaCB0aGUgcHJvZHVjdHMgZGF0YXNldCBjb250YWlucyAzMiB1bmlxdWUgZGVwYXJ0bWVudHMsIG9ubHkgOSBkZXBhcnRtZW50cyB3ZXJlIHJlcHJlc2VudGVkIGluIHRoZSBncmFwaCB3aXRoIHRyYW5zYWN0aW9ucyBpbnZvbHZpbmcgY291cG9uIHJlZGVtcHRpb25zLiBSZWdvcmsgbWlnaHQgd2FudCB0byBleHBsb3JlIGZ1cnRoZXIgaG93IGRpc3BsYXkgbG9jYXRpb25zIGltcGFjdCBzYWxlcyB2YWx1ZSBpZiBjb3Vwb25zIGFyZSBpbnRyb2R1Y2VkIGZvciB0aGUgcmVtYWluaW5nIGRlcGFydG1lbnRzLgoKVG8gcHJvdmlkZSBhIGNsZWFyZXIgcGVyc3BlY3RpdmUgb24gZGVwYXJ0bWVudCBjb250cmlidXRpb25zLCB3ZSBjb252ZXJ0ZWQgdGhlIGFib3ZlIGdyYXBoIGludG8gYSBwZXJjZW50YWdlIGZvcm1hdCwgaGlnaGxpZ2h0aW5nIGhvdyBlYWNoIGRlcGFydG1lbnQgY29udHJpYnV0ZWQgdG8gdGhlIHNhbGVzIHZhbHVlIGF0IGRpZmZlcmVudCBkaXNwbGF5IGxvY2F0aW9ucy4KCioqYi4gU3RhY2sgYmFyIHBsb3Qgd2l0aCBzYWxlcyB2YWx1ZSBiZWluZyBub3JtYWxpemUgdG8gcGVyY2VudGFnZSoqCmBgYHtyLCBmaWcud2lkdGg9MTUsIGZpZy5oZWlnaHQ9OH0KdG90YWxfc2FsZXNfcGVyX2Rpc3BsYXlfbG9jYXRpb24gPC0gc2FtcGxlICU+JQogIGdyb3VwX2J5KGRpc3BsYXlfbG9jYXRpb24pICU+JQogIHN1bW1hcmlzZSh0b3RhbF9zYWxlc19sb2NhdGlvbiA9IHN1bSh0b3RhbCksIC5ncm91cHMgPSAiZHJvcCIpCgpmaW5hbF9kZiA8LSBzYW1wbGUgJT4lCiAgbGVmdF9qb2luKHRvdGFsX3NhbGVzX3Blcl9kaXNwbGF5X2xvY2F0aW9uLCBieSA9ICJkaXNwbGF5X2xvY2F0aW9uIikgJT4lCiAgbXV0YXRlKHBlcmNlbnRhZ2UgPSByb3VuZCgodG90YWwgLyB0b3RhbF9zYWxlc19sb2NhdGlvbikgKiAxMDAsMikpCgpnZ3Bsb3QoZmluYWxfZGYsIGFlcyh4ID0gZGlzcGxheV9sb2NhdGlvbiwgeSA9IHBlcmNlbnRhZ2UsIGZpbGwgPSBkZXBhcnRtZW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJERUxJIiA9ICIjRkY5OTk5IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiRFJVRyBHTSIgPSAiIzY2Q0M5OSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk1FQVQtUENLR0QiID0gIiNGRkNDMDAiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOVVRSSVRJT04iID0gIiMzMzk5RkYiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNRUFUIiA9ICIjRkY2NkNDIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUEFTVFJZIiA9ICIjRkY5OTY2IiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0VBRk9PRC1QQ0tHRCIgPSAiIzk5Q0NGRiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiQ09TTUVUSUNTIiA9ICIjYjNiM2IzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQUk9EVUNFIiA9ICIjZmY3ZjAwIikpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IGRpc3BsYXlfbG9jYXRpb25fbGFiZWxzKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudF9mb3JtYXQoc2NhbGUgPSAxKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArCiAgbGFicyh0aXRsZSA9ICJUb3RhbCBTYWxlcyBieSBEZXBhcnRtZW50IGFuZCBDYW1wYWlnbiBUeXBlIGluIFBlcmNlbnRhZ2UiLAogICAgICAgeCA9ICJEaXNwbGF5IExvY2F0aW9ucyIsCiAgICAgICB5ID0gIlBlcmNlbnRhZ2UgU2FsZXMiLAogICAgICAgZmlsbCA9ICJEZXBhcnRtZW50cyIpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkKICAgICAgICApCmBgYApJbiB0aGlzIHBlcmNlbnRhZ2UgdmlldywgaXQgd2FzIGV2aWRlbnQgdGhhdCBgRFJVRyBHTWAgY29uc2lzdGVudGx5IG1haW50YWluZWQgaXRzIHN0cm9uZyBwZXJmb3JtYW5jZSBhY3Jvc3MgbXVsdGlwbGUgbG9jYXRpb25zLiBJbiBjb250cmFzdCwgYFBST0RVQ0VgIGhhZCBhIHNpZ25pZmljYW50IGNvbnRyaWJ1dGlvbiBhdCB0aGUgYEluIFNoZWxmYCBsb2NhdGlvbiBidXQgdW5kZXJwZXJmb3JtZWQgaW4gb3RoZXJzLgoKVG8gb3B0aW1pemUgc2FsZXMsIHdlIHNob3VsZCBjb25zaWRlciByZWxvY2F0aW5nIG1vcmUgYFBST0RVQ0VgIHByb2R1Y3RzIGZyb20gdGhlIHVuZGVycGVyZm9ybWluZyBsb2NhdGlvbnMgb3IgYWRqdXN0aW5nIGNvdXBvbiBzdHJhdGVnaWVzIHRvIGJvb3N0IHNhbGVzIGluIHRob3NlIGFyZWFzLiBUaGlzIGNvdWxkIGhlbHAgZGlzdHJpYnV0ZSBzYWxlcyBtb3JlIGV2ZW5seSBhbmQgdGFrZSBhZHZhbnRhZ2Ugb2YgdGhlIHVudGFwcGVkIHBvdGVudGlhbCBpbiBkaWZmZXJlbnQgbG9jYXRpb25zLgoKIyMjIyAqKkNvbXBhcmlzb25zOioqIEJlaW5nIGRpc3BsYXllZCB2cy4gbm90IGRpc3BsYXllZCAtIEhhdmluZyBjb3Vwb25zIHJlZGVlbWVkCmBgYHtyfQojIERpc3BsYXkKc2FtcGxlMSA8LSB0cmFuc193X2Rpc3BsYXkgJT4lCiAgZ3JvdXBfYnkoZGVwYXJ0bWVudCkgJT4lCiAgc3VtbWFyaXplKHRvdGFsID0gc3VtKHRvdGFsX3NhbGVzKSwgLmdyb3VwcyA9ICdkcm9wJykgJT4lCiAgYXJyYW5nZShkZXNjKGRlcGFydG1lbnQpKSAlPiUKICBtdXRhdGUodHlwZSA9ICJCZWluZyBEaXNwbGF5ZWQiKQoKIyBOb3QgRGlzcGxheQpzYW1wbGUyIDwtIHRyYW5zX3dfZGlzcGxheTAgJT4lCiAgZ3JvdXBfYnkoZGVwYXJ0bWVudCkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsID0gc3VtKHRvdGFsX3NhbGVzKSkgJT4lCiAgYXJyYW5nZShkZXNjKGRlcGFydG1lbnQpKSAlPiUKICBtdXRhdGUodHlwZSA9ICJOb3QgQmVpbmcgRGlzcGxheWVkIikKCmNvbWJpbmVkIDwtIGJpbmRfcm93cyhzYW1wbGUxLCBzYW1wbGUyKQpjb21iaW5lZApgYGAKCmBgYHtyLCBmaWcud2lkdGg9MjAsIGZpZy5oZWlnaHQ9MTB9CmdncGxvdChkYXRhID0gY29tYmluZWQsIGFlcyh4ID0gZGVwYXJ0bWVudCwgeSA9IHRvdGFsLCBmaWxsID0gdHlwZSkpICsKICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSAiZG9kZ2UiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgoY29tYmluZWQkdG90YWwpICsgMSwgYnkgPSA1MDApLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBsYWJlbF9kb2xsYXIoKSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzY2FsZXM6OmxhYmVsX2RvbGxhcigpKHRvdGFsKSksIAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgCiAgICAgICAgICAgIHZqdXN0ID0gLTEsIAogICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFNhbGVzIEFjcm9zcyBEZXBhcnRtZW50cyB3aXRoIERpc3BsYXkvTm90IERpc3BsYXkiLAogICAgICAgeCA9ICJEZXBhcnRtZW50cyIsCiAgICAgICB5ID0gIlRvdGFsIFNhbGVzIiwKICAgICAgIGZpbGwgPSAiRGlzcGxheSBUeXBlIikgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAxMCksIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KQogICAgICAgICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIkJlaW5nIERpc3BsYXllZCIgPSAibGlnaHRibHVlIiwgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTm90IEJlaW5nIERpc3BsYXllZCIgPSAibGlnaHRncmVlbiIpKQpgYGAKCi0gRGVzcGl0ZSBub3QgYmVpbmcgYWN0aXZlbHkgZGlzcGxheWVkLCB0aGUgYEdST0NFUllgIGRlcGFydG1lbnQgY29udGludWVkIHRvIGxlYWQgaW4gdG90YWwgc2FsZXMgdmFsdWUsIGZvbGxvd2VkIGJ5IHRoZSBgRFJVRyBHTWAgZGVwYXJ0bWVudC4gTm90YWJseSwgdGhlIGBNRUFUYCBkZXBhcnRtZW50LCB3aGljaCBsaWtlbHkgaW5jbHVkZXMgZnJlc2ggbWVhdCwgYXBwZWFyZWQgdG8gYmUgcHVyY2hhc2VkIHByaW1hcmlseSB3aGVuIG5vdCBkaXNwbGF5ZWQuCgotIEFub3RoZXIgb2JzZXJ2YXRpb24gaXMgdGhhdCB0aGUgYFRSQVZFTCAmIExFSVNVUkVgIGRlcGFydG1lbnQgb25seSBzYXcgcHVyY2hhc2VzIHdoZW4gaXRzIHByb2R1Y3RzIHdlcmUgbm90IGRpc3BsYXllZCwgc3VnZ2VzdGluZyBhIHVuaXF1ZSBwdXJjaGFzaW5nIGJlaGF2aW9yIGluIHRoaXMgY2F0ZWdvcnkuCgoKCiMjIyBNYWlsZXIgTG9jYXRpb24gQW5hbHlzaXMKIyMjIyAqKkh5cG90aGVzaXM6KiogQ3VzdG9tZXIgaXMgbGlrZWx5IHRvIHJlZGVlbSBjb3Vwb25zIGlmIHByb2R1Y3RzIGFyZSBwbGFjZWQgYXQgaW50ZXJpb3IgcGFnZSBmZWF0dXJlIGluIG1haWwuCgpTaW1pbGFyIHRvIGRpc3BsYXkgbG9jYXRpb25zLCB3ZSBhbHNvIHdhbnRlZCB0byBsZWFybiB0aGUgdG90YWwgc2FsZXMgdmFsdWUgYWNyb3NzIGRlcGFydG1lbnRzIHdpdGggbWFpbGVyIGxvY2F0aW9ucy4gCgpXZSBmaXJzdCBvYnRhaW5lZCB0aGUgZGF0YSBieSBwZXJmb3JtaW5nIGdyb3VwaW5nIGFuZCBzdW1tYXJ5OgpgYGB7cn0Kc2FtcGxlIDwtIHRyYW5zX3dfbWFpbGVyICU+JQogIGRwbHlyOjpmaWx0ZXIoZGVwYXJ0bWVudCAhPSAiR1JPQ0VSWSIpICU+JQogIGdyb3VwX2J5KG1haWxlcl9sb2NhdGlvbiwgZGVwYXJ0bWVudCkgJT4lCiAgc3VtbWFyaXNlKHRvdGFsID0gc3VtKHRvdGFsX3NhbGVzKSwgLmdyb3VwcyA9ICJkcm9wIikgJT4lCiAgYXJyYW5nZShkZXNjKHRvdGFsKSkKCnByaW50KHNhbXBsZSwgbj01KQpgYGAKCioqYS4gU3RhY2sgYmFyIHBsb3Qgd2l0aCBhY3R1YWwgc2FsZXMgdmFsdWUgZm9yIGVhY2ggbWFpbGVyIGxvY2F0aW9uKioKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xMn0KZ2dwbG90KGRhdGEgPSBzYW1wbGUsIGFlcyh4ID0gbWFpbGVyX2xvY2F0aW9uLCB5ID0gdG90YWwsIGZpbGwgPSBkZXBhcnRtZW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICBzY2FsZV94X2Rpc2NyZXRlKGxhYmVscyA9IG1haWxlcl9sb2NhdGlvbl9sYWJlbHMpICsgCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzID0gYygiRFJVRyBHTSIgPSAiIzhkYTBjYiIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUVBVCIgPSAiI2E2ZDg1NCIsICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTUVBVC1QQ0tHRCIgPSAiI2U3OGFjMyIsICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUFJPRFVDRSIgPSAiIzY2YzJhNSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0VBRk9PRC1QQ0tHRCIgPSAiI2ZiOWE5OSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiREVMSSIgPSAiI2U1YzQ5NCIsICAgICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlNFQUZPT0QiID0gIiNiM2IzYjMiLCAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIk5VVFJJVElPTiIgPSAiI2ZmZDkyZiIsICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDT1NNRVRJQ1MiID0gIiMzM2EwMmMiLCAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJDT1VQT04iID0gIiNmZjdmMDAiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBBU1RSWSIgPSAiIzFmNzhiNCIsICAgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlRSQVZFTCAmIExFSVNVUkUiID0gIiNlMzFhMWMiKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgMzUwMCwgYnkgPSA1MDApLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBsYWJlbF9kb2xsYXIoKSkgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAxMCksIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KQogICAgICAgICkgKwogIGxhYnModGl0bGUgPSAiVG90YWwgU2FsZXMgYnkgRGVwYXJ0bWVudCBhbmQgTWFpbGVyIExvY2F0aW9ucyIsCiAgICAgICB4ID0gIk1haWxlciBMb2NhdGlvbnMiLAogICAgICAgeSA9ICJUb3RhbCBTYWxlIiwKICAgICAgIGZpbGwgPSAiRGVwYXJ0bWVudHMiKQpgYGAKLSBXaXRoIG1haWxlciBsb2NhdGlvbnMsIG1vcmUgZGVwYXJ0bWVudHMgd2VyZSByZXByZXNlbnRlZCBjb21wYXJlZCB0byBkaXNwbGF5IGxvY2F0aW9ucywgYnV0IGBNRUFUYCBhbmQgYE1FQVQtUENLR0RgIHN0aWxsIGRvbWluYXRlZCBpbiB0b3RhbCBzYWxlcyB2YWx1ZSBhY3Jvc3MgdmFyaW91cyBsb2NhdGlvbnMuIEludGVyZXN0aW5nbHksIGBEUlVHIEdNYCBub3cgY29uY2VudHJhdGVkIGl0cyBzYWxlcyB2YWx1ZSBwcmltYXJpbHkgb24gdGhlIGBJbnRlcmlvciBwYWdlIGZlYXR1cmVgLCB1bmxpa2UgdGhlIGJyb2FkZXIgZGlzdHJpYnV0aW9uIHNlZW4gaW4gZGlzcGxheSBsb2NhdGlvbnMuCgotIEFzIG5vdGVkIGVhcmxpZXIsIHdoaWxlIHRoZSBgcHJvZHVjdHNgIGRhdGFzZXQgaW5jbHVkZXMgMzIgdW5pcXVlIGRlcGFydG1lbnRzLCBvbmx5IDEzIHdlcmUgcmVmbGVjdGVkIGluIHRoaXMgYW5hbHlzaXMuIFJlZ29yayBtYXkgd2FudCB0byBleHBsb3JlIG9wcG9ydHVuaXRpZXMgdG8gcHJvbW90ZSB0aGUgcmVtYWluaW5nIGRlcGFydG1lbnRzIHVzaW5nIGNvdXBvbnMgaW4gbWFpbGVyIGxvY2F0aW9ucy4KCi0gVGhlIGBUUkFWRUwgJiBMRUlTVVJFYCBkZXBhcnRtZW50LCB3aGljaCB3YXNuJ3Qgc2VlbiBpbiB0aGUgZGlzcGxheSBsb2NhdGlvbnMgYW5hbHlzaXMsIHNob3dlZCBhIHNtYWxsIHNhbGVzIHZhbHVlIGFjcm9zcyB0aHJlZSBkaWZmZXJlbnQgbWFpbGVyIGxvY2F0aW9ucy4gSXQgbWF5IGJlIHdvcnRoIGNvbmR1Y3RpbmcgYSBzdXJ2ZXkgdG8gZGV0ZXJtaW5lIHdoZXRoZXIgaW5jcmVhc2luZyB0aGlzIGRlcGFydG1lbnQncyBwcmVzZW5jZSBpbiBtYWlsZXJzLCBwYXJ0aWN1bGFybHkgaW4gdGhlIGBJbnRlcmlvciBwYWdlIGZlYXR1cmVgLCBjb3VsZCBib29zdCBpdHMgc2FsZXMgdmFsdWUuCgoqKmIuIFN0YWNrIGJhciBwbG90IHdpdGggc2FsZXMgdmFsdWUgYmVpbmcgbm9ybWFsaXplIHRvIHBlcmNlbnRhZ2UqKgpgYGB7ciwgZmlnLndpZHRoPTE1LCBmaWcuaGVpZ2h0PTh9IAp0b3RhbF9zYWxlc19wZXJfbWFpbGVyX2xvY2F0aW9uIDwtIHNhbXBsZSAlPiUKICBncm91cF9ieShtYWlsZXJfbG9jYXRpb24pICU+JQogIHN1bW1hcmlzZSh0b3RhbF9zYWxlc19sb2NhdGlvbiA9IHN1bSh0b3RhbCksIC5ncm91cHMgPSAiZHJvcCIpCgpmaW5hbF9kZiA8LSBzYW1wbGUgJT4lCiAgbGVmdF9qb2luKHRvdGFsX3NhbGVzX3Blcl9tYWlsZXJfbG9jYXRpb24sIGJ5ID0gIm1haWxlcl9sb2NhdGlvbiIpICU+JQogIG11dGF0ZShwZXJjZW50YWdlID0gcm91bmQoKHRvdGFsIC8gdG90YWxfc2FsZXNfbG9jYXRpb24pICogMTAwLDIpKQoKZ2dwbG90KGZpbmFsX2RmLCBhZXMoeCA9IG1haWxlcl9sb2NhdGlvbiwgeSA9IHBlcmNlbnRhZ2UsIGZpbGwgPSBkZXBhcnRtZW50KSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJzdGFjayIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJEUlVHIEdNIiA9ICIjOGRhMGNiIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNRUFUIiA9ICIjYTZkODU0IiwgICAgICAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNRUFULVBDS0dEIiA9ICIjZTc4YWMzIiwgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQUk9EVUNFIiA9ICIjNjZjMmE1IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJTRUFGT09ELVBDS0dEIiA9ICIjZmI5YTk5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJERUxJIiA9ICIjZTVjNDk0IiwgICAgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiU0VBRk9PRCIgPSAiI2IzYjNiMyIsICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTlVUUklUSU9OIiA9ICIjZmZkOTJmIiwgICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNPU01FVElDUyIgPSAiIzMzYTAyYyIsICAgIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIkNPVVBPTiIgPSAiI2ZmN2YwMCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUEFTVFJZIiA9ICIjMWY3OGI0IiwgICAgICAgCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiVFJBVkVMICYgTEVJU1VSRSIgPSAiI2UzMWExYyIpKSArCiAgc2NhbGVfeF9kaXNjcmV0ZShsYWJlbHMgPSBtYWlsZXJfbG9jYXRpb25fbGFiZWxzKSArCiAgdGhlbWVfY2xhc3NpYygpICsKICBzY2FsZV95X2NvbnRpbnVvdXMoZXhwYW5kID0gYygwLDIpLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBzY2FsZXM6OnBlcmNlbnRfZm9ybWF0KHNjYWxlID0gMSkpICsKICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAyMCksCiAgICAgICAgYXhpcy50aXRsZS54ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbih0ID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGl0bGUueSA9IGVsZW1lbnRfdGV4dChtYXJnaW4gPSBtYXJnaW4ociA9IDEwKSwgc2l6ZSA9IDE0KSwKICAgICAgICBheGlzLnRleHQueSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC50aXRsZSA9IGVsZW1lbnRfdGV4dChzaXplID0gMTQpLAogICAgICAgIGxlZ2VuZC50ZXh0ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCkKICAgICAgICApICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFNhbGVzIGJ5IERlcGFydG1lbnQgYW5kIENhbXBhaWduIFR5cGUgaW4gUGVyY2VudGFnZSIsCiAgICAgICB4ID0gIk1haWxlciBMb2NhdGlvbnMiLAogICAgICAgeSA9ICJQZXJjZW50YWdlIFNhbGVzIiwKICAgICAgIGZpbGwgPSAiRGVwYXJ0bWVudHMiKQpgYGAKLSBXaXRoIHBlcmNlbnRhZ2UgdmlldywgYERFTElgIGRlcGFydG1lbnQgY29udHJpYnV0aW9uIHRvIHNhbGVzIHZhbHVlIHdhcyBzZWVuIG1vcmUgY2xlYXJseSBub3cuIFRoZXJlIHdlcmUgdG9vIG1hbnkgZGVwYXJ0bWVudHMgYmVpbmcgcHJlc2VudGVkIGF0IGBJbnRlcmlvciBwYWdlIGZlYXR1cmVgLiBXZSBjYW4gdHJ5IHRvIG1pbmltaXplIHRoaXMgYW5kIGZ1cnRoZXIgc3R1ZHkgdGhlIGNvbmNlbnRyYXRpb24gb2YgZWFjaCBkZXBhcnRtZW50IG9uIHNwZWNpZmljIG1haWxlciBsb2NhdGlvbi4KCgoKIyMjIyAqKkNvbXBhcmlzb25zOioqIEJlaW5nIHByZXNlbnRlZCBhbmQgbm90IHByZXNlbnRlZCBpbiBtYWlsIC0gSGF2aW5nIGNvdXBvbnMgcmVkZWVtZWQKYGBge3IsIGZpZy53aWR0aD0xNSwgZmlnLmhlaWdodD0xMH0KIyBNYWlsIHByZXNlbnRlZApzYW1wbGUxIDwtIHRyYW5zX3dfbWFpbGVyICU+JQogIGRwbHlyOjpmaWx0ZXIoZGVwYXJ0bWVudCE9IkdST0NFUlkiKSAlPiUKICBncm91cF9ieShkZXBhcnRtZW50KSAlPiUKICBzdW1tYXJpemUodG90YWwgPSBzdW0odG90YWxfc2FsZXMpLCAuZ3JvdXBzID0gJ2Ryb3AnKSAlPiUKICBhcnJhbmdlKGRlc2MoZGVwYXJ0bWVudCkpICU+JQogIG11dGF0ZSh0eXBlID0gIk1haWwgUHJlc2VudGVkIikKCiMgTm90IE1haWwgcHJlc2VudGVkCnNhbXBsZTIgPC0gdHJhbnNfd19tYWlsZXIwICU+JQogIGRwbHlyOjpmaWx0ZXIoZGVwYXJ0bWVudCAhPSAiR1JPQ0VSWSIpICU+JQogIGdyb3VwX2J5KGRlcGFydG1lbnQpICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IHN1bSh0b3RhbF9zYWxlcykpICU+JQogIGFycmFuZ2UoZGVzYyhkZXBhcnRtZW50KSkgJT4lCiAgbXV0YXRlKHR5cGUgPSAiTm90IE1haWwgUHJlc2VudGVkIikKCmNvbWJpbmVkIDwtIGJpbmRfcm93cyhzYW1wbGUxLCBzYW1wbGUyKQpjb21iaW5lZApgYGAKYGBge3IsIGZpZy53aWR0aD0xOCwgZmlnLmhlaWdodD0xNX0KZ2dwbG90KGRhdGEgPSBjb21iaW5lZCwgYWVzKHggPSBkZXBhcnRtZW50LCB5ID0gdG90YWwsIGZpbGwgPSB0eXBlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9ICJkb2RnZSIpICsgIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3MgPSBzZXEoMCwgbWF4KGNvbWJpbmVkJHRvdGFsKSArIDEwMCwgYnkgPSA1MDApLAogICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBsYWJlbF9kb2xsYXIoKSkgKwogIGdlb21fdGV4dChhZXMobGFiZWwgPSBzY2FsZXM6OmxhYmVsX2RvbGxhcigpKHRvdGFsKSksIAogICAgICAgICAgICBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHdpZHRoID0gMC45KSwgCiAgICAgICAgICAgIHZqdXN0ID0gLTEsIAogICAgICAgICAgICBjb2xvciA9ICJibGFjayIpICsKICBsYWJzKHRpdGxlID0gIlRvdGFsIFNhbGVzIEFjcm9zcyBEZXBhcnRtZW50cyB3aXRoIE1haWwgUHJlc2VuY2UiLAogICAgICAgeCA9ICJEZXBhcnRtZW50cyIsCiAgICAgICB5ID0gIlRvdGFsIFNhbGVzIiwKICAgICAgIGZpbGwgPSAiTWFpbCBQcmVzZW5jZSBUeXBlIikgKwogIHRoZW1lX2NsYXNzaWMoKSArIAogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAxMCksIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KQogICAgICAgICkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoIk1haWwgUHJlc2VudGVkIiA9ICJsaWdodGJsdWUiLCAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJOb3QgTWFpbCBQcmVzZW50ZWQiID0gImxpZ2h0Z3JlZW4iKSkKYGBgCkl0IGlzIGV2aWRlbnQgdGhhdCBubyBkZXBhcnRtZW50IGdlbmVyYXRlZCBoaWdoZXIgc2FsZXMgdmFsdWUgd2l0aG91dCBiZWluZyBmZWF0dXJlZCBpbiB0aGUgbWFpbC4gTm90YWJseSwgYENPVVBPTmAgYW5kIGBUUkFWRUwgJiBMRUlTVVJFYCB3ZXJlIHRoZSBvbmx5IHR3byBkZXBhcnRtZW50cyB0aGF0IHNhdyBubyBzYWxlcyB3aGVuIG5vdCBpbmNsdWRlZCBpbiBtYWlsIHByb21vdGlvbnMuIEluIGNvbnRyYXN0IHRvIHRoZSBkaXNwbGF5IGxvY2F0aW9uIGFuYWx5c2lzLCB0aGUgYE1FQVRgIGRlcGFydG1lbnQgc2hvd2VkIGEgc2lnbmlmaWNhbnQgaW5jcmVhc2UgaW4gc2FsZXMgd2hlbiBmZWF0dXJlZCBpbiBtYWlsZXJzLCB3aXRoIHNhbGVzIGFwcHJveGltYXRlbHkgMjAwIHRpbWVzIGhpZ2hlciB0aGFuIG5vdCBiZWluZyBkaXNwbGF5ZWQuIFRoaXMgdW5kZXJzY29yZXMgdGhlIGltcG9ydGFuY2Ugb2YgZW5zdXJpbmcgdGhhdCBgTUVBVGAgY29udGludWVzIHRvIGJlIHByb21pbmVudGx5IGZlYXR1cmVkIGluIG1haWwgY2FtcGFpZ25zLgoKIyMjIERpc3BsYXkgdnMuIE1haWxlciBQcm9tb3Rpb25zCk5leHQsIHdlIGFpbWVkIHRvIGNvbXBhcmUgdGhlIHNhbGVzIHZhbHVlcyBiZXR3ZWVuIGRlcGFydG1lbnRzIGJhc2VkIG9uIHRoZWlyIHByZXNlbnRhdGlvbiB0eXBlOiBkaXNwbGF5IGxvY2F0aW9uIGFuZCBtYWlsZXIgbG9jYXRpb24uIFRoaXMgYW5hbHlzaXMgd2lsbCBoZWxwIHVzIGlkZW50aWZ5IHdoaWNoIHByb21vdGlvbmFsIHN0cmF0ZWd54oCUZGlzcGxheSBvciBtYWlsZXLigJRpcyBtb3JlIGVmZmVjdGl2ZSBmb3IgZWFjaCBkZXBhcnRtZW50LCBhbGxvd2luZyB1cyB0byBiZXR0ZXIgdW5kZXJzdGFuZCBob3cgcHJlc2VudGF0aW9uIHR5cGUgaW5mbHVlbmNlcyBjdXN0b21lciBwdXJjaGFzaW5nIGJlaGF2aW9yIGFuZCBvdmVyYWxsIHNhbGVzIHBlcmZvcm1hbmNlIGFjcm9zcyBkZXBhcnRtZW50cy4KCmBgYHtyLCBmaWcud2lkdGg9MTgsIGZpZy5oZWlnaHQ9MTJ9Cm1haWxlcl9wcm9tbyA8LSB0cmFuc193X21haWxlciAlPiUKICBzZWxlY3QoZGVwYXJ0bWVudCwgdG90YWxfc2FsZXMpICU+JQogIGdyb3VwX2J5KGRlcGFydG1lbnQpICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IHN1bSh0b3RhbF9zYWxlcyksIC5ncm91cHM9ImRyb3AiKSAlPiUKICBtdXRhdGUocHJvbW9fdHlwZSA9ICJNYWlsIikKCmRpc3BsYXlfcHJvbW8gPC0gdHJhbnNfd19kaXNwbGF5ICU+JQogIHVuZ3JvdXAoKSAlPiUKICBzZWxlY3QoZGVwYXJ0bWVudCwgdG90YWxfc2FsZXMpICU+JQogIGdyb3VwX2J5KGRlcGFydG1lbnQpICU+JQogIHN1bW1hcmlzZSh0b3RhbCA9IHN1bSh0b3RhbF9zYWxlcyksIC5ncm91cHM9ImRyb3AiKSAlPiUKICBtdXRhdGUocHJvbW9fdHlwZSA9ICJEaXNwbGF5IikKCnN1bW1hcnlfZGF0YSA8LSBiaW5kX3Jvd3MobWFpbGVyX3Byb21vLCBkaXNwbGF5X3Byb21vKQoKZ2dwbG90KGRhdGEgPSBzdW1tYXJ5X2RhdGEsIGFlcyh4ID0gZGVwYXJ0bWVudCwgeSA9IHRvdGFsLCBjb2xvciA9IHByb21vX3R5cGUpKSArCiAgZ2VvbV9wb2ludChhbHBoYSA9IDAuNiwgc2l6ZSA9IDEwKSArCiAgZ2VvbV9zZWdtZW50KGFlcyh4ZW5kID0gZGVwYXJ0bWVudCwgeWVuZCA9IDApLCBsaW5ldHlwZSA9ICJkb3R0ZWQiLCBsaW5ld2lkdGggPSAxLjUpICsgCiAgbGFicyh0aXRsZSA9ICJTYWxlcyBDb21wYXJpc29uIEJldHdlZW4gRGlzcGxheSBhbmQgTWFpbGVyIFByb21vdGlvbnMiLAogICAgICAgeCA9ICJEZXBhcnRtZW50IiwKICAgICAgIHkgPSAiVG90YWwgU2FsZXMiLAogICAgICAgY29sb3IgPSAiUHJlc2VudGVkIFR5cGUiKSArCiAgc2NhbGVfeV9jb250aW51b3VzKGJyZWFrcyA9IHNlcSgwLCBtYXgoc3VtbWFyeV9kYXRhJHRvdGFsKSArIDEsIGJ5ID0gMzAwKSwKICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gbGFiZWxfZG9sbGFyKCkpICsKICB0aGVtZV9jbGFzc2ljKCkgKwogIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDIwKSwKICAgICAgICBheGlzLnRpdGxlLnggPSBlbGVtZW50X3RleHQobWFyZ2luID0gbWFyZ2luKHQgPSAxMCksIHNpemUgPSAxNCksCiAgICAgICAgYXhpcy50aXRsZS55ID0gZWxlbWVudF90ZXh0KG1hcmdpbiA9IG1hcmdpbihyID0gMTApLCBzaXplID0gMTQpLAogICAgICAgIGF4aXMudGV4dC55ID0gZWxlbWVudF90ZXh0KHNpemUgPSAxMiksCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF90ZXh0KHNpemUgPSAxNCksCiAgICAgICAgbGVnZW5kLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDE0KQogICAgICAgICkKYGBgCgpOZXZlcnRoZWxlc3MsIHRoZSBgR1JPQ0VSWWAgZGVwYXJ0bWVudCByZW1haW5lZCB0aGUgdG9wIHBlcmZvcm1lciBhY3Jvc3MgYWxsIHByb21vdGlvbiB0eXBlcy4gSG93ZXZlciwgaXQgYXBwZWFycyB0aGF0IHByZXNlbnRpbmcgcHJvbW90aW9ucyB0aHJvdWdoIG1haWwgZ2VuZXJhbGx5IGxlZCB0byBoaWdoZXIgc2FsZXMuIFRoaXMgc3VnZ2VzdHMgdGhhdCBjdXN0b21lcnMgdGVuZCB0byBwdXJjaGFzZSBhbmQgcmVkZWVtIGNvdXBvbnMgbW9yZSBmcmVxdWVudGx5IHdoZW4gdGhleSBzZWUgcHJvbW90aW9ucyBpbiB0aGUgbWFpbCwgaW5kaWNhdGluZyBhIHNoaWZ0IGluIGNvbnN1bWVyIGJlaGF2aW9yIHRvd2FyZCBvbmxpbmUgc2hvcHBpbmcuCgpSZWdvcmsgY291bGQgY29uc2lkZXIgY29uZHVjdGluZyBhIHN0dWR5IG9uIG9ubGluZSBzaG9wcGluZyBiZWhhdmlvciBhbmQgc3BlY2lmaWNhbGx5IHRlc3QgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgVHlwZSBBIHByb21vdGlvbiBjYW1wYWlnbnMgdmlhIG1haWxpbmcuCgoKCiMjIFN1bW1hcnkKKioxLiBJbnNpZ2h0cyoqCgotIFRoZSBUeXBlIEEgcHJvbW90aW9uIGNhbXBhaWduIHByb3ZlZCB0byBiZSB0aGUgbW9zdCBlZmZlY3RpdmUgcHJvbW90aW9uYWwgc3RyYXRlZ3ksIGdlbmVyYXRpbmcgb3ZlciAkNyw2MDAgaW4gc2FsZXMgdmFsdWUgYW5kIGFwcHJveGltYXRlbHkgMiw0MDAgcmVkZW1wdGlvbiBjb3VudHMgYWNyb3NzIGFsbCBkZXBhcnRtZW50cy4gVGhpcyBzdWNjZXNzIHN1Z2dlc3RzIHRoYXQgVHlwZSBBIGNhbiBzZXJ2ZSBhcyBhIG1vZGVsIGZvciBmdXR1cmUgbWFya2V0aW5nIGNhbXBhaWducy4gSG93ZXZlciwgdGhlcmUgYXJlIGRlcGFydG1lbnRzIHRoYXQgd2VyZSBub3QgaW5jbHVkZWQgaW4gdGhpcyBjYW1wYWlnbi4gV2UgcmVjb21tZW5kIGNvbmR1Y3RpbmcgZnVydGhlciBhbmFseXNpcyB0byBkZXRlcm1pbmUgaG93IHdlbGwgVHlwZSBBIHN0cmF0ZWdpZXMgY291bGQgYmUgYXBwbGllZCB0byB0aG9zZSBkZXBhcnRtZW50cyBhbmQgd2hldGhlciBpdCBjb3VsZCBsZWFkIHRvIGFuIGluY3JlYXNlIGluIHRoZWlyIHNhbGVzIHZhbHVlLgoKLSBBZGRpdGlvbmFsbHksIFR5cGUgQiBhbmQgVHlwZSBDIGNhbXBhaWducyBkaWQgbm90IGdlbmVyYXRlIHNhbGVzIGZyb20gc2V2ZXJhbCBkZXBhcnRtZW50cy4gVGhpcyB3YXJyYW50cyBmdXJ0aGVyIGludmVzdGlnYXRpb24gaW50byB0aGUgZmFjdG9ycyB0aGF0IG1heSBoYXZlIGNvbnRyaWJ1dGVkIHRvIHRoaXMgbGFjayBvZiBzYWxlcywgd2hpY2ggY291bGQgaGVscCByZWZpbmUgZnV0dXJlIHByb21vdGlvbmFsIHN0cmF0ZWdpZXMgYW5kIGluY3JlYXNlIHRoZWlyIGVmZmVjdGl2ZW5lc3MuCgoqKiphLiBEaXNwbGF5IExvY2F0aW9ucyoqKgoKLSBJbiB0aGUgYW5hbHlzaXMgb2YgZGlzcGxheSBsb2NhdGlvbnMsIHRoZSBgTUVBVC1QQ0tHRGAgZGVwYXJ0bWVudCBoYWQgc2lnbmlmaWNhbnRseSBoaWdoZXIgc2FsZXMgdmFsdWUgYXQgdGhlIGBTdG9yZSByZWFyYCBjb21wYXJlZCB0byBvdGhlciBsb2NhdGlvbnMuIFRoaXMgc3VnZ2VzdHMgdGhhdCBSZWdvcmsgbWlnaHQgd2FudCB0byBjb25zaWRlciBwbGFjaW5nIG1vcmUgcHJvZHVjdHMgZnJvbSB0aGUgYE1FQVQtUENLR0RgIGRlcGFydG1lbnQgaW4gdGhlIHN0b3JlIHJlYXIuIEEgc2ltaWxhciBzdHJhdGVneSBjb3VsZCBiZSBhcHBsaWVkIHRvIHRoZSBgUFJPRFVDRWAgZGVwYXJ0bWVudCwgd2hlcmUgcGxhY2luZyBwcm9kdWN0cyBpbiB0aGUgYEluIHNoZWxmYCBsb2NhdGlvbiBjb3VsZCBib29zdCBzYWxlcy4KCi0gV2hlbiBjb21wYXJpbmcgc2FsZXMgdmFsdWUgYmV0d2VlbiBkaXNwbGF5ZWQgYW5kIG5vbi1kaXNwbGF5ZWQgcHJvZHVjdHMsIHRoZSByZXN1bHRzIHdlcmUgc3VycHJpc2luZy4gRXhjZXB0IGZvciBgR1JPQ0VSWWAsIGBNRUFUYCBoYWQgc2lnbmlmaWNhbnRseSBoaWdoZXIgc2FsZXMgdmFsdWUgd2hlbiBub3QgZGlzcGxheWVkIGNvbXBhcmVkIHRvIHdoZW4gaXQgd2FzIGRpc3BsYXllZC4gVGhpcyB0cmVuZCB3YXMgYWxzbyBvYnNlcnZlZCBpbiB0aGUgYFBST0RVQ0VgIGFuZCBgVFJBVkVMICYgTEVJU1VSRWAgZGVwYXJ0bWVudHMsIHdpdGggdGhlIGxhdHRlciBub3QgYXBwZWFyaW5nIGluIHRoZSBkaXNwbGF5IGxvY2F0aW9uIGFuYWx5c2lzLiBXZSByZWNvbW1lbmQgcnVubmluZyBhIGNhbXBhaWduIHRoYXQgcGxhY2VzIGBUUkFWRUwgJiBMRUlTVVJFYCBwcm9kdWN0cyBpbiBkaXNwbGF5IGxvY2F0aW9ucyB3aXRoIGNvdXBvbnMgdG8gc2VlIGlmIHRoaXMgZ2VuZXJhdGVzIGEgbm90aWNlYWJsZSBpbmNyZWFzZSBpbiBzYWxlcyB2YWx1ZS4gCgoqKipiLiBNYWlsZXIgTG9jYXRpb25zKioqCgotIEluIHRoZSBtYWlsZXIgbG9jYXRpb25zIGFuYWx5c2lzLCBtb3JlIGRlcGFydG1lbnRzIHNob3dlZCBzYWxlcyB2YWx1ZSBjb21wYXJlZCB0byB0aGUgZGlzcGxheSBsb2NhdGlvbnMuIFNwZWNpZmljYWxseSwgZGVwYXJ0bWVudHMgbGlrZSBgVFJBVkVMICYgTEVJU1VSRWAgYW5kIGBERUxJYCwgd2hpY2ggd2VyZSBub3Qgc2VlbiBpbiB0aGUgZGlzcGxheSBsb2NhdGlvbiBhbmFseXNpcywgYmVjYW1lIG1vcmUgcHJvbWluZW50IHdoZW4gbG9va2luZyBhdCB0aGUgcGVyY2VudGFnZSB2aWV3LiBUaGUgdG90YWwgc2FsZXMgdmFsdWUgaW4gbWFpbCBsb2NhdGlvbnMgd2FzIGdlbmVyYWxseSBoaWdoZXIgdGhhbiBpbiBkaXNwbGF5IGxvY2F0aW9ucywgc3VnZ2VzdGluZyBhIHNoaWZ0IGluIGNvbnN1bWVyIGJlaGF2aW9yIHRvd2FyZCBvbmxpbmUgc2hvcHBpbmcuIFJlZ29yayBtYXkgd2FudCB0byBjb25zaWRlciBpbnZlc3RpbmcgZnVydGhlciBpbiB0ZWNobm9sb2dpZXMgdGhhdCBzdXBwb3J0IG9ubGluZSBwcm9tb3Rpb25zIHRvIGNhcGl0YWxpemUgb24gdGhpcyB0cmVuZCBhbmQgZHJpdmUgaGlnaGVyIHJldmVudWUuCgotIEEgY29tcGFyaXNvbiBiZXR3ZWVuIG1haWwgcHJlc2VuY2UgYW5kIG5vIG1haWwgcHJlc2VuY2UgaW5kaWNhdGVzIHRoYXQgc2FsZXMgdmFsdWUgd2FzIHNpZ25pZmljYW50bHkgaGlnaGVyIHdoZW4gcHJvbW90aW9ucyB3ZXJlIHByZXNlbnRlZCB2aWEgbWFpbC4gVGhpcyBhcHByb2FjaCBzaG91bGQgYmUgY29udGludWVkLCBhbmQgZnVydGhlciBjYW1wYWlnbiBzdHJhdGVnaWVzIHNob3VsZCBiZSBkZXZlbG9wZWQgdG8gb3B0aW1pemUgdGhlIGVmZmVjdGl2ZW5lc3Mgb2YgdGhpcyBwcm9tb3Rpb25hbCBtZXRob2QuCgoqKipjLiBEaXNwbGF5IHZzLiBNYWlsZXIqKioKCi0gQXMgbm90ZWQgZWFybGllciwgdGhlcmUgaGFzIGJlZW4gYSBub3RpY2VhYmxlIHNoaWZ0IGluIGN1c3RvbWVyIGJlaGF2aW9yIHRvd2FyZHMgb25saW5lIHNob3BwaW5nLiBUaGlzIGlzIGEga2V5IGFyZWEgd2hlcmUgUmVnb3JrIGNhbiBmdXJ0aGVyIGludmVzdC4gQnkgYW5hbHl6aW5nIHRoZSBpbXBhY3Qgb2YgbWFpbGVyIGxvY2F0aW9ucywgdGhlIG1hcmtldGluZyB0ZWFtIGF0IFJlZ29yayBjYW4gc3R1ZHkgaG93IHRvIGludGVncmF0ZSBkZXBhcnRtZW50cyB0aGF0IHdlcmUgbm90IHByZXZpb3VzbHkgaW5jbHVkZWQgaW4gcHJvbW90aW9ucy4gVGhpcyBjb3VsZCBwcmVzZW50IG9wcG9ydHVuaXRpZXMgdG8gaW5jcmVhc2Ugc2FsZXMgdmFsdWUgZm9yIHRob3NlIGRlcGFydG1lbnRzIGFuZCBvZmZlciBjb3Vwb25zIGZvciB0aGVpciBwcm9kdWN0cywgcG90ZW50aWFsbHkgZHJpdmluZyBoaWdoZXIgZW5nYWdlbWVudCBhbmQgcmV2ZW51ZS4KCioqMi4gTGltaXRhdGlvbnMqKgoKLSBXZSBoYXZlIG5vdCB5ZXQgY29uc2lkZXJlZCB0aGUgaW1wYWN0IG9mIHRpbWUgZnJhbWVzIG9uIHRoZXNlIHByb21vdGlvbmFsIGNhbXBhaWducy4gRnV0dXJlIHdvcmsgc2hvdWxkIGluY2x1ZGUgYW4gYW5hbHlzaXMgb2YgaG93IHRoZSB0aW1pbmcgb2YgcHJvbW90aW9ucyBpbmZsdWVuY2VzIHNhbGVzIHBlcmZvcm1hbmNlLgoKLSBUaGUgYXNzdW1wdGlvbiBtYWRlIGF0IHRoZSBiZWdpbm5pbmcgb2YgdGhlIHJlcG9ydCwgdGhhdCBhbGwgdHJhbnNhY3Rpb25zIHdpdGggYW55IGRpc2NvdW50IGFyZSByZWRlZW1lZCBjb3Vwb25zLCBoYXMgeWV0IHRvIGJlIGNvbmZpcm1lZC4gRnVydGhlciB2YWxpZGF0aW9uIG9mIHRoaXMgYXNzdW1wdGlvbiBpcyBuZWNlc3NhcnkgdG8gZW5zdXJlIHRoZSBhY2N1cmFjeSBvZiB0aGUgYW5hbHlzaXMuCgotIFRoZXJlIGFyZSBhcHByb3hpbWF0ZWx5IDIgbWlsbGlvbiBjb3Vwb24gcmVkZW1wdGlvbnMgd2l0aG91dCBjb3JyZXNwb25kaW5nIHRyYW5zYWN0aW9uIHJlY29yZHMuIENvbGxlY3RpbmcgbW9yZSBkYXRhIG9uIHRoZXNlIHJlZGVtcHRpb25zIGNvdWxkIHByb3ZpZGUgdmFsdWFibGUgaW5zaWdodHMgYW5kIHBvdGVudGlhbGx5IGFmZmVjdCB0aGUgdG90YWwgc2FsZXMgdmFsdWUgb2YgdGhlIGRpZmZlcmVudCBjYW1wYWlnbiB0eXBlcy4KCgoKCgo=