Bellabeatis a successful small business who specializes in health-focused smart devices for women. With their innovative smart device design, they have potential to become a larger player in the global market.

1 Ask

1.1 Business task

The stakeholders at Bellabeat would like to identify new opportunities for growth. Given this task, I will be conducting an analysis based on the information of its users and how they are utilizing the products offered by the company. I then will make useful recommendations to unlock new marketing strategies based on trends within the data.

1.2 Key stakeholders

  • Urška Sršen: Bellabeat’s co-founder and Chief Creative Officer.
  • Sando Mur: Mathematician and Bellabeat’s co-founder.
  • Bellabeat marketing analytics team.

2 Prepare

##About the data:

The data used for this analysis is public and accessible by everyone. The source of the data is reliable as it was provided by FitBit Fitness Tracker, which is a well known entity.

To download the data, click here.

Limitations: + The data is from 2016. FitBit devices have likely changed over time to deliver more accurate results. + This data was collected through survey which may indicate that the results are not accurate, as surveys rely on participants providing honest answers. + The sample size is relatively small, which can lead to potential sampling bias. Furthermore, since the target audience is specifically women, we should factor this unreliability into any business decision as a result from this analysis.

2.1 Installing packages

To begin, start by installing the necessary packages and loading them into the library for easy analysis.

# installing packages.

install.packages("tidyverse")
install.packages("kableExtra")
install.packages("highcharter")
install.packages("RColorBrewer")
install.packages("scales")
install.packages("cowplot")
install.packages("plotly")
install.packages("dplyr")

2.2 Loading libraries

# Importing libraries.

library(tidyverse)
library(kableExtra)   # For viewing tables in cool ways
library(scales)   # For transforming numbers to percentages
library(highcharter) # For Interactive graphs
library(RColorBrewer)  # For different Pallet colors
library(cowplot) # For plotting visuals for comparison
library(plotly) # For plotting interactive pie charts for comparison
library(dplyr)

##Importing Data:

The data provided by FitBit Fitness Tracker has 18 CSV files. Each of these datasets have information related to smart-device usage. Such as, Activity level, Step counting, and workout Intensities, etc.

After previewing the data, I decided to use the following files as they contain all the necessary information I need to carry out my analysis.

below I will import the datasets I’ll be using.

2.3 Daily activity

daily_activity <- read.csv("C:/Users/Keeley/Desktop/Fitabase Data 4.12.16-5.12.16/dailyActivity_merged.csv")

2.4 Daily sleep

daily_sleep <- read.csv("C:/Users/Keeley/Desktop/Fitabase Data 4.12.16-5.12.16/sleepDay_merged.csv")

2.5 Hourly steps

hourly_steps <- read.csv("C:/Users/Keeley/Desktop/Fitabase Data 4.12.16-5.12.16/hourlySteps_merged.csv")

2.6 Weight

weight <- read.csv("C:/Users/Keeley/Desktop/Fitabase Data 4.12.16-5.12.16/weightLogInfo_merged.csv")

3 Process

3.1 Viewing the datasets

3.1.1 Daily activity

# Viewing the datasets using the "kableExtra" package

kbl(daily_activity[1:5, ], 'html', caption = 'Table 1: Daily activity') %>% 
  kable_styling(bootstrap_options = c("striped", "hover", "condensed")) %>% 
  scroll_box(width = "100%")  # Adding a scroll bar
Table 1: Daily activity
Id ActivityDate TotalSteps TotalDistance TrackerDistance LoggedActivitiesDistance VeryActiveDistance ModeratelyActiveDistance LightActiveDistance SedentaryActiveDistance VeryActiveMinutes FairlyActiveMinutes LightlyActiveMinutes SedentaryMinutes Calories
1503960366 4/12/2016 13162 8.50 8.50 0 1.88 0.55 6.06 0 25 13 328 728 1985
1503960366 4/13/2016 10735 6.97 6.97 0 1.57 0.69 4.71 0 21 19 217 776 1797
1503960366 4/14/2016 10460 6.74 6.74 0 2.44 0.40 3.91 0 30 11 181 1218 1776
1503960366 4/15/2016 9762 6.28 6.28 0 2.14 1.26 2.83 0 29 34 209 726 1745
1503960366 4/16/2016 12669 8.16 8.16 0 2.71 0.41 5.04 0 36 10 221 773 1863

3.1.2 Daily sleep

kbl(daily_sleep[1:5, ], 'html', caption = 'Table 2: Daily sleep') %>% 
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F)
Table 2: Daily sleep
Id SleepDay TotalSleepRecords TotalMinutesAsleep TotalTimeInBed
1503960366 4/12/2016 12:00:00 AM 1 327 346
1503960366 4/13/2016 12:00:00 AM 2 384 407
1503960366 4/15/2016 12:00:00 AM 1 412 442
1503960366 4/16/2016 12:00:00 AM 2 340 367
1503960366 4/17/2016 12:00:00 AM 1 700 712

3.1.3 Hourly steps

kbl(hourly_steps[1:5, ], 'html', caption = 'Table 3: Hourly steps') %>% 
kable_styling(bootstrap_options = c("striped", "hover"), full_width = F)
Table 3: Hourly steps
Id ActivityHour StepTotal
1503960366 4/12/2016 12:00:00 AM 373
1503960366 4/12/2016 1:00:00 AM 160
1503960366 4/12/2016 2:00:00 AM 151
1503960366 4/12/2016 3:00:00 AM 0
1503960366 4/12/2016 4:00:00 AM 0

3.1.4 Weight

kbl(weight[1:5, ], 'html', caption = 'Table 4: Weight') %>% 
  kable_styling(bootstrap_options = c("striped", "hover"), full_width = F)
Table 4: Weight
Id Date WeightKg WeightPounds Fat BMI IsManualReport LogId
1503960366 5/2/2016 11:59:59 PM 52.6 115.9631 22 22.65 True 1.462234e+12
1503960366 5/3/2016 11:59:59 PM 52.6 115.9631 NA 22.65 True 1.462320e+12
1927972279 4/13/2016 1:08:52 AM 133.5 294.3171 NA 47.54 False 1.460510e+12
2873212765 4/21/2016 11:59:59 PM 56.7 125.0021 NA 21.45 True 1.461283e+12
2873212765 5/12/2016 11:59:59 PM 57.3 126.3249 NA 21.69 True 1.463098e+12

3.2 Cleaning data

While further reviewing the datasets, we can observe that some columns won’t be needed. We will remove these variables that aren’t useful for our analysis.

# removing unnecessary columns

daily_activity <- daily_activity %>% select(-c(TrackerDistance))

weight <- weight %>% select(-c(WeightKg, Fat, IsManualReport, LogId))

I will start by checking unique Id’s, removing duplicates, renaming columns for consistency, and splitting the datetime into two columns. I’ll also clean up the date columns to make them all match the correct format.

# Checking the number of unique Id's

n_distinct(daily_activity$Id)
[1] 33
n_distinct(daily_sleep$Id)
[1] 24
n_distinct(weight$Id)
[1] 8
n_distinct(hourly_steps$Id)
[1] 33
# Checking for duplicates

sum(duplicated(daily_activity))
[1] 0
sum(duplicated(daily_sleep))
[1] 3
sum(duplicated(weight))
[1] 0
sum(duplicated(hourly_steps))
[1] 0
# Remove duplicates and drop null values

daily_sleep <- daily_sleep[!duplicated(daily_sleep),]
# Confirming duplicates in sleep table were removed

sum(duplicated(daily_sleep))
[1] 0

Here, I’ll focus on formatting, consistent column names, and splitting “Date” and “Time” into their own separate columns.

# split datetime into its own columns "Date", "Time" and rename columns for consistency.

weight <- separate(data = weight, col = Date, into  = c('Date', 'Time'), sep = ' ')

daily_sleep <- separate(data = daily_sleep, col = SleepDay, into  = c('Date', 'Time'), sep = ' ')

hourly_steps <- separate(data = hourly_steps, col = ActivityHour, into  = c('Date', 'Time'), sep = ' ')

colnames(daily_activity)[2]="Date" 


# formatting variables.

daily_activity <- daily_activity %>%
  mutate(Date = as_datetime(Date, format = "%m/%d/%Y"))

daily_sleep <- daily_sleep %>%
  mutate(Date = as_datetime(Date,format ="%m/%d/%Y"))

hourly_steps<- hourly_steps %>% 
  mutate(Date = as_datetime(Date,format ="%m/%d/%Y"))

weight <- weight %>%
  mutate(Date = as_datetime(Date,format ="%m/%d/%Y"))

3.3 Checking variables

# Checking variables
str(daily_activity)
'data.frame':   940 obs. of  14 variables:
 $ Id                      : num  1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ Date                    : POSIXct, format: "2016-04-12" "2016-04-13" "2016-04-14" "2016-04-15" ...
 $ TotalSteps              : int  13162 10735 10460 9762 12669 9705 13019 15506 10544 9819 ...
 $ TotalDistance           : num  8.5 6.97 6.74 6.28 8.16 ...
 $ LoggedActivitiesDistance: num  0 0 0 0 0 0 0 0 0 0 ...
 $ VeryActiveDistance      : num  1.88 1.57 2.44 2.14 2.71 ...
 $ ModeratelyActiveDistance: num  0.55 0.69 0.4 1.26 0.41 ...
 $ LightActiveDistance     : num  6.06 4.71 3.91 2.83 5.04 ...
 $ SedentaryActiveDistance : num  0 0 0 0 0 0 0 0 0 0 ...
 $ VeryActiveMinutes       : int  25 21 30 29 36 38 42 50 28 19 ...
 $ FairlyActiveMinutes     : int  13 19 11 34 10 20 16 31 12 8 ...
 $ LightlyActiveMinutes    : int  328 217 181 209 221 164 233 264 205 211 ...
 $ SedentaryMinutes        : int  728 776 1218 726 773 539 1149 775 818 838 ...
 $ Calories                : int  1985 1797 1776 1745 1863 1728 1921 2035 1786 1775 ...

4 Analyze

I’ll set up a common theme for plots in the analysis here before I start. This makes our visuals more streamline and consistent.

# Creating common theme for plots

custom_theme <- function() {
  theme(
    panel.border = element_rect(colour = "black", 
                                fill = NA, 
                                linetype = 1),
    panel.background = element_rect(fill = "white", 
                                    color = 'grey50'),
    panel.grid.minor.y = element_blank(),
    axis.text = element_text(colour = "black", 
                             face = "italic", 
                             family = "Helvetica"),
    axis.title = element_text(colour = "black", 
                              family = "Helvetica"),
    axis.ticks = element_line(colour = "black"),
    plot.title = element_text(size=23, 
                              hjust = 0.5, 
                              family = "Helvetica"),
    plot.subtitle=element_text(size=16, 
                               hjust = 0.5),
    plot.caption = element_text(colour = "black", 
                             face = "italic", 
                             family = "Helvetica")
  )
}

4.1 Smart Device Usage

To really dig into consumer behaviors, we’ll need to understand how the devices are being used and for what purposes. We will do this by grouping the datasets daily_activity, daily_sleep, and weight. This will allow us to find the distinct number of participants, the frequencies they’re using smart devices, as well as the distribution.

4.1.1 Grouping users

# Grouping daily activity for daily averages of users

daily_activity_user_summary <- daily_activity %>%
 group_by(Id) %>%
 summarise(n = n(), steps = round(mean(TotalSteps)), calories = round(mean(Calories)),
           VeryActiveMinutes = round(mean(VeryActiveMinutes)), 
           FairlyActiveMinutes = round(mean(FairlyActiveMinutes)), 
           LightlyActiveMinutes = round(mean(LightlyActiveMinutes)), 
           SedentaryMinutes = round(mean(SedentaryMinutes)))



# Grouping sleep log by user
daily_sleep_user_summary <- daily_sleep %>%
  group_by(Id) %>%
  summarise(n=n(), averageTimeAsleep = mean(TotalMinutesAsleep), averageTimeInBed = mean(TotalTimeInBed))

# Grouping weight log by user
weight_user_summary <- weight %>%
  group_by(Id) %>%
  summarise(n=n(), weight_lbs = mean(WeightPounds), max_weight = max(WeightPounds), min_weight = min(WeightPounds), averageBMI = mean(BMI))

n_distinct(daily_activity_user_summary$Id)
[1] 33
n_distinct(daily_sleep_user_summary$Id)
[1] 24
n_distinct(weight_user_summary$Id)
[1] 8

Participant numbers in this activity are 33 for activity, 24 for sleep, and 8 for weight datasets.

If we categorize the users based on how often they log per day, then group the data based on the categories, we will be able see the total number, and percentage of users in each category. Which will give us a better picture of how the devices are being used.

To do this, I’ll start by categorize them into “low”, “medium” and “regular” for usage of 0-10, 11-20, and 21-31 days.

After grouping the data, we’ll pull the total number and percentage so we can create a visualization of the distribution by usage for all three types of health data.

4.1.2 Assigning categories, grouping, sorting and viewing the usage distribution

# Assigning categories to users based on number of days logged

daily_activity_user_summary$Usage <- ifelse(daily_activity_user_summary$n<=10,"Low", 
                ifelse(daily_activity_user_summary$n>10 & daily_activity_user_summary$n<21, "Medium",
                       "Regular"))

daily_sleep_user_summary$Usage <- ifelse(daily_sleep_user_summary$n<=10,"Low", 
                ifelse(daily_sleep_user_summary$n>10 & daily_sleep_user_summary$n<21, "Medium", "Regular"))

weight_user_summary$Usage <- ifelse(weight_user_summary$n<=10,"Low", 
                ifelse(weight_user_summary$n>10 & weight_user_summary$n<21, "Medium", "Regular"))



# Grouping users based on number of days logged

daily_activity_usage <- daily_activity_user_summary %>%
  group_by(Usage) %>%
  summarise(userCount=n(), average_usage= round(mean(n),0))

daily_sleep_usage <- daily_sleep_user_summary %>%
  group_by(Usage) %>%
  summarise(userCount=n(), average_usage= round(mean(n),0))

weight_usage <- weight_user_summary %>%
  group_by(Usage) %>%
  summarise(userCount=n(), average_usage= round(mean(n),0))



# Sorting categories based on the number of users in each category

daily_activity_usage <- daily_activity_usage[order(-daily_activity_usage$average_usage),]
daily_sleep_usage <- daily_sleep_usage[order(-daily_sleep_usage$average_usage),]
weight_usage <- weight_usage[order(-weight_usage$average_usage),]



# Adding percentage column for the number of users in each category

daily_activity_usage$perc <-scales::percent(
  daily_activity_usage$userCount/sum(daily_activity_usage$userCount))
daily_sleep_usage$perc <- scales::percent(
  daily_sleep_usage$userCount/sum(daily_sleep_usage$userCount))
weight_usage$perc <- scales::percent(
  weight_usage$userCount/sum(weight_usage$userCount))



# Visualizing the representation of usage

colors <- c("mediumblue" , "mediumvioletred","darkorange" )
colors1 <- c("mediumblue", "mediumvioletred","darkorange" )
fig_usage <- plot_ly()
fig_usage <- fig_usage %>% add_pie(data = daily_activity_usage, labels = ~Usage, values = ~userCount,
                       name = "Activity", domain = list(x = c(0, 0.4), y = c(0.4, 1)),
                        marker = list(colors = colors, line = list(color = '#FFFFFF', width = 1)))

fig_usage <- fig_usage %>% add_pie(data = daily_sleep_usage, labels = ~Usage, values = ~userCount,
                       name = "Sleep", domain = list(x = c(0.6, 1), y = c(0.4, 1)),
                       marker = list(colors = colors, line = list(color = '#FFFFFF', width = 1)),
                       showlegend = TRUE)
                       
fig_usage <- fig_usage %>% add_pie(data = weight_usage, labels = ~Usage, values = ~userCount,
                       name = "Weight", domain = list(x = c(0.25, 0.75), y = c(0, 0.6)),
                      marker = list(colors = colors1, line = list(color = '#FFFFFF', width = 1)))

fig_usage <- fig_usage %>% layout(title = "Usage distribution in activity, sleep and weight data sets", showlegend = F,
         xaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE),
         yaxis = list(showgrid = FALSE, zeroline = FALSE, showticklabels = FALSE))

fig_usage
NA
NA
NA

Here we can see that 87.9% of users are regularly logging when it comes to daily activity. We have 9.09% medium, and 3.03% low frequency users.

However, if we look at the charts for sleep and weight we can see that they’re both being underutilized. Sleep logs show only 50% of users are logging regularly and weight has a mere 25%.

Key takeaway: Users are not logging their sleep and weight as often. Providing the devices with a feature to remind users to log activities would be helpful.

4.1.3 Monitoring Inactivity

# Creating a Weekday category for further evaluation.

Date <- daily_activity %>% distinct(Date)
Date$WeekDay <- weekdays(Date$Date)                             
daily_activity <- merge(daily_activity, Date, by= "Date")

# mapping our Weekday category with activity logs to see any trends of inactive logging.

ggplot(data=daily_activity)+
  geom_col(mapping=aes(x=SedentaryMinutes,y=factor(WeekDay,levels=c("Sunday","Monday","Tuesday", "Wednesday", "Thursday","Friday","Saturday"))),fill='steelblue3')+labs(title="Weekly Inactive Minutes",y= " ",
        caption = 'Data Source: FitBit Fitness Tracker Data')

Users are most inactive Tuesday to Thursday. These days could be spiked up higher due to a normal work week.

Key takeaway: Providing reminders or notifications for inactivity can also help encourage movement when users have been inactive for too long.

4.2 Steps

4.2.1 Analyse Hourly Steps

Now, let’s look at is when users are most active.

# Checking Total Steps vs. Hours of the day.

hourly_steps %>%
  group_by(Time) %>%
  summarize(average_steps = mean(StepTotal)) %>%
  ggplot() +
  geom_col(mapping = aes(x=Time, y = average_steps, fill = average_steps)) + 
  labs(title = "Steps taken each hour of the day", 
       x= " ",
       y = "Hourly Steps",
       caption = 'Data Source: FitBit Fitness Tracker Data') + 
  scale_fill_gradient(low = "mediumblue", high = "darkorange")+
  theme(axis.text.x = element_text(angle = 90))

Users are most active between the hours of 5:30p.m.-9:30p.m., and again around 10:00a.m., which is consistent with a modern work schedule.

Key takeaway: It appears that our audience are mostly corporate workers who utilize their lunch and after work hours for movement. Possible recommendation would be articles on how to achieve movement while at work.

4.2.2 Daily step activity

# Checking users' daily activity throughout the week.
weekday_steps <- daily_activity %>%
  mutate(weekday = weekdays(Date))
weekday_steps$weekday <-ordered(weekday_steps$weekday, levels=c("Sunday","Monday", "Tuesday", "Wednesday", "Thursday","Friday", "Saturday"))

# Grouping by weekday
weekday_steps <-weekday_steps%>%
  group_by(weekday) %>%
  summarize (daily_steps = mean(TotalSteps))

# Visualizing users activity by day
ggplot(weekday_steps,aes(x=weekday, y=daily_steps)) +
  geom_col(fill = "steelblue3") +
  geom_hline(yintercept = 10000) +
  geom_text(data=weekday_steps,aes(label = round(daily_steps), vjust = 1.5))+
labs(title="Users activity by day",
    x= " ",
    y = "Daily Steps",
    caption = 'Data Source: FitBit Fitness Tracker Data')+
  theme(axis.text.x = element_text(angle = 45,vjust = 0.5, hjust = 1))

In this bar chart, we can see the difference between days of the week. Noting that Tuesdays and Saturdays are the best day for users’.

# calculating average steps less than 10,000 daily

round(nrow(daily_activity_user_summary[daily_activity_user_summary$steps < 10000,]) / nrow(daily_activity_user_summary) * 100)
[1] 79

79% of our users average below the recommended 10,000 steps a day (per CDC guidelines). Users get the highest amount of steps on Saturdays, and the least on Sundays. During the week their step count is generally lower.

Key takeaway: To help users meet their target step count, Bellabeat can provide notifications and reminders to walk more when inactive and at specific times in the day.

4.3 Calories burned

4.3.1 Calories burned by steps

# Checking Calories burned by Total Steps

daily_activity %>% 
  group_by(TotalSteps, Calories) %>% 
  ggplot(aes(x = TotalSteps , y = Calories, color = Calories)) +
  geom_point() +
  geom_smooth() + 
  custom_theme() +
  theme(legend.position = c(.8, .3),
        legend.spacing.y = unit(1, "mm"), 
        panel.border = element_rect(colour = "black", fill=NA),
        legend.background = element_blank(),
        legend.box.background = element_rect(colour = "black")) +
  labs(title = 'Calories burned by total steps taken',
       y = 'Calories',
       x = 'Total Steps',
       caption = 'Data Source: FitBit Fitness Tracker Data')

In the above graph, we can clearly see a positive correlation between Steps and Calories burned. To check the correlation, we will use the Pearson Correlation Coefficient which will measure the linear correlation between the two variables. To read more, click here.

# Running the Pearsons Correlation Coefficient.

cor.test(daily_activity$TotalSteps, daily_activity$Calories, method = 'pearson', conf.level = 0.95)

    Pearson's product-moment correlation

data:  daily_activity$TotalSteps and daily_activity$Calories
t = 22.472, df = 938, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5483688 0.6316184
sample estimates:
      cor 
0.5915681 

We can see that the correlation is almost 60%. This means that there is a strong relationship between steps and calories burned.

Key takeaway: There is a strong relationship between total steps taken and calories burned. Simply put, the more steps you take the more calories you burn.

4.3.2 Calories Burned by activity level

# Calories Burned vs. Daily activity level

par(mfrow = c(2, 2))

p1 <- ggplot(daily_activity, aes(VeryActiveMinutes, Calories)) + geom_point() + labs(x = "Heavily Active", y = "Calories", title = "Heavy, Moderate, Light and Sedentary Activity vs Calories", ) + theme_minimal() + geom_smooth(method = "lm")


p2 <- ggplot(daily_activity, aes(FairlyActiveMinutes, Calories)) + geom_point() + labs(x = "Moderately Active", y = "", title = " ") + theme_minimal() + theme(axis.text.y = element_blank()) + geom_smooth(method = "lm")

p3 <- ggplot(daily_activity, aes(LightlyActiveMinutes, Calories)) + geom_point() + labs(x = "Lightly Active", y = "", title = " ") + theme_minimal() + theme(axis.text.y = element_blank()) + geom_smooth(method = "lm")

p4 <- ggplot(daily_activity, aes(SedentaryMinutes, Calories)) + geom_point() + labs(x = "Sedentary", y = "", title = " ") + theme_minimal() + theme(axis.text.y = element_blank()) + geom_smooth(method = "lm")

plot_grid(p1, p2, p3, p4, ncol = 4)  

There is a positive correlation between the type of activity and the calories burned. Meaning the more active you are, the more calories you burn, and vice verse.

Key takeaway: Staying active will make a huge difference in the amount of calories you burn on a daily basis.

4.4 Exercise

4.4.1 Intensity of exercise activity

# Checking users' activity levels.

daily_activity %>% 
  select(VeryActiveDistance, 
         ModeratelyActiveDistance, 
         LightActiveDistance, SedentaryActiveDistance) %>% 
  summarise(across(everything(), list(sum))) %>% 
  gather(activities, value) %>% 
  mutate(ratio = value / sum(value),
         label = percent(ratio %>% round(4))) %>% 
mutate(activities = factor(activities, 
                          labels = c('Light Activity','Moderate Activity', 'Sedentary','Heavy Activity'))) %>% 
  ggplot(aes(x = (activities), 
             y = value, 
             label = label, 
             fill = activities)) +
  geom_bar(stat='identity') +
  geom_label(aes(label = label), 
             fill = "beige", 
             colour = "black",
             vjust = 0.5) +
  custom_theme() +
  scale_fill_brewer(palette="Accent") +
  theme(legend.position="none") +
  labs(
    title = "Intensity of exercise activity",
    x = "Activity level",
    y = "Distance",
    caption = 'Data Source: FitBit Fitness Tracker Data'
  )

The most common exercise activity level is light.

Key takeaway: Users seem to prefer lighter activities. Adding in-app articles or even a guided exercise program could bring more engagement.

4.4.2 Distribution of daily activity level

# Checking the Distribution % of daily activity levels.

daily_activity %>% 
  select(VeryActiveMinutes, 
         FairlyActiveMinutes, 
         LightlyActiveMinutes, 
         SedentaryMinutes) %>% 
  summarise(across(everything(), list(sum))) %>% 
  gather(active_level, minutes) %>% 
  mutate(active_level = factor(active_level, 
                          labels = c('Moderate Activity','Light Activity',
                                     'Sedentary','Heavy Activity'))) %>% 
  hchart('pie', hcaes(x = active_level, y = minutes)) %>% 
  hc_title(text = "Distribution of daily activity level in minutes",
           style = list(fontFamily = "Helvetica", fontSize = "30px"),
           align = "center") %>% 
  hc_tooltip(pointFormat = "<b>Value:</b> {point.y} <br>
                 <b>Percentage</b> {point.percentage:,.2f}%") %>% 
  hc_credits(
    enabled = TRUE, 
    text = "<em>Data Source: FitBit Fitness Tracker Data</em></span>",
    style = list(fontSize = "12px", color = 'black',type = "spline")
    ) 

Above we can see that the majority of people are sedentary during the day.

Key takeaway: Movement reminders would help reduce this number.

4.5 Sleep

4.5.1 Sleep distribution

# Distribution sleep time.

daily_sleep %>% 
  select(TotalMinutesAsleep) %>% 
  drop_na() %>% 
  mutate(sleep_quality = ifelse(TotalMinutesAsleep <= 420, 'Less than 7h',
                         ifelse(TotalMinutesAsleep <= 540, '7h to 9h', 
                         'More than 9h'))) %>%
  mutate(sleep_quality = factor(sleep_quality, 
                          levels = c('Less than 7h','7h to 9h',
                                     'More than 9h'))) %>% 
  ggplot(aes(x = TotalMinutesAsleep, fill = sleep_quality)) +
  geom_histogram(position = 'dodge', bins = 30) +
  custom_theme() +
  scale_fill_manual(values=c("coral1", "turquoise4", "deeppink4")) +
  theme(legend.position = c(.80, .80),
        legend.title = element_blank(),
        legend.spacing.y = unit(0, "mm"), 
        panel.border = element_rect(colour = "black", fill=NA),
        legend.background = element_blank(),
        legend.box.background = element_rect(colour = "black")) +
  labs(
    title = "Sleep distribution",
    x = "Time slept (minutes)",
    y = "Count",
    caption = 'Data Source: FitBit Fitness Tracker Data'
  )

We can see by this graph that users sleep averages to about 300 to 520 minutes (or 5 to 8 hours) of sleep each day.

Key takeaway: according to the CDC guidelines, users should be getting approximately 7-9 hours of sleep each night. Users are not quite meeting this recommended goal. Developing an in-app sleep routine or schedule would help users meet their sleep goals. Articles about sleep routines would be helpful as well.

4.5.2 Sleep vs distance covered

# Merging daily activity and daily sleep for sleep analysis

daily_sleep_activity <- merge(daily_activity, daily_sleep, by = "Id")
# Checking Sleep vs. Distance Covered.

daily_sleep_activity %>% 
  select(Id, TotalDistance, TotalMinutesAsleep) %>% 
  group_by(Id) %>% 
  summarise_all(list(~mean(., na.rm=TRUE))) %>% 
  drop_na() %>% 
  mutate(Id = factor(Id)) %>% 
  ggplot() +
  geom_bar(aes(x = Id, y = TotalDistance), stat = "identity", fill = 'steelblue3', alpha = 0.7) +
  geom_point(aes(x = Id, y = TotalMinutesAsleep/60), color = 'gray20') +
  geom_segment(aes(x = Id, xend = Id, y = 0, yend = TotalMinutesAsleep/60), color = 'gray20' ,group = 1) +
  custom_theme() +
  scale_y_continuous(limits=c(0, 12), name = "Total Distance", 
    sec.axis = sec_axis(~.*60, name = "Sleep in minutes")) +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  theme(axis.title.y.right = element_text(color = "gray20"), 
        axis.ticks.y.right = element_line(color = "gray20"),
        axis.text.y.right = element_text(color = "gray20")) +
  labs(
    title = "Average distance vs average sleep by user",
    x = "Users",
    caption = 'Data Source: FitBit Fitness Tracker Data'
  )

We can see that moving a greater distance doesn’t equate to a better nights sleep (on average).

Let’s look into this further by checking the sleep quality vs step totals.

4.5.3 Sleep quality vs Step totals

# Checking Sleep Quality vs. Step Totals.

daily_sleep_activity %>% 
  select(TotalMinutesAsleep, TotalSteps) %>% 
  mutate(sleep_quality = ifelse(TotalMinutesAsleep <= 420, 'Less than 7h',
                         ifelse(TotalMinutesAsleep <= 540, '7h to 9h', 
                         'More than 9h'))) %>% 
  mutate(active_level = ifelse(TotalSteps >= 15000,'More than 15,000 steps',
                        ifelse(TotalSteps >= 10000,'10,000 to 14,999 steps',
                        ifelse(TotalSteps >= 5000, '5,000 to 9,999 steps',
                        'Less than 4,999 steps')))) %>% 
  select(-c(TotalMinutesAsleep, TotalSteps)) %>% 
  drop_na() %>% 
  group_by(sleep_quality, active_level) %>% 
  summarise(counts = n()) %>% 
  mutate(active_level = factor(active_level, 
                                levels = c('Less than 4,999 steps',
                                           '5,000 to 9,999 steps',
                                           '10,000 to 14,999 steps',
                                           'More than 15,000 steps'))) %>% 
  mutate(sleep_quality = factor(sleep_quality, 
                          levels = c('Less than 7h','7h to 9h',
                                     'More than 9h'))) %>% 
  ggplot(aes(x = sleep_quality, 
             y = counts, 
             fill = sleep_quality)) +
  geom_bar(stat = "identity") +
  custom_theme() +
  scale_fill_manual(values=c("coral", "turquoise3", "deeppink3")) +
  facet_wrap(~active_level, nrow = 1) +
  theme(legend.position = "none") +
  theme(axis.text.x = element_text(angle = 45, hjust = 1)) +
  theme(strip.text = element_text(colour = 'black', size = 8)) +
  theme(strip.background = element_rect(fill = "beige", color = 'black'))+
  labs(
    title = "Sleep quality by steps",
    x = "Sleep quality",
    y = "Count",
    caption = 'Data Source: FitBit Fitness Tracker Data'
  )

Here we can see that the best sleep on average is achieved when the users total sleep does not exceed 9,999 steps a day.

Key takeaway: Users sleep quality is the best when they stay within their step goals.


5 Share

5.1 Key takeaways

logging:

  1. Users are not logging their sleep and weight as often. Providing the devices with a feature to remind users to log activities would be helpful.

Movement:

  1. Providing reminders or notifications for inactivity can help encourage movement when users have been inactive for too long.
  2. To help users meet their target step count, Bellabeat can provide notifications and reminders to walk more when inactive and at specific times in the day.
  3. In order to create a habit of exercising every day, Bellabeat could send a notification at a specific time for the user to remain consistent throughout the week. Reminding users that even light activity, is still activity.

Calories:

  1. Calories are burnt by the steps taken daily. Based on users’ objectives, Bellabeat could recommend a minimum number of steps for users to take to encourage them to achieve their goals throughout the day.

Activities:

  1. Users seem to prefer lighter activities. Adding in-app articles or even a guided exercise program could bring more engagement.

Sleep:

  1. According to the CDC guidelines, users should be getting approximately 7-9 hours of sleep each night. Users are not quite meeting this recommended goal. Developing an in-app sleep routine or schedule would help users meet their sleep goals.
  2. Studies have shown that a routine before bed can help achieve a better sleep. Bellabeat could send a reminder to start that routine at a certain time and recommend activities to relax and improve sleep. Articles about sleep routines would be helpful as well.
  3. The data shows that in order to sleep better the best type of exercise is light to moderate (less than 10,000 steps). Bellabeat could loop this recommendation into the above reminder by encouraging light movement that could better suit their physical needs during the workweek.

Engagement:

  1. Bellabeats main focus is to create more engagement with their users by providing a good consumer experience. It is easy to gain a following, but the idea is to maintain one as well and prevent any decrease in monthly users. That’s why it is important to inform users how our services will benefit them in the long run. Small things such as congratulating a user for walking a farther distance today compared to yesterday will go a long way.
  2. It appears that our audience are mostly corporate workers who utilize their lunch and after work hours for movement and exercise. A possible recommendation would be to add articles on how to achieve movement while at work.
  3. A great way Bellabeat could motivate users is also recommending some mindful eating habits and recipe articles within the Bellabeat app.
  4. Providing quarterly or annual health reports could prove to encourage ongoing user engagement.
  5. A recommended diet plan based off of users BMI or health status can help users be more knowledgeable of their health.
  6. 1 month free trials can help more users sign up for this service.
  7. Establishing a social media presence can help consumers learn more about what services Bellabeat offers.

5.2 Recomendations

  • Logging, inactivity, step goal, exercise and sleep routine reminders and notifications.
  • Recommendation on minimum steps throughout the day to encourage users.
  • In-app articles on exercise including one specifically geared towards corporate workers.
  • In-app guided exercise program.
  • In-app sleep routines or schedules.
  • Good consumer experiences, such as congratulating users for reaching higher milestones than the previous days.
  • Quarterly or annual health reports for members.
  • Diet plan based off of users BMI or health report.
  • 1 month free trials to bring in more consumers.
  • Establishing a social media presence to help consumers learn more about our services.
Thank you for reading. If you would like to view more of my work, you can access my portfolio here.
LS0tDQp0aXRsZTogIkNhcHN0b25lIFByb2plY3QgfCBCZWxsYWJlYXQgRGF0YSBBbmFseXNpcyINCmF1dGhvcjogIkFsaWNpYSBBbGtpcmUiDQpkYXRlOiAiYHIgZm9ybWF0KFN5cy50aW1lKCksICclZCAlQiwgJVknKWAiDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOiANCiAgICB0b2M6IHllcw0KICAgIG51bWJlcl9zZWN0aW9uczogeWVzDQogICAgdGhlbWU6IGNvc21vDQogICAgaGlnaGxpZ2h0OiB0YW5nbw0KLS0tDQoNCmBgYHtyIHNldHVwLCBpbmNsdWRlPUZBTFNFfQ0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQ0KYGBgDQoNCjxjZW50ZXI+PGltZw0Kc3JjPSJodHRwczovL3d3dy5jaGlwaW4uY29tL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE3LzA1L1doYXQtaXQtVHJhY2tzLmpwZyI+DQo8L2NlbnRlcj4NCg0KDQo8Y2VudGVyPjxmb250IHNpemU9IjMiPioqQmVsbGFiZWF0KippcyBhIHN1Y2Nlc3NmdWwgc21hbGwgYnVzaW5lc3Mgd2hvIHNwZWNpYWxpemVzIGluIGhlYWx0aC1mb2N1c2VkIHNtYXJ0IGRldmljZXMgZm9yIHdvbWVuLiBXaXRoIHRoZWlyIGlubm92YXRpdmUgc21hcnQgZGV2aWNlIGRlc2lnbiwgdGhleSBoYXZlIHBvdGVudGlhbCB0byBiZWNvbWUgYSBsYXJnZXIgcGxheWVyIGluIHRoZSBnbG9iYWwgbWFya2V0LjwvY2VudGVyPg0KDQojIEFzaw0KDQojIyBCdXNpbmVzcyB0YXNrDQoNClRoZSBzdGFrZWhvbGRlcnMgYXQgQmVsbGFiZWF0IHdvdWxkIGxpa2UgdG8gaWRlbnRpZnkgbmV3IG9wcG9ydHVuaXRpZXMgZm9yIGdyb3d0aC4gR2l2ZW4gdGhpcyB0YXNrLCBJIHdpbGwgYmUgY29uZHVjdGluZyBhbiBhbmFseXNpcyBiYXNlZCBvbiB0aGUgaW5mb3JtYXRpb24gb2YgaXRzIHVzZXJzIGFuZCBob3cgdGhleSBhcmUgdXRpbGl6aW5nIHRoZSBwcm9kdWN0cyBvZmZlcmVkIGJ5IHRoZSBjb21wYW55LiBJIHRoZW4gd2lsbCBtYWtlIHVzZWZ1bCByZWNvbW1lbmRhdGlvbnMgdG8gdW5sb2NrIG5ldyBtYXJrZXRpbmcgc3RyYXRlZ2llcyBiYXNlZCBvbiB0cmVuZHMgd2l0aGluIHRoZSBkYXRhLg0KDQojIyBLZXkgc3Rha2Vob2xkZXJzDQoNCiogX1VyxaFrYSBTcsWhZW5fOiBCZWxsYWJlYXTigJlzIGNvLWZvdW5kZXIgYW5kIENoaWVmIENyZWF0aXZlIE9mZmljZXIuDQoqIF9TYW5kbyBNdXJfOiBNYXRoZW1hdGljaWFuIGFuZCBCZWxsYWJlYXTigJlzIGNvLWZvdW5kZXIuDQoqIF9CZWxsYWJlYXQgbWFya2V0aW5nIGFuYWx5dGljcyB0ZWFtLl8NCg0KKioqDQoNCiMgUHJlcGFyZSB7LnRhYnNldCAudGFic2V0LWZhZGUgLnRhYnNldC1waWxsc30NCg0KIyNBYm91dCB0aGUgZGF0YToNCg0KVGhlIGRhdGEgdXNlZCBmb3IgdGhpcyBhbmFseXNpcyBpcyBwdWJsaWMgYW5kIGFjY2Vzc2libGUgYnkgZXZlcnlvbmUuIFRoZSBzb3VyY2Ugb2YgdGhlIGRhdGEgaXMgcmVsaWFibGUgYXMgaXQgd2FzIHByb3ZpZGVkIGJ5IEZpdEJpdCBGaXRuZXNzIFRyYWNrZXIsIHdoaWNoIGlzIGEgd2VsbCBrbm93biBlbnRpdHkuDQoNClRvIGRvd25sb2FkIHRoZSBkYXRhLCBjbGljayBbaGVyZV0oaHR0cHM6Ly93d3cua2FnZ2xlLmNvbS9kYXRhc2V0cy9hcmFzaG5pYy9maXRiaXQpLg0KDQpMaW1pdGF0aW9uczoNCisgVGhlIGRhdGEgaXMgZnJvbSAyMDE2LiBGaXRCaXQgZGV2aWNlcyBoYXZlIGxpa2VseSBjaGFuZ2VkIG92ZXIgdGltZSB0byBkZWxpdmVyIG1vcmUgYWNjdXJhdGUgcmVzdWx0cy4NCisgVGhpcyBkYXRhIHdhcyBjb2xsZWN0ZWQgdGhyb3VnaCBzdXJ2ZXkgd2hpY2ggbWF5IGluZGljYXRlIHRoYXQgdGhlIHJlc3VsdHMgYXJlIG5vdCBhY2N1cmF0ZSwgYXMgc3VydmV5cyByZWx5IG9uIHBhcnRpY2lwYW50cyBwcm92aWRpbmcgaG9uZXN0IGFuc3dlcnMuDQorIFRoZSBzYW1wbGUgc2l6ZSBpcyByZWxhdGl2ZWx5IHNtYWxsLCB3aGljaCBjYW4gbGVhZCB0byBwb3RlbnRpYWwgc2FtcGxpbmcgYmlhcy4gRnVydGhlcm1vcmUsIHNpbmNlIHRoZSB0YXJnZXQgYXVkaWVuY2UgaXMgc3BlY2lmaWNhbGx5IHdvbWVuLCB3ZSBzaG91bGQgZmFjdG9yIHRoaXMgdW5yZWxpYWJpbGl0eSBpbnRvIGFueSBidXNpbmVzcyBkZWNpc2lvbiBhcyBhIHJlc3VsdCBmcm9tIHRoaXMgYW5hbHlzaXMuDQoNCiMjIEluc3RhbGxpbmcgcGFja2FnZXMNCg0KVG8gYmVnaW4sIHN0YXJ0IGJ5IGluc3RhbGxpbmcgdGhlIG5lY2Vzc2FyeSBwYWNrYWdlcyBhbmQgbG9hZGluZyB0aGVtIGludG8gdGhlIGxpYnJhcnkgZm9yIGVhc3kgYW5hbHlzaXMuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIGluc3RhbGxpbmcgcGFja2FnZXMuDQoNCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQppbnN0YWxsLnBhY2thZ2VzKCJrYWJsZUV4dHJhIikNCmluc3RhbGwucGFja2FnZXMoImhpZ2hjaGFydGVyIikNCmluc3RhbGwucGFja2FnZXMoIlJDb2xvckJyZXdlciIpDQppbnN0YWxsLnBhY2thZ2VzKCJzY2FsZXMiKQ0KaW5zdGFsbC5wYWNrYWdlcygiY293cGxvdCIpDQppbnN0YWxsLnBhY2thZ2VzKCJwbG90bHkiKQ0KaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQ0KYGBgDQoNCiMjIExvYWRpbmcgbGlicmFyaWVzDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEltcG9ydGluZyBsaWJyYXJpZXMuDQoNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShrYWJsZUV4dHJhKSAgICMgRm9yIHZpZXdpbmcgdGFibGVzIGluIGNvb2wgd2F5cw0KbGlicmFyeShzY2FsZXMpICAgIyBGb3IgdHJhbnNmb3JtaW5nIG51bWJlcnMgdG8gcGVyY2VudGFnZXMNCmxpYnJhcnkoaGlnaGNoYXJ0ZXIpICMgRm9yIEludGVyYWN0aXZlIGdyYXBocw0KbGlicmFyeShSQ29sb3JCcmV3ZXIpICAjIEZvciBkaWZmZXJlbnQgUGFsbGV0IGNvbG9ycw0KbGlicmFyeShjb3dwbG90KSAjIEZvciBwbG90dGluZyB2aXN1YWxzIGZvciBjb21wYXJpc29uDQpsaWJyYXJ5KHBsb3RseSkgIyBGb3IgcGxvdHRpbmcgaW50ZXJhY3RpdmUgcGllIGNoYXJ0cyBmb3IgY29tcGFyaXNvbg0KbGlicmFyeShkcGx5cikNCmBgYA0KDQojI0ltcG9ydGluZyBEYXRhOg0KDQpUaGUgZGF0YSBwcm92aWRlZCBieSBGaXRCaXQgRml0bmVzcyBUcmFja2VyIGhhcyAxOCBDU1YgZmlsZXMuIEVhY2ggb2YgdGhlc2UgZGF0YXNldHMgaGF2ZSBpbmZvcm1hdGlvbiByZWxhdGVkIHRvIHNtYXJ0LWRldmljZSB1c2FnZS4gU3VjaCBhcywgQWN0aXZpdHkgbGV2ZWwsIFN0ZXAgY291bnRpbmcsIGFuZCB3b3Jrb3V0IEludGVuc2l0aWVzLCBldGMuIA0KDQpBZnRlciBwcmV2aWV3aW5nIHRoZSBkYXRhLCBJIGRlY2lkZWQgdG8gdXNlIHRoZSBmb2xsb3dpbmcgZmlsZXMgYXMgdGhleSBjb250YWluIGFsbCB0aGUgbmVjZXNzYXJ5IGluZm9ybWF0aW9uIEkgbmVlZCB0byBjYXJyeSBvdXQgbXkgYW5hbHlzaXMuDQoNCmJlbG93IEkgd2lsbCBpbXBvcnQgdGhlIGRhdGFzZXRzIEknbGwgYmUgdXNpbmcuDQoNCiMjIERhaWx5IGFjdGl2aXR5DQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KZGFpbHlfYWN0aXZpdHkgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL0tlZWxleS9EZXNrdG9wL0ZpdGFiYXNlIERhdGEgNC4xMi4xNi01LjEyLjE2L2RhaWx5QWN0aXZpdHlfbWVyZ2VkLmNzdiIpDQpgYGANCg0KIyMgRGFpbHkgc2xlZXANCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQpkYWlseV9zbGVlcCA8LSByZWFkLmNzdigiQzovVXNlcnMvS2VlbGV5L0Rlc2t0b3AvRml0YWJhc2UgRGF0YSA0LjEyLjE2LTUuMTIuMTYvc2xlZXBEYXlfbWVyZ2VkLmNzdiIpDQpgYGANCg0KIyMgSG91cmx5IHN0ZXBzDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KaG91cmx5X3N0ZXBzIDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9LZWVsZXkvRGVza3RvcC9GaXRhYmFzZSBEYXRhIDQuMTIuMTYtNS4xMi4xNi9ob3VybHlTdGVwc19tZXJnZWQuY3N2IikNCmBgYA0KDQojIyBXZWlnaHQNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQp3ZWlnaHQgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL0tlZWxleS9EZXNrdG9wL0ZpdGFiYXNlIERhdGEgNC4xMi4xNi01LjEyLjE2L3dlaWdodExvZ0luZm9fbWVyZ2VkLmNzdiIpDQpgYGANCg0KIyBQcm9jZXNzDQoNCiMjIFZpZXdpbmcgdGhlIGRhdGFzZXRzIHsudGFic2V0IC50YWJzZXQtZmFkZSAudGFic2V0LXBpbGxzfQ0KDQojIyMgRGFpbHkgYWN0aXZpdHkNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgVmlld2luZyB0aGUgZGF0YXNldHMgdXNpbmcgdGhlICJrYWJsZUV4dHJhIiBwYWNrYWdlDQoNCmtibChkYWlseV9hY3Rpdml0eVsxOjUsIF0sICdodG1sJywgY2FwdGlvbiA9ICdUYWJsZSAxOiBEYWlseSBhY3Rpdml0eScpICU+JSANCiAga2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiLCAiY29uZGVuc2VkIikpICU+JSANCiAgc2Nyb2xsX2JveCh3aWR0aCA9ICIxMDAlIikgICMgQWRkaW5nIGEgc2Nyb2xsIGJhcg0KYGBgDQoNCiMjIyBEYWlseSBzbGVlcA0KDQpgYGB7cn0NCmtibChkYWlseV9zbGVlcFsxOjUsIF0sICdodG1sJywgY2FwdGlvbiA9ICdUYWJsZSAyOiBEYWlseSBzbGVlcCcpICU+JSANCmthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCg0KIyMjIEhvdXJseSBzdGVwcw0KDQpgYGB7cn0NCmtibChob3VybHlfc3RlcHNbMTo1LCBdLCAnaHRtbCcsIGNhcHRpb24gPSAnVGFibGUgMzogSG91cmx5IHN0ZXBzJykgJT4lIA0Ka2FibGVfc3R5bGluZyhib290c3RyYXBfb3B0aW9ucyA9IGMoInN0cmlwZWQiLCAiaG92ZXIiKSwgZnVsbF93aWR0aCA9IEYpDQpgYGANCg0KDQojIyMgV2VpZ2h0DQoNCmBgYHtyfQ0Ka2JsKHdlaWdodFsxOjUsIF0sICdodG1sJywgY2FwdGlvbiA9ICdUYWJsZSA0OiBXZWlnaHQnKSAlPiUgDQogIGthYmxlX3N0eWxpbmcoYm9vdHN0cmFwX29wdGlvbnMgPSBjKCJzdHJpcGVkIiwgImhvdmVyIiksIGZ1bGxfd2lkdGggPSBGKQ0KYGBgDQoNCg0KIyMgQ2xlYW5pbmcgZGF0YQ0KDQpXaGlsZSBmdXJ0aGVyIHJldmlld2luZyB0aGUgZGF0YXNldHMsIHdlIGNhbiBvYnNlcnZlIHRoYXQgc29tZSBjb2x1bW5zIHdvbid0IGJlIG5lZWRlZC4gV2Ugd2lsbCByZW1vdmUgdGhlc2UgdmFyaWFibGVzIHRoYXQgYXJlbuKAmXQgdXNlZnVsIGZvciBvdXIgYW5hbHlzaXMuDQoNCmBgYHtyfQ0KIyByZW1vdmluZyB1bm5lY2Vzc2FyeSBjb2x1bW5zDQoNCmRhaWx5X2FjdGl2aXR5IDwtIGRhaWx5X2FjdGl2aXR5ICU+JSBzZWxlY3QoLWMoVHJhY2tlckRpc3RhbmNlKSkNCg0Kd2VpZ2h0IDwtIHdlaWdodCAlPiUgc2VsZWN0KC1jKFdlaWdodEtnLCBGYXQsIElzTWFudWFsUmVwb3J0LCBMb2dJZCkpDQoNCmBgYA0KDQpJIHdpbGwgc3RhcnQgYnkgY2hlY2tpbmcgdW5pcXVlIElkJ3MsIHJlbW92aW5nIGR1cGxpY2F0ZXMsIHJlbmFtaW5nIGNvbHVtbnMgZm9yIGNvbnNpc3RlbmN5LCBhbmQgc3BsaXR0aW5nIHRoZSBkYXRldGltZSBpbnRvIHR3byBjb2x1bW5zLiBJJ2xsIGFsc28gY2xlYW4gdXAgdGhlIGRhdGUgY29sdW1ucyB0byBtYWtlIHRoZW0gYWxsIG1hdGNoIHRoZSBjb3JyZWN0IGZvcm1hdC4gIA0KDQpgYGB7cn0NCiMgQ2hlY2tpbmcgdGhlIG51bWJlciBvZiB1bmlxdWUgSWQncw0KDQpuX2Rpc3RpbmN0KGRhaWx5X2FjdGl2aXR5JElkKQ0KDQpuX2Rpc3RpbmN0KGRhaWx5X3NsZWVwJElkKQ0KDQpuX2Rpc3RpbmN0KHdlaWdodCRJZCkNCg0Kbl9kaXN0aW5jdChob3VybHlfc3RlcHMkSWQpDQoNCmBgYA0KDQoNCmBgYHtyfQ0KIyBDaGVja2luZyBmb3IgZHVwbGljYXRlcw0KDQpzdW0oZHVwbGljYXRlZChkYWlseV9hY3Rpdml0eSkpDQoNCnN1bShkdXBsaWNhdGVkKGRhaWx5X3NsZWVwKSkNCg0Kc3VtKGR1cGxpY2F0ZWQod2VpZ2h0KSkNCg0Kc3VtKGR1cGxpY2F0ZWQoaG91cmx5X3N0ZXBzKSkNCg0KYGBgDQoNCg0KYGBge3J9DQojIFJlbW92ZSBkdXBsaWNhdGVzIGFuZCBkcm9wIG51bGwgdmFsdWVzDQoNCmRhaWx5X3NsZWVwIDwtIGRhaWx5X3NsZWVwWyFkdXBsaWNhdGVkKGRhaWx5X3NsZWVwKSxdDQoNCmBgYA0KDQpgYGB7cn0NCiMgQ29uZmlybWluZyBkdXBsaWNhdGVzIGluIHNsZWVwIHRhYmxlIHdlcmUgcmVtb3ZlZA0KDQpzdW0oZHVwbGljYXRlZChkYWlseV9zbGVlcCkpDQoNCmBgYA0KDQpIZXJlLCBJJ2xsIGZvY3VzIG9uIGZvcm1hdHRpbmcsIGNvbnNpc3RlbnQgY29sdW1uIG5hbWVzLCBhbmQgc3BsaXR0aW5nICJEYXRlIiBhbmQgIlRpbWUiIGludG8gdGhlaXIgb3duIHNlcGFyYXRlIGNvbHVtbnMuDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIHNwbGl0IGRhdGV0aW1lIGludG8gaXRzIG93biBjb2x1bW5zICJEYXRlIiwgIlRpbWUiIGFuZCByZW5hbWUgY29sdW1ucyBmb3IgY29uc2lzdGVuY3kuDQoNCndlaWdodCA8LSBzZXBhcmF0ZShkYXRhID0gd2VpZ2h0LCBjb2wgPSBEYXRlLCBpbnRvICA9IGMoJ0RhdGUnLCAnVGltZScpLCBzZXAgPSAnICcpDQoNCmRhaWx5X3NsZWVwIDwtIHNlcGFyYXRlKGRhdGEgPSBkYWlseV9zbGVlcCwgY29sID0gU2xlZXBEYXksIGludG8gID0gYygnRGF0ZScsICdUaW1lJyksIHNlcCA9ICcgJykNCg0KaG91cmx5X3N0ZXBzIDwtIHNlcGFyYXRlKGRhdGEgPSBob3VybHlfc3RlcHMsIGNvbCA9IEFjdGl2aXR5SG91ciwgaW50byAgPSBjKCdEYXRlJywgJ1RpbWUnKSwgc2VwID0gJyAnKQ0KDQpjb2xuYW1lcyhkYWlseV9hY3Rpdml0eSlbMl09IkRhdGUiIA0KDQoNCiMgZm9ybWF0dGluZyB2YXJpYWJsZXMuDQoNCmRhaWx5X2FjdGl2aXR5IDwtIGRhaWx5X2FjdGl2aXR5ICU+JQ0KICBtdXRhdGUoRGF0ZSA9IGFzX2RhdGV0aW1lKERhdGUsIGZvcm1hdCA9ICIlbS8lZC8lWSIpKQ0KDQpkYWlseV9zbGVlcCA8LSBkYWlseV9zbGVlcCAlPiUNCiAgbXV0YXRlKERhdGUgPSBhc19kYXRldGltZShEYXRlLGZvcm1hdCA9IiVtLyVkLyVZIikpDQoNCmhvdXJseV9zdGVwczwtIGhvdXJseV9zdGVwcyAlPiUgDQogIG11dGF0ZShEYXRlID0gYXNfZGF0ZXRpbWUoRGF0ZSxmb3JtYXQgPSIlbS8lZC8lWSIpKQ0KDQp3ZWlnaHQgPC0gd2VpZ2h0ICU+JQ0KICBtdXRhdGUoRGF0ZSA9IGFzX2RhdGV0aW1lKERhdGUsZm9ybWF0ID0iJW0vJWQvJVkiKSkNCg0KYGBgDQoNCiMjIENoZWNraW5nIHZhcmlhYmxlcyANCg0KYGBge3J9DQojIENoZWNraW5nIHZhcmlhYmxlcw0Kc3RyKGRhaWx5X2FjdGl2aXR5KQ0KDQpgYGANCioqKg0KDQojIEFuYWx5emUNCg0KSSdsbCBzZXQgdXAgYSBjb21tb24gdGhlbWUgZm9yIHBsb3RzIGluIHRoZSBhbmFseXNpcyBoZXJlIGJlZm9yZSBJIHN0YXJ0LiBUaGlzIG1ha2VzIG91ciB2aXN1YWxzIG1vcmUgc3RyZWFtbGluZSBhbmQgY29uc2lzdGVudC4NCg0KYGBge3J9DQojIENyZWF0aW5nIGNvbW1vbiB0aGVtZSBmb3IgcGxvdHMNCg0KY3VzdG9tX3RoZW1lIDwtIGZ1bmN0aW9uKCkgew0KICB0aGVtZSgNCiAgICBwYW5lbC5ib3JkZXIgPSBlbGVtZW50X3JlY3QoY29sb3VyID0gImJsYWNrIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZpbGwgPSBOQSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpbmV0eXBlID0gMSksDQogICAgcGFuZWwuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChmaWxsID0gIndoaXRlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvciA9ICdncmV5NTAnKSwNCiAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCksDQogICAgYXhpcy50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICJibGFjayIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYWNlID0gIml0YWxpYyIsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiSGVsdmV0aWNhIiksDQogICAgYXhpcy50aXRsZSA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhbWlseSA9ICJIZWx2ZXRpY2EiKSwNCiAgICBheGlzLnRpY2tzID0gZWxlbWVudF9saW5lKGNvbG91ciA9ICJibGFjayIpLA0KICAgIHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoc2l6ZT0yMywgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBoanVzdCA9IDAuNSwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmYW1pbHkgPSAiSGVsdmV0aWNhIiksDQogICAgcGxvdC5zdWJ0aXRsZT1lbGVtZW50X3RleHQoc2l6ZT0xNiwgDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaGp1c3QgPSAwLjUpLA0KICAgIHBsb3QuY2FwdGlvbiA9IGVsZW1lbnRfdGV4dChjb2xvdXIgPSAiYmxhY2siLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFjZSA9ICJpdGFsaWMiLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFtaWx5ID0gIkhlbHZldGljYSIpDQogICkNCn0NCmBgYA0KDQojIyBTbWFydCBEZXZpY2UgVXNhZ2UNCg0KVG8gcmVhbGx5IGRpZyBpbnRvIGNvbnN1bWVyIGJlaGF2aW9ycywgd2UnbGwgbmVlZCB0byB1bmRlcnN0YW5kIGhvdyB0aGUgZGV2aWNlcyBhcmUgYmVpbmcgdXNlZCBhbmQgZm9yIHdoYXQgcHVycG9zZXMuIFdlIHdpbGwgZG8gdGhpcyBieSBncm91cGluZyB0aGUgZGF0YXNldHMgZGFpbHlfYWN0aXZpdHksIGRhaWx5X3NsZWVwLCBhbmQgd2VpZ2h0LiBUaGlzIHdpbGwgYWxsb3cgdXMgdG8gZmluZCB0aGUgZGlzdGluY3QgbnVtYmVyIG9mIHBhcnRpY2lwYW50cywgdGhlIGZyZXF1ZW5jaWVzIHRoZXkncmUgdXNpbmcgc21hcnQgZGV2aWNlcywgYXMgd2VsbCBhcyB0aGUgZGlzdHJpYnV0aW9uLg0KDQojIyMgR3JvdXBpbmcgdXNlcnMNCg0KYGBge3J9DQojIEdyb3VwaW5nIGRhaWx5IGFjdGl2aXR5IGZvciBkYWlseSBhdmVyYWdlcyBvZiB1c2Vycw0KDQpkYWlseV9hY3Rpdml0eV91c2VyX3N1bW1hcnkgPC0gZGFpbHlfYWN0aXZpdHkgJT4lDQogZ3JvdXBfYnkoSWQpICU+JQ0KIHN1bW1hcmlzZShuID0gbigpLCBzdGVwcyA9IHJvdW5kKG1lYW4oVG90YWxTdGVwcykpLCBjYWxvcmllcyA9IHJvdW5kKG1lYW4oQ2Fsb3JpZXMpKSwNCiAgICAgICAgICAgVmVyeUFjdGl2ZU1pbnV0ZXMgPSByb3VuZChtZWFuKFZlcnlBY3RpdmVNaW51dGVzKSksIA0KICAgICAgICAgICBGYWlybHlBY3RpdmVNaW51dGVzID0gcm91bmQobWVhbihGYWlybHlBY3RpdmVNaW51dGVzKSksIA0KICAgICAgICAgICBMaWdodGx5QWN0aXZlTWludXRlcyA9IHJvdW5kKG1lYW4oTGlnaHRseUFjdGl2ZU1pbnV0ZXMpKSwgDQogICAgICAgICAgIFNlZGVudGFyeU1pbnV0ZXMgPSByb3VuZChtZWFuKFNlZGVudGFyeU1pbnV0ZXMpKSkNCg0KDQoNCiMgR3JvdXBpbmcgc2xlZXAgbG9nIGJ5IHVzZXINCmRhaWx5X3NsZWVwX3VzZXJfc3VtbWFyeSA8LSBkYWlseV9zbGVlcCAlPiUNCiAgZ3JvdXBfYnkoSWQpICU+JQ0KICBzdW1tYXJpc2Uobj1uKCksIGF2ZXJhZ2VUaW1lQXNsZWVwID0gbWVhbihUb3RhbE1pbnV0ZXNBc2xlZXApLCBhdmVyYWdlVGltZUluQmVkID0gbWVhbihUb3RhbFRpbWVJbkJlZCkpDQoNCiMgR3JvdXBpbmcgd2VpZ2h0IGxvZyBieSB1c2VyDQp3ZWlnaHRfdXNlcl9zdW1tYXJ5IDwtIHdlaWdodCAlPiUNCiAgZ3JvdXBfYnkoSWQpICU+JQ0KICBzdW1tYXJpc2Uobj1uKCksIHdlaWdodF9sYnMgPSBtZWFuKFdlaWdodFBvdW5kcyksIG1heF93ZWlnaHQgPSBtYXgoV2VpZ2h0UG91bmRzKSwgbWluX3dlaWdodCA9IG1pbihXZWlnaHRQb3VuZHMpLCBhdmVyYWdlQk1JID0gbWVhbihCTUkpKQ0KDQpuX2Rpc3RpbmN0KGRhaWx5X2FjdGl2aXR5X3VzZXJfc3VtbWFyeSRJZCkNCg0Kbl9kaXN0aW5jdChkYWlseV9zbGVlcF91c2VyX3N1bW1hcnkkSWQpDQoNCm5fZGlzdGluY3Qod2VpZ2h0X3VzZXJfc3VtbWFyeSRJZCkNCg0KYGBgDQoNClBhcnRpY2lwYW50IG51bWJlcnMgaW4gdGhpcyBhY3Rpdml0eSBhcmUgMzMgZm9yIGFjdGl2aXR5LCAyNCBmb3Igc2xlZXAsIGFuZCA4IGZvciB3ZWlnaHQgZGF0YXNldHMuDQoNCklmIHdlIGNhdGVnb3JpemUgdGhlIHVzZXJzIGJhc2VkIG9uIGhvdyBvZnRlbiB0aGV5IGxvZyBwZXIgZGF5LCB0aGVuIGdyb3VwIHRoZSBkYXRhIGJhc2VkIG9uIHRoZSBjYXRlZ29yaWVzLCB3ZSB3aWxsIGJlIGFibGUgc2VlIHRoZSB0b3RhbCBudW1iZXIsIGFuZCBwZXJjZW50YWdlIG9mIHVzZXJzIGluIGVhY2ggY2F0ZWdvcnkuIFdoaWNoIHdpbGwgZ2l2ZSB1cyBhIGJldHRlciBwaWN0dXJlIG9mIGhvdyB0aGUgZGV2aWNlcyBhcmUgYmVpbmcgdXNlZC4NCg0KVG8gZG8gdGhpcywgSSdsbCBzdGFydCBieSBjYXRlZ29yaXplIHRoZW0gaW50byAibG93IiwgIm1lZGl1bSIgYW5kICJyZWd1bGFyIiBmb3IgdXNhZ2Ugb2YgMC0xMCwgMTEtMjAsIGFuZCAyMS0zMSBkYXlzLg0KDQpBZnRlciBncm91cGluZyB0aGUgZGF0YSwgd2UnbGwgcHVsbCB0aGUgdG90YWwgbnVtYmVyIGFuZCBwZXJjZW50YWdlIHNvIHdlIGNhbiBjcmVhdGUgYSB2aXN1YWxpemF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gYnkgdXNhZ2UgZm9yIGFsbCB0aHJlZSB0eXBlcyBvZiBoZWFsdGggZGF0YS4NCg0KIyMjIEFzc2lnbmluZyBjYXRlZ29yaWVzLCBncm91cGluZywgc29ydGluZyBhbmQgdmlld2luZyB0aGUgdXNhZ2UgZGlzdHJpYnV0aW9uDQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIEFzc2lnbmluZyBjYXRlZ29yaWVzIHRvIHVzZXJzIGJhc2VkIG9uIG51bWJlciBvZiBkYXlzIGxvZ2dlZA0KDQpkYWlseV9hY3Rpdml0eV91c2VyX3N1bW1hcnkkVXNhZ2UgPC0gaWZlbHNlKGRhaWx5X2FjdGl2aXR5X3VzZXJfc3VtbWFyeSRuPD0xMCwiTG93IiwgDQogICAgICAgICAgICAgICAgaWZlbHNlKGRhaWx5X2FjdGl2aXR5X3VzZXJfc3VtbWFyeSRuPjEwICYgZGFpbHlfYWN0aXZpdHlfdXNlcl9zdW1tYXJ5JG48MjEsICJNZWRpdW0iLA0KICAgICAgICAgICAgICAgICAgICAgICAiUmVndWxhciIpKQ0KDQpkYWlseV9zbGVlcF91c2VyX3N1bW1hcnkkVXNhZ2UgPC0gaWZlbHNlKGRhaWx5X3NsZWVwX3VzZXJfc3VtbWFyeSRuPD0xMCwiTG93IiwgDQogICAgICAgICAgICAgICAgaWZlbHNlKGRhaWx5X3NsZWVwX3VzZXJfc3VtbWFyeSRuPjEwICYgZGFpbHlfc2xlZXBfdXNlcl9zdW1tYXJ5JG48MjEsICJNZWRpdW0iLCAiUmVndWxhciIpKQ0KDQp3ZWlnaHRfdXNlcl9zdW1tYXJ5JFVzYWdlIDwtIGlmZWxzZSh3ZWlnaHRfdXNlcl9zdW1tYXJ5JG48PTEwLCJMb3ciLCANCiAgICAgICAgICAgICAgICBpZmVsc2Uod2VpZ2h0X3VzZXJfc3VtbWFyeSRuPjEwICYgd2VpZ2h0X3VzZXJfc3VtbWFyeSRuPDIxLCAiTWVkaXVtIiwgIlJlZ3VsYXIiKSkNCg0KDQoNCiMgR3JvdXBpbmcgdXNlcnMgYmFzZWQgb24gbnVtYmVyIG9mIGRheXMgbG9nZ2VkDQoNCmRhaWx5X2FjdGl2aXR5X3VzYWdlIDwtIGRhaWx5X2FjdGl2aXR5X3VzZXJfc3VtbWFyeSAlPiUNCiAgZ3JvdXBfYnkoVXNhZ2UpICU+JQ0KICBzdW1tYXJpc2UodXNlckNvdW50PW4oKSwgYXZlcmFnZV91c2FnZT0gcm91bmQobWVhbihuKSwwKSkNCg0KZGFpbHlfc2xlZXBfdXNhZ2UgPC0gZGFpbHlfc2xlZXBfdXNlcl9zdW1tYXJ5ICU+JQ0KICBncm91cF9ieShVc2FnZSkgJT4lDQogIHN1bW1hcmlzZSh1c2VyQ291bnQ9bigpLCBhdmVyYWdlX3VzYWdlPSByb3VuZChtZWFuKG4pLDApKQ0KDQp3ZWlnaHRfdXNhZ2UgPC0gd2VpZ2h0X3VzZXJfc3VtbWFyeSAlPiUNCiAgZ3JvdXBfYnkoVXNhZ2UpICU+JQ0KICBzdW1tYXJpc2UodXNlckNvdW50PW4oKSwgYXZlcmFnZV91c2FnZT0gcm91bmQobWVhbihuKSwwKSkNCg0KDQoNCiMgU29ydGluZyBjYXRlZ29yaWVzIGJhc2VkIG9uIHRoZSBudW1iZXIgb2YgdXNlcnMgaW4gZWFjaCBjYXRlZ29yeQ0KDQpkYWlseV9hY3Rpdml0eV91c2FnZSA8LSBkYWlseV9hY3Rpdml0eV91c2FnZVtvcmRlcigtZGFpbHlfYWN0aXZpdHlfdXNhZ2UkYXZlcmFnZV91c2FnZSksXQ0KZGFpbHlfc2xlZXBfdXNhZ2UgPC0gZGFpbHlfc2xlZXBfdXNhZ2Vbb3JkZXIoLWRhaWx5X3NsZWVwX3VzYWdlJGF2ZXJhZ2VfdXNhZ2UpLF0NCndlaWdodF91c2FnZSA8LSB3ZWlnaHRfdXNhZ2Vbb3JkZXIoLXdlaWdodF91c2FnZSRhdmVyYWdlX3VzYWdlKSxdDQoNCg0KDQojIEFkZGluZyBwZXJjZW50YWdlIGNvbHVtbiBmb3IgdGhlIG51bWJlciBvZiB1c2VycyBpbiBlYWNoIGNhdGVnb3J5DQoNCmRhaWx5X2FjdGl2aXR5X3VzYWdlJHBlcmMgPC1zY2FsZXM6OnBlcmNlbnQoDQogIGRhaWx5X2FjdGl2aXR5X3VzYWdlJHVzZXJDb3VudC9zdW0oZGFpbHlfYWN0aXZpdHlfdXNhZ2UkdXNlckNvdW50KSkNCmRhaWx5X3NsZWVwX3VzYWdlJHBlcmMgPC0gc2NhbGVzOjpwZXJjZW50KA0KICBkYWlseV9zbGVlcF91c2FnZSR1c2VyQ291bnQvc3VtKGRhaWx5X3NsZWVwX3VzYWdlJHVzZXJDb3VudCkpDQp3ZWlnaHRfdXNhZ2UkcGVyYyA8LSBzY2FsZXM6OnBlcmNlbnQoDQogIHdlaWdodF91c2FnZSR1c2VyQ291bnQvc3VtKHdlaWdodF91c2FnZSR1c2VyQ291bnQpKQ0KDQoNCg0KIyBWaXN1YWxpemluZyB0aGUgcmVwcmVzZW50YXRpb24gb2YgdXNhZ2UNCg0KY29sb3JzIDwtIGMoIm1lZGl1bWJsdWUiICwgIm1lZGl1bXZpb2xldHJlZCIsImRhcmtvcmFuZ2UiICkNCmNvbG9yczEgPC0gYygibWVkaXVtYmx1ZSIsICJtZWRpdW12aW9sZXRyZWQiLCJkYXJrb3JhbmdlIiApDQpmaWdfdXNhZ2UgPC0gcGxvdF9seSgpDQpmaWdfdXNhZ2UgPC0gZmlnX3VzYWdlICU+JSBhZGRfcGllKGRhdGEgPSBkYWlseV9hY3Rpdml0eV91c2FnZSwgbGFiZWxzID0gflVzYWdlLCB2YWx1ZXMgPSB+dXNlckNvdW50LA0KICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIkFjdGl2aXR5IiwgZG9tYWluID0gbGlzdCh4ID0gYygwLCAwLjQpLCB5ID0gYygwLjQsIDEpKSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG1hcmtlciA9IGxpc3QoY29sb3JzID0gY29sb3JzLCBsaW5lID0gbGlzdChjb2xvciA9ICcjRkZGRkZGJywgd2lkdGggPSAxKSkpDQoNCmZpZ191c2FnZSA8LSBmaWdfdXNhZ2UgJT4lIGFkZF9waWUoZGF0YSA9IGRhaWx5X3NsZWVwX3VzYWdlLCBsYWJlbHMgPSB+VXNhZ2UsIHZhbHVlcyA9IH51c2VyQ291bnQsDQogICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiU2xlZXAiLCBkb21haW4gPSBsaXN0KHggPSBjKDAuNiwgMSksIHkgPSBjKDAuNCwgMSkpLA0KICAgICAgICAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9ycyA9IGNvbG9ycywgbGluZSA9IGxpc3QoY29sb3IgPSAnI0ZGRkZGRicsIHdpZHRoID0gMSkpLA0KICAgICAgICAgICAgICAgICAgICAgICBzaG93bGVnZW5kID0gVFJVRSkNCiAgICAgICAgICAgICAgICAgICAgICAgDQpmaWdfdXNhZ2UgPC0gZmlnX3VzYWdlICU+JSBhZGRfcGllKGRhdGEgPSB3ZWlnaHRfdXNhZ2UsIGxhYmVscyA9IH5Vc2FnZSwgdmFsdWVzID0gfnVzZXJDb3VudCwNCiAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJXZWlnaHQiLCBkb21haW4gPSBsaXN0KHggPSBjKDAuMjUsIDAuNzUpLCB5ID0gYygwLCAwLjYpKSwNCiAgICAgICAgICAgICAgICAgICAgICBtYXJrZXIgPSBsaXN0KGNvbG9ycyA9IGNvbG9yczEsIGxpbmUgPSBsaXN0KGNvbG9yID0gJyNGRkZGRkYnLCB3aWR0aCA9IDEpKSkNCg0KZmlnX3VzYWdlIDwtIGZpZ191c2FnZSAlPiUgbGF5b3V0KHRpdGxlID0gIlVzYWdlIGRpc3RyaWJ1dGlvbiBpbiBhY3Rpdml0eSwgc2xlZXAgYW5kIHdlaWdodCBkYXRhIHNldHMiLCBzaG93bGVnZW5kID0gRiwNCiAgICAgICAgIHhheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSwNCiAgICAgICAgIHlheGlzID0gbGlzdChzaG93Z3JpZCA9IEZBTFNFLCB6ZXJvbGluZSA9IEZBTFNFLCBzaG93dGlja2xhYmVscyA9IEZBTFNFKSkNCg0KZmlnX3VzYWdlDQoNCg0KDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHRoYXQgODcuOSUgb2YgdXNlcnMgYXJlIHJlZ3VsYXJseSBsb2dnaW5nIHdoZW4gaXQgY29tZXMgdG8gZGFpbHkgYWN0aXZpdHkuIFdlIGhhdmUgOS4wOSUgbWVkaXVtLCBhbmQgMy4wMyUgbG93IGZyZXF1ZW5jeSB1c2Vycy4NCg0KSG93ZXZlciwgaWYgd2UgbG9vayBhdCB0aGUgY2hhcnRzIGZvciBzbGVlcCBhbmQgd2VpZ2h0IHdlIGNhbiBzZWUgdGhhdCB0aGV5J3JlIGJvdGggYmVpbmcgdW5kZXJ1dGlsaXplZC4gU2xlZXAgbG9ncyBzaG93IG9ubHkgNTAlIG9mIHVzZXJzIGFyZSBsb2dnaW5nIHJlZ3VsYXJseSBhbmQgd2VpZ2h0IGhhcyBhIG1lcmUgMjUlLiANCg0KS2V5IHRha2Vhd2F5OiBVc2VycyBhcmUgbm90IGxvZ2dpbmcgdGhlaXIgc2xlZXAgYW5kIHdlaWdodCBhcyBvZnRlbi4gUHJvdmlkaW5nIHRoZSBkZXZpY2VzIHdpdGggYSBmZWF0dXJlIHRvIHJlbWluZCB1c2VycyB0byBsb2cgYWN0aXZpdGllcyB3b3VsZCBiZSBoZWxwZnVsLg0KDQojIyMgTW9uaXRvcmluZyBJbmFjdGl2aXR5DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIENyZWF0aW5nIGEgV2Vla2RheSBjYXRlZ29yeSBmb3IgZnVydGhlciBldmFsdWF0aW9uLg0KDQpEYXRlIDwtIGRhaWx5X2FjdGl2aXR5ICU+JSBkaXN0aW5jdChEYXRlKQ0KRGF0ZSRXZWVrRGF5IDwtIHdlZWtkYXlzKERhdGUkRGF0ZSkgICAgICAgICAgICAgICAgICAgICAgICAgICAgIA0KZGFpbHlfYWN0aXZpdHkgPC0gbWVyZ2UoZGFpbHlfYWN0aXZpdHksIERhdGUsIGJ5PSAiRGF0ZSIpDQoNCiMgbWFwcGluZyBvdXIgV2Vla2RheSBjYXRlZ29yeSB3aXRoIGFjdGl2aXR5IGxvZ3MgdG8gc2VlIGFueSB0cmVuZHMgb2YgaW5hY3RpdmUgbG9nZ2luZy4NCg0KZ2dwbG90KGRhdGE9ZGFpbHlfYWN0aXZpdHkpKw0KICBnZW9tX2NvbChtYXBwaW5nPWFlcyh4PVNlZGVudGFyeU1pbnV0ZXMseT1mYWN0b3IoV2Vla0RheSxsZXZlbHM9YygiU3VuZGF5IiwiTW9uZGF5IiwiVHVlc2RheSIsICJXZWRuZXNkYXkiLCAiVGh1cnNkYXkiLCJGcmlkYXkiLCJTYXR1cmRheSIpKSksZmlsbD0nc3RlZWxibHVlMycpK2xhYnModGl0bGU9IldlZWtseSBJbmFjdGl2ZSBNaW51dGVzIix5PSAiICIsDQogICAgICAgIGNhcHRpb24gPSAnRGF0YSBTb3VyY2U6IEZpdEJpdCBGaXRuZXNzIFRyYWNrZXIgRGF0YScpDQpgYGANCg0KVXNlcnMgYXJlIG1vc3QgaW5hY3RpdmUgVHVlc2RheSB0byBUaHVyc2RheS4gVGhlc2UgZGF5cyBjb3VsZCBiZSBzcGlrZWQgdXAgaGlnaGVyIGR1ZSB0byBhIG5vcm1hbCB3b3JrIHdlZWsuIA0KDQpLZXkgdGFrZWF3YXk6IFByb3ZpZGluZyByZW1pbmRlcnMgb3Igbm90aWZpY2F0aW9ucyBmb3IgaW5hY3Rpdml0eSBjYW4gYWxzbyBoZWxwIGVuY291cmFnZSBtb3ZlbWVudCB3aGVuIHVzZXJzIGhhdmUgYmVlbiBpbmFjdGl2ZSBmb3IgdG9vIGxvbmcuIA0KDQojIyBTdGVwcw0KDQojIyMgQW5hbHlzZSBIb3VybHkgU3RlcHMNCg0KTm93LCBsZXQncyBsb29rIGF0IGlzIHdoZW4gdXNlcnMgYXJlIG1vc3QgYWN0aXZlLg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDaGVja2luZyBUb3RhbCBTdGVwcyB2cy4gSG91cnMgb2YgdGhlIGRheS4NCg0KaG91cmx5X3N0ZXBzICU+JQ0KICBncm91cF9ieShUaW1lKSAlPiUNCiAgc3VtbWFyaXplKGF2ZXJhZ2Vfc3RlcHMgPSBtZWFuKFN0ZXBUb3RhbCkpICU+JQ0KICBnZ3Bsb3QoKSArDQogIGdlb21fY29sKG1hcHBpbmcgPSBhZXMoeD1UaW1lLCB5ID0gYXZlcmFnZV9zdGVwcywgZmlsbCA9IGF2ZXJhZ2Vfc3RlcHMpKSArIA0KICBsYWJzKHRpdGxlID0gIlN0ZXBzIHRha2VuIGVhY2ggaG91ciBvZiB0aGUgZGF5IiwgDQogICAgICAgeD0gIiAiLA0KICAgICAgIHkgPSAiSG91cmx5IFN0ZXBzIiwNCiAgICAgICBjYXB0aW9uID0gJ0RhdGEgU291cmNlOiBGaXRCaXQgRml0bmVzcyBUcmFja2VyIERhdGEnKSArIA0KICBzY2FsZV9maWxsX2dyYWRpZW50KGxvdyA9ICJtZWRpdW1ibHVlIiwgaGlnaCA9ICJkYXJrb3JhbmdlIikrDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTApKQ0KYGBgDQoNClVzZXJzIGFyZSBtb3N0IGFjdGl2ZSBiZXR3ZWVuIHRoZSBob3VycyBvZiA1OjMwcC5tLi05OjMwcC5tLiwgYW5kIGFnYWluIGFyb3VuZCAxMDowMGEubS4sIHdoaWNoIGlzIGNvbnNpc3RlbnQgd2l0aCBhIG1vZGVybiB3b3JrIHNjaGVkdWxlLg0KDQpLZXkgdGFrZWF3YXk6IEl0IGFwcGVhcnMgdGhhdCBvdXIgYXVkaWVuY2UgYXJlIG1vc3RseSBjb3Jwb3JhdGUgd29ya2VycyB3aG8gdXRpbGl6ZSB0aGVpciBsdW5jaCBhbmQgYWZ0ZXIgd29yayBob3VycyBmb3IgbW92ZW1lbnQuIFBvc3NpYmxlIHJlY29tbWVuZGF0aW9uIHdvdWxkIGJlIGFydGljbGVzIG9uIGhvdyB0byBhY2hpZXZlIG1vdmVtZW50IHdoaWxlIGF0IHdvcmsuIA0KDQojIyMgRGFpbHkgc3RlcCBhY3Rpdml0eQ0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDaGVja2luZyB1c2VycycgZGFpbHkgYWN0aXZpdHkgdGhyb3VnaG91dCB0aGUgd2Vlay4NCndlZWtkYXlfc3RlcHMgPC0gZGFpbHlfYWN0aXZpdHkgJT4lDQogIG11dGF0ZSh3ZWVrZGF5ID0gd2Vla2RheXMoRGF0ZSkpDQp3ZWVrZGF5X3N0ZXBzJHdlZWtkYXkgPC1vcmRlcmVkKHdlZWtkYXlfc3RlcHMkd2Vla2RheSwgbGV2ZWxzPWMoIlN1bmRheSIsIk1vbmRheSIsICJUdWVzZGF5IiwgIldlZG5lc2RheSIsICJUaHVyc2RheSIsIkZyaWRheSIsICJTYXR1cmRheSIpKQ0KDQojIEdyb3VwaW5nIGJ5IHdlZWtkYXkNCndlZWtkYXlfc3RlcHMgPC13ZWVrZGF5X3N0ZXBzJT4lDQogIGdyb3VwX2J5KHdlZWtkYXkpICU+JQ0KICBzdW1tYXJpemUgKGRhaWx5X3N0ZXBzID0gbWVhbihUb3RhbFN0ZXBzKSkNCg0KIyBWaXN1YWxpemluZyB1c2VycyBhY3Rpdml0eSBieSBkYXkNCmdncGxvdCh3ZWVrZGF5X3N0ZXBzLGFlcyh4PXdlZWtkYXksIHk9ZGFpbHlfc3RlcHMpKSArDQogIGdlb21fY29sKGZpbGwgPSAic3RlZWxibHVlMyIpICsNCiAgZ2VvbV9obGluZSh5aW50ZXJjZXB0ID0gMTAwMDApICsNCiAgZ2VvbV90ZXh0KGRhdGE9d2Vla2RheV9zdGVwcyxhZXMobGFiZWwgPSByb3VuZChkYWlseV9zdGVwcyksIHZqdXN0ID0gMS41KSkrDQpsYWJzKHRpdGxlPSJVc2VycyBhY3Rpdml0eSBieSBkYXkiLA0KICAgIHg9ICIgIiwNCiAgICB5ID0gIkRhaWx5IFN0ZXBzIiwNCiAgICBjYXB0aW9uID0gJ0RhdGEgU291cmNlOiBGaXRCaXQgRml0bmVzcyBUcmFja2VyIERhdGEnKSsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSx2anVzdCA9IDAuNSwgaGp1c3QgPSAxKSkNCg0KYGBgDQoNCkluIHRoaXMgYmFyIGNoYXJ0LCB3ZSBjYW4gc2VlIHRoZSBkaWZmZXJlbmNlIGJldHdlZW4gZGF5cyBvZiB0aGUgd2Vlay4gTm90aW5nIHRoYXQgKipUdWVzZGF5cyoqIGFuZCAqKlNhdHVyZGF5cyoqIGFyZSB0aGUgYmVzdCBkYXkgZm9yIHVzZXJzJy4NCg0KYGBge3J9DQojIGNhbGN1bGF0aW5nIGF2ZXJhZ2Ugc3RlcHMgbGVzcyB0aGFuIDEwLDAwMCBkYWlseQ0KDQpyb3VuZChucm93KGRhaWx5X2FjdGl2aXR5X3VzZXJfc3VtbWFyeVtkYWlseV9hY3Rpdml0eV91c2VyX3N1bW1hcnkkc3RlcHMgPCAxMDAwMCxdKSAvIG5yb3coZGFpbHlfYWN0aXZpdHlfdXNlcl9zdW1tYXJ5KSAqIDEwMCkNCmBgYA0KDQo3OSUgb2Ygb3VyIHVzZXJzIGF2ZXJhZ2UgYmVsb3cgdGhlIHJlY29tbWVuZGVkIDEwLDAwMCBzdGVwcyBhIGRheSAocGVyIENEQyBndWlkZWxpbmVzKS4gVXNlcnMgZ2V0IHRoZSBoaWdoZXN0IGFtb3VudCBvZiBzdGVwcyBvbiBTYXR1cmRheXMsIGFuZCB0aGUgbGVhc3Qgb24gU3VuZGF5cy4gRHVyaW5nIHRoZSB3ZWVrIHRoZWlyIHN0ZXAgY291bnQgaXMgZ2VuZXJhbGx5IGxvd2VyLiANCg0KS2V5IHRha2Vhd2F5OiBUbyBoZWxwIHVzZXJzIG1lZXQgdGhlaXIgdGFyZ2V0IHN0ZXAgY291bnQsIEJlbGxhYmVhdCBjYW4gcHJvdmlkZSBub3RpZmljYXRpb25zIGFuZCByZW1pbmRlcnMgdG8gd2FsayBtb3JlIHdoZW4gaW5hY3RpdmUgYW5kIGF0IHNwZWNpZmljIHRpbWVzIGluIHRoZSBkYXkuDQoNCiMjIENhbG9yaWVzIGJ1cm5lZCANCg0KIyMjIENhbG9yaWVzIGJ1cm5lZCBieSBzdGVwcw0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDaGVja2luZyBDYWxvcmllcyBidXJuZWQgYnkgVG90YWwgU3RlcHMNCg0KZGFpbHlfYWN0aXZpdHkgJT4lIA0KICBncm91cF9ieShUb3RhbFN0ZXBzLCBDYWxvcmllcykgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBUb3RhbFN0ZXBzICwgeSA9IENhbG9yaWVzLCBjb2xvciA9IENhbG9yaWVzKSkgKw0KICBnZW9tX3BvaW50KCkgKw0KICBnZW9tX3Ntb290aCgpICsgDQogIGN1c3RvbV90aGVtZSgpICsNCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gYyguOCwgLjMpLA0KICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgxLCAibW0iKSwgDQogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BKSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5ib3guYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siKSkgKw0KICBsYWJzKHRpdGxlID0gJ0NhbG9yaWVzIGJ1cm5lZCBieSB0b3RhbCBzdGVwcyB0YWtlbicsDQogICAgICAgeSA9ICdDYWxvcmllcycsDQogICAgICAgeCA9ICdUb3RhbCBTdGVwcycsDQogICAgICAgY2FwdGlvbiA9ICdEYXRhIFNvdXJjZTogRml0Qml0IEZpdG5lc3MgVHJhY2tlciBEYXRhJykNCmBgYA0KDQpJbiB0aGUgYWJvdmUgZ3JhcGgsIHdlIGNhbiBjbGVhcmx5IHNlZSBhIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gU3RlcHMgYW5kIENhbG9yaWVzIGJ1cm5lZC4gVG8gY2hlY2sgdGhlIGNvcnJlbGF0aW9uLCB3ZSB3aWxsIHVzZSB0aGUgUGVhcnNvbiBDb3JyZWxhdGlvbiBDb2VmZmljaWVudCB3aGljaCB3aWxsIG1lYXN1cmUgdGhlIGxpbmVhciBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB0d28gdmFyaWFibGVzLg0KVG8gcmVhZCBtb3JlLCBjbGljayBbaGVyZV0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvUGVhcnNvbl9jb3JyZWxhdGlvbl9jb2VmZmljaWVudCkuDQoNCmBgYHtyfQ0KIyBSdW5uaW5nIHRoZSBQZWFyc29ucyBDb3JyZWxhdGlvbiBDb2VmZmljaWVudC4NCg0KY29yLnRlc3QoZGFpbHlfYWN0aXZpdHkkVG90YWxTdGVwcywgZGFpbHlfYWN0aXZpdHkkQ2Fsb3JpZXMsIG1ldGhvZCA9ICdwZWFyc29uJywgY29uZi5sZXZlbCA9IDAuOTUpDQpgYGANCg0KV2UgY2FuIHNlZSB0aGF0IHRoZSBjb3JyZWxhdGlvbiBpcyBhbG1vc3QgNjAlLiBUaGlzIG1lYW5zIHRoYXQgdGhlcmUgaXMgYSBzdHJvbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gc3RlcHMgYW5kIGNhbG9yaWVzIGJ1cm5lZC4gDQoNCktleSB0YWtlYXdheTogVGhlcmUgaXMgYSBzdHJvbmcgcmVsYXRpb25zaGlwIGJldHdlZW4gdG90YWwgc3RlcHMgdGFrZW4gYW5kIGNhbG9yaWVzIGJ1cm5lZC4gU2ltcGx5IHB1dCwgdGhlIG1vcmUgc3RlcHMgeW91IHRha2UgdGhlIG1vcmUgY2Fsb3JpZXMgeW91IGJ1cm4uDQoNCiMjIyBDYWxvcmllcyBCdXJuZWQgYnkgYWN0aXZpdHkgbGV2ZWwNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQ2Fsb3JpZXMgQnVybmVkIHZzLiBEYWlseSBhY3Rpdml0eSBsZXZlbA0KDQpwYXIobWZyb3cgPSBjKDIsIDIpKQ0KDQpwMSA8LSBnZ3Bsb3QoZGFpbHlfYWN0aXZpdHksIGFlcyhWZXJ5QWN0aXZlTWludXRlcywgQ2Fsb3JpZXMpKSArIGdlb21fcG9pbnQoKSArIGxhYnMoeCA9ICJIZWF2aWx5IEFjdGl2ZSIsIHkgPSAiQ2Fsb3JpZXMiLCB0aXRsZSA9ICJIZWF2eSwgTW9kZXJhdGUsIExpZ2h0IGFuZCBTZWRlbnRhcnkgQWN0aXZpdHkgdnMgQ2Fsb3JpZXMiLCApICsgdGhlbWVfbWluaW1hbCgpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikNCg0KDQpwMiA8LSBnZ3Bsb3QoZGFpbHlfYWN0aXZpdHksIGFlcyhGYWlybHlBY3RpdmVNaW51dGVzLCBDYWxvcmllcykpICsgZ2VvbV9wb2ludCgpICsgbGFicyh4ID0gIk1vZGVyYXRlbHkgQWN0aXZlIiwgeSA9ICIiLCB0aXRsZSA9ICIgIikgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSkgKyBnZW9tX3Ntb290aChtZXRob2QgPSAibG0iKQ0KDQpwMyA8LSBnZ3Bsb3QoZGFpbHlfYWN0aXZpdHksIGFlcyhMaWdodGx5QWN0aXZlTWludXRlcywgQ2Fsb3JpZXMpKSArIGdlb21fcG9pbnQoKSArIGxhYnMoeCA9ICJMaWdodGx5IEFjdGl2ZSIsIHkgPSAiIiwgdGl0bGUgPSAiICIpICsgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUoYXhpcy50ZXh0LnkgPSBlbGVtZW50X2JsYW5rKCkpICsgZ2VvbV9zbW9vdGgobWV0aG9kID0gImxtIikNCg0KcDQgPC0gZ2dwbG90KGRhaWx5X2FjdGl2aXR5LCBhZXMoU2VkZW50YXJ5TWludXRlcywgQ2Fsb3JpZXMpKSArIGdlb21fcG9pbnQoKSArIGxhYnMoeCA9ICJTZWRlbnRhcnkiLCB5ID0gIiIsIHRpdGxlID0gIiAiKSArIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGF4aXMudGV4dC55ID0gZWxlbWVudF9ibGFuaygpKSArIGdlb21fc21vb3RoKG1ldGhvZCA9ICJsbSIpDQoNCnBsb3RfZ3JpZChwMSwgcDIsIHAzLCBwNCwgbmNvbCA9IDQpICANCg0KYGBgDQoNClRoZXJlIGlzIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gYmV0d2VlbiB0aGUgdHlwZSBvZiBhY3Rpdml0eSBhbmQgdGhlIGNhbG9yaWVzIGJ1cm5lZC4gTWVhbmluZyB0aGUgbW9yZSBhY3RpdmUgeW91IGFyZSwgdGhlIG1vcmUgY2Fsb3JpZXMgeW91IGJ1cm4sIGFuZCB2aWNlIHZlcnNlLiANCg0KS2V5IHRha2Vhd2F5OiBTdGF5aW5nIGFjdGl2ZSB3aWxsIG1ha2UgYSBodWdlIGRpZmZlcmVuY2UgaW4gdGhlIGFtb3VudCBvZiBjYWxvcmllcyB5b3UgYnVybiBvbiBhIGRhaWx5IGJhc2lzLg0KDQojIyBFeGVyY2lzZQ0KDQojIyMgSW50ZW5zaXR5IG9mIGV4ZXJjaXNlIGFjdGl2aXR5DQoNCmBgYHtyIG1lc3NhZ2U9RkFMU0UsIHdhcm5pbmc9RkFMU0V9DQojIENoZWNraW5nIHVzZXJzJyBhY3Rpdml0eSBsZXZlbHMuDQoNCmRhaWx5X2FjdGl2aXR5ICU+JSANCiAgc2VsZWN0KFZlcnlBY3RpdmVEaXN0YW5jZSwgDQogICAgICAgICBNb2RlcmF0ZWx5QWN0aXZlRGlzdGFuY2UsIA0KICAgICAgICAgTGlnaHRBY3RpdmVEaXN0YW5jZSwgU2VkZW50YXJ5QWN0aXZlRGlzdGFuY2UpICU+JSANCiAgc3VtbWFyaXNlKGFjcm9zcyhldmVyeXRoaW5nKCksIGxpc3Qoc3VtKSkpICU+JSANCiAgZ2F0aGVyKGFjdGl2aXRpZXMsIHZhbHVlKSAlPiUgDQogIG11dGF0ZShyYXRpbyA9IHZhbHVlIC8gc3VtKHZhbHVlKSwNCiAgICAgICAgIGxhYmVsID0gcGVyY2VudChyYXRpbyAlPiUgcm91bmQoNCkpKSAlPiUgDQptdXRhdGUoYWN0aXZpdGllcyA9IGZhY3RvcihhY3Rpdml0aWVzLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGFiZWxzID0gYygnTGlnaHQgQWN0aXZpdHknLCdNb2RlcmF0ZSBBY3Rpdml0eScsICdTZWRlbnRhcnknLCdIZWF2eSBBY3Rpdml0eScpKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSAoYWN0aXZpdGllcyksIA0KICAgICAgICAgICAgIHkgPSB2YWx1ZSwgDQogICAgICAgICAgICAgbGFiZWwgPSBsYWJlbCwgDQogICAgICAgICAgICAgZmlsbCA9IGFjdGl2aXRpZXMpKSArDQogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JykgKw0KICBnZW9tX2xhYmVsKGFlcyhsYWJlbCA9IGxhYmVsKSwgDQogICAgICAgICAgICAgZmlsbCA9ICJiZWlnZSIsIA0KICAgICAgICAgICAgIGNvbG91ciA9ICJibGFjayIsDQogICAgICAgICAgICAgdmp1c3QgPSAwLjUpICsNCiAgY3VzdG9tX3RoZW1lKCkgKw0KICBzY2FsZV9maWxsX2JyZXdlcihwYWxldHRlPSJBY2NlbnQiKSArDQogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpICsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJJbnRlbnNpdHkgb2YgZXhlcmNpc2UgYWN0aXZpdHkiLA0KICAgIHggPSAiQWN0aXZpdHkgbGV2ZWwiLA0KICAgIHkgPSAiRGlzdGFuY2UiLA0KICAgIGNhcHRpb24gPSAnRGF0YSBTb3VyY2U6IEZpdEJpdCBGaXRuZXNzIFRyYWNrZXIgRGF0YScNCiAgKQ0KYGBgDQoNClRoZSBtb3N0IGNvbW1vbiBleGVyY2lzZSBhY3Rpdml0eSBsZXZlbCBpcyAqKmxpZ2h0KiouDQoNCktleSB0YWtlYXdheTogVXNlcnMgc2VlbSB0byBwcmVmZXIgbGlnaHRlciBhY3Rpdml0aWVzLiBBZGRpbmcgaW4tYXBwIGFydGljbGVzIG9yIGV2ZW4gYSBndWlkZWQgZXhlcmNpc2UgcHJvZ3JhbSBjb3VsZCBicmluZyBtb3JlIGVuZ2FnZW1lbnQuDQoNCiMjIyBEaXN0cmlidXRpb24gb2YgZGFpbHkgYWN0aXZpdHkgbGV2ZWwNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQ2hlY2tpbmcgdGhlIERpc3RyaWJ1dGlvbiAlIG9mIGRhaWx5IGFjdGl2aXR5IGxldmVscy4NCg0KZGFpbHlfYWN0aXZpdHkgJT4lIA0KICBzZWxlY3QoVmVyeUFjdGl2ZU1pbnV0ZXMsIA0KICAgICAgICAgRmFpcmx5QWN0aXZlTWludXRlcywgDQogICAgICAgICBMaWdodGx5QWN0aXZlTWludXRlcywgDQogICAgICAgICBTZWRlbnRhcnlNaW51dGVzKSAlPiUgDQogIHN1bW1hcmlzZShhY3Jvc3MoZXZlcnl0aGluZygpLCBsaXN0KHN1bSkpKSAlPiUgDQogIGdhdGhlcihhY3RpdmVfbGV2ZWwsIG1pbnV0ZXMpICU+JSANCiAgbXV0YXRlKGFjdGl2ZV9sZXZlbCA9IGZhY3RvcihhY3RpdmVfbGV2ZWwsIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsYWJlbHMgPSBjKCdNb2RlcmF0ZSBBY3Rpdml0eScsJ0xpZ2h0IEFjdGl2aXR5JywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnU2VkZW50YXJ5JywnSGVhdnkgQWN0aXZpdHknKSkpICU+JSANCiAgaGNoYXJ0KCdwaWUnLCBoY2Flcyh4ID0gYWN0aXZlX2xldmVsLCB5ID0gbWludXRlcykpICU+JSANCiAgaGNfdGl0bGUodGV4dCA9ICJEaXN0cmlidXRpb24gb2YgZGFpbHkgYWN0aXZpdHkgbGV2ZWwgaW4gbWludXRlcyIsDQogICAgICAgICAgIHN0eWxlID0gbGlzdChmb250RmFtaWx5ID0gIkhlbHZldGljYSIsIGZvbnRTaXplID0gIjMwcHgiKSwNCiAgICAgICAgICAgYWxpZ24gPSAiY2VudGVyIikgJT4lIA0KICBoY190b29sdGlwKHBvaW50Rm9ybWF0ID0gIjxiPlZhbHVlOjwvYj4ge3BvaW50Lnl9IDxicj4NCiAgICAgICAgICAgICAgICAgPGI+UGVyY2VudGFnZTwvYj4ge3BvaW50LnBlcmNlbnRhZ2U6LC4yZn0lIikgJT4lIA0KICBoY19jcmVkaXRzKA0KICAgIGVuYWJsZWQgPSBUUlVFLCANCiAgICB0ZXh0ID0gIjxlbT5EYXRhIFNvdXJjZTogRml0Qml0IEZpdG5lc3MgVHJhY2tlciBEYXRhPC9lbT48L3NwYW4+IiwNCiAgICBzdHlsZSA9IGxpc3QoZm9udFNpemUgPSAiMTJweCIsIGNvbG9yID0gJ2JsYWNrJyx0eXBlID0gInNwbGluZSIpDQogICAgKSANCmBgYA0KDQpBYm92ZSB3ZSBjYW4gc2VlIHRoYXQgdGhlIG1ham9yaXR5IG9mIHBlb3BsZSBhcmUgKipzZWRlbnRhcnkqKiBkdXJpbmcgdGhlIGRheS4NCg0KS2V5IHRha2Vhd2F5OiBNb3ZlbWVudCByZW1pbmRlcnMgd291bGQgaGVscCByZWR1Y2UgdGhpcyBudW1iZXIuDQoNCiMjIFNsZWVwDQoNCiMjIyBTbGVlcCBkaXN0cmlidXRpb24NCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgRGlzdHJpYnV0aW9uIHNsZWVwIHRpbWUuDQoNCmRhaWx5X3NsZWVwICU+JSANCiAgc2VsZWN0KFRvdGFsTWludXRlc0FzbGVlcCkgJT4lIA0KICBkcm9wX25hKCkgJT4lIA0KICBtdXRhdGUoc2xlZXBfcXVhbGl0eSA9IGlmZWxzZShUb3RhbE1pbnV0ZXNBc2xlZXAgPD0gNDIwLCAnTGVzcyB0aGFuIDdoJywNCiAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoVG90YWxNaW51dGVzQXNsZWVwIDw9IDU0MCwgJzdoIHRvIDloJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgJ01vcmUgdGhhbiA5aCcpKSkgJT4lDQogIG11dGF0ZShzbGVlcF9xdWFsaXR5ID0gZmFjdG9yKHNsZWVwX3F1YWxpdHksIA0KICAgICAgICAgICAgICAgICAgICAgICAgICBsZXZlbHMgPSBjKCdMZXNzIHRoYW4gN2gnLCc3aCB0byA5aCcsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgJ01vcmUgdGhhbiA5aCcpKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSBUb3RhbE1pbnV0ZXNBc2xlZXAsIGZpbGwgPSBzbGVlcF9xdWFsaXR5KSkgKw0KICBnZW9tX2hpc3RvZ3JhbShwb3NpdGlvbiA9ICdkb2RnZScsIGJpbnMgPSAzMCkgKw0KICBjdXN0b21fdGhlbWUoKSArDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJjb3JhbDEiLCAidHVycXVvaXNlNCIsICJkZWVwcGluazQiKSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSBjKC44MCwgLjgwKSwNCiAgICAgICAgbGVnZW5kLnRpdGxlID0gZWxlbWVudF9ibGFuaygpLA0KICAgICAgICBsZWdlbmQuc3BhY2luZy55ID0gdW5pdCgwLCAibW0iKSwgDQogICAgICAgIHBhbmVsLmJvcmRlciA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siLCBmaWxsPU5BKSwNCiAgICAgICAgbGVnZW5kLmJhY2tncm91bmQgPSBlbGVtZW50X2JsYW5rKCksDQogICAgICAgIGxlZ2VuZC5ib3guYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvdXIgPSAiYmxhY2siKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIlNsZWVwIGRpc3RyaWJ1dGlvbiIsDQogICAgeCA9ICJUaW1lIHNsZXB0IChtaW51dGVzKSIsDQogICAgeSA9ICJDb3VudCIsDQogICAgY2FwdGlvbiA9ICdEYXRhIFNvdXJjZTogRml0Qml0IEZpdG5lc3MgVHJhY2tlciBEYXRhJw0KICApDQpgYGANCg0KV2UgY2FuIHNlZSBieSB0aGlzIGdyYXBoIHRoYXQgdXNlcnMgc2xlZXAgYXZlcmFnZXMgdG8gYWJvdXQgMzAwIHRvIDUyMCBtaW51dGVzIChvciA1IHRvIDggaG91cnMpIG9mIHNsZWVwIGVhY2ggZGF5Lg0KDQpLZXkgdGFrZWF3YXk6IGFjY29yZGluZyB0byB0aGUgQ0RDIGd1aWRlbGluZXMsIHVzZXJzIHNob3VsZCBiZSBnZXR0aW5nIGFwcHJveGltYXRlbHkgNy05IGhvdXJzIG9mIHNsZWVwIGVhY2ggbmlnaHQuIFVzZXJzIGFyZSBub3QgcXVpdGUgbWVldGluZyB0aGlzIHJlY29tbWVuZGVkIGdvYWwuIERldmVsb3BpbmcgYW4gaW4tYXBwIHNsZWVwIHJvdXRpbmUgb3Igc2NoZWR1bGUgd291bGQgaGVscCB1c2VycyBtZWV0IHRoZWlyIHNsZWVwIGdvYWxzLiBBcnRpY2xlcyBhYm91dCBzbGVlcCByb3V0aW5lcyB3b3VsZCBiZSBoZWxwZnVsIGFzIHdlbGwuDQoNCiMjIyBTbGVlcCB2cyBkaXN0YW5jZSBjb3ZlcmVkDQoNCmBgYHtyfQ0KIyBNZXJnaW5nIGRhaWx5IGFjdGl2aXR5IGFuZCBkYWlseSBzbGVlcCBmb3Igc2xlZXAgYW5hbHlzaXMNCg0KZGFpbHlfc2xlZXBfYWN0aXZpdHkgPC0gbWVyZ2UoZGFpbHlfYWN0aXZpdHksIGRhaWx5X3NsZWVwLCBieSA9ICJJZCIpDQpgYGANCg0KDQpgYGB7ciBtZXNzYWdlPUZBTFNFLCB3YXJuaW5nPUZBTFNFfQ0KIyBDaGVja2luZyBTbGVlcCB2cy4gRGlzdGFuY2UgQ292ZXJlZC4NCg0KZGFpbHlfc2xlZXBfYWN0aXZpdHkgJT4lIA0KICBzZWxlY3QoSWQsIFRvdGFsRGlzdGFuY2UsIFRvdGFsTWludXRlc0FzbGVlcCkgJT4lIA0KICBncm91cF9ieShJZCkgJT4lIA0KICBzdW1tYXJpc2VfYWxsKGxpc3Qofm1lYW4oLiwgbmEucm09VFJVRSkpKSAlPiUgDQogIGRyb3BfbmEoKSAlPiUgDQogIG11dGF0ZShJZCA9IGZhY3RvcihJZCkpICU+JSANCiAgZ2dwbG90KCkgKw0KICBnZW9tX2JhcihhZXMoeCA9IElkLCB5ID0gVG90YWxEaXN0YW5jZSksIHN0YXQgPSAiaWRlbnRpdHkiLCBmaWxsID0gJ3N0ZWVsYmx1ZTMnLCBhbHBoYSA9IDAuNykgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gSWQsIHkgPSBUb3RhbE1pbnV0ZXNBc2xlZXAvNjApLCBjb2xvciA9ICdncmF5MjAnKSArDQogIGdlb21fc2VnbWVudChhZXMoeCA9IElkLCB4ZW5kID0gSWQsIHkgPSAwLCB5ZW5kID0gVG90YWxNaW51dGVzQXNsZWVwLzYwKSwgY29sb3IgPSAnZ3JheTIwJyAsZ3JvdXAgPSAxKSArDQogIGN1c3RvbV90aGVtZSgpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxpbWl0cz1jKDAsIDEyKSwgbmFtZSA9ICJUb3RhbCBEaXN0YW5jZSIsIA0KICAgIHNlYy5heGlzID0gc2VjX2F4aXMofi4qNjAsIG5hbWUgPSAiU2xlZXAgaW4gbWludXRlcyIpKSArDQogIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsNCiAgdGhlbWUoYXhpcy50aXRsZS55LnJpZ2h0ID0gZWxlbWVudF90ZXh0KGNvbG9yID0gImdyYXkyMCIpLCANCiAgICAgICAgYXhpcy50aWNrcy55LnJpZ2h0ID0gZWxlbWVudF9saW5lKGNvbG9yID0gImdyYXkyMCIpLA0KICAgICAgICBheGlzLnRleHQueS5yaWdodCA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmF5MjAiKSkgKw0KICBsYWJzKA0KICAgIHRpdGxlID0gIkF2ZXJhZ2UgZGlzdGFuY2UgdnMgYXZlcmFnZSBzbGVlcCBieSB1c2VyIiwNCiAgICB4ID0gIlVzZXJzIiwNCiAgICBjYXB0aW9uID0gJ0RhdGEgU291cmNlOiBGaXRCaXQgRml0bmVzcyBUcmFja2VyIERhdGEnDQogICkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgbW92aW5nIGEgZ3JlYXRlciBkaXN0YW5jZSBkb2Vzbid0IGVxdWF0ZSB0byBhIGJldHRlciBuaWdodHMgc2xlZXAgKG9uIGF2ZXJhZ2UpLiANCg0KTGV0J3MgbG9vayBpbnRvIHRoaXMgZnVydGhlciBieSBjaGVja2luZyB0aGUgc2xlZXAgcXVhbGl0eSB2cyBzdGVwIHRvdGFscy4NCg0KIyMjIFNsZWVwIHF1YWxpdHkgdnMgU3RlcCB0b3RhbHMNCg0KYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRX0NCiMgQ2hlY2tpbmcgU2xlZXAgUXVhbGl0eSB2cy4gU3RlcCBUb3RhbHMuDQoNCmRhaWx5X3NsZWVwX2FjdGl2aXR5ICU+JSANCiAgc2VsZWN0KFRvdGFsTWludXRlc0FzbGVlcCwgVG90YWxTdGVwcykgJT4lIA0KICBtdXRhdGUoc2xlZXBfcXVhbGl0eSA9IGlmZWxzZShUb3RhbE1pbnV0ZXNBc2xlZXAgPD0gNDIwLCAnTGVzcyB0aGFuIDdoJywNCiAgICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoVG90YWxNaW51dGVzQXNsZWVwIDw9IDU0MCwgJzdoIHRvIDloJywgDQogICAgICAgICAgICAgICAgICAgICAgICAgJ01vcmUgdGhhbiA5aCcpKSkgJT4lIA0KICBtdXRhdGUoYWN0aXZlX2xldmVsID0gaWZlbHNlKFRvdGFsU3RlcHMgPj0gMTUwMDAsJ01vcmUgdGhhbiAxNSwwMDAgc3RlcHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFRvdGFsU3RlcHMgPj0gMTAwMDAsJzEwLDAwMCB0byAxNCw5OTkgc3RlcHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFRvdGFsU3RlcHMgPj0gNTAwMCwgJzUsMDAwIHRvIDksOTk5IHN0ZXBzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICdMZXNzIHRoYW4gNCw5OTkgc3RlcHMnKSkpKSAlPiUgDQogIHNlbGVjdCgtYyhUb3RhbE1pbnV0ZXNBc2xlZXAsIFRvdGFsU3RlcHMpKSAlPiUgDQogIGRyb3BfbmEoKSAlPiUgDQogIGdyb3VwX2J5KHNsZWVwX3F1YWxpdHksIGFjdGl2ZV9sZXZlbCkgJT4lIA0KICBzdW1tYXJpc2UoY291bnRzID0gbigpKSAlPiUgDQogIG11dGF0ZShhY3RpdmVfbGV2ZWwgPSBmYWN0b3IoYWN0aXZlX2xldmVsLCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygnTGVzcyB0aGFuIDQsOTk5IHN0ZXBzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnNSwwMDAgdG8gOSw5OTkgc3RlcHMnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICcxMCwwMDAgdG8gMTQsOTk5IHN0ZXBzJywNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAnTW9yZSB0aGFuIDE1LDAwMCBzdGVwcycpKSkgJT4lIA0KICBtdXRhdGUoc2xlZXBfcXVhbGl0eSA9IGZhY3RvcihzbGVlcF9xdWFsaXR5LCANCiAgICAgICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzID0gYygnTGVzcyB0aGFuIDdoJywnN2ggdG8gOWgnLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICdNb3JlIHRoYW4gOWgnKSkpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gc2xlZXBfcXVhbGl0eSwgDQogICAgICAgICAgICAgeSA9IGNvdW50cywgDQogICAgICAgICAgICAgZmlsbCA9IHNsZWVwX3F1YWxpdHkpKSArDQogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArDQogIGN1c3RvbV90aGVtZSgpICsNCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPWMoImNvcmFsIiwgInR1cnF1b2lzZTMiLCAiZGVlcHBpbmszIikpICsNCiAgZmFjZXRfd3JhcCh+YWN0aXZlX2xldmVsLCBucm93ID0gMSkgKw0KICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIpICsNCiAgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSwgaGp1c3QgPSAxKSkgKw0KICB0aGVtZShzdHJpcC50ZXh0ID0gZWxlbWVudF90ZXh0KGNvbG91ciA9ICdibGFjaycsIHNpemUgPSA4KSkgKw0KICB0aGVtZShzdHJpcC5iYWNrZ3JvdW5kID0gZWxlbWVudF9yZWN0KGZpbGwgPSAiYmVpZ2UiLCBjb2xvciA9ICdibGFjaycpKSsNCiAgbGFicygNCiAgICB0aXRsZSA9ICJTbGVlcCBxdWFsaXR5IGJ5IHN0ZXBzIiwNCiAgICB4ID0gIlNsZWVwIHF1YWxpdHkiLA0KICAgIHkgPSAiQ291bnQiLA0KICAgIGNhcHRpb24gPSAnRGF0YSBTb3VyY2U6IEZpdEJpdCBGaXRuZXNzIFRyYWNrZXIgRGF0YScNCiAgKQ0KYGBgDQoNCkhlcmUgd2UgY2FuIHNlZSB0aGF0IHRoZSBiZXN0IHNsZWVwIG9uIGF2ZXJhZ2UgaXMgYWNoaWV2ZWQgd2hlbiB0aGUgdXNlcnMgdG90YWwgc2xlZXAgZG9lcyBub3QgZXhjZWVkIDksOTk5IHN0ZXBzIGEgZGF5Lg0KDQpLZXkgdGFrZWF3YXk6IFVzZXJzIHNsZWVwIHF1YWxpdHkgaXMgdGhlIGJlc3Qgd2hlbiB0aGV5IHN0YXkgd2l0aGluIHRoZWlyIHN0ZXAgZ29hbHMuDQoNCiFbXShodHRwczovL3d3dy5zbGVlcGFkdmlzb3Iub3JnL3dwLWNvbnRlbnQvdXBsb2Fkcy8yMDE5LzA0L3dvbWFuLXNsZWVwaW5nLXBlYWNlZnVsbHkuanBlZykNCg0KKioqDQoNCiMgU2hhcmUNCg0KIyMgS2V5IHRha2Vhd2F5cw0KDQpfbG9nZ2luZzpfDQoNCjEuIFVzZXJzIGFyZSBub3QgbG9nZ2luZyB0aGVpciBzbGVlcCBhbmQgd2VpZ2h0IGFzIG9mdGVuLiBQcm92aWRpbmcgdGhlIGRldmljZXMgd2l0aCBhIGZlYXR1cmUgdG8gcmVtaW5kIHVzZXJzIHRvIGxvZyBhY3Rpdml0aWVzIHdvdWxkIGJlIGhlbHBmdWwuDQoNCl9Nb3ZlbWVudDpfIA0KDQoxLiBQcm92aWRpbmcgcmVtaW5kZXJzIG9yIG5vdGlmaWNhdGlvbnMgZm9yIGluYWN0aXZpdHkgY2FuIGhlbHAgZW5jb3VyYWdlIG1vdmVtZW50IHdoZW4gdXNlcnMgaGF2ZSBiZWVuIGluYWN0aXZlIGZvciB0b28gbG9uZy4gDQoyLiBUbyBoZWxwIHVzZXJzIG1lZXQgdGhlaXIgdGFyZ2V0IHN0ZXAgY291bnQsIEJlbGxhYmVhdCBjYW4gcHJvdmlkZSBub3RpZmljYXRpb25zIGFuZCByZW1pbmRlcnMgdG8gd2FsayBtb3JlIHdoZW4gaW5hY3RpdmUgYW5kIGF0IHNwZWNpZmljIHRpbWVzIGluIHRoZSBkYXkuIA0KMy4gIEluIG9yZGVyIHRvIGNyZWF0ZSBhIGhhYml0IG9mIGV4ZXJjaXNpbmcgZXZlcnkgZGF5LCBCZWxsYWJlYXQgY291bGQgc2VuZCBhIG5vdGlmaWNhdGlvbiBhdCBhIHNwZWNpZmljIHRpbWUgZm9yIHRoZSB1c2VyIHRvIHJlbWFpbiBjb25zaXN0ZW50IHRocm91Z2hvdXQgdGhlIHdlZWsuIFJlbWluZGluZyB1c2VycyB0aGF0IGV2ZW4gbGlnaHQgYWN0aXZpdHksIGlzIHN0aWxsIGFjdGl2aXR5Lg0KDQpfQ2Fsb3JpZXM6Xw0KDQoxLiBDYWxvcmllcyBhcmUgYnVybnQgYnkgdGhlIHN0ZXBzIHRha2VuIGRhaWx5LiBCYXNlZCBvbiB1c2Vyc+KAmSBvYmplY3RpdmVzLCBCZWxsYWJlYXQgY291bGQgcmVjb21tZW5kIGEgbWluaW11bSBudW1iZXIgb2Ygc3RlcHMgZm9yIHVzZXJzIHRvIHRha2UgdG8gZW5jb3VyYWdlIHRoZW0gdG8gYWNoaWV2ZSB0aGVpciBnb2FscyB0aHJvdWdob3V0IHRoZSBkYXkuDQoNCl9BY3Rpdml0aWVzOl8gDQoNCjEuIFVzZXJzIHNlZW0gdG8gcHJlZmVyIGxpZ2h0ZXIgYWN0aXZpdGllcy4gQWRkaW5nIGluLWFwcCBhcnRpY2xlcyBvciBldmVuIGEgZ3VpZGVkIGV4ZXJjaXNlIHByb2dyYW0gY291bGQgYnJpbmcgbW9yZSBlbmdhZ2VtZW50Lg0KDQpfU2xlZXA6Xw0KDQoxLiBBY2NvcmRpbmcgdG8gdGhlIENEQyBndWlkZWxpbmVzLCB1c2VycyBzaG91bGQgYmUgZ2V0dGluZyBhcHByb3hpbWF0ZWx5IDctOSBob3VycyBvZiBzbGVlcCBlYWNoIG5pZ2h0LiBVc2VycyBhcmUgbm90IHF1aXRlIG1lZXRpbmcgdGhpcyByZWNvbW1lbmRlZCBnb2FsLiBEZXZlbG9waW5nIGFuIGluLWFwcCBzbGVlcCByb3V0aW5lIG9yIHNjaGVkdWxlIHdvdWxkIGhlbHAgdXNlcnMgbWVldCB0aGVpciBzbGVlcCBnb2Fscy4gDQoyLiBTdHVkaWVzIGhhdmUgc2hvd24gdGhhdCBhIHJvdXRpbmUgYmVmb3JlIGJlZCBjYW4gaGVscCBhY2hpZXZlIGEgYmV0dGVyIHNsZWVwLiBCZWxsYWJlYXQgY291bGQgc2VuZCBhIHJlbWluZGVyIHRvIHN0YXJ0IHRoYXQgcm91dGluZSBhdCBhIGNlcnRhaW4gdGltZSBhbmQgcmVjb21tZW5kIGFjdGl2aXRpZXMgdG8gcmVsYXggYW5kIGltcHJvdmUgc2xlZXAuIEFydGljbGVzIGFib3V0IHNsZWVwIHJvdXRpbmVzIHdvdWxkIGJlIGhlbHBmdWwgYXMgd2VsbC4NCjMuIFRoZSBkYXRhIHNob3dzIHRoYXQgaW4gb3JkZXIgdG8gc2xlZXAgYmV0dGVyIHRoZSBiZXN0IHR5cGUgb2YgZXhlcmNpc2UgaXMgbGlnaHQgdG8gbW9kZXJhdGUgKGxlc3MgdGhhbiAxMCwwMDAgc3RlcHMpLiBCZWxsYWJlYXQgY291bGQgbG9vcCB0aGlzIHJlY29tbWVuZGF0aW9uIGludG8gdGhlIGFib3ZlIHJlbWluZGVyIGJ5IGVuY291cmFnaW5nIGxpZ2h0IG1vdmVtZW50IHRoYXQgY291bGQgYmV0dGVyIHN1aXQgdGhlaXIgcGh5c2ljYWwgbmVlZHMgZHVyaW5nIHRoZSB3b3Jrd2Vlay4NCg0KX0VuZ2FnZW1lbnQ6XyANCg0KMS4gQmVsbGFiZWF0cyBtYWluIGZvY3VzIGlzIHRvIGNyZWF0ZSBtb3JlIGVuZ2FnZW1lbnQgd2l0aCB0aGVpciB1c2VycyBieSBwcm92aWRpbmcgYSBnb29kIGNvbnN1bWVyIGV4cGVyaWVuY2UuIEl0IGlzIGVhc3kgdG8gZ2FpbiBhIGZvbGxvd2luZywgYnV0IHRoZSBpZGVhIGlzIHRvIG1haW50YWluIG9uZSBhcyB3ZWxsIGFuZCBwcmV2ZW50IGFueSBkZWNyZWFzZSBpbiBtb250aGx5IHVzZXJzLiBUaGF04oCZcyB3aHkgaXQgaXMgaW1wb3J0YW50IHRvIGluZm9ybSB1c2VycyBob3cgb3VyIHNlcnZpY2VzIHdpbGwgYmVuZWZpdCB0aGVtIGluIHRoZSBsb25nIHJ1bi4gU21hbGwgdGhpbmdzIHN1Y2ggYXMgY29uZ3JhdHVsYXRpbmcgYSB1c2VyIGZvciB3YWxraW5nIGEgZmFydGhlciBkaXN0YW5jZSB0b2RheSBjb21wYXJlZCB0byB5ZXN0ZXJkYXkgd2lsbCBnbyBhIGxvbmcgd2F5Lg0KMi4gSXQgYXBwZWFycyB0aGF0IG91ciBhdWRpZW5jZSBhcmUgbW9zdGx5IGNvcnBvcmF0ZSB3b3JrZXJzIHdobyB1dGlsaXplIHRoZWlyIGx1bmNoIGFuZCBhZnRlciB3b3JrIGhvdXJzIGZvciBtb3ZlbWVudCBhbmQgZXhlcmNpc2UuIEEgcG9zc2libGUgcmVjb21tZW5kYXRpb24gd291bGQgYmUgdG8gYWRkIGFydGljbGVzIG9uIGhvdyB0byBhY2hpZXZlIG1vdmVtZW50IHdoaWxlIGF0IHdvcmsuDQozLiBBIGdyZWF0IHdheSBCZWxsYWJlYXQgY291bGQgbW90aXZhdGUgdXNlcnMgaXMgYWxzbyByZWNvbW1lbmRpbmcgc29tZSBtaW5kZnVsIGVhdGluZyBoYWJpdHMgYW5kIHJlY2lwZSBhcnRpY2xlcyB3aXRoaW4gdGhlIEJlbGxhYmVhdCBhcHAuDQo0LiBQcm92aWRpbmcgcXVhcnRlcmx5IG9yIGFubnVhbCBoZWFsdGggcmVwb3J0cyBjb3VsZCBwcm92ZSB0byBlbmNvdXJhZ2Ugb25nb2luZyB1c2VyIGVuZ2FnZW1lbnQuDQo1LiBBIHJlY29tbWVuZGVkIGRpZXQgcGxhbiBiYXNlZCBvZmYgb2YgdXNlcnMgQk1JIG9yIGhlYWx0aCBzdGF0dXMgY2FuIGhlbHAgdXNlcnMgYmUgbW9yZSBrbm93bGVkZ2VhYmxlIG9mIHRoZWlyIGhlYWx0aC4NCjYuIDEgbW9udGggZnJlZSB0cmlhbHMgY2FuIGhlbHAgbW9yZSB1c2VycyBzaWduIHVwIGZvciB0aGlzIHNlcnZpY2UuDQo3LiBFc3RhYmxpc2hpbmcgYSBzb2NpYWwgbWVkaWEgcHJlc2VuY2UgY2FuIGhlbHAgY29uc3VtZXJzIGxlYXJuIG1vcmUgYWJvdXQgd2hhdCBzZXJ2aWNlcyBCZWxsYWJlYXQgb2ZmZXJzLg0KDQoqKioNCg0KIVtdKGh0dHBzOi8vbGF3cmVuY2Vncm91cC5uZXQuYXUvd3AtY29udGVudC91cGxvYWRzLzIwMTkvMDgvU2V0dGluZy1CdXNpbmVzcy1Hb2Fscy1hbmQtb2JqZWN0aXZlcy1mb3ItMjAxOS5qcGcpDQoNCiMjIFJlY29tZW5kYXRpb25zDQoNCiogTG9nZ2luZywgaW5hY3Rpdml0eSwgc3RlcCBnb2FsLCBleGVyY2lzZSBhbmQgc2xlZXAgcm91dGluZSByZW1pbmRlcnMgYW5kIG5vdGlmaWNhdGlvbnMuDQoqIFJlY29tbWVuZGF0aW9uIG9uIG1pbmltdW0gc3RlcHMgdGhyb3VnaG91dCB0aGUgZGF5IHRvIGVuY291cmFnZSB1c2Vycy4NCiogSW4tYXBwIGFydGljbGVzIG9uIGV4ZXJjaXNlIGluY2x1ZGluZyBvbmUgc3BlY2lmaWNhbGx5IGdlYXJlZCB0b3dhcmRzIGNvcnBvcmF0ZSB3b3JrZXJzLg0KKiBJbi1hcHAgZ3VpZGVkIGV4ZXJjaXNlIHByb2dyYW0uDQoqIEluLWFwcCBzbGVlcCByb3V0aW5lcyBvciBzY2hlZHVsZXMuIA0KKiBHb29kIGNvbnN1bWVyIGV4cGVyaWVuY2VzLCBzdWNoIGFzIGNvbmdyYXR1bGF0aW5nIHVzZXJzIGZvciByZWFjaGluZyBoaWdoZXIgbWlsZXN0b25lcyB0aGFuIHRoZSBwcmV2aW91cyBkYXlzLg0KKiBRdWFydGVybHkgb3IgYW5udWFsIGhlYWx0aCByZXBvcnRzIGZvciBtZW1iZXJzLg0KKiBEaWV0IHBsYW4gYmFzZWQgb2ZmIG9mIHVzZXJzIEJNSSBvciBoZWFsdGggcmVwb3J0LiANCiogMSBtb250aCBmcmVlIHRyaWFscyB0byBicmluZyBpbiBtb3JlIGNvbnN1bWVycy4NCiogRXN0YWJsaXNoaW5nIGEgc29jaWFsIG1lZGlhIHByZXNlbmNlIHRvIGhlbHAgY29uc3VtZXJzIGxlYXJuIG1vcmUgYWJvdXQgb3VyIHNlcnZpY2VzLg0KDQoNCjxjZW50ZXI+PGZvbnQgc2l6ZT0iMyI+ICoqVGhhbmsgeW91IGZvciByZWFkaW5nLiBJZiB5b3Ugd291bGQgbGlrZSB0byB2aWV3IG1vcmUgb2YgbXkgd29yaywgeW91IGNhbiBhY2Nlc3MgbXkgcG9ydGZvbGlvIFtoZXJlXSgpLioqPC9mb250PiA8L2NlbnRlcj4=