Marketing Strategies Regarding Campaigns
Class: Data Mining (4080-001)
Authors: An Nguyen, Avneet Dharni, Oanh Dang, Roan Zappanti, Tatum Arey

Prerequities

Note: RColorBrewer provides color schemes for graphs

library(tidyverse)
library(lubridate)
library(completejourney)
library(RColorBrewer)
library(dplyr)


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

Introduction

Our objective is to evaluate success factors and areas for growth in Campaigns A, B, and C. This will allow us to understand if the initiatives we are implementing are optimized to their fullest potential.

We took 3 primary things into consideration during our analysis; who we are serving, what we are serving, and where we are serving it. These pillars provide a thorough analysis to understand each campaign’s strengths and weaknesses. This analysis will provide strategic recommendations to revise our campaigns for increased sales for Regork.

Business Objective: Identify a potential growth area where the company could invest future resources to increase revenue and profit

Business Question 1: Which campaign types generated most sales and what households are associated with that campaign type?

Business Question 2: What further information can we glean from this data about Campaign Types B and C that can be used to develop a marketing strategy?

Answering the Questions


Initial Analysis on sales by campaign and household size

Based on the results of this exploratory graph, we discovered differences between sales in Type A, Type B, and Type C. This was our starting point for looking into campaigns.


# Creating a new table with summarized sales by campaign
sales_by_campaign <- campaign_descriptions %>%
  inner_join(campaigns) %>%
  inner_join(transactions) %>%
  inner_join(demographics) %>%
  group_by(campaign_id, campaign_type, household_comp) %>%
  summarise(total_sales = sum(sales_value, na.rm = TRUE)) %>%
  arrange(desc(total_sales))
# Graphing the sales by campaign with household information
sales_by_campaign %>%
  ggplot(aes(x = campaign_type, y = total_sales, fill = household_comp)) +
  geom_col(stat = "identity", position = "dodge") +
  scale_y_continuous(labels = scales::dollar) +
  labs(
    title = "Total sales by campaign",
    subtitle = "Campaign running from 2016 to 2017",
    x = "Campaign type",
    y = "Total sales",
    fill = "Household size")


Product Count & Sales Amt Analysis

Next, we wanted to know how many products were involved in each campaign type, and we wanted to validate that sales amounts differed by campaign with a second graph.

It seems that campaign C lacks in both product count and total sales value.

# Joining tables for further general analysis
df <-transactions %>%
  left_join(products) %>%
  left_join(demographics) %>%
  full_join(campaigns) %>%
  full_join(campaign_descriptions)

  
# This graph show how many products is applied for each type of campaign
#This is the reason why campaign A has the most sales value 
#(df2 is the sales value of each campaign type)
df1 <- df %>%
  group_by(campaign_type) %>%
  count(n_distinct(product_id)) %>%
  summarise(total_products= sum(n))

df1 %>% ggplot(aes(x= campaign_type,y=total_products)) +
  geom_segment( aes(x= campaign_type,xend= campaign_type, y=0, yend= total_products, color= "Orange"), show.legend = FALSE)+
  geom_point(color= 'Orange', size = 3, show.legend = FALSE) + 
  scale_y_continuous(trans= 'log2',name = "Number of products", labels = scales::comma) +
  scale_x_discrete(name= "Campaign Type")+
  labs( title = "Number of products in each campaign type",
        subtitle = "This graph shows the number of products applied \n in each campaign type",
       caption = "http://https://github.com/bradleyboehmke/completejourney")

# Creating a second data frame that looks at sales by campaign type without household information (more intuitive to understand)
df2<- df %>%
  group_by(campaign_type) %>%
summarise(total_sales_campaign_type= sum(sales_value))

df2 %>% 
  ggplot(aes(x= campaign_type, y= total_sales_campaign_type, fill= campaign_type))+
  geom_bar(stat = "identity", show.legend = FALSE)+
  scale_fill_brewer(palette = "Oranges")+
  scale_y_continuous( name = "Sales Value of each Campaign Type", labels = scales::dollar)+
  scale_x_discrete(name= "Campaign Type")+
  labs( title = "Total Sales Value of Each Campaign Type",
       caption = "http://https://github.com/bradleyboehmke/completejourney")


An Analysis: Income Analysis

Additionally, we wanted to know what which household income factors were attributed the most (percentage-wise) to each Campaign Type.

Based on the resulting stacked graph, middle-income families are the target market of these campaigns.


# Without saving any variables, create the graph output of the count of households by income across campaign types.
demographics %>%
  inner_join(transactions) %>%
  inner_join(campaigns) %>%
  inner_join(campaign_descriptions) %>%
  filter(campaign_type %in% c("Type A","Type B", "Type C")) %>%
  group_by(campaign_type) %>%
  count(income, campaign_type) %>%
  mutate(pct = n/sum(n)) %>%
  ggplot(aes(reorder(income,n), y = n, fill = campaign_type)) +
    geom_bar(stat = "identity") +
    theme(axis.title.y = element_blank()) +
    theme(panel.grid.major = element_blank(), 
        panel.grid.minor = element_blank(),
        panel.background = element_blank(),
        axis.line = element_line(colour = "black"),
        plot.caption = element_text(size = 8, margin = margin(t = 10), color = "grey70", hjust = 0)) +
    scale_fill_brewer(palette = "Oranges") +
    # Adding labels of percentages if wanted
    # geom_text(aes(label = round(pct, 2)), hjust = -0.1,
    # position = position_stack(vjust = 0.01),
    # color = "black", check_overlap = TRUE) +
    scale_y_continuous(labels = scales::number) +
    coord_flip() +
  labs(
    title = "Income Distribution Among All Campaign Types",
    x = "Income range",
    y = "Count",
    caption = "Source: R data package that provides access to data in the Complete Journey package \n provided by 84.51",
    fill = "Campaign type")


Campaign-Location Analysis

Finally, we wanted to know which promotional display locations contributed to each campaign type by counting the # of unique proucts that were placed in each location during the campaign. (For the sake of options, we made 2 graphs for each type).

Locations 0 and 7 are the top two for Types A and B, while locations 5 and 0 are common for type C (which has lower performance).

#Defining variables to analyze locations in each campaign. This uses large data frames.
plot_data <- promotions %>% 
    group_by(display_location, product_id) %>% 
    select(display_location, product_id) %>% 
    inner_join(products, by = "product_id") %>% 
    inner_join(coupons, by = "product_id") %>% 
    inner_join(campaign_descriptions, by = "campaign_id") %>%
    select(campaign_type, display_location, product_id) %>% 
    group_by(campaign_type, display_location) %>% 
    summarize(distinct = n_distinct(product_id)) %>%
    arrange(campaign_type, desc(distinct))

location_plot1 <- NA
location_plot2 <- NA
# This function intakes the campaign type (ex: 'Type A') and outputs two ranked graphs regarding location counts by distinct product id

campaign_locations <- function(campaign_tp, messages = TRUE){
  
    
  location_plot1 <<- plot_data %>%
    filter(campaign_type == campaign_tp) %>% 
    ggplot(aes(x = reorder(display_location, -distinct), 
                y = distinct,
               fill = reorder(display_location, -distinct))) + 
      geom_bar(stat = "identity", show.legend = FALSE) +
      scale_y_continuous(labels = scales::comma) +
      scale_fill_grey() +
      xlab("Display Locations") +
      ylab("Distinct Product Count") +
      labs( title = paste("# of unique products per display location"),
            subtitle = paste("For", campaign_tp))
    
  location_plot2 <<- plot_data %>% 
    filter(campaign_type == campaign_tp) %>% 
    ggplot(aes(x = distinct, 
               y = reorder(display_location, distinct),
               col = -distinct)) + 
      geom_point(show.legend = FALSE, size = 5) +
      scale_fill_distiller(type = "seq", palette = "Blues") +      
      xlab("Distinct Product Count") +
      ylab("Display Location") +
      scale_x_continuous(labels = scales::comma) +
      labs( title = paste("# of unique products per display location"),
            subtitle = paste("For", campaign_tp))
  
  message("Function complete")
}  
# Running the 'campgain_locations()' function and displaying outputs
cmpgns <- c("Type A","Type B","Type C")
for(i in cmpgns){
  campaign_locations(i)
  print(location_plot1)
  print(location_plot2)
  rm(location_plot1)
  rm(location_plot2)
}
Function complete

rm(cmpgns)
rm(i)
rm(plot_data)


Summary

Problem Statement:

Which campaign types generated most sales and what households are associated with that campaign type? Additionally, what further information can be gleaned from this data about Campaign Types A, B, and C that can be used to develop a marketing strategy?

Analysis Methodology

Addressing the strategy required to properly invest in all three campaigns required a multi-metric approach, so the analysis was divided into major categories of:

  • Basic sales metrics by campaign type

  • Household income distributions

  • Location popularity distributions

Once broken into the above-stated parts, each analysis used dplyr to join, mutate, and filter (and more) multiple data tables from the completejourney data. Then, the created data frames were visualized utilizing ggplot2 and RColorBrewer.

Strategic Insights

From a marketing perspective, we wanted to address the 4 P’s (pricing, product, placement, & promotion) to fully understand the consumer. We recognized that the most popular income range for all 3 campaigns was 50-74K. This is a great insight to understand the consumers that are reacting positively to our campaigns. It will also allow us to implement a strategic pricing and promotion initiative in place.

Although that is great, we also have some missed opportunities in the other income ranges. This would be a chance for Regork to investigate why that may be and innovate solutions to address that in future campaigns.

Next, we also found that the higher the number of products offered, the higher the sales value for the campaign. This product variety seems to attract consumers and positively impact their buying habits.

Finally, we discovered that campaigns A & B were very successful using display locations 0 and 7. Grocery stores are very strategic in their product placement to increase sales. We wanted to be sensitive to this and understand how this influences campaign sales.

Implications on the Customer

Based on the findings listed above, our recommendations are to:

  1. Continue to target the 50-74K income range

  2. Increase the number of products per campaign (particularly C)

  3. Rotate the campaigns among display locations 0 and 7

This will not only drive already-profitable growth in the 50-74k income range, but also shift Campaign C’s low-quality strategy into the same, highly successful strategies as A and B.

Analysis Limitations

After conducting out analysis, we discovered 4 primary limitations; unknown locations, overlapping campaigns, campaign budget, and unknown product categories.

One of our recommendations was to rotate the campaigns between display locations 0 and 7. Although that is good in theory, this poses some potential road blocks. First, we do not have any information on these locations other than their designated number. This is crucial information especially in a grocery store setting. Is it an end cap? Is it refrigerated? How much space is available? All of these things would be necessary information to implement the recommendation.

Next, since we recommended rotating the campaigns this means no campaigns can overlap in timelines which could cause an issue. Another limitation is campaign budget. One of our recommendations was to add more products to Campaign C. However, more products require more capital which may not be available in the budget.

Lastly, the data set did not provide product categories. This information would’ve been instrumental in creating more specific and successful campaigns.

LS0tDQp0aXRsZTogIkdyb3VwIDExIFByb2plY3QiDQpkYXRlOiAnIGByIFN5cy5EYXRlKClgJw0Kb3V0cHV0OiBodG1sX25vdGVib29rDQotLS0NCk1hcmtldGluZyBTdHJhdGVnaWVzIFJlZ2FyZGluZyBDYW1wYWlnbnMgPGJyPg0KQ2xhc3M6IERhdGEgTWluaW5nICg0MDgwLTAwMSkgPGJyPg0KQXV0aG9yczogQW4gTmd1eWVuLCBBdm5lZXQgRGhhcm5pLCBPYW5oIERhbmcsIFJvYW4gWmFwcGFudGksIFRhdHVtIEFyZXkgDQo8YnI+PGJyPg0KICANCiMjIFByZXJlcXVpdGllcw0KTm90ZTogUkNvbG9yQnJld2VyIHByb3ZpZGVzIGNvbG9yIHNjaGVtZXMgZm9yIGdyYXBocw0KYGBge3IsIHdhcm5pbmc9RkFMU0UsIG1lc3NhZ2U9RkFMU0V9DQpsaWJyYXJ5KHRpZHl2ZXJzZSkNCmxpYnJhcnkobHVicmlkYXRlKQ0KbGlicmFyeShjb21wbGV0ZWpvdXJuZXkpDQpsaWJyYXJ5KFJDb2xvckJyZXdlcikNCmxpYnJhcnkoZHBseXIpDQoNCg0KYyhwcm9tb3Rpb25zLCB0cmFuc2FjdGlvbnMpICU8LSUgZ2V0X2RhdGEod2hpY2ggPSAnYm90aCcsIHZlcmJvc2UgPSBGQUxTRSkNCmBgYA0KIyMgSW50cm9kdWN0aW9uICANCg0KT3VyIG9iamVjdGl2ZSBpcyB0byBldmFsdWF0ZSBzdWNjZXNzIGZhY3RvcnMgYW5kIGFyZWFzIGZvciBncm93dGggaW4gQ2FtcGFpZ25zIEEsIEIsIGFuZCBDLiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gdW5kZXJzdGFuZCBpZiB0aGUgaW5pdGlhdGl2ZXMgd2UgYXJlIGltcGxlbWVudGluZyBhcmUgb3B0aW1pemVkIHRvIHRoZWlyIGZ1bGxlc3QgcG90ZW50aWFsLiA8YnI+IDxicj5XZSB0b29rIDMgcHJpbWFyeSB0aGluZ3MgaW50byBjb25zaWRlcmF0aW9uIGR1cmluZyBvdXIgYW5hbHlzaXM7IHdobyB3ZSBhcmUgc2VydmluZywgd2hhdCB3ZSBhcmUgc2VydmluZywgYW5kIHdoZXJlIHdlIGFyZSBzZXJ2aW5nIGl0LiBUaGVzZSBwaWxsYXJzIHByb3ZpZGUgYSB0aG9yb3VnaCBhbmFseXNpcyB0byB1bmRlcnN0YW5kIGVhY2ggY2FtcGFpZ24ncyBzdHJlbmd0aHMgYW5kIHdlYWtuZXNzZXMuIFRoaXMgYW5hbHlzaXMgd2lsbCBwcm92aWRlIHN0cmF0ZWdpYyByZWNvbW1lbmRhdGlvbnMgdG8gcmV2aXNlIG91ciBjYW1wYWlnbnMgZm9yIGluY3JlYXNlZCBzYWxlcyBmb3IgUmVnb3JrLg0KDQoqKkJ1c2luZXNzIE9iamVjdGl2ZSoqOiBJZGVudGlmeSBhIHBvdGVudGlhbCBncm93dGggYXJlYSB3aGVyZSB0aGUgY29tcGFueSBjb3VsZCBpbnZlc3QgZnV0dXJlIHJlc291cmNlcyB0byBpbmNyZWFzZSByZXZlbnVlIGFuZCBwcm9maXQNCg0KKipCdXNpbmVzcyBRdWVzdGlvbiAxKio6IFdoaWNoIGNhbXBhaWduIHR5cGVzIGdlbmVyYXRlZCBtb3N0IHNhbGVzIGFuZCB3aGF0IGhvdXNlaG9sZHMgYXJlIGFzc29jaWF0ZWQgd2l0aCB0aGF0IGNhbXBhaWduIHR5cGU/DQoNCioqQnVzaW5lc3MgUXVlc3Rpb24gMjoqKiBXaGF0IGZ1cnRoZXIgaW5mb3JtYXRpb24gY2FuIHdlIGdsZWFuIGZyb20gdGhpcyBkYXRhIGFib3V0IENhbXBhaWduIFR5cGVzIEIgYW5kIEMgdGhhdCBjYW4gYmUgdXNlZCB0byBkZXZlbG9wIGEgbWFya2V0aW5nIHN0cmF0ZWd5PyAgPGJyPjxicj4NCg0KLSBXaGF0IGFyZSB0aGUgdG90YWwgc2FsZXMgYW5kIG51bWJlciBvZiBwcm9kdWN0cyB3aXRoaW4gZWFjaCBjYW1wYWlnbj8NCg0KLSAgV2hhdCBhcmUgdGhlIGluY29tZSBkaXN0cmlidXRpb25zIGFtb25nIGVhY2ggY2FtcGFpZ25zIHR5cGU/ICANCg0KLSBXaGF0IGRpc3BsYXkgbG9jYXRpb25zIGxlYWQgdG8gaGlnaGVyIHNhbGVzLCBhbmQgd2hhdCBsb2NhdGlvbnMgYXJlIGNvbW1vbiB3aXRoaW4gZWFjaCBjYW1wYWlnbiB0eXBlPyANCjxicj48YnI+DQoNCg0KIyMgQW5zd2VyaW5nIHRoZSBRdWVzdGlvbnMNCg0KPGJyPg0KDQojIyMjIEluaXRpYWwgQW5hbHlzaXMgb24gc2FsZXMgYnkgY2FtcGFpZ24gYW5kIGhvdXNlaG9sZCBzaXplDQoNCkJhc2VkIG9uIHRoZSByZXN1bHRzIG9mIHRoaXMgZXhwbG9yYXRvcnkgZ3JhcGgsIHdlIGRpc2NvdmVyZWQgZGlmZmVyZW5jZXMgYmV0d2VlbiBzYWxlcyBpbiBUeXBlIEEsIFR5cGUgQiwgYW5kIFR5cGUgQy4gVGhpcyB3YXMgb3VyIHN0YXJ0aW5nIHBvaW50IGZvciBsb29raW5nIGludG8gY2FtcGFpZ25zLg0KYGBge3IsIG1lc3NhZ2U9IEZBTFNFLCBpbmNsdWRlPSBUUlVFLCBlY2hvPSBUUlVFfSANCg0KIyBDcmVhdGluZyBhIG5ldyB0YWJsZSB3aXRoIHN1bW1hcml6ZWQgc2FsZXMgYnkgY2FtcGFpZ24NCnNhbGVzX2J5X2NhbXBhaWduIDwtIGNhbXBhaWduX2Rlc2NyaXB0aW9ucyAlPiUNCiAgaW5uZXJfam9pbihjYW1wYWlnbnMpICU+JQ0KICBpbm5lcl9qb2luKHRyYW5zYWN0aW9ucykgJT4lDQogIGlubmVyX2pvaW4oZGVtb2dyYXBoaWNzKSAlPiUNCiAgZ3JvdXBfYnkoY2FtcGFpZ25faWQsIGNhbXBhaWduX3R5cGUsIGhvdXNlaG9sZF9jb21wKSAlPiUNCiAgc3VtbWFyaXNlKHRvdGFsX3NhbGVzID0gc3VtKHNhbGVzX3ZhbHVlLCBuYS5ybSA9IFRSVUUpKSAlPiUNCiAgYXJyYW5nZShkZXNjKHRvdGFsX3NhbGVzKSkNCmBgYA0KDQoNCmBgYHtyLCB3YXJuaW5nPUZBTFNFfQ0KIyBHcmFwaGluZyB0aGUgc2FsZXMgYnkgY2FtcGFpZ24gd2l0aCBob3VzZWhvbGQgaW5mb3JtYXRpb24NCnNhbGVzX2J5X2NhbXBhaWduICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBjYW1wYWlnbl90eXBlLCB5ID0gdG90YWxfc2FsZXMsIGZpbGwgPSBob3VzZWhvbGRfY29tcCkpICsNCiAgZ2VvbV9jb2woc3RhdCA9ICJpZGVudGl0eSIsIHBvc2l0aW9uID0gImRvZGdlIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJUb3RhbCBzYWxlcyBieSBjYW1wYWlnbiIsDQogICAgc3VidGl0bGUgPSAiQ2FtcGFpZ24gcnVubmluZyBmcm9tIDIwMTYgdG8gMjAxNyIsDQogICAgeCA9ICJDYW1wYWlnbiB0eXBlIiwNCiAgICB5ID0gIlRvdGFsIHNhbGVzIiwNCiAgICBmaWxsID0gIkhvdXNlaG9sZCBzaXplIikNCmBgYA0KPGJyPg0KDQojIyMjIFByb2R1Y3QgQ291bnQgJiBTYWxlcyBBbXQgQW5hbHlzaXMNCk5leHQsIHdlIHdhbnRlZCB0byBrbm93IGhvdyBtYW55IHByb2R1Y3RzIHdlcmUgaW52b2x2ZWQgaW4gZWFjaCBjYW1wYWlnbiB0eXBlLCBhbmQgd2Ugd2FudGVkIHRvIHZhbGlkYXRlIHRoYXQgc2FsZXMgYW1vdW50cyBkaWZmZXJlZCBieSBjYW1wYWlnbiB3aXRoIGEgc2Vjb25kIGdyYXBoLiANCjxicj48YnI+DQpJdCBzZWVtcyB0aGF0ICoqY2FtcGFpZ24gQyoqIGxhY2tzIGluIGJvdGggcHJvZHVjdCBjb3VudCBhbmQgdG90YWwgc2FsZXMgdmFsdWUuDQoNCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCiMgSm9pbmluZyB0YWJsZXMgZm9yIGZ1cnRoZXIgZ2VuZXJhbCBhbmFseXNpcw0KZGYgPC10cmFuc2FjdGlvbnMgJT4lDQogIGxlZnRfam9pbihwcm9kdWN0cykgJT4lDQogIGxlZnRfam9pbihkZW1vZ3JhcGhpY3MpICU+JQ0KICBmdWxsX2pvaW4oY2FtcGFpZ25zKSAlPiUNCiAgZnVsbF9qb2luKGNhbXBhaWduX2Rlc2NyaXB0aW9ucykNCg0KICANCiMgVGhpcyBncmFwaCBzaG93IGhvdyBtYW55IHByb2R1Y3RzIGlzIGFwcGxpZWQgZm9yIGVhY2ggdHlwZSBvZiBjYW1wYWlnbg0KI1RoaXMgaXMgdGhlIHJlYXNvbiB3aHkgY2FtcGFpZ24gQSBoYXMgdGhlIG1vc3Qgc2FsZXMgdmFsdWUgDQojKGRmMiBpcyB0aGUgc2FsZXMgdmFsdWUgb2YgZWFjaCBjYW1wYWlnbiB0eXBlKQ0KZGYxIDwtIGRmICU+JQ0KICBncm91cF9ieShjYW1wYWlnbl90eXBlKSAlPiUNCiAgY291bnQobl9kaXN0aW5jdChwcm9kdWN0X2lkKSkgJT4lDQogIHN1bW1hcmlzZSh0b3RhbF9wcm9kdWN0cz0gc3VtKG4pKQ0KDQpkZjEgJT4lIGdncGxvdChhZXMoeD0gY2FtcGFpZ25fdHlwZSx5PXRvdGFsX3Byb2R1Y3RzKSkgKw0KICBnZW9tX3NlZ21lbnQoIGFlcyh4PSBjYW1wYWlnbl90eXBlLHhlbmQ9IGNhbXBhaWduX3R5cGUsIHk9MCwgeWVuZD0gdG90YWxfcHJvZHVjdHMsIGNvbG9yPSAiT3JhbmdlIiksIHNob3cubGVnZW5kID0gRkFMU0UpKw0KICBnZW9tX3BvaW50KGNvbG9yPSAnT3JhbmdlJywgc2l6ZSA9IDMsIHNob3cubGVnZW5kID0gRkFMU0UpICsgDQogIHNjYWxlX3lfY29udGludW91cyh0cmFucz0gJ2xvZzInLG5hbWUgPSAiTnVtYmVyIG9mIHByb2R1Y3RzIiwgbGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICBzY2FsZV94X2Rpc2NyZXRlKG5hbWU9ICJDYW1wYWlnbiBUeXBlIikrDQogIGxhYnMoIHRpdGxlID0gIk51bWJlciBvZiBwcm9kdWN0cyBpbiBlYWNoIGNhbXBhaWduIHR5cGUiLA0KICAgICAgICBzdWJ0aXRsZSA9ICJUaGlzIGdyYXBoIHNob3dzIHRoZSBudW1iZXIgb2YgcHJvZHVjdHMgYXBwbGllZCBcbiBpbiBlYWNoIGNhbXBhaWduIHR5cGUiLA0KICAgICAgIGNhcHRpb24gPSAiaHR0cDovL2h0dHBzOi8vZ2l0aHViLmNvbS9icmFkbGV5Ym9laG1rZS9jb21wbGV0ZWpvdXJuZXkiKQ0KYGBgDQoNCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT0gRkFMU0V9DQojIENyZWF0aW5nIGEgc2Vjb25kIGRhdGEgZnJhbWUgdGhhdCBsb29rcyBhdCBzYWxlcyBieSBjYW1wYWlnbiB0eXBlIHdpdGhvdXQgaG91c2Vob2xkIGluZm9ybWF0aW9uIChtb3JlIGludHVpdGl2ZSB0byB1bmRlcnN0YW5kKQ0KZGYyPC0gZGYgJT4lDQogIGdyb3VwX2J5KGNhbXBhaWduX3R5cGUpICU+JQ0Kc3VtbWFyaXNlKHRvdGFsX3NhbGVzX2NhbXBhaWduX3R5cGU9IHN1bShzYWxlc192YWx1ZSkpDQoNCmRmMiAlPiUgDQogIGdncGxvdChhZXMoeD0gY2FtcGFpZ25fdHlwZSwgeT0gdG90YWxfc2FsZXNfY2FtcGFpZ25fdHlwZSwgZmlsbD0gY2FtcGFpZ25fdHlwZSkpKw0KICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5Iiwgc2hvdy5sZWdlbmQgPSBGQUxTRSkrDQogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiT3JhbmdlcyIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMoIG5hbWUgPSAiU2FsZXMgVmFsdWUgb2YgZWFjaCBDYW1wYWlnbiBUeXBlIiwgbGFiZWxzID0gc2NhbGVzOjpkb2xsYXIpKw0KICBzY2FsZV94X2Rpc2NyZXRlKG5hbWU9ICJDYW1wYWlnbiBUeXBlIikrDQogIGxhYnMoIHRpdGxlID0gIlRvdGFsIFNhbGVzIFZhbHVlIG9mIEVhY2ggQ2FtcGFpZ24gVHlwZSIsDQogICAgICAgY2FwdGlvbiA9ICJodHRwOi8vaHR0cHM6Ly9naXRodWIuY29tL2JyYWRsZXlib2VobWtlL2NvbXBsZXRlam91cm5leSIpDQpgYGANCjxicj4NCg0KIyMjIyBBbiBBbmFseXNpczogSW5jb21lIEFuYWx5c2lzDQoNCkFkZGl0aW9uYWxseSwgd2Ugd2FudGVkIHRvIGtub3cgd2hhdCB3aGljaCBob3VzZWhvbGQgaW5jb21lIGZhY3RvcnMgd2VyZSBhdHRyaWJ1dGVkIHRoZSBtb3N0IChwZXJjZW50YWdlLXdpc2UpIHRvIGVhY2ggQ2FtcGFpZ24gVHlwZS4NCjxicj48YnI+DQpCYXNlZCBvbiB0aGUgcmVzdWx0aW5nIHN0YWNrZWQgZ3JhcGgsIG1pZGRsZS1pbmNvbWUgZmFtaWxpZXMgYXJlIHRoZSB0YXJnZXQgbWFya2V0IG9mIHRoZXNlIGNhbXBhaWducy4NCmBgYHtyIGVjaG89VFJVRSwgd2FybmluZz1GQUxTRSwgbWVzc2FnZT1GQUxTRX0NCg0KIyBXaXRob3V0IHNhdmluZyBhbnkgdmFyaWFibGVzLCBjcmVhdGUgdGhlIGdyYXBoIG91dHB1dCBvZiB0aGUgY291bnQgb2YgaG91c2Vob2xkcyBieSBpbmNvbWUgYWNyb3NzIGNhbXBhaWduIHR5cGVzLg0KZGVtb2dyYXBoaWNzICU+JQ0KICBpbm5lcl9qb2luKHRyYW5zYWN0aW9ucykgJT4lDQogIGlubmVyX2pvaW4oY2FtcGFpZ25zKSAlPiUNCiAgaW5uZXJfam9pbihjYW1wYWlnbl9kZXNjcmlwdGlvbnMpICU+JQ0KICBmaWx0ZXIoY2FtcGFpZ25fdHlwZSAlaW4lIGMoIlR5cGUgQSIsIlR5cGUgQiIsICJUeXBlIEMiKSkgJT4lDQogIGdyb3VwX2J5KGNhbXBhaWduX3R5cGUpICU+JQ0KICBjb3VudChpbmNvbWUsIGNhbXBhaWduX3R5cGUpICU+JQ0KICBtdXRhdGUocGN0ID0gbi9zdW0obikpICU+JQ0KICBnZ3Bsb3QoYWVzKHJlb3JkZXIoaW5jb21lLG4pLCB5ID0gbiwgZmlsbCA9IGNhbXBhaWduX3R5cGUpKSArDQogICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsNCiAgICB0aGVtZShheGlzLnRpdGxlLnkgPSBlbGVtZW50X2JsYW5rKCkpICsNCiAgICB0aGVtZShwYW5lbC5ncmlkLm1ham9yID0gZWxlbWVudF9ibGFuaygpLCANCiAgICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgICAgYXhpcy5saW5lID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgICAgICBwbG90LmNhcHRpb24gPSBlbGVtZW50X3RleHQoc2l6ZSA9IDgsIG1hcmdpbiA9IG1hcmdpbih0ID0gMTApLCBjb2xvciA9ICJncmV5NzAiLCBoanVzdCA9IDApKSArDQogICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJPcmFuZ2VzIikgKw0KICAgICMgQWRkaW5nIGxhYmVscyBvZiBwZXJjZW50YWdlcyBpZiB3YW50ZWQNCiAgICAjIGdlb21fdGV4dChhZXMobGFiZWwgPSByb3VuZChwY3QsIDIpKSwgaGp1c3QgPSAtMC4xLA0KICAgICMgcG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuMDEpLA0KICAgICMgY29sb3IgPSAiYmxhY2siLCBjaGVja19vdmVybGFwID0gVFJVRSkgKw0KICAgIHNjYWxlX3lfY29udGludW91cyhsYWJlbHMgPSBzY2FsZXM6Om51bWJlcikgKw0KICAgIGNvb3JkX2ZsaXAoKSArDQogIGxhYnMoDQogICAgdGl0bGUgPSAiSW5jb21lIERpc3RyaWJ1dGlvbiBBbW9uZyBBbGwgQ2FtcGFpZ24gVHlwZXMiLA0KICAgIHggPSAiSW5jb21lIHJhbmdlIiwNCiAgICB5ID0gIkNvdW50IiwNCiAgICBjYXB0aW9uID0gIlNvdXJjZTogUiBkYXRhIHBhY2thZ2UgdGhhdCBwcm92aWRlcyBhY2Nlc3MgdG8gZGF0YSBpbiB0aGUgQ29tcGxldGUgSm91cm5leSBwYWNrYWdlIFxuIHByb3ZpZGVkIGJ5IDg0LjUxIiwNCiAgICBmaWxsID0gIkNhbXBhaWduIHR5cGUiKQ0KYGBgDQoNCjxicj4NCg0KIyMjIyBDYW1wYWlnbi1Mb2NhdGlvbiBBbmFseXNpcw0KDQpGaW5hbGx5LCB3ZSB3YW50ZWQgdG8ga25vdyB3aGljaCBwcm9tb3Rpb25hbCBkaXNwbGF5IGxvY2F0aW9ucyBjb250cmlidXRlZCB0byBlYWNoIGNhbXBhaWduIHR5cGUgYnkgY291bnRpbmcgdGhlICMgb2YgdW5pcXVlIHByb3VjdHMgdGhhdCB3ZXJlIHBsYWNlZCBpbiBlYWNoIGxvY2F0aW9uIGR1cmluZyB0aGUgY2FtcGFpZ24uDQooRm9yIHRoZSBzYWtlIG9mIG9wdGlvbnMsIHdlIG1hZGUgMiBncmFwaHMgZm9yIGVhY2ggdHlwZSkuDQoNCkxvY2F0aW9ucyAwIGFuZCA3IGFyZSB0aGUgdG9wIHR3byBmb3IgVHlwZXMgQSBhbmQgQiwgd2hpbGUgbG9jYXRpb25zIDUgYW5kIDAgYXJlIGNvbW1vbiBmb3IgdHlwZSBDICh3aGljaCBoYXMgbG93ZXIgcGVyZm9ybWFuY2UpLg0KDQpgYGB7ciBEZWZpbmluZyBEYXRhICYgVmFyaWFibGVzLCBlY2hvID0gVFJVRSwgbWVzc2FnZT0gRkFMU0V9DQojRGVmaW5pbmcgdmFyaWFibGVzIHRvIGFuYWx5emUgbG9jYXRpb25zIGluIGVhY2ggY2FtcGFpZ24uIFRoaXMgdXNlcyBsYXJnZSBkYXRhIGZyYW1lcy4NCnBsb3RfZGF0YSA8LSBwcm9tb3Rpb25zICU+JSANCiAgICBncm91cF9ieShkaXNwbGF5X2xvY2F0aW9uLCBwcm9kdWN0X2lkKSAlPiUgDQogICAgc2VsZWN0KGRpc3BsYXlfbG9jYXRpb24sIHByb2R1Y3RfaWQpICU+JSANCiAgICBpbm5lcl9qb2luKHByb2R1Y3RzLCBieSA9ICJwcm9kdWN0X2lkIikgJT4lIA0KICAgIGlubmVyX2pvaW4oY291cG9ucywgYnkgPSAicHJvZHVjdF9pZCIpICU+JSANCiAgICBpbm5lcl9qb2luKGNhbXBhaWduX2Rlc2NyaXB0aW9ucywgYnkgPSAiY2FtcGFpZ25faWQiKSAlPiUNCiAgICBzZWxlY3QoY2FtcGFpZ25fdHlwZSwgZGlzcGxheV9sb2NhdGlvbiwgcHJvZHVjdF9pZCkgJT4lIA0KICAgIGdyb3VwX2J5KGNhbXBhaWduX3R5cGUsIGRpc3BsYXlfbG9jYXRpb24pICU+JSANCiAgICBzdW1tYXJpemUoZGlzdGluY3QgPSBuX2Rpc3RpbmN0KHByb2R1Y3RfaWQpKSAlPiUNCiAgICBhcnJhbmdlKGNhbXBhaWduX3R5cGUsIGRlc2MoZGlzdGluY3QpKQ0KDQpsb2NhdGlvbl9wbG90MSA8LSBOQQ0KbG9jYXRpb25fcGxvdDIgPC0gTkENCmBgYA0KDQpgYGB7ciBDcmVhdGUgdGhlIEZ1bmN0aW9uLCBlY2hvID0gVFJVRX0NCiMgVGhpcyBmdW5jdGlvbiBpbnRha2VzIHRoZSBjYW1wYWlnbiB0eXBlIChleDogJ1R5cGUgQScpIGFuZCBvdXRwdXRzIHR3byByYW5rZWQgZ3JhcGhzIHJlZ2FyZGluZyBsb2NhdGlvbiBjb3VudHMgYnkgZGlzdGluY3QgcHJvZHVjdCBpZA0KDQpjYW1wYWlnbl9sb2NhdGlvbnMgPC0gZnVuY3Rpb24oY2FtcGFpZ25fdHAsIG1lc3NhZ2VzID0gVFJVRSl7DQogIA0KICAgIA0KICBsb2NhdGlvbl9wbG90MSA8PC0gcGxvdF9kYXRhICU+JQ0KICAgIGZpbHRlcihjYW1wYWlnbl90eXBlID09IGNhbXBhaWduX3RwKSAlPiUgDQogICAgZ2dwbG90KGFlcyh4ID0gcmVvcmRlcihkaXNwbGF5X2xvY2F0aW9uLCAtZGlzdGluY3QpLCANCiAgICAgICAgICAgICAgICB5ID0gZGlzdGluY3QsDQogICAgICAgICAgICAgICBmaWxsID0gcmVvcmRlcihkaXNwbGF5X2xvY2F0aW9uLCAtZGlzdGluY3QpKSkgKyANCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBzaG93LmxlZ2VuZCA9IEZBTFNFKSArDQogICAgICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICAgICAgc2NhbGVfZmlsbF9ncmV5KCkgKw0KICAgICAgeGxhYigiRGlzcGxheSBMb2NhdGlvbnMiKSArDQogICAgICB5bGFiKCJEaXN0aW5jdCBQcm9kdWN0IENvdW50IikgKw0KICAgICAgbGFicyggdGl0bGUgPSBwYXN0ZSgiIyBvZiB1bmlxdWUgcHJvZHVjdHMgcGVyIGRpc3BsYXkgbG9jYXRpb24iKSwNCiAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIkZvciIsIGNhbXBhaWduX3RwKSkNCiAgICANCiAgbG9jYXRpb25fcGxvdDIgPDwtIHBsb3RfZGF0YSAlPiUgDQogICAgZmlsdGVyKGNhbXBhaWduX3R5cGUgPT0gY2FtcGFpZ25fdHApICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBkaXN0aW5jdCwgDQogICAgICAgICAgICAgICB5ID0gcmVvcmRlcihkaXNwbGF5X2xvY2F0aW9uLCBkaXN0aW5jdCksDQogICAgICAgICAgICAgICBjb2wgPSAtZGlzdGluY3QpKSArIA0KICAgICAgZ2VvbV9wb2ludChzaG93LmxlZ2VuZCA9IEZBTFNFLCBzaXplID0gNSkgKw0KICAgICAgc2NhbGVfZmlsbF9kaXN0aWxsZXIodHlwZSA9ICJzZXEiLCBwYWxldHRlID0gIkJsdWVzIikgKyAgICAgIA0KICAgICAgeGxhYigiRGlzdGluY3QgUHJvZHVjdCBDb3VudCIpICsNCiAgICAgIHlsYWIoIkRpc3BsYXkgTG9jYXRpb24iKSArDQogICAgICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzID0gc2NhbGVzOjpjb21tYSkgKw0KICAgICAgbGFicyggdGl0bGUgPSBwYXN0ZSgiIyBvZiB1bmlxdWUgcHJvZHVjdHMgcGVyIGRpc3BsYXkgbG9jYXRpb24iKSwNCiAgICAgICAgICAgIHN1YnRpdGxlID0gcGFzdGUoIkZvciIsIGNhbXBhaWduX3RwKSkNCiAgDQogIG1lc3NhZ2UoIkZ1bmN0aW9uIGNvbXBsZXRlIikNCn0gIA0KDQpgYGANCg0KYGBge3IgUnVuIHRoZSBGdW5jdGlvbiwgbWVzc2FnZXMgPSBGQUxTRX0NCiMgUnVubmluZyB0aGUgJ2NhbXBnYWluX2xvY2F0aW9ucygpJyBmdW5jdGlvbiBhbmQgZGlzcGxheWluZyBvdXRwdXRzDQpjbXBnbnMgPC0gYygiVHlwZSBBIiwiVHlwZSBCIiwiVHlwZSBDIikNCmZvcihpIGluIGNtcGducyl7DQogIGNhbXBhaWduX2xvY2F0aW9ucyhpKQ0KICBwcmludChsb2NhdGlvbl9wbG90MSkNCiAgcHJpbnQobG9jYXRpb25fcGxvdDIpDQogIHJtKGxvY2F0aW9uX3Bsb3QxKQ0KICBybShsb2NhdGlvbl9wbG90MikNCn0NCg0Kcm0oY21wZ25zKQ0Kcm0oaSkNCnJtKHBsb3RfZGF0YSkNCmBgYA0KPGJyPg0KDQojIyBTdW1tYXJ5DQoNCiMjIyBQcm9ibGVtIFN0YXRlbWVudDoNCldoaWNoIGNhbXBhaWduIHR5cGVzIGdlbmVyYXRlZCBtb3N0IHNhbGVzIGFuZCB3aGF0IGhvdXNlaG9sZHMgYXJlIGFzc29jaWF0ZWQgd2l0aCB0aGF0IGNhbXBhaWduIHR5cGU/IEFkZGl0aW9uYWxseSwgd2hhdCBmdXJ0aGVyIGluZm9ybWF0aW9uIGNhbiBiZSBnbGVhbmVkIGZyb20gdGhpcyBkYXRhIGFib3V0IENhbXBhaWduIFR5cGVzIEEsIEIsIGFuZCBDIHRoYXQgY2FuIGJlIHVzZWQgdG8gZGV2ZWxvcCBhIG1hcmtldGluZyBzdHJhdGVneT8gPGJyPjxicj4NCg0KIyMjIEFuYWx5c2lzIE1ldGhvZG9sb2d5DQpBZGRyZXNzaW5nIHRoZSBzdHJhdGVneSByZXF1aXJlZCB0byBwcm9wZXJseSBpbnZlc3QgaW4gYWxsIHRocmVlIGNhbXBhaWducyByZXF1aXJlZCBhIG11bHRpLW1ldHJpYyBhcHByb2FjaCwgc28gdGhlIGFuYWx5c2lzIHdhcyBkaXZpZGVkIGludG8gbWFqb3IgY2F0ZWdvcmllcyBvZjogPGJyPg0KDQotIEJhc2ljIHNhbGVzIG1ldHJpY3MgYnkgY2FtcGFpZ24gdHlwZQ0KDQotIEhvdXNlaG9sZCBpbmNvbWUgZGlzdHJpYnV0aW9ucyAgDQoNCi0gTG9jYXRpb24gcG9wdWxhcml0eSBkaXN0cmlidXRpb25zIA0KDQpPbmNlIGJyb2tlbiBpbnRvIHRoZSBhYm92ZS1zdGF0ZWQgcGFydHMsIGVhY2ggYW5hbHlzaXMgdXNlZCBkcGx5ciB0byBqb2luLCBtdXRhdGUsIGFuZCBmaWx0ZXIgKGFuZCBtb3JlKSBtdWx0aXBsZSBkYXRhIHRhYmxlcyBmcm9tIHRoZSBjb21wbGV0ZWpvdXJuZXkgZGF0YS4gVGhlbiwgdGhlIGNyZWF0ZWQgZGF0YSBmcmFtZXMgd2VyZSB2aXN1YWxpemVkIHV0aWxpemluZyBnZ3Bsb3QyIGFuZCBSQ29sb3JCcmV3ZXIuPGJyPjxicj4NCg0KIyMjIFN0cmF0ZWdpYyBJbnNpZ2h0cw0KRnJvbSBhIG1hcmtldGluZyBwZXJzcGVjdGl2ZSwgd2Ugd2FudGVkIHRvIGFkZHJlc3MgdGhlIDQgUCdzIChwcmljaW5nLCBwcm9kdWN0LCBwbGFjZW1lbnQsICYgcHJvbW90aW9uKSB0byBmdWxseSB1bmRlcnN0YW5kIHRoZSBjb25zdW1lci4gDQpXZSByZWNvZ25pemVkIHRoYXQgdGhlIG1vc3QgcG9wdWxhciBpbmNvbWUgcmFuZ2UgZm9yIGFsbCAzIGNhbXBhaWducyB3YXMgNTAtNzRLLiBUaGlzIGlzIGEgZ3JlYXQgaW5zaWdodCB0byB1bmRlcnN0YW5kIHRoZSBjb25zdW1lcnMgdGhhdCBhcmUgcmVhY3RpbmcgcG9zaXRpdmVseSB0byBvdXIgY2FtcGFpZ25zLiBJdCB3aWxsIGFsc28gYWxsb3cgdXMgdG8gaW1wbGVtZW50IGEgc3RyYXRlZ2ljIHByaWNpbmcgYW5kIHByb21vdGlvbiBpbml0aWF0aXZlIGluIHBsYWNlLiA8YnI+PGJyPkFsdGhvdWdoIHRoYXQgaXMgZ3JlYXQsIHdlIGFsc28gaGF2ZSBzb21lIG1pc3NlZCBvcHBvcnR1bml0aWVzIGluIHRoZSBvdGhlciBpbmNvbWUgcmFuZ2VzLiBUaGlzIHdvdWxkIGJlIGEgY2hhbmNlIGZvciBSZWdvcmsgdG8gaW52ZXN0aWdhdGUgd2h5IHRoYXQgbWF5IGJlIGFuZCBpbm5vdmF0ZSBzb2x1dGlvbnMgdG8gYWRkcmVzcyB0aGF0IGluIGZ1dHVyZSBjYW1wYWlnbnMuIDxicj48YnI+TmV4dCwgd2UgYWxzbyBmb3VuZCB0aGF0IHRoZSBoaWdoZXIgdGhlIG51bWJlciBvZiBwcm9kdWN0cyBvZmZlcmVkLCB0aGUgaGlnaGVyIHRoZSBzYWxlcyB2YWx1ZSBmb3IgdGhlIGNhbXBhaWduLiBUaGlzIHByb2R1Y3QgdmFyaWV0eSBzZWVtcyB0byBhdHRyYWN0IGNvbnN1bWVycyBhbmQgcG9zaXRpdmVseSBpbXBhY3QgdGhlaXIgYnV5aW5nIGhhYml0cy4gPGJyPjxicj5GaW5hbGx5LCB3ZSBkaXNjb3ZlcmVkIHRoYXQgY2FtcGFpZ25zIEEgJiBCIHdlcmUgdmVyeSBzdWNjZXNzZnVsIHVzaW5nIGRpc3BsYXkgbG9jYXRpb25zIDAgYW5kIDcuIEdyb2Nlcnkgc3RvcmVzIGFyZSB2ZXJ5IHN0cmF0ZWdpYyBpbiB0aGVpciBwcm9kdWN0IHBsYWNlbWVudCB0byBpbmNyZWFzZSBzYWxlcy4gV2Ugd2FudGVkIHRvIGJlIHNlbnNpdGl2ZSB0byB0aGlzIGFuZCB1bmRlcnN0YW5kIGhvdyB0aGlzIGluZmx1ZW5jZXMgY2FtcGFpZ24gc2FsZXMuPGJyPjxicj4NCg0KIyMjIEltcGxpY2F0aW9ucyBvbiB0aGUgQ3VzdG9tZXINCkJhc2VkIG9uIHRoZSBmaW5kaW5ncyBsaXN0ZWQgYWJvdmUsIG91ciByZWNvbW1lbmRhdGlvbnMgYXJlIHRvOjxicj4NCg0KMS4gQ29udGludWUgdG8gdGFyZ2V0IHRoZSA1MC03NEsgaW5jb21lIHJhbmdlIA0KDQoyLiBJbmNyZWFzZSB0aGUgbnVtYmVyIG9mIHByb2R1Y3RzIHBlciBjYW1wYWlnbiAocGFydGljdWxhcmx5IEMpIA0KDQozLiBSb3RhdGUgdGhlIGNhbXBhaWducyBhbW9uZyBkaXNwbGF5IGxvY2F0aW9ucyAwIGFuZCA3DQoNClRoaXMgd2lsbCBub3Qgb25seSBkcml2ZSBhbHJlYWR5LXByb2ZpdGFibGUgZ3Jvd3RoIGluIHRoZSA1MC03NGsgaW5jb21lIHJhbmdlLCBidXQgYWxzbyBzaGlmdCBDYW1wYWlnbiBDJ3MgbG93LXF1YWxpdHkgc3RyYXRlZ3kgaW50byB0aGUgc2FtZSwgaGlnaGx5IHN1Y2Nlc3NmdWwgc3RyYXRlZ2llcyBhcyBBIGFuZCBCLiA8YnI+PGJyPg0KDQojIyMgQW5hbHlzaXMgTGltaXRhdGlvbnMNCkFmdGVyIGNvbmR1Y3Rpbmcgb3V0IGFuYWx5c2lzLCB3ZSBkaXNjb3ZlcmVkIDQgcHJpbWFyeSBsaW1pdGF0aW9uczsgdW5rbm93biBsb2NhdGlvbnMsIG92ZXJsYXBwaW5nIGNhbXBhaWducywgY2FtcGFpZ24gYnVkZ2V0LCBhbmQgdW5rbm93biBwcm9kdWN0IGNhdGVnb3JpZXMuIDxicj48YnI+T25lIG9mIG91ciByZWNvbW1lbmRhdGlvbnMgd2FzIHRvIHJvdGF0ZSB0aGUgY2FtcGFpZ25zIGJldHdlZW4gZGlzcGxheSBsb2NhdGlvbnMgMCBhbmQgNy4gQWx0aG91Z2ggdGhhdCBpcyBnb29kIGluIHRoZW9yeSwgdGhpcyBwb3NlcyBzb21lIHBvdGVudGlhbCByb2FkIGJsb2Nrcy4gRmlyc3QsIHdlIGRvIG5vdCBoYXZlIGFueSBpbmZvcm1hdGlvbiBvbiB0aGVzZSBsb2NhdGlvbnMgb3RoZXIgdGhhbiB0aGVpciBkZXNpZ25hdGVkIG51bWJlci4gVGhpcyBpcyBjcnVjaWFsIGluZm9ybWF0aW9uIGVzcGVjaWFsbHkgaW4gYSBncm9jZXJ5IHN0b3JlIHNldHRpbmcuIElzIGl0IGFuIGVuZCBjYXA/IElzIGl0IHJlZnJpZ2VyYXRlZD8gSG93IG11Y2ggc3BhY2UgaXMgYXZhaWxhYmxlPyBBbGwgb2YgdGhlc2UgdGhpbmdzIHdvdWxkIGJlIG5lY2Vzc2FyeSBpbmZvcm1hdGlvbiB0byBpbXBsZW1lbnQgdGhlIHJlY29tbWVuZGF0aW9uLiA8YnI+PGJyPk5leHQsIHNpbmNlIHdlIHJlY29tbWVuZGVkIHJvdGF0aW5nIHRoZSBjYW1wYWlnbnMgdGhpcyBtZWFucyBubyBjYW1wYWlnbnMgY2FuIG92ZXJsYXAgaW4gdGltZWxpbmVzIHdoaWNoIGNvdWxkIGNhdXNlIGFuIGlzc3VlLiBBbm90aGVyIGxpbWl0YXRpb24gaXMgY2FtcGFpZ24gYnVkZ2V0LiBPbmUgb2Ygb3VyIHJlY29tbWVuZGF0aW9ucyB3YXMgdG8gYWRkIG1vcmUgcHJvZHVjdHMgdG8gQ2FtcGFpZ24gQy4gSG93ZXZlciwgbW9yZSBwcm9kdWN0cyByZXF1aXJlIG1vcmUgY2FwaXRhbCB3aGljaCBtYXkgbm90IGJlIGF2YWlsYWJsZSBpbiB0aGUgYnVkZ2V0LiA8YnI+PGJyPkxhc3RseSwgdGhlIGRhdGEgc2V0IGRpZCBub3QgcHJvdmlkZSBwcm9kdWN0IGNhdGVnb3JpZXMuIFRoaXMgaW5mb3JtYXRpb24gd291bGQndmUgYmVlbiBpbnN0cnVtZW50YWwgaW4gY3JlYXRpbmcgbW9yZSBzcGVjaWZpYyBhbmQgc3VjY2Vzc2Z1bCBjYW1wYWlnbnMuDQoNCg0K