Introduction

Bellabeat is a wellness company focused on improving women’s health through innovative, technology-driven solutions. Bellabeat’s products integrate their app and suite of smart fitness devices to track and provide personalized insights towards optimal customer health and wellness.

In order to gain deeper insights into their core audience, Bellabeat Co-founder, Urška Sršen, seeks actionable data on the fitness habits of non-Bellabeat smart device users.

The Business Task

From available data from past FitBit users, this analysis seeks to address the following questions:

  • What are some trends in smart device usage?

  • How could these trends apply to Bellabeat customers?

  • How could these trends help influence Bellabeat marketing strategy?

Key Stakeholders

The key stakeholders for this analysis are:

  • Urška Sršen: Bellabeat Co-founder and CPO

  • Sandro Mur: Bellabeat Co-founder and CEO

  • Bellabeat Marketing Analytics Team: The team of data analysts responsible for collecting, analyzing, and reporting data to help guide Bellabeat’s marketing strategy.

Initial Concerns and Theories

  • The initial concern for this analysis will be accessing, cleaning, and identifying relevant data from available sources.

  • An initial theory for this analysis is that the data will be simultaneously limited and overabundant; the pool of FitBit user data may be smaller than desired, but concurrently, contain more information per user than may be necessary for this analysis.

Gathering the Data

Data Source and Credibility

The source of the data used in this analysis is the CC0: Public Domain FitBit Fitness Tracker Data by Möbius. It consists of FitBit data generated by 30 respondents to a survey distributed by Amazon Mechanical Turk. The respondents consented to the public availability of personal FitBit data including daily, hourly, and minute-level reporting of physical activity, heart rate, and sleep monitoring. The data gathered covers a 30-day period from April 12, 2016 through May 12, 2016.

Data Concerns

  • The limited respondent pool of 30 FitBit users is not large enough to represent the general population as a whole.

  • The limited respondent pool, combined with the FitBit data source, implies a bias toward existing FitBit users or those currently interested in or working toward health improvement.

  • The gathered data lacks demographic information, including but not limited to age, gender, and occupation, the presence of which could reveal additional insights.

  • These factors combine to imply a more limited application scope for this data analysis. As such, this analysis will most likely be relevant to those currently involved in health and fitness improvement or those interested in future health and fitness improvement.

Importing the Data

Setting up the initial RStudio environment

install.packages("tidyverse")
Error in install.packages : Updating loaded packages
library("tidyverse")

install.packages("janitor")
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
trying URL 'https://cran.rstudio.com/bin/windows/contrib/4.5/janitor_2.2.1.zip'
Content type 'application/zip' length 292970 bytes (286 KB)
downloaded 286 KB
package ‘janitor’ successfully unpacked and MD5 sums checked

The downloaded binary packages are in
    C:\Users\balut\AppData\Local\Temp\Rtmpm2UNwS\downloaded_packages
install.packages("tidyverse")
WARNING: Rtools is required to build R packages but is not currently installed. Please download and install the appropriate version of Rtools before proceeding:

https://cran.rstudio.com/bin/windows/Rtools/
Warning in install.packages :
  package ‘tidyverse’ is in use and will not be installed
library("janitor")
Importing dailyActivity_merged

This data contains information about users’ daily FitBit tracked activities.


daily_activity <- read.csv("dailyActivity_merged.csv")

# Use the clean_names() function to change all headers to lowercase with underscores
daily_activity <- clean_names(daily_activity)

# Gain a quick initial look at the data frame
head(daily_activity)

# Gain a structural look at the data frame
str(daily_activity)
'data.frame':   940 obs. of  15 variables:
 $ id                        : num  1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ activity_date             : chr  "4/12/2016" "4/13/2016" "4/14/2016" "4/15/2016" ...
 $ total_steps               : int  13162 10735 10460 9762 12669 9705 13019 15506 10544 9819 ...
 $ total_distance            : num  8.5 6.97 6.74 6.28 8.16 ...
 $ tracker_distance          : num  8.5 6.97 6.74 6.28 8.16 ...
 $ logged_activities_distance: num  0 0 0 0 0 0 0 0 0 0 ...
 $ very_active_distance      : num  1.88 1.57 2.44 2.14 2.71 ...
 $ moderately_active_distance: num  0.55 0.69 0.4 1.26 0.41 ...
 $ light_active_distance     : num  6.06 4.71 3.91 2.83 5.04 ...
 $ sedentary_active_distance : num  0 0 0 0 0 0 0 0 0 0 ...
 $ very_active_minutes       : int  25 21 30 29 36 38 42 50 28 19 ...
 $ fairly_active_minutes     : int  13 19 11 34 10 20 16 31 12 8 ...
 $ lightly_active_minutes    : int  328 217 181 209 221 164 233 264 205 211 ...
 $ sedentary_minutes         : int  728 776 1218 726 773 539 1149 775 818 838 ...
 $ calories                  : int  1985 1797 1776 1745 1863 1728 1921 2035 1786 1775 ...
# View the data frame
view(daily_activity)
Importing heartrate_seconds_merged

This data contains information about users’ heart rates, measured every five seconds.


heartrate_data <- read.csv("heartrate_seconds_merged.csv")

# Use the clean_names() function to change all headers to lowercase with underscores
heartrate_data <- clean_names(heartrate_data)

# Gain a quick initial look at the data frame
head(heartrate_data)

# Gain a structural look at the data frame
str(heartrate_data)
'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 ...
# View the data frame
view(heartrate_data)
  • The heartrate data features almost 2.5 million rows, so this may have to be cleaned and adjusted before analysis.
Importing sleepDay_merged

This data contains information about users’ sleep duration.


sleep_data <- read.csv("sleepDay_merged.csv")

# Use the clean_names() function to change all headers to lowercase with underscores
sleep_data <- clean_names(sleep_data)

# Gain a quick initial look at the data frame
head(sleep_data)

# Gain a structural look at the data frame
str(sleep_data)
'data.frame':   413 obs. of  5 variables:
 $ id                  : num  1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ sleep_day           : 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" ...
 $ total_sleep_records : int  1 2 1 2 1 1 1 1 1 1 ...
 $ total_minutes_asleep: int  327 384 412 340 700 304 360 325 361 430 ...
 $ total_time_in_bed   : int  346 407 442 367 712 320 377 364 384 449 ...
# View the data frame
view(sleep_data)
Importing weightLogInfo_merged

This data contains information about users’ body weight.


weight_log_data <- read.csv("weightLogInfo_merged.csv")

# Use the clean_names() function to change all headers to lowercase with underscores
weight_log_data <- clean_names(weight_log_data)

# Gain a quick initial look at the data frame
head(weight_log_data)

# Gain a structural look at the data frame
str(weight_log_data)
'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" ...
 $ weight_kg       : num  52.6 52.6 133.5 56.7 57.3 ...
 $ weight_pounds   : 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 ...
 $ is_manual_report: chr  "True" "True" "False" "True" ...
 $ log_id          : num  1.46e+12 1.46e+12 1.46e+12 1.46e+12 1.46e+12 ...
# View the data frame
view(weight_log_data)

Data Consolidation

A perusal of the data sources reveal certain redundancies in the information whose consolidation, reduction, or elimination should result in more efficient data analysis.

  • The file dailyActivity_merged.csv already contains the datasets present in dailyCalories_merged.csv, dailyIntensities_merged.csv, and dailySteps_merged.csv. To eliminate redundancy, the dailyCalories_merged, dailyIntensities_merged, and dailySteps_merged files can be removed from this analysis.

  • The hourly data records, hourlyCalories_merged.csv, hourlyIntensities_merged.csv, and hourlySteps_merged.csv contain Id and ActivityHour fields in common. To reduce the number of files required for analysis, hourlyCalories_merged, hourlyIntensities_merged, and hourlySteps_merged will be combined into the file combined_HourlyData.csv.

Merging and importing hourly data

# Using purrr::map (part of tidyverse)
# library(purrr) is not needed because it was previously loaded in tidyverse

file_paths_hourly <- list.files(path = "hourly_data", pattern = "*.csv", full.names = TRUE)
list_of_hourly_dfs <- lapply(file_paths_hourly, read_csv)
hourly_activity <- Reduce(inner_join, list_of_hourly_dfs)
write_csv(hourly_activity, "combined_HourlyData.csv")

#Use the clean_names() function to change all headers to lowercase with underscores
hourly_activity <- clean_names(hourly_activity)

# Gain a quick initial look at the data frame
head(hourly_activity)

# Gain a structural look at the data frame
str(hourly_activity)
spc_tbl_ [22,099 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
 $ id               : num [1:22099] 1.5e+09 1.5e+09 1.5e+09 1.5e+09 1.5e+09 ...
 $ activity_hour    : chr [1:22099] "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         : num [1:22099] 81 61 59 47 48 48 48 47 68 141 ...
 $ total_intensity  : num [1:22099] 20 8 7 0 0 0 0 0 13 30 ...
 $ average_intensity: num [1:22099] 0.333 0.133 0.117 0 0 ...
 $ step_total       : num [1:22099] 373 160 151 0 0 ...
 - attr(*, "spec")=
  .. cols(
  ..   Id = col_double(),
  ..   ActivityHour = col_character(),
  ..   Calories = col_double()
  .. )
 - attr(*, "problems")=<externalptr> 
# View the data frame
view(hourly_activity)
  • The newly created hourly_activity data frame contains the combined information about users’ hourly tracked activities.

Verifying Data Integrity

The previous structural looks at the data frames show that the data is currently in the correct format (character, numeric, or integer). The data frames will be checked for any missing or duplicated values.

Checking for missing values

# Check for missing values
sum(is.na(daily_activity))
[1] 0
sum(is.na(heartrate_data))
[1] 0
sum(is.na(hourly_activity))
[1] 0
sum(is.na(sleep_data))
[1] 0
sum(is.na(weight_log_data))
[1] 65
  • Checking for missing values finds that the majority of the “fat” field in weight_log_data is empty. Because of this lack of data, this analysis will focus on the “weight” attribute without consideration for the “fat” attribute.
Removing the “fat” column from weight_log_data

weight_log_data_2 <- select(weight_log_data, -fat)
  • The cleaned data is stored in the weight_log_data_2 data frame.
Checking for duplicate rows

# Checking daily_activity
daily_activity %>%
  group_by_all() %>%
  filter(n()>1)

# Checking heartrate_data
heartrate_data %>%
  group_by_all() %>%
  filter(n()>1)

# Checking hourly_activity
hourly_activity %>%
  group_by_all() %>%
  filter(n()>1)

# Checking weight_log_data
weight_log_data %>%
  group_by_all() %>%
  filter(n()>1)

# Checking sleep_data
sleep_data %>%
  group_by_all() %>%
  filter(n()>1)
NA
  • The only data frame with duplicate rows is sleep_data, which contains six duplicate rows.
Removing duplicate rows

sleep_data_clean <- distinct(sleep_data)
  • The duplicate rows have been removed from sleep_data, and the resulting data is stored in the new data frame, sleep_data_clean.

Separating Date and Time in the Data

  • Separating the time data will make it easier to analyze hourly activity data and heart rate data.

  • Separating the date data will make it easier to analyze daily activity and weight log data.

Separating the hourly_activity date-time data

# Convert the date time to 24-hour format
hourly_activity$activity_hour <- mdy_hms(hourly_activity$activity_hour)

# Separate the date and time
hourly_activity_clean <- hourly_activity %>%
  separate(activity_hour, c("date", "time"), " ")

# Change the midnight time (NA) to 00:00:01 as this is measured hourly, and the seconds will not make a difference
hourly_activity_clean$time[is.na(hourly_activity_clean$time)] <- "00:00:01"
Separating the heartrate_data date-time data

# Convert the date time to 24-hour format
heartrate_data$time <- mdy_hms(heartrate_data$time)

# Separate the date and time
heartrate_data_clean <- heartrate_data %>%
  separate(time, c("date", "time"), " ")

# Remove any missing values
heartrate_data_clean <- na.omit(heartrate_data_clean)
Separating the weight_log_data_2 date-time data

# Convert the date time to 24-hour format
weight_log_data_2$date <- mdy_hms(weight_log_data_2$date)

# Separate the date and time
weight_log_data_clean <- weight_log_data_2 %>%
  separate(date, c("date", "time"), " ")

Summarizing the Cleaned Data

Now that the data has been properly cleaned and verified, an initial summary of their contents can be examined.

Counting Unique User IDs

Determine how many unique users submitted data for each data frame.


# Count users submitting daily activity logs
n_distinct(daily_activity$id) 
[1] 33
# Count users submitting heart rate logs
n_distinct(heartrate_data_clean$id) 
[1] 14
# Count users submitting hourly activity logs
n_distinct(hourly_activity_clean$id) 
[1] 33
# Count users submitting sleep logs
n_distinct(sleep_data_clean$id) 
[1] 24
# Count users submitting weight logs
n_distinct(weight_log_data_clean$id) 
[1] 8

The number of unique users submitting fitness data varies depending on the type of data:

  • 33 unique users submitted logs for daily activity and hourly activity

  • 24 unique users submitted logs for sleep

  • 14 unique users submitted logs for heart rate

  • 8 unique users submitted logs for weight

FitBit Definitions

Some of the user data refers to terms specific to FitBit. These terms are defined as the following:

METs: Metabolic Equivalents of Task, a measurement of energy expenditure due to physical activity.

Activity levels: FitBit activity classification based on METs:

  • Sedentary = <1.5 METs

  • Lightly Active = 1.5 to 3 METs (e.g., leisurely walking or household chores)

  • Fairly Active = 3 to 6 METs (e.g., brisk walking or dancing)

  • Very Active = 6+ METs (e.g., jogging or running)

Summarizing daily activity

daily_activity %>%
  select(total_steps,
         total_distance,
         very_active_minutes,
         fairly_active_minutes,
         lightly_active_minutes,
         sedentary_minutes,
         calories) %>%
  summary()
  total_steps    total_distance   very_active_minutes
 Min.   :    0   Min.   : 0.000   Min.   :  0.00     
 1st Qu.: 3790   1st Qu.: 2.620   1st Qu.:  0.00     
 Median : 7406   Median : 5.245   Median :  4.00     
 Mean   : 7638   Mean   : 5.490   Mean   : 21.16     
 3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.: 32.00     
 Max.   :36019   Max.   :28.030   Max.   :210.00     
 fairly_active_minutes lightly_active_minutes
 Min.   :  0.00        Min.   :  0.0         
 1st Qu.:  0.00        1st Qu.:127.0         
 Median :  6.00        Median :199.0         
 Mean   : 13.56        Mean   :192.8         
 3rd Qu.: 19.00        3rd Qu.:264.0         
 Max.   :143.00        Max.   :518.0         
 sedentary_minutes    calories   
 Min.   :   0.0    Min.   :   0  
 1st Qu.: 729.8    1st Qu.:1828  
 Median :1057.5    Median :2134  
 Mean   : 991.2    Mean   :2304  
 3rd Qu.:1229.5    3rd Qu.:2793  
 Max.   :1440.0    Max.   :4900  

Daily Activity Observations

Of the 33 users who submitted daily activity data:

  • The average user accumulates 7638 steps per day.

  • The average user is active for 5.490 kilometers per day.

  • The average user is very active for 21.16 minutes per day.

  • The average user is fairly active for 13.56 minutes per day.

  • The average user is lightly active for 192.8 minutes (3.2 hours) per day.

  • The average user burns 2304 calories per day.

The activity observations show that users spend most activity time being lightly active, but with more time spent in very active exercise than in fairly active exercise, implying light, everyday activities punctuated by focused bursts of more intense activity.

Summarizing heart rate data

# Generate a general heart rate summary
heartrate_data_clean %>%
  select(value,
         time) %>%
  summary()
     value            time          
 Min.   : 36.00   Length:2483546    
 1st Qu.: 63.00   Class :character  
 Median : 73.00   Mode  :character  
 Mean   : 77.33                     
 3rd Qu.: 88.00                     
 Max.   :203.00                     

Heart Rate Data Observations

Of the 14 users who submitted heart rate data:

  • The average heart rate is 77.33 bpm (beats per minute).

  • The maximum heart rate of 203 bpm is very high (according to the American Heart Association, the maximum heart rate should be 200 bpm at 20 years of age, with the maximum lowering as age increases).

Summarizing sleep data

sleep_data_clean %>%
  select(total_minutes_asleep,
         total_time_in_bed) %>%
  summary()
 total_minutes_asleep total_time_in_bed
 Min.   : 58.0        Min.   : 61.0    
 1st Qu.:361.0        1st Qu.:403.8    
 Median :432.5        Median :463.0    
 Mean   :419.2        Mean   :458.5    
 3rd Qu.:490.0        3rd Qu.:526.0    
 Max.   :796.0        Max.   :961.0    

Sleep Data Observations

Of the 24 users who submitted sleep data:

  • The average sleep time is 419.2 minutes (~7 hours).

  • The average time in bed is 458.2 minutes (7.64 hours, or 7 hours, 38 minutes).

The difference between average time in bed and average sleep time (38 minutes) can be attributed to time lying in bed before falling asleep and/or time lying in bed after waking up.

Summarizing weight log data

weight_log_data_clean %>%
  select(weight_pounds,
         bmi) %>%
  summary()
 weight_pounds        bmi       
 Min.   :116.0   Min.   :21.45  
 1st Qu.:135.4   1st Qu.:23.96  
 Median :137.8   Median :24.39  
 Mean   :158.8   Mean   :25.19  
 3rd Qu.:187.5   3rd Qu.:25.56  
 Max.   :294.3   Max.   :47.54  

Weight Log Data Observations

  • Only 8 users submitted weight logs.

  • The average weight is 158.8 pounds.

  • The average BMI (Body Mass Index) is 25.19. A BMI of 25 to 29.9 is considered overweight; therefore, the average FitBit user could be classified as overweight.

  • Because of the small sample size, any weight data conclusions cannot be considered high in accuracy.

Data Visualizations

Relationship Between Daily Steps and Calories Burned

ggplot(data=daily_activity, aes(x=total_steps, y=calories))+
  geom_point(color="darkorange")+
  geom_smooth(color="red")+
  labs(title="Daily Steps vs Calories Burned",
       x="Total Daily Steps",
       y="Daily Calories Burned")

  • This plot shows a generally positive correlation between total daily steps and calories burned.

Comparison of Activity Intensity Types


# Prepare the data for a bar chart
activity_type_data <- daily_activity %>%
  pivot_longer(cols = c(very_active_minutes, fairly_active_minutes, lightly_active_minutes), names_to="Variable", values_to="Value")

# Reorder the bars in the chart
activity_type_data$Variable <- factor(activity_type_data$Variable, levels=c("very_active_minutes", "fairly_active_minutes", "lightly_active_minutes"))

# Create the bar chart from the prepared data "activity_type_data"
ggplot(activity_type_data, aes(x=Variable, y=Value))+
  geom_bar(stat="identity", fill="dodgerblue3")+
  labs(title="Comparison of Activity Intensity Types",
    x="Activity Intensity",
    y="Activity Minutes")

A comparison of activity intensity types shows that:

  • The vast majority of activity time is spent performing light activities.

  • More time is spent being very active than fairly active.

Examination of Activity and Days of the Week

Average daily steps

# Convert dates to POSIXct format before converting to days of the week
daily_activity$date_posixct <- as.POSIXct(daily_activity$activity_date, format="%m/%d/%Y")

# Find days of the week from date_posixct
daily_activity$dotw <- wday(daily_activity$date_posixct, label=TRUE, abbr=FALSE)

# Create the dotw_activity data frame
dotw_activity <- daily_activity %>%
  group_by(dotw) %>%
  summarise(avg_steps_dotw=as.integer(mean(total_steps)),
            vam_dotw=as.integer(mean(very_active_minutes)),
            fam_dotw=as.integer(mean(fairly_active_minutes)),
            lam_dotw=as.integer(mean(lightly_active_minutes)),
            total_minutes_dotw=as.integer(sum(vam_dotw, fam_dotw, lam_dotw)))

# Plot days of the week vs average daily steps
ggplot(dotw_activity, aes(x=dotw, y=avg_steps_dotw))+
  geom_bar(stat="identity", fill="dodgerblue")+
  labs(title="Average Steps by Day of the Week",
    x="Day of the Week",
    y="Average Daily Steps")+
  theme(axis.text.x=element_text(angle=30),
        legend.position="none")+
  geom_text(aes(label=avg_steps_dotw, vjust=1.2))

NA
NA
  • The most active step days are Saturday and Tuesday.

  • The least active step day is Sunday.

Daily active minutes

ggplot(dotw_activity, aes(x=dotw, y=total_minutes_dotw))+
  geom_bar(stat="identity", fill="dodgerblue")+
  labs(title="Total Active Minutes Daily",
    x="Day of the Week",
    y="Total Active Minutes")+
  theme(axis.text.x=element_text(angle=30),
        legend.position="none")+
  geom_text(aes(label=total_minutes_dotw, vjust=1.2))

  • The most active day of the week is Saturday.

  • The least active day of the week is Sunday.


# Pivot the data for the bar chart
dotw_intensity <- dotw_activity %>%
  pivot_longer(cols = c(vam_dotw, fam_dotw), names_to="variable", values_to="value")

# Create the bar chart from the prepared data "dotw_intensity"
ggplot(dotw_intensity, aes(x=dotw, fill=variable, y=value))+
  geom_bar(stat="identity", position="dodge")+
  labs(title="Comparison of Fairly and Very Active Minutes Daily",
    x="Day of the Week",
    y="Activity Minutes")+
  theme(axis.text.x=element_text(angle=30))+
  scale_fill_discrete(name="Activity Type", labels=c("Fairly Active", "Very Active"))+
  geom_text(aes(label=value))

  • “Very Active” minutes occur the most on Monday and the least on Sunday.

  • “Fairly Active” minutes occur the most on Saturday and the least on Thursday.


# Plot days of the week vs lightly active minutes
ggplot(dotw_activity, aes(x=dotw, y=lam_dotw))+
  geom_bar(stat="identity", fill="dodgerblue")+
  labs(title="Lightly Active Minutes Daily",
    x="Day of the Week",
    y="Lightly Active Minutes")+
  theme(axis.text.x=element_text(angle=30),
        legend.position="none")+
  geom_text(aes(label=lam_dotw, vjust=1.2))

  • “Lightly Active” minutes occur the most on Saturday and the least on Sunday.

Relationship Between Active Minutes and Calories Burned

Very active minutes
ggplot(data=daily_activity, aes(x=very_active_minutes, y=calories))+
  geom_point(color="darkorange")+
  geom_smooth(color="red")+
  labs(title="Very Active Minutes vs Calories Burned",
       x="Very Active Minutes",
       y="Calories Burned")

Fairly active minutes
ggplot(data=daily_activity, aes(x=fairly_active_minutes, y=calories))+
  geom_point(color="darkorange")+
  geom_smooth(color="red")+
  labs(title="Fairly Active Minutes vs Calories Burned",
       x="Fairly Active Minutes",
       y="Calories Burned")

Lightly active minutes
ggplot(data=daily_activity, aes(x=lightly_active_minutes, y=calories))+
  geom_point(color="darkorange")+
  geom_smooth(color="red")+
  labs(title="Lightly Active Minutes vs Calories Burned",
       x="Lightly Active Minutes",
       y="Calories Burned")

The plots of activity minutes versus calories burned demonstrate:

  • A strong positive correlation between very active minutes and calories burned.

  • An inconclusive or even slightly negative correlation between fairly active minutes and calories burned.

  • A slight correlation between lightly active minutes and calories burned.

Relationship Between Average Hourly Intensity and Calories Burned


ggplot(data=hourly_activity_clean, aes(x=average_intensity, y=calories))+
  geom_jitter(color="darkorange")+
  geom_smooth(method="gam", formula=y~s(x, bs="cs"), color="red")+
  labs(title="Average Hourly Intensity vs Calories Burned",
       x="Average Hourly Intensity",
       y="Calories Burned")

  • This plot demonstrates an even clearer positive correlation between average hourly intensity and calories burned than the plot of very active minutes versus calories burned.

Activity by Time of Day


# Prepare the hourly_activity_clean data for time analysis
hourly_activity_df <- hourly_activity_clean
hourly_activity_df$time <- hms(hourly_activity_clean$time)
hour_posix <- hourly_activity_df$time

# Group the average total intensity values by hour values
total_intensity_hourly <- hourly_activity_df %>%
  group_by(hour = hour(hour_posix)) %>%
  summarise(avg_intensity=mean(total_intensity))

# Limit the average total intensity values to 1 decimal place for ease of reading
avg_intensity_rd <- format(round(total_intensity_hourly$avg_intensity, 1))

# Plot the average total intensity per hour into a bar/column chart
ggplot(data=total_intensity_hourly, aes(x=hour, y=avg_intensity))+
  geom_col(fill="darkorange")+
  scale_x_continuous(labels=scales::date_format("%H"), breaks=unique(total_intensity_hourly$hour)) +
  labs(title="Activity Intensity by Time of Day",
       x="Time of Day",
       y="Average Total Hourly Intensity")+
  geom_text(aes(label=avg_intensity_rd, angle=60, hjust=0.5))

NA
NA

This chart shows some general activity trends:

  • The most active time of the day is evening, from 5pm through 7pm. This implies an exercise period after the work day has ended.

  • The second-most active time period during the day is 12pm through 2pm, implying an exercise period during lunch break hours.

  • The least active time period is 12am through 4am, implying minimal activity due to sleep.

Plot of User Weight Over Time (by date)


ggplot(data=weight_log_data_clean, aes(x=date, y=weight_pounds, group=id))+
  geom_line(linewidth=1.5, color="dodgerblue3")+
  labs(title="Weight (lbs) over Time",
       x="Date",
       y="Weight in Pounds")+
  annotate("text", x=I(15), y=I(0.55), label="Each line represents a unique individual's weight data", color="darkblue")+
  theme(axis.text.x = element_text(angle=90), legend.position="none")

This plot shows:

  • A very slight decrease in weight over time for two of the users.

  • A slight increase in weight for one user.

  • Inconclusive weight gain or loss for two users.

  • The data sample size is too small to generate any conclusive actionable insights.

Data Conclusions

Suggestions for Bellabeat

Information Gathering

  • The smaller-than-expected sample size for heart rate (14 out of 33 users) and weight log (8 out of 33 users) imply a need to better inform users of the importance of tracking those attributes. Bellabeat may consider incentivizing such tracking.

  • Additional user information such as age, gender, and occupational hours could help in providing more substantive data correlations and more customizable exercise plans.

Marketing Recommendations

  • Users tend to be most active on Saturday and in the early evening. Advertising and marketing should focus on messaging and timing that target those days and time periods. Examples could include social media ads focused on the 4pm time slot or on Fridays, reminding people to stay active for fitness and health in the upcoming weekend.

App Recommendations

  • One user displayed a heart rate considered very high, at 203 bpm. Bellabeat should consider adding in a visual and/or audio alert when user heart rates exceed a maximum healthy heart rate for their age.

  • The lack of consistent weight log data may be improved with dedicated app-integration and connectivity with BlueTooth-connected scales.

  • Bellabeat should consider adding in periodic reminders for users to track not just physical activity, but heart rate and weight data. Perhaps kudos, achievements, or badges could be given to users as a reward for providing more comprehensive health tracking.

  • Data strongly suggests that higher-intensity activity directly correlates with higher caloric burn rates. Bellabeat should consider encouraging custom, high-intensity exercise plans for those looking to burn the most calories.

  • Bellabeat should provide an option for app users to receive periodic reminders for daily movement and activity, as even light activity correlates with an increase in caloric burn. This would be most effective if targeted for activity during lunch breaks or at the end of the work day, as these times show the highest active intensity rates.

Ultimately, further analysis of fitness tracker users requires both a larger sample size and additional data. As is, current data is enough to provide broad guidelines for actionable steps, but further data gathering and analysis is recommended.

LS0tDQp0aXRsZTogIkdvb2dsZSBEYXRhIEFuYWx5dGljczogQmVsbGFiZWF0IENhc2UgU3R1ZHkiDQphdXRob3I6ICJKb2VsIFBlbGluYSINCmRhdGU6ICIwNy8yMy8yMDI1ICINCm91dHB1dDogaHRtbF9ub3RlYm9vaw0KLS0tDQojIyBJbnRyb2R1Y3Rpb24NCioqQmVsbGFiZWF0KiogaXMgYSB3ZWxsbmVzcyBjb21wYW55IGZvY3VzZWQgb24gaW1wcm92aW5nIHdvbWVuJ3MgaGVhbHRoIHRocm91Z2ggaW5ub3ZhdGl2ZSwgdGVjaG5vbG9neS1kcml2ZW4gc29sdXRpb25zLiBCZWxsYWJlYXQncyBwcm9kdWN0cyBpbnRlZ3JhdGUgdGhlaXIgYXBwIGFuZCBzdWl0ZSBvZiBzbWFydCBmaXRuZXNzIGRldmljZXMgdG8gdHJhY2sgYW5kIHByb3ZpZGUgcGVyc29uYWxpemVkIGluc2lnaHRzIHRvd2FyZHMgb3B0aW1hbCBjdXN0b21lciBoZWFsdGggYW5kIHdlbGxuZXNzLg0KDQpJbiBvcmRlciB0byBnYWluIGRlZXBlciBpbnNpZ2h0cyBpbnRvIHRoZWlyIGNvcmUgYXVkaWVuY2UsIEJlbGxhYmVhdCBDby1mb3VuZGVyLCBVcsWha2EgU3LFoWVuLCBzZWVrcyBhY3Rpb25hYmxlIGRhdGEgb24gdGhlIGZpdG5lc3MgaGFiaXRzIG9mIG5vbi1CZWxsYWJlYXQgc21hcnQgZGV2aWNlIHVzZXJzLg0KDQojIyMjIFRoZSBCdXNpbmVzcyBUYXNrDQpGcm9tIGF2YWlsYWJsZSBkYXRhIGZyb20gcGFzdCBGaXRCaXQgdXNlcnMsIHRoaXMgYW5hbHlzaXMgc2Vla3MgdG8gYWRkcmVzcyB0aGUgZm9sbG93aW5nIHF1ZXN0aW9uczoNCg0KKiBXaGF0IGFyZSBzb21lIHRyZW5kcyBpbiBzbWFydCBkZXZpY2UgdXNhZ2U/DQoNCiogSG93IGNvdWxkIHRoZXNlIHRyZW5kcyBhcHBseSB0byBCZWxsYWJlYXQgY3VzdG9tZXJzPw0KDQoqIEhvdyBjb3VsZCB0aGVzZSB0cmVuZHMgaGVscCBpbmZsdWVuY2UgQmVsbGFiZWF0IG1hcmtldGluZyBzdHJhdGVneT8NCg0KIyMjIyBLZXkgU3Rha2Vob2xkZXJzDQpUaGUga2V5IHN0YWtlaG9sZGVycyBmb3IgdGhpcyBhbmFseXNpcyBhcmU6DQoNCiogKipVcsWha2EgU3LFoWVuKio6IEJlbGxhYmVhdCBDby1mb3VuZGVyIGFuZCBDUE8NCg0KKiAqKlNhbmRybyBNdXIqKjogQmVsbGFiZWF0IENvLWZvdW5kZXIgYW5kIENFTw0KDQoqICoqQmVsbGFiZWF0IE1hcmtldGluZyBBbmFseXRpY3MgVGVhbSoqOiBUaGUgdGVhbSBvZiBkYXRhIGFuYWx5c3RzIHJlc3BvbnNpYmxlIGZvciBjb2xsZWN0aW5nLCBhbmFseXppbmcsIGFuZCByZXBvcnRpbmcgZGF0YSB0byBoZWxwIGd1aWRlIEJlbGxhYmVhdCdzIG1hcmtldGluZyBzdHJhdGVneS4NCg0KIyMjIyBJbml0aWFsIENvbmNlcm5zIGFuZCBUaGVvcmllcw0KKiBUaGUgaW5pdGlhbCBjb25jZXJuIGZvciB0aGlzIGFuYWx5c2lzIHdpbGwgYmUgYWNjZXNzaW5nLCBjbGVhbmluZywgYW5kIGlkZW50aWZ5aW5nIHJlbGV2YW50IGRhdGEgZnJvbSBhdmFpbGFibGUgc291cmNlcy4NCg0KKiBBbiBpbml0aWFsIHRoZW9yeSBmb3IgdGhpcyBhbmFseXNpcyBpcyB0aGF0IHRoZSBkYXRhIHdpbGwgYmUgc2ltdWx0YW5lb3VzbHkgbGltaXRlZCBhbmQgb3ZlcmFidW5kYW50OyB0aGUgcG9vbCBvZiBGaXRCaXQgdXNlciBkYXRhIG1heSBiZSBzbWFsbGVyIHRoYW4gZGVzaXJlZCwgYnV0IGNvbmN1cnJlbnRseSwgY29udGFpbiBtb3JlIGluZm9ybWF0aW9uICBwZXIgdXNlciB0aGFuIG1heSBiZSBuZWNlc3NhcnkgZm9yIHRoaXMgYW5hbHlzaXMuDQoNCiMjIEdhdGhlcmluZyB0aGUgRGF0YQ0KDQojIyMjIERhdGEgU291cmNlIGFuZCBDcmVkaWJpbGl0eQ0KVGhlIHNvdXJjZSBvZiB0aGUgZGF0YSB1c2VkIGluIHRoaXMgYW5hbHlzaXMgaXMgdGhlICoqQ0MwOiBQdWJsaWMgRG9tYWluIEZpdEJpdCBGaXRuZXNzIFRyYWNrZXIgRGF0YSBieSBNw7ZiaXVzLioqIEl0IGNvbnNpc3RzIG9mIEZpdEJpdCBkYXRhIGdlbmVyYXRlZCBieSAzMCByZXNwb25kZW50cyB0byBhIHN1cnZleSBkaXN0cmlidXRlZCBieSBBbWF6b24gTWVjaGFuaWNhbCBUdXJrLiBUaGUgcmVzcG9uZGVudHMgY29uc2VudGVkIHRvIHRoZSBwdWJsaWMgYXZhaWxhYmlsaXR5IG9mIHBlcnNvbmFsIEZpdEJpdCBkYXRhIGluY2x1ZGluZyBkYWlseSwgaG91cmx5LCBhbmQgbWludXRlLWxldmVsIHJlcG9ydGluZyBvZiBwaHlzaWNhbCBhY3Rpdml0eSwgaGVhcnQgcmF0ZSwgYW5kIHNsZWVwIG1vbml0b3JpbmcuIFRoZSBkYXRhIGdhdGhlcmVkIGNvdmVycyBhIDMwLWRheSBwZXJpb2QgZnJvbSBBcHJpbCAxMiwgMjAxNiB0aHJvdWdoIE1heSAxMiwgMjAxNi4NCg0KIyMjIyBEYXRhIENvbmNlcm5zDQoqIFRoZSBsaW1pdGVkIHJlc3BvbmRlbnQgcG9vbCBvZiAzMCBGaXRCaXQgdXNlcnMgaXMgbm90IGxhcmdlIGVub3VnaCB0byByZXByZXNlbnQgdGhlIGdlbmVyYWwgcG9wdWxhdGlvbiBhcyBhIHdob2xlLg0KDQoqIFRoZSBsaW1pdGVkIHJlc3BvbmRlbnQgcG9vbCwgY29tYmluZWQgd2l0aCB0aGUgRml0Qml0IGRhdGEgc291cmNlLCBpbXBsaWVzIGEgYmlhcyB0b3dhcmQgZXhpc3RpbmcgRml0Qml0IHVzZXJzIG9yIHRob3NlIGN1cnJlbnRseSBpbnRlcmVzdGVkIGluIG9yIHdvcmtpbmcgdG93YXJkIGhlYWx0aCBpbXByb3ZlbWVudC4NCg0KKiBUaGUgZ2F0aGVyZWQgZGF0YSBsYWNrcyBkZW1vZ3JhcGhpYyBpbmZvcm1hdGlvbiwgaW5jbHVkaW5nIGJ1dCBub3QgbGltaXRlZCB0byBhZ2UsIGdlbmRlciwgYW5kIG9jY3VwYXRpb24sIHRoZSBwcmVzZW5jZSBvZiB3aGljaCBjb3VsZCByZXZlYWwgYWRkaXRpb25hbCBpbnNpZ2h0cy4NCg0KKiBUaGVzZSBmYWN0b3JzIGNvbWJpbmUgdG8gaW1wbHkgYSBtb3JlIGxpbWl0ZWQgYXBwbGljYXRpb24gc2NvcGUgZm9yIHRoaXMgZGF0YSBhbmFseXNpcy4gQXMgc3VjaCwgdGhpcyBhbmFseXNpcyB3aWxsIG1vc3QgbGlrZWx5IGJlIHJlbGV2YW50IHRvIHRob3NlIGN1cnJlbnRseSBpbnZvbHZlZCBpbiBoZWFsdGggYW5kIGZpdG5lc3MgaW1wcm92ZW1lbnQgb3IgdGhvc2UgaW50ZXJlc3RlZCBpbiBmdXR1cmUgaGVhbHRoIGFuZCBmaXRuZXNzIGltcHJvdmVtZW50LiANCg0KIyMjIyBJbXBvcnRpbmcgdGhlIERhdGENCg0KIyMjIyMgU2V0dGluZyB1cCB0aGUgaW5pdGlhbCBSU3R1ZGlvIGVudmlyb25tZW50DQpgYGB7ciBTZXQgdXAgdGhlIGluaXRpYWwgZW52aXJvbm1lbnR9DQoNCmluc3RhbGwucGFja2FnZXMoInRpZHl2ZXJzZSIpDQpsaWJyYXJ5KCJ0aWR5dmVyc2UiKQ0KDQppbnN0YWxsLnBhY2thZ2VzKCJqYW5pdG9yIikNCmxpYnJhcnkoImphbml0b3IiKQ0KDQpgYGANCg0KIyMjIyMgSW1wb3J0aW5nIGRhaWx5QWN0aXZpdHlfbWVyZ2VkDQpUaGlzIGRhdGEgY29udGFpbnMgaW5mb3JtYXRpb24gYWJvdXQgdXNlcnMnIGRhaWx5IEZpdEJpdCB0cmFja2VkIGFjdGl2aXRpZXMuDQpgYGB7ciBDcmVhdGUgZGFpbHlfYWN0aXZpdHkgZGF0YSBmcmFtZSwgd2FybmluZz1GQUxTRX0NCg0KZGFpbHlfYWN0aXZpdHkgPC0gcmVhZC5jc3YoImRhaWx5QWN0aXZpdHlfbWVyZ2VkLmNzdiIpDQoNCiMgVXNlIHRoZSBjbGVhbl9uYW1lcygpIGZ1bmN0aW9uIHRvIGNoYW5nZSBhbGwgaGVhZGVycyB0byBsb3dlcmNhc2Ugd2l0aCB1bmRlcnNjb3Jlcw0KZGFpbHlfYWN0aXZpdHkgPC0gY2xlYW5fbmFtZXMoZGFpbHlfYWN0aXZpdHkpDQoNCiMgR2FpbiBhIHF1aWNrIGluaXRpYWwgbG9vayBhdCB0aGUgZGF0YSBmcmFtZQ0KaGVhZChkYWlseV9hY3Rpdml0eSkNCg0KIyBHYWluIGEgc3RydWN0dXJhbCBsb29rIGF0IHRoZSBkYXRhIGZyYW1lDQpzdHIoZGFpbHlfYWN0aXZpdHkpDQoNCiMgVmlldyB0aGUgZGF0YSBmcmFtZQ0KdmlldyhkYWlseV9hY3Rpdml0eSkNCg0KYGBgDQoNCiMjIyMjIEltcG9ydGluZyBoZWFydHJhdGVfc2Vjb25kc19tZXJnZWQNClRoaXMgZGF0YSBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCB1c2VycycgaGVhcnQgcmF0ZXMsIG1lYXN1cmVkIGV2ZXJ5IGZpdmUgc2Vjb25kcy4NCmBgYHtyIENyZWF0ZSBoZWFydHJhdGVfZGF0YSBkYXRhIGZyYW1lfQ0KDQpoZWFydHJhdGVfZGF0YSA8LSByZWFkLmNzdigiaGVhcnRyYXRlX3NlY29uZHNfbWVyZ2VkLmNzdiIpDQoNCiMgVXNlIHRoZSBjbGVhbl9uYW1lcygpIGZ1bmN0aW9uIHRvIGNoYW5nZSBhbGwgaGVhZGVycyB0byBsb3dlcmNhc2Ugd2l0aCB1bmRlcnNjb3Jlcw0KaGVhcnRyYXRlX2RhdGEgPC0gY2xlYW5fbmFtZXMoaGVhcnRyYXRlX2RhdGEpDQoNCiMgR2FpbiBhIHF1aWNrIGluaXRpYWwgbG9vayBhdCB0aGUgZGF0YSBmcmFtZQ0KaGVhZChoZWFydHJhdGVfZGF0YSkNCg0KIyBHYWluIGEgc3RydWN0dXJhbCBsb29rIGF0IHRoZSBkYXRhIGZyYW1lDQpzdHIoaGVhcnRyYXRlX2RhdGEpDQoNCiMgVmlldyB0aGUgZGF0YSBmcmFtZQ0KdmlldyhoZWFydHJhdGVfZGF0YSkNCg0KYGBgDQoqIFRoZSBoZWFydHJhdGUgZGF0YSBmZWF0dXJlcyBhbG1vc3QgMi41IG1pbGxpb24gcm93cywgc28gdGhpcyBtYXkgaGF2ZSB0byBiZSBjbGVhbmVkIGFuZCBhZGp1c3RlZCBiZWZvcmUgYW5hbHlzaXMuDQoNCiMjIyMjIEltcG9ydGluZyBzbGVlcERheV9tZXJnZWQNClRoaXMgZGF0YSBjb250YWlucyBpbmZvcm1hdGlvbiBhYm91dCB1c2Vycycgc2xlZXAgZHVyYXRpb24uDQpgYGB7ciBDcmVhdGUgc2xlZXBfZGF0YSBkYXRhIGZyYW1lfQ0KDQpzbGVlcF9kYXRhIDwtIHJlYWQuY3N2KCJzbGVlcERheV9tZXJnZWQuY3N2IikNCg0KIyBVc2UgdGhlIGNsZWFuX25hbWVzKCkgZnVuY3Rpb24gdG8gY2hhbmdlIGFsbCBoZWFkZXJzIHRvIGxvd2VyY2FzZSB3aXRoIHVuZGVyc2NvcmVzDQpzbGVlcF9kYXRhIDwtIGNsZWFuX25hbWVzKHNsZWVwX2RhdGEpDQoNCiMgR2FpbiBhIHF1aWNrIGluaXRpYWwgbG9vayBhdCB0aGUgZGF0YSBmcmFtZQ0KaGVhZChzbGVlcF9kYXRhKQ0KDQojIEdhaW4gYSBzdHJ1Y3R1cmFsIGxvb2sgYXQgdGhlIGRhdGEgZnJhbWUNCnN0cihzbGVlcF9kYXRhKQ0KDQojIFZpZXcgdGhlIGRhdGEgZnJhbWUNCnZpZXcoc2xlZXBfZGF0YSkNCg0KYGBgDQoNCiMjIyMjIEltcG9ydGluZyB3ZWlnaHRMb2dJbmZvX21lcmdlZA0KVGhpcyBkYXRhIGNvbnRhaW5zIGluZm9ybWF0aW9uIGFib3V0IHVzZXJzJyBib2R5IHdlaWdodC4NCmBgYHtyIENyZWF0ZSB3ZWlnaHRfbG9nX2RhdGF9DQoNCndlaWdodF9sb2dfZGF0YSA8LSByZWFkLmNzdigid2VpZ2h0TG9nSW5mb19tZXJnZWQuY3N2IikNCg0KIyBVc2UgdGhlIGNsZWFuX25hbWVzKCkgZnVuY3Rpb24gdG8gY2hhbmdlIGFsbCBoZWFkZXJzIHRvIGxvd2VyY2FzZSB3aXRoIHVuZGVyc2NvcmVzDQp3ZWlnaHRfbG9nX2RhdGEgPC0gY2xlYW5fbmFtZXMod2VpZ2h0X2xvZ19kYXRhKQ0KDQojIEdhaW4gYSBxdWljayBpbml0aWFsIGxvb2sgYXQgdGhlIGRhdGEgZnJhbWUNCmhlYWQod2VpZ2h0X2xvZ19kYXRhKQ0KDQojIEdhaW4gYSBzdHJ1Y3R1cmFsIGxvb2sgYXQgdGhlIGRhdGEgZnJhbWUNCnN0cih3ZWlnaHRfbG9nX2RhdGEpDQoNCiMgVmlldyB0aGUgZGF0YSBmcmFtZQ0Kdmlldyh3ZWlnaHRfbG9nX2RhdGEpDQoNCmBgYA0KDQojIyMjIERhdGEgQ29uc29saWRhdGlvbg0KQSBwZXJ1c2FsIG9mIHRoZSBkYXRhIHNvdXJjZXMgcmV2ZWFsIGNlcnRhaW4gcmVkdW5kYW5jaWVzIGluIHRoZSBpbmZvcm1hdGlvbiB3aG9zZSBjb25zb2xpZGF0aW9uLCByZWR1Y3Rpb24sIG9yIGVsaW1pbmF0aW9uIHNob3VsZCByZXN1bHQgaW4gbW9yZSBlZmZpY2llbnQgZGF0YSBhbmFseXNpcy4NCg0KKiBUaGUgZmlsZSAqKmRhaWx5QWN0aXZpdHlfbWVyZ2VkLmNzdioqIGFscmVhZHkgY29udGFpbnMgdGhlIGRhdGFzZXRzIHByZXNlbnQgaW4gKipkYWlseUNhbG9yaWVzX21lcmdlZC5jc3YqKiwgKipkYWlseUludGVuc2l0aWVzX21lcmdlZC5jc3YqKiwgYW5kICoqZGFpbHlTdGVwc19tZXJnZWQuY3N2KiouIFRvIGVsaW1pbmF0ZSByZWR1bmRhbmN5LCB0aGUgZGFpbHlDYWxvcmllc19tZXJnZWQsIGRhaWx5SW50ZW5zaXRpZXNfbWVyZ2VkLCBhbmQgZGFpbHlTdGVwc19tZXJnZWQgZmlsZXMgY2FuIGJlIHJlbW92ZWQgZnJvbSB0aGlzIGFuYWx5c2lzLg0KDQoqIFRoZSBob3VybHkgZGF0YSByZWNvcmRzLCAqKmhvdXJseUNhbG9yaWVzX21lcmdlZC5jc3YqKiwgKipob3VybHlJbnRlbnNpdGllc19tZXJnZWQuY3N2KiosIGFuZCAqKmhvdXJseVN0ZXBzX21lcmdlZC5jc3YqKiBjb250YWluIElkIGFuZCBBY3Rpdml0eUhvdXIgZmllbGRzIGluIGNvbW1vbi4gVG8gcmVkdWNlIHRoZSBudW1iZXIgb2YgZmlsZXMgcmVxdWlyZWQgZm9yIGFuYWx5c2lzLCBob3VybHlDYWxvcmllc19tZXJnZWQsIGhvdXJseUludGVuc2l0aWVzX21lcmdlZCwgYW5kIGhvdXJseVN0ZXBzX21lcmdlZCB3aWxsIGJlIGNvbWJpbmVkIGludG8gdGhlIGZpbGUgKipjb21iaW5lZF9Ib3VybHlEYXRhLmNzdioqLg0KDQojIyMjIyBNZXJnaW5nIGFuZCBpbXBvcnRpbmcgaG91cmx5IGRhdGENCmBgYHtyIE1lcmdlIGhvdXJseSBkYXRhc2V0c30NCg0KIyBVc2luZyBwdXJycjo6bWFwIChwYXJ0IG9mIHRpZHl2ZXJzZSkNCiMgbGlicmFyeShwdXJycikgaXMgbm90IG5lZWRlZCBiZWNhdXNlIGl0IHdhcyBwcmV2aW91c2x5IGxvYWRlZCBpbiB0aWR5dmVyc2UNCg0KZmlsZV9wYXRoc19ob3VybHkgPC0gbGlzdC5maWxlcyhwYXRoID0gImhvdXJseV9kYXRhIiwgcGF0dGVybiA9ICIqLmNzdiIsIGZ1bGwubmFtZXMgPSBUUlVFKQ0KbGlzdF9vZl9ob3VybHlfZGZzIDwtIGxhcHBseShmaWxlX3BhdGhzX2hvdXJseSwgcmVhZF9jc3YpDQpob3VybHlfYWN0aXZpdHkgPC0gUmVkdWNlKGlubmVyX2pvaW4sIGxpc3Rfb2ZfaG91cmx5X2RmcykNCndyaXRlX2Nzdihob3VybHlfYWN0aXZpdHksICJjb21iaW5lZF9Ib3VybHlEYXRhLmNzdiIpDQoNCiNVc2UgdGhlIGNsZWFuX25hbWVzKCkgZnVuY3Rpb24gdG8gY2hhbmdlIGFsbCBoZWFkZXJzIHRvIGxvd2VyY2FzZSB3aXRoIHVuZGVyc2NvcmVzDQpob3VybHlfYWN0aXZpdHkgPC0gY2xlYW5fbmFtZXMoaG91cmx5X2FjdGl2aXR5KQ0KDQojIEdhaW4gYSBxdWljayBpbml0aWFsIGxvb2sgYXQgdGhlIGRhdGEgZnJhbWUNCmhlYWQoaG91cmx5X2FjdGl2aXR5KQ0KDQojIEdhaW4gYSBzdHJ1Y3R1cmFsIGxvb2sgYXQgdGhlIGRhdGEgZnJhbWUNCnN0cihob3VybHlfYWN0aXZpdHkpDQoNCiMgVmlldyB0aGUgZGF0YSBmcmFtZQ0Kdmlldyhob3VybHlfYWN0aXZpdHkpDQpgYGANCiogVGhlIG5ld2x5IGNyZWF0ZWQgKipob3VybHlfYWN0aXZpdHkqKiBkYXRhIGZyYW1lIGNvbnRhaW5zIHRoZSBjb21iaW5lZCBpbmZvcm1hdGlvbiBhYm91dCB1c2VycycgaG91cmx5IHRyYWNrZWQgYWN0aXZpdGllcy4NCg0KIyMjIyBWZXJpZnlpbmcgRGF0YSBJbnRlZ3JpdHkNClRoZSBwcmV2aW91cyBzdHJ1Y3R1cmFsIGxvb2tzIGF0IHRoZSBkYXRhIGZyYW1lcyBzaG93IHRoYXQgdGhlIGRhdGEgaXMgY3VycmVudGx5IGluIHRoZSBjb3JyZWN0IGZvcm1hdCAoY2hhcmFjdGVyLCBudW1lcmljLCBvciBpbnRlZ2VyKS4gVGhlIGRhdGEgZnJhbWVzIHdpbGwgYmUgY2hlY2tlZCBmb3IgYW55IG1pc3Npbmcgb3IgZHVwbGljYXRlZCB2YWx1ZXMuDQoNCg0KIyMjIyMgQ2hlY2tpbmcgZm9yIG1pc3NpbmcgdmFsdWVzDQpgYGB7ciBDaGVjayBmb3IgbWlzc2luZyB2YWx1ZXN9DQoNCiMgQ2hlY2sgZm9yIG1pc3NpbmcgdmFsdWVzDQpzdW0oaXMubmEoZGFpbHlfYWN0aXZpdHkpKQ0KDQpzdW0oaXMubmEoaGVhcnRyYXRlX2RhdGEpKQ0KDQpzdW0oaXMubmEoaG91cmx5X2FjdGl2aXR5KSkNCg0Kc3VtKGlzLm5hKHNsZWVwX2RhdGEpKQ0KDQpzdW0oaXMubmEod2VpZ2h0X2xvZ19kYXRhKSkNCg0KYGBgDQoqIENoZWNraW5nIGZvciBtaXNzaW5nIHZhbHVlcyBmaW5kcyB0aGF0IHRoZSBtYWpvcml0eSBvZiB0aGUgImZhdCIgZmllbGQgaW4gKip3ZWlnaHRfbG9nX2RhdGEqKiBpcyBlbXB0eS4gQmVjYXVzZSBvZiB0aGlzIGxhY2sgb2YgZGF0YSwgdGhpcyBhbmFseXNpcyB3aWxsIGZvY3VzIG9uIHRoZSAid2VpZ2h0IiBhdHRyaWJ1dGUgd2l0aG91dCBjb25zaWRlcmF0aW9uIGZvciB0aGUgImZhdCIgYXR0cmlidXRlLg0KDQojIyMjIyBSZW1vdmluZyB0aGUgImZhdCIgY29sdW1uIGZyb20gd2VpZ2h0X2xvZ19kYXRhDQpgYGB7ciBUcmltIHRoZSB3ZWlnaHRfbG9nX2RhdGF9DQoNCndlaWdodF9sb2dfZGF0YV8yIDwtIHNlbGVjdCh3ZWlnaHRfbG9nX2RhdGEsIC1mYXQpDQoNCmBgYA0KKiBUaGUgY2xlYW5lZCBkYXRhIGlzIHN0b3JlZCBpbiB0aGUgKip3ZWlnaHRfbG9nX2RhdGFfMioqIGRhdGEgZnJhbWUuDQoNCiMjIyMjIENoZWNraW5nIGZvciBkdXBsaWNhdGUgcm93cw0KYGBge3IgQ2hlY2sgZm9yIGR1cGxpY2F0ZSByb3dzfQ0KDQojIENoZWNraW5nIGRhaWx5X2FjdGl2aXR5DQpkYWlseV9hY3Rpdml0eSAlPiUNCiAgZ3JvdXBfYnlfYWxsKCkgJT4lDQogIGZpbHRlcihuKCk+MSkNCg0KIyBDaGVja2luZyBoZWFydHJhdGVfZGF0YQ0KaGVhcnRyYXRlX2RhdGEgJT4lDQogIGdyb3VwX2J5X2FsbCgpICU+JQ0KICBmaWx0ZXIobigpPjEpDQoNCiMgQ2hlY2tpbmcgaG91cmx5X2FjdGl2aXR5DQpob3VybHlfYWN0aXZpdHkgJT4lDQogIGdyb3VwX2J5X2FsbCgpICU+JQ0KICBmaWx0ZXIobigpPjEpDQoNCiMgQ2hlY2tpbmcgd2VpZ2h0X2xvZ19kYXRhDQp3ZWlnaHRfbG9nX2RhdGEgJT4lDQogIGdyb3VwX2J5X2FsbCgpICU+JQ0KICBmaWx0ZXIobigpPjEpDQoNCiMgQ2hlY2tpbmcgc2xlZXBfZGF0YQ0Kc2xlZXBfZGF0YSAlPiUNCiAgZ3JvdXBfYnlfYWxsKCkgJT4lDQogIGZpbHRlcihuKCk+MSkNCg0KYGBgDQoqIFRoZSBvbmx5IGRhdGEgZnJhbWUgd2l0aCBkdXBsaWNhdGUgcm93cyBpcyBzbGVlcF9kYXRhLCB3aGljaCBjb250YWlucyBzaXggZHVwbGljYXRlIHJvd3MuDQoNCiMjIyMjIFJlbW92aW5nIGR1cGxpY2F0ZSByb3dzDQpgYGB7ciBSZW1vdmUgZHVwbGljYXRlIHJvd3MgaW4gc2xlZXBfZGF0YX0NCg0Kc2xlZXBfZGF0YV9jbGVhbiA8LSBkaXN0aW5jdChzbGVlcF9kYXRhKQ0KDQpgYGANCiogVGhlIGR1cGxpY2F0ZSByb3dzIGhhdmUgYmVlbiByZW1vdmVkIGZyb20gc2xlZXBfZGF0YSwgYW5kIHRoZSByZXN1bHRpbmcgZGF0YSBpcyBzdG9yZWQgaW4gdGhlIG5ldyBkYXRhIGZyYW1lLCAqKnNsZWVwX2RhdGFfY2xlYW4qKi4NCg0KIyMjIyBTZXBhcmF0aW5nIERhdGUgYW5kIFRpbWUgaW4gdGhlIERhdGENCiogU2VwYXJhdGluZyB0aGUgdGltZSBkYXRhIHdpbGwgbWFrZSBpdCBlYXNpZXIgdG8gYW5hbHl6ZSBob3VybHkgYWN0aXZpdHkgZGF0YSBhbmQgaGVhcnQgcmF0ZSBkYXRhLg0KDQoqIFNlcGFyYXRpbmcgdGhlIGRhdGUgZGF0YSB3aWxsIG1ha2UgaXQgZWFzaWVyIHRvIGFuYWx5emUgZGFpbHkgYWN0aXZpdHkgYW5kIHdlaWdodCBsb2cgZGF0YS4NCg0KIyMjIyMgU2VwYXJhdGluZyB0aGUgaG91cmx5X2FjdGl2aXR5IGRhdGUtdGltZSBkYXRhDQpgYGB7ciBTZXBhcmF0ZSB0aGUgaG91cmx5X2FjdGl2aXR5IGRhdGUgYW5kIHRpbWUgY29sdW1uc30NCg0KIyBDb252ZXJ0IHRoZSBkYXRlIHRpbWUgdG8gMjQtaG91ciBmb3JtYXQNCmhvdXJseV9hY3Rpdml0eSRhY3Rpdml0eV9ob3VyIDwtIG1keV9obXMoaG91cmx5X2FjdGl2aXR5JGFjdGl2aXR5X2hvdXIpDQoNCiMgU2VwYXJhdGUgdGhlIGRhdGUgYW5kIHRpbWUNCmhvdXJseV9hY3Rpdml0eV9jbGVhbiA8LSBob3VybHlfYWN0aXZpdHkgJT4lDQogIHNlcGFyYXRlKGFjdGl2aXR5X2hvdXIsIGMoImRhdGUiLCAidGltZSIpLCAiICIpDQoNCiMgQ2hhbmdlIHRoZSBtaWRuaWdodCB0aW1lIChOQSkgdG8gMDA6MDA6MDEgYXMgdGhpcyBpcyBtZWFzdXJlZCBob3VybHksIGFuZCB0aGUgc2Vjb25kcyB3aWxsIG5vdCBtYWtlIGEgZGlmZmVyZW5jZQ0KaG91cmx5X2FjdGl2aXR5X2NsZWFuJHRpbWVbaXMubmEoaG91cmx5X2FjdGl2aXR5X2NsZWFuJHRpbWUpXSA8LSAiMDA6MDA6MDEiDQoNCmBgYA0KDQojIyMjIyBTZXBhcmF0aW5nIHRoZSBoZWFydHJhdGVfZGF0YSBkYXRlLXRpbWUgZGF0YQ0KYGBge3IgU2VwYXJhdGUgdGhlIGhlYXJ0cmF0ZV9kYXRhIGRhdGUgYW5kIHRpbWUgY29sdW1uc30NCg0KIyBDb252ZXJ0IHRoZSBkYXRlIHRpbWUgdG8gMjQtaG91ciBmb3JtYXQNCmhlYXJ0cmF0ZV9kYXRhJHRpbWUgPC0gbWR5X2htcyhoZWFydHJhdGVfZGF0YSR0aW1lKQ0KDQojIFNlcGFyYXRlIHRoZSBkYXRlIGFuZCB0aW1lDQpoZWFydHJhdGVfZGF0YV9jbGVhbiA8LSBoZWFydHJhdGVfZGF0YSAlPiUNCiAgc2VwYXJhdGUodGltZSwgYygiZGF0ZSIsICJ0aW1lIiksICIgIikNCg0KIyBSZW1vdmUgYW55IG1pc3NpbmcgdmFsdWVzDQpoZWFydHJhdGVfZGF0YV9jbGVhbiA8LSBuYS5vbWl0KGhlYXJ0cmF0ZV9kYXRhX2NsZWFuKQ0KDQpgYGANCg0KIyMjIyMgU2VwYXJhdGluZyB0aGUgd2VpZ2h0X2xvZ19kYXRhXzIgZGF0ZS10aW1lIGRhdGENCmBgYHtyIFNlcGFyYXRlIHRoZSB3ZWlnaHRfbG9nX2RhdGFfMiBkYXRlIGFuZCB0aW1lIGNvbHVtbnN9DQoNCiMgQ29udmVydCB0aGUgZGF0ZSB0aW1lIHRvIDI0LWhvdXIgZm9ybWF0DQp3ZWlnaHRfbG9nX2RhdGFfMiRkYXRlIDwtIG1keV9obXMod2VpZ2h0X2xvZ19kYXRhXzIkZGF0ZSkNCg0KIyBTZXBhcmF0ZSB0aGUgZGF0ZSBhbmQgdGltZQ0Kd2VpZ2h0X2xvZ19kYXRhX2NsZWFuIDwtIHdlaWdodF9sb2dfZGF0YV8yICU+JQ0KICBzZXBhcmF0ZShkYXRlLCBjKCJkYXRlIiwgInRpbWUiKSwgIiAiKQ0KDQpgYGANCg0KIyMgU3VtbWFyaXppbmcgdGhlIENsZWFuZWQgRGF0YQ0KTm93IHRoYXQgdGhlIGRhdGEgaGFzIGJlZW4gcHJvcGVybHkgY2xlYW5lZCBhbmQgdmVyaWZpZWQsIGFuIGluaXRpYWwgc3VtbWFyeSBvZiB0aGVpciBjb250ZW50cyBjYW4gYmUgZXhhbWluZWQuDQoNCiMjIyMgQ291bnRpbmcgVW5pcXVlIFVzZXIgSURzDQpEZXRlcm1pbmUgaG93IG1hbnkgdW5pcXVlIHVzZXJzIHN1Ym1pdHRlZCBkYXRhIGZvciBlYWNoIGRhdGEgZnJhbWUuDQpgYGB7ciBDaGVjayBmb3IgdW5pcXVlIHVzZXIgSURzfQ0KDQojIENvdW50IHVzZXJzIHN1Ym1pdHRpbmcgZGFpbHkgYWN0aXZpdHkgbG9ncw0Kbl9kaXN0aW5jdChkYWlseV9hY3Rpdml0eSRpZCkgDQoNCiMgQ291bnQgdXNlcnMgc3VibWl0dGluZyBoZWFydCByYXRlIGxvZ3MNCm5fZGlzdGluY3QoaGVhcnRyYXRlX2RhdGFfY2xlYW4kaWQpIA0KDQojIENvdW50IHVzZXJzIHN1Ym1pdHRpbmcgaG91cmx5IGFjdGl2aXR5IGxvZ3MNCm5fZGlzdGluY3QoaG91cmx5X2FjdGl2aXR5X2NsZWFuJGlkKSANCg0KIyBDb3VudCB1c2VycyBzdWJtaXR0aW5nIHNsZWVwIGxvZ3MNCm5fZGlzdGluY3Qoc2xlZXBfZGF0YV9jbGVhbiRpZCkgDQoNCiMgQ291bnQgdXNlcnMgc3VibWl0dGluZyB3ZWlnaHQgbG9ncw0Kbl9kaXN0aW5jdCh3ZWlnaHRfbG9nX2RhdGFfY2xlYW4kaWQpIA0KDQpgYGANClRoZSBudW1iZXIgb2YgdW5pcXVlIHVzZXJzIHN1Ym1pdHRpbmcgZml0bmVzcyBkYXRhIHZhcmllcyBkZXBlbmRpbmcgb24gdGhlIHR5cGUgb2YgZGF0YToNCg0KKiAzMyB1bmlxdWUgdXNlcnMgc3VibWl0dGVkIGxvZ3MgZm9yICoqZGFpbHkgYWN0aXZpdHkqKiBhbmQgKipob3VybHkgYWN0aXZpdHkqKg0KDQoqIDI0IHVuaXF1ZSB1c2VycyBzdWJtaXR0ZWQgbG9ncyBmb3IgKipzbGVlcCoqDQoNCiogMTQgdW5pcXVlIHVzZXJzIHN1Ym1pdHRlZCBsb2dzIGZvciAqKmhlYXJ0IHJhdGUqKg0KDQoqIDggdW5pcXVlIHVzZXJzIHN1Ym1pdHRlZCBsb2dzIGZvciAqKndlaWdodCoqDQoNCiMjIyMgRml0Qml0IERlZmluaXRpb25zDQpTb21lIG9mIHRoZSB1c2VyIGRhdGEgcmVmZXJzIHRvIHRlcm1zIHNwZWNpZmljIHRvIEZpdEJpdC4gVGhlc2UgdGVybXMgYXJlIGRlZmluZWQgYXMgdGhlIGZvbGxvd2luZzoNCg0KKipNRVRzOioqICoqTWV0YWJvbGljIEVxdWl2YWxlbnRzIG9mIFRhc2ssKiogYSBtZWFzdXJlbWVudCBvZiBlbmVyZ3kgZXhwZW5kaXR1cmUgZHVlIHRvIHBoeXNpY2FsIGFjdGl2aXR5Lg0KDQoqKkFjdGl2aXR5IGxldmVsczoqKiBGaXRCaXQgYWN0aXZpdHkgY2xhc3NpZmljYXRpb24gYmFzZWQgb24gTUVUczoNCg0KKiAqKlNlZGVudGFyeSoqID0gPDEuNSBNRVRzDQoNCiogKipMaWdodGx5IEFjdGl2ZSoqID0gMS41IHRvIDMgTUVUcyAoZS5nLiwgbGVpc3VyZWx5IHdhbGtpbmcgb3IgaG91c2Vob2xkIGNob3JlcykNCg0KKiAqKkZhaXJseSBBY3RpdmUqKiA9IDMgdG8gNiBNRVRzIChlLmcuLCBicmlzayB3YWxraW5nIG9yIGRhbmNpbmcpDQoNCiogKipWZXJ5IEFjdGl2ZSoqID0gNisgTUVUcyAoZS5nLiwgam9nZ2luZyBvciBydW5uaW5nKQ0KDQojIyMjIyBTdW1tYXJpemluZyBkYWlseSBhY3Rpdml0eQ0KYGBge3IgU3VtbWFyaXplIHJlbGV2YW50IGRhdGEgZnJvbSBkYWlseV9hY3Rpdml0eX0NCg0KZGFpbHlfYWN0aXZpdHkgJT4lDQogIHNlbGVjdCh0b3RhbF9zdGVwcywNCiAgICAgICAgIHRvdGFsX2Rpc3RhbmNlLA0KICAgICAgICAgdmVyeV9hY3RpdmVfbWludXRlcywNCiAgICAgICAgIGZhaXJseV9hY3RpdmVfbWludXRlcywNCiAgICAgICAgIGxpZ2h0bHlfYWN0aXZlX21pbnV0ZXMsDQogICAgICAgICBzZWRlbnRhcnlfbWludXRlcywNCiAgICAgICAgIGNhbG9yaWVzKSAlPiUNCiAgc3VtbWFyeSgpDQoNCmBgYA0KIyMjIyBEYWlseSBBY3Rpdml0eSBPYnNlcnZhdGlvbnMNCk9mIHRoZSAzMyB1c2VycyB3aG8gc3VibWl0dGVkIGRhaWx5IGFjdGl2aXR5IGRhdGE6DQoNCiogVGhlIGF2ZXJhZ2UgdXNlciBhY2N1bXVsYXRlcyA3NjM4IHN0ZXBzIHBlciBkYXkuDQoNCiogVGhlIGF2ZXJhZ2UgdXNlciBpcyBhY3RpdmUgZm9yIDUuNDkwIGtpbG9tZXRlcnMgcGVyIGRheS4NCg0KKiBUaGUgYXZlcmFnZSB1c2VyIGlzIHZlcnkgYWN0aXZlIGZvciAyMS4xNiBtaW51dGVzIHBlciBkYXkuDQoNCiogVGhlIGF2ZXJhZ2UgdXNlciBpcyBmYWlybHkgYWN0aXZlIGZvciAxMy41NiBtaW51dGVzIHBlciBkYXkuDQoNCiogVGhlIGF2ZXJhZ2UgdXNlciBpcyBsaWdodGx5IGFjdGl2ZSBmb3IgMTkyLjggbWludXRlcyAoMy4yIGhvdXJzKSBwZXIgZGF5Lg0KDQoqIFRoZSBhdmVyYWdlIHVzZXIgYnVybnMgMjMwNCBjYWxvcmllcyBwZXIgZGF5Lg0KDQpUaGUgYWN0aXZpdHkgb2JzZXJ2YXRpb25zIHNob3cgdGhhdCB1c2VycyBzcGVuZCBtb3N0IGFjdGl2aXR5IHRpbWUgYmVpbmcgbGlnaHRseSBhY3RpdmUsIGJ1dCB3aXRoIG1vcmUgdGltZSBzcGVudCBpbiB2ZXJ5IGFjdGl2ZSBleGVyY2lzZSB0aGFuIGluIGZhaXJseSBhY3RpdmUgZXhlcmNpc2UsIGltcGx5aW5nIGxpZ2h0LCBldmVyeWRheSBhY3Rpdml0aWVzIHB1bmN0dWF0ZWQgYnkgZm9jdXNlZCBidXJzdHMgb2YgbW9yZSBpbnRlbnNlIGFjdGl2aXR5Lg0KDQojIyMjIyBTdW1tYXJpemluZyBoZWFydCByYXRlIGRhdGENCmBgYHtyIFN1bW1hcml6ZSByZWxldmFudCBkYXRhIGZyb20gaGVhcnRyYXRlX2RhdGFfY2xlYW59DQoNCiMgR2VuZXJhdGUgYSBnZW5lcmFsIGhlYXJ0IHJhdGUgc3VtbWFyeQ0KaGVhcnRyYXRlX2RhdGFfY2xlYW4gJT4lDQogIHNlbGVjdCh2YWx1ZSwNCiAgICAgICAgIHRpbWUpICU+JQ0KICBzdW1tYXJ5KCkNCg0KYGBgDQojIyMjIEhlYXJ0IFJhdGUgRGF0YSBPYnNlcnZhdGlvbnMNCk9mIHRoZSAxNCB1c2VycyB3aG8gc3VibWl0dGVkIGhlYXJ0IHJhdGUgZGF0YToNCg0KKiBUaGUgYXZlcmFnZSBoZWFydCByYXRlIGlzIDc3LjMzIGJwbSAoYmVhdHMgcGVyIG1pbnV0ZSkuDQoNCiogVGhlIG1heGltdW0gaGVhcnQgcmF0ZSBvZiAyMDMgYnBtIGlzIHZlcnkgaGlnaCAoYWNjb3JkaW5nIHRvIHRoZSBbQW1lcmljYW4gSGVhcnQgQXNzb2NpYXRpb25dKGh0dHBzOi8vd3d3LmhlYXJ0Lm9yZy9lbi9oZWFsdGh5LWxpdmluZy9maXRuZXNzL2ZpdG5lc3MtYmFzaWNzL3RhcmdldC1oZWFydC1yYXRlcyksIHRoZSBtYXhpbXVtIGhlYXJ0IHJhdGUgc2hvdWxkIGJlIDIwMCBicG0gYXQgMjAgeWVhcnMgb2YgYWdlLCB3aXRoIHRoZSBtYXhpbXVtIGxvd2VyaW5nIGFzIGFnZSBpbmNyZWFzZXMpLg0KDQoNCiMjIyMjIFN1bW1hcml6aW5nIHNsZWVwIGRhdGENCmBgYHtyIFN1bW1hcml6ZSByZWxldmFudCBkYXRhIGZyb20gc2xlZXBfZGF0YV9jbGVhbn0NCg0Kc2xlZXBfZGF0YV9jbGVhbiAlPiUNCiAgc2VsZWN0KHRvdGFsX21pbnV0ZXNfYXNsZWVwLA0KICAgICAgICAgdG90YWxfdGltZV9pbl9iZWQpICU+JQ0KICBzdW1tYXJ5KCkNCg0KYGBgDQojIyMjIFNsZWVwIERhdGEgT2JzZXJ2YXRpb25zDQpPZiB0aGUgMjQgdXNlcnMgd2hvIHN1Ym1pdHRlZCBzbGVlcCBkYXRhOg0KDQoqIFRoZSBhdmVyYWdlIHNsZWVwIHRpbWUgaXMgNDE5LjIgbWludXRlcyAofjcgaG91cnMpLg0KDQoqIFRoZSBhdmVyYWdlIHRpbWUgaW4gYmVkIGlzIDQ1OC4yIG1pbnV0ZXMgKDcuNjQgaG91cnMsIG9yIDcgaG91cnMsIDM4IG1pbnV0ZXMpLg0KDQpUaGUgZGlmZmVyZW5jZSBiZXR3ZWVuIGF2ZXJhZ2UgdGltZSBpbiBiZWQgYW5kIGF2ZXJhZ2Ugc2xlZXAgdGltZSAoMzggbWludXRlcykgY2FuIGJlIGF0dHJpYnV0ZWQgdG8gdGltZSBseWluZyBpbiBiZWQgYmVmb3JlIGZhbGxpbmcgYXNsZWVwIGFuZC9vciB0aW1lIGx5aW5nIGluIGJlZCBhZnRlciB3YWtpbmcgdXAuDQoNCiMjIyMjIFN1bW1hcml6aW5nIHdlaWdodCBsb2cgZGF0YQ0KYGBge3IgU3VtbWFyaXplIHJlbGV2YW50IGRhdGEgZnJvbSB3ZWlnaHRfbG9nX2RhdGFfY2xlYW59DQoNCndlaWdodF9sb2dfZGF0YV9jbGVhbiAlPiUNCiAgc2VsZWN0KHdlaWdodF9wb3VuZHMsDQogICAgICAgICBibWkpICU+JQ0KICBzdW1tYXJ5KCkNCg0KYGBgDQojIyMjIFdlaWdodCBMb2cgRGF0YSBPYnNlcnZhdGlvbnMNCiogT25seSA4IHVzZXJzIHN1Ym1pdHRlZCB3ZWlnaHQgbG9ncy4NCg0KKiBUaGUgYXZlcmFnZSB3ZWlnaHQgaXMgMTU4LjggcG91bmRzLg0KDQoqIFRoZSBhdmVyYWdlIEJNSSAoQm9keSBNYXNzIEluZGV4KSBpcyAyNS4xOS4gQSBCTUkgb2YgMjUgdG8gMjkuOSBpcyBjb25zaWRlcmVkIG92ZXJ3ZWlnaHQ7IHRoZXJlZm9yZSwgdGhlIGF2ZXJhZ2UgRml0Qml0IHVzZXIgY291bGQgYmUgY2xhc3NpZmllZCBhcyBvdmVyd2VpZ2h0Lg0KDQoqIEJlY2F1c2Ugb2YgdGhlIHNtYWxsIHNhbXBsZSBzaXplLCBhbnkgd2VpZ2h0IGRhdGEgY29uY2x1c2lvbnMgY2Fubm90IGJlIGNvbnNpZGVyZWQgaGlnaCBpbiBhY2N1cmFjeS4NCg0KDQojIyBEYXRhIFZpc3VhbGl6YXRpb25zDQoNCiMjIyMgUmVsYXRpb25zaGlwIEJldHdlZW4gRGFpbHkgU3RlcHMgYW5kIENhbG9yaWVzIEJ1cm5lZA0KYGBge3IgZ2dwbG90IHRvdGFsX3N0ZXBzIHZzIGNhbG9yaWVzfQ0KZ2dwbG90KGRhdGE9ZGFpbHlfYWN0aXZpdHksIGFlcyh4PXRvdGFsX3N0ZXBzLCB5PWNhbG9yaWVzKSkrDQogIGdlb21fcG9pbnQoY29sb3I9ImRhcmtvcmFuZ2UiKSsNCiAgZ2VvbV9zbW9vdGgoY29sb3I9InJlZCIpKw0KICBsYWJzKHRpdGxlPSJEYWlseSBTdGVwcyB2cyBDYWxvcmllcyBCdXJuZWQiLA0KICAgICAgIHg9IlRvdGFsIERhaWx5IFN0ZXBzIiwNCiAgICAgICB5PSJEYWlseSBDYWxvcmllcyBCdXJuZWQiKQ0KDQpgYGANCiogVGhpcyBwbG90IHNob3dzIGEgZ2VuZXJhbGx5IHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdG90YWwgZGFpbHkgc3RlcHMgYW5kIGNhbG9yaWVzIGJ1cm5lZC4NCg0KIyMjIyBDb21wYXJpc29uIG9mIEFjdGl2aXR5IEludGVuc2l0eSBUeXBlcw0KYGBge3IgZ2dwbG90IGFjdGl2aXR5IGludGVuc2l0eSB0eXBlc30NCg0KIyBQcmVwYXJlIHRoZSBkYXRhIGZvciBhIGJhciBjaGFydA0KYWN0aXZpdHlfdHlwZV9kYXRhIDwtIGRhaWx5X2FjdGl2aXR5ICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGModmVyeV9hY3RpdmVfbWludXRlcywgZmFpcmx5X2FjdGl2ZV9taW51dGVzLCBsaWdodGx5X2FjdGl2ZV9taW51dGVzKSwgbmFtZXNfdG89IlZhcmlhYmxlIiwgdmFsdWVzX3RvPSJWYWx1ZSIpDQoNCiMgUmVvcmRlciB0aGUgYmFycyBpbiB0aGUgY2hhcnQNCmFjdGl2aXR5X3R5cGVfZGF0YSRWYXJpYWJsZSA8LSBmYWN0b3IoYWN0aXZpdHlfdHlwZV9kYXRhJFZhcmlhYmxlLCBsZXZlbHM9YygidmVyeV9hY3RpdmVfbWludXRlcyIsICJmYWlybHlfYWN0aXZlX21pbnV0ZXMiLCAibGlnaHRseV9hY3RpdmVfbWludXRlcyIpKQ0KDQojIENyZWF0ZSB0aGUgYmFyIGNoYXJ0IGZyb20gdGhlIHByZXBhcmVkIGRhdGEgImFjdGl2aXR5X3R5cGVfZGF0YSINCmdncGxvdChhY3Rpdml0eV90eXBlX2RhdGEsIGFlcyh4PVZhcmlhYmxlLCB5PVZhbHVlKSkrDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0iZG9kZ2VyYmx1ZTMiKSsNCiAgbGFicyh0aXRsZT0iQ29tcGFyaXNvbiBvZiBBY3Rpdml0eSBJbnRlbnNpdHkgVHlwZXMiLA0KICAgIHg9IkFjdGl2aXR5IEludGVuc2l0eSIsDQogICAgeT0iQWN0aXZpdHkgTWludXRlcyIpDQoNCmBgYA0KQSBjb21wYXJpc29uIG9mIGFjdGl2aXR5IGludGVuc2l0eSB0eXBlcyBzaG93cyB0aGF0Og0KDQoqIFRoZSB2YXN0IG1ham9yaXR5IG9mIGFjdGl2aXR5IHRpbWUgaXMgc3BlbnQgcGVyZm9ybWluZyBsaWdodCBhY3Rpdml0aWVzLg0KDQoqIE1vcmUgdGltZSBpcyBzcGVudCBiZWluZyB2ZXJ5IGFjdGl2ZSB0aGFuIGZhaXJseSBhY3RpdmUuDQoNCiMjIyMgRXhhbWluYXRpb24gb2YgQWN0aXZpdHkgYW5kIERheXMgb2YgdGhlIFdlZWsNCiMjIyMjIEF2ZXJhZ2UgZGFpbHkgc3RlcHMNCmBgYHtyIENvbXBhcmUgYXZlcmFnZSBkYWlseSBzdGVwcyBieSBkYXkgb2YgdGhlIHdlZWt9DQoNCiMgQ29udmVydCBkYXRlcyB0byBQT1NJWGN0IGZvcm1hdCBiZWZvcmUgY29udmVydGluZyB0byBkYXlzIG9mIHRoZSB3ZWVrDQpkYWlseV9hY3Rpdml0eSRkYXRlX3Bvc2l4Y3QgPC0gYXMuUE9TSVhjdChkYWlseV9hY3Rpdml0eSRhY3Rpdml0eV9kYXRlLCBmb3JtYXQ9IiVtLyVkLyVZIikNCg0KIyBGaW5kIGRheXMgb2YgdGhlIHdlZWsgZnJvbSBkYXRlX3Bvc2l4Y3QNCmRhaWx5X2FjdGl2aXR5JGRvdHcgPC0gd2RheShkYWlseV9hY3Rpdml0eSRkYXRlX3Bvc2l4Y3QsIGxhYmVsPVRSVUUsIGFiYnI9RkFMU0UpDQoNCiMgQ3JlYXRlIHRoZSBkb3R3X2FjdGl2aXR5IGRhdGEgZnJhbWUNCmRvdHdfYWN0aXZpdHkgPC0gZGFpbHlfYWN0aXZpdHkgJT4lDQogIGdyb3VwX2J5KGRvdHcpICU+JQ0KICBzdW1tYXJpc2UoYXZnX3N0ZXBzX2RvdHc9YXMuaW50ZWdlcihtZWFuKHRvdGFsX3N0ZXBzKSksDQogICAgICAgICAgICB2YW1fZG90dz1hcy5pbnRlZ2VyKG1lYW4odmVyeV9hY3RpdmVfbWludXRlcykpLA0KICAgICAgICAgICAgZmFtX2RvdHc9YXMuaW50ZWdlcihtZWFuKGZhaXJseV9hY3RpdmVfbWludXRlcykpLA0KICAgICAgICAgICAgbGFtX2RvdHc9YXMuaW50ZWdlcihtZWFuKGxpZ2h0bHlfYWN0aXZlX21pbnV0ZXMpKSwNCiAgICAgICAgICAgIHRvdGFsX21pbnV0ZXNfZG90dz1hcy5pbnRlZ2VyKHN1bSh2YW1fZG90dywgZmFtX2RvdHcsIGxhbV9kb3R3KSkpDQoNCiMgUGxvdCBkYXlzIG9mIHRoZSB3ZWVrIHZzIGF2ZXJhZ2UgZGFpbHkgc3RlcHMNCmdncGxvdChkb3R3X2FjdGl2aXR5LCBhZXMoeD1kb3R3LCB5PWF2Z19zdGVwc19kb3R3KSkrDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0iZG9kZ2VyYmx1ZSIpKw0KICBsYWJzKHRpdGxlPSJBdmVyYWdlIFN0ZXBzIGJ5IERheSBvZiB0aGUgV2VlayIsDQogICAgeD0iRGF5IG9mIHRoZSBXZWVrIiwNCiAgICB5PSJBdmVyYWdlIERhaWx5IFN0ZXBzIikrDQogIHRoZW1lKGF4aXMudGV4dC54PWVsZW1lbnRfdGV4dChhbmdsZT0zMCksDQogICAgICAgIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpKw0KICBnZW9tX3RleHQoYWVzKGxhYmVsPWF2Z19zdGVwc19kb3R3LCB2anVzdD0xLjIpKQ0KDQoNCmBgYA0KKiBUaGUgbW9zdCBhY3RpdmUgc3RlcCBkYXlzIGFyZSBTYXR1cmRheSBhbmQgVHVlc2RheS4NCg0KKiBUaGUgbGVhc3QgYWN0aXZlIHN0ZXAgZGF5IGlzIFN1bmRheS4NCg0KIyMjIyMgRGFpbHkgYWN0aXZlIG1pbnV0ZXMNCmBgYHtyIENvbXBhcmUgdG90YWwgYWN0aXZlIG1pbnV0ZXMgYnkgZGF5IG9mIHRoZSB3ZWVrIH0NCg0KZ2dwbG90KGRvdHdfYWN0aXZpdHksIGFlcyh4PWRvdHcsIHk9dG90YWxfbWludXRlc19kb3R3KSkrDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0iZG9kZ2VyYmx1ZSIpKw0KICBsYWJzKHRpdGxlPSJUb3RhbCBBY3RpdmUgTWludXRlcyBEYWlseSIsDQogICAgeD0iRGF5IG9mIHRoZSBXZWVrIiwNCiAgICB5PSJUb3RhbCBBY3RpdmUgTWludXRlcyIpKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9MzApLA0KICAgICAgICBsZWdlbmQucG9zaXRpb249Im5vbmUiKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD10b3RhbF9taW51dGVzX2RvdHcsIHZqdXN0PTEuMikpDQoNCmBgYA0KKiBUaGUgbW9zdCBhY3RpdmUgZGF5IG9mIHRoZSB3ZWVrIGlzIFNhdHVyZGF5Lg0KDQoqIFRoZSBsZWFzdCBhY3RpdmUgZGF5IG9mIHRoZSB3ZWVrIGlzIFN1bmRheS4NCg0KYGBge3IgQ29tcGFyZSB2ZXJ5L2ZhaXJseSBhY3RpdmUgbWludXRlcyBieSBkYXkgb2YgdGhlIHdlZWsgfQ0KDQojIFBpdm90IHRoZSBkYXRhIGZvciB0aGUgYmFyIGNoYXJ0DQpkb3R3X2ludGVuc2l0eSA8LSBkb3R3X2FjdGl2aXR5ICU+JQ0KICBwaXZvdF9sb25nZXIoY29scyA9IGModmFtX2RvdHcsIGZhbV9kb3R3KSwgbmFtZXNfdG89InZhcmlhYmxlIiwgdmFsdWVzX3RvPSJ2YWx1ZSIpDQoNCiMgQ3JlYXRlIHRoZSBiYXIgY2hhcnQgZnJvbSB0aGUgcHJlcGFyZWQgZGF0YSAiZG90d19pbnRlbnNpdHkiDQpnZ3Bsb3QoZG90d19pbnRlbnNpdHksIGFlcyh4PWRvdHcsIGZpbGw9dmFyaWFibGUsIHk9dmFsdWUpKSsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZG9kZ2UiKSsNCiAgbGFicyh0aXRsZT0iQ29tcGFyaXNvbiBvZiBGYWlybHkgYW5kIFZlcnkgQWN0aXZlIE1pbnV0ZXMgRGFpbHkiLA0KICAgIHg9IkRheSBvZiB0aGUgV2VlayIsDQogICAgeT0iQWN0aXZpdHkgTWludXRlcyIpKw0KICB0aGVtZShheGlzLnRleHQueD1lbGVtZW50X3RleHQoYW5nbGU9MzApKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lPSJBY3Rpdml0eSBUeXBlIiwgbGFiZWxzPWMoIkZhaXJseSBBY3RpdmUiLCAiVmVyeSBBY3RpdmUiKSkrDQogIGdlb21fdGV4dChhZXMobGFiZWw9dmFsdWUpKQ0KYGBgDQoqICJWZXJ5IEFjdGl2ZSIgbWludXRlcyBvY2N1ciB0aGUgbW9zdCBvbiBNb25kYXkgYW5kIHRoZSBsZWFzdCBvbiBTdW5kYXkuDQoNCiogIkZhaXJseSBBY3RpdmUiIG1pbnV0ZXMgb2NjdXIgdGhlIG1vc3Qgb24gU2F0dXJkYXkgYW5kIHRoZSBsZWFzdCBvbiBUaHVyc2RheS4NCg0KYGBge3IgQ29tcGFyZSBsaWdodGx5IGFjdGl2ZSBtaW51dGVzIGJ5IGRheSBvZiB0aGUgd2VlayB9DQoNCiMgUGxvdCBkYXlzIG9mIHRoZSB3ZWVrIHZzIGxpZ2h0bHkgYWN0aXZlIG1pbnV0ZXMNCmdncGxvdChkb3R3X2FjdGl2aXR5LCBhZXMoeD1kb3R3LCB5PWxhbV9kb3R3KSkrDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IiwgZmlsbD0iZG9kZ2VyYmx1ZSIpKw0KICBsYWJzKHRpdGxlPSJMaWdodGx5IEFjdGl2ZSBNaW51dGVzIERhaWx5IiwNCiAgICB4PSJEYXkgb2YgdGhlIFdlZWsiLA0KICAgIHk9IkxpZ2h0bHkgQWN0aXZlIE1pbnV0ZXMiKSsNCiAgdGhlbWUoYXhpcy50ZXh0Lng9ZWxlbWVudF90ZXh0KGFuZ2xlPTMwKSwNCiAgICAgICAgbGVnZW5kLnBvc2l0aW9uPSJub25lIikrDQogIGdlb21fdGV4dChhZXMobGFiZWw9bGFtX2RvdHcsIHZqdXN0PTEuMikpDQoNCmBgYA0KKiAiTGlnaHRseSBBY3RpdmUiIG1pbnV0ZXMgb2NjdXIgdGhlIG1vc3Qgb24gU2F0dXJkYXkgYW5kIHRoZSBsZWFzdCBvbiBTdW5kYXkuDQoNCiMjIyMgUmVsYXRpb25zaGlwIEJldHdlZW4gQWN0aXZlIE1pbnV0ZXMgYW5kIENhbG9yaWVzIEJ1cm5lZA0KIyMjIyMgVmVyeSBhY3RpdmUgbWludXRlcw0KYGBge3IgZ2dwbG90IHZlcnlfYWN0aXZlX21pbnV0ZXMgdnMgY2Fsb3JpZXN9DQpnZ3Bsb3QoZGF0YT1kYWlseV9hY3Rpdml0eSwgYWVzKHg9dmVyeV9hY3RpdmVfbWludXRlcywgeT1jYWxvcmllcykpKw0KICBnZW9tX3BvaW50KGNvbG9yPSJkYXJrb3JhbmdlIikrDQogIGdlb21fc21vb3RoKGNvbG9yPSJyZWQiKSsNCiAgbGFicyh0aXRsZT0iVmVyeSBBY3RpdmUgTWludXRlcyB2cyBDYWxvcmllcyBCdXJuZWQiLA0KICAgICAgIHg9IlZlcnkgQWN0aXZlIE1pbnV0ZXMiLA0KICAgICAgIHk9IkNhbG9yaWVzIEJ1cm5lZCIpDQoNCmBgYA0KDQojIyMjIyBGYWlybHkgYWN0aXZlIG1pbnV0ZXMNCmBgYHtyIGdncGxvdCBmYWlybHlfYWN0aXZlX21pbnV0ZXMgdnMgY2Fsb3JpZXN9DQpnZ3Bsb3QoZGF0YT1kYWlseV9hY3Rpdml0eSwgYWVzKHg9ZmFpcmx5X2FjdGl2ZV9taW51dGVzLCB5PWNhbG9yaWVzKSkrDQogIGdlb21fcG9pbnQoY29sb3I9ImRhcmtvcmFuZ2UiKSsNCiAgZ2VvbV9zbW9vdGgoY29sb3I9InJlZCIpKw0KICBsYWJzKHRpdGxlPSJGYWlybHkgQWN0aXZlIE1pbnV0ZXMgdnMgQ2Fsb3JpZXMgQnVybmVkIiwNCiAgICAgICB4PSJGYWlybHkgQWN0aXZlIE1pbnV0ZXMiLA0KICAgICAgIHk9IkNhbG9yaWVzIEJ1cm5lZCIpDQoNCmBgYA0KDQojIyMjIyBMaWdodGx5IGFjdGl2ZSBtaW51dGVzDQpgYGB7ciBnZ3Bsb3QgbGlnaHRseV9hY3RpdmVfbWludXRlcyB2cyBjYWxvcmllc30NCmdncGxvdChkYXRhPWRhaWx5X2FjdGl2aXR5LCBhZXMoeD1saWdodGx5X2FjdGl2ZV9taW51dGVzLCB5PWNhbG9yaWVzKSkrDQogIGdlb21fcG9pbnQoY29sb3I9ImRhcmtvcmFuZ2UiKSsNCiAgZ2VvbV9zbW9vdGgoY29sb3I9InJlZCIpKw0KICBsYWJzKHRpdGxlPSJMaWdodGx5IEFjdGl2ZSBNaW51dGVzIHZzIENhbG9yaWVzIEJ1cm5lZCIsDQogICAgICAgeD0iTGlnaHRseSBBY3RpdmUgTWludXRlcyIsDQogICAgICAgeT0iQ2Fsb3JpZXMgQnVybmVkIikNCg0KYGBgDQpUaGUgcGxvdHMgb2YgYWN0aXZpdHkgbWludXRlcyB2ZXJzdXMgY2Fsb3JpZXMgYnVybmVkIGRlbW9uc3RyYXRlOg0KDQoqIEEgc3Ryb25nIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gdmVyeSBhY3RpdmUgbWludXRlcyBhbmQgY2Fsb3JpZXMgYnVybmVkLg0KDQoqIEFuIGluY29uY2x1c2l2ZSBvciBldmVuIHNsaWdodGx5IG5lZ2F0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gZmFpcmx5IGFjdGl2ZSBtaW51dGVzIGFuZCBjYWxvcmllcyBidXJuZWQuDQoNCiogQSBzbGlnaHQgY29ycmVsYXRpb24gYmV0d2VlbiBsaWdodGx5IGFjdGl2ZSBtaW51dGVzIGFuZCBjYWxvcmllcyBidXJuZWQuDQoNCiMjIyMgUmVsYXRpb25zaGlwIEJldHdlZW4gQXZlcmFnZSBIb3VybHkgSW50ZW5zaXR5IGFuZCBDYWxvcmllcyBCdXJuZWQNCmBgYHtyIGdncGxvdCBhdmVyYWdlX2ludGVuc2l0eSB2cyBjYWxvcmllc30NCg0KZ2dwbG90KGRhdGE9aG91cmx5X2FjdGl2aXR5X2NsZWFuLCBhZXMoeD1hdmVyYWdlX2ludGVuc2l0eSwgeT1jYWxvcmllcykpKw0KICBnZW9tX2ppdHRlcihjb2xvcj0iZGFya29yYW5nZSIpKw0KICBnZW9tX3Ntb290aChtZXRob2Q9ImdhbSIsIGZvcm11bGE9eX5zKHgsIGJzPSJjcyIpLCBjb2xvcj0icmVkIikrDQogIGxhYnModGl0bGU9IkF2ZXJhZ2UgSG91cmx5IEludGVuc2l0eSB2cyBDYWxvcmllcyBCdXJuZWQiLA0KICAgICAgIHg9IkF2ZXJhZ2UgSG91cmx5IEludGVuc2l0eSIsDQogICAgICAgeT0iQ2Fsb3JpZXMgQnVybmVkIikNCg0KYGBgDQoqIFRoaXMgcGxvdCBkZW1vbnN0cmF0ZXMgYW4gZXZlbiBjbGVhcmVyIHBvc2l0aXZlIGNvcnJlbGF0aW9uIGJldHdlZW4gYXZlcmFnZSBob3VybHkgaW50ZW5zaXR5IGFuZCBjYWxvcmllcyBidXJuZWQgdGhhbiB0aGUgcGxvdCBvZiB2ZXJ5IGFjdGl2ZSBtaW51dGVzIHZlcnN1cyBjYWxvcmllcyBidXJuZWQuDQoNCiMjIyMgQWN0aXZpdHkgYnkgVGltZSBvZiBEYXkNCmBgYHtyIGdncGxvdCB0aW1lIHZzIHRvdGFsX2ludGVuc2l0eX0NCg0KIyBQcmVwYXJlIHRoZSBob3VybHlfYWN0aXZpdHlfY2xlYW4gZGF0YSBmb3IgdGltZSBhbmFseXNpcw0KaG91cmx5X2FjdGl2aXR5X2RmIDwtIGhvdXJseV9hY3Rpdml0eV9jbGVhbg0KaG91cmx5X2FjdGl2aXR5X2RmJHRpbWUgPC0gaG1zKGhvdXJseV9hY3Rpdml0eV9jbGVhbiR0aW1lKQ0KaG91cl9wb3NpeCA8LSBob3VybHlfYWN0aXZpdHlfZGYkdGltZQ0KDQojIEdyb3VwIHRoZSBhdmVyYWdlIHRvdGFsIGludGVuc2l0eSB2YWx1ZXMgYnkgaG91ciB2YWx1ZXMNCnRvdGFsX2ludGVuc2l0eV9ob3VybHkgPC0gaG91cmx5X2FjdGl2aXR5X2RmICU+JQ0KICBncm91cF9ieShob3VyID0gaG91cihob3VyX3Bvc2l4KSkgJT4lDQogIHN1bW1hcmlzZShhdmdfaW50ZW5zaXR5PW1lYW4odG90YWxfaW50ZW5zaXR5KSkNCg0KIyBMaW1pdCB0aGUgYXZlcmFnZSB0b3RhbCBpbnRlbnNpdHkgdmFsdWVzIHRvIDEgZGVjaW1hbCBwbGFjZSBmb3IgZWFzZSBvZiByZWFkaW5nDQphdmdfaW50ZW5zaXR5X3JkIDwtIGZvcm1hdChyb3VuZCh0b3RhbF9pbnRlbnNpdHlfaG91cmx5JGF2Z19pbnRlbnNpdHksIDEpKQ0KDQojIFBsb3QgdGhlIGF2ZXJhZ2UgdG90YWwgaW50ZW5zaXR5IHBlciBob3VyIGludG8gYSBiYXIvY29sdW1uIGNoYXJ0DQpnZ3Bsb3QoZGF0YT10b3RhbF9pbnRlbnNpdHlfaG91cmx5LCBhZXMoeD1ob3VyLCB5PWF2Z19pbnRlbnNpdHkpKSsNCiAgZ2VvbV9jb2woZmlsbD0iZGFya29yYW5nZSIpKw0KICBzY2FsZV94X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6ZGF0ZV9mb3JtYXQoIiVIIiksIGJyZWFrcz11bmlxdWUodG90YWxfaW50ZW5zaXR5X2hvdXJseSRob3VyKSkgKw0KICBsYWJzKHRpdGxlPSJBY3Rpdml0eSBJbnRlbnNpdHkgYnkgVGltZSBvZiBEYXkiLA0KICAgICAgIHg9IlRpbWUgb2YgRGF5IiwNCiAgICAgICB5PSJBdmVyYWdlIFRvdGFsIEhvdXJseSBJbnRlbnNpdHkiKSsNCiAgZ2VvbV90ZXh0KGFlcyhsYWJlbD1hdmdfaW50ZW5zaXR5X3JkLCBhbmdsZT02MCwgaGp1c3Q9MC41KSkNCiAgDQoNCmBgYA0KVGhpcyBjaGFydCBzaG93cyBzb21lIGdlbmVyYWwgYWN0aXZpdHkgdHJlbmRzOg0KDQoqIFRoZSBtb3N0IGFjdGl2ZSB0aW1lIG9mIHRoZSBkYXkgaXMgZXZlbmluZywgZnJvbSA1cG0gdGhyb3VnaCA3cG0uIFRoaXMgaW1wbGllcyBhbiBleGVyY2lzZSBwZXJpb2QgYWZ0ZXIgdGhlIHdvcmsgZGF5IGhhcyBlbmRlZC4NCg0KKiBUaGUgc2Vjb25kLW1vc3QgYWN0aXZlIHRpbWUgcGVyaW9kIGR1cmluZyB0aGUgZGF5IGlzIDEycG0gdGhyb3VnaCAycG0sIGltcGx5aW5nIGFuIGV4ZXJjaXNlIHBlcmlvZCBkdXJpbmcgbHVuY2ggYnJlYWsgaG91cnMuDQoNCiogVGhlIGxlYXN0IGFjdGl2ZSB0aW1lIHBlcmlvZCBpcyAxMmFtIHRocm91Z2ggNGFtLCBpbXBseWluZyBtaW5pbWFsIGFjdGl2aXR5IGR1ZSB0byBzbGVlcC4NCg0KDQojIyMjIFBsb3Qgb2YgVXNlciBXZWlnaHQgT3ZlciBUaW1lIChieSBkYXRlKQ0KYGBge3IgZ2dwbG90IHdlaWdodF9wb3VuZHMgdnMgZGF0ZX0NCg0KZ2dwbG90KGRhdGE9d2VpZ2h0X2xvZ19kYXRhX2NsZWFuLCBhZXMoeD1kYXRlLCB5PXdlaWdodF9wb3VuZHMsIGdyb3VwPWlkKSkrDQogIGdlb21fbGluZShsaW5ld2lkdGg9MS41LCBjb2xvcj0iZG9kZ2VyYmx1ZTMiKSsNCiAgbGFicyh0aXRsZT0iV2VpZ2h0IChsYnMpIG92ZXIgVGltZSIsDQogICAgICAgeD0iRGF0ZSIsDQogICAgICAgeT0iV2VpZ2h0IGluIFBvdW5kcyIpKw0KICBhbm5vdGF0ZSgidGV4dCIsIHg9SSgxNSksIHk9SSgwLjU1KSwgbGFiZWw9IkVhY2ggbGluZSByZXByZXNlbnRzIGEgdW5pcXVlIGluZGl2aWR1YWwncyB3ZWlnaHQgZGF0YSIsIGNvbG9yPSJkYXJrYmx1ZSIpKw0KICB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZT05MCksIGxlZ2VuZC5wb3NpdGlvbj0ibm9uZSIpDQoNCmBgYA0KVGhpcyBwbG90IHNob3dzOg0KDQoqIEEgdmVyeSBzbGlnaHQgZGVjcmVhc2UgaW4gd2VpZ2h0IG92ZXIgdGltZSBmb3IgdHdvIG9mIHRoZSB1c2Vycy4NCg0KKiBBIHNsaWdodCBpbmNyZWFzZSBpbiB3ZWlnaHQgZm9yIG9uZSB1c2VyLg0KDQoqIEluY29uY2x1c2l2ZSB3ZWlnaHQgZ2FpbiBvciBsb3NzIGZvciB0d28gdXNlcnMuDQoNCiogVGhlIGRhdGEgc2FtcGxlIHNpemUgaXMgdG9vIHNtYWxsIHRvIGdlbmVyYXRlIGFueSBjb25jbHVzaXZlIGFjdGlvbmFibGUgaW5zaWdodHMuDQoNCiMjIERhdGEgQ29uY2x1c2lvbnMNCiogQmFzZWQgb24gdGhlIGF2ZXJhZ2UgYm9keSBtYXNzIGluZGV4IChCTUkpIG9mIDI1LjE5LCB1c2VycyBhcmUgZ2VuZXJhbGx5IHNsaWdodGx5IG92ZXJ3ZWlnaHQsIGltcGx5aW5nIGEgcHJlZGlzcG9zaXRpb24gZm9yIHRob3NlIGN1cnJlbnRseSBvdXQgb2Ygc2hhcGUgdG8gdXNlIHRoZSBGaXRCaXQgdG8gdHJhY2sgZXhlcmNpc2UgYWN0aXZpdHkuIEFzIHRoaXMgaXMgYmFzZWQgb24gb25seSBlaWdodCB1c2Vycycgd2VpZ2h0IGxvZ3MsIHRoaXMgb2JzZXJ2YXRpb24gbXVzdCBiZSBjb25zaWRlcmVkIHVsdGltYXRlbHkgaW5jb25jbHVzaXZlIGF0IHRoaXMgdGltZS4NCg0KKiBVc2VycyB0ZW5kIHRvIHNwZW5kIG1vc3Qgb2YgdGhlaXIgdGltZSBlbmdhZ2VkIGluIGxpZ2h0IGFjdGl2aXR5IHdpdGggc2hvcnQgYnVyc3RzIG9mIGhpZ2hlci1pbnRlbnNpdHkgYWN0aXZpdHkuDQoNCiogTW9zdCB1c2VyIGFjdGl2aXR5IG9jY3VycyBvbiBTYXR1cmRheSwgYWx0aG91Z2ggdGhlIG1vc3QgaW50ZW5zZSBhY3Rpdml0eSBvY2N1cnMgb24gTW9uZGF5LiBTdXJwcmlzaW5nbHksIHRoZSBsZWFzdCBvdmVyYWxsIHVzZXIgYWN0aXZpdHkgYWxzbyBvY2N1cnMgb24gTW9uZGF5LiBUaGlzIGltcGxpZXMgZm9jdXNlZCwgaGlnaC1pbXBhY3QgeWV0IHNob3J0LWR1cmF0aW9uIGV4ZXJjaXNlIGFjdGl2aXR5IGF0IHRoZSBzdGFydCBvZiB0aGUgdHlwaWNhbCB3b3JrIHdlZWsuDQoNCiogVXNlciBhY3Rpdml0eSBzZWVtcyB0byB0YWtlIHBsYWNlIG1vc3Qgb2Z0ZW4ganVzdCBhZnRlciB3b3JrIGhvdXJzICg1cG0gdG8gN3BtKSBvciByaWdodCBhcm91bmQgbHVuY2ggdGltZSAoMTIgbm9vbiB0byAycG0pLg0KDQoqIEFuIGluY3JlYXNlIGluIGRhaWx5IHN0ZXBzIGFuZCB2ZXJ5IGFjdGl2ZSB0aW1lIGRpc3BsYXlzIGEgc29saWQgcG9zaXRpdmUgY29ycmVsYXRpb24gd2l0aCBhbiBpbmNyZWFzZSBpbiBkYWlseSBidXJuZWQgY2Fsb3JpZXMuDQoNCiogQXZlcmFnZSBob3VybHkgaW50ZW5zaXR5IGRlbW9uc3RyYXRlcyBhIHN0cm9uZyBwb3NpdGl2ZSBjb3JyZWxhdGlvbiB3aXRoIGhvdXJseSBjYWxvcmllcyBidXJuZWQuDQoNCiogVXNlcnMgYXZlcmFnZSBhIGhlYWx0aHkgYW1vdW50IG9mIHNsZWVwIGZvciBhZHVsdHMsIGF0IGdlbmVyYWxseSBzZXZlbiBob3VycyBvZiBzbGVlcCBhIG5pZ2h0Lg0KDQoqIFRoZSBzbWFsbCBzYW1wbGUgc2l6ZSBpcyBzdWItb3B0aW1hbCwgZXNwZWNpYWxseSBpbiB0aGUgcmVwb3J0aW5nIG9mIHdlaWdodCBkYXRhLCB3aXRoIG9ubHkgZWlnaHQgdXNlcnMgbG9nZ2luZyB3ZWlnaHRzLCBhbmQgb25seSB0d28gb2YgdGhvc2UgdXNlcnMgbG9nZ2luZyB3ZWlnaHRzIG92ZXIgdGhlIDMwLWRheSB0aW1lIHBlcmlvZC4NCg0KIyMgU3VnZ2VzdGlvbnMgZm9yIEJlbGxhYmVhdA0KIyMjIyBJbmZvcm1hdGlvbiBHYXRoZXJpbmcNCiogVGhlIHNtYWxsZXItdGhhbi1leHBlY3RlZCBzYW1wbGUgc2l6ZSBmb3IgaGVhcnQgcmF0ZSAoMTQgb3V0IG9mIDMzIHVzZXJzKSBhbmQgd2VpZ2h0IGxvZyAoOCBvdXQgb2YgMzMgdXNlcnMpIGltcGx5IGEgbmVlZCB0byBiZXR0ZXIgaW5mb3JtIHVzZXJzIG9mIHRoZSBpbXBvcnRhbmNlIG9mIHRyYWNraW5nIHRob3NlIGF0dHJpYnV0ZXMuIEJlbGxhYmVhdCBtYXkgY29uc2lkZXIgaW5jZW50aXZpemluZyBzdWNoIHRyYWNraW5nLg0KDQoqIEFkZGl0aW9uYWwgdXNlciBpbmZvcm1hdGlvbiBzdWNoIGFzIGFnZSwgZ2VuZGVyLCBhbmQgb2NjdXBhdGlvbmFsIGhvdXJzIGNvdWxkIGhlbHAgaW4gcHJvdmlkaW5nIG1vcmUgc3Vic3RhbnRpdmUgZGF0YSBjb3JyZWxhdGlvbnMgYW5kIG1vcmUgY3VzdG9taXphYmxlIGV4ZXJjaXNlIHBsYW5zLg0KDQojIyMjIE1hcmtldGluZyBSZWNvbW1lbmRhdGlvbnMNCiogVXNlcnMgdGVuZCB0byBiZSBtb3N0IGFjdGl2ZSBvbiBTYXR1cmRheSBhbmQgaW4gdGhlIGVhcmx5IGV2ZW5pbmcuIEFkdmVydGlzaW5nIGFuZCBtYXJrZXRpbmcgc2hvdWxkIGZvY3VzIG9uIG1lc3NhZ2luZyBhbmQgdGltaW5nIHRoYXQgdGFyZ2V0IHRob3NlIGRheXMgYW5kIHRpbWUgcGVyaW9kcy4gRXhhbXBsZXMgY291bGQgaW5jbHVkZSBzb2NpYWwgbWVkaWEgYWRzIGZvY3VzZWQgb24gdGhlIDRwbSB0aW1lIHNsb3Qgb3Igb24gRnJpZGF5cywgcmVtaW5kaW5nIHBlb3BsZSB0byBzdGF5IGFjdGl2ZSBmb3IgZml0bmVzcyBhbmQgaGVhbHRoIGluIHRoZSB1cGNvbWluZyB3ZWVrZW5kLg0KDQojIyMjIEFwcCBSZWNvbW1lbmRhdGlvbnMNCiogT25lIHVzZXIgZGlzcGxheWVkIGEgaGVhcnQgcmF0ZSBjb25zaWRlcmVkIHZlcnkgaGlnaCwgYXQgMjAzIGJwbS4gQmVsbGFiZWF0IHNob3VsZCBjb25zaWRlciBhZGRpbmcgaW4gYSB2aXN1YWwgYW5kL29yIGF1ZGlvIGFsZXJ0IHdoZW4gdXNlciBoZWFydCByYXRlcyBleGNlZWQgYSBtYXhpbXVtIGhlYWx0aHkgaGVhcnQgcmF0ZSBmb3IgdGhlaXIgYWdlLg0KDQoqIFRoZSBsYWNrIG9mIGNvbnNpc3RlbnQgd2VpZ2h0IGxvZyBkYXRhIG1heSBiZSBpbXByb3ZlZCB3aXRoIGRlZGljYXRlZCBhcHAtaW50ZWdyYXRpb24gYW5kIGNvbm5lY3Rpdml0eSB3aXRoIEJsdWVUb290aC1jb25uZWN0ZWQgc2NhbGVzLg0KDQoqIEJlbGxhYmVhdCBzaG91bGQgY29uc2lkZXIgYWRkaW5nIGluIHBlcmlvZGljIHJlbWluZGVycyBmb3IgdXNlcnMgdG8gdHJhY2sgbm90IGp1c3QgcGh5c2ljYWwgYWN0aXZpdHksIGJ1dCBoZWFydCByYXRlIGFuZCB3ZWlnaHQgZGF0YS4gUGVyaGFwcyAqa3Vkb3MsIGFjaGlldmVtZW50cywgb3IgYmFkZ2VzKiBjb3VsZCBiZSBnaXZlbiB0byB1c2VycyBhcyBhIHJld2FyZCBmb3IgcHJvdmlkaW5nIG1vcmUgY29tcHJlaGVuc2l2ZSBoZWFsdGggdHJhY2tpbmcuDQoNCiogRGF0YSBzdHJvbmdseSBzdWdnZXN0cyB0aGF0IGhpZ2hlci1pbnRlbnNpdHkgYWN0aXZpdHkgZGlyZWN0bHkgY29ycmVsYXRlcyB3aXRoIGhpZ2hlciBjYWxvcmljIGJ1cm4gcmF0ZXMuIEJlbGxhYmVhdCBzaG91bGQgY29uc2lkZXIgZW5jb3VyYWdpbmcgKmN1c3RvbSwgaGlnaC1pbnRlbnNpdHkgZXhlcmNpc2UgcGxhbnMqIGZvciB0aG9zZSBsb29raW5nIHRvIGJ1cm4gdGhlIG1vc3QgY2Fsb3JpZXMuDQoNCiogQmVsbGFiZWF0IHNob3VsZCBwcm92aWRlIGFuIG9wdGlvbiBmb3IgYXBwIHVzZXJzIHRvIHJlY2VpdmUgcGVyaW9kaWMgcmVtaW5kZXJzIGZvciBkYWlseSBtb3ZlbWVudCBhbmQgYWN0aXZpdHksIGFzIGV2ZW4gbGlnaHQgYWN0aXZpdHkgY29ycmVsYXRlcyB3aXRoIGFuIGluY3JlYXNlIGluIGNhbG9yaWMgYnVybi4gVGhpcyB3b3VsZCBiZSBtb3N0IGVmZmVjdGl2ZSBpZiB0YXJnZXRlZCBmb3IgYWN0aXZpdHkgZHVyaW5nIGx1bmNoIGJyZWFrcyBvciBhdCB0aGUgZW5kIG9mIHRoZSB3b3JrIGRheSwgYXMgdGhlc2UgdGltZXMgc2hvdyB0aGUgaGlnaGVzdCBhY3RpdmUgaW50ZW5zaXR5IHJhdGVzLg0KDQpVbHRpbWF0ZWx5LCBmdXJ0aGVyIGFuYWx5c2lzIG9mIGZpdG5lc3MgdHJhY2tlciB1c2VycyByZXF1aXJlcyBib3RoIGEgbGFyZ2VyIHNhbXBsZSBzaXplIGFuZCBhZGRpdGlvbmFsIGRhdGEuIEFzIGlzLCBjdXJyZW50IGRhdGEgaXMgZW5vdWdoIHRvIHByb3ZpZGUgYnJvYWQgZ3VpZGVsaW5lcyBmb3IgYWN0aW9uYWJsZSBzdGVwcywgYnV0IGZ1cnRoZXIgZGF0YSBnYXRoZXJpbmcgYW5kIGFuYWx5c2lzIGlzIHJlY29tbWVuZGVkLg0KDQo=