Summary

Bellabeat is a high-tech company that manufactures health-focused smart products.They offer different smart devices that collect data on activity, sleep, stress, and reproductive health to empower women with knowledge about their own health and habits.

The main focus of this case was to analyze smart devices fitness data and determined how it could help unlock new growth opportunities for Bellabeat. We used information from FitBit-app to gain insight into how consumers use non-Bellabeat smart devices and to improve our product Leaf smart.

The FitBit Fitness Tracker Data

The FitBit Fitness Tracker dataset is a public data set(CC0: Public Domain) that is made available on Kaggle through the user Mobius.

This dataset is generated by respondents to a distributed survey via Amazon Mechanical Turk between 03.12.2016-05.12.2016 (1 month period). Thirty eligible Fitbit users consented to the submission of personal tracker data, including minute-level output for physical activity, heart rate, and sleep monitoring. Individual reports can be parsed by export session ID (column A) or timestamp (column B). Variation between output represents use of different types of Fitbit trackers and individual tracking behaviors / preferences.

The dataset is a collection of 18 .csv files. 15 in long format, 3 in wide format. The datasets consists of wide-ranging information from activity metrics, calories, sleep records, metabolic equivalent of tasks (METs), heart rate and steps; in timeframes of seconds, minutes, hours and days. There was no metadata provided.

Preview of the Data

We first want to preview some information about all the data we have, so we can establish a working plan. We are going to load all the tables and make an exploration from the structure of every table. We are going to use the readr R package to import the excel data into the r workspace. Also we are going to load some others library that will be use during the process.

#We change our system preferences to English for outputs of weekdays, months, etc.
Sys.setlocale("LC_TIME", "English")
[1] "English_United States.1252"
library(readr)
library(tidyverse)
library(janitor)
library(lubridate)
library(DataExplorer)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(VennDiagram)
Loading required package: grid
Loading required package: futile.logger

The next code import all the .csv files from a specific directory into R

filename<-list.files(path="Fitabase Data 4.12.16-5.12.16/", pattern="*.csv")

for (i in 1:length(filename)) 
  assign(filename[i], read.csv(paste("Fitabase Data 4.12.16-5.12.16/", filename[i], sep="")))

list.files(path="Fitabase Data 4.12.16-5.12.16/", pattern="*.csv")
 [1] "dailyActivity_merged.csv"           "dailyCalories_merged.csv"           "dailyIntensities_merged.csv"        "dailySteps_merged.csv"             
 [5] "heartrate_seconds_merged.csv"       "hourlyCalories_merged.csv"          "hourlyIntensities_merged.csv"       "hourlySteps_merged.csv"            
 [9] "minuteCaloriesNarrow_merged.csv"    "minuteCaloriesWide_merged.csv"      "minuteIntensitiesNarrow_merged.csv" "minuteIntensitiesWide_merged.csv"  
[13] "minuteMETsNarrow_merged.csv"        "minuteSleep_merged.csv"             "minuteStepsNarrow_merged.csv"       "minuteStepsWide_merged.csv"        
[17] "sleepDay_merged.csv"                "weightLogInfo_merged.csv"          

dailyActivity_merged.csv preview

The first exploration will be on the dailyActivity_merged file. We want to explore the structure of the data. As we can see, this table has 15 variables and a total of 940 observations, where each observations correspond for a day of a specific user (Id). The data in the file are all numerical except for the column ActivityDate, which is a chr type.

str(dailyActivity_merged.csv)
'data.frame':   940 obs. of  15 variables:
 $ Id                      : num  1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ ActivityDate            : chr  "4/12/2016" "4/13/2016" "4/14/2016" "4/15/2016" ...
 $ TotalSteps              : int  13162 10735 10460 9762 12669 9705 13019 15506 10544 9819 ...
 $ TotalDistance           : num  8.5 6.97 6.74 6.28 8.16 ...
 $ TrackerDistance         : 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 ...

dailyCalories_merged.csv preview

This table has 3 variables and a total of 940 observations, where each observations correspond for a day of a specific user (Id) and the total calories burner that day. So now we know that every table with 940 observations is a resume from each day activity from a specific user and they all join in: “dailyActivity_merged.csv”.

str(dailyCalories_merged.csv)
'data.frame':   940 obs. of  3 variables:
 $ Id         : num  1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ ActivityDay: chr  "4/12/2016" "4/13/2016" "4/14/2016" "4/15/2016" ...
 $ Calories   : int  1985 1797 1776 1745 1863 1728 1921 2035 1786 1775 ...

heartrate_seconds_merged.csv preview

This tables has a lot of observations as you can see (2483658). The first we observe is that the Id is different from the id of the others table. So we group by id identify the Ids numbers and try to compare with our previus data. We found that some Ids are missing to respect from the others tables, here we only have 14 vs the 33 on the others tables, this can be due that the individuals missing don’t have this function active on her their device due to configuration issues.

str(heartrate_seconds_merged.csv)
'data.frame':   2483658 obs. of  3 variables:
 $ Id   : num  2.02e+09 2.02e+09 2.02e+09 2.02e+09 2.02e+09 ...
 $ Time : chr  "4/12/2016 7:21:00 AM" "4/12/2016 7:21:05 AM" "4/12/2016 7:21:10 AM" "4/12/2016 7:21:20 AM" ...
 $ Value: int  97 102 105 103 101 95 91 93 94 93 ...
heartrate_seconds_merged.csv %>%
  group_by(Id) %>%
  summarise(count = n())%>%
  nrow()
[1] 14
dailyActivity_merged.csv %>%
  group_by(Id) %>%
  summarise(count = n())%>%
  nrow()
[1] 33

hourlyCalories_merged.csv preview

Now we want to undertsant the data inside the files with 22099 observations. This files contains the calories from a specific user in intervals of 1 hours for each day. So the sum of the data here for each day, should be equal to the data on the file with daily record. We gonna make one validation for the calories, and as we can see there are some incompatibiltys between the data fo the total calories on dailyActivity_merged.csv and hourlyCalories_merged.csv.

str(hourlyCalories_merged.csv)
'data.frame':   22099 obs. of  3 variables:
 $ Id          : num  1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ ActivityHour: chr  "4/12/2016 12:00:00 AM" "4/12/2016 1:00:00 AM" "4/12/2016 2:00:00 AM" "4/12/2016 3:00:00 AM" ...
 $ Calories    : int  81 61 59 47 48 48 48 47 68 141 ...
verification_hourlyCalories <- hourlyCalories_merged.csv %>%
  group_by(Id) %>%
  arrange(Id) %>%
  summarise(sum = sum(Calories))

verification_dailyActivity <- dailyActivity_merged.csv %>%
  group_by(Id) %>%
  arrange(Id) %>%
  summarise(sum = sum(Calories))

verification_dailyActivity %>%
  mutate(difference = verification_dailyActivity$sum - verification_hourlyCalories$sum) %>% 
  head()

minuteCaloriesNarrow_merged.csv preview

We want to explore where the inconsistency start. So we explore the minutes files and compare between the hours file. So we see that we have incompatibilitys also here.

verification_minuteCaloriesNarrow <- minuteCaloriesNarrow_merged.csv %>%
  group_by(Id) %>%
  arrange(Id) %>%
  summarise(sum = sum(Calories))

verification_hourlyCalories %>%
  mutate(difference = verification_minuteCaloriesNarrow $sum - verification_hourlyCalories$sum) %>% 
  head()

sleepDay_merged.csv and weightLogInfo_merged.csv preview

Finally we are going to explore the last two tables, sleepDay_merged.csv and weightLogInfo_merged.csv. We can observ that the sleepDay table have information for each day for each user id, however the rows are much less than the dailyActivity_merged.csv file, so we invistigate this descrepancy first by the number of user, and we see that some user dont apper here.

str(sleepDay_merged.csv)
'data.frame':   413 obs. of  5 variables:
 $ Id                : num  1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ SleepDay          : chr  "4/12/2016 12:00:00 AM" "4/13/2016 12:00:00 AM" "4/15/2016 12:00:00 AM" "4/16/2016 12:00:00 AM" ...
 $ TotalSleepRecords : int  1 2 1 2 1 1 1 1 1 1 ...
 $ TotalMinutesAsleep: int  327 384 412 340 700 304 360 325 361 430 ...
 $ TotalTimeInBed    : int  346 407 442 367 712 320 377 364 384 449 ...
sleepDay_merged.csv %>%
  group_by(Id)%>%
  arrange(Id)%>%
  summarise(count = n())%>%
  nrow()
[1] 24

For the weightLogInfo_merged.csv we observe that only 8 users are on the report.

str(weightLogInfo_merged.csv)
'data.frame':   67 obs. of  8 variables:
 $ Id            : num  1.50e+09 1.50e+09 1.93e+09 2.87e+09 2.87e+09 ...
 $ Date          : chr  "5/2/2016 11:59:59 PM" "5/3/2016 11:59:59 PM" "4/13/2016 1:08:52 AM" "4/21/2016 11:59:59 PM" ...
 $ WeightKg      : num  52.6 52.6 133.5 56.7 57.3 ...
 $ WeightPounds  : num  116 116 294 125 126 ...
 $ Fat           : int  22 NA NA NA NA 25 NA NA NA NA ...
 $ BMI           : num  22.6 22.6 47.5 21.5 21.7 ...
 $ IsManualReport: chr  "True" "True" "False" "True" ...
 $ LogId         : num  1.46e+12 1.46e+12 1.46e+12 1.46e+12 1.46e+12 ...
unique(weightLogInfo_merged.csv[c("Id")])%>% 
  nrow()
[1] 8

Not keys was found about information such as: participants demographic, age, gender, weather indicators. Unfortunately, this associated with the small sample size would limit the scope of analysis that can be performed.

Cleaning and Formatting Data-sets

We are going to combine all the data from dailyActivity_merged.csv, sleepDay_merged.csv and weightLogInfo_merged.csv in a single data-set. First we are going to Cleaning and Formatting the Data-sets. We are going to make all the variables lowercase through the function clean_names() from the jupiter library. Also we are going to change the name of all the dates variables to the name date in all tables, and finally change the format of dates to year-month-day.

dailyActivity_clean <- dailyActivity_merged.csv %>%
 clean_names() %>%
 rename(date = activity_date)%>%
 mutate(date = as.Date(date, format = "%m/%d/%Y"))

sleepDay_clean <- sleepDay_merged.csv %>%
 clean_names() %>%
 rename(date = sleep_day)%>%
 mutate(date = as.Date(date, format = "%m/%d/%Y"))


weightLogInfo_clean <- weightLogInfo_merged.csv %>%
 rename(date = Date)%>%
 clean_names() %>%
 mutate(date = as.Date(date, format = "%m/%d/%Y"))

Now we are going to prepare the data for a merge/join between tables, so we need to clean the data from any duplicate and null value.

sum(duplicated(dailyActivity_clean))
[1] 0
sum(is.na (dailyActivity_clean))
[1] 0
sum(duplicated(sleepDay_clean))
[1] 3
sum(is.na (sleepDay_clean))
[1] 0
sum(duplicated(weightLogInfo_clean))
[1] 0
sum(is.na (weightLogInfo_clean))
[1] 65

So we have found that sleepDay_clean have duplicate values and there are Null values in weightLogInfo_clean, however this are only for one column (fat), so we are only going to clean for the duplicates.

sleepDay_clean <- sleepDay_clean %>%
  distinct()

Finally we are going to merge all the data in one data-frame and change the format of id from numeric to string for classify each user as a categorie.

dailyActivity_join <- dailyActivity_clean %>%
  left_join(sleepDay_clean, by = c("id", "date")) %>%
  left_join(., weightLogInfo_clean, by = c("id", "date")) 

#now we change the data type for the id column

dailyActivity_join$id <- as.character(dailyActivity_join$id)

head(dailyActivity_join)

We also are going to use the data on hourlyCalories_merged.csv, hourlyIntensities_merged.csv and hourlySteps_merged.csv. We are just going to review for any duplicate.

sum(duplicated(hourlyCalories_merged.csv))
[1] 0
sum(duplicated(hourlyIntensities_merged.csv))
[1] 0
sum(duplicated(hourlySteps_merged.csv))
[1] 0

Now we are going to format the hours and also clean the names.

hourlyCalories_clean <- hourlyCalories_merged.csv %>%
 clean_names() %>%
 rename(date_time = activity_hour)%>%
 mutate(date_time = mdy_hms(hourlyCalories_merged.csv$ActivityHour))

hourlyIntensities_clean <- hourlyIntensities_merged.csv %>%
 clean_names() %>%
 rename(date_time = activity_hour)%>%
 mutate(date_time = mdy_hms(hourlyCalories_merged.csv$ActivityHour))


hourlySteps_clean <- hourlySteps_merged.csv %>%
 rename(date_time = ActivityHour)%>%
 clean_names() %>%
 mutate(date_time = mdy_hms(hourlyCalories_merged.csv$ActivityHour))

Since we not found any duplicate, we are going to merge all the data in one single file hourlyActivity_join

hourlyActivity_join <- hourlyCalories_clean %>%
  inner_join(hourlyIntensities_clean, by = c("id", "date_time"))%>%
  inner_join(.,hourlySteps_clean, by = c("id", "date_time"))

#We also going to separate the date form the hour for management facility
  hourlyActivity_join <- hourlyActivity_join %>%
  separate(date_time, into = c("date", "time"), sep= " ")%>%
  
#and we going to change the format of the hour to only show hour and minute
  mutate(time = format(parse_date_time(as.character(time), "HMS"), format = "%H:%M"))

#now we change the data type for the id column
hourlyActivity_join$id <- as.character(hourlyActivity_join$id)

head(hourlyActivity_join)

Since we already merge our maindataframes, we can drop all the others files form the R environment (for performance and cleanliness).

#First we list all the dataframes we have to visualizate them
ls()
 [1] "avg_income_year"                    "dailyActivity_clean"                "dailyActivity_join"                 "dailyActivity_merged.csv"          
 [5] "dailyCalories_merged.csv"           "dailyIntensities_merged.csv"        "dailySteps_merged.csv"              "filename"                          
 [9] "filterdata"                         "gss"                                "heartrate_seconds_merged.csv"       "hourlyActivity_join"               
[13] "hourlyCalories_clean"               "hourlyCalories_merged.csv"          "hourlyIntensities_clean"            "hourlyIntensities_merged.csv"      
[17] "hourlySteps_clean"                  "hourlySteps_merged.csv"             "i"                                  "minuteCaloriesNarrow_merged.csv"   
[21] "minuteCaloriesWide_merged.csv"      "minuteIntensitiesNarrow_merged.csv" "minuteIntensitiesWide_merged.csv"   "minuteMETsNarrow_merged.csv"       
[25] "minuteSleep_merged.csv"             "minuteStepsNarrow_merged.csv"       "minuteStepsWide_merged.csv"         "sleepDay_clean"                    
[29] "sleepDay_merged.csv"                "verification_dailyActivity"         "verification_hourlyCalories"        "verification_minuteCaloriesNarrow" 
[33] "weightLogInfo_clean"                "weightLogInfo_merged.csv"          
#Now we drop all dataframes except  the ones we create and will use on the future.
rm(list=setdiff(ls(), c("dailyActivity_join", 'hourlyActivity_join', 'dailyActivity_clean', 'sleepDay_clean', 'weightLogInfo_clean', 'heartrate_seconds_merged.csv')))

Finally, in reality we are not going to use all columns in dailyActivity_join, so we can drop some columns (for performance and cleanliness).

dailyActivity_join <- dailyActivity_join %>% 
  select(-c(total_distance,
            tracker_distance,
            logged_activities_distance, 
            very_active_distance, 
            moderately_active_distance, 
            light_active_distance,
            sedentary_active_distance, 
            total_sleep_records,  
            total_time_in_bed,  
            weight_kg,
            weight_pounds,
            fat,
            bmi,
            is_manual_report,
            log_id))

Normality Analyze of data frames

Here we are going to investigate the normality of the numerical data, to know more about the limitations about our data. Lest start with the variables inside dailyActivity_join:

#Here we going to use the library  DataExplorer, since our data frame have some categorical variables and will be difficult to make a loop for ggplot2.
dailyActivity_join %>%
  plot_histogram( 
    ncol = 3,
    ggtheme = theme_light()
    )

We can see that some variables have near a normal behavior with little skew or abnormally values. i.e. calories, total_minutes_aesleep, lightly_active_minutes and others have a strong right skewed distributions i.e. fairly_active_minutes and very_active_minutes.

Now we are analyze the data inside hourlyActivity_join:

hourlyActivity_join %>%
  plot_histogram( 
    ncol = 3,
    ggtheme = theme_light()
    )

Here we can see that all variables are right skewed. This is related to fact that most of the hours the people are going to be working or sleeping, and since the intensity is low is normal to have a skewed plot for the calories.

Data analyze

Distribution of the tracking of the devices

We are ready to make some questions from our Data. The first question we want to investigate is:

  • Which is the distribution of the usage of the apps on the differents activities?

We already know the answer to this question thanks to the initial exploration we did. We have 33 user that use her device to track her daily activity, 24 users that track her sleep behavior, 8 users that tracks her weight loss/gain and 14 users that track her heart rate. So let put this information on a plot.

# We are going to plot a Venn diagram between the 4 file dailyActivity_clean, sleepDay_clean, weightLogInfo_clean and heartrate_seconds_merged.csv
#First we need to create the sets. We are going to create for each dataframe a set of unique Ids.

step_ids <- unique(dailyActivity_clean$id, incomparables = FALSE)
sleep_ids <- unique(sleepDay_clean$id, incomparables = FALSE)
heartrate_ids <- unique(heartrate_seconds_merged.csv$Id, incomparables = FALSE)
weight_ids <- unique(weightLogInfo_clean$id, incomparables = FALSE)

#now we create the graph, Frist we need a list vector.
x <- list(A=step_ids, B=sleep_ids, C=heartrate_ids, D=weight_ids)

#function to display Venn diagram inside markdown, for this we need to call the library VennDiagram
display_venn <- function(x, ...){
  grid.newpage()
  venn_object <- venn.diagram(x, filename = NULL, ...)
  grid.draw(venn_object)
}

#display Venn diagram
display_venn(
  x,
  category.names = c("Steps count", "Sleep monitor", "Heart monitor", "Weight tracking"),
  fill = c("#999999", "#E69F00", "#56B4E9", "#009E73")
  )

Type of users per activity level

Here we will ascertain how often the participants use their smart devices. With daily_activity, we will assume that days with < 200 TotalSteps taken, are days where users have not used their watches. We will filter out these inactive day and assign the following designations:

  • Low Use - 1 to 5 days
  • Moderate Use - 5 to 20 days
  • High Use - 21 to 31 days

Breaking down the analysis further in this way will help us understand the different trends underlying each Usage Groups.

#Here we create a table to classify the users according to the times they appear in the data frame
dailyActivity_join %>%
  filter(total_steps > 200) %>%
  group_by(id) %>%
  summarize(count = n()) %>%
  mutate(usage =  ifelse(count <= 5,  "Low use", 
                        ifelse(count <= 20,  "Moderate use", 
                        ifelse(count <= 31,  "High Use", NA))))%>%

#We will now create a percentage data frame to better visualize the results in the graph. We are also ordering our usage levels.
#the :: here call the library scales to use the function percent, since we only usign once, we dont need to load the library.
  group_by(usage) %>%
  summarise(total = n()) %>%
  mutate(perc = total/sum(total))%>%
  mutate(perc = scales::percent(perc)) %>% 

#Now that we have our new table we can create our plot.
  ggplot(aes(x = "", y = total, fill = usage )) +
    geom_bar(stat='identity', width = 1) +
    coord_polar("y", start=0)+
    theme_void()+
    theme(plot.title = element_text(hjust = 0.5, vjust= -5, size = 20, face = "bold")) +
    geom_text(aes(label = perc, x = 1.25),position = position_stack(vjust = 0.5)) +
    labs(title = "Usage Group Distribution") +
    guides(fill = guide_legend(title = "Usage Type"))

Analyzing our results we can see that 63.6% of the users of our sample use their device frequently almost very day - between 25 to 31 days, 27.3% use their device 15 to 25 days. 6.1% of our sample use their device between 5 to 15 days and 3.0% use their devices very rarely.

Time used smart device and distribution

We will analyse the steps taken by users within and between groups per day and hour. Lets start with the daily steps for each user between groups.

#here we create a new column on our data frame with the classification we did before, since we are going to need it for the rest of the analyzes.
dailyActivity_join <- dailyActivity_join %>%
  filter(total_steps > 200) %>%
  group_by(id) %>%
  mutate(count = n()) %>%
  mutate(usage =  ifelse(count <= 5,  "Low use",
                        ifelse(count <= 20,  "Moderate use",
                        ifelse(count <= 31,  "High use", NA))), groups="drop") %>%

#We are going to organize the level in the order we want they appear on the plots.
 mutate(usage = factor(usage, level = c('Low use','Moderate use','High use'))) %>% 
  
#As we group and apply this to our main dataframe, we need to ungroup or we are going to get all values of summarize function grouping by id.
 ungroup(id)
dailyActivity_join %>%
  ggplot(aes(x = date, y = total_steps, group = id, color = id)) +
    geom_line() +
    theme(legend.position = "none")+
    facet_wrap(~usage, ncol = 1)

There is not specific trend here, since some very High use users have some days with low total steps. Now we are going to plot the average steps by day of each group.

dailyActivity_join %>%
  group_by(usage, date) %>% 
  summarize(average_steps = mean(total_steps)) %>%
  ggplot(aes(x = date, y = average_steps, fill = usage,  color = usage)) + 
     geom_col()+
     facet_wrap(~usage)
`summarise()` has grouped output by 'usage'. You can override using the `.groups` argument.

Now we going to visualizate this on a better manner trough a boxplot diagram.

dailyActivity_join %>%
  group_by(usage, date) %>% 
  summarize(average_steps = mean(total_steps)) %>%
  ggplot(aes(x = usage, y = average_steps, fill = usage,  color = usage)) + 
     geom_boxplot()
`summarise()` has grouped output by 'usage'. You can override using the `.groups` argument.

Finally, we are going to plot the average use of the devices per week day.

#First we create and column that containt each weekday
dailyActivity_join %>%
  mutate(weekday = weekdays(as.Date(date)), 
         weekday = fct_relevel(weekday, c("Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"))) %>% 

#Now we group by usage and weekday, get the average, the confidence interval and finally we plot.
  group_by(weekday, usage) %>% 
  summarize(average_steps = mean(total_steps), ci = qt(0.975, n())*sd(total_steps)/sqrt(n()))%>%
  ggplot(aes(x = weekday, y = average_steps, fill = usage,  color = usage)) +
     geom_col()+
#code for add intervals of confidence  
     #geom_errorbar(aes(ymin =  average_steps - ci, ymax =  average_steps + ci), width = 0.2, colour = 'black') +
     facet_wrap(~usage, ncol=1)
`summarise()` has grouped output by 'weekday'. You can override using the `.groups` argument.

We can see some patrons from our data:

  • Average steps per day increases as usage of devices increases, we are going to invistigate more on this in the next section.

  • For moderate and high use users, there is not a clear day that show a higher mean than the other days (is necessary to do a t-test, however you need to be aware that data is not independent within groups and between groups).

  • Low use users (1 individue) does not seem to display any difference on the mean against the moderate use users.

Usage during the day (a more in deep analysis)

Now that we have some trends of usage, we want to the distribution of usage during the day of the devices, and how this is correlate to some activities. For this we are going to be working with the hourlyActivity_join table. The first we are going to investigate is the distribution usage of the devices during each day of the week for each group.

#Since this is other data frame, we need to make the classification again, first we sum the values of total steps per day to filter bt values > 200 on the other step
hourlyActivity_join <- hourlyActivity_join %>%
  group_by(id, day(date)) %>%
  rename(day = "day(date)") %>%
  mutate(total_steps = sum(step_total)) %>% 
  ungroup(id, day)
#now we sum the days a user use the devices and make the categorization.
hourlyActivity_join <- hourlyActivity_join %>%
  filter(total_steps > 200) %>%
  group_by(id) %>% 
  mutate(days_usage = n_distinct(day(date))) %>% 
  mutate(usage =  ifelse(days_usage <= 5,  "Low use", 
                        ifelse(days_usage <= 20,  "Moderate use", 
                        ifelse(days_usage  <= 31,  "High use", NA)))) %>% 
#We are going to organize the level in the order we want they appear on the plots.
  mutate(usage = factor(usage, level = c('Low use','Moderate use','High use'))) %>% 
  ungroup(id)
#Now we plot
hourlyActivity_join  %>%
  mutate(weekday = format(ymd(date), format = '%a'), 
         weekday = fct_relevel(weekday, c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"))) %>%
  group_by(time, weekday, usage) %>% 
  summarize(average_steps = mean(step_total)) %>% 
  ggplot(aes(x = time, y = average_steps, fill = average_steps)) +
    viridis::scale_fill_viridis(option = "D")+
    geom_col()+
    facet_grid(usage~weekday)+
    theme(axis.text.x = element_text(size = 5, angle = 90))
`summarise()` has grouped output by 'time', 'weekday'. You can override using the `.groups` argument.

#We also going to make a heat plot for the same distribution to have other options for presentation.
hourlyActivity_join %>%
  mutate(weekday = format(ymd(date), format = '%a'), 
         weekday = fct_relevel(weekday, c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"))) %>%
  group_by(weekday,time, usage) %>% 
  summarize(average_steps = mean(step_total)) %>% 
  ggplot(aes(x = time, y = weekday, fill = average_steps)) +
    viridis::scale_fill_viridis(option = "D")+
    geom_tile()+
    geom_text(aes(label = round(average_steps, digits = 0)), color = "black", size = 2.0) +
    facet_wrap(~usage, ncol=1)+
    theme(axis.text.x = element_text(size = 5, angle = 90))
`summarise()` has grouped output by 'weekday', 'time'. You can override using the `.groups` argument.

We can see some patrons from our data:

  • The high use users start their day an hour earlier (6:00AM) compared to other groups and end her day and hour later (22:00 PM). During the weekdays the peaks are between, 5:00 to 8:00 PM, suggesting habitual excercise as work ends.

  • Moderate Use users display peaks in their steps the Saturdays and Sundays, between 8:00 AM to 12:00 PM.

More specfic questions about the data

As we see on the last part, there are some hours where the users have some peaks, we want to investigate is this is related with Exercise sessions (we can go to gym and just do weight or we can spend some time doing cardio on a treadmill). For this we are going to investigate the intensity variable and we want to response some questions:

  • What are the relation between intensity and average steps?
#First we are going to plot the distribution of intensity between the days.
hourlyActivity_join %>%
  mutate(weekday = format(ymd(date), format = '%a'), 
         weekday = fct_relevel(weekday, c("Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"))) %>%
  group_by(time, weekday, usage) %>% 
  summarize(average_intensity_hour = mean(average_intensity))%>% 
  ggplot(aes(x = time, y = average_intensity_hour, fill = average_intensity_hour)) +
    viridis::scale_fill_viridis(option = "inferno")+
    geom_col()+
    facet_grid(usage~weekday)+
    theme(axis.text.x = element_text(size = 5, angle = 90))
`summarise()` has grouped output by 'time', 'weekday'. You can override using the `.groups` argument.

We can see that the plots are very similar between average_intensity and the average_steps per group. So we will expect a linear correlation between both of this variables.

hourlyActivity_join %>%
  mutate(weekday = weekdays(as.Date(date))) %>%
  group_by(time, weekday, usage) %>% 
  summarize(average_intensity_hour = mean(average_intensity), average_steps = mean(step_total) )%>% 
  ggplot(aes(x = average_intensity_hour, y = average_steps)) +
    geom_point()+
    geom_smooth()+
    facet_wrap(~usage)
`summarise()` has grouped output by 'time', 'weekday'. You can override using the `.groups` argument.

So as we expected, we have a positive correlation (almost linear) between the average intensity per hour and average steps per hour. So we can associate the high steps to sessions of exercise where the users is very active. Lets also investigate the correlation between the variables and average calories burner.

hourlyActivity_join %>%
  mutate(weekday = weekdays(as.Date(date))) %>%
  group_by(time, weekday, usage) %>% 
  summarize(average_intensity_hour = mean(average_intensity), average_steps = mean(step_total), average_calories = mean(calories)) %>% 
  GGally::ggpairs(columns = c(4,5,6))
`summarise()` has grouped output by 'time', 'weekday'. You can override using the `.groups` argument.

We can see here a strong correlation between this variables. This is expected, since as we saw before, the high steps are generally associated with high intensity exercise sessions, where the user will tend to burn more calories.

What about sleep behaviur?

Another variable will be interesting to analyze is the sleep behavior.We want to investigate how is the sleep behavior from users according to their active level.

  • What are the relation between active level and sleep hours?
#First we are going to plot the distribution of sleep between the groups.
dailyActivity_join %>%
  group_by(date, usage) %>% 
  summarize(average_sleep_minutes = mean(total_minutes_asleep, na.rm=TRUE)) %>% 
  ggplot(aes(x = usage, y = average_sleep_minutes, fill = usage)) +
    geom_boxplot()
`summarise()` has grouped output by 'date'. You can override using the `.groups` argument.

Here we see that the users have almost the same mean, independent from there usage group, the missing value on the los use group is due that the only user we have on this group don’t have any data about his sleep behavior. We are going to going more in deep making a classification for the time slept:

  • Bad sleep - slept less than 300 minutes
  • Normal Sleep - slept between 300 and 480 minutes
  • Over Sleep - slept more than 480 minutes
dailyActivity_join <- dailyActivity_join %>%
  mutate(sleep_type =  ifelse(total_minutes_asleep<= 300,  "Bad sleep", 
                       ifelse(total_minutes_asleep <= 480,  "Normal sleep", 
                       ifelse(total_minutes_asleep > 480,  "Over sleep", NA))), 
         sleep_type = factor(sleep_type, level = c('Bad sleep','Normal sleep','Over sleep'))) 
dailyActivity_join %>%
  group_by(sleep_type, id) %>%
  summarize(count_sleep = n()) %>%
  drop_na() %>%
  summarize(total_sleep_type = n())  %>% 
  mutate(perc = total_sleep_type/sum(total_sleep_type))%>%
  mutate(perc = scales::percent(perc)) %>% 
  ggplot(aes(x = "", y = total_sleep_type, fill = sleep_type)) +
    geom_bar(stat='identity', width = 1) +
    coord_polar("y", start=0)+
    theme_void()+
    theme(plot.title = element_text(hjust = 0.5, vjust= -5, size = 20, face = "bold")) +
    geom_text(aes(label = perc, x = 1.2),position = position_stack(vjust = 0.5)) +
    labs(title = "Sleep Type Distribution") +
    guides(fill = guide_legend(title = "sleep Type"))
`summarise()` has grouped output by 'sleep_type'. You can override using the `.groups` argument.

#We can also visualizate this distribution through the different Usage groups.
dailyActivity_join %>%
  group_by(usage, sleep_type, id) %>%
  summarize(count_sleep = n()) %>%
  drop_na() %>%
  summarize(total_sleep_type = n())  %>% 
  mutate(perc = total_sleep_type/sum(total_sleep_type))%>%
  mutate(perc = scales::percent(perc)) %>%
  ggplot(aes(x = "", y = total_sleep_type, fill = sleep_type)) +
    geom_bar(stat='identity', width = 1, position = "fill") +
    coord_polar("y", start=0)+
    theme_void()+
    theme(plot.title = element_text(hjust = 0.5, vjust= 5, size = 20, face = "bold")) +
    geom_text(aes(label = perc, x=1.2), position = position_fill(vjust = 0.5)) +
    labs(title = "Sleep Type Distribution") +
    guides(fill = guide_legend(title = "Sleep Type"))+
    facet_wrap(~usage, strip.position = "bottom")
`summarise()` has grouped output by 'usage', 'sleep_type'. You can override using the `.groups` argument.`summarise()` has grouped output by 'usage'. You can override using the `.groups` argument.

Analyzing our results we can see that 26.4% of the times of user reports a bad sleep, 35.8% of the times they have a normal sleep and 35.8% of the times the over sleep. Through the groups we can see that the distribution is near similar to the global. Note that an user can have one day of each category.

Finally, we are going to relate the sleep behavior against the active level, And we are going to classify our users according to their mean active level. This classification will be based on the average active level of each user against the average active level of all users i.e. if an user has her sedentary average greater than the global sedentary average, this user will be classificate as sedentary. Finally if an user isnt in any categorie, we will exclude from the data.

#we need to make a classification for the active level of the users. First we are going to get the average of all users.  
#And we are going to drop the 0 values making them NA values and ignoring them on the mean calculation
temp <- dailyActivity_join %>%
  na_if(0) %>% 
  mutate(sedentary_minutes_avg = mean(sedentary_minutes, na.rm = TRUE), 
            lightly_active_minutes_avg = mean(lightly_active_minutes, na.rm = TRUE),
            fairly_active_minutes_avg = mean(fairly_active_minutes, na.rm = TRUE),
            very_active_minutes_avg = mean(very_active_minutes, na.rm = TRUE)) %>% 

#We are going to replace NA values with 0 to avoid errors in our categorization. After we gonna make the classifiaction using the statement case_when
  mutate(sedentary_minutes = replace(sedentary_minutes,is.na(sedentary_minutes),0),
        lightly_active_minutes = replace(lightly_active_minutes,is.na(lightly_active_minutes),0),
        fairly_active_minutes = replace(fairly_active_minutes,is.na(fairly_active_minutes),0),
        very_active_minutes = replace( very_active_minutes,is.na( very_active_minutes),0)) %>% 
  mutate(active_type = factor(case_when(sedentary_minutes > sedentary_minutes_avg &
                                 lightly_active_minutes <  lightly_active_minutes_avg &
                                 fairly_active_minutes< fairly_active_minutes_avg &
                                 very_active_minutes < very_active_minutes_avg ~ "Sedentary",
                                 lightly_active_minutes >  lightly_active_minutes_avg &
                                 fairly_active_minutes < fairly_active_minutes_avg &
                                 very_active_minutes < very_active_minutes_avg ~ "Lightly Active",
                                 fairly_active_minutes > fairly_active_minutes_avg &
                                 very_active_minutes < very_active_minutes_avg ~ 'Fairly Active',
                                 very_active_minutes > very_active_minutes_avg ~ 'Very Active'), 
                      levels=c("Sedentary", "Lightly Active", "Fairly Active", "Very Active")))%>%
  drop_na(sleep_type, active_type) 
  
#finally we plot.

temp %>% 
    ggplot(aes(x = active_type, fill = sleep_type)) +
    geom_bar(position = "fill") +
    labs(y = "Proportion")


temp %>% 
    ggplot(aes(x = active_type, fill = sleep_type)) +
    geom_bar(position = "fill") +
    labs(y = "Proportion")+
    facet_wrap(~usage) 

Analyzing our results we can see that Sedentary people tend to have a bad sleep behavior. We can also observe that a little activity on the day will tend to a normal sleep. Also as active level increase the oversleep behavior decreace,

Discussion

The FitBit data set confirms that not all users fully utilize the functions of their devices/trackers. All 33 unique IDs used the step count function. 24/33 unique IDs used the sleep tracking function. 14/33 unique IDs used the heart-rate tracking. 8/33 unique IDs used their devices to track their weight.

High Use Group

This group consists of 24 users or 73% of the total sample size, and wears the device regularly between 22-31 days. This is the most active group, and also the most varied in the types of exercises carried out. Varying from light, to fairly and very active forms of exercises. They tends to be active throughout the week with an average of weekly steps of 9054.500. Their sleep behaviur is symyetrical distribuitde between bad, normal and over sleep behavior.

Moderate Use Group

This group consists of 8 users or 24% of the total sample size, and wears the device between 5 - 21 days. Users in this group are less active and walk fewer steps compared to the ‘High Use’ group over the weekdays but active during the weekends, between 08:00AM to 1:00PM. While significantly less active than the ‘High Use’ group, they also stick to their routine and their sleep behaviur is also symyetrical distribuitde between bad, normal and over sleep behavior.

Low Use Group

This group consists of only 1 user or 3% of the sample size, too small to provide any meaningful analysis. Much of the trends are skewed away from any recognizable patterns. With this in mind, this group displays a similar behaviur to the moderate users group. Not sleep behaviur was registered form this user.

final remarks

Bellabeat’s mission is to empower women by providing them with the data to discover themselves.

In order for us to respond to our business task and help Bellabeat on their mission, based on our results, I would advice to use own tracking data for further analysis. Datasets used have a small sample and can be biased since we didn’t have any demographic details of users. Knowing that our main target are young and adult women I would encourage to continue finding trends to be able to create a marketing stragety focused on them.

That being said, after our analysis we have found different trends that may help our online campaign and improve Bellabeat app:

1. Daily notification for exercise: We classified users into 3 categories and saw that the average of users sleep less than 8 hours a day. However we saw a better beahiur on sleep habit when user increment her activity level. We can encourage customers to reach at least daily recommended steps by sending them alarms if they haven’t reached the steps and creating also posts on our app explaining the benefits of reaching that goal.

2. Notification and sleep techniques: In order to reduce the bad sleep and over sleep behaviur, users could set up a desired time to go to sleep and receive a notification minutes before to prepare to sleep. Also offer helpful resources to help customers sleep - ex. breathing advises, podcasts with relaxing music, sleep techniques.

3. Technical support: Base on the the distributions, we found that many user don’t use all the functionality of the devices. Bellabeats can offer helpfull resources and reminders to help customers configurate their devices and get all the benefits form their pruchase.

#export data to excel for some manuals verifications.
write.table(x = dailyActivity_join, file = "my_file.csv")
LS0tDQp0aXRsZTogIkJlbGxhYmVhdCBDYXNlIFN0dWR5OiBHb29nbGUgRGF0YSBBbmFseXRpYyINCm91dHB1dDogDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB0cnVlDQogICAgdGhlbWU6IHVuaXRlZA0KLS0tDQoNCiMgU3VtbWFyeQ0KDQpbQmVsbGFiZWF0XShodHRwczovL2JlbGxhYmVhdC5jb20vKSBpcyBhIGhpZ2gtdGVjaCBjb21wYW55IHRoYXQgbWFudWZhY3R1cmVzIGhlYWx0aC1mb2N1c2VkIHNtYXJ0IHByb2R1Y3RzLlRoZXkgb2ZmZXIgZGlmZmVyZW50IHNtYXJ0IGRldmljZXMgdGhhdCBjb2xsZWN0IGRhdGEgb24gYWN0aXZpdHksIHNsZWVwLCBzdHJlc3MsIGFuZCByZXByb2R1Y3RpdmUgaGVhbHRoIHRvIGVtcG93ZXIgd29tZW4gd2l0aCBrbm93bGVkZ2UgYWJvdXQgdGhlaXIgb3duIGhlYWx0aCBhbmQgaGFiaXRzLg0KDQpUaGUgbWFpbiBmb2N1cyBvZiB0aGlzIGNhc2Ugd2FzIHRvIGFuYWx5emUgc21hcnQgZGV2aWNlcyBmaXRuZXNzIGRhdGEgYW5kIGRldGVybWluZWQgaG93IGl0IGNvdWxkIGhlbHAgdW5sb2NrIG5ldyBncm93dGggb3Bwb3J0dW5pdGllcyBmb3IgQmVsbGFiZWF0LiBXZSB1c2VkIGluZm9ybWF0aW9uIGZyb20gRml0Qml0LWFwcCB0byBnYWluIGluc2lnaHQgaW50byBob3cgY29uc3VtZXJzIHVzZSBub24tQmVsbGFiZWF0IHNtYXJ0IGRldmljZXMgYW5kIHRvIGltcHJvdmUgb3VyIHByb2R1Y3QgKipMZWFmIHNtYXJ0KiouIA0KDQojIyBUaGUgRml0Qml0IEZpdG5lc3MgVHJhY2tlciBEYXRhDQoNCltUaGUgRml0Qml0IEZpdG5lc3MgVHJhY2tlciBkYXRhc2V0XShodHRwczovL3d3dy5rYWdnbGUuY29tL2RhdGFzZXRzL2FyYXNobmljL2ZpdGJpdCkgaXMgYSBwdWJsaWMgZGF0YSBzZXQoQ0MwOiBQdWJsaWMgRG9tYWluKSB0aGF0IGlzIG1hZGUgYXZhaWxhYmxlIG9uIEthZ2dsZSB0aHJvdWdoIHRoZSB1c2VyIE1vYml1cy4NCg0KVGhpcyBkYXRhc2V0IGlzIGdlbmVyYXRlZCBieSByZXNwb25kZW50cyB0byBhIGRpc3RyaWJ1dGVkIHN1cnZleSB2aWEgQW1hem9uIE1lY2hhbmljYWwgVHVyayBiZXR3ZWVuIDAzLjEyLjIwMTYtMDUuMTIuMjAxNiAoMSBtb250aCBwZXJpb2QpLiBUaGlydHkgZWxpZ2libGUgRml0Yml0IHVzZXJzIGNvbnNlbnRlZCB0byB0aGUgc3VibWlzc2lvbiBvZiBwZXJzb25hbCB0cmFja2VyIGRhdGEsIGluY2x1ZGluZyBtaW51dGUtbGV2ZWwgb3V0cHV0IGZvciBwaHlzaWNhbCBhY3Rpdml0eSwgaGVhcnQgcmF0ZSwgYW5kIHNsZWVwIG1vbml0b3JpbmcuIEluZGl2aWR1YWwgcmVwb3J0cyBjYW4gYmUgcGFyc2VkIGJ5IGV4cG9ydCBzZXNzaW9uIElEIChjb2x1bW4gQSkgb3IgdGltZXN0YW1wIChjb2x1bW4gQikuIFZhcmlhdGlvbiBiZXR3ZWVuIG91dHB1dCByZXByZXNlbnRzIHVzZSBvZiBkaWZmZXJlbnQgdHlwZXMgb2YgRml0Yml0IHRyYWNrZXJzIGFuZCBpbmRpdmlkdWFsIHRyYWNraW5nIGJlaGF2aW9ycyAvIHByZWZlcmVuY2VzLg0KDQpUaGUgZGF0YXNldCBpcyBhIGNvbGxlY3Rpb24gb2YgMTggLmNzdiBmaWxlcy4gMTUgaW4gbG9uZyBmb3JtYXQsIDMgaW4gd2lkZSBmb3JtYXQuIFRoZSBkYXRhc2V0cyBjb25zaXN0cyBvZiB3aWRlLXJhbmdpbmcgaW5mb3JtYXRpb24gZnJvbSBhY3Rpdml0eSBtZXRyaWNzLCBjYWxvcmllcywgc2xlZXAgcmVjb3JkcywgbWV0YWJvbGljIGVxdWl2YWxlbnQgb2YgdGFza3MgKE1FVHMpLCBoZWFydCByYXRlIGFuZCBzdGVwczsgaW4gdGltZWZyYW1lcyBvZiBzZWNvbmRzLCBtaW51dGVzLCBob3VycyBhbmQgZGF5cy4gVGhlcmUgd2FzIG5vIG1ldGFkYXRhIHByb3ZpZGVkLiANCg0KIyBQcmV2aWV3IG9mIHRoZSBEYXRhIA0KDQpXZSBmaXJzdCB3YW50IHRvIHByZXZpZXcgc29tZSBpbmZvcm1hdGlvbiBhYm91dCBhbGwgdGhlIGRhdGEgd2UgaGF2ZSwgc28gd2UgY2FuIGVzdGFibGlzaCBhIHdvcmtpbmcgcGxhbi4gV2UgYXJlIGdvaW5nIHRvIGxvYWQgYWxsIHRoZSB0YWJsZXMgYW5kIG1ha2UgYW4gZXhwbG9yYXRpb24gZnJvbSB0aGUgc3RydWN0dXJlIG9mIGV2ZXJ5IHRhYmxlLiBXZSBhcmUgZ29pbmcgdG8gdXNlIHRoZSAqcmVhZHIqIFIgcGFja2FnZSB0byBpbXBvcnQgdGhlIGV4Y2VsIGRhdGEgaW50byB0aGUgciB3b3Jrc3BhY2UuIEFsc28gd2UgYXJlIGdvaW5nIHRvIGxvYWQgc29tZSBvdGhlcnMgbGlicmFyeSB0aGF0IHdpbGwgYmUgdXNlIGR1cmluZyB0aGUgcHJvY2Vzcy4NCg0KYGBge3J9DQojV2UgY2hhbmdlIG91ciBzeXN0ZW0gcHJlZmVyZW5jZXMgdG8gRW5nbGlzaCBmb3Igb3V0cHV0cyBvZiB3ZWVrZGF5cywgbW9udGhzLCBldGMuDQpTeXMuc2V0bG9jYWxlKCJMQ19USU1FIiwgIkVuZ2xpc2giKQ0KYGBgDQoNCmBgYHtyfQ0KbGlicmFyeShyZWFkcikNCmxpYnJhcnkodGlkeXZlcnNlKQ0KbGlicmFyeShqYW5pdG9yKQ0KbGlicmFyeShsdWJyaWRhdGUpDQpsaWJyYXJ5KERhdGFFeHBsb3JlcikNCmxpYnJhcnkoVmVubkRpYWdyYW0pDQpgYGANCg0KVGhlIG5leHQgY29kZSBpbXBvcnQgYWxsIHRoZSAqLmNzdiogZmlsZXMgZnJvbSBhIHNwZWNpZmljIGRpcmVjdG9yeSBpbnRvIFINCg0KYGBge3J9DQpmaWxlbmFtZTwtbGlzdC5maWxlcyhwYXRoPSJGaXRhYmFzZSBEYXRhIDQuMTIuMTYtNS4xMi4xNi8iLCBwYXR0ZXJuPSIqLmNzdiIpDQoNCmZvciAoaSBpbiAxOmxlbmd0aChmaWxlbmFtZSkpIA0KICBhc3NpZ24oZmlsZW5hbWVbaV0sIHJlYWQuY3N2KHBhc3RlKCJGaXRhYmFzZSBEYXRhIDQuMTIuMTYtNS4xMi4xNi8iLCBmaWxlbmFtZVtpXSwgc2VwPSIiKSkpDQoNCmxpc3QuZmlsZXMocGF0aD0iRml0YWJhc2UgRGF0YSA0LjEyLjE2LTUuMTIuMTYvIiwgcGF0dGVybj0iKi5jc3YiKQ0KYGBgDQoNCioqZGFpbHlBY3Rpdml0eV9tZXJnZWQuY3N2IHByZXZpZXcqKg0KDQpUaGUgZmlyc3QgZXhwbG9yYXRpb24gd2lsbCBiZSBvbiB0aGUgKipkYWlseUFjdGl2aXR5X21lcmdlZCoqIGZpbGUuIFdlIHdhbnQgdG8gZXhwbG9yZSB0aGUgc3RydWN0dXJlIG9mIHRoZSBkYXRhLiBBcyB3ZSBjYW4gc2VlLCB0aGlzIHRhYmxlIGhhcyAxNSB2YXJpYWJsZXMgYW5kIGEgdG90YWwgb2YgOTQwIG9ic2VydmF0aW9ucywgd2hlcmUgZWFjaCBvYnNlcnZhdGlvbnMgY29ycmVzcG9uZCBmb3IgYSBkYXkgb2YgYSBzcGVjaWZpYyB1c2VyIChJZCkuIFRoZSBkYXRhIGluIHRoZSBmaWxlIGFyZSBhbGwgbnVtZXJpY2FsIGV4Y2VwdCBmb3IgdGhlIGNvbHVtbiBBY3Rpdml0eURhdGUsIHdoaWNoIGlzIGEgKmNociogdHlwZS4gDQoNCmBgYHtyfQ0Kc3RyKGRhaWx5QWN0aXZpdHlfbWVyZ2VkLmNzdikNCmBgYA0KDQoqKmRhaWx5Q2Fsb3JpZXNfbWVyZ2VkLmNzdiBwcmV2aWV3KioNCg0KVGhpcyB0YWJsZSBoYXMgMyB2YXJpYWJsZXMgYW5kIGEgdG90YWwgb2YgOTQwIG9ic2VydmF0aW9ucywgd2hlcmUgZWFjaCBvYnNlcnZhdGlvbnMgY29ycmVzcG9uZCBmb3IgYSBkYXkgb2YgYSBzcGVjaWZpYyB1c2VyIChJZCkgYW5kIHRoZSB0b3RhbCBjYWxvcmllcyBidXJuZXIgdGhhdCBkYXkuIFNvIG5vdyB3ZSBrbm93IHRoYXQgZXZlcnkgdGFibGUgd2l0aCA5NDAgb2JzZXJ2YXRpb25zIGlzIGEgcmVzdW1lIGZyb20gZWFjaCBkYXkgYWN0aXZpdHkgZnJvbSBhIHNwZWNpZmljIHVzZXIgYW5kIHRoZXkgYWxsIGpvaW4gaW46ICJkYWlseUFjdGl2aXR5X21lcmdlZC5jc3YiLg0KDQpgYGB7cn0NCnN0cihkYWlseUNhbG9yaWVzX21lcmdlZC5jc3YpDQpgYGANCg0KKipoZWFydHJhdGVfc2Vjb25kc19tZXJnZWQuY3N2IHByZXZpZXcqKg0KDQpUaGlzIHRhYmxlcyBoYXMgYSBsb3Qgb2Ygb2JzZXJ2YXRpb25zIGFzIHlvdSBjYW4gc2VlICgyNDgzNjU4KS4gVGhlIGZpcnN0IHdlIG9ic2VydmUgaXMgdGhhdCB0aGUgSWQgaXMgZGlmZmVyZW50IGZyb20gdGhlIGlkIG9mIHRoZSBvdGhlcnMgdGFibGUuIFNvIHdlIGdyb3VwIGJ5IGlkIGlkZW50aWZ5IHRoZSBJZHMgbnVtYmVycyBhbmQgdHJ5IHRvIGNvbXBhcmUgd2l0aCBvdXIgcHJldml1cyBkYXRhLiBXZSBmb3VuZCB0aGF0IHNvbWUgSWRzIGFyZSBtaXNzaW5nIHRvIHJlc3BlY3QgZnJvbSB0aGUgb3RoZXJzIHRhYmxlcywgaGVyZSB3ZSBvbmx5IGhhdmUgMTQgdnMgdGhlIDMzIG9uIHRoZSBvdGhlcnMgdGFibGVzLCB0aGlzIGNhbiBiZSBkdWUgdGhhdCB0aGUgaW5kaXZpZHVhbHMgbWlzc2luZyBkb24ndCBoYXZlIHRoaXMgZnVuY3Rpb24gYWN0aXZlIG9uIGhlciB0aGVpciBkZXZpY2UgZHVlIHRvIGNvbmZpZ3VyYXRpb24gaXNzdWVzLg0KDQpgYGB7cn0NCnN0cihoZWFydHJhdGVfc2Vjb25kc19tZXJnZWQuY3N2KQ0KYGBgDQoNCmBgYHtyfQ0KaGVhcnRyYXRlX3NlY29uZHNfbWVyZ2VkLmNzdiAlPiUNCiAgZ3JvdXBfYnkoSWQpICU+JQ0KICBzdW1tYXJpc2UoY291bnQgPSBuKCkpJT4lDQogIG5yb3coKQ0KDQpkYWlseUFjdGl2aXR5X21lcmdlZC5jc3YgJT4lDQogIGdyb3VwX2J5KElkKSAlPiUNCiAgc3VtbWFyaXNlKGNvdW50ID0gbigpKSU+JQ0KICBucm93KCkNCmBgYA0KDQoqKmhvdXJseUNhbG9yaWVzX21lcmdlZC5jc3YgcHJldmlldyoqDQoNCk5vdyB3ZSB3YW50IHRvIHVuZGVydHNhbnQgdGhlIGRhdGEgaW5zaWRlIHRoZSBmaWxlcyB3aXRoIDIyMDk5IG9ic2VydmF0aW9ucy4gVGhpcyBmaWxlcyBjb250YWlucyB0aGUgY2Fsb3JpZXMgZnJvbSBhIHNwZWNpZmljIHVzZXIgaW4gaW50ZXJ2YWxzIG9mIDEgaG91cnMgZm9yIGVhY2ggZGF5LiBTbyB0aGUgc3VtIG9mIHRoZSBkYXRhIGhlcmUgZm9yIGVhY2ggZGF5LCBzaG91bGQgYmUgZXF1YWwgdG8gdGhlIGRhdGEgb24gdGhlIGZpbGUgd2l0aCBkYWlseSByZWNvcmQuIFdlIGdvbm5hIG1ha2Ugb25lIHZhbGlkYXRpb24gZm9yIHRoZSBjYWxvcmllcywgYW5kIGFzIHdlIGNhbiBzZWUgdGhlcmUgIGFyZSBzb21lIGluY29tcGF0aWJpbHR5cyBiZXR3ZWVuIHRoZSBkYXRhIGZvIHRoZSB0b3RhbCBjYWxvcmllcyBvbiAqZGFpbHlBY3Rpdml0eV9tZXJnZWQuY3N2KiBhbmQgKmhvdXJseUNhbG9yaWVzX21lcmdlZC5jc3YqLg0KDQpgYGB7cn0NCnN0cihob3VybHlDYWxvcmllc19tZXJnZWQuY3N2KQ0KYGBgDQpgYGB7cn0NCnZlcmlmaWNhdGlvbl9ob3VybHlDYWxvcmllcyA8LSBob3VybHlDYWxvcmllc19tZXJnZWQuY3N2ICU+JQ0KICBncm91cF9ieShJZCkgJT4lDQogIGFycmFuZ2UoSWQpICU+JQ0KICBzdW1tYXJpc2Uoc3VtID0gc3VtKENhbG9yaWVzKSkNCg0KdmVyaWZpY2F0aW9uX2RhaWx5QWN0aXZpdHkgPC0gZGFpbHlBY3Rpdml0eV9tZXJnZWQuY3N2ICU+JQ0KICBncm91cF9ieShJZCkgJT4lDQogIGFycmFuZ2UoSWQpICU+JQ0KICBzdW1tYXJpc2Uoc3VtID0gc3VtKENhbG9yaWVzKSkNCg0KdmVyaWZpY2F0aW9uX2RhaWx5QWN0aXZpdHkgJT4lDQogIG11dGF0ZShkaWZmZXJlbmNlID0gdmVyaWZpY2F0aW9uX2RhaWx5QWN0aXZpdHkkc3VtIC0gdmVyaWZpY2F0aW9uX2hvdXJseUNhbG9yaWVzJHN1bSkgJT4lIA0KICBoZWFkKCkNCmBgYA0KDQoqKm1pbnV0ZUNhbG9yaWVzTmFycm93X21lcmdlZC5jc3YgcHJldmlldyoqDQoNCldlIHdhbnQgdG8gZXhwbG9yZSB3aGVyZSB0aGUgaW5jb25zaXN0ZW5jeSBzdGFydC4gU28gd2UgZXhwbG9yZSB0aGUgbWludXRlcyBmaWxlcyBhbmQgY29tcGFyZSBiZXR3ZWVuIHRoZSBob3VycyBmaWxlLiBTbyB3ZSBzZWUgdGhhdCB3ZSBoYXZlIGluY29tcGF0aWJpbGl0eXMgYWxzbyBoZXJlLg0KDQpgYGB7cn0NCnZlcmlmaWNhdGlvbl9taW51dGVDYWxvcmllc05hcnJvdyA8LSBtaW51dGVDYWxvcmllc05hcnJvd19tZXJnZWQuY3N2ICU+JQ0KICBncm91cF9ieShJZCkgJT4lDQogIGFycmFuZ2UoSWQpICU+JQ0KICBzdW1tYXJpc2Uoc3VtID0gc3VtKENhbG9yaWVzKSkNCg0KdmVyaWZpY2F0aW9uX2hvdXJseUNhbG9yaWVzICU+JQ0KICBtdXRhdGUoZGlmZmVyZW5jZSA9IHZlcmlmaWNhdGlvbl9taW51dGVDYWxvcmllc05hcnJvdyAkc3VtIC0gdmVyaWZpY2F0aW9uX2hvdXJseUNhbG9yaWVzJHN1bSkgJT4lIA0KICBoZWFkKCkNCmBgYA0KDQoqKnNsZWVwRGF5X21lcmdlZC5jc3YgYW5kIHdlaWdodExvZ0luZm9fbWVyZ2VkLmNzdiBwcmV2aWV3KiogDQoNCkZpbmFsbHkgd2UgYXJlIGdvaW5nIHRvIGV4cGxvcmUgdGhlIGxhc3QgdHdvIHRhYmxlcywgKnNsZWVwRGF5X21lcmdlZC5jc3YqIGFuZCAqd2VpZ2h0TG9nSW5mb19tZXJnZWQuY3N2Ki4gV2UgY2FuIG9ic2VydiB0aGF0IHRoZSBzbGVlcERheSB0YWJsZSBoYXZlIGluZm9ybWF0aW9uIGZvciBlYWNoIGRheSBmb3IgZWFjaCB1c2VyIGlkLCBob3dldmVyIHRoZSByb3dzIGFyZSBtdWNoIGxlc3MgdGhhbiB0aGUgKmRhaWx5QWN0aXZpdHlfbWVyZ2VkLmNzdiogZmlsZSwgc28gd2UgaW52aXN0aWdhdGUgdGhpcyBkZXNjcmVwYW5jeSBmaXJzdCBieSB0aGUgbnVtYmVyIG9mIHVzZXIsIGFuZCB3ZSBzZWUgdGhhdCBzb21lIHVzZXIgZG9udCBhcHBlciBoZXJlLg0KDQpgYGB7cn0NCnN0cihzbGVlcERheV9tZXJnZWQuY3N2KQ0KYGBgDQoNCmBgYHtyfQ0Kc2xlZXBEYXlfbWVyZ2VkLmNzdiAlPiUNCiAgZ3JvdXBfYnkoSWQpJT4lDQogIGFycmFuZ2UoSWQpJT4lDQogIHN1bW1hcmlzZShjb3VudCA9IG4oKSklPiUNCiAgbnJvdygpDQpgYGANCg0KRm9yIHRoZSAqd2VpZ2h0TG9nSW5mb19tZXJnZWQuY3N2KiB3ZSBvYnNlcnZlIHRoYXQgb25seSA4IHVzZXJzIGFyZSBvbiB0aGUgcmVwb3J0Lg0KDQpgYGB7cn0NCnN0cih3ZWlnaHRMb2dJbmZvX21lcmdlZC5jc3YpDQpgYGANCg0KYGBge3J9DQp1bmlxdWUod2VpZ2h0TG9nSW5mb19tZXJnZWQuY3N2W2MoIklkIildKSU+JSANCiAgbnJvdygpDQpgYGANCg0KTm90IGtleXMgd2FzIGZvdW5kIGFib3V0IGluZm9ybWF0aW9uIHN1Y2ggYXM6IHBhcnRpY2lwYW50cyBkZW1vZ3JhcGhpYywgYWdlLCBnZW5kZXIsIHdlYXRoZXIgaW5kaWNhdG9ycy4gVW5mb3J0dW5hdGVseSwgdGhpcyBhc3NvY2lhdGVkIHdpdGggdGhlIHNtYWxsIHNhbXBsZSBzaXplIHdvdWxkIGxpbWl0IHRoZSBzY29wZSBvZiBhbmFseXNpcyB0aGF0IGNhbiBiZSBwZXJmb3JtZWQuDQoNCiMjIENsZWFuaW5nIGFuZCBGb3JtYXR0aW5nIERhdGEtc2V0cw0KDQpXZSBhcmUgZ29pbmcgdG8gY29tYmluZSBhbGwgdGhlIGRhdGEgZnJvbSAqZGFpbHlBY3Rpdml0eV9tZXJnZWQuY3N2KiwgKnNsZWVwRGF5X21lcmdlZC5jc3YqIGFuZCAqd2VpZ2h0TG9nSW5mb19tZXJnZWQuY3N2KiBpbiBhIHNpbmdsZSBkYXRhLXNldC4gRmlyc3Qgd2UgYXJlIGdvaW5nIHRvIENsZWFuaW5nIGFuZCBGb3JtYXR0aW5nIHRoZSBEYXRhLXNldHMuIFdlIGFyZSBnb2luZyB0byBtYWtlIGFsbCB0aGUgdmFyaWFibGVzIGxvd2VyY2FzZSB0aHJvdWdoIHRoZSBmdW5jdGlvbiAqY2xlYW5fbmFtZXMoKSogZnJvbSB0aGUgKipqdXBpdGVyIGxpYnJhcnkqKi4gQWxzbyB3ZSBhcmUgZ29pbmcgdG8gY2hhbmdlIHRoZSBuYW1lIG9mIGFsbCB0aGUgZGF0ZXMgdmFyaWFibGVzIHRvIHRoZSBuYW1lICoqZGF0ZSoqIGluIGFsbCB0YWJsZXMsIGFuZCBmaW5hbGx5IGNoYW5nZSB0aGUgZm9ybWF0IG9mIGRhdGVzIHRvIHllYXItbW9udGgtZGF5Lg0KDQpgYGB7cn0NCmRhaWx5QWN0aXZpdHlfY2xlYW4gPC0gZGFpbHlBY3Rpdml0eV9tZXJnZWQuY3N2ICU+JQ0KIGNsZWFuX25hbWVzKCkgJT4lDQogcmVuYW1lKGRhdGUgPSBhY3Rpdml0eV9kYXRlKSU+JQ0KIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlLCBmb3JtYXQgPSAiJW0vJWQvJVkiKSkNCg0Kc2xlZXBEYXlfY2xlYW4gPC0gc2xlZXBEYXlfbWVyZ2VkLmNzdiAlPiUNCiBjbGVhbl9uYW1lcygpICU+JQ0KIHJlbmFtZShkYXRlID0gc2xlZXBfZGF5KSU+JQ0KIG11dGF0ZShkYXRlID0gYXMuRGF0ZShkYXRlLCBmb3JtYXQgPSAiJW0vJWQvJVkiKSkNCg0KDQp3ZWlnaHRMb2dJbmZvX2NsZWFuIDwtIHdlaWdodExvZ0luZm9fbWVyZ2VkLmNzdiAlPiUNCiByZW5hbWUoZGF0ZSA9IERhdGUpJT4lDQogY2xlYW5fbmFtZXMoKSAlPiUNCiBtdXRhdGUoZGF0ZSA9IGFzLkRhdGUoZGF0ZSwgZm9ybWF0ID0gIiVtLyVkLyVZIikpDQpgYGANCg0KTm93IHdlIGFyZSBnb2luZyB0byBwcmVwYXJlIHRoZSBkYXRhIGZvciBhIG1lcmdlL2pvaW4gYmV0d2VlbiB0YWJsZXMsIHNvIHdlIG5lZWQgdG8gY2xlYW4gdGhlIGRhdGEgZnJvbSBhbnkgZHVwbGljYXRlIGFuZCBudWxsIHZhbHVlLg0KDQpgYGB7cn0NCnN1bShkdXBsaWNhdGVkKGRhaWx5QWN0aXZpdHlfY2xlYW4pKQ0Kc3VtKGlzLm5hIChkYWlseUFjdGl2aXR5X2NsZWFuKSkNCg0Kc3VtKGR1cGxpY2F0ZWQoc2xlZXBEYXlfY2xlYW4pKQ0Kc3VtKGlzLm5hIChzbGVlcERheV9jbGVhbikpDQoNCnN1bShkdXBsaWNhdGVkKHdlaWdodExvZ0luZm9fY2xlYW4pKQ0Kc3VtKGlzLm5hICh3ZWlnaHRMb2dJbmZvX2NsZWFuKSkNCmBgYA0KDQpTbyB3ZSBoYXZlIGZvdW5kIHRoYXQgc2xlZXBEYXlfY2xlYW4gaGF2ZSBkdXBsaWNhdGUgdmFsdWVzIGFuZCB0aGVyZSBhcmUgTnVsbCB2YWx1ZXMgaW4gd2VpZ2h0TG9nSW5mb19jbGVhbiwgaG93ZXZlciB0aGlzIGFyZSBvbmx5IGZvciBvbmUgY29sdW1uIChmYXQpLCBzbyB3ZSBhcmUgb25seSBnb2luZyB0byBjbGVhbiBmb3IgdGhlIGR1cGxpY2F0ZXMuDQoNCmBgYHtyfQ0Kc2xlZXBEYXlfY2xlYW4gPC0gc2xlZXBEYXlfY2xlYW4gJT4lDQogIGRpc3RpbmN0KCkNCmBgYA0KDQpGaW5hbGx5IHdlIGFyZSBnb2luZyB0byBtZXJnZSBhbGwgdGhlIGRhdGEgaW4gb25lIGRhdGEtZnJhbWUgYW5kIGNoYW5nZSB0aGUgZm9ybWF0IG9mIGlkIGZyb20gbnVtZXJpYyB0byBzdHJpbmcgZm9yIGNsYXNzaWZ5IGVhY2ggdXNlciBhcyBhIGNhdGVnb3JpZS4NCg0KYGBge3J9DQpkYWlseUFjdGl2aXR5X2pvaW4gPC0gZGFpbHlBY3Rpdml0eV9jbGVhbiAlPiUNCiAgbGVmdF9qb2luKHNsZWVwRGF5X2NsZWFuLCBieSA9IGMoImlkIiwgImRhdGUiKSkgJT4lDQogIGxlZnRfam9pbiguLCB3ZWlnaHRMb2dJbmZvX2NsZWFuLCBieSA9IGMoImlkIiwgImRhdGUiKSkgDQoNCiNub3cgd2UgY2hhbmdlIHRoZSBkYXRhIHR5cGUgZm9yIHRoZSBpZCBjb2x1bW4NCg0KZGFpbHlBY3Rpdml0eV9qb2luJGlkIDwtIGFzLmNoYXJhY3RlcihkYWlseUFjdGl2aXR5X2pvaW4kaWQpDQoNCmhlYWQoZGFpbHlBY3Rpdml0eV9qb2luKQ0KYGBgDQoNCldlIGFsc28gYXJlIGdvaW5nIHRvIHVzZSB0aGUgZGF0YSBvbiAqaG91cmx5Q2Fsb3JpZXNfbWVyZ2VkLmNzdiosICpob3VybHlJbnRlbnNpdGllc19tZXJnZWQuY3N2KiBhbmQgKmhvdXJseVN0ZXBzX21lcmdlZC5jc3YqLiBXZSBhcmUganVzdCBnb2luZyB0byByZXZpZXcgZm9yIGFueSBkdXBsaWNhdGUuDQoNCmBgYHtyfQ0Kc3VtKGR1cGxpY2F0ZWQoaG91cmx5Q2Fsb3JpZXNfbWVyZ2VkLmNzdikpDQoNCnN1bShkdXBsaWNhdGVkKGhvdXJseUludGVuc2l0aWVzX21lcmdlZC5jc3YpKQ0KDQpzdW0oZHVwbGljYXRlZChob3VybHlTdGVwc19tZXJnZWQuY3N2KSkNCmBgYA0KDQpOb3cgd2UgYXJlIGdvaW5nIHRvIGZvcm1hdCB0aGUgaG91cnMgYW5kIGFsc28gY2xlYW4gdGhlIG5hbWVzLg0KDQpgYGB7cn0NCmhvdXJseUNhbG9yaWVzX2NsZWFuIDwtIGhvdXJseUNhbG9yaWVzX21lcmdlZC5jc3YgJT4lDQogY2xlYW5fbmFtZXMoKSAlPiUNCiByZW5hbWUoZGF0ZV90aW1lID0gYWN0aXZpdHlfaG91ciklPiUNCiBtdXRhdGUoZGF0ZV90aW1lID0gbWR5X2htcyhob3VybHlDYWxvcmllc19tZXJnZWQuY3N2JEFjdGl2aXR5SG91cikpDQoNCmhvdXJseUludGVuc2l0aWVzX2NsZWFuIDwtIGhvdXJseUludGVuc2l0aWVzX21lcmdlZC5jc3YgJT4lDQogY2xlYW5fbmFtZXMoKSAlPiUNCiByZW5hbWUoZGF0ZV90aW1lID0gYWN0aXZpdHlfaG91ciklPiUNCiBtdXRhdGUoZGF0ZV90aW1lID0gbWR5X2htcyhob3VybHlDYWxvcmllc19tZXJnZWQuY3N2JEFjdGl2aXR5SG91cikpDQoNCg0KaG91cmx5U3RlcHNfY2xlYW4gPC0gaG91cmx5U3RlcHNfbWVyZ2VkLmNzdiAlPiUNCiByZW5hbWUoZGF0ZV90aW1lID0gQWN0aXZpdHlIb3VyKSU+JQ0KIGNsZWFuX25hbWVzKCkgJT4lDQogbXV0YXRlKGRhdGVfdGltZSA9IG1keV9obXMoaG91cmx5Q2Fsb3JpZXNfbWVyZ2VkLmNzdiRBY3Rpdml0eUhvdXIpKQ0KYGBgDQoNClNpbmNlIHdlIG5vdCBmb3VuZCBhbnkgZHVwbGljYXRlLCB3ZSBhcmUgZ29pbmcgdG8gbWVyZ2UgYWxsIHRoZSBkYXRhIGluIG9uZSBzaW5nbGUgZmlsZSAqaG91cmx5QWN0aXZpdHlfam9pbioNCg0KYGBge3J9DQpob3VybHlBY3Rpdml0eV9qb2luIDwtIGhvdXJseUNhbG9yaWVzX2NsZWFuICU+JQ0KICBpbm5lcl9qb2luKGhvdXJseUludGVuc2l0aWVzX2NsZWFuLCBieSA9IGMoImlkIiwgImRhdGVfdGltZSIpKSU+JQ0KICBpbm5lcl9qb2luKC4saG91cmx5U3RlcHNfY2xlYW4sIGJ5ID0gYygiaWQiLCAiZGF0ZV90aW1lIikpDQoNCiNXZSBhbHNvIGdvaW5nIHRvIHNlcGFyYXRlIHRoZSBkYXRlIGZvcm0gdGhlIGhvdXIgZm9yIG1hbmFnZW1lbnQgZmFjaWxpdHkNCiAgaG91cmx5QWN0aXZpdHlfam9pbiA8LSBob3VybHlBY3Rpdml0eV9qb2luICU+JQ0KICBzZXBhcmF0ZShkYXRlX3RpbWUsIGludG8gPSBjKCJkYXRlIiwgInRpbWUiKSwgc2VwPSAiICIpJT4lDQogIA0KI2FuZCB3ZSBnb2luZyB0byBjaGFuZ2UgdGhlIGZvcm1hdCBvZiB0aGUgaG91ciB0byBvbmx5IHNob3cgaG91ciBhbmQgbWludXRlDQogIG11dGF0ZSh0aW1lID0gZm9ybWF0KHBhcnNlX2RhdGVfdGltZShhcy5jaGFyYWN0ZXIodGltZSksICJITVMiKSwgZm9ybWF0ID0gIiVIOiVNIikpDQoNCiNub3cgd2UgY2hhbmdlIHRoZSBkYXRhIHR5cGUgZm9yIHRoZSBpZCBjb2x1bW4NCmhvdXJseUFjdGl2aXR5X2pvaW4kaWQgPC0gYXMuY2hhcmFjdGVyKGhvdXJseUFjdGl2aXR5X2pvaW4kaWQpDQoNCmhlYWQoaG91cmx5QWN0aXZpdHlfam9pbikNCmBgYA0KDQpTaW5jZSB3ZSBhbHJlYWR5IG1lcmdlIG91ciBtYWluZGF0YWZyYW1lcywgd2UgY2FuIGRyb3AgYWxsIHRoZSBvdGhlcnMgZmlsZXMgZm9ybSB0aGUgUiBlbnZpcm9ubWVudCAoZm9yIHBlcmZvcm1hbmNlIGFuZCBjbGVhbmxpbmVzcykuDQoNCmBgYHtyfQ0KI0ZpcnN0IHdlIGxpc3QgYWxsIHRoZSBkYXRhZnJhbWVzIHdlIGhhdmUgdG8gdmlzdWFsaXphdGUgdGhlbQ0KbHMoKQ0KYGBgDQoNCg0KYGBge3J9DQojTm93IHdlIGRyb3AgYWxsIGRhdGFmcmFtZXMgZXhjZXB0ICB0aGUgb25lcyB3ZSBjcmVhdGUgYW5kIHdpbGwgdXNlIG9uIHRoZSBmdXR1cmUuDQpybShsaXN0PXNldGRpZmYobHMoKSwgYygiZGFpbHlBY3Rpdml0eV9qb2luIiwgJ2hvdXJseUFjdGl2aXR5X2pvaW4nLCAnZGFpbHlBY3Rpdml0eV9jbGVhbicsICdzbGVlcERheV9jbGVhbicsICd3ZWlnaHRMb2dJbmZvX2NsZWFuJywgJ2hlYXJ0cmF0ZV9zZWNvbmRzX21lcmdlZC5jc3YnKSkpDQpgYGANCg0KRmluYWxseSwgaW4gcmVhbGl0eSB3ZSBhcmUgbm90IGdvaW5nIHRvIHVzZSBhbGwgY29sdW1ucyBpbiAqKmRhaWx5QWN0aXZpdHlfam9pbioqLCBzbyB3ZSBjYW4gZHJvcCBzb21lIGNvbHVtbnMgKGZvciBwZXJmb3JtYW5jZSBhbmQgY2xlYW5saW5lc3MpLg0KDQpgYGB7cn0NCmRhaWx5QWN0aXZpdHlfam9pbiA8LSBkYWlseUFjdGl2aXR5X2pvaW4gJT4lIA0KICBzZWxlY3QoLWModG90YWxfZGlzdGFuY2UsDQogICAgICAgICAgICB0cmFja2VyX2Rpc3RhbmNlLA0KICAgICAgICAgICAgbG9nZ2VkX2FjdGl2aXRpZXNfZGlzdGFuY2UsIA0KICAgICAgICAgICAgdmVyeV9hY3RpdmVfZGlzdGFuY2UsIA0KICAgICAgICAgICAgbW9kZXJhdGVseV9hY3RpdmVfZGlzdGFuY2UsIA0KICAgICAgICAgICAgbGlnaHRfYWN0aXZlX2Rpc3RhbmNlLA0KICAgICAgICAgICAgc2VkZW50YXJ5X2FjdGl2ZV9kaXN0YW5jZSwgDQogICAgICAgICAgICB0b3RhbF9zbGVlcF9yZWNvcmRzLCAgDQogICAgICAgICAgICB0b3RhbF90aW1lX2luX2JlZCwgIA0KICAgICAgICAgICAgd2VpZ2h0X2tnLA0KICAgICAgICAgICAgd2VpZ2h0X3BvdW5kcywNCiAgICAgICAgICAgIGZhdCwNCiAgICAgICAgICAgIGJtaSwNCiAgICAgICAgICAgIGlzX21hbnVhbF9yZXBvcnQsDQogICAgICAgICAgICBsb2dfaWQpKQ0KYGBgDQoNCg0KIyMgTm9ybWFsaXR5IEFuYWx5emUgb2YgZGF0YSBmcmFtZXMNCg0KSGVyZSB3ZSBhcmUgZ29pbmcgdG8gaW52ZXN0aWdhdGUgdGhlIG5vcm1hbGl0eSBvZiB0aGUgbnVtZXJpY2FsIGRhdGEsIHRvIGtub3cgbW9yZSBhYm91dCB0aGUgbGltaXRhdGlvbnMgYWJvdXQgb3VyIGRhdGEuIExlc3Qgc3RhcnQgd2l0aCB0aGUgdmFyaWFibGVzIGluc2lkZSAqZGFpbHlBY3Rpdml0eV9qb2luKjoNCg0KYGBge3J9DQojSGVyZSB3ZSBnb2luZyB0byB1c2UgdGhlIGxpYnJhcnkgIERhdGFFeHBsb3Jlciwgc2luY2Ugb3VyIGRhdGEgZnJhbWUgaGF2ZSBzb21lIGNhdGVnb3JpY2FsIHZhcmlhYmxlcyBhbmQgd2lsbCBiZSBkaWZmaWN1bHQgdG8gbWFrZSBhIGxvb3AgZm9yIGdncGxvdDIuDQpkYWlseUFjdGl2aXR5X2pvaW4gJT4lDQogIHBsb3RfaGlzdG9ncmFtKCANCiAgICBuY29sID0gMywNCiAgICBnZ3RoZW1lID0gdGhlbWVfbGlnaHQoKQ0KICAgICkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgc29tZSB2YXJpYWJsZXMgaGF2ZSBuZWFyIGEgbm9ybWFsIGJlaGF2aW9yIHdpdGggbGl0dGxlIHNrZXcgb3IgYWJub3JtYWxseSB2YWx1ZXMuIGkuZS4gKmNhbG9yaWVzLCB0b3RhbF9taW51dGVzX2Flc2xlZXAsIGxpZ2h0bHlfYWN0aXZlX21pbnV0ZXMqIGFuZCBvdGhlcnMgaGF2ZSBhIHN0cm9uZyByaWdodCBza2V3ZWQgZGlzdHJpYnV0aW9ucyBpLmUuICpmYWlybHlfYWN0aXZlX21pbnV0ZXMqIGFuZCAqdmVyeV9hY3RpdmVfbWludXRlcyouIA0KDQpOb3cgd2UgYXJlIGFuYWx5emUgdGhlIGRhdGEgaW5zaWRlICpob3VybHlBY3Rpdml0eV9qb2luKjoNCg0KYGBge3J9DQpob3VybHlBY3Rpdml0eV9qb2luICU+JQ0KICBwbG90X2hpc3RvZ3JhbSggDQogICAgbmNvbCA9IDMsDQogICAgZ2d0aGVtZSA9IHRoZW1lX2xpZ2h0KCkNCiAgICApDQpgYGANCg0KSGVyZSB3ZSBjYW4gc2VlIHRoYXQgYWxsIHZhcmlhYmxlcyBhcmUgcmlnaHQgc2tld2VkLiBUaGlzIGlzIHJlbGF0ZWQgdG8gZmFjdCB0aGF0IG1vc3Qgb2YgdGhlIGhvdXJzIHRoZSBwZW9wbGUgYXJlIGdvaW5nIHRvIGJlIHdvcmtpbmcgb3Igc2xlZXBpbmcsIGFuZCBzaW5jZSB0aGUgaW50ZW5zaXR5IGlzIGxvdyBpcyBub3JtYWwgdG8gaGF2ZSBhIHNrZXdlZCBwbG90IGZvciB0aGUgY2Fsb3JpZXMuDQoNCiMgRGF0YSBhbmFseXplDQoNCiMjIERpc3RyaWJ1dGlvbiBvZiB0aGUgdHJhY2tpbmcgb2YgdGhlIGRldmljZXMNCg0KV2UgYXJlIHJlYWR5IHRvIG1ha2Ugc29tZSBxdWVzdGlvbnMgZnJvbSBvdXIgRGF0YS4gVGhlIGZpcnN0IHF1ZXN0aW9uIHdlIHdhbnQgdG8gaW52ZXN0aWdhdGUgaXM6DQoNCiogKipXaGljaCBpcyB0aGUgZGlzdHJpYnV0aW9uIG9mIHRoZSB1c2FnZSBvZiB0aGUgYXBwcyBvbiB0aGUgZGlmZmVyZW50cyBhY3Rpdml0aWVzPyoqDQoNCldlIGFscmVhZHkga25vdyB0aGUgYW5zd2VyIHRvIHRoaXMgcXVlc3Rpb24gdGhhbmtzIHRvIHRoZSBpbml0aWFsIGV4cGxvcmF0aW9uIHdlIGRpZC4gV2UgaGF2ZSAzMyB1c2VyIHRoYXQgdXNlIGhlciBkZXZpY2UgdG8gdHJhY2sgaGVyIGRhaWx5IGFjdGl2aXR5LCAyNCB1c2VycyB0aGF0IHRyYWNrIGhlciBzbGVlcCBiZWhhdmlvciwgOCB1c2VycyB0aGF0IHRyYWNrcyBoZXIgd2VpZ2h0IGxvc3MvZ2FpbiBhbmQgMTQgdXNlcnMgdGhhdCB0cmFjayBoZXIgaGVhcnQgcmF0ZS4gU28gbGV0IHB1dCB0aGlzIGluZm9ybWF0aW9uIG9uIGEgcGxvdC4NCg0KYGBge3J9DQojIFdlIGFyZSBnb2luZyB0byBwbG90IGEgVmVubiBkaWFncmFtIGJldHdlZW4gdGhlIDQgZmlsZSBkYWlseUFjdGl2aXR5X2NsZWFuLCBzbGVlcERheV9jbGVhbiwgd2VpZ2h0TG9nSW5mb19jbGVhbiBhbmQgaGVhcnRyYXRlX3NlY29uZHNfbWVyZ2VkLmNzdg0KI0ZpcnN0IHdlIG5lZWQgdG8gY3JlYXRlIHRoZSBzZXRzLiBXZSBhcmUgZ29pbmcgdG8gY3JlYXRlIGZvciBlYWNoIGRhdGFmcmFtZSBhIHNldCBvZiB1bmlxdWUgSWRzLg0KDQpzdGVwX2lkcyA8LSB1bmlxdWUoZGFpbHlBY3Rpdml0eV9jbGVhbiRpZCwgaW5jb21wYXJhYmxlcyA9IEZBTFNFKQ0Kc2xlZXBfaWRzIDwtIHVuaXF1ZShzbGVlcERheV9jbGVhbiRpZCwgaW5jb21wYXJhYmxlcyA9IEZBTFNFKQ0KaGVhcnRyYXRlX2lkcyA8LSB1bmlxdWUoaGVhcnRyYXRlX3NlY29uZHNfbWVyZ2VkLmNzdiRJZCwgaW5jb21wYXJhYmxlcyA9IEZBTFNFKQ0Kd2VpZ2h0X2lkcyA8LSB1bmlxdWUod2VpZ2h0TG9nSW5mb19jbGVhbiRpZCwgaW5jb21wYXJhYmxlcyA9IEZBTFNFKQ0KDQojbm93IHdlIGNyZWF0ZSB0aGUgZ3JhcGgsIEZyaXN0IHdlIG5lZWQgYSBsaXN0IHZlY3Rvci4NCnggPC0gbGlzdChBPXN0ZXBfaWRzLCBCPXNsZWVwX2lkcywgQz1oZWFydHJhdGVfaWRzLCBEPXdlaWdodF9pZHMpDQoNCiNmdW5jdGlvbiB0byBkaXNwbGF5IFZlbm4gZGlhZ3JhbSBpbnNpZGUgbWFya2Rvd24sIGZvciB0aGlzIHdlIG5lZWQgdG8gY2FsbCB0aGUgbGlicmFyeSBWZW5uRGlhZ3JhbQ0KZGlzcGxheV92ZW5uIDwtIGZ1bmN0aW9uKHgsIC4uLil7DQogIGdyaWQubmV3cGFnZSgpDQogIHZlbm5fb2JqZWN0IDwtIHZlbm4uZGlhZ3JhbSh4LCBmaWxlbmFtZSA9IE5VTEwsIC4uLikNCiAgZ3JpZC5kcmF3KHZlbm5fb2JqZWN0KQ0KfQ0KDQojZGlzcGxheSBWZW5uIGRpYWdyYW0NCmRpc3BsYXlfdmVubigNCiAgeCwNCiAgY2F0ZWdvcnkubmFtZXMgPSBjKCJTdGVwcyBjb3VudCIsICJTbGVlcCBtb25pdG9yIiwgIkhlYXJ0IG1vbml0b3IiLCAiV2VpZ2h0IHRyYWNraW5nIiksDQogIGZpbGwgPSBjKCIjOTk5OTk5IiwgIiNFNjlGMDAiLCAiIzU2QjRFOSIsICIjMDA5RTczIikNCiAgKQ0KYGBgDQoNCiMjIFR5cGUgb2YgdXNlcnMgcGVyIGFjdGl2aXR5IGxldmVsDQoNCkhlcmUgd2Ugd2lsbCBhc2NlcnRhaW4gaG93IG9mdGVuIHRoZSBwYXJ0aWNpcGFudHMgdXNlIHRoZWlyIHNtYXJ0IGRldmljZXMuIFdpdGggZGFpbHlfYWN0aXZpdHksIHdlIHdpbGwgYXNzdW1lIHRoYXQgZGF5cyB3aXRoIDwgMjAwIFRvdGFsU3RlcHMgdGFrZW4sIGFyZSBkYXlzIHdoZXJlIHVzZXJzIGhhdmUgbm90IHVzZWQgdGhlaXIgd2F0Y2hlcy4gV2Ugd2lsbCBmaWx0ZXIgb3V0IHRoZXNlIGluYWN0aXZlIGRheSBhbmQgYXNzaWduIHRoZSBmb2xsb3dpbmcgZGVzaWduYXRpb25zOg0KDQoqIExvdyBVc2UgLSAxIHRvIDUgZGF5cyANCiogTW9kZXJhdGUgVXNlIC0gNSB0byAyMCBkYXlzIA0KKiBIaWdoIFVzZSAtIDIxIHRvIDMxIGRheXMNCg0KQnJlYWtpbmcgZG93biB0aGUgYW5hbHlzaXMgZnVydGhlciBpbiB0aGlzIHdheSB3aWxsIGhlbHAgdXMgdW5kZXJzdGFuZCB0aGUgZGlmZmVyZW50IHRyZW5kcyB1bmRlcmx5aW5nIGVhY2ggVXNhZ2UgR3JvdXBzLg0KDQpgYGB7cn0NCiNIZXJlIHdlIGNyZWF0ZSBhIHRhYmxlIHRvIGNsYXNzaWZ5IHRoZSB1c2VycyBhY2NvcmRpbmcgdG8gdGhlIHRpbWVzIHRoZXkgYXBwZWFyIGluIHRoZSBkYXRhIGZyYW1lDQpkYWlseUFjdGl2aXR5X2pvaW4gJT4lDQogIGZpbHRlcih0b3RhbF9zdGVwcyA+IDIwMCkgJT4lDQogIGdyb3VwX2J5KGlkKSAlPiUNCiAgc3VtbWFyaXplKGNvdW50ID0gbigpKSAlPiUNCiAgbXV0YXRlKHVzYWdlID0gIGlmZWxzZShjb3VudCA8PSA1LCAgIkxvdyB1c2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShjb3VudCA8PSAyMCwgICJNb2RlcmF0ZSB1c2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShjb3VudCA8PSAzMSwgICJIaWdoIFVzZSIsIE5BKSkpKSU+JQ0KDQojV2Ugd2lsbCBub3cgY3JlYXRlIGEgcGVyY2VudGFnZSBkYXRhIGZyYW1lIHRvIGJldHRlciB2aXN1YWxpemUgdGhlIHJlc3VsdHMgaW4gdGhlIGdyYXBoLiBXZSBhcmUgYWxzbyBvcmRlcmluZyBvdXIgdXNhZ2UgbGV2ZWxzLg0KI3RoZSA6OiBoZXJlIGNhbGwgdGhlIGxpYnJhcnkgc2NhbGVzIHRvIHVzZSB0aGUgZnVuY3Rpb24gcGVyY2VudCwgc2luY2Ugd2Ugb25seSB1c2lnbiBvbmNlLCB3ZSBkb250IG5lZWQgdG8gbG9hZCB0aGUgbGlicmFyeS4NCiAgZ3JvdXBfYnkodXNhZ2UpICU+JQ0KICBzdW1tYXJpc2UodG90YWwgPSBuKCkpICU+JQ0KICBtdXRhdGUocGVyYyA9IHRvdGFsL3N1bSh0b3RhbCkpJT4lDQogIG11dGF0ZShwZXJjID0gc2NhbGVzOjpwZXJjZW50KHBlcmMpKSAlPiUgDQoNCiNOb3cgdGhhdCB3ZSBoYXZlIG91ciBuZXcgdGFibGUgd2UgY2FuIGNyZWF0ZSBvdXIgcGxvdC4NCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSB0b3RhbCwgZmlsbCA9IHVzYWdlICkpICsNCiAgICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoID0gMSkgKw0KICAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQ9MCkrDQogICAgdGhlbWVfdm9pZCgpKw0KICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHZqdXN0PSAtNSwgc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSkgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwZXJjLCB4ID0gMS4yNSkscG9zaXRpb24gPSBwb3NpdGlvbl9zdGFjayh2anVzdCA9IDAuNSkpICsNCiAgICBsYWJzKHRpdGxlID0gIlVzYWdlIEdyb3VwIERpc3RyaWJ1dGlvbiIpICsNCiAgICBndWlkZXMoZmlsbCA9IGd1aWRlX2xlZ2VuZCh0aXRsZSA9ICJVc2FnZSBUeXBlIikpDQpgYGANCg0KQW5hbHl6aW5nIG91ciByZXN1bHRzIHdlIGNhbiBzZWUgdGhhdCA2My42JSBvZiB0aGUgdXNlcnMgb2Ygb3VyIHNhbXBsZSB1c2UgdGhlaXIgZGV2aWNlIGZyZXF1ZW50bHkgYWxtb3N0IHZlcnkgZGF5IC0gYmV0d2VlbiAyNSB0byAzMSBkYXlzLCAyNy4zJSB1c2UgdGhlaXIgZGV2aWNlIDE1IHRvIDI1IGRheXMuIDYuMSUgb2Ygb3VyIHNhbXBsZSB1c2UgdGhlaXIgZGV2aWNlIGJldHdlZW4gNSB0byAxNSBkYXlzIGFuZCAzLjAlIHVzZSB0aGVpciBkZXZpY2VzIHZlcnkgcmFyZWx5LiANCg0KIyMgVGltZSB1c2VkIHNtYXJ0IGRldmljZSBhbmQgZGlzdHJpYnV0aW9uIA0KDQpXZSB3aWxsIGFuYWx5c2UgdGhlIHN0ZXBzIHRha2VuIGJ5IHVzZXJzIHdpdGhpbiBhbmQgYmV0d2VlbiBncm91cHMgcGVyIGRheSBhbmQgaG91ci4gTGV0cyBzdGFydCB3aXRoIHRoZSBkYWlseSBzdGVwcyBmb3IgZWFjaCB1c2VyIGJldHdlZW4gZ3JvdXBzLg0KDQpgYGB7cn0NCiNoZXJlIHdlIGNyZWF0ZSBhIG5ldyBjb2x1bW4gb24gb3VyIGRhdGEgZnJhbWUgd2l0aCB0aGUgY2xhc3NpZmljYXRpb24gd2UgZGlkIGJlZm9yZSwgc2luY2Ugd2UgYXJlIGdvaW5nIHRvIG5lZWQgaXQgZm9yIHRoZSByZXN0IG9mIHRoZSBhbmFseXplcy4NCmRhaWx5QWN0aXZpdHlfam9pbiA8LSBkYWlseUFjdGl2aXR5X2pvaW4gJT4lDQogIGZpbHRlcih0b3RhbF9zdGVwcyA+IDIwMCkgJT4lDQogIGdyb3VwX2J5KGlkKSAlPiUNCiAgbXV0YXRlKGNvdW50ID0gbigpKSAlPiUNCiAgbXV0YXRlKHVzYWdlID0gIGlmZWxzZShjb3VudCA8PSA1LCAgIkxvdyB1c2UiLA0KICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGNvdW50IDw9IDIwLCAgIk1vZGVyYXRlIHVzZSIsDQogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoY291bnQgPD0gMzEsICAiSGlnaCB1c2UiLCBOQSkpKSwgZ3JvdXBzPSJkcm9wIikgJT4lDQoNCiNXZSBhcmUgZ29pbmcgdG8gb3JnYW5pemUgdGhlIGxldmVsIGluIHRoZSBvcmRlciB3ZSB3YW50IHRoZXkgYXBwZWFyIG9uIHRoZSBwbG90cy4NCiBtdXRhdGUodXNhZ2UgPSBmYWN0b3IodXNhZ2UsIGxldmVsID0gYygnTG93IHVzZScsJ01vZGVyYXRlIHVzZScsJ0hpZ2ggdXNlJykpKSAlPiUgDQogIA0KI0FzIHdlIGdyb3VwIGFuZCBhcHBseSB0aGlzIHRvIG91ciBtYWluIGRhdGFmcmFtZSwgd2UgbmVlZCB0byB1bmdyb3VwIG9yIHdlIGFyZSBnb2luZyB0byBnZXQgYWxsIHZhbHVlcyBvZiBzdW1tYXJpemUgZnVuY3Rpb24gZ3JvdXBpbmcgYnkgaWQuDQogdW5ncm91cChpZCkNCmBgYA0KICANCmBgYHtyfQ0KZGFpbHlBY3Rpdml0eV9qb2luICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gdG90YWxfc3RlcHMsIGdyb3VwID0gaWQsIGNvbG9yID0gaWQpKSArDQogICAgZ2VvbV9saW5lKCkgKw0KICAgIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJub25lIikrDQogICAgZmFjZXRfd3JhcCh+dXNhZ2UsIG5jb2wgPSAxKQ0KYGBgDQoNClRoZXJlIGlzIG5vdCBzcGVjaWZpYyB0cmVuZCBoZXJlLCBzaW5jZSBzb21lIHZlcnkgSGlnaCB1c2UgdXNlcnMgaGF2ZSBzb21lIGRheXMgd2l0aCBsb3cgdG90YWwgc3RlcHMuIE5vdyB3ZSBhcmUgZ29pbmcgdG8gcGxvdCB0aGUgYXZlcmFnZSBzdGVwcyBieSBkYXkgb2YgZWFjaCBncm91cC4NCg0KYGBge3J9DQpkYWlseUFjdGl2aXR5X2pvaW4gJT4lDQogIGdyb3VwX2J5KHVzYWdlLCBkYXRlKSAlPiUgDQogIHN1bW1hcml6ZShhdmVyYWdlX3N0ZXBzID0gbWVhbih0b3RhbF9zdGVwcykpICU+JQ0KICBnZ3Bsb3QoYWVzKHggPSBkYXRlLCB5ID0gYXZlcmFnZV9zdGVwcywgZmlsbCA9IHVzYWdlLCAgY29sb3IgPSB1c2FnZSkpICsgDQogICAgIGdlb21fY29sKCkrDQogICAgIGZhY2V0X3dyYXAofnVzYWdlKQ0KYGBgDQoNCk5vdyB3ZSBnb2luZyB0byB2aXN1YWxpemF0ZSB0aGlzIG9uIGEgYmV0dGVyIG1hbm5lciB0cm91Z2ggYSBib3hwbG90IGRpYWdyYW0uDQoNCmBgYHtyfQ0KZGFpbHlBY3Rpdml0eV9qb2luICU+JQ0KICBncm91cF9ieSh1c2FnZSwgZGF0ZSkgJT4lIA0KICBzdW1tYXJpemUoYXZlcmFnZV9zdGVwcyA9IG1lYW4odG90YWxfc3RlcHMpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gdXNhZ2UsIHkgPSBhdmVyYWdlX3N0ZXBzLCBmaWxsID0gdXNhZ2UsICBjb2xvciA9IHVzYWdlKSkgKyANCiAgICAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQpGaW5hbGx5LCB3ZSBhcmUgZ29pbmcgdG8gcGxvdCB0aGUgYXZlcmFnZSB1c2Ugb2YgdGhlIGRldmljZXMgcGVyIHdlZWsgZGF5Lg0KDQpgYGB7cn0NCiNGaXJzdCB3ZSBjcmVhdGUgYW5kIGNvbHVtbiB0aGF0IGNvbnRhaW50IGVhY2ggd2Vla2RheQ0KZGFpbHlBY3Rpdml0eV9qb2luICU+JQ0KICBtdXRhdGUod2Vla2RheSA9IHdlZWtkYXlzKGFzLkRhdGUoZGF0ZSkpLCANCiAgICAgICAgIHdlZWtkYXkgPSBmY3RfcmVsZXZlbCh3ZWVrZGF5LCBjKCJTdW5kYXkiLCAiTW9uZGF5IiwgIlR1ZXNkYXkiLCAiV2VkbmVzZGF5IiwgIlRodXJzZGF5IiwgIkZyaWRheSIsICJTYXR1cmRheSIpKSkgJT4lIA0KDQojTm93IHdlIGdyb3VwIGJ5IHVzYWdlIGFuZCB3ZWVrZGF5LCBnZXQgdGhlIGF2ZXJhZ2UsIHRoZSBjb25maWRlbmNlIGludGVydmFsIGFuZCBmaW5hbGx5IHdlIHBsb3QuDQogIGdyb3VwX2J5KHdlZWtkYXksIHVzYWdlKSAlPiUgDQogIHN1bW1hcml6ZShhdmVyYWdlX3N0ZXBzID0gbWVhbih0b3RhbF9zdGVwcyksIGNpID0gcXQoMC45NzUsIG4oKSkqc2QodG90YWxfc3RlcHMpL3NxcnQobigpKSklPiUNCiAgZ2dwbG90KGFlcyh4ID0gd2Vla2RheSwgeSA9IGF2ZXJhZ2Vfc3RlcHMsIGZpbGwgPSB1c2FnZSwgIGNvbG9yID0gdXNhZ2UpKSArDQogICAgIGdlb21fY29sKCkrDQojY29kZSBmb3IgYWRkIGludGVydmFscyBvZiBjb25maWRlbmNlICANCiAgICAgI2dlb21fZXJyb3JiYXIoYWVzKHltaW4gPSAgYXZlcmFnZV9zdGVwcyAtIGNpLCB5bWF4ID0gIGF2ZXJhZ2Vfc3RlcHMgKyBjaSksIHdpZHRoID0gMC4yLCBjb2xvdXIgPSAnYmxhY2snKSArDQogICAgIGZhY2V0X3dyYXAofnVzYWdlLCBuY29sPTEpDQpgYGANCg0KV2UgY2FuIHNlZSBzb21lIHBhdHJvbnMgZnJvbSBvdXIgZGF0YToNCg0KKiBBdmVyYWdlIHN0ZXBzIHBlciBkYXkgaW5jcmVhc2VzIGFzIHVzYWdlIG9mIGRldmljZXMgaW5jcmVhc2VzLCB3ZSBhcmUgZ29pbmcgdG8gaW52aXN0aWdhdGUgbW9yZSBvbiB0aGlzIGluIHRoZSBuZXh0IHNlY3Rpb24uDQoNCiogRm9yIG1vZGVyYXRlIGFuZCBoaWdoIHVzZSB1c2VycywgdGhlcmUgaXMgbm90IGEgY2xlYXIgZGF5IHRoYXQgc2hvdyBhIGhpZ2hlciBtZWFuIHRoYW4gdGhlIG90aGVyIGRheXMgKGlzIG5lY2Vzc2FyeSB0byBkbyBhIHQtdGVzdCwgaG93ZXZlciB5b3UgbmVlZCB0byBiZSBhd2FyZSB0aGF0IGRhdGEgaXMgbm90IGluZGVwZW5kZW50IHdpdGhpbiBncm91cHMgYW5kIGJldHdlZW4gZ3JvdXBzKS4NCg0KKiBMb3cgdXNlIHVzZXJzICgxIGluZGl2aWR1ZSkgZG9lcyBub3Qgc2VlbSB0byBkaXNwbGF5IGFueSBkaWZmZXJlbmNlIG9uIHRoZSBtZWFuIGFnYWluc3QgdGhlIG1vZGVyYXRlIHVzZSB1c2Vycy4NCg0KIyMgVXNhZ2UgZHVyaW5nIHRoZSBkYXkgKGEgbW9yZSBpbiBkZWVwIGFuYWx5c2lzKQ0KDQpOb3cgdGhhdCB3ZSBoYXZlIHNvbWUgdHJlbmRzIG9mIHVzYWdlLCB3ZSB3YW50IHRvIHRoZSBkaXN0cmlidXRpb24gb2YgdXNhZ2UgZHVyaW5nIHRoZSBkYXkgb2YgdGhlIGRldmljZXMsIGFuZCBob3cgdGhpcyBpcyBjb3JyZWxhdGUgdG8gc29tZSBhY3Rpdml0aWVzLiBGb3IgdGhpcyB3ZSBhcmUgZ29pbmcgdG8gYmUgd29ya2luZyB3aXRoIHRoZSAqaG91cmx5QWN0aXZpdHlfam9pbiogdGFibGUuIFRoZSBmaXJzdCB3ZSBhcmUgZ29pbmcgdG8gaW52ZXN0aWdhdGUgaXMgdGhlIGRpc3RyaWJ1dGlvbiB1c2FnZSBvZiB0aGUgZGV2aWNlcyBkdXJpbmcgZWFjaCBkYXkgb2YgdGhlIHdlZWsgZm9yIGVhY2ggZ3JvdXAuIA0KDQpgYGB7cn0NCiNTaW5jZSB0aGlzIGlzIG90aGVyIGRhdGEgZnJhbWUsIHdlIG5lZWQgdG8gbWFrZSB0aGUgY2xhc3NpZmljYXRpb24gYWdhaW4sIGZpcnN0IHdlIHN1bSB0aGUgdmFsdWVzIG9mIHRvdGFsIHN0ZXBzIHBlciBkYXkgdG8gZmlsdGVyIGJ0IHZhbHVlcyA+IDIwMCBvbiB0aGUgb3RoZXIgc3RlcA0KaG91cmx5QWN0aXZpdHlfam9pbiA8LSBob3VybHlBY3Rpdml0eV9qb2luICU+JQ0KICBncm91cF9ieShpZCwgZGF5KGRhdGUpKSAlPiUNCiAgcmVuYW1lKGRheSA9ICJkYXkoZGF0ZSkiKSAlPiUNCiAgbXV0YXRlKHRvdGFsX3N0ZXBzID0gc3VtKHN0ZXBfdG90YWwpKSAlPiUgDQogIHVuZ3JvdXAoaWQsIGRheSkNCmBgYA0KDQpgYGB7cn0NCiNub3cgd2Ugc3VtIHRoZSBkYXlzIGEgdXNlciB1c2UgdGhlIGRldmljZXMgYW5kIG1ha2UgdGhlIGNhdGVnb3JpemF0aW9uLg0KaG91cmx5QWN0aXZpdHlfam9pbiA8LSBob3VybHlBY3Rpdml0eV9qb2luICU+JQ0KICBmaWx0ZXIodG90YWxfc3RlcHMgPiAyMDApICU+JQ0KICBncm91cF9ieShpZCkgJT4lIA0KICBtdXRhdGUoZGF5c191c2FnZSA9IG5fZGlzdGluY3QoZGF5KGRhdGUpKSkgJT4lIA0KICBtdXRhdGUodXNhZ2UgPSAgaWZlbHNlKGRheXNfdXNhZ2UgPD0gNSwgICJMb3cgdXNlIiwgDQogICAgICAgICAgICAgICAgICAgICAgICBpZmVsc2UoZGF5c191c2FnZSA8PSAyMCwgICJNb2RlcmF0ZSB1c2UiLCANCiAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShkYXlzX3VzYWdlICA8PSAzMSwgICJIaWdoIHVzZSIsIE5BKSkpKSAlPiUgDQojV2UgYXJlIGdvaW5nIHRvIG9yZ2FuaXplIHRoZSBsZXZlbCBpbiB0aGUgb3JkZXIgd2Ugd2FudCB0aGV5IGFwcGVhciBvbiB0aGUgcGxvdHMuDQogIG11dGF0ZSh1c2FnZSA9IGZhY3Rvcih1c2FnZSwgbGV2ZWwgPSBjKCdMb3cgdXNlJywnTW9kZXJhdGUgdXNlJywnSGlnaCB1c2UnKSkpICU+JSANCiAgdW5ncm91cChpZCkNCmBgYA0KDQpgYGB7cn0NCiNOb3cgd2UgcGxvdA0KaG91cmx5QWN0aXZpdHlfam9pbiAgJT4lDQogIG11dGF0ZSh3ZWVrZGF5ID0gZm9ybWF0KHltZChkYXRlKSwgZm9ybWF0ID0gJyVhJyksIA0KICAgICAgICAgd2Vla2RheSA9IGZjdF9yZWxldmVsKHdlZWtkYXksIGMoIlN1biIsICJNb24iLCAiVHVlIiwgIldlZCIsICJUaHUiLCAiRnJpIiwgIlNhdCIpKSkgJT4lDQogIGdyb3VwX2J5KHRpbWUsIHdlZWtkYXksIHVzYWdlKSAlPiUgDQogIHN1bW1hcml6ZShhdmVyYWdlX3N0ZXBzID0gbWVhbihzdGVwX3RvdGFsKSkgJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gYXZlcmFnZV9zdGVwcywgZmlsbCA9IGF2ZXJhZ2Vfc3RlcHMpKSArDQogICAgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJEIikrDQogICAgZ2VvbV9jb2woKSsNCiAgICBmYWNldF9ncmlkKHVzYWdlfndlZWtkYXkpKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1LCBhbmdsZSA9IDkwKSkNCmBgYA0KDQpgYGB7cn0NCiNXZSBhbHNvIGdvaW5nIHRvIG1ha2UgYSBoZWF0IHBsb3QgZm9yIHRoZSBzYW1lIGRpc3RyaWJ1dGlvbiB0byBoYXZlIG90aGVyIG9wdGlvbnMgZm9yIHByZXNlbnRhdGlvbi4NCmhvdXJseUFjdGl2aXR5X2pvaW4gJT4lDQogIG11dGF0ZSh3ZWVrZGF5ID0gZm9ybWF0KHltZChkYXRlKSwgZm9ybWF0ID0gJyVhJyksIA0KICAgICAgICAgd2Vla2RheSA9IGZjdF9yZWxldmVsKHdlZWtkYXksIGMoIlN1biIsICJNb24iLCAiVHVlIiwgIldlZCIsICJUaHUiLCAiRnJpIiwgIlNhdCIpKSkgJT4lDQogIGdyb3VwX2J5KHdlZWtkYXksdGltZSwgdXNhZ2UpICU+JSANCiAgc3VtbWFyaXplKGF2ZXJhZ2Vfc3RlcHMgPSBtZWFuKHN0ZXBfdG90YWwpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHRpbWUsIHkgPSB3ZWVrZGF5LCBmaWxsID0gYXZlcmFnZV9zdGVwcykpICsNCiAgICB2aXJpZGlzOjpzY2FsZV9maWxsX3ZpcmlkaXMob3B0aW9uID0gIkQiKSsNCiAgICBnZW9tX3RpbGUoKSsNCiAgICBnZW9tX3RleHQoYWVzKGxhYmVsID0gcm91bmQoYXZlcmFnZV9zdGVwcywgZGlnaXRzID0gMCkpLCBjb2xvciA9ICJibGFjayIsIHNpemUgPSAyLjApICsNCiAgICBmYWNldF93cmFwKH51c2FnZSwgbmNvbD0xKSsNCiAgICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChzaXplID0gNSwgYW5nbGUgPSA5MCkpDQpgYGANCg0KV2UgY2FuIHNlZSBzb21lIHBhdHJvbnMgZnJvbSBvdXIgZGF0YToNCg0KKiBUaGUgaGlnaCB1c2UgdXNlcnMgc3RhcnQgdGhlaXIgZGF5IGFuIGhvdXIgZWFybGllciAoNjowMEFNKSBjb21wYXJlZCB0byBvdGhlciBncm91cHMgYW5kIGVuZCBoZXIgZGF5IGFuZCBob3VyIGxhdGVyICgyMjowMCBQTSkuIER1cmluZyB0aGUgd2Vla2RheXMgdGhlIHBlYWtzIGFyZSBiZXR3ZWVuLCA1OjAwIHRvIDg6MDAgUE0sIHN1Z2dlc3RpbmcgaGFiaXR1YWwgZXhjZXJjaXNlIGFzIHdvcmsgZW5kcy4NCg0KKiBNb2RlcmF0ZSBVc2UgdXNlcnMgZGlzcGxheSBwZWFrcyBpbiB0aGVpciBzdGVwcyB0aGUgU2F0dXJkYXlzIGFuZCBTdW5kYXlzLCBiZXR3ZWVuIDg6MDAgQU0gdG8gMTI6MDAgUE0uDQoNCiMjIE1vcmUgc3BlY2ZpYyBxdWVzdGlvbnMgYWJvdXQgdGhlIGRhdGENCg0KQXMgd2Ugc2VlIG9uIHRoZSBsYXN0IHBhcnQsIHRoZXJlIGFyZSBzb21lIGhvdXJzIHdoZXJlIHRoZSB1c2VycyBoYXZlIHNvbWUgcGVha3MsIHdlIHdhbnQgdG8gaW52ZXN0aWdhdGUgaXMgdGhpcyBpcyByZWxhdGVkIHdpdGggRXhlcmNpc2Ugc2Vzc2lvbnMgKHdlIGNhbiBnbyB0byBneW0gYW5kIGp1c3QgZG8gd2VpZ2h0IG9yIHdlIGNhbiBzcGVuZCBzb21lIHRpbWUgZG9pbmcgY2FyZGlvIG9uIGEgdHJlYWRtaWxsKS4gRm9yIHRoaXMgd2UgYXJlIGdvaW5nIHRvIGludmVzdGlnYXRlIHRoZSBpbnRlbnNpdHkgdmFyaWFibGUgYW5kIHdlIHdhbnQgdG8gcmVzcG9uc2Ugc29tZSBxdWVzdGlvbnM6DQoNCiogKipXaGF0IGFyZSB0aGUgcmVsYXRpb24gYmV0d2VlbiBpbnRlbnNpdHkgYW5kIGF2ZXJhZ2Ugc3RlcHM/KioNCg0KYGBge3J9DQojRmlyc3Qgd2UgYXJlIGdvaW5nIHRvIHBsb3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiBpbnRlbnNpdHkgYmV0d2VlbiB0aGUgZGF5cy4NCmhvdXJseUFjdGl2aXR5X2pvaW4gJT4lDQogIG11dGF0ZSh3ZWVrZGF5ID0gZm9ybWF0KHltZChkYXRlKSwgZm9ybWF0ID0gJyVhJyksIA0KICAgICAgICAgd2Vla2RheSA9IGZjdF9yZWxldmVsKHdlZWtkYXksIGMoIlN1biIsICJNb24iLCAiVHVlIiwgIldlZCIsICJUaHUiLCAiRnJpIiwgIlNhdCIpKSkgJT4lDQogIGdyb3VwX2J5KHRpbWUsIHdlZWtkYXksIHVzYWdlKSAlPiUgDQogIHN1bW1hcml6ZShhdmVyYWdlX2ludGVuc2l0eV9ob3VyID0gbWVhbihhdmVyYWdlX2ludGVuc2l0eSkpJT4lIA0KICBnZ3Bsb3QoYWVzKHggPSB0aW1lLCB5ID0gYXZlcmFnZV9pbnRlbnNpdHlfaG91ciwgZmlsbCA9IGF2ZXJhZ2VfaW50ZW5zaXR5X2hvdXIpKSArDQogICAgdmlyaWRpczo6c2NhbGVfZmlsbF92aXJpZGlzKG9wdGlvbiA9ICJpbmZlcm5vIikrDQogICAgZ2VvbV9jb2woKSsNCiAgICBmYWNldF9ncmlkKHVzYWdlfndlZWtkYXkpKw0KICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KHNpemUgPSA1LCBhbmdsZSA9IDkwKSkNCmBgYA0KDQpXZSBjYW4gc2VlIHRoYXQgdGhlIHBsb3RzIGFyZSB2ZXJ5IHNpbWlsYXIgYmV0d2VlbiBhdmVyYWdlX2ludGVuc2l0eSBhbmQgdGhlIGF2ZXJhZ2Vfc3RlcHMgcGVyIGdyb3VwLiBTbyB3ZSB3aWxsIGV4cGVjdCBhIGxpbmVhciBjb3JyZWxhdGlvbiBiZXR3ZWVuIGJvdGggb2YgdGhpcyB2YXJpYWJsZXMuDQoNCmBgYHtyfQ0KaG91cmx5QWN0aXZpdHlfam9pbiAlPiUNCiAgbXV0YXRlKHdlZWtkYXkgPSB3ZWVrZGF5cyhhcy5EYXRlKGRhdGUpKSkgJT4lDQogIGdyb3VwX2J5KHRpbWUsIHdlZWtkYXksIHVzYWdlKSAlPiUgDQogIHN1bW1hcml6ZShhdmVyYWdlX2ludGVuc2l0eV9ob3VyID0gbWVhbihhdmVyYWdlX2ludGVuc2l0eSksIGF2ZXJhZ2Vfc3RlcHMgPSBtZWFuKHN0ZXBfdG90YWwpICklPiUgDQogIGdncGxvdChhZXMoeCA9IGF2ZXJhZ2VfaW50ZW5zaXR5X2hvdXIsIHkgPSBhdmVyYWdlX3N0ZXBzKSkgKw0KICAgIGdlb21fcG9pbnQoKSsNCiAgICBnZW9tX3Ntb290aCgpKw0KICAgIGZhY2V0X3dyYXAofnVzYWdlKQ0KYGBgDQoNClNvIGFzIHdlIGV4cGVjdGVkLCB3ZSBoYXZlIGEgcG9zaXRpdmUgY29ycmVsYXRpb24gKGFsbW9zdCBsaW5lYXIpIGJldHdlZW4gdGhlIGF2ZXJhZ2UgaW50ZW5zaXR5IHBlciBob3VyIGFuZCBhdmVyYWdlIHN0ZXBzIHBlciBob3VyLiBTbyB3ZSBjYW4gYXNzb2NpYXRlIHRoZSBoaWdoIHN0ZXBzIHRvIHNlc3Npb25zIG9mIGV4ZXJjaXNlIHdoZXJlIHRoZSB1c2VycyBpcyB2ZXJ5IGFjdGl2ZS4gTGV0cyBhbHNvIGludmVzdGlnYXRlIHRoZSBjb3JyZWxhdGlvbiBiZXR3ZWVuIHRoZSB2YXJpYWJsZXMgYW5kIGF2ZXJhZ2UgY2Fsb3JpZXMgYnVybmVyLg0KDQpgYGB7cn0NCmhvdXJseUFjdGl2aXR5X2pvaW4gJT4lDQogIG11dGF0ZSh3ZWVrZGF5ID0gd2Vla2RheXMoYXMuRGF0ZShkYXRlKSkpICU+JQ0KICBncm91cF9ieSh0aW1lLCB3ZWVrZGF5LCB1c2FnZSkgJT4lIA0KICBzdW1tYXJpemUoYXZlcmFnZV9pbnRlbnNpdHlfaG91ciA9IG1lYW4oYXZlcmFnZV9pbnRlbnNpdHkpLCBhdmVyYWdlX3N0ZXBzID0gbWVhbihzdGVwX3RvdGFsKSwgYXZlcmFnZV9jYWxvcmllcyA9IG1lYW4oY2Fsb3JpZXMpKSAlPiUgDQogIEdHYWxseTo6Z2dwYWlycyhjb2x1bW5zID0gYyg0LDUsNikpDQpgYGANCg0KV2UgY2FuIHNlZSBoZXJlIGEgc3Ryb25nIGNvcnJlbGF0aW9uIGJldHdlZW4gdGhpcyB2YXJpYWJsZXMuIFRoaXMgaXMgZXhwZWN0ZWQsIHNpbmNlIGFzIHdlIHNhdyBiZWZvcmUsIHRoZSBoaWdoIHN0ZXBzIGFyZSBnZW5lcmFsbHkgYXNzb2NpYXRlZCB3aXRoIGhpZ2ggaW50ZW5zaXR5IGV4ZXJjaXNlIHNlc3Npb25zLCB3aGVyZSB0aGUgdXNlciB3aWxsIHRlbmQgdG8gYnVybiBtb3JlIGNhbG9yaWVzLiANCg0KIyMjIFdoYXQgYWJvdXQgc2xlZXAgYmVoYXZpdXI/DQoNCkFub3RoZXIgdmFyaWFibGUgd2lsbCBiZSBpbnRlcmVzdGluZyB0byBhbmFseXplIGlzIHRoZSBzbGVlcCBiZWhhdmlvci5XZSB3YW50IHRvIGludmVzdGlnYXRlIGhvdyBpcyB0aGUgc2xlZXAgYmVoYXZpb3IgZnJvbSB1c2VycyBhY2NvcmRpbmcgdG8gdGhlaXIgYWN0aXZlIGxldmVsLg0KDQoqICoqV2hhdCBhcmUgdGhlIHJlbGF0aW9uIGJldHdlZW4gYWN0aXZlIGxldmVsIGFuZCBzbGVlcCBob3Vycz8qKg0KDQpgYGB7cn0NCiNGaXJzdCB3ZSBhcmUgZ29pbmcgdG8gcGxvdCB0aGUgZGlzdHJpYnV0aW9uIG9mIHNsZWVwIGJldHdlZW4gdGhlIGdyb3Vwcy4NCmRhaWx5QWN0aXZpdHlfam9pbiAlPiUNCiAgZ3JvdXBfYnkoZGF0ZSwgdXNhZ2UpICU+JSANCiAgc3VtbWFyaXplKGF2ZXJhZ2Vfc2xlZXBfbWludXRlcyA9IG1lYW4odG90YWxfbWludXRlc19hc2xlZXAsIG5hLnJtPVRSVUUpKSAlPiUgDQogIGdncGxvdChhZXMoeCA9IHVzYWdlLCB5ID0gYXZlcmFnZV9zbGVlcF9taW51dGVzLCBmaWxsID0gdXNhZ2UpKSArDQogICAgZ2VvbV9ib3hwbG90KCkNCmBgYA0KDQpIZXJlIHdlIHNlZSB0aGF0IHRoZSB1c2VycyBoYXZlIGFsbW9zdCB0aGUgc2FtZSBtZWFuLCBpbmRlcGVuZGVudCBmcm9tIHRoZXJlIHVzYWdlIGdyb3VwLCB0aGUgbWlzc2luZyB2YWx1ZSBvbiB0aGUgbG9zIHVzZSBncm91cCBpcyBkdWUgdGhhdCB0aGUgb25seSB1c2VyIHdlIGhhdmUgb24gdGhpcyBncm91cCBkb24ndCBoYXZlIGFueSBkYXRhIGFib3V0IGhpcyBzbGVlcCBiZWhhdmlvci4gV2UgYXJlIGdvaW5nIHRvIGdvaW5nIG1vcmUgaW4gZGVlcCBtYWtpbmcgYSBjbGFzc2lmaWNhdGlvbiBmb3IgdGhlIHRpbWUgc2xlcHQ6DQoNCiogQmFkIHNsZWVwIC0gc2xlcHQgbGVzcyB0aGFuIDMwMCBtaW51dGVzDQoqIE5vcm1hbCBTbGVlcCAgLSBzbGVwdCBiZXR3ZWVuIDMwMCBhbmQgNDgwIG1pbnV0ZXMNCiogT3ZlciBTbGVlcCAtIHNsZXB0IG1vcmUgdGhhbiA0ODAgbWludXRlcw0KDQpgYGB7cn0NCmRhaWx5QWN0aXZpdHlfam9pbiA8LSBkYWlseUFjdGl2aXR5X2pvaW4gJT4lDQogIG11dGF0ZShzbGVlcF90eXBlID0gIGlmZWxzZSh0b3RhbF9taW51dGVzX2FzbGVlcDw9IDMwMCwgICJCYWQgc2xlZXAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHRvdGFsX21pbnV0ZXNfYXNsZWVwIDw9IDQ4MCwgICJOb3JtYWwgc2xlZXAiLCANCiAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKHRvdGFsX21pbnV0ZXNfYXNsZWVwID4gNDgwLCAgIk92ZXIgc2xlZXAiLCBOQSkpKSwgDQogICAgICAgICBzbGVlcF90eXBlID0gZmFjdG9yKHNsZWVwX3R5cGUsIGxldmVsID0gYygnQmFkIHNsZWVwJywnTm9ybWFsIHNsZWVwJywnT3ZlciBzbGVlcCcpKSkgDQpgYGANCg0KYGBge3J9DQpkYWlseUFjdGl2aXR5X2pvaW4gJT4lDQogIGdyb3VwX2J5KHNsZWVwX3R5cGUsIGlkKSAlPiUNCiAgc3VtbWFyaXplKGNvdW50X3NsZWVwID0gbigpKSAlPiUNCiAgZHJvcF9uYSgpICU+JQ0KICBzdW1tYXJpemUodG90YWxfc2xlZXBfdHlwZSA9IG4oKSkgICU+JSANCiAgbXV0YXRlKHBlcmMgPSB0b3RhbF9zbGVlcF90eXBlL3N1bSh0b3RhbF9zbGVlcF90eXBlKSklPiUNCiAgbXV0YXRlKHBlcmMgPSBzY2FsZXM6OnBlcmNlbnQocGVyYykpICU+JSANCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSB0b3RhbF9zbGVlcF90eXBlLCBmaWxsID0gc2xlZXBfdHlwZSkpICsNCiAgICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoID0gMSkgKw0KICAgIGNvb3JkX3BvbGFyKCJ5Iiwgc3RhcnQ9MCkrDQogICAgdGhlbWVfdm9pZCgpKw0KICAgIHRoZW1lKHBsb3QudGl0bGUgPSBlbGVtZW50X3RleHQoaGp1c3QgPSAwLjUsIHZqdXN0PSAtNSwgc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSkgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwZXJjLCB4ID0gMS4yKSxwb3NpdGlvbiA9IHBvc2l0aW9uX3N0YWNrKHZqdXN0ID0gMC41KSkgKw0KICAgIGxhYnModGl0bGUgPSAiU2xlZXAgVHlwZSBEaXN0cmlidXRpb24iKSArDQogICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQodGl0bGUgPSAic2xlZXAgVHlwZSIpKQ0KYGBgDQoNCmBgYHtyfQ0KI1dlIGNhbiBhbHNvIHZpc3VhbGl6YXRlIHRoaXMgZGlzdHJpYnV0aW9uIHRocm91Z2ggdGhlIGRpZmZlcmVudCBVc2FnZSBncm91cHMuDQpkYWlseUFjdGl2aXR5X2pvaW4gJT4lDQogIGdyb3VwX2J5KHVzYWdlLCBzbGVlcF90eXBlLCBpZCkgJT4lDQogIHN1bW1hcml6ZShjb3VudF9zbGVlcCA9IG4oKSkgJT4lDQogIGRyb3BfbmEoKSAlPiUNCiAgc3VtbWFyaXplKHRvdGFsX3NsZWVwX3R5cGUgPSBuKCkpICAlPiUgDQogIG11dGF0ZShwZXJjID0gdG90YWxfc2xlZXBfdHlwZS9zdW0odG90YWxfc2xlZXBfdHlwZSkpJT4lDQogIG11dGF0ZShwZXJjID0gc2NhbGVzOjpwZXJjZW50KHBlcmMpKSAlPiUNCiAgZ2dwbG90KGFlcyh4ID0gIiIsIHkgPSB0b3RhbF9zbGVlcF90eXBlLCBmaWxsID0gc2xlZXBfdHlwZSkpICsNCiAgICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoID0gMSwgcG9zaXRpb24gPSAiZmlsbCIpICsNCiAgICBjb29yZF9wb2xhcigieSIsIHN0YXJ0PTApKw0KICAgIHRoZW1lX3ZvaWQoKSsNCiAgICB0aGVtZShwbG90LnRpdGxlID0gZWxlbWVudF90ZXh0KGhqdXN0ID0gMC41LCB2anVzdD0gNSwgc2l6ZSA9IDIwLCBmYWNlID0gImJvbGQiKSkgKw0KICAgIGdlb21fdGV4dChhZXMobGFiZWwgPSBwZXJjLCB4PTEuMiksIHBvc2l0aW9uID0gcG9zaXRpb25fZmlsbCh2anVzdCA9IDAuNSkpICsNCiAgICBsYWJzKHRpdGxlID0gIlNsZWVwIFR5cGUgRGlzdHJpYnV0aW9uIikgKw0KICAgIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKHRpdGxlID0gIlNsZWVwIFR5cGUiKSkrDQogICAgZmFjZXRfd3JhcCh+dXNhZ2UsIHN0cmlwLnBvc2l0aW9uID0gImJvdHRvbSIpDQpgYGANCg0KQW5hbHl6aW5nIG91ciByZXN1bHRzIHdlIGNhbiBzZWUgdGhhdCAyNi40JSBvZiB0aGUgdGltZXMgb2YgdXNlciByZXBvcnRzIGEgYmFkIHNsZWVwLCAzNS44JSBvZiB0aGUgdGltZXMgdGhleSBoYXZlIGEgbm9ybWFsIHNsZWVwIGFuZCAzNS44JSBvZiB0aGUgdGltZXMgdGhlIG92ZXIgc2xlZXAuIFRocm91Z2ggdGhlIGdyb3VwcyB3ZSBjYW4gc2VlIHRoYXQgdGhlIGRpc3RyaWJ1dGlvbiBpcyBuZWFyIHNpbWlsYXIgdG8gdGhlIGdsb2JhbC4gTm90ZSB0aGF0IGFuIHVzZXIgY2FuIGhhdmUgb25lIGRheSBvZiBlYWNoIGNhdGVnb3J5Lg0KDQpGaW5hbGx5LCB3ZSBhcmUgZ29pbmcgdG8gcmVsYXRlIHRoZSBzbGVlcCBiZWhhdmlvciBhZ2FpbnN0IHRoZSBhY3RpdmUgbGV2ZWwsIEFuZCB3ZSBhcmUgZ29pbmcgdG8gY2xhc3NpZnkgb3VyIHVzZXJzIGFjY29yZGluZyB0byB0aGVpciBtZWFuIGFjdGl2ZSBsZXZlbC4gVGhpcyBjbGFzc2lmaWNhdGlvbiB3aWxsIGJlIGJhc2VkIG9uIHRoZSBhdmVyYWdlIGFjdGl2ZSBsZXZlbCBvZiBlYWNoIHVzZXIgYWdhaW5zdCB0aGUgYXZlcmFnZSBhY3RpdmUgbGV2ZWwgb2YgYWxsIHVzZXJzIGkuZS4gaWYgYW4gdXNlciBoYXMgaGVyIHNlZGVudGFyeSBhdmVyYWdlIGdyZWF0ZXIgdGhhbiB0aGUgZ2xvYmFsIHNlZGVudGFyeSBhdmVyYWdlLCB0aGlzIHVzZXIgd2lsbCBiZSBjbGFzc2lmaWNhdGUgYXMgc2VkZW50YXJ5LiBGaW5hbGx5IGlmIGFuIHVzZXIgaXNudCBpbiBhbnkgY2F0ZWdvcmllLCB3ZSB3aWxsIGV4Y2x1ZGUgZnJvbSB0aGUgZGF0YS4NCg0KYGBge3J9DQojd2UgbmVlZCB0byBtYWtlIGEgY2xhc3NpZmljYXRpb24gZm9yIHRoZSBhY3RpdmUgbGV2ZWwgb2YgdGhlIHVzZXJzLiBGaXJzdCB3ZSBhcmUgZ29pbmcgdG8gZ2V0IHRoZSBhdmVyYWdlIG9mIGFsbCB1c2Vycy4gIA0KI0FuZCB3ZSBhcmUgZ29pbmcgdG8gZHJvcCB0aGUgMCB2YWx1ZXMgbWFraW5nIHRoZW0gTkEgdmFsdWVzIGFuZCBpZ25vcmluZyB0aGVtIG9uIHRoZSBtZWFuIGNhbGN1bGF0aW9uDQp0ZW1wIDwtIGRhaWx5QWN0aXZpdHlfam9pbiAlPiUNCiAgbmFfaWYoMCkgJT4lIA0KICBtdXRhdGUoc2VkZW50YXJ5X21pbnV0ZXNfYXZnID0gbWVhbihzZWRlbnRhcnlfbWludXRlcywgbmEucm0gPSBUUlVFKSwgDQogICAgICAgICAgICBsaWdodGx5X2FjdGl2ZV9taW51dGVzX2F2ZyA9IG1lYW4obGlnaHRseV9hY3RpdmVfbWludXRlcywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIGZhaXJseV9hY3RpdmVfbWludXRlc19hdmcgPSBtZWFuKGZhaXJseV9hY3RpdmVfbWludXRlcywgbmEucm0gPSBUUlVFKSwNCiAgICAgICAgICAgIHZlcnlfYWN0aXZlX21pbnV0ZXNfYXZnID0gbWVhbih2ZXJ5X2FjdGl2ZV9taW51dGVzLCBuYS5ybSA9IFRSVUUpKSAlPiUgDQoNCiNXZSBhcmUgZ29pbmcgdG8gcmVwbGFjZSBOQSB2YWx1ZXMgd2l0aCAwIHRvIGF2b2lkIGVycm9ycyBpbiBvdXIgY2F0ZWdvcml6YXRpb24uIEFmdGVyIHdlIGdvbm5hIG1ha2UgdGhlIGNsYXNzaWZpYWN0aW9uIHVzaW5nIHRoZSBzdGF0ZW1lbnQgY2FzZV93aGVuDQogIG11dGF0ZShzZWRlbnRhcnlfbWludXRlcyA9IHJlcGxhY2Uoc2VkZW50YXJ5X21pbnV0ZXMsaXMubmEoc2VkZW50YXJ5X21pbnV0ZXMpLDApLA0KICAgICAgICBsaWdodGx5X2FjdGl2ZV9taW51dGVzID0gcmVwbGFjZShsaWdodGx5X2FjdGl2ZV9taW51dGVzLGlzLm5hKGxpZ2h0bHlfYWN0aXZlX21pbnV0ZXMpLDApLA0KICAgICAgICBmYWlybHlfYWN0aXZlX21pbnV0ZXMgPSByZXBsYWNlKGZhaXJseV9hY3RpdmVfbWludXRlcyxpcy5uYShmYWlybHlfYWN0aXZlX21pbnV0ZXMpLDApLA0KICAgICAgICB2ZXJ5X2FjdGl2ZV9taW51dGVzID0gcmVwbGFjZSggdmVyeV9hY3RpdmVfbWludXRlcyxpcy5uYSggdmVyeV9hY3RpdmVfbWludXRlcyksMCkpICU+JSANCiAgbXV0YXRlKGFjdGl2ZV90eXBlID0gZmFjdG9yKGNhc2Vfd2hlbihzZWRlbnRhcnlfbWludXRlcyA+IHNlZGVudGFyeV9taW51dGVzX2F2ZyAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBsaWdodGx5X2FjdGl2ZV9taW51dGVzIDwgIGxpZ2h0bHlfYWN0aXZlX21pbnV0ZXNfYXZnICYNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZhaXJseV9hY3RpdmVfbWludXRlczwgZmFpcmx5X2FjdGl2ZV9taW51dGVzX2F2ZyAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ5X2FjdGl2ZV9taW51dGVzIDwgdmVyeV9hY3RpdmVfbWludXRlc19hdmcgfiAiU2VkZW50YXJ5IiwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGxpZ2h0bHlfYWN0aXZlX21pbnV0ZXMgPiAgbGlnaHRseV9hY3RpdmVfbWludXRlc19hdmcgJg0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFpcmx5X2FjdGl2ZV9taW51dGVzIDwgZmFpcmx5X2FjdGl2ZV9taW51dGVzX2F2ZyAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ5X2FjdGl2ZV9taW51dGVzIDwgdmVyeV9hY3RpdmVfbWludXRlc19hdmcgfiAiTGlnaHRseSBBY3RpdmUiLA0KICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgZmFpcmx5X2FjdGl2ZV9taW51dGVzID4gZmFpcmx5X2FjdGl2ZV9taW51dGVzX2F2ZyAmDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ5X2FjdGl2ZV9taW51dGVzIDwgdmVyeV9hY3RpdmVfbWludXRlc19hdmcgfiAnRmFpcmx5IEFjdGl2ZScsDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB2ZXJ5X2FjdGl2ZV9taW51dGVzID4gdmVyeV9hY3RpdmVfbWludXRlc19hdmcgfiAnVmVyeSBBY3RpdmUnKSwgDQogICAgICAgICAgICAgICAgICAgICAgbGV2ZWxzPWMoIlNlZGVudGFyeSIsICJMaWdodGx5IEFjdGl2ZSIsICJGYWlybHkgQWN0aXZlIiwgIlZlcnkgQWN0aXZlIikpKSU+JQ0KICBkcm9wX25hKHNsZWVwX3R5cGUsIGFjdGl2ZV90eXBlKSANCiAgDQojZmluYWxseSB3ZSBwbG90Lg0KDQp0ZW1wICU+JSANCiAgICBnZ3Bsb3QoYWVzKHggPSBhY3RpdmVfdHlwZSwgZmlsbCA9IHNsZWVwX3R5cGUpKSArDQogICAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsNCiAgICBsYWJzKHkgPSAiUHJvcG9ydGlvbiIpDQoNCnRlbXAgJT4lIA0KICAgIGdncGxvdChhZXMoeCA9IGFjdGl2ZV90eXBlLCBmaWxsID0gc2xlZXBfdHlwZSkpICsNCiAgICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICAgIGxhYnMoeSA9ICJQcm9wb3J0aW9uIikrDQogICAgZmFjZXRfd3JhcCh+dXNhZ2UpIA0KYGBgDQoNCkFuYWx5emluZyBvdXIgcmVzdWx0cyB3ZSBjYW4gc2VlIHRoYXQgU2VkZW50YXJ5IHBlb3BsZSB0ZW5kIHRvIGhhdmUgYSBiYWQgc2xlZXAgYmVoYXZpb3IuIFdlIGNhbiBhbHNvIG9ic2VydmUgdGhhdCBhIGxpdHRsZSBhY3Rpdml0eSBvbiB0aGUgZGF5IHdpbGwgdGVuZCB0byBhIG5vcm1hbCBzbGVlcC4gQWxzbyBhcyBhY3RpdmUgbGV2ZWwgaW5jcmVhc2UgdGhlIG92ZXJzbGVlcCBiZWhhdmlvciBkZWNyZWFjZSwNCg0KIyBEaXNjdXNzaW9uDQoNClRoZSBGaXRCaXQgZGF0YSBzZXQgY29uZmlybXMgdGhhdCBub3QgYWxsIHVzZXJzIGZ1bGx5IHV0aWxpemUgdGhlIGZ1bmN0aW9ucyBvZiB0aGVpciBkZXZpY2VzL3RyYWNrZXJzLiBBbGwgMzMgdW5pcXVlIElEcyB1c2VkIHRoZSBzdGVwIGNvdW50IGZ1bmN0aW9uLiAyNC8zMyB1bmlxdWUgSURzIHVzZWQgdGhlIHNsZWVwIHRyYWNraW5nIGZ1bmN0aW9uLiAxNC8zMyB1bmlxdWUgSURzIHVzZWQgdGhlIGhlYXJ0LXJhdGUgdHJhY2tpbmcuIDgvMzMgdW5pcXVlIElEcyB1c2VkIHRoZWlyIGRldmljZXMgdG8gdHJhY2sgdGhlaXIgd2VpZ2h0Lg0KDQoqKkhpZ2ggVXNlIEdyb3VwKioNCg0KVGhpcyBncm91cCBjb25zaXN0cyBvZiAyNCB1c2VycyBvciA3MyUgb2YgdGhlIHRvdGFsIHNhbXBsZSBzaXplLCBhbmQgd2VhcnMgdGhlIGRldmljZSByZWd1bGFybHkgYmV0d2VlbiAyMi0zMSBkYXlzLiBUaGlzIGlzIHRoZSBtb3N0IGFjdGl2ZSBncm91cCwgYW5kIGFsc28gdGhlIG1vc3QgdmFyaWVkIGluIHRoZSB0eXBlcyBvZiBleGVyY2lzZXMgY2FycmllZCBvdXQuIFZhcnlpbmcgZnJvbSBsaWdodCwgdG8gZmFpcmx5IGFuZCB2ZXJ5IGFjdGl2ZSBmb3JtcyBvZiBleGVyY2lzZXMuIFRoZXkgdGVuZHMgdG8gYmUgYWN0aXZlIHRocm91Z2hvdXQgdGhlIHdlZWsgd2l0aCBhbiBhdmVyYWdlIG9mIHdlZWtseSBzdGVwcyBvZiA5MDU0LjUwMC4gVGhlaXIgc2xlZXAgYmVoYXZpdXIgaXMgc3lteWV0cmljYWwgZGlzdHJpYnVpdGRlIGJldHdlZW4gYmFkLCBub3JtYWwgYW5kIG92ZXIgc2xlZXAgYmVoYXZpb3IuDQoNCioqTW9kZXJhdGUgVXNlIEdyb3VwKioNCg0KVGhpcyBncm91cCBjb25zaXN0cyBvZiA4IHVzZXJzIG9yIDI0JSBvZiB0aGUgdG90YWwgc2FtcGxlIHNpemUsIGFuZCB3ZWFycyB0aGUgZGV2aWNlIGJldHdlZW4gNSAtIDIxIGRheXMuIFVzZXJzIGluIHRoaXMgZ3JvdXAgYXJlIGxlc3MgYWN0aXZlIGFuZCB3YWxrIGZld2VyIHN0ZXBzIGNvbXBhcmVkIHRvIHRoZSDigJhIaWdoIFVzZeKAmSBncm91cCBvdmVyIHRoZSB3ZWVrZGF5cyBidXQgYWN0aXZlIGR1cmluZyB0aGUgd2Vla2VuZHMsIGJldHdlZW4gMDg6MDBBTSB0byAxOjAwUE0uIFdoaWxlIHNpZ25pZmljYW50bHkgbGVzcyBhY3RpdmUgdGhhbiB0aGUg4oCYSGlnaCBVc2XigJkgZ3JvdXAsIHRoZXkgYWxzbyBzdGljayB0byB0aGVpciByb3V0aW5lIGFuZCB0aGVpciBzbGVlcCBiZWhhdml1ciBpcyBhbHNvIHN5bXlldHJpY2FsIGRpc3RyaWJ1aXRkZSBiZXR3ZWVuIGJhZCwgbm9ybWFsIGFuZCBvdmVyIHNsZWVwIGJlaGF2aW9yLg0KDQoNCioqTG93IFVzZSBHcm91cCoqDQoNClRoaXMgZ3JvdXAgY29uc2lzdHMgb2Ygb25seSAxIHVzZXIgb3IgMyUgb2YgdGhlIHNhbXBsZSBzaXplLCB0b28gc21hbGwgdG8gcHJvdmlkZSBhbnkgbWVhbmluZ2Z1bCBhbmFseXNpcy4gTXVjaCBvZiB0aGUgdHJlbmRzIGFyZSBza2V3ZWQgYXdheSBmcm9tIGFueSByZWNvZ25pemFibGUgcGF0dGVybnMuIFdpdGggdGhpcyBpbiBtaW5kLCB0aGlzIGdyb3VwIGRpc3BsYXlzIGEgc2ltaWxhciBiZWhhdml1ciB0byB0aGUgbW9kZXJhdGUgdXNlcnMgZ3JvdXAuIE5vdCBzbGVlcCBiZWhhdml1ciB3YXMgcmVnaXN0ZXJlZCBmb3JtIHRoaXMgdXNlci4NCg0KIyBmaW5hbCByZW1hcmtzIA0KDQpCZWxsYWJlYXQncyBtaXNzaW9uIGlzIHRvIGVtcG93ZXIgd29tZW4gYnkgcHJvdmlkaW5nIHRoZW0gd2l0aCB0aGUgZGF0YSB0byBkaXNjb3ZlciB0aGVtc2VsdmVzLg0KDQpJbiBvcmRlciBmb3IgdXMgdG8gcmVzcG9uZCB0byBvdXIgYnVzaW5lc3MgdGFzayBhbmQgaGVscCBCZWxsYWJlYXQgb24gdGhlaXIgbWlzc2lvbiwgYmFzZWQgb24gb3VyIHJlc3VsdHMsIEkgd291bGQgYWR2aWNlIHRvIHVzZSBvd24gdHJhY2tpbmcgZGF0YSBmb3IgZnVydGhlciBhbmFseXNpcy4gRGF0YXNldHMgdXNlZCBoYXZlIGEgc21hbGwgc2FtcGxlIGFuZCBjYW4gYmUgYmlhc2VkIHNpbmNlIHdlIGRpZG4ndCBoYXZlIGFueSBkZW1vZ3JhcGhpYyBkZXRhaWxzIG9mIHVzZXJzLiBLbm93aW5nIHRoYXQgb3VyIG1haW4gdGFyZ2V0IGFyZSB5b3VuZyBhbmQgYWR1bHQgd29tZW4gSSB3b3VsZCBlbmNvdXJhZ2UgdG8gY29udGludWUgZmluZGluZyB0cmVuZHMgdG8gYmUgYWJsZSB0byBjcmVhdGUgYSBtYXJrZXRpbmcgc3RyYWdldHkgZm9jdXNlZCBvbiB0aGVtLg0KDQpUaGF0IGJlaW5nIHNhaWQsIGFmdGVyIG91ciBhbmFseXNpcyB3ZSBoYXZlIGZvdW5kIGRpZmZlcmVudCB0cmVuZHMgdGhhdCBtYXkgaGVscCBvdXIgb25saW5lIGNhbXBhaWduIGFuZCBpbXByb3ZlIEJlbGxhYmVhdCBhcHA6DQoNCioqMS4gRGFpbHkgbm90aWZpY2F0aW9uIGZvciBleGVyY2lzZToqKglXZSBjbGFzc2lmaWVkIHVzZXJzIGludG8gMyBjYXRlZ29yaWVzIGFuZCBzYXcgdGhhdCB0aGUgYXZlcmFnZSBvZiB1c2VycyBzbGVlcCBsZXNzIHRoYW4gOCBob3VycyBhIGRheS4gSG93ZXZlciB3ZSBzYXcgYSBiZXR0ZXIgYmVhaGl1ciBvbiBzbGVlcCBoYWJpdCB3aGVuIHVzZXIgaW5jcmVtZW50IGhlciBhY3Rpdml0eSBsZXZlbC4gV2UgY2FuIGVuY291cmFnZSBjdXN0b21lcnMgdG8gcmVhY2ggYXQgbGVhc3QgIGRhaWx5IHJlY29tbWVuZGVkIHN0ZXBzIGJ5IHNlbmRpbmcgdGhlbSBhbGFybXMgaWYgdGhleSBoYXZlbid0IHJlYWNoZWQgdGhlIHN0ZXBzIGFuZCBjcmVhdGluZyBhbHNvIHBvc3RzIG9uIG91ciBhcHAgZXhwbGFpbmluZyB0aGUgYmVuZWZpdHMgb2YgcmVhY2hpbmcgdGhhdCBnb2FsLiANCg0KKioyLiBOb3RpZmljYXRpb24gYW5kIHNsZWVwIHRlY2huaXF1ZXM6KioJSW4gb3JkZXIgdG8gcmVkdWNlIHRoZSBiYWQgc2xlZXAgYW5kIG92ZXIgc2xlZXAgYmVoYXZpdXIsIHVzZXJzIGNvdWxkIHNldCB1cCBhIGRlc2lyZWQgdGltZSB0byBnbyB0byBzbGVlcCBhbmQgcmVjZWl2ZSBhIG5vdGlmaWNhdGlvbiBtaW51dGVzIGJlZm9yZSB0byBwcmVwYXJlIHRvIHNsZWVwLiBBbHNvIG9mZmVyIGhlbHBmdWwgcmVzb3VyY2VzIHRvIGhlbHAgY3VzdG9tZXJzIHNsZWVwIC0gZXguIGJyZWF0aGluZyBhZHZpc2VzLCBwb2RjYXN0cyB3aXRoIHJlbGF4aW5nIG11c2ljLCBzbGVlcCB0ZWNobmlxdWVzLg0KDQoqKjMuIFRlY2huaWNhbCBzdXBwb3J0OiAqKiBCYXNlIG9uIHRoZSB0aGUgZGlzdHJpYnV0aW9ucywgd2UgZm91bmQgdGhhdCBtYW55IHVzZXIgZG9uJ3QgdXNlIGFsbCB0aGUgZnVuY3Rpb25hbGl0eSBvZiB0aGUgZGV2aWNlcy4gQmVsbGFiZWF0cyBjYW4gb2ZmZXIgaGVscGZ1bGwgcmVzb3VyY2VzIGFuZCByZW1pbmRlcnMgdG8gaGVscCBjdXN0b21lcnMgY29uZmlndXJhdGUgdGhlaXIgZGV2aWNlcyBhbmQgZ2V0IGFsbCB0aGUgYmVuZWZpdHMgZm9ybSB0aGVpciBwcnVjaGFzZS4NCg0KYGBge3J9DQojZXhwb3J0IGRhdGEgdG8gZXhjZWwgZm9yIHNvbWUgbWFudWFscyB2ZXJpZmljYXRpb25zLg0Kd3JpdGUudGFibGUoeCA9IGRhaWx5QWN0aXZpdHlfam9pbiwgZmlsZSA9ICJteV9maWxlLmNzdiIpDQpgYGANCg0K