####################
# Import Libraries #
####################
library(Amelia)
library(grid)
library(gridExtra)
library(tidyverse)
library(lubridate)
library(plotly)
library(factoextra)
library(reshape)
##############################
# Graphics Themes for GGplot #
##############################
my_theme <- function(base_size = 10, base_family = "sans"){
  theme_minimal(base_size = base_size, base_family = base_family) +
    theme(
      axis.text = element_text(size = 10),
      axis.text.x = element_text(angle = 0, vjust = 0.5, hjust = 0.5),
      axis.title = element_text(size = 12),
      panel.grid.major = element_line(color = "grey"),
      panel.grid.minor = element_blank(),
      panel.background = element_rect(fill = "#ffffef"),
      strip.background = element_rect(fill = "#ffbb00", color = "black", size =0.5),
      strip.text = element_text(face = "bold", size = 10, color = "black"),
      legend.position = "bottom",
      legend.justification = "center",
      legend.background = element_blank(),
      panel.border = element_rect(color = "grey30", fill = NA, size = 0.5)
    )
}
theme_set(my_theme())

mycolors=c("#f32440","#ffd700","#ff8c00","#c9e101","#c100e6","#39d3d6",
           "#e84412", "#B2182B", "#D6604D", "#F4A582", "#FDDBC7", "#D1E5F0",
           "#999999", "#E69F00", "#56B4E9")

1 Introduction

Channel KLMN is based in Los Angeles and airs a weekly national political talk show creatively called US Politics This Week. Their only TV commercial advertising US Politics This Week features the Mayor of Los Angeles.

2 Objective

To understand and verify if there is a lower fraction of people who saw the commercials with their local Mayor watched US Politics This Week as compared to people who saw the commercial with the Mayor of Los Angeles.

3 About the Data

The Dataset is from the Channel KLMN. This data is filled with two data sets which are test_data.csv and viewer_data. The first one data set has 9 features and the second one has 4 features.

sales components in 3 supemarket branches and includes 17 variables.

3.1 Load the Data

####################
# Read the DataSet #
###################
setwd("C:/Wecsley/USA/Samba_TV")
test_data <- read.csv("test_data.csv")
viewer_data <- read.csv("viewer_data.csv")

The test_data information.

##############################
# test_data data information #
##############################
head(test_data)
viewer_id date tv_make tv_size uhd_capable tv_provider total_time_watched watched test
24726768 2018-01-16 Sony 70 0 Comcast 10.75 0 1
25001464 2018-01-18 Sony 32 0 2.75 0 0
28291998 2018-01-18 Sony 50 1 Dish Network 20.00 0 0
17057157 2018-01-19 Sony 32 0 Comcast 1.50 0 0
29504447 2018-01-17 Sony 32 0 Comcast 17.50 0 0
15529477 2018-01-18 LG 32 0 Cox 14.75 0 1
dim(test_data)
## [1] 418026      9
names(test_data)
## [1] "viewer_id"          "date"               "tv_make"           
## [4] "tv_size"            "uhd_capable"        "tv_provider"       
## [7] "total_time_watched" "watched"            "test"

The viewer_data information.

################################
# viewer_data data information #
################################
head(viewer_data)
viewer_id gender age city
1918165 Female 39 Dallas
27662619 Female 28 New York
5493662 Female 53 Detroit
14441247 Male 41 New York
25595927 Male 53 Seattle
16827252 Male 37 New York
dim(viewer_data)
## [1] 417464      4
names(viewer_data)
## [1] "viewer_id" "gender"    "age"       "city"

Attribute information test_data

  • viewer_id: the ID of the viewer

  • date: the date the viewer saw a commercial for “US Politics This Week”

  • tv_make: the make (i.e., brand) of TV

  • tv_size: the size of the TV in inches (approximately measured as the diagonal of the screen)

  • uhd_capable: whether the TV is (1) or is not (0) capable of displaying Ultra-High-Definition television content

  • tv_provider: the cable or satellite TV provider

  • total_time_watched: the total amount of TV watched (in hours) on the day in the date column

  • watched: whether the viewer watched (1) “US Politics This Week” or not (0)

  • test: viewers are split into test (1) and control (0) groups; test viewers saw the new commercial with their local Mayor while control viewers saw the old commercial with the Mayor of Los Angeles

Attribute information test_data

  • viewer_id: the ID of the viewer (same ID as in the test_data.csv file)

  • gender: the viewer’s gender

  • age: the viewer’s age

  • city: the viewer’s city

First, we will perform a cleaning process in the database, in order to carry out an exploration of the Data, to better understand the variables.

3.2 Data Cleansing & Join the Data Sets

####################
# Features Summary #
####################
Hmisc::describe(test_data)
## test_data 
## 
##  9  Variables      418026  Observations
## --------------------------------------------------------------------------------
## viewer_id 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##   418026        0   418020        1 18355853 14390488    45765    81659 
##      .25      .50      .75      .90      .95 
##  7526723 18383268 29163528 35670978 37848442 
## 
## lowest :    10000    10002    10003    10005    10006
## highest: 39999726 39999736 39999781 39999808 39999921
## --------------------------------------------------------------------------------
## date 
##        n  missing distinct 
##   418026        0        5 
##                                                                  
## Value      2018-01-15 2018-01-16 2018-01-17 2018-01-18 2018-01-19
## Frequency       86641      82989      83145      82544      82707
## Proportion      0.207      0.199      0.199      0.197      0.198
## --------------------------------------------------------------------------------
## tv_make 
##        n  missing distinct 
##   418026        0        4 
##                                           
## Value           LG Philips    Sony Toshiba
## Frequency    41769   41836  271922   62499
## Proportion    0.10    0.10    0.65    0.15
## --------------------------------------------------------------------------------
## tv_size 
##        n  missing distinct     Info     Mean      Gmd 
##   418026        0        8    0.984    51.87    13.97 
##                                                           
## Value         32    40    43    50    55    60    65    70
## Frequency  52286 52388 52190 52086 52254 52240 52305 52277
## Proportion 0.125 0.125 0.125 0.125 0.125 0.125 0.125 0.125
## --------------------------------------------------------------------------------
## uhd_capable 
##        n  missing distinct     Info      Sum     Mean      Gmd 
##   418026        0        2    0.482    83964   0.2009    0.321 
## 
## --------------------------------------------------------------------------------
## tv_provider 
##        n  missing distinct 
##   418026        0        6 
##                                                                 
## Value                                  Comcast               Cox
## Frequency              52720            109796             72809
## Proportion             0.126             0.263             0.174
##                                                                 
## Value                DirecTV      Dish Network Time Warner Cable
## Frequency              36738             36249            109714
## Proportion             0.088             0.087             0.262
## --------------------------------------------------------------------------------
## total_time_watched 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##   418026        0       95        1    10.04    7.074     1.25     2.00 
##      .25      .50      .75      .90      .95 
##     5.00     9.50    14.50    19.25    21.25 
## 
## lowest :  0.25  0.50  0.75  1.00  1.25, highest: 22.75 23.00 23.25 23.50 23.75
## --------------------------------------------------------------------------------
## watched 
##        n  missing distinct     Info      Sum     Mean      Gmd 
##   418026        0        2    0.155    22802  0.05455   0.1031 
## 
## --------------------------------------------------------------------------------
## test 
##        n  missing distinct     Info      Sum     Mean      Gmd 
##   418026        0        2     0.75   204327   0.4888   0.4997 
## 
## --------------------------------------------------------------------------------
############################# 
# Graphic for Missing Data  #
#############################
missmap(test_data)

####################
# Features Summary #
####################
Hmisc::describe(viewer_data)
## viewer_data 
## 
##  4  Variables      417464  Observations
## --------------------------------------------------------------------------------
## viewer_id 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##   417464        0   417464        1 18379082 14381963    45723    81560 
##      .25      .50      .75      .90      .95 
##  7571130 18415881 29179002 35677581 37850556 
## 
## lowest :    10000    10002    10003    10005    10006
## highest: 39999726 39999736 39999781 39999808 39999921
## --------------------------------------------------------------------------------
## gender 
##        n  missing distinct 
##   417464        0        2 
##                         
## Value      Female   Male
## Frequency  209416 208048
## Proportion  0.502  0.498
## --------------------------------------------------------------------------------
## age 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##   417464        0       55    0.999    40.47    13.62       24       26 
##      .25      .50      .75      .90      .95 
##       31       39       48       57       63 
## 
## lowest : 18 19 20 21 22, highest: 68 71 72 73 74
## --------------------------------------------------------------------------------
## city 
##        n  missing distinct 
##   417464        0       15 
## 
## Atlanta (24326, 0.058), Boston (24418, 0.058), Chicago (33043, 0.079), Dallas
## (26681, 0.064), Detroit (17931, 0.043), Houston (24391, 0.058), Los Angeles
## (52513, 0.126), Miami (17285, 0.041), Minneapolis (17295, 0.041), New York
## (69893, 0.167), Philadelphia (28756, 0.069), Phoenix (19332, 0.046), San
## Francisco (24300, 0.058), Seattle (18696, 0.045), Tampa (18604, 0.045)
## --------------------------------------------------------------------------------
############################# 
# Graphic for Missing Data  #
#############################
missmap(viewer_data)

As We can see, there is no missing in both data sets. Let´s now merge the two data sets.

######################################
# Join the two Data Set by Viewer ID #
######################################
full_data <- merge(test_data, viewer_data,
                                  by = "viewer_id") %>% 
  na.omit() 

#########################
# Structure of the Data #
#########################
head(full_data)
viewer_id date tv_make tv_size uhd_capable tv_provider total_time_watched watched test gender age city
10000 2018-01-15 Sony 55 0 Time Warner Cable 5.25 0 0 Female 31 Los Angeles
10002 2018-01-17 Sony 60 1 Time Warner Cable 23.25 0 0 Male 41 Los Angeles
10003 2018-01-19 LG 70 1 Cox 9.25 0 0 Female 29 Los Angeles
10005 2018-01-15 Sony 43 0 Comcast 2.75 0 0 Male 53 Los Angeles
10006 2018-01-16 LG 55 0 Time Warner Cable 20.00 1 0 Female 58 Los Angeles
10007 2018-01-16 Sony 70 0 Time Warner Cable 16.50 0 0 Male 38 Los Angeles
####################
# Features Summary #
####################
Hmisc::describe(full_data %>%
                  select(tv_make, tv_provider, total_time_watched,
                         gender, age, city))
## full_data %>% select(tv_make, tv_provider, total_time_watched, gender, age, city) 
## 
##  6  Variables      417470  Observations
## --------------------------------------------------------------------------------
## tv_make 
##        n  missing distinct 
##   417470        0        4 
##                                           
## Value           LG Philips    Sony Toshiba
## Frequency    41706   41774  271574   62416
## Proportion   0.100   0.100   0.651   0.150
## --------------------------------------------------------------------------------
## tv_provider 
##        n  missing distinct 
##   417470        0        6 
##                                                                 
## Value                                  Comcast               Cox
## Frequency              52644            109641             72700
## Proportion             0.126             0.263             0.174
##                                                                 
## Value                DirecTV      Dish Network Time Warner Cable
## Frequency              36701             36213            109571
## Proportion             0.088             0.087             0.262
## --------------------------------------------------------------------------------
## total_time_watched 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##   417470        0       95        1    10.04    7.075     1.25     2.00 
##      .25      .50      .75      .90      .95 
##     5.00     9.50    14.50    19.25    21.25 
## 
## lowest :  0.25  0.50  0.75  1.00  1.25, highest: 22.75 23.00 23.25 23.50 23.75
## --------------------------------------------------------------------------------
## gender 
##        n  missing distinct 
##   417470        0        2 
##                         
## Value      Female   Male
## Frequency  209420 208050
## Proportion  0.502  0.498
## --------------------------------------------------------------------------------
## age 
##        n  missing distinct     Info     Mean      Gmd      .05      .10 
##   417470        0       55    0.999    40.47    13.62       24       26 
##      .25      .50      .75      .90      .95 
##       31       39       48       57       63 
## 
## lowest : 18 19 20 21 22, highest: 68 71 72 73 74
## --------------------------------------------------------------------------------
## city 
##        n  missing distinct 
##   417470        0       15 
## 
## Atlanta (24327, 0.058), Boston (24418, 0.058), Chicago (33045, 0.079), Dallas
## (26681, 0.064), Detroit (17931, 0.043), Houston (24391, 0.058), Los Angeles
## (52513, 0.126), Miami (17285, 0.041), Minneapolis (17296, 0.041), New York
## (69894, 0.167), Philadelphia (28756, 0.069), Phoenix (19332, 0.046), San
## Francisco (24300, 0.058), Seattle (18696, 0.045), Tampa (18605, 0.045)
## --------------------------------------------------------------------------------

Let´s convert the date feature. Because this features is a Date feature, but in the original data set it is like Factor.

####################
# Convert the Date #
####################
full_data$date <- as.Date(full_data$date, "%Y-%m-%d")
str(full_data)
## 'data.frame':    417470 obs. of  12 variables:
##  $ viewer_id         : int  10000 10002 10003 10005 10006 10007 10012 10014 10015 10016 ...
##  $ date              : Date, format: "2018-01-15" "2018-01-17" ...
##  $ tv_make           : Factor w/ 4 levels "LG","Philips",..: 3 3 1 3 1 3 3 3 1 2 ...
##  $ tv_size           : int  55 60 70 43 55 70 65 40 50 70 ...
##  $ uhd_capable       : int  0 1 1 0 0 0 0 0 0 0 ...
##  $ tv_provider       : Factor w/ 6 levels "","Comcast","Cox",..: 6 6 3 2 6 6 2 3 5 1 ...
##  $ total_time_watched: num  5.25 23.25 9.25 2.75 20 ...
##  $ watched           : int  0 0 0 0 1 0 0 0 0 0 ...
##  $ test              : int  0 0 0 0 0 0 0 0 0 0 ...
##  $ gender            : Factor w/ 2 levels "Female","Male": 1 2 1 2 1 2 2 1 1 2 ...
##  $ age               : int  31 41 29 53 58 38 62 38 42 54 ...
##  $ city              : Factor w/ 15 levels "Atlanta","Boston",..: 7 7 7 7 7 7 7 7 7 7 ...

4 Data Preprocessing, Exploration & Visualizations

Let´s reproduce the first result found by Channel KLMN’s data scientist. I mean, let´s figure it out if there is a large fraction of people who saw the commercial for US Politics This Week and watched the show are from Los Angeles, while a much lower fraction of people from other cities who saw the commercial watched the show

#####################
# Grouping by City  #
#####################
full_data_frac <- full_data %>% 
  select(watched, city) %>% 
  group_by(city) %>% 
  summarise(Total = sum(watched)) %>% 
  mutate(Proportion = Total/sum(Total)) %>%
  arrange(-Total)
full_data_frac
city Total Proportion
Los Angeles 5412 0.2373476
New York 3599 0.1578370
Chicago 1672 0.0733269
Dallas 1315 0.0576704
Houston 1268 0.0556092
Boston 1267 0.0555653
Atlanta 1230 0.0539426
San Francisco 1210 0.0530655
Phoenix 1016 0.0445575
Tampa 958 0.0420139
Minneapolis 913 0.0400403
Detroit 899 0.0394264
Miami 881 0.0386370
Philadelphia 675 0.0296027
Seattle 487 0.0213578
###########
# Graphic #
###########
plot_full_data_frac <- full_data_frac %>% 
                ggplot(aes(x = city,
                           y = Proportion,
                           fill = city)) +
               my_theme() +
               geom_col(position = "dodge") +
               scale_y_continuous(limits = c(0, 0.3))
ggplotly(plot_full_data_frac)

As We can see, the fraction of people who saw the commercial for US Politics This Week and watched the show from Los Angeles is \(23,7\%\) is the highest fraction, the second city is from New York which fraction is \(15,8\%\). I mean, the fraction from of people who saw the commercial from Los Angeles is \(50\%\) higher than New York viewers.

Now, Let´s Reproduce the negative result found by Channel KLMN’s data scientist, i.e, let´s investigate if the commercials with local Mayors really driving a lower fraction of people to watch the show.

#############################
# Grouping by City and Test #
#############################
full_data_test <- full_data %>% 
  select(watched, city, test) %>% 
  group_by(city, test) %>% 
  summarise(Total_Watched = sum(watched)) %>% 
  mutate(Proportion_Watched = Total_Watched/sum(Total_Watched)) %>%
  arrange(-Total_Watched)
full_data_test
city test Total_Watched Proportion_Watched
Los Angeles 0 5412 1.0000000
New York 0 1808 0.5023618
New York 1 1791 0.4976382
Chicago 1 878 0.5251196
Chicago 0 794 0.4748804
Boston 1 662 0.5224941
Houston 1 662 0.5220820
Dallas 1 660 0.5019011
Dallas 0 655 0.4980989
Atlanta 1 641 0.5211382
San Francisco 1 626 0.5173554
Houston 0 606 0.4779180
Boston 0 605 0.4775059
Atlanta 0 589 0.4788618
San Francisco 0 584 0.4826446
Philadelphia 1 575 0.8518519
Phoenix 1 516 0.5078740
Tampa 1 501 0.5229645
Phoenix 0 500 0.4921260
Detroit 1 489 0.5439377
Minneapolis 1 477 0.5224535
Miami 1 469 0.5323496
Tampa 0 457 0.4770355
Minneapolis 0 436 0.4775465
Miami 0 412 0.4676504
Detroit 0 410 0.4560623
Seattle 1 407 0.8357290
Philadelphia 0 100 0.1481481
Seattle 0 80 0.1642710
###########
# Graphic #
###########
plot_full_data_test <- full_data_test %>% 
                ggplot(aes(x = city,
                           y = Proportion_Watched)) +
               my_theme() +
               geom_col(position = "dodge") +
  scale_fill_manual(values=mycolors)+
  facet_grid(test~.)
ggplotly(plot_full_data_test)

As We can see, almost all the cities, has almost the same proportion of viewers who saw the new commercial with their local Mayor and the viewers who saw the old commercial with the Mayor of Los Angeles. Only two cities (Philadelphia and Seattle) has such difference between these two groups.

So, this Plots show us that the commercials with local Mayors are not driving a lower fraction of people to watch the show in almost the cities.

Let´s grouping by type test (0 or 1) in order to verify if there is a statistical significance between these two groups. Our Hypotheses will be:

\(H0:\) The proportion of group 0 is equal \(50/%\)

\(Ha:\) The proportion of group 0 is less than \(50/%\)

I mean, if the \(Ha\) is true, this means that the fraction of viewers who saw the old commercial with the Mayor of Los Angeles is less than the fraction of viewers saw the new commercial with their local Mayor. For this test We have to exclude the Los Angeles city from the Data Set, because the proportion of this group is, obvious equal to 1, because the experiment is about the Major from Los Angeles.

##################################################
# Grouping by City  and Test without Los Angeles #
##################################################
full_data_hyp <- subset(full_data, city!="Los Angeles") %>% 
  select(watched, city, test) %>% 
  group_by(test) %>% 
  summarise(Total = sum(watched)) %>% 
  mutate(Proportion_Watched = Total/sum(Total)) %>%
  arrange(-Total)
full_data_hyp
test Total Proportion_Watched
1 9354 0.5378953
0 8036 0.4621047
###########
# Graphic #
###########
plot_full_data_hyp <- full_data_hyp %>% 
                ggplot(aes(x = as.factor(test),
                           y = Proportion_Watched,
                           fill = as.factor(test))) +
               my_theme() +
               geom_col(position = "dodge") +
  scale_fill_manual(values=mycolors)
ggplotly(plot_full_data_hyp)
##########
# Test Z #
##########
prop.test(full_data_hyp$Total[2], sum(full_data_hyp$Total), p = 0.5, 
          alternative = "less",
          correct = TRUE)
## 
##  1-sample proportions test with continuity correction
## 
## data:  full_data_hyp$Total[2] out of sum(full_data_hyp$Total), null probability 0.5
## X-squared = 99.741, df = 1, p-value < 2.2e-16
## alternative hypothesis: true p is less than 0.5
## 95 percent confidence interval:
##  0.0000000 0.4683575
## sample estimates:
##         p 
## 0.4621047

As We can see, the fraction of viewers saw the new commercial with their local Mayor while is really less than the old commercial with the Mayor of Los Angeles.

5 Investigating the Difference

City x test

full_data$test <- as.factor(full_data$test)
a1 <- full_data %>% ggplot(aes(x=city,fill=test))+
  geom_bar(position="fill",color="black",alpha=0.8,show.legend = T)+
  scale_fill_manual(values=mycolors)+
  coord_flip()+
  ggtitle("city")
a1

Watched x test

a2 <- full_data %>% ggplot(aes(x=watched,fill=test))+
  geom_bar(position="fill",color="black",alpha=0.8,show.legend = T)+
  scale_fill_manual(values=mycolors)+
  coord_flip()+
  ggtitle("watched")
a2

uhd_capable x test

a3 <- full_data %>% ggplot(aes(x=uhd_capable,fill=test))+
  geom_bar(position="fill",color="black",alpha=0.8,show.legend = T)+
  scale_fill_manual(values=mycolors)+
  coord_flip()+
  ggtitle("uhd_capable")
a3

tv_provider x test

a4 <- full_data %>% ggplot(aes(x=tv_provider,fill=test))+
  geom_bar(position="fill",color="black",alpha=0.8,show.legend = T)+
  scale_fill_manual(values=mycolors)+
  coord_flip()+
  ggtitle("tv_provider")
a4

Gender x test

a5 <- full_data %>% ggplot(aes(x=gender,fill=test))+
  geom_bar(position="fill",color="black",alpha=0.8,show.legend = T)+
  scale_fill_manual(values=mycolors)+
  coord_flip()+
  ggtitle("gender")
a5

Gender x city

a6 <- full_data %>% ggplot(aes(x=gender,fill=city))+
  geom_bar(position="fill",color="black",alpha=0.8,show.legend = T)+
  scale_fill_manual(values=mycolors)+
  coord_flip()+
  ggtitle("city")
a6

total_time_watched x test

full_data %>% gather(total_time_watched,
              key="Features",value="Value") %>%
  ggplot(aes(x=test,y=Value,fill=test))+
  geom_boxplot(alpha=0.8,color="black")+
  coord_flip()+facet_wrap(~Features,ncol=1,scales="free")+
  scale_fill_manual(values=mycolors)

total_time_watched x city

full_data %>% gather(total_time_watched,
              key="Features",value="Value") %>%
  ggplot(aes(x=city,y=Value,fill=city))+
  geom_boxplot(alpha=0.8,color="black")+
  coord_flip()+facet_wrap(~Features,ncol=1,scales="free")+
  scale_fill_manual(values=mycolors)

6 Extra: Pivot Table

With this Pivot Table We can manipulate and see the distribution for whatever variables combination. It´s totally interactive Pivot Table. We can drag the variables to the columns or rows, choose the categories, make filters, choose which metrics or graphs.

########################
# Import the Libraries #
########################
library(rpivotTable)
library(data.table)

rpivotTable(setDT(full_data),
            cols= "age",
            rows = "city",
            rendererName = "Heatmap")

7 Code for this Experiment

#########################
# Code for the function #
#########################
######################
# Read the Data Sets #
######################
test_data <- read.csv("test_data.csv")
viewer_data <- read.csv("viewer_data.csv")

######################################
# Join the two Data Set by Viewer ID #
######################################
data <- merge(test_data, viewer_data,
              by = "viewer_id") %>% 
  na.omit()

#################################################
# Grouping by City ans Test without Los Angeles #
#################################################
data <- subset(full_data, city!="Los Angeles") %>% 
  select(watched, city, test) %>% 
  group_by(test) %>% 
  summarise(Total = sum(watched)) %>% 
  mutate(Proportion_Watched = Total/sum(Total)) %>%
  arrange(-Total)

test_prop <- function(data){  
  
  ##########
  # Test Z #
  ##########
  result <- prop.test(data$Total[2], sum(data$Total), p = 0.5, 
                      alternative = "less",
                      correct = TRUE)
  
  ##################
  # Output of Test #
  ##################
  output <- ifelse(result$p.value > 0.05, 
                   "The commercials with local Mayors is driving a higher fraction of people to watch the show", 
                   "The commercials with local Mayors is driving a lower fraction of people to watch the show")
  print(output)
}
LS0tDQp0aXRsZTogPGNlbnRlcj4gVVMgUG9saXRpY3MgVGhpcyBXZWVrIFRhbGsgU2hvdyBBbmFseXNpcyA8Y2VudGVyPg0KYXV0aG9yOiA8Y2VudGVyPiBXZWNzbGV5IE8uIFByYXRlcyA8Y2VudGVyPg0KZGF0ZTogPGNlbnRlcj4gRGVjZW1iZXIsIDE1LCAyMDIwIDwvY2VudGVyPg0Kb3V0cHV0OiANCiAgaHRtbF9kb2N1bWVudDogDQogICAgY29kZV9kb3dubG9hZDogeWVzDQogICAgY29kZV9mb2xkaW5nOiBzaG93DQogICAgZGZfcHJpbnQ6IGthYmxlDQogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMNCiAgICB0aGVtZTogZmxhdGx5DQogICAgdG9jOiB5ZXMNCiAgICB0b2NfZmxvYXQ6IHllcw0Kc3VidGl0bGU6IDxjZW50ZXI+IFVTIFBvbGl0aWNzIFRoaXMgV2VlayBUYWxrIFNob3cgQW5hbHlzaXMgPC9jZW50ZXI+DQpwYWdldGl0bGU6IDxjZW50ZXI+IFVTIFBvbGl0aWNzIFRoaXMgV2VlayBUYWxrIFNob3cgQW5hbHlzaXMgPC9jZW50ZXI+DQotLS0NCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgSW1wb3J0IExpYnJhcmllcyAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KbGlicmFyeShBbWVsaWEpDQpsaWJyYXJ5KGdyaWQpDQpsaWJyYXJ5KGdyaWRFeHRyYSkNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KHBsb3RseSkNCmxpYnJhcnkoZmFjdG9leHRyYSkNCmxpYnJhcnkocmVzaGFwZSkNCg0KYGBgDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBHcmFwaGljcyBUaGVtZXMgZm9yIEdHcGxvdCAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCm15X3RoZW1lIDwtIGZ1bmN0aW9uKGJhc2Vfc2l6ZSA9IDEwLCBiYXNlX2ZhbWlseSA9ICJzYW5zIil7DQogIHRoZW1lX21pbmltYWwoYmFzZV9zaXplID0gYmFzZV9zaXplLCBiYXNlX2ZhbWlseSA9IGJhc2VfZmFtaWx5KSArDQogICAgdGhlbWUoDQogICAgICBheGlzLnRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSwNCiAgICAgIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gMCwgdmp1c3QgPSAwLjUsIGhqdXN0ID0gMC41KSwNCiAgICAgIGF4aXMudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEyKSwNCiAgICAgIHBhbmVsLmdyaWQubWFqb3IgPSBlbGVtZW50X2xpbmUoY29sb3IgPSAiZ3JleSIpLA0KICAgICAgcGFuZWwuZ3JpZC5taW5vciA9IGVsZW1lbnRfYmxhbmsoKSwNCiAgICAgIHBhbmVsLmJhY2tncm91bmQgPSBlbGVtZW50X3JlY3QoZmlsbCA9ICIjZmZmZmVmIiksDQogICAgICBzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiI2ZmYmIwMCIsIGNvbG9yID0gImJsYWNrIiwgc2l6ZSA9MC41KSwNCiAgICAgIHN0cmlwLnRleHQgPSBlbGVtZW50X3RleHQoZmFjZSA9ICJib2xkIiwgc2l6ZSA9IDEwLCBjb2xvciA9ICJibGFjayIpLA0KICAgICAgbGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsDQogICAgICBsZWdlbmQuanVzdGlmaWNhdGlvbiA9ICJjZW50ZXIiLA0KICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3IgPSAiZ3JleTMwIiwgZmlsbCA9IE5BLCBzaXplID0gMC41KQ0KICAgICkNCn0NCnRoZW1lX3NldChteV90aGVtZSgpKQ0KDQpteWNvbG9ycz1jKCIjZjMyNDQwIiwiI2ZmZDcwMCIsIiNmZjhjMDAiLCIjYzllMTAxIiwiI2MxMDBlNiIsIiMzOWQzZDYiLA0KICAgICAgICAgICAiI2U4NDQxMiIsICIjQjIxODJCIiwgIiNENjYwNEQiLCAiI0Y0QTU4MiIsICIjRkREQkM3IiwgIiNEMUU1RjAiLA0KICAgICAgICAgICAiIzk5OTk5OSIsICIjRTY5RjAwIiwgIiM1NkI0RTkiKQ0KDQpgYGANCg0KIyBJbnRyb2R1Y3Rpb24gey50YWJzZXR9DQoNCkNoYW5uZWwgS0xNTiBpcyBiYXNlZCBpbiBMb3MgQW5nZWxlcyBhbmQgYWlycyBhIHdlZWtseSBuYXRpb25hbCBwb2xpdGljYWwgdGFsayBzaG93IGNyZWF0aXZlbHkgY2FsbGVkIGBVUyBQb2xpdGljcyBUaGlzIFdlZWtgLiBUaGVpciBvbmx5IFRWIGNvbW1lcmNpYWwgYWR2ZXJ0aXNpbmcgYFVTIFBvbGl0aWNzIFRoaXMgV2Vla2AgZmVhdHVyZXMgdGhlIE1heW9yIG9mIExvcyBBbmdlbGVzLg0KDQojIE9iamVjdGl2ZQ0KDQpUbyB1bmRlcnN0YW5kIGFuZCB2ZXJpZnkgaWYgdGhlcmUgaXMgYSBsb3dlciBmcmFjdGlvbiBvZiBwZW9wbGUgd2hvIHNhdyB0aGUgY29tbWVyY2lhbHMgd2l0aCB0aGVpciBsb2NhbCBNYXlvciB3YXRjaGVkIGBVUyBQb2xpdGljcyBUaGlzIFdlZWtgIGFzIGNvbXBhcmVkIHRvIHBlb3BsZSB3aG8gc2F3IHRoZSBjb21tZXJjaWFsIHdpdGggdGhlIE1heW9yIG9mIExvcyBBbmdlbGVzLiANCg0KIyBBYm91dCB0aGUgRGF0YQ0KDQpUaGUgRGF0YXNldCBpcyBmcm9tIHRoZSBgQ2hhbm5lbCBLTE1OYC4gVGhpcyBkYXRhIGlzIGZpbGxlZCB3aXRoIHR3byBkYXRhIHNldHMgd2hpY2ggYXJlIGB0ZXN0X2RhdGEuY3N2YCBhbmQgYHZpZXdlcl9kYXRhYC4gVGhlIGZpcnN0IG9uZSBkYXRhIHNldCBoYXMgOSBmZWF0dXJlcyBhbmQgdGhlIHNlY29uZCBvbmUgaGFzIDQgZmVhdHVyZXMuIA0KDQpzYWxlcyBjb21wb25lbnRzIGluIDMgc3VwZW1hcmtldCBicmFuY2hlcyBhbmQgaW5jbHVkZXMgMTcgdmFyaWFibGVzLg0KDQojIyBMb2FkIHRoZSBEYXRhDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIFJlYWQgdGhlIERhdGFTZXQgIw0KIyMjIyMjIyMjIyMjIyMjIyMjIw0Kc2V0d2QoIkM6L1dlY3NsZXkvVVNBL1NhbWJhX1RWIikNCnRlc3RfZGF0YSA8LSByZWFkLmNzdigidGVzdF9kYXRhLmNzdiIpDQp2aWV3ZXJfZGF0YSA8LSByZWFkLmNzdigidmlld2VyX2RhdGEuY3N2IikNCg0KYGBgDQoNClRoZSBgdGVzdF9kYXRhYCBpbmZvcm1hdGlvbi4gDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyB0ZXN0X2RhdGEgZGF0YSBpbmZvcm1hdGlvbiAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmhlYWQodGVzdF9kYXRhKQ0KZGltKHRlc3RfZGF0YSkNCm5hbWVzKHRlc3RfZGF0YSkNCg0KYGBgDQoNClRoZSBgdmlld2VyX2RhdGFgIGluZm9ybWF0aW9uLiANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgdmlld2VyX2RhdGEgZGF0YSBpbmZvcm1hdGlvbiAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KaGVhZCh2aWV3ZXJfZGF0YSkNCmRpbSh2aWV3ZXJfZGF0YSkNCm5hbWVzKHZpZXdlcl9kYXRhKQ0KDQpgYGANCg0KKkF0dHJpYnV0ZSBpbmZvcm1hdGlvbiBgdGVzdF9kYXRhYCoNCg0KLSBgdmlld2VyX2lkYDogdGhlIElEIG9mIHRoZSB2aWV3ZXINCg0KLSBgZGF0ZWA6IHRoZSBkYXRlIHRoZSB2aWV3ZXIgc2F3IGEgY29tbWVyY2lhbCBmb3Ig4oCcVVMgUG9saXRpY3MgVGhpcyBXZWVr4oCdDQotIGB0dl9tYWtlYDogdGhlIG1ha2UgKGkuZS4sIGJyYW5kKSBvZiBUVg0KDQotIGB0dl9zaXplYDogdGhlIHNpemUgb2YgdGhlIFRWIGluIGluY2hlcyAoYXBwcm94aW1hdGVseSBtZWFzdXJlZCBhcyB0aGUgZGlhZ29uYWwgb2YgdGhlIHNjcmVlbikNCg0KLSBgdWhkX2NhcGFibGVgOiB3aGV0aGVyIHRoZSBUViBpcyAoMSkgb3IgaXMgbm90ICgwKSBjYXBhYmxlIG9mIGRpc3BsYXlpbmcgVWx0cmEtSGlnaC1EZWZpbml0aW9uIHRlbGV2aXNpb24gY29udGVudA0KDQotIGB0dl9wcm92aWRlcmA6IHRoZSBjYWJsZSBvciBzYXRlbGxpdGUgVFYgcHJvdmlkZXINCg0KLSBgdG90YWxfdGltZV93YXRjaGVkYDogdGhlIHRvdGFsIGFtb3VudCBvZiBUViB3YXRjaGVkIChpbiBob3Vycykgb24gdGhlIGRheSBpbiB0aGUgYGRhdGVgIGNvbHVtbg0KDQotIGB3YXRjaGVkYDogd2hldGhlciB0aGUgdmlld2VyIHdhdGNoZWQgKDEpIOKAnFVTIFBvbGl0aWNzIFRoaXMgV2Vla+KAnSBvciBub3QgKDApDQoNCi0gYHRlc3RgOiB2aWV3ZXJzIGFyZSBzcGxpdCBpbnRvIHRlc3QgKDEpIGFuZCBjb250cm9sICgwKSBncm91cHM7IHRlc3Qgdmlld2VycyBzYXcgdGhlIG5ldyBjb21tZXJjaWFsIHdpdGggdGhlaXIgbG9jYWwgTWF5b3Igd2hpbGUgY29udHJvbCB2aWV3ZXJzIHNhdyB0aGUgb2xkIGNvbW1lcmNpYWwgd2l0aCB0aGUgTWF5b3Igb2YgTG9zIEFuZ2VsZXMNCg0KKkF0dHJpYnV0ZSBpbmZvcm1hdGlvbiBgdGVzdF9kYXRhYCoNCg0KLSBgdmlld2VyX2lkYDogdGhlIElEIG9mIHRoZSB2aWV3ZXIgKHNhbWUgSUQgYXMgaW4gdGhlIGB0ZXN0X2RhdGEuY3N2YCBmaWxlKQ0KDQotIGBnZW5kZXJgOiB0aGUgdmlld2Vy4oCZcyBnZW5kZXINCg0KLSBgYWdlYDogdGhlIHZpZXdlcuKAmXMgYWdlDQoNCi0gYGNpdHlgOiB0aGUgdmlld2Vy4oCZcyBjaXR5DQoNCkZpcnN0LCB3ZSB3aWxsIHBlcmZvcm0gYSBjbGVhbmluZyBwcm9jZXNzIGluIHRoZSBkYXRhYmFzZSwgaW4gb3JkZXIgdG8gY2Fycnkgb3V0IGFuIGV4cGxvcmF0aW9uIG9mIHRoZSBEYXRhLCB0byBiZXR0ZXIgdW5kZXJzdGFuZCB0aGUgdmFyaWFibGVzLg0KDQojIyBEYXRhIENsZWFuc2luZyAmIEpvaW4gdGhlIERhdGEgU2V0cw0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBGZWF0dXJlcyBTdW1tYXJ5ICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjDQpIbWlzYzo6ZGVzY3JpYmUodGVzdF9kYXRhKQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyANCiMgR3JhcGhpYyBmb3IgTWlzc2luZyBEYXRhICAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KbWlzc21hcCh0ZXN0X2RhdGEpDQoNCmBgYA0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBGZWF0dXJlcyBTdW1tYXJ5ICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjDQpIbWlzYzo6ZGVzY3JpYmUodmlld2VyX2RhdGEpDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIA0KIyBHcmFwaGljIGZvciBNaXNzaW5nIERhdGEgICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQptaXNzbWFwKHZpZXdlcl9kYXRhKQ0KDQpgYGANCg0KQXMgV2UgY2FuIHNlZSwgdGhlcmUgaXMgbm8gbWlzc2luZyBpbiBib3RoIGRhdGEgc2V0cy4gTGV0wrRzIG5vdyBtZXJnZSB0aGUgdHdvIGRhdGEgc2V0cy4gDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEpvaW4gdGhlIHR3byBEYXRhIFNldCBieSBWaWV3ZXIgSUQgIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmZ1bGxfZGF0YSA8LSBtZXJnZSh0ZXN0X2RhdGEsIHZpZXdlcl9kYXRhLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJ5ID0gInZpZXdlcl9pZCIpICU+JSANCiAgbmEub21pdCgpIA0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIFN0cnVjdHVyZSBvZiB0aGUgRGF0YSAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpoZWFkKGZ1bGxfZGF0YSkNCg0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgRmVhdHVyZXMgU3VtbWFyeSAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIw0KSG1pc2M6OmRlc2NyaWJlKGZ1bGxfZGF0YSAlPiUNCiAgICAgICAgICAgICAgICAgIHNlbGVjdCh0dl9tYWtlLCB0dl9wcm92aWRlciwgdG90YWxfdGltZV93YXRjaGVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgIGdlbmRlciwgYWdlLCBjaXR5KSkNCg0KYGBgDQoNCkxldMK0cyBjb252ZXJ0IHRoZSBgZGF0ZWAgZmVhdHVyZS4gQmVjYXVzZSB0aGlzIGZlYXR1cmVzIGlzIGEgYERhdGVgIGZlYXR1cmUsIGJ1dCBpbiB0aGUgb3JpZ2luYWwgZGF0YSBzZXQgaXQgaXMgbGlrZSBgRmFjdG9yYC4gDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIENvbnZlcnQgdGhlIERhdGUgIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmZ1bGxfZGF0YSRkYXRlIDwtIGFzLkRhdGUoZnVsbF9kYXRhJGRhdGUsICIlWS0lbS0lZCIpDQpzdHIoZnVsbF9kYXRhKQ0KDQpgYGANCg0KIyBEYXRhIFByZXByb2Nlc3NpbmcsIEV4cGxvcmF0aW9uICYgVmlzdWFsaXphdGlvbnMgey50YWJzZXR9DQoNCkxldMK0cyByZXByb2R1Y2UgdGhlIGZpcnN0IHJlc3VsdCBmb3VuZCBieSBDaGFubmVsIEtMTU7igJlzIGRhdGEgc2NpZW50aXN0LiBJIG1lYW4sIGxldMK0cyBmaWd1cmUgaXQgb3V0IGlmICp0aGVyZSBpcyBhIGxhcmdlIGZyYWN0aW9uIG9mIHBlb3BsZSB3aG8gc2F3IHRoZSBjb21tZXJjaWFsIGZvciBgVVMgUG9saXRpY3MgVGhpcyBXZWVrYCBhbmQgd2F0Y2hlZCB0aGUgc2hvdyBhcmUgZnJvbSBMb3MgQW5nZWxlcywgd2hpbGUgYSBtdWNoIGxvd2VyIGZyYWN0aW9uIG9mIHBlb3BsZSBmcm9tIG90aGVyIGNpdGllcyB3aG8gc2F3IHRoZSBjb21tZXJjaWFsIHdhdGNoZWQgdGhlIHNob3cqDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBHcm91cGluZyBieSBDaXR5ICAjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmZ1bGxfZGF0YV9mcmFjIDwtIGZ1bGxfZGF0YSAlPiUgDQogIHNlbGVjdCh3YXRjaGVkLCBjaXR5KSAlPiUgDQogIGdyb3VwX2J5KGNpdHkpICU+JSANCiAgc3VtbWFyaXNlKFRvdGFsID0gc3VtKHdhdGNoZWQpKSAlPiUgDQogIG11dGF0ZShQcm9wb3J0aW9uID0gVG90YWwvc3VtKFRvdGFsKSkgJT4lDQogIGFycmFuZ2UoLVRvdGFsKQ0KZnVsbF9kYXRhX2ZyYWMNCg0KIyMjIyMjIyMjIyMNCiMgR3JhcGhpYyAjDQojIyMjIyMjIyMjIw0KcGxvdF9mdWxsX2RhdGFfZnJhYyA8LSBmdWxsX2RhdGFfZnJhYyAlPiUgDQogICAgICAgICAgICAgICAgZ2dwbG90KGFlcyh4ID0gY2l0eSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBQcm9wb3J0aW9uLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgZmlsbCA9IGNpdHkpKSArDQogICAgICAgICAgICAgICBteV90aGVtZSgpICsNCiAgICAgICAgICAgICAgIGdlb21fY29sKHBvc2l0aW9uID0gImRvZGdlIikgKw0KICAgICAgICAgICAgICAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgMC4zKSkNCmdncGxvdGx5KHBsb3RfZnVsbF9kYXRhX2ZyYWMpDQoNCmBgYA0KDQpBcyBXZSBjYW4gc2VlLCB0aGUgZnJhY3Rpb24gb2YgcGVvcGxlIHdobyBzYXcgdGhlIGNvbW1lcmNpYWwgZm9yIGBVUyBQb2xpdGljcyBUaGlzIFdlZWtgIGFuZCB3YXRjaGVkIHRoZSBzaG93IGZyb20gTG9zIEFuZ2VsZXMgaXMgJDIzLDdcJSQgaXMgdGhlIGhpZ2hlc3QgZnJhY3Rpb24sIHRoZSBzZWNvbmQgY2l0eSBpcyBmcm9tIE5ldyBZb3JrIHdoaWNoIGZyYWN0aW9uIGlzICQxNSw4XCUkLiBJIG1lYW4sIHRoZSBmcmFjdGlvbiBmcm9tIG9mIHBlb3BsZSB3aG8gc2F3IHRoZSBjb21tZXJjaWFsIGZyb20gTG9zIEFuZ2VsZXMgaXMgJDUwXCUkIGhpZ2hlciB0aGFuIE5ldyBZb3JrIHZpZXdlcnMuDQoNCk5vdywgTGV0wrRzIFJlcHJvZHVjZSB0aGUgbmVnYXRpdmUgcmVzdWx0IGZvdW5kIGJ5IENoYW5uZWwgS0xNTuKAmXMgZGF0YSBzY2llbnRpc3QsIGkuZSwgbGV0wrRzIGludmVzdGlnYXRlIGlmIHRoZSBjb21tZXJjaWFscyB3aXRoIGxvY2FsIE1heW9ycyByZWFsbHkgZHJpdmluZyBhIGxvd2VyIGZyYWN0aW9uIG9mIHBlb3BsZSB0byB3YXRjaCB0aGUgc2hvdy4gDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEdyb3VwaW5nIGJ5IENpdHkgYW5kIFRlc3QgIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmZ1bGxfZGF0YV90ZXN0IDwtIGZ1bGxfZGF0YSAlPiUgDQogIHNlbGVjdCh3YXRjaGVkLCBjaXR5LCB0ZXN0KSAlPiUgDQogIGdyb3VwX2J5KGNpdHksIHRlc3QpICU+JSANCiAgc3VtbWFyaXNlKFRvdGFsX1dhdGNoZWQgPSBzdW0od2F0Y2hlZCkpICU+JSANCiAgbXV0YXRlKFByb3BvcnRpb25fV2F0Y2hlZCA9IFRvdGFsX1dhdGNoZWQvc3VtKFRvdGFsX1dhdGNoZWQpKSAlPiUNCiAgYXJyYW5nZSgtVG90YWxfV2F0Y2hlZCkNCmZ1bGxfZGF0YV90ZXN0DQoNCiMjIyMjIyMjIyMjDQojIEdyYXBoaWMgIw0KIyMjIyMjIyMjIyMNCnBsb3RfZnVsbF9kYXRhX3Rlc3QgPC0gZnVsbF9kYXRhX3Rlc3QgJT4lIA0KICAgICAgICAgICAgICAgIGdncGxvdChhZXMoeCA9IGNpdHksDQogICAgICAgICAgICAgICAgICAgICAgICAgICB5ID0gUHJvcG9ydGlvbl9XYXRjaGVkKSkgKw0KICAgICAgICAgICAgICAgbXlfdGhlbWUoKSArDQogICAgICAgICAgICAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3JzKSsNCiAgZmFjZXRfZ3JpZCh0ZXN0fi4pDQpnZ3Bsb3RseShwbG90X2Z1bGxfZGF0YV90ZXN0KQ0KDQpgYGANCg0KQXMgV2UgY2FuIHNlZSwgYWxtb3N0IGFsbCB0aGUgYGNpdGllc2AsIGhhcyBhbG1vc3QgdGhlIHNhbWUgcHJvcG9ydGlvbiBvZiB2aWV3ZXJzIHdobyBzYXcgdGhlIG5ldyBjb21tZXJjaWFsIHdpdGggdGhlaXIgbG9jYWwgTWF5b3IgYW5kIHRoZSB2aWV3ZXJzIHdobyBzYXcgdGhlIG9sZCBjb21tZXJjaWFsIHdpdGggdGhlIE1heW9yIG9mIExvcyBBbmdlbGVzLiBPbmx5IHR3byBgY2l0aWVzYCAoUGhpbGFkZWxwaGlhIGFuZCBTZWF0dGxlKSBoYXMgc3VjaCBkaWZmZXJlbmNlIGJldHdlZW4gdGhlc2UgdHdvIGdyb3Vwcy4gDQoNClNvLCB0aGlzIFBsb3RzIHNob3cgdXMgdGhhdCB0aGUgY29tbWVyY2lhbHMgd2l0aCBsb2NhbCBNYXlvcnMgYXJlIG5vdCBkcml2aW5nIGEgbG93ZXIgZnJhY3Rpb24gb2YgcGVvcGxlIHRvIHdhdGNoIHRoZSBzaG93IGluIGFsbW9zdCB0aGUgYGNpdGllc2AuIA0KDQpMZXTCtHMgZ3JvdXBpbmcgYnkgdHlwZSB0ZXN0ICgwIG9yIDEpIGluIG9yZGVyIHRvIHZlcmlmeSBpZiB0aGVyZSBpcyBhIHN0YXRpc3RpY2FsIHNpZ25pZmljYW5jZSBiZXR3ZWVuIHRoZXNlIHR3byBncm91cHMuIE91ciBIeXBvdGhlc2VzIHdpbGwgYmU6DQoNCiRIMDokIFRoZSBwcm9wb3J0aW9uIG9mIGdyb3VwIDAgaXMgZXF1YWwgJDUwLyUkDQoNCiRIYTokIFRoZSBwcm9wb3J0aW9uIG9mIGdyb3VwIDAgaXMgbGVzcyB0aGFuICQ1MC8lJA0KDQpJIG1lYW4sIGlmIHRoZSAkSGEkIGlzIHRydWUsIHRoaXMgbWVhbnMgdGhhdCB0aGUgZnJhY3Rpb24gb2Ygdmlld2VycyB3aG8gc2F3IHRoZSBvbGQgY29tbWVyY2lhbCB3aXRoIHRoZSBNYXlvciBvZiBMb3MgQW5nZWxlcyBpcyBsZXNzIHRoYW4gdGhlIGZyYWN0aW9uIG9mIHZpZXdlcnMgc2F3IHRoZSBuZXcgY29tbWVyY2lhbCB3aXRoIHRoZWlyIGxvY2FsIE1heW9yLiBGb3IgdGhpcyB0ZXN0IFdlIGhhdmUgdG8gZXhjbHVkZSB0aGUgTG9zIEFuZ2VsZXMgY2l0eSBmcm9tIHRoZSBEYXRhIFNldCwgYmVjYXVzZSB0aGUgcHJvcG9ydGlvbiBvZiB0aGlzIGdyb3VwIGlzLCBvYnZpb3VzIGVxdWFsIHRvIDEsIGJlY2F1c2UgdGhlIGV4cGVyaW1lbnQgaXMgYWJvdXQgdGhlIE1ham9yIGZyb20gTG9zIEFuZ2VsZXMuIA0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBHcm91cGluZyBieSBDaXR5ICBhbmQgVGVzdCB3aXRob3V0IExvcyBBbmdlbGVzICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpmdWxsX2RhdGFfaHlwIDwtIHN1YnNldChmdWxsX2RhdGEsIGNpdHkhPSJMb3MgQW5nZWxlcyIpICU+JSANCiAgc2VsZWN0KHdhdGNoZWQsIGNpdHksIHRlc3QpICU+JSANCiAgZ3JvdXBfYnkodGVzdCkgJT4lIA0KICBzdW1tYXJpc2UoVG90YWwgPSBzdW0od2F0Y2hlZCkpICU+JSANCiAgbXV0YXRlKFByb3BvcnRpb25fV2F0Y2hlZCA9IFRvdGFsL3N1bShUb3RhbCkpICU+JQ0KICBhcnJhbmdlKC1Ub3RhbCkNCmZ1bGxfZGF0YV9oeXANCg0KIyMjIyMjIyMjIyMNCiMgR3JhcGhpYyAjDQojIyMjIyMjIyMjIw0KcGxvdF9mdWxsX2RhdGFfaHlwIDwtIGZ1bGxfZGF0YV9oeXAgJT4lIA0KICAgICAgICAgICAgICAgIGdncGxvdChhZXMoeCA9IGFzLmZhY3Rvcih0ZXN0KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgIHkgPSBQcm9wb3J0aW9uX1dhdGNoZWQsDQogICAgICAgICAgICAgICAgICAgICAgICAgICBmaWxsID0gYXMuZmFjdG9yKHRlc3QpKSkgKw0KICAgICAgICAgICAgICAgbXlfdGhlbWUoKSArDQogICAgICAgICAgICAgICBnZW9tX2NvbChwb3NpdGlvbiA9ICJkb2RnZSIpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3JzKQ0KZ2dwbG90bHkocGxvdF9mdWxsX2RhdGFfaHlwKQ0KDQpgYGANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIyMjIyMjIw0KIyBUZXN0IFogIw0KIyMjIyMjIyMjIw0KcHJvcC50ZXN0KGZ1bGxfZGF0YV9oeXAkVG90YWxbMl0sIHN1bShmdWxsX2RhdGFfaHlwJFRvdGFsKSwgcCA9IDAuNSwgDQogICAgICAgICAgYWx0ZXJuYXRpdmUgPSAibGVzcyIsDQogICAgICAgICAgY29ycmVjdCA9IFRSVUUpDQoNCmBgYA0KDQpBcyBXZSBjYW4gc2VlLCB0aGUgZnJhY3Rpb24gb2Ygdmlld2VycyBzYXcgdGhlIG5ldyBjb21tZXJjaWFsIHdpdGggdGhlaXIgbG9jYWwgTWF5b3Igd2hpbGUgaXMgcmVhbGx5IGxlc3MgdGhhbiB0aGUgb2xkIGNvbW1lcmNpYWwgd2l0aCB0aGUgTWF5b3Igb2YgTG9zIEFuZ2VsZXMuIA0KDQojIEludmVzdGlnYXRpbmcgdGhlIERpZmZlcmVuY2V7LnRhYnNldH0NCg0KKkNpdHkgeCB0ZXN0KiANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZnVsbF9kYXRhJHRlc3QgPC0gYXMuZmFjdG9yKGZ1bGxfZGF0YSR0ZXN0KQ0KYTEgPC0gZnVsbF9kYXRhICU+JSBnZ3Bsb3QoYWVzKHg9Y2l0eSxmaWxsPXRlc3QpKSsNCiAgZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiLGNvbG9yPSJibGFjayIsYWxwaGE9MC44LHNob3cubGVnZW5kID0gVCkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teWNvbG9ycykrDQogIGNvb3JkX2ZsaXAoKSsNCiAgZ2d0aXRsZSgiY2l0eSIpDQphMQ0KDQpgYGANCg0KKldhdGNoZWQgeCB0ZXN0KiANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KYTIgPC0gZnVsbF9kYXRhICU+JSBnZ3Bsb3QoYWVzKHg9d2F0Y2hlZCxmaWxsPXRlc3QpKSsNCiAgZ2VvbV9iYXIocG9zaXRpb249ImZpbGwiLGNvbG9yPSJibGFjayIsYWxwaGE9MC44LHNob3cubGVnZW5kID0gVCkrDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1teWNvbG9ycykrDQogIGNvb3JkX2ZsaXAoKSsNCiAgZ2d0aXRsZSgid2F0Y2hlZCIpDQphMg0KDQpgYGANCg0KKnVoZF9jYXBhYmxlIHggdGVzdCogDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmEzIDwtIGZ1bGxfZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXVoZF9jYXBhYmxlLGZpbGw9dGVzdCkpKw0KICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsY29sb3I9ImJsYWNrIixhbHBoYT0wLjgsc2hvdy5sZWdlbmQgPSBUKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3JzKSsNCiAgY29vcmRfZmxpcCgpKw0KICBnZ3RpdGxlKCJ1aGRfY2FwYWJsZSIpDQphMw0KDQpgYGANCg0KKnR2X3Byb3ZpZGVyIHggdGVzdCogDQoNCmBgYHtyIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCmE0IDwtIGZ1bGxfZGF0YSAlPiUgZ2dwbG90KGFlcyh4PXR2X3Byb3ZpZGVyLGZpbGw9dGVzdCkpKw0KICBnZW9tX2Jhcihwb3NpdGlvbj0iZmlsbCIsY29sb3I9ImJsYWNrIixhbHBoYT0wLjgsc2hvdy5sZWdlbmQgPSBUKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3JzKSsNCiAgY29vcmRfZmxpcCgpKw0KICBnZ3RpdGxlKCJ0dl9wcm92aWRlciIpDQphNA0KDQpgYGANCg0KKkdlbmRlciB4IHRlc3QqIA0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQphNSA8LSBmdWxsX2RhdGEgJT4lIGdncGxvdChhZXMoeD1nZW5kZXIsZmlsbD10ZXN0KSkrDQogIGdlb21fYmFyKHBvc2l0aW9uPSJmaWxsIixjb2xvcj0iYmxhY2siLGFscGhhPTAuOCxzaG93LmxlZ2VuZCA9IFQpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvcnMpKw0KICBjb29yZF9mbGlwKCkrDQogIGdndGl0bGUoImdlbmRlciIpDQphNQ0KDQpgYGANCg0KKkdlbmRlciB4IGNpdHkqIA0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQphNiA8LSBmdWxsX2RhdGEgJT4lIGdncGxvdChhZXMoeD1nZW5kZXIsZmlsbD1jaXR5KSkrDQogIGdlb21fYmFyKHBvc2l0aW9uPSJmaWxsIixjb2xvcj0iYmxhY2siLGFscGhhPTAuOCxzaG93LmxlZ2VuZCA9IFQpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvcnMpKw0KICBjb29yZF9mbGlwKCkrDQogIGdndGl0bGUoImNpdHkiKQ0KYTYNCg0KYGBgDQoNCip0b3RhbF90aW1lX3dhdGNoZWQgeCB0ZXN0KiANCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZnVsbF9kYXRhICU+JSBnYXRoZXIodG90YWxfdGltZV93YXRjaGVkLA0KICAgICAgICAgICAgICBrZXk9IkZlYXR1cmVzIix2YWx1ZT0iVmFsdWUiKSAlPiUNCiAgZ2dwbG90KGFlcyh4PXRlc3QseT1WYWx1ZSxmaWxsPXRlc3QpKSsNCiAgZ2VvbV9ib3hwbG90KGFscGhhPTAuOCxjb2xvcj0iYmxhY2siKSsNCiAgY29vcmRfZmxpcCgpK2ZhY2V0X3dyYXAofkZlYXR1cmVzLG5jb2w9MSxzY2FsZXM9ImZyZWUiKSsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPW15Y29sb3JzKQ0KDQpgYGANCg0KKnRvdGFsX3RpbWVfd2F0Y2hlZCB4IGNpdHkqIA0KDQpgYGB7ciBlY2hvPVRSVUUsIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpmdWxsX2RhdGEgJT4lIGdhdGhlcih0b3RhbF90aW1lX3dhdGNoZWQsDQogICAgICAgICAgICAgIGtleT0iRmVhdHVyZXMiLHZhbHVlPSJWYWx1ZSIpICU+JQ0KICBnZ3Bsb3QoYWVzKHg9Y2l0eSx5PVZhbHVlLGZpbGw9Y2l0eSkpKw0KICBnZW9tX2JveHBsb3QoYWxwaGE9MC44LGNvbG9yPSJibGFjayIpKw0KICBjb29yZF9mbGlwKCkrZmFjZXRfd3JhcCh+RmVhdHVyZXMsbmNvbD0xLHNjYWxlcz0iZnJlZSIpKw0KICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9bXljb2xvcnMpDQoNCmBgYA0KDQojIEV4dHJhOiBQaXZvdCBUYWJsZSB7LnRhYnNldH0NCg0KV2l0aCB0aGlzIGBQaXZvdCBUYWJsZWAgV2UgY2FuIG1hbmlwdWxhdGUgYW5kIHNlZSB0aGUgZGlzdHJpYnV0aW9uIGZvciB3aGF0ZXZlciB2YXJpYWJsZXMgY29tYmluYXRpb24uIEl0wrRzIHRvdGFsbHkgaW50ZXJhY3RpdmUgUGl2b3QgVGFibGUuIFdlIGNhbiBkcmFnIHRoZSB2YXJpYWJsZXMgdG8gdGhlIGNvbHVtbnMgb3Igcm93cywgY2hvb3NlIHRoZSBjYXRlZ29yaWVzLCBtYWtlIGZpbHRlcnMsIGNob29zZSB3aGljaCBtZXRyaWNzIG9yIGdyYXBocy4NCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQojIEltcG9ydCB0aGUgTGlicmFyaWVzICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KbGlicmFyeShycGl2b3RUYWJsZSkNCmxpYnJhcnkoZGF0YS50YWJsZSkNCg0KcnBpdm90VGFibGUoc2V0RFQoZnVsbF9kYXRhKSwNCiAgICAgICAgICAgIGNvbHM9ICJhZ2UiLA0KICAgICAgICAgICAgcm93cyA9ICJjaXR5IiwNCiAgICAgICAgICAgIHJlbmRlcmVyTmFtZSA9ICJIZWF0bWFwIikNCmBgYA0KDQojIENvZGUgZm9yIHRoaXMgRXhwZXJpbWVudCB7LnRhYnNldH0NCg0KYGBge3IgZWNobz1UUlVFLCBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBDb2RlIGZvciB0aGUgZnVuY3Rpb24gIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBSZWFkIHRoZSBEYXRhIFNldHMgIw0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KdGVzdF9kYXRhIDwtIHJlYWQuY3N2KCJ0ZXN0X2RhdGEuY3N2IikNCnZpZXdlcl9kYXRhIDwtIHJlYWQuY3N2KCJ2aWV3ZXJfZGF0YS5jc3YiKQ0KDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyBKb2luIHRoZSB0d28gRGF0YSBTZXQgYnkgVmlld2VyIElEICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjDQpkYXRhIDwtIG1lcmdlKHRlc3RfZGF0YSwgdmlld2VyX2RhdGEsDQogICAgICAgICAgICAgIGJ5ID0gInZpZXdlcl9pZCIpICU+JSANCiAgbmEub21pdCgpDQoNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCiMgR3JvdXBpbmcgYnkgQ2l0eSBhbnMgVGVzdCB3aXRob3V0IExvcyBBbmdlbGVzICMNCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCmRhdGEgPC0gc3Vic2V0KGZ1bGxfZGF0YSwgY2l0eSE9IkxvcyBBbmdlbGVzIikgJT4lIA0KICBzZWxlY3Qod2F0Y2hlZCwgY2l0eSwgdGVzdCkgJT4lIA0KICBncm91cF9ieSh0ZXN0KSAlPiUgDQogIHN1bW1hcmlzZShUb3RhbCA9IHN1bSh3YXRjaGVkKSkgJT4lIA0KICBtdXRhdGUoUHJvcG9ydGlvbl9XYXRjaGVkID0gVG90YWwvc3VtKFRvdGFsKSkgJT4lDQogIGFycmFuZ2UoLVRvdGFsKQ0KDQp0ZXN0X3Byb3AgPC0gZnVuY3Rpb24oZGF0YSl7ICANCiAgDQogICMjIyMjIyMjIyMNCiAgIyBUZXN0IFogIw0KICAjIyMjIyMjIyMjDQogIHJlc3VsdCA8LSBwcm9wLnRlc3QoZGF0YSRUb3RhbFsyXSwgc3VtKGRhdGEkVG90YWwpLCBwID0gMC41LCANCiAgICAgICAgICAgICAgICAgICAgICBhbHRlcm5hdGl2ZSA9ICJsZXNzIiwNCiAgICAgICAgICAgICAgICAgICAgICBjb3JyZWN0ID0gVFJVRSkNCiAgDQogICMjIyMjIyMjIyMjIyMjIyMjIw0KICAjIE91dHB1dCBvZiBUZXN0ICMNCiAgIyMjIyMjIyMjIyMjIyMjIyMjDQogIG91dHB1dCA8LSBpZmVsc2UocmVzdWx0JHAudmFsdWUgPiAwLjA1LCANCiAgICAgICAgICAgICAgICAgICAiVGhlIGNvbW1lcmNpYWxzIHdpdGggbG9jYWwgTWF5b3JzIGlzIGRyaXZpbmcgYSBoaWdoZXIgZnJhY3Rpb24gb2YgcGVvcGxlIHRvIHdhdGNoIHRoZSBzaG93IiwgDQogICAgICAgICAgICAgICAgICAgIlRoZSBjb21tZXJjaWFscyB3aXRoIGxvY2FsIE1heW9ycyBpcyBkcml2aW5nIGEgbG93ZXIgZnJhY3Rpb24gb2YgcGVvcGxlIHRvIHdhdGNoIHRoZSBzaG93IikNCiAgcHJpbnQob3V0cHV0KQ0KfQ0KYGBgDQo=