Load installed packages

library(tidyverse)
library(lubridate)
library(dplyr)
library(ggplot2)
library(tidyr)

Load CSV files

daily_activity <- read.csv("C:/Users/cmor7/OneDrive/Desktop/Case Study/BellaBeat/Data/Fitabase Data 4.12.16-5.12.16/dailyActivity_merged.csv")
sleep_day <- read.csv("C:/Users/cmor7/OneDrive/Desktop/Case Study/BellaBeat/Data/Fitabase Data 4.12.16-5.12.16/sleepDay_merged.csv")
hourly_intensities <- read.csv("C:/Users/cmor7/OneDrive/Desktop/Case Study/BellaBeat/Data/Fitabase Data 4.12.16-5.12.16/hourlyIntensities_merged.csv")
hourly_calories <- read.csv("C:/Users/cmor7/OneDrive/Desktop/Case Study/BellaBeat/Data/Fitabase Data 4.12.16-5.12.16/hourlyCalories_merged.csv")
weight_log <- read.csv("C:/Users/cmor7/OneDrive/Desktop/Case Study/BellaBeat/Data/Fitabase Data 4.12.16-5.12.16/weightLogInfo_merged.csv")

Review head data of loaded CSV files

head(daily_activity)
head(sleep_day)
head(hourly_intensities)
head(hourly_calories)
head(weight_log)

Review column names of loaded CSV files

colnames(daily_activity)
 [1] "Id"                       "ActivityDate"            
 [3] "TotalSteps"               "TotalDistance"           
 [5] "TrackerDistance"          "LoggedActivitiesDistance"
 [7] "VeryActiveDistance"       "ModeratelyActiveDistance"
 [9] "LightActiveDistance"      "SedentaryActiveDistance" 
[11] "VeryActiveMinutes"        "FairlyActiveMinutes"     
[13] "LightlyActiveMinutes"     "SedentaryMinutes"        
[15] "Calories"                
colnames(sleep_day)
[1] "Id"                 "SleepDay"           "TotalSleepRecords" 
[4] "TotalMinutesAsleep" "TotalTimeInBed"    
colnames(hourly_intensities)
[1] "Id"               "ActivityHour"     "TotalIntensity"   "AverageIntensity"
colnames(hourly_calories)
[1] "Id"           "ActivityHour" "Calories"    
colnames(weight_log)
[1] "Id"             "Date"           "WeightKg"       "WeightPounds"  
[5] "Fat"            "BMI"            "IsManualReport" "LogId"         

Fix date mismatches

The daily_activity contains just a date but the others contain a date and time. I will need to seperate the date and time into their own columns in these other data frames. Additionally, the data type for each date column will need to be set to a date format as well:

sleep_day <- sleep_day %>% 
  mutate(
    SleepDay = mdy_hms(SleepDay),  # Parse datetime (adjust format if needed)
    Date = as.Date(SleepDay),
  )
head(sleep_day)

daily_activity <- daily_activity %>% 
  mutate(
    ActivityDate = mdy(ActivityDate),
    Date = as.Date(ActivityDate)
  )

hourly_intensities <- hourly_intensities %>% 
  mutate(
    ActivityHour = mdy_hms(ActivityHour),  # Parse datetime (adjust format if needed)
    Date = as.Date(ActivityHour),
    Time = format(ActivityHour, "%H:%M")
  )

hourly_calories <- hourly_calories %>% 
  mutate(
    ActivityHour = mdy_hms(ActivityHour),  # Parse datetime (adjust format if needed)
    Date = as.Date(ActivityHour),
    Time = format(ActivityHour, "%H:%M")
  )

weight_log <- weight_log %>% 
  mutate(
    Date = mdy_hms(Date),  # Parse datetime (adjust format if needed)
    Date = as.Date(Date)
  )

Summary Statistics

How many unique participants are there in each dataframe?

n_distinct(daily_activity$Id)
[1] 33
n_distinct(sleep_day$Id)
[1] 24
n_distinct(hourly_intensities$Id)
[1] 33
n_distinct(hourly_calories$Id)
[1] 33
n_distinct(weight_log$Id)
[1] 8

How many observations are there in each dataframe?

nrow(daily_activity)
[1] 940
nrow(sleep_day)
[1] 413
nrow(hourly_intensities)
[1] 22099
nrow(hourly_calories)
[1] 22099
nrow(weight_log)
[1] 67

daily_activity dataframe:

daily_activity %>%  
  select(TotalSteps,
         TotalDistance,
         SedentaryMinutes,
         Calories) %>%
  summary()
   TotalSteps    TotalDistance    SedentaryMinutes    Calories   
 Min.   :    0   Min.   : 0.000   Min.   :   0.0   Min.   :   0  
 1st Qu.: 3790   1st Qu.: 2.620   1st Qu.: 729.8   1st Qu.:1828  
 Median : 7406   Median : 5.245   Median :1057.5   Median :2134  
 Mean   : 7638   Mean   : 5.490   Mean   : 991.2   Mean   :2304  
 3rd Qu.:10727   3rd Qu.: 7.713   3rd Qu.:1229.5   3rd Qu.:2793  
 Max.   :36019   Max.   :28.030   Max.   :1440.0   Max.   :4900  

sleep_day dataframe:

sleep_day %>%  
  select(TotalSleepRecords,
         TotalMinutesAsleep,
         TotalTimeInBed) %>%
  summary()
 TotalSleepRecords TotalMinutesAsleep TotalTimeInBed 
 Min.   :1.000     Min.   : 58.0      Min.   : 61.0  
 1st Qu.:1.000     1st Qu.:361.0      1st Qu.:403.0  
 Median :1.000     Median :433.0      Median :463.0  
 Mean   :1.119     Mean   :419.5      Mean   :458.6  
 3rd Qu.:1.000     3rd Qu.:490.0      3rd Qu.:526.0  
 Max.   :3.000     Max.   :796.0      Max.   :961.0  

hourly_intensities dataframe:

hourly_intensities %>%  
  select(TotalIntensity,
         AverageIntensity) %>%
  summary()
 TotalIntensity   AverageIntensity
 Min.   :  0.00   Min.   :0.0000  
 1st Qu.:  0.00   1st Qu.:0.0000  
 Median :  3.00   Median :0.0500  
 Mean   : 12.04   Mean   :0.2006  
 3rd Qu.: 16.00   3rd Qu.:0.2667  
 Max.   :180.00   Max.   :3.0000  

hourly_calories dataframe:

hourly_calories %>%  
  select(Calories) %>%
  summary()
    Calories     
 Min.   : 42.00  
 1st Qu.: 63.00  
 Median : 83.00  
 Mean   : 97.39  
 3rd Qu.:108.00  
 Max.   :948.00  

Joining sleep_day and daily_activity

activity_sleep <- full_join(sleep_day, daily_activity, by=c("Id", "Date"))

Verify Distinct Id Count After Join

n_distinct(activity_sleep$Id)
[1] 33

Visualization

ggplot(data=daily_activity, aes(x=TotalSteps, y=SedentaryMinutes)) + geom_point()+ geom_smooth()+ labs(title="Total Steps vs. Sedentary Minutes", y="Total Sedentary Minutes", x="Total Steps")

ggplot(data=daily_activity, aes(x=TotalSteps, y=Calories)) + geom_point() + geom_smooth()+ labs(title="Total Steps vs. Calories", x="Total Steps")

ggplot(data=sleep_day, aes(x=TotalMinutesAsleep, y=TotalTimeInBed)) + geom_point()+ geom_smooth()+ labs(title="Total Sleep Minutes vs. Total Time In Bed", y="Total Time in Bed", x="Total Sleep Minutes")

ggplot(data = activity_sleep, aes(x=TotalMinutesAsleep, y=TotalSteps)) + geom_point() + geom_smooth()+ labs(title="Total Sleep Minutes vs. Total Steps", y="Total Steps", x="Total Sleep Minutes")

ggplot(data = activity_sleep, aes(x=TotalMinutesAsleep, y=SedentaryMinutes)) + geom_point() + geom_smooth()+ labs(title="Total Sleep Minutes vs. Total Sedentary Minutes", y="Total Sedentary Minutes", x="Total Sleep Minutes")

ggplot(data = activity_sleep, aes(x=TotalMinutesAsleep, y=Calories)) + geom_point() + geom_smooth() + labs(title="Total Sleep Minutes vs. Calories", x="Total Sleep Minutes")


grouped_hourly_intensities <- hourly_intensities %>%
  group_by(Time) %>%
  drop_na() %>%
  summarise(mean_intensity = mean(TotalIntensity))

ggplot(data=grouped_hourly_intensities, aes(x=Time, y=mean_intensity)) + geom_col(fill='grey', color="darkblue") + theme(axis.text.x = element_text(angle = 45)) + labs(title="Average Total Intensity vs. Time", y="Average Total Intensity")


grouped_hourly_calories <- hourly_calories %>%
  group_by(Time) %>%
  drop_na() %>%
  summarise(mean_calories = mean(Calories))

ggplot(data=grouped_hourly_calories, aes(x=Time, y=mean_calories)) + geom_col(fill='grey', color="darkblue") + theme(axis.text.x = element_text(angle = 45)) + labs(title="Average Calories vs. Time", y="Mean Calories")

Correlations

# Correlations with p-values
# Sedentary vs Sleep
cor_test_sed_sleep <- cor.test(activity_sleep$SedentaryMinutes, activity_sleep$TotalMinutesAsleep)
print(cor_test_sed_sleep)  # r and p-value

    Pearson's product-moment correlation

data:  activity_sleep$SedentaryMinutes and activity_sleep$TotalMinutesAsleep
t = -15.181, df = 411, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.6578402 -0.5337719
sample estimates:
      cor 
-0.599394 
# Steps vs Sedentary
cor_test_steps_sed <- cor.test(daily_activity$TotalSteps, daily_activity$SedentaryMinutes)
print(cor_test_steps_sed)

    Pearson's product-moment correlation

data:  daily_activity$TotalSteps and daily_activity$SedentaryMinutes
t = -10.615, df = 938, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 -0.3833971 -0.2691782
sample estimates:
       cor 
-0.3274835 
# Steps vs Calories
cor_test_steps_cal <- cor.test(daily_activity$TotalSteps, daily_activity$Calories)
print(cor_test_steps_cal)

    Pearson's product-moment correlation

data:  daily_activity$TotalSteps and daily_activity$Calories
t = 22.472, df = 938, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.5483688 0.6316184
sample estimates:
      cor 
0.5915681 
# Sleep vs Time in Bed
cor_test_sleep_bed <- cor.test(sleep_day$TotalMinutesAsleep, sleep_day$TotalTimeInBed)
print(cor_test_sleep_bed)

    Pearson's product-moment correlation

data:  sleep_day$TotalMinutesAsleep and sleep_day$TotalTimeInBed
t = 51.483, df = 411, p-value < 2.2e-16
alternative hypothesis: true correlation is not equal to 0
95 percent confidence interval:
 0.9162253 0.9423445
sample estimates:
      cor 
0.9304575 
# Sleep efficiency (ratio) for insomnia outliers
sleep_day$SleepEfficiency <- sleep_day$TotalMinutesAsleep / sleep_day$TotalTimeInBed
mean_eff <- mean(sleep_day$SleepEfficiency, na.rm = TRUE)
std_eff <- sd(sleep_day$SleepEfficiency, na.rm = TRUE)
outliers <- sleep_day[sleep_day$SleepEfficiency < (mean_eff - 2 * std_eff), ]
print(paste("Mean Efficiency:", round(mean_eff, 3), "Std:", round(std_eff, 3), "Outliers:", nrow(outliers)))
[1] "Mean Efficiency: 0.917 Std: 0.087 Outliers: 27"
# Visualize efficiency
ggplot(sleep_day, aes(x = SleepEfficiency)) + geom_histogram(binwidth = 0.01, color="darkblue", fill="gray") + 
  labs(title = "Distribution of Sleep Efficiency", y = "Number of Sleep Records", x="Sleep Efficiency")

Data Limitations

Key Insights From Data Analysis:

  1. 5pm-7pm are the most active hours of the day as confirmed by the comparisons of Average Total Intensity and Calories burned to Time.
  2. 12pm-2pm is the second most active time period.
  3. People with higher sedentary minutes had less total minutes asleep.
  4. Total sleep minutes increase with total time in bed so outliers that spend more time in bed but get less sleep may be having insomnia or poor sleep hygiene.
  5. People that took the most steps generally burned the most calories
  6. There is a unique relationship between total steps and sedentary minutes. Sedentary minutes decrease with more total steps up to about 10,000 steps. There is an inflection point here where more stpes after 10,000 tends to lead to an increase in sedentary minutes which may indicate fatigue or needed rest.

Recomendations:

  1. Send activity related notification reminders to users to be active prior to the 5pm-7pm most active window, especially if they have not been active earlier in the day.
  2. Send notifications to people with less total minutes slept to improve sleep by being more active throughout the day.
  3. Send notifications to outlying users who spend more time in bed but get less sleep than the average with sleep hygiene tips since they might be experiencing insomnia.
  4. Send notifications to people that exceed 10,000 steps per day to remind them that they might need more recovery and rest activities.
LS0tDQp0aXRsZTogIkJlbGxhQmVhdCBDYXNlIFN0dWR5Ig0Kb3V0cHV0Og0KICBodG1sX25vdGVib29rOiBkZWZhdWx0DQogIHBkZl9kb2N1bWVudDogZGVmYXVsdA0KLS0tDQojIyMgTG9hZCBpbnN0YWxsZWQgcGFja2FnZXMNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGx1YnJpZGF0ZSkNCmxpYnJhcnkoZHBseXIpDQpsaWJyYXJ5KGdncGxvdDIpDQpsaWJyYXJ5KHRpZHlyKQ0KYGBgDQojIyMgTG9hZCBDU1YgZmlsZXMNCmBgYHtyfQ0KZGFpbHlfYWN0aXZpdHkgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL2Ntb3I3L09uZURyaXZlL0Rlc2t0b3AvQ2FzZSBTdHVkeS9CZWxsYUJlYXQvRGF0YS9GaXRhYmFzZSBEYXRhIDQuMTIuMTYtNS4xMi4xNi9kYWlseUFjdGl2aXR5X21lcmdlZC5jc3YiKQ0Kc2xlZXBfZGF5IDwtIHJlYWQuY3N2KCJDOi9Vc2Vycy9jbW9yNy9PbmVEcml2ZS9EZXNrdG9wL0Nhc2UgU3R1ZHkvQmVsbGFCZWF0L0RhdGEvRml0YWJhc2UgRGF0YSA0LjEyLjE2LTUuMTIuMTYvc2xlZXBEYXlfbWVyZ2VkLmNzdiIpDQpob3VybHlfaW50ZW5zaXRpZXMgPC0gcmVhZC5jc3YoIkM6L1VzZXJzL2Ntb3I3L09uZURyaXZlL0Rlc2t0b3AvQ2FzZSBTdHVkeS9CZWxsYUJlYXQvRGF0YS9GaXRhYmFzZSBEYXRhIDQuMTIuMTYtNS4xMi4xNi9ob3VybHlJbnRlbnNpdGllc19tZXJnZWQuY3N2IikNCmhvdXJseV9jYWxvcmllcyA8LSByZWFkLmNzdigiQzovVXNlcnMvY21vcjcvT25lRHJpdmUvRGVza3RvcC9DYXNlIFN0dWR5L0JlbGxhQmVhdC9EYXRhL0ZpdGFiYXNlIERhdGEgNC4xMi4xNi01LjEyLjE2L2hvdXJseUNhbG9yaWVzX21lcmdlZC5jc3YiKQ0Kd2VpZ2h0X2xvZyA8LSByZWFkLmNzdigiQzovVXNlcnMvY21vcjcvT25lRHJpdmUvRGVza3RvcC9DYXNlIFN0dWR5L0JlbGxhQmVhdC9EYXRhL0ZpdGFiYXNlIERhdGEgNC4xMi4xNi01LjEyLjE2L3dlaWdodExvZ0luZm9fbWVyZ2VkLmNzdiIpDQpgYGANCiMjIyBSZXZpZXcgaGVhZCBkYXRhIG9mIGxvYWRlZCBDU1YgZmlsZXMNCmBgYHtyfQ0KaGVhZChkYWlseV9hY3Rpdml0eSkNCmhlYWQoc2xlZXBfZGF5KQ0KaGVhZChob3VybHlfaW50ZW5zaXRpZXMpDQpoZWFkKGhvdXJseV9jYWxvcmllcykNCmhlYWQod2VpZ2h0X2xvZykNCmBgYA0KIyMjIFJldmlldyBjb2x1bW4gbmFtZXMgb2YgbG9hZGVkIENTViBmaWxlcw0KYGBge3J9DQpjb2xuYW1lcyhkYWlseV9hY3Rpdml0eSkNCmNvbG5hbWVzKHNsZWVwX2RheSkNCmNvbG5hbWVzKGhvdXJseV9pbnRlbnNpdGllcykNCmNvbG5hbWVzKGhvdXJseV9jYWxvcmllcykNCmNvbG5hbWVzKHdlaWdodF9sb2cpDQpgYGANCiMjIyBGaXggZGF0ZSBtaXNtYXRjaGVzDQpUaGUgZGFpbHlfYWN0aXZpdHkgY29udGFpbnMganVzdCBhIGRhdGUgYnV0IHRoZSBvdGhlcnMgY29udGFpbiBhIGRhdGUgYW5kIHRpbWUuIEkgd2lsbCBuZWVkIHRvIHNlcGVyYXRlIHRoZSBkYXRlIGFuZCB0aW1lIGludG8gdGhlaXIgb3duIGNvbHVtbnMgaW4gdGhlc2Ugb3RoZXIgZGF0YSBmcmFtZXMuIEFkZGl0aW9uYWxseSwgdGhlIGRhdGEgdHlwZSBmb3IgZWFjaCBkYXRlIGNvbHVtbiB3aWxsIG5lZWQgdG8gYmUgc2V0IHRvIGEgZGF0ZSBmb3JtYXQgYXMgd2VsbDoNCmBgYHtyfQ0Kc2xlZXBfZGF5IDwtIHNsZWVwX2RheSAlPiUgDQogIG11dGF0ZSgNCiAgICBTbGVlcERheSA9IG1keV9obXMoU2xlZXBEYXkpLCAgIyBQYXJzZSBkYXRldGltZSAoYWRqdXN0IGZvcm1hdCBpZiBuZWVkZWQpDQogICAgRGF0ZSA9IGFzLkRhdGUoU2xlZXBEYXkpLA0KICApDQpoZWFkKHNsZWVwX2RheSkNCg0KZGFpbHlfYWN0aXZpdHkgPC0gZGFpbHlfYWN0aXZpdHkgJT4lIA0KICBtdXRhdGUoDQogICAgQWN0aXZpdHlEYXRlID0gbWR5KEFjdGl2aXR5RGF0ZSksDQogICAgRGF0ZSA9IGFzLkRhdGUoQWN0aXZpdHlEYXRlKQ0KICApDQoNCmhvdXJseV9pbnRlbnNpdGllcyA8LSBob3VybHlfaW50ZW5zaXRpZXMgJT4lIA0KICBtdXRhdGUoDQogICAgQWN0aXZpdHlIb3VyID0gbWR5X2htcyhBY3Rpdml0eUhvdXIpLCAgIyBQYXJzZSBkYXRldGltZSAoYWRqdXN0IGZvcm1hdCBpZiBuZWVkZWQpDQogICAgRGF0ZSA9IGFzLkRhdGUoQWN0aXZpdHlIb3VyKSwNCiAgICBUaW1lID0gZm9ybWF0KEFjdGl2aXR5SG91ciwgIiVIOiVNIikNCiAgKQ0KDQpob3VybHlfY2Fsb3JpZXMgPC0gaG91cmx5X2NhbG9yaWVzICU+JSANCiAgbXV0YXRlKA0KICAgIEFjdGl2aXR5SG91ciA9IG1keV9obXMoQWN0aXZpdHlIb3VyKSwgICMgUGFyc2UgZGF0ZXRpbWUgKGFkanVzdCBmb3JtYXQgaWYgbmVlZGVkKQ0KICAgIERhdGUgPSBhcy5EYXRlKEFjdGl2aXR5SG91ciksDQogICAgVGltZSA9IGZvcm1hdChBY3Rpdml0eUhvdXIsICIlSDolTSIpDQogICkNCg0Kd2VpZ2h0X2xvZyA8LSB3ZWlnaHRfbG9nICU+JSANCiAgbXV0YXRlKA0KICAgIERhdGUgPSBtZHlfaG1zKERhdGUpLCAgIyBQYXJzZSBkYXRldGltZSAoYWRqdXN0IGZvcm1hdCBpZiBuZWVkZWQpDQogICAgRGF0ZSA9IGFzLkRhdGUoRGF0ZSkNCiAgKQ0KYGBgDQojIyBTdW1tYXJ5IFN0YXRpc3RpY3MNCiMjIyBIb3cgbWFueSB1bmlxdWUgcGFydGljaXBhbnRzIGFyZSB0aGVyZSBpbiBlYWNoIGRhdGFmcmFtZT8NCmBgYHtyfQ0Kbl9kaXN0aW5jdChkYWlseV9hY3Rpdml0eSRJZCkNCm5fZGlzdGluY3Qoc2xlZXBfZGF5JElkKQ0Kbl9kaXN0aW5jdChob3VybHlfaW50ZW5zaXRpZXMkSWQpDQpuX2Rpc3RpbmN0KGhvdXJseV9jYWxvcmllcyRJZCkNCm5fZGlzdGluY3Qod2VpZ2h0X2xvZyRJZCkNCmBgYA0KIyMjIEhvdyBtYW55IG9ic2VydmF0aW9ucyBhcmUgdGhlcmUgaW4gZWFjaCBkYXRhZnJhbWU/DQpgYGB7cn0NCm5yb3coZGFpbHlfYWN0aXZpdHkpDQpucm93KHNsZWVwX2RheSkNCm5yb3coaG91cmx5X2ludGVuc2l0aWVzKQ0KbnJvdyhob3VybHlfY2Fsb3JpZXMpDQpucm93KHdlaWdodF9sb2cpDQpgYGANCiMjIyBkYWlseV9hY3Rpdml0eSBkYXRhZnJhbWU6DQpgYGB7cn0NCmRhaWx5X2FjdGl2aXR5ICU+JSAgDQogIHNlbGVjdChUb3RhbFN0ZXBzLA0KICAgICAgICAgVG90YWxEaXN0YW5jZSwNCiAgICAgICAgIFNlZGVudGFyeU1pbnV0ZXMsDQogICAgICAgICBDYWxvcmllcykgJT4lDQogIHN1bW1hcnkoKQ0KYGBgDQojIyMgc2xlZXBfZGF5IGRhdGFmcmFtZToNCmBgYHtyfQ0Kc2xlZXBfZGF5ICU+JSAgDQogIHNlbGVjdChUb3RhbFNsZWVwUmVjb3JkcywNCiAgICAgICAgIFRvdGFsTWludXRlc0FzbGVlcCwNCiAgICAgICAgIFRvdGFsVGltZUluQmVkKSAlPiUNCiAgc3VtbWFyeSgpDQpgYGANCiMjIyBob3VybHlfaW50ZW5zaXRpZXMgZGF0YWZyYW1lOg0KYGBge3J9DQpob3VybHlfaW50ZW5zaXRpZXMgJT4lICANCiAgc2VsZWN0KFRvdGFsSW50ZW5zaXR5LA0KICAgICAgICAgQXZlcmFnZUludGVuc2l0eSkgJT4lDQogIHN1bW1hcnkoKQ0KYGBgDQojIyMgaG91cmx5X2NhbG9yaWVzIGRhdGFmcmFtZToNCmBgYHtyfQ0KaG91cmx5X2NhbG9yaWVzICU+JSAgDQogIHNlbGVjdChDYWxvcmllcykgJT4lDQogIHN1bW1hcnkoKQ0KYGBgDQojIyBKb2luaW5nIHNsZWVwX2RheSBhbmQgZGFpbHlfYWN0aXZpdHkNCmBgYHtyfQ0KYWN0aXZpdHlfc2xlZXAgPC0gZnVsbF9qb2luKHNsZWVwX2RheSwgZGFpbHlfYWN0aXZpdHksIGJ5PWMoIklkIiwgIkRhdGUiKSkNCmBgYA0KIyMjIFZlcmlmeSBEaXN0aW5jdCBJZCBDb3VudCBBZnRlciBKb2luDQpgYGB7cn0NCm5fZGlzdGluY3QoYWN0aXZpdHlfc2xlZXAkSWQpDQpgYGANCiMjIFZpc3VhbGl6YXRpb24gDQpgYGB7cn0NCmdncGxvdChkYXRhPWRhaWx5X2FjdGl2aXR5LCBhZXMoeD1Ub3RhbFN0ZXBzLCB5PVNlZGVudGFyeU1pbnV0ZXMpKSArIGdlb21fcG9pbnQoKSsgZ2VvbV9zbW9vdGgoKSsgbGFicyh0aXRsZT0iVG90YWwgU3RlcHMgdnMuIFNlZGVudGFyeSBNaW51dGVzIiwgeT0iVG90YWwgU2VkZW50YXJ5IE1pbnV0ZXMiLCB4PSJUb3RhbCBTdGVwcyIpDQpnZ3Bsb3QoZGF0YT1kYWlseV9hY3Rpdml0eSwgYWVzKHg9VG90YWxTdGVwcywgeT1DYWxvcmllcykpICsgZ2VvbV9wb2ludCgpICsgZ2VvbV9zbW9vdGgoKSsgbGFicyh0aXRsZT0iVG90YWwgU3RlcHMgdnMuIENhbG9yaWVzIiwgeD0iVG90YWwgU3RlcHMiKQ0KZ2dwbG90KGRhdGE9c2xlZXBfZGF5LCBhZXMoeD1Ub3RhbE1pbnV0ZXNBc2xlZXAsIHk9VG90YWxUaW1lSW5CZWQpKSArIGdlb21fcG9pbnQoKSsgZ2VvbV9zbW9vdGgoKSsgbGFicyh0aXRsZT0iVG90YWwgU2xlZXAgTWludXRlcyB2cy4gVG90YWwgVGltZSBJbiBCZWQiLCB5PSJUb3RhbCBUaW1lIGluIEJlZCIsIHg9IlRvdGFsIFNsZWVwIE1pbnV0ZXMiKQ0KZ2dwbG90KGRhdGEgPSBhY3Rpdml0eV9zbGVlcCwgYWVzKHg9VG90YWxNaW51dGVzQXNsZWVwLCB5PVRvdGFsU3RlcHMpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkrIGxhYnModGl0bGU9IlRvdGFsIFNsZWVwIE1pbnV0ZXMgdnMuIFRvdGFsIFN0ZXBzIiwgeT0iVG90YWwgU3RlcHMiLCB4PSJUb3RhbCBTbGVlcCBNaW51dGVzIikNCmdncGxvdChkYXRhID0gYWN0aXZpdHlfc2xlZXAsIGFlcyh4PVRvdGFsTWludXRlc0FzbGVlcCwgeT1TZWRlbnRhcnlNaW51dGVzKSkgKyBnZW9tX3BvaW50KCkgKyBnZW9tX3Ntb290aCgpKyBsYWJzKHRpdGxlPSJUb3RhbCBTbGVlcCBNaW51dGVzIHZzLiBUb3RhbCBTZWRlbnRhcnkgTWludXRlcyIsIHk9IlRvdGFsIFNlZGVudGFyeSBNaW51dGVzIiwgeD0iVG90YWwgU2xlZXAgTWludXRlcyIpDQpnZ3Bsb3QoZGF0YSA9IGFjdGl2aXR5X3NsZWVwLCBhZXMoeD1Ub3RhbE1pbnV0ZXNBc2xlZXAsIHk9Q2Fsb3JpZXMpKSArIGdlb21fcG9pbnQoKSArIGdlb21fc21vb3RoKCkgKyBsYWJzKHRpdGxlPSJUb3RhbCBTbGVlcCBNaW51dGVzIHZzLiBDYWxvcmllcyIsIHg9IlRvdGFsIFNsZWVwIE1pbnV0ZXMiKQ0KDQpncm91cGVkX2hvdXJseV9pbnRlbnNpdGllcyA8LSBob3VybHlfaW50ZW5zaXRpZXMgJT4lDQogIGdyb3VwX2J5KFRpbWUpICU+JQ0KICBkcm9wX25hKCkgJT4lDQogIHN1bW1hcmlzZShtZWFuX2ludGVuc2l0eSA9IG1lYW4oVG90YWxJbnRlbnNpdHkpKQ0KDQpnZ3Bsb3QoZGF0YT1ncm91cGVkX2hvdXJseV9pbnRlbnNpdGllcywgYWVzKHg9VGltZSwgeT1tZWFuX2ludGVuc2l0eSkpICsgZ2VvbV9jb2woZmlsbD0nZ3JleScsIGNvbG9yPSJkYXJrYmx1ZSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSkpICsgbGFicyh0aXRsZT0iQXZlcmFnZSBUb3RhbCBJbnRlbnNpdHkgdnMuIFRpbWUiLCB5PSJBdmVyYWdlIFRvdGFsIEludGVuc2l0eSIpDQoNCmdyb3VwZWRfaG91cmx5X2NhbG9yaWVzIDwtIGhvdXJseV9jYWxvcmllcyAlPiUNCiAgZ3JvdXBfYnkoVGltZSkgJT4lDQogIGRyb3BfbmEoKSAlPiUNCiAgc3VtbWFyaXNlKG1lYW5fY2Fsb3JpZXMgPSBtZWFuKENhbG9yaWVzKSkNCg0KZ2dwbG90KGRhdGE9Z3JvdXBlZF9ob3VybHlfY2Fsb3JpZXMsIGFlcyh4PVRpbWUsIHk9bWVhbl9jYWxvcmllcykpICsgZ2VvbV9jb2woZmlsbD0nZ3JleScsIGNvbG9yPSJkYXJrYmx1ZSIpICsgdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X3RleHQoYW5nbGUgPSA0NSkpICsgbGFicyh0aXRsZT0iQXZlcmFnZSBDYWxvcmllcyB2cy4gVGltZSIsIHk9Ik1lYW4gQ2Fsb3JpZXMiKQ0KYGBgDQojIyBDb3JyZWxhdGlvbnMNCmBgYHtyfQ0KIyBDb3JyZWxhdGlvbnMgd2l0aCBwLXZhbHVlcw0KIyBTZWRlbnRhcnkgdnMgU2xlZXANCmNvcl90ZXN0X3NlZF9zbGVlcCA8LSBjb3IudGVzdChhY3Rpdml0eV9zbGVlcCRTZWRlbnRhcnlNaW51dGVzLCBhY3Rpdml0eV9zbGVlcCRUb3RhbE1pbnV0ZXNBc2xlZXApDQpwcmludChjb3JfdGVzdF9zZWRfc2xlZXApICAjIHIgYW5kIHAtdmFsdWUNCg0KIyBTdGVwcyB2cyBTZWRlbnRhcnkNCmNvcl90ZXN0X3N0ZXBzX3NlZCA8LSBjb3IudGVzdChkYWlseV9hY3Rpdml0eSRUb3RhbFN0ZXBzLCBkYWlseV9hY3Rpdml0eSRTZWRlbnRhcnlNaW51dGVzKQ0KcHJpbnQoY29yX3Rlc3Rfc3RlcHNfc2VkKQ0KDQojIFN0ZXBzIHZzIENhbG9yaWVzDQpjb3JfdGVzdF9zdGVwc19jYWwgPC0gY29yLnRlc3QoZGFpbHlfYWN0aXZpdHkkVG90YWxTdGVwcywgZGFpbHlfYWN0aXZpdHkkQ2Fsb3JpZXMpDQpwcmludChjb3JfdGVzdF9zdGVwc19jYWwpDQoNCiMgU2xlZXAgdnMgVGltZSBpbiBCZWQNCmNvcl90ZXN0X3NsZWVwX2JlZCA8LSBjb3IudGVzdChzbGVlcF9kYXkkVG90YWxNaW51dGVzQXNsZWVwLCBzbGVlcF9kYXkkVG90YWxUaW1lSW5CZWQpDQpwcmludChjb3JfdGVzdF9zbGVlcF9iZWQpDQoNCiMgU2xlZXAgZWZmaWNpZW5jeSAocmF0aW8pIGZvciBpbnNvbW5pYSBvdXRsaWVycw0Kc2xlZXBfZGF5JFNsZWVwRWZmaWNpZW5jeSA8LSBzbGVlcF9kYXkkVG90YWxNaW51dGVzQXNsZWVwIC8gc2xlZXBfZGF5JFRvdGFsVGltZUluQmVkDQptZWFuX2VmZiA8LSBtZWFuKHNsZWVwX2RheSRTbGVlcEVmZmljaWVuY3ksIG5hLnJtID0gVFJVRSkNCnN0ZF9lZmYgPC0gc2Qoc2xlZXBfZGF5JFNsZWVwRWZmaWNpZW5jeSwgbmEucm0gPSBUUlVFKQ0Kb3V0bGllcnMgPC0gc2xlZXBfZGF5W3NsZWVwX2RheSRTbGVlcEVmZmljaWVuY3kgPCAobWVhbl9lZmYgLSAyICogc3RkX2VmZiksIF0NCnByaW50KHBhc3RlKCJNZWFuIEVmZmljaWVuY3k6Iiwgcm91bmQobWVhbl9lZmYsIDMpLCAiU3RkOiIsIHJvdW5kKHN0ZF9lZmYsIDMpLCAiT3V0bGllcnM6IiwgbnJvdyhvdXRsaWVycykpKQ0KDQojIFZpc3VhbGl6ZSBlZmZpY2llbmN5DQpnZ3Bsb3Qoc2xlZXBfZGF5LCBhZXMoeCA9IFNsZWVwRWZmaWNpZW5jeSkpICsgZ2VvbV9oaXN0b2dyYW0oYmlud2lkdGggPSAwLjAxLCBjb2xvcj0iZGFya2JsdWUiLCBmaWxsPSJncmF5IikgKyANCiAgbGFicyh0aXRsZSA9ICJEaXN0cmlidXRpb24gb2YgU2xlZXAgRWZmaWNpZW5jeSIsIHkgPSAiTnVtYmVyIG9mIFNsZWVwIFJlY29yZHMiLCB4PSJTbGVlcCBFZmZpY2llbmN5IikNCmBgYA0KIyMgRGF0YSBMaW1pdGF0aW9ucw0KLSBTYW1wbGUgc2l6ZTogT25seSAzMyB1bmlxdWUgSURzDQotIE5vIGdlbmRlci9hZ2U6IExpbWl0cyBhcHBsaWNhYmlsaXR5IHRvIEJlbGxhYmVhdCdzIGZlbWFsZSB1c2Vycy4NCi0gQmlhczogU2VsZi1zZWxlY3RlZCBwYXJ0aWNpcGFudHM7IHdlaWdodCBsb2dnaW5nIGxvdyBkdWUgdG8gbWFudWFsIGVudHJ5Lg0KDQojIyBLZXkgSW5zaWdodHMgRnJvbSBEYXRhIEFuYWx5c2lzOg0KMSkgNXBtLTdwbSBhcmUgdGhlIG1vc3QgYWN0aXZlIGhvdXJzIG9mIHRoZSBkYXkgYXMgY29uZmlybWVkIGJ5IHRoZSBjb21wYXJpc29ucyBvZiBBdmVyYWdlIFRvdGFsIEludGVuc2l0eSBhbmQgQ2Fsb3JpZXMgYnVybmVkIHRvIFRpbWUuIA0KMikgMTJwbS0ycG0gaXMgdGhlIHNlY29uZCBtb3N0IGFjdGl2ZSB0aW1lIHBlcmlvZC4gIA0KMykgUGVvcGxlIHdpdGggaGlnaGVyIHNlZGVudGFyeSBtaW51dGVzIGhhZCBsZXNzIHRvdGFsIG1pbnV0ZXMgYXNsZWVwLiAgDQo0KSBUb3RhbCBzbGVlcCBtaW51dGVzIGluY3JlYXNlIHdpdGggdG90YWwgdGltZSBpbiBiZWQgc28gb3V0bGllcnMgdGhhdCBzcGVuZCBtb3JlIHRpbWUgaW4gYmVkIGJ1dCBnZXQgbGVzcyBzbGVlcCBtYXkgYmUgaGF2aW5nIGluc29tbmlhIG9yIHBvb3Igc2xlZXAgaHlnaWVuZS4gDQo1KSBQZW9wbGUgdGhhdCB0b29rIHRoZSBtb3N0IHN0ZXBzIGdlbmVyYWxseSBidXJuZWQgdGhlIG1vc3QgY2Fsb3JpZXMNCjYpIFRoZXJlIGlzIGEgdW5pcXVlIHJlbGF0aW9uc2hpcCBiZXR3ZWVuIHRvdGFsIHN0ZXBzIGFuZCBzZWRlbnRhcnkgbWludXRlcy4gU2VkZW50YXJ5IG1pbnV0ZXMgZGVjcmVhc2Ugd2l0aCBtb3JlIHRvdGFsIHN0ZXBzIHVwIHRvIGFib3V0IDEwLDAwMCBzdGVwcy4gIFRoZXJlIGlzIGFuIGluZmxlY3Rpb24gcG9pbnQgaGVyZSB3aGVyZSBtb3JlIHN0cGVzIGFmdGVyIDEwLDAwMCB0ZW5kcyB0byBsZWFkIHRvIGFuIGluY3JlYXNlIGluIHNlZGVudGFyeSBtaW51dGVzIHdoaWNoIG1heSBpbmRpY2F0ZSBmYXRpZ3VlIG9yIG5lZWRlZCByZXN0LiANCg0KIyMgUmVjb21lbmRhdGlvbnM6DQoxKSBTZW5kIGFjdGl2aXR5IHJlbGF0ZWQgbm90aWZpY2F0aW9uIHJlbWluZGVycyB0byB1c2VycyB0byBiZSBhY3RpdmUgcHJpb3IgdG8gdGhlIDVwbS03cG0gbW9zdCBhY3RpdmUgd2luZG93LCBlc3BlY2lhbGx5IGlmIHRoZXkgaGF2ZSBub3QgYmVlbiBhY3RpdmUgZWFybGllciBpbiB0aGUgZGF5Lg0KMikgU2VuZCBub3RpZmljYXRpb25zIHRvIHBlb3BsZSB3aXRoIGxlc3MgdG90YWwgbWludXRlcyBzbGVwdCB0byBpbXByb3ZlIHNsZWVwIGJ5IGJlaW5nIG1vcmUgYWN0aXZlIHRocm91Z2hvdXQgdGhlIGRheS4gDQozKSBTZW5kIG5vdGlmaWNhdGlvbnMgdG8gb3V0bHlpbmcgdXNlcnMgd2hvIHNwZW5kIG1vcmUgdGltZSBpbiBiZWQgYnV0IGdldCBsZXNzIHNsZWVwIHRoYW4gdGhlIGF2ZXJhZ2Ugd2l0aCBzbGVlcCBoeWdpZW5lIHRpcHMgc2luY2UgdGhleSBtaWdodCBiZSBleHBlcmllbmNpbmcgaW5zb21uaWEuIA0KNCkgU2VuZCBub3RpZmljYXRpb25zIHRvIHBlb3BsZSB0aGF0IGV4Y2VlZCAxMCwwMDAgc3RlcHMgcGVyIGRheSB0byByZW1pbmQgdGhlbSB0aGF0IHRoZXkgbWlnaHQgbmVlZCBtb3JlIHJlY292ZXJ5IGFuZCByZXN0IGFjdGl2aXRpZXMuIA0KDQo=