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,
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